@gravity-ui/charts 1.18.0 → 1.18.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/dist/cjs/components/AxisY/prepare-axis-data.d.ts +3 -2
  2. package/dist/cjs/components/AxisY/prepare-axis-data.js +6 -6
  3. package/dist/cjs/components/AxisY/utils.d.ts +4 -2
  4. package/dist/cjs/components/AxisY/utils.js +47 -14
  5. package/dist/cjs/components/ChartInner/index.js +2 -1
  6. package/dist/cjs/components/ChartInner/useChartInnerProps.js +1 -2
  7. package/dist/cjs/components/Legend/index.js +1 -1
  8. package/dist/cjs/hooks/useAxisScales/index.d.ts +0 -1
  9. package/dist/cjs/hooks/useAxisScales/index.js +42 -21
  10. package/dist/cjs/hooks/useChartOptions/utils.d.ts +5 -2
  11. package/dist/cjs/hooks/useChartOptions/utils.js +2 -3
  12. package/dist/cjs/hooks/useChartOptions/y-axis.d.ts +1 -3
  13. package/dist/cjs/hooks/useChartOptions/y-axis.js +3 -5
  14. package/dist/cjs/hooks/useSeries/prepare-bar-y.js +3 -1
  15. package/dist/cjs/hooks/useShapes/bar-y/prepare-data.d.ts +4 -2
  16. package/dist/cjs/hooks/useShapes/bar-y/prepare-data.js +46 -24
  17. package/dist/cjs/hooks/useShapes/index.js +2 -0
  18. package/dist/cjs/hooks/useZoom/utils.js +24 -8
  19. package/dist/cjs/hooks/utils/bar-y.d.ts +7 -10
  20. package/dist/cjs/hooks/utils/bar-y.js +33 -18
  21. package/dist/cjs/utils/chart/array.js +3 -0
  22. package/dist/cjs/utils/chart/format.js +2 -2
  23. package/dist/cjs/utils/chart/index.js +2 -1
  24. package/dist/cjs/utils/chart/labels.d.ts +27 -5
  25. package/dist/cjs/utils/chart/labels.js +39 -3
  26. package/dist/cjs/validation/index.js +4 -1
  27. package/dist/esm/components/AxisY/prepare-axis-data.d.ts +3 -2
  28. package/dist/esm/components/AxisY/prepare-axis-data.js +6 -6
  29. package/dist/esm/components/AxisY/utils.d.ts +4 -2
  30. package/dist/esm/components/AxisY/utils.js +47 -14
  31. package/dist/esm/components/ChartInner/index.js +2 -1
  32. package/dist/esm/components/ChartInner/useChartInnerProps.js +1 -2
  33. package/dist/esm/components/Legend/index.js +1 -1
  34. package/dist/esm/hooks/useAxisScales/index.d.ts +0 -1
  35. package/dist/esm/hooks/useAxisScales/index.js +42 -21
  36. package/dist/esm/hooks/useChartOptions/utils.d.ts +5 -2
  37. package/dist/esm/hooks/useChartOptions/utils.js +2 -3
  38. package/dist/esm/hooks/useChartOptions/y-axis.d.ts +1 -3
  39. package/dist/esm/hooks/useChartOptions/y-axis.js +3 -5
  40. package/dist/esm/hooks/useSeries/prepare-bar-y.js +3 -1
  41. package/dist/esm/hooks/useShapes/bar-y/prepare-data.d.ts +4 -2
  42. package/dist/esm/hooks/useShapes/bar-y/prepare-data.js +46 -24
  43. package/dist/esm/hooks/useShapes/index.js +2 -0
  44. package/dist/esm/hooks/useZoom/utils.js +24 -8
  45. package/dist/esm/hooks/utils/bar-y.d.ts +7 -10
  46. package/dist/esm/hooks/utils/bar-y.js +33 -18
  47. package/dist/esm/utils/chart/array.js +3 -0
  48. package/dist/esm/utils/chart/format.js +2 -2
  49. package/dist/esm/utils/chart/index.js +2 -1
  50. package/dist/esm/utils/chart/labels.d.ts +27 -5
  51. package/dist/esm/utils/chart/labels.js +39 -3
  52. package/dist/esm/validation/index.js +4 -1
  53. package/package.json +1 -1
@@ -1,10 +1,11 @@
1
- import type { ChartScale, PreparedAxis, PreparedSplit } from '../../hooks';
1
+ import type { ChartScale, PreparedAxis, PreparedSeries, PreparedSplit } from '../../hooks';
2
2
  import type { AxisYData } from './types';
