@shohojdhara/atomix 0.3.12 → 0.3.14

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 (155) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +2 -0
  3. package/dist/atomix.css +101 -88
  4. package/dist/atomix.css.map +1 -1
  5. package/dist/atomix.min.css +5 -15258
  6. package/dist/atomix.min.css.map +1 -1
  7. package/dist/charts.d.ts +1 -1
  8. package/dist/charts.js +17 -19
  9. package/dist/charts.js.map +1 -1
  10. package/dist/core.d.ts +41 -11
  11. package/dist/core.js +55 -41
  12. package/dist/core.js.map +1 -1
  13. package/dist/forms.d.ts +28 -11
  14. package/dist/forms.js +25 -24
  15. package/dist/forms.js.map +1 -1
  16. package/dist/heavy.d.ts +1 -1
  17. package/dist/heavy.js +32 -25
  18. package/dist/heavy.js.map +1 -1
  19. package/dist/index.d.ts +122 -46
  20. package/dist/index.esm.js +865 -200
  21. package/dist/index.esm.js.map +1 -1
  22. package/dist/index.js +870 -204
  23. package/dist/index.js.map +1 -1
  24. package/dist/index.min.js +1 -1
  25. package/dist/index.min.js.map +1 -1
  26. package/dist/theme.d.ts +27 -2
  27. package/dist/theme.js +721 -108
  28. package/dist/theme.js.map +1 -1
  29. package/package.json +1 -1
  30. package/scripts/atomix-cli.js +610 -1111
  31. package/scripts/cli/component-generator.js +610 -0
  32. package/scripts/cli/documentation-sync.js +542 -0
  33. package/scripts/cli/interactive-init.js +84 -288
  34. package/scripts/cli/mappings.js +211 -0
  35. package/scripts/cli/migration-tools.js +95 -288
  36. package/scripts/cli/template-manager.js +107 -0
  37. package/scripts/cli/templates/README.md +123 -0
  38. package/scripts/cli/templates/composable-templates.js +149 -0
  39. package/scripts/cli/templates/config-templates.js +126 -0
  40. package/scripts/cli/templates/index.js +95 -0
  41. package/scripts/cli/templates/project-templates.js +214 -0
  42. package/scripts/cli/templates/react-templates.js +261 -0
  43. package/scripts/cli/templates/scss-templates.js +156 -0
  44. package/scripts/cli/templates/storybook-templates.js +236 -0
  45. package/scripts/cli/templates/testing-templates.js +45 -0
  46. package/scripts/cli/templates/token-templates.js +447 -0
  47. package/scripts/cli/templates/types-templates.js +133 -0
  48. package/scripts/cli/templates-original-backup.js +1655 -0
  49. package/scripts/cli/templates.js +35 -0
  50. package/scripts/cli/templates_backup.js +684 -0
  51. package/scripts/cli/theme-bridge.js +20 -14
  52. package/scripts/cli/token-manager.js +150 -77
  53. package/scripts/cli/utils.js +37 -25
  54. package/src/components/Accordion/Accordion.stories.tsx +5 -5
  55. package/src/components/Accordion/Accordion.test.tsx +57 -0
  56. package/src/components/Accordion/Accordion.tsx +4 -0
  57. package/src/components/AtomixGlass/AtomixGlassContainer.tsx +41 -44
  58. package/src/components/AtomixGlass/stories/AtomixGlass.stories.tsx +1 -1
  59. package/src/components/AtomixGlass/stories/Examples.stories.tsx +37 -37
  60. package/src/components/AtomixGlass/stories/Modes.stories.tsx +1 -2
  61. package/src/components/AtomixGlass/stories/Playground.stories.tsx +50 -51
  62. package/src/components/Avatar/Avatar.stories.tsx +26 -26
  63. package/src/components/Badge/Badge.stories.tsx +31 -31
  64. package/src/components/Badge/Badge.test.tsx +51 -0
  65. package/src/components/Badge/Badge.tsx +20 -1
  66. package/src/components/Block/Block.stories.tsx +5 -5
  67. package/src/components/Breadcrumb/Breadcrumb.stories.tsx +1 -1
  68. package/src/components/Breadcrumb/Breadcrumb.tsx +2 -2
  69. package/src/components/Button/Button.stories.tsx +13 -13
  70. package/src/components/Button/Button.tsx +4 -4
  71. package/src/components/Button/ButtonGroup.stories.tsx +2 -2
  72. package/src/components/Button/README.md +5 -0
  73. package/src/components/Callout/Callout.stories.tsx +11 -11
  74. package/src/components/Callout/Callout.test.tsx +10 -10
  75. package/src/components/Callout/Callout.tsx +7 -7
  76. package/src/components/Callout/README.md +9 -8
  77. package/src/components/Card/Card.tsx +2 -2
  78. package/src/components/Chart/Chart.stories.tsx +6 -6
  79. package/src/components/Chart/Chart.tsx +1 -1
  80. package/src/components/ColorModeToggle/ColorModeToggle.stories.tsx +1 -1
  81. package/src/components/DataTable/DataTable.tsx +14 -12
  82. package/src/components/DatePicker/DatePicker.stories.tsx +6 -6
  83. package/src/components/Dropdown/Dropdown.stories.tsx +4 -4
  84. package/src/components/Form/Checkbox.stories.tsx +3 -3
  85. package/src/components/Form/Checkbox.tsx +4 -2
  86. package/src/components/Form/Form.stories.tsx +3 -3
  87. package/src/components/Form/FormGroup.stories.tsx +1 -1
  88. package/src/components/Form/Input.stories.tsx +28 -16
  89. package/src/components/Form/Input.test.tsx +59 -0
  90. package/src/components/Form/Input.tsx +97 -95
  91. package/src/components/Form/Radio.stories.tsx +94 -94
  92. package/src/components/Form/Radio.tsx +2 -2
  93. package/src/components/Form/Select.stories.tsx +4 -4
  94. package/src/components/Form/Select.tsx +2 -2
  95. package/src/components/Form/Textarea.stories.tsx +22 -7
  96. package/src/components/Form/Textarea.test.tsx +45 -0
  97. package/src/components/Form/Textarea.tsx +88 -86
  98. package/src/components/List/List.stories.tsx +2 -2
  99. package/src/components/Modal/Modal.stories.tsx +4 -4
  100. package/src/components/Navigation/Navbar/Navbar.stories.tsx +5 -5
  101. package/src/components/Navigation/Navbar/Navbar.tsx +1 -1
  102. package/src/components/Navigation/README.md +1 -1
  103. package/src/components/Pagination/Pagination.stories.tsx +5 -2
  104. package/src/components/Pagination/Pagination.tsx +1 -1
  105. package/src/components/PhotoViewer/PhotoViewer.stories.tsx +10 -10
  106. package/src/components/Popover/Popover.stories.tsx +1 -1
  107. package/src/components/ProductReview/ProductReview.tsx +1 -1
  108. package/src/components/Progress/Progress.tsx +46 -46
  109. package/src/components/Rating/Rating.stories.tsx +4 -4
  110. package/src/components/Rating/Rating.tsx +8 -8
  111. package/src/components/Slider/Slider.stories.tsx +63 -63
  112. package/src/components/Spinner/Spinner.stories.tsx +2 -2
  113. package/src/components/Spinner/Spinner.test.tsx +35 -0
  114. package/src/components/Spinner/Spinner.tsx +9 -2
  115. package/src/components/Testimonial/Testimonial.stories.tsx +1 -1
  116. package/src/components/Toggle/Toggle.stories.tsx +32 -9
  117. package/src/components/Toggle/Toggle.test.tsx +91 -0
  118. package/src/components/Toggle/Toggle.tsx +44 -27
  119. package/src/components/Tooltip/Tooltip.tsx +1 -1
  120. package/src/layouts/Grid/Grid.stories.tsx +49 -49
  121. package/src/layouts/MasonryGrid/MasonryGrid.stories.tsx +2 -2
  122. package/src/lib/composables/useAccordion.ts +12 -3
  123. package/src/lib/composables/useBreadcrumb.ts +2 -2
  124. package/src/lib/composables/useCallout.ts +7 -7
  125. package/src/lib/composables/useNavbar.ts +1 -1
  126. package/src/lib/constants/components.ts +1 -1
  127. package/src/lib/storybook/InteractiveDemo.tsx +113 -0
  128. package/src/lib/storybook/PreviewContainer.tsx +36 -0
  129. package/src/lib/storybook/VariantsGrid.tsx +21 -0
  130. package/src/lib/storybook/index.ts +3 -0
  131. package/src/lib/theme/core/createThemeObject.ts +9 -5
  132. package/src/lib/theme/devtools/CLI.ts +155 -0
  133. package/src/lib/theme/devtools/DesignTokensCustomizer.stories.tsx +213 -0
  134. package/src/lib/theme/devtools/DesignTokensCustomizer.tsx +566 -0
  135. package/src/lib/theme/devtools/LiveEditor.tsx +2 -1
  136. package/src/lib/theme/devtools/index.ts +3 -0
  137. package/src/lib/theme/errors/errors.ts +8 -0
  138. package/src/lib/theme/runtime/ThemeProvider.tsx +117 -57
  139. package/src/lib/theme/runtime/__tests__/ThemeProvider.integration.test.tsx +305 -0
  140. package/src/lib/theme/runtime/__tests__/ThemeProvider.test.tsx +588 -0
  141. package/src/lib/theme/utils/__tests__/themeValidation.test.ts +264 -0
  142. package/src/lib/theme/utils/index.ts +1 -0
  143. package/src/lib/theme/utils/themeValidation.ts +501 -0
  144. package/src/lib/theme-tools.ts +32 -3
  145. package/src/lib/types/components.ts +81 -26
  146. package/src/lib/utils/themeNaming.ts +1 -1
  147. package/src/styles/06-components/_components.atomix-glass.scss +14 -15
  148. package/src/styles/06-components/_components.callout.scss +29 -33
  149. package/src/styles/06-components/_index.scss +1 -1
  150. package/src/styles/99-utilities/_utilities.display.scss +14 -3
  151. package/src/styles/99-utilities/_utilities.flex.scss +10 -10
  152. package/src/styles/99-utilities/_utilities.text.scss +28 -8
  153. package/scripts/cli/__tests__/cli-commands.test.js +0 -204
  154. package/scripts/cli/__tests__/utils.test.js +0 -201
  155. package/scripts/cli/__tests__/vitest.config.js +0 -26
