@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.
- package/.claude/settings.local.json +16 -0
- package/.playwright-mcp/dark-mode-borders.png +0 -0
- package/.playwright-mcp/dark-mode-inputs.png +0 -0
- package/.playwright-mcp/dark-sidebar.png +0 -0
- package/.playwright-mcp/features-centered-v2.png +0 -0
- package/.playwright-mcp/features-centered-v3.png +0 -0
- package/.playwright-mcp/features-centered.png +0 -0
- package/.playwright-mcp/features-check.png +0 -0
- package/.playwright-mcp/landing-dark.png +0 -0
- package/.playwright-mcp/light-mode-borders.png +0 -0
- package/.playwright-mcp/sidebar-dark-mode.png +0 -0
- package/.playwright-mcp/sidebar-light-mode.png +0 -0
- package/.playwright-mcp/signin-dark.png +0 -0
- package/CHANGELOG.md +8 -0
- package/core/DynamicIcon.jsx +13 -58
- package/package.json +1 -1
|
@@ -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
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/CHANGELOG.md
CHANGED
package/core/DynamicIcon.jsx
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
62
|
+
* Render a Lucide icon by name string using a static import of all icons.
|
|
100
63
|
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
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
|
|
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
|
|
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
|