import React, { useCallback, useMemo, useRef, useState } from "react"; import { IconType } from "react-icons"; import * as LuIcons from "react-icons/lu"; import { Input } from "@/components/ui/input"; import { Popover, PopoverContent, PopoverTrigger, } from "@/components/ui/popover"; import { IoClose } from "react-icons/io5"; import Heading from "../ui/heading"; import { cn } from "@/lib/utils"; import { Button } from "../ui/button"; export type IconName = keyof typeof LuIcons; export type IconElement = { name?: string; Icon?: IconType; }; type IconPickerProps = { selectedIcon?: IconElement; setSelectedIcon?: React.Dispatch< React.SetStateAction >; }; export default function IconPicker({ selectedIcon, setSelectedIcon, }: IconPickerProps) { const [open, setOpen] = useState(false); const containerRef = useRef(null); const [searchTerm, setSearchTerm] = useState(""); const iconSets = useMemo(() => [...Object.entries(LuIcons)], []); const icons = useMemo( () => iconSets.filter( ([name]) => name.toLowerCase().includes(searchTerm.toLowerCase()) || searchTerm === "", ), [iconSets, searchTerm], ); const handleIconSelect = useCallback( ({ name, Icon }: IconElement) => { if (setSelectedIcon) { setSelectedIcon({ name, Icon }); } setSearchTerm(""); }, [setSelectedIcon], ); return (
{ setOpen(open); }} > {!selectedIcon?.name || !selectedIcon?.Icon ? ( ) : (
{selectedIcon.name .replace(/^Lu/, "") .replace(/([A-Z])/g, " $1")}
{ handleIconSelect({ name: undefined, Icon: undefined }); }} />
)}
Select an icon { setOpen(false); }} />
setSearchTerm(e.target.value)} />
{icons.map(([name, Icon]) => (
{ handleIconSelect({ name, Icon }); setOpen(false); }} />
))}
); } type IconRendererProps = { icon: IconType; size?: number; className?: string; }; export function IconRenderer({ icon, size, className }: IconRendererProps) { return <>{React.createElement(icon, { size, className })}; }