UNPKG

backsplash-app

Version:
107 lines (90 loc) 3.2 kB
import { useState, useMemo, useEffect } from "react"; import Fuse from "fuse.js"; import { Category } from "@/types/categories"; interface SearchableStyle { categoryId: string; categoryTitle: string; style: { label: string; description?: string; preview_url?: string; }; } export interface UseModeSearchProps { wallpaperCategories: Category[]; setExpandedCategories: (categories: string[] | ((prevCategories: string[]) => string[])) => void; } export interface UseModeSearchReturn { searchQuery: string; setSearchQuery: (query: string) => void; filteredCategories: Category[]; handleSearchChange: (e: React.ChangeEvent<HTMLInputElement>) => void; clearSearch: () => void; } export function useModeSearch({ wallpaperCategories, setExpandedCategories }: UseModeSearchProps): UseModeSearchReturn { const [searchQuery, setSearchQuery] = useState<string>(""); // Create fuse instance for search const fuse = useMemo(() => { // Create a flat list of all styles with their category info for searching const searchableItems: SearchableStyle[] = wallpaperCategories.flatMap((category) => category.styles.map((style) => ({ categoryId: category.id, categoryTitle: category.title, style, })), ); return new Fuse(searchableItems, { keys: ["categoryTitle", "style.label", "style.description"], threshold: 0.3, includeScore: true, }); }, [wallpaperCategories]); // Filter categories based on search query const filteredCategories = useMemo(() => { if (!searchQuery.trim()) { return wallpaperCategories; } const searchResults = fuse.search(searchQuery); const matchedCategoryIds = new Set(searchResults.map((result) => result.item.categoryId)); // Return categories that have matching styles, but only include the matching styles return wallpaperCategories .map((category) => { if (!matchedCategoryIds.has(category.id)) { return null; } // Include only styles that match the search const matchedStyleIds = new Set( searchResults .filter((result) => result.item.categoryId === category.id) .map((result) => result.item.style.label), ); const filteredStyles = category.styles.filter((style) => matchedStyleIds.has(style.label)); return { ...category, styles: filteredStyles, }; }) .filter(Boolean) as Category[]; }, [wallpaperCategories, searchQuery, fuse]); // Automatically expand categories with search results useEffect(() => { if (searchQuery.trim() && filteredCategories.length > 0) { setExpandedCategories(filteredCategories.map((category) => category.id)); } }, [searchQuery, filteredCategories, setExpandedCategories]); // Handle search input change const handleSearchChange = (e: React.ChangeEvent<HTMLInputElement>) => { setSearchQuery(e.target.value); }; // Clear search function const clearSearch = () => { setSearchQuery(""); }; return { searchQuery, setSearchQuery, filteredCategories, handleSearchChange, clearSearch, }; }