@gravity-ui/chartkit 5.15.0 → 5.16.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 (60) hide show
  1. package/build/plugins/d3/renderer/components/Legend.js +129 -66
  2. package/build/plugins/d3/renderer/components/styles.css +8 -0
  3. package/build/plugins/d3/renderer/constants/defaults/legend.d.ts +12 -4
  4. package/build/plugins/d3/renderer/constants/defaults/legend.js +4 -0
  5. package/build/plugins/d3/renderer/hooks/useSeries/prepare-area.js +1 -0
  6. package/build/plugins/d3/renderer/hooks/useSeries/prepare-bar-x.js +1 -0
  7. package/build/plugins/d3/renderer/hooks/useSeries/prepare-bar-y.js +8 -6
  8. package/build/plugins/d3/renderer/hooks/useSeries/prepare-legend.js +58 -11
  9. package/build/plugins/d3/renderer/hooks/useSeries/prepare-line.js +1 -0
  10. package/build/plugins/d3/renderer/hooks/useSeries/prepare-treemap.js +1 -0
  11. package/build/plugins/d3/renderer/hooks/useSeries/prepare-waterfall.js +1 -0
  12. package/build/plugins/d3/renderer/hooks/useSeries/types.d.ts +23 -1
  13. package/build/plugins/d3/renderer/hooks/useShapes/HtmlLayer.d.ts +8 -0
  14. package/build/plugins/d3/renderer/hooks/useShapes/HtmlLayer.js +22 -0
  15. package/build/plugins/d3/renderer/hooks/useShapes/area/index.d.ts +1 -0
  16. package/build/plugins/d3/renderer/hooks/useShapes/area/index.js +5 -2
  17. package/build/plugins/d3/renderer/hooks/useShapes/area/prepare-data.js +18 -3
  18. package/build/plugins/d3/renderer/hooks/useShapes/area/types.d.ts +2 -1
  19. package/build/plugins/d3/renderer/hooks/useShapes/bar-x/index.d.ts +1 -0
  20. package/build/plugins/d3/renderer/hooks/useShapes/bar-x/index.js +5 -2
  21. package/build/plugins/d3/renderer/hooks/useShapes/bar-x/prepare-data.js +21 -4
  22. package/build/plugins/d3/renderer/hooks/useShapes/bar-x/types.d.ts +2 -1
  23. package/build/plugins/d3/renderer/hooks/useShapes/bar-y/index.d.ts +1 -0
  24. package/build/plugins/d3/renderer/hooks/useShapes/bar-y/index.js +18 -23
  25. package/build/plugins/d3/renderer/hooks/useShapes/bar-y/prepare-data.js +44 -3
  26. package/build/plugins/d3/renderer/hooks/useShapes/bar-y/types.d.ts +3 -0
  27. package/build/plugins/d3/renderer/hooks/useShapes/index.js +7 -7
  28. package/build/plugins/d3/renderer/hooks/useShapes/line/index.d.ts +1 -0
  29. package/build/plugins/d3/renderer/hooks/useShapes/line/index.js +5 -2
  30. package/build/plugins/d3/renderer/hooks/useShapes/line/prepare-data.js +17 -1
  31. package/build/plugins/d3/renderer/hooks/useShapes/line/types.d.ts +2 -1
  32. package/build/plugins/d3/renderer/hooks/useShapes/pie/index.js +2 -10
  33. package/build/plugins/d3/renderer/hooks/useShapes/pie/prepare-data.js +0 -1
  34. package/build/plugins/d3/renderer/hooks/useShapes/scatter/index.d.ts +1 -0
  35. package/build/plugins/d3/renderer/hooks/useShapes/scatter/index.js +5 -2
  36. package/build/plugins/d3/renderer/hooks/useShapes/scatter/prepare-data.js +1 -0
  37. package/build/plugins/d3/renderer/hooks/useShapes/scatter/types.d.ts +2 -0
  38. package/build/plugins/d3/renderer/hooks/useShapes/treemap/index.d.ts +1 -0
  39. package/build/plugins/d3/renderer/hooks/useShapes/treemap/index.js +5 -2
  40. package/build/plugins/d3/renderer/hooks/useShapes/treemap/prepare-data.js +1 -1
  41. package/build/plugins/d3/renderer/hooks/useShapes/treemap/types.d.ts +2 -0
  42. package/build/plugins/d3/renderer/hooks/useShapes/waterfall/index.d.ts +1 -0
  43. package/build/plugins/d3/renderer/hooks/useShapes/waterfall/index.js +5 -2
  44. package/build/plugins/d3/renderer/hooks/useShapes/waterfall/prepare-data.js +1 -0
  45. package/build/plugins/d3/renderer/hooks/useShapes/waterfall/types.d.ts +2 -1
  46. package/build/plugins/d3/renderer/types/index.d.ts +3 -1
  47. package/build/plugins/d3/renderer/utils/axis-generators/bottom.d.ts +5 -4
  48. package/build/plugins/d3/renderer/utils/axis-generators/bottom.js +11 -7
  49. package/build/plugins/d3/renderer/utils/axis.d.ts +1 -1
  50. package/build/plugins/d3/renderer/utils/axis.js +1 -1
  51. package/build/plugins/d3/renderer/utils/color.d.ts +10 -0
  52. package/build/plugins/d3/renderer/utils/color.js +43 -0
  53. package/build/plugins/d3/renderer/utils/index.d.ts +2 -0
  54. package/build/plugins/d3/renderer/utils/index.js +2 -0
  55. package/build/plugins/d3/renderer/utils/legend.d.ts +8 -0
  56. package/build/plugins/d3/renderer/utils/legend.js +23 -0
  57. package/build/plugins/d3/renderer/utils/text.js +17 -10
  58. package/build/types/widget-data/bar-x.d.ts +1 -1
  59. package/build/types/widget-data/legend.d.ts +24 -0
  60. package/package.json +1 -1
