@gravity-ui/charts 1.11.1 → 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.
Files changed (77) hide show
  1. package/dist/cjs/components/Axis/AxisY.d.ts +1 -0
  2. package/dist/cjs/components/Axis/AxisY.js +14 -13
  3. package/dist/cjs/components/ChartInner/index.js +2 -2
  4. package/dist/cjs/components/ChartInner/useChartInnerProps.d.ts +1 -0
  5. package/dist/cjs/components/ChartInner/useChartInnerProps.js +44 -13
  6. package/dist/cjs/components/Legend/index.js +6 -2
  7. package/dist/cjs/components/Tooltip/DefaultContent.js +4 -4
  8. package/dist/cjs/hooks/useAxisScales/index.js +15 -6
  9. package/dist/cjs/hooks/useChartOptions/index.d.ts +6 -2
  10. package/dist/cjs/hooks/useChartOptions/index.js +4 -4
  11. package/dist/cjs/hooks/useSeries/index.d.ts +1 -19
  12. package/dist/cjs/hooks/useSeries/index.js +7 -25
  13. package/dist/cjs/hooks/useSeries/prepare-area.js +1 -2
  14. package/dist/cjs/hooks/useSeries/prepare-legend.js +5 -1
  15. package/dist/cjs/hooks/useSeries/types.d.ts +1 -0
  16. package/dist/cjs/hooks/useShapes/area/prepare-data.js +2 -1
  17. package/dist/cjs/hooks/useShapes/line/prepare-data.js +3 -2
  18. package/dist/cjs/hooks/useShapes/utils.d.ts +6 -0
  19. package/dist/cjs/hooks/useShapes/utils.js +29 -4
  20. package/dist/cjs/hooks/useShapes/waterfall/index.js +1 -1
  21. package/dist/cjs/libs/format-number/index.d.ts +7 -3
  22. package/dist/cjs/libs/format-number/index.js +8 -3
  23. package/dist/cjs/libs/format-number/types.d.ts +0 -1
  24. package/dist/cjs/types/chart/tooltip.d.ts +1 -0
  25. package/dist/cjs/types/formatter.d.ts +0 -1
  26. package/dist/cjs/utils/chart/axis-generators/bottom.js +21 -28
  27. package/dist/cjs/utils/chart/get-closest-data.d.ts +1 -0
  28. package/dist/cjs/utils/chart/get-closest-data.js +2 -5
  29. package/dist/cjs/utils/chart/index.d.ts +1 -0
  30. package/dist/cjs/utils/chart/index.js +3 -2
  31. package/dist/cjs/utils/chart/series/index.d.ts +1 -0
  32. package/dist/cjs/utils/chart/series/index.js +1 -0
  33. package/dist/cjs/utils/chart/series/sorting.d.ts +2 -0
  34. package/dist/cjs/utils/chart/series/sorting.js +12 -0
  35. package/dist/{esm/hooks/hooks-utils → cjs/utils/chart}/zoom.d.ts +5 -2
  36. package/dist/{esm/hooks/hooks-utils → cjs/utils/chart}/zoom.js +36 -9
  37. package/dist/esm/components/Axis/AxisY.d.ts +1 -0
  38. package/dist/esm/components/Axis/AxisY.js +14 -13
  39. package/dist/esm/components/ChartInner/index.js +2 -2
  40. package/dist/esm/components/ChartInner/useChartInnerProps.d.ts +1 -0
  41. package/dist/esm/components/ChartInner/useChartInnerProps.js +44 -13
  42. package/dist/esm/components/Legend/index.js +6 -2
  43. package/dist/esm/components/Tooltip/DefaultContent.js +4 -4
  44. package/dist/esm/hooks/useAxisScales/index.js +15 -6
  45. package/dist/esm/hooks/useChartOptions/index.d.ts +6 -2
  46. package/dist/esm/hooks/useChartOptions/index.js +4 -4
  47. package/dist/esm/hooks/useSeries/index.d.ts +1 -19
  48. package/dist/esm/hooks/useSeries/index.js +7 -25
  49. package/dist/esm/hooks/useSeries/prepare-area.js +1 -2
  50. package/dist/esm/hooks/useSeries/prepare-legend.js +5 -1
  51. package/dist/esm/hooks/useSeries/types.d.ts +1 -0
  52. package/dist/esm/hooks/useShapes/area/prepare-data.js +2 -1
  53. package/dist/esm/hooks/useShapes/line/prepare-data.js +3 -2
  54. package/dist/esm/hooks/useShapes/utils.d.ts +6 -0
  55. package/dist/esm/hooks/useShapes/utils.js +29 -4
  56. package/dist/esm/hooks/useShapes/waterfall/index.js +1 -1
  57. package/dist/esm/libs/format-number/index.d.ts +7 -3
  58. package/dist/esm/libs/format-number/index.js +8 -3
  59. package/dist/esm/libs/format-number/types.d.ts +0 -1
  60. package/dist/esm/types/chart/tooltip.d.ts +1 -0
  61. package/dist/esm/types/formatter.d.ts +0 -1
  62. package/dist/esm/utils/chart/axis-generators/bottom.js +21 -28
  63. package/dist/esm/utils/chart/get-closest-data.d.ts +1 -0
  64. package/dist/esm/utils/chart/get-closest-data.js +2 -5
  65. package/dist/esm/utils/chart/index.d.ts +1 -0
  66. package/dist/esm/utils/chart/index.js +3 -2
  67. package/dist/esm/utils/chart/series/index.d.ts +1 -0
  68. package/dist/esm/utils/chart/series/index.js +1 -0
  69. package/dist/esm/utils/chart/series/sorting.d.ts +2 -0
  70. package/dist/esm/utils/chart/series/sorting.js +12 -0
  71. package/dist/{cjs/hooks/hooks-utils → esm/utils/chart}/zoom.d.ts +5 -2
  72. package/dist/{cjs/hooks/hooks-utils → esm/utils/chart}/zoom.js +36 -9
  73. package/package.json +1 -1
  74. package/dist/cjs/hooks/hooks-utils/index.d.ts +0 -1
  75. package/dist/cjs/hooks/hooks-utils/index.js +0 -1
  76. package/dist/esm/hooks/hooks-utils/index.d.ts +0 -1
  77. package/dist/esm/hooks/hooks-utils/index.js +0 -1
