import * as React from "react"; import { iconImports, strokeIconNames, type IconImportName, type IconComponentProps, } from "./dynamic-icon-imports.js"; export interface DynamicIconProps extends React.ComponentProps<"svg"> { /** * The name of the icon to render. * - Lucide icons: kebab-case (e.g., "camera", "chevron-down") * - Flag icons: ISO 3166-1 alpha-3 or PascalCase (e.g., "USA", "BasqueCountry") * - Social media icons: PascalCase (e.g., "FacebookOriginal", "TiktokNeutral") * - Custom icons: kebab-case (e.g., "bright-local-logo") */ name: IconImportName | (string & {}); /** * Icon size in pixels (applies to both width and height) * @default 16 */ size?: number; /** * Icon color (uses currentColor by default) */ color?: string; /** * Stroke width for line icons (Lucide/custom only, ignored for flag/social media) * @default 1.33 */ strokeWidth?: number; /** * When true, keeps stroke width consistent regardless of icon size. * Only applies to Lucide and custom icons. Ignored for flag/social media icons. * @default true */ absoluteStrokeWidth?: boolean; /** * Fallback content displayed while the icon is loading. * Defaults to an invisible placeholder that matches the icon's dimensions to prevent layout shift. */ fallback?: React.ReactNode; } /** * Pre-create all lazy components at module load time. * React.lazy() still defers the actual icon import until the component is rendered, * maintaining code-splitting benefits. This only creates lightweight lazy wrappers. */ const lazyIcons: Record< string, React.LazyExoticComponent> > = {}; for (const [name, loader] of Object.entries(iconImports)) { if (loader) { lazyIcons[name] = React.lazy( loader as () => Promise<{ default: React.ComponentType; }> ); } } /** * DynamicIcon component for rendering any icon by name at runtime. * * Supports Lucide icons, custom BrightLocal icons, flag icons (ISO 3166-1 alpha-3), * and social media icons — all resolved from a single unified registry. * * @example * // Lucide icon * * * @example * // Flag icon * * * @example * // Social media icon * * * @example * // With custom loading fallback * } /> * * @example * // Opt out of default placeholder * * * @example * // Database-driven * */ export const DynamicIcon = React.forwardRef( ( { name, size = 16, strokeWidth = 1.33, absoluteStrokeWidth = true, fallback, ...props }, ref ) => { const Icon = lazyIcons[name]; if (!Icon) { console.warn(`Icon "${name}" not found in icon registry`); return null; } // Only pass stroke-specific props to icons that support them. // Flag and social media icons spread props onto and would // trigger React warnings for unknown DOM attributes like absoluteStrokeWidth. const iconProps: IconComponentProps = strokeIconNames.has(name) ? { ref, size, strokeWidth, absoluteStrokeWidth, ...props } : { ref, size, ...props }; const placeholder = fallback !== undefined ? ( fallback ) : (