@gravity-ui/charts 1.34.0 → 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 (55) 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 +23 -10
  5. package/dist/cjs/components/ChartInner/useChartInnerProps.d.ts +1 -0
  6. package/dist/cjs/components/ChartInner/useChartInnerProps.js +3 -1
  7. package/dist/cjs/components/RangeSlider/index.js +1 -0
  8. package/dist/cjs/hooks/useAxis/x-axis.js +1 -1
  9. package/dist/cjs/hooks/useBrush/index.js +24 -5
  10. package/dist/cjs/hooks/useBrush/types.d.ts +1 -0
  11. package/dist/cjs/hooks/useBrush/utils.d.ts +4 -0
  12. package/dist/cjs/hooks/useBrush/utils.js +4 -0
  13. package/dist/cjs/hooks/useShapes/area/prepare-data.js +85 -25
  14. package/dist/cjs/hooks/useShapes/index.d.ts +3 -0
  15. package/dist/cjs/hooks/useShapes/index.js +40 -27
  16. package/dist/cjs/hooks/useShapes/utils.d.ts +10 -0
  17. package/dist/cjs/hooks/useShapes/utils.js +15 -0
  18. package/dist/cjs/setup-jsdom.d.ts +0 -0
  19. package/dist/cjs/setup-jsdom.js +19 -0
  20. package/dist/cjs/utils/chart/axis/common.d.ts +2 -2
  21. package/dist/cjs/utils/chart/axis/common.js +2 -2
  22. package/dist/cjs/utils/chart/axis/x-axis.d.ts +11 -10
  23. package/dist/cjs/utils/chart/axis/x-axis.js +24 -3
  24. package/dist/cjs/utils/chart/index.d.ts +2 -29
  25. package/dist/cjs/utils/chart/index.js +2 -19
  26. package/dist/cjs/utils/chart/series-type-guards.d.ts +30 -0
  27. package/dist/cjs/utils/chart/series-type-guards.js +19 -0
  28. package/dist/esm/components/AxisX/prepare-axis-data.d.ts +6 -5
  29. package/dist/esm/components/AxisX/prepare-axis-data.js +2 -2
  30. package/dist/esm/components/AxisY/utils.js +3 -3
  31. package/dist/esm/components/ChartInner/index.js +23 -10
  32. package/dist/esm/components/ChartInner/useChartInnerProps.d.ts +1 -0
  33. package/dist/esm/components/ChartInner/useChartInnerProps.js +3 -1
  34. package/dist/esm/components/RangeSlider/index.js +1 -0
  35. package/dist/esm/hooks/useAxis/x-axis.js +1 -1
  36. package/dist/esm/hooks/useBrush/index.js +24 -5
  37. package/dist/esm/hooks/useBrush/types.d.ts +1 -0
  38. package/dist/esm/hooks/useBrush/utils.d.ts +4 -0
  39. package/dist/esm/hooks/useBrush/utils.js +4 -0
  40. package/dist/esm/hooks/useShapes/area/prepare-data.js +85 -25
  41. package/dist/esm/hooks/useShapes/index.d.ts +3 -0
  42. package/dist/esm/hooks/useShapes/index.js +40 -27
  43. package/dist/esm/hooks/useShapes/utils.d.ts +10 -0
  44. package/dist/esm/hooks/useShapes/utils.js +15 -0
  45. package/dist/esm/setup-jsdom.d.ts +0 -0
  46. package/dist/esm/setup-jsdom.js +19 -0
  47. package/dist/esm/utils/chart/axis/common.d.ts +2 -2
  48. package/dist/esm/utils/chart/axis/common.js +2 -2
  49. package/dist/esm/utils/chart/axis/x-axis.d.ts +11 -10
  50. package/dist/esm/utils/chart/axis/x-axis.js +24 -3
  51. package/dist/esm/utils/chart/index.d.ts +2 -29
  52. package/dist/esm/utils/chart/index.js +2 -19
  53. package/dist/esm/utils/chart/series-type-guards.d.ts +30 -0
  54. package/dist/esm/utils/chart/series-type-guards.js +19 -0
  55. 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();
