@gravity-ui/chartkit 4.10.1 → 4.12.0
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/build/plugins/d3/renderer/components/Legend.js +54 -20
- package/build/plugins/d3/renderer/components/Tooltip/DefaultContent.js +3 -3
- package/build/plugins/d3/renderer/components/styles.css +4 -4
- package/build/plugins/d3/renderer/hooks/useChartOptions/x-axis.js +1 -4
- package/build/plugins/d3/renderer/hooks/useChartOptions/y-axis.js +1 -4
- package/build/plugins/d3/renderer/hooks/useSeries/constants.d.ts +1 -0
- package/build/plugins/d3/renderer/hooks/useSeries/constants.js +1 -0
- package/build/plugins/d3/renderer/hooks/useSeries/prepare-legend.d.ts +1 -1
- package/build/plugins/d3/renderer/hooks/useSeries/prepare-line-series.d.ts +2 -0
- package/build/plugins/d3/renderer/hooks/useSeries/prepare-line-series.js +16 -4
- package/build/plugins/d3/renderer/hooks/useSeries/prepare-pie.d.ts +8 -0
- package/build/plugins/d3/renderer/hooks/useSeries/prepare-pie.js +48 -0
- package/build/plugins/d3/renderer/hooks/useSeries/prepareSeries.js +1 -38
- package/build/plugins/d3/renderer/hooks/useSeries/types.d.ts +25 -4
- package/build/plugins/d3/renderer/hooks/useSeries/utils.js +2 -2
- package/build/plugins/d3/renderer/hooks/useShapes/index.d.ts +2 -1
- package/build/plugins/d3/renderer/hooks/useShapes/index.js +8 -5
- package/build/plugins/d3/renderer/hooks/useShapes/pie/index.d.ts +12 -0
- package/build/plugins/d3/renderer/hooks/useShapes/pie/index.js +155 -0
- package/build/plugins/d3/renderer/hooks/useShapes/pie/prepare-data.d.ts +9 -0
- package/build/plugins/d3/renderer/hooks/useShapes/pie/prepare-data.js +161 -0
- package/build/plugins/d3/renderer/hooks/useShapes/pie/types.d.ts +34 -0
- package/build/plugins/d3/renderer/hooks/useShapes/pie/types.js +1 -0
- package/build/plugins/d3/renderer/hooks/useShapes/pie/utils.d.ts +4 -0
- package/build/plugins/d3/renderer/hooks/useShapes/pie/utils.js +15 -0
- package/build/plugins/d3/renderer/hooks/useShapes/styles.css +1 -0
- package/build/plugins/d3/renderer/hooks/useShapes/utils.js +2 -6
- package/build/plugins/d3/renderer/types/index.d.ts +1 -1
- package/build/plugins/d3/renderer/utils/axis-generators/bottom.js +1 -1
- package/build/plugins/d3/renderer/utils/labels.d.ts +4 -1
- package/build/plugins/d3/renderer/utils/labels.js +14 -5
- package/build/plugins/d3/renderer/utils/text.d.ts +3 -3
- package/build/plugins/d3/renderer/utils/text.js +10 -7
- package/build/plugins/highcharts/renderer/components/withSplitPane/withSplitPane.js +4 -3
- package/build/plugins/yagr/renderer/YagrWidget.js +1 -1
- package/build/plugins/yagr/renderer/useWidgetData.d.ts +1 -1
- package/build/plugins/yagr/renderer/utils.js +6 -0
- package/build/plugins/yagr/types.d.ts +1 -1
- package/build/types/widget-data/legend.d.ts +8 -0
- package/build/types/widget-data/pie.d.ts +29 -0
- package/build/types/widget-data/tooltip.d.ts +5 -1
- package/package.json +2 -2
- package/build/plugins/d3/renderer/hooks/useShapes/pie.d.ts +0 -13
- package/build/plugins/d3/renderer/hooks/useShapes/pie.js +0 -204
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { select } from 'd3';
|
|
2
|
+
import { select, line as lineGenerator } from 'd3';
|
|
3
3
|
import { block } from '../../../../utils/cn';
|
|
4
4
|
const b = block('d3-legend');
|
|
5
5
|
const getLegendPosition = (args) => {
|
|
@@ -56,6 +56,58 @@ const appendPaginator = (args) => {
|
|
|
56
56
|
});
|
|
57
57
|
paginationLine.attr('transform', transform);
|
|
58
58
|
};
|
|
59
|
+
const legendSymbolGenerator = lineGenerator()
|
|
60
|
+
.x((d) => d.x)
|
|
61
|
+
.y((d) => d.y);
|
|
62
|
+
function renderLegendSymbol(args) {
|
|
63
|
+
const { selection, legend } = args;
|
|
64
|
+
const line = selection.data();
|
|
65
|
+
const getXPosition = (i) => {
|
|
66
|
+
return line.slice(0, i).reduce((acc, legendItem) => {
|
|
67
|
+
return (acc +
|
|
68
|
+
legendItem.symbol.width +
|
|
69
|
+
legendItem.symbol.padding +
|
|
70
|
+
legendItem.textWidth +
|
|
71
|
+
legend.itemDistance);
|
|
72
|
+
}, 0);
|
|
73
|
+
};
|
|
74
|
+
selection.each(function (d, i) {
|
|
75
|
+
const element = select(this);
|
|
76
|
+
const x = getXPosition(i);
|
|
77
|
+
const className = b('item-symbol', { shape: d.symbol.shape, unselected: !d.visible });
|
|
78
|
+
const color = d.visible ? d.color : '';
|
|
79
|
+
switch (d.symbol.shape) {
|
|
80
|
+
case 'path': {
|
|
81
|
+
const y = legend.lineHeight / 2;
|
|
82
|
+
const points = [
|
|
83
|
+
{ x: x, y },
|
|
84
|
+
{ x: x + d.symbol.width, y },
|
|
85
|
+
];
|
|
86
|
+
element
|
|
87
|
+
.append('path')
|
|
88
|
+
.attr('d', legendSymbolGenerator(points))
|
|
89
|
+
.attr('fill', 'none')
|
|
90
|
+
.attr('stroke-width', d.symbol.strokeWidth)
|
|
91
|
+
.attr('class', className)
|
|
92
|
+
.style('stroke', color);
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
case 'rect': {
|
|
96
|
+
const y = (legend.lineHeight - d.symbol.height) / 2;
|
|
97
|
+
element
|
|
98
|
+
.append('rect')
|
|
99
|
+
.attr('x', x)
|
|
100
|
+
.attr('y', y)
|
|
101
|
+
.attr('width', d.symbol.width)
|
|
102
|
+
.attr('height', d.symbol.height)
|
|
103
|
+
.attr('rx', d.symbol.radius)
|
|
104
|
+
.attr('class', className)
|
|
105
|
+
.style('fill', color);
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
}
|
|
59
111
|
export const Legend = (props) => {
|
|
60
112
|
const { boundsWidth, chartSeries, legend, items, config, onItemClick } = props;
|
|
61
113
|
const ref = React.useRef(null);
|
|
@@ -95,25 +147,7 @@ export const Legend = (props) => {
|
|
|
95
147
|
legend.itemDistance);
|
|
96
148
|
}, 0);
|
|
97
149
|
};
|
|
98
|
-
legendItemTemplate
|
|
99
|
-
.append('rect')
|
|
100
|
-
.attr('x', function (_d, i) {
|
|
101
|
-
return getXPosition(i);
|
|
102
|
-
})
|
|
103
|
-
.attr('y', (legendItem) => {
|
|
104
|
-
return (legend.lineHeight - legendItem.symbol.height) / 2;
|
|
105
|
-
})
|
|
106
|
-
.attr('width', (legendItem) => {
|
|
107
|
-
return legendItem.symbol.width;
|
|
108
|
-
})
|
|
109
|
-
.attr('height', (legendItem) => legendItem.symbol.height)
|
|
110
|
-
.attr('rx', (legendItem) => legendItem.symbol.radius)
|
|
111
|
-
.attr('class', function (d) {
|
|
112
|
-
return b('item-shape', { unselected: !d.visible });
|
|
113
|
-
})
|
|
114
|
-
.style('fill', function (d) {
|
|
115
|
-
return d.visible ? d.color : '';
|
|
116
|
-
});
|
|
150
|
+
renderLegendSymbol({ selection: legendItemTemplate, legend });
|
|
117
151
|
legendItemTemplate
|
|
118
152
|
.append('text')
|
|
119
153
|
.attr('x', function (legendItem, i) {
|
|
@@ -55,12 +55,12 @@ export const DefaultContent = ({ hovered, xAxis, yAxis }) => {
|
|
|
55
55
|
xRow))));
|
|
56
56
|
}
|
|
57
57
|
case 'pie': {
|
|
58
|
-
const
|
|
58
|
+
const pieSeriesData = data;
|
|
59
59
|
return (React.createElement("div", { key: id },
|
|
60
60
|
React.createElement("span", null,
|
|
61
|
-
|
|
61
|
+
pieSeriesData.name || pieSeriesData.id,
|
|
62
62
|
"\u00A0"),
|
|
63
|
-
React.createElement("span", null,
|
|
63
|
+
React.createElement("span", null, pieSeriesData.value)));
|
|
64
64
|
}
|
|
65
65
|
default: {
|
|
66
66
|
return null;
|
|
@@ -21,12 +21,12 @@
|
|
|
21
21
|
user-select: none;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
.chartkit-d3-legend__item-
|
|
25
|
-
fill: var(--g-color-
|
|
24
|
+
.chartkit-d3-legend__item-symbol_shape_rect.chartkit-d3-legend__item-symbol_unselected {
|
|
25
|
+
fill: var(--g-color-text-hint);
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
.chartkit-d3-legend__item-
|
|
29
|
-
|
|
28
|
+
.chartkit-d3-legend__item-symbol_shape_path.chartkit-d3-legend__item-symbol_unselected {
|
|
29
|
+
stroke: var(--g-color-text-hint);
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
.chartkit-d3-legend__item-text {
|
|
@@ -29,10 +29,7 @@ function getLabelSettings({ axis, series, width, autoRotation = true, }) {
|
|
|
29
29
|
const labelsHeight = rotation
|
|
30
30
|
? getLabelsSize({
|
|
31
31
|
labels,
|
|
32
|
-
style:
|
|
33
|
-
'font-size': axis.labels.style.fontSize,
|
|
34
|
-
'font-weight': axis.labels.style.fontWeight || 'normal',
|
|
35
|
-
},
|
|
32
|
+
style: axis.labels.style,
|
|
36
33
|
rotation,
|
|
37
34
|
}).maxHeight
|
|
38
35
|
: axis.labels.lineHeight;
|
|
@@ -18,10 +18,7 @@ const getAxisLabelMaxWidth = (args) => {
|
|
|
18
18
|
}));
|
|
19
19
|
return getLabelsSize({
|
|
20
20
|
labels,
|
|
21
|
-
style:
|
|
22
|
-
'font-size': axis.labels.style.fontSize,
|
|
23
|
-
'font-weight': axis.labels.style.fontWeight || '',
|
|
24
|
-
},
|
|
21
|
+
style: axis.labels.style,
|
|
25
22
|
rotation: axis.labels.rotation,
|
|
26
23
|
}).maxWidth;
|
|
27
24
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { BaseTextStyle } from '../../../../../types';
|
|
2
2
|
export declare const DEFAULT_LEGEND_SYMBOL_SIZE = 8;
|
|
3
|
+
export declare const DEFAULT_LEGEND_SYMBOL_PADDING = 5;
|
|
3
4
|
export declare const DEFAULT_DATALABELS_PADDING = 5;
|
|
4
5
|
export declare const DEFAULT_DATALABELS_STYLE: BaseTextStyle;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ChartKitWidgetData } from '../../../../../types
|
|
1
|
+
import type { ChartKitWidgetData } from '../../../../../types';
|
|
2
2
|
import type { PreparedAxis, PreparedChart } from '../useChartOptions/types';
|
|
3
3
|
import type { PreparedLegend, PreparedSeries, LegendItem } from './types';
|
|
4
4
|
export declare const getPreparedLegend: (args: {
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { ScaleOrdinal } from 'd3';
|
|
2
2
|
import { ChartKitWidgetSeriesOptions, LineSeries } from '../../../../../types';
|
|
3
3
|
import { PreparedLegend, PreparedSeries } from './types';
|
|
4
|
+
export declare const DEFAULT_LEGEND_SYMBOL_SIZE = 16;
|
|
5
|
+
export declare const DEFAULT_LINE_WIDTH = 1;
|
|
4
6
|
type PrepareLineSeriesArgs = {
|
|
5
7
|
colorScale: ScaleOrdinal<string, string>;
|
|
6
8
|
series: LineSeries[];
|
|
@@ -1,10 +1,22 @@
|
|
|
1
1
|
import get from 'lodash/get';
|
|
2
|
-
import { DEFAULT_DATALABELS_PADDING, DEFAULT_DATALABELS_STYLE } from './constants';
|
|
3
|
-
import { prepareLegendSymbol } from './utils';
|
|
2
|
+
import { DEFAULT_DATALABELS_PADDING, DEFAULT_DATALABELS_STYLE, DEFAULT_LEGEND_SYMBOL_PADDING, } from './constants';
|
|
4
3
|
import { getRandomCKId } from '../../../../../utils';
|
|
4
|
+
export const DEFAULT_LEGEND_SYMBOL_SIZE = 16;
|
|
5
|
+
export const DEFAULT_LINE_WIDTH = 1;
|
|
6
|
+
function prepareLineLegendSymbol(series, seriesOptions) {
|
|
7
|
+
var _a;
|
|
8
|
+
const symbolOptions = ((_a = series.legend) === null || _a === void 0 ? void 0 : _a.symbol) || {};
|
|
9
|
+
const defaultLineWidth = get(seriesOptions, 'line.lineWidth', DEFAULT_LINE_WIDTH);
|
|
10
|
+
return {
|
|
11
|
+
shape: 'path',
|
|
12
|
+
width: (symbolOptions === null || symbolOptions === void 0 ? void 0 : symbolOptions.width) || DEFAULT_LEGEND_SYMBOL_SIZE,
|
|
13
|
+
padding: (symbolOptions === null || symbolOptions === void 0 ? void 0 : symbolOptions.padding) || DEFAULT_LEGEND_SYMBOL_PADDING,
|
|
14
|
+
strokeWidth: get(series, 'lineWidth', defaultLineWidth),
|
|
15
|
+
};
|
|
16
|
+
}
|
|
5
17
|
export function prepareLineSeries(args) {
|
|
6
18
|
const { colorScale, series: seriesList, seriesOptions, legend } = args;
|
|
7
|
-
const defaultLineWidth = get(seriesOptions, 'line.lineWidth',
|
|
19
|
+
const defaultLineWidth = get(seriesOptions, 'line.lineWidth', DEFAULT_LINE_WIDTH);
|
|
8
20
|
return seriesList.map((series) => {
|
|
9
21
|
var _a, _b;
|
|
10
22
|
const id = getRandomCKId();
|
|
@@ -19,7 +31,7 @@ export function prepareLineSeries(args) {
|
|
|
19
31
|
visible: get(series, 'visible', true),
|
|
20
32
|
legend: {
|
|
21
33
|
enabled: get(series, 'legend.enabled', legend.enabled),
|
|
22
|
-
symbol:
|
|
34
|
+
symbol: prepareLineLegendSymbol(series, seriesOptions),
|
|
23
35
|
},
|
|
24
36
|
data: series.data,
|
|
25
37
|
dataLabels: {
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { PieSeries } from '../../../../../types';
|
|
2
|
+
import { PreparedLegend, PreparedSeries } from './types';
|
|
3
|
+
type PreparePieSeriesArgs = {
|
|
4
|
+
series: PieSeries;
|
|
5
|
+
legend: PreparedLegend;
|
|
6
|
+
};
|
|
7
|
+
export declare function preparePieSeries(args: PreparePieSeriesArgs): PreparedSeries[];
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { scaleOrdinal } from 'd3';
|
|
2
|
+
import { DEFAULT_PALETTE } from '../../constants';
|
|
3
|
+
import { getRandomCKId } from '../../../../../utils';
|
|
4
|
+
import get from 'lodash/get';
|
|
5
|
+
import { DEFAULT_DATALABELS_PADDING, DEFAULT_DATALABELS_STYLE } from './constants';
|
|
6
|
+
import { prepareLegendSymbol } from './utils';
|
|
7
|
+
export function preparePieSeries(args) {
|
|
8
|
+
const { series, legend } = args;
|
|
9
|
+
const dataNames = series.data.map((d) => d.name);
|
|
10
|
+
const colorScale = scaleOrdinal(dataNames, DEFAULT_PALETTE);
|
|
11
|
+
const stackId = getRandomCKId();
|
|
12
|
+
const preparedSeries = series.data.map((dataItem) => {
|
|
13
|
+
var _a, _b, _c;
|
|
14
|
+
const result = {
|
|
15
|
+
type: 'pie',
|
|
16
|
+
data: dataItem,
|
|
17
|
+
dataLabels: {
|
|
18
|
+
enabled: get(series, 'dataLabels.enabled', true),
|
|
19
|
+
style: Object.assign({}, DEFAULT_DATALABELS_STYLE, (_a = series.dataLabels) === null || _a === void 0 ? void 0 : _a.style),
|
|
20
|
+
padding: get(series, 'dataLabels.padding', DEFAULT_DATALABELS_PADDING),
|
|
21
|
+
allowOverlap: get(series, 'dataLabels.allowOverlap', false),
|
|
22
|
+
connectorPadding: get(series, 'dataLabels.connectorPadding', 5),
|
|
23
|
+
connectorShape: get(series, 'dataLabels.connectorShape', 'polyline'),
|
|
24
|
+
distance: get(series, 'dataLabels.distance', 25),
|
|
25
|
+
connectorCurve: get(series, 'dataLabels.connectorCurve', 'basic'),
|
|
26
|
+
},
|
|
27
|
+
label: dataItem.label,
|
|
28
|
+
value: dataItem.value,
|
|
29
|
+
visible: typeof dataItem.visible === 'boolean' ? dataItem.visible : true,
|
|
30
|
+
name: dataItem.name,
|
|
31
|
+
id: '',
|
|
32
|
+
color: dataItem.color || colorScale(dataItem.name),
|
|
33
|
+
legend: {
|
|
34
|
+
enabled: get(series, 'legend.enabled', legend.enabled),
|
|
35
|
+
symbol: prepareLegendSymbol(series),
|
|
36
|
+
},
|
|
37
|
+
center: series.center || ['50%', '50%'],
|
|
38
|
+
borderColor: series.borderColor || '',
|
|
39
|
+
borderRadius: (_b = series.borderRadius) !== null && _b !== void 0 ? _b : 0,
|
|
40
|
+
borderWidth: (_c = series.borderWidth) !== null && _c !== void 0 ? _c : 1,
|
|
41
|
+
radius: series.radius || '100%',
|
|
42
|
+
innerRadius: series.innerRadius || 0,
|
|
43
|
+
stackId,
|
|
44
|
+
};
|
|
45
|
+
return result;
|
|
46
|
+
});
|
|
47
|
+
return preparedSeries;
|
|
48
|
+
}
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import cloneDeep from 'lodash/cloneDeep';
|
|
2
2
|
import get from 'lodash/get';
|
|
3
|
-
import { scaleOrdinal } from 'd3';
|
|
4
|
-
import { getRandomCKId } from '../../../../../utils';
|
|
5
|
-
import { DEFAULT_PALETTE } from '../../constants';
|
|
6
3
|
import { prepareLineSeries } from './prepare-line-series';
|
|
7
4
|
import { prepareBarXSeries } from './prepare-bar-x';
|
|
8
5
|
import { prepareBarYSeries } from './prepare-bar-y';
|
|
9
6
|
import { prepareLegendSymbol } from './utils';
|
|
10
7
|
import { ChartKitError } from '../../../../../libs';
|
|
8
|
+
import { preparePieSeries } from './prepare-pie';
|
|
11
9
|
function prepareAxisRelatedSeries(args) {
|
|
12
10
|
const { colorScale, series, legend } = args;
|
|
13
11
|
const preparedSeries = cloneDeep(series);
|
|
@@ -21,41 +19,6 @@ function prepareAxisRelatedSeries(args) {
|
|
|
21
19
|
};
|
|
22
20
|
return [preparedSeries];
|
|
23
21
|
}
|
|
24
|
-
function preparePieSeries(args) {
|
|
25
|
-
const { series, legend } = args;
|
|
26
|
-
const dataNames = series.data.map((d) => d.name);
|
|
27
|
-
const colorScale = scaleOrdinal(dataNames, DEFAULT_PALETTE);
|
|
28
|
-
const stackId = getRandomCKId();
|
|
29
|
-
const preparedSeries = series.data.map((dataItem) => {
|
|
30
|
-
var _a, _b;
|
|
31
|
-
const result = {
|
|
32
|
-
type: 'pie',
|
|
33
|
-
data: dataItem,
|
|
34
|
-
dataLabels: {
|
|
35
|
-
enabled: get(series, 'dataLabels.enabled', true),
|
|
36
|
-
},
|
|
37
|
-
label: dataItem.label,
|
|
38
|
-
value: dataItem.value,
|
|
39
|
-
visible: typeof dataItem.visible === 'boolean' ? dataItem.visible : true,
|
|
40
|
-
name: dataItem.name,
|
|
41
|
-
id: '',
|
|
42
|
-
color: dataItem.color || colorScale(dataItem.name),
|
|
43
|
-
legend: {
|
|
44
|
-
enabled: get(series, 'legend.enabled', legend.enabled),
|
|
45
|
-
symbol: prepareLegendSymbol(series),
|
|
46
|
-
},
|
|
47
|
-
center: series.center || ['50%', '50%'],
|
|
48
|
-
borderColor: series.borderColor || '',
|
|
49
|
-
borderRadius: (_a = series.borderRadius) !== null && _a !== void 0 ? _a : 0,
|
|
50
|
-
borderWidth: (_b = series.borderWidth) !== null && _b !== void 0 ? _b : 1,
|
|
51
|
-
radius: series.radius || '100%',
|
|
52
|
-
innerRadius: series.innerRadius || 0,
|
|
53
|
-
stackId,
|
|
54
|
-
};
|
|
55
|
-
return result;
|
|
56
|
-
});
|
|
57
|
-
return preparedSeries;
|
|
58
|
-
}
|
|
59
22
|
export function prepareSeries(args) {
|
|
60
23
|
const { type, series, seriesOptions, legend, colorScale } = args;
|
|
61
24
|
switch (type) {
|
|
@@ -1,9 +1,13 @@
|
|
|
1
|
-
import { BarXSeries, BarXSeriesData, BaseTextStyle, ChartKitWidgetLegend, PieSeries, PieSeriesData, RectLegendSymbolOptions, ScatterSeries, ScatterSeriesData, BarYSeries, BarYSeriesData, LineSeries, LineSeriesData } from '../../../../../types';
|
|
1
|
+
import { BarXSeries, BarXSeriesData, BaseTextStyle, ChartKitWidgetLegend, PieSeries, PieSeriesData, RectLegendSymbolOptions, ScatterSeries, ScatterSeriesData, BarYSeries, BarYSeriesData, LineSeries, LineSeriesData, ConnectorShape, ConnectorCurve, PathLegendSymbolOptions } from '../../../../../types';
|
|
2
2
|
import type { SeriesOptionsDefaults } from '../../constants';
|
|
3
3
|
export type RectLegendSymbol = {
|
|
4
4
|
shape: 'rect';
|
|
5
5
|
} & Required<RectLegendSymbolOptions>;
|
|
6
|
-
export type
|
|
6
|
+
export type PathLegendSymbol = {
|
|
7
|
+
shape: 'path';
|
|
8
|
+
strokeWidth: number;
|
|
9
|
+
} & Required<PathLegendSymbolOptions>;
|
|
10
|
+
export type PreparedLegendSymbol = RectLegendSymbol | PathLegendSymbol;
|
|
7
11
|
export type PreparedLegend = Required<ChartKitWidgetLegend> & {
|
|
8
12
|
height: number;
|
|
9
13
|
lineHeight: number;
|
|
@@ -67,12 +71,29 @@ export type PreparedBarYSeries = {
|
|
|
67
71
|
maxWidth: number;
|
|
68
72
|
};
|
|
69
73
|
} & BasePreparedSeries;
|
|
70
|
-
export type PreparedPieSeries =
|
|
74
|
+
export type PreparedPieSeries = {
|
|
75
|
+
type: PieSeries['type'];
|
|
71
76
|
data: PieSeriesData;
|
|
72
77
|
value: PieSeriesData['value'];
|
|
78
|
+
borderColor: string;
|
|
79
|
+
borderWidth: number;
|
|
80
|
+
borderRadius: number;
|
|
81
|
+
center?: [string | number | null, string | number | null];
|
|
82
|
+
radius?: string | number;
|
|
83
|
+
innerRadius?: string | number;
|
|
73
84
|
stackId: string;
|
|
74
85
|
label?: PieSeriesData['label'];
|
|
75
|
-
|
|
86
|
+
dataLabels: {
|
|
87
|
+
enabled: boolean;
|
|
88
|
+
padding: number;
|
|
89
|
+
style: BaseTextStyle;
|
|
90
|
+
allowOverlap: boolean;
|
|
91
|
+
connectorPadding: number;
|
|
92
|
+
connectorShape: ConnectorShape;
|
|
93
|
+
distance: number;
|
|
94
|
+
connectorCurve: ConnectorCurve;
|
|
95
|
+
};
|
|
96
|
+
} & BasePreparedSeries;
|
|
76
97
|
export type PreparedLineSeries = {
|
|
77
98
|
type: LineSeries['type'];
|
|
78
99
|
data: LineSeriesData[];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DEFAULT_LEGEND_SYMBOL_SIZE } from './constants';
|
|
1
|
+
import { DEFAULT_LEGEND_SYMBOL_PADDING, DEFAULT_LEGEND_SYMBOL_SIZE } from './constants';
|
|
2
2
|
export const getActiveLegendItems = (series) => {
|
|
3
3
|
return series.reduce((acc, s) => {
|
|
4
4
|
if (s.legend.enabled && s.visible) {
|
|
@@ -19,6 +19,6 @@ export function prepareLegendSymbol(series) {
|
|
|
19
19
|
width: (symbolOptions === null || symbolOptions === void 0 ? void 0 : symbolOptions.width) || DEFAULT_LEGEND_SYMBOL_SIZE,
|
|
20
20
|
height: symbolHeight,
|
|
21
21
|
radius: (symbolOptions === null || symbolOptions === void 0 ? void 0 : symbolOptions.radius) || symbolHeight / 2,
|
|
22
|
-
padding: (symbolOptions === null || symbolOptions === void 0 ? void 0 : symbolOptions.padding) ||
|
|
22
|
+
padding: (symbolOptions === null || symbolOptions === void 0 ? void 0 : symbolOptions.padding) || DEFAULT_LEGEND_SYMBOL_PADDING,
|
|
23
23
|
};
|
|
24
24
|
}
|
|
@@ -5,12 +5,13 @@ import type { ChartScale } from '../useAxisScales';
|
|
|
5
5
|
import type { PreparedSeries, PreparedSeriesOptions } from '../';
|
|
6
6
|
import type { PreparedBarXData } from './bar-x';
|
|
7
7
|
import type { PreparedScatterData } from './scatter';
|
|
8
|
+
import type { PreparedPieData } from './pie/types';
|
|
8
9
|
import type { PreparedLineData } from './line/types';
|
|
9
10
|
import type { PreparedBarYData } from './bar-y/types';
|
|
10
11
|
export type { PreparedBarXData } from './bar-x';
|
|
11
12
|
export type { PreparedScatterData } from './scatter';
|
|
12
13
|
import './styles.css';
|
|
13
|
-
export type ShapeData = PreparedBarXData | PreparedBarYData | PreparedScatterData | PreparedLineData;
|
|
14
|
+
export type ShapeData = PreparedBarXData | PreparedBarYData | PreparedScatterData | PreparedLineData | PreparedPieData;
|
|
14
15
|
type Args = {
|
|
15
16
|
boundsWidth: number;
|
|
16
17
|
boundsHeight: number;
|
|
@@ -3,7 +3,8 @@ import { group } from 'd3';
|
|
|
3
3
|
import { getOnlyVisibleSeries } from '../../utils';
|
|
4
4
|
import { BarXSeriesShapes, prepareBarXData } from './bar-x';
|
|
5
5
|
import { ScatterSeriesShape, prepareScatterData } from './scatter';
|
|
6
|
-
import {
|
|
6
|
+
import { PieSeriesShapes } from './pie';
|
|
7
|
+
import { preparePieData } from './pie/prepare-data';
|
|
7
8
|
import { prepareLineData } from './line/prepare-data';
|
|
8
9
|
import { LineSeriesShapes } from './line';
|
|
9
10
|
import { BarYSeriesShapes, prepareBarYData } from './bar-y';
|
|
@@ -75,10 +76,12 @@ export const useShapes = (args) => {
|
|
|
75
76
|
break;
|
|
76
77
|
}
|
|
77
78
|
case 'pie': {
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
79
|
+
const preparedData = preparePieData({
|
|
80
|
+
series: chartSeries,
|
|
81
|
+
boundsWidth,
|
|
82
|
+
boundsHeight,
|
|
83
|
+
});
|
|
84
|
+
acc.push(React.createElement(PieSeriesShapes, { key: "pie", dispatcher: dispatcher, preparedData: preparedData, seriesOptions: seriesOptions, svgContainer: svgContainer }));
|
|
82
85
|
}
|
|
83
86
|
}
|
|
84
87
|
return acc;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { Dispatch } from 'd3';
|
|
3
|
+
import { PreparedSeriesOptions } from '../../useSeries/types';
|
|
4
|
+
import { PreparedPieData } from './types';
|
|
5
|
+
type PreparePieSeriesArgs = {
|
|
6
|
+
dispatcher: Dispatch<object>;
|
|
7
|
+
preparedData: PreparedPieData[];
|
|
8
|
+
seriesOptions: PreparedSeriesOptions;
|
|
9
|
+
svgContainer: SVGSVGElement | null;
|
|
10
|
+
};
|
|
11
|
+
export declare function PieSeriesShapes(args: PreparePieSeriesArgs): React.JSX.Element;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import get from 'lodash/get';
|
|
3
|
+
import { arc, color, pointer, select } from 'd3';
|
|
4
|
+
import { block } from '../../../../../../utils/cn';
|
|
5
|
+
import { setActiveState } from '../utils';
|
|
6
|
+
import { line as lineGenerator } from 'd3-shape';
|
|
7
|
+
import { setEllipsisForOverflowTexts } from '../../../utils';
|
|
8
|
+
import { getCurveFactory } from './utils';
|
|
9
|
+
const b = block('d3-pie');
|
|
10
|
+
export function PieSeriesShapes(args) {
|
|
11
|
+
const { dispatcher, preparedData, seriesOptions, svgContainer } = args;
|
|
12
|
+
const ref = React.useRef(null);
|
|
13
|
+
React.useEffect(() => {
|
|
14
|
+
if (!ref.current) {
|
|
15
|
+
return () => { };
|
|
16
|
+
}
|
|
17
|
+
const svgElement = select(ref.current);
|
|
18
|
+
svgElement.selectAll('*').remove();
|
|
19
|
+
const segmentSelector = `.${b('segment')}`;
|
|
20
|
+
const connectorSelector = `.${b('connector')}`;
|
|
21
|
+
const shapesSelection = svgElement
|
|
22
|
+
.selectAll('pie')
|
|
23
|
+
.data(preparedData)
|
|
24
|
+
.join('g')
|
|
25
|
+
.attr('id', (pieData) => pieData.id)
|
|
26
|
+
.attr('class', b('item'))
|
|
27
|
+
.attr('transform', (pieData) => {
|
|
28
|
+
const [x, y] = pieData.center;
|
|
29
|
+
return `translate(${x}, ${y})`;
|
|
30
|
+
})
|
|
31
|
+
.style('stroke', (pieData) => pieData.borderColor)
|
|
32
|
+
.style('stroke-width', (pieData) => pieData.borderWidth);
|
|
33
|
+
shapesSelection
|
|
34
|
+
.selectAll(segmentSelector)
|
|
35
|
+
.data((pieData) => pieData.segments)
|
|
36
|
+
.join('path')
|
|
37
|
+
.attr('d', (d) => {
|
|
38
|
+
const arcGenerator = arc()
|
|
39
|
+
.innerRadius(d.data.pie.innerRadius)
|
|
40
|
+
.outerRadius(d.data.pie.radius)
|
|
41
|
+
.cornerRadius(d.data.pie.borderRadius);
|
|
42
|
+
return arcGenerator(d);
|
|
43
|
+
})
|
|
44
|
+
.attr('class', b('segment'))
|
|
45
|
+
.attr('fill', (d) => d.data.color);
|
|
46
|
+
shapesSelection
|
|
47
|
+
.selectAll('text')
|
|
48
|
+
.data((pieData) => pieData.labels)
|
|
49
|
+
.join('text')
|
|
50
|
+
.text((d) => d.text)
|
|
51
|
+
.attr('class', b('label'))
|
|
52
|
+
.attr('x', (d) => d.x)
|
|
53
|
+
.attr('y', (d) => d.y)
|
|
54
|
+
.attr('text-anchor', (d) => d.textAnchor)
|
|
55
|
+
.style('font-size', (d) => d.style.fontSize)
|
|
56
|
+
.style('font-weight', (d) => d.style.fontWeight || null)
|
|
57
|
+
.style('fill', (d) => d.style.fontColor || null)
|
|
58
|
+
.call(setEllipsisForOverflowTexts, (d) => d.size.width > d.maxWidth ? d.maxWidth : Infinity);
|
|
59
|
+
// Add the polyline between chart and labels
|
|
60
|
+
shapesSelection
|
|
61
|
+
.selectAll(connectorSelector)
|
|
62
|
+
.data((pieData) => pieData.labels)
|
|
63
|
+
.enter()
|
|
64
|
+
.append('path')
|
|
65
|
+
.attr('class', b('connector'))
|
|
66
|
+
.attr('d', (d) => {
|
|
67
|
+
let line = lineGenerator();
|
|
68
|
+
const curveFactory = getCurveFactory(d.segment.pie);
|
|
69
|
+
if (curveFactory) {
|
|
70
|
+
line = line.curve(curveFactory);
|
|
71
|
+
}
|
|
72
|
+
return line(d.connector.points);
|
|
73
|
+
})
|
|
74
|
+
.attr('stroke', (d) => d.connector.color)
|
|
75
|
+
.attr('stroke-width', 1)
|
|
76
|
+
.attr('stroke-linejoin', 'round')
|
|
77
|
+
.attr('stroke-linecap', 'round')
|
|
78
|
+
.style('fill', 'none');
|
|
79
|
+
const eventName = `hover-shape.pie`;
|
|
80
|
+
const hoverOptions = get(seriesOptions, 'pie.states.hover');
|
|
81
|
+
const inactiveOptions = get(seriesOptions, 'pie.states.inactive');
|
|
82
|
+
svgElement
|
|
83
|
+
.on('mousemove', (e) => {
|
|
84
|
+
const datum = select(e.target).datum();
|
|
85
|
+
const seriesId = get(datum, 'data.series.id', get(datum, 'series.id'));
|
|
86
|
+
const currentSegment = preparedData.reduce((result, pie) => {
|
|
87
|
+
var _a;
|
|
88
|
+
return (result || ((_a = pie.segments.find((s) => s.data.series.id === seriesId)) === null || _a === void 0 ? void 0 : _a.data));
|
|
89
|
+
}, undefined);
|
|
90
|
+
if (currentSegment) {
|
|
91
|
+
const data = {
|
|
92
|
+
series: {
|
|
93
|
+
id: currentSegment.series.id,
|
|
94
|
+
type: 'pie',
|
|
95
|
+
name: currentSegment.series.name,
|
|
96
|
+
},
|
|
97
|
+
data: currentSegment.series,
|
|
98
|
+
};
|
|
99
|
+
dispatcher.call('hover-shape', {}, [data], pointer(e, svgContainer));
|
|
100
|
+
}
|
|
101
|
+
})
|
|
102
|
+
.on('mouseleave', () => {
|
|
103
|
+
dispatcher.call('hover-shape', {}, undefined);
|
|
104
|
+
});
|
|
105
|
+
dispatcher.on(eventName, (data) => {
|
|
106
|
+
const selectedSeriesId = data === null || data === void 0 ? void 0 : data[0].series.id;
|
|
107
|
+
const hoverEnabled = hoverOptions === null || hoverOptions === void 0 ? void 0 : hoverOptions.enabled;
|
|
108
|
+
const inactiveEnabled = inactiveOptions === null || inactiveOptions === void 0 ? void 0 : inactiveOptions.enabled;
|
|
109
|
+
shapesSelection.datum((_d, index, list) => {
|
|
110
|
+
const pieSelection = select(list[index]);
|
|
111
|
+
pieSelection
|
|
112
|
+
.selectAll(segmentSelector)
|
|
113
|
+
.datum((d, i, elements) => {
|
|
114
|
+
const hovered = Boolean(hoverEnabled && d.data.series.id === selectedSeriesId);
|
|
115
|
+
if (d.data.hovered !== hovered) {
|
|
116
|
+
d.data.hovered = hovered;
|
|
117
|
+
select(elements[i]).attr('fill', () => {
|
|
118
|
+
var _a;
|
|
119
|
+
const initialColor = d.data.color;
|
|
120
|
+
if (d.data.hovered) {
|
|
121
|
+
return (((_a = color(initialColor)) === null || _a === void 0 ? void 0 : _a.brighter(hoverOptions === null || hoverOptions === void 0 ? void 0 : hoverOptions.brightness).toString()) || initialColor);
|
|
122
|
+
}
|
|
123
|
+
return initialColor;
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
setActiveState({
|
|
127
|
+
element: elements[i],
|
|
128
|
+
state: inactiveOptions,
|
|
129
|
+
active: Boolean(!inactiveEnabled ||
|
|
130
|
+
!selectedSeriesId ||
|
|
131
|
+
selectedSeriesId === d.data.series.id),
|
|
132
|
+
datum: d.data,
|
|
133
|
+
});
|
|
134
|
+
return d;
|
|
135
|
+
});
|
|
136
|
+
const labelSelection = pieSelection.selectAll('tspan');
|
|
137
|
+
const connectorSelection = pieSelection.selectAll(connectorSelector);
|
|
138
|
+
labelSelection.merge(connectorSelection).datum((d, i, elements) => {
|
|
139
|
+
return setActiveState({
|
|
140
|
+
element: elements[i],
|
|
141
|
+
state: inactiveOptions,
|
|
142
|
+
active: Boolean(!inactiveEnabled ||
|
|
143
|
+
!selectedSeriesId ||
|
|
144
|
+
selectedSeriesId === d.series.id),
|
|
145
|
+
datum: d,
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
return () => {
|
|
151
|
+
dispatcher.on(eventName, null);
|
|
152
|
+
};
|
|
153
|
+
}, [dispatcher, preparedData, seriesOptions, svgContainer]);
|
|
154
|
+
return React.createElement("g", { ref: ref, className: b(), style: { zIndex: 9 } });
|
|
155
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { PreparedPieSeries } from '../../useSeries/types';
|
|
2
|
+
import { PreparedPieData } from './types';
|
|
3
|
+
type Args = {
|
|
4
|
+
series: PreparedPieSeries[];
|
|
5
|
+
boundsWidth: number;
|
|
6
|
+
boundsHeight: number;
|
|
7
|
+
};
|
|
8
|
+
export declare function preparePieData(args: Args): PreparedPieData[];
|
|
9
|
+
export {};
|