'use client'; import { useEffect, useRef, useState } from 'react'; import { createPortal } from 'react-dom'; import { FiSearch, FiX } from 'react-icons/fi'; interface SearchModalProps { isOpen: boolean; onClose: () => void; } export function SearchModal({ isOpen, onClose }: SearchModalProps) { const [isLoaded, setIsLoaded] = useState(false); const searchContainerRef = useRef(null); const pagefindUIRef = useRef(null); useEffect(() => { if (!isOpen) return; let link: HTMLLinkElement | null = null; let script: HTMLScriptElement | null = null; // Load Pagefind UI dynamically when modal opens const loadPagefind = async () => { if (pagefindUIRef.current) { // Already loaded return; } try { // Load Pagefind UI CSS link = document.createElement('link'); link.rel = 'stylesheet'; link.href = '/_pagefind/pagefind-ui.css'; document.head.appendChild(link); // Load Pagefind UI JS script = document.createElement('script'); script.src = '/_pagefind/pagefind-ui.js'; script.onload = () => { if (searchContainerRef.current && (window as any).PagefindUI) { pagefindUIRef.current = new (window as any).PagefindUI({ element: searchContainerRef.current, bundlePath: '/_pagefind/', showSubResults: true, showImages: false, excerptLength: 15, resetStyles: false, autofocus: true, translations: { placeholder: '搜尋文章...', clear_search: '清除', load_more: '載入更多結果', search_label: '搜尋此網站', filters_label: '篩選', zero_results: '找不到 [SEARCH_TERM] 的結果', many_results: '找到 [COUNT] 個 [SEARCH_TERM] 的結果', one_result: '找到 [COUNT] 個 [SEARCH_TERM] 的結果', alt_search: '找不到 [SEARCH_TERM] 的結果。改為顯示 [DIFFERENT_TERM] 的結果', search_suggestion: '找不到 [SEARCH_TERM] 的結果。請嘗試以下搜尋:', searching: '搜尋中...' } }); setIsLoaded(true); // Auto-focus the search input after a short delay setTimeout(() => { const input = searchContainerRef.current?.querySelector('input[type="search"]') as HTMLInputElement; if (input) { input.focus(); } }, 100); } }; document.head.appendChild(script); } catch (error) { console.error('Failed to load Pagefind:', error); } }; loadPagefind(); // Cleanup function to prevent duplicate initializations return () => { if (link && link.parentNode) { link.parentNode.removeChild(link); } if (script && script.parentNode) { script.parentNode.removeChild(script); } if (pagefindUIRef.current && pagefindUIRef.current.destroy) { pagefindUIRef.current.destroy(); pagefindUIRef.current = null; } }; }, [isOpen]); useEffect(() => { const handleEscape = (e: KeyboardEvent) => { if (e.key === 'Escape' && isOpen) { onClose(); } }; document.addEventListener('keydown', handleEscape); return () => document.removeEventListener('keydown', handleEscape); }, [isOpen, onClose]); useEffect(() => { // Prevent body scroll when modal is open if (isOpen) { document.body.style.overflow = 'hidden'; } else { document.body.style.overflow = ''; } return () => { document.body.style.overflow = ''; }; }, [isOpen]); if (!isOpen) return null; // Use portal to render modal at document body level to avoid z-index stacking context issues if (typeof window === 'undefined') return null; return createPortal(
e.stopPropagation()} > {/* Header */}
全站搜尋
{/* Search Container */}
{!isLoaded && (

載入搜尋引擎...

)}
{/* Footer */}
按 ESC 關閉 支援中英文全文搜尋
, document.body ); } export function SearchButton({ onClick }: { onClick: () => void }) { useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { if ((e.metaKey || e.ctrlKey) && e.key === 'k') { e.preventDefault(); onClick(); } }; document.addEventListener('keydown', handleKeyDown); return () => document.removeEventListener('keydown', handleKeyDown); }, [onClick]); return ( ); }