'use client'; import * as React from 'react'; import Fuse from 'fuse.js'; import { Input } from '@/components/ui/Input'; import { Card } from '@/components/ui/Card'; import { EmptyState } from '@/components/ui/EmptyState'; import { Search, X, Heart, Clock, List, Shuffle, Plus, Check } from 'lucide-react'; import { cn } from '@/lib/utils/cn'; import { Button } from '@/components/ui/Button'; import type { FigletFont } from '@/types/figlet'; import { getFavorites, getRecentFonts, toggleFavorite, isFavorite } from '@/lib/storage/favorites'; export interface FontSelectorProps { fonts: FigletFont[]; selectedFont: string; onSelectFont: (fontName: string) => void; onRandomFont?: () => void; isComparisonMode?: boolean; comparisonFonts?: string[]; onAddToComparison?: (fontName: string) => void; className?: string; } type FilterType = 'all' | 'favorites' | 'recent'; export function FontSelector({ fonts, selectedFont, onSelectFont, onRandomFont, isComparisonMode = false, comparisonFonts = [], onAddToComparison, className }: FontSelectorProps) { const [searchQuery, setSearchQuery] = React.useState(''); const [filter, setFilter] = React.useState('all'); const [favorites, setFavorites] = React.useState([]); const [recentFonts, setRecentFonts] = React.useState([]); const searchInputRef = React.useRef(null); // Load favorites and recent fonts React.useEffect(() => { setFavorites(getFavorites()); setRecentFonts(getRecentFonts()); }, []); // Keyboard shortcuts React.useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { // "/" to focus search if (e.key === '/' && !e.ctrlKey && !e.metaKey) { e.preventDefault(); searchInputRef.current?.focus(); } // "Esc" to clear search if (e.key === 'Escape' && searchQuery) { e.preventDefault(); setSearchQuery(''); searchInputRef.current?.blur(); } }; window.addEventListener('keydown', handleKeyDown); return () => window.removeEventListener('keydown', handleKeyDown); }, [searchQuery]); // Initialize Fuse.js for fuzzy search const fuse = React.useMemo(() => { return new Fuse(fonts, { keys: ['name', 'fileName'], threshold: 0.3, includeScore: true, }); }, [fonts]); const filteredFonts = React.useMemo(() => { let fontsToFilter = fonts; // Apply category filter if (filter === 'favorites') { fontsToFilter = fonts.filter(f => favorites.includes(f.name)); } else if (filter === 'recent') { fontsToFilter = fonts.filter(f => recentFonts.includes(f.name)); // Sort by recent order fontsToFilter.sort((a, b) => { return recentFonts.indexOf(a.name) - recentFonts.indexOf(b.name); }); } // Apply search query if (!searchQuery) return fontsToFilter; const results = fuse.search(searchQuery); const searchResults = results.map(result => result.item); // Filter search results by category if (filter === 'favorites') { return searchResults.filter(f => favorites.includes(f.name)); } else if (filter === 'recent') { return searchResults.filter(f => recentFonts.includes(f.name)); } return searchResults; }, [fonts, searchQuery, fuse, filter, favorites, recentFonts]); const handleToggleFavorite = (fontName: string, e: React.MouseEvent) => { e.stopPropagation(); toggleFavorite(fontName); setFavorites(getFavorites()); }; return (

Select Font

{onRandomFont && ( )}
{/* Filter Tabs */}
{/* Search Input */}
setSearchQuery(e.target.value)} className="pl-9 pr-9" /> {searchQuery && ( )}
{/* Font List */}
{filteredFonts.length === 0 ? ( ) : ( filteredFonts.map((font) => { const isInComparison = comparisonFonts.includes(font.name); return (
{isComparisonMode && onAddToComparison && ( )}
); }) )}
{/* Stats */}
{filteredFonts.length} font{filteredFonts.length !== 1 ? 's' : ''} {filter === 'favorites' && ` • ${favorites.length} total favorites`} {filter === 'recent' && ` • ${recentFonts.length} recent`}
); }