@shohojdhara/atomix 0.3.0 → 0.3.2

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.
Files changed (144) hide show
  1. package/CHANGELOG.md +0 -1
  2. package/README.md +3 -5
  3. package/dist/atomix.css +753 -643
  4. package/dist/atomix.min.css +3 -5
  5. package/dist/index.d.ts +3075 -247
  6. package/dist/index.esm.js +20412 -16601
  7. package/dist/index.esm.js.map +1 -1
  8. package/dist/index.js +20379 -16605
  9. package/dist/index.js.map +1 -1
  10. package/dist/index.min.js +1 -1
  11. package/dist/index.min.js.map +1 -1
  12. package/package.json +1 -11
  13. package/src/components/AtomixGlass/AtomixGlass.test.tsx +21 -32
  14. package/src/components/AtomixGlass/AtomixGlass.tsx +55 -42
  15. package/src/components/AtomixGlass/AtomixGlassContainer.tsx +205 -57
  16. package/src/components/AtomixGlass/GlassFilter.tsx +22 -8
  17. package/src/components/AtomixGlass/__snapshots__/AtomixGlass.test.tsx.snap +221 -0
  18. package/src/components/AtomixGlass/atomixGLass.old.tsx +0 -3
  19. package/src/components/AtomixGlass/shader-utils.ts +8 -0
  20. package/src/components/AtomixGlass/stories/AtomixGlass.stories.tsx +319 -100
  21. package/src/components/AtomixGlass/stories/Examples.stories.tsx +601 -105
  22. package/src/components/AtomixGlass/stories/Modes.stories.tsx +30 -12
  23. package/src/components/AtomixGlass/stories/Playground.stories.tsx +173 -38
  24. package/src/components/AtomixGlass/stories/ShaderVariants.stories.tsx +18 -18
  25. package/src/components/AtomixGlass/stories/shared-components.tsx +27 -5
  26. package/src/components/Button/Button.tsx +62 -17
  27. package/src/components/Callout/Callout.test.tsx +8 -14
  28. package/src/components/Card/Card.tsx +103 -1
  29. package/src/components/Card/index.ts +3 -2
  30. package/src/components/Icon/index.ts +1 -1
  31. package/src/components/Modal/Modal.stories.tsx +29 -38
  32. package/src/components/Modal/Modal.tsx +4 -4
  33. package/src/components/Navigation/SideMenu/SideMenu.tsx +49 -41
  34. package/src/components/Navigation/SideMenu/SideMenuItem.tsx +63 -24
  35. package/src/components/Popover/Popover.tsx +1 -1
  36. package/src/components/VideoPlayer/VideoPlayer.stories.tsx +977 -400
  37. package/src/components/VideoPlayer/VideoPlayer.tsx +1 -6
  38. package/src/lib/composables/shared-mouse-tracker.ts +133 -0
  39. package/src/lib/composables/useAtomixGlass.ts +333 -145
  40. package/src/lib/index.ts +1 -4
  41. package/src/lib/theme/composeTheme.ts +375 -0
  42. package/src/lib/theme/config/index.ts +21 -0
  43. package/src/lib/theme/config/loader.ts +276 -0
  44. package/src/lib/theme/config/types.ts +98 -0
  45. package/src/lib/theme/config/validator.ts +326 -0
  46. package/src/lib/theme/constants.ts +183 -0
  47. package/src/lib/theme/core/ThemeCache.ts +283 -0
  48. package/src/lib/theme/core/ThemeEngine.test.ts +146 -0
  49. package/src/lib/theme/core/ThemeEngine.ts +657 -0
  50. package/src/lib/theme/core/ThemeRegistry.ts +284 -0
  51. package/src/lib/theme/core/ThemeValidator.ts +530 -0
  52. package/src/lib/theme/core/index.ts +24 -0
  53. package/src/lib/theme/createTheme.ts +521 -0
  54. package/src/lib/theme/devtools/CLI.ts +279 -0
  55. package/src/lib/theme/devtools/Inspector.tsx +594 -0
  56. package/src/lib/theme/devtools/Preview.tsx +392 -0
  57. package/src/lib/theme/devtools/index.ts +21 -0
  58. package/src/lib/theme/errors.test.ts +207 -0
  59. package/src/lib/theme/errors.ts +233 -0
  60. package/src/lib/theme/generateCSSVariables.ts +797 -0
  61. package/src/lib/theme/generators/CSSGenerator.ts +311 -0
  62. package/src/lib/theme/generators/ConfigGenerator.ts +287 -0
  63. package/src/lib/theme/generators/TypeGenerator.ts +228 -0
  64. package/src/lib/theme/generators/index.ts +21 -0
  65. package/src/lib/theme/i18n/index.ts +9 -0
  66. package/src/lib/theme/i18n/rtl.ts +325 -0
  67. package/src/lib/theme/index.ts +221 -10
  68. package/src/lib/theme/monitoring/ThemeAnalytics.ts +409 -0
  69. package/src/lib/theme/monitoring/index.ts +17 -0
  70. package/src/lib/theme/overrides/ComponentOverrides.ts +243 -0
  71. package/src/lib/theme/overrides/index.ts +15 -0
  72. package/src/lib/theme/runtime/ThemeErrorBoundary.tsx +233 -0
  73. package/src/lib/theme/runtime/ThemeManager.test.ts +176 -0
  74. package/src/lib/theme/runtime/ThemeManager.ts +442 -0
  75. package/src/lib/theme/runtime/ThemeProvider.tsx +318 -0
  76. package/src/lib/theme/runtime/index.ts +17 -0
  77. package/src/lib/theme/runtime/useTheme.ts +52 -0
  78. package/src/lib/theme/studio/ThemeStudio.tsx +312 -0
  79. package/src/lib/theme/studio/index.ts +8 -0
  80. package/src/lib/theme/themeUtils.ts +333 -0
  81. package/src/lib/theme/types.ts +340 -9
  82. package/src/lib/theme/utils.ts +23 -22
  83. package/src/lib/theme/whitelabel/WhiteLabelManager.ts +364 -0
  84. package/src/lib/theme/whitelabel/index.ts +13 -0
  85. package/src/lib/types/components.ts +148 -59
  86. package/src/styles/01-settings/_index.scss +2 -2
  87. package/src/styles/01-settings/_settings.badge.scss +3 -3
  88. package/src/styles/01-settings/_settings.border-radius.scss +1 -1
  89. package/src/styles/01-settings/_settings.callout.scss +1 -1
  90. package/src/styles/01-settings/_settings.card.scss +1 -1
  91. package/src/styles/01-settings/{_settings.maps.scss → _settings.design-tokens.scss} +163 -49
  92. package/src/styles/01-settings/_settings.input.scss +1 -1
  93. package/src/styles/01-settings/_settings.modal.scss +1 -1
  94. package/src/styles/01-settings/_settings.navbar.scss +1 -1
  95. package/src/styles/01-settings/_settings.spacing.scss +14 -13
  96. package/src/styles/01-settings/_settings.upload.scss +1 -1
  97. package/src/styles/03-generic/_generic.root.scss +131 -50
  98. package/src/styles/05-objects/_objects.block.scss +1 -1
  99. package/src/styles/06-components/_components.atomix-glass.scss +20 -22
  100. package/src/styles/06-components/_components.badge.scss +2 -2
  101. package/src/styles/06-components/_components.button.scss +1 -1
  102. package/src/styles/06-components/_components.callout.scss +1 -1
  103. package/src/styles/06-components/_components.card.scss +74 -2
  104. package/src/styles/06-components/_components.chart.scss +3 -3
  105. package/src/styles/06-components/_components.dropdown.scss +6 -0
  106. package/src/styles/06-components/_components.footer.scss +1 -1
  107. package/src/styles/06-components/_components.list-group.scss +1 -1
  108. package/src/styles/06-components/_components.list.scss +1 -1
  109. package/src/styles/06-components/_components.menu.scss +1 -1
  110. package/src/styles/06-components/_components.messages.scss +1 -1
  111. package/src/styles/06-components/_components.modal.scss +7 -2
  112. package/src/styles/06-components/_components.navbar.scss +1 -1
  113. package/src/styles/06-components/_components.popover.scss +10 -0
  114. package/src/styles/06-components/_components.product-review.scss +1 -1
  115. package/src/styles/06-components/_components.progress.scss +1 -1
  116. package/src/styles/06-components/_components.rating.scss +1 -1
  117. package/src/styles/06-components/_components.spinner.scss +1 -1
  118. package/src/styles/99-utilities/_utilities.background.scss +1 -1
  119. package/src/styles/99-utilities/_utilities.border.scss +28 -59
  120. package/src/styles/99-utilities/_utilities.gradient.scss +12 -0
  121. package/src/styles/99-utilities/_utilities.link.scss +1 -1
  122. package/src/styles/99-utilities/_utilities.position.scss +8 -15
  123. package/src/styles/99-utilities/_utilities.scss +2 -0
  124. package/src/styles/99-utilities/_utilities.spacing.scss +76 -121
  125. package/src/styles/99-utilities/_utilities.text.scss +31 -50
  126. package/dist/themes/applemix.css +0 -15411
  127. package/dist/themes/applemix.min.css +0 -72
  128. package/dist/themes/boomdevs.css +0 -15001
  129. package/dist/themes/boomdevs.min.css +0 -405
  130. package/dist/themes/esrar.css +0 -17195
  131. package/dist/themes/esrar.min.css +0 -189
  132. package/dist/themes/flashtrade.css +0 -16408
  133. package/dist/themes/flashtrade.min.css +0 -192
  134. package/dist/themes/mashroom.css +0 -29900
  135. package/dist/themes/mashroom.min.css +0 -403
  136. package/dist/themes/shaj-default.css +0 -16024
  137. package/dist/themes/shaj-default.min.css +0 -500
  138. package/src/lib/theme/ThemeManager.stories.tsx +0 -472
  139. package/src/lib/theme/ThemeManager.test.ts +0 -186
  140. package/src/lib/theme/ThemeManager.ts +0 -501
  141. package/src/lib/theme/ThemeProvider.tsx +0 -227
  142. package/src/lib/theme/useTheme.test.tsx +0 -66
  143. package/src/lib/theme/useTheme.ts +0 -80
  144. package/src/lib/theme/utils.test.ts +0 -140
