@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.
Files changed (225) hide show
  1. package/README.md +19 -0
  2. package/dist/atomix.css +1703 -1544
  3. package/dist/atomix.min.css +4 -4
  4. package/dist/index.d.ts +1465 -963
  5. package/dist/index.esm.js +16289 -25908
  6. package/dist/index.esm.js.map +1 -1
  7. package/dist/index.js +15650 -21780
  8. package/dist/index.js.map +1 -1
  9. package/dist/index.min.js +1 -1
  10. package/dist/index.min.js.map +1 -1
  11. package/dist/themes/applemix.css +15008 -0
  12. package/dist/themes/applemix.min.css +72 -0
  13. package/dist/themes/boomdevs.css +1608 -1450
  14. package/dist/themes/boomdevs.min.css +5 -5
  15. package/dist/themes/esrar.css +1702 -1543
  16. package/dist/themes/esrar.min.css +4 -4
  17. package/dist/themes/flashtrade.css +15159 -0
  18. package/dist/themes/flashtrade.min.css +86 -0
  19. package/dist/themes/mashroom.css +1699 -1540
  20. package/dist/themes/mashroom.min.css +7 -7
  21. package/dist/themes/shaj-default.css +1693 -1534
  22. package/dist/themes/shaj-default.min.css +4 -4
  23. package/package.json +6 -17
  24. package/src/components/Accordion/Accordion.stories.tsx +662 -21
  25. package/src/components/Accordion/Accordion.tsx +21 -14
  26. package/src/components/AtomixGlass/AtomixGlass.test.tsx +106 -72
  27. package/src/components/AtomixGlass/AtomixGlass.tsx +529 -1195
  28. package/src/components/AtomixGlass/AtomixGlassContainer.tsx +400 -0
  29. package/src/components/AtomixGlass/GlassFilter.tsx +156 -0
  30. package/src/components/AtomixGlass/README.md +124 -2
  31. package/src/components/AtomixGlass/atomixGLass.old.tsx +1266 -0
  32. package/src/components/AtomixGlass/glass-utils.ts +263 -0
  33. package/src/components/AtomixGlass/shader-utils.ts +792 -68
  34. package/src/components/AtomixGlass/stories/AtomixGlass.stories.tsx +1250 -0
  35. package/src/components/AtomixGlass/stories/Examples.stories.tsx +5768 -0
  36. package/src/components/AtomixGlass/stories/Modes.stories.tsx +1065 -0
  37. package/src/components/AtomixGlass/stories/Playground.stories.tsx +1129 -0
  38. package/src/components/AtomixGlass/stories/ShaderVariants.stories.tsx +395 -0
  39. package/src/components/AtomixGlass/stories/shared-components.tsx +301 -0
  40. package/src/components/AtomixGlass/utils.ts +3 -3
  41. package/src/components/Avatar/Avatar.tsx +3 -0
  42. package/src/components/Avatar/AvatarGroup.tsx +2 -1
  43. package/src/components/Badge/Badge.stories.tsx +76 -55
  44. package/src/components/Badge/Badge.tsx +12 -14
  45. package/src/components/Breadcrumb/Breadcrumb.tsx +23 -4
  46. package/src/components/Button/Button.stories.tsx +501 -20
  47. package/src/components/Button/Button.tsx +5 -8
  48. package/src/components/Callout/Callout.stories.tsx +86 -35
  49. package/src/components/Callout/Callout.tsx +31 -9
  50. package/src/components/Card/Card.stories.tsx +565 -2
  51. package/src/components/Card/Card.tsx +15 -4
  52. package/src/components/Card/ElevationCard.tsx +2 -0
  53. package/src/components/Chart/AnimatedChart.tsx +179 -156
  54. package/src/components/Chart/AreaChart.tsx +123 -12
  55. package/src/components/Chart/BarChart.tsx +91 -100
  56. package/src/components/Chart/BaseChart.tsx +80 -0
  57. package/src/components/Chart/BubbleChart.tsx +114 -290
  58. package/src/components/Chart/CandlestickChart.tsx +282 -622
  59. package/src/components/Chart/Chart.stories.tsx +576 -179
  60. package/src/components/Chart/Chart.tsx +374 -75
  61. package/src/components/Chart/ChartRenderer.tsx +371 -220
  62. package/src/components/Chart/ChartToolbar.tsx +372 -61
  63. package/src/components/Chart/ChartTooltip.tsx +33 -18
  64. package/src/components/Chart/DonutChart.tsx +172 -254
  65. package/src/components/Chart/FunnelChart.tsx +169 -240
  66. package/src/components/Chart/GaugeChart.tsx +224 -392
  67. package/src/components/Chart/HeatmapChart.tsx +302 -440
  68. package/src/components/Chart/LineChart.tsx +148 -103
  69. package/src/components/Chart/MultiAxisChart.tsx +267 -395
  70. package/src/components/Chart/PieChart.tsx +114 -64
  71. package/src/components/Chart/RadarChart.tsx +202 -218
  72. package/src/components/Chart/ScatterChart.tsx +111 -97
  73. package/src/components/Chart/TreemapChart.tsx +147 -222
  74. package/src/components/Chart/WaterfallChart.tsx +253 -291
  75. package/src/components/Chart/index.ts +11 -9
  76. package/src/components/Chart/types.ts +85 -9
  77. package/src/components/Chart/utils.ts +66 -0
  78. package/src/components/ColorModeToggle/ColorModeToggle.tsx +6 -3
  79. package/src/components/Countdown/Countdown.tsx +4 -0
  80. package/src/components/DataTable/DataTable.tsx +2 -1
  81. package/src/components/DatePicker/DatePicker.stories.tsx +689 -12
  82. package/src/components/DatePicker/DatePicker.tsx +3 -9
  83. package/src/components/DatePicker/types.ts +5 -0
  84. package/src/components/Dropdown/Dropdown.stories.tsx +32 -25
  85. package/src/components/Dropdown/Dropdown.tsx +26 -28
  86. package/src/components/EdgePanel/EdgePanel.stories.tsx +473 -2
  87. package/src/components/EdgePanel/EdgePanel.tsx +101 -13
  88. package/src/components/Footer/Footer.stories.tsx +187 -60
  89. package/src/components/Footer/Footer.test.tsx +134 -0
  90. package/src/components/Footer/Footer.tsx +133 -34
  91. package/src/components/Footer/FooterLink.tsx +1 -1
  92. package/src/components/Footer/FooterSection.tsx +53 -36
  93. package/src/components/Footer/FooterSocialLink.tsx +32 -29
  94. package/src/components/Footer/README.md +82 -3
  95. package/src/components/Footer/index.ts +1 -1
  96. package/src/components/Form/Checkbox.stories.tsx +13 -5
  97. package/src/components/Form/Checkbox.tsx +3 -6
  98. package/src/components/Form/Form.stories.tsx +10 -3
  99. package/src/components/Form/Form.tsx +2 -0
  100. package/src/components/Form/FormGroup.tsx +2 -1
  101. package/src/components/Form/Input.stories.tsx +12 -11
  102. package/src/components/Form/Input.tsx +97 -95
  103. package/src/components/Form/Radio.stories.tsx +22 -7
  104. package/src/components/Form/Radio.tsx +3 -6
  105. package/src/components/Form/Select.stories.tsx +21 -6
  106. package/src/components/Form/Select.tsx +3 -5
  107. package/src/components/Form/Textarea.stories.tsx +13 -11
  108. package/src/components/Form/Textarea.tsx +88 -86
  109. package/src/components/Hero/Hero.stories.tsx +2 -3
  110. package/src/components/Hero/Hero.tsx +5 -6
  111. package/src/components/Icon/Icon.tsx +12 -1
  112. package/src/components/List/List.tsx +2 -1
  113. package/src/components/List/ListGroup.tsx +2 -1
  114. package/src/components/Messages/Messages.stories.tsx +113 -0
  115. package/src/components/Messages/Messages.tsx +52 -9
  116. package/src/components/Modal/Modal.stories.tsx +48 -32
  117. package/src/components/Modal/Modal.tsx +19 -24
  118. package/src/components/Navigation/Menu/MegaMenu.tsx +2 -2
  119. package/src/components/Navigation/Menu/Menu.tsx +2 -2
  120. package/src/components/Navigation/Nav/Nav.stories.tsx +469 -0
  121. package/src/components/Navigation/Nav/Nav.tsx +22 -4
  122. package/src/components/Navigation/Nav/NavDropdown.tsx +10 -1
  123. package/src/components/Navigation/Navbar/Navbar.stories.tsx +413 -0
  124. package/src/components/Navigation/Navbar/Navbar.tsx +70 -29
  125. package/src/components/Navigation/SideMenu/SideMenu.stories.tsx +340 -0
  126. package/src/components/Navigation/SideMenu/SideMenu.tsx +29 -2
  127. package/src/components/Pagination/Pagination.stories.tsx +13 -6
  128. package/src/components/Pagination/Pagination.tsx +7 -6
  129. package/src/components/PhotoViewer/PhotoViewer.tsx +2 -1
  130. package/src/components/Popover/Popover.stories.tsx +32 -24
  131. package/src/components/Popover/Popover.tsx +4 -1
  132. package/src/components/ProductReview/ProductReview.tsx +8 -2
  133. package/src/components/Progress/Progress.tsx +19 -3
  134. package/src/components/Rating/Rating.stories.tsx +11 -6
  135. package/src/components/Rating/Rating.tsx +3 -5
  136. package/src/components/River/River.tsx +5 -5
  137. package/src/components/SectionIntro/SectionIntro.tsx +8 -2
  138. package/src/components/Slider/Slider.stories.tsx +4 -4
  139. package/src/components/Slider/Slider.tsx +4 -3
  140. package/src/components/Spinner/Spinner.tsx +19 -3
  141. package/src/components/Steps/Steps.stories.tsx +5 -4
  142. package/src/components/Steps/Steps.tsx +8 -5
  143. package/src/components/Tab/Tab.stories.tsx +4 -3
  144. package/src/components/Tab/Tab.tsx +8 -6
  145. package/src/components/Testimonial/Testimonial.tsx +8 -2
  146. package/src/components/Todo/Todo.tsx +2 -1
  147. package/src/components/Toggle/Toggle.stories.tsx +5 -4
  148. package/src/components/Toggle/Toggle.tsx +8 -5
  149. package/src/components/Tooltip/Tooltip.stories.tsx +40 -30
  150. package/src/components/Tooltip/Tooltip.tsx +9 -2
  151. package/src/components/Upload/Upload.stories.tsx +252 -0
  152. package/src/components/Upload/Upload.tsx +92 -53
  153. package/src/components/VideoPlayer/VideoPlayer.tsx +3 -1
  154. package/src/components/index.ts +0 -4
  155. package/src/layouts/Grid/Grid.stories.tsx +10 -23
  156. package/src/layouts/Grid/Grid.tsx +20 -1
  157. package/src/layouts/Grid/GridCol.tsx +76 -48
  158. package/src/lib/composables/useAtomixGlass.ts +861 -44
  159. package/src/lib/composables/useBarChart.ts +21 -4
  160. package/src/lib/composables/useChart.ts +227 -370
  161. package/src/lib/composables/useChartExport.ts +19 -78
  162. package/src/lib/composables/useChartToolbar.ts +11 -21
  163. package/src/lib/composables/useEdgePanel.ts +125 -71
  164. package/src/lib/composables/useFooter.ts +3 -3
  165. package/src/lib/composables/useGlassContainer.ts +16 -7
  166. package/src/lib/composables/useLineChart.ts +11 -2
  167. package/src/lib/composables/usePieChart.ts +4 -14
  168. package/src/lib/composables/useRiver.ts +5 -0
  169. package/src/lib/composables/useSlider.ts +62 -24
  170. package/src/lib/composables/useVideoPlayer.ts +60 -63
  171. package/src/lib/constants/components.ts +147 -32
  172. package/src/lib/types/components.ts +355 -25
  173. package/src/lib/utils/displacement-generator.ts +55 -49
  174. package/src/lib/utils/icons.ts +1 -1
  175. package/src/lib/utils/index.ts +16 -10
  176. package/src/styles/01-settings/_settings.accordion.scss +19 -19
  177. package/src/styles/01-settings/_settings.animations.scss +5 -5
  178. package/src/styles/01-settings/_settings.avatar-group.scss +1 -1
  179. package/src/styles/01-settings/_settings.avatar.scss +17 -17
  180. package/src/styles/01-settings/_settings.background.scss +0 -3
  181. package/src/styles/01-settings/_settings.badge.scss +1 -1
  182. package/src/styles/01-settings/_settings.breadcrumb.scss +1 -1
  183. package/src/styles/01-settings/_settings.card.scss +1 -1
  184. package/src/styles/01-settings/_settings.chart.scss +65 -2
  185. package/src/styles/01-settings/_settings.dropdown.scss +1 -1
  186. package/src/styles/01-settings/_settings.edge-panel.scss +1 -1
  187. package/src/styles/01-settings/_settings.footer.scss +35 -42
  188. package/src/styles/01-settings/_settings.input.scss +1 -1
  189. package/src/styles/01-settings/_settings.list.scss +1 -1
  190. package/src/styles/01-settings/_settings.rating.scss +1 -1
  191. package/src/styles/01-settings/_settings.tabs.scss +1 -1
  192. package/src/styles/01-settings/_settings.upload.scss +6 -5
  193. package/src/styles/02-tools/_tools.animations.scss +4 -5
  194. package/src/styles/02-tools/_tools.background.scss +1 -13
  195. package/src/styles/02-tools/_tools.glass.scss +0 -1
  196. package/src/styles/02-tools/_tools.utility-api.scss +91 -48
  197. package/src/styles/03-generic/_generic.root.scss +1 -4
  198. package/src/styles/04-elements/_elements.body.scss +0 -1
  199. package/src/styles/06-components/_components.atomix-glass.scss +249 -0
  200. package/src/styles/06-components/_components.badge.scss +8 -23
  201. package/src/styles/06-components/_components.button.scss +8 -3
  202. package/src/styles/06-components/_components.callout.scss +10 -5
  203. package/src/styles/06-components/_components.card.scss +2 -14
  204. package/src/styles/06-components/_components.chart.scss +969 -1449
  205. package/src/styles/06-components/_components.dropdown.scss +19 -7
  206. package/src/styles/06-components/_components.edge-panel.scss +103 -0
  207. package/src/styles/06-components/_components.footer.scss +166 -85
  208. package/src/styles/06-components/_components.input.scss +8 -9
  209. package/src/styles/06-components/_components.list.scss +1 -0
  210. package/src/styles/06-components/_components.messages.scss +176 -0
  211. package/src/styles/06-components/_components.modal.scss +16 -4
  212. package/src/styles/06-components/_components.navbar.scss +12 -1
  213. package/src/styles/06-components/_components.side-menu.scss +5 -0
  214. package/src/styles/06-components/_components.skeleton.scss +8 -6
  215. package/src/styles/06-components/_components.upload.scss +219 -4
  216. package/src/styles/06-components/old.chart.styles.scss +1 -30
  217. package/src/styles/99-utilities/_index.scss +1 -0
  218. package/src/styles/99-utilities/_utilities.glass-fixes.scss +1 -0
  219. package/src/styles/99-utilities/_utilities.scss +1 -1
  220. package/src/components/AtomixGlass/AtomixGlass.stories.tsx +0 -3011
  221. package/src/components/AtomixGlass/AtomixGlassComprehensivePreview.stories.tsx +0 -1369
  222. package/src/components/Chart/AdvancedChart.tsx +0 -624
  223. package/src/components/Chart/LineChartNew.tsx +0 -167
  224. package/src/components/Chart/RealTimeChart.tsx +0 -436
  225. 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
