@spteck/fluentui-react-charts 1.0.6 → 1.0.8

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 (105) hide show
  1. package/dist/charts/BarChart/BarChart.d.ts +2 -1
  2. package/dist/charts/ComboChart/ComboChart.d.ts +2 -1
  3. package/dist/charts/Doughnut/DoughnutChart.d.ts +2 -1
  4. package/dist/charts/PieChart/PieChart.d.ts +2 -1
  5. package/dist/charts/areaChart/AreaChart.d.ts +2 -1
  6. package/dist/charts/barHorizontalChart/BarHotizontalChart.d.ts +2 -1
  7. package/dist/charts/bubbleChart/BubbleChart.d.ts +2 -1
  8. package/dist/charts/floatBarChart/FloatBarChart.d.ts +2 -1
  9. package/dist/charts/index.d.ts +14 -0
  10. package/dist/charts/lineChart/LineChart.d.ts +2 -1
  11. package/dist/charts/polarChart/PolarChart.d.ts +2 -1
  12. package/dist/charts/radarChart/RadarChart.d.ts +2 -1
  13. package/dist/charts/scatterChart/ScatterChart.d.ts +2 -1
  14. package/dist/charts/stackedLineChart/StackedLineChart.d.ts +2 -1
  15. package/dist/charts/steamChart/SteamChart.d.ts +2 -1
  16. package/dist/components/index.d.ts +0 -14
  17. package/dist/fluentui-react-charts.cjs.development.js +1367 -1066
  18. package/dist/fluentui-react-charts.cjs.development.js.map +1 -1
  19. package/dist/fluentui-react-charts.cjs.production.min.js +1 -1
  20. package/dist/fluentui-react-charts.cjs.production.min.js.map +1 -1
  21. package/dist/fluentui-react-charts.esm.js +1353 -1066
  22. package/dist/fluentui-react-charts.esm.js.map +1 -1
  23. package/dist/index.d.ts +1 -0
  24. package/package.json +4 -4
  25. package/src/assets/sample1.png +0 -0
  26. package/src/assets/sample2.png +0 -0
  27. package/src/assets/sample3.png +0 -0
  28. package/src/charts/BarChart/BarChart.tsx +0 -227
  29. package/src/charts/BarChart/README.MD +0 -335
  30. package/src/charts/BarChart/index.ts +0 -1
  31. package/src/charts/ComboChart/ComboChart.tsx +0 -209
  32. package/src/charts/ComboChart/README.MD +0 -347
  33. package/src/charts/ComboChart/index.ts +0 -1
  34. package/src/charts/Doughnut/DoughnutChart.tsx +0 -152
  35. package/src/charts/Doughnut/README.MD +0 -296
  36. package/src/charts/Doughnut/index.ts +0 -1
  37. package/src/charts/PieChart/PieChart.tsx +0 -148
  38. package/src/charts/PieChart/README.MD +0 -315
  39. package/src/charts/PieChart/index.ts +0 -1
  40. package/src/charts/areaChart/AreaChart.tsx +0 -195
  41. package/src/charts/areaChart/README.MD +0 -236
  42. package/src/charts/areaChart/index.ts +0 -1
  43. package/src/charts/barHorizontalChart/BarHotizontalChart.tsx +0 -200
  44. package/src/charts/barHorizontalChart/README.MD +0 -278
  45. package/src/charts/barHorizontalChart/index.ts +0 -2
  46. package/src/charts/bubbleChart/BubbleChart.tsx +0 -184
  47. package/src/charts/bubbleChart/README.MD +0 -275
  48. package/src/charts/bubbleChart/index.ts +0 -1
  49. package/src/charts/floatBarChart/FloatBarChart.tsx +0 -178
  50. package/src/charts/floatBarChart/README.MD +0 -354
  51. package/src/charts/floatBarChart/index.ts +0 -1
  52. package/src/charts/lineChart/LineChart.tsx +0 -200
  53. package/src/charts/lineChart/README.MD +0 -354
  54. package/src/charts/lineChart/index.ts +0 -1
  55. package/src/charts/polarChart/PolarChart.tsx +0 -161
  56. package/src/charts/polarChart/README.MD +0 -336
  57. package/src/charts/polarChart/index.ts +0 -1
  58. package/src/charts/radarChart/README.MD +0 -388
  59. package/src/charts/radarChart/RadarChart.tsx +0 -173
  60. package/src/charts/radarChart/index.ts +0 -1
  61. package/src/charts/scatterChart/README.MD +0 -335
  62. package/src/charts/scatterChart/ScatterChart.tsx +0 -155
  63. package/src/charts/scatterChart/index.ts +0 -1
  64. package/src/charts/stackedLineChart/README.MD +0 -396
  65. package/src/charts/stackedLineChart/StackedLineChart.tsx +0 -188
  66. package/src/charts/stackedLineChart/index.ts +0 -1
  67. package/src/charts/steamChart/README.MD +0 -414
  68. package/src/charts/steamChart/SteamChart.tsx +0 -236
  69. package/src/charts/steamChart/index.ts +0 -1
  70. package/src/components/RenderLabel/RenderLabel.tsx +0 -39
  71. package/src/components/RenderLabel/index.ts +0 -2
  72. package/src/components/RenderLabel/useRenderLabelStylesStyles.ts +0 -25
  73. package/src/components/RenderLegend/RenderLegend.tsx +0 -40
  74. package/src/components/RenderTooltip/RenderTooltip.tsx +0 -111
  75. package/src/components/buttonMenu/ButtonMenu.tsx +0 -186
  76. package/src/components/buttonMenu/IButtonMenuOption.ts +0 -9
  77. package/src/components/buttonMenu/IButtonMenuProps.tsx +0 -40
  78. package/src/components/dashboard/DashBoard.tsx +0 -314
  79. package/src/components/dashboard/ExampleDashboardUsage.tsx +0 -114
  80. package/src/components/dashboard/IDashboardProps.tsx +0 -11
  81. package/src/components/dashboard/NoDashboards.tsx +0 -26
  82. package/src/components/dashboard/index.ts +0 -3
  83. package/src/components/dashboard/selectZoom/SelectZoom.tsx +0 -189
  84. package/src/components/dashboard/useDashboardStyles.ts +0 -76
  85. package/src/components/index.ts +0 -17
  86. package/src/components/legendContainer/LegendContainer.tsx +0 -118
  87. package/src/components/legendeButton/LegendButton.tsx +0 -57
  88. package/src/components/renderSliceLegend/RenderSliceLegend.tsx +0 -46
  89. package/src/components/renderValueLegend/RenderValueLegend.tsx +0 -43
  90. package/src/components/stack/IStackProps.tsx +0 -94
  91. package/src/components/stack/Stack.tsx +0 -103
  92. package/src/components/svgImages/BusinessReportIcon.tsx +0 -218
  93. package/src/components/themeProvider/ThemeProvider.tsx +0 -48
  94. package/src/constants/Constants.tsx +0 -23
  95. package/src/graphGlobalStyles/useGraphGlobalStyles.ts +0 -28
  96. package/src/hooks/index.ts +0 -1
  97. package/src/hooks/useChartFactory.tsx +0 -136
  98. package/src/hooks/useChartUtils.tsx +0 -187
  99. package/src/hooks/useIndexedDBCache.ts +0 -122
  100. package/src/hooks/useResponsiveLegend.ts +0 -35
  101. package/src/index.tsx +0 -5
  102. package/src/models/ChartDatum.ts +0 -4
  103. package/src/models/ICardChartContainer.tsx +0 -11
  104. package/src/models/IChart.ts +0 -50
  105. package/src/models/index.ts +0 -3
