@gravity-ui/charts 1.10.2 → 1.11.0

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 (83) hide show
  1. package/dist/cjs/components/ChartInner/index.js +8 -3
  2. package/dist/cjs/components/ChartInner/useChartInnerHandlers.d.ts +2 -1
  3. package/dist/cjs/components/ChartInner/useChartInnerHandlers.js +1 -5
  4. package/dist/cjs/components/ChartInner/useChartInnerProps.d.ts +2 -0
  5. package/dist/cjs/components/ChartInner/useChartInnerProps.js +9 -3
  6. package/dist/cjs/components/ChartInner/utils.d.ts +1 -1
  7. package/dist/cjs/components/ChartInner/utils.js +3 -3
  8. package/dist/cjs/hooks/hooks-utils/zoom.d.ts +1 -1
  9. package/dist/cjs/hooks/hooks-utils/zoom.js +2 -2
  10. package/dist/cjs/hooks/useAxisScales/index.js +49 -18
  11. package/dist/cjs/hooks/useChartOptions/x-axis.js +3 -14
  12. package/dist/cjs/hooks/useChartOptions/y-axis.js +5 -24
  13. package/dist/cjs/hooks/useShapes/area/index.d.ts +1 -0
  14. package/dist/cjs/hooks/useShapes/area/index.js +13 -9
  15. package/dist/cjs/hooks/useShapes/area/prepare-data.d.ts +1 -0
  16. package/dist/cjs/hooks/useShapes/area/prepare-data.js +2 -1
  17. package/dist/cjs/hooks/useShapes/area/types.d.ts +1 -0
  18. package/dist/cjs/hooks/useShapes/bar-x/index.d.ts +1 -0
  19. package/dist/cjs/hooks/useShapes/bar-x/index.js +2 -2
  20. package/dist/cjs/hooks/useShapes/bar-y/index.d.ts +1 -0
  21. package/dist/cjs/hooks/useShapes/bar-y/index.js +2 -2
  22. package/dist/cjs/hooks/useShapes/index.d.ts +2 -0
  23. package/dist/cjs/hooks/useShapes/index.js +11 -6
  24. package/dist/cjs/hooks/useShapes/line/index.d.ts +1 -0
  25. package/dist/cjs/hooks/useShapes/line/index.js +16 -12
  26. package/dist/cjs/hooks/useShapes/line/prepare-data.d.ts +1 -0
  27. package/dist/cjs/hooks/useShapes/line/prepare-data.js +2 -1
  28. package/dist/cjs/hooks/useShapes/line/types.d.ts +1 -0
  29. package/dist/cjs/hooks/useShapes/marker.js +6 -0
  30. package/dist/cjs/hooks/useShapes/scatter/prepare-data.d.ts +1 -0
  31. package/dist/cjs/hooks/useShapes/scatter/prepare-data.js +6 -3
  32. package/dist/cjs/hooks/useShapes/scatter/types.d.ts +1 -0
  33. package/dist/cjs/hooks/useShapes/waterfall/index.d.ts +1 -0
  34. package/dist/cjs/hooks/useShapes/waterfall/index.js +2 -2
  35. package/dist/cjs/hooks/useZoom/index.js +1 -1
  36. package/dist/cjs/hooks/useZoom/utils.d.ts +1 -1
  37. package/dist/cjs/hooks/useZoom/utils.js +3 -3
  38. package/dist/cjs/types/chart/axis.d.ts +4 -5
  39. package/dist/cjs/utils/chart/axis-generators/bottom.js +3 -1
  40. package/dist/cjs/utils/chart/index.d.ts +2 -0
  41. package/dist/cjs/utils/chart/index.js +31 -0
  42. package/dist/esm/components/ChartInner/index.js +8 -3
  43. package/dist/esm/components/ChartInner/useChartInnerHandlers.d.ts +2 -1
  44. package/dist/esm/components/ChartInner/useChartInnerHandlers.js +1 -5
  45. package/dist/esm/components/ChartInner/useChartInnerProps.d.ts +2 -0
  46. package/dist/esm/components/ChartInner/useChartInnerProps.js +9 -3
  47. package/dist/esm/components/ChartInner/utils.d.ts +1 -1
  48. package/dist/esm/components/ChartInner/utils.js +3 -3
  49. package/dist/esm/hooks/hooks-utils/zoom.d.ts +1 -1
  50. package/dist/esm/hooks/hooks-utils/zoom.js +2 -2
  51. package/dist/esm/hooks/useAxisScales/index.js +49 -18
  52. package/dist/esm/hooks/useChartOptions/x-axis.js +3 -14
  53. package/dist/esm/hooks/useChartOptions/y-axis.js +5 -24
  54. package/dist/esm/hooks/useShapes/area/index.d.ts +1 -0
  55. package/dist/esm/hooks/useShapes/area/index.js +13 -9
  56. package/dist/esm/hooks/useShapes/area/prepare-data.d.ts +1 -0
  57. package/dist/esm/hooks/useShapes/area/prepare-data.js +2 -1
  58. package/dist/esm/hooks/useShapes/area/types.d.ts +1 -0
  59. package/dist/esm/hooks/useShapes/bar-x/index.d.ts +1 -0
  60. package/dist/esm/hooks/useShapes/bar-x/index.js +2 -2
  61. package/dist/esm/hooks/useShapes/bar-y/index.d.ts +1 -0
  62. package/dist/esm/hooks/useShapes/bar-y/index.js +2 -2
  63. package/dist/esm/hooks/useShapes/index.d.ts +2 -0
  64. package/dist/esm/hooks/useShapes/index.js +11 -6
  65. package/dist/esm/hooks/useShapes/line/index.d.ts +1 -0
  66. package/dist/esm/hooks/useShapes/line/index.js +16 -12
  67. package/dist/esm/hooks/useShapes/line/prepare-data.d.ts +1 -0
  68. package/dist/esm/hooks/useShapes/line/prepare-data.js +2 -1
  69. package/dist/esm/hooks/useShapes/line/types.d.ts +1 -0
  70. package/dist/esm/hooks/useShapes/marker.js +6 -0
  71. package/dist/esm/hooks/useShapes/scatter/prepare-data.d.ts +1 -0
  72. package/dist/esm/hooks/useShapes/scatter/prepare-data.js +6 -3
  73. package/dist/esm/hooks/useShapes/scatter/types.d.ts +1 -0
  74. package/dist/esm/hooks/useShapes/waterfall/index.d.ts +1 -0
  75. package/dist/esm/hooks/useShapes/waterfall/index.js +2 -2
  76. package/dist/esm/hooks/useZoom/index.js +1 -1
  77. package/dist/esm/hooks/useZoom/utils.d.ts +1 -1
  78. package/dist/esm/hooks/useZoom/utils.js +3 -3
  79. package/dist/esm/types/chart/axis.d.ts +4 -5
  80. package/dist/esm/utils/chart/axis-generators/bottom.js +3 -1
  81. package/dist/esm/utils/chart/index.d.ts +2 -0
  82. package/dist/esm/utils/chart/index.js +31 -0
  83. package/package.json +1 -1
