@gravity-ui/charts 1.11.2 → 1.11.3

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.
@@ -10,6 +10,7 @@ type Props = {
10
10
  plotBeforeRef?: React.MutableRefObject<SVGGElement | null>;
11
11
  plotAfterRef?: React.MutableRefObject<SVGGElement | null>;
12
12
  bottomLimit?: number;
13
+ topLimit?: number;
13
14
  };
14
15
  export declare const AxisY: (props: Props) => React.JSX.Element;
15
16
  export {};
@@ -4,11 +4,8 @@ import { block, calculateCos, calculateSin, formatAxisTickLabel, getAxisHeight,
4
4
  import './styles.css';
5
5
  const b = block('axis');
6
6
  function transformLabel(args) {
7
- const { node, axis, isTopOffsetOverload = false } = args;
8
- let topOffset = axis.labels.lineHeight / 2;
9
- if (isTopOffsetOverload) {
10
- topOffset = 0;
11
- }
7
+ const { node, axis, startTopOffset } = args;
8
+ let topOffset = startTopOffset !== null && startTopOffset !== void 0 ? startTopOffset : axis.labels.lineHeight / 2;
12
9
  let leftOffset = axis.labels.margin;
13
10
  if (axis.position === 'left') {
14
11
  leftOffset = leftOffset * -1;
@@ -85,7 +82,7 @@ function getTitlePosition(args) {
85
82
  return { x, y };
86
83
  }
87
84
  export const AxisY = (props) => {
88
- const { axes: allAxes, width, height: totalHeight, scale, split, plotBeforeRef, plotAfterRef, bottomLimit = 0, } = props;
85
+ const { axes: allAxes, width, height: totalHeight, scale, split, plotBeforeRef, plotAfterRef, bottomLimit = 0, topLimit = 0, } = props;
89
86
  const height = getAxisHeight({ split, boundsHeight: totalHeight });
90
87
  const ref = React.useRef(null);
91
88
  const lineGenerator = line();
@@ -140,16 +137,20 @@ export const AxisY = (props) => {
140
137
  .style('transform', function () {
141
138
  return transformLabel({ node: this, axis: d });
142
139
  });
143
- labels.each(function (_d, i) {
144
- if (i === 0) {
145
- const currentElement = this;
146
- const currentElementPosition = currentElement.getBoundingClientRect();
147
- const text = select(currentElement);
148
- if (currentElementPosition.bottom > bottomLimit) {
140
+ labels.each(function (_d, i, nodes) {
141
+ const isFirstNode = i === 0;
142
+ const isLastNode = i === nodes.length - 1;
143
+ if (isFirstNode || isLastNode) {
144
+ const labelNode = this;
145
+ const labelNodeRect = labelNode.getBoundingClientRect();
146
+ const shouldBeTransformed = (isFirstNode && labelNodeRect.bottom > bottomLimit) ||
147
+ (isLastNode && labelNodeRect.top < topLimit);
148
+ if (shouldBeTransformed) {
149
+ const text = select(labelNode);
149
150
  const transform = transformLabel({
150
151
  node: this,
151
152
  axis: d,
152
- isTopOffsetOverload: true,
153
+ startTopOffset: isLastNode ? labelNodeRect.height : 0,
153
154
  });
154
155
  text.style('transform', transform);
155
156
  if (d.labels.rotation) {
@@ -23,7 +23,7 @@ export const ChartInner = (props) => {
23
23
  const plotAfterRef = React.useRef(null);
24
24
  const dispatcher = React.useMemo(() => getDispatcher(), []);
25
25
  const clipPathId = useUniqId();
26
- const { boundsHeight, boundsOffsetLeft, boundsOffsetTop, boundsWidth, handleLegendItemClick, handleZoomReset, isOutsideBounds, legendConfig, legendItems, preparedSeries, preparedSplit, preparedLegend, prevHeight, prevWidth, shapes, shapesData, title, tooltip, xAxis, xScale, yAxis, yScale, svgXPos, svgBottomPos, } = useChartInnerProps(Object.assign(Object.assign({}, props), { dispatcher,
26
+ const { boundsHeight, boundsOffsetLeft, boundsOffsetTop, boundsWidth, handleLegendItemClick, handleZoomReset, isOutsideBounds, legendConfig, legendItems, preparedSeries, preparedSplit, preparedLegend, prevHeight, prevWidth, shapes, shapesData, title, tooltip, xAxis, xScale, yAxis, yScale, svgXPos, svgBottomPos, svgTopPos, } = useChartInnerProps(Object.assign(Object.assign({}, props), { dispatcher,
27
27
  htmlLayout, svgContainer: svgRef.current, plotNode: plotRef.current, clipPathId }));
28
28
  const { tooltipPinned, togglePinTooltip, unpinTooltip } = useChartInnerState({
29
29
  dispatcher,
@@ -94,7 +94,7 @@ export const ChartInner = (props) => {
94
94
  })),
95
95
  React.createElement("g", { width: boundsWidth, height: boundsHeight, transform: `translate(${[boundsOffsetLeft, boundsOffsetTop].join(',')})`, ref: plotRef },
96
96
  xScale && (yScale === null || yScale === void 0 ? void 0 : yScale.length) && (React.createElement(React.Fragment, null,
97
- React.createElement(AxisY, { bottomLimit: svgBottomPos, axes: yAxis, width: boundsWidth, height: boundsHeight, scale: yScale, split: preparedSplit, plotBeforeRef: plotBeforeRef, plotAfterRef: plotAfterRef }),
97
+ React.createElement(AxisY, { bottomLimit: svgBottomPos, topLimit: svgTopPos, axes: yAxis, width: boundsWidth, height: boundsHeight, scale: yScale, split: preparedSplit, plotBeforeRef: plotBeforeRef, plotAfterRef: plotAfterRef }),
98
98
  xAxis && (React.createElement("g", { transform: `translate(0, ${boundsHeight})` },
99
99
  React.createElement(AxisX, { leftmostLimit: svgXPos, axis: xAxis, width: boundsWidth, height: boundsHeight, scale: xScale, split: preparedSplit, plotBeforeRef: plotBeforeRef, plotAfterRef: plotAfterRef }))))),
100
100
  React.createElement("g", { ref: plotBeforeRef }),
@@ -11,6 +11,7 @@ type Props = ChartInnerProps & {
11
11
  };
12
12
  export declare function useChartInnerProps(props: Props): {
13
13
  svgBottomPos: number | undefined;
14
+ svgTopPos: number | undefined;
14
15
  svgXPos: number | undefined;
15
16
  boundsHeight: number;
16
17
  boundsOffsetLeft: number;
@@ -13,8 +13,14 @@ export function useChartInnerProps(props) {
13
13
  const { width, height, data, dispatcher, htmlLayout, svgContainer, plotNode, clipPathId } = props;
14
14
  const prevWidth = usePrevious(width);
15
15
  const prevHeight = usePrevious(height);
16
+ const { chart, title, tooltip, colors } = useChartOptions({
17
+ seriesData: data.series.data,
18
+ chart: data.chart,
19
+ colors: data.colors,
20
+ title: data.title,
21
+ tooltip: data.tooltip,
22
+ });
16
23
  const [zoomState, setZoomState] = React.useState({});
17
- const { chart, title, tooltip, colors } = useChartOptions({ data });
18
24
  const sortedSeriesData = React.useMemo(() => {
19
25
  return getSortedSeriesData(data.series.data);
20
26
  }, [data.series.data]);
@@ -141,12 +147,13 @@ export function useChartInnerProps(props) {
141
147
  }
142
148
  return acc;
143
149
  }, 0);
144
- const { x, bottom } = (_a = svgContainer === null || svgContainer === void 0 ? void 0 : svgContainer.getBoundingClientRect()) !== null && _a !== void 0 ? _a : {};
150
+ const { bottom, top, x } = (_a = svgContainer === null || svgContainer === void 0 ? void 0 : svgContainer.getBoundingClientRect()) !== null && _a !== void 0 ? _a : {};
145
151
  const handleZoomReset = React.useCallback(() => {
146
152
  setZoomState({});
147
153
  }, []);
148
154
  return {
149
155
  svgBottomPos: bottom,
156
+ svgTopPos: top,
150
157
  svgXPos: x,
151
158
  boundsHeight,
152
159
  boundsOffsetLeft,
@@ -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, handleOverflowingText, } from '../../utils';
5
+ import { block, createGradientRect, getContinuesColorFn, getLabelsSize, getLineDashArray, getSymbol, getUniqId, handleOverflowingText, } from '../../utils';
6
6
  import { axisBottom } from '../../utils/chart/axis-generators';
7
7
  import './styles.css';
8
8
  const b = block('legend');
@@ -305,6 +305,7 @@ export const Legend = (props) => {
305
305
  maxTickCount: 4,
306
306
  tickColor: '#fff',
307
307
  labelFormat: (value) => formatNumber(value, { unit: 'auto' }),
308
+ labelsStyle: legend.ticks.style,
308
309
  },
309
310
  domain: {
310
311
  size: legend.width,
@@ -381,5 +382,8 @@ export const Legend = (props) => {
381
382
  pageIndex,
382
383
  htmlLayout,
383
384
  ]);
384
- return React.createElement("g", { className: b(), ref: ref, width: boundsWidth, height: legend.height });
385
+ // due to asynchronous processing, we only need to work with the actual element
386
+ // eslint-disable-next-line react-hooks/exhaustive-deps
387
+ const key = React.useMemo(() => getUniqId(), [legend, config]);
388
+ return React.createElement("g", { key: key, className: b(), ref: ref, width: boundsWidth, height: legend.height });
385
389
  };
@@ -1,7 +1,11 @@
1
- import type { ChartData } from '../../types';
1
+ import type { ChartSeries, ChartTitle, ChartTooltip, ChartOptions as GeneralChartOptions } from '../../types';
2
2
  import type { ChartOptions } from './types';
3
3
  type Args = {
4
- data: ChartData;
4
+ seriesData: ChartSeries[];
5
+ chart?: GeneralChartOptions;
6
+ colors?: string[];
7
+ title?: ChartTitle;
8
+ tooltip?: ChartTooltip;
5
9
  };
6
10
  export declare const useChartOptions: (args: Args) => ChartOptions;
7
11
  export {};
@@ -4,21 +4,21 @@ import { getPreparedChart } from './chart';
4
4
  import { getPreparedTitle } from './title';
5
5
  import { getPreparedTooltip } from './tooltip';
6
6
  export const useChartOptions = (args) => {
7
- const { data: { chart, title, tooltip, colors, series }, } = args;
7
+ const { chart, colors, seriesData, title, tooltip } = args;
8
8
  const options = React.useMemo(() => {
9
9
  const preparedTitle = getPreparedTitle({ title });
10
10
  const preparedTooltip = getPreparedTooltip({ tooltip });
11
11
  const preparedChart = getPreparedChart({
12
12
  chart,
13
13
  preparedTitle,
14
- seriesData: series.data,
14
+ seriesData,
15
15
  });
16
16
  return {
17
+ colors: colors !== null && colors !== void 0 ? colors : DEFAULT_PALETTE,
17
18
  chart: preparedChart,
18
19
  title: preparedTitle,
19
20
  tooltip: preparedTooltip,
20
- colors: colors !== null && colors !== void 0 ? colors : DEFAULT_PALETTE,
21
21
  };
22
- }, [chart, colors, title, tooltip, series.data]);
22
+ }, [chart, colors, seriesData, title, tooltip]);
23
23
  return options;
24
24
  };
@@ -21,9 +21,13 @@ export async function getPreparedLegend(args) {
21
21
  const titleText = isTitleEnabled ? get(legend, 'title.text', '') : '';
22
22
  const titleSize = await getLabelsSize({ labels: [titleText], style: titleStyle });
23
23
  const titleHeight = isTitleEnabled ? titleSize.maxHeight : 0;
24
+ const tickStyle = {
25
+ fontSize: '12px',
26
+ };
24
27
  const ticks = {
25
28
  labelsMargin: 4,
26
- labelsLineHeight: 12,
29
+ labelsLineHeight: (await getLabelsSize({ labels: ['Tmp'], style: tickStyle })).maxHeight,
30
+ style: tickStyle,
27
31
  };
28
32
  const colorScale = {
29
33
  colors: [],
@@ -26,6 +26,7 @@ export type PreparedLegend = Required<Omit<ChartLegend, 'title' | 'colorScale'>>
26
26
  ticks: {
27
27
  labelsMargin: number;
28
28
  labelsLineHeight: number;
29
+ style: BaseTextStyle;
29
30
  };
30
31
  colorScale: {
31
32
  colors: string[];
@@ -90,27 +90,15 @@ export async function axisBottom(args) {
90
90
  });
91
91
  }
92
92
  else {
93
- // remove overlapping labels
94
93
  let elementX = 0;
95
- selection
96
- .selectAll('.tick')
97
- .filter(function () {
98
- const node = this;
99
- const r = node.getBoundingClientRect();
100
- if (r.left < elementX) {
101
- return true;
102
- }
103
- elementX = r.right + labelsPaddings;
104
- return false;
105
- })
106
- .remove();
107
94
  // add an ellipsis to the labels that go beyond the boundaries of the chart
95
+ // and remove overlapping labels
108
96
  labels.each(function (_d, i, nodes) {
109
- var _a;
97
+ var _a, _b;
98
+ const currentElement = this;
99
+ const currentElementPosition = currentElement.getBoundingClientRect();
110
100
  if (i === 0) {
111
- const currentElement = this;
112
101
  const text = select(currentElement);
113
- const currentElementPosition = currentElement.getBoundingClientRect();
114
102
  const nextElement = nodes[i + 1];
115
103
  const nextElementPosition = nextElement === null || nextElement === void 0 ? void 0 : nextElement.getBoundingClientRect();
116
104
  if (currentElementPosition.left < leftmostLimit) {
@@ -123,18 +111,23 @@ export async function axisBottom(args) {
123
111
  setEllipsisForOverflowText(text, remainSpace);
124
112
  }
125
113
  }
126
- if (i === nodes.length - 1) {
127
- const currentElement = this;
128
- const prevElement = nodes[i - 1];
129
- const text = select(currentElement);
130
- const currentElementPosition = currentElement.getBoundingClientRect();
131
- const prevElementPosition = prevElement === null || prevElement === void 0 ? void 0 : prevElement.getBoundingClientRect();
132
- const lackingSpace = Math.max(0, currentElementPosition.right - right);
133
- if (lackingSpace) {
134
- const remainSpace = right - ((prevElementPosition === null || prevElementPosition === void 0 ? void 0 : prevElementPosition.right) || 0) - labelsPaddings;
135
- const translateX = -lackingSpace;
136
- text.style('transform', `translate(${translateX}px,${translateY}px)`);
137
- setEllipsisForOverflowText(text, remainSpace);
114
+ else {
115
+ if (currentElementPosition.left < elementX) {
116
+ (_b = currentElement.closest('.tick')) === null || _b === void 0 ? void 0 : _b.remove();
117
+ return;
118
+ }
119
+ elementX = currentElementPosition.right + labelsPaddings;
120
+ if (i === nodes.length - 1) {
121
+ const prevElement = nodes[i - 1];
122
+ const text = select(currentElement);
123
+ const prevElementPosition = prevElement === null || prevElement === void 0 ? void 0 : prevElement.getBoundingClientRect();
124
+ const lackingSpace = Math.max(0, currentElementPosition.right - right);
125
+ if (lackingSpace) {
126
+ const remainSpace = right - ((prevElementPosition === null || prevElementPosition === void 0 ? void 0 : prevElementPosition.right) || 0) - labelsPaddings;
127
+ const translateX = -lackingSpace;
128
+ text.style('transform', `translate(${translateX}px,${translateY}px)`);
129
+ setEllipsisForOverflowText(text, remainSpace);
130
+ }
138
131
  }
139
132
  }
140
133
  });
@@ -10,6 +10,7 @@ type Props = {
10
10
  plotBeforeRef?: React.MutableRefObject<SVGGElement | null>;
11
11
  plotAfterRef?: React.MutableRefObject<SVGGElement | null>;
12
12
  bottomLimit?: number;
13
+ topLimit?: number;
13
14
  };
14
15
  export declare const AxisY: (props: Props) => React.JSX.Element;
15
16
  export {};
@@ -4,11 +4,8 @@ import { block, calculateCos, calculateSin, formatAxisTickLabel, getAxisHeight,
4
4
  import './styles.css';
5
5
  const b = block('axis');
6
6
  function transformLabel(args) {
7
- const { node, axis, isTopOffsetOverload = false } = args;
8
- let topOffset = axis.labels.lineHeight / 2;
9
- if (isTopOffsetOverload) {
10
- topOffset = 0;
11
- }
7
+ const { node, axis, startTopOffset } = args;
8
+ let topOffset = startTopOffset !== null && startTopOffset !== void 0 ? startTopOffset : axis.labels.lineHeight / 2;
12
9
  let leftOffset = axis.labels.margin;
13
10
  if (axis.position === 'left') {
14
11
  leftOffset = leftOffset * -1;
@@ -85,7 +82,7 @@ function getTitlePosition(args) {
85
82
  return { x, y };
86
83
  }
87
84
  export const AxisY = (props) => {
88
- const { axes: allAxes, width, height: totalHeight, scale, split, plotBeforeRef, plotAfterRef, bottomLimit = 0, } = props;
85
+ const { axes: allAxes, width, height: totalHeight, scale, split, plotBeforeRef, plotAfterRef, bottomLimit = 0, topLimit = 0, } = props;
89
86
  const height = getAxisHeight({ split, boundsHeight: totalHeight });
90
87
  const ref = React.useRef(null);
91
88
  const lineGenerator = line();
@@ -140,16 +137,20 @@ export const AxisY = (props) => {
140
137
  .style('transform', function () {
141
138
  return transformLabel({ node: this, axis: d });
142
139
  });
143
- labels.each(function (_d, i) {
144
- if (i === 0) {
145
- const currentElement = this;
146
- const currentElementPosition = currentElement.getBoundingClientRect();
147
- const text = select(currentElement);
148
- if (currentElementPosition.bottom > bottomLimit) {
140
+ labels.each(function (_d, i, nodes) {
141
+ const isFirstNode = i === 0;
142
+ const isLastNode = i === nodes.length - 1;
143
+ if (isFirstNode || isLastNode) {
144
+ const labelNode = this;
145
+ const labelNodeRect = labelNode.getBoundingClientRect();
146
+ const shouldBeTransformed = (isFirstNode && labelNodeRect.bottom > bottomLimit) ||
147
+ (isLastNode && labelNodeRect.top < topLimit);
148
+ if (shouldBeTransformed) {
149
+ const text = select(labelNode);
149
150
  const transform = transformLabel({
150
151
  node: this,
151
152
  axis: d,
152
- isTopOffsetOverload: true,
153
+ startTopOffset: isLastNode ? labelNodeRect.height : 0,
153
154
  });
154
155
  text.style('transform', transform);
155
156
  if (d.labels.rotation) {
@@ -23,7 +23,7 @@ export const ChartInner = (props) => {
23
23
  const plotAfterRef = React.useRef(null);
24
24
  const dispatcher = React.useMemo(() => getDispatcher(), []);
25
25
  const clipPathId = useUniqId();
26
- const { boundsHeight, boundsOffsetLeft, boundsOffsetTop, boundsWidth, handleLegendItemClick, handleZoomReset, isOutsideBounds, legendConfig, legendItems, preparedSeries, preparedSplit, preparedLegend, prevHeight, prevWidth, shapes, shapesData, title, tooltip, xAxis, xScale, yAxis, yScale, svgXPos, svgBottomPos, } = useChartInnerProps(Object.assign(Object.assign({}, props), { dispatcher,
26
+ const { boundsHeight, boundsOffsetLeft, boundsOffsetTop, boundsWidth, handleLegendItemClick, handleZoomReset, isOutsideBounds, legendConfig, legendItems, preparedSeries, preparedSplit, preparedLegend, prevHeight, prevWidth, shapes, shapesData, title, tooltip, xAxis, xScale, yAxis, yScale, svgXPos, svgBottomPos, svgTopPos, } = useChartInnerProps(Object.assign(Object.assign({}, props), { dispatcher,
27
27
  htmlLayout, svgContainer: svgRef.current, plotNode: plotRef.current, clipPathId }));
28
28
  const { tooltipPinned, togglePinTooltip, unpinTooltip } = useChartInnerState({
29
29
  dispatcher,
@@ -94,7 +94,7 @@ export const ChartInner = (props) => {
94
94
  })),
95
95
  React.createElement("g", { width: boundsWidth, height: boundsHeight, transform: `translate(${[boundsOffsetLeft, boundsOffsetTop].join(',')})`, ref: plotRef },
96
96
  xScale && (yScale === null || yScale === void 0 ? void 0 : yScale.length) && (React.createElement(React.Fragment, null,
97
- React.createElement(AxisY, { bottomLimit: svgBottomPos, axes: yAxis, width: boundsWidth, height: boundsHeight, scale: yScale, split: preparedSplit, plotBeforeRef: plotBeforeRef, plotAfterRef: plotAfterRef }),
97
+ React.createElement(AxisY, { bottomLimit: svgBottomPos, topLimit: svgTopPos, axes: yAxis, width: boundsWidth, height: boundsHeight, scale: yScale, split: preparedSplit, plotBeforeRef: plotBeforeRef, plotAfterRef: plotAfterRef }),
98
98
  xAxis && (React.createElement("g", { transform: `translate(0, ${boundsHeight})` },
99
99
  React.createElement(AxisX, { leftmostLimit: svgXPos, axis: xAxis, width: boundsWidth, height: boundsHeight, scale: xScale, split: preparedSplit, plotBeforeRef: plotBeforeRef, plotAfterRef: plotAfterRef }))))),
100
100
  React.createElement("g", { ref: plotBeforeRef }),
@@ -11,6 +11,7 @@ type Props = ChartInnerProps & {
11
11
  };
12
12
  export declare function useChartInnerProps(props: Props): {
13
13
  svgBottomPos: number | undefined;
14
+ svgTopPos: number | undefined;
14
15
  svgXPos: number | undefined;
15
16
  boundsHeight: number;
16
17
  boundsOffsetLeft: number;
@@ -13,8 +13,14 @@ export function useChartInnerProps(props) {
13
13
  const { width, height, data, dispatcher, htmlLayout, svgContainer, plotNode, clipPathId } = props;
14
14
  const prevWidth = usePrevious(width);
15
15
  const prevHeight = usePrevious(height);
16
+ const { chart, title, tooltip, colors } = useChartOptions({
17
+ seriesData: data.series.data,
18
+ chart: data.chart,
19
+ colors: data.colors,
20
+ title: data.title,
21
+ tooltip: data.tooltip,
22
+ });
16
23
  const [zoomState, setZoomState] = React.useState({});
17
- const { chart, title, tooltip, colors } = useChartOptions({ data });
18
24
  const sortedSeriesData = React.useMemo(() => {
19
25
  return getSortedSeriesData(data.series.data);
20
26
  }, [data.series.data]);
@@ -141,12 +147,13 @@ export function useChartInnerProps(props) {
141
147
  }
142
148
  return acc;
143
149
  }, 0);
144
- const { x, bottom } = (_a = svgContainer === null || svgContainer === void 0 ? void 0 : svgContainer.getBoundingClientRect()) !== null && _a !== void 0 ? _a : {};
150
+ const { bottom, top, x } = (_a = svgContainer === null || svgContainer === void 0 ? void 0 : svgContainer.getBoundingClientRect()) !== null && _a !== void 0 ? _a : {};
145
151
  const handleZoomReset = React.useCallback(() => {
146
152
  setZoomState({});
147
153
  }, []);
148
154
  return {
149
155
  svgBottomPos: bottom,
156
+ svgTopPos: top,
150
157
  svgXPos: x,
151
158
  boundsHeight,
152
159
  boundsOffsetLeft,
@@ -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, handleOverflowingText, } from '../../utils';
5
+ import { block, createGradientRect, getContinuesColorFn, getLabelsSize, getLineDashArray, getSymbol, getUniqId, handleOverflowingText, } from '../../utils';
6
6
  import { axisBottom } from '../../utils/chart/axis-generators';
7
7
  import './styles.css';
8
8
  const b = block('legend');
@@ -305,6 +305,7 @@ export const Legend = (props) => {
305
305
  maxTickCount: 4,
306
306
  tickColor: '#fff',
307
307
  labelFormat: (value) => formatNumber(value, { unit: 'auto' }),
308
+ labelsStyle: legend.ticks.style,
308
309
  },
309
310
  domain: {
310
311
  size: legend.width,
@@ -381,5 +382,8 @@ export const Legend = (props) => {
381
382
  pageIndex,
382
383
  htmlLayout,
383
384
  ]);
384
- return React.createElement("g", { className: b(), ref: ref, width: boundsWidth, height: legend.height });
385
+ // due to asynchronous processing, we only need to work with the actual element
386
+ // eslint-disable-next-line react-hooks/exhaustive-deps
387
+ const key = React.useMemo(() => getUniqId(), [legend, config]);
388
+ return React.createElement("g", { key: key, className: b(), ref: ref, width: boundsWidth, height: legend.height });
385
389
  };
@@ -1,7 +1,11 @@
1
- import type { ChartData } from '../../types';
1
+ import type { ChartSeries, ChartTitle, ChartTooltip, ChartOptions as GeneralChartOptions } from '../../types';
2
2
  import type { ChartOptions } from './types';
3
3
  type Args = {
4
- data: ChartData;
4
+ seriesData: ChartSeries[];
5
+ chart?: GeneralChartOptions;
6
+ colors?: string[];
7
+ title?: ChartTitle;
8
+ tooltip?: ChartTooltip;
5
9
  };
6
10
  export declare const useChartOptions: (args: Args) => ChartOptions;
7
11
  export {};
@@ -4,21 +4,21 @@ import { getPreparedChart } from './chart';
4
4
  import { getPreparedTitle } from './title';
5
5
  import { getPreparedTooltip } from './tooltip';
6
6
  export const useChartOptions = (args) => {
7
- const { data: { chart, title, tooltip, colors, series }, } = args;
7
+ const { chart, colors, seriesData, title, tooltip } = args;
8
8
  const options = React.useMemo(() => {
9
9
  const preparedTitle = getPreparedTitle({ title });
10
10
  const preparedTooltip = getPreparedTooltip({ tooltip });
11
11
  const preparedChart = getPreparedChart({
12
12
  chart,
13
13
  preparedTitle,
14
- seriesData: series.data,
14
+ seriesData,
15
15
  });
16
16
  return {
17
+ colors: colors !== null && colors !== void 0 ? colors : DEFAULT_PALETTE,
17
18
  chart: preparedChart,
18
19
  title: preparedTitle,
19
20
  tooltip: preparedTooltip,
20
- colors: colors !== null && colors !== void 0 ? colors : DEFAULT_PALETTE,
21
21
  };
22
- }, [chart, colors, title, tooltip, series.data]);
22
+ }, [chart, colors, seriesData, title, tooltip]);
23
23
  return options;
24
24
  };
@@ -21,9 +21,13 @@ export async function getPreparedLegend(args) {
21
21
  const titleText = isTitleEnabled ? get(legend, 'title.text', '') : '';
22
22
  const titleSize = await getLabelsSize({ labels: [titleText], style: titleStyle });
23
23
  const titleHeight = isTitleEnabled ? titleSize.maxHeight : 0;
24
+ const tickStyle = {
25
+ fontSize: '12px',
26
+ };
24
27
  const ticks = {
25
28
  labelsMargin: 4,
26
- labelsLineHeight: 12,
29
+ labelsLineHeight: (await getLabelsSize({ labels: ['Tmp'], style: tickStyle })).maxHeight,
30
+ style: tickStyle,
27
31
  };
28
32
  const colorScale = {
29
33
  colors: [],
@@ -26,6 +26,7 @@ export type PreparedLegend = Required<Omit<ChartLegend, 'title' | 'colorScale'>>
26
26
  ticks: {
27
27
  labelsMargin: number;
28
28
  labelsLineHeight: number;
29
+ style: BaseTextStyle;
29
30
  };
30
31
  colorScale: {
31
32
  colors: string[];
@@ -90,27 +90,15 @@ export async function axisBottom(args) {
90
90
  });
91
91
  }
92
92
  else {
93
- // remove overlapping labels
94
93
  let elementX = 0;
95
- selection
96
- .selectAll('.tick')
97
- .filter(function () {
98
- const node = this;
99
- const r = node.getBoundingClientRect();
100
- if (r.left < elementX) {
101
- return true;
102
- }
103
- elementX = r.right + labelsPaddings;
104
- return false;
105
- })
106
- .remove();
107
94
  // add an ellipsis to the labels that go beyond the boundaries of the chart
95
+ // and remove overlapping labels
108
96
  labels.each(function (_d, i, nodes) {
109
- var _a;
97
+ var _a, _b;
98
+ const currentElement = this;
99
+ const currentElementPosition = currentElement.getBoundingClientRect();
110
100
  if (i === 0) {
111
- const currentElement = this;
112
101
  const text = select(currentElement);
113
- const currentElementPosition = currentElement.getBoundingClientRect();
114
102
  const nextElement = nodes[i + 1];
115
103
  const nextElementPosition = nextElement === null || nextElement === void 0 ? void 0 : nextElement.getBoundingClientRect();
116
104
  if (currentElementPosition.left < leftmostLimit) {
@@ -123,18 +111,23 @@ export async function axisBottom(args) {
123
111
  setEllipsisForOverflowText(text, remainSpace);
124
112
  }
125
113
  }
126
- if (i === nodes.length - 1) {
127
- const currentElement = this;
128
- const prevElement = nodes[i - 1];
129
- const text = select(currentElement);
130
- const currentElementPosition = currentElement.getBoundingClientRect();
131
- const prevElementPosition = prevElement === null || prevElement === void 0 ? void 0 : prevElement.getBoundingClientRect();
132
- const lackingSpace = Math.max(0, currentElementPosition.right - right);
133
- if (lackingSpace) {
134
- const remainSpace = right - ((prevElementPosition === null || prevElementPosition === void 0 ? void 0 : prevElementPosition.right) || 0) - labelsPaddings;
135
- const translateX = -lackingSpace;
136
- text.style('transform', `translate(${translateX}px,${translateY}px)`);
137
- setEllipsisForOverflowText(text, remainSpace);
114
+ else {
115
+ if (currentElementPosition.left < elementX) {
116
+ (_b = currentElement.closest('.tick')) === null || _b === void 0 ? void 0 : _b.remove();
117
+ return;
118
+ }
119
+ elementX = currentElementPosition.right + labelsPaddings;
120
+ if (i === nodes.length - 1) {
121
+ const prevElement = nodes[i - 1];
122
+ const text = select(currentElement);
123
+ const prevElementPosition = prevElement === null || prevElement === void 0 ? void 0 : prevElement.getBoundingClientRect();
124
+ const lackingSpace = Math.max(0, currentElementPosition.right - right);
125
+ if (lackingSpace) {
126
+ const remainSpace = right - ((prevElementPosition === null || prevElementPosition === void 0 ? void 0 : prevElementPosition.right) || 0) - labelsPaddings;
127
+ const translateX = -lackingSpace;
128
+ text.style('transform', `translate(${translateX}px,${translateY}px)`);
129
+ setEllipsisForOverflowText(text, remainSpace);
130
+ }
138
131
  }
139
132
  }
140
133
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gravity-ui/charts",
3
- "version": "1.11.2",
3
+ "version": "1.11.3",
4
4
  "description": "React component used to render charts",
5
5
  "license": "MIT",
6
6
  "main": "dist/cjs/index.js",