mirror of
				https://github.com/walkxcode/dashboard-icons.git
				synced 2025-10-31 16:57:58 +01:00 
			
		
		
		
	feat: implement debounced search query and normalize filtering
This commit is contained in:
		| @@ -37,11 +37,20 @@ export function IconSearch({ icons }: IconSearchProps) { | ||||
| 	const router = useRouter() | ||||
| 	const pathname = usePathname() | ||||
| 	const [searchQuery, setSearchQuery] = useState(initialQuery ?? "") | ||||
| 	const [debouncedQuery, setDebouncedQuery] = useState(initialQuery ?? "") | ||||
| 	const [selectedCategories, setSelectedCategories] = useState<string[]>(initialCategories ?? []) | ||||
| 	const [sortOption, setSortOption] = useState<SortOption>(initialSort) | ||||
| 	const timeoutRef = useRef<NodeJS.Timeout | null>(null) | ||||
| 	const { resolvedTheme } = useTheme() | ||||
|  | ||||
| 	useEffect(() => { | ||||
| 		const timer = setTimeout(() => { | ||||
| 			setDebouncedQuery(searchQuery) | ||||
| 		}, 200) | ||||
|  | ||||
| 		return () => clearTimeout(timer) | ||||
| 	}, [searchQuery]) | ||||
|  | ||||
| 	// Extract all unique categories | ||||
| 	const allCategories = useMemo(() => { | ||||
| 		const categories = new Set<string>() | ||||
| @@ -66,11 +75,17 @@ export function IconSearch({ icons }: IconSearchProps) { | ||||
|  | ||||
| 			// Then filter by search query | ||||
| 			if (query.trim()) { | ||||
| 				const q = query.toLowerCase() | ||||
| 				// Normalization function: lowercase, remove spaces and hyphens | ||||
| 				const normalizeString = (str: string) => str.toLowerCase().replace(/[-\s]/g, '') | ||||
| 				const normalizedQuery = normalizeString(query) | ||||
|  | ||||
| 				filtered = filtered.filter(({ name, data }) => { | ||||
| 					if (name.toLowerCase().includes(q)) return true | ||||
| 					if (data.aliases.some((alias) => alias.toLowerCase().includes(q))) return true | ||||
| 					if (data.categories.some((category) => category.toLowerCase().includes(q))) return true | ||||
| 					// Check normalized name | ||||
| 					if (normalizeString(name).includes(normalizedQuery)) return true | ||||
| 					// Check normalized aliases | ||||
| 					if (data.aliases.some((alias) => normalizeString(alias).includes(normalizedQuery))) return true | ||||
| 					// Check normalized categories | ||||
| 					if (data.categories.some((category) => normalizeString(category).includes(normalizedQuery))) return true | ||||
| 					return false | ||||
| 				}) | ||||
| 			} | ||||
| @@ -89,6 +104,7 @@ export function IconSearch({ icons }: IconSearchProps) { | ||||
| 			} | ||||
|  | ||||
| 			// Default sort (relevance or fallback to alphabetical) | ||||
| 			// TODO: Implement actual relevance sorting | ||||
| 			return filtered.sort((a, b) => a.name.localeCompare(b.name)) | ||||
| 		}, | ||||
| 		[icons], | ||||
| @@ -114,10 +130,10 @@ export function IconSearch({ icons }: IconSearchProps) { | ||||
| 		return matches | ||||
| 	}, [icons, searchQuery]) | ||||
|  | ||||
| 	// Use useMemo for filtered icons | ||||
| 	// Use useMemo for filtered icons with debounced query | ||||
| 	const filteredIcons = useMemo(() => { | ||||
| 		return filterIcons(searchQuery, selectedCategories, sortOption) | ||||
| 	}, [filterIcons, searchQuery, selectedCategories, sortOption]) | ||||
| 		return filterIcons(debouncedQuery, selectedCategories, sortOption) | ||||
| 	}, [filterIcons, debouncedQuery, selectedCategories, sortOption]) | ||||
|  | ||||
| 	const updateResults = useCallback( | ||||
| 		(query: string, categories: string[], sort: SortOption) => { | ||||
| @@ -140,8 +156,6 @@ export function IconSearch({ icons }: IconSearchProps) { | ||||
| 		[pathname, router, initialSort], | ||||
| 	) | ||||
|  | ||||
| 	 | ||||
|  | ||||
| 	const handleSearch = useCallback( | ||||
| 		(query: string) => { | ||||
| 			setSearchQuery(query) | ||||
| @@ -197,7 +211,10 @@ export function IconSearch({ icons }: IconSearchProps) { | ||||
| 	}, []) | ||||
|  | ||||
| 	useEffect(() => { | ||||
| 		if (filteredIcons.length === 0) { | ||||
| 		if (filteredIcons.length === 0 && searchQuery && searchQuery.length > 3) { | ||||
| 			console.log("no icons found", { | ||||
| 				query: searchQuery, | ||||
| 			}) | ||||
| 			posthog.capture("no icons found", { | ||||
| 				query: searchQuery, | ||||
| 			}) | ||||
|   | ||||
| @@ -7,7 +7,7 @@ import { Suspense, useEffect } from "react" | ||||
|  | ||||
| export function PostHogProvider({ children }: { children: React.ReactNode }) { | ||||
| 	useEffect(() => { | ||||
| 		if (process.env.NODE_ENV === "development" || process.env.DISABLE_POSTHOG === "true") return | ||||
| 		if (process.env.DISABLE_POSTHOG === "true") return | ||||
| 		// biome-ignore lint/style/noNonNullAssertion: The NEXT_PUBLIC_POSTHOG_KEY environment variable is guaranteed to be set in production. | ||||
| 		posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY!, { | ||||
| 			ui_host: "https://eu.posthog.com", | ||||
|   | ||||
		Reference in New Issue
	
	Block a user