@@ -2,7 +2,7 @@ import React from 'react';
2
2
  import { extent, scaleBand, scaleLinear, scaleLog, scaleUtc } from 'd3';
3
3
  import get from 'lodash/get';
4
4
  import { DEFAULT_AXIS_TYPE } from '../../constants';
5
- import { CHART_SERIES_WITH_VOLUME_ON_Y_AXIS, getAxisHeight, getDataCategoryValue, getDefaultMaxXAxisValue, getDomainDataXBySeries, getDomainDataYBySeries, getOnlyVisibleSeries, isAxisRelatedSeries, isSeriesWithCategoryValues, } from '../../utils';
5
+ import { CHART_SERIES_WITH_VOLUME_ON_Y_AXIS, getAxisHeight, getDataCategoryValue, getDefaultMaxXAxisValue, getDefaultMinXAxisValue, getDomainDataXBySeries, getDomainDataYBySeries, getOnlyVisibleSeries, isAxisRelatedSeries, isSeriesWithCategoryValues, } from '../../utils';
6
6
  const isNumericalArrayData = (data) => {
7
7
  return data.every((d) => typeof d === 'number' || d === null);
8
8
  };
@@ -20,7 +20,8 @@ const filterCategoriesByVisibleSeries = (args) => {
20
20
  };
