@shohojdhara/atomix 0.4.1 → 0.4.3

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 (131) hide show
  1. package/dist/atomix.css +9351 -9259
  2. package/dist/atomix.css.map +1 -1
  3. package/dist/atomix.min.css +4 -4
  4. package/dist/atomix.min.css.map +1 -1
  5. package/dist/charts.d.ts +12 -19
  6. package/dist/charts.js +555 -358
  7. package/dist/charts.js.map +1 -1
  8. package/dist/core.d.ts +21 -24
  9. package/dist/core.js +435 -265
  10. package/dist/core.js.map +1 -1
  11. package/dist/forms.d.ts +11 -18
  12. package/dist/forms.js +411 -257
  13. package/dist/forms.js.map +1 -1
  14. package/dist/heavy.d.ts +14 -21
  15. package/dist/heavy.js +409 -254
  16. package/dist/heavy.js.map +1 -1
  17. package/dist/index.d.ts +38 -41
  18. package/dist/index.esm.js +731 -487
  19. package/dist/index.esm.js.map +1 -1
  20. package/dist/index.js +733 -492
  21. package/dist/index.js.map +1 -1
  22. package/dist/index.min.js +1 -1
  23. package/dist/index.min.js.map +1 -1
  24. package/package.json +1 -1
  25. package/scripts/atomix-cli.js +34 -1
  26. package/src/components/AtomixGlass/AtomixGlass.tsx +82 -54
  27. package/src/components/AtomixGlass/AtomixGlassContainer.tsx +17 -18
  28. package/src/components/AtomixGlass/README.md +5 -5
  29. package/src/components/AtomixGlass/stories/Customization.stories.tsx +2 -2
  30. package/src/components/AtomixGlass/stories/Examples.stories.tsx +42 -42
  31. package/src/components/AtomixGlass/stories/Modes.stories.tsx +5 -5
  32. package/src/components/AtomixGlass/stories/Overview.stories.tsx +3 -3
  33. package/src/components/AtomixGlass/stories/Performance.stories.tsx +2 -2
  34. package/src/components/AtomixGlass/stories/Playground.stories.tsx +45 -45
  35. package/src/components/AtomixGlass/stories/Shaders.stories.tsx +3 -3
  36. package/src/components/Badge/Badge.stories.tsx +1 -1
  37. package/src/components/Badge/Badge.tsx +1 -1
  38. package/src/components/Breadcrumb/Breadcrumb.tsx +90 -77
  39. package/src/components/Breadcrumb/index.ts +2 -2
  40. package/src/components/Button/Button.stories.tsx +1 -1
  41. package/src/components/Button/Button.tsx +2 -1
  42. package/src/components/Button/README.md +2 -2
  43. package/src/components/Callout/Callout.test.tsx +3 -3
  44. package/src/components/Callout/Callout.tsx +2 -2
  45. package/src/components/Callout/README.md +2 -2
  46. package/src/components/Card/Card.tsx +31 -11
  47. package/src/components/Chart/Chart.stories.tsx +1 -1
  48. package/src/components/Chart/Chart.tsx +5 -5
  49. package/src/components/Chart/TreemapChart.tsx +37 -29
  50. package/src/components/DatePicker/readme.md +3 -3
  51. package/src/components/Dropdown/Dropdown.stories.tsx +1 -1
  52. package/src/components/Dropdown/Dropdown.tsx +276 -273
  53. package/src/components/EdgePanel/EdgePanel.stories.tsx +7 -7
  54. package/src/components/Footer/FooterLink.tsx +2 -2
  55. package/src/components/Form/Checkbox.stories.tsx +1 -1
  56. package/src/components/Form/Checkbox.tsx +1 -1
  57. package/src/components/Form/Input.stories.tsx +1 -1
  58. package/src/components/Form/Input.tsx +1 -1
  59. package/src/components/Form/Radio.stories.tsx +1 -1
  60. package/src/components/Form/Radio.tsx +1 -1
  61. package/src/components/Form/Select.stories.tsx +1 -1
  62. package/src/components/Form/Select.tsx +1 -1
  63. package/src/components/Form/Textarea.stories.tsx +1 -1
  64. package/src/components/Form/Textarea.tsx +1 -1
  65. package/src/components/Hero/Hero.stories.tsx +2 -2
  66. package/src/components/Hero/Hero.tsx +2 -2
  67. package/src/components/Messages/Messages.stories.tsx +1 -1
  68. package/src/components/Messages/Messages.tsx +2 -2
  69. package/src/components/Modal/Modal.stories.tsx +1 -1
  70. package/src/components/Navigation/Nav/Nav.stories.tsx +2 -2
  71. package/src/components/Navigation/Nav/Nav.tsx +1 -1
  72. package/src/components/Navigation/Nav/NavItem.tsx +6 -3
  73. package/src/components/Navigation/Navbar/Navbar.stories.tsx +3 -3
  74. package/src/components/Navigation/Navbar/Navbar.tsx +1 -1
  75. package/src/components/Navigation/SideMenu/SideMenu.stories.tsx +2 -2
  76. package/src/components/Navigation/SideMenu/SideMenu.tsx +1 -1
  77. package/src/components/Pagination/Pagination.stories.tsx +1 -1
  78. package/src/components/Pagination/Pagination.tsx +1 -1
  79. package/src/components/Popover/Popover.stories.tsx +1 -1
  80. package/src/components/Popover/Popover.tsx +1 -1
  81. package/src/components/Progress/Progress.tsx +1 -1
  82. package/src/components/Rating/Rating.stories.tsx +1 -1
  83. package/src/components/Rating/Rating.test.tsx +73 -0
  84. package/src/components/Rating/Rating.tsx +25 -37
  85. package/src/components/Spinner/Spinner.tsx +1 -1
  86. package/src/components/Steps/Steps.stories.tsx +1 -1
  87. package/src/components/Steps/Steps.tsx +2 -2
  88. package/src/components/Tabs/Tabs.stories.tsx +1 -1
  89. package/src/components/Tabs/Tabs.tsx +1 -1
  90. package/src/components/Todo/Todo.tsx +0 -1
  91. package/src/components/Toggle/Toggle.stories.tsx +1 -1
  92. package/src/components/Toggle/Toggle.tsx +1 -1
  93. package/src/components/Tooltip/Tooltip.stories.tsx +1 -1
  94. package/src/components/VideoPlayer/VideoPlayer.stories.tsx +2 -2
  95. package/src/lib/composables/__tests__/useAtomixGlassPerf.test.tsx +88 -0
  96. package/src/lib/composables/__tests__/useChart.test.ts +50 -0
  97. package/src/lib/composables/__tests__/useChart.test.tsx +139 -0
  98. package/src/lib/composables/__tests__/useHeroBackgroundSlider.test.tsx +59 -0
  99. package/src/lib/composables/__tests__/useSliderAutoplay.test.tsx +68 -0
  100. package/src/lib/composables/atomix-glass/useGlassBackgroundDetection.ts +329 -0
  101. package/src/lib/composables/atomix-glass/useGlassCornerRadius.ts +82 -0
  102. package/src/lib/composables/atomix-glass/useGlassMouseTracking.ts +153 -0
  103. package/src/lib/composables/atomix-glass/useGlassOverLight.ts +198 -0
  104. package/src/lib/composables/atomix-glass/useGlassSize.ts +117 -0
  105. package/src/lib/composables/atomix-glass/useGlassState.ts +112 -0
  106. package/src/lib/composables/atomix-glass/useGlassTransforms.ts +160 -0
  107. package/src/lib/composables/glass-styles.ts +302 -0
  108. package/src/lib/composables/index.ts +0 -4
  109. package/src/lib/composables/useAtomixGlass.ts +331 -522
  110. package/src/lib/composables/useAtomixGlassStyles.ts +307 -0
  111. package/src/lib/composables/useBarChart.ts +1 -1
  112. package/src/lib/composables/useBreadcrumb.ts +6 -6
  113. package/src/lib/composables/useChart.ts +104 -21
  114. package/src/lib/composables/useHeroBackgroundSlider.ts +16 -7
  115. package/src/lib/composables/useSlider.ts +66 -34
  116. package/src/lib/theme/devtools/CLI.ts +1 -1
  117. package/src/lib/theme/utils/__tests__/themeUtils.test.ts +213 -0
  118. package/src/lib/types/components.ts +18 -21
  119. package/src/lib/utils/__tests__/dom.test.ts +100 -0
  120. package/src/lib/utils/__tests__/fontPreloader.test.ts +102 -0
  121. package/src/styles/02-tools/_tools.breakpoints.scss +1 -1
  122. package/src/styles/02-tools/_tools.utility-api.scss +6 -6
  123. package/src/styles/06-components/_components.accordion.scss +0 -2
  124. package/src/styles/06-components/_components.chart.scss +0 -1
  125. package/src/styles/06-components/_components.dropdown.scss +0 -1
  126. package/src/styles/06-components/_components.edge-panel.scss +0 -2
  127. package/src/styles/06-components/_components.photoviewer.scss +0 -1
  128. package/src/styles/06-components/_components.river.scss +0 -1
  129. package/src/styles/06-components/_components.slider.scss +0 -3
  130. package/src/styles/99-utilities/_utilities.glass-fixes.scss +0 -1
  131. package/src/styles/99-utilities/_utilities.text.scss +1 -0
