viconic-react-icons 1.1.7 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1978,6 +1978,30 @@ var import_react = __toESM(require("react"));
1978
1978
  })();
1979
1979
 
1980
1980
  // src/index.jsx
1981
+ (function _preloadKitCache() {
1982
+ if (typeof window === "undefined" || typeof localStorage === "undefined") return;
1983
+ try {
1984
+ const W = window.__viconic = window.__viconic || {};
1985
+ W.icons = W.icons || {};
1986
+ const TTL = 3 * 24 * 60 * 60 * 1e3;
1987
+ for (let i = 0; i < localStorage.length; i++) {
1988
+ const key = localStorage.key(i);
1989
+ if (!key || !key.startsWith("viconic:")) continue;
1990
+ try {
1991
+ const c = JSON.parse(localStorage.getItem(key));
1992
+ if (c && c.d && Date.now() - c.t < TTL) {
1993
+ for (const n in c.d) {
1994
+ if (!Object.prototype.hasOwnProperty.call(c.d, n)) continue;
1995
+ const svg = c.d[n];
1996
+ if (!W.icons[n]) W.icons[n] = svg;
1997
+ }
1998
+ }
1999
+ } catch (e) {
2000
+ }
2001
+ }
2002
+ } catch (e) {
2003
+ }
2004
+ })();
1981
2005
  var _initializedKits = /* @__PURE__ */ new Set();
