@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,184 +0,0 @@
1
- import {
2
- BubbleDataPoint,
3
- CategoryScale,
4
- Chart as ChartJS,
5
- ChartOptions,
6
- Legend,
7
- LinearScale,
8
- PointElement,
9
- Title,
10
- Tooltip,
11
- } from 'chart.js';
12
- import React, { useMemo, useState } from 'react';
13
- import { Theme, webLightTheme } from '@fluentui/react-components';
14
-
15
- import { Bubble } from 'react-chartjs-2';
16
- import ChartDataLabels from 'chartjs-plugin-datalabels';
17
- import RenderLegend from '../../components/RenderLegend/RenderLegend';
18
- import { useChartUtils } from '../../hooks/useChartUtils';
19
- import { useGraphGlobalStyles } from '../../graphGlobalStyles/useGraphGlobalStyles';
20
-
21
- ChartJS.register(
22
- CategoryScale,
23
- LinearScale,
24
- PointElement,
25
- Tooltip,
26
- Legend,
27
- ChartDataLabels,
28
- Title
29
- );
30
-
31
- export interface BubbleChartProps<T> {
32
- data: { label: string; data: T[] }[];
33
- getPrimary: (datum: T) => string | number | Date;
34
- getSecondary: (datum: T) => number;
35
- getRadius: (datum: T) => number;
36
- title?: string;
37
- showDataLabels?: boolean;
38
- theme?: Theme;
39
- }
40
-
41
- export default function BubbleChart<T extends object>({
42
- data,
43
- getPrimary,
44
- getSecondary,
45
- getRadius,
46
- showDataLabels,
47
- title,
48
- theme = webLightTheme,
49
- }: BubbleChartProps<T>) {
50
- const [visibleSeries, setVisibleSeries] = useState(() =>
51
- data.length > 1 ? data.map(s => s.label) : [data[0]?.label]
52
- );
53
-
54
- const { lightenColor, getFluentPalette, createFluentTooltip } = useChartUtils(
55
- theme
56
- );
57
-
58
- const styles = useGraphGlobalStyles();
59
- const seriesColors = useMemo(() => {
60
- return data.reduce((acc, series, idx) => {
61
- const base = getFluentPalette(theme)[
62
- idx % getFluentPalette(theme).length
63
- ];
64
- const color = lightenColor(base, 0.3);
65
- acc[series.label] = color;
66
- return acc;
67
- }, {} as Record<string, string>);
68
- }, [data, theme]);
69
-
70
- const toggleSeries = (label: string) => {
71
- setVisibleSeries(prev => {
72
- const isVisible = prev.includes(label);
73
- const next = isVisible ? prev.filter(l => l !== label) : [...prev, label];
74
- return next.length === 0 && data.length > 0 ? [data[0].label] : next;
75
- });
76
- };
77
-
78
- const chartData = useMemo(() => {
79
- return {
80
- datasets: data
81
- .filter(series => visibleSeries.includes(series.label))
82
- .map(series => ({
83
- label: series.label,
84
- data: series.data.map(d => ({
85
- x: getPrimary(d),
86
- y: getSecondary(d),
87
- r: getRadius(d),
88
- })) as BubbleDataPoint[],
89
- backgroundColor: seriesColors[series.label],
90
- borderColor: theme.colorNeutralStroke1,
91
- borderWidth: 1,
92
- hoverBorderWidth: 2,
93
- })),
94
- };
95
- }, [data, visibleSeries, getPrimary, getSecondary, getRadius, seriesColors]);
96
-
97
- const { fontFamily, fontSize, labelColor, gridColor } = useMemo(() => ({
98
- fontFamily: theme.fontFamilyBase,
99
- fontSize: parseInt(theme.fontSizeBase200.replace('px', '')) || 14,
100
- labelColor: theme.colorNeutralForeground1,
101
- gridColor: theme.colorNeutralStroke2,
102
- }), [theme]);
103
-
104
- const options: ChartOptions<'bubble'> = useMemo(() => ({
105
- responsive: true,
106
- maintainAspectRatio: false,
107
- plugins: {
108
- title: {
109
- display: !!title,
110
- text: title,
111
- font: {
112
- size: 14,
113
- family: theme.fontFamilyBase,
114
- weight: theme.fontWeightSemibold,
115
- },
116
- color: theme.colorNeutralForeground1,
117
- padding: {
118
- top: 20,
119
- bottom: 20,
120
- },
121
- },
122
- datalabels: {
123
- display: showDataLabels,
124
- color: theme.colorNeutralForeground1,
125
- font: {
126
- family: theme.fontFamilyBase,
127
- size: parseInt(theme.fontSizeBase200.replace('px', '')) || 14,
128
- },
129
- },
130
- legend: { display: false },
131
- tooltip: createFluentTooltip<'bubble'>(theme),
132
- },
133
- scales: {
134
- x: {
135
- type:
136
- typeof getPrimary(data[0]?.data[0]) === 'number'
137
- ? 'linear'
138
- : 'category',
139
- ticks: {
140
- color: labelColor,
141
- font: { family: fontFamily, size: fontSize },
142
- },
143
- grid: { color: gridColor },
144
- },
145
- y: {
146
- ticks: {
147
- color: labelColor,
148
- font: { family: fontFamily, size: fontSize },
149
- },
150
- grid: { color: gridColor },
151
- },
152
- },
153
- }), [
154
- title,
155
- showDataLabels,
156
- theme,
157
- getPrimary,
158
- getSecondary,
159
- data,
160
- fontFamily,
161
- fontSize,
162
- labelColor,
163
- gridColor,
164
- createFluentTooltip,
165
- ]);
166
-
167
- return (
168
- <>
169
- <div className={styles.chartWithLegend}>
170
- <div className={styles.chartArea}>
171
- <Bubble data={chartData} options={options} />
172
- </div>
173
- <div className={styles.legendArea}>
174
- <RenderLegend
175
- data={data}
176
- visibleSeries={visibleSeries}
177
- seriesColors={seriesColors}
178
- toggleSeries={toggleSeries}
179
- />
180
- </div>
181
- </div>
182
- </>
183
- );
184
- }
@@ -1,275 +0,0 @@
1
- # BubbleChart Component
2
-
3
- A powerful bubble chart component built with Chart.js and Fluent UI React. This component visualizes three-dimensional data where each bubble represents a data point with X and Y coordinates, and the bubble size represents a third dimension, making it perfect for complex data analysis and correlation visualization.
4
-
5
- ## Features
6
-
7
- - **Three-Dimensional Data Visualization**: Display X, Y, and size (radius) data in a single chart
8
- - **Multiple Series Support**: Compare multiple datasets with different colored bubble series
9
- - **Interactive Legend**: Toggle series visibility with click interactions
10
- - **Flexible Data Types**: Support for numeric, string, and Date values on axes
11
- - **Fluent UI Integration**: Seamless integration with Fluent UI themes and design system
12
- - **Data Labels**: Optional display of values directly on bubbles
13
- - **Responsive Design**: Automatically adapts to container dimensions
14
- - **TypeScript Support**: Full TypeScript support with generic types
15
- - **Custom Tooltips**: Rich tooltips showing all three data dimensions
16
- - **Dynamic Scaling**: Automatic axis scaling based on data ranges
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 { BubbleChart } from './components/bubbleChart/BubbleChart';
29
- import { webLightTheme } from '@fluentui/react-components';
30
-
31
- interface SalesData {
32
- product: string;
33
- revenue: number;
34
- profit: number;
35
- marketShare: number;
36
- }
37
-
38
- const salesData: SalesData[] = [
39
- { product: 'Product A', revenue: 150000, profit: 45000, marketShare: 15 },
40
- { product: 'Product B', revenue: 200000, profit: 60000, marketShare: 22 },
41
- { product: 'Product C', revenue: 120000, profit: 30000, marketShare: 12 },
42
- { product: 'Product D', revenue: 180000, profit: 50000, marketShare: 18 },
43
- ];
44
-
45
- function App() {
46
- return (
47
- <div style={{ width: '800px', height: '600px' }}>
48
- <BubbleChart
49
- data={[
50
- { label: 'Product Performance', data: salesData }
51
- ]}
52
- getPrimary={(datum) => datum.revenue} // X-axis: Revenue
53
- getSecondary={(datum) => datum.profit} // Y-axis: Profit
54
- getRadius={(datum) => datum.marketShare} // Bubble size: Market Share
55
- title="Product Performance Analysis"
56
- theme={webLightTheme}
57
- />
58
- </div>
59
- );
60
- }
61
- ```
62
-
63
- ## Props
64
-
65
- ### BubbleChartProps<T>
66
-
67
- | Prop | Type | Required | Default | Description |
68
- |------|------|----------|---------|-------------|
69
- | `data` | `{ label: string; data: T[] }[]` | Yes | - | Array of data series with labels and data points |
70
- | `getPrimary` | `(datum: T) => string \| number \| Date` | Yes | - | Function to extract the X-axis value from each data point |
71
- | `getSecondary` | `(datum: T) => number` | Yes | - | Function to extract the Y-axis value from each data point |
72
- | `getRadius` | `(datum: T) => number` | Yes | - | Function to extract the bubble size from each data point |
73
- | `title` | `string` | No | - | Chart title displayed at the top |
74
- | `showDataLabels` | `boolean` | No | `false` | Whether to show data labels on bubbles |
75
- | `theme` | `Theme` | No | `webLightTheme` | Fluent UI theme object for styling |
76
-
77
- ## Advanced Usage
78
-
79
- ### Multiple Series Comparison
80
-
81
- ```tsx
82
- interface CompanyMetrics {
83
- company: string;
84
- employees: number;
85
- revenue: number;
86
- satisfaction: number;
87
- }
88
-
89
- const techCompanies: CompanyMetrics[] = [
90
- { company: 'TechCorp', employees: 5000, revenue: 2000000, satisfaction: 85 },
91
- { company: 'DataSoft', employees: 3000, revenue: 1500000, satisfaction: 78 },
92
- { company: 'CloudTech', employees: 8000, revenue: 3500000, satisfaction: 92 },
93
- ];
94
-
95
- const startups: CompanyMetrics[] = [
96
- { company: 'StartupA', employees: 50, revenue: 100000, satisfaction: 88 },
97
- { company: 'StartupB', employees: 80, revenue: 150000, satisfaction: 82 },
98
- { company: 'StartupC', employees: 120, revenue: 200000, satisfaction: 90 },
99
- ];
100
-
101
- <BubbleChart
102
- data={[
103
- { label: 'Established Companies', data: techCompanies },
104
- { label: 'Startups', data: startups }
105
- ]}
106
- getPrimary={(datum) => datum.employees} // X-axis: Number of employees
107
- getSecondary={(datum) => datum.revenue} // Y-axis: Revenue
108
- getRadius={(datum) => datum.satisfaction} // Bubble size: Satisfaction score
109
- title="Company Performance: Size vs Revenue vs Satisfaction"
110
- showDataLabels={true}
111
- theme={webLightTheme}
112
- />
113
- ```
114
-
115
- ### Financial Portfolio Analysis
116
-
117
- ```tsx
118
- interface Investment {
119
- asset: string;
120
- risk: number; // 1-10 scale
121
- return: number; // Percentage
122
- allocation: number; // Portfolio percentage
123
- }
124
-
125
- const portfolioData: Investment[] = [
126
- { asset: 'Stocks', risk: 7, return: 12.5, allocation: 40 },
127
- { asset: 'Bonds', risk: 3, return: 4.2, allocation: 30 },
128
- { asset: 'Real Estate', risk: 5, return: 8.1, allocation: 20 },
129
- { asset: 'Commodities', risk: 8, return: 6.8, allocation: 10 },
130
- ];
131
-
132
- <BubbleChart
133
- data={[{ label: 'Investment Portfolio', data: portfolioData }]}
134
- getPrimary={(datum) => datum.risk} // X-axis: Risk level
135
- getSecondary={(datum) => datum.return} // Y-axis: Expected return
136
- getRadius={(datum) => datum.allocation} // Bubble size: Portfolio allocation
137
- title="Portfolio Risk vs Return Analysis"
138
- showDataLabels={true}
139
- theme={webLightTheme}
140
- />
141
- ```
142
-
143
- ### Time-based Analysis
144
-
145
- ```tsx
146
- interface ProjectData {
147
- project: string;
148
- startDate: Date;
149
- duration: number; // Days
150
- budget: number; // Dollars
151
- }
152
-
153
- const projects: ProjectData[] = [
154
- { project: 'Project Alpha', startDate: new Date('2024-01-15'), duration: 90, budget: 150000 },
155
- { project: 'Project Beta', startDate: new Date('2024-02-01'), duration: 120, budget: 200000 },
156
- { project: 'Project Gamma', startDate: new Date('2024-03-10'), duration: 60, budget: 100000 },
157
- ];
158
-
159
- <BubbleChart
160
- data={[{ label: 'Project Timeline', data: projects }]}
161
- getPrimary={(datum) => datum.startDate} // X-axis: Start date
162
- getSecondary={(datum) => datum.duration} // Y-axis: Duration
163
- getRadius={(datum) => datum.budget / 5000} // Bubble size: Budget (scaled)
164
- title="Project Timeline vs Duration vs Budget"
165
- theme={webLightTheme}
166
- />
167
- ```
168
-
169
- ## Data Structure
170
-
171
- The component expects data in the following format:
172
-
173
- ```tsx
174
- interface ChartSeries<T> {
175
- label: string; // Series name (appears in legend)
176
- data: T[]; // Array of data points
177
- }
178
- ```
179
-
180
- Each data point `T` should contain:
181
-
182
- - A primary value (X-axis) - string, number, or Date
183
- - A secondary value (Y-axis) - number
184
- - A radius value (bubble size) - number
185
-
186
- ## Use Cases
187
-
188
- Bubble charts are particularly effective for:
189
-
190
- ### Business Intelligence
191
-
192
- - **Market Analysis**: Market size vs growth rate vs market share
193
- - **Product Portfolio**: Price vs demand vs profit margin
194
- - **Customer Segmentation**: Spend vs frequency vs lifetime value
195
-
196
- ### Scientific Data
197
-
198
- - **Research Results**: Variable A vs Variable B vs sample size
199
- - **Performance Metrics**: Speed vs accuracy vs resource usage
200
- - **Correlation Analysis**: Three-variable relationship visualization
201
-
202
- ### Financial Analysis
203
-
204
- - **Investment Comparison**: Risk vs return vs investment amount
205
- - **Asset Allocation**: Volatility vs yield vs portfolio weight
206
- - **Performance Tracking**: Time vs value vs volume
207
-
208
- ## Styling and Theme Integration
209
-
210
- The component automatically applies Fluent UI theme styles:
211
-
212
- ```tsx
213
- // Bubble styling
214
- backgroundColor: Derived from theme palette
215
- borderColor: theme.colorNeutralStroke1
216
- borderWidth: 1
217
- hoverBorderWidth: 2
218
-
219
- // Typography
220
- fontFamily: theme.fontFamilyBase
221
- fontSize: theme.fontSizeBase200
222
- fontWeight: theme.fontWeightSemibold
223
-
224
- // Colors
225
- labelColor: theme.colorNeutralForeground1
226
- gridColor: theme.colorNeutralStroke2
227
- ```
228
-
229
- ## Interactive Features
230
-
231
- ### Legend Controls
232
-
233
- - Click legend items to show/hide data series
234
- - Visual feedback on hover states
235
- - At least one series must remain visible
236
- - Colors automatically assigned from theme palette
237
-
238
- ### Bubble Interactions
239
-
240
- - Hover effects with border highlighting
241
- - Rich tooltips showing all three data dimensions
242
- - Smooth transitions and animations
243
-
244
- ### Responsive Scaling
245
-
246
- - Automatic axis scaling based on data ranges
247
- - Dynamic bubble size scaling
248
- - Responsive layout for different screen sizes
249
-
250
- ## Performance Optimizations
251
-
252
- The component includes several React optimizations:
253
-
254
- ````tsx
255
- // Memoized color calculations
256
- const seriesColors = useMemo(() => {
257
- // Color generation logic
258
- }, [data, theme]);
259
-
260
- // Memoized chart data transformation
261
- const chartData = useMemo(() => {
262
- return {
263
- datasets: data
264
- .filter(series => visibleSeries.includes(series.label))
265
- .map(series => ({
266
- label: series.label,
267
- data: series.data.map(d => ({
268
- x: getPrimary(d),
269
- y: getSecondary(d),
270
- r: getRadius(d),
271
- })) as BubbleDataPoint[],
272
- // ... styling
273
- })),
274
- };
275
- }, [data, visibleSeries, getPrimary, getSecondary, getRadius, seriesColors]);
@@ -1 +0,0 @@
1
- export * from './BubbleChart';
@@ -1,178 +0,0 @@
1
- import {
2
- BarElement,
3
- CategoryScale,
4
- Chart as ChartJS,
5
- ChartOptions,
6
- Legend,
7
- LinearScale,
8
- Title,
9
- Tooltip,
10
- } from 'chart.js';
11
- import React, { useMemo, useState } from 'react';
12
- import { Theme, webLightTheme } from '@fluentui/react-components';
13
-
14
- import { Bar } from 'react-chartjs-2';
15
- import ChartDataLabels from 'chartjs-plugin-datalabels';
16
- import RenderLegend from '../../components/RenderLegend/RenderLegend';
17
- import { useChartUtils } from '../../hooks/useChartUtils';
18
- import { useGraphGlobalStyles } from '../../graphGlobalStyles/useGraphGlobalStyles';
19
-
20
- ChartJS.register(ChartDataLabels);
21
- ChartJS.register(
22
- CategoryScale,
23
- LinearScale,
24
- BarElement,
25
- Tooltip,
26
- Legend,
27
- Title
28
- );
29
-
30
- export interface FloatingBarChartProps<T> {
31
- data: { label: string; data: T[] }[];
32
- getPrimary: (datum: T) => string | number;
33
- getRange: (datum: T) => [number, number]; // e.g., [min, max]
34
- title?: string;
35
- showDataLabels?: boolean;
36
- theme?: Theme;
37
- }
38
-
39
- export default function FloatingBarChart<T extends object>({
40
- data,
41
- getPrimary,
42
- getRange,
43
- title,
44
- showDataLabels = false,
45
- theme = webLightTheme,
46
- }: FloatingBarChartProps<T>) {
47
- const [visibleSeries, setVisibleSeries] = useState(() =>
48
- data.length > 1 ? data.map(s => s.label) : [data[0]?.label]
49
- );
50
-
51
- const styles = useGraphGlobalStyles();
52
- const { lightenColor, getFluentPalette, createFluentTooltip } = useChartUtils(
53
- theme
54
- );
55
-
56
- const seriesColors = useMemo(() => {
57
- return data.reduce((acc, series, idx) => {
58
- const base = getFluentPalette(theme)[
59
- idx % getFluentPalette(theme).length
60
- ];
61
- const color = lightenColor(base, 0.3);
62
- acc[series.label] = color;
63
- return acc;
64
- }, {} as Record<string, string>);
65
- }, [data, theme]);
66
-
67
- const toggleSeries = (label: string) => {
68
- setVisibleSeries(prev => {
69
- const isVisible = prev.includes(label);
70
- const next = isVisible ? prev.filter(l => l !== label) : [...prev, label];
71
- return next.length === 0 && data.length > 0 ? [data[0].label] : next;
72
- });
73
- };
74
-
75
- const allCategories = useMemo(() => {
76
- const set = new Set<string | number>();
77
- data.forEach(series => {
78
- series.data.forEach(d => set.add(getPrimary(d)));
79
- });
80
- return Array.from(set);
81
- }, [data, getPrimary]);
82
-
83
- const chartData = useMemo(() => {
84
- return {
85
- labels: allCategories,
86
- datasets: data
87
- .filter(series => visibleSeries.includes(series.label))
88
- .map(series => ({
89
- label: series.label,
90
- data: allCategories.map(cat => {
91
- const match = series.data.find(d => getPrimary(d) === cat);
92
- return match ? getRange(match) : [0, 0];
93
- }),
94
- backgroundColor: seriesColors[series.label],
95
- borderRadius: 2,
96
- })),
97
- };
98
- }, [data, visibleSeries, allCategories, getPrimary, getRange, seriesColors]);
99
-
100
- const { fontFamily, fontSize, labelColor, gridColor } = useMemo(() => ({
101
- fontFamily: theme.fontFamilyBase,
102
- fontSize: parseInt(theme.fontSizeBase200.replace('px', '')) || 14,
103
- labelColor: theme.colorNeutralForeground1,
104
- gridColor: theme.colorNeutralStroke2,
105
- }), [theme]);
106
-
107
- const options: ChartOptions<'bar'> = useMemo(() => ({
108
- responsive: true,
109
- maintainAspectRatio: false,
110
- plugins: {
111
- title: {
112
- display: !!title,
113
- text: title,
114
- font: {
115
- size: 14,
116
- family: theme.fontFamilyBase,
117
- weight: theme.fontWeightSemibold,
118
- },
119
- color: theme.colorNeutralForeground1,
120
- padding: {
121
- top: 20,
122
- bottom: 20,
123
- },
124
- },
125
- datalabels: {
126
- display: showDataLabels,
127
- color: theme.colorNeutralForeground1,
128
- font: {
129
- family: theme.fontFamilyBase,
130
- size: parseInt(theme.fontSizeBase200.replace('px', '')) || 14,
131
- },
132
- },
133
- legend: { display: false },
134
- tooltip: createFluentTooltip<'bar'>(theme),
135
- },
136
- scales: {
137
- x: {
138
- ticks: {
139
- color: labelColor,
140
- font: { family: fontFamily, size: fontSize },
141
- },
142
- grid: { color: gridColor },
143
- },
144
- y: {
145
- ticks: {
146
- color: labelColor,
147
- font: { family: fontFamily, size: fontSize },
148
- },
149
- grid: { color: gridColor },
150
- },
151
- },
152
- }), [
153
- title,
154
- theme,
155
- showDataLabels,
156
- labelColor,
157
- fontFamily,
158
- fontSize,
159
- gridColor,
160
- createFluentTooltip
161
- ]);
162
-
163
- return (
164
- <div className={styles.chartWithLegend}>
165
- <div className={styles.chartArea}>
166
- <Bar data={chartData} options={options} />
167
- </div>
168
- <div className={styles.legendArea}>
169
- <RenderLegend
170
- data={data}
171
- visibleSeries={visibleSeries}
172
- seriesColors={seriesColors}
173
- toggleSeries={toggleSeries}
174
- />
175
- </div>
176
- </div>
177
- );
178
- }