# All files / src/components QuickOpenPalette.tsx

97.14% Statements 34/35
91.66% Branches 11/12
90.9% Functions 10/11
96.77% Lines 30/31

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>
      )
    }