@shohojdhara/atomix 0.5.1 → 0.5.4

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 (145) hide show
  1. package/atomix.config.ts +45 -33
  2. package/build-tools/webpack-loader.js +5 -4
  3. package/dist/atomix.css +138 -17
  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 +23 -23
  9. package/dist/charts.js +40 -37
  10. package/dist/charts.js.map +1 -1
  11. package/dist/config.d.ts +699 -0
  12. package/dist/config.js +17 -0
  13. package/dist/config.js.map +1 -0
  14. package/dist/core.d.ts +2 -2
  15. package/dist/core.js +111 -50
  16. package/dist/core.js.map +1 -1
  17. package/dist/forms.d.ts +3 -6
  18. package/dist/forms.js +2 -2
  19. package/dist/forms.js.map +1 -1
  20. package/dist/heavy.d.ts +1 -1
  21. package/dist/heavy.js +173 -111
  22. package/dist/heavy.js.map +1 -1
  23. package/dist/index.d.ts +1881 -790
  24. package/dist/index.esm.js +2713 -816
  25. package/dist/index.esm.js.map +1 -1
  26. package/dist/index.js +2693 -780
  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.d.ts +1390 -276
  33. package/dist/theme.js +2133 -625
  34. package/dist/theme.js.map +1 -1
  35. package/package.json +14 -9
  36. package/scripts/atomix-cli.js +15 -1
  37. package/scripts/cli/__tests__/complexity-utils.test.js +24 -0
  38. package/scripts/cli/__tests__/detector.test.js +50 -0
  39. package/scripts/cli/__tests__/template-engine.test.js +23 -0
  40. package/scripts/cli/__tests__/test-setup.js +3 -0
  41. package/scripts/cli/commands/doctor.js +15 -3
  42. package/scripts/cli/commands/generate.js +113 -51
  43. package/scripts/cli/internal/ai-engine.js +30 -10
  44. package/scripts/cli/internal/complexity-utils.js +60 -0
  45. package/scripts/cli/internal/component-validator.js +49 -16
  46. package/scripts/cli/internal/config-loader.js +30 -20
  47. package/scripts/cli/internal/generator.js +89 -36
  48. package/scripts/cli/internal/hook-generator.js +5 -2
  49. package/scripts/cli/internal/itcss-generator.js +16 -12
  50. package/scripts/cli/templates/next-templates.js +81 -30
  51. package/scripts/cli/templates/storybook-templates.js +12 -2
  52. package/scripts/cli/utils/detector.js +45 -7
  53. package/scripts/cli/utils/diagnostics.js +78 -0
  54. package/scripts/cli/utils/telemetry.js +13 -0
  55. package/src/components/Accordion/Accordion.stories.tsx +4 -0
  56. package/src/components/AtomixGlass/AtomixGlassContainer.tsx +1 -1
  57. package/src/components/AtomixGlass/__snapshots__/AtomixGlass.test.tsx.snap +219 -0
  58. package/src/components/AtomixGlass/glass-utils.ts +1 -1
  59. package/src/components/Button/Button.tsx +114 -57
  60. package/src/components/Callout/Callout.tsx +4 -4
  61. package/src/components/Chart/ChartRenderer.tsx +1 -1
  62. package/src/components/Chart/DonutChart.tsx +11 -8
  63. package/src/components/EdgePanel/EdgePanel.tsx +119 -115
  64. package/src/components/Form/Select.tsx +4 -4
  65. package/src/components/List/List.tsx +4 -4
  66. package/src/components/Navigation/SideMenu/SideMenu.tsx +6 -6
  67. package/src/components/PhotoViewer/PhotoViewerImage.tsx +1 -1
  68. package/src/components/ProductReview/ProductReview.tsx +4 -2
  69. package/src/components/Rating/Rating.tsx +4 -2
  70. package/src/components/SectionIntro/SectionIntro.tsx +4 -2
  71. package/src/components/Steps/Steps.tsx +1 -1
  72. package/src/components/Tabs/Tabs.tsx +5 -5
  73. package/src/components/Testimonial/Testimonial.tsx +4 -2
  74. package/src/components/VideoPlayer/VideoPlayer.tsx +4 -2
  75. package/src/layouts/CssGrid/CssGrid.stories.tsx +464 -0
  76. package/src/layouts/CssGrid/CssGrid.tsx +215 -0
  77. package/src/layouts/CssGrid/index.ts +8 -0
  78. package/src/layouts/CssGrid/scripts/CssGrid.js +284 -0
  79. package/src/layouts/CssGrid/scripts/index.js +43 -0
  80. package/src/layouts/Grid/scripts/Container.js +139 -0
  81. package/src/layouts/Grid/scripts/Grid.js +184 -0
  82. package/src/layouts/Grid/scripts/GridCol.js +273 -0
  83. package/src/layouts/Grid/scripts/Row.js +154 -0
  84. package/src/layouts/Grid/scripts/index.js +48 -0
  85. package/src/layouts/MasonryGrid/MasonryGrid.tsx +71 -59
  86. package/src/lib/composables/atomix-glass/useGlassSize.ts +1 -1
  87. package/src/lib/composables/useAccordion.ts +5 -5
  88. package/src/lib/composables/useAtomixGlass.ts +3 -3
  89. package/src/lib/composables/useBarChart.ts +2 -2
  90. package/src/lib/composables/useChart.ts +3 -2
  91. package/src/lib/composables/useChartToolbar.ts +48 -66
  92. package/src/lib/composables/useDataTable.ts +1 -1
  93. package/src/lib/composables/useDatePicker.ts +2 -2
  94. package/src/lib/composables/useEdgePanel.ts +45 -54
  95. package/src/lib/composables/useHeroBackgroundSlider.ts +5 -5
  96. package/src/lib/composables/usePhotoViewer.ts +2 -3
  97. package/src/lib/composables/usePieChart.ts +1 -1
  98. package/src/lib/composables/usePopover.ts +151 -139
  99. package/src/lib/composables/useSideMenu.ts +28 -41
  100. package/src/lib/composables/useSlider.ts +2 -6
  101. package/src/lib/composables/useTooltip.ts +2 -2
  102. package/src/lib/config/index.ts +38 -323
  103. package/src/lib/config/loader.ts +419 -0
  104. package/src/lib/config/public-api.ts +43 -0
  105. package/src/lib/config/types.ts +389 -0
  106. package/src/lib/config/validator.ts +305 -0
  107. package/src/lib/theme/adapters/index.ts +1 -1
  108. package/src/lib/theme/adapters/themeAdapter.ts +358 -229
  109. package/src/lib/theme/components/ThemeToggle.tsx +276 -0
  110. package/src/lib/theme/config/configLoader.ts +351 -0
  111. package/src/lib/theme/config/loader.ts +221 -0
  112. package/src/lib/theme/core/createTheme.ts +126 -50
  113. package/src/lib/theme/core/createThemeObject.ts +7 -4
  114. package/src/lib/theme/devtools/Comparator.tsx +1 -1
  115. package/src/lib/theme/devtools/Inspector.tsx +1 -1
  116. package/src/lib/theme/devtools/LiveEditor.tsx +1 -1
  117. package/src/lib/theme/hooks/useThemeSwitcher.ts +164 -0
  118. package/src/lib/theme/index.ts +322 -38
  119. package/src/lib/theme/runtime/ThemeProvider.tsx +45 -11
  120. package/src/lib/theme/runtime/__tests__/ThemeProvider.test.tsx +44 -393
  121. package/src/lib/theme/runtime/useTheme.ts +1 -0
  122. package/src/lib/theme/tokens/tokens.ts +101 -1
  123. package/src/lib/theme/types.ts +91 -0
  124. package/src/lib/theme/utils/performanceMonitor.ts +315 -0
  125. package/src/lib/theme/utils/responsive.ts +280 -0
  126. package/src/lib/theme/utils/themeUtils.ts +531 -117
  127. package/src/styles/01-settings/_index.scss +1 -0
  128. package/src/styles/01-settings/_settings.atomix-glass.scss +174 -0
  129. package/src/styles/01-settings/_settings.masonry-grid.scss +42 -6
  130. package/src/styles/02-tools/_tools.glass.scss +6 -0
  131. package/src/styles/05-objects/_objects.masonry-grid.scss +162 -24
  132. package/src/styles/06-components/_components.atomix-glass.scss +4 -4
  133. package/src/lib/composables/useBreadcrumb.ts +0 -81
  134. package/src/lib/composables/useChartInteractions.ts +0 -123
  135. package/src/lib/composables/useChartPerformance.ts +0 -347
  136. package/src/lib/composables/useDropdown.ts +0 -338
  137. package/src/lib/composables/useModal.ts +0 -110
  138. package/src/lib/hooks/usePerformanceMonitor.ts +0 -148
  139. package/src/lib/utils/displacement-generator.ts +0 -92
  140. package/src/lib/utils/memoryMonitor.ts +0 -191
  141. package/src/styles/01-settings/_settings.testtypecheck.scss +0 -53
  142. package/src/styles/01-settings/_settings.typedbutton.scss +0 -53
  143. package/src/styles/06-components/_components.testbutton.scss +0 -212
  144. package/src/styles/06-components/_components.testtypecheck.scss +0 -212
  145. package/src/styles/06-components/_components.typedbutton.scss +0 -212
