@shohojdhara/atomix 0.4.0 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. package/dist/atomix.css +0 -14
  2. package/dist/atomix.css.map +1 -1
  3. package/dist/atomix.min.css +4 -4
  4. package/dist/atomix.min.css.map +1 -1
  5. package/dist/charts.d.ts +12 -19
  6. package/dist/charts.js +555 -359
  7. package/dist/charts.js.map +1 -1
  8. package/dist/core.d.ts +98 -28
  9. package/dist/core.js +1082 -733
  10. package/dist/core.js.map +1 -1
  11. package/dist/forms.d.ts +26 -21
  12. package/dist/forms.js +937 -350
  13. package/dist/forms.js.map +1 -1
  14. package/dist/heavy.d.ts +14 -21
  15. package/dist/heavy.js +409 -256
  16. package/dist/heavy.js.map +1 -1
  17. package/dist/index.d.ts +518 -284
  18. package/dist/index.esm.js +1993 -1237
  19. package/dist/index.esm.js.map +1 -1
  20. package/dist/index.js +1994 -1237
  21. package/dist/index.js.map +1 -1
  22. package/dist/index.min.js +1 -1
  23. package/dist/index.min.js.map +1 -1
  24. package/package.json +2 -2
  25. package/scripts/atomix-cli.js +43 -1
  26. package/scripts/cli/__tests__/utils.test.js +6 -2
  27. package/scripts/cli/migration-tools.js +2 -2
  28. package/scripts/cli/theme-bridge.js +7 -9
  29. package/scripts/cli/utils.js +2 -1
  30. package/src/components/Accordion/Accordion.stories.tsx +40 -0
  31. package/src/components/Accordion/Accordion.tsx +174 -56
  32. package/src/components/Accordion/AccordionCompound.test.tsx +70 -0
  33. package/src/components/AtomixGlass/AtomixGlass.tsx +82 -54
  34. package/src/components/AtomixGlass/AtomixGlassContainer.tsx +17 -18
  35. package/src/components/AtomixGlass/README.md +5 -5
  36. package/src/components/AtomixGlass/stories/Customization.stories.tsx +2 -2
  37. package/src/components/AtomixGlass/stories/Examples.stories.tsx +42 -42
  38. package/src/components/AtomixGlass/stories/Modes.stories.tsx +5 -5
  39. package/src/components/AtomixGlass/stories/Overview.stories.tsx +3 -3
  40. package/src/components/AtomixGlass/stories/Performance.stories.tsx +2 -2
  41. package/src/components/AtomixGlass/stories/Playground.stories.tsx +45 -45
  42. package/src/components/AtomixGlass/stories/Shaders.stories.tsx +3 -3
  43. package/src/components/Badge/Badge.stories.tsx +1 -1
  44. package/src/components/Badge/Badge.tsx +1 -1
  45. package/src/components/Breadcrumb/Breadcrumb.tsx +185 -65
  46. package/src/components/Breadcrumb/BreadcrumbCompound.test.tsx +84 -0
  47. package/src/components/Breadcrumb/index.ts +2 -2
  48. package/src/components/Button/Button.stories.tsx +1 -1
  49. package/src/components/Button/README.md +2 -2
  50. package/src/components/Callout/Callout.stories.tsx +166 -1011
  51. package/src/components/Callout/Callout.test.tsx +3 -3
  52. package/src/components/Callout/Callout.tsx +196 -84
  53. package/src/components/Callout/CalloutCompound.test.tsx +72 -0
  54. package/src/components/Callout/README.md +2 -2
  55. package/src/components/Chart/Chart.stories.tsx +1 -1
  56. package/src/components/Chart/Chart.tsx +5 -5
  57. package/src/components/Chart/TreemapChart.tsx +37 -29
  58. package/src/components/DatePicker/readme.md +3 -3
  59. package/src/components/Dropdown/Dropdown.stories.tsx +1 -1
  60. package/src/components/Dropdown/Dropdown.tsx +133 -20
  61. package/src/components/Dropdown/DropdownCompound.test.tsx +64 -0
  62. package/src/components/EdgePanel/EdgePanel.stories.tsx +7 -7
  63. package/src/components/EdgePanel/EdgePanel.tsx +164 -112
  64. package/src/components/EdgePanel/EdgePanelCompound.test.tsx +53 -0
  65. package/src/components/Form/Checkbox.stories.tsx +1 -1
  66. package/src/components/Form/Checkbox.tsx +1 -1
  67. package/src/components/Form/Input.stories.tsx +1 -1
  68. package/src/components/Form/Input.tsx +1 -1
  69. package/src/components/Form/Radio.stories.tsx +1 -1
  70. package/src/components/Form/Radio.tsx +1 -1
  71. package/src/components/Form/Select.stories.tsx +24 -1
  72. package/src/components/Form/Select.test.tsx +99 -0
  73. package/src/components/Form/Select.tsx +145 -94
  74. package/src/components/Form/SelectOption.tsx +88 -0
  75. package/src/components/Form/Textarea.stories.tsx +1 -1
  76. package/src/components/Form/Textarea.tsx +1 -1
  77. package/src/components/Hero/Hero.stories.tsx +39 -2
  78. package/src/components/Hero/Hero.test.tsx +142 -0
  79. package/src/components/Hero/Hero.tsx +143 -4
  80. package/src/components/List/List.test.tsx +62 -0
  81. package/src/components/List/List.tsx +16 -5
  82. package/src/components/List/ListItem.tsx +20 -0
  83. package/src/components/Messages/Messages.stories.tsx +1 -1
  84. package/src/components/Messages/Messages.tsx +2 -2
  85. package/src/components/Modal/Modal.stories.tsx +66 -2
  86. package/src/components/Modal/Modal.tsx +115 -35
  87. package/src/components/Modal/ModalCompound.test.tsx +94 -0
  88. package/src/components/Navigation/Nav/Nav.stories.tsx +2 -2
  89. package/src/components/Navigation/Nav/Nav.tsx +1 -1
  90. package/src/components/Navigation/Navbar/Navbar.stories.tsx +3 -3
  91. package/src/components/Navigation/Navbar/Navbar.tsx +1 -1
  92. package/src/components/Navigation/SideMenu/SideMenu.stories.tsx +2 -2
  93. package/src/components/Navigation/SideMenu/SideMenu.tsx +1 -1
  94. package/src/components/Pagination/Pagination.stories.tsx +1 -1
  95. package/src/components/Pagination/Pagination.tsx +1 -1
  96. package/src/components/Popover/Popover.stories.tsx +1 -1
  97. package/src/components/Popover/Popover.tsx +1 -1
  98. package/src/components/Progress/Progress.tsx +1 -1
  99. package/src/components/Rating/Rating.stories.tsx +1 -1
  100. package/src/components/Rating/Rating.test.tsx +73 -0
  101. package/src/components/Rating/Rating.tsx +25 -37
  102. package/src/components/Spinner/Spinner.tsx +1 -1
  103. package/src/components/Steps/Steps.stories.tsx +1 -1
  104. package/src/components/Steps/Steps.tsx +125 -22
  105. package/src/components/Steps/StepsCompound.test.tsx +81 -0
  106. package/src/components/Tabs/Tabs.stories.tsx +1 -1
  107. package/src/components/Tabs/Tabs.tsx +198 -45
  108. package/src/components/Tabs/TabsCompound.test.tsx +64 -0
  109. package/src/components/Todo/Todo.tsx +0 -1
  110. package/src/components/Toggle/Toggle.stories.tsx +1 -1
  111. package/src/components/Toggle/Toggle.tsx +1 -1
  112. package/src/components/Tooltip/Tooltip.stories.tsx +1 -1
  113. package/src/components/VideoPlayer/VideoPlayer.stories.tsx +2 -2
  114. package/src/lib/composables/__tests__/useAtomixGlassPerf.test.tsx +88 -0
  115. package/src/lib/composables/__tests__/useChart.test.ts +50 -0
  116. package/src/lib/composables/__tests__/useChart.test.tsx +139 -0
  117. package/src/lib/composables/__tests__/useHeroBackgroundSlider.test.tsx +59 -0
  118. package/src/lib/composables/__tests__/useSliderAutoplay.test.tsx +68 -0
  119. package/src/lib/composables/atomix-glass/useGlassBackgroundDetection.ts +329 -0
  120. package/src/lib/composables/atomix-glass/useGlassCornerRadius.ts +82 -0
  121. package/src/lib/composables/atomix-glass/useGlassMouseTracking.ts +153 -0
  122. package/src/lib/composables/atomix-glass/useGlassOverLight.ts +198 -0
  123. package/src/lib/composables/atomix-glass/useGlassSize.ts +117 -0
  124. package/src/lib/composables/atomix-glass/useGlassState.ts +112 -0
  125. package/src/lib/composables/atomix-glass/useGlassTransforms.ts +160 -0
  126. package/src/lib/composables/glass-styles.ts +302 -0
  127. package/src/lib/composables/index.ts +0 -8
  128. package/src/lib/composables/useAtomixGlass.ts +331 -537
  129. package/src/lib/composables/useAtomixGlassStyles.ts +307 -0
  130. package/src/lib/composables/useBarChart.ts +1 -1
  131. package/src/lib/composables/useBreadcrumb.ts +6 -6
  132. package/src/lib/composables/useChart.ts +104 -21
  133. package/src/lib/composables/useHeroBackgroundSlider.ts +16 -7
  134. package/src/lib/composables/useSlider.ts +66 -34
  135. package/src/lib/theme/devtools/CLI.ts +2 -10
  136. package/src/lib/theme/utils/__tests__/themeUtils.test.ts +213 -0
  137. package/src/lib/types/components.ts +21 -23
  138. package/src/lib/utils/__tests__/componentUtils.test.ts +57 -2
  139. package/src/lib/utils/__tests__/dom.test.ts +100 -0
  140. package/src/lib/utils/__tests__/fontPreloader.test.ts +102 -0
  141. package/src/lib/utils/__tests__/themeNaming.test.ts +117 -0
  142. package/src/lib/utils/themeNaming.ts +1 -1
  143. package/src/styles/06-components/_components.accordion.scss +0 -2
  144. package/src/styles/06-components/_components.chart.scss +0 -1
  145. package/src/styles/06-components/_components.dropdown.scss +0 -1
  146. package/src/styles/06-components/_components.edge-panel.scss +0 -2
  147. package/src/styles/06-components/_components.photoviewer.scss +0 -1
  148. package/src/styles/06-components/_components.river.scss +0 -1
  149. package/src/styles/06-components/_components.slider.scss +0 -3
  150. package/src/styles/99-utilities/_utilities.glass-fixes.scss +0 -1
