@gravity-ui/chartkit 4.0.0-beta.7 → 4.1.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/README.md +2 -1
- package/build/plugins/d3/renderer/D3Widget.js +14 -5
- package/build/plugins/d3/renderer/components/Chart.js +1 -7
- package/build/plugins/d3/renderer/hooks/useChartDimensions/index.d.ts +1 -5
- package/build/plugins/d3/renderer/hooks/useChartDimensions/index.js +3 -5
- package/build/plugins/d3/renderer/hooks/useChartOptions/chart.d.ts +3 -1
- package/build/plugins/d3/renderer/hooks/useChartOptions/chart.js +47 -10
- package/build/plugins/d3/renderer/hooks/useChartOptions/index.js +2 -0
- 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 +12 -27
- package/build/plugins/d3/renderer/hooks/useShapes/scatter.d.ts +3 -3
- package/build/plugins/d3/renderer/hooks/useShapes/scatter.js +57 -33
- 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 +5 -0
- package/build/types/widget-data/pie.d.ts +0 -2
- package/package.json +6 -2
package/README.md
CHANGED
|
@@ -19,7 +19,8 @@ import '@gravity-ui/uikit/styles/styles.scss';
|
|
|
19
19
|
```typescript
|
|
20
20
|
import {ThemeProvider} from '@gravity-ui/uikit';
|
|
21
21
|
import ChartKit, {settings} from '@gravity-ui/chartkit';
|
|
22
|
-
import {YagrPlugin
|
|
22
|
+
import {YagrPlugin} from '@gravity-ui/chartkit/yagr';
|
|
23
|
+
import type {YagrWidgetData} from '@gravity-ui/chartkit/yagr';
|
|
23
24
|
|
|
24
25
|
import '@gravity-ui/uikit/styles/styles.scss';
|
|
25
26
|
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { select } from 'd3';
|
|
3
3
|
import debounce from 'lodash/debounce';
|
|
4
|
+
import { getRandomCKId } from '../../../utils';
|
|
4
5
|
import { Chart } from './components';
|
|
5
6
|
const D3Widget = React.forwardRef(function D3Widget(props, forwardedRef) {
|
|
6
7
|
const ref = React.useRef(null);
|
|
7
8
|
const debounced = React.useRef();
|
|
8
9
|
const [dimensions, setDimensions] = React.useState();
|
|
9
10
|
const handleResize = React.useCallback(() => {
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
var _a;
|
|
12
|
+
const parentElement = (_a = ref.current) === null || _a === void 0 ? void 0 : _a.parentElement;
|
|
13
|
+
if (parentElement) {
|
|
14
|
+
const { top, left, width, height } = parentElement.getBoundingClientRect();
|
|
12
15
|
setDimensions({ top, left, width, height });
|
|
13
16
|
}
|
|
14
17
|
}, []);
|
|
@@ -25,16 +28,22 @@ const D3Widget = React.forwardRef(function D3Widget(props, forwardedRef) {
|
|
|
25
28
|
}), [handleResize]);
|
|
26
29
|
React.useEffect(() => {
|
|
27
30
|
const selection = select(window);
|
|
28
|
-
selection.
|
|
31
|
+
// https://github.com/d3/d3-selection/blob/main/README.md#handling-events
|
|
32
|
+
const eventName = `resize.${getRandomCKId()}`;
|
|
33
|
+
selection.on(eventName, debuncedHandleResize);
|
|
29
34
|
return () => {
|
|
30
35
|
// https://d3js.org/d3-selection/events#selection_on
|
|
31
|
-
selection.on(
|
|
36
|
+
selection.on(eventName, null);
|
|
32
37
|
};
|
|
33
38
|
}, [debuncedHandleResize]);
|
|
34
39
|
React.useEffect(() => {
|
|
35
40
|
// dimensions initialize
|
|
36
41
|
handleResize();
|
|
37
42
|
}, [handleResize]);
|
|
38
|
-
return (React.createElement("div", { ref: ref, style: {
|
|
43
|
+
return (React.createElement("div", { ref: ref, style: {
|
|
44
|
+
width: (dimensions === null || dimensions === void 0 ? void 0 : dimensions.width) || '100%',
|
|
45
|
+
height: (dimensions === null || dimensions === void 0 ? void 0 : dimensions.height) || '100%',
|
|
46
|
+
position: 'relative',
|
|
47
|
+
} }, (dimensions === null || dimensions === void 0 ? void 0 : dimensions.width) && (dimensions === null || dimensions === void 0 ? void 0 : dimensions.height) && (React.createElement(Chart, { top: (dimensions === null || dimensions === void 0 ? void 0 : dimensions.top) || 0, left: dimensions.left || 0, width: dimensions.width, height: dimensions.height, data: props.data }))));
|
|
39
48
|
});
|
|
40
49
|
export default D3Widget;
|
|
@@ -18,9 +18,6 @@ export const Chart = (props) => {
|
|
|
18
18
|
width,
|
|
19
19
|
height,
|
|
20
20
|
margin: chart.margin,
|
|
21
|
-
legend,
|
|
22
|
-
title,
|
|
23
|
-
xAxis,
|
|
24
21
|
yAxis,
|
|
25
22
|
});
|
|
26
23
|
const { preparedSeries, handleLegendItemClick } = useSeries({ series: data.series, legend });
|
|
@@ -51,10 +48,7 @@ export const Chart = (props) => {
|
|
|
51
48
|
return (React.createElement(React.Fragment, null,
|
|
52
49
|
React.createElement("svg", { ref: svgRef, className: b({ hovered: chartHovered }), width: width, height: height, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave },
|
|
53
50
|
title && React.createElement(Title, Object.assign({}, title, { chartWidth: width })),
|
|
54
|
-
React.createElement("g", { width: boundsWidth, height: boundsHeight, transform: `translate(${[
|
|
55
|
-
chart.margin.left,
|
|
56
|
-
chart.margin.top + ((title === null || title === void 0 ? void 0 : title.height) || 0),
|
|
57
|
-
].join(',')})` },
|
|
51
|
+
React.createElement("g", { width: boundsWidth, height: boundsHeight, transform: `translate(${[chart.margin.left, chart.margin.top].join(',')})` },
|
|
58
52
|
xScale && yScale && (React.createElement(React.Fragment, null,
|
|
59
53
|
React.createElement(AxisY, { axises: yAxis, width: boundsWidth, height: boundsHeight, scale: yScale }),
|
|
60
54
|
React.createElement("g", { transform: `translate(0, ${boundsHeight})` },
|
|
@@ -1,17 +1,13 @@
|
|
|
1
1
|
import type { ChartMargin } from '../../../../../types/widget-data';
|
|
2
|
-
import type { PreparedAxis
|
|
2
|
+
import type { PreparedAxis } from '../useChartOptions/types';
|
|
3
3
|
type Args = {
|
|
4
4
|
width: number;
|
|
5
5
|
height: number;
|
|
6
6
|
margin: ChartMargin;
|
|
7
|
-
legend: PreparedLegend;
|
|
8
|
-
title?: PreparedTitle;
|
|
9
|
-
xAxis?: PreparedAxis;
|
|
10
7
|
yAxis?: PreparedAxis[];
|
|
11
8
|
};
|
|
12
9
|
export declare const useChartDimensions: (args: Args) => {
|
|
13
10
|
boundsWidth: number;
|
|
14
11
|
boundsHeight: number;
|
|
15
|
-
legendHeight: number;
|
|
16
12
|
};
|
|
17
13
|
export {};
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
export const useChartDimensions = (args) => {
|
|
2
|
-
const { margin,
|
|
3
|
-
const titleHeight = (title === null || title === void 0 ? void 0 : title.height) || 0;
|
|
4
|
-
const xAxisTitleHeight = (xAxis === null || xAxis === void 0 ? void 0 : xAxis.title.height) || 0;
|
|
2
|
+
const { margin, width, height, yAxis } = args;
|
|
5
3
|
const yAxisTitleHeight = (yAxis === null || yAxis === void 0 ? void 0 : yAxis.reduce((acc, axis) => {
|
|
6
4
|
return acc + (axis.title.height || 0);
|
|
7
5
|
}, 0)) || 0;
|
|
8
6
|
const boundsWidth = width - margin.right - margin.left - yAxisTitleHeight;
|
|
9
|
-
const boundsHeight = height - margin.top - margin.bottom
|
|
10
|
-
return { boundsWidth, boundsHeight
|
|
7
|
+
const boundsHeight = height - margin.top - margin.bottom;
|
|
8
|
+
return { boundsWidth, boundsHeight };
|
|
11
9
|
};
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import type { ChartKitWidgetData } from '../../../../../types/widget-data';
|
|
2
|
-
import type { PreparedAxis, PreparedChart } from './types';
|
|
2
|
+
import type { PreparedAxis, PreparedChart, PreparedTitle, PreparedLegend } from './types';
|
|
3
3
|
export declare const getPreparedChart: (args: {
|
|
4
4
|
chart: ChartKitWidgetData['chart'];
|
|
5
5
|
series: ChartKitWidgetData['series'];
|
|
6
|
+
preparedLegend: PreparedLegend;
|
|
6
7
|
preparedXAxis: PreparedAxis;
|
|
7
8
|
preparedY1Axis: PreparedAxis;
|
|
9
|
+
preparedTitle?: PreparedTitle;
|
|
8
10
|
}) => PreparedChart;
|
|
@@ -42,26 +42,63 @@ const getAxisLabelMaxWidth = (args) => {
|
|
|
42
42
|
.remove();
|
|
43
43
|
return width;
|
|
44
44
|
};
|
|
45
|
-
|
|
46
|
-
const { chart,
|
|
47
|
-
const hasAxisRelatedSeries = series.data.some(isAxisRelatedSeries);
|
|
48
|
-
let marginBottom = get(chart, 'margin.bottom', 0);
|
|
49
|
-
let marginLeft = get(chart, 'margin.left', 0);
|
|
45
|
+
const getMarginTop = (args) => {
|
|
46
|
+
const { chart, hasAxisRelatedSeries, preparedY1Axis, preparedTitle } = args;
|
|
50
47
|
let marginTop = get(chart, 'margin.top', 0);
|
|
51
|
-
|
|
48
|
+
if (hasAxisRelatedSeries) {
|
|
49
|
+
marginTop +=
|
|
50
|
+
getHorisontalSvgTextHeight({ text: 'Tmp', style: preparedY1Axis.labels.style }) / 2;
|
|
51
|
+
}
|
|
52
|
+
if (preparedTitle === null || preparedTitle === void 0 ? void 0 : preparedTitle.height) {
|
|
53
|
+
marginTop += preparedTitle.height;
|
|
54
|
+
}
|
|
55
|
+
return marginTop;
|
|
56
|
+
};
|
|
57
|
+
const getMarginBottom = (args) => {
|
|
58
|
+
const { chart, hasAxisRelatedSeries, preparedLegend, preparedXAxis } = args;
|
|
59
|
+
let marginBottom = get(chart, 'margin.bottom', 0) + preparedLegend.height;
|
|
52
60
|
if (hasAxisRelatedSeries) {
|
|
53
61
|
marginBottom +=
|
|
54
|
-
preparedXAxis.
|
|
62
|
+
preparedXAxis.title.height +
|
|
55
63
|
getHorisontalSvgTextHeight({ text: 'Tmp', style: preparedXAxis.labels.style });
|
|
64
|
+
if (preparedXAxis.labels.enabled) {
|
|
65
|
+
marginBottom += preparedXAxis.labels.padding;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return marginBottom;
|
|
69
|
+
};
|
|
70
|
+
const getMarginLeft = (args) => {
|
|
71
|
+
const { chart, hasAxisRelatedSeries, series, preparedY1Axis } = args;
|
|
72
|
+
let marginLeft = get(chart, 'margin.left', 0);
|
|
73
|
+
if (hasAxisRelatedSeries) {
|
|
56
74
|
marginLeft +=
|
|
57
75
|
AXIS_WIDTH +
|
|
58
76
|
preparedY1Axis.labels.padding +
|
|
59
77
|
getAxisLabelMaxWidth({ axis: preparedY1Axis, series: series.data }) +
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
78
|
+
preparedY1Axis.title.height;
|
|
79
|
+
}
|
|
80
|
+
return marginLeft;
|
|
81
|
+
};
|
|
82
|
+
const getMarginRight = (args) => {
|
|
83
|
+
const { chart, hasAxisRelatedSeries, series, preparedXAxis } = args;
|
|
84
|
+
let marginRight = get(chart, 'margin.right', 0);
|
|
85
|
+
if (hasAxisRelatedSeries) {
|
|
63
86
|
marginRight += getAxisLabelMaxWidth({ axis: preparedXAxis, series: series.data }) / 2;
|
|
64
87
|
}
|
|
88
|
+
return marginRight;
|
|
89
|
+
};
|
|
90
|
+
export const getPreparedChart = (args) => {
|
|
91
|
+
const { chart, series, preparedLegend, preparedXAxis, preparedY1Axis, preparedTitle } = args;
|
|
92
|
+
const hasAxisRelatedSeries = series.data.some(isAxisRelatedSeries);
|
|
93
|
+
const marginTop = getMarginTop({ chart, hasAxisRelatedSeries, preparedY1Axis, preparedTitle });
|
|
94
|
+
const marginBottom = getMarginBottom({
|
|
95
|
+
chart,
|
|
96
|
+
hasAxisRelatedSeries,
|
|
97
|
+
preparedLegend,
|
|
98
|
+
preparedXAxis,
|
|
99
|
+
});
|
|
100
|
+
const marginLeft = getMarginLeft({ chart, hasAxisRelatedSeries, series, preparedY1Axis });
|
|
101
|
+
const marginRight = getMarginRight({ chart, hasAxisRelatedSeries, series, preparedXAxis });
|
|
65
102
|
return {
|
|
66
103
|
margin: {
|
|
67
104
|
top: marginTop,
|
|
@@ -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', (d) => d.data.color || 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,8 +1,9 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { group } from 'd3';
|
|
3
|
+
import { getRandomCKId } from '../../../../../utils';
|
|
3
4
|
import { getOnlyVisibleSeries } from '../../utils';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
5
|
+
import { BarXSeriesShapes } from './bar-x';
|
|
6
|
+
import { ScatterSeriesShape } from './scatter';
|
|
6
7
|
import { PieSeriesComponent } from './pie';
|
|
7
8
|
import './styles.css';
|
|
8
9
|
export const useShapes = (args) => {
|
|
@@ -15,40 +16,22 @@ export const useShapes = (args) => {
|
|
|
15
16
|
switch (seriesType) {
|
|
16
17
|
case 'bar-x': {
|
|
17
18
|
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
|
-
}));
|
|
19
|
+
acc.push(React.createElement(BarXSeriesShapes, { key: "bar-x", series: chartSeries, xAxis: xAxis, xScale: xScale, yAxis: yAxis, yScale: yScale, top: top, left: left, svgContainer: svgContainer, onSeriesMouseMove: onSeriesMouseMove, onSeriesMouseLeave: onSeriesMouseLeave }));
|
|
30
20
|
}
|
|
31
21
|
break;
|
|
32
22
|
}
|
|
33
23
|
case 'scatter': {
|
|
34
24
|
if (xScale && yScale) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
left,
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
xScale,
|
|
41
|
-
yAxis,
|
|
42
|
-
yScale,
|
|
43
|
-
onSeriesMouseMove,
|
|
44
|
-
onSeriesMouseLeave,
|
|
45
|
-
svgContainer,
|
|
46
|
-
}));
|
|
25
|
+
const scatterShapes = chartSeries.map((scatterSeries, i) => {
|
|
26
|
+
const id = getRandomCKId();
|
|
27
|
+
return (React.createElement(ScatterSeriesShape, { key: `${i}-${id}`, top: top, left: left, series: scatterSeries, xAxis: xAxis, xScale: xScale, yAxis: yAxis, yScale: yScale, onSeriesMouseMove: onSeriesMouseMove, onSeriesMouseLeave: onSeriesMouseLeave, svgContainer: svgContainer }));
|
|
28
|
+
});
|
|
29
|
+
acc.push(...scatterShapes);
|
|
47
30
|
}
|
|
48
31
|
break;
|
|
49
32
|
}
|
|
50
33
|
case 'pie': {
|
|
51
|
-
const groupedPieSeries = group(chartSeries, (
|
|
34
|
+
const groupedPieSeries = group(chartSeries, (pieSeries) => pieSeries.stackId);
|
|
52
35
|
acc.push(...Array.from(groupedPieSeries).map(([key, pieSeries]) => {
|
|
53
36
|
return (React.createElement(PieSeriesComponent, { key: `pie-${key}`, boundsWidth: boundsWidth, boundsHeight: boundsHeight, series: pieSeries, onSeriesMouseMove: onSeriesMouseMove, onSeriesMouseLeave: onSeriesMouseLeave, svgContainer: svgContainer }));
|
|
54
37
|
}));
|
|
@@ -65,6 +48,8 @@ export const useShapes = (args) => {
|
|
|
65
48
|
yAxis,
|
|
66
49
|
yScale,
|
|
67
50
|
svgContainer,
|
|
51
|
+
left,
|
|
52
|
+
top,
|
|
68
53
|
onSeriesMouseMove,
|
|
69
54
|
onSeriesMouseLeave,
|
|
70
55
|
]);
|
|
@@ -3,10 +3,10 @@ import { ChartOptions } from '../useChartOptions/types';
|
|
|
3
3
|
import { ChartScale } from '../useAxisScales';
|
|
4
4
|
import { OnSeriesMouseLeave, OnSeriesMouseMove } from '../useTooltip/types';
|
|
5
5
|
import { ScatterSeries } from '../../../../../types/widget-data';
|
|
6
|
-
type
|
|
6
|
+
type ScatterSeriesShapeProps = {
|
|
7
7
|
top: number;
|
|
8
8
|
left: number;
|
|
9
|
-
series: ScatterSeries
|
|
9
|
+
series: ScatterSeries;
|
|
10
10
|
xAxis: ChartOptions['xAxis'];
|
|
11
11
|
xScale: ChartScale;
|
|
12
12
|
yAxis: ChartOptions['yAxis'];
|
|
@@ -15,5 +15,5 @@ type PrepareScatterSeriesArgs = {
|
|
|
15
15
|
onSeriesMouseMove?: OnSeriesMouseMove;
|
|
16
16
|
onSeriesMouseLeave?: OnSeriesMouseLeave;
|
|
17
17
|
};
|
|
18
|
-
export declare function
|
|
18
|
+
export declare function ScatterSeriesShape(props: ScatterSeriesShapeProps): React.JSX.Element;
|
|
19
19
|
export {};
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { pointer } from 'd3';
|
|
1
|
+
import { pointer, select } from 'd3';
|
|
2
2
|
import React from 'react';
|
|
3
3
|
import { block } from '../../../../../utils/cn';
|
|
4
|
-
import { getRandomCKId } from '../../../../../utils';
|
|
5
4
|
const b = block('d3-scatter');
|
|
6
5
|
const DEFAULT_SCATTER_POINT_RADIUS = 4;
|
|
7
6
|
const prepareCategoricalScatterData = (data) => {
|
|
@@ -10,11 +9,9 @@ const prepareCategoricalScatterData = (data) => {
|
|
|
10
9
|
const prepareLinearScatterData = (data) => {
|
|
11
10
|
return data.filter((d) => typeof d.x === 'number' && typeof d.y === 'number');
|
|
12
11
|
};
|
|
13
|
-
const
|
|
14
|
-
const { point, xAxis, xScale
|
|
15
|
-
const r = point.radius || DEFAULT_SCATTER_POINT_RADIUS;
|
|
12
|
+
const getCxAttr = (args) => {
|
|
13
|
+
const { point, xAxis, xScale } = args;
|
|
16
14
|
let cx;
|
|
17
|
-
let cy;
|
|
18
15
|
if (xAxis.type === 'category') {
|
|
19
16
|
const xBandScale = xScale;
|
|
20
17
|
cx = (xBandScale(point.category) || 0) + xBandScale.step() / 2;
|
|
@@ -23,6 +20,11 @@ const getPointProperties = (args) => {
|
|
|
23
20
|
const xLinearScale = xScale;
|
|
24
21
|
cx = xLinearScale(point.x);
|
|
25
22
|
}
|
|
23
|
+
return cx;
|
|
24
|
+
};
|
|
25
|
+
const getCyAttr = (args) => {
|
|
26
|
+
const { point, yAxis, yScale } = args;
|
|
27
|
+
let cy;
|
|
26
28
|
if (yAxis[0].type === 'category') {
|
|
27
29
|
const yBandScale = yScale;
|
|
28
30
|
cy = (yBandScale(point.category) || 0) + yBandScale.step() / 2;
|
|
@@ -31,35 +33,57 @@ const getPointProperties = (args) => {
|
|
|
31
33
|
const yLinearScale = yScale;
|
|
32
34
|
cy = yLinearScale(point.y);
|
|
33
35
|
}
|
|
34
|
-
return
|
|
36
|
+
return cy;
|
|
35
37
|
};
|
|
36
|
-
export function
|
|
37
|
-
const {
|
|
38
|
-
|
|
38
|
+
export function ScatterSeriesShape(props) {
|
|
39
|
+
const { series, xAxis, xScale, yAxis, yScale, svgContainer, left, top, onSeriesMouseMove, onSeriesMouseLeave, } = props;
|
|
40
|
+
const ref = React.useRef(null);
|
|
41
|
+
React.useEffect(() => {
|
|
39
42
|
var _a;
|
|
40
|
-
|
|
43
|
+
if (!ref.current) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const svgElement = select(ref.current);
|
|
47
|
+
svgElement.selectAll('*').remove();
|
|
41
48
|
const preparedData = xAxis.type === 'category' || ((_a = yAxis[0]) === null || _a === void 0 ? void 0 : _a.type) === 'category'
|
|
42
|
-
? prepareCategoricalScatterData(
|
|
43
|
-
: prepareLinearScatterData(
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
? prepareCategoricalScatterData(series.data)
|
|
50
|
+
: prepareLinearScatterData(series.data);
|
|
51
|
+
svgElement
|
|
52
|
+
.selectAll('allPoints')
|
|
53
|
+
.data(preparedData)
|
|
54
|
+
.enter()
|
|
55
|
+
.append('circle')
|
|
56
|
+
.attr('class', b('point'))
|
|
57
|
+
.attr('fill', (d) => d.color || series.color || '')
|
|
58
|
+
.attr('r', (d) => d.radius || DEFAULT_SCATTER_POINT_RADIUS)
|
|
59
|
+
.attr('cx', (d) => getCxAttr({ point: d, xAxis, xScale }))
|
|
60
|
+
.attr('cy', (d) => getCyAttr({ point: d, yAxis, yScale }))
|
|
61
|
+
.on('mousemove', (e, d) => {
|
|
62
|
+
const [x, y] = pointer(e, svgContainer);
|
|
63
|
+
onSeriesMouseMove === null || onSeriesMouseMove === void 0 ? void 0 : onSeriesMouseMove({
|
|
64
|
+
hovered: {
|
|
65
|
+
data: d,
|
|
66
|
+
series,
|
|
67
|
+
},
|
|
68
|
+
pointerPosition: [x - left, y - top],
|
|
51
69
|
});
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
70
|
+
})
|
|
71
|
+
.on('mouseleave', () => {
|
|
72
|
+
if (onSeriesMouseLeave) {
|
|
73
|
+
onSeriesMouseLeave();
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
}, [
|
|
77
|
+
series,
|
|
78
|
+
xAxis,
|
|
79
|
+
xScale,
|
|
80
|
+
yAxis,
|
|
81
|
+
yScale,
|
|
82
|
+
svgContainer,
|
|
83
|
+
left,
|
|
84
|
+
top,
|
|
85
|
+
onSeriesMouseMove,
|
|
86
|
+
onSeriesMouseLeave,
|
|
87
|
+
]);
|
|
88
|
+
return React.createElement("g", { ref: ref, className: b() });
|
|
65
89
|
}
|
|
@@ -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> = {
|
|
@@ -21,7 +22,11 @@ export type BaseSeriesData<T = any> = {
|
|
|
21
22
|
* Here you can add additional data for your own event callbacks and formatter callbacks
|
|
22
23
|
*/
|
|
23
24
|
custom?: T;
|
|
25
|
+
/** Individual color for the data chunk (point in scatter, segment in pie, bar etc) */
|
|
26
|
+
color?: string;
|
|
24
27
|
};
|
|
25
28
|
export type BaseTextStyle = {
|
|
26
29
|
fontSize: string;
|
|
30
|
+
fontWeight?: string;
|
|
31
|
+
fontColor?: string;
|
|
27
32
|
};
|
|
@@ -5,8 +5,6 @@ export type PieSeriesData<T = any> = BaseSeriesData<T> & {
|
|
|
5
5
|
value: number;
|
|
6
6
|
/** The name of the pie segment (used in legend, tooltip etc). */
|
|
7
7
|
name: string;
|
|
8
|
-
/** Individual color for the pie segment. */
|
|
9
|
-
color?: string;
|
|
10
8
|
/** Initial visibility of the pie segment. */
|
|
11
9
|
visible?: boolean;
|
|
12
10
|
/** Initial data label of the pie segment. If not specified, the value is used. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gravity-ui/chartkit",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.1.0",
|
|
4
4
|
"description": "React component used to render charts based on any sources you need",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": "git@github.com:gravity-ui/ChartKit.git",
|
|
@@ -41,10 +41,14 @@
|
|
|
41
41
|
"publishConfig": {
|
|
42
42
|
"access": "public"
|
|
43
43
|
},
|
|
44
|
+
"sideEffects": [
|
|
45
|
+
"*.css",
|
|
46
|
+
"*.scss"
|
|
47
|
+
],
|
|
44
48
|
"dependencies": {
|
|
45
49
|
"@bem-react/classname": "^1.6.0",
|
|
46
50
|
"@gravity-ui/date-utils": "^1.4.1",
|
|
47
|
-
"@gravity-ui/yagr": "^3.7.
|
|
51
|
+
"@gravity-ui/yagr": "^3.7.13",
|
|
48
52
|
"d3": "^7.8.5",
|
|
49
53
|
"lodash": "^4.17.21",
|
|
50
54
|
"react-split-pane": "^0.1.92"
|