@gravity-ui/charts 1.32.1 → 1.34.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 (99) hide show
  1. package/dist/cjs/components/ChartInner/useChartInnerProps.js +3 -2
  2. package/dist/cjs/components/Tooltip/DefaultTooltipContent/index.js +5 -1
  3. package/dist/cjs/hooks/index.d.ts +2 -0
  4. package/dist/cjs/hooks/index.js +2 -0
  5. package/dist/cjs/hooks/useAxis/index.d.ts +5 -3
  6. package/dist/cjs/hooks/useAxis/index.js +3 -3
  7. package/dist/cjs/hooks/useAxis/types.d.ts +6 -0
  8. package/dist/cjs/hooks/useAxis/x-axis.js +2 -0
  9. package/dist/cjs/hooks/useAxis/y-axis.d.ts +10 -0
  10. package/dist/cjs/hooks/useAxis/y-axis.js +32 -21
  11. package/dist/cjs/hooks/useAxisScales/index.d.ts +4 -20
  12. package/dist/cjs/hooks/useAxisScales/index.js +76 -436
  13. package/dist/cjs/hooks/useAxisScales/types.d.ts +6 -0
  14. package/dist/cjs/hooks/useAxisScales/types.js +1 -0
  15. package/dist/cjs/hooks/useAxisScales/utils.d.ts +12 -7
  16. package/dist/cjs/hooks/useAxisScales/utils.js +54 -14
  17. package/dist/cjs/hooks/useAxisScales/x-scale.d.ts +15 -0
  18. package/dist/cjs/hooks/useAxisScales/x-scale.js +247 -0
  19. package/dist/cjs/hooks/useAxisScales/y-scale.d.ts +10 -0
  20. package/dist/cjs/hooks/useAxisScales/y-scale.js +299 -0
  21. package/dist/cjs/hooks/useNormalizedOriginalData/index.d.ts +2 -0
  22. package/dist/cjs/hooks/useRangeSlider/index.js +1 -0
  23. package/dist/cjs/hooks/useRangeSlider/types.d.ts +1 -1
  24. package/dist/cjs/hooks/useRangeSlider/utils.d.ts +1 -1
  25. package/dist/cjs/hooks/useShapes/area/prepare-data.d.ts +1 -1
  26. package/dist/cjs/hooks/useShapes/area/prepare-data.js +25 -11
  27. package/dist/cjs/hooks/useShapes/bar-x/prepare-data.d.ts +1 -1
  28. package/dist/cjs/hooks/useShapes/bar-y/prepare-data.d.ts +1 -1
  29. package/dist/cjs/hooks/useShapes/heatmap/prepare-data.d.ts +1 -1
  30. package/dist/cjs/hooks/useShapes/index.d.ts +1 -1
  31. package/dist/cjs/hooks/useShapes/line/prepare-data.d.ts +1 -1
  32. package/dist/cjs/hooks/useShapes/scatter/prepare-data.d.ts +1 -1
  33. package/dist/cjs/hooks/useShapes/utils.d.ts +1 -1
  34. package/dist/cjs/hooks/useShapes/waterfall/prepare-data.d.ts +1 -1
  35. package/dist/cjs/hooks/useYAxisLabelWidth/index.d.ts +11 -0
  36. package/dist/cjs/hooks/useYAxisLabelWidth/index.js +48 -0
  37. package/dist/cjs/hooks/useZoom/index.d.ts +1 -1
  38. package/dist/cjs/hooks/useZoom/utils.d.ts +1 -1
  39. package/dist/cjs/hooks/utils/bar-x.d.ts +1 -1
  40. package/dist/cjs/hooks/utils/bar-x.js +1 -1
  41. package/dist/cjs/hooks/utils/bar-y.d.ts +1 -1
  42. package/dist/cjs/hooks/utils/bar-y.js +1 -1
  43. package/dist/cjs/i18n/keysets/en.json +2 -1
  44. package/dist/cjs/i18n/keysets/ru.json +2 -1
  45. package/dist/cjs/types/chart/axis.d.ts +25 -0
  46. package/dist/cjs/types/chart/tooltip.d.ts +3 -2
  47. package/dist/cjs/utils/chart/axis/common.d.ts +1 -0
  48. package/dist/cjs/utils/chart/axis/common.js +6 -0
  49. package/dist/cjs/validation/validate-axes.js +35 -0
  50. package/dist/esm/components/ChartInner/useChartInnerProps.js +3 -2
  51. package/dist/esm/components/Tooltip/DefaultTooltipContent/index.js +5 -1
  52. package/dist/esm/hooks/index.d.ts +2 -0
  53. package/dist/esm/hooks/index.js +2 -0
  54. package/dist/esm/hooks/useAxis/index.d.ts +5 -3
  55. package/dist/esm/hooks/useAxis/index.js +3 -3
  56. package/dist/esm/hooks/useAxis/types.d.ts +6 -0
  57. package/dist/esm/hooks/useAxis/x-axis.js +2 -0
  58. package/dist/esm/hooks/useAxis/y-axis.d.ts +10 -0
  59. package/dist/esm/hooks/useAxis/y-axis.js +32 -21
  60. package/dist/esm/hooks/useAxisScales/index.d.ts +4 -20
  61. package/dist/esm/hooks/useAxisScales/index.js +76 -436
  62. package/dist/esm/hooks/useAxisScales/types.d.ts +6 -0
  63. package/dist/esm/hooks/useAxisScales/types.js +1 -0
  64. package/dist/esm/hooks/useAxisScales/utils.d.ts +12 -7
  65. package/dist/esm/hooks/useAxisScales/utils.js +54 -14
  66. package/dist/esm/hooks/useAxisScales/x-scale.d.ts +15 -0
  67. package/dist/esm/hooks/useAxisScales/x-scale.js +247 -0
  68. package/dist/esm/hooks/useAxisScales/y-scale.d.ts +10 -0
  69. package/dist/esm/hooks/useAxisScales/y-scale.js +299 -0
  70. package/dist/esm/hooks/useNormalizedOriginalData/index.d.ts +2 -0
  71. package/dist/esm/hooks/useRangeSlider/index.js +1 -0
  72. package/dist/esm/hooks/useRangeSlider/types.d.ts +1 -1
  73. package/dist/esm/hooks/useRangeSlider/utils.d.ts +1 -1
  74. package/dist/esm/hooks/useShapes/area/prepare-data.d.ts +1 -1
  75. package/dist/esm/hooks/useShapes/area/prepare-data.js +25 -11
  76. package/dist/esm/hooks/useShapes/bar-x/prepare-data.d.ts +1 -1
  77. package/dist/esm/hooks/useShapes/bar-y/prepare-data.d.ts +1 -1
  78. package/dist/esm/hooks/useShapes/heatmap/prepare-data.d.ts +1 -1
  79. package/dist/esm/hooks/useShapes/index.d.ts +1 -1
  80. package/dist/esm/hooks/useShapes/line/prepare-data.d.ts +1 -1
  81. package/dist/esm/hooks/useShapes/scatter/prepare-data.d.ts +1 -1
  82. package/dist/esm/hooks/useShapes/utils.d.ts +1 -1
  83. package/dist/esm/hooks/useShapes/waterfall/prepare-data.d.ts +1 -1
  84. package/dist/esm/hooks/useYAxisLabelWidth/index.d.ts +11 -0
  85. package/dist/esm/hooks/useYAxisLabelWidth/index.js +48 -0
  86. package/dist/esm/hooks/useZoom/index.d.ts +1 -1
  87. package/dist/esm/hooks/useZoom/utils.d.ts +1 -1
  88. package/dist/esm/hooks/utils/bar-x.d.ts +1 -1
  89. package/dist/esm/hooks/utils/bar-x.js +1 -1
  90. package/dist/esm/hooks/utils/bar-y.d.ts +1 -1
  91. package/dist/esm/hooks/utils/bar-y.js +1 -1
  92. package/dist/esm/i18n/keysets/en.json +2 -1
  93. package/dist/esm/i18n/keysets/ru.json +2 -1
  94. package/dist/esm/types/chart/axis.d.ts +25 -0
  95. package/dist/esm/types/chart/tooltip.d.ts +3 -2
  96. package/dist/esm/utils/chart/axis/common.d.ts +1 -0
  97. package/dist/esm/utils/chart/axis/common.js +6 -0
  98. package/dist/esm/validation/validate-axes.js +35 -0
  99. package/package.json +1 -1
