@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/index.js CHANGED
@@ -4,7 +4,7 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: !0
5
5
  });
6
6
 
7
- var jsxRuntime = require("react/jsx-runtime"), React = require("react"), PhosphorIcons = require("@phosphor-icons/react"), reactDom = require("react-dom");
7
+ var jsxRuntime = require("react/jsx-runtime"), React = require("react"), PhosphorIcons = require("@phosphor-icons/react"), reactDom = require("react-dom"), fs = require("fs"), path$4 = require("path");
8
8
 
9
9
  function _interopDefaultCompat(e) {
10
10
  return e && "object" == typeof e && "default" in e ? e : {
@@ -1988,19 +1988,19 @@ function useAccordion(initialProps) {
1988
1988
  disabled: !1,
1989
1989
  iconPosition: "right",
1990
1990
  ...initialProps
1991
- }, isControlled = "boolean" == typeof defaultProps.isOpen, [internalOpen, setInternalOpen] = React.useState(defaultProps.defaultOpen || !1), isOpen = isControlled ? defaultProps.isOpen : internalOpen, [panelHeight, setPanelHeight] = React.useState(isOpen ? "auto" : "0px"), panelRef = React.useRef(null), contentRef = React.useRef(null), updatePanelHeight = () => {
1991
+ }, isControlled = "boolean" == typeof defaultProps.isOpen, [internalOpen, setInternalOpen] = React.useState(defaultProps.defaultOpen || !1), isOpen = isControlled ? defaultProps.isOpen : internalOpen, [panelHeight, setPanelHeight] = React.useState(isOpen ? "auto" : "0px"), panelRef = React.useRef(null), contentRef = React.useRef(null), updatePanelHeight = React.useCallback((() => {
1992
1992
  if (contentRef.current && panelRef.current) {
1993
1993
  const height = isOpen ? `${contentRef.current.clientHeight}px` : "0px";
1994
1994
  panelRef.current.style.setProperty(ACCORDION.CSS_VARS.PANEL_HEIGHT, height), setPanelHeight(height);
1995
1995
  }
1996
- };
1996
+ }), [ isOpen ]);
1997
1997
  // Controlled/uncontrolled open state
1998
1998
  /**
1999
1999
  * Effect to update panel height when open state changes
2000
2000
  */
2001
2001
  return React.useEffect((() => {
2002
2002
  updatePanelHeight();
2003
- }), [ isOpen ]),
2003
+ }), [ isOpen, updatePanelHeight ]),
2004
2004
  /**
2005
2005
  * Effect to handle window resize and update panel height
2006
2006
  */
@@ -2009,7 +2009,7 @@ function useAccordion(initialProps) {
2009
2009
  isOpen && updatePanelHeight();
2010
2010
  };
2011
2011
  return window.addEventListener("resize", handleResize), () => window.removeEventListener("resize", handleResize);
