Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in / Register
Toggle navigation
I
integrity_risk_prevention
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
David Yang
integrity_risk_prevention
Commits
cb8971aa
Commit
cb8971aa
authored
Aug 27, 2026
by
Andy-bubu
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix: stabilize multi-select autocomplete values
parent
9d6dea24
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
87 additions
and
46 deletions
+87
-46
FormSelect.jsx
src/components/FormSelect.jsx
+1
-1
XinyuanAutocomplete.jsx
src/components/XinyuanAutocomplete.jsx
+76
-43
TailwindEditableField.jsx
...s/RecordDetailDrawer/components/TailwindEditableField.jsx
+10
-2
No files found.
src/components/FormSelect.jsx
View file @
cb8971aa
...
...
@@ -112,7 +112,7 @@ export default function FormSelect({
selectionProps
.
selectedKeys
=
new
Set
(
selectedValues
.
map
(
toOptionKey
));
selectionProps
.
onSelectionChange
=
(
keys
)
=>
{
const
nextKeys
=
keys
===
'all'
?
options
.
map
((
option
)
=>
option
.
value
)
:
[...
keys
];
keys
===
'all'
?
options
.
map
((
option
)
=>
option
.
id
)
:
[...
keys
];
emitChange
(
nextKeys
);
};
}
else
{
...
...
src/components/XinyuanAutocomplete.jsx
View file @
cb8971aa
import
{
ChevronDown
,
Check
,
Close
,
FIELD_SUFFIX_ICON_STYLE
,
Search
,
...
...
@@ -31,6 +32,10 @@ function getSelectedKeySet(selectionMode, selectedKey, selectedKeys) {
);
}
function
toSelectionKey
(
value
)
{
return
String
(
value
??
''
);
}
export
default
function
XinyuanAutocomplete
({
'aria-label'
:
ariaLabel
,
className
,
...
...
@@ -72,15 +77,18 @@ export default function XinyuanAutocomplete({
selectedKey
,
selectedKeys
,
);
const
selectedKeyStringSet
=
new
Set
(
Array
.
from
(
selectedKeySet
).
map
(
toSelectionKey
),
);
const
optionMap
=
new
Map
(
options
.
map
((
option
)
=>
[
String
(
option
.
id
),
option
]),
options
.
map
((
option
)
=>
[
toSelectionKey
(
option
.
id
),
option
]),
);
const
selectedOptions
=
selectionMode
===
'multiple'
&&
selectedKeys
===
'all'
?
options
:
[...
selectedKeySet
].
map
((
key
)
=>
optionMap
.
get
(
String
(
key
)
)
||
{
id
:
String
(
key
)
,
label
:
String
(
key
)
,
:
[...
selectedKeyS
tringS
et
].
map
((
key
)
=>
optionMap
.
get
(
key
)
||
{
id
:
key
,
label
:
key
,
},
);
const
resolvedValueText
=
...
...
@@ -90,8 +98,15 @@ export default function XinyuanAutocomplete({
:
placeholder
);
const
rootSelectionProps
=
selectionMode
===
'multiple'
?
{
selectedKeys
}
?
{
selectedKeys
:
selectedKeyStringSet
}
:
{
selectedKey
};
const
listBoxSelectionProps
=
selectionMode
===
'multiple'
?
{
selectionMode
,
selectedKeys
:
selectedKeyStringSet
,
}
:
{};
const
rootOpenProps
=
typeof
isOpen
===
'boolean'
?
{
isOpen
}
:
{};
const
activeFilter
=
shouldFilter
?
filter
||
defaultFilter
:
null
;
const
visibleOptions
=
isLoading
||
errorText
...
...
@@ -106,34 +121,34 @@ export default function XinyuanAutocomplete({
const
handleClear
=
()
=>
{
onSelectionChange
?.(
selectionMode
===
'multiple'
?
new
Set
()
:
null
);
};
const
handleListSelectionChange
=
(
keys
)
=>
{
if
(
selectionMode
===
'multiple'
)
{
onSelectionChange
?.(
keys
);
const
handleMultipleOptionPress
=
(
option
)
=>
{
if
(
selectionMode
!==
'multiple'
||
!
onSelectionChange
||
option
?.
isDisabled
)
{
return
;
}
const
nextKey
=
keys
===
'all'
?
null
:
[...
keys
][
0
]
||
null
;
onSelectionChange
?.(
nextKey
);
};
const
handleOptionAction
=
(
optionId
)
=>
{
const
normalizedOptionId
=
String
(
optionId
);
if
(
selectionMode
===
'multiple'
)
{
const
nextKeys
=
new
Set
(
selectedKeySet
);
if
(
nextKeys
.
has
(
normalizedOptionId
))
{
nextKeys
.
delete
(
normalizedOptionId
);
}
else
{
nextKeys
.
add
(
normalizedOptionId
);
}
onSelectionChange
?.(
nextKeys
);
const
optionKey
=
toSelectionKey
(
option
.
id
);
if
(
!
optionKey
)
{
return
;
}
onSelectionChange
?.(
normalizedOptionId
);
const
nextKeys
=
new
Set
(
Array
.
from
(
selectedKeyStringSet
).
filter
(
Boolean
),
);
if
(
nextKeys
.
has
(
optionKey
))
{
nextKeys
.
delete
(
optionKey
);
}
else
{
nextKeys
.
add
(
optionKey
);
}
onSelectionChange
(
nextKeys
);
};
const
handleTagRemove
=
(
keys
)
=>
{
const
removedKeys
=
new
Set
([...
keys
].
map
(
(
key
)
=>
String
(
key
)
));
const
removedKeys
=
new
Set
([...
keys
].
map
(
toSelectionKey
));
const
nextKeys
=
new
Set
(
[...
selectedKeyS
et
].
filter
((
key
)
=>
!
removedKeys
.
has
(
String
(
key
)
)),
[...
selectedKeyS
tringSet
].
filter
((
key
)
=>
!
removedKeys
.
has
(
key
)),
);
onSelectionChange
?.(
nextKeys
);
};
...
...
@@ -225,11 +240,12 @@ export default function XinyuanAutocomplete({
</
SearchField
>
<
ListBox
aria
-
label=
{
ariaLabel
||
placeholder
}
dependencies=
{
[
Array
.
from
(
selectedKeyStringSet
).
sort
().
join
(
'
\
u0000'
),
]
}
items=
{
visibleOptions
}
slot=
{
null
}
selectedKeys=
{
selectedKeySet
}
selectionMode=
{
selectionMode
}
onSelectionChange=
{
handleListSelectionChange
}
{
...
listBoxSelectionProps
}
renderEmptyState=
{
()
=>
(
<
div
className=
"grid min-h-20 place-items-center px-3 py-4"
...
...
@@ -239,21 +255,38 @@ export default function XinyuanAutocomplete({
</
div
>
)
}
>
{
(
option
)
=>
(
<
ListBox
.
Item
id=
{
option
.
id
}
isDisabled=
{
option
.
isDisabled
}
textValue=
{
option
.
textValue
||
option
.
label
}
onAction=
{
()
=>
handleOptionAction
(
option
.
id
)
}
>
{
renderOption
?
(
renderOption
(
option
)
)
:
(
<
Label
className=
"block min-w-0 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) }
:
{}
)}
>
{
renderOption
?
(
renderOption
(
option
)
)
:
(
<
Label
className=
"block min-w-0 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
>
...
...
src/pages/RecordDetailDrawer/components/TailwindEditableField.jsx
View file @
cb8971aa
...
...
@@ -259,6 +259,9 @@ function UserSelector({ value, onChange, multiple, variant }) {
function
FieldOptionSelect
({
multiple
,
onChange
,
options
,
value
,
variant
})
{
const
values
=
multiple
?
toArray
(
value
).
map
(
String
)
:
[
String
(
value
||
''
)];
const
optionValueMap
=
new
Map
(
options
.
map
((
option
)
=>
[
String
(
option
.
value
),
option
.
value
]),
);
const
selectedOptions
=
options
.
filter
((
option
)
=>
values
.
includes
(
String
(
option
.
value
)));
const
displayValue
=
selectedOptions
.
length
?
selectedOptions
.
map
((
option
)
=>
option
.
label
).
join
(
'、'
)
...
...
@@ -266,8 +269,13 @@ function FieldOptionSelect({ multiple, onChange, options, value, variant }) {
const
selectProps
=
multiple
?
{
selectedKeys
:
new
Set
(
values
),
onSelectionChange
:
(
keys
)
=>
onChange
(
keys
===
'all'
?
options
.
map
((
option
)
=>
option
.
value
)
:
[...
keys
]),
onSelectionChange
:
(
keys
)
=>
onChange
(
keys
===
'all'
?
options
.
map
((
option
)
=>
option
.
value
)
:
[...
keys
]
.
map
((
key
)
=>
optionValueMap
.
get
(
String
(
key
)))
.
filter
((
item
)
=>
item
!==
undefined
),
),
}
:
{
selectedKey
:
values
[
0
]
||
null
,
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment