@gravity-ui/charts 1.42.4 → 1.43.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) 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 +16 -0
  8. package/dist/cjs/components/ChartInner/useChartInnerHandlers.js +6 -5
  9. package/dist/cjs/components/ChartInner/useChartInnerProps.d.ts +2 -4
  10. package/dist/cjs/components/ChartInner/useChartInnerProps.js +1 -1
  11. package/dist/cjs/components/ChartInner/useDefaultState.js +4 -3
  12. package/dist/cjs/components/ChartInner/utils/chart.js +1 -1
  13. package/dist/cjs/components/ChartInner/utils/normalized-original-data.d.ts +1 -0
  14. package/dist/cjs/components/ChartInner/utils/title.d.ts +2 -1
  15. package/dist/cjs/components/ChartInner/utils/title.js +69 -11
  16. package/dist/cjs/components/Title/index.js +3 -5
  17. package/dist/cjs/components/Tooltip/ChartTooltipContent.d.ts +2 -1
  18. package/dist/cjs/components/Tooltip/ChartTooltipContent.js +3 -2
  19. package/dist/cjs/components/Tooltip/index.js +2 -2
  20. package/dist/cjs/core/axes/types.d.ts +26 -9
  21. package/dist/cjs/core/axes/x-axis.js +14 -1
  22. package/dist/cjs/core/axes/y-axis.js +20 -7
  23. package/dist/cjs/core/constants/defaults/axis.d.ts +1 -0
  24. package/dist/cjs/core/constants/defaults/axis.js +1 -0
  25. package/dist/cjs/core/scales/y-scale.js +37 -13
  26. package/dist/cjs/core/types/chart/axis.d.ts +43 -1
  27. package/dist/cjs/core/types/chart/title.d.ts +10 -0
  28. package/dist/cjs/core/types/chart/tooltip.d.ts +3 -1
  29. package/dist/cjs/core/utils/common.js +1 -1
  30. package/dist/cjs/core/utils/get-hovered-plots.d.ts +3 -2
  31. package/dist/cjs/core/utils/get-hovered-plots.js +28 -4
  32. package/dist/cjs/core/utils/text.js +12 -2
  33. package/dist/cjs/hooks/types.d.ts +5 -2
  34. package/dist/cjs/hooks/useShapes/area/prepare-data.js +11 -6
  35. package/dist/cjs/hooks/useShapes/bar-x/prepare-data.js +12 -4
  36. package/dist/cjs/hooks/useTooltip/index.d.ts +3 -2
  37. package/dist/cjs/hooks/useTooltip/index.js +5 -3
  38. package/dist/esm/components/AxisX/AxisX.js +27 -0
  39. package/dist/esm/components/AxisX/prepare-axis-data.js +41 -0
  40. package/dist/esm/components/AxisX/types.d.ts +18 -1
  41. package/dist/esm/components/AxisY/AxisY.js +27 -0
  42. package/dist/esm/components/AxisY/prepare-axis-data.js +41 -0
  43. package/dist/esm/components/AxisY/types.d.ts +18 -1
  44. package/dist/esm/components/ChartInner/index.js +16 -0
  45. package/dist/esm/components/ChartInner/useChartInnerHandlers.js +6 -5
  46. package/dist/esm/components/ChartInner/useChartInnerProps.d.ts +2 -4
  47. package/dist/esm/components/ChartInner/useChartInnerProps.js +1 -1
  48. package/dist/esm/components/ChartInner/useDefaultState.js +4 -3
  49. package/dist/esm/components/ChartInner/utils/chart.js +1 -1
  50. package/dist/esm/components/ChartInner/utils/normalized-original-data.d.ts +1 -0
  51. package/dist/esm/components/ChartInner/utils/title.d.ts +2 -1
  52. package/dist/esm/components/ChartInner/utils/title.js +69 -11
  53. package/dist/esm/components/Title/index.js +3 -5
  54. package/dist/esm/components/Tooltip/ChartTooltipContent.d.ts +2 -1
  55. package/dist/esm/components/Tooltip/ChartTooltipContent.js +3 -2
  56. package/dist/esm/components/Tooltip/index.js +2 -2
  57. package/dist/esm/core/axes/types.d.ts +26 -9
  58. package/dist/esm/core/axes/x-axis.js +14 -1
  59. package/dist/esm/core/axes/y-axis.js +20 -7
  60. package/dist/esm/core/constants/defaults/axis.d.ts +1 -0
  61. package/dist/esm/core/constants/defaults/axis.js +1 -0
  62. package/dist/esm/core/scales/y-scale.js +37 -13
  63. package/dist/esm/core/types/chart/axis.d.ts +43 -1
  64. package/dist/esm/core/types/chart/title.d.ts +10 -0
  65. package/dist/esm/core/types/chart/tooltip.d.ts +3 -1
  66. package/dist/esm/core/utils/common.js +1 -1
  67. package/dist/esm/core/utils/get-hovered-plots.d.ts +3 -2
  68. package/dist/esm/core/utils/get-hovered-plots.js +28 -4
  69. package/dist/esm/core/utils/text.js +12 -2
  70. package/dist/esm/hooks/types.d.ts +5 -2
  71. package/dist/esm/hooks/useShapes/area/prepare-data.js +11 -6
  72. package/dist/esm/hooks/useShapes/bar-x/prepare-data.js +12 -4
  73. package/dist/esm/hooks/useTooltip/index.d.ts +3 -2
  74. package/dist/esm/hooks/useTooltip/index.js +5 -3
  75. package/package.json +1 -1