3
- export declare function prepareAxisData({ axis, split, scale, top: topOffset, width, height, }: {
3
+ export declare function prepareAxisData({ axis, split, scale, top: topOffset, width, height, series, }: {
4
4
  axis: PreparedAxis;
5
5
  split: PreparedSplit;
6
6
  scale: ChartScale;
7
7
  top: number;
8
8
  width: number;
9
9
  height: number;
10
+ series: PreparedSeries[];
10
11
  }): Promise<AxisYData>;
@@ -1,5 +1,5 @@
1
1
  import { getUniqId } from '@gravity-ui/uikit';
2
- import { calculateCos, calculateSin, getBandsPosition, getLabelFormatter, getLabelsSize, getTextSizeFn, getTextWithElipsis, wrapText, } from '../../utils';
2
+ import { calculateCos, calculateSin, formatAxisTickLabel, getBandsPosition, getLabelsSize, getMinSpaceBetween, getTextSizeFn, getTextWithElipsis, wrapText, } from '../../utils';
3
3
  import { getTickValues } from './utils';
4
4
  async function getSvgAxisLabel({ getTextSize, text, axis, top, left, labelMaxHeight, topOffset, }) {
5
5
  var _a;
@@ -81,7 +81,7 @@ async function getSvgAxisLabel({ getTextSize, text, axis, top, left, labelMaxHei
81
81
  return svgLabel;
82
82
  }
83
83
  // eslint-disable-next-line complexity
84
- export async function prepareAxisData({ axis, split, scale, top: topOffset, width, height, }) {
84
+ export async function prepareAxisData({ axis, split, scale, top: topOffset, width, height, series, }) {
85
85
  var _a, _b, _c;
86
86
  const axisPlotTopPosition = ((_a = split.plots[axis.plotIndex]) === null || _a === void 0 ? void 0 : _a.top) || 0;
87
87
  const axisHeight = ((_b = split.plots[axis.plotIndex]) === null || _b === void 0 ? void 0 : _b.height) || height;
@@ -94,9 +94,9 @@ export async function prepareAxisData({ axis, split, scale, top: topOffset, widt
94
94
  const ticks = [];
95
95
  const getTextSize = getTextSizeFn({ style: axis.labels.style });
96
96
  const labelLineHeight = (await getTextSize('Tmp')).height;
97
- const values = getTickValues({ scale, axis, labelLineHeight });
97
+ const values = getTickValues({ scale, axis, labelLineHeight, series });
98
+ const tickStep = getMinSpaceBetween(values, (d) => Number(d.value));
98
99
  const labelMaxHeight = values.length > 1 ? values[0].y - values[1].y - axis.labels.padding * 2 : axisHeight;
99
- const labelFormatter = getLabelFormatter({ axis, scale });
100
100
  for (let i = 0; i < values.length; i++) {
101
101
  const tickValue = values[i];
102
102
  const y = axisPlotTopPosition + tickValue.y;
@@ -125,7 +125,7 @@ export async function prepareAxisData({ axis, split, scale, top: topOffset, widt
125
125
  };
126
126
  }
127
127
  else {
128
- const text = labelFormatter(tickValue.value);
128
+ const text = formatAxisTickLabel({ value: tickValue.value, axis, step: tickStep });
129
129
  svgLabel = await getSvgAxisLabel({
130
130
  getTextSize,
131
131
  text,
@@ -226,7 +226,7 @@ export async function prepareAxisData({ axis, split, scale, top: topOffset, widt
226
226
  content: titleContent,
227
227
  style: axis.title.style,
228
228
  size: rotatedTitleSize,
229
- x: x,
229
+ x,
230
230
  y: axisPlotTopPosition + y,
231
231
  rotate: rotateAngle,
232
232
  offset: -(originalTextSize.height / titleContent.length) * (titleContent.length - 1),
@@ -1,8 +1,10 @@
1
- import type { ChartScale, PreparedAxis } from '../../hooks';
2
- export declare function getTickValues({ scale, axis, labelLineHeight, }: {
1
+ import type { ChartScale, PreparedAxis, PreparedSeries } from '../../hooks';
2
+ import type { ChartSeries } from '../../types';
3
+ export declare function getTickValues({ scale, axis, labelLineHeight, series, }: {
3
4
  scale: ChartScale;
4
5
  axis: PreparedAxis;
5
6
  labelLineHeight: number;
7
+ series: PreparedSeries[] | ChartSeries[];
6
8
  }): {
7
9
  y: number;
8
10
  value: number | Date;
@@ -1,4 +1,4 @@
1
- import { getMinSpaceBetween, getTicksCount, isBandScale } from '../../utils';
1
+ import { getDomainDataYBySeries, getMinSpaceBetween, getTicksCount, isBandScale } from '../../utils';
2
2
  function thinOut(items, delta) {
3
3
  const arr = [];
4
4
  for (let i = 0; i < items.length; i = i + delta) {
@@ -6,30 +6,63 @@ function thinOut(items, delta) {
6
6
  }
7
7
  return arr;
8
8
  }
9
- export function getTickValues({ scale, axis, labelLineHeight, }) {
9
+ export function getTickValues({ scale, axis, labelLineHeight, series, }) {
10
10
  if ('ticks' in scale && typeof scale.ticks === 'function') {
11
11
  const range = scale.range();
12
12
  const height = Math.abs(range[0] - range[1]);
13
13
  if (!height) {
14
14
  return [];
15
15
  }
16
- let ticksCount = getTicksCount({ axis, range: height });
17
- let result = scale.ticks(ticksCount).map((t) => ({
16
+ const getScaleTicks = () => {
17
+ var _a;
18
+ if (series.some((s) => s.type === 'bar-y')) {
19
+ const domainData = getDomainDataYBySeries(series);
20
+ if (domainData.length < 3) {
21
+ return domainData;
22
+ }
23
+ const ticksCount = (_a = getTicksCount({ axis, range: height })) !== null && _a !== void 0 ? _a : domainData.length;
24
+ return scale.ticks(Math.min(ticksCount, domainData.length));
25
+ }
26
+ const ticksCount = getTicksCount({ axis, range: height });
27
+ return scale.ticks(ticksCount);
28
+ };
29
+ const scaleTicks = getScaleTicks();
30
+ const originalTickValues = scaleTicks.map((t) => ({
18
31
  y: scale(t),
19
32
  value: t,
20
33
  }));
21
- if (result.length <= 1) {
22
- return result;
34
+ if (originalTickValues.length <= 1) {
35
+ return originalTickValues;
23
36
  }
24
- let labelHeight = getMinSpaceBetween(result, (d) => d.y) - axis.labels.padding * 2;
25
- ticksCount = result.length - 1;
26
- while (labelHeight < labelLineHeight && result.length > 1) {
37
+ // first, we try to draw "beautiful" tick values
38
+ let result = originalTickValues;
39
+ let availableSpaceForLabel = getMinSpaceBetween(result, (d) => d.y) - axis.labels.padding * 2;
40
+ let ticksCount = result.length - 1;
41
+ while (availableSpaceForLabel < labelLineHeight && result.length > 1) {
27
42
  ticksCount = ticksCount ? ticksCount - 1 : result.length - 1;
28
- result = scale.ticks(ticksCount).map((t) => ({
43
+ const newScaleTicks = scale.ticks(ticksCount);
44
+ result = newScaleTicks.map((t) => ({
29
45
  y: scale(t),
30
46
  value: t,
31
47
  }));
32
- labelHeight = getMinSpaceBetween(result, (d) => d.y) - axis.labels.padding * 2;
48
+ availableSpaceForLabel =
49
+ getMinSpaceBetween(result, (d) => d.y) - axis.labels.padding * 2;
50
+ }
51
+ // when this is not possible (for example, such values cannot be selected for the logarithmic axis with a small range)
52
+ // just thin out the originally proposed result
53
+ if (!result.length) {
54
+ result = originalTickValues;
55
+ availableSpaceForLabel =
56
+ getMinSpaceBetween(result, (d) => d.y) - axis.labels.padding * 2;
57
+ let delta = 2;
58
+ while (availableSpaceForLabel < labelLineHeight && result.length > 1) {
59
+ result = thinOut(result, delta);
60
+ if (result.length > 1) {
61
+ delta += 1;
62
+ availableSpaceForLabel =
63
+ getMinSpaceBetween(result, (d) => d.y) - axis.labels.padding * 2;
64
+ }
65
+ }
33
66
  }
34
67
  return result;
35
68
  }
@@ -47,13 +80,13 @@ export function getTickValues({ scale, axis, labelLineHeight, }) {
47
80
  return items;
48
81
  }
49
82
  let result = [...items];
50
- let labelHeight = result[0].y - result[1].y - axis.labels.padding * 2;
83
+ let availableSpaceForLabel = Math.abs(result[0].y - result[1].y) - axis.labels.padding * 2;
51
84
  let delta = 2;
52
- while (labelHeight < labelLineHeight && result.length > 1) {
85
+ while (availableSpaceForLabel < labelLineHeight && result.length > 1) {
53
86
  result = thinOut(items, delta);
54
87
  if (result.length > 1) {
55
88
  delta += 1;
56
- labelHeight = result[0].y - result[1].y - axis.labels.padding * 2;
89
+ availableSpaceForLabel = result[0].y - result[1].y - axis.labels.padding * 2;
57
90
  }
58
91
  }
59
92
  return result;
@@ -100,6 +100,7 @@ export const ChartInner = (props) => {
100
100
  width: boundsWidth,
101
101
  height: boundsHeight,
102
102
  split: preparedSplit,
103
+ series: preparedSeries,
103
104
  });
104
105
  items.push(axisData);
105
106
  }
@@ -108,7 +109,7 @@ export const ChartInner = (props) => {
108
109
  setYAxisDataItems(items);
109
110
  }
110
111
  })();
111
- }, [boundsHeight, boundsOffsetTop, boundsWidth, preparedSplit, yAxis, yScale]);
112
+ }, [boundsHeight, boundsOffsetTop, boundsWidth, preparedSeries, preparedSplit, yAxis, yScale]);
112
113
  return (React.createElement("div", { className: b() },
113
114
  React.createElement("svg", { ref: svgRef, width: width, height: height,
114
115
  // We use onPointerMove here because onMouseMove works incorrectly when the zoom setting is enabled:
@@ -66,10 +66,9 @@ export function useChartInnerProps(props) {
66
66
  boundsHeight: estimatedBoundsHeight,
67
67
  width,
68
68
  seriesData: zoomedSeriesData,
69
- seriesOptions: preparedSeriesOptions,
70
69
  yAxis: data.yAxis,
71
70
  }).then((val) => setYAxis(val));
72
- }, [data.yAxis, estimatedBoundsHeight, height, preparedSeriesOptions, width, zoomedSeriesData]);
71
+ }, [data.yAxis, estimatedBoundsHeight, height, width, zoomedSeriesData]);
73
72
  const { preparedSeries, preparedLegend, handleLegendItemClick } = useSeries({
74
73
  colors,
75
74
  legend: data.legend,
@@ -85,7 +85,7 @@ function renderLegendSymbol(args) {
85
85
  case 'path': {
86
86
  const y = legendLineHeight / 2;
87
87
  const points = [
88
- { x: x, y },
88
+ { x, y },
89
89
  { x: x + d.symbol.width, y },
90
90
  ];
91
91
  element
@@ -23,7 +23,6 @@ export declare function createYScale(args: {
23
23
  axis: PreparedAxis;
24
24
  boundsHeight: number;
25
25
  series: (PreparedSeries | ChartSeries)[];
26
- seriesOptions: PreparedSeriesOptions;
27
26
  }): ScaleBand<string> | ScaleLinear<number, number, never> | ScaleTime<number, number, never> | undefined;
28
27
  export declare function createXScale(args: {
29
28
  axis: PreparedAxis | ChartAxis;
@@ -3,7 +3,7 @@ import { extent, scaleBand, scaleLinear, scaleLog, scaleUtc } from 'd3';
3
3
  import get from 'lodash/get';
4
4
  import { DEFAULT_AXIS_TYPE, SeriesType } from '../../constants';
5
5
  import { CHART_SERIES_WITH_VOLUME_ON_Y_AXIS, getAxisHeight, getDataCategoryValue, getDefaultMaxXAxisValue, getDefaultMinXAxisValue, getDomainDataXBySeries, getDomainDataYBySeries, getOnlyVisibleSeries, isAxisRelatedSeries, isSeriesWithCategoryValues, } from '../../utils';
6
- import { getBarYLayoutForNumericScale, groupBarYDataByYValue } from '../utils';
6
+ import { getBandSize } from '../utils';
7
7
  import { getBarXLayoutForNumericScale, groupBarXDataByXValue } from '../utils/bar-x';
8
8
  const X_AXIS_ZOOM_PADDING = 0.02;
9
9
  function validateArrayData(data) {
@@ -40,25 +40,12 @@ function filterCategoriesByVisibleSeries(args) {
40
40
  // axis is validated in `validation/index.ts`, so the value of `axis.type` is definitely valid.
41
41
  // eslint-disable-next-line consistent-return
42
42
  function getYScaleRange(args) {
43
- const { axis, boundsHeight, series, seriesOptions } = args;
43
+ const { axis, boundsHeight } = args;
44
44
  switch (axis.type) {
45
45
  case 'datetime':
46
46
  case 'linear':
47
47
  case 'logarithmic': {
48
- let range = [boundsHeight, boundsHeight * axis.maxPadding];
49
- const barYSeries = series.filter((s) => s.type === SeriesType.BarY);
50
- if (barYSeries.length) {
51
- const groupedData = groupBarYDataByYValue(barYSeries, [axis]);
52
- if (Object.keys(groupedData).length > 1) {
53
- const { bandSize } = getBarYLayoutForNumericScale({
54
- plotHeight: boundsHeight - boundsHeight * axis.maxPadding,
55
- groupedData,
56
- seriesOptions: seriesOptions,
57
- });
58
- const offset = bandSize / 2;
59
- range = [range[0] - offset, range[1] + offset];
60
- }
61
- }
48
+ const range = [boundsHeight, 0];
62
49
  switch (axis.order) {
63
50
  case 'sortDesc':
64
51
  case 'reverse': {
@@ -74,12 +61,12 @@ function getYScaleRange(args) {
74
61
  }
75
62
  // eslint-disable-next-line complexity
76
63
  export function createYScale(args) {
77
- const { axis, boundsHeight, series, seriesOptions } = args;
64
+ const { axis, boundsHeight, series } = args;
78
65
  const yMinProps = get(axis, 'min');
79
66
  const yMaxProps = get(axis, 'max');
80
67
  const yCategories = get(axis, 'categories');
81
68
  const yTimestamps = get(axis, 'timestamps');
82
- const range = getYScaleRange({ axis, boundsHeight, series, seriesOptions });
69
+ const range = getYScaleRange({ axis, boundsHeight });
83
70
  switch (axis.type) {
84
71
  case 'linear':
85
72
  case 'logarithmic': {
@@ -100,7 +87,23 @@ export function createYScale(args) {
100
87
  yMax = hasSeriesWithVolumeOnYAxis ? Math.max(yMaxDomain, 0) : yMaxDomain;
101
88
  }
102
89
  const scaleFn = axis.type === 'logarithmic' ? scaleLog : scaleLinear;
103
- return scaleFn().domain([yMin, yMax]).range(range).nice();
90
+ const scale = scaleFn().domain([yMin, yMax]).range(range);
91
+ let offsetMin = 0;
92
+ let offsetMax = boundsHeight * axis.maxPadding;
93
+ const barYSeries = series.filter((s) => s.type === SeriesType.BarY);
94
+ if (barYSeries.length) {
95
+ if (domain.length > 1) {
96
+ const bandWidth = getBandSize({
97
+ scale: scale,
98
+ domain: domain,
99
+ });
100
+ offsetMin += bandWidth / 2;
101
+ offsetMax += bandWidth / 2;
102
+ }
103
+ }
104
+ const domainOffsetMin = Math.abs(scale.invert(offsetMin) - scale.invert(0));
105
+ const domainOffsetMax = Math.abs(scale.invert(offsetMax) - scale.invert(0));
106
+ return scale.domain([yMin - domainOffsetMin, yMax + domainOffsetMax]);
104
107
  }
105
108
  break;
106
109
  }
@@ -132,7 +135,23 @@ export function createYScale(args) {
132
135
  const [yMinTimestamp, yMaxTimestamp] = extent(domain);
133
136
  const yMin = typeof yMinProps === 'number' ? yMinProps : yMinTimestamp;
134
137
  const yMax = typeof yMaxProps === 'number' ? yMaxProps : yMaxTimestamp;
135
- return scaleUtc().domain([yMin, yMax]).range(range).nice();
138
+ const scale = scaleUtc().domain([yMin, yMax]).range(range);
139
+ let offsetMin = 0;
140
+ let offsetMax = boundsHeight * axis.maxPadding;
141
+ const barYSeries = series.filter((s) => s.type === SeriesType.BarY);
142
+ if (barYSeries.length) {
143
+ if (Object.keys(domain).length > 1) {
144
+ const bandWidth = getBandSize({
145
+ scale: scale,
146
+ domain: domain,
147
+ });
148
+ offsetMin += bandWidth / 2;
149
+ offsetMax += bandWidth / 2;
150
+ }
151
+ }
152
+ const domainOffsetMin = Math.abs(scale.invert(offsetMin).getTime() - scale.invert(0).getTime());
153
+ const domainOffsetMax = Math.abs(scale.invert(offsetMax).getTime() - scale.invert(0).getTime());
154
+ return scale.domain([yMin - domainOffsetMin, yMax + domainOffsetMax]);
136
155
  }
137
156
  }
138
157
  }
@@ -207,6 +226,9 @@ export function createXScale(args) {
207
226
  if (hasOnlyNullValues || domainData.length === 0) {
208
227
  return undefined;
209
228
  }
229
+ if (series.some((s) => s.stacking === 'percent')) {
230
+ return scaleLinear().domain([0, 100]).range(range);
231
+ }
210
232
  if (hasNumberAndNullValues) {
211
233
  const [xMinDomain, xMaxDomain] = extent(domainData);
212
234
  let xMin;
@@ -304,7 +326,6 @@ const createScales = (args) => {
304
326
  axis,
305
327
  boundsHeight: axisHeight,
306
328
  series: visibleAxisSeries.length ? visibleAxisSeries : axisSeries,
307
- seriesOptions,
308
329
  });
309
330
  }),
310
331
  };
@@ -1,4 +1,4 @@
1
- import type { AxisPlot, ChartYAxis } from '../../types';
1
+ import type { AxisPlot, ChartAxis } from '../../types';
2
2
  export declare function prepareAxisPlotLabel(d: AxisPlot): {
3
3
  text: string;
4
4
  style: {
@@ -8,4 +8,7 @@ export declare function prepareAxisPlotLabel(d: AxisPlot): {
8
8
  };
9
9
  padding: number;
10
10
  };
11
- export declare function getAxisCategories(axis?: ChartYAxis): string[] | undefined;
11
+ export declare function getAxisCategories({ categories, order, }?: {
12
+ categories?: string[];
13
+ order?: ChartAxis['order'];
14
+ }): string[] | undefined;
@@ -8,10 +8,9 @@ export function prepareAxisPlotLabel(d) {
8
8
  padding: (_e = (_d = d.label) === null || _d === void 0 ? void 0 : _d.padding) !== null && _e !== void 0 ? _e : 5,
9
9
  };
10
10
  }
11
- export function getAxisCategories(axis) {
12
- const categories = axis === null || axis === void 0 ? void 0 : axis.categories;
11
+ export function getAxisCategories({ categories, order, } = {}) {
13
12
  if (categories) {
14
- switch (axis.order) {
13
+ switch (order) {
15
14
  case 'reverse': {
16
15
  return reverse(categories);
17
16
  }
@@ -1,11 +1,9 @@
1
1
  import type { ChartSeries, ChartYAxis } from '../../types';
2
- import type { PreparedSeriesOptions } from '../useSeries/types';
3
2
  import type { PreparedAxis } from './types';
4
- export declare const getPreparedYAxis: ({ height, boundsHeight, width, seriesData, seriesOptions, yAxis, }: {
3
+ export declare const getPreparedYAxis: ({ height, boundsHeight, width, seriesData, yAxis, }: {
5
4
  height: number;
6
5
  boundsHeight: number;
7
6
  width: number;
8
7
  seriesData: ChartSeries[];
9
- seriesOptions: PreparedSeriesOptions;
10
8
  yAxis: ChartYAxis[] | undefined;
11
9
  }) => Promise<PreparedAxis[]>;
@@ -5,7 +5,7 @@ import { calculateNumericProperty, formatAxisTickLabel, getClosestPointsRange, g
5
5
  import { createYScale } from '../useAxisScales';
6
6
  import { getAxisCategories, prepareAxisPlotLabel } from './utils';
7
7
  const getAxisLabelMaxWidth = async (args) => {
8
- const { axis, seriesData, seriesOptions, height } = args;
8
+ const { axis, seriesData, height } = args;
9
9
  if (!axis.labels.enabled) {
10
10
  return { height: 0, width: 0 };
11
11
  }
@@ -13,14 +13,13 @@ const getAxisLabelMaxWidth = async (args) => {
13
13
  axis,
14
14
  boundsHeight: height,
15
15
  series: seriesData,
16
- seriesOptions,
17
16
  });
18
17
  if (!scale) {
19
18
  return { height: 0, width: 0 };
20
19
  }
21
20
  const getTextSize = getTextSizeFn({ style: axis.labels.style });
22
21
  const labelLineHeight = (await getTextSize('Tmp')).height;
23
- const tickValues = getTickValues({ axis, scale, labelLineHeight });
22
+ const tickValues = getTickValues({ axis, scale, labelLineHeight, series: seriesData });
24
23
  const ticks = getScaleTicks(scale);
25
24
  const tickStep = getClosestPointsRange(axis, ticks);
26
25
  if (axis.type === 'datetime' && !axis.labels.dateFormat) {
@@ -39,7 +38,7 @@ const getAxisLabelMaxWidth = async (args) => {
39
38
  });
40
39
  return { height: size.maxHeight, width: size.maxWidth };
41
40
  };
42
- export const getPreparedYAxis = ({ height, boundsHeight, width, seriesData, seriesOptions, yAxis, }) => {
41
+ export const getPreparedYAxis = ({ height, boundsHeight, width, seriesData, yAxis, }) => {
43
42
  const axisByPlot = [];
44
43
  const axisItems = yAxis || [{}];
45
44
  const hasAxisRelatedSeries = seriesData.some(isAxisRelatedSeries);
@@ -153,7 +152,6 @@ export const getPreparedYAxis = ({ height, boundsHeight, width, seriesData, seri
153
152
  const { height: labelsHeight, width: labelsWidth } = await getAxisLabelMaxWidth({
154
153
  axis: preparedAxis,
155
154
  seriesData,
156
- seriesOptions,
157
155
  height: boundsHeight,
158
156
  });
159
157
  preparedAxis.labels.height = labelsHeight;
@@ -9,7 +9,9 @@ async function prepareDataLabels(series) {
9
9
  const style = Object.assign({}, DEFAULT_DATALABELS_STYLE, (_a = series.dataLabels) === null || _a === void 0 ? void 0 : _a.style);
10
10
  const html = get(series, 'dataLabels.html', false);
11
11
  const labels = enabled
12
- ? series.data.map((d) => getFormattedValue(Object.assign({ value: d.x || d.label }, series.dataLabels)))
12
+ ? series.data
13
+ .filter((d) => Boolean(d.x || d.label))
14
+ .map((d) => getFormattedValue(Object.assign({ value: d.x || d.label }, series.dataLabels)))
13
15
  : [];
14
16
  const { maxHeight = 0, maxWidth = 0 } = await getLabelsSize({
15
17
  labels,
@@ -2,11 +2,13 @@ import type { ChartScale } from '../../useAxisScales';
2
2
  import type { PreparedAxis } from '../../useChartOptions/types';
3
3
  import type { PreparedBarYSeries, PreparedSeriesOptions } from '../../useSeries/types';
4
4
  import type { BarYShapesArgs } from './types';
5
- export declare const prepareBarYData: (args: {
5
+ export declare function prepareBarYData(args: {
6
+ boundsHeight: number;
7
+ boundsWidth: number;
6
8
  series: PreparedBarYSeries[];
7
9
  seriesOptions: PreparedSeriesOptions;
8
10
  xAxis: PreparedAxis;
9
11
  xScale: ChartScale;
10
12
  yAxis: PreparedAxis[];
11
13
  yScale: (ChartScale | undefined)[];
12
- }) => Promise<BarYShapesArgs>;
14
+ }): Promise<BarYShapesArgs>;
@@ -1,12 +1,12 @@
1
1
  import { ascending, descending, sort } from 'd3';
2
2
  import get from 'lodash/get';
3
- import { filterOverlappingLabels, getLabelsSize, getTextSizeFn } from '../../../utils';
3
+ import { filterOverlappingLabels, getHtmlLabelConstraintedPosition, getLabelsSize, getSvgLabelConstraintedPosition, getTextSizeFn, } from '../../../utils';
4
4
  import { getFormattedValue } from '../../../utils/chart/format';
5
- import { getBarYLayoutForCategoryScale, getBarYLayoutForNumericScale, groupBarYDataByYValue, } from '../../utils';
5
+ import { getBarYLayout, groupBarYDataByYValue } from '../../utils';
6
6
  const DEFAULT_LABEL_PADDING = 7;
7
- export const prepareBarYData = async (args) => {
7
+ export async function prepareBarYData(args) {
8
8
  var _a;
9
- const { series, seriesOptions, yAxis, xScale, yScale: [yScale], } = args;
9
+ const { boundsHeight, boundsWidth, series, seriesOptions, yAxis, xScale, yScale: [yScale], } = args;
10
10
  const stackGap = seriesOptions['bar-y'].stackGap;
11
11
  const xLinearScale = xScale;
12
12
  const yLinearScale = yScale;
@@ -18,7 +18,6 @@ export const prepareBarYData = async (args) => {
18
18
  };
19
19
  }
20
20
  const yScaleRange = yLinearScale.range();
21
- const plotHeight = Math.abs(yScaleRange[0] - yScaleRange[1]);
22
21
  const sortingOptions = get(seriesOptions, 'bar-y.dataSorting');
23
22
  const comparator = (sortingOptions === null || sortingOptions === void 0 ? void 0 : sortingOptions.direction) === 'desc' ? descending : ascending;
24
23
  const sortKey = (() => {
@@ -35,13 +34,13 @@ export const prepareBarYData = async (args) => {
35
34
  }
36
35
  })();
37
36
  const groupedData = groupBarYDataByYValue(series, yAxis);
38
- const { bandSize, barGap, barSize } = yAxis[0].type === 'category'
39
- ? getBarYLayoutForCategoryScale({ groupedData, seriesOptions, yScale: yLinearScale })
40
- : getBarYLayoutForNumericScale({
41
- groupedData,
42
- seriesOptions,
43
- plotHeight: plotHeight - plotHeight * yAxis[0].maxPadding,
44
- });
37
+ const plotHeight = Math.abs(yScaleRange[0] - yScaleRange[1]);
38
+ const { bandSize, barGap, barSize } = getBarYLayout({
39
+ groupedData,
40
+ seriesOptions,
41
+ plotHeight,
42
+ scale: yScale,
43
+ });
45
44
  const result = [];
46
45
  const baseRangeValue = xLinearScale.range()[0];
47
46
  Object.entries(groupedData).forEach(([yValue, val]) => {
@@ -106,7 +105,7 @@ export const prepareBarYData = async (args) => {
106
105
  });
107
106
  });
108
107
  let labels = [];
109
- const htmlElements = [];
108
+ let htmlElements = [];
110
109
  const map = new Map();
111
110
  for (let i = 0; i < result.length; i++) {
112
111
  const prepared = result[i];
@@ -114,9 +113,6 @@ export const prepareBarYData = async (args) => {
114
113
  if (dataLabels.enabled) {
115
114
  const data = prepared.data;
116
115
  const content = getFormattedValue(Object.assign({ value: data.label || data.x }, dataLabels));
117
- const x = dataLabels.inside
118
- ? prepared.x + prepared.width / 2
119
- : prepared.x + prepared.width + DEFAULT_LABEL_PADDING;
120
116
  const y = prepared.y + prepared.height / 2;
121
117
  if (dataLabels.html) {
122
118
  const { maxHeight: height, maxWidth: width } = await getLabelsSize({
@@ -124,12 +120,23 @@ export const prepareBarYData = async (args) => {
124
120
  style: dataLabels.style,
125
121
  html: dataLabels.html,
126
122
  });
127
- htmlElements.push({
123
+ const x = dataLabels.inside
124
+ ? prepared.x + prepared.width / 2 - width / 2
125
+ : prepared.x + prepared.width + DEFAULT_LABEL_PADDING;
126
+ const constrainedPosition = getHtmlLabelConstraintedPosition({
127
+ boundsHeight,
128
+ boundsWidth,
129
+ height,
130
+ width,
128
131
  x,
129
132
  y: y - height / 2,
133
+ });
134
+ htmlElements.push({
130
135
  content,
131
136
  size: { width, height },
132
137
  style: dataLabels.style,
138
+ x: constrainedPosition.x,
139
+ y: constrainedPosition.y,
133
140
  });
134
141
  }
135
142
  else {
@@ -138,24 +145,39 @@ export const prepareBarYData = async (args) => {
138
145
  }
139
146
  const getTextSize = map.get(dataLabels.style);
140
147
  const { width, height } = await getTextSize(content);
141
- labels.push({
148
+ const x = dataLabels.inside
149
+ ? prepared.x + prepared.width / 2 - width / 2
150
+ : prepared.x + prepared.width + DEFAULT_LABEL_PADDING;
151
+ const constrainedPosition = getSvgLabelConstraintedPosition({
152
+ boundsHeight,
153
+ boundsWidth,
154
+ height,
155
+ width,
142
156
  x,
143
157
  y: y + height / 2,
144
- text: content,
145
- textAnchor: dataLabels.inside ? 'middle' : 'right',
146
- style: dataLabels.style,
147
- series: prepared.series,
158
+ });
159
+ labels.push({
148
160
  size: { width, height },
161
+ series: prepared.series,
162
+ style: dataLabels.style,
163
+ text: content,
164
+ textAnchor: 'start',
165
+ x: constrainedPosition.x,
166
+ y: constrainedPosition.y,
149
167
  });
150
168
  }
151
169
  }
152
170
  }
153
- if (!((_a = result[0]) === null || _a === void 0 ? void 0 : _a.series.dataLabels.allowOverlap)) {
171
+ const allowOverlap = (_a = result[0]) === null || _a === void 0 ? void 0 : _a.series.dataLabels.allowOverlap;
172
+ if (labels.length && !allowOverlap) {
154
173
  labels = filterOverlappingLabels(labels);
155
174
  }
175
+ else if (htmlElements.length && !allowOverlap) {
176
+ htmlElements = filterOverlappingLabels(htmlElements);
177
+ }
156
178
  return {
157
179
  shapes: result,
158
180
  labels,
159
181
  htmlElements,
160
182
  };
161
- };
183
+ }
@@ -56,6 +56,8 @@ export const useShapes = (args) => {
56
56
  case 'bar-y': {
57
57
  if (xAxis && xScale && (yScale === null || yScale === void 0 ? void 0 : yScale.length)) {
58
58
  const preparedData = await prepareBarYData({
59
+ boundsHeight,
60
+ boundsWidth,
59
61
  series: chartSeries,
60
62
  seriesOptions,
61
63
  xAxis,