@rakeyshgidwani/roger-ui-bank-theme-stan-design 0.1.4 → 0.1.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 (164) hide show
  1. package/CHANGELOG.md +1 -1
  2. package/dist/index.d.ts +131 -131
  3. package/dist/index.esm.js +148 -148
  4. package/dist/index.js +148 -148
  5. package/dist/styles.css +1 -1
  6. package/package.json +1 -1
  7. package/src/components/ui/accessibility-demo.tsx +271 -0
  8. package/src/components/ui/advanced-component-architecture-demo.tsx +916 -0
  9. package/src/components/ui/advanced-transition-system-demo.tsx +670 -0
  10. package/src/components/ui/advanced-transition-system.tsx +395 -0
  11. package/src/components/ui/animation/animated-container.tsx +166 -0
  12. package/src/components/ui/animation/index.ts +19 -0
  13. package/src/components/ui/animation/staggered-container.tsx +68 -0
  14. package/src/components/ui/animation-demo.tsx +250 -0
  15. package/src/components/ui/badge.tsx +33 -0
  16. package/src/components/ui/battery-conscious-animation-demo.tsx +568 -0
  17. package/src/components/ui/border-radius-shadow-demo.tsx +187 -0
  18. package/src/components/ui/button.tsx +36 -0
  19. package/src/components/ui/card.tsx +207 -0
  20. package/src/components/ui/checkbox.tsx +30 -0
  21. package/src/components/ui/color-preview.tsx +411 -0
  22. package/src/components/ui/data-display/chart.tsx +653 -0
  23. package/src/components/ui/data-display/data-grid-simple.tsx +76 -0
  24. package/src/components/ui/data-display/data-grid.tsx +680 -0
  25. package/src/components/ui/data-display/list.tsx +456 -0
  26. package/src/components/ui/data-display/table.tsx +482 -0
  27. package/src/components/ui/data-display/timeline.tsx +441 -0
  28. package/src/components/ui/data-display/tree.tsx +602 -0
  29. package/src/components/ui/data-display/types.ts +536 -0
  30. package/src/components/ui/enterprise-mobile-experience-demo.tsx +749 -0
  31. package/src/components/ui/enterprise-mobile-experience.tsx +464 -0
  32. package/src/components/ui/feedback/alert.tsx +157 -0
  33. package/src/components/ui/feedback/progress.tsx +292 -0
  34. package/src/components/ui/feedback/skeleton.tsx +185 -0
  35. package/src/components/ui/feedback/toast.tsx +280 -0
  36. package/src/components/ui/feedback/types.ts +125 -0
  37. package/src/components/ui/font-preview.tsx +288 -0
  38. package/src/components/ui/form-demo.tsx +553 -0
  39. package/src/components/ui/hardware-acceleration-demo.tsx +547 -0
  40. package/src/components/ui/input.tsx +35 -0
  41. package/src/components/ui/label.tsx +16 -0
  42. package/src/components/ui/layout-demo.tsx +367 -0
  43. package/src/components/ui/layouts/adaptive-layout.tsx +139 -0
  44. package/src/components/ui/layouts/desktop-layout.tsx +224 -0
  45. package/src/components/ui/layouts/index.ts +10 -0
  46. package/src/components/ui/layouts/mobile-layout.tsx +162 -0
  47. package/src/components/ui/layouts/tablet-layout.tsx +197 -0
  48. package/src/components/ui/mobile-form-validation.tsx +451 -0
  49. package/src/components/ui/mobile-input-demo.tsx +201 -0
  50. package/src/components/ui/mobile-input.tsx +281 -0
  51. package/src/components/ui/mobile-skeleton-loading-demo.tsx +638 -0
  52. package/src/components/ui/navigation/breadcrumb.tsx +158 -0
  53. package/src/components/ui/navigation/index.ts +36 -0
  54. package/src/components/ui/navigation/menu.tsx +374 -0
  55. package/src/components/ui/navigation/navigation-demo.tsx +324 -0
  56. package/src/components/ui/navigation/pagination.tsx +272 -0
  57. package/src/components/ui/navigation/sidebar.tsx +383 -0
  58. package/src/components/ui/navigation/stepper.tsx +303 -0
  59. package/src/components/ui/navigation/tabs.tsx +205 -0
  60. package/src/components/ui/navigation/types.ts +299 -0
  61. package/src/components/ui/overlay/backdrop.tsx +81 -0
  62. package/src/components/ui/overlay/focus-manager.tsx +143 -0
  63. package/src/components/ui/overlay/index.ts +36 -0
  64. package/src/components/ui/overlay/modal.tsx +270 -0
  65. package/src/components/ui/overlay/overlay-manager.tsx +110 -0
  66. package/src/components/ui/overlay/popover.tsx +462 -0
  67. package/src/components/ui/overlay/portal.tsx +79 -0
  68. package/src/components/ui/overlay/tooltip.tsx +303 -0
  69. package/src/components/ui/overlay/types.ts +196 -0
  70. package/src/components/ui/performance-demo.tsx +596 -0
  71. package/src/components/ui/semantic-input-system-demo.tsx +502 -0
  72. package/src/components/ui/semantic-input-system-demo.tsx.disabled +873 -0
  73. package/src/components/ui/tablet-layout.tsx +192 -0
  74. package/src/components/ui/theme-customizer.tsx +386 -0
  75. package/src/components/ui/theme-preview.tsx +310 -0
  76. package/src/components/ui/theme-switcher.tsx +264 -0
  77. package/src/components/ui/theme-toggle.tsx +38 -0
  78. package/src/components/ui/token-demo.tsx +195 -0
  79. package/src/components/ui/touch-demo.tsx +462 -0
  80. package/src/components/ui/touch-friendly-interface-demo.tsx +519 -0
  81. package/src/components/ui/touch-friendly-interface.tsx +296 -0
  82. package/src/hooks/index.ts +190 -0
  83. package/src/hooks/use-accessibility-support.ts +518 -0
  84. package/src/hooks/use-adaptive-layout.ts +289 -0
  85. package/src/hooks/use-advanced-patterns.ts +294 -0
  86. package/src/hooks/use-advanced-transition-system.ts +393 -0
  87. package/src/hooks/use-animation-profile.ts +288 -0
  88. package/src/hooks/use-battery-animations.ts +384 -0
  89. package/src/hooks/use-battery-conscious-loading.ts +475 -0
  90. package/src/hooks/use-battery-optimization.ts +330 -0
  91. package/src/hooks/use-battery-status.ts +299 -0
  92. package/src/hooks/use-component-performance.ts +344 -0
  93. package/src/hooks/use-device-loading-states.ts +459 -0
  94. package/src/hooks/use-device.tsx +110 -0
  95. package/src/hooks/use-enterprise-mobile-experience.ts +488 -0
  96. package/src/hooks/use-form-feedback.ts +403 -0
  97. package/src/hooks/use-form-performance.ts +513 -0
  98. package/src/hooks/use-frame-rate.ts +251 -0
  99. package/src/hooks/use-gestures.ts +338 -0
  100. package/src/hooks/use-hardware-acceleration.ts +341 -0
  101. package/src/hooks/use-input-accessibility.ts +455 -0
  102. package/src/hooks/use-input-performance.ts +506 -0
  103. package/src/hooks/use-layout-performance.ts +319 -0
  104. package/src/hooks/use-loading-accessibility.ts +535 -0
  105. package/src/hooks/use-loading-performance.ts +473 -0
  106. package/src/hooks/use-memory-usage.ts +287 -0
  107. package/src/hooks/use-mobile-form-layout.ts +464 -0
  108. package/src/hooks/use-mobile-form-validation.ts +518 -0
  109. package/src/hooks/use-mobile-keyboard-optimization.ts +472 -0
  110. package/src/hooks/use-mobile-layout.ts +302 -0
  111. package/src/hooks/use-mobile-optimization.ts +406 -0
  112. package/src/hooks/use-mobile-skeleton.ts +402 -0
  113. package/src/hooks/use-mobile-touch.ts +414 -0
  114. package/src/hooks/use-performance-throttling.ts +348 -0
  115. package/src/hooks/use-performance.ts +316 -0
  116. package/src/hooks/use-reusable-architecture.ts +414 -0
  117. package/src/hooks/use-semantic-input-types.ts +357 -0
  118. package/src/hooks/use-semantic-input.ts +565 -0
  119. package/src/hooks/use-tablet-layout.ts +384 -0
  120. package/src/hooks/use-touch-friendly-input.ts +524 -0
  121. package/src/hooks/use-touch-friendly-interface.ts +331 -0
  122. package/src/hooks/use-touch-optimization.ts +375 -0
  123. package/src/index.ts +279 -279
  124. package/src/lib/utils.ts +6 -0
  125. package/src/themes/README.md +272 -0
  126. package/src/themes/ThemeContext.tsx +31 -0
  127. package/src/themes/ThemeProvider.tsx +232 -0
  128. package/src/themes/accessibility/index.ts +27 -0
  129. package/src/themes/accessibility.ts +259 -0
  130. package/src/themes/aria-patterns.ts +420 -0
  131. package/src/themes/base-themes.ts +55 -0
  132. package/src/themes/colorManager.ts +380 -0
  133. package/src/themes/examples/dark-theme.ts +154 -0
  134. package/src/themes/examples/minimal-theme.ts +108 -0
  135. package/src/themes/focus-management.ts +701 -0
  136. package/src/themes/fontLoader.ts +201 -0
  137. package/src/themes/high-contrast.ts +621 -0
  138. package/src/themes/index.ts +19 -0
  139. package/src/themes/inheritance.ts +227 -0
  140. package/src/themes/keyboard-navigation.ts +550 -0
  141. package/src/themes/motion-reduction.ts +662 -0
  142. package/src/themes/navigation.ts +238 -0
  143. package/src/themes/screen-reader.ts +645 -0
  144. package/src/themes/systemThemeDetector.ts +182 -0
  145. package/src/themes/themeCSSUpdater.ts +262 -0
  146. package/src/themes/themePersistence.ts +238 -0
  147. package/src/themes/themes/default.ts +586 -0
  148. package/src/themes/themes/harvey.ts +554 -0
  149. package/src/themes/themes/stan-design.ts +683 -0
  150. package/src/themes/types.ts +460 -0
  151. package/src/themes/useSystemTheme.ts +48 -0
  152. package/src/themes/useTheme.ts +87 -0
  153. package/src/themes/validation.ts +462 -0
  154. package/src/tokens/index.ts +34 -0
  155. package/src/tokens/tokenExporter.ts +397 -0
  156. package/src/tokens/tokenGenerator.ts +276 -0
  157. package/src/tokens/tokenManager.ts +248 -0
  158. package/src/tokens/tokenValidator.ts +543 -0
  159. package/src/tokens/types.ts +78 -0
  160. package/src/utils/bundle-analyzer.ts +260 -0
  161. package/src/utils/bundle-splitting.ts +483 -0
  162. package/src/utils/lazy-loading.ts +441 -0
  163. package/src/utils/performance-monitor.ts +513 -0
  164. package/src/utils/tree-shaking.ts +274 -0
