feat: add onSubmit prop to CommandPalette for full-text search
All checks were successful
Build & Publish / publish (push) Successful in 58s
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:
@@ -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" />
|
||||
|
||||
Reference in New Issue
Block a user