@gravity-ui/charts 1.43.0 → 1.43.1
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/dist/cjs/components/ChartInner/index.js +3 -3
- package/dist/cjs/components/ChartInner/useChartInnerHandlers.d.ts +3 -3
- package/dist/cjs/components/ChartInner/useChartInnerHandlers.js +10 -12
- package/dist/cjs/components/ChartInner/useChartInnerProps.js +8 -4
- package/dist/cjs/components/ChartInner/utils/title.d.ts +3 -2
- package/dist/cjs/components/ChartInner/utils/title.js +19 -14
- package/dist/cjs/components/Title/index.d.ts +1 -3
- package/dist/cjs/components/Title/index.js +2 -2
- package/dist/cjs/core/constants/index.d.ts +0 -1
- package/dist/cjs/core/constants/index.js +0 -1
- package/dist/cjs/core/utils/labels.d.ts +1 -1
- package/dist/cjs/core/utils/labels.js +3 -2
- package/dist/cjs/hooks/useSeries/index.js +8 -2
- package/dist/cjs/hooks/useShapes/area/index.js +2 -2
- package/dist/cjs/hooks/useShapes/area/prepare-data.js +4 -4
- package/dist/cjs/hooks/useShapes/area/types.d.ts +2 -2
- package/dist/cjs/hooks/useShapes/bar-x/index.js +2 -2
- package/dist/cjs/hooks/useShapes/bar-x/prepare-data.js +15 -12
- package/dist/cjs/hooks/useShapes/bar-x/types.d.ts +2 -2
- package/dist/cjs/hooks/useShapes/index.js +18 -5
- package/dist/cjs/hooks/useShapes/line/index.js +7 -16
- package/dist/cjs/hooks/useShapes/line/prepare-data.d.ts +2 -0
- package/dist/cjs/hooks/useShapes/line/prepare-data.js +11 -7
- package/dist/cjs/hooks/useShapes/line/types.d.ts +2 -2
- package/dist/cjs/types/chart-ui.d.ts +4 -0
- package/dist/esm/components/ChartInner/index.js +3 -3
- package/dist/esm/components/ChartInner/useChartInnerHandlers.d.ts +3 -3
- package/dist/esm/components/ChartInner/useChartInnerHandlers.js +10 -12
- package/dist/esm/components/ChartInner/useChartInnerProps.js +8 -4
- package/dist/esm/components/ChartInner/utils/title.d.ts +3 -2
- package/dist/esm/components/ChartInner/utils/title.js +19 -14
- package/dist/esm/components/Title/index.d.ts +1 -3
- package/dist/esm/components/Title/index.js +2 -2
- package/dist/esm/core/constants/index.d.ts +0 -1
- package/dist/esm/core/constants/index.js +0 -1
- package/dist/esm/core/utils/labels.d.ts +1 -1
- package/dist/esm/core/utils/labels.js +3 -2
- package/dist/esm/hooks/useSeries/index.js +8 -2
- package/dist/esm/hooks/useShapes/area/index.js +2 -2
- package/dist/esm/hooks/useShapes/area/prepare-data.js +4 -4
- package/dist/esm/hooks/useShapes/area/types.d.ts +2 -2
- package/dist/esm/hooks/useShapes/bar-x/index.js +2 -2
- package/dist/esm/hooks/useShapes/bar-x/prepare-data.js +15 -12
- package/dist/esm/hooks/useShapes/bar-x/types.d.ts +2 -2
- package/dist/esm/hooks/useShapes/index.js +18 -5
- package/dist/esm/hooks/useShapes/line/index.js +7 -16
- package/dist/esm/hooks/useShapes/line/prepare-data.d.ts +2 -0
- package/dist/esm/hooks/useShapes/line/prepare-data.js +11 -7
- package/dist/esm/hooks/useShapes/line/types.d.ts +2 -2
- package/dist/esm/types/chart-ui.d.ts +4 -0
- package/package.json +1 -1
- package/dist/cjs/core/constants/misc.d.ts +0 -1
- package/dist/cjs/core/constants/misc.js +0 -7
- package/dist/esm/core/constants/misc.d.ts +0 -1
- package/dist/esm/core/constants/misc.js +0 -7
|
@@ -71,7 +71,7 @@ export const ChartInner = (props) => {
|
|
|
71
71
|
value: allPreparedSeries,
|
|
72
72
|
delay: DEBOUNCED_VALUE_DELAY,
|
|
73
73
|
});
|
|
74
|
-
const { handleChartClick,
|
|
74
|
+
const { handleChartClick, handlePointerLeave, throttledHandlePointerMove, throttledHandleTouchMove, } = useChartInnerHandlers({
|
|
75
75
|
boundsHeight,
|
|
76
76
|
boundsOffsetLeft,
|
|
77
77
|
boundsOffsetTop,
|
|
@@ -256,7 +256,7 @@ export const ChartInner = (props) => {
|
|
|
256
256
|
React.createElement("rect", { x: 0, y: 0, width: boundsWidth, height: boundsHeight })),
|
|
257
257
|
React.createElement("clipPath", { id: getClipPathIdByBounds({ clipPathId, bounds: 'horizontal' }) },
|
|
258
258
|
React.createElement("rect", { x: 0, y: -boundsHeight, width: boundsWidth, height: boundsHeight * 3 }))),
|
|
259
|
-
preparedTitle && React.createElement(Title, Object.assign({}, preparedTitle
|
|
259
|
+
preparedTitle && React.createElement(Title, Object.assign({}, preparedTitle)),
|
|
260
260
|
React.createElement("g", { transform: `translate(0, ${boundsOffsetTop})` }, preparedSplit === null || preparedSplit === void 0 ? void 0 : preparedSplit.plots.map((plot, index) => {
|
|
261
261
|
return React.createElement(PlotTitle, { key: `plot-${index}`, title: plot.title });
|
|
262
262
|
})),
|
|
@@ -283,7 +283,7 @@ export const ChartInner = (props) => {
|
|
|
283
283
|
React.createElement("svg", { ref: svgRef, width: width, height: height,
|
|
284
284
|
// We use onPointerMove here because onMouseMove works incorrectly when the zoom setting is enabled:
|
|
285
285
|
// when starting to select an area, the tooltip remains in the position where the selection began
|
|
286
|
-
onPointerMove:
|
|
286
|
+
onPointerMove: throttledHandlePointerMove, onPointerLeave: handlePointerLeave, onTouchStart: throttledHandleTouchMove, onTouchMove: throttledHandleTouchMove, onClick: handleChartClick }, initialized ? chartContent : null),
|
|
287
287
|
React.createElement("div", { className: b('html-layer'), ref: setHtmlLayout, style: {
|
|
288
288
|
'--g-html-layout-transform': `translate(${boundsOffsetLeft}px, ${boundsOffsetTop}px)`,
|
|
289
289
|
} }),
|
|
@@ -23,8 +23,8 @@ type Props = {
|
|
|
23
23
|
};
|
|
24
24
|
export declare function useChartInnerHandlers(props: Props): {
|
|
25
25
|
handleChartClick: (event: React.MouseEvent<SVGSVGElement>) => void;
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
throttledHandleTouchMove: import("lodash").DebouncedFuncLeading<React.TouchEventHandler<SVGSVGElement
|
|
26
|
+
handlePointerLeave: React.PointerEventHandler<SVGSVGElement>;
|
|
27
|
+
throttledHandlePointerMove: import("lodash").DebouncedFuncLeading<React.PointerEventHandler<SVGSVGElement>>;
|
|
28
|
+
throttledHandleTouchMove: import("lodash").DebouncedFuncLeading<React.TouchEventHandler<SVGSVGElement>>;
|
|
29
29
|
};
|
|
30
30
|
export {};
|
|
@@ -2,7 +2,6 @@ import React from 'react';
|
|
|
2
2
|
import { pointer } from 'd3-selection';
|
|
3
3
|
import get from 'lodash/get';
|
|
4
4
|
import throttle from 'lodash/throttle';
|
|
5
|
-
import { IS_TOUCH_ENABLED } from '../../core/constants';
|
|
6
5
|
import { EventType } from '../../core/utils';
|
|
7
6
|
import { getClosestPoints } from '../../core/utils/get-closest-data';
|
|
8
7
|
import { getHoveredPlots } from '../../core/utils/get-hovered-plots';
|
|
@@ -47,18 +46,19 @@ export function useChartInnerHandlers(props) {
|
|
|
47
46
|
hoveredPlotBands: plotBands,
|
|
48
47
|
}, event);
|
|
49
48
|
};
|
|
50
|
-
const
|
|
49
|
+
const handlePointerMove = (event) => {
|
|
50
|
+
if (event.pointerType === 'touch') {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
51
53
|
const [pointerX, pointerY] = pointer(event, svgContainer);
|
|
52
54
|
handleMove([pointerX, pointerY], event);
|
|
53
55
|
};
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
: throttle(handleMouseMove, tooltipThrottle);
|
|
57
|
-
const handleMouseLeave = (event) => {
|
|
56
|
+
const throttledHandlePointerMove = throttle(handlePointerMove, tooltipThrottle);
|
|
57
|
+
const handlePointerLeave = (event) => {
|
|
58
58
|
if (tooltipPinned) {
|
|
59
59
|
return;
|
|
60
60
|
}
|
|
61
|
-
|
|
61
|
+
throttledHandlePointerMove.cancel();
|
|
62
62
|
dispatcher.call(EventType.HOVER_SHAPE, {}, undefined);
|
|
63
63
|
dispatcher.call(EventType.POINTERMOVE_CHART, {}, undefined, event);
|
|
64
64
|
};
|
|
@@ -67,9 +67,7 @@ export function useChartInnerHandlers(props) {
|
|
|
67
67
|
const [pointerX, pointerY] = pointer(touch, svgContainer);
|
|
68
68
|
handleMove([pointerX, pointerY], event);
|
|
69
69
|
};
|
|
70
|
-
const throttledHandleTouchMove =
|
|
71
|
-
? throttle(handleTouchMove, tooltipThrottle)
|
|
72
|
-
: undefined;
|
|
70
|
+
const throttledHandleTouchMove = throttle(handleTouchMove, tooltipThrottle);
|
|
73
71
|
const handleChartClick = (event) => {
|
|
74
72
|
const [pointerX, pointerY] = pointer(event, svgContainer);
|
|
75
73
|
const x = pointerX - boundsOffsetLeft;
|
|
@@ -116,8 +114,8 @@ export function useChartInnerHandlers(props) {
|
|
|
116
114
|
};
|
|
117
115
|
return {
|
|
118
116
|
handleChartClick,
|
|
119
|
-
|
|
120
|
-
|
|
117
|
+
handlePointerLeave,
|
|
118
|
+
throttledHandlePointerMove,
|
|
121
119
|
throttledHandleTouchMove,
|
|
122
120
|
};
|
|
123
121
|
}
|
|
@@ -48,15 +48,19 @@ export function useChartInnerProps(props) {
|
|
|
48
48
|
currentRunRef.current++;
|
|
49
49
|
const currentRun = currentRunRef.current;
|
|
50
50
|
(async function () {
|
|
51
|
-
var _a, _b, _c;
|
|
51
|
+
var _a, _b, _c, _d;
|
|
52
52
|
const chartDataChanged = !(previousChartData.current && isEqual(previousChartData.current, data));
|
|
53
|
-
const preparedTitle = await getPreparedTitle({
|
|
53
|
+
const preparedTitle = await getPreparedTitle({
|
|
54
|
+
title: data.title,
|
|
55
|
+
chartWidth: width,
|
|
56
|
+
chartMargin: (_a = data.chart) === null || _a === void 0 ? void 0 : _a.margin,
|
|
57
|
+
});
|
|
54
58
|
const preparedChart = getPreparedChart({
|
|
55
59
|
chart: data.chart,
|
|
56
60
|
seriesData: data.series.data,
|
|
57
61
|
preparedTitle,
|
|
58
62
|
});
|
|
59
|
-
const colors = (
|
|
63
|
+
const colors = (_b = data.colors) !== null && _b !== void 0 ? _b : DEFAULT_PALETTE;
|
|
60
64
|
const normalizedSeriesData = getSortedSeriesData({
|
|
61
65
|
seriesData: data.series.data,
|
|
62
66
|
xAxis: data.xAxis,
|
|
@@ -79,7 +83,7 @@ export function useChartInnerProps(props) {
|
|
|
79
83
|
});
|
|
80
84
|
}
|
|
81
85
|
else {
|
|
82
|
-
allPreparedSeries = (
|
|
86
|
+
allPreparedSeries = (_d = (_c = prevStateValue.current) === null || _c === void 0 ? void 0 : _c.allPreparedSeries) !== null && _d !== void 0 ? _d : [];
|
|
83
87
|
}
|
|
84
88
|
const activeLegendItems = selectedLegendItems !== null && selectedLegendItems !== void 0 ? selectedLegendItems : getActiveLegendItems(allPreparedSeries);
|
|
85
89
|
const visiblePreparedSeries = getVisibleSeries({
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { PreparedTitle } from '../../../hooks/types';
|
|
2
|
-
import type { ChartData } from '../../../types';
|
|
3
|
-
export declare const getPreparedTitle: ({ title, chartWidth, }: {
|
|
2
|
+
import type { ChartData, ChartMargin } from '../../../types';
|
|
3
|
+
export declare const getPreparedTitle: ({ title, chartWidth, chartMargin, }: {
|
|
4
4
|
title: ChartData["title"];
|
|
5
5
|
chartWidth: number;
|
|
6
|
+
chartMargin?: Partial<ChartMargin>;
|
|
6
7
|
}) => Promise<PreparedTitle | undefined>;
|
|
@@ -2,25 +2,30 @@ import get from 'lodash/get';
|
|
|
2
2
|
import { getTextSizeFn, getTextWithElipsis, wrapText } from '../../../core/utils';
|
|
3
3
|
const DEFAULT_TITLE_FONT_SIZE = '15px';
|
|
4
4
|
const DEFAULT_TITLE_MARGIN = 10;
|
|
5
|
-
export const getPreparedTitle = async ({ title, chartWidth, }) => {
|
|
6
|
-
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
5
|
+
export const getPreparedTitle = async ({ title, chartWidth, chartMargin, }) => {
|
|
6
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
|
7
|
+
const chartMarginTop = (_a = chartMargin === null || chartMargin === void 0 ? void 0 : chartMargin.top) !== null && _a !== void 0 ? _a : 0;
|
|
8
|
+
const chartMarginLeft = (_b = chartMargin === null || chartMargin === void 0 ? void 0 : chartMargin.left) !== null && _b !== void 0 ? _b : 0;
|
|
9
|
+
const chartMarginRight = (_c = chartMargin === null || chartMargin === void 0 ? void 0 : chartMargin.right) !== null && _c !== void 0 ? _c : 0;
|
|
7
10
|
const titleText = get(title, 'text');
|
|
8
11
|
const titleStyle = {
|
|
9
|
-
fontSize: (
|
|
10
|
-
fontWeight: (
|
|
11
|
-
fontColor: (
|
|
12
|
+
fontSize: (_e = (_d = title === null || title === void 0 ? void 0 : title.style) === null || _d === void 0 ? void 0 : _d.fontSize) !== null && _e !== void 0 ? _e : DEFAULT_TITLE_FONT_SIZE,
|
|
13
|
+
fontWeight: (_g = (_f = title === null || title === void 0 ? void 0 : title.style) === null || _f === void 0 ? void 0 : _f.fontWeight) !== null && _g !== void 0 ? _g : 'var(--g-text-subheader-font-weight)',
|
|
14
|
+
fontColor: (_j = (_h = title === null || title === void 0 ? void 0 : title.style) === null || _h === void 0 ? void 0 : _h.fontColor) !== null && _j !== void 0 ? _j : 'var(--g-color-text-primary)',
|
|
12
15
|
};
|
|
13
16
|
if (!titleText) {
|
|
14
17
|
return undefined;
|
|
15
18
|
}
|
|
16
19
|
const getTitleTextSize = getTextSizeFn({ style: titleStyle });
|
|
17
|
-
const maxRowCount = (
|
|
20
|
+
const maxRowCount = (_k = title === null || title === void 0 ? void 0 : title.maxRowCount) !== null && _k !== void 0 ? _k : 1;
|
|
18
21
|
const contentRows = [];
|
|
22
|
+
const usableWidth = chartWidth - chartMarginLeft - chartMarginRight;
|
|
23
|
+
const xCenter = chartMarginLeft + usableWidth / 2;
|
|
19
24
|
if (maxRowCount > 1) {
|
|
20
25
|
let titleTextRows = await wrapText({
|
|
21
26
|
text: titleText,
|
|
22
27
|
style: titleStyle,
|
|
23
|
-
width:
|
|
28
|
+
width: usableWidth,
|
|
24
29
|
getTextSize: getTitleTextSize,
|
|
25
30
|
});
|
|
26
31
|
titleTextRows = titleTextRows.reduce((acc, row, index) => {
|
|
@@ -38,15 +43,15 @@ export const getPreparedTitle = async ({ title, chartWidth, }) => {
|
|
|
38
43
|
if (i === titleTextRows.length - 1) {
|
|
39
44
|
textRowContent = await getTextWithElipsis({
|
|
40
45
|
text: textRowContent,
|
|
41
|
-
maxWidth:
|
|
46
|
+
maxWidth: usableWidth,
|
|
42
47
|
getTextWidth: async (s) => (await getTitleTextSize(s)).width,
|
|
43
48
|
});
|
|
44
49
|
}
|
|
45
50
|
const textRowSize = await getTitleTextSize(textRowContent);
|
|
46
51
|
contentRows.push({
|
|
47
52
|
text: textRowContent,
|
|
48
|
-
x:
|
|
49
|
-
y: textRow.y + textRowSize.hangingOffset,
|
|
53
|
+
x: xCenter,
|
|
54
|
+
y: textRow.y + textRowSize.hangingOffset + chartMarginTop,
|
|
50
55
|
size: textRowSize,
|
|
51
56
|
});
|
|
52
57
|
}
|
|
@@ -54,14 +59,14 @@ export const getPreparedTitle = async ({ title, chartWidth, }) => {
|
|
|
54
59
|
else {
|
|
55
60
|
const truncatedText = await getTextWithElipsis({
|
|
56
61
|
text: titleText,
|
|
57
|
-
maxWidth:
|
|
62
|
+
maxWidth: usableWidth,
|
|
58
63
|
getTextWidth: async (s) => (await getTitleTextSize(s)).width,
|
|
59
64
|
});
|
|
60
65
|
const textSize = await getTitleTextSize(truncatedText);
|
|
61
66
|
contentRows.push({
|
|
62
67
|
text: truncatedText,
|
|
63
|
-
x:
|
|
64
|
-
y: textSize.hangingOffset,
|
|
68
|
+
x: xCenter,
|
|
69
|
+
y: textSize.hangingOffset + chartMarginTop,
|
|
65
70
|
size: textSize,
|
|
66
71
|
});
|
|
67
72
|
}
|
|
@@ -71,7 +76,7 @@ export const getPreparedTitle = async ({ title, chartWidth, }) => {
|
|
|
71
76
|
text: titleText,
|
|
72
77
|
style: titleStyle,
|
|
73
78
|
height: titleHeight,
|
|
74
|
-
margin: (
|
|
79
|
+
margin: (_l = title === null || title === void 0 ? void 0 : title.margin) !== null && _l !== void 0 ? _l : DEFAULT_TITLE_MARGIN,
|
|
75
80
|
qa: title === null || title === void 0 ? void 0 : title.qa,
|
|
76
81
|
contentRows,
|
|
77
82
|
};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
export const Title = (props) => {
|
|
3
|
-
const {
|
|
3
|
+
const { style, qa, contentRows } = props;
|
|
4
4
|
return (React.createElement("text", { dominantBaseline: "hanging", textAnchor: "middle", style: {
|
|
5
5
|
fill: style === null || style === void 0 ? void 0 : style.fontColor,
|
|
6
6
|
fontSize: style === null || style === void 0 ? void 0 : style.fontSize,
|
|
7
7
|
fontWeight: style === null || style === void 0 ? void 0 : style.fontWeight,
|
|
8
|
-
}, "data-qa": qa }, contentRows.map((row, i) => (React.createElement("tspan", { key: i, x:
|
|
8
|
+
}, "data-qa": qa }, contentRows.map((row, i) => (React.createElement("tspan", { key: i, x: row.x, y: row.y, dominantBaseline: "hanging", dangerouslySetInnerHTML: { __html: row.text } })))));
|
|
9
9
|
};
|
|
@@ -3,7 +3,7 @@ export declare function getLeftPosition(label: LabelData): number;
|
|
|
3
3
|
export declare function getOverlappingByX(rect1: LabelData | HtmlItem, rect2: LabelData | HtmlItem, gap?: number): number;
|
|
4
4
|
export declare function getOverlappingByY(rect1: LabelData | HtmlItem, rect2: LabelData | HtmlItem, gap?: number): number;
|
|
5
5
|
export declare function isLabelsOverlapping<T extends LabelData | HtmlItem>(label1: T, label2: T, padding?: number): boolean;
|
|
6
|
-
export declare function filterOverlappingLabels<T extends LabelData | HtmlItem>(labels: T[]): T[];
|
|
6
|
+
export declare function filterOverlappingLabels<T extends LabelData | HtmlItem>(labels: T[], renderedSvgLabels?: T[]): T[];
|
|
7
7
|
export declare function getSvgLabelConstraintedPosition(args: {
|
|
8
8
|
boundsHeight: number;
|
|
9
9
|
boundsWidth: number;
|
|
@@ -32,11 +32,12 @@ export function getOverlappingByY(rect1, rect2, gap = 0) {
|
|
|
32
32
|
export function isLabelsOverlapping(label1, label2, padding = 0) {
|
|
33
33
|
return Boolean(getOverlappingByX(label1, label2, padding) && getOverlappingByY(label1, label2, padding));
|
|
34
34
|
}
|
|
35
|
-
export function filterOverlappingLabels(labels) {
|
|
35
|
+
export function filterOverlappingLabels(labels, renderedSvgLabels) {
|
|
36
36
|
const result = [];
|
|
37
37
|
const sorted = sortBy(labels, (d) => d.y, (d) => ('textAnchor' in d ? getLeftPosition(d) : d.x));
|
|
38
38
|
sorted.forEach((label) => {
|
|
39
|
-
if (!
|
|
39
|
+
if (!(renderedSvgLabels === null || renderedSvgLabels === void 0 ? void 0 : renderedSvgLabels.some((l) => isLabelsOverlapping(label, l))) &&
|
|
40
|
+
!result.some((l) => isLabelsOverlapping(label, l))) {
|
|
40
41
|
result.push(label);
|
|
41
42
|
}
|
|
42
43
|
});
|
|
@@ -13,14 +13,20 @@ export const getVisibleSeries = ({ preparedSeries, activeLegendItems, }) => {
|
|
|
13
13
|
export const getPreparedSeries = async ({ seriesData, seriesOptions, colors, preparedLegend, }) => {
|
|
14
14
|
const seriesNames = getSeriesNames(seriesData);
|
|
15
15
|
const colorScale = scaleOrdinal(seriesNames, colors);
|
|
16
|
-
const groupedSeries = group(seriesData, (item) =>
|
|
16
|
+
const groupedSeries = group(seriesData, (item, index) => {
|
|
17
|
+
if (item.type === 'line') {
|
|
18
|
+
return `${item.type}_${index}`;
|
|
19
|
+
}
|
|
20
|
+
return item.type;
|
|
21
|
+
});
|
|
17
22
|
const acc = [];
|
|
18
23
|
if (!preparedLegend) {
|
|
19
24
|
return acc;
|
|
20
25
|
}
|
|
21
26
|
const list = Array.from(groupedSeries);
|
|
22
27
|
for (let i = 0; i < list.length; i++) {
|
|
23
|
-
const [
|
|
28
|
+
const [_groupId, seriesList] = list[i];
|
|
29
|
+
const seriesType = seriesList[0].type;
|
|
24
30
|
acc.push(...(await prepareSeries({
|
|
25
31
|
type: seriesType,
|
|
26
32
|
series: seriesList,
|
|
@@ -60,7 +60,7 @@ export const AreaSeriesShapes = (args) => {
|
|
|
60
60
|
.attr('fill', (d) => d.color)
|
|
61
61
|
.attr('opacity', (d) => d.opacity);
|
|
62
62
|
let dataLabels = preparedData.reduce((acc, d) => {
|
|
63
|
-
return acc.concat(d.
|
|
63
|
+
return acc.concat(d.svgLabels);
|
|
64
64
|
}, []);
|
|
65
65
|
if (!allowOverlapDataLabels) {
|
|
66
66
|
dataLabels = filterOverlappingLabels(dataLabels);
|
|
@@ -191,7 +191,7 @@ export const AreaSeriesShapes = (args) => {
|
|
|
191
191
|
};
|
|
192
192
|
}, [allowOverlapDataLabels, dispatcher, preparedData, seriesOptions]);
|
|
193
193
|
const htmlLayerData = React.useMemo(() => {
|
|
194
|
-
const items = preparedData.map((d) => d === null || d === void 0 ? void 0 : d.
|
|
194
|
+
const items = preparedData.map((d) => d === null || d === void 0 ? void 0 : d.htmlLabels).flat();
|
|
195
195
|
if (allowOverlapDataLabels) {
|
|
196
196
|
return { htmlElements: items };
|
|
197
197
|
}
|
|
@@ -300,7 +300,7 @@ export const prepareAreaData = async (args) => {
|
|
|
300
300
|
seriesStackData.push({
|
|
301
301
|
points,
|
|
302
302
|
markers,
|
|
303
|
-
|
|
303
|
+
svgLabels: [],
|
|
304
304
|
color: s.color,
|
|
305
305
|
opacity: s.opacity,
|
|
306
306
|
width: s.lineWidth,
|
|
@@ -308,7 +308,7 @@ export const prepareAreaData = async (args) => {
|
|
|
308
308
|
hovered: false,
|
|
309
309
|
active: true,
|
|
310
310
|
id: s.id,
|
|
311
|
-
|
|
311
|
+
htmlLabels: [],
|
|
312
312
|
});
|
|
313
313
|
}
|
|
314
314
|
for (let itemIndex = 0; itemIndex < seriesStackData.length; itemIndex++) {
|
|
@@ -323,8 +323,8 @@ export const prepareAreaData = async (args) => {
|
|
|
323
323
|
yAxisTop: itemYAxisTop,
|
|
324
324
|
isOutsideBounds,
|
|
325
325
|
});
|
|
326
|
-
item.
|
|
327
|
-
item.
|
|
326
|
+
item.svgLabels.push(...labelsData.svgLabels);
|
|
327
|
+
item.htmlLabels.push(...labelsData.htmlLabels);
|
|
328
328
|
}
|
|
329
329
|
}
|
|
330
330
|
result.push(...seriesStackData);
|
|
@@ -49,7 +49,7 @@ export const BarXSeriesShapes = (args) => {
|
|
|
49
49
|
.attr('fill', (d) => d.data.color || d.series.color)
|
|
50
50
|
.attr('opacity', (d) => d.opacity)
|
|
51
51
|
.attr('cursor', (d) => d.series.cursor);
|
|
52
|
-
let dataLabels = preparedData.map((d) => d.
|
|
52
|
+
let dataLabels = preparedData.map((d) => d.svgLabels).flat();
|
|
53
53
|
if (!allowOverlapDataLabels) {
|
|
54
54
|
dataLabels = filterOverlappingLabels(dataLabels);
|
|
55
55
|
}
|
|
@@ -114,7 +114,7 @@ export const BarXSeriesShapes = (args) => {
|
|
|
114
114
|
};
|
|
115
115
|
}, [allowOverlapDataLabels, dispatcher, preparedData, seriesOptions]);
|
|
116
116
|
const htmlLayerData = React.useMemo(() => {
|
|
117
|
-
const items = preparedData.map((d) => d === null || d === void 0 ? void 0 : d.
|
|
117
|
+
const items = preparedData.map((d) => d === null || d === void 0 ? void 0 : d.htmlLabels).flat();
|
|
118
118
|
if (allowOverlapDataLabels) {
|
|
119
119
|
return { htmlElements: items };
|
|
120
120
|
}
|
|
@@ -174,7 +174,8 @@ export const prepareBarXData = async (args) => {
|
|
|
174
174
|
opacity: get(yValue.data, 'opacity', null),
|
|
175
175
|
data: yValue.data,
|
|
176
176
|
series: yValue.series,
|
|
177
|
-
|
|
177
|
+
htmlLabels: [],
|
|
178
|
+
svgLabels: [],
|
|
178
179
|
isLastStackItem,
|
|
179
180
|
};
|
|
180
181
|
stackItems.push(barData);
|
|
@@ -211,17 +212,19 @@ export const prepareBarXData = async (args) => {
|
|
|
211
212
|
!isRangeSlider &&
|
|
212
213
|
(!isBarOutsideBounds || isZeroValue)) {
|
|
213
214
|
const label = await getLabelData(barData, xMax);
|
|
214
|
-
if (
|
|
215
|
-
barData.
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
215
|
+
if (label) {
|
|
216
|
+
if (barData.series.dataLabels.html) {
|
|
217
|
+
barData.htmlLabels.push({
|
|
218
|
+
x: label.x,
|
|
219
|
+
y: label.y,
|
|
220
|
+
content: label.text,
|
|
221
|
+
size: label.size,
|
|
222
|
+
style: label.style,
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
barData.svgLabels.push(label);
|
|
227
|
+
}
|
|
225
228
|
}
|
|
226
229
|
}
|
|
227
230
|
}
|
|
@@ -7,8 +7,8 @@ export type PreparedBarXData = Omit<TooltipDataChunkBarX, 'series'> & {
|
|
|
7
7
|
height: number;
|
|
8
8
|
opacity: number | null;
|
|
9
9
|
series: PreparedBarXSeries;
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
svgLabels: LabelData[];
|
|
11
|
+
htmlLabels: HtmlItem[];
|
|
12
12
|
isLastStackItem: boolean;
|
|
13
13
|
/**
|
|
14
14
|
* the utility field for storing the original height (for recalculations, etc.)
|
|
@@ -33,11 +33,20 @@ function shouldUseClipPathId(seriesType, clipPathBySeriesType) {
|
|
|
33
33
|
export async function getShapes(args) {
|
|
34
34
|
const { boundsWidth, boundsHeight, clipPathId, clipPathBySeriesType, dispatcher, htmlLayout, isOutsideBounds = IS_OUTSIDE_BOUNDS, isRangeSlider, series, seriesOptions, split, xAxis, xScale, yAxis, yScale, zoomState, } = args;
|
|
35
35
|
const visibleSeries = getOnlyVisibleSeries(series);
|
|
36
|
-
const groupedSeries = group(visibleSeries, (item) =>
|
|
36
|
+
const groupedSeries = group(visibleSeries, (item) => {
|
|
37
|
+
if (item.type === 'line') {
|
|
38
|
+
return item.id;
|
|
39
|
+
}
|
|
40
|
+
return item.type;
|
|
41
|
+
});
|
|
37
42
|
const shapesData = [];
|
|
38
43
|
const shapes = [];
|
|
39
|
-
|
|
40
|
-
|
|
44
|
+
const layers = [];
|
|
45
|
+
const groupedSeriesItems = Array.from(groupedSeries);
|
|
46
|
+
for (let index = groupedSeriesItems.length - 1; index >= 0; index--) {
|
|
47
|
+
const item = groupedSeriesItems[index];
|
|
48
|
+
const [groupId, chartSeries] = item;
|
|
49
|
+
const seriesType = chartSeries[0].type;
|
|
41
50
|
switch (seriesType) {
|
|
42
51
|
case SERIES_TYPE.BarX: {
|
|
43
52
|
if (xAxis && xScale && (yScale === null || yScale === void 0 ? void 0 : yScale.length)) {
|
|
@@ -54,6 +63,7 @@ export async function getShapes(args) {
|
|
|
54
63
|
});
|
|
55
64
|
shapes[index] = (React.createElement(BarXSeriesShapes, { key: SERIES_TYPE.BarX, dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData, htmlLayout: htmlLayout, clipPathId: clipPathId }));
|
|
56
65
|
shapesData.splice(index, 0, ...preparedData);
|
|
66
|
+
layers.push(...preparedData);
|
|
57
67
|
}
|
|
58
68
|
break;
|
|
59
69
|
}
|
|
@@ -100,14 +110,16 @@ export async function getShapes(args) {
|
|
|
100
110
|
split,
|
|
101
111
|
isOutsideBounds,
|
|
102
112
|
isRangeSlider,
|
|
113
|
+
otherLayers: layers,
|
|
103
114
|
});
|
|
104
115
|
const resultClipPathId = getSeriesClipPathId({
|
|
105
116
|
clipPathId,
|
|
106
117
|
yAxis,
|
|
107
118
|
zoomState,
|
|
108
119
|
});
|
|
109
|
-
shapes[index] = (React.createElement(LineSeriesShapes, { key:
|
|
120
|
+
shapes[index] = (React.createElement(LineSeriesShapes, { key: groupId, dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData, htmlLayout: htmlLayout, clipPathId: resultClipPathId }));
|
|
110
121
|
shapesData.splice(index, 0, ...preparedData);
|
|
122
|
+
layers.push(...preparedData);
|
|
111
123
|
}
|
|
112
124
|
break;
|
|
113
125
|
}
|
|
@@ -125,6 +137,7 @@ export async function getShapes(args) {
|
|
|
125
137
|
});
|
|
126
138
|
shapes[index] = (React.createElement(AreaSeriesShapes, { key: SERIES_TYPE.Area, dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData, htmlLayout: htmlLayout, clipPathId: clipPathId }));
|
|
127
139
|
shapesData.splice(index, 0, ...preparedData);
|
|
140
|
+
layers.push(...preparedData);
|
|
128
141
|
}
|
|
129
142
|
break;
|
|
130
143
|
}
|
|
@@ -217,7 +230,7 @@ export async function getShapes(args) {
|
|
|
217
230
|
});
|
|
218
231
|
}
|
|
219
232
|
}
|
|
220
|
-
}
|
|
233
|
+
}
|
|
221
234
|
return { shapes, shapesData };
|
|
222
235
|
}
|
|
223
236
|
export const useShapes = (args) => {
|
|
@@ -3,7 +3,7 @@ import { color } from 'd3-color';
|
|
|
3
3
|
import { select } from 'd3-selection';
|
|
4
4
|
import { line as lineGenerator } from 'd3-shape';
|
|
5
5
|
import get from 'lodash/get';
|
|
6
|
-
import {
|
|
6
|
+
import { getLineDashArray } from '../../../core/utils';
|
|
7
7
|
import { block } from '../../../utils';
|
|
8
8
|
import { HtmlLayer } from '../HtmlLayer';
|
|
9
9
|
import { getMarkerHaloVisibility, getMarkerVisibility, renderMarker, selectMarkerHalo, selectMarkerSymbol, setMarker, } from '../marker';
|
|
@@ -15,9 +15,6 @@ export const LineSeriesShapes = (args) => {
|
|
|
15
15
|
const plotRef = React.useRef(null);
|
|
16
16
|
const markersRef = React.useRef(null);
|
|
17
17
|
const hoverMarkersRef = React.useRef(null);
|
|
18
|
-
const allowOverlapDataLabels = React.useMemo(() => {
|
|
19
|
-
return preparedData.some((d) => d === null || d === void 0 ? void 0 : d.series.dataLabels.allowOverlap);
|
|
20
|
-
}, [preparedData]);
|
|
21
18
|
React.useEffect(() => {
|
|
22
19
|
if (!plotRef.current || !markersRef.current) {
|
|
23
20
|
return () => { };
|
|
@@ -46,12 +43,9 @@ export const LineSeriesShapes = (args) => {
|
|
|
46
43
|
.attr('stroke-dasharray', (d) => getLineDashArray(d.dashStyle, d.lineWidth))
|
|
47
44
|
.attr('opacity', (d) => d.opacity)
|
|
48
45
|
.attr('cursor', (d) => d.series.cursor);
|
|
49
|
-
|
|
50
|
-
return acc.concat(d.
|
|
46
|
+
const dataLabels = preparedData.reduce((acc, d) => {
|
|
47
|
+
return acc.concat(d.svgLabels);
|
|
51
48
|
}, []);
|
|
52
|
-
if (!allowOverlapDataLabels) {
|
|
53
|
-
dataLabels = filterOverlappingLabels(dataLabels);
|
|
54
|
-
}
|
|
55
49
|
const labelsSelection = plotSvgElement
|
|
56
50
|
.selectAll('text')
|
|
57
51
|
.data(dataLabels)
|
|
@@ -175,14 +169,11 @@ export const LineSeriesShapes = (args) => {
|
|
|
175
169
|
return () => {
|
|
176
170
|
dispatcher === null || dispatcher === void 0 ? void 0 : dispatcher.on('hover-shape.line', null);
|
|
177
171
|
};
|
|
178
|
-
}, [
|
|
172
|
+
}, [dispatcher, preparedData, seriesOptions]);
|
|
179
173
|
const htmlLayerData = React.useMemo(() => {
|
|
180
|
-
const items = preparedData.map((d) => d === null || d === void 0 ? void 0 : d.
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
}
|
|
184
|
-
return { htmlElements: filterOverlappingLabels(items) };
|
|
185
|
-
}, [allowOverlapDataLabels, preparedData]);
|
|
174
|
+
const items = preparedData.map((d) => d === null || d === void 0 ? void 0 : d.htmlLabels).flat();
|
|
175
|
+
return { htmlElements: items };
|
|
176
|
+
}, [preparedData]);
|
|
186
177
|
return (React.createElement(React.Fragment, null,
|
|
187
178
|
React.createElement("g", { ref: plotRef, className: b(), clipPath: `url(#${clipPathId})` }),
|
|
188
179
|
React.createElement("g", { ref: markersRef }),
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { PreparedSplit } from '../../../core/layout/split-types';
|
|
2
2
|
import type { ChartScale } from '../../../core/scales/types';
|
|
3
|
+
import type { ShapeDataWithLabels } from '../../../types';
|
|
3
4
|
import type { PreparedXAxis, PreparedYAxis } from '../../useAxis/types';
|
|
4
5
|
import type { PreparedLineSeries } from '../../useSeries/types';
|
|
5
6
|
import type { PreparedLineData } from './types';
|
|
@@ -12,4 +13,5 @@ export declare const prepareLineData: (args: {
|
|
|
12
13
|
split: PreparedSplit;
|
|
13
14
|
isOutsideBounds: (x: number, y: number) => boolean;
|
|
14
15
|
isRangeSlider?: boolean;
|
|
16
|
+
otherLayers: ShapeDataWithLabels[];
|
|
15
17
|
}) => Promise<PreparedLineData[]>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getLabelsSize, getTextSizeFn } from '../../../core/utils';
|
|
1
|
+
import { filterOverlappingLabels, getLabelsSize, getTextSizeFn } from '../../../core/utils';
|
|
2
2
|
import { getFormattedValue } from '../../../core/utils/format';
|
|
3
3
|
import { getXValue, getYValue } from '../utils';
|
|
4
4
|
async function getHtmlLabel(point, series, xMax) {
|
|
@@ -16,7 +16,7 @@ async function getHtmlLabel(point, series, xMax) {
|
|
|
16
16
|
}
|
|
17
17
|
export const prepareLineData = async (args) => {
|
|
18
18
|
var _a, _b, _c, _d;
|
|
19
|
-
const { series, xAxis, yAxis, xScale, yScale, split, isOutsideBounds, isRangeSlider } = args;
|
|
19
|
+
const { series, xAxis, yAxis, xScale, yScale, split, isOutsideBounds, isRangeSlider, otherLayers, } = args;
|
|
20
20
|
const [_xMin, xRangeMax] = xScale.range();
|
|
21
21
|
const xMax = xRangeMax;
|
|
22
22
|
const acc = [];
|
|
@@ -44,8 +44,8 @@ export const prepareLineData = async (args) => {
|
|
|
44
44
|
series: s,
|
|
45
45
|
};
|
|
46
46
|
});
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
let htmlElements = [];
|
|
48
|
+
let svgLabels = [];
|
|
49
49
|
if (s.dataLabels.enabled && !isRangeSlider) {
|
|
50
50
|
if (s.dataLabels.html) {
|
|
51
51
|
const list = await Promise.all(points.reduce((result, p) => {
|
|
@@ -83,11 +83,15 @@ export const prepareLineData = async (args) => {
|
|
|
83
83
|
series: s,
|
|
84
84
|
active: true,
|
|
85
85
|
};
|
|
86
|
-
|
|
86
|
+
svgLabels.push(labelData);
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
|
+
if (!s.dataLabels.allowOverlap) {
|
|
92
|
+
svgLabels = filterOverlappingLabels(svgLabels, otherLayers.map((l) => l.svgLabels).flat());
|
|
93
|
+
htmlElements = filterOverlappingLabels(htmlElements, otherLayers.map((l) => l.htmlLabels).flat());
|
|
94
|
+
}
|
|
91
95
|
let markers = [];
|
|
92
96
|
const hasPerPointNormalMarkers = s.data.some((d) => { var _a, _b, _c; return (_c = (_b = (_a = d.marker) === null || _a === void 0 ? void 0 : _a.states) === null || _b === void 0 ? void 0 : _b.normal) === null || _c === void 0 ? void 0 : _c.enabled; });
|
|
93
97
|
if (s.marker.states.normal.enabled || hasPerPointNormalMarkers) {
|
|
@@ -111,12 +115,12 @@ export const prepareLineData = async (args) => {
|
|
|
111
115
|
const result = {
|
|
112
116
|
points,
|
|
113
117
|
markers,
|
|
114
|
-
|
|
118
|
+
svgLabels: svgLabels,
|
|
115
119
|
series: s,
|
|
116
120
|
hovered: false,
|
|
117
121
|
active: true,
|
|
118
122
|
id: s.id,
|
|
119
|
-
htmlElements,
|
|
123
|
+
htmlLabels: htmlElements,
|
|
120
124
|
color: s.color,
|
|
121
125
|
lineWidth: (_c = (isRangeSlider ? s.rangeSlider.lineWidth : undefined)) !== null && _c !== void 0 ? _c : s.lineWidth,
|
|
122
126
|
dashStyle: s.dashStyle,
|