@shohojdhara/atomix 0.2.3 → 0.2.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.
- package/README.md +19 -0
- package/dist/atomix.css +1703 -1544
- package/dist/atomix.min.css +4 -4
- package/dist/index.d.ts +1465 -963
- package/dist/index.esm.js +16289 -25908
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +15650 -21780
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/dist/themes/applemix.css +15008 -0
- package/dist/themes/applemix.min.css +72 -0
- package/dist/themes/boomdevs.css +1608 -1450
- package/dist/themes/boomdevs.min.css +5 -5
- package/dist/themes/esrar.css +1702 -1543
- package/dist/themes/esrar.min.css +4 -4
- package/dist/themes/flashtrade.css +15159 -0
- package/dist/themes/flashtrade.min.css +86 -0
- package/dist/themes/mashroom.css +1699 -1540
- package/dist/themes/mashroom.min.css +7 -7
- package/dist/themes/shaj-default.css +1693 -1534
- package/dist/themes/shaj-default.min.css +4 -4
- package/package.json +6 -17
- package/src/components/Accordion/Accordion.stories.tsx +662 -21
- package/src/components/Accordion/Accordion.tsx +21 -14
- package/src/components/AtomixGlass/AtomixGlass.test.tsx +106 -72
- package/src/components/AtomixGlass/AtomixGlass.tsx +529 -1195
- package/src/components/AtomixGlass/AtomixGlassContainer.tsx +400 -0
- package/src/components/AtomixGlass/GlassFilter.tsx +156 -0
- package/src/components/AtomixGlass/README.md +124 -2
- package/src/components/AtomixGlass/atomixGLass.old.tsx +1266 -0
- package/src/components/AtomixGlass/glass-utils.ts +263 -0
- package/src/components/AtomixGlass/shader-utils.ts +792 -68
- package/src/components/AtomixGlass/stories/AtomixGlass.stories.tsx +1250 -0
- package/src/components/AtomixGlass/stories/Examples.stories.tsx +5768 -0
- package/src/components/AtomixGlass/stories/Modes.stories.tsx +1065 -0
- package/src/components/AtomixGlass/stories/Playground.stories.tsx +1129 -0
- package/src/components/AtomixGlass/stories/ShaderVariants.stories.tsx +395 -0
- package/src/components/AtomixGlass/stories/shared-components.tsx +301 -0
- package/src/components/AtomixGlass/utils.ts +3 -3
- package/src/components/Avatar/Avatar.tsx +3 -0
- package/src/components/Avatar/AvatarGroup.tsx +2 -1
- package/src/components/Badge/Badge.stories.tsx +76 -55
- package/src/components/Badge/Badge.tsx +12 -14
- package/src/components/Breadcrumb/Breadcrumb.tsx +23 -4
- package/src/components/Button/Button.stories.tsx +501 -20
- package/src/components/Button/Button.tsx +5 -8
- package/src/components/Callout/Callout.stories.tsx +86 -35
- package/src/components/Callout/Callout.tsx +31 -9
- package/src/components/Card/Card.stories.tsx +565 -2
- package/src/components/Card/Card.tsx +15 -4
- package/src/components/Card/ElevationCard.tsx +2 -0
- package/src/components/Chart/AnimatedChart.tsx +179 -156
- package/src/components/Chart/AreaChart.tsx +123 -12
- package/src/components/Chart/BarChart.tsx +91 -100
- package/src/components/Chart/BaseChart.tsx +80 -0
- package/src/components/Chart/BubbleChart.tsx +114 -290
- package/src/components/Chart/CandlestickChart.tsx +282 -622
- package/src/components/Chart/Chart.stories.tsx +576 -179
- package/src/components/Chart/Chart.tsx +374 -75
- package/src/components/Chart/ChartRenderer.tsx +371 -220
- package/src/components/Chart/ChartToolbar.tsx +372 -61
- package/src/components/Chart/ChartTooltip.tsx +33 -18
- package/src/components/Chart/DonutChart.tsx +172 -254
- package/src/components/Chart/FunnelChart.tsx +169 -240
- package/src/components/Chart/GaugeChart.tsx +224 -392
- package/src/components/Chart/HeatmapChart.tsx +302 -440
- package/src/components/Chart/LineChart.tsx +148 -103
- package/src/components/Chart/MultiAxisChart.tsx +267 -395
- package/src/components/Chart/PieChart.tsx +114 -64
- package/src/components/Chart/RadarChart.tsx +202 -218
- package/src/components/Chart/ScatterChart.tsx +111 -97
- package/src/components/Chart/TreemapChart.tsx +147 -222
- package/src/components/Chart/WaterfallChart.tsx +253 -291
- package/src/components/Chart/index.ts +11 -9
- package/src/components/Chart/types.ts +85 -9
- package/src/components/Chart/utils.ts +66 -0
- package/src/components/ColorModeToggle/ColorModeToggle.tsx +6 -3
- package/src/components/Countdown/Countdown.tsx +4 -0
- package/src/components/DataTable/DataTable.tsx +2 -1
- package/src/components/DatePicker/DatePicker.stories.tsx +689 -12
- package/src/components/DatePicker/DatePicker.tsx +3 -9
- package/src/components/DatePicker/types.ts +5 -0
- package/src/components/Dropdown/Dropdown.stories.tsx +32 -25
- package/src/components/Dropdown/Dropdown.tsx +26 -28
- package/src/components/EdgePanel/EdgePanel.stories.tsx +473 -2
- package/src/components/EdgePanel/EdgePanel.tsx +101 -13
- package/src/components/Footer/Footer.stories.tsx +187 -60
- package/src/components/Footer/Footer.test.tsx +134 -0
- package/src/components/Footer/Footer.tsx +133 -34
- package/src/components/Footer/FooterLink.tsx +1 -1
- package/src/components/Footer/FooterSection.tsx +53 -36
- package/src/components/Footer/FooterSocialLink.tsx +32 -29
- package/src/components/Footer/README.md +82 -3
- package/src/components/Footer/index.ts +1 -1
- package/src/components/Form/Checkbox.stories.tsx +13 -5
- package/src/components/Form/Checkbox.tsx +3 -6
- package/src/components/Form/Form.stories.tsx +10 -3
- package/src/components/Form/Form.tsx +2 -0
- package/src/components/Form/FormGroup.tsx +2 -1
- package/src/components/Form/Input.stories.tsx +12 -11
- package/src/components/Form/Input.tsx +97 -95
- package/src/components/Form/Radio.stories.tsx +22 -7
- package/src/components/Form/Radio.tsx +3 -6
- package/src/components/Form/Select.stories.tsx +21 -6
- package/src/components/Form/Select.tsx +3 -5
- package/src/components/Form/Textarea.stories.tsx +13 -11
- package/src/components/Form/Textarea.tsx +88 -86
- package/src/components/Hero/Hero.stories.tsx +2 -3
- package/src/components/Hero/Hero.tsx +5 -6
- package/src/components/Icon/Icon.tsx +12 -1
- package/src/components/List/List.tsx +2 -1
- package/src/components/List/ListGroup.tsx +2 -1
- package/src/components/Messages/Messages.stories.tsx +113 -0
- package/src/components/Messages/Messages.tsx +52 -9
- package/src/components/Modal/Modal.stories.tsx +48 -32
- package/src/components/Modal/Modal.tsx +19 -24
- package/src/components/Navigation/Menu/MegaMenu.tsx +2 -2
- package/src/components/Navigation/Menu/Menu.tsx +2 -2
- package/src/components/Navigation/Nav/Nav.stories.tsx +469 -0
- package/src/components/Navigation/Nav/Nav.tsx +22 -4
- package/src/components/Navigation/Nav/NavDropdown.tsx +10 -1
- package/src/components/Navigation/Navbar/Navbar.stories.tsx +413 -0
- package/src/components/Navigation/Navbar/Navbar.tsx +70 -29
- package/src/components/Navigation/SideMenu/SideMenu.stories.tsx +340 -0
- package/src/components/Navigation/SideMenu/SideMenu.tsx +29 -2
- package/src/components/Pagination/Pagination.stories.tsx +13 -6
- package/src/components/Pagination/Pagination.tsx +7 -6
- package/src/components/PhotoViewer/PhotoViewer.tsx +2 -1
- package/src/components/Popover/Popover.stories.tsx +32 -24
- package/src/components/Popover/Popover.tsx +4 -1
- package/src/components/ProductReview/ProductReview.tsx +8 -2
- package/src/components/Progress/Progress.tsx +19 -3
- package/src/components/Rating/Rating.stories.tsx +11 -6
- package/src/components/Rating/Rating.tsx +3 -5
- package/src/components/River/River.tsx +5 -5
- package/src/components/SectionIntro/SectionIntro.tsx +8 -2
- package/src/components/Slider/Slider.stories.tsx +4 -4
- package/src/components/Slider/Slider.tsx +4 -3
- package/src/components/Spinner/Spinner.tsx +19 -3
- package/src/components/Steps/Steps.stories.tsx +5 -4
- package/src/components/Steps/Steps.tsx +8 -5
- package/src/components/Tab/Tab.stories.tsx +4 -3
- package/src/components/Tab/Tab.tsx +8 -6
- package/src/components/Testimonial/Testimonial.tsx +8 -2
- package/src/components/Todo/Todo.tsx +2 -1
- package/src/components/Toggle/Toggle.stories.tsx +5 -4
- package/src/components/Toggle/Toggle.tsx +8 -5
- package/src/components/Tooltip/Tooltip.stories.tsx +40 -30
- package/src/components/Tooltip/Tooltip.tsx +9 -2
- package/src/components/Upload/Upload.stories.tsx +252 -0
- package/src/components/Upload/Upload.tsx +92 -53
- package/src/components/VideoPlayer/VideoPlayer.tsx +3 -1
- package/src/components/index.ts +0 -4
- package/src/layouts/Grid/Grid.stories.tsx +10 -23
- package/src/layouts/Grid/Grid.tsx +20 -1
- package/src/layouts/Grid/GridCol.tsx +76 -48
- package/src/lib/composables/useAtomixGlass.ts +861 -44
- package/src/lib/composables/useBarChart.ts +21 -4
- package/src/lib/composables/useChart.ts +227 -370
- package/src/lib/composables/useChartExport.ts +19 -78
- package/src/lib/composables/useChartToolbar.ts +11 -21
- package/src/lib/composables/useEdgePanel.ts +125 -71
- package/src/lib/composables/useFooter.ts +3 -3
- package/src/lib/composables/useGlassContainer.ts +16 -7
- package/src/lib/composables/useLineChart.ts +11 -2
- package/src/lib/composables/usePieChart.ts +4 -14
- package/src/lib/composables/useRiver.ts +5 -0
- package/src/lib/composables/useSlider.ts +62 -24
- package/src/lib/composables/useVideoPlayer.ts +60 -63
- package/src/lib/constants/components.ts +147 -32
- package/src/lib/types/components.ts +355 -25
- package/src/lib/utils/displacement-generator.ts +55 -49
- package/src/lib/utils/icons.ts +1 -1
- package/src/lib/utils/index.ts +16 -10
- package/src/styles/01-settings/_settings.accordion.scss +19 -19
- package/src/styles/01-settings/_settings.animations.scss +5 -5
- package/src/styles/01-settings/_settings.avatar-group.scss +1 -1
- package/src/styles/01-settings/_settings.avatar.scss +17 -17
- package/src/styles/01-settings/_settings.background.scss +0 -3
- package/src/styles/01-settings/_settings.badge.scss +1 -1
- package/src/styles/01-settings/_settings.breadcrumb.scss +1 -1
- package/src/styles/01-settings/_settings.card.scss +1 -1
- package/src/styles/01-settings/_settings.chart.scss +65 -2
- package/src/styles/01-settings/_settings.dropdown.scss +1 -1
- package/src/styles/01-settings/_settings.edge-panel.scss +1 -1
- package/src/styles/01-settings/_settings.footer.scss +35 -42
- package/src/styles/01-settings/_settings.input.scss +1 -1
- package/src/styles/01-settings/_settings.list.scss +1 -1
- package/src/styles/01-settings/_settings.rating.scss +1 -1
- package/src/styles/01-settings/_settings.tabs.scss +1 -1
- package/src/styles/01-settings/_settings.upload.scss +6 -5
- package/src/styles/02-tools/_tools.animations.scss +4 -5
- package/src/styles/02-tools/_tools.background.scss +1 -13
- package/src/styles/02-tools/_tools.glass.scss +0 -1
- package/src/styles/02-tools/_tools.utility-api.scss +91 -48
- package/src/styles/03-generic/_generic.root.scss +1 -4
- package/src/styles/04-elements/_elements.body.scss +0 -1
- package/src/styles/06-components/_components.atomix-glass.scss +249 -0
- package/src/styles/06-components/_components.badge.scss +8 -23
- package/src/styles/06-components/_components.button.scss +8 -3
- package/src/styles/06-components/_components.callout.scss +10 -5
- package/src/styles/06-components/_components.card.scss +2 -14
- package/src/styles/06-components/_components.chart.scss +969 -1449
- package/src/styles/06-components/_components.dropdown.scss +19 -7
- package/src/styles/06-components/_components.edge-panel.scss +103 -0
- package/src/styles/06-components/_components.footer.scss +166 -85
- package/src/styles/06-components/_components.input.scss +8 -9
- package/src/styles/06-components/_components.list.scss +1 -0
- package/src/styles/06-components/_components.messages.scss +176 -0
- package/src/styles/06-components/_components.modal.scss +16 -4
- package/src/styles/06-components/_components.navbar.scss +12 -1
- package/src/styles/06-components/_components.side-menu.scss +5 -0
- package/src/styles/06-components/_components.skeleton.scss +8 -6
- package/src/styles/06-components/_components.upload.scss +219 -4
- package/src/styles/06-components/old.chart.styles.scss +1 -30
- package/src/styles/99-utilities/_index.scss +1 -0
- package/src/styles/99-utilities/_utilities.glass-fixes.scss +1 -0
- package/src/styles/99-utilities/_utilities.scss +1 -1
- package/src/components/AtomixGlass/AtomixGlass.stories.tsx +0 -3011
- package/src/components/AtomixGlass/AtomixGlassComprehensivePreview.stories.tsx +0 -1369
- package/src/components/Chart/AdvancedChart.tsx +0 -624
- package/src/components/Chart/LineChartNew.tsx +0 -167
- package/src/components/Chart/RealTimeChart.tsx +0 -436
- package/src/components/DatePicker/DatePicker copy.tsx +0 -551
|
@@ -17,6 +17,7 @@ export interface ChartInteractionState {
|
|
|
17
17
|
selectedPoints: Array<{ datasetIndex: number; pointIndex: number }>;
|
|
18
18
|
zoomLevel: number;
|
|
19
19
|
panOffset: { x: number; y: number };
|
|
20
|
+
panEnabled: boolean;
|
|
20
21
|
isAnimating: boolean;
|
|
21
22
|
isDragging: boolean;
|
|
22
23
|
dragStart: { x: number; y: number } | null;
|
|
@@ -66,6 +67,7 @@ export function useChart(initialProps?: Partial<ChartProps>) {
|
|
|
66
67
|
selectedPoints: [],
|
|
67
68
|
zoomLevel: 1,
|
|
68
69
|
panOffset: { x: 0, y: 0 },
|
|
70
|
+
panEnabled: false,
|
|
69
71
|
isAnimating: false,
|
|
70
72
|
isDragging: false,
|
|
71
73
|
dragStart: null,
|
|
@@ -99,143 +101,18 @@ export function useChart(initialProps?: Partial<ChartProps>) {
|
|
|
99
101
|
|
|
100
102
|
// Animation frame ref for smooth updates
|
|
101
103
|
const animationFrameRef = useRef<number | null>(null);
|
|
102
|
-
const containerRef = useRef<HTMLDivElement>(null);
|
|
103
|
-
const svgRef = useRef<SVGSVGElement>(null);
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Generate chart class based on properties
|
|
107
|
-
*/
|
|
108
|
-
const generateChartClass = useCallback(
|
|
109
|
-
(props: Partial<ChartProps>): string => {
|
|
110
|
-
const {
|
|
111
|
-
type = defaultProps.type,
|
|
112
|
-
size = defaultProps.size,
|
|
113
|
-
variant = defaultProps.variant,
|
|
114
|
-
loading = defaultProps.loading,
|
|
115
|
-
error,
|
|
116
|
-
className = '',
|
|
117
|
-
} = props;
|
|
118
|
-
|
|
119
|
-
const typeClass = type ? `${CHART.TYPE_PREFIX}${type}` : '';
|
|
120
|
-
const sizeClass = size === 'md' ? '' : `${CHART.SIZE_PREFIX}${size}`;
|
|
121
|
-
const variantClass = variant ? `${CHART.VARIANT_PREFIX}${variant}` : '';
|
|
122
|
-
const loadingClass = loading ? CHART.LOADING_STATE_CLASS : '';
|
|
123
|
-
const errorClass = error ? CHART.ERROR_STATE_CLASS : '';
|
|
124
|
-
|
|
125
|
-
return `${CHART.BASE_CLASS} ${typeClass} ${variantClass} ${sizeClass} ${loadingClass} ${errorClass} ${className}`.trim();
|
|
126
|
-
},
|
|
127
|
-
[defaultProps]
|
|
128
|
-
);
|
|
129
104
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
const attributes: Record<string, string> = {
|
|
137
|
-
role: 'img',
|
|
138
|
-
'aria-live': 'polite',
|
|
105
|
+
// Cleanup animation frame on unmount
|
|
106
|
+
useEffect(() => {
|
|
107
|
+
return () => {
|
|
108
|
+
if (animationFrameRef.current) {
|
|
109
|
+
cancelAnimationFrame(animationFrameRef.current);
|
|
110
|
+
}
|
|
139
111
|
};
|
|
140
|
-
|
|
141
|
-
if (loading) {
|
|
142
|
-
attributes['aria-busy'] = 'true';
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
if (error) {
|
|
146
|
-
attributes['aria-invalid'] = 'true';
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
if (type) {
|
|
150
|
-
attributes['data-chart-type'] = type;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
return attributes;
|
|
154
112
|
}, []);
|
|
155
113
|
|
|
156
114
|
/**
|
|
157
|
-
*
|
|
158
|
-
*/
|
|
159
|
-
const calculateScales = useCallback(
|
|
160
|
-
(
|
|
161
|
-
datasets: ChartDataset[],
|
|
162
|
-
width: number = CHART.DEFAULT_WIDTH,
|
|
163
|
-
height: number = CHART.DEFAULT_HEIGHT,
|
|
164
|
-
padding = { top: 20, right: 30, bottom: 40, left: 50 },
|
|
165
|
-
config?: any
|
|
166
|
-
): ChartScales | null => {
|
|
167
|
-
if (!datasets.length) return null;
|
|
168
|
-
|
|
169
|
-
const innerWidth = width - padding.left - padding.right;
|
|
170
|
-
const innerHeight = height - padding.top - padding.bottom;
|
|
171
|
-
|
|
172
|
-
// Calculate value bounds
|
|
173
|
-
const allValues = datasets.flatMap(
|
|
174
|
-
dataset => dataset.data?.map(d => (typeof d.value === 'number' ? d.value : 0)) || []
|
|
175
|
-
);
|
|
176
|
-
|
|
177
|
-
if (allValues.length === 0) return null;
|
|
178
|
-
|
|
179
|
-
const minValue = config?.yAxis?.min ?? Math.min(0, ...allValues);
|
|
180
|
-
const maxValue = config?.yAxis?.max ?? Math.max(...allValues, 1);
|
|
181
|
-
const valueRange = maxValue - minValue;
|
|
182
|
-
|
|
183
|
-
// Scale functions with zoom and pan support
|
|
184
|
-
const xScale = (index: number, dataLength?: number) => {
|
|
185
|
-
const totalLength = dataLength || datasets[0]?.data?.length || 1;
|
|
186
|
-
if (totalLength <= 1) return padding.left + innerWidth / 2;
|
|
187
|
-
|
|
188
|
-
const baseX = padding.left + (index / (totalLength - 1)) * innerWidth;
|
|
189
|
-
return baseX * interactionState.zoomLevel + interactionState.panOffset.x;
|
|
190
|
-
};
|
|
191
|
-
|
|
192
|
-
const yScale = (value: number) => {
|
|
193
|
-
if (valueRange === 0) return padding.top + innerHeight / 2;
|
|
194
|
-
|
|
195
|
-
const baseY = padding.top + innerHeight - ((value - minValue) / valueRange) * innerHeight;
|
|
196
|
-
return baseY * interactionState.zoomLevel + interactionState.panOffset.y;
|
|
197
|
-
};
|
|
198
|
-
|
|
199
|
-
return {
|
|
200
|
-
xScale,
|
|
201
|
-
yScale,
|
|
202
|
-
minValue,
|
|
203
|
-
maxValue,
|
|
204
|
-
valueRange,
|
|
205
|
-
innerWidth,
|
|
206
|
-
innerHeight,
|
|
207
|
-
width,
|
|
208
|
-
height,
|
|
209
|
-
padding,
|
|
210
|
-
};
|
|
211
|
-
},
|
|
212
|
-
[interactionState.zoomLevel, interactionState.panOffset]
|
|
213
|
-
);
|
|
214
|
-
|
|
215
|
-
/**
|
|
216
|
-
* Generate color palette using CSS custom properties
|
|
217
|
-
*/
|
|
218
|
-
const getChartColors = useCallback((count: number) => {
|
|
219
|
-
const colors = [
|
|
220
|
-
'var(--atomix-primary)',
|
|
221
|
-
'var(--atomix-secondary)',
|
|
222
|
-
'var(--atomix-success)',
|
|
223
|
-
'var(--atomix-info)',
|
|
224
|
-
'var(--atomix-warning)',
|
|
225
|
-
'var(--atomix-error)',
|
|
226
|
-
'var(--atomix-primary-5)',
|
|
227
|
-
'var(--atomix-primary-7)',
|
|
228
|
-
'var(--atomix-primary-3)',
|
|
229
|
-
'var(--atomix-gray-6)',
|
|
230
|
-
'var(--atomix-gray-8)',
|
|
231
|
-
'var(--atomix-gray-4)',
|
|
232
|
-
];
|
|
233
|
-
|
|
234
|
-
return Array.from({ length: count }, (_, i) => colors[i % colors.length]);
|
|
235
|
-
}, []);
|
|
236
|
-
|
|
237
|
-
/**
|
|
238
|
-
* Enhanced point interaction handlers
|
|
115
|
+
* Point interaction handlers
|
|
239
116
|
*/
|
|
240
117
|
const handlePointHover = useCallback(
|
|
241
118
|
(
|
|
@@ -249,7 +126,6 @@ export function useChart(initialProps?: Partial<ChartProps>) {
|
|
|
249
126
|
setInteractionState(prev => ({
|
|
250
127
|
...prev,
|
|
251
128
|
hoveredPoint: { datasetIndex, pointIndex, x, y, clientX, clientY },
|
|
252
|
-
focusedPointIndex: pointIndex,
|
|
253
129
|
}));
|
|
254
130
|
},
|
|
255
131
|
[]
|
|
@@ -262,44 +138,35 @@ export function useChart(initialProps?: Partial<ChartProps>) {
|
|
|
262
138
|
}));
|
|
263
139
|
}, []);
|
|
264
140
|
|
|
265
|
-
const handlePointClick = useCallback(
|
|
266
|
-
(
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
p => p.datasetIndex === datasetIndex && p.pointIndex === pointIndex
|
|
271
|
-
);
|
|
272
|
-
|
|
273
|
-
let newSelectedPoints;
|
|
274
|
-
if (existingIndex >= 0) {
|
|
275
|
-
// Remove if already selected
|
|
276
|
-
newSelectedPoints = prev.selectedPoints.filter((_, i) => i !== existingIndex);
|
|
277
|
-
} else {
|
|
278
|
-
// Add to selection
|
|
279
|
-
newSelectedPoints = multiSelect ? [...prev.selectedPoints, pointKey] : [pointKey];
|
|
280
|
-
}
|
|
141
|
+
const handlePointClick = useCallback((datasetIndex: number, pointIndex: number) => {
|
|
142
|
+
setInteractionState(prev => {
|
|
143
|
+
const isSelected = prev.selectedPoints.some(
|
|
144
|
+
point => point.datasetIndex === datasetIndex && point.pointIndex === pointIndex
|
|
145
|
+
);
|
|
281
146
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
147
|
+
return {
|
|
148
|
+
...prev,
|
|
149
|
+
selectedPoints: isSelected
|
|
150
|
+
? prev.selectedPoints.filter(
|
|
151
|
+
point => !(point.datasetIndex === datasetIndex && point.pointIndex === pointIndex)
|
|
152
|
+
)
|
|
153
|
+
: [...prev.selectedPoints, { datasetIndex, pointIndex }],
|
|
154
|
+
};
|
|
155
|
+
});
|
|
156
|
+
}, []);
|
|
290
157
|
|
|
291
158
|
/**
|
|
292
|
-
*
|
|
159
|
+
* Zoom and pan handlers
|
|
293
160
|
*/
|
|
294
|
-
const handleZoom = useCallback((delta: number, centerX
|
|
161
|
+
const handleZoom = useCallback((delta: number, centerX?: number, centerY?: number) => {
|
|
295
162
|
setInteractionState(prev => {
|
|
296
163
|
const zoomFactor = 1 - delta * 0.001;
|
|
297
164
|
const newZoomLevel = Math.max(0.1, Math.min(10, prev.zoomLevel * zoomFactor));
|
|
298
|
-
|
|
299
|
-
// Adjust pan offset to zoom towards the center point
|
|
300
165
|
const zoomRatio = newZoomLevel / prev.zoomLevel;
|
|
166
|
+
|
|
167
|
+
// Adjust pan offset to zoom towards center point
|
|
301
168
|
const newPanOffset = {
|
|
302
|
-
x: centerX - (centerX - prev.panOffset.x) * zoomRatio,
|
|
169
|
+
x: centerX ? centerX - (centerX - prev.panOffset.x) * zoomRatio : prev.panOffset.x,
|
|
303
170
|
y: centerY ? centerY - (centerY - prev.panOffset.y) * zoomRatio : prev.panOffset.y,
|
|
304
171
|
};
|
|
305
172
|
|
|
@@ -372,8 +239,8 @@ export function useChart(initialProps?: Partial<ChartProps>) {
|
|
|
372
239
|
},
|
|
373
240
|
};
|
|
374
241
|
|
|
375
|
-
// Single touch - start dragging
|
|
376
|
-
if (touches.length === 1 && touches[0]) {
|
|
242
|
+
// Single touch - start dragging only if pan is enabled
|
|
243
|
+
if (touches.length === 1 && touches[0] && prev.panEnabled) {
|
|
377
244
|
const rect = (event.target as Element).getBoundingClientRect();
|
|
378
245
|
const x = touches[0].x - rect.left;
|
|
379
246
|
const y = touches[0].y - rect.top;
|
|
@@ -418,20 +285,56 @@ export function useChart(initialProps?: Partial<ChartProps>) {
|
|
|
418
285
|
setInteractionState(prev => {
|
|
419
286
|
const rect = (event.target as Element).getBoundingClientRect();
|
|
420
287
|
|
|
421
|
-
// Single touch - pan
|
|
422
|
-
if (
|
|
288
|
+
// Single touch - pan (only if pan is enabled)
|
|
289
|
+
if (
|
|
290
|
+
touches.length === 1 &&
|
|
291
|
+
touches[0] &&
|
|
292
|
+
prev.isDragging &&
|
|
293
|
+
prev.dragStart &&
|
|
294
|
+
prev.panEnabled
|
|
295
|
+
) {
|
|
423
296
|
const x = touches[0].x - rect.left;
|
|
424
297
|
const y = touches[0].y - rect.top;
|
|
425
|
-
const deltaX = x - prev.dragStart.x;
|
|
426
|
-
const deltaY = y - prev.dragStart.y;
|
|
427
298
|
|
|
299
|
+
// Use previous touch position for delta calculation if available
|
|
300
|
+
const prevTouch = prev.touchState.touches[0];
|
|
301
|
+
let deltaX, deltaY;
|
|
302
|
+
|
|
303
|
+
if (prevTouch) {
|
|
304
|
+
// Calculate delta from previous touch position
|
|
305
|
+
const prevX = prevTouch.x - rect.left;
|
|
306
|
+
const prevY = prevTouch.y - rect.top;
|
|
307
|
+
deltaX = x - prevX;
|
|
308
|
+
deltaY = y - prevY;
|
|
309
|
+
} else {
|
|
310
|
+
// Fallback to drag start position
|
|
311
|
+
deltaX = x - prev.dragStart.x;
|
|
312
|
+
deltaY = y - prev.dragStart.y;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Apply sensitivity reduction for touch
|
|
316
|
+
const sensitivity = 0.6; // Slightly higher than mouse for touch comfort
|
|
317
|
+
const adjustedDeltaX = deltaX * sensitivity;
|
|
318
|
+
const adjustedDeltaY = deltaY * sensitivity;
|
|
319
|
+
|
|
320
|
+
// Only pan if there's meaningful movement (reduces jitter)
|
|
321
|
+
if (Math.abs(adjustedDeltaX) > 1 || Math.abs(adjustedDeltaY) > 1) {
|
|
322
|
+
return {
|
|
323
|
+
...prev,
|
|
324
|
+
panOffset: {
|
|
325
|
+
x: prev.panOffset.x + adjustedDeltaX,
|
|
326
|
+
y: prev.panOffset.y + adjustedDeltaY,
|
|
327
|
+
},
|
|
328
|
+
touchState: {
|
|
329
|
+
...prev.touchState,
|
|
330
|
+
touches,
|
|
331
|
+
},
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// Update touch state even if no panning occurred
|
|
428
336
|
return {
|
|
429
337
|
...prev,
|
|
430
|
-
panOffset: {
|
|
431
|
-
x: prev.panOffset.x + deltaX,
|
|
432
|
-
y: prev.panOffset.y + deltaY,
|
|
433
|
-
},
|
|
434
|
-
dragStart: { x, y },
|
|
435
338
|
touchState: {
|
|
436
339
|
...prev.touchState,
|
|
437
340
|
touches,
|
|
@@ -500,7 +403,7 @@ export function useChart(initialProps?: Partial<ChartProps>) {
|
|
|
500
403
|
}));
|
|
501
404
|
|
|
502
405
|
setInteractionState(prev => {
|
|
503
|
-
//
|
|
406
|
+
// If no touches left, end all touch interactions
|
|
504
407
|
if (touches.length === 0) {
|
|
505
408
|
return {
|
|
506
409
|
...prev,
|
|
@@ -516,15 +419,14 @@ export function useChart(initialProps?: Partial<ChartProps>) {
|
|
|
516
419
|
};
|
|
517
420
|
}
|
|
518
421
|
|
|
519
|
-
//
|
|
520
|
-
if (touches.length === 1 &&
|
|
422
|
+
// If one touch left, continue dragging if pan is enabled
|
|
423
|
+
if (touches.length === 1 && prev.panEnabled) {
|
|
521
424
|
const rect = (event.target as Element).getBoundingClientRect();
|
|
522
|
-
const x = touches[0]
|
|
523
|
-
const y = touches[0]
|
|
425
|
+
const x = touches[0]!.x - rect.left;
|
|
426
|
+
const y = touches[0]!.y - rect.top;
|
|
524
427
|
|
|
525
428
|
return {
|
|
526
429
|
...prev,
|
|
527
|
-
isDragging: true,
|
|
528
430
|
dragStart: { x, y },
|
|
529
431
|
touchState: {
|
|
530
432
|
...prev.touchState,
|
|
@@ -535,6 +437,24 @@ export function useChart(initialProps?: Partial<ChartProps>) {
|
|
|
535
437
|
};
|
|
536
438
|
}
|
|
537
439
|
|
|
440
|
+
// If two touches left, continue pinching
|
|
441
|
+
if (touches.length === 2) {
|
|
442
|
+
const distance = Math.sqrt(
|
|
443
|
+
Math.pow(touches[1]!.x - touches[0]!.x, 2) + Math.pow(touches[1]!.y - touches[0]!.y, 2)
|
|
444
|
+
);
|
|
445
|
+
|
|
446
|
+
return {
|
|
447
|
+
...prev,
|
|
448
|
+
touchState: {
|
|
449
|
+
...prev.touchState,
|
|
450
|
+
touches,
|
|
451
|
+
lastDistance: distance,
|
|
452
|
+
isPinching: true,
|
|
453
|
+
},
|
|
454
|
+
isDragging: false,
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
|
|
538
458
|
return {
|
|
539
459
|
...prev,
|
|
540
460
|
touchState: {
|
|
@@ -545,208 +465,146 @@ export function useChart(initialProps?: Partial<ChartProps>) {
|
|
|
545
465
|
});
|
|
546
466
|
}, []);
|
|
547
467
|
|
|
548
|
-
const handlePointerDown = useCallback(
|
|
549
|
-
(event
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
isDragging: true,
|
|
563
|
-
dragStart: {
|
|
564
|
-
x: event.clientX - (event.target as Element).getBoundingClientRect().left,
|
|
565
|
-
y: event.clientY - (event.target as Element).getBoundingClientRect().top,
|
|
566
|
-
},
|
|
567
|
-
}));
|
|
568
|
-
} else if (!isTouch) {
|
|
569
|
-
// Regular mouse interaction
|
|
570
|
-
const rect = (event.target as Element).getBoundingClientRect();
|
|
571
|
-
const x = event.clientX - rect.left;
|
|
572
|
-
const y = event.clientY - rect.top;
|
|
573
|
-
handleDragStart(x, y);
|
|
574
|
-
}
|
|
575
|
-
},
|
|
576
|
-
[handleDragStart]
|
|
577
|
-
);
|
|
578
|
-
|
|
579
|
-
const handlePointerMove = useCallback(
|
|
580
|
-
(event: PointerEvent | React.PointerEvent) => {
|
|
581
|
-
const rect = (event.target as Element).getBoundingClientRect();
|
|
582
|
-
const x = event.clientX - rect.left;
|
|
583
|
-
const y = event.clientY - rect.top;
|
|
584
|
-
|
|
585
|
-
if (event.pointerType === 'pen' && interactionState.penState.isPen) {
|
|
586
|
-
setInteractionState(prev => {
|
|
587
|
-
if (prev.isDragging && prev.dragStart) {
|
|
588
|
-
const deltaX = x - prev.dragStart.x;
|
|
589
|
-
const deltaY = y - prev.dragStart.y;
|
|
590
|
-
|
|
591
|
-
// Pen pressure affects pan sensitivity
|
|
592
|
-
const pressureMultiplier = Math.max(0.1, event.pressure || 0.5);
|
|
593
|
-
|
|
594
|
-
return {
|
|
595
|
-
...prev,
|
|
596
|
-
panOffset: {
|
|
597
|
-
x: prev.panOffset.x + deltaX * pressureMultiplier,
|
|
598
|
-
y: prev.panOffset.y + deltaY * pressureMultiplier,
|
|
599
|
-
},
|
|
600
|
-
dragStart: { x, y },
|
|
601
|
-
penState: {
|
|
602
|
-
...prev.penState,
|
|
603
|
-
pressure: event.pressure || 0,
|
|
604
|
-
tiltX: (event as any).tiltX || 0,
|
|
605
|
-
tiltY: (event as any).tiltY || 0,
|
|
606
|
-
},
|
|
607
|
-
};
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
return {
|
|
611
|
-
...prev,
|
|
612
|
-
penState: {
|
|
613
|
-
...prev.penState,
|
|
614
|
-
pressure: event.pressure || 0,
|
|
615
|
-
tiltX: (event as any).tiltX || 0,
|
|
616
|
-
tiltY: (event as any).tiltY || 0,
|
|
617
|
-
},
|
|
618
|
-
};
|
|
619
|
-
});
|
|
620
|
-
} else if (event.pointerType !== 'touch') {
|
|
621
|
-
// Regular mouse move
|
|
622
|
-
handleCrosshair(x, y);
|
|
623
|
-
|
|
624
|
-
if (interactionState.isDragging && interactionState.dragStart) {
|
|
625
|
-
const deltaX = x - interactionState.dragStart.x;
|
|
626
|
-
const deltaY = y - interactionState.dragStart.y;
|
|
627
|
-
handlePan(deltaX, deltaY);
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
|
-
},
|
|
631
|
-
[interactionState, handleCrosshair, handlePan]
|
|
632
|
-
);
|
|
468
|
+
const handlePointerDown = useCallback((event: PointerEvent) => {
|
|
469
|
+
if (event.pointerType === 'pen') {
|
|
470
|
+
setInteractionState(prev => ({
|
|
471
|
+
...prev,
|
|
472
|
+
penState: {
|
|
473
|
+
...prev.penState,
|
|
474
|
+
isPen: true,
|
|
475
|
+
pressure: event.pressure,
|
|
476
|
+
tiltX: event.tiltX,
|
|
477
|
+
tiltY: event.tiltY,
|
|
478
|
+
},
|
|
479
|
+
}));
|
|
480
|
+
}
|
|
481
|
+
}, []);
|
|
633
482
|
|
|
634
|
-
const
|
|
635
|
-
(event
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
}));
|
|
648
|
-
} else if (event.pointerType !== 'touch') {
|
|
649
|
-
handleDragEnd();
|
|
650
|
-
}
|
|
651
|
-
},
|
|
652
|
-
[handleDragEnd]
|
|
653
|
-
);
|
|
483
|
+
const handlePointerMove = useCallback((event: PointerEvent) => {
|
|
484
|
+
if (event.pointerType === 'pen') {
|
|
485
|
+
setInteractionState(prev => ({
|
|
486
|
+
...prev,
|
|
487
|
+
penState: {
|
|
488
|
+
...prev.penState,
|
|
489
|
+
pressure: event.pressure,
|
|
490
|
+
tiltX: event.tiltX,
|
|
491
|
+
tiltY: event.tiltY,
|
|
492
|
+
},
|
|
493
|
+
}));
|
|
494
|
+
}
|
|
495
|
+
}, []);
|
|
654
496
|
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
497
|
+
const handlePointerUp = useCallback((event: PointerEvent) => {
|
|
498
|
+
if (event.pointerType === 'pen') {
|
|
499
|
+
setInteractionState(prev => ({
|
|
500
|
+
...prev,
|
|
501
|
+
penState: {
|
|
502
|
+
...prev.penState,
|
|
503
|
+
isPen: false,
|
|
504
|
+
pressure: 0,
|
|
505
|
+
tiltX: 0,
|
|
506
|
+
tiltY: 0,
|
|
507
|
+
},
|
|
508
|
+
}));
|
|
509
|
+
}
|
|
667
510
|
}, []);
|
|
668
511
|
|
|
669
512
|
/**
|
|
670
|
-
*
|
|
513
|
+
* Chart calculations and utilities
|
|
671
514
|
*/
|
|
672
|
-
const
|
|
673
|
-
|
|
515
|
+
const calculateScales = useCallback(
|
|
516
|
+
(
|
|
517
|
+
datasets: ChartDataset[],
|
|
518
|
+
width: number = CHART.DEFAULT_WIDTH,
|
|
519
|
+
height: number = CHART.DEFAULT_HEIGHT,
|
|
520
|
+
padding: { top: number; right: number; bottom: number; left: number } = {
|
|
521
|
+
top: 20,
|
|
522
|
+
right: 20,
|
|
523
|
+
bottom: 30,
|
|
524
|
+
left: 40,
|
|
525
|
+
},
|
|
526
|
+
config?: ChartProps['config']
|
|
527
|
+
): ChartScales | null => {
|
|
528
|
+
if (!datasets || datasets.length === 0) return null;
|
|
674
529
|
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
530
|
+
// Flatten all data points to find min/max values
|
|
531
|
+
const allDataPoints = datasets.flatMap(dataset => dataset.data);
|
|
532
|
+
if (allDataPoints.length === 0) return null;
|
|
678
533
|
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
const
|
|
682
|
-
const progress = Math.min(elapsed / duration, 1);
|
|
534
|
+
const minValue = Math.min(...allDataPoints.map(point => point.value));
|
|
535
|
+
const maxValue = Math.max(...allDataPoints.map(point => point.value));
|
|
536
|
+
const valueRange = maxValue - minValue || 1; // Avoid division by zero
|
|
683
537
|
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
setInteractionState(prev => ({ ...prev, isAnimating: false }));
|
|
688
|
-
}
|
|
689
|
-
};
|
|
538
|
+
// Apply padding
|
|
539
|
+
const innerWidth = width - padding.left - padding.right;
|
|
540
|
+
const innerHeight = height - padding.top - padding.bottom;
|
|
690
541
|
|
|
691
|
-
|
|
692
|
-
|
|
542
|
+
// Create scale functions
|
|
543
|
+
const xScale = (index: number, dataLength: number = allDataPoints.length) => {
|
|
544
|
+
if (dataLength <= 1) return padding.left + innerWidth / 2;
|
|
545
|
+
return padding.left + (index / (dataLength - 1)) * innerWidth;
|
|
546
|
+
};
|
|
547
|
+
|
|
548
|
+
const yScale = (value: number) => {
|
|
549
|
+
// Invert Y axis (SVG coordinates start from top)
|
|
550
|
+
return padding.top + innerHeight - ((value - minValue) / valueRange) * innerHeight;
|
|
551
|
+
};
|
|
552
|
+
|
|
553
|
+
return {
|
|
554
|
+
xScale,
|
|
555
|
+
yScale,
|
|
556
|
+
minValue,
|
|
557
|
+
maxValue,
|
|
558
|
+
valueRange,
|
|
559
|
+
innerWidth,
|
|
560
|
+
innerHeight,
|
|
561
|
+
width,
|
|
562
|
+
height,
|
|
563
|
+
padding,
|
|
564
|
+
};
|
|
565
|
+
},
|
|
566
|
+
[]
|
|
567
|
+
);
|
|
693
568
|
|
|
694
|
-
const
|
|
695
|
-
if (
|
|
696
|
-
|
|
697
|
-
|
|
569
|
+
const getChartColors = useCallback((count: number): (string | undefined)[] => {
|
|
570
|
+
if (count <= 0) return [];
|
|
571
|
+
|
|
572
|
+
// Generate colors from the theme
|
|
573
|
+
const colors = [];
|
|
574
|
+
for (let i = 0; i < count; i++) {
|
|
575
|
+
// Cycle through available colors
|
|
576
|
+
const colorIndex = i % CHART.DEFAULT_COLORS.length;
|
|
577
|
+
colors.push(CHART.DEFAULT_COLORS[colorIndex]);
|
|
698
578
|
}
|
|
699
|
-
setInteractionState(prev => ({ ...prev, isAnimating: false }));
|
|
700
|
-
}, []);
|
|
701
579
|
|
|
702
|
-
|
|
703
|
-
useEffect(() => {
|
|
704
|
-
return () => {
|
|
705
|
-
if (animationFrameRef.current) {
|
|
706
|
-
cancelAnimationFrame(animationFrameRef.current);
|
|
707
|
-
}
|
|
708
|
-
};
|
|
580
|
+
return colors;
|
|
709
581
|
}, []);
|
|
710
582
|
|
|
583
|
+
// Ref for SVG element
|
|
584
|
+
const svgRef = useRef<SVGSVGElement>(null);
|
|
585
|
+
|
|
586
|
+
// Return all chart functionality
|
|
711
587
|
return {
|
|
712
588
|
// State
|
|
713
589
|
interactionState,
|
|
714
|
-
|
|
715
|
-
selectedPoints: interactionState.selectedPoints,
|
|
716
|
-
zoomLevel: interactionState.zoomLevel,
|
|
717
|
-
panOffset: interactionState.panOffset,
|
|
718
|
-
isAnimating: interactionState.isAnimating,
|
|
719
|
-
isDragging: interactionState.isDragging,
|
|
720
|
-
crosshair: interactionState.crosshair,
|
|
721
|
-
brushSelection: interactionState.brushSelection,
|
|
722
|
-
focusedPointIndex: interactionState.focusedPointIndex,
|
|
723
|
-
|
|
724
|
-
// Refs
|
|
725
|
-
containerRef,
|
|
726
|
-
svgRef,
|
|
727
|
-
|
|
728
|
-
// Props and attributes
|
|
729
|
-
defaultProps,
|
|
730
|
-
generateChartClass,
|
|
731
|
-
chartAttributes: generateChartAttributes(initialProps || {}),
|
|
732
|
-
|
|
733
|
-
// Calculations
|
|
734
|
-
calculateScales,
|
|
735
|
-
getChartColors,
|
|
590
|
+
setInteractionState,
|
|
736
591
|
|
|
737
|
-
//
|
|
592
|
+
// Point interaction handlers
|
|
738
593
|
handlePointHover,
|
|
739
594
|
handlePointLeave,
|
|
740
595
|
handlePointClick,
|
|
596
|
+
|
|
597
|
+
// Zoom and pan handlers
|
|
741
598
|
handleZoom,
|
|
742
599
|
handlePan,
|
|
743
600
|
handleDragStart,
|
|
744
601
|
handleDragEnd,
|
|
602
|
+
|
|
603
|
+
// Crosshair handlers
|
|
745
604
|
handleCrosshair,
|
|
746
605
|
clearCrosshair,
|
|
747
|
-
resetView,
|
|
748
606
|
|
|
749
|
-
// Touch and
|
|
607
|
+
// Touch and pen handlers
|
|
750
608
|
handleTouchStart,
|
|
751
609
|
handleTouchMove,
|
|
752
610
|
handleTouchEnd,
|
|
@@ -754,12 +612,10 @@ export function useChart(initialProps?: Partial<ChartProps>) {
|
|
|
754
612
|
handlePointerMove,
|
|
755
613
|
handlePointerUp,
|
|
756
614
|
|
|
757
|
-
//
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
// State setters for advanced use cases
|
|
762
|
-
setInteractionState,
|
|
615
|
+
// Utilities
|
|
616
|
+
calculateScales,
|
|
617
|
+
getChartColors,
|
|
618
|
+
svgRef,
|
|
763
619
|
};
|
|
764
620
|
}
|
|
765
621
|
|
|
@@ -976,8 +832,8 @@ export function useChartAccessibility(
|
|
|
976
832
|
.map((dataset, i) => {
|
|
977
833
|
const dataCount = dataset.data?.length || 0;
|
|
978
834
|
const values = dataset.data?.map(d => d.value).filter(v => typeof v === 'number') || [];
|
|
979
|
-
const min = Math.min(...values);
|
|
980
|
-
const max = Math.max(...values);
|
|
835
|
+
const min = values.length > 0 ? Math.min(...values) : 0;
|
|
836
|
+
const max = values.length > 0 ? Math.max(...values) : 0;
|
|
981
837
|
|
|
982
838
|
return `Dataset ${i + 1}: ${dataset.label}, ${dataCount} points, range ${min} to ${max}`;
|
|
983
839
|
})
|
|
@@ -1021,16 +877,17 @@ export function useChartPerformance(
|
|
|
1021
877
|
if (!enableMemoization) return null;
|
|
1022
878
|
|
|
1023
879
|
// Cache expensive scale calculations
|
|
1024
|
-
return datasets.map(dataset =>
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
880
|
+
return datasets.map(dataset => {
|
|
881
|
+
const values = dataset.data?.map(d => d.value).filter(v => typeof v === 'number') || [];
|
|
882
|
+
const validValues = values.length > 0 ? values : [0];
|
|
883
|
+
|
|
884
|
+
return {
|
|
885
|
+
label: dataset.label,
|
|
886
|
+
dataLength: dataset.data?.length || 0,
|
|
887
|
+
minValue: Math.min(...validValues),
|
|
888
|
+
maxValue: Math.max(...validValues),
|
|
889
|
+
};
|
|
890
|
+
});
|
|
1034
891
|
}, [datasets, enableMemoization]);
|
|
1035
892
|
|
|
1036
893
|
// Debounced updates
|