Commit 8832cdff authored by Andy-bubu's avatar Andy-bubu

fix: stabilize controlled autocomplete selections

parent 06cc0518
import { import {
ChevronDown, ChevronDown,
Check,
Close, Close,
FIELD_SUFFIX_ICON_STYLE, FIELD_SUFFIX_ICON_STYLE,
Search, Search,
...@@ -27,6 +28,10 @@ function getSelectedKeySet(selectionMode, selectedKey, selectedKeys) { ...@@ -27,6 +28,10 @@ function getSelectedKeySet(selectionMode, selectedKey, selectedKeys) {
); );
} }
function toSelectionKey(value) {
return String(value ?? "");
}
export default function XinyuanAutocomplete({ export default function XinyuanAutocomplete({
"aria-label": ariaLabel, "aria-label": ariaLabel,
className, className,
...@@ -61,8 +66,13 @@ export default function XinyuanAutocomplete({ ...@@ -61,8 +66,13 @@ export default function XinyuanAutocomplete({
selectedKey, selectedKey,
selectedKeys, selectedKeys,
); );
const selectedOptions = options.filter((option) => const selectedKeyStringSet = new Set(
selectedKeySet.has(option.id), Array.from(selectedKeySet).map(toSelectionKey),
);
const selectedOptions = options.filter(
(option) =>
selectedKeySet.has(option.id) ||
selectedKeyStringSet.has(toSelectionKey(option.id)),
); );
const resolvedValueText = const resolvedValueText =
valueText ?? valueText ??
...@@ -71,7 +81,31 @@ export default function XinyuanAutocomplete({ ...@@ -71,7 +81,31 @@ export default function XinyuanAutocomplete({
: placeholder); : placeholder);
const rootSelectionProps = const rootSelectionProps =
selectionMode === "multiple" ? { selectedKeys } : { selectedKey }; selectionMode === "multiple" ? { selectedKeys } : { selectedKey };
const listBoxSelectionProps =
selectionMode === "multiple" ? { selectionMode, selectedKeys } : {};
const visibleOptions = isLoading || errorText ? [] : options; const visibleOptions = isLoading || errorText ? [] : options;
const handleMultipleOptionPress = (option) => {
if (
selectionMode !== "multiple" ||
!onSelectionChange ||
option?.isDisabled
) {
return;
}
const optionKey = toSelectionKey(option.id);
if (!optionKey) {
return;
}
const nextKeys = new Set(Array.from(selectedKeyStringSet).filter(Boolean));
if (nextKeys.has(optionKey)) {
nextKeys.delete(optionKey);
} else {
nextKeys.add(optionKey);
}
onSelectionChange(nextKeys);
};
return ( return (
<Autocomplete <Autocomplete
...@@ -122,7 +156,9 @@ export default function XinyuanAutocomplete({ ...@@ -122,7 +156,9 @@ export default function XinyuanAutocomplete({
</SearchField> </SearchField>
<ListBox <ListBox
aria-label={ariaLabel || placeholder} aria-label={ariaLabel || placeholder}
dependencies={[Array.from(selectedKeyStringSet).join("\u0000")]}
items={visibleOptions} items={visibleOptions}
{...listBoxSelectionProps}
renderEmptyState={() => ( renderEmptyState={() => (
<div <div
className="grid min-h-20 place-items-center px-3 py-4" className="grid min-h-20 place-items-center px-3 py-4"
...@@ -132,22 +168,40 @@ export default function XinyuanAutocomplete({ ...@@ -132,22 +168,40 @@ export default function XinyuanAutocomplete({
</div> </div>
)} )}
> >
{(option) => ( {(option) => {
<ListBox.Item const isSelected = selectedKeyStringSet.has(
id={option.id} toSelectionKey(option.id),
isDisabled={option.isDisabled} );
textValue={option.textValue || option.label}
> return (
{renderOption ? ( <ListBox.Item
renderOption(option) id={option.id}
) : ( isDisabled={option.isDisabled}
<Label className="truncate" title={option.label}> textValue={option.textValue || option.label}
{option.label} {...(selectionMode === "multiple"
</Label> ? { onPress: () => handleMultipleOptionPress(option) }
)} : {})}
<ListBox.ItemIndicator /> >
</ListBox.Item> {renderOption ? (
)} renderOption(option)
) : (
<Label className="truncate" title={option.label}>
{option.label}
</Label>
)}
{selectionMode === "multiple" ? (
<span
aria-hidden="true"
className="ml-auto inline-flex w-5 justify-center text-foreground"
>
{isSelected ? <Check size={14} /> : null}
</span>
) : (
<ListBox.ItemIndicator />
)}
</ListBox.Item>
);
}}
</ListBox> </ListBox>
</Autocomplete.Filter> </Autocomplete.Filter>
</Autocomplete.Popover> </Autocomplete.Popover>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment