'use client' import { useState, useRef, useEffect } from 'react' import { Input } from '@/registry/new-york/ui/input' import { Command, CommandList, CommandGroup, CommandItem } from '@/registry/new-york/ui/command' import { Search, X } from 'lucide-react' import { Button } from '@/registry/new-york/ui/button' import type { NavigationData, NavigationItem, NavigationSubItem } from '@/types/navigation' import type { SiteConfig } from '@/types/site' interface SearchBarProps { navigationData: NavigationData onSearch: (query: string) => void searchResults: Array<{ category: NavigationItem items: (NavigationItem | NavigationSubItem)[] subCategories: Array<{ title: string items: (NavigationItem | NavigationSubItem)[] }> }> searchQuery: string siteConfig?: SiteConfig } export function SearchBar({ onSearch, searchResults, searchQuery, siteConfig }: SearchBarProps) { const [isFocused, setIsFocused] = useState(false) const searchRef = useRef(null) const inputRef = useRef(null) useEffect(() => { const handleClickOutside = (event: MouseEvent) => { if (searchRef.current && !searchRef.current.contains(event.target as Node)) { setIsFocused(false) } } document.addEventListener('mousedown', handleClickOutside) return () => document.removeEventListener('mousedown', handleClickOutside) }, []) useEffect(() => { const handleKeyDown = (event: KeyboardEvent) => { if (event.key === 'Escape') { setIsFocused(false) inputRef.current?.blur() } if ((event.metaKey || event.ctrlKey) && event.key === 'k') { event.preventDefault() inputRef.current?.focus() setIsFocused(true) } } document.addEventListener('keydown', handleKeyDown) return () => document.removeEventListener('keydown', handleKeyDown) }, []) const highlightText = (text: string) => { if (!searchQuery) return text const parts = text.split(new RegExp(`(${searchQuery})`, 'gi')) return parts.map((part, i) => part.toLowerCase() === searchQuery.toLowerCase() ? {part} : part ) } const handleInputChange = (value: string) => { onSearch(value) setIsFocused(true) } const handleItemSelect = (item: NavigationItem | NavigationSubItem) => { const itemWithHref = item as NavigationSubItem if (itemWithHref.href) { const linkTarget = siteConfig?.navigation?.linkTarget || '_blank' if (linkTarget === '_self') { window.location.href = itemWithHref.href } else { window.open(itemWithHref.href, linkTarget) } } onSearch('') setIsFocused(false) } const clearSearch = () => { onSearch('') setIsFocused(false) inputRef.current?.focus() } const showResults = isFocused && searchQuery.trim().length > 0 return (
handleInputChange(e.target.value)} onFocus={() => setIsFocused(true)} className="pl-10 pr-20 h-10 rounded-lg border shadow-sm" />
{searchQuery && ( )} K
{showResults && (
{searchResults.length === 0 ? (
未找到与 “{searchQuery}” 相关的导航
尝试使用不同的关键词搜索
) : ( searchResults.map((result) => ( {result.items.map((item) => ( handleItemSelect(item)} className="flex items-center gap-3 py-3 px-3 cursor-pointer hover:bg-accent/50" >
{item.icon && ( {`${item.title} { const target = e.target as HTMLImageElement target.style.display = 'none' }} /> )}
{highlightText(item.title)} {item.description && ( {highlightText(item.description)} )}
))} {result.subCategories.map((sub) => (
{result.category.title} / {sub.title}
{sub.items.map((item) => ( handleItemSelect(item)} className="flex items-center gap-3 py-3 px-3 cursor-pointer hover:bg-accent/50" >
{item.icon && ( {`${item.title} { const target = e.target as HTMLImageElement target.style.display = 'none' }} /> )}
{highlightText(item.title)} {item.description && ( {highlightText(item.description)} )}
))}
))}
)) )}
)}
) }