vite-awesome-svg-loader
Version:
A universal Vite SVG loader. Imports SVGs as source code, base64 and data URI. Preserves stroke width. Replaces colors with currentColor or custom colors. Creates SVG sprites. Optimizes SVGs.
1 lines • 9.52 kB
Source Map (JSON)
{"version":3,"file":"index-CyvitpnO.cjs","sources":["../../../integration-utils/src/svg-symbols.ts","../../../integration-utils/src/svg-icon.ts"],"sourcesContent":["import styles from \"@/assets/symbols.scss?inline\";\n\nimport debounce from \"debounce\";\nimport MurmurHash3 from \"imurmurhash\";\nimport { createStyle } from \"common-utils\";\n\n/**\n * ID of an `<svg>` element that contains all symbols\n */\nexport const SVG_ID = \"vite-awesome-svg-loader-symbols\";\n\n/**\n * ID of a `<style>` element that contains symbols' styles\n */\nexport const SVG_SYMBOLS_STYLE_ID = \"vite-awesome-svg-loader-symbols-styles\";\n\n/**\n * Prepended to the symbol ID\n */\nexport const SYMBOL_ID_PREFIX = \"svg-\";\n\n/**\n * Attribute of a symbol element that contains count of elements that uses that symbol\n */\nexport const USAGES_COUNT_ATTR = \"data-count\";\n\ntype MaybeSymbol = SVGSymbolElement | null | undefined;\n\n/**\n * Should be called whenever image source is updated, and component is mounted.\n *\n * @param prevSrc Previous symbol source code, i.e. value of `import \"./my/image.svg\"`. If there's no previous source, pass `undefined`.\n * @param src Symbol source code, i.e. value of `import \"./my/image.svg\"`\n * @returns Attributes that should be bound to the `<svg>` tag. These attributes should override attributes bound by the user.\n */\nexport function onSrcUpdate(prevSrc: string | undefined, src: string) {\n if (typeof window === \"undefined\" || prevSrc === src) {\n return {};\n }\n\n createStyle(SVG_SYMBOLS_STYLE_ID, styles); // Reuse common function for initialization to simplify API\n\n // Get or create SVG symbols container\n\n let svgEl = document.getElementById(SVG_ID) as SVGSVGElement | null;\n\n if (!svgEl) {\n svgEl = document.createElementNS(\"http://www.w3.org/2000/svg\", \"svg\");\n svgEl.id = SVG_ID;\n svgEl.setAttribute(\"aria-hidden\", \"true\");\n document.body.appendChild(svgEl);\n }\n\n // Reduce usages count of previous symbol\n\n if (prevSrc) {\n const id = SYMBOL_ID_PREFIX + new MurmurHash3(prevSrc).result();\n reduceSymbolUsages(document.getElementById(id) as any);\n }\n\n const id = SYMBOL_ID_PREFIX + new MurmurHash3(src).result(); // Use hash of the source code as an ID\n\n // Check if symbol already exists. If so, increment its usages count.\n\n const existingSymbol = document.getElementById(id) as SVGSymbolElement | null;\n\n if (existingSymbol) {\n existingSymbol.setAttribute(USAGES_COUNT_ATTR, getSymbolUsagesCount(existingSymbol) + 1 + \"\");\n return { id, attrs: getSvgAttrs(existingSymbol) };\n }\n\n // Create new symbol\n\n const svg = new DOMParser().parseFromString(src, \"application/xml\").firstElementChild;\n\n if (svg?.querySelector(\"parsererror\")) {\n console.error(\"Source code is not a valid SVG: \" + src);\n return { id };\n }\n\n if (!svg) {\n console.error(\"Missing child in SVG: \" + src);\n return { id };\n }\n\n const symbol = document.createElementNS(\"http://www.w3.org/2000/svg\", \"symbol\");\n\n for (let i = 0; i < svg.attributes.length; i++) {\n const attr = svg.attributes[i];\n symbol.setAttribute(attr.name, attr.value);\n }\n\n symbol.id = id;\n symbol.setAttribute(USAGES_COUNT_ATTR, \"1\");\n\n while (svg.children.length) {\n symbol.appendChild(svg.children[0]);\n }\n\n svgEl.appendChild(symbol);\n return { id, attrs: getSvgAttrs(symbol) };\n}\n\n/**\n * Should be called whenever component is unmounted\n * @param symbolOrId Symbol or ID of a symbol. If nullish value is provided, won't do anything.\n */\nexport function onUnmount(symbolOrId: SVGSymbolElement | string | null | undefined) {\n if (!symbolOrId || typeof window === \"undefined\") {\n return;\n }\n\n reduceSymbolUsages(typeof symbolOrId === \"string\" ? (document.getElementById(symbolOrId) as any) : symbolOrId);\n}\n\nfunction reduceSymbolUsages(symbol: SVGSymbolElement | null | undefined) {\n if (!symbol) {\n return;\n }\n\n const newCount = getSymbolUsagesCount(symbol, 1) - 1;\n symbol.setAttribute(USAGES_COUNT_ATTR, newCount + \"\");\n\n if (newCount <= 0) {\n symbolsToRemove.push(symbol);\n scheduleSymbolsRemoval();\n }\n}\n\nconst symbolsToRemove: SVGSymbolElement[] = [];\n\nconst scheduleSymbolsRemoval = debounce(() => {\n for (let i = symbolsToRemove.length - 1; i >= 0; i--) {\n const symbol = symbolsToRemove[i];\n\n if (symbol.parentElement && getSymbolUsagesCount(symbol) <= 0) {\n symbol.parentElement.removeChild(symbol);\n }\n\n symbolsToRemove.pop();\n }\n}, 5000);\n\nfunction getSymbolUsagesCount(symbol: MaybeSymbol, nanReplacement = 0) {\n if (!symbol) {\n return nanReplacement;\n }\n\n const count = parseInt(symbol.getAttribute(USAGES_COUNT_ATTR) || \"1\");\n return isNaN(count) ? nanReplacement : count;\n}\n\nfunction getSvgAttrs(symbol: SVGSymbolElement) {\n let viewBox = symbol.getAttribute(\"viewBox\") || \"\";\n\n if (!viewBox) {\n for (const attr of [\"x\", \"y\", \"width\", \"height\"]) {\n const value = symbol.getAttribute(attr);\n viewBox += (value || \"0\") + \" \";\n }\n }\n\n return {\n viewBox,\n width: \"100%\",\n height: \"100%\",\n };\n}\n","import styles from \"@/assets/icons.scss?inline\";\n\nimport { createStyle } from \"common-utils\";\nimport { SvgIconStyleProps } from \"./types\";\n\n/**\n * ID of a `<style>` element that contains SVG icons' styles\n */\nexport const SVG_ICONS_STYLE_ID = \"vite-awesome-svg-loader-icons-styles\";\n\n/**\n * Default `SvgIcon` color transition\n */\nexport const SVG_ICON_DEFAULT_COLOR_TRANSITION = \"0.3s linear\";\n\n/**\n * Initializes SVG icons. Must be called in component's constructor function. Executes only once, so it may be called\n * multiple times.\n */\nexport function initSvgIcons() {\n if (typeof window !== \"undefined\") {\n createStyle(SVG_ICONS_STYLE_ID, styles);\n }\n}\n\n/**\n * Converts `SvgIcon` props into a \"CSS variable -> value\" pairs that should be passed to the element's `style`\n * attribute.\n *\n * Example value:\n *\n * ```ts\n * {\n * \"--size\": \"48rem\",\n * \"--color\": \"red\",\n * }\n * ```\n *\n * @param props `SvgIcon` props\n * @returns Icon style\n */\nexport function getSvgIconStyle(props: SvgIconStyleProps) {\n const style: Record<string, string> = {};\n\n if (props.size) {\n style[\"--size\"] = props.size;\n }\n\n if (props.color) {\n style[\"--color\"] = props.color;\n }\n\n style[\"--color-transition\"] = props.colorTransition || SVG_ICON_DEFAULT_COLOR_TRANSITION;\n\n return style;\n}\n"],"names":["SVG_ID","SVG_SYMBOLS_STYLE_ID","SYMBOL_ID_PREFIX","USAGES_COUNT_ATTR","onSrcUpdate","prevSrc","src","createStyle","styles","svgEl","id","MurmurHash3","reduceSymbolUsages","existingSymbol","getSymbolUsagesCount","getSvgAttrs","svg","symbol","i","attr","onUnmount","symbolOrId","newCount","symbolsToRemove","scheduleSymbolsRemoval","debounce","nanReplacement","count","viewBox","value","SVG_ICONS_STYLE_ID","SVG_ICON_DEFAULT_COLOR_TRANSITION","initSvgIcons","getSvgIconStyle","props","style"],"mappings":"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"}