@gravity-ui/charts 1.21.0 → 1.23.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/dist/cjs/components/ChartInner/index.js +7 -3
- package/dist/cjs/components/ChartInner/useChartInnerProps.d.ts +16 -2
- package/dist/cjs/components/ChartInner/useChartInnerProps.js +2 -3
- package/dist/cjs/components/ChartInner/utils.d.ts +10 -1
- package/dist/cjs/components/ChartInner/utils.js +60 -0
- package/dist/cjs/components/Tooltip/DefaultTooltipContent/utils.d.ts +2 -2
- package/dist/cjs/constants/index.d.ts +1 -0
- package/dist/cjs/constants/index.js +1 -0
- package/dist/cjs/constants/zoom.d.ts +6 -0
- package/dist/cjs/constants/zoom.js +5 -0
- package/dist/cjs/hooks/useBrush/index.js +7 -6
- package/dist/cjs/hooks/useBrush/types.d.ts +3 -2
- package/dist/cjs/hooks/useChartOptions/chart.js +1 -77
- package/dist/cjs/hooks/useChartOptions/zoom.d.ts +11 -0
- package/dist/cjs/hooks/useChartOptions/zoom.js +88 -0
- package/dist/cjs/hooks/useSeries/prepare-area.js +15 -1
- package/dist/cjs/hooks/useSeries/prepare-bar-x.js +13 -1
- package/dist/cjs/hooks/useSeries/prepare-bar-y.d.ts +2 -2
- package/dist/cjs/hooks/useSeries/prepare-bar-y.js +13 -1
- package/dist/cjs/hooks/useSeries/prepare-heatmap.js +13 -1
- package/dist/cjs/hooks/useSeries/prepare-legend.js +1 -1
- package/dist/cjs/hooks/useSeries/prepare-line.js +15 -1
- package/dist/cjs/hooks/useSeries/prepare-pie.js +15 -2
- package/dist/cjs/hooks/useSeries/prepare-scatter.js +16 -1
- package/dist/cjs/hooks/useSeries/prepare-waterfall.js +18 -2
- package/dist/cjs/hooks/useShapes/area/index.js +2 -0
- package/dist/cjs/hooks/useShapes/area/prepare-data.js +37 -22
- package/dist/cjs/hooks/useShapes/area/types.d.ts +5 -2
- package/dist/cjs/hooks/useShapes/bar-x/prepare-data.js +8 -2
- package/dist/cjs/hooks/useShapes/bar-y/index.d.ts +1 -1
- package/dist/cjs/hooks/useShapes/bar-y/index.js +19 -22
- package/dist/cjs/hooks/useShapes/bar-y/prepare-data.js +24 -5
- package/dist/cjs/hooks/useShapes/bar-y/types.d.ts +2 -2
- package/dist/cjs/hooks/useShapes/bar-y/utils.d.ts +3 -0
- package/dist/cjs/hooks/useShapes/bar-y/utils.js +44 -0
- package/dist/cjs/hooks/useShapes/heatmap/prepare-data.js +7 -3
- package/dist/cjs/hooks/useShapes/line/index.js +1 -0
- package/dist/cjs/hooks/useShapes/line/prepare-data.js +41 -16
- package/dist/cjs/hooks/useShapes/line/types.d.ts +7 -3
- package/dist/cjs/hooks/useShapes/pie/prepare-data.js +8 -4
- package/dist/cjs/hooks/useShapes/scatter/prepare-data.js +1 -1
- package/dist/cjs/hooks/useShapes/utils.d.ts +14 -6
- package/dist/cjs/hooks/useShapes/utils.js +66 -18
- package/dist/cjs/hooks/useShapes/waterfall/prepare-data.js +18 -8
- package/dist/cjs/hooks/useTooltip/index.js +8 -4
- package/dist/cjs/hooks/useZoom/index.js +2 -0
- package/dist/cjs/hooks/useZoom/utils.d.ts +3 -2
- package/dist/cjs/hooks/useZoom/utils.js +4 -3
- package/dist/cjs/hooks/utils/bar-y.d.ts +8 -1
- package/dist/cjs/hooks/utils/bar-y.js +4 -0
- package/dist/cjs/i18n/keysets/en.json +2 -2
- package/dist/cjs/i18n/keysets/ru.json +2 -2
- package/dist/cjs/types/chart/area.d.ts +11 -1
- package/dist/cjs/types/chart/bar-x.d.ts +10 -1
- package/dist/cjs/types/chart/bar-y.d.ts +10 -1
- package/dist/cjs/types/chart/heatmap.d.ts +10 -1
- package/dist/cjs/types/chart/line.d.ts +11 -1
- package/dist/cjs/types/chart/pie.d.ts +10 -1
- package/dist/cjs/types/chart/scatter.d.ts +11 -2
- package/dist/cjs/types/chart/waterfall.d.ts +10 -1
- package/dist/cjs/types/chart/zoom.d.ts +31 -1
- package/dist/cjs/utils/chart/get-closest-data.js +12 -7
- package/dist/cjs/utils/chart/series/sorting.js +17 -4
- package/dist/cjs/utils/chart/text.js +24 -21
- package/dist/cjs/utils/chart/zoom.js +4 -2
- package/dist/cjs/validation/index.js +3 -3
- package/dist/esm/components/ChartInner/index.js +7 -3
- package/dist/esm/components/ChartInner/useChartInnerProps.d.ts +16 -2
- package/dist/esm/components/ChartInner/useChartInnerProps.js +2 -3
- package/dist/esm/components/ChartInner/utils.d.ts +10 -1
- package/dist/esm/components/ChartInner/utils.js +60 -0
- package/dist/esm/components/Tooltip/DefaultTooltipContent/utils.d.ts +2 -2
- package/dist/esm/constants/index.d.ts +1 -0
- package/dist/esm/constants/index.js +1 -0
- package/dist/esm/constants/zoom.d.ts +6 -0
- package/dist/esm/constants/zoom.js +5 -0
- package/dist/esm/hooks/useBrush/index.js +7 -6
- package/dist/esm/hooks/useBrush/types.d.ts +3 -2
- package/dist/esm/hooks/useChartOptions/chart.js +1 -77
- package/dist/esm/hooks/useChartOptions/zoom.d.ts +11 -0
- package/dist/esm/hooks/useChartOptions/zoom.js +88 -0
- package/dist/esm/hooks/useSeries/prepare-area.js +15 -1
- package/dist/esm/hooks/useSeries/prepare-bar-x.js +13 -1
- package/dist/esm/hooks/useSeries/prepare-bar-y.d.ts +2 -2
- package/dist/esm/hooks/useSeries/prepare-bar-y.js +13 -1
- package/dist/esm/hooks/useSeries/prepare-heatmap.js +13 -1
- package/dist/esm/hooks/useSeries/prepare-legend.js +1 -1
- package/dist/esm/hooks/useSeries/prepare-line.js +15 -1
- package/dist/esm/hooks/useSeries/prepare-pie.js +15 -2
- package/dist/esm/hooks/useSeries/prepare-scatter.js +16 -1
- package/dist/esm/hooks/useSeries/prepare-waterfall.js +18 -2
- package/dist/esm/hooks/useShapes/area/index.js +2 -0
- package/dist/esm/hooks/useShapes/area/prepare-data.js +37 -22
- package/dist/esm/hooks/useShapes/area/types.d.ts +5 -2
- package/dist/esm/hooks/useShapes/bar-x/prepare-data.js +8 -2
- package/dist/esm/hooks/useShapes/bar-y/index.d.ts +1 -1
- package/dist/esm/hooks/useShapes/bar-y/index.js +19 -22
- package/dist/esm/hooks/useShapes/bar-y/prepare-data.js +24 -5
- package/dist/esm/hooks/useShapes/bar-y/types.d.ts +2 -2
- package/dist/esm/hooks/useShapes/bar-y/utils.d.ts +3 -0
- package/dist/esm/hooks/useShapes/bar-y/utils.js +44 -0
- package/dist/esm/hooks/useShapes/heatmap/prepare-data.js +7 -3
- package/dist/esm/hooks/useShapes/line/index.js +1 -0
- package/dist/esm/hooks/useShapes/line/prepare-data.js +41 -16
- package/dist/esm/hooks/useShapes/line/types.d.ts +7 -3
- package/dist/esm/hooks/useShapes/pie/prepare-data.js +8 -4
- package/dist/esm/hooks/useShapes/scatter/prepare-data.js +1 -1
- package/dist/esm/hooks/useShapes/utils.d.ts +14 -6
- package/dist/esm/hooks/useShapes/utils.js +66 -18
- package/dist/esm/hooks/useShapes/waterfall/prepare-data.js +18 -8
- package/dist/esm/hooks/useTooltip/index.js +8 -4
- package/dist/esm/hooks/useZoom/index.js +2 -0
- package/dist/esm/hooks/useZoom/utils.d.ts +3 -2
- package/dist/esm/hooks/useZoom/utils.js +4 -3
- package/dist/esm/hooks/utils/bar-y.d.ts +8 -1
- package/dist/esm/hooks/utils/bar-y.js +4 -0
- package/dist/esm/i18n/keysets/en.json +2 -2
- package/dist/esm/i18n/keysets/ru.json +2 -2
- package/dist/esm/types/chart/area.d.ts +11 -1
- package/dist/esm/types/chart/bar-x.d.ts +10 -1
- package/dist/esm/types/chart/bar-y.d.ts +10 -1
- package/dist/esm/types/chart/heatmap.d.ts +10 -1
- package/dist/esm/types/chart/line.d.ts +11 -1
- package/dist/esm/types/chart/pie.d.ts +10 -1
- package/dist/esm/types/chart/scatter.d.ts +11 -2
- package/dist/esm/types/chart/waterfall.d.ts +10 -1
- package/dist/esm/types/chart/zoom.d.ts +31 -1
- package/dist/esm/utils/chart/get-closest-data.js +12 -7
- package/dist/esm/utils/chart/series/sorting.js +17 -4
- package/dist/esm/utils/chart/text.js +24 -21
- package/dist/esm/utils/chart/zoom.js +4 -2
- package/dist/esm/validation/index.js +3 -3
- package/package.json +1 -1
|
@@ -4,13 +4,26 @@ import { DEFAULT_DATALABELS_STYLE } from '../../constants';
|
|
|
4
4
|
import { getUniqId } from '../../utils';
|
|
5
5
|
import { DEFAULT_DATALABELS_PADDING } from './constants';
|
|
6
6
|
import { prepareLegendSymbol } from './utils';
|
|
7
|
+
function prepareSeriesData(series) {
|
|
8
|
+
var _a;
|
|
9
|
+
const nullMode = (_a = series.nullMode) !== null && _a !== void 0 ? _a : 'skip';
|
|
10
|
+
const data = series.data;
|
|
11
|
+
switch (nullMode) {
|
|
12
|
+
case 'zero':
|
|
13
|
+
return data.map((p) => { var _a; return (Object.assign(Object.assign({}, p), { value: (_a = p.value) !== null && _a !== void 0 ? _a : 0 })); });
|
|
14
|
+
case 'skip':
|
|
15
|
+
default:
|
|
16
|
+
return data.filter((p) => p.value !== null);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
7
19
|
export function preparePieSeries(args) {
|
|
8
20
|
const { series, seriesOptions, legend, colors } = args;
|
|
9
|
-
const
|
|
21
|
+
const preparedData = prepareSeriesData(series);
|
|
22
|
+
const dataNames = preparedData.map((d) => d.name);
|
|
10
23
|
const colorScale = scaleOrdinal(dataNames, colors);
|
|
11
24
|
const stackId = getUniqId();
|
|
12
25
|
const seriesHoverState = get(seriesOptions, 'pie.states.hover');
|
|
13
|
-
const preparedSeries =
|
|
26
|
+
const preparedSeries = preparedData.map((dataItem, i) => {
|
|
14
27
|
var _a, _b, _c, _d, _e, _f;
|
|
15
28
|
const result = {
|
|
16
29
|
type: 'pie',
|
|
@@ -20,6 +20,21 @@ function prepareMarker(series, seriesOptions, index) {
|
|
|
20
20
|
},
|
|
21
21
|
};
|
|
22
22
|
}
|
|
23
|
+
function prepareSeriesData(series) {
|
|
24
|
+
var _a;
|
|
25
|
+
const nullMode = (_a = series.nullMode) !== null && _a !== void 0 ? _a : 'skip';
|
|
26
|
+
const data = series.data;
|
|
27
|
+
switch (nullMode) {
|
|
28
|
+
case 'zero':
|
|
29
|
+
return data.map((p) => {
|
|
30
|
+
var _a, _b;
|
|
31
|
+
return (Object.assign(Object.assign({}, p), { x: (_a = p.x) !== null && _a !== void 0 ? _a : 0, y: (_b = p.y) !== null && _b !== void 0 ? _b : 0 }));
|
|
32
|
+
});
|
|
33
|
+
case 'skip':
|
|
34
|
+
default:
|
|
35
|
+
return data.filter((p) => p.y !== null && p.x !== null);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
23
38
|
export function prepareScatterSeries(args) {
|
|
24
39
|
const { colorScale, series, seriesOptions, legend } = args;
|
|
25
40
|
return series.map((s, index) => {
|
|
@@ -36,7 +51,7 @@ export function prepareScatterSeries(args) {
|
|
|
36
51
|
enabled: get(s, 'legend.enabled', legend.enabled),
|
|
37
52
|
symbol: prepareLegendSymbol(s, symbolType),
|
|
38
53
|
},
|
|
39
|
-
data: s
|
|
54
|
+
data: prepareSeriesData(s),
|
|
40
55
|
marker: prepareMarker(s, seriesOptions, index),
|
|
41
56
|
cursor: get(s, 'cursor', null),
|
|
42
57
|
yAxis: get(s, 'yAxis', 0),
|
|
@@ -3,6 +3,21 @@ import { DEFAULT_DATALABELS_STYLE } from '../../constants';
|
|
|
3
3
|
import { getUniqId } from '../../utils';
|
|
4
4
|
import { DEFAULT_DATALABELS_PADDING } from './constants';
|
|
5
5
|
import { prepareLegendSymbol } from './utils';
|
|
6
|
+
function prepareSeriesData(series) {
|
|
7
|
+
var _a;
|
|
8
|
+
const nullMode = (_a = series.nullMode) !== null && _a !== void 0 ? _a : 'skip';
|
|
9
|
+
const data = series.data;
|
|
10
|
+
switch (nullMode) {
|
|
11
|
+
case 'zero':
|
|
12
|
+
return data.map((d) => {
|
|
13
|
+
var _a;
|
|
14
|
+
return (Object.assign(Object.assign({}, d), { y: d.total ? d.y : ((_a = d.y) !== null && _a !== void 0 ? _a : 0) }));
|
|
15
|
+
});
|
|
16
|
+
case 'skip':
|
|
17
|
+
default:
|
|
18
|
+
return data.filter((d) => d.y !== null);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
6
21
|
export function prepareWaterfallSeries(args) {
|
|
7
22
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
|
|
8
23
|
const { colorScale, series: seriesList, legend, colors } = args;
|
|
@@ -33,14 +48,15 @@ export function prepareWaterfallSeries(args) {
|
|
|
33
48
|
const positive = Object.assign(Object.assign({}, common), { name: (_g = (_f = (_e = series.legend) === null || _e === void 0 ? void 0 : _e.itemText) === null || _f === void 0 ? void 0 : _f.positive) !== null && _g !== void 0 ? _g : `${series.name} ↑`, id: getUniqId(), color: series.positiveColor || positiveColor, data: [] });
|
|
34
49
|
const negative = Object.assign(Object.assign({}, common), { name: (_k = (_j = (_h = series.legend) === null || _h === void 0 ? void 0 : _h.itemText) === null || _j === void 0 ? void 0 : _j.negative) !== null && _k !== void 0 ? _k : `${series.name} ↓`, id: getUniqId(), color: series.negativeColor || negativeColor, data: [] });
|
|
35
50
|
const totals = Object.assign(Object.assign({}, common), { name: (_o = (_m = (_l = series.legend) === null || _l === void 0 ? void 0 : _l.itemText) === null || _m === void 0 ? void 0 : _m.totals) !== null && _o !== void 0 ? _o : series.name, id: getUniqId(), data: [] });
|
|
36
|
-
|
|
51
|
+
const preparedData = prepareSeriesData(series);
|
|
52
|
+
preparedData.forEach((d, index) => {
|
|
37
53
|
var _a;
|
|
38
54
|
const value = (_a = d === null || d === void 0 ? void 0 : d.y) !== null && _a !== void 0 ? _a : 0;
|
|
39
55
|
const dataItem = Object.assign(Object.assign({}, d), { index });
|
|
40
56
|
if (d === null || d === void 0 ? void 0 : d.total) {
|
|
41
57
|
totals.data.push(dataItem);
|
|
42
58
|
}
|
|
43
|
-
else if (value
|
|
59
|
+
else if (value >= 0) {
|
|
44
60
|
positive.data.push(dataItem);
|
|
45
61
|
}
|
|
46
62
|
else if (value < 0) {
|
|
@@ -22,6 +22,7 @@ export const AreaSeriesShapes = (args) => {
|
|
|
22
22
|
const inactiveOptions = get(seriesOptions, 'area.states.inactive');
|
|
23
23
|
const line = lineGenerator()
|
|
24
24
|
.x((d) => d.x)
|
|
25
|
+
.defined((d) => d.y !== null)
|
|
25
26
|
.y((d) => d.y);
|
|
26
27
|
plotSvgElement.selectAll('*').remove();
|
|
27
28
|
markersSvgElement.selectAll('*').remove();
|
|
@@ -41,6 +42,7 @@ export const AreaSeriesShapes = (args) => {
|
|
|
41
42
|
.attr('stroke-linejoin', 'round')
|
|
42
43
|
.attr('stroke-linecap', 'round');
|
|
43
44
|
const area = areaGenerator()
|
|
45
|
+
.defined((d) => d.y !== null)
|
|
44
46
|
.x((d) => d.x)
|
|
45
47
|
.y0((d) => d.y0)
|
|
46
48
|
.y1((d) => d.y);
|
|
@@ -35,8 +35,9 @@ function getXValues(series, xAxis, xScale) {
|
|
|
35
35
|
const key = String(xAxis.type === 'category'
|
|
36
36
|
? getDataCategoryValue({ axisDirection: 'x', categories, data: d })
|
|
37
37
|
: d.x);
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
const xValue = getXValue({ point: d, points: s.data, xAxis, xScale });
|
|
39
|
+
if (!acc.has(key) && xValue !== null) {
|
|
40
|
+
acc.set(key, xValue);
|
|
40
41
|
}
|
|
41
42
|
});
|
|
42
43
|
return acc;
|
|
@@ -53,6 +54,7 @@ function getXValues(series, xAxis, xScale) {
|
|
|
53
54
|
return Array.from(xValues);
|
|
54
55
|
}
|
|
55
56
|
export const prepareAreaData = async (args) => {
|
|
57
|
+
var _a;
|
|
56
58
|
const { series, xAxis, xScale, yAxis, yScale, boundsHeight: plotHeight, isOutsideBounds } = args;
|
|
57
59
|
const [_xMin, xRangeMax] = xScale.range();
|
|
58
60
|
const xMax = xRangeMax / (1 - xAxis.maxPadding);
|
|
@@ -74,12 +76,12 @@ export const prepareAreaData = async (args) => {
|
|
|
74
76
|
if (!seriesYScale) {
|
|
75
77
|
continue;
|
|
76
78
|
}
|
|
77
|
-
const yMin = getYValue({
|
|
79
|
+
const yMin = (_a = getYValue({
|
|
78
80
|
point: { y: 0 },
|
|
79
81
|
points: s.data,
|
|
80
82
|
yAxis: seriesYAxis,
|
|
81
83
|
yScale: seriesYScale,
|
|
82
|
-
});
|
|
84
|
+
})) !== null && _a !== void 0 ? _a : 0;
|
|
83
85
|
const seriesData = s.data.reduce((m, d) => {
|
|
84
86
|
const key = String(xAxis.type === 'category'
|
|
85
87
|
? getDataCategoryValue({
|
|
@@ -91,20 +93,21 @@ export const prepareAreaData = async (args) => {
|
|
|
91
93
|
return m.set(key, d);
|
|
92
94
|
}, new Map());
|
|
93
95
|
const points = xValues.reduce((pointsAcc, [x, xValue]) => {
|
|
96
|
+
var _a;
|
|
94
97
|
const accumulatedYValue = accumulatedYValues.get(x) || 0;
|
|
95
|
-
const d = seriesData.get(x)
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
98
|
+
const d = (_a = seriesData.get(x)) !== null && _a !== void 0 ? _a : {
|
|
99
|
+
x,
|
|
100
|
+
y: 0,
|
|
101
|
+
};
|
|
102
|
+
const yValue = getYValue({ point: d, yAxis: seriesYAxis, yScale: seriesYScale });
|
|
103
|
+
const yPointValue = yValue === null ? null : yValue - accumulatedYValue;
|
|
104
|
+
if (yPointValue !== null) {
|
|
105
|
+
accumulatedYValues.set(x, yMin - yPointValue);
|
|
106
|
+
}
|
|
104
107
|
pointsAcc.push({
|
|
105
108
|
y0: yMin - accumulatedYValue,
|
|
106
109
|
x: xValue,
|
|
107
|
-
y:
|
|
110
|
+
y: yPointValue,
|
|
108
111
|
data: d,
|
|
109
112
|
series: s,
|
|
110
113
|
});
|
|
@@ -113,7 +116,13 @@ export const prepareAreaData = async (args) => {
|
|
|
113
116
|
let labels = [];
|
|
114
117
|
const htmlElements = [];
|
|
115
118
|
if (s.dataLabels.enabled) {
|
|
116
|
-
const labelItems = await Promise.all(points.
|
|
119
|
+
const labelItems = await Promise.all(points.reduce((labelItemsAcc, p) => {
|
|
120
|
+
if (p.y === null) {
|
|
121
|
+
return labelItemsAcc;
|
|
122
|
+
}
|
|
123
|
+
labelItemsAcc.push(getLabelData(p, s, xMax));
|
|
124
|
+
return labelItemsAcc;
|
|
125
|
+
}, []));
|
|
117
126
|
if (s.dataLabels.html) {
|
|
118
127
|
const htmlLabels = await Promise.all(labelItems.map(async (l) => {
|
|
119
128
|
var _a;
|
|
@@ -142,12 +151,18 @@ export const prepareAreaData = async (args) => {
|
|
|
142
151
|
}
|
|
143
152
|
let markers = [];
|
|
144
153
|
if (s.marker.states.normal.enabled || s.marker.states.hover.enabled) {
|
|
145
|
-
markers = points.
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
154
|
+
markers = points.reduce((markersAcc, p) => {
|
|
155
|
+
if (p.y === null) {
|
|
156
|
+
return markersAcc;
|
|
157
|
+
}
|
|
158
|
+
markersAcc.push({
|
|
159
|
+
point: p,
|
|
160
|
+
active: true,
|
|
161
|
+
hovered: false,
|
|
162
|
+
clipped: isOutsideBounds(p.x, p.y),
|
|
163
|
+
});
|
|
164
|
+
return markersAcc;
|
|
165
|
+
}, []);
|
|
151
166
|
}
|
|
152
167
|
seriesStackData.push({
|
|
153
168
|
points,
|
|
@@ -170,7 +185,7 @@ export const prepareAreaData = async (args) => {
|
|
|
170
185
|
const ratio = plotHeight / stackHeight;
|
|
171
186
|
seriesStackData.forEach((item) => {
|
|
172
187
|
const point = item.points[index];
|
|
173
|
-
if (point) {
|
|
188
|
+
if (point.y !== null && point.y !== undefined) {
|
|
174
189
|
const height = (point.y0 - point.y) * ratio;
|
|
175
190
|
point.y0 = plotHeight - height - acc;
|
|
176
191
|
point.y = point.y0 + height;
|
|
@@ -3,13 +3,16 @@ import type { PreparedAreaSeries } from '../../useSeries/types';
|
|
|
3
3
|
export type PointData = {
|
|
4
4
|
y0: number;
|
|
5
5
|
x: number;
|
|
6
|
-
y: number;
|
|
6
|
+
y: number | null;
|
|
7
7
|
data: AreaSeriesData;
|
|
8
8
|
series: PreparedAreaSeries;
|
|
9
9
|
color?: string;
|
|
10
10
|
};
|
|
11
|
+
export type MarkerPointData = PointData & {
|
|
12
|
+
y: number;
|
|
13
|
+
};
|
|
11
14
|
export type MarkerData = {
|
|
12
|
-
point:
|
|
15
|
+
point: MarkerPointData;
|
|
13
16
|
active: boolean;
|
|
14
17
|
hovered: boolean;
|
|
15
18
|
clipped: boolean;
|
|
@@ -3,6 +3,7 @@ import get from 'lodash/get';
|
|
|
3
3
|
import { getDataCategoryValue, getLabelsSize } from '../../../utils';
|
|
4
4
|
import { getFormattedValue } from '../../../utils/chart/format';
|
|
5
5
|
import { MIN_BAR_GAP, MIN_BAR_GROUP_GAP, MIN_BAR_WIDTH } from '../../constants';
|
|
6
|
+
const isSeriesDataValid = (d) => d.y !== null;
|
|
6
7
|
async function getLabelData(d) {
|
|
7
8
|
if (!d.series.dataLabels.enabled) {
|
|
8
9
|
return undefined;
|
|
@@ -30,7 +31,9 @@ async function getLabelData(d) {
|
|
|
30
31
|
series: d.series,
|
|
31
32
|
};
|
|
32
33
|
}
|
|
34
|
+
// eslint-disable-next-line complexity
|
|
33
35
|
export const prepareBarXData = async (args) => {
|
|
36
|
+
var _a;
|
|
34
37
|
const { series, seriesOptions, xAxis, xScale, yScale, boundsHeight: plotHeight } = args;
|
|
35
38
|
const stackGap = seriesOptions['bar-x'].stackGap;
|
|
36
39
|
const categories = get(xAxis, 'categories', []);
|
|
@@ -55,6 +58,9 @@ export const prepareBarXData = async (args) => {
|
|
|
55
58
|
const data = {};
|
|
56
59
|
series.forEach((s) => {
|
|
57
60
|
s.data.forEach((d) => {
|
|
61
|
+
if (!isSeriesDataValid(d)) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
58
64
|
const xValue = xAxis.type === 'category'
|
|
59
65
|
? getDataCategoryValue({ axisDirection: 'x', categories, data: d })
|
|
60
66
|
: d.x;
|
|
@@ -129,7 +135,7 @@ export const prepareBarXData = async (args) => {
|
|
|
129
135
|
xCenter = xLinearScale(Number(xValue));
|
|
130
136
|
}
|
|
131
137
|
const x = xCenter - currentGroupWidth / 2 + (rectWidth + rectGap) * groupItemIndex;
|
|
132
|
-
const yDataValue = yValue.data.y;
|
|
138
|
+
const yDataValue = ((_a = yValue.data.y) !== null && _a !== void 0 ? _a : 0);
|
|
133
139
|
const y = seriesYScale(yDataValue);
|
|
134
140
|
const base = seriesYScale(0);
|
|
135
141
|
const isLastStackItem = yValueIndex === sortedData.length - 1;
|
|
@@ -138,7 +144,7 @@ export const prepareBarXData = async (args) => {
|
|
|
138
144
|
if (shapeHeight < 0) {
|
|
139
145
|
shapeHeight = height;
|
|
140
146
|
}
|
|
141
|
-
if (
|
|
147
|
+
if (shapeHeight < 0) {
|
|
142
148
|
continue;
|
|
143
149
|
}
|
|
144
150
|
const barData = {
|
|
@@ -3,10 +3,10 @@ import { color, select } from 'd3';
|
|
|
3
3
|
import get from 'lodash/get';
|
|
4
4
|
import { block } from '../../../utils';
|
|
5
5
|
import { HtmlLayer } from '../HtmlLayer';
|
|
6
|
-
import {
|
|
6
|
+
import { getAdjustedRectBorderPath, getAdjustedRectPath } from './utils';
|
|
7
7
|
export { prepareBarYData } from './prepare-data';
|
|
8
8
|
const b = block('bar-y');
|
|
9
|
-
export
|
|
9
|
+
export function BarYSeriesShapes(args) {
|
|
10
10
|
const { dispatcher, preparedData: { shapes: preparedData, labels: dataLabels, htmlElements }, seriesOptions, htmlLayout, clipPathId, } = args;
|
|
11
11
|
const hoveredDataRef = React.useRef(null);
|
|
12
12
|
const ref = React.useRef(null);
|
|
@@ -16,33 +16,29 @@ export const BarYSeriesShapes = (args) => {
|
|
|
16
16
|
}
|
|
17
17
|
const svgElement = select(ref.current);
|
|
18
18
|
svgElement.selectAll('*').remove();
|
|
19
|
-
const
|
|
20
|
-
.selectAll('
|
|
19
|
+
const segmentSelection = svgElement
|
|
20
|
+
.selectAll(`path.${b('segment')}`)
|
|
21
21
|
.data(preparedData)
|
|
22
22
|
.join('path')
|
|
23
|
-
.attr('d', (d) =>
|
|
24
|
-
const borderRadius = d.isLastStackItem
|
|
25
|
-
? Math.min(d.height, d.width / 2, d.series.borderRadius)
|
|
26
|
-
: 0;
|
|
27
|
-
const p = getRectPath({
|
|
28
|
-
x: d.x,
|
|
29
|
-
y: d.y,
|
|
30
|
-
width: d.width,
|
|
31
|
-
height: d.height,
|
|
32
|
-
borderRadius: [0, borderRadius, borderRadius, 0],
|
|
33
|
-
});
|
|
34
|
-
return p.toString();
|
|
35
|
-
})
|
|
23
|
+
.attr('d', (d) => getAdjustedRectPath(d))
|
|
36
24
|
.attr('class', b('segment'))
|
|
37
25
|
.attr('x', (d) => d.x)
|
|
38
26
|
.attr('y', (d) => d.y)
|
|
39
27
|
.attr('height', (d) => d.height)
|
|
40
28
|
.attr('width', (d) => d.width)
|
|
41
29
|
.attr('fill', (d) => d.color)
|
|
42
|
-
.attr('stroke', (d) => d.borderColor)
|
|
43
|
-
.attr('stroke-width', (d) => d.borderWidth)
|
|
44
30
|
.attr('opacity', (d) => d.data.opacity || null)
|
|
45
31
|
.attr('cursor', (d) => d.series.cursor);
|
|
32
|
+
const borderSelection = svgElement
|
|
33
|
+
.selectAll(`path.${b('segment-border')}`)
|
|
34
|
+
.data(preparedData.filter((d) => d.borderWidth > 0))
|
|
35
|
+
.join('path')
|
|
36
|
+
.attr('d', (d) => getAdjustedRectBorderPath(d))
|
|
37
|
+
.attr('class', b('segment-border'))
|
|
38
|
+
.attr('fill', (d) => d.borderColor)
|
|
39
|
+
.attr('fill-rule', 'evenodd')
|
|
40
|
+
.attr('opacity', (d) => d.data.opacity || null)
|
|
41
|
+
.attr('pointer-events', 'none');
|
|
46
42
|
const labelSelection = svgElement
|
|
47
43
|
.selectAll('text')
|
|
48
44
|
.data(dataLabels)
|
|
@@ -64,7 +60,7 @@ export const BarYSeriesShapes = (args) => {
|
|
|
64
60
|
acc.add(d.data.y);
|
|
65
61
|
return acc;
|
|
66
62
|
}, new Set());
|
|
67
|
-
|
|
63
|
+
segmentSelection.attr('fill', (d) => {
|
|
68
64
|
var _a;
|
|
69
65
|
const fillColor = d.color;
|
|
70
66
|
if (hovered === null || hovered === void 0 ? void 0 : hovered.has(d.data.y)) {
|
|
@@ -82,7 +78,8 @@ export const BarYSeriesShapes = (args) => {
|
|
|
82
78
|
}
|
|
83
79
|
return null;
|
|
84
80
|
};
|
|
85
|
-
|
|
81
|
+
segmentSelection.attr('opacity', newOpacity);
|
|
82
|
+
borderSelection.attr('opacity', newOpacity);
|
|
86
83
|
labelSelection.attr('opacity', newOpacity);
|
|
87
84
|
}
|
|
88
85
|
}
|
|
@@ -97,4 +94,4 @@ export const BarYSeriesShapes = (args) => {
|
|
|
97
94
|
return (React.createElement(React.Fragment, null,
|
|
98
95
|
React.createElement("g", { ref: ref, className: b(), clipPath: `url(#${clipPathId})` }),
|
|
99
96
|
React.createElement(HtmlLayer, { preparedData: { htmlElements }, htmlLayout: htmlLayout })));
|
|
100
|
-
}
|
|
97
|
+
}
|
|
@@ -10,6 +10,7 @@ export async function prepareBarYData(args) {
|
|
|
10
10
|
const stackGap = seriesOptions['bar-y'].stackGap;
|
|
11
11
|
const xLinearScale = xScale;
|
|
12
12
|
const yLinearScale = yScale;
|
|
13
|
+
const [baseRangeValue] = xLinearScale.range();
|
|
13
14
|
if (!yLinearScale) {
|
|
14
15
|
return {
|
|
15
16
|
shapes: [],
|
|
@@ -42,7 +43,6 @@ export async function prepareBarYData(args) {
|
|
|
42
43
|
scale: yScale,
|
|
43
44
|
});
|
|
44
45
|
const result = [];
|
|
45
|
-
const baseRangeValue = xLinearScale.range()[0];
|
|
46
46
|
Object.entries(groupedData).forEach(([yValue, val]) => {
|
|
47
47
|
const stacks = Object.values(val);
|
|
48
48
|
const currentBarHeight = barSize * stacks.length + barGap * (stacks.length - 1);
|
|
@@ -64,6 +64,9 @@ export async function prepareBarYData(args) {
|
|
|
64
64
|
ratio = xLinearScale.range()[1] / sum;
|
|
65
65
|
}
|
|
66
66
|
sortedData.forEach(({ data, series: s }, xValueIndex) => {
|
|
67
|
+
if (data.x === null) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
67
70
|
let center;
|
|
68
71
|
if (yAxis[0].type === 'category') {
|
|
69
72
|
const bandScale = yScale;
|
|
@@ -79,24 +82,40 @@ export async function prepareBarYData(args) {
|
|
|
79
82
|
}
|
|
80
83
|
const y = center - currentBarHeight / 2 + (barSize + barGap) * groupItemIndex;
|
|
81
84
|
const xValue = Number(data.x);
|
|
82
|
-
const isLastStackItem = xValueIndex === sortedData.length - 1;
|
|
83
85
|
const width = Math.abs(xLinearScale(xValue) * ratio - base);
|
|
84
86
|
let shapeWidth = width - (stackItems.length ? stackGap : 0);
|
|
85
87
|
if (shapeWidth < 0) {
|
|
86
88
|
shapeWidth = width;
|
|
87
89
|
}
|
|
88
|
-
if (shapeWidth
|
|
90
|
+
if (shapeWidth < 0) {
|
|
89
91
|
return;
|
|
90
92
|
}
|
|
91
93
|
const itemStackGap = width - shapeWidth;
|
|
94
|
+
const borderWidth = barSize > s.borderWidth * 2 ? s.borderWidth : 0;
|
|
95
|
+
const isFirstInStack = xValueIndex === 0;
|
|
96
|
+
const isLastStackItem = xValueIndex === sortedData.length - 1;
|
|
97
|
+
// Calculate position with border compensation
|
|
98
|
+
// Border extends halfBorder outward from the shape, so we need to adjust position
|
|
99
|
+
let itemX = (xValue > baseRangeValue ? stackSum : stackSum - width) + itemStackGap;
|
|
100
|
+
const halfBorder = borderWidth / 2;
|
|
101
|
+
if (isFirstInStack && xValue > 0) {
|
|
102
|
+
// Positive bar: border extends left, so shift position left by halfBorder
|
|
103
|
+
// to keep the visual left edge at the zero line
|
|
104
|
+
itemX -= halfBorder;
|
|
105
|
+
}
|
|
106
|
+
else if (isFirstInStack && xValue < 0) {
|
|
107
|
+
// Negative bar: border extends right, so shift position right by halfBorder
|
|
108
|
+
// to keep the visual right edge at the zero line
|
|
109
|
+
itemX += halfBorder;
|
|
110
|
+
}
|
|
92
111
|
const item = {
|
|
93
|
-
x:
|
|
112
|
+
x: itemX,
|
|
94
113
|
y: y,
|
|
95
114
|
width: shapeWidth,
|
|
96
115
|
height: barSize,
|
|
97
116
|
color: data.color || s.color,
|
|
98
117
|
borderColor: s.borderColor,
|
|
99
|
-
borderWidth
|
|
118
|
+
borderWidth,
|
|
100
119
|
opacity: get(data, 'opacity', null),
|
|
101
120
|
data,
|
|
102
121
|
series: s,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { HtmlItem, LabelData,
|
|
1
|
+
import type { HtmlItem, LabelData, TooltipDataChunkBarY } from '../../../types';
|
|
2
2
|
import type { PreparedBarYSeries } from '../../useSeries/types';
|
|
3
|
-
export type PreparedBarYData = Omit<
|
|
3
|
+
export type PreparedBarYData = Omit<TooltipDataChunkBarY, 'series'> & {
|
|
4
4
|
x: number;
|
|
5
5
|
y: number;
|
|
6
6
|
width: number;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { getRectBorderPath, getRectPath } from '../utils';
|
|
2
|
+
export function getAdjustedRectPath(d) {
|
|
3
|
+
const borderRadius = d.isLastStackItem
|
|
4
|
+
? Math.min(d.height, d.width / 2, d.series.borderRadius)
|
|
5
|
+
: 0;
|
|
6
|
+
// Fill should match the inner border dimensions to prevent color bleeding
|
|
7
|
+
const halfBorder = d.borderWidth / 2;
|
|
8
|
+
const innerBorderRadius = Math.max(borderRadius - halfBorder, 0);
|
|
9
|
+
// Adjust fill position and size based on border width
|
|
10
|
+
let fillX = d.x;
|
|
11
|
+
let fillY = d.y;
|
|
12
|
+
let fillWidth = d.width;
|
|
13
|
+
let fillHeight = d.height;
|
|
14
|
+
let fillBorderRadiusRight = borderRadius;
|
|
15
|
+
if (d.borderWidth > 0) {
|
|
16
|
+
// Inset fill by halfBorder on all sides
|
|
17
|
+
fillX = d.x + halfBorder;
|
|
18
|
+
fillY = d.y + halfBorder;
|
|
19
|
+
fillWidth = d.width - d.borderWidth;
|
|
20
|
+
fillHeight = d.height - d.borderWidth;
|
|
21
|
+
fillBorderRadiusRight = innerBorderRadius;
|
|
22
|
+
}
|
|
23
|
+
const p = getRectPath({
|
|
24
|
+
x: fillX,
|
|
25
|
+
y: fillY,
|
|
26
|
+
width: fillWidth,
|
|
27
|
+
height: fillHeight,
|
|
28
|
+
borderRadius: [0, fillBorderRadiusRight, fillBorderRadiusRight, 0],
|
|
29
|
+
});
|
|
30
|
+
return p.toString();
|
|
31
|
+
}
|
|
32
|
+
export function getAdjustedRectBorderPath(d) {
|
|
33
|
+
const borderRadius = d.isLastStackItem
|
|
34
|
+
? Math.min(d.height, d.width / 2, d.series.borderRadius)
|
|
35
|
+
: 0;
|
|
36
|
+
return getRectBorderPath({
|
|
37
|
+
x: d.x,
|
|
38
|
+
y: d.y,
|
|
39
|
+
width: d.width,
|
|
40
|
+
height: d.height,
|
|
41
|
+
borderWidth: d.borderWidth,
|
|
42
|
+
borderRadius: [0, borderRadius, borderRadius, 0],
|
|
43
|
+
});
|
|
44
|
+
}
|
|
@@ -8,8 +8,11 @@ export async function prepareHeatmapData({ series, xAxis, xScale, yAxis, yScale,
|
|
|
8
8
|
const xDomainData = getDomainDataXBySeries([series]);
|
|
9
9
|
const bandWidth = getBandSize({ domain: xDomainData, scale: xScale });
|
|
10
10
|
const xAxisCategories = (_b = xAxis.categories) !== null && _b !== void 0 ? _b : [];
|
|
11
|
-
const heatmapItems = series.data.
|
|
11
|
+
const heatmapItems = series.data.reduce((items, d) => {
|
|
12
12
|
var _a, _b, _c;
|
|
13
|
+
if (d.value === null) {
|
|
14
|
+
return items;
|
|
15
|
+
}
|
|
13
16
|
let x = 0;
|
|
14
17
|
if (isBandScale(xScale)) {
|
|
15
18
|
x = (_a = xScale(xAxisCategories[d.x])) !== null && _a !== void 0 ? _a : 0;
|
|
@@ -36,8 +39,9 @@ export async function prepareHeatmapData({ series, xAxis, xScale, yAxis, yScale,
|
|
|
36
39
|
borderWidth: series.borderWidth,
|
|
37
40
|
data: d,
|
|
38
41
|
};
|
|
39
|
-
|
|
40
|
-
|
|
42
|
+
items.push(item);
|
|
43
|
+
return items;
|
|
44
|
+
}, []);
|
|
41
45
|
const svgDataLabels = [];
|
|
42
46
|
const htmlDataLabels = [];
|
|
43
47
|
if (series.dataLabels.enabled) {
|
|
@@ -21,6 +21,7 @@ export const LineSeriesShapes = (args) => {
|
|
|
21
21
|
const hoverOptions = get(seriesOptions, 'line.states.hover');
|
|
22
22
|
const inactiveOptions = get(seriesOptions, 'line.states.inactive');
|
|
23
23
|
const line = lineGenerator()
|
|
24
|
+
.defined((d) => d.y !== null && d.x !== null)
|
|
24
25
|
.x((d) => d.x)
|
|
25
26
|
.y((d) => d.y);
|
|
26
27
|
plotSvgElement.selectAll('*').remove();
|
|
@@ -53,33 +53,58 @@ export const prepareLineData = async (args) => {
|
|
|
53
53
|
if (!seriesYScale) {
|
|
54
54
|
continue;
|
|
55
55
|
}
|
|
56
|
-
const points = s.data.map((d) =>
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
56
|
+
const points = s.data.map((d) => {
|
|
57
|
+
const yValue = getYValue({
|
|
58
|
+
point: d,
|
|
59
|
+
points: s.data,
|
|
60
|
+
yAxis: seriesYAxis,
|
|
61
|
+
yScale: seriesYScale,
|
|
62
|
+
});
|
|
63
|
+
return {
|
|
64
|
+
x: getXValue({ point: d, points: s.data, xAxis, xScale }),
|
|
65
|
+
y: yValue === null ? null : yAxisTop + yValue,
|
|
66
|
+
active: true,
|
|
67
|
+
data: d,
|
|
68
|
+
series: s,
|
|
69
|
+
};
|
|
70
|
+
});
|
|
64
71
|
const htmlElements = [];
|
|
65
72
|
let labels = [];
|
|
66
73
|
if (s.dataLabels.enabled) {
|
|
67
74
|
if (s.dataLabels.html) {
|
|
68
|
-
const list = await Promise.all(points.
|
|
75
|
+
const list = await Promise.all(points.reduce((result, p) => {
|
|
76
|
+
if (p.y === null) {
|
|
77
|
+
return result;
|
|
78
|
+
}
|
|
79
|
+
result.push(getHtmlLabel(p, s, xMax));
|
|
80
|
+
return result;
|
|
81
|
+
}, []));
|
|
69
82
|
htmlElements.push(...list);
|
|
70
83
|
}
|
|
71
84
|
else {
|
|
72
|
-
labels = await Promise.all(points.
|
|
85
|
+
labels = await Promise.all(points.reduce((result, p) => {
|
|
86
|
+
if (p.y === null) {
|
|
87
|
+
return result;
|
|
88
|
+
}
|
|
89
|
+
result.push(getLabelData(p, s, xMax));
|
|
90
|
+
return result;
|
|
91
|
+
}, []));
|
|
73
92
|
}
|
|
74
93
|
}
|
|
75
94
|
let markers = [];
|
|
76
95
|
if (s.marker.states.normal.enabled || s.marker.states.hover.enabled) {
|
|
77
|
-
markers = points.
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
96
|
+
markers = points.reduce((result, p) => {
|
|
97
|
+
if (p.y === null || p.x === null) {
|
|
98
|
+
return result;
|
|
99
|
+
}
|
|
100
|
+
result.push({
|
|
101
|
+
point: p,
|
|
102
|
+
active: true,
|
|
103
|
+
hovered: false,
|
|
104
|
+
clipped: isOutsideBounds(p.x, p.y),
|
|
105
|
+
});
|
|
106
|
+
return result;
|
|
107
|
+
}, []);
|
|
83
108
|
}
|
|
84
109
|
const result = {
|
|
85
110
|
points,
|
|
@@ -2,14 +2,18 @@ import type { DashStyle, LineCap } from '../../../constants';
|
|
|
2
2
|
import type { HtmlItem, LabelData, LineSeriesData } from '../../../types';
|
|
3
3
|
import type { PreparedLineSeries } from '../../useSeries/types';
|
|
4
4
|
export type PointData = {
|
|
5
|
-
x: number;
|
|
6
|
-
y: number;
|
|
5
|
+
x: number | null;
|
|
6
|
+
y: number | null;
|
|
7
7
|
data: LineSeriesData;
|
|
8
8
|
series: PreparedLineSeries;
|
|
9
9
|
color?: string;
|
|
10
10
|
};
|
|
11
|
+
export type MarkerPointData = PointData & {
|
|
12
|
+
y: number;
|
|
13
|
+
x: number;
|
|
14
|
+
};
|
|
11
15
|
export type MarkerData = {
|
|
12
|
-
point:
|
|
16
|
+
point: MarkerPointData;
|
|
13
17
|
active: boolean;
|
|
14
18
|
hovered: boolean;
|
|
15
19
|
clipped: boolean;
|