@gravity-ui/charts 1.9.0 → 1.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (199) hide show
  1. package/dist/cjs/components/Axis/AxisX.d.ts +2 -1
  2. package/dist/cjs/components/Axis/AxisX.js +149 -143
  3. package/dist/cjs/components/Axis/AxisY.d.ts +2 -1
  4. package/dist/cjs/components/Axis/AxisY.js +113 -91
  5. package/dist/cjs/components/ChartInner/index.js +23 -10
  6. package/dist/cjs/components/ChartInner/useChartInnerHandlers.d.ts +1 -1
  7. package/dist/cjs/components/ChartInner/useChartInnerProps.d.ts +8 -5
  8. package/dist/cjs/components/ChartInner/useChartInnerProps.js +55 -9
  9. package/dist/cjs/components/ChartInner/utils.d.ts +3 -0
  10. package/dist/cjs/components/ChartInner/utils.js +28 -0
  11. package/dist/cjs/components/Legend/index.js +203 -195
  12. package/dist/cjs/components/Tooltip/ChartTooltipContent.d.ts +1 -1
  13. package/dist/cjs/components/Tooltip/DefaultContent.d.ts +1 -1
  14. package/dist/cjs/components/Tooltip/DefaultContent.js +1 -1
  15. package/dist/cjs/components/Tooltip/index.d.ts +1 -1
  16. package/dist/cjs/hooks/hooks-utils/index.d.ts +1 -0
  17. package/dist/cjs/hooks/hooks-utils/index.js +1 -0
  18. package/dist/cjs/hooks/hooks-utils/zoom.d.ts +8 -0
  19. package/dist/cjs/hooks/hooks-utils/zoom.js +81 -0
  20. package/dist/cjs/hooks/useAxisScales/index.d.ts +4 -2
  21. package/dist/cjs/hooks/useAxisScales/index.js +22 -8
  22. package/dist/cjs/hooks/useBrush/index.d.ts +3 -0
  23. package/dist/cjs/hooks/useBrush/index.js +70 -0
  24. package/dist/cjs/hooks/useBrush/styles.css +10 -0
  25. package/dist/cjs/hooks/useBrush/types.d.ts +24 -0
  26. package/dist/cjs/hooks/useBrush/types.js +1 -0
  27. package/dist/cjs/hooks/useChartDimensions/index.d.ts +3 -3
  28. package/dist/cjs/hooks/useChartDimensions/index.js +2 -2
  29. package/dist/cjs/hooks/useChartDimensions/utils.d.ts +2 -2
  30. package/dist/cjs/hooks/useChartOptions/chart.d.ts +2 -1
  31. package/dist/cjs/hooks/useChartOptions/chart.js +80 -1
  32. package/dist/cjs/hooks/useChartOptions/index.js +3 -2
  33. package/dist/cjs/hooks/useChartOptions/types.d.ts +3 -1
  34. package/dist/cjs/hooks/useChartOptions/x-axis.d.ts +3 -3
  35. package/dist/cjs/hooks/useChartOptions/x-axis.js +11 -11
  36. package/dist/cjs/hooks/useChartOptions/y-axis.d.ts +3 -3
  37. package/dist/cjs/hooks/useChartOptions/y-axis.js +22 -18
  38. package/dist/cjs/hooks/useCrosshair/index.d.ts +1 -1
  39. package/dist/cjs/hooks/useCrosshair/index.js +2 -2
  40. package/dist/cjs/hooks/useSeries/index.d.ts +8 -6
  41. package/dist/cjs/hooks/useSeries/index.js +41 -22
  42. package/dist/cjs/hooks/useSeries/prepare-bar-y.d.ts +27 -2
  43. package/dist/cjs/hooks/useSeries/prepare-bar-y.js +5 -5
  44. package/dist/cjs/hooks/useSeries/prepare-legend.d.ts +1 -1
  45. package/dist/cjs/hooks/useSeries/prepare-legend.js +6 -5
  46. package/dist/cjs/hooks/useSeries/prepareSeries.d.ts +1 -1
  47. package/dist/cjs/hooks/useSeries/prepareSeries.js +2 -2
  48. package/dist/cjs/hooks/useShapes/area/index.js +1 -1
  49. package/dist/cjs/hooks/useShapes/area/prepare-data.d.ts +1 -1
  50. package/dist/cjs/hooks/useShapes/area/prepare-data.js +32 -16
  51. package/dist/cjs/hooks/useShapes/area/types.d.ts +1 -0
  52. package/dist/cjs/hooks/useShapes/bar-x/prepare-data.d.ts +1 -1
  53. package/dist/cjs/hooks/useShapes/bar-x/prepare-data.js +17 -13
  54. package/dist/cjs/hooks/useShapes/bar-y/prepare-data.d.ts +1 -1
  55. package/dist/cjs/hooks/useShapes/bar-y/prepare-data.js +6 -6
  56. package/dist/cjs/hooks/useShapes/index.d.ts +1 -1
  57. package/dist/cjs/hooks/useShapes/index.js +40 -31
  58. package/dist/cjs/hooks/useShapes/line/prepare-data.d.ts +1 -1
  59. package/dist/cjs/hooks/useShapes/line/prepare-data.js +14 -11
  60. package/dist/cjs/hooks/useShapes/line/types.d.ts +1 -0
  61. package/dist/cjs/hooks/useShapes/marker.js +2 -2
  62. package/dist/cjs/hooks/useShapes/pie/index.js +3 -3
  63. package/dist/cjs/hooks/useShapes/pie/prepare-data.d.ts +1 -1
  64. package/dist/cjs/hooks/useShapes/pie/prepare-data.js +15 -11
  65. package/dist/cjs/hooks/useShapes/radar/prepare-data.d.ts +1 -1
  66. package/dist/cjs/hooks/useShapes/radar/prepare-data.js +6 -7
  67. package/dist/cjs/hooks/useShapes/radar/types.d.ts +1 -0
  68. package/dist/cjs/hooks/useShapes/scatter/index.js +0 -1
  69. package/dist/cjs/hooks/useShapes/scatter/prepare-data.js +2 -0
  70. package/dist/cjs/hooks/useShapes/scatter/types.d.ts +1 -0
  71. package/dist/cjs/hooks/useShapes/treemap/prepare-data.d.ts +1 -1
  72. package/dist/cjs/hooks/useShapes/treemap/prepare-data.js +19 -16
  73. package/dist/cjs/hooks/useShapes/waterfall/prepare-data.d.ts +1 -1
  74. package/dist/cjs/hooks/useShapes/waterfall/prepare-data.js +8 -7
  75. package/dist/cjs/hooks/useZoom/index.d.ts +18 -0
  76. package/dist/cjs/hooks/useZoom/index.js +54 -0
  77. package/dist/cjs/hooks/useZoom/types.d.ts +19 -0
  78. package/dist/cjs/hooks/useZoom/types.js +1 -0
  79. package/dist/cjs/hooks/useZoom/utils.d.ts +12 -0
  80. package/dist/cjs/hooks/useZoom/utils.js +128 -0
  81. package/dist/cjs/types/chart/chart.d.ts +5 -0
  82. package/dist/cjs/types/chart/pie.d.ts +1 -1
  83. package/dist/cjs/types/chart/tooltip.d.ts +1 -1
  84. package/dist/cjs/types/chart/zoom.d.ts +36 -0
  85. package/dist/cjs/types/chart/zoom.js +1 -0
  86. package/dist/cjs/types/index.d.ts +1 -0
  87. package/dist/cjs/types/index.js +1 -0
  88. package/dist/cjs/types/misc.d.ts +7 -0
  89. package/dist/cjs/utils/chart/axis-generators/bottom.d.ts +1 -1
  90. package/dist/cjs/utils/chart/axis-generators/bottom.js +29 -28
  91. package/dist/cjs/utils/chart/axis.d.ts +1 -1
  92. package/dist/cjs/utils/chart/axis.js +2 -2
  93. package/dist/cjs/utils/chart/get-closest-data.js +1 -1
  94. package/dist/cjs/utils/chart/text.d.ts +7 -7
  95. package/dist/cjs/utils/chart/text.js +45 -30
  96. package/dist/cjs/utils/chart-ui/pie-center-text.d.ts +1 -1
  97. package/dist/cjs/utils/chart-ui/pie-center-text.js +2 -2
  98. package/dist/cjs/validation/index.d.ts +1 -1
  99. package/dist/cjs/validation/index.js +16 -16
  100. package/dist/esm/components/Axis/AxisX.d.ts +2 -1
  101. package/dist/esm/components/Axis/AxisX.js +149 -143
  102. package/dist/esm/components/Axis/AxisY.d.ts +2 -1
  103. package/dist/esm/components/Axis/AxisY.js +113 -91
  104. package/dist/esm/components/ChartInner/index.js +23 -10
  105. package/dist/esm/components/ChartInner/useChartInnerHandlers.d.ts +1 -1
  106. package/dist/esm/components/ChartInner/useChartInnerProps.d.ts +8 -5
  107. package/dist/esm/components/ChartInner/useChartInnerProps.js +55 -9
  108. package/dist/esm/components/ChartInner/utils.d.ts +3 -0
  109. package/dist/esm/components/ChartInner/utils.js +28 -0
  110. package/dist/esm/components/Legend/index.js +203 -195
  111. package/dist/esm/components/Tooltip/ChartTooltipContent.d.ts +1 -1
  112. package/dist/esm/components/Tooltip/DefaultContent.d.ts +1 -1
  113. package/dist/esm/components/Tooltip/DefaultContent.js +1 -1
  114. package/dist/esm/components/Tooltip/index.d.ts +1 -1
  115. package/dist/esm/hooks/hooks-utils/index.d.ts +1 -0
  116. package/dist/esm/hooks/hooks-utils/index.js +1 -0
  117. package/dist/esm/hooks/hooks-utils/zoom.d.ts +8 -0
  118. package/dist/esm/hooks/hooks-utils/zoom.js +81 -0
  119. package/dist/esm/hooks/useAxisScales/index.d.ts +4 -2
  120. package/dist/esm/hooks/useAxisScales/index.js +22 -8
  121. package/dist/esm/hooks/useBrush/index.d.ts +3 -0
  122. package/dist/esm/hooks/useBrush/index.js +70 -0
  123. package/dist/esm/hooks/useBrush/styles.css +10 -0
  124. package/dist/esm/hooks/useBrush/types.d.ts +24 -0
  125. package/dist/esm/hooks/useBrush/types.js +1 -0
  126. package/dist/esm/hooks/useChartDimensions/index.d.ts +3 -3
  127. package/dist/esm/hooks/useChartDimensions/index.js +2 -2
  128. package/dist/esm/hooks/useChartDimensions/utils.d.ts +2 -2
  129. package/dist/esm/hooks/useChartOptions/chart.d.ts +2 -1
  130. package/dist/esm/hooks/useChartOptions/chart.js +80 -1
  131. package/dist/esm/hooks/useChartOptions/index.js +3 -2
  132. package/dist/esm/hooks/useChartOptions/types.d.ts +3 -1
  133. package/dist/esm/hooks/useChartOptions/x-axis.d.ts +3 -3
  134. package/dist/esm/hooks/useChartOptions/x-axis.js +11 -11
  135. package/dist/esm/hooks/useChartOptions/y-axis.d.ts +3 -3
  136. package/dist/esm/hooks/useChartOptions/y-axis.js +22 -18
  137. package/dist/esm/hooks/useCrosshair/index.d.ts +1 -1
  138. package/dist/esm/hooks/useCrosshair/index.js +2 -2
  139. package/dist/esm/hooks/useSeries/index.d.ts +8 -6
  140. package/dist/esm/hooks/useSeries/index.js +41 -22
  141. package/dist/esm/hooks/useSeries/prepare-bar-y.d.ts +27 -2
  142. package/dist/esm/hooks/useSeries/prepare-bar-y.js +5 -5
  143. package/dist/esm/hooks/useSeries/prepare-legend.d.ts +1 -1
  144. package/dist/esm/hooks/useSeries/prepare-legend.js +6 -5
  145. package/dist/esm/hooks/useSeries/prepareSeries.d.ts +1 -1
  146. package/dist/esm/hooks/useSeries/prepareSeries.js +2 -2
  147. package/dist/esm/hooks/useShapes/area/index.js +1 -1
  148. package/dist/esm/hooks/useShapes/area/prepare-data.d.ts +1 -1
  149. package/dist/esm/hooks/useShapes/area/prepare-data.js +32 -16
  150. package/dist/esm/hooks/useShapes/area/types.d.ts +1 -0
  151. package/dist/esm/hooks/useShapes/bar-x/prepare-data.d.ts +1 -1
  152. package/dist/esm/hooks/useShapes/bar-x/prepare-data.js +17 -13
  153. package/dist/esm/hooks/useShapes/bar-y/prepare-data.d.ts +1 -1
  154. package/dist/esm/hooks/useShapes/bar-y/prepare-data.js +6 -6
  155. package/dist/esm/hooks/useShapes/index.d.ts +1 -1
  156. package/dist/esm/hooks/useShapes/index.js +40 -31
  157. package/dist/esm/hooks/useShapes/line/prepare-data.d.ts +1 -1
  158. package/dist/esm/hooks/useShapes/line/prepare-data.js +14 -11
  159. package/dist/esm/hooks/useShapes/line/types.d.ts +1 -0
  160. package/dist/esm/hooks/useShapes/marker.js +2 -2
  161. package/dist/esm/hooks/useShapes/pie/index.js +3 -3
  162. package/dist/esm/hooks/useShapes/pie/prepare-data.d.ts +1 -1
  163. package/dist/esm/hooks/useShapes/pie/prepare-data.js +15 -11
  164. package/dist/esm/hooks/useShapes/radar/prepare-data.d.ts +1 -1
  165. package/dist/esm/hooks/useShapes/radar/prepare-data.js +6 -7
  166. package/dist/esm/hooks/useShapes/radar/types.d.ts +1 -0
  167. package/dist/esm/hooks/useShapes/scatter/index.js +0 -1
  168. package/dist/esm/hooks/useShapes/scatter/prepare-data.js +2 -0
  169. package/dist/esm/hooks/useShapes/scatter/types.d.ts +1 -0
  170. package/dist/esm/hooks/useShapes/treemap/prepare-data.d.ts +1 -1
  171. package/dist/esm/hooks/useShapes/treemap/prepare-data.js +19 -16
  172. package/dist/esm/hooks/useShapes/waterfall/prepare-data.d.ts +1 -1
  173. package/dist/esm/hooks/useShapes/waterfall/prepare-data.js +8 -7
  174. package/dist/esm/hooks/useZoom/index.d.ts +18 -0
  175. package/dist/esm/hooks/useZoom/index.js +54 -0
  176. package/dist/esm/hooks/useZoom/types.d.ts +19 -0
  177. package/dist/esm/hooks/useZoom/types.js +1 -0
  178. package/dist/esm/hooks/useZoom/utils.d.ts +12 -0
  179. package/dist/esm/hooks/useZoom/utils.js +128 -0
  180. package/dist/esm/types/chart/chart.d.ts +5 -0
  181. package/dist/esm/types/chart/pie.d.ts +1 -1
  182. package/dist/esm/types/chart/tooltip.d.ts +1 -1
  183. package/dist/esm/types/chart/zoom.d.ts +36 -0
  184. package/dist/esm/types/chart/zoom.js +1 -0
  185. package/dist/esm/types/index.d.ts +1 -0
  186. package/dist/esm/types/index.js +1 -0
  187. package/dist/esm/types/misc.d.ts +7 -0
  188. package/dist/esm/utils/chart/axis-generators/bottom.d.ts +1 -1
  189. package/dist/esm/utils/chart/axis-generators/bottom.js +29 -28
  190. package/dist/esm/utils/chart/axis.d.ts +1 -1
  191. package/dist/esm/utils/chart/axis.js +2 -2
  192. package/dist/esm/utils/chart/get-closest-data.js +1 -1
  193. package/dist/esm/utils/chart/text.d.ts +7 -7
  194. package/dist/esm/utils/chart/text.js +45 -30
  195. package/dist/esm/utils/chart-ui/pie-center-text.d.ts +1 -1
  196. package/dist/esm/utils/chart-ui/pie-center-text.js +2 -2
  197. package/dist/esm/validation/index.d.ts +1 -1
  198. package/dist/esm/validation/index.js +16 -16
  199. package/package.json +2 -1
