datastake-daf 0.6.308 → 0.6.310
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/components/index.js +312 -139
- package/package.json +1 -1
- package/src/@daf/core/components/Charts/AreaChart/index.jsx +13 -1
- package/src/@daf/core/components/Charts/BarChart/index.jsx +15 -2
- package/src/@daf/core/components/Charts/ColumnChart/ColumnChart.stories.jsx +168 -8
- package/src/@daf/core/components/Charts/ColumnChart/index.jsx +14 -1
- package/src/@daf/core/components/Charts/DonutPie/index.js +14 -2
- package/src/@daf/core/components/Charts/LineChart/index.jsx +18 -2
- package/src/@daf/core/components/Charts/PieChart/PieChart.stories.js +7 -0
- package/src/@daf/core/components/Charts/PieChart/chart.jsx +142 -31
- package/src/@daf/core/components/Charts/PieChart/style.js +25 -0
- package/src/@daf/core/components/Dashboard/Widget/WidgetImage/index.jsx +2 -1
- package/src/@daf/core/components/Dashboard/Widget/WidgetImage/index.stories.jsx +1 -0
package/package.json
CHANGED
|
@@ -72,11 +72,15 @@ const AreaChart = ({
|
|
|
72
72
|
formattedXAxis = (s) => s,
|
|
73
73
|
fillOpacity = 0.7,
|
|
74
74
|
height,
|
|
75
|
+
t = (s) => s,
|
|
76
|
+
isPdf = false,
|
|
77
|
+
extraLegendConfig = {},
|
|
75
78
|
...rest
|
|
76
79
|
}) => {
|
|
77
80
|
const containerRef = useRef(null);
|
|
78
81
|
const chartRef = useRef(null);
|
|
79
82
|
const { token } = useToken();
|
|
83
|
+
const hasLegendConfig = Object.keys(extraLegendConfig).length > 0;
|
|
80
84
|
|
|
81
85
|
useEffect(() => {
|
|
82
86
|
if (!containerRef.current) {
|
|
@@ -117,7 +121,15 @@ const AreaChart = ({
|
|
|
117
121
|
areaStyle: {
|
|
118
122
|
fillOpacity,
|
|
119
123
|
},
|
|
120
|
-
legend:
|
|
124
|
+
legend: isPdf
|
|
125
|
+
? {
|
|
126
|
+
itemName: {
|
|
127
|
+
formatter: (text) => t(text),
|
|
128
|
+
},
|
|
129
|
+
position: 'bottom',
|
|
130
|
+
...extraLegendConfig,
|
|
131
|
+
}
|
|
132
|
+
: hasLegendConfig || false,
|
|
121
133
|
line: false,
|
|
122
134
|
...rest,
|
|
123
135
|
};
|
|
@@ -84,11 +84,16 @@ export default function BarChart({
|
|
|
84
84
|
formattedXAxis = (s) => s,
|
|
85
85
|
color,
|
|
86
86
|
height,
|
|
87
|
+
t = (s) => s,
|
|
88
|
+
isPdf = false,
|
|
89
|
+
extraLegendConfig = {},
|
|
90
|
+
...rest
|
|
87
91
|
}) {
|
|
88
92
|
const containerRef = React.useRef(null);
|
|
89
93
|
const chartRef = React.useRef(null);
|
|
90
94
|
const { token } = useToken();
|
|
91
|
-
|
|
95
|
+
const hasLegendConfig = Object.keys(extraLegendConfig).length > 0;
|
|
96
|
+
|
|
92
97
|
React.useEffect(() => {
|
|
93
98
|
if (!containerRef.current) {
|
|
94
99
|
return;
|
|
@@ -129,7 +134,15 @@ export default function BarChart({
|
|
|
129
134
|
isStack,
|
|
130
135
|
color: color || token.colorPrimary7,
|
|
131
136
|
seriesField,
|
|
132
|
-
legend:
|
|
137
|
+
legend: isPdf
|
|
138
|
+
? {
|
|
139
|
+
itemName: {
|
|
140
|
+
formatter: (text) => t(text),
|
|
141
|
+
},
|
|
142
|
+
position: 'bottom',
|
|
143
|
+
...extraLegendConfig,
|
|
144
|
+
}
|
|
145
|
+
: hasLegendConfig || false,
|
|
133
146
|
animation: animated,
|
|
134
147
|
};
|
|
135
148
|
|
|
@@ -232,20 +232,23 @@ export const Percentage = {
|
|
|
232
232
|
},
|
|
233
233
|
};
|
|
234
234
|
|
|
235
|
-
const colorConfig = {
|
|
235
|
+
export const colorConfig = {
|
|
236
236
|
governance: {
|
|
237
|
-
|
|
238
|
-
|
|
237
|
+
improvement: "#4b6ae7",
|
|
238
|
+
evaluation: "#96b1ff",
|
|
239
|
+
negative: "#F04438",
|
|
239
240
|
},
|
|
240
241
|
social: {
|
|
241
|
-
|
|
242
|
-
|
|
242
|
+
improvement: "#864dd7",
|
|
243
|
+
evaluation: "#bf92ef",
|
|
244
|
+
negative: "#F04438",
|
|
243
245
|
},
|
|
244
246
|
environmental: {
|
|
245
|
-
|
|
246
|
-
|
|
247
|
+
improvement: "#f9b836",
|
|
248
|
+
evaluation: "#fedb7e",
|
|
249
|
+
negative: "#F04438",
|
|
247
250
|
},
|
|
248
|
-
}
|
|
251
|
+
}
|
|
249
252
|
|
|
250
253
|
export const functionColor = {
|
|
251
254
|
name: "Function Color",
|
|
@@ -301,3 +304,160 @@ export const functionColor = {
|
|
|
301
304
|
},
|
|
302
305
|
},
|
|
303
306
|
};
|
|
307
|
+
|
|
308
|
+
const getChartColor = (chartData, currentSelectedOption) => (s) => {
|
|
309
|
+
const all = chartData;
|
|
310
|
+
const [category, type] = s.groupBy.split("-");
|
|
311
|
+
const evaluationItem = all.find(
|
|
312
|
+
(item) => item.groupBy === `${category}-Baseline`,
|
|
313
|
+
);
|
|
314
|
+
const improvementItem = all.find(
|
|
315
|
+
(item) => item.groupBy === `${category}-Improvement`,
|
|
316
|
+
);
|
|
317
|
+
const isImprovementNegative =
|
|
318
|
+
improvementItem?.total - evaluationItem?.evaluationScore < 0;
|
|
319
|
+
|
|
320
|
+
if (currentSelectedOption === "aggregate") {
|
|
321
|
+
return (
|
|
322
|
+
(isImprovementNegative && type === "Improvement"
|
|
323
|
+
? colorConfig?.[category].negative
|
|
324
|
+
: colorConfig?.[category]?.[
|
|
325
|
+
type === "Baseline" ? "evaluation" : type.toLowerCase()
|
|
326
|
+
]) || "#BFBFBF"
|
|
327
|
+
);
|
|
328
|
+
} else {
|
|
329
|
+
return (
|
|
330
|
+
(isImprovementNegative && type === "Improvement"
|
|
331
|
+
? colorConfig?.[
|
|
332
|
+
currentSelectedOption.includes("governance")
|
|
333
|
+
? "governance"
|
|
334
|
+
: currentSelectedOption
|
|
335
|
+
].negative
|
|
336
|
+
: colorConfig?.[
|
|
337
|
+
currentSelectedOption.includes("governance")
|
|
338
|
+
? "governance"
|
|
339
|
+
: currentSelectedOption
|
|
340
|
+
]?.[type === "Baseline" ? "evaluation" : type.toLowerCase()]) ||
|
|
341
|
+
"#BFBFBF"
|
|
342
|
+
);
|
|
343
|
+
}
|
|
344
|
+
};
|
|
345
|
+
const graphData = [
|
|
346
|
+
{
|
|
347
|
+
"label": "Governance & Organisational Capacity",
|
|
348
|
+
"type": "Improvement",
|
|
349
|
+
"combinedKey": "governance-Improvement",
|
|
350
|
+
"groupBy": "governance-Improvement",
|
|
351
|
+
"value": 31,
|
|
352
|
+
"improvementScore": -31,
|
|
353
|
+
"evaluationScore": 76,
|
|
354
|
+
"color": "#F04438",
|
|
355
|
+
"total": 45,
|
|
356
|
+
"isNa": false
|
|
357
|
+
},
|
|
358
|
+
{
|
|
359
|
+
"label": "Governance & Organisational Capacity",
|
|
360
|
+
"type": "Baseline",
|
|
361
|
+
"combinedKey": "governance-Baseline",
|
|
362
|
+
"groupBy": "governance-Baseline",
|
|
363
|
+
"value": 45,
|
|
364
|
+
"evaluationScore": 76,
|
|
365
|
+
"color": "#96b1ff",
|
|
366
|
+
"total": 45,
|
|
367
|
+
"isNa": false
|
|
368
|
+
},
|
|
369
|
+
{
|
|
370
|
+
"label": "Social",
|
|
371
|
+
"type": "Improvement",
|
|
372
|
+
"combinedKey": "social-Improvement",
|
|
373
|
+
"groupBy": "social-Improvement",
|
|
374
|
+
"value": 61,
|
|
375
|
+
"improvementScore": -61,
|
|
376
|
+
"evaluationScore": 94,
|
|
377
|
+
"color": "#F04438",
|
|
378
|
+
"total": 33,
|
|
379
|
+
"isNa": false
|
|
380
|
+
},
|
|
381
|
+
{
|
|
382
|
+
"label": "Social",
|
|
383
|
+
"type": "Baseline",
|
|
384
|
+
"combinedKey": "social-Baseline",
|
|
385
|
+
"groupBy": "social-Baseline",
|
|
386
|
+
"value": 33,
|
|
387
|
+
"evaluationScore": 94,
|
|
388
|
+
"color": "#bf92ef",
|
|
389
|
+
"total": 33,
|
|
390
|
+
"isNa": false
|
|
391
|
+
},
|
|
392
|
+
{
|
|
393
|
+
"label": "Environmental",
|
|
394
|
+
"type": "Improvement",
|
|
395
|
+
"combinedKey": "environmental-Improvement",
|
|
396
|
+
"groupBy": "environmental-Improvement",
|
|
397
|
+
"value": 0,
|
|
398
|
+
"improvementScore": 0,
|
|
399
|
+
"evaluationScore": 100,
|
|
400
|
+
"color": "#f9b836",
|
|
401
|
+
"total": 100,
|
|
402
|
+
"isNa": false
|
|
403
|
+
},
|
|
404
|
+
{
|
|
405
|
+
"label": "Environmental",
|
|
406
|
+
"type": "Baseline",
|
|
407
|
+
"combinedKey": "environmental-Baseline",
|
|
408
|
+
"groupBy": "environmental-Baseline",
|
|
409
|
+
"value": 100,
|
|
410
|
+
"evaluationScore": 100,
|
|
411
|
+
"color": "#fedb7e",
|
|
412
|
+
"total": 100,
|
|
413
|
+
"isNa": false
|
|
414
|
+
}
|
|
415
|
+
]
|
|
416
|
+
|
|
417
|
+
const renderTooltipContent = (title, data) => {
|
|
418
|
+
const baselineItem = data.find((item) => item?.data?.type === "Baseline");
|
|
419
|
+
const improvementItem = data.find((item) => item?.data?.type === "Improvement");
|
|
420
|
+
const baseline = Number(baselineItem?.data?.evaluationScore) || 0;
|
|
421
|
+
const improvement = Number(improvementItem?.data?.improvementScore);
|
|
422
|
+
const total = Math.min(100, baseline + improvement);
|
|
423
|
+
|
|
424
|
+
return {
|
|
425
|
+
title,
|
|
426
|
+
items: [
|
|
427
|
+
{
|
|
428
|
+
label: "sbg::current",
|
|
429
|
+
value: improvementItem?.data?.isNa ? "N/A" : `${total}%`,
|
|
430
|
+
},
|
|
431
|
+
{
|
|
432
|
+
label: improvement < 0 ? "sbg::deterioration" : "sbg::improvement",
|
|
433
|
+
value: improvementItem?.data?.isNa ? "N/A" : `${improvement}%`,
|
|
434
|
+
color: improvementItem?.data?.color || "green",
|
|
435
|
+
},
|
|
436
|
+
{
|
|
437
|
+
label: "sbg::baseline",
|
|
438
|
+
value: baselineItem?.data?.isNa ? "N/A" : `${baseline}%`,
|
|
439
|
+
color: baselineItem?.data?.color || "blue",
|
|
440
|
+
},
|
|
441
|
+
],
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
export const Pdf = {
|
|
446
|
+
name: "Pdf",
|
|
447
|
+
args: {
|
|
448
|
+
data: graphData,
|
|
449
|
+
isPdf: true,
|
|
450
|
+
xFieldKey: "label",
|
|
451
|
+
yFieldKey: "value",
|
|
452
|
+
isStack: true,
|
|
453
|
+
seriesField:"groupBy",
|
|
454
|
+
groupField:"combinedKey",
|
|
455
|
+
color: getChartColor(graphData, "aggregate"),
|
|
456
|
+
renderTooltipContent: renderTooltipContent,
|
|
457
|
+
showBackground: true,
|
|
458
|
+
isPercentage: true,
|
|
459
|
+
},
|
|
460
|
+
render: (args) => {
|
|
461
|
+
return <ColumnChart {...args} />;
|
|
462
|
+
},
|
|
463
|
+
}
|
|
@@ -86,10 +86,15 @@ export default function ColumnChart({
|
|
|
86
86
|
color,
|
|
87
87
|
height,
|
|
88
88
|
groupField,
|
|
89
|
+
t = (s) => s,
|
|
90
|
+
isPdf = false,
|
|
91
|
+
extraLegendConfig = {},
|
|
92
|
+
...rest
|
|
89
93
|
}) {
|
|
90
94
|
const containerRef = React.useRef(null);
|
|
91
95
|
const chartRef = React.useRef(null);
|
|
92
96
|
const { token } = useToken();
|
|
97
|
+
const hasLegendConfig = Object.keys(extraLegendConfig).length > 0;
|
|
93
98
|
|
|
94
99
|
React.useEffect(() => {
|
|
95
100
|
if (!containerRef.current) {
|
|
@@ -137,7 +142,15 @@ export default function ColumnChart({
|
|
|
137
142
|
: formattedYAxis,
|
|
138
143
|
},
|
|
139
144
|
},
|
|
140
|
-
legend:
|
|
145
|
+
legend: isPdf
|
|
146
|
+
? {
|
|
147
|
+
itemName: {
|
|
148
|
+
formatter: (text) => t(text),
|
|
149
|
+
},
|
|
150
|
+
position: 'bottom',
|
|
151
|
+
...extraLegendConfig,
|
|
152
|
+
}
|
|
153
|
+
: hasLegendConfig || false,
|
|
141
154
|
// Add background pattern only when showBackground is true and isPercentage is true
|
|
142
155
|
...(showBackground && isPercentage && {
|
|
143
156
|
columnBackground: {
|
|
@@ -71,12 +71,16 @@ export default function DonutPie({
|
|
|
71
71
|
tooltipConfig = {},
|
|
72
72
|
meta,
|
|
73
73
|
animation = false,
|
|
74
|
+
t = (s) => s,
|
|
75
|
+
isPdf = false,
|
|
76
|
+
extraLegendConfig = {},
|
|
74
77
|
...rest
|
|
75
78
|
}) {
|
|
76
79
|
const containerRef = useRef(null);
|
|
77
80
|
const chartRef = useRef(null);
|
|
78
81
|
const { token } = useToken();
|
|
79
|
-
|
|
82
|
+
const hasLegendConfig = Object.keys(extraLegendConfig).length > 0;
|
|
83
|
+
|
|
80
84
|
// Memoize processed data for progress mode
|
|
81
85
|
const processedData = useMemo(() => {
|
|
82
86
|
if (!data || data.length === 0) {
|
|
@@ -108,7 +112,15 @@ export default function DonutPie({
|
|
|
108
112
|
color: color || [token.colorPrimary7, "#E8EDF3"],
|
|
109
113
|
radius,
|
|
110
114
|
innerRadius,
|
|
111
|
-
legend
|
|
115
|
+
legend: isPdf
|
|
116
|
+
? {
|
|
117
|
+
itemName: {
|
|
118
|
+
formatter: (text) => t(text),
|
|
119
|
+
},
|
|
120
|
+
position: 'bottom',
|
|
121
|
+
...extraLegendConfig,
|
|
122
|
+
}
|
|
123
|
+
: hasLegendConfig || false,
|
|
112
124
|
label,
|
|
113
125
|
statistic,
|
|
114
126
|
tooltip: tooltipOption,
|
|
@@ -82,11 +82,16 @@ export default function LineChart({
|
|
|
82
82
|
isPercentage = false,
|
|
83
83
|
height,
|
|
84
84
|
autoHideXLabel = true,
|
|
85
|
+
t = (s) => s,
|
|
86
|
+
isPdf = false,
|
|
87
|
+
extraLegendConfig = {},
|
|
88
|
+
...rest
|
|
85
89
|
}) {
|
|
86
90
|
const containerRef = React.useRef(null);
|
|
87
91
|
const chartRef = React.useRef(null);
|
|
88
92
|
const { token } = useToken();
|
|
89
|
-
|
|
93
|
+
const hasLegendConfig = Object.keys(extraLegendConfig).length > 0;
|
|
94
|
+
|
|
90
95
|
React.useEffect(() => {
|
|
91
96
|
if (!containerRef.current) {
|
|
92
97
|
return;
|
|
@@ -140,7 +145,15 @@ export default function LineChart({
|
|
|
140
145
|
fillOpacity: isArea ? 0.15 : 0,
|
|
141
146
|
},
|
|
142
147
|
},
|
|
143
|
-
legend:
|
|
148
|
+
legend: isPdf
|
|
149
|
+
? {
|
|
150
|
+
itemName: {
|
|
151
|
+
formatter: (text) => t(text),
|
|
152
|
+
},
|
|
153
|
+
position: 'bottom',
|
|
154
|
+
...extraLegendConfig,
|
|
155
|
+
}
|
|
156
|
+
: hasLegendConfig || false,
|
|
144
157
|
};
|
|
145
158
|
|
|
146
159
|
if (!chartRef.current) {
|
|
@@ -165,6 +178,9 @@ export default function LineChart({
|
|
|
165
178
|
isPercentage,
|
|
166
179
|
renderTooltipContent,
|
|
167
180
|
tooltipConfig,
|
|
181
|
+
isPdf,
|
|
182
|
+
extraLegendConfig,
|
|
183
|
+
t,
|
|
168
184
|
]);
|
|
169
185
|
|
|
170
186
|
React.useEffect(() => {
|
|
@@ -20,20 +20,27 @@ export const Primary = {
|
|
|
20
20
|
data: [
|
|
21
21
|
{
|
|
22
22
|
"percent": 0.8333333333333334,
|
|
23
|
+
"label": "Label 1",
|
|
23
24
|
"color": "#F5C2AC"
|
|
24
25
|
},
|
|
25
26
|
{
|
|
26
27
|
"percent": 0.06666666666666667,
|
|
28
|
+
"label": "Label 2",
|
|
27
29
|
"color": "#F0A888"
|
|
28
30
|
},
|
|
29
31
|
{
|
|
30
32
|
"percent": 0.06666666666666667,
|
|
33
|
+
"label": "Label 3",
|
|
31
34
|
"color": "#DF571E"
|
|
32
35
|
},
|
|
33
36
|
{
|
|
34
37
|
"percent": 0.03333333333333333,
|
|
38
|
+
"label": "Label 4",
|
|
35
39
|
"color": "#C04B19"
|
|
36
40
|
}
|
|
37
41
|
],
|
|
42
|
+
legend: false,
|
|
43
|
+
isPdf: true,
|
|
44
|
+
isPercentage: true,
|
|
38
45
|
},
|
|
39
46
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import PropTypes from 'prop-types'
|
|
2
2
|
import React, { useCallback, useMemo, useRef, useState, Fragment } from 'react';
|
|
3
3
|
import Tooltip from './tooltip.jsx';
|
|
4
|
-
import { ChartStyle } from './style.js';
|
|
4
|
+
import { ChartStyle, LegendStyle } from './style.js';
|
|
5
5
|
|
|
6
6
|
const Chart = ({
|
|
7
7
|
data = [],
|
|
@@ -16,6 +16,9 @@ const Chart = ({
|
|
|
16
16
|
mouseXOffset = 30,
|
|
17
17
|
mouseYOffset = 50,
|
|
18
18
|
children = null,
|
|
19
|
+
legend,
|
|
20
|
+
isPdf = false,
|
|
21
|
+
isPercentage = false,
|
|
19
22
|
}) => {
|
|
20
23
|
const [hoveredGroup, setHoveredGroup] = useState(null);
|
|
21
24
|
const svgRef = useRef();
|
|
@@ -50,68 +53,121 @@ const Chart = ({
|
|
|
50
53
|
setMouseX(x);
|
|
51
54
|
|
|
52
55
|
if (hoveredGroup === null) {
|
|
53
|
-
setHoveredGroup({ ...hG, id });
|
|
56
|
+
setHoveredGroup({ ...hG, id, fromLegend: false });
|
|
54
57
|
}
|
|
55
58
|
}, [svgRef, containerRef, hoveredGroup, mouseLocked]);
|
|
56
59
|
|
|
60
|
+
const legendConfig = useMemo(() => {
|
|
61
|
+
if (legend === false || legend === null) {
|
|
62
|
+
return isPdf ? {} : null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (typeof legend === 'object') {
|
|
66
|
+
return legend;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (legend === true || legend === undefined || isPdf) {
|
|
70
|
+
return {};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return null;
|
|
74
|
+
}, [legend, isPdf]);
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
const legendItems = useMemo(() => {
|
|
78
|
+
const items = Array.isArray(data) ? data : [];
|
|
79
|
+
|
|
80
|
+
if (!legendConfig) {
|
|
81
|
+
return [];
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const { itemName } = legendConfig;
|
|
85
|
+
const getName = typeof itemName?.formatter === 'function'
|
|
86
|
+
? (item, index) => itemName.formatter(item.name || item.label || `Item ${index + 1}`, item, index)
|
|
87
|
+
: (item, index) => item.name || item.label || `Item ${index + 1}`;
|
|
88
|
+
|
|
89
|
+
return items.map((item, index) => ({
|
|
90
|
+
...item,
|
|
91
|
+
label: getName(item, index),
|
|
92
|
+
}));
|
|
93
|
+
}, [data, legendConfig]);
|
|
94
|
+
|
|
57
95
|
const dataToShow = useMemo(() => {
|
|
58
96
|
let cumulativePercent = 0;
|
|
97
|
+
function getRotationAngle(percent) {
|
|
98
|
+
let angle = percent * 360 - 90;
|
|
99
|
+
|
|
100
|
+
if (angle > 90 && angle < 270) {
|
|
101
|
+
angle += 180;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const tilt = 30;
|
|
105
|
+
return angle + tilt;
|
|
106
|
+
}
|
|
107
|
+
|
|
59
108
|
|
|
60
109
|
return data.map((d, i) => {
|
|
61
110
|
const [startX, startY] = getCoordinatesForPercent(cumulativePercent);
|
|
62
|
-
|
|
111
|
+
const startPercent = cumulativePercent;
|
|
112
|
+
|
|
113
|
+
cumulativePercent += d.percent;
|
|
63
114
|
const [endX, endY] = getCoordinatesForPercent(cumulativePercent);
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const
|
|
67
|
-
const
|
|
68
|
-
|
|
115
|
+
|
|
116
|
+
// Midpoint of this slice
|
|
117
|
+
const midPercent = startPercent + d.percent / 2;
|
|
118
|
+
const [labelX, labelY] = getCoordinatesForPercent(midPercent);
|
|
119
|
+
|
|
120
|
+
const largeArcFlag = d.percent > 0.5 ? 1 : 0;
|
|
121
|
+
|
|
69
122
|
const pathData = [
|
|
70
123
|
`M ${startX} ${startY}`,
|
|
71
124
|
`A 1 1 0 ${largeArcFlag} 1 ${endX} ${endY}`,
|
|
72
125
|
`L 0 0`,
|
|
73
126
|
].join(' ');
|
|
74
|
-
|
|
75
|
-
const borderPathData = [
|
|
76
|
-
`M ${borderStartX} ${borderStartY}`,
|
|
77
|
-
`A 1 1 0 0 1 ${borderEndX} ${borderEndY}`,
|
|
78
|
-
`L 0 0`,
|
|
79
|
-
].join(' ');
|
|
80
|
-
|
|
127
|
+
|
|
81
128
|
return (
|
|
82
129
|
<Fragment key={i.toString()}>
|
|
83
130
|
<path
|
|
84
131
|
d={pathData}
|
|
85
|
-
data-bs-toggle="tooltip"
|
|
86
|
-
data-bs-placement="top"
|
|
87
|
-
title="Tooltip on top"
|
|
88
132
|
fill={d.color}
|
|
89
133
|
style={{
|
|
90
134
|
opacity: changeOpacityOnHover
|
|
91
|
-
? hoveredGroup === null
|
|
135
|
+
? hoveredGroup === null
|
|
136
|
+
? 1
|
|
137
|
+
: hoveredGroup.id === i
|
|
138
|
+
? 1
|
|
139
|
+
: 0.4
|
|
92
140
|
: 1,
|
|
93
141
|
}}
|
|
94
142
|
onMouseEnter={() => {
|
|
95
|
-
if (mouseLocked) {
|
|
96
|
-
|
|
143
|
+
if (!mouseLocked) {
|
|
144
|
+
setHoveredGroup({ ...d, id: i });
|
|
97
145
|
}
|
|
98
|
-
|
|
99
|
-
setHoveredGroup({ ...d, id: i })
|
|
100
146
|
}}
|
|
101
147
|
onMouseLeave={() => {
|
|
102
|
-
if (mouseLocked) {
|
|
103
|
-
|
|
148
|
+
if (!mouseLocked) {
|
|
149
|
+
setHoveredGroup(null);
|
|
104
150
|
}
|
|
105
|
-
|
|
106
|
-
setHoveredGroup(null)
|
|
107
151
|
}}
|
|
108
152
|
onMouseMove={(e) => onMouseLeaveHandler(e, d, i)}
|
|
109
153
|
onClick={() => setMouseLocked((prev) => !prev)}
|
|
110
154
|
/>
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
155
|
+
|
|
156
|
+
{/* White border if needed */}
|
|
157
|
+
<path d={pathData} fill="none" stroke="white" strokeWidth="0.001" />
|
|
158
|
+
|
|
159
|
+
{/* Label */}
|
|
160
|
+
{isPdf ? <text
|
|
161
|
+
x={labelX * 0.6}
|
|
162
|
+
y={labelY * 0.6}
|
|
163
|
+
textAnchor="middle"
|
|
164
|
+
dominantBaseline="middle"
|
|
165
|
+
fontSize="0.1"
|
|
166
|
+
fill="#000"
|
|
167
|
+
transform={`rotate(${getRotationAngle(midPercent)}, ${labelX * 0.6}, ${labelY * 0.6})`}
|
|
168
|
+
>
|
|
169
|
+
{d.percent ? isPercentage ? `${Math.round(d.percent * 100)}%` : d.percent : d.label}
|
|
170
|
+
</text> : null}
|
|
115
171
|
</Fragment>
|
|
116
172
|
);
|
|
117
173
|
});
|
|
@@ -163,6 +219,61 @@ const Chart = ({
|
|
|
163
219
|
{dataToShow}
|
|
164
220
|
</svg>
|
|
165
221
|
{tooltip}
|
|
222
|
+
{!!legendConfig && legendItems.length > 0 && (
|
|
223
|
+
<LegendStyle className={legendConfig?.className} data-position={legendConfig?.position || 'bottom'}>
|
|
224
|
+
{legendItems.map((item, index) => {
|
|
225
|
+
const markerStyle = legendConfig?.marker || {};
|
|
226
|
+
|
|
227
|
+
return (
|
|
228
|
+
<li
|
|
229
|
+
key={item.id ?? index}
|
|
230
|
+
className={item.className}
|
|
231
|
+
onMouseEnter={() => {
|
|
232
|
+
if (mouseLocked) {
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
setHoveredGroup({ ...item, id: index, fromLegend: true });
|
|
237
|
+
}}
|
|
238
|
+
onMouseLeave={() => {
|
|
239
|
+
if (mouseLocked) {
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
setHoveredGroup(null);
|
|
244
|
+
}}
|
|
245
|
+
onClick={() => {
|
|
246
|
+
if (!changeOpacityOnHover) {
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (hoveredGroup?.id === index && hoveredGroup?.fromLegend) {
|
|
251
|
+
setHoveredGroup(null);
|
|
252
|
+
setMouseLocked(false);
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
setHoveredGroup({ ...item, id: index, fromLegend: true });
|
|
257
|
+
setMouseLocked((prev) => !prev);
|
|
258
|
+
}}
|
|
259
|
+
>
|
|
260
|
+
<span
|
|
261
|
+
className="legend-marker"
|
|
262
|
+
style={{
|
|
263
|
+
backgroundColor: item.color,
|
|
264
|
+
width: markerStyle?.size || 12,
|
|
265
|
+
height: markerStyle?.size || 12,
|
|
266
|
+
borderRadius: markerStyle?.shape === 'square' ? 2 : '50%',
|
|
267
|
+
border: markerStyle?.style === 'line' ? `2px solid ${item.color}` : undefined,
|
|
268
|
+
background: markerStyle?.style === 'line' ? 'transparent' : item.color,
|
|
269
|
+
}}
|
|
270
|
+
/>
|
|
271
|
+
<span>{item.label}</span>
|
|
272
|
+
</li>
|
|
273
|
+
);
|
|
274
|
+
})}
|
|
275
|
+
</LegendStyle>
|
|
276
|
+
)}
|
|
166
277
|
{children}
|
|
167
278
|
</ChartStyle>
|
|
168
279
|
);
|
|
@@ -197,4 +197,29 @@ export const TooltipStyle = styled.div`
|
|
|
197
197
|
|
|
198
198
|
`;
|
|
199
199
|
|
|
200
|
+
export const LegendStyle = styled.ul`
|
|
201
|
+
list-style: none;
|
|
202
|
+
margin: var(--size-lg) 0 0;
|
|
203
|
+
padding: 0;
|
|
204
|
+
display: flex;
|
|
205
|
+
flex-wrap: wrap;
|
|
206
|
+
gap: 12px;
|
|
207
|
+
justify-content: center;
|
|
208
|
+
|
|
209
|
+
li {
|
|
210
|
+
display: flex;
|
|
211
|
+
align-items: center;
|
|
212
|
+
gap: 8px;
|
|
213
|
+
font-size: 14px;
|
|
214
|
+
color: #344054;
|
|
215
|
+
white-space: nowrap;
|
|
216
|
+
|
|
217
|
+
.legend-marker {
|
|
218
|
+
width: 12px;
|
|
219
|
+
height: 12px;
|
|
220
|
+
border-radius: 50%;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
`;
|
|
224
|
+
|
|
200
225
|
export default Style;
|
|
@@ -8,6 +8,7 @@ export default function ImageWidget({
|
|
|
8
8
|
description,
|
|
9
9
|
imgAlt,
|
|
10
10
|
noDescriptionText,
|
|
11
|
+
isPdf = false,
|
|
11
12
|
...props
|
|
12
13
|
}) {
|
|
13
14
|
return (
|
|
@@ -42,7 +43,7 @@ export default function ImageWidget({
|
|
|
42
43
|
|
|
43
44
|
<div
|
|
44
45
|
style={{
|
|
45
|
-
maxHeight: "195px",
|
|
46
|
+
maxHeight: isPdf ? "100%" : "195px",
|
|
46
47
|
paddingRight: "20px",
|
|
47
48
|
overflowY: "auto",
|
|
48
49
|
scrollbarWidth: "thin",
|
|
@@ -23,6 +23,7 @@ export const Primary = {
|
|
|
23
23
|
image: "https://picsum.photos/200/300",
|
|
24
24
|
description:
|
|
25
25
|
"This is a description of the image widget. It can be long and scrollable. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
|
|
26
|
+
isPdf: true,
|
|
26
27
|
},
|
|
27
28
|
};
|
|
28
29
|
|