@@ -23,6 +23,7 @@ export const AxisX = (props) => {
23
23
  const plotDataAttr = 'data-plot-x';
24
24
  const plotBandDataAttr = `data-plot-x-band-${preparedAxisData.id}`;
25
25
  const plotLineDataAttr = `data-plot-x-line-${preparedAxisData.id}`;
26
+ const plotShapeDataAttr = `data-plot-x-shape-${preparedAxisData.id}`;
26
27
  if (plotBeforeRef === null || plotBeforeRef === void 0 ? void 0 : plotBeforeRef.current) {
27
28
  plotBeforeContainer = select(plotBeforeRef.current);
28
29
  }
@@ -186,14 +187,40 @@ export const AxisX = (props) => {
186
187
  setPlotLines(plotBeforeContainer, preparedAxisData.plotLines.filter((item) => item.layerPlacement === 'before'));
187
188
  setPlotLines(plotAfterContainer, preparedAxisData.plotLines.filter((item) => item.layerPlacement === 'after'));
188
189
  }
190
+ if (preparedAxisData.plotShapes.length > 0) {
191
+ const setPlotShapes = (plotContainer, plotShapes) => {
192
+ if (!plotContainer || !plotShapes.length) {
193
+ return;
194
+ }
195
+ plotContainer
196
+ .selectAll(`[${plotShapeDataAttr}]`)
197
+ .remove()
198
+ .data(plotShapes)
199
+ .join('g')
200
+ .attr(plotDataAttr, 1)
201
+ .attr(plotShapeDataAttr, 1)
202
+ .attr('transform', (d) => `translate(${d.x}, ${d.y})`)
203
+ .attr('opacity', (d) => d.opacity)
204
+ .html((d) => d.renderer({
205
+ x: d.x,
206
+ y: 0,
207
+ plotHeight: d.plotHeight,
208
+ plotWidth: d.plotWidth,
209
+ }));
210
+ };
211
+ setPlotShapes(plotBeforeContainer, preparedAxisData.plotShapes.filter((item) => item.layerPlacement === 'before'));
212
+ setPlotShapes(plotAfterContainer, preparedAxisData.plotShapes.filter((item) => item.layerPlacement === 'after'));
213
+ }
189
214
  return () => {
190
215
  if (plotBeforeContainer) {
191
216
  plotBeforeContainer.selectAll(`[${plotBandDataAttr}]`).remove();
192
217
  plotBeforeContainer.selectAll(`[${plotLineDataAttr}]`).remove();
218
+ plotBeforeContainer.selectAll(`[${plotShapeDataAttr}]`).remove();
193
219
  }
194
220
  if (plotAfterContainer) {
195
221
  plotAfterContainer.selectAll(`[${plotBandDataAttr}]`).remove();
196
222
  plotAfterContainer.selectAll(`[${plotLineDataAttr}]`).remove();
223
+ plotAfterContainer.selectAll(`[${plotShapeDataAttr}]`).remove();
197
224
  }
198
225
  };
199
226
  }, [lineGenerator, plotAfterRef, plotBeforeRef, preparedAxisData]);
@@ -1,4 +1,5 @@
1
1
  import { getUniqId } from '@gravity-ui/uikit';
2
+ import { select } from 'd3-selection';
2
3
  import { calculateCos, calculateSin, formatAxisTickLabel, getBandsPosition, getLabelsSize, getMinSpaceBetween, getTextSizeFn, getTextWithElipsis, } from '../../core/utils';
3
4
  import { getXAxisTickValues } from '../../core/utils/axis/x-axis';
4
5
  import { getMultilineTitleContentRows } from '../utils/axis-title';
@@ -328,6 +329,45 @@ export async function prepareXAxisData({ axis, boundsOffsetLeft, boundsOffsetRig
328
329
  dashStyle: plotLine.dashStyle,
329
330
  });
330
331
  }
