import React, { useState, useEffect } from 'react'; import { Box, TextInput, ActionIcon, Text, Group } from '@mantine/core'; import { useTranslation } from 'react-i18next'; import { LocalIcon } from '../shared/LocalIcon'; import { ViewerContext } from '../../contexts/ViewerContext'; interface SearchInterfaceProps { visible: boolean; onClose: () => void; } export function SearchInterface({ visible, onClose }: SearchInterfaceProps) { const { t } = useTranslation(); const viewerContext = React.useContext(ViewerContext); const searchState = viewerContext?.getSearchState(); const searchResults = searchState?.results; const searchActiveIndex = searchState?.activeIndex; const searchActions = viewerContext?.searchActions; const [searchQuery, setSearchQuery] = useState(''); const [jumpToValue, setJumpToValue] = useState(''); const [resultInfo, setResultInfo] = useState<{ currentIndex: number; totalResults: number; query: string; } | null>(null); const [isSearching, setIsSearching] = useState(false); // Monitor search state changes useEffect(() => { if (!visible) return; const checkSearchState = () => { // Use ViewerContext state instead of window APIs if (searchResults && searchResults.length > 0) { const activeIndex = searchActiveIndex || 1; setResultInfo({ currentIndex: activeIndex, totalResults: searchResults.length, query: searchQuery // Use local search query }); } else if (searchQuery && searchResults?.length === 0) { // Show "no results" state setResultInfo({ currentIndex: 0, totalResults: 0, query: searchQuery }); } else { setResultInfo(null); } }; // Check immediately and then poll for updates checkSearchState(); const interval = setInterval(checkSearchState, 200); return () => clearInterval(interval); }, [visible, searchResults, searchActiveIndex, searchQuery]); const handleSearch = async (query: string) => { if (!query.trim()) { // If query is empty, clear the search handleClearSearch(); return; } if (query.trim() && searchActions) { setIsSearching(true); try { await searchActions.search(query.trim()); } catch (error) { console.error('Search failed:', error); } finally { setIsSearching(false); } } }; const handleKeyDown = (event: React.KeyboardEvent) => { if (event.key === 'Enter') { handleSearch(searchQuery); } else if (event.key === 'Escape') { onClose(); } }; const handleNext = () => { searchActions?.next(); }; const handlePrevious = () => { searchActions?.previous(); }; const handleClearSearch = () => { searchActions?.clear(); setSearchQuery(''); setResultInfo(null); }; // No longer need to sync with external API on mount - removed const handleJumpToResult = (index: number) => { // Use context actions instead of window API - functionality simplified for now if (resultInfo && index >= 1 && index <= resultInfo.totalResults) { // Note: goToResult functionality would need to be implemented in SearchAPIBridge console.log('Jump to result:', index); } }; const handleJumpToSubmit = () => { const index = parseInt(jumpToValue); if (index && resultInfo && index >= 1 && index <= resultInfo.totalResults) { handleJumpToResult(index); } }; const handleJumpToKeyDown = (e: React.KeyboardEvent) => { if (e.key === 'Enter') { handleJumpToSubmit(); } }; const _handleClose = () => { handleClearSearch(); onClose(); }; return ( {/* Header */} {t('search.title', 'Search PDF')} {/* Search input */} { const newValue = e.currentTarget.value; setSearchQuery(newValue); // If user clears the input, clear the search highlights if (!newValue.trim()) { handleClearSearch(); } }} onKeyDown={handleKeyDown} style={{ flex: 1 }} rightSection={ handleSearch(searchQuery)} disabled={!searchQuery.trim() || isSearching} loading={isSearching} > } /> {/* Results info and navigation */} {resultInfo && ( {resultInfo.totalResults === 0 ? ( {t('search.noResults', 'No results found')} ) : ( setJumpToValue(e.currentTarget.value)} onKeyDown={handleJumpToKeyDown} onBlur={handleJumpToSubmit} placeholder={resultInfo.currentIndex.toString()} style={{ width: '3rem' }} type="number" min="1" max={resultInfo.totalResults} /> of {resultInfo.totalResults} )} {resultInfo.totalResults > 0 && ( = resultInfo.totalResults} aria-label="Next result" > )} )} {/* Loading state */} {isSearching && ( {t('search.searching', 'Searching...')} )} ); }