@gravity-ui/charts 1.7.1 → 1.9.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 (95) hide show
  1. package/dist/cjs/components/Axis/AxisX.js +10 -8
  2. package/dist/cjs/components/Axis/AxisY.js +16 -36
  3. package/dist/cjs/components/ChartInner/index.js +15 -1
  4. package/dist/cjs/components/ChartInner/useChartInnerProps.d.ts +4 -2
  5. package/dist/cjs/components/ChartInner/useChartInnerProps.js +4 -1
  6. package/dist/cjs/components/Legend/index.d.ts +1 -0
  7. package/dist/cjs/components/Legend/index.js +113 -51
  8. package/dist/cjs/components/Legend/styles.css +13 -0
  9. package/dist/cjs/components/Tooltip/DefaultContent.js +6 -3
  10. package/dist/cjs/constants/chart-types.d.ts +12 -0
  11. package/dist/cjs/constants/chart-types.js +12 -0
  12. package/dist/cjs/constants/defaults/axis.d.ts +3 -1
  13. package/dist/cjs/constants/defaults/axis.js +10 -0
  14. package/dist/cjs/constants/index.d.ts +6 -47
  15. package/dist/cjs/constants/index.js +6 -72
  16. package/dist/cjs/constants/layout-algorithms.d.ts +7 -0
  17. package/dist/cjs/constants/layout-algorithms.js +8 -0
  18. package/dist/cjs/constants/line-styles.d.ts +20 -0
  19. package/dist/cjs/constants/line-styles.js +20 -0
  20. package/dist/cjs/constants/palette.d.ts +1 -0
  21. package/dist/cjs/constants/palette.js +22 -0
  22. package/dist/cjs/constants/symbol-types.d.ts +7 -0
  23. package/dist/cjs/constants/symbol-types.js +8 -0
  24. package/dist/cjs/constants/typography.d.ts +1 -0
  25. package/dist/cjs/constants/typography.js +1 -0
  26. package/dist/cjs/hooks/index.d.ts +1 -0
  27. package/dist/cjs/hooks/index.js +1 -0
  28. package/dist/cjs/hooks/useChartOptions/types.d.ts +4 -2
  29. package/dist/cjs/hooks/useChartOptions/x-axis.js +11 -2
  30. package/dist/cjs/hooks/useChartOptions/y-axis.js +14 -3
  31. package/dist/cjs/hooks/useCrosshair/index.d.ts +17 -0
  32. package/dist/cjs/hooks/useCrosshair/index.js +139 -0
  33. package/dist/cjs/hooks/useCrosshair/useCrosshairHover.d.ts +11 -0
  34. package/dist/cjs/hooks/useCrosshair/useCrosshairHover.js +18 -0
  35. package/dist/cjs/hooks/useSeries/index.d.ts +4 -2
  36. package/dist/cjs/hooks/useSeries/prepare-legend.d.ts +11 -9
  37. package/dist/cjs/hooks/useSeries/prepare-legend.js +78 -23
  38. package/dist/cjs/hooks/useSeries/prepare-line.d.ts +1 -2
  39. package/dist/cjs/hooks/useSeries/prepare-line.js +3 -3
  40. package/dist/cjs/hooks/useSeries/types.d.ts +7 -3
  41. package/dist/cjs/hooks/useShapes/waterfall/index.js +2 -2
  42. package/dist/cjs/types/chart/axis.d.ts +26 -21
  43. package/dist/cjs/types/chart/legend.d.ts +6 -0
  44. package/dist/cjs/types/chart/line.d.ts +1 -1
  45. package/dist/cjs/types/chart/series.d.ts +1 -1
  46. package/dist/cjs/utils/chart/axis.d.ts +1 -0
  47. package/dist/cjs/utils/chart/axis.js +8 -0
  48. package/dist/esm/components/Axis/AxisX.js +10 -8
  49. package/dist/esm/components/Axis/AxisY.js +16 -36
  50. package/dist/esm/components/ChartInner/index.js +15 -1
  51. package/dist/esm/components/ChartInner/useChartInnerProps.d.ts +4 -2
  52. package/dist/esm/components/ChartInner/useChartInnerProps.js +4 -1
  53. package/dist/esm/components/Legend/index.d.ts +1 -0
  54. package/dist/esm/components/Legend/index.js +113 -51
  55. package/dist/esm/components/Legend/styles.css +13 -0
  56. package/dist/esm/components/Tooltip/DefaultContent.js +6 -3
  57. package/dist/esm/constants/chart-types.d.ts +12 -0
  58. package/dist/esm/constants/chart-types.js +12 -0
  59. package/dist/esm/constants/defaults/axis.d.ts +3 -1
  60. package/dist/esm/constants/defaults/axis.js +10 -0
  61. package/dist/esm/constants/index.d.ts +6 -47
  62. package/dist/esm/constants/index.js +6 -72
  63. package/dist/esm/constants/layout-algorithms.d.ts +7 -0
  64. package/dist/esm/constants/layout-algorithms.js +8 -0
  65. package/dist/esm/constants/line-styles.d.ts +20 -0
  66. package/dist/esm/constants/line-styles.js +20 -0
  67. package/dist/esm/constants/palette.d.ts +1 -0
  68. package/dist/esm/constants/palette.js +22 -0
  69. package/dist/esm/constants/symbol-types.d.ts +7 -0
  70. package/dist/esm/constants/symbol-types.js +8 -0
  71. package/dist/esm/constants/typography.d.ts +1 -0
  72. package/dist/esm/constants/typography.js +1 -0
  73. package/dist/esm/hooks/index.d.ts +1 -0
  74. package/dist/esm/hooks/index.js +1 -0
  75. package/dist/esm/hooks/useChartOptions/types.d.ts +4 -2
  76. package/dist/esm/hooks/useChartOptions/x-axis.js +11 -2
  77. package/dist/esm/hooks/useChartOptions/y-axis.js +14 -3
  78. package/dist/esm/hooks/useCrosshair/index.d.ts +17 -0
  79. package/dist/esm/hooks/useCrosshair/index.js +139 -0
  80. package/dist/esm/hooks/useCrosshair/useCrosshairHover.d.ts +11 -0
  81. package/dist/esm/hooks/useCrosshair/useCrosshairHover.js +18 -0
  82. package/dist/esm/hooks/useSeries/index.d.ts +4 -2
  83. package/dist/esm/hooks/useSeries/prepare-legend.d.ts +11 -9
  84. package/dist/esm/hooks/useSeries/prepare-legend.js +78 -23
  85. package/dist/esm/hooks/useSeries/prepare-line.d.ts +1 -2
  86. package/dist/esm/hooks/useSeries/prepare-line.js +3 -3
  87. package/dist/esm/hooks/useSeries/types.d.ts +7 -3
  88. package/dist/esm/hooks/useShapes/waterfall/index.js +2 -2
  89. package/dist/esm/types/chart/axis.d.ts +26 -21
  90. package/dist/esm/types/chart/legend.d.ts +6 -0
  91. package/dist/esm/types/chart/line.d.ts +1 -1
  92. package/dist/esm/types/chart/series.d.ts +1 -1
  93. package/dist/esm/utils/chart/axis.d.ts +1 -0
  94. package/dist/esm/utils/chart/axis.js +8 -0
  95. package/package.json +1 -1