@@ -69,15 +69,15 @@ export const DropdownTrigger = forwardRef<HTMLDivElement, React.HTMLAttributes<H
69
69
  // If we use <Dropdown.Trigger><Button/></Dropdown.Trigger>, we want the Button to be the trigger.
70
70
 
71
71
  return (
72
- <div
73
- ref={ref}
74
- className={`c-dropdown__toggle ${className}`.trim()}
75
- onClick={onClick}
76
- onKeyDown={onKeyDown}
77
- {...props}
78
- >
79
- {children}
80
- </div>
72
+ <div
73
+ ref={ref}
74
+ className={`c-dropdown__toggle ${className}`.trim()}
75
+ onClick={onClick}
76
+ onKeyDown={onKeyDown}
77
+ {...props}
78
+ >
79
+ {children}
80
+ </div>
81
81
  );
82
82
  }
83
83
  );
@@ -125,6 +125,7 @@ export const DropdownItem: React.FC<DropdownItemProps> = memo(
125
125
 
126
126
  const linkProps = {
127
127
  href,
128
+ to: href,
128
129
  className: itemClasses,
129
130
  onClick: handleClick,
130
131
  role: 'menuitem',
@@ -173,6 +174,7 @@ export const DropdownItem: React.FC<DropdownItemProps> = memo(
173
174
  );
174
175
  }
175
176
  );