@@ -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;
@@ -1,26 +1,37 @@
1
1
  import React from 'react';
2
2
  import { useAxisScales, useChartDimensions, useChartOptions, usePrevious, useSeries, useShapes, useSplit, } from '../../hooks';
3
- import { getZoomedSeriesData } from '../../hooks/hooks-utils';
4
3
  import { getYAxisWidth } from '../../hooks/useChartDimensions/utils';
5
4
  import { getPreparedXAxis } from '../../hooks/useChartOptions/x-axis';
6
5
  import { getPreparedYAxis } from '../../hooks/useChartOptions/y-axis';
6
+ import { getLegendComponents } from '../../hooks/useSeries/prepare-legend';
7
+ import { getPreparedOptions } from '../../hooks/useSeries/prepare-options';
7
8
  import { useZoom } from '../../hooks/useZoom';
9
+ import { getSortedSeriesData, getZoomedSeriesData } from '../../utils';
8
10
  import { hasAtLeastOneSeriesDataPerPlot } from './utils';
9
11
  export function useChartInnerProps(props) {
10
12
  var _a;
11
13
  const { width, height, data, dispatcher, htmlLayout, svgContainer, plotNode, clipPathId } = props;
12
14
  const prevWidth = usePrevious(width);
13
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
+ });
14
23
  const [zoomState, setZoomState] = React.useState({});