@@ -3,11 +3,12 @@ import { color, select } from 'd3';
3
3
  import get from 'lodash/get';
4
4
  import { block } from '../../../../../../utils/cn';
5
5
  import { filterOverlappingLabels } from '../../../utils';
6
+ import { HtmlLayer } from '../HtmlLayer';
6
7
  export { prepareBarXData } from './prepare-data';
7
8
  export * from './types';
8
9
  const b = block('d3-bar-x');
9
10
  export const BarXSeriesShapes = (args) => {
10
- const { dispatcher, preparedData, seriesOptions } = args;
11
+ const { dispatcher, preparedData, seriesOptions, htmlLayout } = args;
11
12
  const ref = React.useRef(null);
12
13
  React.useEffect(() => {
13
14
  var _a;
@@ -89,5 +90,7 @@ export const BarXSeriesShapes = (args) => {
89
90
  dispatcher.on('hover-shape.bar-x', null);
90
91
  };
91
92
  }, [dispatcher, preparedData, seriesOptions]);
92
- return React.createElement("g", { ref: ref, className: b() });
93
+ return (React.createElement(React.Fragment, null,
94
+ React.createElement("g", { ref: ref, className: b() }),
95
+ React.createElement(HtmlLayer, { preparedData: preparedData, htmlLayout: htmlLayout })));
93
96
  };
@@ -8,15 +8,21 @@ function getLabelData(d) {
8
8
  }
9
9
  const text = String(d.data.label || d.data.y);
10
10
  const style = d.series.dataLabels.style;
11
- const { maxHeight: height, maxWidth: width } = getLabelsSize({ labels: [text], style });
11
+ const html = d.series.dataLabels.html;
12
+ const { maxHeight: height, maxWidth: width } = getLabelsSize({
13
+ labels: [text],
14
+ style,
15
+ html,
16
+ });
12
17
  let y = Math.max(height, d.y - d.series.dataLabels.padding);
13
18
  if (d.series.dataLabels.inside) {
14
19
  y = d.y + d.height / 2;
15
20
  }
21
+ const x = d.x + d.width / 2;
16
22
  return {
17
23
  text,
18
- x: d.x + d.width / 2,
19
- y,
24
+ x: html ? x - width / 2 : x,
25
+ y: html ? y - height : y,
20
26
  style,
21
27
  size: { width, height },
22
28
  textAnchor: 'middle',
@@ -122,8 +128,19 @@ export const prepareBarXData = (args) => {
122
128
  opacity: get(yValue.data, 'opacity', null),
123
129
  data: yValue.data,
124
130
  series: yValue.series,
131
+ htmlElements: [],
125
132
  };
126
- barData.label = getLabelData(barData);
133
+ const label = getLabelData(barData);
134
+ if (yValue.series.dataLabels.html && label) {
135
+ barData.htmlElements.push({
136
+ x: label.x,
137
+ y: label.y,
138
+ content: label.text,
139
+ });
140
+ }
141
+ else {
142
+ barData.label = getLabelData(barData);
143
+ }
127
144
  stackItems.push(barData);
128
145
  stackHeight += height + 1;
129
146
  });
@@ -1,5 +1,5 @@
1
1
  import { TooltipDataChunkBarX } from '../../../../../../types';
2
- import { LabelData } from '../../../types';
2
+ import { HtmlItem, LabelData } from '../../../types';
3
3
  import { PreparedBarXSeries } from '../../useSeries/types';
