@stevederico/skateboard-ui 2.15.0 → 2.17.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.
@@ -0,0 +1,16 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(for file in App.jsx AppSidebar.jsx Context.jsx Layout.jsx TabBar.jsx TextView.jsx SettingsView.jsx SignInView.jsx LandingView.jsx UpgradeSheet.jsx PaymentView.jsx SignUpView.jsx)",
5
+ "Bash(do)",
6
+ "Bash(if [ -f \"$file\" ])",
7
+ "Bash(then)",
8
+ "Bash(node --check:*)",
9
+ "Bash(echo:*)",
10
+ "Bash(fi)",
11
+ "Bash(done)",
12
+ "Bash(git commit:*)",
13
+ "Bash(git push:*)"
14
+ ]
15
+ }
16
+ }
Binary file
Binary file
Binary file
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # CHANGELOG
2
2
 
3
+ 2.17.0
4
+
5
+ Fix DynamicIcon forwardRef detection
6
+
7
+ 2.16.0
8
+
9
+ Fix DynamicIcon static imports
10
+
3
11
  2.15.0
4
12
 
5
13
  Add authOverlay 401 retry logic
@@ -1,5 +1,4 @@
1
- import React, { useState, useEffect } from "react";
2
- import { cn } from "../shadcn/lib/utils.js";
1
+ import * as LucideIcons from "lucide-react";
3
2
 
4
3
  /**
5
4
  * Convert a kebab-case, snake_case, or space-separated string to PascalCase.
@@ -27,42 +26,6 @@ function toKebabCase(str) {
27
26
  .toLowerCase();
28
27
  }
29
28
 
30
- /** Cache of resolved icon components keyed by name */
31
- const iconCache = new Map();
32
-
33
- /** Cache of in-flight import promises keyed by module name */
34
- const importCache = new Map();
35
-
36
- /**
37
- * Load a single Lucide icon by its kebab-case file name (e.g. "arrow-right").
38
- * Each icon is imported individually (~1KB) instead of loading the entire
39
- * icon library. Results are cached for instant subsequent lookups.
40
- *
41
- * @param {string} kebabName - kebab-case icon file name
42
- * @param {string} cacheKey - PascalCase name used as cache key
43
- * @returns {Promise<React.ComponentType|null>} Icon component or null
44
- */
45
- function loadIcon(kebabName, cacheKey) {
46
- if (iconCache.has(cacheKey)) return Promise.resolve(iconCache.get(cacheKey));
47
- if (importCache.has(cacheKey)) return importCache.get(cacheKey);
48
-
49
- const promise = import(`/node_modules/lucide-react/dist/esm/icons/${kebabName}.js`)
50
- .then((mod) => {
51
- const Icon = mod.default || null;
52
- iconCache.set(cacheKey, Icon);
53
- importCache.delete(cacheKey);
54
- return Icon;
55
- })
56
- .catch(() => {
57
- iconCache.set(cacheKey, null);
58
- importCache.delete(cacheKey);
59
- return null;
60
- });
61
-
62
- importCache.set(cacheKey, promise);
63
- return promise;
64
- }
65
-
66
29
  /**
67
30
  * Resolve an icon name in any format to a PascalCase name and kebab-case file path.
68
31
  * e.g. "layout-dashboard" → { pascal: "LayoutDashboard", kebab: "layout-dashboard" }
@@ -84,23 +47,23 @@ function toIconName(name) {
84
47
  }
85
48
 
86
49
  /**
87
- * Check if a name string has been resolved to a valid icon.
88
- * Returns false for unloaded or invalid icons.
50
+ * Check if a name string can be resolved to a valid Lucide icon.
89
51
  *
90
52
  * @param {string} name - Icon name to check
91
- * @returns {boolean} True if icon is cached and valid
53
+ * @returns {boolean} True if icon exists in the Lucide library
92
54
  */
93
55
  export function canResolveIcon(name) {
94
56
  const { pascal } = toIconName(name);
95
- return iconCache.has(pascal) && iconCache.get(pascal) !== null;
57
+ const icon = LucideIcons[pascal];
58
+ return !!icon && (typeof icon === "function" || typeof icon?.render === "function");
96
59
  }
97
60
 
98
61
  /**
99
- * Render a Lucide icon by name string with per-icon lazy loading.
62
+ * Render a Lucide icon by name string using a static import of all icons.
100
63
  *
101
- * Each icon is imported individually from lucide-react (~1KB per icon)
102
- * instead of loading the entire library. Resolved icons are cached in memory
103
- * for instant rendering on subsequent uses.
64
+ * All icons are bundled at build time from lucide-react, ensuring they work
65
+ * in both Vite dev mode and production builds. Icons are looked up synchronously
66
+ * by PascalCase name from the LucideIcons namespace.
104
67
  *
105
68
  * Accepts kebab-case ("layout-dashboard"), PascalCase ("LayoutDashboard"),
106
69
  * or legacy prefixed ("IconLayoutDashboard") names.
@@ -111,7 +74,7 @@ export function canResolveIcon(name) {
111
74
  * @param {string} [props.color='currentColor'] - Icon stroke color
112
75
  * @param {number} [props.strokeWidth=2] - Stroke width
113
76
  * @param {string} [props.className] - Additional CSS classes
114
- * @returns {JSX.Element|null} Rendered icon or null if loading/not found
77
+ * @returns {JSX.Element|null} Rendered icon or null if not found
115
78
  *
116
79
  * @example
117
80
  * import DynamicIcon from '@stevederico/skateboard-ui/DynamicIcon';
@@ -128,18 +91,10 @@ const DynamicIcon = ({
128
91
  className,
129
92
  ...props
130
93
  }) => {
131
- const { pascal, kebab } = toIconName(name);
132
-
133
- const [Icon, setIcon] = useState(() => iconCache.get(pascal) || null);
134
-
135
- useEffect(() => {
136
- if (Icon) return;
137
- loadIcon(kebab, pascal).then((resolved) => {
138
- if (resolved) setIcon(() => resolved);
139
- });
140
- }, [pascal, kebab, Icon]);
94
+ const { pascal } = toIconName(name);
95
+ const Icon = LucideIcons[pascal];
141
96
 
142
- if (!Icon) return null;
97
+ if (!Icon || (typeof Icon !== "function" && typeof Icon?.render !== "function")) return null;
143
98
 
144
99
  return (
145
100
  <Icon
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@stevederico/skateboard-ui",
3
3
  "private": false,
4
- "version": "2.15.0",
4
+ "version": "2.17.0",
5
5
  "type": "module",
6
6
  "exports": {
7
7
  "./Sidebar": {