@@ -7,6 +7,18 @@ import { resolve, relative, isAbsolute, normalize } from 'path';
7
7
  import { existsSync } from 'fs';
8
8
  import { access } from 'fs/promises';
9
9
 
10
+ /**
11
+ * Enhanced Error Class for CLI
12
+ */
13
+ export class AtomixCLIError extends Error {
14
+ constructor(message, code, suggestions = []) {
15
+ super(message);
16
+ this.name = 'AtomixCLIError';
17
+ this.code = code;
18
+ this.suggestions = suggestions;
19
+ }
20
+ }
21
+
10
22
  /**
11
23
  * Validates and sanitizes file paths to prevent directory traversal attacks
12
24
  * @param {string} inputPath - The path to validate
@@ -17,13 +29,13 @@ export function validatePath(inputPath, basePath = process.cwd()) {
17
29
  try {
18
30
  // Normalize the paths to remove any '..' or '.' segments
19
31
  const normalizedBase = normalize(resolve(basePath));
20
- const normalizedInput = normalize(isAbsolute(inputPath)
21
- ? inputPath
32
+ const normalizedInput = normalize(isAbsolute(inputPath)
33
+ ? inputPath
22
34
  : resolve(basePath, inputPath));
23
-
35
+
24
36
  // Check if the resolved path is within the base directory
25
37
  const relativePath = relative(normalizedBase, normalizedInput);
26
-
38
+
27
39
  // If the relative path starts with '..', it's outside the base directory
28
40
  if (relativePath.startsWith('..')) {
29
41
  return {
@@ -32,7 +44,7 @@ export function validatePath(inputPath, basePath = process.cwd()) {
32
44
  error: 'Path is outside the project directory'
33
45
  };
34
46
  }
35
-
47
+
36
48
  // Additional checks for sensitive paths
37
49
  const sensitivePatterns = [
38
50
  /^\.git/,
@@ -43,7 +55,7 @@ export function validatePath(inputPath, basePath = process.cwd()) {
43
55
  /private/i,
44
56
  /secret/i
45
57
  ];
46
-
58
+
47
59
  for (const pattern of sensitivePatterns) {
48
60
  if (pattern.test(relativePath)) {
49
61
  return {
@@ -53,7 +65,7 @@ export function validatePath(inputPath, basePath = process.cwd()) {
53
65
  };
54
66
  }
55
67
  }
56
-
68
+
57
69
  return {
58
70
  isValid: true,
59
71
  safePath: normalizedInput,
@@ -80,7 +92,7 @@ export function validateComponentName(name) {
80
92
  error: 'Component name must be a non-empty string'
81
93
  };
82
94
  }
83
-
95
+
84
96
  // Check PascalCase: starts with uppercase, only contains letters and numbers
85
97
  if (!/^[A-Z][a-zA-Z0-9]*$/.test(name)) {
86
98
  return {
@@ -88,20 +100,20 @@ export function validateComponentName(name) {
88
100
  error: 'Component name must be in PascalCase (e.g., Button, CardHeader)'
89
101
  };
90
102
  }
91
-
103
+
92
104
  // Check for reserved words
93
105
  const reservedWords = [
94
106
  'Component', 'React', 'Fragment', 'Suspense', 'StrictMode',
95
107
  'Error', 'Loading', 'App', 'Root', 'Document', 'Html'
96
108
  ];
97
-
109
+
98
110
  if (reservedWords.includes(name)) {
99
111
  return {
100
112
  isValid: false,
101
113
  error: `"${name}" is a reserved word. Please choose a different name.`
102
114
  };
103
115
  }
104
-
116
+
105
117
  // Check minimum length
106
118
  if (name.length < 2) {
107
119
  return {
@@ -109,7 +121,7 @@ export function validateComponentName(name) {
109
121
  error: 'Component name must be at least 2 characters long'
110
122
  };
111
123
  }
112
-
124
+
113
125
  return { isValid: true };
114
126
  }
115
127
 
@@ -125,7 +137,7 @@ export function validateThemeName(name) {
125
137
  error: 'Theme name must be a non-empty string'
126
138
  };
127
139
  }
128
-
140
+
129
141
  // Check kebab-case: lowercase letters, numbers, and hyphens
130
142
  if (!/^[a-z][a-z0-9-]*$/.test(name)) {
131
143
  return {
@@ -133,7 +145,7 @@ export function validateThemeName(name) {
133
145
  error: 'Theme name must be lowercase and use hyphens (e.g., dark-theme)'
134
146
  };
135
147
  }
136
-
148
+
137
149
  // Check for consecutive hyphens
138
150
  if (/--/.test(name)) {
139
151
  return {
@@ -141,7 +153,7 @@ export function validateThemeName(name) {
141
153
  error: 'Theme name cannot contain consecutive hyphens'
142
154
  };
143
155
  }
144
-
156
+
145
157
  // Check for trailing hyphen
146
158
  if (name.endsWith('-')) {
147
159
  return {
@@ -149,7 +161,7 @@ export function validateThemeName(name) {
149
161
  error: 'Theme name cannot end with a hyphen'
150
162
  };
151
163
  }
152
-
164
+
153
165
  return { isValid: true };
154
166
  }
155
167
 
@@ -162,7 +174,7 @@ export function sanitizeInput(input) {
162
174
  if (typeof input !== 'string') {
163
175
  return String(input);
164
176
  }
165
-
177
+
166
178
  // Remove any shell metacharacters that could be dangerous
167
179
  return input
168
180
  .replace(/[;&|`$<>\\]/g, '')
@@ -190,7 +202,7 @@ export async function fileExists(filePath) {
190
202
  */