package/src/lib/index.ts CHANGED
@@ -10,7 +10,4 @@ export const composables: typeof composablesImport = composablesImport;
10
10
  export const utils: typeof utilsImport = utilsImport;
11
11
  export const types: typeof typesImport = typesImport;
12
12
  export const constants: typeof constantsImport = constantsImport;
13
- export const theme: typeof themeImport = themeImport;
14
-
15
- // Also export theme utilities directly for convenience
16
- export * from './theme';
13
+ export const theme: typeof themeImport = themeImport;
@@ -0,0 +1,375 @@
1
+ /**
2
+ * Theme Composition Utilities
3
+ *
4
+ * Utilities for composing, merging, and extending themes.
5
+ */
6
+
7
+ import type { Theme, ThemeOptions } from './types';
8
+ import { createTheme } from './createTheme';
9
+
10
+ // ============================================================================
11
+ // Deep Merge Utility
12
+ // ============================================================================
13
+
14
+ /**
15
+ * Check if value is an object
16
+ */
17
+ function isObject(item: any): item is Record<string, any> {
18
+ return item && typeof item === 'object' && !Array.isArray(item) && typeof item !== 'function';
19
+ }
20
+
21
+ /**
22
+ * Deep merge multiple objects
23
+ * Later objects override earlier ones
24
+ */
25
+ export function deepMerge<T extends Record<string, any>>(...objects: Partial<T>[]): T {
26
+ if (objects.length === 0) return {} as T;
27
+ if (objects.length === 1) return objects[0] as T;
28
+
29
+ const [target, ...sources] = objects;
30
+ const result = { ...target } as T;
31
+
32
+ for (const source of sources) {
33
+ if (!source) continue;
34
+
35
+ for (const key in source) {
36
+ if (!source.hasOwnProperty(key)) continue;
37
+
38
+ const targetValue = result[key];
39
+ const sourceValue = source[key];
40
+
41
+ if (isObject(targetValue) && isObject(sourceValue)) {
42
+ // Recursively merge objects
43
+ result[key] = deepMerge(targetValue as any, sourceValue as any) as any;
44
+ } else {
45
+ // Override with source value
46
+ result[key] = sourceValue as any;
47
+ }
48
+ }
49
+ }
50
+
51
+ return result;
52
+ }
53
+
54
+ // ============================================================================
55
+ // Theme Merging
56
+ // ============================================================================
57
+
58
+ /**
59
+ * Merge multiple theme options into a single theme options object
60
+ *
61
+ * @param themes - Theme options to merge
62
+ * @returns Merged theme options
63
+ *
64
+ * @example
65
+ * ```typescript
66
+ * const baseTheme = { palette: { primary: { main: '#000' } } };
67
+ * const customTheme = { palette: { secondary: { main: '#fff' } } };
68
+ * const merged = mergeTheme(baseTheme, customTheme);
69
+ * ```
70
+ */
71
+ export function mergeTheme(...themes: ThemeOptions[]): ThemeOptions {
72
+ return deepMerge({}, ...themes);
73
+ }
74
+
75
+ /**
76
+ * Extend an existing theme with new options
77
+ *
78
+ * @param baseTheme - Base theme to extend (can be Theme or ThemeOptions)
79
+ * @param extension - Theme options to extend with
80
+ * @returns New theme with extended options
81
+ *
82
+ * @example
83
+ * ```typescript
84
+ * const base = createTheme({ palette: { primary: { main: '#000' } } });
85
+ * const extended = extendTheme(base, {
86
+ * palette: { secondary: { main: '#fff' } }
87
+ * });
88
+ * ```
89
+ */
90
+ export function extendTheme(baseTheme: Theme | ThemeOptions, extension: ThemeOptions): Theme {
91
+ // If baseTheme is a complete Theme, extract the options
92
+ const baseOptions: ThemeOptions = (baseTheme as any).__isJSTheme
93
+ ? extractThemeOptions(baseTheme as Theme)
94
+ : (baseTheme as ThemeOptions);
95
+
96
+ const merged = mergeTheme(baseOptions, extension);
97
+ return createTheme(merged);
98
+ }
99
+
100
+ /**
101
+ * Extract theme options from a complete Theme object
102
+ */
103
+ function extractThemeOptions(theme: Theme): ThemeOptions {
104
+ return {
105
+ name: theme.name,
106
+ class: theme.class,
107
+ description: theme.description,
108
+ author: theme.author,
109
+ version: theme.version,
110
+ tags: theme.tags,
111
+ supportsDarkMode: theme.supportsDarkMode,
112
+ status: theme.status,
113
+ a11y: theme.a11y,
114
+ color: theme.color,
115
+ features: theme.features,
116
+ dependencies: theme.dependencies,
117
+ palette: {
118
+ primary: theme.palette.primary,
119
+ secondary: theme.palette.secondary,
120
+ error: theme.palette.error,
121
+ warning: theme.palette.warning,
122
+ info: theme.palette.info,
123
+ success: theme.palette.success,
124
+ background: theme.palette.background,
125
+ text: theme.palette.text,
126
+ },
127
+ typography: {
128
+ fontFamily: theme.typography.fontFamily,
129
+ fontSize: theme.typography.fontSize,
130
+ fontWeightLight: theme.typography.fontWeightLight,
131
+ fontWeightRegular: theme.typography.fontWeightRegular,
132
+ fontWeightMedium: theme.typography.fontWeightMedium,
133
+ fontWeightSemiBold: theme.typography.fontWeightSemiBold,
134
+ fontWeightBold: theme.typography.fontWeightBold,
135
+ h1: theme.typography.h1,
136
+ h2: theme.typography.h2,
137
+ h3: theme.typography.h3,
138
+ h4: theme.typography.h4,
139
+ h5: theme.typography.h5,
140
+ h6: theme.typography.h6,
141
+ body1: theme.typography.body1,
142
+ body2: theme.typography.body2,
143
+ },
144
+ shadows: theme.shadows,
145
+ transitions: theme.transitions,
146
+ zIndex: theme.zIndex,
147
+ custom: theme.custom,
148
+ };
149
+ }
150
+
151
+ // ============================================================================
152
+ // Theme Variants
153
+ // ============================================================================
154
+
155
+ /**
156
+ * Create light and dark variants from a base theme
157
+ *
158
+ * @param baseTheme - Base theme options
159
+ * @returns Object with light and dark theme variants
160
+ *
161
+ * @example
162
+ * ```typescript
163
+ * const { light, dark } = createThemeVariants({
164
+ * palette: { primary: { main: '#7AFFD7' } }
165
+ * });
166
+ * ```
167
+ */
168
+ export function createThemeVariants(baseTheme: ThemeOptions): {
169
+ light: Theme;
170
+ dark: Theme;
171
+ } {
172
+ // Light theme (use base as-is or with light adjustments)
173
+ const lightTheme = createTheme({
174
+ ...baseTheme,
175
+ name: `${baseTheme.name || 'Custom'} Light`,
176
+ supportsDarkMode: false,
177
+ });
178
+
179
+ // Dark theme (invert colors)
180
+ const darkTheme = createTheme({
181
+ ...baseTheme,
182
+ name: `${baseTheme.name || 'Custom'} Dark`,
183
+ supportsDarkMode: true,
184
+ palette: {
185
+ ...baseTheme.palette,
186
+ background: {
187
+ default: '#121212',
188
+ paper: '#1E1E1E',
189
+ subtle: '#2A2A2A',
190
+ ...baseTheme.palette?.background,
191
+ },
192
+ text: {
193
+ primary: 'rgba(255, 255, 255, 0.87)',
194
+ secondary: 'rgba(255, 255, 255, 0.6)',
195
+ disabled: 'rgba(255, 255, 255, 0.38)',
196
+ ...baseTheme.palette?.text,
197
+ },
198
+ },
199
+ });
200
+
201
+ return { light: lightTheme, dark: darkTheme };
202
+ }
203
+
204
+ // ============================================================================
205
+ // Theme Overrides
206
+ // ============================================================================
207
+
208
+ /**
209
+ * Create a theme with specific overrides
210
+ *
211
+ * @param baseTheme - Base theme
212
+ * @param overrides - Specific overrides to apply
213
+ * @returns New theme with overrides
214
+ *
215
+ * @example
216
+ * ```typescript
217
+ * const theme = overrideTheme(baseTheme, {
218
+ * 'palette.primary.main': '#FF0000',
219
+ * 'typography.fontSize': 16,
220
+ * });
221
+ * ```
222
+ */
223
+ export function overrideTheme(
224
+ baseTheme: Theme | ThemeOptions,
225
+ overrides: Record<string, any>
226
+ ): Theme {
227
+ const baseOptions: ThemeOptions = (baseTheme as any).__isJSTheme
228
+ ? extractThemeOptions(baseTheme as Theme)
229
+ : (baseTheme as ThemeOptions);
230
+
231
+ // Convert dot notation overrides to nested object
232
+ const nestedOverrides: any = {};
233
+ for (const [path, value] of Object.entries(overrides)) {
234
+ const keys = path.split('.');
235
+ let current = nestedOverrides;
236
+
237
+ for (let i = 0; i < keys.length - 1; i++) {
238
+ const key = keys[i] as string;
239
+ if (typeof key !== 'string' || key === '') {
240
+ throw new Error('Invalid override key in theme override: ' + String(key));
241
+ }
242
+ if (typeof current !== 'object' || current === null) {
243
+ throw new Error('Cannot set override for path due to non-object path segment');
244
+ }
245
+ if (!(key in current) || typeof current[key] !== 'object' || current[key] === null) {
246
+ current[key] = {};
247
+ }
248
+ current = current[key] as Record<string, any>;
249
+ }
250
+
251
+ const lastKey = keys[keys.length - 1] as string;
252
+ if (typeof lastKey === 'string') {
253
+ current[lastKey] = value;
254
+ }
255
+
256
+ }
257
+
258
+ return createTheme(deepMerge(baseOptions, nestedOverrides));
259
+ }
260
+
261
+ // ============================================================================
262
+ // Theme Composition Helpers
263
+ // ============================================================================
264
+
265
+ /**
266
+ * Compose multiple themes by merging them in order
267
+ *
268
+ * @param themes - Themes to compose (later themes override earlier ones)
269
+ * @returns Composed theme
270
+ *
271
+ * @example
272
+ * ```typescript
273
+ * const theme = composeThemes(
274
+ * baseTheme,
275
+ * brandTheme,
276
+ * customizationTheme
277
+ * );
278
+ * ```
279
+ */
280
+ export function composeThemes(...themes: (Theme | ThemeOptions)[]): Theme {
281
+ const options = themes.map((theme) =>
282
+ (theme as any).__isJSTheme ? extractThemeOptions(theme as Theme) : (theme as ThemeOptions)
283
+ );
284
+
285
+ return createTheme(mergeTheme(...options));
286
+ }
287
+
288
+ /**
289
+ * Create a theme preset with common configurations
290
+ *
291
+ * @param preset - Preset name
292
+ * @param customizations - Additional customizations
293
+ * @returns Theme with preset applied
294
+ */
295
+ export function createThemePreset(
296
+ preset: 'minimal' | 'modern' | 'classic' | 'vibrant',
297
+ customizations?: ThemeOptions
298
+ ): Theme {
299
+ const presets: Record<string, ThemeOptions> = {
300
+ minimal: {
301
+ name: 'Minimal',
302
+ palette: {
303
+ primary: { main: '#000000' },
304
+ secondary: { main: '#FFFFFF' },
305
+ background: {
306
+ default: '#FFFFFF',
307
+ paper: '#F5F5F5',
308
+ subtle: '#FAFAFA',
309
+ },
310
+ },
311
+ typography: {
312
+ fontFamily: '"Helvetica Neue", Helvetica, Arial, sans-serif',
313
+ },
314
+ },
315
+ modern: {
316
+ name: 'Modern',
317
+ palette: {
318
+ primary: { main: '#7AFFD7' },
319
+ secondary: { main: '#FF5733' },
320
+ background: {
321
+ default: '#FAFAFA',
322
+ paper: '#FFFFFF',
323
+ subtle: '#F5F5F5',
324
+ },
325
+ },
326
+ typography: {
327
+ fontFamily: '"Inter", "Roboto", sans-serif',
328
+ },
329
+ },
330
+ classic: {
331
+ name: 'Classic',
332
+ palette: {
333
+ primary: { main: '#1976D2' },
334
+ secondary: { main: '#DC004E' },
335
+ background: {
336
+ default: '#FFFFFF',
337
+ paper: '#F5F5F5',
338
+ subtle: '#EEEEEE',
339
+ },
340
+ },
341
+ typography: {
342
+ fontFamily: '"Roboto", "Helvetica", "Arial", sans-serif',
343
+ },
344
+ },
345
+ vibrant: {
346
+ name: 'Vibrant',
347
+ palette: {
348
+ primary: { main: '#FF6B6B' },
349
+ secondary: { main: '#4ECDC4' },
350
+ background: {
351
+ default: '#FFF8F0',
352
+ paper: '#FFFFFF',
353
+ subtle: '#FFF0E0',
354
+ },
355
+ },
356
+ typography: {
357
+ fontFamily: '"Poppins", "Roboto", sans-serif',
358
+ },
359
+ },
360
+ };
361
+
362
+ const basePreset: ThemeOptions = (presets[preset] ?? presets['modern']) as ThemeOptions;
363
+ const customThemeOptions: ThemeOptions = customizations ?? ({} as ThemeOptions);
364
+ return createTheme(mergeTheme(basePreset, customThemeOptions));
365
+ }
366
+
367
+ export default {
368
+ deepMerge,
369
+ mergeTheme,
370
+ extendTheme,
371
+ createThemeVariants,
372
+ overrideTheme,
373
+ composeThemes,
374
+ createThemePreset,
375
+ };
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Theme Configuration Module
3
+ *
4
+ * Exports for theme configuration loading and validation
5
+ */
6
+
7
+ export { loadThemeConfig, clearConfigCache, getCachedConfig, reloadThemeConfig } from './loader';
8
+ export { validateConfig } from './validator';
9
+ export type {
10
+ ConfigLoaderOptions,
11
+ LoadedThemeConfig,
12
+ ConfigValidationResult,
13
+ ThemeMetadata,
14
+ ThemeConfig,
15
+ ThemeDefinition,
16
+ CSSThemeDefinition,
17
+ JSThemeDefinition,
18
+ BuildConfig,
19
+ RuntimeConfig,
20
+ IntegrationConfig,
21
+ } from './types';
@@ -0,0 +1,276 @@
1
+ /**
2
+ * Theme Configuration Loader
3
+ *
4
+ * Loads and validates the theme configuration from theme.config.ts
5
+ */
6
+
7
+ import type {
8
+ ConfigLoaderOptions,
9
+ LoadedThemeConfig,
10
+ ConfigValidationResult,
11
+ } from './types';
12
+ import { validateConfig } from './validator';
13
+ import { ThemeError, ThemeErrorCode, getLogger } from '../errors';
14
+ import {
15
+ DEFAULT_CONFIG_PATH,
16
+ DEFAULT_CONFIG_RELATIVE_PATH,
17
+ DEFAULT_BASE_PATH,
18
+ DEFAULT_STORAGE_KEY,
19
+ DEFAULT_DATA_ATTRIBUTE,
20
+ DEFAULT_INTEGRATION_CLASS_NAMES,
21
+ DEFAULT_INTEGRATION_CSS_VARIABLES,
22
+ DEFAULT_BUILD_OUTPUT_DIR,
23
+ DEFAULT_SASS_CONFIG,
24
+ ENV_DEFAULTS,
25
+ } from '../constants';
26
+
27
+ /**
28
+ * Cache for loaded configuration
29
+ */
30
+ let cachedConfig: LoadedThemeConfig | null = null;
31
+
32
+ /**
33
+ * Logger instance
34
+ */
35
+ const logger = getLogger();
36
+
37
+ /**
38
+ * Load theme configuration from theme.config.ts
39
+ *
40
+ * @param options - Loader options
41
+ * @returns Loaded and validated theme configuration
42
+ *
43
+ * @example
44
+ * ```typescript
45
+ * import { loadThemeConfig } from '@shohojdhara/atomix/theme/config';
46
+ * const config = loadThemeConfig();
47
+ * ```
48
+ */
49
+ export function loadThemeConfig(
50
+ options: ConfigLoaderOptions = {}
51
+ ): LoadedThemeConfig {
52
+ const {
53
+ configPath = DEFAULT_CONFIG_PATH,
54
+ validate = true,
55
+ env = typeof process !== 'undefined' && process.env ? (process.env.NODE_ENV === 'production' ? 'production' : 'development') : 'development',
56
+ } = options;
57
+
58
+ // Return cached config if available
59
+ if (cachedConfig) {
60
+ return cachedConfig;
61
+ }
62
+
63
+ // Try to load config dynamically
64
+ let config: LoadedThemeConfig;
65
+
66
+ try {
67
+ // In browser/Vite environment, we can't load theme.config.ts dynamically
68
+ // This is expected and we'll use the fallback config
69
+ if (typeof window !== 'undefined') {
70
+ throw new Error('Theme config loading not supported in browser environment');
71
+ }
72
+
73
+ // In Node.js/bundler environment, try require (CommonJS) first
74
+ // Check if we're in a browser environment first
75
+ if (typeof window !== 'undefined' || typeof require === 'undefined') {
76
+ throw new Error('Theme config loading not supported in browser environment');
77
+ }
78
+
79
+ // Type for theme config module
80
+ interface ThemeConfigModule {
81
+ default?: LoadedThemeConfig;
82
+ [key: string]: unknown;
83
+ }
84
+
85
+ let themeConfigModule: ThemeConfigModule;
86
+
87
+ // Try require (Node.js/CommonJS) - works in Node.js environments
88
+ try {
89
+ // Try relative path first (for build tools)
90
+ try {
91
+ themeConfigModule = require(DEFAULT_CONFIG_RELATIVE_PATH) as ThemeConfigModule;
92
+ } catch {
93
+ // If relative path fails, try to resolve from process.cwd()
94
+ const path = require('path') as typeof import('path');
95
+ const fs = require('fs') as typeof import('fs');
96
+ const configFilePath = path.resolve(process.cwd(), configPath);
97
+
98
+ // Check if file exists
99
+ if (fs.existsSync(configFilePath)) {
100
+ // Delete from cache to force reload
101
+ const resolvedPath = require.resolve(configFilePath);
102
+ if (require.cache && require.cache[resolvedPath]) {
103
+ delete require.cache[resolvedPath];
104
+ }
105
+ themeConfigModule = require(configFilePath) as ThemeConfigModule;
106
+ } else {
107
+ throw new Error(`Config file not found: ${configFilePath}`);
108
+ }
109
+ }
110
+ } catch (requireError) {
111
+ // If require fails, throw to fall through to error handling
112
+ const errorMessage = requireError instanceof Error
113
+ ? requireError.message
114
+ : String(requireError);
115
+ throw new ThemeError(
116
+ `Cannot load theme config: ${errorMessage}`,
117
+ ThemeErrorCode.CONFIG_LOAD_FAILED,
118
+ { configPath, error: errorMessage }
119
+ );
120
+ }
121
+
122
+ const rawConfig = (themeConfigModule.default || themeConfigModule) as LoadedThemeConfig;
123
+
124
+ // Apply environment-specific overrides
125
+ const processedConfig = applyEnvOverrides(rawConfig, env);
126
+
127
+ // Validate if requested
128
+ let validationResult: ConfigValidationResult | null = null;
129
+ if (validate) {
130
+ validationResult = validateConfig(processedConfig);
131
+ }
132
+
133
+ config = {
134
+ ...processedConfig,
135
+ validated: validate,
136
+ errors: validationResult?.errors,
137
+ warnings: validationResult?.warnings,
138
+ };
139
+ } catch (error) {
140
+ // Fallback: return default config structure
141
+ const errorMessage = error instanceof Error ? error.message : String(error);
142
+ logger.warn(`Failed to load theme config from ${configPath}`, {
143
+ configPath,
144
+ error: errorMessage,
145
+ });
146
+
147
+ config = {
148
+ themes: {},
149
+ build: {
150
+ output: {
151
+ directory: DEFAULT_BUILD_OUTPUT_DIR,
152
+ formats: {
153
+ expanded: '.css',
154
+ compressed: '.min.css',
155
+ },
156
+ },
157
+ sass: {
158
+ ...DEFAULT_SASS_CONFIG,
159
+ loadPaths: [...DEFAULT_SASS_CONFIG.loadPaths],
160
+ },
161
+ },
162
+ runtime: {
163
+ basePath: DEFAULT_BASE_PATH,
164
+ cdnPath: null,
165
+ preload: [],
166
+ lazy: true,
167
+ defaultTheme: '', // No default - use built-in styles (empty string instead of undefined)
168
+ storageKey: DEFAULT_STORAGE_KEY,
169
+ dataAttribute: DEFAULT_DATA_ATTRIBUTE,
170
+ enablePersistence: true,
171
+ useMinified: env === 'production',
172
+ },
173
+ integration: {
174
+ cssVariables: DEFAULT_INTEGRATION_CSS_VARIABLES,
175
+ classNames: DEFAULT_INTEGRATION_CLASS_NAMES,
176
+ },
177
+ dependencies: {},
178
+ validated: false,
179
+ errors: [`Failed to load config: ${error instanceof Error ? error.message : String(error)}`],
180
+ warnings: [],
181
+ };
182
+ }
183
+
184
+ // Cache the loaded config
185
+ cachedConfig = config;
186
+
187
+ return config;
188
+ }
189
+
190
+ /**
191
+ * Apply environment-specific overrides to configuration
192
+ *
193
+ * @param config - Base configuration
194
+ * @param env - Environment name
195
+ * @returns Configuration with environment overrides applied
196
+ */
197
+ function applyEnvOverrides(
198
+ config: LoadedThemeConfig,
199
+ env: 'development' | 'production' | 'test'
200
+ ): LoadedThemeConfig {
201
+ const overridden = { ...config };
202
+
203
+ // Production overrides
204
+ if (env === 'production') {
205
+ if (overridden.runtime) {
206
+ overridden.runtime = {
207
+ ...overridden.runtime,
208
+ useMinified: true,
209
+ lazy: true,
210
+ };
211
+ }
212
+ }
213
+
214
+ // Development overrides
215
+ if (env === 'development') {
216
+ if (overridden.runtime) {
217
+ overridden.runtime = {
218
+ ...overridden.runtime,
219
+ useMinified: false,
220
+ lazy: false,
221
+ };
222
+ }
223
+ if (overridden.build) {
224
+ overridden.build = {
225
+ ...overridden.build,
226
+ sass: {
227
+ ...overridden.build.sass,
228
+ sourceMap: true,
229
+ },
230
+ };
231
+ }
232
+ }
233
+
234
+ // Test overrides
235
+ if (env === 'test') {
236
+ if (overridden.runtime) {
237
+ overridden.runtime = {
238
+ ...overridden.runtime,
239
+ enablePersistence: false,
240
+ preload: [],
241
+ };
242
+ }
243
+ }
244
+
245
+ return overridden;
246
+ }
247
+
248
+ /**
249
+ * Clear the cached configuration
250
+ * Useful for testing or hot reloading
251
+ */
252
+ export function clearConfigCache(): void {
253
+ cachedConfig = null;
254
+ }
255
+
256
+ /**
257
+ * Get cached configuration without loading
258
+ *
259
+ * @returns Cached configuration or null
260
+ */
261
+ export function getCachedConfig(): LoadedThemeConfig | null {
262
+ return cachedConfig;
263
+ }
264
+
265
+ /**
266
+ * Reload configuration (clears cache and loads fresh)
267
+ *
268
+ * @param options - Loader options
269
+ * @returns Freshly loaded configuration
270
+ */
271
+ export function reloadThemeConfig(
272
+ options: ConfigLoaderOptions = {}
273
+ ): LoadedThemeConfig {
274
+ clearConfigCache();
275
+ return loadThemeConfig(options);
276
+ }