this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 127 lines 3.1 kB view raw
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;