@gravity-ui/chartkit 4.17.0 → 4.18.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/i18n/keysets/en.json +2 -1
- package/build/i18n/keysets/ru.json +2 -1
- package/build/plugins/d3/examples/area/PercentStacking.d.ts +2 -0
- package/build/plugins/d3/examples/area/PercentStacking.js +47 -0
- package/build/plugins/d3/examples/bar-x/PercentStack.d.ts +2 -0
- package/build/plugins/d3/examples/bar-x/PercentStack.js +47 -0
- package/build/plugins/d3/examples/bar-y/PercentStacking.d.ts +2 -0
- package/build/plugins/d3/examples/bar-y/PercentStacking.js +49 -0
- package/build/plugins/d3/examples/pie/DonutWithTotals.d.ts +2 -0
- package/build/plugins/d3/examples/pie/DonutWithTotals.js +36 -0
- package/build/plugins/d3/examples/scatter/Basic.js +0 -21
- package/build/plugins/d3/index.d.ts +1 -0
- package/build/plugins/d3/index.js +1 -0
- package/build/plugins/d3/renderer/D3Widget.js +4 -2
- package/build/plugins/d3/renderer/components/Tooltip/TooltipTriggerArea.js +5 -4
- package/build/plugins/d3/renderer/hooks/useSeries/constants.d.ts +2 -0
- package/build/plugins/d3/renderer/hooks/useSeries/constants.js +7 -1
- package/build/plugins/d3/renderer/hooks/useSeries/prepare-area.d.ts +2 -2
- package/build/plugins/d3/renderer/hooks/useSeries/prepare-area.js +2 -8
- package/build/plugins/d3/renderer/hooks/useSeries/{prepare-line-series.d.ts → prepare-line.d.ts} +2 -2
- package/build/plugins/d3/renderer/hooks/useSeries/{prepare-line-series.js → prepare-line.js} +2 -8
- package/build/plugins/d3/renderer/hooks/useSeries/prepare-pie.js +1 -0
- package/build/plugins/d3/renderer/hooks/useSeries/prepare-scatter.d.ts +11 -0
- package/build/plugins/d3/renderer/hooks/useSeries/prepare-scatter.js +45 -0
- package/build/plugins/d3/renderer/hooks/useSeries/prepareSeries.js +3 -24
- package/build/plugins/d3/renderer/hooks/useSeries/types.d.ts +24 -4
- package/build/plugins/d3/renderer/hooks/useSeries/utils.d.ts +1 -1
- package/build/plugins/d3/renderer/hooks/useSeries/utils.js +1 -1
- package/build/plugins/d3/renderer/hooks/useShapes/area/prepare-data.js +18 -0
- package/build/plugins/d3/renderer/hooks/useShapes/bar-x/prepare-data.js +15 -3
- package/build/plugins/d3/renderer/hooks/useShapes/bar-y/prepare-data.js +15 -4
- package/build/plugins/d3/renderer/hooks/useShapes/index.d.ts +2 -2
- package/build/plugins/d3/renderer/hooks/useShapes/marker.d.ts +4 -3
- package/build/plugins/d3/renderer/hooks/useShapes/marker.js +18 -19
- package/build/plugins/d3/renderer/hooks/useShapes/pie/index.js +14 -0
- package/build/plugins/d3/renderer/hooks/useShapes/scatter/index.d.ts +1 -2
- package/build/plugins/d3/renderer/hooks/useShapes/scatter/index.js +44 -56
- package/build/plugins/d3/renderer/hooks/useShapes/scatter/prepare-data.d.ts +1 -10
- package/build/plugins/d3/renderer/hooks/useShapes/scatter/prepare-data.js +6 -8
- package/build/plugins/d3/renderer/hooks/useShapes/scatter/types.d.ts +15 -0
- package/build/plugins/d3/renderer/hooks/useShapes/scatter/types.js +1 -0
- package/build/plugins/d3/renderer/hooks/useShapes/utils.d.ts +1 -1
- package/build/plugins/d3/renderer/utils/symbol.d.ts +1 -3
- package/build/plugins/d3/renderer/validation/index.js +18 -1
- package/build/plugins/d3/utils/index.d.ts +4 -0
- package/build/plugins/d3/utils/index.js +4 -0
- package/build/plugins/d3/utils/pie-center-text.d.ts +7 -0
- package/build/plugins/d3/utils/pie-center-text.js +23 -0
- package/build/types/widget-data/area.d.ts +2 -2
- package/build/types/widget-data/line.d.ts +1 -5
- package/build/types/widget-data/marker.d.ts +2 -0
- package/build/types/widget-data/pie.d.ts +11 -0
- package/build/types/widget-data/series.d.ts +9 -4
- package/build/types/widget-data/tooltip.d.ts +5 -1
- package/package.json +5 -3
|
@@ -50,6 +50,8 @@ function getXValues(series, xAxis, xScale) {
|
|
|
50
50
|
}
|
|
51
51
|
export const prepareAreaData = (args) => {
|
|
52
52
|
const { series, xAxis, xScale, yScale } = args;
|
|
53
|
+
const yLinearScale = yScale;
|
|
54
|
+
const plotHeight = yLinearScale(yLinearScale.domain()[0]);
|
|
53
55
|
const yAxis = args.yAxis[0];
|
|
54
56
|
const [_xMin, xRangeMax] = xScale.range();
|
|
55
57
|
const xMax = xRangeMax / (1 - xAxis.maxPadding);
|
|
@@ -109,6 +111,22 @@ export const prepareAreaData = (args) => {
|
|
|
109
111
|
});
|
|
110
112
|
return acc;
|
|
111
113
|
}, []);
|
|
114
|
+
if (series.some((s) => s.stacking === 'percent')) {
|
|
115
|
+
xValues.forEach(([x], index) => {
|
|
116
|
+
const stackHeight = accumulatedYValues.get(x) || 0;
|
|
117
|
+
let acc = 0;
|
|
118
|
+
const ratio = plotHeight / stackHeight;
|
|
119
|
+
seriesStackData.forEach((item) => {
|
|
120
|
+
const point = item.points[index];
|
|
121
|
+
if (point) {
|
|
122
|
+
const height = (point.y0 - point.y) * ratio;
|
|
123
|
+
point.y0 = plotHeight - height - acc;
|
|
124
|
+
point.y = point.y0 + height;
|
|
125
|
+
acc += height;
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
}
|
|
112
130
|
return result.concat(seriesStackData);
|
|
113
131
|
}, []);
|
|
114
132
|
};
|
|
@@ -25,6 +25,8 @@ function getLabelData(d) {
|
|
|
25
25
|
}
|
|
26
26
|
export const prepareBarXData = (args) => {
|
|
27
27
|
const { series, seriesOptions, xAxis, xScale, yScale } = args;
|
|
28
|
+
const yLinearScale = yScale;
|
|
29
|
+
const plotHeight = yLinearScale(yLinearScale.domain()[0]);
|
|
28
30
|
const categories = get(xAxis, 'categories', []);
|
|
29
31
|
const barMaxWidth = get(seriesOptions, 'bar-x.barMaxWidth');
|
|
30
32
|
const barPadding = get(seriesOptions, 'bar-x.barPadding');
|
|
@@ -93,6 +95,7 @@ export const prepareBarXData = (args) => {
|
|
|
93
95
|
const currentGroupWidth = rectWidth * stacks.length + rectGap * (stacks.length - 1);
|
|
94
96
|
stacks.forEach((yValues, groupItemIndex) => {
|
|
95
97
|
let stackHeight = 0;
|
|
98
|
+
const stackItems = [];
|
|
96
99
|
const sortedData = sortKey
|
|
97
100
|
? sort(yValues, (a, b) => comparator(get(a, sortKey), get(b, sortKey)))
|
|
98
101
|
: yValues;
|
|
@@ -107,9 +110,8 @@ export const prepareBarXData = (args) => {
|
|
|
107
110
|
xCenter = xLinearScale(Number(xValue));
|
|
108
111
|
}
|
|
109
112
|
const x = xCenter - currentGroupWidth / 2 + (rectWidth + rectGap) * groupItemIndex;
|
|
110
|
-
const yLinearScale = yScale;
|
|
111
113
|
const y = yLinearScale(yValue.data.y);
|
|
112
|
-
const height =
|
|
114
|
+
const height = plotHeight - y;
|
|
113
115
|
const barData = {
|
|
114
116
|
x,
|
|
115
117
|
y: y - stackHeight,
|
|
@@ -119,9 +121,19 @@ export const prepareBarXData = (args) => {
|
|
|
119
121
|
series: yValue.series,
|
|
120
122
|
};
|
|
121
123
|
barData.label = getLabelData(barData);
|
|
122
|
-
|
|
124
|
+
stackItems.push(barData);
|
|
123
125
|
stackHeight += height + 1;
|
|
124
126
|
});
|
|
127
|
+
if (series.some((s) => s.stacking === 'percent')) {
|
|
128
|
+
let acc = 0;
|
|
129
|
+
const ratio = plotHeight / (stackHeight - stackItems.length);
|
|
130
|
+
stackItems.forEach((item) => {
|
|
131
|
+
item.height = item.height * ratio;
|
|
132
|
+
item.y = plotHeight - item.height - acc;
|
|
133
|
+
acc += item.height + 1;
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
result.push(...stackItems);
|
|
125
137
|
});
|
|
126
138
|
});
|
|
127
139
|
return result;
|
|
@@ -49,6 +49,8 @@ function getBandWidth(series, yAxis, yScale) {
|
|
|
49
49
|
}
|
|
50
50
|
export const prepareBarYData = (args) => {
|
|
51
51
|
const { series, seriesOptions, yAxis, xScale, yScale } = args;
|
|
52
|
+
const xLinearScale = xScale;
|
|
53
|
+
const plotWidth = xLinearScale(xLinearScale.domain()[1]);
|
|
52
54
|
const barMaxWidth = get(seriesOptions, 'bar-y.barMaxWidth');
|
|
53
55
|
const barPadding = get(seriesOptions, 'bar-y.barPadding');
|
|
54
56
|
const groupPadding = get(seriesOptions, 'bar-y.groupPadding');
|
|
@@ -80,6 +82,7 @@ export const prepareBarYData = (args) => {
|
|
|
80
82
|
const currentBarHeight = barHeight * stacks.length + rectGap * (stacks.length - 1);
|
|
81
83
|
stacks.forEach((measureValues, groupItemIndex) => {
|
|
82
84
|
let stackSum = 0;
|
|
85
|
+
const stackItems = [];
|
|
83
86
|
const sortedData = sortKey
|
|
84
87
|
? sort(measureValues, (a, b) => comparator(get(a, sortKey), get(b, sortKey)))
|
|
85
88
|
: measureValues;
|
|
@@ -94,10 +97,8 @@ export const prepareBarYData = (args) => {
|
|
|
94
97
|
center = scale(Number(yValue));
|
|
95
98
|
}
|
|
96
99
|
const y = center - currentBarHeight / 2 + (barHeight + rectGap) * groupItemIndex;
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
const width = x - xLinearScale(xLinearScale.domain()[0]);
|
|
100
|
-
result.push({
|
|
100
|
+
const width = xLinearScale(data.x);
|
|
101
|
+
stackItems.push({
|
|
101
102
|
x: stackSum,
|
|
102
103
|
y,
|
|
103
104
|
width,
|
|
@@ -108,6 +109,16 @@ export const prepareBarYData = (args) => {
|
|
|
108
109
|
});
|
|
109
110
|
stackSum += width + 1;
|
|
110
111
|
});
|
|
112
|
+
if (series.some((s) => s.stacking === 'percent')) {
|
|
113
|
+
let acc = 0;
|
|
114
|
+
const ratio = plotWidth / (stackSum - stackItems.length);
|
|
115
|
+
stackItems.forEach((item) => {
|
|
116
|
+
item.width = item.width * ratio;
|
|
117
|
+
item.x = acc;
|
|
118
|
+
acc += item.width;
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
result.push(...stackItems);
|
|
111
122
|
});
|
|
112
123
|
});
|
|
113
124
|
return result;
|
|
@@ -4,12 +4,12 @@ import type { PreparedAxis } from '../useChartOptions/types';
|
|
|
4
4
|
import type { ChartScale } from '../useAxisScales';
|
|
5
5
|
import type { PreparedSeries, PreparedSeriesOptions } from '../';
|
|
6
6
|
import type { PreparedBarXData } from './bar-x';
|
|
7
|
-
import type { PreparedScatterData } from './scatter';
|
|
7
|
+
import type { PreparedScatterData } from './scatter/types';
|
|
8
8
|
import type { PreparedPieData } from './pie/types';
|
|
9
9
|
import type { PreparedLineData } from './line/types';
|
|
10
10
|
import type { PreparedBarYData } from './bar-y/types';
|
|
11
11
|
export type { PreparedBarXData } from './bar-x';
|
|
12
|
-
export type { PreparedScatterData } from './scatter';
|
|
12
|
+
export type { PreparedScatterData } from './scatter/types';
|
|
13
13
|
import type { PreparedAreaData } from './area/types';
|
|
14
14
|
import './styles.css';
|
|
15
15
|
export type ShapeData = PreparedBarXData | PreparedBarYData | PreparedScatterData | PreparedLineData | PreparedPieData | PreparedAreaData;
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { BaseType, Selection } from 'd3';
|
|
2
2
|
import { MarkerData as LineMarkerData } from './line/types';
|
|
3
3
|
import { MarkerData as AreaMarkerData } from './area/types';
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
import { MarkerData as ScatterMarkerData } from './scatter/types';
|
|
5
|
+
type MarkerData = LineMarkerData | AreaMarkerData | ScatterMarkerData;
|
|
6
|
+
export declare function renderMarker<T extends MarkerData>(selection: Selection<BaseType | SVGGElement, T, SVGGElement, unknown>): Selection<BaseType | SVGGElement, T, SVGGElement, unknown>;
|
|
6
7
|
export declare function getMarkerVisibility(d: MarkerData): "" | "hidden";
|
|
7
8
|
export declare function getMarkerHaloVisibility(d: MarkerData): "" | "hidden";
|
|
8
9
|
export declare function setMarker<T extends BaseType, D extends MarkerData>(selection: Selection<T, D, BaseType | null, unknown>, state: 'normal' | 'hover'): void;
|
|
9
|
-
export declare function getMarkerSymbol(type:
|
|
10
|
+
export declare function getMarkerSymbol(type: "circle" | "diamond" | "square" | "triangle" | "triangle-down" | undefined, radius: number): string | null;
|
|
10
11
|
export declare function selectMarkerHalo<T>(parentSelection: Selection<BaseType, T, null, undefined>): Selection<BaseType, T, null, undefined>;
|
|
11
12
|
export declare function selectMarkerSymbol<T>(parentSelection: Selection<BaseType, T, null, undefined>): Selection<BaseType, T, null, undefined>;
|
|
12
13
|
export {};
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
import { symbol
|
|
1
|
+
import { symbol } from 'd3';
|
|
2
2
|
import { block } from '../../../../../utils/cn';
|
|
3
|
+
import { SymbolType } from '../../../../../constants';
|
|
4
|
+
import { getSymbol } from '../../utils';
|
|
5
|
+
import get from 'lodash/get';
|
|
3
6
|
const b = block('d3-marker');
|
|
4
7
|
const haloClassName = b('halo');
|
|
5
8
|
const symbolClassName = b('symbol');
|
|
@@ -14,9 +17,11 @@ export function renderMarker(selection) {
|
|
|
14
17
|
.append('path')
|
|
15
18
|
.attr('class', haloClassName)
|
|
16
19
|
.attr('d', (d) => {
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
+
const series = d.point.series;
|
|
21
|
+
const type = series.marker.states.normal.symbol;
|
|
22
|
+
const radius = get(d.point.data, 'radius', series.marker.states.hover.radius);
|
|
23
|
+
const haloSize = series.marker.states.hover.halo.size;
|
|
24
|
+
return getMarkerSymbol(type, radius + haloSize);
|
|
20
25
|
})
|
|
21
26
|
.attr('fill', (d) => d.point.series.color)
|
|
22
27
|
.attr('opacity', (d) => d.point.series.marker.states.hover.halo.opacity)
|
|
@@ -42,25 +47,19 @@ export function getMarkerHaloVisibility(d) {
|
|
|
42
47
|
export function setMarker(selection, state) {
|
|
43
48
|
selection
|
|
44
49
|
.attr('d', (d) => {
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
50
|
+
const series = d.point.series;
|
|
51
|
+
const type = series.marker.states.normal.symbol;
|
|
52
|
+
const radius = get(d.point.data, 'radius', series.marker.states[state].radius);
|
|
53
|
+
const size = radius + series.marker.states[state].borderWidth;
|
|
54
|
+
return getMarkerSymbol(type, size);
|
|
48
55
|
})
|
|
49
56
|
.attr('stroke-width', (d) => d.point.series.marker.states[state].borderWidth)
|
|
50
57
|
.attr('stroke', (d) => d.point.series.marker.states[state].borderColor);
|
|
51
58
|
}
|
|
52
|
-
export function getMarkerSymbol(type, radius) {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
return symbol(symbolSquare, size)();
|
|
57
|
-
}
|
|
58
|
-
case 'circle':
|
|
59
|
-
default: {
|
|
60
|
-
const size = Math.pow(radius, 2) * Math.PI;
|
|
61
|
-
return symbol(symbolCircle, size)();
|
|
62
|
-
}
|
|
63
|
-
}
|
|
59
|
+
export function getMarkerSymbol(type = SymbolType.Circle, radius) {
|
|
60
|
+
const symbolFn = getSymbol(type);
|
|
61
|
+
const size = Math.pow(radius, 2) * Math.PI;
|
|
62
|
+
return symbol(symbolFn, size)();
|
|
64
63
|
}
|
|
65
64
|
export function selectMarkerHalo(parentSelection) {
|
|
66
65
|
return parentSelection.select(`.${haloClassName}`);
|
|
@@ -34,6 +34,7 @@ export function PieSeriesShapes(args) {
|
|
|
34
34
|
})
|
|
35
35
|
.style('stroke', (pieData) => pieData.borderColor)
|
|
36
36
|
.style('stroke-width', (pieData) => pieData.borderWidth);
|
|
37
|
+
// Render halo appearing outside the hovered slice
|
|
37
38
|
shapesSelection
|
|
38
39
|
.selectAll('halo')
|
|
39
40
|
.data((pieData) => {
|
|
@@ -55,6 +56,7 @@ export function PieSeriesShapes(args) {
|
|
|
55
56
|
.attr('opacity', (d) => d.data.pie.halo.opacity)
|
|
56
57
|
.attr('z-index', -1)
|
|
57
58
|
.attr('visibility', getHaloVisibility);
|
|
59
|
+
// Render segments
|
|
58
60
|
shapesSelection
|
|
59
61
|
.selectAll(segmentSelector)
|
|
60
62
|
.data((pieData) => pieData.segments)
|
|
@@ -101,6 +103,18 @@ export function PieSeriesShapes(args) {
|
|
|
101
103
|
.attr('stroke-linejoin', 'round')
|
|
102
104
|
.attr('stroke-linecap', 'round')
|
|
103
105
|
.style('fill', 'none');
|
|
106
|
+
// Render custom shapes if defined
|
|
107
|
+
shapesSelection.each(function (d, index, nodes) {
|
|
108
|
+
var _a, _b;
|
|
109
|
+
const customShape = (_b = (_a = d.series).renderCustomShape) === null || _b === void 0 ? void 0 : _b.call(_a, {
|
|
110
|
+
series: {
|
|
111
|
+
innerRadius: d.innerRadius,
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
if (customShape) {
|
|
115
|
+
nodes[index].append(customShape);
|
|
116
|
+
}
|
|
117
|
+
});
|
|
104
118
|
const eventName = `hover-shape.pie`;
|
|
105
119
|
const hoverOptions = get(seriesOptions, 'pie.states.hover');
|
|
106
120
|
const inactiveOptions = get(seriesOptions, 'pie.states.inactive');
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { Dispatch } from 'd3';
|
|
3
3
|
import { PreparedSeriesOptions } from '../../useSeries/types';
|
|
4
|
-
import type { PreparedScatterData } from './
|
|
4
|
+
import type { PreparedScatterData } from './types';
|
|
5
5
|
export { prepareScatterData } from './prepare-data';
|
|
6
|
-
export type { PreparedScatterData } from './prepare-data';
|
|
7
6
|
type ScatterSeriesShapeProps = {
|
|
8
7
|
dispatcher: Dispatch<object>;
|
|
9
8
|
preparedData: PreparedScatterData[];
|
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import get from 'lodash/get';
|
|
3
|
-
import {
|
|
3
|
+
import { pointer, select } from 'd3';
|
|
4
4
|
import { block } from '../../../../../../utils/cn';
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { SymbolType } from '../../../../../../constants';
|
|
5
|
+
import { setActiveState, shapeKey } from '../utils';
|
|
6
|
+
import { getMarkerHaloVisibility, renderMarker, selectMarkerHalo, selectMarkerSymbol, setMarker, } from '../marker';
|
|
8
7
|
export { prepareScatterData } from './prepare-data';
|
|
9
8
|
const b = block('d3-scatter');
|
|
10
|
-
const EMPTY_SELECTION = null;
|
|
11
|
-
const isNodeContainsScatterData = (node) => {
|
|
12
|
-
return isNodeContainsD3Data(node);
|
|
13
|
-
};
|
|
14
9
|
export function ScatterSeriesShape(props) {
|
|
15
10
|
const { dispatcher, preparedData, seriesOptions, svgContainer } = props;
|
|
16
11
|
const ref = React.useRef(null);
|
|
@@ -21,30 +16,29 @@ export function ScatterSeriesShape(props) {
|
|
|
21
16
|
const svgElement = select(ref.current);
|
|
22
17
|
const hoverOptions = get(seriesOptions, 'scatter.states.hover');
|
|
23
18
|
const inactiveOptions = get(seriesOptions, 'scatter.states.inactive');
|
|
19
|
+
svgElement.selectAll('*').remove();
|
|
24
20
|
const selection = svgElement
|
|
25
21
|
.selectAll('path')
|
|
26
22
|
.data(preparedData, shapeKey)
|
|
27
|
-
.join(
|
|
28
|
-
.
|
|
29
|
-
|
|
30
|
-
const scatterSymbol = getSymbol(symbolType);
|
|
31
|
-
// D3 takes size as square pixels, so we need to make square pixels size by multiplying
|
|
32
|
-
// https://d3js.org/d3-shape/symbol#symbol
|
|
33
|
-
return symbol(scatterSymbol, d.size * d.size)();
|
|
34
|
-
})
|
|
35
|
-
.attr('transform', (d) => {
|
|
36
|
-
return 'translate(' + d.cx + ',' + d.cy + ')';
|
|
37
|
-
})
|
|
38
|
-
.attr('fill', (d) => d.data.color || d.series.color || '');
|
|
23
|
+
.join('g')
|
|
24
|
+
.call(renderMarker)
|
|
25
|
+
.attr('fill', (d) => d.point.data.color || d.point.series.color || '');
|
|
39
26
|
svgElement
|
|
40
27
|
.on('mousemove', (e) => {
|
|
41
|
-
const
|
|
42
|
-
if (!
|
|
28
|
+
const datum = select(e.target).datum();
|
|
29
|
+
if (!datum) {
|
|
43
30
|
return;
|
|
44
31
|
}
|
|
45
32
|
const [pointerX, pointerY] = pointer(e, svgContainer);
|
|
46
|
-
const
|
|
47
|
-
|
|
33
|
+
const data = {
|
|
34
|
+
series: {
|
|
35
|
+
id: datum.point.series.id,
|
|
36
|
+
type: 'scatter',
|
|
37
|
+
name: datum.point.series.name,
|
|
38
|
+
},
|
|
39
|
+
data: datum.point.data,
|
|
40
|
+
};
|
|
41
|
+
dispatcher.call('hover-shape', {}, [data], [pointerX, pointerY]);
|
|
48
42
|
})
|
|
49
43
|
.on('mouseleave', () => {
|
|
50
44
|
dispatcher.call('hover-shape', {}, undefined);
|
|
@@ -52,41 +46,35 @@ export function ScatterSeriesShape(props) {
|
|
|
52
46
|
const hoverEnabled = hoverOptions === null || hoverOptions === void 0 ? void 0 : hoverOptions.enabled;
|
|
53
47
|
const inactiveEnabled = inactiveOptions === null || inactiveOptions === void 0 ? void 0 : inactiveOptions.enabled;
|
|
54
48
|
dispatcher.on('hover-shape.scatter', (data) => {
|
|
55
|
-
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
if (
|
|
63
|
-
|
|
64
|
-
|
|
49
|
+
var _a;
|
|
50
|
+
const selected = data === null || data === void 0 ? void 0 : data.find((d) => d.series.type === 'scatter');
|
|
51
|
+
const selectedDataItem = selected === null || selected === void 0 ? void 0 : selected.data;
|
|
52
|
+
const selectedSeriesId = (_a = selected === null || selected === void 0 ? void 0 : selected.series) === null || _a === void 0 ? void 0 : _a.id;
|
|
53
|
+
selection.datum((d, index, list) => {
|
|
54
|
+
const elementSelection = select(list[index]);
|
|
55
|
+
const hovered = Boolean(hoverEnabled && d.point.data === selectedDataItem);
|
|
56
|
+
if (d.hovered !== hovered) {
|
|
57
|
+
d.hovered = hovered;
|
|
58
|
+
elementSelection.attr('z-index', hovered ? 999 : null);
|
|
59
|
+
selectMarkerHalo(elementSelection).attr('visibility', getMarkerHaloVisibility);
|
|
60
|
+
selectMarkerSymbol(elementSelection).call(setMarker, hovered ? 'hover' : 'normal');
|
|
61
|
+
}
|
|
62
|
+
if (hovered) {
|
|
63
|
+
elementSelection.raise();
|
|
65
64
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
65
|
+
if (d.point.series.marker.states.normal.enabled) {
|
|
66
|
+
const isActive = Boolean(!inactiveEnabled ||
|
|
67
|
+
!selectedSeriesId ||
|
|
68
|
+
selectedSeriesId === d.point.series.id);
|
|
69
|
+
setActiveState({
|
|
70
|
+
element: list[index],
|
|
71
|
+
state: inactiveOptions,
|
|
72
|
+
active: isActive,
|
|
73
|
+
datum: d,
|
|
74
|
+
});
|
|
70
75
|
}
|
|
76
|
+
return d;
|
|
71
77
|
});
|
|
72
|
-
selection.data(updates, shapeKey).join(() => EMPTY_SELECTION, (update) => {
|
|
73
|
-
update
|
|
74
|
-
.attr('fill', (d) => {
|
|
75
|
-
var _a;
|
|
76
|
-
const initialColor = d.data.color || d.series.color || '';
|
|
77
|
-
if (d.hovered) {
|
|
78
|
-
return (((_a = color(initialColor)) === null || _a === void 0 ? void 0 : _a.brighter(hoverOptions === null || hoverOptions === void 0 ? void 0 : hoverOptions.brightness).toString()) || initialColor);
|
|
79
|
-
}
|
|
80
|
-
return initialColor;
|
|
81
|
-
})
|
|
82
|
-
.attr('opacity', function (d) {
|
|
83
|
-
if (!d.active) {
|
|
84
|
-
return (inactiveOptions === null || inactiveOptions === void 0 ? void 0 : inactiveOptions.opacity) || null;
|
|
85
|
-
}
|
|
86
|
-
return null;
|
|
87
|
-
});
|
|
88
|
-
return update;
|
|
89
|
-
}, (exit) => exit);
|
|
90
78
|
});
|
|
91
79
|
return () => {
|
|
92
80
|
dispatcher.on('hover-shape.scatter', null);
|
|
@@ -1,16 +1,7 @@
|
|
|
1
|
-
import type { TooltipDataChunkScatter } from '../../../../../../types';
|
|
2
1
|
import type { ChartScale } from '../../useAxisScales';
|
|
3
2
|
import type { PreparedAxis } from '../../useChartOptions/types';
|
|
4
3
|
import { PreparedScatterSeries } from '../../useSeries/types';
|
|
5
|
-
|
|
6
|
-
cx: number;
|
|
7
|
-
cy: number;
|
|
8
|
-
series: PreparedScatterSeries;
|
|
9
|
-
hovered: boolean;
|
|
10
|
-
active: boolean;
|
|
11
|
-
id: number;
|
|
12
|
-
size: number;
|
|
13
|
-
};
|
|
4
|
+
import { PreparedScatterData } from './types';
|
|
14
5
|
export declare const prepareScatterData: (args: {
|
|
15
6
|
series: PreparedScatterSeries[];
|
|
16
7
|
xAxis: PreparedAxis;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { getXValue, getYValue } from '../utils';
|
|
2
|
-
const DEFAULT_SCATTER_POINT_SIZE = 7;
|
|
3
2
|
const getFilteredLinearScatterData = (data) => {
|
|
4
3
|
return data.filter((d) => typeof d.x === 'number' && typeof d.y === 'number');
|
|
5
4
|
};
|
|
@@ -10,16 +9,15 @@ export const prepareScatterData = (args) => {
|
|
|
10
9
|
? s.data
|
|
11
10
|
: getFilteredLinearScatterData(s.data);
|
|
12
11
|
filteredData.forEach((d) => {
|
|
13
|
-
const size = d.radius ? d.radius * 2 : DEFAULT_SCATTER_POINT_SIZE;
|
|
14
12
|
acc.push({
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
13
|
+
point: {
|
|
14
|
+
data: d,
|
|
15
|
+
series: s,
|
|
16
|
+
x: getXValue({ point: d, xAxis, xScale }),
|
|
17
|
+
y: getYValue({ point: d, yAxis, yScale }),
|
|
18
|
+
},
|
|
19
19
|
hovered: false,
|
|
20
20
|
active: true,
|
|
21
|
-
id: acc.length - 1,
|
|
22
|
-
size,
|
|
23
21
|
});
|
|
24
22
|
});
|
|
25
23
|
return acc;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ScatterSeriesData } from '../../../../../../types';
|
|
2
|
+
import { PreparedScatterSeries } from '../../useSeries/types';
|
|
3
|
+
type PointData = {
|
|
4
|
+
x: number;
|
|
5
|
+
y: number;
|
|
6
|
+
data: ScatterSeriesData;
|
|
7
|
+
series: PreparedScatterSeries;
|
|
8
|
+
};
|
|
9
|
+
export type MarkerData = {
|
|
10
|
+
point: PointData;
|
|
11
|
+
active: boolean;
|
|
12
|
+
hovered: boolean;
|
|
13
|
+
};
|
|
14
|
+
export type PreparedScatterData = MarkerData;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -17,7 +17,7 @@ export declare function getYValue(args: {
|
|
|
17
17
|
yAxis: PreparedAxis;
|
|
18
18
|
yScale: ChartScale;
|
|
19
19
|
}): number;
|
|
20
|
-
export declare const shapeKey: (d: unknown) => string |
|
|
20
|
+
export declare const shapeKey: (d: unknown) => string | -1;
|
|
21
21
|
export declare function setActiveState<T extends {
|
|
22
22
|
active?: boolean;
|
|
23
23
|
}>(args: {
|
|
@@ -1,5 +1,3 @@
|
|
|
1
1
|
import { SymbolType } from '../../../../constants';
|
|
2
2
|
export declare const getSymbolType: (index: number) => SymbolType;
|
|
3
|
-
export declare const getSymbol: (symbolType: SymbolType) =>
|
|
4
|
-
draw: (context: CanvasPath, size: number) => void;
|
|
5
|
-
};
|
|
3
|
+
export declare const getSymbol: (symbolType: `${SymbolType}`) => import("d3-shape").SymbolType;
|
|
@@ -96,6 +96,18 @@ const validatePieSeries = ({ series }) => {
|
|
|
96
96
|
}
|
|
97
97
|
});
|
|
98
98
|
};
|
|
99
|
+
const validateStacking = ({ series }) => {
|
|
100
|
+
const availableStackingValues = ['normal', 'percent'];
|
|
101
|
+
if (series.stacking && !availableStackingValues.includes(series.stacking)) {
|
|
102
|
+
throw new ChartKitError({
|
|
103
|
+
code: CHARTKIT_ERROR_CODE.INVALID_DATA,
|
|
104
|
+
message: i18n('error', 'label_invalid-series-property', {
|
|
105
|
+
key: 'stacking',
|
|
106
|
+
values: availableStackingValues,
|
|
107
|
+
}),
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
};
|
|
99
111
|
const validateSeries = (args) => {
|
|
100
112
|
const { series, xAxis, yAxis } = args;
|
|
101
113
|
if (!AVAILABLE_SERIES_TYPES.includes(series.type)) {
|
|
@@ -107,8 +119,13 @@ const validateSeries = (args) => {
|
|
|
107
119
|
});
|
|
108
120
|
}
|
|
109
121
|
switch (series.type) {
|
|
110
|
-
case '
|
|
122
|
+
case 'area':
|
|
111
123
|
case 'bar-y':
|
|
124
|
+
case 'bar-x': {
|
|
125
|
+
validateXYSeries({ series, xAxis, yAxis });
|
|
126
|
+
validateStacking({ series });
|
|
127
|
+
break;
|
|
128
|
+
}
|
|
112
129
|
case 'line':
|
|
113
130
|
case 'scatter': {
|
|
114
131
|
validateXYSeries({ series, xAxis, yAxis });
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { create } from 'd3';
|
|
2
|
+
import get from 'lodash/get';
|
|
3
|
+
import { getLabelsSize } from '../renderer/utils';
|
|
4
|
+
const MAX_FONT_SIZE = 64;
|
|
5
|
+
export function pieCenterText(text, options) {
|
|
6
|
+
if (!text) {
|
|
7
|
+
return undefined;
|
|
8
|
+
}
|
|
9
|
+
const padding = get(options, 'padding', 12);
|
|
10
|
+
return function (args) {
|
|
11
|
+
let fontSize = MAX_FONT_SIZE;
|
|
12
|
+
const textSize = getLabelsSize({ labels: [text], style: { fontSize: `${fontSize}px` } });
|
|
13
|
+
fontSize = (fontSize * (args.series.innerRadius - padding) * 2) / textSize.maxWidth;
|
|
14
|
+
const container = create('svg:g');
|
|
15
|
+
container
|
|
16
|
+
.append('text')
|
|
17
|
+
.text(text)
|
|
18
|
+
.attr('text-anchor', 'middle')
|
|
19
|
+
.attr('alignment-baseline', 'middle')
|
|
20
|
+
.style('font-size', `${fontSize}px`);
|
|
21
|
+
return container.node();
|
|
22
|
+
};
|
|
23
|
+
}
|
|
@@ -30,11 +30,11 @@ export type AreaSeries<T = any> = BaseSeries & {
|
|
|
30
30
|
/** The name of the series (used in legend, tooltip etc) */
|
|
31
31
|
name: string;
|
|
32
32
|
/** Whether to stack the values of each series on top of each other.
|
|
33
|
-
* Possible values are undefined to disable, "normal" to stack by value
|
|
33
|
+
* Possible values are undefined to disable, "normal" to stack by value or "percent"
|
|
34
34
|
*
|
|
35
35
|
* @default undefined
|
|
36
36
|
* */
|
|
37
|
-
stacking?: 'normal';
|
|
37
|
+
stacking?: 'normal' | 'percent';
|
|
38
38
|
/** This option allows grouping series in a stacked chart */
|
|
39
39
|
stackId?: string;
|
|
40
40
|
/** The main color of the series (hex, rgba) */
|
|
@@ -20,10 +20,6 @@ export type LineSeriesData<T = any> = BaseSeriesData<T> & {
|
|
|
20
20
|
/** Data label value of the point. If not specified, the y value is used. */
|
|
21
21
|
label?: string | number;
|
|
22
22
|
};
|
|
23
|
-
export type LineMarkerSymbol = 'circle' | 'square';
|
|
24
|
-
export type LineMarkerOptions = PointMarkerOptions & {
|
|
25
|
-
symbol?: LineMarkerSymbol;
|
|
26
|
-
};
|
|
27
23
|
export type LineSeries<T = any> = BaseSeries & {
|
|
28
24
|
type: typeof SeriesType.Line;
|
|
29
25
|
data: LineSeriesData<T>[];
|
|
@@ -41,7 +37,7 @@ export type LineSeries<T = any> = BaseSeries & {
|
|
|
41
37
|
symbol?: RectLegendSymbolOptions;
|
|
42
38
|
};
|
|
43
39
|
/** Options for the point markers of line series */
|
|
44
|
-
marker?:
|
|
40
|
+
marker?: PointMarkerOptions;
|
|
45
41
|
/** Option for line stroke style */
|
|
46
42
|
dashStyle?: `${DashStyle}`;
|
|
47
43
|
/** Option for line cap style */
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { SymbolType } from '../../constants';
|
|
1
2
|
export type PointMarkerOptions = {
|
|
2
3
|
/** Enable or disable the point marker */
|
|
3
4
|
enabled?: boolean;
|
|
@@ -7,4 +8,5 @@ export type PointMarkerOptions = {
|
|
|
7
8
|
borderColor?: string;
|
|
8
9
|
/** The width of the point marker's border */
|
|
9
10
|
borderWidth?: number;
|
|
11
|
+
symbol?: `${SymbolType}`;
|
|
10
12
|
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { BaseType } from 'd3';
|
|
1
2
|
import { SeriesType } from '../../constants';
|
|
2
3
|
import type { BaseSeries, BaseSeriesData } from './base';
|
|
3
4
|
import { ChartKitWidgetLegend, RectLegendSymbolOptions } from './legend';
|
|
@@ -71,4 +72,14 @@ export type PieSeries<T = any> = BaseSeries & {
|
|
|
71
72
|
* */
|
|
72
73
|
connectorCurve?: ConnectorCurve;
|
|
73
74
|
};
|
|
75
|
+
/**
|
|
76
|
+
* Function for adding custom svg nodes for a series
|
|
77
|
+
*
|
|
78
|
+
* @return BaseType
|
|
79
|
+
* */
|
|
80
|
+
renderCustomShape?: (args: {
|
|
81
|
+
series: {
|
|
82
|
+
innerRadius: number;
|
|
83
|
+
};
|
|
84
|
+
}) => BaseType;
|
|
74
85
|
};
|