@gravity-ui/chartkit 4.4.1 → 4.6.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 (61) hide show
  1. package/build/plugins/d3/renderer/components/AxisX.d.ts +2 -1
  2. package/build/plugins/d3/renderer/components/AxisX.js +51 -49
  3. package/build/plugins/d3/renderer/components/AxisY.js +28 -31
  4. package/build/plugins/d3/renderer/components/Chart.js +19 -7
  5. package/build/plugins/d3/renderer/components/Legend.d.ts +5 -6
  6. package/build/plugins/d3/renderer/components/Legend.js +139 -84
  7. package/build/plugins/d3/renderer/components/styles.css +27 -0
  8. package/build/plugins/d3/renderer/constants/defaults/axis.d.ts +5 -0
  9. package/build/plugins/d3/renderer/constants/defaults/axis.js +5 -0
  10. package/build/plugins/d3/renderer/constants/defaults/index.d.ts +2 -0
  11. package/build/plugins/d3/renderer/constants/defaults/index.js +2 -0
  12. package/build/plugins/d3/renderer/constants/defaults/legend.d.ts +4 -0
  13. package/build/plugins/d3/renderer/constants/defaults/legend.js +8 -0
  14. package/build/plugins/d3/renderer/{constants.d.ts → constants/index.d.ts} +1 -1
  15. package/build/plugins/d3/renderer/{constants.js → constants/index.js} +1 -1
  16. package/build/plugins/d3/renderer/hooks/useAxisScales/index.d.ts +3 -1
  17. package/build/plugins/d3/renderer/hooks/useAxisScales/index.js +64 -59
  18. package/build/plugins/d3/renderer/hooks/useChartDimensions/index.d.ts +7 -4
  19. package/build/plugins/d3/renderer/hooks/useChartDimensions/index.js +65 -7
  20. package/build/plugins/d3/renderer/hooks/useChartDimensions/utils.d.ts +6 -0
  21. package/build/plugins/d3/renderer/hooks/useChartDimensions/utils.js +7 -0
  22. package/build/plugins/d3/renderer/hooks/useChartOptions/chart.d.ts +1 -3
  23. package/build/plugins/d3/renderer/hooks/useChartOptions/chart.js +12 -75
  24. package/build/plugins/d3/renderer/hooks/useChartOptions/index.d.ts +3 -1
  25. package/build/plugins/d3/renderer/hooks/useChartOptions/index.js +3 -8
  26. package/build/plugins/d3/renderer/hooks/useChartOptions/types.d.ts +3 -6
  27. package/build/plugins/d3/renderer/hooks/useChartOptions/x-axis.js +4 -2
  28. package/build/plugins/d3/renderer/hooks/useChartOptions/y-axis.d.ts +3 -2
  29. package/build/plugins/d3/renderer/hooks/useChartOptions/y-axis.js +31 -4
  30. package/build/plugins/d3/renderer/hooks/useSeries/constants.d.ts +1 -1
  31. package/build/plugins/d3/renderer/hooks/useSeries/constants.js +1 -1
  32. package/build/plugins/d3/renderer/hooks/useSeries/index.d.ts +19 -7
  33. package/build/plugins/d3/renderer/hooks/useSeries/index.js +26 -8
  34. package/build/plugins/d3/renderer/hooks/useSeries/prepare-legend.d.ts +27 -0
  35. package/build/plugins/d3/renderer/hooks/useSeries/prepare-legend.js +92 -0
  36. package/build/plugins/d3/renderer/hooks/useSeries/prepareSeries.d.ts +1 -2
  37. package/build/plugins/d3/renderer/hooks/useSeries/types.d.ts +26 -1
  38. package/build/plugins/d3/renderer/utils/axis-generators/bottom.d.ts +21 -0
  39. package/build/plugins/d3/renderer/utils/axis-generators/bottom.js +75 -0
  40. package/build/plugins/d3/renderer/utils/axis-generators/index.d.ts +1 -0
  41. package/build/plugins/d3/renderer/utils/axis-generators/index.js +1 -0
  42. package/build/plugins/d3/renderer/utils/axis.d.ts +22 -0
  43. package/build/plugins/d3/renderer/utils/axis.js +49 -0
  44. package/build/plugins/d3/renderer/utils/index.d.ts +8 -4
  45. package/build/plugins/d3/renderer/utils/index.js +17 -6
  46. package/build/plugins/d3/renderer/utils/text.d.ts +20 -0
  47. package/build/plugins/d3/renderer/utils/text.js +62 -0
  48. package/build/plugins/d3/renderer/utils/time.d.ts +3 -0
  49. package/build/plugins/d3/renderer/utils/time.js +34 -0
  50. package/build/plugins/highcharts/renderer/components/HighchartsComponent.js +3 -3
  51. package/build/plugins/shared/format-number/format-number.d.ts +1 -0
  52. package/build/plugins/shared/format-number/format-number.js +19 -20
  53. package/build/types/widget-data/axis.d.ts +13 -1
  54. package/build/types/widget-data/legend.d.ts +24 -7
  55. package/build/utils/common.d.ts +1 -0
  56. package/build/utils/common.js +1 -1
  57. package/build/utils/index.d.ts +1 -1
  58. package/build/utils/index.js +1 -1
  59. package/package.json +2 -2
  60. package/build/plugins/d3/renderer/hooks/useChartOptions/legend.d.ts +0 -6
  61. package/build/plugins/d3/renderer/hooks/useChartOptions/legend.js +0 -12
