import { useState, useCallback, useMemo } from "react"; import { Search, X } from "lucide-react"; import { Input } from "~/components/ui/input"; import { Button } from "~/components/ui/button"; import { useDebounce } from "~/hooks/use-debounce"; import type { RawTriplet } from "./type"; interface SpaceSearchProps { triplets: RawTriplet[]; searchQuery: string; onSearchChange: (query: string) => void; placeholder?: string; } export function SpaceSearch({ triplets, searchQuery, onSearchChange, placeholder = "Search in statement facts...", }: SpaceSearchProps) { const [inputValue, setInputValue] = useState(searchQuery); // Debounce the search to avoid too many re-renders const debouncedSearchQuery = useDebounce(inputValue, 300); // Update parent component when debounced value changes useMemo(() => { if (debouncedSearchQuery !== searchQuery) { onSearchChange(debouncedSearchQuery); } }, [debouncedSearchQuery, searchQuery, onSearchChange]); // Helper to determine if a node is a statement const isStatementNode = useCallback((node: any) => { // Check if node has a fact attribute (indicates it's a statement) return ( node.attributes?.fact || (node.labels && node.labels.includes("Statement")) ); }, []); // Count statement nodes that match the search const matchingStatements = useMemo(() => { if (!debouncedSearchQuery.trim()) return 0; const query = debouncedSearchQuery.toLowerCase(); const statements: Record = {}; triplets.forEach((triplet) => { // Check if source node is a statement and matches if ( isStatementNode(triplet.sourceNode) && triplet.sourceNode.attributes?.fact?.toLowerCase().includes(query) ) { statements[triplet.sourceNode.uuid] = 1; } // Check if target node is a statement and matches if ( isStatementNode(triplet.targetNode) && triplet.targetNode.attributes?.fact?.toLowerCase().includes(query) ) { statements[triplet.targetNode.uuid] = 1; } }); return Object.keys(statements).length; }, [triplets, debouncedSearchQuery]); const handleInputChange = (event: React.ChangeEvent) => { setInputValue(event.target.value); }; const handleClear = () => { setInputValue(""); onSearchChange(""); }; const hasSearchQuery = inputValue.trim().length > 0; return (
{hasSearchQuery && ( )}
{/* Show search results count */} {debouncedSearchQuery.trim() && (
{matchingStatements} statement{matchingStatements !== 1 ? "s" : ""}
)}
); }