UNPKG

nuxt-svg-sprite-icon

Version:

A powerful SVG sprite module for Nuxt 3 & 4 that automatically generates SVG sprites from your assets and provides an easy-to-use component for displaying icons.

119 lines (118 loc) 4.34 kB
import { defineNuxtPlugin } from "#app"; import { spriteContent, options } from "#svg-sprite-data"; const CONTAINER_ID = "nuxt-svg-sprite-container"; const MAX_RETRY_COUNT = 3; const RETRY_DELAY = 100; const PAINT_SERVER_FEATURE_REGEX = /url\(#|<(linearGradient|radialGradient|pattern|filter|mask|clipPath)\b/i; const state = { isSpriteContainerAdded: false, isInitialized: false, retryCount: 0 }; function needsPaintServerCompat(spriteContent2) { try { return Object.values(spriteContent2).some((content) => PAINT_SERVER_FEATURE_REGEX.test(content)); } catch { return false; } } export default defineNuxtPlugin({ name: "svg-sprite-icon-client", setup() { if (process.server) return {}; const initializeSvgSprite = async () => { if (state.isInitialized) { return; } try { await addSpriteContainer(); state.isInitialized = true; } catch (error) { console.error("Failed to initialize SVG sprite system:", error); if (state.retryCount < MAX_RETRY_COUNT) { state.retryCount++; setTimeout(() => { initializeSvgSprite(); }, RETRY_DELAY * state.retryCount); } } }; const addSpriteContainer = async () => { if (state.isSpriteContainerAdded || !document?.body) { return; } if (!spriteContent || Object.keys(spriteContent).length === 0) { console.warn("[nuxt-svg-sprite-icon] No SVG content found"); return; } removeExistingContainer(); const container = createSpriteContainer(); const spriteElements = Object.entries(spriteContent).filter(([, content]) => content && typeof content === "string" && content.trim()).map(([, content]) => content); if (spriteElements.length === 0) { console.warn("[nuxt-svg-sprite-icon] No valid SVG content to add"); return; } const fragment = document.createDocumentFragment(); const tempDiv = document.createElement("div"); tempDiv.innerHTML = spriteElements.join(""); while (tempDiv.firstChild) { fragment.appendChild(tempDiv.firstChild); } container.appendChild(fragment); document.body.insertBefore(container, document.body.firstChild); state.isSpriteContainerAdded = true; if (process.env.NODE_ENV === "development") { console.log(`[nuxt-svg-sprite-icon] Added ${spriteElements.length} sprite(s) to DOM`); } }; const removeExistingContainer = () => { const existingContainer = document.getElementById(CONTAINER_ID); if (existingContainer) { existingContainer.remove(); } }; const createSpriteContainer = () => { const container = document.createElement("div"); container.id = CONTAINER_ID; const compatMode = needsPaintServerCompat(spriteContent); container.style.cssText = compatMode ? "position: absolute; width: 0; height: 0; overflow: hidden;" : "display: none; position: absolute; width: 0; height: 0; overflow: hidden;"; container.setAttribute("aria-hidden", "true"); container.setAttribute("data-nuxt-svg-sprite", "true"); container.setAttribute("data-nuxt-svg-sprite-compat", compatMode ? "paint-server" : "default"); return container; }; const reloadSprites = async () => { state.isSpriteContainerAdded = false; state.isInitialized = false; state.retryCount = 0; await initializeSvgSprite(); }; const getSpriteStats = () => { return { spriteCount: Object.keys(spriteContent).length, isInitialized: state.isInitialized, isContainerAdded: state.isSpriteContainerAdded, options: { ...options } }; }; if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", initializeSvgSprite, { once: true }); } else { if (typeof requestIdleCallback !== "undefined") { requestIdleCallback(initializeSvgSprite); } else { setTimeout(initializeSvgSprite, 0); } } return { provide: { svgSprite: { reload: reloadSprites, getStats: getSpriteStats, getOptions: () => ({ ...options }), isReady: () => state.isInitialized && state.isSpriteContainerAdded } } }; } });