fix(multiselect): allow to have options with the same label
This commit is contained in:
parent
fd9f480083
commit
83afe1ad8c
1 changed files with 17 additions and 45 deletions
|
@ -4,10 +4,7 @@ import React, {
|
||||||
useRef,
|
useRef,
|
||||||
useContext,
|
useContext,
|
||||||
createContext,
|
createContext,
|
||||||
useEffect,
|
|
||||||
useId,
|
useId,
|
||||||
useLayoutEffect,
|
|
||||||
MutableRefObject,
|
|
||||||
ReactNode,
|
ReactNode,
|
||||||
ChangeEventHandler,
|
ChangeEventHandler,
|
||||||
KeyboardEventHandler
|
KeyboardEventHandler
|
||||||
|
@ -28,7 +25,6 @@ import invariant from 'tiny-invariant';
|
||||||
import { useDeferredSubmit, useHiddenField } from './shared/hooks';
|
import { useDeferredSubmit, useHiddenField } from './shared/hooks';
|
||||||
|
|
||||||
const Context = createContext<{
|
const Context = createContext<{
|
||||||
selectionsRef: MutableRefObject<string[]>;
|
|
||||||
onRemove: (value: string) => void;
|
onRemove: (value: string) => void;
|
||||||
} | null>(null);
|
} | null>(null);
|
||||||
|
|
||||||
|
@ -38,16 +34,6 @@ function isOptions(options: string[] | Option[]): options is Option[] {
|
||||||
return Array.isArray(options[0]);
|
return Array.isArray(options[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
const optionValueByLabel = (
|
|
||||||
values: string[],
|
|
||||||
options: Option[],
|
|
||||||
label: string
|
|
||||||
): string => {
|
|
||||||
const maybeOption: Option | undefined = values.includes(label)
|
|
||||||
? [label, label]
|
|
||||||
: options.find(([optionLabel]) => optionLabel == label);
|
|
||||||
return maybeOption ? maybeOption[1] : '';
|
|
||||||
};
|
|
||||||
const optionLabelByValue = (
|
const optionLabelByValue = (
|
||||||
values: string[],
|
values: string[],
|
||||||
options: Option[],
|
options: Option[],
|
||||||
|
@ -141,7 +127,7 @@ export default function ComboMultiple({
|
||||||
|
|
||||||
const onSelect = (value: string) => {
|
const onSelect = (value: string) => {
|
||||||
const maybeValue = [...extraOptions, ...optionsWithLabels].find(
|
const maybeValue = [...extraOptions, ...optionsWithLabels].find(
|
||||||
([val]) => val == value
|
([, val]) => val == value
|
||||||
);
|
);
|
||||||
const selectedValue = maybeValue && maybeValue[1];
|
const selectedValue = maybeValue && maybeValue[1];
|
||||||
if (selectedValue) {
|
if (selectedValue) {
|
||||||
|
@ -167,8 +153,7 @@ export default function ComboMultiple({
|
||||||
hidePopover();
|
hidePopover();
|
||||||
};
|
};
|
||||||
|
|
||||||
const onRemove = (label: string) => {
|
const onRemove = (optionValue: string) => {
|
||||||
const optionValue = optionValueByLabel(newValues, optionsWithLabels, label);
|
|
||||||
if (optionValue) {
|
if (optionValue) {
|
||||||
saveSelection((selections) =>
|
saveSelection((selections) =>
|
||||||
selections.filter((value) => value != optionValue)
|
selections.filter((value) => value != optionValue)
|
||||||
|
@ -242,13 +227,11 @@ export default function ComboMultiple({
|
||||||
{selections.map((selection) => (
|
{selections.map((selection) => (
|
||||||
<ComboboxToken
|
<ComboboxToken
|
||||||
key={selection}
|
key={selection}
|
||||||
|
value={selection}
|
||||||
describedby={removedLabelledby}
|
describedby={removedLabelledby}
|
||||||
value={optionLabelByValue(
|
>
|
||||||
newValues,
|
{optionLabelByValue(newValues, optionsWithLabels, selection)}
|
||||||
optionsWithLabels,
|
</ComboboxToken>
|
||||||
selection
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
<ComboboxInput
|
<ComboboxInput
|
||||||
|
@ -287,11 +270,15 @@ export default function ComboMultiple({
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
)}
|
)}
|
||||||
{results.map(([label], index) => {
|
{results.map(([label, value], index) => {
|
||||||
if (label.startsWith('--')) {
|
if (label.startsWith('--')) {
|
||||||
return <ComboboxSeparator key={index} value={label} />;
|
return <ComboboxSeparator key={index} value={label} />;
|
||||||
}
|
}
|
||||||
return <ComboboxOption key={index} value={label} />;
|
return (
|
||||||
|
<ComboboxOption key={index} value={value}>
|
||||||
|
{label}
|
||||||
|
</ComboboxOption>
|
||||||
|
);
|
||||||
})}
|
})}
|
||||||
</ComboboxList>
|
</ComboboxList>
|
||||||
</ComboboxPopover>
|
</ComboboxPopover>
|
||||||
|
@ -307,22 +294,8 @@ function ComboboxTokenLabel({
|
||||||
onRemove: (value: string) => void;
|
onRemove: (value: string) => void;
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
}) {
|
}) {
|
||||||
const selectionsRef = useRef<string[]>([]);
|
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
|
||||||
selectionsRef.current = [];
|
|
||||||
return () => {
|
|
||||||
selectionsRef.current = [];
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Context.Provider
|
<Context.Provider value={{ onRemove }}>
|
||||||
value={{
|
|
||||||
onRemove,
|
|
||||||
selectionsRef
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div data-reach-combobox-token-label>{children}</div>
|
<div data-reach-combobox-token-label>{children}</div>
|
||||||
</Context.Provider>
|
</Context.Provider>
|
||||||
);
|
);
|
||||||
|
@ -339,17 +312,16 @@ function ComboboxSeparator({ value }: { value: string }) {
|
||||||
function ComboboxToken({
|
function ComboboxToken({
|
||||||
value,
|
value,
|
||||||
describedby,
|
describedby,
|
||||||
|
children,
|
||||||
...props
|
...props
|
||||||
}: {
|
}: {
|
||||||
value: string;
|
value: string;
|
||||||
describedby: string;
|
describedby: string;
|
||||||
|
children: ReactNode;
|
||||||
}) {
|
}) {
|
||||||
const context = useContext(Context);
|
const context = useContext(Context);
|
||||||
invariant(context, 'invalid context');
|
invariant(context, 'invalid context');
|
||||||
const { selectionsRef, onRemove } = context;
|
const { onRemove } = context;
|
||||||
useEffect(() => {
|
|
||||||
selectionsRef.current.push(value);
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<li data-reach-combobox-token {...props}>
|
<li data-reach-combobox-token {...props}>
|
||||||
|
@ -366,7 +338,7 @@ function ComboboxToken({
|
||||||
aria-describedby={describedby}
|
aria-describedby={describedby}
|
||||||
>
|
>
|
||||||
<XIcon className="icon-size mr-1" aria-hidden="true" />
|
<XIcon className="icon-size mr-1" aria-hidden="true" />
|
||||||
{value}
|
{children}
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in a new issue