Newer
Older
vmk-ui-kit / src / js / components / icon-helper.js
@Eugene Sukhodolskiy Eugene Sukhodolskiy 1 day ago 1 KB Integrate user-provided SVG icon set
/**
 * Icon helper for VMK UI Kit.
 *
 * Loads SVG icons from /assets/icons/svg/ by name and renders them inline.
 * Icon names match keys in /assets/icons/index.json.
 *
 * Usage:
 *   iconNode("arrows/arrow/right") -> SVGElement
 *   iconHtml("essentials/user") -> string
 */

const indexUrl = "/assets/icons/index.json";
let indexPromise = null;

function loadIndex() {
  if (indexPromise) return indexPromise;
  indexPromise = fetch(indexUrl)
    .then(r => {
      if (!r.ok) throw new Error(`Failed to load icon index: ${r.status}`);
      return r.json();
    })
    .catch(err => {
      console.error(err);
      return {};
    });
  return indexPromise;
}

function svgUrl(relativePath) {
  return `/assets/icons/svg/${relativePath}`;
}

function sanitizeSvg(svgText) {
  // Keep only <svg> element and its children; strip scripts, events, styles.
  return svgText
    .replace(/<script[\s\S]*?<\/script>/gi, "")
    .replace(/on\w+="[^"]*"/gi, "")
    .replace(/on\w+='[^']*'/gi, "")
    .replace(/<style[\s\S]*?<\/style>/gi, "")
    .replace(/<!--[\s\S]*?-->/g, "");
}

export async function iconSvg(name) {
  const index = await loadIndex();
  const rel = index[name];
  if (!rel) {
    console.warn(`Icon not found: ${name}`);
    return "";
  }
  const res = await fetch(svgUrl(rel));
  if (!res.ok) {
    console.warn(`Failed to fetch icon: ${name}`);
    return "";
  }
  return sanitizeSvg(await res.text());
}

export async function iconNode(name) {
  const svgText = await iconSvg(name);
  if (!svgText) return null;
  const wrapper = document.createElement("span");
  wrapper.innerHTML = svgText;
  const svg = wrapper.querySelector("svg");
  if (!svg) return null;
  svg.setAttribute("aria-hidden", "true");
  svg.classList.add("vmk-icon");
  return svg;
}

export function iconHtml(name) {
  return iconSvg(name);
}