@gravity-ui/chartkit 5.3.3 → 5.5.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/plugins/d3/renderer/components/Chart.js +28 -8
- package/build/plugins/d3/renderer/components/Tooltip/DefaultContent.js +70 -40
- package/build/plugins/d3/renderer/components/Tooltip/index.d.ts +2 -5
- package/build/plugins/d3/renderer/components/Tooltip/index.js +4 -3
- package/build/plugins/d3/renderer/components/styles.css +14 -0
- 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/useChartOptions/y-axis.js +13 -3
- 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 +14 -2
- package/build/plugins/d3/renderer/hooks/useShapes/area/index.js +13 -10
- package/build/plugins/d3/renderer/hooks/useShapes/index.d.ts +2 -2
- package/build/plugins/d3/renderer/hooks/useShapes/index.js +23 -5
- package/build/plugins/d3/renderer/hooks/useShapes/line/index.js +13 -10
- package/build/plugins/d3/renderer/hooks/useShapes/pie/index.d.ts +0 -1
- package/build/plugins/d3/renderer/hooks/useShapes/pie/index.js +6 -23
- package/build/plugins/d3/renderer/hooks/useShapes/scatter/index.d.ts +0 -1
- package/build/plugins/d3/renderer/hooks/useShapes/scatter/index.js +4 -24
- package/build/plugins/d3/renderer/hooks/useShapes/styles.css +4 -0
- package/build/plugins/d3/renderer/hooks/useShapes/treemap/index.d.ts +0 -1
- package/build/plugins/d3/renderer/hooks/useShapes/treemap/index.js +6 -13
- 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.d.ts +15 -0
- package/build/plugins/d3/renderer/utils/get-closest-data.js +167 -0
- package/build/plugins/d3/renderer/utils/index.d.ts +1 -5
- package/build/plugins/d3/renderer/utils/index.js +11 -7
- 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/highcharts/renderer/components/HighchartsComponent.d.ts +1 -1
- package/build/plugins/highcharts/renderer/helpers/config/config.d.ts +1 -1
- package/build/plugins/highcharts/renderer/helpers/config/config.js +2 -6
- package/build/plugins/highcharts/renderer/helpers/graph.d.ts +1 -1
- package/build/types/widget-data/index.d.ts +1 -0
- package/build/types/widget-data/index.js +1 -0
- package/build/types/widget-data/series.d.ts +21 -2
- package/build/types/widget-data/tooltip.d.ts +8 -1
- package/build/types/widget-data/waterfall.d.ts +39 -0
- package/build/types/widget-data/waterfall.js +1 -0
- package/package.json +1 -1
- package/build/plugins/d3/renderer/components/Tooltip/TooltipTriggerArea.d.ts +0 -12
- package/build/plugins/d3/renderer/components/Tooltip/TooltipTriggerArea.js +0 -153
|
@@ -61,13 +61,12 @@ export const LineSeriesShapes = (args) => {
|
|
|
61
61
|
const hoverEnabled = hoverOptions === null || hoverOptions === void 0 ? void 0 : hoverOptions.enabled;
|
|
62
62
|
const inactiveEnabled = inactiveOptions === null || inactiveOptions === void 0 ? void 0 : inactiveOptions.enabled;
|
|
63
63
|
dispatcher.on('hover-shape.line', (data) => {
|
|
64
|
-
|
|
65
|
-
const
|
|
66
|
-
const
|
|
67
|
-
const selectedSeriesId = (_a = selected === null || selected === void 0 ? void 0 : selected.series) === null || _a === void 0 ? void 0 : _a.id;
|
|
64
|
+
const selected = (data === null || data === void 0 ? void 0 : data.filter((d) => d.series.type === 'line')) || [];
|
|
65
|
+
const selectedDataItems = selected.map((d) => d.data);
|
|
66
|
+
const selectedSeriesIds = selected.map((d) => { var _a; return (_a = d.series) === null || _a === void 0 ? void 0 : _a.id; });
|
|
68
67
|
lineSelection.datum((d, index, list) => {
|
|
69
68
|
const elementSelection = select(list[index]);
|
|
70
|
-
const hovered = Boolean(hoverEnabled && d.id
|
|
69
|
+
const hovered = Boolean(hoverEnabled && selectedSeriesIds.includes(d.id));
|
|
71
70
|
if (d.hovered !== hovered) {
|
|
72
71
|
d.hovered = hovered;
|
|
73
72
|
elementSelection.attr('stroke', (d) => {
|
|
@@ -82,7 +81,9 @@ export const LineSeriesShapes = (args) => {
|
|
|
82
81
|
return setActiveState({
|
|
83
82
|
element: list[index],
|
|
84
83
|
state: inactiveOptions,
|
|
85
|
-
active: Boolean(!inactiveEnabled ||
|
|
84
|
+
active: Boolean(!inactiveEnabled ||
|
|
85
|
+
!selectedSeriesIds.length ||
|
|
86
|
+
selectedSeriesIds.includes(d.id)),
|
|
86
87
|
datum: d,
|
|
87
88
|
});
|
|
88
89
|
});
|
|
@@ -90,13 +91,15 @@ export const LineSeriesShapes = (args) => {
|
|
|
90
91
|
return setActiveState({
|
|
91
92
|
element: list[index],
|
|
92
93
|
state: inactiveOptions,
|
|
93
|
-
active: Boolean(!inactiveEnabled ||
|
|
94
|
+
active: Boolean(!inactiveEnabled ||
|
|
95
|
+
!selectedSeriesIds.length ||
|
|
96
|
+
selectedSeriesIds.includes(d.series.id)),
|
|
94
97
|
datum: d,
|
|
95
98
|
});
|
|
96
99
|
});
|
|
97
100
|
markerSelection.datum((d, index, list) => {
|
|
98
101
|
const elementSelection = select(list[index]);
|
|
99
|
-
const hovered = Boolean(hoverEnabled && d.point.data
|
|
102
|
+
const hovered = Boolean(hoverEnabled && selectedDataItems.includes(d.point.data));
|
|
100
103
|
if (d.hovered !== hovered) {
|
|
101
104
|
d.hovered = hovered;
|
|
102
105
|
elementSelection.attr('visibility', getMarkerVisibility(d));
|
|
@@ -105,8 +108,8 @@ export const LineSeriesShapes = (args) => {
|
|
|
105
108
|
}
|
|
106
109
|
if (d.point.series.marker.states.normal.enabled) {
|
|
107
110
|
const isActive = Boolean(!inactiveEnabled ||
|
|
108
|
-
!
|
|
109
|
-
|
|
111
|
+
!selectedSeriesIds.length ||
|
|
112
|
+
selectedSeriesIds.includes(d.point.series.id));
|
|
110
113
|
setActiveState({
|
|
111
114
|
element: list[index],
|
|
112
115
|
state: inactiveOptions,
|
|
@@ -6,7 +6,6 @@ type PreparePieSeriesArgs = {
|
|
|
6
6
|
dispatcher: Dispatch<object>;
|
|
7
7
|
preparedData: PreparedPieData[];
|
|
8
8
|
seriesOptions: PreparedSeriesOptions;
|
|
9
|
-
svgContainer: SVGSVGElement | null;
|
|
10
9
|
};
|
|
11
10
|
export declare function getHaloVisibility(d: PieArcDatum<SegmentData>): "" | "hidden";
|
|
12
11
|
export declare function PieSeriesShapes(args: PreparePieSeriesArgs): React.JSX.Element;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { arc, color, line as lineGenerator,
|
|
2
|
+
import { arc, color, line as lineGenerator, select } from 'd3';
|
|
3
3
|
import get from 'lodash/get';
|
|
4
4
|
import { block } from '../../../../../../utils/cn';
|
|
5
5
|
import { setEllipsisForOverflowTexts } from '../../../utils';
|
|
@@ -11,7 +11,7 @@ export function getHaloVisibility(d) {
|
|
|
11
11
|
return enabled ? '' : 'hidden';
|
|
12
12
|
}
|
|
13
13
|
export function PieSeriesShapes(args) {
|
|
14
|
-
const { dispatcher, preparedData, seriesOptions
|
|
14
|
+
const { dispatcher, preparedData, seriesOptions } = args;
|
|
15
15
|
const ref = React.useRef(null);
|
|
16
16
|
React.useEffect(() => {
|
|
17
17
|
if (!ref.current) {
|
|
@@ -127,32 +127,15 @@ export function PieSeriesShapes(args) {
|
|
|
127
127
|
const eventName = `hover-shape.pie`;
|
|
128
128
|
const hoverOptions = get(seriesOptions, 'pie.states.hover');
|
|
129
129
|
const inactiveOptions = get(seriesOptions, 'pie.states.inactive');
|
|
130
|
-
svgElement
|
|
131
|
-
.on('mousemove', (e) => {
|
|
132
|
-
const currentSegment = getSelectedSegment(e.target);
|
|
133
|
-
if (currentSegment) {
|
|
134
|
-
const data = {
|
|
135
|
-
series: {
|
|
136
|
-
id: currentSegment.series.id,
|
|
137
|
-
type: 'pie',
|
|
138
|
-
name: currentSegment.series.name,
|
|
139
|
-
},
|
|
140
|
-
data: currentSegment.series.data,
|
|
141
|
-
};
|
|
142
|
-
dispatcher.call('hover-shape', {}, [data], pointer(e, svgContainer));
|
|
143
|
-
}
|
|
144
|
-
})
|
|
145
|
-
.on('mouseleave', () => {
|
|
146
|
-
dispatcher.call('hover-shape', {}, undefined);
|
|
147
|
-
})
|
|
148
|
-
.on('click', (e) => {
|
|
130
|
+
svgElement.on('click', (e) => {
|
|
149
131
|
const selectedSegment = getSelectedSegment(e.target);
|
|
150
132
|
if (selectedSegment) {
|
|
151
133
|
dispatcher.call('click-chart', undefined, { point: selectedSegment.series.data, series: selectedSegment.series }, e);
|
|
152
134
|
}
|
|
153
135
|
});
|
|
154
136
|
dispatcher.on(eventName, (data) => {
|
|
155
|
-
|
|
137
|
+
var _a, _b;
|
|
138
|
+
const selectedSeriesId = (_b = (_a = data === null || data === void 0 ? void 0 : data[0]) === null || _a === void 0 ? void 0 : _a.series) === null || _b === void 0 ? void 0 : _b.id;
|
|
156
139
|
const hoverEnabled = hoverOptions === null || hoverOptions === void 0 ? void 0 : hoverOptions.enabled;
|
|
157
140
|
const inactiveEnabled = inactiveOptions === null || inactiveOptions === void 0 ? void 0 : inactiveOptions.enabled;
|
|
158
141
|
shapesSelection.datum((_d, index, list) => {
|
|
@@ -202,6 +185,6 @@ export function PieSeriesShapes(args) {
|
|
|
202
185
|
return () => {
|
|
203
186
|
dispatcher.on(eventName, null);
|
|
204
187
|
};
|
|
205
|
-
}, [dispatcher, preparedData, seriesOptions
|
|
188
|
+
}, [dispatcher, preparedData, seriesOptions]);
|
|
206
189
|
return React.createElement("g", { ref: ref, className: b(), style: { zIndex: 9 } });
|
|
207
190
|
}
|
|
@@ -7,6 +7,5 @@ type ScatterSeriesShapeProps = {
|
|
|
7
7
|
dispatcher: Dispatch<object>;
|
|
8
8
|
preparedData: PreparedScatterData[];
|
|
9
9
|
seriesOptions: PreparedSeriesOptions;
|
|
10
|
-
svgContainer: SVGSVGElement | null;
|
|
11
10
|
};
|
|
12
11
|
export declare function ScatterSeriesShape(props: ScatterSeriesShapeProps): React.JSX.Element;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { select } from 'd3';
|
|
3
3
|
import get from 'lodash/get';
|
|
4
4
|
import { block } from '../../../../../../utils/cn';
|
|
5
5
|
import { getMarkerHaloVisibility, renderMarker, selectMarkerHalo, selectMarkerSymbol, setMarker, } from '../marker';
|
|
@@ -7,7 +7,7 @@ import { setActiveState, shapeKey } from '../utils';
|
|
|
7
7
|
export { prepareScatterData } from './prepare-data';
|
|
8
8
|
const b = block('d3-scatter');
|
|
9
9
|
export function ScatterSeriesShape(props) {
|
|
10
|
-
const { dispatcher, preparedData, seriesOptions
|
|
10
|
+
const { dispatcher, preparedData, seriesOptions } = props;
|
|
11
11
|
const ref = React.useRef(null);
|
|
12
12
|
React.useEffect(() => {
|
|
13
13
|
if (!ref.current) {
|
|
@@ -28,27 +28,7 @@ export function ScatterSeriesShape(props) {
|
|
|
28
28
|
const getSelectedPoint = (element) => {
|
|
29
29
|
return select(element).datum();
|
|
30
30
|
};
|
|
31
|
-
svgElement
|
|
32
|
-
.on('mousemove', (e) => {
|
|
33
|
-
const datum = getSelectedPoint(e.target);
|
|
34
|
-
if (!datum) {
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
const [pointerX, pointerY] = pointer(e, svgContainer);
|
|
38
|
-
const data = {
|
|
39
|
-
series: {
|
|
40
|
-
id: datum.point.series.id,
|
|
41
|
-
type: 'scatter',
|
|
42
|
-
name: datum.point.series.name,
|
|
43
|
-
},
|
|
44
|
-
data: datum.point.data,
|
|
45
|
-
};
|
|
46
|
-
dispatcher.call('hover-shape', {}, [data], [pointerX, pointerY]);
|
|
47
|
-
})
|
|
48
|
-
.on('mouseleave', () => {
|
|
49
|
-
dispatcher.call('hover-shape', {}, undefined);
|
|
50
|
-
})
|
|
51
|
-
.on('click', (e) => {
|
|
31
|
+
svgElement.on('click', (e) => {
|
|
52
32
|
const datum = getSelectedPoint(e.target);
|
|
53
33
|
if (datum) {
|
|
54
34
|
dispatcher.call('click-chart', undefined, { point: datum.point.data, series: datum.point.series }, e);
|
|
@@ -90,6 +70,6 @@ export function ScatterSeriesShape(props) {
|
|
|
90
70
|
return () => {
|
|
91
71
|
dispatcher.on('hover-shape.scatter', null);
|
|
92
72
|
};
|
|
93
|
-
}, [dispatcher, preparedData, seriesOptions
|
|
73
|
+
}, [dispatcher, preparedData, seriesOptions]);
|
|
94
74
|
return React.createElement("g", { ref: ref, className: b() });
|
|
95
75
|
}
|
|
@@ -6,7 +6,6 @@ type ShapeProps = {
|
|
|
6
6
|
dispatcher: Dispatch<object>;
|
|
7
7
|
preparedData: PreparedTreemapData;
|
|
8
8
|
seriesOptions: PreparedSeriesOptions;
|
|
9
|
-
svgContainer: SVGSVGElement | null;
|
|
10
9
|
};
|
|
11
10
|
export declare const TreemapSeriesShape: (props: ShapeProps) => React.JSX.Element;
|
|
12
11
|
export {};
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { color,
|
|
2
|
+
import { color, select } from 'd3';
|
|
3
3
|
import get from 'lodash/get';
|
|
4
4
|
import { block } from '../../../../../../utils/cn';
|
|
5
5
|
import { setEllipsisForOverflowTexts } from '../../../utils';
|
|
6
6
|
const b = block('d3-treemap');
|
|
7
7
|
export const TreemapSeriesShape = (props) => {
|
|
8
|
-
const { dispatcher, preparedData, seriesOptions
|
|
8
|
+
const { dispatcher, preparedData, seriesOptions } = props;
|
|
9
9
|
const ref = React.useRef(null);
|
|
10
10
|
React.useEffect(() => {
|
|
11
11
|
if (!ref.current) {
|
|
@@ -52,22 +52,15 @@ export const TreemapSeriesShape = (props) => {
|
|
|
52
52
|
const eventName = `hover-shape.treemap`;
|
|
53
53
|
const hoverOptions = get(seriesOptions, 'treemap.states.hover');
|
|
54
54
|
const inactiveOptions = get(seriesOptions, 'treemap.states.inactive');
|
|
55
|
-
svgElement
|
|
56
|
-
.on('mousemove', (e) => {
|
|
57
|
-
const datum = getSelectedPart(e.target);
|
|
58
|
-
dispatcher.call('hover-shape', {}, [{ data: datum.data, series }], pointer(e, svgContainer));
|
|
59
|
-
})
|
|
60
|
-
.on('mouseleave', () => {
|
|
61
|
-
dispatcher.call('hover-shape', {}, undefined);
|
|
62
|
-
})
|
|
63
|
-
.on('click', (e) => {
|
|
55
|
+
svgElement.on('click', (e) => {
|
|
64
56
|
const datum = getSelectedPart(e.target);
|
|
65
57
|
dispatcher.call('click-chart', undefined, { point: datum.data, series }, e);
|
|
66
58
|
});
|
|
67
59
|
dispatcher.on(eventName, (data) => {
|
|
60
|
+
var _a;
|
|
68
61
|
const hoverEnabled = hoverOptions === null || hoverOptions === void 0 ? void 0 : hoverOptions.enabled;
|
|
69
62
|
const inactiveEnabled = inactiveOptions === null || inactiveOptions === void 0 ? void 0 : inactiveOptions.enabled;
|
|
70
|
-
const hoveredData = data === null || data === void 0 ? void 0 : data[0].data;
|
|
63
|
+
const hoveredData = (_a = data === null || data === void 0 ? void 0 : data[0]) === null || _a === void 0 ? void 0 : _a.data;
|
|
71
64
|
rectSelection.datum((d, index, list) => {
|
|
72
65
|
const currentRect = select(list[index]);
|
|
73
66
|
const hovered = Boolean(hoverEnabled && hoveredData === d.data);
|
|
@@ -106,6 +99,6 @@ export const TreemapSeriesShape = (props) => {
|
|
|
106
99
|
return () => {
|
|
107
100
|
dispatcher.on(eventName, null);
|
|
108
101
|
};
|
|
109
|
-
}, [dispatcher, preparedData, seriesOptions
|
|
102
|
+
}, [dispatcher, preparedData, seriesOptions]);
|
|
110
103
|
return React.createElement("g", { ref: ref, className: b() });
|
|
111
104
|
};
|
|
@@ -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[];
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import get from 'lodash/get';
|
|
2
|
+
import sortBy from 'lodash/sortBy';
|
|
3
|
+
import { getLabelsSize } from '../../../utils';
|
|
4
|
+
import { MIN_BAR_GAP, MIN_BAR_WIDTH } from '../constants';
|
|
5
|
+
import { getXValue, getYValue } from '../utils';
|
|
6
|
+
function getLabelData(d, plotHeight) {
|
|
7
|
+
if (!d.series.dataLabels.enabled) {
|
|
8
|
+
return undefined;
|
|
9
|
+
}
|
|
10
|
+
const text = String(d.data.label || d.subTotal);
|
|
11
|
+
const style = d.series.dataLabels.style;
|
|
12
|
+
const { maxHeight: height, maxWidth: width } = getLabelsSize({ labels: [text], style });
|
|
13
|
+
let y;
|
|
14
|
+
if (Number(d.data.y) > 0 || d.data.total) {
|
|
15
|
+
y = Math.max(height, d.y - d.series.dataLabels.padding);
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
y = Math.min(plotHeight - d.series.dataLabels.padding, d.y + d.height + d.series.dataLabels.padding + height);
|
|
19
|
+
}
|
|
20
|
+
return {
|
|
21
|
+
text,
|
|
22
|
+
x: d.x + d.width / 2,
|
|
23
|
+
y,
|
|
24
|
+
style,
|
|
25
|
+
size: { width, height },
|
|
26
|
+
textAnchor: 'middle',
|
|
27
|
+
series: d.series,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function getBandWidth(args) {
|
|
31
|
+
const { series, xAxis, xScale } = args;
|
|
32
|
+
if (xAxis.type === 'category') {
|
|
33
|
+
const xBandScale = xScale;
|
|
34
|
+
return xBandScale.bandwidth();
|
|
35
|
+
}
|
|
36
|
+
const xLinearScale = xScale;
|
|
37
|
+
const xValues = series.reduce((acc, s) => {
|
|
38
|
+
s.data.forEach((dataItem) => acc.push(Number(dataItem.x)));
|
|
39
|
+
return acc;
|
|
40
|
+
}, []);
|
|
41
|
+
let bandWidth = Infinity;
|
|
42
|
+
xValues.sort().forEach((xValue, index) => {
|
|
43
|
+
if (index > 0 && xValue !== xValues[index - 1]) {
|
|
44
|
+
const dist = xLinearScale(xValue) - xLinearScale(xValues[index - 1]);
|
|
45
|
+
if (dist < bandWidth) {
|
|
46
|
+
bandWidth = dist;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
return bandWidth;
|
|
51
|
+
}
|
|
52
|
+
export const prepareWaterfallData = (args) => {
|
|
53
|
+
const { series, seriesOptions, xAxis, xScale, yAxis: [yAxis], yScale, } = args;
|
|
54
|
+
const yLinearScale = yScale;
|
|
55
|
+
const plotHeight = yLinearScale(yLinearScale.domain()[0]);
|
|
56
|
+
const barMaxWidth = get(seriesOptions, 'waterfall.barMaxWidth');
|
|
57
|
+
const barPadding = get(seriesOptions, 'waterfall.barPadding');
|
|
58
|
+
const flattenData = series.reduce((acc, s) => {
|
|
59
|
+
acc.push(...s.data.map((d) => ({ data: d, series: s })));
|
|
60
|
+
return acc;
|
|
61
|
+
}, []);
|
|
62
|
+
const data = sortBy(flattenData, (d) => d.data.x);
|
|
63
|
+
const bandWidth = getBandWidth({
|
|
64
|
+
series,
|
|
65
|
+
xAxis,
|
|
66
|
+
xScale,
|
|
67
|
+
});
|
|
68
|
+
const rectGap = Math.max(bandWidth * barPadding, MIN_BAR_GAP);
|
|
69
|
+
const rectWidth = Math.max(MIN_BAR_WIDTH, Math.min(bandWidth - rectGap, barMaxWidth));
|
|
70
|
+
const yZero = getYValue({
|
|
71
|
+
point: { y: 0 },
|
|
72
|
+
yScale,
|
|
73
|
+
yAxis,
|
|
74
|
+
});
|
|
75
|
+
let totalValue = 0;
|
|
76
|
+
const result = [];
|
|
77
|
+
data.forEach((item, _index) => {
|
|
78
|
+
if (typeof item.data.y !== 'number' && !item.data.total) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
if (!item.data.total) {
|
|
82
|
+
totalValue += Number(item.data.y);
|
|
83
|
+
}
|
|
84
|
+
const prevPoint = result[result.length - 1];
|
|
85
|
+
const xCenter = getXValue({ point: item.data, xAxis, xScale });
|
|
86
|
+
const x = xCenter - rectWidth / 2;
|
|
87
|
+
const yValue = Number(item.data.total ? totalValue : item.data.y);
|
|
88
|
+
const height = yZero -
|
|
89
|
+
getYValue({
|
|
90
|
+
point: { y: Math.abs(yValue) },
|
|
91
|
+
yScale,
|
|
92
|
+
yAxis,
|
|
93
|
+
});
|
|
94
|
+
let y;
|
|
95
|
+
if (!prevPoint || item.data.total) {
|
|
96
|
+
y = getYValue({
|
|
97
|
+
point: {
|
|
98
|
+
y: yValue > 0 ? yValue : 0,
|
|
99
|
+
},
|
|
100
|
+
yScale,
|
|
101
|
+
yAxis,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
else if (Number(prevPoint.data.y) < 0) {
|
|
105
|
+
if (Number(item.data.y) > 0) {
|
|
106
|
+
y = prevPoint.y + prevPoint.height - height;
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
y = prevPoint.y + prevPoint.height;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
else if (Number(item.data.y) < 0) {
|
|
113
|
+
y = prevPoint.y;
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
y = prevPoint.y - height;
|
|
117
|
+
}
|
|
118
|
+
const preparedData = {
|
|
119
|
+
x,
|
|
120
|
+
y,
|
|
121
|
+
width: rectWidth,
|
|
122
|
+
height,
|
|
123
|
+
opacity: get(item.data, 'opacity', null),
|
|
124
|
+
data: item.data,
|
|
125
|
+
series: item.series,
|
|
126
|
+
subTotal: totalValue,
|
|
127
|
+
};
|
|
128
|
+
preparedData.label = getLabelData(preparedData, plotHeight);
|
|
129
|
+
result.push(preparedData);
|
|
130
|
+
});
|
|
131
|
+
return result;
|
|
132
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { WaterfallSeriesData } from '../../../../../../types';
|
|
2
|
+
import { LabelData } from '../../../types';
|
|
3
|
+
import { PreparedWaterfallSeries } from '../../useSeries/types';
|
|
4
|
+
export type PreparedWaterfallData = {
|
|
5
|
+
x: number;
|
|
6
|
+
y: number;
|
|
7
|
+
width: number;
|
|
8
|
+
height: number;
|
|
9
|
+
opacity: number | null;
|
|
10
|
+
series: PreparedWaterfallSeries;
|
|
11
|
+
data: WaterfallSeriesData;
|
|
12
|
+
label?: LabelData;
|
|
13
|
+
subTotal: number;
|
|
14
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ChartKitWidgetSeries, ChartKitWidgetSeriesData, TooltipDataChunk } from '../../../../types';
|
|
2
|
+
import type { ShapeData } from '../hooks';
|
|
3
|
+
type GetClosestPointsArgs = {
|
|
4
|
+
position: [number, number];
|
|
5
|
+
shapesData: ShapeData[];
|
|
6
|
+
};
|
|
7
|
+
export type ShapePoint = {
|
|
8
|
+
x: number;
|
|
9
|
+
y0: number;
|
|
10
|
+
y1: number;
|
|
11
|
+
data: ChartKitWidgetSeriesData;
|
|
12
|
+
series: ChartKitWidgetSeries;
|
|
13
|
+
};
|
|
14
|
+
export declare function getClosestPoints(args: GetClosestPointsArgs): TooltipDataChunk[];
|
|
15
|
+
export {};
|