@shohojdhara/atomix 0.4.1 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/dist/atomix.css +9351 -9259
  2. package/dist/atomix.css.map +1 -1
  3. package/dist/atomix.min.css +4 -4
  4. package/dist/atomix.min.css.map +1 -1
  5. package/dist/charts.d.ts +12 -19
  6. package/dist/charts.js +555 -358
  7. package/dist/charts.js.map +1 -1
  8. package/dist/core.d.ts +21 -24
  9. package/dist/core.js +435 -265
  10. package/dist/core.js.map +1 -1
  11. package/dist/forms.d.ts +11 -18
  12. package/dist/forms.js +411 -257
  13. package/dist/forms.js.map +1 -1
  14. package/dist/heavy.d.ts +14 -21
  15. package/dist/heavy.js +409 -254
  16. package/dist/heavy.js.map +1 -1
  17. package/dist/index.d.ts +38 -41
  18. package/dist/index.esm.js +731 -487
  19. package/dist/index.esm.js.map +1 -1
  20. package/dist/index.js +733 -492
  21. package/dist/index.js.map +1 -1
  22. package/dist/index.min.js +1 -1
  23. package/dist/index.min.js.map +1 -1
  24. package/package.json +1 -1
  25. package/scripts/atomix-cli.js +34 -1
  26. package/src/components/AtomixGlass/AtomixGlass.tsx +82 -54
  27. package/src/components/AtomixGlass/AtomixGlassContainer.tsx +17 -18
  28. package/src/components/AtomixGlass/README.md +5 -5
  29. package/src/components/AtomixGlass/stories/Customization.stories.tsx +2 -2
  30. package/src/components/AtomixGlass/stories/Examples.stories.tsx +42 -42
  31. package/src/components/AtomixGlass/stories/Modes.stories.tsx +5 -5
  32. package/src/components/AtomixGlass/stories/Overview.stories.tsx +3 -3
  33. package/src/components/AtomixGlass/stories/Performance.stories.tsx +2 -2
  34. package/src/components/AtomixGlass/stories/Playground.stories.tsx +45 -45
  35. package/src/components/AtomixGlass/stories/Shaders.stories.tsx +3 -3
  36. package/src/components/Badge/Badge.stories.tsx +1 -1
  37. package/src/components/Badge/Badge.tsx +1 -1
  38. package/src/components/Breadcrumb/Breadcrumb.tsx +90 -77
  39. package/src/components/Breadcrumb/index.ts +2 -2
  40. package/src/components/Button/Button.stories.tsx +1 -1
  41. package/src/components/Button/Button.tsx +2 -1
  42. package/src/components/Button/README.md +2 -2
  43. package/src/components/Callout/Callout.test.tsx +3 -3
  44. package/src/components/Callout/Callout.tsx +2 -2
  45. package/src/components/Callout/README.md +2 -2
  46. package/src/components/Card/Card.tsx +31 -11
  47. package/src/components/Chart/Chart.stories.tsx +1 -1
  48. package/src/components/Chart/Chart.tsx +5 -5
  49. package/src/components/Chart/TreemapChart.tsx +37 -29
  50. package/src/components/DatePicker/readme.md +3 -3
  51. package/src/components/Dropdown/Dropdown.stories.tsx +1 -1
  52. package/src/components/Dropdown/Dropdown.tsx +276 -273
  53. package/src/components/EdgePanel/EdgePanel.stories.tsx +7 -7
  54. package/src/components/Footer/FooterLink.tsx +2 -2
  55. package/src/components/Form/Checkbox.stories.tsx +1 -1
  56. package/src/components/Form/Checkbox.tsx +1 -1
  57. package/src/components/Form/Input.stories.tsx +1 -1
  58. package/src/components/Form/Input.tsx +1 -1
  59. package/src/components/Form/Radio.stories.tsx +1 -1
  60. package/src/components/Form/Radio.tsx +1 -1
  61. package/src/components/Form/Select.stories.tsx +1 -1
  62. package/src/components/Form/Select.tsx +1 -1
  63. package/src/components/Form/Textarea.stories.tsx +1 -1
  64. package/src/components/Form/Textarea.tsx +1 -1
  65. package/src/components/Hero/Hero.stories.tsx +2 -2
  66. package/src/components/Hero/Hero.tsx +2 -2
  67. package/src/components/Messages/Messages.stories.tsx +1 -1
  68. package/src/components/Messages/Messages.tsx +2 -2
  69. package/src/components/Modal/Modal.stories.tsx +1 -1
  70. package/src/components/Navigation/Nav/Nav.stories.tsx +2 -2
  71. package/src/components/Navigation/Nav/Nav.tsx +1 -1
  72. package/src/components/Navigation/Nav/NavItem.tsx +6 -3
  73. package/src/components/Navigation/Navbar/Navbar.stories.tsx +3 -3
  74. package/src/components/Navigation/Navbar/Navbar.tsx +1 -1
  75. package/src/components/Navigation/SideMenu/SideMenu.stories.tsx +2 -2
  76. package/src/components/Navigation/SideMenu/SideMenu.tsx +1 -1
  77. package/src/components/Pagination/Pagination.stories.tsx +1 -1
  78. package/src/components/Pagination/Pagination.tsx +1 -1
  79. package/src/components/Popover/Popover.stories.tsx +1 -1
  80. package/src/components/Popover/Popover.tsx +1 -1
  81. package/src/components/Progress/Progress.tsx +1 -1
  82. package/src/components/Rating/Rating.stories.tsx +1 -1
  83. package/src/components/Rating/Rating.test.tsx +73 -0
  84. package/src/components/Rating/Rating.tsx +25 -37
  85. package/src/components/Spinner/Spinner.tsx +1 -1
  86. package/src/components/Steps/Steps.stories.tsx +1 -1
  87. package/src/components/Steps/Steps.tsx +2 -2
  88. package/src/components/Tabs/Tabs.stories.tsx +1 -1
  89. package/src/components/Tabs/Tabs.tsx +1 -1
  90. package/src/components/Todo/Todo.tsx +0 -1
  91. package/src/components/Toggle/Toggle.stories.tsx +1 -1
  92. package/src/components/Toggle/Toggle.tsx +1 -1
  93. package/src/components/Tooltip/Tooltip.stories.tsx +1 -1
  94. package/src/components/VideoPlayer/VideoPlayer.stories.tsx +2 -2
  95. package/src/lib/composables/__tests__/useAtomixGlassPerf.test.tsx +88 -0
  96. package/src/lib/composables/__tests__/useChart.test.ts +50 -0
  97. package/src/lib/composables/__tests__/useChart.test.tsx +139 -0
  98. package/src/lib/composables/__tests__/useHeroBackgroundSlider.test.tsx +59 -0
  99. package/src/lib/composables/__tests__/useSliderAutoplay.test.tsx +68 -0
  100. package/src/lib/composables/atomix-glass/useGlassBackgroundDetection.ts +329 -0
  101. package/src/lib/composables/atomix-glass/useGlassCornerRadius.ts +82 -0
  102. package/src/lib/composables/atomix-glass/useGlassMouseTracking.ts +153 -0
  103. package/src/lib/composables/atomix-glass/useGlassOverLight.ts +198 -0
  104. package/src/lib/composables/atomix-glass/useGlassSize.ts +117 -0
  105. package/src/lib/composables/atomix-glass/useGlassState.ts +112 -0
  106. package/src/lib/composables/atomix-glass/useGlassTransforms.ts +160 -0
  107. package/src/lib/composables/glass-styles.ts +302 -0
  108. package/src/lib/composables/index.ts +0 -4
  109. package/src/lib/composables/useAtomixGlass.ts +331 -522
  110. package/src/lib/composables/useAtomixGlassStyles.ts +307 -0
  111. package/src/lib/composables/useBarChart.ts +1 -1
  112. package/src/lib/composables/useBreadcrumb.ts +6 -6
  113. package/src/lib/composables/useChart.ts +104 -21
  114. package/src/lib/composables/useHeroBackgroundSlider.ts +16 -7
  115. package/src/lib/composables/useSlider.ts +66 -34
  116. package/src/lib/theme/devtools/CLI.ts +1 -1
  117. package/src/lib/theme/utils/__tests__/themeUtils.test.ts +213 -0
  118. package/src/lib/types/components.ts +18 -21
  119. package/src/lib/utils/__tests__/dom.test.ts +100 -0
  120. package/src/lib/utils/__tests__/fontPreloader.test.ts +102 -0
  121. package/src/styles/02-tools/_tools.breakpoints.scss +1 -1
  122. package/src/styles/02-tools/_tools.utility-api.scss +6 -6
  123. package/src/styles/06-components/_components.accordion.scss +0 -2
  124. package/src/styles/06-components/_components.chart.scss +0 -1
  125. package/src/styles/06-components/_components.dropdown.scss +0 -1
  126. package/src/styles/06-components/_components.edge-panel.scss +0 -2
  127. package/src/styles/06-components/_components.photoviewer.scss +0 -1
  128. package/src/styles/06-components/_components.river.scss +0 -1
  129. package/src/styles/06-components/_components.slider.scss +0 -3
  130. package/src/styles/99-utilities/_utilities.glass-fixes.scss +0 -1
  131. package/src/styles/99-utilities/_utilities.text.scss +1 -0
