@shohojdhara/atomix 0.5.0 → 0.5.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 (168) hide show
  1. package/atomix.config.ts +12 -0
  2. package/build-tools/webpack-loader.js +5 -4
  3. package/dist/atomix.css +230 -83
  4. package/dist/atomix.css.map +1 -1
  5. package/dist/atomix.min.css +1 -1
  6. package/dist/atomix.min.css.map +1 -1
  7. package/dist/build-tools/webpack-loader.js +5 -4
  8. package/dist/charts.d.ts +24 -23
  9. package/dist/charts.js +271 -369
  10. package/dist/charts.js.map +1 -1
  11. package/dist/config.d.ts +624 -0
  12. package/dist/config.js +59 -0
  13. package/dist/config.js.map +1 -0
  14. package/dist/core.d.ts +3 -2
  15. package/dist/core.js +342 -382
  16. package/dist/core.js.map +1 -1
  17. package/dist/forms.d.ts +4 -6
  18. package/dist/forms.js +233 -334
  19. package/dist/forms.js.map +1 -1
  20. package/dist/heavy.d.ts +11 -2
  21. package/dist/heavy.js +406 -445
  22. package/dist/heavy.js.map +1 -1
  23. package/dist/index.d.ts +109 -65
  24. package/dist/index.esm.js +654 -748
  25. package/dist/index.esm.js.map +1 -1
  26. package/dist/index.js +621 -717
  27. package/dist/index.js.map +1 -1
  28. package/dist/index.min.js +1 -1
  29. package/dist/index.min.js.map +1 -1
  30. package/dist/layout.js +59 -60
  31. package/dist/layout.js.map +1 -1
  32. package/dist/theme.js +4 -4
  33. package/dist/theme.js.map +1 -1
  34. package/package.json +24 -9
  35. package/scripts/atomix-cli.js +15 -1
  36. package/scripts/cli/__tests__/complexity-utils.test.js +24 -0
  37. package/scripts/cli/__tests__/detector.test.js +50 -0
  38. package/scripts/cli/__tests__/template-engine.test.js +23 -0
  39. package/scripts/cli/__tests__/test-setup.js +1 -133
  40. package/scripts/cli/commands/doctor.js +15 -3
  41. package/scripts/cli/commands/generate.js +113 -51
  42. package/scripts/cli/internal/ai-engine.js +30 -10
  43. package/scripts/cli/internal/complexity-utils.js +60 -0
  44. package/scripts/cli/internal/component-validator.js +49 -16
  45. package/scripts/cli/internal/generator.js +89 -36
  46. package/scripts/cli/internal/hook-generator.js +5 -2
  47. package/scripts/cli/internal/itcss-generator.js +16 -12
  48. package/scripts/cli/templates/next-templates.js +81 -30
  49. package/scripts/cli/templates/storybook-templates.js +12 -2
  50. package/scripts/cli/utils/detector.js +45 -7
  51. package/scripts/cli/utils/diagnostics.js +78 -0
  52. package/scripts/cli/utils/telemetry.js +13 -0
  53. package/src/components/Accordion/Accordion.stories.tsx +4 -0
  54. package/src/components/AtomixGlass/AtomixGlass.tsx +188 -128
  55. package/src/components/AtomixGlass/AtomixGlassContainer.tsx +63 -91
  56. package/src/components/AtomixGlass/PerformanceDashboard.tsx +153 -201
  57. package/src/components/AtomixGlass/__snapshots__/AtomixGlass.test.tsx.snap +9 -6
  58. package/src/components/AtomixGlass/glass-utils.ts +51 -1
  59. package/src/components/AtomixGlass/stories/AnimationFeatures.stories.tsx +52 -46
  60. package/src/components/AtomixGlass/stories/Examples.stories.tsx +573 -236
  61. package/src/components/AtomixGlass/stories/Playground.stories.tsx +88 -41
  62. package/src/components/AtomixGlass/stories/argTypes.ts +19 -19
  63. package/src/components/AtomixGlass/stories/shared-components.tsx +7 -12
  64. package/src/components/AtomixGlass/stories/types.ts +3 -3
  65. package/src/components/Button/Button.tsx +114 -57
  66. package/src/components/Callout/Callout.tsx +4 -4
  67. package/src/components/Chart/ChartRenderer.tsx +1 -1
  68. package/src/components/Chart/DonutChart.tsx +11 -8
  69. package/src/components/EdgePanel/EdgePanel.tsx +119 -115
  70. package/src/components/Form/Select.tsx +4 -4
  71. package/src/components/List/List.tsx +4 -4
  72. package/src/components/Navigation/SideMenu/SideMenu.tsx +6 -6
  73. package/src/components/PhotoViewer/PhotoViewerImage.tsx +1 -1
  74. package/src/components/ProductReview/ProductReview.tsx +4 -2
  75. package/src/components/Rating/Rating.tsx +4 -2
  76. package/src/components/SectionIntro/SectionIntro.tsx +4 -2
  77. package/src/components/Steps/Steps.tsx +1 -1
  78. package/src/components/Tabs/Tabs.tsx +5 -5
  79. package/src/components/Testimonial/Testimonial.tsx +4 -2
  80. package/src/components/VideoPlayer/VideoPlayer.tsx +4 -2
  81. package/src/layouts/CssGrid/CssGrid.stories.tsx +464 -0
  82. package/src/layouts/CssGrid/CssGrid.tsx +215 -0
  83. package/src/layouts/CssGrid/index.ts +8 -0
  84. package/src/layouts/CssGrid/scripts/CssGrid.js +284 -0
  85. package/src/layouts/CssGrid/scripts/index.js +43 -0
  86. package/src/layouts/Grid/scripts/Container.js +139 -0
  87. package/src/layouts/Grid/scripts/Grid.js +184 -0
  88. package/src/layouts/Grid/scripts/GridCol.js +273 -0
  89. package/src/layouts/Grid/scripts/Row.js +154 -0
  90. package/src/layouts/Grid/scripts/index.js +48 -0
  91. package/src/layouts/MasonryGrid/MasonryGrid.tsx +71 -59
  92. package/src/lib/composables/atomix-glass/useGlassSize.ts +1 -1
  93. package/src/lib/composables/useAccordion.ts +5 -5
  94. package/src/lib/composables/useAtomixGlass.ts +111 -74
  95. package/src/lib/composables/useAtomixGlassStyles.ts +0 -2
  96. package/src/lib/composables/useBarChart.ts +2 -2
  97. package/src/lib/composables/useChart.ts +3 -2
  98. package/src/lib/composables/useChartToolbar.ts +48 -66
  99. package/src/lib/composables/useDataTable.ts +1 -1
  100. package/src/lib/composables/useDatePicker.ts +2 -2
  101. package/src/lib/composables/useEdgePanel.ts +45 -54
  102. package/src/lib/composables/useHeroBackgroundSlider.ts +5 -5
  103. package/src/lib/composables/usePhotoViewer.ts +2 -3
  104. package/src/lib/composables/usePieChart.ts +1 -1
  105. package/src/lib/composables/usePopover.ts +151 -139
  106. package/src/lib/composables/useSideMenu.ts +28 -41
  107. package/src/lib/composables/useSlider.ts +2 -6
  108. package/src/lib/composables/useTooltip.ts +2 -2
  109. package/src/lib/config/index.ts +39 -0
  110. package/src/lib/constants/components.ts +1 -0
  111. package/src/lib/theme/devtools/Comparator.tsx +1 -1
  112. package/src/lib/theme/devtools/Inspector.tsx +1 -1
  113. package/src/lib/theme/devtools/LiveEditor.tsx +1 -1
  114. package/src/lib/theme/runtime/ThemeProvider.tsx +1 -1
  115. package/src/lib/types/components.ts +1 -0
  116. package/src/styles/01-settings/_index.scss +1 -0
  117. package/src/styles/01-settings/_settings.atomix-glass.scss +174 -0
  118. package/src/styles/01-settings/_settings.masonry-grid.scss +42 -6
  119. package/src/styles/02-tools/_tools.glass.scss +6 -0
  120. package/src/styles/05-objects/_objects.masonry-grid.scss +162 -24
  121. package/src/styles/06-components/_components.atomix-glass.scss +160 -99
  122. package/scripts/cli/__tests__/README.md +0 -81
  123. package/scripts/cli/__tests__/basic.test.js +0 -116
  124. package/scripts/cli/__tests__/clean.test.js +0 -278
  125. package/scripts/cli/__tests__/component-generator.test.js +0 -332
  126. package/scripts/cli/__tests__/component-validator.test.js +0 -433
  127. package/scripts/cli/__tests__/generator.test.js +0 -613
  128. package/scripts/cli/__tests__/glass-motion.test.js +0 -256
  129. package/scripts/cli/__tests__/integration.test.js +0 -938
  130. package/scripts/cli/__tests__/migrate.test.js +0 -74
  131. package/scripts/cli/__tests__/security.test.js +0 -206
  132. package/scripts/cli/__tests__/theme-bridge.test.js +0 -507
  133. package/scripts/cli/__tests__/token-manager.test.js +0 -251
  134. package/scripts/cli/__tests__/token-provider.test.js +0 -361
  135. package/scripts/cli/__tests__/utils.test.js +0 -165
  136. package/src/components/AtomixGlass/stories/AnimationTests.stories.tsx +0 -95
  137. package/src/components/AtomixGlass/stories/CardExamples.stories.tsx +0 -212
  138. package/src/components/AtomixGlass/stories/Customization.stories.tsx +0 -131
  139. package/src/components/AtomixGlass/stories/DashboardExamples.stories.tsx +0 -348
  140. package/src/components/AtomixGlass/stories/EcommerceExamples.stories.tsx +0 -410
  141. package/src/components/AtomixGlass/stories/FormExamples.stories.tsx +0 -436
  142. package/src/components/AtomixGlass/stories/HeroExamples.stories.tsx +0 -264
  143. package/src/components/AtomixGlass/stories/InteractivePlayground.stories.tsx +0 -247
  144. package/src/components/AtomixGlass/stories/MobileUIExamples.stories.tsx +0 -418
  145. package/src/components/AtomixGlass/stories/ModalExamples.stories.tsx +0 -402
  146. package/src/components/AtomixGlass/stories/Modes.stories.tsx +0 -1082
  147. package/src/components/AtomixGlass/stories/Overview.stories.tsx +0 -497
  148. package/src/components/AtomixGlass/stories/Performance.stories.tsx +0 -103
  149. package/src/components/AtomixGlass/stories/PresetGallery.stories.tsx +0 -335
  150. package/src/components/AtomixGlass/stories/Shaders.stories.tsx +0 -395
  151. package/src/components/AtomixGlass/stories/WidgetExamples.stories.tsx +0 -441
  152. package/src/components/TypedButton/TypedButton.stories.tsx +0 -59
  153. package/src/components/TypedButton/TypedButton.tsx +0 -39
  154. package/src/components/TypedButton/index.ts +0 -2
  155. package/src/lib/composables/useBreadcrumb.ts +0 -81
  156. package/src/lib/composables/useChartInteractions.ts +0 -123
  157. package/src/lib/composables/useChartPerformance.ts +0 -347
  158. package/src/lib/composables/useDropdown.ts +0 -338
  159. package/src/lib/composables/useModal.ts +0 -110
  160. package/src/lib/composables/useTypedButton.ts +0 -66
  161. package/src/lib/hooks/usePerformanceMonitor.ts +0 -148
  162. package/src/lib/utils/displacement-generator.ts +0 -92
  163. package/src/lib/utils/memoryMonitor.ts +0 -191
  164. package/src/styles/01-settings/_settings.testtypecheck.scss +0 -53
  165. package/src/styles/01-settings/_settings.typedbutton.scss +0 -53
  166. package/src/styles/06-components/_components.testbutton.scss +0 -212
  167. package/src/styles/06-components/_components.testtypecheck.scss +0 -212
  168. package/src/styles/06-components/_components.typedbutton.scss +0 -212