@@ -0,0 +1,307 @@
1
+ import { ATOMIX_GLASS } from '../constants/components';
2
+ import { calculateDistance, calculateElementCenter, calculateMouseInfluence, validateGlassSize, clampBlur } from '../../components/AtomixGlass/glass-utils';
3
+ import type { GlassSize, MousePosition, OverLightObjectConfig } from '../types/components';
4
+
5
+ /**
6
+ * Updates the styles of the AtomixGlass wrapper and container elements imperatively
7
+ * to avoid React re-renders on mouse movement.
8
+ */
9
+ export const updateAtomixGlassStyles = (
10
+ wrapperElement: HTMLElement | null,
11
+ containerElement: HTMLElement | null,
12
+ params: {
13
+ mouseOffset: MousePosition;
14
+ globalMousePosition: MousePosition;
15
+ glassSize: GlassSize;
16
+ isHovered: boolean;
17
+ isActive: boolean;
18
+ isOverLight: boolean;
19
+ baseOverLightConfig: {
20
+ opacity: number;
21
+ borderOpacity: number;
22
+ contrast: number;
23
+ brightness: number;
24
+ shadowIntensity: number;
25
+ saturationBoost: number;
26
+ };
27
+ effectiveBorderRadius: number;
28
+ effectiveWithoutEffects: boolean;
29
+ effectiveReducedMotion: boolean;
30
+ elasticity: number;
31
+ directionalScale: string;
32
+ onClick?: () => void;
33
+ withLiquidBlur?: boolean;
34
+ blurAmount?: number;
35
+ saturation?: number;
36
+ padding?: string;
37
+ }
38
+ ) => {
39
+ if (!wrapperElement && !containerElement) return;
40
+
41
+ const {
42
+ mouseOffset,
43
+ globalMousePosition,
44
+ glassSize,
45
+ isHovered,
46
+ isActive,
47
+ isOverLight,
48
+ baseOverLightConfig,
49
+ effectiveBorderRadius,
50
+ effectiveWithoutEffects,
51
+ effectiveReducedMotion,
52
+ elasticity,
53
+ directionalScale,
54
+ onClick,
55
+ withLiquidBlur,
56
+ blurAmount = ATOMIX_GLASS.DEFAULTS.BLUR_AMOUNT,
57
+ saturation = ATOMIX_GLASS.DEFAULTS.SATURATION,
58
+ padding = ATOMIX_GLASS.DEFAULTS.PADDING,
59
+ } = params;
60
+
61
+ // Calculate mouse influence
62
+ const mouseInfluence = calculateMouseInfluence(mouseOffset);
63
+ const hoverIntensity = isHovered ? 1.4 : 1;
64
+ const activeIntensity = isActive ? 1.6 : 1;
65
+
66
+ // Calculate dynamic OverLight config
67
+ const overLightConfig = {
68
+ opacity: baseOverLightConfig.opacity * hoverIntensity * activeIntensity,
69
+ contrast: Math.min(1.6, baseOverLightConfig.contrast + mouseInfluence * 0.1),
70
+ brightness: Math.min(1.1, baseOverLightConfig.brightness + mouseInfluence * 0.05),
71
+ shadowIntensity: Math.min(1.2, Math.max(0.5, baseOverLightConfig.shadowIntensity + mouseInfluence * 0.2)),
72
+ borderOpacity: Math.min(1.0, Math.max(0.3, baseOverLightConfig.borderOpacity + mouseInfluence * 0.1)),
73
+ saturationBoost: baseOverLightConfig.saturationBoost
74
+ };
75
+
76
+ // Calculate elastic translation
77
+ let elasticTranslation = { x: 0, y: 0 };
78
+ if (!effectiveWithoutEffects && wrapperElement) {
79
+ const rect = wrapperElement.getBoundingClientRect();
80
+ const center = calculateElementCenter(rect);
81
+
82
+ // Calculate fade in factor
83
+ let fadeInFactor = 0;
84
+ if (globalMousePosition.x && globalMousePosition.y && validateGlassSize(glassSize)) {
85
+ const edgeDistanceX = Math.max(0, Math.abs(globalMousePosition.x - center.x) - glassSize.width / 2);
86
+ const edgeDistanceY = Math.max(0, Math.abs(globalMousePosition.y - center.y) - glassSize.height / 2);
87
+ const edgeDistance = calculateDistance({ x: edgeDistanceX, y: edgeDistanceY }, { x: 0, y: 0 });
88
+ fadeInFactor = edgeDistance > ATOMIX_GLASS.CONSTANTS.ACTIVATION_ZONE ? 0 : 1 - edgeDistance / ATOMIX_GLASS.CONSTANTS.ACTIVATION_ZONE;
89
+ }
90
+
91
+ elasticTranslation = {
92
+ x: (globalMousePosition.x - center.x) * elasticity * 0.1 * fadeInFactor,
93
+ y: (globalMousePosition.y - center.y) * elasticity * 0.1 * fadeInFactor,
94
+ };
95
+ }
96
+
97
+ const transformStyle = effectiveWithoutEffects
98
+ ? isActive && Boolean(onClick) ? 'scale(0.98)' : 'scale(1)'
99
+ : `translate(${elasticTranslation.x}px, ${elasticTranslation.y}px) ${isActive && Boolean(onClick) ? 'scale(0.96)' : directionalScale}`;
100
+
101
+ // Update Wrapper Styles (glassVars)
102
+ if (wrapperElement) {
103
+ const mx = mouseOffset.x;
104
+ const my = mouseOffset.y;
105
+ const absMx = Math.abs(mx);
106
+ const absMy = Math.abs(my);
107
+ const GRADIENT = ATOMIX_GLASS.CONSTANTS.GRADIENT;
108
+
109
+ const borderGradientAngle = GRADIENT.BASE_ANGLE + mx * GRADIENT.ANGLE_MULTIPLIER;
110
+ const borderStop1 = Math.max(
111
+ GRADIENT.BORDER_STOP_1.MIN,
112
+ GRADIENT.BORDER_STOP_1.BASE + my * GRADIENT.BORDER_STOP_1.MULTIPLIER
113
+ );
114
+ const borderStop2 = Math.min(
115
+ GRADIENT.BORDER_STOP_2.MAX,
116
+ GRADIENT.BORDER_STOP_2.BASE + my * GRADIENT.BORDER_STOP_2.MULTIPLIER
117
+ );
118
+ const borderOpacities = [
119
+ GRADIENT.BORDER_OPACITY.BASE_1 + absMx * GRADIENT.BORDER_OPACITY.MULTIPLIER_LOW,
120
+ GRADIENT.BORDER_OPACITY.BASE_2 + absMx * GRADIENT.BORDER_OPACITY.MULTIPLIER_HIGH,
121
+ GRADIENT.BORDER_OPACITY.BASE_3 + absMx * GRADIENT.BORDER_OPACITY.MULTIPLIER_LOW,
122
+ GRADIENT.BORDER_OPACITY.BASE_4 + absMx * GRADIENT.BORDER_OPACITY.MULTIPLIER_HIGH,
123
+ ];
124
+
125
+ const configBorderOpacity = overLightConfig.borderOpacity;
126
+ const whiteColor = ATOMIX_GLASS.CONSTANTS.PALETTE.WHITE;
127
+ const blackColor = ATOMIX_GLASS.CONSTANTS.PALETTE.BLACK;
128
+
129
+ const hoverPositions = {
130
+ hover1: {
131
+ x: GRADIENT.CENTER_POSITION + mx / GRADIENT.HOVER_POSITION.DIVISOR_1,
132
+ y: GRADIENT.CENTER_POSITION + my / GRADIENT.HOVER_POSITION.DIVISOR_1,
133
+ },
134
+ hover2: {
135
+ x: GRADIENT.CENTER_POSITION + mx / GRADIENT.HOVER_POSITION.DIVISOR_2,
136
+ y: GRADIENT.CENTER_POSITION + my / GRADIENT.HOVER_POSITION.DIVISOR_2,
137
+ },
138
+ hover3: {
139
+ x: GRADIENT.CENTER_POSITION + mx * GRADIENT.HOVER_POSITION.MULTIPLIER_3,
140
+ y: GRADIENT.CENTER_POSITION + my * GRADIENT.HOVER_POSITION.MULTIPLIER_3,
141
+ },
142
+ };
143
+
144
+ const basePosition = {
145
+ x: GRADIENT.CENTER_POSITION + mx * GRADIENT.BASE_LAYER_MULTIPLIER,
146
+ y: GRADIENT.CENTER_POSITION + my * GRADIENT.BASE_LAYER_MULTIPLIER,
147
+ };
148
+
149
+ const opacityValues = {
150
+ hover1: isHovered || isActive ? 0.5 : 0,
151
+ hover2: isActive ? 0.5 : 0,
152
+ hover3: isHovered ? 0.4 : isActive ? 0.8 : 0,
153
+ base: isOverLight ? overLightConfig.opacity : 0,
154
+ over: isOverLight ? overLightConfig.opacity * 1.1 : 0,
155
+ };
156
+
157
+ const style = wrapperElement.style;
158
+
159
+ style.setProperty('--atomix-glass-transform', transformStyle || 'none');
160
+
161
+ // Gradients
162
+ style.setProperty('--atomix-glass-border-gradient-1', `linear-gradient(${borderGradientAngle}deg, rgba(${whiteColor}, 0) 0%, rgba(${whiteColor}, ${(borderOpacities[0] ?? 1) * configBorderOpacity}) ${borderStop1}%, rgba(${whiteColor}, ${(borderOpacities[1] ?? 1) * configBorderOpacity}) ${borderStop2}%, rgba(${whiteColor}, 0) 100%)`);
163
+ style.setProperty('--atomix-glass-border-gradient-2', `linear-gradient(${borderGradientAngle}deg, rgba(${whiteColor}, 0) 0%, rgba(${whiteColor}, ${(borderOpacities[2] ?? 1) * configBorderOpacity}) ${borderStop1}%, rgba(${whiteColor}, ${(borderOpacities[3] ?? 1) * configBorderOpacity}) ${borderStop2}%, rgba(${whiteColor}, 0) 100%)`);
164
+
165
+ // Hover gradients
166
+ style.setProperty('--atomix-glass-hover-1-gradient', isOverLight
167
+ ? `radial-gradient(circle at ${hoverPositions.hover1.x}% ${hoverPositions.hover1.y}%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_1.BLACK_START}) 0%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_1.BLACK_MID}) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_1.BLACK_STOP}%, rgba(${blackColor}, 0) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_1.BLACK_END}%)`
168
+ : `radial-gradient(circle at ${hoverPositions.hover1.x}% ${hoverPositions.hover1.y}%, rgba(${whiteColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_1.WHITE_START}) 0%, rgba(${whiteColor}, 0) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_1.WHITE_STOP}%)`);
169
+
170
+ style.setProperty('--atomix-glass-hover-2-gradient', isOverLight
171
+ ? `radial-gradient(circle at ${hoverPositions.hover2.x}% ${hoverPositions.hover2.y}%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_2.BLACK_START}) 0%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_2.BLACK_MID}) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_2.BLACK_STOP}%, rgba(${blackColor}, 0) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_2.BLACK_END}%)`
172
+ : `radial-gradient(circle at ${hoverPositions.hover2.x}% ${hoverPositions.hover2.y}%, rgba(${whiteColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_2.WHITE_START}) 0%, rgba(${whiteColor}, 0) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_2.WHITE_STOP}%)`);
173
+
174
+ style.setProperty('--atomix-glass-hover-3-gradient', isOverLight
175
+ ? `radial-gradient(circle at ${hoverPositions.hover3.x}% ${hoverPositions.hover3.y}%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_3.BLACK_START}) 0%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_3.BLACK_MID}) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_3.BLACK_STOP}%, rgba(${blackColor}, 0) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_3.BLACK_END}%)`
176
+ : `radial-gradient(circle at ${hoverPositions.hover3.x}% ${hoverPositions.hover3.y}%, rgba(${whiteColor}, ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_3.WHITE_START}) 0%, rgba(${whiteColor}, 0) ${ATOMIX_GLASS.CONSTANTS.GRADIENT_OPACITY.HOVER_3.WHITE_STOP}%)`);
177
+
178
+ style.setProperty('--atomix-glass-base-gradient', isOverLight
179
+ ? `linear-gradient(${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.ANGLE}deg, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_START_BASE + mx * ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_START_MULTIPLIER}) 0%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_MID_BASE + my * ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_MID_MULTIPLIER}) ${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_MID_STOP}%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_END_BASE + absMx * ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.BLACK_END_MULTIPLIER}) 100%)`
180
+ : `rgba(${whiteColor}, ${ATOMIX_GLASS.CONSTANTS.BASE_GRADIENT.WHITE_OPACITY})`);
181
+
182
+ style.setProperty('--atomix-glass-overlay-gradient', isOverLight
183
+ ? `radial-gradient(circle at ${basePosition.x}% ${basePosition.y}%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_START_BASE + absMx * ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_START_MULTIPLIER}) 0%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_MID}) ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_MID_STOP}%, rgba(${blackColor}, ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_END_BASE + absMy * ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.BLACK_END_MULTIPLIER}) 100%)`
184
+ : `rgba(${whiteColor}, ${ATOMIX_GLASS.CONSTANTS.OVERLAY_GRADIENT.WHITE_OPACITY})`);
185
+
186
+ // Opacities
187
+ style.setProperty('--atomix-glass-hover-1-opacity', opacityValues.hover1.toString());
188
+ style.setProperty('--atomix-glass-hover-2-opacity', opacityValues.hover2.toString());
189
+ style.setProperty('--atomix-glass-hover-3-opacity', opacityValues.hover3.toString());
190
+ style.setProperty('--atomix-glass-base-opacity', opacityValues.base.toString());
191
+ style.setProperty('--atomix-glass-overlay-opacity', opacityValues.over.toString());
192
+ style.setProperty('--atomix-glass-overlay-highlight-opacity', (opacityValues.over * ATOMIX_GLASS.CONSTANTS.OVERLAY_HIGHLIGHT.OPACITY_MULTIPLIER).toString());
193
+
194
+ // Other
195
+ style.setProperty('--atomix-glass-blend-mode', isOverLight ? 'multiply' : 'overlay');
196
+ style.setProperty('--atomix-glass-radius', `${effectiveBorderRadius}px`);
197
+ }
198
+
199
+ // Update Container Styles (containerVars)
200
+ if (containerElement) {
201
+ const mx = mouseOffset.x;
202
+ const my = mouseOffset.y;
203
+
204
+ // Constants for blur calculation
205
+ const EDGE_BLUR_MULTIPLIER = 1.25;
206
+ const CENTER_BLUR_MULTIPLIER = 1.1;
207
+ const FLOW_BLUR_MULTIPLIER = 1.2;
208
+ const MOUSE_INFLUENCE_BLUR_FACTOR = 0.15;
209
+ const EDGE_INTENSITY_MOUSE_FACTOR = 0.15;
210
+ const CENTER_INTENSITY_MOUSE_FACTOR = 0.1;
211
+ const MAX_BLUR_RELATIVE = 2;
212
+
213
+ const rect = containerElement.getBoundingClientRect();
214
+
215
+ let liquidBlur = {
216
+ baseBlur: blurAmount,
217
+ edgeBlur: blurAmount * EDGE_BLUR_MULTIPLIER,
218
+ centerBlur: blurAmount * CENTER_BLUR_MULTIPLIER,
219
+ flowBlur: blurAmount * FLOW_BLUR_MULTIPLIER,
220
+ };
221
+
222
+ if (withLiquidBlur && rect) {
223
+ const mouseInfluence = calculateMouseInfluence(mouseOffset);
224
+ const maxBlur = blurAmount * MAX_BLUR_RELATIVE;
225
+
226
+ const baseBlur = Math.min(
227
+ maxBlur,
228
+ blurAmount + mouseInfluence * blurAmount * MOUSE_INFLUENCE_BLUR_FACTOR
229
+ );
230
+ const edgeIntensity = mouseInfluence * EDGE_INTENSITY_MOUSE_FACTOR;
231
+ const edgeBlur = Math.min(maxBlur, baseBlur * (0.8 + edgeIntensity * 0.4));
232
+ const centerIntensity = mouseInfluence * CENTER_INTENSITY_MOUSE_FACTOR;
233
+ const centerBlur = Math.min(maxBlur, baseBlur * (0.3 + centerIntensity * 0.3));
234
+ const flowBlur = Math.min(maxBlur, baseBlur * FLOW_BLUR_MULTIPLIER);
235
+
236
+ liquidBlur = {
237
+ baseBlur: clampBlur(baseBlur),
238
+ edgeBlur: clampBlur(edgeBlur),
239
+ centerBlur: clampBlur(centerBlur),
240
+ flowBlur: clampBlur(flowBlur),
241
+ };
242
+ }
243
+
244
+ // Backdrop filter
245
+ let backdropFilterString = `blur(${blurAmount}px) saturate(${saturation}%) contrast(1.05) brightness(1.05)`;
246
+
247
+ const dynamicSaturation = saturation + (liquidBlur.baseBlur || 0) * 20;
248
+ const area = rect ? rect.width * rect.height : 0;
249
+ const areaIsLarge = area > 180000;
250
+ const devicePrefersPerformance = effectiveReducedMotion || effectiveWithoutEffects;
251
+ const useMultiPass = withLiquidBlur && !devicePrefersPerformance && !areaIsLarge;
252
+
253
+ if (useMultiPass) {
254
+ const weightedBlur = clampBlur(
255
+ liquidBlur.baseBlur * 0.4 +
256
+ liquidBlur.edgeBlur * 0.25 +
257
+ liquidBlur.centerBlur * 0.15 +
258
+ liquidBlur.flowBlur * 0.2
259
+ );
260
+ backdropFilterString = `blur(${weightedBlur}px) saturate(${Math.min(dynamicSaturation, 200)}%) contrast(${overLightConfig.contrast}) brightness(${overLightConfig.brightness})`;
261
+ } else {
262
+ const effectiveBlur = clampBlur(
263
+ Math.max(
264
+ liquidBlur.baseBlur,
265
+ liquidBlur.edgeBlur * 0.8,
266
+ liquidBlur.centerBlur * 1.1,
267
+ liquidBlur.flowBlur * 0.9
268
+ )
269
+ );
270
+ backdropFilterString = `blur(${effectiveBlur}px) saturate(${Math.min(dynamicSaturation, 200)}%) contrast(${overLightConfig.contrast}) brightness(${overLightConfig.brightness})`;
271
+ }
272
+
273
+ // Container variables
274
+ const style = containerElement.style;
275
+
276
+ style.setProperty('--atomix-glass-container-width', `${glassSize.width}`);
277
+ style.setProperty('--atomix-glass-container-height', `${glassSize.height}`);
278
+ style.setProperty('--atomix-glass-container-padding', padding);
279
+ style.setProperty('--atomix-glass-container-radius', `${effectiveBorderRadius}px`);
280
+
281
+ style.setProperty('--atomix-glass-container-backdrop', backdropFilterString);
282
+
283
+ // Shadows
284
+ style.setProperty('--atomix-glass-container-shadow', isOverLight
285
+ ? [
286
+ `inset 0 1px 0 rgba(255, 255, 255, ${(0.4 + mx * 0.002) * (overLightConfig.shadowIntensity || 1)})`,
287
+ `inset 0 -1px 0 rgba(0, 0, 0, ${(0.2 + Math.abs(my) * 0.001) * (overLightConfig.shadowIntensity || 1)})`,
288
+ `inset 0 0 20px rgba(0, 0, 0, ${(0.08 + Math.abs(mx + my) * 0.001) * (overLightConfig.shadowIntensity || 1)})`,
289
+ `0 2px 12px rgba(0, 0, 0, ${(0.12 + Math.abs(my) * 0.002) * (overLightConfig.shadowIntensity || 1)})`,
290
+ ].join(', ')
291
+ : '0 0 20px rgba(0, 0, 0, 0.15) inset, 0 4px 8px rgba(0, 0, 0, 0.08) inset');
292
+
293
+ style.setProperty('--atomix-glass-container-shadow-opacity', effectiveWithoutEffects ? '0' : '1');
294
+
295
+ style.setProperty('--atomix-glass-container-bg', isOverLight
296
+ ? `linear-gradient(${180 + mx * 0.5}deg, rgba(255, 255, 255, 0.1) 0%, transparent 20%, transparent 80%, rgba(0, 0, 0, 0.05) 100%)`
297
+ : 'none');
298
+
299
+ style.setProperty('--atomix-glass-container-text-shadow', isOverLight
300
+ ? '0px 2px 12px rgba(0, 0, 0, 0)'
301
+ : '0px 2px 12px rgba(0, 0, 0, 0.4)');
302
+
303
+ style.setProperty('--atomix-glass-container-box-shadow', isOverLight
304
+ ? '0px 16px 70px rgba(0, 0, 0, 0.75)'
305
+ : '0px 12px 40px rgba(0, 0, 0, 0.25)');
306
+ }
307
+ };
@@ -18,7 +18,7 @@ export interface BarChartOptions {
18
18
  /**
19
19
  * Corner radius for bars
20
20
  */
21
- cornerRadius?: number;
21
+ borderRadius?: number;
22
22
 
23
23
  /**
24
24
  * Padding between bar groups
@@ -1,8 +1,8 @@
1
- import { BreadcrumbItem } from '../../components/Breadcrumb/Breadcrumb';
1
+ import { BreadcrumbItemType } from '../../components/Breadcrumb/Breadcrumb';
2
2
  import { BREADCRUMB } from '../constants/components';
3
3
 
4
4
  interface BreadcrumbOptions {
5
- items: BreadcrumbItem[];
5
+ items: BreadcrumbItemType[];
6
6
  divider?: React.ReactNode;
7
7
  className?: string;
8
8
  'aria-label'?: string;
@@ -40,7 +40,7 @@ export function useBreadcrumb(initialOptions?: Partial<BreadcrumbOptions>) {
40
40
  * @param isLast - Whether this is the last item
41
41
  * @returns Class string
42
42
  */
43
- const generateItemClass = (item: BreadcrumbItem, isLast: boolean): string => {
43
+ const generateItemClass = (item: BreadcrumbItemType, isLast: boolean): string => {
44
44
  return [BREADCRUMB.CLASSES.ITEM, item.active || isLast ? BREADCRUMB.CLASSES.ACTIVE : '']
45
45
  .filter(Boolean)
46
46
  .join(' ')
@@ -53,7 +53,7 @@ export function useBreadcrumb(initialOptions?: Partial<BreadcrumbOptions>) {
53
53
  * @param isLast - Whether this is the last item
54
54
  * @returns Whether item should be a link
55
55
  */
56
- const isItemLink = (item: BreadcrumbItem, isLast: boolean): boolean => {
56
+ const isItemLink = (item: BreadcrumbItemType, isLast: boolean): boolean => {
57
57
  return Boolean(item.href && !item.active && !isLast);
58
58
  };
59
59
 
@@ -62,9 +62,9 @@ export function useBreadcrumb(initialOptions?: Partial<BreadcrumbOptions>) {
62
62
  * @param jsonString - JSON string of items
63
63
  * @returns Array of breadcrumb items
64
64
  */
65
- const parseItemsFromJson = (jsonString: string): BreadcrumbItem[] => {
65
+ const parseItemsFromJson = (jsonString: string): BreadcrumbItemType[] => {
66
66
  try {
67
- return JSON.parse(jsonString) as BreadcrumbItem[];
67
+ return JSON.parse(jsonString) as BreadcrumbItemType[];
68
68
  } catch (error) {
69
69
  console.error('Error parsing breadcrumb items:', error);
70
70
  return [];
@@ -1,6 +1,6 @@
1
1
  import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
2
  import { CHART } from '../constants/components';
3
- import { ChartDataset, ChartProps } from '../types/components';
3
+ import { ChartDataset, ChartProps, ChartDataPoint } from '../types/components';
4
4
 
5
5
  /**
6
6
  * Chart interaction state interface
@@ -527,12 +527,32 @@ export function useChart(initialProps?: Partial<ChartProps>) {
527
527
  ): ChartScales | null => {
528
528
  if (!datasets || datasets.length === 0) return null;
529
529
 
530
- // Flatten all data points to find min/max values
531
- const allDataPoints = datasets.flatMap(dataset => dataset.data);
532
- if (allDataPoints.length === 0) return null;
530
+ // Calculate total points and min/max values efficiently avoiding spread operator
531
+ let totalPoints = 0;
532
+ let minValue = Infinity;
533
+ let maxValue = -Infinity;
534
+ let hasValidData = false;
535
+
536
+ for (const dataset of datasets) {
537
+ if (dataset.data) {
538
+ totalPoints += dataset.data.length;
539
+ const { min, max, hasValid } = getDatasetBounds(dataset.data);
540
+ if (hasValid) {
541
+ if (min < minValue) minValue = min;
542
+ if (max > maxValue) maxValue = max;
543
+ hasValidData = true;
544
+ }
545
+ }
546
+ }
547
+
548
+ if (totalPoints === 0) return null;
549
+
550
+ // Handle case with no valid numeric data
551
+ if (!hasValidData) {
552
+ minValue = 0;
553
+ maxValue = 0;
554
+ }
533
555
 
534
- const minValue = Math.min(...allDataPoints.map(point => point.value));
535
- const maxValue = Math.max(...allDataPoints.map(point => point.value));
536
556
  const valueRange = maxValue - minValue || 1; // Avoid division by zero
537
557
 
538
558
  // Apply padding
@@ -540,7 +560,7 @@ export function useChart(initialProps?: Partial<ChartProps>) {
540
560
  const innerHeight = height - padding.top - padding.bottom;
541
561
 
542
562
  // Create scale functions
543
- const xScale = (index: number, dataLength: number = allDataPoints.length) => {
563
+ const xScale = (index: number, dataLength: number = totalPoints) => {
544
564
  if (dataLength <= 1) return padding.left + innerWidth / 2;
545
565
  return padding.left + (index / (dataLength - 1)) * innerWidth;
546
566
  };
@@ -641,6 +661,23 @@ export function useChartData(
641
661
  realTimeInterval = 1000,
642
662
  } = options || {};
643
663
 
664
+ const lastDataSignature = useRef<string>('');
665
+
666
+ // Helper to generate a signature for the dataset to detect changes
667
+ const getDatasetSignature = useCallback((data: ChartDataset[]) => {
668
+ return data
669
+ .map(d => {
670
+ // Use JSON stringify for robustness to detect any value change, including historical data
671
+ return `${d.label}:${JSON.stringify(d.data)}`;
672
+ })
673
+ .join('|');
674
+ }, []);
675
+
676
+ // Update signature when processedData changes (e.g. via props)
677
+ useEffect(() => {
678
+ lastDataSignature.current = getDatasetSignature(processedData);
679
+ }, [processedData, getDatasetSignature]);
680
+
644
681
  // Data decimation for performance
645
682
  const decimateData = useCallback(
646
683
  (data: ChartDataset[], maxPoints: number) => {
@@ -677,10 +714,20 @@ export function useChartData(
677
714
  const n = values.length;
678
715
  if (n < 2) return values.map((): null => null);
679
716
 
680
- const xSum = values.reduce((sum, _, i) => sum + i, 0);
681
- const ySum = values.reduce((sum, val) => sum + val, 0);
682
- const xySum = values.reduce((sum, val, i) => sum + i * val, 0);
683
- const x2Sum = values.reduce((sum, _, i) => sum + i * i, 0);
717
+ let xSum = 0;
718
+ let ySum = 0;
719
+ let xySum = 0;
720
+ let x2Sum = 0;
721
+
722
+ for (let i = 0; i < n; i++) {
723
+ const val = values[i];
724
+ // Treat null/undefined as 0 to match original reduce behavior
725
+ const safeVal = typeof val === 'number' ? val : 0;
726
+ xSum += i;
727
+ ySum += safeVal;
728
+ xySum += i * safeVal;
729
+ x2Sum += i * i;
730
+ }
684
731
 
685
732
  const slope = (n * xySum - xSum * ySum) / (n * x2Sum - xSum * xSum);
686
733
  const intercept = (ySum - slope * xSum) / n;
@@ -711,11 +758,20 @@ export function useChartData(
711
758
  if (!enableRealTime) return undefined;
712
759
 
713
760
  const interval = setInterval(() => {
714
- setProcessedData(prev => [...prev]); // Trigger re-render for real-time updates
761
+ setProcessedData(prev => {
762
+ const currentSignature = getDatasetSignature(prev);
763
+ // Only trigger update if signature changed
764
+ if (currentSignature === lastDataSignature.current) {
765
+ return prev;
766
+ }
767
+ // Note: We do not update lastDataSignature.current here to avoid side effects in updater.
768
+ // It will be updated by the useEffect([processedData]) when the state update completes.
769
+ return [...prev];
770
+ });
715
771
  }, realTimeInterval);
716
772
 
717
773
  return () => clearInterval(interval);
718
- }, [enableRealTime, realTimeInterval]);
774
+ }, [enableRealTime, realTimeInterval, getDatasetSignature]);
719
775
 
720
776
  return {
721
777
  processedData,
@@ -831,11 +887,11 @@ export function useChartAccessibility(
831
887
  const datasetDescriptions = datasets
832
888
  .map((dataset, i) => {
833
889
  const dataCount = dataset.data?.length || 0;
834
- const values = dataset.data?.map(d => d.value).filter(v => typeof v === 'number') || [];
835
- const min = values.length > 0 ? Math.min(...values) : 0;
836
- const max = values.length > 0 ? Math.max(...values) : 0;
890
+ const { min, max, hasValid } = getDatasetBounds(dataset.data);
891
+ const minVal = hasValid ? min : 0;
892
+ const maxVal = hasValid ? max : 0;
837
893
 
838
- return `Dataset ${i + 1}: ${dataset.label}, ${dataCount} points, range ${min} to ${max}`;
894
+ return `Dataset ${i + 1}: ${dataset.label}, ${dataCount} points, range ${minVal} to ${maxVal}`;
839
895
  })
840
896
  .join('. ');
841
897
 
@@ -878,14 +934,13 @@ export function useChartPerformance(
878
934
 
879
935
  // Cache expensive scale calculations
880
936
  return datasets.map(dataset => {
881
- const values = dataset.data?.map(d => d.value).filter(v => typeof v === 'number') || [];
882
- const validValues = values.length > 0 ? values : [0];
937
+ const { min, max, hasValid } = getDatasetBounds(dataset.data);
883
938
 
884
939
  return {
885
940
  label: dataset.label,
886
941
  dataLength: dataset.data?.length || 0,
887
- minValue: Math.min(...validValues),
888
- maxValue: Math.max(...validValues),
942
+ minValue: hasValid ? min : 0,
943
+ maxValue: hasValid ? max : 0,
889
944
  };
890
945
  });
891
946
  }, [datasets, enableMemoization]);
@@ -937,3 +992,31 @@ export function useChartPerformance(
937
992
  getVisibleRange,
938
993
  };
939
994
  }
995
+
996
+ /**
997
+ * Helper to calculate min/max values from a dataset efficiently
998
+ * avoiding spread operator which can cause stack overflow on large arrays
999
+ */
1000
+ export function getDatasetBounds(data: ChartDataPoint[] | undefined): {
1001
+ min: number;
1002
+ max: number;
1003
+ hasValid: boolean;
1004
+ } {
1005
+ let min = Infinity;
1006
+ let max = -Infinity;
1007
+ let hasValid = false;
1008
+
1009
+ if (data && data.length > 0) {
1010
+ for (let i = 0; i < data.length; i++) {
1011
+ const point = data[i];
1012
+ if (point && typeof point.value === 'number') {
1013
+ const val = point.value;
1014
+ if (val < min) min = val;
1015
+ if (val > max) max = val;
1016
+ hasValid = true;
1017
+ }
1018
+ }
1019
+ }
1020
+
1021
+ return { min, max, hasValid };
1022
+ }
@@ -55,6 +55,7 @@ export function useHeroBackgroundSlider(
55
55
  const [isTransitioning, setIsTransitioning] = useState(false);
56
56
  const autoplayRef = useRef<NodeJS.Timeout | null>(null);
57
57
  const isPausedRef = useRef(false);
58
+ const callbackRef = useRef<() => void>();
58
59
 
59
60
  // Create refs for slide containers
60
61
  const slideRefs = useMemo(
@@ -124,6 +125,15 @@ export function useHeroBackgroundSlider(
124
125
  handleSlideTransition(nextIndex);
125
126
  }, [currentIndex, slides.length, loop, handleSlideTransition]);
126
127
 
128
+ // Update callbackRef whenever nextSlide or isTransitioning changes
129
+ useEffect(() => {
130
+ callbackRef.current = () => {
131
+ if (!isPausedRef.current && !isTransitioning) {
132
+ nextSlide();
133
+ }
134
+ };
135
+ }, [nextSlide, isTransitioning]);
136
+
127
137
  /**
128
138
  * Pause autoplay
129
139
  */
@@ -146,13 +156,13 @@ export function useHeroBackgroundSlider(
146
156
  // Restart autoplay
147
157
  if (!autoplayRef.current) {
148
158
  autoplayRef.current = setInterval(() => {
149
- if (!isPausedRef.current && !isTransitioning) {
150
- nextSlide();
159
+ if (callbackRef.current) {
160
+ callbackRef.current();
151
161
  }
152
162
  }, delay);
153
163
  }
154
164
  }
155
- }, [autoplay, slides.length, nextSlide, isTransitioning]);
165
+ }, [autoplay, slides.length]);
156
166
 
157
167
  // Autoplay effect
158
168
  useEffect(() => {
@@ -161,7 +171,6 @@ export function useHeroBackgroundSlider(
161
171
  }
162
172
 
163
173
  const delay = typeof autoplay === 'object' ? autoplay.delay : 3000;
164
- const pauseOnHover = typeof autoplay === 'object' ? autoplay.pauseOnHover : false;
165
174
 
166
175
  // Clear any existing interval
167
176
  if (autoplayRef.current) {
@@ -172,8 +181,8 @@ export function useHeroBackgroundSlider(
172
181
  // Start autoplay if not paused
173
182
  if (!isPausedRef.current) {
174
183
  autoplayRef.current = setInterval(() => {
175
- if (!isPausedRef.current && !isTransitioning) {
176
- nextSlide();
184
+ if (callbackRef.current) {
185
+ callbackRef.current();
177
186
  }
178
187
  }, delay);
179
188
  }
@@ -184,7 +193,7 @@ export function useHeroBackgroundSlider(
184
193
  autoplayRef.current = null;
185
194
  }
186
195
  };
187
- }, [autoplay, slides.length, nextSlide, isTransitioning]);
196
+ }, [autoplay, slides.length]);
188
197
 
189
198
  // Initialize first video if needed
190
199
  useEffect(() => {