Published
Edited
Dec 15, 2020
Importers
2 stars
Insert cell
Insert cell
multiselect
Insert cell
Insert cell
Insert cell
MultiSelect = component(({ options, onChange, label, selection, debug }) => {
const inputLabel = label ?? 'Search'; // The label to apply to the search input
const enableDebug = debug ?? false; // Whether to enable debug mode (to show internal state)

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>
`;
})
Insert cell
Insert cell
styles = jsx`
<style>
.multiselect-results {
margin-top: 5px;
max-height: 400px;
overflow-y: auto;
}

.multiselect-option {
padding: 0 5px;
margin-bottom: 5px;
border: 1px solid black;
border-radius: 3px;
cursor: pointer;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
</style>
`
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
import {
render,
component,
jsx,
memo,
forwardRef,
React,
ReactDOM,
createElement,
Children,
createRef,
createContext,
lazy,
Fragment,
StrictMode,
Suspense,
cloneElement,
useCallback,
useContext,
useEffect,
useImperativeHandle,
useLayoutEffect,
useMemo,
useReducer,
useRef,
useState,
useDebugValue,
createPortal,
__SECRET_SWITCHER
} from '@j-f1/react'
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more