@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
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import { pointer } from 'd3';
|
|
3
|
+
import throttle from 'lodash/throttle';
|
|
2
4
|
import { block } from '../../../../utils/cn';
|
|
3
5
|
import { getD3Dispatcher } from '../d3-dispatcher';
|
|
4
|
-
import { useAxisScales, useChartDimensions, useChartOptions, useSeries, useShapes
|
|
6
|
+
import { useAxisScales, useChartDimensions, useChartOptions, useSeries, useShapes } from '../hooks';
|
|
5
7
|
import { getWidthOccupiedByYAxis } from '../hooks/useChartDimensions/utils';
|
|
6
8
|
import { getPreparedXAxis } from '../hooks/useChartOptions/x-axis';
|
|
7
9
|
import { getPreparedYAxis } from '../hooks/useChartOptions/y-axis';
|
|
10
|
+
import { getClosestPoints } from '../utils/get-closest-data';
|
|
8
11
|
import { AxisX } from './AxisX';
|
|
9
12
|
import { AxisY } from './AxisY';
|
|
10
13
|
import { Legend } from './Legend';
|
|
11
14
|
import { Title } from './Title';
|
|
12
|
-
import { Tooltip
|
|
15
|
+
import { Tooltip } from './Tooltip';
|
|
13
16
|
import './styles.css';
|
|
14
17
|
const b = block('d3');
|
|
18
|
+
const THROTTLE_DELAY = 50;
|
|
15
19
|
export const Chart = (props) => {
|
|
16
20
|
var _a, _b;
|
|
17
21
|
const { width, height, data } = props;
|
|
@@ -48,7 +52,6 @@ export const Chart = (props) => {
|
|
|
48
52
|
xAxis,
|
|
49
53
|
yAxis,
|
|
50
54
|
});
|
|
51
|
-
const { hovered, pointerPosition } = useTooltip({ dispatcher, tooltip });
|
|
52
55
|
const { shapes, shapesData } = useShapes({
|
|
53
56
|
boundsWidth,
|
|
54
57
|
boundsHeight,
|
|
@@ -59,7 +62,6 @@ export const Chart = (props) => {
|
|
|
59
62
|
xScale,
|
|
60
63
|
yAxis,
|
|
61
64
|
yScale,
|
|
62
|
-
svgContainer: svgRef.current,
|
|
63
65
|
});
|
|
64
66
|
const clickHandler = (_b = (_a = data.chart) === null || _a === void 0 ? void 0 : _a.events) === null || _b === void 0 ? void 0 : _b.click;
|
|
65
67
|
React.useEffect(() => {
|
|
@@ -72,16 +74,34 @@ export const Chart = (props) => {
|
|
|
72
74
|
}, [dispatcher, clickHandler]);
|
|
73
75
|
const boundsOffsetTop = chart.margin.top;
|
|
74
76
|
const boundsOffsetLeft = chart.margin.left + getWidthOccupiedByYAxis({ preparedAxis: yAxis });
|
|
77
|
+
const handleMouseMove = (event) => {
|
|
78
|
+
const [pointerX, pointerY] = pointer(event, svgRef.current);
|
|
79
|
+
const x = pointerX - boundsOffsetLeft;
|
|
80
|
+
const y = pointerY - boundsOffsetTop;
|
|
81
|
+
if (x < 0 || x > boundsWidth || y < 0 || y > boundsHeight) {
|
|
82
|
+
dispatcher.call('hover-shape', {}, undefined);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
const closest = getClosestPoints({
|
|
86
|
+
position: [x, y],
|
|
87
|
+
shapesData,
|
|
88
|
+
});
|
|
89
|
+
dispatcher.call('hover-shape', event.target, closest, [pointerX, pointerY]);
|
|
90
|
+
};
|
|
91
|
+
const throttledHandleMouseMove = throttle(handleMouseMove, THROTTLE_DELAY);
|
|
92
|
+
const handleMouseLeave = () => {
|
|
93
|
+
throttledHandleMouseMove.cancel();
|
|
94
|
+
dispatcher.call('hover-shape', {}, undefined);
|
|
95
|
+
};
|
|
75
96
|
return (React.createElement(React.Fragment, null,
|
|
76
|
-
React.createElement("svg", { ref: svgRef, className: b(), width: width, height: height },
|
|
97
|
+
React.createElement("svg", { ref: svgRef, className: b(), width: width, height: height, onMouseMove: throttledHandleMouseMove, onMouseLeave: handleMouseLeave },
|
|
77
98
|
title && React.createElement(Title, Object.assign({}, title, { chartWidth: width })),
|
|
78
99
|
React.createElement("g", { width: boundsWidth, height: boundsHeight, transform: `translate(${[boundsOffsetLeft, boundsOffsetTop].join(',')})` },
|
|
79
100
|
xScale && yScale && (React.createElement(React.Fragment, null,
|
|
80
101
|
React.createElement(AxisY, { axises: yAxis, width: boundsWidth, height: boundsHeight, scale: yScale }),
|
|
81
102
|
React.createElement("g", { transform: `translate(0, ${boundsHeight})` },
|
|
82
103
|
React.createElement(AxisX, { axis: xAxis, width: boundsWidth, height: boundsHeight, scale: xScale })))),
|
|
83
|
-
shapes,
|
|
84
|
-
(tooltip === null || tooltip === void 0 ? void 0 : tooltip.enabled) && Boolean(shapesData.length) && (React.createElement(TooltipTriggerArea, { boundsWidth: boundsWidth, boundsHeight: boundsHeight, dispatcher: dispatcher, shapesData: shapesData, svgContainer: svgRef.current }))),
|
|
104
|
+
shapes),
|
|
85
105
|
preparedLegend.enabled && (React.createElement(Legend, { chartSeries: preparedSeries, boundsWidth: boundsWidth, legend: preparedLegend, items: legendItems, config: legendConfig, onItemClick: handleLegendItemClick }))),
|
|
86
|
-
React.createElement(Tooltip, { dispatcher: dispatcher, tooltip: tooltip, svgContainer: svgRef.current, xAxis: xAxis, yAxis: yAxis[0]
|
|
106
|
+
React.createElement(Tooltip, { dispatcher: dispatcher, tooltip: tooltip, svgContainer: svgRef.current, xAxis: xAxis, yAxis: yAxis[0] })));
|
|
87
107
|
};
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { dateTime } from '@gravity-ui/date-utils';
|
|
3
3
|
import get from 'lodash/get';
|
|
4
|
+
import { block } from '../../../../../utils/cn';
|
|
4
5
|
import { formatNumber } from '../../../../shared';
|
|
5
|
-
import { getDataCategoryValue } from '../../utils';
|
|
6
|
+
import { getDataCategoryValue, getWaterfallPointSubtotal } from '../../utils';
|
|
7
|
+
const b = block('d3-tooltip');
|
|
6
8
|
const DEFAULT_DATE_FORMAT = 'DD.MM.YY';
|
|
7
9
|
const getRowData = (fieldName, axis, data) => {
|
|
8
10
|
switch (axis.type) {
|
|
@@ -26,47 +28,75 @@ const getRowData = (fieldName, axis, data) => {
|
|
|
26
28
|
};
|
|
27
29
|
const getXRowData = (xAxis, data) => getRowData('x', xAxis, data);
|
|
28
30
|
const getYRowData = (yAxis, data) => getRowData('y', yAxis, data);
|
|
31
|
+
const getMeasureValue = (data, xAxis, yAxis) => {
|
|
32
|
+
var _a, _b;
|
|
33
|
+
if (data.every((item) => ['pie', 'treemap', 'waterfall'].includes(item.series.type))) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
if (data.some((item) => item.series.type === 'bar-y')) {
|
|
37
|
+
return getYRowData(yAxis, (_a = data[0]) === null || _a === void 0 ? void 0 : _a.data);
|
|
38
|
+
}
|
|
39
|
+
return getXRowData(xAxis, (_b = data[0]) === null || _b === void 0 ? void 0 : _b.data);
|
|
40
|
+
};
|
|
29
41
|
export const DefaultContent = ({ hovered, xAxis, yAxis }) => {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
React.createElement(
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
const measureValue = getMeasureValue(hovered, xAxis, yAxis);
|
|
43
|
+
return (React.createElement(React.Fragment, null,
|
|
44
|
+
measureValue && React.createElement("div", null, measureValue),
|
|
45
|
+
hovered.map(({ data, series, closest }, i) => {
|
|
46
|
+
const id = `${get(series, 'id')}_${i}`;
|
|
47
|
+
const color = get(series, 'color');
|
|
48
|
+
switch (series.type) {
|
|
49
|
+
case 'scatter':
|
|
50
|
+
case 'line':
|
|
51
|
+
case 'area':
|
|
52
|
+
case 'bar-x': {
|
|
53
|
+
const value = (React.createElement(React.Fragment, null,
|
|
54
|
+
series.name,
|
|
55
|
+
": ",
|
|
56
|
+
getYRowData(yAxis, data)));
|
|
57
|
+
return (React.createElement("div", { key: id, className: b('content-row') },
|
|
58
|
+
React.createElement("div", { className: b('color'), style: { backgroundColor: color } }),
|
|
59
|
+
React.createElement("div", null, closest ? React.createElement("b", null, value) : React.createElement("span", null, value))));
|
|
60
|
+
}
|
|
61
|
+
case 'waterfall': {
|
|
62
|
+
const isTotal = get(data, 'total', false);
|
|
63
|
+
const subTotal = getWaterfallPointSubtotal(data, series);
|
|
64
|
+
return (React.createElement("div", { key: `${id}_${get(data, 'x')}` },
|
|
65
|
+
!isTotal && (React.createElement(React.Fragment, null,
|
|
66
|
+
React.createElement("div", { key: id, className: b('content-row') },
|
|
67
|
+
React.createElement("b", null, getXRowData(xAxis, data))),
|
|
68
|
+
React.createElement("div", { className: b('content-row') },
|
|
69
|
+
React.createElement("span", null,
|
|
70
|
+
series.name,
|
|
71
|
+
"\u00A0"),
|
|
72
|
+
React.createElement("span", null, getYRowData(yAxis, data))))),
|
|
73
|
+
React.createElement("div", { key: id, className: b('content-row') },
|
|
74
|
+
isTotal ? 'Total' : 'Subtotal',
|
|
44
75
|
": ",
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
React.createElement("div",
|
|
76
|
+
subTotal)));
|
|
77
|
+
}
|
|
78
|
+
case 'bar-y': {
|
|
79
|
+
const value = (React.createElement(React.Fragment, null,
|
|
80
|
+
series.name,
|
|
81
|
+
": ",
|
|
82
|
+
getXRowData(xAxis, data)));
|
|
83
|
+
return (React.createElement("div", { key: id, className: b('content-row') },
|
|
84
|
+
React.createElement("div", { className: b('color'), style: { backgroundColor: color } }),
|
|
85
|
+
React.createElement("div", null, closest ? React.createElement("b", null, value) : React.createElement("span", null, value))));
|
|
86
|
+
}
|
|
87
|
+
case 'pie':
|
|
88
|
+
case 'treemap': {
|
|
89
|
+
const seriesData = data;
|
|
90
|
+
return (React.createElement("div", { key: id, className: b('content-row') },
|
|
91
|
+
React.createElement("div", { className: b('color'), style: { backgroundColor: color } }),
|
|
53
92
|
React.createElement("span", null,
|
|
54
|
-
|
|
55
|
-
"
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
return (React.createElement("div", { key: id },
|
|
62
|
-
React.createElement("span", null,
|
|
63
|
-
pieSeriesData.name || pieSeriesData.id,
|
|
64
|
-
"\u00A0"),
|
|
65
|
-
React.createElement("span", null, pieSeriesData.value)));
|
|
93
|
+
seriesData.name || seriesData.id,
|
|
94
|
+
"\u00A0"),
|
|
95
|
+
React.createElement("span", null, seriesData.value)));
|
|
96
|
+
}
|
|
97
|
+
default: {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
66
100
|
}
|
|
67
|
-
|
|
68
|
-
return null;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
})));
|
|
101
|
+
})));
|
|
72
102
|
};
|
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { Dispatch } from 'd3';
|
|
3
|
-
import type {
|
|
4
|
-
import type { PointerPosition, PreparedAxis, PreparedTooltip } from '../../hooks';
|
|
5
|
-
export * from './TooltipTriggerArea';
|
|
3
|
+
import type { PreparedAxis, PreparedTooltip } from '../../hooks';
|
|
6
4
|
type TooltipProps = {
|
|
7
5
|
dispatcher: Dispatch<object>;
|
|
8
6
|
tooltip: PreparedTooltip;
|
|
9
7
|
svgContainer: SVGSVGElement | null;
|
|
10
8
|
xAxis: PreparedAxis;
|
|
11
9
|
yAxis: PreparedAxis;
|
|
12
|
-
hovered?: TooltipDataChunk[];
|
|
13
|
-
pointerPosition?: PointerPosition;
|
|
14
10
|
};
|
|
15
11
|
export declare const Tooltip: (props: TooltipProps) => React.JSX.Element | null;
|
|
12
|
+
export {};
|
|
@@ -2,11 +2,12 @@ import React from 'react';
|
|
|
2
2
|
import { Popup, useVirtualElementRef } from '@gravity-ui/uikit';
|
|
3
3
|
import isNil from 'lodash/isNil';
|
|
4
4
|
import { block } from '../../../../../utils/cn';
|
|
5
|
+
import { useTooltip } from '../../hooks';
|
|
5
6
|
import { DefaultContent } from './DefaultContent';
|
|
6
|
-
export * from './TooltipTriggerArea';
|
|
7
7
|
const b = block('d3-tooltip');
|
|
8
8
|
export const Tooltip = (props) => {
|
|
9
|
-
const { tooltip, xAxis, yAxis,
|
|
9
|
+
const { tooltip, xAxis, yAxis, svgContainer, dispatcher } = props;
|
|
10
|
+
const { hovered, pointerPosition } = useTooltip({ dispatcher, tooltip });
|
|
10
11
|
const containerRect = (svgContainer === null || svgContainer === void 0 ? void 0 : svgContainer.getBoundingClientRect()) || { left: 0, top: 0 };
|
|
11
12
|
const left = ((pointerPosition === null || pointerPosition === void 0 ? void 0 : pointerPosition[0]) || 0) + containerRect.left;
|
|
12
13
|
const top = ((pointerPosition === null || pointerPosition === void 0 ? void 0 : pointerPosition[1]) || 0) + containerRect.top;
|
|
@@ -22,6 +23,6 @@ export const Tooltip = (props) => {
|
|
|
22
23
|
React.useEffect(() => {
|
|
23
24
|
window.dispatchEvent(new CustomEvent('scroll'));
|
|
24
25
|
}, [left, top]);
|
|
25
|
-
return hovered ? (React.createElement(Popup, { className: b(), open: true, anchorRef: anchorRef, offset: [0, 20], placement: ['right', 'left', 'top', 'bottom'], modifiers: [{ name: 'preventOverflow', options: { padding: 10, altAxis: true } }] },
|
|
26
|
+
return (hovered === null || hovered === void 0 ? void 0 : hovered.length) ? (React.createElement(Popup, { className: b(), open: true, anchorRef: anchorRef, offset: [0, 20], placement: ['right', 'left', 'top', 'bottom'], modifiers: [{ name: 'preventOverflow', options: { padding: 10, altAxis: true } }] },
|
|
26
27
|
React.createElement("div", { className: b('content') }, content))) : null;
|
|
27
28
|
};
|
|
@@ -97,4 +97,18 @@
|
|
|
97
97
|
border-radius: 3px;
|
|
98
98
|
box-shadow: 0 2px 12px var(--g-color-sfx-shadow);
|
|
99
99
|
text-wrap: nowrap;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.chartkit-d3-tooltip__content-row {
|
|
103
|
+
display: flex;
|
|
104
|
+
align-items: center;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.chartkit-d3-tooltip__color {
|
|
108
|
+
height: 8px;
|
|
109
|
+
width: 16px;
|
|
110
|
+
display: inline-block;
|
|
111
|
+
margin-right: 8px;
|
|
112
|
+
border-radius: 2px;
|
|
113
|
+
background-color: #dddddd;
|
|
100
114
|
}
|
|
@@ -13,6 +13,12 @@ type DefaultBarYSeriesOptions = Partial<ChartKitWidgetSeriesOptions['bar-x']> &
|
|
|
13
13
|
groupPadding: number;
|
|
14
14
|
};
|
|
15
15
|
};
|
|
16
|
-
|
|
16
|
+
type DefaultWaterfallSeriesOptions = Partial<ChartKitWidgetSeriesOptions['waterfall']> & {
|
|
17
|
+
waterfall: {
|
|
18
|
+
barMaxWidth: number;
|
|
19
|
+
barPadding: number;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
export type SeriesOptionsDefaults = Partial<ChartKitWidgetSeriesOptions> & DefaultBarXSeriesOptions & DefaultBarYSeriesOptions & DefaultWaterfallSeriesOptions;
|
|
17
23
|
export declare const seriesOptionsDefaults: SeriesOptionsDefaults;
|
|
18
24
|
export {};
|
|
@@ -89,4 +89,18 @@ export const seriesOptionsDefaults = {
|
|
|
89
89
|
},
|
|
90
90
|
},
|
|
91
91
|
},
|
|
92
|
+
waterfall: {
|
|
93
|
+
barMaxWidth: 50,
|
|
94
|
+
barPadding: 0.1,
|
|
95
|
+
states: {
|
|
96
|
+
hover: {
|
|
97
|
+
enabled: true,
|
|
98
|
+
brightness: 0.3,
|
|
99
|
+
},
|
|
100
|
+
inactive: {
|
|
101
|
+
enabled: false,
|
|
102
|
+
opacity: 0.5,
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
},
|
|
92
106
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import get from 'lodash/get';
|
|
2
2
|
import { DEFAULT_AXIS_LABEL_FONT_SIZE, axisLabelsDefaults, yAxisTitleDefaults, } from '../../constants';
|
|
3
|
-
import { formatAxisTickLabel, getClosestPointsRange, getHorisontalSvgTextHeight, getLabelsSize, getScaleTicks, } from '../../utils';
|
|
3
|
+
import { formatAxisTickLabel, getClosestPointsRange, getHorisontalSvgTextHeight, getLabelsSize, getScaleTicks, getWaterfallPointSubtotal, } from '../../utils';
|
|
4
4
|
import { createYScale } from '../useAxisScales';
|
|
5
5
|
const getAxisLabelMaxWidth = (args) => {
|
|
6
6
|
const { axis, series } = args;
|
|
@@ -24,9 +24,19 @@ const getAxisLabelMaxWidth = (args) => {
|
|
|
24
24
|
};
|
|
25
25
|
function getAxisMin(axis, series) {
|
|
26
26
|
const min = axis === null || axis === void 0 ? void 0 : axis.min;
|
|
27
|
-
const seriesWithVolume = ['bar-x', 'area'];
|
|
27
|
+
const seriesWithVolume = ['bar-x', 'area', 'waterfall'];
|
|
28
28
|
if (typeof min === 'undefined' && (series === null || series === void 0 ? void 0 : series.some((s) => seriesWithVolume.includes(s.type)))) {
|
|
29
|
-
return
|
|
29
|
+
return series.reduce((minValue, s) => {
|
|
30
|
+
switch (s.type) {
|
|
31
|
+
case 'waterfall': {
|
|
32
|
+
const minSubTotal = s.data.reduce((res, d) => Math.min(res, getWaterfallPointSubtotal(d, s) || 0), 0);
|
|
33
|
+
return Math.min(minValue, minSubTotal);
|
|
34
|
+
}
|
|
35
|
+
default: {
|
|
36
|
+
return minValue;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}, 0);
|
|
30
40
|
}
|
|
31
41
|
return min;
|
|
32
42
|
}
|
|
@@ -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';
|
|
@@ -208,7 +208,19 @@ export type PreparedTreemapSeries = {
|
|
|
208
208
|
};
|
|
209
209
|
layoutAlgorithm: `${LayoutAlgorithm}`;
|
|
210
210
|
} & BasePreparedSeries & Omit<TreemapSeries, keyof BasePreparedSeries>;
|
|
211
|
-
export type
|
|
211
|
+
export type PreparedWaterfallSeries = {
|
|
212
|
+
type: WaterfallSeries['type'];
|
|
213
|
+
data: WaterfallSeriesData[];
|
|
214
|
+
dataLabels: {
|
|
215
|
+
enabled: boolean;
|
|
216
|
+
style: BaseTextStyle;
|
|
217
|
+
allowOverlap: boolean;
|
|
218
|
+
padding: number;
|
|
219
|
+
};
|
|
220
|
+
positiveColor: string;
|
|
221
|
+
negativeColor: string;
|
|
222
|
+
} & BasePreparedSeries;
|
|
223
|
+
export type PreparedSeries = PreparedScatterSeries | PreparedBarXSeries | PreparedBarYSeries | PreparedPieSeries | PreparedLineSeries | PreparedAreaSeries | PreparedTreemapSeries | PreparedWaterfallSeries;
|
|
212
224
|
export type PreparedSeriesOptions = SeriesOptionsDefaults;
|
|
213
225
|
export type StackedSeries = BarXSeries | AreaSeries | BarYSeries;
|
|
214
226
|
export {};
|
|
@@ -73,14 +73,13 @@ export const AreaSeriesShapes = (args) => {
|
|
|
73
73
|
const hoverEnabled = hoverOptions === null || hoverOptions === void 0 ? void 0 : hoverOptions.enabled;
|
|
74
74
|
const inactiveEnabled = inactiveOptions === null || inactiveOptions === void 0 ? void 0 : inactiveOptions.enabled;
|
|
75
75
|
dispatcher.on('hover-shape.area', (data) => {
|
|
76
|
-
|
|
77
|
-
const
|
|
78
|
-
const
|
|
79
|
-
const selectedSeriesId = (_a = selected === null || selected === void 0 ? void 0 : selected.series) === null || _a === void 0 ? void 0 : _a.id;
|
|
76
|
+
const selected = (data === null || data === void 0 ? void 0 : data.filter((d) => d.series.type === 'area')) || [];
|
|
77
|
+
const selectedDataItems = selected.map((d) => d.data);
|
|
78
|
+
const selectedSeriesIds = selected.map((d) => { var _a; return (_a = d.series) === null || _a === void 0 ? void 0 : _a.id; });
|
|
80
79
|
shapeSelection.datum((d, index, list) => {
|
|
81
80
|
var _a;
|
|
82
81
|
const elementSelection = select(list[index]);
|
|
83
|
-
const hovered = Boolean(hoverEnabled && d.id
|
|
82
|
+
const hovered = Boolean(hoverEnabled && selectedSeriesIds.includes(d.id));
|
|
84
83
|
if (d.hovered !== hovered) {
|
|
85
84
|
d.hovered = hovered;
|
|
86
85
|
let strokeColor = d.color || '';
|
|
@@ -95,7 +94,9 @@ export const AreaSeriesShapes = (args) => {
|
|
|
95
94
|
return setActiveState({
|
|
96
95
|
element: list[index],
|
|
97
96
|
state: inactiveOptions,
|
|
98
|
-
active: Boolean(!inactiveEnabled ||
|
|
97
|
+
active: Boolean(!inactiveEnabled ||
|
|
98
|
+
!selectedSeriesIds.length ||
|
|
99
|
+
selectedSeriesIds.includes(d.id)),
|
|
99
100
|
datum: d,
|
|
100
101
|
});
|
|
101
102
|
});
|
|
@@ -103,13 +104,15 @@ export const AreaSeriesShapes = (args) => {
|
|
|
103
104
|
return setActiveState({
|
|
104
105
|
element: list[index],
|
|
105
106
|
state: inactiveOptions,
|
|
106
|
-
active: Boolean(!inactiveEnabled ||
|
|
107
|
+
active: Boolean(!inactiveEnabled ||
|
|
108
|
+
!selectedSeriesIds.length ||
|
|
109
|
+
selectedSeriesIds.includes(d.series.id)),
|
|
107
110
|
datum: d,
|
|
108
111
|
});
|
|
109
112
|
});
|
|
110
113
|
markerSelection.datum((d, index, list) => {
|
|
111
114
|
const elementSelection = select(list[index]);
|
|
112
|
-
const hovered = Boolean(hoverEnabled && d.point.data
|
|
115
|
+
const hovered = Boolean(hoverEnabled && selectedDataItems.includes(d.point.data));
|
|
113
116
|
if (d.hovered !== hovered) {
|
|
114
117
|
d.hovered = hovered;
|
|
115
118
|
elementSelection.attr('visibility', getMarkerVisibility(d));
|
|
@@ -118,8 +121,8 @@ export const AreaSeriesShapes = (args) => {
|
|
|
118
121
|
}
|
|
119
122
|
if (d.point.series.marker.states.normal.enabled) {
|
|
120
123
|
const isActive = Boolean(!inactiveEnabled ||
|
|
121
|
-
!
|
|
122
|
-
|
|
124
|
+
!selectedSeriesIds.length ||
|
|
125
|
+
selectedSeriesIds.includes(d.point.series.id));
|
|
123
126
|
setActiveState({
|
|
124
127
|
element: list[index],
|
|
125
128
|
state: inactiveOptions,
|
|
@@ -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;
|
|
@@ -21,7 +22,6 @@ type Args = {
|
|
|
21
22
|
seriesOptions: PreparedSeriesOptions;
|
|
22
23
|
xAxis: PreparedAxis;
|
|
23
24
|
yAxis: PreparedAxis[];
|
|
24
|
-
svgContainer: SVGSVGElement | null;
|
|
25
25
|
xScale?: ChartScale;
|
|
26
26
|
yScale?: ChartScale;
|
|
27
27
|
};
|
|
@@ -12,9 +12,10 @@ 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
|
-
const { boundsWidth, boundsHeight, dispatcher, series, seriesOptions, xAxis, xScale, yAxis, yScale,
|
|
18
|
+
const { boundsWidth, boundsHeight, dispatcher, series, seriesOptions, xAxis, xScale, yAxis, yScale, } = args;
|
|
18
19
|
const shapesComponents = React.useMemo(() => {
|
|
19
20
|
const visibleSeries = getOnlyVisibleSeries(series);
|
|
20
21
|
const groupedSeries = group(visibleSeries, (item) => item.type);
|
|
@@ -52,6 +53,21 @@ export const useShapes = (args) => {
|
|
|
52
53
|
}
|
|
53
54
|
break;
|
|
54
55
|
}
|
|
56
|
+
case 'waterfall': {
|
|
57
|
+
if (xScale && yScale) {
|
|
58
|
+
const preparedData = prepareWaterfallData({
|
|
59
|
+
series: chartSeries,
|
|
60
|
+
seriesOptions,
|
|
61
|
+
xAxis,
|
|
62
|
+
xScale,
|
|
63
|
+
yAxis,
|
|
64
|
+
yScale,
|
|
65
|
+
});
|
|
66
|
+
acc.push(React.createElement(WaterfallSeriesShapes, { key: "waterfall", dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData }));
|
|
67
|
+
shapesData.push(...preparedData);
|
|
68
|
+
}
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
55
71
|
case 'line': {
|
|
56
72
|
if (xScale && yScale) {
|
|
57
73
|
const preparedData = prepareLineData({
|
|
@@ -89,7 +105,8 @@ export const useShapes = (args) => {
|
|
|
89
105
|
yAxis: yAxis[0],
|
|
90
106
|
yScale,
|
|
91
107
|
});
|
|
92
|
-
acc.push(React.createElement(ScatterSeriesShape, { key: "scatter", dispatcher: dispatcher, preparedData: preparedData, seriesOptions: seriesOptions
|
|
108
|
+
acc.push(React.createElement(ScatterSeriesShape, { key: "scatter", dispatcher: dispatcher, preparedData: preparedData, seriesOptions: seriesOptions }));
|
|
109
|
+
shapesData.push(...preparedData);
|
|
93
110
|
}
|
|
94
111
|
break;
|
|
95
112
|
}
|
|
@@ -99,7 +116,8 @@ export const useShapes = (args) => {
|
|
|
99
116
|
boundsWidth,
|
|
100
117
|
boundsHeight,
|
|
101
118
|
});
|
|
102
|
-
acc.push(React.createElement(PieSeriesShapes, { key: "pie", dispatcher: dispatcher, preparedData: preparedData, seriesOptions: seriesOptions
|
|
119
|
+
acc.push(React.createElement(PieSeriesShapes, { key: "pie", dispatcher: dispatcher, preparedData: preparedData, seriesOptions: seriesOptions }));
|
|
120
|
+
shapesData.push(...preparedData);
|
|
103
121
|
break;
|
|
104
122
|
}
|
|
105
123
|
case 'treemap': {
|
|
@@ -110,7 +128,8 @@ export const useShapes = (args) => {
|
|
|
110
128
|
width: boundsWidth,
|
|
111
129
|
height: boundsHeight,
|
|
112
130
|
});
|
|
113
|
-
acc.push(React.createElement(TreemapSeriesShape, { key: "treemap", dispatcher: dispatcher, preparedData: preparedData, seriesOptions: seriesOptions
|
|
131
|
+
acc.push(React.createElement(TreemapSeriesShape, { key: "treemap", dispatcher: dispatcher, preparedData: preparedData, seriesOptions: seriesOptions }));
|
|
132
|
+
shapesData.push(preparedData);
|
|
114
133
|
}
|
|
115
134
|
}
|
|
116
135
|
return acc;
|
|
@@ -126,7 +145,6 @@ export const useShapes = (args) => {
|
|
|
126
145
|
xScale,
|
|
127
146
|
yAxis,
|
|
128
147
|
yScale,
|
|
129
|
-
svgContainer,
|
|
130
148
|
]);
|
|
131
149
|
return { shapes: shapesComponents.shapes, shapesData: shapesComponents.shapesData };
|
|
132
150
|
};
|