@gravity-ui/chartkit 5.6.0 → 5.8.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/examples/area/NegativeValues.d.ts +2 -0
- package/build/plugins/d3/examples/area/NegativeValues.js +24 -0
- package/build/plugins/d3/examples/bar-x/NegativeValues.d.ts +2 -0
- package/build/plugins/d3/examples/bar-x/NegativeValues.js +41 -0
- package/build/plugins/d3/examples/bar-y/NegativeValues.d.ts +2 -0
- package/build/plugins/d3/examples/bar-y/NegativeValues.js +40 -0
- package/build/plugins/d3/renderer/components/AxisX.d.ts +2 -1
- package/build/plugins/d3/renderer/components/AxisX.js +13 -3
- package/build/plugins/d3/renderer/components/AxisY.d.ts +4 -3
- package/build/plugins/d3/renderer/components/AxisY.js +17 -8
- package/build/plugins/d3/renderer/components/Chart.js +14 -3
- package/build/plugins/d3/renderer/components/PlotTitle.d.ts +7 -0
- package/build/plugins/d3/renderer/components/PlotTitle.js +12 -0
- package/build/plugins/d3/renderer/components/styles.css +7 -1
- package/build/plugins/d3/renderer/hooks/index.d.ts +1 -0
- package/build/plugins/d3/renderer/hooks/index.js +1 -0
- package/build/plugins/d3/renderer/hooks/useAxisScales/index.d.ts +3 -1
- package/build/plugins/d3/renderer/hooks/useAxisScales/index.js +30 -14
- package/build/plugins/d3/renderer/hooks/useChartDimensions/utils.js +13 -2
- package/build/plugins/d3/renderer/hooks/useChartOptions/types.d.ts +1 -0
- package/build/plugins/d3/renderer/hooks/useChartOptions/x-axis.d.ts +2 -2
- package/build/plugins/d3/renderer/hooks/useChartOptions/x-axis.js +14 -2
- package/build/plugins/d3/renderer/hooks/useChartOptions/y-axis.d.ts +2 -2
- package/build/plugins/d3/renderer/hooks/useChartOptions/y-axis.js +18 -8
- package/build/plugins/d3/renderer/hooks/useSeries/prepare-bar-y.js +2 -1
- package/build/plugins/d3/renderer/hooks/useShapes/area/prepare-data.js +15 -5
- package/build/plugins/d3/renderer/hooks/useShapes/bar-x/prepare-data.js +5 -3
- package/build/plugins/d3/renderer/hooks/useShapes/bar-y/prepare-data.js +5 -3
- package/build/plugins/d3/renderer/hooks/useShapes/index.d.ts +2 -1
- package/build/plugins/d3/renderer/hooks/useShapes/index.js +2 -1
- package/build/plugins/d3/renderer/hooks/useShapes/line/prepare-data.d.ts +6 -4
- package/build/plugins/d3/renderer/hooks/useShapes/line/prepare-data.js +6 -3
- package/build/plugins/d3/renderer/hooks/useSplit/index.d.ts +14 -0
- package/build/plugins/d3/renderer/hooks/useSplit/index.js +57 -0
- package/build/plugins/d3/renderer/hooks/useSplit/types.d.ts +17 -0
- package/build/plugins/d3/renderer/hooks/useSplit/types.js +1 -0
- package/build/plugins/d3/renderer/utils/axis-generators/bottom.d.ts +1 -1
- package/build/plugins/d3/renderer/utils/axis-generators/bottom.js +16 -8
- package/build/plugins/d3/renderer/utils/axis.d.ts +6 -2
- package/build/plugins/d3/renderer/utils/axis.js +7 -0
- package/build/plugins/d3/renderer/utils/index.d.ts +4 -1
- package/build/plugins/d3/renderer/utils/index.js +51 -25
- package/build/plugins/highcharts/renderer/helpers/config/config.js +2 -2
- package/build/types/widget-data/axis.d.ts +10 -0
- package/build/types/widget-data/bar-y.d.ts +2 -1
- package/build/types/widget-data/index.d.ts +8 -3
- package/build/types/widget-data/index.js +1 -0
- package/build/types/widget-data/split.d.ts +13 -0
- package/build/types/widget-data/split.js +1 -0
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import get from 'lodash/get';
|
|
2
2
|
import { DEFAULT_AXIS_LABEL_FONT_SIZE, axisLabelsDefaults, yAxisTitleDefaults, } from '../../constants';
|
|
3
|
-
import { formatAxisTickLabel, getClosestPointsRange, getHorisontalSvgTextHeight, getLabelsSize, getScaleTicks, getWaterfallPointSubtotal, } from '../../utils';
|
|
3
|
+
import { CHART_SERIES_WITH_VOLUME_ON_Y_AXIS, formatAxisTickLabel, getClosestPointsRange, getHorisontalSvgTextHeight, getLabelsSize, getScaleTicks, getWaterfallPointSubtotal, } from '../../utils';
|
|
4
4
|
import { createYScale } from '../useAxisScales';
|
|
5
5
|
const getAxisLabelMaxWidth = (args) => {
|
|
6
6
|
const { axis, series } = args;
|
|
@@ -24,8 +24,8 @@ const getAxisLabelMaxWidth = (args) => {
|
|
|
24
24
|
};
|
|
25
25
|
function getAxisMin(axis, series) {
|
|
26
26
|
const min = axis === null || axis === void 0 ? void 0 : axis.min;
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
if (typeof min === 'undefined' &&
|
|
28
|
+
(series === null || series === void 0 ? void 0 : series.some((s) => CHART_SERIES_WITH_VOLUME_ON_Y_AXIS.includes(s.type)))) {
|
|
29
29
|
return series.reduce((minValue, s) => {
|
|
30
30
|
switch (s.type) {
|
|
31
31
|
case 'waterfall': {
|
|
@@ -33,7 +33,8 @@ function getAxisMin(axis, series) {
|
|
|
33
33
|
return Math.min(minValue, minSubTotal);
|
|
34
34
|
}
|
|
35
35
|
default: {
|
|
36
|
-
|
|
36
|
+
const minYValue = s.data.reduce((res, d) => Math.min(res, get(d, 'y', 0)), 0);
|
|
37
|
+
return Math.min(minValue, minYValue);
|
|
37
38
|
}
|
|
38
39
|
}
|
|
39
40
|
}, 0);
|
|
@@ -41,8 +42,16 @@ function getAxisMin(axis, series) {
|
|
|
41
42
|
return min;
|
|
42
43
|
}
|
|
43
44
|
export const getPreparedYAxis = ({ series, yAxis, }) => {
|
|
44
|
-
|
|
45
|
-
|
|
45
|
+
const axisByPlot = [];
|
|
46
|
+
const axisItems = yAxis || [{}];
|
|
47
|
+
return axisItems.map((axisItem) => {
|
|
48
|
+
const plotIndex = get(axisItem, 'plotIndex', 0);
|
|
49
|
+
const firstPlotAxis = !axisByPlot[plotIndex];
|
|
50
|
+
if (firstPlotAxis) {
|
|
51
|
+
axisByPlot[plotIndex] = [];
|
|
52
|
+
}
|
|
53
|
+
axisByPlot[plotIndex].push(axisItem);
|
|
54
|
+
const defaultAxisPosition = firstPlotAxis ? 'left' : 'right';
|
|
46
55
|
const labelsEnabled = get(axisItem, 'labels.enabled', true);
|
|
47
56
|
const labelsStyle = {
|
|
48
57
|
fontSize: get(axisItem, 'labels.style.fontSize', DEFAULT_AXIS_LABEL_FONT_SIZE),
|
|
@@ -85,12 +94,13 @@ export const getPreparedYAxis = ({ series, yAxis, }) => {
|
|
|
85
94
|
min: getAxisMin(axisItem, series),
|
|
86
95
|
maxPadding: get(axisItem, 'maxPadding', 0.05),
|
|
87
96
|
grid: {
|
|
88
|
-
enabled: get(axisItem, 'grid.enabled',
|
|
97
|
+
enabled: get(axisItem, 'grid.enabled', firstPlotAxis),
|
|
89
98
|
},
|
|
90
99
|
ticks: {
|
|
91
100
|
pixelInterval: get(axisItem, 'ticks.pixelInterval'),
|
|
92
101
|
},
|
|
93
|
-
position:
|
|
102
|
+
position: get(axisItem, 'position', defaultAxisPosition),
|
|
103
|
+
plotIndex: get(axisItem, 'plotIndex', 0),
|
|
94
104
|
};
|
|
95
105
|
if (labelsEnabled) {
|
|
96
106
|
preparedAxis.labels.width = getAxisLabelMaxWidth({ axis: preparedAxis, series });
|
|
@@ -13,9 +13,10 @@ function prepareDataLabels(series) {
|
|
|
13
13
|
style,
|
|
14
14
|
})
|
|
15
15
|
: {};
|
|
16
|
+
const inside = series.stacking === 'percent' ? true : get(series, 'dataLabels.inside', false);
|
|
16
17
|
return {
|
|
17
18
|
enabled,
|
|
18
|
-
inside
|
|
19
|
+
inside,
|
|
19
20
|
style,
|
|
20
21
|
maxHeight,
|
|
21
22
|
maxWidth,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { group, sort } from 'd3';
|
|
2
|
-
import { getLabelsSize, getLeftPosition } from '../../../utils';
|
|
2
|
+
import { getDataCategoryValue, getLabelsSize, getLeftPosition } from '../../../utils';
|
|
3
3
|
import { getXValue, getYValue } from '../utils';
|
|
4
4
|
function getLabelData(point, series, xMax) {
|
|
5
5
|
const text = String(point.data.label || point.data.y);
|
|
@@ -28,9 +28,12 @@ function getLabelData(point, series, xMax) {
|
|
|
28
28
|
return labelData;
|
|
29
29
|
}
|
|
30
30
|
function getXValues(series, xAxis, xScale) {
|
|
31
|
+
const categories = xAxis.categories || [];
|
|
31
32
|
const xValues = series.reduce((acc, s) => {
|
|
32
33
|
s.data.forEach((d) => {
|
|
33
|
-
const key = String(
|
|
34
|
+
const key = String(xAxis.type === 'category'
|
|
35
|
+
? getDataCategoryValue({ axisDirection: 'x', categories, data: d })
|
|
36
|
+
: d.x);
|
|
34
37
|
if (!acc.has(key)) {
|
|
35
38
|
acc.set(key, getXValue({ point: d, xAxis, xScale }));
|
|
36
39
|
}
|
|
@@ -38,7 +41,7 @@ function getXValues(series, xAxis, xScale) {
|
|
|
38
41
|
return acc;
|
|
39
42
|
}, new Map());
|
|
40
43
|
if (xAxis.type === 'category') {
|
|
41
|
-
return
|
|
44
|
+
return categories.reduce((acc, category) => {
|
|
42
45
|
const xValue = xValues.get(category);
|
|
43
46
|
if (typeof xValue === 'number') {
|
|
44
47
|
acc.push([category, xValue]);
|
|
@@ -62,9 +65,16 @@ export const prepareAreaData = (args) => {
|
|
|
62
65
|
const yAxisIndex = s.yAxis;
|
|
63
66
|
const seriesYAxis = yAxis[yAxisIndex];
|
|
64
67
|
const seriesYScale = yScale[yAxisIndex];
|
|
65
|
-
const
|
|
68
|
+
const yMin = getYValue({ point: { y: 0 }, yAxis: seriesYAxis, yScale: seriesYScale });
|
|
66
69
|
const seriesData = s.data.reduce((m, d) => {
|
|
67
|
-
|
|
70
|
+
const key = String(xAxis.type === 'category'
|
|
71
|
+
? getDataCategoryValue({
|
|
72
|
+
axisDirection: 'x',
|
|
73
|
+
categories: xAxis.categories || [],
|
|
74
|
+
data: d,
|
|
75
|
+
})
|
|
76
|
+
: d.x);
|
|
77
|
+
return m.set(key, d);
|
|
68
78
|
}, new Map());
|
|
69
79
|
const points = xValues.reduce((pointsAcc, [x, xValue]) => {
|
|
70
80
|
const accumulatedYValue = accumulatedYValues.get(x) || 0;
|
|
@@ -110,11 +110,13 @@ export const prepareBarXData = (args) => {
|
|
|
110
110
|
xCenter = xLinearScale(Number(xValue));
|
|
111
111
|
}
|
|
112
112
|
const x = xCenter - currentGroupWidth / 2 + (rectWidth + rectGap) * groupItemIndex;
|
|
113
|
-
const
|
|
114
|
-
const
|
|
113
|
+
const yDataValue = yValue.data.y;
|
|
114
|
+
const y = seriesYScale(yDataValue);
|
|
115
|
+
const base = seriesYScale(0);
|
|
116
|
+
const height = yDataValue > 0 ? base - y : y - base;
|
|
115
117
|
const barData = {
|
|
116
118
|
x,
|
|
117
|
-
y: y - stackHeight,
|
|
119
|
+
y: yDataValue > 0 ? y - stackHeight : seriesYScale(0),
|
|
118
120
|
width: rectWidth,
|
|
119
121
|
height,
|
|
120
122
|
opacity: get(yValue.data, 'opacity', null),
|
|
@@ -81,7 +81,8 @@ export const prepareBarYData = (args) => {
|
|
|
81
81
|
const stacks = Object.values(val);
|
|
82
82
|
const currentBarHeight = barHeight * stacks.length + rectGap * (stacks.length - 1);
|
|
83
83
|
stacks.forEach((measureValues, groupItemIndex) => {
|
|
84
|
-
|
|
84
|
+
const base = xLinearScale(0);
|
|
85
|
+
let stackSum = base;
|
|
85
86
|
const stackItems = [];
|
|
86
87
|
const sortedData = sortKey
|
|
87
88
|
? sort(measureValues, (a, b) => comparator(get(a, sortKey), get(b, sortKey)))
|
|
@@ -97,9 +98,10 @@ export const prepareBarYData = (args) => {
|
|
|
97
98
|
center = scale(Number(yValue));
|
|
98
99
|
}
|
|
99
100
|
const y = center - currentBarHeight / 2 + (barHeight + rectGap) * groupItemIndex;
|
|
100
|
-
const
|
|
101
|
+
const xValue = Number(data.x);
|
|
102
|
+
const width = xValue > 0 ? xLinearScale(xValue) - base : base - xLinearScale(xValue);
|
|
101
103
|
stackItems.push({
|
|
102
|
-
x: stackSum,
|
|
104
|
+
x: xValue > 0 ? stackSum : stackSum - width,
|
|
103
105
|
y,
|
|
104
106
|
width,
|
|
105
107
|
height: barHeight,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { Dispatch } from 'd3';
|
|
3
|
-
import type { PreparedSeries, PreparedSeriesOptions } from '../';
|
|
3
|
+
import type { PreparedSeries, PreparedSeriesOptions, PreparedSplit } from '../';
|
|
4
4
|
import type { ChartScale } from '../useAxisScales';
|
|
5
5
|
import type { PreparedAxis } from '../useChartOptions/types';
|
|
6
6
|
import type { PreparedAreaData } from './area/types';
|
|
@@ -24,6 +24,7 @@ type Args = {
|
|
|
24
24
|
yAxis: PreparedAxis[];
|
|
25
25
|
xScale?: ChartScale;
|
|
26
26
|
yScale?: ChartScale[];
|
|
27
|
+
split: PreparedSplit;
|
|
27
28
|
};
|
|
28
29
|
export declare const useShapes: (args: Args) => {
|
|
29
30
|
shapes: React.ReactElement<any, string | React.JSXElementConstructor<any>>[];
|
|
@@ -15,7 +15,7 @@ import { prepareTreemapData } from './treemap/prepare-data';
|
|
|
15
15
|
import { WaterfallSeriesShapes, prepareWaterfallData } from './waterfall';
|
|
16
16
|
import './styles.css';
|
|
17
17
|
export const useShapes = (args) => {
|
|
18
|
-
const { boundsWidth, boundsHeight, dispatcher, series, seriesOptions, xAxis, xScale, yAxis, yScale, } = args;
|
|
18
|
+
const { boundsWidth, boundsHeight, dispatcher, series, seriesOptions, xAxis, xScale, yAxis, yScale, split, } = args;
|
|
19
19
|
const shapesComponents = React.useMemo(() => {
|
|
20
20
|
const visibleSeries = getOnlyVisibleSeries(series);
|
|
21
21
|
const groupedSeries = group(visibleSeries, (item) => item.type);
|
|
@@ -77,6 +77,7 @@ export const useShapes = (args) => {
|
|
|
77
77
|
xScale,
|
|
78
78
|
yAxis,
|
|
79
79
|
yScale,
|
|
80
|
+
split,
|
|
80
81
|
});
|
|
81
82
|
acc.push(React.createElement(LineSeriesShapes, { key: "line", dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData }));
|
|
82
83
|
shapesData.push(...preparedData);
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import { ChartScale } from '../../useAxisScales';
|
|
2
|
-
import { PreparedAxis } from '../../useChartOptions/types';
|
|
3
|
-
import { PreparedLineSeries } from '../../useSeries/types';
|
|
4
|
-
import {
|
|
1
|
+
import type { ChartScale } from '../../useAxisScales';
|
|
2
|
+
import type { PreparedAxis } from '../../useChartOptions/types';
|
|
3
|
+
import type { PreparedLineSeries } from '../../useSeries/types';
|
|
4
|
+
import type { PreparedSplit } from '../../useSplit/types';
|
|
5
|
+
import type { PreparedLineData } from './types';
|
|
5
6
|
export declare const prepareLineData: (args: {
|
|
6
7
|
series: PreparedLineSeries[];
|
|
7
8
|
xAxis: PreparedAxis;
|
|
8
9
|
xScale: ChartScale;
|
|
9
10
|
yAxis: PreparedAxis[];
|
|
10
11
|
yScale: ChartScale[];
|
|
12
|
+
split: PreparedSplit;
|
|
11
13
|
}) => PreparedLineData[];
|
|
@@ -27,15 +27,18 @@ function getLabelData(point, series, xMax) {
|
|
|
27
27
|
return labelData;
|
|
28
28
|
}
|
|
29
29
|
export const prepareLineData = (args) => {
|
|
30
|
-
const { series, xAxis, xScale, yScale } = args;
|
|
31
|
-
const yAxis = args.yAxis[0];
|
|
30
|
+
const { series, xAxis, yAxis, xScale, yScale, split } = args;
|
|
32
31
|
const [_xMin, xRangeMax] = xScale.range();
|
|
33
32
|
const xMax = xRangeMax / (1 - xAxis.maxPadding);
|
|
34
33
|
return series.reduce((acc, s) => {
|
|
34
|
+
var _a;
|
|
35
|
+
const yAxisIndex = s.yAxis;
|
|
36
|
+
const seriesYAxis = yAxis[yAxisIndex];
|
|
37
|
+
const yAxisTop = ((_a = split.plots[seriesYAxis.plotIndex]) === null || _a === void 0 ? void 0 : _a.top) || 0;
|
|
35
38
|
const seriesYScale = yScale[s.yAxis];
|
|
36
39
|
const points = s.data.map((d) => ({
|
|
37
40
|
x: getXValue({ point: d, xAxis, xScale }),
|
|
38
|
-
y: getYValue({ point: d, yAxis, yScale: seriesYScale }),
|
|
41
|
+
y: yAxisTop + getYValue({ point: d, yAxis: seriesYAxis, yScale: seriesYScale }),
|
|
39
42
|
active: true,
|
|
40
43
|
data: d,
|
|
41
44
|
series: s,
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ChartKitWidgetSplit } from '../../../../../types';
|
|
2
|
+
import type { PreparedSplit } from './types';
|
|
3
|
+
type UseSplitArgs = {
|
|
4
|
+
split?: ChartKitWidgetSplit;
|
|
5
|
+
boundsHeight: number;
|
|
6
|
+
chartWidth: number;
|
|
7
|
+
};
|
|
8
|
+
export declare function getPlotHeight(args: {
|
|
9
|
+
split: ChartKitWidgetSplit | undefined;
|
|
10
|
+
boundsHeight: number;
|
|
11
|
+
gap: number;
|
|
12
|
+
}): number;
|
|
13
|
+
export declare const useSplit: (args: UseSplitArgs) => PreparedSplit;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import get from 'lodash/get';
|
|
2
|
+
import { calculateNumericProperty, getHorisontalSvgTextHeight } from '../../utils';
|
|
3
|
+
const DEFAULT_TITLE_FONT_SIZE = '15px';
|
|
4
|
+
const TITLE_TOP_BOTTOM_PADDING = 8;
|
|
5
|
+
function preparePlotTitle(args) {
|
|
6
|
+
const { title, plotIndex, plotHeight, chartWidth, gap } = args;
|
|
7
|
+
const titleText = (title === null || title === void 0 ? void 0 : title.text) || '';
|
|
8
|
+
const titleStyle = {
|
|
9
|
+
fontSize: get(title, 'style.fontSize', DEFAULT_TITLE_FONT_SIZE),
|
|
10
|
+
fontWeight: get(title, 'style.fontWeight'),
|
|
11
|
+
};
|
|
12
|
+
const titleHeight = titleText
|
|
13
|
+
? getHorisontalSvgTextHeight({ text: titleText, style: titleStyle }) +
|
|
14
|
+
TITLE_TOP_BOTTOM_PADDING * 2
|
|
15
|
+
: 0;
|
|
16
|
+
const top = plotIndex * (plotHeight + gap);
|
|
17
|
+
return {
|
|
18
|
+
text: titleText,
|
|
19
|
+
x: chartWidth / 2,
|
|
20
|
+
y: top + titleHeight / 2,
|
|
21
|
+
style: titleStyle,
|
|
22
|
+
height: titleHeight,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export function getPlotHeight(args) {
|
|
26
|
+
const { split, boundsHeight, gap } = args;
|
|
27
|
+
const plots = (split === null || split === void 0 ? void 0 : split.plots) || [];
|
|
28
|
+
if (plots.length > 1) {
|
|
29
|
+
return (boundsHeight - gap * (plots.length - 1)) / plots.length;
|
|
30
|
+
}
|
|
31
|
+
return boundsHeight;
|
|
32
|
+
}
|
|
33
|
+
export const useSplit = (args) => {
|
|
34
|
+
var _a;
|
|
35
|
+
const { split, boundsHeight, chartWidth } = args;
|
|
36
|
+
const splitGap = (_a = calculateNumericProperty({ value: split === null || split === void 0 ? void 0 : split.gap, base: boundsHeight })) !== null && _a !== void 0 ? _a : 0;
|
|
37
|
+
const plotHeight = getPlotHeight({ split: split, boundsHeight, gap: splitGap });
|
|
38
|
+
const plots = (split === null || split === void 0 ? void 0 : split.plots) || [];
|
|
39
|
+
return {
|
|
40
|
+
plots: plots.map((p, index) => {
|
|
41
|
+
const title = preparePlotTitle({
|
|
42
|
+
title: p.title,
|
|
43
|
+
plotIndex: index,
|
|
44
|
+
gap: splitGap,
|
|
45
|
+
plotHeight,
|
|
46
|
+
chartWidth,
|
|
47
|
+
});
|
|
48
|
+
const top = index * (plotHeight + splitGap);
|
|
49
|
+
return {
|
|
50
|
+
top: top + title.height,
|
|
51
|
+
height: plotHeight - title.height,
|
|
52
|
+
title,
|
|
53
|
+
};
|
|
54
|
+
}),
|
|
55
|
+
gap: splitGap,
|
|
56
|
+
};
|
|
57
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { BaseTextStyle } from '../../../../../types';
|
|
2
|
+
export type PreparedSplit = {
|
|
3
|
+
plots: PreparedPlot[];
|
|
4
|
+
gap: number;
|
|
5
|
+
};
|
|
6
|
+
export type PreparedPlot = {
|
|
7
|
+
title: PreparedPlotTitle;
|
|
8
|
+
top: number;
|
|
9
|
+
height: number;
|
|
10
|
+
};
|
|
11
|
+
export type PreparedPlotTitle = {
|
|
12
|
+
x: number;
|
|
13
|
+
y: number;
|
|
14
|
+
text: string;
|
|
15
|
+
style?: Partial<BaseTextStyle>;
|
|
16
|
+
height: number;
|
|
17
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,20 +1,22 @@
|
|
|
1
|
-
import { select } from 'd3';
|
|
1
|
+
import { path, select } from 'd3';
|
|
2
2
|
import { getXAxisItems, getXAxisOffset, getXTickPosition } from '../axis';
|
|
3
3
|
import { calculateCos, calculateSin } from '../math';
|
|
4
4
|
import { getLabelsSize, setEllipsisForOverflowText } from '../text';
|
|
5
5
|
function addDomain(selection, options) {
|
|
6
6
|
const { size, color } = options;
|
|
7
|
-
selection
|
|
7
|
+
const domainPath = selection
|
|
8
8
|
.selectAll('.domain')
|
|
9
9
|
.data([null])
|
|
10
10
|
.enter()
|
|
11
11
|
.insert('path', '.tick')
|
|
12
12
|
.attr('class', 'domain')
|
|
13
|
-
.attr('stroke', color || 'currentColor')
|
|
14
13
|
.attr('d', `M0,0V0H${size}`);
|
|
14
|
+
if (color) {
|
|
15
|
+
domainPath.style('stroke', color);
|
|
16
|
+
}
|
|
15
17
|
}
|
|
16
18
|
export function axisBottom(args) {
|
|
17
|
-
const { scale, ticks: { labelFormat, labelsPaddings = 0, labelsMargin = 0, labelsMaxWidth = Infinity, labelsStyle, labelsLineHeight,
|
|
19
|
+
const { scale, ticks: { labelFormat, labelsPaddings = 0, labelsMargin = 0, labelsMaxWidth = Infinity, labelsStyle, labelsLineHeight, items: tickItems, count: ticksCount, maxTickCount, rotation, }, domain: { size: domainSize, color: domainColor }, } = args;
|
|
18
20
|
const offset = getXAxisOffset();
|
|
19
21
|
const position = getXTickPosition({ scale, offset });
|
|
20
22
|
const values = getXAxisItems({ scale, count: ticksCount, maxCount: maxTickCount });
|
|
@@ -26,22 +28,28 @@ export function axisBottom(args) {
|
|
|
26
28
|
var _a, _b;
|
|
27
29
|
const x = ((_b = (_a = selection.node()) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect()) === null || _b === void 0 ? void 0 : _b.x) || 0;
|
|
28
30
|
const right = x + domainSize;
|
|
29
|
-
|
|
31
|
+
const top = -tickItems[0][0] || 0;
|
|
32
|
+
let transform = `translate(0, ${labelHeight + labelsMargin - top}px)`;
|
|
30
33
|
if (rotation) {
|
|
31
|
-
const labelsOffsetTop = labelHeight * calculateCos(rotation) + labelsMargin;
|
|
34
|
+
const labelsOffsetTop = labelHeight * calculateCos(rotation) + labelsMargin - top;
|
|
32
35
|
let labelsOffsetLeft = calculateSin(rotation) * labelHeight;
|
|
33
36
|
if (Math.abs(rotation) % 360 === 90) {
|
|
34
37
|
labelsOffsetLeft += ((rotation > 0 ? -1 : 1) * labelHeight) / 2;
|
|
35
38
|
}
|
|
36
39
|
transform = `translate(${-labelsOffsetLeft}px, ${labelsOffsetTop}px) rotate(${rotation}deg)`;
|
|
37
40
|
}
|
|
41
|
+
const tickPath = path();
|
|
42
|
+
tickItems.forEach(([start, end]) => {
|
|
43
|
+
tickPath.moveTo(0, start);
|
|
44
|
+
tickPath.lineTo(0, end);
|
|
45
|
+
});
|
|
38
46
|
selection
|
|
39
47
|
.selectAll('.tick')
|
|
40
48
|
.data(values)
|
|
41
49
|
.order()
|
|
42
50
|
.join((el) => {
|
|
43
51
|
const tick = el.append('g').attr('class', 'tick');
|
|
44
|
-
tick.append('
|
|
52
|
+
tick.append('path').attr('d', tickPath.toString()).attr('stroke', 'currentColor');
|
|
45
53
|
tick.append('text')
|
|
46
54
|
.text(labelFormat)
|
|
47
55
|
.attr('fill', 'currentColor')
|
|
@@ -56,7 +64,7 @@ export function axisBottom(args) {
|
|
|
56
64
|
return tick;
|
|
57
65
|
})
|
|
58
66
|
.attr('transform', function (d) {
|
|
59
|
-
return `translate(${position(d) + offset},
|
|
67
|
+
return `translate(${position(d) + offset}, ${top})`;
|
|
60
68
|
});
|
|
61
69
|
// Remove tick that has the same x coordinate like domain
|
|
62
70
|
selection
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { AxisDomain, AxisScale, ScaleBand } from 'd3';
|
|
2
|
-
import { PreparedAxis } from '../hooks';
|
|
1
|
+
import type { AxisDomain, AxisScale, ScaleBand } from 'd3';
|
|
2
|
+
import type { PreparedAxis, PreparedSplit } from '../hooks';
|
|
3
3
|
export declare function getTicksCount({ axis, range }: {
|
|
4
4
|
axis: PreparedAxis;
|
|
5
5
|
range: number;
|
|
@@ -20,3 +20,7 @@ export declare function getMaxTickCount({ axis, width }: {
|
|
|
20
20
|
axis: PreparedAxis;
|
|
21
21
|
width: number;
|
|
22
22
|
}): number;
|
|
23
|
+
export declare function getAxisHeight(args: {
|
|
24
|
+
split: PreparedSplit;
|
|
25
|
+
boundsHeight: number;
|
|
26
|
+
}): number;
|
|
@@ -41,3 +41,10 @@ export function getMaxTickCount({ axis, width }) {
|
|
|
41
41
|
const minTickWidth = parseInt(axis.labels.style.fontSize) + axis.labels.padding;
|
|
42
42
|
return Math.floor(width / minTickWidth);
|
|
43
43
|
}
|
|
44
|
+
export function getAxisHeight(args) {
|
|
45
|
+
const { split, boundsHeight } = args;
|
|
46
|
+
if (split.plots.length > 1) {
|
|
47
|
+
return split.plots[0].height;
|
|
48
|
+
}
|
|
49
|
+
return boundsHeight;
|
|
50
|
+
}
|
|
@@ -8,6 +8,8 @@ export * from './axis';
|
|
|
8
8
|
export * from './labels';
|
|
9
9
|
export * from './symbol';
|
|
10
10
|
export * from './series';
|
|
11
|
+
export declare const CHART_SERIES_WITH_VOLUME_ON_Y_AXIS: ChartKitWidgetSeries['type'][];
|
|
12
|
+
export declare const CHART_SERIES_WITH_VOLUME_ON_X_AXIS: ChartKitWidgetSeries['type'][];
|
|
11
13
|
export type AxisDirection = 'x' | 'y';
|
|
12
14
|
type UnknownSeries = {
|
|
13
15
|
type: ChartKitWidgetSeries['type'];
|
|
@@ -38,7 +40,8 @@ export declare function isSeriesWithCategoryValues(series: UnknownSeries): serie
|
|
|
38
40
|
category: string;
|
|
39
41
|
}[];
|
|
40
42
|
};
|
|
41
|
-
export declare const getDomainDataXBySeries: (series: UnknownSeries[]) =>
|
|
43
|
+
export declare const getDomainDataXBySeries: (series: UnknownSeries[]) => unknown[];
|
|
44
|
+
export declare function getDefaultMaxXAxisValue(series: UnknownSeries[]): 0 | undefined;
|
|
42
45
|
export declare const getDomainDataYBySeries: (series: UnknownSeries[]) => unknown[];
|
|
43
46
|
export declare const getSeriesNames: (series: ChartKitWidgetSeries[]) => string[];
|
|
44
47
|
export declare const getOnlyVisibleSeries: <T extends {
|
|
@@ -15,6 +15,12 @@ export * from './labels';
|
|
|
15
15
|
export * from './symbol';
|
|
16
16
|
export * from './series';
|
|
17
17
|
const CHARTS_WITHOUT_AXIS = ['pie', 'treemap'];
|
|
18
|
+
export const CHART_SERIES_WITH_VOLUME_ON_Y_AXIS = [
|
|
19
|
+
'bar-x',
|
|
20
|
+
'area',
|
|
21
|
+
'waterfall',
|
|
22
|
+
];
|
|
23
|
+
export const CHART_SERIES_WITH_VOLUME_ON_X_AXIS = ['bar-y'];
|
|
18
24
|
/**
|
|
19
25
|
* Checks whether the series should be drawn with axes.
|
|
20
26
|
*
|
|
@@ -33,42 +39,62 @@ export function isSeriesWithNumericalYValues(series) {
|
|
|
33
39
|
export function isSeriesWithCategoryValues(series) {
|
|
34
40
|
return isAxisRelatedSeries(series);
|
|
35
41
|
}
|
|
42
|
+
function getDomainDataForStackedSeries(seriesList, keyAttr = 'x', valueAttr = 'y') {
|
|
43
|
+
const acc = [];
|
|
44
|
+
const stackedSeries = group(seriesList, getSeriesStackId);
|
|
45
|
+
Array.from(stackedSeries).forEach(([_stackId, seriesStack]) => {
|
|
46
|
+
const values = {};
|
|
47
|
+
seriesStack.forEach((singleSeries) => {
|
|
48
|
+
const data = new Map();
|
|
49
|
+
singleSeries.data.forEach((point) => {
|
|
50
|
+
const key = String(point[keyAttr]);
|
|
51
|
+
let value = 0;
|
|
52
|
+
if (valueAttr in point && typeof point[valueAttr] === 'number') {
|
|
53
|
+
value = point[valueAttr];
|
|
54
|
+
}
|
|
55
|
+
if (data.has(key)) {
|
|
56
|
+
value = Math.max(value, data.get(key));
|
|
57
|
+
}
|
|
58
|
+
data.set(key, value);
|
|
59
|
+
});
|
|
60
|
+
Array.from(data).forEach(([key, value]) => {
|
|
61
|
+
values[key] = (values[key] || 0) + value;
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
acc.push(...Object.values(values));
|
|
65
|
+
});
|
|
66
|
+
return acc;
|
|
67
|
+
}
|
|
36
68
|
export const getDomainDataXBySeries = (series) => {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
69
|
+
const groupedSeries = group(series, (item) => item.type);
|
|
70
|
+
return Array.from(groupedSeries).reduce((acc, [type, seriesList]) => {
|
|
71
|
+
switch (type) {
|
|
72
|
+
case 'bar-y': {
|
|
73
|
+
acc.push(...getDomainDataForStackedSeries(seriesList, 'y', 'x'));
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
default: {
|
|
77
|
+
seriesList.filter(isSeriesWithNumericalXValues).forEach((s) => {
|
|
78
|
+
acc.push(...s.data.map((d) => d.x));
|
|
79
|
+
});
|
|
80
|
+
}
|
|
40
81
|
}
|
|
41
82
|
return acc;
|
|
42
83
|
}, []);
|
|
43
84
|
};
|
|
85
|
+
export function getDefaultMaxXAxisValue(series) {
|
|
86
|
+
if (series.some((s) => s.type === 'bar-y')) {
|
|
87
|
+
return 0;
|
|
88
|
+
}
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
44
91
|
export const getDomainDataYBySeries = (series) => {
|
|
45
92
|
const groupedSeries = group(series, (item) => item.type);
|
|
46
93
|
return Array.from(groupedSeries).reduce((acc, [type, seriesList]) => {
|
|
47
94
|
switch (type) {
|
|
48
95
|
case 'area':
|
|
49
96
|
case 'bar-x': {
|
|
50
|
-
|
|
51
|
-
Array.from(stackedSeries).forEach(([_stackId, seriesStack]) => {
|
|
52
|
-
const values = {};
|
|
53
|
-
seriesStack.forEach((singleSeries) => {
|
|
54
|
-
const data = new Map();
|
|
55
|
-
singleSeries.data.forEach((point) => {
|
|
56
|
-
const key = String(point.x);
|
|
57
|
-
let value = 0;
|
|
58
|
-
if (point.y && typeof point.y === 'number') {
|
|
59
|
-
value = point.y;
|
|
60
|
-
}
|
|
61
|
-
if (data.has(key)) {
|
|
62
|
-
value = Math.max(value, data.get(key));
|
|
63
|
-
}
|
|
64
|
-
data.set(key, value);
|
|
65
|
-
});
|
|
66
|
-
Array.from(data).forEach(([key, value]) => {
|
|
67
|
-
values[key] = (values[key] || 0) + value;
|
|
68
|
-
});
|
|
69
|
-
});
|
|
70
|
-
acc.push(...Object.values(values));
|
|
71
|
-
});
|
|
97
|
+
acc.push(...getDomainDataForStackedSeries(seriesList));
|
|
72
98
|
break;
|
|
73
99
|
}
|
|
74
100
|
case 'waterfall': {
|
|
@@ -1328,8 +1328,8 @@ export function prepareConfig(data, options, isMobile, holidays) {
|
|
|
1328
1328
|
// Callback setExtremes used because of it obligatory invocation on every zoom event
|
|
1329
1329
|
// setTimeout used because of absence resetZoomButton node in dom on first zoom event
|
|
1330
1330
|
setTimeout(() => {
|
|
1331
|
-
var _a;
|
|
1332
|
-
const text = (_a = this.chart
|
|
1331
|
+
var _a, _b;
|
|
1332
|
+
const text = (_b = (_a = this.chart) === null || _a === void 0 ? void 0 : _a.resetZoomButton) === null || _b === void 0 ? void 0 : _b.text;
|
|
1333
1333
|
if (text) {
|
|
1334
1334
|
text.translate(0, -6);
|
|
1335
1335
|
}
|
|
@@ -65,3 +65,13 @@ export type ChartKitWidgetAxis = {
|
|
|
65
65
|
* */
|
|
66
66
|
maxPadding?: number;
|
|
67
67
|
};
|
|
68
|
+
export type ChartKitWidgetXAxis = ChartKitWidgetAxis;
|
|
69
|
+
export type ChartKitWidgetYAxis = ChartKitWidgetAxis & {
|
|
70
|
+
/** Axis location.
|
|
71
|
+
* Possible values - 'left' and 'right'.
|
|
72
|
+
* */
|
|
73
|
+
position?: 'left' | 'right';
|
|
74
|
+
/** Property for splitting charts. Determines which area the axis is located in.
|
|
75
|
+
* */
|
|
76
|
+
plotIndex?: number;
|
|
77
|
+
};
|
|
@@ -45,7 +45,8 @@ export type BarYSeries<T = any> = BaseSeries & {
|
|
|
45
45
|
grouping?: boolean;
|
|
46
46
|
dataLabels?: ChartKitWidgetSeriesOptions['dataLabels'] & {
|
|
47
47
|
/**
|
|
48
|
-
* Whether to align the data label inside or outside the box
|
|
48
|
+
* Whether to align the data label inside or outside the box.
|
|
49
|
+
* For charts with a percentage stack, it is always true.
|
|
49
50
|
*
|
|
50
51
|
* @default false
|
|
51
52
|
* */
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ChartKitWidgetXAxis, ChartKitWidgetYAxis } from './axis';
|
|
2
2
|
import type { ChartKitWidgetChart } from './chart';
|
|
3
3
|
import type { ChartKitWidgetLegend } from './legend';
|
|
4
4
|
import type { ChartKitWidgetSeries, ChartKitWidgetSeriesOptions } from './series';
|
|
5
|
+
import type { ChartKitWidgetSplit } from './split';
|
|
5
6
|
import type { ChartKitWidgetTitle } from './title';
|
|
6
7
|
import type { ChartKitWidgetTooltip } from './tooltip';
|
|
7
8
|
export * from './axis';
|
|
@@ -15,6 +16,7 @@ export * from './bar-y';
|
|
|
15
16
|
export * from './area';
|
|
16
17
|
export * from './line';
|
|
17
18
|
export * from './series';
|
|
19
|
+
export * from './split';
|
|
18
20
|
export * from './title';
|
|
19
21
|
export * from './tooltip';
|
|
20
22
|
export * from './halo';
|
|
@@ -29,6 +31,9 @@ export type ChartKitWidgetData<T = any> = {
|
|
|
29
31
|
};
|
|
30
32
|
title?: ChartKitWidgetTitle;
|
|
31
33
|
tooltip?: ChartKitWidgetTooltip<T>;
|
|
32
|
-
xAxis?:
|
|
33
|
-
yAxis?:
|
|
34
|
+
xAxis?: ChartKitWidgetXAxis;
|
|
35
|
+
yAxis?: ChartKitWidgetYAxis[];
|
|
36
|
+
/** Setting for displaying charts on different plots.
|
|
37
|
+
* It can be used to visualize related information on multiple charts. */
|
|
38
|
+
split?: ChartKitWidgetSplit;
|
|
34
39
|
};
|