Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 | 57x 57x 57x 57x 22x 11x 11x 11x 57x 47x 16x 16x 16x 1x 1x 15x 3x 3x 3x 3x 3x 16x 16x 57x 21x 1x 2x 43x 1x 1x | import { useState, useRef, useEffect } from 'react'
import type { VaultEntry } from '../types'
import { NoteSearchList } from './NoteSearchList'
import { useNoteSearch } from '../hooks/useNoteSearch'
interface QuickOpenPaletteProps {
open: boolean
entries: VaultEntry[]
onSelect: (entry: VaultEntry) => void
onClose: () => void
}
export function QuickOpenPalette({ open, entries, onSelect, onClose }: QuickOpenPaletteProps) {
const [query, setQuery] = useState('')
const inputRef = useRef<HTMLInputElement>(null)
const { results, selectedIndex, setSelectedIndex, handleKeyDown } = useNoteSearch(entries, query)
useEffect(() => {
if (open) {
// eslint-disable-next-line react-hooks/set-state-in-effect -- reset on dialog open
setQuery('')
setSelectedIndex(0)
setTimeout(() => inputRef.current?.focus(), 50)
}
}, [open, setSelectedIndex])
useEffect(() => {
if (!open) return
const handler = (e: KeyboardEvent) => {
handleKeyDown(e)
if (e.key === 'Escape') {
e.preventDefault()
onClose()
} else if (e.key === 'Enter') {
e.preventDefault()
const selected = results[selectedIndex]
Eif (selected) {
onSelect(selected.entry)
onClose()
}
}
}
window.addEventListener('keydown', handler)
return () => window.removeEventListener('keydown', handler)
}, [open, results, selectedIndex, onSelect, onClose, handleKeyDown])
if (!open) return null
return (
<div
className="fixed inset-0 z-[1000] flex justify-center bg-[var(--shadow-dialog)] pt-[15vh]"
onClick={onClose}
>
<div
className="flex w-[500px] max-w-[90vw] max-h-[400px] flex-col self-start overflow-hidden rounded-xl border border-[var(--border-dialog)] bg-popover shadow-[0_8px_32px_var(--shadow-dialog)]"
onClick={(e) => e.stopPropagation()}
>
<input
ref={inputRef}
className="border-b border-border bg-transparent px-4 py-3 text-[15px] text-foreground outline-none placeholder:text-muted-foreground"
type="text"
placeholder="Search notes..."
value={query}
onChange={(e) => setQuery(e.target.value)}
/>
<NoteSearchList
items={results}
selectedIndex={selectedIndex}
getItemKey={(item) => item.entry.path}
onItemClick={(item) => {
onSelect(item.entry)
onClose()
}}
onItemHover={(i) => setSelectedIndex(i)}
emptyMessage="No matching notes"
className="flex-1 overflow-y-auto"
/>
</div>
</div>
)
}
|