@gravity-ui/charts 1.11.0 → 1.11.1

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.
@@ -125,6 +125,9 @@ export const AxisY = (props) => {
125
125
  scale: seriesScale,
126
126
  });
127
127
  yAxisGenerator(axisItem);
128
+ // because the standard generator interrupts the desired font
129
+ // https://github.com/d3/d3-axis/blob/main/src/axis.js#L110
130
+ axisItem.attr('font-family', null);
128
131
  if (d.labels.enabled) {
129
132
  const labels = axisItem.selectAll('.tick text');
130
133
  const tickTexts = labels
@@ -23,10 +23,12 @@ export function useChartInnerProps(props) {
23
23
  }, [data.series.data, data.xAxis, data.yAxis, zoomState]);
24
24
  const [xAxis, setXAxis] = React.useState(null);
25
25
  React.useEffect(() => {
26
+ setXAxis(null);
26
27
  getPreparedXAxis({ xAxis: data.xAxis, width, seriesData: zoomedSeriesData }).then((val) => setXAxis(val));
27
28
  }, [data.xAxis, width, zoomedSeriesData]);
28
29
  const [yAxis, setYAxis] = React.useState([]);
29
30
  React.useEffect(() => {
31
+ setYAxis([]);
30
32
  getPreparedYAxis({ yAxis: data.yAxis, height, seriesData: zoomedSeriesData }).then((val) => setYAxis(val));
31
33
  }, [data.yAxis, height, zoomedSeriesData]);