191
203
  export function isCI() {
192
204
  return !!(
193
- process.env.CI ||
205
+ process.env.CI ||
194
206
  process.env.CONTINUOUS_INTEGRATION ||
195
207
  process.env.GITHUB_ACTIONS ||
196
208
  process.env.GITLAB_CI ||
@@ -205,9 +217,9 @@ export function isCI() {
205
217
  * @returns {boolean}
206
218
  */
207
219
  export function isDebug() {
208
- return process.env.ATOMIX_DEBUG === 'true' ||
209
- process.argv.includes('--debug') ||
210
- process.argv.includes('-d');
220
+ return process.env.ATOMIX_DEBUG === 'true' ||
221
+ process.argv.includes('--debug') ||
222
+ process.argv.includes('-d');
211
223
  }
212
224
 
213
225
  /**
@@ -271,7 +283,7 @@ export function isValidColor(color) {
271
283
  /^hsla\(/i, // hsla()
272
284
  /^var\(--/ // CSS custom property
273
285
  ];
274
-
286
+
275
287
  return patterns.some(pattern => pattern.test(color));
276
288
  }
277
289
 
@@ -284,7 +296,7 @@ export function isValidColor(color) {
284
296
  export function validateNpmScripts(packageJson, requiredScripts = []) {
285
297
  const scripts = packageJson.scripts || {};
286
298
  const missing = requiredScripts.filter(script => !scripts[script]);
287
-
299
+
288
300
  return {
289
301
  valid: missing.length === 0,
290
302
  missing
@@ -311,7 +323,7 @@ export function checkNodeVersion(requiredVersion = '18.0.0') {
311
323
  const currentVersion = process.version.substring(1); // Remove 'v' prefix
312
324
  const current = currentVersion.split('.').map(Number);
313
325
  const required = requiredVersion.split('.').map(Number);
314
-
326
+
315
327
  let compatible = true;
316
328
  for (let i = 0; i < required.length; i++) {
317
329
  if (current[i] < required[i]) {
@@ -321,7 +333,7 @@ export function checkNodeVersion(requiredVersion = '18.0.0') {
321
333
  break;
322
334
  }
323
335
  }
324
-
336
+
325
337
  return {
326
338
  compatible,
327
339
  current: currentVersion,
@@ -145,7 +145,7 @@ export const AccordionGroup: Story = {
145
145
  render: () => (
146
146
  <div>
147
147
  <h2>Accordion Group</h2>
148
- <div className="u-d-flex u-flex-column u-gap-3" style={{ width: '500px' }}>
148
+ <div className="u-flex u-flex-column u-gap-3" style={{ width: '500px' }}>
149
149
  <Accordion title="First Accordion" defaultOpen={true}>
150
150
  <p>Content of the first accordion.</p>
151
151
  </Accordion>
@@ -186,7 +186,7 @@ export const AllVariants: Story = {
186
186
  render: () => (
187
187
  <div>
188
188
  <h2>All Accordion Variants</h2>
189
- <div className="u-d-flex u-flex-column u-gap-5">
189
+ <div className="u-flex u-flex-column u-gap-5">
190
190
  <div>
191
191
  <h3>Default</h3>
192
192
  <Accordion title="Default Accordion">
@@ -445,7 +445,7 @@ export const GlassGroup: Story = {
445
445
  Glass Accordion Group
446
446
  </h2>
447
447
  <div
448
- className="u-d-flex u-flex-column u-gap-3"
448
+ className="u-flex u-flex-column u-gap-3"
449
449
  style={{ width: '100%', maxWidth: '600px', margin: '0 auto' }}
450
450
  >
451
451
  <Accordion title="First Glass Accordion" defaultOpen={true} glass>
@@ -835,7 +835,7 @@ export const GlassInteractiveShowcase: Story = {
835
835
  Interactive Glass Accordion Showcase
836
836
  </h2>
837
837
 
838
- <div className="u-d-flex u-flex-column u-gap-3">
838
+ <div className="u-flex u-flex-column u-gap-3">
839
839
  <Accordion
840
840
  title="Features & Benefits"
841
841
  isOpen={openIndex === 0}
@@ -975,7 +975,7 @@ export const GlassRichContent: Story = {
975
975
  Glass Accordion with Rich Content
976
976
  </h2>
977
977
 
978
- <div className="u-d-flex u-flex-column u-gap-3">
978
+ <div className="u-flex u-flex-column u-gap-3">
979
979
  <Accordion title="Design Philosophy" defaultOpen={true} glass={true}>
980
980
  <div>
981
981
  <p style={{ marginBottom: '1rem' }}>
@@ -0,0 +1,57 @@
1
+ import { render, screen, fireEvent } from '@testing-library/react';
2
+ import { describe, it, expect, vi } from 'vitest';
3
+ import { Accordion } from './Accordion';
4
+ import React from 'react';
5
+
6
+ describe('Accordion Component', () => {
7
+ it('renders correctly with title', () => {
8
+ render(<Accordion title="Test Accordion">Content</Accordion>);
9
+ expect(screen.getByText('Test Accordion')).toBeInTheDocument();
10
+ expect(screen.getByText('Content')).toBeInTheDocument();
11
+ });
12
+
13
+ it('toggles when clicked', () => {
14
+ const onOpenChange = vi.fn();
15
+ render(<Accordion title="Test" onOpenChange={onOpenChange}>Content</Accordion>);
16
+ const button = screen.getByRole('button');
17
+
18
+ fireEvent.click(button);
19
+ expect(onOpenChange).toHaveBeenCalledWith(true);
20
+ expect(button).toHaveAttribute('aria-expanded', 'true');
21
+
22
+ fireEvent.click(button);
23
+ expect(onOpenChange).toHaveBeenCalledWith(false);
24
+ expect(button).toHaveAttribute('aria-expanded', 'false');
25
+ });
26
+
27
+ it('calls legacy onOpen/onClose handlers', () => {
28
+ const onOpen = vi.fn();
29
+ const onClose = vi.fn();
30
+ render(<Accordion title="Test" onOpen={onOpen} onClose={onClose}>Content</Accordion>);
31
+ const button = screen.getByRole('button');
32
+
33
+ fireEvent.click(button);
34
+ expect(onOpen).toHaveBeenCalled();
35
+
36
+ fireEvent.click(button);
37
+ expect(onClose).toHaveBeenCalled();
38
+ });
39
+
40
+ it('handles controlled state', () => {
41
+ const onOpenChange = vi.fn();
42
+ const { rerender } = render(<Accordion title="Test" isOpen={false} onOpenChange={onOpenChange}>Content</Accordion>);
43
+ const button = screen.getByRole('button');
44
+
45
+ fireEvent.click(button);
46
+ expect(onOpenChange).toHaveBeenCalledWith(true);
47
+ expect(button).toHaveAttribute('aria-expanded', 'false'); // Should not change internally
48
+
49
+ rerender(<Accordion title="Test" isOpen={true} onOpenChange={onOpenChange}>Content</Accordion>);
50
+ expect(button).toHaveAttribute('aria-expanded', 'true');
51
+ });
52
+
53
+ it('supports glass effect', () => {
54
+ const { container } = render(<Accordion title="Test" glass>Content</Accordion>);
55
+ expect(container.querySelector('.c-accordion--glass')).toBeInTheDocument();
56
+ });
57
+ });
@@ -12,6 +12,8 @@ export const Accordion: React.FC<AccordionProps> = memo(({
12
12
  defaultOpen = false,
13
13
  isOpen: controlledOpen,
14
14
  onOpenChange,
15
+ onOpen,
16
+ onClose,
15
17
  disabled = false,
16
18
  iconPosition = 'right',
17
19
  icon,
@@ -39,6 +41,8 @@ export const Accordion: React.FC<AccordionProps> = memo(({
39
41
  iconPosition,
40
42
  isOpen: controlledOpen,
41
43
  onOpenChange,
44
+ onOpen,
45
+ onClose,
42
46
  });
43
47
 
44
48
  // Default icon
@@ -14,6 +14,9 @@ import { ATOMIX_GLASS } from '../../lib/constants/components';
14
14
 
15
15
  const { CONSTANTS } = ATOMIX_GLASS;
16
16
 
17
+ // Module-level counter for deterministic ID generation
18
+ let idCounter = 0;
19
+
17
20
  // Module-level shared shader cache with LRU eviction
18
21
  const MAX_CACHE_SIZE = 15;
19
22
  interface ShaderCacheEntry {
@@ -131,16 +134,10 @@ export const AtomixGlassContainer = forwardRef<HTMLDivElement, AtomixGlassContai
131
134
  ref
132
135
  ) => {
133
136
  // Generate a stable, deterministic ID for SSR compatibility
134
- // Use a counter-based approach to avoid hydration mismatches
135
- const [filterId] = useState(() => {
136
- // Use a simple counter for deterministic IDs
137
- if (typeof window === 'undefined') {
138
- // Server-side: use a predictable pattern
139
- return `atomix-glass-filter-ssr-${Math.random().toString(36).substring(2, 11)}`;
140
- }
141
- // Client-side: use timestamp + random for uniqueness
142
- return `atomix-glass-filter-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
143
- });
137
+ // Use a module-level counter that's consistent across server and client
138
+ const filterId = useMemo(() => {
139
+ return `atomix-glass-filter-${++idCounter}`;
140
+ }, []);
144
141
 
145
142
  const [shaderMapUrl, setShaderMapUrl] = useState<string>('');
146
143
  const shaderGeneratorRef = useRef<any>(null);
@@ -207,27 +204,20 @@ export const AtomixGlassContainer = forwardRef<HTMLDivElement, AtomixGlassContai
207
204
  fragment: selectedShader,
208
205
  });
209
206
 
210
- // Use requestIdleCallback if available for non-blocking generation
211
- const generate = () => {
207
+ // Defer shader generation with longer delay to avoid blocking
208
+ setTimeout(() => {
212
209
  const url = shaderGeneratorRef.current?.updateShader() || '';
213
210
  setCachedShader(cacheKey, url);
214
211
  setShaderMapUrl(url);
215
- };
216
-
217
- if (typeof requestIdleCallback !== 'undefined') {
218
- requestIdleCallback(generate, { timeout: 1000 });
219
- } else {
220
- // Fallback to setTimeout for browsers without requestIdleCallback
221
- setTimeout(generate, 0);
222
- }
212
+ }, 100);
223
213
  } catch (error) {
224
214
  console.warn('AtomixGlassContainer: Error generating shader map', error);
225
215
  setShaderMapUrl(''); // Fallback to empty string
226
216
  }
227
217
  };
228
218
 
229
- // Debounce with 300ms delay
230
- shaderDebounceTimeoutRef.current = setTimeout(generateShader, 300);
219
+ // Debounce with 500ms delay to reduce frequency
220
+ shaderDebounceTimeoutRef.current = setTimeout(generateShader, 500);
231
221
  } else {
232
222
  // Not in shader mode, clear URL
233
223
  setShaderMapUrl('');
@@ -406,7 +396,6 @@ export const AtomixGlassContainer = forwardRef<HTMLDivElement, AtomixGlassContai
406
396
  };
407
397
  }
408
398
  }, [
409
- filterId,
410
399
  liquidBlur,
411
400
  saturation,
412
401
  blurAmount,
@@ -484,10 +473,12 @@ export const AtomixGlassContainer = forwardRef<HTMLDivElement, AtomixGlassContai
484
473
  >
485
474
  <div
486
475
  className={ATOMIX_GLASS.INNER_CLASS}
487
- style={{
488
- padding: `var(--atomix-glass-container-padding)`,
489
- boxShadow: `var(--atomix-glass-container-box-shadow)`,
490
- }}
476
+ style={
477
+ {
478
+ padding: `var(--atomix-glass-container-padding)`,
479
+ boxShadow: `var(--atomix-glass-container-box-shadow)`,
480
+ } as CSSProperties
481
+ }
491
482
  onMouseEnter={onMouseEnter}
492
483
  onMouseLeave={onMouseLeave}
493
484
  onMouseDown={onMouseDown}
@@ -513,31 +504,37 @@ export const AtomixGlassContainer = forwardRef<HTMLDivElement, AtomixGlassContai
513
504
  {/* Enhanced Apple Liquid Glass Inner Shadow Layer */}
514
505
  <div
515
506
  className={ATOMIX_GLASS.FILTER_OVERLAY_CLASS}
516
- style={{
517
- filter: `url(#${filterId})`,
518
- backdropFilter: `var(--atomix-glass-container-backdrop)`,
519
- borderRadius: `var(--atomix-glass-container-radius)`,
520
- }}
507
+ style={
508
+ {
509
+ filter: `url(#${filterId})`,
510
+ backdropFilter: `var(--atomix-glass-container-backdrop)`,
511
+ borderRadius: `var(--atomix-glass-container-radius)`,
512
+ } as CSSProperties
513
+ }
521
514
  />
522
515
  <div
523
516
  className={ATOMIX_GLASS.FILTER_SHADOW_CLASS}
524
- style={{
525
- boxShadow: `var(--atomix-glass-container-shadow)`,
526
- opacity: `var(--atomix-glass-container-shadow-opacity)`,
527
- background: `var(--atomix-glass-container-bg)`,
528
- borderRadius: `var(--atomix-glass-container-radius)`,
529
- }}
517
+ style={
518
+ {
519
+ boxShadow: `var(--atomix-glass-container-shadow)`,
520
+ opacity: `var(--atomix-glass-container-shadow-opacity)`,
521
+ background: `var(--atomix-glass-container-bg)`,
522
+ borderRadius: `var(--atomix-glass-container-radius)`,
523
+ } as CSSProperties
524
+ }
530
525
  />
531
526
  </div>
532
527
 
533
528
  <div
534
529
  ref={contentRef}
535
530
  className={ATOMIX_GLASS.CONTENT_CLASS}
536
- style={{
537
- position: 'relative',
538
- textShadow: `var(--atomix-glass-container-text-shadow)`,
539
- ...(elasticity > 0 ? { zIndex: 100 } : {}),
540
- }}
531
+ style={
532
+ {
533
+ position: 'relative',
534
+ textShadow: `var(--atomix-glass-container-text-shadow)`,
535
+ ...(elasticity > 0 ? { zIndex: 100 } : {}),
536
+ } as CSSProperties
537
+ }
541
538
  >
542
539
  {children}
543
540
  </div>
@@ -547,4 +544,4 @@ export const AtomixGlassContainer = forwardRef<HTMLDivElement, AtomixGlassContai
547
544
  }
548
545
  );
549
546
 
550
- AtomixGlassContainer.displayName = 'AtomixGlassContainer';
547
+ AtomixGlassContainer.displayName = 'AtomixGlassContainer';
@@ -946,7 +946,7 @@ export const PerformanceOptimization: Story = {
946
946
  <div style={{ margin: '0 auto', width: '100%' }}>
947
947
  {/* Header Section */}
948
948
  <div style={{ textAlign: 'center', marginBottom: '60px' }}>
949
- <Badge variant="primary" label="Performance Guide" glass={{className: 'u-d-inline-block', children:<></>}} />
949
+ <Badge variant="primary" label="Performance Guide" glass={{className: 'u-inline-block', children:<></>}} />
950
950
  <h1
951
951
  style={{
952
952
  color: '#fff',