@@ -6,6 +6,7 @@ import { getPreparedRangeSlider } from '../../hooks/useAxis/range-slider';
6
6
  import { getPreparedChart } from '../../hooks/useChartOptions/chart';
7
7
  import { getPreparedTitle } from '../../hooks/useChartOptions/title';
8
8
  import { getPreparedTooltip } from '../../hooks/useChartOptions/tooltip';
9
+ import { getClipPathIdByBounds } from '../../hooks/useShapes/utils';
9
10
  import { EventType, block, getDispatcher, isBandScale } from '../../utils';
10
11
  import { AxisX } from '../AxisX/AxisX';
11
12
  import { prepareXAxisData } from '../AxisX/prepare-axis-data';
@@ -63,7 +64,7 @@ export const ChartInner = (props) => {
63
64
  preparedRangeSlider,
64
65
  tooltip: preparedTooltip,
65
66
  });
66
- 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,
67
68
  dispatcher,
68
69
  htmlLayout, plotNode: plotRef.current, preparedChart, rangeSliderDomain: (_a = rangeSliderRef.current) === null || _a === void 0 ? void 0 : _a.getDomain(), rangeSliderState, svgContainer: svgRef.current, updateZoomState,
69
70
  zoomState }));
@@ -159,18 +160,29 @@ export const ChartInner = (props) => {
159
160
  if (axis && scale) {
160
161
  const axisData = await prepareXAxisData({
161
162
  axis,
162
- yAxis,
163
- scale,
164
- boundsWidth,
165
163
  boundsOffsetLeft: boundsOffsetLeft,
166
164
  boundsOffsetRight: width - boundsWidth - boundsOffsetLeft,
165
+ boundsWidth,
167
166
  height: boundsHeight,
167
+ scale,
168
+ series: preparedSeries.filter((s) => s.visible),
168
169
  split: preparedSplit,
170
+ yAxis,
169
171
  });
170
172
  items.push(...axisData);
171
173
  }
172
174
  return items;
173
- }, [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
+ ]);
174
186
  const xAxisDataItems = useAsyncState([], setXAxisDataItems);
