I want to build a component that lists all the icons like the one on the website https://lucide.dev/icons #1164
Unanswered
mustafamoe
asked this question in
Q&A
Replies: 2 comments
-
|
trying to achieve same but afraid this "import * as lucide from " would have a huge impact |
Beta Was this translation helpful? Give feedback.
0 replies
-
|
You can try that one component from my Bootstrap Application for lucide-react import { Search } from 'lucide-react';
import { DynamicIcon, iconNames } from 'lucide-react/dynamic';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { Button, Dropdown, Form, InputGroup } from 'react-bootstrap';
export type IconName = (typeof iconNames)[number];
interface IconSelectorProps {
value: IconName | string;
onChange: (iconName: IconName) => void;
placeholder?: string;
className?: string;
maxIcons?: number;
}
export default function IconSelector({
value,
onChange,
placeholder = 'Search icons...',
className = '',
maxIcons = 50,
}: IconSelectorProps) {
const [isOpen, setIsOpen] = useState(false);
const [searchTerm, setSearchTerm] = useState('');
const dropdownRef = useRef<HTMLDivElement>(null);
// Filter and prepare icons
const filteredIcons = useMemo(() => {
const searchLower = searchTerm.toLowerCase().trim();
const icons = searchLower
? iconNames.filter(
name =>
name.toLowerCase().includes(searchLower) ||
name.replace(/-/g, ' ').includes(searchLower) ||
name.replace(/-/g, '').includes(searchLower),
)
: iconNames;
return icons.slice(0, maxIcons);
}, [searchTerm, maxIcons]);
// Get selected icon display name
const selectedIconDisplay = useMemo(() => {
if (!value || !iconNames.includes(value as IconName)) return null;
return (value as string).replace(/-/g, ' ');
}, [value]);
const handleSelect = useCallback(
(iconName: IconName) => {
onChange(iconName);
setIsOpen(false);
setSearchTerm('');
},
[onChange],
);
// Handle click outside
useEffect(() => {
const handleClickOutside = (event: MouseEvent | TouchEvent) => {
if (
dropdownRef.current &&
!dropdownRef.current.contains(event.target as Node)
) {
setIsOpen(false);
}
};
if (isOpen) {
document.addEventListener('mousedown', handleClickOutside);
document.addEventListener('touchstart', handleClickOutside);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
document.removeEventListener('touchstart', handleClickOutside);
};
}
}, [isOpen]);
const columns = window.innerWidth < 768 ? 5 : 8;
return (
<div className={`position-relative ${className}`} ref={dropdownRef}>
<Dropdown show={isOpen} onToggle={setIsOpen}>
<Dropdown.Toggle
variant="outline-secondary"
className="w-100 d-flex align-items-center justify-content-between py-2 px-3"
style={{ minHeight: '44px' }}
>
<div className="d-flex align-items-center gap-2">
{value ? (
<>
<DynamicIcon name={value as IconName} size={20} />
<span className="text-capitalize">{selectedIconDisplay}</span>
</>
) : (
<span className="text-muted">Select an icon</span>
)}
</div>
</Dropdown.Toggle>
<Dropdown.Menu
className="w-100 p-2 shadow"
style={{ minWidth: '320px', maxWidth: '500px' }}
>
{/* Search */}
<div className="mb-2">
<InputGroup size="sm">
<InputGroup.Text className="border-0 bg-transparent">
<Search size={16} />
</InputGroup.Text>
<Form.Control
type="text"
placeholder={placeholder}
value={searchTerm}
onChange={e => setSearchTerm(e.target.value)}
className="border-0 shadow-none"
style={{ fontSize: '16px' }}
/>
</InputGroup>
</div>
{/* Icons Grid */}
<div
className="d-grid gap-1"
style={{
gridTemplateColumns: `repeat(${columns}, 1fr)`,
maxHeight: '300px',
overflowY: 'auto',
}}
>
{filteredIcons.length === 0 ? (
<div
className="text-center text-muted py-4"
style={{ gridColumn: `1 / -1` }}
>
No icons found
</div>
) : (
filteredIcons.map(iconName => (
<Button
key={iconName}
variant={value === iconName ? 'primary' : 'outline-secondary'}
size="sm"
className="p-2 d-flex align-items-center justify-content-center"
onClick={() => handleSelect(iconName)}
title={iconName.replace(/-/g, ' ')}
style={{ aspectRatio: '1' }}
>
<DynamicIcon name={iconName} size={20} />
</Button>
))
)}
</div>
</Dropdown.Menu>
</Dropdown>
</div>
);
} |
Beta Was this translation helpful? Give feedback.
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
-
i did this
I did this and it's working fine, but there are some typescript errors, any way to resolve them?
or if there is a better way to do it.
thanks!
Beta Was this translation helpful? Give feedback.
All reactions