4
4
  export type PreparedBarXData = Omit<TooltipDataChunkBarX, 'series'> & {
5
5
  x: number;
@@ -9,4 +9,5 @@ export type PreparedBarXData = Omit<TooltipDataChunkBarX, 'series'> & {
9
9
  opacity: number | null;
10
10
  series: PreparedBarXSeries;
11
11
  label?: LabelData;
12
+ htmlElements: HtmlItem[];
12
13
  };
@@ -7,5 +7,6 @@ type Args = {
7
7
  dispatcher: Dispatch<object>;
8
8
  preparedData: PreparedBarYData[];
9
9
  seriesOptions: PreparedSeriesOptions;
10
+ htmlLayout: HTMLElement | null;
10
11
  };
11
12
  export declare const BarYSeriesShapes: (args: Args) => React.JSX.Element;
@@ -2,11 +2,11 @@ import React from 'react';
2
2
  import { color, select } from 'd3';
3
3
  import get from 'lodash/get';
4
4
  import { block } from '../../../../../../utils/cn';
5
+ import { HtmlLayer } from '../HtmlLayer';
5
6
  export { prepareBarYData } from './prepare-data';
6
- const DEFAULT_LABEL_PADDING = 7;
7
7
  const b = block('d3-bar-y');
8
8
  export const BarYSeriesShapes = (args) => {
9
- const { dispatcher, preparedData, seriesOptions } = args;
9
+ const { dispatcher, preparedData, seriesOptions, htmlLayout } = args;
10
10
  const ref = React.useRef(null);
11
11
  React.useEffect(() => {
12
12
  if (!ref.current) {
@@ -26,31 +26,24 @@ export const BarYSeriesShapes = (args) => {
26
26
  .attr('fill', (d) => d.color)
27
27
  .attr('opacity', (d) => d.data.opacity || null)
28
28
  .attr('cursor', (d) => d.series.cursor);
29
- const dataLabels = preparedData.filter((d) => d.series.dataLabels.enabled);
29
+ const dataLabels = preparedData.reduce((acc, d) => {
30
+ if (d.label) {
31
+ acc.push(d.label);
32
+ }
33
+ return acc;
34
+ }, []);
30
35
  const labelSelection = svgElement
31
36
  .selectAll('text')
32
37
  .data(dataLabels)
33
38
  .join('text')
34
- .text((d) => String(d.data.label || d.data.x))
39
+ .text((d) => d.text)
35
40
  .attr('class', b('label'))
36
- .attr('x', (d) => {
37
- if (d.series.dataLabels.inside) {
38
- return d.x + d.width / 2;
39
- }
40
- return d.x + d.width + DEFAULT_LABEL_PADDING;
41
- })
42
- .attr('y', (d) => {
43
- return d.y + d.height / 2 + d.series.dataLabels.maxHeight / 2;
44
- })
45
- .attr('text-anchor', (d) => {
46
- if (d.series.dataLabels.inside) {
47
- return 'middle';
48
- }
49
- return 'right';
50
- })
51
- .style('font-size', (d) => d.series.dataLabels.style.fontSize)
52
- .style('font-weight', (d) => d.series.dataLabels.style.fontWeight || null)
53
- .style('fill', (d) => d.series.dataLabels.style.fontColor || null);
41
+ .attr('x', (d) => d.x)
42
+ .attr('y', (d) => d.y)
43
+ .attr('text-anchor', (d) => d.textAnchor)
44
+ .style('font-size', (d) => d.style.fontSize)
45
+ .style('font-weight', (d) => d.style.fontWeight || null)
46
+ .style('fill', (d) => d.style.fontColor || null);
54
47
  const hoverOptions = get(seriesOptions, 'bar-y.states.hover');
55
48
  const inactiveOptions = get(seriesOptions, 'bar-y.states.inactive');
56
49
  dispatcher.on('hover-shape.bar-y', (data) => {
@@ -85,5 +78,7 @@ export const BarYSeriesShapes = (args) => {
85
78
  dispatcher.on('hover-shape.bar-y', null);
86
79
  };
87
80
  }, [dispatcher, preparedData, seriesOptions]);
88
- return React.createElement("g", { ref: ref, className: b() });
81
+ return (React.createElement(React.Fragment, null,
82
+ React.createElement("g", { ref: ref, className: b() }),
83
+ React.createElement(HtmlLayer, { preparedData: preparedData, htmlLayout: htmlLayout })));
89
84
  };
@@ -1,7 +1,8 @@
1
1
  import { ascending, descending, max, sort } from 'd3';
2
2
  import get from 'lodash/get';
3
- import { getDataCategoryValue } from '../../../utils';
3
+ import { getDataCategoryValue, getLabelsSize } from '../../../utils';
4
4
  import { MIN_BAR_GAP, MIN_BAR_GROUP_GAP, MIN_BAR_WIDTH } from '../constants';
5
+ const DEFAULT_LABEL_PADDING = 7;
5
6
  function groupByYValue(series, yAxis) {
6
7
  const data = {};
7
8
  series.forEach((s) => {
@@ -47,6 +48,41 @@ function getBandWidth(series, yAxis, yScale) {
47
48
  }
48
49
  return bandWidth;
49
50
  }
51
+ function setLabel(prepared) {
52
+ const dataLabels = prepared.series.dataLabels;
53
+ if (!dataLabels.enabled) {
54
+ return;
55
+ }
56
+ const data = prepared.data;
57
+ const content = String(data.label || data.x);
58
+ const { maxHeight: height, maxWidth: width } = getLabelsSize({
59
+ labels: [content],
60
+ style: dataLabels.style,
61
+ html: dataLabels.html,
62
+ });
63
+ const x = dataLabels.inside
64
+ ? prepared.x + prepared.width / 2
65
+ : prepared.x + prepared.width + DEFAULT_LABEL_PADDING;
66
+ const y = prepared.y + prepared.height / 2;
67
+ if (dataLabels.html) {
68
+ prepared.htmlElements.push({
69
+ x,
70
+ y: y - height / 2,
71
+ content,
72
+ });
73
+ }
74
+ else {
75
+ prepared.label = {
76
+ x,
77
+ y: y + height / 2,
78
+ text: content,
79
+ textAnchor: dataLabels.inside ? 'middle' : 'right',
80
+ style: dataLabels.style,
81
+ series: prepared.series,
82
+ size: { width, height },
83
+ };
84
+ }
85
+ }
50
86
  export const prepareBarYData = (args) => {
51
87
  const { series, seriesOptions, yAxis, xScale, yScale: [yScale], } = args;
52
88
  const xLinearScale = xScale;
@@ -100,7 +136,7 @@ export const prepareBarYData = (args) => {
100
136
  const y = center - currentBarHeight / 2 + (barHeight + rectGap) * groupItemIndex;
101
137
  const xValue = Number(data.x);
102
138
  const width = xValue > 0 ? xLinearScale(xValue) - base : base - xLinearScale(xValue);
103
- stackItems.push({
139
+ const item = {
104
140
  x: xValue > 0 ? stackSum : stackSum - width,
105
141
  y,
106
142
  width,
@@ -109,7 +145,9 @@ export const prepareBarYData = (args) => {
109
145
  opacity: get(data, 'opacity', null),
110
146
  data,
111
147
  series: s,
112
- });
148
+ htmlElements: [],
149
+ };
150
+ stackItems.push(item);
113
151
  stackSum += width + 1;
114
152
  });
115
153
  if (series.some((s) => s.stacking === 'percent')) {
@@ -124,5 +162,8 @@ export const prepareBarYData = (args) => {
124
162
  result.push(...stackItems);
125
163
  });
126
164
  });
165
+ result.forEach((d) => {
166
+ setLabel(d);
167
+ });
127
168
  return result;
128
169
  };
@@ -1,4 +1,5 @@
1
1
  import { TooltipDataChunkBarX } from '../../../../../../types';
2
+ import { HtmlItem, LabelData } from '../../../types';
2
3
  import { PreparedBarYSeries } from '../../useSeries/types';
3
4
  export type PreparedBarYData = Omit<TooltipDataChunkBarX, 'series'> & {
4
5
  x: number;
@@ -8,4 +9,6 @@ export type PreparedBarYData = Omit<TooltipDataChunkBarX, 'series'> & {
8
9
  color: string;
9
10
  opacity: number | null;
10
11
  series: PreparedBarYSeries;
12
+ label?: LabelData;
13
+ htmlElements: HtmlItem[];
11
14
  };
@@ -34,7 +34,7 @@ export const useShapes = (args) => {
34
34
  yScale,
35
35
  boundsHeight,
36
36
  });
37
- acc.push(React.createElement(BarXSeriesShapes, { key: "bar-x", dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData }));
37
+ acc.push(React.createElement(BarXSeriesShapes, { key: "bar-x", dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData, htmlLayout: htmlLayout }));
38
38
  shapesData.push(...preparedData);
39
39
  }
40
40
  break;
@@ -49,7 +49,7 @@ export const useShapes = (args) => {
49
49
  yAxis,
50
50
  yScale,
51
51
  });
52
- acc.push(React.createElement(BarYSeriesShapes, { key: "bar-y", dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData }));
52
+ acc.push(React.createElement(BarYSeriesShapes, { key: "bar-y", dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData, htmlLayout: htmlLayout }));
53
53
  shapesData.push(...preparedData);
54
54
  }
55
55
  break;
@@ -64,7 +64,7 @@ export const useShapes = (args) => {
64
64
  yAxis,
65
65
  yScale,
66
66
  });
67
- acc.push(React.createElement(WaterfallSeriesShapes, { key: "waterfall", dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData }));
67
+ acc.push(React.createElement(WaterfallSeriesShapes, { key: "waterfall", dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData, htmlLayout: htmlLayout }));
68
68
  shapesData.push(...preparedData);
69
69
  }
