@spteck/fluentui-react-charts 1.0.7 → 1.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/charts/BarChart/BarChart.d.ts +2 -1
- package/dist/charts/ComboChart/ComboChart.d.ts +2 -1
- package/dist/charts/Doughnut/DoughnutChart.d.ts +2 -1
- package/dist/charts/PieChart/PieChart.d.ts +2 -1
- package/dist/charts/areaChart/AreaChart.d.ts +2 -1
- package/dist/charts/barHorizontalChart/BarHotizontalChart.d.ts +2 -1
- package/dist/charts/bubbleChart/BubbleChart.d.ts +2 -1
- package/dist/charts/floatBarChart/FloatBarChart.d.ts +2 -1
- package/dist/charts/index.d.ts +14 -0
- package/dist/charts/lineChart/LineChart.d.ts +2 -1
- package/dist/charts/polarChart/PolarChart.d.ts +2 -1
- package/dist/charts/radarChart/RadarChart.d.ts +2 -1
- package/dist/charts/scatterChart/ScatterChart.d.ts +2 -1
- package/dist/charts/stackedLineChart/StackedLineChart.d.ts +2 -1
- package/dist/charts/steamChart/SteamChart.d.ts +2 -1
- package/dist/components/index.d.ts +0 -14
- package/dist/fluentui-react-charts.cjs.development.js +1086 -1072
- package/dist/fluentui-react-charts.cjs.development.js.map +1 -1
- package/dist/fluentui-react-charts.cjs.production.min.js +1 -1
- package/dist/fluentui-react-charts.cjs.production.min.js.map +1 -1
- package/dist/fluentui-react-charts.esm.js +1074 -1074
- package/dist/fluentui-react-charts.esm.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/package.json +5 -5
- package/src/assets/sample1.png +0 -0
- package/src/assets/sample2.png +0 -0
- package/src/assets/sample3.png +0 -0
- package/src/charts/BarChart/BarChart.tsx +0 -227
- package/src/charts/BarChart/README.MD +0 -335
- package/src/charts/BarChart/index.ts +0 -1
- package/src/charts/ComboChart/ComboChart.tsx +0 -209
- package/src/charts/ComboChart/README.MD +0 -347
- package/src/charts/ComboChart/index.ts +0 -1
- package/src/charts/Doughnut/DoughnutChart.tsx +0 -152
- package/src/charts/Doughnut/README.MD +0 -296
- package/src/charts/Doughnut/index.ts +0 -1
- package/src/charts/PieChart/PieChart.tsx +0 -148
- package/src/charts/PieChart/README.MD +0 -315
- package/src/charts/PieChart/index.ts +0 -1
- package/src/charts/areaChart/AreaChart.tsx +0 -195
- package/src/charts/areaChart/README.MD +0 -236
- package/src/charts/areaChart/index.ts +0 -1
- package/src/charts/barHorizontalChart/BarHotizontalChart.tsx +0 -200
- package/src/charts/barHorizontalChart/README.MD +0 -278
- package/src/charts/barHorizontalChart/index.ts +0 -2
- package/src/charts/bubbleChart/BubbleChart.tsx +0 -184
- package/src/charts/bubbleChart/README.MD +0 -275
- package/src/charts/bubbleChart/index.ts +0 -1
- package/src/charts/floatBarChart/FloatBarChart.tsx +0 -178
- package/src/charts/floatBarChart/README.MD +0 -354
- package/src/charts/floatBarChart/index.ts +0 -1
- package/src/charts/lineChart/LineChart.tsx +0 -200
- package/src/charts/lineChart/README.MD +0 -354
- package/src/charts/lineChart/index.ts +0 -1
- package/src/charts/polarChart/PolarChart.tsx +0 -161
- package/src/charts/polarChart/README.MD +0 -336
- package/src/charts/polarChart/index.ts +0 -1
- package/src/charts/radarChart/README.MD +0 -388
- package/src/charts/radarChart/RadarChart.tsx +0 -173
- package/src/charts/radarChart/index.ts +0 -1
- package/src/charts/scatterChart/README.MD +0 -335
- package/src/charts/scatterChart/ScatterChart.tsx +0 -155
- package/src/charts/scatterChart/index.ts +0 -1
- package/src/charts/stackedLineChart/README.MD +0 -396
- package/src/charts/stackedLineChart/StackedLineChart.tsx +0 -188
- package/src/charts/stackedLineChart/index.ts +0 -1
- package/src/charts/steamChart/README.MD +0 -414
- package/src/charts/steamChart/SteamChart.tsx +0 -236
- package/src/charts/steamChart/index.ts +0 -1
- package/src/components/RenderLabel/RenderLabel.tsx +0 -39
- package/src/components/RenderLabel/index.ts +0 -2
- package/src/components/RenderLabel/useRenderLabelStylesStyles.ts +0 -25
- package/src/components/RenderLegend/RenderLegend.tsx +0 -40
- package/src/components/RenderTooltip/RenderTooltip.tsx +0 -111
- package/src/components/buttonMenu/ButtonMenu.tsx +0 -186
- package/src/components/buttonMenu/IButtonMenuOption.ts +0 -9
- package/src/components/buttonMenu/IButtonMenuProps.tsx +0 -40
- package/src/components/dashboard/DashBoard.tsx +0 -314
- package/src/components/dashboard/ExampleDashboardUsage.tsx +0 -114
- package/src/components/dashboard/IDashboardProps.tsx +0 -11
- package/src/components/dashboard/NoDashboards.tsx +0 -26
- package/src/components/dashboard/index.ts +0 -3
- package/src/components/dashboard/selectZoom/SelectZoom.tsx +0 -184
- package/src/components/dashboard/useDashboardStyles.ts +0 -76
- package/src/components/index.ts +0 -17
- package/src/components/legendContainer/LegendContainer.tsx +0 -118
- package/src/components/legendeButton/LegendButton.tsx +0 -57
- package/src/components/renderSliceLegend/RenderSliceLegend.tsx +0 -46
- package/src/components/renderValueLegend/RenderValueLegend.tsx +0 -43
- package/src/components/stack/IStackProps.tsx +0 -94
- package/src/components/stack/Stack.tsx +0 -103
- package/src/components/svgImages/BusinessReportIcon.tsx +0 -218
- package/src/components/themeProvider/ThemeProvider.tsx +0 -48
- package/src/constants/Constants.tsx +0 -23
- package/src/graphGlobalStyles/useGraphGlobalStyles.ts +0 -28
- package/src/hooks/index.ts +0 -1
- package/src/hooks/useChartFactory.tsx +0 -136
- package/src/hooks/useChartUtils.tsx +0 -187
- package/src/hooks/useIndexedDBCache.ts +0 -119
- package/src/hooks/useResponsiveLegend.ts +0 -35
- package/src/index.tsx +0 -5
- package/src/models/ChartDatum.ts +0 -4
- package/src/models/ICardChartContainer.tsx +0 -11
- package/src/models/IChart.ts +0 -50
- package/src/models/index.ts +0 -3
|
@@ -1,354 +0,0 @@
|
|
|
1
|
-
# FloatingBarChart Component
|
|
2
|
-
|
|
3
|
-
A specialized floating bar chart component built with Chart.js and Fluent UI React. This component displays bars that "float" between two values, making it perfect for visualizing ranges, intervals, temperature variations, price ranges, time periods, and other data that has both minimum and maximum values.
|
|
4
|
-
|
|
5
|
-
## Features
|
|
6
|
-
|
|
7
|
-
- **Range Visualization**: Display data with both minimum and maximum values as floating bars
|
|
8
|
-
- **Multiple Series Support**: Compare multiple range datasets with different colors
|
|
9
|
-
- **Interactive Legend**: Toggle series visibility with click interactions
|
|
10
|
-
- **Fluent UI Integration**: Seamless integration with Fluent UI themes and design system
|
|
11
|
-
- **Data Labels**: Optional display of range values directly on chart elements
|
|
12
|
-
- **Responsive Design**: Automatically adapts to container dimensions
|
|
13
|
-
- **TypeScript Support**: Full TypeScript support with generic types
|
|
14
|
-
- **Custom Tooltips**: Rich tooltips showing detailed range information
|
|
15
|
-
- **Rounded Corners**: Modern bar styling with configurable border radius
|
|
16
|
-
|
|
17
|
-
## Installation
|
|
18
|
-
|
|
19
|
-
```bash
|
|
20
|
-
npm install chart.js react-chartjs-2 chartjs-plugin-datalabels @fluentui/react-components
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
## Basic Usage
|
|
24
|
-
|
|
25
|
-
```tsx
|
|
26
|
-
import React from 'react';
|
|
27
|
-
import { FloatingBarChart } from './components/floatBarChart/FloatingBarChart';
|
|
28
|
-
import { webLightTheme } from '@fluentui/react-components';
|
|
29
|
-
|
|
30
|
-
interface TemperatureData {
|
|
31
|
-
month: string;
|
|
32
|
-
minTemp: number;
|
|
33
|
-
maxTemp: number;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const temperatureData: TemperatureData[] = [
|
|
37
|
-
{ month: 'January', minTemp: 25, maxTemp: 45 },
|
|
38
|
-
{ month: 'February', minTemp: 30, maxTemp: 50 },
|
|
39
|
-
{ month: 'March', minTemp: 40, maxTemp: 65 },
|
|
40
|
-
{ month: 'April', minTemp: 50, maxTemp: 75 },
|
|
41
|
-
{ month: 'May', minTemp: 60, maxTemp: 85 },
|
|
42
|
-
{ month: 'June', minTemp: 70, maxTemp: 95 },
|
|
43
|
-
];
|
|
44
|
-
|
|
45
|
-
function App() {
|
|
46
|
-
return (
|
|
47
|
-
<div style={{ width: '800px', height: '400px' }}>
|
|
48
|
-
<FloatingBarChart
|
|
49
|
-
data={[
|
|
50
|
-
{ label: 'Temperature Range', data: temperatureData }
|
|
51
|
-
]}
|
|
52
|
-
getPrimary={(datum) => datum.month}
|
|
53
|
-
getRange={(datum) => [datum.minTemp, datum.maxTemp]}
|
|
54
|
-
title="Monthly Temperature Ranges"
|
|
55
|
-
theme={webLightTheme}
|
|
56
|
-
/>
|
|
57
|
-
</div>
|
|
58
|
-
);
|
|
59
|
-
}
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
## Props
|
|
63
|
-
|
|
64
|
-
### FloatingBarChartProps<T>
|
|
65
|
-
|
|
66
|
-
| Prop | Type | Required | Default | Description |
|
|
67
|
-
|------|------|----------|---------|-------------|
|
|
68
|
-
| `data` | `{ label: string; data: T[] }[]` | Yes | - | Array of data series with labels and data points |
|
|
69
|
-
| `getPrimary` | `(datum: T) => string \| number` | Yes | - | Function to extract the x-axis category from each data point |
|
|
70
|
-
| `getRange` | `(datum: T) => [number, number]` | Yes | - | Function to extract the [min, max] range from each data point |
|
|
71
|
-
| `title` | `string` | No | - | Chart title displayed at the top |
|
|
72
|
-
| `showDataLabels` | `boolean` | No | `false` | Whether to show range values on chart elements |
|
|
73
|
-
| `theme` | `Theme` | No | `webLightTheme` | Fluent UI theme object for styling |
|
|
74
|
-
|
|
75
|
-
## Advanced Usage
|
|
76
|
-
|
|
77
|
-
### Stock Price Ranges
|
|
78
|
-
|
|
79
|
-
```tsx
|
|
80
|
-
interface StockData {
|
|
81
|
-
date: string;
|
|
82
|
-
low: number;
|
|
83
|
-
high: number;
|
|
84
|
-
open: number;
|
|
85
|
-
close: number;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const stockPrices: StockData[] = [
|
|
89
|
-
{ date: '2024-01-01', low: 145.20, high: 152.80, open: 148.50, close: 151.25 },
|
|
90
|
-
{ date: '2024-01-02', low: 149.75, high: 156.30, open: 151.25, close: 154.90 },
|
|
91
|
-
{ date: '2024-01-03', low: 152.10, high: 159.45, open: 154.90, close: 157.80 },
|
|
92
|
-
{ date: '2024-01-04', low: 155.25, high: 162.15, open: 157.80, close: 160.45 },
|
|
93
|
-
];
|
|
94
|
-
|
|
95
|
-
<FloatingBarChart
|
|
96
|
-
data={[{ label: 'Daily Price Range', data: stockPrices }]}
|
|
97
|
-
getPrimary={(datum) => datum.date}
|
|
98
|
-
getRange={(datum) => [datum.low, datum.high]}
|
|
99
|
-
title="Stock Price Daily Ranges"
|
|
100
|
-
showDataLabels={true}
|
|
101
|
-
theme={webLightTheme}
|
|
102
|
-
/>
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
### Project Timeline Ranges
|
|
106
|
-
|
|
107
|
-
```tsx
|
|
108
|
-
interface ProjectPhase {
|
|
109
|
-
phase: string;
|
|
110
|
-
plannedStart: number; // Week number
|
|
111
|
-
plannedEnd: number; // Week number
|
|
112
|
-
actualStart?: number;
|
|
113
|
-
actualEnd?: number;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
const projectData: ProjectPhase[] = [
|
|
117
|
-
{ phase: 'Planning', plannedStart: 1, plannedEnd: 4, actualStart: 1, actualEnd: 5 },
|
|
118
|
-
{ phase: 'Design', plannedStart: 3, plannedEnd: 8, actualStart: 4, actualEnd: 9 },
|
|
119
|
-
{ phase: 'Development', plannedStart: 6, plannedEnd: 16, actualStart: 7, actualEnd: 18 },
|
|
120
|
-
{ phase: 'Testing', plannedStart: 14, plannedEnd: 20, actualStart: 16, actualEnd: 22 },
|
|
121
|
-
{ phase: 'Deployment', plannedStart: 18, plannedEnd: 22, actualStart: 20, actualEnd: 24 },
|
|
122
|
-
];
|
|
123
|
-
|
|
124
|
-
<FloatingBarChart
|
|
125
|
-
data={[
|
|
126
|
-
{
|
|
127
|
-
label: 'Planned Timeline',
|
|
128
|
-
data: projectData.map(p => ({
|
|
129
|
-
phase: p.phase,
|
|
130
|
-
range: [p.plannedStart, p.plannedEnd]
|
|
131
|
-
}))
|
|
132
|
-
},
|
|
133
|
-
{
|
|
134
|
-
label: 'Actual Timeline',
|
|
135
|
-
data: projectData
|
|
136
|
-
.filter(p => p.actualStart && p.actualEnd)
|
|
137
|
-
.map(p => ({
|
|
138
|
-
phase: p.phase,
|
|
139
|
-
range: [p.actualStart!, p.actualEnd!]
|
|
140
|
-
}))
|
|
141
|
-
}
|
|
142
|
-
]}
|
|
143
|
-
getPrimary={(datum) => datum.phase}
|
|
144
|
-
getRange={(datum) => datum.range}
|
|
145
|
-
title="Project Timeline: Planned vs Actual"
|
|
146
|
-
showDataLabels={true}
|
|
147
|
-
theme={webLightTheme}
|
|
148
|
-
/>
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
### Salary Ranges by Position
|
|
152
|
-
|
|
153
|
-
```tsx
|
|
154
|
-
interface SalaryRange {
|
|
155
|
-
position: string;
|
|
156
|
-
minSalary: number;
|
|
157
|
-
maxSalary: number;
|
|
158
|
-
medianSalary: number;
|
|
159
|
-
experience: 'junior' | 'mid' | 'senior';
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
const salaryData: SalaryRange[] = [
|
|
163
|
-
{ position: 'Software Engineer', minSalary: 70000, maxSalary: 120000, medianSalary: 95000, experience: 'mid' },
|
|
164
|
-
{ position: 'Senior Engineer', minSalary: 110000, maxSalary: 180000, medianSalary: 145000, experience: 'senior' },
|
|
165
|
-
{ position: 'Product Manager', minSalary: 85000, maxSalary: 150000, medianSalary: 117500, experience: 'mid' },
|
|
166
|
-
{ position: 'Data Scientist', minSalary: 80000, maxSalary: 140000, medianSalary: 110000, experience: 'mid' },
|
|
167
|
-
];
|
|
168
|
-
|
|
169
|
-
<FloatingBarChart
|
|
170
|
-
data={[{ label: 'Salary Ranges', data: salaryData }]}
|
|
171
|
-
getPrimary={(datum) => datum.position}
|
|
172
|
-
getRange={(datum) => [datum.minSalary, datum.maxSalary]}
|
|
173
|
-
title="Salary Ranges by Position"
|
|
174
|
-
showDataLabels={true}
|
|
175
|
-
theme={webLightTheme}
|
|
176
|
-
/>
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
### Performance Metrics Ranges
|
|
180
|
-
|
|
181
|
-
```tsx
|
|
182
|
-
interface PerformanceMetric {
|
|
183
|
-
metric: string;
|
|
184
|
-
target: number;
|
|
185
|
-
acceptable: number;
|
|
186
|
-
poor: number;
|
|
187
|
-
current: number;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
const performanceData: PerformanceMetric[] = [
|
|
191
|
-
{ metric: 'Response Time (ms)', target: 0, acceptable: 200, poor: 500, current: 150 },
|
|
192
|
-
{ metric: 'CPU Usage (%)', target: 0, acceptable: 70, poor: 90, current: 45 },
|
|
193
|
-
{ metric: 'Memory Usage (%)', target: 0, acceptable: 80, poor: 95, current: 65 },
|
|
194
|
-
{ metric: 'Error Rate (%)', target: 0, acceptable: 1, poor: 5, current: 0.3 },
|
|
195
|
-
];
|
|
196
|
-
|
|
197
|
-
<FloatingBarChart
|
|
198
|
-
data={[
|
|
199
|
-
{
|
|
200
|
-
label: 'Acceptable Range',
|
|
201
|
-
data: performanceData.map(p => ({
|
|
202
|
-
metric: p.metric,
|
|
203
|
-
range: [p.target, p.acceptable]
|
|
204
|
-
}))
|
|
205
|
-
},
|
|
206
|
-
{
|
|
207
|
-
label: 'Poor Performance',
|
|
208
|
-
data: performanceData.map(p => ({
|
|
209
|
-
metric: p.metric,
|
|
210
|
-
range: [p.acceptable, p.poor]
|
|
211
|
-
}))
|
|
212
|
-
}
|
|
213
|
-
]}
|
|
214
|
-
getPrimary={(datum) => datum.metric}
|
|
215
|
-
getRange={(datum) => datum.range}
|
|
216
|
-
title="Performance Metrics: Acceptable vs Poor Ranges"
|
|
217
|
-
theme={webLightTheme}
|
|
218
|
-
/>
|
|
219
|
-
```
|
|
220
|
-
|
|
221
|
-
## Data Structure
|
|
222
|
-
|
|
223
|
-
The component expects data in the following format:
|
|
224
|
-
|
|
225
|
-
```tsx
|
|
226
|
-
interface ChartSeries<T> {
|
|
227
|
-
label: string; // Series name (appears in legend)
|
|
228
|
-
data: T[]; // Array of data points
|
|
229
|
-
}
|
|
230
|
-
```
|
|
231
|
-
|
|
232
|
-
Each data point `T` should contain:
|
|
233
|
-
|
|
234
|
-
- A primary value (x-axis category) - string or number
|
|
235
|
-
- A range function that returns [min, max] - tuple of numbers
|
|
236
|
-
|
|
237
|
-
## Range Data Format
|
|
238
|
-
|
|
239
|
-
The `getRange` function should return a tuple with exactly two numbers:
|
|
240
|
-
|
|
241
|
-
```tsx
|
|
242
|
-
// Valid range formats
|
|
243
|
-
getRange: (datum) => [datum.min, datum.max]
|
|
244
|
-
getRange: (datum) => [0, datum.value] // From zero to value
|
|
245
|
-
getRange: (datum) => [datum.start, datum.end] // Time periods
|
|
246
|
-
getRange: (datum) => [datum.low, datum.high] // Price ranges
|
|
247
|
-
```
|
|
248
|
-
|
|
249
|
-
## Use Cases
|
|
250
|
-
|
|
251
|
-
Floating bar charts are particularly effective for:
|
|
252
|
-
|
|
253
|
-
### Financial Data
|
|
254
|
-
|
|
255
|
-
- **Stock Prices**: Daily high/low ranges
|
|
256
|
-
- **Budget Ranges**: Min/max spending limits
|
|
257
|
-
- **Price Variations**: Product price fluctuations
|
|
258
|
-
- **Investment Returns**: Best/worst case scenarios
|
|
259
|
-
|
|
260
|
-
### Project Management
|
|
261
|
-
|
|
262
|
-
- **Timeline Ranges**: Planned vs actual durations
|
|
263
|
-
- **Resource Allocation**: Min/max team sizes
|
|
264
|
-
- **Budget Estimates**: Cost range projections
|
|
265
|
-
- **Risk Assessment**: Impact range analysis
|
|
266
|
-
|
|
267
|
-
### Scientific Data
|
|
268
|
-
|
|
269
|
-
- **Temperature Ranges**: Daily, monthly, or seasonal variations
|
|
270
|
-
- **Measurement Tolerances**: Acceptable value ranges
|
|
271
|
-
- **Confidence Intervals**: Statistical data ranges
|
|
272
|
-
- **Performance Metrics**: Acceptable operating ranges
|
|
273
|
-
|
|
274
|
-
### Business Analytics
|
|
275
|
-
|
|
276
|
-
- **Sales Forecasts**: Optimistic/pessimistic projections
|
|
277
|
-
- **Capacity Planning**: Min/max resource requirements
|
|
278
|
-
- **Quality Metrics**: Acceptable performance ranges
|
|
279
|
-
- **Customer Satisfaction**: Score ranges by category
|
|
280
|
-
|
|
281
|
-
## Interactive Features
|
|
282
|
-
|
|
283
|
-
### Legend Controls
|
|
284
|
-
|
|
285
|
-
- Click legend items to show/hide data series
|
|
286
|
-
- Visual feedback on hover states
|
|
287
|
-
- At least one series must remain visible
|
|
288
|
-
- Colors automatically assigned from theme palette
|
|
289
|
-
|
|
290
|
-
### Bar Interactions
|
|
291
|
-
|
|
292
|
-
- Hover effects on floating bars
|
|
293
|
-
- Rich tooltips showing range details
|
|
294
|
-
- Smooth transitions and animations
|
|
295
|
-
|
|
296
|
-
### Responsive Behavior
|
|
297
|
-
|
|
298
|
-
- Chart automatically resizes to container dimensions
|
|
299
|
-
- Legend adapts to available space
|
|
300
|
-
- Maintains readability across different screen sizes
|
|
301
|
-
|
|
302
|
-
## Styling and Theme Integration
|
|
303
|
-
|
|
304
|
-
The component uses Fluent UI theme tokens:
|
|
305
|
-
|
|
306
|
-
```tsx
|
|
307
|
-
// Bar styling
|
|
308
|
-
backgroundColor: Derived from theme palette
|
|
309
|
-
borderRadius: 2 // Rounded corners
|
|
310
|
-
borderWidth: 1
|
|
311
|
-
|
|
312
|
-
// Typography
|
|
313
|
-
fontFamily: theme.fontFamilyBase
|
|
314
|
-
fontSize: theme.fontSizeBase200
|
|
315
|
-
fontWeight: theme.fontWeightSemibold
|
|
316
|
-
color: theme.colorNeutralForeground1
|
|
317
|
-
|
|
318
|
-
// Grid and axes
|
|
319
|
-
labelColor: theme.colorNeutralForeground1
|
|
320
|
-
gridColor: theme.colorNeutralStroke2
|
|
321
|
-
```
|
|
322
|
-
|
|
323
|
-
## Performance Optimizations
|
|
324
|
-
|
|
325
|
-
The component includes several React optimizations:
|
|
326
|
-
|
|
327
|
-
````tsx
|
|
328
|
-
// Memoized color calculations
|
|
329
|
-
const seriesColors = useMemo(() => {
|
|
330
|
-
return data.reduce((acc, series, idx) => {
|
|
331
|
-
const base = getFluentPalette(theme)[idx % getFluentPalette(theme).length];
|
|
332
|
-
const color = lightenColor(base, 0.3);
|
|
333
|
-
acc[series.label] = color;
|
|
334
|
-
return acc;
|
|
335
|
-
}, {} as Record<string, string>);
|
|
336
|
-
}, [data, theme]);
|
|
337
|
-
|
|
338
|
-
// Memoized chart data transformation
|
|
339
|
-
const chartData = useMemo(() => {
|
|
340
|
-
return {
|
|
341
|
-
labels: allCategories,
|
|
342
|
-
datasets: data
|
|
343
|
-
.filter(series => visibleSeries.includes(series.label))
|
|
344
|
-
.map(series => ({
|
|
345
|
-
label: series.label,
|
|
346
|
-
data: allCategories.map(cat => {
|
|
347
|
-
const match = series.data.find(d => getPrimary(d) === cat);
|
|
348
|
-
return match ? getRange(match) : [0, 0];
|
|
349
|
-
}),
|
|
350
|
-
backgroundColor: seriesColors[series.label],
|
|
351
|
-
borderRadius: 2,
|
|
352
|
-
})),
|
|
353
|
-
};
|
|
354
|
-
}, [data, visibleSeries, allCategories, getPrimary, getRange, seriesColors]);
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './FloatBarChart';
|
|
@@ -1,200 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
CategoryScale,
|
|
3
|
-
Chart as ChartJS,
|
|
4
|
-
ChartOptions,
|
|
5
|
-
Legend,
|
|
6
|
-
LineElement,
|
|
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 ChartDataLabels from 'chartjs-plugin-datalabels';
|
|
16
|
-
import { Line } from 'react-chartjs-2';
|
|
17
|
-
import RenderLegend from '../../components/RenderLegend/RenderLegend';
|
|
18
|
-
import { useChartUtils } from '../../hooks/useChartUtils';
|
|
19
|
-
import { useGraphGlobalStyles } from '../../graphGlobalStyles/useGraphGlobalStyles';
|
|
20
|
-
|
|
21
|
-
ChartJS.register(ChartDataLabels);
|
|
22
|
-
ChartJS.register(
|
|
23
|
-
LineElement,
|
|
24
|
-
PointElement,
|
|
25
|
-
CategoryScale,
|
|
26
|
-
LinearScale,
|
|
27
|
-
Tooltip,
|
|
28
|
-
Legend,
|
|
29
|
-
Title
|
|
30
|
-
);
|
|
31
|
-
|
|
32
|
-
export interface LineChartProps<T> {
|
|
33
|
-
data: { label: string; data: T[] }[];
|
|
34
|
-
getPrimary: (datum: T) => string | number;
|
|
35
|
-
getSecondary: (datum: T) => number;
|
|
36
|
-
title?: string;
|
|
37
|
-
showDataLabels?: boolean;
|
|
38
|
-
theme?: Theme;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export default function LineChart<T extends object>({
|
|
42
|
-
data,
|
|
43
|
-
getPrimary,
|
|
44
|
-
getSecondary,
|
|
45
|
-
title,
|
|
46
|
-
showDataLabels = false,
|
|
47
|
-
theme = webLightTheme,
|
|
48
|
-
}: LineChartProps<T>) {
|
|
49
|
-
const [visibleSeries, setVisibleSeries] = useState(() =>
|
|
50
|
-
data.length > 1 ? data.map(s => s.label) : [data[0]?.label]
|
|
51
|
-
);
|
|
52
|
-
|
|
53
|
-
const styles = useGraphGlobalStyles();
|
|
54
|
-
const { lightenColor, getFluentPalette , createFluentTooltip} = useChartUtils(theme);
|
|
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 ? getSecondary(match) : null;
|
|
93
|
-
}),
|
|
94
|
-
borderColor: seriesColors[series.label],
|
|
95
|
-
backgroundColor: seriesColors[series.label],
|
|
96
|
-
tension: 0.4,
|
|
97
|
-
fill: false,
|
|
98
|
-
pointRadius: 4,
|
|
99
|
-
})),
|
|
100
|
-
};
|
|
101
|
-
}, [
|
|
102
|
-
data,
|
|
103
|
-
visibleSeries,
|
|
104
|
-
allCategories,
|
|
105
|
-
getPrimary,
|
|
106
|
-
getSecondary,
|
|
107
|
-
seriesColors,
|
|
108
|
-
]);
|
|
109
|
-
|
|
110
|
-
const { fontFamily, fontSize, labelColor, gridColor } = useMemo(() => ({
|
|
111
|
-
fontFamily: theme.fontFamilyBase,
|
|
112
|
-
fontSize: parseInt(theme.fontSizeBase200.replace('px', '')) || 14,
|
|
113
|
-
labelColor: theme.colorNeutralForeground1,
|
|
114
|
-
gridColor: theme.colorNeutralStroke2,
|
|
115
|
-
}), [theme]);
|
|
116
|
-
|
|
117
|
-
const options: ChartOptions<'line'> = useMemo(() => ({
|
|
118
|
-
responsive: true,
|
|
119
|
-
maintainAspectRatio: false,
|
|
120
|
-
plugins: {
|
|
121
|
-
title: {
|
|
122
|
-
display: !!title,
|
|
123
|
-
text: title,
|
|
124
|
-
font: {
|
|
125
|
-
size: 14,
|
|
126
|
-
family: theme.fontFamilyBase,
|
|
127
|
-
weight: theme.fontWeightSemibold,
|
|
128
|
-
},
|
|
129
|
-
color: theme.colorNeutralForeground1,
|
|
130
|
-
padding: {
|
|
131
|
-
top: 20,
|
|
132
|
-
bottom: 20,
|
|
133
|
-
},
|
|
134
|
-
},
|
|
135
|
-
datalabels: {
|
|
136
|
-
display: showDataLabels,
|
|
137
|
-
color: theme.colorNeutralForeground1,
|
|
138
|
-
font: {
|
|
139
|
-
family: theme.fontFamilyBase,
|
|
140
|
-
size: parseInt(theme.fontSizeBase200.replace('px', '')) || 14,
|
|
141
|
-
},
|
|
142
|
-
},
|
|
143
|
-
legend: { display: false },
|
|
144
|
-
tooltip: createFluentTooltip<'line'>(theme),
|
|
145
|
-
},
|
|
146
|
-
scales: {
|
|
147
|
-
x: {
|
|
148
|
-
ticks: {
|
|
149
|
-
color: labelColor,
|
|
150
|
-
font: {
|
|
151
|
-
family: fontFamily,
|
|
152
|
-
size: fontSize,
|
|
153
|
-
},
|
|
154
|
-
},
|
|
155
|
-
grid: {
|
|
156
|
-
color: gridColor,
|
|
157
|
-
},
|
|
158
|
-
},
|
|
159
|
-
y: {
|
|
160
|
-
ticks: {
|
|
161
|
-
color: labelColor,
|
|
162
|
-
font: {
|
|
163
|
-
family: fontFamily,
|
|
164
|
-
size: fontSize,
|
|
165
|
-
},
|
|
166
|
-
},
|
|
167
|
-
grid: {
|
|
168
|
-
color: gridColor,
|
|
169
|
-
},
|
|
170
|
-
},
|
|
171
|
-
},
|
|
172
|
-
}), [
|
|
173
|
-
title,
|
|
174
|
-
theme,
|
|
175
|
-
showDataLabels,
|
|
176
|
-
labelColor,
|
|
177
|
-
fontFamily,
|
|
178
|
-
fontSize,
|
|
179
|
-
gridColor,
|
|
180
|
-
createFluentTooltip
|
|
181
|
-
]);
|
|
182
|
-
|
|
183
|
-
return (
|
|
184
|
-
|
|
185
|
-
<div className={styles.chartWithLegend}>
|
|
186
|
-
<div className={styles.chartArea}>
|
|
187
|
-
<Line data={chartData} options={options} />
|
|
188
|
-
</div>
|
|
189
|
-
<div className={styles.legendArea} >
|
|
190
|
-
<RenderLegend
|
|
191
|
-
data={data}
|
|
192
|
-
visibleSeries={visibleSeries}
|
|
193
|
-
seriesColors={seriesColors}
|
|
194
|
-
toggleSeries={toggleSeries}
|
|
195
|
-
/>
|
|
196
|
-
</div>
|
|
197
|
-
</div>
|
|
198
|
-
|
|
199
|
-
);
|
|
200
|
-
}
|