15
- const { chart, title, tooltip, colors } = useChartOptions({ data });
16
- const zoomedSeriesData = React.useMemo(() => {
24
+ const sortedSeriesData = React.useMemo(() => {
25
+ return getSortedSeriesData(data.series.data);
26
+ }, [data.series.data]);
27
+ const { zoomedSeriesData, zoomedShapesSeriesData } = React.useMemo(() => {
17
28
  return getZoomedSeriesData({
18
- seriesData: data.series.data,
29
+ seriesData: sortedSeriesData,
19
30
  xAxis: data.xAxis,
20
31
  yAxes: data.yAxis,
21
32
  zoomState,
22
33
  });
23
- }, [data.series.data, data.xAxis, data.yAxis, zoomState]);
34
+ }, [data.xAxis, data.yAxis, sortedSeriesData, zoomState]);
24
35
  const [xAxis, setXAxis] = React.useState(null);
25
36
  React.useEffect(() => {
26
37
  setXAxis(null);
@@ -31,17 +42,36 @@ export function useChartInnerProps(props) {
31
42
  setYAxis([]);
32
43
  getPreparedYAxis({ yAxis: data.yAxis, height, seriesData: zoomedSeriesData }).then((val) => setYAxis(val));
33
44
  }, [data.yAxis, height, zoomedSeriesData]);
34
- const { legendItems, legendConfig, preparedSeries, preparedSeriesOptions, preparedLegend, handleLegendItemClick, } = useSeries({
35
- chartWidth: width,
36
- chartHeight: height,
37
- chartMargin: chart.margin,
45
+ const preparedSeriesOptions = React.useMemo(() => {
46
+ return getPreparedOptions(data.series.options);
47
+ }, [data.series.options]);
48
+ const { preparedSeries, preparedLegend, handleLegendItemClick } = useSeries({
38
49
  colors,
39
50
  legend: data.legend,
40
51
  originalSeriesData: data.series.data,
41
52
  seriesData: zoomedSeriesData,
42
53
  seriesOptions: data.series.options,
43
- preparedYAxis: yAxis,
44
54
  });
55
+ const { preparedSeries: preparedShapesSeries } = useSeries({
56
+ colors,
57
+ legend: data.legend,
58
+ originalSeriesData: data.series.data,
59
+ seriesData: zoomedShapesSeriesData,
60
+ seriesOptions: data.series.options,
61
+ });
62
+ const { legendConfig, legendItems } = React.useMemo(() => {
63
+ if (!preparedLegend) {
64
+ return { legendConfig: undefined, legendItems: [] };
65
+ }
66
+ return getLegendComponents({
67
+ chartWidth: width,
68
+ chartHeight: height,
69
+ chartMargin: chart.margin,
70
+ series: preparedSeries,
71
+ preparedLegend,
72
+ preparedYAxis: yAxis,
73
+ });
74
+ }, [width, height, chart.margin, preparedSeries, preparedLegend, yAxis]);
45
75
  const { boundsWidth, boundsHeight } = useChartDimensions({
46
76
  width,
47
77
  height,
@@ -69,7 +99,7 @@ export function useChartInnerProps(props) {
69
99
  boundsWidth,
70
100
  boundsHeight,
71
101
  dispatcher,
72
- series: preparedSeries,
102
+ series: preparedShapesSeries,
73
103
  seriesOptions: preparedSeriesOptions,
74
104
  xAxis,
75
105
  xScale,
@@ -81,7 +111,7 @@ export function useChartInnerProps(props) {
81
111
  isOutsideBounds,
82
112
  });
83
113
  const handleAttemptToSetZoomState = React.useCallback((nextZoomState) => {
84
- const nextZoomedSeriesData = getZoomedSeriesData({
114
+ const { zoomedSeriesData: nextZoomedSeriesData } = getZoomedSeriesData({
85
115
  seriesData: zoomedSeriesData,
86
116
  xAxis: data.xAxis,
87
117
  yAxes: data.yAxis,
@@ -117,12 +147,13 @@ export function useChartInnerProps(props) {
117
147
  }
118
148
  return acc;
119
149
  }, 0);
120
- 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 : {};
121
151
  const handleZoomReset = React.useCallback(() => {
122
152
  setZoomState({});
123
153
  }, []);
124
154
  return {
125
155
  svgBottomPos: bottom,
156
+ svgTopPos: top,
126
157
  svgXPos: x,
127
158
  boundsHeight,
128
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,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import get from 'lodash/get';
3
- import { block, getDataCategoryValue, getWaterfallPointSubtotal } from '../../utils';
3
+ import { block, getDataCategoryValue } from '../../utils';
4
4
  import { getFormattedValue } from '../../utils/chart/format';
5
5
  const b = block('tooltip');
6
6
  const DEFAULT_DATE_FORMAT = 'DD.MM.YY';
@@ -62,7 +62,7 @@ export const DefaultContent = ({ hovered, xAxis, yAxis, valueFormat }) => {
62
62
  measureValue && React.createElement("div", { className: b('series-name') }, measureValue),
63
63
  // eslint-disable-next-line complexity
64
64
  hovered.map((seriesItem, i) => {
65
- var _a;
65
+ var _a, _b;
66
66
  const { data, series, closest } = seriesItem;
67
67
  const id = `${get(series, 'id')}_${i}`;
68
68
  const color = get(series, 'color');
@@ -87,7 +87,7 @@ export const DefaultContent = ({ hovered, xAxis, yAxis, valueFormat }) => {
87
87
  }
88
88
  case 'waterfall': {
89
89
  const isTotal = get(data, 'total', false);
90
- const subTotalValue = getWaterfallPointSubtotal(data, series);
90
+ const subTotalValue = (_a = seriesItem.subTotal) !== null && _a !== void 0 ? _a : 0;
91
91
  const format = valueFormat !== null && valueFormat !== void 0 ? valueFormat : getDefaultValueFormat({ axis: yAxis });
92
92
  const subTotal = getFormattedValue({
93
93
  value: subTotalValue,
@@ -145,7 +145,7 @@ export const DefaultContent = ({ hovered, xAxis, yAxis, valueFormat }) => {
145
145
  }
146
146
  case 'sankey': {
147
147
  const { target, data: source } = seriesItem;
148
- const value = (_a = source.links.find((d) => d.name === (target === null || target === void 0 ? void 0 : target.name))) === null || _a === void 0 ? void 0 : _a.value;
148
+ const value = (_b = source.links.find((d) => d.name === (target === null || target === void 0 ? void 0 : target.name))) === null || _b === void 0 ? void 0 : _b.value;
149
149
  const formattedValue = getFormattedValue({
150
150
  value,
151
151
  format: valueFormat !== null && valueFormat !== void 0 ? valueFormat : { type: 'number' },
@@ -3,6 +3,7 @@ import { extent, scaleBand, scaleLinear, scaleLog, scaleUtc } from 'd3';
3
3
  import get from 'lodash/get';
4
4
  import { DEFAULT_AXIS_TYPE } from '../../constants';
5
5
  import { CHART_SERIES_WITH_VOLUME_ON_Y_AXIS, getAxisHeight, getDataCategoryValue, getDefaultMaxXAxisValue, getDefaultMinXAxisValue, getDomainDataXBySeries, getDomainDataYBySeries, getOnlyVisibleSeries, isAxisRelatedSeries, isSeriesWithCategoryValues, } from '../../utils';
6
+ const X_AXIS_ZOOM_PADDING = 0.02;
6
7
  const isNumericalArrayData = (data) => {
7
8
  return data.every((d) => typeof d === 'number' || d === null);
8
9
  };
@@ -103,8 +104,10 @@ export function createXScale(axis, series, boundsWidth, hasZoomX) {
103
104
  const xCategories = get(axis, 'categories');
104
105
  const xTimestamps = get(axis, 'timestamps');
105
106
  const maxPadding = get(axis, 'maxPadding', 0);
106
- const xAxisMinPadding = boundsWidth * maxPadding + calculateXAxisPadding(series);
107
- const xRange = [0, boundsWidth - xAxisMinPadding];
107
+ const xAxisMaxPadding = boundsWidth * maxPadding + calculateXAxisPadding(series);
108
+ const xAxisZoomPadding = boundsWidth * X_AXIS_ZOOM_PADDING;
109
+ const xRange = [0, boundsWidth - xAxisMaxPadding];
110
+ const xRangeZoom = [0 + xAxisZoomPadding, boundsWidth - xAxisZoomPadding];
108
111
  switch (xType) {
109
112
  case 'linear':
110
113
  case 'logarithmic': {
@@ -131,7 +134,9 @@ export function createXScale(axis, series, boundsWidth, hasZoomX) {
131
134
  : xMaxDomain;
132
135
  }
133
136
  const scaleFn = xType === 'logarithmic' ? scaleLog : scaleLinear;
134
- const scale = scaleFn().domain([xMin, xMax]).range(xRange);
137
+ const scale = scaleFn()
138
+ .domain([xMin, xMax])
139
+ .range(hasZoomX ? xRangeZoom : xRange);
135
140
  if (!hasZoomX) {
136
141
  scale.nice();
137
142
  }
@@ -147,7 +152,7 @@ export function createXScale(axis, series, boundsWidth, hasZoomX) {
147
152
  series: series,
148
153
  });
149
154
  const xScale = scaleBand().domain(filteredCategories).range([0, boundsWidth]);
150
- if (xScale.step() / 2 < xAxisMinPadding) {
155
+ if (xScale.step() / 2 < xAxisMaxPadding) {
151
156
  xScale.range(xRange);
152
157
  }
153
158
  return xScale;
@@ -159,7 +164,9 @@ export function createXScale(axis, series, boundsWidth, hasZoomX) {
159
164
  const [xMinTimestamp, xMaxTimestamp] = extent(xTimestamps);
160
165
  const xMin = typeof xMinProps === 'number' ? xMinProps : xMinTimestamp;
161
166
  const xMax = typeof xMaxProps === 'number' ? xMaxProps : xMaxTimestamp;
162
- const scale = scaleUtc().domain([xMin, xMax]).range(xRange);
167
+ const scale = scaleUtc()
168
+ .domain([xMin, xMax])
169
+ .range(hasZoomX ? xRangeZoom : xRange);
163
170
  if (!hasZoomX) {
164
171
  scale.nice();
165
172
  }
@@ -171,7 +178,9 @@ export function createXScale(axis, series, boundsWidth, hasZoomX) {
171
178
  const [xMinTimestamp, xMaxTimestamp] = extent(domain);
172
179
  const xMin = typeof xMinProps === 'number' ? xMinProps : xMinTimestamp;
173
180
  const xMax = typeof xMaxProps === 'number' ? xMaxProps : xMaxTimestamp;
174
- const scale = scaleUtc().domain([xMin, xMax]).range(xRange);
181
+ const scale = scaleUtc()
182
+ .domain([xMin, xMax])
183
+ .range(hasZoomX ? xRangeZoom : xRange);
175
184
  if (!hasZoomX) {
176
185
  scale.nice();
177
186
  }
@@ -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
  };
@@ -1,34 +1,16 @@
1
1
  import type { ChartData } from '../../types';
2
- import type { PreparedAxis, PreparedChart } from '../useChartOptions/types';
3
2
  import type { OnLegendItemClick, PreparedLegend, PreparedSeries } from './types';
4
3
  type Args = {
5
- chartWidth: number;
6
- chartHeight: number;
7
- chartMargin: PreparedChart['margin'];
8
4
  colors: string[];
9
5
  legend: ChartData['legend'];
10
6
  originalSeriesData: ChartData['series']['data'];
11
7
  seriesData: ChartData['series']['data'];
12
8
  seriesOptions: ChartData['series']['options'];
13
- preparedYAxis: PreparedAxis[];
9
+ preparedLegend?: PreparedLegend;
14
10
  };
15
11
  export declare const useSeries: (args: Args) => {
16
- legendItems: never[] | import("./types").LegendItem[][];
17
- legendConfig: {
18
- offset: {
19
- left: number;
20
- top: number;
21
- };
22
- pagination: {
23
- pages: {
24
- start: number;
25
- end: number;
26
- }[];
27
- } | undefined;
28
- } | undefined;
29
12
  preparedLegend: PreparedLegend | null;
30
13
  preparedSeries: PreparedSeries[];
31
- preparedSeriesOptions: import("../../constants").SeriesOptionsDefaults;
32
14
  handleLegendItemClick: OnLegendItemClick;
33
15
  };
34
16
  export {};
@@ -2,16 +2,17 @@ import React from 'react';
2
2
  import { group, scaleOrdinal } from 'd3';
3
3
  import { getSeriesNames } from '../../utils';
4
4
  import { usePrevious } from '../usePrevious';
5
- import { getLegendComponents, getPreparedLegend } from './prepare-legend';
6
- import { getPreparedOptions } from './prepare-options';
5
+ import { getPreparedLegend } from './prepare-legend';
7
6
  import { prepareSeries } from './prepareSeries';
8
7
  import { getActiveLegendItems, getAllLegendItems } from './utils';
9
8
  export const useSeries = (args) => {
10
- const { chartWidth, chartHeight, chartMargin, legend, originalSeriesData, preparedYAxis, seriesData, seriesOptions, colors, } = args;
11
- const [preparedLegend, setPreparedLegend] = React.useState(null);
9
+ const { legend, originalSeriesData, seriesData, seriesOptions, colors, preparedLegend: preparedLegendProps = null, } = args;
10
+ const [preparedLegend, setPreparedLegend] = React.useState(preparedLegendProps);
12
11
  React.useEffect(() => {
13
- getPreparedLegend({ legend, series: seriesData }).then((value) => setPreparedLegend(value));
14
- }, [legend, seriesData]);
12
+ if (!preparedLegendProps) {
13
+ getPreparedLegend({ legend, series: seriesData }).then((value) => setPreparedLegend(value));
14
+ }
15
+ }, [legend, preparedLegendProps, seriesData]);
15
16
  const [preparedSeries, setPreparedSeries] = React.useState([]);
16
17
  const [activeLegendItems, setActiveLegendItems] = React.useState(getActiveLegendItems(preparedSeries));
17
18
  React.useEffect(() => {
@@ -39,9 +40,6 @@ export const useSeries = (args) => {
39
40
  setActiveLegendItems(getActiveLegendItems(acc));
40
41
  })();
41
42
  }, [seriesData, seriesOptions, preparedLegend, colors]);
42
- const preparedSeriesOptions = React.useMemo(() => {
43
- return getPreparedOptions(seriesOptions);
44
- }, [seriesOptions]);
45
43
  const prevOriginalSeriesData = usePrevious(originalSeriesData);
46
44
  const chartSeries = React.useMemo(() => {
47
45
  return preparedSeries.map((singleSeries) => {
@@ -51,19 +49,6 @@ export const useSeries = (args) => {
51
49
  return singleSeries;
52
50
  });
53
51
  }, [preparedSeries, activeLegendItems]);
54
- const { legendConfig, legendItems } = React.useMemo(() => {
55
- if (!preparedLegend) {
56
- return { legendConfig: undefined, legendItems: [] };
57
- }
58
- return getLegendComponents({
59
- chartHeight,
60
- chartMargin,
61
- chartWidth,
62
- series: chartSeries,
63
- preparedLegend,
64
- preparedYAxis,
65
- });
66
- }, [chartWidth, chartHeight, chartMargin, chartSeries, preparedLegend, preparedYAxis]);
67
52
  const handleLegendItemClick = React.useCallback(({ name, metaKey }) => {
68
53
  const allItems = getAllLegendItems(preparedSeries);
69
54
  const onlyItemSelected = activeLegendItems.length === 1 && activeLegendItems.includes(name);
@@ -91,11 +76,8 @@ export const useSeries = (args) => {
91
76
  }
92
77
  }, [originalSeriesData, prevOriginalSeriesData, preparedSeries]);
93
78
  return {
94
- legendItems,
95
- legendConfig,
96
79
  preparedLegend,
97
80
  preparedSeries: chartSeries,
98
- preparedSeriesOptions,
99
81
  handleLegendItemClick,
100
82
  };
101
83
  };
@@ -1,4 +1,3 @@
1
- import { sort } from 'd3';
2
1
  import get from 'lodash/get';
3
2
  import merge from 'lodash/merge';
4
3
  import { DEFAULT_DATALABELS_STYLE } from '../../constants';
@@ -46,7 +45,7 @@ export function prepareArea(args) {
46
45
  enabled: get(series, 'legend.enabled', legend.enabled),
47
46
  symbol: prepareLegendSymbol(series),
48
47
  },
49
- data: sort(series.data, (d) => d.x),
48
+ data: series.data,
50
49
  stacking: series.stacking,
51
50
  stackId: getSeriesStackId(series),
52
51
  dataLabels: {
@@ -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[];
@@ -36,7 +36,7 @@ function getXValues(series, xAxis, xScale) {
36
36
  ? getDataCategoryValue({ axisDirection: 'x', categories, data: d })
37
37
  : d.x);
38
38
  if (!acc.has(key)) {
39
- acc.set(key, getXValue({ point: d, xAxis, xScale }));
39
+ acc.set(key, getXValue({ point: d, points: s.data, xAxis, xScale }));
40
40
  }
41
41
  });
42
42
  return acc;
@@ -73,6 +73,7 @@ export const prepareAreaData = async (args) => {
73
73
  const seriesYScale = yScale[yAxisIndex];
74
74
  const yMin = getYValue({
75
75
  point: { y: 0 },
76
+ points: s.data,
76
77
  yAxis: seriesYAxis,
77
78
  yScale: seriesYScale,
78
79
  });
@@ -51,8 +51,9 @@ export const prepareLineData = async (args) => {
51
51
  const yAxisTop = ((_a = split.plots[seriesYAxis.plotIndex]) === null || _a === void 0 ? void 0 : _a.top) || 0;
52
52
  const seriesYScale = yScale[s.yAxis];
53
53
  const points = s.data.map((d) => ({
54
- x: getXValue({ point: d, xAxis, xScale }),
55
- y: yAxisTop + getYValue({ point: d, yAxis: seriesYAxis, yScale: seriesYScale }),
54
+ x: getXValue({ point: d, points: s.data, xAxis, xScale }),
55
+ y: yAxisTop +
56
+ getYValue({ point: d, points: s.data, yAxis: seriesYAxis, yScale: seriesYScale }),
56
57
  active: true,
57
58
  data: d,
58
59
  series: s,
@@ -6,6 +6,9 @@ export declare function getXValue(args: {
6
6
  point: {
7
7
  x?: number | string;
8
8
  };
9
+ points?: {
10
+ x?: number | string;
11
+ }[];
9
12
  xAxis: PreparedAxis;
10
13
  xScale: ChartScale;
11
14
  }): number;
@@ -13,6 +16,9 @@ export declare function getYValue(args: {
13
16
  point: {
14
17
  y?: number | string;
15
18
  };
19
+ points?: {
20
+ y?: number | string;
21
+ }[];
16
22
  yAxis: PreparedAxis;
17
23
  yScale: ChartScale;
18
24
  }): number;
@@ -1,26 +1,51 @@
1
1
  import { path, select } from 'd3';
2
2
  import get from 'lodash/get';
3
3
  import { getDataCategoryValue } from '../../utils';
4
+ const ONE_POINT_DOMAIN_DATA_CAPACITY = 3;
4
5
  export function getXValue(args) {
5
- const { point, xAxis, xScale } = args;
6
+ const { point, points, xAxis, xScale } = args;
6
7
  if (xAxis.type === 'category') {
7
8
  const xBandScale = xScale;
8
9
  const categories = get(xAxis, 'categories', []);
9
10
  const dataCategory = getDataCategoryValue({ axisDirection: 'x', categories, data: point });
10
11
  return (xBandScale(dataCategory) || 0) + xBandScale.step() / 2;
11
12
  }
12
- const xLinearScale = xScale;
13
+ let xLinearScale = xScale;
14
+ const [xMinDomain, xMaxDomain] = xLinearScale.domain();
15
+ if (Number(xMinDomain) === Number(xMaxDomain) &&
16
+ (points === null || points === void 0 ? void 0 : points.length) === ONE_POINT_DOMAIN_DATA_CAPACITY) {
17
+ const x1 = points[0].x;
18
+ const xTarget = points[1].x;
19
+ const x3 = points[2].x;
20
+ const xMin = Math.min(x1, xTarget, x3);
21
+ const xMax = Math.max(x1, xTarget, x3);
22
+ xLinearScale = xLinearScale
23
+ .copy()
24
+ .domain([xMin + (xTarget - xMin) / 2, xMax - (xMax - xTarget) / 2]);
25
+ }
13
26
  return xLinearScale(point.x);
14
27
  }
15
28
  export function getYValue(args) {
16
- const { point, yAxis, yScale } = args;
29
+ const { point, points, yAxis, yScale } = args;
17
30
  if (yAxis.type === 'category') {
18
31
  const yBandScale = yScale;
19
32
  const categories = get(yAxis, 'categories', []);
20
33
  const dataCategory = getDataCategoryValue({ axisDirection: 'y', categories, data: point });
21
34
  return (yBandScale(dataCategory) || 0) + yBandScale.step() / 2;
22
35
  }
23
- const yLinearScale = yScale;
36
+ let yLinearScale = yScale;
37
+ const [yMinDomain, yMaxDomain] = yLinearScale.domain();
38
+ if (Number(yMinDomain) === Number(yMaxDomain) &&
39
+ (points === null || points === void 0 ? void 0 : points.length) === ONE_POINT_DOMAIN_DATA_CAPACITY) {
40
+ const y1 = points[0].y;
41
+ const yTarget = points[1].y;
42
+ const y2 = points[2].y;
43
+ const yMin = Math.min(y1, yTarget, y2);
44
+ const yMax = Math.max(y1, yTarget, y2);
45
+ yLinearScale = yLinearScale
46
+ .copy()
47
+ .domain([yMin + (yTarget - yMin) / 2, yMax - (yMax - yTarget) / 2]);
48
+ }
24
49
  return yLinearScale(point.y);
25
50
  }
26
51
  export function shapeKey(d) {
@@ -125,7 +125,7 @@ export const WaterfallSeriesShapes = (args) => {
125
125
  return () => {
126
126
  dispatcher.on('hover-shape.waterfall', null);
127
127
  };
128
- }, [dispatcher, preparedData, seriesOptions]);
128
+ }, [connectorSelector, dispatcher, preparedData, seriesOptions]);
129
129
  return (React.createElement(React.Fragment, null,
130
130
  React.createElement("g", { ref: ref, className: b(), clipPath: `url(#${clipPathId})` }),
131
131
  React.createElement(HtmlLayer, { preparedData: preparedData, htmlLayout: htmlLayout })));
@@ -1,5 +1,9 @@
1
1
  import type { FormatNumberOptions, FormatOptions } from './types';
2
- export declare const formatBytes: (value: number, options?: FormatOptions) => string;
3
- export declare const formatDuration: (value: number, options?: FormatOptions) => string;
4
- export declare const getNumberUnitRate: (value: number) => number;
2
+ export declare const formatBytes: (value: number, options?: FormatOptions & {
3
+ unitRate?: number;
4
+ }) => string;
5
+ export declare const formatDuration: (value: number, options?: FormatOptions & {
6
+ unitRate?: number;
7
+ }) => string;
8
+ export declare const getDefaultUnit: (value: number) => string | undefined;
5
9
  export declare const formatNumber: (value: number | string, options?: FormatNumberOptions) => string;