175
187
  React.useEffect(() => {
176
188
  if (!initialized && xScale) {
@@ -206,16 +218,17 @@ export const ChartInner = (props) => {
206
218
  updateRangeSliderState,
207
219
  xScale,
208
220
  ]);
209
- const areShapesReady = shapes.length > 0;
210
221
  React.useEffect(() => {
211
- if (areShapesReady) {
222
+ if (shapesReady) {
212
223
  onReady === null || onReady === void 0 ? void 0 : onReady({ dimensions: { width, height } });
213
224
  }
214
- }, [height, areShapesReady, onReady, width]);
225
+ }, [height, shapesReady, onReady, width]);
215
226
  const chartContent = (React.createElement(React.Fragment, null,
216
227
  React.createElement("defs", null,
217
- React.createElement("clipPath", { id: clipPathId },
218
- React.createElement("rect", { x: 0, y: 0, width: boundsWidth, height: boundsHeight }))),
228
+ React.createElement("clipPath", { id: getClipPathIdByBounds({ clipPathId }) },
229
+ React.createElement("rect", { x: 0, y: 0, width: boundsWidth, height: boundsHeight })),
230
+ React.createElement("clipPath", { id: getClipPathIdByBounds({ clipPathId, bounds: 'horizontal' }) },
231
+ React.createElement("rect", { x: 0, y: -boundsHeight, width: boundsWidth, height: boundsHeight * 3 }))),
219
232
  preparedTitle && React.createElement(Title, Object.assign({}, preparedTitle, { chartWidth: width })),
220
233
  React.createElement("g", { transform: `translate(0, ${boundsOffsetTop})` }, preparedSplit.plots.map((plot, index) => {
221
234
  return React.createElement(PlotTitle, { key: `plot-${index}`, title: plot.title });
@@ -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,
@@ -126,6 +126,7 @@ export function useChartInnerProps(props) {
126
126
  htmlLayout,
127
127
  clipPathId,
128
128
  isOutsideBounds,
129
+ zoomState,
129
130
  });
130
131
  const handleAttemptToSetZoomState = React.useCallback((nextZoomState) => {
131
132
  const { preparedSeries: nextZoomedSeriesData } = getZoomedSeriesData({
@@ -182,6 +183,7 @@ export function useChartInnerProps(props) {
182
183
  prevWidth,
183
184
  shapes,
184
185
  shapesData,
186
+ shapesReady,
185
187
  svgXPos: x,
186
188
  xAxis,
187
189
  xScale,
@@ -59,6 +59,7 @@ function RangeSliderComponent(props, forwardedRef) {
59
59
  brushOptions: brush,
60
60
  node: ref.current,
61
61
  onBrushEnd,
62
+ preventNullSelection: true,
62
63
  selection,
63
64
  type: 'x',
64
65
  });
@@ -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);
@@ -1,11 +1,11 @@
1
1
  import React from 'react';
2
- import { brush, brushX, brushY, select } from 'd3';
2
+ import { brush, brushX, brushY, pointer, select } from 'd3';
3
3
  import { block } from '../../utils';
4
- import { getNormalizedSelection, setBrushBorder, setBrushHandles } from './utils';
4
+ import { getDefaultSelection, getNormalizedSelection, setBrushBorder, setBrushHandles, } from './utils';
5
5
  import './styles.css';
6
6
  const b = block('brush');
7
7
  export function useBrush(props) {
8
- const { areas, brushOptions, disabled, node, selection, type, onBrushStart, onBrush, onBrushEnd, } = props;
8
+ const { areas, brushOptions, disabled, node, preventNullSelection = false, selection, type, onBrushStart, onBrush, onBrushEnd, } = props;
9
9
  React.useEffect(() => {
10
10
  if (!node || !areas.length || disabled) {
11
11
  return () => { };
@@ -103,7 +103,15 @@ export function useBrush(props) {
103
103
  });
104
104
  }
105
105
  if (event.sourceEvent) {
106
- onBrushEnd === null || onBrushEnd === void 0 ? void 0 : onBrushEnd.call(this, instance, event.selection);
106
+ let resultSelection = event.selection;
107
+ if (preventNullSelection && !resultSelection) {
108
+ const [pointerPositionX] = pointer(event, this);
109
+ resultSelection = getDefaultSelection({
110
+ brushWidth,
111
+ pointerPositionX,
112
+ });
113
+ }
114
+ onBrushEnd === null || onBrushEnd === void 0 ? void 0 : onBrushEnd.call(this, instance, resultSelection);
107
115
  }
108
116
  });
109
117
  groupSelection.call(instance);
@@ -129,5 +137,16 @@ export function useBrush(props) {
129
137
  groupSelection === null || groupSelection === void 0 ? void 0 : groupSelection.remove();
130
138
  });
131
139
  };
132
- }, [areas, brushOptions, disabled, node, selection, type, onBrushStart, onBrush, onBrushEnd]);
140
+ }, [
141
+ areas,
142
+ brushOptions,
143
+ disabled,
144
+ node,
145
+ preventNullSelection,
146
+ selection,
147
+ type,
148
+ onBrushStart,
149
+ onBrush,
150
+ onBrushEnd,
151
+ ]);
133
152
  }