32
34
  const { legendItems, legendConfig, preparedSeries, preparedSeriesOptions, preparedLegend, handleLegendItemClick, } = useSeries({
@@ -321,6 +321,7 @@ export const Legend = (props) => {
321
321
  .call(xAxisGenerator);
322
322
  legendWidth = legend.width;
323
323
  }
324
+ const legendTitleClassname = b('title');
324
325
  if (legend.title.enable) {
325
326
  const { maxWidth: titleWidth } = await getLabelsSize({
326
327
  labels: [legend.title.text],
@@ -342,7 +343,6 @@ export const Legend = (props) => {
342
343
  break;
343
344
  }
344
345
  }
345
- const legendTitleClassname = b('title');
346
346
  svgElement.selectAll(`.${legendTitleClassname}`).remove();
347
347
  svgElement
348
348
  .append('g')
@@ -355,6 +355,9 @@ export const Legend = (props) => {
355
355
  .style('dominant-baseline', 'text-before-edge')
356
356
  .html(legend.title.text);
357
357
  }
358
+ else {
359
+ svgElement.selectAll(`.${legendTitleClassname}`).remove();
360
+ }
358
361
  const { left } = getLegendPosition({
359
362
  align: legend.align,
360
363
  width: boundsWidth,
@@ -1,3 +1,4 @@
1
+ import { sort } from 'd3';
1
2
  import get from 'lodash/get';
2
3
  import merge from 'lodash/merge';
3
4
  import { DEFAULT_DATALABELS_STYLE } from '../../constants';
@@ -45,7 +46,7 @@ export function prepareArea(args) {
45
46
  enabled: get(series, 'legend.enabled', legend.enabled),
46
47
  symbol: prepareLegendSymbol(series),
47
48
  },
48
- data: series.data,
49
+ data: sort(series.data, (d) => d.x),
49
50
  stacking: series.stacking,
50
51
  stackId: getSeriesStackId(series),
51
52
  dataLabels: {
@@ -3,7 +3,7 @@ import clone from 'lodash/clone';
3
3
  import get from 'lodash/get';
4
4
  import merge from 'lodash/merge';
5
5
  import { CONTINUOUS_LEGEND_SIZE, legendDefaults } from '../../constants';
6
- import { getDefaultColorStops, getDomainForContinuousColorScale, getHorisontalSvgTextHeight, getLabelsSize, } from '../../utils';
6
+ import { getDefaultColorStops, getDomainForContinuousColorScale, getLabelsSize } from '../../utils';
7
7
  import { getBoundsWidth } from '../useChartDimensions';
8
8
  import { getYAxisWidth } from '../useChartDimensions/utils';
9
9
  export async function getPreparedLegend(args) {
@@ -13,7 +13,7 @@ export async function getPreparedLegend(args) {
13
13
  const defaultItemStyle = clone(legendDefaults.itemStyle);
14
14
  const itemStyle = get(legend, 'itemStyle');
15
15
  const computedItemStyle = merge(defaultItemStyle, itemStyle);
16
- const lineHeight = getHorisontalSvgTextHeight({ text: 'Tmp', style: computedItemStyle });
16
+ const lineHeight = (await getLabelsSize({ labels: ['Tmp'], style: computedItemStyle })).maxHeight;
17
17
  const legendType = get(legend, 'type', 'discrete');
18
18
  const isTitleEnabled = Boolean((_a = legend === null || legend === void 0 ? void 0 : legend.title) === null || _a === void 0 ? void 0 : _a.text);
19
19
  const titleMargin = isTitleEnabled ? get(legend, 'title.margin', 4) : 0;
@@ -1,4 +1,4 @@
1
- import { group, sort } from 'd3';
1
+ import { group } from 'd3';
2
2
  import { getDataCategoryValue, getLabelsSize, getLeftPosition } from '../../../utils';
3
3
  import { getFormattedValue } from '../../../utils/chart/format';
4
4
  import { getXValue, getYValue } from '../utils';
@@ -50,7 +50,7 @@ function getXValues(series, xAxis, xScale) {
50
50
  return acc;
51
51
  }, []);
52
52
  }
53
- return sort(Array.from(xValues), ([_x, xValue]) => xValue);
53
+ return Array.from(xValues);
54
54
  }
55
55
  export const prepareAreaData = async (args) => {
56
56
  const { series, xAxis, xScale, yAxis, yScale, boundsHeight: plotHeight, isOutsideBounds } = args;
@@ -23,160 +23,167 @@ export const useShapes = (args) => {
23
23
  const { boundsWidth, boundsHeight, dispatcher, series, seriesOptions, xAxis, xScale, yAxis, yScale, split, htmlLayout, clipPathId, isOutsideBounds, } = args;
24
24
  const [shapesElemens, setShapesElements] = React.useState([]);
25
25
  const [shapesElemensData, setShapesElemensData] = React.useState([]);
26
- const setShapes = React.useCallback(async () => {
27
- const visibleSeries = getOnlyVisibleSeries(series);
28
- const groupedSeries = group(visibleSeries, (item) => item.type);
29
- const shapesData = [];
30
- const shapes = [];
31
- await Promise.all(
32
- // eslint-disable-next-line complexity
33
- Array.from(groupedSeries).map(async (item) => {
34
- const [seriesType, chartSeries] = item;
35
- switch (seriesType) {
36
- case 'bar-x': {
37
- if (xAxis && xScale && yScale) {
38
- const preparedData = await prepareBarXData({
39
- series: chartSeries,
40
- seriesOptions,
41
- xAxis,
42
- xScale,
43
- yAxis,
44
- yScale,
45
- boundsHeight,
46
- });
47
- shapes.push(React.createElement(BarXSeriesShapes, { key: "bar-x", dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData, htmlLayout: htmlLayout, clipPathId: clipPathId }));
48
- shapesData.push(...preparedData);
26
+ const countedRef = React.useRef(0);
27
+ React.useEffect(() => {
28
+ countedRef.current++;
29
+ (async () => {
30
+ const currentRun = countedRef.current;
31
+ const visibleSeries = getOnlyVisibleSeries(series);
32
+ const groupedSeries = group(visibleSeries, (item) => item.type);
33
+ const shapesData = [];
34
+ const shapes = [];
35
+ await Promise.all(
36
+ // eslint-disable-next-line complexity
37
+ Array.from(groupedSeries).map(async (item) => {
38
+ const [seriesType, chartSeries] = item;
39
+ switch (seriesType) {
40
+ case 'bar-x': {
41
+ if (xAxis && xScale && (yScale === null || yScale === void 0 ? void 0 : yScale.length)) {
42
+ const preparedData = await prepareBarXData({
43
+ series: chartSeries,
44
+ seriesOptions,
45
+ xAxis,
46
+ xScale,
47
+ yAxis,
48
+ yScale,
49
+ boundsHeight,
50
+ });
51
+ shapes.push(React.createElement(BarXSeriesShapes, { key: "bar-x", dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData, htmlLayout: htmlLayout, clipPathId: clipPathId }));
52
+ shapesData.push(...preparedData);
53
+ }
54
+ break;
49
55
  }
50
- break;
51
- }
52
- case 'bar-y': {
53
- if (xAxis && xScale && yScale) {
54
- const preparedData = await prepareBarYData({
56
+ case 'bar-y': {
57
+ if (xAxis && xScale && (yScale === null || yScale === void 0 ? void 0 : yScale.length)) {
58
+ const preparedData = await prepareBarYData({
59
+ series: chartSeries,
60
+ seriesOptions,
61
+ xAxis,
62
+ xScale,
63
+ yAxis,
64
+ yScale,
65
+ });
66
+ shapes.push(React.createElement(BarYSeriesShapes, { key: "bar-y", dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData, htmlLayout: htmlLayout, clipPathId: clipPathId }));
67
+ shapesData.push(...preparedData);
68
+ }
69
+ break;
70
+ }
71
+ case 'waterfall': {
72
+ if (xAxis && xScale && (yScale === null || yScale === void 0 ? void 0 : yScale.length)) {
73
+ const preparedData = await prepareWaterfallData({
74
+ series: chartSeries,
75
+ seriesOptions,
76
+ xAxis,
77
+ xScale,
78
+ yAxis,
79
+ yScale,
80
+ });
81
+ shapes.push(React.createElement(WaterfallSeriesShapes, { key: "waterfall", dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData, htmlLayout: htmlLayout, clipPathId: clipPathId }));
82
+ shapesData.push(...preparedData);
83
+ }
84
+ break;
85
+ }
86
+ case 'line': {
87
+ if (xAxis && xScale && (yScale === null || yScale === void 0 ? void 0 : yScale.length)) {
88
+ const preparedData = await prepareLineData({
89
+ series: chartSeries,
90
+ xAxis,
91
+ xScale,
92
+ yAxis,
93
+ yScale,
94
+ split,
95
+ isOutsideBounds,
96
+ });
97
+ shapes.push(React.createElement(LineSeriesShapes, { key: "line", dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData, htmlLayout: htmlLayout, clipPathId: clipPathId }));
98
+ shapesData.push(...preparedData);
99
+ }
100
+ break;
101
+ }
102
+ case 'area': {
103
+ if (xAxis && xScale && (yScale === null || yScale === void 0 ? void 0 : yScale.length)) {
104
+ const preparedData = await prepareAreaData({
105
+ series: chartSeries,
106
+ xAxis,
107
+ xScale,
108
+ yAxis,
109
+ yScale,
110
+ boundsHeight,
111
+ isOutsideBounds,
112
+ });
113
+ shapes.push(React.createElement(AreaSeriesShapes, { key: "area", dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData, htmlLayout: htmlLayout, clipPathId: clipPathId }));
114
+ shapesData.push(...preparedData);
115
+ }
116
+ break;
117
+ }
118
+ case 'scatter': {
119
+ if (xAxis && xScale && (yScale === null || yScale === void 0 ? void 0 : yScale.length)) {
120
+ const preparedData = prepareScatterData({
121
+ series: chartSeries,
122
+ xAxis,
123
+ xScale,
124
+ yAxis,
125
+ yScale,
126
+ isOutsideBounds,
127
+ });
128
+ shapes.push(React.createElement(ScatterSeriesShape, { key: "scatter", dispatcher: dispatcher, preparedData: preparedData, seriesOptions: seriesOptions, htmlLayout: htmlLayout }));
129
+ shapesData.push(...preparedData);
130
+ }
131
+ break;
132
+ }
133
+ case 'pie': {
134
+ const preparedData = await preparePieData({
55
135
  series: chartSeries,
56
- seriesOptions,
57
- xAxis,
58
- xScale,
59
- yAxis,
60
- yScale,
136
+ boundsWidth,
137
+ boundsHeight,
61
138
  });
62
- shapes.push(React.createElement(BarYSeriesShapes, { key: "bar-y", dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData, htmlLayout: htmlLayout, clipPathId: clipPathId }));
139
+ shapes.push(React.createElement(PieSeriesShapes, { key: "pie", dispatcher: dispatcher, preparedData: preparedData, seriesOptions: seriesOptions, htmlLayout: htmlLayout }));
63
140
  shapesData.push(...preparedData);
141
+ break;
64
142
  }
65
- break;
66
- }
67
- case 'waterfall': {
68
- if (xAxis && xScale && yScale) {
69
- const preparedData = await prepareWaterfallData({
70
- series: chartSeries,
71
- seriesOptions,
72
- xAxis,
73
- xScale,
74
- yAxis,
75
- yScale,
143
+ case 'treemap': {
144
+ const preparedData = await prepareTreemapData({
145
+ // We should have exactly one series with "treemap" type
146
+ // Otherwise data validation should emit an error
147
+ series: chartSeries[0],
148
+ width: boundsWidth,
149
+ height: boundsHeight,
76
150
  });
77
- shapes.push(React.createElement(WaterfallSeriesShapes, { key: "waterfall", dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData, htmlLayout: htmlLayout, clipPathId: clipPathId }));
78
- shapesData.push(...preparedData);
151
+ shapes.push(React.createElement(TreemapSeriesShape, { key: "treemap", dispatcher: dispatcher, preparedData: preparedData, seriesOptions: seriesOptions, htmlLayout: htmlLayout }));
152
+ shapesData.push(preparedData);
153
+ break;
79
154
  }
80
- break;
81
- }
82
- case 'line': {
83
- if (xAxis && xScale && yScale) {
84
- const preparedData = await prepareLineData({
85
- series: chartSeries,
86
- xAxis,
87
- xScale,
88
- yAxis,
89
- yScale,
90
- split,
91
- isOutsideBounds,
155
+ case 'sankey': {
156
+ const preparedData = prepareSankeyData({
157
+ series: chartSeries[0],
158
+ width: boundsWidth,
159
+ height: boundsHeight,
92
160
  });
93
- shapes.push(React.createElement(LineSeriesShapes, { key: "line", dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData, htmlLayout: htmlLayout, clipPathId: clipPathId }));
94
- shapesData.push(...preparedData);
161
+ shapes.push(React.createElement(SankeySeriesShape, { key: "sankey", dispatcher: dispatcher, preparedData: preparedData, seriesOptions: seriesOptions, htmlLayout: htmlLayout }));
162
+ shapesData.push(preparedData);
163
+ break;
95
164
  }
96
- break;
97
- }
98
- case 'area': {
99
- if (xAxis && xScale && yScale) {
100
- const preparedData = await prepareAreaData({
165
+ case 'radar': {
166
+ const preparedData = await prepareRadarData({
101
167
  series: chartSeries,
102
- xAxis,
103
- xScale,
104
- yAxis,
105
- yScale,
168
+ boundsWidth,
106
169
  boundsHeight,
107
- isOutsideBounds,
108
170
  });
109
- shapes.push(React.createElement(AreaSeriesShapes, { key: "area", dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData, htmlLayout: htmlLayout, clipPathId: clipPathId }));
171
+ shapes.push(React.createElement(RadarSeriesShapes, { key: "radar", dispatcher: dispatcher, series: preparedData, seriesOptions: seriesOptions, htmlLayout: htmlLayout }));
110
172
  shapesData.push(...preparedData);
173
+ break;
111
174
  }
112
- break;
113
- }
114
- case 'scatter': {
115
- if (xAxis && xScale && yScale) {
116
- const preparedData = prepareScatterData({
117
- series: chartSeries,
118
- xAxis,
119
- xScale,
120
- yAxis,
121
- yScale,
122
- isOutsideBounds,
175
+ default: {
176
+ throw new ChartError({
177
+ message: `The display method is not defined for a series with type "${seriesType}"`,
123
178
  });
124
- shapes.push(React.createElement(ScatterSeriesShape, { key: "scatter", dispatcher: dispatcher, preparedData: preparedData, seriesOptions: seriesOptions, htmlLayout: htmlLayout }));
125
- shapesData.push(...preparedData);
126
179
  }
127
- break;
128
- }
129
- case 'pie': {
130
- const preparedData = await preparePieData({
131
- series: chartSeries,
132
- boundsWidth,
133
- boundsHeight,
134
- });
135
- shapes.push(React.createElement(PieSeriesShapes, { key: "pie", dispatcher: dispatcher, preparedData: preparedData, seriesOptions: seriesOptions, htmlLayout: htmlLayout }));
136
- shapesData.push(...preparedData);
137
- break;
138
- }
139
- case 'treemap': {
140
- const preparedData = await prepareTreemapData({
141
- // We should have exactly one series with "treemap" type
142
- // Otherwise data validation should emit an error
143
- series: chartSeries[0],
144
- width: boundsWidth,
145
- height: boundsHeight,
146
- });
147
- shapes.push(React.createElement(TreemapSeriesShape, { key: "treemap", dispatcher: dispatcher, preparedData: preparedData, seriesOptions: seriesOptions, htmlLayout: htmlLayout }));
148
- shapesData.push(preparedData);
149
- break;
150
- }
151
- case 'sankey': {
152
- const preparedData = prepareSankeyData({
153
- series: chartSeries[0],
154
- width: boundsWidth,
155
- height: boundsHeight,
156
- });
157
- shapes.push(React.createElement(SankeySeriesShape, { key: "sankey", dispatcher: dispatcher, preparedData: preparedData, seriesOptions: seriesOptions, htmlLayout: htmlLayout }));
158
- shapesData.push(preparedData);
159
- break;
160
- }
161
- case 'radar': {
162
- const preparedData = await prepareRadarData({
163
- series: chartSeries,
164
- boundsWidth,
165
- boundsHeight,
166
- });
167
- shapes.push(React.createElement(RadarSeriesShapes, { key: "radar", dispatcher: dispatcher, series: preparedData, seriesOptions: seriesOptions, htmlLayout: htmlLayout }));
168
- shapesData.push(...preparedData);
169
- break;
170
- }
171
- default: {
172
- throw new ChartError({
173
- message: `The display method is not defined for a series with type "${seriesType}"`,
174
- });
175
180
  }
181
+ }));
182
+ if (countedRef.current === currentRun) {
183
+ setShapesElements(shapes);
184
+ setShapesElemensData(shapesData);
176
185
  }
177
- }));
178
- setShapesElements(shapes);
179
- setShapesElemensData(shapesData);
186
+ })();
180
187
  }, [
181
188
  boundsHeight,
182
189
  boundsWidth,
@@ -192,8 +199,5 @@ export const useShapes = (args) => {
192
199
  clipPathId,
193
200
  isOutsideBounds,
194
201
  ]);
195
- React.useEffect(() => {
196
- setShapes();
197
- }, [setShapes]);
198
202
  return { shapes: shapesElemens, shapesData: shapesElemensData };
199
203
  };
@@ -125,6 +125,9 @@ export const AxisY = (props) => {
125
125
  scale: seriesScale,
126
126
  });
127
127
  yAxisGenerator(axisItem);
128
+ // because the standard generator interrupts the desired font
129
+ // https://github.com/d3/d3-axis/blob/main/src/axis.js#L110
130
+ axisItem.attr('font-family', null);
128
131
  if (d.labels.enabled) {
129
132
  const labels = axisItem.selectAll('.tick text');
130
133
  const tickTexts = labels
@@ -23,10 +23,12 @@ export function useChartInnerProps(props) {
23
23
  }, [data.series.data, data.xAxis, data.yAxis, zoomState]);
24
24
  const [xAxis, setXAxis] = React.useState(null);
25
25
  React.useEffect(() => {
26
+ setXAxis(null);
26
27
  getPreparedXAxis({ xAxis: data.xAxis, width, seriesData: zoomedSeriesData }).then((val) => setXAxis(val));
27
28
  }, [data.xAxis, width, zoomedSeriesData]);
28
29
  const [yAxis, setYAxis] = React.useState([]);
29
30
  React.useEffect(() => {
31
+ setYAxis([]);
30
32
  getPreparedYAxis({ yAxis: data.yAxis, height, seriesData: zoomedSeriesData }).then((val) => setYAxis(val));
31
33
  }, [data.yAxis, height, zoomedSeriesData]);
32
34
  const { legendItems, legendConfig, preparedSeries, preparedSeriesOptions, preparedLegend, handleLegendItemClick, } = useSeries({
@@ -321,6 +321,7 @@ export const Legend = (props) => {
321
321
  .call(xAxisGenerator);
322
322
  legendWidth = legend.width;
323
323
  }
324
+ const legendTitleClassname = b('title');
324
325
  if (legend.title.enable) {
325
326
  const { maxWidth: titleWidth } = await getLabelsSize({
326
327
  labels: [legend.title.text],
@@ -342,7 +343,6 @@ export const Legend = (props) => {
342
343
  break;
343
344
  }
344
345
  }
345
- const legendTitleClassname = b('title');
346
346
  svgElement.selectAll(`.${legendTitleClassname}`).remove();
347
347
  svgElement
348
348
  .append('g')
@@ -355,6 +355,9 @@ export const Legend = (props) => {
355
355
  .style('dominant-baseline', 'text-before-edge')
356
356
  .html(legend.title.text);
357
357
  }
358
+ else {
359
+ svgElement.selectAll(`.${legendTitleClassname}`).remove();
360
+ }
358
361
  const { left } = getLegendPosition({
359
362
  align: legend.align,
360
363
  width: boundsWidth,
@@ -1,3 +1,4 @@
1
+ import { sort } from 'd3';
1
2
  import get from 'lodash/get';
2
3
  import merge from 'lodash/merge';
3
4
  import { DEFAULT_DATALABELS_STYLE } from '../../constants';
@@ -45,7 +46,7 @@ export function prepareArea(args) {
45
46
  enabled: get(series, 'legend.enabled', legend.enabled),
46
47
  symbol: prepareLegendSymbol(series),
47
48
  },
48
- data: series.data,
49
+ data: sort(series.data, (d) => d.x),
49
50
  stacking: series.stacking,
50
51
  stackId: getSeriesStackId(series),
51
52
  dataLabels: {
@@ -3,7 +3,7 @@ import clone from 'lodash/clone';
3
3
  import get from 'lodash/get';
4
4
  import merge from 'lodash/merge';
5
5
  import { CONTINUOUS_LEGEND_SIZE, legendDefaults } from '../../constants';
6
- import { getDefaultColorStops, getDomainForContinuousColorScale, getHorisontalSvgTextHeight, getLabelsSize, } from '../../utils';
6
+ import { getDefaultColorStops, getDomainForContinuousColorScale, getLabelsSize } from '../../utils';
7
7
  import { getBoundsWidth } from '../useChartDimensions';
8
8
  import { getYAxisWidth } from '../useChartDimensions/utils';
9
9
  export async function getPreparedLegend(args) {
@@ -13,7 +13,7 @@ export async function getPreparedLegend(args) {
13
13
  const defaultItemStyle = clone(legendDefaults.itemStyle);
14
14
  const itemStyle = get(legend, 'itemStyle');
15
15
  const computedItemStyle = merge(defaultItemStyle, itemStyle);
16
- const lineHeight = getHorisontalSvgTextHeight({ text: 'Tmp', style: computedItemStyle });
16
+ const lineHeight = (await getLabelsSize({ labels: ['Tmp'], style: computedItemStyle })).maxHeight;
17
17
  const legendType = get(legend, 'type', 'discrete');
18
18
  const isTitleEnabled = Boolean((_a = legend === null || legend === void 0 ? void 0 : legend.title) === null || _a === void 0 ? void 0 : _a.text);
19
19
  const titleMargin = isTitleEnabled ? get(legend, 'title.margin', 4) : 0;
@@ -1,4 +1,4 @@
1
- import { group, sort } from 'd3';
1
+ import { group } from 'd3';
2
2
  import { getDataCategoryValue, getLabelsSize, getLeftPosition } from '../../../utils';
3
3
  import { getFormattedValue } from '../../../utils/chart/format';
4
4
  import { getXValue, getYValue } from '../utils';
@@ -50,7 +50,7 @@ function getXValues(series, xAxis, xScale) {
50
50
  return acc;
51
51
  }, []);
52
52
  }
53
- return sort(Array.from(xValues), ([_x, xValue]) => xValue);
53
+ return Array.from(xValues);
54
54
  }
55
55
  export const prepareAreaData = async (args) => {
56
56
  const { series, xAxis, xScale, yAxis, yScale, boundsHeight: plotHeight, isOutsideBounds } = args;
@@ -23,160 +23,167 @@ export const useShapes = (args) => {
23
23
  const { boundsWidth, boundsHeight, dispatcher, series, seriesOptions, xAxis, xScale, yAxis, yScale, split, htmlLayout, clipPathId, isOutsideBounds, } = args;
24
24
  const [shapesElemens, setShapesElements] = React.useState([]);
25
25
  const [shapesElemensData, setShapesElemensData] = React.useState([]);
26
- const setShapes = React.useCallback(async () => {
27
- const visibleSeries = getOnlyVisibleSeries(series);
28
- const groupedSeries = group(visibleSeries, (item) => item.type);
29
- const shapesData = [];
30
- const shapes = [];
31
- await Promise.all(
32
- // eslint-disable-next-line complexity
33
- Array.from(groupedSeries).map(async (item) => {
34
- const [seriesType, chartSeries] = item;
35
- switch (seriesType) {
36
- case 'bar-x': {
37
- if (xAxis && xScale && yScale) {
38
- const preparedData = await prepareBarXData({
39
- series: chartSeries,
40
- seriesOptions,
41
- xAxis,
42
- xScale,
43
- yAxis,
44
- yScale,
45
- boundsHeight,
46
- });
47
- shapes.push(React.createElement(BarXSeriesShapes, { key: "bar-x", dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData, htmlLayout: htmlLayout, clipPathId: clipPathId }));
48
- shapesData.push(...preparedData);
26
+ const countedRef = React.useRef(0);
27
+ React.useEffect(() => {
28
+ countedRef.current++;
29
+ (async () => {
30
+ const currentRun = countedRef.current;
31
+ const visibleSeries = getOnlyVisibleSeries(series);
32
+ const groupedSeries = group(visibleSeries, (item) => item.type);
33
+ const shapesData = [];
34
+ const shapes = [];
35
+ await Promise.all(
36
+ // eslint-disable-next-line complexity
37
+ Array.from(groupedSeries).map(async (item) => {
38
+ const [seriesType, chartSeries] = item;
39
+ switch (seriesType) {
40
+ case 'bar-x': {
41
+ if (xAxis && xScale && (yScale === null || yScale === void 0 ? void 0 : yScale.length)) {
42
+ const preparedData = await prepareBarXData({
43
+ series: chartSeries,
44
+ seriesOptions,
45
+ xAxis,
46
+ xScale,
47
+ yAxis,
48
+ yScale,
49
+ boundsHeight,
50
+ });
51
+ shapes.push(React.createElement(BarXSeriesShapes, { key: "bar-x", dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData, htmlLayout: htmlLayout, clipPathId: clipPathId }));
52
+ shapesData.push(...preparedData);
53
+ }
54
+ break;
49
55
  }
50
- break;
51
- }
52
- case 'bar-y': {
53
- if (xAxis && xScale && yScale) {
54
- const preparedData = await prepareBarYData({
56
+ case 'bar-y': {
57
+ if (xAxis && xScale && (yScale === null || yScale === void 0 ? void 0 : yScale.length)) {
58
+ const preparedData = await prepareBarYData({
59
+ series: chartSeries,
60
+ seriesOptions,
61
+ xAxis,
62
+ xScale,
63
+ yAxis,
64
+ yScale,
65
+ });
66
+ shapes.push(React.createElement(BarYSeriesShapes, { key: "bar-y", dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData, htmlLayout: htmlLayout, clipPathId: clipPathId }));
67
+ shapesData.push(...preparedData);
68
+ }
69
+ break;
70
+ }
71
+ case 'waterfall': {
72
+ if (xAxis && xScale && (yScale === null || yScale === void 0 ? void 0 : yScale.length)) {
73
+ const preparedData = await prepareWaterfallData({
74
+ series: chartSeries,
75
+ seriesOptions,
76
+ xAxis,
77
+ xScale,
78
+ yAxis,
79
+ yScale,
80
+ });
81
+ shapes.push(React.createElement(WaterfallSeriesShapes, { key: "waterfall", dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData, htmlLayout: htmlLayout, clipPathId: clipPathId }));
82
+ shapesData.push(...preparedData);
83
+ }
84
+ break;
85
+ }
86
+ case 'line': {
87
+ if (xAxis && xScale && (yScale === null || yScale === void 0 ? void 0 : yScale.length)) {
88
+ const preparedData = await prepareLineData({
89
+ series: chartSeries,
90
+ xAxis,
91
+ xScale,
92
+ yAxis,
93
+ yScale,
94
+ split,
95
+ isOutsideBounds,
96
+ });
97
+ shapes.push(React.createElement(LineSeriesShapes, { key: "line", dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData, htmlLayout: htmlLayout, clipPathId: clipPathId }));
98
+ shapesData.push(...preparedData);
99
+ }
100
+ break;
101
+ }
102
+ case 'area': {
103
+ if (xAxis && xScale && (yScale === null || yScale === void 0 ? void 0 : yScale.length)) {
104
+ const preparedData = await prepareAreaData({
105
+ series: chartSeries,
106
+ xAxis,
107
+ xScale,
108
+ yAxis,
109
+ yScale,
110
+ boundsHeight,
111
+ isOutsideBounds,
112
+ });
113
+ shapes.push(React.createElement(AreaSeriesShapes, { key: "area", dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData, htmlLayout: htmlLayout, clipPathId: clipPathId }));
114
+ shapesData.push(...preparedData);
115
+ }
116
+ break;
117
+ }
118
+ case 'scatter': {
119
+ if (xAxis && xScale && (yScale === null || yScale === void 0 ? void 0 : yScale.length)) {
120
+ const preparedData = prepareScatterData({
121
+ series: chartSeries,
122
+ xAxis,
123
+ xScale,
124
+ yAxis,
125
+ yScale,
126
+ isOutsideBounds,
127
+ });
128
+ shapes.push(React.createElement(ScatterSeriesShape, { key: "scatter", dispatcher: dispatcher, preparedData: preparedData, seriesOptions: seriesOptions, htmlLayout: htmlLayout }));
129
+ shapesData.push(...preparedData);
130
+ }
131
+ break;
132
+ }
133
+ case 'pie': {
134
+ const preparedData = await preparePieData({
55
135
  series: chartSeries,
56
- seriesOptions,
57
- xAxis,
58
- xScale,
59
- yAxis,
60
- yScale,
136
+ boundsWidth,
137
+ boundsHeight,
61
138
  });
62
- shapes.push(React.createElement(BarYSeriesShapes, { key: "bar-y", dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData, htmlLayout: htmlLayout, clipPathId: clipPathId }));
139
+ shapes.push(React.createElement(PieSeriesShapes, { key: "pie", dispatcher: dispatcher, preparedData: preparedData, seriesOptions: seriesOptions, htmlLayout: htmlLayout }));
63
140
  shapesData.push(...preparedData);
141
+ break;
64
142
  }
65
- break;
66
- }
67
- case 'waterfall': {
68
- if (xAxis && xScale && yScale) {
69
- const preparedData = await prepareWaterfallData({
70
- series: chartSeries,
71
- seriesOptions,
72
- xAxis,
73
- xScale,
74
- yAxis,
75
- yScale,
143
+ case 'treemap': {
144
+ const preparedData = await prepareTreemapData({
145
+ // We should have exactly one series with "treemap" type
146
+ // Otherwise data validation should emit an error
147
+ series: chartSeries[0],
148
+ width: boundsWidth,
149
+ height: boundsHeight,
76
150
  });
77
- shapes.push(React.createElement(WaterfallSeriesShapes, { key: "waterfall", dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData, htmlLayout: htmlLayout, clipPathId: clipPathId }));
78
- shapesData.push(...preparedData);
151
+ shapes.push(React.createElement(TreemapSeriesShape, { key: "treemap", dispatcher: dispatcher, preparedData: preparedData, seriesOptions: seriesOptions, htmlLayout: htmlLayout }));
152
+ shapesData.push(preparedData);
153
+ break;
79
154
  }
80
- break;
81
- }
82
- case 'line': {
83
- if (xAxis && xScale && yScale) {
84
- const preparedData = await prepareLineData({
85
- series: chartSeries,
86
- xAxis,
87
- xScale,
88
- yAxis,
89
- yScale,
90
- split,
91
- isOutsideBounds,
155
+ case 'sankey': {
156
+ const preparedData = prepareSankeyData({
157
+ series: chartSeries[0],
158
+ width: boundsWidth,
159
+ height: boundsHeight,
92
160
  });
93
- shapes.push(React.createElement(LineSeriesShapes, { key: "line", dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData, htmlLayout: htmlLayout, clipPathId: clipPathId }));
94
- shapesData.push(...preparedData);
161
+ shapes.push(React.createElement(SankeySeriesShape, { key: "sankey", dispatcher: dispatcher, preparedData: preparedData, seriesOptions: seriesOptions, htmlLayout: htmlLayout }));
162
+ shapesData.push(preparedData);
163
+ break;
95
164
  }
96
- break;
97
- }
98
- case 'area': {
99
- if (xAxis && xScale && yScale) {
100
- const preparedData = await prepareAreaData({
165
+ case 'radar': {
166
+ const preparedData = await prepareRadarData({
101
167
  series: chartSeries,
102
- xAxis,
103
- xScale,
104
- yAxis,
105
- yScale,
168
+ boundsWidth,
106
169
  boundsHeight,
107
- isOutsideBounds,
108
170
  });
109
- shapes.push(React.createElement(AreaSeriesShapes, { key: "area", dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData, htmlLayout: htmlLayout, clipPathId: clipPathId }));
171
+ shapes.push(React.createElement(RadarSeriesShapes, { key: "radar", dispatcher: dispatcher, series: preparedData, seriesOptions: seriesOptions, htmlLayout: htmlLayout }));
110
172
  shapesData.push(...preparedData);
173
+ break;
111
174
  }
112
- break;
113
- }
114
- case 'scatter': {
115
- if (xAxis && xScale && yScale) {
116
- const preparedData = prepareScatterData({
117
- series: chartSeries,
118
- xAxis,
119
- xScale,
120
- yAxis,
121
- yScale,
122
- isOutsideBounds,
175
+ default: {
176
+ throw new ChartError({
177
+ message: `The display method is not defined for a series with type "${seriesType}"`,
123
178
  });
124
- shapes.push(React.createElement(ScatterSeriesShape, { key: "scatter", dispatcher: dispatcher, preparedData: preparedData, seriesOptions: seriesOptions, htmlLayout: htmlLayout }));
125
- shapesData.push(...preparedData);
126
179
  }
127
- break;
128
- }
129
- case 'pie': {
130
- const preparedData = await preparePieData({
131
- series: chartSeries,
132
- boundsWidth,
133
- boundsHeight,
134
- });
135
- shapes.push(React.createElement(PieSeriesShapes, { key: "pie", dispatcher: dispatcher, preparedData: preparedData, seriesOptions: seriesOptions, htmlLayout: htmlLayout }));
136
- shapesData.push(...preparedData);
137
- break;
138
- }
139
- case 'treemap': {
140
- const preparedData = await prepareTreemapData({
141
- // We should have exactly one series with "treemap" type
142
- // Otherwise data validation should emit an error
143
- series: chartSeries[0],
144
- width: boundsWidth,
145
- height: boundsHeight,
146
- });
147
- shapes.push(React.createElement(TreemapSeriesShape, { key: "treemap", dispatcher: dispatcher, preparedData: preparedData, seriesOptions: seriesOptions, htmlLayout: htmlLayout }));
148
- shapesData.push(preparedData);
149
- break;
150
- }
151
- case 'sankey': {
152
- const preparedData = prepareSankeyData({
153
- series: chartSeries[0],
154
- width: boundsWidth,
155
- height: boundsHeight,
156
- });
157
- shapes.push(React.createElement(SankeySeriesShape, { key: "sankey", dispatcher: dispatcher, preparedData: preparedData, seriesOptions: seriesOptions, htmlLayout: htmlLayout }));
158
- shapesData.push(preparedData);
159
- break;
160
- }
161
- case 'radar': {
162
- const preparedData = await prepareRadarData({
163
- series: chartSeries,
164
- boundsWidth,
165
- boundsHeight,
166
- });
167
- shapes.push(React.createElement(RadarSeriesShapes, { key: "radar", dispatcher: dispatcher, series: preparedData, seriesOptions: seriesOptions, htmlLayout: htmlLayout }));
168
- shapesData.push(...preparedData);
169
- break;
170
- }
171
- default: {
172
- throw new ChartError({
173
- message: `The display method is not defined for a series with type "${seriesType}"`,
174
- });
175
180
  }
181
+ }));
182
+ if (countedRef.current === currentRun) {
183
+ setShapesElements(shapes);
184
+ setShapesElemensData(shapesData);
176
185
  }
177
- }));
178
- setShapesElements(shapes);
179
- setShapesElemensData(shapesData);
186
+ })();
180
187
  }, [
181
188
  boundsHeight,
182
189
  boundsWidth,
@@ -192,8 +199,5 @@ export const useShapes = (args) => {
192
199
  clipPathId,
193
200
  isOutsideBounds,
194
201
  ]);
195
- React.useEffect(() => {
196
- setShapes();
197
- }, [setShapes]);
198
202
  return { shapes: shapesElemens, shapesData: shapesElemensData };
199
203
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gravity-ui/charts",
3
- "version": "1.11.0",
3
+ "version": "1.11.1",
4
4
  "description": "React component used to render charts",
5
5
  "license": "MIT",
6
6
  "main": "dist/cjs/index.js",