Commit c2b5338a authored by Andy-bubu's avatar Andy-bubu

fix: stabilize multi-select autocomplete values

parent bf90cc17
import {
ChevronDown,
Check,
Close,
FIELD_SUFFIX_ICON_STYLE,
Search,
......@@ -17,6 +18,18 @@ function filterOption(textValue, inputValue) {
return !query || String(textValue || '').toLocaleLowerCase().includes(query);
}
function getSelectedKeySet(selectionMode, selectedKey, selectedKeys) {
if (selectionMode === 'multiple') {
return new Set(selectedKeys || []);
}
return new Set(selectedKey === null || selectedKey === undefined ? [] : [selectedKey]);
}
function toSelectionKey(value) {
return String(value ?? '');
}
export default function XinyuanAutocomplete({
'aria-label': ariaLabel,
className,
......@@ -28,24 +41,75 @@ export default function XinyuanAutocomplete({
placeholder = '请选择',
searchPlaceholder = '输入关键词搜索',
selectedKey,
selectedKeys,
selectionMode = 'single',
variant = 'secondary',
}) {
const selectedOption = options.find((option) => option.id === selectedKey);
const selectedKeySet = getSelectedKeySet(
selectionMode,
selectedKey,
selectedKeys,
);
const selectedKeyStringSet = new Set(
Array.from(selectedKeySet).map(toSelectionKey),
);
const selectedOptions = options.filter((option) =>
selectedKeyStringSet.has(toSelectionKey(option.id)),
);
const valueText = selectedOptions.length
? selectedOptions.map((option) => option.label).join('、')
: placeholder;
const rootSelectionProps =
selectionMode === 'multiple'
? { selectedKeys }
: { selectedKey };
const listBoxSelectionProps =
selectionMode === 'multiple'
? {
selectionMode,
selectedKeys,
}
: {};
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 (
<Autocomplete
{...rootSelectionProps}
aria-label={ariaLabel || placeholder}
className={className}
fullWidth={fullWidth}
isDisabled={isDisabled}
selectedKey={selectedKey}
selectionMode={selectionMode}
variant={variant}
onSelectionChange={onSelectionChange}
>
<Autocomplete.Trigger>
<Autocomplete.Value>
<span className="block min-w-0 truncate">
{selectedOption?.label || placeholder}
{valueText}
</span>
</Autocomplete.Value>
<Autocomplete.Indicator>
......@@ -71,18 +135,39 @@ export default function XinyuanAutocomplete({
<ListBox
aria-label={ariaLabel || placeholder}
items={options}
{...listBoxSelectionProps}
renderEmptyState={() => (
<div className="grid min-h-20 place-items-center px-3 py-4 text-sm text-muted">
{emptyText}
</div>
)}
>
{(option) => (
<ListBox.Item id={option.id} textValue={option.label}>
<Label className="truncate">{option.label}</Label>
<ListBox.ItemIndicator />
</ListBox.Item>
)}
{(option) => {
const isSelected = selectedKeyStringSet.has(toSelectionKey(option.id));
return (
<ListBox.Item
id={option.id}
isDisabled={option.isDisabled}
textValue={option.textValue || option.label}
{...(selectionMode === 'multiple'
? { onPress: () => handleMultipleOptionPress(option) }
: {})}
>
<Label className="truncate">{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>
</Autocomplete.Filter>
</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