70
70
  break;
@@ -79,7 +79,7 @@ export const useShapes = (args) => {
79
79
  yScale,
80
80
  split,
81
81
  });
82
- acc.push(React.createElement(LineSeriesShapes, { key: "line", dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData }));
82
+ acc.push(React.createElement(LineSeriesShapes, { key: "line", dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData, htmlLayout: htmlLayout }));
83
83
  shapesData.push(...preparedData);
84
84
  }
85
85
  break;
@@ -94,7 +94,7 @@ export const useShapes = (args) => {
94
94
  yScale,
95
95
  boundsHeight,
96
96
  });
97
- acc.push(React.createElement(AreaSeriesShapes, { key: "area", dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData }));
97
+ acc.push(React.createElement(AreaSeriesShapes, { key: "area", dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData, htmlLayout: htmlLayout }));
98
98
  shapesData.push(...preparedData);
99
99
  }
100
100
  break;
@@ -108,7 +108,7 @@ export const useShapes = (args) => {
108
108
  yAxis,
109
109
  yScale,
110
110
  });
111
- acc.push(React.createElement(ScatterSeriesShape, { key: "scatter", dispatcher: dispatcher, preparedData: preparedData, seriesOptions: seriesOptions }));
111
+ acc.push(React.createElement(ScatterSeriesShape, { key: "scatter", dispatcher: dispatcher, preparedData: preparedData, seriesOptions: seriesOptions, htmlLayout: htmlLayout }));
112
112
  shapesData.push(...preparedData);