- * Generate chart attributes for accessibility
132
- */
133
- const generateChartAttributes = useCallback((props: Partial<ChartProps>) => {
134
- const { loading, error, type } = props;
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
- * Calculate chart dimensions and scales with zoom and pan support
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
- (datasetIndex: number, pointIndex: number, multiSelect: boolean = false) => {
267
- setInteractionState(prev => {
268
- const pointKey = { datasetIndex, pointIndex };
269
- const existingIndex = prev.selectedPoints.findIndex(
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
- return {
283
- ...prev,
284
- selectedPoints: newSelectedPoints,
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
- * Enhanced zoom and pan handlers
159
+ * Zoom and pan handlers
293
160
  */
294
- const handleZoom = useCallback((delta: number, centerX: number, centerY?: number) => {
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 (touches.length === 1 && touches[0] && prev.isDragging && prev.dragStart) {
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
- // Reset states when no touches remain
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
- // Single touch remaining - switch from pinch to pan
520
- if (touches.length === 1 && touches[0] && prev.touchState.isPinching) {
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].x - rect.left;
523
- const y = touches[0].y - rect.top;
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: PointerEvent | React.PointerEvent) => {
550
- const isPen = event.pointerType === 'pen';
551
- const isTouch = event.pointerType === 'touch';
552
-
553
- if (isPen) {
554
- setInteractionState(prev => ({
555
- ...prev,
556
- penState: {
557
- isPen: true,
558
- pressure: event.pressure || 0,
559
- tiltX: (event as any).tiltX || 0,
560
- tiltY: (event as any).tiltY || 0,
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 handlePointerUp = useCallback(
635
- (event: PointerEvent | React.PointerEvent) => {
636
- if (event.pointerType === 'pen') {
637
- setInteractionState(prev => ({
638
- ...prev,
639
- isDragging: false,
640
- dragStart: null,
641
- penState: {
642
- isPen: false,
643
- pressure: 0,
644
- tiltX: 0,
645
- tiltY: 0,
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
- * Reset all interactions
657
- */
658
- const resetView = useCallback(() => {
659
- setInteractionState(prev => ({
660
- ...prev,
661
- zoomLevel: 1,
662
- panOffset: { x: 0, y: 0 },
663
- selectedPoints: [],
664
- crosshair: null,
665
- brushSelection: null,
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
- * Enhanced animation helpers
513
+ * Chart calculations and utilities
671
514
  */
672
- const startAnimation = useCallback((duration: number = 1000) => {
673
- setInteractionState(prev => ({ ...prev, isAnimating: true }));
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
- if (animationFrameRef.current) {
676
- cancelAnimationFrame(animationFrameRef.current);
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
- const startTime = Date.now();
680
- const animate = () => {
681
- const elapsed = Date.now() - startTime;
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
- if (progress < 1) {
685
- animationFrameRef.current = requestAnimationFrame(animate);
686
- } else {
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
- animationFrameRef.current = requestAnimationFrame(animate);
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 stopAnimation = useCallback(() => {
695
- if (animationFrameRef.current) {
696
- cancelAnimationFrame(animationFrameRef.current);
697
- animationFrameRef.current = null;
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
- // Cleanup animation on unmount
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
- hoveredPoint: interactionState.hoveredPoint,
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
- // Enhanced interactions
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 pointer handlers
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
- // Animation
758
- startAnimation,
759
- stopAnimation,
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
- label: dataset.label,
1026
- dataLength: dataset.data?.length || 0,
1027
- minValue: Math.min(
1028
- ...(dataset.data?.map(d => d.value).filter(v => typeof v === 'number') || [0])
1029
- ),
1030
- maxValue: Math.max(
1031
- ...(dataset.data?.map(d => d.value).filter(v => typeof v === 'number') || [0])
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