react-dual-listbox
Version:
A feature-rich dual listbox for React.
99 lines (83 loc) • 3.13 kB
JSX
import React, { useCallback } from 'react';
import DualListBox, { propTypes } from './DualListBox';
/**
* Flatten an array of options to a simple list of values.
*
* @param {Array} options
* @param {function} getOptionValue
*
* @returns {Array}
*/
function flattenOptions(options, getOptionValue) {
const flattened = [];
options.forEach((option) => {
const value = getOptionValue(option);
if (value !== undefined) {
// Flatten single-level options
flattened.push(value);
} else if (option.options !== undefined) {
// Flatten optgroup options
option.options.forEach((subOption) => {
flattened.push(getOptionValue(subOption));
});
}
});
return flattened;
}
function ObjectValueWrapper(props) {
const {
available,
getOptionValue,
options,
selected,
onChange,
} = props;
const simpleAvailable = available ? flattenOptions(available, getOptionValue) : undefined;
const simpleSelected = flattenOptions(selected, getOptionValue);
const simpleOnChange = useCallback((newSelected, userSelection, controlKey) => {
const sourceValues = { selected: newSelected };
const complexValues = { selected: [] };
// Reconstruct option objects for both the selected values and user selection
Object.keys(sourceValues).forEach((key) => {
// Note that complex values become expensive if there are several options
sourceValues[key].forEach((value) => {
options.forEach((option) => {
const optionValue = getOptionValue(option);
if (optionValue) {
// Reconstruct single-level option
if (optionValue === value) {
complexValues[key].push(option);
}
} else {
// Reconstruct optgroup options with those children
const subSelected = [];
option.options.forEach((subOption) => {
if (getOptionValue(subOption) === value) {
subSelected.push(subOption);
}
});
if (subSelected.length > 0) {
complexValues[key].push({
...option,
options: subSelected,
});
}
}
});
});
});
onChange(complexValues.selected, userSelection, controlKey);
}, [getOptionValue, options]);
/* eslint-disable react/jsx-props-no-spreading */
return (
<DualListBox
{...props}
available={simpleAvailable}
selected={simpleSelected}
onChange={simpleOnChange}
/>
);
/* eslint-enable */
}
ObjectValueWrapper.propTypes = propTypes;
export default ObjectValueWrapper;