@gravity-ui/charts 1.32.1 → 1.33.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 (65) hide show
  1. package/dist/cjs/hooks/index.d.ts +1 -0
  2. package/dist/cjs/hooks/index.js +1 -0
  3. package/dist/cjs/hooks/useAxis/x-axis.js +2 -0
  4. package/dist/cjs/hooks/useAxis/y-axis.js +2 -0
  5. package/dist/cjs/hooks/useAxisScales/index.d.ts +6 -8
  6. package/dist/cjs/hooks/useAxisScales/index.js +145 -47
  7. package/dist/cjs/hooks/useAxisScales/types.d.ts +6 -0
  8. package/dist/cjs/hooks/useAxisScales/types.js +1 -0
  9. package/dist/cjs/hooks/useAxisScales/utils.d.ts +9 -1
  10. package/dist/cjs/hooks/useAxisScales/utils.js +74 -0
  11. package/dist/cjs/hooks/useNormalizedOriginalData/index.d.ts +2 -0
  12. package/dist/cjs/hooks/useRangeSlider/index.js +1 -0
  13. package/dist/cjs/hooks/useRangeSlider/types.d.ts +1 -1
  14. package/dist/cjs/hooks/useRangeSlider/utils.d.ts +1 -1
  15. package/dist/cjs/hooks/useShapes/area/prepare-data.d.ts +1 -1
  16. package/dist/cjs/hooks/useShapes/area/prepare-data.js +25 -11
  17. package/dist/cjs/hooks/useShapes/bar-x/prepare-data.d.ts +1 -1
  18. package/dist/cjs/hooks/useShapes/bar-y/prepare-data.d.ts +1 -1
  19. package/dist/cjs/hooks/useShapes/heatmap/prepare-data.d.ts +1 -1
  20. package/dist/cjs/hooks/useShapes/index.d.ts +1 -1
  21. package/dist/cjs/hooks/useShapes/line/prepare-data.d.ts +1 -1
  22. package/dist/cjs/hooks/useShapes/scatter/prepare-data.d.ts +1 -1
  23. package/dist/cjs/hooks/useShapes/utils.d.ts +1 -1
  24. package/dist/cjs/hooks/useShapes/waterfall/prepare-data.d.ts +1 -1
  25. package/dist/cjs/hooks/useZoom/index.d.ts +1 -1
  26. package/dist/cjs/hooks/useZoom/utils.d.ts +1 -1
  27. package/dist/cjs/hooks/utils/bar-x.d.ts +1 -1
  28. package/dist/cjs/hooks/utils/bar-y.d.ts +1 -1
  29. package/dist/cjs/i18n/keysets/en.json +2 -1
  30. package/dist/cjs/i18n/keysets/ru.json +2 -1
  31. package/dist/cjs/types/chart/axis.d.ts +25 -0
  32. package/dist/cjs/validation/validate-axes.js +35 -0
  33. package/dist/esm/hooks/index.d.ts +1 -0
  34. package/dist/esm/hooks/index.js +1 -0
  35. package/dist/esm/hooks/useAxis/x-axis.js +2 -0
  36. package/dist/esm/hooks/useAxis/y-axis.js +2 -0
  37. package/dist/esm/hooks/useAxisScales/index.d.ts +6 -8
  38. package/dist/esm/hooks/useAxisScales/index.js +145 -47
  39. package/dist/esm/hooks/useAxisScales/types.d.ts +6 -0
  40. package/dist/esm/hooks/useAxisScales/types.js +1 -0
  41. package/dist/esm/hooks/useAxisScales/utils.d.ts +9 -1
  42. package/dist/esm/hooks/useAxisScales/utils.js +74 -0
  43. package/dist/esm/hooks/useNormalizedOriginalData/index.d.ts +2 -0
  44. package/dist/esm/hooks/useRangeSlider/index.js +1 -0
  45. package/dist/esm/hooks/useRangeSlider/types.d.ts +1 -1
  46. package/dist/esm/hooks/useRangeSlider/utils.d.ts +1 -1
  47. package/dist/esm/hooks/useShapes/area/prepare-data.d.ts +1 -1
  48. package/dist/esm/hooks/useShapes/area/prepare-data.js +25 -11
  49. package/dist/esm/hooks/useShapes/bar-x/prepare-data.d.ts +1 -1
  50. package/dist/esm/hooks/useShapes/bar-y/prepare-data.d.ts +1 -1
  51. package/dist/esm/hooks/useShapes/heatmap/prepare-data.d.ts +1 -1
  52. package/dist/esm/hooks/useShapes/index.d.ts +1 -1
  53. package/dist/esm/hooks/useShapes/line/prepare-data.d.ts +1 -1
  54. package/dist/esm/hooks/useShapes/scatter/prepare-data.d.ts +1 -1
  55. package/dist/esm/hooks/useShapes/utils.d.ts +1 -1
  56. package/dist/esm/hooks/useShapes/waterfall/prepare-data.d.ts +1 -1
  57. package/dist/esm/hooks/useZoom/index.d.ts +1 -1
  58. package/dist/esm/hooks/useZoom/utils.d.ts +1 -1
  59. package/dist/esm/hooks/utils/bar-x.d.ts +1 -1
  60. package/dist/esm/hooks/utils/bar-y.d.ts +1 -1
  61. package/dist/esm/i18n/keysets/en.json +2 -1
  62. package/dist/esm/i18n/keysets/ru.json +2 -1
  63. package/dist/esm/types/chart/axis.d.ts +25 -0
  64. package/dist/esm/validation/validate-axes.js +35 -0
  65. package/package.json +1 -1