113
113
  }
114
114
  break;
@@ -131,7 +131,7 @@ export const useShapes = (args) => {
131
131
  width: boundsWidth,
132
132
  height: boundsHeight,
133
133
  });
134
- acc.push(React.createElement(TreemapSeriesShape, { key: "treemap", dispatcher: dispatcher, preparedData: preparedData, seriesOptions: seriesOptions }));
134
+ acc.push(React.createElement(TreemapSeriesShape, { key: "treemap", dispatcher: dispatcher, preparedData: preparedData, seriesOptions: seriesOptions, htmlLayout: htmlLayout }));
135
135
  shapesData.push(preparedData);
136
136
  }
137
137
  }
@@ -6,6 +6,7 @@ type Args = {
6
6
  dispatcher: Dispatch<object>;
7
7
  preparedData: PreparedLineData[];
8
8
  seriesOptions: PreparedSeriesOptions;
9
+ htmlLayout: HTMLElement | null;
9
10
  };
10
11
  export declare const LineSeriesShapes: (args: Args) => React.JSX.Element;
11
12
  export {};
@@ -3,11 +3,12 @@ import { color, line as lineGenerator, select } from 'd3';
3
3
  import get from 'lodash/get';
4
4
  import { block } from '../../../../../../utils/cn';
5
5
  import { filterOverlappingLabels } from '../../../utils';
6
+ import { HtmlLayer } from '../HtmlLayer';
6
7
  import { getMarkerHaloVisibility, getMarkerVisibility, renderMarker, selectMarkerHalo, selectMarkerSymbol, setMarker, } from '../marker';
7
8
  import { getLineDashArray, setActiveState } from '../utils';
8
9
  const b = block('d3-line');
9
10
  export const LineSeriesShapes = (args) => {
10
- const { dispatcher, preparedData, seriesOptions } = args;
11
+ const { dispatcher, preparedData, seriesOptions, htmlLayout } = args;
11
12
  const ref = React.useRef(null);
12
13
  React.useEffect(() => {
13
14
  var _a;
@@ -124,5 +125,7 @@ export const LineSeriesShapes = (args) => {
124
125
  dispatcher.on('hover-shape.line', null);
125
126
  };
126
127
  }, [dispatcher, preparedData, seriesOptions]);
127
- return React.createElement("g", { ref: ref, className: b() });
128
+ return (React.createElement(React.Fragment, null,
129
+ React.createElement("g", { ref: ref, className: b() }),
130
+ React.createElement(HtmlLayer, { preparedData: preparedData, htmlLayout: htmlLayout })));
128
131
  };
@@ -26,6 +26,15 @@ function getLabelData(point, series, xMax) {
26
26
  }
27
27
  return labelData;
28
28
  }
