Multi Select

style.css:

.multi-select {
  border-radius: 5px;
  font-size: 15px;
  min-width: 120px;
  background-color: white;
  box-sizing: border-box;
}
            
.multi-select .placeholder {
  border: 1px #ddd solid;
  background-color: white;
  border-radius: 5px;
  height: 21px;
  font-size: 13px;
  color: rgb(37, 35, 35);
  font-weight: 500;
  cursor: pointer;
  padding: 5px 20px;
}
            
.multi-select .options {
  border: 1px solid #ddd;
  background: white;
  color: black;
  max-height: 300px;
  overflow: auto;
  border-radius: 2px;
}
            
.multi-select .options .option {
  display: block;
  color: rgb(37, 35, 35);
  font-weight: 500px;
  font-size: 14px;
  margin-top: 0;
  padding-top: 2px;
}
            
.multi-select .options .option:hover {
  background-color: dodgerblue;
  color: white;
}
import React, { useRef, useState } from "react"
import ElementPopper from "react-element-popper"

export default function App() {
  const [values, setValues] = useState(["1", "2"])

  return (
    <MultiSelect
      values={values}
      onChange={setValues}
      options={[
        ["option 1", "1"],
        ["option 2", "2"],
        ["option 3", "3"],
        ["option 4", "4"],
        ["option 5", "5"],
      ]}
    />
  )
}
    
function MultiSelect({ options = [], values = [], onChange }) {
  const [isMenuOpen, setIsMenuOpen] = useState(false)
  const ref = useRef()

  const mustSelectAll = values.length !== options.length
    
  return (
    <ElementPopper
      ref={ref}
      containerClassName="multi-select"
      element={(
        <div
          className="placeholder"
          onClick={() => setIsMenuOpen(!isMenuOpen)}
        >
          click here to select
        </div>
      )}
      popper={isMenuOpen && (
        <div className="options" style={{ width: ref?.current?.clientWidth || "100px" }}>
          <label className="option">
            <input
              type="checkbox"
              checked={!mustSelectAll}
              onChange={selectAll}
            />
            <span>select all</span>
          </label>
          {options.map(([text, value], index) => (
            <label key={index} className="option">
              <input
                type="checkbox"
                value={value}
                checked={values.includes(value)}
                onChange={select}
              />
              <span>{text}</span>
            </label>
          ))}
        </div>
      )}
      offsetY={2}
    />
  )
    
  function selectAll() {
    if (mustSelectAll) {
      values = options.map(option => option[1])
    } else {
      values = []
    }

    if (onChange) onChange(values)
  }

  function select(e) {
    let value = e.target.value

    if (values.includes(value)) {
      values = values.filter(val => val !== value)
    } else {
      values.push(value)
    }

    if (onChange) onChange([...values])
  }
}
click here to select

Handle Click Outside

Add this code to the previous example for handling clicks outside of the component.

useEffect(() => {
  function handleClickOutside(e) {
    if (ref.current && !ref.current.contains(e.target)) {
      setIsMenuOpen(false)
    }
  }
  
  document.addEventListener("click", handleClickOutside)
  return () => document.removeEventListener("click", handleClickOutside)
}, [])
click here to select