21
21
  export function createYScale(axis, series, boundsHeight) {
22
22
  const yType = get(axis, 'type', DEFAULT_AXIS_TYPE);
23
- const yMin = get(axis, 'min');
23
+ const yMinProps = get(axis, 'min');
24
+ const yMaxProps = get(axis, 'max');
24
25
  const yCategories = get(axis, 'categories');
25
26
  const yTimestamps = get(axis, 'timestamps');
26
27
  switch (yType) {
@@ -29,14 +30,18 @@ export function createYScale(axis, series, boundsHeight) {
29
30
  const domain = getDomainDataYBySeries(series);
30
31
  const range = [boundsHeight, boundsHeight * axis.maxPadding];
31
32
  if (isNumericalArrayData(domain)) {
32
- const [domainYMin, domainMax] = extent(domain);
33
- const yMinValue = typeof yMin === 'number' ? yMin : domainYMin;
34
- let yMaxValue = domainMax;
35
- if (series.some((s) => CHART_SERIES_WITH_VOLUME_ON_Y_AXIS.includes(s.type))) {
36
- yMaxValue = Math.max(yMaxValue, 0);
33
+ const [yMinDomain, yMaxDomain] = extent(domain);
34
+ const yMin = typeof yMinProps === 'number' ? yMinProps : yMinDomain;
35
+ let yMax;
36
+ if (typeof yMaxProps === 'number') {
37
+ yMax = yMaxProps;
38
+ }
39
+ else {
40
+ const hasSeriesWithVolumeOnYAxis = series.some((s) => CHART_SERIES_WITH_VOLUME_ON_Y_AXIS.includes(s.type));
41
+ yMax = hasSeriesWithVolumeOnYAxis ? Math.max(yMaxDomain, 0) : yMaxDomain;
37
42
  }
38
43
  const scaleFn = yType === 'logarithmic' ? scaleLog : scaleLinear;
39
- return scaleFn().domain([yMinValue, yMaxValue]).range(range).nice();
44
+ return scaleFn().domain([yMin, yMax]).range(range).nice();
40
45
  }
41
46
  break;
42
47
  }
@@ -54,13 +59,17 @@ export function createYScale(axis, series, boundsHeight) {
54
59
  case 'datetime': {
55
60
  const range = [boundsHeight, boundsHeight * axis.maxPadding];
56
61
  if (yTimestamps) {
57
- const [yMin, yMax] = extent(yTimestamps);
62
+ const [yMinTimestamp, yMaxTimestamp] = extent(yTimestamps);
63
+ const yMin = typeof yMinProps === 'number' ? yMinProps : yMinTimestamp;
64
+ const yMax = typeof yMaxProps === 'number' ? yMaxProps : yMaxTimestamp;
58
65
  return scaleUtc().domain([yMin, yMax]).range(range).nice();
59
66
  }
60
67
  else {
61
68
  const domain = getDomainDataYBySeries(series);
62
69
  if (isNumericalArrayData(domain)) {
63
- const [yMin, yMax] = extent(domain);
70
+ const [yMinTimestamp, yMaxTimestamp] = extent(domain);
71
+ const yMin = typeof yMinProps === 'number' ? yMinProps : yMinTimestamp;
72
+ const yMax = typeof yMaxProps === 'number' ? yMaxProps : yMaxTimestamp;
64
73
  return scaleUtc().domain([yMin, yMax]).range(range).nice();
65
74
  }
66
75
  }
@@ -86,9 +95,10 @@ function calculateXAxisPadding(series) {
86
95
  });
87
96
  return result;
88
97
  }
98
+ // eslint-disable-next-line complexity
89
99
  export function createXScale(axis, series, boundsWidth, hasZoomX) {
90
- const xMin = get(axis, 'min');
91
- const xMax = getDefaultMaxXAxisValue(series);
100
+ const xMinProps = get(axis, 'min');
101
+ const xMaxProps = get(axis, 'max');
92
102
  const xType = get(axis, 'type', DEFAULT_AXIS_TYPE);
93
103
  const xCategories = get(axis, 'categories');
94
104
  const xTimestamps = get(axis, 'timestamps');
@@ -100,11 +110,28 @@ export function createXScale(axis, series, boundsWidth, hasZoomX) {
100
110
  case 'logarithmic': {
101
111
  const domain = getDomainDataXBySeries(series);
102
112
  if (isNumericalArrayData(domain)) {
103
- const [domainXMin, domainXMax] = extent(domain);
104
- const xMinValue = typeof xMin === 'number' ? xMin : domainXMin;
105
- const xMaxValue = typeof xMax === 'number' ? Math.max(xMax, domainXMax) : domainXMax;
113
+ const [xMinDomain, xMaxDomain] = extent(domain);
114
+ let xMin;
115
+ let xMax;
116
+ if (typeof xMinProps === 'number') {
117
+ xMin = xMinProps;
118
+ }
119
+ else {
120
+ const xMinDefault = getDefaultMinXAxisValue(series);
121
+ xMin = xMinDefault !== null && xMinDefault !== void 0 ? xMinDefault : xMinDomain;
122
+ }
123
+ if (typeof xMaxProps === 'number') {
124
+ xMax = xMaxProps;
125
+ }
126
+ else {
127
+ const xMaxDefault = getDefaultMaxXAxisValue(series);
128
+ xMax =
129
+ typeof xMaxDefault === 'number'
130
+ ? Math.max(xMaxDefault, xMaxDomain)
131
+ : xMaxDomain;
132
+ }
106
133
  const scaleFn = xType === 'logarithmic' ? scaleLog : scaleLinear;
107
- const scale = scaleFn().domain([xMinValue, xMaxValue]).range(xRange);
134
+ const scale = scaleFn().domain([xMin, xMax]).range(xRange);
108
135
  if (!hasZoomX) {
109
136
  scale.nice();
110
137
  }
@@ -129,7 +156,9 @@ export function createXScale(axis, series, boundsWidth, hasZoomX) {
129
156
  }
130
157
  case 'datetime': {
131
158
  if (xTimestamps) {
132
- const [xMin, xMax] = extent(xTimestamps);
159
+ const [xMinTimestamp, xMaxTimestamp] = extent(xTimestamps);
160
+ const xMin = typeof xMinProps === 'number' ? xMinProps : xMinTimestamp;
161
+ const xMax = typeof xMaxProps === 'number' ? xMaxProps : xMaxTimestamp;
133
162
  const scale = scaleUtc().domain([xMin, xMax]).range(xRange);
134
163
  if (!hasZoomX) {
135
164
  scale.nice();
@@ -139,7 +168,9 @@ export function createXScale(axis, series, boundsWidth, hasZoomX) {
139
168
  else {
140
169
  const domain = getDomainDataXBySeries(series);
141
170
  if (isNumericalArrayData(domain)) {
142
- const [xMin, xMax] = extent(domain);
171
+ const [xMinTimestamp, xMaxTimestamp] = extent(domain);
172
+ const xMin = typeof xMinProps === 'number' ? xMinProps : xMinTimestamp;
173
+ const xMax = typeof xMaxProps === 'number' ? xMaxProps : xMaxTimestamp;
143
174
  const scale = scaleUtc().domain([xMin, xMax]).range(xRange);
144
175
  if (!hasZoomX) {
145
176
  scale.nice();
@@ -1,6 +1,6 @@
1
1
  import get from 'lodash/get';
2
2
  import { DASH_STYLE, DEFAULT_AXIS_LABEL_FONT_SIZE, axisCrosshairDefaults, axisLabelsDefaults, xAxisTitleDefaults, } from '../../constants';
3
- import { CHART_SERIES_WITH_VOLUME_ON_X_AXIS, calculateCos, formatAxisTickLabel, getClosestPointsRange, getHorisontalSvgTextHeight, getLabelsSize, getMaxTickCount, getTicksCount, getXAxisItems, hasOverlappingLabels, wrapText, } from '../../utils';
3
+ import { calculateCos, formatAxisTickLabel, getClosestPointsRange, getHorisontalSvgTextHeight, getLabelsSize, getMaxTickCount, getTicksCount, getXAxisItems, hasOverlappingLabels, wrapText, } from '../../utils';
4
4
  import { createXScale } from '../useAxisScales';
5
5
  async function getLabelSettings({ axis, seriesData, width, autoRotation = true, }) {
6
6
  const scale = createXScale(axis, seriesData, width);
@@ -36,18 +36,6 @@ async function getLabelSettings({ axis, seriesData, width, autoRotation = true,
36
36
  const maxHeight = rotation ? calculateCos(rotation) * axis.labels.maxWidth : labelsHeight;
37
37
  return { height: Math.min(maxHeight, labelsHeight), rotation };
38
38
  }
39
- function getAxisMin(axis, series) {
40
- const min = axis === null || axis === void 0 ? void 0 : axis.min;
41
- if (typeof min === 'undefined' &&
42
- (series === null || series === void 0 ? void 0 : series.some((s) => CHART_SERIES_WITH_VOLUME_ON_X_AXIS.includes(s.type)))) {
43
- return series.reduce((minValue, s) => {
44
- // @ts-expect-error
45
- const minYValue = s.data.reduce((res, d) => Math.min(res, get(d, 'x', 0)), 0);
46
- return Math.min(minValue, minYValue);
47
- }, 0);
48
- }
49
- return min;
50
- }
51
39
  export const getPreparedXAxis = async ({ xAxis, seriesData, width, }) => {
52
40
  var _a;
53
41
  const titleText = get(xAxis, 'title.text', '');
@@ -92,7 +80,8 @@ export const getPreparedXAxis = async ({ xAxis, seriesData, width, }) => {
92
80
  align: get(xAxis, 'title.align', xAxisTitleDefaults.align),
93
81
  maxRowCount: get(xAxis, 'title.maxRowCount', xAxisTitleDefaults.maxRowCount),
94
82
  },
95
- min: getAxisMin(xAxis, seriesData),
83
+ min: get(xAxis, 'min'),
84
+ max: get(xAxis, 'max'),
96
85
  maxPadding: get(xAxis, 'maxPadding', 0.01),
97
86
  grid: {
98
87
  enabled: get(xAxis, 'grid.enabled', true),
@@ -1,6 +1,6 @@
1
1
  import get from 'lodash/get';
2
2
  import { DASH_STYLE, DEFAULT_AXIS_LABEL_FONT_SIZE, DEFAULT_AXIS_TYPE, axisCrosshairDefaults, axisLabelsDefaults, yAxisTitleDefaults, } from '../../constants';
3
- import { CHART_SERIES_WITH_VOLUME_ON_Y_AXIS, formatAxisTickLabel, getClosestPointsRange, getHorisontalSvgTextHeight, getLabelsSize, getScaleTicks, getWaterfallPointSubtotal, isAxisRelatedSeries, wrapText, } from '../../utils';
3
+ import { formatAxisTickLabel, getClosestPointsRange, getDefaultMinYAxisValue, getHorisontalSvgTextHeight, getLabelsSize, getScaleTicks, isAxisRelatedSeries, wrapText, } from '../../utils';
4
4
  import { createYScale } from '../useAxisScales';
5
5
  const getAxisLabelMaxWidth = async (args) => {
6
6
  const { axis, seriesData } = args;
@@ -23,26 +23,6 @@ const getAxisLabelMaxWidth = async (args) => {
23
23
  });
24
24
  return size.maxWidth;
25
25
  };
26
- function getAxisMin(axis, seriesData) {
27
- const min = axis === null || axis === void 0 ? void 0 : axis.min;
28
- if (typeof min === 'undefined' &&
29
- (seriesData === null || seriesData === void 0 ? void 0 : seriesData.some((s) => CHART_SERIES_WITH_VOLUME_ON_Y_AXIS.includes(s.type)))) {
30
- return seriesData.reduce((minValue, s) => {
31
- switch (s.type) {
32
- case 'waterfall': {
33
- const minSubTotal = s.data.reduce((res, d) => Math.min(res, getWaterfallPointSubtotal(d, s) || 0), 0);
34
- return Math.min(minValue, minSubTotal);
35
- }
36
- default: {
37
- // @ts-expect-error
38
- const minYValue = s.data.reduce((res, d) => Math.min(res, get(d, 'y', 0)), 0);
39
- return Math.min(minValue, minYValue);
40
- }
41
- }
42
- }, 0);
43
- }
44
- return min;
45
- }
46
26
  export const getPreparedYAxis = ({ seriesData, yAxis, height, }) => {
47
27
  const axisByPlot = [];
48
28
  const axisItems = yAxis || [{}];
@@ -51,7 +31,7 @@ export const getPreparedYAxis = ({ seriesData, yAxis, height, }) => {
51
31
  return Promise.resolve([]);
52
32
  }
53
33
  return Promise.all(axisItems.map(async (axisItem) => {
54
- var _a;
34
+ var _a, _b;
55
35
  const plotIndex = get(axisItem, 'plotIndex', 0);
56
36
  const firstPlotAxis = !axisByPlot[plotIndex];
57
37
  if (firstPlotAxis) {
@@ -104,11 +84,12 @@ export const getPreparedYAxis = ({ seriesData, yAxis, height, }) => {
104
84
  align: get(axisItem, 'title.align', yAxisTitleDefaults.align),
105
85
  maxRowCount: titleMaxRowsCount,
106
86
  },
107
- min: getAxisMin(axisItem, seriesData),
87
+ min: (_a = get(axisItem, 'min')) !== null && _a !== void 0 ? _a : getDefaultMinYAxisValue(seriesData),
88
+ max: get(axisItem, 'max'),
108
89
  maxPadding: get(axisItem, 'maxPadding', 0.05),
109
90
  grid: {
110
91
  enabled: get(axisItem, 'grid.enabled', firstPlotAxis ||
111
- (!firstPlotAxis && !((_a = axisByPlot[plotIndex][0].visible) !== null && _a !== void 0 ? _a : true))),
92
+ (!firstPlotAxis && !((_b = axisByPlot[plotIndex][0].visible) !== null && _b !== void 0 ? _b : true))),
112
93
  },
113
94
  ticks: {
114
95
  pixelInterval: get(axisItem, 'ticks.pixelInterval'),
@@ -7,6 +7,7 @@ type Args = {
7
7
  preparedData: PreparedAreaData[];
8
8
  seriesOptions: PreparedSeriesOptions;
9
9
  htmlLayout: HTMLElement | null;
10
+ clipPathId: string;
10
11
  };
11
12
  export declare const AreaSeriesShapes: (args: Args) => React.JSX.Element;
12
13
  export {};
@@ -7,22 +7,25 @@ import { getMarkerHaloVisibility, getMarkerVisibility, renderMarker, selectMarke
7
7
  import { setActiveState } from '../utils';
8
8
  const b = block('area');
9
9
  export const AreaSeriesShapes = (args) => {
10
- const { dispatcher, preparedData, seriesOptions, htmlLayout } = args;
10
+ const { dispatcher, preparedData, seriesOptions, htmlLayout, clipPathId } = args;
11
11
  const hoveredDataRef = React.useRef(null);
12
- const ref = React.useRef(null);
12
+ const plotRef = React.useRef(null);
13
+ const markersRef = React.useRef(null);
13
14
  React.useEffect(() => {
14
15
  var _a;
15
- if (!ref.current) {
16
+ if (!plotRef.current || !markersRef.current) {
16
17
  return () => { };
17
18
  }
18
- const svgElement = select(ref.current);
19
+ const plotSvgElement = select(plotRef.current);
20
+ const markersSvgElement = select(markersRef.current);
19
21
  const hoverOptions = get(seriesOptions, 'area.states.hover');
20
22
  const inactiveOptions = get(seriesOptions, 'area.states.inactive');
21
23
  const line = lineGenerator()
22
24
  .x((d) => d.x)
23
25
  .y((d) => d.y);
24
- svgElement.selectAll('*').remove();
25
- const shapeSelection = svgElement
26
+ plotSvgElement.selectAll('*').remove();
27
+ markersSvgElement.selectAll('*').remove();
28
+ const shapeSelection = plotSvgElement
26
29
  .selectAll('shape')
27
30
  .data(preparedData)
28
31
  .join('g')
@@ -53,7 +56,7 @@ export const AreaSeriesShapes = (args) => {
53
56
  if (!((_a = preparedData[0]) === null || _a === void 0 ? void 0 : _a.series.dataLabels.allowOverlap)) {
54
57
  dataLabels = filterOverlappingLabels(dataLabels);
55
58
  }
56
- const labelsSelection = svgElement
59
+ const labelsSelection = plotSvgElement
57
60
  .selectAll('text')
58
61
  .data(dataLabels)
59
62
  .join('text')
@@ -66,7 +69,7 @@ export const AreaSeriesShapes = (args) => {
66
69
  .style('font-weight', (d) => d.style.fontWeight || null)
67
70
  .style('fill', (d) => d.style.fontColor || null);
68
71
  const markers = preparedData.reduce((acc, d) => acc.concat(d.markers), []);
69
- const markerSelection = svgElement
72
+ const markerSelection = markersSvgElement
70
73
  .selectAll('marker')
71
74
  .data(markers)
72
75
  .join('g')
@@ -144,6 +147,7 @@ export const AreaSeriesShapes = (args) => {
144
147
  };
145
148
  }, [dispatcher, preparedData, seriesOptions]);
146
149
  return (React.createElement(React.Fragment, null,
147
- React.createElement("g", { ref: ref, className: b() }),
150
+ React.createElement("g", { ref: plotRef, className: b(), clipPath: `url(#${clipPathId})` }),
151
+ React.createElement("g", { ref: markersRef }),
148
152
  React.createElement(HtmlLayer, { preparedData: preparedData, htmlLayout: htmlLayout })));
149
153
  };
@@ -9,4 +9,5 @@ export declare const prepareAreaData: (args: {
9
9
  yAxis: PreparedAxis[];
10
10
  yScale: ChartScale[];
11
11
  boundsHeight: number;
12
+ isOutsideBounds: (x: number, y: number) => boolean;
12
13
  }) => Promise<PreparedAreaData[]>;
@@ -53,7 +53,7 @@ function getXValues(series, xAxis, xScale) {
53
53
  return sort(Array.from(xValues), ([_x, xValue]) => xValue);
54
54
  }
55
55
  export const prepareAreaData = async (args) => {
56
- const { series, xAxis, xScale, yAxis, yScale, boundsHeight: plotHeight } = args;
56
+ const { series, xAxis, xScale, yAxis, yScale, boundsHeight: plotHeight, isOutsideBounds } = args;
57
57
  const [_xMin, xRangeMax] = xScale.range();
58
58
  const xMax = xRangeMax / (1 - xAxis.maxPadding);
59
59
  const result = [];
@@ -142,6 +142,7 @@ export const prepareAreaData = async (args) => {
142
142
  point: p,
143
143
  active: true,
144
144
  hovered: false,
145
+ clipped: isOutsideBounds(p.x, p.y),
145
146
  }));
146
147
  }
147
148
  seriesStackData.push({
@@ -12,6 +12,7 @@ export type MarkerData = {
12
12
  point: PointData;
13
13
  active: boolean;
14
14
  hovered: boolean;
15
+ clipped: boolean;
15
16
  };
16
17
  export type PreparedAreaData = {
17
18
  id: string;
@@ -9,5 +9,6 @@ type Args = {
9
9
  preparedData: PreparedBarXData[];
10
10
  seriesOptions: PreparedSeriesOptions;
11
11
  htmlLayout: HTMLElement | null;
12
+ clipPathId: string;
12
13
  };
13
14
  export declare const BarXSeriesShapes: (args: Args) => React.JSX.Element;
@@ -8,7 +8,7 @@ export { prepareBarXData } from './prepare-data';
8
8
  export * from './types';
9
9
  const b = block('bar-x');
10
10
  export const BarXSeriesShapes = (args) => {
11
- const { dispatcher, preparedData, seriesOptions, htmlLayout } = args;
11
+ const { dispatcher, preparedData, seriesOptions, htmlLayout, clipPathId } = args;
12
12
  const hoveredDataRef = React.useRef(null);
13
13
  const ref = React.useRef(null);
14
14
  React.useEffect(() => {
@@ -110,6 +110,6 @@ export const BarXSeriesShapes = (args) => {
110
110
  };
111
111
  }, [dispatcher, preparedData, seriesOptions]);
112
112
  return (React.createElement(React.Fragment, null,
113
- React.createElement("g", { ref: ref, className: b() }),
113
+ React.createElement("g", { ref: ref, className: b(), clipPath: `url(#${clipPathId})` }),
114
114
  React.createElement(HtmlLayer, { preparedData: preparedData, htmlLayout: htmlLayout })));
115
115
  };
@@ -8,5 +8,6 @@ type Args = {
8
8
  preparedData: PreparedBarYData[];
9
9
  seriesOptions: PreparedSeriesOptions;
10
10
  htmlLayout: HTMLElement | null;
11
+ clipPathId: string;
11
12
  };
12
13
  export declare const BarYSeriesShapes: (args: Args) => React.JSX.Element;
@@ -7,7 +7,7 @@ import { getRectPath } from '../utils';
7
7
  export { prepareBarYData } from './prepare-data';
8
8
  const b = block('bar-y');
9
9
  export const BarYSeriesShapes = (args) => {
10
- const { dispatcher, preparedData, seriesOptions, htmlLayout } = args;
10
+ const { dispatcher, preparedData, seriesOptions, htmlLayout, clipPathId } = args;
11
11
  const hoveredDataRef = React.useRef(null);
12
12
  const ref = React.useRef(null);
13
13
  React.useEffect(() => {
@@ -99,6 +99,6 @@ export const BarYSeriesShapes = (args) => {
99
99
  };
100
100
  }, [dispatcher, preparedData, seriesOptions]);
101
101
  return (React.createElement(React.Fragment, null,
102
- React.createElement("g", { ref: ref, className: b() }),
102
+ React.createElement("g", { ref: ref, className: b(), clipPath: `url(#${clipPathId})` }),
103
103
  React.createElement(HtmlLayer, { preparedData: preparedData, htmlLayout: htmlLayout })));
104
104
  };
@@ -28,6 +28,8 @@ type Args = {
28
28
  yScale?: ChartScale[];
29
29
  split: PreparedSplit;
30
30
  htmlLayout: HTMLElement | null;
31
+ clipPathId: string;
32
+ isOutsideBounds: (x: number, y: number) => boolean;
31
33
  };
32
34
  export declare const useShapes: (args: Args) => {
33
35
  shapes: React.ReactElement<any, string | React.JSXElementConstructor<any>>[];
@@ -20,7 +20,7 @@ import { prepareTreemapData } from './treemap/prepare-data';
20
20
  import { WaterfallSeriesShapes, prepareWaterfallData } from './waterfall';
21
21
  import './styles.css';
22
22
  export const useShapes = (args) => {
23
- const { boundsWidth, boundsHeight, dispatcher, series, seriesOptions, xAxis, xScale, yAxis, yScale, split, htmlLayout, } = args;
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
26
  const setShapes = React.useCallback(async () => {
@@ -44,7 +44,7 @@ export const useShapes = (args) => {
44
44
  yScale,
45
45
  boundsHeight,
46
46
  });
47
- shapes.push(React.createElement(BarXSeriesShapes, { key: "bar-x", dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData, htmlLayout: htmlLayout }));
47
+ shapes.push(React.createElement(BarXSeriesShapes, { key: "bar-x", dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData, htmlLayout: htmlLayout, clipPathId: clipPathId }));
48
48
  shapesData.push(...preparedData);
49
49
  }
50
50
  break;
@@ -59,7 +59,7 @@ export const useShapes = (args) => {
59
59
  yAxis,
60
60
  yScale,
61
61
  });
62
- shapes.push(React.createElement(BarYSeriesShapes, { key: "bar-y", dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData, htmlLayout: htmlLayout }));
62
+ shapes.push(React.createElement(BarYSeriesShapes, { key: "bar-y", dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData, htmlLayout: htmlLayout, clipPathId: clipPathId }));
63
63
  shapesData.push(...preparedData);
64
64
  }
65
65
  break;
@@ -74,7 +74,7 @@ export const useShapes = (args) => {
74
74
  yAxis,
75
75
  yScale,
76
76
  });
77
- shapes.push(React.createElement(WaterfallSeriesShapes, { key: "waterfall", dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData, htmlLayout: htmlLayout }));
77
+ shapes.push(React.createElement(WaterfallSeriesShapes, { key: "waterfall", dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData, htmlLayout: htmlLayout, clipPathId: clipPathId }));
78
78
  shapesData.push(...preparedData);
79
79
  }
80
80
  break;
@@ -88,8 +88,9 @@ export const useShapes = (args) => {
88
88
  yAxis,
89
89
  yScale,
90
90
  split,
91
+ isOutsideBounds,
91
92
  });
92
- shapes.push(React.createElement(LineSeriesShapes, { key: "line", dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData, htmlLayout: htmlLayout }));
93
+ shapes.push(React.createElement(LineSeriesShapes, { key: "line", dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData, htmlLayout: htmlLayout, clipPathId: clipPathId }));
93
94
  shapesData.push(...preparedData);
94
95
  }
95
96
  break;
@@ -103,8 +104,9 @@ export const useShapes = (args) => {
103
104
  yAxis,
104
105
  yScale,
105
106
  boundsHeight,
107
+ isOutsideBounds,
106
108
  });
107
- shapes.push(React.createElement(AreaSeriesShapes, { key: "area", dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData, htmlLayout: htmlLayout }));
109
+ shapes.push(React.createElement(AreaSeriesShapes, { key: "area", dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData, htmlLayout: htmlLayout, clipPathId: clipPathId }));
108
110
  shapesData.push(...preparedData);
109
111
  }
110
112
  break;
@@ -117,6 +119,7 @@ export const useShapes = (args) => {
117
119
  xScale,
118
120
  yAxis,
119
121
  yScale,
122
+ isOutsideBounds,
120
123
  });
121
124
  shapes.push(React.createElement(ScatterSeriesShape, { key: "scatter", dispatcher: dispatcher, preparedData: preparedData, seriesOptions: seriesOptions, htmlLayout: htmlLayout }));
122
125
  shapesData.push(...preparedData);
@@ -186,6 +189,8 @@ export const useShapes = (args) => {
186
189
  xScale,
187
190
  yAxis,
188
191
  yScale,
192
+ clipPathId,
193
+ isOutsideBounds,
189
194
  ]);
190
195
  React.useEffect(() => {
191
196
  setShapes();
@@ -7,6 +7,7 @@ type Args = {
7
7
  preparedData: PreparedLineData[];
8
8
  seriesOptions: PreparedSeriesOptions;
9
9
  htmlLayout: HTMLElement | null;
10
+ clipPathId: string;
10
11
  };
11
12
  export declare const LineSeriesShapes: (args: Args) => React.JSX.Element;
12
13
  export {};
@@ -7,22 +7,25 @@ import { getMarkerHaloVisibility, getMarkerVisibility, renderMarker, selectMarke
7
7
  import { setActiveState } from '../utils';
8
8
  const b = block('line');
9
9
  export const LineSeriesShapes = (args) => {
10
- const { dispatcher, preparedData, seriesOptions, htmlLayout } = args;
10
+ const { dispatcher, preparedData, seriesOptions, htmlLayout, clipPathId } = args;
11
11
  const hoveredDataRef = React.useRef(null);
12
- const ref = React.useRef(null);
12
+ const plotRef = React.useRef(null);
13
+ const markersRef = React.useRef(null);
13
14
  React.useEffect(() => {
14
15
  var _a;
15
- if (!ref.current) {
16
+ if (!plotRef.current || !markersRef.current) {
16
17
  return () => { };
17
18
  }
18
- const svgElement = select(ref.current);
19
+ const plotSvgElement = select(plotRef.current);
20
+ const markersSvgElement = select(markersRef.current);
19
21
  const hoverOptions = get(seriesOptions, 'line.states.hover');
20
22
  const inactiveOptions = get(seriesOptions, 'line.states.inactive');
21
23
  const line = lineGenerator()
22
24
  .x((d) => d.x)
23
25
  .y((d) => d.y);
24
- svgElement.selectAll('*').remove();
25
- const lineSelection = svgElement
26
+ plotSvgElement.selectAll('*').remove();
27
+ markersSvgElement.selectAll('*').remove();
28
+ const lineSelection = plotSvgElement
26
29
  .selectAll('path')
27
30
  .data(preparedData)
28
31
  .join('path')
@@ -41,7 +44,7 @@ export const LineSeriesShapes = (args) => {
41
44
  if (!((_a = preparedData[0]) === null || _a === void 0 ? void 0 : _a.series.dataLabels.allowOverlap)) {
42
45
  dataLabels = filterOverlappingLabels(dataLabels);
43
46
  }
44
- const labelsSelection = svgElement
47
+ const labelsSelection = plotSvgElement
45
48
  .selectAll('text')
46
49
  .data(dataLabels)
47
50
  .join('text')
@@ -54,7 +57,7 @@ export const LineSeriesShapes = (args) => {
54
57
  .style('font-weight', (d) => d.style.fontWeight || null)
55
58
  .style('fill', (d) => d.style.fontColor || null);
56
59
  const markers = preparedData.reduce((acc, d) => acc.concat(d.markers), []);
57
- const markerSelection = svgElement
60
+ const markerSelection = markersSvgElement
58
61
  .selectAll('marker')
59
62
  .data(markers)
60
63
  .join('g')
@@ -71,10 +74,10 @@ export const LineSeriesShapes = (args) => {
71
74
  const hovered = Boolean(hoverEnabled && selectedSeriesIds.includes(d.id));
72
75
  if (d.hovered !== hovered) {
73
76
  d.hovered = hovered;
74
- elementSelection.attr('stroke', (d) => {
77
+ elementSelection.attr('stroke', (dSelection) => {
75
78
  var _a;
76
- const initialColor = d.color || '';
77
- if (d.hovered) {
79
+ const initialColor = dSelection.color || '';
80
+ if (dSelection.hovered) {
78
81
  return (((_a = color(initialColor)) === null || _a === void 0 ? void 0 : _a.brighter(hoverOptions === null || hoverOptions === void 0 ? void 0 : hoverOptions.brightness).toString()) || initialColor);
79
82
  }
80
83
  return initialColor;
@@ -131,6 +134,7 @@ export const LineSeriesShapes = (args) => {
131
134
  };
132
135
  }, [dispatcher, preparedData, seriesOptions]);
133
136
  return (React.createElement(React.Fragment, null,
134
- React.createElement("g", { ref: ref, className: b() }),
137
+ React.createElement("g", { ref: plotRef, className: b(), clipPath: `url(#${clipPathId})` }),
138
+ React.createElement("g", { ref: markersRef }),
135
139
  React.createElement(HtmlLayer, { preparedData: preparedData, htmlLayout: htmlLayout })));
136
140
  };
@@ -10,4 +10,5 @@ export declare const prepareLineData: (args: {
10
10
  yAxis: PreparedAxis[];
11
11
  yScale: ChartScale[];
12
12
  split: PreparedSplit;
13
+ isOutsideBounds: (x: number, y: number) => boolean;
13
14
  }) => Promise<PreparedLineData[]>;
@@ -40,7 +40,7 @@ async function getHtmlLabel(point, series, xMax) {
40
40
  }
41
41
  export const prepareLineData = async (args) => {
42
42
  var _a;
43
- const { series, xAxis, yAxis, xScale, yScale, split } = args;
43
+ const { series, xAxis, yAxis, xScale, yScale, split, isOutsideBounds } = args;
44
44
  const [_xMin, xRangeMax] = xScale.range();
45
45
  const xMax = xRangeMax / (1 - xAxis.maxPadding);
46
46
  const acc = [];
@@ -74,6 +74,7 @@ export const prepareLineData = async (args) => {
74
74
  point: p,
75
75
  active: true,
76
76
  hovered: false,
77
+ clipped: isOutsideBounds(p.x, p.y),
77
78
  }));
78
79
  }
79
80
  const result = {
@@ -12,6 +12,7 @@ export type MarkerData = {
12
12
  point: PointData;
13
13
  active: boolean;
14
14
  hovered: boolean;
15
+ clipped: boolean;
15
16
  };
16
17
  export type PreparedLineData = {
17
18
  id: string;
@@ -16,6 +16,9 @@ export function renderMarker(selection) {
16
16
  .append('path')
17
17
  .attr('class', haloClassName)
18
18
  .attr('d', (d) => {
19
+ if ('clipped' in d && d.clipped) {
20
+ return null;
21
+ }
19
22
  const series = d.point.series;
20
23
  const type = series.marker.states.normal.symbol;
21
24
  const radius = get(d.point.data, 'radius', series.marker.states.hover.radius);
@@ -53,6 +56,9 @@ export function getMarkerHaloVisibility(d) {
53
56
  export function setMarker(selection, state) {
54
57
  selection
55
58
  .attr('d', (d) => {
59
+ if ('clipped' in d && d.clipped) {
60
+ return null;
61
+ }
56
62
  const series = d.point.series;
57
63
  const type = series.marker.states.normal.symbol;
58
64
  const radius = get(d.point.data, 'radius', series.marker.states[state].radius);
@@ -8,4 +8,5 @@ export declare const prepareScatterData: (args: {
8
8
  xScale: ChartScale;
9
9
  yAxis: PreparedAxis[];
10
10
  yScale: ChartScale[];
11
+ isOutsideBounds: (x: number, y: number) => boolean;
11
12
  }) => PreparedScatterData[];
@@ -4,7 +4,7 @@ const getFilteredLinearScatterData = (data) => {
4
4
  return data.filter((d) => typeof d.x === 'number' && typeof d.y === 'number');
5
5
  };
6
6
  export const prepareScatterData = (args) => {
7
- const { series, xAxis, xScale, yAxis, yScale } = args;
7
+ const { series, xAxis, xScale, yAxis, yScale, isOutsideBounds } = args;
8
8
  return series.reduce((acc, s) => {
9
9
  const yAxisIndex = get(s, 'yAxis', 0);
10
10
  const seriesYAxis = yAxis[yAxisIndex];
@@ -14,18 +14,21 @@ export const prepareScatterData = (args) => {
14
14
  : getFilteredLinearScatterData(s.data);
15
15
  filteredData.forEach((d) => {
16
16
  var _a;
17
+ const x = getXValue({ point: d, xAxis, xScale });
18
+ const y = getYValue({ point: d, yAxis: seriesYAxis, yScale: seriesYScale });
17
19
  acc.push({
18
20
  point: {
19
21
  data: d,
20
22
  series: s,
21
- x: getXValue({ point: d, xAxis, xScale }),
22
- y: getYValue({ point: d, yAxis: seriesYAxis, yScale: seriesYScale }),
23
+ x,
24
+ y,
23
25
  opacity: get(d, 'opacity', null),
24
26
  color: (_a = d.color) !== null && _a !== void 0 ? _a : s.color,
25
27
  },
26
28
  hovered: false,
27
29
  active: true,
28
30
  htmlElements: [],
31
+ clipped: isOutsideBounds(x, y),
29
32
  });
30
33
  });
31
34
  return acc;