@gravity-ui/charts 1.34.1 → 1.34.2

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 (41) hide show
  1. package/dist/cjs/components/AxisX/prepare-axis-data.d.ts +6 -5
  2. package/dist/cjs/components/AxisX/prepare-axis-data.js +2 -2
  3. package/dist/cjs/components/AxisY/utils.js +3 -3
  4. package/dist/cjs/components/ChartInner/index.js +18 -8
  5. package/dist/cjs/components/ChartInner/useChartInnerProps.d.ts +1 -0
  6. package/dist/cjs/components/ChartInner/useChartInnerProps.js +2 -1
  7. package/dist/cjs/hooks/useAxis/x-axis.js +1 -1
  8. package/dist/cjs/hooks/useShapes/area/prepare-data.js +85 -25
  9. package/dist/cjs/hooks/useShapes/index.d.ts +1 -0
  10. package/dist/cjs/hooks/useShapes/index.js +32 -26
  11. package/dist/cjs/setup-jsdom.d.ts +0 -0
  12. package/dist/cjs/setup-jsdom.js +19 -0
  13. package/dist/cjs/utils/chart/axis/common.d.ts +2 -2
  14. package/dist/cjs/utils/chart/axis/common.js +2 -2
  15. package/dist/cjs/utils/chart/axis/x-axis.d.ts +11 -10
  16. package/dist/cjs/utils/chart/axis/x-axis.js +24 -3
  17. package/dist/cjs/utils/chart/index.d.ts +2 -29
  18. package/dist/cjs/utils/chart/index.js +2 -19
  19. package/dist/cjs/utils/chart/series-type-guards.d.ts +30 -0
  20. package/dist/cjs/utils/chart/series-type-guards.js +19 -0
  21. package/dist/esm/components/AxisX/prepare-axis-data.d.ts +6 -5
  22. package/dist/esm/components/AxisX/prepare-axis-data.js +2 -2
  23. package/dist/esm/components/AxisY/utils.js +3 -3
  24. package/dist/esm/components/ChartInner/index.js +18 -8
  25. package/dist/esm/components/ChartInner/useChartInnerProps.d.ts +1 -0
  26. package/dist/esm/components/ChartInner/useChartInnerProps.js +2 -1
  27. package/dist/esm/hooks/useAxis/x-axis.js +1 -1
  28. package/dist/esm/hooks/useShapes/area/prepare-data.js +85 -25
  29. package/dist/esm/hooks/useShapes/index.d.ts +1 -0
  30. package/dist/esm/hooks/useShapes/index.js +32 -26
  31. package/dist/esm/setup-jsdom.d.ts +0 -0
  32. package/dist/esm/setup-jsdom.js +19 -0
  33. package/dist/esm/utils/chart/axis/common.d.ts +2 -2
  34. package/dist/esm/utils/chart/axis/common.js +2 -2
  35. package/dist/esm/utils/chart/axis/x-axis.d.ts +11 -10
  36. package/dist/esm/utils/chart/axis/x-axis.js +24 -3
  37. package/dist/esm/utils/chart/index.d.ts +2 -29
  38. package/dist/esm/utils/chart/index.js +2 -19
  39. package/dist/esm/utils/chart/series-type-guards.d.ts +30 -0
  40. package/dist/esm/utils/chart/series-type-guards.js +19 -0
  41. package/package.json +1 -1
@@ -1,12 +1,13 @@
1
- import type { ChartScale, PreparedAxis, PreparedSplit } from '../../hooks';
1
+ import type { ChartScale, PreparedAxis, PreparedSeries, PreparedSplit } from '../../hooks';
2
2
  import type { AxisXData } from './types';
