@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
@@ -7,9 +7,7 @@ import React, {
7
7
  useRef,
8
8
  useState,
9
9
  useCallback,
10
- useMemo,
11
10
  Children,
12
- cloneElement,
13
11
  isValidElement,
14
12
  } from 'react';
15
13
  // Import styles for scoped CSS modules
@@ -122,7 +120,7 @@ export const MasonryGrid = forwardRef<HTMLDivElement, MasonryGridProps>(
122
120
  // === REFS & STATE ===
123
121
  const [columns, setColumns] = useState(xs);
124
122
  const [positions, setPositions] = useState<ItemPosition[]>([]);
125
- const [layoutComplete, setLayoutComplete] = useState(false);
123
+ const [, setLayoutComplete] = useState(false);
126
124
  const [loadingImages, setLoadingImages] = useState(false);
127
125
  const containerRef = useRef<HTMLDivElement>(null);
128
126
  const columnHeights = useRef<number[]>([]);
@@ -178,6 +176,32 @@ export const MasonryGrid = forwardRef<HTMLDivElement, MasonryGridProps>(
178
176
  setItems(newItems);
179
177
  }, [children]);
180
178
 
179
+ // === MANAGE ITEM LAYOUT ===
180
+ const calculateLayout = useCallback(() => {
181
+ if (!containerRef.current || items.length === 0) return;
182
+ const containerWidth = containerRef.current.offsetWidth;
183
+ const colWidth = (containerWidth - gap * (columns - 1)) / columns;
184
+ columnHeights.current = Array(columns).fill(0);
185
+ const newPositions: ItemPosition[] = [];
186
+ items.forEach((item, index) => {
187
+ if (item.ref.current) {
188
+ // Find the shortest column
189
+ const shortestCol = columnHeights.current.indexOf(Math.min(...columnHeights.current));
190
+ const left = shortestCol * (colWidth + gap);
191
+ const top = columnHeights.current[shortestCol] ?? 0;
192
+ const height = item.ref.current.offsetHeight;
193
+ columnHeights.current[shortestCol] = top + height + gap;
194
+ newPositions[index] = {
195
+ left,
196
+ top,
197
+ width: colWidth,
198
+ height,
199
+ };
200
+ }
201
+ });
202
+ setPositions(newPositions);
203
+ }, [items, columns, gap]);
204
+
181
205
  // === TRACK & MANAGE IMAGES ===
182
206
 
183
207
  const handleImageLoad = useCallback(
@@ -195,30 +219,28 @@ export const MasonryGrid = forwardRef<HTMLDivElement, MasonryGridProps>(
195
219
  itemElement.classList.remove('o-masonry-grid__item-loading');
196
220
  }
197
221
  }
198
- // Ensure layout is recalculated after DOM paints the item image (prevents overlap on slow/late image loads)
199
- requestAnimationFrame(() => {
200
- requestAnimationFrame(() => {
222
+ // Schedule layout recalculation after next paint to prevent overlap
223
+ const scheduleLayoutUpdate = () => {
224
+ const frameId = requestAnimationFrame(() => {
225
+ onImageLoad?.(imagesLoadedCount.current, totalImagesCount.current);
201
226
  calculateLayout();
202
227
  });
203
- });
204
- onImageLoad?.(imagesLoadedCount.current, totalImagesCount.current);
228
+ return () => cancelAnimationFrame(frameId);
229
+ };
230
+
231
+ // Clean up previous scheduled updates
232
+ const cleanup = scheduleLayoutUpdate();
205
233
 
206
234
  // If all images have loaded, update loading state and complete layout
207
235
  if (imagesLoadedCount.current >= totalImagesCount.current && totalImagesCount.current > 0) {
208
236
  setLayoutComplete(true);
209
- setLoadingImages(false); // This ensures the loading class is removed *immediately* after images load
210
- // Force a double requestAnimationFrame for final layout calculation after all images are loaded (guarantees DOM paint)
211
- requestAnimationFrame(() => {
212
- requestAnimationFrame(() => {
213
- calculateLayout();
214
- // As a failsafe, if still present for some render lag, force another setLoadingImages(false)
215
- setLoadingImages(false);
216
- });
217
- });
237
+ setLoadingImages(false);
238
+ setTimeout(() => cleanup(), 0); // Clean up after current execution
239
+ scheduleLayoutUpdate();
218
240
  onLayoutComplete?.();
219
241
  }
220
242
  },
221
- [onImageLoad, onLayoutComplete, imagesLoaded]
243
+ [onImageLoad, onLayoutComplete, imagesLoaded, calculateLayout]
222
244
  );