@@ -15,6 +15,7 @@ export interface BrushArea {
15
15
  export interface UseBrushProps {
16
16
  areas: BrushArea[];
17
17
  node: SVGGElement | null;
18
+ preventNullSelection?: boolean;
18
19
  brushOptions?: DeepRequired<ChartBrush>;
19
20
  disabled?: boolean;
20
21
  onBrush?: (this: SVGGElement, brushInstance: BrushBehavior<unknown>, selection: BrushSelection) => void;
@@ -17,3 +17,7 @@ export declare function getNormalizedSelection(args: {
17
17
  selection: BrushSelection;
18
18
  width: number;
19
19
  }): [number, number] | [[number, number], [number, number]];
20
+ export declare function getDefaultSelection(args: {
21
+ brushWidth: number;
22
+ pointerPositionX: number;
23
+ }): BrushSelection;
@@ -170,3 +170,7 @@ export function getNormalizedSelection(args) {
170
170
  }
171
171
  return resultSelection;
172
172
  }
173
+ export function getDefaultSelection(args) {
174
+ const { brushWidth, pointerPositionX } = args;
175
+ return pointerPositionX < 0 ? [0, 1] : [brushWidth - 1, brushWidth];
176
+ }
@@ -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) => {
@@ -5,6 +5,7 @@ import type { PreparedXAxis, PreparedYAxis } from '../useAxis/types';
5
5
  import type { ChartScale } from '../useAxisScales/types';
6
6
  import type { PreparedSeries, PreparedSeriesOptions } from '../useSeries/types';
7
7
  import type { PreparedSplit } from '../useSplit/types';
8
+ import type { ZoomState } from '../useZoom/types';
8
9
  import type { PreparedAreaData } from './area/types';
9
10
  import type { PreparedBarXData } from './bar-x';
10
11
  import type { PreparedBarYData } from './bar-y/types';
@@ -37,8 +38,10 @@ type Args = {
37
38
  isRangeSlider?: boolean;
38
39
  xScale?: ChartScale;
39
40
  yScale?: (ChartScale | undefined)[];
41
+ zoomState?: Partial<ZoomState>;
40
42
  };
41
43
  export declare const useShapes: (args: Args) => {
42
44
  shapes: React.ReactElement<any, string | React.JSXElementConstructor<any>>[];
43
45
  shapesData: ShapeData[];
46
+ shapesReady: boolean;
44
47
  };
@@ -20,6 +20,7 @@ import { prepareSankeyData } from './sankey/prepare-data';
20
20
  import { ScatterSeriesShape, prepareScatterData } from './scatter';
21
21
  import { TreemapSeriesShape } from './treemap';
22
22
  import { prepareTreemapData } from './treemap/prepare-data';
23
+ import { getSeriesClipPathId } from './utils';
23
24
  import { WaterfallSeriesShapes, prepareWaterfallData } from './waterfall';
24
25
  import './styles.css';
25
26
  function IS_OUTSIDE_BOUNDS() {
@@ -30,9 +31,10 @@ function shouldUseClipPathId(seriesType, clipPathBySeriesType) {
30
31
  return (_a = clipPathBySeriesType === null || clipPathBySeriesType === void 0 ? void 0 : clipPathBySeriesType[seriesType]) !== null && _a !== void 0 ? _a : true;
31
32
  }
32
33
  export const useShapes = (args) => {
33
- const { boundsWidth, boundsHeight, clipPathId, clipPathBySeriesType, dispatcher, htmlLayout, isOutsideBounds = IS_OUTSIDE_BOUNDS, isRangeSlider, series, seriesOptions, split, xAxis, xScale, yAxis, yScale, } = args;
34
+ const { boundsWidth, boundsHeight, clipPathId, clipPathBySeriesType, dispatcher, htmlLayout, isOutsideBounds = IS_OUTSIDE_BOUNDS, isRangeSlider, series, seriesOptions, split, xAxis, xScale, yAxis, yScale, zoomState, } = args;
34
35
  const [shapesElemens, setShapesElements] = React.useState([]);
35
36
  const [shapesElemensData, setShapesElemensData] = React.useState([]);
37
+ const shapesReadyRef = React.useRef(false);
36
38
  const countedRef = React.useRef(0);
37
39
  React.useEffect(() => {
38
40
  countedRef.current++;
@@ -44,7 +46,7 @@ export const useShapes = (args) => {
44
46
  const shapes = [];
45
47
  await Promise.all(
46
48
  // eslint-disable-next-line complexity
47
- Array.from(groupedSeries).map(async (item) => {
49
+ Array.from(groupedSeries).map(async (item, index) => {
48
50
  const [seriesType, chartSeries] = item;
49
51
  switch (seriesType) {
50
52
  case SERIES_TYPE.BarX: {
@@ -60,8 +62,8 @@ export const useShapes = (args) => {
60
62
  split,
61
63
  isRangeSlider,
62
64
  });
63
- shapes.push(React.createElement(BarXSeriesShapes, { key: SERIES_TYPE.BarX, dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData, htmlLayout: htmlLayout, clipPathId: clipPathId }));
64
- 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);
65
67
  }
66
68
  break;
67
69
  }
@@ -77,8 +79,8 @@ export const useShapes = (args) => {
77
79
  yAxis,
78
80
  yScale,
79
81
  });
80
- shapes.push(React.createElement(BarYSeriesShapes, { key: SERIES_TYPE.BarY, dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData, htmlLayout: htmlLayout, clipPathId: clipPathId }));
81
- 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);
82
84
  }
83
85
  break;
84
86
  }
@@ -92,8 +94,8 @@ export const useShapes = (args) => {
92
94
  yAxis,
93
95
  yScale,
94
96
  });
95
- shapes.push(React.createElement(WaterfallSeriesShapes, { key: SERIES_TYPE.Waterfall, dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData, htmlLayout: htmlLayout, clipPathId: clipPathId }));
96
- 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);
97
99
  }
98
100
  break;
99
101
  }
@@ -109,8 +111,13 @@ export const useShapes = (args) => {
109
111
  isOutsideBounds,
110
112
  isRangeSlider,
111
113
  });