3
- export declare function prepareXAxisData({ axis, yAxis, scale, boundsWidth, boundsOffsetLeft, boundsOffsetRight, height, split, }: {
3
+ export declare function prepareXAxisData({ axis, boundsOffsetLeft, boundsOffsetRight, boundsWidth, height, scale, series, split, yAxis, }: {
4
4
  axis: PreparedAxis;
5
- yAxis: PreparedAxis[];
6
- scale: ChartScale;
7
- boundsWidth: number;
8
5
  boundsOffsetLeft: number;
9
6
  boundsOffsetRight: number;
7
+ boundsWidth: number;
10
8
  height: number;
9
+ scale: ChartScale;
10
+ series: PreparedSeries[];
11
11
  split: PreparedSplit;
12
+ yAxis: PreparedAxis[];
12
13
  }): Promise<AxisXData[]>;
@@ -68,7 +68,7 @@ async function getSvgAxisLabel({ getTextSize, text, axis, top, left, labelMaxWid
68
68
  return svgLabel;
69
69
  }
70
70
  // eslint-disable-next-line complexity
71
- export async function prepareXAxisData({ axis, yAxis, scale, boundsWidth, boundsOffsetLeft, boundsOffsetRight, height, split, }) {
71
+ export async function prepareXAxisData({ axis, boundsOffsetLeft, boundsOffsetRight, boundsWidth, height, scale, series, split, yAxis, }) {
72
72
  var _a, _b, _c, _d, _e, _f;
73
73
  const xAxisItems = [];
74
74
  for (let plotIndex = 0; plotIndex < split.plots.length; plotIndex++) {
@@ -95,7 +95,7 @@ export async function prepareXAxisData({ axis, yAxis, scale, boundsWidth, bounds
95
95
  const ticks = [];
96
96
  const getTextSize = getTextSizeFn({ style: axis.labels.style });
97
97
  const labelLineHeight = (await getTextSize('Tmp')).height;
98
- const values = getXAxisTickValues({ scale, axis, labelLineHeight });
98
+ const values = getXAxisTickValues({ scale, axis, labelLineHeight, series });
99
99
  const tickStep = getMinSpaceBetween(values, (d) => Number(d.value));
100
100
  const labelMaxWidth = values.length > 1
101
101
  ? Math.abs(values[0].x - values[1].x) - axis.labels.padding * 2
@@ -1,4 +1,4 @@
1
- import { getDomainDataYBySeries, getMinSpaceBetween, getTicksCount, isBandScale, thinOut, } from '../../utils';
1
+ import { getDomainDataYBySeries, getMinSpaceBetween, getTicksCountByPixelInterval, isBandScale, thinOut, } from '../../utils';
2
2
  export function getTickValues({ scale, axis, labelLineHeight, series, }) {
3
3
  if ('ticks' in scale && typeof scale.ticks === 'function') {
4
4
  const range = scale.range();
@@ -13,10 +13,10 @@ export function getTickValues({ scale, axis, labelLineHeight, series, }) {
13
13
  if (domainData.length < 3) {
14
14
  return domainData;
15
15
  }
16
- const ticksCount = (_a = getTicksCount({ axis, range: height })) !== null && _a !== void 0 ? _a : domainData.length;
16
+ const ticksCount = (_a = getTicksCountByPixelInterval({ axis, axisWidth: height })) !== null && _a !== void 0 ? _a : domainData.length;
17
17
  return scale.ticks(Math.min(ticksCount, domainData.length));
18
18
  }
19
- const ticksCount = getTicksCount({ axis, range: height });
19
+ const ticksCount = getTicksCountByPixelInterval({ axis, axisWidth: height });
20
20
  return scale.ticks(ticksCount);
21
21
  };
22
22
  const scaleTicks = getScaleTicks();
@@ -64,7 +64,7 @@ export const ChartInner = (props) => {
64
64
  preparedRangeSlider,
65
65
  tooltip: preparedTooltip,
66
66
  });
67
- const { allPreparedSeries, boundsHeight, boundsOffsetLeft, boundsOffsetTop, boundsWidth, handleLegendItemClick, isOutsideBounds, legendConfig, legendItems, preparedLegend, preparedSeries, preparedSeriesOptions, preparedSplit, prevHeight, prevWidth, shapes, shapesData, xAxis, xScale, yAxis, yScale, } = useChartInnerProps(Object.assign(Object.assign({}, props), { clipPathId,
67
+ const { allPreparedSeries, boundsHeight, boundsOffsetLeft, boundsOffsetTop, boundsWidth, handleLegendItemClick, isOutsideBounds, legendConfig, legendItems, preparedLegend, preparedSeries, preparedSeriesOptions, preparedSplit, prevHeight, prevWidth, shapes, shapesData, shapesReady, xAxis, xScale, yAxis, yScale, } = useChartInnerProps(Object.assign(Object.assign({}, props), { clipPathId,
68
68
  dispatcher,
69
69
  htmlLayout, plotNode: plotRef.current, preparedChart, rangeSliderDomain: (_a = rangeSliderRef.current) === null || _a === void 0 ? void 0 : _a.getDomain(), rangeSliderState, svgContainer: svgRef.current, updateZoomState,
70
70
  zoomState }));
@@ -160,18 +160,29 @@ export const ChartInner = (props) => {
160
160
  if (axis && scale) {
161
161
  const axisData = await prepareXAxisData({
162
162
  axis,
163
- yAxis,
164
- scale,
165
- boundsWidth,
166
163
  boundsOffsetLeft: boundsOffsetLeft,
167
164
  boundsOffsetRight: width - boundsWidth - boundsOffsetLeft,
165
+ boundsWidth,
168
166
  height: boundsHeight,
167
+ scale,
168
+ series: preparedSeries.filter((s) => s.visible),
169
169
  split: preparedSplit,
170
+ yAxis,
170
171
  });
171
172
  items.push(...axisData);
172
173
  }
173
174
  return items;
174
- }, [xAxis, xScale, yAxis, boundsWidth, boundsOffsetLeft, width, boundsHeight, preparedSplit]);
175
+ }, [
176
+ boundsHeight,
177
+ boundsOffsetLeft,
178
+ boundsWidth,
179
+ preparedSeries,
180
+ preparedSplit,
181
+ width,
182
+ xAxis,
183
+ xScale,
184
+ yAxis,
185
+ ]);
175
186
  const xAxisDataItems = useAsyncState([], setXAxisDataItems);
176
187
  React.useEffect(() => {
177
188
  if (!initialized && xScale) {
@@ -207,12 +218,11 @@ export const ChartInner = (props) => {
207
218
  updateRangeSliderState,
208
219
  xScale,
209
220
  ]);
210
- const areShapesReady = shapes.length > 0;
211
221
  React.useEffect(() => {
212
- if (areShapesReady) {
222
+ if (shapesReady) {
213
223
  onReady === null || onReady === void 0 ? void 0 : onReady({ dimensions: { width, height } });
214
224
  }
215
- }, [height, areShapesReady, onReady, width]);
225
+ }, [height, shapesReady, onReady, width]);
216
226
  const chartContent = (React.createElement(React.Fragment, null,
217
227
  React.createElement("defs", null,
218
228
  React.createElement("clipPath", { id: getClipPathIdByBounds({ clipPathId }) },
@@ -44,6 +44,7 @@ export declare function useChartInnerProps(props: Props): {
44
44
  prevWidth: number | undefined;
45
45
  shapes: React.ReactElement<any, string | React.JSXElementConstructor<any>>[];
46
46
  shapesData: import("../../hooks").ShapeData[];
47
+ shapesReady: boolean;
47
48
  svgXPos: number | undefined;
48
49
  xAxis: import("../../hooks").PreparedXAxis | null;
49
50
  xScale: import("../../hooks").ChartScale | undefined;
@@ -111,7 +111,7 @@ export function useChartInnerProps(props) {
111
111
  const isOutsideBounds = React.useCallback((x, y) => {
112
112
  return x < 0 || x > boundsWidth || y < 0 || y > boundsHeight;
113
113
  }, [boundsHeight, boundsWidth]);
114
- const { shapes, shapesData } = useShapes({
114
+ const { shapes, shapesData, shapesReady } = useShapes({
115
115
  boundsWidth,
116
116
  boundsHeight,
117
117
  clipPathBySeriesType: CLIP_PATH_BY_SERIES_TYPE,
@@ -183,6 +183,7 @@ export function useChartInnerProps(props) {
183
183
  prevWidth,
184
184
  shapes,
185
185
  shapesData,
186
+ shapesReady,
186
187
  svgXPos: x,
187
188
  xAxis,
188
189
  xScale,
@@ -15,7 +15,7 @@ async function setLabelSettings({ axis, seriesData, width, axisLabels, }) {
15
15
  }
16
16
  const getTextSize = getTextSizeFn({ style: axis.labels.style });
17
17
  const labelLineHeight = (await getTextSize('Tmp')).height;
18
- const tickValues = getXAxisTickValues({ axis, scale, labelLineHeight });
18
+ const tickValues = getXAxisTickValues({ axis, scale, labelLineHeight, series: seriesData });
19
19
  const tickStep = getMinSpaceBetween(tickValues, (d) => Number(d.value));
20
20
  if (axis.type === 'datetime' && !(axisLabels === null || axisLabels === void 0 ? void 0 : axisLabels.dateFormat)) {
21
21
  axis.labels.dateFormat = getDefaultDateFormat(tickStep);
@@ -94,11 +94,13 @@ export const prepareAreaData = async (args) => {
94
94
  const positiveStackValues = new Map();
95
95
  const negativeStackValues = new Map();
96
96
  xValues.forEach(([key]) => {
97
- positiveStackValues.set(key, 0);
98
- negativeStackValues.set(key, 0);
97
+ positiveStackValues.set(key, { prev: 0, next: 0 });
98
+ negativeStackValues.set(key, { prev: 0, next: 0 });
99
99
  });
100
100
  const seriesStackData = [];
101
- for (let j = 0; j < seriesStack.length; j++) {
101
+ // Process series in reverse order so that the first series in input
102
+ // appears at the top of the stack (furthest from baseline)
103
+ for (let j = seriesStack.length - 1; j >= 0; j--) {
102
104
  const s = seriesStack[j];
103
105
  const yAxisIndex = s.yAxis;
104
106
  const seriesYAxis = yAxis[yAxisIndex];
@@ -123,40 +125,97 @@ export const prepareAreaData = async (args) => {
123
125
  : d.x);
124
126
  return m.set(key, d);
125
127
  }, new Map());
126
- const points = xValues.reduce((pointsAcc, [x, xValue]) => {
127
- var _a, _b;
128
+ const points = xValues.reduce((pointsAcc, [x, xValue], index) => {
129
+ var _a, _b, _c, _d, _e, _f, _g, _h;
128
130
  const d = (_a = seriesData.get(x)) !== null && _a !== void 0 ? _a : {
129
131
  x,
130
132
  y: 0,
131
133
  };
132
134
  const yDataValue = (_b = d.y) !== null && _b !== void 0 ? _b : null;
135
+ if (s.nullMode === 'connect' && yDataValue === null) {
136
+ return pointsAcc;
137
+ }
133
138
  const yValue = getYValue({ point: d, yAxis: seriesYAxis, yScale: seriesYScale });
134
- let y = null;
135
- let y0 = yAxisTop + yMin;
136
139
  if (typeof yDataValue === 'number' && yValue !== null) {
140
+ const prevPoint = seriesData.get((_c = xValues[index - 1]) === null || _c === void 0 ? void 0 : _c[0]);
141
+ const nextPoint = seriesData.get((_d = xValues[index + 1]) === null || _d === void 0 ? void 0 : _d[0]);
142
+ const currentPointStackHeight = Math.abs(yMin - yValue);
137
143
  if (yDataValue >= 0) {
138
- const positiveStackHeight = positiveStackValues.get(x) || 0;
139
- y = yAxisTop + yValue - positiveStackHeight;
140
- y0 -= positiveStackHeight;
141
- positiveStackValues.set(x, positiveStackHeight + (yMin - yValue));
144
+ const positiveStackHeights = positiveStackValues.get(x);
145
+ let prevSectionStackHeight = (_e = positiveStackHeights === null || positiveStackHeights === void 0 ? void 0 : positiveStackHeights.prev) !== null && _e !== void 0 ? _e : 0;
146
+ let nextSectionStackHeight = (_f = positiveStackHeights === null || positiveStackHeights === void 0 ? void 0 : positiveStackHeights.next) !== null && _f !== void 0 ? _f : 0;
147
+ pointsAcc.push({
148
+ y0: yAxisTop + yMin - prevSectionStackHeight,
149
+ x: xValue,
150
+ y: yAxisTop + yValue - prevSectionStackHeight,
151
+ data: d,
152
+ series: s,
153
+ });
154
+ if (prevSectionStackHeight !== nextSectionStackHeight) {
155
+ pointsAcc.push({
156
+ y0: yAxisTop + yMin - nextSectionStackHeight,
157
+ x: xValue,
158
+ y: yAxisTop + yValue - nextSectionStackHeight,
159
+ data: d,
160
+ series: s,
161
+ });
162
+ }
163
+ if ((prevPoint === null || prevPoint === void 0 ? void 0 : prevPoint.y) !== null) {
164
+ prevSectionStackHeight =
165
+ prevSectionStackHeight + currentPointStackHeight;
166
+ }
167
+ if ((nextPoint === null || nextPoint === void 0 ? void 0 : nextPoint.y) !== null) {
168
+ nextSectionStackHeight =
169
+ nextSectionStackHeight + currentPointStackHeight;
170
+ }
171
+ positiveStackValues.set(x, {
172
+ prev: prevSectionStackHeight,
173
+ next: nextSectionStackHeight,
174
+ });
142
175
  }
143
176
  else {
144
- const negativeStackHeight = negativeStackValues.get(x) || 0;
145
- y = yAxisTop + yValue + negativeStackHeight;
146
- y0 += negativeStackHeight;
147
- negativeStackValues.set(x, negativeStackHeight + (yValue - yMin));
177
+ const negativeStackHeights = negativeStackValues.get(x);
178
+ let prevSectionStackHeight = (_g = negativeStackHeights === null || negativeStackHeights === void 0 ? void 0 : negativeStackHeights.prev) !== null && _g !== void 0 ? _g : 0;
179
+ let nextSectionStackHeight = (_h = negativeStackHeights === null || negativeStackHeights === void 0 ? void 0 : negativeStackHeights.next) !== null && _h !== void 0 ? _h : 0;
180
+ pointsAcc.push({
181
+ y0: yAxisTop + yMin + prevSectionStackHeight,
182
+ x: xValue,
183
+ y: yAxisTop + yValue + prevSectionStackHeight,
184
+ data: d,
185
+ series: s,
186
+ });
187
+ if (prevSectionStackHeight !== nextSectionStackHeight) {
188
+ pointsAcc.push({
189
+ y0: yAxisTop + yMin + nextSectionStackHeight,
190
+ x: xValue,
191
+ y: yAxisTop + yValue + nextSectionStackHeight,
192
+ data: d,
193
+ series: s,
194
+ });
195
+ }
196
+ if ((prevPoint === null || prevPoint === void 0 ? void 0 : prevPoint.y) !== null) {
197
+ prevSectionStackHeight =
198
+ prevSectionStackHeight + currentPointStackHeight;
199
+ }
200
+ if ((nextPoint === null || nextPoint === void 0 ? void 0 : nextPoint.y) !== null) {
201
+ nextSectionStackHeight =
202
+ nextSectionStackHeight + currentPointStackHeight;
203
+ }
204
+ negativeStackValues.set(x, {
205
+ prev: prevSectionStackHeight,
206
+ next: nextSectionStackHeight,
207
+ });
148
208
  }
149
209
  }
150
- if (s.nullMode === 'connect' && yDataValue === null) {
151
- return pointsAcc;
210
+ else {
211
+ pointsAcc.push({
212
+ y0: yAxisTop + yMin,
213
+ x: xValue,
214
+ y: null,
215
+ data: d,
216
+ series: s,
217
+ });
152
218
  }
153
- pointsAcc.push({
154
- y0,
155
- x: xValue,
156
- y,
157
- data: d,
158
- series: s,
159
- });
160
219
  return pointsAcc;
161
220
  }, []);
162
221
  const labels = [];
@@ -197,7 +256,8 @@ export const prepareAreaData = async (args) => {
197
256
  }
198
257
  if (series.some((s) => s.stacking === 'percent')) {
199
258
  xValues.forEach(([x], index) => {
200
- const stackHeight = positiveStackValues.get(x) || 0;
259
+ var _a;
260
+ const stackHeight = ((_a = positiveStackValues.get(x)) === null || _a === void 0 ? void 0 : _a.prev) || 0;
201
261
  let acc = 0;
202
262
  const ratio = plotHeight / stackHeight;
203
263
  seriesStackData.forEach((item) => {
@@ -43,4 +43,5 @@ type Args = {
43
43
  export declare const useShapes: (args: Args) => {
44
44
  shapes: React.ReactElement<any, string | React.JSXElementConstructor<any>>[];
45
45
  shapesData: ShapeData[];
46
+ shapesReady: boolean;
46
47
  };
@@ -34,6 +34,7 @@ export const useShapes = (args) => {
34
34
  const { boundsWidth, boundsHeight, clipPathId, clipPathBySeriesType, dispatcher, htmlLayout, isOutsideBounds = IS_OUTSIDE_BOUNDS, isRangeSlider, series, seriesOptions, split, xAxis, xScale, yAxis, yScale, zoomState, } = args;
35
35
  const [shapesElemens, setShapesElements] = React.useState([]);
36
36
  const [shapesElemensData, setShapesElemensData] = React.useState([]);
37
+ const shapesReadyRef = React.useRef(false);
37
38
  const countedRef = React.useRef(0);
38
39
  React.useEffect(() => {
39
40
  countedRef.current++;
@@ -45,7 +46,7 @@ export const useShapes = (args) => {
45
46
  const shapes = [];
46
47
  await Promise.all(
47
48
  // eslint-disable-next-line complexity
48
- Array.from(groupedSeries).map(async (item) => {
49
+ Array.from(groupedSeries).map(async (item, index) => {
49
50
  const [seriesType, chartSeries] = item;
50
51
  switch (seriesType) {
51
52
  case SERIES_TYPE.BarX: {
@@ -61,8 +62,8 @@ export const useShapes = (args) => {
61
62
  split,
62
63
  isRangeSlider,
63
64
  });
64
- shapes.push(React.createElement(BarXSeriesShapes, { key: SERIES_TYPE.BarX, dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData, htmlLayout: htmlLayout, clipPathId: clipPathId }));
65
- shapesData.push(...preparedData);
65
+ shapes[index] = (React.createElement(BarXSeriesShapes, { key: SERIES_TYPE.BarX, dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData, htmlLayout: htmlLayout, clipPathId: clipPathId }));
66
+ shapesData.splice(index, 0, ...preparedData);
66
67
  }
67
68
  break;
68
69
  }
@@ -78,8 +79,8 @@ export const useShapes = (args) => {
78
79
  yAxis,
79
80
  yScale,
80
81
  });
81
- shapes.push(React.createElement(BarYSeriesShapes, { key: SERIES_TYPE.BarY, dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData, htmlLayout: htmlLayout, clipPathId: clipPathId }));
82
- shapesData.push(...preparedData.shapes);
82
+ shapes[index] = (React.createElement(BarYSeriesShapes, { key: SERIES_TYPE.BarY, dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData, htmlLayout: htmlLayout, clipPathId: clipPathId }));
83
+ shapesData.splice(index, 0, ...preparedData.shapes);
83
84
  }
84
85
  break;
85
86
  }
@@ -93,8 +94,8 @@ export const useShapes = (args) => {
93
94
  yAxis,
94
95
  yScale,
95
96
  });
96
- shapes.push(React.createElement(WaterfallSeriesShapes, { key: SERIES_TYPE.Waterfall, dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData, htmlLayout: htmlLayout, clipPathId: clipPathId }));
97
- shapesData.push(...preparedData);
97
+ shapes[index] = (React.createElement(WaterfallSeriesShapes, { key: SERIES_TYPE.Waterfall, dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData, htmlLayout: htmlLayout, clipPathId: clipPathId }));
98
+ shapesData.splice(index, 0, ...preparedData);
98
99
  }
99
100
  break;
100
101
  }
@@ -115,8 +116,8 @@ export const useShapes = (args) => {
115
116
  yAxis,
116
117
  zoomState,
117
118
  });
118
- shapes.push(React.createElement(LineSeriesShapes, { key: SERIES_TYPE.Line, dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData, htmlLayout: htmlLayout, clipPathId: resultClipPathId }));
119
- shapesData.push(...preparedData);
119
+ shapes[index] = (React.createElement(LineSeriesShapes, { key: SERIES_TYPE.Line, dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData, htmlLayout: htmlLayout, clipPathId: resultClipPathId }));
120
+ shapesData.splice(index, 0, ...preparedData);
120
121
  }
121
122
  break;
122
123
  }
@@ -133,8 +134,8 @@ export const useShapes = (args) => {
133
134
  isOutsideBounds,
134
135
  isRangeSlider,
135
136
  });
136
- shapes.push(React.createElement(AreaSeriesShapes, { key: SERIES_TYPE.Area, dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData, htmlLayout: htmlLayout, clipPathId: clipPathId }));
137
- shapesData.push(...preparedData);
137
+ shapes[index] = (React.createElement(AreaSeriesShapes, { key: SERIES_TYPE.Area, dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData, htmlLayout: htmlLayout, clipPathId: clipPathId }));
138
+ shapesData.splice(index, 0, ...preparedData);
138
139
  }
139
140
  break;
140
141
  }
@@ -148,10 +149,10 @@ export const useShapes = (args) => {
148
149
  yScale,
149
150
  isOutsideBounds,
150
151
  });
151
- shapes.push(React.createElement(ScatterSeriesShape, { key: SERIES_TYPE.Scatter, clipPathId: shouldUseClipPathId(SERIES_TYPE.Scatter, clipPathBySeriesType)
152
+ shapes[index] = (React.createElement(ScatterSeriesShape, { key: SERIES_TYPE.Scatter, clipPathId: shouldUseClipPathId(SERIES_TYPE.Scatter, clipPathBySeriesType)
152
153
  ? clipPathId
153
154
  : undefined, dispatcher: dispatcher, preparedData: preparedData, seriesOptions: seriesOptions, htmlLayout: htmlLayout }));
154
- shapesData.push(...preparedData);
155
+ shapesData.splice(index, 0, ...preparedData);
155
156
  }
156
157
  break;
157
158
  }
@@ -161,8 +162,8 @@ export const useShapes = (args) => {
161
162
  boundsWidth,
162
163
  boundsHeight,
163
164
  });
164
- shapes.push(React.createElement(PieSeriesShapes, { key: SERIES_TYPE.Pie, dispatcher: dispatcher, preparedData: preparedData, seriesOptions: seriesOptions, htmlLayout: htmlLayout }));
165
- shapesData.push(...preparedData);
165
+ shapes[index] = (React.createElement(PieSeriesShapes, { key: SERIES_TYPE.Pie, dispatcher: dispatcher, preparedData: preparedData, seriesOptions: seriesOptions, htmlLayout: htmlLayout }));
166
+ shapesData.splice(index, 0, ...preparedData);
166
167
  break;
167
168
  }
168
169
  case SERIES_TYPE.Treemap: {
@@ -173,8 +174,8 @@ export const useShapes = (args) => {
173
174
  width: boundsWidth,
174
175
  height: boundsHeight,
175
176
  });
176
- shapes.push(React.createElement(TreemapSeriesShape, { key: SERIES_TYPE.Treemap, dispatcher: dispatcher, preparedData: preparedData, seriesOptions: seriesOptions, htmlLayout: htmlLayout }));
177
- shapesData.push(preparedData);
177
+ shapes[index] = (React.createElement(TreemapSeriesShape, { key: SERIES_TYPE.Treemap, dispatcher: dispatcher, preparedData: preparedData, seriesOptions: seriesOptions, htmlLayout: htmlLayout }));
178
+ shapesData.splice(index, 0, preparedData);
178
179
  break;
179
180
  }
180
181
  case SERIES_TYPE.Sankey: {
@@ -183,8 +184,8 @@ export const useShapes = (args) => {
183
184
  width: boundsWidth,
184
185
  height: boundsHeight,
185
186
  });
186
- shapes.push(React.createElement(SankeySeriesShape, { key: SERIES_TYPE.Sankey, dispatcher: dispatcher, preparedData: preparedData, seriesOptions: seriesOptions, htmlLayout: htmlLayout }));
187
- shapesData.push(preparedData);
187
+ shapes[index] = (React.createElement(SankeySeriesShape, { key: SERIES_TYPE.Sankey, dispatcher: dispatcher, preparedData: preparedData, seriesOptions: seriesOptions, htmlLayout: htmlLayout }));
188
+ shapesData.splice(index, 0, preparedData);
188
189
  break;
189
190
  }
190
191
  case SERIES_TYPE.Radar: {
@@ -193,8 +194,8 @@ export const useShapes = (args) => {
193
194
  boundsWidth,
194
195
  boundsHeight,
195
196
  });
196
- shapes.push(React.createElement(RadarSeriesShapes, { key: SERIES_TYPE.Radar, dispatcher: dispatcher, series: preparedData, seriesOptions: seriesOptions, htmlLayout: htmlLayout }));
197
- shapesData.push(...preparedData);
197
+ shapes[index] = (React.createElement(RadarSeriesShapes, { key: SERIES_TYPE.Radar, dispatcher: dispatcher, series: preparedData, seriesOptions: seriesOptions, htmlLayout: htmlLayout }));
198
+ shapesData.splice(index, 0, ...preparedData);
198
199
  break;
199
200
  }
200
201
  case SERIES_TYPE.Heatmap: {
@@ -206,8 +207,8 @@ export const useShapes = (args) => {
206
207
  yAxis: yAxis[0],
207
208
  yScale: yScale[0],
208
209
  });
209
- shapes.push(React.createElement(HeatmapSeriesShapes, { key: SERIES_TYPE.Heatmap, dispatcher: dispatcher, preparedData: preparedData, seriesOptions: seriesOptions, htmlLayout: htmlLayout }));
210
- shapesData.push(preparedData);
210
+ shapes[index] = (React.createElement(HeatmapSeriesShapes, { key: SERIES_TYPE.Heatmap, dispatcher: dispatcher, preparedData: preparedData, seriesOptions: seriesOptions, htmlLayout: htmlLayout }));
211
+ shapesData.splice(index, 0, preparedData);
211
212
  }
212
213
  break;
213
214
  }
@@ -217,8 +218,8 @@ export const useShapes = (args) => {
217
218
  boundsWidth,
218
219
  boundsHeight,
219
220
  });
220
- shapes.push(React.createElement(FunnelSeriesShapes, { key: "funnel", dispatcher: dispatcher, preparedData: preparedData, seriesOptions: seriesOptions, htmlLayout: htmlLayout }));
221
- shapesData.push(preparedData);
221
+ shapes[index] = (React.createElement(FunnelSeriesShapes, { key: "funnel", dispatcher: dispatcher, preparedData: preparedData, seriesOptions: seriesOptions, htmlLayout: htmlLayout }));
222
+ shapesData.splice(index, 0, preparedData);
222
223
  break;
223
224
  }
224
225
  default: {
@@ -229,6 +230,7 @@ export const useShapes = (args) => {
229
230
  }
230
231
  }));
231
232
  if (countedRef.current === currentRun) {
233
+ shapesReadyRef.current = true;
232
234
  setShapesElements(shapes);
233
235
  setShapesElemensData(shapesData);
234
236
  }
@@ -251,5 +253,9 @@ export const useShapes = (args) => {
251
253
  yScale,
252
254
  zoomState,
253
255
  ]);
254
- return { shapes: shapesElemens, shapesData: shapesElemensData };
256
+ return {
257
+ shapes: shapesElemens,
258
+ shapesData: shapesElemensData,
259
+ shapesReady: shapesReadyRef.current,
260
+ };
255
261
  };
File without changes
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ // Global mocks for jsdom environment.
3
+ // Guarded by typeof checks so this file is safe to run in the `node` environment too.
4
+ if (typeof document !== 'undefined') {
5
+ // jsdom does not implement document.fonts
6
+ if (!document.fonts) {
7
+ Object.defineProperty(document, 'fonts', {
8
+ value: { ready: Promise.resolve() },
9
+ configurable: true,
10
+ });
11
+ }
12
+ }
13
+ if (typeof HTMLCanvasElement !== 'undefined') {
14
+ // jsdom does not implement HTMLCanvasElement.prototype.getContext (used for text measurement)
15
+ HTMLCanvasElement.prototype.getContext = (() => ({
16
+ font: '',
17
+ measureText: () => ({ width: 0 }),
18
+ }));
19
+ }
@@ -3,9 +3,9 @@ import type { ChartScale, PreparedAxis, PreparedAxisPlotBand, PreparedSplit } fr
3
3
  import type { ChartAxis } from '../../../types';
4
4
  import type { AxisDirection } from '../types';
5
5
  type Ticks = number[] | string[] | Date[];
6
- export declare function getTicksCount({ axis, range }: {
6
+ export declare function getTicksCountByPixelInterval({ axis, axisWidth, }: {
7
7
  axis: PreparedAxis;
8
- range: number;
8
+ axisWidth: number;
9
9
  }): number | undefined;
10
10
  export declare function isBandScale(scale?: ChartScale | AxisScale<AxisDomain>): scale is ScaleBand<string>;
11
11
  export declare function isTimeScale(scale?: ChartScale | AxisScale<AxisDomain>): scale is ScaleTime<number, number>;
@@ -1,9 +1,9 @@
1
1
  import { ascending, descending, reverse, sort } from 'd3';
2
2
  import clamp from 'lodash/clamp';
3
- export function getTicksCount({ axis, range }) {
3
+ export function getTicksCountByPixelInterval({ axis, axisWidth, }) {
4
4
  let ticksCount;
5
5
  if (axis.ticks.pixelInterval) {
6
- ticksCount = Math.ceil(range / axis.ticks.pixelInterval);
6
+ ticksCount = Math.ceil(axisWidth / axis.ticks.pixelInterval);
7
7
  }
8
8
  return ticksCount;
9
9
  }
@@ -1,12 +1,13 @@
1
- import type { ChartScale, PreparedAxis } from '../../../hooks';
2
- export declare function getXAxisTickValues({ scale, axis, labelLineHeight, }: {
3
- scale: ChartScale;
1
+ import type { ChartScale, PreparedAxis, PreparedSeries } from '../../../hooks';
2
+ import type { ChartSeries } from '../../../types';
3
+ type TickValue = {
4
+ x: number;
5
+ value: number | string | Date;
6
+ };
7
+ export declare function getXAxisTickValues({ axis, labelLineHeight, scale, series, }: {
4
8
  axis: PreparedAxis;
5
9
  labelLineHeight: number;
6
- }): {
7
- x: number;
8
- value: number | Date;
9
- }[] | {
10
- x: number;
11
- value: string;
12
- }[];
10
+ scale: ChartScale;
11
+ series?: ChartSeries[] | PreparedSeries[];
12
+ }): TickValue[];
13
+ export {};
@@ -1,13 +1,34 @@
1
1
  import { getMinSpaceBetween } from '../array';
2
- import { getTicksCount, isBandScale, thinOut } from './common';
3
- export function getXAxisTickValues({ scale, axis, labelLineHeight, }) {
2
+ import { isSeriesWithNumericalXValues } from '../series-type-guards';
3
+ import { getTicksCountByPixelInterval, isBandScale, thinOut } from './common';
4
+ const DEFAULT_TICKS_COUNT = 10;
5
+ function getTicksCount(args) {
6
+ const { axis, axisWidth, series } = args;
7
+ const result = getTicksCountByPixelInterval({ axis, axisWidth });
8
+ if (typeof result === 'number') {
9
+ return result;
10
+ }
11
+ if (series) {
12
+ const xDataSet = new Set();
13
+ series === null || series === void 0 ? void 0 : series.forEach((item) => {
14
+ if (isSeriesWithNumericalXValues(item)) {
15
+ item.data.forEach((data) => {
16
+ xDataSet.add(data.x);
17
+ });
18
+ }
19
+ });
20
+ return xDataSet.size < DEFAULT_TICKS_COUNT ? xDataSet.size : DEFAULT_TICKS_COUNT;
21
+ }
22
+ return DEFAULT_TICKS_COUNT;
23
+ }
24
+ export function getXAxisTickValues({ axis, labelLineHeight, scale, series, }) {
4
25
  if ('ticks' in scale && typeof scale.ticks === 'function') {
5
26
  const range = scale.range();
6
27
  const axisWidth = Math.abs(range[0] - range[1]);
7
28
  if (!axisWidth) {
8
29
  return [];
9
30
  }
10
- const scaleTicksCount = getTicksCount({ axis, range: axisWidth });
31
+ const scaleTicksCount = getTicksCount({ axis, axisWidth, series });
11
32
  const scaleTicks = scale.ticks(scaleTicksCount);
12
33
  const originalTickValues = scaleTicks.map((t) => ({
13
34
  x: scale(t),
@@ -1,4 +1,5 @@
1
1
  import type { BaseTextStyle, ChartSeries, ChartSeriesData } from '../../types';
2
+ import type { UnknownSeries } from './series-type-guards';
2
3
  import type { AxisDirection } from './types';
3
4
  export * from './axis/common';
4
5
  export * from './array';
@@ -8,41 +9,13 @@ export * from './labels';
8
9
  export * from './legend';
9
10
  export * from './math';
10
11
  export * from './series';
12
+ export * from './series-type-guards';
11
13
  export * from './symbol';
12
14
  export * from './text';
13
15
  export * from './time';
14
16
  export * from './zoom';
15
17
  export declare const CHART_SERIES_WITH_VOLUME_ON_Y_AXIS: ChartSeries['type'][];
16
18
  export declare const CHART_SERIES_WITH_VOLUME_ON_X_AXIS: ChartSeries['type'][];
17
- type UnknownSeries = {
18
- type: ChartSeries['type'];
19
- data: unknown;
20
- };
21
- /**
22
- * Checks whether the series should be drawn with axes.
23
- *
24
- * @param series - The series object to check.
25
- * @returns `true` if the series should be drawn with axes, `false` otherwise.
26
- */
27
- export declare function isAxisRelatedSeries(series: UnknownSeries): boolean;
28
- export declare function isSeriesWithNumericalXValues(series: UnknownSeries): series is {
29
- type: ChartSeries['type'];
30
- data: {
31
- x: number;
32
- }[];
33
- };
34
- export declare function isSeriesWithNumericalYValues(series: UnknownSeries): series is {
35
- type: ChartSeries['type'];
36
- data: {
37
- y: number;
38
- }[];
39
- };
40
- export declare function isSeriesWithCategoryValues(series: UnknownSeries): series is {
41
- type: ChartSeries['type'];
42
- data: {
43
- category: string;
44
- }[];
45
- };
46
19
  export declare const getDomainDataXBySeries: (series: UnknownSeries[]) => ({} | undefined)[];
47
20
  export declare function getDefaultMaxXAxisValue(series: UnknownSeries[]): 0 | undefined;
48
21
  export declare function getDefaultMinXAxisValue(series: UnknownSeries[]): number | undefined;