@gravity-ui/chartkit 5.4.0 → 5.6.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/constants/widget-data.d.ts +1 -0
- package/build/constants/widget-data.js +1 -0
- package/build/i18n/keysets/en.json +2 -1
- package/build/i18n/keysets/ru.json +2 -4
- package/build/plugins/d3/examples/area/TwoYAxis.d.ts +2 -0
- package/build/plugins/d3/examples/area/TwoYAxis.js +58 -0
- package/build/plugins/d3/examples/bar-x/TwoYAxis.d.ts +2 -0
- package/build/plugins/d3/examples/bar-x/TwoYAxis.js +58 -0
- package/build/plugins/d3/examples/line/TwoYAxis.d.ts +2 -0
- package/build/plugins/d3/examples/line/TwoYAxis.js +58 -0
- package/build/plugins/d3/examples/mars-weather.d.ts +13 -0
- package/build/plugins/d3/examples/mars-weather.js +1203 -0
- package/build/plugins/d3/examples/scatter/TwoYAxis.d.ts +2 -0
- package/build/plugins/d3/examples/scatter/TwoYAxis.js +58 -0
- package/build/plugins/d3/renderer/components/AxisY.d.ts +1 -1
- package/build/plugins/d3/renderer/components/AxisY.js +112 -79
- package/build/plugins/d3/renderer/components/Chart.js +4 -3
- package/build/plugins/d3/renderer/components/Tooltip/DefaultContent.js +19 -2
- package/build/plugins/d3/renderer/constants/defaults/series-options.d.ts +7 -1
- package/build/plugins/d3/renderer/constants/defaults/series-options.js +14 -0
- package/build/plugins/d3/renderer/hooks/useAxisScales/index.d.ts +1 -1
- package/build/plugins/d3/renderer/hooks/useAxisScales/index.js +8 -1
- package/build/plugins/d3/renderer/hooks/useChartDimensions/utils.d.ts +1 -0
- package/build/plugins/d3/renderer/hooks/useChartDimensions/utils.js +11 -10
- package/build/plugins/d3/renderer/hooks/useChartOptions/types.d.ts +1 -0
- package/build/plugins/d3/renderer/hooks/useChartOptions/x-axis.js +1 -0
- package/build/plugins/d3/renderer/hooks/useChartOptions/y-axis.js +69 -53
- package/build/plugins/d3/renderer/hooks/useSeries/prepare-area.d.ts +1 -1
- package/build/plugins/d3/renderer/hooks/useSeries/prepare-area.js +1 -0
- package/build/plugins/d3/renderer/hooks/useSeries/prepare-bar-x.js +1 -0
- package/build/plugins/d3/renderer/hooks/useSeries/prepare-legend.js +3 -3
- package/build/plugins/d3/renderer/hooks/useSeries/prepare-line.d.ts +1 -1
- package/build/plugins/d3/renderer/hooks/useSeries/prepare-line.js +1 -0
- package/build/plugins/d3/renderer/hooks/useSeries/prepare-scatter.js +1 -0
- package/build/plugins/d3/renderer/hooks/useSeries/prepare-waterfall.d.ts +10 -0
- package/build/plugins/d3/renderer/hooks/useSeries/prepare-waterfall.js +36 -0
- package/build/plugins/d3/renderer/hooks/useSeries/prepareSeries.js +8 -0
- package/build/plugins/d3/renderer/hooks/useSeries/types.d.ts +18 -2
- package/build/plugins/d3/renderer/hooks/useShapes/area/prepare-data.d.ts +2 -1
- package/build/plugins/d3/renderer/hooks/useShapes/area/prepare-data.js +7 -6
- package/build/plugins/d3/renderer/hooks/useShapes/bar-x/prepare-data.d.ts +2 -1
- package/build/plugins/d3/renderer/hooks/useShapes/bar-x/prepare-data.js +4 -4
- package/build/plugins/d3/renderer/hooks/useShapes/bar-y/prepare-data.d.ts +1 -1
- package/build/plugins/d3/renderer/hooks/useShapes/bar-y/prepare-data.js +1 -1
- package/build/plugins/d3/renderer/hooks/useShapes/index.d.ts +3 -2
- package/build/plugins/d3/renderer/hooks/useShapes/index.js +19 -1
- package/build/plugins/d3/renderer/hooks/useShapes/line/prepare-data.d.ts +1 -1
- package/build/plugins/d3/renderer/hooks/useShapes/line/prepare-data.js +2 -1
- package/build/plugins/d3/renderer/hooks/useShapes/scatter/prepare-data.d.ts +2 -2
- package/build/plugins/d3/renderer/hooks/useShapes/scatter/prepare-data.js +5 -2
- package/build/plugins/d3/renderer/hooks/useShapes/styles.css +4 -0
- package/build/plugins/d3/renderer/hooks/useShapes/waterfall/index.d.ts +12 -0
- package/build/plugins/d3/renderer/hooks/useShapes/waterfall/index.js +125 -0
- package/build/plugins/d3/renderer/hooks/useShapes/waterfall/prepare-data.d.ts +12 -0
- package/build/plugins/d3/renderer/hooks/useShapes/waterfall/prepare-data.js +132 -0
- package/build/plugins/d3/renderer/hooks/useShapes/waterfall/types.d.ts +14 -0
- package/build/plugins/d3/renderer/hooks/useShapes/waterfall/types.js +1 -0
- package/build/plugins/d3/renderer/utils/get-closest-data.js +16 -5
- package/build/plugins/d3/renderer/utils/index.d.ts +1 -0
- package/build/plugins/d3/renderer/utils/index.js +11 -0
- package/build/plugins/d3/renderer/utils/series/index.d.ts +1 -0
- package/build/plugins/d3/renderer/utils/series/index.js +1 -0
- package/build/plugins/d3/renderer/utils/series/waterfall.d.ts +4 -0
- package/build/plugins/d3/renderer/utils/series/waterfall.js +25 -0
- package/build/plugins/d3/renderer/utils/text.js +1 -1
- package/build/plugins/d3/renderer/validation/index.js +13 -4
- package/build/plugins/d3/utils/pie-center-text.js +1 -1
- package/build/plugins/highcharts/renderer/components/HighchartsComponent.d.ts +5 -0
- package/build/plugins/highcharts/renderer/helpers/config/config.d.ts +5 -0
- package/build/plugins/highcharts/renderer/helpers/config/config.js +8 -0
- package/build/plugins/highcharts/renderer/helpers/config/options.js +1 -1
- package/build/plugins/highcharts/renderer/helpers/graph.d.ts +5 -0
- package/build/types/widget-data/area.d.ts +2 -0
- package/build/types/widget-data/bar-x.d.ts +2 -0
- package/build/types/widget-data/index.d.ts +1 -0
- package/build/types/widget-data/index.js +1 -0
- package/build/types/widget-data/line.d.ts +2 -0
- package/build/types/widget-data/scatter.d.ts +2 -0
- package/build/types/widget-data/series.d.ts +21 -2
- package/build/types/widget-data/tooltip.d.ts +6 -1
- package/build/types/widget-data/waterfall.d.ts +39 -0
- package/build/types/widget-data/waterfall.js +1 -0
- package/package.json +2 -1
|
@@ -5,10 +5,10 @@ import merge from 'lodash/merge';
|
|
|
5
5
|
import { legendDefaults } from '../../constants';
|
|
6
6
|
import { getHorisontalSvgTextHeight } from '../../utils';
|
|
7
7
|
import { getBoundsWidth } from '../useChartDimensions';
|
|
8
|
-
import {
|
|
8
|
+
import { getYAxisWidth } from '../useChartDimensions/utils';
|
|
9
9
|
export const getPreparedLegend = (args) => {
|
|
10
10
|
const { legend, series } = args;
|
|
11
|
-
const enabled = typeof (legend === null || legend === void 0 ? void 0 : legend.enabled) === 'boolean' ? legend === null || legend === void 0 ? void 0 : legend.enabled : series.length > 1;
|
|
11
|
+
const enabled = Boolean(typeof (legend === null || legend === void 0 ? void 0 : legend.enabled) === 'boolean' ? legend === null || legend === void 0 ? void 0 : legend.enabled : series.length > 1);
|
|
12
12
|
const defaultItemStyle = clone(legendDefaults.itemStyle);
|
|
13
13
|
const itemStyle = get(legend, 'itemStyle');
|
|
14
14
|
const computedItemStyle = merge(defaultItemStyle, itemStyle);
|
|
@@ -90,7 +90,7 @@ export const getLegendComponents = (args) => {
|
|
|
90
90
|
preparedLegend.height = legendHeight;
|
|
91
91
|
const top = chartHeight - chartMargin.bottom - preparedLegend.height;
|
|
92
92
|
const offset = {
|
|
93
|
-
left: chartMargin.left +
|
|
93
|
+
left: chartMargin.left + getYAxisWidth(preparedYAxis[0]),
|
|
94
94
|
top,
|
|
95
95
|
};
|
|
96
96
|
return { legendConfig: { offset, pagination }, legendItems: items };
|
|
@@ -8,9 +8,9 @@ export declare const DEFAULT_DASH_STYLE = DashStyle.Solid;
|
|
|
8
8
|
export declare const DEFAULT_MARKER: {
|
|
9
9
|
enabled: boolean;
|
|
10
10
|
symbol: "circle" | "diamond" | "square" | "triangle" | "triangle-down";
|
|
11
|
-
radius: number;
|
|
12
11
|
borderColor: string;
|
|
13
12
|
borderWidth: number;
|
|
13
|
+
radius: number;
|
|
14
14
|
};
|
|
15
15
|
type PrepareLineSeriesArgs = {
|
|
16
16
|
colorScale: ScaleOrdinal<string, string>;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ScaleOrdinal } from 'd3';
|
|
2
|
+
import type { WaterfallSeries } from '../../../../../types';
|
|
3
|
+
import type { PreparedLegend, PreparedSeries } from './types';
|
|
4
|
+
type PrepareWaterfallSeriesArgs = {
|
|
5
|
+
colorScale: ScaleOrdinal<string, string>;
|
|
6
|
+
series: WaterfallSeries[];
|
|
7
|
+
legend: PreparedLegend;
|
|
8
|
+
};
|
|
9
|
+
export declare function prepareWaterfallSeries(args: PrepareWaterfallSeriesArgs): PreparedSeries[];
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import get from 'lodash/get';
|
|
2
|
+
import { getRandomCKId } from '../../../../../utils';
|
|
3
|
+
import { DEFAULT_PALETTE } from '../../constants';
|
|
4
|
+
import { DEFAULT_DATALABELS_PADDING, DEFAULT_DATALABELS_STYLE } from './constants';
|
|
5
|
+
import { prepareLegendSymbol } from './utils';
|
|
6
|
+
export function prepareWaterfallSeries(args) {
|
|
7
|
+
const { colorScale, series: seriesList, legend } = args;
|
|
8
|
+
const [, negativeColor, positiveColor] = DEFAULT_PALETTE;
|
|
9
|
+
return seriesList.map((series) => {
|
|
10
|
+
var _a, _b, _c;
|
|
11
|
+
const name = series.name || '';
|
|
12
|
+
const color = series.color || colorScale(name);
|
|
13
|
+
const prepared = {
|
|
14
|
+
type: series.type,
|
|
15
|
+
color,
|
|
16
|
+
positiveColor: positiveColor,
|
|
17
|
+
negativeColor: negativeColor,
|
|
18
|
+
name,
|
|
19
|
+
id: getRandomCKId(),
|
|
20
|
+
visible: get(series, 'visible', true),
|
|
21
|
+
legend: {
|
|
22
|
+
enabled: get(series, 'legend.enabled', legend.enabled),
|
|
23
|
+
symbol: prepareLegendSymbol(series),
|
|
24
|
+
},
|
|
25
|
+
data: series.data,
|
|
26
|
+
dataLabels: {
|
|
27
|
+
enabled: ((_a = series.dataLabels) === null || _a === void 0 ? void 0 : _a.enabled) || false,
|
|
28
|
+
style: Object.assign({}, DEFAULT_DATALABELS_STYLE, (_b = series.dataLabels) === null || _b === void 0 ? void 0 : _b.style),
|
|
29
|
+
allowOverlap: ((_c = series.dataLabels) === null || _c === void 0 ? void 0 : _c.allowOverlap) || false,
|
|
30
|
+
padding: get(series, 'dataLabels.padding', DEFAULT_DATALABELS_PADDING),
|
|
31
|
+
},
|
|
32
|
+
cursor: get(series, 'cursor', null),
|
|
33
|
+
};
|
|
34
|
+
return prepared;
|
|
35
|
+
}, []);
|
|
36
|
+
}
|
|
@@ -6,6 +6,7 @@ import { prepareLineSeries } from './prepare-line';
|
|
|
6
6
|
import { preparePieSeries } from './prepare-pie';
|
|
7
7
|
import { prepareScatterSeries } from './prepare-scatter';
|
|
8
8
|
import { prepareTreemap } from './prepare-treemap';
|
|
9
|
+
import { prepareWaterfallSeries } from './prepare-waterfall';
|
|
9
10
|
export function prepareSeries(args) {
|
|
10
11
|
const { type, series, seriesOptions, legend, colorScale } = args;
|
|
11
12
|
switch (type) {
|
|
@@ -48,6 +49,13 @@ export function prepareSeries(args) {
|
|
|
48
49
|
colorScale,
|
|
49
50
|
});
|
|
50
51
|
}
|
|
52
|
+
case 'waterfall': {
|
|
53
|
+
return prepareWaterfallSeries({
|
|
54
|
+
series: series,
|
|
55
|
+
legend,
|
|
56
|
+
colorScale,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
51
59
|
default: {
|
|
52
60
|
throw new ChartKitError({
|
|
53
61
|
message: `Series type "${type}" does not support data preparation for series that do not support the presence of axes`,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { DashStyle, LayoutAlgorithm, LineCap, SymbolType } from '../../../../../constants';
|
|
2
|
-
import { AreaSeries, AreaSeriesData, BarXSeries, BarXSeriesData, BarYSeries, BarYSeriesData, BaseTextStyle, ChartKitWidgetLegend, ConnectorCurve, ConnectorShape, LineSeries, LineSeriesData, PathLegendSymbolOptions, PieSeries, PieSeriesData, RectLegendSymbolOptions, ScatterSeries, ScatterSeriesData, SymbolLegendSymbolOptions, TreemapSeries, TreemapSeriesData } from '../../../../../types';
|
|
2
|
+
import type { AreaSeries, AreaSeriesData, BarXSeries, BarXSeriesData, BarYSeries, BarYSeriesData, BaseTextStyle, ChartKitWidgetLegend, ConnectorCurve, ConnectorShape, LineSeries, LineSeriesData, PathLegendSymbolOptions, PieSeries, PieSeriesData, RectLegendSymbolOptions, ScatterSeries, ScatterSeriesData, SymbolLegendSymbolOptions, TreemapSeries, TreemapSeriesData, WaterfallSeries, WaterfallSeriesData } from '../../../../../types';
|
|
3
3
|
import type { SeriesOptionsDefaults } from '../../constants';
|
|
4
4
|
export type RectLegendSymbol = {
|
|
5
5
|
shape: 'rect';
|
|
@@ -76,6 +76,7 @@ export type PreparedScatterSeries = {
|
|
|
76
76
|
};
|
|
77
77
|
};
|
|
78
78
|
};
|
|
79
|
+
yAxis: number;
|
|
79
80
|
} & BasePreparedSeries;
|
|
80
81
|
export type PreparedBarXSeries = {
|
|
81
82
|
type: BarXSeries['type'];
|
|
@@ -89,6 +90,7 @@ export type PreparedBarXSeries = {
|
|
|
89
90
|
allowOverlap: boolean;
|
|
90
91
|
padding: number;
|
|
91
92
|
};
|
|
93
|
+
yAxis: number;
|
|
92
94
|
} & BasePreparedSeries;
|
|
93
95
|
export type PreparedBarYSeries = {
|
|
94
96
|
type: BarYSeries['type'];
|
|
@@ -164,6 +166,7 @@ export type PreparedLineSeries = {
|
|
|
164
166
|
dashStyle: DashStyle;
|
|
165
167
|
linecap: LineCap;
|
|
166
168
|
opacity: number | null;
|
|
169
|
+
yAxis: number;
|
|
167
170
|
} & BasePreparedSeries;
|
|
168
171
|
export type PreparedAreaSeries = {
|
|
169
172
|
type: AreaSeries['type'];
|
|
@@ -196,6 +199,7 @@ export type PreparedAreaSeries = {
|
|
|
196
199
|
};
|
|
197
200
|
};
|
|
198
201
|
};
|
|
202
|
+
yAxis: number;
|
|
199
203
|
} & BasePreparedSeries;
|
|
200
204
|
export type PreparedTreemapSeries = {
|
|
201
205
|
type: TreemapSeries['type'];
|
|
@@ -208,7 +212,19 @@ export type PreparedTreemapSeries = {
|
|
|
208
212
|
};
|
|
209
213
|
layoutAlgorithm: `${LayoutAlgorithm}`;
|
|
210
214
|
} & BasePreparedSeries & Omit<TreemapSeries, keyof BasePreparedSeries>;
|
|
211
|
-
export type
|
|
215
|
+
export type PreparedWaterfallSeries = {
|
|
216
|
+
type: WaterfallSeries['type'];
|
|
217
|
+
data: WaterfallSeriesData[];
|
|
218
|
+
dataLabels: {
|
|
219
|
+
enabled: boolean;
|
|
220
|
+
style: BaseTextStyle;
|
|
221
|
+
allowOverlap: boolean;
|
|
222
|
+
padding: number;
|
|
223
|
+
};
|
|
224
|
+
positiveColor: string;
|
|
225
|
+
negativeColor: string;
|
|
226
|
+
} & BasePreparedSeries;
|
|
227
|
+
export type PreparedSeries = PreparedScatterSeries | PreparedBarXSeries | PreparedBarYSeries | PreparedPieSeries | PreparedLineSeries | PreparedAreaSeries | PreparedTreemapSeries | PreparedWaterfallSeries;
|
|
212
228
|
export type PreparedSeriesOptions = SeriesOptionsDefaults;
|
|
213
229
|
export type StackedSeries = BarXSeries | AreaSeries | BarYSeries;
|
|
214
230
|
export {};
|
|
@@ -49,13 +49,9 @@ function getXValues(series, xAxis, xScale) {
|
|
|
49
49
|
return sort(Array.from(xValues), ([_x, xValue]) => xValue);
|
|
50
50
|
}
|
|
51
51
|
export const prepareAreaData = (args) => {
|
|
52
|
-
const { series, xAxis, xScale, yScale } = args;
|
|
53
|
-
const yLinearScale = yScale;
|
|
54
|
-
const plotHeight = yLinearScale(yLinearScale.domain()[0]);
|
|
55
|
-
const yAxis = args.yAxis[0];
|
|
52
|
+
const { series, xAxis, xScale, yAxis, yScale, boundsHeight: plotHeight } = args;
|
|
56
53
|
const [_xMin, xRangeMax] = xScale.range();
|
|
57
54
|
const xMax = xRangeMax / (1 - xAxis.maxPadding);
|
|
58
|
-
const [yMin, _yMax] = yScale.range();
|
|
59
55
|
return Array.from(group(series, (s) => s.stackId)).reduce((result, [_stackId, seriesStack]) => {
|
|
60
56
|
const xValues = getXValues(seriesStack, xAxis, xScale);
|
|
61
57
|
const accumulatedYValues = new Map();
|
|
@@ -63,6 +59,10 @@ export const prepareAreaData = (args) => {
|
|
|
63
59
|
accumulatedYValues.set(key, 0);
|
|
64
60
|
});
|
|
65
61
|
const seriesStackData = seriesStack.reduce((acc, s) => {
|
|
62
|
+
const yAxisIndex = s.yAxis;
|
|
63
|
+
const seriesYAxis = yAxis[yAxisIndex];
|
|
64
|
+
const seriesYScale = yScale[yAxisIndex];
|
|
65
|
+
const [yMin, _yMax] = seriesYScale.range();
|
|
66
66
|
const seriesData = s.data.reduce((m, d) => {
|
|
67
67
|
return m.set(String(d.x), d);
|
|
68
68
|
}, new Map());
|
|
@@ -74,7 +74,8 @@ export const prepareAreaData = (args) => {
|
|
|
74
74
|
// FIXME: think about how to break the series into separate areas(null Y values)
|
|
75
75
|
y: 0,
|
|
76
76
|
};
|
|
77
|
-
const yValue = getYValue({ point: d, yAxis, yScale }) -
|
|
77
|
+
const yValue = getYValue({ point: d, yAxis: seriesYAxis, yScale: seriesYScale }) -
|
|
78
|
+
accumulatedYValue;
|
|
78
79
|
accumulatedYValues.set(x, yMin - yValue);
|
|
79
80
|
pointsAcc.push({
|
|
80
81
|
y0: yMin - accumulatedYValue,
|
|
@@ -24,9 +24,7 @@ function getLabelData(d) {
|
|
|
24
24
|
};
|
|
25
25
|
}
|
|
26
26
|
export const prepareBarXData = (args) => {
|
|
27
|
-
const { series, seriesOptions, xAxis, xScale, yScale } = args;
|
|
28
|
-
const yLinearScale = yScale;
|
|
29
|
-
const plotHeight = yLinearScale(yLinearScale.domain()[0]);
|
|
27
|
+
const { series, seriesOptions, xAxis, xScale, yScale, boundsHeight: plotHeight } = args;
|
|
30
28
|
const categories = get(xAxis, 'categories', []);
|
|
31
29
|
const barMaxWidth = get(seriesOptions, 'bar-x.barMaxWidth');
|
|
32
30
|
const barPadding = get(seriesOptions, 'bar-x.barPadding');
|
|
@@ -100,6 +98,8 @@ export const prepareBarXData = (args) => {
|
|
|
100
98
|
? sort(yValues, (a, b) => comparator(get(a, sortKey), get(b, sortKey)))
|
|
101
99
|
: yValues;
|
|
102
100
|
sortedData.forEach((yValue) => {
|
|
101
|
+
const yAxisIndex = yValue.series.yAxis;
|
|
102
|
+
const seriesYScale = yScale[yAxisIndex];
|
|
103
103
|
let xCenter;
|
|
104
104
|
if (xAxis.type === 'category') {
|
|
105
105
|
const xBandScale = xScale;
|
|
@@ -110,7 +110,7 @@ export const prepareBarXData = (args) => {
|
|
|
110
110
|
xCenter = xLinearScale(Number(xValue));
|
|
111
111
|
}
|
|
112
112
|
const x = xCenter - currentGroupWidth / 2 + (rectWidth + rectGap) * groupItemIndex;
|
|
113
|
-
const y =
|
|
113
|
+
const y = seriesYScale(yValue.data.y);
|
|
114
114
|
const height = plotHeight - y;
|
|
115
115
|
const barData = {
|
|
116
116
|
x,
|
|
@@ -48,7 +48,7 @@ function getBandWidth(series, yAxis, yScale) {
|
|
|
48
48
|
return bandWidth;
|
|
49
49
|
}
|
|
50
50
|
export const prepareBarYData = (args) => {
|
|
51
|
-
const { series, seriesOptions, yAxis, xScale, yScale } = args;
|
|
51
|
+
const { series, seriesOptions, yAxis, xScale, yScale: [yScale], } = args;
|
|
52
52
|
const xLinearScale = xScale;
|
|
53
53
|
const plotWidth = xLinearScale(xLinearScale.domain()[1]);
|
|
54
54
|
const barMaxWidth = get(seriesOptions, 'bar-y.barMaxWidth');
|
|
@@ -11,8 +11,9 @@ import type { PreparedPieData } from './pie/types';
|
|
|
11
11
|
import type { PreparedScatterData } from './scatter/types';
|
|
12
12
|
export type { PreparedBarXData } from './bar-x';
|
|
13
13
|
export type { PreparedScatterData } from './scatter/types';
|
|
14
|
+
import { PreparedWaterfallData } from './waterfall';
|
|
14
15
|
import './styles.css';
|
|
15
|
-
export type ShapeData = PreparedBarXData | PreparedBarYData | PreparedScatterData | PreparedLineData | PreparedPieData | PreparedAreaData;
|
|
16
|
+
export type ShapeData = PreparedBarXData | PreparedBarYData | PreparedScatterData | PreparedLineData | PreparedPieData | PreparedAreaData | PreparedWaterfallData;
|
|
16
17
|
type Args = {
|
|
17
18
|
boundsWidth: number;
|
|
18
19
|
boundsHeight: number;
|
|
@@ -22,7 +23,7 @@ type Args = {
|
|
|
22
23
|
xAxis: PreparedAxis;
|
|
23
24
|
yAxis: PreparedAxis[];
|
|
24
25
|
xScale?: ChartScale;
|
|
25
|
-
yScale?: ChartScale;
|
|
26
|
+
yScale?: ChartScale[];
|
|
26
27
|
};
|
|
27
28
|
export declare const useShapes: (args: Args) => {
|
|
28
29
|
shapes: React.ReactElement<any, string | React.JSXElementConstructor<any>>[];
|
|
@@ -12,6 +12,7 @@ import { preparePieData } from './pie/prepare-data';
|
|
|
12
12
|
import { ScatterSeriesShape, prepareScatterData } from './scatter';
|
|
13
13
|
import { TreemapSeriesShape } from './treemap';
|
|
14
14
|
import { prepareTreemapData } from './treemap/prepare-data';
|
|
15
|
+
import { WaterfallSeriesShapes, prepareWaterfallData } from './waterfall';
|
|
15
16
|
import './styles.css';
|
|
16
17
|
export const useShapes = (args) => {
|
|
17
18
|
const { boundsWidth, boundsHeight, dispatcher, series, seriesOptions, xAxis, xScale, yAxis, yScale, } = args;
|
|
@@ -31,6 +32,7 @@ export const useShapes = (args) => {
|
|
|
31
32
|
xScale,
|
|
32
33
|
yAxis,
|
|
33
34
|
yScale,
|
|
35
|
+
boundsHeight,
|
|
34
36
|
});
|
|
35
37
|
acc.push(React.createElement(BarXSeriesShapes, { key: "bar-x", dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData }));
|
|
36
38
|
shapesData.push(...preparedData);
|
|
@@ -52,6 +54,21 @@ export const useShapes = (args) => {
|
|
|
52
54
|
}
|
|
53
55
|
break;
|
|
54
56
|
}
|
|
57
|
+
case 'waterfall': {
|
|
58
|
+
if (xScale && yScale) {
|
|
59
|
+
const preparedData = prepareWaterfallData({
|
|
60
|
+
series: chartSeries,
|
|
61
|
+
seriesOptions,
|
|
62
|
+
xAxis,
|
|
63
|
+
xScale,
|
|
64
|
+
yAxis,
|
|
65
|
+
yScale,
|
|
66
|
+
});
|
|
67
|
+
acc.push(React.createElement(WaterfallSeriesShapes, { key: "waterfall", dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData }));
|
|
68
|
+
shapesData.push(...preparedData);
|
|
69
|
+
}
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
55
72
|
case 'line': {
|
|
56
73
|
if (xScale && yScale) {
|
|
57
74
|
const preparedData = prepareLineData({
|
|
@@ -74,6 +91,7 @@ export const useShapes = (args) => {
|
|
|
74
91
|
xScale,
|
|
75
92
|
yAxis,
|
|
76
93
|
yScale,
|
|
94
|
+
boundsHeight,
|
|
77
95
|
});
|
|
78
96
|
acc.push(React.createElement(AreaSeriesShapes, { key: "area", dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData }));
|
|
79
97
|
shapesData.push(...preparedData);
|
|
@@ -86,7 +104,7 @@ export const useShapes = (args) => {
|
|
|
86
104
|
series: chartSeries,
|
|
87
105
|
xAxis,
|
|
88
106
|
xScale,
|
|
89
|
-
yAxis
|
|
107
|
+
yAxis,
|
|
90
108
|
yScale,
|
|
91
109
|
});
|
|
92
110
|
acc.push(React.createElement(ScatterSeriesShape, { key: "scatter", dispatcher: dispatcher, preparedData: preparedData, seriesOptions: seriesOptions }));
|
|
@@ -32,9 +32,10 @@ export const prepareLineData = (args) => {
|
|
|
32
32
|
const [_xMin, xRangeMax] = xScale.range();
|
|
33
33
|
const xMax = xRangeMax / (1 - xAxis.maxPadding);
|
|
34
34
|
return series.reduce((acc, s) => {
|
|
35
|
+
const seriesYScale = yScale[s.yAxis];
|
|
35
36
|
const points = s.data.map((d) => ({
|
|
36
37
|
x: getXValue({ point: d, xAxis, xScale }),
|
|
37
|
-
y: getYValue({ point: d, yAxis, yScale }),
|
|
38
|
+
y: getYValue({ point: d, yAxis, yScale: seriesYScale }),
|
|
38
39
|
active: true,
|
|
39
40
|
data: d,
|
|
40
41
|
series: s,
|
|
@@ -6,7 +6,10 @@ const getFilteredLinearScatterData = (data) => {
|
|
|
6
6
|
export const prepareScatterData = (args) => {
|
|
7
7
|
const { series, xAxis, xScale, yAxis, yScale } = args;
|
|
8
8
|
return series.reduce((acc, s) => {
|
|
9
|
-
const
|
|
9
|
+
const yAxisIndex = get(s, 'yAxis', 0);
|
|
10
|
+
const seriesYAxis = yAxis[yAxisIndex];
|
|
11
|
+
const seriesYScale = yScale[yAxisIndex];
|
|
12
|
+
const filteredData = xAxis.type === 'category' || seriesYAxis.type === 'category'
|
|
10
13
|
? s.data
|
|
11
14
|
: getFilteredLinearScatterData(s.data);
|
|
12
15
|
filteredData.forEach((d) => {
|
|
@@ -15,7 +18,7 @@ export const prepareScatterData = (args) => {
|
|
|
15
18
|
data: d,
|
|
16
19
|
series: s,
|
|
17
20
|
x: getXValue({ point: d, xAxis, xScale }),
|
|
18
|
-
y: getYValue({ point: d, yAxis, yScale }),
|
|
21
|
+
y: getYValue({ point: d, yAxis: seriesYAxis, yScale: seriesYScale }),
|
|
19
22
|
opacity: get(d, 'opacity', null),
|
|
20
23
|
},
|
|
21
24
|
hovered: false,
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { Dispatch } from 'd3';
|
|
3
|
+
import type { PreparedSeriesOptions } from '../../useSeries/types';
|
|
4
|
+
import type { PreparedWaterfallData } from './types';
|
|
5
|
+
export { prepareWaterfallData } from './prepare-data';
|
|
6
|
+
export * from './types';
|
|
7
|
+
type Args = {
|
|
8
|
+
dispatcher: Dispatch<object>;
|
|
9
|
+
preparedData: PreparedWaterfallData[];
|
|
10
|
+
seriesOptions: PreparedSeriesOptions;
|
|
11
|
+
};
|
|
12
|
+
export declare const WaterfallSeriesShapes: (args: Args) => React.JSX.Element;
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { color, line as lineGenerator, select } from 'd3';
|
|
3
|
+
import get from 'lodash/get';
|
|
4
|
+
import { DashStyle } from '../../../../../../constants';
|
|
5
|
+
import { block } from '../../../../../../utils/cn';
|
|
6
|
+
import { filterOverlappingLabels, getWaterfallPointColor } from '../../../utils';
|
|
7
|
+
import { getLineDashArray } from '../utils';
|
|
8
|
+
export { prepareWaterfallData } from './prepare-data';
|
|
9
|
+
export * from './types';
|
|
10
|
+
const b = block('d3-waterfall');
|
|
11
|
+
export const WaterfallSeriesShapes = (args) => {
|
|
12
|
+
const { dispatcher, preparedData, seriesOptions } = args;
|
|
13
|
+
const ref = React.useRef(null);
|
|
14
|
+
const connectorSelector = `.${b('connector')}`;
|
|
15
|
+
React.useEffect(() => {
|
|
16
|
+
var _a;
|
|
17
|
+
if (!ref.current) {
|
|
18
|
+
return () => { };
|
|
19
|
+
}
|
|
20
|
+
const svgElement = select(ref.current);
|
|
21
|
+
const hoverOptions = get(seriesOptions, 'waterfall.states.hover');
|
|
22
|
+
const inactiveOptions = get(seriesOptions, 'waterfall.states.inactive');
|
|
23
|
+
svgElement.selectAll('*').remove();
|
|
24
|
+
const rectSelection = svgElement
|
|
25
|
+
.selectAll('allRects')
|
|
26
|
+
.data(preparedData)
|
|
27
|
+
.join('rect')
|
|
28
|
+
.attr('class', b('segment'))
|
|
29
|
+
.attr('x', (d) => d.x)
|
|
30
|
+
.attr('y', (d) => d.y)
|
|
31
|
+
.attr('height', (d) => d.height)
|
|
32
|
+
.attr('width', (d) => d.width)
|
|
33
|
+
.attr('fill', (d) => getWaterfallPointColor(d.data, d.series))
|
|
34
|
+
.attr('opacity', (d) => d.opacity)
|
|
35
|
+
.attr('cursor', (d) => d.series.cursor);
|
|
36
|
+
let dataLabels = preparedData.map((d) => d.label).filter(Boolean);
|
|
37
|
+
if (!((_a = preparedData[0]) === null || _a === void 0 ? void 0 : _a.series.dataLabels.allowOverlap)) {
|
|
38
|
+
dataLabels = filterOverlappingLabels(dataLabels);
|
|
39
|
+
}
|
|
40
|
+
const labelSelection = svgElement
|
|
41
|
+
.selectAll('text')
|
|
42
|
+
.data(dataLabels)
|
|
43
|
+
.join('text')
|
|
44
|
+
.text((d) => d.text)
|
|
45
|
+
.attr('class', b('label'))
|
|
46
|
+
.attr('x', (d) => d.x)
|
|
47
|
+
.attr('y', (d) => d.y)
|
|
48
|
+
.attr('text-anchor', (d) => d.textAnchor)
|
|
49
|
+
.style('font-size', (d) => d.style.fontSize)
|
|
50
|
+
.style('font-weight', (d) => d.style.fontWeight || null)
|
|
51
|
+
.style('fill', (d) => d.style.fontColor || null);
|
|
52
|
+
// Add the connector line between bars
|
|
53
|
+
svgElement
|
|
54
|
+
.selectAll(connectorSelector)
|
|
55
|
+
.data(preparedData)
|
|
56
|
+
.join('path')
|
|
57
|
+
.attr('class', b('connector'))
|
|
58
|
+
.attr('d', (d, index) => {
|
|
59
|
+
const line = lineGenerator();
|
|
60
|
+
const prev = preparedData[index - 1];
|
|
61
|
+
if (!prev) {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
const points = [];
|
|
65
|
+
if (Number(prev.data.y) > 0) {
|
|
66
|
+
points.push([prev.x, prev.y]);
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
points.push([prev.x, prev.y + prev.height]);
|
|
70
|
+
}
|
|
71
|
+
if (Number(d.data.y) > 0 && !d.data.total) {
|
|
72
|
+
points.push([d.x + d.width, d.y + d.height]);
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
points.push([d.x + d.width, d.y]);
|
|
76
|
+
}
|
|
77
|
+
return line(points);
|
|
78
|
+
})
|
|
79
|
+
.attr('stroke-width', 1)
|
|
80
|
+
.attr('stroke-dasharray', () => getLineDashArray(DashStyle.Dash, 1));
|
|
81
|
+
dispatcher.on('hover-shape.waterfall', (data) => {
|
|
82
|
+
const hoverEnabled = hoverOptions === null || hoverOptions === void 0 ? void 0 : hoverOptions.enabled;
|
|
83
|
+
const inactiveEnabled = inactiveOptions === null || inactiveOptions === void 0 ? void 0 : inactiveOptions.enabled;
|
|
84
|
+
if (!data) {
|
|
85
|
+
if (hoverEnabled) {
|
|
86
|
+
rectSelection.attr('fill', (d) => getWaterfallPointColor(d.data, d.series));
|
|
87
|
+
}
|
|
88
|
+
if (inactiveEnabled) {
|
|
89
|
+
rectSelection.attr('opacity', null);
|
|
90
|
+
labelSelection.attr('opacity', null);
|
|
91
|
+
}
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
if (hoverEnabled) {
|
|
95
|
+
const hoveredValues = data.map((d) => d.data.x);
|
|
96
|
+
rectSelection.attr('fill', (d) => {
|
|
97
|
+
var _a;
|
|
98
|
+
const fillColor = getWaterfallPointColor(d.data, d.series);
|
|
99
|
+
if (hoveredValues.includes(d.data.x)) {
|
|
100
|
+
const brightness = hoverOptions === null || hoverOptions === void 0 ? void 0 : hoverOptions.brightness;
|
|
101
|
+
return ((_a = color(fillColor)) === null || _a === void 0 ? void 0 : _a.brighter(brightness).toString()) || fillColor;
|
|
102
|
+
}
|
|
103
|
+
return fillColor;
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
if (inactiveEnabled) {
|
|
107
|
+
const hoveredSeries = data.map((d) => d.series.id);
|
|
108
|
+
rectSelection.attr('opacity', (d) => {
|
|
109
|
+
return hoveredSeries.includes(d.series.id)
|
|
110
|
+
? null
|
|
111
|
+
: (inactiveOptions === null || inactiveOptions === void 0 ? void 0 : inactiveOptions.opacity) || null;
|
|
112
|
+
});
|
|
113
|
+
labelSelection.attr('opacity', (d) => {
|
|
114
|
+
return hoveredSeries.includes(d.series.id)
|
|
115
|
+
? null
|
|
116
|
+
: (inactiveOptions === null || inactiveOptions === void 0 ? void 0 : inactiveOptions.opacity) || null;
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
return () => {
|
|
121
|
+
dispatcher.on('hover-shape.waterfall', null);
|
|
122
|
+
};
|
|
123
|
+
}, [dispatcher, preparedData, seriesOptions]);
|
|
124
|
+
return React.createElement("g", { ref: ref, className: b() });
|
|
125
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ChartScale } from '../../useAxisScales';
|
|
2
|
+
import type { PreparedAxis } from '../../useChartOptions/types';
|
|
3
|
+
import type { PreparedSeriesOptions, PreparedWaterfallSeries } from '../../useSeries/types';
|
|
4
|
+
import type { PreparedWaterfallData } from './types';
|
|
5
|
+
export declare const prepareWaterfallData: (args: {
|
|
6
|
+
series: PreparedWaterfallSeries[];
|
|
7
|
+
seriesOptions: PreparedSeriesOptions;
|
|
8
|
+
xAxis: PreparedAxis;
|
|
9
|
+
xScale: ChartScale;
|
|
10
|
+
yAxis: PreparedAxis[];
|
|
11
|
+
yScale: ChartScale[];
|
|
12
|
+
}) => PreparedWaterfallData[];
|