@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.
Files changed (113) hide show
  1. package/dist/cjs/components/AxisX/AxisX.js +27 -0
  2. package/dist/cjs/components/AxisX/prepare-axis-data.js +41 -0
  3. package/dist/cjs/components/AxisX/types.d.ts +18 -1
  4. package/dist/cjs/components/AxisY/AxisY.js +27 -0
  5. package/dist/cjs/components/AxisY/prepare-axis-data.js +41 -0
  6. package/dist/cjs/components/AxisY/types.d.ts +18 -1
  7. package/dist/cjs/components/ChartInner/index.js +19 -3
  8. package/dist/cjs/components/ChartInner/useChartInnerHandlers.d.ts +3 -3
  9. package/dist/cjs/components/ChartInner/useChartInnerHandlers.js +16 -17
  10. package/dist/cjs/components/ChartInner/useChartInnerProps.d.ts +2 -4
  11. package/dist/cjs/components/ChartInner/useChartInnerProps.js +8 -4
  12. package/dist/cjs/components/ChartInner/useDefaultState.js +4 -3
  13. package/dist/cjs/components/ChartInner/utils/chart.js +1 -1
  14. package/dist/cjs/components/ChartInner/utils/normalized-original-data.d.ts +1 -0
  15. package/dist/cjs/components/ChartInner/utils/title.d.ts +4 -2
  16. package/dist/cjs/components/ChartInner/utils/title.js +77 -14
  17. package/dist/cjs/components/Title/index.d.ts +1 -3
  18. package/dist/cjs/components/Title/index.js +3 -5
  19. package/dist/cjs/components/Tooltip/ChartTooltipContent.d.ts +2 -1
  20. package/dist/cjs/components/Tooltip/ChartTooltipContent.js +3 -2
  21. package/dist/cjs/components/Tooltip/index.js +2 -2
  22. package/dist/cjs/core/axes/types.d.ts +26 -9
  23. package/dist/cjs/core/axes/x-axis.js +14 -1
  24. package/dist/cjs/core/axes/y-axis.js +20 -7
  25. package/dist/cjs/core/constants/defaults/axis.d.ts +1 -0
  26. package/dist/cjs/core/constants/defaults/axis.js +1 -0
  27. package/dist/cjs/core/constants/index.d.ts +0 -1
  28. package/dist/cjs/core/constants/index.js +0 -1
  29. package/dist/cjs/core/scales/y-scale.js +37 -13
  30. package/dist/cjs/core/types/chart/axis.d.ts +43 -1
  31. package/dist/cjs/core/types/chart/title.d.ts +10 -0
  32. package/dist/cjs/core/types/chart/tooltip.d.ts +3 -1
  33. package/dist/cjs/core/utils/common.js +1 -1
  34. package/dist/cjs/core/utils/get-hovered-plots.d.ts +3 -2
  35. package/dist/cjs/core/utils/get-hovered-plots.js +28 -4
  36. package/dist/cjs/core/utils/labels.d.ts +1 -1
  37. package/dist/cjs/core/utils/labels.js +3 -2
  38. package/dist/cjs/core/utils/text.js +12 -2
  39. package/dist/cjs/hooks/types.d.ts +5 -2
  40. package/dist/cjs/hooks/useSeries/index.js +8 -2
  41. package/dist/cjs/hooks/useShapes/area/index.js +2 -2
  42. package/dist/cjs/hooks/useShapes/area/prepare-data.js +15 -10
  43. package/dist/cjs/hooks/useShapes/area/types.d.ts +2 -2
  44. package/dist/cjs/hooks/useShapes/bar-x/index.js +2 -2
  45. package/dist/cjs/hooks/useShapes/bar-x/prepare-data.js +27 -16
  46. package/dist/cjs/hooks/useShapes/bar-x/types.d.ts +2 -2
  47. package/dist/cjs/hooks/useShapes/index.js +18 -5
  48. package/dist/cjs/hooks/useShapes/line/index.js +7 -16
  49. package/dist/cjs/hooks/useShapes/line/prepare-data.d.ts +2 -0
  50. package/dist/cjs/hooks/useShapes/line/prepare-data.js +11 -7
  51. package/dist/cjs/hooks/useShapes/line/types.d.ts +2 -2
  52. package/dist/cjs/hooks/useTooltip/index.d.ts +3 -2
  53. package/dist/cjs/hooks/useTooltip/index.js +5 -3
  54. package/dist/cjs/types/chart-ui.d.ts +4 -0
  55. package/dist/esm/components/AxisX/AxisX.js +27 -0
  56. package/dist/esm/components/AxisX/prepare-axis-data.js +41 -0
  57. package/dist/esm/components/AxisX/types.d.ts +18 -1
  58. package/dist/esm/components/AxisY/AxisY.js +27 -0
  59. package/dist/esm/components/AxisY/prepare-axis-data.js +41 -0
  60. package/dist/esm/components/AxisY/types.d.ts +18 -1
  61. package/dist/esm/components/ChartInner/index.js +19 -3
  62. package/dist/esm/components/ChartInner/useChartInnerHandlers.d.ts +3 -3
  63. package/dist/esm/components/ChartInner/useChartInnerHandlers.js +16 -17
  64. package/dist/esm/components/ChartInner/useChartInnerProps.d.ts +2 -4
  65. package/dist/esm/components/ChartInner/useChartInnerProps.js +8 -4
  66. package/dist/esm/components/ChartInner/useDefaultState.js +4 -3
  67. package/dist/esm/components/ChartInner/utils/chart.js +1 -1
  68. package/dist/esm/components/ChartInner/utils/normalized-original-data.d.ts +1 -0
  69. package/dist/esm/components/ChartInner/utils/title.d.ts +4 -2
  70. package/dist/esm/components/ChartInner/utils/title.js +77 -14
  71. package/dist/esm/components/Title/index.d.ts +1 -3
  72. package/dist/esm/components/Title/index.js +3 -5
  73. package/dist/esm/components/Tooltip/ChartTooltipContent.d.ts +2 -1
  74. package/dist/esm/components/Tooltip/ChartTooltipContent.js +3 -2
  75. package/dist/esm/components/Tooltip/index.js +2 -2
  76. package/dist/esm/core/axes/types.d.ts +26 -9
  77. package/dist/esm/core/axes/x-axis.js +14 -1
  78. package/dist/esm/core/axes/y-axis.js +20 -7
  79. package/dist/esm/core/constants/defaults/axis.d.ts +1 -0
  80. package/dist/esm/core/constants/defaults/axis.js +1 -0
  81. package/dist/esm/core/constants/index.d.ts +0 -1
  82. package/dist/esm/core/constants/index.js +0 -1
  83. package/dist/esm/core/scales/y-scale.js +37 -13
  84. package/dist/esm/core/types/chart/axis.d.ts +43 -1
  85. package/dist/esm/core/types/chart/title.d.ts +10 -0
  86. package/dist/esm/core/types/chart/tooltip.d.ts +3 -1
  87. package/dist/esm/core/utils/common.js +1 -1
  88. package/dist/esm/core/utils/get-hovered-plots.d.ts +3 -2
  89. package/dist/esm/core/utils/get-hovered-plots.js +28 -4
  90. package/dist/esm/core/utils/labels.d.ts +1 -1
  91. package/dist/esm/core/utils/labels.js +3 -2
  92. package/dist/esm/core/utils/text.js +12 -2
  93. package/dist/esm/hooks/types.d.ts +5 -2
  94. package/dist/esm/hooks/useSeries/index.js +8 -2
  95. package/dist/esm/hooks/useShapes/area/index.js +2 -2
  96. package/dist/esm/hooks/useShapes/area/prepare-data.js +15 -10
  97. package/dist/esm/hooks/useShapes/area/types.d.ts +2 -2
  98. package/dist/esm/hooks/useShapes/bar-x/index.js +2 -2
  99. package/dist/esm/hooks/useShapes/bar-x/prepare-data.js +27 -16
  100. package/dist/esm/hooks/useShapes/bar-x/types.d.ts +2 -2
  101. package/dist/esm/hooks/useShapes/index.js +18 -5
  102. package/dist/esm/hooks/useShapes/line/index.js +7 -16
  103. package/dist/esm/hooks/useShapes/line/prepare-data.d.ts +2 -0
  104. package/dist/esm/hooks/useShapes/line/prepare-data.js +11 -7
  105. package/dist/esm/hooks/useShapes/line/types.d.ts +2 -2
  106. package/dist/esm/hooks/useTooltip/index.d.ts +3 -2
  107. package/dist/esm/hooks/useTooltip/index.js +5 -3
  108. package/dist/esm/types/chart-ui.d.ts +4 -0
  109. package/package.json +1 -1
  110. package/dist/cjs/core/constants/misc.d.ts +0 -1
  111. package/dist/cjs/core/constants/misc.js +0 -7
  112. package/dist/esm/core/constants/misc.d.ts +0 -1
  113. 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) <= PLOT_LINE_HIT_THRESHOLD_PX + line.width / 2) {
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, plotBands };
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 (!result.some((l) => isLabelsOverlapping(label, l))) {
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
- context.font = `${(_a = style === null || style === void 0 ? void 0 : style.fontWeight) !== null && _a !== void 0 ? _a : defaultFontWeight} ${(_b = style === null || style === void 0 ? void 0 : style.fontSize) !== null && _b !== void 0 ? _b : defaultFontSize} ${defaultFontFamily}`;
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 { ChartBrush, ChartData, ChartMargin, ChartZoom, DeepRequired } from '../types';
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 = ChartData['title'] & {
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) => item.type);
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 [seriesType, seriesList] = list[i];
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.labels);
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.htmlElements).flat();
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
- const yMin = (_b = getYValue({
148
- point: { y: 0 },
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 && _b !== void 0 ? _b : 0;
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
- labels: [],
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
- htmlElements: [],
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 = ((_c = split.plots[currentYAxis.plotIndex]) === null || _c === void 0 ? void 0 : _c.top) || 0;
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.labels.push(...labelsData.svgLabels);
322
- item.htmlElements.push(...labelsData.htmlLabels);
326
+ item.svgLabels.push(...labelsData.svgLabels);
327
+ item.htmlLabels.push(...labelsData.htmlLabels);
323
328
  }
324
329
  }
325
330
  result.push(...seriesStackData);
@@ -27,6 +27,6 @@ export type PreparedAreaData = {
27
27
  series: PreparedAreaSeries;
28
28
  hovered: boolean;
29
29
  active: boolean;
30
- labels: LabelData[];
31
- htmlElements: HtmlItem[];
30
+ svgLabels: LabelData[];
31
+ htmlLabels: HtmlItem[];
32
32
  };
@@ -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.label).filter(Boolean);
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.htmlElements).flat();
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
- const base = seriesYScale(0);
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
- htmlElements: [],
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 = ((_e = barData.data.y) !== null && _e !== void 0 ? _e : 0) === 0;
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 (barData.series.dataLabels.html && label) {
207
- barData.htmlElements.push({
208
- x: label.x,
209
- y: label.y,
210
- content: label.text,
211
- size: label.size,
212
- style: label.style,
213
- });
214
- }
215
- else {
216
- barData.label = label;
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
- label?: LabelData;
11
- htmlElements: HtmlItem[];
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) => item.type);
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
- await Promise.all(Array.from(groupedSeries).map(async (item, index) => {
40
- const [seriesType, chartSeries] = item;
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: SERIES_TYPE.Line, dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData, htmlLayout: htmlLayout, clipPathId: resultClipPathId }));
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 { filterOverlappingLabels, getLineDashArray } from '../../../core/utils';
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
- let dataLabels = preparedData.reduce((acc, d) => {
50
- return acc.concat(d.labels);
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
- }, [allowOverlapDataLabels, dispatcher, preparedData, seriesOptions]);
172
+ }, [dispatcher, preparedData, seriesOptions]);
179
173
  const htmlLayerData = React.useMemo(() => {
180
- const items = preparedData.map((d) => d === null || d === void 0 ? void 0 : d.htmlElements).flat();
181
- if (allowOverlapDataLabels) {
182
- return { htmlElements: items };
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
- const htmlElements = [];
48
- const labels = [];
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
- labels.push(labelData);
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
- labels,
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
- labels: LabelData[];
29
- htmlElements: HtmlItem[];
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, hoveredPlotBands, pointerPosition }, setTooltipState] = React.useState({});
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
  }