332
+ const plotShapes = [];
333
+ const measureContainer = axis.plotShapes.length
334
+ ? select(document.body)
335
+ .append('svg')
336
+ .style('visibility', 'hidden')
337
+ .style('position', 'absolute')
338
+ .style('top', '-200vw')
339
+ : null;
340
+ for (let i = 0; i < axis.plotShapes.length; i++) {
341
+ if (!measureContainer) {
342
+ break;
343
+ }
344
+ const plotShape = axis.plotShapes[i];
345
+ const axisScale = scale;
346
+ const shapeX = Number(axisScale(plotShape.value));
347
+ if (shapeX < 0 || shapeX > boundsWidth) {
348
+ continue;
349
+ }
350
+ const markup = plotShape.renderer({
351
+ x: shapeX,
352
+ y: 0,
353
+ plotHeight: axisHeight,
354
+ plotWidth: axisWidth,
355
+ });
356
+ const wrapper = measureContainer.append('g').html(markup);
357
+ const bbox = wrapper.node().getBBox();
358
+ wrapper.remove();
359
+ plotShapes.push({
360
+ hitbox: { x: bbox.x, y: bbox.y, width: bbox.width, height: bbox.height },
361
+ layerPlacement: plotShape.layerPlacement,
362
+ opacity: plotShape.opacity,
363
+ plotHeight: axisHeight,
364
+ plotWidth: axisWidth,
365
+ renderer: plotShape.renderer,
366
+ x: shapeX,
367
+ y: axisTop,
368
+ });
369
+ }
370
+ measureContainer === null || measureContainer === void 0 ? void 0 : measureContainer.remove();
331
371
  xAxisItems.push({
332
372
  id: getUniqId(),
333
373
  gridEnabled: axis.grid.enabled,
@@ -336,6 +376,7 @@ export async function prepareXAxisData({ axis, boundsOffsetLeft, boundsOffsetRig
336
376
  domain,
337
377
  plotBands,
338
378
  plotLines,
379
+ plotShapes,
339
380
  });
340
381
  }
341
382
  return xAxisItems;
@@ -1,4 +1,5 @@
1
1
  import type { DashStyle } from 'src/core/constants';
2
+ import type { AxisPlotShape } from '../../core/types/chart/axis';
2
3
  import type { BaseTextStyle, HtmlItem, PlotLayerPlacement, PointPosition } from '../../types';
3
4
  import type { TextRowData } from '../types';
4
5
  export type AxisSvgLabelData = {
@@ -67,6 +68,21 @@ export type AxisPlotBandData = {
67
68
  opacity: number;
68
69
  label: AxisPlotLineLabel | null;
69
70
  };
71
+ export type AxisPlotShapeData = {
72
+ hitbox: {
73
+ x: number;
74
+ y: number;
75
+ width: number;
76
+ height: number;
77
+ };
78
+ layerPlacement: PlotLayerPlacement;
79
+ opacity: number;
80
+ plotHeight: number;
81
+ plotWidth: number;
82
+ renderer: AxisPlotShape['renderer'];
83
+ x: number;
84
+ y: number;
85
+ };
70
86
  export type AxisDomainData = {
71
87
  start: [number, number];
72
88
  end: [number, number];
@@ -78,6 +94,7 @@ export type AxisXData = {
78
94
  title: AxisTitleData | null;
79
95
  domain: AxisDomainData | null;
80
96
  ticks: AxisTickData[];
81
- plotLines: AxisPlotLineData[];
82
97
  plotBands: AxisPlotBandData[];
98
+ plotLines: AxisPlotLineData[];
99
+ plotShapes: AxisPlotShapeData[];
83
100
  };
@@ -28,6 +28,7 @@ export const AxisY = (props) => {
28
28
  const plotDataAttr = 'data-plot-y';
29
29
  const plotBandDataAttr = `data-plot-y-band-${preparedAxisData.id}`;
30
30
  const plotLineDataAttr = `data-plot-y-line-${preparedAxisData.id}`;
31
+ const plotShapeDataAttr = `data-plot-y-shape-${preparedAxisData.id}`;
31
32
  if (plotBeforeRef === null || plotBeforeRef === void 0 ? void 0 : plotBeforeRef.current) {
32
33
  plotBeforeContainer = select(plotBeforeRef.current);
33
34
  }
@@ -191,14 +192,40 @@ export const AxisY = (props) => {
191
192
  setPlotLines(plotBeforeContainer, preparedAxisData.plotLines.filter((item) => item.layerPlacement === 'before'));
192
193
  setPlotLines(plotAfterContainer, preparedAxisData.plotLines.filter((item) => item.layerPlacement === 'after'));
193
194
  }
195
+ if (preparedAxisData.plotShapes.length > 0) {
196
+ const setPlotShapes = (plotContainer, plotShapes) => {
197
+ if (!plotContainer || !plotShapes.length) {
198
+ return;
199
+ }
200
+ plotContainer
201
+ .selectAll(`[${plotShapeDataAttr}]`)
202
+ .remove()
203
+ .data(plotShapes)
204
+ .join('g')
205
+ .attr(plotDataAttr, 1)
206
+ .attr(plotShapeDataAttr, 1)
207
+ .attr('transform', (d) => `translate(${d.x}, ${d.y})`)
208
+ .attr('opacity', (d) => d.opacity)
209
+ .html((d) => d.renderer({
210
+ x: 0,
211
+ y: d.y,
212
+ plotHeight: d.plotHeight,
213
+ plotWidth: d.plotWidth,
214
+ }));
215
+ };
216
+ setPlotShapes(plotBeforeContainer, preparedAxisData.plotShapes.filter((item) => item.layerPlacement === 'before'));
217
+ setPlotShapes(plotAfterContainer, preparedAxisData.plotShapes.filter((item) => item.layerPlacement === 'after'));
218
+ }
194
219
  return () => {
195
220
  if (plotBeforeContainer) {
196
221
  plotBeforeContainer.selectAll(`[${plotBandDataAttr}]`).remove();
197
222
  plotBeforeContainer.selectAll(`[${plotLineDataAttr}]`).remove();
223
+ plotBeforeContainer.selectAll(`[${plotShapeDataAttr}]`).remove();
198
224
  }
199
225
  if (plotAfterContainer) {
200
226
  plotAfterContainer.selectAll(`[${plotBandDataAttr}]`).remove();
201
227
  plotAfterContainer.selectAll(`[${plotLineDataAttr}]`).remove();
228
+ plotAfterContainer.selectAll(`[${plotShapeDataAttr}]`).remove();
202
229
  }
203
230
  };
204
231
  }, [lineGenerator, plotAfterRef, plotBeforeRef, preparedAxisData]);
@@ -1,4 +1,5 @@
1
1
  import { getUniqId } from '@gravity-ui/uikit';
2
+ import { select } from 'd3-selection';
2
3
  import { calculateCos, calculateSin, formatAxisTickLabel, getBandsPosition, getLabelsSize, getMinSpaceBetween, getTextSizeFn, getTextWithElipsis, wrapText, } from '../../core/utils';
3
4
  import { prepareHtmlYAxisTitle, prepareSvgYAxisTitle } from './prepare-axis-title';
4
5
  import { getTickValues } from './utils';
@@ -290,6 +291,45 @@ export async function prepareYAxisData({ axis, split, scale, top: topOffset, wid
290
291
  dashStyle: plotLine.dashStyle,
291
292
  });
292
293
  }
294
+ const plotShapes = [];
295
+ const measureContainer = axis.plotShapes.length
296
+ ? select(document.body)
297
+ .append('svg')
298
+ .style('visibility', 'hidden')
299
+ .style('position', 'absolute')
300
+ .style('top', '-200vw')
301
+ : null;
302
+ for (let i = 0; i < axis.plotShapes.length; i++) {
303
+ if (!measureContainer) {
304
+ break;
305
+ }
306
+ const plotShape = axis.plotShapes[i];
307
+ const axisScale = scale;
308
+ const shapeY = Number(axisScale(plotShape.value));
309
+ if (shapeY < 0 || shapeY > axisHeight) {
310
+ continue;
311
+ }
312
+ const markup = plotShape.renderer({
313
+ x: 0,
314
+ y: shapeY,
315
+ plotHeight: axisHeight,
316
+ plotWidth: width,
317
+ });
318
+ const wrapper = measureContainer.append('g').html(markup);
319
+ const bbox = wrapper.node().getBBox();
320
+ wrapper.remove();
321
+ plotShapes.push({
322
+ hitbox: { x: bbox.x, y: bbox.y, width: bbox.width, height: bbox.height },
323
+ layerPlacement: plotShape.layerPlacement,
324
+ opacity: plotShape.opacity,
325
+ plotHeight: axisHeight,
326
+ plotWidth: width,
327
+ renderer: plotShape.renderer,
328
+ x: 0,
329
+ y: axisPlotTopPosition + shapeY,
330
+ });
331
+ }
332
+ measureContainer === null || measureContainer === void 0 ? void 0 : measureContainer.remove();
293
333
  return {
294
334
  id: getUniqId(),
295
335
  gridEnabled: axis.grid.enabled,
@@ -298,5 +338,6 @@ export async function prepareYAxisData({ axis, split, scale, top: topOffset, wid
298
338
  domain,
299
339
  plotBands,
300
340
  plotLines,
341
+ plotShapes,
301
342
  };
302
343
  }
@@ -1,4 +1,5 @@
1
1
  import type { DashStyle } from 'src/core/constants';
2
+ import type { AxisPlotShape } from '../../core/types/chart/axis';
2
3
  import type { BaseTextStyle, HtmlItem, PlotLayerPlacement, PointPosition } from '../../types';
3
4
  import type { TextRowData } from '../types';
4
5
  export type AxisSvgLabelData = {
@@ -78,6 +79,21 @@ export type AxisPlotBandData = {
78
79
  opacity: number;
79
80
  label: AxisPlotLineLabel | null;
80
81
  };
82
+ export type AxisPlotShapeData = {
83
+ hitbox: {
84
+ x: number;
85
+ y: number;
86
+ width: number;
87
+ height: number;
88
+ };
89
+ layerPlacement: PlotLayerPlacement;
90
+ opacity: number;
91
+ plotHeight: number;
92
+ plotWidth: number;
93
+ renderer: AxisPlotShape['renderer'];
94
+ x: number;
95
+ y: number;
96
+ };
81
97
  export type AxisDomainData = {
82
98
  start: [number, number];
83
99
  end: [number, number];
@@ -89,6 +105,7 @@ export type AxisYData = {
89
105
  title: HtmlAxisTitleData | SvgAxisTitleData | null;
90
106
  domain: AxisDomainData | null;
91
107
  ticks: AxisTickData[];
92
- plotLines: AxisPlotLineData[];
93
108
  plotBands: AxisPlotBandData[];
109
+ plotLines: AxisPlotLineData[];
110
+ plotShapes: AxisPlotShapeData[];
94
111
  };
@@ -154,6 +154,13 @@ export const ChartInner = (props) => {
154
154
  split: preparedSplit,
155
155
  series: preparedSeries.filter((s) => s.visible),
156
156
  });
157
+ axisData.plotShapes.forEach((shapeData, j) => {
158
+ if (axis.plotShapes[j]) {
159
+ axis.plotShapes[j].hitbox = shapeData.hitbox;
160
+ axis.plotShapes[j].x = shapeData.x;
161
+ axis.plotShapes[j].y = shapeData.y;
162
+ }
163
+ });
157
164
  items.push(axisData);
158
165
  }
159
166
  }
@@ -180,6 +187,15 @@ export const ChartInner = (props) => {
180
187
  split: preparedSplit,
181
188
  yAxis,
182
189
  });
