"use client" import {useCallback, useEffect, useState} from "react" import {useRouter} from "next/navigation" import { CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, } from "@/components/ui/command" import {Kbd, KbdGroup} from "@/components/ui/kbd" import {type SearchItem, searchItems} from "@/lib/utils/search-data" import {FileText, Home, Settings, Shield} from "lucide-react" import {useUser} from "@/contexts/user-context" interface SearchDialogProps { open: boolean onOpenChange: (open: boolean) => void } const categoryIcons = { page: Home, feature: FileText, setting: Settings, admin: Shield, } const categoryLabels = { page: '页面', feature: '功能', setting: '设置', admin: '管理', } const getTips = (metaKey: string) => [ ( <> Tips: 还可以使用 / 来打开此界面 ), ( <> Tips: 使用 ↑ ↓ 来切换选中项 ), ( <> Tips: 按住 {metaKey} + ↵ 在新标签页打开 ), ( <> 你知道吗:搜索功能还在持续升级中 ) ] export function SearchDialog({ open, onOpenChange }: SearchDialogProps) { const router = useRouter() const { user } = useUser() const [search, setSearch] = useState('') const [currentTip, setCurrentTip] = useState(null) const [results, setResults] = useState([]) const [metaKey, setMetaKey] = useState("⌘") const [isCtrlPressed, setIsCtrlPressed] = useState(false) useEffect(() => { if (typeof navigator !== 'undefined' && !navigator.userAgent?.includes("Mac")) { setMetaKey("Ctrl") } }, []) useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { if (e.key === 'Control' || e.key === 'Meta') { setIsCtrlPressed(true) } } const handleKeyUp = (e: KeyboardEvent) => { if (e.key === 'Control' || e.key === 'Meta') { setIsCtrlPressed(false) } } window.addEventListener('keydown', handleKeyDown) window.addEventListener('keyup', handleKeyUp) return () => { window.removeEventListener('keydown', handleKeyDown) window.removeEventListener('keyup', handleKeyUp) } }, []) useEffect(() => { if (open) { const tips = getTips(metaKey) const randomTip = tips[Math.floor(Math.random() * tips.length)] setCurrentTip(randomTip) } }, [open, metaKey]) useEffect(() => { const items = searchItems(search, user?.is_admin) setResults(items) }, [search, user?.is_admin]) const handleSelect = useCallback((item: SearchItem, openInNewTab = false) => { onOpenChange(false) if (openInNewTab) { window.open(item.url, '_blank') } else { router.push(item.url) } setSearch('') }, [onOpenChange, router]) useEffect(() => { const down = (e: KeyboardEvent) => { if (e.key === 'k' && (e.metaKey || e.ctrlKey)) { e.preventDefault() onOpenChange(!open) } if (e.key === '/'&& !e.ctrlKey && !e.metaKey && !e.altKey) { const target = e.target as HTMLElement const isEditing = target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.tagName === 'SELECT' || target.isContentEditable || target.closest('[contenteditable="true"]') if (!isEditing) { e.preventDefault() onOpenChange(true) } } } document.addEventListener('keydown', down) return () => document.removeEventListener('keydown', down) }, [open, onOpenChange]) // Group results by category const groupedResults = results.reduce((acc, item) => { if (!acc[item.category]) { acc[item.category] = [] } acc[item.category].push(item) return acc }, {} as Record) return ( 没有找到相关内容,换个词试试? {Object.entries(groupedResults).map(([category, items]) => { const Icon = categoryIcons[category as keyof typeof categoryIcons] return ( {items.map((item) => ( handleSelect(item, isCtrlPressed)} className="flex items-center justify-between" >
{item.matchRange ? ( <> {item.title.substring(0, item.matchRange[0])} {item.title.substring(item.matchRange[0], item.matchRange[1] + 1)} {item.title.substring(item.matchRange[1] + 1)} ) : ( item.title )} {item.description}
{categoryLabels[item.category as keyof typeof categoryLabels]}
))}
) })}
{isCtrlPressed && {metaKey}} ↵ {isCtrlPressed ? '在新标签页打开' : '打开'}
Esc 关闭搜索界面
{currentTip}
) }