import { useState, useRef, useEffect, useCallback } from 'react'
import type { VaultEntry } from '../types'
import { NoteSearchList } from './NoteSearchList'
import { useNoteSearch } from '../hooks/useNoteSearch'
import { translate, type AppLocale } from '../lib/i18n'
import { Input } from '@/components/ui/input'
import { Button } from '@/components/ui/button'
import { Plus } from '@phosphor-icons/react'
import type { NoteSearchResult } from '../hooks/useNoteSearch'
interface QuickOpenPaletteProps {
open: boolean
entries: VaultEntry[]
isLoading?: boolean
onSelect: (entry: VaultEntry) => void
onCreateNote?: (title: string) => unknown
onClose: () => void
locale?: AppLocale
}
interface QuickOpenCreateActionProps {
title: string
onCreate: () => void
locale: AppLocale
}
function quickOpenEmptyMessage(isLoading: boolean, locale: AppLocale): string {
return isLoading ? translate(locale, 'status.vault.reloading') : translate(locale, 'noteList.empty.noMatching')
}
function QuickOpenCreateAction({ title, onCreate, locale }: QuickOpenCreateActionProps) {
return (
)
}
function useQuickOpenCreateAction({
query,
isLoading,
resultCount,
onCreateNote,
onClose,
}: {
query: string
isLoading: boolean
resultCount: number
onCreateNote?: (title: string) => unknown
onClose: () => void
}) {
const title = query.trim()
const canCreate = Boolean(onCreateNote && title && !isLoading && resultCount === 0)
const create = useCallback(() => {
if (!canCreate) return
onCreateNote?.(title)
onClose()
}, [canCreate, title, onCreateNote, onClose])
return { canCreate, create, title }
}
function useQuickOpenKeyboard({
open,
results,
selectedIndex,
onSelect,
onClose,
handleKeyDown,
createFromQuery,
}: {
open: boolean
results: NoteSearchResult[]
selectedIndex: number
onSelect: (entry: VaultEntry) => void
onClose: () => void
handleKeyDown: (e: KeyboardEvent) => void
createFromQuery: () => void
}) {
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.at(selectedIndex)
if (selected) {
onSelect(selected.entry)
onClose()
} else {
createFromQuery()
}
}
}
window.addEventListener('keydown', handler)
return () => window.removeEventListener('keydown', handler)
}, [open, results, selectedIndex, onSelect, onClose, handleKeyDown, createFromQuery])
}
export function QuickOpenPalette({ open, entries, isLoading = false, onSelect, onCreateNote, onClose, locale = 'en' }: QuickOpenPaletteProps) {
const [query, setQuery] = useState('')
const inputRef = useRef(null)
const { results, selectedIndex, setSelectedIndex, handleKeyDown } = useNoteSearch(entries, query)
const createAction = useQuickOpenCreateAction({ query, isLoading, resultCount: results.length, onCreateNote, onClose })
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])
useQuickOpenKeyboard({ open, results, selectedIndex, onSelect, onClose, handleKeyDown, createFromQuery: createAction.create })
if (!open) return null
return (
)
}