177
+ DropdownItem.displayName = 'DropdownItem';
176
178
 
177
179
  /**
178
180
  * DropdownDivider component for separating groups of items
@@ -180,6 +182,7 @@ export const DropdownItem: React.FC<DropdownItemProps> = memo(
180
182
  export const DropdownDivider: React.FC<DropdownDividerProps> = memo(({ className = '' }) => {
181
183
  return <li className={`c-dropdown__divider ${className}`} role="separator" />;
182
184
  });
185
+ DropdownDivider.displayName = 'DropdownDivider';
183
186
 
184
187
  /**
185
188
  * DropdownHeader component for section headers
@@ -189,6 +192,7 @@ export const DropdownHeader: React.FC<DropdownHeaderProps> = memo(
189
192
  return <li className={`c-dropdown__header ${className}`}>{children}</li>;
190
193
  }
191
194
  );
195
+ DropdownHeader.displayName = 'DropdownHeader';
192
196
 
193
197
  // Helper context to pass glass prop to DropdownMenu
194
198
  const DropdownStyleContext = createContext<{ glass?: AtomixGlassProps | boolean }>({});
@@ -204,298 +208,297 @@ type DropdownComponent = React.FC<DropdownProps> & {
204
208
  Header: typeof DropdownHeader;
205
209
  };
206
210
 
207
- export const Dropdown: DropdownComponent = memo(
208
- ({
209
- children,
210
- menu,
211
- placement = 'bottom-start',
212
- trigger = 'click',
213
- offset = DROPDOWN.DEFAULTS.OFFSET,
214
- isOpen: controlledIsOpen,
215
- onOpenChange,
216
- closeOnClickOutside = true,
217
- closeOnEscape = true,
218
- maxHeight,
219
- minWidth = DROPDOWN.DEFAULTS.MIN_WIDTH,
220
- variant,
221
- className = '',
222
- style,
223
- glass,
224
- ...props
225
- }: DropdownProps) => {
226
- // Set up controlled vs uncontrolled state
227
- const [uncontrolledIsOpen, setUncontrolledIsOpen] = useState(false);
228
- const isControlled = controlledIsOpen !== undefined;
229
- const isOpen = isControlled ? controlledIsOpen : uncontrolledIsOpen;
230
-
231
- // Create refs
232
- const dropdownRef = useRef<HTMLDivElement>(null);
233
- const toggleRef = useRef<HTMLDivElement>(null);
234
- const menuRef = useRef<HTMLDivElement>(null);
235
-
236
- // Generate unique ID
237
- const dropdownId = useRef(`dropdown-${Math.random().toString(36).substring(2, 9)}`).current;
238
-
239
- // State change handlers
240
- const setIsOpen = useCallback(
241
- (nextIsOpen: boolean) => {
242
- if (!isControlled) {
243
- setUncontrolledIsOpen(nextIsOpen);
244
- }
245
- if (onOpenChange) {
246
- onOpenChange(nextIsOpen);
247
- }
248
- },
249
- [isControlled, onOpenChange]
250
- );
251
-
252
- const toggle = useCallback(() => setIsOpen(!isOpen), [isOpen, setIsOpen]);
253
-
254
- const close = useCallback(() => {
255
- setIsOpen(false);
256
- // Return focus to the toggle button after closing
257
- setTimeout(() => {
258
- toggleRef.current?.focus();
259
- }, 0);
260
- }, [setIsOpen]);
261
-
262
- // Click outside handler
263
- useEffect(() => {
264
- if (!isOpen || !closeOnClickOutside) return undefined;
265
-
266
- const handleClickOutside = (e: MouseEvent) => {
267
- if (dropdownRef.current && !dropdownRef.current.contains(e.target as Node)) {
268
- close();
269
- }
270
- };
211
+ export const Dropdown: DropdownComponent = memo(function DropdownBase({
212
+ children,
213
+ menu,
214
+ placement = 'bottom-start',
215
+ trigger = 'click',
216
+ offset = DROPDOWN.DEFAULTS.OFFSET,
217
+ isOpen: controlledIsOpen,
218
+ onOpenChange,
219
+ closeOnClickOutside = true,
220
+ closeOnEscape = true,
221
+ maxHeight,
222
+ minWidth = DROPDOWN.DEFAULTS.MIN_WIDTH,
223
+ variant,
224
+ className = '',
225
+ style,
226
+ glass,
227
+ ...props
228
+ }: DropdownProps) {
229
+ // Set up controlled vs uncontrolled state
230
+ const [uncontrolledIsOpen, setUncontrolledIsOpen] = useState(false);
231
+ const isControlled = controlledIsOpen !== undefined;
232
+ const isOpen = isControlled ? controlledIsOpen : uncontrolledIsOpen;
233
+
234
+ // Create refs
235
+ const dropdownRef = useRef<HTMLDivElement>(null);
236
+ const toggleRef = useRef<HTMLDivElement>(null);
237
+ const menuRef = useRef<HTMLDivElement>(null);
238
+
239
+ // Generate unique ID
240
+ const dropdownId = useRef(`dropdown-${Math.random().toString(36).substring(2, 9)}`).current;
241
+
242
+ // State change handlers
243
+ const setIsOpen = useCallback(
244
+ (nextIsOpen: boolean) => {
245
+ if (!isControlled) {
246
+ setUncontrolledIsOpen(nextIsOpen);
247
+ }
248
+ if (onOpenChange) {
249
+ onOpenChange(nextIsOpen);
250
+ }
251
+ },
252
+ [isControlled, onOpenChange]
253
+ );
254
+
255
+ const toggle = useCallback(() => setIsOpen(!isOpen), [isOpen, setIsOpen]);
256
+
257
+ const close = useCallback(() => {
258
+ setIsOpen(false);
259
+ // Return focus to the toggle button after closing
260
+ setTimeout(() => {
261
+ toggleRef.current?.focus();
262
+ }, 0);
263
+ }, [setIsOpen]);
264
+
265
+ // Click outside handler
266
+ useEffect(() => {
267
+ if (!isOpen || !closeOnClickOutside) return undefined;
268
+
269
+ const handleClickOutside = (e: MouseEvent) => {
270
+ if (dropdownRef.current && !dropdownRef.current.contains(e.target as Node)) {
271
+ close();
272
+ }
273
+ };
271
274
 
272
- document.addEventListener('mousedown', handleClickOutside);
273
- return () => document.removeEventListener('mousedown', handleClickOutside);
274
- }, [isOpen, closeOnClickOutside, close]);
275
+ document.addEventListener('mousedown', handleClickOutside);
276
+ return () => document.removeEventListener('mousedown', handleClickOutside);
277
+ }, [isOpen, closeOnClickOutside, close]);
275
278
 
276
- // Escape key handler
277
- useEffect(() => {
278
- if (!isOpen || !closeOnEscape) return undefined;
279
+ // Escape key handler
280
+ useEffect(() => {
281
+ if (!isOpen || !closeOnEscape) return undefined;
279
282
 
280
- const handleKeyDown = (e: KeyboardEvent) => {
281
- if (e.key === 'Escape') {
282
- close();
283
- }
284
- };
283
+ const handleKeyDown = (e: KeyboardEvent) => {
284
+ if (e.key === 'Escape') {
285
+ close();
286
+ }
287
+ };
285
288
 
286
- document.addEventListener('keydown', handleKeyDown);
287
- return () => document.removeEventListener('keydown', handleKeyDown);
288
- }, [isOpen, closeOnEscape, close]);
289
+ document.addEventListener('keydown', handleKeyDown);
290
+ return () => document.removeEventListener('keydown', handleKeyDown);
291
+ }, [isOpen, closeOnEscape, close]);
289
292
 
290
- // Keyboard navigation
291
- const handleKeyDown = useCallback((e: React.KeyboardEvent) => {
292
- if (!menuRef.current) return;
293
+ // Keyboard navigation
294
+ const handleKeyDown = useCallback((e: React.KeyboardEvent) => {
295
+ if (!menuRef.current) return;
293
296
 
294
- const focusableItems = menuRef.current.querySelectorAll<HTMLElement>(
295
- '[role="menuitem"]:not([disabled])'
296
- );
297
- if (!focusableItems.length) return;
297
+ const focusableItems = menuRef.current.querySelectorAll<HTMLElement>(
298
+ '[role="menuitem"]:not([disabled])'
299
+ );
300
+ if (!focusableItems.length) return;
298
301
 
299
- const currentIndex = Array.from(focusableItems).findIndex(
300
- item => item === document.activeElement
301
- );
302
+ const currentIndex = Array.from(focusableItems).findIndex(
303
+ item => item === document.activeElement
304
+ );
302
305
 
303
- switch (e.key) {
304
- case 'ArrowDown':
305
- e.preventDefault();
306
- if (currentIndex < focusableItems.length - 1) {
307
- focusableItems[currentIndex + 1]?.focus();
308
- } else {
309
- focusableItems[0]?.focus();
310
- }
311
- break;
312
-
313
- case 'ArrowUp':
314
- e.preventDefault();
315
- if (currentIndex > 0) {
316
- focusableItems[currentIndex - 1]?.focus();
317
- } else {
318
- focusableItems[focusableItems.length - 1]?.focus();
319
- }
320
- break;
321
-
322
- case 'Home':
323
- e.preventDefault();
306
+ switch (e.key) {
307
+ case 'ArrowDown':
308
+ e.preventDefault();
309
+ if (currentIndex < focusableItems.length - 1) {
310
+ focusableItems[currentIndex + 1]?.focus();
311
+ } else {
324
312
  focusableItems[0]?.focus();
325
- break;
313
+ }
314
+ break;
326
315
 
327
- case 'End':
328
- e.preventDefault();
316
+ case 'ArrowUp':
317
+ e.preventDefault();
318
+ if (currentIndex > 0) {
319
+ focusableItems[currentIndex - 1]?.focus();
320
+ } else {
329
321
  focusableItems[focusableItems.length - 1]?.focus();
330
- break;
331
- }
332
- }, []);
333
-
334
- // Event handlers
335
- const handleToggleClick = useCallback(
336
- (e: React.MouseEvent) => {
337
- if (trigger === 'click') {
338
- e.preventDefault();
339
- e.stopPropagation();
340
- toggle();
341
322
  }
342
- },
343
- [trigger, toggle]
344
- );
323
+ break;
345
324
 
346
- const handleToggleKeyDown = useCallback(
347
- (e: React.KeyboardEvent) => {
348
- if ((e.key === 'Enter' || e.key === ' ' || e.key === 'ArrowDown') && !isOpen) {
349
- e.preventDefault();
350
- setIsOpen(true);
351
-
352
- // Only focus the first menu item when using keyboard navigation
353
- if (e.key === 'ArrowDown' && menuRef.current) {
354
- setTimeout(() => {
355
- const firstItem = menuRef.current?.querySelector<HTMLElement>('[role="menuitem"]');
356
- firstItem?.focus();
357
- }, 100);
358
- }
359
- } else if (e.key === 'Escape' && isOpen) {
360
- e.preventDefault();
361
- close();
362
- }
363
- },
364
- [isOpen, setIsOpen, close]
365
- );
325
+ case 'Home':
326
+ e.preventDefault();
327
+ focusableItems[0]?.focus();
328
+ break;
366
329
 
367
- // Hover handlers for trigger="hover"
368
- const handleHoverOpen = useCallback(() => {
369
- if (trigger === 'hover') {
370
- setIsOpen(true);
330
+ case 'End':
331
+ e.preventDefault();
332
+ focusableItems[focusableItems.length - 1]?.focus();
333
+ break;
334
+ }
335
+ }, []);
336
+
337
+ // Event handlers
338
+ const handleToggleClick = useCallback(
339
+ (e: React.MouseEvent) => {
340
+ if (trigger === 'click') {
341
+ e.preventDefault();
342
+ e.stopPropagation();
343
+ toggle();
371
344
  }
372
- }, [trigger, setIsOpen]);
373
-
374
- // Build class names
375
- const dropdownClasses = [
376
- 'c-dropdown',
377
- trigger === 'click' ? 'c-dropdown--onclick' : '',
378
- variant ? `c-dropdown--${variant}` : '',
379
- isOpen ? 'is-open' : '',
380
- glass ? 'c-dropdown--glass' : '',
381
- className,
382
- ]
383
- .filter(Boolean)
384
- .join(' ');
345
+ },
346
+ [trigger, toggle]
347
+ );
348
+
349
+ const handleToggleKeyDown = useCallback(
350
+ (e: React.KeyboardEvent) => {
351
+ if ((e.key === 'Enter' || e.key === ' ' || e.key === 'ArrowDown') && !isOpen) {
352
+ e.preventDefault();
353
+ setIsOpen(true);
385
354
 
386
- // Menu styles
387
- const menuStyleProps: React.CSSProperties = {};
388
- if (maxHeight) menuStyleProps.maxHeight = maxHeight;
389
- if (minWidth !== undefined) {
390
- menuStyleProps.minWidth = typeof minWidth === 'number' ? `${minWidth}px` : minWidth;
355
+ // Only focus the first menu item when using keyboard navigation
356
+ if (e.key === 'ArrowDown' && menuRef.current) {
357
+ setTimeout(() => {
358
+ const firstItem = menuRef.current?.querySelector<HTMLElement>('[role="menuitem"]');
359
+ firstItem?.focus();
360
+ }, 100);
361
+ }
362
+ } else if (e.key === 'Escape' && isOpen) {
363
+ e.preventDefault();
364
+ close();
365
+ }
366
+ },
367
+ [isOpen, setIsOpen, close]
368
+ );
369
+
370
+ // Hover handlers for trigger="hover"
371
+ const handleHoverOpen = useCallback(() => {
372
+ if (trigger === 'hover') {
373
+ setIsOpen(true);
391
374
  }
375
+ }, [trigger, setIsOpen]);
376
+
377
+ // Build class names
378
+ const dropdownClasses = [
379
+ 'c-dropdown',
380
+ trigger === 'click' ? 'c-dropdown--onclick' : '',
381
+ variant ? `c-dropdown--${variant}` : '',
382
+ isOpen ? 'is-open' : '',
383
+ glass ? 'c-dropdown--glass' : '',
384
+ className,
385
+ ]
386
+ .filter(Boolean)
387
+ .join(' ');
388
+
389
+ // Menu styles
390
+ const menuStyleProps: React.CSSProperties = {};
391
+ if (maxHeight) menuStyleProps.maxHeight = maxHeight;
392
+ if (minWidth !== undefined) {
393
+ menuStyleProps.minWidth = typeof minWidth === 'number' ? `${minWidth}px` : minWidth;
394
+ }
392
395
 
393
- // Determine content structure
394
- // Legacy: menu prop + children as trigger
395
- // Compound: children contains Trigger and Menu
396
+ // Determine content structure
397
+ // Legacy: menu prop + children as trigger
398
+ // Compound: children contains Trigger and Menu
396
399
 
397
- const hasCompoundComponents = React.Children.toArray(children).some((child) =>
400
+ const hasCompoundComponents = React.Children.toArray(children).some(
401
+ child =>
398
402
  React.isValidElement(child) &&
399
403
  ['DropdownTrigger', 'DropdownMenu'].includes((child.type as any).displayName)
400
- );
401
-
402
- let triggerContent: ReactNode;
403
- let menuContentNode: ReactNode;
404
-
405
- if (hasCompoundComponents) {
406
- // Find Trigger and Menu in children
407
- React.Children.forEach(children, (child) => {
408
- if (React.isValidElement(child)) {
409
- if ((child.type as any).displayName === 'DropdownTrigger') {
410
- triggerContent = React.cloneElement(child, {
411
- ref: toggleRef,
412
- onClick: (e: React.MouseEvent) => {
413
- handleToggleClick(e);
414
- (child.props as any).onClick?.(e);
415
- },
416
- onKeyDown: (e: React.KeyboardEvent) => {
417
- handleToggleKeyDown(e);
418
- (child.props as any).onKeyDown?.(e);
419
- },
420
- 'aria-haspopup': 'menu',
421
- 'aria-expanded': isOpen,
422
- 'aria-controls': dropdownId,
423
- tabIndex: 0,
424
- } as any);
425
- } else if ((child.type as any).displayName === 'DropdownMenu') {
426
- menuContentNode = child;
427
- }
404
+ );
405
+
406
+ let triggerContent: ReactNode;
407
+ let menuContentNode: ReactNode;
408
+
409
+ if (hasCompoundComponents) {
410
+ // Find Trigger and Menu in children
411
+ React.Children.forEach(children, child => {
412
+ if (React.isValidElement(child)) {
413
+ if ((child.type as any).displayName === 'DropdownTrigger') {
414
+ triggerContent = React.cloneElement(child, {
415
+ ref: toggleRef,
416
+ onClick: (e: React.MouseEvent) => {
417
+ handleToggleClick(e);
418
+ (child.props as any).onClick?.(e);
419
+ },
420
+ onKeyDown: (e: React.KeyboardEvent) => {
421
+ handleToggleKeyDown(e);
422
+ (child.props as any).onKeyDown?.(e);
423
+ },
424
+ 'aria-haspopup': 'menu',
425
+ 'aria-expanded': isOpen,
426
+ 'aria-controls': dropdownId,
427
+ tabIndex: 0,
428
+ } as any);
429
+ } else if ((child.type as any).displayName === 'DropdownMenu') {
430
+ menuContentNode = child;
428
431
  }
429
- });
430
- } else {
431
- // Legacy mode
432
- triggerContent = (
433
- <div
434
- ref={toggleRef}
435
- className="c-dropdown__toggle"
436
- onClick={handleToggleClick}
437
- onKeyDown={handleToggleKeyDown}
438
- aria-haspopup="menu"
439
- aria-expanded={isOpen}
440
- aria-controls={dropdownId}
441
- tabIndex={0}
442
- >
443
- {children}
444
- </div>
445
- );
446
- menuContentNode = (
447
- <ul className={`c-dropdown__menu ${glass ? 'c-dropdown__menu--glass' : ''}`}>{menu}</ul>
448
- );
449
- }
450
-
451
- const menuContent = (
452
- <div className="c-dropdown__menu-inner" style={menuStyleProps}>
453
- <DropdownStyleContext.Provider value={{ glass }}>
454
- <DropdownContext.Provider value={{ isOpen, close, id: dropdownId, trigger }}>
455
- {menuContentNode}
456
- </DropdownContext.Provider>
457
- </DropdownStyleContext.Provider>
432
+ }
433
+ });
434
+ } else {
435
+ // Legacy mode
436
+ triggerContent = (
437
+ <div
438
+ ref={toggleRef}
439
+ className="c-dropdown__toggle"
440
+ onClick={handleToggleClick}
441
+ onKeyDown={handleToggleKeyDown}
442
+ aria-haspopup="menu"
443
+ aria-expanded={isOpen}
444
+ aria-controls={dropdownId}
445
+ tabIndex={0}
446
+ >
447
+ {children}
458
448
  </div>
459
449
  );
450
+ menuContentNode = (
451
+ <ul className={`c-dropdown__menu ${glass ? 'c-dropdown__menu--glass' : ''}`}>{menu}</ul>
452
+ );
453
+ }
454
+
455
+ const menuContent = (
456
+ <div className="c-dropdown__menu-inner" style={menuStyleProps}>
457
+ <DropdownStyleContext.Provider value={{ glass }}>
458
+ <DropdownContext.Provider value={{ isOpen, close, id: dropdownId, trigger }}>
459
+ {menuContentNode}
460
+ </DropdownContext.Provider>
461
+ </DropdownStyleContext.Provider>
462
+ </div>
463
+ );
464
+
465
+ return (
466
+ <div
467
+ ref={dropdownRef}
468
+ className={dropdownClasses}
469
+ style={style}
470
+ onMouseEnter={trigger === 'hover' ? handleHoverOpen : undefined}
471
+ {...props}
472
+ >
473
+ {triggerContent}
460
474
 
461
- return (
462
475
  <div
463
- ref={dropdownRef}
464
- className={dropdownClasses}
465
- style={style}
466
- onMouseEnter={trigger === 'hover' ? handleHoverOpen : undefined}
467
- {...props}
476
+ ref={menuRef}
477
+ id={dropdownId}
478
+ className={`c-dropdown__menu-wrapper c-dropdown__menu-wrapper--${placement} ${isOpen ? 'is-open' : ''} ${glass ? 'is-glass' : ''}`}
479
+ role="menu"
480
+ aria-orientation="vertical"
481
+ aria-hidden={!isOpen}
482
+ onKeyDown={handleKeyDown}
468
483
  >
469
- {triggerContent}
470
-
471
- <div
472
- ref={menuRef}
473
- id={dropdownId}
474
- className={`c-dropdown__menu-wrapper c-dropdown__menu-wrapper--${placement} ${isOpen ? 'is-open' : ''} ${glass ? 'is-glass' : ''}`}
475
- role="menu"
476
- aria-orientation="vertical"
477
- aria-hidden={!isOpen}
478
- onKeyDown={handleKeyDown}
479
- >
480
- {glass
481
- ? // Default glass settings for dropdowns
482
- (() => {
483
- const defaultGlassProps = {
484
- displacementScale: 20,
485
- elasticity: 0,
486
- };
487
-
488
- const glassProps =
489
- glass === true ? defaultGlassProps : { ...defaultGlassProps, ...glass };
490
-
491
- return <AtomixGlass {...glassProps}>{menuContent}</AtomixGlass>;
492
- })()
493
- : menuContent}
494
- </div>
484
+ {glass
485
+ ? // Default glass settings for dropdowns
486
+ (() => {
487
+ const defaultGlassProps = {
488
+ displacementScale: 20,
489
+ elasticity: 0,
490
+ };
491
+
492
+ const glassProps =
493
+ glass === true ? defaultGlassProps : { ...defaultGlassProps, ...glass };
494
+
495
+ return <AtomixGlass {...glassProps}>{menuContent}</AtomixGlass>;
496
+ })()
497
+ : menuContent}
495
498
  </div>
496
- );
497
- }
498
- ) as unknown as DropdownComponent;
499
+ </div>
500
+ );
501
+ }) as unknown as DropdownComponent;
499
502
 
500
503
  export type { DropdownProps, DropdownItemProps, DropdownDividerProps, DropdownHeaderProps };
501
504