@gravity-ui/charts 1.42.4 → 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/AxisX/AxisX.js +27 -0
- package/dist/cjs/components/AxisX/prepare-axis-data.js +41 -0
- package/dist/cjs/components/AxisX/types.d.ts +18 -1
- package/dist/cjs/components/AxisY/AxisY.js +27 -0
- package/dist/cjs/components/AxisY/prepare-axis-data.js +41 -0
- package/dist/cjs/components/AxisY/types.d.ts +18 -1
- package/dist/cjs/components/ChartInner/index.js +19 -3
- package/dist/cjs/components/ChartInner/useChartInnerHandlers.d.ts +3 -3
- package/dist/cjs/components/ChartInner/useChartInnerHandlers.js +16 -17
- package/dist/cjs/components/ChartInner/useChartInnerProps.d.ts +2 -4
- package/dist/cjs/components/ChartInner/useChartInnerProps.js +8 -4
- package/dist/cjs/components/ChartInner/useDefaultState.js +4 -3
- package/dist/cjs/components/ChartInner/utils/chart.js +1 -1
- package/dist/cjs/components/ChartInner/utils/normalized-original-data.d.ts +1 -0
- package/dist/cjs/components/ChartInner/utils/title.d.ts +4 -2
- package/dist/cjs/components/ChartInner/utils/title.js +77 -14
- package/dist/cjs/components/Title/index.d.ts +1 -3
- package/dist/cjs/components/Title/index.js +3 -5
- package/dist/cjs/components/Tooltip/ChartTooltipContent.d.ts +2 -1
- package/dist/cjs/components/Tooltip/ChartTooltipContent.js +3 -2
- package/dist/cjs/components/Tooltip/index.js +2 -2
- package/dist/cjs/core/axes/types.d.ts +26 -9
- package/dist/cjs/core/axes/x-axis.js +14 -1
- package/dist/cjs/core/axes/y-axis.js +20 -7
- package/dist/cjs/core/constants/defaults/axis.d.ts +1 -0
- package/dist/cjs/core/constants/defaults/axis.js +1 -0
- package/dist/cjs/core/constants/index.d.ts +0 -1
- package/dist/cjs/core/constants/index.js +0 -1
- package/dist/cjs/core/scales/y-scale.js +37 -13
- package/dist/cjs/core/types/chart/axis.d.ts +43 -1
- package/dist/cjs/core/types/chart/title.d.ts +10 -0
- package/dist/cjs/core/types/chart/tooltip.d.ts +3 -1
- package/dist/cjs/core/utils/common.js +1 -1
- package/dist/cjs/core/utils/get-hovered-plots.d.ts +3 -2
- package/dist/cjs/core/utils/get-hovered-plots.js +28 -4
- package/dist/cjs/core/utils/labels.d.ts +1 -1
- package/dist/cjs/core/utils/labels.js +3 -2
- package/dist/cjs/core/utils/text.js +12 -2
- package/dist/cjs/hooks/types.d.ts +5 -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 +15 -10
- 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 +27 -16
- 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/hooks/useTooltip/index.d.ts +3 -2
- package/dist/cjs/hooks/useTooltip/index.js +5 -3
- package/dist/cjs/types/chart-ui.d.ts +4 -0
- package/dist/esm/components/AxisX/AxisX.js +27 -0
- package/dist/esm/components/AxisX/prepare-axis-data.js +41 -0
- package/dist/esm/components/AxisX/types.d.ts +18 -1
- package/dist/esm/components/AxisY/AxisY.js +27 -0
- package/dist/esm/components/AxisY/prepare-axis-data.js +41 -0
- package/dist/esm/components/AxisY/types.d.ts +18 -1
- package/dist/esm/components/ChartInner/index.js +19 -3
- package/dist/esm/components/ChartInner/useChartInnerHandlers.d.ts +3 -3
- package/dist/esm/components/ChartInner/useChartInnerHandlers.js +16 -17
- package/dist/esm/components/ChartInner/useChartInnerProps.d.ts +2 -4
- package/dist/esm/components/ChartInner/useChartInnerProps.js +8 -4
- package/dist/esm/components/ChartInner/useDefaultState.js +4 -3
- package/dist/esm/components/ChartInner/utils/chart.js +1 -1
- package/dist/esm/components/ChartInner/utils/normalized-original-data.d.ts +1 -0
- package/dist/esm/components/ChartInner/utils/title.d.ts +4 -2
- package/dist/esm/components/ChartInner/utils/title.js +77 -14
- package/dist/esm/components/Title/index.d.ts +1 -3
- package/dist/esm/components/Title/index.js +3 -5
- package/dist/esm/components/Tooltip/ChartTooltipContent.d.ts +2 -1
- package/dist/esm/components/Tooltip/ChartTooltipContent.js +3 -2
- package/dist/esm/components/Tooltip/index.js +2 -2
- package/dist/esm/core/axes/types.d.ts +26 -9
- package/dist/esm/core/axes/x-axis.js +14 -1
- package/dist/esm/core/axes/y-axis.js +20 -7
- package/dist/esm/core/constants/defaults/axis.d.ts +1 -0
- package/dist/esm/core/constants/defaults/axis.js +1 -0
- package/dist/esm/core/constants/index.d.ts +0 -1
- package/dist/esm/core/constants/index.js +0 -1
- package/dist/esm/core/scales/y-scale.js +37 -13
- package/dist/esm/core/types/chart/axis.d.ts +43 -1
- package/dist/esm/core/types/chart/title.d.ts +10 -0
- package/dist/esm/core/types/chart/tooltip.d.ts +3 -1
- package/dist/esm/core/utils/common.js +1 -1
- package/dist/esm/core/utils/get-hovered-plots.d.ts +3 -2
- package/dist/esm/core/utils/get-hovered-plots.js +28 -4
- package/dist/esm/core/utils/labels.d.ts +1 -1
- package/dist/esm/core/utils/labels.js +3 -2
- package/dist/esm/core/utils/text.js +12 -2
- package/dist/esm/hooks/types.d.ts +5 -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 +15 -10
- 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 +27 -16
- 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/hooks/useTooltip/index.d.ts +3 -2
- package/dist/esm/hooks/useTooltip/index.js +5 -3
- 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
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { getBandsPosition, isBandScale } from './axis/common';
|
|
2
|
-
const PLOT_LINE_HIT_THRESHOLD_PX = 4;
|
|
3
2
|
function getHoveredAxisPlotBands(args) {
|
|
4
3
|
const { pointerPx, plotBands, scale, axis } = args;
|
|
5
4
|
const axisScale = scale;
|
|
@@ -22,16 +21,31 @@ function getHoveredAxisPlotLines(args) {
|
|
|
22
21
|
}
|
|
23
22
|
for (const line of plotLines) {
|
|
24
23
|
const linePx = Number(scale(line.value));
|
|
25
|
-
if (Math.abs(pointerPx - linePx) <=
|
|
24
|
+
if (Math.abs(pointerPx - linePx) <= line.hoverThreshold + line.width / 2) {
|
|
26
25
|
result.push(line);
|
|
27
26
|
}
|
|
28
27
|
}
|
|
29
28
|
return result;
|
|
30
29
|
}
|
|
30
|
+
function getHoveredAxisPlotShapes(args) {
|
|
31
|
+
const { pointerX, pointerY, plotShapes } = args;
|
|
32
|
+
const result = [];
|
|
33
|
+
for (const shape of plotShapes) {
|
|
34
|
+
const left = shape.x + shape.hitbox.x;
|
|
35
|
+
const top = shape.y + shape.hitbox.y;
|
|
36
|
+
const inX = pointerX >= left && pointerX <= left + shape.hitbox.width;
|
|
37
|
+
const inY = pointerY >= top && pointerY <= top + shape.hitbox.height;
|
|
38
|
+
if (inX && inY) {
|
|
39
|
+
result.push(shape);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return result;
|
|
43
|
+
}
|
|
31
44
|
export function getHoveredPlots(args) {
|
|
32
45
|
const { pointerX, pointerY, xAxis, yAxis, xScale, yScale } = args;
|
|
33
|
-
const plotLines = [];
|
|
34
46
|
const plotBands = [];
|
|
47
|
+
const plotLines = [];
|
|
48
|
+
const plotShapes = [];
|
|
35
49
|
if (xAxis && xScale) {
|
|
36
50
|
plotBands.push(...getHoveredAxisPlotBands({
|
|
37
51
|
pointerPx: pointerX,
|
|
@@ -44,6 +58,11 @@ export function getHoveredPlots(args) {
|
|
|
44
58
|
plotLines: xAxis.plotLines,
|
|
45
59
|
scale: xScale,
|
|
46
60
|
}));
|
|
61
|
+
plotShapes.push(...getHoveredAxisPlotShapes({
|
|
62
|
+
pointerX,
|
|
63
|
+
pointerY,
|
|
64
|
+
plotShapes: xAxis.plotShapes,
|
|
65
|
+
}));
|
|
47
66
|
}
|
|
48
67
|
for (let i = 0; i < yAxis.length; i++) {
|
|
49
68
|
const yAxisItem = yAxis[i];
|
|
@@ -62,6 +81,11 @@ export function getHoveredPlots(args) {
|
|
|
62
81
|
plotLines: yAxisItem.plotLines,
|
|
63
82
|
scale: yScaleItem,
|
|
64
83
|
}));
|
|
84
|
+
plotShapes.push(...getHoveredAxisPlotShapes({
|
|
85
|
+
pointerX,
|
|
86
|
+
pointerY,
|
|
87
|
+
plotShapes: yAxisItem.plotShapes,
|
|
88
|
+
}));
|
|
65
89
|
}
|
|
66
|
-
return { plotLines,
|
|
90
|
+
return { plotBands, plotLines, plotShapes };
|
|
67
91
|
}
|
|
@@ -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
|
});
|
|
@@ -189,10 +189,20 @@ export function getTextSizeFn({ style }) {
|
|
|
189
189
|
const defaultFontFamily = computedStyle.getPropertyValue('font-family');
|
|
190
190
|
const defaultFontSize = computedStyle.getPropertyValue('font-size');
|
|
191
191
|
const defaultFontWeight = computedStyle.getPropertyValue('font-weight');
|
|
192
|
+
const resolveCSSVar = (value) => {
|
|
193
|
+
const match = value.match(/^var\(\s*([\w-]+)/);
|
|
194
|
+
if (match) {
|
|
195
|
+
return computedStyle.getPropertyValue(match[1]).trim() || value;
|
|
196
|
+
}
|
|
197
|
+
return value;
|
|
198
|
+
};
|
|
192
199
|
return async (str) => {
|
|
193
|
-
var _a, _b;
|
|
194
200
|
await document.fonts.ready;
|
|
195
|
-
|
|
201
|
+
const fontWeight = (style === null || style === void 0 ? void 0 : style.fontWeight)
|
|
202
|
+
? resolveCSSVar(String(style.fontWeight))
|
|
203
|
+
: defaultFontWeight;
|
|
204
|
+
const fontSize = (style === null || style === void 0 ? void 0 : style.fontSize) ? resolveCSSVar(style.fontSize) : defaultFontSize;
|
|
205
|
+
context.font = `${fontWeight} ${fontSize} ${defaultFontFamily}`;
|
|
196
206
|
const textMetric = context.measureText(unescapeHtml(str));
|
|
197
207
|
// we calculate hanging based on an approximate algorithm from chromium
|
|
198
208
|
// https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/html/canvas/text_metrics.cc;l=32;drc=7cf6ac3dd6dca800fbc0d28e80a7732d4ea90340?q=member_hanging_&ss=chromium%2Fchromium%2Fsrc
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { TextRowData } from '../components/types';
|
|
2
|
+
import type { ChartBrush, ChartData, ChartMargin, ChartTitle, ChartZoom, DeepRequired } from '../types';
|
|
2
3
|
export type PreparedZoom = DeepRequired<Omit<ChartZoom, 'enabled' | 'brush'>> & DeepRequired<{
|
|
3
4
|
brush: ChartBrush;
|
|
4
5
|
}>;
|
|
@@ -6,8 +7,10 @@ export type PreparedChart = {
|
|
|
6
7
|
margin: ChartMargin;
|
|
7
8
|
zoom: PreparedZoom | null;
|
|
8
9
|
};
|
|
9
|
-
export type PreparedTitle =
|
|
10
|
+
export type PreparedTitle = Omit<ChartTitle, 'margin'> & {
|
|
10
11
|
height: number;
|
|
12
|
+
margin: number;
|
|
13
|
+
contentRows: TextRowData[];
|
|
11
14
|
};
|
|
12
15
|
export type PreparedTooltip = ChartData['tooltip'] & {
|
|
13
16
|
enabled: boolean;
|
|
@@ -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
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { group } from 'd3-array';
|
|
1
|
+
import { group, min } from 'd3-array';
|
|
2
2
|
import isNil from 'lodash/isNil';
|
|
3
3
|
import round from 'lodash/round';
|
|
4
4
|
import { getDataCategoryValue, getLabelsSize, getTextSizeFn } from '../../../core/utils';
|
|
@@ -76,7 +76,7 @@ async function prepareDataLabels({ series, points, xMax, yAxisTop, isOutsideBoun
|
|
|
76
76
|
return { svgLabels, htmlLabels };
|
|
77
77
|
}
|
|
78
78
|
export const prepareAreaData = async (args) => {
|
|
79
|
-
var _a, _b, _c;
|
|
79
|
+
var _a, _b, _c, _d;
|
|
80
80
|
const { series, xAxis, xScale, yAxis, yScale, split, isOutsideBounds, isRangeSlider } = args;
|
|
81
81
|
const [_xMin, xRangeMax] = xScale.range();
|
|
82
82
|
const xMax = xRangeMax;
|
|
@@ -144,12 +144,17 @@ export const prepareAreaData = async (args) => {
|
|
|
144
144
|
continue;
|
|
145
145
|
}
|
|
146
146
|
const yAxisTop = ((_a = split.plots[plotIndex]) === null || _a === void 0 ? void 0 : _a.top) || 0;
|
|
147
|
-
|
|
148
|
-
|
|
147
|
+
let base = 0;
|
|
148
|
+
if (seriesYAxis.type === 'logarithmic') {
|
|
149
|
+
const domainData = seriesYScale.domain();
|
|
150
|
+
base = (_b = min(domainData)) !== null && _b !== void 0 ? _b : 0;
|
|
151
|
+
}
|
|
152
|
+
const yMin = (_c = getYValue({
|
|
153
|
+
point: { y: base },
|
|
149
154
|
points: s.data,
|
|
150
155
|
yAxis: seriesYAxis,
|
|
151
156
|
yScale: seriesYScale,
|
|
152
|
-
})) !== null &&
|
|
157
|
+
})) !== null && _c !== void 0 ? _c : 0;
|
|
153
158
|
const seriesData = s.data.reduce((m, d) => {
|
|
154
159
|
const key = String(xAxis.type === 'category'
|
|
155
160
|
? getDataCategoryValue({
|
|
@@ -295,7 +300,7 @@ export const prepareAreaData = async (args) => {
|
|
|
295
300
|
seriesStackData.push({
|
|
296
301
|
points,
|
|
297
302
|
markers,
|
|
298
|
-
|
|
303
|
+
svgLabels: [],
|
|
299
304
|
color: s.color,
|
|
300
305
|
opacity: s.opacity,
|
|
301
306
|
width: s.lineWidth,
|
|
@@ -303,13 +308,13 @@ export const prepareAreaData = async (args) => {
|
|
|
303
308
|
hovered: false,
|
|
304
309
|
active: true,
|
|
305
310
|
id: s.id,
|
|
306
|
-
|
|
311
|
+
htmlLabels: [],
|
|
307
312
|
});
|
|
308
313
|
}
|
|
309
314
|
for (let itemIndex = 0; itemIndex < seriesStackData.length; itemIndex++) {
|
|
310
315
|
const item = seriesStackData[itemIndex];
|
|
311
316
|
const currentYAxis = yAxis[item.series.yAxis];
|
|
312
|
-
const itemYAxisTop = ((
|
|
317
|
+
const itemYAxisTop = ((_d = split.plots[currentYAxis.plotIndex]) === null || _d === void 0 ? void 0 : _d.top) || 0;
|
|
313
318
|
if (item.series.dataLabels.enabled && !isRangeSlider) {
|
|
314
319
|
const labelsData = await prepareDataLabels({
|
|
315
320
|
series: item.series,
|
|
@@ -318,8 +323,8 @@ export const prepareAreaData = async (args) => {
|
|
|
318
323
|
yAxisTop: itemYAxisTop,
|
|
319
324
|
isOutsideBounds,
|
|
320
325
|
});
|
|
321
|
-
item.
|
|
322
|
-
item.
|
|
326
|
+
item.svgLabels.push(...labelsData.svgLabels);
|
|
327
|
+
item.htmlLabels.push(...labelsData.htmlLabels);
|
|
323
328
|
}
|
|
324
329
|
}
|
|
325
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
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ascending, descending, max, reverse, sort } from 'd3-array';
|
|
1
|
+
import { ascending, descending, max, min, reverse, sort } from 'd3-array';
|
|
2
2
|
import get from 'lodash/get';
|
|
3
3
|
import { getDataCategoryValue, getLabelsSize } from '../../../core/utils';
|
|
4
4
|
import { getFormattedValue } from '../../../core/utils/format';
|
|
@@ -35,7 +35,7 @@ async function getLabelData(d, xMax) {
|
|
|
35
35
|
};
|
|
36
36
|
}
|
|
37
37
|
export const prepareBarXData = async (args) => {
|
|
38
|
-
var _a, _b, _c, _d, _e;
|
|
38
|
+
var _a, _b, _c, _d, _e, _f;
|
|
39
39
|
const { series, seriesOptions, xAxis, xScale, yAxis, yScale, boundsHeight: plotHeight, split, isRangeSlider, } = args;
|
|
40
40
|
const stackGap = seriesOptions['bar-x'].stackGap;
|
|
41
41
|
const categories = (_a = xAxis === null || xAxis === void 0 ? void 0 : xAxis.categories) !== null && _a !== void 0 ? _a : [];
|
|
@@ -145,7 +145,15 @@ export const prepareBarXData = async (args) => {
|
|
|
145
145
|
const x = xCenter - currentGroupWidth / 2 + (rectWidth + rectGap) * groupItemIndex;
|
|
146
146
|
const yDataValue = ((_d = yValue.data.y) !== null && _d !== void 0 ? _d : 0);
|
|
147
147
|
const y = seriesYScale(yDataValue);
|
|
148
|
-
|
|
148
|
+
let base = 0;
|
|
149
|
+
if (seriesYAxis.type === 'logarithmic') {
|
|
150
|
+
const domainData = seriesYScale.domain();
|
|
151
|
+
const yMinValue = (_e = min(domainData)) !== null && _e !== void 0 ? _e : 0;
|
|
152
|
+
base = seriesYScale(yMinValue);
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
base = seriesYScale(0);
|
|
156
|
+
}
|
|
149
157
|
const isLastStackItem = yValueIndex === sortedData.length - 1;
|
|
150
158
|
const height = Math.abs(base - y);
|
|
151
159
|
let shapeHeight = height - (stackItems.length ? stackGap : 0);
|
|
@@ -166,7 +174,8 @@ export const prepareBarXData = async (args) => {
|
|
|
166
174
|
opacity: get(yValue.data, 'opacity', null),
|
|
167
175
|
data: yValue.data,
|
|
168
176
|
series: yValue.series,
|
|
169
|
-
|
|
177
|
+
htmlLabels: [],
|
|
178
|
+
svgLabels: [],
|
|
170
179
|
isLastStackItem,
|
|
171
180
|
};
|
|
172
181
|
stackItems.push(barData);
|
|
@@ -198,22 +207,24 @@ export const prepareBarXData = async (args) => {
|
|
|
198
207
|
barData.x >= xMax ||
|
|
199
208
|
barData.y + barData.height <= 0 ||
|
|
200
209
|
barData.y >= plotHeight;
|
|
201
|
-
const isZeroValue = ((
|
|
210
|
+
const isZeroValue = ((_f = barData.data.y) !== null && _f !== void 0 ? _f : 0) === 0;
|
|
202
211
|
if (barData.series.dataLabels.enabled &&
|
|
203
212
|
!isRangeSlider &&
|
|
204
213
|
(!isBarOutsideBounds || isZeroValue)) {
|
|
205
214
|
const label = await getLabelData(barData, xMax);
|
|
206
|
-
if (
|
|
207
|
-
barData.
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
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
|
+
}
|
|
217
228
|
}
|
|
218
229
|
}
|
|
219
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,
|
|
@@ -25,8 +25,8 @@ export type PreparedLineData = {
|
|
|
25
25
|
series: PreparedLineSeries;
|
|
26
26
|
hovered: boolean;
|
|
27
27
|
active: boolean;
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
svgLabels: LabelData[];
|
|
29
|
+
htmlLabels: HtmlItem[];
|
|
30
30
|
color: string;
|
|
31
31
|
dashStyle: DashStyle;
|
|
32
32
|
linecap: LineCap;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Dispatch } from 'd3-dispatch';
|
|
2
|
-
import type { AxisPlotBand, AxisPlotLine, PointPosition, TooltipDataChunk } from '../../types';
|
|
2
|
+
import type { AxisPlotBand, AxisPlotLine, AxisPlotShape, PointPosition, TooltipDataChunk } from '../../types';
|
|
3
3
|
import type { PreparedTooltip } from '../types';
|
|
4
4
|
import type { PreparedXAxis, PreparedYAxis } from '../useAxis/types';
|
|
5
5
|
type Args = {
|
|
@@ -10,8 +10,9 @@ type Args = {
|
|
|
10
10
|
};
|
|
11
11
|
export declare const useTooltip: ({ dispatcher, tooltip, xAxis, yAxis }: Args) => {
|
|
12
12
|
hovered: TooltipDataChunk[] | undefined;
|
|
13
|
-
hoveredPlotLines: AxisPlotLine[] | undefined;
|
|
14
13
|
hoveredPlotBands: AxisPlotBand[] | undefined;
|
|
14
|
+
hoveredPlotLines: AxisPlotLine[] | undefined;
|
|
15
|
+
hoveredPlotShapes: AxisPlotShape[] | undefined;
|
|
15
16
|
pointerPosition: PointPosition | undefined;
|
|
16
17
|
};
|
|
17
18
|
export {};
|
|
@@ -2,7 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import isEqual from 'lodash/isEqual';
|
|
3
3
|
import { getSortedHovered } from '../../components/Tooltip/DefaultTooltipContent/utils';
|
|
4
4
|
export const useTooltip = ({ dispatcher, tooltip, xAxis, yAxis }) => {
|
|
5
|
-
const [{ hovered, hoveredPlotLines,
|
|
5
|
+
const [{ hovered, hoveredPlotBands, hoveredPlotLines, hoveredPlotShapes, pointerPosition }, setTooltipState,] = React.useState({});
|
|
6
6
|
const prevHovered = React.useRef(hovered);
|
|
7
7
|
React.useEffect(() => {
|
|
8
8
|
if (tooltip === null || tooltip === void 0 ? void 0 : tooltip.enabled) {
|
|
@@ -17,8 +17,9 @@ export const useTooltip = ({ dispatcher, tooltip, xAxis, yAxis }) => {
|
|
|
17
17
|
const isHoveredChanged = !isEqual(prevHovered.current, sortedHovered);
|
|
18
18
|
const newTooltipState = {
|
|
19
19
|
hovered: isHoveredChanged ? sortedHovered : prevHovered.current,
|
|
20
|
-
hoveredPlotLines: nextHoveredPlots === null || nextHoveredPlots === void 0 ? void 0 : nextHoveredPlots.lines,
|
|
21
20
|
hoveredPlotBands: nextHoveredPlots === null || nextHoveredPlots === void 0 ? void 0 : nextHoveredPlots.bands,
|
|
21
|
+
hoveredPlotLines: nextHoveredPlots === null || nextHoveredPlots === void 0 ? void 0 : nextHoveredPlots.lines,
|
|
22
|
+
hoveredPlotShapes: nextHoveredPlots === null || nextHoveredPlots === void 0 ? void 0 : nextHoveredPlots.shapes,
|
|
22
23
|
pointerPosition: nextPointerPosition,
|
|
23
24
|
};
|
|
24
25
|
if (isHoveredChanged) {
|
|
@@ -35,8 +36,9 @@ export const useTooltip = ({ dispatcher, tooltip, xAxis, yAxis }) => {
|
|
|
35
36
|
}, [dispatcher, tooltip, xAxis, yAxis]);
|
|
36
37
|
return {
|
|
37
38
|
hovered,
|
|
38
|
-
hoveredPlotLines,
|
|
39
39
|
hoveredPlotBands,
|
|
40
|
+
hoveredPlotLines,
|
|
41
|
+
hoveredPlotShapes,
|
|
40
42
|
pointerPosition,
|
|
41
43
|
};
|
|
42
44
|
};
|
|
@@ -25,6 +25,10 @@ export interface HtmlItem {
|
|
|
25
25
|
};
|
|
26
26
|
style?: BaseTextStyle & React.CSSProperties;
|
|
27
27
|
}
|
|
28
|
+
export interface ShapeDataWithLabels {
|
|
29
|
+
svgLabels: LabelData[];
|
|
30
|
+
htmlLabels: HtmlItem[];
|
|
31
|
+
}
|
|
28
32
|
export interface ShapeDataWithHtmlItems {
|
|
29
33
|
htmlElements: HtmlItem[];
|
|
30
34
|
}
|