1982
2006
  function initViconic(options = {}) {
1983
2007
  const { kitId, cdnBase = "cdn.viconic.dev", version } = options;
@@ -2013,31 +2037,56 @@ var ViconicIcon = ({ name, className, style, size, color, ...props }) => {
2013
2037
  prevNameRef.current = name;
2014
2038
  }
2015
2039
  const isKitIcon = name && name.startsWith("@");
2016
- if (window.CopyIcons && window.CopyIcons.forceProcess && !isKitIcon) {
2017
- window.CopyIcons.forceProcess(el);
2018
- }
2019
- const isLoaded = el.classList.contains("vi-ok") || el.classList.contains("svg-loaded");
2020
- if (isLoaded) return;
2021
- let retries = 0;
2022
- const maxRetries = 100;
2023
- let hasTriggeredReload = false;
2024
- const timer = setInterval(() => {
2025
- retries++;
2026
- if (el.classList.contains("vi-ok") || el.classList.contains("svg-loaded")) {
2027
- clearInterval(timer);
2028
- return;
2040
+ const prefixMatch = isKitIcon ? name.match(/^@([^/]+)\/(.+)$/) : null;
2041
+ function isInjected() {
2042
+ return el.classList.contains("vi-ok") || el.classList.contains("svg-loaded");
2043
+ }
2044
+ if (!isInjected()) {
2045
+ const W = window.__viconic;
2046
+ if (W && W.icons) {
2047
+ const iconBaseName = prefixMatch ? prefixMatch[2] : null;
2048
+ const svg = iconBaseName && W.icons[iconBaseName] || name && W.icons[name] || null;
2049
+ if (svg) {
2050
+ el.innerHTML = svg;
2051
+ el.classList.add("vi-ok", "svg-loaded");
2052
+ }
2053
+ }
2054
+ }
2055
+ if (!isInjected() && !isKitIcon) {
2056
+ if (window.CopyIcons && window.CopyIcons.forceProcess) {
2057
+ window.CopyIcons.forceProcess(el);
2058
+ }
2059
+ }
2060
+ if (!isInjected() && el._u) {
2061
+ el._u();
2062
+ }
2063
+ if (isInjected()) return;
2064
+ const delays = [50, 100, 200, 400, 800, 500, 500, 500, 500, 500, 500, 500];
2065
+ let attempt = 0;
2066
+ let timerId = null;
2067
+ let reloadTriggered = false;
2068
+ function tryInject() {
2069
+ if (isInjected()) return;
2070
+ const W = window.__viconic;
2071
+ if (W && W.icons) {
2072
+ const iconBaseName = prefixMatch ? prefixMatch[2] : null;
2073
+ const svg = iconBaseName && W.icons[iconBaseName] || name && W.icons[name] || null;
2074
+ if (svg) {
2075
+ el.innerHTML = svg;
2076
+ el.classList.add("vi-ok", "svg-loaded");
2077
+ return;
2078
+ }
2029
2079
  }
2030
2080
  if (el._u) el._u();
2031
- if (window.CopyIcons && window.CopyIcons.forceProcess && !isKitIcon) {
2081
+ if (!isKitIcon && window.CopyIcons && window.CopyIcons.forceProcess) {
2032
2082
  window.CopyIcons.forceProcess(el);
2033
2083
  }
2034
- if (isKitIcon && !hasTriggeredReload && retries === 10) {
2035
- hasTriggeredReload = true;
2036
- const prefixMatch = name.match(/^@([^\/]+)/);
2037
- if (prefixMatch && window.CopyIconsKit) {
2038
- const prefix = prefixMatch[1];
2039
- for (const kitId in window.CopyIconsKit) {
2040
- const kit = window.CopyIconsKit[kitId];
2084
+ if (isKitIcon && !reloadTriggered && attempt >= 4 && prefixMatch) {
2085
+ reloadTriggered = true;
2086
+ const prefix = prefixMatch[1];
2087
+ if (window.CopyIconsKit) {
2088
+ for (const kId in window.CopyIconsKit) {
2089
+ const kit = window.CopyIconsKit[kId];
2041
2090
  if (kit.config && kit.config.prefix === prefix && typeof kit.reload === "function") {
2042
2091
  kit.reload();
2043
2092
  break;
@@ -2045,9 +2094,16 @@ var ViconicIcon = ({ name, className, style, size, color, ...props }) => {
2045
2094
  }
2046
2095
  }
2047
2096
  }
2048
- if (retries >= maxRetries) clearInterval(timer);
2049
- }, 100);
2050
- return () => clearInterval(timer);
2097
+ if (isInjected()) return;
2098
+ attempt++;
2099
+ if (attempt < delays.length) {
2100
+ timerId = setTimeout(tryInject, delays[attempt]);
2101
+ }
2102
+ }
2103
+ timerId = setTimeout(tryInject, delays[0]);
2104
+ return () => {
2105
+ if (timerId) clearTimeout(timerId);
2106
+ };
2051
2107
  }, [name, className, size, color, style]);
2052
2108
  const combinedStyle = {
2053
2109
  ...size ? { fontSize: size } : {},
package/dist/index.mjs CHANGED
@@ -1943,6 +1943,30 @@ import React, { useEffect, useRef } from "react";
1943
1943
  })();
1944
1944
 
1945
1945
  // src/index.jsx
1946
+ (function _preloadKitCache() {
1947
+ if (typeof window === "undefined" || typeof localStorage === "undefined") return;
1948
+ try {
1949
+ const W = window.__viconic = window.__viconic || {};
1950
+ W.icons = W.icons || {};
1951
+ const TTL = 3 * 24 * 60 * 60 * 1e3;
1952
+ for (let i = 0; i < localStorage.length; i++) {
1953
+ const key = localStorage.key(i);
1954
+ if (!key || !key.startsWith("viconic:")) continue;
1955
+ try {
1956
+ const c = JSON.parse(localStorage.getItem(key));
1957
+ if (c && c.d && Date.now() - c.t < TTL) {
1958
+ for (const n in c.d) {
1959
+ if (!Object.prototype.hasOwnProperty.call(c.d, n)) continue;
1960
+ const svg = c.d[n];
1961
+ if (!W.icons[n]) W.icons[n] = svg;
1962
+ }
1963
+ }
1964
+ } catch (e) {
1965
+ }
1966
+ }
1967
+ } catch (e) {
1968
+ }
1969
+ })();
1946
1970
  var _initializedKits = /* @__PURE__ */ new Set();
1947
1971
  function initViconic(options = {}) {
1948
1972
  const { kitId, cdnBase = "cdn.viconic.dev", version } = options;
@@ -1978,31 +2002,56 @@ var ViconicIcon = ({ name, className, style, size, color, ...props }) => {
1978
2002
  prevNameRef.current = name;
1979
2003
  }
1980
2004
  const isKitIcon = name && name.startsWith("@");
1981
- if (window.CopyIcons && window.CopyIcons.forceProcess && !isKitIcon) {
1982
- window.CopyIcons.forceProcess(el);
1983
- }
1984
- const isLoaded = el.classList.contains("vi-ok") || el.classList.contains("svg-loaded");
1985
- if (isLoaded) return;
1986
- let retries = 0;
1987
- const maxRetries = 100;
1988
- let hasTriggeredReload = false;
1989
- const timer = setInterval(() => {
1990
- retries++;
1991
- if (el.classList.contains("vi-ok") || el.classList.contains("svg-loaded")) {
1992
- clearInterval(timer);
1993
- return;
2005
+ const prefixMatch = isKitIcon ? name.match(/^@([^/]+)\/(.+)$/) : null;
2006
+ function isInjected() {
2007
+ return el.classList.contains("vi-ok") || el.classList.contains("svg-loaded");
2008
+ }
2009
+ if (!isInjected()) {
2010
+ const W = window.__viconic;
2011
+ if (W && W.icons) {
2012
+ const iconBaseName = prefixMatch ? prefixMatch[2] : null;
2013
+ const svg = iconBaseName && W.icons[iconBaseName] || name && W.icons[name] || null;
2014
+ if (svg) {
2015
+ el.innerHTML = svg;
2016
+ el.classList.add("vi-ok", "svg-loaded");
2017
+ }
2018
+ }
2019
+ }
2020
+ if (!isInjected() && !isKitIcon) {
2021
+ if (window.CopyIcons && window.CopyIcons.forceProcess) {
2022
+ window.CopyIcons.forceProcess(el);
2023
+ }
2024
+ }
2025
+ if (!isInjected() && el._u) {
2026
+ el._u();
2027
+ }
2028
+ if (isInjected()) return;
2029
+ const delays = [50, 100, 200, 400, 800, 500, 500, 500, 500, 500, 500, 500];
2030
+ let attempt = 0;
2031
+ let timerId = null;
2032
+ let reloadTriggered = false;
2033
+ function tryInject() {
2034
+ if (isInjected()) return;
2035
+ const W = window.__viconic;
2036
+ if (W && W.icons) {
2037
+ const iconBaseName = prefixMatch ? prefixMatch[2] : null;
2038
+ const svg = iconBaseName && W.icons[iconBaseName] || name && W.icons[name] || null;
2039
+ if (svg) {
2040
+ el.innerHTML = svg;
2041
+ el.classList.add("vi-ok", "svg-loaded");
2042
+ return;
2043
+ }
1994
2044
  }
1995
2045
  if (el._u) el._u();
1996
- if (window.CopyIcons && window.CopyIcons.forceProcess && !isKitIcon) {
2046
+ if (!isKitIcon && window.CopyIcons && window.CopyIcons.forceProcess) {
1997
2047
  window.CopyIcons.forceProcess(el);
1998
2048
  }
1999
- if (isKitIcon && !hasTriggeredReload && retries === 10) {
2000
- hasTriggeredReload = true;
2001
- const prefixMatch = name.match(/^@([^\/]+)/);
2002
- if (prefixMatch && window.CopyIconsKit) {
2003
- const prefix = prefixMatch[1];
2004
- for (const kitId in window.CopyIconsKit) {
2005
- const kit = window.CopyIconsKit[kitId];
2049
+ if (isKitIcon && !reloadTriggered && attempt >= 4 && prefixMatch) {
2050
+ reloadTriggered = true;
2051
+ const prefix = prefixMatch[1];
2052
+ if (window.CopyIconsKit) {
2053
+ for (const kId in window.CopyIconsKit) {
2054
+ const kit = window.CopyIconsKit[kId];
2006
2055
  if (kit.config && kit.config.prefix === prefix && typeof kit.reload === "function") {
2007
2056
  kit.reload();
2008
2057
  break;
@@ -2010,9 +2059,16 @@ var ViconicIcon = ({ name, className, style, size, color, ...props }) => {
2010
2059
  }
2011
2060
  }
2012
2061
  }
2013
- if (retries >= maxRetries) clearInterval(timer);
2014
- }, 100);
2015
- return () => clearInterval(timer);
2062
+ if (isInjected()) return;
2063
+ attempt++;
2064
+ if (attempt < delays.length) {
2065
+ timerId = setTimeout(tryInject, delays[attempt]);
2066
+ }
2067
+ }
2068
+ timerId = setTimeout(tryInject, delays[0]);
2069
+ return () => {
2070
+ if (timerId) clearTimeout(timerId);
2071
+ };
2016
2072
  }, [name, className, size, color, style]);
2017
2073
  const combinedStyle = {
2018
2074
  ...size ? { fontSize: size } : {},
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "viconic-react-icons",
3
- "version": "1.1.7",
3
+ "version": "1.3.0",
4
4
  "description": "Viconic Smart Icons loader for React — supports Kit and 200k+ system icons",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -51,12 +51,12 @@
51
51
  // already successfully injected. We intercept define() to patch _u()
52
52
  // so it skips elements that Smart Loader has already handled.
53
53
  const _origCEDefine = customElements.define.bind(customElements);
54
- customElements.define = function(name, cls, opts) {
54
+ customElements.define = function (name, cls, opts) {
55
55
  if (name === 'viconic-icon' && cls && cls.prototype) {
56
56
  // Patch _u() to skip elements already handled by Smart Loader
57
57
  if (cls.prototype._u) {
58
58
  const origU = cls.prototype._u;
59
- cls.prototype._u = function() {
59
+ cls.prototype._u = function () {
60
60
  if (this.classList.contains('svg-loaded')) return;
61
61
  return origU.call(this);
62
62
  };
@@ -64,7 +64,7 @@
64
64
  // Patch connectedCallback to also guard svg-loaded
65
65
  if (cls.prototype.connectedCallback) {
66
66
  const origCC = cls.prototype.connectedCallback;
67
- cls.prototype.connectedCallback = function() {
67
+ cls.prototype.connectedCallback = function () {
68
68
  if (this.classList.contains('svg-loaded')) return;
69
69
  return origCC.call(this);
70
70
  };
@@ -771,13 +771,13 @@
771
771
  const fillStyle = (originalFill === 'none') ? 'none' : 'currentColor';
772
772
  svg.style.cssText = `width:1em;height:1em;fill:${fillStyle};display:inline-block;vertical-align:middle;`;
773
773
  }
774
-
774
+
775
775
  if (element.getAttribute('animate') === 'trace') {
776
776
  const isStrokeBased = (originalFill === 'none');
777
777
  svg.style.setProperty('--trace-end-stroke', isStrokeBased ? 'currentColor' : 'transparent');
778
778
  svg.style.setProperty('--trace-end-stroke-width', isStrokeBased ? (svg.getAttribute('stroke-width') || '2px') : '0px');
779
779
  svg.style.setProperty('--trace-stroke-width', isStrokeBased ? (svg.getAttribute('stroke-width') || '2px') : '1px');
780
-
780
+
781
781
  const paths = svg.querySelectorAll('path, ellipse, circle, rect, polygon, polyline');
782
782
  for (let i = 0; i < paths.length; i++) {
783
783
  const p = paths[i];
package/src/index.jsx CHANGED
@@ -1,6 +1,36 @@
1
1
  import React, { useEffect, useRef } from 'react';
2
2
  import './copyicons-smart-loader.js';
3
3
 
4
+ // ============================================
5
+ // PRE-LOAD KIT CACHE SYNCHRONOUSLY AT MODULE LEVEL
6
+ // Same architecture as kit loader: read localStorage before any render
7
+ // so icons are available instantly when components mount.
8
+ // ============================================
9
+ (function _preloadKitCache() {
10
+ if (typeof window === 'undefined' || typeof localStorage === 'undefined') return;
11
+ try {
12
+ const W = (window.__viconic = window.__viconic || {});
13
+ W.icons = W.icons || {};
14
+ const TTL = 3 * 24 * 60 * 60 * 1000; // 3 days
15
+ for (let i = 0; i < localStorage.length; i++) {
16
+ const key = localStorage.key(i);
17
+ if (!key || !key.startsWith('viconic:')) continue;
18
+ try {
19
+ const c = JSON.parse(localStorage.getItem(key));
20
+ if (c && c.d && Date.now() - c.t < TTL) {
21
+ // Register all icon name variants into W.icons
22
+ for (const n in c.d) {
23
+ if (!Object.prototype.hasOwnProperty.call(c.d, n)) continue;
24
+ const svg = c.d[n];
25
+ // Store by plain name so _u() can find it regardless of prefix
26
+ if (!W.icons[n]) W.icons[n] = svg;
27
+ }
28
+ }
29
+ } catch (e) { /* corrupted entry, skip */ }
30
+ }
31
+ } catch (e) { }
32
+ })();
33
+
4
34
  // ============================================
5
35
  // Track initialized kits to avoid duplicate scripts
6
36
  // ============================================
@@ -37,14 +67,14 @@ export function initViconic(options = {}) {
37
67
 
38
68
  const script = document.createElement('script');
39
69
  script.id = scriptId;
40
-
70
+
41
71
  // Add cache busting for development or specific version updates
42
72
  let url = `https://${cdnBase}/kits/${kitId}/loader.js`;
43
73
  if (version) {
44
74
  const vQuery = version === 'dev' ? Date.now() : version;
45
75
  url += `?v=${vQuery}`;
46
76
  }
47
-
77
+
48
78
  script.src = url;
49
79
  script.async = true;
50
80
  document.head.appendChild(script);
@@ -72,53 +102,87 @@ export const ViconicIcon = ({ name, className, style, size, color, ...props }) =
72
102
  if (!el) return;
73
103
 
74
104
  // --- FIX DOM REUSE (Reconciliation) ---
75
- // React reuses DOM nodes. If `name` changes, we must wipe the old state
76
- // because the DOM node might still have 'svg-loaded' or 'vi-ok'.
77
105
  if (prevNameRef.current !== name) {
78
106
  el.innerHTML = '';
79
107
  el.classList.remove('vi-ok', 'svg-loaded', 'vi-mono', 'ci-multicolor');
80
108
  prevNameRef.current = name;
81
109
  }
82
110
 
83
- // Path 1: Smart loader (system icons with prefix:name format)
84
111
  const isKitIcon = name && name.startsWith('@');
85
- if (window.CopyIcons && window.CopyIcons.forceProcess && !isKitIcon) {
86
- window.CopyIcons.forceProcess(el);
112
+ const prefixMatch = isKitIcon ? name.match(/^@([^/]+)\/(.+)$/) : null;
113
+
114
+ function isInjected() {
115
+ return el.classList.contains('vi-ok') || el.classList.contains('svg-loaded');
87
116
  }
88
117
 
89
- // Path 2: Kit icons kit loader.js registers icons async into
90
- // window.__viconic.icons and the custom element _u() method handles injection.
91
- // If kit hasn't loaded yet, retry periodically until icon appears.
92
- const isLoaded = el.classList.contains('vi-ok') || el.classList.contains('svg-loaded');
93
- if (isLoaded) return;
94
-
95
- let retries = 0;
96
- const maxRetries = 100; // 100 × 100ms = 10 seconds max
97
- let hasTriggeredReload = false;
98
-
99
- const timer = setInterval(() => {
100
- retries++;
101
- if (el.classList.contains('vi-ok') || el.classList.contains('svg-loaded')) {
102
- clearInterval(timer);
103
- return;
118
+ // --- PATH 1: Synchronous inject from W.icons (pre-loaded from localStorage at module init) ---
119
+ if (!isInjected()) {
120
+ const W = window.__viconic;
121
+ if (W && W.icons) {
122
+ // Try all key variants: iconBaseName, @prefix/name, prefix:name, prefix/name
123
+ const iconBaseName = prefixMatch ? prefixMatch[2] : null;
124
+ const svg = (iconBaseName && W.icons[iconBaseName])
125
+ || (name && W.icons[name])
126
+ || null;
127
+ if (svg) {
128
+ el.innerHTML = svg;
129
+ el.classList.add('vi-ok', 'svg-loaded');
130
+ }
104
131
  }
105
- // Trigger custom element update
132
+ }
133
+
134
+ // --- PATH 2: Smart loader for system icons (prefix:name) ---
135
+ if (!isInjected() && !isKitIcon) {
136
+ if (window.CopyIcons && window.CopyIcons.forceProcess) {
137
+ window.CopyIcons.forceProcess(el);
138
+ }
139
+ }
140
+
141
+ // --- PATH 3: Custom element _u() call (kit loader registered element) ---
142
+ if (!isInjected() && el._u) {
143
+ el._u();
144
+ }
145
+
146
+ if (isInjected()) return;
147
+
148
+ // --- PATH 4: Fallback retry — kit loader script may still be loading async ---
149
+ // Use short exponential backoff: 50ms, 100ms, 200ms, 400ms, 800ms, then every 500ms
150
+ // Max 8 seconds total. Much cheaper than 100 × 100ms polling.
151
+ const delays = [50, 100, 200, 400, 800, 500, 500, 500, 500, 500, 500, 500];
152
+ let attempt = 0;
153
+ let timerId = null;
154
+ let reloadTriggered = false;
155
+
156
+ function tryInject() {
157
+ if (isInjected()) return;
158
+
159
+ // Try W.icons again (kit may have loaded by now)
160
+ const W = window.__viconic;
161
+ if (W && W.icons) {
162
+ const iconBaseName = prefixMatch ? prefixMatch[2] : null;
163
+ const svg = (iconBaseName && W.icons[iconBaseName])
164
+ || (name && W.icons[name])
165
+ || null;
166
+ if (svg) {
167
+ el.innerHTML = svg;
168
+ el.classList.add('vi-ok', 'svg-loaded');
169
+ return;
170
+ }
171
+ }
172
+
173
+ // Try custom element _u()
106
174
  if (el._u) el._u();
107
- // Also try smart loader if it's not a kit icon
108
- if (window.CopyIcons && window.CopyIcons.forceProcess && !isKitIcon) {
175
+ if (!isKitIcon && window.CopyIcons && window.CopyIcons.forceProcess) {
109
176
  window.CopyIcons.forceProcess(el);
110
177
  }
111
-
112
- // HMR / Missing Kit Icon Fallback:
113
- // If it's a kit icon and hasn't loaded after 1 second, it might be newly added
114
- // in the Viconic Dashboard. We trigger a background reload of the kit.
115
- if (isKitIcon && !hasTriggeredReload && retries === 10) {
116
- hasTriggeredReload = true;
117
- const prefixMatch = name.match(/^@([^\/]+)/);
118
- if (prefixMatch && window.CopyIconsKit) {
119
- const prefix = prefixMatch[1];
120
- for (const kitId in window.CopyIconsKit) {
121
- const kit = window.CopyIconsKit[kitId];
178
+
179
+ // Trigger kit reload once if still not loaded after ~1s
180
+ if (isKitIcon && !reloadTriggered && attempt >= 4 && prefixMatch) {
181
+ reloadTriggered = true;
182
+ const prefix = prefixMatch[1];
183
+ if (window.CopyIconsKit) {
184
+ for (const kId in window.CopyIconsKit) {
185
+ const kit = window.CopyIconsKit[kId];
122
186
  if (kit.config && kit.config.prefix === prefix && typeof kit.reload === 'function') {
123
187
  kit.reload();
124
188
  break;
@@ -126,11 +190,17 @@ export const ViconicIcon = ({ name, className, style, size, color, ...props }) =
126
190
  }
127
191
  }
128
192
  }
129
-
130
- if (retries >= maxRetries) clearInterval(timer);
131
- }, 100);
132
193
 
133
- return () => clearInterval(timer);
194
+ if (isInjected()) return;
195
+
196
+ attempt++;
197
+ if (attempt < delays.length) {
198
+ timerId = setTimeout(tryInject, delays[attempt]);
199
+ }
200
+ }
201
+
202
+ timerId = setTimeout(tryInject, delays[0]);
203
+ return () => { if (timerId) clearTimeout(timerId); };
134
204
  }, [name, className, size, color, style]);
135
205
 
136
206
  const combinedStyle = {