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/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.useGlobalConfig = exports.useColors = exports.useComponentConfig = exports.useThemeValue = exports.useVariant = exports.useTheme = void 0;
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 CACHE & STORAGE */
100
+ /* CDN THEME LOADER */
107
101
  /* -------------------------------------------------------------------------- */
108
- var CACHE_KEY = 'funui_theme_cache';
109
- var CACHE_EXPIRY = 1000 * 60 * 60; // 1 hour
110
- var getThemeFromCache = function (projectId) {
111
- if (typeof window === 'undefined')
112
- return null;
113
- try {
114
- var cached = localStorage.getItem("".concat(CACHE_KEY, "_").concat(projectId));
115
- if (!cached)
116
- return null;
117
- var parsed = JSON.parse(cached);
118
- var isExpired = Date.now() - parsed.timestamp > CACHE_EXPIRY;
119
- if (isExpired) {
120
- localStorage.removeItem("".concat(CACHE_KEY, "_").concat(projectId));
121
- return null;
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
- return parsed.data;
124
- }
125
- catch (error) {
126
- console.error('Error reading theme cache:', error);
127
- return null;
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 saveThemeToCache = function (projectId, data) {
131
- if (typeof window === 'undefined')
163
+ var applyColorVariables = function (colors, root) {
164
+ if (!colors)
132
165
  return;
133
- try {
134
- var cache = {
135
- projectId: projectId,
136
- data: data,
137
- timestamp: Date.now(),
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
- /* SINGLETON LISTENER */
147
- /* -------------------------------------------------------------------------- */
148
- var activeListener = null;
149
- var currentProjectId = null;
150
- var listenerSubscribers = new Set();
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
- // Clean up old listener if project changed
160
- if (activeListener && currentProjectId !== projectId) {
161
- activeListener();
162
- activeListener = null;
163
- currentProjectId = null;
180
+ // Apply typography if it exists
181
+ if (themeConfig.typography) {
182
+ applyTypographyVariables(themeConfig.typography, root);
164
183
  }
165
- // Create new listener
166
- var docRef = (0, firestore_1.doc)(db, 'Projects', projectId);
167
- currentProjectId = projectId;
168
- var unsubscribe = (0, firestore_1.onSnapshot)(docRef, function (snapshot) {
169
- var data = snapshot.data();
170
- if (!data)
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
- /* ---------------------- Firebase Theme Sync ----------------------- */
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
- // Step 1: Try to load from cache immediately
230
- var cachedTheme = getThemeFromCache(projectId);
231
- if (cachedTheme) {
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
- docRef = (0, firestore_1.doc)(db, 'Projects', projectId);
244
- return [4 /*yield*/, (0, firestore_1.getDoc)(docRef)];
241
+ return [4 /*yield*/, loadThemeFromCDN(projectId)];
245
242
  case 1:
246
- snapshot = _a.sent();
247
- if (snapshot.exists()) {
248
- data = snapshot.data();
249
- applyThemeData(data, root);
250
- saveThemeToCache(projectId, data);
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 fetching initial theme:', err_1);
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
- // Only fetch if no cache
267
- if (!cachedTheme) {
268
- fetchInitialData();
269
- }
270
- else {
271
- // We have cache, mark as loaded after short delay
272
- setTimeout(function () { return setIsInitialLoad(false); }, 100);
273
- }
274
- // Step 3: Subscribe to real-time updates
275
- var unsubscribe = subscribeToTheme(projectId, function (data) {
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 config = (_a = data.theme_config) !== null && _a !== void 0 ? _a : {};
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(config);
290
+ setThemeConfig(themeConfig);
287
291
  setProjectData(data);
288
- // Apply CSS variables
289
- Object.entries(config).forEach(function (_a) {
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
- /* HELPER HOOKS */
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
- // Hook to get global config
338
- var useGlobalConfig = function () {
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.global) || {};
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.useGlobalConfig = useGlobalConfig;
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 - only applies config if variant exists
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 - only applies if variant is provided and exists
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
- * Utility function to check if a component variant exists
67
- * Useful for conditional rendering logic
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[];