package/dist/heavy.d.ts CHANGED
@@ -578,7 +578,7 @@ declare global {
578
578
  * AtomixGlass - wrapped with React.memo to prevent unnecessary re-renders.
579
579
  * Ref is forwarded to the root `<div>` element.
580
580
  */
581
- declare const AtomixGlass: React$1.MemoExoticComponent<React$1.ForwardRefExoticComponent<AtomixGlassProps & React$1.RefAttributes<HTMLDivElement>>>;
581
+ declare const AtomixGlass: React$1.NamedExoticComponent<AtomixGlassProps & React$1.RefAttributes<HTMLDivElement>>;
582
582
 
583
583
  /**
584
584
  * Advanced Video Player Component
package/dist/heavy.js CHANGED
@@ -3575,11 +3575,13 @@ const VideoPlayer = forwardRef((({src: src, type: type = "video", youtubeId: yo
3575
3575
  detectBorderRadius();
3576
3576
  // Create ResizeObserver to watch for style changes
3577
3577
  let resizeObserver = null;
3578
- return "undefined" != typeof ResizeObserver && containerRef.current && (resizeObserver = new ResizeObserver(detectBorderRadius),
3578
+ "undefined" != typeof ResizeObserver && containerRef.current && (resizeObserver = new ResizeObserver(detectBorderRadius),
3579
3579
  resizeObserver.observe(containerRef.current)),
3580
3580
  // Also listen for window resize (in case styles change)
3581
- window.addEventListener("resize", detectBorderRadius), () => {
3582
- window.removeEventListener("resize", detectBorderRadius), resizeObserver && containerRef.current && (resizeObserver.unobserve(containerRef.current),
3581
+ window.addEventListener("resize", detectBorderRadius);
3582
+ const currentContainer = containerRef.current;
3583
+ return () => {
3584
+ window.removeEventListener("resize", detectBorderRadius), resizeObserver && currentContainer && (resizeObserver.unobserve(currentContainer),
3583
3585
  resizeObserver.disconnect());
3584
3586
  };
3585
3587
  }), []);
@@ -3914,7 +3916,7 @@ VideoPlayer.displayName = "VideoPlayer";
3914
3916
  */
3915
3917
  const MasonryGrid = forwardRef((({children: children, className: className = "", xs: xs = 1, sm: sm, md: md, lg: lg, xl: xl, xxl: xxl, gap: gap = 16, animate: animate = !0, imagesLoaded: imagesLoaded = !0, onLayoutComplete: onLayoutComplete, onImageLoad: onImageLoad, ...props}, ref) => {
3916
3918
  // === REFS & STATE ===
3917
- const [columns, setColumns] = useState(xs), [positions, setPositions] = useState([]), [layoutComplete, setLayoutComplete] = useState(!1), [loadingImages, setLoadingImages] = useState(!1), containerRef = useRef(null), columnHeights = useRef([]), imagesLoadedCount = useRef(0), totalImagesCount = useRef(0), imageElements = useRef(new Map);
3919
+ const [columns, setColumns] = useState(xs), [positions, setPositions] = useState([]), [, setLayoutComplete] = useState(!1), [loadingImages, setLoadingImages] = useState(!1), containerRef = useRef(null), columnHeights = useRef([]), imagesLoadedCount = useRef(0), totalImagesCount = useRef(0), imageElements = useRef(new Map);
3918
3920
  useEffect((() => {
3919
3921
  setLoadingImages(!!imagesLoaded);
3920
3922
  }), [ columns, imagesLoaded ]),
@@ -3943,34 +3945,45 @@ const MasonryGrid = forwardRef((({children: children, className: className = ""
3943
3945
  });
3944
3946
  })), setItems(newItems);
3945
3947
  }), [ children ]);
