MultiSelect
MultiSelect.tsx
import React, { useState, useEffect, useCallback } from 'react';
import './MultiSelect.css';
type Option = {
label: string;
value: string;
};
type MultiSelectProps = {
options: Option[];
onChange: (values: string[]) => void;
};
const MultiSelect = (props: MultiSelectProps) => {
const { options, onChange } = props;
const [selectedOptions, setSelectedOptions] = useState<string[]>([]);
const [isOpen, setIsOpen] = useState<boolean>(false);
const selectRef = React.createRef<HTMLDivElement>();
const handleSelectOption = useCallback(
(optionValue: string) => {
if (selectedOptions.includes(optionValue)) {
setSelectedOptions(selectedOptions.filter(value => value !== optionValue));
} else {
setSelectedOptions([...selectedOptions, optionValue]);
}
},
[selectedOptions],
);
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (selectRef.current && !selectRef.current.contains(event.target as Node)) {
setIsOpen(false);
}
};
document.addEventListener('mousedown', handleClickOutside);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, [selectRef]);
useEffect(() => {
onChange(selectedOptions);
}, [selectedOptions, onChange]);
return (
<div className="multi-select" ref={selectRef}>
<div className="multi-select__control" onClick={() => setIsOpen(!isOpen)}>
<div className="multi-select__selected-options">
{selectedOptions.length > 0 ? (
selectedOptions.map(value => (
<div className="multi-select__selected-option" key={value}>
{value}
</div>
))
) : (
<div className="multi-select__placeholder">Select an option</div>
)}
</div>
<div className={`multi-select__arrow ${isOpen ? 'multi-select__arrow--up' : 'multi-select__arrow--down'}`} />
</div>
{isOpen && (
<div className="multi-select__options">
{options.map(option => (
<div
key={option.value}
className={`multi-select__option ${selectedOptions.includes(option.value) ? 'multi-select__option--selected' : ''}`}
onClick={() => handleSelectOption(option.value)}
>
{option.label}
</div>
))}
</div>
)}
</div>
);
};
export default MultiSelect;
MultiSelect.module.css
import React, { useState } from 'react';
import MultiSelect from './MultiSelect';
const options = [
{ label: 'Option 1', value: 'option1' },
{ label: 'Option 2', value: 'option2' },
{ label: 'Option 3', value: 'option3' },
];
const Example = () => {
const [selectedOptions, setSelectedOptions] = useState<string[]>([]);
const handleChange = (values: string[]) => {
setSelectedOptions(values);
};
return (
<div>
<MultiSelect options={options} onChange={handleChange} />
<div>Selected options: {selectedOptions.join(', ')}</div>
</div>
);
};
export default Example;