112
- shapes.push(React.createElement(LineSeriesShapes, { key: SERIES_TYPE.Line, dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData, htmlLayout: htmlLayout, clipPathId: clipPathId }));
113
- shapesData.push(...preparedData);
114
+ const resultClipPathId = getSeriesClipPathId({
115
+ clipPathId,
116
+ yAxis,
117
+ zoomState,
118
+ });
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);
114
121
  }
115
122
  break;
116
123
  }
@@ -127,8 +134,8 @@ export const useShapes = (args) => {
127
134
  isOutsideBounds,
128
135
  isRangeSlider,
129
136
  });
130
- shapes.push(React.createElement(AreaSeriesShapes, { key: SERIES_TYPE.Area, dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData, htmlLayout: htmlLayout, clipPathId: clipPathId }));
131
- 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);
132
139
  }
133
140
  break;
134
141
  }
@@ -142,10 +149,10 @@ export const useShapes = (args) => {
142
149
  yScale,
143
150
  isOutsideBounds,
144
151
  });
145
- 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)
146
153
  ? clipPathId
147
154
  : undefined, dispatcher: dispatcher, preparedData: preparedData, seriesOptions: seriesOptions, htmlLayout: htmlLayout }));
148
- shapesData.push(...preparedData);
155
+ shapesData.splice(index, 0, ...preparedData);
149
156
  }
150
157
  break;
151
158
  }
@@ -155,8 +162,8 @@ export const useShapes = (args) => {
155
162
  boundsWidth,
156
163
  boundsHeight,
157
164
  });
158
- shapes.push(React.createElement(PieSeriesShapes, { key: SERIES_TYPE.Pie, dispatcher: dispatcher, preparedData: preparedData, seriesOptions: seriesOptions, htmlLayout: htmlLayout }));
159
- 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);
160
167
  break;
161
168
  }
162
169
  case SERIES_TYPE.Treemap: {
@@ -167,8 +174,8 @@ export const useShapes = (args) => {
167
174
  width: boundsWidth,
168
175
  height: boundsHeight,
169
176
  });
170
- shapes.push(React.createElement(TreemapSeriesShape, { key: SERIES_TYPE.Treemap, dispatcher: dispatcher, preparedData: preparedData, seriesOptions: seriesOptions, htmlLayout: htmlLayout }));
171
- 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);
172
179
  break;
