"use client"; import Icon from "@/shared/ui/icon"; import { Input } from "@/shared/ui/input"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/shared/ui/select"; import { HelpCircle, Search } from "lucide-react"; import React, { Suspense, useDeferredValue, useEffect, useMemo, useRef, useState, type ComponentType, type LazyExoticComponent, } from "react"; import { useTranslation } from "react-i18next"; // 🔹 Lazy icon faqat tanlangan icon uchun const LazyIcon: React.FC<{ name: string }> = ({ name }) => { // eslint-disable-next-line @typescript-eslint/no-explicit-any const IconComp: LazyExoticComponent> = React.lazy( async () => { const icons = await import("lucide-react"); // eslint-disable-next-line @typescript-eslint/no-explicit-any return { default: (icons as any)[name] || HelpCircle }; }, ); return ( }> ); }; interface IconSelectProps { selectedIcon?: string; setSelectedIcon: (value: string) => void; } const IconSelect: React.FC = ({ selectedIcon, setSelectedIcon, }) => { const [icons, setIcons] = useState([]); const { t } = useTranslation(); const [visibleIcons, setVisibleIcons] = useState([]); const [chunkSize] = useState(100); const [index, setIndex] = useState(1); const [containerEl, setContainerEl] = useState(null); const [isOpen, setIsOpen] = useState(false); const [searchTerm, setSearchTerm] = useState(""); const loaderRef = useRef(null); const deferredSearch = useDeferredValue(searchTerm); useEffect(() => { if (!isOpen) return; const loadIcons = async () => { const mod = await import("lucide-react"); const allIcons = Object.keys(mod).filter((k) => /^[A-Z]/.test(k)); setIcons(allIcons); setVisibleIcons(allIcons.slice(0, chunkSize)); setIndex(1); }; loadIcons(); }, [isOpen, chunkSize]); useEffect(() => { if (!containerEl || !loaderRef.current || !isOpen) return; const observer = new IntersectionObserver( (entries) => { if (entries[0].isIntersecting) { const start = index * chunkSize; const end = start + chunkSize; const next = icons.slice(start, end); if (next.length > 0) { setVisibleIcons((p) => [...p, ...next]); setIndex((p) => p + 1); } } }, { root: containerEl, threshold: 1.0 }, ); observer.observe(loaderRef.current); return () => observer.disconnect(); }, [containerEl, icons, index, chunkSize, isOpen]); const filteredIcons = useMemo(() => { const term = deferredSearch.trim().toLowerCase(); if (!term) return visibleIcons; return icons.filter((n) => n.toLowerCase().includes(term)); }, [icons, visibleIcons, deferredSearch]); const handleOpenChange = (open: boolean) => { setIsOpen(open); if (!open) { setVisibleIcons([]); setIcons([]); setIndex(1); setSearchTerm(""); } }; return ( setSearchTerm(e.target.value)} className="h-8 text-sm" /> {filteredIcons.map((iconName) => (
{iconName}
))} {!searchTerm && isOpen && (
{visibleIcons.length < icons.length && ( {t("Yuklanmoqda...")} )}
)} ); }; export default IconSelect;