import { useVirtualizer } from '@tanstack/react-virtual' import * as icons from 'lucide-react' import { Search, X } from 'lucide-react' import { useState } from 'react' import { Button } from '@/components/ui/button' import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger, } from '@/components/ui/dialog' import { Input } from '@/components/ui/input' import { cn } from '@/lib/utils' const allIcons = icons as unknown as Record> const ALL_ICON_NAMES: string[] = Object.keys(icons) .filter((key) => /^[A-Z]/.test(key) && !key.endsWith('Icon')) .sort() const COLUMNS = 8 const ROW_HEIGHT = 40 interface IconPickerDialogProps { value: string | null onChange: (iconName: string | null) => void } export const IconPickerDialog = ({ value, onChange }: IconPickerDialogProps) => { const [open, setOpen] = useState(false) const [filter, setFilter] = useState('') const [scrollElement, setScrollElement] = useState(null) const filteredIcons = filter ? ALL_ICON_NAMES.filter((name) => name.toLowerCase().includes(filter.toLowerCase())) : ALL_ICON_NAMES const rowCount = Math.ceil(filteredIcons.length / COLUMNS) const virtualizer = useVirtualizer({ count: rowCount, getScrollElement: () => scrollElement, estimateSize: () => ROW_HEIGHT, overscan: 5, }) const CurrentIcon = (value && allIcons[value]) || null return ( { setOpen(isOpen) if (!isOpen) setFilter('') }} > }> {CurrentIcon ? : } {value ?? '选择图标'} 选择图标 搜索并选择一个图标 · 共 {ALL_ICON_NAMES.length} 个可用图标
{ setFilter(e.target.value) scrollElement?.scrollTo({ top: 0 }) }} placeholder="搜索图标..." className="flex-1" /> {value && ( )}
{virtualizer.getVirtualItems().map((virtualRow) => { const startIndex = virtualRow.index * COLUMNS const rowIcons = filteredIcons.slice(startIndex, startIndex + COLUMNS) return (
{rowIcons.map((name) => { const Icon = allIcons[name] if (!Icon) return null return ( ) })}
) })}
{filteredIcons.length === 0 &&

未找到匹配图标

}
) }