173
180
  }
174
181
  case SERIES_TYPE.Sankey: {
@@ -177,8 +184,8 @@ export const useShapes = (args) => {
177
184
  width: boundsWidth,
178
185
  height: boundsHeight,
179
186
  });
180
- shapes.push(React.createElement(SankeySeriesShape, { key: SERIES_TYPE.Sankey, dispatcher: dispatcher, preparedData: preparedData, seriesOptions: seriesOptions, htmlLayout: htmlLayout }));
181
- 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);
182
189
  break;
183
190
  }
184
191
  case SERIES_TYPE.Radar: {
@@ -187,8 +194,8 @@ export const useShapes = (args) => {
187
194
  boundsWidth,
188
195
  boundsHeight,
189
196
  });
190
- shapes.push(React.createElement(RadarSeriesShapes, { key: SERIES_TYPE.Radar, dispatcher: dispatcher, series: preparedData, seriesOptions: seriesOptions, htmlLayout: htmlLayout }));
191
- 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);
192
199
  break;
193
200
  }
194
201
  case SERIES_TYPE.Heatmap: {
@@ -200,8 +207,8 @@ export const useShapes = (args) => {
200
207
  yAxis: yAxis[0],
201
208
  yScale: yScale[0],
202
209
  });
203
- shapes.push(React.createElement(HeatmapSeriesShapes, { key: SERIES_TYPE.Heatmap, dispatcher: dispatcher, preparedData: preparedData, seriesOptions: seriesOptions, htmlLayout: htmlLayout }));
204
- 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);
205
212
  }
206
213
  break;
207
214
  }
@@ -211,8 +218,8 @@ export const useShapes = (args) => {
211
218
  boundsWidth,
212
219
  boundsHeight,
213
220
  });
214
- shapes.push(React.createElement(FunnelSeriesShapes, { key: "funnel", dispatcher: dispatcher, preparedData: preparedData, seriesOptions: seriesOptions, htmlLayout: htmlLayout }));
215
- 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);
216
223
  break;
217
224
  }
218
225
  default: {
@@ -223,6 +230,7 @@ export const useShapes = (args) => {
223
230
  }
224
231
  }));
225
232
  if (countedRef.current === currentRun) {
233
+ shapesReadyRef.current = true;
226
234
  setShapesElements(shapes);
227
235
  setShapesElemensData(shapesData);
228
236
  }
@@ -243,6 +251,11 @@ export const useShapes = (args) => {
243
251
  xScale,
244
252
  yAxis,
245
253
  yScale,
254
+ zoomState,
246
255
  ]);
247
- return { shapes: shapesElemens, shapesData: shapesElemensData };
256
+ return {
257
+ shapes: shapesElemens,
258
+ shapesData: shapesElemensData,
259
+ shapesReady: shapesReadyRef.current,
260
+ };
248
261
  };
@@ -2,6 +2,7 @@ import type { BaseType } from 'd3';
2
2
  import type { BasicInactiveState } from '../../types';
3
3
  import type { PreparedXAxis, PreparedYAxis } from '../useAxis/types';
4
4
  import type { ChartScale } from '../useAxisScales/types';
5
+ import type { ZoomState } from '../useZoom/types';
5
6
  export declare function getXValue(args: {
6
7
  point: {
7
8
  x?: number | string | null;
@@ -46,3 +47,12 @@ export declare function getRectBorderPath(args: {
46
47
  borderWidth: number;
47
48
  borderRadius?: number | number[];
48
49
  }): string;
50
+ export declare function getClipPathIdByBounds(args: {
51
+ clipPathId: string;
52
+ bounds?: 'horizontal';
53
+ }): string;
54
+ export declare function getSeriesClipPathId(args: {
55
+ clipPathId: string;
56
+ yAxis: PreparedYAxis[];
57
+ zoomState?: Partial<ZoomState>;
58
+ }): string;