@@ -0,0 +1,653 @@
1
+ import React, { useMemo, useRef } from 'react';
2
+ import { useTheme } from '../../../themes/useTheme';
3
+ import {
4
+ ChartProps,
5
+ ChartData
6
+ } from './types';
7
+
8
+ // Simple icon components (inline SVG)
9
+ const BarChartIcon: React.FC<{ className?: string }> = ({ className = 'w-4 h-4' }) => (
10
+ <svg
11
+ className={className}
12
+ fill="none"
13
+ stroke="currentColor"
14
+ viewBox="0 0 24 24"
15
+ >
16
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
17
+ </svg>
18
+ );
19
+
20
+ const LineChartIcon: React.FC<{ className?: string }> = ({ className = 'w-4 h-4' }) => (
21
+ <svg
22
+ className={className}
23
+ fill="none"
24
+ stroke="currentColor"
25
+ viewBox="0 0 24 24"
26
+ >
27
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M7 12l3-3 3 3 4-4" />
28
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 21l4-4 4-4 4-4" />
29
+ </svg>
30
+ );
31
+
32
+ const PieChartIcon: React.FC<{ className?: string }> = ({ className = 'w-4 h-4' }) => (
33
+ <svg
34
+ className={className}
35
+ fill="none"
36
+ stroke="currentColor"
37
+ viewBox="0 0 24 24"
38
+ >
39
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 2L12 12L22 12A10 10 0 0 0 12 2Z" />
40
+ <circle cx="12" cy="12" r="10" strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} />
41
+ </svg>
42
+ );
43
+
44
+ // Default color fallbacks for when theme is not available
45
+ const getDefaultColors = () => ({
46
+ surface: {
47
+ background: '#ffffff',
48
+ surface: '#f3f4f6',
49
+ border: '#d1d5db',
50
+ divider: '#e5e7eb'
51
+ },
52
+ text: {
53
+ primary: '#111827',
54
+ secondary: '#6b7280',
55
+ muted: '#9ca3af',
56
+ inverse: '#ffffff',
57
+ onPrimary: '#ffffff',
58
+ onSecondary: '#ffffff',
59
+ onSurface: '#111827'
60
+ },
61
+ interactive: {
62
+ hover: '#f3f4f6',
63
+ active: '#e5e7eb',
64
+ focus: '#3b82f6',
65
+ disabled: '#d1d5db'
66
+ },
67
+ primary: { 500: '#3b82f6' },
68
+ semantic: {
69
+ success: '#10b981',
70
+ warning: '#f59e0b',
71
+ error: '#ef4444',
72
+ info: '#3b82f6'
73
+ }
74
+ });
75
+
76
+ // Chart color palette
77
+ const getChartColors = (colors: any) => [
78
+ colors.primary[500],
79
+ colors.semantic.success,
80
+ colors.semantic.warning,
81
+ colors.semantic.error,
82
+ colors.semantic.info,
83
+ '#8b5cf6', // Purple
84
+ '#f97316', // Orange
85
+ '#06b6d4', // Cyan
86
+ '#84cc16', // Lime
87
+ '#ec4899', // Pink
88
+ ];
89
+
90
+ // Chart Legend Component
91
+ const ChartLegend: React.FC<{
92
+ datasets: ChartData['datasets'];
93
+ colors: any;
94
+ size: 'sm' | 'md' | 'lg';
95
+ }> = ({ datasets, colors, size }) => {
96
+ const chartColors = getChartColors(colors);
97
+
98
+ const sizeClasses = {
99
+ sm: 'chart__legend--sm',
100
+ md: 'chart__legend--md',
101
+ lg: 'chart__legend--lg'
102
+ };
103
+
104
+ const dotSizes = {
105
+ sm: 'chart__legend-color--sm',
106
+ md: 'chart__legend-color--md',
107
+ lg: 'chart__legend-color--lg'
108
+ };
109
+
110
+ return (
111
+ <div className={`chart__legend ${sizeClasses[size]}`}>
112
+ {datasets.map((dataset, index) => (
113
+ <div key={index} className="chart__legend-item">
114
+ <div
115
+ className={`chart__legend-color ${dotSizes[size]}`}
116
+ style={{
117
+ backgroundColor: dataset.color || chartColors[index % chartColors.length]
118
+ }}
119
+ />
120
+ <span className="chart__legend-label">
121
+ {dataset.label}
122
+ </span>
123
+ </div>
124
+ ))}
125
+ </div>
126
+ );
127
+ };
128
+
129
+ // Simple Bar Chart Component
130
+ const BarChart: React.FC<{
131
+ data: ChartData;
132
+ width: number;
133
+ height: number;
134
+ colors: any;
135
+ showValues?: boolean;
136
+ animate?: boolean;
137
+ }> = ({ data, width, height, colors, showValues = false, animate = false }) => {
138
+ const chartColors = getChartColors(colors);
139
+ const padding = 60;
140
+ const chartWidth = width - padding * 2;
141
+ const chartHeight = height - padding * 2;
142
+
143
+ // Calculate max value for scaling
144
+ const maxValue = useMemo(() => {
145
+ const allValues = data.datasets.flatMap(dataset =>
146
+ dataset.data.map(point => typeof point === 'number' ? point : point.y)
147
+ );
148
+ return Math.max(...allValues, 0);
149
+ }, [data]);
150
+
151
+ const barWidth = chartWidth / (data.labels.length * data.datasets.length);
152
+ const groupWidth = chartWidth / data.labels.length;
153
+
154
+ return (
155
+ <svg width={width} height={height} style={{ background: colors.surface.background }}>
156
+ {/* Grid lines */}
157
+ {[0, 0.25, 0.5, 0.75, 1].map((percent, index) => {
158
+ const y = padding + chartHeight * (1 - percent);
159
+ return (
160
+ <g key={index}>
161
+ <line
162
+ x1={padding}
163
+ y1={y}
164
+ x2={padding + chartWidth}
165
+ y2={y}
166
+ stroke={colors.surface.border}
167
+ strokeWidth={1}
168
+ strokeDasharray={percent === 0 ? '0' : '2,2'}
169
+ />
170
+ <text
171
+ x={padding - 10}
172
+ y={y + 4}
173
+ textAnchor="end"
174
+ fill={colors.text.muted}
175
+ fontSize="12"
176
+ >
177
+ {Math.round(maxValue * percent)}
178
+ </text>
179
+ </g>
180
+ );
181
+ })}
182
+
183
+ {/* Bars */}
184
+ {data.datasets.map((dataset, datasetIndex) => (
185
+ <g key={datasetIndex}>
186
+ {dataset.data.map((value, pointIndex) => {
187
+ const numericValue = typeof value === 'number' ? value : value.y;
188
+ const barHeight = (numericValue / maxValue) * chartHeight;
189
+ const x = padding + pointIndex * groupWidth + datasetIndex * barWidth;
190
+ const y = padding + chartHeight - barHeight;
191
+ const color = dataset.color || chartColors[datasetIndex % chartColors.length];
192
+
193
+ return (
194
+ <g key={pointIndex}>
195
+ <rect
196
+ x={x}
197
+ y={animate ? padding + chartHeight : y}
198
+ width={barWidth * 0.8}
199
+ height={animate ? 0 : barHeight}
200
+ fill={color}
201
+ rx={2}
202
+ >
203
+ {animate && (
204
+ <animate
205
+ attributeName="height"
206
+ from="0"
207
+ to={barHeight}
208
+ dur="0.8s"
209
+ begin={`${pointIndex * 0.1}s`}
210
+ fill="freeze"
211
+ />
212
+ )}
213
+ {animate && (
214
+ <animate
215
+ attributeName="y"
216
+ from={padding + chartHeight}
217
+ to={y}
218
+ dur="0.8s"
219
+ begin={`${pointIndex * 0.1}s`}
220
+ fill="freeze"
221
+ />
222
+ )}
223
+ </rect>
224
+
225
+ {/* Value labels */}
226
+ {showValues && (
227
+ <text
228
+ x={x + (barWidth * 0.8) / 2}
229
+ y={y - 5}
230
+ textAnchor="middle"
231
+ fill={colors.text.secondary}
232
+ fontSize="10"
233
+ >
234
+ {numericValue}
235
+ </text>
236
+ )}
237
+ </g>
238
+ );
239
+ })}
240
+ </g>
241
+ ))}
242
+
243
+ {/* X-axis labels */}
244
+ {data.labels.map((label, index) => (
245
+ <text
246
+ key={index}
247
+ x={padding + index * groupWidth + groupWidth / 2}
248
+ y={height - padding + 20}
249
+ textAnchor="middle"
250
+ fill={colors.text.secondary}
251
+ fontSize="12"
252
+ >
253
+ {label}
254
+ </text>
255
+ ))}
256
+ </svg>
257
+ );
258
+ };
259
+
260
+ // Simple Line Chart Component
261
+ const LineChart: React.FC<{
262
+ data: ChartData;
263
+ width: number;
264
+ height: number;
265
+ colors: any;
266
+ showPoints?: boolean;
267
+ animate?: boolean;
268
+ }> = ({ data, width, height, colors, showPoints = true, animate = false }) => {
269
+ const chartColors = getChartColors(colors);
270
+ const padding = 60;
271
+ const chartWidth = width - padding * 2;
272
+ const chartHeight = height - padding * 2;
273
+
274
+ // Calculate max value for scaling
275
+ const maxValue = useMemo(() => {
276
+ const allValues = data.datasets.flatMap(dataset =>
277
+ dataset.data.map(point => typeof point === 'number' ? point : point.y)
278
+ );
279
+ return Math.max(...allValues, 0);
280
+ }, [data]);
281
+
282
+ return (
283
+ <svg width={width} height={height} style={{ background: colors.surface.background }}>
284
+ {/* Grid lines */}
285
+ {[0, 0.25, 0.5, 0.75, 1].map((percent, index) => {
286
+ const y = padding + chartHeight * (1 - percent);
287
+ return (
288
+ <g key={index}>
289
+ <line
290
+ x1={padding}
291
+ y1={y}
292
+ x2={padding + chartWidth}
293
+ y2={y}
294
+ stroke={colors.surface.border}
295
+ strokeWidth={1}
296
+ strokeDasharray={percent === 0 ? '0' : '2,2'}
297
+ />
298
+ <text
299
+ x={padding - 10}
300
+ y={y + 4}
301
+ textAnchor="end"
302
+ fill={colors.text.muted}
303
+ fontSize="12"
304
+ >
305
+ {Math.round(maxValue * percent)}
306
+ </text>
307
+ </g>
308
+ );
309
+ })}
310
+
311
+ {/* Lines and points */}
312
+ {data.datasets.map((dataset, datasetIndex) => {
313
+ const color = dataset.color || chartColors[datasetIndex % chartColors.length];
314
+ const points = dataset.data.map((value, index) => {
315
+ const numericValue = typeof value === 'number' ? value : value.y;
316
+ const x = padding + (index / (data.labels.length - 1)) * chartWidth;
317
+ const y = padding + chartHeight - (numericValue / maxValue) * chartHeight;
318
+ return { x, y, value: numericValue };
319
+ });
320
+
321
+ // Create path for line
322
+ const pathData = points.reduce((path, point, index) => {
323
+ const command = index === 0 ? 'M' : 'L';
324
+ return `${path} ${command} ${point.x} ${point.y}`;
325
+ }, '');
326
+
327
+ return (
328
+ <g key={datasetIndex}>
329
+ {/* Line */}
330
+ <path
331
+ d={pathData}
332
+ fill="none"
333
+ stroke={color}
334
+ strokeWidth={2}
335
+ strokeLinecap="round"
336
+ strokeLinejoin="round"
337
+ >
338
+ {animate && (
339
+ <animate
340
+ attributeName="stroke-dasharray"
341
+ from={`0 ${chartWidth * 2}`}
342
+ to={`${chartWidth * 2} 0`}
343
+ dur="1.5s"
344
+ fill="freeze"
345
+ />
346
+ )}
347
+ </path>
348
+
349
+ {/* Points */}
350
+ {showPoints && points.map((point, pointIndex) => (
351
+ <circle
352
+ key={pointIndex}
353
+ cx={point.x}
354
+ cy={point.y}
355
+ r={4}
356
+ fill={color}
357
+ stroke={colors.surface.background}
358
+ strokeWidth={2}
359
+ >
360
+ {animate && (
361
+ <animate
362
+ attributeName="r"
363
+ from="0"
364
+ to="4"
365
+ dur="0.5s"
366
+ begin={`${1.5 + pointIndex * 0.1}s`}
367
+ fill="freeze"
368
+ />
369
+ )}
370
+ </circle>
371
+ ))}
372
+ </g>
373
+ );
374
+ })}
375
+
376
+ {/* X-axis labels */}
377
+ {data.labels.map((label, index) => (
378
+ <text
379
+ key={index}
380
+ x={padding + (index / (data.labels.length - 1)) * chartWidth}
381
+ y={height - padding + 20}
382
+ textAnchor="middle"
383
+ fill={colors.text.secondary}
384
+ fontSize="12"
385
+ >
386
+ {label}
387
+ </text>
388
+ ))}
389
+ </svg>
390
+ );
391
+ };
392
+
393
+ // Simple Pie Chart Component
394
+ const PieChart: React.FC<{
395
+ data: ChartData;
396
+ width: number;
397
+ height: number;
398
+ colors: any;
399
+ showLabels?: boolean;
400
+ animate?: boolean;
401
+ }> = ({ data, width, height, colors, showLabels = true, animate = false }) => {
402
+ const chartColors = getChartColors(colors);
403
+ const centerX = width / 2;
404
+ const centerY = height / 2;
405
+ const radius = Math.min(width, height) / 2 - 40;
406
+
407
+ // Calculate total value and angles
408
+ const dataset = data.datasets[0];
409
+ const values = dataset?.data.map(point => typeof point === 'number' ? point : point.y) || [];
410
+ const total = values.reduce((sum, value) => sum + value, 0);
411
+
412
+ let currentAngle = -Math.PI / 2; // Start at top
413
+ const segments = values.map((value, index) => {
414
+ const percentage = value / total;
415
+ const angle = percentage * 2 * Math.PI;
416
+ const startAngle = currentAngle;
417
+ const endAngle = currentAngle + angle;
418
+ currentAngle += angle;
419
+
420
+ // Calculate arc path
421
+ const x1 = centerX + radius * Math.cos(startAngle);
422
+ const y1 = centerY + radius * Math.sin(startAngle);
423
+ const x2 = centerX + radius * Math.cos(endAngle);
424
+ const y2 = centerY + radius * Math.sin(endAngle);
425
+
426
+ const largeArc = angle > Math.PI ? 1 : 0;
427
+ const pathData = `M ${centerX} ${centerY} L ${x1} ${y1} A ${radius} ${radius} 0 ${largeArc} 1 ${x2} ${y2} Z`;
428
+
429
+ // Label position
430
+ const labelAngle = startAngle + angle / 2;
431
+ const labelX = centerX + (radius * 0.7) * Math.cos(labelAngle);
432
+ const labelY = centerY + (radius * 0.7) * Math.sin(labelAngle);
433
+
434
+ return {
435
+ pathData,
436
+ color: chartColors[index % chartColors.length],
437
+ label: data.labels[index],
438
+ value,
439
+ percentage: Math.round(percentage * 100),
440
+ labelX,
441
+ labelY
442
+ };
443
+ });
444
+
445
+ return (
446
+ <svg width={width} height={height} style={{ background: colors.surface.background }}>
447
+ {segments.map((segment, index) => (
448
+ <g key={index}>
449
+ {/* Pie segment */}
450
+ <path
451
+ d={segment.pathData}
452
+ fill={segment.color}
453
+ stroke={colors.surface.background}
454
+ strokeWidth={2}
455
+ >
456
+ {animate && (
457
+ <animate
458
+ attributeName="opacity"
459
+ from="0"
460
+ to="1"
461
+ dur="0.5s"
462
+ begin={`${index * 0.2}s`}
463
+ fill="freeze"
464
+ />
465
+ )}
466
+ </path>
467
+
468
+ {/* Labels */}
469
+ {showLabels && segment.percentage > 5 && (
470
+ <text
471
+ x={segment.labelX}
472
+ y={segment.labelY}
473
+ textAnchor="middle"
474
+ fill={colors.text.onPrimary}
475
+ fontSize="12"
476
+ fontWeight="bold"
477
+ >
478
+ {segment.percentage}%
479
+ </text>
480
+ )}
481
+ </g>
482
+ ))}
483
+
484
+ {/* Center circle for doughnut effect */}
485
+ <circle
486
+ cx={centerX}
487
+ cy={centerY}
488
+ r={radius * 0.4}
489
+ fill={colors.surface.background}
490
+ stroke={colors.surface.border}
491
+ strokeWidth={1}
492
+ />
493
+ </svg>
494
+ );
495
+ };
496
+
497
+ // Main Chart Component
498
+ export const Chart: React.FC<ChartProps> = ({
499
+ type = 'bar',
500
+ data,
501
+ options = {},
502
+ height = 400,
503
+ width = '100%',
504
+
505
+ showLegend = true,
506
+ showValues = false,
507
+
508
+ animate = true,
509
+ loading = false,
510
+ error = null,
511
+ emptyMessage = 'No data available',
512
+ className = '',
513
+ theme = 'stan-design',
514
+ size = 'md',
515
+ variant = 'default'
516
+ }) => {
517
+ const { getTheme } = useTheme();
518
+ const themeConfig = getTheme(theme);
519
+ const colors = themeConfig?.colors || getDefaultColors();
520
+ const containerRef = useRef<HTMLDivElement>(null);
521
+
522
+ // Calculate responsive dimensions
523
+ const dimensions = useMemo(() => {
524
+ if (typeof width === 'number') {
525
+ return { width, height: typeof height === 'number' ? height : 400 };
526
+ }
527
+
528
+ // For responsive width, we'll use a default and let CSS handle it
529
+ return {
530
+ width: 600,
531
+ height: typeof height === 'number' ? height : 400
532
+ };
533
+ }, [width, height]);
534
+
535
+ // Chart type icon
536
+ const getChartIcon = () => {
537
+ switch (type) {
538
+ case 'line':
539
+ case 'area':
540
+ return <LineChartIcon className="chart__icon" />;
541
+ case 'pie':
542
+ case 'doughnut':
543
+ return <PieChartIcon className="chart__icon" />;
544
+ case 'bar':
545
+ default:
546
+ return <BarChartIcon className="chart__icon" />;
547
+ }
548
+ };
549
+
550
+ // Render appropriate chart component
551
+ const renderChart = () => {
552
+ const chartProps = {
553
+ data,
554
+ width: dimensions.width,
555
+ height: dimensions.height,
556
+ colors,
557
+ animate,
558
+ ...options
559
+ };
560
+
561
+ switch (type) {
562
+ case 'line':
563
+ case 'area':
564
+ return <LineChart {...chartProps} showPoints={options.showPoints} />;
565
+ case 'pie':
566
+ case 'doughnut':
567
+ return <PieChart {...chartProps} showLabels={options.showLabels} />;
568
+ case 'bar':
569
+ default:
570
+ return <BarChart {...chartProps} showValues={showValues} />;
571
+ }
572
+ };
573
+
574
+ if (loading) {
575
+ return (
576
+ <div className={`chart__loading chart__container--${size} chart__container--${variant} ${className}`}>
577
+ <div className="chart__loading-spinner"></div>
578
+ <span>Loading chart...</span>
579
+ </div>
580
+ );
581
+ }
582
+
583
+ if (error) {
584
+ return (
585
+ <div className={`chart__error chart__container--${size} chart__container--${variant} ${className}`}>
586
+ <div className="chart__error-content">
587
+ <div className="chart__error-header">
588
+ {getChartIcon()}
589
+ <span>Chart Error</span>
590
+ </div>
591
+ <p>{error}</p>
592
+ </div>
593
+ </div>
594
+ );
595
+ }
596
+
597
+ if (!data.datasets.length || !data.labels.length) {
598
+ return (
599
+ <div className={`chart__empty chart__container--${size} chart__container--${variant} ${className}`}>
600
+ <div className="chart__empty-content">
601
+ <div className="chart__empty-icon">
602
+ {getChartIcon()}
603
+ </div>
604
+ <span>No Data</span>
605
+ <p>{emptyMessage}</p>
606
+ </div>
607
+ </div>
608
+ );
609
+ }
610
+
611
+ return (
612
+ <div
613
+ ref={containerRef}
614
+ className={`chart__container ${className || ''}`}
615
+ >
616
+ {/* Chart Title */}
617
+ {options.title && (
618
+ <div className="chart__header">
619
+ <h3 className="chart__title">
620
+ {options.title}
621
+ </h3>
622
+ {options.subtitle && (
623
+ <p className="chart__subtitle">
624
+ {options.subtitle}
625
+ </p>
626
+ )}
627
+ </div>
628
+ )}
629
+
630
+ {/* Chart Container */}
631
+ <div
632
+ className="chart__canvas"
633
+ style={{
634
+ width: typeof width === 'string' ? width : `${width}px`,
635
+ maxWidth: '100%'
636
+ }}
637
+ >
638
+ {renderChart()}
639
+ </div>
640
+
641
+ {/* Legend */}
642
+ {showLegend && data.datasets.length > 1 && (
643
+ <ChartLegend
644
+ datasets={data.datasets}
645
+ colors={colors}
646
+ size={size}
647
+ />
648
+ )}
649
+ </div>
650
+ );
651
+ };
652
+
653
+ export default Chart;