funuicss 3.6.17 → 3.6.19
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/cors.json +8 -0
- package/css/fun.css +632 -360
- package/gsutil +0 -0
- package/package.json +1 -1
- package/ui/button/Button.js +56 -39
- package/ui/input/Input.js +289 -159
- package/ui/theme/theme.d.ts +16 -6
- package/ui/theme/theme.js +160 -135
- package/utils/componentUtils.d.ts +9 -6
- package/utils/componentUtils.js +42 -170
- package/utils/FireStore.d.ts +0 -10
- package/utils/FireStore.js +0 -273
- package/utils/Firebase.d.ts +0 -2
- package/utils/Firebase.js +0 -16
package/ui/theme/theme.js
CHANGED
|
@@ -70,15 +70,10 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
|
70
70
|
}
|
|
71
71
|
};
|
|
72
72
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
73
|
-
exports.
|
|
73
|
+
exports.useComponentVariant = exports.useTypographyValue = exports.useColor = exports.useProjectData = exports.useThemeConfig = exports.useTypography = exports.useColors = exports.useComponentConfig = exports.useThemeValue = exports.useVariant = exports.useTheme = void 0;
|
|
74
74
|
var react_1 = __importStar(require("react"));
|
|
75
75
|
var themes_1 = require("./themes");
|
|
76
76
|
var darkenUtils_1 = require("./darkenUtils");
|
|
77
|
-
var firestore_1 = require("firebase/firestore");
|
|
78
|
-
var db;
|
|
79
|
-
if (typeof window !== 'undefined') {
|
|
80
|
-
db = require('../../utils/Firebase').default;
|
|
81
|
-
}
|
|
82
77
|
var ThemeContext = (0, react_1.createContext)({
|
|
83
78
|
variant: 'standard',
|
|
84
79
|
setVariant: function () { },
|
|
@@ -96,95 +91,104 @@ var useTheme = function () {
|
|
|
96
91
|
return context;
|
|
97
92
|
};
|
|
98
93
|
exports.useTheme = useTheme;
|
|
99
|
-
// Backward compatibility
|
|
100
94
|
var useVariant = function () {
|
|
101
95
|
var _a = (0, exports.useTheme)(), variant = _a.variant, setVariant = _a.setVariant;
|
|
102
96
|
return { variant: variant, setVariant: setVariant };
|
|
103
97
|
};
|
|
104
98
|
exports.useVariant = useVariant;
|
|
105
99
|
/* -------------------------------------------------------------------------- */
|
|
106
|
-
/* THEME
|
|
100
|
+
/* CDN THEME LOADER */
|
|
107
101
|
/* -------------------------------------------------------------------------- */
|
|
108
|
-
var
|
|
109
|
-
var
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
102
|
+
var loadThemeFromCDN = function (projectId) { return __awaiter(void 0, void 0, void 0, function () {
|
|
103
|
+
var publicResponse, data, error_1, publicUrl, response, data, error_2;
|
|
104
|
+
return __generator(this, function (_a) {
|
|
105
|
+
switch (_a.label) {
|
|
106
|
+
case 0:
|
|
107
|
+
_a.trys.push([0, 4, , 5]);
|
|
108
|
+
return [4 /*yield*/, fetch("/funui.json", {
|
|
109
|
+
cache: 'no-cache',
|
|
110
|
+
})];
|
|
111
|
+
case 1:
|
|
112
|
+
publicResponse = _a.sent();
|
|
113
|
+
if (!publicResponse.ok) return [3 /*break*/, 3];
|
|
114
|
+
return [4 /*yield*/, publicResponse.json()];
|
|
115
|
+
case 2:
|
|
116
|
+
data = _a.sent();
|
|
117
|
+
console.log('✅ Loaded theme from public folder');
|
|
118
|
+
return [2 /*return*/, data];
|
|
119
|
+
case 3: return [3 /*break*/, 5];
|
|
120
|
+
case 4:
|
|
121
|
+
error_1 = _a.sent();
|
|
122
|
+
console.log('No theme in public folder, checking Firebase Storage...');
|
|
123
|
+
return [3 /*break*/, 5];
|
|
124
|
+
case 5:
|
|
125
|
+
_a.trys.push([5, 10, , 11]);
|
|
126
|
+
publicUrl = "https://firebasestorage.googleapis.com/v0/b/funui-4bcd1.firebasestorage.app/o/themes%2F".concat(projectId, ".json?alt=media");
|
|
127
|
+
return [4 /*yield*/, fetch(publicUrl, {
|
|
128
|
+
cache: 'no-cache',
|
|
129
|
+
})];
|
|
130
|
+
case 6:
|
|
131
|
+
response = _a.sent();
|
|
132
|
+
if (!response.ok) return [3 /*break*/, 8];
|
|
133
|
+
return [4 /*yield*/, response.json()];
|
|
134
|
+
case 7:
|
|
135
|
+
data = _a.sent();
|
|
136
|
+
console.log('✅ Loaded theme from Firebase Storage CDN');
|
|
137
|
+
return [2 /*return*/, data];
|
|
138
|
+
case 8:
|
|
139
|
+
console.error('Firebase Storage fetch failed:', response.status, response.statusText);
|
|
140
|
+
_a.label = 9;
|
|
141
|
+
case 9: return [3 /*break*/, 11];
|
|
142
|
+
case 10:
|
|
143
|
+
error_2 = _a.sent();
|
|
144
|
+
console.error('Error loading from Firebase Storage:', error_2);
|
|
145
|
+
return [3 /*break*/, 11];
|
|
146
|
+
case 11: return [2 /*return*/, null];
|
|
122
147
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
148
|
+
});
|
|
149
|
+
}); };
|
|
150
|
+
/* -------------------------------------------------------------------------- */
|
|
151
|
+
/* CSS VARIABLE APPLIER */
|
|
152
|
+
/* -------------------------------------------------------------------------- */
|
|
153
|
+
var applyTypographyVariables = function (typography, root) {
|
|
154
|
+
if (!typography)
|
|
155
|
+
return;
|
|
156
|
+
Object.entries(typography).forEach(function (_a) {
|
|
157
|
+
var key = _a[0], value = _a[1];
|
|
158
|
+
// Convert flat keys to CSS custom property format
|
|
159
|
+
var cssVarName = "--".concat(key.replace(/_/g, '-'));
|
|
160
|
+
root.style.setProperty(cssVarName, value);
|
|
161
|
+
});
|
|
129
162
|
};
|
|
130
|
-
var
|
|
131
|
-
if (
|
|
163
|
+
var applyColorVariables = function (colors, root) {
|
|
164
|
+
if (!colors)
|
|
132
165
|
return;
|
|
133
|
-
|
|
134
|
-
var
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
localStorage.setItem("".concat(CACHE_KEY, "_").concat(projectId), JSON.stringify(cache));
|
|
140
|
-
}
|
|
141
|
-
catch (error) {
|
|
142
|
-
console.error('Error saving theme cache:', error);
|
|
143
|
-
}
|
|
166
|
+
Object.entries(colors).forEach(function (_a) {
|
|
167
|
+
var key = _a[0], value = _a[1];
|
|
168
|
+
// Convert flat keys to CSS custom property format
|
|
169
|
+
var cssVarName = "--".concat(key.replace(/_/g, '-'));
|
|
170
|
+
root.style.setProperty(cssVarName, value);
|
|
171
|
+
});
|
|
144
172
|
};
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
var subscribeToTheme = function (projectId, callback) {
|
|
152
|
-
listenerSubscribers.add(callback);
|
|
153
|
-
// If listener already exists for this project, just subscribe
|
|
154
|
-
if (currentProjectId === projectId && activeListener) {
|
|
155
|
-
return function () {
|
|
156
|
-
listenerSubscribers.delete(callback);
|
|
157
|
-
};
|
|
173
|
+
var applyThemeConfig = function (themeConfig, root) {
|
|
174
|
+
if (!themeConfig)
|
|
175
|
+
return;
|
|
176
|
+
// Apply colors if they exist
|
|
177
|
+
if (themeConfig.colors) {
|
|
178
|
+
applyColorVariables(themeConfig.colors, root);
|
|
158
179
|
}
|
|
159
|
-
//
|
|
160
|
-
if (
|
|
161
|
-
|
|
162
|
-
activeListener = null;
|
|
163
|
-
currentProjectId = null;
|
|
180
|
+
// Apply typography if it exists
|
|
181
|
+
if (themeConfig.typography) {
|
|
182
|
+
applyTypographyVariables(themeConfig.typography, root);
|
|
164
183
|
}
|
|
165
|
-
//
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
return;
|
|
172
|
-
// Save to cache
|
|
173
|
-
saveThemeToCache(projectId, data);
|
|
174
|
-
// Notify all subscribers
|
|
175
|
-
listenerSubscribers.forEach(function (cb) { return cb(data); });
|
|
176
|
-
}, function (error) {
|
|
177
|
-
console.error('Theme listener error:', error);
|
|
178
|
-
});
|
|
179
|
-
activeListener = unsubscribe;
|
|
180
|
-
return function () {
|
|
181
|
-
listenerSubscribers.delete(callback);
|
|
182
|
-
if (listenerSubscribers.size === 0 && activeListener) {
|
|
183
|
-
activeListener();
|
|
184
|
-
activeListener = null;
|
|
185
|
-
currentProjectId = null;
|
|
184
|
+
// Apply any other theme config properties
|
|
185
|
+
Object.entries(themeConfig).forEach(function (_a) {
|
|
186
|
+
var key = _a[0], value = _a[1];
|
|
187
|
+
if (key !== 'colors' && key !== 'typography' && typeof value === 'string') {
|
|
188
|
+
var cssVarName = "--".concat(key.replace(/_/g, '-'));
|
|
189
|
+
root.style.setProperty(cssVarName, value);
|
|
186
190
|
}
|
|
187
|
-
};
|
|
191
|
+
});
|
|
188
192
|
};
|
|
189
193
|
/* -------------------------------------------------------------------------- */
|
|
190
194
|
/* COMPONENT */
|
|
@@ -197,6 +201,7 @@ var ThemeProvider = function (_a) {
|
|
|
197
201
|
var _g = (0, react_1.useState)(true), isLoading = _g[0], setIsLoading = _g[1];
|
|
198
202
|
var _h = (0, react_1.useState)(true), isInitialLoad = _h[0], setIsInitialLoad = _h[1];
|
|
199
203
|
var _j = (0, react_1.useState)(null), error = _j[0], setError = _j[1];
|
|
204
|
+
var _k = (0, react_1.useState)(null), currentVersion = _k[0], setCurrentVersion = _k[1];
|
|
200
205
|
/* -------------------------- Apply base theme --------------------------- */
|
|
201
206
|
(0, react_1.useEffect)(function () {
|
|
202
207
|
var root = document.documentElement;
|
|
@@ -218,7 +223,7 @@ var ThemeProvider = function (_a) {
|
|
|
218
223
|
});
|
|
219
224
|
}
|
|
220
225
|
}, [theme]);
|
|
221
|
-
/* ----------------------
|
|
226
|
+
/* ---------------------- CDN Theme Sync ----------------------- */
|
|
222
227
|
(0, react_1.useEffect)(function () {
|
|
223
228
|
if (typeof window === 'undefined' || !projectId) {
|
|
224
229
|
setIsLoading(false);
|
|
@@ -226,34 +231,38 @@ var ThemeProvider = function (_a) {
|
|
|
226
231
|
return;
|
|
227
232
|
}
|
|
228
233
|
var root = document.documentElement;
|
|
229
|
-
|
|
230
|
-
var
|
|
231
|
-
|
|
232
|
-
applyThemeData(cachedTheme, root);
|
|
233
|
-
setIsLoading(false);
|
|
234
|
-
// Still initialLoad true, but we have cached data to show
|
|
235
|
-
}
|
|
236
|
-
// Step 2: Fetch fresh data (doesn't block render if we have cache)
|
|
237
|
-
var fetchInitialData = function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
238
|
-
var docRef, snapshot, data, err_1;
|
|
234
|
+
var pollTimer;
|
|
235
|
+
var fetchFromCDN = function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
236
|
+
var cdnTheme, newVersion, err_1;
|
|
239
237
|
return __generator(this, function (_a) {
|
|
240
238
|
switch (_a.label) {
|
|
241
239
|
case 0:
|
|
242
240
|
_a.trys.push([0, 2, 3, 4]);
|
|
243
|
-
|
|
244
|
-
return [4 /*yield*/, (0, firestore_1.getDoc)(docRef)];
|
|
241
|
+
return [4 /*yield*/, loadThemeFromCDN(projectId)];
|
|
245
242
|
case 1:
|
|
246
|
-
|
|
247
|
-
if (
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
243
|
+
cdnTheme = _a.sent();
|
|
244
|
+
if (cdnTheme) {
|
|
245
|
+
newVersion = cdnTheme.version || Date.now();
|
|
246
|
+
if (!currentVersion || newVersion !== currentVersion) {
|
|
247
|
+
// Apply theme data to CSS variables and state
|
|
248
|
+
applyThemeData(cdnTheme, root);
|
|
249
|
+
setCurrentVersion(newVersion);
|
|
250
|
+
console.log('✅ Theme updated from CDN');
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
console.log('✓ Theme unchanged');
|
|
254
|
+
}
|
|
255
|
+
setError(null);
|
|
256
|
+
}
|
|
257
|
+
else {
|
|
258
|
+
console.warn('⚠️ No theme found in CDN');
|
|
259
|
+
setError('Theme not found in CDN');
|
|
251
260
|
}
|
|
252
261
|
return [3 /*break*/, 4];
|
|
253
262
|
case 2:
|
|
254
263
|
err_1 = _a.sent();
|
|
255
|
-
console.error('Error
|
|
256
|
-
setError('Failed to load theme');
|
|
264
|
+
console.error('Error loading theme from CDN:', err_1);
|
|
265
|
+
setError('Failed to load theme from CDN');
|
|
257
266
|
return [3 /*break*/, 4];
|
|
258
267
|
case 3:
|
|
259
268
|
setIsLoading(false);
|
|
@@ -263,35 +272,26 @@ var ThemeProvider = function (_a) {
|
|
|
263
272
|
}
|
|
264
273
|
});
|
|
265
274
|
}); };
|
|
266
|
-
//
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
applyThemeData(data, root);
|
|
277
|
-
});
|
|
278
|
-
return unsubscribe;
|
|
279
|
-
}, [projectId]);
|
|
280
|
-
/* Helper function to apply theme data */
|
|
275
|
+
// Initial load
|
|
276
|
+
fetchFromCDN();
|
|
277
|
+
// Poll for updates every 5 minutes
|
|
278
|
+
pollTimer = setInterval(function () {
|
|
279
|
+
fetchFromCDN();
|
|
280
|
+
}, 5 * 60 * 1000);
|
|
281
|
+
return function () {
|
|
282
|
+
clearInterval(pollTimer);
|
|
283
|
+
};
|
|
284
|
+
}, [projectId, currentVersion]);
|
|
281
285
|
var applyThemeData = function (data, root) {
|
|
282
286
|
var _a;
|
|
283
|
-
var
|
|
287
|
+
var themeConfig = (_a = data.theme_config) !== null && _a !== void 0 ? _a : {};
|
|
284
288
|
var newVariant = data.default_variation || 'standard';
|
|
285
289
|
setVariant(newVariant);
|
|
286
|
-
setThemeConfig(
|
|
290
|
+
setThemeConfig(themeConfig);
|
|
287
291
|
setProjectData(data);
|
|
288
|
-
// Apply CSS variables
|
|
289
|
-
|
|
290
|
-
var key = _a[0], value = _a[1];
|
|
291
|
-
root.style.setProperty(key.startsWith('--') ? key : "--".concat(key), String(value));
|
|
292
|
-
});
|
|
292
|
+
// Apply all theme config to CSS variables
|
|
293
|
+
applyThemeConfig(themeConfig, root);
|
|
293
294
|
};
|
|
294
|
-
/* Memoize context value to prevent unnecessary re-renders */
|
|
295
295
|
var contextValue = (0, react_1.useMemo)(function () { return ({
|
|
296
296
|
variant: variant,
|
|
297
297
|
setVariant: setVariant,
|
|
@@ -301,42 +301,67 @@ var ThemeProvider = function (_a) {
|
|
|
301
301
|
isInitialLoad: isInitialLoad,
|
|
302
302
|
error: error,
|
|
303
303
|
}); }, [variant, themeConfig, projectData, isLoading, isInitialLoad, error]);
|
|
304
|
-
/* ------------------------------- Render ------------------------------- */
|
|
305
304
|
return (react_1.default.createElement(ThemeContext.Provider, { value: contextValue },
|
|
306
305
|
react_1.default.createElement("div", { className: "theme-".concat(theme, " ").concat(funcss), style: {
|
|
307
306
|
backgroundColor: 'var(--page-bg)',
|
|
308
307
|
color: 'var(--text-color)',
|
|
309
308
|
minHeight: minHeight,
|
|
310
|
-
// Smooth transition when theme changes
|
|
311
309
|
transition: isInitialLoad ? 'none' : 'background-color 0.3s ease, color 0.3s ease',
|
|
312
310
|
} }, children)));
|
|
313
311
|
};
|
|
314
312
|
exports.default = ThemeProvider;
|
|
315
313
|
/* -------------------------------------------------------------------------- */
|
|
316
|
-
/*
|
|
314
|
+
/* HELPER HOOKS */
|
|
317
315
|
/* -------------------------------------------------------------------------- */
|
|
318
|
-
// Hook to get specific theme values
|
|
319
316
|
var useThemeValue = function (key) {
|
|
320
317
|
var themeConfig = (0, exports.useTheme)().themeConfig;
|
|
321
318
|
return themeConfig[key];
|
|
322
319
|
};
|
|
323
320
|
exports.useThemeValue = useThemeValue;
|
|
324
|
-
// Hook to get component-specific config
|
|
325
321
|
var useComponentConfig = function (componentName) {
|
|
326
322
|
var _a;
|
|
327
323
|
var projectData = (0, exports.useTheme)().projectData;
|
|
328
324
|
return ((_a = projectData === null || projectData === void 0 ? void 0 : projectData.components) === null || _a === void 0 ? void 0 : _a[componentName]) || {};
|
|
329
325
|
};
|
|
330
326
|
exports.useComponentConfig = useComponentConfig;
|
|
331
|
-
// Hook to get colors
|
|
332
327
|
var useColors = function () {
|
|
328
|
+
var _a;
|
|
333
329
|
var projectData = (0, exports.useTheme)().projectData;
|
|
334
|
-
return (projectData === null || projectData === void 0 ? void 0 : projectData.colors) || {};
|
|
330
|
+
return ((_a = projectData === null || projectData === void 0 ? void 0 : projectData.theme_config) === null || _a === void 0 ? void 0 : _a.colors) || {};
|
|
335
331
|
};
|
|
336
332
|
exports.useColors = useColors;
|
|
337
|
-
|
|
338
|
-
var
|
|
333
|
+
var useTypography = function () {
|
|
334
|
+
var _a;
|
|
339
335
|
var projectData = (0, exports.useTheme)().projectData;
|
|
340
|
-
return (projectData === null || projectData === void 0 ? void 0 : projectData.
|
|
336
|
+
return ((_a = projectData === null || projectData === void 0 ? void 0 : projectData.theme_config) === null || _a === void 0 ? void 0 : _a.typography) || {};
|
|
337
|
+
};
|
|
338
|
+
exports.useTypography = useTypography;
|
|
339
|
+
var useThemeConfig = function () {
|
|
340
|
+
var projectData = (0, exports.useTheme)().projectData;
|
|
341
|
+
return (projectData === null || projectData === void 0 ? void 0 : projectData.theme_config) || {};
|
|
342
|
+
};
|
|
343
|
+
exports.useThemeConfig = useThemeConfig;
|
|
344
|
+
var useProjectData = function () {
|
|
345
|
+
var projectData = (0, exports.useTheme)().projectData;
|
|
346
|
+
return projectData;
|
|
347
|
+
};
|
|
348
|
+
exports.useProjectData = useProjectData;
|
|
349
|
+
// Get specific color value
|
|
350
|
+
var useColor = function (colorName) {
|
|
351
|
+
var colors = (0, exports.useColors)();
|
|
352
|
+
return colors[colorName];
|
|
353
|
+
};
|
|
354
|
+
exports.useColor = useColor;
|
|
355
|
+
// Get specific typography value
|
|
356
|
+
var useTypographyValue = function (property) {
|
|
357
|
+
var typography = (0, exports.useTypography)();
|
|
358
|
+
return typography[property];
|
|
359
|
+
};
|
|
360
|
+
exports.useTypographyValue = useTypographyValue;
|
|
361
|
+
// Get component variant
|
|
362
|
+
var useComponentVariant = function (componentName, variantName) {
|
|
363
|
+
if (variantName === void 0) { variantName = 'default'; }
|
|
364
|
+
var componentConfig = (0, exports.useComponentConfig)(componentName);
|
|
365
|
+
return componentConfig[variantName] || {};
|
|
341
366
|
};
|
|
342
|
-
exports.
|
|
367
|
+
exports.useComponentVariant = useComponentVariant;
|
|
@@ -46,15 +46,15 @@ export interface UseComponentConfigReturn extends ComponentConfig {
|
|
|
46
46
|
*/
|
|
47
47
|
export declare const getComponentConfig: (projectData: ProjectData | null | undefined, componentName: string, variantName?: string) => ComponentConfig;
|
|
48
48
|
/**
|
|
49
|
-
* Merge component config with local props -
|
|
49
|
+
* Merge component config with local props - LOCAL PROPS OVERRIDE CONFIG
|
|
50
50
|
*
|
|
51
51
|
* @param config - Component configuration from getComponentConfig
|
|
52
|
-
* @param localProps - Props passed directly to the component
|
|
52
|
+
* @param localProps - Props passed directly to the component (OVERRIDES CONFIG)
|
|
53
53
|
* @returns Merged configuration with metadata
|
|
54
54
|
*/
|
|
55
55
|
export declare const mergeComponentConfig: (config: ComponentConfig, localProps?: ComponentProps) => MergedConfig;
|
|
56
56
|
/**
|
|
57
|
-
* Hook for easy component config usage
|
|
57
|
+
* Hook for easy component config usage with LOCAL PROP OVERRIDE
|
|
58
58
|
* Uses useMemo to prevent unnecessary re-computation
|
|
59
59
|
*
|
|
60
60
|
* @param componentName - Name of the component
|
|
@@ -63,12 +63,15 @@ export declare const mergeComponentConfig: (config: ComponentConfig, localProps?
|
|
|
63
63
|
*/
|
|
64
64
|
export declare const useComponentConfiguration: (componentName: string, variantName?: string) => UseComponentConfigReturn;
|
|
65
65
|
/**
|
|
66
|
-
*
|
|
67
|
-
*
|
|
66
|
+
* Hook that directly returns merged props with local override
|
|
67
|
+
* Perfect for direct use in components
|
|
68
|
+
*/
|
|
69
|
+
export declare const useComponentProps: (componentName: string, variantName?: string, localProps?: ComponentProps) => ComponentProps;
|
|
70
|
+
/**
|
|
71
|
+
* Quick utility to check if a component variant exists
|
|
68
72
|
*/
|
|
69
73
|
export declare const hasComponentVariant: (projectData: ProjectData | null | undefined, componentName: string, variantName: string) => boolean;
|
|
70
74
|
/**
|
|
71
75
|
* Get all available variants for a component
|
|
72
|
-
* Useful for variant selectors/dropdowns
|
|
73
76
|
*/
|
|
74
77
|
export declare const getAvailableVariants: (projectData: ProjectData | null | undefined, componentName: string) => string[];
|