@shohojdhara/atomix 0.3.4 → 0.3.5

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 (114) hide show
  1. package/dist/atomix.css +9 -10
  2. package/dist/atomix.css.map +1 -0
  3. package/dist/atomix.min.css +15108 -11
  4. package/dist/atomix.min.css.map +1 -0
  5. package/dist/charts.d.ts +1929 -0
  6. package/dist/charts.js +6482 -0
  7. package/dist/charts.js.map +1 -0
  8. package/dist/core.d.ts +1289 -0
  9. package/dist/core.js +3357 -0
  10. package/dist/core.js.map +1 -0
  11. package/dist/forms.d.ts +1085 -0
  12. package/dist/forms.js +2450 -0
  13. package/dist/forms.js.map +1 -0
  14. package/dist/heavy.d.ts +636 -0
  15. package/dist/heavy.js +4550 -0
  16. package/dist/heavy.js.map +1 -0
  17. package/dist/index.d.ts +5161 -4990
  18. package/dist/index.esm.js +1457 -784
  19. package/dist/index.esm.js.map +1 -1
  20. package/dist/index.js +1473 -790
  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/dist/layout.d.ts +300 -0
  25. package/dist/layout.js +336 -0
  26. package/dist/layout.js.map +1 -0
  27. package/dist/theme.d.ts +1992 -0
  28. package/dist/theme.js +5348 -0
  29. package/dist/theme.js.map +1 -0
  30. package/package.json +66 -20
  31. package/scripts/atomix-cli.js +544 -16
  32. package/scripts/cli/__tests__/cli-commands.test.js +204 -0
  33. package/scripts/cli/__tests__/utils.test.js +201 -0
  34. package/scripts/cli/__tests__/vitest.config.js +26 -0
  35. package/scripts/cli/interactive-init.js +1 -1
  36. package/scripts/cli/token-manager.js +32 -7
  37. package/scripts/cli/utils.js +347 -0
  38. package/src/components/Accordion/Accordion.tsx +5 -54
  39. package/src/components/Accordion/index.ts +1 -1
  40. package/src/components/Avatar/Avatar.tsx +3 -3
  41. package/src/components/Badge/Badge.tsx +3 -3
  42. package/src/components/Breadcrumb/Breadcrumb.tsx +3 -3
  43. package/src/components/Card/ElevationCard.tsx +1 -1
  44. package/src/components/Chart/AnimatedChart.tsx +19 -17
  45. package/src/components/Chart/AreaChart.tsx +5 -1
  46. package/src/components/Chart/BarChart.tsx +1 -0
  47. package/src/components/Chart/BubbleChart.tsx +6 -5
  48. package/src/components/Chart/ChartToolbar.tsx +1 -0
  49. package/src/components/Chart/FunnelChart.tsx +1 -1
  50. package/src/components/Chart/RadarChart.tsx +19 -12
  51. package/src/components/Chart/ScatterChart.tsx +3 -3
  52. package/src/components/Chart/TreemapChart.tsx +2 -1
  53. package/src/components/Chart/WaterfallChart.tsx +0 -1
  54. package/src/components/Chart/types.ts +12 -2
  55. package/src/components/Chart/utils.ts +4 -3
  56. package/src/components/DataTable/DataTable.tsx +3 -3
  57. package/src/components/Dropdown/Dropdown.tsx +12 -9
  58. package/src/components/Footer/FooterSection.tsx +3 -3
  59. package/src/components/Form/Checkbox.tsx +3 -3
  60. package/src/components/Form/Input.tsx +4 -2
  61. package/src/components/Form/Radio.tsx +3 -3
  62. package/src/components/Form/Select.tsx +3 -3
  63. package/src/components/Form/Textarea.tsx +4 -2
  64. package/src/components/List/List.stories.tsx +3 -3
  65. package/src/components/List/List.tsx +3 -3
  66. package/src/components/List/ListGroup.tsx +3 -1
  67. package/src/components/Modal/Modal.tsx +3 -3
  68. package/src/components/Navigation/Menu/MegaMenu.tsx +9 -3
  69. package/src/components/Navigation/Menu/Menu.tsx +9 -3
  70. package/src/components/Pagination/Pagination.tsx +6 -5
  71. package/src/components/PhotoViewer/PhotoViewerImage.tsx +2 -2
  72. package/src/components/Popover/Popover.tsx +4 -4
  73. package/src/components/Progress/Progress.tsx +6 -2
  74. package/src/components/Rating/Rating.tsx +5 -2
  75. package/src/components/Slider/Slider.tsx +10 -9
  76. package/src/components/Spinner/Spinner.tsx +3 -3
  77. package/src/components/Tabs/Tabs.tsx +3 -3
  78. package/src/components/Tooltip/Tooltip.tsx +3 -3
  79. package/src/components/index.ts +5 -2
  80. package/src/layouts/MasonryGrid/MasonryGrid.tsx +2 -2
  81. package/src/lib/composables/useChartPerformance.ts +102 -78
  82. package/src/lib/composables/useChartScale.ts +10 -0
  83. package/src/lib/composables/useHero.ts +9 -2
  84. package/src/lib/composables/useHeroBackgroundSlider.ts +5 -3
  85. package/src/lib/composables/useSideMenu.ts +1 -0
  86. package/src/lib/composables/useVideoPlayer.ts +3 -2
  87. package/src/lib/config/loader.ts +55 -13
  88. package/src/lib/hooks/index.ts +0 -1
  89. package/src/lib/hooks/useComponentCustomization.ts +10 -14
  90. package/src/lib/hooks/usePerformanceMonitor.ts +149 -0
  91. package/src/lib/patterns/index.ts +2 -2
  92. package/src/lib/patterns/slots.tsx +2 -2
  93. package/src/lib/theme/composeTheme.ts +1 -1
  94. package/src/lib/theme/core/ThemeEngine.ts +8 -0
  95. package/src/lib/theme/core/ThemeValidator.ts +5 -2
  96. package/src/lib/theme/devtools/Inspector.tsx +1 -1
  97. package/src/lib/theme/devtools/LiveEditor.tsx +11 -5
  98. package/src/lib/theme/generateCSSVariables.ts +1 -1
  99. package/src/lib/theme/i18n/rtl.ts +2 -1
  100. package/src/lib/theme/runtime/ThemeApplicator.ts +28 -11
  101. package/src/lib/theme/runtime/ThemeErrorBoundary.tsx +3 -3
  102. package/src/lib/theme/runtime/ThemeManager.ts +4 -0
  103. package/src/lib/theme-tools.ts +1 -1
  104. package/src/lib/types/components.ts +183 -34
  105. package/src/lib/types/partProps.ts +0 -16
  106. package/src/lib/utils/fontPreloader.ts +148 -0
  107. package/src/lib/utils/index.ts +11 -0
  108. package/src/lib/utils/memoryMonitor.ts +189 -0
  109. package/src/styles/01-settings/_settings.fonts.scss +2 -5
  110. package/src/styles/03-generic/_generated-root.css +22 -1
  111. package/src/styles/06-components/_components.navbar.scss +0 -6
  112. package/src/themes/themes.config.js +37 -4
  113. package/scripts/build-themes.js +0 -208
  114. package/src/components/AtomixGlass/atomixGLass.old.tsx +0 -1263
