@gravity-ui/chartkit 3.6.0 → 3.7.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/hooks/useSeries/index.js +5 -3
- package/build/plugins/d3/renderer/hooks/useSeries/prepareSeries.d.ts +2 -1
- package/build/plugins/d3/renderer/hooks/useSeries/prepareSeries.js +46 -5
- package/build/plugins/d3/renderer/hooks/useSeries/types.d.ts +7 -1
- package/build/plugins/d3/renderer/hooks/useShapes/bar-x.d.ts +3 -3
- package/build/plugins/d3/renderer/hooks/useShapes/bar-x.js +93 -22
- package/build/plugins/d3/renderer/hooks/useShapes/index.js +2 -13
- package/build/plugins/d3/renderer/hooks/useShapes/styles.css +4 -0
- package/build/plugins/d3/renderer/utils/index.js +30 -3
- package/build/types/widget-data/bar-x.d.ts +12 -1
- package/build/types/widget-data/base.d.ts +3 -0
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { scaleOrdinal } from 'd3';
|
|
2
|
+
import { group, scaleOrdinal } from 'd3';
|
|
3
3
|
import { DEFAULT_PALETTE } from '../../constants';
|
|
4
4
|
import { getSeriesNames } from '../../utils';
|
|
5
5
|
import { getActiveLegendItems, getAllLegendItems } from './utils';
|
|
@@ -9,9 +9,11 @@ export const useSeries = (args) => {
|
|
|
9
9
|
const preparedSeries = React.useMemo(() => {
|
|
10
10
|
const seriesNames = getSeriesNames(series);
|
|
11
11
|
const colorScale = scaleOrdinal(seriesNames, DEFAULT_PALETTE);
|
|
12
|
-
|
|
12
|
+
const groupedSeries = group(series, (item) => item.type);
|
|
13
|
+
return Array.from(groupedSeries).reduce((acc, [seriesType, seriesList]) => {
|
|
13
14
|
acc.push(...prepareSeries({
|
|
14
|
-
|
|
15
|
+
type: seriesType,
|
|
16
|
+
series: seriesList,
|
|
15
17
|
legend,
|
|
16
18
|
colorScale,
|
|
17
19
|
}));
|
|
@@ -3,7 +3,8 @@ import type { ChartKitWidgetSeries } from '../../../../../types/widget-data';
|
|
|
3
3
|
import type { PreparedLegend } from '../useChartOptions/types';
|
|
4
4
|
import type { PreparedSeries } from './types';
|
|
5
5
|
export declare function prepareSeries(args: {
|
|
6
|
-
|
|
6
|
+
type: ChartKitWidgetSeries['type'];
|
|
7
|
+
series: ChartKitWidgetSeries[];
|
|
7
8
|
legend: PreparedLegend;
|
|
8
9
|
colorScale: ScaleOrdinal<string, string>;
|
|
9
10
|
}): PreparedSeries[];
|
|
@@ -4,6 +4,10 @@ import get from 'lodash/get';
|
|
|
4
4
|
import { DEFAULT_PALETTE } from '../../constants';
|
|
5
5
|
import { DEFAULT_LEGEND_SYMBOL_SIZE } from './constants';
|
|
6
6
|
import { getRandomCKId } from '../../../../../utils';
|
|
7
|
+
const DEFAULT_DATALABELS_STYLE = {
|
|
8
|
+
fontSize: '11px',
|
|
9
|
+
fontWeight: 'bold',
|
|
10
|
+
};
|
|
7
11
|
function prepareLegendSymbol(series) {
|
|
8
12
|
var _a;
|
|
9
13
|
switch (series.type) {
|
|
@@ -34,6 +38,35 @@ function prepareAxisRelatedSeries(args) {
|
|
|
34
38
|
};
|
|
35
39
|
return [preparedSeries];
|
|
36
40
|
}
|
|
41
|
+
function prepareBarXSeries(args) {
|
|
42
|
+
const { colorScale, series, legend } = args;
|
|
43
|
+
const commonStackId = getRandomCKId();
|
|
44
|
+
return series.map((singleSeries) => {
|
|
45
|
+
var _a, _b, _c, _d;
|
|
46
|
+
const name = singleSeries.name || '';
|
|
47
|
+
const color = singleSeries.color || colorScale(name);
|
|
48
|
+
return {
|
|
49
|
+
type: singleSeries.type,
|
|
50
|
+
color: color,
|
|
51
|
+
name: name,
|
|
52
|
+
visible: get(singleSeries, 'visible', true),
|
|
53
|
+
legend: {
|
|
54
|
+
enabled: get(singleSeries, 'legend.enabled', legend.enabled),
|
|
55
|
+
symbol: prepareLegendSymbol(singleSeries),
|
|
56
|
+
},
|
|
57
|
+
data: singleSeries.data,
|
|
58
|
+
stacking: singleSeries.stacking,
|
|
59
|
+
stackId: singleSeries.stacking === 'normal' ? commonStackId : getRandomCKId(),
|
|
60
|
+
dataLabels: {
|
|
61
|
+
enabled: ((_a = singleSeries.dataLabels) === null || _a === void 0 ? void 0 : _a.enabled) || false,
|
|
62
|
+
inside: typeof ((_b = singleSeries.dataLabels) === null || _b === void 0 ? void 0 : _b.inside) === 'boolean'
|
|
63
|
+
? (_c = singleSeries.dataLabels) === null || _c === void 0 ? void 0 : _c.inside
|
|
64
|
+
: false,
|
|
65
|
+
style: Object.assign({}, DEFAULT_DATALABELS_STYLE, (_d = singleSeries.dataLabels) === null || _d === void 0 ? void 0 : _d.style),
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
}, []);
|
|
69
|
+
}
|
|
37
70
|
function preparePieSeries(args) {
|
|
38
71
|
const { series, legend } = args;
|
|
39
72
|
const dataNames = series.data.map((d) => d.name);
|
|
@@ -68,14 +101,22 @@ function preparePieSeries(args) {
|
|
|
68
101
|
return preparedSeries;
|
|
69
102
|
}
|
|
70
103
|
export function prepareSeries(args) {
|
|
71
|
-
const { series, legend, colorScale } = args;
|
|
72
|
-
switch (
|
|
104
|
+
const { type, series, legend, colorScale } = args;
|
|
105
|
+
switch (type) {
|
|
73
106
|
case 'pie': {
|
|
74
|
-
return
|
|
107
|
+
return series.reduce((acc, singleSeries) => {
|
|
108
|
+
acc.push(...preparePieSeries({ series: singleSeries, legend }));
|
|
109
|
+
return acc;
|
|
110
|
+
}, []);
|
|
75
111
|
}
|
|
76
|
-
case 'scatter':
|
|
77
112
|
case 'bar-x': {
|
|
78
|
-
return
|
|
113
|
+
return prepareBarXSeries({ series: series, legend, colorScale });
|
|
114
|
+
}
|
|
115
|
+
case 'scatter': {
|
|
116
|
+
return series.reduce((acc, singleSeries) => {
|
|
117
|
+
acc.push(...prepareAxisRelatedSeries({ series: singleSeries, legend, colorScale }));
|
|
118
|
+
return acc;
|
|
119
|
+
}, []);
|
|
79
120
|
}
|
|
80
121
|
default: {
|
|
81
122
|
const seriesType = get(series, 'type');
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BarXSeries, BarXSeriesData, PieSeries, PieSeriesData, RectLegendSymbolOptions, ScatterSeries, ScatterSeriesData } from '../../../../../types/widget-data';
|
|
1
|
+
import { BarXSeries, BarXSeriesData, BaseTextStyle, PieSeries, PieSeriesData, RectLegendSymbolOptions, ScatterSeries, ScatterSeriesData } from '../../../../../types/widget-data';
|
|
2
2
|
export type RectLegendSymbol = {
|
|
3
3
|
shape: 'rect';
|
|
4
4
|
} & Required<RectLegendSymbolOptions>;
|
|
@@ -19,6 +19,12 @@ export type PreparedScatterSeries = {
|
|
|
19
19
|
export type PreparedBarXSeries = {
|
|
20
20
|
type: BarXSeries['type'];
|
|
21
21
|
data: BarXSeriesData[];
|
|
22
|
+
stackId: string;
|
|
23
|
+
dataLabels: {
|
|
24
|
+
enabled: boolean;
|
|
25
|
+
inside: boolean;
|
|
26
|
+
style: BaseTextStyle;
|
|
27
|
+
};
|
|
22
28
|
} & BasePreparedSeries;
|
|
23
29
|
export type PreparedPieSeries = BasePreparedSeries & Required<Omit<PieSeries, 'data'>> & {
|
|
24
30
|
data: PieSeriesData['value'];
|
|
@@ -2,11 +2,11 @@ import React from 'react';
|
|
|
2
2
|
import { ChartOptions } from '../useChartOptions/types';
|
|
3
3
|
import { ChartScale } from '../useAxisScales';
|
|
4
4
|
import { OnSeriesMouseLeave, OnSeriesMouseMove } from '../useTooltip/types';
|
|
5
|
-
import {
|
|
5
|
+
import { PreparedBarXSeries } from '../useSeries/types';
|
|
6
6
|
type Args = {
|
|
7
7
|
top: number;
|
|
8
8
|
left: number;
|
|
9
|
-
series:
|
|
9
|
+
series: PreparedBarXSeries[];
|
|
10
10
|
xAxis: ChartOptions['xAxis'];
|
|
11
11
|
xScale: ChartScale;
|
|
12
12
|
yAxis: ChartOptions['yAxis'];
|
|
@@ -15,5 +15,5 @@ type Args = {
|
|
|
15
15
|
onSeriesMouseLeave?: OnSeriesMouseLeave;
|
|
16
16
|
svgContainer: SVGSVGElement | null;
|
|
17
17
|
};
|
|
18
|
-
export declare function
|
|
18
|
+
export declare function BarXSeriesShapes(args: Args): React.JSX.Element;
|
|
19
19
|
export {};
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { block } from '../../../../../utils/cn';
|
|
3
|
-
import { pointer } from 'd3';
|
|
4
|
-
import { getRandomCKId } from '../../../../../utils';
|
|
3
|
+
import { group, pointer, select } from 'd3';
|
|
5
4
|
const DEFAULT_BAR_RECT_WIDTH = 50;
|
|
6
5
|
const DEFAULT_LINEAR_BAR_RECT_WIDTH = 20;
|
|
7
6
|
const MIN_RECT_GAP = 1;
|
|
8
|
-
const
|
|
7
|
+
const DEFAULT_LABEL_PADDING = 7;
|
|
8
|
+
const b = block('d3-bar-x');
|
|
9
9
|
const getRectProperties = (args) => {
|
|
10
10
|
const { point, xAxis, xScale, yAxis, yScale, minPointDistance } = args;
|
|
11
11
|
let cx;
|
|
@@ -48,32 +48,103 @@ function minDiff(arr) {
|
|
|
48
48
|
}
|
|
49
49
|
return result;
|
|
50
50
|
}
|
|
51
|
-
export function
|
|
51
|
+
export function BarXSeriesShapes(args) {
|
|
52
52
|
const { top, left, series, xAxis, xScale, yAxis, yScale, onSeriesMouseMove, onSeriesMouseLeave, svgContainer, } = args;
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
});
|
|
66
|
-
|
|
53
|
+
const ref = React.useRef(null);
|
|
54
|
+
React.useEffect(() => {
|
|
55
|
+
if (!ref.current) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const svgElement = select(ref.current);
|
|
59
|
+
svgElement.selectAll('*').remove();
|
|
60
|
+
const xValues = xAxis.type === 'category'
|
|
61
|
+
? []
|
|
62
|
+
: series.reduce((acc, { data }) => {
|
|
63
|
+
data.forEach((dataItem) => acc.push(Number(dataItem.x)));
|
|
64
|
+
return acc;
|
|
65
|
+
}, []);
|
|
66
|
+
const minPointDistance = minDiff(xValues);
|
|
67
|
+
const stackedSeriesMap = group(series, (item) => item.stackId);
|
|
68
|
+
Array.from(stackedSeriesMap).forEach(([, stackedSeries]) => {
|
|
69
|
+
const stackHeights = {};
|
|
70
|
+
stackedSeries.forEach((item) => {
|
|
71
|
+
const shapes = item.data.map((dataItem) => {
|
|
72
|
+
const rectProps = getRectProperties({
|
|
73
|
+
point: dataItem,
|
|
74
|
+
xAxis,
|
|
75
|
+
xScale,
|
|
76
|
+
yAxis,
|
|
77
|
+
yScale,
|
|
78
|
+
minPointDistance,
|
|
79
|
+
});
|
|
80
|
+
if (!stackHeights[rectProps.x]) {
|
|
81
|
+
stackHeights[rectProps.x] = 0;
|
|
82
|
+
}
|
|
83
|
+
const rectY = rectProps.y - stackHeights[rectProps.x];
|
|
84
|
+
stackHeights[rectProps.x] += rectProps.height + 1;
|
|
85
|
+
return Object.assign(Object.assign({}, rectProps), { y: rectY, data: dataItem });
|
|
86
|
+
});
|
|
87
|
+
svgElement
|
|
88
|
+
.selectAll('allRects')
|
|
89
|
+
.data(shapes)
|
|
90
|
+
.join('rect')
|
|
91
|
+
.attr('class', b('segment'))
|
|
92
|
+
.attr('x', (d) => d.x)
|
|
93
|
+
.attr('y', (d) => d.y)
|
|
94
|
+
.attr('height', (d) => d.height)
|
|
95
|
+
.attr('width', (d) => d.width)
|
|
96
|
+
.attr('fill', item.color)
|
|
97
|
+
.on('mousemove', (e, point) => {
|
|
67
98
|
const [x, y] = pointer(e, svgContainer);
|
|
68
99
|
onSeriesMouseMove === null || onSeriesMouseMove === void 0 ? void 0 : onSeriesMouseMove({
|
|
69
100
|
hovered: {
|
|
70
|
-
data: point,
|
|
101
|
+
data: point.data,
|
|
71
102
|
series: item,
|
|
72
103
|
},
|
|
73
104
|
pointerPosition: [x - left, y - top],
|
|
74
105
|
});
|
|
75
|
-
}
|
|
106
|
+
})
|
|
107
|
+
.on('mouseleave', () => {
|
|
108
|
+
if (onSeriesMouseLeave) {
|
|
109
|
+
onSeriesMouseLeave();
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
if (item.dataLabels.enabled) {
|
|
113
|
+
const selection = svgElement
|
|
114
|
+
.selectAll('allLabels')
|
|
115
|
+
.data(shapes)
|
|
116
|
+
.join('text')
|
|
117
|
+
.text((d) => String(d.data.label || d.data.y))
|
|
118
|
+
.attr('class', b('label'))
|
|
119
|
+
.attr('x', (d) => d.x + d.width / 2)
|
|
120
|
+
.attr('y', (d) => {
|
|
121
|
+
if (item.dataLabels.inside) {
|
|
122
|
+
return d.y + d.height / 2;
|
|
123
|
+
}
|
|
124
|
+
return d.y - DEFAULT_LABEL_PADDING;
|
|
125
|
+
})
|
|
126
|
+
.attr('text-anchor', 'middle')
|
|
127
|
+
.style('font-size', item.dataLabels.style.fontSize);
|
|
128
|
+
if (item.dataLabels.style.fontWeight) {
|
|
129
|
+
selection.style('font-weight', item.dataLabels.style.fontWeight);
|
|
130
|
+
}
|
|
131
|
+
if (item.dataLabels.style.fontColor) {
|
|
132
|
+
selection.style('fill', item.dataLabels.style.fontColor);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
});
|
|
76
136
|
});
|
|
77
|
-
|
|
78
|
-
|
|
137
|
+
}, [
|
|
138
|
+
onSeriesMouseMove,
|
|
139
|
+
onSeriesMouseLeave,
|
|
140
|
+
svgContainer,
|
|
141
|
+
xAxis,
|
|
142
|
+
xScale,
|
|
143
|
+
yAxis,
|
|
144
|
+
yScale,
|
|
145
|
+
series,
|
|
146
|
+
left,
|
|
147
|
+
top,
|
|
148
|
+
]);
|
|
149
|
+
return React.createElement("g", { ref: ref, className: b() });
|
|
79
150
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { group } from 'd3';
|
|
3
3
|
import { getOnlyVisibleSeries } from '../../utils';
|
|
4
|
-
import {
|
|
4
|
+
import { BarXSeriesShapes } from './bar-x';
|
|
5
5
|
import { prepareScatterSeries } from './scatter';
|
|
6
6
|
import { PieSeriesComponent } from './pie';
|
|
7
7
|
import './styles.css';
|
|
@@ -15,18 +15,7 @@ export const useShapes = (args) => {
|
|
|
15
15
|
switch (seriesType) {
|
|
16
16
|
case 'bar-x': {
|
|
17
17
|
if (xScale && yScale) {
|
|
18
|
-
acc.push(
|
|
19
|
-
top,
|
|
20
|
-
left,
|
|
21
|
-
series: chartSeries,
|
|
22
|
-
xAxis,
|
|
23
|
-
xScale,
|
|
24
|
-
yAxis,
|
|
25
|
-
yScale,
|
|
26
|
-
onSeriesMouseMove,
|
|
27
|
-
onSeriesMouseLeave,
|
|
28
|
-
svgContainer,
|
|
29
|
-
}));
|
|
18
|
+
acc.push(React.createElement(BarXSeriesShapes, Object.assign({}, args, { key: "bar-x", series: chartSeries, xScale: xScale, yScale: yScale })));
|
|
30
19
|
}
|
|
31
20
|
break;
|
|
32
21
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { select } from 'd3';
|
|
1
|
+
import { group, select } from 'd3';
|
|
2
2
|
import get from 'lodash/get';
|
|
3
3
|
import { dateTime } from '@gravity-ui/date-utils';
|
|
4
4
|
import { formatNumber } from '../../../shared';
|
|
@@ -32,8 +32,35 @@ export const getDomainDataXBySeries = (series) => {
|
|
|
32
32
|
}, []);
|
|
33
33
|
};
|
|
34
34
|
export const getDomainDataYBySeries = (series) => {
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
const groupedSeries = group(series, (item) => item.type);
|
|
36
|
+
return Array.from(groupedSeries).reduce((acc, [type, seriesList]) => {
|
|
37
|
+
switch (type) {
|
|
38
|
+
case 'bar-x': {
|
|
39
|
+
const barXSeries = seriesList;
|
|
40
|
+
const stackedSeries = group(barXSeries, (item) => item.stackId);
|
|
41
|
+
Array.from(stackedSeries).forEach(([, stack]) => {
|
|
42
|
+
const values = {};
|
|
43
|
+
stack.forEach((singleSeries) => {
|
|
44
|
+
singleSeries.data.forEach((point) => {
|
|
45
|
+
const key = String(point.x || point.category);
|
|
46
|
+
if (typeof values[key] === 'undefined') {
|
|
47
|
+
values[key] = 0;
|
|
48
|
+
}
|
|
49
|
+
if (point.y) {
|
|
50
|
+
values[key] += point.y;
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
acc.push(...Object.values(values));
|
|
55
|
+
});
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
default: {
|
|
59
|
+
seriesList.filter(isSeriesWithNumericalYValues).forEach((s) => {
|
|
60
|
+
acc.push(...s.data.map((d) => d.y));
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
37
64
|
return acc;
|
|
38
65
|
}, []);
|
|
39
66
|
};
|
|
@@ -8,6 +8,8 @@ export type BarXSeriesData<T = any> = BaseSeriesData<T> & {
|
|
|
8
8
|
y?: number;
|
|
9
9
|
/** Corresponding value of axis category */
|
|
10
10
|
category?: string;
|
|
11
|
+
/** Data label value of the bar-x column. If not specified, the y value is used. */
|
|
12
|
+
label?: string | number;
|
|
11
13
|
};
|
|
12
14
|
export type BarXSeries<T = any> = BaseSeries & {
|
|
13
15
|
type: 'bar-x';
|
|
@@ -16,6 +18,11 @@ export type BarXSeries<T = any> = BaseSeries & {
|
|
|
16
18
|
name: string;
|
|
17
19
|
/** The main color of the series (hex, rgba) */
|
|
18
20
|
color?: string;
|
|
21
|
+
/** Whether to stack the values of each series on top of each other.
|
|
22
|
+
* Possible values are undefined to disable, "normal" to stack by value or "percent"
|
|
23
|
+
*
|
|
24
|
+
* @default undefined
|
|
25
|
+
* */
|
|
19
26
|
stacking?: 'normal' | 'percent';
|
|
20
27
|
/** This option allows grouping series in a stacked chart */
|
|
21
28
|
stackId?: string;
|
|
@@ -26,7 +33,11 @@ export type BarXSeries<T = any> = BaseSeries & {
|
|
|
26
33
|
* */
|
|
27
34
|
grouping?: boolean;
|
|
28
35
|
dataLabels?: ChartKitWidgetSeriesOptions['dataLabels'] & {
|
|
29
|
-
/**
|
|
36
|
+
/**
|
|
37
|
+
* Whether to align the data label inside or outside the box
|
|
38
|
+
*
|
|
39
|
+
* @default false
|
|
40
|
+
* */
|
|
30
41
|
inside?: boolean;
|
|
31
42
|
};
|
|
32
43
|
/** Individual series legend options. Has higher priority than legend options in widget data */
|
|
@@ -12,6 +12,7 @@ export type BaseSeries = {
|
|
|
12
12
|
* @default true
|
|
13
13
|
*/
|
|
14
14
|
enabled?: boolean;
|
|
15
|
+
style?: Partial<BaseTextStyle>;
|
|
15
16
|
};
|
|
16
17
|
};
|
|
17
18
|
export type BaseSeriesData<T = any> = {
|
|
@@ -24,4 +25,6 @@ export type BaseSeriesData<T = any> = {
|
|
|
24
25
|
};
|
|
25
26
|
export type BaseTextStyle = {
|
|
26
27
|
fontSize: string;
|
|
28
|
+
fontWeight?: string;
|
|
29
|
+
fontColor?: string;
|
|
27
30
|
};
|
package/package.json
CHANGED