@@ -1,4 +1,4 @@
1
- import { useState, useRef, useEffect, RefObject } from 'react';
1
+ import { useState, useRef, useEffect, RefObject, useCallback } from 'react';
2
2
 
3
3
  type PopoverPosition = 'top' | 'bottom' | 'left' | 'right';
4
4
  type PopoverTrigger = 'click' | 'hover';
@@ -57,14 +57,17 @@ export const usePopover = ({
57
57
  const isOpenState = isControlled ? controlledIsOpen : isOpen;
58
58
 
59
59
  // Define setIsOpen function before using it in useEffect
60
- const setIsOpen = (newIsOpen: boolean) => {
61
- if (!isControlled) {
62
- setIsOpenState(newIsOpen);
63
- }
64
- if (onOpenChange) {
65
- onOpenChange(newIsOpen);
66
- }
67
- };
60
+ const setIsOpen = useCallback(
61
+ (newIsOpen: boolean) => {
62
+ if (!isControlled) {
63
+ setIsOpenState(newIsOpen);
64
+ }
65
+ if (onOpenChange) {
66
+ onOpenChange(newIsOpen);
67
+ }
68
+ },
69
+ [isControlled, onOpenChange]
70
+ );
68
71
 
69
72
  // Handle hover events if trigger is hover
70
73
  useEffect(() => {
@@ -115,141 +118,150 @@ export const usePopover = ({
115
118
  popoverRef.current.addEventListener('mouseenter', handlePopoverMouseEnter);
116
119
  popoverRef.current.addEventListener('mouseleave', handlePopoverMouseLeave);
117
120
 
121
+ const currentTrigger = triggerRef.current;
122
+ const currentPopover = popoverRef.current;
123
+
118
124
  return () => {
119
- if (triggerRef.current) {
120
- triggerRef.current.removeEventListener('mouseenter', handleTriggerMouseEnter);
121
- triggerRef.current.removeEventListener('mouseleave', handleTriggerMouseLeave);
125
+ if (currentTrigger) {
126
+ currentTrigger.removeEventListener('mouseenter', handleTriggerMouseEnter);
127
+ currentTrigger.removeEventListener('mouseleave', handleTriggerMouseLeave);
122
128
  }
123
- if (popoverRef.current) {
124
- popoverRef.current.removeEventListener('mouseenter', handlePopoverMouseEnter);
125
- popoverRef.current.removeEventListener('mouseleave', handlePopoverMouseLeave);
129
+ if (currentPopover) {
130
+ currentPopover.removeEventListener('mouseenter', handlePopoverMouseEnter);
131
+ currentPopover.removeEventListener('mouseleave', handlePopoverMouseLeave);
126
132
  }
127
133
  if (timeoutRef.current !== null) {
128
134
  window.clearTimeout(timeoutRef.current);
129
135
  }
130
136
  };
131
- }, [trigger, delay, isOpenState]);
132
-
133
- const updatePosition = (event?: Event) => {
134
- if (!triggerRef.current || !popoverRef.current) return;
135
-
136
- const triggerRect = triggerRef.current.getBoundingClientRect();
137
- const popoverRect = popoverRef.current.getBoundingClientRect();
138
- const viewportWidth = window.innerWidth;
139
- const viewportHeight = window.innerHeight;
140
-
141
- // Check if the trigger is near viewport edges
142
- const isNearViewportEdge =
143
- triggerRect.top < 50 ||
144
- triggerRect.bottom > viewportHeight - 50 ||
145
- triggerRect.left < 50 ||
146
- triggerRect.right > viewportWidth - 50;
147
-
148
- // If this is a scroll update and trigger isn't near edges, skip repositioning
149
- if (event?.type === 'scroll' && !isNearViewportEdge) {
150
- return;
151
- }
152
-
153
- // Calculate space available in each direction
154
- const spaceTop = triggerRect.top;
155
- const spaceBottom = viewportHeight - triggerRect.bottom;
156
- const spaceLeft = triggerRect.left;
157
- const spaceRight = viewportWidth - triggerRect.right;
158
-
159
- // Determine best position based on available space
160
- let bestPosition: PopoverPosition = position === 'auto' ? 'top' : (position as PopoverPosition);
161
-
162
- // If specified position is 'auto', find the position with most space
163
- if (position === 'auto') {
164
- const spaces = [
165
- { position: 'top', space: spaceTop },
166
- { position: 'right', space: spaceRight },
167
- { position: 'bottom', space: spaceBottom },
168
- { position: 'left', space: spaceLeft },
169
- ];
170
-
171
- // Sort by available space (descending)
172
- spaces.sort((a, b) => b.space - a.space);
173
-
174
- // Select position with most space
175
- bestPosition = spaces[0]?.position as PopoverPosition;
176
- } else {
177
- // Check if the preferred position has enough space
178
- const needsFlip =
179
- (position === 'top' &&
180
- spaceTop < popoverRect.height + offset &&
181
- spaceBottom >= popoverRect.height + offset) ||
182
- (position === 'bottom' &&
183
- spaceBottom < popoverRect.height + offset &&
184
- spaceTop >= popoverRect.height + offset) ||
185
- (position === 'left' &&
186
- spaceLeft < popoverRect.width + offset &&
187
- spaceRight >= popoverRect.width + offset) ||
188
- (position === 'right' &&
189
- spaceRight < popoverRect.width + offset &&
190
- spaceLeft >= popoverRect.width + offset);
191
-
192
- if (needsFlip) {
193
- // Flip to the opposite side
194
- const oppositePositions: Record<PopoverPosition | 'auto', PopoverPosition> = {
195
- top: 'bottom',
196
- bottom: 'top',
197
- left: 'right',
198
- right: 'left',
199
- auto: 'bottom',
200
- };
201
- bestPosition = oppositePositions[position as PopoverPosition | 'auto'];
137
+ }, [trigger, delay, isOpenState, setIsOpen]);
138
+
139
+ const updatePosition = useCallback(
140
+ (event?: Event) => {
141
+ if (!triggerRef.current || !popoverRef.current) return;
142
+
143
+ const triggerRect = triggerRef.current.getBoundingClientRect();
144
+ const popoverRect = popoverRef.current.getBoundingClientRect();
145
+ const viewportWidth = window.innerWidth;
146
+ const viewportHeight = window.innerHeight;
147
+
148
+ // Check if the trigger is near viewport edges
149
+ const isNearViewportEdge =
150
+ triggerRect.top < 50 ||
151
+ triggerRect.bottom > viewportHeight - 50 ||
152
+ triggerRect.left < 50 ||
153
+ triggerRect.right > viewportWidth - 50;
154
+
155
+ // If this is a scroll update and trigger isn't near edges, skip repositioning
156
+ if (event?.type === 'scroll' && !isNearViewportEdge) {
157
+ return;
202
158
  }
203
- }
204
-
205
- setCurrentPosition(bestPosition);
206
-
207
- // Calculate position based on the determined best position
208
- let top = 0;
209
- let left = 0;
210
-
211
- // Calculate viewport-relative position
212
- switch (bestPosition) {
213
- case 'top':
214
- top = triggerRect.top - popoverRect.height - offset;
215
- left = triggerRect.left + triggerRect.width / 2 - popoverRect.width / 2;
216
- break;
217
- case 'bottom':
218
- top = triggerRect.bottom + offset;
219
- left = triggerRect.left + triggerRect.width / 2 - popoverRect.width / 2;
220
- break;
221
- case 'left':
222
- top = triggerRect.top + triggerRect.height / 2 - popoverRect.height / 2;
223
- left = triggerRect.left - popoverRect.width - offset;
224
- break;
225
- case 'right':
226
- top = triggerRect.top + triggerRect.height / 2 - popoverRect.height / 2;
227
- left = triggerRect.right + offset;
228
- break;
229
- }
230
-
231
- // Constrain to viewport boundaries
232
- if (left < 0) {
233
- left = 5;
234
- } else if (left + popoverRect.width > viewportWidth) {
235
- left = viewportWidth - popoverRect.width - 5;
236
- }
237
-
238
- if (top < 0) {
239
- top = 5;
240
- } else if (top + popoverRect.height > viewportHeight) {
241
- top = viewportHeight - popoverRect.height - 5;
242
- }
243
-
244
- // Add scroll position to convert viewport coordinates to absolute position
245
- const absoluteTop = top + window.scrollY;
246
- const absoluteLeft = left + window.scrollX;
247
-
248
- // Apply position using absolute positioning to follow when scrolling
249
- popoverRef.current.style.position = 'absolute';
250
- popoverRef.current.style.top = `${absoluteTop}px`;
251
- popoverRef.current.style.left = `${absoluteLeft}px`;
252
- };
159
+
160
+ // Calculate space available in each direction
161
+ const spaceTop = triggerRect.top;
162
+ const spaceBottom = viewportHeight - triggerRect.bottom;
163
+ const spaceLeft = triggerRect.left;
164
+ const spaceRight = viewportWidth - triggerRect.right;
165
+
166
+ // Determine best position based on available space
167
+ let bestPosition: PopoverPosition =
168
+ position === 'auto' ? 'top' : (position as PopoverPosition);
169
+
170
+ // If specified position is 'auto', find the position with most space
171
+ if (position === 'auto') {
172
+ const spaces = [
173
+ { position: 'top', space: spaceTop },
174
+ { position: 'right', space: spaceRight },
175
+ { position: 'bottom', space: spaceBottom },
176
+ { position: 'left', space: spaceLeft },
177
+ ];
178
+
179
+ // Sort by available space (descending)
180
+ spaces.sort((a, b) => b.space - a.space);
181
+
182
+ // Select position with most space
183
+ bestPosition = spaces[0]?.position as PopoverPosition;
184
+ } else {
185
+ // Check if the preferred position has enough space
186
+ const needsFlip =
187
+ (position === 'top' &&
188
+ spaceTop < popoverRect.height + offset &&
189
+ spaceBottom >= popoverRect.height + offset) ||
190
+ (position === 'bottom' &&
191
+ spaceBottom < popoverRect.height + offset &&
192
+ spaceTop >= popoverRect.height + offset) ||
193
+ (position === 'left' &&
194
+ spaceLeft < popoverRect.width + offset &&
195
+ spaceRight >= popoverRect.width + offset) ||
196
+ (position === 'right' &&
197
+ spaceRight < popoverRect.width + offset &&
198
+ spaceLeft >= popoverRect.width + offset);
199
+
200
+ if (needsFlip) {
201
+ // Flip to the opposite side
202
+ const oppositePositions: Record<PopoverPosition | 'auto', PopoverPosition> = {
203
+ top: 'bottom',
204
+ bottom: 'top',
205
+ left: 'right',
206
+ right: 'left',
207
+ auto: 'bottom',
208
+ };
209
+ bestPosition = oppositePositions[position as PopoverPosition | 'auto'];
210
+ }
211
+ }
212
+
213
+ setCurrentPosition(bestPosition);
214
+
215
+ // Calculate position based on the determined best position
216
+ let top = 0;
217
+ let left = 0;
218
+
219
+ // Calculate viewport-relative position
220
+ switch (bestPosition) {
221
+ case 'top':
222
+ top = triggerRect.top - popoverRect.height - offset;
223
+ left = triggerRect.left + triggerRect.width / 2 - popoverRect.width / 2;
224
+ break;
225
+ case 'bottom':
226
+ top = triggerRect.bottom + offset;
227
+ left = triggerRect.left + triggerRect.width / 2 - popoverRect.width / 2;
228
+ break;
229
+ case 'left':
230
+ top = triggerRect.top + triggerRect.height / 2 - popoverRect.height / 2;
231
+ left = triggerRect.left - popoverRect.width - offset;
232
+ break;
233
+ case 'right':
234
+ top = triggerRect.top + triggerRect.height / 2 - popoverRect.height / 2;
235
+ left = triggerRect.right + offset;
236
+ break;
237
+ }
238
+
239
+ // Constrain to viewport boundaries
240
+ if (left < 0) {
241
+ left = 5;
242
+ } else if (left + popoverRect.width > viewportWidth) {
243
+ left = viewportWidth - popoverRect.width - 5;
244
+ }
245
+
246
+ if (top < 0) {
247
+ top = 5;
248
+ } else if (top + popoverRect.height > viewportHeight) {
249
+ top = viewportHeight - popoverRect.height - 5;
250
+ }
251
+
252
+ // Add scroll position to convert viewport coordinates to absolute position
253
+ const absoluteTop = top + window.scrollY;
254
+ const absoluteLeft = left + window.scrollX;
255
+
256
+ // Apply position using absolute positioning to follow when scrolling
257
+ if (popoverRef.current) {
258
+ popoverRef.current.style.position = 'absolute';
259
+ popoverRef.current.style.top = `${absoluteTop}px`;
260
+ popoverRef.current.style.left = `${absoluteLeft}px`;
261
+ }
262
+ },
263
+ [position, offset]
264
+ );
253
265
 
254
266
  // Position the popover
255
267
  useEffect(() => {
@@ -289,7 +301,7 @@ export const usePopover = ({
289
301
  }
290
302
  clearInterval(intervalId);
291
303
  };
292
- }, [isOpenState, position, offset]);
304
+ }, [isOpenState, updatePosition]);
293
305
 
294
306
  // Handle click outside to close popover
295
307
  useEffect(() => {
@@ -311,7 +323,7 @@ export const usePopover = ({
311
323
  return () => {
312
324
  document.removeEventListener('mousedown', handleClickOutside);
313
325
  };
314
- }, [isOpenState, closeOnClickOutside]);
326
+ }, [isOpenState, closeOnClickOutside, setIsOpen]);
315
327
 
316
328
  // Handle escape key to close popover
317
329
  useEffect(() => {
@@ -328,7 +340,7 @@ export const usePopover = ({
328
340
  return () => {
329
341
  document.removeEventListener('keydown', handleEscapeKey);
330
342
  };
331
- }, [isOpenState, closeOnEscape]);
343
+ }, [isOpenState, closeOnEscape, setIsOpen]);
332
344
 
333
345
  // Clean up on unmount
334
346
  useEffect(() => {
@@ -8,20 +8,18 @@ import { SIDE_MENU } from '../constants/components';
8
8
  * @returns SideMenu state and methods
9
9
  */
10
10
  export function useSideMenu(initialProps?: Partial<SideMenuProps>) {
11
- // Default side menu properties
12
- const defaultProps: Partial<SideMenuProps> = {
13
- collapsible: true,
14
- collapsibleDesktop: false,
15
- defaultCollapsedDesktop: false,
16
- isOpen: false,
17
- ...initialProps,
18
- };
11
+ const {
12
+ collapsible = true,
13
+ collapsibleDesktop = false,
14
+ defaultCollapsedDesktop = false,
15
+ isOpen,
16
+ onToggle,
17
+ disabled = false,
18
+ } = initialProps || {};
19
19
 
20
20
  // Local open state for when not controlled externally
21
21
  const [isOpenState, setIsOpenState] = useState(
22
- defaultProps.defaultCollapsedDesktop !== undefined
23
- ? !defaultProps.defaultCollapsedDesktop
24
- : defaultProps.isOpen || false
22
+ defaultCollapsedDesktop !== undefined ? !defaultCollapsedDesktop : isOpen || false
25
23
  );
26
24
 
27
25
  // Refs for managing responsive behavior
@@ -31,19 +29,18 @@ export function useSideMenu(initialProps?: Partial<SideMenuProps>) {
31
29
 
32
30
  // Update local state when external state changes
33
31
  useEffect(() => {
34
- if (typeof defaultProps.isOpen !== 'undefined') {
35
- setIsOpenState(defaultProps.isOpen);
36
- } else if (defaultProps.defaultCollapsedDesktop !== undefined) {
37
- setIsOpenState(!defaultProps.defaultCollapsedDesktop);
32
+ if (typeof isOpen !== 'undefined') {
33
+ setIsOpenState(isOpen);
34
+ } else if (defaultCollapsedDesktop !== undefined) {
35
+ setIsOpenState(!defaultCollapsedDesktop);
38
36
  }
39
- }, [defaultProps.isOpen, defaultProps.defaultCollapsedDesktop]);
37
+ }, [isOpen, defaultCollapsedDesktop]);
40
38
 
41
39
  // Set initial height on mount
42
40
  useEffect(() => {
43
41
  const isMobile = window.innerWidth < 768;
44
- const shouldCollapse = isMobile ? defaultProps.collapsible : defaultProps.collapsibleDesktop;
45
- const currentOpen =
46
- typeof defaultProps.isOpen !== 'undefined' ? defaultProps.isOpen : isOpenState;
42
+ const shouldCollapse = isMobile ? collapsible : collapsibleDesktop;
43
+ const currentOpen = typeof isOpen !== 'undefined' ? isOpen : isOpenState;
47
44
 
48
45
  if (shouldCollapse && wrapperRef.current && innerRef.current) {
49
46
  // Use setTimeout to ensure DOM is fully rendered
@@ -62,13 +59,13 @@ export function useSideMenu(initialProps?: Partial<SideMenuProps>) {
62
59
  wrapperRef.current.style.height = 'auto';
63
60
  }
64
61
  return undefined;
65
- }, []); // Only run on mount
62
+ }, [collapsible, collapsibleDesktop, isOpen, isOpenState]);
66
63
 
67
64
  // Handle responsive behavior - vertical collapse for both mobile and desktop
68
65
  useEffect(() => {
69
66
  const handleResize = () => {
70
67
  const isMobile = window.innerWidth < 768; // MD breakpoint
71
- const shouldCollapse = isMobile ? defaultProps.collapsible : defaultProps.collapsibleDesktop;
68
+ const shouldCollapse = isMobile ? collapsible : collapsibleDesktop;
72
69
 
73
70
  if (!shouldCollapse) {
74
71
  // Not collapsible - always show content
@@ -77,8 +74,7 @@ export function useSideMenu(initialProps?: Partial<SideMenuProps>) {
77
74
  }
78
75
  } else if (wrapperRef.current && innerRef.current) {
79
76
  // Set proper height for vertical animation (both mobile and desktop)
80
- const currentOpen =
81
- typeof defaultProps.isOpen !== 'undefined' ? defaultProps.isOpen : isOpenState;
77
+ const currentOpen = typeof isOpen !== 'undefined' ? isOpen : isOpenState;
82
78
 
83
79
  // Use requestAnimationFrame to ensure DOM is ready
84
80
  requestAnimationFrame(() => {
@@ -101,22 +97,15 @@ export function useSideMenu(initialProps?: Partial<SideMenuProps>) {
101
97
  clearTimeout(timeoutId);
102
98
  window.removeEventListener('resize', handleResize);
103
99
  };
104
- }, [
105
- defaultProps.collapsible,
106
- defaultProps.collapsibleDesktop,
107
- defaultProps.isOpen,
108
- defaultProps.onToggle,
109
- isOpenState,
110
- ]);
100
+ }, [collapsible, collapsibleDesktop, isOpen, onToggle, isOpenState]);
111
101
 
112
102
  // Update wrapper height when open state changes (both mobile and desktop)
113
103
  useEffect(() => {
114
104
  const isMobile = window.innerWidth < 768;
115
- const shouldCollapse = isMobile ? defaultProps.collapsible : defaultProps.collapsibleDesktop;
105
+ const shouldCollapse = isMobile ? collapsible : collapsibleDesktop;
116
106
 
117
107
  if (shouldCollapse && wrapperRef.current && innerRef.current) {
118
- const currentOpen =
119
- typeof defaultProps.isOpen !== 'undefined' ? defaultProps.isOpen : isOpenState;
108
+ const currentOpen = typeof isOpen !== 'undefined' ? isOpen : isOpenState;
120
109
 
121
110
  // Use requestAnimationFrame to ensure DOM is ready
122
111
  requestAnimationFrame(() => {
@@ -132,7 +121,7 @@ export function useSideMenu(initialProps?: Partial<SideMenuProps>) {
132
121
  // Not collapsible - always show content
133
122
  wrapperRef.current.style.height = 'auto';
134
123
  }
135
- }, [defaultProps.isOpen, isOpenState, defaultProps.collapsible, defaultProps.collapsibleDesktop]);
124
+ }, [isOpen, isOpenState, collapsible, collapsibleDesktop]);
136
125
 
137
126
  /**
138
127
  * Generate side menu class based on properties
@@ -159,14 +148,13 @@ export function useSideMenu(initialProps?: Partial<SideMenuProps>) {
159
148
  * Handle toggle click (mobile)
160
149
  */
161
150
  const handleToggle = () => {
162
- if (defaultProps.disabled) return;
151
+ if (disabled) return;
163
152
 
164
- const newState =
165
- typeof defaultProps.isOpen !== 'undefined' ? !defaultProps.isOpen : !isOpenState;
153
+ const newState = typeof isOpen !== 'undefined' ? !isOpen : !isOpenState;
166
154
 
167
- if (typeof defaultProps.onToggle === 'function') {
155
+ if (typeof onToggle === 'function') {
168
156
  // Controlled component
169
- defaultProps.onToggle(newState);
157
+ onToggle(newState);
170
158
  } else {
171
159
  // Uncontrolled component
172
160
  setIsOpenState(newState);
@@ -185,11 +173,10 @@ export function useSideMenu(initialProps?: Partial<SideMenuProps>) {
185
173
  * @returns Current open state
186
174
  */
187
175
  const getCurrentOpenState = (): boolean => {
188
- return typeof defaultProps.isOpen !== 'undefined' ? defaultProps.isOpen : isOpenState;
176
+ return typeof isOpen !== 'undefined' ? isOpen : isOpenState;
189
177
  };
190
178
 
191
179
  return {
192
- defaultProps,
193
180
  isOpenState: getCurrentOpenState(),
194
181
  wrapperRef,
195
182
  innerRef,
@@ -39,7 +39,7 @@ export function useSlider(options: UseSliderOptions): UseSliderReturn {
39
39
  onSlideChange,
40
40
  } = options;
41
41
 
42
- const slides = Array.isArray(rawSlides) ? rawSlides : [];
42
+ const slides = useMemo(() => (Array.isArray(rawSlides) ? rawSlides : []), [rawSlides]);
43
43
 
44
44
  const containerRef = useRef<HTMLDivElement | null>(null);
45
45
  const wrapperRef = useRef<HTMLDivElement | null>(null);
@@ -364,8 +364,6 @@ export function useSlider(options: UseSliderOptions): UseSliderReturn {
364
364
  isTransitioning,
365
365
  speed,
366
366
  onSlideChange,
367
- allSlides.length,
368
- loopedSlides,
369
367
  autoplay,
370
368
  ]);
371
369
 
@@ -426,8 +424,6 @@ export function useSlider(options: UseSliderOptions): UseSliderReturn {
426
424
  isTransitioning,
427
425
  speed,
428
426
  onSlideChange,
429
- allSlides.length,
430
- loopedSlides,
431
427
  autoplay,
432
428
  ]);
433
429
 
@@ -458,7 +454,7 @@ export function useSlider(options: UseSliderOptions): UseSliderReturn {
458
454
  onSlideChange?.(index);
459
455
  }, speed);
460
456
  },
461
- [realIndex, isTransitioning, speed, onSlideChange, loop, loopedSlides, autoplay]
457
+ [realIndex, isTransitioning, speed, onSlideChange, loop, slides.length, autoplay]
462
458
  );
463
459
 
464
460
  const handleTouchStart = useCallback(
@@ -28,8 +28,8 @@ interface UseTooltipResult {
28
28
  isVisible: boolean;
29
29
  isPositioned: boolean;
30
30
  tooltipId: string;
31
- triggerRef: RefObject<HTMLDivElement>;
32
- tooltipRef: RefObject<HTMLDivElement>;
31
+ triggerRef: RefObject<HTMLDivElement | null>;
32
+ tooltipRef: RefObject<HTMLDivElement | null>;
33
33
  tooltipStyle: React.CSSProperties;
34
34
  arrowStyle: React.CSSProperties;
35
35
  showTooltip: () => void;
@@ -206,6 +206,29 @@ export interface TokenEngineConfig {
206
206
  };
207
207
  }
208
208
 
209
+ /**
210
+ * CLI component generator defaults (merged before CLI flags; flags win).
211
+ */
212
+ export interface GeneratorConfig {
213
+ /** Default output directory for generated components */
214
+ outputPath?: string;
215
+ /** Override detected framework */
216
+ framework?: 'react' | 'next' | 'vanilla';
217
+ /** Per-feature defaults (CLI --no-* flags override) */
218
+ features?: {
219
+ storybook?: boolean;
220
+ hook?: boolean;
221
+ styles?: boolean;
222
+ tests?: boolean;
223
+ };
224
+ /** Composable hooks directory relative to project root */
225
+ hookOutputDir?: string;
226
+ /** Story file: side-effect import for global Atomix styles */
227
+ storybookCssImport?: string;
228
+ /** Barrel file strategy for new components */
229
+ barrel?: 'index' | 'none';
230
+ }
231
+
209
232
  /**
210
233
  * Atomix Configuration Interface
211
234
  *
@@ -241,6 +264,17 @@ export interface AtomixConfig {
241
264
  model?: string;
242
265
  /** API key for the provider */
243
266
  apiKey?: string;
267
+ /** Temperature for AI creativity (0.0-1.0, default: 0.7) */
268
+ temperature?: number;
269
+ /** Maximum tokens per AI response (default: 4000) */
270
+ maxTokens?: number;
271
+ /** Rate limiting configuration */
272
+ rateLimit?: {
273
+ /** Maximum requests allowed */
274
+ requests: number;
275
+ /** Time window in milliseconds */
276
+ windowMs: number;
277
+ };
244
278
  };
245
279
 
246
280
  /**
@@ -255,6 +289,11 @@ export interface AtomixConfig {
255
289
  anonymize?: boolean;
256
290
  };
257
291
 
292
+ /**
293
+ * `atomix generate` defaults (CLI overrides these)
294
+ */
295
+ generator?: GeneratorConfig;
296
+
258
297
  /**
259
298
  * Theme customization (Tailwind-like)
260
299
  *
@@ -1631,6 +1631,7 @@ export const ATOMIX_GLASS = {
1631
1631
  FILTER_OVERLAY_CLASS: 'c-atomix-glass__filter-overlay',
1632
1632
  FILTER_SHADOW_CLASS: 'c-atomix-glass__filter-shadow',
1633
1633
  CONTENT_CLASS: 'c-atomix-glass__content',
1634
+ BORDER_BACKDROP_CLASS: 'c-atomix-glass__border-backdrop',
1634
1635
  BORDER_1_CLASS: 'c-atomix-glass__border-1',
1635
1636
  BORDER_2_CLASS: 'c-atomix-glass__border-2',
1636
1637
  HOVER_1_CLASS: 'c-atomix-glass__hover-1',
@@ -48,7 +48,7 @@ export const ThemeComparator: React.FC<ThemeComparatorProps> = ({
48
48
  const [debouncedSearchQuery, setDebouncedSearchQuery] = useState<string>('');
49
49
  const [filterType, setFilterType] = useState<'all' | 'added' | 'removed' | 'changed'>('all');
50
50
  const [filterCategory, setFilterCategory] = useState<string>('all');
51
- const searchTimeoutRef = useRef<NodeJS.Timeout>();
51
+ const searchTimeoutRef = useRef<NodeJS.Timeout | undefined>(undefined);
52
52
 
53
53
  // Debounce search query
54
54
  useEffect(() => {
@@ -57,7 +57,7 @@ export const ThemeInspector: React.FC<ThemeInspectorProps> = ({
57
57
  const [searchQuery, setSearchQuery] = useState<string>('');
58
58
  const [debouncedSearchQuery, setDebouncedSearchQuery] = useState<string>('');
59
59
  const [copiedPath, setCopiedPath] = useState<string | null>(null);
60
- const searchTimeoutRef = useRef<NodeJS.Timeout>();
60
+ const searchTimeoutRef = useRef<NodeJS.Timeout | undefined>(undefined);
61
61
 
62
62
  // Debounce search query
63
63
  useEffect(() => {
@@ -200,7 +200,7 @@ export const ThemeLiveEditor: React.FC<ThemeLiveEditorProps> = ({
200
200
  );
201
201
 
202
202
  // Debounced JSON update to history
203
- const jsonUpdateTimeoutRef = useRef<NodeJS.Timeout>();
203
+ const jsonUpdateTimeoutRef = useRef<NodeJS.Timeout | undefined>(undefined);
204
204
  useEffect(() => {
205
205
  if (error) return;
206
206
 
@@ -91,7 +91,7 @@ export const ThemeProvider: React.FC<ThemeProviderProps> = ({
91
91
 
92
92
  // Default fallback
93
93
  return 'default';
94
- }, [defaultTheme, enablePersistence, storageKey]);
94
+ }, [defaultTheme, enablePersistence, storageKey, storageAdapter]);
95
95
 
96
96
  // Initialize state - handle both string and DesignTokens for defaultTheme
97
97
  const [currentTheme, setCurrentTheme] = useState<string>(() => {
@@ -232,6 +232,7 @@ export interface AtomixGlassProps extends React.HTMLAttributes<HTMLDivElement> {
232
232
  overLight?: OverLightConfig;
233
233
  mode?: DisplacementMode;
234
234
  onClick?: () => void;
235
+ isFixedOrSticky?: boolean;
235
236
 
236
237
  /**
237
238
  * Shader variant for shader mode
@@ -13,6 +13,7 @@
13
13
  // Component settings
14
14
  @forward './settings.accordion';
15
15
  @forward './settings.animations';
16
+ @forward './settings.atomix-glass';
16
17
  @forward './settings.avatar-group';
17
18
  @forward './settings.avatar';
18
19
  @forward './settings.badge';