2012
- }), [ isOpen ]), {
2012
+ }), [ isOpen, updatePanelHeight ]), {
2013
2013
  state: {
2014
2014
  isOpen: isOpen,
2015
2015
  panelHeight: panelHeight
@@ -3587,7 +3587,7 @@ function useResponsiveGlass({baseParams: baseParams, breakpoints: breakpoints =
3587
3587
  * @param config Monitor configuration
3588
3588
  * @returns Performance metrics and controls
3589
3589
  */
3590
- function usePerformanceMonitor(config = {}) {
3590
+ function usePerformanceMonitor$1(config = {}) {
3591
3591
  const {enabled: enabled = !0, targetFps: targetFps = 60, minFps: minFps = 45, scaleUpThreshold: scaleUpThreshold = 58, lowFpsFrames: lowFpsFrames = 3, highFpsFrames: highFpsFrames = 10, debug: debug = !1, showOverlay: showOverlay = !1} = config, [metrics, setMetrics] = React.useState({
3592
3592
  fps: 0,
3593
3593
  frameTime: 0,
@@ -4143,7 +4143,7 @@ function getDevicePreset(presetName) {
4143
4143
  debug: !1
4144
4144
  });
4145
4145
  // Performance monitoring - tracks FPS, frame time, memory usage
4146
- const {metrics: performanceMetrics, toggleMonitoring: toggleMonitoring} = usePerformanceMonitor({
4146
+ const {metrics: performanceMetrics, toggleMonitoring: toggleMonitoring} = usePerformanceMonitor$1({
4147
4147
  enabled: debugPerformance,
4148
4148
  // Enable when debugPerformance is true
4149
4149
  debug: !1,
@@ -5144,7 +5144,7 @@ const BreadcrumbItem = React.forwardRef((({children: children, href: href, acti
5144
5144
 
5145
5145
  BreadcrumbItem.displayName = "BreadcrumbItem";
5146
5146
 
5147
- const Breadcrumb = React.memo((function({items: items, divider: divider, className: className = "", "aria-label": ariaLabel = "Breadcrumb", linkComponent: linkComponent, style: style, children: children}) {
5147
+ const BreadcrumbComponent = React.memo((function({items: items, divider: divider, className: className = "", "aria-label": ariaLabel = "Breadcrumb", linkComponent: linkComponent, style: style, children: children}) {
5148
5148
  const breadcrumbClasses = [ BREADCRUMB.CLASSES.BASE, className ].filter(Boolean).join(" ");
5149
5149
  let content;
5150
5150
  if (items && items.length > 0)
@@ -5184,7 +5184,7 @@ const Breadcrumb = React.memo((function({items: items, divider: divider, classN
5184
5184
  children: content
5185
5185
  })
5186
5186
  });
5187
- }));
5187
+ })), Breadcrumb = BreadcrumbComponent;
5188
5188
 
5189
5189
  /**
5190
5190
  * Spinner state and functionality
@@ -5344,7 +5344,100 @@ class ThemeNaming {
5344
5344
 
5345
5345
  ThemeNaming.prefix = "atomix";
5346
5346
 
5347
- const Button = React__default.default.memo( React.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) => {
5347
+ var aCallable = aCallable$3, toObject = toObject$2, IndexedObject = indexedObject, lengthOfArrayLike = lengthOfArrayLike$2, $TypeError = TypeError, REDUCE_EMPTY = "Reduce of empty array with no initial value", createMethod = function(IS_RIGHT) {
5348
+ return function(that, callbackfn, argumentsLength, memo) {
5349
+ var O = toObject(that), self = IndexedObject(O), length = lengthOfArrayLike(O);
5350
+ if (aCallable(callbackfn), 0 === length && argumentsLength < 2) throw new $TypeError(REDUCE_EMPTY);
5351
+ var index = IS_RIGHT ? length - 1 : 0, i = IS_RIGHT ? -1 : 1;
5352
+ if (argumentsLength < 2) for (;;) {
5353
+ if (index in self) {
5354
+ memo = self[index], index += i;
5355
+ break;
5356
+ }
5357
+ if (index += i, IS_RIGHT ? index < 0 : length <= index) throw new $TypeError(REDUCE_EMPTY);
5358
+ }
5359
+ for (;IS_RIGHT ? index >= 0 : length > index; index += i) index in self && (memo = callbackfn(memo, self[index], index, O));
5360
+ return memo;
5361
+ };
5362
+ }, arrayReduce = {
5363
+ // `Array.prototype.reduce` method
5364
+ // https://tc39.es/ecma262/#sec-array.prototype.reduce
5365
+ left: createMethod(!1),
5366
+ // `Array.prototype.reduceRight` method
5367
+ // https://tc39.es/ecma262/#sec-array.prototype.reduceright
5368
+ right: createMethod(!0)
5369
+ }, fails = fails$9, globalThis$1 = globalThis_1, userAgent = environmentUserAgent, classof = classofRaw$2, userAgentStartsWith = function(string) {
5370
+ return userAgent.slice(0, string.length) === string;
5371
+ }, environment = userAgentStartsWith("Bun/") ? "BUN" : userAgentStartsWith("Cloudflare-Workers") ? "CLOUDFLARE" : userAgentStartsWith("Deno/") ? "DENO" : userAgentStartsWith("Node.js/") ? "NODE" : globalThis$1.Bun && "string" == typeof Bun.version ? "BUN" : globalThis$1.Deno && "object" == typeof Deno.version ? "DENO" : "process" === classof(globalThis$1.process) ? "NODE" : globalThis$1.window && globalThis$1.document ? "BROWSER" : "REST", $reduce = arrayReduce.left;
5372
+
5373
+ // `Array.prototype.reduce` method
5374
+ // https://tc39.es/ecma262/#sec-array.prototype.reduce
5375
+ _export({
5376
+ target: "Array",
5377
+ proto: !0,
5378
+ forced: !("NODE" === environment) && environmentV8Version > 79 && environmentV8Version < 83 || !function(METHOD_NAME, argument) {
5379
+ var method = [][METHOD_NAME];
5380
+ return !!method && fails((function() {
5381
+ // eslint-disable-next-line no-useless-call -- required for testing
5382
+ method.call(null, argument || function() {
5383
+ return 1;
5384
+ }, 1);
5385
+ }));
5386
+ }("reduce")
5387
+ }, {
5388
+ reduce: function(callbackfn /* , initialValue */) {
5389
+ var length = arguments.length;
5390
+ return $reduce(this, callbackfn, length, length > 1 ? arguments[1] : void 0);
5391
+ }
5392
+ });
5393
+
5394
+ var reduce$3 = getBuiltInPrototypeMethod$3("Array", "reduce"), isPrototypeOf = objectIsPrototypeOf, method = reduce$3, ArrayPrototype = Array.prototype, _reduceInstanceProperty = getDefaultExportFromCjs((function(it) {
5395
+ var own = it.reduce;
5396
+ return it === ArrayPrototype || isPrototypeOf(ArrayPrototype, it) && own === ArrayPrototype.reduce ? method : own;
5397
+ }));
5398
+
5399
+ /**
5400
+ * Render a slot with the given props
5401
+ *
5402
+ * Priority order:
5403
+ * 1. render function
5404
+ * 2. component
5405
+ * 3. children
5406
+ * 4. fallback
5407
+ *
5408
+ * @example
5409
+ * renderSlot(
5410
+ * { render: (props) => <CustomButton {...props} /> },
5411
+ * { onClick: handleClick, children: 'Click me' }
5412
+ * )
5413
+ */
5414
+ function renderSlot(slot, props, fallback) {
5415
+ // No slot provided, use fallback
5416
+ if (!slot) return fallback;
5417
+ // Slot is a plain React node
5418
+ if ( React__default.default.isValidElement(slot) || "string" == typeof slot || "number" == typeof slot) return slot;
5419
+ // Slot is an object with rendering options
5420
+ if ("object" == typeof slot && null !== slot) {
5421
+ const slotObj = slot;
5422
+ // Priority 1: render function
5423
+ if (slotObj.render && "function" == typeof slotObj.render) return slotObj.render(props);
5424
+ // Priority 2: component
5425
+ if (slotObj.component) {
5426
+ const Component = slotObj.component;
5427
+ return jsxRuntime.jsx(Component, {
5428
+ ...props
5429
+ });
5430
+ }
5431
+ // Priority 3: children
5432
+ if (void 0 !== slotObj.children) return slotObj.children;
5433
+ }
5434
+ // Fallback
5435
+ return fallback;
5436
+ }
5437
+
5438
+ /**
5439
+ * Check if a value is a slot configuration
5440
+ */ const Button = React__default.default.memo( React.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) => {
5348
5441
  const isDisabled = disabled || loading, shouldRenderAsLink = Boolean(href), iconElement = iconName ? jsxRuntime.jsx(Icon, {
5349
5442
  name: iconName,
5350
5443
  size: iconSize
@@ -5360,17 +5453,28 @@ const Button = React__default.default.memo( React.forwardRef((({label: label, c
5360
5453
  children: [ loading && jsxRuntime.jsx("span", {
5361
5454
  className: ThemeNaming.bemClass(THEME_NAMING.BUTTON_PREFIX, THEME_NAMING.SPINNER_ELEMENT),
5362
5455
  "aria-hidden": "true",
5363
- children: jsxRuntime.jsx(Spinner, {
5456
+ children: renderSlot(slots?.spinner, {
5457
+ className: ThemeNaming.bemClass(THEME_NAMING.BUTTON_PREFIX, THEME_NAMING.SPINNER_ELEMENT),
5364
5458
  size: spinnerSize,
5365
5459
  variant: "link" === variant || "string" == typeof variant && variant.startsWith("outline-") ? "primary" : "danger" === variant ? "error" : variant
5366
- })
5460
+ }, jsxRuntime.jsx(Spinner, {
5461
+ size: spinnerSize,
5462
+ variant: "link" === variant || "string" == typeof variant && variant.startsWith("outline-") ? "primary" : "danger" === variant ? "error" : variant
5463
+ }))
5367
5464
  }), iconElement && !loading && jsxRuntime.jsx("span", {
5368
5465
  className: ThemeNaming.bemClass(THEME_NAMING.BUTTON_PREFIX, THEME_NAMING.ICON_ELEMENT),
5369
5466
  "aria-hidden": "true",
5370
- children: iconElement
5467
+ children: renderSlot(slots?.icon, {
5468
+ className: ThemeNaming.bemClass(THEME_NAMING.BUTTON_PREFIX, THEME_NAMING.ICON_ELEMENT),
5469
+ children: iconElement,
5470
+ size: iconSize
5471
+ }, iconElement)
5371
5472
  }), !iconOnly && buttonText && jsxRuntime.jsx("span", {
5372
5473
  className: ThemeNaming.bemClass(THEME_NAMING.BUTTON_PREFIX, THEME_NAMING.LABEL_ELEMENT),
5373
- children: buttonText
5474
+ children: renderSlot(slots?.label, {
5475
+ className: ThemeNaming.bemClass(THEME_NAMING.BUTTON_PREFIX, THEME_NAMING.LABEL_ELEMENT),
5476
+ children: buttonText
5477
+ }, buttonText)
5374
5478
  }) ]
5375
5479
  }), buttonProps = {
5376
5480
  className: buttonClass,
@@ -5387,48 +5491,59 @@ const Button = React__default.default.memo( React.forwardRef((({label: label, c
5387
5491
  tabIndex: void 0 !== tabIndex ? tabIndex : isDisabled ? -1 : 0,
5388
5492
  style: style,
5389
5493
  ...props
5390
- };
5391
- // Determine if we should render as a link
5392
- // If disabled, we still check href, but we might want to render as button or anchor with aria-disabled
5393
- // The previous logic was Boolean(href && !isDisabled). This meant if disabled, it renders as <button>.
5394
- // This is a safe fallback for disabled links.
5395
- let content;
5396
- // Render as anchor if href is provided
5397
- if (shouldRenderAsLink)
5398
- // Use custom linkComponent if provided (e.g., Next.js Link)
5399
- if (linkComponent) {
5400
- const LinkComp = linkComponent, linkProps = {
5494
+ }, buttonChildren = renderSlot(slots?.root, {
5495
+ className: buttonClass,
5496
+ children: buttonContent,
5497
+ disabled: isDisabled,
5498
+ loading: loading,
5499
+ onClick: handleClickEvent,
5500
+ type: type,
5501
+ "aria-label": safeAriaLabel,
5502
+ "aria-disabled": isDisabled,
5503
+ "aria-busy": loading
5504
+ }, (() => {
5505
+ // Render as anchor if href is provided
5506
+ if (shouldRenderAsLink) {
5507
+ // Use custom linkComponent if provided (e.g., Next.js Link)
5508
+ if (linkComponent) {
5509
+ const LinkComp = linkComponent, linkProps = {
5510
+ ...buttonProps,
5511
+ ref: ref,
5512
+ // linkComponent usually forwards ref to anchor
5513
+ href: isDisabled ? void 0 : href,
5514
+ to: isDisabled ? void 0 : href,
5515
+ target: target,
5516
+ rel: "_blank" === target ? "noopener noreferrer" : void 0
5517
+ };
5518
+ return jsxRuntime.jsx(LinkComp, {
5519
+ ...linkProps,
5520
+ children: buttonContent
5521
+ });
5522
+ }
5523
+ // Fallback to regular anchor tag
5524
+ return jsxRuntime.jsx("a", {
5525
+ ...buttonProps,
5526
+ ref: ref,
5527
+ href: isDisabled ? void 0 : href,
5528
+ target: target,
5529
+ rel: "_blank" === target ? "noopener noreferrer" : void 0,
5530
+ children: buttonContent
5531
+ });
5532
+ }
5533
+ // Default button rendering
5534
+ return jsxRuntime.jsx(Component, {
5401
5535
  ...buttonProps,
5402
5536
  ref: ref,
5403
- // linkComponent usually forwards ref to anchor
5404
- href: isDisabled ? void 0 : href,
5405
- to: isDisabled ? void 0 : href,
5406
- target: target,
5407
- rel: "_blank" === target ? "noopener noreferrer" : void 0
5408
- };
5409
- content = jsxRuntime.jsx(LinkComp, {
5410
- ...linkProps,
5537
+ type: "button" === Component ? type : void 0,
5538
+ disabled: isDisabled,
5411
5539
  children: buttonContent
5412
5540
  });
5413
- } else
5414
- // Fallback to regular anchor tag
5415
- content = jsxRuntime.jsx("a", {
5416
- ...buttonProps,
5417
- ref: ref,
5418
- href: isDisabled ? void 0 : href,
5419
- target: target,
5420
- rel: "_blank" === target ? "noopener noreferrer" : void 0,
5421
- children: buttonContent
5422
- }); else
5423
- // Default button rendering
5424
- content = jsxRuntime.jsx(Component, {
5425
- ...buttonProps,
5426
- ref: ref,
5427
- type: "button" === Component ? type : void 0,
5428
- disabled: isDisabled,
5429
- children: buttonContent
5430
- });
5431
- if (glass) {
5541
+ })());
5542
+ // Determine if we should render as a link
5543
+ // If disabled, we still check href, but we might want to render as button or anchor with aria-disabled
5544
+ // The previous logic was Boolean(href && !isDisabled). This meant if disabled, it renders as <button>.
5545
+ // This is a safe fallback for disabled links.
5546
+ if (glass) {
5432
5547
  // Default glass props
5433
5548
  const defaultGlassProps = {
5434
5549
  displacementScale: 20,
@@ -5441,10 +5556,10 @@ const Button = React__default.default.memo( React.forwardRef((({label: label, c
5441
5556
  };
5442
5557
  return jsxRuntime.jsx(AtomixGlass, {
5443
5558
  ...glassProps,
5444
- children: content
5559
+ children: buttonChildren
5445
5560
  });
5446
5561
  }
5447
- return content;
5562
+ return buttonChildren;
5448
5563
  })));
5449
5564
 
5450
5565
  Button.displayName = "Button";
@@ -5552,7 +5667,7 @@ const CalloutContent = React.forwardRef((({children: children, className: class
5552
5667
 
5553
5668
  CalloutContent.displayName = "CalloutContent";
5554
5669
 
5555
- const Callout = React.memo((({title: title, children: children, icon: icon, variant: variant = "primary", onClose: onClose, actions: actions, compact: compact = !1, isToast: isToast = !1, glass: glass, className: className, style: style, ...props}) => {
5670
+ const CalloutComponentBase = ({title: title, children: children, icon: icon, variant: variant = "primary", onClose: onClose, actions: actions, compact: compact = !1, isToast: isToast = !1, glass: glass, className: className, style: style, ...props}) => {
5556
5671
  const {generateCalloutClass: generateCalloutClass, handleClose: handleClose} =
5557
5672
  /**
5558
5673
  * Callout state and functionality
@@ -5678,7 +5793,7 @@ const Callout = React.memo((({title: title, children: children, icon: icon, var
5678
5793
  style: style,
5679
5794
  children: calloutContent
5680
5795
  });
5681
- }));
5796
+ }, Callout = React.memo(CalloutComponentBase);
5682
5797
 
5683
5798
  Callout.displayName = "Callout",
5684
5799
  // Attach subcomponents
@@ -5968,60 +6083,6 @@ const ElevationCard = ({elevationClass: elevationClass = "is-elevated", classNam
5968
6083
  });
5969
6084
  };
5970
6085
 
5971
- ElevationCard.displayName = "ElevationCard";
5972
-
5973
- var aCallable = aCallable$3, toObject = toObject$2, IndexedObject = indexedObject, lengthOfArrayLike = lengthOfArrayLike$2, $TypeError = TypeError, REDUCE_EMPTY = "Reduce of empty array with no initial value", createMethod = function(IS_RIGHT) {
5974
- return function(that, callbackfn, argumentsLength, memo) {
5975
- var O = toObject(that), self = IndexedObject(O), length = lengthOfArrayLike(O);
5976
- if (aCallable(callbackfn), 0 === length && argumentsLength < 2) throw new $TypeError(REDUCE_EMPTY);
5977
- var index = IS_RIGHT ? length - 1 : 0, i = IS_RIGHT ? -1 : 1;
5978
- if (argumentsLength < 2) for (;;) {
5979
- if (index in self) {
5980
- memo = self[index], index += i;
5981
- break;
5982
- }
5983
- if (index += i, IS_RIGHT ? index < 0 : length <= index) throw new $TypeError(REDUCE_EMPTY);
5984
- }
5985
- for (;IS_RIGHT ? index >= 0 : length > index; index += i) index in self && (memo = callbackfn(memo, self[index], index, O));
5986
- return memo;
5987
- };
5988
- }, arrayReduce = {
5989
- // `Array.prototype.reduce` method
5990
- // https://tc39.es/ecma262/#sec-array.prototype.reduce
5991
- left: createMethod(!1),
5992
- // `Array.prototype.reduceRight` method
5993
- // https://tc39.es/ecma262/#sec-array.prototype.reduceright
5994
- right: createMethod(!0)
5995
- }, fails = fails$9, globalThis$1 = globalThis_1, userAgent = environmentUserAgent, classof = classofRaw$2, userAgentStartsWith = function(string) {
5996
- return userAgent.slice(0, string.length) === string;
5997
- }, environment = userAgentStartsWith("Bun/") ? "BUN" : userAgentStartsWith("Cloudflare-Workers") ? "CLOUDFLARE" : userAgentStartsWith("Deno/") ? "DENO" : userAgentStartsWith("Node.js/") ? "NODE" : globalThis$1.Bun && "string" == typeof Bun.version ? "BUN" : globalThis$1.Deno && "object" == typeof Deno.version ? "DENO" : "process" === classof(globalThis$1.process) ? "NODE" : globalThis$1.window && globalThis$1.document ? "BROWSER" : "REST", $reduce = arrayReduce.left;
5998
-
5999
- // `Array.prototype.reduce` method
6000
- // https://tc39.es/ecma262/#sec-array.prototype.reduce
6001
- _export({
6002
- target: "Array",
6003
- proto: !0,
6004
- forced: !("NODE" === environment) && environmentV8Version > 79 && environmentV8Version < 83 || !function(METHOD_NAME, argument) {
6005
- var method = [][METHOD_NAME];
6006
- return !!method && fails((function() {
6007
- // eslint-disable-next-line no-useless-call -- required for testing
6008
- method.call(null, argument || function() {
6009
- return 1;
6010
- }, 1);
6011
- }));
6012
- }("reduce")
6013
- }, {
6014
- reduce: function(callbackfn /* , initialValue */) {
6015
- var length = arguments.length;
6016
- return $reduce(this, callbackfn, length, length > 1 ? arguments[1] : void 0);
6017
- }
6018
- });
6019
-
6020
- var reduce$3 = getBuiltInPrototypeMethod$3("Array", "reduce"), isPrototypeOf = objectIsPrototypeOf, method = reduce$3, ArrayPrototype = Array.prototype, _reduceInstanceProperty = getDefaultExportFromCjs((function(it) {
6021
- var own = it.reduce;
6022
- return it === ArrayPrototype || isPrototypeOf(ArrayPrototype, it) && own === ArrayPrototype.reduce ? method : own;
6023
- }));
6024
-
6025
6086
  /**
6026
6087
  * Comprehensive chart hook with shared functionality
6027
6088
  * @param initialProps - Initial chart properties
@@ -6059,8 +6120,11 @@ function useChart(initialProps) {
6059
6120
  }), animationFrameRef = React.useRef(null);
6060
6121
  // Default chart properties
6061
6122
  // Cleanup animation frame on unmount
6062
- React.useEffect((() => () => {
6063
- animationFrameRef.current && cancelAnimationFrame(animationFrameRef.current);
6123
+ React.useEffect((() => {
6124
+ const currentRef = animationFrameRef.current;
6125
+ return () => {
6126
+ currentRef && cancelAnimationFrame(currentRef);
6127
+ };
6064
6128
  }), []);
6065
6129
  /**
6066
6130
  * Point interaction handlers
@@ -6451,7 +6515,9 @@ function getDatasetBounds(data) {
6451
6515
 
6452
6516
  /**
6453
6517
  * Hook for managing chart toolbar state and generating chart-specific configurations
6454
- */ const ChartToolbar = React.memo( React.forwardRef((({chartType: chartType = "line", groups: groups = [], enableDefaults: enableDefaults = !0, defaults: defaults = {
6518
+ */ ElevationCard.displayName = "ElevationCard";
6519
+
6520
+ const ChartToolbar = React.memo( React.forwardRef((({chartType: chartType = "line", groups: groups = [], enableDefaults: enableDefaults = !0, defaults: defaults = {
6455
6521
  refresh: !0,
6456
6522
  export: !0,
6457
6523
  fullscreen: !0,
@@ -6941,8 +7007,8 @@ toolbarConfig: toolbarConfig, customToolbarActions: customToolbarActions, custom
6941
7007
  }), [ chartType ]), finalDefaults = React.useMemo((() => ({
6942
7008
  ...getChartDefaults(),
6943
7009
  ...defaults
6944
- })), [ getChartDefaults, defaults ]), enhancedHandlers = {
6945
- onRefresh: React.useCallback((() => {
7010
+ })), [ getChartDefaults, defaults ]), enhancedHandlers = React.useMemo((() => ({
7011
+ onRefresh: () => {
6946
7012
  setState((prev => ({
6947
7013
  ...prev,
6948
7014
  isRefreshing: !0
@@ -6952,8 +7018,8 @@ toolbarConfig: toolbarConfig, customToolbarActions: customToolbarActions, custom
6952
7018
  isRefreshing: !1
6953
7019
  })));
6954
7020
  }), 1e3);
6955
- }), [ handlers.onRefresh ]),
6956
- onExport: React.useCallback((async format => {
7021
+ },
7022
+ onExport: async format => {
6957
7023
  setState((prev => ({
6958
7024
  ...prev,
6959
7025
  isExporting: !0
@@ -6966,70 +7032,70 @@ toolbarConfig: toolbarConfig, customToolbarActions: customToolbarActions, custom
6966
7032
  isExporting: !1
6967
7033
  })));
6968
7034
  }
6969
- }), [ handlers.onExport ]),
6970
- onFullscreen: React.useCallback((isFullscreen => {
7035
+ },
7036
+ onFullscreen: isFullscreen => {
6971
7037
  setState((prev => ({
6972
7038
  ...prev,
6973
7039
  isFullscreen: isFullscreen
6974
7040
  }))), handlers.onFullscreen?.(isFullscreen);
6975
- }), [ handlers.onFullscreen ]),
6976
- onZoomIn: React.useCallback((() => {
7041
+ },
7042
+ onZoomIn: () => {
6977
7043
  setState((prev => ({
6978
7044
  ...prev,
6979
7045
  zoomLevel: Math.min(1.2 * prev.zoomLevel, 5)
6980
7046
  }))), handlers.onZoomIn?.();
6981
- }), [ handlers.onZoomIn ]),
6982
- onZoomOut: React.useCallback((() => {
7047
+ },
7048
+ onZoomOut: () => {
6983
7049
  setState((prev => ({
6984
7050
  ...prev,
6985
7051
  zoomLevel: Math.max(prev.zoomLevel / 1.2, .2)
6986
7052
  }))), handlers.onZoomOut?.();
6987
- }), [ handlers.onZoomOut ]),
6988
- onZoomReset: React.useCallback((() => {
7053
+ },
7054
+ onZoomReset: () => {
6989
7055
  setState((prev => ({
6990
7056
  ...prev,
6991
7057
  zoomLevel: 1
6992
7058
  }))), handlers.onZoomReset?.();
6993
- }), [ handlers.onZoomReset ]),
6994
- onPanToggle: React.useCallback((enabled => {
7059
+ },
7060
+ onPanToggle: enabled => {
6995
7061
  setState((prev => ({
6996
7062
  ...prev,
6997
7063
  panEnabled: enabled
6998
7064
  }))), handlers.onPanToggle?.(enabled);
6999
- }), [ handlers.onPanToggle ]),
7000
- onReset: React.useCallback((() => {
7065
+ },
7066
+ onReset: () => {
7001
7067
  setState((prev => ({
7002
7068
  ...prev,
7003
7069
  zoomLevel: 1,
7004
7070
  panEnabled: !1
7005
7071
  }))), handlers.onReset?.();
7006
- }), [ handlers.onReset ]),
7007
- onGridToggle: React.useCallback((show => {
7072
+ },
7073
+ onGridToggle: show => {
7008
7074
  setState((prev => ({
7009
7075
  ...prev,
7010
7076
  showGrid: show
7011
7077
  }))), handlers.onGridToggle?.(show);
7012
- }), [ handlers.onGridToggle ]),
7013
- onLegendToggle: React.useCallback((show => {
7078
+ },
7079
+ onLegendToggle: show => {
7014
7080
  setState((prev => ({
7015
7081
  ...prev,
7016
7082
  showLegend: show
7017
7083
  }))), handlers.onLegendToggle?.(show);
7018
- }), [ handlers.onLegendToggle ]),
7019
- onTooltipsToggle: React.useCallback((show => {
7084
+ },
7085
+ onTooltipsToggle: show => {
7020
7086
  setState((prev => ({
7021
7087
  ...prev,
7022
7088
  showTooltips: show
7023
7089
  }))), handlers.onTooltipsToggle?.(show);
7024
- }), [ handlers.onTooltipsToggle ]),
7025
- onAnimationsToggle: React.useCallback((enabled => {
7090
+ },
7091
+ onAnimationsToggle: enabled => {
7026
7092
  setState((prev => ({
7027
7093
  ...prev,
7028
7094
  animationsEnabled: enabled
7029
7095
  }))), handlers.onAnimationsToggle?.(enabled);
7030
- }), [ handlers.onAnimationsToggle ]),
7031
- onSettings: React.useCallback((() => {}), [])
7032
- }, generateToolbarGroups = React.useCallback((() => {
7096
+ },
7097
+ onSettings: () => {}
7098
+ })), [ handlers ]), generateToolbarGroups = React.useCallback((() => {
7033
7099
  const groups = [], dataActions = [];
7034
7100
  // Data actions group
7035
7101
  finalDefaults.refresh && dataActions.push({
@@ -7154,7 +7220,7 @@ toolbarConfig: toolbarConfig, customToolbarActions: customToolbarActions, custom
7154
7220
  actions: customActions
7155
7221
  });
7156
7222
  return groups;
7157
- }), [ chartType, finalDefaults, state, enhancedHandlers, customActions, customGroups ]);
7223
+ }), [ finalDefaults, state, enhancedHandlers, customActions, customGroups ]);
7158
7224
  // Keyboard shortcuts
7159
7225
  return React.useEffect((() => {
7160
7226
  const handleKeyDown = event => {
@@ -7746,7 +7812,7 @@ const ChartRenderer = React.memo( React.forwardRef((({datasets: datasets = [],
7746
7812
  announcement: announcement,
7747
7813
  focusedPoint: focusedPoint,
7748
7814
  getAccessibleDescription: () => "Chart description"
7749
- })), [ announcement, focusedPoint ]), transform = React.useMemo((() => chartContext ? `translate(${chartContext.panOffset.x}px, ${chartContext.panOffset.y}px) scale(${chartContext.zoomLevel})` : ""), [ chartContext?.panOffset.x, chartContext?.panOffset.y, chartContext?.zoomLevel ]), chartData = React.useMemo((() => {
7815
+ })), [ announcement, focusedPoint ]), transform = React.useMemo((() => chartContext ? `translate(${chartContext.panOffset.x}px, ${chartContext.panOffset.y}px) scale(${chartContext.zoomLevel})` : ""), [ chartContext ]), chartData = React.useMemo((() => {
7750
7816
  // Return null if dimensions not ready to prevent calculation with invalid dimensions
7751
7817
  if (!isInitialized || 0 === dimensions.width || 0 === dimensions.height) return null;
7752
7818
  const scales = calculateScales(processedData, dimensions.width, dimensions.height, void 0, config);
@@ -8159,7 +8225,7 @@ function useBarChart(datasets, options = {}) {
8159
8225
  opacity: .4
8160
8226
  } ]
8161
8227
  };
8162
- })) : []), [ options.useGradients ]), formatValue = React.useCallback((value => options.valueFormatter ? options.valueFormatter(value) : value.toString()), [ options.valueFormatter ]);
8228
+ })) : []), [ options.useGradients ]), formatValue = React.useCallback((value => options.valueFormatter ? options.valueFormatter(value) : value.toString()), [ options ]);
8163
8229
  return {
8164
8230
  // State
8165
8231
  hoveredBar: hoveredBar,
@@ -8185,7 +8251,7 @@ function useBarChart(datasets, options = {}) {
8185
8251
  y: horizontal ? y + height / 2 : y - 5
8186
8252
  };
8187
8253
  }
8188
- }), [ options.dataLabelPosition ]),
8254
+ }), [ options ]),
8189
8255
  // Handlers
8190
8256
  handleBarHover: handleBarHover,
8191
8257
  handleBarLeave: handleBarLeave,
@@ -8495,13 +8561,13 @@ const DonutChart = React.memo( React.forwardRef((({datasets: datasets = [], con
8495
8561
  roundedCorners: !0
8496
8562
  }, onDataPointClick: onDataPointClick, ...props}, ref) => {
8497
8563
  // Use the first dataset for donut chart
8498
- const dataset = datasets.length > 0 ? datasets[0] : {
8564
+ const dataset = React.useMemo((() => datasets.length > 0 ? datasets[0] : {
8499
8565
  label: "",
8500
8566
  data: []
8501
- }, chartData = React.useMemo((() => {
8567
+ }), [ datasets ]), chartData = React.useMemo((() => {
8502
8568
  if (!dataset?.data?.length) return null;
8503
8569
  // Filter out invalid data points
8504
- const validDataPoints = dataset?.data?.filter((point => "number" == typeof point.value && !isNaN(point.value) && isFinite(point.value) && point.value > 0));
8570
+ const validDataPoints = (dataset?.data || []).filter((point => "number" == typeof point.value && !isNaN(point.value) && isFinite(point.value) && point.value > 0));
8505
8571
  return validDataPoints.length ? {
8506
8572
  validDataPoints: validDataPoints
8507
8573
  } : null;
@@ -9460,7 +9526,7 @@ function usePieChart(data, options = {}) {
9460
9526
  const parts = [];
9461
9527
  return !1 !== options.showLabels && parts.push(slice.label), options.showPercentages && parts.push(`${Math.round(slice.percentage)}%`),
9462
9528
  options.showValues && parts.push(slice.value.toString()), parts.join(" - ");
9463
- }), [ options.labelFormatter, options.showLabels, options.showPercentages, options.showValues ]), getSliceTransform = React.useCallback(((slice, isHovered) => isHovered && options.enableHoverEffects && options.hoverOffset ? `translate(${Math.cos(slice.midAngle) * options.hoverOffset}, ${Math.sin(slice.midAngle) * options.hoverOffset})` : ""), [ options.enableHoverEffects, options.hoverOffset ]), isSliceSelected = React.useCallback((index => selectedSlices.has(index)), [ selectedSlices ]);
9529
+ }), [ options ]), getSliceTransform = React.useCallback(((slice, isHovered) => isHovered && options.enableHoverEffects && options.hoverOffset ? `translate(${Math.cos(slice.midAngle) * options.hoverOffset}, ${Math.sin(slice.midAngle) * options.hoverOffset})` : ""), [ options.enableHoverEffects, options.hoverOffset ]), isSliceSelected = React.useCallback((index => selectedSlices.has(index)), [ selectedSlices ]);
9464
9530
  return {
9465
9531
  // Data
9466
9532
  processedData: processedData,
@@ -10896,7 +10962,7 @@ const DataTable = React.memo((({data: data, columns: columns, className: classN
10896
10962
  const newOrder = columns.map((col => col.key)), currentOrderSet = new Set(columnOrder), newOrderSet = new Set(newOrder);
10897
10963
  // Only update if there are actual differences
10898
10964
  newOrder.length === columnOrder.length && newOrder.every((key => currentOrderSet.has(key))) && columnOrder.every((key => newOrderSet.has(key))) || setColumnOrder(newOrder);
10899
- }), [ columns ]),
10965
+ }), [ columns, columnOrder ]),
10900
10966
  // Update column visibility when columns prop changes
10901
10967
  React.useEffect((() => {
10902
10968
  setColumnVisibility((prev => {
@@ -11441,7 +11507,7 @@ function formatDate(date, format) {
11441
11507
  /**
11442
11508
  * Check if a date is within a min and max range
11443
11509
  */ function useDatePicker({value: value, onChange: onChange, selectionMode: selectionMode = "single", startDate: startDate, endDate: endDate, onRangeChange: onRangeChange, format: format = "MM/dd/yyyy", minDate: minDate, maxDate: maxDate, inline: inline = !1} = {}) {
11444
- const [isOpen, setIsOpen] = React.useState(inline), [inputValue, setInputValue] = React.useState(value ? formatDate(value, format) : ""), [rangeInputValue, setRangeInputValue] = React.useState(startDate && endDate ? `${formatDate(startDate, format)} - ${formatDate(endDate, format)}` : startDate ? `${formatDate(startDate, format)} - Select end date` : ""), [viewDate, setViewDate] = React.useState(value || startDate || new Date), [viewMode, setViewMode] = React.useState("days"), [rangeSelectionState, setRangeSelectionState] = React.useState(!startDate || startDate && endDate ? "start" : "end"), datePickerRef = React.useRef(null), inputRef = React.useRef(null), today = new Date, currentMonth = viewDate.getMonth(), currentYear = viewDate.getFullYear(), daysInMonth = getDaysInMonth(currentYear, currentMonth), firstDayOfMonth = new Date(currentYear, currentMonth, 1).getDay();
11510
+ const [isOpen, setIsOpen] = React.useState(inline), [inputValue, setInputValue] = React.useState(value ? formatDate(value, format) : ""), [rangeInputValue, setRangeInputValue] = React.useState(startDate && endDate ? `${formatDate(startDate, format)} - ${formatDate(endDate, format)}` : startDate ? `${formatDate(startDate, format)} - Select end date` : ""), [viewDate, setViewDate] = React.useState(value || startDate || new Date), [viewMode, setViewMode] = React.useState("days"), [rangeSelectionState, setRangeSelectionState] = React.useState(!startDate || startDate && endDate ? "start" : "end"), datePickerRef = React.useRef(null), inputRef = React.useRef(null), today = React.useMemo((() => new Date), []), currentMonth = viewDate.getMonth(), currentYear = viewDate.getFullYear(), daysInMonth = getDaysInMonth(currentYear, currentMonth), firstDayOfMonth = new Date(currentYear, currentMonth, 1).getDay();
11445
11511
  // Update input value when value or range dates change externally
11446
11512
  React.useEffect((() => {
11447
11513
  "single" === selectionMode ? setInputValue(value ? formatDate(value, format) : "") : (setRangeInputValue(startDate && endDate ? `${formatDate(startDate, format)} - ${formatDate(endDate, format)}` : startDate ? `${formatDate(startDate, format)} - Select end date` : ""),
@@ -11964,21 +12030,11 @@ const DatePicker = React.forwardRef((({value: value, onChange: onChange, select
11964
12030
  * @returns EdgePanel state and methods
11965
12031
  */
11966
12032
  function useEdgePanel(initialProps) {
11967
- // Default EdgePanel properties
11968
- const defaultProps = {
11969
- position: "start",
11970
- mode: "slide",
11971
- isOpen: !1,
11972
- backdrop: !0,
11973
- closeOnBackdropClick: !0,
11974
- closeOnEscape: !0,
11975
- glass: void 0,
11976
- ...initialProps
11977
- }, [isOpen, setIsOpen] = React.useState(defaultProps.isOpen || !1), containerRef = React.useRef(null), backdropRef = React.useRef(null), adjustBodyPadding = React.useCallback((() => {
11978
- if (!containerRef.current || "push" !== defaultProps.mode) return;
11979
- const {position: position} = defaultProps, size = "top" === position || "bottom" === position ? containerRef.current.clientHeight : containerRef.current.clientWidth;
12033
+ const {position: position = "start", mode: mode = "slide", isOpen: propIsOpen = !1, backdrop: backdrop = !0, closeOnBackdropClick: closeOnBackdropClick = !0, closeOnEscape: closeOnEscape = !0, glass: glass, onOpenChange: onOpenChange, className: className = ""} = initialProps || {}, [isOpen, setIsOpen] = React.useState(propIsOpen || !1), containerRef = React.useRef(null), backdropRef = React.useRef(null), adjustBodyPadding = React.useCallback((() => {
12034
+ if (!containerRef.current || "push" !== mode) return;
12035
+ const size = "top" === position || "bottom" === position ? containerRef.current.clientHeight : containerRef.current.clientWidth;
11980
12036
  // Map position to CSS padding property
11981
- let paddingProperty;
12037
+ let paddingProperty;
11982
12038
  switch (position) {
11983
12039
  case "start":
11984
12040
  paddingProperty = "paddingLeft";
@@ -11993,9 +12049,8 @@ function useEdgePanel(initialProps) {
11993
12049
  paddingProperty = `padding${position.charAt(0).toUpperCase() + position.slice(1)}`;
11994
12050
  }
11995
12051
  document.body.style[paddingProperty] = `${size}px`, document.body.classList.add("is-pushed");
11996
- }), [ defaultProps.mode, defaultProps.position ]), resetBodyPadding = React.useCallback((() => {
11997
- if ("push" !== defaultProps.mode) return;
11998
- const {position: position} = defaultProps;
12052
+ }), [ mode, position ]), resetBodyPadding = React.useCallback((() => {
12053
+ if ("push" !== mode) return;
11999
12054
  // Map position to CSS padding property
12000
12055
  let paddingProperty;
12001
12056
  switch (position) {
@@ -12012,11 +12067,10 @@ function useEdgePanel(initialProps) {
12012
12067
  paddingProperty = `padding${position.charAt(0).toUpperCase() + position.slice(1)}`;
12013
12068
  }
12014
12069
  document.body.style[paddingProperty] = "", document.body.classList.remove("is-pushed");
12015
- }), [ defaultProps.mode, defaultProps.position ]), openPanel = React.useCallback(((useFadeAnimation = !1) => {
12070
+ }), [ mode, position ]), openPanel = React.useCallback(((useFadeAnimation = !1) => {
12016
12071
  if (setIsOpen(!0), document.body.classList.add("is-edgepanel-open"), containerRef.current) {
12017
- const {mode: mode} = defaultProps;
12018
12072
  // Only add animation if not in 'none' mode
12019
- if ("none" !== mode) if (useFadeAnimation) {
12073
+ if ("none" !== mode) if (useFadeAnimation) {
12020
12074
  // Add fade animation class
12021
12075
  containerRef.current.classList.add("is-fade-animating"), containerRef.current.offsetHeight;
12022
12076
  // Remove animation class after animation completes
@@ -12036,14 +12090,13 @@ function useEdgePanel(initialProps) {
12036
12090
  // Set transform or opacity based on animation type
12037
12091
  useFadeAnimation ? (containerRef.current.style.opacity = "1", containerRef.current.style.transform = "") : containerRef.current.style.transform = "translate(0)",
12038
12092
  // If push mode, adjust body padding
12039
- "push" === defaultProps.mode && adjustBodyPadding();
12093
+ "push" === mode && adjustBodyPadding();
12040
12094
  }
12041
- defaultProps.onOpenChange && defaultProps.onOpenChange(!0);
12042
- }), [ defaultProps, adjustBodyPadding ]), closePanel = React.useCallback(((useFadeAnimation = !1) => {
12095
+ onOpenChange && onOpenChange(!0);
12096
+ }), [ mode, adjustBodyPadding, onOpenChange ]), closePanel = React.useCallback(((useFadeAnimation = !1) => {
12043
12097
  if (containerRef.current) {
12044
- const {position: position, mode: mode} = defaultProps;
12045
12098
  // Only add animation if not in 'none' mode
12046
- if ("none" !== mode) if (useFadeAnimation) {
12099
+ if ("none" !== mode) if (useFadeAnimation) {
12047
12100
  // Add fade out animation class
12048
12101
  containerRef.current.classList.add("is-fade-animating-out");
12049
12102
  // Capture container for setTimeout
@@ -12065,46 +12118,42 @@ function useEdgePanel(initialProps) {
12065
12118
  // Then set transform
12066
12119
  containerRef.current.style.transform = position ? EDGE_PANEL.TRANSFORM_VALUES[position] : "",
12067
12120
  // Reset body padding if push mode
12068
- "push" === defaultProps.mode && resetBodyPadding(), setTimeout((() => {
12069
- setIsOpen(!1), document.body.classList.remove("is-edgepanel-open"), defaultProps.onOpenChange && defaultProps.onOpenChange(!1);
12121
+ "push" === mode && resetBodyPadding(), setTimeout((() => {
12122
+ setIsOpen(!1), document.body.classList.remove("is-edgepanel-open"), onOpenChange && onOpenChange(!1);
12070
12123
  }), "none" === mode ? 0 : EDGE_PANEL.ANIMATION_DURATION);
12071
- } else setIsOpen(!1), document.body.classList.remove("is-edgepanel-open"), defaultProps.onOpenChange && defaultProps.onOpenChange(!1);
12072
- }), [ defaultProps, resetBodyPadding ]), handleEscapeKey = React.useCallback((event => {
12073
- defaultProps.closeOnEscape && "Escape" === event.key && isOpen && closePanel();
12074
- }), [ closePanel, defaultProps.closeOnEscape, isOpen ]), handleBackdropClick = React.useCallback((event => {
12075
- defaultProps.closeOnBackdropClick && event.target === event.currentTarget && closePanel();
12076
- }), [ closePanel, defaultProps.closeOnBackdropClick ]);
12124
+ } else setIsOpen(!1), document.body.classList.remove("is-edgepanel-open"), onOpenChange && onOpenChange(!1);
12125
+ }), [ mode, position, onOpenChange, resetBodyPadding ]), handleEscapeKey = React.useCallback((event => {
12126
+ closeOnEscape && "Escape" === event.key && isOpen && closePanel();
12127
+ }), [ closePanel, closeOnEscape, isOpen ]), handleBackdropClick = React.useCallback((event => {
12128
+ closeOnBackdropClick && event.target === event.currentTarget && closePanel();
12129
+ }), [ closePanel, closeOnBackdropClick ]);
12077
12130
  /**
12078
12131
  * Set up event listeners for keyboard events
12079
12132
  */
12080
- return React.useEffect((() => (isOpen && defaultProps.closeOnEscape && document.addEventListener("keydown", handleEscapeKey),
12133
+ return React.useEffect((() => (isOpen && closeOnEscape && document.addEventListener("keydown", handleEscapeKey),
12081
12134
  () => {
12082
12135
  document.removeEventListener("keydown", handleEscapeKey);
12083
- })), [ isOpen, handleEscapeKey, defaultProps.closeOnEscape ]),
12136
+ })), [ isOpen, handleEscapeKey, closeOnEscape ]),
12084
12137
  /**
12085
12138
  * Set initial transform values
12086
12139
  */
12087
12140
  React.useEffect((() => {
12088
- if (containerRef.current) {
12089
- const {position: position, mode: mode} = defaultProps;
12090
- isOpen || "slide" !== mode && "push" !== mode || !position || (containerRef.current.style.transform = EDGE_PANEL.TRANSFORM_VALUES[position],
12091
- // Set initial opacity for fade animations
12092
- defaultProps.glass && (containerRef.current.style.opacity = "0"));
12093
- }
12094
- }), [ defaultProps.mode, defaultProps.position, defaultProps.glass, isOpen ]),
12141
+ containerRef.current && (isOpen || "slide" !== mode && "push" !== mode || !position || (containerRef.current.style.transform = EDGE_PANEL.TRANSFORM_VALUES[position],
12142
+ // Set initial opacity for fade animations
12143
+ glass && (containerRef.current.style.opacity = "0")));
12144
+ }), [ mode, position, glass, isOpen ]),
12095
12145
  /**
12096
12146
  * Sync with prop changes
12097
12147
  */
12098
12148
  React.useEffect((() => {
12099
- void 0 !== defaultProps.isOpen && defaultProps.isOpen !== isOpen && (defaultProps.isOpen ? openPanel(!!defaultProps.glass) : closePanel(!!defaultProps.glass));
12100
- }), [ defaultProps.isOpen, closePanel, isOpen, openPanel, defaultProps.glass ]),
12101
- {
12149
+ void 0 !== propIsOpen && propIsOpen !== isOpen && (propIsOpen ? openPanel(!!glass) : closePanel(!!glass));
12150
+ }), [ propIsOpen, closePanel, isOpen, openPanel, glass ]), {
12102
12151
  isOpen: isOpen,
12103
12152
  containerRef: containerRef,
12104
12153
  backdropRef: backdropRef,
12105
12154
  generateEdgePanelClass: props => {
12106
- const {position: position = defaultProps.position, className: className = "", isOpen: propIsOpen} = props, baseClass = EDGE_PANEL.CLASSES.BASE;
12107
- return `${baseClass} ${position ? `${baseClass}--${position}` : ""} ${propIsOpen ?? isOpen ? EDGE_PANEL.CLASSES.IS_OPEN : ""} ${className}`.trim();
12155
+ const {position: propPosition = position, className: propClassName = className, isOpen: argIsOpen} = props, baseClass = EDGE_PANEL.CLASSES.BASE;
12156
+ return `${baseClass} ${propPosition ? `${baseClass}--${propPosition}` : ""} ${argIsOpen ?? isOpen ? EDGE_PANEL.CLASSES.IS_OPEN : ""} ${propClassName}`.trim();
12108
12157
  },
12109
12158
  openPanel: openPanel,
12110
12159
  closePanel: closePanel,
@@ -12155,7 +12204,7 @@ const EdgePanelCloseButton = React.forwardRef((({className: className = "", onC
12155
12204
 
12156
12205
  EdgePanelCloseButton.displayName = "EdgePanelCloseButton";
12157
12206
 
12158
- const EdgePanel = React.memo((({title: title, children: children, position: position = "start", mode: mode = "slide", isOpen: isOpen = !1, onOpenChange: onOpenChange, backdrop: backdrop = !0, closeOnBackdropClick: closeOnBackdropClick = !0, closeOnEscape: closeOnEscape = !0, className: className = "", style: style, glass: glass}) => {
12207
+ const EdgePanelComponentBase = ({title: title, children: children, position: position = "start", mode: mode = "slide", isOpen: isOpen = !1, onOpenChange: onOpenChange, backdrop: backdrop = !0, closeOnBackdropClick: closeOnBackdropClick = !0, closeOnEscape: closeOnEscape = !0, className: className = "", style: style, glass: glass}) => {
12159
12208
  const {isOpen: isOpenState, containerRef: containerRef, backdropRef: backdropRef, generateEdgePanelClass: generateEdgePanelClass, closePanel: closePanel, handleBackdropClick: handleBackdropClick} = useEdgePanel({
12160
12209
  position: position,
12161
12210
  mode: mode,
@@ -12231,7 +12280,7 @@ const EdgePanel = React.memo((({title: title, children: children, position: pos
12231
12280
  }) : panelContent
12232
12281
  }) ]
12233
12282
  });
12234
- }));
12283
+ }, EdgePanel = React.memo(EdgePanelComponentBase);
12235
12284
 
12236
12285
  /**
12237
12286
  * Form state and functionality
@@ -12486,7 +12535,7 @@ function useHero(initialProps) {
12486
12535
  * @returns Slider state and methods
12487
12536
  */
12488
12537
  function(config) {
12489
- const {slides: slides, autoplay: autoplay, loop: loop = !0, transition: transition = "fade", transitionDuration: transitionDuration = 1e3} = config, [currentIndex, setCurrentIndex] = React.useState(0), [isTransitioning, setIsTransitioning] = React.useState(!1), autoplayRef = React.useRef(null), isPausedRef = React.useRef(!1), callbackRef = React.useRef(), slideRefs = React.useMemo((() => slides.map((() => React__default.default.createRef()))), [ slides.length ]), videoRefs = React.useMemo((() => slides.map((() => React__default.default.createRef()))), [ slides.length ]), handleSlideTransition = React.useCallback((nextIndex => {
12538
+ const {slides: slides, autoplay: autoplay, loop: loop = !0, transition: transition = "fade", transitionDuration: transitionDuration = 1e3} = config, [currentIndex, setCurrentIndex] = React.useState(0), [isTransitioning, setIsTransitioning] = React.useState(!1), autoplayRef = React.useRef(null), isPausedRef = React.useRef(!1), callbackRef = React.useRef(void 0), slideRefs = React.useMemo((() => slides.map((() => React__default.default.createRef()))), [ slides ]), videoRefs = React.useMemo((() => slides.map((() => React__default.default.createRef()))), [ slides ]), handleSlideTransition = React.useCallback((nextIndex => {
12490
12539
  if (nextIndex === currentIndex || isTransitioning) return;
12491
12540
  if (nextIndex < 0 || nextIndex >= slides.length) return;
12492
12541
  setIsTransitioning(!0),
@@ -12805,22 +12854,15 @@ function useHero(initialProps) {
12805
12854
  * @param initialProps - Initial side menu properties
12806
12855
  * @returns SideMenu state and methods
12807
12856
  */ function useSideMenu(initialProps) {
12808
- // Default side menu properties
12809
- const defaultProps = {
12810
- collapsible: !0,
12811
- collapsibleDesktop: !1,
12812
- defaultCollapsedDesktop: !1,
12813
- isOpen: !1,
12814
- ...initialProps
12815
- }, [isOpenState, setIsOpenState] = React.useState(void 0 !== defaultProps.defaultCollapsedDesktop ? !defaultProps.defaultCollapsedDesktop : defaultProps.isOpen || !1), wrapperRef = React.useRef(null), innerRef = React.useRef(null), sideMenuRef = React.useRef(null);
12857
+ const {collapsible: collapsible = !0, collapsibleDesktop: collapsibleDesktop = !1, defaultCollapsedDesktop: defaultCollapsedDesktop = !1, isOpen: isOpen, onToggle: onToggle, disabled: disabled = !1} = initialProps || {}, [isOpenState, setIsOpenState] = React.useState(void 0 !== defaultCollapsedDesktop ? !defaultCollapsedDesktop : isOpen || !1), wrapperRef = React.useRef(null), innerRef = React.useRef(null), sideMenuRef = React.useRef(null);
12816
12858
  // Local open state for when not controlled externally
12817
12859
  // Update local state when external state changes
12818
12860
  React.useEffect((() => {
12819
- void 0 !== defaultProps.isOpen ? setIsOpenState(defaultProps.isOpen) : void 0 !== defaultProps.defaultCollapsedDesktop && setIsOpenState(!defaultProps.defaultCollapsedDesktop);
12820
- }), [ defaultProps.isOpen, defaultProps.defaultCollapsedDesktop ]),
12861
+ void 0 !== isOpen ? setIsOpenState(isOpen) : void 0 !== defaultCollapsedDesktop && setIsOpenState(!defaultCollapsedDesktop);
12862
+ }), [ isOpen, defaultCollapsedDesktop ]),
12821
12863
  // Set initial height on mount
12822
12864
  React.useEffect((() => {
12823
- const shouldCollapse = window.innerWidth < 768 ? defaultProps.collapsible : defaultProps.collapsibleDesktop, currentOpen = void 0 !== defaultProps.isOpen ? defaultProps.isOpen : isOpenState;
12865
+ const shouldCollapse = window.innerWidth < 768 ? collapsible : collapsibleDesktop, currentOpen = void 0 !== isOpen ? isOpen : isOpenState;
12824
12866
  if (shouldCollapse && wrapperRef.current && innerRef.current) {
12825
12867
  // Use setTimeout to ensure DOM is fully rendered
12826
12868
  const timeoutId = setTimeout((() => {
@@ -12829,14 +12871,14 @@ function useHero(initialProps) {
12829
12871
  return () => clearTimeout(timeoutId);
12830
12872
  }
12831
12873
  !shouldCollapse && wrapperRef.current && (wrapperRef.current.style.height = "auto");
12832
- }), []), // Only run on mount
12874
+ }), [ collapsible, collapsibleDesktop, isOpen, isOpenState ]),
12833
12875
  // Handle responsive behavior - vertical collapse for both mobile and desktop
12834
12876
  React.useEffect((() => {
12835
12877
  const handleResize = () => {
12836
- if (window.innerWidth < 768 ? defaultProps.collapsible : defaultProps.collapsibleDesktop) {
12878
+ if (window.innerWidth < 768 ? collapsible : collapsibleDesktop) {
12837
12879
  if (wrapperRef.current && innerRef.current) {
12838
12880
  // Set proper height for vertical animation (both mobile and desktop)
12839
- const currentOpen = void 0 !== defaultProps.isOpen ? defaultProps.isOpen : isOpenState;
12881
+ const currentOpen = void 0 !== isOpen ? isOpen : isOpenState;
12840
12882
  // Use requestAnimationFrame to ensure DOM is ready
12841
12883
  requestAnimationFrame((() => {
12842
12884
  wrapperRef.current && innerRef.current && (wrapperRef.current.style.height = currentOpen ? `${innerRef.current.scrollHeight}px` : "0px");
@@ -12850,12 +12892,12 @@ function useHero(initialProps) {
12850
12892
  return window.addEventListener("resize", handleResize), () => {
12851
12893
  clearTimeout(timeoutId), window.removeEventListener("resize", handleResize);
12852
12894
  };
12853
- }), [ defaultProps.collapsible, defaultProps.collapsibleDesktop, defaultProps.isOpen, defaultProps.onToggle, isOpenState ]),
12895
+ }), [ collapsible, collapsibleDesktop, isOpen, onToggle, isOpenState ]),
12854
12896
  // Update wrapper height when open state changes (both mobile and desktop)
12855
12897
  React.useEffect((() => {
12856
- const shouldCollapse = window.innerWidth < 768 ? defaultProps.collapsible : defaultProps.collapsibleDesktop;
12898
+ const shouldCollapse = window.innerWidth < 768 ? collapsible : collapsibleDesktop;
12857
12899
  if (shouldCollapse && wrapperRef.current && innerRef.current) {
12858
- const currentOpen = void 0 !== defaultProps.isOpen ? defaultProps.isOpen : isOpenState;
12900
+ const currentOpen = void 0 !== isOpen ? isOpen : isOpenState;
12859
12901
  // Use requestAnimationFrame to ensure DOM is ready
12860
12902
  requestAnimationFrame((() => {
12861
12903
  wrapperRef.current && innerRef.current && (wrapperRef.current.style.height = currentOpen ? `${innerRef.current.scrollHeight}px` : "0px");
@@ -12863,26 +12905,25 @@ function useHero(initialProps) {
12863
12905
  } else !shouldCollapse && wrapperRef.current && (
12864
12906
  // Not collapsible - always show content
12865
12907
  wrapperRef.current.style.height = "auto");
12866
- }), [ defaultProps.isOpen, isOpenState, defaultProps.collapsible, defaultProps.collapsibleDesktop ]);
12908
+ }), [ isOpen, isOpenState, collapsible, collapsibleDesktop ]);
12867
12909
  /**
12868
12910
  * Generate side menu class based on properties
12869
12911
  * @param props - Side menu properties
12870
12912
  * @returns Class string
12871
12913
  */
12872
12914
  const handleToggle = () => {
12873
- if (defaultProps.disabled) return;
12874
- const newState = void 0 !== defaultProps.isOpen ? !defaultProps.isOpen : !isOpenState;
12875
- "function" == typeof defaultProps.onToggle ?
12915
+ if (disabled) return;
12916
+ const newState = void 0 !== isOpen ? !isOpen : !isOpenState;
12917
+ "function" == typeof onToggle ?
12876
12918
  // Controlled component
12877
- defaultProps.onToggle(newState) :
12919
+ onToggle(newState) :
12878
12920
  // Uncontrolled component
12879
12921
  setIsOpenState(newState);
12880
- }, getCurrentOpenState = () => void 0 !== defaultProps.isOpen ? defaultProps.isOpen : isOpenState;
12922
+ }, getCurrentOpenState = () => void 0 !== isOpen ? isOpen : isOpenState;
12881
12923
  /**
12882
12924
  * Generate wrapper class
12883
12925
  * @returns Class string
12884
12926
  */ return {
12885
- defaultProps: defaultProps,
12886
12927
  isOpenState: getCurrentOpenState(),
12887
12928
  wrapperRef: wrapperRef,
12888
12929
  innerRef: innerRef,
@@ -13465,21 +13506,21 @@ const DEFAULT_ATOMIX_FONTS = [ {
13465
13506
  const [interaction, setInteraction] = React.useState({
13466
13507
  hoveredIndex: null,
13467
13508
  selectedIndex: null
13468
- });
13509
+ }), handlePointHover = React.useCallback((index => {
13510
+ setInteraction((prev => ({
13511
+ ...prev,
13512
+ hoveredIndex: index
13513
+ })));
13514
+ }), []), handlePointClick = React.useCallback((index => {
13515
+ setInteraction((prev => ({
13516
+ ...prev,
13517
+ selectedIndex: prev.selectedIndex === index ? null : index
13518
+ })));
13519
+ }), []);
13469
13520
  return {
13470
13521
  interaction: interaction,
13471
- handlePointHover: React.useCallback((index => {
13472
- setInteraction((prev => ({
13473
- ...prev,
13474
- hoveredIndex: index
13475
- })));
13476
- }), []),
13477
- handlePointClick: React.useCallback((index => {
13478
- setInteraction((prev => ({
13479
- ...prev,
13480
- selectedIndex: prev.selectedIndex === index ? null : index
13481
- })));
13482
- }), []),
13522
+ handlePointHover: handlePointHover,
13523
+ handlePointClick: handlePointClick,
13483
13524
  clearInteraction: React.useCallback((() => {
13484
13525
  setInteraction({
13485
13526
  hoveredIndex: null,
@@ -13522,7 +13563,7 @@ var composablesImport = Object.freeze({
13522
13563
  useNavDropdown: useNavDropdown,
13523
13564
  useNavItem: useNavItem,
13524
13565
  useNavbar: useNavbar,
13525
- usePerformanceMonitor: usePerformanceMonitor,
13566
+ usePerformanceMonitor: usePerformanceMonitor$1,
13526
13567
  usePieChart: usePieChart,
13527
13568
  useRadio: useRadio,
13528
13569
  useResponsiveGlass: useResponsiveGlass,
@@ -13585,7 +13626,7 @@ SelectOption.displayName = "SelectOption";
13585
13626
  /**
13586
13627
  * Select - A component for dropdown selection
13587
13628
  */
13588
- const Select = React.memo((({options: options, value: value, onChange: onChange, onBlur: onBlur, onFocus: onFocus, placeholder: placeholder = "Select an option", className: className = "", style: style, disabled: disabled = !1, required: required = !1, id: id, name: name, size: size = "md", invalid: invalid = !1, valid: valid = !1, multiple: multiple = !1, "aria-label": ariaLabel, "aria-describedby": ariaDescribedBy, glass: glass, children: children}) => {
13629
+ const SelectComponentBase = ({options: options, value: value, onChange: onChange, onBlur: onBlur, onFocus: onFocus, placeholder: placeholder = "Select an option", className: className = "", style: style, disabled: disabled = !1, required: required = !1, id: id, name: name, size: size = "md", invalid: invalid = !1, valid: valid = !1, multiple: multiple = !1, "aria-label": ariaLabel, "aria-describedby": ariaDescribedBy, glass: glass, children: children}) => {
13589
13630
  const {generateSelectClass: generateSelectClass} = useSelect({
13590
13631
  size: size,
13591
13632
  disabled: disabled,
@@ -13776,7 +13817,7 @@ const Select = React.memo((({options: options, value: value, onChange: onChange
13776
13817
  });
13777
13818
  }
13778
13819
  return selectContent;
13779
- }));
13820
+ }, Select = React.memo(SelectComponentBase);
13780
13821
 
13781
13822
  Select.displayName = "Select", Select.Option = SelectOption;
13782
13823
 
@@ -14343,7 +14384,7 @@ Footer.displayName = "Footer";
14343
14384
  */
14344
14385
  const MasonryGrid = React.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) => {
14345
14386
  // === REFS & STATE ===
14346
- const [columns, setColumns] = React.useState(xs), [positions, setPositions] = React.useState([]), [layoutComplete, setLayoutComplete] = React.useState(!1), [loadingImages, setLoadingImages] = React.useState(!1), containerRef = React.useRef(null), columnHeights = React.useRef([]), imagesLoadedCount = React.useRef(0), totalImagesCount = React.useRef(0), imageElements = React.useRef(new Map);
14387
+ const [columns, setColumns] = React.useState(xs), [positions, setPositions] = React.useState([]), [, setLayoutComplete] = React.useState(!1), [loadingImages, setLoadingImages] = React.useState(!1), containerRef = React.useRef(null), columnHeights = React.useRef([]), imagesLoadedCount = React.useRef(0), totalImagesCount = React.useRef(0), imageElements = React.useRef(new Map);
14347
14388
  React.useEffect((() => {
14348
14389
  setLoadingImages(!!imagesLoaded);
14349
14390
  }), [ columns, imagesLoaded ]),
@@ -14372,34 +14413,45 @@ const MasonryGrid = React.forwardRef((({children: children, className: classNam
14372
14413
  });
14373
14414
  })), setItems(newItems);
14374
14415
  }), [ children ]);
14375
- // === TRACK & MANAGE IMAGES ===
14376
- const handleImageLoad = React.useCallback((img => {
14377
- if (!imageElements.current.get(img)) {
14378
- // Add loaded class for animation
14379
- if (imageElements.current.set(img, !0), imagesLoadedCount.current += 1, containerRef.current && imagesLoaded) {
14380
- const itemElement = img.closest(".o-masonry-grid > div");
14381
- itemElement && (itemElement.offsetHeight, itemElement.classList.add("o-masonry-grid__item-loaded"),
14382
- itemElement.classList.remove("o-masonry-grid__item-loading"));
14416
+ // === MANAGE ITEM LAYOUT ===
14417
+ const calculateLayout = React.useCallback((() => {
14418
+ if (!containerRef.current || 0 === items.length) return;
14419
+ const colWidth = (containerRef.current.offsetWidth - gap * (columns - 1)) / columns;
14420
+ columnHeights.current = Array(columns).fill(0);
14421
+ const newPositions = [];
14422
+ items.forEach(((item, index) => {
14423
+ if (item.ref.current) {
14424
+ // Find the shortest column
14425
+ const shortestCol = columnHeights.current.indexOf(Math.min(...columnHeights.current)), left = shortestCol * (colWidth + gap), top = columnHeights.current[shortestCol] ?? 0, height = item.ref.current.offsetHeight;
14426
+ columnHeights.current[shortestCol] = top + height + gap, newPositions[index] = {
14427
+ left: left,
14428
+ top: top,
14429
+ width: colWidth,
14430
+ height: height
14431
+ };
14383
14432
  }
14384
- // Ensure layout is recalculated after DOM paints the item image (prevents overlap on slow/late image loads)
14385
- requestAnimationFrame((() => {
14386
- requestAnimationFrame((() => {
14387
- calculateLayout();
14388
- }));
14389
- })), onImageLoad?.(imagesLoadedCount.current, totalImagesCount.current),
14390
- // If all images have loaded, update loading state and complete layout
14391
- imagesLoadedCount.current >= totalImagesCount.current && totalImagesCount.current > 0 && (setLayoutComplete(!0),
14392
- setLoadingImages(!1), // This ensures the loading class is removed *immediately* after images load
14393
- // Force a double requestAnimationFrame for final layout calculation after all images are loaded (guarantees DOM paint)
14394
- requestAnimationFrame((() => {
14395
- requestAnimationFrame((() => {
14396
- calculateLayout(),
14397
- // As a failsafe, if still present for some render lag, force another setLoadingImages(false)
14398
- setLoadingImages(!1);
14399
- }));
14400
- })), onLayoutComplete?.());
14433
+ })), setPositions(newPositions);
14434
+ }), [ items, columns, gap ]), handleImageLoad = React.useCallback((img => {
14435
+ if (imageElements.current.get(img)) return;
14436
+ // Add loaded class for animation
14437
+ if (imageElements.current.set(img, !0), imagesLoadedCount.current += 1, containerRef.current && imagesLoaded) {
14438
+ const itemElement = img.closest(".o-masonry-grid > div");
14439
+ itemElement && (itemElement.offsetHeight, itemElement.classList.add("o-masonry-grid__item-loaded"),
14440
+ itemElement.classList.remove("o-masonry-grid__item-loading"));
14401
14441
  }
14402
- }), [ onImageLoad, onLayoutComplete, imagesLoaded ]), trackImages = React.useCallback((() => {
14442
+ // Schedule layout recalculation after next paint to prevent overlap
14443
+ const scheduleLayoutUpdate = () => {
14444
+ const frameId = requestAnimationFrame((() => {
14445
+ onImageLoad?.(imagesLoadedCount.current, totalImagesCount.current), calculateLayout();
14446
+ }));
14447
+ return () => cancelAnimationFrame(frameId);
14448
+ }, cleanup = scheduleLayoutUpdate();
14449
+ // Clean up previous scheduled updates
14450
+ // If all images have loaded, update loading state and complete layout
14451
+ imagesLoadedCount.current >= totalImagesCount.current && totalImagesCount.current > 0 && (setLayoutComplete(!0),
14452
+ setLoadingImages(!1), setTimeout((() => cleanup()), 0), // Clean up after current execution
14453
+ scheduleLayoutUpdate(), onLayoutComplete?.());
14454
+ }), [ onImageLoad, onLayoutComplete, imagesLoaded, calculateLayout ]), trackImages = React.useCallback((() => {
14403
14455
  if (!imagesLoaded || !containerRef.current) return;
14404
14456
  imageElements.current.clear(), imagesLoadedCount.current = 0;
14405
14457
  const images = containerRef.current.querySelectorAll("img");
@@ -14418,30 +14470,21 @@ const MasonryGrid = React.forwardRef((({children: children, className: classNam
14418
14470
  img.removeEventListener("error", masonryImg._masonryLoadHandler), delete masonryImg._masonryLoadHandler);
14419
14471
  }));
14420
14472
  });
14421
- }), [ imagesLoaded, handleImageLoad, onLayoutComplete ]), calculateLayout = React.useCallback((() => {
14422
- if (!containerRef.current || 0 === items.length) return;
14423
- const colWidth = (containerRef.current.offsetWidth - gap * (columns - 1)) / columns;
14424
- columnHeights.current = Array(columns).fill(0);
14425
- const newPositions = [];
14426
- items.forEach(((item, index) => {
14427
- if (item.ref.current) {
14428
- // Find the shortest column
14429
- const shortestCol = columnHeights.current.indexOf(Math.min(...columnHeights.current)), left = shortestCol * (colWidth + gap), top = columnHeights.current[shortestCol] ?? 0, height = item.ref.current.offsetHeight;
14430
- columnHeights.current[shortestCol] = top + height + gap, newPositions[index] = {
14431
- left: left,
14432
- top: top,
14433
- width: colWidth,
14434
- height: height
14435
- };
14436
- }
14437
- })), setPositions(newPositions);
14438
- }), [ items, columns, gap ]);
14439
- // === OBSERVE CONTAINER RESIZE ===
14473
+ }), [ imagesLoaded, handleImageLoad, onLayoutComplete ]);
14474
+ // === TRACK & MANAGE IMAGES ===
14475
+ // === OBSERVE CONTAINER RESIZE ===
14440
14476
  React.useEffect((() => {
14441
14477
  if (!containerRef.current) return;
14442
- let animationFrame = null;
14443
- const observer = new ResizeObserver((() => {
14444
- animationFrame && cancelAnimationFrame(animationFrame), animationFrame = requestAnimationFrame((() => calculateLayout()));
14478
+ let animationFrame = null, lastWidth = 0;
14479
+ const observer = new ResizeObserver((entries => {
14480
+ const entry = entries[0];
14481
+ if (!entry) return;
14482
+ const currentWidth = entry.contentRect.width;
14483
+ // Only recalculate if width actually changed (prevents excessive calculations)
14484
+ Math.abs(currentWidth - lastWidth) > 1 && (animationFrame && cancelAnimationFrame(animationFrame),
14485
+ animationFrame = requestAnimationFrame((() => {
14486
+ calculateLayout(), lastWidth = currentWidth;
14487
+ })));
14445
14488
  }));
14446
14489
  return observer.observe(containerRef.current), () => {
14447
14490
  observer.disconnect(), animationFrame && cancelAnimationFrame(animationFrame);
@@ -14452,24 +14495,21 @@ const MasonryGrid = React.forwardRef((({children: children, className: classNam
14452
14495
  setLayoutComplete(!0), void setLoadingImages(!1))
14453
14496
  // Only reset layoutComplete when items or columns change
14454
14497
  ), [ items, columns, calculateLayout, imagesLoaded, trackImages ]),
14455
- // === NEW: Add ResizeObservers to all grid items for bulletproof image+content measurement ===
14498
+ // === ADD RESIZEOBSERVERS TO GRID ITEMS FOR DYNAMIC CONTENT MEASUREMENT ===
14456
14499
  React__default.default.useEffect((() => {
14457
- // Clean up old observers if items ever change
14458
14500
  const observers = [];
14501
+ let animationFrame = null;
14502
+ // Debounced layout calculation for item resize events
14503
+ const debouncedCalculateLayout = () => {
14504
+ animationFrame && cancelAnimationFrame(animationFrame), animationFrame = requestAnimationFrame(calculateLayout);
14505
+ };
14459
14506
  return items.forEach((item => {
14460
14507
  if (item.ref.current) {
14461
- const obs = new ResizeObserver((() => {
14462
- // Double rAF: ensures layout only runs after DOM/paint/async renders
14463
- requestAnimationFrame((() => {
14464
- requestAnimationFrame((() => {
14465
- calculateLayout();
14466
- }));
14467
- }));
14468
- }));
14508
+ const obs = new ResizeObserver(debouncedCalculateLayout);
14469
14509
  obs.observe(item.ref.current), observers.push(obs);
14470
14510
  }
14471
14511
  })), () => {
14472
- observers.forEach((obs => obs.disconnect()));
14512
+ observers.forEach((obs => obs.disconnect())), animationFrame && cancelAnimationFrame(animationFrame);
14473
14513
  };
14474
14514
  }), [ items, calculateLayout ]);
14475
14515
  // Ensure loadingImages state resets when items/columns/imagesLoaded change
@@ -14954,7 +14994,7 @@ const ListItem = React.forwardRef((({children: children, className: className =
14954
14994
 
14955
14995
  ListItem.displayName = "ListItem";
14956
14996
 
14957
- const List = React.memo((({children: children, variant: variant = "default", className: className = "", style: style, ...props}) => {
14997
+ const ListComponentBase = ({children: children, variant: variant = "default", className: className = "", style: style, ...props}) => {
14958
14998
  var _context;
14959
14999
  // Generate CSS classes
14960
15000
  const listClasses = [ LIST.BASE_CLASS, "default" !== variant && `c-list--${variant}`, className ].filter(Boolean).join(" "), ListElement = _includesInstanceProperty(_context = [ "number", "text" ]).call(_context, variant) ? "ol" : "ul";
@@ -14968,7 +15008,7 @@ const List = React.memo((({children: children, variant: variant = "default", cl
14968
15008
  children: child
14969
15009
  })))
14970
15010
  });
14971
- }));
15011
+ }, List = React.memo(ListComponentBase);
14972
15012
 
14973
15013
  List.displayName = "List", List.Item = ListItem;
14974
15014
 
@@ -15919,12 +15959,12 @@ const SideMenu = React.forwardRef((({title: title, children: children, menuItem
15919
15959
  const index = Number(key);
15920
15960
  index >= currentLength && (delete nestedWrapperRefs.current[index], delete nestedInnerRefs.current[index]);
15921
15961
  })));
15922
- }), [ menuItems?.length ]);
15962
+ }), [ menuItems ]);
15923
15963
  // Helper function to update nested wrapper height
15924
- const updateNestedHeight = (index, isOpen) => {
15964
+ const updateNestedHeight = React.useCallback(((index, isOpen) => {
15925
15965
  const wrapper = nestedWrapperRefs.current[index], inner = nestedInnerRefs.current[index];
15926
15966
  wrapper && inner && (wrapper.style.height = isOpen ? `${inner.scrollHeight}px` : "0px");
15927
- };
15967
+ }), []);
15928
15968
  // Set initial heights for nested wrappers on mount and when menuItems change
15929
15969
  React.useEffect((() => {
15930
15970
  if (!menuItems?.length) return;
@@ -15938,7 +15978,7 @@ const SideMenu = React.forwardRef((({title: title, children: children, menuItem
15938
15978
  // Only run when menuItems change, nestedItemStates is read but not in deps to avoid loops
15939
15979
  // eslint-disable-next-line react-hooks/exhaustive-deps
15940
15980
  ;
15941
- }), [ menuItems?.length ]),
15981
+ }), [ menuItems, updateNestedHeight ]),
15942
15982
  // Update nested wrapper heights when state changes
15943
15983
  React.useEffect((() => {
15944
15984
  if (!menuItems?.length) return;
@@ -15951,7 +15991,7 @@ const SideMenu = React.forwardRef((({title: title, children: children, menuItem
15951
15991
  })), () => {
15952
15992
  frameIds.forEach((id => cancelAnimationFrame(id)));
15953
15993
  };
15954
- }), [ nestedItemStates, menuItems?.length ]);
15994
+ }), [ nestedItemStates, menuItems, updateNestedHeight ]);
15955
15995
  // Combine refs using utility
15956
15996
  const combinedRef = useForkRef(sideMenuRef, ref), sideMenuClass = generateSideMenuClass({
15957
15997
  className: className,
@@ -16854,7 +16894,7 @@ React.useEffect((() => {
16854
16894
  }
16855
16895
  };
16856
16896
  }));
16857
- }), [ isMounted, currentIndex, calculateBounds, constrainPosition ]), setImagePosition = React.useCallback((position => {
16897
+ }), [ currentIndex, calculateBounds, constrainPosition ]), setImagePosition = React.useCallback((position => {
16858
16898
  setImageStates((prev => {
16859
16899
  const currentState = prev[currentIndex] || {
16860
16900
  zoomLevel: 1,
@@ -16904,7 +16944,7 @@ React.useEffect((() => {
16904
16944
  }
16905
16945
  };
16906
16946
  }));
16907
- }), [ isMounted, currentIndex, calculateBounds, constrainPosition ]), handleWheel = React.useCallback((event => {
16947
+ }), [ currentIndex, calculateBounds, constrainPosition ]), handleWheel = React.useCallback((event => {
16908
16948
  var _context;
16909
16949
  if (!isMounted || !event || !event.currentTarget) return;
16910
16950
  // Additional safety check for the target element
@@ -17261,7 +17301,7 @@ React.useEffect((() => {
17261
17301
  }
17262
17302
  return prev;
17263
17303
  }));
17264
- }), [ isMounted, enableGestures, isDragging, startDragPosition, currentIndex, constrainPosition, calculateBounds ]), handleTouchEnd = React.useCallback((() => {
17304
+ }), [ enableGestures, isDragging, startDragPosition, currentIndex, constrainPosition, calculateBounds ]), handleTouchEnd = React.useCallback((() => {
17265
17305
  setIsDragging(!1), lastDistanceRef.current = null, lastMidpointRef.current = null;
17266
17306
  }), []), currentState = imageStates[currentIndex] || {
17267
17307
  zoomLevel: 1,
@@ -17465,9 +17505,9 @@ const PopoverContext = React.createContext({
17465
17505
  triggerType: "click"
17466
17506
  }), Popover = ({content: content, position: position = "top", trigger: trigger = "click", className: className = "", style: style, delay: delay = 0, offset: offset = 12, defaultOpen: defaultOpen = !1, isOpen: controlledIsOpen, onOpenChange: onOpenChange, closeOnClickOutside: closeOnClickOutside = !0, closeOnEscape: closeOnEscape = !0, id: id, children: children, glass: glass}) => {
17467
17507
  const {isOpen: isOpen, setIsOpen: setIsOpen, triggerRef: triggerRef, popoverRef: popoverRef, arrowRef: arrowRef, popoverId: popoverId, currentPosition: currentPosition, updatePosition: updatePosition} = (({position: position = "top", trigger: trigger = "click", offset: offset = 12, delay: delay = 0, defaultOpen: defaultOpen = !1, isOpen: controlledIsOpen, onOpenChange: onOpenChange, closeOnClickOutside: closeOnClickOutside = !0, closeOnEscape: closeOnEscape = !0, id: id}) => {
17468
- const [isOpen, setIsOpenState] = React.useState(defaultOpen), [currentPosition, setCurrentPosition] = React.useState("auto" === position ? "top" : position), triggerRef = React.useRef(null), popoverRef = React.useRef(null), arrowRef = React.useRef(null), timeoutRef = React.useRef(null), popoverId = id || `popover-${Math.random().toString(36).slice(2, 11)}`, isControlled = void 0 !== controlledIsOpen, isOpenState = isControlled ? controlledIsOpen : isOpen, setIsOpen = newIsOpen => {
17508
+ const [isOpen, setIsOpenState] = React.useState(defaultOpen), [currentPosition, setCurrentPosition] = React.useState("auto" === position ? "top" : position), triggerRef = React.useRef(null), popoverRef = React.useRef(null), arrowRef = React.useRef(null), timeoutRef = React.useRef(null), popoverId = id || `popover-${Math.random().toString(36).slice(2, 11)}`, isControlled = void 0 !== controlledIsOpen, isOpenState = isControlled ? controlledIsOpen : isOpen, setIsOpen = React.useCallback((newIsOpen => {
17469
17509
  isControlled || setIsOpenState(newIsOpen), onOpenChange && onOpenChange(newIsOpen);
17470
- };
17510
+ }), [ isControlled, onOpenChange ]);
17471
17511
  // Handle hover events if trigger is hover
17472
17512
  React.useEffect((() => {
17473
17513
  if ("hover" !== trigger || !triggerRef.current || !popoverRef.current) return;
@@ -17487,17 +17527,16 @@ const PopoverContext = React.createContext({
17487
17527
  setIsOpen(!1);
17488
17528
  };
17489
17529
  // Add hover event listeners
17490
- return triggerRef.current.addEventListener("mouseenter", handleTriggerMouseEnter),
17491
- triggerRef.current.addEventListener("mouseleave", handleTriggerMouseLeave), popoverRef.current.addEventListener("mouseenter", handlePopoverMouseEnter),
17492
- popoverRef.current.addEventListener("mouseleave", handlePopoverMouseLeave), () => {
17493
- triggerRef.current && (triggerRef.current.removeEventListener("mouseenter", handleTriggerMouseEnter),
17494
- triggerRef.current.removeEventListener("mouseleave", handleTriggerMouseLeave)),
17495
- popoverRef.current && (popoverRef.current.removeEventListener("mouseenter", handlePopoverMouseEnter),
17496
- popoverRef.current.removeEventListener("mouseleave", handlePopoverMouseLeave)),
17497
- null !== timeoutRef.current && window.clearTimeout(timeoutRef.current);
17530
+ triggerRef.current.addEventListener("mouseenter", handleTriggerMouseEnter), triggerRef.current.addEventListener("mouseleave", handleTriggerMouseLeave),
17531
+ popoverRef.current.addEventListener("mouseenter", handlePopoverMouseEnter), popoverRef.current.addEventListener("mouseleave", handlePopoverMouseLeave);
17532
+ const currentTrigger = triggerRef.current, currentPopover = popoverRef.current;
17533
+ return () => {
17534
+ currentTrigger && (currentTrigger.removeEventListener("mouseenter", handleTriggerMouseEnter),
17535
+ currentTrigger.removeEventListener("mouseleave", handleTriggerMouseLeave)), currentPopover && (currentPopover.removeEventListener("mouseenter", handlePopoverMouseEnter),
17536
+ currentPopover.removeEventListener("mouseleave", handlePopoverMouseLeave)), null !== timeoutRef.current && window.clearTimeout(timeoutRef.current);
17498
17537
  };
17499
- }), [ trigger, delay, isOpenState ]);
17500
- const updatePosition = event => {
17538
+ }), [ trigger, delay, isOpenState, setIsOpen ]);
17539
+ const updatePosition = React.useCallback((event => {
17501
17540
  if (!triggerRef.current || !popoverRef.current) return;
17502
17541
  const triggerRect = triggerRef.current.getBoundingClientRect(), popoverRect = popoverRef.current.getBoundingClientRect(), viewportWidth = window.innerWidth, viewportHeight = window.innerHeight, isNearViewportEdge = triggerRect.top < 50 || triggerRect.bottom > viewportHeight - 50 || triggerRect.left < 50 || triggerRect.right > viewportWidth - 50;
17503
17542
  // If this is a scroll update and trigger isn't near edges, skip repositioning
@@ -17558,9 +17597,9 @@ const PopoverContext = React.createContext({
17558
17597
  // Add scroll position to convert viewport coordinates to absolute position
17559
17598
  const absoluteTop = top + window.scrollY, absoluteLeft = left + window.scrollX;
17560
17599
  // Apply position using absolute positioning to follow when scrolling
17561
- popoverRef.current.style.position = "absolute", popoverRef.current.style.top = `${absoluteTop}px`,
17562
- popoverRef.current.style.left = `${absoluteLeft}px`;
17563
- };
17600
+ popoverRef.current && (popoverRef.current.style.position = "absolute", popoverRef.current.style.top = `${absoluteTop}px`,
17601
+ popoverRef.current.style.left = `${absoluteLeft}px`);
17602
+ }), [ position, offset ]);
17564
17603
  // Position the popover
17565
17604
  return React.useEffect((() => {
17566
17605
  if (!isOpenState || !triggerRef.current || !popoverRef.current) return;
@@ -17586,7 +17625,7 @@ const PopoverContext = React.createContext({
17586
17625
  window.removeEventListener("resize", updatePosition), window.removeEventListener("scroll", handleScroll),
17587
17626
  scrollTimeout && clearTimeout(scrollTimeout), clearInterval(intervalId);
17588
17627
  };
17589
- }), [ isOpenState, position, offset ]),
17628
+ }), [ isOpenState, updatePosition ]),
17590
17629
  // Handle click outside to close popover
17591
17630
  React.useEffect((() => {
17592
17631
  if (!isOpenState || !closeOnClickOutside) return;
@@ -17596,7 +17635,7 @@ const PopoverContext = React.createContext({
17596
17635
  return document.addEventListener("mousedown", handleClickOutside), () => {
17597
17636
  document.removeEventListener("mousedown", handleClickOutside);
17598
17637
  };
17599
- }), [ isOpenState, closeOnClickOutside ]),
17638
+ }), [ isOpenState, closeOnClickOutside, setIsOpen ]),
17600
17639
  // Handle escape key to close popover
17601
17640
  React.useEffect((() => {
17602
17641
  if (!isOpenState || !closeOnEscape) return;
@@ -17606,7 +17645,7 @@ const PopoverContext = React.createContext({
17606
17645
  return document.addEventListener("keydown", handleEscapeKey), () => {
17607
17646
  document.removeEventListener("keydown", handleEscapeKey);
17608
17647
  };
17609
- }), [ isOpenState, closeOnEscape ]),
17648
+ }), [ isOpenState, closeOnEscape, setIsOpen ]),
17610
17649
  // Clean up on unmount
17611
17650
  React.useEffect((() => () => {
17612
17651
  null !== timeoutRef.current && window.clearTimeout(timeoutRef.current);
@@ -17787,10 +17826,11 @@ const calculateStarValue = (e, starValue, allowHalf) => {
17787
17826
  }), [ readOnly, onChange, allowHalf ]);
17788
17827
  // Use vanilla JS implementation if specified
17789
17828
  React.useEffect((() => {
17790
- if (useVanillaJS && "undefined" != typeof window && internalRef.current)
17829
+ if (!useVanillaJS || "undefined" == typeof window || !internalRef.current) return;
17830
+ const currentInstance = ratingInstance.current;
17791
17831
  // Cleanup on unmount
17792
- return () => {
17793
- ratingInstance.current && ratingInstance.current.destroy();
17832
+ return () => {
17833
+ currentInstance && currentInstance.destroy();
17794
17834
  };
17795
17835
  }), [ useVanillaJS, valueProp, defaultValue, maxValue, allowHalf, readOnly, size, variant, onChange ]),
17796
17836
  // Update vanilla JS implementation when props change
@@ -17911,10 +17951,11 @@ const ProductReview = ({productName: productName, productImage: productImage, in
17911
17951
  const [rating, setRating] = React.useState(initialRating), [comment, setComment] = React.useState(""), [submitted, setSubmitted] = React.useState(!1), reviewRef = React.useRef(null), reviewInstance = React.useRef(null);
17912
17952
  React.useEffect((() => {
17913
17953
  // Only run on client-side
17914
- if ("undefined" != typeof window && reviewRef.current)
17954
+ if ("undefined" == typeof window || !reviewRef.current) return;
17955
+ const currentInstance = reviewInstance.current;
17915
17956
  // Cleanup on unmount
17916
- return () => {
17917
- reviewInstance.current && reviewInstance.current.destroy();
17957
+ return () => {
17958
+ currentInstance && currentInstance.destroy();
17918
17959
  };
17919
17960
  }), [ productName, productImage, initialRating, maxRating, allowHalf, ratingColor, onSubmit ]);
17920
17961
  const handleSubmit = e => {
@@ -18159,10 +18200,11 @@ const SectionIntro = ({title: title, label: label, text: text, actions: actions,
18159
18200
  const sectionIntroRef = React.useRef(null), sectionIntroInstance = React.useRef(null);
18160
18201
  React.useEffect((() => {
18161
18202
  // Only run on client-side
18162
- if ("undefined" != typeof window && sectionIntroRef.current)
18203
+ if ("undefined" == typeof window || !sectionIntroRef.current) return;
18204
+ const currentInstance = sectionIntroInstance.current;
18163
18205
  // Cleanup on unmount
18164
- return () => {
18165
- sectionIntroInstance.current && sectionIntroInstance.current.destroy();
18206
+ return () => {
18207
+ currentInstance && currentInstance.destroy();
18166
18208
  };
18167
18209
  }), [ alignment, backgroundImageSrc, showOverlay, size, skeleton ]);
18168
18210
  // Determine CSS classes
@@ -18246,7 +18288,7 @@ SectionIntro.displayName = "SectionIntro";
18246
18288
 
18247
18289
  const Slider = React.forwardRef(((props, ref) => {
18248
18290
  const {slides: slides = [], height: height = 300, width: width = "100%", slidesToShow: slidesToShow = 1, spaceBetween: spaceBetween = 0, loop: loop = !1, initialSlide: initialSlide = 0, direction: direction = "horizontal", speed: speed = 300, allowTouchMove: allowTouchMove = !0, threshold: threshold = 50, grabCursor: grabCursor = !0, autoplay: autoplay, navigation: navigation, pagination: pagination, className: className, style: style, onSlideChange: onSlideChange, ...rest} = props, validSlides = Array.isArray(slides) ? slides : [], slider = function(options) {
18249
- const {slides: rawSlides, slidesToShow: slidesToShow = 1, spaceBetween: spaceBetween = 0, loop: loop = !1, initialSlide: initialSlide = 0, direction: direction = "horizontal", speed: speed = 300, allowTouchMove: allowTouchMove = !0, threshold: threshold = 50, autoplay: autoplay, onSlideChange: onSlideChange} = options, slides = Array.isArray(rawSlides) ? rawSlides : [], containerRef = React.useRef(null), wrapperRef = React.useRef(null), repositioningRef = React.useRef(!1), autoplayRef = React.useRef(null), [autoplayRunning, setAutoplayRunning] = React.useState(!1), sliderStateRef = React.useRef({
18291
+ const {slides: rawSlides, slidesToShow: slidesToShow = 1, spaceBetween: spaceBetween = 0, loop: loop = !1, initialSlide: initialSlide = 0, direction: direction = "horizontal", speed: speed = 300, allowTouchMove: allowTouchMove = !0, threshold: threshold = 50, autoplay: autoplay, onSlideChange: onSlideChange} = options, slides = React.useMemo((() => Array.isArray(rawSlides) ? rawSlides : []), [ rawSlides ]), containerRef = React.useRef(null), wrapperRef = React.useRef(null), repositioningRef = React.useRef(!1), autoplayRef = React.useRef(null), [autoplayRunning, setAutoplayRunning] = React.useState(!1), sliderStateRef = React.useRef({
18250
18292
  isTransitioning: !1,
18251
18293
  loop: loop,
18252
18294
  slides: slides,
@@ -18381,7 +18423,7 @@ const Slider = React.forwardRef(((props, ref) => {
18381
18423
  setIsTransitioning(!1), onSlideChange?.(nextIndex);
18382
18424
  }), speed);
18383
18425
  }
18384
- }), [ realIndex, internalIndex, slides.length, slidesToShow, loop, isTransitioning, speed, onSlideChange, allSlides.length, loopedSlides, autoplay ]), slidePrev = React.useCallback((() => {
18426
+ }), [ realIndex, internalIndex, slides.length, slidesToShow, loop, isTransitioning, speed, onSlideChange, autoplay ]), slidePrev = React.useCallback((() => {
18385
18427
  if (!isTransitioning) if (
18386
18428
  // Stop autoplay on interaction if disableOnInteraction is true
18387
18429
  autoplay && "object" == typeof autoplay && autoplay.disableOnInteraction && autoplayRef.current && (clearInterval(autoplayRef.current),
@@ -18403,7 +18445,7 @@ const Slider = React.forwardRef(((props, ref) => {
18403
18445
  setIsTransitioning(!1), onSlideChange?.(prevIndex);
18404
18446
  }), speed);
18405
18447
  }
18406
- }), [ realIndex, internalIndex, slides.length, loop, isTransitioning, speed, onSlideChange, allSlides.length, loopedSlides, autoplay ]), goToSlide = React.useCallback((index => {
18448
+ }), [ realIndex, internalIndex, slides.length, loop, isTransitioning, speed, onSlideChange, autoplay ]), goToSlide = React.useCallback((index => {
18407
18449
  isTransitioning || index === realIndex || (
18408
18450
  // Stop autoplay on interaction if disableOnInteraction is true
18409
18451
  autoplay && "object" == typeof autoplay && autoplay.disableOnInteraction && autoplayRef.current && (clearInterval(autoplayRef.current),
@@ -18411,7 +18453,7 @@ const Slider = React.forwardRef(((props, ref) => {
18411
18453
  setRealIndex(index), setInternalIndex(loop ? slides.length + index : index), setTimeout((() => {
18412
18454
  setIsTransitioning(!1), onSlideChange?.(index);
18413
18455
  }), speed));
18414
- }), [ realIndex, isTransitioning, speed, onSlideChange, loop, loopedSlides, autoplay ]), handleTouchStart = React.useCallback((e => {
18456
+ }), [ realIndex, isTransitioning, speed, onSlideChange, loop, slides.length, autoplay ]), handleTouchStart = React.useCallback((e => {
18415
18457
  if (!allowTouchMove) return;
18416
18458
  // Stop autoplay on interaction if disableOnInteraction is true
18417
18459
  autoplay && "object" == typeof autoplay && autoplay.disableOnInteraction && autoplayRef.current && (clearInterval(autoplayRef.current),
@@ -18666,7 +18708,7 @@ const Steps = ({items: items, activeIndex: activeIndex = 0, vertical: vertical =
18666
18708
  let content;
18667
18709
  React.useEffect((() => {
18668
18710
  currentStep !== activeIndex && setCurrentStep(activeIndex);
18669
- }), [ activeIndex ]),
18711
+ }), [ activeIndex, currentStep ]),
18670
18712
  // Legacy rendering
18671
18713
  content = items && items.length > 0 ? items.map(((item, index) => jsxRuntime.jsx(StepsItem, {
18672
18714
  index: index,
@@ -18810,7 +18852,7 @@ const TabsPanel = React.forwardRef((({children: children, className: className
18810
18852
 
18811
18853
  TabsPanel.displayName = "TabsPanel";
18812
18854
 
18813
- const Tabs = React.memo((({items: items, activeIndex: activeIndex = TAB.DEFAULTS.ACTIVE_INDEX, onTabChange: onTabChange, className: className = "", style: style, glass: glass, children: children}) => {
18855
+ const TabsComponentBase = ({items: items, activeIndex: activeIndex = TAB.DEFAULTS.ACTIVE_INDEX, onTabChange: onTabChange, className: className = "", style: style, glass: glass, children: children}) => {
18814
18856
  const [currentTab, setCurrentTab] = React.useState(activeIndex), handleTabClick = index => {
18815
18857
  setCurrentTab(index), onTabChange && onTabChange(index);
18816
18858
  }, handleKeyDown = (event, totalTabs) => {
@@ -18926,7 +18968,7 @@ const Tabs = React.memo((({items: items, activeIndex: activeIndex = TAB.DEFAULT
18926
18968
  });
18927
18969
  }
18928
18970
  return wrapper;
18929
- }));
18971
+ }, Tabs = React.memo(TabsComponentBase);
18930
18972
 
18931
18973
  Tabs.displayName = "Tabs", Tabs.List = TabsList, Tabs.Trigger = TabsTrigger, Tabs.Panels = TabsPanels,
18932
18974
  Tabs.Panel = TabsPanel;
@@ -18938,10 +18980,11 @@ const Testimonial = ({quote: quote, author: author, size: size = "", skeleton: s
18938
18980
  const testimonialRef = React.useRef(null), testimonialInstance = React.useRef(null);
18939
18981
  React.useEffect((() => {
18940
18982
  // Only run on client-side
18941
- if ("undefined" != typeof window && testimonialRef.current)
18983
+ if ("undefined" == typeof window || !testimonialRef.current) return;
18984
+ const currentInstance = testimonialInstance.current;
18942
18985
  // Cleanup on unmount
18943
- return () => {
18944
- testimonialInstance.current && testimonialInstance.current.destroy();
18986
+ return () => {
18987
+ currentInstance && currentInstance.destroy();
18945
18988
  };
18946
18989
  }), [ size, skeleton ]);
18947
18990
  // Determine CSS classes
@@ -20082,11 +20125,13 @@ const VideoPlayer = React.forwardRef((({src: src, type: type = "video", youtube
20082
20125
  detectBorderRadius();
20083
20126
  // Create ResizeObserver to watch for style changes
20084
20127
  let resizeObserver = null;
20085
- return "undefined" != typeof ResizeObserver && containerRef.current && (resizeObserver = new ResizeObserver(detectBorderRadius),
20128
+ "undefined" != typeof ResizeObserver && containerRef.current && (resizeObserver = new ResizeObserver(detectBorderRadius),
20086
20129
  resizeObserver.observe(containerRef.current)),
20087
20130
  // Also listen for window resize (in case styles change)
20088
- window.addEventListener("resize", detectBorderRadius), () => {
20089
- window.removeEventListener("resize", detectBorderRadius), resizeObserver && containerRef.current && (resizeObserver.unobserve(containerRef.current),
20131
+ window.addEventListener("resize", detectBorderRadius);
20132
+ const currentContainer = containerRef.current;
20133
+ return () => {
20134
+ window.removeEventListener("resize", detectBorderRadius), resizeObserver && currentContainer && (resizeObserver.unobserve(currentContainer),
20090
20135
  resizeObserver.disconnect());
20091
20136
  };
20092
20137
  }), []);
@@ -20856,7 +20901,51 @@ const defaultTokens = {
20856
20901
  "breakpoint-md": "768px",
20857
20902
  "breakpoint-lg": "992px",
20858
20903
  "breakpoint-xl": "1200px",
20859
- "breakpoint-xxl": "1440px"
20904
+ "breakpoint-xxl": "1440px",
20905
+ // Advanced Features - Interactive Effects (Phase 2)
20906
+ "interactive-vortex-enabled": "false",
20907
+ "interactive-vortex-strength": "0.5",
20908
+ "interactive-vortex-radius": "100",
20909
+ "interactive-vortex-decay": "0.8",
20910
+ "interactive-chromatic-enabled": "false",
20911
+ "interactive-chromatic-mode": "lateral",
20912
+ "interactive-chromatic-red-shift": "0.02",
20913
+ "interactive-chromatic-green-shift": "0",
20914
+ "interactive-chromatic-blue-shift": "-0.02",
20915
+ "interactive-chromatic-edge-only": "false",
20916
+ "interactive-chromatic-edge-threshold": "0.5",
20917
+ "interactive-mouse-sensitivity": "1.0",
20918
+ "interactive-mouse-trail-effect": "false",
20919
+ "interactive-animation-speed-base": "1.0",
20920
+ "interactive-animation-speed-multiplier": "1.0",
20921
+ // Advanced Features - Optimization (Phase 3)
20922
+ "optimization-breakpoint-mobile": "0px",
20923
+ "optimization-breakpoint-tablet": "768px",
20924
+ "optimization-breakpoint-desktop": "1024px",
20925
+ "optimization-breakpoint-wide": "1440px",
20926
+ "optimization-device-scaling-mobile": "0.5",
20927
+ "optimization-device-scaling-tablet": "0.75",
20928
+ "optimization-device-scaling-desktop": "1.0",
20929
+ "optimization-performance-fps-target": "60",
20930
+ "optimization-auto-scaling-enabled": "false",
20931
+ "optimization-auto-scaling-low-end": "0.5",
20932
+ "optimization-auto-scaling-mid-range": "0.75",
20933
+ "optimization-auto-scaling-high-end": "1.0",
20934
+ // Advanced Features - Visual Polish (Phase 4)
20935
+ "visual-polish-border-iridescent-glow": "false",
20936
+ "visual-polish-border-shimmer-effect": "false",
20937
+ "visual-polish-border-beveled-edges": "false",
20938
+ "visual-polish-border-pulsing-glow": "false",
20939
+ "visual-polish-content-aware-blur-enabled": "false",
20940
+ "visual-polish-content-aware-depth-detection": "false",
20941
+ "visual-polish-content-aware-edge-preservation": "false",
20942
+ "visual-polish-content-aware-variable-radius": "false",
20943
+ "visual-polish-holographic-enabled": "false",
20944
+ "visual-polish-holographic-rainbow-diffraction": "false",
20945
+ "visual-polish-holographic-scanline-animation": "false",
20946
+ "visual-polish-holographic-grid-overlay": "false",
20947
+ "visual-polish-holographic-data-stream": "false",
20948
+ "visual-polish-holographic-pulse-rings": "false"
20860
20949
  };
20861
20950
 
20862
20951
  /**
@@ -20936,158 +21025,479 @@ const defaultTokens = {
20936
21025
  }
20937
21026
 
20938
21027
  /**
20939
- * Theme System Error Handling
21028
+ * Theme Adapter
20940
21029
  *
20941
- * Centralized error handling for the Atomix theme system.
20942
- * Provides custom error classes and logging utilities.
21030
+ * Converts between Theme objects and DesignTokens
20943
21031
  */
20944
21032
  /**
20945
- * Theme error codes
20946
- */ var ThemeErrorCode, LogLevel;
20947
-
20948
- !function(ThemeErrorCode) {
20949
- /** Theme not found in registry */
20950
- ThemeErrorCode.THEME_NOT_FOUND = "THEME_NOT_FOUND",
20951
- /** Theme failed to load */
20952
- ThemeErrorCode.THEME_LOAD_FAILED = "THEME_LOAD_FAILED",
20953
- /** Theme validation failed */
20954
- ThemeErrorCode.THEME_VALIDATION_FAILED = "THEME_VALIDATION_FAILED",
20955
- /** Configuration loading failed */
20956
- ThemeErrorCode.CONFIG_LOAD_FAILED = "CONFIG_LOAD_FAILED",
20957
- /** Configuration validation failed */
20958
- ThemeErrorCode.CONFIG_VALIDATION_FAILED = "CONFIG_VALIDATION_FAILED",
20959
- /** Circular dependency detected */
20960
- ThemeErrorCode.CIRCULAR_DEPENDENCY = "CIRCULAR_DEPENDENCY",
20961
- /** Missing dependency */
20962
- ThemeErrorCode.MISSING_DEPENDENCY = "MISSING_DEPENDENCY",
20963
- /** Storage operation failed */
20964
- ThemeErrorCode.STORAGE_ERROR = "STORAGE_ERROR",
20965
- /** Invalid theme name */
20966
- ThemeErrorCode.INVALID_THEME_NAME = "INVALID_THEME_NAME",
20967
- /** CSS injection failed */
20968
- ThemeErrorCode.CSS_INJECTION_FAILED = "CSS_INJECTION_FAILED",
20969
- /** Invalid color format */
20970
- ThemeErrorCode.INVALID_COLOR_FORMAT = "INVALID_COLOR_FORMAT",
20971
- /** Missing required token */
20972
- ThemeErrorCode.MISSING_REQUIRED_TOKEN = "MISSING_REQUIRED_TOKEN",
20973
- /** Accessibility contrast violation */
20974
- ThemeErrorCode.CONTRAST_VIOLATION = "CONTRAST_VIOLATION",
20975
- /** Invalid token type */
20976
- ThemeErrorCode.INVALID_TOKEN_TYPE = "INVALID_TOKEN_TYPE",
20977
- /** Unknown error */
20978
- ThemeErrorCode.UNKNOWN_ERROR = "UNKNOWN_ERROR";
20979
- }(ThemeErrorCode || (ThemeErrorCode = {}));
21033
+ * Convert a Theme object to DesignTokens
21034
+ */ function themeToDesignTokens(theme) {
21035
+ const tokens = {};
21036
+ // Convert colors
21037
+ if (theme.palette) {
21038
+ // Primary color
21039
+ if (theme.palette.primary) {
21040
+ const primaryMain = theme.palette.primary.main;
21041
+ tokens.primary = primaryMain;
21042
+ const rgb = hexToRgb$2(primaryMain);
21043
+ rgb && (tokens["primary-rgb"] = rgb);
21044
+ }
21045
+ // Secondary color
21046
+ if (theme.palette.secondary) {
21047
+ const secondaryMain = theme.palette.secondary.main;
21048
+ tokens.secondary = secondaryMain;
21049
+ const rgb = hexToRgb$2(secondaryMain);
21050
+ rgb && (tokens["secondary-rgb"] = rgb);
21051
+ }
21052
+ // Other colors
21053
+ const colorKeys = [ "error", "warning", "info", "success" ];
21054
+ for (const key of colorKeys) if (theme.palette[key]) {
21055
+ const colorMain = theme.palette[key].main;
21056
+ tokens[key] = colorMain;
21057
+ const rgb = hexToRgb$2(colorMain);
21058
+ rgb && (tokens[`${key}-rgb`] = rgb);
21059
+ }
21060
+ // Background colors
21061
+ theme.palette.background && (tokens["body-bg"] = theme.palette.background.default),
21062
+ // Text colors
21063
+ theme.palette.text && (tokens["body-color"] = theme.palette.text.primary);
21064
+ }
21065
+ // Convert typography
21066
+ // Convert border radius
21067
+ if (theme.typography && (tokens["body-font-family"] = theme.typography.fontFamily,
21068
+ tokens["body-font-size"] = `${theme.typography.fontSize}px`, tokens["font-weight-normal"] = `${theme.typography.fontWeightRegular}`,
21069
+ tokens["font-weight-bold"] = `${theme.typography.fontWeightBold}`),
21070
+ // Convert spacing
21071
+ "function" == typeof theme.spacing && (
21072
+ // If spacing is a function, call it with some values to get results
21073
+ tokens["spacing-1"] = theme.spacing(1), tokens["spacing-2"] = theme.spacing(2),
21074
+ tokens["spacing-4"] = theme.spacing(4)),
21075
+ // Convert breakpoints
21076
+ theme.breakpoints?.values && (tokens["breakpoint-xs"] = `${theme.breakpoints.values.xs}px`,
21077
+ tokens["breakpoint-sm"] = `${theme.breakpoints.values.sm}px`, tokens["breakpoint-md"] = `${theme.breakpoints.values.md}px`,
21078
+ tokens["breakpoint-lg"] = `${theme.breakpoints.values.lg}px`, tokens["breakpoint-xl"] = `${theme.breakpoints.values.xl}px`),
21079
+ // Convert shadows
21080
+ theme.shadows && (tokens["box-shadow"] = theme.shadows[2], // Use a moderate shadow
21081
+ tokens["box-shadow-sm"] = theme.shadows[1], tokens["box-shadow-lg"] = theme.shadows[3]),
21082
+ // Convert transitions
21083
+ theme.transitions && (tokens["transition-duration-base"] = `${theme.transitions.duration.standard}ms`),
21084
+ // Convert z-index
21085
+ theme.zIndex && (tokens["z-modal"] = `${theme.zIndex.modal}`, tokens["z-popover"] = `${theme.zIndex.popover}`,
21086
+ tokens["z-tooltip"] = `${theme.zIndex.tooltip}`), theme.borderRadius) {
21087
+ const baseRadius = theme.borderRadius.base;
21088
+ tokens["border-radius"] = "number" == typeof baseRadius ? `${baseRadius}px` : baseRadius;
21089
+ }
21090
+ // Add advanced feature tokens if available in theme
21091
+ if (theme.custom) {
21092
+ // Interactive Effects (Phase 2)
21093
+ if (theme.custom.interactiveEffects) {
21094
+ const ie = theme.custom.interactiveEffects;
21095
+ // Vortex effects
21096
+ ie.vortex && (tokens["interactive-vortex-enabled"] = String(ie.vortex.enabled ?? !1),
21097
+ tokens["interactive-vortex-strength"] = String(ie.vortex.strength ?? .5), tokens["interactive-vortex-radius"] = String(ie.vortex.radius ?? 100),
21098
+ tokens["interactive-vortex-decay"] = String(ie.vortex.decay ?? .8)),
21099
+ // Chromatic aberration
21100
+ ie.chromaticAberration && (tokens["interactive-chromatic-enabled"] = String(ie.chromaticAberration.enabled ?? !1),
21101
+ tokens["interactive-chromatic-mode"] = ie.chromaticAberration.mode ?? "lateral",
21102
+ tokens["interactive-chromatic-red-shift"] = String(ie.chromaticAberration.redShift ?? .02),
21103
+ tokens["interactive-chromatic-green-shift"] = String(ie.chromaticAberration.greenShift ?? 0),
21104
+ tokens["interactive-chromatic-blue-shift"] = String(ie.chromaticAberration.blueShift ?? -.02),
21105
+ tokens["interactive-chromatic-edge-only"] = String(ie.chromaticAberration.edgeOnly ?? !1),
21106
+ tokens["interactive-chromatic-edge-threshold"] = String(ie.chromaticAberration.edgeThreshold ?? .5)),
21107
+ // Mouse interaction
21108
+ ie.mouseInteraction && (tokens["interactive-mouse-sensitivity"] = String(ie.mouseInteraction.sensitivity ?? 1),
21109
+ tokens["interactive-mouse-trail-effect"] = String(ie.mouseInteraction.trailEffect ?? !1)),
21110
+ // Animation speed
21111
+ ie.animationSpeed && (tokens["interactive-animation-speed-base"] = String(ie.animationSpeed.base ?? 1),
21112
+ tokens["interactive-animation-speed-multiplier"] = String(ie.animationSpeed.timeMultiplier ?? 1));
21113
+ }
21114
+ // Optimization (Phase 3)
21115
+ if (theme.custom.optimization) {
21116
+ const opt = theme.custom.optimization;
21117
+ // Responsive breakpoints
21118
+ opt.responsive && (opt.responsive.breakpoints && (tokens["optimization-breakpoint-mobile"] = opt.responsive.breakpoints.mobile ?? "0px",
21119
+ tokens["optimization-breakpoint-tablet"] = opt.responsive.breakpoints.tablet ?? "768px",
21120
+ tokens["optimization-breakpoint-desktop"] = opt.responsive.breakpoints.desktop ?? "1024px",
21121
+ tokens["optimization-breakpoint-wide"] = opt.responsive.breakpoints.wide ?? "1440px"),
21122
+ opt.responsive.deviceScaling && (tokens["optimization-device-scaling-mobile"] = String(opt.responsive.deviceScaling.mobile ?? .5),
21123
+ tokens["optimization-device-scaling-tablet"] = String(opt.responsive.deviceScaling.tablet ?? .75),
21124
+ tokens["optimization-device-scaling-desktop"] = String(opt.responsive.deviceScaling.desktop ?? 1))),
21125
+ // Performance settings
21126
+ opt.performance && (tokens["optimization-performance-fps-target"] = String(opt.performance.fpsTarget ?? 60),
21127
+ tokens["optimization-auto-scaling-enabled"] = String(opt.performance.autoScaling ?? !1)),
21128
+ // Auto-scaling settings
21129
+ opt.autoScaling && (tokens["optimization-auto-scaling-enabled"] = String(opt.autoScaling.enabled ?? !1),
21130
+ tokens["optimization-auto-scaling-low-end"] = String(opt.autoScaling.qualityThresholds?.lowEnd ?? .5),
21131
+ tokens["optimization-auto-scaling-mid-range"] = String(opt.autoScaling.qualityThresholds?.midRange ?? .75),
21132
+ tokens["optimization-auto-scaling-high-end"] = String(opt.autoScaling.qualityThresholds?.highEnd ?? 1));
21133
+ }
21134
+ // Visual Polish (Phase 4)
21135
+ if (theme.custom.visualPolish) {
21136
+ const vp = theme.custom.visualPolish;
21137
+ vp.borders && (tokens["visual-polish-border-iridescent-glow"] = String(vp.borders.iridescentGlow ?? !1),
21138
+ tokens["visual-polish-border-shimmer-effect"] = String(vp.borders.shimmerEffect ?? !1),
21139
+ tokens["visual-polish-border-beveled-edges"] = String(vp.borders.beveledEdges ?? !1),
21140
+ tokens["visual-polish-border-pulsing-glow"] = String(vp.borders.pulsingGlow ?? !1)),
21141
+ vp.contentAwareBlur && (tokens["visual-polish-content-aware-blur-enabled"] = String(vp.contentAwareBlur.enabled ?? !1),
21142
+ tokens["visual-polish-content-aware-depth-detection"] = String(vp.contentAwareBlur.depthDetection ?? !1),
21143
+ tokens["visual-polish-content-aware-edge-preservation"] = String(vp.contentAwareBlur.edgePreservation ?? !1),
21144
+ tokens["visual-polish-content-aware-variable-radius"] = String(vp.contentAwareBlur.variableRadius ?? !1)),
21145
+ vp.holographicEffects && (tokens["visual-polish-holographic-enabled"] = String(vp.holographicEffects.enabled ?? !1),
21146
+ tokens["visual-polish-holographic-rainbow-diffraction"] = String(vp.holographicEffects.rainbowDiffraction ?? !1),
21147
+ tokens["visual-polish-holographic-scanline-animation"] = String(vp.holographicEffects.scanlineAnimation ?? !1),
21148
+ tokens["visual-polish-holographic-grid-overlay"] = String(vp.holographicEffects.gridOverlay ?? !1),
21149
+ tokens["visual-polish-holographic-data-stream"] = String(vp.holographicEffects.dataStream ?? !1),
21150
+ tokens["visual-polish-holographic-pulse-rings"] = String(vp.holographicEffects.pulseRings ?? !1));
21151
+ }
21152
+ }
21153
+ // Create full tokens object with defaults
21154
+ return createTokens(tokens);
21155
+ }
20980
21156
 
20981
21157
  /**
20982
- * Custom error class for theme-related errors
20983
- */
20984
- class ThemeError extends Error {
20985
- constructor(message, code = ThemeErrorCode.UNKNOWN_ERROR, context) {
20986
- super(message), this.name = "ThemeError", this.code = code, this.context = context,
20987
- this.timestamp = Date.now(),
20988
- // Maintains proper stack trace for where our error was thrown (only available on V8)
20989
- Error.captureStackTrace && Error.captureStackTrace(this, ThemeError);
20990
- }
20991
- /**
20992
- * Convert error to JSON for logging
20993
- */ toJSON() {
20994
- return {
20995
- name: this.name,
20996
- message: this.message,
20997
- code: this.code,
20998
- context: this.context,
20999
- timestamp: this.timestamp,
21000
- stack: this.stack
21001
- };
21158
+ * Converts an AtomixConfig to DesignTokens
21159
+ *
21160
+ * This function maps the configuration from the user-facing format
21161
+ * to the internal DesignTokens format used by the theme system.
21162
+ *
21163
+ * @param config - The configuration object to convert
21164
+ * @returns DesignTokens object ready for theme generation
21165
+ *
21166
+ * @example
21167
+ * ```typescript
21168
+ * import { configToTokens } from '@shohojdhara/atomix/theme';
21169
+ *
21170
+ * const config = {
21171
+ * prefix: 'myapp',
21172
+ * theme: { extend: { colors: { primary: { main: '#7AFFD7' } } } }
21173
+ * };
21174
+ * const tokens = configToTokens(config);
21175
+ * ```
21176
+ */ function configToTokens(config) {
21177
+ const prefix = config.prefix || "atomix", theme = config.theme || {};
21178
+ // Start with default tokens
21179
+ let tokens = {
21180
+ ...defaultTokens
21181
+ };
21182
+ // Apply theme extensions
21183
+ // Apply advanced features if available in config
21184
+ if (theme.extend &&
21185
+ // Apply extensions to tokens
21186
+ Object.entries(theme.extend).forEach((([category, values]) => {
21187
+ "object" == typeof values && null !== values && Object.entries(values).forEach((([key, value]) => {
21188
+ // Map theme categories to token names
21189
+ const tokenName = `${category}-${key}`;
21190
+ "string" == typeof value || "number" == typeof value ? tokens[tokenName] = String(value) : "object" == typeof value && null !== value &&
21191
+ // Handle nested objects like color scales
21192
+ Object.entries(value).forEach((([nestedKey, nestedValue]) => {
21193
+ "string" != typeof nestedValue && "number" != typeof nestedValue || (tokens[`${tokenName}-${nestedKey}`] = String(nestedValue));
21194
+ }));
21195
+ }));
21196
+ })),
21197
+ // Apply theme tokens if provided (completely replacing defaults)
21198
+ theme.tokens && (tokens = {
21199
+ ...tokens,
21200
+ ...theme.tokens
21201
+ }), config) {
21202
+ // Interactive Effects (Phase 2)
21203
+ if (config.interactiveEffects) {
21204
+ const ie = config.interactiveEffects;
21205
+ // Vortex effects
21206
+ ie.vortex && (tokens["interactive-vortex-enabled"] = String(ie.vortex.enabled ?? !1),
21207
+ tokens["interactive-vortex-strength"] = String(ie.vortex.strength ?? .5), tokens["interactive-vortex-radius"] = String(ie.vortex.radius ?? 100),
21208
+ tokens["interactive-vortex-decay"] = String(ie.vortex.decay ?? .8), tokens["interactive-vortex-curl-noise"] = String(ie.vortex.curlNoise ?? !1),
21209
+ tokens["interactive-vortex-velocity-tracking"] = String(ie.vortex.velocityTracking ?? !1)),
21210
+ // Chromatic aberration
21211
+ ie.chromaticAberration && (tokens["interactive-chromatic-enabled"] = String(ie.chromaticAberration.enabled ?? !1),
21212
+ tokens["interactive-chromatic-mode"] = ie.chromaticAberration.mode ?? "lateral",
21213
+ tokens["interactive-chromatic-red-shift"] = String(ie.chromaticAberration.redShift ?? .02),
21214
+ tokens["interactive-chromatic-green-shift"] = String(ie.chromaticAberration.greenShift ?? 0),
21215
+ tokens["interactive-chromatic-blue-shift"] = String(ie.chromaticAberration.blueShift ?? -.02),
21216
+ tokens["interactive-chromatic-edge-only"] = String(ie.chromaticAberration.edgeOnly ?? !1),
21217
+ tokens["interactive-chromatic-edge-threshold"] = String(ie.chromaticAberration.edgeThreshold ?? .5)),
21218
+ // Mouse interaction
21219
+ ie.mouseInteraction && (tokens["interactive-mouse-sensitivity"] = String(ie.mouseInteraction.sensitivity ?? 1),
21220
+ tokens["interactive-mouse-trail-effect"] = String(ie.mouseInteraction.trailEffect ?? !1),
21221
+ tokens["interactive-mouse-pressure-sensitivity"] = String(ie.mouseInteraction.pressureSensitivity ?? !1)),
21222
+ // Animation speed
21223
+ ie.animationSpeed && (tokens["interactive-animation-speed-base"] = String(ie.animationSpeed.base ?? 1),
21224
+ tokens["interactive-animation-speed-multiplier"] = String(ie.animationSpeed.timeMultiplier ?? 1));
21225
+ }
21226
+ // Optimization (Phase 3)
21227
+ if (config.optimization) {
21228
+ const opt = config.optimization;
21229
+ // Responsive breakpoints
21230
+ opt.responsive && (opt.responsive.breakpoints && (tokens["optimization-breakpoint-mobile"] = opt.responsive.breakpoints.mobile ?? "0px",
21231
+ tokens["optimization-breakpoint-tablet"] = opt.responsive.breakpoints.tablet ?? "768px",
21232
+ tokens["optimization-breakpoint-desktop"] = opt.responsive.breakpoints.desktop ?? "1024px",
21233
+ tokens["optimization-breakpoint-wide"] = opt.responsive.breakpoints.wide ?? "1440px"),
21234
+ opt.responsive.deviceScaling && (tokens["optimization-device-scaling-mobile"] = String(opt.responsive.deviceScaling.mobile ?? .5),
21235
+ tokens["optimization-device-scaling-tablet"] = String(opt.responsive.deviceScaling.tablet ?? .75),
21236
+ tokens["optimization-device-scaling-desktop"] = String(opt.responsive.deviceScaling.desktop ?? 1))),
21237
+ // Performance settings
21238
+ opt.performance && (tokens["optimization-performance-fps-target"] = String(opt.performance.fpsTarget ?? 60),
21239
+ tokens["optimization-auto-scaling-enabled"] = String(opt.performance.autoScaling ?? !1),
21240
+ tokens["optimization-monitor-dashboard-enabled"] = String(opt.performance.monitorDashboard ?? !1)),
21241
+ // Auto-scaling settings
21242
+ opt.autoScaling && (tokens["optimization-auto-scaling-enabled"] = String(opt.autoScaling.enabled ?? !1),
21243
+ tokens["optimization-auto-scaling-low-end"] = String(opt.autoScaling.qualityThresholds?.lowEnd ?? .5),
21244
+ tokens["optimization-auto-scaling-mid-range"] = String(opt.autoScaling.qualityThresholds?.midRange ?? .75),
21245
+ tokens["optimization-auto-scaling-high-end"] = String(opt.autoScaling.qualityThresholds?.highEnd ?? 1));
21246
+ }
21247
+ // Visual Polish (Phase 4)
21248
+ if (config.visualPolish) {
21249
+ const vp = config.visualPolish;
21250
+ vp.borders && (tokens["visual-polish-border-iridescent-glow"] = String(vp.borders.iridescentGlow ?? !1),
21251
+ tokens["visual-polish-border-shimmer-effect"] = String(vp.borders.shimmerEffect ?? !1),
21252
+ tokens["visual-polish-border-beveled-edges"] = String(vp.borders.beveledEdges ?? !1),
21253
+ tokens["visual-polish-border-pulsing-glow"] = String(vp.borders.pulsingGlow ?? !1)),
21254
+ vp.contentAwareBlur && (tokens["visual-polish-content-aware-blur-enabled"] = String(vp.contentAwareBlur.enabled ?? !1),
21255
+ tokens["visual-polish-content-aware-depth-detection"] = String(vp.contentAwareBlur.depthDetection ?? !1),
21256
+ tokens["visual-polish-content-aware-edge-preservation"] = String(vp.contentAwareBlur.edgePreservation ?? !1),
21257
+ tokens["visual-polish-content-aware-variable-radius"] = String(vp.contentAwareBlur.variableRadius ?? !1)),
21258
+ vp.holographicEffects && (tokens["visual-polish-holographic-enabled"] = String(vp.holographicEffects.enabled ?? !1),
21259
+ tokens["visual-polish-holographic-rainbow-diffraction"] = String(vp.holographicEffects.rainbowDiffraction ?? !1),
21260
+ tokens["visual-polish-holographic-scanline-animation"] = String(vp.holographicEffects.scanlineAnimation ?? !1),
21261
+ tokens["visual-polish-holographic-grid-overlay"] = String(vp.holographicEffects.gridOverlay ?? !1),
21262
+ tokens["visual-polish-holographic-data-stream"] = String(vp.holographicEffects.dataStream ?? !1),
21263
+ tokens["visual-polish-holographic-pulse-rings"] = String(vp.holographicEffects.pulseRings ?? !1));
21264
+ }
21002
21265
  }
21266
+ // Apply prefix to all tokens
21267
+ const prefixedTokens = {};
21268
+ return Object.entries(tokens).forEach((([key, value]) => {
21269
+ // If the token key already starts with the prefix, use as-is
21270
+ // Otherwise, add the prefix
21271
+ const prefixedKey = key.startsWith(prefix) ? key : `${prefix}-${key}`;
21272
+ prefixedTokens[prefixedKey] = value;
21273
+ })), prefixedTokens;
21003
21274
  }
21004
21275
 
21005
21276
  /**
21006
- * Log level
21007
- */ !function(LogLevel) {
21008
- LogLevel[LogLevel.ERROR = 0] = "ERROR", LogLevel[LogLevel.WARN = 1] = "WARN", LogLevel[LogLevel.INFO = 2] = "INFO",
21009
- LogLevel[LogLevel.DEBUG = 3] = "DEBUG";
21010
- }(LogLevel || (LogLevel = {}));
21277
+ * Convert hex color to RGB
21278
+ */ function hexToRgb$2(hex) {
21279
+ hex = hex.replace(/^#?([a-f\d])([a-f\d])([a-f\d])$/i, ((m, r, g, b) => r + r + g + g + b + b));
21280
+ const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
21281
+ return result && result[1] && result[2] && result[3] ? `${parseInt(result[1], 16)}, ${parseInt(result[2], 16)}, ${parseInt(result[3], 16)}` : "0, 0, 0";
21282
+ }
21011
21283
 
21012
21284
  /**
21013
- * Theme Logger
21285
+ * Converts DesignTokens to CSS variables
21014
21286
  *
21015
- * Centralized logging for the theme system.
21016
- * Replaces console statements with structured logging.
21287
+ * @param tokens - The tokens to convert
21288
+ * @returns A record of CSS variable names and values
21289
+ */ function designTokensToCSSVars(tokens) {
21290
+ const cssVars = {};
21291
+ return Object.entries(tokens).forEach((([key, value]) => {
21292
+ void 0 !== value && (cssVars[`--atomix-${key}`] = String(value));
21293
+ })), cssVars;
21294
+ }
21295
+
21296
+ /**
21297
+ * Load theme from config file (synchronous, Node.js only)
21298
+ * @param configPath - Path to config file (default: atomix.config.ts)
21299
+ * @returns DesignTokens from theme configuration
21300
+ * @throws Error if config loading is not available in browser environment
21017
21301
  */
21018
- class ThemeLogger {
21019
- constructor(config = {}) {
21020
- this.config = {
21021
- level: config.level ?? ("undefined" != typeof process && "production" === process.env?.NODE_ENV ? LogLevel.WARN : LogLevel.INFO),
21022
- enableConsole: config.enableConsole ?? !0,
21023
- onError: config.onError,
21024
- onWarn: config.onWarn,
21025
- onInfo: config.onInfo,
21026
- onDebug: config.onDebug
21027
- };
21028
- }
21029
- /**
21030
- * Log an error
21031
- */ error(message, error, context) {
21032
- if (this.config.level < LogLevel.ERROR) return;
21033
- const errorObj = error instanceof Error ? error : new Error(message), themeError = error instanceof ThemeError ? error : new ThemeError(message, ThemeErrorCode.UNKNOWN_ERROR, context);
21034
- this.config.enableConsole && console.error(`[ThemeError] ${message}`, {
21035
- error: errorObj,
21036
- context: {
21037
- ...context,
21038
- ...themeError.context
21039
- },
21040
- code: themeError.code
21041
- }), this.config.onError?.(themeError, context);
21302
+ /**
21303
+ * Validate Atomix configuration structure
21304
+ *
21305
+ * Performs basic validation to catch common configuration errors early.
21306
+ * Returns warnings for potential issues.
21307
+ *
21308
+ * @param config - Configuration object to validate
21309
+ * @returns Array of validation warnings (empty if valid)
21310
+ *
21311
+ * @example
21312
+ * ```typescript
21313
+ * import { loadAtomixConfig, validateConfig } from '@shohojdhara/atomix/config';
21314
+ *
21315
+ * const config = loadAtomixConfig();
21316
+ * const warnings = validateConfig(config);
21317
+ * warnings.forEach(w => console.warn(w));
21318
+ * ```
21319
+ */
21320
+ function validateConfig$1(config) {
21321
+ const warnings = [];
21322
+ // Check prefix format
21323
+ // Check theme structure
21324
+ if (config.prefix && (/^[a-zA-Z][a-zA-Z0-9-]*$/.test(config.prefix) || warnings.push(`Invalid prefix "${config.prefix}". Prefix should start with a letter and contain only letters, numbers, and hyphens.\nExample: "myapp", "brand-ui", "enterprise"`),
21325
+ config.prefix.length < 2 && warnings.push(`Prefix "${config.prefix}" is too short. Use at least 2 characters for clarity.\nExample: "app" instead of "a"`)),
21326
+ config.theme && (
21327
+ // Warn if both extend and tokens are provided
21328
+ config.theme.extend && config.theme.tokens && warnings.push("Both theme.extend and theme.tokens are defined. theme.tokens will take precedence and completely replace the default token system.\nIf you want to extend defaults, remove theme.tokens and use only theme.extend."),
21329
+ config.theme.extend)) {
21330
+ const extend = config.theme.extend, validThemeKeys = [ "colors", "typography", "spacing", "borderRadius", "shadows", "zIndex", "transitions", "breakpoints" ];
21331
+ // Check for common typos in theme properties
21332
+ Object.keys(extend).forEach((key => {
21333
+ _includesInstanceProperty(validThemeKeys).call(validThemeKeys, key) || warnings.push(`Unknown theme property: "${key}"\nValid properties: ${validThemeKeys.join(", ")}\nDid you mean one of these? Check for typos.`);
21334
+ }));
21042
21335
  }
21043
- /**
21044
- * Log a warning
21045
- */ warn(message, context) {
21046
- this.config.level < LogLevel.WARN || (this.config.enableConsole && console.warn(`[ThemeWarning] ${message}`, context || {}),
21047
- this.config.onWarn?.(message, context));
21336
+ // Validate advanced features
21337
+ if (config.interactiveEffects) {
21338
+ const ie = config.interactiveEffects;
21339
+ // Validate vortex settings
21340
+ ie.vortex && (ie.vortex.strength && (ie.vortex.strength < 0 || ie.vortex.strength > 10) && warnings.push("Vortex strength should be between 0 and 10 for optimal performance"),
21341
+ ie.vortex.radius && ie.vortex.radius < 0 && warnings.push("Vortex radius should be a positive number"),
21342
+ ie.vortex.decay && (ie.vortex.decay <= 0 || ie.vortex.decay > 1) && warnings.push("Vortex decay should be between 0 and 1")),
21343
+ // Validate chromatic aberration settings
21344
+ ie.chromaticAberration && (ie.chromaticAberration.redShift && Math.abs(ie.chromaticAberration.redShift) > .1 && warnings.push("Chromatic red shift value seems unusually high (>0.1), verify this is intended"),
21345
+ ie.chromaticAberration.blueShift && Math.abs(ie.chromaticAberration.blueShift) > .1 && warnings.push("Chromatic blue shift value seems unusually high (>0.1), verify this is intended"),
21346
+ ie.chromaticAberration.edgeThreshold && (ie.chromaticAberration.edgeThreshold < 0 || ie.chromaticAberration.edgeThreshold > 1) && warnings.push("Chromatic edge threshold should be between 0 and 1")),
21347
+ // Validate mouse interaction settings
21348
+ ie.mouseInteraction && ie.mouseInteraction.sensitivity && ie.mouseInteraction.sensitivity < 0 && warnings.push("Mouse sensitivity should be a positive number"),
21349
+ // Validate animation speed settings
21350
+ ie.animationSpeed && (ie.animationSpeed.base && ie.animationSpeed.base <= 0 && warnings.push("Animation base speed should be greater than 0"),
21351
+ ie.animationSpeed.timeMultiplier && ie.animationSpeed.timeMultiplier <= 0 && warnings.push("Animation time multiplier should be greater than 0"));
21048
21352
  }
21049
- /**
21050
- * Log an info message
21051
- */ info(message, context) {
21052
- this.config.level < LogLevel.INFO || (this.config.enableConsole && console.info(`[ThemeInfo] ${message}`, context || {}),
21053
- this.config.onInfo?.(message, context));
21353
+ // Validate optimization settings
21354
+ if (config.optimization) {
21355
+ const opt = config.optimization;
21356
+ // Validate responsive breakpoints
21357
+ if (opt.responsive && opt.responsive.breakpoints) {
21358
+ const breakpoints = opt.responsive.breakpoints;
21359
+ breakpoints.mobile && !isValidCSSLength(breakpoints.mobile) && warnings.push("Mobile breakpoint value is not a valid CSS length"),
21360
+ breakpoints.tablet && !isValidCSSLength(breakpoints.tablet) && warnings.push("Tablet breakpoint value is not a valid CSS length"),
21361
+ breakpoints.desktop && !isValidCSSLength(breakpoints.desktop) && warnings.push("Desktop breakpoint value is not a valid CSS length"),
21362
+ breakpoints.wide && !isValidCSSLength(breakpoints.wide) && warnings.push("Wide breakpoint value is not a valid CSS length");
21363
+ }
21364
+ // Validate device scaling
21365
+ if (opt.responsive && opt.responsive.deviceScaling) {
21366
+ const scaling = opt.responsive.deviceScaling;
21367
+ scaling.mobile && (scaling.mobile <= 0 || scaling.mobile > 1) && warnings.push("Mobile device scaling should be between 0 and 1"),
21368
+ scaling.tablet && (scaling.tablet <= 0 || scaling.tablet > 1) && warnings.push("Tablet device scaling should be between 0 and 1"),
21369
+ scaling.desktop && (scaling.desktop <= 0 || scaling.desktop > 1) && warnings.push("Desktop device scaling should be between 0 and 1");
21370
+ }
21371
+ // Validate performance settings
21372
+ // Validate auto-scaling thresholds
21373
+ if (opt.performance && opt.performance.fpsTarget && (opt.performance.fpsTarget <= 0 || opt.performance.fpsTarget > 240) && warnings.push("FPS target should be a reasonable value (typically 30-120)"),
21374
+ opt.autoScaling && opt.autoScaling.qualityThresholds) {
21375
+ const thresholds = opt.autoScaling.qualityThresholds;
21376
+ thresholds.lowEnd && (thresholds.lowEnd < 0 || thresholds.lowEnd > 1) && warnings.push("Auto-scaling low-end threshold should be between 0 and 1"),
21377
+ thresholds.midRange && (thresholds.midRange < 0 || thresholds.midRange > 1) && warnings.push("Auto-scaling mid-range threshold should be between 0 and 1"),
21378
+ thresholds.highEnd && (thresholds.highEnd < 0 || thresholds.highEnd > 1) && warnings.push("Auto-scaling high-end threshold should be between 0 and 1");
21379
+ }
21054
21380
  }
21055
- /**
21056
- * Log a debug message
21057
- */ debug(message, context) {
21058
- this.config.level < LogLevel.DEBUG || (this.config.enableConsole, this.config.onDebug?.(message, context));
21381
+ // Validate visual polish settings
21382
+ if (config.visualPolish) {
21383
+ const vp = config.visualPolish;
21384
+ // Validate content aware blur settings
21385
+ vp.contentAwareBlur && (void 0 !== vp.contentAwareBlur.edgePreservation && "boolean" != typeof vp.contentAwareBlur.edgePreservation && warnings.push("Content-aware blur edge preservation should be a boolean value"),
21386
+ void 0 !== vp.contentAwareBlur.depthDetection && "boolean" != typeof vp.contentAwareBlur.depthDetection && warnings.push("Content-aware blur depth detection should be a boolean value")),
21387
+ // Validate holographic effects settings
21388
+ vp.holographicEffects && (void 0 !== vp.holographicEffects.enabled && "boolean" != typeof vp.holographicEffects.enabled && warnings.push("Holographic effects enabled should be a boolean value"),
21389
+ void 0 !== vp.holographicEffects.rainbowDiffraction && "boolean" != typeof vp.holographicEffects.rainbowDiffraction && warnings.push("Holographic rainbow diffraction should be a boolean value"));
21059
21390
  }
21391
+ // Validate AI settings
21392
+ var _context;
21393
+ return config.ai && (config.ai.provider && !_includesInstanceProperty(_context = [ "openai", "anthropic" ]).call(_context, config.ai.provider) && warnings.push(`Unknown AI provider: "${config.ai.provider}". Supported: openai, anthropic`),
21394
+ config.ai.temperature && (config.ai.temperature < 0 || config.ai.temperature > 1) && warnings.push("AI temperature should be between 0 and 1"),
21395
+ config.ai.maxTokens && config.ai.maxTokens < 100 && warnings.push("AI maxTokens should typically be 100 or more"),
21396
+ config.ai.rateLimit && (config.ai.rateLimit.requests <= 0 && warnings.push("AI rate limit requests should be greater than 0"),
21397
+ config.ai.rateLimit.windowMs <= 0 && warnings.push("AI rate limit window should be greater than 0"))),
21398
+ // Validate telemetry settings
21399
+ config.telemetry && config.telemetry.path && !config.telemetry.path.endsWith(".json") && warnings.push("Telemetry path should typically end with .json"),
21400
+ warnings;
21060
21401
  }
21061
21402
 
21062
21403
  /**
21063
- * Default logger instance
21064
- */ let defaultLogger = null;
21404
+ * Helper function to validate CSS length values
21405
+ */ function isValidCSSLength(value) {
21406
+ return /^(\d+(\.\d+)?)(px|em|rem|%|vw|vh|vmin|vmax|cm|mm|in|pt|pc|ex|ch)?$/.test(value);
21407
+ }
21065
21408
 
21066
21409
  /**
21067
- * Get or create default logger
21068
- */ function getLogger() {
21069
- return defaultLogger || (defaultLogger = new ThemeLogger), defaultLogger;
21410
+ * Load Atomix configuration from project root
21411
+ *
21412
+ * Attempts to load atomix.config.ts, atomix.config.js, or atomix.config.json from the current working directory.
21413
+ * Falls back to default config if file doesn't exist.
21414
+ *
21415
+ * @param options - Loader options
21416
+ * @returns Loaded configuration or default
21417
+ *
21418
+ * @example
21419
+ * ```typescript
21420
+ * import { loadAtomixConfig } from '@shohojdhara/atomix/config';
21421
+ * import { createTheme } from '@shohojdhara/atomix/theme';
21422
+ *
21423
+ * const config = loadAtomixConfig();
21424
+ * const theme = createTheme(config.theme?.tokens || {});
21425
+ * ```
21426
+ */ function loadAtomixConfig(options = {}) {
21427
+ const {configPath: configPath, required: required = !1} = options, defaultConfig = {
21428
+ prefix: "atomix",
21429
+ theme: {
21430
+ extend: {}
21431
+ }
21432
+ };
21433
+ // Default config
21434
+ // In browser environments, config loading is not supported
21435
+ if ("undefined" != typeof window) {
21436
+ if (required) throw new Error('Config loading requires Node.js file system access.\n\nSolutions:\n1. Provide tokens explicitly to createTheme():\n const css = createTheme({ "--brand-primary": "#6366f1" });\n\n2. Use SSR framework (Next.js, Remix, Astro)\n\n3. Load config on server and pass to client\n\nSee examples/config-examples/browser-only.config.ts');
21437
+ return defaultConfig;
21438
+ }
21439
+ // If a specific config path is provided, try to load it directly
21440
+ if (configPath) return loadConfigAtPath(configPath, required, defaultConfig);
21441
+ // Otherwise, try standard locations in order of preference
21442
+ const possiblePaths = [ "atomix.config.ts", "atomix.config.js", "atomix.config.json" ];
21443
+ for (const path of possiblePaths) {
21444
+ const config = loadConfigAtPath(path, !1, defaultConfig);
21445
+ // If we found a valid config, return it
21446
+ if (JSON.stringify(config) !== JSON.stringify(defaultConfig)) return config;
21447
+ }
21448
+ // If no config file was found or all contained only defaults, return default config
21449
+ if (required) throw new Error('No Atomix configuration file found in project root.\n\nExpected one of:\n - atomix.config.ts (recommended)\n - atomix.config.js\n - atomix.config.json\n\nQuick Fix:\n1. Create a config file in your project root:\n touch atomix.config.ts\n\n2. Add basic configuration:\n import { defineConfig } from "@shohojdhara/atomix/config";\n export default defineConfig({ prefix: "myapp" });\n\n3. Or copy an example:\n cp node_modules/@shohojdhara/atomix/examples/config-examples/standard.config.ts ./atomix.config.ts');
21450
+ return defaultConfig;
21451
+ }
21452
+
21453
+ /**
21454
+ * Helper function to load config from a specific path
21455
+ */ function loadConfigAtPath(path, required, defaultConfig) {
21456
+ try {
21457
+ // Use dynamic import for ESM compatibility
21458
+ const configModule = require(path), config = configModule.default || configModule;
21459
+ // Validate it's an AtomixConfig
21460
+ if (config && "object" == typeof config) return config;
21461
+ throw new Error("Invalid config format");
21462
+ } catch (error) {
21463
+ if (required) throw new Error(`Failed to load config from ${path}: ${error.message}`);
21464
+ // Return default config if not required
21465
+ return defaultConfig;
21466
+ }
21070
21467
  }
21071
21468
 
21469
+ /**
21470
+ * Resolve config path
21471
+ *
21472
+ * Finds atomix.config.ts in the project, checking common locations.
21473
+ * Returns null in browser environments where file system access is not available.
21474
+ *
21475
+ * This function is designed to help tools identify if a config exists without loading it.
21476
+ *
21477
+ * @param configPath - Optional custom path to check
21478
+ * @returns Absolute path to config file or null if not found
21479
+ */
21072
21480
  /**
21073
21481
  * Core Theme Functions
21074
21482
  *
21075
- * Simplified theme system using DesignTokens only.
21483
+ * Unified theme system that handles both DesignTokens and Theme objects.
21076
21484
  * Config-first approach: loads from atomix.config.ts when no input is provided.
21485
+ * Config-first approach: loads advanced features from config when available.
21077
21486
  */
21078
21487
  /**
21079
- * Create theme CSS from DesignTokens
21488
+ * Create theme CSS from tokens or Theme object
21080
21489
  *
21081
21490
  * **Config-First Approach**: If no input is provided, loads from `atomix.config.ts`.
21491
+ * Config file is required for automatic loading.
21082
21492
  *
21083
- * @param input - DesignTokens (partial) or undefined (loads from config)
21493
+ * @param input - DesignTokens (partial), Theme object, or undefined (loads from config)
21084
21494
  * @param options - CSS generation options (prefix is automatically read from config if not provided)
21085
21495
  * @returns CSS string with custom properties
21086
21496
  * @throws Error if config loading fails when no input is provided
21087
21497
  *
21088
21498
  * @example
21089
21499
  * ```typescript
21090
- * // Loads from atomix.config.ts
21500
+ * // Loads from atomix.config.ts (config file required)
21091
21501
  * const css = createTheme();
21092
21502
  *
21093
21503
  * // Using DesignTokens
@@ -21096,42 +21506,151 @@ class ThemeLogger {
21096
21506
  * 'spacing-4': '1rem',
21097
21507
  * });
21098
21508
  *
21509
+ * // Using Theme object
21510
+ * const theme = createThemeObject({ palette: { primary: { main: '#7c3aed' } } });
21511
+ * const css = createTheme(theme);
21512
+ *
21099
21513
  * // With custom options
21100
21514
  * const css = createTheme(undefined, { prefix: 'myapp', selector: ':root' });
21101
21515
  * ```
21102
- */ function createTheme(input, options) {
21103
- // Validate options if provided
21104
- if (options?.prefix) {
21105
- const prefixPattern = /^[a-z][a-z0-9-]*$/;
21106
- if (!prefixPattern.test(options.prefix)) throw new ThemeError(`Invalid CSS variable prefix: "${options.prefix}". Prefix must start with a lowercase letter and contain only lowercase letters, numbers, and hyphens (e.g., "atomix", "my-app").`, ThemeErrorCode.THEME_VALIDATION_FAILED, {
21107
- prefix: options.prefix,
21108
- pattern: prefixPattern.toString()
21109
- });
21110
- }
21111
- // Validate selector if provided
21112
- if (options?.selector && ("string" != typeof options.selector || 0 === options.selector.trim().length)) throw new ThemeError(`Invalid CSS selector: "${options.selector}". Selector must be a non-empty string (e.g., ":root", ".my-theme").`, ThemeErrorCode.THEME_VALIDATION_FAILED, {
21113
- selector: options.selector
21114
- });
21115
- // Determine tokens based on input
21116
- let tokens;
21117
- if (input) {
21118
- // Validate input tokens structure
21119
- if ("object" != typeof input || null === input || Array.isArray(input)) throw new ThemeError(`Invalid tokens input. Expected an object with DesignTokens, but received: ${typeof input}.`, ThemeErrorCode.THEME_VALIDATION_FAILED, {
21120
- inputType: typeof input
21121
- });
21122
- // Use DesignTokens directly
21123
- tokens = input;
21516
+ */
21517
+ function createTheme(input, options) {
21518
+ let tokens, configPrefix;
21519
+ // If no input provided, load from config (required)
21520
+ if (input)
21521
+ // Convert Theme object to DesignTokens
21522
+ tokens = !0 === input.__isJSTheme || input.palette && input.typography ? themeToDesignTokens(input) : input; else {
21523
+ const configTokens = function() {
21524
+ // Check if we're in a browser environment
21525
+ if ("undefined" != typeof window) throw new Error("loadThemeFromConfigSync: Not available in browser environment. Config loading requires Node.js/SSR environment.");
21526
+ // Use dynamic import to load the config loader
21527
+ // This allows bundlers to handle external dependencies properly
21528
+ let loadAtomixConfig;
21529
+ try {
21530
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
21531
+ const {loadAtomixConfig: loader} = require("../../config/loader");
21532
+ loadAtomixConfig = loader;
21533
+ } catch (error) {
21534
+ throw new Error("Config loader module not available");
21535
+ }
21536
+ const config = loadAtomixConfig({
21537
+ configPath: undefined,
21538
+ required: !0
21539
+ });
21540
+ if (!config?.theme) return createTokens({});
21541
+ if ((obj = config.theme) && "object" == typeof obj && (obj.palette || obj.typography || obj.spacing || obj.breakpoints || obj.colors)) return themeToDesignTokens(config.theme);
21542
+ // Handle the config.theme object which has extend/tokens/themes properties
21543
+ // Extract the actual tokens from the theme configuration
21544
+ // Helper type guard function
21545
+ var obj;
21546
+ const themeConfig = config.theme;
21547
+ let tokensToApply = {};
21548
+ return themeConfig.tokens ?
21549
+ // If tokens is provided, use it as the base
21550
+ tokensToApply = themeConfig.tokens : themeConfig.extend && (
21551
+ // If only extend is provided, use it as overrides
21552
+ tokensToApply = themeConfig.extend),
21553
+ // Apply advanced feature configurations as tokens
21554
+ config.interactiveEffects && (
21555
+ // Vortex effects
21556
+ config.interactiveEffects.vortex && (tokensToApply = {
21557
+ ...tokensToApply,
21558
+ "interactive-vortex-enabled": String(config.interactiveEffects.vortex.enabled ?? !1),
21559
+ "interactive-vortex-strength": String(config.interactiveEffects.vortex.strength ?? .5),
21560
+ "interactive-vortex-radius": String(config.interactiveEffects.vortex.radius ?? 100),
21561
+ "interactive-vortex-decay": String(config.interactiveEffects.vortex.decay ?? .8)
21562
+ }),
21563
+ // Chromatic aberration
21564
+ config.interactiveEffects.chromaticAberration && (tokensToApply = {
21565
+ ...tokensToApply,
21566
+ "interactive-chromatic-enabled": String(config.interactiveEffects.chromaticAberration.enabled ?? !1),
21567
+ "interactive-chromatic-mode": config.interactiveEffects.chromaticAberration.mode ?? "lateral",
21568
+ "interactive-chromatic-red-shift": String(config.interactiveEffects.chromaticAberration.redShift ?? .02),
21569
+ "interactive-chromatic-green-shift": String(config.interactiveEffects.chromaticAberration.greenShift ?? 0),
21570
+ "interactive-chromatic-blue-shift": String(config.interactiveEffects.chromaticAberration.blueShift ?? -.02),
21571
+ "interactive-chromatic-edge-only": String(config.interactiveEffects.chromaticAberration.edgeOnly ?? !1),
21572
+ "interactive-chromatic-edge-threshold": String(config.interactiveEffects.chromaticAberration.edgeThreshold ?? .5)
21573
+ }),
21574
+ // Mouse interaction
21575
+ config.interactiveEffects.mouseInteraction && (tokensToApply = {
21576
+ ...tokensToApply,
21577
+ "interactive-mouse-sensitivity": String(config.interactiveEffects.mouseInteraction.sensitivity ?? 1),
21578
+ "interactive-mouse-trail-effect": String(config.interactiveEffects.mouseInteraction.trailEffect ?? !1)
21579
+ }),
21580
+ // Animation speed
21581
+ config.interactiveEffects.animationSpeed && (tokensToApply = {
21582
+ ...tokensToApply,
21583
+ "interactive-animation-speed-base": String(config.interactiveEffects.animationSpeed.base ?? 1),
21584
+ "interactive-animation-speed-multiplier": String(config.interactiveEffects.animationSpeed.timeMultiplier ?? 1)
21585
+ })),
21586
+ // Apply optimization configurations as tokens
21587
+ config.optimization && (
21588
+ // Responsive breakpoints
21589
+ config.optimization.responsive && (config.optimization.responsive.breakpoints && (tokensToApply = {
21590
+ ...tokensToApply,
21591
+ "optimization-breakpoint-mobile": config.optimization.responsive.breakpoints.mobile ?? "0px",
21592
+ "optimization-breakpoint-tablet": config.optimization.responsive.breakpoints.tablet ?? "768px",
21593
+ "optimization-breakpoint-desktop": config.optimization.responsive.breakpoints.desktop ?? "1024px",
21594
+ "optimization-breakpoint-wide": config.optimization.responsive.breakpoints.wide ?? "1440px"
21595
+ }), config.optimization.responsive.deviceScaling && (tokensToApply = {
21596
+ ...tokensToApply,
21597
+ "optimization-device-scaling-mobile": String(config.optimization.responsive.deviceScaling.mobile ?? .5),
21598
+ "optimization-device-scaling-tablet": String(config.optimization.responsive.deviceScaling.tablet ?? .75),
21599
+ "optimization-device-scaling-desktop": String(config.optimization.responsive.deviceScaling.desktop ?? 1)
21600
+ })),
21601
+ // Performance settings
21602
+ config.optimization.performance && (tokensToApply = {
21603
+ ...tokensToApply,
21604
+ "optimization-performance-fps-target": String(config.optimization.performance.fpsTarget ?? 60),
21605
+ "optimization-auto-scaling-enabled": String(config.optimization.performance.autoScaling ?? !1)
21606
+ }),
21607
+ // Auto-scaling settings
21608
+ config.optimization.autoScaling && (tokensToApply = {
21609
+ ...tokensToApply,
21610
+ "optimization-auto-scaling-enabled": String(config.optimization.autoScaling.enabled ?? !1),
21611
+ "optimization-auto-scaling-low-end": String(config.optimization.autoScaling.qualityThresholds?.lowEnd ?? .5),
21612
+ "optimization-auto-scaling-mid-range": String(config.optimization.autoScaling.qualityThresholds?.midRange ?? .75),
21613
+ "optimization-auto-scaling-high-end": String(config.optimization.autoScaling.qualityThresholds?.highEnd ?? 1)
21614
+ })),
21615
+ // Apply visual polish configurations as tokens
21616
+ config.visualPolish && (config.visualPolish.borders && (tokensToApply = {
21617
+ ...tokensToApply,
21618
+ "visual-polish-border-iridescent-glow": String(config.visualPolish.borders.iridescentGlow ?? !1),
21619
+ "visual-polish-border-shimmer-effect": String(config.visualPolish.borders.shimmerEffect ?? !1),
21620
+ "visual-polish-border-beveled-edges": String(config.visualPolish.borders.beveledEdges ?? !1),
21621
+ "visual-polish-border-pulsing-glow": String(config.visualPolish.borders.pulsingGlow ?? !1)
21622
+ }), config.visualPolish.contentAwareBlur && (tokensToApply = {
21623
+ ...tokensToApply,
21624
+ "visual-polish-content-aware-blur-enabled": String(config.visualPolish.contentAwareBlur.enabled ?? !1),
21625
+ "visual-polish-content-aware-depth-detection": String(config.visualPolish.contentAwareBlur.depthDetection ?? !1),
21626
+ "visual-polish-content-aware-edge-preservation": String(config.visualPolish.contentAwareBlur.edgePreservation ?? !1),
21627
+ "visual-polish-content-aware-variable-radius": String(config.visualPolish.contentAwareBlur.variableRadius ?? !1)
21628
+ }), config.visualPolish.holographicEffects && (tokensToApply = {
21629
+ ...tokensToApply,
21630
+ "visual-polish-holographic-enabled": String(config.visualPolish.holographicEffects.enabled ?? !1),
21631
+ "visual-polish-holographic-rainbow-diffraction": String(config.visualPolish.holographicEffects.rainbowDiffraction ?? !1),
21632
+ "visual-polish-holographic-scanline-animation": String(config.visualPolish.holographicEffects.scanlineAnimation ?? !1),
21633
+ "visual-polish-holographic-grid-overlay": String(config.visualPolish.holographicEffects.gridOverlay ?? !1),
21634
+ "visual-polish-holographic-data-stream": String(config.visualPolish.holographicEffects.dataStream ?? !1),
21635
+ "visual-polish-holographic-pulse-rings": String(config.visualPolish.holographicEffects.pulseRings ?? !1)
21636
+ })), createTokens(tokensToApply);
21637
+ }();
21638
+ // Get prefix from config
21639
+ try {
21640
+ // Use the imported function directly instead of require to avoid bundling issues
21641
+ const config = loadAtomixConfig({
21642
+ configPath: "atomix.config.ts",
21643
+ required: !0
21644
+ });
21645
+ configPrefix = config?.prefix;
21646
+ } catch (error) {
21647
+ // Prefix loading failed, but tokens were loaded, so continue
21648
+ }
21649
+ tokens = configTokens;
21124
21650
  }
21125
21651
  // Merge with defaults and generate CSS
21126
- else
21127
- // Auto-loading config from file system is removed for browser compatibility.
21128
- // If no input is provided, we return an empty theme (using defaults only) or user must provide tokens.
21129
- // This allows createTheme to be isomorphic.
21130
- // Warn in development if no input provided
21131
- "production" !== process.env.NODE_ENV && "undefined" != typeof window && console.warn("Atomix: createTheme() called without tokens. Using default tokens only."),
21132
- tokens = {};
21133
- const allTokens = createTokens(tokens), prefix = options?.prefix ?? "atomix";
21134
- // Get prefix from options or use default
21652
+ const allTokens = createTokens(tokens), prefix = options?.prefix ?? configPrefix ?? "atomix";
21653
+ // Get prefix from options, config, or use default
21135
21654
  return generateCSSVariables$1(allTokens, {
21136
21655
  ...options,
21137
21656
  prefix: prefix
@@ -21345,17 +21864,150 @@ class ThemeLogger {
21345
21864
  */
21346
21865
  /**
21347
21866
  * Default storage key for theme persistence
21348
- */ "undefined" != typeof process && process.env;
21349
-
21867
+ */
21350
21868
  /**
21351
- * Check if code is running in a browser environment
21869
+ * Theme System Error Handling
21870
+ *
21871
+ * Centralized error handling for the Atomix theme system.
21872
+ * Provides custom error classes and logging utilities.
21352
21873
  */
21353
- const isBrowser = () => "undefined" != typeof window && "undefined" != typeof document, isServer = () => !isBrowser(), sanitizePath = path => path.replace(/[<>"']/g, "").replace(/\.\./g, "").replace(/\/+/g, "/").replace(/^\/+|\/+$/g, "") // Trim leading/trailing slashes
21354
- , buildThemePath = (themeName, basePath = "/themes", useMinified = !1, cdnPath = null) => {
21355
- // Validate theme name to prevent path injection
21356
- if (!isValidThemeName(themeName)) throw new ThemeError(`Invalid theme name: "${themeName}". Theme names must be lowercase alphanumeric with hyphens (e.g., "my-theme").`, ThemeErrorCode.INVALID_THEME_NAME, {
21357
- themeName: themeName,
21358
- pattern: /^[a-z0-9]+(-[a-z0-9]+)*$/
21874
+ /**
21875
+ * Theme error codes
21876
+ */
21877
+ var ThemeErrorCode, LogLevel;
21878
+
21879
+ "undefined" != typeof process && process.env, function(ThemeErrorCode) {
21880
+ /** Theme not found in registry */
21881
+ ThemeErrorCode.THEME_NOT_FOUND = "THEME_NOT_FOUND",
21882
+ /** Theme failed to load */
21883
+ ThemeErrorCode.THEME_LOAD_FAILED = "THEME_LOAD_FAILED",
21884
+ /** Theme validation failed */
21885
+ ThemeErrorCode.THEME_VALIDATION_FAILED = "THEME_VALIDATION_FAILED",
21886
+ /** Configuration loading failed */
21887
+ ThemeErrorCode.CONFIG_LOAD_FAILED = "CONFIG_LOAD_FAILED",
21888
+ /** Configuration validation failed */
21889
+ ThemeErrorCode.CONFIG_VALIDATION_FAILED = "CONFIG_VALIDATION_FAILED",
21890
+ /** Circular dependency detected */
21891
+ ThemeErrorCode.CIRCULAR_DEPENDENCY = "CIRCULAR_DEPENDENCY",
21892
+ /** Missing dependency */
21893
+ ThemeErrorCode.MISSING_DEPENDENCY = "MISSING_DEPENDENCY",
21894
+ /** Storage operation failed */
21895
+ ThemeErrorCode.STORAGE_ERROR = "STORAGE_ERROR",
21896
+ /** Invalid theme name */
21897
+ ThemeErrorCode.INVALID_THEME_NAME = "INVALID_THEME_NAME",
21898
+ /** CSS injection failed */
21899
+ ThemeErrorCode.CSS_INJECTION_FAILED = "CSS_INJECTION_FAILED",
21900
+ /** Invalid color format */
21901
+ ThemeErrorCode.INVALID_COLOR_FORMAT = "INVALID_COLOR_FORMAT",
21902
+ /** Missing required token */
21903
+ ThemeErrorCode.MISSING_REQUIRED_TOKEN = "MISSING_REQUIRED_TOKEN",
21904
+ /** Accessibility contrast violation */
21905
+ ThemeErrorCode.CONTRAST_VIOLATION = "CONTRAST_VIOLATION",
21906
+ /** Invalid token type */
21907
+ ThemeErrorCode.INVALID_TOKEN_TYPE = "INVALID_TOKEN_TYPE",
21908
+ /** Unknown error */
21909
+ ThemeErrorCode.UNKNOWN_ERROR = "UNKNOWN_ERROR";
21910
+ }(ThemeErrorCode || (ThemeErrorCode = {}));
21911
+
21912
+ /**
21913
+ * Custom error class for theme-related errors
21914
+ */
21915
+ class ThemeError extends Error {
21916
+ constructor(message, code = ThemeErrorCode.UNKNOWN_ERROR, context) {
21917
+ super(message), this.name = "ThemeError", this.code = code, this.context = context,
21918
+ this.timestamp = Date.now(),
21919
+ // Maintains proper stack trace for where our error was thrown (only available on V8)
21920
+ Error.captureStackTrace && Error.captureStackTrace(this, ThemeError);
21921
+ }
21922
+ /**
21923
+ * Convert error to JSON for logging
21924
+ */ toJSON() {
21925
+ return {
21926
+ name: this.name,
21927
+ message: this.message,
21928
+ code: this.code,
21929
+ context: this.context,
21930
+ timestamp: this.timestamp,
21931
+ stack: this.stack
21932
+ };
21933
+ }
21934
+ }
21935
+
21936
+ /**
21937
+ * Log level
21938
+ */ !function(LogLevel) {
21939
+ LogLevel[LogLevel.ERROR = 0] = "ERROR", LogLevel[LogLevel.WARN = 1] = "WARN", LogLevel[LogLevel.INFO = 2] = "INFO",
21940
+ LogLevel[LogLevel.DEBUG = 3] = "DEBUG";
21941
+ }(LogLevel || (LogLevel = {}));
21942
+
21943
+ /**
21944
+ * Theme Logger
21945
+ *
21946
+ * Centralized logging for the theme system.
21947
+ * Replaces console statements with structured logging.
21948
+ */
21949
+ class ThemeLogger {
21950
+ constructor(config = {}) {
21951
+ this.config = {
21952
+ level: config.level ?? ("undefined" != typeof process && "production" === process.env?.NODE_ENV ? LogLevel.WARN : LogLevel.INFO),
21953
+ enableConsole: config.enableConsole ?? !0,
21954
+ onError: config.onError,
21955
+ onWarn: config.onWarn,
21956
+ onInfo: config.onInfo,
21957
+ onDebug: config.onDebug
21958
+ };
21959
+ }
21960
+ /**
21961
+ * Log an error
21962
+ */ error(message, error, context) {
21963
+ if (this.config.level < LogLevel.ERROR) return;
21964
+ const errorObj = error instanceof Error ? error : new Error(message), themeError = error instanceof ThemeError ? error : new ThemeError(message, ThemeErrorCode.UNKNOWN_ERROR, context);
21965
+ this.config.enableConsole && console.error(`[ThemeError] ${message}`, {
21966
+ error: errorObj,
21967
+ context: {
21968
+ ...context,
21969
+ ...themeError.context
21970
+ },
21971
+ code: themeError.code
21972
+ }), this.config.onError?.(themeError, context);
21973
+ }
21974
+ /**
21975
+ * Log a warning
21976
+ */ warn(message, context) {
21977
+ this.config.level < LogLevel.WARN || (this.config.enableConsole && console.warn(`[ThemeWarning] ${message}`, context || {}),
21978
+ this.config.onWarn?.(message, context));
21979
+ }
21980
+ /**
21981
+ * Log an info message
21982
+ */ info(message, context) {
21983
+ this.config.level < LogLevel.INFO || (this.config.enableConsole && console.info(`[ThemeInfo] ${message}`, context || {}),
21984
+ this.config.onInfo?.(message, context));
21985
+ }
21986
+ /**
21987
+ * Log a debug message
21988
+ */ debug(message, context) {
21989
+ this.config.level < LogLevel.DEBUG || (this.config.enableConsole, this.config.onDebug?.(message, context));
21990
+ }
21991
+ }
21992
+
21993
+ /**
21994
+ * Default logger instance
21995
+ */ let defaultLogger = null;
21996
+
21997
+ /**
21998
+ * Get or create default logger
21999
+ */ function getLogger() {
22000
+ return defaultLogger || (defaultLogger = new ThemeLogger), defaultLogger;
22001
+ }
22002
+
22003
+ /**
22004
+ * Check if code is running in a browser environment
22005
+ */ const isBrowser = () => "undefined" != typeof window && "undefined" != typeof document, isServer = () => !isBrowser(), sanitizePath = path => path.replace(/[<>"']/g, "").replace(/\.\./g, "").replace(/\/+/g, "/").replace(/^\/+|\/+$/g, "") // Trim leading/trailing slashes
22006
+ , buildThemePath = (themeName, basePath = "/themes", useMinified = !1, cdnPath = null) => {
22007
+ // Validate theme name to prevent path injection
22008
+ if (!isValidThemeName(themeName)) throw new ThemeError(`Invalid theme name: "${themeName}". Theme names must be lowercase alphanumeric with hyphens (e.g., "my-theme").`, ThemeErrorCode.INVALID_THEME_NAME, {
22009
+ themeName: themeName,
22010
+ pattern: /^[a-z0-9]+(-[a-z0-9]+)*$/
21359
22011
  });
21360
22012
  const fileName = `${themeName}${useMinified ? ".min.css" : ".css"}`;
21361
22013
  return cdnPath ? `${sanitizePath(cdnPath)}/${fileName}` : `${sanitizePath(basePath)}/${fileName.replace(/^\//, "")}`;
@@ -21434,105 +22086,427 @@ const isBrowser = () => "undefined" != typeof window && "undefined" != typeof do
21434
22086
  }))
21435
22087
  });
21436
22088
 
22089
+ // ============================================================================
22090
+ // Theme Mode Switching
22091
+ // ============================================================================
21437
22092
  /**
21438
- * Theme Utilities
22093
+ * Switch between light and dark themes
21439
22094
  *
21440
- * Helper utilities for working with themes, including color manipulation,
21441
- * spacing helpers, and theme value accessors.
21442
- */
22095
+ * Automatically toggles a class on the root element and persists the choice.
22096
+ *
22097
+ * @param mode - Theme mode ('light', 'dark', or 'system')
22098
+ * @param options - Configuration options
22099
+ *
22100
+ * @example
22101
+ * ```typescript
22102
+ * import { switchTheme } from '@shohojdhara/atomix/theme/utils';
22103
+ *
22104
+ * // Switch to dark mode
22105
+ * switchTheme('dark');
22106
+ *
22107
+ * // Toggle between light/dark
22108
+ * const current = getCurrentTheme();
22109
+ * switchTheme(current === 'dark' ? 'light' : 'dark');
22110
+ * ```
22111
+ */ function switchTheme(mode, options = {}) {
22112
+ const {selector: selector = ":root", storageKey: storageKey = "atomix-theme", enableTransition: enableTransition = !0, transitionDuration: transitionDuration = 300} = options, resolvedMode = "system" === mode ? getSystemTheme() : mode, root = ":root" === selector ? document.documentElement : document.querySelector(selector);
22113
+ // Determine actual mode (resolve 'system')
22114
+ if (root) {
22115
+ // Add transition class if enabled
22116
+ if (enableTransition) {
22117
+ const htmlRoot = root;
22118
+ htmlRoot.style.transition = `all ${transitionDuration}ms ease-in-out`,
22119
+ // Remove transition after it completes
22120
+ setTimeout((() => {
22121
+ htmlRoot.style.transition = "";
22122
+ }), transitionDuration);
22123
+ }
22124
+ // Apply theme class
22125
+ root.classList.remove("atomix-theme-light", "atomix-theme-dark"), root.classList.add(`atomix-theme-${resolvedMode}`),
22126
+ // Set data attribute for CSS selectors
22127
+ root.setAttribute("data-theme", resolvedMode),
22128
+ // Persist choice
22129
+ persistTheme(resolvedMode, {
22130
+ storageKey: storageKey
22131
+ }),
22132
+ // Dispatch custom event for listeners
22133
+ window.dispatchEvent(new CustomEvent("atomix-theme-change", {
22134
+ detail: {
22135
+ mode: resolvedMode
22136
+ }
22137
+ }));
22138
+ }
22139
+ }
22140
+
22141
+ /**
22142
+ * Toggle between light and dark themes
22143
+ *
22144
+ * @param options - Configuration options
22145
+ * @returns The new theme mode
22146
+ *
22147
+ * @example
22148
+ * ```typescript
22149
+ * const newMode = toggleTheme();
22150
+ * console.log('Switched to:', newMode);
22151
+ * ```
22152
+ */ function toggleTheme(options = {}) {
22153
+ const next = "dark" === getCurrentTheme(options.storageKey) ? "light" : "dark";
22154
+ return switchTheme(next, options), next;
22155
+ }
22156
+
22157
+ /**
22158
+ * Get current theme mode
22159
+ *
22160
+ * @param storageKey - Storage key (default: 'atomix-theme')
22161
+ * @returns Current theme mode or 'light' if not set
22162
+ */ function getCurrentTheme(storageKey = "atomix-theme") {
22163
+ return "undefined" == typeof window ? "light" : localStorage.getItem(storageKey) || "light";
22164
+ }
22165
+
22166
+ /**
22167
+ * Get system theme preference
22168
+ *
22169
+ * @returns 'dark' if system prefers dark mode, 'light' otherwise
22170
+ */ function getSystemTheme() {
22171
+ return "undefined" == typeof window ? "light" : window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
22172
+ }
22173
+
22174
+ /**
22175
+ * Initialize theme based on saved preference or system preference
22176
+ *
22177
+ * Call this once at app startup.
22178
+ *
22179
+ * @param options - Configuration options
22180
+ * @returns The initialized theme mode
22181
+ *
22182
+ * @example
22183
+ * ```typescript
22184
+ * // In your app entry point
22185
+ * import { initializeTheme } from '@shohojdhara/atomix/theme/utils';
22186
+ *
22187
+ * const theme = initializeTheme();
22188
+ * console.log('Theme initialized:', theme);
22189
+ * ```
22190
+ */ function initializeTheme(options = {}) {
22191
+ const saved = getCurrentTheme(options.storageKey);
22192
+ // If no saved preference, use system preference
22193
+ if (!saved || "system" === saved) {
22194
+ const system = getSystemTheme();
22195
+ return switchTheme(system, options), system;
22196
+ }
22197
+ // Use saved preference
22198
+ return switchTheme(saved, options), saved;
22199
+ }
22200
+
22201
+ /**
22202
+ * Listen for system theme changes
22203
+ *
22204
+ * @param callback - Function to call when system theme changes
22205
+ * @returns Cleanup function to stop listening
22206
+ *
22207
+ * @example
22208
+ * ```typescript
22209
+ * const cleanup = listenToSystemTheme((mode) => {
22210
+ * console.log('System theme changed to:', mode);
22211
+ * switchTheme(mode);
22212
+ * });
22213
+ *
22214
+ * // Later, when component unmounts
22215
+ * cleanup();
22216
+ * ```
22217
+ */ function listenToSystemTheme(callback) {
22218
+ if ("undefined" == typeof window) return () => {};
22219
+ const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)"), handler = e => {
22220
+ callback(e.matches ? "dark" : "light");
22221
+ };
22222
+ // Modern browsers
22223
+ return mediaQuery.addEventListener ? (mediaQuery.addEventListener("change", handler),
22224
+ () => mediaQuery.removeEventListener("change", handler)) : (
22225
+ // Fallback for older browsers
22226
+ mediaQuery.addListener(handler), () => mediaQuery.removeListener(handler));
22227
+ }
22228
+
22229
+ // ============================================================================
22230
+ // Theme Persistence
22231
+ // ============================================================================
22232
+ /**
22233
+ * Save theme preference to storage
22234
+ *
22235
+ * @param mode - Theme mode to save
22236
+ * @param options - Persistence options
22237
+ */ function persistTheme(mode, options = {}) {
22238
+ if ("undefined" == typeof window) return;
22239
+ const {storageKey: storageKey = "atomix-theme", storageType: storageType = "localStorage"} = options;
22240
+ ("localStorage" === storageType ? localStorage : sessionStorage).setItem(storageKey, mode);
22241
+ }
22242
+
22243
+ /**
22244
+ * Clear saved theme preference
22245
+ *
22246
+ * @param options - Persistence options
22247
+ */ function clearThemePreference(options = {}) {
22248
+ if ("undefined" == typeof window) return;
22249
+ const {storageKey: storageKey = "atomix-theme", storageType: storageType = "localStorage"} = options;
22250
+ ("localStorage" === storageType ? localStorage : sessionStorage).removeItem(storageKey);
22251
+ }
22252
+
21443
22253
  // ============================================================================
21444
- // Color Manipulation Utilities
22254
+ // Theme Tokens Manipulation
21445
22255
  // ============================================================================
21446
22256
  /**
21447
- * Convert hex color to RGB object
22257
+ * Merge multiple token sets
22258
+ *
22259
+ * Deep merges token objects, with later tokens overriding earlier ones.
22260
+ *
22261
+ * @param tokens - Token objects to merge
22262
+ * @returns Merged tokens
22263
+ *
22264
+ * @example
22265
+ * ```typescript
22266
+ * const merged = mergeTokens(
22267
+ * baseTokens,
22268
+ * { colors: { primary: { main: '#custom' } } }
22269
+ * );
22270
+ * ```
22271
+ */ function mergeTokens(...tokens) {
22272
+ return _reduceInstanceProperty(tokens).call(tokens, ((acc, current) => deepMerge(acc, current)), {});
22273
+ }
22274
+
22275
+ /**
22276
+ * Override specific tokens
22277
+ *
22278
+ * Creates a new token object with specific overrides.
22279
+ *
22280
+ * @param base - Base tokens
22281
+ * @param overrides - Tokens to override
22282
+ * @returns New tokens with overrides applied
22283
+ *
22284
+ * @example
22285
+ * ```typescript
22286
+ * const customized = overrideTokens(defaultTokens, {
22287
+ * colors: {
22288
+ * primary: { main: '#ff0000' }
22289
+ * }
22290
+ * });
22291
+ * ```
22292
+ */ function overrideTokens(base, overrides) {
22293
+ return deepMerge({
22294
+ ...base
22295
+ }, overrides);
22296
+ }
22297
+
22298
+ /**
22299
+ * Pick specific token categories
22300
+ *
22301
+ * Extracts only the specified categories from tokens.
22302
+ *
22303
+ * @param tokens - Source tokens
22304
+ * @param categories - Categories to pick
22305
+ * @returns Tokens with only selected categories
22306
+ *
22307
+ * @example
22308
+ * ```typescript
22309
+ * const colorTokens = pickTokens(allTokens, ['colors']);
22310
+ * ```
22311
+ */ function pickTokens(tokens, categories) {
22312
+ const result = {};
22313
+ return categories.forEach((category => {
22314
+ tokens[category] && (result[category] = tokens[category]);
22315
+ })), result;
22316
+ }
22317
+
22318
+ /**
22319
+ * Omit specific token categories
22320
+ *
22321
+ * Removes specified categories from tokens.
22322
+ *
22323
+ * @param tokens - Source tokens
22324
+ * @param categories - Categories to omit
22325
+ * @returns Tokens without omitted categories
22326
+ *
22327
+ * @example
22328
+ * ```typescript
22329
+ * const withoutColors = omitTokens(allTokens, ['colors']);
22330
+ * ```
22331
+ */ function omitTokens(tokens, categories) {
22332
+ const result = {
22333
+ ...tokens
22334
+ };
22335
+ return categories.forEach((category => {
22336
+ delete result[category];
22337
+ })), result;
22338
+ }
22339
+
22340
+ // ============================================================================
22341
+ // Color Utilities
22342
+ // ============================================================================
22343
+ /**
22344
+ * Convert hex color to RGB
22345
+ *
22346
+ * @param hex - Hex color (with or without #)
22347
+ * @returns RGB object { r, g, b }
21448
22348
  */ function hexToRgb$1(hex) {
21449
- const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
21450
- return result ? {
21451
- r: parseInt(result[1], 16),
21452
- g: parseInt(result[2], 16),
21453
- b: parseInt(result[3], 16)
21454
- } : null;
22349
+ // Validate
22350
+ if (
22351
+ // Remove # if present
22352
+ // Handle shorthand hex
22353
+ 3 === (hex = hex.replace(/^#/, "")).length && (hex = hex.split("").map((c => c + c)).join("")),
22354
+ 6 !== hex.length) return null;
22355
+ const num = parseInt(hex, 16);
22356
+ return {
22357
+ r: num >> 16 & 255,
22358
+ g: num >> 8 & 255,
22359
+ b: 255 & num
22360
+ };
21455
22361
  }
21456
22362
 
21457
22363
  /**
21458
- * Convert RGB to hex color
22364
+ * Convert RGB to hex
22365
+ *
22366
+ * @param r - Red (0-255)
22367
+ * @param g - Green (0-255)
22368
+ * @param b - Blue (0-255)
22369
+ * @returns Hex color with #
21459
22370
  */ function rgbToHex(r, g, b) {
21460
- const toHex = val => Math.round(Math.max(0, Math.min(255, val))).toString(16).padStart(2, "0");
21461
- return `#${toHex(r ?? 0)}${toHex(g ?? 0)}${toHex(b ?? 0)}`;
22371
+ return "#" + [ r, g, b ].map((x => {
22372
+ const hex = x.toString(16);
22373
+ return 1 === hex.length ? "0" + hex : hex;
22374
+ })).join("");
21462
22375
  }
21463
22376
 
21464
22377
  /**
21465
- * Calculate relative luminance of a color
21466
- * Used for determining contrast ratios
21467
- */ function getLuminance(color) {
21468
- const rgb = hexToRgb$1(color);
22378
+ * Calculate luminance of a color
22379
+ *
22380
+ * Used for determining contrast ratios.
22381
+ *
22382
+ * @param hex - Hex color
22383
+ * @returns Luminance value (0-1)
22384
+ */ function getLuminance(hex) {
22385
+ const rgb = hexToRgb$1(hex);
21469
22386
  if (!rgb) return 0;
21470
- const {r: r, g: g, b: b} = rgb, [rs, gs, bs] = [ r ?? 0, g ?? 0, b ?? 0 ].map((c => {
21471
- const val = c / 255;
21472
- return val <= .03928 ? val / 12.92 : Math.pow((val + .055) / 1.055, 2.4);
21473
- }));
21474
- return .2126 * (rs ?? 0) + .7152 * (gs ?? 0) + .0722 * (bs ?? 0);
22387
+ const [r, g, b] = [ rgb.r, rgb.g, rgb.b ].map((v => (v /= 255) <= .03928 ? v / 12.92 : Math.pow((v + .055) / 1.055, 2.4)));
22388
+ return .2126 * (r ?? 0) + .7152 * (g ?? 0) + .0722 * (b ?? 0);
21475
22389
  }
21476
22390
 
21477
22391
  /**
21478
22392
  * Calculate contrast ratio between two colors
21479
- */ function getContrastRatio(foreground, background) {
21480
- const lumA = getLuminance(foreground), lumB = getLuminance(background);
21481
- return (Math.max(lumA, lumB) + .05) / (Math.min(lumA, lumB) + .05);
22393
+ *
22394
+ * @param hex1 - First hex color
22395
+ * @param hex2 - Second hex color
22396
+ * @returns Contrast ratio (1-21)
22397
+ */ function getContrastRatio(hex1, hex2) {
22398
+ const lum1 = getLuminance(hex1), lum2 = getLuminance(hex2);
22399
+ return (Math.max(lum1, lum2) + .05) / (Math.min(lum1, lum2) + .05);
22400
+ }
22401
+
22402
+ /**
22403
+ * Check if text color passes WCAG AA standard
22404
+ *
22405
+ * @param textColor - Text color hex
22406
+ * @param backgroundColor - Background color hex
22407
+ * @param size - Font size ('small' or 'large')
22408
+ * @returns true if passes WCAG AA
22409
+ */ function isAccessible(textColor, backgroundColor, size = "small") {
22410
+ return getContrastRatio(textColor, backgroundColor) >= ("large" === size ? 3 : 4.5);
21482
22411
  }
21483
22412
 
21484
22413
  /**
21485
- * Get appropriate contrast text color (black or white) for a background color
21486
- */ function getContrastText(background, threshold = 3) {
21487
- const contrastWithWhite = getContrastRatio("#FFFFFF", background), contrastWithBlack = getContrastRatio("#000000", background);
21488
- return contrastWithWhite >= threshold ? "#FFFFFF" : contrastWithBlack >= threshold ? "#000000" : contrastWithWhite > contrastWithBlack ? "#FFFFFF" : "#000000";
22414
+ * Get appropriate text color (black or white) for a background
22415
+ *
22416
+ * @param backgroundColor - Background hex color
22417
+ * @param threshold - Contrast threshold (default: 3)
22418
+ * @returns '#000000' or '#FFFFFF'
22419
+ */ function getContrastText(backgroundColor, threshold = 3) {
22420
+ return getContrastRatio(backgroundColor, "#FFFFFF") >= threshold ? "#FFFFFF" : "#000000";
21489
22421
  }
21490
22422
 
21491
22423
  /**
21492
- * Lighten a color by a given amount
22424
+ * Lighten a color
21493
22425
  *
21494
- * @param color - Hex color string
21495
- * @param amount - Amount to lighten (0-1), default 0.2
22426
+ * @param hex - Base hex color
22427
+ * @param amount - Amount to lighten (0-1)
21496
22428
  * @returns Lightened hex color
21497
- */ function lighten(color, amount = .2) {
21498
- const rgb = hexToRgb$1(color);
21499
- if (!rgb) return color;
21500
- const {r: r, g: g, b: b} = rgb, lightenValue = val => Math.min(255, Math.round(val + (255 - val) * amount));
21501
- return rgbToHex(lightenValue(r), lightenValue(g), lightenValue(b));
22429
+ */ function lighten(hex, amount = 0) {
22430
+ const rgb = hexToRgb$1(hex);
22431
+ if (!rgb) return hex;
22432
+ // Use amount directly as factor (0-1)
22433
+ const factor = Math.max(0, Math.min(1, amount)), r = Math.round(rgb.r + (255 - rgb.r) * factor), g = Math.round(rgb.g + (255 - rgb.g) * factor), b = Math.round(rgb.b + (255 - rgb.b) * factor);
22434
+ return rgbToHex(Math.min(255, r), Math.min(255, g), Math.min(255, b));
21502
22435
  }
21503
22436
 
21504
22437
  /**
21505
- * Darken a color by a given amount
22438
+ * Darken a color
21506
22439
  *
21507
- * @param color - Hex color string
21508
- * @param amount - Amount to darken (0-1), default 0.2
22440
+ * @param hex - Base hex color
22441
+ * @param amount - Amount to darken (0-1)
21509
22442
  * @returns Darkened hex color
21510
- */ function darken(color, amount = .2) {
21511
- const rgb = hexToRgb$1(color);
21512
- if (!rgb) return color;
21513
- const {r: r, g: g, b: b} = rgb, darkenValue = val => Math.max(0, Math.round(val * (1 - amount)));
21514
- return rgbToHex(darkenValue(r), darkenValue(g), darkenValue(b));
22443
+ */ function darken(hex, amount = 0) {
22444
+ const rgb = hexToRgb$1(hex);
22445
+ if (!rgb) return hex;
22446
+ // Use amount directly as factor (0-1)
22447
+ const factor = Math.max(0, Math.min(1, amount)), r = Math.round(rgb.r * (1 - factor)), g = Math.round(rgb.g * (1 - factor)), b = Math.round(rgb.b * (1 - factor));
22448
+ return rgbToHex(Math.max(0, r), Math.max(0, g), Math.max(0, b));
21515
22449
  }
21516
22450
 
21517
22451
  /**
21518
- * Add alpha (opacity) to a color
22452
+ * Add alpha to a color
21519
22453
  *
21520
- * @param color - Hex color string
22454
+ * @param hex - Hex color
21521
22455
  * @param opacity - Opacity value (0-1)
21522
22456
  * @returns RGBA color string
21523
- */ function alpha(color, opacity) {
21524
- const rgb = hexToRgb$1(color);
21525
- if (!rgb) return color;
21526
- const {r: r, g: g, b: b} = rgb;
21527
- return `rgba(${r}, ${g}, ${b}, ${Math.max(0, Math.min(1, opacity))})`;
22457
+ */ function alpha(hex, opacity) {
22458
+ const rgb = hexToRgb$1(hex);
22459
+ if (!rgb) return hex;
22460
+ const validOpacity = Math.max(0, Math.min(1, opacity));
22461
+ return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${validOpacity})`;
21528
22462
  }
21529
22463
 
21530
22464
  /**
21531
22465
  * Emphasize a color (lighten if dark, darken if light)
21532
22466
  *
21533
- * @param color - Hex color string
21534
- * @param coefficient - Amount to emphasize (0-1), default 0.15
22467
+ * @param hex - Hex color
22468
+ * @param amount - Amount to emphasize (0-1)
21535
22469
  * @returns Emphasized hex color
22470
+ */ function emphasize(hex, amount = .15) {
22471
+ return getLuminance(hex) > .5 ? darken(hex, amount) : lighten(hex, amount);
22472
+ }
22473
+
22474
+ /**
22475
+ * Create a spacing utility
22476
+ *
22477
+ * @param spacingInput - Spacing configuration
22478
+ * @returns Spacing function
22479
+ */ function createSpacing(spacingInput = 4) {
22480
+ return (...values) => 0 === values.length ? "0px" : "function" == typeof spacingInput ? spacingInput(...values) : values.map((value => {
22481
+ if ("number" == typeof spacingInput) return value * spacingInput + "px";
22482
+ if (Array.isArray(spacingInput)) {
22483
+ const scaled = spacingInput[value];
22484
+ return "number" == typeof scaled ? `${scaled}px` : `${value}px`;
22485
+ }
22486
+ return `${value}px`;
22487
+ })).join(" ");
22488
+ }
22489
+
22490
+ /**
22491
+ * CSS Variable Generator
22492
+ *
22493
+ * Generates CSS custom properties from theme objects and injects them into the DOM.
22494
+ *
22495
+ * **Token Naming Alignment:**
22496
+ * This generator produces CSS variables that match the SCSS token naming pattern exactly:
22497
+ * - Colors: --atomix-primary, --atomix-primary-1 through --atomix-primary-10
22498
+ * - Spacing: --atomix-spacing-1, --atomix-spacing-4, etc.
22499
+ * - Typography: --atomix-font-size-base, --atomix-font-weight-normal, etc.
22500
+ * - Shadows: --atomix-box-shadow, --atomix-box-shadow-sm, etc.
22501
+ *
22502
+ * All tokens follow the flat structure pattern used in SCSS (not nested like --atomix-palette-primary-main).
22503
+ * This ensures compatibility between SCSS themes and JavaScript themes.
22504
+ *
22505
+ * @see src/styles/03-generic/_generic.root.scss for SCSS token definitions
22506
+ */
22507
+ /**
22508
+ * Convert a nested object to flat CSS variable declarations
22509
+ * Uses iterative approach for better performance with large objects
21536
22510
  */
21537
22511
  /**
21538
22512
  * Generate a color scale from a base color (1-10 steps)
@@ -21600,18 +22574,7 @@ function generateCSSVariables(theme, options = {}) {
21600
22574
  color.dark && (vars[`${prefix}-${key}-hover`] = color.dark),
21601
22575
  // Generate semantic color variants (matches SCSS patterns)
21602
22576
  // Text emphasis: emphasized version of the color for text (--atomix-primary-text-emphasis)
21603
- vars[`${prefix}-${key}-text-emphasis`] = function(color, coefficient = .15) {
21604
- return getLuminance(color) > .5 ? darken(color, coefficient) : lighten(color, coefficient);
21605
- }
21606
- // ============================================================================
21607
- // Spacing Utilities
21608
- // ============================================================================
21609
- /**
21610
- * Create a spacing function from various input types
21611
- *
21612
- * @param spacingInput - Spacing configuration (number, array, or function), default 4
21613
- * @returns Spacing function
21614
- */ (color.main, .15),
22577
+ vars[`${prefix}-${key}-text-emphasis`] = emphasize(color.main, .15),
21615
22578
  // Background subtle: very light version for backgrounds (--atomix-primary-bg-subtle)
21616
22579
  vars[`${prefix}-${key}-bg-subtle`] = alpha(color.main, .1),
21617
22580
  // Border subtle: light version for borders (--atomix-primary-border-subtle)
@@ -21956,29 +22919,7 @@ function generateCSSVariables(theme, options = {}) {
21956
22919
  return vars[`${prefix}-focus-ring-width`] = "3px", vars[`${prefix}-focus-ring-offset`] = "2px",
21957
22920
  vars[`${prefix}-focus-ring-opacity`] = "0.25", vars;
21958
22921
  }(theme.palette, prefix)), theme.custom && Object.keys(theme.custom).length > 0) {
21959
- const customVars =
21960
- /**
21961
- * CSS Variable Generator
21962
- *
21963
- * Generates CSS custom properties from theme objects and injects them into the DOM.
21964
- *
21965
- * **Token Naming Alignment:**
21966
- * This generator produces CSS variables that match the SCSS token naming pattern exactly:
21967
- * - Colors: --atomix-primary, --atomix-primary-1 through --atomix-primary-10
21968
- * - Spacing: --atomix-spacing-1, --atomix-spacing-4, etc.
21969
- * - Typography: --atomix-font-size-base, --atomix-font-weight-normal, etc.
21970
- * - Shadows: --atomix-box-shadow, --atomix-box-shadow-sm, etc.
21971
- *
21972
- * All tokens follow the flat structure pattern used in SCSS (not nested like --atomix-palette-primary-main).
21973
- * This ensures compatibility between SCSS themes and JavaScript themes.
21974
- *
21975
- * @see src/styles/03-generic/_generic.root.scss for SCSS token definitions
21976
- */
21977
- /**
21978
- * Convert a nested object to flat CSS variable declarations
21979
- * Uses iterative approach for better performance with large objects
21980
- */
21981
- function(obj, prefix = "", result = {}) {
22922
+ const customVars = function(obj, prefix = "", result = {}) {
21982
22923
  // Use iterative approach with stack to avoid deep recursion
21983
22924
  const stack = [ {
21984
22925
  obj: obj,
@@ -22432,19 +23373,29 @@ const logger = getLogger(), ThemeProvider = ({children: children, defaultTheme:
22432
23373
  // Check storage first
22433
23374
  if (enablePersistence && storageAdapter.isAvailable()) {
22434
23375
  const stored = storageAdapter.getItem(storageKey);
22435
- if (stored) return stored;
23376
+ if (stored) {
23377
+ // If it looks like a JSON object, parse it
23378
+ if (stored.trim().startsWith("{")) try {
23379
+ return JSON.parse(stored);
23380
+ } catch (e) {
23381
+ return logger.error("Failed to parse stored theme tokens", e), stored;
23382
+ }
23383
+ return stored;
23384
+ }
22436
23385
  }
22437
23386
  // If defaultTheme is provided, use it
22438
23387
  return null != defaultTheme ? defaultTheme : "default";
22439
23388
  // Default fallback
22440
- }), [ defaultTheme, enablePersistence, storageKey ]), [currentTheme, setCurrentTheme] = React.useState((() => "string" == typeof initialDefaultTheme ? initialDefaultTheme : "tokens-theme")), [activeTokens, setActiveTokens] = React.useState((() => {
22441
- // If defaultTheme is DesignTokens, validate and store them
22442
- if (defaultTheme && "string" != typeof defaultTheme) {
23389
+ }), [ defaultTheme, enablePersistence, storageKey, storageAdapter ]), [currentTheme, setCurrentTheme] = React.useState((() => "string" == typeof initialDefaultTheme ? initialDefaultTheme : "tokens-theme")), [activeTokens, setActiveTokens] = React.useState((() => {
23390
+ // 1. Check if initialDefaultTheme (from storage) is an object
23391
+ if (initialDefaultTheme && "string" != typeof initialDefaultTheme) {
23392
+ const {tokens: tokens, validation: validation} = validateAndMergeTokens(initialDefaultTheme);
23393
+ if (validation.valid) return tokens;
23394
+ }
23395
+ // 2. Check if defaultTheme prop is an object
23396
+ if (defaultTheme && "string" != typeof defaultTheme) {
22443
23397
  const {tokens: tokens, validation: validation} = validateAndMergeTokens(defaultTheme);
22444
- return validation.valid ? tokens : (logger.warn("Invalid default theme tokens, using defaults", {
22445
- errors: validation.errors,
22446
- warnings: validation.warnings
22447
- }), createTokens({}));
23398
+ if (validation.valid) return tokens;
22448
23399
  }
22449
23400
  return null;
22450
23401
  })), [isLoading, setIsLoading] = React.useState(!1), [error, setError] = React.useState(null), loadedThemesRef = React.useRef(new Set), themePromisesRef = React.useRef({}), abortControllerRef = React.useRef(null);
@@ -22456,10 +23407,14 @@ const logger = getLogger(), ThemeProvider = ({children: children, defaultTheme:
22456
23407
  React.useEffect((() => {
22457
23408
  isServer() || applyThemeAttributes(String(currentTheme), dataAttribute);
22458
23409
  }), [ currentTheme, dataAttribute ]),
22459
- // Handle theme persistence
23410
+ // Handle persistence
22460
23411
  React.useEffect((() => {
22461
- enablePersistence && storageAdapter.isAvailable() && storageAdapter.setItem(storageKey, String(currentTheme));
22462
- }), [ currentTheme, storageKey, enablePersistence, storageAdapter ]),
23412
+ enablePersistence && storageAdapter.isAvailable() && ("tokens-theme" === currentTheme ?
23413
+ // Only persist if we have actual tokens to store
23414
+ activeTokens && storageAdapter.setItem(storageKey, JSON.stringify(activeTokens)) :
23415
+ // Persist named theme string
23416
+ storageAdapter.setItem(storageKey, String(currentTheme)));
23417
+ }), [ currentTheme, activeTokens, enablePersistence, storageKey, storageAdapter ]),
22463
23418
  // Cleanup: Remove completed promises and abort controllers on unmount
22464
23419
  React.useEffect((() => () => {
22465
23420
  // Cancel any in-flight theme loads
@@ -22600,20 +23555,21 @@ const logger = getLogger(), ThemeProvider = ({children: children, defaultTheme:
22600
23555
  setIsLoading(!1);
22601
23556
  }
22602
23557
  }
22603
- }), [ themes, isThemeLoaded, handleError, basePath, useMinified, cdnPath ]), themeManager = React.useMemo((() => ({})), []), availableThemes = React.useMemo((() => Object.entries(themes).map((([name, metadata]) => ({
23558
+ }), [ themes, isThemeLoaded, handleError, basePath, useMinified, cdnPath ]), updateTheme = React.useCallback((async (sectionOrTokens, values) => setTheme("string" == typeof sectionOrTokens && values ? values : sectionOrTokens)), [ setTheme ]), themeManager = React.useMemo((() => ({})), []), availableThemes = React.useMemo((() => Object.entries(themes).map((([name, metadata]) => ({
22604
23559
  ...metadata,
22605
23560
  name: name
22606
23561
  })))), [ themes ]), contextValue = React.useMemo((() => ({
22607
23562
  theme: currentTheme,
22608
23563
  activeTokens: activeTokens,
22609
23564
  setTheme: setTheme,
23565
+ updateTheme: updateTheme,
22610
23566
  availableThemes: availableThemes,
22611
23567
  isLoading: isLoading,
22612
23568
  error: error,
22613
23569
  isThemeLoaded: isThemeLoaded,
22614
23570
  preloadTheme: preloadTheme,
22615
23571
  themeManager: themeManager
22616
- })), [ currentTheme, activeTokens, setTheme, availableThemes,
23572
+ })), [ currentTheme, activeTokens, setTheme, updateTheme, availableThemes,
22617
23573
  // Use memoized value
22618
23574
  isLoading, error, isThemeLoaded, preloadTheme, themeManager ]);
22619
23575
  // Check if theme is loaded
@@ -22667,6 +23623,7 @@ function useTheme() {
22667
23623
  theme: context.theme,
22668
23624
  activeTokens: context.activeTokens,
22669
23625
  setTheme: context.setTheme,
23626
+ updateTheme: context.updateTheme,
22670
23627
  availableThemes: context.availableThemes,
22671
23628
  isLoading: context.isLoading,
22672
23629
  error: context.error,
@@ -22902,6 +23859,389 @@ function useThemeTokens() {
22902
23859
  }
22903
23860
  }
22904
23861
 
23862
+ /**
23863
+ * useThemeSwitcher Hook
23864
+ *
23865
+ * React hook for managing theme switching with persistence and system preference detection.
23866
+ * Provides an easy-to-use API for dark/light mode toggling.
23867
+ *
23868
+ * @example
23869
+ * ```tsx
23870
+ * import { useThemeSwitcher } from '@shohojdhara/atomix/theme';
23871
+ *
23872
+ * function ThemeToggle() {
23873
+ * const { mode, toggle, setMode, isDark } = useThemeSwitcher();
23874
+ *
23875
+ * return (
23876
+ * <button onClick={toggle}>
23877
+ * {isDark ? '☀️ Light' : '🌙 Dark'}
23878
+ * </button>
23879
+ * );
23880
+ * }
23881
+ * ```
23882
+ */
23883
+ /**
23884
+ * Hook for managing theme switching
23885
+ *
23886
+ * @param options - Configuration options
23887
+ * @returns Theme switcher controls
23888
+ */ function useThemeSwitcher(options = {}) {
23889
+ const {initialMode: initialMode = "system", syncWithSystem: syncWithSystem = !1, storageKey: storageKey = "atomix-theme", enableTransition: enableTransition = !0, transitionDuration: transitionDuration = 300} = options, [mode, setModeState] = React.useState((() => {
23890
+ if ("undefined" == typeof window) return initialMode;
23891
+ // Check for saved preference first
23892
+ const saved = getCurrentTheme(storageKey);
23893
+ return saved && "system" !== saved ? saved : "system" === initialMode ? getSystemTheme() : initialMode;
23894
+ // Fall back to initial mode or system
23895
+ }));
23896
+ // State for current mode
23897
+ // Initialize theme on mount
23898
+ return React.useEffect((() => {
23899
+ "undefined" != typeof window && (
23900
+ // Initialize with proper theme application
23901
+ initializeTheme({
23902
+ storageKey: storageKey,
23903
+ enableTransition: enableTransition,
23904
+ transitionDuration: transitionDuration
23905
+ }),
23906
+ // Update state to match initialized theme
23907
+ setModeState(getCurrentTheme(storageKey)));
23908
+ }), [ storageKey, enableTransition, transitionDuration ]),
23909
+ // Listen for system theme changes if enabled
23910
+ React.useEffect((() => {
23911
+ if (syncWithSystem) return listenToSystemTheme((newMode => {
23912
+ setModeState(newMode), switchTheme(newMode, {
23913
+ storageKey: storageKey,
23914
+ enableTransition: enableTransition,
23915
+ transitionDuration: transitionDuration
23916
+ });
23917
+ }));
23918
+ }), [ syncWithSystem, storageKey, enableTransition, transitionDuration ]), {
23919
+ mode: mode,
23920
+ isDark: "dark" === mode,
23921
+ isLight: "light" === mode,
23922
+ toggle: React.useCallback((() => {
23923
+ const newMode = toggleTheme({
23924
+ storageKey: storageKey,
23925
+ enableTransition: enableTransition,
23926
+ transitionDuration: transitionDuration
23927
+ });
23928
+ return setModeState(newMode), newMode;
23929
+ }), [ storageKey, enableTransition, transitionDuration ]),
23930
+ setMode: React.useCallback((newMode => {
23931
+ switchTheme(newMode, {
23932
+ storageKey: storageKey,
23933
+ enableTransition: enableTransition,
23934
+ transitionDuration: transitionDuration
23935
+ }), setModeState(newMode);
23936
+ }), [ storageKey, enableTransition, transitionDuration ]),
23937
+ resetToSystem: React.useCallback((() => {
23938
+ const systemMode = getSystemTheme();
23939
+ switchTheme(systemMode, {
23940
+ storageKey: storageKey,
23941
+ enableTransition: enableTransition,
23942
+ transitionDuration: transitionDuration
23943
+ }), setModeState(systemMode);
23944
+ }), [ storageKey, enableTransition, transitionDuration ]),
23945
+ clearPreference: React.useCallback((() => {
23946
+ "undefined" != typeof window && localStorage.removeItem(storageKey);
23947
+ }), [ storageKey ])
23948
+ };
23949
+ }
23950
+
23951
+ /**
23952
+ * ThemeToggle component with multiple variants
23953
+ */ const ThemeToggle = ({className: className = "", showLabel: showLabel = !1, lightLabel: lightLabel = "Light", darkLabel: darkLabel = "Dark", iconSize: iconSize = 20, variant: variant = "icon", render: render, ariaLabel: ariaLabel = "Toggle theme", ...hookOptions}) => {
23954
+ const {mode: mode, isDark: isDark, toggle: toggle} = useThemeSwitcher(hookOptions);
23955
+ // Custom render
23956
+ return render ? jsxRuntime.jsx(jsxRuntime.Fragment, {
23957
+ children: render({
23958
+ isDark: isDark,
23959
+ toggle: toggle,
23960
+ mode: mode
23961
+ })
23962
+ }) :
23963
+ // Icon-only variant (default)
23964
+ "icon" === variant ? jsxRuntime.jsx("button", {
23965
+ onClick: toggle,
23966
+ className: `theme-toggle theme-toggle-icon ${className}`,
23967
+ "aria-label": ariaLabel,
23968
+ title: isDark ? darkLabel : lightLabel,
23969
+ style: {
23970
+ background: "none",
23971
+ border: "none",
23972
+ cursor: "pointer",
23973
+ padding: "8px",
23974
+ borderRadius: "50%",
23975
+ display: "flex",
23976
+ alignItems: "center",
23977
+ justifyContent: "center",
23978
+ transition: "all 0.3s ease-in-out"
23979
+ },
23980
+ children: isDark ? jsxRuntime.jsxs("svg", {
23981
+ width: iconSize,
23982
+ height: iconSize,
23983
+ viewBox: "0 0 24 24",
23984
+ fill: "none",
23985
+ stroke: "currentColor",
23986
+ strokeWidth: "2",
23987
+ strokeLinecap: "round",
23988
+ strokeLinejoin: "round",
23989
+ children: [ jsxRuntime.jsx("circle", {
23990
+ cx: "12",
23991
+ cy: "12",
23992
+ r: "5"
23993
+ }), jsxRuntime.jsx("line", {
23994
+ x1: "12",
23995
+ y1: "1",
23996
+ x2: "12",
23997
+ y2: "3"
23998
+ }), jsxRuntime.jsx("line", {
23999
+ x1: "12",
24000
+ y1: "21",
24001
+ x2: "12",
24002
+ y2: "23"
24003
+ }), jsxRuntime.jsx("line", {
24004
+ x1: "4.22",
24005
+ y1: "4.22",
24006
+ x2: "5.64",
24007
+ y2: "5.64"
24008
+ }), jsxRuntime.jsx("line", {
24009
+ x1: "18.36",
24010
+ y1: "18.36",
24011
+ x2: "19.78",
24012
+ y2: "19.78"
24013
+ }), jsxRuntime.jsx("line", {
24014
+ x1: "1",
24015
+ y1: "12",
24016
+ x2: "3",
24017
+ y2: "12"
24018
+ }), jsxRuntime.jsx("line", {
24019
+ x1: "21",
24020
+ y1: "12",
24021
+ x2: "23",
24022
+ y2: "12"
24023
+ }), jsxRuntime.jsx("line", {
24024
+ x1: "4.22",
24025
+ y1: "19.78",
24026
+ x2: "5.64",
24027
+ y2: "18.36"
24028
+ }), jsxRuntime.jsx("line", {
24029
+ x1: "18.36",
24030
+ y1: "5.64",
24031
+ x2: "19.78",
24032
+ y2: "4.22"
24033
+ }) ]
24034
+ }) : jsxRuntime.jsx("svg", {
24035
+ width: iconSize,
24036
+ height: iconSize,
24037
+ viewBox: "0 0 24 24",
24038
+ fill: "none",
24039
+ stroke: "currentColor",
24040
+ strokeWidth: "2",
24041
+ strokeLinecap: "round",
24042
+ strokeLinejoin: "round",
24043
+ children: jsxRuntime.jsx("path", {
24044
+ d: "M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"
24045
+ })
24046
+ })
24047
+ }) :
24048
+ // Button variant with text
24049
+ "button" === variant ? jsxRuntime.jsxs("button", {
24050
+ onClick: toggle,
24051
+ className: `theme-toggle theme-toggle-button ${className}`,
24052
+ "aria-label": ariaLabel,
24053
+ style: {
24054
+ display: "flex",
24055
+ alignItems: "center",
24056
+ gap: "8px",
24057
+ padding: "8px 16px",
24058
+ borderRadius: "8px",
24059
+ border: "1px solid currentColor",
24060
+ background: "transparent",
24061
+ color: "inherit",
24062
+ cursor: "pointer",
24063
+ fontSize: "14px",
24064
+ fontWeight: "500",
24065
+ transition: "all 0.3s ease-in-out"
24066
+ },
24067
+ children: [ isDark ? jsxRuntime.jsxs("svg", {
24068
+ width: iconSize,
24069
+ height: iconSize,
24070
+ viewBox: "0 0 24 24",
24071
+ fill: "none",
24072
+ stroke: "currentColor",
24073
+ strokeWidth: "2",
24074
+ strokeLinecap: "round",
24075
+ strokeLinejoin: "round",
24076
+ children: [ jsxRuntime.jsx("circle", {
24077
+ cx: "12",
24078
+ cy: "12",
24079
+ r: "5"
24080
+ }), jsxRuntime.jsx("line", {
24081
+ x1: "12",
24082
+ y1: "1",
24083
+ x2: "12",
24084
+ y2: "3"
24085
+ }), jsxRuntime.jsx("line", {
24086
+ x1: "12",
24087
+ y1: "21",
24088
+ x2: "12",
24089
+ y2: "23"
24090
+ }), jsxRuntime.jsx("line", {
24091
+ x1: "4.22",
24092
+ y1: "4.22",
24093
+ x2: "5.64",
24094
+ y2: "5.64"
24095
+ }), jsxRuntime.jsx("line", {
24096
+ x1: "18.36",
24097
+ y1: "18.36",
24098
+ x2: "19.78",
24099
+ y2: "19.78"
24100
+ }), jsxRuntime.jsx("line", {
24101
+ x1: "1",
24102
+ y1: "12",
24103
+ x2: "3",
24104
+ y2: "12"
24105
+ }), jsxRuntime.jsx("line", {
24106
+ x1: "21",
24107
+ y1: "12",
24108
+ x2: "23",
24109
+ y2: "12"
24110
+ }), jsxRuntime.jsx("line", {
24111
+ x1: "4.22",
24112
+ y1: "19.78",
24113
+ x2: "5.64",
24114
+ y2: "18.36"
24115
+ }), jsxRuntime.jsx("line", {
24116
+ x1: "18.36",
24117
+ y1: "5.64",
24118
+ x2: "19.78",
24119
+ y2: "4.22"
24120
+ }) ]
24121
+ }) : jsxRuntime.jsx("svg", {
24122
+ width: iconSize,
24123
+ height: iconSize,
24124
+ viewBox: "0 0 24 24",
24125
+ fill: "none",
24126
+ stroke: "currentColor",
24127
+ strokeWidth: "2",
24128
+ strokeLinecap: "round",
24129
+ strokeLinejoin: "round",
24130
+ children: jsxRuntime.jsx("path", {
24131
+ d: "M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"
24132
+ })
24133
+ }), showLabel && jsxRuntime.jsx("span", {
24134
+ children: isDark ? darkLabel : lightLabel
24135
+ }) ]
24136
+ }) :
24137
+ // Switch/toggle variant
24138
+ "switch" === variant ? jsxRuntime.jsx("div", {
24139
+ className: `theme-toggle theme-toggle-switch ${className}`,
24140
+ role: "button",
24141
+ tabIndex: 0,
24142
+ onClick: toggle,
24143
+ onKeyDown: e => "Enter" === e.key && toggle(),
24144
+ "aria-label": ariaLabel,
24145
+ style: {
24146
+ position: "relative",
24147
+ width: "56px",
24148
+ height: "28px",
24149
+ borderRadius: "14px",
24150
+ background: isDark ? "#4b5563" : "#d1d5db",
24151
+ cursor: "pointer",
24152
+ transition: "background 0.3s ease-in-out",
24153
+ display: "flex",
24154
+ alignItems: "center",
24155
+ padding: "2px"
24156
+ },
24157
+ children: jsxRuntime.jsx("div", {
24158
+ style: {
24159
+ position: "absolute",
24160
+ left: isDark ? "auto" : "2px",
24161
+ right: isDark ? "2px" : "auto",
24162
+ width: "24px",
24163
+ height: "24px",
24164
+ borderRadius: "50%",
24165
+ background: "white",
24166
+ boxShadow: "0 2px 4px rgba(0, 0, 0, 0.2)",
24167
+ transition: "all 0.3s ease-in-out",
24168
+ display: "flex",
24169
+ alignItems: "center",
24170
+ justifyContent: "center"
24171
+ },
24172
+ children: isDark ? jsxRuntime.jsx("svg", {
24173
+ width: "14",
24174
+ height: "14",
24175
+ viewBox: "0 0 24 24",
24176
+ fill: "none",
24177
+ stroke: "#4b5563",
24178
+ strokeWidth: "2",
24179
+ strokeLinecap: "round",
24180
+ strokeLinejoin: "round",
24181
+ children: jsxRuntime.jsx("path", {
24182
+ d: "M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"
24183
+ })
24184
+ }) : jsxRuntime.jsxs("svg", {
24185
+ width: "14",
24186
+ height: "14",
24187
+ viewBox: "0 0 24 24",
24188
+ fill: "none",
24189
+ stroke: "#f59e0b",
24190
+ strokeWidth: "2",
24191
+ strokeLinecap: "round",
24192
+ strokeLinejoin: "round",
24193
+ children: [ jsxRuntime.jsx("circle", {
24194
+ cx: "12",
24195
+ cy: "12",
24196
+ r: "5"
24197
+ }), jsxRuntime.jsx("line", {
24198
+ x1: "12",
24199
+ y1: "1",
24200
+ x2: "12",
24201
+ y2: "3"
24202
+ }), jsxRuntime.jsx("line", {
24203
+ x1: "12",
24204
+ y1: "21",
24205
+ x2: "12",
24206
+ y2: "23"
24207
+ }), jsxRuntime.jsx("line", {
24208
+ x1: "4.22",
24209
+ y1: "4.22",
24210
+ x2: "5.64",
24211
+ y2: "5.64"
24212
+ }), jsxRuntime.jsx("line", {
24213
+ x1: "18.36",
24214
+ y1: "18.36",
24215
+ x2: "19.78",
24216
+ y2: "19.78"
24217
+ }), jsxRuntime.jsx("line", {
24218
+ x1: "1",
24219
+ y1: "12",
24220
+ x2: "3",
24221
+ y2: "12"
24222
+ }), jsxRuntime.jsx("line", {
24223
+ x1: "21",
24224
+ y1: "12",
24225
+ x2: "23",
24226
+ y2: "12"
24227
+ }), jsxRuntime.jsx("line", {
24228
+ x1: "4.22",
24229
+ y1: "19.78",
24230
+ x2: "5.64",
24231
+ y2: "18.36"
24232
+ }), jsxRuntime.jsx("line", {
24233
+ x1: "18.36",
24234
+ y1: "5.64",
24235
+ x2: "19.78",
24236
+ y2: "4.22"
24237
+ }) ]
24238
+ })
24239
+ })
24240
+ }) : null;
24241
+ };
24242
+
24243
+ ThemeToggle.displayName = "ThemeToggle";
24244
+
22905
24245
  /**
22906
24246
  * Theme Applicator
22907
24247
  *
@@ -22914,7 +24254,8 @@ function useThemeTokens() {
22914
24254
  * Theme applicator class for runtime theme application
22915
24255
  *
22916
24256
  * Uses the unified theme system for efficient CSS variable generation and injection.
22917
- */ class ThemeApplicator {
24257
+ */
24258
+ class ThemeApplicator {
22918
24259
  constructor(root = document.documentElement) {
22919
24260
  this.styleId = "atomix-theme-applicator", this.root = root;
22920
24261
  }
@@ -23740,7 +25081,7 @@ class ThemeValidator {
23740
25081
  *
23741
25082
  * Provides detailed inspection and debugging information for themes
23742
25083
  */ const ThemeInspector = ({theme: theme, showValidation: showValidation = !0, showCSSVariables: showCSSVariables = !0, showStructure: showStructure = !0, className: className, style: style}) => {
23743
- const [activeTab, setActiveTab] = React.useState("overview"), [expandedSections, setExpandedSections] = React.useState(new Set([ "palette" ])), [searchQuery, setSearchQuery] = React.useState(""), [debouncedSearchQuery, setDebouncedSearchQuery] = React.useState(""), [copiedPath, setCopiedPath] = React.useState(null), searchTimeoutRef = React.useRef();
25084
+ const [activeTab, setActiveTab] = React.useState("overview"), [expandedSections, setExpandedSections] = React.useState(new Set([ "palette" ])), [searchQuery, setSearchQuery] = React.useState(""), [debouncedSearchQuery, setDebouncedSearchQuery] = React.useState(""), [copiedPath, setCopiedPath] = React.useState(null), searchTimeoutRef = React.useRef(void 0);
23744
25085
  // Debounce search query
23745
25086
  React.useEffect((() => (searchTimeoutRef.current && clearTimeout(searchTimeoutRef.current),
23746
25087
  searchTimeoutRef.current = setTimeout((() => {
@@ -24146,7 +25487,7 @@ class ThemeValidator {
24146
25487
  }) ]
24147
25488
  });
24148
25489
  }, ThemeComparator = ({themeA: themeA, themeB: themeB, showOnlyDifferences: showOnlyDifferences = !1, className: className, style: style}) => {
24149
- const [searchQuery, setSearchQuery] = React.useState(""), [debouncedSearchQuery, setDebouncedSearchQuery] = React.useState(""), [filterType, setFilterType] = React.useState("all"), [filterCategory, setFilterCategory] = React.useState("all"), searchTimeoutRef = React.useRef();
25490
+ const [searchQuery, setSearchQuery] = React.useState(""), [debouncedSearchQuery, setDebouncedSearchQuery] = React.useState(""), [filterType, setFilterType] = React.useState("all"), [filterCategory, setFilterCategory] = React.useState("all"), searchTimeoutRef = React.useRef(void 0);
24150
25491
  // Debounce search query
24151
25492
  React.useEffect((() => (searchTimeoutRef.current && clearTimeout(searchTimeoutRef.current),
24152
25493
  searchTimeoutRef.current = setTimeout((() => {
@@ -24653,13 +25994,13 @@ class ThemeValidator {
24653
25994
  function createPaletteColor(color) {
24654
25995
  return "string" == typeof color ? {
24655
25996
  main: color,
24656
- light: lighten(color),
24657
- dark: darken(color),
25997
+ light: lighten(color, .15),
25998
+ dark: darken(color, .15),
24658
25999
  contrastText: getContrastText(color)
24659
26000
  } : {
24660
26001
  main: color.main || "#000000",
24661
- light: color.light || lighten(color.main || "#000000"),
24662
- dark: color.dark || darken(color.main || "#000000"),
26002
+ light: color.light || lighten(color.main || "#000000", .15),
26003
+ dark: color.dark || darken(color.main || "#000000", .15),
24663
26004
  contrastText: color.contrastText || getContrastText(color.main || "#000000")
24664
26005
  };
24665
26006
  }
@@ -24694,23 +26035,19 @@ function createThemeObject(...options) {
24694
26035
  },
24695
26036
  background: {
24696
26037
  default: mergedOptions.palette?.background?.default || DEFAULT_PALETTE.background.default,
26038
+ paper: mergedOptions.palette?.background?.paper || DEFAULT_PALETTE.background.paper,
24697
26039
  subtle: mergedOptions.palette?.background?.subtle || DEFAULT_PALETTE.background.subtle
24698
26040
  },
24699
26041
  text: {
24700
26042
  primary: mergedOptions.palette?.text?.primary || DEFAULT_PALETTE.text.primary,
24701
26043
  secondary: mergedOptions.palette?.text?.secondary || DEFAULT_PALETTE.text.secondary,
24702
26044
  disabled: mergedOptions.palette?.text?.disabled || DEFAULT_PALETTE.text.disabled
24703
- }
26045
+ },
26046
+ // Spread other palette properties
26047
+ ...mergedOptions.palette
24704
26048
  }, typography = deepMerge({
24705
26049
  ...DEFAULT_TYPOGRAPHY
24706
- }, mergedOptions.typography || {}), spacing = function(spacingInput = 4) {
24707
- // If it's already a function, return it
24708
- return "function" == typeof spacingInput ? spacingInput :
24709
- // If it's a number, create a function that multiplies by that number
24710
- "number" == typeof spacingInput ? (...values) => 0 === values.length ? "0px" : values.map((value => value * spacingInput + "px")).join(" ") :
24711
- // If it's an array, use it as a scale
24712
- Array.isArray(spacingInput) ? (...values) => 0 === values.length ? "0px" : values.map((value => `${spacingInput[value] || value}px`)).join(" ") : (...values) => 0 === values.length ? "0px" : values.map((value => 4 * value + "px")).join(" ");
24713
- }(mergedOptions.spacing), breakpoints = function(breakpointsInput) {
26050
+ }, mergedOptions.typography || {}), spacing = createSpacing(mergedOptions.spacing), breakpoints = function(breakpointsInput) {
24714
26051
  const values = {
24715
26052
  xs: 0,
24716
26053
  sm: 576,
@@ -24942,7 +26279,7 @@ const ThemeLiveEditor = ({initialTheme: initialTheme, onChange: onChange, classN
24942
26279
  } catch (err) {
24943
26280
  setError(err instanceof Error ? err.message : "Invalid JSON");
24944
26281
  }
24945
- }), [ updateTheme ]), jsonUpdateTimeoutRef = React.useRef();
26282
+ }), [ updateTheme ]), jsonUpdateTimeoutRef = React.useRef(void 0);
24946
26283
  // Debounced JSON update to history
24947
26284
  React.useEffect((() => {
24948
26285
  if (!error) {
@@ -25534,45 +26871,357 @@ const ThemeLiveEditor = ({initialTheme: initialTheme, onChange: onChange, classN
25534
26871
  * Design Tokens Customizer Component
25535
26872
  */
25536
26873
  /**
25537
- * Theme Adapter
26874
+ * Theme Helper Functions
25538
26875
  *
25539
- * Converts between Theme objects and DesignTokens.
26876
+ * Utility functions for working with DesignTokens
25540
26877
  */
25541
26878
  /**
25542
- * Convert DesignTokens to Theme-compatible CSS variables
26879
+ * Check if a value is DesignTokens
26880
+ *
26881
+ * Type guard to check if an object is DesignTokens format.
25543
26882
  *
25544
- * @param tokens - DesignTokens object
25545
- * @returns CSS variables object compatible with Theme.cssVars
26883
+ * @param value - Value to check
26884
+ * @returns True if value is DesignTokens
25546
26885
  */
25547
- function designTokensToCSSVars(tokens) {
25548
- const cssVars = {};
25549
- return Object.entries(tokens).forEach((([key, value]) => {
25550
- void 0 !== value && (cssVars[`--atomix-${key}`] = String(value));
25551
- })), cssVars;
26886
+ function isDesignTokens(value) {
26887
+ if (!value || "object" != typeof value) return !1;
26888
+ // DesignTokens is a flat object with string keys, no nested structures
26889
+ const obj = value;
26890
+ // Check for absence of Theme-specific properties
26891
+ if ("palette" in obj || "typography" in obj || "__isJSTheme" in obj) return !1;
26892
+ // Check if it has DesignTokens-like structure (flat string keys)
26893
+ const keys = Object.keys(obj);
26894
+ return 0 !== keys.length && keys.some((key => /^[a-z]+(-[a-z0-9]+)*$/.test(key) && "string" == typeof obj[key]));
26895
+ // Check if keys look like DesignTokens (kebab-case, no nesting)
26896
+ }
26897
+
26898
+ /**
26899
+ * Performance monitor class
26900
+ */ class PerformanceMonitor {
26901
+ /**
26902
+ * Create a new performance monitor
26903
+ *
26904
+ * @param config Configuration options
26905
+ */
26906
+ constructor(config) {
26907
+ this.frameCount = 0, this.lastSampleTime = 0, this.lastFpsUpdate = 0, this.frameTimes = [],
26908
+ this.animationFrameId = null, this.isActive = !1, this.startTime = 0, this.config = {
26909
+ fpsTarget: config?.fpsTarget ?? 60,
26910
+ sampleInterval: config?.sampleInterval ?? 500,
26911
+ onUpdate: config?.onUpdate ?? (() => {}),
26912
+ onDegraded: config?.onDegraded ?? (() => {}),
26913
+ enableMemoryMonitoring: config?.enableMemoryMonitoring ?? ("undefined" != typeof window && window.performance && window.performance.memory)
26914
+ };
26915
+ }
26916
+ /**
26917
+ * Start monitoring performance
26918
+ */ start() {
26919
+ this.isActive || (this.isActive = !0, this.frameCount = 0, this.lastSampleTime = performance.now(),
26920
+ this.lastFpsUpdate = this.lastSampleTime, this.frameTimes = [], this.startTime = this.lastSampleTime,
26921
+ this.animationFrameId = requestAnimationFrame(this.onFrame.bind(this)));
26922
+ }
26923
+ /**
26924
+ * Stop monitoring performance
26925
+ */ stop() {
26926
+ this.animationFrameId && (cancelAnimationFrame(this.animationFrameId), this.animationFrameId = null),
26927
+ this.isActive = !1;
26928
+ }
26929
+ /**
26930
+ * Get current performance metrics
26931
+ */ getMetrics() {
26932
+ var _context;
26933
+ const now = performance.now(), elapsed = now - this.lastFpsUpdate, fps = elapsed > 0 ? Math.round(this.frameCount / elapsed * 1e3) : 0, avgFrameTime = this.frameTimes.length > 0 ? _reduceInstanceProperty(_context = this.frameTimes).call(_context, ((a, b) => a + b), 0) / this.frameTimes.length : 0, peakFrameTime = this.frameTimes.length > 0 ? Math.max(...this.frameTimes) : 0;
26934
+ // Get memory stats if available
26935
+ let memory;
26936
+ if (this.config.enableMemoryMonitoring) {
26937
+ const perf = window.performance;
26938
+ perf && perf.memory && (memory = {
26939
+ usedJSHeapSize: perf.memory.usedJSHeapSize,
26940
+ totalJSHeapSize: perf.memory.totalJSHeapSize,
26941
+ jsHeapSizeLimit: perf.memory.jsHeapSizeLimit
26942
+ });
26943
+ }
26944
+ return {
26945
+ fps: fps,
26946
+ frameTime: avgFrameTime,
26947
+ peakFrameTime: peakFrameTime,
26948
+ memory: memory,
26949
+ timestamp: now,
26950
+ isDegraded: fps < .7 * this.config.fpsTarget
26951
+ };
26952
+ }
26953
+ /**
26954
+ * Get the current FPS
26955
+ */ getFps() {
26956
+ return this.getMetrics().fps;
26957
+ }
26958
+ /**
26959
+ * Check if performance is degraded
26960
+ */ isPerformanceDegraded() {
26961
+ return this.getMetrics().isDegraded;
26962
+ }
26963
+ /**
26964
+ * Private method called on each animation frame
26965
+ */ onFrame(timestamp) {
26966
+ if (!this.isActive) return;
26967
+ // Calculate frame time
26968
+ const frameTime = timestamp - this.lastSampleTime;
26969
+ // Check if we need to update metrics
26970
+ if (this.frameTimes.push(frameTime),
26971
+ // Keep only the last 60 frame times for averaging
26972
+ this.frameTimes.length > 60 && this.frameTimes.shift(), this.frameCount++, this.lastSampleTime = timestamp,
26973
+ timestamp - this.lastFpsUpdate >= this.config.sampleInterval) {
26974
+ const metrics = this.getMetrics();
26975
+ // Call update callback
26976
+ this.config.onUpdate(metrics),
26977
+ // Check for degradation
26978
+ metrics.isDegraded && this.config.onDegraded(metrics),
26979
+ // Reset counters
26980
+ this.frameCount = 0, this.lastFpsUpdate = timestamp;
26981
+ }
26982
+ this.animationFrameId = requestAnimationFrame(this.onFrame.bind(this));
26983
+ }
26984
+ /**
26985
+ * Run a performance test for a specific function
26986
+ *
26987
+ * @param fn Function to test
26988
+ * @param iterations Number of iterations (default: 100)
26989
+ * @returns Average execution time in ms
26990
+ */ async testFunctionPerformance(fn, iterations = 100) {
26991
+ const times = [];
26992
+ for (let i = 0; i < iterations; i++) {
26993
+ const start = performance.now();
26994
+ fn();
26995
+ const end = performance.now();
26996
+ times.push(end - start);
26997
+ }
26998
+ return _reduceInstanceProperty(times).call(times, ((a, b) => a + b), 0) / times.length;
26999
+ }
27000
+ }
27001
+
27002
+ /**
27003
+ * Create a performance monitor instance
27004
+ *
27005
+ * @param config Configuration options
27006
+ * @returns PerformanceMonitor instance
27007
+ *
27008
+ * @example
27009
+ * ```typescript
27010
+ * import { createPerformanceMonitor } from '@shohojdhara/atomix/theme';
27011
+ *
27012
+ * const monitor = createPerformanceMonitor({
27013
+ * fpsTarget: 60,
27014
+ * onUpdate: (metrics) => console.log('FPS:', metrics.fps),
27015
+ * onDegraded: (metrics) => console.warn('Performance degraded!', metrics),
27016
+ * });
27017
+ *
27018
+ * monitor.start();
27019
+ *
27020
+ * // Later...
27021
+ * monitor.stop();
27022
+ * ```
27023
+ */ function createPerformanceMonitor(config) {
27024
+ return new PerformanceMonitor(config);
27025
+ }
27026
+
27027
+ /**
27028
+ * Hook for React components to monitor performance
27029
+ *
27030
+ * @param config Configuration options
27031
+ * @returns Performance metrics and monitor controls
27032
+ *
27033
+ * @example
27034
+ * ```typescript
27035
+ * import { usePerformanceMonitor } from '@shohojdhara/atomix/theme';
27036
+ *
27037
+ * function MyComponent() {
27038
+ * const { metrics, start, stop } = usePerformanceMonitor({ fpsTarget: 60 });
27039
+ *
27040
+ * useEffect(() => {
27041
+ * start();
27042
+ * return () => stop();
27043
+ * }, []);
27044
+ *
27045
+ * return <div>FPS: {metrics.fps}</div>;
27046
+ * }
27047
+ * ```
27048
+ */ function usePerformanceMonitor(config) {
27049
+ const [monitor] = React__default.default.useState((() => createPerformanceMonitor(config))), [metrics, setMetrics] = React__default.default.useState((() => "undefined" != typeof window ? monitor.getMetrics() : {
27050
+ fps: 0,
27051
+ frameTime: 0,
27052
+ peakFrameTime: 0,
27053
+ timestamp: 0,
27054
+ isDegraded: !1
27055
+ })), start = React__default.default.useCallback((() => {
27056
+ "undefined" != typeof window && monitor.start();
27057
+ }), [ monitor ]), stop = React__default.default.useCallback((() => {
27058
+ "undefined" != typeof window && monitor.stop();
27059
+ }), [ monitor ]);
27060
+ return React__default.default.useEffect((() => {
27061
+ if ("undefined" == typeof window) return;
27062
+ // Update metrics when monitor callbacks fire
27063
+ const originalOnUpdate = config?.onUpdate;
27064
+ return monitor.config.onUpdate = newMetrics => {
27065
+ setMetrics(newMetrics), originalOnUpdate?.(newMetrics);
27066
+ }, () => {
27067
+ monitor.stop();
27068
+ };
27069
+ }), [ monitor, config?.onUpdate ]), {
27070
+ metrics: metrics,
27071
+ start: start,
27072
+ stop: stop
27073
+ };
25552
27074
  }
25553
27075
 
25554
27076
  /**
25555
- * Theme Helper Functions
27077
+ * Responsive Utility for Atomix Theme System
25556
27078
  *
25557
- * Utility functions for working with DesignTokens
27079
+ * Provides responsive breakpoint detection and device-aware parameter scaling
27080
+ * based on configuration from the advanced optimization features.
25558
27081
  */
25559
27082
  /**
25560
- * Check if a value is DesignTokens
27083
+ * Responsive utility class
27084
+ */ class ResponsiveUtil {
27085
+ constructor(config) {
27086
+ this.currentDevice = "desktop", // Default
27087
+ this.resizeHandler = null, this.observer = null, this.config = config, this.currentDevice = this.getCurrentDeviceType(),
27088
+ // Set up resize listener
27089
+ this.setupResizeListener();
27090
+ }
27091
+ /**
27092
+ * Get the current device type based on viewport width
27093
+ */ getCurrentDeviceType() {
27094
+ if ("undefined" == typeof window) return "desktop";
27095
+ // SSR fallback
27096
+ const width = window.innerWidth;
27097
+ // Parse breakpoint values to numbers
27098
+ this.parsePxValue(this.config.breakpoints.mobile);
27099
+ const tabletWidth = this.parsePxValue(this.config.breakpoints.tablet), desktopWidth = this.parsePxValue(this.config.breakpoints.desktop), wideWidth = this.parsePxValue(this.config.breakpoints.wide);
27100
+ return width < tabletWidth ? "mobile" : width < desktopWidth ? "tablet" : width < wideWidth ? "desktop" : "wide";
27101
+ }
27102
+ /**
27103
+ * Get the scaling factor for the current device
27104
+ */ getCurrentScalingFactor() {
27105
+ // 'wide' devices use the same scaling as 'desktop'
27106
+ const scalingKey = "wide" === this.currentDevice ? "desktop" : this.currentDevice;
27107
+ return this.config.deviceScaling[scalingKey] || 1;
27108
+ }
27109
+ /**
27110
+ * Scale a value based on the current device's scaling factor
27111
+ */ scaleValue(value) {
27112
+ return value * this.getCurrentScalingFactor();
27113
+ }
27114
+ /**
27115
+ * Check if the current device matches a specific type
27116
+ */ isDevice(device) {
27117
+ return this.currentDevice === device;
27118
+ }
27119
+ /**
27120
+ * Check if the current device is mobile or smaller
27121
+ */ isMobileOrSmaller() {
27122
+ return "mobile" === this.currentDevice;
27123
+ }
27124
+ /**
27125
+ * Check if the current device is tablet or smaller
27126
+ */ isTabletOrSmaller() {
27127
+ return "mobile" === this.currentDevice || "tablet" === this.currentDevice;
27128
+ }
27129
+ /**
27130
+ * Check if the current device is desktop or larger
27131
+ */ isDesktopOrLarger() {
27132
+ return "desktop" === this.currentDevice || "wide" === this.currentDevice;
27133
+ }
27134
+ /**
27135
+ * Update the responsive configuration
27136
+ */ updateConfig(config) {
27137
+ this.config = config, this.currentDevice = this.getCurrentDeviceType();
27138
+ }
27139
+ /**
27140
+ * Destroy the responsive utility and clean up listeners
27141
+ */ destroy() {
27142
+ this.resizeHandler && (window.removeEventListener("resize", this.resizeHandler),
27143
+ this.resizeHandler = null), this.observer && (this.observer.disconnect(), this.observer = null);
27144
+ }
27145
+ /**
27146
+ * Parse a CSS value to pixels
27147
+ */ parsePxValue(value) {
27148
+ return value.endsWith("px") ? parseFloat(value.slice(0, -2)) :
27149
+ // For other units, we'll use a rough conversion assuming 16px base
27150
+ value.endsWith("rem") ? 16 * parseFloat(value.slice(0, -3)) : value.endsWith("em") ? 16 * parseFloat(value.slice(0, -2)) : parseFloat(value) || 0;
27151
+ }
27152
+ /**
27153
+ * Set up the resize listener
27154
+ */ setupResizeListener() {
27155
+ if ("undefined" == typeof window) return;
27156
+ // Throttled resize handler
27157
+ let resizeTimeout = null;
27158
+ const handleResize = () => {
27159
+ resizeTimeout && window.clearTimeout(resizeTimeout), resizeTimeout = window.setTimeout((() => {
27160
+ const newDeviceType = this.getCurrentDeviceType();
27161
+ newDeviceType !== this.currentDevice && (this.currentDevice = newDeviceType);
27162
+ }), 150);
27163
+ } // Throttle to 150ms
27164
+ ;
27165
+ this.resizeHandler = handleResize, window.addEventListener("resize", handleResize),
27166
+ // Also observe the document body for size changes
27167
+ "undefined" != typeof ResizeObserver && (this.observer = new ResizeObserver(handleResize),
27168
+ this.observer.observe(document.body));
27169
+ }
27170
+ }
27171
+
27172
+ /**
27173
+ * Create a responsive utility instance
25561
27174
  *
25562
- * Type guard to check if an object is DesignTokens format.
27175
+ * @param config Responsive configuration
27176
+ * @returns ResponsiveUtil instance
27177
+ */ function createResponsiveUtil(config) {
27178
+ return new ResponsiveUtil(config);
27179
+ }
27180
+
27181
+ /**
27182
+ * Hook for React components to use responsive features
25563
27183
  *
25564
- * @param value - Value to check
25565
- * @returns True if value is DesignTokens
25566
- */ function isDesignTokens(value) {
25567
- if (!value || "object" != typeof value) return !1;
25568
- // DesignTokens is a flat object with string keys, no nested structures
25569
- const obj = value;
25570
- // Check for absence of Theme-specific properties
25571
- if ("palette" in obj || "typography" in obj || "__isJSTheme" in obj) return !1;
25572
- // Check if it has DesignTokens-like structure (flat string keys)
25573
- const keys = Object.keys(obj);
25574
- return 0 !== keys.length && keys.some((key => /^[a-z]+(-[a-z0-9]+)*$/.test(key) && "string" == typeof obj[key]));
25575
- // Check if keys look like DesignTokens (kebab-case, no nesting)
27184
+ * @param config Responsive configuration
27185
+ * @returns Current device type and utility functions
27186
+ */ function useResponsive(config) {
27187
+ const [util] = React__default.default.useState((() => createResponsiveUtil(config))), [deviceType, setDeviceType] = React__default.default.useState((() => "undefined" != typeof window ? util.getCurrentDeviceType() : "desktop"));
27188
+ return React__default.default.useEffect((() => {
27189
+ if ("undefined" == typeof window) return;
27190
+ const handleResize = () => {
27191
+ const newDeviceType = util.getCurrentDeviceType();
27192
+ newDeviceType !== deviceType && setDeviceType(newDeviceType);
27193
+ };
27194
+ // Update device type on mount
27195
+ return setDeviceType(util.getCurrentDeviceType()),
27196
+ // Listen for resize events
27197
+ window.addEventListener("resize", handleResize), () => {
27198
+ window.removeEventListener("resize", handleResize), util.destroy();
27199
+ };
27200
+ }), [ util, deviceType ]), "undefined" == typeof window ? {
27201
+ deviceType: "desktop",
27202
+ isMobile: !1,
27203
+ isTablet: !1,
27204
+ isDesktop: !0,
27205
+ isWide: !1,
27206
+ scaleValue: value => value,
27207
+ getCurrentDeviceType: () => "desktop",
27208
+ getCurrentScalingFactor: () => 1,
27209
+ isMobileOrSmaller: () => !1,
27210
+ isTabletOrSmaller: () => !0,
27211
+ isDesktopOrLarger: () => !0
27212
+ } : {
27213
+ deviceType: deviceType,
27214
+ isMobile: "mobile" === deviceType,
27215
+ isTablet: "tablet" === deviceType,
27216
+ isDesktop: "desktop" === deviceType,
27217
+ isWide: "wide" === deviceType,
27218
+ scaleValue: value => util.scaleValue(value),
27219
+ getCurrentDeviceType: () => util.getCurrentDeviceType(),
27220
+ getCurrentScalingFactor: () => util.getCurrentScalingFactor(),
27221
+ isMobileOrSmaller: () => util.isMobileOrSmaller(),
27222
+ isTabletOrSmaller: () => util.isTabletOrSmaller(),
27223
+ isDesktopOrLarger: () => util.isDesktopOrLarger()
27224
+ };
25576
27225
  }
25577
27226
 
25578
27227
  /**
@@ -25760,6 +27409,32 @@ class RTLManager {
25760
27409
  }
25761
27410
  }
25762
27411
 
27412
+ /**
27413
+ * Create RTL manager instance
27414
+ */ function createRTLManager(config) {
27415
+ return new RTLManager(config);
27416
+ }
27417
+
27418
+ /**
27419
+ * Check if locale is RTL
27420
+ */ function isRTLLocale(locale) {
27421
+ return RTL_LOCALES.has(locale.toLowerCase());
27422
+ }
27423
+
27424
+ /**
27425
+ * Get direction from locale
27426
+ */ function getDirectionFromLocale(locale) {
27427
+ return isRTLLocale(locale) ? "rtl" : "ltr";
27428
+ }
27429
+
27430
+ /**
27431
+ * RTL-aware CSS helper
27432
+ *
27433
+ * Returns appropriate CSS based on direction
27434
+ */ function rtlCSS(ltrCSS, rtlCSS, direction = "ltr") {
27435
+ return "rtl" === direction ? rtlCSS : ltrCSS;
27436
+ }
27437
+
25763
27438
  /**
25764
27439
  * Theme System Exports
25765
27440
  *
@@ -25782,8 +27457,6 @@ class RTLManager {
25782
27457
  // ============================================================================
25783
27458
  // Core Theme Functions
25784
27459
  // ============================================================================
25785
- // Create theme CSS from DesignTokens
25786
- // File saving utilities removed to prevent bundling Node.js modules in browser
25787
27460
  /**
25788
27461
  * Inject theme CSS into DOM
25789
27462
  */ function injectTheme(css, id = "atomix-theme") {
@@ -25796,6 +27469,115 @@ class RTLManager {
25796
27469
  removeCSS(id);
25797
27470
  }
25798
27471
 
27472
+ /**
27473
+ * Main theme module interface
27474
+ */ var index = {
27475
+ // Core
27476
+ createTheme: createTheme,
27477
+ injectTheme: injectTheme,
27478
+ removeTheme: removeTheme,
27479
+ // Context and Provider
27480
+ ThemeProvider: ThemeProvider,
27481
+ useTheme: useTheme,
27482
+ useThemeTokens: useThemeTokens,
27483
+ ThemeContext: ThemeContext,
27484
+ ThemeErrorBoundary: ThemeErrorBoundary,
27485
+ // Adapters
27486
+ configToTokens: configToTokens,
27487
+ designTokensToCSSVars: designTokensToCSSVars,
27488
+ // Theme Utils
27489
+ switchTheme: switchTheme,
27490
+ toggleTheme: toggleTheme,
27491
+ getCurrentTheme: getCurrentTheme,
27492
+ getSystemTheme: getSystemTheme,
27493
+ initializeTheme: initializeTheme,
27494
+ listenToSystemTheme: listenToSystemTheme,
27495
+ persistTheme: persistTheme,
27496
+ clearThemePreference: clearThemePreference,
27497
+ // Token Manipulation
27498
+ mergeTokens: mergeTokens,
27499
+ overrideTokens: overrideTokens,
27500
+ pickTokens: pickTokens,
27501
+ omitTokens: omitTokens,
27502
+ // Color Utilities
27503
+ hexToRgb: hexToRgb$1,
27504
+ rgbToHex: rgbToHex,
27505
+ getLuminance: getLuminance,
27506
+ getContrastRatio: getContrastRatio,
27507
+ isAccessible: isAccessible,
27508
+ getContrastText: getContrastText,
27509
+ lighten: lighten,
27510
+ darken: darken,
27511
+ alpha: alpha,
27512
+ emphasize: emphasize,
27513
+ createSpacing: createSpacing,
27514
+ // Performance utilities
27515
+ createPerformanceMonitor: createPerformanceMonitor,
27516
+ usePerformanceMonitor: usePerformanceMonitor,
27517
+ // Responsive utilities
27518
+ createResponsiveUtil: createResponsiveUtil,
27519
+ useResponsive: useResponsive,
27520
+ // Components
27521
+ ThemeToggle: ThemeToggle,
27522
+ ThemeApplicator: ThemeApplicator,
27523
+ applyTheme: applyTheme,
27524
+ getThemeApplicator: getThemeApplicator,
27525
+ // Registry
27526
+ createThemeRegistry: createThemeRegistry,
27527
+ registerTheme: registerTheme,
27528
+ unregisterTheme: unregisterTheme,
27529
+ hasTheme: hasTheme,
27530
+ getTheme: getTheme,
27531
+ getAllThemes: getAllThemes,
27532
+ getThemeIds: getThemeIds,
27533
+ clearThemes: clearThemes,
27534
+ getThemeCount: getThemeCount,
27535
+ // Composition
27536
+ deepMerge: deepMerge,
27537
+ mergeTheme: mergeTheme,
27538
+ extendTheme: extendTheme,
27539
+ // Tokens
27540
+ createTokens: createTokens,
27541
+ defaultTokens: defaultTokens,
27542
+ // Generators
27543
+ generateCSSVariables: generateCSSVariables$1,
27544
+ generateCSSVariablesForSelector: generateCSSVariablesForSelector,
27545
+ // Naming
27546
+ generateClassName: generateClassName,
27547
+ generateCSSVariableName: generateCSSVariableName,
27548
+ normalizeThemeTokens: normalizeThemeTokens,
27549
+ camelToKebab: camelToKebab,
27550
+ themePropertyToCSSVar: themePropertyToCSSVar,
27551
+ // Component Theming
27552
+ getComponentThemeValue: getComponentThemeValue,
27553
+ generateComponentCSSVars: generateComponentCSSVars,
27554
+ applyComponentTheme: applyComponentTheme,
27555
+ useComponentTheme: useComponentTheme,
27556
+ // Hooks
27557
+ useThemeSwitcher: useThemeSwitcher,
27558
+ // Helpers
27559
+ isDesignTokens: isDesignTokens,
27560
+ // CSS Variable Mapper
27561
+ mapSCSSTokensToCSSVars: mapSCSSTokensToCSSVars,
27562
+ applyCSSVariables: applyCSSVariables,
27563
+ removeCSSVariables: removeCSSVariables,
27564
+ getCSSVariable: getCSSVariable,
27565
+ cssVarsToStyle: cssVarsToStyle,
27566
+ mergeCSSVars: mergeCSSVars,
27567
+ isValidCSSVariableName: isValidCSSVariableName,
27568
+ extractComponentName: extractComponentName,
27569
+ // Injection Utils
27570
+ injectCSS: injectCSS$1,
27571
+ removeCSS: removeCSS,
27572
+ isCSSInjected: isCSSInjected,
27573
+ // I18n
27574
+ RTLManager: RTLManager,
27575
+ createRTLManager: createRTLManager,
27576
+ isRTLLocale: isRTLLocale,
27577
+ getDirectionFromLocale: getDirectionFromLocale,
27578
+ rtlCSS: rtlCSS
27579
+ };
27580
+
25799
27581
  /**
25800
27582
  * CSS Variables Constants
25801
27583
  *
@@ -26086,46 +27868,138 @@ function mergePartStyles(base, override) {
26086
27868
  }
26087
27869
 
26088
27870
  /**
26089
- * Render a slot with the given props
27871
+ * Hook to merge theme overrides with component props
26090
27872
  *
26091
- * Priority order:
26092
- * 1. render function
26093
- * 2. component
26094
- * 3. children
26095
- * 4. fallback
27873
+ * @example
27874
+ * function Button(props: ButtonProps) {
27875
+ * const customization = useComponentCustomization('Button', props);
27876
+ *
27877
+ * return (
27878
+ * <button
27879
+ * className={customization.className}
27880
+ * style={customization.style}
27881
+ * >
27882
+ * {props.children}
27883
+ * </button>
27884
+ * );
27885
+ * }
27886
+ */
27887
+ /**
27888
+ * Configuration Validator
27889
+ *
27890
+ * Provides detailed validation and feedback for Atomix configurations,
27891
+ * especially for advanced features (Phases 2, 3, and 4).
27892
+ */
27893
+ /**
27894
+ * Validate an Atomix configuration with detailed feedback
27895
+ *
27896
+ * @param config - The configuration to validate
27897
+ * @param options - Validation options
27898
+ * @returns Detailed validation result
26096
27899
  *
26097
27900
  * @example
26098
- * renderSlot(
26099
- * { render: (props) => <CustomButton {...props} /> },
26100
- * { onClick: handleClick, children: 'Click me' }
26101
- * )
26102
- */ function renderSlot(slot, props, fallback) {
26103
- // No slot provided, use fallback
26104
- if (!slot) return fallback;
26105
- // Slot is a plain React node
26106
- if ( React__default.default.isValidElement(slot) || "string" == typeof slot || "number" == typeof slot) return slot;
26107
- // Slot is an object with rendering options
26108
- if ("object" == typeof slot && null !== slot) {
26109
- const slotObj = slot;
26110
- // Priority 1: render function
26111
- if (slotObj.render && "function" == typeof slotObj.render) return slotObj.render(props);
26112
- // Priority 2: component
26113
- if (slotObj.component) {
26114
- const Component = slotObj.component;
26115
- return jsxRuntime.jsx(Component, {
26116
- ...props
26117
- });
27901
+ * ```typescript
27902
+ * import { validateConfiguration } from '@shohojdhara/atomix/config';
27903
+ *
27904
+ * const config = { /* your config *\/ };
27905
+ * const result = validateConfiguration(config);
27906
+ *
27907
+ * if (!result.isValid) {
27908
+ * console.warn('Warnings:', result.warnings);
27909
+ * console.info('Suggestions:', result.suggestions);
27910
+ * }
27911
+ * ```
27912
+ */
27913
+ function validateConfiguration(config, options) {
27914
+ const {performanceAnalysis: performanceAnalysis = !0, compatibilityReport: compatibilityReport = !0} = options || {}, warnings = [], suggestions = [];
27915
+ let performanceImpact = "low";
27916
+ // Use the existing validation
27917
+ const baseWarnings = validateConfig$1(config);
27918
+ warnings.push(...baseWarnings),
27919
+ // Analyze advanced features for performance impact
27920
+ performanceAnalysis && (performanceImpact =
27921
+ /**
27922
+ * Analyze the performance impact of a configuration
27923
+ */
27924
+ function(config) {
27925
+ let impactScore = 0;
27926
+ // Analyze interactive effects
27927
+ if (config.interactiveEffects) {
27928
+ const ie = config.interactiveEffects;
27929
+ ie.vortex?.enabled && (impactScore += 2), ie.chromaticAberration?.enabled && (impactScore += 1),
27930
+ ie.mouseInteraction?.trailEffect && (impactScore += 1), ie.mouseInteraction?.pressureSensitivity && (impactScore += 1);
26118
27931
  }
26119
- // Priority 3: children
26120
- if (void 0 !== slotObj.children) return slotObj.children;
27932
+ // Analyze visual polish effects
27933
+ if (config.visualPolish) {
27934
+ const vp = config.visualPolish;
27935
+ vp.borders?.iridescentGlow && (impactScore += 1), vp.borders?.shimmerEffect && (impactScore += 1),
27936
+ vp.contentAwareBlur?.enabled && (impactScore += 2), vp.holographicEffects?.enabled && (impactScore += 2),
27937
+ vp.holographicEffects?.scanlineAnimation && (impactScore += 1), vp.holographicEffects?.dataStream && (impactScore += 1),
27938
+ vp.holographicEffects?.pulseRings && (impactScore += 1);
27939
+ }
27940
+ // Analyze optimization settings
27941
+ return config.optimization?.autoScaling?.enabled && (impactScore -= 1),
27942
+ impactScore >= 6 ? "high" : impactScore >= 3 ? "medium" : "low";
26121
27943
  }
26122
- // Fallback
26123
- return fallback;
27944
+ /**
27945
+ * Generate suggestions based on the configuration
27946
+ */ (config)),
27947
+ // Generate suggestions based on configuration
27948
+ function(config, suggestions) {
27949
+ // Suggest enabling performance optimizations if heavy effects are used
27950
+ (config.interactiveEffects || config.visualPolish?.holographicEffects?.enabled) && (config.optimization?.autoScaling?.enabled || suggestions.push("Consider enabling auto-scaling in optimization settings to adjust effects based on device performance: optimization.autoScaling.enabled = true"),
27951
+ config.optimization?.performance?.fpsTarget || suggestions.push("Set a target FPS in optimization.performance.fpsTarget to ensure smooth performance when using interactive effects")),
27952
+ // Suggest responsive breakpoints if optimization is partially configured
27953
+ config.optimization && !config.optimization.responsive && suggestions.push("Consider adding responsive breakpoints in optimization.responsive.breakpoints to adapt advanced effects based on device type"),
27954
+ // Suggest disabling heavy effects on lower-end devices
27955
+ config.visualPolish?.holographicEffects?.enabled && suggestions.push("For better performance on lower-end devices, consider conditionally disabling holographic effects based on device capabilities"),
27956
+ // Suggest using content-aware blur with performance considerations
27957
+ config.visualPolish?.contentAwareBlur?.enabled && suggestions.push("Content-aware blur can be expensive; consider setting a maximum blur radius or using simpler blur techniques for mobile devices"),
27958
+ // Suggest using chromatic aberration适度
27959
+ config.interactiveEffects?.chromaticAberration?.enabled && (config.interactiveEffects.chromaticAberration.redShift && Math.abs(config.interactiveEffects.chromaticAberration.redShift) > .05 && suggestions.push("High chromatic aberration red shift values (>0.05) may cause discomfort for some users; consider reducing to improve accessibility"),
27960
+ config.interactiveEffects.chromaticAberration.blueShift && Math.abs(config.interactiveEffects.chromaticAberration.blueShift) > .05 && suggestions.push("High chromatic aberration blue shift values (>0.05) may cause discomfort for some users; consider reducing to improve accessibility"));
27961
+ }
27962
+ /**
27963
+ * Generate a compatibility report for the configuration
27964
+ */ (config, suggestions);
27965
+ // Determine overall validity
27966
+ const isValid = 0 === warnings.length, compatibility = compatibilityReport ? function(config) {
27967
+ return {
27968
+ browsers: !(config.visualPolish?.holographicEffects?.enabled || config.visualPolish?.contentAwareBlur?.enabled || config.interactiveEffects?.vortex?.enabled || config.interactiveEffects?.chromaticAberration?.enabled),
27969
+ // May have issues on older browsers
27970
+ ssr: !0,
27971
+ // Works fine with SSR
27972
+ frameworks: [ "react", "vue", "angular", "svelte", "vanillajs" ]
27973
+ };
27974
+ }
27975
+ /**
27976
+ * Print a detailed configuration report to the console
27977
+ *
27978
+ * @param config - The configuration to analyze
27979
+ * @param title - Optional title for the report
27980
+ *
27981
+ * @example
27982
+ * ```typescript
27983
+ * import { printConfigReport } from '@shohojdhara/atomix/config';
27984
+ *
27985
+ * const config = { /* your config *\/ };
27986
+ * printConfigReport(config, 'My Application Config');
27987
+ * ```
27988
+ */ (config) : {
27989
+ browsers: !0,
27990
+ ssr: !0,
27991
+ frameworks: [ "react", "vue", "angular", "svelte", "vanillajs" ]
27992
+ };
27993
+ // Generate compatibility report
27994
+ return {
27995
+ isValid: isValid,
27996
+ warnings: warnings,
27997
+ suggestions: suggestions,
27998
+ performanceImpact: performanceImpact,
27999
+ compatibility: compatibility
28000
+ };
26124
28001
  }
26125
28002
 
26126
- /**
26127
- * Check if a value is a slot configuration
26128
- */
26129
28003
  // Import and re-export as namespaces with proper typing
26130
28004
  // Export as namespaces with explicit typing
26131
28005
  const composables = composablesImport, utils = utilsImport, types = typesImport, constants = constantsImport, theme = Object.freeze({
@@ -26140,19 +28014,30 @@ const composables = composablesImport, utils = utilsImport, types = typesImport,
26140
28014
  ThemeLiveEditor: ThemeLiveEditor,
26141
28015
  ThemePreview: ThemePreview,
26142
28016
  ThemeProvider: ThemeProvider,
28017
+ ThemeToggle: ThemeToggle,
26143
28018
  ThemeValidator: ThemeValidator,
28019
+ alpha: alpha,
26144
28020
  applyCSSVariables: applyCSSVariables,
26145
28021
  applyComponentTheme: applyComponentTheme,
26146
28022
  applyTheme: applyTheme,
26147
28023
  camelToKebab: camelToKebab,
28024
+ clearThemePreference: clearThemePreference,
26148
28025
  clearThemes: clearThemes,
28026
+ configToTokens: configToTokens,
28027
+ createPerformanceMonitor: createPerformanceMonitor,
28028
+ createRTLManager: createRTLManager,
28029
+ createResponsiveUtil: createResponsiveUtil,
28030
+ createSpacing: createSpacing,
26149
28031
  createTheme: createTheme,
26150
28032
  createThemeRegistry: createThemeRegistry,
26151
28033
  createTokens: createTokens,
26152
28034
  cssVarsToStyle: cssVarsToStyle,
28035
+ darken: darken,
26153
28036
  deepMerge: deepMerge,
28037
+ default: index,
26154
28038
  defaultTokens: defaultTokens,
26155
28039
  designTokensToCSSVars: designTokensToCSSVars,
28040
+ emphasize: emphasize,
26156
28041
  extendTheme: extendTheme,
26157
28042
  extractComponentName: extractComponentName,
26158
28043
  generateCSSVariableName: generateCSSVariableName,
@@ -26163,29 +28048,53 @@ const composables = composablesImport, utils = utilsImport, types = typesImport,
26163
28048
  getAllThemes: getAllThemes,
26164
28049
  getCSSVariable: getCSSVariable,
26165
28050
  getComponentThemeValue: getComponentThemeValue,
28051
+ getContrastRatio: getContrastRatio,
28052
+ getContrastText: getContrastText,
28053
+ getCurrentTheme: getCurrentTheme,
28054
+ getDirectionFromLocale: getDirectionFromLocale,
28055
+ getLuminance: getLuminance,
28056
+ getSystemTheme: getSystemTheme,
26166
28057
  getTheme: getTheme,
26167
28058
  getThemeApplicator: getThemeApplicator,
26168
28059
  getThemeCount: getThemeCount,
26169
28060
  getThemeIds: getThemeIds,
26170
28061
  hasTheme: hasTheme,
28062
+ hexToRgb: hexToRgb$1,
28063
+ initializeTheme: initializeTheme,
26171
28064
  injectCSS: injectCSS$1,
26172
28065
  injectTheme: injectTheme,
28066
+ isAccessible: isAccessible,
26173
28067
  isCSSInjected: isCSSInjected,
26174
28068
  isDesignTokens: isDesignTokens,
28069
+ isRTLLocale: isRTLLocale,
26175
28070
  isValidCSSVariableName: isValidCSSVariableName,
28071
+ lighten: lighten,
28072
+ listenToSystemTheme: listenToSystemTheme,
26176
28073
  mapSCSSTokensToCSSVars: mapSCSSTokensToCSSVars,
26177
28074
  mergeCSSVars: mergeCSSVars,
26178
28075
  mergeTheme: mergeTheme,
28076
+ mergeTokens: mergeTokens,
26179
28077
  normalizeThemeTokens: normalizeThemeTokens,
28078
+ omitTokens: omitTokens,
28079
+ overrideTokens: overrideTokens,
28080
+ persistTheme: persistTheme,
28081
+ pickTokens: pickTokens,
26180
28082
  registerTheme: registerTheme,
26181
28083
  removeCSS: removeCSS,
26182
28084
  removeCSSVariables: removeCSSVariables,
26183
28085
  removeTheme: removeTheme,
28086
+ rgbToHex: rgbToHex,
28087
+ rtlCSS: rtlCSS,
28088
+ switchTheme: switchTheme,
26184
28089
  themePropertyToCSSVar: themePropertyToCSSVar,
28090
+ toggleTheme: toggleTheme,
26185
28091
  unregisterTheme: unregisterTheme,
26186
28092
  useComponentTheme: useComponentTheme,
26187
28093
  useHistory: useHistory,
28094
+ usePerformanceMonitor: usePerformanceMonitor,
28095
+ useResponsive: useResponsive,
26188
28096
  useTheme: useTheme,
28097
+ useThemeSwitcher: useThemeSwitcher,
26189
28098
  useThemeTokens: useThemeTokens
26190
28099
  }), atomix = {
26191
28100
  // Re-export all components and utilities
@@ -26253,10 +28162,11 @@ exports.TYPEDBUTTON = TYPEDBUTTON, exports.Tabs = Tabs, exports.Testimonial = Te
26253
28162
  exports.Textarea = Textarea, exports.ThemeApplicator = ThemeApplicator, exports.ThemeComparator = ThemeComparator,
26254
28163
  exports.ThemeContext = ThemeContext, exports.ThemeErrorBoundary = ThemeErrorBoundary,
26255
28164
  exports.ThemeInspector = ThemeInspector, exports.ThemeLiveEditor = ThemeLiveEditor,
26256
- exports.ThemePreview = ThemePreview, exports.ThemeProvider = ThemeProvider, exports.ThemeValidator = ThemeValidator,
26257
- exports.Todo = Todo, exports.Toggle = Toggle, exports.Tooltip = Tooltip, exports.TreemapChart = TreemapChart,
26258
- exports.UPLOAD = UPLOAD, exports.Upload = Upload, exports.VIDEO_PLAYER = VIDEO_PLAYER,
26259
- exports.VideoPlayer = VideoPlayer, exports.WaterfallChart = WaterfallChart, exports.applyCSSVariables = applyCSSVariables,
28165
+ exports.ThemePreview = ThemePreview, exports.ThemeProvider = ThemeProvider, exports.ThemeToggle = ThemeToggle,
28166
+ exports.ThemeValidator = ThemeValidator, exports.Todo = Todo, exports.Toggle = Toggle,
28167
+ exports.Tooltip = Tooltip, exports.TreemapChart = TreemapChart, exports.UPLOAD = UPLOAD,
28168
+ exports.Upload = Upload, exports.VIDEO_PLAYER = VIDEO_PLAYER, exports.VideoPlayer = VideoPlayer,
28169
+ exports.WaterfallChart = WaterfallChart, exports.alpha = alpha, exports.applyCSSVariables = applyCSSVariables,
26260
28170
  exports.applyCSSVarsToStyle =
26261
28171
  /**
26262
28172
  * Utility to apply CSS variables to style object
@@ -26270,60 +28180,16 @@ function(cssVars, baseStyle) {
26270
28180
  };
26271
28181
  }
26272
28182
  /**
26273
- * Atomix Configuration System
26274
- *
26275
- * Tailwind-like configuration for customizing the Atomix Design System.
26276
- *
26277
- * External developers can create `atomix.config.ts` in their project root
26278
- * to customize design tokens, similar to Tailwind's tailwind.config.js
26279
- *
26280
- * @example
26281
- * ```typescript
26282
- * // atomix.config.ts (in your project)
26283
- * import { defineConfig } from '@shohojdhara/atomix/config';
26284
- *
26285
- * export default defineConfig({
26286
- * theme: {
26287
- * extend: {
26288
- * colors: {
26289
- * primary: { main: '#7AFFD7' },
26290
- * },
26291
- * },
26292
- * },
26293
- * });
26294
- * ```
26295
- */
26296
- /**
26297
- * Helper function to define Atomix configuration with type safety
28183
+ * Configuration Types
26298
28184
  *
26299
- * @param config - Atomix configuration object
26300
- * @returns The configuration object
28185
+ * Type definitions for the Atomix configuration system.
26301
28186
  */
26302
28187
  /**
26303
28188
  * Helper function to define Atomix configuration with type safety
26304
- *
26305
- * Similar to Tailwind's defineConfig, provides autocomplete and type checking.
26306
- *
26307
- * @param config - Atomix configuration object
26308
- * @returns The configuration object
26309
- *
26310
- * @example
26311
- * ```typescript
26312
- * import { defineConfig } from '@shohojdhara/atomix/config';
26313
- *
26314
- * export default defineConfig({
26315
- * theme: {
26316
- * extend: {
26317
- * colors: {
26318
- * primary: { main: '#7AFFD7' },
26319
- * },
26320
- * },
26321
- * },
26322
- * });
26323
- * ```
26324
28189
  */ , exports.applyComponentTheme = applyComponentTheme, exports.applyPartStyles = applyPartStyles,
26325
- exports.applyTheme = applyTheme, exports.camelToKebab = camelToKebab, exports.clearThemes = clearThemes,
26326
- exports.composables = composables, exports.constants = constants, exports.createBreakpoints = createBreakpoints$1,
28190
+ exports.applyTheme = applyTheme, exports.camelToKebab = camelToKebab, exports.clearThemePreference = clearThemePreference,
28191
+ exports.clearThemes = clearThemes, exports.composables = composables, exports.configToTokens = configToTokens,
28192
+ exports.constants = constants, exports.createBreakpoints = createBreakpoints$1,
26327
28193
  exports.createCSSVarStyle = createCSSVarStyle, exports.createDarkVariant =
26328
28194
  /**
26329
28195
  * Create a dark theme variant from a light theme
@@ -26380,7 +28246,9 @@ function(lightTheme) {
26380
28246
  /**
26381
28247
  * Validate theme structure
26382
28248
  */ , exports.createDebugAttrs = createDebugAttrs, exports.createFontPreloadLink = createFontPreloadLink,
26383
- exports.createPartProps = createPartProps, exports.createSlotComponent =
28249
+ exports.createPartProps = createPartProps, exports.createPerformanceMonitor = createPerformanceMonitor,
28250
+ exports.createRTLManager = createRTLManager, exports.createResponsiveUtil = createResponsiveUtil,
28251
+ exports.createSlotComponent =
26384
28252
  /**
26385
28253
  * Create a slot wrapper component
26386
28254
  *
@@ -26418,11 +28286,13 @@ function(defaultElement = "div") {
26418
28286
  }
26419
28287
  /**
26420
28288
  * Hook to manage slot rendering
26421
- */ , exports.createTheme = createTheme, exports.createThemeRegistry = createThemeRegistry,
26422
- exports.createTokens = createTokens, exports.cssVarsToStyle = cssVarsToStyle, exports.deepMerge = deepMerge,
28289
+ */ , exports.createSpacing = createSpacing, exports.createTheme = createTheme,
28290
+ exports.createThemeRegistry = createThemeRegistry, exports.createTokens = createTokens,
28291
+ exports.cssVarsToStyle = cssVarsToStyle, exports.darken = darken, exports.deepMerge = deepMerge,
26423
28292
  exports.default = atomix, exports.defaultTokens = defaultTokens, exports.defineConfig = function(config) {
26424
28293
  return config;
26425
- }, exports.designTokensToCSSVars = designTokensToCSSVars, exports.exportTheme =
28294
+ }, exports.designTokensToCSSVars = designTokensToCSSVars, exports.emphasize = emphasize,
28295
+ exports.exportTheme =
26426
28296
  /**
26427
28297
  * Export theme as JSON
26428
28298
  */
@@ -26442,9 +28312,12 @@ exports.getAllThemes = getAllThemes, exports.getCSSVariable = getCSSVariable, ex
26442
28312
  */
26443
28313
  function(component) {
26444
28314
  return COMPONENT_CSS_VARS[component];
26445
- }, exports.getComponentThemeValue = getComponentThemeValue, exports.getDefaultBreakpoints = getDefaultBreakpoints,
26446
- exports.getDevicePreset = getDevicePreset, exports.getMobileOptimizedParams = getMobileOptimizedParams,
26447
- exports.getPartStyles = getPartStyles, exports.getQualityMultipliers = getQualityMultipliers,
28315
+ }, exports.getComponentThemeValue = getComponentThemeValue, exports.getContrastRatio = getContrastRatio,
28316
+ exports.getContrastText = getContrastText, exports.getCurrentTheme = getCurrentTheme,
28317
+ exports.getDefaultBreakpoints = getDefaultBreakpoints, exports.getDevicePreset = getDevicePreset,
28318
+ exports.getDirectionFromLocale = getDirectionFromLocale, exports.getLuminance = getLuminance,
28319
+ exports.getMobileOptimizedParams = getMobileOptimizedParams, exports.getPartStyles = getPartStyles,
28320
+ exports.getQualityMultipliers = getQualityMultipliers, exports.getSystemTheme = getSystemTheme,
26448
28321
  exports.getTheme = getTheme, exports.getThemeApplicator = getThemeApplicator, exports.getThemeCount = getThemeCount,
26449
28322
  exports.getThemeIds = getThemeIds, exports.getThemeMetadata =
26450
28323
  /**
@@ -26465,7 +28338,7 @@ function(theme) {
26465
28338
  /**
26466
28339
  * Check if theme supports dark mode
26467
28340
  */ , exports.hasCustomization = hasCustomization, exports.hasTheme = hasTheme,
26468
- exports.importTheme = function(json) {
28341
+ exports.hexToRgb = hexToRgb$1, exports.importTheme = function(json) {
26469
28342
  try {
26470
28343
  return JSON.parse(json);
26471
28344
  } catch (error) {
@@ -26476,15 +28349,49 @@ exports.importTheme = function(json) {
26476
28349
  // are already exported from './theme' module. Import them directly from there.
26477
28350
  // This file only exports theme-tools specific utilities.
26478
28351
  // Export all components individually for better tree-shaking
26479
- , exports.injectCSS = injectCSS$1, exports.injectTheme = injectTheme, exports.isCSSInjected = isCSSInjected,
26480
- exports.isDesignTokens = isDesignTokens, exports.isSlot = function(value) {
28352
+ , exports.initializeTheme = initializeTheme, exports.injectCSS = injectCSS$1, exports.injectTheme = injectTheme,
28353
+ exports.isAccessible = isAccessible, exports.isCSSInjected = isCSSInjected, exports.isDesignTokens = isDesignTokens,
28354
+ exports.isRTLLocale = isRTLLocale, exports.isSlot = function(value) {
26481
28355
  return "object" == typeof value && null !== value && ("render" in value || "component" in value || "children" in value);
26482
28356
  }
26483
28357
  /**
26484
28358
  * Merge multiple slot configurations
26485
28359
  * Later slots override earlier ones
26486
28360
  */ , exports.isValidCSSVariableName = isValidCSSVariableName, exports.isYouTubeUrl = isYouTubeUrl,
26487
- exports.mapSCSSTokensToCSSVars = mapSCSSTokensToCSSVars, exports.mergeCSSVars = mergeCSSVars,
28361
+ exports.lighten = lighten, exports.listenToSystemTheme = listenToSystemTheme, exports.loadAtomixConfig = loadAtomixConfig,
28362
+ exports.loadConfig =
28363
+ /**
28364
+ * Public API for loading and managing Atomix configuration
28365
+ *
28366
+ * This module provides the public-facing API for configuration loading
28367
+ * in external projects.
28368
+ */
28369
+ /**
28370
+ * Load Atomix configuration from an external project.
28371
+ *
28372
+ * @param options - Loading options
28373
+ * @returns The loaded configuration
28374
+ *
28375
+ * @example
28376
+ * ```typescript
28377
+ * import { loadConfig } from '@shohojdhara/atomix/config';
28378
+ *
28379
+ * const config = loadConfig();
28380
+ * console.log(config.prefix); // 'atomix' or user's custom prefix
28381
+ * ```
28382
+ */
28383
+ function(options) {
28384
+ return loadAtomixConfig({
28385
+ configPath: options?.configPath,
28386
+ required: options?.required ?? !1
28387
+ });
28388
+ }
28389
+ /**
28390
+ * Validate Atomix configuration structure.
28391
+ *
28392
+ * @param config - Configuration object to validate
28393
+ * @returns Array of validation warnings (empty if valid)
28394
+ */ , exports.mapSCSSTokensToCSSVars = mapSCSSTokensToCSSVars, exports.mergeCSSVars = mergeCSSVars,
26488
28395
  exports.mergeClassNames = mergeClassNames, exports.mergeComponentProps = mergeComponentProps,
26489
28396
  exports.mergePartStyles = mergePartStyles, exports.mergeSlots = function(...slots) {
26490
28397
  const filtered = slots.filter((s => void 0 !== s));
@@ -26492,8 +28399,17 @@ exports.mergePartStyles = mergePartStyles, exports.mergeSlots = function(...slot
26492
28399
  ...acc,
26493
28400
  ...slot
26494
28401
  })));
26495
- }, exports.mergeTheme = mergeTheme, exports.normalizeThemeTokens = normalizeThemeTokens,
26496
- exports.preloadFonts = preloadFonts, exports.quickTheme =
28402
+ }, exports.mergeTheme = mergeTheme, exports.mergeTokens = mergeTokens, exports.normalizeThemeTokens = normalizeThemeTokens,
28403
+ exports.omitTokens = omitTokens, exports.overrideTokens = overrideTokens, exports.persistTheme = persistTheme,
28404
+ exports.pickTokens = pickTokens, exports.preloadFonts = preloadFonts, exports.printConfigReport = function(config, title) {
28405
+ const result = validateConfiguration(config);
28406
+ result.warnings.length > 0 && result.warnings.forEach((warning => {})), result.suggestions.length > 0 && result.suggestions.forEach((suggestion => {}));
28407
+ const featuresDetected = [];
28408
+ config.interactiveEffects && featuresDetected.push("Interactive Effects"), config.optimization && featuresDetected.push("Optimization"),
28409
+ config.visualPolish && featuresDetected.push("Visual Polish"), config.ai && featuresDetected.push("AI Integration"),
28410
+ config.tokenEngine && featuresDetected.push("Token Engine"), config.generator && featuresDetected.push("Component Generator"),
28411
+ featuresDetected.length > 0 && featuresDetected.forEach((feature => {}));
28412
+ }, exports.quickTheme =
26497
28413
  /**
26498
28414
  * Quick theme creator with sensible defaults
26499
28415
  */
@@ -26510,11 +28426,23 @@ function(name, primaryColor, secondaryColor) {
26510
28426
  }
26511
28427
  });
26512
28428
  }, exports.registerTheme = registerTheme, exports.removeCSS = removeCSS, exports.removeCSSVariables = removeCSSVariables,
26513
- exports.removeTheme = removeTheme, exports.renderSlot = renderSlot, exports.sliderConstants = sliderConstants,
28429
+ exports.removeTheme = removeTheme, exports.renderSlot = renderSlot, exports.resolveConfigPath = function(configPath) {
28430
+ // In browser environments, config resolution is not possible
28431
+ if ("undefined" != typeof window) return null;
28432
+ // If a specific config path is provided, check if it exists
28433
+ if (configPath) {
28434
+ const absPath = path$4.join(process.cwd(), configPath);
28435
+ return fs.existsSync(absPath) ? absPath : null;
28436
+ }
28437
+ // Otherwise, check standard locations
28438
+ const possiblePaths = [ path$4.join(process.cwd(), "atomix.config.ts"), path$4.join(process.cwd(), "atomix.config.js"), path$4.join(process.cwd(), "atomix.config.json") ];
28439
+ for (const path of possiblePaths) if (fs.existsSync(path)) return path;
28440
+ return null;
28441
+ }, exports.rgbToHex = rgbToHex, exports.rtlCSS = rtlCSS, exports.sliderConstants = sliderConstants,
26514
28442
  exports.supportsDarkMode = function(theme) {
26515
28443
  var _context;
26516
28444
  return "dark" === theme.palette?.mode || !0 === theme.supportsDarkMode || Boolean((null == (_context = theme.a11y?.modes) ? void 0 : Function.call.bind(_includesInstanceProperty(_context), _context))?.("dark"));
26517
- }, exports.theme = theme, exports.themePropertyToCSSVar = themePropertyToCSSVar,
28445
+ }, exports.switchTheme = switchTheme, exports.theme = theme, exports.themePropertyToCSSVar = themePropertyToCSSVar,
26518
28446
  exports.themeToCSS =
26519
28447
  /**
26520
28448
  * Generate CSS string from theme
@@ -26524,28 +28452,11 @@ function(theme, selector = ":root") {
26524
28452
  selector: selector,
26525
28453
  prefix: "atomix"
26526
28454
  });
26527
- }, exports.types = types, exports.unregisterTheme = unregisterTheme, exports.useAccordion = useAccordion,
26528
- exports.useAtomixGlass = useAtomixGlass, exports.useBadge = useBadge, exports.useBarChart = useBarChart,
26529
- exports.useBlock = useBlock, exports.useChartData = useChartData, exports.useChartInteraction = useChartInteraction,
26530
- exports.useChartScale = useChartScale, exports.useComponentCustomization =
26531
- /**
26532
- * Hook to merge theme overrides with component props
26533
- *
26534
- * @example
26535
- * function Button(props: ButtonProps) {
26536
- * const customization = useComponentCustomization('Button', props);
26537
- *
26538
- * return (
26539
- * <button
26540
- * className={customization.className}
26541
- * style={customization.style}
26542
- * >
26543
- * {props.children}
26544
- * </button>
26545
- * );
26546
- * }
26547
- */
26548
- function(component, props) {
28455
+ }, exports.toggleTheme = toggleTheme, exports.types = types, exports.unregisterTheme = unregisterTheme,
28456
+ exports.useAccordion = useAccordion, exports.useAtomixGlass = useAtomixGlass, exports.useBadge = useBadge,
28457
+ exports.useBarChart = useBarChart, exports.useBlock = useBlock, exports.useChartData = useChartData,
28458
+ exports.useChartInteraction = useChartInteraction, exports.useChartScale = useChartScale,
28459
+ exports.useComponentCustomization = function(component, props) {
26549
28460
  const {theme: theme} = useTheme(), cssVars = React.useMemo((() => mergeCSSVars(theme?.components?.[component]?.cssVars || {}, props.cssVars || {})), [ theme, component, props.cssVars ]), parts = React.useMemo((() => {
26550
28461
  const themeParts = theme?.components?.[component]?.parts || {}, propParts = props.parts || {}, merged = {};
26551
28462
  return new Set([ ...Object.keys(themeParts), ...Object.keys(propParts) ]).forEach((partName => {
@@ -26584,14 +28495,16 @@ exports.useMergedProps = function(defaultProps, props) {
26584
28495
  ...props
26585
28496
  })), [ defaultProps, props ]);
26586
28497
  }, exports.useNav = useNav, exports.useNavDropdown = useNavDropdown, exports.useNavItem = useNavItem,
26587
- exports.useNavbar = useNavbar, exports.usePerformanceMonitor = usePerformanceMonitor,
26588
- exports.usePieChart = usePieChart, exports.useRadio = useRadio, exports.useResponsiveGlass = useResponsiveGlass,
28498
+ exports.useNavbar = useNavbar, exports.usePieChart = usePieChart, exports.useRadio = useRadio,
28499
+ exports.useResponsive = useResponsive, exports.useResponsiveGlass = useResponsiveGlass,
26589
28500
  exports.useRiver = useRiver, exports.useSelect = useSelect, exports.useSideMenu = useSideMenu,
26590
28501
  exports.useSideMenuItem = useSideMenuItem, exports.useSlot = function(slot, props, fallback) {
26591
28502
  return React__default.default.useMemo((() => renderSlot(slot, props, fallback)), [ slot, props, fallback ]);
26592
28503
  }, exports.useSpinner = useSpinner, exports.useTextarea = useTextarea, exports.useTheme = useTheme,
26593
- exports.useThemeTokens = useThemeTokens, exports.useTodo = useTodo, exports.utils = utils,
26594
- exports.validateTheme = function(theme) {
28504
+ exports.useThemeSwitcher = useThemeSwitcher, exports.useThemeTokens = useThemeTokens,
28505
+ exports.useTodo = useTodo, exports.utils = utils, exports.validateConfig = function(config) {
28506
+ return validateConfig$1(config);
28507
+ }, exports.validateConfiguration = validateConfiguration, exports.validateTheme = function(theme) {
26595
28508
  const errors = [];
26596
28509
  return theme.name || errors.push("Theme must have a name"), theme.palette || errors.push("Theme must have a palette"),
26597
28510
  theme.palette && !theme.palette.primary && errors.push("Theme palette must have a primary color"),