3946
- // === TRACK & MANAGE IMAGES ===
3947
- const handleImageLoad = useCallback((img => {
3948
- if (!imageElements.current.get(img)) {
3949
- // Add loaded class for animation
3950
- if (imageElements.current.set(img, !0), imagesLoadedCount.current += 1, containerRef.current && imagesLoaded) {
3951
- const itemElement = img.closest(".o-masonry-grid > div");
3952
- itemElement && (itemElement.offsetHeight, itemElement.classList.add("o-masonry-grid__item-loaded"),
3953
- itemElement.classList.remove("o-masonry-grid__item-loading"));
3948
+ // === MANAGE ITEM LAYOUT ===
3949
+ const calculateLayout = useCallback((() => {
3950
+ if (!containerRef.current || 0 === items.length) return;
3951
+ const colWidth = (containerRef.current.offsetWidth - gap * (columns - 1)) / columns;
3952
+ columnHeights.current = Array(columns).fill(0);
3953
+ const newPositions = [];
3954
+ items.forEach(((item, index) => {
3955
+ if (item.ref.current) {
3956
+ // Find the shortest column
3957
+ const shortestCol = columnHeights.current.indexOf(Math.min(...columnHeights.current)), left = shortestCol * (colWidth + gap), top = columnHeights.current[shortestCol] ?? 0, height = item.ref.current.offsetHeight;
3958
+ columnHeights.current[shortestCol] = top + height + gap, newPositions[index] = {
3959
+ left: left,
3960
+ top: top,
3961
+ width: colWidth,
3962
+ height: height
3963
+ };
3954
3964
  }
3955
- // Ensure layout is recalculated after DOM paints the item image (prevents overlap on slow/late image loads)
3956
- requestAnimationFrame((() => {
3957
- requestAnimationFrame((() => {
3958
- calculateLayout();
3959
- }));
3960
- })), onImageLoad?.(imagesLoadedCount.current, totalImagesCount.current),
3961
- // If all images have loaded, update loading state and complete layout
3962
- imagesLoadedCount.current >= totalImagesCount.current && totalImagesCount.current > 0 && (setLayoutComplete(!0),
3963
- setLoadingImages(!1), // This ensures the loading class is removed *immediately* after images load
3964
- // Force a double requestAnimationFrame for final layout calculation after all images are loaded (guarantees DOM paint)
3965
- requestAnimationFrame((() => {
3966
- requestAnimationFrame((() => {
3967
- calculateLayout(),
3968
- // As a failsafe, if still present for some render lag, force another setLoadingImages(false)
3969
- setLoadingImages(!1);
3970
- }));
3971
- })), onLayoutComplete?.());
3965
+ })), setPositions(newPositions);
3966
+ }), [ items, columns, gap ]), handleImageLoad = useCallback((img => {
3967
+ if (imageElements.current.get(img)) return;
3968
+ // Add loaded class for animation
3969
+ if (imageElements.current.set(img, !0), imagesLoadedCount.current += 1, containerRef.current && imagesLoaded) {
3970
+ const itemElement = img.closest(".o-masonry-grid > div");
3971
+ itemElement && (itemElement.offsetHeight, itemElement.classList.add("o-masonry-grid__item-loaded"),
3972
+ itemElement.classList.remove("o-masonry-grid__item-loading"));
3972
3973
  }
3973
- }), [ onImageLoad, onLayoutComplete, imagesLoaded ]), trackImages = useCallback((() => {
3974
+ // Schedule layout recalculation after next paint to prevent overlap
3975
+ const scheduleLayoutUpdate = () => {
3976
+ const frameId = requestAnimationFrame((() => {
3977
+ onImageLoad?.(imagesLoadedCount.current, totalImagesCount.current), calculateLayout();
3978
+ }));
3979
+ return () => cancelAnimationFrame(frameId);
3980
+ }, cleanup = scheduleLayoutUpdate();
3981
+ // Clean up previous scheduled updates
3982
+ // If all images have loaded, update loading state and complete layout
3983
+ imagesLoadedCount.current >= totalImagesCount.current && totalImagesCount.current > 0 && (setLayoutComplete(!0),
3984
+ setLoadingImages(!1), setTimeout((() => cleanup()), 0), // Clean up after current execution
3985
+ scheduleLayoutUpdate(), onLayoutComplete?.());
3986
+ }), [ onImageLoad, onLayoutComplete, imagesLoaded, calculateLayout ]), trackImages = useCallback((() => {
3974
3987
  if (!imagesLoaded || !containerRef.current) return;
3975
3988
  imageElements.current.clear(), imagesLoadedCount.current = 0;
3976
3989
  const images = containerRef.current.querySelectorAll("img");
@@ -3989,30 +4002,21 @@ const MasonryGrid = forwardRef((({children: children, className: className = ""
3989
4002
  img.removeEventListener("error", masonryImg._masonryLoadHandler), delete masonryImg._masonryLoadHandler);
3990
4003
  }));
3991
4004
  });
3992
- }), [ imagesLoaded, handleImageLoad, onLayoutComplete ]), calculateLayout = useCallback((() => {
3993
- if (!containerRef.current || 0 === items.length) return;
3994
- const colWidth = (containerRef.current.offsetWidth - gap * (columns - 1)) / columns;
3995
- columnHeights.current = Array(columns).fill(0);
3996
- const newPositions = [];
3997
- items.forEach(((item, index) => {
3998
- if (item.ref.current) {
3999
- // Find the shortest column
4000
- const shortestCol = columnHeights.current.indexOf(Math.min(...columnHeights.current)), left = shortestCol * (colWidth + gap), top = columnHeights.current[shortestCol] ?? 0, height = item.ref.current.offsetHeight;
4001
- columnHeights.current[shortestCol] = top + height + gap, newPositions[index] = {
4002
- left: left,
4003
- top: top,
4004
- width: colWidth,
4005
- height: height
4006
- };
4007
- }
4008
- })), setPositions(newPositions);
4009
- }), [ items, columns, gap ]);
4010
- // === OBSERVE CONTAINER RESIZE ===
4005
+ }), [ imagesLoaded, handleImageLoad, onLayoutComplete ]);
4006
+ // === TRACK & MANAGE IMAGES ===
4007
+ // === OBSERVE CONTAINER RESIZE ===
4011
4008
  useEffect((() => {
4012
4009
  if (!containerRef.current) return;
4013
- let animationFrame = null;
4014
- const observer = new ResizeObserver((() => {
4015
- animationFrame && cancelAnimationFrame(animationFrame), animationFrame = requestAnimationFrame((() => calculateLayout()));
4010
+ let animationFrame = null, lastWidth = 0;
4011
+ const observer = new ResizeObserver((entries => {
4012
+ const entry = entries[0];
4013
+ if (!entry) return;
4014
+ const currentWidth = entry.contentRect.width;
4015
+ // Only recalculate if width actually changed (prevents excessive calculations)
4016
+ Math.abs(currentWidth - lastWidth) > 1 && (animationFrame && cancelAnimationFrame(animationFrame),
4017
+ animationFrame = requestAnimationFrame((() => {
4018
+ calculateLayout(), lastWidth = currentWidth;
4019
+ })));
4016
4020
  }));
4017
4021
  return observer.observe(containerRef.current), () => {
4018
4022
  observer.disconnect(), animationFrame && cancelAnimationFrame(animationFrame);
@@ -4023,24 +4027,21 @@ const MasonryGrid = forwardRef((({children: children, className: className = ""
4023
4027
  setLayoutComplete(!0), void setLoadingImages(!1))
4024
4028
  // Only reset layoutComplete when items or columns change
4025
4029
  ), [ items, columns, calculateLayout, imagesLoaded, trackImages ]),
4026
- // === NEW: Add ResizeObservers to all grid items for bulletproof image+content measurement ===
4030
+ // === ADD RESIZEOBSERVERS TO GRID ITEMS FOR DYNAMIC CONTENT MEASUREMENT ===
4027
4031
  React.useEffect((() => {
4028
- // Clean up old observers if items ever change
4029
4032
  const observers = [];
4033
+ let animationFrame = null;
4034
+ // Debounced layout calculation for item resize events
4035
+ const debouncedCalculateLayout = () => {
4036
+ animationFrame && cancelAnimationFrame(animationFrame), animationFrame = requestAnimationFrame(calculateLayout);
4037
+ };
4030
4038
  return items.forEach((item => {
4031
4039
  if (item.ref.current) {
4032
- const obs = new ResizeObserver((() => {
4033
- // Double rAF: ensures layout only runs after DOM/paint/async renders
4034
- requestAnimationFrame((() => {
4035
- requestAnimationFrame((() => {
4036
- calculateLayout();
4037
- }));
4038
- }));
4039
- }));
4040
+ const obs = new ResizeObserver(debouncedCalculateLayout);
4040
4041
  obs.observe(item.ref.current), observers.push(obs);
4041
4042
  }
4042
4043
  })), () => {
4043
- observers.forEach((obs => obs.disconnect()));
4044
+ observers.forEach((obs => obs.disconnect())), animationFrame && cancelAnimationFrame(animationFrame);
4044
4045
  };
4045
4046
  }), [ items, calculateLayout ]);
4046
4047
  // Ensure loadingImages state resets when items/columns/imagesLoaded change
@@ -4351,9 +4352,48 @@ class ThemeNaming {
4351
4352
  }
4352
4353
  }
4353
4354
 
4355
+ /**
4356
+ * Render a slot with the given props
4357
+ *
4358
+ * Priority order:
4359
+ * 1. render function
4360
+ * 2. component
4361
+ * 3. children
4362
+ * 4. fallback
4363
+ *
4364
+ * @example
4365
+ * renderSlot(
4366
+ * { render: (props) => <CustomButton {...props} /> },
4367
+ * { onClick: handleClick, children: 'Click me' }
4368
+ * )
4369
+ */
4370
+ function renderSlot(slot, props, fallback) {
4371
+ // No slot provided, use fallback
4372
+ if (!slot) return fallback;
4373
+ // Slot is a plain React node
4374
+ if ( React.isValidElement(slot) || "string" == typeof slot || "number" == typeof slot) return slot;
4375
+ // Slot is an object with rendering options
4376
+ if ("object" == typeof slot && null !== slot) {
4377
+ const slotObj = slot;
4378
+ // Priority 1: render function
4379
+ if (slotObj.render && "function" == typeof slotObj.render) return slotObj.render(props);
4380
+ // Priority 2: component
4381
+ if (slotObj.component) {
4382
+ const Component = slotObj.component;
4383
+ return jsx(Component, {
4384
+ ...props
4385
+ });
4386
+ }
4387
+ // Priority 3: children
4388
+ if (void 0 !== slotObj.children) return slotObj.children;
4389
+ }
4390
+ // Fallback
4391
+ return fallback;
4392
+ }
4393
+
4354
4394
  ThemeNaming.prefix = "atomix";
4355
4395
 
4356
- const Button = React.memo( forwardRef((({label: label, children: children, onClick: onClick, variant: variant = "primary", size: size = "md", disabled: disabled = !1, loading: loading = !1, loadingText: loadingText, icon: icon, iconName: iconName, iconSize: iconSize = "sm", iconPosition: iconPosition = "start", iconOnly: iconOnly = !1, rounded: rounded = !1, fullWidth: fullWidth = !1, block: block = !1, active: active = !1, selected: selected = !1, type: type = "button", className: className = "", as: Component = "button", href: href, target: target, glass: glass, onHover: onHover, onFocus: onFocus, onBlur: onBlur, "aria-label": ariaLabel, "aria-describedby": ariaDescribedBy, "aria-expanded": ariaExpanded, "aria-controls": ariaControls, tabIndex: tabIndex, style: style, linkComponent: linkComponent, ...props}, ref) => {
4396
+ const Button = React.memo( forwardRef((({label: label, children: children, onClick: onClick, variant: variant = "primary", size: size = "md", disabled: disabled = !1, loading: loading = !1, loadingText: loadingText, icon: icon, iconName: iconName, iconSize: iconSize = "sm", iconPosition: iconPosition = "start", iconOnly: iconOnly = !1, rounded: rounded = !1, fullWidth: fullWidth = !1, block: block = !1, active: active = !1, selected: selected = !1, type: type = "button", className: className = "", as: Component = "button", href: href, target: target, glass: glass, onHover: onHover, onFocus: onFocus, onBlur: onBlur, "aria-label": ariaLabel, "aria-describedby": ariaDescribedBy, "aria-expanded": ariaExpanded, "aria-controls": ariaControls, tabIndex: tabIndex, style: style, linkComponent: linkComponent, slots: slots, ...props}, ref) => {
4357
4397
  const isDisabled = disabled || loading, shouldRenderAsLink = Boolean(href), iconElement = iconName ? jsx(Icon, {
4358
4398
  name: iconName,
4359
4399
  size: iconSize
@@ -4369,17 +4409,28 @@ const Button = React.memo( forwardRef((({label: label, children: children, onCl
4369
4409
  children: [ loading && jsx("span", {
4370
4410
  className: ThemeNaming.bemClass("btn", "spinner"),
4371
4411
  "aria-hidden": "true",
4372
- children: jsx(Spinner, {
4412
+ children: renderSlot(slots?.spinner, {
4413
+ className: ThemeNaming.bemClass("btn", "spinner"),
4373
4414
  size: spinnerSize,
4374
4415
  variant: "link" === variant || "string" == typeof variant && variant.startsWith("outline-") ? "primary" : "danger" === variant ? "error" : variant
4375
- })
4416
+ }, jsx(Spinner, {
4417
+ size: spinnerSize,
4418
+ variant: "link" === variant || "string" == typeof variant && variant.startsWith("outline-") ? "primary" : "danger" === variant ? "error" : variant
4419
+ }))
4376
4420
  }), iconElement && !loading && jsx("span", {
4377
4421
  className: ThemeNaming.bemClass("btn", "icon"),
4378
4422
  "aria-hidden": "true",
4379
- children: iconElement
4423
+ children: renderSlot(slots?.icon, {
4424
+ className: ThemeNaming.bemClass("btn", "icon"),
4425
+ children: iconElement,
4426
+ size: iconSize
4427
+ }, iconElement)
4380
4428
  }), !iconOnly && buttonText && jsx("span", {
4381
4429
  className: ThemeNaming.bemClass("btn", "label"),
4382
- children: buttonText
4430
+ children: renderSlot(slots?.label, {
4431
+ className: ThemeNaming.bemClass("btn", "label"),
4432
+ children: buttonText
4433
+ }, buttonText)
4383
4434
  }) ]
4384
4435
  }), buttonProps = {
4385
4436
  className: buttonClass,
@@ -4396,48 +4447,59 @@ const Button = React.memo( forwardRef((({label: label, children: children, onCl
4396
4447
  tabIndex: void 0 !== tabIndex ? tabIndex : isDisabled ? -1 : 0,
4397
4448
  style: style,
4398
4449
  ...props
4399
- };
4400
- // Determine if we should render as a link
4401
- // If disabled, we still check href, but we might want to render as button or anchor with aria-disabled
4402
- // The previous logic was Boolean(href && !isDisabled). This meant if disabled, it renders as <button>.
4403
- // This is a safe fallback for disabled links.
4404
- let content;
4405
- // Render as anchor if href is provided
4406
- if (shouldRenderAsLink)
4407
- // Use custom linkComponent if provided (e.g., Next.js Link)
4408
- if (linkComponent) {
4409
- const LinkComp = linkComponent, linkProps = {
4450
+ }, buttonChildren = renderSlot(slots?.root, {
4451
+ className: buttonClass,
4452
+ children: buttonContent,
4453
+ disabled: isDisabled,
4454
+ loading: loading,
4455
+ onClick: handleClickEvent,
4456
+ type: type,
4457
+ "aria-label": safeAriaLabel,
4458
+ "aria-disabled": isDisabled,
4459
+ "aria-busy": loading
4460
+ }, (() => {
4461
+ // Render as anchor if href is provided
4462
+ if (shouldRenderAsLink) {
4463
+ // Use custom linkComponent if provided (e.g., Next.js Link)
4464
+ if (linkComponent) {
4465
+ const LinkComp = linkComponent, linkProps = {
4466
+ ...buttonProps,
4467
+ ref: ref,
4468
+ // linkComponent usually forwards ref to anchor
4469
+ href: isDisabled ? void 0 : href,
4470
+ to: isDisabled ? void 0 : href,
4471
+ target: target,
4472
+ rel: "_blank" === target ? "noopener noreferrer" : void 0
4473
+ };
4474
+ return jsx(LinkComp, {
4475
+ ...linkProps,
4476
+ children: buttonContent
4477
+ });
4478
+ }
4479
+ // Fallback to regular anchor tag
4480
+ return jsx("a", {
4481
+ ...buttonProps,
4482
+ ref: ref,
4483
+ href: isDisabled ? void 0 : href,
4484
+ target: target,
4485
+ rel: "_blank" === target ? "noopener noreferrer" : void 0,
4486
+ children: buttonContent
4487
+ });
4488
+ }
4489
+ // Default button rendering
4490
+ return jsx(Component, {
4410
4491
  ...buttonProps,
4411
4492
  ref: ref,
4412
- // linkComponent usually forwards ref to anchor
4413
- href: isDisabled ? void 0 : href,
4414
- to: isDisabled ? void 0 : href,
4415
- target: target,
4416
- rel: "_blank" === target ? "noopener noreferrer" : void 0
4417
- };
4418
- content = jsx(LinkComp, {
4419
- ...linkProps,
4493
+ type: "button" === Component ? type : void 0,
4494
+ disabled: isDisabled,
4420
4495
  children: buttonContent
4421
4496
  });
4422
- } else
4423
- // Fallback to regular anchor tag
4424
- content = jsx("a", {
4425
- ...buttonProps,
4426
- ref: ref,
4427
- href: isDisabled ? void 0 : href,
4428
- target: target,
4429
- rel: "_blank" === target ? "noopener noreferrer" : void 0,
4430
- children: buttonContent
4431
- }); else
4432
- // Default button rendering
4433
- content = jsx(Component, {
4434
- ...buttonProps,
4435
- ref: ref,
4436
- type: "button" === Component ? type : void 0,
4437
- disabled: isDisabled,
4438
- children: buttonContent
4439
- });
4440
- if (glass) {
4497
+ })());
4498
+ // Determine if we should render as a link
4499
+ // If disabled, we still check href, but we might want to render as button or anchor with aria-disabled
4500
+ // The previous logic was Boolean(href && !isDisabled). This meant if disabled, it renders as <button>.
4501
+ // This is a safe fallback for disabled links.
4502
+ if (glass) {
4441
4503
  // Default glass props
4442
4504
  const defaultGlassProps = {
4443
4505
  displacementScale: 20,
@@ -4450,10 +4512,10 @@ const Button = React.memo( forwardRef((({label: label, children: children, onCl
4450
4512
  };
4451
4513
  return jsx(AtomixGlass, {
4452
4514
  ...glassProps,
4453
- children: content
4515
+ children: buttonChildren
4454
4516
  });
4455
4517
  }
4456
- return content;
4518
+ return buttonChildren;
4457
4519
  })));
4458
4520
 
4459
4521
  Button.displayName = "Button";
@@ -4992,7 +5054,7 @@ useEffect((() => {
4992
5054
  }
4993
5055
  };
4994
5056
  }));
4995
- }), [ isMounted, currentIndex, calculateBounds, constrainPosition ]), setImagePosition = useCallback((position => {
5057
+ }), [ currentIndex, calculateBounds, constrainPosition ]), setImagePosition = useCallback((position => {
4996
5058
  setImageStates((prev => {
4997
5059
  const currentState = prev[currentIndex] || {
4998
5060
  zoomLevel: 1,
@@ -5042,7 +5104,7 @@ useEffect((() => {
5042
5104
  }
5043
5105
  };
5044
5106
  }));
5045
- }), [ isMounted, currentIndex, calculateBounds, constrainPosition ]), handleWheel = useCallback((event => {
5107
+ }), [ currentIndex, calculateBounds, constrainPosition ]), handleWheel = useCallback((event => {
5046
5108
  var _context;
5047
5109
  if (!isMounted || !event || !event.currentTarget) return;
5048
5110
  // Additional safety check for the target element
@@ -5399,7 +5461,7 @@ useEffect((() => {
5399
5461
  }
5400
5462
  return prev;
5401
5463
  }));
5402
- }), [ isMounted, enableGestures, isDragging, startDragPosition, currentIndex, constrainPosition, calculateBounds ]), handleTouchEnd = useCallback((() => {
5464
+ }), [ enableGestures, isDragging, startDragPosition, currentIndex, constrainPosition, calculateBounds ]), handleTouchEnd = useCallback((() => {
5403
5465
  setIsDragging(!1), lastDistanceRef.current = null, lastMidpointRef.current = null;
5404
5466
  }), []), currentState = imageStates[currentIndex] || {
5405
5467
  zoomLevel: 1,