@shohojdhara/atomix 0.3.15 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (245) hide show
  1. package/build-tools/index.d.ts +31 -30
  2. package/build-tools/package.json +4 -21
  3. package/dist/atomix.css +20924 -2611
  4. package/dist/atomix.css.map +1 -1
  5. package/dist/atomix.min.css +76 -2
  6. package/dist/atomix.min.css.map +1 -1
  7. package/dist/build-tools/index.d.ts +31 -30
  8. package/dist/build-tools/package.json +4 -21
  9. package/dist/charts.js.map +1 -1
  10. package/dist/core.js.map +1 -1
  11. package/dist/forms.js.map +1 -1
  12. package/dist/heavy.js.map +1 -1
  13. package/dist/index.d.ts +144 -18
  14. package/dist/index.esm.js +110 -55
  15. package/dist/index.esm.js.map +1 -1
  16. package/dist/index.js +110 -55
  17. package/dist/index.js.map +1 -1
  18. package/dist/index.min.js +1 -1
  19. package/dist/index.min.js.map +1 -1
  20. package/dist/layout.js.map +1 -1
  21. package/dist/theme.d.ts +9 -9
  22. package/dist/theme.js.map +1 -1
  23. package/package.json +1 -1
  24. package/src/components/Accordion/Accordion.stories.tsx +32 -23
  25. package/src/components/Accordion/Accordion.test.tsx +70 -50
  26. package/src/components/Accordion/Accordion.tsx +99 -94
  27. package/src/components/AtomixGlass/AtomixGlass.test.tsx +1 -1
  28. package/src/components/AtomixGlass/GlassFilter.tsx +9 -16
  29. package/src/components/AtomixGlass/glass-utils.ts +4 -3
  30. package/src/components/AtomixGlass/shader-utils.ts +128 -52
  31. package/src/components/AtomixGlass/stories/Playground.stories.tsx +1 -1
  32. package/src/components/AtomixGlass/stories/Shaders.stories.tsx +1 -1
  33. package/src/components/Avatar/Avatar.stories.tsx +45 -62
  34. package/src/components/Avatar/Avatar.tsx +58 -56
  35. package/src/components/Badge/Badge.stories.tsx +20 -9
  36. package/src/components/Badge/Badge.test.tsx +41 -41
  37. package/src/components/Badge/Badge.tsx +64 -62
  38. package/src/components/Block/Block.stories.tsx +14 -4
  39. package/src/components/Breadcrumb/Breadcrumb.stories.tsx +9 -8
  40. package/src/components/Breadcrumb/Breadcrumb.tsx +62 -60
  41. package/src/components/Button/Button.stories.tsx +13 -22
  42. package/src/components/Button/Button.test.tsx +97 -81
  43. package/src/components/Button/Button.tsx +46 -14
  44. package/src/components/Button/ButtonGroup.stories.tsx +37 -32
  45. package/src/components/Button/ButtonGroup.tsx +4 -15
  46. package/src/components/Callout/Callout.stories.tsx +109 -16
  47. package/src/components/Card/Card.stories.tsx +67 -36
  48. package/src/components/Card/Card.tsx +30 -14
  49. package/src/components/Chart/AreaChart.tsx +1 -1
  50. package/src/components/Chart/CandlestickChart.tsx +23 -16
  51. package/src/components/Chart/Chart.stories.tsx +4 -9
  52. package/src/components/Chart/Chart.tsx +40 -44
  53. package/src/components/Chart/ChartRenderer.tsx +39 -12
  54. package/src/components/Chart/ChartToolbar.tsx +21 -5
  55. package/src/components/Chart/DonutChart.tsx +1 -1
  56. package/src/components/Chart/FunnelChart.tsx +4 -1
  57. package/src/components/Chart/GaugeChart.tsx +3 -1
  58. package/src/components/Chart/HeatmapChart.tsx +50 -37
  59. package/src/components/Chart/LineChart.tsx +3 -2
  60. package/src/components/Chart/MultiAxisChart.tsx +24 -16
  61. package/src/components/Chart/RadarChart.tsx +19 -17
  62. package/src/components/Chart/ScatterChart.tsx +29 -21
  63. package/src/components/ColorModeToggle/ColorModeToggle.stories.tsx +6 -2
  64. package/src/components/ColorModeToggle/ColorModeToggle.tsx +15 -3
  65. package/src/components/Countdown/Countdown.stories.tsx +7 -7
  66. package/src/components/DataTable/DataTable.stories.tsx +43 -38
  67. package/src/components/DataTable/DataTable.test.tsx +26 -148
  68. package/src/components/DataTable/DataTable.tsx +485 -456
  69. package/src/components/DatePicker/DatePicker.stories.tsx +32 -47
  70. package/src/components/DatePicker/DatePicker.tsx +31 -26
  71. package/src/components/Dropdown/Dropdown.stories.tsx +2 -5
  72. package/src/components/Dropdown/Dropdown.tsx +313 -299
  73. package/src/components/EdgePanel/EdgePanel.stories.tsx +6 -19
  74. package/src/components/EdgePanel/EdgePanel.tsx +1 -3
  75. package/src/components/Footer/Footer.stories.tsx +21 -16
  76. package/src/components/Footer/Footer.tsx +130 -128
  77. package/src/components/Footer/FooterLink.tsx +2 -2
  78. package/src/components/Form/Checkbox.test.tsx +49 -49
  79. package/src/components/Form/Checkbox.tsx +108 -100
  80. package/src/components/Form/Form.stories.tsx +2 -10
  81. package/src/components/Form/Input.stories.tsx +22 -39
  82. package/src/components/Form/Input.test.tsx +38 -44
  83. package/src/components/Form/Radio.stories.tsx +6 -12
  84. package/src/components/Form/Radio.tsx +68 -66
  85. package/src/components/Form/Select.tsx +184 -182
  86. package/src/components/Form/Textarea.test.tsx +27 -32
  87. package/src/components/Hero/Hero.stories.tsx +56 -23
  88. package/src/components/Hero/Hero.tsx +201 -55
  89. package/src/components/Icon/index.ts +7 -1
  90. package/src/components/List/List.tsx +19 -23
  91. package/src/components/Modal/Modal.stories.tsx +2 -1
  92. package/src/components/Modal/Modal.tsx +130 -127
  93. package/src/components/Navigation/Menu/MegaMenu.tsx +70 -70
  94. package/src/components/Navigation/Nav/NavDropdown.tsx +1 -5
  95. package/src/components/Navigation/SideMenu/SideMenu.stories.tsx +128 -28
  96. package/src/components/Navigation/SideMenu/SideMenu.tsx +5 -7
  97. package/src/components/Navigation/SideMenu/SideMenuItem.tsx +4 -5
  98. package/src/components/Pagination/Pagination.stories.tsx +7 -4
  99. package/src/components/Pagination/Pagination.tsx +199 -202
  100. package/src/components/PhotoViewer/PhotoViewer.tsx +4 -1
  101. package/src/components/Popover/Popover.stories.tsx +99 -192
  102. package/src/components/Popover/Popover.tsx +41 -37
  103. package/src/components/Progress/Progress.stories.tsx +35 -44
  104. package/src/components/River/River.stories.tsx +2 -1
  105. package/src/components/SectionIntro/SectionIntro.stories.tsx +71 -71
  106. package/src/components/Slider/Slider.stories.tsx +12 -4
  107. package/src/components/Spinner/Spinner.stories.tsx +3 -1
  108. package/src/components/Spinner/Spinner.test.tsx +23 -23
  109. package/src/components/Spinner/Spinner.tsx +43 -46
  110. package/src/components/Steps/Steps.stories.tsx +8 -6
  111. package/src/components/Tabs/Tabs.stories.tsx +12 -9
  112. package/src/components/Tabs/Tabs.tsx +74 -72
  113. package/src/components/Toggle/Toggle.stories.tsx +27 -13
  114. package/src/components/Toggle/Toggle.test.tsx +65 -70
  115. package/src/components/Toggle/Toggle.tsx +4 -1
  116. package/src/components/Tooltip/Tooltip.stories.tsx +24 -20
  117. package/src/components/Tooltip/Tooltip.tsx +104 -106
  118. package/src/components/Upload/Upload.stories.tsx +129 -127
  119. package/src/components/Upload/Upload.tsx +287 -283
  120. package/src/components/VideoPlayer/VideoPlayer.tsx +6 -1
  121. package/src/components/index.ts +13 -2
  122. package/src/layouts/Grid/Grid.stories.tsx +9 -3
  123. package/src/layouts/MasonryGrid/MasonryGrid.tsx +5 -1
  124. package/src/lib/__tests__/theme-tools.test.ts +32 -6
  125. package/src/lib/composables/shared-mouse-tracker.ts +13 -14
  126. package/src/lib/composables/useAtomixGlass.ts +106 -49
  127. package/src/lib/composables/useChartExport.ts +1 -1
  128. package/src/lib/composables/useDataTable.ts +29 -17
  129. package/src/lib/composables/useHero.ts +58 -14
  130. package/src/lib/composables/useHeroBackgroundSlider.ts +2 -9
  131. package/src/lib/composables/useInput.ts +10 -8
  132. package/src/lib/composables/useSideMenu.ts +6 -5
  133. package/src/lib/composables/useTooltip.ts +1 -2
  134. package/src/lib/composables/useVideoPlayer.ts +44 -35
  135. package/src/lib/config/index.ts +154 -154
  136. package/src/lib/constants/cssVariables.ts +29 -29
  137. package/src/lib/hooks/__tests__/useComponentCustomization.test.ts +2 -6
  138. package/src/lib/hooks/index.ts +1 -1
  139. package/src/lib/hooks/useComponentCustomization.ts +11 -17
  140. package/src/lib/hooks/usePerformanceMonitor.ts +6 -7
  141. package/src/lib/patterns/__tests__/slots.test.ts +1 -1
  142. package/src/lib/patterns/index.ts +1 -1
  143. package/src/lib/patterns/slots.tsx +8 -13
  144. package/src/lib/storybook/InteractiveDemo.tsx +13 -18
  145. package/src/lib/storybook/PreviewContainer.tsx +1 -1
  146. package/src/lib/storybook/VariantsGrid.tsx +3 -7
  147. package/src/lib/storybook/index.ts +1 -1
  148. package/src/lib/theme/adapters/cssVariableMapper.ts +47 -74
  149. package/src/lib/theme/adapters/index.ts +3 -9
  150. package/src/lib/theme/adapters/themeAdapter.ts +41 -26
  151. package/src/lib/theme/config/index.ts +1 -1
  152. package/src/lib/theme/config/types.ts +2 -2
  153. package/src/lib/theme/config/validator.ts +10 -5
  154. package/src/lib/theme/constants/constants.ts +2 -2
  155. package/src/lib/theme/constants/index.ts +1 -2
  156. package/src/lib/theme/core/__tests__/createTheme.test.ts +20 -22
  157. package/src/lib/theme/core/composeTheme.ts +32 -26
  158. package/src/lib/theme/core/createTheme.ts +1 -1
  159. package/src/lib/theme/core/createThemeObject.ts +308 -301
  160. package/src/lib/theme/core/index.ts +3 -3
  161. package/src/lib/theme/devtools/CLI.ts +106 -104
  162. package/src/lib/theme/devtools/Comparator.tsx +50 -32
  163. package/src/lib/theme/devtools/DesignTokensCustomizer.stories.tsx +50 -48
  164. package/src/lib/theme/devtools/DesignTokensCustomizer.tsx +257 -63
  165. package/src/lib/theme/devtools/Inspector.tsx +75 -60
  166. package/src/lib/theme/devtools/LiveEditor.tsx +97 -76
  167. package/src/lib/theme/devtools/Preview.tsx +150 -106
  168. package/src/lib/theme/devtools/ThemeValidator.ts +29 -21
  169. package/src/lib/theme/devtools/index.ts +3 -9
  170. package/src/lib/theme/devtools/useHistory.ts +23 -21
  171. package/src/lib/theme/errors/errors.ts +12 -11
  172. package/src/lib/theme/errors/index.ts +2 -7
  173. package/src/lib/theme/generators/generateCSS.ts +9 -13
  174. package/src/lib/theme/generators/generateCSSNested.ts +1 -6
  175. package/src/lib/theme/generators/generateCSSVariables.ts +673 -630
  176. package/src/lib/theme/generators/index.ts +1 -4
  177. package/src/lib/theme/i18n/index.ts +1 -1
  178. package/src/lib/theme/i18n/rtl.ts +13 -13
  179. package/src/lib/theme/index.ts +7 -16
  180. package/src/lib/theme/runtime/ThemeApplicator.ts +4 -4
  181. package/src/lib/theme/runtime/ThemeContext.tsx +1 -1
  182. package/src/lib/theme/runtime/ThemeErrorBoundary.tsx +19 -23
  183. package/src/lib/theme/runtime/ThemeProvider.tsx +230 -239
  184. package/src/lib/theme/runtime/__tests__/ThemeProvider.integration.test.tsx +1 -1
  185. package/src/lib/theme/runtime/__tests__/ThemeProvider.test.tsx +24 -29
  186. package/src/lib/theme/runtime/index.ts +2 -5
  187. package/src/lib/theme/runtime/useTheme.ts +18 -18
  188. package/src/lib/theme/runtime/useThemeTokens.ts +22 -22
  189. package/src/lib/theme/test/testTheme.ts +15 -16
  190. package/src/lib/theme/tokens/index.ts +2 -7
  191. package/src/lib/theme/tokens/tokens.ts +25 -24
  192. package/src/lib/theme/types.ts +428 -411
  193. package/src/lib/theme/utils/__tests__/themeValidation.test.ts +3 -3
  194. package/src/lib/theme/utils/componentTheming.ts +18 -18
  195. package/src/lib/theme/utils/domUtils.ts +277 -289
  196. package/src/lib/theme/utils/index.ts +1 -2
  197. package/src/lib/theme/utils/injectCSS.ts +10 -14
  198. package/src/lib/theme/utils/naming.ts +20 -16
  199. package/src/lib/theme/utils/themeHelpers.ts +10 -12
  200. package/src/lib/theme/utils/themeUtils.ts +85 -86
  201. package/src/lib/theme/utils/themeValidation.ts +82 -33
  202. package/src/lib/theme-tools.ts +8 -6
  203. package/src/lib/types/components.ts +172 -71
  204. package/src/lib/types/partProps.ts +1 -1
  205. package/src/lib/utils/__tests__/csv.test.ts +1 -1
  206. package/src/lib/utils/componentUtils.ts +8 -12
  207. package/src/lib/utils/csv.ts +3 -1
  208. package/src/lib/utils/dataTableExport.ts +1 -5
  209. package/src/lib/utils/fontPreloader.ts +10 -19
  210. package/src/lib/utils/icons.ts +4 -1
  211. package/src/lib/utils/index.ts +2 -6
  212. package/src/lib/utils/memoryMonitor.ts +10 -8
  213. package/src/lib/utils/themeNaming.ts +2 -2
  214. package/src/styles/01-settings/_index.scss +0 -1
  215. package/src/styles/01-settings/_settings.colors.scss +8 -8
  216. package/src/styles/01-settings/_settings.design-tokens.scss +61 -50
  217. package/src/styles/01-settings/_settings.navbar.scss +1 -1
  218. package/src/styles/01-settings/_settings.spacing.scss +3 -4
  219. package/src/styles/01-settings/_settings.tooltip.scss +1 -1
  220. package/src/styles/01-settings/_settings.typography.scss +1 -1
  221. package/src/styles/02-tools/_tools.button.scss +51 -21
  222. package/src/styles/02-tools/_tools.utility-api.scss +30 -18
  223. package/src/styles/03-generic/_generic.root.scss +4 -3
  224. package/src/styles/06-components/_components.atomix-glass.scss +13 -9
  225. package/src/styles/06-components/_components.button.scss +16 -4
  226. package/src/styles/06-components/_components.callout.scss +27 -21
  227. package/src/styles/06-components/_components.card.scss +5 -14
  228. package/src/styles/06-components/_components.chart.scss +22 -19
  229. package/src/styles/06-components/_components.checkbox.scss +3 -1
  230. package/src/styles/06-components/_components.color-mode-toggle.scss +3 -1
  231. package/src/styles/06-components/_components.edge-panel.scss +9 -2
  232. package/src/styles/06-components/_components.footer.scss +1 -1
  233. package/src/styles/06-components/_components.side-menu.scss +5 -5
  234. package/src/styles/06-components/_components.toggle.scss +18 -0
  235. package/src/styles/06-components/_index.scss +1 -1
  236. package/src/styles/06-components/old.chart.styles.scss +0 -2
  237. package/src/styles/99-utilities/_utilities.border.scss +69 -27
  238. package/src/styles/99-utilities/_utilities.display.scss +1 -1
  239. package/src/styles/99-utilities/_utilities.opacity.scss +10 -0
  240. package/src/styles/99-utilities/_utilities.position.scss +16 -9
  241. package/src/styles/99-utilities/_utilities.scss +1 -1
  242. package/src/styles/99-utilities/_utilities.sizes.scss +47 -18
  243. package/src/styles/99-utilities/_utilities.spacing.scss +118 -66
  244. package/src/styles/99-utilities/_utilities.text-gradient.scss +30 -30
  245. package/src/styles/99-utilities/_utilities.text.scss +67 -46
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Theme Manager Utility Functions
3
- *
3
+ *
4
4
  * Helper functions for theme operations including CSS loading, DOM manipulation,