@@ -1,209 +0,0 @@
1
- import {
2
- BarElement,
3
- CategoryScale,
4
- Chart as ChartJS,
5
- ChartOptions,
6
- Legend,
7
- LineElement,
8
- LinearScale,
9
- PointElement,
10
- Title,
11
- Tooltip,
12
- } from 'chart.js';
13
- import React, { useMemo, useState } from 'react';
14
- import { Theme, webLightTheme } from '@fluentui/react-components';
15
-
16
- import { Chart } from 'react-chartjs-2';
17
- import ChartDataLabels from 'chartjs-plugin-datalabels';
18
- import RenderLegend from '../../components/RenderLegend/RenderLegend';
19
- import { useChartUtils } from '../../hooks/useChartUtils';
20
- import { useGraphGlobalStyles } from '../../graphGlobalStyles/useGraphGlobalStyles';
21
-
22
- ChartJS.register(ChartDataLabels);
23
- ChartJS.register(
24
- CategoryScale,
25
- LinearScale,
26
- BarElement,
27
- LineElement,
28
- PointElement,
29
- Tooltip,
30
- Legend,
31
- Title
32
- );
33
-
34
- export interface ComboChartProps<T> {
35
- data: {
36
- label: string;
37
- type: 'bar' | 'line';
38
- data: T[];
39
- yAxisID?: string;
40
- }[];
41
- getPrimary: (datum: T) => string | number;
42
- getSecondary: (datum: T) => number;
43
- title?: string;
44
- showDataLabels?: boolean;
45
- theme?: Theme;
46
- }
47
-
48
- export default function ComboChart<T extends object>({
49
- data,
50
- getPrimary,
51
- getSecondary,
52
- title,
53
- showDataLabels = false,
54
- theme = webLightTheme,
55
- }: ComboChartProps<T>) {
56
- const [visibleSeries, setVisibleSeries] = useState(() =>
57
- data.map(s => s.label)
58
- );
59
-
60
- const styles = useGraphGlobalStyles();
61
- const { lightenColor, getFluentPalette, createFluentTooltip } = useChartUtils(theme);
62
-
63
- const seriesColors = useMemo(() => {
64
- return data.reduce((acc, series, idx) => {
65
- const base = getFluentPalette(theme)[
66
- idx % getFluentPalette(theme).length
67
- ];
68
- acc[series.label] = lightenColor(base, 0.3);
69
- return acc;
70
- }, {} as Record<string, string>);
71
- }, [data, theme]);
72
-
73
- const toggleSeries = (label: string) => {
74
- setVisibleSeries(prev => {
75
- const isVisible = prev.includes(label);
76
- const next = isVisible ? prev.filter(l => l !== label) : [...prev, label];
77
- return next.length === 0 ? [data[0].label] : next;
78
- });
79
- };
80
-
81
- const allCategories = useMemo(() => {
82
- const set = new Set<string | number>();
83
- data.forEach(series => series.data.forEach(d => set.add(getPrimary(d))));
84
- return Array.from(set);
85
- }, [data, getPrimary]);
86
-
87
- const chartData = useMemo(() => {
88
- const sortedSeries = data
89
- .filter(series => visibleSeries.includes(series.label))
90
- .sort((a, b) => {
91
- // Ensure bars come before lines
92
- if (a.type === 'bar' && b.type === 'line') return -1;
93
- if (a.type === 'line' && b.type === 'bar') return 1;
94
- return 0;
95
- });
96
-
97
- return {
98
- labels: allCategories,
99
- datasets: sortedSeries.map(series => ({
100
- type: series.type,
101
- label: series.label,
102
- yAxisID: series.yAxisID ?? 'y',
103
- data: allCategories.map(cat => {
104
- const found = series.data.find(d => getPrimary(d) === cat);
105
- return found ? getSecondary(found) : 0;
106
- }),
107
- backgroundColor: seriesColors[series.label],
108
- borderColor: seriesColors[series.label],
109
- fill: series.type === 'bar',
110
- tension: series.type === 'line' ? 0.4 : 0,
111
- pointRadius: series.type === 'line' ? 3 : 0,
112
- borderWidth: series.type === 'line' ? 2 : 1,
113
- order: series.type === 'bar' ? 1 : 0, // Ensure bars are drawn first
114
- })),
115
- };
116
- }, [
117
- data,
118
- visibleSeries,
119
- allCategories,
120
- getPrimary,
121
- getSecondary,
122
- seriesColors,
123
- ]);
124
-
125
- const { fontFamily, fontSize, labelColor, gridColor } = useMemo(() => ({
126
- fontFamily: theme.fontFamilyBase,
127
- fontSize: parseInt(theme.fontSizeBase200.replace('px', '')) || 14,
128
- labelColor: theme.colorNeutralForeground1,
129
- gridColor: theme.colorNeutralStroke2,
130
- }), [theme]);
131
-
132
-
133
-
134
- const options: ChartOptions<'bar' | 'line'> = useMemo(() => ( {
135
- responsive: true,
136
- maintainAspectRatio: false,
137
- plugins: {
138
- title: {
139
- display: !!title,
140
- text: title,
141
- font: {
142
- size: 14,
143
- family: theme.fontFamilyBase,
144
- weight: theme.fontWeightSemibold,
145
- },
146
- color: theme.colorNeutralForeground1,
147
- padding: {
148
- top: 20,
149
- bottom: 20,
150
- },
151
- },
152
- datalabels: {
153
- display: showDataLabels,
154
- color: theme.colorNeutralForeground1,
155
- font: {
156
- family: theme.fontFamilyBase,
157
- size: parseInt(theme.fontSizeBase200.replace('px', '')) || 14,
158
- },
159
- },
160
- legend: { display: false },
161
- tooltip: createFluentTooltip<'bar' | 'line'>(theme),
162
- },
163
- scales: {
164
- x: {
165
- ticks: {
166
- color: labelColor,
167
- font: { family: fontFamily, size: fontSize },
168
- },
169
- grid: { color: gridColor },
170
- },
171
- y: {
172
- position: 'left',
173
- ticks: {
174
- color: labelColor,
175
- font: { family: fontFamily, size: fontSize },
176
- },
177
- grid: { color: gridColor },
178
- stacked: false,
179
- },
180
- },
181
- }), [
182
- title,
183
- showDataLabels,
184
- theme,
185
- fontFamily,
186
- fontSize,
187
- labelColor,
188
- gridColor,
189
- createFluentTooltip,
190
- ]);
191
-
192
- return (
193
- <>
194
- <div className={styles.chartWithLegend}>
195
- <div className={styles.chartArea}>
196
- <Chart type="bar" data={chartData} options={options} />
197
- </div>
198
- <div className={styles.legendArea}>
199
- <RenderLegend
200
- data={data}
201
- visibleSeries={visibleSeries}
202
- seriesColors={seriesColors}
203
- toggleSeries={toggleSeries}
204
- />
205
- </div>
206
- </div>
207
- </>
208
- );
209
- }
@@ -1,347 +0,0 @@
1
- # ComboChart Component
2
-
3
- A versatile combination chart component built with Chart.js and Fluent UI React. This component allows you to display multiple chart types (bars and lines) in a single visualization, making it perfect for comparing different types of metrics or showing relationships between various data series.
4
-
5
- ## Features
6
-
7
- - **Mixed Chart Types**: Combine bar and line charts in a single visualization
8
- - **Dual Y-Axes Support**: Optional secondary y-axis for different data scales
9
- - **Multiple Series Support**: Display multiple data series with different chart types
10
- - **Interactive Legend**: Toggle series visibility with click interactions
11
- - **Fluent UI Integration**: Seamless integration with Fluent UI themes and design system
12
- - **Data Labels**: Optional display of values directly on chart elements
13
- - **Responsive Design**: Automatically adapts to container dimensions
14
- - **TypeScript Support**: Full TypeScript support with generic types
15
- - **Custom Tooltips**: Rich tooltips showing detailed information
16
- - **Smart Rendering Order**: Bars render behind lines for optimal visibility
17
-
18
- ## Installation
19
-
20
- ```bash
21
- npm install chart.js react-chartjs-2 chartjs-plugin-datalabels @fluentui/react-components
22
- ```
23
-
24
- ## Basic Usage
25
-
26
- ```tsx
27
- import React from 'react';
28
- import { ComboChart } from './components/ComboChart/ComboChart';
29
- import { webLightTheme } from '@fluentui/react-components';
30
-
31
- interface SalesData {
32
- month: string;
33
- revenue: number;
34
- units: number;
35
- }
36
-
37
- const salesData: SalesData[] = [
38
- { month: 'Jan', revenue: 150000, units: 1200 },
39
- { month: 'Feb', revenue: 180000, units: 1450 },
40
- { month: 'Mar', revenue: 165000, units: 1320 },
41
- { month: 'Apr', revenue: 200000, units: 1600 },
42
- ];
43
-
44
- function App() {
45
- return (
46
- <div style={{ width: '800px', height: '400px' }}>
47
- <ComboChart
48
- data={[
49
- {
50
- label: 'Revenue',
51
- type: 'bar',
52
- data: salesData
53
- },
54
- {
55
- label: 'Units Sold',
56
- type: 'line',
57
- data: salesData
58
- }
59
- ]}
60
- getPrimary={(datum) => datum.month}
61
- getSecondary={(datum) => datum.revenue} // Will use different values per series
62
- title="Revenue and Units Sold Over Time"
63
- theme={webLightTheme}
64
- />
65
- </div>
66
- );
67
- }
68
- ```
69
-
70
- ## Props
71
-
72
- ### ComboChartProps<T>
73
-
74
- | Prop | Type | Required | Default | Description |
75
- |------|------|----------|---------|-------------|
76
- | `data` | `{ label: string; type: 'bar' \| 'line'; data: T[]; yAxisID?: string }[]` | Yes | - | Array of data series with labels, chart types, and optional y-axis assignment |
77
- | `getPrimary` | `(datum: T) => string \| number` | Yes | - | Function to extract the x-axis category from each data point |
78
- | `getSecondary` | `(datum: T) => number` | Yes | - | Function to extract the y-axis value from each data point |
79
- | `title` | `string` | No | - | Chart title displayed at the top |
80
- | `showDataLabels` | `boolean` | No | `false` | Whether to show data values on chart elements |
81
- | `theme` | `Theme` | No | `webLightTheme` | Fluent UI theme object for styling |
82
-
83
- ## Advanced Usage
84
-
85
- ### Revenue vs Performance Metrics
86
-
87
- ```tsx
88
- interface BusinessMetrics {
89
- quarter: string;
90
- revenue: number;
91
- customerSatisfaction: number;
92
- marketShare: number;
93
- }
94
-
95
- const businessData: BusinessMetrics[] = [
96
- { quarter: 'Q1 2024', revenue: 2500000, customerSatisfaction: 85, marketShare: 15.2 },
97
- { quarter: 'Q2 2024', revenue: 2800000, customerSatisfaction: 87, marketShare: 16.1 },
98
- { quarter: 'Q3 2024', revenue: 3100000, customerSatisfaction: 89, marketShare: 17.3 },
99
- { quarter: 'Q4 2024', revenue: 3400000, customerSatisfaction: 91, marketShare: 18.7 },
100
- ];
101
-
102
- <ComboChart
103
- data={[
104
- {
105
- label: 'Revenue ($)',
106
- type: 'bar',
107
- data: businessData.map(d => ({ quarter: d.quarter, value: d.revenue })),
108
- yAxisID: 'y'
109
- },
110
- {
111
- label: 'Customer Satisfaction (%)',
112
- type: 'line',
113
- data: businessData.map(d => ({ quarter: d.quarter, value: d.customerSatisfaction })),
114
- yAxisID: 'y1'
115
- },
116
- {
117
- label: 'Market Share (%)',
118
- type: 'line',
119
- data: businessData.map(d => ({ quarter: d.quarter, value: d.marketShare })),
120
- yAxisID: 'y1'
121
- }
122
- ]}
123
- getPrimary={(datum) => datum.quarter}
124
- getSecondary={(datum) => datum.value}
125
- title="Business Performance Dashboard"
126
- showDataLabels={true}
127
- theme={webLightTheme}
128
- />
129
- ```
130
-
131
- ### Sales Performance Analysis
132
-
133
- ```tsx
134
- interface SalesPerformance {
135
- region: string;
136
- actualSales: number;
137
- targetSales: number;
138
- growthRate: number;
139
- }
140
-
141
- const performanceData: SalesPerformance[] = [
142
- { region: 'North America', actualSales: 450000, targetSales: 420000, growthRate: 12.5 },
143
- { region: 'Europe', actualSales: 380000, targetSales: 400000, growthRate: 8.2 },
144
- { region: 'Asia Pacific', actualSales: 520000, targetSales: 480000, growthRate: 15.7 },
145
- { region: 'Latin America', actualSales: 280000, targetSales: 300000, growthRate: 6.3 },
146
- ];
147
-
148
- <ComboChart
149
- data={[
150
- {
151
- label: 'Actual Sales',
152
- type: 'bar',
153
- data: performanceData.map(d => ({ region: d.region, value: d.actualSales }))
154
- },
155
- {
156
- label: 'Target Sales',
157
- type: 'bar',
158
- data: performanceData.map(d => ({ region: d.region, value: d.targetSales }))
159
- },
160
- {
161
- label: 'Growth Rate (%)',
162
- type: 'line',
163
- data: performanceData.map(d => ({ region: d.region, value: d.growthRate })),
164
- yAxisID: 'y1'
165
- }
166
- ]}
167
- getPrimary={(datum) => datum.region}
168
- getSecondary={(datum) => datum.value}
169
- title="Regional Sales Performance vs Growth"
170
- theme={webLightTheme}
171
- />
172
- ```
173
-
174
- ### Financial KPIs Dashboard
175
-
176
- ```tsx
177
- interface FinancialKPIs {
178
- month: string;
179
- revenue: number;
180
- expenses: number;
181
- profitMargin: number;
182
- cashFlow: number;
183
- }
184
-
185
- <ComboChart
186
- data={[
187
- {
188
- label: 'Revenue',
189
- type: 'bar',
190
- data: financialData.map(d => ({ month: d.month, value: d.revenue }))
191
- },
192
- {
193
- label: 'Expenses',
194
- type: 'bar',
195
- data: financialData.map(d => ({ month: d.month, value: d.expenses }))
196
- },
197
- {
198
- label: 'Profit Margin (%)',
199
- type: 'line',
200
- data: financialData.map(d => ({ month: d.month, value: d.profitMargin })),
201
- yAxisID: 'y1'
202
- },
203
- {
204
- label: 'Cash Flow',
205
- type: 'line',
206
- data: financialData.map(d => ({ month: d.month, value: d.cashFlow }))
207
- }
208
- ]}
209
- getPrimary={(datum) => datum.month}
210
- getSecondary={(datum) => datum.value}
211
- title="Financial Performance Overview"
212
- showDataLabels={true}
213
- />
214
- ```
215
-
216
- ## Data Structure
217
-
218
- The component expects data in the following format:
219
-
220
- ```tsx
221
- interface ChartSeries<T> {
222
- label: string; // Series name (appears in legend)
223
- type: 'bar' | 'line'; // Chart type for this series
224
- data: T[]; // Array of data points
225
- yAxisID?: string; // Optional y-axis assignment ('y' or 'y1')
226
- }
227
- ```
228
-
229
- Each data point `T` should contain:
230
-
231
- - A primary value (x-axis category) - string or number
232
- - A secondary value (y-axis value) - number
233
-
234
- ## Chart Type Characteristics
235
-
236
- ### Bar Series
237
-
238
- - Rendered as vertical bars
239
- - Ideal for discrete categories and comparisons
240
- - Supports stacking (when all series are bars)
241
- - Better for showing absolute values
242
-
243
- ### Line Series
244
-
245
- - Rendered as connected points with lines
246
- - Perfect for trends and continuous data
247
- - Smooth curves with configurable tension
248
- - Better for showing changes over time
249
-
250
- ## Y-Axis Configuration
251
-
252
- ### Single Y-Axis (Default)
253
-
254
- All series share the same y-axis scale:
255
-
256
- ```tsx
257
- data={[
258
- { label: 'Series 1', type: 'bar', data: data1 },
259
- { label: 'Series 2', type: 'line', data: data2 }
260
- ]}
261
- ```
262
-
263
- ### Dual Y-Axes
264
-
265
- Different series can use different y-axis scales:
266
-
267
- ```tsx
268
- data={[
269
- { label: 'Revenue ($)', type: 'bar', data: revenueData, yAxisID: 'y' },
270
- { label: 'Growth (%)', type: 'line', data: growthData, yAxisID: 'y1' }
271
- ]}
272
- ```
273
-
274
- ## Rendering Order
275
-
276
- The component automatically optimizes rendering order:
277
-
278
- 1. **Bar series** are rendered first (background layer)
279
- 2. **Line series** are rendered on top (foreground layer)
280
- 3. This ensures lines are always visible over bars
281
-
282
- ## Interactive Features
283
-
284
- ### Legend Controls
285
-
286
- - Click legend items to show/hide series
287
- - Visual feedback on hover states
288
- - At least one series must remain visible
289
- - Colors automatically assigned from theme palette
290
-
291
- ### Smart Data Handling
292
-
293
- - Automatic category aggregation across all series
294
- - Missing data points default to 0
295
- - Efficient data transformation and caching
296
-
297
- ### Responsive Behavior
298
-
299
- - Chart automatically resizes to container dimensions
300
- - Legend adapts to available space
301
- - Maintains readability across different screen sizes
302
-
303
- ## Styling and Theme Integration
304
-
305
- The component uses Fluent UI theme tokens:
306
-
307
- ```tsx
308
- // Bar styling
309
- backgroundColor: Theme color palette
310
- borderWidth: 1
311
-
312
- // Line styling
313
- borderColor: Theme color palette
314
- borderWidth: 2
315
- tension: 0.4 (smooth curves)
316
- pointRadius: 3
317
-
318
- // Typography
319
- fontFamily: theme.fontFamilyBase
320
- fontSize: theme.fontSizeBase200
321
- color: theme.colorNeutralForeground1
322
- ```
323
-
324
- ## Performance Optimizations
325
-
326
- ````tsx
327
- // Memoized color calculations
328
- const seriesColors = useMemo(() => {
329
- return data.reduce((acc, series, idx) => {
330
- const base = getFluentPalette(theme)[idx % getFluentPalette(theme).length];
331
- acc[series.label] = lightenColor(base, 0.3);
332
- return acc;
333
- }, {} as Record<string, string>);
334
- }, [data, theme]);
335
-
336
- // Memoized chart data with optimized sorting
337
- const chartData = useMemo(() => {
338
- const sortedSeries = data
339
- .filter(series => visibleSeries.includes(series.label))
340
- .sort((a, b) => {
341
- // Ensure bars come before lines
342
- if (a.type === 'bar' && b.type === 'line') return -1;
343
- if (a.type === 'line' && b.type === 'bar') return 1;
344
- return 0;
345
- });
346
- // ... rest of transformation
347
- }, [data, visibleSeries, allCategories, getPrimary, getSecondary, seriesColors]);
@@ -1 +0,0 @@
1
- export * from './ComboChart';
@@ -1,152 +0,0 @@
1
- import {
2
- ArcElement,
3
- Chart as ChartJS,
4
- ChartOptions,
5
- Legend,
6
- Title,
7
- Tooltip,
8
- } from 'chart.js';
9
- import React, { useMemo, useState } from 'react';
10
- import { Theme, webLightTheme } from '@fluentui/react-components';
11
-
12
- import ChartDataLabels from 'chartjs-plugin-datalabels';
13
- import { Doughnut } from 'react-chartjs-2';
14
- import RenderValueLegend from '../../components/renderValueLegend/RenderValueLegend';
15
- import { useChartUtils } from '../../hooks/useChartUtils';
16
- import { useGraphGlobalStyles } from '../../graphGlobalStyles/useGraphGlobalStyles';
17
-
18
- ChartJS.register(ChartDataLabels);
19
- ChartJS.register(ArcElement, Tooltip, Legend, Title);
20
-
21
- export interface DoughnutChartProps<T> {
22
- data: {
23
- label: string;
24
- data: T[];
25
- }[];
26
- getLabel: (datum: T) => string;
27
- getValue: (datum: T) => number;
28
- title?: string;
29
- showDataLabels?: boolean;
30
- theme?: Theme;
31
- }
32
-
33
- export default function DoughnutChart<T extends object>({
34
- data,
35
- getLabel,
36
- getValue,
37
- title,
38
- showDataLabels = true,
39
- theme = webLightTheme,
40
- }: DoughnutChartProps<T>) {
41
- const styles = useGraphGlobalStyles();
42
- const { lightenColor, getFluentPalette, createFluentTooltip } = useChartUtils(
43
- theme
44
- );
45
- const [hiddenLabels, setHiddenLabels] = useState<string[]>([]);
46
-
47
- const toggleLabel = (label: string): void => {
48
- setHiddenLabels(prev =>
49
- prev.includes(label) ? prev.filter(l => l !== label) : [...prev, label]
50
- );
51
- };
52
-
53
- const valueMap = useMemo(() => {
54
- const map = new Map<string, number>();
55
- data.forEach(series => {
56
- series.data.forEach(d => {
57
- const label = getLabel(d);
58
- const value = getValue(d);
59
- map.set(label, (map.get(label) || 0) + value);
60
- });
61
- });
62
- return map;
63
- }, [data, getLabel, getValue]);
64
-
65
- const { allLabels, colors } = useMemo(() => {
66
- const allLabels = Array.from(valueMap.keys());
67
- const palette = getFluentPalette(theme);
68
- const colors = allLabels.map((_, i) =>
69
- lightenColor(palette[i % palette.length], 0.3)
70
- );
71
- return { allLabels, colors };
72
- }, [valueMap, getFluentPalette, theme, lightenColor]);
73
-
74
- const { filteredLabels, values, visibleColors } = useMemo(() => {
75
- const filteredLabels = allLabels.filter(label => !hiddenLabels.includes(label));
76
- const values = filteredLabels.map(label => valueMap.get(label) || 0);
77
- const visibleColors = filteredLabels.map(label => {
78
- const idx = allLabels.indexOf(label);
79
- return colors[idx];
80
- });
81
- return { filteredLabels, values, visibleColors };
82
- }, [allLabels, hiddenLabels, valueMap, colors]);
83
-
84
- const chartData = useMemo(() => ({
85
- labels: filteredLabels,
86
- datasets: [
87
- {
88
- data: values,
89
- backgroundColor: visibleColors,
90
- borderWidth: 1,
91
- },
92
- ],
93
- }), [filteredLabels, values, visibleColors]);
94
-
95
- const legendEntries = useMemo(() => {
96
- return allLabels.map((label, idx) => ({
97
- label,
98
- value: valueMap.get(label) || 0,
99
- color: colors[idx],
100
- }));
101
- }, [allLabels, valueMap, colors]);
102
-
103
- const options: ChartOptions<'doughnut'> = useMemo(
104
- () => ({
105
- responsive: true,
106
- maintainAspectRatio: false,
107
- plugins: {
108
- legend: { display: false },
109
- tooltip: createFluentTooltip<'doughnut'>(theme),
110
- title: {
111
- display: !!title,
112
- text: title,
113
- font: {
114
- size: 14,
115
- family: theme.fontFamilyBase,
116
- weight: theme.fontWeightSemibold,
117
- },
118
- color: theme.colorNeutralForeground1,
119
- padding: {
120
- top: 20,
121
- bottom: 20,
122
- },
123
- },
124
- datalabels: {
125
- display: showDataLabels,
126
- color: theme.colorNeutralForeground1,
127
- font: {
128
- family: theme.fontFamilyBase,
129
- size: parseInt(theme.fontSizeBase200.replace('px', '')) || 14,
130
- },
131
- formatter: (value: number) => value,
132
- },
133
- },
134
- }),
135
- [title, theme, showDataLabels, createFluentTooltip]
136
- );
137
-
138
- return (
139
- <div className={styles.chartWithLegend}>
140
- <div className={styles.chartArea}>
141
- <Doughnut data={chartData} options={options} />
142
- </div>
143
- <div className={styles.legendArea}>
144
- <RenderValueLegend
145
- entries={legendEntries}
146
- visibleLabels={filteredLabels}
147
- toggleLabel={toggleLabel}
148
- />
149
- </div>
150
- </div>
151
- );
152
- }