@@ -5,6 +5,7 @@ type Props = {
5
5
  width: number;
6
6
  height: number;
7
7
  scale: ChartScale;
8
+ chartWidth: number;
8
9
  };
9
- export declare const AxisX: ({ axis, width, height, scale }: Props) => React.JSX.Element;
10
+ export declare const AxisX: React.MemoExoticComponent<({ axis, width, height, scale, chartWidth }: Props) => React.JSX.Element>;
10
11
  export {};
@@ -1,11 +1,24 @@
1
1
  import React from 'react';
2
- import { axisBottom, select } from 'd3';
2
+ import { select } from 'd3';
3
3
  import { block } from '../../../../utils/cn';
4
- import { formatAxisTickLabel, parseTransformStyle } from '../utils';
4
+ import { formatAxisTickLabel, getClosestPointsRange, setEllipsisForOverflowText, getTicksCount, getScaleTicks, getMaxTickCount, } from '../utils';
5
+ import { axisBottom } from '../utils/axis-generators';
5
6
  const b = block('d3-axis');
6
- const EMPTY_SPACE_BETWEEN_LABELS = 10;
7
- // FIXME: add overflow ellipsis for the labels that out of boundaries
8
- export const AxisX = ({ axis, width, height, scale }) => {
7
+ function getLabelFormatter({ axis, scale }) {
8
+ const ticks = getScaleTicks(scale);
9
+ const tickStep = getClosestPointsRange(axis, ticks);
10
+ return (value) => {
11
+ if (!axis.labels.enabled) {
12
+ return '';
13
+ }
14
+ return formatAxisTickLabel({
15
+ axis,
16
+ value,
17
+ step: tickStep,
18
+ });
19
+ };
20
+ }
21
+ export const AxisX = React.memo(({ axis, width, height, scale, chartWidth }) => {
9
22
  const ref = React.useRef(null);
10
23
  React.useEffect(() => {
11
24
  if (!ref.current) {
@@ -13,39 +26,40 @@ export const AxisX = ({ axis, width, height, scale }) => {
13
26
  }
14
27
  const svgElement = select(ref.current);
15
28
  svgElement.selectAll('*').remove();
16
- const tickSize = axis.grid.enabled ? height * -1 : 0;
17
- let xAxisGenerator = axisBottom(scale)
18
- .tickSize(tickSize)
19
- .tickPadding(axis.labels.padding)
20
- .tickFormat((value) => {
21
- if (!axis.labels.enabled) {
22
- return '';
23
- }
24
- return formatAxisTickLabel({
25
- axisType: axis.type,
26
- value,
27
- dateFormat: axis.labels['dateFormat'],
28
- numberFormat: axis.labels['numberFormat'],
29
- });
29
+ const xAxisGenerator = axisBottom({
30
+ scale: scale,
31
+ ticks: {
32
+ size: axis.grid.enabled ? height * -1 : 0,
33
+ labelFormat: getLabelFormatter({ axis, scale }),
34
+ labelsPaddings: axis.labels.padding,
35
+ labelsMargin: axis.labels.margin,
36
+ labelsStyle: axis.labels.style,
37
+ count: getTicksCount({ axis, range: width }),
38
+ maxTickCount: getMaxTickCount({ axis, width }),
39
+ autoRotation: axis.labels.autoRotation,
40
+ },
41
+ domain: {
42
+ size: width,
43
+ color: axis.lineColor,
44
+ },
30
45
  });
31
- if (axis.ticks.pixelInterval) {
32
- const ticksCount = width / axis.ticks.pixelInterval;
33
- xAxisGenerator = xAxisGenerator.ticks(ticksCount);
34
- }
35
46
  svgElement.call(xAxisGenerator).attr('class', b());
36
- svgElement
37
- .select('.domain')
38
- .attr('d', `M0,0V0H${width}`)
39
- .style('stroke', axis.lineColor || '');
40
47
  if (axis.labels.enabled) {
41
- svgElement.selectAll('.tick text').style('font-size', axis.labels.style.fontSize);
42
- }
43
- const transformStyle = svgElement.select('.tick').attr('transform');
44
- const { x } = parseTransformStyle(transformStyle);
45
- if (x === 0) {
46
- // Remove tick that has the same x coordinate like domain
47
- svgElement.select('.tick').remove();
48
+ svgElement.style('font-size', axis.labels.style.fontSize);
48
49
  }
50
+ // add an ellipsis to the labels on the right that go beyond the boundaries of the chart
51
+ svgElement.selectAll('.tick text').each(function () {
52
+ var _a;
53
+ const node = this;
54
+ const textRect = node.getBBox();
55
+ const matrix = ((_a = node.transform.baseVal.consolidate()) === null || _a === void 0 ? void 0 : _a.matrix) || {};
56
+ const right = matrix.a * textRect.right + matrix.c * textRect.bottom + matrix.e;
57
+ if (right > chartWidth) {
58
+ const maxWidth = textRect.width - (right - chartWidth) * 2;
59
+ select(node).call(setEllipsisForOverflowText, maxWidth);
60
+ }
61
+ });
62
+ // add an axis header if necessary
49
63
  if (axis.title.text) {
50
64
  const textY = axis.title.height + parseInt(axis.labels.style.fontSize) + axis.labels.padding;
51
65
  svgElement
@@ -55,21 +69,9 @@ export const AxisX = ({ axis, width, height, scale }) => {
55
69
  .attr('x', width / 2)
56
70
  .attr('y', textY)
57
71
  .attr('font-size', axis.title.style.fontSize)
58
- .text(axis.title.text);
72
+ .text(axis.title.text)
73
+ .call(setEllipsisForOverflowText, width);
59
74
  }
60
- let elementX = 0;
61
- svgElement
62
- .selectAll('.tick')
63
- .filter(function () {
64
- const node = this;
65
- const r = node.getBoundingClientRect();
66
- if (r.left < elementX) {
67
- return true;
68
- }
69
- elementX = r.right + EMPTY_SPACE_BETWEEN_LABELS;
70
- return false;
71
- })
72
- .remove();
73
75
  }, [axis, width, height, scale]);
74
76
  return React.createElement("g", { ref: ref });
75
- };
77
+ });
@@ -1,28 +1,9 @@
1
1
  import React from 'react';
2
2
  import { axisLeft, select } from 'd3';
3
3
  import { block } from '../../../../utils/cn';
4
- import { formatAxisTickLabel, parseTransformStyle } from '../utils';
4
+ import { formatAxisTickLabel, getClosestPointsRange, parseTransformStyle, setEllipsisForOverflowText, setEllipsisForOverflowTexts, getTicksCount, getScaleTicks, } from '../utils';
5
5
  const b = block('d3-axis');
6
- const EMPTY_SPACE_BETWEEN_LABELS = 10;
7
- // Note: this method do not prepared for rotated labels
8
- const removeOverlappingYTicks = (axis) => {
9
- var _a;
10
- const a = axis.selectAll('g.tick').nodes();
11
- if (a.length <= 1) {
12
- return;
13
- }
14
- for (let i = 0, x = 0; i < a.length; i++) {
15
- const node = a[i];
16
- const r = node.getBoundingClientRect();
17
- if (r.bottom > x && i !== 0) {
18
- (_a = node === null || node === void 0 ? void 0 : node.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(node);
19
- }
20
- else {
21
- x = r.top - EMPTY_SPACE_BETWEEN_LABELS;
22
- }
23
- }
24
- };
25
- // FIXME: add overflow ellipsis for the labels that out of boundaries
6
+ const MAX_WIDTH = 80;
26
7
  export const AxisY = ({ axises, width, height, scale }) => {
27
8
  const ref = React.useRef(null);
28
9
  React.useEffect(() => {
@@ -33,22 +14,22 @@ export const AxisY = ({ axises, width, height, scale }) => {
33
14
  const svgElement = select(ref.current);
34
15
  svgElement.selectAll('*').remove();
35
16
  const tickSize = axis.grid.enabled ? width * -1 : 0;
17
+ const step = getClosestPointsRange(axis, getScaleTicks(scale));
36
18
  let yAxisGenerator = axisLeft(scale)
37
19
  .tickSize(tickSize)
38
- .tickPadding(axis.labels.padding)
20
+ .tickPadding(axis.labels.margin)
39
21
  .tickFormat((value) => {
40
22
  if (!axis.labels.enabled) {
41
23
  return '';
42
24
  }
43
25
  return formatAxisTickLabel({
44
- axisType: axis.type,
26
+ axis,
45
27
  value,
46
- dateFormat: axis.labels['dateFormat'],
47
- numberFormat: axis.labels['numberFormat'],
28
+ step,
48
29
  });
49
30
  });
50
- if (axis.ticks.pixelInterval) {
51
- const ticksCount = height / axis.ticks.pixelInterval;
31
+ const ticksCount = getTicksCount({ axis, range: height });
32
+ if (ticksCount) {
52
33
  yAxisGenerator = yAxisGenerator.ticks(ticksCount);
53
34
  }
54
35
  svgElement.call(yAxisGenerator).attr('class', b());
@@ -57,10 +38,11 @@ export const AxisY = ({ axises, width, height, scale }) => {
57
38
  .attr('d', `M0,${height}H0V0`)
58
39
  .style('stroke', axis.lineColor || '');
59
40
  if (axis.labels.enabled) {
60
- svgElement
41
+ const tickTexts = svgElement
61
42
  .selectAll('.tick text')
62
43
  .style('font-size', axis.labels.style.fontSize)
63
44
  .style('transform', 'translateY(-1px)');
45
+ tickTexts.call(setEllipsisForOverflowTexts, MAX_WIDTH);
64
46
  }
65
47
  const transformStyle = svgElement.select('.tick').attr('transform');
66
48
  const { y } = parseTransformStyle(transformStyle);
@@ -68,8 +50,23 @@ export const AxisY = ({ axises, width, height, scale }) => {
68
50
  // Remove stroke from tick that has the same y coordinate like domain
69
51
  svgElement.select('.tick line').style('stroke', 'none');
70
52
  }
53
+ // remove overlapping ticks
54
+ // Note: this method do not prepared for rotated labels
55
+ let elementY = 0;
56
+ svgElement
57
+ .selectAll('.tick')
58
+ .filter(function (_d, index) {
59
+ const node = this;
60
+ const r = node.getBoundingClientRect();
61
+ if (r.bottom > elementY && index !== 0) {
62
+ return true;
63
+ }
64
+ elementY = r.top - axis.labels.padding;
65
+ return false;
66
+ })
67
+ .remove();
71
68
  if (axis.title.text) {
72
- const textY = axis.title.height + axis.labels.padding;
69
+ const textY = axis.title.height + axis.labels.margin;
73
70
  svgElement
74
71
  .append('text')
75
72
  .attr('class', b('title'))
@@ -78,9 +75,9 @@ export const AxisY = ({ axises, width, height, scale }) => {
78
75
  .attr('dx', -height / 2)
79
76
  .attr('font-size', axis.title.style.fontSize)
80
77
  .attr('transform', 'rotate(-90)')
81
- .text(axis.title.text);
78
+ .text(axis.title.text)
79
+ .call(setEllipsisForOverflowText, height);
82
80
  }
83
- removeOverlappingYTicks(svgElement);
84
81
  }, [axises, width, height, scale]);
85
82
  return React.createElement("g", { ref: ref });
86
83
  };
@@ -1,26 +1,38 @@
1
1
  import React from 'react';
2
2
  import { block } from '../../../../utils/cn';
3
+ import { useAxisScales, useChartDimensions, useChartEvents, useChartOptions, useSeries, useShapes, useTooltip, } from '../hooks';
3
4
  import { AxisY } from './AxisY';
4
5
  import { AxisX } from './AxisX';
5
6
  import { Legend } from './Legend';
6
7
  import { Title } from './Title';
7
8
  import { Tooltip } from './Tooltip';
8
- import { useChartDimensions, useChartEvents, useChartOptions, useAxisScales, useSeries, useShapes, useTooltip, } from '../hooks';
9
9
  import './styles.css';
10
10
  const b = block('d3');
11
11
  export const Chart = (props) => {
12
- const { top, left, width, height, data } = props;
13
12
  // FIXME: add data validation
13
+ const { top, left, width, height, data } = props;
14
14
  const svgRef = React.createRef();
15
15
  const { chartHovered, handleMouseEnter, handleMouseLeave } = useChartEvents();
16
- const { chart, legend, title, tooltip, xAxis, yAxis } = useChartOptions(data);
16
+ const { chart, title, tooltip, xAxis, yAxis } = useChartOptions({
17
+ data,
18
+ });
19
+ const { legendItems, legendConfig, preparedSeries, preparedLegend, handleLegendItemClick } = useSeries({
20
+ chartWidth: width,
21
+ chartHeight: height,
22
+ chartMargin: chart.margin,
23
+ series: data.series,
24
+ legend: data.legend,
25
+ preparedYAxis: yAxis,
26
+ });
17
27
  const { boundsWidth, boundsHeight } = useChartDimensions({
18
28
  width,
19
29
  height,
20
30
  margin: chart.margin,
21
- yAxis,
31
+ preparedLegend,
32
+ preparedXAxis: xAxis,
33
+ preparedYAxis: yAxis,
34
+ preparedSeries: preparedSeries,
22
35
  });
23
- const { preparedSeries, handleLegendItemClick } = useSeries({ series: data.series, legend });
24
36
  const { xScale, yScale } = useAxisScales({
25
37
  boundsWidth,
26
38
  boundsHeight,
@@ -53,8 +65,8 @@ export const Chart = (props) => {
53
65
  xScale && yScale && (React.createElement(React.Fragment, null,
54
66
  React.createElement(AxisY, { axises: yAxis, width: boundsWidth, height: boundsHeight, scale: yScale }),
55
67
  React.createElement("g", { transform: `translate(0, ${boundsHeight})` },
56
- React.createElement(AxisX, { axis: xAxis, width: boundsWidth, height: boundsHeight, scale: xScale })))),
68
+ React.createElement(AxisX, { axis: xAxis, width: boundsWidth, height: boundsHeight, scale: xScale, chartWidth: width })))),
57
69
  shapes),
58
- legend.enabled && (React.createElement(Legend, { width: boundsWidth, offsetWidth: chart.margin.left, height: legend.height, legend: legend, offsetHeight: height - legend.height / 2, chartSeries: preparedSeries, onItemClick: handleLegendItemClick }))),
70
+ preparedLegend.enabled && (React.createElement(Legend, { chartSeries: preparedSeries, boundsWidth: boundsWidth, legend: preparedLegend, items: legendItems, config: legendConfig, onItemClick: handleLegendItemClick }))),
59
71
  React.createElement(Tooltip, { hovered: hovered, pointerPosition: pointerPosition, tooltip: tooltip, xAxis: xAxis, yAxis: yAxis[0] })));
60
72
  };
@@ -1,12 +1,11 @@
1
1
  import React from 'react';
2
- import type { OnLegendItemClick, PreparedLegend, PreparedSeries } from '../hooks';
2
+ import type { OnLegendItemClick, PreparedLegend, PreparedSeries, LegendItem, LegendConfig } from '../hooks';
3
3
  type Props = {
4
- width: number;
5
- height: number;
6
- legend: PreparedLegend;
7
- offsetWidth: number;
8
- offsetHeight: number;
4
+ boundsWidth: number;
9
5
  chartSeries: PreparedSeries[];
6
+ legend: PreparedLegend;
7
+ items: LegendItem[][];
8
+ config: LegendConfig;
10
9
  onItemClick: OnLegendItemClick;
11
10
  };
12
11
  export declare const Legend: (props: Props) => React.JSX.Element;
@@ -1,18 +1,8 @@
1
1
  import React from 'react';
2
- import { select, sum } from 'd3';
3
- import get from 'lodash/get';
2
+ import { select } from 'd3';
4
3
  import { block } from '../../../../utils/cn';
5
4
  const b = block('d3-legend');
6
- const getLegendItems = (series) => {
7
- return series.reduce((acc, s) => {
8
- const legendEnabled = get(s, 'legend.enabled', true);
9
- if (legendEnabled) {
10
- acc.push(Object.assign(Object.assign({}, s), { symbol: s.legend.symbol }));
11
- }
12
- return acc;
13
- }, []);
14
- };
15
- function getLegendPosition(args) {
5
+ const getLegendPosition = (args) => {
16
6
  const { align, offsetWidth, width, contentWidth } = args;
17
7
  const top = 0;
18
8
  if (align === 'left') {
@@ -22,85 +12,150 @@ function getLegendPosition(args) {
22
12
  return { top, left: offsetWidth + width - contentWidth };
23
13
  }
24
14
  return { top, left: offsetWidth + width / 2 - contentWidth / 2 };
25
- }
15
+ };
16
+ const appendPaginator = (args) => {
17
+ const { container, offset, maxPage, legend, transform, onArrowClick } = args;
18
+ const paginationLine = container.append('g').attr('class', b('pagination'));
19
+ let computedWidth = 0;
20
+ paginationLine
21
+ .append('text')
22
+ .text('▲')
23
+ .attr('class', function () {
24
+ return b('pagination-arrow', { inactive: offset === 0 });
25
+ })
26
+ .style('font-size', legend.itemStyle.fontSize)
27
+ .each(function () {
28
+ computedWidth += this.getComputedTextLength();
29
+ })
30
+ .on('click', function () {
31
+ if (offset - 1 >= 0) {
32
+ onArrowClick(offset - 1);
33
+ }
34
+ });
35
+ paginationLine
36
+ .append('text')
37
+ .text(`${offset + 1}/${maxPage}`)
38
+ .attr('class', b('pagination-counter'))
39
+ .attr('x', computedWidth)
40
+ .style('font-size', legend.itemStyle.fontSize)
41
+ .each(function () {
42
+ computedWidth += this.getComputedTextLength();
43
+ });
44
+ paginationLine
45
+ .append('text')
46
+ .text('▼')
47
+ .attr('class', function () {
48
+ return b('pagination-arrow', { inactive: offset === maxPage - 1 });
49
+ })
50
+ .attr('x', computedWidth)
51
+ .style('font-size', legend.itemStyle.fontSize)
52
+ .on('click', function () {
53
+ if (offset + 1 < maxPage) {
54
+ onArrowClick(offset + 1);
55
+ }
56
+ });
57
+ paginationLine.attr('transform', transform);
58
+ };
26
59
  export const Legend = (props) => {
27
- const { width, offsetWidth, height, offsetHeight, chartSeries, legend, onItemClick } = props;
60
+ const { boundsWidth, chartSeries, legend, items, config, onItemClick } = props;
28
61
  const ref = React.useRef(null);
62
+ const [paginationOffset, setPaginationOffset] = React.useState(0);
63
+ React.useEffect(() => {
64
+ setPaginationOffset(0);
65
+ }, [boundsWidth]);
29
66
  React.useEffect(() => {
67
+ var _a;
30
68
  if (!ref.current) {
31
69
  return;
32
70
  }
33
- const legendItems = getLegendItems(chartSeries);
34
- const textWidths = [0];
35
71
  const svgElement = select(ref.current);
36
72
  svgElement.selectAll('*').remove();
37
- const legendItemTemplate = svgElement
38
- .selectAll('legend-history')
39
- .data(legendItems)
40
- .enter()
41
- .append('g')
42
- .attr('class', b('item'))
43
- .on('click', function (e, d) {
44
- onItemClick({ name: d.name, metaKey: e.metaKey });
45
- });
46
- svgElement
47
- .selectAll('*')
48
- .data(legendItems)
49
- .append('text')
50
- .text(function (d) {
51
- return d.name;
52
- })
53
- .each(function () {
54
- textWidths.push(this.getComputedTextLength());
55
- })
56
- .remove();
57
- legendItemTemplate
58
- .append('rect')
59
- .attr('x', function (legendItem, i) {
60
- return (i * legendItem.symbol.width +
61
- i * legend.itemDistance +
62
- i * legendItem.symbol.padding +
63
- textWidths.slice(0, i + 1).reduce((acc, tw) => acc + tw, 0));
64
- })
65
- .attr('y', (legendItem) => offsetHeight - legendItem.symbol.height / 2)
66
- .attr('width', (legendItem) => {
67
- return legendItem.symbol.width;
68
- })
69
- .attr('height', (legendItem) => legendItem.symbol.height)
70
- .attr('rx', (legendItem) => legendItem.symbol.radius)
71
- .attr('class', b('item-shape'))
72
- .style('fill', function (d) {
73
- return d.color;
73
+ const limit = (_a = config.pagination) === null || _a === void 0 ? void 0 : _a.limit;
74
+ const pageItems = typeof limit === 'number'
75
+ ? items.slice(paginationOffset * limit, paginationOffset * limit + limit)
76
+ : items;
77
+ pageItems.forEach((line, lineIndex) => {
78
+ var _a;
79
+ const textWidths = [];
80
+ const legendLine = svgElement.append('g').attr('class', b('line'));
81
+ const legendItemTemplate = legendLine
82
+ .selectAll('legend-history')
83
+ .data(line)
84
+ .enter()
85
+ .append('g')
86
+ .attr('class', b('item'))
87
+ .on('click', function (e, d) {
88
+ onItemClick({ name: d.name, metaKey: e.metaKey });
89
+ })
90
+ .each(function (d) {
91
+ textWidths.push(d.textWidth);
92
+ });
93
+ legendItemTemplate
94
+ .append('rect')
95
+ .attr('x', function (legendItem, i) {
96
+ return (i * legendItem.symbol.width +
97
+ i * legend.itemDistance +
98
+ i * legendItem.symbol.padding +
99
+ textWidths.slice(0, i).reduce((acc, tw) => acc + tw, 0));
100
+ })
101
+ .attr('y', (legendItem) => {
102
+ const lineOffset = legend.lineHeight * lineIndex;
103
+ return config.offset.top + lineOffset - legendItem.symbol.height / 2;
104
+ })
105
+ .attr('width', (legendItem) => {
106
+ return legendItem.symbol.width;
107
+ })
108
+ .attr('height', (legendItem) => legendItem.symbol.height)
109
+ .attr('rx', (legendItem) => legendItem.symbol.radius)
110
+ .attr('class', function (d) {
111
+ return b('item-shape', { unselected: !d.visible });
112
+ })
113
+ .style('fill', function (d) {
114
+ return d.visible ? d.color : '';
115
+ });
116
+ legendItemTemplate
117
+ .append('text')
118
+ .attr('x', function (legendItem, i) {
119
+ return (i * legendItem.symbol.width +
120
+ i * legend.itemDistance +
121
+ i * legendItem.symbol.padding +
122
+ legendItem.symbol.width +
123
+ legendItem.symbol.padding +
124
+ textWidths.slice(0, i).reduce((acc, tw) => acc + tw, 0));
125
+ })
126
+ .attr('y', config.offset.top + legend.lineHeight * lineIndex)
127
+ .attr('class', function (d) {
128
+ const mods = { selected: d.visible, unselected: !d.visible };
129
+ return b('item-text', mods);
130
+ })
131
+ .text(function (d) {
132
+ return ('name' in d && d.name);
133
+ })
134
+ .style('font-size', legend.itemStyle.fontSize)
135
+ .style('alignment-baseline', 'middle');
136
+ const contentWidth = ((_a = legendLine.node()) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect().width) || 0;
137
+ const { left } = getLegendPosition({
138
+ align: legend.align,
139
+ width: boundsWidth,
140
+ offsetWidth: config.offset.left,
141
+ contentWidth,
142
+ });
143
+ legendLine.attr('transform', `translate(${[left, 0].join(',')})`);
74
144
  });
75
- legendItemTemplate
76
- .append('text')
77
- .attr('x', function (legendItem, i) {
78
- return (i * legendItem.symbol.width +
79
- i * legend.itemDistance +
80
- i * legendItem.symbol.padding +
81
- legendItem.symbol.width +
82
- legendItem.symbol.padding +
83
- textWidths.slice(0, i + 1).reduce((acc, tw) => acc + tw, 0));
84
- })
85
- .attr('y', offsetHeight)
86
- .attr('class', function (d) {
87
- const mods = { selected: d.visible, unselected: !d.visible };
88
- return b('item-text', mods);
89
- })
90
- .text(function (d) {
91
- return ('name' in d && d.name);
92
- })
93
- .style('alignment-baseline', 'middle');
94
- const contentWidth = sum(textWidths) +
95
- sum(legendItems, (item) => item.symbol.width + item.symbol.padding) +
96
- legend.itemDistance * (legendItems.length - 1);
97
- const { left } = getLegendPosition({
98
- align: legend.align,
99
- width,
100
- offsetWidth,
101
- contentWidth,
102
- });
103
- svgElement.attr('transform', `translate(${[left, 0].join(',')})`);
104
- }, [width, offsetWidth, height, offsetHeight, chartSeries, onItemClick, legend]);
105
- return React.createElement("g", { ref: ref, width: width, height: height });
145
+ if (config.pagination) {
146
+ const transform = `translate(${[
147
+ config.offset.left,
148
+ config.offset.top + legend.lineHeight * config.pagination.limit,
149
+ ].join(',')})`;
150
+ appendPaginator({
151
+ container: svgElement,
152
+ offset: paginationOffset,
153
+ maxPage: config.pagination.maxPage,
154
+ legend,
155
+ transform,
156
+ onArrowClick: setPaginationOffset,
157
+ });
158
+ }
159
+ }, [boundsWidth, chartSeries, onItemClick, legend, items, config, paginationOffset]);
160
+ return React.createElement("g", { ref: ref, width: boundsWidth, height: legend.height });
106
161
  };
@@ -23,6 +23,10 @@
23
23
  fill: var(--g-color-base-misc-medium);
24
24
  }
25
25
 
26
+ .chartkit-d3-legend__item-shape_unselected {
27
+ fill: var(--g-color-text-hint);
28
+ }
29
+
26
30
  .chartkit-d3-legend__item-text {
27
31
  fill: var(--g-color-text-secondary);
28
32
  }
@@ -35,6 +39,29 @@
35
39
  fill: var(--g-color-text-complementary);
36
40
  }
37
41
 
42
+ .chartkit-d3-legend__pagination {
43
+ fill: var(--g-color-text-primary);
44
+ user-select: none;
45
+ }
46
+
47
+ .chartkit-d3-legend__pagination-counter, .chartkit-d3-legend__pagination-arrow {
48
+ alignment-baseline: middle;
49
+ }
50
+
51
+ .chartkit-d3-legend__pagination-arrow {
52
+ fill: var(--g-color-text-brand);
53
+ cursor: pointer;
54
+ }
55
+
56
+ .chartkit-d3-legend__pagination-arrow_inactive {
57
+ fill: var(--g-color-base-generic-accent-disabled);
58
+ cursor: inherit;
59
+ }
60
+
61
+ .chartkit-d3-legend__pagination-arrow:hover:not(.chartkit-d3-legend__pagination-arrow_inactive) {
62
+ fill: var(--g-color-base-brand-hover);
63
+ }
64
+
38
65
  .chartkit-d3-title {
39
66
  font-size: var(--g-text-subheader-2-font-size);
40
67
  font-weight: var(--g-text-subheader-font-weight);
@@ -0,0 +1,5 @@
1
+ export declare const axisLabelsDefaults: {
2
+ margin: number;
3
+ padding: number;
4
+ fontSize: number;
5
+ };
@@ -0,0 +1,5 @@
1
+ export const axisLabelsDefaults = {
2
+ margin: 10,
3
+ padding: 10,
4
+ fontSize: 11,
5
+ };
@@ -0,0 +1,2 @@
1
+ export * from './axis';
2
+ export * from './legend';
@@ -0,0 +1,2 @@
1
+ export * from './axis';
2
+ export * from './legend';
@@ -0,0 +1,4 @@
1
+ import type { ChartKitWidgetLegend } from '../../../../../types';
2
+ type LegendDefaults = Required<Omit<ChartKitWidgetLegend, 'enabled'>> & Pick<ChartKitWidgetLegend, 'enabled'>;
3
+ export declare const legendDefaults: LegendDefaults;
4
+ export {};
@@ -0,0 +1,8 @@
1
+ export const legendDefaults = {
2
+ align: 'center',
3
+ itemDistance: 20,
4
+ margin: 15,
5
+ itemStyle: {
6
+ fontSize: '12px',
7
+ },
8
+ };
@@ -1,4 +1,4 @@
1
+ export * from './defaults';
1
2
  export declare const DEFAULT_PALETTE: string[];
2
3
  export declare const DEFAULT_AXIS_LABEL_FONT_SIZE = "11px";
3
- export declare const DEFAULT_AXIS_LABEL_PADDING = 10;
4
4
  export declare const DEFAULT_AXIS_TITLE_FONT_SIZE = "14px";
@@ -1,3 +1,4 @@
1
+ export * from './defaults';
1
2
  export const DEFAULT_PALETTE = [
2
3
  '#4DA2F1',
3
4
  '#FF3D64',
@@ -21,5 +22,4 @@ export const DEFAULT_PALETTE = [
21
22
  '#DCA3D7',
22
23
  ];
23
24
  export const DEFAULT_AXIS_LABEL_FONT_SIZE = '11px';
24
- export const DEFAULT_AXIS_LABEL_PADDING = 10;
25
25
  export const DEFAULT_AXIS_TITLE_FONT_SIZE = '14px';