MultiSelect = component(({ options, onChange, label, selection, debug }) => {
const inputLabel = label ?? 'Search';
const enableDebug = debug ?? false;
if (!options) {
throw new Error('Please provide options to the MultiSelect component');
}
if (!onChange) {
throw new Error(
'Please provide an onChange function to MultiSelect component'
);
}
if (debug) {
console.debug(selection || []);
}
const [query, setQuery] = useState('');
const [queryResults, setQueryResults] = useState(options);
const [selected, setSelected] = useState(selection || []);
const selectInverse = () => {
setSelected(options.filter(option => !selected.includes(option)));
};
const handleSelection = (event, value) => {
event.stopPropagation();
if (event.ctrlKey) {
if (selected.includes(value)) {
const newSelected = selected.filter(el => el != value);
setSelected(newSelected);
} else {
const newSelected = [...selected, value];
setSelected(newSelected);
}
}
};
useEffect(() => setQueryResults(options), [options]);
useEffect(() => onChange(selected), [selected]);
useEffect(() => {
const regexp = new RegExp(query, 'i');
const newQueryResults = options.filter(result => regexp.test(result));
setQueryResults(newQueryResults);
}, [query]);
return jsx`
<div>
${
debug
? jsx`
<div>
<pre>${JSON.stringify({ query, selected })}</pre>
<pre>${JSON.stringify({ queryResults })}</pre>
</div>`
: ''
}
${styles}
<div>
<button onClick=${() => setSelected(options)}>Select All</button>
<button onClick=${() => setSelected([])}>Deselect All</button>
<button onClick=${selectInverse}>Select Inverse</button>
</div>
<div>
<span>${inputLabel}: <input
value=${query}
onChange=${e => setQuery(e.target.value)}/>
</span>
</div>
<div className="multiselect-results">
${queryResults.map(
(result, idx) => jsx`
<div
title="use ctrl+click to select"
className="multiselect-option"
key=${idx}
style=${{
backgroundColor: selected.includes(result) ? '#C6E5FF' : ''
}}
onClick=${e => handleSelection(e, result)}
>${result}</div>
`
)}
</div>
</div>
`;
})