@utahdts/utah-design-system
Version:
Utah Design System React Library
92 lines (84 loc) • 3.03 kB
JSX
import { useEffect, useMemo, useRef } from 'react';
import { useImmer } from 'use-immer';
import { useRefAlways } from '../../../../hooks/useRefAlways';
import { MultiSelectContext } from './MultiSelectContext';
/** @typedef {import('@utahdts/utah-design-system').MultiSelectContext} MultiSelectContextType */
/** @typedef {import('@utahdts/utah-design-system').MultiSelectContextNonStateRef} MultiSelectContextNonStateRef */
/** @typedef {import('@utahdts/utah-design-system').MultiSelectContextValue} MultiSelectContextValue */
/**
* @param {object} props
* @param {import('react').ReactNode} props.children
* @param {string} props.multiSelectId
* @param {string[]} [props.defaultValues]
* @param {((newValues: string[]) => void)} [props.onChange]
* @param {(() => void)} [props.onClear]
* @param {string[]} [props.values]
* @returns {import('react').JSX.Element}
*/
export default function MultiSelectContextProvider({
children,
multiSelectId,
defaultValues,
onChange,
onClear,
values,
}) {
const onChangeRef = useRefAlways(onChange);
const multiSelectContextNonStateRef = /** @type {typeof useRef<MultiSelectContextNonStateRef>} */ (useRef)({
comboBoxDivElement: null,
selectedOptionTagRefs: [],
textInput: null,
});
const multiSelectImmer = /** @type {typeof useImmer<MultiSelectContextValue>} */ (useImmer)(() => ({
clearButtonHasFocus: false,
comboBoxOptions: [],
focusedValueTagIndex: NaN,
tagTemplate: null,
isOptionsExpanded: false,
multiSelectId,
onChange: (newValues) => {
if (onChangeRef.current) {
onChangeRef.current(newValues);
} else {
multiSelectImmer[1]((draftContext) => {
draftContext.selectedValues = newValues;
});
}
},
onClear: onClear ?? (() => multiSelectImmer[1]((draftContext) => { draftContext.selectedValues = []; })),
optionTagClassNames: {},
selectedValues: values ?? defaultValues ?? [],
textInputHasFocus: false,
}));
// when values changes externally, update the inner context state
useEffect(
() => {
multiSelectImmer[1]((draftContext) => {
if (values && draftContext.selectedValues !== values) {
draftContext.selectedValues = values;
}
});
},
[values]
);
// when values change internally (backspace to delete tag), call onchange
useEffect(
() => {
if (multiSelectImmer[0].selectedValues !== undefined) {
onChange?.(multiSelectImmer[0].selectedValues);
}
},
[multiSelectImmer[0].selectedValues]
);
// eslint-disable-next-line max-len
/** @type {[MultiSelectContextValue, import('use-immer').Updater<MultiSelectContextValue>, import('react').MutableRefObject<MultiSelectContextNonStateRef>]} */
const providerValue = useMemo(
() => [...multiSelectImmer, multiSelectContextNonStateRef],
[multiSelectImmer]
);
return (
<MultiSelectContext.Provider value={providerValue}>
{children}
</MultiSelectContext.Provider>
);
}