190
+ axisData.forEach((data) => {
191
+ data.plotShapes.forEach((shapeData, i) => {
192
+ if (axis.plotShapes[i]) {
193
+ axis.plotShapes[i].hitbox = shapeData.hitbox;
194
+ axis.plotShapes[i].x = shapeData.x;
195
+ axis.plotShapes[i].y = shapeData.y;
196
+ }
197
+ });
198
+ });
183
199
  items.push(...axisData);
184
200
  }
185
201
  return items;
@@ -29,7 +29,7 @@ export function useChartInnerHandlers(props) {
29
29
  boundsHeight,
30
30
  boundsWidth,
31
31
  });
32
- const { plotLines, plotBands } = getHoveredPlots({
32
+ const { plotBands, plotLines, plotShapes } = getHoveredPlots({
33
33
  pointerX: x,
34
34
  pointerY: y,
35
35
  xAxis,
@@ -37,7 +37,7 @@ export function useChartInnerHandlers(props) {
37
37
  xScale,
38
38
  yScale,
39
39
  });
40
- const hoveredPlotsArg = { lines: plotLines, bands: plotBands };
40
+ const hoveredPlotsArg = { bands: plotBands, lines: plotLines, shapes: plotShapes };
41
41
  dispatcher.call(EventType.HOVER_SHAPE, event.target, closest, [pointerX, pointerY], hoveredPlotsArg);
42
42
  dispatcher.call(EventType.POINTERMOVE_CHART, {}, {
43
43
  hovered: closest,
@@ -93,7 +93,7 @@ export function useChartInnerHandlers(props) {
93
93
  dispatcher.call(EventType.CLICK_CHART, undefined, { point: selected.data, series: selected.series }, event);
94
94
  const nextTooltipFixed = !tooltipPinned;
95
95
  if (!nextTooltipFixed) {
96
- const { plotLines, plotBands } = getHoveredPlots({
96
+ const { plotBands, plotLines, plotShapes } = getHoveredPlots({
97
97
  pointerX: x,
98
98
  pointerY: y,
99
99
  xAxis,
@@ -101,14 +101,15 @@ export function useChartInnerHandlers(props) {
101
101
  xScale,
102
102
  yScale,
103
103
  });
104
- const hoveredPlotsArg = { lines: plotLines, bands: plotBands };
104
+ const hoveredPlotsArg = { bands: plotBands, lines: plotLines, shapes: plotShapes };
105
105
  dispatcher.call(EventType.HOVER_SHAPE, event.target, items, [pointerX, pointerY], hoveredPlotsArg);
106
106
  dispatcher.call(EventType.POINTERMOVE_CHART, {}, {
107
107
  hovered: items,
108
108
  xAxis,
109
109
  yAxis: yAxis[0],
110
- hoveredPlotLines: plotLines,
111
110
  hoveredPlotBands: plotBands,
111
+ hoveredPlotLines: plotLines,
112
+ hoveredPlotShapes: plotShapes,
112
113
  }, event);
113
114
  }
114
115
  togglePinTooltip === null || togglePinTooltip === void 0 ? void 0 : togglePinTooltip(nextTooltipFixed, event);
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
2
  import type { Dispatch } from 'd3-dispatch';
3
3
  import type { ChartScale, LegendItem, OnLegendItemClick, PreparedLegend, PreparedSeries, PreparedSplit, PreparedXAxis, PreparedYAxis, RangeSliderState, ShapeData, ZoomState } from '../../hooks';
4
- import type { PreparedChart } from '../../hooks/types';
4
+ import type { PreparedChart, PreparedTitle } from '../../hooks/types';
5
5
  import type { LegendConfig } from '../../types';
6
6
  import type { ChartInnerProps } from './types';
7
7
  type Props = ChartInnerProps & {
@@ -24,9 +24,7 @@ export declare function useChartInnerProps(props: Props): {
24
24
  shapesData: ShapeData[];
25
25
  shapesReady: boolean;
26
26
  handleLegendItemClick: OnLegendItemClick;
27
- preparedTitle: (import("../..").ChartTitle & {
28
- height: number;
29
- }) | undefined;
27
+ preparedTitle: PreparedTitle | undefined;
30
28
  preparedChart: PreparedChart | undefined;
31
29
  allPreparedSeries?: PreparedSeries[] | undefined;
32
30
  legendConfig?: LegendConfig | undefined;
@@ -50,7 +50,7 @@ export function useChartInnerProps(props) {
50
50
  (async function () {
51
51
  var _a, _b, _c;
52
52
  const chartDataChanged = !(previousChartData.current && isEqual(previousChartData.current, data));
53
- const preparedTitle = await getPreparedTitle({ title: data.title });
53
+ const preparedTitle = await getPreparedTitle({ title: data.title, chartWidth: width });
54
54
  const preparedChart = getPreparedChart({
55
55
  chart: data.chart,
56
56
  seriesData: data.series.data,
@@ -29,7 +29,7 @@ export function useDefaultState(props) {
29
29
  boundsHeight,
30
30
  boundsWidth,
31
31
  });
32
- const { plotLines, plotBands } = getHoveredPlots({
32
+ const { plotBands, plotLines, plotShapes } = getHoveredPlots({
33
33
  pointerX: x,
34
34
  pointerY: y,
35
35
  xAxis,
@@ -37,7 +37,7 @@ export function useDefaultState(props) {
37
37
  xScale,
38
38
  yScale,
39
39
  });
40
- const hoveredPlotsArg = { lines: plotLines, bands: plotBands };
40
+ const hoveredPlotsArg = { bands: plotBands, lines: plotLines, shapes: plotShapes };
41
41
  const svgPointerX = x + boundsOffsetLeft;
42
42
  const svgPointerY = y + boundsOffsetTop;
43
43
  dispatcher.call(EventType.HOVER_SHAPE, undefined, closest, [svgPointerX, svgPointerY], hoveredPlotsArg);
@@ -55,8 +55,9 @@ export function useDefaultState(props) {
55
55
  hovered: closest,
56
56
  xAxis,
57
57
  yAxis: yAxis[0],
58
- hoveredPlotLines: plotLines,
59
58
  hoveredPlotBands: plotBands,
59
+ hoveredPlotLines: plotLines,
60
+ hoveredPlotShapes: plotShapes,
60
61
  }, syntheticEvent);
61
62
  });
62
63
  }, [
@@ -4,7 +4,7 @@ const getMarginTop = (args) => {
4
4
  const { chart, preparedTitle } = args;
5
5
  let marginTop = get(chart, 'margin.top', 0);
6
6
  if (preparedTitle === null || preparedTitle === void 0 ? void 0 : preparedTitle.height) {
7
- marginTop += preparedTitle.height;
7
+ marginTop += preparedTitle.height + preparedTitle.margin;
8
8
  }
9
9
  return marginTop;
10
10
  };
@@ -22,6 +22,7 @@ export declare function getNormalizedXAxis(props: {
22
22
  maxPadding?: number;
23
23
  plotLines?: import("../../..").AxisPlotLine[];
24
24
  plotBands?: import("../../..").AxisPlotBand[];
25
+ plotShapes?: import("../../..").AxisPlotShape[];
25
26
  tickMarks?: import("../../..").ChartAxisTickMarks;
26
27
  visible?: boolean;
27
28
  order?: "sortAsc" | "sortDesc" | "reverse";
@@ -1,5 +1,6 @@
1
1
  import type { PreparedTitle } from '../../../hooks/types';
2
2
  import type { ChartData } from '../../../types';
3
- export declare const getPreparedTitle: ({ title, }: {
3
+ export declare const getPreparedTitle: ({ title, chartWidth, }: {
4
4
  title: ChartData["title"];
5
+ chartWidth: number;
5
6
  }) => Promise<PreparedTitle | undefined>;
@@ -1,20 +1,78 @@
1
1
  import get from 'lodash/get';
2
- import { getTextSizeFn } from '../../../core/utils';
2
+ import { getTextSizeFn, getTextWithElipsis, wrapText } from '../../../core/utils';
3
3
  const DEFAULT_TITLE_FONT_SIZE = '15px';
4
- const TITLE_PADDINGS = 8 * 2;
5
- export const getPreparedTitle = async ({ title, }) => {
6
- var _a, _b, _c, _d, _e, _f;
4
+ const DEFAULT_TITLE_MARGIN = 10;
5
+ export const getPreparedTitle = async ({ title, chartWidth, }) => {
6
+ var _a, _b, _c, _d, _e, _f, _g, _h;
7
7
  const titleText = get(title, 'text');
8
8
  const titleStyle = {
9
9
  fontSize: (_b = (_a = title === null || title === void 0 ? void 0 : title.style) === null || _a === void 0 ? void 0 : _a.fontSize) !== null && _b !== void 0 ? _b : DEFAULT_TITLE_FONT_SIZE,
10
10
  fontWeight: (_d = (_c = title === null || title === void 0 ? void 0 : title.style) === null || _c === void 0 ? void 0 : _c.fontWeight) !== null && _d !== void 0 ? _d : 'var(--g-text-subheader-font-weight)',
11
11
  fontColor: (_f = (_e = title === null || title === void 0 ? void 0 : title.style) === null || _e === void 0 ? void 0 : _e.fontColor) !== null && _f !== void 0 ? _f : 'var(--g-color-text-primary)',
12
12
  };
13
- const titleHeight = titleText
14
- ? (await getTextSizeFn({ style: titleStyle })(titleText)).height + TITLE_PADDINGS
15
- : 0;
16
- const preparedTitle = titleText
17
- ? { text: titleText, style: titleStyle, height: titleHeight, qa: title === null || title === void 0 ? void 0 : title.qa }
18
- : undefined;
19
- return preparedTitle;
13
+ if (!titleText) {
14
+ return undefined;
15
+ }
16
+ const getTitleTextSize = getTextSizeFn({ style: titleStyle });
17
+ const maxRowCount = (_g = title === null || title === void 0 ? void 0 : title.maxRowCount) !== null && _g !== void 0 ? _g : 1;
18
+ const contentRows = [];
19
+ if (maxRowCount > 1) {
20
+ let titleTextRows = await wrapText({
21
+ text: titleText,
22
+ style: titleStyle,
23
+ width: chartWidth,
24
+ getTextSize: getTitleTextSize,
25
+ });
26
+ titleTextRows = titleTextRows.reduce((acc, row, index) => {
27
+ if (index < maxRowCount) {
28
+ acc.push(row);
29
+ }
30
+ else {
31
+ acc[maxRowCount - 1].text += row.text;
32
+ }
33
+ return acc;
34
+ }, []);
35
+ for (let i = 0; i < titleTextRows.length; i++) {
36
+ const textRow = titleTextRows[i];
37
+ let textRowContent = textRow.text.trim();
38
+ if (i === titleTextRows.length - 1) {
39
+ textRowContent = await getTextWithElipsis({
40
+ text: textRowContent,
41
+ maxWidth: chartWidth,
42
+ getTextWidth: async (s) => (await getTitleTextSize(s)).width,
43
+ });
44
+ }
45
+ const textRowSize = await getTitleTextSize(textRowContent);
46
+ contentRows.push({
47
+ text: textRowContent,
48
+ x: 0,
49
+ y: textRow.y + textRowSize.hangingOffset,
50
+ size: textRowSize,
51
+ });
52
+ }
53
+ }
54
+ else {
55
+ const truncatedText = await getTextWithElipsis({
56
+ text: titleText,
57
+ maxWidth: chartWidth,
58
+ getTextWidth: async (s) => (await getTitleTextSize(s)).width,
59
+ });
60
+ const textSize = await getTitleTextSize(truncatedText);
61
+ contentRows.push({
62
+ text: truncatedText,
63
+ x: 0,
64
+ y: textSize.hangingOffset,
65
+ size: textSize,
66
+ });
67
+ }
68
+ const totalTextHeight = contentRows.reduce((acc, row) => acc + row.size.height, 0);
69
+ const titleHeight = totalTextHeight;
70
+ return {
71
+ text: titleText,
72
+ style: titleStyle,
73
+ height: titleHeight,
74
+ margin: (_h = title === null || title === void 0 ? void 0 : title.margin) !== null && _h !== void 0 ? _h : DEFAULT_TITLE_MARGIN,
75
+ qa: title === null || title === void 0 ? void 0 : title.qa,
76
+ contentRows,
77
+ };
20
78
  };
@@ -1,11 +1,9 @@
1
1
  import React from 'react';
2
2
  export const Title = (props) => {
3
- const { chartWidth, text, height, style, qa } = props;
4
- return (React.createElement("text", { dx: chartWidth / 2, dy: height / 2, dominantBaseline: "middle", textAnchor: "middle", style: {
3
+ const { chartWidth, style, qa, contentRows } = props;
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
- lineHeight: `${height}px`,
9
- }, "data-qa": qa },
10
- React.createElement("tspan", { dangerouslySetInnerHTML: { __html: text } })));
8
+ }, "data-qa": qa }, contentRows.map((row, i) => (React.createElement("tspan", { key: i, x: chartWidth / 2, y: row.y, dominantBaseline: "hanging", dangerouslySetInnerHTML: { __html: row.text } })))));
11
9
  };
@@ -7,8 +7,9 @@ export interface ChartTooltipContentProps {
7
7
  rowRenderer?: ChartTooltip['rowRenderer'];
8
8
  valueFormat?: ChartTooltip['valueFormat'];
9
9
  headerFormat?: ChartTooltip['headerFormat'];
10
- hoveredPlotLines?: ChartTooltipRendererArgs['hoveredPlotLines'];
11
10
  hoveredPlotBands?: ChartTooltipRendererArgs['hoveredPlotBands'];
11
+ hoveredPlotLines?: ChartTooltipRendererArgs['hoveredPlotLines'];
12
+ hoveredPlotShapes?: ChartTooltipRendererArgs['hoveredPlotShapes'];
12
13
  totals?: ChartTooltip['totals'];
13
14
  xAxis?: ChartXAxis | null;
14
15
  yAxis?: ChartYAxis;
@@ -2,15 +2,16 @@ import React from 'react';
2
2
  import isNil from 'lodash/isNil';
3
3
  import { DefaultTooltipContent } from './DefaultTooltipContent';
4
4
  export const ChartTooltipContent = React.memo((props) => {
5
- const { hovered, hoveredPlotLines, hoveredPlotBands, xAxis, yAxis, renderer, rowRenderer, valueFormat, headerFormat, totals, pinned, qa, } = props;
5
+ const { hovered, hoveredPlotBands, hoveredPlotLines, hoveredPlotShapes, xAxis, yAxis, renderer, rowRenderer, valueFormat, headerFormat, totals, pinned, qa, } = props;
6
6
  if (!hovered) {
7
7
  return null;
8
8
  }
9
9
  const customTooltip = renderer === null || renderer === void 0 ? void 0 : renderer({
10
10
  headerFormat,
11
11
  hovered,
12
- hoveredPlotLines,
13
12
  hoveredPlotBands,
13
+ hoveredPlotLines,
14
+ hoveredPlotShapes,
14
15
  xAxis,
15
16
  yAxis,
16
17
  });
@@ -7,7 +7,7 @@ import './styles.css';
7
7
  const b = block('tooltip');
8
8
  export const Tooltip = (props) => {
9
9
  const { tooltip, xAxis, yAxis, svgContainer, dispatcher, tooltipPinned, onOutsideClick } = props;
10
- const { hovered, hoveredPlotLines, hoveredPlotBands, pointerPosition } = useTooltip({
10
+ const { hovered, hoveredPlotBands, hoveredPlotLines, hoveredPlotShapes, pointerPosition } = useTooltip({
11
11
  dispatcher,
12
12
  tooltip,
13
13
  xAxis,
@@ -28,5 +28,5 @@ export const Tooltip = (props) => {
28
28
  }, [left, top]);
29
29
  return (hovered === null || hovered === void 0 ? void 0 : hovered.length) ? (React.createElement(Popup, { anchorElement: anchor, className: b({ pinned: tooltipPinned }), disableTransition: true, floatingStyles: tooltipPinned ? undefined : { pointerEvents: 'none' }, offset: { mainAxis: 20 }, onOpenChange: tooltipPinned ? handleOnOpenChange : undefined, open: true, placement: ['right', 'left', 'top', 'bottom'] },
30
30
  React.createElement("div", { className: b('popup-content') },
31
- React.createElement(ChartTooltipContent, { hovered: hovered, hoveredPlotLines: hoveredPlotLines, hoveredPlotBands: hoveredPlotBands, pinned: tooltipPinned, renderer: tooltip.renderer, rowRenderer: tooltip.rowRenderer, totals: tooltip.totals, valueFormat: tooltip.valueFormat, headerFormat: tooltip.headerFormat, xAxis: xAxis, yAxis: yAxis, qa: tooltip.qa })))) : null;
31
+ React.createElement(ChartTooltipContent, { hovered: hovered, hoveredPlotBands: hoveredPlotBands, hoveredPlotLines: hoveredPlotLines, hoveredPlotShapes: hoveredPlotShapes, pinned: tooltipPinned, renderer: tooltip.renderer, rowRenderer: tooltip.rowRenderer, totals: tooltip.totals, valueFormat: tooltip.valueFormat, headerFormat: tooltip.headerFormat, xAxis: xAxis, yAxis: yAxis, qa: tooltip.qa })))) : null;
32
32
  };