@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
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { Delaunay, bisector, sort } from 'd3';
|
|
2
|
+
import get from 'lodash/get';
|
|
3
|
+
import groupBy from 'lodash/groupBy';
|
|
4
|
+
function getClosestPointsByXValue(x, y, points) {
|
|
5
|
+
var _a, _b;
|
|
6
|
+
const sorted = sort(points, (p) => p.x);
|
|
7
|
+
const closestXIndex = bisector((p) => p.x).center(sorted, x);
|
|
8
|
+
if (closestXIndex === -1) {
|
|
9
|
+
return [];
|
|
10
|
+
}
|
|
11
|
+
const closestX = sorted[closestXIndex].x;
|
|
12
|
+
const closestPoints = sort(points.filter((p) => p.x === closestX), (p) => p.y0);
|
|
13
|
+
let closestYIndex = -1;
|
|
14
|
+
if (y < ((_a = closestPoints[0]) === null || _a === void 0 ? void 0 : _a.y0)) {
|
|
15
|
+
closestYIndex = 0;
|
|
16
|
+
}
|
|
17
|
+
else if (y > ((_b = closestPoints[closestPoints.length - 1]) === null || _b === void 0 ? void 0 : _b.y1)) {
|
|
18
|
+
closestYIndex = closestPoints.length - 1;
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
closestYIndex = closestPoints.findIndex((p) => y > p.y0 && y < p.y1);
|
|
22
|
+
}
|
|
23
|
+
return closestPoints.map((p, i) => ({
|
|
24
|
+
data: p.data,
|
|
25
|
+
series: p.series,
|
|
26
|
+
closest: i === closestYIndex,
|
|
27
|
+
}));
|
|
28
|
+
}
|
|
29
|
+
function getSeriesType(shapeData) {
|
|
30
|
+
return get(shapeData, 'series.type') || get(shapeData, 'point.series.type');
|
|
31
|
+
}
|
|
32
|
+
export function getClosestPoints(args) {
|
|
33
|
+
const { position, shapesData } = args;
|
|
34
|
+
const [pointerX, pointerY] = position;
|
|
35
|
+
const result = [];
|
|
36
|
+
const groups = groupBy(shapesData, getSeriesType);
|
|
37
|
+
Object.entries(groups).forEach(([seriesType, list]) => {
|
|
38
|
+
var _a, _b;
|
|
39
|
+
switch (seriesType) {
|
|
40
|
+
case 'bar-x': {
|
|
41
|
+
const points = list.map((d) => ({
|
|
42
|
+
data: d.data,
|
|
43
|
+
series: d.series,
|
|
44
|
+
x: d.x + d.width / 2,
|
|
45
|
+
y0: d.y,
|
|
46
|
+
y1: d.y + d.height,
|
|
47
|
+
}));
|
|
48
|
+
result.push(...getClosestPointsByXValue(pointerX, pointerY, points));
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
case 'waterfall': {
|
|
52
|
+
const points = list.map((d) => ({
|
|
53
|
+
data: d.data,
|
|
54
|
+
series: d.series,
|
|
55
|
+
x: d.x + d.width / 2,
|
|
56
|
+
y0: d.y,
|
|
57
|
+
y1: d.y + d.height,
|
|
58
|
+
}));
|
|
59
|
+
result.push(...getClosestPointsByXValue(pointerX, pointerY, points));
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
case 'area': {
|
|
63
|
+
const points = list.reduce((acc, d) => {
|
|
64
|
+
Array.prototype.push.apply(acc, d.points.map((p) => ({
|
|
65
|
+
data: p.data,
|
|
66
|
+
series: p.series,
|
|
67
|
+
x: p.x,
|
|
68
|
+
y0: p.y0,
|
|
69
|
+
y1: p.y,
|
|
70
|
+
})));
|
|
71
|
+
return acc;
|
|
72
|
+
}, []);
|
|
73
|
+
result.push(...getClosestPointsByXValue(pointerX, pointerY, points));
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
case 'line': {
|
|
77
|
+
const points = list.reduce((acc, d) => {
|
|
78
|
+
acc.push(...d.points.map((p) => ({
|
|
79
|
+
data: p.data,
|
|
80
|
+
series: p.series,
|
|
81
|
+
x: p.x,
|
|
82
|
+
y0: p.y,
|
|
83
|
+
y1: p.y,
|
|
84
|
+
})));
|
|
85
|
+
return acc;
|
|
86
|
+
}, []);
|
|
87
|
+
result.push(...getClosestPointsByXValue(pointerX, pointerY, points));
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
case 'bar-y': {
|
|
91
|
+
const points = list;
|
|
92
|
+
const sorted = sort(points, (p) => p.y);
|
|
93
|
+
const closestYIndex = bisector((p) => p.y).center(sorted, pointerY);
|
|
94
|
+
let closestPoints = [];
|
|
95
|
+
let closestXIndex = -1;
|
|
96
|
+
if (closestYIndex !== -1) {
|
|
97
|
+
const closestY = sorted[closestYIndex].y;
|
|
98
|
+
closestPoints = sort(points.filter((p) => p.y === closestY), (p) => p.x);
|
|
99
|
+
const lastPoint = closestPoints[closestPoints.length - 1];
|
|
100
|
+
if (pointerX < ((_a = closestPoints[0]) === null || _a === void 0 ? void 0 : _a.x)) {
|
|
101
|
+
closestXIndex = 0;
|
|
102
|
+
}
|
|
103
|
+
else if (lastPoint && pointerX > lastPoint.x + lastPoint.width) {
|
|
104
|
+
closestXIndex = closestPoints.length - 1;
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
closestXIndex = closestPoints.findIndex((p) => pointerX > p.x && pointerX < p.x + p.width);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
result.push(...closestPoints.map((p, i) => ({
|
|
111
|
+
data: p.data,
|
|
112
|
+
series: p.series,
|
|
113
|
+
closest: i === closestXIndex,
|
|
114
|
+
})));
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
case 'scatter': {
|
|
118
|
+
const points = list;
|
|
119
|
+
const delaunayX = Delaunay.from(points, (d) => d.point.x, (d) => d.point.y);
|
|
120
|
+
const closestPoint = points[delaunayX.find(pointerX, pointerY)];
|
|
121
|
+
if (closestPoint) {
|
|
122
|
+
result.push({
|
|
123
|
+
data: closestPoint.point.data,
|
|
124
|
+
series: closestPoint.point.series,
|
|
125
|
+
closest: true,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
case 'pie': {
|
|
131
|
+
const points = list.map((d) => d.segments).flat();
|
|
132
|
+
const closestPoint = points.find((p) => {
|
|
133
|
+
const { center, radius } = p.data.pie;
|
|
134
|
+
const x = pointerX - center[0];
|
|
135
|
+
const y = pointerY - center[1];
|
|
136
|
+
let angle = Math.atan2(y, x) + 0.5 * Math.PI;
|
|
137
|
+
angle = angle < 0 ? Math.PI * 2 + angle : angle;
|
|
138
|
+
const polarRadius = Math.sqrt(x * x + y * y);
|
|
139
|
+
return angle >= p.startAngle && angle <= p.endAngle && polarRadius < radius;
|
|
140
|
+
});
|
|
141
|
+
if (closestPoint) {
|
|
142
|
+
result.push({
|
|
143
|
+
data: closestPoint.data.series.data,
|
|
144
|
+
series: closestPoint.data.series,
|
|
145
|
+
closest: true,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
150
|
+
case 'treemap': {
|
|
151
|
+
const data = list;
|
|
152
|
+
const closestPoint = (_b = data[0]) === null || _b === void 0 ? void 0 : _b.leaves.find((l) => {
|
|
153
|
+
return (pointerX >= l.x0 && pointerX <= l.x1 && pointerY >= l.y0 && pointerY <= l.y1);
|
|
154
|
+
});
|
|
155
|
+
if (closestPoint) {
|
|
156
|
+
result.push({
|
|
157
|
+
data: closestPoint.data,
|
|
158
|
+
series: data[0].series,
|
|
159
|
+
closest: true,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
break;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
return result;
|
|
167
|
+
}
|
|
@@ -7,10 +7,8 @@ export * from './time';
|
|
|
7
7
|
export * from './axis';
|
|
8
8
|
export * from './labels';
|
|
9
9
|
export * from './symbol';
|
|
10
|
+
export * from './series';
|
|
10
11
|
export type AxisDirection = 'x' | 'y';
|
|
11
|
-
export type NodeWithD3Data<T = unknown> = Element & {
|
|
12
|
-
__data__: T;
|
|
13
|
-
};
|
|
14
12
|
type UnknownSeries = {
|
|
15
13
|
type: ChartKitWidgetSeries['type'];
|
|
16
14
|
data: unknown;
|
|
@@ -73,5 +71,3 @@ export declare const getDataCategoryValue: (args: {
|
|
|
73
71
|
data: ChartKitWidgetSeriesData;
|
|
74
72
|
}) => string;
|
|
75
73
|
export declare function getClosestPointsRange(axis: PreparedAxis, points: AxisDomain[]): number | undefined;
|
|
76
|
-
export declare const isNodeContainsD3Data: (node?: Element | null) => node is NodeWithD3Data<unknown>;
|
|
77
|
-
export declare const extractD3DataFromNode: <T extends unknown>(node: NodeWithD3Data<T>) => T;
|
|
@@ -13,6 +13,7 @@ export * from './time';
|
|
|
13
13
|
export * from './axis';
|
|
14
14
|
export * from './labels';
|
|
15
15
|
export * from './symbol';
|
|
16
|
+
export * from './series';
|
|
16
17
|
const CHARTS_WITHOUT_AXIS = ['pie', 'treemap'];
|
|
17
18
|
/**
|
|
18
19
|
* Checks whether the series should be drawn with axes.
|
|
@@ -70,6 +71,16 @@ export const getDomainDataYBySeries = (series) => {
|
|
|
70
71
|
});
|
|
71
72
|
break;
|
|
72
73
|
}
|
|
74
|
+
case 'waterfall': {
|
|
75
|
+
let yValue = 0;
|
|
76
|
+
seriesList.forEach((s) => {
|
|
77
|
+
s.data.forEach((d) => {
|
|
78
|
+
yValue += Number(d.y) || 0;
|
|
79
|
+
acc.push(yValue);
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
break;
|
|
83
|
+
}
|
|
73
84
|
default: {
|
|
74
85
|
seriesList.filter(isSeriesWithNumericalYValues).forEach((s) => {
|
|
75
86
|
acc.push(...s.data.map((d) => d.y));
|
|
@@ -170,10 +181,3 @@ export function getClosestPointsRange(axis, points) {
|
|
|
170
181
|
}
|
|
171
182
|
return points[1] - points[0];
|
|
172
183
|
}
|
|
173
|
-
// https://d3js.org/d3-selection/joining#selection_data
|
|
174
|
-
export const isNodeContainsD3Data = (node) => {
|
|
175
|
-
return Boolean(node && '__data__' in node);
|
|
176
|
-
};
|
|
177
|
-
export const extractD3DataFromNode = (node) => {
|
|
178
|
-
return node.__data__;
|
|
179
|
-
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './waterfall';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './waterfall';
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { WaterfallSeriesData } from '../../../../../types';
|
|
2
|
+
import { PreparedWaterfallSeries } from '../../hooks';
|
|
3
|
+
export declare function getWaterfallPointColor(point: WaterfallSeriesData, series: PreparedWaterfallSeries): string;
|
|
4
|
+
export declare function getWaterfallPointSubtotal(point: WaterfallSeriesData, series: PreparedWaterfallSeries): number | null;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export function getWaterfallPointColor(point, series) {
|
|
2
|
+
if (point.color) {
|
|
3
|
+
return point.color;
|
|
4
|
+
}
|
|
5
|
+
if (point.total) {
|
|
6
|
+
return series.color;
|
|
7
|
+
}
|
|
8
|
+
if (Number(point.y) > 0) {
|
|
9
|
+
return series.positiveColor;
|
|
10
|
+
}
|
|
11
|
+
return series.negativeColor;
|
|
12
|
+
}
|
|
13
|
+
export function getWaterfallPointSubtotal(point, series) {
|
|
14
|
+
const pointIndex = series.data.indexOf(point);
|
|
15
|
+
if (pointIndex === -1) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
return series.data.reduce((sum, d, index) => {
|
|
19
|
+
if (index <= pointIndex) {
|
|
20
|
+
const value = d.total ? 0 : Number(d.y);
|
|
21
|
+
return sum + value;
|
|
22
|
+
}
|
|
23
|
+
return sum;
|
|
24
|
+
}, 0);
|
|
25
|
+
}
|
|
@@ -1266,12 +1266,8 @@ export function prepareConfig(data, options, isMobile, holidays) {
|
|
|
1266
1266
|
},
|
|
1267
1267
|
point: {
|
|
1268
1268
|
events: {
|
|
1269
|
-
click: function (
|
|
1270
|
-
|
|
1271
|
-
this.series.chart.tooltip.hide();
|
|
1272
|
-
this.series.data[this.index].remove();
|
|
1273
|
-
return true;
|
|
1274
|
-
}
|
|
1269
|
+
click: function () {
|
|
1270
|
+
// Prevent slicing of pie segment after clicking it
|
|
1275
1271
|
return false;
|
|
1276
1272
|
},
|
|
1277
1273
|
},
|
|
@@ -9,8 +9,9 @@ import type { PointMarkerOptions } from './marker';
|
|
|
9
9
|
import type { PieSeries, PieSeriesData } from './pie';
|
|
10
10
|
import type { ScatterSeries, ScatterSeriesData } from './scatter';
|
|
11
11
|
import type { TreemapSeries, TreemapSeriesData } from './treemap';
|
|
12
|
-
|
|
13
|
-
export type
|
|
12
|
+
import type { WaterfallSeries, WaterfallSeriesData } from './waterfall';
|
|
13
|
+
export type ChartKitWidgetSeries<T = any> = ScatterSeries<T> | PieSeries<T> | BarXSeries<T> | BarYSeries<T> | LineSeries<T> | AreaSeries<T> | TreemapSeries<T> | WaterfallSeries<T>;
|
|
14
|
+
export type ChartKitWidgetSeriesData<T = any> = ScatterSeriesData<T> | PieSeriesData<T> | BarXSeriesData<T> | BarYSeriesData<T> | LineSeriesData<T> | AreaSeriesData<T> | TreemapSeriesData<T> | WaterfallSeriesData<T>;
|
|
14
15
|
export type DataLabelRendererData<T = any> = {
|
|
15
16
|
data: ChartKitWidgetSeriesData<T>;
|
|
16
17
|
};
|
|
@@ -202,5 +203,23 @@ export type ChartKitWidgetSeriesOptions = {
|
|
|
202
203
|
inactive?: BasicInactiveState;
|
|
203
204
|
};
|
|
204
205
|
};
|
|
206
|
+
waterfall?: {
|
|
207
|
+
/** The maximum allowed pixel width for a column.
|
|
208
|
+
* This prevents the columns from becoming too wide when there is a small number of points in the chart.
|
|
209
|
+
*
|
|
210
|
+
* @default 50
|
|
211
|
+
*/
|
|
212
|
+
barMaxWidth?: number;
|
|
213
|
+
/** Padding between each column or bar, in x axis units.
|
|
214
|
+
*
|
|
215
|
+
* @default 0.1
|
|
216
|
+
* */
|
|
217
|
+
barPadding?: number;
|
|
218
|
+
/** Options for the series states that provide additional styling information to the series. */
|
|
219
|
+
states?: {
|
|
220
|
+
hover?: BasicHoverState;
|
|
221
|
+
inactive?: BasicInactiveState;
|
|
222
|
+
};
|
|
223
|
+
};
|
|
205
224
|
};
|
|
206
225
|
export {};
|
|
@@ -6,6 +6,7 @@ import type { LineSeries, LineSeriesData } from './line';
|
|
|
6
6
|
import type { PieSeries, PieSeriesData } from './pie';
|
|
7
7
|
import type { ScatterSeries, ScatterSeriesData } from './scatter';
|
|
8
8
|
import type { TreemapSeries, TreemapSeriesData } from './treemap';
|
|
9
|
+
import type { WaterfallSeries, WaterfallSeriesData } from './waterfall';
|
|
9
10
|
export type TooltipDataChunkBarX<T = any> = {
|
|
10
11
|
data: BarXSeriesData<T>;
|
|
11
12
|
series: BarXSeries<T>;
|
|
@@ -50,7 +51,13 @@ export type TooltipDataChunkTreemap<T = any> = {
|
|
|
50
51
|
data: TreemapSeriesData<T>;
|
|
51
52
|
series: TreemapSeries<T>;
|
|
52
53
|
};
|
|
53
|
-
export type
|
|
54
|
+
export type TooltipDataChunkWaterfall<T = any> = {
|
|
55
|
+
data: WaterfallSeriesData<T>;
|
|
56
|
+
series: WaterfallSeries<T>;
|
|
57
|
+
};
|
|
58
|
+
export type TooltipDataChunk<T = any> = (TooltipDataChunkBarX<T> | TooltipDataChunkBarY<T> | TooltipDataChunkPie<T> | TooltipDataChunkScatter<T> | TooltipDataChunkLine<T> | TooltipDataChunkArea<T> | TooltipDataChunkTreemap<T> | TooltipDataChunkWaterfall<T>) & {
|
|
59
|
+
closest?: boolean;
|
|
60
|
+
};
|
|
54
61
|
export type ChartKitWidgetTooltip<T = any> = {
|
|
55
62
|
enabled?: boolean;
|
|
56
63
|
/** Specifies the renderer for the tooltip. If returned null default tooltip renderer will be used. */
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { SeriesType } from '../../constants';
|
|
2
|
+
import type { BaseSeries, BaseSeriesData } from './base';
|
|
3
|
+
import { ChartKitWidgetLegend, RectLegendSymbolOptions } from './legend';
|
|
4
|
+
export type WaterfallSeriesData<T = any> = BaseSeriesData<T> & {
|
|
5
|
+
/**
|
|
6
|
+
* The `x` value. Depending on the context , it may represents:
|
|
7
|
+
* - numeric value (for `linear` x axis)
|
|
8
|
+
* - timestamp value (for `datetime` x axis)
|
|
9
|
+
* - x axis category value (for `category` x axis). If the type is a string, then it is a category value itself. If the type is a number, then it is the index of an element in the array of categories described in `xAxis.categories`
|
|
10
|
+
*/
|
|
11
|
+
x?: string | number;
|
|
12
|
+
/**
|
|
13
|
+
* The `y` value. Depending on the context , it may represents:
|
|
14
|
+
* - numeric value (for `linear` y axis)
|
|
15
|
+
*/
|
|
16
|
+
y?: number;
|
|
17
|
+
/** Data label value of the point. If not specified, the y value is used. */
|
|
18
|
+
label?: string | number;
|
|
19
|
+
/** Individual opacity for the point. */
|
|
20
|
+
opacity?: number;
|
|
21
|
+
/** When this property is true, the point display the total sum across the entire series. */
|
|
22
|
+
total?: boolean;
|
|
23
|
+
};
|
|
24
|
+
export type WaterfallSeries<T = any> = BaseSeries & {
|
|
25
|
+
type: typeof SeriesType.Waterfall;
|
|
26
|
+
data: WaterfallSeriesData<T>[];
|
|
27
|
+
/** The name of the series (used in legend, tooltip etc). */
|
|
28
|
+
name: string;
|
|
29
|
+
/** The main color of the series (hex, rgba). */
|
|
30
|
+
color?: string;
|
|
31
|
+
/** The color used for positive values. If it is not specified, the general color of the series is used. */
|
|
32
|
+
positiveColor?: string;
|
|
33
|
+
/** The color used for negative values. If it is not specified, the general color of the series is used. */
|
|
34
|
+
negativeColor?: string;
|
|
35
|
+
/** Individual series legend options. Has higher priority than legend options in widget data. */
|
|
36
|
+
legend?: ChartKitWidgetLegend & {
|
|
37
|
+
symbol?: RectLegendSymbolOptions;
|
|
38
|
+
};
|
|
39
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import type { Dispatch } from 'd3';
|
|
3
|
-
import type { ShapeData } from '../../hooks';
|
|
4
|
-
type Args = {
|
|
5
|
-
boundsWidth: number;
|
|
6
|
-
boundsHeight: number;
|
|
7
|
-
dispatcher: Dispatch<object>;
|
|
8
|
-
shapesData: ShapeData[];
|
|
9
|
-
svgContainer: SVGSVGElement | null;
|
|
10
|
-
};
|
|
11
|
-
export declare const TooltipTriggerArea: (args: Args) => React.JSX.Element;
|
|
12
|
-
export {};
|
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { bisector, group, pointer, sort } from 'd3';
|
|
3
|
-
import get from 'lodash/get';
|
|
4
|
-
import throttle from 'lodash/throttle';
|
|
5
|
-
import { extractD3DataFromNode, isNodeContainsD3Data } from '../../utils';
|
|
6
|
-
const THROTTLE_DELAY = 50;
|
|
7
|
-
const isNodeContainsData = (node) => {
|
|
8
|
-
return isNodeContainsD3Data(node);
|
|
9
|
-
};
|
|
10
|
-
function getBarXShapeData(args) {
|
|
11
|
-
var _a;
|
|
12
|
-
const { shapesData, point: [pointerX, pointerY], top, left, xData, container, } = args;
|
|
13
|
-
const barWidthOffset = shapesData[0].width / 2;
|
|
14
|
-
const xPosition = pointerX - left - barWidthOffset;
|
|
15
|
-
const xDataIndex = bisector((d) => d.x).center(xData, xPosition);
|
|
16
|
-
const xNodes = Array.from((container === null || container === void 0 ? void 0 : container.querySelectorAll(`[x="${(_a = xData[xDataIndex]) === null || _a === void 0 ? void 0 : _a.x}"]`)) || []);
|
|
17
|
-
if (xNodes.length === 1 && isNodeContainsData(xNodes[0])) {
|
|
18
|
-
return [extractD3DataFromNode(xNodes[0])];
|
|
19
|
-
}
|
|
20
|
-
if (xNodes.length > 1 && xNodes.every(isNodeContainsData)) {
|
|
21
|
-
const yPosition = pointerY - top;
|
|
22
|
-
const xyNode = xNodes.find((node, i) => {
|
|
23
|
-
const { y, height } = extractD3DataFromNode(node);
|
|
24
|
-
if (i === xNodes.length - 1) {
|
|
25
|
-
return yPosition <= y + height;
|
|
26
|
-
}
|
|
27
|
-
return yPosition >= y && yPosition <= y + height;
|
|
28
|
-
});
|
|
29
|
-
if (xyNode) {
|
|
30
|
-
return [extractD3DataFromNode(xyNode)];
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
return [];
|
|
34
|
-
}
|
|
35
|
-
function getLineShapesData(args) {
|
|
36
|
-
const { xData, point: [pointerX], } = args;
|
|
37
|
-
const xDataIndex = bisector((d) => d.x).center(xData, pointerX);
|
|
38
|
-
const selectedLineShape = xData[xDataIndex];
|
|
39
|
-
if (selectedLineShape) {
|
|
40
|
-
return [
|
|
41
|
-
{
|
|
42
|
-
series: selectedLineShape.series,
|
|
43
|
-
data: selectedLineShape.data,
|
|
44
|
-
},
|
|
45
|
-
];
|
|
46
|
-
}
|
|
47
|
-
return [];
|
|
48
|
-
}
|
|
49
|
-
function getBarYData(args) {
|
|
50
|
-
var _a;
|
|
51
|
-
const { data, point: [pointerX, pointerY], } = args;
|
|
52
|
-
const yDataIndex = bisector((d) => d.y).center(data, pointerY);
|
|
53
|
-
const shapesByY = ((_a = data[yDataIndex]) === null || _a === void 0 ? void 0 : _a.items) || [];
|
|
54
|
-
const xDataIndex = bisector((d) => d.x).left(shapesByY, pointerX);
|
|
55
|
-
const result = shapesByY[Math.min(xDataIndex, shapesByY.length - 1)];
|
|
56
|
-
return result
|
|
57
|
-
? [
|
|
58
|
-
{
|
|
59
|
-
series: result.series,
|
|
60
|
-
data: result.data,
|
|
61
|
-
},
|
|
62
|
-
]
|
|
63
|
-
: [];
|
|
64
|
-
}
|
|
65
|
-
export const TooltipTriggerArea = (args) => {
|
|
66
|
-
const { boundsWidth, boundsHeight, dispatcher, shapesData, svgContainer } = args;
|
|
67
|
-
const rectRef = React.useRef(null);
|
|
68
|
-
const xBarData = React.useMemo(() => {
|
|
69
|
-
const result = shapesData
|
|
70
|
-
.filter((sd) => get(sd, 'series.type') === 'bar-x')
|
|
71
|
-
.map((sd) => ({ x: sd.x, data: sd }));
|
|
72
|
-
return sort(result, (item) => item.x);
|
|
73
|
-
}, [shapesData]);
|
|
74
|
-
const xLineData = React.useMemo(() => {
|
|
75
|
-
const result = shapesData
|
|
76
|
-
.filter((sd) => ['line', 'area'].includes(sd.series.type))
|
|
77
|
-
.reduce((acc, sd) => {
|
|
78
|
-
return acc.concat(sd.points.map((d) => ({
|
|
79
|
-
x: d.x,
|
|
80
|
-
data: d.data,
|
|
81
|
-
series: d.series,
|
|
82
|
-
})));
|
|
83
|
-
}, []);
|
|
84
|
-
return sort(result, (item) => item.x);
|
|
85
|
-
}, [shapesData]);
|
|
86
|
-
const barYData = React.useMemo(() => {
|
|
87
|
-
const barYShapeData = shapesData.filter((sd) => get(sd, 'series.type') === 'bar-y');
|
|
88
|
-
const result = Array.from(group(barYShapeData, (sd) => sd.y)).map(([y, shapes]) => {
|
|
89
|
-
const yValue = y + shapes[0].height / 2;
|
|
90
|
-
return {
|
|
91
|
-
y: yValue,
|
|
92
|
-
items: sort(shapes.map((shape) => {
|
|
93
|
-
const preparedData = shape;
|
|
94
|
-
return {
|
|
95
|
-
x: preparedData.x + preparedData.width,
|
|
96
|
-
data: preparedData.data,
|
|
97
|
-
series: preparedData.series,
|
|
98
|
-
};
|
|
99
|
-
}), (item) => item.x),
|
|
100
|
-
};
|
|
101
|
-
});
|
|
102
|
-
return sort(result, (item) => item.y);
|
|
103
|
-
}, [shapesData]);
|
|
104
|
-
const getShapeData = (point) => {
|
|
105
|
-
var _a, _b;
|
|
106
|
-
const { left: ownLeft, top: ownTop } = ((_a = rectRef.current) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect()) || {
|
|
107
|
-
left: 0,
|
|
108
|
-
top: 0,
|
|
109
|
-
};
|
|
110
|
-
const { left: containerLeft, top: containerTop } = (svgContainer === null || svgContainer === void 0 ? void 0 : svgContainer.getBoundingClientRect()) || {
|
|
111
|
-
left: 0,
|
|
112
|
-
top: 0,
|
|
113
|
-
};
|
|
114
|
-
const [pointerX, pointerY] = point; //pointer(e, svgContainer);
|
|
115
|
-
const result = [];
|
|
116
|
-
result === null || result === void 0 ? void 0 : result.push(...getBarXShapeData({
|
|
117
|
-
shapesData,
|
|
118
|
-
point: [pointerX, pointerY],
|
|
119
|
-
left: ownLeft - containerLeft,
|
|
120
|
-
top: ownTop - containerTop,
|
|
121
|
-
xData: xBarData,
|
|
122
|
-
container: (_b = rectRef.current) === null || _b === void 0 ? void 0 : _b.parentElement,
|
|
123
|
-
}), ...getLineShapesData({
|
|
124
|
-
xData: xLineData,
|
|
125
|
-
point: [pointerX - (ownLeft - containerLeft), pointerY - (ownTop - containerTop)],
|
|
126
|
-
}), ...getBarYData({
|
|
127
|
-
data: barYData,
|
|
128
|
-
point: [pointerX - (ownLeft - containerLeft), pointerY - (ownTop - containerTop)],
|
|
129
|
-
}));
|
|
130
|
-
return result;
|
|
131
|
-
};
|
|
132
|
-
const handleMouseMove = (e) => {
|
|
133
|
-
const [pointerX, pointerY] = pointer(e, svgContainer);
|
|
134
|
-
const hoverShapeData = getShapeData([pointerX, pointerY]);
|
|
135
|
-
if (hoverShapeData.length) {
|
|
136
|
-
const position = [pointerX, pointerY];
|
|
137
|
-
dispatcher.call('hover-shape', e.target, hoverShapeData, position);
|
|
138
|
-
}
|
|
139
|
-
};
|
|
140
|
-
const throttledHandleMouseMove = throttle(handleMouseMove, THROTTLE_DELAY);
|
|
141
|
-
const handleMouseLeave = () => {
|
|
142
|
-
throttledHandleMouseMove.cancel();
|
|
143
|
-
dispatcher.call('hover-shape', {}, undefined);
|
|
144
|
-
};
|
|
145
|
-
const handleClick = (e) => {
|
|
146
|
-
const [pointerX, pointerY] = pointer(e, svgContainer);
|
|
147
|
-
const shapeData = getShapeData([pointerX, pointerY]);
|
|
148
|
-
if (shapeData.length) {
|
|
149
|
-
dispatcher.call('click-chart', undefined, { point: get(shapeData, '[0].data'), series: get(shapeData, '[0].series') }, e);
|
|
150
|
-
}
|
|
151
|
-
};
|
|
152
|
-
return (React.createElement("rect", { ref: rectRef, width: boundsWidth, height: boundsHeight, fill: "transparent", onMouseMove: throttledHandleMouseMove, onMouseLeave: handleMouseLeave, onClick: handleClick }));
|
|
153
|
-
};
|