this repo has no description
1import moize from 'moize';
2import { useEffect, useRef, useState } from 'preact/hooks';
3
4import escapeHTML from '../utils/escape-html';
5
6import { ICONS } from './ICONS';
7
8const SIZES = {
9 xs: 8,
10 s: 12,
11 m: 16,
12 l: 20,
13 xl: 24,
14 xxl: 32,
15};
16
17const ICONDATA = {};
18
19// Memoize the dangerouslySetInnerHTML of the SVGs
20const INVALID_ID_CHARS_REGEX = /[^a-zA-Z0-9]/g;
21const SVGICon = moize(
22 function ({ icon, title, width, height, body, rotate, flip }) {
23 const titleID = title?.replace(INVALID_ID_CHARS_REGEX, '-') || '';
24 const id = `icon-${icon}-${titleID}`;
25 const html = title
26 ? `<title id="${id}">${escapeHTML(title)}</title>${body}`
27 : body;
28 return (
29 <svg
30 role={title ? 'img' : 'presentation'}
31 aria-labelledby={id}
32 viewBox={`0 0 ${width} ${height}`}
33 dangerouslySetInnerHTML={{ __html: html }}
34 style={{
35 transform: `${rotate ? `rotate(${rotate})` : ''} ${
36 flip ? `scaleX(-1)` : ''
37 }`,
38 }}
39 />
40 );
41 },
42 {
43 isShallowEqual: true,
44 maxSize: Object.keys(ICONS).length,
45 matchesArg: (cacheKeyArg, keyArg) =>
46 cacheKeyArg.icon === keyArg.icon &&
47 cacheKeyArg.title === keyArg.title &&
48 cacheKeyArg.body === keyArg.body,
49 },
50);
51
52function Icon({
53 icon,
54 size = 'm',
55 alt,
56 title,
57 class: className = '',
58 style = {},
59}) {
60 if (!icon) return null;
61
62 const iconSize = SIZES[size];
63 let iconBlock = ICONS[icon];
64 if (!iconBlock) {
65 console.warn(`Icon ${icon} not found`);
66 return null;
67 }
68
69 let rotate,
70 flip,
71 rtl = false;
72 if (Array.isArray(iconBlock)) {
73 [iconBlock, rotate, flip] = iconBlock;
74 } else if (typeof iconBlock === 'object') {
75 ({ rotate, flip, rtl } = iconBlock);
76 iconBlock = iconBlock.module;
77 }
78
79 const [iconData, setIconData] = useState(ICONDATA[icon]);
80 const currentIcon = useRef(icon);
81 useEffect(() => {
82 if (iconData && currentIcon.current === icon) return;
83 (async () => {
84 const iconB = await iconBlock();
85 setIconData(iconB.default);
86 ICONDATA[icon] = iconB.default;
87 })();
88 currentIcon.current = icon;
89 }, [icon]);
90
91 return (
92 <span
93 class={`icon ${className} ${rtl ? 'rtl-flip' : ''}`}
94 style={{
95 width: `${iconSize}px`,
96 height: `${iconSize}px`,
97 ...style,
98 }}
99 data-icon={icon}
100 >
101 {iconData && (
102 // <svg
103 // width={iconSize}
104 // height={iconSize}
105 // viewBox={`0 0 ${iconData.width} ${iconData.height}`}
106 // dangerouslySetInnerHTML={{ __html: iconData.body }}
107 // style={{
108 // transform: `${rotate ? `rotate(${rotate})` : ''} ${
109 // flip ? `scaleX(-1)` : ''
110 // }`,
111 // }}
112 // />
113 <SVGICon
114 icon={icon}
115 title={title || alt}
116 width={iconData.width}
117 height={iconData.height}
118 body={iconData.body}
119 rotate={rotate}
120 flip={flip}
121 />
122 )}
123 </span>
124 );
125}
126
127export default Icon;