@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.
- package/atomix.config.ts +45 -33
- package/build-tools/webpack-loader.js +5 -4
- package/dist/atomix.css +138 -17
- package/dist/atomix.css.map +1 -1
- package/dist/atomix.min.css +1 -1
- package/dist/atomix.min.css.map +1 -1
- package/dist/build-tools/webpack-loader.js +5 -4
- package/dist/charts.d.ts +23 -23
- package/dist/charts.js +40 -37
- package/dist/charts.js.map +1 -1
- package/dist/config.d.ts +699 -0
- package/dist/config.js +17 -0
- package/dist/config.js.map +1 -0
- package/dist/core.d.ts +2 -2
- package/dist/core.js +111 -50
- package/dist/core.js.map +1 -1
- package/dist/forms.d.ts +3 -6
- package/dist/forms.js +2 -2
- package/dist/forms.js.map +1 -1
- package/dist/heavy.d.ts +1 -1
- package/dist/heavy.js +173 -111
- package/dist/heavy.js.map +1 -1
- package/dist/index.d.ts +1881 -790
- package/dist/index.esm.js +2713 -816
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +2693 -780
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/dist/layout.js +59 -60
- package/dist/layout.js.map +1 -1
- package/dist/theme.d.ts +1390 -276
- package/dist/theme.js +2133 -625
- package/dist/theme.js.map +1 -1
- package/package.json +14 -9
- package/scripts/atomix-cli.js +15 -1
- package/scripts/cli/__tests__/complexity-utils.test.js +24 -0
- package/scripts/cli/__tests__/detector.test.js +50 -0
- package/scripts/cli/__tests__/template-engine.test.js +23 -0
- package/scripts/cli/__tests__/test-setup.js +3 -0
- package/scripts/cli/commands/doctor.js +15 -3
- package/scripts/cli/commands/generate.js +113 -51
- package/scripts/cli/internal/ai-engine.js +30 -10
- package/scripts/cli/internal/complexity-utils.js +60 -0
- package/scripts/cli/internal/component-validator.js +49 -16
- package/scripts/cli/internal/config-loader.js +30 -20
- package/scripts/cli/internal/generator.js +89 -36
- package/scripts/cli/internal/hook-generator.js +5 -2
- package/scripts/cli/internal/itcss-generator.js +16 -12
- package/scripts/cli/templates/next-templates.js +81 -30
- package/scripts/cli/templates/storybook-templates.js +12 -2
- package/scripts/cli/utils/detector.js +45 -7
- package/scripts/cli/utils/diagnostics.js +78 -0
- package/scripts/cli/utils/telemetry.js +13 -0
- package/src/components/Accordion/Accordion.stories.tsx +4 -0
- package/src/components/AtomixGlass/AtomixGlassContainer.tsx +1 -1
- package/src/components/AtomixGlass/__snapshots__/AtomixGlass.test.tsx.snap +219 -0
- package/src/components/AtomixGlass/glass-utils.ts +1 -1
- package/src/components/Button/Button.tsx +114 -57
- package/src/components/Callout/Callout.tsx +4 -4
- package/src/components/Chart/ChartRenderer.tsx +1 -1
- package/src/components/Chart/DonutChart.tsx +11 -8
- package/src/components/EdgePanel/EdgePanel.tsx +119 -115
- package/src/components/Form/Select.tsx +4 -4
- package/src/components/List/List.tsx +4 -4
- package/src/components/Navigation/SideMenu/SideMenu.tsx +6 -6
- package/src/components/PhotoViewer/PhotoViewerImage.tsx +1 -1
- package/src/components/ProductReview/ProductReview.tsx +4 -2
- package/src/components/Rating/Rating.tsx +4 -2
- package/src/components/SectionIntro/SectionIntro.tsx +4 -2
- package/src/components/Steps/Steps.tsx +1 -1
- package/src/components/Tabs/Tabs.tsx +5 -5
- package/src/components/Testimonial/Testimonial.tsx +4 -2
- package/src/components/VideoPlayer/VideoPlayer.tsx +4 -2
- package/src/layouts/CssGrid/CssGrid.stories.tsx +464 -0
- package/src/layouts/CssGrid/CssGrid.tsx +215 -0
- package/src/layouts/CssGrid/index.ts +8 -0
- package/src/layouts/CssGrid/scripts/CssGrid.js +284 -0
- package/src/layouts/CssGrid/scripts/index.js +43 -0
- package/src/layouts/Grid/scripts/Container.js +139 -0
- package/src/layouts/Grid/scripts/Grid.js +184 -0
- package/src/layouts/Grid/scripts/GridCol.js +273 -0
- package/src/layouts/Grid/scripts/Row.js +154 -0
- package/src/layouts/Grid/scripts/index.js +48 -0
- package/src/layouts/MasonryGrid/MasonryGrid.tsx +71 -59
- package/src/lib/composables/atomix-glass/useGlassSize.ts +1 -1
- package/src/lib/composables/useAccordion.ts +5 -5
- package/src/lib/composables/useAtomixGlass.ts +3 -3
- package/src/lib/composables/useBarChart.ts +2 -2
- package/src/lib/composables/useChart.ts +3 -2
- package/src/lib/composables/useChartToolbar.ts +48 -66
- package/src/lib/composables/useDataTable.ts +1 -1
- package/src/lib/composables/useDatePicker.ts +2 -2
- package/src/lib/composables/useEdgePanel.ts +45 -54
- package/src/lib/composables/useHeroBackgroundSlider.ts +5 -5
- package/src/lib/composables/usePhotoViewer.ts +2 -3
- package/src/lib/composables/usePieChart.ts +1 -1
- package/src/lib/composables/usePopover.ts +151 -139
- package/src/lib/composables/useSideMenu.ts +28 -41
- package/src/lib/composables/useSlider.ts +2 -6
- package/src/lib/composables/useTooltip.ts +2 -2
- package/src/lib/config/index.ts +38 -323
- package/src/lib/config/loader.ts +419 -0
- package/src/lib/config/public-api.ts +43 -0
- package/src/lib/config/types.ts +389 -0
- package/src/lib/config/validator.ts +305 -0
- package/src/lib/theme/adapters/index.ts +1 -1
- package/src/lib/theme/adapters/themeAdapter.ts +358 -229
- package/src/lib/theme/components/ThemeToggle.tsx +276 -0
- package/src/lib/theme/config/configLoader.ts +351 -0
- package/src/lib/theme/config/loader.ts +221 -0
- package/src/lib/theme/core/createTheme.ts +126 -50
- package/src/lib/theme/core/createThemeObject.ts +7 -4
- package/src/lib/theme/devtools/Comparator.tsx +1 -1
- package/src/lib/theme/devtools/Inspector.tsx +1 -1
- package/src/lib/theme/devtools/LiveEditor.tsx +1 -1
- package/src/lib/theme/hooks/useThemeSwitcher.ts +164 -0
- package/src/lib/theme/index.ts +322 -38
- package/src/lib/theme/runtime/ThemeProvider.tsx +45 -11
- package/src/lib/theme/runtime/__tests__/ThemeProvider.test.tsx +44 -393
- package/src/lib/theme/runtime/useTheme.ts +1 -0
- package/src/lib/theme/tokens/tokens.ts +101 -1
- package/src/lib/theme/types.ts +91 -0
- package/src/lib/theme/utils/performanceMonitor.ts +315 -0
- package/src/lib/theme/utils/responsive.ts +280 -0
- package/src/lib/theme/utils/themeUtils.ts +531 -117
- package/src/styles/01-settings/_index.scss +1 -0
- package/src/styles/01-settings/_settings.atomix-glass.scss +174 -0
- package/src/styles/01-settings/_settings.masonry-grid.scss +42 -6
- package/src/styles/02-tools/_tools.glass.scss +6 -0
- package/src/styles/05-objects/_objects.masonry-grid.scss +162 -24
- package/src/styles/06-components/_components.atomix-glass.scss +4 -4
- package/src/lib/composables/useBreadcrumb.ts +0 -81
- package/src/lib/composables/useChartInteractions.ts +0 -123
- package/src/lib/composables/useChartPerformance.ts +0 -347
- package/src/lib/composables/useDropdown.ts +0 -338
- package/src/lib/composables/useModal.ts +0 -110
- package/src/lib/hooks/usePerformanceMonitor.ts +0 -148
- package/src/lib/utils/displacement-generator.ts +0 -92
- package/src/lib/utils/memoryMonitor.ts +0 -191
- package/src/styles/01-settings/_settings.testtypecheck.scss +0 -53
- package/src/styles/01-settings/_settings.typedbutton.scss +0 -53
- package/src/styles/06-components/_components.testbutton.scss +0 -212
- package/src/styles/06-components/_components.testtypecheck.scss +0 -212
- 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 [
|
|
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
|
-
//
|
|
199
|
-
|
|
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
|
-
|
|
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);
|
|
210
|
-
|
|
211
|
-
|
|
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
|
-
|
|
296
|
-
|
|
297
|
-
|
|
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
|
-
// ===
|
|
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.
|
|
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.
|
|
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
|
|
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
|
|
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 (
|
|
109
|
-
cancelAnimationFrame(
|
|
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
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
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
|
-
|
|
173
|
-
(isFullscreen: boolean) => {
|
|
170
|
+
onFullscreen: (isFullscreen: boolean) => {
|
|
174
171
|
setState(prev => ({ ...prev, isFullscreen }));
|
|
175
172
|
handlers.onFullscreen?.(isFullscreen);
|
|
176
173
|
},
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
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
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
}, [
|
|
426
|
+
}, [finalDefaults, state, enhancedHandlers, customActions, customGroups]);
|
|
445
427
|
|
|
446
428
|
// Keyboard shortcuts
|
|
447
429
|
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);
|