'use client'; import { useState, useMemo, useRef, useCallback, useEffect } from 'react'; import { useTranslations } from 'next-intl'; import { iconForName, loadAllIconNames, POPULAR_ICON_NAMES, toStoredIconName, toTablerName } from '@/components/icons'; import { Search, X } from '@/components/icons'; import { cn } from '@/lib/utils'; import { Input } from '@/components/ui/input'; interface IconPickerProps { value: string; onChange: (iconName: string) => void; className?: string; } export function IconPicker({ value, onChange, className }: IconPickerProps) { const t = useTranslations('sidebar_apps'); const [search, setSearch] = useState(''); const [showAll, setShowAll] = useState(false); const gridRef = useRef(null); // The full Tabler list is a separate chunk, fetched the first time "show all" // is opened. Names are Tabler's kebab-case names; the stored value carries a // `tabler:` prefix (older settings hold Lucide names, see toTablerName). const [allIconNames, setAllIconNames] = useState(null); useEffect(() => { if (!showAll || allIconNames) return; let live = true; loadAllIconNames().then((names) => { if (live) setAllIconNames(names); }); return () => { live = false; }; }, [showAll, allIconNames]); const selected = toTablerName(value); const filteredIcons = useMemo(() => { const source = showAll ? allIconNames ?? POPULAR_ICON_NAMES : POPULAR_ICON_NAMES; const q = search.trim().toLowerCase().replace(/\s+/g, '-'); // Without a search the full list is capped; typing narrows it. if (!q) return source.slice(0, 600); return source.filter(name => name.includes(q)); }, [search, showAll, allIconNames]); const renderIcon = useCallback((name: string) => { const IconComponent = iconForName(toStoredIconName(name)); if (!IconComponent) return null; return ; }, []); return (
setSearch(e.target.value)} placeholder={t('search_icons')} className="ps-8 h-8 text-xs" /> {search && ( )}
{filteredIcons.map(name => ( ))} {filteredIcons.length === 0 && (

{t('no_icons_found')}

)}
); }