@@ -0,0 +1,15 @@
1
+ import type { PreparedAxis, PreparedSeries, RangeSliderState } from '../../hooks';
2
+ import type { ChartAxis, ChartSeries } from '../../types';
3
+ export declare function getXMaxDomainResult(args: {
4
+ xMaxDomain: number;
5
+ xMaxProps?: number;
6
+ xMaxRangeSlider?: number;
7
+ xMaxZoom?: number;
8
+ }): number;
9
+ export declare function createXScale(args: {
10
+ axis: PreparedAxis | ChartAxis;
11
+ boundsWidth: number;
12
+ series: (PreparedSeries | ChartSeries)[];
13
+ rangeSliderState?: RangeSliderState;
14
+ zoomStateX?: [number, number];
15
+ }): import("d3-scale").ScaleBand<string> | import("d3-scale").ScaleLinear<number, number, never> | import("d3-scale").ScaleTime<number, number, never> | undefined;
@@ -0,0 +1,247 @@
1
+ import { extent, scaleBand, scaleLinear, scaleLog, scaleUtc } from 'd3';
2
+ import get from 'lodash/get';
3
+ import { DEFAULT_AXIS_TYPE, SERIES_TYPE } from '../../constants';
4
+ import { getAxisCategories, getDefaultMaxXAxisValue, getDefaultMinXAxisValue, getDomainDataXBySeries, } from '../../utils';
5
+ import { getBandSize } from '../utils/get-band-size';
6
+ import { checkIsPointDomain, filterCategoriesByVisibleSeries, getMinMaxPropsOrState, hasOnlyMarkerSeries, validateArrayData, } from './utils';
7
+ const X_AXIS_ZOOM_PADDING = 0.02;
8
+ function calculateXAxisPadding(series) {
9
+ let result = 0;
10
+ series.forEach((s) => {
11
+ var _a, _b;
12
+ switch (s.type) {
13
+ case 'bar-y': {
14
+ // Since labels can be located to the right of the bar, need to add an additional space
15
+ const inside = get(s, 'dataLabels.inside');
16
+ if (!inside) {
17
+ const labelsMaxWidth = get(s, 'dataLabels.maxWidth', 0) + ((_b = (_a = s.dataLabels) === null || _a === void 0 ? void 0 : _a.padding) !== null && _b !== void 0 ? _b : 0);
18
+ result = Math.max(result, labelsMaxWidth);
19
+ }
20
+ break;
21
+ }
22
+ }
23
+ });
24
+ return result;
25
+ }
26
+ function isSeriesWithXAxisOffset(series) {
27
+ const types = [SERIES_TYPE.Heatmap, SERIES_TYPE.BarX];
28
+ return series.some((s) => types.includes(s.type));
29
+ }
30
+ export function getXMaxDomainResult(args) {
31
+ const { xMaxDomain, xMaxProps, xMaxRangeSlider, xMaxZoom } = args;
32
+ let xMaxDomainResult = xMaxDomain;
33
+ // When xMaxRangeSlider is provided, we use it directly without considering xMaxDomain.
34
+ // This is intentional: the range slider needs to display the chart's maxPadding area,
35
+ // which would be clipped if we constrained it to xMaxDomain.
36
+ if (typeof xMaxRangeSlider === 'number') {
37
+ xMaxDomainResult = xMaxRangeSlider;
38
+ }
39
+ else if (typeof xMaxZoom === 'number' && xMaxZoom < xMaxDomain) {
40
+ xMaxDomainResult = xMaxZoom;
41
+ }
42
+ else if (typeof xMaxProps === 'number' && xMaxProps < xMaxDomain) {
43
+ xMaxDomainResult = xMaxProps;
44
+ }
45
+ return xMaxDomainResult;
46
+ }
47
+ function getXScaleRange({ boundsWidth, hasZoomX }) {
48
+ const xAxisZoomPadding = boundsWidth * X_AXIS_ZOOM_PADDING;
49
+ const xRange = [0, boundsWidth];
50
+ const xRangeZoom = [0 + xAxisZoomPadding, boundsWidth - xAxisZoomPadding];
51
+ const range = hasZoomX ? xRangeZoom : xRange;
52
+ return range;
53
+ }
54
+ // eslint-disable-next-line complexity
55
+ export function createXScale(args) {
56
+ const { axis, boundsWidth, series, rangeSliderState, zoomStateX } = args;
57
+ const [xMinPropsOrState, xMaxPropsOrState] = getMinMaxPropsOrState({
58
+ axis,
59
+ maxValues: [zoomStateX === null || zoomStateX === void 0 ? void 0 : zoomStateX[1], rangeSliderState === null || rangeSliderState === void 0 ? void 0 : rangeSliderState.max],
60
+ minValues: [zoomStateX === null || zoomStateX === void 0 ? void 0 : zoomStateX[0], rangeSliderState === null || rangeSliderState === void 0 ? void 0 : rangeSliderState.min],
61
+ });
62
+ const xType = get(axis, 'type', DEFAULT_AXIS_TYPE);
63
+ const hasZoomX = Boolean(zoomStateX);
64
+ let xCategories = get(axis, 'categories');
65
+ if (rangeSliderState && xCategories) {
66
+ xCategories = getAxisCategories({
67
+ categories: xCategories,
68
+ min: rangeSliderState.min,
69
+ max: rangeSliderState.max,
70
+ order: axis.order,
71
+ });
72
+ }
73
+ const maxPadding = get(axis, 'maxPadding', 0);
74
+ const xAxisMaxPadding = boundsWidth * maxPadding + calculateXAxisPadding(series);
75
+ const range = getXScaleRange({
76
+ boundsWidth,
77
+ hasZoomX,
78
+ });
79
+ switch (axis.order) {
80
+ case 'sortDesc':
81
+ case 'reverse': {
82
+ range.reverse();
83
+ }
84
+ }
85
+ switch (xType) {
86
+ case 'linear':
87
+ case 'logarithmic': {
88
+ const domainData = getDomainDataXBySeries(series);
89
+ const { hasNumberAndNullValues, hasOnlyNullValues } = validateArrayData(domainData);
90
+ if (hasOnlyNullValues || domainData.length === 0) {
91
+ return undefined;
92
+ }
93
+ if (series.some((s) => s.type === 'bar-y' && s.stacking === 'percent')) {
94
+ return scaleLinear().domain([0, 100]).range(range);
95
+ }
96
+ if (hasNumberAndNullValues) {
97
+ const [xMinDomain, xMaxDomain] = extent(domainData);
98
+ const isPointDomain = hasOnlyMarkerSeries(series)
99
+ ? checkIsPointDomain([xMinDomain, xMaxDomain])
100
+ : false;
101
+ let xMin;
102
+ let xMax;
103
+ if (typeof xMinPropsOrState === 'number' && !isPointDomain) {
104
+ xMin = xMinPropsOrState;
105
+ }
106
+ else if (xType === 'logarithmic') {
107
+ xMin = xMinDomain;
108
+ }
109
+ else {
110
+ const xMinDefault = getDefaultMinXAxisValue(series);
111
+ xMin = xMinDefault !== null && xMinDefault !== void 0 ? xMinDefault : xMinDomain;
112
+ }
113
+ if (typeof xMaxPropsOrState === 'number' && !isPointDomain) {
114
+ xMax = xMaxPropsOrState;
115
+ }
116
+ else {
117
+ const xMaxDefault = getDefaultMaxXAxisValue(series);
118
+ xMax =
119
+ typeof xMaxDefault === 'number'
120
+ ? Math.max(xMaxDefault, xMaxDomain)
121
+ : xMaxDomain;
122
+ }
123
+ const scaleFn = xType === 'logarithmic' ? scaleLog : scaleLinear;
124
+ const scale = scaleFn().domain([xMin, xMax]).range(range);
125
+ let offsetMin = 0;
126
+ let offsetMax = xAxisMaxPadding;
127
+ const hasOffset = isSeriesWithXAxisOffset(series);
128
+ if (hasOffset) {
129
+ if (domainData.length > 1) {
130
+ const bandWidth = getBandSize({
131
+ scale: scale,
132
+ domain: domainData,
133
+ });
134
+ offsetMin += bandWidth / 2;
135
+ offsetMax += bandWidth / 2;
136
+ }
137
+ }
138
+ const isMinSpecified = typeof get(axis, 'min') === 'number' && !zoomStateX;
139
+ const isMaxSpecified = typeof get(axis, 'max') === 'number' && !zoomStateX;
140
+ const domainOffsetMin = isMinSpecified
141
+ ? 0
142
+ : Math.abs(scale.invert(offsetMin) - scale.invert(0));
143
+ const domainOffsetMax = isMaxSpecified
144
+ ? 0
145
+ : Math.abs(scale.invert(offsetMax) - scale.invert(0));
146
+ // 10 is the default value for the number of ticks. Here, to preserve the appearance of a series with a small number of points
147
+ const nicedDomain = scale.copy().nice(Math.max(10, domainData.length)).domain();
148
+ scale.domain([xMin - domainOffsetMin, xMax + domainOffsetMax]);
149
+ const startOnTick = get(axis, 'startOnTick', true);
150
+ const endOnTick = get(axis, 'endOnTick', true);
151
+ if (!hasZoomX && !hasOffset && nicedDomain.length === 2) {
152
+ const domainWithOffset = scale.domain();
153
+ scale.domain([
154
+ startOnTick
155
+ ? Math.min(nicedDomain[0], domainWithOffset[0])
156
+ : domainWithOffset[0],
157
+ endOnTick
158
+ ? Math.max(nicedDomain[1], domainWithOffset[1])
159
+ : domainWithOffset[1],
160
+ ]);
161
+ }
162
+ return scale;
163
+ }
164
+ break;
165
+ }
166
+ case 'category': {
167
+ if (xCategories) {
168
+ const filteredCategories = filterCategoriesByVisibleSeries({
169
+ axisDirection: 'x',
170
+ categories: xCategories,
171
+ series: series,
172
+ });
173
+ const xScale = scaleBand().domain(filteredCategories).range([0, boundsWidth]);
174
+ if (xScale.step() / 2 < xAxisMaxPadding) {
175
+ xScale.range(range);
176
+ }
177
+ return xScale;
178
+ }
179
+ break;
180
+ }
181
+ case 'datetime': {
182
+ let domain = null;
183
+ const domainData = get(axis, 'timestamps') || getDomainDataXBySeries(series);
184
+ const { hasNumberAndNullValues, hasOnlyNullValues } = validateArrayData(domainData);
185
+ if (hasOnlyNullValues || domainData.length === 0) {
186
+ return undefined;
187
+ }
188
+ if (hasNumberAndNullValues) {
189
+ const [xMinTimestamp, xMaxTimestamp] = extent(domainData);
190
+ const isPointDomain = checkIsPointDomain([xMinTimestamp, xMaxTimestamp]);
191
+ const xMin = typeof xMinPropsOrState === 'number' &&
192
+ xMinPropsOrState > xMinTimestamp &&
193
+ !isPointDomain
194
+ ? xMinPropsOrState
195
+ : xMinTimestamp;
196
+ const xMax = getXMaxDomainResult({
197
+ xMaxDomain: xMaxTimestamp,
198
+ xMaxProps: get(axis, 'max'),
199
+ xMaxRangeSlider: rangeSliderState === null || rangeSliderState === void 0 ? void 0 : rangeSliderState.max,
200
+ xMaxZoom: zoomStateX === null || zoomStateX === void 0 ? void 0 : zoomStateX[1],
201
+ });
202
+ domain = [xMin, xMax];
203
+ const scale = scaleUtc().domain(domain).range(range);
204
+ let offsetMin = 0;
205
+ let offsetMax = xAxisMaxPadding;
206
+ const hasOffset = isSeriesWithXAxisOffset(series);
207
+ if (hasOffset) {
208
+ if (domainData.length > 1) {
209
+ const bandWidth = getBandSize({
210
+ scale: scale,
211
+ domain: domainData,
212
+ });
213
+ offsetMin += bandWidth / 2;
214
+ offsetMax += bandWidth / 2;
215
+ }
216
+ }
217
+ const isMinSpecified = typeof get(axis, 'min') === 'number' && !zoomStateX;
218
+ const isMaxSpecified = typeof get(axis, 'max') === 'number' && !zoomStateX;
219
+ const domainOffsetMin = isMinSpecified
220
+ ? 0
221
+ : Math.abs(scale.invert(offsetMin).getTime() - scale.invert(0).getTime());
222
+ const domainOffsetMax = isMaxSpecified
223
+ ? 0
224
+ : Math.abs(scale.invert(offsetMax).getTime() - scale.invert(0).getTime());
225
+ // 10 is the default value for the number of ticks. Here, to preserve the appearance of a series with a small number of points
226
+ const nicedDomain = scale.copy().nice(Math.max(10, domainData.length)).domain();
227
+ scale.domain([xMin - domainOffsetMin, xMax + domainOffsetMax]);
228
+ const startOnTick = get(axis, 'startOnTick', true);
229
+ const endOnTick = get(axis, 'endOnTick', true);
230
+ if (!hasZoomX && !hasOffset && nicedDomain.length === 2) {
231
+ const domainWithOffset = scale.domain();
232
+ scale.domain([
233
+ startOnTick
234
+ ? Math.min(Number(nicedDomain[0]), Number(domainWithOffset[0]))
235
+ : Number(domainWithOffset[0]),
236
+ endOnTick
237
+ ? Math.max(Number(nicedDomain[1]), Number(domainWithOffset[1]))
238
+ : Number(domainWithOffset[1]),
239
+ ]);
240
+ }
241
+ return scale;
242
+ }
243
+ break;
244
+ }
245
+ }
246
+ throw new Error('Failed to create xScale');
247
+ }
@@ -0,0 +1,10 @@
1
+ import type { PreparedAxis, PreparedSeries } from '../../hooks';
2
+ import type { ChartSeries } from '../../types';
3
+ export declare function createYScale(args: {
4
+ axis: PreparedAxis;
5
+ boundsHeight: number;
6
+ series: PreparedSeries[] | ChartSeries[];
7
+ primaryAxis?: PreparedAxis;
8
+ primaryTicksCount?: number;
9
+ zoomStateY?: [number, number];
10
+ }): import("d3-scale").ScaleBand<string> | import("d3-scale").ScaleLinear<number, number, never> | import("d3-scale").ScaleTime<number, number, never> | undefined;
@@ -0,0 +1,299 @@
1
+ import { extent, scaleBand, scaleLinear, scaleLog, scaleUtc, tickStep, ticks } from 'd3';
2
+ import get from 'lodash/get';
3
+ import { getTickValues } from '../../components/AxisY/utils';
4
+ import { SERIES_TYPE } from '../../constants';
5
+ import { CHART_SERIES_WITH_VOLUME_ON_Y_AXIS, getDomainDataYBySeries, shouldSyncAxisWithPrimary, } from '../../utils';
6
+ import { getBandSize } from '../utils/get-band-size';
7
+ import { checkIsPointDomain, filterCategoriesByVisibleSeries, getMinMaxPropsOrState, hasOnlyMarkerSeries, validateArrayData, } from './utils';
8
+ // axis is validated in `validation/index.ts`, so the value of `axis.type` is definitely valid.
9
+ // eslint-disable-next-line consistent-return
10
+ function getYScaleRange(args) {
11
+ const { axis, boundsHeight } = args;
12
+ switch (axis.type) {
13
+ case 'datetime':
14
+ case 'linear':
15
+ case 'logarithmic': {
16
+ const range = [boundsHeight, 0];
17
+ switch (axis.order) {
18
+ case 'sortDesc':
19
+ case 'reverse': {
20
+ range.reverse();
21
+ }
22
+ }
23
+ return range;
24
+ }
25
+ case 'category': {
26
+ return [boundsHeight, 0];
27
+ }
28
+ }
29
+ }
30
+ function isSeriesWithYAxisOffset(series) {
31
+ const types = [SERIES_TYPE.BarY, SERIES_TYPE.Heatmap];
32
+ return series.some((s) => types.includes(s.type));
33
+ }
34
+ function getDomainSyncedToPrimaryTicks(args) {
35
+ const { primaryTicksCount, scale, yMin, yMax } = args;
36
+ const [dMin, dMax] = scale.domain();
37
+ let secondaryTicks = ticks(dMin, dMax, primaryTicksCount);
38
+ let i = 1;
39
+ // Need to reduce the number of ticks to primaryTicksCount - 2, so that we can later
40
+ // add one tick each at the top and bottom edges of the chart
41
+ while (secondaryTicks.length > primaryTicksCount - 2) {
42
+ secondaryTicks = ticks(dMin, dMax, primaryTicksCount - i);
43
+ if (secondaryTicks.length === 0) {
44
+ secondaryTicks = ticks(dMin, dMax, primaryTicksCount - i + 1);
45
+ break;
46
+ }
47
+ i += 1;
48
+ }
49
+ const step = tickStep(dMin, dMax, secondaryTicks.length);
50
+ let ticksCountDiff = primaryTicksCount - secondaryTicks.length;
51
+ let deltaMin = Math.abs(dMin - secondaryTicks[0]);
52
+ let deltaMax = Math.abs(dMax - secondaryTicks[secondaryTicks.length - 1]);
53
+ while (ticksCountDiff > 0) {
54
+ if (deltaMin > deltaMax) {
55
+ secondaryTicks.unshift(secondaryTicks[0] - step);
56
+ deltaMin -= step;
57
+ }
58
+ else {
59
+ secondaryTicks.push(secondaryTicks[secondaryTicks.length - 1] + step);
60
+ deltaMax -= step;
61
+ }
62
+ ticksCountDiff -= 1;
63
+ }
64
+ if (secondaryTicks[secondaryTicks.length - 1] < yMax) {
65
+ secondaryTicks[secondaryTicks.length - 1] += step;
66
+ }
67
+ if (secondaryTicks[0] > yMin) {
68
+ secondaryTicks[0] -= step;
69
+ }
70
+ return [secondaryTicks[0], secondaryTicks[secondaryTicks.length - 1]];
71
+ }
72
+ function getDomainMinAlignedToStartTick(args) {
73
+ var _a, _b;
74
+ const { axis, range, scale, series } = args;
75
+ const [dMin, dMax] = scale.domain();
76
+ const tickValues = getTickValues({
77
+ axis,
78
+ scale,
79
+ labelLineHeight: axis.labels.lineHeight,
80
+ series,
81
+ });
82
+ const isStartOnTick = tickValues[0].y === range[0];
83
+ let dNewMin = dMin;
84
+ if (!isStartOnTick) {
85
+ let step;
86
+ if (typeof ((_a = tickValues[0]) === null || _a === void 0 ? void 0 : _a.value) === 'number' && typeof ((_b = tickValues[1]) === null || _b === void 0 ? void 0 : _b.value) === 'number') {
87
+ step = tickValues[1].value - tickValues[0].value;
88
+ }
89
+ else {
90
+ step = tickStep(dMin, dMax, 1);
91
+ }
92
+ dNewMin = tickValues[0].value - step;
93
+ }
94
+ return dNewMin;
95
+ }
96
+ function getDomainMaxAlignedToEndTick(args) {
97
+ var _a, _b;
98
+ const { axis, range, scale, series } = args;
99
+ const [dMin, dMax] = scale.domain();
100
+ const tickValues = getTickValues({
101
+ axis,
102
+ scale,
103
+ labelLineHeight: axis.labels.lineHeight,
104
+ series,
105
+ });
106
+ const isEndOnTick = tickValues[tickValues.length - 1].y === range[1];
107
+ let dNewMax = dMax;
108
+ if (!isEndOnTick) {
109
+ let step;
110
+ if (typeof ((_a = tickValues[0]) === null || _a === void 0 ? void 0 : _a.value) === 'number' && typeof ((_b = tickValues[1]) === null || _b === void 0 ? void 0 : _b.value) === 'number') {
111
+ step = tickValues[1].value - tickValues[0].value;
112
+ }
113
+ else {
114
+ step = tickStep(dMin, dMax, 1);
115
+ }
116
+ dNewMax = tickValues[tickValues.length - 1].value + step;
117
+ }
118
+ return dNewMax;
119
+ }
120
+ // eslint-disable-next-line complexity
121
+ export function createYScale(args) {
122
+ const { axis, boundsHeight, series, primaryAxis, primaryTicksCount, zoomStateY } = args;
123
+ const [yMinPropsOrState, yMaxPropsOrState] = getMinMaxPropsOrState({
124
+ axis,
125
+ maxValues: [zoomStateY === null || zoomStateY === void 0 ? void 0 : zoomStateY[1]],
126
+ minValues: [zoomStateY === null || zoomStateY === void 0 ? void 0 : zoomStateY[0]],
127
+ });
128
+ const yCategories = get(axis, 'categories');
129
+ const yTimestamps = get(axis, 'timestamps');
130
+ const range = getYScaleRange({ axis, boundsHeight });
131
+ switch (axis.type) {
132
+ case 'linear':
133
+ case 'logarithmic': {
134
+ const domain = getDomainDataYBySeries(series);
135
+ const { hasNumberAndNullValues, hasOnlyNullValues } = validateArrayData(domain);
136
+ if (hasOnlyNullValues || domain.length === 0) {
137
+ return undefined;
138
+ }
139
+ if (series.some((s) => (s.type === 'bar-x' || s.type === 'area') && s.stacking === 'percent')) {
140
+ return scaleLinear().domain([0, 100]).range(range);
141
+ }
142
+ if (hasNumberAndNullValues) {
143
+ const [yMinDomain, yMaxDomain] = extent(domain);
144
+ const isPointDomain = hasOnlyMarkerSeries(series)
145
+ ? checkIsPointDomain([yMinDomain, yMaxDomain])
146
+ : false;
147
+ const yMin = typeof yMinPropsOrState === 'number' && !isPointDomain
148
+ ? yMinPropsOrState
149
+ : yMinDomain;
150
+ let yMax;
151
+ if (typeof yMaxPropsOrState === 'number' && !isPointDomain) {
152
+ yMax = yMaxPropsOrState;
153
+ }
154
+ else {
155
+ const hasSeriesWithVolumeOnYAxis = series.some((s) => CHART_SERIES_WITH_VOLUME_ON_Y_AXIS.includes(s.type));
156
+ yMax = hasSeriesWithVolumeOnYAxis ? Math.max(yMaxDomain, 0) : yMaxDomain;
157
+ }
158
+ const scaleFn = axis.type === 'logarithmic' ? scaleLog : scaleLinear;
159
+ let scale = scaleFn().domain([yMin, yMax]).range(range);
160
+ let offsetMin = 0;
161
+ // We should ignore padding if we are drawing only one point on the plot.
162
+ let offsetMax = yMin === yMax ? 0 : boundsHeight * axis.maxPadding;
163
+ if (isSeriesWithYAxisOffset(series)) {
164
+ if (domain.length > 1) {
165
+ const bandWidth = getBandSize({
166
+ scale: scale,
167
+ domain: domain,
168
+ });
169
+ offsetMin += bandWidth / 2;
170
+ offsetMax += bandWidth / 2;
171
+ }
172
+ }
173
+ const isMinSpecified = typeof get(axis, 'min') === 'number' && !zoomStateY;
174
+ const isMaxSpecified = typeof get(axis, 'max') === 'number' && !zoomStateY;
175
+ const domainOffsetMin = isMinSpecified
176
+ ? 0
177
+ : Math.abs(scale.invert(offsetMin) - scale.invert(0));
178
+ const domainOffsetMax = isMaxSpecified
179
+ ? 0
180
+ : Math.abs(scale.invert(offsetMax) - scale.invert(0));
181
+ scale = scale.domain([yMin - domainOffsetMin, yMax + domainOffsetMax]);
182
+ const startOnTick = get(axis, 'startOnTick', false);
183
+ const endOnTick = get(axis, 'endOnTick', false);
184
+ const shouldBeSyncedWithPrimary = primaryAxis
185
+ ? shouldSyncAxisWithPrimary(axis, primaryAxis)
186
+ : false;
187
+ if (shouldBeSyncedWithPrimary &&
188
+ typeof primaryTicksCount === 'number' &&
189
+ primaryTicksCount >= 2) {
190
+ const newDomain = getDomainSyncedToPrimaryTicks({
191
+ scale,
192
+ primaryTicksCount,
193
+ yMin,
194
+ yMax,
195
+ });
196
+ scale.domain(newDomain);
197
+ }
198
+ if (startOnTick && (!primaryAxis || !shouldBeSyncedWithPrimary)) {
199
+ const [_, dMax] = scale.domain();
200
+ const dNewMin = getDomainMinAlignedToStartTick({ axis, range, scale, series });
201
+ scale.domain([dNewMin, dMax]);
202
+ }
203
+ if (endOnTick && (!primaryAxis || !shouldBeSyncedWithPrimary)) {
204
+ const [dMin, _] = scale.domain();
205
+ const dNewMax = getDomainMaxAlignedToEndTick({ axis, range, scale, series });
206
+ scale.domain([dMin, dNewMax]);
207
+ }
208
+ return scale;
209
+ }
210
+ break;
211
+ }
212
+ case 'category': {
213
+ if (yCategories) {
214
+ const filteredCategories = filterCategoriesByVisibleSeries({
215
+ axisDirection: 'y',
216
+ categories: yCategories,
217
+ series: series,
218
+ });
219
+ return scaleBand().domain(filteredCategories).range(range);
220
+ }
221
+ break;
222
+ }
223
+ case 'datetime': {
224
+ if (yTimestamps) {
225
+ const [yMinTimestamp, yMaxTimestamp] = extent(yTimestamps);
226
+ const isPointDomain = hasOnlyMarkerSeries(series)
227
+ ? checkIsPointDomain([yMinTimestamp, yMaxTimestamp])
228
+ : false;
229
+ const yMin = typeof yMinPropsOrState === 'number' &&
230
+ !isPointDomain &&
231
+ yMinPropsOrState > yMinTimestamp
232
+ ? yMinPropsOrState
233
+ : yMinTimestamp;
234
+ const yMax = typeof yMaxPropsOrState === 'number' &&
235
+ !isPointDomain &&
236
+ yMaxPropsOrState < yMaxTimestamp
237
+ ? yMaxPropsOrState
238
+ : yMaxTimestamp;
239
+ const scale = scaleUtc().domain([yMin, yMax]).range(range);
240
+ const startOnTick = get(axis, 'startOnTick', true);
241
+ const endOnTick = get(axis, 'endOnTick', true);
242
+ if (startOnTick || endOnTick) {
243
+ const nicedDomain = scale.copy().nice().domain();
244
+ return scale.domain([
245
+ startOnTick ? Number(nicedDomain[0]) : yMin,
246
+ endOnTick ? Number(nicedDomain[1]) : yMax,
247
+ ]);
248
+ }
249
+ return scale;
250
+ }
251
+ else {
252
+ const domain = getDomainDataYBySeries(series);
253
+ const { hasNumberAndNullValues, hasOnlyNullValues } = validateArrayData(domain);
254
+ if (hasOnlyNullValues || domain.length === 0) {
255
+ return undefined;
256
+ }
257
+ if (hasNumberAndNullValues) {
258
+ const [yMinTimestamp, yMaxTimestamp] = extent(domain);
259
+ const isPointDomain = hasOnlyMarkerSeries(series)
260
+ ? checkIsPointDomain([yMinTimestamp, yMaxTimestamp])
261
+ : false;
262
+ const yMin = typeof yMinPropsOrState === 'number' &&
263
+ !isPointDomain &&
264
+ yMinPropsOrState > yMinTimestamp
265
+ ? yMinPropsOrState
266
+ : yMinTimestamp;
267
+ const yMax = typeof yMaxPropsOrState === 'number' &&
268
+ !isPointDomain &&
269
+ yMaxPropsOrState < yMaxTimestamp
270
+ ? yMaxPropsOrState
271
+ : yMaxTimestamp;
272
+ const scale = scaleUtc().domain([yMin, yMax]).range(range);
273
+ let offsetMin = 0;
274
+ let offsetMax = boundsHeight * axis.maxPadding;
275
+ if (isSeriesWithYAxisOffset(series)) {
276
+ if (Object.keys(domain).length > 1) {
277
+ const bandWidth = getBandSize({
278
+ scale: scale,
279
+ domain: domain,
280
+ });
281
+ offsetMin += bandWidth / 2;
282
+ offsetMax += bandWidth / 2;
283
+ }
284
+ }
285
+ const isMinSpecified = typeof get(axis, 'min') === 'number' && !zoomStateY;
286
+ const isMaxSpecified = typeof get(axis, 'max') === 'number' && !zoomStateY;
287
+ const domainOffsetMin = isMinSpecified
288
+ ? 0
289
+ : Math.abs(scale.invert(offsetMin).getTime() - scale.invert(0).getTime());
290
+ const domainOffsetMax = isMaxSpecified
291
+ ? 0
292
+ : Math.abs(scale.invert(offsetMax).getTime() - scale.invert(0).getTime());
293
+ return scale.domain([yMin - domainOffsetMin, yMax + domainOffsetMax]);
294
+ }
295
+ }
296
+ }
297
+ }
298
+ throw new Error('Failed to create yScale');
299
+ }
@@ -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';