import classNames from 'classnames'; import React, { FC, ImgHTMLAttributes, SVGProps, useEffect, useState } from 'react'; import { EmptyIcon } from './EmptyIcon'; import { IconName, SVGRProps, allIcons, isBundledIcon, loadIcon } from './assets/index'; export type { IconName } from './assets'; export const iconsList = allIcons; let iconsCache: Record>> = {}; /** * Preload a list of icons in cache * @param icons - the list of icons to preload * @returns true if the icons have been preloaded */ export async function preloadIcons(icons: IconName[]) { const preloadedIcons = await Promise.all(icons.map((icon) => loadIcon(icon))); preloadedIcons.forEach(({ component }, i) => { iconsCache[icons[i]] = (() => component) as unknown as FC>; }); return true; } /** * Removes icons from cache * @param icon? - the icon to remove, or nothing to clear the whole cache * @returns an object containing the removed icons */ export const clearIconCache = (iconName?: IconName) => { let deletedItems; if (iconName) { deletedItems = { [iconName]: iconsCache[iconName] }; delete iconsCache[iconName]; } else { deletedItems = { ...iconsCache }; iconsCache = {}; } return deletedItems; }; export interface IconProps extends SVGProps { /** Classi aggiuntive da usare per il componente Badge */ className?: string; /** Le varianti di colore definite in Bootstrap Italia */ color?: 'success' | 'warning' | 'danger' | 'note' | 'important' | string; /** Le dimensioni dell'icona. In ordine dalla più grande alla più piccola: "xl", "lg", '' (stringa vuota), "sm", "xs". */ size?: 'xl' | 'lg' | '' | 'sm' | 'xs'; /** * Il nome dell'icona da mostrare. Per una lista completa vedi: * Lista icone. * In caso di un'immagine esterna l'URL da utilizzare. **/ icon: string; /** Il titolo da dare all'icona. Quando si utilizza un'immagine esterna viene utilizzato come attributo "alt". */ title?: string; /** Quando abilitato riduce la dimensione dell'icona all'interno del contenitore. */ padding?: boolean; /** Funzione chiamata al caricamento dell'icona */ onIconLoad?: () => void; /** Id da utilizzare in caso di testing */ testId?: string; } export const Icon: FC = ({ color = '', size = '', icon = '', title = '', className, padding = false, onIconLoad, testId, ...attributes }) => { const [IconComponent, setCurrentIcon] = useState & SVGRProps>>(iconsCache[icon]); const classes = classNames('icon', className, { [`icon-${color}`]: color, [`icon-${size}`]: size, 'icon-padded': padding }); useEffect(() => { if (isBundledIcon(icon) && !iconsCache[icon]) { loadIcon(icon).then(({ component }) => { iconsCache[icon] = (() => component) as unknown as FC & SVGRProps>; setCurrentIcon(iconsCache[icon]); onIconLoad?.(); }); } else { if (IconComponent !== iconsCache[icon]) { setCurrentIcon(iconsCache[icon]); } onIconLoad?.(); } }, [IconComponent, icon, onIconLoad]); if (!isBundledIcon(icon)) { // assume it's an image and let the browser do its job return ( {title})} /> ); } if (!IconComponent) { return ( ); } return ( ); };