223
245
 
224
246
  const trackImages = useCallback(() => {
@@ -262,40 +284,28 @@ export const MasonryGrid = forwardRef<HTMLDivElement, MasonryGridProps>(
262
284
  };
263
285
  }, [imagesLoaded, handleImageLoad, onLayoutComplete]);
264
286
 
265
- // === MANAGE ITEM LAYOUT ===
266
- const calculateLayout = useCallback(() => {
267
- if (!containerRef.current || items.length === 0) return;
268
- const containerWidth = containerRef.current.offsetWidth;
269
- const colWidth = (containerWidth - gap * (columns - 1)) / columns;
270
- columnHeights.current = Array(columns).fill(0);
271
- const newPositions: ItemPosition[] = [];
272
- items.forEach((item, index) => {
273
- if (item.ref.current) {
274
- // Find the shortest column
275
- const shortestCol = columnHeights.current.indexOf(Math.min(...columnHeights.current));
276
- const left = shortestCol * (colWidth + gap);
277
- const top = columnHeights.current[shortestCol] ?? 0;
278
- const height = item.ref.current.offsetHeight;
279
- columnHeights.current[shortestCol] = top + height + gap;
280
- newPositions[index] = {
281
- left,
282
- top,
283
- width: colWidth,
284
- height,
285
- };
286
- }
287
- });
288
- setPositions(newPositions);
289
- }, [items, columns, gap]);
290
-
291
287
  // === OBSERVE CONTAINER RESIZE ===
