fix(ui): SearchableValueSelect 支持 commitOnType,恢复域名边打边筛
设计补刀:域名筛换成可搜 Select 后丢失「键入即过滤当前页」。新增 commitOnType——键入即 onChange、列表当建议、「全部」清空;长选项 truncate+title。域名筛启用;采集器 sidecar 命名空间/工作负载长列表一并改为可搜(同刀)。
This commit is contained in:
1 parent
8c9f5a95f8
commit
305d7c5904
3 files changed
+50
-24
No files matched your search
@@ -16,6 +16,11 @@ interface SearchableValueSelectProps {
|
||||
allowEmpty?: boolean
|
||||
/** Label for the empty option and closed-state display when value is empty. */
|
||||
emptyLabel?: string
|
||||
/**
|
||||
* Commit every keystroke via onChange (list is suggestions only).
|
||||
* Used by domain filter: type to filter the current page without requiring a pick.
|
||||
*/
|
||||
commitOnType?: boolean
|
||||
loading?: boolean
|
||||
disabled?: boolean
|
||||
className?: string
|
||||
@@ -33,6 +38,7 @@ export function SearchableValueSelect({
|
||||
placeholder,
|
||||
allowEmpty = false,
|
||||
emptyLabel,
|
||||
commitOnType = false,
|
||||
loading = false,
|
||||
disabled = false,
|
||||
className,
|
||||
@@ -46,9 +52,9 @@ export function SearchableValueSelect({
|
||||
const id = useId()
|
||||
const emptyText = emptyLabel ?? t('select.any')
|
||||
const choices = values.filter(item => item.toLowerCase().includes(filter.toLowerCase()))
|
||||
// active index accounts for optional empty row at top
|
||||
const optionCount = (allowEmpty ? 1 : 0) + choices.length
|
||||
const closedDisplay = value || (allowEmpty ? emptyText : '')
|
||||
const inputValue = open ? filter : closedDisplay
|
||||
|
||||
useEffect(() => {
|
||||
const close = (event: MouseEvent) => {
|
||||
@@ -60,7 +66,7 @@ export function SearchableValueSelect({
|
||||
|
||||
function choose(next: string) {
|
||||
onChange(next)
|
||||
setFilter('')
|
||||
setFilter(commitOnType ? next : '')
|
||||
setActive(0)
|
||||
setOpen(false)
|
||||
}
|
||||
@@ -81,6 +87,7 @@ export function SearchableValueSelect({
|
||||
else {
|
||||
const item = choices[allowEmpty ? active - 1 : active]
|
||||
if (item) choose(item)
|
||||
else if (commitOnType) setOpen(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -98,11 +105,20 @@ export function SearchableValueSelect({
|
||||
aria-expanded={open}
|
||||
aria-controls={`${id}-options`}
|
||||
disabled={disabled}
|
||||
value={open ? filter : closedDisplay}
|
||||
value={inputValue}
|
||||
placeholder={placeholder ?? t('select.placeholder')}
|
||||
className={cn('h-9', triggerClassName)}
|
||||
onFocus={() => { setFilter(''); setOpen(true) }}
|
||||
onChange={event => { setFilter(event.target.value); setActive(0); setOpen(true) }}
|
||||
onFocus={() => {
|
||||
setFilter(commitOnType ? value : '')
|
||||
setOpen(true)
|
||||
}}
|
||||
onChange={event => {
|
||||
const next = event.target.value
|
||||
setFilter(next)
|
||||
setActive(0)
|
||||
setOpen(true)
|
||||
if (commitOnType) onChange(next)
|
||||
}}
|
||||
onKeyDown={onKeyDown}
|
||||
/>
|
||||
{open && (
|
||||
@@ -112,7 +128,8 @@ export function SearchableValueSelect({
|
||||
type="button"
|
||||
role="option"
|
||||
aria-selected={!value}
|
||||
className={cn('block w-full rounded px-2 py-1.5 text-left text-sm hover:bg-muted', active === 0 && 'bg-primary/10 text-primary')}
|
||||
className={cn('block w-full truncate rounded px-2 py-1.5 text-left text-sm hover:bg-muted', active === 0 && 'bg-primary/10 text-primary')}
|
||||
title={emptyText}
|
||||
onMouseDown={event => event.preventDefault()}
|
||||
onClick={() => choose('')}
|
||||
>
|
||||
@@ -132,8 +149,9 @@ export function SearchableValueSelect({
|
||||
type="button"
|
||||
role="option"
|
||||
aria-selected={item === value}
|
||||
title={item}
|
||||
className={cn(
|
||||
'block w-full rounded px-2 py-1.5 text-left text-sm',
|
||||
'block w-full truncate rounded px-2 py-1.5 text-left text-sm',
|
||||
optionIndex === active ? 'bg-primary/10 text-primary' : 'hover:bg-muted',
|
||||
)}
|
||||
onMouseDown={event => event.preventDefault()}
|
||||
|
||||
Reference in new issue
Block a user