funuicss 3.7.14 → 3.7.16

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.
@@ -10,8 +10,17 @@ var __assign = (this && this.__assign) || function () {
10
10
  };
11
11
  return __assign.apply(this, arguments);
12
12
  };
13
+ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
14
+ if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
15
+ if (ar || !(i in from)) {
16
+ if (!ar) ar = Array.prototype.slice.call(from, 0, i);
17
+ ar[i] = from[i];
18
+ }
19
+ }
20
+ return to.concat(ar || Array.prototype.slice.call(from));
21
+ };
13
22
  Object.defineProperty(exports, "__esModule", { value: true });
14
- exports.getAvailableVariants = exports.hasComponentVariant = exports.useComponentProps = exports.useComponentConfiguration = exports.mergeComponentConfig = exports.getComponentConfig = void 0;
23
+ exports.getVariableByName = exports.getAssetByName = exports.getUsedReferences = exports.needsInterpolation = exports.createAssetReference = exports.createVariableReference = exports.formatReferenceForDisplay = exports.isAssetReference = exports.isVariableReference = exports.useAssetUrl = exports.useAsset = exports.useVariable = exports.useValue = exports.getProjectAssets = exports.getProjectVariables = exports.getAvailableVariants = exports.hasComponentVariant = exports.useComponentProps = exports.useComponentConfiguration = exports.mergeComponentConfig = exports.getComponentConfig = void 0;
15
24
  var theme_1 = require("../ui/theme/theme");
16
25
  var react_1 = require("react");
17
26
  /**
@@ -24,23 +33,144 @@ var isValidVariantName = function (variantName) {
24
33
  * Filter out empty string and undefined values from component props
25
34
  */
26
35
  var filterEmptyProps = function (props) {
36
+ if (!props)
37
+ return {};
27
38
  var filtered = {};
28
39
  for (var key in props) {
29
- var value = props[key];
30
- // Only include props that are not undefined and not empty strings
31
- if (value !== undefined && value !== '') {
32
- filtered[key] = value;
40
+ if (Object.prototype.hasOwnProperty.call(props, key)) {
41
+ var value = props[key];
42
+ // Only include props that are not undefined and not empty strings
43
+ if (value !== undefined && value !== '') {
44
+ filtered[key] = value;
45
+ }
33
46
  }
34
47
  }
35
48
  return filtered;
36
49
  };
37
50
  /**
38
- * Universal component config getter
39
- *
40
- * @param projectData - The project configuration data
41
- * @param componentName - Name of the component to get config for
42
- * @param variantName - Name of the variant (defaults to 'default')
43
- * @returns Component configuration with metadata
51
+ * Extract variable name from {{variable_name}} pattern
52
+ */
53
+ var extractVariableName = function (value) {
54
+ if (typeof value !== 'string')
55
+ return null;
56
+ // Match exact {{variable}} pattern
57
+ var match = value.match(/^\{\{\s*([^}]+)\s*\}\}$/);
58
+ return match ? match[1].trim() : null;
59
+ };
60
+ /**
61
+ * Extract asset name from {{{asset_name}}} pattern
62
+ */
63
+ var extractAssetName = function (value) {
64
+ if (typeof value !== 'string')
65
+ return null;
66
+ // Match exact {{{asset}}} pattern
67
+ var match = value.match(/^\{\{\{\s*([^}]+)\s*\}\}\}$/);
68
+ return match ? match[1].trim() : null;
69
+ };
70
+ /**
71
+ * Get variable value from project variables
72
+ */
73
+ var getVariableValue = function (variableName, projectData) {
74
+ if (!(projectData === null || projectData === void 0 ? void 0 : projectData.variables) || !variableName)
75
+ return null;
76
+ var variable = projectData.variables.find(function (v) { return v.name === variableName; });
77
+ return (variable === null || variable === void 0 ? void 0 : variable.value) || null;
78
+ };
79
+ /**
80
+ * Get asset URL from project assets
81
+ */
82
+ var getAssetUrl = function (assetName, projectData) {
83
+ if (!(projectData === null || projectData === void 0 ? void 0 : projectData.assets) || !assetName)
84
+ return null;
85
+ var asset = projectData.assets.find(function (a) { return a.name === assetName; });
86
+ return (asset === null || asset === void 0 ? void 0 : asset.url) || null;
87
+ };
88
+ /**
89
+ * Get asset by name
90
+ */
91
+ var getAsset = function (assetName, projectData) {
92
+ if (!(projectData === null || projectData === void 0 ? void 0 : projectData.assets) || !assetName)
93
+ return null;
94
+ var asset = projectData.assets.find(function (a) { return a.name === assetName; });
95
+ return asset || null;
96
+ };
97
+ /**
98
+ * Interpolate values (variables and assets) in props
99
+ */
100
+ var interpolateValues = function (props, projectData) {
101
+ if (!props || Object.keys(props).length === 0) {
102
+ return props || {};
103
+ }
104
+ var result = {};
105
+ // Create lookup maps for faster access
106
+ var variableMap = {};
107
+ var assetMap = {};
108
+ if (projectData === null || projectData === void 0 ? void 0 : projectData.variables) {
109
+ projectData.variables.forEach(function (variable) {
110
+ variableMap[variable.name] = variable.value;
111
+ });
112
+ }
113
+ if (projectData === null || projectData === void 0 ? void 0 : projectData.assets) {
114
+ projectData.assets.forEach(function (asset) {
115
+ assetMap[asset.name] = asset.url;
116
+ });
117
+ }
118
+ for (var key in props) {
119
+ if (!Object.prototype.hasOwnProperty.call(props, key))
120
+ continue;
121
+ var value = props[key];
122
+ // Handle strings with interpolation
123
+ if (typeof value === 'string') {
124
+ // Check for asset reference first ({{{asset}}})
125
+ var assetName = extractAssetName(value);
126
+ if (assetName && assetMap[assetName]) {
127
+ result[key] = assetMap[assetName];
128
+ continue;
129
+ }
130
+ // Check for variable reference ({{variable}})
131
+ var variableName = extractVariableName(value);
132
+ if (variableName && variableMap[variableName]) {
133
+ result[key] = variableMap[variableName];
134
+ continue;
135
+ }
136
+ // No interpolation needed
137
+ result[key] = value;
138
+ }
139
+ // Handle arrays - process string elements only
140
+ else if (Array.isArray(value)) {
141
+ result[key] = value.map(function (item) {
142
+ if (typeof item === 'string') {
143
+ // Check for asset reference
144
+ var assetName = extractAssetName(item);
145
+ if (assetName && assetMap[assetName]) {
146
+ return assetMap[assetName];
147
+ }
148
+ // Check for variable reference
149
+ var variableName = extractVariableName(item);
150
+ if (variableName && variableMap[variableName]) {
151
+ return variableMap[variableName];
152
+ }
153
+ }
154
+ return item;
155
+ });
156
+ }
157
+ // Handle objects - create shallow copy (no deep interpolation)
158
+ else if (value && typeof value === 'object' && !Array.isArray(value)) {
159
+ result[key] = __assign({}, value);
160
+ }
161
+ // Handle arrays that are objects - create shallow copy
162
+ else if (Array.isArray(value)) {
163
+ result[key] = __spreadArray([], value, true);
164
+ }
165
+ // All other values pass through
166
+ else {
167
+ result[key] = value;
168
+ }
169
+ }
170
+ return result;
171
+ };
172
+ /**
173
+ * Universal component config getter with interpolation
44
174
  */