@@ -54,6 +54,9 @@ export const Rating = forwardRef<HTMLDivElement, RatingProps>(
54
54
  onChange,
55
55
  });
56
56
 
57
+ // Create forked ref - must be called unconditionally
58
+ const forkedRef = useForkRef(internalRef, ref);
59
+
57
60
  // Handle mouse enter on star with half-star support
58
61
  const handleMouseEnter = useCallback(
59
62
  (e: React.MouseEvent, starValue: number) => {
@@ -171,7 +174,7 @@ export const Rating = forwardRef<HTMLDivElement, RatingProps>(
171
174
  // If using vanilla JS, just render the container
172
175
  if (useVanillaJS) {
173
176
  return (
174
- <div className={ratingClasses} ref={useForkRef(internalRef, ref)} id={id} {...restProps}>
177
+ <div className={ratingClasses} ref={forkedRef} id={id} {...restProps}>
175
178
  {/* Stars will be generated by the vanilla JS implementation */}
176
179
  </div>
177
180
  );
@@ -268,7 +271,7 @@ export const Rating = forwardRef<HTMLDivElement, RatingProps>(
268
271
  const ratingContent = (
269
272
  <div
270
273
  className={ratingClasses}
271
- ref={useForkRef(internalRef, ref)}
274
+ ref={forkedRef}
272
275
  id={id}
273
276
  style={style}
274
277
  data-readonly={readOnly ? 'true' : 'false'}
@@ -25,16 +25,9 @@ export const Slider = forwardRef<HTMLDivElement, SliderProps>((props, ref) => {
25
25
  ...rest
26
26
  } = props;
27
27
 
28
- if (!slides || slides.length === 0) {
29
- return (
30
- <div className="c-slider c-slider--empty" style={{ height, width, ...style }}>
31
- <div className="c-slider__empty-message">No slides available</div>
32
- </div>
33
- );
34
- }
35
-
28
+ // Hooks must be called unconditionally - before early return
36
29
  const slider = useSlider({
37
- slides,
30
+ slides: slides || [],
38
31
  slidesToShow,
39
32
  spaceBetween,
40
33
  loop,
@@ -72,6 +65,14 @@ export const Slider = forwardRef<HTMLDivElement, SliderProps>((props, ref) => {
72
65
  return allSlides.length * (slideWidth + spaceBetween) - spaceBetween;
73
66
  }, [allSlides.length, slideWidth, spaceBetween]);
74
67
 
68
+ if (!slides || slides.length === 0) {
69
+ return (
70
+ <div className="c-slider c-slider--empty" style={{ height, width, ...style }}>
71
+ <div className="c-slider__empty-message">No slides available</div>
72
+ </div>
73
+ );
74
+ }
75
+
75
76
  const containerClasses = [
76
77
  'c-slider',
77
78
  direction === 'vertical' && 'c-slider--vertical',
@@ -1,10 +1,10 @@
1
- import React from 'react';
1
+ import React, { memo } from 'react';
2
2
  import { SpinnerProps } from '../../lib/types/components';
3
3
  import { useSpinner } from '../../lib/composables/useSpinner';
4
4
  import { SPINNER } from '../../lib/constants/components';
5
5
  import { AtomixGlass } from '../AtomixGlass/AtomixGlass';
6
6
 
7
- export const Spinner: React.FC<SpinnerProps> = ({
7
+ export const Spinner: React.FC<SpinnerProps> = memo(({
8
8
  size = 'md',
9
9
  variant = 'primary',
10
10
  fullscreen = false,
@@ -43,7 +43,7 @@ export const Spinner: React.FC<SpinnerProps> = ({
43
43
  }
44
44
 
45
45
  return spinnerContent;
46
- };
46
+ });
47
47
 
48
48
  export type { SpinnerProps };
49
49
 
@@ -1,4 +1,4 @@
1
- import React, { useState, ReactNode } from 'react';
1
+ import React, { useState, ReactNode, memo } from 'react';
2
2
  import { TAB } from '../../lib/constants/components';
3
3
  import { AtomixGlass } from '../AtomixGlass/AtomixGlass';
4
4
  import { AtomixGlassProps } from '../../lib/types/components';
@@ -61,7 +61,7 @@ export interface TabsProps {
61
61
  /**
62
62
  * Tabs component for switching between different content panels
63
63
  */
64
- export const Tabs: React.FC<TabsProps> = ({
64
+ export const Tabs: React.FC<TabsProps> = memo(({
65
65
  items,
66
66
  activeIndex = TAB.DEFAULTS.ACTIVE_INDEX,
67
67
  onTabChange,
@@ -137,7 +137,7 @@ export const Tabs: React.FC<TabsProps> = ({
137
137
  }
138
138
 
139
139
  return tabContent;
140
- };
140
+ });
141
141
 
142
142
  Tabs.displayName = 'Tabs';
143
143
 
@@ -1,4 +1,4 @@
1
- import React, { ReactNode } from 'react';
1
+ import React, { ReactNode, memo } from 'react';
2
2
  import { TOOLTIP } from '../../lib/constants/components';
3
3
  import { AtomixGlass } from '../AtomixGlass/AtomixGlass';
4
4
  import { AtomixGlassProps } from '../../lib/types/components';
@@ -60,7 +60,7 @@ export interface TooltipProps {
60
60
  glass?: AtomixGlassProps | boolean;
61
61
  }
62
62
 
63
- export const Tooltip: React.FC<TooltipProps> = ({
63
+ export const Tooltip: React.FC<TooltipProps> = memo(({
64
64
  content,
65
65
  children,
66
66
  position = TOOLTIP.DEFAULTS.POSITION,
@@ -162,7 +162,7 @@ export const Tooltip: React.FC<TooltipProps> = ({
162
162
  )}
163
163
  </div>
164
164
  );
165
- };
165
+ });
166
166
 
167
167
  Tooltip.displayName = 'Tooltip';
168
168
 
@@ -1,5 +1,6 @@
1
1
  export type { SliderProps, VideoPlayerProps } from '../lib/types/components';
2
- export { default as Accordion, type AccordionProps } from './Accordion/Accordion';
2
+ export { default as Accordion } from './Accordion/Accordion';
3
+ export type { AccordionProps } from '../lib/types/components';
3
4
  export { default as AtomixLogo, type AtomixLogoProps } from './AtomixLogo/AtomixLogo';
4
5
  export { default as AtomixGlass, type AtomixGlassProps } from './AtomixGlass';
5
6
  export { default as Avatar, type AvatarProps } from './Avatar/Avatar';
@@ -37,7 +38,7 @@ export {
37
38
  type BubbleDataPoint,
38
39
  type CandlestickChartProps,
39
40
  type CandlestickDataPoint,
40
- type ChartProps,
41
+ // ChartProps exported separately from lib/types/components to avoid conflict
41
42
  type DonutChartProps,
42
43
  type FunnelChartProps,
43
44
  type FunnelDataPoint,
@@ -56,6 +57,8 @@ export {
56
57
  type WaterfallChartProps,
57
58
  type WaterfallDataPoint,
58
59
  } from './Chart';
60
+ // Export ChartProps from lib/types/components to avoid duplicate export conflict
61
+ export type { ChartProps } from '../lib/types/components';
59
62
  export {
60
63
  default as ColorModeToggle,
61
64
  type ColorModeToggleProps,
@@ -374,7 +374,7 @@ export const MasonryGrid = forwardRef<HTMLDivElement, MasonryGridProps>(
374
374
  const position = positions[index];
375
375
  if (!position) {
376
376
  return (
377
- <div key={item.id} ref={item.ref} style={{ opacity: 0, position: 'absolute' }}>
377
+ <div key={item.id} ref={item.ref as React.LegacyRef<HTMLDivElement>} style={{ opacity: 0, position: 'absolute' }}>
378
378
  {item.element}
379
379
  </div>
380
380
  );
@@ -382,7 +382,7 @@ export const MasonryGrid = forwardRef<HTMLDivElement, MasonryGridProps>(
382
382
  return (
383
383
  <div
384
384
  key={item.id}
385
- ref={item.ref}
385
+ ref={item.ref as React.LegacyRef<HTMLDivElement>}
386
386
  className="o-masonry-grid__item"
387
387
  style={{
388
388
  position: 'absolute',
@@ -70,35 +70,33 @@ export function useChartPerformance() {
70
70
  height: number,
71
71
  padding: { top: number; right: number; bottom: number; left: number }
72
72
  ) => {
73
- return useMemo(() => {
74
- if (!datasets.length) return null;
75
-
76
- const innerWidth = width - padding.left - padding.right;
77
- const innerHeight = height - padding.top - padding.bottom;
78
-
79
- // Calculate bounds efficiently
80
- const allValues = datasets.flatMap(dataset => dataset.data.map(d => d.value));
81
- const minValue = Math.min(...allValues);
82
- const maxValue = Math.max(...allValues);
83
- const valueRange = maxValue - minValue;
84
-
85
- // Pre-calculate scale functions for better performance
86
- const xScale = (i: number, dataLength: number) =>
87
- padding.left + (i / (dataLength - 1)) * innerWidth;
88
-
89
- const yScale = (value: number) =>
90
- padding.top + innerHeight - ((value - minValue) / valueRange) * innerHeight;
91
-
92
- return {
93
- xScale,
94
- yScale,
95
- minValue,
96
- maxValue,
97
- valueRange,
98
- innerWidth,
99
- innerHeight,
100
- };
101
- }, [datasets, width, height, padding.top, padding.right, padding.bottom, padding.left]);
73
+ if (!datasets.length) return null;
74
+
75
+ const innerWidth = width - padding.left - padding.right;
76
+ const innerHeight = height - padding.top - padding.bottom;
77
+
78
+ // Calculate bounds efficiently
79
+ const allValues = datasets.flatMap(dataset => dataset.data.map(d => d.value));
80
+ const minValue = Math.min(...allValues);
81
+ const maxValue = Math.max(...allValues);
82
+ const valueRange = maxValue - minValue;
83
+
84
+ // Pre-calculate scale functions for better performance
85
+ const xScale = (i: number, dataLength: number) =>
86
+ padding.left + (i / (dataLength - 1)) * innerWidth;
87
+
88
+ const yScale = (value: number) =>
89
+ padding.top + innerHeight - ((value - minValue) / valueRange) * innerHeight;
90
+
91
+ return {
92
+ xScale,
93
+ yScale,
94
+ minValue,
95
+ maxValue,
96
+ valueRange,
97
+ innerWidth,
98
+ innerHeight,
99
+ };
102
100
  },
103
101
  []
104
102
  );
@@ -113,47 +111,65 @@ export function useChartPerformance() {
113
111
  viewportEnd: number,
114
112
  bufferSize: number = 50
115
113
  ) => {
116
- return useMemo(() => {
117
- if (data.length <= 1000) {
118
- // No virtualization needed for small datasets
119
- return {
120
- visibleData: data,
121
- startIndex: 0,
122
- endIndex: data.length - 1,
123
- isVirtualized: false,
124
- };
125
- }
126
-
127
- const start = Math.max(0, viewportStart - bufferSize);
128
- const end = Math.min(data.length - 1, viewportEnd + bufferSize);
129
-
114
+ if (data.length <= 1000) {
115
+ // No virtualization needed for small datasets
130
116
  return {
131
- visibleData: data.slice(start, end + 1),
132
- startIndex: start,
133
- endIndex: end,
134
- isVirtualized: true,
135
- totalLength: data.length,
117
+ visibleData: data,
118
+ startIndex: 0,
119
+ endIndex: data.length - 1,
120
+ isVirtualized: false,
136
121
  };
137
- }, [data, viewportStart, viewportEnd, bufferSize]);
122
+ }
123
+
124
+ const start = Math.max(0, viewportStart - bufferSize);
125
+ const end = Math.min(data.length - 1, viewportEnd + bufferSize);
126
+
127
+ return {
128
+ visibleData: data.slice(start, end + 1),
129
+ startIndex: start,
130
+ endIndex: end,
131
+ isVirtualized: true,
132
+ totalLength: data.length,
133
+ };
138
134
  },
139
135
  []
140
136
  );
141
137
 
142
138
  /**
143
139
  * Debounced data updates for real-time charts
140
+ * Returns a debounced function that maintains timeout state across calls
141
+ * Uses a closure to maintain state - each call to useDebouncedUpdates creates
142
+ * a new debounced function with its own persistent timeout state
144
143
  */
145
144
  const useDebouncedUpdates = useCallback((updateFunction: () => void, delay: number = 100) => {
146
- const timeoutRef = useRef<NodeJS.Timeout | null>(null);
147
-
148
- return useCallback(() => {
149
- if (timeoutRef.current) {
150
- clearTimeout(timeoutRef.current);
145
+ // Use a closure variable to maintain timeout state across multiple calls to the returned function
146
+ // This variable is created once when useDebouncedUpdates is called and persists
147
+ // across all invocations of the returned debounced function
148
+ let timeoutId: NodeJS.Timeout | null = null;
149
+
150
+ const debouncedFn: (() => void) & { cancel: () => void } = () => {
151
+ // Clear any existing timeout before setting a new one
152
+ if (timeoutId !== null) {
153
+ clearTimeout(timeoutId);
154
+ timeoutId = null;
151
155
  }
152
156
 
153
- timeoutRef.current = setTimeout(() => {
157
+ // Set new timeout and store the ID
158
+ timeoutId = setTimeout(() => {
154
159
  updateFunction();
160
+ timeoutId = null;
155
161
  }, delay);
156
- }, [updateFunction, delay]);
162
+ };
163
+
164
+ // Add cleanup method to cancel pending debounced calls
165
+ debouncedFn.cancel = () => {
166
+ if (timeoutId !== null) {
167
+ clearTimeout(timeoutId);
168
+ timeoutId = null;
169
+ }
170
+ };
171
+
172
+ return debouncedFn;
157
173
  }, []);
158
174
 
159
175
  /**
@@ -192,32 +208,40 @@ export function useChartPerformance() {
192
208
 
193
209
  /**
194
210
  * Optimized animation frame handling
211
+ * Returns animation control functions that maintain state across calls
212
+ * Uses closures to maintain state - each call to useAnimationFrame creates
213
+ * a new animation controller with its own persistent state
195
214
  */
196
215
  const useAnimationFrame = useCallback((callback: () => void) => {
197
- const requestRef = useRef<number | null>(null);
198
- const previousTimeRef = useRef<number | null>(null);
199
-
200
- const animate = useCallback(
201
- (time: number) => {
202
- if (previousTimeRef.current !== undefined) {
203
- const deltaTime = time - (previousTimeRef.current || 0);
204
- callback();
205
- }
206
- previousTimeRef.current = time;
207
- requestRef.current = requestAnimationFrame(animate);
208
- },
209
- [callback]
210
- );
211
-
212
- const startAnimation = useCallback(() => {
213
- requestRef.current = requestAnimationFrame(animate);
214
- }, [animate]);
216
+ // Use closure variables to maintain animation state across multiple calls
217
+ // These variables are created once when useAnimationFrame is called and persist
218
+ // across all invocations of the returned animation control functions
219
+ let requestId: number | null = null;
220
+ let previousTime: number | null = null;
221
+
222
+ const animate = (time: number) => {
223
+ if (previousTime !== null && previousTime !== undefined) {
224
+ const deltaTime = time - previousTime;
225
+ callback();
226
+ }
227
+ previousTime = time;
228
+ requestId = requestAnimationFrame(animate);
229
+ };
230
+
231
+ const startAnimation = () => {
232
+ // Only start if not already running
233
+ if (requestId === null) {
234
+ requestId = requestAnimationFrame(animate);
235
+ }
236
+ };
215
237
 
216
- const stopAnimation = useCallback(() => {
217
- if (requestRef.current) {
218
- cancelAnimationFrame(requestRef.current);
238
+ const stopAnimation = () => {
239
+ if (requestId !== null) {
240
+ cancelAnimationFrame(requestId);
241
+ requestId = null;
219
242
  }
220
- }, []);
243
+ previousTime = null;
244
+ };
221
245
 
222
246
  return { startAnimation, stopAnimation };
223
247
  }, []);
@@ -18,6 +18,11 @@ export function useChartScale(
18
18
  return {
19
19
  xScale: () => padding.left,
20
20
  yScale: () => padding.top + innerHeight,
21
+ minValue: 0,
22
+ maxValue: 0,
23
+ valueRange: 0,
24
+ innerWidth,
25
+ innerHeight,
21
26
  width,
22
27
  height,
23
28
  padding,
@@ -40,6 +45,11 @@ export function useChartScale(
40
45
  return {
41
46
  xScale,
42
47
  yScale,
48
+ minValue,
49
+ maxValue,
50
+ valueRange,
51
+ innerWidth,
52
+ innerHeight,
43
53
  width,
44
54
  height,
45
55
  padding,
@@ -97,10 +97,17 @@ export function useHero(initialProps?: Partial<HeroProps>): UseHeroResult {
97
97
  const hasBackgroundSlider = !!defaultProps.backgroundSlider;
98
98
 
99
99
  /**
100
- * Initialize background slider hook if enabled
100
+ * Initialize background slider hook - always call hook, conditionally use result
101
101
  */
102
+ const backgroundSliderResult = useHeroBackgroundSlider(
103
+ defaultProps.backgroundSlider || {
104
+ slides: [],
105
+ autoplay: { delay: 5000, pauseOnHover: true }
106
+ }
107
+ );
108
+
102
109
  const backgroundSlider = hasBackgroundSlider && defaultProps.backgroundSlider
103
- ? useHeroBackgroundSlider(defaultProps.backgroundSlider)
110
+ ? backgroundSliderResult
104
111
  : undefined;
105
112
 
106
113
  /**
@@ -89,8 +89,9 @@ export function useHeroBackgroundSlider(
89
89
 
90
90
  // Play video if it's a video slide
91
91
  const currentVideo = videoRefs[nextIndex]?.current;
92
- if (currentVideo && slides[nextIndex].type === 'video') {
93
- const videoOptions = slides[nextIndex].videoOptions || {};
92
+ const nextSlide = slides[nextIndex];
93
+ if (currentVideo && nextSlide && nextSlide.type === 'video') {
94
+ const videoOptions = nextSlide.videoOptions || {};
94
95
  if (videoOptions.autoplay !== false) {
95
96
  currentVideo.play().catch(() => {
96
97
  // Ignore autoplay errors
@@ -100,7 +101,8 @@ export function useHeroBackgroundSlider(
100
101
 
101
102
  // Pause previous video if it exists
102
103
  const prevVideo = videoRefs[currentIndex]?.current;
103
- if (prevVideo && slides[currentIndex].type === 'video') {
104
+ const currentSlide = slides[currentIndex];
105
+ if (prevVideo && currentSlide && currentSlide.type === 'video') {
104
106
  prevVideo.pause();
105
107
  }
106
108
 
@@ -60,6 +60,7 @@ export function useSideMenu(initialProps?: Partial<SideMenuProps>) {
60
60
  } else if (!shouldCollapse && wrapperRef.current) {
61
61
  wrapperRef.current.style.height = 'auto';
62
62
  }
63
+ return undefined;
63
64
  }, []); // Only run on mount
64
65
 
65
66
  // Handle responsive behavior - vertical collapse for both mobile and desktop
@@ -41,18 +41,19 @@ export function useVideoPlayer({
41
41
  const [currentQuality, setCurrentQuality] = useState<VideoQuality | null>(quality?.[0] || null);
42
42
  const [showControls, setShowControls] = useState(true);
43
43
 
44
- const controlsTimeoutRef = useRef<NodeJS.Timeout>(null);
44
+ const controlsTimeoutRef = useRef<NodeJS.Timeout | null>(null);
45
45
 
46
46
  const resetControlsTimeout = useCallback(() => {
47
47
  if (controlsTimeoutRef.current) {
48
48
  clearTimeout(controlsTimeoutRef.current);
49
49
  }
50
50
  setShowControls(true);
51
- controlsTimeoutRef.current = setTimeout(() => {
51
+ const timeout = setTimeout(() => {
52
52
  if (isPlaying) {
53
53
  setShowControls(false);
54
54
  }
55
55
  }, 3000);
56
+ controlsTimeoutRef.current = timeout;
56
57
  }, [isPlaying]);
57
58
 
58
59
  const play = useCallback(async () => {
@@ -76,26 +76,68 @@ export function loadAtomixConfig(
76
76
  * Resolve config path
77
77
  *
78
78
  * Finds atomix.config.ts in the project, checking common locations.
79
+ * Returns null in browser environments where file system access is not available.
80
+ *
81
+ * This function is designed to work in Node.js environments only.
82
+ * In browser builds, it will always return null without attempting to access Node.js modules.
83
+ *
84
+ * @internal This function uses Node.js modules and should not be called in browser environments.
79
85
  */
80
86
  export function resolveConfigPath(): string | null {
81
- if (typeof process === 'undefined' || !process.cwd) {
87
+ // Early return for browser environments - prevents any Node.js module access
88
+ // This check happens before any require() calls, preventing bundlers from analyzing them
89
+ if (typeof window !== 'undefined' || typeof process === 'undefined' || !process.cwd) {
82
90
  return null;
83
91
  }
84
92
 
85
- const fs = require('fs');
86
- const path = require('path');
87
-
88
- const cwd = process.cwd();
89
- const possiblePaths = [
90
- path.join(cwd, 'atomix.config.ts'),
91
- path.join(cwd, 'atomix.config.js'),
92
- path.join(cwd, 'atomix.config.mjs'),
93
- ];
93
+ // Only attempt to load Node.js modules in Node.js runtime
94
+ // Use a lazy-loading pattern that prevents static analysis by bundlers
95
+ try {
96
+ // Create a function that only executes in Node.js runtime
97
+ // Use string-based module names to prevent static analysis by bundlers
98
+ const loadNodeModules = () => {
99
+ // These requires are only executed at runtime in Node.js environments
100
+ // They are marked as external in Rollup config and should not be bundled
101
+ // Using string concatenation and computed property access to prevent static analysis
102
+ if (typeof require === 'undefined') {
103
+ return null;
104
+ }
105
+
106
+ // Use a try-catch wrapper to safely access require
107
+ try {
108
+ // Build module names dynamically to prevent static analysis
109
+ const moduleNames: [string, string] = ['f' + 's', 'p' + 'a' + 't' + 'h'];
110
+ // eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires
111
+ const fs = require(moduleNames[0]);
112
+ // eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires
113
+ const path = require(moduleNames[1]);
114
+ return { fs, path };
115
+ } catch {
116
+ return null;
117
+ }
118
+ };
94
119
 
95
- for (const configPath of possiblePaths) {
96
- if (fs.existsSync(configPath)) {
97
- return configPath;
120
+ const modules = loadNodeModules();
121
+ if (!modules) {
122
+ return null;
98
123
  }
124
+
125
+ const { fs, path } = modules;
126
+ const cwd = process.cwd();
127
+ const possiblePaths = [
128
+ path.join(cwd, 'atomix.config.ts'),
129
+ path.join(cwd, 'atomix.config.js'),
130
+ path.join(cwd, 'atomix.config.mjs'),
131
+ ];
132
+
133
+ for (const configPath of possiblePaths) {
134
+ if (fs.existsSync(configPath)) {
135
+ return configPath;
136
+ }
137
+ }
138
+ } catch (error) {
139
+ // Silently fail in browser environments or when modules are unavailable
140
+ return null;
99
141
  }
100
142
 
101
143
  return null;
@@ -8,7 +8,6 @@ export {
8
8
  useComponentCustomization,
9
9
  useComponentDefaultProps,
10
10
  useMergedProps,
11
- mergeClassNames,
12
11
  applyCSSVarsToStyle,
13
12
  } from './useComponentCustomization';
14
13
 
@@ -11,6 +11,7 @@ import type { ComponentPartsMap } from '../types/partProps';
11
11
  import type { ComponentCSSVariables } from '../constants/cssVariables';
12
12
  import { mergeCSSVars } from '../theme/cssVariableMapper';
13
13
  import { mergePartStyles } from '../types/partProps';
14
+ import { mergeClassNames } from '../utils/componentUtils';
14
15
 
15
16
  /**
16
17
  * Component names that support customization
@@ -22,7 +23,9 @@ export type ComponentName = keyof ComponentPartsMap;
22
23
  */
23
24
  export interface CustomizableComponentProps<T extends ComponentName> {
24
25
  /** CSS variable overrides */
25
- cssVars?: Partial<Record<ComponentCSSVariables[T], string | number>>;
26
+ cssVars?: T extends keyof ComponentCSSVariables
27
+ ? Partial<Record<ComponentCSSVariables[T], string | number>>
28
+ : Record<string, string | number>;
26
29
  /** Part-based styling */
27
30
  parts?: ComponentPartsMap[T];
28
31
  /** Additional className */
@@ -72,17 +75,17 @@ export function useComponentCustomization<T extends ComponentName>(
72
75
 
73
76
  // Merge CSS variables
74
77
  const cssVars = useMemo(() => {
75
- const themeVars = theme?.components?.[component]?.cssVars || {};
78
+ const themeVars = (theme as any)?.components?.[component]?.cssVars || {};
76
79
  const propVars = props.cssVars || {};
77
80
  return mergeCSSVars(themeVars, propVars as any);
78
81
  }, [theme, component, props.cssVars]);
79
82
 
80
83
  // Merge parts
81
84
  const parts = useMemo(() => {
82
- const themeParts = theme?.components?.[component]?.parts || {};
83
- const propParts = props.parts || {};
85
+ const themeParts = (theme as any)?.components?.[component]?.parts || {};
86
+ const propParts = (props.parts || {}) as Record<string, any>;
84
87
 
85
- const merged: any = {};
88
+ const merged: Record<string, any> = {};
86
89
  const allPartNames = new Set([
87
90
  ...Object.keys(themeParts),
88
91
  ...Object.keys(propParts),
@@ -100,7 +103,7 @@ export function useComponentCustomization<T extends ComponentName>(
100
103
 
101
104
  // Merge className
102
105
  const className = useMemo(() => {
103
- const themeClassName = theme?.components?.[component]?.className || '';
106
+ const themeClassName = (theme as any)?.components?.[component]?.className || '';
104
107
  const propClassName = props.className || '';
105
108
  return [themeClassName, propClassName].filter(Boolean).join(' ');
106
109
  }, [theme, component, props.className]);
@@ -136,7 +139,7 @@ export function useComponentDefaultProps<T extends ComponentName>(
136
139
  const { theme } = useTheme();
137
140
 
138
141
  return useMemo(() => {
139
- return theme?.components?.[component]?.defaultProps || {};
142
+ return (theme as any)?.components?.[component]?.defaultProps || {};
140
143
  }, [theme, component]);
141
144
  }
142
145
 
@@ -152,13 +155,6 @@ export function useMergedProps<T extends Record<string, any>>(
152
155
  }, [defaultProps, props]);
153
156
  }
154
157
 
155
- /**
156
- * Utility to create className from parts
157
- */
158
- export function mergeClassNames(...classNames: Array<string | undefined | null | false>): string {
159
- return classNames.filter(Boolean).join(' ');
160
- }
161
-
162
158
  /**
163
159
  * Utility to apply CSS variables to style object
164
160
  */