29
+ function getHtmlLabel(point, series, xMax) {
30
+ const content = String(point.data.label || point.data.y);
31
+ const size = getLabelsSize({ labels: [content], html: true });
32
+ return {
33
+ x: Math.min(xMax - size.maxWidth, Math.max(0, point.x)),
34
+ y: Math.max(0, point.y - series.dataLabels.padding - size.maxHeight),
35
+ content,
36
+ };
37
+ }
29
38
  export const prepareLineData = (args) => {
30
39
  const { series, xAxis, yAxis, xScale, yScale, split } = args;
31
40
  const [_xMin, xRangeMax] = xScale.range();
@@ -43,9 +52,15 @@ export const prepareLineData = (args) => {
43
52
  data: d,
44
53
  series: s,
45
54
  }));
55
+ const htmlElements = [];
46
56
  let labels = [];
47
57
  if (s.dataLabels.enabled) {
48
- labels = points.map((p) => getLabelData(p, s, xMax));
58
+ if (s.dataLabels.html) {
59
+ htmlElements.push(...points.map((p) => getHtmlLabel(p, s, xMax)));
60
+ }
61
+ else {
62
+ labels = points.map((p) => getLabelData(p, s, xMax));
63
+ }
49
64
  }
50
65
  let markers = [];
51
66
  if (s.marker.states.normal.enabled || s.marker.states.hover.enabled) {
@@ -68,6 +83,7 @@ export const prepareLineData = (args) => {
68
83
  dashStyle: s.dashStyle,
69
84
  linecap: s.linecap,
70
85
  opacity: s.opacity,
86
+ htmlElements,
71
87
  };
72
88
  acc.push(result);
73
89
  return acc;
@@ -1,6 +1,6 @@
1
1
  import { DashStyle, LineCap } from '../../../../../../constants';
2
2
  import { LineSeriesData } from '../../../../../../types';
3
- import { LabelData } from '../../../types';
3
+ import { HtmlItem, LabelData } from '../../../types';
4
4
  import { PreparedLineSeries } from '../../useSeries/types';
5
5
  export type PointData = {
6
6
  x: number;
@@ -26,4 +26,5 @@ export type PreparedLineData = {
26
26
  dashStyle: DashStyle;
27
27
  linecap: LineCap;
28
28
  opacity: number | null;
29
+ htmlElements: HtmlItem[];
29
30
  };
@@ -1,9 +1,9 @@
1
1
  import React from 'react';
2
- import { Portal } from '@gravity-ui/uikit';
3
2
  import { arc, color, select } from 'd3';
4
3
  import get from 'lodash/get';
5
4
  import { block } from '../../../../../../utils/cn';
6
5
  import { setEllipsisForOverflowTexts } from '../../../utils';
6
+ import { HtmlLayer } from '../HtmlLayer';
7
7
  import { setActiveState } from '../utils';
8
8
  const b = block('d3-pie');
9
9
  export function getHaloVisibility(d) {
@@ -13,12 +13,6 @@ export function getHaloVisibility(d) {
13
13
  export function PieSeriesShapes(args) {
14
14
  const { dispatcher, preparedData, seriesOptions, htmlLayout } = args;
15
15
  const ref = React.useRef(null);
16
- const htmlItems = React.useMemo(() => {
17
- return preparedData.reduce((result, d) => {
18
- result.push(...d.htmlElements);
19
- return result;
20
- }, []);
21
- }, [preparedData]);
22
16
  React.useEffect(() => {
23
17
  if (!ref.current) {
24
18
  return () => { };
@@ -174,7 +168,5 @@ export function PieSeriesShapes(args) {
174
168
  }, [dispatcher, preparedData, seriesOptions]);
175
169
  return (React.createElement(React.Fragment, null,
176
170
  React.createElement("g", { ref: ref, className: b(), style: { zIndex: 9 } }),
177
- htmlLayout && (React.createElement(Portal, { container: htmlLayout }, htmlItems.map((item, index) => {
178
- return (React.createElement("div", { key: index, dangerouslySetInnerHTML: { __html: item.content }, style: { position: 'absolute', left: item.x, top: item.y } }));
179
- })))));
171
+ React.createElement(HtmlLayer, { preparedData: preparedData, htmlLayout: htmlLayout })));
180
172
  }
@@ -125,7 +125,6 @@ export function preparePieData(args) {
125
125
  active: true,
126
126
  segment: relatedSegment.data,
127
127
  angle: midAngle,
128
- html: shouldUseHtml,
129
128
  };
130
129
  let overlap = false;
131
130
  if (prevLabel) {
@@ -7,5 +7,6 @@ type ScatterSeriesShapeProps = {
7
7
  dispatcher: Dispatch<object>;
8
8
  preparedData: PreparedScatterData[];
9
9
  seriesOptions: PreparedSeriesOptions;
10
+ htmlLayout: HTMLElement | null;
10
11
  };
11
12
  export declare function ScatterSeriesShape(props: ScatterSeriesShapeProps): React.JSX.Element;
@@ -2,12 +2,13 @@ import React from 'react';
2
2
  import { select } from 'd3';
3
3
  import get from 'lodash/get';
4
4
  import { block } from '../../../../../../utils/cn';
5
+ import { HtmlLayer } from '../HtmlLayer';
5
6
  import { getMarkerHaloVisibility, renderMarker, selectMarkerHalo, selectMarkerSymbol, setMarker, } from '../marker';
6
7
  import { setActiveState, shapeKey } from '../utils';
7
8
  export { prepareScatterData } from './prepare-data';
8
9
  const b = block('d3-scatter');
9
10
  export function ScatterSeriesShape(props) {
10
- const { dispatcher, preparedData, seriesOptions } = props;
11
+ const { dispatcher, preparedData, seriesOptions, htmlLayout } = props;
11
12
  const ref = React.useRef(null);
12
13
  React.useEffect(() => {
13
14
  if (!ref.current) {
@@ -62,5 +63,7 @@ export function ScatterSeriesShape(props) {
62
63
  dispatcher.on('hover-shape.scatter', null);
63
64
  };
64
65
  }, [dispatcher, preparedData, seriesOptions]);
65
- return React.createElement("g", { ref: ref, className: b() });
66
+ return (React.createElement(React.Fragment, null,
67
+ React.createElement("g", { ref: ref, className: b() }),
68
+ React.createElement(HtmlLayer, { preparedData: preparedData, htmlLayout: htmlLayout })));
66
69
  }
@@ -23,6 +23,7 @@ export const prepareScatterData = (args) => {
23
23
  },
24
24
  hovered: false,
25
25
  active: true,
26
+ htmlElements: [],
26
27
  });
27
28
  });
28
29
  return acc;
@@ -1,4 +1,5 @@
1
1
  import { ScatterSeriesData } from '../../../../../../types';
2
+ import { HtmlItem } from '../../../types';
2
3
  import { PreparedScatterSeries } from '../../useSeries/types';
3
4
  type PointData = {
4
5
  x: number;
@@ -11,6 +12,7 @@ export type MarkerData = {
11
12
  point: PointData;
12
13
  active: boolean;
13
14
  hovered: boolean;
15
+ htmlElements: HtmlItem[];
14
16
  };
15
17
  export type PreparedScatterData = MarkerData;
16
18
  export {};
@@ -6,6 +6,7 @@ type ShapeProps = {
6
6
  dispatcher: Dispatch<object>;
7
7
  preparedData: PreparedTreemapData;
8
8
  seriesOptions: PreparedSeriesOptions;
9
+ htmlLayout: HTMLElement | null;
9
10
  };
10
11
  export declare const TreemapSeriesShape: (props: ShapeProps) => React.JSX.Element;
11
12
  export {};
@@ -3,9 +3,10 @@ import { color, select } from 'd3';
3
3
  import get from 'lodash/get';
4
4
  import { block } from '../../../../../../utils/cn';
5
5
  import { setEllipsisForOverflowTexts } from '../../../utils';
6
+ import { HtmlLayer } from '../HtmlLayer';
6
7
  const b = block('d3-treemap');
7
8
  export const TreemapSeriesShape = (props) => {
8
- const { dispatcher, preparedData, seriesOptions } = props;
9
+ const { dispatcher, preparedData, seriesOptions, htmlLayout } = props;
9
10
  const ref = React.useRef(null);
10
11
  React.useEffect(() => {
11
12
  if (!ref.current) {
@@ -92,5 +93,7 @@ export const TreemapSeriesShape = (props) => {
92
93
  dispatcher.on(eventName, null);
93
94
  };
94
95
  }, [dispatcher, preparedData, seriesOptions]);
95
- return React.createElement("g", { ref: ref, className: b() });
96
+ return (React.createElement(React.Fragment, null,
97
+ React.createElement("g", { ref: ref, className: b() }),
98
+ React.createElement(HtmlLayer, { preparedData: preparedData, htmlLayout: htmlLayout })));
96
99
  };
@@ -51,7 +51,7 @@ export function prepareTreemapData(args) {
51
51
  })(hierarchy);
52
52
  const leaves = root.leaves();
53
53
  const labelData = ((_a = series.dataLabels) === null || _a === void 0 ? void 0 : _a.enabled) ? getLabelData(leaves) : [];
54
- return { labelData, leaves, series };
54
+ return { labelData, leaves, series, htmlElements: [] };
55
55
  }
56
56
  function getSeriesDataWithRootNode(series) {
57
57
  return series.data.reduce((acc, d) => {
@@ -1,5 +1,6 @@
1
1
  import type { HierarchyRectangularNode } from 'd3';
2
2
  import type { TreemapSeriesData } from '../../../../../../types';
3
+ import { HtmlItem } from '../../../types';
3
4
  import type { PreparedTreemapSeries } from '../../useSeries/types';
4
5
  export type TreemapLabelData = {
5
6
  text: string;
@@ -12,4 +13,5 @@ export type PreparedTreemapData = {
12
13
  labelData: TreemapLabelData[];
13
14
  leaves: HierarchyRectangularNode<TreemapSeriesData<any>>[];
14
15
  series: PreparedTreemapSeries;
16
+ htmlElements: HtmlItem[];
15
17
  };
@@ -8,5 +8,6 @@ type Args = {
8
8
  dispatcher: Dispatch<object>;
9
9
  preparedData: PreparedWaterfallData[];
10
10
  seriesOptions: PreparedSeriesOptions;
11
+ htmlLayout: HTMLElement | null;
11
12
  };
12
13
  export declare const WaterfallSeriesShapes: (args: Args) => React.JSX.Element;
@@ -4,12 +4,13 @@ import get from 'lodash/get';
4
4
  import { DashStyle } from '../../../../../../constants';
5
5
  import { block } from '../../../../../../utils/cn';
6
6
  import { filterOverlappingLabels, getWaterfallPointColor } from '../../../utils';
7
+ import { HtmlLayer } from '../HtmlLayer';
7
8
  import { getLineDashArray } from '../utils';
8
9
  export { prepareWaterfallData } from './prepare-data';
9
10
  export * from './types';
10
11
  const b = block('d3-waterfall');
11
12
  export const WaterfallSeriesShapes = (args) => {
12
- const { dispatcher, preparedData, seriesOptions } = args;
13
+ const { dispatcher, preparedData, seriesOptions, htmlLayout } = args;
13
14
  const ref = React.useRef(null);
14
15
  const connectorSelector = `.${b('connector')}`;
15
16
  React.useEffect(() => {
@@ -121,5 +122,7 @@ export const WaterfallSeriesShapes = (args) => {
121
122
  dispatcher.on('hover-shape.waterfall', null);
122
123
  };
123
124
  }, [dispatcher, preparedData, seriesOptions]);
124
- return React.createElement("g", { ref: ref, className: b() });
125
+ return (React.createElement(React.Fragment, null,
126
+ React.createElement("g", { ref: ref, className: b() }),
127
+ React.createElement(HtmlLayer, { preparedData: preparedData, htmlLayout: htmlLayout })));
125
128
  };
@@ -124,6 +124,7 @@ export const prepareWaterfallData = (args) => {
124
124
  data: item.data,
125
125
  series: item.series,
126
126
  subTotal: totalValue,
127
+ htmlElements: [],
127
128
  };
128
129
  preparedData.label = getLabelData(preparedData, plotHeight);
129
130
  result.push(preparedData);
@@ -1,5 +1,5 @@
1
1
  import { WaterfallSeriesData } from '../../../../../../types';
2
- import { LabelData } from '../../../types';
2
+ import { HtmlItem, LabelData } from '../../../types';
3
3
  import { PreparedWaterfallSeries } from '../../useSeries/types';
4
4
  export type PreparedWaterfallData = {
5
5
  x: number;
@@ -11,4 +11,5 @@ export type PreparedWaterfallData = {
11
11
  data: WaterfallSeriesData;
12
12
  label?: LabelData;
13
13
  subTotal: number;
14
+ htmlElements: HtmlItem[];
14
15
  };
@@ -13,10 +13,12 @@ export type LabelData = {
13
13
  id: string;
14
14
  };
15
15
  active?: boolean;
16
- html?: boolean;
17
16
  };
18
17
  export type HtmlItem = {
19
18
  x: number;
20
19
  y: number;
21
20
  content: string;
22
21
  };
22
+ export type ShapeDataWithHtmlItems = {
23
+ htmlElements: HtmlItem[];
24
+ };
@@ -4,15 +4,16 @@ type AxisBottomArgs = {
4
4
  scale: AxisScale<AxisDomain>;
5
5
  ticks: {
6
6
  count?: number;
7
- maxTickCount: number;
8
- labelFormat: (value: any) => string;
7
+ maxTickCount?: number;
8
+ labelFormat?: (value: any) => string;
9
9
  labelsPaddings?: number;
10
10
  labelsMargin?: number;
11
11
  labelsStyle?: BaseTextStyle;
12
12
  labelsMaxWidth?: number;
13
13
  labelsLineHeight: number;
14
- items: [number, number][];
15
- rotation: number;
14
+ items?: [number, number][];
15
+ rotation?: number;
16
+ tickColor?: string;
16
17
  };
17
18
  domain: {
18
19
  size: number;