feat: add onSubmit prop to CommandPalette for full-text search
All checks were successful
Build & Publish / publish (push) Successful in 58s

When Enter is pressed without explicit arrow/mouse navigation,
fires onSubmit with the raw query instead of selecting the
focused result. Enables using the palette as a search filter.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-03-27 23:30:36 +01:00
parent 433d582da6
commit a12b374fb2

View File

@@ -14,6 +14,9 @@ interface CommandPaletteProps {
data: SearchResult[]
onOpen?: () => void
onQueryChange?: (query: string) => void
/** Called when Enter is pressed without the user explicitly selecting a result (arrow keys/click).
* Useful for applying the query as a full-text search filter. */
onSubmit?: (query: string) => void
}
const CATEGORY_LABELS: Record<SearchCategory | 'all', string> = {
@@ -64,12 +67,13 @@ function highlightText(text: string, query: string, matchRanges?: [number, numbe
return <>{parts}</>
}
export function CommandPalette({ open, onClose, onSelect, data, onOpen, onQueryChange }: CommandPaletteProps) {
export function CommandPalette({ open, onClose, onSelect, data, onOpen, onQueryChange, onSubmit }: CommandPaletteProps) {
const [query, setQuery] = useState('')
const [activeCategory, setActiveCategory] = useState<SearchCategory | 'all'>('all')
const [scopeFilters, setScopeFilters] = useState<ScopeFilter[]>([])
const [focusedIdx, setFocusedIdx] = useState(0)
const [expandedId, setExpandedId] = useState<string | null>(null)
const userNavigated = useRef(false)
const inputRef = useRef<HTMLInputElement>(null)
const listRef = useRef<HTMLDivElement>(null)
@@ -92,6 +96,7 @@ export function CommandPalette({ open, onClose, onSelect, data, onOpen, onQueryC
setQuery('')
setFocusedIdx(0)
setExpandedId(null)
userNavigated.current = false
}
}, [open])
@@ -150,15 +155,20 @@ export function CommandPalette({ open, onClose, onSelect, data, onOpen, onQueryC
break
case 'ArrowDown':
e.preventDefault()
userNavigated.current = true
setFocusedIdx((i) => Math.min(i + 1, flatResults.length - 1))
break
case 'ArrowUp':
e.preventDefault()
userNavigated.current = true
setFocusedIdx((i) => Math.max(i - 1, 0))
break
case 'Enter':
e.preventDefault()
if (flatResults[focusedIdx]) {
if (!userNavigated.current && onSubmit && query.trim()) {
onSubmit(query.trim())
onClose()
} else if (flatResults[focusedIdx]) {
onSelect(flatResults[focusedIdx])
onClose()
}
@@ -213,6 +223,7 @@ export function CommandPalette({ open, onClose, onSelect, data, onOpen, onQueryC
onChange={(e) => {
setQuery(e.target.value)
setFocusedIdx(0)
userNavigated.current = false
onQueryChange?.(e.target.value)
}}
aria-label="Search"
@@ -282,7 +293,7 @@ export function CommandPalette({ open, onClose, onSelect, data, onOpen, onQueryC
onSelect(result)
onClose()
}}
onMouseEnter={() => setFocusedIdx(flatIdx)}
onMouseEnter={() => { userNavigated.current = true; setFocusedIdx(flatIdx) }}
>
<div className={styles.itemMain}>
{result.icon && (
@@ -353,7 +364,7 @@ export function CommandPalette({ open, onClose, onSelect, data, onOpen, onQueryC
</div>
<div className={styles.shortcut}>
<KeyboardHint keys="Enter" />
<span>Open</span>
<span>Search</span>
</div>
<div className={styles.shortcut}>
<KeyboardHint keys="Esc" />