({
value,
onSelect,
isOpen,
onClose,
})
| 326 | * Similar to Notion's icon picker interface |
| 327 | */ |
| 328 | export const IconPicker: React.FC<IconPickerProps> = ({ |
| 329 | value, |
| 330 | onSelect, |
| 331 | isOpen, |
| 332 | onClose, |
| 333 | }) => { |
| 334 | const [searchQuery, setSearchQuery] = useState(""); |
| 335 | const [hoveredIcon, setHoveredIcon] = useState<string | null>(null); |
| 336 | |
| 337 | // Filter icons based on search query |
| 338 | const filteredCategories = useMemo(() => { |
| 339 | if (!searchQuery.trim()) return ICON_CATEGORIES; |
| 340 | |
| 341 | const query = searchQuery.toLowerCase(); |
| 342 | const filtered: Record<string, IconItem[]> = {}; |
| 343 | |
| 344 | Object.entries(ICON_CATEGORIES).forEach(([category, icons]) => { |
| 345 | const matchingIcons = icons.filter(({ name }) => |
| 346 | name.toLowerCase().includes(query) |
| 347 | ); |
| 348 | if (matchingIcons.length > 0) { |
| 349 | filtered[category] = matchingIcons; |
| 350 | } |
| 351 | }); |
| 352 | |
| 353 | return filtered; |
| 354 | }, [searchQuery]); |
| 355 | |
| 356 | // Get all icons for search |
| 357 | const allIcons = useMemo(() => { |
| 358 | return Object.values(ICON_CATEGORIES).flat(); |
| 359 | }, []); |
| 360 | |
| 361 | const handleSelect = (iconName: string) => { |
| 362 | onSelect(iconName); |
| 363 | onClose(); |
| 364 | setSearchQuery(""); |
| 365 | }; |
| 366 | |
| 367 | return ( |
| 368 | <Dialog open={isOpen} onOpenChange={onClose}> |
| 369 | <DialogContent className="max-w-2xl max-h-[80vh] p-0"> |
| 370 | <DialogHeader className="px-6 py-4 border-b"> |
| 371 | <DialogTitle>Choose an icon</DialogTitle> |
| 372 | </DialogHeader> |
| 373 | |
| 374 | {/* Search Bar */} |
| 375 | <div className="px-6 py-3 border-b"> |
| 376 | <div className="relative"> |
| 377 | <SearchIcon className="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-muted-foreground" /> |
| 378 | <Input |
| 379 | placeholder="Search icons..." |
| 380 | value={searchQuery} |
| 381 | onChange={(e) => setSearchQuery(e.target.value)} |
| 382 | className="pl-10" |
| 383 | autoFocus |
| 384 | /> |
| 385 | </div> |
nothing calls this directly
no test coverage detected