@gravity-ui/chartkit 4.8.0 → 4.9.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/plugins/d3/examples/bar-x/GroupedColumns.js +4 -2
- package/build/plugins/d3/examples/bar-y/Basic.d.ts +2 -0
- package/build/plugins/d3/examples/bar-y/Basic.js +43 -0
- package/build/plugins/d3/examples/bar-y/GroupedColumns.d.ts +2 -0
- package/build/plugins/d3/examples/bar-y/GroupedColumns.js +48 -0
- package/build/plugins/d3/examples/bar-y/StackedColumns.d.ts +2 -0
- package/build/plugins/d3/examples/bar-y/StackedColumns.js +47 -0
- package/build/plugins/d3/examples/combined/LineAndBarX.d.ts +2 -0
- package/build/plugins/d3/examples/combined/LineAndBarX.js +61 -0
- package/build/plugins/d3/examples/line/Basic.d.ts +2 -0
- package/build/plugins/d3/examples/line/Basic.js +66 -0
- package/build/plugins/d3/examples/nintendoGames.d.ts +40 -10
- package/build/plugins/d3/examples/nintendoGames.js +2416 -2189
- package/build/plugins/d3/renderer/D3Widget.js +27 -11
- package/build/plugins/d3/renderer/components/Chart.d.ts +0 -2
- package/build/plugins/d3/renderer/components/Chart.js +4 -6
- package/build/plugins/d3/renderer/components/Tooltip/DefaultContent.d.ts +2 -2
- package/build/plugins/d3/renderer/components/Tooltip/DefaultContent.js +42 -35
- package/build/plugins/d3/renderer/components/Tooltip/TooltipTriggerArea.d.ts +0 -2
- package/build/plugins/d3/renderer/components/Tooltip/TooltipTriggerArea.js +118 -48
- package/build/plugins/d3/renderer/components/Tooltip/index.d.ts +1 -0
- package/build/plugins/d3/renderer/components/Tooltip/index.js +5 -4
- package/build/plugins/d3/renderer/components/styles.css +1 -0
- package/build/plugins/d3/renderer/constants/defaults/series-options.d.ts +9 -2
- package/build/plugins/d3/renderer/constants/defaults/series-options.js +27 -0
- package/build/plugins/d3/renderer/hooks/useAxisScales/index.js +15 -1
- package/build/plugins/d3/renderer/hooks/useChartOptions/x-axis.js +3 -3
- package/build/plugins/d3/renderer/hooks/useChartOptions/y-axis.js +3 -3
- package/build/plugins/d3/renderer/hooks/useSeries/constants.d.ts +3 -0
- package/build/plugins/d3/renderer/hooks/useSeries/constants.js +5 -0
- package/build/plugins/d3/renderer/hooks/useSeries/index.d.ts +1 -1
- package/build/plugins/d3/renderer/hooks/useSeries/index.js +2 -1
- package/build/plugins/d3/renderer/hooks/useSeries/prepare-bar-x.d.ts +10 -0
- package/build/plugins/d3/renderer/hooks/useSeries/prepare-bar-x.js +38 -0
- package/build/plugins/d3/renderer/hooks/useSeries/prepare-bar-y.d.ts +10 -0
- package/build/plugins/d3/renderer/hooks/useSeries/prepare-bar-y.js +50 -0
- package/build/plugins/d3/renderer/hooks/useSeries/prepare-line-series.d.ts +11 -0
- package/build/plugins/d3/renderer/hooks/useSeries/prepare-line-series.js +32 -0
- package/build/plugins/d3/renderer/hooks/useSeries/prepareSeries.d.ts +2 -1
- package/build/plugins/d3/renderer/hooks/useSeries/prepareSeries.js +21 -60
- package/build/plugins/d3/renderer/hooks/useSeries/types.d.ts +24 -2
- package/build/plugins/d3/renderer/hooks/useSeries/utils.d.ts +3 -1
- package/build/plugins/d3/renderer/hooks/useSeries/utils.js +13 -0
- package/build/plugins/d3/renderer/hooks/useShapes/bar-x/prepare-data.js +4 -6
- package/build/plugins/d3/renderer/hooks/useShapes/bar-y/index.d.ts +11 -0
- package/build/plugins/d3/renderer/hooks/useShapes/bar-y/index.js +87 -0
- package/build/plugins/d3/renderer/hooks/useShapes/bar-y/prepare-data.d.ts +12 -0
- package/build/plugins/d3/renderer/hooks/useShapes/bar-y/prepare-data.js +114 -0
- package/build/plugins/d3/renderer/hooks/useShapes/bar-y/types.d.ts +10 -0
- package/build/plugins/d3/renderer/hooks/useShapes/bar-y/types.js +1 -0
- package/build/plugins/d3/renderer/hooks/useShapes/constants.d.ts +3 -0
- package/build/plugins/d3/renderer/hooks/useShapes/constants.js +3 -0
- package/build/plugins/d3/renderer/hooks/useShapes/index.d.ts +4 -4
- package/build/plugins/d3/renderer/hooks/useShapes/index.js +35 -5
- package/build/plugins/d3/renderer/hooks/useShapes/line/index.d.ts +11 -0
- package/build/plugins/d3/renderer/hooks/useShapes/line/index.js +98 -0
- package/build/plugins/d3/renderer/hooks/useShapes/line/prepare-data.d.ts +11 -0
- package/build/plugins/d3/renderer/hooks/useShapes/line/prepare-data.js +21 -0
- package/build/plugins/d3/renderer/hooks/useShapes/line/types.d.ts +16 -0
- package/build/plugins/d3/renderer/hooks/useShapes/line/types.js +1 -0
- package/build/plugins/d3/renderer/hooks/useShapes/pie.d.ts +0 -2
- package/build/plugins/d3/renderer/hooks/useShapes/pie.js +3 -3
- package/build/plugins/d3/renderer/hooks/useShapes/scatter/index.d.ts +0 -2
- package/build/plugins/d3/renderer/hooks/useShapes/scatter/index.js +6 -6
- package/build/plugins/d3/renderer/hooks/useShapes/scatter/prepare-data.d.ts +1 -1
- package/build/plugins/d3/renderer/hooks/useShapes/scatter/prepare-data.js +3 -34
- package/build/plugins/d3/renderer/hooks/useShapes/styles.css +6 -0
- package/build/plugins/d3/renderer/hooks/useShapes/utils.d.ts +17 -0
- package/build/plugins/d3/renderer/hooks/useShapes/utils.js +25 -0
- package/build/plugins/d3/renderer/utils/axis-generators/bottom.js +3 -3
- package/build/plugins/d3/renderer/utils/text.d.ts +5 -7
- package/build/plugins/d3/renderer/utils/text.js +4 -15
- package/build/types/widget-data/bar-y.d.ts +55 -0
- package/build/types/widget-data/bar-y.js +1 -0
- package/build/types/widget-data/base.d.ts +4 -0
- package/build/types/widget-data/index.d.ts +2 -0
- package/build/types/widget-data/index.js +2 -0
- package/build/types/widget-data/line.d.ts +37 -0
- package/build/types/widget-data/line.js +1 -0
- package/build/types/widget-data/series.d.ts +53 -3
- package/build/types/widget-data/tooltip.d.ts +15 -1
- package/build/utils/index.d.ts +1 -1
- package/build/utils/index.js +1 -1
- package/build/utils/performance.d.ts +3 -0
- package/build/utils/performance.js +8 -0
- package/package.json +3 -2
|
@@ -1,28 +1,44 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { select } from 'd3';
|
|
3
3
|
import debounce from 'lodash/debounce';
|
|
4
|
-
import
|
|
4
|
+
import afterFrame from 'afterframe';
|
|
5
|
+
import { getRandomCKId, measurePerformance } from '../../../utils';
|
|
5
6
|
import { Chart } from './components';
|
|
6
7
|
const D3Widget = React.forwardRef(function D3Widget(props, forwardedRef) {
|
|
7
|
-
const { data, onLoad, onRender } = props;
|
|
8
|
+
const { data, onLoad, onRender, onChartLoad } = props;
|
|
8
9
|
const ref = React.useRef(null);
|
|
9
10
|
const debounced = React.useRef();
|
|
10
11
|
const [dimensions, setDimensions] = React.useState();
|
|
11
|
-
|
|
12
|
+
const performanceMeasure = React.useRef(measurePerformance());
|
|
12
13
|
React.useLayoutEffect(() => {
|
|
13
|
-
if (
|
|
14
|
-
|
|
14
|
+
if (onChartLoad) {
|
|
15
|
+
onChartLoad({});
|
|
15
16
|
}
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
}, [onChartLoad]);
|
|
18
|
+
React.useLayoutEffect(() => {
|
|
19
|
+
if (dimensions === null || dimensions === void 0 ? void 0 : dimensions.width) {
|
|
20
|
+
if (!performanceMeasure.current) {
|
|
21
|
+
performanceMeasure.current = measurePerformance();
|
|
22
|
+
}
|
|
23
|
+
afterFrame(() => {
|
|
24
|
+
var _a;
|
|
25
|
+
const renderTime = (_a = performanceMeasure.current) === null || _a === void 0 ? void 0 : _a.end();
|
|
26
|
+
onRender === null || onRender === void 0 ? void 0 : onRender({
|
|
27
|
+
renderTime,
|
|
28
|
+
});
|
|
29
|
+
onLoad === null || onLoad === void 0 ? void 0 : onLoad({
|
|
30
|
+
widgetRendering: renderTime,
|
|
31
|
+
});
|
|
32
|
+
performanceMeasure.current = null;
|
|
33
|
+
});
|
|
18
34
|
}
|
|
19
|
-
}, []);
|
|
35
|
+
}, [data, onRender, onLoad, dimensions]);
|
|
20
36
|
const handleResize = React.useCallback(() => {
|
|
21
37
|
var _a;
|
|
22
38
|
const parentElement = (_a = ref.current) === null || _a === void 0 ? void 0 : _a.parentElement;
|
|
23
39
|
if (parentElement) {
|
|
24
|
-
const {
|
|
25
|
-
setDimensions({
|
|
40
|
+
const { width, height } = parentElement.getBoundingClientRect();
|
|
41
|
+
setDimensions({ width, height });
|
|
26
42
|
}
|
|
27
43
|
}, []);
|
|
28
44
|
const debuncedHandleResize = React.useMemo(() => {
|
|
@@ -54,6 +70,6 @@ const D3Widget = React.forwardRef(function D3Widget(props, forwardedRef) {
|
|
|
54
70
|
width: (dimensions === null || dimensions === void 0 ? void 0 : dimensions.width) || '100%',
|
|
55
71
|
height: (dimensions === null || dimensions === void 0 ? void 0 : dimensions.height) || '100%',
|
|
56
72
|
position: 'relative',
|
|
57
|
-
} }, (dimensions === null || dimensions === void 0 ? void 0 : dimensions.width) && (dimensions === null || dimensions === void 0 ? void 0 : dimensions.height) && (React.createElement(Chart, {
|
|
73
|
+
} }, (dimensions === null || dimensions === void 0 ? void 0 : dimensions.width) && (dimensions === null || dimensions === void 0 ? void 0 : dimensions.height) && (React.createElement(Chart, { width: dimensions.width, height: dimensions.height, data: data }))));
|
|
58
74
|
});
|
|
59
75
|
export default D3Widget;
|
|
@@ -14,8 +14,8 @@ import './styles.css';
|
|
|
14
14
|
const b = block('d3');
|
|
15
15
|
export const Chart = (props) => {
|
|
16
16
|
// FIXME: add data validation
|
|
17
|
-
const {
|
|
18
|
-
const svgRef = React.
|
|
17
|
+
const { width, height, data } = props;
|
|
18
|
+
const svgRef = React.useRef(null);
|
|
19
19
|
const dispatcher = React.useMemo(() => {
|
|
20
20
|
return getD3Dispatcher();
|
|
21
21
|
}, []);
|
|
@@ -50,8 +50,6 @@ export const Chart = (props) => {
|
|
|
50
50
|
});
|
|
51
51
|
const { hovered, pointerPosition } = useTooltip({ dispatcher, tooltip });
|
|
52
52
|
const { shapes, shapesData } = useShapes({
|
|
53
|
-
top,
|
|
54
|
-
left,
|
|
55
53
|
boundsWidth,
|
|
56
54
|
boundsHeight,
|
|
57
55
|
dispatcher,
|
|
@@ -74,7 +72,7 @@ export const Chart = (props) => {
|
|
|
74
72
|
React.createElement("g", { transform: `translate(0, ${boundsHeight})` },
|
|
75
73
|
React.createElement(AxisX, { axis: xAxis, width: boundsWidth, height: boundsHeight, scale: xScale })))),
|
|
76
74
|
shapes,
|
|
77
|
-
(tooltip === null || tooltip === void 0 ? void 0 : tooltip.enabled) && Boolean(shapesData.length) && (React.createElement(TooltipTriggerArea, { boundsWidth: boundsWidth, boundsHeight: boundsHeight, dispatcher: dispatcher,
|
|
75
|
+
(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 }))),
|
|
78
76
|
preparedLegend.enabled && (React.createElement(Legend, { chartSeries: preparedSeries, boundsWidth: boundsWidth, legend: preparedLegend, items: legendItems, config: legendConfig, onItemClick: handleLegendItemClick }))),
|
|
79
|
-
React.createElement(Tooltip, { dispatcher: dispatcher, tooltip: tooltip, xAxis: xAxis, yAxis: yAxis[0], hovered: hovered, pointerPosition: pointerPosition })));
|
|
77
|
+
React.createElement(Tooltip, { dispatcher: dispatcher, tooltip: tooltip, svgContainer: svgRef.current, xAxis: xAxis, yAxis: yAxis[0], hovered: hovered, pointerPosition: pointerPosition })));
|
|
80
78
|
};
|
|
@@ -2,9 +2,9 @@ import React from 'react';
|
|
|
2
2
|
import type { TooltipDataChunk } from '../../../../../types';
|
|
3
3
|
import type { PreparedAxis } from '../../hooks';
|
|
4
4
|
type Props = {
|
|
5
|
-
hovered: TooltipDataChunk;
|
|
5
|
+
hovered: TooltipDataChunk[];
|
|
6
6
|
xAxis: PreparedAxis;
|
|
7
7
|
yAxis: PreparedAxis;
|
|
8
8
|
};
|
|
9
|
-
export declare const DefaultContent: ({ hovered, xAxis, yAxis }: Props) => React.JSX.Element
|
|
9
|
+
export declare const DefaultContent: ({ hovered, xAxis, yAxis }: Props) => React.JSX.Element;
|
|
10
10
|
export {};
|
|
@@ -5,13 +5,16 @@ import { formatNumber } from '../../../../shared';
|
|
|
5
5
|
import { getDataCategoryValue } from '../../utils';
|
|
6
6
|
const DEFAULT_DATE_FORMAT = 'DD.MM.YY';
|
|
7
7
|
const getRowData = (fieldName, axis, data) => {
|
|
8
|
-
const categories = get(axis, 'categories', []);
|
|
9
8
|
switch (axis.type) {
|
|
10
9
|
case 'category': {
|
|
10
|
+
const categories = get(axis, 'categories', []);
|
|
11
11
|
return getDataCategoryValue({ axisDirection: fieldName, categories, data });
|
|
12
12
|
}
|
|
13
13
|
case 'datetime': {
|
|
14
14
|
const value = get(data, fieldName);
|
|
15
|
+
if (!value) {
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
15
18
|
return dateTime({ input: value }).format(DEFAULT_DATE_FORMAT);
|
|
16
19
|
}
|
|
17
20
|
case 'linear':
|
|
@@ -24,40 +27,44 @@ const getRowData = (fieldName, axis, data) => {
|
|
|
24
27
|
const getXRowData = (xAxis, data) => getRowData('x', xAxis, data);
|
|
25
28
|
const getYRowData = (yAxis, data) => getRowData('y', yAxis, data);
|
|
26
29
|
export const DefaultContent = ({ hovered, xAxis, yAxis }) => {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
React.createElement("
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
30
|
+
return (React.createElement(React.Fragment, null, hovered.map(({ data, series }, i) => {
|
|
31
|
+
const id = get(series, 'id', i);
|
|
32
|
+
switch (series.type) {
|
|
33
|
+
case 'scatter':
|
|
34
|
+
case 'line':
|
|
35
|
+
case 'bar-x': {
|
|
36
|
+
const xRow = getXRowData(xAxis, data);
|
|
37
|
+
const yRow = getYRowData(yAxis, data);
|
|
38
|
+
return (React.createElement("div", { key: id },
|
|
39
|
+
React.createElement("div", null, xRow),
|
|
40
|
+
React.createElement("div", null,
|
|
41
|
+
React.createElement("span", null,
|
|
42
|
+
React.createElement("b", null, series.name),
|
|
43
|
+
": ",
|
|
44
|
+
yRow))));
|
|
45
|
+
}
|
|
46
|
+
case 'bar-y': {
|
|
47
|
+
const xRow = getXRowData(xAxis, data);
|
|
48
|
+
const yRow = getYRowData(yAxis, data);
|
|
49
|
+
return (React.createElement("div", { key: id },
|
|
50
|
+
React.createElement("div", null, yRow),
|
|
51
|
+
React.createElement("div", null,
|
|
52
|
+
React.createElement("span", null,
|
|
53
|
+
React.createElement("b", null, series.name),
|
|
54
|
+
": ",
|
|
55
|
+
xRow))));
|
|
56
|
+
}
|
|
57
|
+
case 'pie': {
|
|
58
|
+
const pieSeries = series;
|
|
59
|
+
return (React.createElement("div", { key: id },
|
|
46
60
|
React.createElement("span", null,
|
|
47
|
-
|
|
48
|
-
"
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
React.createElement("span", null,
|
|
55
|
-
pieSeries.name || pieSeries.id,
|
|
56
|
-
"\u00A0"),
|
|
57
|
-
React.createElement("span", null, pieSeries.value)));
|
|
61
|
+
pieSeries.name || pieSeries.id,
|
|
62
|
+
"\u00A0"),
|
|
63
|
+
React.createElement("span", null, pieSeries.value)));
|
|
64
|
+
}
|
|
65
|
+
default: {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
58
68
|
}
|
|
59
|
-
|
|
60
|
-
return null;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
69
|
+
})));
|
|
63
70
|
};
|
|
@@ -1,66 +1,136 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import throttle from 'lodash/throttle';
|
|
3
|
-
import { bisector, pointer, sort } from 'd3';
|
|
3
|
+
import { bisector, pointer, sort, group } from 'd3';
|
|
4
4
|
import { extractD3DataFromNode, isNodeContainsD3Data } from '../../utils';
|
|
5
5
|
const THROTTLE_DELAY = 50;
|
|
6
6
|
const isNodeContainsData = (node) => {
|
|
7
7
|
return isNodeContainsD3Data(node);
|
|
8
8
|
};
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
function getBarXShapeData(args) {
|
|
10
|
+
var _a;
|
|
11
|
+
const { shapesData, point: [pointerX, pointerY], top, left, xData, container, } = args;
|
|
12
|
+
const barWidthOffset = shapesData[0].width / 2;
|
|
13
|
+
const xPosition = pointerX - left - barWidthOffset;
|
|
14
|
+
const xDataIndex = bisector((d) => d.x).center(xData, xPosition);
|
|
15
|
+
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}"]`)) || []);
|
|
16
|
+
if (xNodes.length === 1 && isNodeContainsData(xNodes[0])) {
|
|
17
|
+
return [extractD3DataFromNode(xNodes[0])];
|
|
12
18
|
}
|
|
13
|
-
|
|
14
|
-
|
|
19
|
+
if (xNodes.length > 1 && xNodes.every(isNodeContainsData)) {
|
|
20
|
+
const yPosition = pointerY - top;
|
|
21
|
+
const xyNode = xNodes.find((node, i) => {
|
|
22
|
+
const { y, height } = extractD3DataFromNode(node);
|
|
23
|
+
if (i === xNodes.length - 1) {
|
|
24
|
+
return yPosition <= y + height;
|
|
25
|
+
}
|
|
26
|
+
return yPosition >= y && yPosition <= y + height;
|
|
27
|
+
});
|
|
28
|
+
if (xyNode) {
|
|
29
|
+
return [extractD3DataFromNode(xyNode)];
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return [];
|
|
33
|
+
}
|
|
34
|
+
function getLineShapesData(args) {
|
|
35
|
+
const { xData, point: [pointerX], } = args;
|
|
36
|
+
const xDataIndex = bisector((d) => d.x).center(xData, pointerX);
|
|
37
|
+
const selectedLineShape = xData[xDataIndex];
|
|
38
|
+
if (selectedLineShape) {
|
|
39
|
+
return [
|
|
40
|
+
{
|
|
41
|
+
series: selectedLineShape.series,
|
|
42
|
+
data: selectedLineShape.data,
|
|
43
|
+
},
|
|
44
|
+
];
|
|
45
|
+
}
|
|
46
|
+
return [];
|
|
47
|
+
}
|
|
48
|
+
function getBarYData(args) {
|
|
49
|
+
var _a;
|
|
50
|
+
const { data, point: [pointerX, pointerY], } = args;
|
|
51
|
+
const yDataIndex = bisector((d) => d.y).center(data, pointerY);
|
|
52
|
+
const shapesByY = ((_a = data[yDataIndex]) === null || _a === void 0 ? void 0 : _a.items) || [];
|
|
53
|
+
const xDataIndex = bisector((d) => d.x).left(shapesByY, pointerX);
|
|
54
|
+
const result = shapesByY[Math.min(xDataIndex, shapesByY.length - 1)];
|
|
55
|
+
return result
|
|
56
|
+
? [
|
|
57
|
+
{
|
|
58
|
+
series: result.series,
|
|
59
|
+
data: result.data,
|
|
60
|
+
},
|
|
61
|
+
]
|
|
62
|
+
: [];
|
|
63
|
+
}
|
|
15
64
|
export const TooltipTriggerArea = (args) => {
|
|
16
|
-
const { boundsWidth, boundsHeight, dispatcher,
|
|
65
|
+
const { boundsWidth, boundsHeight, dispatcher, shapesData, svgContainer } = args;
|
|
17
66
|
const rectRef = React.useRef(null);
|
|
18
|
-
const
|
|
19
|
-
|
|
67
|
+
const xBarData = React.useMemo(() => {
|
|
68
|
+
const result = shapesData
|
|
69
|
+
.filter((sd) => sd.series.type === 'bar-x')
|
|
70
|
+
.map((sd) => ({ x: sd.x, data: sd }));
|
|
71
|
+
return sort(result, (item) => item.x);
|
|
72
|
+
}, [shapesData]);
|
|
73
|
+
const xLineData = React.useMemo(() => {
|
|
74
|
+
const result = shapesData
|
|
75
|
+
.filter((sd) => sd.series.type === 'line')
|
|
76
|
+
.reduce((acc, sd) => {
|
|
77
|
+
return acc.concat(sd.points.map((d) => ({
|
|
78
|
+
x: d.x,
|
|
79
|
+
data: d.data,
|
|
80
|
+
series: sd.series,
|
|
81
|
+
})));
|
|
82
|
+
}, []);
|
|
83
|
+
return sort(result, (item) => item.x);
|
|
20
84
|
}, [shapesData]);
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
85
|
+
const barYData = React.useMemo(() => {
|
|
86
|
+
const barYShapeData = shapesData.filter((sd) => sd.series.type === 'bar-y');
|
|
87
|
+
const result = Array.from(group(barYShapeData, (sd) => sd.y)).map(([y, shapes]) => {
|
|
88
|
+
const yValue = y + shapes[0].height / 2;
|
|
89
|
+
return {
|
|
90
|
+
y: yValue,
|
|
91
|
+
items: sort(shapes.map((shape) => {
|
|
92
|
+
const preparedData = shape;
|
|
93
|
+
return {
|
|
94
|
+
x: preparedData.x + preparedData.width,
|
|
95
|
+
data: preparedData.data,
|
|
96
|
+
series: shape.series,
|
|
97
|
+
};
|
|
98
|
+
}), (item) => item.x),
|
|
99
|
+
};
|
|
100
|
+
});
|
|
101
|
+
return sort(result, (item) => item.y);
|
|
102
|
+
}, [shapesData]);
|
|
103
|
+
const handleMouseMove = (e) => {
|
|
104
|
+
var _a, _b;
|
|
105
|
+
const { left: ownLeft, top: ownTop } = ((_a = rectRef.current) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect()) || {
|
|
106
|
+
left: 0,
|
|
107
|
+
top: 0,
|
|
108
|
+
};
|
|
109
|
+
const { left: containerLeft, top: containerTop } = (svgContainer === null || svgContainer === void 0 ? void 0 : svgContainer.getBoundingClientRect()) || {
|
|
110
|
+
left: 0,
|
|
111
|
+
top: 0,
|
|
112
|
+
};
|
|
29
113
|
const [pointerX, pointerY] = pointer(e, svgContainer);
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
if (xyNode) {
|
|
48
|
-
hoverShapeData = [extractD3DataFromNode(xyNode)];
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
if (hoverShapeData) {
|
|
52
|
-
const position = [pointerX - offsetLeft, pointerY - offsetTop];
|
|
114
|
+
const hoverShapeData = [];
|
|
115
|
+
hoverShapeData === null || hoverShapeData === void 0 ? void 0 : hoverShapeData.push(...getBarXShapeData({
|
|
116
|
+
shapesData,
|
|
117
|
+
point: [pointerX, pointerY],
|
|
118
|
+
left: ownLeft - containerLeft,
|
|
119
|
+
top: ownTop - containerTop,
|
|
120
|
+
xData: xBarData,
|
|
121
|
+
container: (_b = rectRef.current) === null || _b === void 0 ? void 0 : _b.parentElement,
|
|
122
|
+
}), ...getLineShapesData({
|
|
123
|
+
xData: xLineData,
|
|
124
|
+
point: [pointerX - (ownLeft - containerLeft), pointerY - (ownTop - containerTop)],
|
|
125
|
+
}), ...getBarYData({
|
|
126
|
+
data: barYData,
|
|
127
|
+
point: [pointerX - (ownLeft - containerLeft), pointerY - (ownTop - containerTop)],
|
|
128
|
+
}));
|
|
129
|
+
if (hoverShapeData.length) {
|
|
130
|
+
const position = [pointerX, pointerY];
|
|
53
131
|
dispatcher.call('hover-shape', e.target, hoverShapeData, position);
|
|
54
132
|
}
|
|
55
133
|
};
|
|
56
|
-
const handleMouseMove = (e) => {
|
|
57
|
-
switch (calculationType) {
|
|
58
|
-
case 'x-primary': {
|
|
59
|
-
handleXprimaryMouseMove(e);
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
};
|
|
64
134
|
const throttledHandleMouseMove = throttle(handleMouseMove, THROTTLE_DELAY);
|
|
65
135
|
const handleMouseLeave = () => {
|
|
66
136
|
throttledHandleMouseMove.cancel();
|
|
@@ -6,7 +6,7 @@ export * from './TooltipTriggerArea';
|
|
|
6
6
|
const b = block('d3-tooltip');
|
|
7
7
|
const POINTER_OFFSET_X = 20;
|
|
8
8
|
export const Tooltip = (props) => {
|
|
9
|
-
const { tooltip, xAxis, yAxis, hovered, pointerPosition } = props;
|
|
9
|
+
const { tooltip, svgContainer, xAxis, yAxis, hovered, pointerPosition } = props;
|
|
10
10
|
const ref = React.useRef(null);
|
|
11
11
|
const size = React.useMemo(() => {
|
|
12
12
|
if (ref.current && hovered) {
|
|
@@ -20,8 +20,9 @@ export const Tooltip = (props) => {
|
|
|
20
20
|
if (hovered && pointerPosition && size) {
|
|
21
21
|
const { clientWidth } = document.documentElement;
|
|
22
22
|
const { width, height } = size;
|
|
23
|
+
const rect = (svgContainer === null || svgContainer === void 0 ? void 0 : svgContainer.getBoundingClientRect()) || { left: 0, top: 0 };
|
|
23
24
|
const [pointerLeft, pointetTop] = pointerPosition;
|
|
24
|
-
const outOfRightBoudary = pointerLeft + width + POINTER_OFFSET_X >= clientWidth;
|
|
25
|
+
const outOfRightBoudary = pointerLeft + width + rect.left + POINTER_OFFSET_X >= clientWidth;
|
|
25
26
|
const outOfTopBoundary = pointetTop - height / 2 <= 0;
|
|
26
27
|
const left = outOfRightBoudary
|
|
27
28
|
? pointerLeft - width - POINTER_OFFSET_X
|
|
@@ -33,14 +34,14 @@ export const Tooltip = (props) => {
|
|
|
33
34
|
return { left: -1000, top: -1000 };
|
|
34
35
|
}
|
|
35
36
|
return undefined;
|
|
36
|
-
}, [hovered, pointerPosition, size]);
|
|
37
|
+
}, [hovered, pointerPosition, size, svgContainer]);
|
|
37
38
|
const content = React.useMemo(() => {
|
|
38
39
|
var _a;
|
|
39
40
|
if (!hovered) {
|
|
40
41
|
return null;
|
|
41
42
|
}
|
|
42
43
|
const customTooltip = (_a = tooltip.renderer) === null || _a === void 0 ? void 0 : _a.call(tooltip, { hovered });
|
|
43
|
-
return isNil(customTooltip) ? (React.createElement(DefaultContent, { hovered: hovered
|
|
44
|
+
return isNil(customTooltip) ? (React.createElement(DefaultContent, { hovered: hovered, xAxis: xAxis, yAxis: yAxis })) : (customTooltip);
|
|
44
45
|
}, [hovered, tooltip, xAxis, yAxis]);
|
|
45
46
|
if (!position || !hovered) {
|
|
46
47
|
return null;
|
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
import type { ChartKitWidgetSeriesOptions } from '../../../../../types';
|
|
2
|
-
type
|
|
2
|
+
type DefaultBarXSeriesOptions = Partial<ChartKitWidgetSeriesOptions['bar-x']> & {
|
|
3
3
|
'bar-x': {
|
|
4
4
|
barMaxWidth: number;
|
|
5
5
|
barPadding: number;
|
|
6
6
|
groupPadding: number;
|
|
7
7
|
};
|
|
8
8
|
};
|
|
9
|
-
|
|
9
|
+
type DefaultBarYSeriesOptions = Partial<ChartKitWidgetSeriesOptions['bar-x']> & {
|
|
10
|
+
'bar-y': {
|
|
11
|
+
barMaxWidth: number;
|
|
12
|
+
barPadding: number;
|
|
13
|
+
groupPadding: number;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
export type SeriesOptionsDefaults = Partial<ChartKitWidgetSeriesOptions> & DefaultBarXSeriesOptions & DefaultBarYSeriesOptions;
|
|
10
17
|
export declare const seriesOptionsDefaults: SeriesOptionsDefaults;
|
|
11
18
|
export {};
|
|
@@ -14,6 +14,21 @@ export const seriesOptionsDefaults = {
|
|
|
14
14
|
},
|
|
15
15
|
},
|
|
16
16
|
},
|
|
17
|
+
'bar-y': {
|
|
18
|
+
barMaxWidth: 50,
|
|
19
|
+
barPadding: 0.1,
|
|
20
|
+
groupPadding: 0.2,
|
|
21
|
+
states: {
|
|
22
|
+
hover: {
|
|
23
|
+
enabled: true,
|
|
24
|
+
brightness: 0.3,
|
|
25
|
+
},
|
|
26
|
+
inactive: {
|
|
27
|
+
enabled: true,
|
|
28
|
+
opacity: 0.5,
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
},
|
|
17
32
|
pie: {
|
|
18
33
|
states: {
|
|
19
34
|
hover: {
|
|
@@ -38,4 +53,16 @@ export const seriesOptionsDefaults = {
|
|
|
38
53
|
},
|
|
39
54
|
},
|
|
40
55
|
},
|
|
56
|
+
line: {
|
|
57
|
+
states: {
|
|
58
|
+
hover: {
|
|
59
|
+
enabled: true,
|
|
60
|
+
brightness: 0.3,
|
|
61
|
+
},
|
|
62
|
+
inactive: {
|
|
63
|
+
enabled: true,
|
|
64
|
+
opacity: 0.5,
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
},
|
|
41
68
|
};
|
|
@@ -62,13 +62,27 @@ export function createYScale(axis, series, boundsHeight) {
|
|
|
62
62
|
}
|
|
63
63
|
throw new Error('Failed to create yScale');
|
|
64
64
|
}
|
|
65
|
+
function calculateXAxisPadding(series) {
|
|
66
|
+
let result = 0;
|
|
67
|
+
series.forEach((s) => {
|
|
68
|
+
switch (s.type) {
|
|
69
|
+
case 'bar-y': {
|
|
70
|
+
// Since labels can be located to the right of the bar, need to add an additional space
|
|
71
|
+
const labelsMaxWidth = get(s, 'dataLabels.maxWidth', 0);
|
|
72
|
+
result = Math.max(result, labelsMaxWidth);
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
return result;
|
|
78
|
+
}
|
|
65
79
|
export function createXScale(axis, series, boundsWidth) {
|
|
66
80
|
const xMin = get(axis, 'min');
|
|
67
81
|
const xType = get(axis, 'type', 'linear');
|
|
68
82
|
const xCategories = get(axis, 'categories');
|
|
69
83
|
const xTimestamps = get(axis, 'timestamps');
|
|
70
84
|
const maxPadding = get(axis, 'maxPadding', 0);
|
|
71
|
-
const xAxisMinPadding = boundsWidth * maxPadding;
|
|
85
|
+
const xAxisMinPadding = boundsWidth * maxPadding + calculateXAxisPadding(series);
|
|
72
86
|
const xRange = [0, boundsWidth - xAxisMinPadding];
|
|
73
87
|
switch (xType) {
|
|
74
88
|
case 'linear': {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import get from 'lodash/get';
|
|
2
2
|
import { axisLabelsDefaults, DEFAULT_AXIS_LABEL_FONT_SIZE, xAxisTitleDefaults, } from '../../constants';
|
|
3
|
-
import { calculateCos, formatAxisTickLabel, getClosestPointsRange, getHorisontalSvgTextHeight,
|
|
3
|
+
import { calculateCos, formatAxisTickLabel, getClosestPointsRange, getHorisontalSvgTextHeight, getLabelsSize, getMaxTickCount, getTicksCount, getXAxisItems, hasOverlappingLabels, } from '../../utils';
|
|
4
4
|
import { createXScale } from '../useAxisScales';
|
|
5
5
|
function getLabelSettings({ axis, series, width, autoRotation = true, }) {
|
|
6
6
|
const scale = createXScale(axis, series, width);
|
|
@@ -27,14 +27,14 @@ function getLabelSettings({ axis, series, width, autoRotation = true, }) {
|
|
|
27
27
|
const defaultRotation = overlapping && autoRotation ? -45 : 0;
|
|
28
28
|
const rotation = axis.labels.rotation || defaultRotation;
|
|
29
29
|
const labelsHeight = rotation
|
|
30
|
-
?
|
|
30
|
+
? getLabelsSize({
|
|
31
31
|
labels,
|
|
32
32
|
style: {
|
|
33
33
|
'font-size': axis.labels.style.fontSize,
|
|
34
34
|
'font-weight': axis.labels.style.fontWeight || 'normal',
|
|
35
35
|
},
|
|
36
36
|
rotation,
|
|
37
|
-
})
|
|
37
|
+
}).maxHeight
|
|
38
38
|
: axis.labels.lineHeight;
|
|
39
39
|
const maxHeight = rotation ? calculateCos(rotation) * axis.labels.maxWidth : labelsHeight;
|
|
40
40
|
return { height: Math.min(maxHeight, labelsHeight), rotation };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import get from 'lodash/get';
|
|
2
2
|
import { axisLabelsDefaults, DEFAULT_AXIS_LABEL_FONT_SIZE, yAxisTitleDefaults, } from '../../constants';
|
|
3
|
-
import { getHorisontalSvgTextHeight, formatAxisTickLabel, getClosestPointsRange, getScaleTicks,
|
|
3
|
+
import { getHorisontalSvgTextHeight, formatAxisTickLabel, getClosestPointsRange, getScaleTicks, getLabelsSize, } from '../../utils';
|
|
4
4
|
import { createYScale } from '../useAxisScales';
|
|
5
5
|
const getAxisLabelMaxWidth = (args) => {
|
|
6
6
|
const { axis, series } = args;
|
|
@@ -16,14 +16,14 @@ const getAxisLabelMaxWidth = (args) => {
|
|
|
16
16
|
value: tick,
|
|
17
17
|
step,
|
|
18
18
|
}));
|
|
19
|
-
return
|
|
19
|
+
return getLabelsSize({
|
|
20
20
|
labels,
|
|
21
21
|
style: {
|
|
22
22
|
'font-size': axis.labels.style.fontSize,
|
|
23
23
|
'font-weight': axis.labels.style.fontWeight || '',
|
|
24
24
|
},
|
|
25
25
|
rotation: axis.labels.rotation,
|
|
26
|
-
});
|
|
26
|
+
}).maxWidth;
|
|
27
27
|
};
|
|
28
28
|
export const getPreparedYAxis = ({ series, yAxis, }) => {
|
|
29
29
|
// FIXME: add support for n axises
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ChartKitWidgetData } from '../../../../../types
|
|
1
|
+
import type { ChartKitWidgetData } from '../../../../../types';
|
|
2
2
|
import type { PreparedAxis, PreparedChart } from '../useChartOptions/types';
|
|
3
3
|
import type { PreparedSeries, OnLegendItemClick } from './types';
|
|
4
4
|
type Args = {
|
|
@@ -17,12 +17,13 @@ export const useSeries = (args) => {
|
|
|
17
17
|
acc.push(...prepareSeries({
|
|
18
18
|
type: seriesType,
|
|
19
19
|
series: seriesList,
|
|
20
|
+
seriesOptions,
|
|
20
21
|
legend: preparedLegend,
|
|
21
22
|
colorScale,
|
|
22
23
|
}));
|
|
23
24
|
return acc;
|
|
24
25
|
}, []);
|
|
25
|
-
}, [series, preparedLegend]);
|
|
26
|
+
}, [series, seriesOptions, preparedLegend]);
|
|
26
27
|
const preparedSeriesOptions = React.useMemo(() => {
|
|
27
28
|
return getPreparedOptions(seriesOptions);
|
|
28
29
|
}, [seriesOptions]);
|