292
288
  useEffect(() => {
293
289
  if (!containerRef.current) return undefined;
290
+
294
291
  let animationFrame: ReturnType<typeof requestAnimationFrame> | null = null;
295
- const observer = new ResizeObserver(() => {
296
- if (animationFrame) cancelAnimationFrame(animationFrame);
297
- animationFrame = requestAnimationFrame(() => calculateLayout());
292
+ let lastWidth = 0;
293
+
294
+ const observer = new ResizeObserver((entries) => {
295
+ const entry = entries[0];
296
+ if (!entry) return;
297
+ const currentWidth = entry.contentRect.width;
298
+
299
+ // Only recalculate if width actually changed (prevents excessive calculations)
300
+ if (Math.abs(currentWidth - lastWidth) > 1) {
301
+ if (animationFrame) cancelAnimationFrame(animationFrame);
302
+ animationFrame = requestAnimationFrame(() => {
303
+ calculateLayout();
304
+ lastWidth = currentWidth;
305
+ });
306
+ }
298
307
  });
308
+
299
309
  observer.observe(containerRef.current);
300
310
  return () => {
301
311
  observer.disconnect();
@@ -317,26 +327,28 @@ export const MasonryGrid = forwardRef<HTMLDivElement, MasonryGridProps>(
317
327
  // Only reset layoutComplete when items or columns change
318
328
  }, [items, columns, calculateLayout, imagesLoaded, trackImages]);
319
329
 
320
- // === NEW: Add ResizeObservers to all grid items for bulletproof image+content measurement ===
330
+ // === ADD RESIZEOBSERVERS TO GRID ITEMS FOR DYNAMIC CONTENT MEASUREMENT ===
321
331
  React.useEffect(() => {
322
- // Clean up old observers if items ever change
323
332
  const observers: ResizeObserver[] = [];
333
+ let animationFrame: ReturnType<typeof requestAnimationFrame> | null = null;
334
+
335
+ // Debounced layout calculation for item resize events
336
+ const debouncedCalculateLayout = () => {
337
+ if (animationFrame) cancelAnimationFrame(animationFrame);
338
+ animationFrame = requestAnimationFrame(calculateLayout);
339
+ };
340
+
324
341
  items.forEach(item => {
325
342
  if (item.ref.current) {
326
- const obs = new ResizeObserver(() => {
327
- // Double rAF: ensures layout only runs after DOM/paint/async renders
328
- requestAnimationFrame(() => {
329
- requestAnimationFrame(() => {
330
- calculateLayout();
331
- });
332
- });
333
- });
343
+ const obs = new ResizeObserver(debouncedCalculateLayout);
334
344
  obs.observe(item.ref.current);
335
345
  observers.push(obs);
336
346
  }
337
347
  });
348
+
338
349
  return () => {
339
350
  observers.forEach(obs => obs.disconnect());
351
+ if (animationFrame) cancelAnimationFrame(animationFrame);
340
352
  };
341
353
  }, [items, calculateLayout]);
342
354
 
@@ -376,7 +388,7 @@ export const MasonryGrid = forwardRef<HTMLDivElement, MasonryGridProps>(
376
388
  return (
377
389
  <div
378
390
  key={item.id}
379
- ref={item.ref as React.LegacyRef<HTMLDivElement>}
391
+ ref={item.ref as React.RefObject<HTMLDivElement>}
380
392
  style={{ opacity: 0, position: 'absolute' }}
381
393
  >
382
394
  {item.element}
@@ -386,7 +398,7 @@ export const MasonryGrid = forwardRef<HTMLDivElement, MasonryGridProps>(
386
398
  return (
387
399
  <div
388
400
  key={item.id}
389
- ref={item.ref as React.LegacyRef<HTMLDivElement>}
401
+ ref={item.ref as React.RefObject<HTMLDivElement>}
390
402
  className="o-masonry-grid__item"
391
403
  style={{
392
404
  position: 'absolute',
@@ -6,7 +6,7 @@ import type { GlassSize } from '../../types/components';
6
6
  const { CONSTANTS } = ATOMIX_GLASS;
7
7
 
8
8
  interface UseGlassSizeProps {
9
- glassRef: React.RefObject<HTMLDivElement>;
9
+ glassRef: React.RefObject<HTMLDivElement | null>;
10
10
  effectiveBorderRadius: number;
11
11
  cachedRectRef?: React.MutableRefObject<DOMRect | null>;
12
12
  }
@@ -1,5 +1,5 @@
1
1
  import { AccordionProps, AccordionState, IconPosition, ElementRefs } from '../types/components';
2
- import { useState, useEffect, useRef } from 'react';
2
+ import { useState, useEffect, useRef, useCallback } from 'react';
3
3
  import { ACCORDION } from '../constants/components';
4
4
 
5
5
  /**
@@ -72,20 +72,20 @@ export function useAccordion(
72
72
  /**
73
73
  * Update panel height based on content
74
74
  */
75
- const updatePanelHeight = (): void => {
75
+ const updatePanelHeight = useCallback((): void => {
76
76
  if (contentRef.current && panelRef.current) {
77
77
  const height = isOpen ? `${contentRef.current.clientHeight}px` : '0px';
78
78
  panelRef.current.style.setProperty(ACCORDION.CSS_VARS.PANEL_HEIGHT, height);
79
79
  setPanelHeight(height);
80
80
  }
81
- };
81
+ }, [isOpen]);
82
82
 
83
83
  /**
84
84
  * Effect to update panel height when open state changes
85
85
  */
86
86
  useEffect(() => {
87
87
  updatePanelHeight();
88
- }, [isOpen]);
88
+ }, [isOpen, updatePanelHeight]);
89
89
 
90
90
  /**
91
91
  * Effect to handle window resize and update panel height
@@ -99,7 +99,7 @@ export function useAccordion(
99
99
 
100
100
  window.addEventListener('resize', handleResize);
101
101
  return () => window.removeEventListener('resize', handleResize);
102
- }, [isOpen]);
102
+ }, [isOpen, updatePanelHeight]);
103
103
 
104
104
  /**
105
105
  * Generate accordion class names based on state
@@ -153,9 +153,9 @@ const setCachedBackgroundDetection = (
153
153
  };
154
154
 
155
155
  interface UseAtomixGlassOptions extends Omit<AtomixGlassProps, 'children'> {
156
- glassRef: React.RefObject<HTMLDivElement>;
157
- contentRef: React.RefObject<HTMLDivElement>;
158
- wrapperRef?: React.RefObject<HTMLDivElement>;
156
+ glassRef: React.RefObject<HTMLDivElement | null>;
157
+ contentRef: React.RefObject<HTMLDivElement | null>;
158
+ wrapperRef?: React.RefObject<HTMLDivElement | null>;
159
159
  children?: React.ReactNode;
160
160
  isFixedOrSticky?: boolean;
161
161
  // Phase 1: Time-Based Animation System
@@ -325,7 +325,7 @@ export function useBarChart(datasets: ChartDataset[], options: BarChartOptions =
325
325
  }
326
326
  return value.toString();
327
327
  },
328
- [options.valueFormatter]
328
+ [options]
329
329
  );
330
330
 
331
331
  // Calculate data label position
@@ -352,7 +352,7 @@ export function useBarChart(datasets: ChartDataset[], options: BarChartOptions =
352
352
  };
353
353
  }
354
354
  },
355
- [options.dataLabelPosition]
355
+ [options]
356
356
  );
357
357
 
358
358
  return {
@@ -104,9 +104,10 @@ export function useChart(initialProps?: Partial<ChartProps>) {
104
104
 
105
105
  // Cleanup animation frame on unmount
106
106
  useEffect(() => {
107
+ const currentRef = animationFrameRef.current;
107
108
  return () => {
108
- if (animationFrameRef.current) {
109
- cancelAnimationFrame(animationFrameRef.current);
109
+ if (currentRef) {
110
+ cancelAnimationFrame(currentRef);
110
111
  }
111
112
  };
112
113
  }, []);
@@ -148,17 +148,17 @@ export function useChartToolbar(
148
148
  return { ...getChartDefaults(), ...defaults };
149
149
  }, [getChartDefaults, defaults]);
150
150
 
151
- const enhancedHandlers = {
152
- onRefresh: useCallback(() => {
153
- setState(prev => ({ ...prev, isRefreshing: true }));
154
- handlers.onRefresh?.();
155
- setTimeout(() => {
156
- setState(prev => ({ ...prev, isRefreshing: false }));
157
- }, 1000);
158
- }, [handlers.onRefresh]),
159
-
160
- onExport: useCallback(
161
- async (format: string) => {
151
+ const enhancedHandlers = useMemo(
152
+ () => ({
153
+ onRefresh: () => {
154
+ setState(prev => ({ ...prev, isRefreshing: true }));
155
+ handlers.onRefresh?.();
156
+ setTimeout(() => {
157
+ setState(prev => ({ ...prev, isRefreshing: false }));
158
+ }, 1000);
159
+ },
160
+
161
+ onExport: async (format: string) => {
162
162
  setState(prev => ({ ...prev, isExporting: true }));
163
163
  try {
164
164
  await handlers.onExport?.(format);
@@ -166,83 +166,65 @@ export function useChartToolbar(
166
166
  setState(prev => ({ ...prev, isExporting: false }));
167
167
  }
168
168
  },
169
- [handlers.onExport]
170
- ),
171
169
 
172
- onFullscreen: useCallback(
173
- (isFullscreen: boolean) => {
170
+ onFullscreen: (isFullscreen: boolean) => {
174
171
  setState(prev => ({ ...prev, isFullscreen }));
175
172
  handlers.onFullscreen?.(isFullscreen);
176
173
  },
177
- [handlers.onFullscreen]
178
- ),
179
-
180
- onZoomIn: useCallback(() => {
181
- setState(prev => ({ ...prev, zoomLevel: Math.min(prev.zoomLevel * 1.2, 5) }));
182
- handlers.onZoomIn?.();
183
- }, [handlers.onZoomIn]),
184
-
185
- onZoomOut: useCallback(() => {
186
- setState(prev => ({ ...prev, zoomLevel: Math.max(prev.zoomLevel / 1.2, 0.2) }));
187
- handlers.onZoomOut?.();
188
- }, [handlers.onZoomOut]),
189
-
190
- onZoomReset: useCallback(() => {
191
- setState(prev => ({ ...prev, zoomLevel: 1 }));
192
- handlers.onZoomReset?.();
193
- }, [handlers.onZoomReset]),
194
-
195
- onPanToggle: useCallback(
196
- (enabled: boolean) => {
174
+
175
+ onZoomIn: () => {
176
+ setState(prev => ({ ...prev, zoomLevel: Math.min(prev.zoomLevel * 1.2, 5) }));
177
+ handlers.onZoomIn?.();
178
+ },
179
+
180
+ onZoomOut: () => {
181
+ setState(prev => ({ ...prev, zoomLevel: Math.max(prev.zoomLevel / 1.2, 0.2) }));
182
+ handlers.onZoomOut?.();
183
+ },
184
+
185
+ onZoomReset: () => {
186
+ setState(prev => ({ ...prev, zoomLevel: 1 }));
187
+ handlers.onZoomReset?.();
188
+ },
189
+
190
+ onPanToggle: (enabled: boolean) => {
197
191
  setState(prev => ({ ...prev, panEnabled: enabled }));
198
192
  handlers.onPanToggle?.(enabled);
199
193
  },
200
- [handlers.onPanToggle]
201
- ),
202
-
203
- onReset: useCallback(() => {
204
- setState(prev => ({
205
- ...prev,
206
- zoomLevel: 1,
207
- panEnabled: false,
208
- }));
209
- handlers.onReset?.();
210
- }, [handlers.onReset]),
211
-
212
- onGridToggle: useCallback(
213
- (show: boolean) => {
194
+
195
+ onReset: () => {
196
+ setState(prev => ({
197
+ ...prev,
198
+ zoomLevel: 1,
199
+ panEnabled: false,
200
+ }));
201
+ handlers.onReset?.();
202
+ },
203
+
204
+ onGridToggle: (show: boolean) => {
214
205
  setState(prev => ({ ...prev, showGrid: show }));
215
206
  handlers.onGridToggle?.(show);
216
207
  },
217
- [handlers.onGridToggle]
218
- ),
219
208
 
220
- onLegendToggle: useCallback(
221
- (show: boolean) => {
209
+ onLegendToggle: (show: boolean) => {
222
210
  setState(prev => ({ ...prev, showLegend: show }));
223
211
  handlers.onLegendToggle?.(show);
224
212
  },
225
- [handlers.onLegendToggle]
226
- ),
227
213
 
228
- onTooltipsToggle: useCallback(
229
- (show: boolean) => {
214
+ onTooltipsToggle: (show: boolean) => {
230
215
  setState(prev => ({ ...prev, showTooltips: show }));
231
216
  handlers.onTooltipsToggle?.(show);
232
217
  },
233
- [handlers.onTooltipsToggle]
234
- ),
235
218
 
236
- onAnimationsToggle: useCallback(
237
- (enabled: boolean) => {
219
+ onAnimationsToggle: (enabled: boolean) => {
238
220
  setState(prev => ({ ...prev, animationsEnabled: enabled }));
239
221
  handlers.onAnimationsToggle?.(enabled);
240
222
  },
241
- [handlers.onAnimationsToggle]
242
- ),
243
223
 
244
- onSettings: useCallback(() => {}, []),
245
- };
224
+ onSettings: () => {},
225
+ }),
226
+ [handlers]
227
+ );
246
228
 
247
229
  // Generate chart-specific toolbar groups
248
230
  const generateToolbarGroups = useCallback((): ChartToolbarGroup[] => {
@@ -441,7 +423,7 @@ export function useChartToolbar(
441
423
  }
442
424
 
443
425
  return groups;
444
- }, [chartType, finalDefaults, state, enhancedHandlers, customActions, customGroups]);
426
+ }, [finalDefaults, state, enhancedHandlers, customActions, customGroups]);
445
427
 
446
428
  // Keyboard shortcuts
447
429
  useEffect(() => {
@@ -255,7 +255,7 @@ export function useDataTable({
255
255
  ) {
256
256
  setColumnOrder(newOrder);
257
257
  }
258
- }, [columns]);
258
+ }, [columns, columnOrder]);
259
259
 
260
260
  // Update column visibility when columns prop changes
261
261
  useEffect(() => {
@@ -1,4 +1,4 @@
1
- import { useState, useRef, useCallback, useEffect } from 'react';
1
+ import { useState, useRef, useCallback, useEffect, useMemo } from 'react';
2
2
  import {
3
3
  DatePickerViewMode,
4
4
  DatePickerSelectionMode,
@@ -106,7 +106,7 @@ export function useDatePicker({
106
106
  const datePickerRef = useRef<HTMLDivElement>(null);
107
107
  const inputRef = useRef<HTMLInputElement>(null);
108
108
 
109
- const today = new Date();
109
+ const today = useMemo(() => new Date(), []);
110
110
  const currentMonth = viewDate.getMonth();
111
111
  const currentYear = viewDate.getFullYear();
112
112
  const daysInMonth = getDaysInMonth(currentYear, currentMonth);