@@ -6,7 +6,7 @@ type TooltipProps = {
6
6
  dispatcher: Dispatch<object>;
7
7
  tooltip: PreparedTooltip;
8
8
  svgContainer: SVGSVGElement | null;
9
- xAxis: PreparedAxis;
9
+ xAxis: PreparedAxis | null;
10
10
  yAxis: PreparedAxis;
11
11
  tooltipPinned: boolean;
12
12
  onOutsideClick?: () => void;
@@ -0,0 +1 @@
1
+ export * from './zoom';
@@ -0,0 +1 @@
1
+ export * from './zoom';
@@ -0,0 +1,8 @@
1
+ import type { ChartSeries, ChartXAxis, ChartYAxis } from '../../types';
2
+ import type { ZoomState } from '../useZoom/types';
3
+ export declare function getZoomedSeriesData(args: {
4
+ seriesData: ChartSeries[];
5
+ zoomState: Partial<ZoomState>;
6
+ xAxis?: ChartXAxis;
7
+ yAxises?: ChartYAxis[];
8
+ }): ChartSeries[];
@@ -0,0 +1,81 @@
1
+ // eslint-disable-next-line complexity
2
+ function isValueInRange(args) {
3
+ const { axis, max, min, value } = args;
4
+ if (value === undefined) {
5
+ return false;
6
+ }
7
+ const axisType = (axis === null || axis === void 0 ? void 0 : axis.type) || 'linear';
8
+ switch (axisType) {
9
+ case 'datetime':
10
+ case 'linear':
11
+ case 'logarithmic': {
12
+ const numValue = typeof value === 'string' ? Number(value) : value;
13
+ const numMin = typeof min === 'string' ? Number(min) : min;
14
+ const numMax = typeof max === 'string' ? Number(max) : max;
15
+ if (Number.isNaN(numValue) || Number.isNaN(numMin) || Number.isNaN(numMax)) {
16
+ return false;
17
+ }
18
+ return numValue >= numMin && numValue <= numMax;
19
+ }
20
+ case 'category': {
21
+ const categories = (axis === null || axis === void 0 ? void 0 : axis.categories) || [];
22
+ if (typeof value === 'string' && typeof min === 'number' && typeof max === 'number') {
23
+ const valueIndex = categories.indexOf(value);
24
+ if (min === -1 || max === -1 || valueIndex === -1) {
25
+ return false;
26
+ }
27
+ return valueIndex >= Math.min(min, max) && valueIndex <= Math.max(min, max);
28
+ }
29
+ if (typeof value === 'number' && typeof min === 'number' && typeof max === 'number') {
30
+ if (min === -1 || max === -1) {
31
+ return false;
32
+ }
33
+ return value >= Math.min(min, max) && value <= Math.max(min, max);
34
+ }
35
+ return false;
36
+ }
37
+ default: {
38
+ return false;
39
+ }
40
+ }
41
+ }
42
+ export function getZoomedSeriesData(args) {
43
+ const { seriesData, xAxis, yAxises, zoomState } = args;
44
+ if (Object.keys(zoomState).length < 0) {
45
+ return seriesData;
46
+ }
47
+ return seriesData.map((seriesItem) => {
48
+ const filteredData = seriesItem.data.reduce((acc, point) => {
49
+ let inXRange = true;
50
+ let inYRange = true;
51
+ if (zoomState.x) {
52
+ const [xMin, xMax] = zoomState.x;
53
+ const x = 'x' in point ? point.x : undefined;
54
+ inXRange = isValueInRange({
55
+ axis: xAxis,
56
+ value: x,
57
+ min: xMin,
58
+ max: xMax,
59
+ });
60
+ }
61
+ if (zoomState.y) {
62
+ const yAxisIndex = 'yAxis' in seriesItem && typeof seriesItem.yAxis === 'number'
63
+ ? seriesItem.yAxis
64
+ : 0;
65
+ const [yMin, yMax] = zoomState.y[yAxisIndex];
66
+ const y = 'y' in point ? point.y : undefined;
67
+ inYRange = isValueInRange({
68
+ axis: yAxises === null || yAxises === void 0 ? void 0 : yAxises[yAxisIndex],
69
+ value: y,
70
+ min: yMin,
71
+ max: yMax,
72
+ });
73
+ }
74
+ if (inXRange && inYRange) {
75
+ acc.push(point);
76
+ }
77
+ return acc;
78
+ }, []);
79
+ return Object.assign(Object.assign({}, seriesItem), { data: filteredData });
80
+ });
81
+ }
@@ -8,16 +8,18 @@ type Args = {
8
8
  boundsWidth: number;
9
9
  boundsHeight: number;
10
10
  series: PreparedSeries[];
11
- xAxis: PreparedAxis;
11
+ xAxis: PreparedAxis | null;
12
12
  yAxis: PreparedAxis[];
13
13
  split: PreparedSplit;
14
+ hasZoomX?: boolean;
15
+ hasZoomY?: boolean;
14
16
  };
15
17
  type ReturnValue = {
16
18
  xScale?: ChartScale;
17
19
  yScale?: ChartScale[];
18
20
  };
19
21
  export declare function createYScale(axis: PreparedAxis, series: PreparedSeries[], boundsHeight: number): ScaleBand<string> | ScaleLinear<number, number, never> | ScaleTime<number, number, never>;
20
- export declare function createXScale(axis: PreparedAxis | ChartAxis, series: (PreparedSeries | ChartSeries)[], boundsWidth: number): ScaleBand<string> | ScaleLinear<number, number, never> | ScaleTime<number, number, never>;
22
+ export declare function createXScale(axis: PreparedAxis | ChartAxis, series: (PreparedSeries | ChartSeries)[], boundsWidth: number, hasZoomX?: boolean): ScaleBand<string> | ScaleLinear<number, number, never> | ScaleTime<number, number, never>;
21
23
  /**
22
24
  * Uses to create scales for axis related series
23
25
  */
@@ -86,7 +86,7 @@ function calculateXAxisPadding(series) {
86
86
  });
