3. Multiselect

by Vitalii_N

HTML

<div id="app"></div>

CSS

.container {
  width: 300px;
  margin: 20px;
}

.selected-display {
  padding: 5px;
  height: 20px;
  border: 1px solid #ccc;
  cursor: pointer;
}

.dropdown {
  border: 1px solid #ccc;
  list-style: none;
  padding: 0;
}

React

// Your task is to implement multiselect from scratch (styling is not required)
// While closed it should show selected items separated by coma,
// on click it should open a list where you can check/uncheck items

const {createRoot} = ReactDOM;
const {useEffect, useState, useRef, useMemo, useCallback} = React;

const Component = () => {
  const [items, setItems] = useState([
    { label: 'd', value: 'd' },
    { label: 'a', value: 'a' },
    { label: 'b', value: 'b' },
    { label: 'c', value: 'c' },
  ]);
  const [selected, setSelected] = useState([]);
  const [showDropdown, setShowDropdown] = useState(false);
  const dropdownRef = useRef(null);
  const selectedDisplayRef = useRef(null);
  
  const handleClickOutsideDropdown = useCallback((event) => {
    if (dropdownRef.current && !dropdownRef.current.contains(event.target) &&
        !selectedDisplayRef.current.contains(event.target)) {
      setShowDropdown(false);
    }
  }, [dropdownRef, selectedDisplayRef]);
  
  useEffect(() => { 
    document.addEventListener('mousedown', handleClickOutsideDropdown);
    return () => document.removeEventListener('mousedown', handleClickOutsideDropdown);
  }, [handleClickOutsideDropdown]);

  const handleToggleDropdown = () => {
    setShowDropdown(!showDropdown);
  };

  const handleSelectChange = (value) => {
    setSelected((prev) =>
	    prev.includes(value) ? prev.filter((item) => item !== value) : [...prev, value]
    );
  };

  const dropdownItems = useMemo(() => (
    <ul ref={dropdownRef} className="dropdown">
      {items.map((item) => (
        <li key={item.value}>
          <label>
            <input
              type="checkbox"
              checked={selected.includes(item.value)}
              onChange={() => handleSelectChange(item.value)}
              />
            {item.label}
          </label>
        </li>
      ))}
    </ul>
  ), [items, selected]);

  return (
    <div className="container">
      <div ref={selectedDisplayRef}...