@page-speed/icon
Advanced tools
+36
-18
@@ -165,2 +165,3 @@ 'use strict'; | ||
| } | ||
| var isTestEnvironment = typeof process !== "undefined" && process.env.NODE_ENV === "test"; | ||
| function Icon({ | ||
@@ -176,4 +177,3 @@ name, | ||
| }) { | ||
| const [svgContent, setSvgContent] = React__namespace.useState(null); | ||
| const [hasError, setHasError] = React__namespace.useState(false); | ||
| const [state, setState] = React__namespace.useState({ svg: null, error: false }); | ||
| const parsedName = React__namespace.useMemo(() => parseIconName(name), [name]); | ||
@@ -193,17 +193,35 @@ const requestUrl = React__namespace.useMemo(() => { | ||
| if (!requestUrl) { | ||
| setSvgContent(null); | ||
| setHasError(true); | ||
| setState((prev) => prev.error && !prev.svg ? prev : { svg: null, error: true }); | ||
| return; | ||
| } | ||
| setHasError(false); | ||
| setSvgContent(null); | ||
| fetchIconSvg(requestUrl).then((svg) => { | ||
| if (!disposed) { | ||
| setSvgContent(svg); | ||
| setState((prev) => !prev.svg && !prev.error ? prev : { svg: null, error: false }); | ||
| const loadIcon = async () => { | ||
| try { | ||
| const svg = await fetchIconSvg(requestUrl); | ||
| if (!disposed) { | ||
| if (isTestEnvironment) { | ||
| queueMicrotask(() => { | ||
| if (!disposed) { | ||
| setState({ svg, error: false }); | ||
| } | ||
| }); | ||
| } else { | ||
| setState({ svg, error: false }); | ||
| } | ||
| } | ||
| } catch { | ||
| if (!disposed) { | ||
| if (isTestEnvironment) { | ||
| queueMicrotask(() => { | ||
| if (!disposed) { | ||
| setState({ svg: null, error: true }); | ||
| } | ||
| }); | ||
| } else { | ||
| setState({ svg: null, error: true }); | ||
| } | ||
| } | ||
| } | ||
| }).catch(() => { | ||
| if (!disposed) { | ||
| setHasError(true); | ||
| } | ||
| }); | ||
| }; | ||
| loadIcon(); | ||
| return () => { | ||
@@ -215,3 +233,3 @@ disposed = true; | ||
| const a11yProps = ariaLabel ? { role: "img", "aria-label": ariaLabel } : { "aria-hidden": true }; | ||
| if (!svgContent) { | ||
| if (!state.svg) { | ||
| return /* @__PURE__ */ jsxRuntime.jsx( | ||
@@ -222,5 +240,5 @@ "span", | ||
| style: { width: size, height: size }, | ||
| "data-state": hasError ? "error" : "loading", | ||
| "data-state": state.error ? "error" : "loading", | ||
| ...a11yProps, | ||
| children: hasError ? fallback : null | ||
| children: state.error ? fallback : null | ||
| } | ||
@@ -239,3 +257,3 @@ ); | ||
| "data-state": "ready", | ||
| dangerouslySetInnerHTML: { __html: svgContent }, | ||
| dangerouslySetInnerHTML: { __html: state.svg }, | ||
| ...a11yProps | ||
@@ -242,0 +260,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../../src/utils/iconUrl.ts","../../src/utils/svg.ts","../../src/utils/fetchIconSvg.ts","../../src/core/Icon.tsx"],"names":["React","jsx"],"mappings":"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","file":"Icon.cjs","sourcesContent":["import type { BuildIconRequestUrlOptions, ParsedIconName } from \"../types\";\n\nexport const DEFAULT_ICON_API_BASE_URL = \"https://icons.opensite.ai\";\n\nexport function parseIconName(name: string): ParsedIconName | null {\n if (!name || typeof name !== \"string\") {\n return null;\n }\n\n const trimmed = name.trim();\n if (!trimmed) {\n return null;\n }\n\n const slashIndex = trimmed.indexOf(\"/\");\n const colonIndex = trimmed.indexOf(\":\");\n\n let separatorIndex = -1;\n if (slashIndex >= 0 && colonIndex >= 0) {\n separatorIndex = Math.min(slashIndex, colonIndex);\n } else if (slashIndex >= 0) {\n separatorIndex = slashIndex;\n } else if (colonIndex >= 0) {\n separatorIndex = colonIndex;\n }\n\n if (separatorIndex <= 0 || separatorIndex >= trimmed.length - 1) {\n return null;\n }\n\n const prefix = trimmed.slice(0, separatorIndex).trim();\n const iconName = trimmed.slice(separatorIndex + 1).trim();\n\n if (!prefix || !iconName) {\n return null;\n }\n\n return { prefix, iconName };\n}\n\nexport function buildIconRequestUrl({\n name,\n size,\n apiKey,\n baseUrl = DEFAULT_ICON_API_BASE_URL\n}: BuildIconRequestUrlOptions): string {\n const parsed = parseIconName(name);\n if (!parsed) {\n throw new Error(\n `Invalid icon name \\\"${name}\\\". Expected format \\\"prefix/name\\\" or \\\"prefix:name\\\".`\n );\n }\n\n if (!apiKey || !apiKey.trim()) {\n throw new Error(\"A non-empty apiKey is required to fetch icons.\");\n }\n\n const normalizedSize = Number.isFinite(size) && size > 0 ? Math.round(size) : 28;\n const normalizedBaseUrl = baseUrl.endsWith(\"/\") ? baseUrl.slice(0, -1) : baseUrl;\n\n const url = new URL(\n `/api/icon/${encodeURIComponent(parsed.prefix)}/${encodeURIComponent(parsed.iconName)}`,\n normalizedBaseUrl\n );\n\n url.searchParams.set(\"format\", \"svg\");\n url.searchParams.set(\"width\", String(normalizedSize));\n url.searchParams.set(\"height\", String(normalizedSize));\n url.searchParams.set(\"key\", apiKey);\n\n return url.toString();\n}\n","const HEX_BLACK_REGEX = /#000000\\b|#000\\b/i;\nconst BLACK_WORD_REGEX = /\\bblack\\b/i;\nconst RGB_BLACK_REGEX = /rgb\\(\\s*0\\s*,\\s*0\\s*,\\s*0\\s*\\)/i;\n\nconst STROKE_ATTR_REGEX = /stroke=[\"']([^\"']+)[\"']/gi;\nconst FILL_ATTR_REGEX = /fill=[\"']([^\"']+)[\"']/gi;\nconst STYLE_ATTR_REGEX = /style=[\"']([^\"']+)[\"']/gi;\n\nfunction normalizeColorToken(value: string): string {\n const trimmed = value.trim();\n if (\n HEX_BLACK_REGEX.test(trimmed) ||\n BLACK_WORD_REGEX.test(trimmed) ||\n RGB_BLACK_REGEX.test(trimmed)\n ) {\n return \"currentColor\";\n }\n return trimmed;\n}\n\nfunction normalizeStyleInline(style: string): string {\n return style\n .split(\";\")\n .map((segment) => segment.trim())\n .filter(Boolean)\n .map((segment) => {\n const [rawProperty, rawValue] = segment.split(\":\");\n if (!rawProperty || !rawValue) {\n return segment;\n }\n const property = rawProperty.trim().toLowerCase();\n if (property !== \"stroke\" && property !== \"fill\") {\n return segment;\n }\n const value = normalizeColorToken(rawValue);\n return `${rawProperty.trim()}: ${value}`;\n })\n .join(\"; \");\n}\n\nexport function processSvgForCurrentColor(svg: string): string {\n let processed = svg;\n\n processed = processed.replace(STROKE_ATTR_REGEX, (_full, value: string) => {\n const nextValue = normalizeColorToken(value);\n return `stroke=\"${nextValue}\"`;\n });\n\n processed = processed.replace(FILL_ATTR_REGEX, (_full, value: string) => {\n const trimmed = value.trim();\n if (trimmed.toLowerCase() === \"none\") {\n return `fill=\"${trimmed}\"`;\n }\n const nextValue = normalizeColorToken(trimmed);\n return `fill=\"${nextValue}\"`;\n });\n\n processed = processed.replace(STYLE_ATTR_REGEX, (_full, value: string) => {\n const normalized = normalizeStyleInline(value);\n return `style=\"${normalized}\"`;\n });\n\n return processed;\n}\n","import type { FetchIconSvgOptions } from \"../types\";\nimport { processSvgForCurrentColor } from \"./svg\";\n\nconst svgCache = new Map<string, string>();\nconst inFlight = new Map<string, Promise<string>>();\n\nexport function clearIconCache(): void {\n svgCache.clear();\n inFlight.clear();\n}\n\nexport async function fetchIconSvg(\n url: string,\n { fetcher = fetch }: FetchIconSvgOptions = {}\n): Promise<string> {\n const cached = svgCache.get(url);\n if (cached) {\n return cached;\n }\n\n const inflightRequest = inFlight.get(url);\n if (inflightRequest) {\n return inflightRequest;\n }\n\n const request = (async () => {\n const response = await fetcher(url);\n if (!response.ok) {\n throw new Error(`Failed to fetch icon: ${response.status}`);\n }\n\n const rawSvg = await response.text();\n const processedSvg = processSvgForCurrentColor(rawSvg);\n svgCache.set(url, processedSvg);\n return processedSvg;\n })();\n\n inFlight.set(url, request);\n\n try {\n return await request;\n } finally {\n inFlight.delete(url);\n }\n}\n","\"use client\";\n\nimport * as React from \"react\";\n\nimport type { IconProps } from \"../types\";\nimport { buildIconRequestUrl, fetchIconSvg, parseIconName } from \"../utils\";\n\nfunction joinClassNames(...classNames: Array<string | undefined>): string {\n return classNames.filter(Boolean).join(\" \");\n}\n\nexport function Icon({\n name,\n size = 28,\n color,\n className,\n alt,\n apiKey,\n baseUrl,\n fallback\n}: IconProps) {\n const [svgContent, setSvgContent] = React.useState<string | null>(null);\n const [hasError, setHasError] = React.useState(false);\n\n const parsedName = React.useMemo(() => parseIconName(name), [name]);\n\n const requestUrl = React.useMemo(() => {\n if (!parsedName) {\n return null;\n }\n\n try {\n return buildIconRequestUrl({ name, size, apiKey, baseUrl });\n } catch {\n return null;\n }\n }, [apiKey, baseUrl, name, parsedName, size]);\n\n React.useEffect(() => {\n let disposed = false;\n\n if (!requestUrl) {\n setSvgContent(null);\n setHasError(true);\n return;\n }\n\n setHasError(false);\n setSvgContent(null);\n\n fetchIconSvg(requestUrl)\n .then((svg) => {\n if (!disposed) {\n setSvgContent(svg);\n }\n })\n .catch(() => {\n if (!disposed) {\n setHasError(true);\n }\n });\n\n return () => {\n disposed = true;\n };\n }, [requestUrl]);\n\n const ariaLabel = alt || parsedName?.iconName;\n const a11yProps = ariaLabel\n ? ({ role: \"img\", \"aria-label\": ariaLabel } as const)\n : ({ \"aria-hidden\": true } as const);\n\n if (!svgContent) {\n return (\n <span\n className={joinClassNames(\"inline-block\", className)}\n style={{ width: size, height: size }}\n data-state={hasError ? \"error\" : \"loading\"}\n {...a11yProps}\n >\n {hasError ? fallback : null}\n </span>\n );\n }\n\n return (\n <span\n className={joinClassNames(\"inline-flex items-center justify-center\", className)}\n style={{\n width: size,\n height: size,\n color: color || \"inherit\"\n }}\n data-state=\"ready\"\n dangerouslySetInnerHTML={{ __html: svgContent }}\n {...a11yProps}\n />\n );\n}\n\nexport const DynamicIcon = Icon;\n"]} | ||
| {"version":3,"sources":["../../src/utils/iconUrl.ts","../../src/utils/svg.ts","../../src/utils/fetchIconSvg.ts","../../src/core/Icon.tsx"],"names":["React","jsx"],"mappings":"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","file":"Icon.cjs","sourcesContent":["import type { BuildIconRequestUrlOptions, ParsedIconName } from \"../types\";\n\nexport const DEFAULT_ICON_API_BASE_URL = \"https://icons.opensite.ai\";\n\nexport function parseIconName(name: string): ParsedIconName | null {\n if (!name || typeof name !== \"string\") {\n return null;\n }\n\n const trimmed = name.trim();\n if (!trimmed) {\n return null;\n }\n\n const slashIndex = trimmed.indexOf(\"/\");\n const colonIndex = trimmed.indexOf(\":\");\n\n let separatorIndex = -1;\n if (slashIndex >= 0 && colonIndex >= 0) {\n separatorIndex = Math.min(slashIndex, colonIndex);\n } else if (slashIndex >= 0) {\n separatorIndex = slashIndex;\n } else if (colonIndex >= 0) {\n separatorIndex = colonIndex;\n }\n\n if (separatorIndex <= 0 || separatorIndex >= trimmed.length - 1) {\n return null;\n }\n\n const prefix = trimmed.slice(0, separatorIndex).trim();\n const iconName = trimmed.slice(separatorIndex + 1).trim();\n\n if (!prefix || !iconName) {\n return null;\n }\n\n return { prefix, iconName };\n}\n\nexport function buildIconRequestUrl({\n name,\n size,\n apiKey,\n baseUrl = DEFAULT_ICON_API_BASE_URL\n}: BuildIconRequestUrlOptions): string {\n const parsed = parseIconName(name);\n if (!parsed) {\n throw new Error(\n `Invalid icon name \\\"${name}\\\". Expected format \\\"prefix/name\\\" or \\\"prefix:name\\\".`\n );\n }\n\n if (!apiKey || !apiKey.trim()) {\n throw new Error(\"A non-empty apiKey is required to fetch icons.\");\n }\n\n const normalizedSize = Number.isFinite(size) && size > 0 ? Math.round(size) : 28;\n const normalizedBaseUrl = baseUrl.endsWith(\"/\") ? baseUrl.slice(0, -1) : baseUrl;\n\n const url = new URL(\n `/api/icon/${encodeURIComponent(parsed.prefix)}/${encodeURIComponent(parsed.iconName)}`,\n normalizedBaseUrl\n );\n\n url.searchParams.set(\"format\", \"svg\");\n url.searchParams.set(\"width\", String(normalizedSize));\n url.searchParams.set(\"height\", String(normalizedSize));\n url.searchParams.set(\"key\", apiKey);\n\n return url.toString();\n}\n","const HEX_BLACK_REGEX = /#000000\\b|#000\\b/i;\nconst BLACK_WORD_REGEX = /\\bblack\\b/i;\nconst RGB_BLACK_REGEX = /rgb\\(\\s*0\\s*,\\s*0\\s*,\\s*0\\s*\\)/i;\n\nconst STROKE_ATTR_REGEX = /stroke=[\"']([^\"']+)[\"']/gi;\nconst FILL_ATTR_REGEX = /fill=[\"']([^\"']+)[\"']/gi;\nconst STYLE_ATTR_REGEX = /style=[\"']([^\"']+)[\"']/gi;\n\nfunction normalizeColorToken(value: string): string {\n const trimmed = value.trim();\n if (\n HEX_BLACK_REGEX.test(trimmed) ||\n BLACK_WORD_REGEX.test(trimmed) ||\n RGB_BLACK_REGEX.test(trimmed)\n ) {\n return \"currentColor\";\n }\n return trimmed;\n}\n\nfunction normalizeStyleInline(style: string): string {\n return style\n .split(\";\")\n .map((segment) => segment.trim())\n .filter(Boolean)\n .map((segment) => {\n const [rawProperty, rawValue] = segment.split(\":\");\n if (!rawProperty || !rawValue) {\n return segment;\n }\n const property = rawProperty.trim().toLowerCase();\n if (property !== \"stroke\" && property !== \"fill\") {\n return segment;\n }\n const value = normalizeColorToken(rawValue);\n return `${rawProperty.trim()}: ${value}`;\n })\n .join(\"; \");\n}\n\nexport function processSvgForCurrentColor(svg: string): string {\n let processed = svg;\n\n processed = processed.replace(STROKE_ATTR_REGEX, (_full, value: string) => {\n const nextValue = normalizeColorToken(value);\n return `stroke=\"${nextValue}\"`;\n });\n\n processed = processed.replace(FILL_ATTR_REGEX, (_full, value: string) => {\n const trimmed = value.trim();\n if (trimmed.toLowerCase() === \"none\") {\n return `fill=\"${trimmed}\"`;\n }\n const nextValue = normalizeColorToken(trimmed);\n return `fill=\"${nextValue}\"`;\n });\n\n processed = processed.replace(STYLE_ATTR_REGEX, (_full, value: string) => {\n const normalized = normalizeStyleInline(value);\n return `style=\"${normalized}\"`;\n });\n\n return processed;\n}\n","import type { FetchIconSvgOptions } from \"../types\";\nimport { processSvgForCurrentColor } from \"./svg\";\n\nconst svgCache = new Map<string, string>();\nconst inFlight = new Map<string, Promise<string>>();\n\nexport function clearIconCache(): void {\n svgCache.clear();\n inFlight.clear();\n}\n\nexport async function fetchIconSvg(\n url: string,\n { fetcher = fetch }: FetchIconSvgOptions = {}\n): Promise<string> {\n const cached = svgCache.get(url);\n if (cached) {\n return cached;\n }\n\n const inflightRequest = inFlight.get(url);\n if (inflightRequest) {\n return inflightRequest;\n }\n\n const request = (async () => {\n const response = await fetcher(url);\n if (!response.ok) {\n throw new Error(`Failed to fetch icon: ${response.status}`);\n }\n\n const rawSvg = await response.text();\n const processedSvg = processSvgForCurrentColor(rawSvg);\n svgCache.set(url, processedSvg);\n return processedSvg;\n })();\n\n inFlight.set(url, request);\n\n try {\n return await request;\n } finally {\n inFlight.delete(url);\n }\n}\n","\"use client\";\n\nimport * as React from \"react\";\n\nimport type { IconProps } from \"../types\";\nimport { buildIconRequestUrl, fetchIconSvg, parseIconName } from \"../utils\";\n\nfunction joinClassNames(...classNames: Array<string | undefined>): string {\n return classNames.filter(Boolean).join(\" \");\n}\n\n// Check if we're in a test environment\nconst isTestEnvironment = typeof process !== \"undefined\" && process.env.NODE_ENV === \"test\";\n\nexport function Icon({\n name,\n size = 28,\n color,\n className,\n alt,\n apiKey,\n baseUrl,\n fallback\n}: IconProps) {\n const [state, setState] = React.useState<{\n svg: string | null;\n error: boolean;\n }>({ svg: null, error: false });\n\n const parsedName = React.useMemo(() => parseIconName(name), [name]);\n\n const requestUrl = React.useMemo(() => {\n if (!parsedName) {\n return null;\n }\n\n try {\n return buildIconRequestUrl({ name, size, apiKey, baseUrl });\n } catch {\n return null;\n }\n }, [apiKey, baseUrl, name, parsedName, size]);\n\n React.useEffect(() => {\n let disposed = false;\n\n if (!requestUrl) {\n // Only set error state if not already in error state\n setState((prev) => (prev.error && !prev.svg ? prev : { svg: null, error: true }));\n return;\n }\n\n // Reset state only if needed\n setState((prev) => (!prev.svg && !prev.error ? prev : { svg: null, error: false }));\n\n // In test environment, use a microtask to avoid act warnings\n const loadIcon = async () => {\n try {\n const svg = await fetchIconSvg(requestUrl);\n if (!disposed) {\n if (isTestEnvironment) {\n // Use queueMicrotask to ensure state updates happen in the next tick\n queueMicrotask(() => {\n if (!disposed) {\n setState({ svg, error: false });\n }\n });\n } else {\n setState({ svg, error: false });\n }\n }\n } catch {\n if (!disposed) {\n if (isTestEnvironment) {\n queueMicrotask(() => {\n if (!disposed) {\n setState({ svg: null, error: true });\n }\n });\n } else {\n setState({ svg: null, error: true });\n }\n }\n }\n };\n\n loadIcon();\n\n return () => {\n disposed = true;\n };\n }, [requestUrl]);\n\n const ariaLabel = alt || parsedName?.iconName;\n const a11yProps = ariaLabel\n ? ({ role: \"img\", \"aria-label\": ariaLabel } as const)\n : ({ \"aria-hidden\": true } as const);\n\n if (!state.svg) {\n return (\n <span\n className={joinClassNames(\"inline-block\", className)}\n style={{ width: size, height: size }}\n data-state={state.error ? \"error\" : \"loading\"}\n {...a11yProps}\n >\n {state.error ? fallback : null}\n </span>\n );\n }\n\n return (\n <span\n className={joinClassNames(\"inline-flex items-center justify-center\", className)}\n style={{\n width: size,\n height: size,\n color: color || \"inherit\"\n }}\n data-state=\"ready\"\n dangerouslySetInnerHTML={{ __html: state.svg }}\n {...a11yProps}\n />\n );\n}\n\nexport const DynamicIcon = Icon;\n"]} |
+36
-18
@@ -143,2 +143,3 @@ import * as React from 'react'; | ||
| } | ||
| var isTestEnvironment = typeof process !== "undefined" && process.env.NODE_ENV === "test"; | ||
| function Icon({ | ||
@@ -154,4 +155,3 @@ name, | ||
| }) { | ||
| const [svgContent, setSvgContent] = React.useState(null); | ||
| const [hasError, setHasError] = React.useState(false); | ||
| const [state, setState] = React.useState({ svg: null, error: false }); | ||
| const parsedName = React.useMemo(() => parseIconName(name), [name]); | ||
@@ -171,17 +171,35 @@ const requestUrl = React.useMemo(() => { | ||
| if (!requestUrl) { | ||
| setSvgContent(null); | ||
| setHasError(true); | ||
| setState((prev) => prev.error && !prev.svg ? prev : { svg: null, error: true }); | ||
| return; | ||
| } | ||
| setHasError(false); | ||
| setSvgContent(null); | ||
| fetchIconSvg(requestUrl).then((svg) => { | ||
| if (!disposed) { | ||
| setSvgContent(svg); | ||
| setState((prev) => !prev.svg && !prev.error ? prev : { svg: null, error: false }); | ||
| const loadIcon = async () => { | ||
| try { | ||
| const svg = await fetchIconSvg(requestUrl); | ||
| if (!disposed) { | ||
| if (isTestEnvironment) { | ||
| queueMicrotask(() => { | ||
| if (!disposed) { | ||
| setState({ svg, error: false }); | ||
| } | ||
| }); | ||
| } else { | ||
| setState({ svg, error: false }); | ||
| } | ||
| } | ||
| } catch { | ||
| if (!disposed) { | ||
| if (isTestEnvironment) { | ||
| queueMicrotask(() => { | ||
| if (!disposed) { | ||
| setState({ svg: null, error: true }); | ||
| } | ||
| }); | ||
| } else { | ||
| setState({ svg: null, error: true }); | ||
| } | ||
| } | ||
| } | ||
| }).catch(() => { | ||
| if (!disposed) { | ||
| setHasError(true); | ||
| } | ||
| }); | ||
| }; | ||
| loadIcon(); | ||
| return () => { | ||
@@ -193,3 +211,3 @@ disposed = true; | ||
| const a11yProps = ariaLabel ? { role: "img", "aria-label": ariaLabel } : { "aria-hidden": true }; | ||
| if (!svgContent) { | ||
| if (!state.svg) { | ||
| return /* @__PURE__ */ jsx( | ||
@@ -200,5 +218,5 @@ "span", | ||
| style: { width: size, height: size }, | ||
| "data-state": hasError ? "error" : "loading", | ||
| "data-state": state.error ? "error" : "loading", | ||
| ...a11yProps, | ||
| children: hasError ? fallback : null | ||
| children: state.error ? fallback : null | ||
| } | ||
@@ -217,3 +235,3 @@ ); | ||
| "data-state": "ready", | ||
| dangerouslySetInnerHTML: { __html: svgContent }, | ||
| dangerouslySetInnerHTML: { __html: state.svg }, | ||
| ...a11yProps | ||
@@ -220,0 +238,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../../src/utils/iconUrl.ts","../../src/utils/svg.ts","../../src/utils/fetchIconSvg.ts","../../src/core/Icon.tsx"],"names":[],"mappings":"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","file":"Icon.js","sourcesContent":["import type { BuildIconRequestUrlOptions, ParsedIconName } from \"../types\";\n\nexport const DEFAULT_ICON_API_BASE_URL = \"https://icons.opensite.ai\";\n\nexport function parseIconName(name: string): ParsedIconName | null {\n if (!name || typeof name !== \"string\") {\n return null;\n }\n\n const trimmed = name.trim();\n if (!trimmed) {\n return null;\n }\n\n const slashIndex = trimmed.indexOf(\"/\");\n const colonIndex = trimmed.indexOf(\":\");\n\n let separatorIndex = -1;\n if (slashIndex >= 0 && colonIndex >= 0) {\n separatorIndex = Math.min(slashIndex, colonIndex);\n } else if (slashIndex >= 0) {\n separatorIndex = slashIndex;\n } else if (colonIndex >= 0) {\n separatorIndex = colonIndex;\n }\n\n if (separatorIndex <= 0 || separatorIndex >= trimmed.length - 1) {\n return null;\n }\n\n const prefix = trimmed.slice(0, separatorIndex).trim();\n const iconName = trimmed.slice(separatorIndex + 1).trim();\n\n if (!prefix || !iconName) {\n return null;\n }\n\n return { prefix, iconName };\n}\n\nexport function buildIconRequestUrl({\n name,\n size,\n apiKey,\n baseUrl = DEFAULT_ICON_API_BASE_URL\n}: BuildIconRequestUrlOptions): string {\n const parsed = parseIconName(name);\n if (!parsed) {\n throw new Error(\n `Invalid icon name \\\"${name}\\\". Expected format \\\"prefix/name\\\" or \\\"prefix:name\\\".`\n );\n }\n\n if (!apiKey || !apiKey.trim()) {\n throw new Error(\"A non-empty apiKey is required to fetch icons.\");\n }\n\n const normalizedSize = Number.isFinite(size) && size > 0 ? Math.round(size) : 28;\n const normalizedBaseUrl = baseUrl.endsWith(\"/\") ? baseUrl.slice(0, -1) : baseUrl;\n\n const url = new URL(\n `/api/icon/${encodeURIComponent(parsed.prefix)}/${encodeURIComponent(parsed.iconName)}`,\n normalizedBaseUrl\n );\n\n url.searchParams.set(\"format\", \"svg\");\n url.searchParams.set(\"width\", String(normalizedSize));\n url.searchParams.set(\"height\", String(normalizedSize));\n url.searchParams.set(\"key\", apiKey);\n\n return url.toString();\n}\n","const HEX_BLACK_REGEX = /#000000\\b|#000\\b/i;\nconst BLACK_WORD_REGEX = /\\bblack\\b/i;\nconst RGB_BLACK_REGEX = /rgb\\(\\s*0\\s*,\\s*0\\s*,\\s*0\\s*\\)/i;\n\nconst STROKE_ATTR_REGEX = /stroke=[\"']([^\"']+)[\"']/gi;\nconst FILL_ATTR_REGEX = /fill=[\"']([^\"']+)[\"']/gi;\nconst STYLE_ATTR_REGEX = /style=[\"']([^\"']+)[\"']/gi;\n\nfunction normalizeColorToken(value: string): string {\n const trimmed = value.trim();\n if (\n HEX_BLACK_REGEX.test(trimmed) ||\n BLACK_WORD_REGEX.test(trimmed) ||\n RGB_BLACK_REGEX.test(trimmed)\n ) {\n return \"currentColor\";\n }\n return trimmed;\n}\n\nfunction normalizeStyleInline(style: string): string {\n return style\n .split(\";\")\n .map((segment) => segment.trim())\n .filter(Boolean)\n .map((segment) => {\n const [rawProperty, rawValue] = segment.split(\":\");\n if (!rawProperty || !rawValue) {\n return segment;\n }\n const property = rawProperty.trim().toLowerCase();\n if (property !== \"stroke\" && property !== \"fill\") {\n return segment;\n }\n const value = normalizeColorToken(rawValue);\n return `${rawProperty.trim()}: ${value}`;\n })\n .join(\"; \");\n}\n\nexport function processSvgForCurrentColor(svg: string): string {\n let processed = svg;\n\n processed = processed.replace(STROKE_ATTR_REGEX, (_full, value: string) => {\n const nextValue = normalizeColorToken(value);\n return `stroke=\"${nextValue}\"`;\n });\n\n processed = processed.replace(FILL_ATTR_REGEX, (_full, value: string) => {\n const trimmed = value.trim();\n if (trimmed.toLowerCase() === \"none\") {\n return `fill=\"${trimmed}\"`;\n }\n const nextValue = normalizeColorToken(trimmed);\n return `fill=\"${nextValue}\"`;\n });\n\n processed = processed.replace(STYLE_ATTR_REGEX, (_full, value: string) => {\n const normalized = normalizeStyleInline(value);\n return `style=\"${normalized}\"`;\n });\n\n return processed;\n}\n","import type { FetchIconSvgOptions } from \"../types\";\nimport { processSvgForCurrentColor } from \"./svg\";\n\nconst svgCache = new Map<string, string>();\nconst inFlight = new Map<string, Promise<string>>();\n\nexport function clearIconCache(): void {\n svgCache.clear();\n inFlight.clear();\n}\n\nexport async function fetchIconSvg(\n url: string,\n { fetcher = fetch }: FetchIconSvgOptions = {}\n): Promise<string> {\n const cached = svgCache.get(url);\n if (cached) {\n return cached;\n }\n\n const inflightRequest = inFlight.get(url);\n if (inflightRequest) {\n return inflightRequest;\n }\n\n const request = (async () => {\n const response = await fetcher(url);\n if (!response.ok) {\n throw new Error(`Failed to fetch icon: ${response.status}`);\n }\n\n const rawSvg = await response.text();\n const processedSvg = processSvgForCurrentColor(rawSvg);\n svgCache.set(url, processedSvg);\n return processedSvg;\n })();\n\n inFlight.set(url, request);\n\n try {\n return await request;\n } finally {\n inFlight.delete(url);\n }\n}\n","\"use client\";\n\nimport * as React from \"react\";\n\nimport type { IconProps } from \"../types\";\nimport { buildIconRequestUrl, fetchIconSvg, parseIconName } from \"../utils\";\n\nfunction joinClassNames(...classNames: Array<string | undefined>): string {\n return classNames.filter(Boolean).join(\" \");\n}\n\nexport function Icon({\n name,\n size = 28,\n color,\n className,\n alt,\n apiKey,\n baseUrl,\n fallback\n}: IconProps) {\n const [svgContent, setSvgContent] = React.useState<string | null>(null);\n const [hasError, setHasError] = React.useState(false);\n\n const parsedName = React.useMemo(() => parseIconName(name), [name]);\n\n const requestUrl = React.useMemo(() => {\n if (!parsedName) {\n return null;\n }\n\n try {\n return buildIconRequestUrl({ name, size, apiKey, baseUrl });\n } catch {\n return null;\n }\n }, [apiKey, baseUrl, name, parsedName, size]);\n\n React.useEffect(() => {\n let disposed = false;\n\n if (!requestUrl) {\n setSvgContent(null);\n setHasError(true);\n return;\n }\n\n setHasError(false);\n setSvgContent(null);\n\n fetchIconSvg(requestUrl)\n .then((svg) => {\n if (!disposed) {\n setSvgContent(svg);\n }\n })\n .catch(() => {\n if (!disposed) {\n setHasError(true);\n }\n });\n\n return () => {\n disposed = true;\n };\n }, [requestUrl]);\n\n const ariaLabel = alt || parsedName?.iconName;\n const a11yProps = ariaLabel\n ? ({ role: \"img\", \"aria-label\": ariaLabel } as const)\n : ({ \"aria-hidden\": true } as const);\n\n if (!svgContent) {\n return (\n <span\n className={joinClassNames(\"inline-block\", className)}\n style={{ width: size, height: size }}\n data-state={hasError ? \"error\" : \"loading\"}\n {...a11yProps}\n >\n {hasError ? fallback : null}\n </span>\n );\n }\n\n return (\n <span\n className={joinClassNames(\"inline-flex items-center justify-center\", className)}\n style={{\n width: size,\n height: size,\n color: color || \"inherit\"\n }}\n data-state=\"ready\"\n dangerouslySetInnerHTML={{ __html: svgContent }}\n {...a11yProps}\n />\n );\n}\n\nexport const DynamicIcon = Icon;\n"]} | ||
| {"version":3,"sources":["../../src/utils/iconUrl.ts","../../src/utils/svg.ts","../../src/utils/fetchIconSvg.ts","../../src/core/Icon.tsx"],"names":[],"mappings":"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","file":"Icon.js","sourcesContent":["import type { BuildIconRequestUrlOptions, ParsedIconName } from \"../types\";\n\nexport const DEFAULT_ICON_API_BASE_URL = \"https://icons.opensite.ai\";\n\nexport function parseIconName(name: string): ParsedIconName | null {\n if (!name || typeof name !== \"string\") {\n return null;\n }\n\n const trimmed = name.trim();\n if (!trimmed) {\n return null;\n }\n\n const slashIndex = trimmed.indexOf(\"/\");\n const colonIndex = trimmed.indexOf(\":\");\n\n let separatorIndex = -1;\n if (slashIndex >= 0 && colonIndex >= 0) {\n separatorIndex = Math.min(slashIndex, colonIndex);\n } else if (slashIndex >= 0) {\n separatorIndex = slashIndex;\n } else if (colonIndex >= 0) {\n separatorIndex = colonIndex;\n }\n\n if (separatorIndex <= 0 || separatorIndex >= trimmed.length - 1) {\n return null;\n }\n\n const prefix = trimmed.slice(0, separatorIndex).trim();\n const iconName = trimmed.slice(separatorIndex + 1).trim();\n\n if (!prefix || !iconName) {\n return null;\n }\n\n return { prefix, iconName };\n}\n\nexport function buildIconRequestUrl({\n name,\n size,\n apiKey,\n baseUrl = DEFAULT_ICON_API_BASE_URL\n}: BuildIconRequestUrlOptions): string {\n const parsed = parseIconName(name);\n if (!parsed) {\n throw new Error(\n `Invalid icon name \\\"${name}\\\". Expected format \\\"prefix/name\\\" or \\\"prefix:name\\\".`\n );\n }\n\n if (!apiKey || !apiKey.trim()) {\n throw new Error(\"A non-empty apiKey is required to fetch icons.\");\n }\n\n const normalizedSize = Number.isFinite(size) && size > 0 ? Math.round(size) : 28;\n const normalizedBaseUrl = baseUrl.endsWith(\"/\") ? baseUrl.slice(0, -1) : baseUrl;\n\n const url = new URL(\n `/api/icon/${encodeURIComponent(parsed.prefix)}/${encodeURIComponent(parsed.iconName)}`,\n normalizedBaseUrl\n );\n\n url.searchParams.set(\"format\", \"svg\");\n url.searchParams.set(\"width\", String(normalizedSize));\n url.searchParams.set(\"height\", String(normalizedSize));\n url.searchParams.set(\"key\", apiKey);\n\n return url.toString();\n}\n","const HEX_BLACK_REGEX = /#000000\\b|#000\\b/i;\nconst BLACK_WORD_REGEX = /\\bblack\\b/i;\nconst RGB_BLACK_REGEX = /rgb\\(\\s*0\\s*,\\s*0\\s*,\\s*0\\s*\\)/i;\n\nconst STROKE_ATTR_REGEX = /stroke=[\"']([^\"']+)[\"']/gi;\nconst FILL_ATTR_REGEX = /fill=[\"']([^\"']+)[\"']/gi;\nconst STYLE_ATTR_REGEX = /style=[\"']([^\"']+)[\"']/gi;\n\nfunction normalizeColorToken(value: string): string {\n const trimmed = value.trim();\n if (\n HEX_BLACK_REGEX.test(trimmed) ||\n BLACK_WORD_REGEX.test(trimmed) ||\n RGB_BLACK_REGEX.test(trimmed)\n ) {\n return \"currentColor\";\n }\n return trimmed;\n}\n\nfunction normalizeStyleInline(style: string): string {\n return style\n .split(\";\")\n .map((segment) => segment.trim())\n .filter(Boolean)\n .map((segment) => {\n const [rawProperty, rawValue] = segment.split(\":\");\n if (!rawProperty || !rawValue) {\n return segment;\n }\n const property = rawProperty.trim().toLowerCase();\n if (property !== \"stroke\" && property !== \"fill\") {\n return segment;\n }\n const value = normalizeColorToken(rawValue);\n return `${rawProperty.trim()}: ${value}`;\n })\n .join(\"; \");\n}\n\nexport function processSvgForCurrentColor(svg: string): string {\n let processed = svg;\n\n processed = processed.replace(STROKE_ATTR_REGEX, (_full, value: string) => {\n const nextValue = normalizeColorToken(value);\n return `stroke=\"${nextValue}\"`;\n });\n\n processed = processed.replace(FILL_ATTR_REGEX, (_full, value: string) => {\n const trimmed = value.trim();\n if (trimmed.toLowerCase() === \"none\") {\n return `fill=\"${trimmed}\"`;\n }\n const nextValue = normalizeColorToken(trimmed);\n return `fill=\"${nextValue}\"`;\n });\n\n processed = processed.replace(STYLE_ATTR_REGEX, (_full, value: string) => {\n const normalized = normalizeStyleInline(value);\n return `style=\"${normalized}\"`;\n });\n\n return processed;\n}\n","import type { FetchIconSvgOptions } from \"../types\";\nimport { processSvgForCurrentColor } from \"./svg\";\n\nconst svgCache = new Map<string, string>();\nconst inFlight = new Map<string, Promise<string>>();\n\nexport function clearIconCache(): void {\n svgCache.clear();\n inFlight.clear();\n}\n\nexport async function fetchIconSvg(\n url: string,\n { fetcher = fetch }: FetchIconSvgOptions = {}\n): Promise<string> {\n const cached = svgCache.get(url);\n if (cached) {\n return cached;\n }\n\n const inflightRequest = inFlight.get(url);\n if (inflightRequest) {\n return inflightRequest;\n }\n\n const request = (async () => {\n const response = await fetcher(url);\n if (!response.ok) {\n throw new Error(`Failed to fetch icon: ${response.status}`);\n }\n\n const rawSvg = await response.text();\n const processedSvg = processSvgForCurrentColor(rawSvg);\n svgCache.set(url, processedSvg);\n return processedSvg;\n })();\n\n inFlight.set(url, request);\n\n try {\n return await request;\n } finally {\n inFlight.delete(url);\n }\n}\n","\"use client\";\n\nimport * as React from \"react\";\n\nimport type { IconProps } from \"../types\";\nimport { buildIconRequestUrl, fetchIconSvg, parseIconName } from \"../utils\";\n\nfunction joinClassNames(...classNames: Array<string | undefined>): string {\n return classNames.filter(Boolean).join(\" \");\n}\n\n// Check if we're in a test environment\nconst isTestEnvironment = typeof process !== \"undefined\" && process.env.NODE_ENV === \"test\";\n\nexport function Icon({\n name,\n size = 28,\n color,\n className,\n alt,\n apiKey,\n baseUrl,\n fallback\n}: IconProps) {\n const [state, setState] = React.useState<{\n svg: string | null;\n error: boolean;\n }>({ svg: null, error: false });\n\n const parsedName = React.useMemo(() => parseIconName(name), [name]);\n\n const requestUrl = React.useMemo(() => {\n if (!parsedName) {\n return null;\n }\n\n try {\n return buildIconRequestUrl({ name, size, apiKey, baseUrl });\n } catch {\n return null;\n }\n }, [apiKey, baseUrl, name, parsedName, size]);\n\n React.useEffect(() => {\n let disposed = false;\n\n if (!requestUrl) {\n // Only set error state if not already in error state\n setState((prev) => (prev.error && !prev.svg ? prev : { svg: null, error: true }));\n return;\n }\n\n // Reset state only if needed\n setState((prev) => (!prev.svg && !prev.error ? prev : { svg: null, error: false }));\n\n // In test environment, use a microtask to avoid act warnings\n const loadIcon = async () => {\n try {\n const svg = await fetchIconSvg(requestUrl);\n if (!disposed) {\n if (isTestEnvironment) {\n // Use queueMicrotask to ensure state updates happen in the next tick\n queueMicrotask(() => {\n if (!disposed) {\n setState({ svg, error: false });\n }\n });\n } else {\n setState({ svg, error: false });\n }\n }\n } catch {\n if (!disposed) {\n if (isTestEnvironment) {\n queueMicrotask(() => {\n if (!disposed) {\n setState({ svg: null, error: true });\n }\n });\n } else {\n setState({ svg: null, error: true });\n }\n }\n }\n };\n\n loadIcon();\n\n return () => {\n disposed = true;\n };\n }, [requestUrl]);\n\n const ariaLabel = alt || parsedName?.iconName;\n const a11yProps = ariaLabel\n ? ({ role: \"img\", \"aria-label\": ariaLabel } as const)\n : ({ \"aria-hidden\": true } as const);\n\n if (!state.svg) {\n return (\n <span\n className={joinClassNames(\"inline-block\", className)}\n style={{ width: size, height: size }}\n data-state={state.error ? \"error\" : \"loading\"}\n {...a11yProps}\n >\n {state.error ? fallback : null}\n </span>\n );\n }\n\n return (\n <span\n className={joinClassNames(\"inline-flex items-center justify-center\", className)}\n style={{\n width: size,\n height: size,\n color: color || \"inherit\"\n }}\n data-state=\"ready\"\n dangerouslySetInnerHTML={{ __html: state.svg }}\n {...a11yProps}\n />\n );\n}\n\nexport const DynamicIcon = Icon;\n"]} |
+36
-18
@@ -167,2 +167,3 @@ 'use strict'; | ||
| } | ||
| var isTestEnvironment = typeof process !== "undefined" && process.env.NODE_ENV === "test"; | ||
| function Icon({ | ||
@@ -178,4 +179,3 @@ name, | ||
| }) { | ||
| const [svgContent, setSvgContent] = React__namespace.useState(null); | ||
| const [hasError, setHasError] = React__namespace.useState(false); | ||
| const [state, setState] = React__namespace.useState({ svg: null, error: false }); | ||
| const parsedName = React__namespace.useMemo(() => parseIconName(name), [name]); | ||
@@ -195,17 +195,35 @@ const requestUrl = React__namespace.useMemo(() => { | ||
| if (!requestUrl) { | ||
| setSvgContent(null); | ||
| setHasError(true); | ||
| setState((prev) => prev.error && !prev.svg ? prev : { svg: null, error: true }); | ||
| return; | ||
| } | ||
| setHasError(false); | ||
| setSvgContent(null); | ||
| fetchIconSvg(requestUrl).then((svg) => { | ||
| if (!disposed) { | ||
| setSvgContent(svg); | ||
| setState((prev) => !prev.svg && !prev.error ? prev : { svg: null, error: false }); | ||
| const loadIcon = async () => { | ||
| try { | ||
| const svg = await fetchIconSvg(requestUrl); | ||
| if (!disposed) { | ||
| if (isTestEnvironment) { | ||
| queueMicrotask(() => { | ||
| if (!disposed) { | ||
| setState({ svg, error: false }); | ||
| } | ||
| }); | ||
| } else { | ||
| setState({ svg, error: false }); | ||
| } | ||
| } | ||
| } catch { | ||
| if (!disposed) { | ||
| if (isTestEnvironment) { | ||
| queueMicrotask(() => { | ||
| if (!disposed) { | ||
| setState({ svg: null, error: true }); | ||
| } | ||
| }); | ||
| } else { | ||
| setState({ svg: null, error: true }); | ||
| } | ||
| } | ||
| } | ||
| }).catch(() => { | ||
| if (!disposed) { | ||
| setHasError(true); | ||
| } | ||
| }); | ||
| }; | ||
| loadIcon(); | ||
| return () => { | ||
@@ -217,3 +235,3 @@ disposed = true; | ||
| const a11yProps = ariaLabel ? { role: "img", "aria-label": ariaLabel } : { "aria-hidden": true }; | ||
| if (!svgContent) { | ||
| if (!state.svg) { | ||
| return /* @__PURE__ */ jsxRuntime.jsx( | ||
@@ -224,5 +242,5 @@ "span", | ||
| style: { width: size, height: size }, | ||
| "data-state": hasError ? "error" : "loading", | ||
| "data-state": state.error ? "error" : "loading", | ||
| ...a11yProps, | ||
| children: hasError ? fallback : null | ||
| children: state.error ? fallback : null | ||
| } | ||
@@ -241,3 +259,3 @@ ); | ||
| "data-state": "ready", | ||
| dangerouslySetInnerHTML: { __html: svgContent }, | ||
| dangerouslySetInnerHTML: { __html: state.svg }, | ||
| ...a11yProps | ||
@@ -244,0 +262,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../../src/utils/iconUrl.ts","../../src/utils/svg.ts","../../src/utils/fetchIconSvg.ts","../../src/core/Icon.tsx"],"names":["React","jsx"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { BuildIconRequestUrlOptions, ParsedIconName } from \"../types\";\n\nexport const DEFAULT_ICON_API_BASE_URL = \"https://icons.opensite.ai\";\n\nexport function parseIconName(name: string): ParsedIconName | null {\n if (!name || typeof name !== \"string\") {\n return null;\n }\n\n const trimmed = name.trim();\n if (!trimmed) {\n return null;\n }\n\n const slashIndex = trimmed.indexOf(\"/\");\n const colonIndex = trimmed.indexOf(\":\");\n\n let separatorIndex = -1;\n if (slashIndex >= 0 && colonIndex >= 0) {\n separatorIndex = Math.min(slashIndex, colonIndex);\n } else if (slashIndex >= 0) {\n separatorIndex = slashIndex;\n } else if (colonIndex >= 0) {\n separatorIndex = colonIndex;\n }\n\n if (separatorIndex <= 0 || separatorIndex >= trimmed.length - 1) {\n return null;\n }\n\n const prefix = trimmed.slice(0, separatorIndex).trim();\n const iconName = trimmed.slice(separatorIndex + 1).trim();\n\n if (!prefix || !iconName) {\n return null;\n }\n\n return { prefix, iconName };\n}\n\nexport function buildIconRequestUrl({\n name,\n size,\n apiKey,\n baseUrl = DEFAULT_ICON_API_BASE_URL\n}: BuildIconRequestUrlOptions): string {\n const parsed = parseIconName(name);\n if (!parsed) {\n throw new Error(\n `Invalid icon name \\\"${name}\\\". Expected format \\\"prefix/name\\\" or \\\"prefix:name\\\".`\n );\n }\n\n if (!apiKey || !apiKey.trim()) {\n throw new Error(\"A non-empty apiKey is required to fetch icons.\");\n }\n\n const normalizedSize = Number.isFinite(size) && size > 0 ? Math.round(size) : 28;\n const normalizedBaseUrl = baseUrl.endsWith(\"/\") ? baseUrl.slice(0, -1) : baseUrl;\n\n const url = new URL(\n `/api/icon/${encodeURIComponent(parsed.prefix)}/${encodeURIComponent(parsed.iconName)}`,\n normalizedBaseUrl\n );\n\n url.searchParams.set(\"format\", \"svg\");\n url.searchParams.set(\"width\", String(normalizedSize));\n url.searchParams.set(\"height\", String(normalizedSize));\n url.searchParams.set(\"key\", apiKey);\n\n return url.toString();\n}\n","const HEX_BLACK_REGEX = /#000000\\b|#000\\b/i;\nconst BLACK_WORD_REGEX = /\\bblack\\b/i;\nconst RGB_BLACK_REGEX = /rgb\\(\\s*0\\s*,\\s*0\\s*,\\s*0\\s*\\)/i;\n\nconst STROKE_ATTR_REGEX = /stroke=[\"']([^\"']+)[\"']/gi;\nconst FILL_ATTR_REGEX = /fill=[\"']([^\"']+)[\"']/gi;\nconst STYLE_ATTR_REGEX = /style=[\"']([^\"']+)[\"']/gi;\n\nfunction normalizeColorToken(value: string): string {\n const trimmed = value.trim();\n if (\n HEX_BLACK_REGEX.test(trimmed) ||\n BLACK_WORD_REGEX.test(trimmed) ||\n RGB_BLACK_REGEX.test(trimmed)\n ) {\n return \"currentColor\";\n }\n return trimmed;\n}\n\nfunction normalizeStyleInline(style: string): string {\n return style\n .split(\";\")\n .map((segment) => segment.trim())\n .filter(Boolean)\n .map((segment) => {\n const [rawProperty, rawValue] = segment.split(\":\");\n if (!rawProperty || !rawValue) {\n return segment;\n }\n const property = rawProperty.trim().toLowerCase();\n if (property !== \"stroke\" && property !== \"fill\") {\n return segment;\n }\n const value = normalizeColorToken(rawValue);\n return `${rawProperty.trim()}: ${value}`;\n })\n .join(\"; \");\n}\n\nexport function processSvgForCurrentColor(svg: string): string {\n let processed = svg;\n\n processed = processed.replace(STROKE_ATTR_REGEX, (_full, value: string) => {\n const nextValue = normalizeColorToken(value);\n return `stroke=\"${nextValue}\"`;\n });\n\n processed = processed.replace(FILL_ATTR_REGEX, (_full, value: string) => {\n const trimmed = value.trim();\n if (trimmed.toLowerCase() === \"none\") {\n return `fill=\"${trimmed}\"`;\n }\n const nextValue = normalizeColorToken(trimmed);\n return `fill=\"${nextValue}\"`;\n });\n\n processed = processed.replace(STYLE_ATTR_REGEX, (_full, value: string) => {\n const normalized = normalizeStyleInline(value);\n return `style=\"${normalized}\"`;\n });\n\n return processed;\n}\n","import type { FetchIconSvgOptions } from \"../types\";\nimport { processSvgForCurrentColor } from \"./svg\";\n\nconst svgCache = new Map<string, string>();\nconst inFlight = new Map<string, Promise<string>>();\n\nexport function clearIconCache(): void {\n svgCache.clear();\n inFlight.clear();\n}\n\nexport async function fetchIconSvg(\n url: string,\n { fetcher = fetch }: FetchIconSvgOptions = {}\n): Promise<string> {\n const cached = svgCache.get(url);\n if (cached) {\n return cached;\n }\n\n const inflightRequest = inFlight.get(url);\n if (inflightRequest) {\n return inflightRequest;\n }\n\n const request = (async () => {\n const response = await fetcher(url);\n if (!response.ok) {\n throw new Error(`Failed to fetch icon: ${response.status}`);\n }\n\n const rawSvg = await response.text();\n const processedSvg = processSvgForCurrentColor(rawSvg);\n svgCache.set(url, processedSvg);\n return processedSvg;\n })();\n\n inFlight.set(url, request);\n\n try {\n return await request;\n } finally {\n inFlight.delete(url);\n }\n}\n","\"use client\";\n\nimport * as React from \"react\";\n\nimport type { IconProps } from \"../types\";\nimport { buildIconRequestUrl, fetchIconSvg, parseIconName } from \"../utils\";\n\nfunction joinClassNames(...classNames: Array<string | undefined>): string {\n return classNames.filter(Boolean).join(\" \");\n}\n\nexport function Icon({\n name,\n size = 28,\n color,\n className,\n alt,\n apiKey,\n baseUrl,\n fallback\n}: IconProps) {\n const [svgContent, setSvgContent] = React.useState<string | null>(null);\n const [hasError, setHasError] = React.useState(false);\n\n const parsedName = React.useMemo(() => parseIconName(name), [name]);\n\n const requestUrl = React.useMemo(() => {\n if (!parsedName) {\n return null;\n }\n\n try {\n return buildIconRequestUrl({ name, size, apiKey, baseUrl });\n } catch {\n return null;\n }\n }, [apiKey, baseUrl, name, parsedName, size]);\n\n React.useEffect(() => {\n let disposed = false;\n\n if (!requestUrl) {\n setSvgContent(null);\n setHasError(true);\n return;\n }\n\n setHasError(false);\n setSvgContent(null);\n\n fetchIconSvg(requestUrl)\n .then((svg) => {\n if (!disposed) {\n setSvgContent(svg);\n }\n })\n .catch(() => {\n if (!disposed) {\n setHasError(true);\n }\n });\n\n return () => {\n disposed = true;\n };\n }, [requestUrl]);\n\n const ariaLabel = alt || parsedName?.iconName;\n const a11yProps = ariaLabel\n ? ({ role: \"img\", \"aria-label\": ariaLabel } as const)\n : ({ \"aria-hidden\": true } as const);\n\n if (!svgContent) {\n return (\n <span\n className={joinClassNames(\"inline-block\", className)}\n style={{ width: size, height: size }}\n data-state={hasError ? \"error\" : \"loading\"}\n {...a11yProps}\n >\n {hasError ? fallback : null}\n </span>\n );\n }\n\n return (\n <span\n className={joinClassNames(\"inline-flex items-center justify-center\", className)}\n style={{\n width: size,\n height: size,\n color: color || \"inherit\"\n }}\n data-state=\"ready\"\n dangerouslySetInnerHTML={{ __html: svgContent }}\n {...a11yProps}\n />\n );\n}\n\nexport const DynamicIcon = Icon;\n"]} | ||
| {"version":3,"sources":["../../src/utils/iconUrl.ts","../../src/utils/svg.ts","../../src/utils/fetchIconSvg.ts","../../src/core/Icon.tsx"],"names":["React","jsx"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { BuildIconRequestUrlOptions, ParsedIconName } from \"../types\";\n\nexport const DEFAULT_ICON_API_BASE_URL = \"https://icons.opensite.ai\";\n\nexport function parseIconName(name: string): ParsedIconName | null {\n if (!name || typeof name !== \"string\") {\n return null;\n }\n\n const trimmed = name.trim();\n if (!trimmed) {\n return null;\n }\n\n const slashIndex = trimmed.indexOf(\"/\");\n const colonIndex = trimmed.indexOf(\":\");\n\n let separatorIndex = -1;\n if (slashIndex >= 0 && colonIndex >= 0) {\n separatorIndex = Math.min(slashIndex, colonIndex);\n } else if (slashIndex >= 0) {\n separatorIndex = slashIndex;\n } else if (colonIndex >= 0) {\n separatorIndex = colonIndex;\n }\n\n if (separatorIndex <= 0 || separatorIndex >= trimmed.length - 1) {\n return null;\n }\n\n const prefix = trimmed.slice(0, separatorIndex).trim();\n const iconName = trimmed.slice(separatorIndex + 1).trim();\n\n if (!prefix || !iconName) {\n return null;\n }\n\n return { prefix, iconName };\n}\n\nexport function buildIconRequestUrl({\n name,\n size,\n apiKey,\n baseUrl = DEFAULT_ICON_API_BASE_URL\n}: BuildIconRequestUrlOptions): string {\n const parsed = parseIconName(name);\n if (!parsed) {\n throw new Error(\n `Invalid icon name \\\"${name}\\\". Expected format \\\"prefix/name\\\" or \\\"prefix:name\\\".`\n );\n }\n\n if (!apiKey || !apiKey.trim()) {\n throw new Error(\"A non-empty apiKey is required to fetch icons.\");\n }\n\n const normalizedSize = Number.isFinite(size) && size > 0 ? Math.round(size) : 28;\n const normalizedBaseUrl = baseUrl.endsWith(\"/\") ? baseUrl.slice(0, -1) : baseUrl;\n\n const url = new URL(\n `/api/icon/${encodeURIComponent(parsed.prefix)}/${encodeURIComponent(parsed.iconName)}`,\n normalizedBaseUrl\n );\n\n url.searchParams.set(\"format\", \"svg\");\n url.searchParams.set(\"width\", String(normalizedSize));\n url.searchParams.set(\"height\", String(normalizedSize));\n url.searchParams.set(\"key\", apiKey);\n\n return url.toString();\n}\n","const HEX_BLACK_REGEX = /#000000\\b|#000\\b/i;\nconst BLACK_WORD_REGEX = /\\bblack\\b/i;\nconst RGB_BLACK_REGEX = /rgb\\(\\s*0\\s*,\\s*0\\s*,\\s*0\\s*\\)/i;\n\nconst STROKE_ATTR_REGEX = /stroke=[\"']([^\"']+)[\"']/gi;\nconst FILL_ATTR_REGEX = /fill=[\"']([^\"']+)[\"']/gi;\nconst STYLE_ATTR_REGEX = /style=[\"']([^\"']+)[\"']/gi;\n\nfunction normalizeColorToken(value: string): string {\n const trimmed = value.trim();\n if (\n HEX_BLACK_REGEX.test(trimmed) ||\n BLACK_WORD_REGEX.test(trimmed) ||\n RGB_BLACK_REGEX.test(trimmed)\n ) {\n return \"currentColor\";\n }\n return trimmed;\n}\n\nfunction normalizeStyleInline(style: string): string {\n return style\n .split(\";\")\n .map((segment) => segment.trim())\n .filter(Boolean)\n .map((segment) => {\n const [rawProperty, rawValue] = segment.split(\":\");\n if (!rawProperty || !rawValue) {\n return segment;\n }\n const property = rawProperty.trim().toLowerCase();\n if (property !== \"stroke\" && property !== \"fill\") {\n return segment;\n }\n const value = normalizeColorToken(rawValue);\n return `${rawProperty.trim()}: ${value}`;\n })\n .join(\"; \");\n}\n\nexport function processSvgForCurrentColor(svg: string): string {\n let processed = svg;\n\n processed = processed.replace(STROKE_ATTR_REGEX, (_full, value: string) => {\n const nextValue = normalizeColorToken(value);\n return `stroke=\"${nextValue}\"`;\n });\n\n processed = processed.replace(FILL_ATTR_REGEX, (_full, value: string) => {\n const trimmed = value.trim();\n if (trimmed.toLowerCase() === \"none\") {\n return `fill=\"${trimmed}\"`;\n }\n const nextValue = normalizeColorToken(trimmed);\n return `fill=\"${nextValue}\"`;\n });\n\n processed = processed.replace(STYLE_ATTR_REGEX, (_full, value: string) => {\n const normalized = normalizeStyleInline(value);\n return `style=\"${normalized}\"`;\n });\n\n return processed;\n}\n","import type { FetchIconSvgOptions } from \"../types\";\nimport { processSvgForCurrentColor } from \"./svg\";\n\nconst svgCache = new Map<string, string>();\nconst inFlight = new Map<string, Promise<string>>();\n\nexport function clearIconCache(): void {\n svgCache.clear();\n inFlight.clear();\n}\n\nexport async function fetchIconSvg(\n url: string,\n { fetcher = fetch }: FetchIconSvgOptions = {}\n): Promise<string> {\n const cached = svgCache.get(url);\n if (cached) {\n return cached;\n }\n\n const inflightRequest = inFlight.get(url);\n if (inflightRequest) {\n return inflightRequest;\n }\n\n const request = (async () => {\n const response = await fetcher(url);\n if (!response.ok) {\n throw new Error(`Failed to fetch icon: ${response.status}`);\n }\n\n const rawSvg = await response.text();\n const processedSvg = processSvgForCurrentColor(rawSvg);\n svgCache.set(url, processedSvg);\n return processedSvg;\n })();\n\n inFlight.set(url, request);\n\n try {\n return await request;\n } finally {\n inFlight.delete(url);\n }\n}\n","\"use client\";\n\nimport * as React from \"react\";\n\nimport type { IconProps } from \"../types\";\nimport { buildIconRequestUrl, fetchIconSvg, parseIconName } from \"../utils\";\n\nfunction joinClassNames(...classNames: Array<string | undefined>): string {\n return classNames.filter(Boolean).join(\" \");\n}\n\n// Check if we're in a test environment\nconst isTestEnvironment = typeof process !== \"undefined\" && process.env.NODE_ENV === \"test\";\n\nexport function Icon({\n name,\n size = 28,\n color,\n className,\n alt,\n apiKey,\n baseUrl,\n fallback\n}: IconProps) {\n const [state, setState] = React.useState<{\n svg: string | null;\n error: boolean;\n }>({ svg: null, error: false });\n\n const parsedName = React.useMemo(() => parseIconName(name), [name]);\n\n const requestUrl = React.useMemo(() => {\n if (!parsedName) {\n return null;\n }\n\n try {\n return buildIconRequestUrl({ name, size, apiKey, baseUrl });\n } catch {\n return null;\n }\n }, [apiKey, baseUrl, name, parsedName, size]);\n\n React.useEffect(() => {\n let disposed = false;\n\n if (!requestUrl) {\n // Only set error state if not already in error state\n setState((prev) => (prev.error && !prev.svg ? prev : { svg: null, error: true }));\n return;\n }\n\n // Reset state only if needed\n setState((prev) => (!prev.svg && !prev.error ? prev : { svg: null, error: false }));\n\n // In test environment, use a microtask to avoid act warnings\n const loadIcon = async () => {\n try {\n const svg = await fetchIconSvg(requestUrl);\n if (!disposed) {\n if (isTestEnvironment) {\n // Use queueMicrotask to ensure state updates happen in the next tick\n queueMicrotask(() => {\n if (!disposed) {\n setState({ svg, error: false });\n }\n });\n } else {\n setState({ svg, error: false });\n }\n }\n } catch {\n if (!disposed) {\n if (isTestEnvironment) {\n queueMicrotask(() => {\n if (!disposed) {\n setState({ svg: null, error: true });\n }\n });\n } else {\n setState({ svg: null, error: true });\n }\n }\n }\n };\n\n loadIcon();\n\n return () => {\n disposed = true;\n };\n }, [requestUrl]);\n\n const ariaLabel = alt || parsedName?.iconName;\n const a11yProps = ariaLabel\n ? ({ role: \"img\", \"aria-label\": ariaLabel } as const)\n : ({ \"aria-hidden\": true } as const);\n\n if (!state.svg) {\n return (\n <span\n className={joinClassNames(\"inline-block\", className)}\n style={{ width: size, height: size }}\n data-state={state.error ? \"error\" : \"loading\"}\n {...a11yProps}\n >\n {state.error ? fallback : null}\n </span>\n );\n }\n\n return (\n <span\n className={joinClassNames(\"inline-flex items-center justify-center\", className)}\n style={{\n width: size,\n height: size,\n color: color || \"inherit\"\n }}\n data-state=\"ready\"\n dangerouslySetInnerHTML={{ __html: state.svg }}\n {...a11yProps}\n />\n );\n}\n\nexport const DynamicIcon = Icon;\n"]} |
+36
-18
@@ -145,2 +145,3 @@ import * as React from 'react'; | ||
| } | ||
| var isTestEnvironment = typeof process !== "undefined" && process.env.NODE_ENV === "test"; | ||
| function Icon({ | ||
@@ -156,4 +157,3 @@ name, | ||
| }) { | ||
| const [svgContent, setSvgContent] = React.useState(null); | ||
| const [hasError, setHasError] = React.useState(false); | ||
| const [state, setState] = React.useState({ svg: null, error: false }); | ||
| const parsedName = React.useMemo(() => parseIconName(name), [name]); | ||
@@ -173,17 +173,35 @@ const requestUrl = React.useMemo(() => { | ||
| if (!requestUrl) { | ||
| setSvgContent(null); | ||
| setHasError(true); | ||
| setState((prev) => prev.error && !prev.svg ? prev : { svg: null, error: true }); | ||
| return; | ||
| } | ||
| setHasError(false); | ||
| setSvgContent(null); | ||
| fetchIconSvg(requestUrl).then((svg) => { | ||
| if (!disposed) { | ||
| setSvgContent(svg); | ||
| setState((prev) => !prev.svg && !prev.error ? prev : { svg: null, error: false }); | ||
| const loadIcon = async () => { | ||
| try { | ||
| const svg = await fetchIconSvg(requestUrl); | ||
| if (!disposed) { | ||
| if (isTestEnvironment) { | ||
| queueMicrotask(() => { | ||
| if (!disposed) { | ||
| setState({ svg, error: false }); | ||
| } | ||
| }); | ||
| } else { | ||
| setState({ svg, error: false }); | ||
| } | ||
| } | ||
| } catch { | ||
| if (!disposed) { | ||
| if (isTestEnvironment) { | ||
| queueMicrotask(() => { | ||
| if (!disposed) { | ||
| setState({ svg: null, error: true }); | ||
| } | ||
| }); | ||
| } else { | ||
| setState({ svg: null, error: true }); | ||
| } | ||
| } | ||
| } | ||
| }).catch(() => { | ||
| if (!disposed) { | ||
| setHasError(true); | ||
| } | ||
| }); | ||
| }; | ||
| loadIcon(); | ||
| return () => { | ||
@@ -195,3 +213,3 @@ disposed = true; | ||
| const a11yProps = ariaLabel ? { role: "img", "aria-label": ariaLabel } : { "aria-hidden": true }; | ||
| if (!svgContent) { | ||
| if (!state.svg) { | ||
| return /* @__PURE__ */ jsx( | ||
@@ -202,5 +220,5 @@ "span", | ||
| style: { width: size, height: size }, | ||
| "data-state": hasError ? "error" : "loading", | ||
| "data-state": state.error ? "error" : "loading", | ||
| ...a11yProps, | ||
| children: hasError ? fallback : null | ||
| children: state.error ? fallback : null | ||
| } | ||
@@ -219,3 +237,3 @@ ); | ||
| "data-state": "ready", | ||
| dangerouslySetInnerHTML: { __html: svgContent }, | ||
| dangerouslySetInnerHTML: { __html: state.svg }, | ||
| ...a11yProps | ||
@@ -222,0 +240,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../../src/utils/iconUrl.ts","../../src/utils/svg.ts","../../src/utils/fetchIconSvg.ts","../../src/core/Icon.tsx"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { BuildIconRequestUrlOptions, ParsedIconName } from \"../types\";\n\nexport const DEFAULT_ICON_API_BASE_URL = \"https://icons.opensite.ai\";\n\nexport function parseIconName(name: string): ParsedIconName | null {\n if (!name || typeof name !== \"string\") {\n return null;\n }\n\n const trimmed = name.trim();\n if (!trimmed) {\n return null;\n }\n\n const slashIndex = trimmed.indexOf(\"/\");\n const colonIndex = trimmed.indexOf(\":\");\n\n let separatorIndex = -1;\n if (slashIndex >= 0 && colonIndex >= 0) {\n separatorIndex = Math.min(slashIndex, colonIndex);\n } else if (slashIndex >= 0) {\n separatorIndex = slashIndex;\n } else if (colonIndex >= 0) {\n separatorIndex = colonIndex;\n }\n\n if (separatorIndex <= 0 || separatorIndex >= trimmed.length - 1) {\n return null;\n }\n\n const prefix = trimmed.slice(0, separatorIndex).trim();\n const iconName = trimmed.slice(separatorIndex + 1).trim();\n\n if (!prefix || !iconName) {\n return null;\n }\n\n return { prefix, iconName };\n}\n\nexport function buildIconRequestUrl({\n name,\n size,\n apiKey,\n baseUrl = DEFAULT_ICON_API_BASE_URL\n}: BuildIconRequestUrlOptions): string {\n const parsed = parseIconName(name);\n if (!parsed) {\n throw new Error(\n `Invalid icon name \\\"${name}\\\". Expected format \\\"prefix/name\\\" or \\\"prefix:name\\\".`\n );\n }\n\n if (!apiKey || !apiKey.trim()) {\n throw new Error(\"A non-empty apiKey is required to fetch icons.\");\n }\n\n const normalizedSize = Number.isFinite(size) && size > 0 ? Math.round(size) : 28;\n const normalizedBaseUrl = baseUrl.endsWith(\"/\") ? baseUrl.slice(0, -1) : baseUrl;\n\n const url = new URL(\n `/api/icon/${encodeURIComponent(parsed.prefix)}/${encodeURIComponent(parsed.iconName)}`,\n normalizedBaseUrl\n );\n\n url.searchParams.set(\"format\", \"svg\");\n url.searchParams.set(\"width\", String(normalizedSize));\n url.searchParams.set(\"height\", String(normalizedSize));\n url.searchParams.set(\"key\", apiKey);\n\n return url.toString();\n}\n","const HEX_BLACK_REGEX = /#000000\\b|#000\\b/i;\nconst BLACK_WORD_REGEX = /\\bblack\\b/i;\nconst RGB_BLACK_REGEX = /rgb\\(\\s*0\\s*,\\s*0\\s*,\\s*0\\s*\\)/i;\n\nconst STROKE_ATTR_REGEX = /stroke=[\"']([^\"']+)[\"']/gi;\nconst FILL_ATTR_REGEX = /fill=[\"']([^\"']+)[\"']/gi;\nconst STYLE_ATTR_REGEX = /style=[\"']([^\"']+)[\"']/gi;\n\nfunction normalizeColorToken(value: string): string {\n const trimmed = value.trim();\n if (\n HEX_BLACK_REGEX.test(trimmed) ||\n BLACK_WORD_REGEX.test(trimmed) ||\n RGB_BLACK_REGEX.test(trimmed)\n ) {\n return \"currentColor\";\n }\n return trimmed;\n}\n\nfunction normalizeStyleInline(style: string): string {\n return style\n .split(\";\")\n .map((segment) => segment.trim())\n .filter(Boolean)\n .map((segment) => {\n const [rawProperty, rawValue] = segment.split(\":\");\n if (!rawProperty || !rawValue) {\n return segment;\n }\n const property = rawProperty.trim().toLowerCase();\n if (property !== \"stroke\" && property !== \"fill\") {\n return segment;\n }\n const value = normalizeColorToken(rawValue);\n return `${rawProperty.trim()}: ${value}`;\n })\n .join(\"; \");\n}\n\nexport function processSvgForCurrentColor(svg: string): string {\n let processed = svg;\n\n processed = processed.replace(STROKE_ATTR_REGEX, (_full, value: string) => {\n const nextValue = normalizeColorToken(value);\n return `stroke=\"${nextValue}\"`;\n });\n\n processed = processed.replace(FILL_ATTR_REGEX, (_full, value: string) => {\n const trimmed = value.trim();\n if (trimmed.toLowerCase() === \"none\") {\n return `fill=\"${trimmed}\"`;\n }\n const nextValue = normalizeColorToken(trimmed);\n return `fill=\"${nextValue}\"`;\n });\n\n processed = processed.replace(STYLE_ATTR_REGEX, (_full, value: string) => {\n const normalized = normalizeStyleInline(value);\n return `style=\"${normalized}\"`;\n });\n\n return processed;\n}\n","import type { FetchIconSvgOptions } from \"../types\";\nimport { processSvgForCurrentColor } from \"./svg\";\n\nconst svgCache = new Map<string, string>();\nconst inFlight = new Map<string, Promise<string>>();\n\nexport function clearIconCache(): void {\n svgCache.clear();\n inFlight.clear();\n}\n\nexport async function fetchIconSvg(\n url: string,\n { fetcher = fetch }: FetchIconSvgOptions = {}\n): Promise<string> {\n const cached = svgCache.get(url);\n if (cached) {\n return cached;\n }\n\n const inflightRequest = inFlight.get(url);\n if (inflightRequest) {\n return inflightRequest;\n }\n\n const request = (async () => {\n const response = await fetcher(url);\n if (!response.ok) {\n throw new Error(`Failed to fetch icon: ${response.status}`);\n }\n\n const rawSvg = await response.text();\n const processedSvg = processSvgForCurrentColor(rawSvg);\n svgCache.set(url, processedSvg);\n return processedSvg;\n })();\n\n inFlight.set(url, request);\n\n try {\n return await request;\n } finally {\n inFlight.delete(url);\n }\n}\n","\"use client\";\n\nimport * as React from \"react\";\n\nimport type { IconProps } from \"../types\";\nimport { buildIconRequestUrl, fetchIconSvg, parseIconName } from \"../utils\";\n\nfunction joinClassNames(...classNames: Array<string | undefined>): string {\n return classNames.filter(Boolean).join(\" \");\n}\n\nexport function Icon({\n name,\n size = 28,\n color,\n className,\n alt,\n apiKey,\n baseUrl,\n fallback\n}: IconProps) {\n const [svgContent, setSvgContent] = React.useState<string | null>(null);\n const [hasError, setHasError] = React.useState(false);\n\n const parsedName = React.useMemo(() => parseIconName(name), [name]);\n\n const requestUrl = React.useMemo(() => {\n if (!parsedName) {\n return null;\n }\n\n try {\n return buildIconRequestUrl({ name, size, apiKey, baseUrl });\n } catch {\n return null;\n }\n }, [apiKey, baseUrl, name, parsedName, size]);\n\n React.useEffect(() => {\n let disposed = false;\n\n if (!requestUrl) {\n setSvgContent(null);\n setHasError(true);\n return;\n }\n\n setHasError(false);\n setSvgContent(null);\n\n fetchIconSvg(requestUrl)\n .then((svg) => {\n if (!disposed) {\n setSvgContent(svg);\n }\n })\n .catch(() => {\n if (!disposed) {\n setHasError(true);\n }\n });\n\n return () => {\n disposed = true;\n };\n }, [requestUrl]);\n\n const ariaLabel = alt || parsedName?.iconName;\n const a11yProps = ariaLabel\n ? ({ role: \"img\", \"aria-label\": ariaLabel } as const)\n : ({ \"aria-hidden\": true } as const);\n\n if (!svgContent) {\n return (\n <span\n className={joinClassNames(\"inline-block\", className)}\n style={{ width: size, height: size }}\n data-state={hasError ? \"error\" : \"loading\"}\n {...a11yProps}\n >\n {hasError ? fallback : null}\n </span>\n );\n }\n\n return (\n <span\n className={joinClassNames(\"inline-flex items-center justify-center\", className)}\n style={{\n width: size,\n height: size,\n color: color || \"inherit\"\n }}\n data-state=\"ready\"\n dangerouslySetInnerHTML={{ __html: svgContent }}\n {...a11yProps}\n />\n );\n}\n\nexport const DynamicIcon = Icon;\n"]} | ||
| {"version":3,"sources":["../../src/utils/iconUrl.ts","../../src/utils/svg.ts","../../src/utils/fetchIconSvg.ts","../../src/core/Icon.tsx"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { BuildIconRequestUrlOptions, ParsedIconName } from \"../types\";\n\nexport const DEFAULT_ICON_API_BASE_URL = \"https://icons.opensite.ai\";\n\nexport function parseIconName(name: string): ParsedIconName | null {\n if (!name || typeof name !== \"string\") {\n return null;\n }\n\n const trimmed = name.trim();\n if (!trimmed) {\n return null;\n }\n\n const slashIndex = trimmed.indexOf(\"/\");\n const colonIndex = trimmed.indexOf(\":\");\n\n let separatorIndex = -1;\n if (slashIndex >= 0 && colonIndex >= 0) {\n separatorIndex = Math.min(slashIndex, colonIndex);\n } else if (slashIndex >= 0) {\n separatorIndex = slashIndex;\n } else if (colonIndex >= 0) {\n separatorIndex = colonIndex;\n }\n\n if (separatorIndex <= 0 || separatorIndex >= trimmed.length - 1) {\n return null;\n }\n\n const prefix = trimmed.slice(0, separatorIndex).trim();\n const iconName = trimmed.slice(separatorIndex + 1).trim();\n\n if (!prefix || !iconName) {\n return null;\n }\n\n return { prefix, iconName };\n}\n\nexport function buildIconRequestUrl({\n name,\n size,\n apiKey,\n baseUrl = DEFAULT_ICON_API_BASE_URL\n}: BuildIconRequestUrlOptions): string {\n const parsed = parseIconName(name);\n if (!parsed) {\n throw new Error(\n `Invalid icon name \\\"${name}\\\". Expected format \\\"prefix/name\\\" or \\\"prefix:name\\\".`\n );\n }\n\n if (!apiKey || !apiKey.trim()) {\n throw new Error(\"A non-empty apiKey is required to fetch icons.\");\n }\n\n const normalizedSize = Number.isFinite(size) && size > 0 ? Math.round(size) : 28;\n const normalizedBaseUrl = baseUrl.endsWith(\"/\") ? baseUrl.slice(0, -1) : baseUrl;\n\n const url = new URL(\n `/api/icon/${encodeURIComponent(parsed.prefix)}/${encodeURIComponent(parsed.iconName)}`,\n normalizedBaseUrl\n );\n\n url.searchParams.set(\"format\", \"svg\");\n url.searchParams.set(\"width\", String(normalizedSize));\n url.searchParams.set(\"height\", String(normalizedSize));\n url.searchParams.set(\"key\", apiKey);\n\n return url.toString();\n}\n","const HEX_BLACK_REGEX = /#000000\\b|#000\\b/i;\nconst BLACK_WORD_REGEX = /\\bblack\\b/i;\nconst RGB_BLACK_REGEX = /rgb\\(\\s*0\\s*,\\s*0\\s*,\\s*0\\s*\\)/i;\n\nconst STROKE_ATTR_REGEX = /stroke=[\"']([^\"']+)[\"']/gi;\nconst FILL_ATTR_REGEX = /fill=[\"']([^\"']+)[\"']/gi;\nconst STYLE_ATTR_REGEX = /style=[\"']([^\"']+)[\"']/gi;\n\nfunction normalizeColorToken(value: string): string {\n const trimmed = value.trim();\n if (\n HEX_BLACK_REGEX.test(trimmed) ||\n BLACK_WORD_REGEX.test(trimmed) ||\n RGB_BLACK_REGEX.test(trimmed)\n ) {\n return \"currentColor\";\n }\n return trimmed;\n}\n\nfunction normalizeStyleInline(style: string): string {\n return style\n .split(\";\")\n .map((segment) => segment.trim())\n .filter(Boolean)\n .map((segment) => {\n const [rawProperty, rawValue] = segment.split(\":\");\n if (!rawProperty || !rawValue) {\n return segment;\n }\n const property = rawProperty.trim().toLowerCase();\n if (property !== \"stroke\" && property !== \"fill\") {\n return segment;\n }\n const value = normalizeColorToken(rawValue);\n return `${rawProperty.trim()}: ${value}`;\n })\n .join(\"; \");\n}\n\nexport function processSvgForCurrentColor(svg: string): string {\n let processed = svg;\n\n processed = processed.replace(STROKE_ATTR_REGEX, (_full, value: string) => {\n const nextValue = normalizeColorToken(value);\n return `stroke=\"${nextValue}\"`;\n });\n\n processed = processed.replace(FILL_ATTR_REGEX, (_full, value: string) => {\n const trimmed = value.trim();\n if (trimmed.toLowerCase() === \"none\") {\n return `fill=\"${trimmed}\"`;\n }\n const nextValue = normalizeColorToken(trimmed);\n return `fill=\"${nextValue}\"`;\n });\n\n processed = processed.replace(STYLE_ATTR_REGEX, (_full, value: string) => {\n const normalized = normalizeStyleInline(value);\n return `style=\"${normalized}\"`;\n });\n\n return processed;\n}\n","import type { FetchIconSvgOptions } from \"../types\";\nimport { processSvgForCurrentColor } from \"./svg\";\n\nconst svgCache = new Map<string, string>();\nconst inFlight = new Map<string, Promise<string>>();\n\nexport function clearIconCache(): void {\n svgCache.clear();\n inFlight.clear();\n}\n\nexport async function fetchIconSvg(\n url: string,\n { fetcher = fetch }: FetchIconSvgOptions = {}\n): Promise<string> {\n const cached = svgCache.get(url);\n if (cached) {\n return cached;\n }\n\n const inflightRequest = inFlight.get(url);\n if (inflightRequest) {\n return inflightRequest;\n }\n\n const request = (async () => {\n const response = await fetcher(url);\n if (!response.ok) {\n throw new Error(`Failed to fetch icon: ${response.status}`);\n }\n\n const rawSvg = await response.text();\n const processedSvg = processSvgForCurrentColor(rawSvg);\n svgCache.set(url, processedSvg);\n return processedSvg;\n })();\n\n inFlight.set(url, request);\n\n try {\n return await request;\n } finally {\n inFlight.delete(url);\n }\n}\n","\"use client\";\n\nimport * as React from \"react\";\n\nimport type { IconProps } from \"../types\";\nimport { buildIconRequestUrl, fetchIconSvg, parseIconName } from \"../utils\";\n\nfunction joinClassNames(...classNames: Array<string | undefined>): string {\n return classNames.filter(Boolean).join(\" \");\n}\n\n// Check if we're in a test environment\nconst isTestEnvironment = typeof process !== \"undefined\" && process.env.NODE_ENV === \"test\";\n\nexport function Icon({\n name,\n size = 28,\n color,\n className,\n alt,\n apiKey,\n baseUrl,\n fallback\n}: IconProps) {\n const [state, setState] = React.useState<{\n svg: string | null;\n error: boolean;\n }>({ svg: null, error: false });\n\n const parsedName = React.useMemo(() => parseIconName(name), [name]);\n\n const requestUrl = React.useMemo(() => {\n if (!parsedName) {\n return null;\n }\n\n try {\n return buildIconRequestUrl({ name, size, apiKey, baseUrl });\n } catch {\n return null;\n }\n }, [apiKey, baseUrl, name, parsedName, size]);\n\n React.useEffect(() => {\n let disposed = false;\n\n if (!requestUrl) {\n // Only set error state if not already in error state\n setState((prev) => (prev.error && !prev.svg ? prev : { svg: null, error: true }));\n return;\n }\n\n // Reset state only if needed\n setState((prev) => (!prev.svg && !prev.error ? prev : { svg: null, error: false }));\n\n // In test environment, use a microtask to avoid act warnings\n const loadIcon = async () => {\n try {\n const svg = await fetchIconSvg(requestUrl);\n if (!disposed) {\n if (isTestEnvironment) {\n // Use queueMicrotask to ensure state updates happen in the next tick\n queueMicrotask(() => {\n if (!disposed) {\n setState({ svg, error: false });\n }\n });\n } else {\n setState({ svg, error: false });\n }\n }\n } catch {\n if (!disposed) {\n if (isTestEnvironment) {\n queueMicrotask(() => {\n if (!disposed) {\n setState({ svg: null, error: true });\n }\n });\n } else {\n setState({ svg: null, error: true });\n }\n }\n }\n };\n\n loadIcon();\n\n return () => {\n disposed = true;\n };\n }, [requestUrl]);\n\n const ariaLabel = alt || parsedName?.iconName;\n const a11yProps = ariaLabel\n ? ({ role: \"img\", \"aria-label\": ariaLabel } as const)\n : ({ \"aria-hidden\": true } as const);\n\n if (!state.svg) {\n return (\n <span\n className={joinClassNames(\"inline-block\", className)}\n style={{ width: size, height: size }}\n data-state={state.error ? \"error\" : \"loading\"}\n {...a11yProps}\n >\n {state.error ? fallback : null}\n </span>\n );\n }\n\n return (\n <span\n className={joinClassNames(\"inline-flex items-center justify-center\", className)}\n style={{\n width: size,\n height: size,\n color: color || \"inherit\"\n }}\n data-state=\"ready\"\n dangerouslySetInnerHTML={{ __html: state.svg }}\n {...a11yProps}\n />\n );\n}\n\nexport const DynamicIcon = Icon;\n"]} |
+36
-18
@@ -171,2 +171,3 @@ 'use strict'; | ||
| } | ||
| var isTestEnvironment = typeof process !== "undefined" && process.env.NODE_ENV === "test"; | ||
| function Icon({ | ||
@@ -182,4 +183,3 @@ name, | ||
| }) { | ||
| const [svgContent, setSvgContent] = React__namespace.useState(null); | ||
| const [hasError, setHasError] = React__namespace.useState(false); | ||
| const [state, setState] = React__namespace.useState({ svg: null, error: false }); | ||
| const parsedName = React__namespace.useMemo(() => parseIconName(name), [name]); | ||
@@ -199,17 +199,35 @@ const requestUrl = React__namespace.useMemo(() => { | ||
| if (!requestUrl) { | ||
| setSvgContent(null); | ||
| setHasError(true); | ||
| setState((prev) => prev.error && !prev.svg ? prev : { svg: null, error: true }); | ||
| return; | ||
| } | ||
| setHasError(false); | ||
| setSvgContent(null); | ||
| fetchIconSvg(requestUrl).then((svg) => { | ||
| if (!disposed) { | ||
| setSvgContent(svg); | ||
| setState((prev) => !prev.svg && !prev.error ? prev : { svg: null, error: false }); | ||
| const loadIcon = async () => { | ||
| try { | ||
| const svg = await fetchIconSvg(requestUrl); | ||
| if (!disposed) { | ||
| if (isTestEnvironment) { | ||
| queueMicrotask(() => { | ||
| if (!disposed) { | ||
| setState({ svg, error: false }); | ||
| } | ||
| }); | ||
| } else { | ||
| setState({ svg, error: false }); | ||
| } | ||
| } | ||
| } catch { | ||
| if (!disposed) { | ||
| if (isTestEnvironment) { | ||
| queueMicrotask(() => { | ||
| if (!disposed) { | ||
| setState({ svg: null, error: true }); | ||
| } | ||
| }); | ||
| } else { | ||
| setState({ svg: null, error: true }); | ||
| } | ||
| } | ||
| } | ||
| }).catch(() => { | ||
| if (!disposed) { | ||
| setHasError(true); | ||
| } | ||
| }); | ||
| }; | ||
| loadIcon(); | ||
| return () => { | ||
@@ -221,3 +239,3 @@ disposed = true; | ||
| const a11yProps = ariaLabel ? { role: "img", "aria-label": ariaLabel } : { "aria-hidden": true }; | ||
| if (!svgContent) { | ||
| if (!state.svg) { | ||
| return /* @__PURE__ */ jsxRuntime.jsx( | ||
@@ -228,5 +246,5 @@ "span", | ||
| style: { width: size, height: size }, | ||
| "data-state": hasError ? "error" : "loading", | ||
| "data-state": state.error ? "error" : "loading", | ||
| ...a11yProps, | ||
| children: hasError ? fallback : null | ||
| children: state.error ? fallback : null | ||
| } | ||
@@ -245,3 +263,3 @@ ); | ||
| "data-state": "ready", | ||
| dangerouslySetInnerHTML: { __html: svgContent }, | ||
| dangerouslySetInnerHTML: { __html: state.svg }, | ||
| ...a11yProps | ||
@@ -248,0 +266,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../src/utils/iconUrl.ts","../src/utils/svg.ts","../src/utils/fetchIconSvg.ts","../src/core/Icon.tsx"],"names":["React","jsx"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { BuildIconRequestUrlOptions, ParsedIconName } from \"../types\";\n\nexport const DEFAULT_ICON_API_BASE_URL = \"https://icons.opensite.ai\";\n\nexport function parseIconName(name: string): ParsedIconName | null {\n if (!name || typeof name !== \"string\") {\n return null;\n }\n\n const trimmed = name.trim();\n if (!trimmed) {\n return null;\n }\n\n const slashIndex = trimmed.indexOf(\"/\");\n const colonIndex = trimmed.indexOf(\":\");\n\n let separatorIndex = -1;\n if (slashIndex >= 0 && colonIndex >= 0) {\n separatorIndex = Math.min(slashIndex, colonIndex);\n } else if (slashIndex >= 0) {\n separatorIndex = slashIndex;\n } else if (colonIndex >= 0) {\n separatorIndex = colonIndex;\n }\n\n if (separatorIndex <= 0 || separatorIndex >= trimmed.length - 1) {\n return null;\n }\n\n const prefix = trimmed.slice(0, separatorIndex).trim();\n const iconName = trimmed.slice(separatorIndex + 1).trim();\n\n if (!prefix || !iconName) {\n return null;\n }\n\n return { prefix, iconName };\n}\n\nexport function buildIconRequestUrl({\n name,\n size,\n apiKey,\n baseUrl = DEFAULT_ICON_API_BASE_URL\n}: BuildIconRequestUrlOptions): string {\n const parsed = parseIconName(name);\n if (!parsed) {\n throw new Error(\n `Invalid icon name \\\"${name}\\\". Expected format \\\"prefix/name\\\" or \\\"prefix:name\\\".`\n );\n }\n\n if (!apiKey || !apiKey.trim()) {\n throw new Error(\"A non-empty apiKey is required to fetch icons.\");\n }\n\n const normalizedSize = Number.isFinite(size) && size > 0 ? Math.round(size) : 28;\n const normalizedBaseUrl = baseUrl.endsWith(\"/\") ? baseUrl.slice(0, -1) : baseUrl;\n\n const url = new URL(\n `/api/icon/${encodeURIComponent(parsed.prefix)}/${encodeURIComponent(parsed.iconName)}`,\n normalizedBaseUrl\n );\n\n url.searchParams.set(\"format\", \"svg\");\n url.searchParams.set(\"width\", String(normalizedSize));\n url.searchParams.set(\"height\", String(normalizedSize));\n url.searchParams.set(\"key\", apiKey);\n\n return url.toString();\n}\n","const HEX_BLACK_REGEX = /#000000\\b|#000\\b/i;\nconst BLACK_WORD_REGEX = /\\bblack\\b/i;\nconst RGB_BLACK_REGEX = /rgb\\(\\s*0\\s*,\\s*0\\s*,\\s*0\\s*\\)/i;\n\nconst STROKE_ATTR_REGEX = /stroke=[\"']([^\"']+)[\"']/gi;\nconst FILL_ATTR_REGEX = /fill=[\"']([^\"']+)[\"']/gi;\nconst STYLE_ATTR_REGEX = /style=[\"']([^\"']+)[\"']/gi;\n\nfunction normalizeColorToken(value: string): string {\n const trimmed = value.trim();\n if (\n HEX_BLACK_REGEX.test(trimmed) ||\n BLACK_WORD_REGEX.test(trimmed) ||\n RGB_BLACK_REGEX.test(trimmed)\n ) {\n return \"currentColor\";\n }\n return trimmed;\n}\n\nfunction normalizeStyleInline(style: string): string {\n return style\n .split(\";\")\n .map((segment) => segment.trim())\n .filter(Boolean)\n .map((segment) => {\n const [rawProperty, rawValue] = segment.split(\":\");\n if (!rawProperty || !rawValue) {\n return segment;\n }\n const property = rawProperty.trim().toLowerCase();\n if (property !== \"stroke\" && property !== \"fill\") {\n return segment;\n }\n const value = normalizeColorToken(rawValue);\n return `${rawProperty.trim()}: ${value}`;\n })\n .join(\"; \");\n}\n\nexport function processSvgForCurrentColor(svg: string): string {\n let processed = svg;\n\n processed = processed.replace(STROKE_ATTR_REGEX, (_full, value: string) => {\n const nextValue = normalizeColorToken(value);\n return `stroke=\"${nextValue}\"`;\n });\n\n processed = processed.replace(FILL_ATTR_REGEX, (_full, value: string) => {\n const trimmed = value.trim();\n if (trimmed.toLowerCase() === \"none\") {\n return `fill=\"${trimmed}\"`;\n }\n const nextValue = normalizeColorToken(trimmed);\n return `fill=\"${nextValue}\"`;\n });\n\n processed = processed.replace(STYLE_ATTR_REGEX, (_full, value: string) => {\n const normalized = normalizeStyleInline(value);\n return `style=\"${normalized}\"`;\n });\n\n return processed;\n}\n","import type { FetchIconSvgOptions } from \"../types\";\nimport { processSvgForCurrentColor } from \"./svg\";\n\nconst svgCache = new Map<string, string>();\nconst inFlight = new Map<string, Promise<string>>();\n\nexport function clearIconCache(): void {\n svgCache.clear();\n inFlight.clear();\n}\n\nexport async function fetchIconSvg(\n url: string,\n { fetcher = fetch }: FetchIconSvgOptions = {}\n): Promise<string> {\n const cached = svgCache.get(url);\n if (cached) {\n return cached;\n }\n\n const inflightRequest = inFlight.get(url);\n if (inflightRequest) {\n return inflightRequest;\n }\n\n const request = (async () => {\n const response = await fetcher(url);\n if (!response.ok) {\n throw new Error(`Failed to fetch icon: ${response.status}`);\n }\n\n const rawSvg = await response.text();\n const processedSvg = processSvgForCurrentColor(rawSvg);\n svgCache.set(url, processedSvg);\n return processedSvg;\n })();\n\n inFlight.set(url, request);\n\n try {\n return await request;\n } finally {\n inFlight.delete(url);\n }\n}\n","\"use client\";\n\nimport * as React from \"react\";\n\nimport type { IconProps } from \"../types\";\nimport { buildIconRequestUrl, fetchIconSvg, parseIconName } from \"../utils\";\n\nfunction joinClassNames(...classNames: Array<string | undefined>): string {\n return classNames.filter(Boolean).join(\" \");\n}\n\nexport function Icon({\n name,\n size = 28,\n color,\n className,\n alt,\n apiKey,\n baseUrl,\n fallback\n}: IconProps) {\n const [svgContent, setSvgContent] = React.useState<string | null>(null);\n const [hasError, setHasError] = React.useState(false);\n\n const parsedName = React.useMemo(() => parseIconName(name), [name]);\n\n const requestUrl = React.useMemo(() => {\n if (!parsedName) {\n return null;\n }\n\n try {\n return buildIconRequestUrl({ name, size, apiKey, baseUrl });\n } catch {\n return null;\n }\n }, [apiKey, baseUrl, name, parsedName, size]);\n\n React.useEffect(() => {\n let disposed = false;\n\n if (!requestUrl) {\n setSvgContent(null);\n setHasError(true);\n return;\n }\n\n setHasError(false);\n setSvgContent(null);\n\n fetchIconSvg(requestUrl)\n .then((svg) => {\n if (!disposed) {\n setSvgContent(svg);\n }\n })\n .catch(() => {\n if (!disposed) {\n setHasError(true);\n }\n });\n\n return () => {\n disposed = true;\n };\n }, [requestUrl]);\n\n const ariaLabel = alt || parsedName?.iconName;\n const a11yProps = ariaLabel\n ? ({ role: \"img\", \"aria-label\": ariaLabel } as const)\n : ({ \"aria-hidden\": true } as const);\n\n if (!svgContent) {\n return (\n <span\n className={joinClassNames(\"inline-block\", className)}\n style={{ width: size, height: size }}\n data-state={hasError ? \"error\" : \"loading\"}\n {...a11yProps}\n >\n {hasError ? fallback : null}\n </span>\n );\n }\n\n return (\n <span\n className={joinClassNames(\"inline-flex items-center justify-center\", className)}\n style={{\n width: size,\n height: size,\n color: color || \"inherit\"\n }}\n data-state=\"ready\"\n dangerouslySetInnerHTML={{ __html: svgContent }}\n {...a11yProps}\n />\n );\n}\n\nexport const DynamicIcon = Icon;\n"]} | ||
| {"version":3,"sources":["../src/utils/iconUrl.ts","../src/utils/svg.ts","../src/utils/fetchIconSvg.ts","../src/core/Icon.tsx"],"names":["React","jsx"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { BuildIconRequestUrlOptions, ParsedIconName } from \"../types\";\n\nexport const DEFAULT_ICON_API_BASE_URL = \"https://icons.opensite.ai\";\n\nexport function parseIconName(name: string): ParsedIconName | null {\n if (!name || typeof name !== \"string\") {\n return null;\n }\n\n const trimmed = name.trim();\n if (!trimmed) {\n return null;\n }\n\n const slashIndex = trimmed.indexOf(\"/\");\n const colonIndex = trimmed.indexOf(\":\");\n\n let separatorIndex = -1;\n if (slashIndex >= 0 && colonIndex >= 0) {\n separatorIndex = Math.min(slashIndex, colonIndex);\n } else if (slashIndex >= 0) {\n separatorIndex = slashIndex;\n } else if (colonIndex >= 0) {\n separatorIndex = colonIndex;\n }\n\n if (separatorIndex <= 0 || separatorIndex >= trimmed.length - 1) {\n return null;\n }\n\n const prefix = trimmed.slice(0, separatorIndex).trim();\n const iconName = trimmed.slice(separatorIndex + 1).trim();\n\n if (!prefix || !iconName) {\n return null;\n }\n\n return { prefix, iconName };\n}\n\nexport function buildIconRequestUrl({\n name,\n size,\n apiKey,\n baseUrl = DEFAULT_ICON_API_BASE_URL\n}: BuildIconRequestUrlOptions): string {\n const parsed = parseIconName(name);\n if (!parsed) {\n throw new Error(\n `Invalid icon name \\\"${name}\\\". Expected format \\\"prefix/name\\\" or \\\"prefix:name\\\".`\n );\n }\n\n if (!apiKey || !apiKey.trim()) {\n throw new Error(\"A non-empty apiKey is required to fetch icons.\");\n }\n\n const normalizedSize = Number.isFinite(size) && size > 0 ? Math.round(size) : 28;\n const normalizedBaseUrl = baseUrl.endsWith(\"/\") ? baseUrl.slice(0, -1) : baseUrl;\n\n const url = new URL(\n `/api/icon/${encodeURIComponent(parsed.prefix)}/${encodeURIComponent(parsed.iconName)}`,\n normalizedBaseUrl\n );\n\n url.searchParams.set(\"format\", \"svg\");\n url.searchParams.set(\"width\", String(normalizedSize));\n url.searchParams.set(\"height\", String(normalizedSize));\n url.searchParams.set(\"key\", apiKey);\n\n return url.toString();\n}\n","const HEX_BLACK_REGEX = /#000000\\b|#000\\b/i;\nconst BLACK_WORD_REGEX = /\\bblack\\b/i;\nconst RGB_BLACK_REGEX = /rgb\\(\\s*0\\s*,\\s*0\\s*,\\s*0\\s*\\)/i;\n\nconst STROKE_ATTR_REGEX = /stroke=[\"']([^\"']+)[\"']/gi;\nconst FILL_ATTR_REGEX = /fill=[\"']([^\"']+)[\"']/gi;\nconst STYLE_ATTR_REGEX = /style=[\"']([^\"']+)[\"']/gi;\n\nfunction normalizeColorToken(value: string): string {\n const trimmed = value.trim();\n if (\n HEX_BLACK_REGEX.test(trimmed) ||\n BLACK_WORD_REGEX.test(trimmed) ||\n RGB_BLACK_REGEX.test(trimmed)\n ) {\n return \"currentColor\";\n }\n return trimmed;\n}\n\nfunction normalizeStyleInline(style: string): string {\n return style\n .split(\";\")\n .map((segment) => segment.trim())\n .filter(Boolean)\n .map((segment) => {\n const [rawProperty, rawValue] = segment.split(\":\");\n if (!rawProperty || !rawValue) {\n return segment;\n }\n const property = rawProperty.trim().toLowerCase();\n if (property !== \"stroke\" && property !== \"fill\") {\n return segment;\n }\n const value = normalizeColorToken(rawValue);\n return `${rawProperty.trim()}: ${value}`;\n })\n .join(\"; \");\n}\n\nexport function processSvgForCurrentColor(svg: string): string {\n let processed = svg;\n\n processed = processed.replace(STROKE_ATTR_REGEX, (_full, value: string) => {\n const nextValue = normalizeColorToken(value);\n return `stroke=\"${nextValue}\"`;\n });\n\n processed = processed.replace(FILL_ATTR_REGEX, (_full, value: string) => {\n const trimmed = value.trim();\n if (trimmed.toLowerCase() === \"none\") {\n return `fill=\"${trimmed}\"`;\n }\n const nextValue = normalizeColorToken(trimmed);\n return `fill=\"${nextValue}\"`;\n });\n\n processed = processed.replace(STYLE_ATTR_REGEX, (_full, value: string) => {\n const normalized = normalizeStyleInline(value);\n return `style=\"${normalized}\"`;\n });\n\n return processed;\n}\n","import type { FetchIconSvgOptions } from \"../types\";\nimport { processSvgForCurrentColor } from \"./svg\";\n\nconst svgCache = new Map<string, string>();\nconst inFlight = new Map<string, Promise<string>>();\n\nexport function clearIconCache(): void {\n svgCache.clear();\n inFlight.clear();\n}\n\nexport async function fetchIconSvg(\n url: string,\n { fetcher = fetch }: FetchIconSvgOptions = {}\n): Promise<string> {\n const cached = svgCache.get(url);\n if (cached) {\n return cached;\n }\n\n const inflightRequest = inFlight.get(url);\n if (inflightRequest) {\n return inflightRequest;\n }\n\n const request = (async () => {\n const response = await fetcher(url);\n if (!response.ok) {\n throw new Error(`Failed to fetch icon: ${response.status}`);\n }\n\n const rawSvg = await response.text();\n const processedSvg = processSvgForCurrentColor(rawSvg);\n svgCache.set(url, processedSvg);\n return processedSvg;\n })();\n\n inFlight.set(url, request);\n\n try {\n return await request;\n } finally {\n inFlight.delete(url);\n }\n}\n","\"use client\";\n\nimport * as React from \"react\";\n\nimport type { IconProps } from \"../types\";\nimport { buildIconRequestUrl, fetchIconSvg, parseIconName } from \"../utils\";\n\nfunction joinClassNames(...classNames: Array<string | undefined>): string {\n return classNames.filter(Boolean).join(\" \");\n}\n\n// Check if we're in a test environment\nconst isTestEnvironment = typeof process !== \"undefined\" && process.env.NODE_ENV === \"test\";\n\nexport function Icon({\n name,\n size = 28,\n color,\n className,\n alt,\n apiKey,\n baseUrl,\n fallback\n}: IconProps) {\n const [state, setState] = React.useState<{\n svg: string | null;\n error: boolean;\n }>({ svg: null, error: false });\n\n const parsedName = React.useMemo(() => parseIconName(name), [name]);\n\n const requestUrl = React.useMemo(() => {\n if (!parsedName) {\n return null;\n }\n\n try {\n return buildIconRequestUrl({ name, size, apiKey, baseUrl });\n } catch {\n return null;\n }\n }, [apiKey, baseUrl, name, parsedName, size]);\n\n React.useEffect(() => {\n let disposed = false;\n\n if (!requestUrl) {\n // Only set error state if not already in error state\n setState((prev) => (prev.error && !prev.svg ? prev : { svg: null, error: true }));\n return;\n }\n\n // Reset state only if needed\n setState((prev) => (!prev.svg && !prev.error ? prev : { svg: null, error: false }));\n\n // In test environment, use a microtask to avoid act warnings\n const loadIcon = async () => {\n try {\n const svg = await fetchIconSvg(requestUrl);\n if (!disposed) {\n if (isTestEnvironment) {\n // Use queueMicrotask to ensure state updates happen in the next tick\n queueMicrotask(() => {\n if (!disposed) {\n setState({ svg, error: false });\n }\n });\n } else {\n setState({ svg, error: false });\n }\n }\n } catch {\n if (!disposed) {\n if (isTestEnvironment) {\n queueMicrotask(() => {\n if (!disposed) {\n setState({ svg: null, error: true });\n }\n });\n } else {\n setState({ svg: null, error: true });\n }\n }\n }\n };\n\n loadIcon();\n\n return () => {\n disposed = true;\n };\n }, [requestUrl]);\n\n const ariaLabel = alt || parsedName?.iconName;\n const a11yProps = ariaLabel\n ? ({ role: \"img\", \"aria-label\": ariaLabel } as const)\n : ({ \"aria-hidden\": true } as const);\n\n if (!state.svg) {\n return (\n <span\n className={joinClassNames(\"inline-block\", className)}\n style={{ width: size, height: size }}\n data-state={state.error ? \"error\" : \"loading\"}\n {...a11yProps}\n >\n {state.error ? fallback : null}\n </span>\n );\n }\n\n return (\n <span\n className={joinClassNames(\"inline-flex items-center justify-center\", className)}\n style={{\n width: size,\n height: size,\n color: color || \"inherit\"\n }}\n data-state=\"ready\"\n dangerouslySetInnerHTML={{ __html: state.svg }}\n {...a11yProps}\n />\n );\n}\n\nexport const DynamicIcon = Icon;\n"]} |
+36
-18
@@ -149,2 +149,3 @@ import * as React from 'react'; | ||
| } | ||
| var isTestEnvironment = typeof process !== "undefined" && process.env.NODE_ENV === "test"; | ||
| function Icon({ | ||
@@ -160,4 +161,3 @@ name, | ||
| }) { | ||
| const [svgContent, setSvgContent] = React.useState(null); | ||
| const [hasError, setHasError] = React.useState(false); | ||
| const [state, setState] = React.useState({ svg: null, error: false }); | ||
| const parsedName = React.useMemo(() => parseIconName(name), [name]); | ||
@@ -177,17 +177,35 @@ const requestUrl = React.useMemo(() => { | ||
| if (!requestUrl) { | ||
| setSvgContent(null); | ||
| setHasError(true); | ||
| setState((prev) => prev.error && !prev.svg ? prev : { svg: null, error: true }); | ||
| return; | ||
| } | ||
| setHasError(false); | ||
| setSvgContent(null); | ||
| fetchIconSvg(requestUrl).then((svg) => { | ||
| if (!disposed) { | ||
| setSvgContent(svg); | ||
| setState((prev) => !prev.svg && !prev.error ? prev : { svg: null, error: false }); | ||
| const loadIcon = async () => { | ||
| try { | ||
| const svg = await fetchIconSvg(requestUrl); | ||
| if (!disposed) { | ||
| if (isTestEnvironment) { | ||
| queueMicrotask(() => { | ||
| if (!disposed) { | ||
| setState({ svg, error: false }); | ||
| } | ||
| }); | ||
| } else { | ||
| setState({ svg, error: false }); | ||
| } | ||
| } | ||
| } catch { | ||
| if (!disposed) { | ||
| if (isTestEnvironment) { | ||
| queueMicrotask(() => { | ||
| if (!disposed) { | ||
| setState({ svg: null, error: true }); | ||
| } | ||
| }); | ||
| } else { | ||
| setState({ svg: null, error: true }); | ||
| } | ||
| } | ||
| } | ||
| }).catch(() => { | ||
| if (!disposed) { | ||
| setHasError(true); | ||
| } | ||
| }); | ||
| }; | ||
| loadIcon(); | ||
| return () => { | ||
@@ -199,3 +217,3 @@ disposed = true; | ||
| const a11yProps = ariaLabel ? { role: "img", "aria-label": ariaLabel } : { "aria-hidden": true }; | ||
| if (!svgContent) { | ||
| if (!state.svg) { | ||
| return /* @__PURE__ */ jsx( | ||
@@ -206,5 +224,5 @@ "span", | ||
| style: { width: size, height: size }, | ||
| "data-state": hasError ? "error" : "loading", | ||
| "data-state": state.error ? "error" : "loading", | ||
| ...a11yProps, | ||
| children: hasError ? fallback : null | ||
| children: state.error ? fallback : null | ||
| } | ||
@@ -223,3 +241,3 @@ ); | ||
| "data-state": "ready", | ||
| dangerouslySetInnerHTML: { __html: svgContent }, | ||
| dangerouslySetInnerHTML: { __html: state.svg }, | ||
| ...a11yProps | ||
@@ -226,0 +244,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../src/utils/iconUrl.ts","../src/utils/svg.ts","../src/utils/fetchIconSvg.ts","../src/core/Icon.tsx"],"names":[],"mappings":";;;;;;AAEO,IAAM,yBAAA,GAA4B;AAElC,SAAS,cAAc,IAAA,EAAqC;AACjE,EAAA,IAAI,CAAC,IAAA,IAAQ,OAAO,IAAA,KAAS,QAAA,EAAU;AACrC,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,MAAM,OAAA,GAAU,KAAK,IAAA,EAAK;AAC1B,EAAA,IAAI,CAAC,OAAA,EAAS;AACZ,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,MAAM,UAAA,GAAa,OAAA,CAAQ,OAAA,CAAQ,GAAG,CAAA;AACtC,EAAA,MAAM,UAAA,GAAa,OAAA,CAAQ,OAAA,CAAQ,GAAG,CAAA;AAEtC,EAAA,IAAI,cAAA,GAAiB,EAAA;AACrB,EAAA,IAAI,UAAA,IAAc,CAAA,IAAK,UAAA,IAAc,CAAA,EAAG;AACtC,IAAA,cAAA,GAAiB,IAAA,CAAK,GAAA,CAAI,UAAA,EAAY,UAAU,CAAA;AAAA,EAClD,CAAA,MAAA,IAAW,cAAc,CAAA,EAAG;AAC1B,IAAA,cAAA,GAAiB,UAAA;AAAA,EACnB,CAAA,MAAA,IAAW,cAAc,CAAA,EAAG;AAC1B,IAAA,cAAA,GAAiB,UAAA;AAAA,EACnB;AAEA,EAAA,IAAI,cAAA,IAAkB,CAAA,IAAK,cAAA,IAAkB,OAAA,CAAQ,SAAS,CAAA,EAAG;AAC/D,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,MAAM,SAAS,OAAA,CAAQ,KAAA,CAAM,CAAA,EAAG,cAAc,EAAE,IAAA,EAAK;AACrD,EAAA,MAAM,WAAW,OAAA,CAAQ,KAAA,CAAM,cAAA,GAAiB,CAAC,EAAE,IAAA,EAAK;AAExD,EAAA,IAAI,CAAC,MAAA,IAAU,CAAC,QAAA,EAAU;AACxB,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,OAAO,EAAE,QAAQ,QAAA,EAAS;AAC5B;AAEO,SAAS,mBAAA,CAAoB;AAAA,EAClC,IAAA;AAAA,EACA,IAAA;AAAA,EACA,MAAA;AAAA,EACA,OAAA,GAAU;AACZ,CAAA,EAAuC;AACrC,EAAA,MAAM,MAAA,GAAS,cAAc,IAAI,CAAA;AACjC,EAAA,IAAI,CAAC,MAAA,EAAQ;AACX,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,sBAAuB,IAAI,CAAA,kDAAA;AAAA,KAC7B;AAAA,EACF;AAEA,EAAA,IAAI,CAAC,MAAA,IAAU,CAAC,MAAA,CAAO,MAAK,EAAG;AAC7B,IAAA,MAAM,IAAI,MAAM,gDAAgD,CAAA;AAAA,EAClE;AAEA,EAAA,MAAM,cAAA,GAAiB,MAAA,CAAO,QAAA,CAAS,IAAI,CAAA,IAAK,OAAO,CAAA,GAAI,IAAA,CAAK,KAAA,CAAM,IAAI,CAAA,GAAI,EAAA;AAC9E,EAAA,MAAM,iBAAA,GAAoB,QAAQ,QAAA,CAAS,GAAG,IAAI,OAAA,CAAQ,KAAA,CAAM,CAAA,EAAG,EAAE,CAAA,GAAI,OAAA;AAEzE,EAAA,MAAM,MAAM,IAAI,GAAA;AAAA,IACd,CAAA,UAAA,EAAa,mBAAmB,MAAA,CAAO,MAAM,CAAC,CAAA,CAAA,EAAI,kBAAA,CAAmB,MAAA,CAAO,QAAQ,CAAC,CAAA,CAAA;AAAA,IACrF;AAAA,GACF;AAEA,EAAA,GAAA,CAAI,YAAA,CAAa,GAAA,CAAI,QAAA,EAAU,KAAK,CAAA;AACpC,EAAA,GAAA,CAAI,YAAA,CAAa,GAAA,CAAI,OAAA,EAAS,MAAA,CAAO,cAAc,CAAC,CAAA;AACpD,EAAA,GAAA,CAAI,YAAA,CAAa,GAAA,CAAI,QAAA,EAAU,MAAA,CAAO,cAAc,CAAC,CAAA;AACrD,EAAA,GAAA,CAAI,YAAA,CAAa,GAAA,CAAI,KAAA,EAAO,MAAM,CAAA;AAElC,EAAA,OAAO,IAAI,QAAA,EAAS;AACtB;;;ACvEA,IAAM,eAAA,GAAkB,mBAAA;AACxB,IAAM,gBAAA,GAAmB,YAAA;AACzB,IAAM,eAAA,GAAkB,iCAAA;AAExB,IAAM,iBAAA,GAAoB,2BAAA;AAC1B,IAAM,eAAA,GAAkB,yBAAA;AACxB,IAAM,gBAAA,GAAmB,0BAAA;AAEzB,SAAS,oBAAoB,KAAA,EAAuB;AAClD,EAAA,MAAM,OAAA,GAAU,MAAM,IAAA,EAAK;AAC3B,EAAA,IACE,eAAA,CAAgB,IAAA,CAAK,OAAO,CAAA,IAC5B,gBAAA,CAAiB,IAAA,CAAK,OAAO,CAAA,IAC7B,eAAA,CAAgB,IAAA,CAAK,OAAO,CAAA,EAC5B;AACA,IAAA,OAAO,cAAA;AAAA,EACT;AACA,EAAA,OAAO,OAAA;AACT;AAEA,SAAS,qBAAqB,KAAA,EAAuB;AACnD,EAAA,OAAO,MACJ,KAAA,CAAM,GAAG,CAAA,CACT,GAAA,CAAI,CAAC,OAAA,KAAY,OAAA,CAAQ,IAAA,EAAM,EAC/B,MAAA,CAAO,OAAO,CAAA,CACd,GAAA,CAAI,CAAC,OAAA,KAAY;AAChB,IAAA,MAAM,CAAC,WAAA,EAAa,QAAQ,CAAA,GAAI,OAAA,CAAQ,MAAM,GAAG,CAAA;AACjD,IAAA,IAAI,CAAC,WAAA,IAAe,CAAC,QAAA,EAAU;AAC7B,MAAA,OAAO,OAAA;AAAA,IACT;AACA,IAAA,MAAM,QAAA,GAAW,WAAA,CAAY,IAAA,EAAK,CAAE,WAAA,EAAY;AAChD,IAAA,IAAI,QAAA,KAAa,QAAA,IAAY,QAAA,KAAa,MAAA,EAAQ;AAChD,MAAA,OAAO,OAAA;AAAA,IACT;AACA,IAAA,MAAM,KAAA,GAAQ,oBAAoB,QAAQ,CAAA;AAC1C,IAAA,OAAO,CAAA,EAAG,WAAA,CAAY,IAAA,EAAM,KAAK,KAAK,CAAA,CAAA;AAAA,EACxC,CAAC,CAAA,CACA,IAAA,CAAK,IAAI,CAAA;AACd;AAEO,SAAS,0BAA0B,GAAA,EAAqB;AAC7D,EAAA,IAAI,SAAA,GAAY,GAAA;AAEhB,EAAA,SAAA,GAAY,SAAA,CAAU,OAAA,CAAQ,iBAAA,EAAmB,CAAC,OAAO,KAAA,KAAkB;AACzE,IAAA,MAAM,SAAA,GAAY,oBAAoB,KAAK,CAAA;AAC3C,IAAA,OAAO,WAAW,SAAS,CAAA,CAAA,CAAA;AAAA,EAC7B,CAAC,CAAA;AAED,EAAA,SAAA,GAAY,SAAA,CAAU,OAAA,CAAQ,eAAA,EAAiB,CAAC,OAAO,KAAA,KAAkB;AACvE,IAAA,MAAM,OAAA,GAAU,MAAM,IAAA,EAAK;AAC3B,IAAA,IAAI,OAAA,CAAQ,WAAA,EAAY,KAAM,MAAA,EAAQ;AACpC,MAAA,OAAO,SAAS,OAAO,CAAA,CAAA,CAAA;AAAA,IACzB;AACA,IAAA,MAAM,SAAA,GAAY,oBAAoB,OAAO,CAAA;AAC7C,IAAA,OAAO,SAAS,SAAS,CAAA,CAAA,CAAA;AAAA,EAC3B,CAAC,CAAA;AAED,EAAA,SAAA,GAAY,SAAA,CAAU,OAAA,CAAQ,gBAAA,EAAkB,CAAC,OAAO,KAAA,KAAkB;AACxE,IAAA,MAAM,UAAA,GAAa,qBAAqB,KAAK,CAAA;AAC7C,IAAA,OAAO,UAAU,UAAU,CAAA,CAAA,CAAA;AAAA,EAC7B,CAAC,CAAA;AAED,EAAA,OAAO,SAAA;AACT;;;AC5DA,IAAM,QAAA,uBAAe,GAAA,EAAoB;AACzC,IAAM,QAAA,uBAAe,GAAA,EAA6B;AAE3C,SAAS,cAAA,GAAuB;AACrC,EAAA,QAAA,CAAS,KAAA,EAAM;AACf,EAAA,QAAA,CAAS,KAAA,EAAM;AACjB;AAEA,eAAsB,aACpB,GAAA,EACA,EAAE,UAAU,KAAA,EAAM,GAAyB,EAAC,EAC3B;AACjB,EAAA,MAAM,MAAA,GAAS,QAAA,CAAS,GAAA,CAAI,GAAG,CAAA;AAC/B,EAAA,IAAI,MAAA,EAAQ;AACV,IAAA,OAAO,MAAA;AAAA,EACT;AAEA,EAAA,MAAM,eAAA,GAAkB,QAAA,CAAS,GAAA,CAAI,GAAG,CAAA;AACxC,EAAA,IAAI,eAAA,EAAiB;AACnB,IAAA,OAAO,eAAA;AAAA,EACT;AAEA,EAAA,MAAM,WAAW,YAAY;AAC3B,IAAA,MAAM,QAAA,GAAW,MAAM,OAAA,CAAQ,GAAG,CAAA;AAClC,IAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAChB,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,sBAAA,EAAyB,QAAA,CAAS,MAAM,CAAA,CAAE,CAAA;AAAA,IAC5D;AAEA,IAAA,MAAM,MAAA,GAAS,MAAM,QAAA,CAAS,IAAA,EAAK;AACnC,IAAA,MAAM,YAAA,GAAe,0BAA0B,MAAM,CAAA;AACrD,IAAA,QAAA,CAAS,GAAA,CAAI,KAAK,YAAY,CAAA;AAC9B,IAAA,OAAO,YAAA;AAAA,EACT,CAAA,GAAG;AAEH,EAAA,QAAA,CAAS,GAAA,CAAI,KAAK,OAAO,CAAA;AAEzB,EAAA,IAAI;AACF,IAAA,OAAO,MAAM,OAAA;AAAA,EACf,CAAA,SAAE;AACA,IAAA,QAAA,CAAS,OAAO,GAAG,CAAA;AAAA,EACrB;AACF;ACrCA,SAAS,kBAAkB,UAAA,EAA+C;AACxE,EAAA,OAAO,UAAA,CAAW,MAAA,CAAO,OAAO,CAAA,CAAE,KAAK,GAAG,CAAA;AAC5C;AAEO,SAAS,IAAA,CAAK;AAAA,EACnB,IAAA;AAAA,EACA,IAAA,GAAO,EAAA;AAAA,EACP,KAAA;AAAA,EACA,SAAA;AAAA,EACA,GAAA;AAAA,EACA,MAAA;AAAA,EACA,OAAA;AAAA,EACA;AACF,CAAA,EAAc;AACZ,EAAA,MAAM,CAAC,UAAA,EAAY,aAAa,CAAA,GAAU,eAAwB,IAAI,CAAA;AACtE,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAU,eAAS,KAAK,CAAA;AAEpD,EAAA,MAAM,UAAA,GAAmB,cAAQ,MAAM,aAAA,CAAc,IAAI,CAAA,EAAG,CAAC,IAAI,CAAC,CAAA;AAElE,EAAA,MAAM,UAAA,GAAmB,cAAQ,MAAM;AACrC,IAAA,IAAI,CAAC,UAAA,EAAY;AACf,MAAA,OAAO,IAAA;AAAA,IACT;AAEA,IAAA,IAAI;AACF,MAAA,OAAO,oBAAoB,EAAE,IAAA,EAAM,IAAA,EAAM,MAAA,EAAQ,SAAS,CAAA;AAAA,IAC5D,CAAA,CAAA,MAAQ;AACN,MAAA,OAAO,IAAA;AAAA,IACT;AAAA,EACF,GAAG,CAAC,MAAA,EAAQ,SAAS,IAAA,EAAM,UAAA,EAAY,IAAI,CAAC,CAAA;AAE5C,EAAM,gBAAU,MAAM;AACpB,IAAA,IAAI,QAAA,GAAW,KAAA;AAEf,IAAA,IAAI,CAAC,UAAA,EAAY;AACf,MAAA,aAAA,CAAc,IAAI,CAAA;AAClB,MAAA,WAAA,CAAY,IAAI,CAAA;AAChB,MAAA;AAAA,IACF;AAEA,IAAA,WAAA,CAAY,KAAK,CAAA;AACjB,IAAA,aAAA,CAAc,IAAI,CAAA;AAElB,IAAA,YAAA,CAAa,UAAU,CAAA,CACpB,IAAA,CAAK,CAAC,GAAA,KAAQ;AACb,MAAA,IAAI,CAAC,QAAA,EAAU;AACb,QAAA,aAAA,CAAc,GAAG,CAAA;AAAA,MACnB;AAAA,IACF,CAAC,CAAA,CACA,KAAA,CAAM,MAAM;AACX,MAAA,IAAI,CAAC,QAAA,EAAU;AACb,QAAA,WAAA,CAAY,IAAI,CAAA;AAAA,MAClB;AAAA,IACF,CAAC,CAAA;AAEH,IAAA,OAAO,MAAM;AACX,MAAA,QAAA,GAAW,IAAA;AAAA,IACb,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,UAAU,CAAC,CAAA;AAEf,EAAA,MAAM,SAAA,GAAY,OAAO,UAAA,EAAY,QAAA;AACrC,EAAA,MAAM,SAAA,GAAY,SAAA,GACb,EAAE,IAAA,EAAM,KAAA,EAAO,cAAc,SAAA,EAAU,GACvC,EAAE,aAAA,EAAe,IAAA,EAAK;AAE3B,EAAA,IAAI,CAAC,UAAA,EAAY;AACf,IAAA,uBACE,GAAA;AAAA,MAAC,MAAA;AAAA,MAAA;AAAA,QACC,SAAA,EAAW,cAAA,CAAe,cAAA,EAAgB,SAAS,CAAA;AAAA,QACnD,KAAA,EAAO,EAAE,KAAA,EAAO,IAAA,EAAM,QAAQ,IAAA,EAAK;AAAA,QACnC,YAAA,EAAY,WAAW,OAAA,GAAU,SAAA;AAAA,QAChC,GAAG,SAAA;AAAA,QAEH,qBAAW,QAAA,GAAW;AAAA;AAAA,KACzB;AAAA,EAEJ;AAEA,EAAA,uBACE,GAAA;AAAA,IAAC,MAAA;AAAA,IAAA;AAAA,MACC,SAAA,EAAW,cAAA,CAAe,yCAAA,EAA2C,SAAS,CAAA;AAAA,MAC9E,KAAA,EAAO;AAAA,QACL,KAAA,EAAO,IAAA;AAAA,QACP,MAAA,EAAQ,IAAA;AAAA,QACR,OAAO,KAAA,IAAS;AAAA,OAClB;AAAA,MACA,YAAA,EAAW,OAAA;AAAA,MACX,uBAAA,EAAyB,EAAE,MAAA,EAAQ,UAAA,EAAW;AAAA,MAC7C,GAAG;AAAA;AAAA,GACN;AAEJ;AAEO,IAAM,WAAA,GAAc","file":"index.js","sourcesContent":["import type { BuildIconRequestUrlOptions, ParsedIconName } from \"../types\";\n\nexport const DEFAULT_ICON_API_BASE_URL = \"https://icons.opensite.ai\";\n\nexport function parseIconName(name: string): ParsedIconName | null {\n if (!name || typeof name !== \"string\") {\n return null;\n }\n\n const trimmed = name.trim();\n if (!trimmed) {\n return null;\n }\n\n const slashIndex = trimmed.indexOf(\"/\");\n const colonIndex = trimmed.indexOf(\":\");\n\n let separatorIndex = -1;\n if (slashIndex >= 0 && colonIndex >= 0) {\n separatorIndex = Math.min(slashIndex, colonIndex);\n } else if (slashIndex >= 0) {\n separatorIndex = slashIndex;\n } else if (colonIndex >= 0) {\n separatorIndex = colonIndex;\n }\n\n if (separatorIndex <= 0 || separatorIndex >= trimmed.length - 1) {\n return null;\n }\n\n const prefix = trimmed.slice(0, separatorIndex).trim();\n const iconName = trimmed.slice(separatorIndex + 1).trim();\n\n if (!prefix || !iconName) {\n return null;\n }\n\n return { prefix, iconName };\n}\n\nexport function buildIconRequestUrl({\n name,\n size,\n apiKey,\n baseUrl = DEFAULT_ICON_API_BASE_URL\n}: BuildIconRequestUrlOptions): string {\n const parsed = parseIconName(name);\n if (!parsed) {\n throw new Error(\n `Invalid icon name \\\"${name}\\\". Expected format \\\"prefix/name\\\" or \\\"prefix:name\\\".`\n );\n }\n\n if (!apiKey || !apiKey.trim()) {\n throw new Error(\"A non-empty apiKey is required to fetch icons.\");\n }\n\n const normalizedSize = Number.isFinite(size) && size > 0 ? Math.round(size) : 28;\n const normalizedBaseUrl = baseUrl.endsWith(\"/\") ? baseUrl.slice(0, -1) : baseUrl;\n\n const url = new URL(\n `/api/icon/${encodeURIComponent(parsed.prefix)}/${encodeURIComponent(parsed.iconName)}`,\n normalizedBaseUrl\n );\n\n url.searchParams.set(\"format\", \"svg\");\n url.searchParams.set(\"width\", String(normalizedSize));\n url.searchParams.set(\"height\", String(normalizedSize));\n url.searchParams.set(\"key\", apiKey);\n\n return url.toString();\n}\n","const HEX_BLACK_REGEX = /#000000\\b|#000\\b/i;\nconst BLACK_WORD_REGEX = /\\bblack\\b/i;\nconst RGB_BLACK_REGEX = /rgb\\(\\s*0\\s*,\\s*0\\s*,\\s*0\\s*\\)/i;\n\nconst STROKE_ATTR_REGEX = /stroke=[\"']([^\"']+)[\"']/gi;\nconst FILL_ATTR_REGEX = /fill=[\"']([^\"']+)[\"']/gi;\nconst STYLE_ATTR_REGEX = /style=[\"']([^\"']+)[\"']/gi;\n\nfunction normalizeColorToken(value: string): string {\n const trimmed = value.trim();\n if (\n HEX_BLACK_REGEX.test(trimmed) ||\n BLACK_WORD_REGEX.test(trimmed) ||\n RGB_BLACK_REGEX.test(trimmed)\n ) {\n return \"currentColor\";\n }\n return trimmed;\n}\n\nfunction normalizeStyleInline(style: string): string {\n return style\n .split(\";\")\n .map((segment) => segment.trim())\n .filter(Boolean)\n .map((segment) => {\n const [rawProperty, rawValue] = segment.split(\":\");\n if (!rawProperty || !rawValue) {\n return segment;\n }\n const property = rawProperty.trim().toLowerCase();\n if (property !== \"stroke\" && property !== \"fill\") {\n return segment;\n }\n const value = normalizeColorToken(rawValue);\n return `${rawProperty.trim()}: ${value}`;\n })\n .join(\"; \");\n}\n\nexport function processSvgForCurrentColor(svg: string): string {\n let processed = svg;\n\n processed = processed.replace(STROKE_ATTR_REGEX, (_full, value: string) => {\n const nextValue = normalizeColorToken(value);\n return `stroke=\"${nextValue}\"`;\n });\n\n processed = processed.replace(FILL_ATTR_REGEX, (_full, value: string) => {\n const trimmed = value.trim();\n if (trimmed.toLowerCase() === \"none\") {\n return `fill=\"${trimmed}\"`;\n }\n const nextValue = normalizeColorToken(trimmed);\n return `fill=\"${nextValue}\"`;\n });\n\n processed = processed.replace(STYLE_ATTR_REGEX, (_full, value: string) => {\n const normalized = normalizeStyleInline(value);\n return `style=\"${normalized}\"`;\n });\n\n return processed;\n}\n","import type { FetchIconSvgOptions } from \"../types\";\nimport { processSvgForCurrentColor } from \"./svg\";\n\nconst svgCache = new Map<string, string>();\nconst inFlight = new Map<string, Promise<string>>();\n\nexport function clearIconCache(): void {\n svgCache.clear();\n inFlight.clear();\n}\n\nexport async function fetchIconSvg(\n url: string,\n { fetcher = fetch }: FetchIconSvgOptions = {}\n): Promise<string> {\n const cached = svgCache.get(url);\n if (cached) {\n return cached;\n }\n\n const inflightRequest = inFlight.get(url);\n if (inflightRequest) {\n return inflightRequest;\n }\n\n const request = (async () => {\n const response = await fetcher(url);\n if (!response.ok) {\n throw new Error(`Failed to fetch icon: ${response.status}`);\n }\n\n const rawSvg = await response.text();\n const processedSvg = processSvgForCurrentColor(rawSvg);\n svgCache.set(url, processedSvg);\n return processedSvg;\n })();\n\n inFlight.set(url, request);\n\n try {\n return await request;\n } finally {\n inFlight.delete(url);\n }\n}\n","\"use client\";\n\nimport * as React from \"react\";\n\nimport type { IconProps } from \"../types\";\nimport { buildIconRequestUrl, fetchIconSvg, parseIconName } from \"../utils\";\n\nfunction joinClassNames(...classNames: Array<string | undefined>): string {\n return classNames.filter(Boolean).join(\" \");\n}\n\nexport function Icon({\n name,\n size = 28,\n color,\n className,\n alt,\n apiKey,\n baseUrl,\n fallback\n}: IconProps) {\n const [svgContent, setSvgContent] = React.useState<string | null>(null);\n const [hasError, setHasError] = React.useState(false);\n\n const parsedName = React.useMemo(() => parseIconName(name), [name]);\n\n const requestUrl = React.useMemo(() => {\n if (!parsedName) {\n return null;\n }\n\n try {\n return buildIconRequestUrl({ name, size, apiKey, baseUrl });\n } catch {\n return null;\n }\n }, [apiKey, baseUrl, name, parsedName, size]);\n\n React.useEffect(() => {\n let disposed = false;\n\n if (!requestUrl) {\n setSvgContent(null);\n setHasError(true);\n return;\n }\n\n setHasError(false);\n setSvgContent(null);\n\n fetchIconSvg(requestUrl)\n .then((svg) => {\n if (!disposed) {\n setSvgContent(svg);\n }\n })\n .catch(() => {\n if (!disposed) {\n setHasError(true);\n }\n });\n\n return () => {\n disposed = true;\n };\n }, [requestUrl]);\n\n const ariaLabel = alt || parsedName?.iconName;\n const a11yProps = ariaLabel\n ? ({ role: \"img\", \"aria-label\": ariaLabel } as const)\n : ({ \"aria-hidden\": true } as const);\n\n if (!svgContent) {\n return (\n <span\n className={joinClassNames(\"inline-block\", className)}\n style={{ width: size, height: size }}\n data-state={hasError ? \"error\" : \"loading\"}\n {...a11yProps}\n >\n {hasError ? fallback : null}\n </span>\n );\n }\n\n return (\n <span\n className={joinClassNames(\"inline-flex items-center justify-center\", className)}\n style={{\n width: size,\n height: size,\n color: color || \"inherit\"\n }}\n data-state=\"ready\"\n dangerouslySetInnerHTML={{ __html: svgContent }}\n {...a11yProps}\n />\n );\n}\n\nexport const DynamicIcon = Icon;\n"]} | ||
| {"version":3,"sources":["../src/utils/iconUrl.ts","../src/utils/svg.ts","../src/utils/fetchIconSvg.ts","../src/core/Icon.tsx"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { BuildIconRequestUrlOptions, ParsedIconName } from \"../types\";\n\nexport const DEFAULT_ICON_API_BASE_URL = \"https://icons.opensite.ai\";\n\nexport function parseIconName(name: string): ParsedIconName | null {\n if (!name || typeof name !== \"string\") {\n return null;\n }\n\n const trimmed = name.trim();\n if (!trimmed) {\n return null;\n }\n\n const slashIndex = trimmed.indexOf(\"/\");\n const colonIndex = trimmed.indexOf(\":\");\n\n let separatorIndex = -1;\n if (slashIndex >= 0 && colonIndex >= 0) {\n separatorIndex = Math.min(slashIndex, colonIndex);\n } else if (slashIndex >= 0) {\n separatorIndex = slashIndex;\n } else if (colonIndex >= 0) {\n separatorIndex = colonIndex;\n }\n\n if (separatorIndex <= 0 || separatorIndex >= trimmed.length - 1) {\n return null;\n }\n\n const prefix = trimmed.slice(0, separatorIndex).trim();\n const iconName = trimmed.slice(separatorIndex + 1).trim();\n\n if (!prefix || !iconName) {\n return null;\n }\n\n return { prefix, iconName };\n}\n\nexport function buildIconRequestUrl({\n name,\n size,\n apiKey,\n baseUrl = DEFAULT_ICON_API_BASE_URL\n}: BuildIconRequestUrlOptions): string {\n const parsed = parseIconName(name);\n if (!parsed) {\n throw new Error(\n `Invalid icon name \\\"${name}\\\". Expected format \\\"prefix/name\\\" or \\\"prefix:name\\\".`\n );\n }\n\n if (!apiKey || !apiKey.trim()) {\n throw new Error(\"A non-empty apiKey is required to fetch icons.\");\n }\n\n const normalizedSize = Number.isFinite(size) && size > 0 ? Math.round(size) : 28;\n const normalizedBaseUrl = baseUrl.endsWith(\"/\") ? baseUrl.slice(0, -1) : baseUrl;\n\n const url = new URL(\n `/api/icon/${encodeURIComponent(parsed.prefix)}/${encodeURIComponent(parsed.iconName)}`,\n normalizedBaseUrl\n );\n\n url.searchParams.set(\"format\", \"svg\");\n url.searchParams.set(\"width\", String(normalizedSize));\n url.searchParams.set(\"height\", String(normalizedSize));\n url.searchParams.set(\"key\", apiKey);\n\n return url.toString();\n}\n","const HEX_BLACK_REGEX = /#000000\\b|#000\\b/i;\nconst BLACK_WORD_REGEX = /\\bblack\\b/i;\nconst RGB_BLACK_REGEX = /rgb\\(\\s*0\\s*,\\s*0\\s*,\\s*0\\s*\\)/i;\n\nconst STROKE_ATTR_REGEX = /stroke=[\"']([^\"']+)[\"']/gi;\nconst FILL_ATTR_REGEX = /fill=[\"']([^\"']+)[\"']/gi;\nconst STYLE_ATTR_REGEX = /style=[\"']([^\"']+)[\"']/gi;\n\nfunction normalizeColorToken(value: string): string {\n const trimmed = value.trim();\n if (\n HEX_BLACK_REGEX.test(trimmed) ||\n BLACK_WORD_REGEX.test(trimmed) ||\n RGB_BLACK_REGEX.test(trimmed)\n ) {\n return \"currentColor\";\n }\n return trimmed;\n}\n\nfunction normalizeStyleInline(style: string): string {\n return style\n .split(\";\")\n .map((segment) => segment.trim())\n .filter(Boolean)\n .map((segment) => {\n const [rawProperty, rawValue] = segment.split(\":\");\n if (!rawProperty || !rawValue) {\n return segment;\n }\n const property = rawProperty.trim().toLowerCase();\n if (property !== \"stroke\" && property !== \"fill\") {\n return segment;\n }\n const value = normalizeColorToken(rawValue);\n return `${rawProperty.trim()}: ${value}`;\n })\n .join(\"; \");\n}\n\nexport function processSvgForCurrentColor(svg: string): string {\n let processed = svg;\n\n processed = processed.replace(STROKE_ATTR_REGEX, (_full, value: string) => {\n const nextValue = normalizeColorToken(value);\n return `stroke=\"${nextValue}\"`;\n });\n\n processed = processed.replace(FILL_ATTR_REGEX, (_full, value: string) => {\n const trimmed = value.trim();\n if (trimmed.toLowerCase() === \"none\") {\n return `fill=\"${trimmed}\"`;\n }\n const nextValue = normalizeColorToken(trimmed);\n return `fill=\"${nextValue}\"`;\n });\n\n processed = processed.replace(STYLE_ATTR_REGEX, (_full, value: string) => {\n const normalized = normalizeStyleInline(value);\n return `style=\"${normalized}\"`;\n });\n\n return processed;\n}\n","import type { FetchIconSvgOptions } from \"../types\";\nimport { processSvgForCurrentColor } from \"./svg\";\n\nconst svgCache = new Map<string, string>();\nconst inFlight = new Map<string, Promise<string>>();\n\nexport function clearIconCache(): void {\n svgCache.clear();\n inFlight.clear();\n}\n\nexport async function fetchIconSvg(\n url: string,\n { fetcher = fetch }: FetchIconSvgOptions = {}\n): Promise<string> {\n const cached = svgCache.get(url);\n if (cached) {\n return cached;\n }\n\n const inflightRequest = inFlight.get(url);\n if (inflightRequest) {\n return inflightRequest;\n }\n\n const request = (async () => {\n const response = await fetcher(url);\n if (!response.ok) {\n throw new Error(`Failed to fetch icon: ${response.status}`);\n }\n\n const rawSvg = await response.text();\n const processedSvg = processSvgForCurrentColor(rawSvg);\n svgCache.set(url, processedSvg);\n return processedSvg;\n })();\n\n inFlight.set(url, request);\n\n try {\n return await request;\n } finally {\n inFlight.delete(url);\n }\n}\n","\"use client\";\n\nimport * as React from \"react\";\n\nimport type { IconProps } from \"../types\";\nimport { buildIconRequestUrl, fetchIconSvg, parseIconName } from \"../utils\";\n\nfunction joinClassNames(...classNames: Array<string | undefined>): string {\n return classNames.filter(Boolean).join(\" \");\n}\n\n// Check if we're in a test environment\nconst isTestEnvironment = typeof process !== \"undefined\" && process.env.NODE_ENV === \"test\";\n\nexport function Icon({\n name,\n size = 28,\n color,\n className,\n alt,\n apiKey,\n baseUrl,\n fallback\n}: IconProps) {\n const [state, setState] = React.useState<{\n svg: string | null;\n error: boolean;\n }>({ svg: null, error: false });\n\n const parsedName = React.useMemo(() => parseIconName(name), [name]);\n\n const requestUrl = React.useMemo(() => {\n if (!parsedName) {\n return null;\n }\n\n try {\n return buildIconRequestUrl({ name, size, apiKey, baseUrl });\n } catch {\n return null;\n }\n }, [apiKey, baseUrl, name, parsedName, size]);\n\n React.useEffect(() => {\n let disposed = false;\n\n if (!requestUrl) {\n // Only set error state if not already in error state\n setState((prev) => (prev.error && !prev.svg ? prev : { svg: null, error: true }));\n return;\n }\n\n // Reset state only if needed\n setState((prev) => (!prev.svg && !prev.error ? prev : { svg: null, error: false }));\n\n // In test environment, use a microtask to avoid act warnings\n const loadIcon = async () => {\n try {\n const svg = await fetchIconSvg(requestUrl);\n if (!disposed) {\n if (isTestEnvironment) {\n // Use queueMicrotask to ensure state updates happen in the next tick\n queueMicrotask(() => {\n if (!disposed) {\n setState({ svg, error: false });\n }\n });\n } else {\n setState({ svg, error: false });\n }\n }\n } catch {\n if (!disposed) {\n if (isTestEnvironment) {\n queueMicrotask(() => {\n if (!disposed) {\n setState({ svg: null, error: true });\n }\n });\n } else {\n setState({ svg: null, error: true });\n }\n }\n }\n };\n\n loadIcon();\n\n return () => {\n disposed = true;\n };\n }, [requestUrl]);\n\n const ariaLabel = alt || parsedName?.iconName;\n const a11yProps = ariaLabel\n ? ({ role: \"img\", \"aria-label\": ariaLabel } as const)\n : ({ \"aria-hidden\": true } as const);\n\n if (!state.svg) {\n return (\n <span\n className={joinClassNames(\"inline-block\", className)}\n style={{ width: size, height: size }}\n data-state={state.error ? \"error\" : \"loading\"}\n {...a11yProps}\n >\n {state.error ? fallback : null}\n </span>\n );\n }\n\n return (\n <span\n className={joinClassNames(\"inline-flex items-center justify-center\", className)}\n style={{\n width: size,\n height: size,\n color: color || \"inherit\"\n }}\n data-state=\"ready\"\n dangerouslySetInnerHTML={{ __html: state.svg }}\n {...a11yProps}\n />\n );\n}\n\nexport const DynamicIcon = Icon;\n"]} |
+1
-1
| { | ||
| "name": "@page-speed/icon", | ||
| "version": "0.1.0", | ||
| "version": "0.1.1", | ||
| "description": "Performance-optimized dynamic icon component for DashTrack and OpenSite apps.", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
208689
7.6%2058
5.54%7
600%