@@ -1,6 +1,7 @@
1
1
  export * from './useAxis';
2
2
  export * from './useAxis/types';
3
3
  export * from './useAxisScales';
4
+ export * from './useAxisScales/types';
4
5
  export * from './useBrush';
5
6
  export * from './useBrush/types';
6
7
  export * from './useChartDimensions';
@@ -1,6 +1,7 @@
1
1
  export * from './useAxis';
2
2
  export * from './useAxis/types';
3
3
  export * from './useAxisScales';
4
+ export * from './useAxisScales/types';
4
5
  export * from './useBrush';
5
6
  export * from './useBrush/types';
6
7
  export * from './useChartDimensions';
@@ -127,6 +127,8 @@ export const getPreparedXAxis = async ({ xAxis, seriesData, width, boundsWidth,
127
127
  },
128
128
  min: get(xAxis, 'min'),
129
129
  max: get(xAxis, 'max'),
130
+ startOnTick: get(xAxis, 'startOnTick'),
131
+ endOnTick: get(xAxis, 'endOnTick'),
130
132
  maxPadding,
131
133
  grid: {
132
134
  enabled: shouldHideGrid ? false : get(xAxis, 'grid.enabled', true),
@@ -139,6 +139,8 @@ export const getPreparedYAxis = ({ height, boundsHeight, width, seriesData, yAxi
139
139
  },
140
140
  min: (_l = get(axisItem, 'min')) !== null && _l !== void 0 ? _l : getDefaultMinYAxisValue(axisSeriesData),
141
141
  max: get(axisItem, 'max'),
142
+ startOnTick: get(axisItem, 'startOnTick'),
143
+ endOnTick: get(axisItem, 'endOnTick'),
142
144
  maxPadding: get(axisItem, 'maxPadding', getMaxPaddingBySeries({ series: axisSeriesData })),
143
145
  grid: {
144
146
  enabled: shouldHideGrid
@@ -1,10 +1,6 @@
1
- import type { ScaleBand, ScaleLinear, ScaleTime } from 'd3';
2
1
  import type { PreparedAxis, PreparedSeries, PreparedSplit, RangeSliderState, ZoomState } from '../../hooks';
3
2
  import type { ChartAxis, ChartSeries } from '../../types';
4
- type ChartScaleBand = ScaleBand<string>;
5
- export type ChartScaleLinear = ScaleLinear<number, number>;
6
- export type ChartScaleTime = ScaleTime<number, number>;
7
- export type ChartScale = ChartScaleBand | ChartScaleLinear | ChartScaleTime;
3
+ import type { ChartScale } from './types';
8
4
  type Args = {
9
5
  boundsWidth: number;
10
6
  boundsHeight: number;
@@ -12,6 +8,7 @@ type Args = {
12
8
  xAxis: PreparedAxis | null;
13
9
  yAxis: PreparedAxis[];
14
10
  split: PreparedSplit;
11
+ isRangeSlider?: boolean;
15
12
  rangeSliderState?: RangeSliderState;
16
13
  zoomState?: Partial<ZoomState>;
17
14
  };
@@ -22,16 +19,17 @@ type ReturnValue = {
22
19
  export declare function createYScale(args: {
23
20
  axis: PreparedAxis;
24
21
  boundsHeight: number;
25
- series: (PreparedSeries | ChartSeries)[];
22
+ series: PreparedSeries[] | ChartSeries[];
23
+ primaryTickPositions?: number[];
26
24
  zoomStateY?: [number, number];
27
- }): ScaleBand<string> | ScaleLinear<number, number, never> | ScaleTime<number, number, never> | undefined;
25
+ }): import("d3-scale").ScaleBand<string> | import("d3-scale").ScaleLinear<number, number, never> | import("d3-scale").ScaleTime<number, number, never> | undefined;
28
26
  export declare function createXScale(args: {
29
27
  axis: PreparedAxis | ChartAxis;
30
28
  boundsWidth: number;
31
29
  series: (PreparedSeries | ChartSeries)[];
32
30
  rangeSliderState?: RangeSliderState;
33
31
  zoomStateX?: [number, number];
34
- }): ScaleBand<string> | ScaleLinear<number, number, never> | ScaleTime<number, number, never> | undefined;
32
+ }): import("d3-scale").ScaleBand<string> | import("d3-scale").ScaleLinear<number, number, never> | import("d3-scale").ScaleTime<number, number, never> | undefined;
35
33
  /**
36
34
  * Uses to create scales for axis related series
37
35
  */
@@ -1,10 +1,11 @@
1
1
  import React from 'react';
2
2
  import { extent, scaleBand, scaleLinear, scaleLog, scaleUtc } from 'd3';
3
3
  import get from 'lodash/get';
4
+ import { getTickValues } from '../../components/AxisY/utils';
4
5
  import { DEFAULT_AXIS_TYPE, SERIES_TYPE } from '../../constants';
5
6
  import { CHART_SERIES_WITH_VOLUME_ON_Y_AXIS, getAxisCategories, getAxisHeight, getDataCategoryValue, getDefaultMaxXAxisValue, getDefaultMinXAxisValue, getDomainDataXBySeries, getDomainDataYBySeries, getOnlyVisibleSeries, isAxisRelatedSeries, isSeriesWithCategoryValues, } from '../../utils';
6
7
  import { getBandSize } from '../utils/get-band-size';
7
- import { checkIsPointDomain, getMinMaxPropsOrState, getXMaxDomainResult, hasOnlyMarkerSeries, } from './utils';
8
+ import { checkIsPointDomain, clusterYAxes, getDomainSyncedToPrimaryTicks, getMinMaxPropsOrState, getXMaxDomainResult, hasOnlyMarkerSeries, } from './utils';
8
9
  const X_AXIS_ZOOM_PADDING = 0.02;
9
10
  function validateArrayData(data) {
10
11
  let hasNumberAndNullValues;
@@ -65,7 +66,7 @@ function isSeriesWithYAxisOffset(series) {
65
66
  }
66
67
  // eslint-disable-next-line complexity
67
68
  export function createYScale(args) {
68
- const { axis, boundsHeight, series, zoomStateY } = args;
69
+ const { axis, boundsHeight, series, primaryTickPositions, zoomStateY } = args;
69
70
  const [yMinPropsOrState, yMaxPropsOrState] = getMinMaxPropsOrState({
70
71
  axis,
71
72
  maxValues: [zoomStateY === null || zoomStateY === void 0 ? void 0 : zoomStateY[1]],
@@ -102,29 +103,57 @@ export function createYScale(args) {
102
103
  yMax = hasSeriesWithVolumeOnYAxis ? Math.max(yMaxDomain, 0) : yMaxDomain;
103
104
  }
104
105
  const scaleFn = axis.type === 'logarithmic' ? scaleLog : scaleLinear;
105
- const scale = scaleFn().domain([yMin, yMax]).range(range);
106
- let offsetMin = 0;
107
- // We should ignore padding if we are drawing only one point on the plot.
108
- let offsetMax = yMin === yMax ? 0 : boundsHeight * axis.maxPadding;
109
- if (isSeriesWithYAxisOffset(series)) {
110
- if (domain.length > 1) {
111
- const bandWidth = getBandSize({
112
- scale: scale,
113
- domain: domain,
114
- });
115
- offsetMin += bandWidth / 2;
116
- offsetMax += bandWidth / 2;
106
+ let scale = scaleFn().domain([yMin, yMax]).range(range);
107
+ if (primaryTickPositions) {
108
+ const syncedDomain = getDomainSyncedToPrimaryTicks({
109
+ primaryTickPositions,
110
+ range,
111
+ scaleFn,
112
+ secondaryDomain: scale.domain(),
113
+ });
114
+ scale.domain(syncedDomain);
115
+ }
116
+ else {
117
+ let offsetMin = 0;
118
+ // We should ignore padding if we are drawing only one point on the plot.
119
+ let offsetMax = yMin === yMax ? 0 : boundsHeight * axis.maxPadding;
120
+ if (isSeriesWithYAxisOffset(series)) {
121
+ if (domain.length > 1) {
122
+ const bandWidth = getBandSize({
123
+ scale: scale,
124
+ domain: domain,
125
+ });
126
+ offsetMin += bandWidth / 2;
127
+ offsetMax += bandWidth / 2;
128
+ }
117
129
  }
130
+ const isMinSpecified = typeof get(axis, 'min') === 'number' && !zoomStateY;
131
+ const isMaxSpecified = typeof get(axis, 'max') === 'number' && !zoomStateY;
132
+ const domainOffsetMin = isMinSpecified
133
+ ? 0
134
+ : Math.abs(scale.invert(offsetMin) - scale.invert(0));
135
+ const domainOffsetMax = isMaxSpecified
136
+ ? 0
137
+ : Math.abs(scale.invert(offsetMax) - scale.invert(0));
138
+ scale = scale.domain([yMin - domainOffsetMin, yMax + domainOffsetMax]);
118
139
  }
119
- const isMinSpecified = typeof get(axis, 'min') === 'number' && !zoomStateY;
120
- const isMaxSpecified = typeof get(axis, 'max') === 'number' && !zoomStateY;
121
- const domainOffsetMin = isMinSpecified
122
- ? 0
123
- : Math.abs(scale.invert(offsetMin) - scale.invert(0));
124
- const domainOffsetMax = isMaxSpecified
125
- ? 0
126
- : Math.abs(scale.invert(offsetMax) - scale.invert(0));
127
- return scale.domain([yMin - domainOffsetMin, yMax + domainOffsetMax]);
140
+ // 10 is the default value for the number of ticks. Here, to preserve the appearance of a series with a small number of points
141
+ const nicedDomain = scale.copy().nice(Math.max(10, domain.length)).domain();
142
+ const startOnTick = get(axis, 'startOnTick', false);
143
+ const endOnTick = get(axis, 'endOnTick', false);
144
+ const hasOffset = isSeriesWithYAxisOffset(series);
145
+ if (!zoomStateY && !hasOffset && nicedDomain.length === 2) {
146
+ const domainWithOffset = scale.domain();
147
+ scale.domain([
148
+ startOnTick
149
+ ? Math.min(nicedDomain[0], domainWithOffset[0])
150
+ : domainWithOffset[0],
151
+ endOnTick
152
+ ? Math.max(nicedDomain[1], domainWithOffset[1])
153
+ : domainWithOffset[1],
154
+ ]);
155
+ }
156
+ return scale;
128
157
  }
129
158
  break;
130
159
  }
@@ -155,7 +184,17 @@ export function createYScale(args) {
155
184
  yMaxPropsOrState < yMaxTimestamp
156
185
  ? yMaxPropsOrState
157
186
  : yMaxTimestamp;
158
- return scaleUtc().domain([yMin, yMax]).range(range).nice();
187
+ const scale = scaleUtc().domain([yMin, yMax]).range(range);
188
+ const startOnTick = get(axis, 'startOnTick', true);
189
+ const endOnTick = get(axis, 'endOnTick', true);
190
+ if (startOnTick || endOnTick) {
191
+ const nicedDomain = scale.copy().nice().domain();
192
+ return scale.domain([
193
+ startOnTick ? Number(nicedDomain[0]) : yMin,
194
+ endOnTick ? Number(nicedDomain[1]) : yMax,
195
+ ]);
196
+ }
197
+ return scale;
159
198
  }
160
199
  else {
161
200
  const domain = getDomainDataYBySeries(series);
@@ -330,11 +369,17 @@ export function createXScale(args) {
330
369
  // 10 is the default value for the number of ticks. Here, to preserve the appearance of a series with a small number of points
331
370
  const nicedDomain = scale.copy().nice(Math.max(10, domainData.length)).domain();
332
371
  scale.domain([xMin - domainOffsetMin, xMax + domainOffsetMax]);
372
+ const startOnTick = get(axis, 'startOnTick', true);
373
+ const endOnTick = get(axis, 'endOnTick', true);
333
374
  if (!hasZoomX && !hasOffset && nicedDomain.length === 2) {
334
375
  const domainWithOffset = scale.domain();
335
376
  scale.domain([
336
- Math.min(nicedDomain[0], domainWithOffset[0]),
337
- Math.max(nicedDomain[1], domainWithOffset[1]),
377
+ startOnTick
378
+ ? Math.min(nicedDomain[0], domainWithOffset[0])
379
+ : domainWithOffset[0],
380
+ endOnTick
381
+ ? Math.max(nicedDomain[1], domainWithOffset[1])
382
+ : domainWithOffset[1],
338
383
  ]);
339
384
  }
340
385
  return scale;
@@ -403,11 +448,17 @@ export function createXScale(args) {
403
448
  // 10 is the default value for the number of ticks. Here, to preserve the appearance of a series with a small number of points
404
449
  const nicedDomain = scale.copy().nice(Math.max(10, domainData.length)).domain();
405
450
  scale.domain([xMin - domainOffsetMin, xMax + domainOffsetMax]);
451
+ const startOnTick = get(axis, 'startOnTick', true);
452
+ const endOnTick = get(axis, 'endOnTick', true);
406
453
  if (!hasZoomX && !hasOffset && nicedDomain.length === 2) {
407
454
  const domainWithOffset = scale.domain();
408
455
  scale.domain([
409
- Math.min(Number(nicedDomain[0]), Number(domainWithOffset[0])),
410
- Math.max(Number(nicedDomain[1]), Number(domainWithOffset[1])),
456
+ startOnTick
457
+ ? Math.min(Number(nicedDomain[0]), Number(domainWithOffset[0]))
458
+ : Number(domainWithOffset[0]),
459
+ endOnTick
460
+ ? Math.max(Number(nicedDomain[1]), Number(domainWithOffset[1]))
461
+ : Number(domainWithOffset[1]),
411
462
  ]);
412
463
  }
413
464
  return scale;
@@ -418,11 +469,62 @@ export function createXScale(args) {
418
469
  throw new Error('Failed to create xScale');
419
470
  }
420
471
  const createScales = (args) => {
421
- const { boundsWidth, boundsHeight, rangeSliderState, series, split, xAxis, yAxis, zoomState } = args;
472
+ const { boundsWidth, boundsHeight, isRangeSlider, rangeSliderState, series, split, xAxis, yAxis, zoomState, } = args;
422
473
  let visibleSeries = getOnlyVisibleSeries(series);
423
474
  // Reassign to all series in case of all series unselected,
424
475
  // otherwise we will get an empty space without grid
425
476
  visibleSeries = visibleSeries.length === 0 ? series : visibleSeries;
477
+ const axisHeight = getAxisHeight({ boundsHeight, split });
478
+ let index = 0;
479
+ const yScale = clusterYAxes(yAxis).reduce((acc, cluster) => {
480
+ var _a, _b;
481
+ const [primaryAxis, secondaryAxis] = cluster;
482
+ const mainAxisSeries = series.filter((s) => {
483
+ const seriesAxisIndex = get(s, 'yAxis', 0);
484
+ return seriesAxisIndex === index;
485
+ });
486
+ const visiblePrimaryAxisSeries = getOnlyVisibleSeries(mainAxisSeries);
487
+ const primaryAxisScale = createYScale({
488
+ axis: primaryAxis,
489
+ boundsHeight: axisHeight,
490
+ series: visiblePrimaryAxisSeries.length ? visiblePrimaryAxisSeries : mainAxisSeries,
491
+ zoomStateY: (_a = zoomState === null || zoomState === void 0 ? void 0 : zoomState.y) === null || _a === void 0 ? void 0 : _a[index],
492
+ });
493
+ acc.push(primaryAxisScale);
494
+ index += 1;
495
+ let primaryTickPositions;
496
+ if (primaryAxisScale && secondaryAxis && !isRangeSlider) {
497
+ primaryTickPositions = getTickValues({
498
+ axis: primaryAxis,
499
+ scale: primaryAxisScale,
500
+ labelLineHeight: primaryAxis.labels.lineHeight,
501
+ series: visiblePrimaryAxisSeries.length
502
+ ? visiblePrimaryAxisSeries
503
+ : mainAxisSeries,
504
+ }).map((t) => t.y);
505
+ }
506
+ const secondAxisSeries = series.filter((s) => {
507
+ const seriesAxisIndex = get(s, 'yAxis', 0);
508
+ return seriesAxisIndex === index;
509
+ });
510
+ const visibleSecondAxisSeries = getOnlyVisibleSeries(secondAxisSeries);
511
+ const secondaryAxisScale = secondaryAxis
512
+ ? createYScale({
513
+ axis: secondaryAxis,
514
+ boundsHeight: axisHeight,
515
+ primaryTickPositions,
516
+ series: visibleSecondAxisSeries.length
517
+ ? visibleSecondAxisSeries
518
+ : secondAxisSeries,
519
+ zoomStateY: (_b = zoomState === null || zoomState === void 0 ? void 0 : zoomState.y) === null || _b === void 0 ? void 0 : _b[index],
520
+ })
521
+ : undefined;
522
+ if (secondaryAxisScale) {
523
+ acc.push(secondaryAxisScale);
524
+ index += 1;
525
+ }
526
+ return acc;
527
+ }, []);
426
528
  return {
427
529
  xScale: xAxis
428
530
  ? createXScale({
@@ -433,29 +535,14 @@ const createScales = (args) => {
433
535
  zoomStateX: zoomState === null || zoomState === void 0 ? void 0 : zoomState.x,
434
536
  })
435
537
  : undefined,
436
- yScale: yAxis.map((axis, index) => {
437
- var _a;
438
- const axisSeries = series.filter((s) => {
439
- const seriesAxisIndex = get(s, 'yAxis', 0);
440
- return seriesAxisIndex === index;
441
- });
442
- const visibleAxisSeries = getOnlyVisibleSeries(axisSeries);
443
- const axisHeight = getAxisHeight({ boundsHeight, split });
444
- const zoomStateY = (_a = zoomState === null || zoomState === void 0 ? void 0 : zoomState.y) === null || _a === void 0 ? void 0 : _a[index];
445
- return createYScale({
446
- axis,
447
- boundsHeight: axisHeight,
448
- series: visibleAxisSeries.length ? visibleAxisSeries : axisSeries,
449
- zoomStateY,
450
- });
451
- }),
538
+ yScale,
452
539
  };
453
540
  };
454
541
  /**
455
542
  * Uses to create scales for axis related series
456
543
  */
457
544
  export const useAxisScales = (args) => {
458
- const { boundsWidth, boundsHeight, rangeSliderState, series, split, xAxis, yAxis, zoomState } = args;
545
+ const { boundsWidth, boundsHeight, isRangeSlider, rangeSliderState, series, split, xAxis, yAxis, zoomState, } = args;
459
546
  return React.useMemo(() => {
460
547
  let xScale;
461
548
  let yScale;
@@ -464,6 +551,7 @@ export const useAxisScales = (args) => {
464
551
  ({ xScale, yScale } = createScales({
465
552
  boundsWidth,
466
553
  boundsHeight,
554
+ isRangeSlider,
467
555
  rangeSliderState,
468
556
  series,
469
557
  split,
@@ -473,5 +561,15 @@ export const useAxisScales = (args) => {
473
561
  }));
474
562
  }
475
563
  return { xScale, yScale };
476
- }, [boundsWidth, boundsHeight, rangeSliderState, series, split, xAxis, yAxis, zoomState]);
564
+ }, [
565
+ boundsWidth,
566
+ boundsHeight,
567
+ isRangeSlider,
568
+ rangeSliderState,
569
+ series,
570
+ split,
571
+ xAxis,
572
+ yAxis,
573
+ zoomState,
574
+ ]);
477
575
  };
@@ -0,0 +1,6 @@
1
+ import type { ScaleBand, ScaleLinear, ScaleTime } from 'd3';
2
+ type ChartScaleBand = ScaleBand<string>;
3
+ export type ChartScaleLinear = ScaleLinear<number, number>;
4
+ export type ChartScaleTime = ScaleTime<number, number>;
5
+ export type ChartScale = ChartScaleBand | ChartScaleLinear | ChartScaleTime;
6
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -1,4 +1,5 @@
1
- import type { PreparedAxis, PreparedSeries } from '../../hooks';
1
+ import type { scaleLinear } from 'd3';
2
+ import type { PreparedAxis, PreparedSeries, PreparedYAxis } from '../../hooks';
2
3
  import type { ChartAxis, ChartSeries } from '../../types';
3
4
  type OptionalNumber = number | undefined;
4
5
  export declare function getMinMaxPropsOrState(args: {
@@ -22,4 +23,11 @@ export declare function getXMaxDomainResult(args: {
22
23
  xMaxRangeSlider?: number;
23
24
  xMaxZoom?: number;
24
25
  }): number;
26
+ export declare function clusterYAxes(yAxes: PreparedYAxis[]): [PreparedYAxis, PreparedYAxis?][];
27
+ export declare function getDomainSyncedToPrimaryTicks(args: {
28
+ primaryTickPositions: number[];
29
+ range: [number, number];
30
+ scaleFn: typeof scaleLinear;
31
+ secondaryDomain: number[];
32
+ }): [number, number];
25
33
  export {};
@@ -1,3 +1,4 @@
1
+ import { ticks } from 'd3';
1
2
  import get from 'lodash/get';
2
3
  import { SERIES_TYPE } from '../../constants';
3
4
  const MARKER_SERIES_TYPES = [SERIES_TYPE.Area, SERIES_TYPE.Line, SERIES_TYPE.Scatter];
@@ -49,3 +50,76 @@ export function getXMaxDomainResult(args) {
49
50
  }
50
51
  return xMaxDomainResult;
51
52
  }
53
+ export function clusterYAxes(yAxes) {
54
+ if (yAxes.length <= 1) {
55
+ return yAxes.map((axis) => [axis]);
56
+ }
57
+ const clusters = {};
58
+ yAxes.forEach((axis) => {
59
+ var _a;
60
+ const plotIndex = (_a = axis.plotIndex) !== null && _a !== void 0 ? _a : 0;
61
+ if (!clusters[plotIndex]) {
62
+ clusters[plotIndex] = [];
63
+ }
64
+ clusters[plotIndex].push(axis);
65
+ });
66
+ return Object.values(clusters).map((cluster) => {
67
+ if (cluster.length <= 1) {
68
+ return [cluster[0]];
69
+ }
70
+ const leftAxis = cluster.find((a) => a.position === 'left');
71
+ const secondaryAxis = cluster.find((a) => a !== leftAxis);
72
+ if (leftAxis) {
73
+ return [leftAxis, secondaryAxis];
74
+ }
75
+ return [cluster[0], cluster[1]];
76
+ });
77
+ }
78
+ export function getDomainSyncedToPrimaryTicks(args) {
79
+ const { primaryTickPositions, range, scaleFn, secondaryDomain } = args;
80
+ const [dMin, dMax] = secondaryDomain;
81
+ const primaryPosBottom = primaryTickPositions[0];
82
+ const primaryPosTop = primaryTickPositions[primaryTickPositions.length - 1];
83
+ let secondaryTicks = ticks(dMin, dMax, primaryTickPositions.length);
84
+ let originalStep = 0;
85
+ if (typeof secondaryTicks[0] === 'number' && typeof secondaryTicks[1] === 'number') {
86
+ originalStep = secondaryTicks[1] - secondaryTicks[0];
87
+ }
88
+ let i = 1;
89
+ while (secondaryTicks.length > primaryTickPositions.length) {
90
+ secondaryTicks = ticks(dMin, dMax, primaryTickPositions.length - i);
91
+ i += 1;
92
+ }
93
+ let step = originalStep;
94
+ if (typeof secondaryTicks[0] === 'number' && typeof secondaryTicks[1] === 'number') {
95
+ step = secondaryTicks[1] - secondaryTicks[0];
96
+ }
97
+ let ticksCountDiff = primaryTickPositions.length - secondaryTicks.length;
98
+ let deltaMin = Math.abs(dMin - secondaryTicks[0]);
99
+ let deltaMax = Math.abs(dMax - secondaryTicks[secondaryTicks.length - 1]);
100
+ while (ticksCountDiff > 0) {
101
+ if (deltaMin > deltaMax) {
102
+ secondaryTicks.unshift(secondaryTicks[0] - step);
103
+ deltaMin -= step;
104
+ }
105
+ else {
106
+ secondaryTicks.push(secondaryTicks[secondaryTicks.length - 1] + step);
107
+ deltaMax -= step;
108
+ }
109
+ ticksCountDiff -= 1;
110
+ }
111
+ let tmpScale = scaleFn()
112
+ .domain([secondaryTicks[0], secondaryTicks[secondaryTicks.length - 1]])
113
+ .range([primaryPosBottom, primaryPosTop]);
114
+ let dNewMin = tmpScale.invert(range[0]);
115
+ let dNewMax = tmpScale.invert(range[1]);
116
+ if (dNewMin < dMin) {
117
+ secondaryTicks = secondaryTicks.map((st) => st + step);
118
+ tmpScale = scaleFn()
119
+ .domain([secondaryTicks[0], secondaryTicks[secondaryTicks.length - 1]])
120
+ .range([primaryPosBottom, primaryPosTop]);
121
+ dNewMin = tmpScale.invert(range[0]);
122
+ dNewMax = tmpScale.invert(range[1]);
123
+ }
124
+ return [dNewMin, dNewMax];
125
+ }
@@ -29,6 +29,8 @@ export declare function useNormalizedOriginalData(props: UseOriginalDataProps):
29
29
  plotBands?: import("../../types").AxisPlotBand[];
30
30
  visible?: boolean;
31
31
  order?: "sortAsc" | "sortDesc" | "reverse";
32
+ startOnTick?: boolean;
33
+ endOnTick?: boolean;
32
34
  };
33
35
  normalizedYAxis: import("../../types").ChartYAxis[] | undefined;
34
36
  };
@@ -40,6 +40,7 @@ export function useRangeSlider(props) {
40
40
  const { xScale, yScale } = useAxisScales({
41
41
  boundsHeight: preparedRangeSlider.height,
42
42
  boundsWidth,
43
+ isRangeSlider: true,
43
44
  series: preparedSeries,
44
45
  split: EMPTY_PREPARED_SPLIT,
45
46
  xAxis: preparedXAxis,
@@ -1,6 +1,6 @@
1
1
  import type { ChartXAxis, ChartYAxis } from '../../types';
2
2
  import type { PreparedRangeSlider, PreparedXAxis, PreparedYAxis } from '../useAxis/types';
3
- import type { ChartScale } from '../useAxisScales';
3
+ import type { ChartScale } from '../useAxisScales/types';
4
4
  import type { BrushSelection, UseBrushProps } from '../useBrush/types';
5
5
  import type { PreparedChart } from '../useChartOptions/types';
6
6
  import type { PreparedLegend, PreparedSeries, PreparedSeriesOptions } from '../useSeries/types';
@@ -1,5 +1,5 @@
1
1
  import type { PreparedRangeSlider } from '../useAxis/types';
2
- import type { ChartScale } from '../useAxisScales';
2
+ import type { ChartScale } from '../useAxisScales/types';
3
3
  import type { BrushSelection } from '../useBrush/types';
4
4
  import type { PreparedChart } from '../useChartOptions/types';
5
5
  import type { PreparedLegend } from '../useSeries/types';
@@ -1,5 +1,5 @@
1
1
  import type { PreparedXAxis, PreparedYAxis } from '../../useAxis/types';
2
- import type { ChartScale } from '../../useAxisScales';
2
+ import type { ChartScale } from '../../useAxisScales/types';
3
3
  import type { PreparedAreaSeries } from '../../useSeries/types';
4
4
  import type { PreparedSplit } from '../../useSplit/types';
5
5
  import type { PreparedAreaData } from './types';
@@ -91,9 +91,11 @@ export const prepareAreaData = async (args) => {
91
91
  for (let i = 0; i < list.length; i++) {
92
92
  const [_stackId, seriesStack] = list[i];
93
93
  const xValues = getXValues(seriesStack, xAxis, xScale);
94
- const accumulatedYValues = new Map();
94
+ const positiveStackValues = new Map();
95
+ const negativeStackValues = new Map();
95
96
  xValues.forEach(([key]) => {
96
- accumulatedYValues.set(key, 0);
97
+ positiveStackValues.set(key, 0);
98
+ negativeStackValues.set(key, 0);
97
99
  });
98
100
  const seriesStackData = [];
99
101
  for (let j = 0; j < seriesStack.length; j++) {
@@ -122,24 +124,36 @@ export const prepareAreaData = async (args) => {
122
124
  return m.set(key, d);
123
125
  }, new Map());
124
126
  const points = xValues.reduce((pointsAcc, [x, xValue]) => {
125
- var _a;
126
- const accumulatedYValue = accumulatedYValues.get(x) || 0;
127
+ var _a, _b;
127
128
  const d = (_a = seriesData.get(x)) !== null && _a !== void 0 ? _a : {
128
129
  x,
129
130
  y: 0,
130
131
  };
132
+ const yDataValue = (_b = d.y) !== null && _b !== void 0 ? _b : null;
131
133
  const yValue = getYValue({ point: d, yAxis: seriesYAxis, yScale: seriesYScale });
132
- const yPointValue = yValue === null ? null : yValue - accumulatedYValue;
133
- if (yPointValue !== null) {
134
- accumulatedYValues.set(x, yMin - yPointValue);
134
+ let y = null;
135
+ let y0 = yAxisTop + yMin;
136
+ if (typeof yDataValue === 'number' && yValue !== null) {
137
+ 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));
142
+ }
143
+ else {
144
+ const negativeStackHeight = negativeStackValues.get(x) || 0;
145
+ y = yAxisTop + yValue + negativeStackHeight;
146
+ y0 += negativeStackHeight;
147
+ negativeStackValues.set(x, negativeStackHeight + (yValue - yMin));
148
+ }
135
149
  }
136
- if (s.nullMode === 'connect' && yPointValue === null) {
150
+ if (s.nullMode === 'connect' && yDataValue === null) {
137
151
  return pointsAcc;
138
152
  }
139
153
  pointsAcc.push({
140
- y0: yAxisTop + yMin - accumulatedYValue,
154
+ y0,
141
155
  x: xValue,
142
- y: yPointValue === null ? null : yAxisTop + (yPointValue !== null && yPointValue !== void 0 ? yPointValue : 0),
156
+ y,
143
157
  data: d,
144
158
  series: s,
145
159
  });
@@ -183,7 +197,7 @@ export const prepareAreaData = async (args) => {
183
197
  }
184
198
  if (series.some((s) => s.stacking === 'percent')) {
185
199
  xValues.forEach(([x], index) => {
186
- const stackHeight = accumulatedYValues.get(x) || 0;
200
+ const stackHeight = positiveStackValues.get(x) || 0;
187
201
  let acc = 0;
188
202
  const ratio = plotHeight / stackHeight;
189
203
  seriesStackData.forEach((item) => {
@@ -1,5 +1,5 @@
1
1
  import type { PreparedXAxis, PreparedYAxis } from '../../useAxis/types';
2
- import type { ChartScale } from '../../useAxisScales';
2
+ import type { ChartScale } from '../../useAxisScales/types';
3
3
  import type { PreparedBarXSeries, PreparedSeriesOptions } from '../../useSeries/types';
4
4
  import type { PreparedSplit } from '../../useSplit/types';
5
5
  import type { PreparedBarXData } from './types';
@@ -1,5 +1,5 @@
1
1
  import type { PreparedXAxis, PreparedYAxis } from '../../useAxis/types';
2
- import type { ChartScale } from '../../useAxisScales';
2
+ import type { ChartScale } from '../../useAxisScales/types';
3
3
  import type { PreparedBarYSeries, PreparedSeriesOptions } from '../../useSeries/types';
4
4
  import type { BarYShapesArgs } from './types';
5
5
  export declare function prepareBarYData(args: {
@@ -1,5 +1,5 @@
1
1
  import type { PreparedXAxis, PreparedYAxis } from '../../../hooks/useAxis/types';
2
- import type { ChartScale } from '../../../hooks/useAxisScales';
2
+ import type { ChartScale } from '../../../hooks/useAxisScales/types';
3
3
  import type { PreparedHeatmapSeries } from '../../useSeries/types';
4
4
  import type { PreparedHeatmapData } from './types';
5
5
  type PrepareHeatmapDataArgs = {
@@ -2,7 +2,7 @@ import React from 'react';
2
2
  import type { Dispatch } from 'd3';
3
3
  import type { SeriesType } from '../../constants';
4
4
  import type { PreparedXAxis, PreparedYAxis } from '../useAxis/types';
5
- import type { ChartScale } from '../useAxisScales';
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
8
  import type { PreparedAreaData } from './area/types';
@@ -1,5 +1,5 @@
1
1
  import type { PreparedXAxis, PreparedYAxis } from '../../useAxis/types';
2
- import type { ChartScale } from '../../useAxisScales';
2
+ import type { ChartScale } from '../../useAxisScales/types';
3
3
  import type { PreparedLineSeries } from '../../useSeries/types';
4
4
  import type { PreparedSplit } from '../../useSplit/types';
5
5
  import type { PreparedLineData } from './types';
@@ -1,5 +1,5 @@
1
1
  import type { PreparedXAxis, PreparedYAxis } from '../../useAxis/types';
2
- import type { ChartScale } from '../../useAxisScales';
2
+ import type { ChartScale } from '../../useAxisScales/types';
3
3
  import type { PreparedScatterSeries } from '../../useSeries/types';
4
4
  import type { PreparedScatterData } from './types';
5
5
  export declare function prepareScatterData(args: {
@@ -1,7 +1,7 @@
1
1
  import type { BaseType } from 'd3';
2
2
  import type { BasicInactiveState } from '../../types';
3
3
  import type { PreparedXAxis, PreparedYAxis } from '../useAxis/types';
4
- import type { ChartScale } from '../useAxisScales';
4
+ import type { ChartScale } from '../useAxisScales/types';
5
5
  export declare function getXValue(args: {
6
6
  point: {
7
7
  x?: number | string | null;