87
87
  return result;
88
88
  }
89
- export function createXScale(axis, series, boundsWidth) {
89
+ export function createXScale(axis, series, boundsWidth, hasZoomX) {
90
90
  const xMin = get(axis, 'min');
91
91
  const xMax = getDefaultMaxXAxisValue(series);
92
92
  const xType = get(axis, 'type', DEFAULT_AXIS_TYPE);
@@ -104,7 +104,11 @@ export function createXScale(axis, series, boundsWidth) {
104
104
  const xMinValue = typeof xMin === 'number' ? xMin : domainXMin;
105
105
  const xMaxValue = typeof xMax === 'number' ? Math.max(xMax, domainXMax) : domainXMax;
106
106
  const scaleFn = xType === 'logarithmic' ? scaleLog : scaleLinear;
107
- return scaleFn().domain([xMinValue, xMaxValue]).range(xRange).nice();
107
+ const scale = scaleFn().domain([xMinValue, xMaxValue]).range(xRange);
108
+ if (!hasZoomX) {
109
+ scale.nice();
110
+ }
111
+ return scale;
108
112
  }
109
113
  break;
110
114
  }
@@ -126,13 +130,21 @@ export function createXScale(axis, series, boundsWidth) {
126
130
  case 'datetime': {
127
131
  if (xTimestamps) {
128
132
  const [xMin, xMax] = extent(xTimestamps);
129
- return scaleUtc().domain([xMin, xMax]).range(xRange).nice();
133
+ const scale = scaleUtc().domain([xMin, xMax]).range(xRange);
134
+ if (!hasZoomX) {
135
+ scale.nice();
136
+ }
137
+ return scale;
130
138
  }
131
139
  else {
132
140
  const domain = getDomainDataXBySeries(series);
133
141
  if (isNumericalArrayData(domain)) {
134
142
  const [xMin, xMax] = extent(domain);
135
- return scaleUtc().domain([xMin, xMax]).range(xRange).nice();
143
+ const scale = scaleUtc().domain([xMin, xMax]).range(xRange);
144
+ if (!hasZoomX) {
145
+ scale.nice();
146
+ }
147
+ return scale;
136
148
  }
137
149
  }
138
150
  break;
@@ -141,13 +153,13 @@ export function createXScale(axis, series, boundsWidth) {
141
153
  throw new Error('Failed to create xScale');
142
154
  }
143
155
  const createScales = (args) => {
144
- const { boundsWidth, boundsHeight, series, xAxis, yAxis, split } = args;
156
+ const { boundsWidth, boundsHeight, series, xAxis, yAxis, split, hasZoomX } = args;
145
157
  let visibleSeries = getOnlyVisibleSeries(series);
146
158
  // Reassign to all series in case of all series unselected,
147
159
  // otherwise we will get an empty space without grid
148
160
  visibleSeries = visibleSeries.length === 0 ? series : visibleSeries;
149
161
  return {
150
- xScale: createXScale(xAxis, visibleSeries, boundsWidth),
162
+ xScale: xAxis ? createXScale(xAxis, visibleSeries, boundsWidth, hasZoomX) : undefined,
151
163
  yScale: yAxis.map((axis, index) => {
152
164
  const axisSeries = series.filter((s) => {
153
165
  const seriesAxisIndex = get(s, 'yAxis', 0);
@@ -163,7 +175,7 @@ const createScales = (args) => {
163
175
  * Uses to create scales for axis related series
164
176
  */
165
177
  export const useAxisScales = (args) => {
166
- const { boundsWidth, boundsHeight, series, xAxis, yAxis, split } = args;
178
+ const { boundsWidth, boundsHeight, series, xAxis, yAxis, split, hasZoomX, hasZoomY } = args;
167
179
  return React.useMemo(() => {
168
180
  let xScale;
169
181
  let yScale;
@@ -176,8 +188,10 @@ export const useAxisScales = (args) => {
176
188
  xAxis,
177
189
  yAxis,
178
190
  split,
191
+ hasZoomX,
192
+ hasZoomY,
179
193
  }));
180
194
  }
181
195
  return { xScale, yScale };
182
- }, [boundsWidth, boundsHeight, series, xAxis, yAxis, split]);
196
+ }, [boundsWidth, boundsHeight, series, xAxis, yAxis, split, hasZoomX, hasZoomY]);
183
197
  };
@@ -0,0 +1,3 @@
1
+ import type { UseBrushProps } from './types';
2
+ import './styles.css';
3
+ export declare function useBrush(props: UseBrushProps): void;
@@ -0,0 +1,70 @@
1
+ import React from 'react';
2
+ import { brush, brushX, brushY, select } from 'd3';
3
+ import { block } from '../../utils';
4
+ import './styles.css';
5
+ const b = block('brush');
6
+ export function useBrush(props) {
7
+ const { areas, brushOptions, node, type, onBrushStart, onBrush, onBrushEnd } = props;
8
+ React.useEffect(() => {
9
+ if (!node || !areas.length) {
10
+ return () => { };
11
+ }
12
+ const brushes = [];
13
+ const brushGroupSelections = [];
14
+ const nodeSelection = select(node);
15
+ areas.forEach((area) => {
16
+ let brushFn;
17
+ switch (type) {
18
+ case 'x': {
19
+ brushFn = brushX;
20
+ break;
21
+ }
22
+ case 'y': {
23
+ brushFn = brushY;
24
+ break;
25
+ }
26
+ case 'xy':
27
+ default: {
28
+ brushFn = brush;
29
+ break;
30
+ }
31
+ }
32
+ const brushInstance = brushFn()
33
+ .extent(area.extent)
34
+ .on('start', function () {
35
+ onBrushStart === null || onBrushStart === void 0 ? void 0 : onBrushStart.call(this, brushInstance);
36
+ })
37
+ .on('brush', function (event) {
38
+ onBrush === null || onBrush === void 0 ? void 0 : onBrush.call(this, brushInstance, event.selection);
39
+ })
40
+ .on('end', function (event) {
41
+ onBrushEnd === null || onBrushEnd === void 0 ? void 0 : onBrushEnd.call(this, brushInstance, event.selection);
42
+ });
43
+ brushes.push(brushInstance);
44
+ const brushGroupSelection = nodeSelection
45
+ .append('g')
46
+ .attr('class', b())
47
+ .on('pointerdown', function () {
48
+ this.setAttribute('data-gc-brush-pressed', 'true');
49
+ })
50
+ .on('pointerup', function () {
51
+ this.removeAttribute('data-gc-brush-pressed');
52
+ });
53
+ brushGroupSelection.call(brushInstance);
54
+ if (brushOptions) {
55
+ brushGroupSelection
56
+ .selectAll('.selection')
57
+ .attr('fill-opacity', brushOptions.style.fillOpacity);
58
+ }
59
+ brushGroupSelections.push(brushGroupSelection);
60
+ });
61
+ return () => {
62
+ brushes.forEach((brushInstance) => {
63
+ brushInstance.on('start', null).on('brush', null).on('end', null);
64
+ });
65
+ brushGroupSelections.forEach((selection) => {
66
+ selection === null || selection === void 0 ? void 0 : selection.remove();
67
+ });
68
+ };
69
+ }, [areas, brushOptions, node, type, onBrushStart, onBrush, onBrushEnd]);
70
+ }
@@ -0,0 +1,10 @@
1
+ .gcharts-brush {
2
+ --gcharts-brush-color: rgba(51, 92, 173, 0.25);
3
+ }
4
+ .gcharts-brush:not([data-gc-brush-pressed=true]) .overlay {
5
+ cursor: default;
6
+ }
7
+ .gcharts-brush .selection {
8
+ fill: var(--gcharts-brush-color);
9
+ stroke: none;
10
+ }
@@ -0,0 +1,24 @@
1
+ import type { BrushBehavior } from 'd3';
2
+ import type { PreparedZoom } from '../useChartOptions/types';
3
+ type BrushType = PreparedZoom['type'];
4
+ type BrushSelection = [number, number] | [[number, number], [number, number]];
5
+ export interface BrushArea {
6
+ /**
7
+ * Array of points [[x0, y0], [x1, y1]], where [x0, y0] is the top-left corner
8
+ * and [x1, y1] is the bottom-right corner.
9
+ *
10
+ * The brush extent determines the size of the invisible overlay and also constrains the brush selection;
11
+ * the brush selection cannot go outside the brush extent.
12
+ */
13
+ extent: [[number, number], [number, number]];
14
+ }
15
+ export interface UseBrushProps {
16
+ areas: BrushArea[];
17
+ node: SVGGElement | null;
18
+ type?: BrushType;
19
+ brushOptions?: PreparedZoom['brush'];
20
+ onBrushStart?: (this: SVGGElement, brushInstance: BrushBehavior<unknown>) => void;
21
+ onBrush?: (this: SVGGElement, brushInstance: BrushBehavior<unknown>, selection: BrushSelection) => void;
22
+ onBrushEnd?: (this: SVGGElement, brushInstance: BrushBehavior<unknown>, selection: BrushSelection | null) => void;
23
+ }
24
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -5,9 +5,9 @@ type Args = {
5
5
  width: number;
6
6
  height: number;
7
7
  margin: ChartMargin;
8
- preparedLegend: PreparedLegend;
9
- preparedXAxis: PreparedAxis;
10
- preparedYAxis: PreparedAxis[];
8
+ preparedLegend: PreparedLegend | null;
9
+ preparedXAxis: PreparedAxis | null;
10
+ preparedYAxis: PreparedAxis[] | null;
11
11
  preparedSeries: PreparedSeries[];
12
12
  };
13
13
  export declare const useChartDimensions: (args: Args) => {
@@ -5,10 +5,10 @@ export { getBoundsWidth } from './utils';
5
5
  const getBottomOffset = (args) => {
6
6
  const { hasAxisRelatedSeries, preparedLegend, preparedXAxis } = args;
7
7
  let result = 0;
8
- if (preparedLegend.enabled) {
8
+ if (preparedLegend === null || preparedLegend === void 0 ? void 0 : preparedLegend.enabled) {
9
9
  result += preparedLegend.height + preparedLegend.margin;
10
10
  }
11
- if (!preparedXAxis.visible) {
11
+ if (!(preparedXAxis === null || preparedXAxis === void 0 ? void 0 : preparedXAxis.visible)) {
12
12
  return result;
13
13
  }
14
14
  if (hasAxisRelatedSeries) {
@@ -2,9 +2,9 @@ import type { PreparedAxis, PreparedChart } from '../../hooks';
2
2
  export declare const getBoundsWidth: (args: {
3
3
  chartWidth: number;
4
4
  chartMargin: PreparedChart["margin"];
5
- preparedYAxis: PreparedAxis[];
5
+ preparedYAxis: PreparedAxis[] | null;
6
6
  }) => number;
7
7
  export declare function getYAxisWidth(axis: PreparedAxis | undefined): number;
8
8
  export declare function getWidthOccupiedByYAxis(args: {
9
- preparedAxis: PreparedAxis[];
9
+ preparedAxis: PreparedAxis[] | null;
10
10
  }): number;
@@ -1,6 +1,7 @@
1
- import type { ChartData } from '../../types';
1
+ import type { ChartData, ChartSeries } from '../../types';
2
2
  import type { PreparedChart, PreparedTitle } from './types';
3
3
  export declare const getPreparedChart: (args: {
4
4
  chart: ChartData["chart"];
5
+ seriesData: ChartSeries[];
5
6
  preparedTitle?: PreparedTitle;
6
7
  }) => PreparedChart;
@@ -1,4 +1,6 @@
1
1
  import get from 'lodash/get';
2
+ import intersection from 'lodash/intersection';
3
+ import { SeriesType } from '../../constants';
2
4
  const getMarginTop = (args) => {
3
5
  const { chart, preparedTitle } = args;
4
6
  let marginTop = get(chart, 'margin.top', 0);
@@ -11,12 +13,88 @@ const getMarginRight = (args) => {
11
13
  const { chart } = args;
12
14
  return get(chart, 'margin.right', 0);
13
15
  };
16
+ function mapSeriesTypeToZoomType(seriesType) {
17
+ switch (seriesType) {
18
+ case SeriesType.Area: {
19
+ return ['x', 'y', 'xy'];
20
+ }
21
+ case SeriesType.BarX: {
22
+ return ['x'];
23
+ }
24
+ case SeriesType.BarY: {
25
+ return ['y'];
26
+ }
27
+ case SeriesType.Line: {
28
+ return ['x', 'y', 'xy'];
29
+ }
30
+ case SeriesType.Scatter: {
31
+ return ['x', 'y', 'xy'];
32
+ }
33
+ case SeriesType.Waterfall: {
34
+ return ['x', 'y', 'xy'];
35
+ }
36
+ default: {
37
+ return [];
38
+ }
39
+ }
40
+ }
41
+ function getDefaultZoomType(seriesType) {
42
+ switch (seriesType) {
43
+ case SeriesType.BarY: {
44
+ return 'y';
45
+ }
46
+ case SeriesType.Scatter: {
47
+ return 'xy';
48
+ }
49
+ default: {
50
+ return 'x';
51
+ }
52
+ }
53
+ }
54
+ function getZoomType(args) {
55
+ const { seriesData, zoomType } = args;
56
+ const possibleDefaultZoomTypes = seriesData.map((s) => {
57
+ return getDefaultZoomType(s.type);
58
+ });
59
+ const availableDefaultZoomTypes = intersection(possibleDefaultZoomTypes);
60
+ if (zoomType) {
61
+ const possibleZoomTypes = seriesData.map((s) => {
62
+ return mapSeriesTypeToZoomType(s.type);
63
+ });
64
+ const availableZoomTypes = intersection(...possibleZoomTypes);
65
+ if (availableZoomTypes.includes(zoomType)) {
66
+ return zoomType;
67
+ }
68
+ }
69
+ if (availableDefaultZoomTypes.length) {
70
+ return availableDefaultZoomTypes[0];
71
+ }
72
+ return undefined;
73
+ }
74
+ function getPreparedZoom(args) {
75
+ var _a;
76
+ const { zoom, seriesData } = args;
77
+ if (!(zoom === null || zoom === void 0 ? void 0 : zoom.enabled)) {
78
+ return null;
79
+ }
80
+ const type = getZoomType({ seriesData, zoomType: zoom.type });
81
+ if (!type) {
82
+ return null;
83
+ }
84
+ return {
85
+ type,
86
+ brush: {
87
+ style: Object.assign({ fillOpacity: 1 }, (_a = zoom === null || zoom === void 0 ? void 0 : zoom.brush) === null || _a === void 0 ? void 0 : _a.style),
88
+ },
89
+ };
90
+ }
14
91
  export const getPreparedChart = (args) => {
15
- const { chart, preparedTitle } = args;
92
+ const { chart, preparedTitle, seriesData } = args;
16
93
  const marginTop = getMarginTop({ chart, preparedTitle });
17
94
  const marginBottom = get(chart, 'margin.bottom', 0);
18
95
  const marginLeft = get(chart, 'margin.left', 0);
19
96
  const marginRight = getMarginRight({ chart });
97
+ const zoom = getPreparedZoom({ zoom: chart === null || chart === void 0 ? void 0 : chart.zoom, seriesData });
20
98
  return {
21
99
  margin: {
22
100
  top: marginTop,
@@ -24,5 +102,6 @@ export const getPreparedChart = (args) => {
24
102
  bottom: marginBottom,
25
103
  left: marginLeft,
26
104
  },
105
+ zoom,
27
106
  };
28
107
  };
@@ -4,13 +4,14 @@ import { getPreparedChart } from './chart';
4
4
  import { getPreparedTitle } from './title';
5
5
  import { getPreparedTooltip } from './tooltip';
6
6
  export const useChartOptions = (args) => {
7
- const { data: { chart, title, tooltip, colors }, } = args;
7
+ const { data: { chart, title, tooltip, colors, series }, } = args;
8
8
  const options = React.useMemo(() => {
9
9
  const preparedTitle = getPreparedTitle({ title });
10
10
  const preparedTooltip = getPreparedTooltip({ tooltip });
11
11
  const preparedChart = getPreparedChart({
12
12
  chart,
13
13
  preparedTitle,
14
+ seriesData: series.data,
14
15
  });
15
16
  return {
16
17
  chart: preparedChart,
@@ -18,6 +19,6 @@ export const useChartOptions = (args) => {
18
19
  tooltip: preparedTooltip,
19
20
  colors: colors !== null && colors !== void 0 ? colors : DEFAULT_PALETTE,
20
21
  };
21
- }, [chart, colors, title, tooltip]);
22
+ }, [chart, colors, title, tooltip, series.data]);
22
23
  return options;
23
24
  };
@@ -1,5 +1,5 @@
1
1
  import type { DashStyle } from '../../constants';
2
- import type { AxisCrosshair, AxisPlotBand, BaseTextStyle, ChartAxis, ChartAxisLabels, ChartAxisTitleAlignment, ChartAxisType, ChartData, ChartMargin, PlotLayerPlacement } from '../../types';
2
+ import type { AxisCrosshair, AxisPlotBand, BaseTextStyle, ChartAxis, ChartAxisLabels, ChartAxisTitleAlignment, ChartAxisType, ChartData, ChartMargin, ChartZoom, DeepRequired, PlotLayerPlacement } from '../../types';
3
3
  type PreparedAxisLabels = Omit<ChartAxisLabels, 'enabled' | 'padding' | 'style' | 'autoRotation'> & Required<Pick<ChartAxisLabels, 'enabled' | 'padding' | 'margin' | 'rotation'>> & {
4
4
  style: BaseTextStyle;
5
5
  rotation: number;
@@ -8,8 +8,10 @@ type PreparedAxisLabels = Omit<ChartAxisLabels, 'enabled' | 'padding' | 'style'
8
8
  lineHeight: number;
9
9
  maxWidth: number;
10
10
  };
11
+ export type PreparedZoom = DeepRequired<Omit<ChartZoom, 'enabled'>>;
11
12
  export type PreparedChart = {
12
13
  margin: ChartMargin;
14
+ zoom: PreparedZoom | null;
13
15
  };
14
16
  export type PreparedAxisPlotBand = Required<AxisPlotBand>;
15
17
  export type PreparedAxisCrosshair = Required<AxisCrosshair>;
@@ -1,7 +1,7 @@
1
1
  import type { ChartSeries, ChartXAxis } from '../../types';
2
2
  import type { PreparedAxis } from './types';
3
- export declare const getPreparedXAxis: ({ xAxis, series, width, }: {
3
+ export declare const getPreparedXAxis: ({ xAxis, seriesData, width, }: {
4
4
  xAxis?: ChartXAxis;
5
- series: ChartSeries[];
5
+ seriesData: ChartSeries[];
6
6
  width: number;
7
- }) => PreparedAxis;
7
+ }) => Promise<PreparedAxis>;
@@ -2,8 +2,8 @@ import get from 'lodash/get';
2
2
  import { DASH_STYLE, DEFAULT_AXIS_LABEL_FONT_SIZE, axisCrosshairDefaults, axisLabelsDefaults, xAxisTitleDefaults, } from '../../constants';
3
3
  import { CHART_SERIES_WITH_VOLUME_ON_X_AXIS, calculateCos, formatAxisTickLabel, getClosestPointsRange, getHorisontalSvgTextHeight, getLabelsSize, getMaxTickCount, getTicksCount, getXAxisItems, hasOverlappingLabels, wrapText, } from '../../utils';
4
4
  import { createXScale } from '../useAxisScales';
5
- function getLabelSettings({ axis, series, width, autoRotation = true, }) {
6
- const scale = createXScale(axis, series, width);
5
+ async function getLabelSettings({ axis, seriesData, width, autoRotation = true, }) {
6
+ const scale = createXScale(axis, seriesData, width);
7
7
  const tickCount = getTicksCount({ axis, range: width });
8
8
  const ticks = getXAxisItems({
9
9
  scale: scale,
@@ -27,11 +27,11 @@ function getLabelSettings({ axis, series, width, autoRotation = true, }) {
27
27
  const defaultRotation = overlapping && autoRotation ? -45 : 0;
28
28
  const rotation = axis.labels.rotation || defaultRotation;
29
29
  const labelsHeight = rotation
30
- ? getLabelsSize({
30
+ ? (await getLabelsSize({
31
31
  labels,
32
32
  style: axis.labels.style,
33
33
  rotation,
34
- }).maxHeight
34
+ })).maxHeight
35
35
  : axis.labels.lineHeight;
36
36
  const maxHeight = rotation ? calculateCos(rotation) * axis.labels.maxWidth : labelsHeight;
37
37
  return { height: Math.min(maxHeight, labelsHeight), rotation };
@@ -48,17 +48,17 @@ function getAxisMin(axis, series) {
48
48
  }
49
49
  return min;
50
50
  }
51
- export const getPreparedXAxis = ({ xAxis, series, width, }) => {
51
+ export const getPreparedXAxis = async ({ xAxis, seriesData, width, }) => {
52
52
  var _a;
53
53
  const titleText = get(xAxis, 'title.text', '');
54
54
  const titleStyle = Object.assign(Object.assign({}, xAxisTitleDefaults.style), get(xAxis, 'title.style'));
55
55
  const titleMaxRowsCount = get(xAxis, 'title.maxRowCount', xAxisTitleDefaults.maxRowCount);
56
- const estimatedTitleRows = wrapText({
56
+ const estimatedTitleRows = (await wrapText({
57
57
  text: titleText,
58
58
  style: titleStyle,
59
59
  width,
60
- }).slice(0, titleMaxRowsCount);
61
- const titleSize = getLabelsSize({
60
+ })).slice(0, titleMaxRowsCount);
61
+ const titleSize = await getLabelsSize({
62
62
  labels: [titleText],
63
63
  style: titleStyle,
64
64
  });
@@ -92,7 +92,7 @@ export const getPreparedXAxis = ({ xAxis, series, width, }) => {
92
92
  align: get(xAxis, 'title.align', xAxisTitleDefaults.align),
93
93
  maxRowCount: get(xAxis, 'title.maxRowCount', xAxisTitleDefaults.maxRowCount),
94
94
  },
95
- min: getAxisMin(xAxis, series),
95
+ min: getAxisMin(xAxis, seriesData),
96
96
  maxPadding: get(xAxis, 'maxPadding', 0.01),
97
97
  grid: {
98
98
  enabled: get(xAxis, 'grid.enabled', true),
@@ -128,9 +128,9 @@ export const getPreparedXAxis = ({ xAxis, series, width, }) => {
128
128
  },
129
129
  visible: get(xAxis, 'visible', true),
130
130
  };
131
- const { height, rotation } = getLabelSettings({
131
+ const { height, rotation } = await getLabelSettings({
132
132
  axis: preparedXAxis,
133
- series,
133
+ seriesData,
134
134
  width,
135
135
  autoRotation: (_a = xAxis === null || xAxis === void 0 ? void 0 : xAxis.labels) === null || _a === void 0 ? void 0 : _a.autoRotation,
136
136
  });
@@ -1,7 +1,7 @@
1
1
  import type { ChartSeries, ChartYAxis } from '../../types';
2
2
  import type { PreparedAxis } from './types';
3
- export declare const getPreparedYAxis: ({ series, yAxis, height, }: {
4
- series: ChartSeries[];
3
+ export declare const getPreparedYAxis: ({ seriesData, yAxis, height, }: {
4
+ seriesData: ChartSeries[];
5
5
  yAxis: ChartYAxis[] | undefined;
6
6
  height: number;
7
- }) => PreparedAxis[];
7
+ }) => Promise<PreparedAxis[]>;