45
175
  var getComponentConfig = function (projectData, componentName, variantName) {
46
176
  var _a;
@@ -83,8 +213,10 @@ var getComponentConfig = function (projectData, componentName, variantName) {
83
213
  var variantData = component[targetVariant];
84
214
  // Filter out empty string and undefined props from config
85
215
  var filteredComponentProps = filterEmptyProps((variantData === null || variantData === void 0 ? void 0 : variantData.componentProps) || {});
216
+ // Apply interpolation to component props
217
+ var interpolatedProps = interpolateValues(filteredComponentProps, projectData);
86
218
  return {
87
- componentProps: filteredComponentProps,
219
+ componentProps: interpolatedProps,
88
220
  variantExists: variantExists,
89
221
  actualVariant: targetVariant,
90
222
  availableVariants: availableVariants,
@@ -94,70 +226,54 @@ var getComponentConfig = function (projectData, componentName, variantName) {
94
226
  exports.getComponentConfig = getComponentConfig;
95
227
  /**
96
228
  * Smart merge utility - LOCAL PROPS OVERRIDE CONFIG PROPS
97
- * If a prop exists in both local and config, local wins
98
229
  */
99
- var smartMergeWithLocalOverride = function (configProps, localProps) {
100
- var result = __assign({}, configProps);
101
- // Apply local props - they override config props
230
+ var smartMergeWithLocalOverride = function (configProps, localProps, projectData) {
231
+ // Start with interpolated config props
232
+ var interpolatedConfigProps = interpolateValues(configProps, projectData);
233
+ // Simple merge: local props override config props
234
+ var result = __assign({}, interpolatedConfigProps);
235
+ // Apply local props (they will be interpolated in the final pass)
102
236
  for (var key in localProps) {
103
- if (localProps[key] !== undefined) {
104
- // For objects, do smart merge but local object properties still override
105
- if (typeof localProps[key] === 'object' &&
106
- !Array.isArray(localProps[key]) &&
107
- localProps[key] !== null &&
108
- typeof configProps[key] === 'object' &&
109
- !Array.isArray(configProps[key]) &&
110
- configProps[key] !== null) {
111
- // Merge nested objects but local properties still win
112
- result[key] = __assign(__assign({}, configProps[key]), localProps[key]);
113
- }
114
- else {
115
- // Primitive values or arrays - local always wins
116
- result[key] = localProps[key];
237
+ if (Object.prototype.hasOwnProperty.call(localProps, key)) {
238
+ var value = localProps[key];
239
+ if (value !== undefined) {
240
+ result[key] = value;
117
241
  }
118
242
  }
119
243
  }
120
- return result;
244
+ // Final interpolation pass to handle any variable/asset references in local props
245
+ return interpolateValues(result, projectData);
121
246
  };
122
247
  /**
123
- * Merge component config with local props - LOCAL PROPS OVERRIDE CONFIG
124
- *
125
- * @param config - Component configuration from getComponentConfig
126
- * @param localProps - Props passed directly to the component (OVERRIDES CONFIG)
127
- * @returns Merged configuration with metadata
248
+ * Merge component config with local props
128
249
  */
129
- var mergeComponentConfig = function (config, localProps) {
250
+ var mergeComponentConfig = function (config, localProps, projectData) {
130
251
  if (localProps === void 0) { localProps = {}; }
131
252
  // Only apply config if variant exists and has actual configuration
132
253
  var hasValidConfig = config.variantExists && Object.keys(config.componentProps).length > 0;
133
254
  if (!hasValidConfig) {
255
+ // Still interpolate values in local props even if no config
256
+ var interpolatedLocalProps = interpolateValues(localProps, projectData);
134
257
  return {
135
- props: localProps,
258
+ props: interpolatedLocalProps,
136
259
  variant: config.actualVariant,
137
260
  hasConfig: false
138
261
  };
139
262
  }
140
- // LOCAL PROPS OVERRIDE CONFIG PROPS
141
263
  return {
142
- props: smartMergeWithLocalOverride(config.componentProps, localProps),
264
+ props: smartMergeWithLocalOverride(config.componentProps, localProps, projectData),
143
265
  variant: config.actualVariant,
144
266
  hasConfig: true
145
267
  };
146
268
  };
147
269
  exports.mergeComponentConfig = mergeComponentConfig;
148
270
  /**
149
- * Hook for easy component config usage with LOCAL PROP OVERRIDE
150
- * Uses useMemo to prevent unnecessary re-computation
151
- *
152
- * @param componentName - Name of the component
153
- * @param variantName - Optional variant name
154
- * @returns Configuration object with helper methods
271
+ * Hook for easy component config usage
155
272
  */
156
273
  var useComponentConfiguration = function (componentName, variantName) {
157
274
  var projectData = (0, theme_1.useTheme)().projectData;
158
- // Memoize config computation - only recompute when dependencies change
275
+ // Memoize config computation
159
276
  var config = (0, react_1.useMemo)(function () {
160
- // Check for valid variant name (not empty or whitespace only)
161
277
  if (!isValidVariantName(variantName)) {
162
278
  return {
163
279
  componentProps: {},
@@ -169,31 +285,33 @@ var useComponentConfiguration = function (componentName, variantName) {
169
285
  }
170
286
  return (0, exports.getComponentConfig)(projectData, componentName, variantName);
171
287
  }, [projectData, componentName, variantName]);
172
- // Memoize merge function - LOCAL PROPS OVERRIDE CONFIG
288
+ // Memoize merge function
173
289
  var mergeWithLocal = (0, react_1.useMemo)(function () {
174
290
  return function (localProps) {
175
291
  if (localProps === void 0) { localProps = {}; }
176
- // If no valid variant name was provided, return local props as-is
177
292
  if (!isValidVariantName(variantName)) {
293
+ var interpolatedLocalProps = interpolateValues(localProps, projectData);
178
294
  return {
179
- props: localProps,
295
+ props: interpolatedLocalProps,
180
296
  variant: '',
181
297
  hasConfig: false
182
298
  };
183
299
  }
184
- return (0, exports.mergeComponentConfig)(config, localProps);
300
+ return (0, exports.mergeComponentConfig)(config, localProps, projectData);
185
301
  };
186
- }, [config, variantName]);
187
- // Memoize getProp function (gets from config only, not merged)
302
+ }, [config, variantName, projectData]);
303
+ // Memoize getProp function
188
304
  var getProp = (0, react_1.useMemo)(function () {
189
- return function (propName, defaultValue) { var _a; return ((_a = config.componentProps[propName]) !== null && _a !== void 0 ? _a : defaultValue); };
305
+ return function (propName, defaultValue) {
306
+ var value = config.componentProps[propName];
307
+ return (value !== undefined ? value : defaultValue);
308
+ };
190
309
  }, [config.componentProps]);
191
310
  return __assign(__assign({}, config), { mergeWithLocal: mergeWithLocal, getProp: getProp, hasVariant: config.variantExists, isDefaultVariant: config.actualVariant === 'default' });
192
311
  };
193
312
  exports.useComponentConfiguration = useComponentConfiguration;
194
313
  /**
195
314
  * Hook that directly returns merged props with local override
196
- * Perfect for direct use in components
197
315
  */
198
316
  var useComponentProps = function (componentName, variantName, localProps) {
199
317
  if (variantName === void 0) { variantName = 'default'; }
@@ -201,7 +319,7 @@ var useComponentProps = function (componentName, variantName, localProps) {
201
319
  var projectData = (0, theme_1.useTheme)().projectData;
202
320
  return (0, react_1.useMemo)(function () {
203
321
  var config = (0, exports.getComponentConfig)(projectData, componentName, variantName);
204
- var merged = (0, exports.mergeComponentConfig)(config, localProps);
322
+ var merged = (0, exports.mergeComponentConfig)(config, localProps, projectData);
205
323
  return merged.props;
206
324
  }, [projectData, componentName, variantName, localProps]);
207
325
  };
@@ -222,3 +340,822 @@ var getAvailableVariants = function (projectData, componentName) {
222
340
  return Object.keys(((_a = projectData === null || projectData === void 0 ? void 0 : projectData.components) === null || _a === void 0 ? void 0 : _a[componentName]) || {});
223
341
  };
224
342
  exports.getAvailableVariants = getAvailableVariants;
343
+ /**
344
+ * Get all variables from project
345
+ */
346
+ var getProjectVariables = function (projectData) {
347
+ var _a;
348
+ return ((_a = projectData === null || projectData === void 0 ? void 0 : projectData.variables) === null || _a === void 0 ? void 0 : _a.map(function (v) { return ({
349
+ name: v.name,
350
+ value: v.value
351
+ }); })) || [];
352
+ };
353
+ exports.getProjectVariables = getProjectVariables;
354
+ /**
355
+ * Get all assets from project
356
+ */
357
+ var getProjectAssets = function (projectData) {
358
+ return (projectData === null || projectData === void 0 ? void 0 : projectData.assets) || [];
359
+ };
360
+ exports.getProjectAssets = getProjectAssets;
361
+ /**
362
+ * Hook to get interpolated value for a specific variable or asset reference
363
+ */
364
+ var useValue = function (value) {
365
+ var projectData = (0, theme_1.useTheme)().projectData;
366
+ return (0, react_1.useMemo)(function () {
367
+ if (typeof value !== 'string')
368
+ return value;
369
+ // Check for asset reference ({{{asset}}})
370
+ var assetName = extractAssetName(value);
371
+ if (assetName) {
372
+ var assetUrl = getAssetUrl(assetName, projectData);
373
+ return assetUrl || value;
374
+ }
375
+ // Check for variable reference ({{variable}})
376
+ var variableName = extractVariableName(value);
377
+ if (variableName) {
378
+ var variableValue = getVariableValue(variableName, projectData);
379
+ return variableValue || value;
380
+ }
381
+ return value;
382
+ }, [value, projectData]);
383
+ };
384
+ exports.useValue = useValue;
385
+ /**
386
+ * Hook to get a specific variable value
387
+ */
388
+ var useVariable = function (variableName) {
389
+ var projectData = (0, theme_1.useTheme)().projectData;
390
+ return (0, react_1.useMemo)(function () {
391
+ if (!variableName || !(projectData === null || projectData === void 0 ? void 0 : projectData.variables))
392
+ return null;
393
+ var variable = projectData.variables.find(function (v) { return v.name === variableName; });
394
+ return (variable === null || variable === void 0 ? void 0 : variable.value) || null;
395
+ }, [variableName, projectData]);
396
+ };
397
+ exports.useVariable = useVariable;
398
+ /**
399
+ * Hook to get a specific asset
400
+ */
401
+ var useAsset = function (assetName) {
402
+ var projectData = (0, theme_1.useTheme)().projectData;
403
+ return (0, react_1.useMemo)(function () {
404
+ if (!assetName || !(projectData === null || projectData === void 0 ? void 0 : projectData.assets))
405
+ return null;
406
+ return getAsset(assetName, projectData);
407
+ }, [assetName, projectData]);
408
+ };
409
+ exports.useAsset = useAsset;
410
+ /**
411
+ * Hook to get a specific asset URL
412
+ */
413
+ var useAssetUrl = function (assetName) {
414
+ var projectData = (0, theme_1.useTheme)().projectData;
415
+ return (0, react_1.useMemo)(function () {
416
+ if (!assetName || !(projectData === null || projectData === void 0 ? void 0 : projectData.assets))
417
+ return null;
418
+ return getAssetUrl(assetName, projectData);
419
+ }, [assetName, projectData]);
420
+ };
421
+ exports.useAssetUrl = useAssetUrl;
422
+ /**
423
+ * Check if a value is a variable reference
424
+ */
425
+ var isVariableReference = function (value) {
426
+ return typeof value === 'string' && !!extractVariableName(value);
427
+ };
428
+ exports.isVariableReference = isVariableReference;
429
+ /**
430
+ * Check if a value is an asset reference
431
+ */
432
+ var isAssetReference = function (value) {
433
+ return typeof value === 'string' && !!extractAssetName(value);
434
+ };
435
+ exports.isAssetReference = isAssetReference;
436
+ /**
437
+ * Helper to convert variable/asset references for UI display
438
+ */
439
+ var formatReferenceForDisplay = function (value) {
440
+ if (!value || typeof value !== 'string') {
441
+ return { type: 'custom', display: value || '' };
442
+ }
443
+ var variableName = extractVariableName(value);
444
+ if (variableName) {
445
+ return { type: 'variable', display: variableName };
446
+ }
447
+ var assetName = extractAssetName(value);
448
+ if (assetName) {
449
+ return { type: 'asset', display: assetName };
450
+ }
451
+ return { type: 'custom', display: value };
452
+ };
453
+ exports.formatReferenceForDisplay = formatReferenceForDisplay;
454
+ /**
455
+ * Create a variable reference string
456
+ */
457
+ var createVariableReference = function (variableName) {
458
+ return "{{".concat(variableName, "}}");
459
+ };
460
+ exports.createVariableReference = createVariableReference;
461
+ /**
462
+ * Create an asset reference string
463
+ */
464
+ var createAssetReference = function (assetName) {
465
+ return "{{{".concat(assetName, "}}}");
466
+ };
467
+ exports.createAssetReference = createAssetReference;
468
+ /**
469
+ * Check if a prop value needs interpolation
470
+ */
471
+ var needsInterpolation = function (value) {
472
+ if (typeof value !== 'string')
473
+ return false;
474
+ return (0, exports.isVariableReference)(value) || (0, exports.isAssetReference)(value);
475
+ };
476
+ exports.needsInterpolation = needsInterpolation;
477
+ /**
478
+ * Get all references (variables and assets) used in props
479
+ */
480
+ var getUsedReferences = function (props, projectData) {
481
+ var variables = [];
482
+ var assets = [];
483
+ if (!props || !projectData)
484
+ return { variables: variables, assets: assets };
485
+ var processValue = function (value) {
486
+ if (typeof value === 'string') {
487
+ var variableName = extractVariableName(value);
488
+ if (variableName) {
489
+ variables.push(variableName);
490
+ return;
491
+ }
492
+ var assetName = extractAssetName(value);
493
+ if (assetName) {
494
+ assets.push(assetName);
495
+ }
496
+ }
497
+ else if (Array.isArray(value)) {
498
+ value.forEach(processValue);
499
+ }
500
+ };
501
+ Object.values(props).forEach(processValue);
502
+ return {
503
+ variables: Array.from(new Set(variables)),
504
+ assets: Array.from(new Set(assets))
505
+ };
506
+ };
507
+ exports.getUsedReferences = getUsedReferences;
508
+ /**
509
+ * Get asset by name (exported version)
510
+ */
511
+ var getAssetByName = function (assetName, projectData) {
512
+ return getAsset(assetName, projectData);
513
+ };
514
+ exports.getAssetByName = getAssetByName;
515
+ /**
516
+ * Get variable by name (exported version)
517
+ */
518
+ var getVariableByName = function (variableName, projectData) {
519
+ if (!(projectData === null || projectData === void 0 ? void 0 : projectData.variables) || !variableName)
520
+ return null;
521
+ var variable = projectData.variables.find(function (v) { return v.name === variableName; });
522
+ return variable ? { name: variable.name, value: variable.value } : null;
523
+ };
524
+ exports.getVariableByName = getVariableByName;
525
+ exports.default = {
526
+ getComponentConfig: exports.getComponentConfig,
527
+ mergeComponentConfig: exports.mergeComponentConfig,
528
+ useComponentConfiguration: exports.useComponentConfiguration,
529
+ useComponentProps: exports.useComponentProps,
530
+ hasComponentVariant: exports.hasComponentVariant,
531
+ getAvailableVariants: exports.getAvailableVariants,
532
+ getProjectVariables: exports.getProjectVariables,
533
+ getProjectAssets: exports.getProjectAssets,
534
+ useValue: exports.useValue,
535
+ useVariable: exports.useVariable,
536
+ useAsset: exports.useAsset,
537
+ useAssetUrl: exports.useAssetUrl,
538
+ isVariableReference: exports.isVariableReference,
539
+ isAssetReference: exports.isAssetReference,
540
+ formatReferenceForDisplay: exports.formatReferenceForDisplay,
541
+ createVariableReference: exports.createVariableReference,
542
+ createAssetReference: exports.createAssetReference,
543
+ needsInterpolation: exports.needsInterpolation,
544
+ getUsedReferences: exports.getUsedReferences,
545
+ getAssetByName: exports.getAssetByName,
546
+ getVariableByName: exports.getVariableByName
547
+ };
548
+ // import { useTheme } from "../ui/theme/theme"
549
+ // import { useMemo } from "react"
550
+ // // utils/componentUtils.ts
551
+ // // Type definitions
552
+ // export interface ComponentProps {
553
+ // [key: string]: any
554
+ // }
555
+ // export interface ComponentMetadata {
556
+ // createdAt?: Date
557
+ // updatedAt?: Date
558
+ // isCustom?: boolean
559
+ // baseVariant?: string
560
+ // }
561
+ // export interface ComponentVariant {
562
+ // componentProps: ComponentProps
563
+ // metadata?: ComponentMetadata
564
+ // }
565
+ // export interface ComponentConfig {
566
+ // componentProps: ComponentProps
567
+ // variantExists: boolean
568
+ // actualVariant: string
569
+ // availableVariants: string[]
570
+ // metadata: ComponentMetadata
571
+ // }
572
+ // export interface ProjectData {
573
+ // components?: {
574
+ // [componentName: string]: {
575
+ // [variantName: string]: ComponentVariant
576
+ // }
577
+ // }
578
+ // variables?: Array<{
579
+ // name: string;
580
+ // value: string;
581
+ // category?: string;
582
+ // createdBy?: string;
583
+ // createdAt?: number;
584
+ // updatedBy?: string;
585
+ // updatedAt?: number;
586
+ // }>
587
+ // }
588
+ // export interface MergedConfig {
589
+ // props: ComponentProps
590
+ // variant: string
591
+ // hasConfig: boolean
592
+ // }
593
+ // export interface UseComponentConfigReturn extends ComponentConfig {
594
+ // mergeWithLocal: (localProps?: ComponentProps) => MergedConfig
595
+ // getProp: <T = any>(propName: string, defaultValue?: T) => T
596
+ // hasVariant: boolean
597
+ // isDefaultVariant: boolean
598
+ // }
599
+ // /**
600
+ // * Extract variable name from {{variable_name}} pattern
601
+ // */
602
+ // const extractVariableName = (value: string): string | null => {
603
+ // if (typeof value !== 'string') return null;
604
+ // const match = value.match(/\{\{\s*([^}]+)\s*\}\}/);
605
+ // return match ? match[1].trim() : null;
606
+ // }
607
+ // /**
608
+ // * Get variable value from project variables
609
+ // */
610
+ // const getVariableValue = (
611
+ // variableName: string,
612
+ // projectData: ProjectData | null | undefined
613
+ // ): string | null => {
614
+ // if (!projectData?.variables || !variableName) return null;
615
+ // const variable = projectData.variables.find(v => v.name === variableName);
616
+ // return variable?.value || null;
617
+ // }
618
+ // /**
619
+ // * SAFE Interpolate variables in props - NO RECURSION to prevent stack overflow
620
+ // */
621
+ // const interpolateVariables = (
622
+ // props: ComponentProps,
623
+ // projectData: ProjectData | null | undefined
624
+ // ): ComponentProps => {
625
+ // // Quick return if no variables or empty props
626
+ // if (!projectData?.variables || !props || Object.keys(props).length === 0) {
627
+ // return props;
628
+ // }
629
+ // const result: ComponentProps = {};
630
+ // // Create a lookup map for faster variable access
631
+ // const variableMap = projectData.variables.reduce((acc, variable) => {
632
+ // acc[variable.name] = variable.value;
633
+ // return acc;
634
+ // }, {} as Record<string, string>);
635
+ // // Process each property safely
636
+ // for (const key in props) {
637
+ // if (!Object.prototype.hasOwnProperty.call(props, key)) continue;
638
+ // const value = props[key];
639
+ // // Handle strings with variable interpolation
640
+ // if (typeof value === 'string') {
641
+ // const variableName = extractVariableName(value);
642
+ // if (variableName && variableMap[variableName]) {
643
+ // result[key] = variableMap[variableName];
644
+ // } else {
645
+ // result[key] = value;
646
+ // }
647
+ // }
648
+ // // Handle arrays - shallow process only
649
+ // else if (Array.isArray(value)) {
650
+ // result[key] = value.map(item => {
651
+ // if (typeof item === 'string') {
652
+ // const variableName = extractVariableName(item);
653
+ // return variableName && variableMap[variableName]
654
+ // ? variableMap[variableName]
655
+ // : item;
656
+ // }
657
+ // return item;
658
+ // });
659
+ // }
660
+ // // Handle objects - ONLY shallow copy, NO deep processing
661
+ // else if (value && typeof value === 'object') {
662
+ // // Create a shallow copy to avoid modifying original
663
+ // const shallowCopy = Array.isArray(value) ? [...value] : { ...value };
664
+ // result[key] = shallowCopy;
665
+ // }
666
+ // // All other values pass through
667
+ // else {
668
+ // result[key] = value;
669
+ // }
670
+ // }
671
+ // return result;
672
+ // }
673
+ // /**
674
+ // * Check if a variant name is valid (not empty or whitespace only)
675
+ // */
676
+ // const isValidVariantName = (variantName: string | undefined): boolean => {
677
+ // return !!variantName && variantName.trim() !== '';
678
+ // }
679
+ // /**
680
+ // * Filter out empty string and undefined values from component props
681
+ // */
682
+ // const filterEmptyProps = (props: ComponentProps): ComponentProps => {
683
+ // const filtered: ComponentProps = {};
684
+ // for (const key in props) {
685
+ // const value = props[key];
686
+ // // Only include props that are not undefined and not empty strings
687
+ // if (value !== undefined && value !== '') {
688
+ // filtered[key] = value;
689
+ // }
690
+ // }
691
+ // return filtered;
692
+ // }
693
+ // /**
694
+ // * Universal component config getter with variable interpolation
695
+ // */
696
+ // export const getComponentConfig = (
697
+ // projectData: ProjectData | null | undefined,
698
+ // componentName: string,
699
+ // variantName: string = 'default'
700
+ // ): ComponentConfig => {
701
+ // // Early return if no component exists
702
+ // if (!projectData?.components?.[componentName]) {
703
+ // return {
704
+ // componentProps: {},
705
+ // variantExists: false,
706
+ // actualVariant: variantName,
707
+ // availableVariants: [],
708
+ // metadata: {}
709
+ // }
710
+ // }
711
+ // const component = projectData.components[componentName];
712
+ // const availableVariants = Object.keys(component);
713
+ // // Find the best variant match with fallback chain
714
+ // let targetVariant = variantName;
715
+ // let variantExists = availableVariants.includes(variantName);
716
+ // if (!variantExists) {
717
+ // // Fallback priority: default → first available → none
718
+ // if (availableVariants.includes('default')) {
719
+ // targetVariant = 'default';
720
+ // variantExists = true;
721
+ // } else if (availableVariants.length > 0) {
722
+ // targetVariant = availableVariants[0];
723
+ // variantExists = true;
724
+ // } else {
725
+ // return {
726
+ // componentProps: {},
727
+ // variantExists: false,
728
+ // actualVariant: variantName,
729
+ // availableVariants: [],
730
+ // metadata: {}
731
+ // };
732
+ // }
733
+ // }
734
+ // const variantData = component[targetVariant];
735
+ // // Filter out empty string and undefined props from config
736
+ // let filteredComponentProps = filterEmptyProps(variantData?.componentProps || {});
737
+ // // Apply variable interpolation to component props
738
+ // filteredComponentProps = interpolateVariables(filteredComponentProps, projectData);
739
+ // return {
740
+ // componentProps: filteredComponentProps,
741
+ // variantExists,
742
+ // actualVariant: targetVariant,
743
+ // availableVariants,
744
+ // metadata: variantData?.metadata || {}
745
+ // };
746
+ // }
747
+ // /**
748
+ // * Smart merge utility - LOCAL PROPS OVERRIDE CONFIG PROPS with variable interpolation
749
+ // */
750
+ // const smartMergeWithLocalOverride = (
751
+ // configProps: ComponentProps,
752
+ // localProps: ComponentProps,
753
+ // projectData: ProjectData | null | undefined
754
+ // ): ComponentProps => {
755
+ // // Start with interpolated config props
756
+ // const interpolatedConfigProps = interpolateVariables(configProps, projectData);
757
+ // const interpolatedLocalProps = interpolateVariables(localProps, projectData);
758
+ // // Simple merge: local props override config props
759
+ // const result = { ...interpolatedConfigProps };
760
+ // for (const key in interpolatedLocalProps) {
761
+ // if (interpolatedLocalProps[key] !== undefined) {
762
+ // result[key] = interpolatedLocalProps[key];
763
+ // }
764
+ // }
765
+ // return result;
766
+ // }
767
+ // /**
768
+ // * Merge component config with local props
769
+ // */
770
+ // export const mergeComponentConfig = (
771
+ // config: ComponentConfig,
772
+ // localProps: ComponentProps = {},
773
+ // projectData: ProjectData | null | undefined
774
+ // ): MergedConfig => {
775
+ // // Only apply config if variant exists and has actual configuration
776
+ // const hasValidConfig = config.variantExists && Object.keys(config.componentProps).length > 0;
777
+ // if (!hasValidConfig) {
778
+ // // Still interpolate variables in local props even if no config
779
+ // const interpolatedLocalProps = interpolateVariables(localProps, projectData);
780
+ // return {
781
+ // props: interpolatedLocalProps,
782
+ // variant: config.actualVariant,
783
+ // hasConfig: false
784
+ // };
785
+ // }
786
+ // return {
787
+ // props: smartMergeWithLocalOverride(config.componentProps, localProps, projectData),
788
+ // variant: config.actualVariant,
789
+ // hasConfig: true
790
+ // };
791
+ // }
792
+ // /**
793
+ // * Hook for easy component config usage
794
+ // */
795
+ // export const useComponentConfiguration = (
796
+ // componentName: string,
797
+ // variantName?: string
798
+ // ): UseComponentConfigReturn => {
799
+ // const { projectData } = useTheme();
800
+ // // Memoize config computation
801
+ // const config = useMemo(() => {
802
+ // if (!isValidVariantName(variantName)) {
803
+ // return {
804
+ // componentProps: {},
805
+ // variantExists: false,
806
+ // actualVariant: '',
807
+ // availableVariants: [],
808
+ // metadata: {}
809
+ // };
810
+ // }
811
+ // return getComponentConfig(projectData, componentName, variantName!);
812
+ // }, [projectData, componentName, variantName]);
813
+ // // Memoize merge function
814
+ // const mergeWithLocal = useMemo(() => {
815
+ // return (localProps: ComponentProps = {}): MergedConfig => {
816
+ // if (!isValidVariantName(variantName)) {
817
+ // const interpolatedLocalProps = interpolateVariables(localProps, projectData);
818
+ // return {
819
+ // props: interpolatedLocalProps,
820
+ // variant: '',
821
+ // hasConfig: false
822
+ // };
823
+ // }
824
+ // return mergeComponentConfig(config, localProps, projectData);
825
+ // };
826
+ // }, [config, variantName, projectData]);
827
+ // // Memoize getProp function
828
+ // const getProp = useMemo(() => {
829
+ // return <T = any>(propName: string, defaultValue?: T): T =>
830
+ // (config.componentProps[propName] ?? defaultValue) as T;
831
+ // }, [config.componentProps]);
832
+ // return {
833
+ // ...config,
834
+ // mergeWithLocal,
835
+ // getProp,
836
+ // hasVariant: config.variantExists,
837
+ // isDefaultVariant: config.actualVariant === 'default'
838
+ // };
839
+ // }
840
+ // /**
841
+ // * Hook that directly returns merged props with local override
842
+ // */
843
+ // export const useComponentProps = (
844
+ // componentName: string,
845
+ // variantName: string = 'default',
846
+ // localProps: ComponentProps = {}
847
+ // ): ComponentProps => {
848
+ // const { projectData } = useTheme();
849
+ // return useMemo(() => {
850
+ // const config = getComponentConfig(projectData, componentName, variantName);
851
+ // const merged = mergeComponentConfig(config, localProps, projectData);
852
+ // return merged.props;
853
+ // }, [projectData, componentName, variantName, localProps]);
854
+ // }
855
+ // /**
856
+ // * Quick utility to check if a component variant exists
857
+ // */
858
+ // export const hasComponentVariant = (
859
+ // projectData: ProjectData | null | undefined,
860
+ // componentName: string,
861
+ // variantName: string
862
+ // ): boolean => {
863
+ // return !!projectData?.components?.[componentName]?.[variantName];
864
+ // }
865
+ // /**
866
+ // * Get all available variants for a component
867
+ // */
868
+ // export const getAvailableVariants = (
869
+ // projectData: ProjectData | null | undefined,
870
+ // componentName: string
871
+ // ): string[] => {
872
+ // return Object.keys(projectData?.components?.[componentName] || {});
873
+ // }
874
+ // /**
875
+ // * Get all variables from project
876
+ // */
877
+ // export const getProjectVariables = (
878
+ // projectData: ProjectData | null | undefined
879
+ // ): Array<{name: string; value: string}> => {
880
+ // return projectData?.variables?.map(v => ({
881
+ // name: v.name,
882
+ // value: v.value
883
+ // })) || [];
884
+ // }
885
+ // /**
886
+ // * Hook to get interpolated value for a specific variable or string
887
+ // */
888
+ // export const useVariable = (value: string): string => {
889
+ // const { projectData } = useTheme();
890
+ // return useMemo(() => {
891
+ // if (typeof value !== 'string') return value;
892
+ // const variableName = extractVariableName(value);
893
+ // if (!variableName) return value;
894
+ // const variableValue = getVariableValue(variableName, projectData);
895
+ // return variableValue || value;
896
+ // }, [value, projectData]);
897
+ // }
898
+ // import { useTheme } from "../ui/theme/theme"
899
+ // import { useMemo } from "react"
900
+ // // utils/componentUtils.ts
901
+ // // Type definitions
902
+ // export interface ComponentProps {
903
+ // [key: string]: any
904
+ // }
905
+ // export interface ComponentMetadata {
906
+ // createdAt?: Date
907
+ // updatedAt?: Date
908
+ // isCustom?: boolean
909
+ // baseVariant?: string
910
+ // }
911
+ // export interface ComponentVariant {
912
+ // componentProps: ComponentProps
913
+ // metadata?: ComponentMetadata
914
+ // }
915
+ // export interface ComponentConfig {
916
+ // componentProps: ComponentProps
917
+ // variantExists: boolean
918
+ // actualVariant: string
919
+ // availableVariants: string[]
920
+ // metadata: ComponentMetadata
921
+ // }
922
+ // export interface ProjectData {
923
+ // components?: {
924
+ // [componentName: string]: {
925
+ // [variantName: string]: ComponentVariant
926
+ // }
927
+ // }
928
+ // }
929
+ // export interface MergedConfig {
930
+ // props: ComponentProps
931
+ // variant: string
932
+ // hasConfig: boolean
933
+ // }
934
+ // export interface UseComponentConfigReturn extends ComponentConfig {
935
+ // mergeWithLocal: (localProps?: ComponentProps) => MergedConfig
936
+ // getProp: <T = any>(propName: string, defaultValue?: T) => T
937
+ // hasVariant: boolean
938
+ // isDefaultVariant: boolean
939
+ // }
940
+ // /**
941
+ // * Check if a variant name is valid (not empty or whitespace only)
942
+ // */
943
+ // const isValidVariantName = (variantName: string | undefined): boolean => {
944
+ // return !!variantName && variantName.trim() !== '';
945
+ // }
946
+ // /**
947
+ // * Filter out empty string and undefined values from component props
948
+ // */
949
+ // const filterEmptyProps = (props: ComponentProps): ComponentProps => {
950
+ // const filtered: ComponentProps = {};
951
+ // for (const key in props) {
952
+ // const value = props[key];
953
+ // // Only include props that are not undefined and not empty strings
954
+ // if (value !== undefined && value !== '') {
955
+ // filtered[key] = value;
956
+ // }
957
+ // }
958
+ // return filtered;
959
+ // }
960
+ // /**
961
+ // * Universal component config getter
962
+ // *
963
+ // * @param projectData - The project configuration data
964
+ // * @param componentName - Name of the component to get config for
965
+ // * @param variantName - Name of the variant (defaults to 'default')
966
+ // * @returns Component configuration with metadata
967
+ // */
968
+ // export const getComponentConfig = (
969
+ // projectData: ProjectData | null | undefined,
970
+ // componentName: string,
971
+ // variantName: string = 'default'
972
+ // ): ComponentConfig => {
973
+ // // Early return if no component exists
974
+ // if (!projectData?.components?.[componentName]) {
975
+ // return {
976
+ // componentProps: {},
977
+ // variantExists: false,
978
+ // actualVariant: variantName,
979
+ // availableVariants: [],
980
+ // metadata: {}
981
+ // }
982
+ // }
983
+ // const component = projectData.components[componentName]
984
+ // const availableVariants = Object.keys(component)
985
+ // // Find the best variant match with fallback chain
986
+ // let targetVariant = variantName
987
+ // let variantExists = availableVariants.includes(variantName)
988
+ // if (!variantExists) {
989
+ // // Fallback priority: default → first available → none
990
+ // if (availableVariants.includes('default')) {
991
+ // targetVariant = 'default'
992
+ // variantExists = true
993
+ // } else if (availableVariants.length > 0) {
994
+ // targetVariant = availableVariants[0]
995
+ // variantExists = true
996
+ // } else {
997
+ // return {
998
+ // componentProps: {},
999
+ // variantExists: false,
1000
+ // actualVariant: variantName,
1001
+ // availableVariants: [],
1002
+ // metadata: {}
1003
+ // }
1004
+ // }
1005
+ // }
1006
+ // const variantData = component[targetVariant]
1007
+ // // Filter out empty string and undefined props from config
1008
+ // const filteredComponentProps = filterEmptyProps(variantData?.componentProps || {})
1009
+ // return {
1010
+ // componentProps: filteredComponentProps,
1011
+ // variantExists,
1012
+ // actualVariant: targetVariant,
1013
+ // availableVariants,
1014
+ // metadata: variantData?.metadata || {}
1015
+ // }
1016
+ // }
1017
+ // /**
1018
+ // * Smart merge utility - LOCAL PROPS OVERRIDE CONFIG PROPS
1019
+ // * If a prop exists in both local and config, local wins
1020
+ // */
1021
+ // const smartMergeWithLocalOverride = (
1022
+ // configProps: ComponentProps,
1023
+ // localProps: ComponentProps
1024
+ // ): ComponentProps => {
1025
+ // const result = { ...configProps }
1026
+ // // Apply local props - they override config props
1027
+ // for (const key in localProps) {
1028
+ // if (localProps[key] !== undefined) {
1029
+ // // For objects, do smart merge but local object properties still override
1030
+ // if (typeof localProps[key] === 'object' &&
1031
+ // !Array.isArray(localProps[key]) &&
1032
+ // localProps[key] !== null &&
1033
+ // typeof configProps[key] === 'object' &&
1034
+ // !Array.isArray(configProps[key]) &&
1035
+ // configProps[key] !== null) {
1036
+ // // Merge nested objects but local properties still win
1037
+ // result[key] = { ...configProps[key], ...localProps[key] }
1038
+ // } else {
1039
+ // // Primitive values or arrays - local always wins
1040
+ // result[key] = localProps[key]
1041
+ // }
1042
+ // }
1043
+ // }
1044
+ // return result
1045
+ // }
1046
+ // /**
1047
+ // * Merge component config with local props - LOCAL PROPS OVERRIDE CONFIG
1048
+ // *
1049
+ // * @param config - Component configuration from getComponentConfig
1050
+ // * @param localProps - Props passed directly to the component (OVERRIDES CONFIG)
1051
+ // * @returns Merged configuration with metadata
1052
+ // */
1053
+ // export const mergeComponentConfig = (
1054
+ // config: ComponentConfig,
1055
+ // localProps: ComponentProps = {}
1056
+ // ): MergedConfig => {
1057
+ // // Only apply config if variant exists and has actual configuration
1058
+ // const hasValidConfig = config.variantExists && Object.keys(config.componentProps).length > 0
1059
+ // if (!hasValidConfig) {
1060
+ // return {
1061
+ // props: localProps,
1062
+ // variant: config.actualVariant,
1063
+ // hasConfig: false
1064
+ // }
1065
+ // }
1066
+ // // LOCAL PROPS OVERRIDE CONFIG PROPS
1067
+ // return {
1068
+ // props: smartMergeWithLocalOverride(config.componentProps, localProps),
1069
+ // variant: config.actualVariant,
1070
+ // hasConfig: true
1071
+ // }
1072
+ // }
1073
+ // /**
1074
+ // * Hook for easy component config usage with LOCAL PROP OVERRIDE
1075
+ // * Uses useMemo to prevent unnecessary re-computation
1076
+ // *
1077
+ // * @param componentName - Name of the component
1078
+ // * @param variantName - Optional variant name
1079
+ // * @returns Configuration object with helper methods
1080
+ // */
1081
+ // export const useComponentConfiguration = (
1082
+ // componentName: string,
1083
+ // variantName?: string
1084
+ // ): UseComponentConfigReturn => {
1085
+ // const { projectData } = useTheme()
1086
+ // // Memoize config computation - only recompute when dependencies change
1087
+ // const config = useMemo(() => {
1088
+ // // Check for valid variant name (not empty or whitespace only)
1089
+ // if (!isValidVariantName(variantName)) {
1090
+ // return {
1091
+ // componentProps: {},
1092
+ // variantExists: false,
1093
+ // actualVariant: '',
1094
+ // availableVariants: [],
1095
+ // metadata: {}
1096
+ // }
1097
+ // }
1098
+ // return getComponentConfig(projectData, componentName, variantName!)
1099
+ // }, [projectData, componentName, variantName])
1100
+ // // Memoize merge function - LOCAL PROPS OVERRIDE CONFIG
1101
+ // const mergeWithLocal = useMemo(() => {
1102
+ // return (localProps: ComponentProps = {}): MergedConfig => {
1103
+ // // If no valid variant name was provided, return local props as-is
1104
+ // if (!isValidVariantName(variantName)) {
1105
+ // return {
1106
+ // props: localProps,
1107
+ // variant: '',
1108
+ // hasConfig: false
1109
+ // }
1110
+ // }
1111
+ // return mergeComponentConfig(config, localProps)
1112
+ // }
1113
+ // }, [config, variantName])
1114
+ // // Memoize getProp function (gets from config only, not merged)
1115
+ // const getProp = useMemo(() => {
1116
+ // return <T = any>(propName: string, defaultValue?: T): T =>
1117
+ // (config.componentProps[propName] ?? defaultValue) as T
1118
+ // }, [config.componentProps])
1119
+ // return {
1120
+ // ...config,
1121
+ // mergeWithLocal,
1122
+ // getProp,
1123
+ // hasVariant: config.variantExists,
1124
+ // isDefaultVariant: config.actualVariant === 'default'
1125
+ // }
1126
+ // }
1127
+ // /**
1128
+ // * Hook that directly returns merged props with local override
1129
+ // * Perfect for direct use in components
1130
+ // */
1131
+ // export const useComponentProps = (
1132
+ // componentName: string,
1133
+ // variantName: string = 'default',
1134
+ // localProps: ComponentProps = {}
1135
+ // ): ComponentProps => {
1136
+ // const { projectData } = useTheme()
1137
+ // return useMemo(() => {
1138
+ // const config = getComponentConfig(projectData, componentName, variantName)
1139
+ // const merged = mergeComponentConfig(config, localProps)
1140
+ // return merged.props
1141
+ // }, [projectData, componentName, variantName, localProps])
1142
+ // }
1143
+ // /**
1144
+ // * Quick utility to check if a component variant exists
1145
+ // */
1146
+ // export const hasComponentVariant = (
1147
+ // projectData: ProjectData | null | undefined,
1148
+ // componentName: string,
1149
+ // variantName: string
1150
+ // ): boolean => {
1151
+ // return !!projectData?.components?.[componentName]?.[variantName]
1152
+ // }
1153
+ // /**
1154
+ // * Get all available variants for a component
1155
+ // */
1156
+ // export const getAvailableVariants = (
1157
+ // projectData: ProjectData | null | undefined,
1158
+ // componentName: string
1159
+ // ): string[] => {
1160
+ // return Object.keys(projectData?.components?.[componentName] || {})
1161
+ // }