5
5
  * and theme validation.
6
6
  */
@@ -13,40 +13,40 @@ import { ThemeError, ThemeErrorCode } from '../errors/errors';
13
13
  * Check if code is running in a browser environment
14
14
  */
15
15
  export const isBrowser = (): boolean => {
16
- return typeof window !== 'undefined' && typeof document !== 'undefined';
16
+ return typeof window !== 'undefined' && typeof document !== 'undefined';
17
17
  };
18
18
 
19
19
  /**
20
20
  * Check if code is running on the server (SSR)
21
21
  */
22
22
  export const isServer = (): boolean => {
23
- return !isBrowser();
23
+ return !isBrowser();
24
24
  };
25
25
 
26
26
  /**
27
27
  * Generate a unique ID for theme link elements
28
28
  */
29
29
  export const getThemeLinkId = (themeName: string): string => {
30
- return `${THEME_LINK_ID_PREFIX}${themeName}`;
30
+ return `${THEME_LINK_ID_PREFIX}${themeName}`;
31
31
  };
32
32
 
33
33
  /**
34
34
  * Sanitize path to prevent path injection attacks
35
- *
35
+ *
36
36
  * @param path - Path to sanitize
37
37
  * @returns Sanitized path
38
38
  */
39
39
  const sanitizePath = (path: string): string => {
40
- return path
41
- .replace(/[<>"']/g, '') // Remove dangerous characters
42
- .replace(/\.\./g, '') // Remove path traversal attempts
43
- .replace(/\/+/g, '/') // Normalize multiple slashes
44
- .replace(/^\/+|\/+$/g, ''); // Trim leading/trailing slashes
40
+ return path
41
+ .replace(/[<>"']/g, '') // Remove dangerous characters
42
+ .replace(/\.\./g, '') // Remove path traversal attempts
43
+ .replace(/\/+/g, '/') // Normalize multiple slashes
44
+ .replace(/^\/+|\/+$/g, ''); // Trim leading/trailing slashes
45
45
  };
46
46
 
47
47
  /**
48
48
  * Build the CSS file path for a theme
49
- *
49
+ *
50
50
  * @param themeName - Name of the theme
51
51
  * @param basePath - Base path for theme files
52
52
  * @param useMinified - Whether to use minified CSS
@@ -55,387 +55,375 @@ const sanitizePath = (path: string): string => {
55
55
  * @throws Error if theme name is invalid
56
56
  */
57
57
  export const buildThemePath = (
58
- themeName: string,
59
- basePath: string = '/themes',
60
- useMinified: boolean = false,
61
- cdnPath: string | null = null
58
+ themeName: string,
59
+ basePath: string = '/themes',
60
+ useMinified: boolean = false,
61
+ cdnPath: string | null = null
62
62
  ): string => {
63
- // Validate theme name to prevent path injection
64
- if (!isValidThemeName(themeName)) {
65
- throw new ThemeError(
66
- `Invalid theme name: "${themeName}". Theme names must be lowercase alphanumeric with hyphens (e.g., "my-theme").`,
67
- ThemeErrorCode.INVALID_THEME_NAME,
68
- { themeName, pattern: /^[a-z0-9]+(-[a-z0-9]+)*$/ }
69
- );
70
- }
71
-
72
- const extension = useMinified ? '.min.css' : '.css';
73
- const fileName = `${themeName}${extension}`;
74
-
75
- if (cdnPath) {
76
- // Sanitize CDN path to prevent path injection
77
- const cleanCdnPath = sanitizePath(cdnPath);
78
- return `${cleanCdnPath}/${fileName}`;
79
- }
80
-
81
- // Sanitize basePath to prevent path injection
82
- const cleanBasePath = sanitizePath(basePath);
83
- const cleanFileName = fileName.replace(/^\//, '');
84
-
85
- return `${cleanBasePath}/${cleanFileName}`;
63
+ // Validate theme name to prevent path injection
64
+ if (!isValidThemeName(themeName)) {
65
+ throw new ThemeError(
66
+ `Invalid theme name: "${themeName}". Theme names must be lowercase alphanumeric with hyphens (e.g., "my-theme").`,
67
+ ThemeErrorCode.INVALID_THEME_NAME,
68
+ { themeName, pattern: /^[a-z0-9]+(-[a-z0-9]+)*$/ }
69
+ );
70
+ }
71
+
72
+ const extension = useMinified ? '.min.css' : '.css';
73
+ const fileName = `${themeName}${extension}`;
74
+
75
+ if (cdnPath) {
76
+ // Sanitize CDN path to prevent path injection
77
+ const cleanCdnPath = sanitizePath(cdnPath);
78
+ return `${cleanCdnPath}/${fileName}`;
79
+ }
80
+
81
+ // Sanitize basePath to prevent path injection
82
+ const cleanBasePath = sanitizePath(basePath);
83
+ const cleanFileName = fileName.replace(/^\//, '');
84
+
85
+ return `${cleanBasePath}/${cleanFileName}`;
86
86
  };
87
87
 
88
-
89
-
90
88
  /**
91
89
  * Load theme CSS from a full path
92
- *
90
+ *
93
91
  * @param fullPath - Full path to the CSS file
94
92
  * @param linkId - ID for the link element
95
93
  * @returns Promise that resolves when CSS is loaded
96
94
  */
97
- export const loadThemeCSS = (
98
- fullPath: string,
99
- linkId: string
100
- ): Promise<void> => {
101
- if (isServer()) {
102
- return Promise.resolve();
95
+ export const loadThemeCSS = (fullPath: string, linkId: string): Promise<void> => {
96
+ if (isServer()) {
97
+ return Promise.resolve();
98
+ }
99
+
100
+ return new Promise((resolve, reject) => {
101
+ // Check if theme is already loaded
102
+ const existingLink = document.getElementById(linkId);
103
+ if (existingLink) {
104
+ resolve();
105
+ return;
103
106
  }
104
107
 
105
- return new Promise((resolve, reject) => {
106
- // Check if theme is already loaded
107
- const existingLink = document.getElementById(linkId);
108
- if (existingLink) {
109
- resolve();
110
- return;
111
- }
108
+ // Create link element
109
+ const link = document.createElement('link');
110
+ link.id = linkId;
111
+ link.rel = 'stylesheet';
112
+ link.type = 'text/css';
113
+ link.href = fullPath;
114
+
115
+ // Add data attribute for tracking
116
+ link.setAttribute('data-atomix-theme', 'true');
117
+
118
+ // Handle load success
119
+ link.onload = () => {
120
+ resolve();
121
+ };
112
122
 
113
- // Create link element
114
- const link = document.createElement('link');
115
- link.id = linkId;
116
- link.rel = 'stylesheet';
117
- link.type = 'text/css';
118
- link.href = fullPath;
119
-
120
- // Add data attribute for tracking
121
- link.setAttribute('data-atomix-theme', 'true');
122
-
123
- // Handle load success
124
- link.onload = () => {
125
- resolve();
126
- };
127
-
128
- // Handle load error
129
- link.onerror = () => {
130
- // Remove failed link element
131
- link.remove();
132
- reject(new ThemeError(
133
- `Failed to load theme CSS from: ${fullPath}. Please check that the file exists and is accessible.`,
134
- ThemeErrorCode.THEME_LOAD_FAILED,
135
- { fullPath, linkId }
136
- ));
137
- };
138
-
139
- // Append to head
140
- document.head.appendChild(link);
141
- });
123
+ // Handle load error
124
+ link.onerror = () => {
125
+ // Remove failed link element
126
+ link.remove();
127
+ reject(
128
+ new ThemeError(
129
+ `Failed to load theme CSS from: ${fullPath}. Please check that the file exists and is accessible.`,
130
+ ThemeErrorCode.THEME_LOAD_FAILED,
131
+ { fullPath, linkId }
132
+ )
133
+ );
134
+ };
135
+
136
+ // Append to head
137
+ document.head.appendChild(link);
138
+ });
142
139
  };
143
140
 
144
141
  /**
145
142
  * Remove theme CSS from the DOM
146
- *
143
+ *
147
144
  * @param themeNameOrLinkId - Name of the theme or link ID to remove
148
145
  */
149
146
  export const removeThemeCSS = (themeNameOrLinkId: string): void => {
150
- if (isServer()) {
151
- return;
152
- }
153
-
154
- // Try as link ID first, then as theme name
155
- let link = document.getElementById(themeNameOrLinkId);
156
- if (!link) {
157
- const linkId = getThemeLinkId(themeNameOrLinkId);
158
- link = document.getElementById(linkId);
159
- }
160
-
161
- if (link) {
162
- link.remove();
163
- }
147
+ if (isServer()) {
148
+ return;
149
+ }
150
+
151
+ // Try as link ID first, then as theme name
152
+ let link = document.getElementById(themeNameOrLinkId);
153
+ if (!link) {
154
+ const linkId = getThemeLinkId(themeNameOrLinkId);
155
+ link = document.getElementById(linkId);
156
+ }
157
+
158
+ if (link) {
159
+ link.remove();
160
+ }
164
161
  };
165
162
 
166
163
  /**
167
164
  * Remove all theme CSS files from the DOM
168
165
  */
169
166
  export const removeAllThemeCSS = (): void => {
170
- if (isServer()) {
171
- return;
172
- }
167
+ if (isServer()) {
168
+ return;
169
+ }
173
170
 
174
- const themeLinks = document.querySelectorAll('link[data-atomix-theme]');
175
- themeLinks.forEach(link => link.remove());
171
+ const themeLinks = document.querySelectorAll('link[data-atomix-theme]');
172
+ themeLinks.forEach(link => link.remove());
176
173
  };
177
174
 
178
175
  /**
179
176
  * Apply theme data attributes to the document
180
- *
177
+ *
181
178
  * @param dataAttribute - Data attribute name (default: 'data-theme')
182
179
  * @param themeName - Name of the theme
183
180
  */
184
- export const applyThemeAttributes = (
185
- dataAttribute: string,
186
- themeName: string
187
- ): void => {
188
- if (isServer()) {
189
- return;
190
- }
191
-
192
- // Set data attribute on body (with null check)
193
- if (document.body) {
194
- document.body.setAttribute(dataAttribute, themeName);
195
- }
196
-
197
- // Also set on documentElement for broader compatibility
198
- document.documentElement.setAttribute(dataAttribute, themeName);
181
+ export const applyThemeAttributes = (dataAttribute: string, themeName: string): void => {
182
+ if (isServer()) {
183
+ return;
184
+ }
185
+
186
+ // Set data attribute on body (with null check)
187
+ if (document.body) {
188
+ document.body.setAttribute(dataAttribute, themeName);
189
+ }
190
+
191
+ // Also set on documentElement for broader compatibility
192
+ document.documentElement.setAttribute(dataAttribute, themeName);
199
193
  };
200
194
 
201
195
  /**
202
196
  * Remove theme data attributes from the document
203
- *
197
+ *
204
198
  * @param dataAttribute - Data attribute name (default: 'data-theme')
205
199
  */
206
- export const removeThemeAttributes = (
207
- dataAttribute: string = 'data-theme'
208
- ): void => {
209
- if (isServer()) {
210
- return;
211
- }
212
-
213
- // Remove from body (with null check)
214
- if (document.body) {
215
- document.body.removeAttribute(dataAttribute);
216
- }
217
-
218
- // Remove from documentElement
219
- document.documentElement.removeAttribute(dataAttribute);
200
+ export const removeThemeAttributes = (dataAttribute: string = 'data-theme'): void => {
201
+ if (isServer()) {
202
+ return;
203
+ }
204
+
205
+ // Remove from body (with null check)
206
+ if (document.body) {
207
+ document.body.removeAttribute(dataAttribute);
208
+ }
209
+
210
+ // Remove from documentElement
211
+ document.documentElement.removeAttribute(dataAttribute);
220
212
  };
221
213
 
222
214
  /**
223
215
  * Get the current theme from data attributes
224
- *
216
+ *
225
217
  * @param dataAttribute - Data attribute name (default: 'data-theme')
226
218
  * @returns Current theme name or null
227
219
  */
228
- export const getCurrentThemeFromDOM = (
229
- dataAttribute: string = 'data-theme'
230
- ): string | null => {
231
- if (isServer()) {
232
- return null;
233
- }
234
-
235
- // Add null checks for SSR safety
236
- const bodyTheme = document.body?.getAttribute(dataAttribute);
237
- const htmlTheme = document.documentElement?.getAttribute(dataAttribute);
238
- return bodyTheme || htmlTheme || null;
220
+ export const getCurrentThemeFromDOM = (dataAttribute: string = 'data-theme'): string | null => {
221
+ if (isServer()) {
222
+ return null;
223
+ }
224
+
225
+ // Add null checks for SSR safety
226
+ const bodyTheme = document.body?.getAttribute(dataAttribute);
227
+ const htmlTheme = document.documentElement?.getAttribute(dataAttribute);
228
+ return bodyTheme || htmlTheme || null;
239
229
  };
240
230
 
241
231
  /**
242
232
  * Detect system theme preference
243
- *
233
+ *
244
234
  * @returns 'dark' if system prefers dark mode, 'light' otherwise
245
235
  */
246
236
  export const getSystemTheme = (): 'light' | 'dark' => {
247
- if (isServer()) {
248
- return 'light';
249
- }
237
+ if (isServer()) {
238
+ return 'light';
239
+ }
250
240
 
251
- if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
252
- return 'dark';
253
- }
241
+ if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
242
+ return 'dark';
243
+ }
254
244
 
255
- return 'light';
245
+ return 'light';
256
246
  };
257
247
 
258
248
  /**
259
249
  * Check if a theme is currently loaded in the DOM
260
- *
250
+ *
261
251
  * @param themeName - Name of the theme to check
262
252
  * @returns True if theme CSS is loaded
263
253
  */
264
254
  export const isThemeLoaded = (themeName: string): boolean => {
265
- if (isServer()) {
266
- return false;
267
- }
255
+ if (isServer()) {
256
+ return false;
257
+ }
268
258
 
269
- const linkId = getThemeLinkId(themeName);
270
- return document.getElementById(linkId) !== null;
259
+ const linkId = getThemeLinkId(themeName);
260
+ return document.getElementById(linkId) !== null;
271
261
  };
272
262
 
273
263
  /**
274
264
  * Validate theme metadata
275
- *
265
+ *
276
266
  * @param metadata - Theme metadata to validate
277
267
  * @returns Validation result with errors and warnings
278
268
  */
279
- export const validateThemeMetadata = (
280
- metadata: unknown
281
- ): ThemeValidationResult => {
282
- const errors: string[] = [];
283
- const warnings: string[] = [];
284
-
285
- if (!metadata || typeof metadata !== 'object') {
286
- errors.push('Theme metadata must be an object');
287
- return { valid: false, errors, warnings };
288
- }
289
-
290
- const theme = metadata as Partial<ThemeMetadata>;
291
-
292
- // Required fields
293
- if (!theme.name || typeof theme.name !== 'string') {
294
- errors.push('Theme must have a valid name');
269
+ export const validateThemeMetadata = (metadata: unknown): ThemeValidationResult => {
270
+ const errors: string[] = [];
271
+ const warnings: string[] = [];
272
+
273
+ if (!metadata || typeof metadata !== 'object') {
274
+ errors.push('Theme metadata must be an object');
275
+ return { valid: false, errors, warnings };
276
+ }
277
+
278
+ const theme = metadata as Partial<ThemeMetadata>;
279
+
280
+ // Required fields
281
+ if (!theme.name || typeof theme.name !== 'string') {
282
+ errors.push('Theme must have a valid name');
283
+ }
284
+
285
+ // Optional but recommended fields
286
+ if (!theme.description) {
287
+ warnings.push('Theme should have a description');
288
+ }
289
+
290
+ if (!theme.version) {
291
+ warnings.push('Theme should have a version');
292
+ }
293
+
294
+ if (!theme.author) {
295
+ warnings.push('Theme should have an author');
296
+ }
297
+
298
+ // Validate status if provided
299
+ if (theme.status) {
300
+ const validStatuses = ['stable', 'beta', 'experimental', 'deprecated'];
301
+ if (!validStatuses.includes(theme.status)) {
302
+ errors.push(`Invalid status: ${theme.status}. Must be one of: ${validStatuses.join(', ')}`);
295
303
  }
296
-
297
- // Optional but recommended fields
298
- if (!theme.description) {
299
- warnings.push('Theme should have a description');
300
- }
301
-
302
- if (!theme.version) {
303
- warnings.push('Theme should have a version');
304
- }
305
-
306
- if (!theme.author) {
307
- warnings.push('Theme should have an author');
308
- }
309
-
310
- // Validate status if provided
311
- if (theme.status) {
312
- const validStatuses = ['stable', 'beta', 'experimental', 'deprecated'];
313
- if (!validStatuses.includes(theme.status)) {
314
- errors.push(`Invalid status: ${theme.status}. Must be one of: ${validStatuses.join(', ')}`);
304
+ }
305
+
306
+ // Validate color if provided
307
+ if (theme.color && typeof theme.color !== 'string') {
308
+ errors.push('Theme color must be a string');
309
+ }
310
+
311
+ // Validate a11y if provided
312
+ if (theme.a11y) {
313
+ if (typeof theme.a11y !== 'object') {
314
+ errors.push('Theme a11y must be an object');
315
+ } else {
316
+ if (theme.a11y.contrastTarget !== undefined) {
317
+ if (typeof theme.a11y.contrastTarget !== 'number' || theme.a11y.contrastTarget < 0) {
318
+ errors.push('Theme a11y.contrastTarget must be a positive number');
315
319
  }
316
- }
317
-
318
- // Validate color if provided
319
- if (theme.color && typeof theme.color !== 'string') {
320
- errors.push('Theme color must be a string');
321
- }
322
-
323
- // Validate a11y if provided
324
- if (theme.a11y) {
325
- if (typeof theme.a11y !== 'object') {
326
- errors.push('Theme a11y must be an object');
327
- } else {
328
- if (theme.a11y.contrastTarget !== undefined) {
329
- if (typeof theme.a11y.contrastTarget !== 'number' || theme.a11y.contrastTarget < 0) {
330
- errors.push('Theme a11y.contrastTarget must be a positive number');
331
- }
332
- }
333
- if (theme.a11y.modes !== undefined) {
334
- if (!Array.isArray(theme.a11y.modes)) {
335
- errors.push('Theme a11y.modes must be an array');
336
- }
337
- }
320
+ }
321
+ if (theme.a11y.modes !== undefined) {
322
+ if (!Array.isArray(theme.a11y.modes)) {
323
+ errors.push('Theme a11y.modes must be an array');
338
324
  }
325
+ }
339
326
  }
327
+ }
340
328
 
341
- return {
342
- valid: errors.length === 0,
343
- errors,
344
- warnings,
345
- };
329
+ return {
330
+ valid: errors.length === 0,
331
+ errors,
332
+ warnings,
333
+ };
346
334
  };
347
335
 
348
336
  /**
349
337
  * Validate theme name format
350
- *
338
+ *
351
339
  * @param themeName - Theme name to validate
352
340
  * @returns True if valid
353
341
  */
354
342
  export const isValidThemeName = (themeName: string): boolean => {
355
- if (!themeName || typeof themeName !== 'string') {
356
- return false;
357
- }
343
+ if (!themeName || typeof themeName !== 'string') {
344
+ return false;
345
+ }
358
346
 
359
- // Theme names should be lowercase alphanumeric with hyphens
360
- const validPattern = /^[a-z0-9]+(-[a-z0-9]+)*$/;
361
- return validPattern.test(themeName);
347
+ // Theme names should be lowercase alphanumeric with hyphens
348
+ const validPattern = /^[a-z0-9]+(-[a-z0-9]+)*$/;
349
+ return validPattern.test(themeName);
362
350
  };
363
351
 
364
352
  /**
365
353
  * Create a storage adapter for localStorage
366
354
  */
367
355
  export const createLocalStorageAdapter = () => {
368
- return {
369
- getItem: (key: string): string | null => {
370
- if (isServer()) return null;
371
- try {
372
- return localStorage.getItem(key);
373
- } catch {
374
- return null;
375
- }
376
- },
377
- setItem: (key: string, value: string): void => {
378
- if (isServer()) return;
379
- try {
380
- localStorage.setItem(key, value);
381
- } catch {
382
- // Silently fail if localStorage is not available
383
- }
384
- },
385
- removeItem: (key: string): void => {
386
- if (isServer()) return;
387
- try {
388
- localStorage.removeItem(key);
389
- } catch {
390
- // Silently fail
391
- }
392
- },
393
- isAvailable: (): boolean => {
394
- if (isServer()) return false;
395
- try {
396
- const test = '__atomix_storage_test__';
397
- localStorage.setItem(test, test);
398
- localStorage.removeItem(test);
399
- return true;
400
- } catch {
401
- return false;
402
- }
403
- },
404
- };
356
+ return {
357
+ getItem: (key: string): string | null => {
358
+ if (isServer()) return null;
359
+ try {
360
+ return localStorage.getItem(key);
361
+ } catch {
362
+ return null;
363
+ }
364
+ },
365
+ setItem: (key: string, value: string): void => {
366
+ if (isServer()) return;
367
+ try {
368
+ localStorage.setItem(key, value);
369
+ } catch {
370
+ // Silently fail if localStorage is not available
371
+ }
372
+ },
373
+ removeItem: (key: string): void => {
374
+ if (isServer()) return;
375
+ try {
376
+ localStorage.removeItem(key);
377
+ } catch {
378
+ // Silently fail
379
+ }
380
+ },
381
+ isAvailable: (): boolean => {
382
+ if (isServer()) return false;
383
+ try {
384
+ const test = '__atomix_storage_test__';
385
+ localStorage.setItem(test, test);
386
+ localStorage.removeItem(test);
387
+ return true;
388
+ } catch {
389
+ return false;
390
+ }
391
+ },
392
+ };
405
393
  };
406
394
 
407
395
  /**
408
396
  * Debounce function for performance optimization
409
- *
397
+ *
410
398
  * @param func - Function to debounce
411
399
  * @param wait - Wait time in milliseconds
412
400
  * @returns Debounced function with cancel method
413
401
  */
414
402
  export const debounce = <T extends (...args: any[]) => any>(
415
- func: T,
416
- wait: number
403
+ func: T,
404
+ wait: number
417
405
  ): ((...args: Parameters<T>) => void) & { cancel: () => void } => {
418
- let timeout: ReturnType<typeof setTimeout> | null = null;
419
-
420
- const debounced = function executedFunction(...args: Parameters<T>) {
421
- const later = () => {
422
- timeout = null;
423
- func(...args);
424
- };
406
+ let timeout: ReturnType<typeof setTimeout> | null = null;
425
407
 
426
- if (timeout !== null) {
427
- clearTimeout(timeout);
428
- }
429
- timeout = setTimeout(later, wait);
408
+ const debounced = function executedFunction(...args: Parameters<T>) {
409
+ const later = () => {
410
+ timeout = null;
411
+ func(...args);
430
412
  };
431
413
 
432
- // Add cancel method for cleanup
433
- debounced.cancel = () => {
434
- if (timeout !== null) {
435
- clearTimeout(timeout);
436
- timeout = null;
437
- }
438
- };
414
+ if (timeout !== null) {
415
+ clearTimeout(timeout);
416
+ }
417
+ timeout = setTimeout(later, wait);
418
+ };
419
+
420
+ // Add cancel method for cleanup
421
+ debounced.cancel = () => {
422
+ if (timeout !== null) {
423
+ clearTimeout(timeout);
424
+ timeout = null;
425
+ }
426
+ };
439
427
 
440
- return debounced;
428
+ return debounced;
441
429
  };