@@ -372,7 +372,7 @@ export const GlassCustom: Story = {
372
372
  blurAmount: 2,
373
373
  saturation: 200,
374
374
  aberrationIntensity: 0.8,
375
- cornerRadius: 12,
375
+ borderRadius: 12,
376
376
  } as GlassProps,
377
377
  },
378
378
  render: args => (
@@ -277,7 +277,7 @@ export const Tabs: TabsComponent = memo(
277
277
  blurAmount: 1,
278
278
  saturation: 160,
279
279
  aberrationIntensity: 0.5,
280
- cornerRadius: 8,
280
+ borderRadius: 8,
281
281
  mode: 'shader' as const,
282
282
  };
283
283
 
@@ -2,7 +2,6 @@ import React, { useState, useEffect } from 'react';
2
2
  import { TodoProps } from '../../lib/types/components';
3
3
  import { useTodo } from '../../lib/composables/useTodo';
4
4
  import { Icon } from '../Icon/Icon';
5
- import { TODO } from '../../lib/constants/components';
6
5
  import { generateUUID } from '../../lib/utils';
7
6
 
8
7
  export const Todo: React.FC<TodoProps> = ({
@@ -266,7 +266,7 @@ export const GlassCustom: Story = {
266
266
  blurAmount: 2,
267
267
  saturation: 200,
268
268
  aberrationIntensity: 0.8,
269
- cornerRadius: 12,
269
+ borderRadius: 12,
270
270
  children: <div>Custom glass</div>,
271
271
  },
272
272
  },
@@ -121,7 +121,7 @@ export const Toggle: React.FC<ToggleProps> = ({
121
121
  blurAmount: 1,
122
122
  saturation: 160,
123
123
  aberrationIntensity: 0.5,
124
- cornerRadius: 8,
124
+ borderRadius: 8,
125
125
  mode: 'shader' as const,
126
126
  };
127
127
 
@@ -349,7 +349,7 @@ export const GlassTooltipCustom: Story = {
349
349
  blurAmount: 2,
350
350
  saturation: 200,
351
351
  aberrationIntensity: 1,
352
- cornerRadius: 12,
352
+ borderRadius: 12,
353
353
  mode: 'polar',
354
354
  } as GlassProps,
355
355
  } as any,
@@ -813,7 +813,7 @@ export const GlassCustom: Story = {
813
813
  saturation: 180,
814
814
  aberrationIntensity: 2.5,
815
815
  elasticity: 0.4,
816
- cornerRadius: 20,
816
+ borderRadius: 20,
817
817
  mode: 'prominent',
818
818
  overLight: false,
819
819
  },
@@ -1071,7 +1071,7 @@ export const GlassWithInteractiveContent: Story = {
1071
1071
  saturation: 170,
1072
1072
  aberrationIntensity: 2,
1073
1073
  elasticity: 0.3,
1074
- cornerRadius: 15,
1074
+ borderRadius: 15,
1075
1075
  mode: 'standard',
1076
1076
  },
1077
1077
  glassOpacity: 0.6,
@@ -0,0 +1,88 @@
1
+ import React, { useRef } from 'react';
2
+ import { render, act } from '@testing-library/react';
3
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
4
+ import { useAtomixGlass } from '../useAtomixGlass';
5
+
6
+ describe('useAtomixGlass Performance', () => {
7
+ let renderCount = 0;
8
+ let glassRefOut: React.RefObject<HTMLDivElement> | null = null;
9
+
10
+ const TestComponent = () => {
11
+ renderCount++;
12
+ const glassRef = useRef<HTMLDivElement>(null);
13
+ const contentRef = useRef<HTMLDivElement>(null);
14
+ glassRefOut = glassRef;
15
+
16
+ useAtomixGlass({
17
+ glassRef,
18
+ contentRef,
19
+ elasticity: 0.1, // Ensure elasticity so transform changes
20
+ });
21
+
22
+ return (
23
+ <div ref={glassRef} style={{ width: 200, height: 100 }}>
24
+ <div ref={contentRef}>Content</div>
25
+ </div>
26
+ );
27
+ };
28
+
29
+ beforeEach(() => {
30
+ renderCount = 0;
31
+ glassRefOut = null;
32
+ // Mock getBoundingClientRect
33
+ Element.prototype.getBoundingClientRect = vi.fn(() => ({
34
+ width: 200,
35
+ height: 100,
36
+ top: 0,
37
+ left: 0,
38
+ bottom: 100,
39
+ right: 200,
40
+ x: 0,
41
+ y: 0,
42
+ toJSON: () => {},
43
+ }));
44
+ vi.useFakeTimers();
45
+ });
46
+
47
+ afterEach(() => {
48
+ vi.useRealTimers();
49
+ vi.restoreAllMocks();
50
+ });
51
+
52
+ it('does not re-render on mouse move but updates styles', async () => {
53
+ render(<TestComponent />);
54
+
55
+ // Run initial effects
56
+ await act(async () => {
57
+ vi.runAllTimers();
58
+ });
59
+
60
+ const countAfterSetup = renderCount;
61
+ const initialTransform = glassRefOut?.current?.style.transform;
62
+ console.log(`Initial transform: ${initialTransform}`);
63
+
64
+ // Simulate mouse move
65
+ const moveEvent = new MouseEvent('mousemove', {
66
+ clientX: 50,
67
+ clientY: 50,
68
+ bubbles: true,
69
+ });
70
+
71
+ await act(async () => {
72
+ document.dispatchEvent(moveEvent);
73
+ vi.runAllTimers();
74
+ });
75
+
76
+ console.log(`Render count: ${renderCount}`);
77
+
78
+ // Expect NO re-render
79
+ expect(renderCount).toBe(countAfterSetup);
80
+
81
+ // Expect style update
82
+ const finalTransform = glassRefOut?.current?.style.transform;
83
+ console.log(`Final transform: ${finalTransform}`);
84
+
85
+ expect(finalTransform).not.toBe(initialTransform);
86
+ expect(finalTransform).toContain('translate');
87
+ });
88
+ });
@@ -0,0 +1,50 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { getDatasetBounds } from '../useChart';
3
+ import { ChartDataPoint } from '../../types/components';
4
+
5
+ describe('useChart', () => {
6
+ describe('getDatasetBounds', () => {
7
+ it('should correctly calculate min and max for valid numeric data', () => {
8
+ const data: ChartDataPoint[] = [
9
+ { label: 'A', value: 10 },
10
+ { label: 'B', value: 5 },
11
+ { label: 'C', value: 20 },
12
+ ];
13
+ const result = getDatasetBounds(data);
14
+ expect(result).toEqual({ min: 5, max: 20, hasValid: true });
15
+ });
16
+
17
+ it('should handle empty data', () => {
18
+ const data: ChartDataPoint[] = [];
19
+ const result = getDatasetBounds(data);
20
+ expect(result).toEqual({ min: Infinity, max: -Infinity, hasValid: false });
21
+ });
22
+
23
+ it('should handle undefined data', () => {
24
+ const result = getDatasetBounds(undefined);
25
+ expect(result).toEqual({ min: Infinity, max: -Infinity, hasValid: false });
26
+ });
27
+
28
+ it('should ignore invalid values', () => {
29
+ const data: any[] = [
30
+ { label: 'A', value: 10 },
31
+ { label: 'B', value: 'invalid' },
32
+ { label: 'C', value: null },
33
+ { label: 'D', value: 30 },
34
+ ];
35
+ const result = getDatasetBounds(data as ChartDataPoint[]);
36
+ expect(result).toEqual({ min: 10, max: 30, hasValid: true });
37
+ });
38
+
39
+ it('should handle large datasets without stack overflow', () => {
40
+ const size = 150000;
41
+ const data: ChartDataPoint[] = Array.from({ length: size }, (_, i) => ({
42
+ label: `Point ${i}`,
43
+ value: i
44
+ }));
45
+
46
+ const result = getDatasetBounds(data);
47
+ expect(result).toEqual({ min: 0, max: size - 1, hasValid: true });
48
+ });
49
+ });
50
+ });
@@ -0,0 +1,139 @@
1
+ import { renderHook, act } from '@testing-library/react';
2
+ import { useChartData } from '../useChart';
3
+ import { ChartDataset } from '../../types/components';
4
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
5
+
6
+ describe('useChartData - Real-time Optimization', () => {
7
+ beforeEach(() => {
8
+ vi.useFakeTimers();
9
+ });
10
+
11
+ afterEach(() => {
12
+ vi.useRealTimers();
13
+ });
14
+
15
+ it('should not update processedData if data has not changed', () => {
16
+ const datasets: ChartDataset[] = [
17
+ {
18
+ label: 'Dataset 1',
19
+ data: [
20
+ { label: '1', value: 10 },
21
+ { label: '2', value: 20 },
22
+ ],
23
+ },
24
+ ];
25
+
26
+ const { result } = renderHook(() =>
27
+ useChartData(datasets, {
28
+ enableRealTime: true,
29
+ realTimeInterval: 1000,
30
+ enableDecimation: false,
31
+ })
32
+ );
33
+
34
+ const initialData = result.current.processedData;
35
+
36
+ // Advance time
37
+ act(() => {
38
+ vi.advanceTimersByTime(1100);
39
+ });
40
+
41
+ expect(result.current.processedData).toBe(initialData);
42
+ });
43
+
44
+ it('should update processedData if data length changes (mutation)', () => {
45
+ const datasets: ChartDataset[] = [
46
+ {
47
+ label: 'Dataset 1',
48
+ data: [
49
+ { label: '1', value: 10 },
50
+ { label: '2', value: 20 },
51
+ ],
52
+ },
53
+ ];
54
+
55
+ const { result } = renderHook(() =>
56
+ useChartData(datasets, {
57
+ enableRealTime: true,
58
+ realTimeInterval: 1000,
59
+ enableDecimation: false,
60
+ })
61
+ );
62
+
63
+ const initialData = result.current.processedData;
64
+
65
+ datasets[0].data.push({ label: '3', value: 30 });
66
+
67
+ act(() => {
68
+ vi.advanceTimersByTime(1100);
69
+ });
70
+
71
+ expect(result.current.processedData).not.toBe(initialData);
72
+ expect(result.current.processedData[0].data.length).toBe(3);
73
+ });
74
+
75
+ it('should update processedData if data value changes (mutation)', () => {
76
+ const datasets: ChartDataset[] = [
77
+ {
78
+ label: 'Dataset 1',
79
+ data: [
80
+ { label: '1', value: 10 },
81
+ { label: '2', value: 20 },
82
+ ],
83
+ },
84
+ ];
85
+
86
+ const { result } = renderHook(() =>
87
+ useChartData(datasets, {
88
+ enableRealTime: true,
89
+ realTimeInterval: 1000,
90
+ enableDecimation: false,
91
+ })
92
+ );
93
+
94
+ const initialData = result.current.processedData;
95
+
96
+ datasets[0].data[1].value = 25;
97
+
98
+ act(() => {
99
+ vi.advanceTimersByTime(1100);
100
+ });
101
+
102
+ expect(result.current.processedData).not.toBe(initialData);
103
+ expect(result.current.processedData[0].data[1].value).toBe(25);
104
+ });
105
+
106
+ it('should update processedData if historical data changes (mutation)', () => {
107
+ const datasets: ChartDataset[] = [
108
+ {
109
+ label: 'Dataset 1',
110
+ data: [
111
+ { label: '1', value: 10 },
112
+ { label: '2', value: 20 },
113
+ { label: '3', value: 30 },
114
+ ],
115
+ },
116
+ ];
117
+
118
+ const { result } = renderHook(() =>
119
+ useChartData(datasets, {
120
+ enableRealTime: true,
121
+ realTimeInterval: 1000,
122
+ enableDecimation: false,
123
+ })
124
+ );
125
+
126
+ const initialData = result.current.processedData;
127
+
128
+ // Mutate historical data (index 0)
129
+ datasets[0].data[0].value = 15;
130
+
131
+ act(() => {
132
+ vi.advanceTimersByTime(1100);
133
+ });
134
+
135
+ // Should update
136
+ expect(result.current.processedData).not.toBe(initialData);
137
+ expect(result.current.processedData[0].data[0].value).toBe(15);
138
+ });
139
+ });
@@ -0,0 +1,59 @@
1
+ import { renderHook, act } from '@testing-library/react';
2
+ import { useHeroBackgroundSlider } from '../useHeroBackgroundSlider';
3
+ import { HeroBackgroundSliderConfig } from '../../types/components';
4
+ import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
5
+
6
+ describe('useHeroBackgroundSlider Performance', () => {
7
+ beforeEach(() => {
8
+ vi.useFakeTimers();
9
+ });
10
+
11
+ afterEach(() => {
12
+ vi.useRealTimers();
13
+ vi.restoreAllMocks();
14
+ });
15
+
16
+ it('should not reset interval frequently during autoplay', () => {
17
+ const config: HeroBackgroundSliderConfig = {
18
+ slides: [
19
+ { type: 'image', src: 'slide1.jpg' },
20
+ { type: 'image', src: 'slide2.jpg' },
21
+ { type: 'image', src: 'slide3.jpg' },
22
+ ],
23
+ autoplay: {
24
+ delay: 3000,
25
+ },
26
+ transitionDuration: 1000,
27
+ };
28
+
29
+ const clearIntervalSpy = vi.spyOn(global, 'clearInterval');
30
+
31
+ const { result } = renderHook(() => useHeroBackgroundSlider(config));
32
+
33
+ // Reset call count after initial render
34
+ clearIntervalSpy.mockClear();
35
+
36
+ // 1st Transition
37
+ act(() => {
38
+ vi.advanceTimersByTime(3000);
39
+ });
40
+
41
+ act(() => {
42
+ vi.advanceTimersByTime(1000);
43
+ });
44
+
45
+ // 2nd Transition
46
+ act(() => {
47
+ vi.advanceTimersByTime(3000);
48
+ });
49
+
50
+ act(() => {
51
+ vi.advanceTimersByTime(1000);
52
+ });
53
+
54
+ console.log(`clearInterval calls: ${clearIntervalSpy.mock.calls.length}`);
55
+
56
+ // With optimization, the interval should persist and not be cleared during transitions
57
+ expect(clearIntervalSpy).toHaveBeenCalledTimes(0);
58
+ });
59
+ });
@@ -0,0 +1,68 @@
1
+ import { renderHook, act } from '@testing-library/react';
2
+ import { useSlider } from '../useSlider';
3
+ import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
4
+ import { SliderSlide } from '../../types/components';
5
+
6
+ describe('useSlider Autoplay Optimization', () => {
7
+ const slides: SliderSlide[] = [
8
+ { id: '1', content: 'Slide 1' },
9
+ { id: '2', content: 'Slide 2' },
10
+ { id: '3', content: 'Slide 3' },
11
+ ];
12
+
13
+ beforeEach(() => {
14
+ vi.useFakeTimers();
15
+ });
16
+
17
+ afterEach(() => {
18
+ vi.useRealTimers();
19
+ vi.restoreAllMocks();
20
+ });
21
+
22
+ it('should not reset interval when transitioning state changes', () => {
23
+ const setIntervalSpy = vi.spyOn(global, 'setInterval');
24
+ const clearIntervalSpy = vi.spyOn(global, 'clearInterval');
25
+
26
+ const autoplayConfig = { delay: 1000 };
27
+
28
+ const { result } = renderHook(() =>
29
+ useSlider({
30
+ slides,
31
+ autoplay: autoplayConfig,
32
+ speed: 300,
33
+ slidesToShow: 1,
34
+ })
35
+ );
36
+
37
+ // Initial render should set interval.
38
+ // Note: It might be called more than once due to initial state updates (like internalIndex setting)
39
+ // causing re-renders if dependencies are unstable, though refs should be stable.
40
+ // However, strictly, we want to verify it doesn't reset DURING autoplay cycle.
41
+
42
+ // Let's clear mocks after initial render is done.
43
+ setIntervalSpy.mockClear();
44
+ clearIntervalSpy.mockClear();
45
+
46
+ // Fast-forward to trigger autoplay
47
+ act(() => {
48
+ vi.advanceTimersByTime(1000);
49
+ });
50
+
51
+ // Check if transition started
52
+ expect(result.current.transitioning).toBe(true);
53
+
54
+ // At this point, in the buggy version, transitioning becoming true triggers the effect cleanup and re-setup.
55
+ // So we expect setInterval/clearInterval to have been called.
56
+
57
+ // Let's see what happens after transition ends
58
+ act(() => {
59
+ vi.advanceTimersByTime(300); // speed is 300
60
+ });
61
+
62
+ expect(result.current.transitioning).toBe(false);
63
+
64
+ // In the optimized version, these should be 0 because the interval persists.
65
+ expect(setIntervalSpy).toHaveBeenCalledTimes(0);
66
+ expect(clearIntervalSpy).toHaveBeenCalledTimes(0);
67
+ });
68
+ });