@@ -50,11 +50,11 @@ export const AxisX = React.memo(function AxisX(props) {
50
50
  }
51
51
  const svgElement = select(ref.current);
52
52
  svgElement.selectAll('*').remove();
53
- const plotClassName = b('plot-x');
53
+ const plotDataAttr = 'data-plot-x';
54
54
  let plotContainer = null;
55
55
  if (plotRef === null || plotRef === void 0 ? void 0 : plotRef.current) {
56
56
  plotContainer = select(plotRef.current);
57
- plotContainer.selectAll(`.${plotClassName}`).remove();
57
+ plotContainer.selectAll(`[${plotDataAttr}]`).remove();
58
58
  }
59
59
  if (!axis.visible) {
60
60
  return;
@@ -116,12 +116,13 @@ export const AxisX = React.memo(function AxisX(props) {
116
116
  }
117
117
  // add plot bands
118
118
  if (plotContainer && axis.plotBands.length > 0) {
119
- const plotBandClassName = b('plot-x-band');
119
+ const plotBandDataAttr = 'plot-x-band';
120
120
  const plotBandsSelection = plotContainer
121
- .selectAll(`.${plotBandClassName}-x`)
121
+ .selectAll(`[${plotBandDataAttr}]`)
122
122
  .data(axis.plotBands)
123
123
  .join('g')
124
- .attr('class', `${plotClassName} ${plotBandClassName}-x`);
124
+ .attr(plotDataAttr, 1)
125
+ .attr(plotBandDataAttr, 1);
125
126
  plotBandsSelection
126
127
  .append('rect')
127
128
  .attr('x', (band) => {
@@ -153,12 +154,13 @@ export const AxisX = React.memo(function AxisX(props) {
153
154
  }
154
155
  // add plot lines
155
156
  if (plotContainer && axis.plotLines.length > 0) {
156
- const plotLineClassName = b('plot-x-line');
157
+ const plotLineDataAttr = 'plot-x-line';
157
158
  const plotLinesSelection = plotContainer
158
- .selectAll(`.${plotLineClassName}`)
159
+ .selectAll(`[${plotLineDataAttr}]`)
159
160
  .data(axis.plotLines)
160
161
  .join('g')
161
- .attr('class', `${plotClassName} ${plotLineClassName}`);
162
+ .attr(plotDataAttr, 1)
163
+ .attr(plotLineDataAttr, 1);
162
164
  const lineGenerator = line();
163
165
  plotLinesSelection
164
166
  .append('path')
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import { axisLeft, axisRight, line, select } from 'd3';
3
- import { block, calculateCos, calculateSin, formatAxisTickLabel, getAxisHeight, getAxisTitleRows, getBandsPosition, getClosestPointsRange, getLineDashArray, getScaleTicks, getTicksCount, handleOverflowingText, parseTransformStyle, setEllipsisForOverflowTexts, wrapText, } from '../../utils';
3
+ import { block, calculateCos, calculateSin, formatAxisTickLabel, getAxisHeight, getAxisPlotsPosition, getAxisTitleRows, getBandsPosition, getClosestPointsRange, getLineDashArray, getScaleTicks, getTicksCount, handleOverflowingText, parseTransformStyle, setEllipsisForOverflowTexts, wrapText, } from '../../utils';
4
4
  import './styles.css';
5
5
  const b = block('axis');
6
6
  function transformLabel(args) {
@@ -97,39 +97,17 @@ export const AxisY = (props) => {
97
97
  const svgElement = select(ref.current);
98
98
  svgElement.selectAll('*').remove();
99
99
  let plotContainer = null;
100
- const plotClassName = b('plot-y');
100
+ const plotDataAttr = 'data-plot-y';
101
101
  if (plotRef === null || plotRef === void 0 ? void 0 : plotRef.current) {
102
102
  plotContainer = select(plotRef.current);
103
- plotContainer.selectAll(`.${plotClassName}`).remove();
103
+ plotContainer.selectAll(`[${plotDataAttr}]`).remove();
104
104
  }
105
- const getAxisPosition = (axis) => {
106
- var _a;
107
- const top = ((_a = split.plots[axis.plotIndex]) === null || _a === void 0 ? void 0 : _a.top) || 0;
108
- if (axis.position === 'left') {
109
- return `translate(0, ${top}px)`;
110
- }
111
- return `translate(${width}px, 0)`;
112
- };
113
- const plotLines = axes.reduce((acc, axis) => {
114
- if (axis.plotLines.length) {
115
- acc.push(...axis.plotLines.map((plotLine) => {
116
- return Object.assign(Object.assign({}, plotLine), { transform: getAxisPosition(axis) });
117
- }));
118
- }
119
- return acc;
120
- }, []);
121
- const plotBands = axes.reduce((acc, axis) => {
122
- if (axis.plotBands.length) {
123
- acc.push(...axis.plotBands.map((plotBand) => (Object.assign(Object.assign({}, plotBand), { transform: getAxisPosition(axis) }))));
124
- }
125
- return acc;
126
- }, []);
127
105
  const axisSelection = svgElement
128
106
  .selectAll('axis')
129
107
  .data(axes)
130
108
  .join('g')
131
109
  .attr('class', b())
132
- .style('transform', (d) => getAxisPosition(d));
110
+ .style('transform', (d) => getAxisPlotsPosition(d, split, width));
133
111
  axisSelection.each((d, index, node) => {
134
112
  const seriesScale = scale[index];
135
113
  const axisItem = select(node[index]);
@@ -197,13 +175,14 @@ export const AxisY = (props) => {
197
175
  .remove();
198
176
  }
199
177
  if (plotContainer && d.plotBands.length > 0) {
200
- const plotBandClassName = b('plot-y-band');
178
+ const plotBandDataAttr = `data-plot-y-band-${index}`;
201
179
  const plotBandsSelection = plotContainer
202
- .selectAll(`.${plotBandClassName}`)
203
- .data(plotBands)
180
+ .selectAll(`[${plotBandDataAttr}]`)
181
+ .data(d.plotBands)
204
182
  .join('g')
205
- .attr('class', `${plotClassName} ${plotBandClassName}`)
206
- .style('transform', (plotBand) => plotBand.transform);
183
+ .attr(plotDataAttr, 1)
184
+ .attr(plotBandDataAttr, 1)
185
+ .style('transform', getAxisPlotsPosition(d, split));
207
186
  plotBandsSelection
208
187
  .append('rect')
209
188
  .attr('x', 0)
@@ -234,13 +213,14 @@ export const AxisY = (props) => {
234
213
  });
235
214
  }
236
215
  if (plotContainer && d.plotLines.length > 0) {
237
- const plotLineClassName = b('plot-y-line');
216
+ const plotLineDataAttr = `data-plot-y-line-${index}`;
238
217
  const plotLinesSelection = plotContainer
239
- .selectAll(`.${plotLineClassName}`)
240
- .data(plotLines)
218
+ .selectAll(`[${plotLineDataAttr}]`)
219
+ .data(d.plotLines)
241
220
  .join('g')
242
- .attr('class', `${plotClassName} ${plotLineClassName}`)
243
- .style('transform', (plotLine) => plotLine.transform);
221
+ .attr(plotDataAttr, 1)
222
+ .attr(plotLineDataAttr, 1)
223
+ .style('transform', getAxisPlotsPosition(d, split));
244
224
  plotLinesSelection
245
225
  .append('path')
246
226
  .attr('d', (plotLine) => {
@@ -1,4 +1,5 @@
1
1
  import React from 'react';
2
+ import { useCrosshair } from '../../hooks';
2
3
  import { EventType, block, getDispatcher } from '../../utils';
3
4
  import { AxisX, AxisY } from '../Axis';
4
5
  import { Legend } from '../Legend';
@@ -39,6 +40,19 @@ export const ChartInner = (props) => {
39
40
  });
40
41
  const clickHandler = (_b = (_a = data.chart) === null || _a === void 0 ? void 0 : _a.events) === null || _b === void 0 ? void 0 : _b.click;
41
42
  const pointerMoveHandler = (_d = (_c = data.chart) === null || _c === void 0 ? void 0 : _c.events) === null || _d === void 0 ? void 0 : _d.pointermove;
43
+ useCrosshair({
44
+ split: preparedSplit,
45
+ plotElement: plotRef.current,
46
+ boundsOffsetLeft,
47
+ boundsOffsetTop,
48
+ width: boundsWidth,
49
+ height: boundsHeight,
50
+ xAxis,
51
+ yAxes: yAxis,
52
+ yScale,
53
+ xScale,
54
+ dispatcher,
55
+ });
42
56
  React.useEffect(() => {
43
57
  if (clickHandler) {
44
58
  dispatcher.on(EventType.CLICK_CHART, clickHandler);
@@ -71,7 +85,7 @@ export const ChartInner = (props) => {
71
85
  React.createElement("g", { transform: `translate(0, ${boundsHeight})` },
72
86
  React.createElement(AxisX, { leftmostLimit: svgXPos, axis: xAxis, width: boundsWidth, height: boundsHeight, scale: xScale, split: preparedSplit, plotRef: plotRef })))),
73
87
  shapes),
74
- preparedLegend.enabled && (React.createElement(Legend, { chartSeries: preparedSeries, boundsWidth: boundsWidth, legend: preparedLegend, items: legendItems, config: legendConfig, onItemClick: handleLegendItemClick, onUpdate: unpinTooltip }))),
88
+ preparedLegend.enabled && (React.createElement(Legend, { chartSeries: preparedSeries, boundsWidth: boundsWidth, legend: preparedLegend, items: legendItems, config: legendConfig, onItemClick: handleLegendItemClick, onUpdate: unpinTooltip, htmlLayout: htmlLayerRef.current }))),
75
89
  React.createElement("div", { className: b('html-layer'), ref: htmlLayerRef, style: {
76
90
  '--g-html-layout-transform': `translate(${boundsOffsetLeft}px, ${boundsOffsetTop}px)`,
77
91
  } }),
@@ -20,8 +20,10 @@ export declare function useChartInnerProps(props: Props): {
20
20
  top: number;
21
21
  };
22
22
  pagination: {
23
- limit: number;
24
- maxPage: number;
23
+ pages: {
24
+ start: number;
25
+ end: number;
26
+ }[];
25
27
  } | undefined;
26
28
  };
27
29
  legendItems: import("../../hooks").LegendItem[][];
@@ -56,9 +56,12 @@ export function useChartInnerProps(props) {
56
56
  htmlLayout,
57
57
  });
58
58
  const boundsOffsetTop = chart.margin.top;
59
- // We need to calculate the width of each axis because the first axis can be hidden
59
+ // We need to calculate the width of each left axis because the first axis can be hidden
60
60
  const boundsOffsetLeft = chart.margin.left +
61
61
  yAxis.reduce((acc, axis) => {
62
+ if (axis.position !== 'left') {
63
+ return acc;
64
+ }
62
65
  const axisWidth = getYAxisWidth(axis);
63
66
  if (acc < axisWidth) {
64
67
  acc = axisWidth;
@@ -7,6 +7,7 @@ type Props = {
7
7
  legend: PreparedLegend;
8
8
  items: LegendItem[][];
9
9
  config: LegendConfig;
10
+ htmlLayout: HTMLElement | null;
10
11
  onItemClick: OnLegendItemClick;
11
12
  onUpdate?: () => void;
12
13
  };
@@ -2,7 +2,7 @@ import React from 'react';
2
2
  import { line as lineGenerator, scaleLinear, select, symbol } from 'd3';
3
3
  import { CONTINUOUS_LEGEND_SIZE } from '../../constants';
4
4
  import { formatNumber } from '../../libs';
5
- import { block, createGradientRect, getContinuesColorFn, getLabelsSize, getLineDashArray, getSymbol, } from '../../utils';
5
+ import { block, createGradientRect, getContinuesColorFn, getLabelsSize, getLineDashArray, getSymbol, handleOverflowingText, } from '../../utils';
6
6
  import { axisBottom } from '../../utils/chart/axis-generators';
7
7
  import './styles.css';
8
8
  const b = block('legend');
@@ -18,27 +18,28 @@ const getLegendPosition = (args) => {
18
18
  return { top, left: offsetWidth + width / 2 - contentWidth / 2 };
19
19
  };
20
20
  const appendPaginator = (args) => {
21
- const { container, offset, maxPage, legend, transform, onArrowClick } = args;
21
+ const { container, pageIndex, legend, transform, pages, onArrowClick } = args;
22
22
  const paginationLine = container.append('g').attr('class', b('pagination'));
23
+ const maxPage = pages.length;
23
24
  let computedWidth = 0;
24
25
  paginationLine
25
26
  .append('text')
26
27
  .text('▲')
27
28
  .attr('class', function () {
28
- return b('pagination-arrow', { inactive: offset === 0 });
29
+ return b('pagination-arrow', { inactive: pageIndex === 0 });
29
30
  })
30
31
  .style('font-size', legend.itemStyle.fontSize)
31
32
  .each(function () {
32
33
  computedWidth += this.getComputedTextLength();
33
34
  })
34
35
  .on('click', function () {
35
- if (offset - 1 >= 0) {
36
- onArrowClick(offset - 1);
36
+ if (pageIndex - 1 >= 0) {
37
+ onArrowClick(pageIndex - 1);
37
38
  }
38
39
  });
39
40
  paginationLine
40
41
  .append('text')
41
- .text(`${offset + 1}/${maxPage}`)
42
+ .text(`${pageIndex + 1}/${maxPage}`)
42
43
  .attr('class', b('pagination-counter'))
43
44
  .attr('x', computedWidth)
44
45
  .style('font-size', legend.itemStyle.fontSize)
@@ -49,13 +50,13 @@ const appendPaginator = (args) => {
49
50
  .append('text')
50
51
  .text('▼')
51
52
  .attr('class', function () {
52
- return b('pagination-arrow', { inactive: offset === maxPage - 1 });
53
+ return b('pagination-arrow', { inactive: pageIndex === maxPage - 1 });
53
54
  })
54
55
  .attr('x', computedWidth)
55
56
  .style('font-size', legend.itemStyle.fontSize)
56
57
  .on('click', function () {
57
- if (offset + 1 < maxPage) {
58
- onArrowClick(offset + 1);
58
+ if (pageIndex + 1 < maxPage) {
59
+ onArrowClick(pageIndex + 1);
59
60
  }
60
61
  });
61
62
  paginationLine.attr('transform', transform);
@@ -64,7 +65,7 @@ const legendSymbolGenerator = lineGenerator()
64
65
  .x((d) => d.x)
65
66
  .y((d) => d.y);
66
67
  function renderLegendSymbol(args) {
67
- const { selection, legend } = args;
68
+ const { selection, legend, legendLineHeight } = args;
68
69
  const line = selection.data();
69
70
  const getXPosition = (i) => {
70
71
  return line.slice(0, i).reduce((acc, legendItem) => {
@@ -82,7 +83,7 @@ function renderLegendSymbol(args) {
82
83
  const color = d.visible ? d.color : '';
83
84
  switch (d.symbol.shape) {
84
85
  case 'path': {
85
- const y = legend.lineHeight / 2;
86
+ const y = legendLineHeight / 2;
86
87
  const points = [
87
88
  { x: x, y },
88
89
  { x: x + d.symbol.width, y },
@@ -100,7 +101,7 @@ function renderLegendSymbol(args) {
100
101
  break;
101
102
  }
102
103
  case 'rect': {
103
- const y = (legend.lineHeight - d.symbol.height) / 2;
104
+ const y = (legendLineHeight - d.symbol.height) / 2;
104
105
  element
105
106
  .append('rect')
106
107
  .attr('x', x)
@@ -113,7 +114,7 @@ function renderLegendSymbol(args) {
113
114
  break;
114
115
  }
115
116
  case 'symbol': {
116
- const y = legend.lineHeight / 2;
117
+ const y = legendLineHeight / 2;
117
118
  const translateX = x + d.symbol.width / 2;
118
119
  element
119
120
  .append('svg:path')
@@ -134,28 +135,36 @@ function renderLegendSymbol(args) {
134
135
  });
135
136
  }
136
137
  export const Legend = (props) => {
137
- const { boundsWidth, chartSeries, legend, items, config, onItemClick, onUpdate } = props;
138
+ const { boundsWidth, chartSeries, legend, items, config, htmlLayout, onItemClick, onUpdate } = props;
138
139
  const ref = React.useRef(null);
139
- const [paginationOffset, setPaginationOffset] = React.useState(0);
140
+ const [pageIndex, setPageIndex] = React.useState(0);
140
141
  React.useEffect(() => {
141
- setPaginationOffset(0);
142
+ setPageIndex(0);
142
143
  }, [boundsWidth]);
143
144
  React.useEffect(() => {
144
- var _a, _b, _c, _d, _e;
145
- if (!ref.current) {
145
+ var _a, _b, _c, _d, _e, _f, _g, _h;
146
+ if (!ref.current || !htmlLayout) {
146
147
  return;
147
148
  }
148
149
  const svgElement = select(ref.current);
149
150
  svgElement.selectAll('*').remove();
151
+ const htmlElement = select(htmlLayout);
152
+ htmlElement.selectAll('[data-legend]').remove();
153
+ const htmlContainer = legend.html
154
+ ? htmlElement.append('div').attr('data-legend', 1).style('position', 'absolute')
155
+ : null;
150
156
  let legendWidth = 0;
151
157
  if (legend.type === 'discrete') {
152
- const limit = (_a = config.pagination) === null || _a === void 0 ? void 0 : _a.limit;
153
- const pageItems = typeof limit === 'number'
154
- ? items.slice(paginationOffset * limit, paginationOffset * limit + limit)
158
+ const start = (_b = (_a = config.pagination) === null || _a === void 0 ? void 0 : _a.pages[pageIndex]) === null || _b === void 0 ? void 0 : _b.start;
159
+ const end = (_d = (_c = config.pagination) === null || _c === void 0 ? void 0 : _c.pages[pageIndex]) === null || _d === void 0 ? void 0 : _d.end;
160
+ const pageItems = typeof start === 'number' && typeof end === 'number'
161
+ ? items.slice(start, end)
155
162
  : items;
156
- pageItems.forEach((line, lineIndex) => {
163
+ const legendLineHeights = [];
164
+ pageItems.forEach((line) => {
157
165
  var _a;
158
166
  const legendLine = svgElement.append('g').attr('class', b('line'));
167
+ const htmlLegendLine = htmlContainer === null || htmlContainer === void 0 ? void 0 : htmlContainer.append('div').style('position', 'absolute');
159
168
  const legendItemTemplate = legendLine
160
169
  .selectAll('legend-history')
161
170
  .data(line)
@@ -175,22 +184,65 @@ export const Legend = (props) => {
175
184
  legend.itemDistance);
176
185
  }, 0);
177
186
  };
178
- renderLegendSymbol({ selection: legendItemTemplate, legend });
179
- legendItemTemplate
180
- .append('text')
181
- .attr('x', function (legendItem, i) {
182
- return (getXPosition(i) + legendItem.symbol.width + legendItem.symbol.padding);
183
- })
184
- .attr('height', legend.lineHeight)
185
- .attr('class', function (d) {
186
- const mods = { selected: d.visible, unselected: !d.visible };
187
- return b('item-text', mods);
188
- })
189
- .html(function (d) {
190
- return ('name' in d && d.name);
191
- })
192
- .style('font-size', legend.itemStyle.fontSize);
193
- const contentWidth = ((_a = legendLine.node()) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect().width) || 0;
187
+ const legendLineHeight = Math.max(...line.map((l) => l.height));
188
+ renderLegendSymbol({ selection: legendItemTemplate, legend, legendLineHeight });
189
+ if (htmlLegendLine) {
190
+ htmlLegendLine
191
+ .selectAll('legend-item')
192
+ .data(line)
193
+ .enter()
194
+ .append('div')
195
+ .attr('class', function (d) {
196
+ const mods = { selected: d.visible, unselected: !d.visible };
197
+ return b('item-text-html', mods);
198
+ })
199
+ .style('font-size', legend.itemStyle.fontSize)
200
+ .style('position', 'absolute')
201
+ .style('max-width', function (d) {
202
+ return `${d.textWidth}px`;
203
+ })
204
+ .style('left', function (d, i) {
205
+ return `${getXPosition(i) + d.symbol.width + d.symbol.padding}px`;
206
+ })
207
+ .style('top', function (d) {
208
+ if (d.height < legendLineHeight) {
209
+ return `${(legendLineHeight - d.height) / 2}px`;
210
+ }
211
+ return '0px';
212
+ })
213
+ .on('click', function (e, d) {
214
+ onItemClick({ name: d.name, metaKey: e.metaKey });
215
+ onUpdate === null || onUpdate === void 0 ? void 0 : onUpdate();
216
+ })[legend.html ? 'html' : 'text'](function (d) {
217
+ return d.name;
218
+ });
219
+ }
220
+ else {
221
+ legendItemTemplate
222
+ .append('text')
223
+ .attr('x', function (legendItem, i) {
224
+ return (getXPosition(i) +
225
+ legendItem.symbol.width +
226
+ legendItem.symbol.padding);
227
+ })
228
+ .attr('height', legend.height)
229
+ .attr('class', function (d) {
230
+ const mods = { selected: d.visible, unselected: !d.visible };
231
+ return b('item-text', mods);
232
+ })
233
+ .html(function (d) {
234
+ return ('name' in d && d.name);
235
+ })
236
+ .style('font-size', legend.itemStyle.fontSize)
237
+ .each((d, index, nodes) => {
238
+ if (d.overflowed) {
239
+ handleOverflowingText(nodes[index], d.textWidth);
240
+ }
241
+ });
242
+ }
243
+ const contentWidth = (legend.html
244
+ ? getXPosition(line.length) - legend.itemDistance
245
+ : (_a = legendLine.node()) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect().width) || 0;
194
246
  let left = 0;
195
247
  switch (legend.justifyContent) {
196
248
  case 'center': {
@@ -209,27 +261,26 @@ export const Legend = (props) => {
209
261
  break;
210
262
  }
211
263
  }
212
- const top = legend.lineHeight * lineIndex;
264
+ const top = legendLineHeights.reduce((acc, h) => acc + h, 0);
265
+ legendLineHeights.push(legendLineHeight);
213
266
  legendLine.attr('transform', `translate(${[left, top].join(',')})`);
267
+ htmlLegendLine === null || htmlLegendLine === void 0 ? void 0 : htmlLegendLine.style('transform', `translate(${left}px, ${top}px)`);
214
268
  });
215
269
  if (config.pagination) {
216
- const transform = `translate(${[
217
- 0,
218
- legend.lineHeight * config.pagination.limit + legend.lineHeight / 2,
219
- ].join(',')})`;
270
+ const transform = `translate(${[0, legend.height - legend.lineHeight / 2].join(',')})`;
220
271
  appendPaginator({
221
272
  container: svgElement,
222
- offset: paginationOffset,
223
- maxPage: config.pagination.maxPage,
273
+ pageIndex: pageIndex,
224
274
  legend,
225
275
  transform,
226
- onArrowClick: setPaginationOffset,
276
+ pages: config.pagination.pages,
277
+ onArrowClick: setPageIndex,
227
278
  });
228
279
  }
229
280
  }
230
281
  else {
231
282
  // gradient rect
232
- const domain = (_b = legend.colorScale.domain) !== null && _b !== void 0 ? _b : [];
283
+ const domain = (_e = legend.colorScale.domain) !== null && _e !== void 0 ? _e : [];
233
284
  const rectHeight = CONTINUOUS_LEGEND_SIZE.height;
234
285
  svgElement.call(createGradientRect, {
235
286
  y: legend.title.height + legend.title.margin,
@@ -291,9 +342,9 @@ export const Legend = (props) => {
291
342
  .attr('class', b('title'))
292
343
  .append('text')
293
344
  .attr('dx', dx)
294
- .attr('font-weight', (_c = legend.title.style.fontWeight) !== null && _c !== void 0 ? _c : null)
295
- .attr('font-size', (_d = legend.title.style.fontSize) !== null && _d !== void 0 ? _d : null)
296
- .attr('fill', (_e = legend.title.style.fontColor) !== null && _e !== void 0 ? _e : null)
345
+ .attr('font-weight', (_f = legend.title.style.fontWeight) !== null && _f !== void 0 ? _f : null)
346
+ .attr('font-size', (_g = legend.title.style.fontSize) !== null && _g !== void 0 ? _g : null)
347
+ .attr('fill', (_h = legend.title.style.fontColor) !== null && _h !== void 0 ? _h : null)
297
348
  .style('dominant-baseline', 'text-before-edge')
298
349
  .html(legend.title.text);
299
350
  }
@@ -304,6 +355,17 @@ export const Legend = (props) => {
304
355
  contentWidth: legendWidth,
305
356
  });
306
357
  svgElement.attr('transform', `translate(${[left, config.offset.top].join(',')})`);
307
- }, [boundsWidth, chartSeries, onItemClick, onUpdate, legend, items, config, paginationOffset]);
358
+ htmlContainer === null || htmlContainer === void 0 ? void 0 : htmlContainer.style('transform', `translate(${left}px, ${config.offset.top}px)`);
359
+ }, [
360
+ boundsWidth,
361
+ chartSeries,
362
+ onItemClick,
363
+ onUpdate,
364
+ legend,
365
+ items,
366
+ config,
367
+ pageIndex,
368
+ htmlLayout,
369
+ ]);
308
370
  return React.createElement("g", { className: b(), ref: ref, width: boundsWidth, height: legend.height });
309
371
  };
@@ -27,6 +27,19 @@
27
27
  .gcharts-legend__item-text:hover {
28
28
  fill: var(--g-color-text-complementary);
29
29
  }
30
+ .gcharts-legend__item-text-html {
31
+ overflow: hidden;
32
+ cursor: pointer;
33
+ user-select: none;
34
+ white-space: nowrap;
35
+ text-overflow: ellipsis;
36
+ }
37
+ .gcharts-legend__item-text-html_unselected {
38
+ color: var(--g-color-text-hint);
39
+ }
40
+ .gcharts-legend__item-text-html:hover {
41
+ color: var(--g-color-text-complementary);
42
+ }
30
43
  .gcharts-legend__pagination {
31
44
  user-select: none;
32
45
  fill: var(--g-color-text-primary);
@@ -135,9 +135,12 @@ export const DefaultContent = ({ hovered, xAxis, yAxis, valueFormat }) => {
135
135
  });
136
136
  return (React.createElement("div", { key: id, className: b('content-row') },
137
137
  React.createElement("div", { className: b('color'), style: { backgroundColor: color } }),
138
- React.createElement("span", null,
139
- seriesData.name || seriesData.id,
140
- "\u00A0"),
138
+ React.createElement("span", { dangerouslySetInnerHTML: {
139
+ __html: [seriesData.name || seriesData.id]
140
+ .flat()
141
+ .join('\n'),
142
+ } }),
143
+ "\u00A0",
141
144
  React.createElement("span", null, formattedValue)));
142
145
  }
143
146
  case 'sankey': {
@@ -0,0 +1,12 @@
1
+ export declare const SeriesType: {
2
+ readonly Area: "area";
3
+ readonly BarX: "bar-x";
4
+ readonly BarY: "bar-y";
5
+ readonly Line: "line";
6
+ readonly Pie: "pie";
7
+ readonly Scatter: "scatter";
8
+ readonly Treemap: "treemap";
9
+ readonly Waterfall: "waterfall";
10
+ readonly Sankey: "sankey";
11
+ readonly Radar: "radar";
12
+ };
@@ -0,0 +1,12 @@
1
+ export const SeriesType = {
2
+ Area: 'area',
3
+ BarX: 'bar-x',
4
+ BarY: 'bar-y',
5
+ Line: 'line',
6
+ Pie: 'pie',
7
+ Scatter: 'scatter',
8
+ Treemap: 'treemap',
9
+ Waterfall: 'waterfall',
10
+ Sankey: 'sankey',
11
+ Radar: 'radar',
12
+ };
@@ -1,4 +1,4 @@
1
- import type { BaseTextStyle, ChartAxis, ChartAxisType } from '../../types';
1
+ import type { AxisCrosshair, BaseTextStyle, ChartAxis, ChartAxisType } from '../../types';
2
2
  export declare const axisLabelsDefaults: {
3
3
  margin: number;
4
4
  padding: number;
@@ -8,6 +8,8 @@ export declare const axisLabelsDefaults: {
8
8
  type AxisTitleDefaults = Required<ChartAxis['title']> & {
9
9
  style: BaseTextStyle;
10
10
  };
11
+ type AxisCrosshairDefaults = Required<AxisCrosshair>;
12
+ export declare const axisCrosshairDefaults: AxisCrosshairDefaults;
11
13
  export declare const xAxisTitleDefaults: AxisTitleDefaults;
12
14
  export declare const yAxisTitleDefaults: AxisTitleDefaults;
13
15
  export declare const DEFAULT_AXIS_TYPE: ChartAxisType;
@@ -1,3 +1,4 @@
1
+ import { DASH_STYLE } from '../line-styles';
1
2
  export const axisLabelsDefaults = {
2
3
  margin: 10,
3
4
  padding: 10,
@@ -13,6 +14,15 @@ const axisTitleDefaults = {
13
14
  align: 'center',
14
15
  maxRowCount: 1,
15
16
  };
17
+ export const axisCrosshairDefaults = {
18
+ enabled: false,
19
+ color: 'var(--g-color-line-generic)',
20
+ width: 1,
21
+ snap: true,
22
+ dashStyle: DASH_STYLE.Solid,
23
+ opacity: 1,
24
+ layerPlacement: 'after',
25
+ };
16
26
  export const xAxisTitleDefaults = Object.assign(Object.assign({}, axisTitleDefaults), { margin: 4 });
17
27
  export const yAxisTitleDefaults = Object.assign(Object.assign({}, axisTitleDefaults), { margin: 8 });
18
28
  export const DEFAULT_AXIS_TYPE = 'linear';