@gravity-ui/chartkit 5.6.0 → 5.8.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 (50) hide show
  1. package/build/plugins/d3/examples/area/NegativeValues.d.ts +2 -0
  2. package/build/plugins/d3/examples/area/NegativeValues.js +24 -0
  3. package/build/plugins/d3/examples/bar-x/NegativeValues.d.ts +2 -0
  4. package/build/plugins/d3/examples/bar-x/NegativeValues.js +41 -0
  5. package/build/plugins/d3/examples/bar-y/NegativeValues.d.ts +2 -0
  6. package/build/plugins/d3/examples/bar-y/NegativeValues.js +40 -0
  7. package/build/plugins/d3/renderer/components/AxisX.d.ts +2 -1
  8. package/build/plugins/d3/renderer/components/AxisX.js +13 -3
  9. package/build/plugins/d3/renderer/components/AxisY.d.ts +4 -3
  10. package/build/plugins/d3/renderer/components/AxisY.js +17 -8
  11. package/build/plugins/d3/renderer/components/Chart.js +14 -3
  12. package/build/plugins/d3/renderer/components/PlotTitle.d.ts +7 -0
  13. package/build/plugins/d3/renderer/components/PlotTitle.js +12 -0
  14. package/build/plugins/d3/renderer/components/styles.css +7 -1
  15. package/build/plugins/d3/renderer/hooks/index.d.ts +1 -0
  16. package/build/plugins/d3/renderer/hooks/index.js +1 -0
  17. package/build/plugins/d3/renderer/hooks/useAxisScales/index.d.ts +3 -1
  18. package/build/plugins/d3/renderer/hooks/useAxisScales/index.js +30 -14
  19. package/build/plugins/d3/renderer/hooks/useChartDimensions/utils.js +13 -2
  20. package/build/plugins/d3/renderer/hooks/useChartOptions/types.d.ts +1 -0
  21. package/build/plugins/d3/renderer/hooks/useChartOptions/x-axis.d.ts +2 -2
  22. package/build/plugins/d3/renderer/hooks/useChartOptions/x-axis.js +14 -2
  23. package/build/plugins/d3/renderer/hooks/useChartOptions/y-axis.d.ts +2 -2
  24. package/build/plugins/d3/renderer/hooks/useChartOptions/y-axis.js +18 -8
  25. package/build/plugins/d3/renderer/hooks/useSeries/prepare-bar-y.js +2 -1
  26. package/build/plugins/d3/renderer/hooks/useShapes/area/prepare-data.js +15 -5
  27. package/build/plugins/d3/renderer/hooks/useShapes/bar-x/prepare-data.js +5 -3
  28. package/build/plugins/d3/renderer/hooks/useShapes/bar-y/prepare-data.js +5 -3
  29. package/build/plugins/d3/renderer/hooks/useShapes/index.d.ts +2 -1
  30. package/build/plugins/d3/renderer/hooks/useShapes/index.js +2 -1
  31. package/build/plugins/d3/renderer/hooks/useShapes/line/prepare-data.d.ts +6 -4
  32. package/build/plugins/d3/renderer/hooks/useShapes/line/prepare-data.js +6 -3
  33. package/build/plugins/d3/renderer/hooks/useSplit/index.d.ts +14 -0
  34. package/build/plugins/d3/renderer/hooks/useSplit/index.js +57 -0
  35. package/build/plugins/d3/renderer/hooks/useSplit/types.d.ts +17 -0
  36. package/build/plugins/d3/renderer/hooks/useSplit/types.js +1 -0
  37. package/build/plugins/d3/renderer/utils/axis-generators/bottom.d.ts +1 -1
  38. package/build/plugins/d3/renderer/utils/axis-generators/bottom.js +16 -8
  39. package/build/plugins/d3/renderer/utils/axis.d.ts +6 -2
  40. package/build/plugins/d3/renderer/utils/axis.js +7 -0
  41. package/build/plugins/d3/renderer/utils/index.d.ts +4 -1
  42. package/build/plugins/d3/renderer/utils/index.js +51 -25
  43. package/build/plugins/highcharts/renderer/helpers/config/config.js +2 -2
  44. package/build/types/widget-data/axis.d.ts +10 -0
  45. package/build/types/widget-data/bar-y.d.ts +2 -1
  46. package/build/types/widget-data/index.d.ts +8 -3
  47. package/build/types/widget-data/index.js +1 -0
  48. package/build/types/widget-data/split.d.ts +13 -0
  49. package/build/types/widget-data/split.js +1 -0
  50. package/package.json +1 -1
@@ -1,6 +1,6 @@
1
1
  import get from 'lodash/get';
2
2
  import { DEFAULT_AXIS_LABEL_FONT_SIZE, axisLabelsDefaults, yAxisTitleDefaults, } from '../../constants';
3
- import { formatAxisTickLabel, getClosestPointsRange, getHorisontalSvgTextHeight, getLabelsSize, getScaleTicks, getWaterfallPointSubtotal, } from '../../utils';
3
+ import { CHART_SERIES_WITH_VOLUME_ON_Y_AXIS, formatAxisTickLabel, getClosestPointsRange, getHorisontalSvgTextHeight, getLabelsSize, getScaleTicks, getWaterfallPointSubtotal, } from '../../utils';
4
4
  import { createYScale } from '../useAxisScales';
5
5
  const getAxisLabelMaxWidth = (args) => {
6
6
  const { axis, series } = args;
@@ -24,8 +24,8 @@ const getAxisLabelMaxWidth = (args) => {
24
24
  };
25
25
  function getAxisMin(axis, series) {
26
26
  const min = axis === null || axis === void 0 ? void 0 : axis.min;
27
- const seriesWithVolume = ['bar-x', 'area', 'waterfall'];
28
- if (typeof min === 'undefined' && (series === null || series === void 0 ? void 0 : series.some((s) => seriesWithVolume.includes(s.type)))) {
27
+ if (typeof min === 'undefined' &&
28
+ (series === null || series === void 0 ? void 0 : series.some((s) => CHART_SERIES_WITH_VOLUME_ON_Y_AXIS.includes(s.type)))) {
29
29
  return series.reduce((minValue, s) => {
30
30
  switch (s.type) {
31
31
  case 'waterfall': {
@@ -33,7 +33,8 @@ function getAxisMin(axis, series) {
33
33
  return Math.min(minValue, minSubTotal);
34
34
  }
35
35
  default: {
36
- return minValue;
36
+ const minYValue = s.data.reduce((res, d) => Math.min(res, get(d, 'y', 0)), 0);
37
+ return Math.min(minValue, minYValue);
37
38
  }
38
39
  }
39
40
  }, 0);
@@ -41,8 +42,16 @@ function getAxisMin(axis, series) {
41
42
  return min;
42
43
  }
43
44
  export const getPreparedYAxis = ({ series, yAxis, }) => {
44
- return (yAxis || [{}]).map((axisItem, index) => {
45
- const axisPosition = index === 0 ? 'left' : 'right';
45
+ const axisByPlot = [];
46
+ const axisItems = yAxis || [{}];
47
+ return axisItems.map((axisItem) => {
48
+ const plotIndex = get(axisItem, 'plotIndex', 0);
49
+ const firstPlotAxis = !axisByPlot[plotIndex];
50
+ if (firstPlotAxis) {
51
+ axisByPlot[plotIndex] = [];
52
+ }
53
+ axisByPlot[plotIndex].push(axisItem);
54
+ const defaultAxisPosition = firstPlotAxis ? 'left' : 'right';
46
55
  const labelsEnabled = get(axisItem, 'labels.enabled', true);
47
56
  const labelsStyle = {
48
57
  fontSize: get(axisItem, 'labels.style.fontSize', DEFAULT_AXIS_LABEL_FONT_SIZE),
@@ -85,12 +94,13 @@ export const getPreparedYAxis = ({ series, yAxis, }) => {
85
94
  min: getAxisMin(axisItem, series),
86
95
  maxPadding: get(axisItem, 'maxPadding', 0.05),
87
96
  grid: {
88
- enabled: get(axisItem, 'grid.enabled', index === 0),
97
+ enabled: get(axisItem, 'grid.enabled', firstPlotAxis),
89
98
  },
90
99
  ticks: {
91
100
  pixelInterval: get(axisItem, 'ticks.pixelInterval'),
92
101
  },
93
- position: axisPosition,
102
+ position: get(axisItem, 'position', defaultAxisPosition),
103
+ plotIndex: get(axisItem, 'plotIndex', 0),
94
104
  };
95
105
  if (labelsEnabled) {
96
106
  preparedAxis.labels.width = getAxisLabelMaxWidth({ axis: preparedAxis, series });
@@ -13,9 +13,10 @@ function prepareDataLabels(series) {
13
13
  style,
14
14
  })
15
15
  : {};
16
+ const inside = series.stacking === 'percent' ? true : get(series, 'dataLabels.inside', false);
16
17
  return {
17
18
  enabled,
18
- inside: get(series, 'dataLabels.inside', false),
19
+ inside,
19
20
  style,
20
21
  maxHeight,
21
22
  maxWidth,
@@ -1,5 +1,5 @@
1
1
  import { group, sort } from 'd3';
2
- import { getLabelsSize, getLeftPosition } from '../../../utils';
2
+ import { getDataCategoryValue, getLabelsSize, getLeftPosition } from '../../../utils';
3
3
  import { getXValue, getYValue } from '../utils';
4
4
  function getLabelData(point, series, xMax) {
5
5
  const text = String(point.data.label || point.data.y);
@@ -28,9 +28,12 @@ function getLabelData(point, series, xMax) {
28
28
  return labelData;
29
29
  }
30
30
  function getXValues(series, xAxis, xScale) {
31
+ const categories = xAxis.categories || [];
31
32
  const xValues = series.reduce((acc, s) => {
32
33
  s.data.forEach((d) => {
33
- const key = String(d.x);
34
+ const key = String(xAxis.type === 'category'
35
+ ? getDataCategoryValue({ axisDirection: 'x', categories, data: d })
36
+ : d.x);
34
37
  if (!acc.has(key)) {
35
38
  acc.set(key, getXValue({ point: d, xAxis, xScale }));
36
39
  }
@@ -38,7 +41,7 @@ function getXValues(series, xAxis, xScale) {
38
41
  return acc;
39
42
  }, new Map());
40
43
  if (xAxis.type === 'category') {
41
- return (xAxis.categories || []).reduce((acc, category) => {
44
+ return categories.reduce((acc, category) => {
42
45
  const xValue = xValues.get(category);
43
46
  if (typeof xValue === 'number') {
44
47
  acc.push([category, xValue]);
@@ -62,9 +65,16 @@ export const prepareAreaData = (args) => {
62
65
  const yAxisIndex = s.yAxis;
63
66
  const seriesYAxis = yAxis[yAxisIndex];
64
67
  const seriesYScale = yScale[yAxisIndex];
65
- const [yMin, _yMax] = seriesYScale.range();
68
+ const yMin = getYValue({ point: { y: 0 }, yAxis: seriesYAxis, yScale: seriesYScale });
66
69
  const seriesData = s.data.reduce((m, d) => {
67
- return m.set(String(d.x), d);
70
+ const key = String(xAxis.type === 'category'
71
+ ? getDataCategoryValue({
72
+ axisDirection: 'x',
73
+ categories: xAxis.categories || [],
74
+ data: d,
75
+ })
76
+ : d.x);
77
+ return m.set(key, d);
68
78
  }, new Map());
69
79
  const points = xValues.reduce((pointsAcc, [x, xValue]) => {
70
80
  const accumulatedYValue = accumulatedYValues.get(x) || 0;
@@ -110,11 +110,13 @@ export const prepareBarXData = (args) => {
110
110
  xCenter = xLinearScale(Number(xValue));
111
111
  }
112
112
  const x = xCenter - currentGroupWidth / 2 + (rectWidth + rectGap) * groupItemIndex;
113
- const y = seriesYScale(yValue.data.y);
114
- const height = plotHeight - y;
113
+ const yDataValue = yValue.data.y;
114
+ const y = seriesYScale(yDataValue);
115
+ const base = seriesYScale(0);
116
+ const height = yDataValue > 0 ? base - y : y - base;
115
117
  const barData = {
116
118
  x,
117
- y: y - stackHeight,
119
+ y: yDataValue > 0 ? y - stackHeight : seriesYScale(0),
118
120
  width: rectWidth,
119
121
  height,
120
122
  opacity: get(yValue.data, 'opacity', null),
@@ -81,7 +81,8 @@ export const prepareBarYData = (args) => {
81
81
  const stacks = Object.values(val);
82
82
  const currentBarHeight = barHeight * stacks.length + rectGap * (stacks.length - 1);
83
83
  stacks.forEach((measureValues, groupItemIndex) => {
84
- let stackSum = 0;
84
+ const base = xLinearScale(0);
85
+ let stackSum = base;
85
86
  const stackItems = [];
86
87
  const sortedData = sortKey
87
88
  ? sort(measureValues, (a, b) => comparator(get(a, sortKey), get(b, sortKey)))
@@ -97,9 +98,10 @@ export const prepareBarYData = (args) => {
97
98
  center = scale(Number(yValue));
98
99
  }
99
100
  const y = center - currentBarHeight / 2 + (barHeight + rectGap) * groupItemIndex;
100
- const width = xLinearScale(data.x);
101
+ const xValue = Number(data.x);
102
+ const width = xValue > 0 ? xLinearScale(xValue) - base : base - xLinearScale(xValue);
101
103
  stackItems.push({
102
- x: stackSum,
104
+ x: xValue > 0 ? stackSum : stackSum - width,
103
105
  y,
104
106
  width,
105
107
  height: barHeight,
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import { Dispatch } from 'd3';
3
- import type { PreparedSeries, PreparedSeriesOptions } from '../';
3
+ import type { PreparedSeries, PreparedSeriesOptions, PreparedSplit } from '../';
4
4
  import type { ChartScale } from '../useAxisScales';
5
5
  import type { PreparedAxis } from '../useChartOptions/types';
6
6
  import type { PreparedAreaData } from './area/types';
@@ -24,6 +24,7 @@ type Args = {
24
24
  yAxis: PreparedAxis[];
25
25
  xScale?: ChartScale;
26
26
  yScale?: ChartScale[];
27
+ split: PreparedSplit;
27
28
  };
28
29
  export declare const useShapes: (args: Args) => {
29
30
  shapes: React.ReactElement<any, string | React.JSXElementConstructor<any>>[];
@@ -15,7 +15,7 @@ import { prepareTreemapData } from './treemap/prepare-data';
15
15
  import { WaterfallSeriesShapes, prepareWaterfallData } from './waterfall';
16
16
  import './styles.css';
17
17
  export const useShapes = (args) => {
18
- const { boundsWidth, boundsHeight, dispatcher, series, seriesOptions, xAxis, xScale, yAxis, yScale, } = args;
18
+ const { boundsWidth, boundsHeight, dispatcher, series, seriesOptions, xAxis, xScale, yAxis, yScale, split, } = args;
19
19
  const shapesComponents = React.useMemo(() => {
20
20
  const visibleSeries = getOnlyVisibleSeries(series);
21
21
  const groupedSeries = group(visibleSeries, (item) => item.type);
@@ -77,6 +77,7 @@ export const useShapes = (args) => {
77
77
  xScale,
78
78
  yAxis,
79
79
  yScale,
80
+ split,
80
81
  });
81
82
  acc.push(React.createElement(LineSeriesShapes, { key: "line", dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData }));
82
83
  shapesData.push(...preparedData);
@@ -1,11 +1,13 @@
1
- import { ChartScale } from '../../useAxisScales';
2
- import { PreparedAxis } from '../../useChartOptions/types';
3
- import { PreparedLineSeries } from '../../useSeries/types';
4
- import { PreparedLineData } from './types';
1
+ import type { ChartScale } from '../../useAxisScales';
2
+ import type { PreparedAxis } from '../../useChartOptions/types';
3
+ import type { PreparedLineSeries } from '../../useSeries/types';
4
+ import type { PreparedSplit } from '../../useSplit/types';
5
+ import type { PreparedLineData } from './types';
5
6
  export declare const prepareLineData: (args: {
6
7
  series: PreparedLineSeries[];
7
8
  xAxis: PreparedAxis;
8
9
  xScale: ChartScale;
9
10
  yAxis: PreparedAxis[];
10
11
  yScale: ChartScale[];
12
+ split: PreparedSplit;
11
13
  }) => PreparedLineData[];
@@ -27,15 +27,18 @@ function getLabelData(point, series, xMax) {
27
27
  return labelData;
28
28
  }
29
29
  export const prepareLineData = (args) => {
30
- const { series, xAxis, xScale, yScale } = args;
31
- const yAxis = args.yAxis[0];
30
+ const { series, xAxis, yAxis, xScale, yScale, split } = args;
32
31
  const [_xMin, xRangeMax] = xScale.range();
33
32
  const xMax = xRangeMax / (1 - xAxis.maxPadding);
34
33
  return series.reduce((acc, s) => {
34
+ var _a;
35
+ const yAxisIndex = s.yAxis;
36
+ const seriesYAxis = yAxis[yAxisIndex];
37
+ const yAxisTop = ((_a = split.plots[seriesYAxis.plotIndex]) === null || _a === void 0 ? void 0 : _a.top) || 0;
35
38
  const seriesYScale = yScale[s.yAxis];
36
39
  const points = s.data.map((d) => ({
37
40
  x: getXValue({ point: d, xAxis, xScale }),
38
- y: getYValue({ point: d, yAxis, yScale: seriesYScale }),
41
+ y: yAxisTop + getYValue({ point: d, yAxis: seriesYAxis, yScale: seriesYScale }),
39
42
  active: true,
40
43
  data: d,
41
44
  series: s,
@@ -0,0 +1,14 @@
1
+ import type { ChartKitWidgetSplit } from '../../../../../types';
2
+ import type { PreparedSplit } from './types';
3
+ type UseSplitArgs = {
4
+ split?: ChartKitWidgetSplit;
5
+ boundsHeight: number;
6
+ chartWidth: number;
7
+ };
8
+ export declare function getPlotHeight(args: {
9
+ split: ChartKitWidgetSplit | undefined;
10
+ boundsHeight: number;
11
+ gap: number;
12
+ }): number;
13
+ export declare const useSplit: (args: UseSplitArgs) => PreparedSplit;
14
+ export {};
@@ -0,0 +1,57 @@
1
+ import get from 'lodash/get';
2
+ import { calculateNumericProperty, getHorisontalSvgTextHeight } from '../../utils';
3
+ const DEFAULT_TITLE_FONT_SIZE = '15px';
4
+ const TITLE_TOP_BOTTOM_PADDING = 8;
5
+ function preparePlotTitle(args) {
6
+ const { title, plotIndex, plotHeight, chartWidth, gap } = args;
7
+ const titleText = (title === null || title === void 0 ? void 0 : title.text) || '';
8
+ const titleStyle = {
9
+ fontSize: get(title, 'style.fontSize', DEFAULT_TITLE_FONT_SIZE),
10
+ fontWeight: get(title, 'style.fontWeight'),
11
+ };
12
+ const titleHeight = titleText
13
+ ? getHorisontalSvgTextHeight({ text: titleText, style: titleStyle }) +
14
+ TITLE_TOP_BOTTOM_PADDING * 2
15
+ : 0;
16
+ const top = plotIndex * (plotHeight + gap);
17
+ return {
18
+ text: titleText,
19
+ x: chartWidth / 2,
20
+ y: top + titleHeight / 2,
21
+ style: titleStyle,
22
+ height: titleHeight,
23
+ };
24
+ }
25
+ export function getPlotHeight(args) {
26
+ const { split, boundsHeight, gap } = args;
27
+ const plots = (split === null || split === void 0 ? void 0 : split.plots) || [];
28
+ if (plots.length > 1) {
29
+ return (boundsHeight - gap * (plots.length - 1)) / plots.length;
30
+ }
31
+ return boundsHeight;
32
+ }
33
+ export const useSplit = (args) => {
34
+ var _a;
35
+ const { split, boundsHeight, chartWidth } = args;
36
+ const splitGap = (_a = calculateNumericProperty({ value: split === null || split === void 0 ? void 0 : split.gap, base: boundsHeight })) !== null && _a !== void 0 ? _a : 0;
37
+ const plotHeight = getPlotHeight({ split: split, boundsHeight, gap: splitGap });
38
+ const plots = (split === null || split === void 0 ? void 0 : split.plots) || [];
39
+ return {
40
+ plots: plots.map((p, index) => {
41
+ const title = preparePlotTitle({
42
+ title: p.title,
43
+ plotIndex: index,
44
+ gap: splitGap,
45
+ plotHeight,
46
+ chartWidth,
47
+ });
48
+ const top = index * (plotHeight + splitGap);
49
+ return {
50
+ top: top + title.height,
51
+ height: plotHeight - title.height,
52
+ title,
53
+ };
54
+ }),
55
+ gap: splitGap,
56
+ };
57
+ };
@@ -0,0 +1,17 @@
1
+ import type { BaseTextStyle } from '../../../../../types';
2
+ export type PreparedSplit = {
3
+ plots: PreparedPlot[];
4
+ gap: number;
5
+ };
6
+ export type PreparedPlot = {
7
+ title: PreparedPlotTitle;
8
+ top: number;
9
+ height: number;
10
+ };
11
+ export type PreparedPlotTitle = {
12
+ x: number;
13
+ y: number;
14
+ text: string;
15
+ style?: Partial<BaseTextStyle>;
16
+ height: number;
17
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -11,7 +11,7 @@ type AxisBottomArgs = {
11
11
  labelsStyle?: BaseTextStyle;
12
12
  labelsMaxWidth?: number;
13
13
  labelsLineHeight: number;
14
- size: number;
14
+ items: [number, number][];
15
15
  rotation: number;
16
16
  };
17
17
  domain: {
@@ -1,20 +1,22 @@
1
- import { select } from 'd3';
1
+ import { path, select } from 'd3';
2
2
  import { getXAxisItems, getXAxisOffset, getXTickPosition } from '../axis';
3
3
  import { calculateCos, calculateSin } from '../math';
4
4
  import { getLabelsSize, setEllipsisForOverflowText } from '../text';
5
5
  function addDomain(selection, options) {
6
6
  const { size, color } = options;
7
- selection
7
+ const domainPath = selection
8
8
  .selectAll('.domain')
9
9
  .data([null])
10
10
  .enter()
11
11
  .insert('path', '.tick')
12
12
  .attr('class', 'domain')
13
- .attr('stroke', color || 'currentColor')
14
13
  .attr('d', `M0,0V0H${size}`);
14
+ if (color) {
15
+ domainPath.style('stroke', color);
16
+ }
15
17
  }
16
18
  export function axisBottom(args) {
17
- const { scale, ticks: { labelFormat, labelsPaddings = 0, labelsMargin = 0, labelsMaxWidth = Infinity, labelsStyle, labelsLineHeight, size: tickSize, count: ticksCount, maxTickCount, rotation, }, domain: { size: domainSize, color: domainColor }, } = args;
19
+ const { scale, ticks: { labelFormat, labelsPaddings = 0, labelsMargin = 0, labelsMaxWidth = Infinity, labelsStyle, labelsLineHeight, items: tickItems, count: ticksCount, maxTickCount, rotation, }, domain: { size: domainSize, color: domainColor }, } = args;
18
20
  const offset = getXAxisOffset();
19
21
  const position = getXTickPosition({ scale, offset });
20
22
  const values = getXAxisItems({ scale, count: ticksCount, maxCount: maxTickCount });
@@ -26,22 +28,28 @@ export function axisBottom(args) {
26
28
  var _a, _b;
27
29
  const x = ((_b = (_a = selection.node()) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect()) === null || _b === void 0 ? void 0 : _b.x) || 0;
28
30
  const right = x + domainSize;
29
- let transform = `translate(0, ${labelHeight + labelsMargin}px)`;
31
+ const top = -tickItems[0][0] || 0;
32
+ let transform = `translate(0, ${labelHeight + labelsMargin - top}px)`;
30
33
  if (rotation) {
31
- const labelsOffsetTop = labelHeight * calculateCos(rotation) + labelsMargin;
34
+ const labelsOffsetTop = labelHeight * calculateCos(rotation) + labelsMargin - top;
32
35
  let labelsOffsetLeft = calculateSin(rotation) * labelHeight;
33
36
  if (Math.abs(rotation) % 360 === 90) {
34
37
  labelsOffsetLeft += ((rotation > 0 ? -1 : 1) * labelHeight) / 2;
35
38
  }
36
39
  transform = `translate(${-labelsOffsetLeft}px, ${labelsOffsetTop}px) rotate(${rotation}deg)`;
37
40
  }
41
+ const tickPath = path();
42
+ tickItems.forEach(([start, end]) => {
43
+ tickPath.moveTo(0, start);
44
+ tickPath.lineTo(0, end);
45
+ });
38
46
  selection
39
47
  .selectAll('.tick')
40
48
  .data(values)
41
49
  .order()
42
50
  .join((el) => {
43
51
  const tick = el.append('g').attr('class', 'tick');
44
- tick.append('line').attr('stroke', 'currentColor').attr('y2', tickSize);
52
+ tick.append('path').attr('d', tickPath.toString()).attr('stroke', 'currentColor');
45
53
  tick.append('text')
46
54
  .text(labelFormat)
47
55
  .attr('fill', 'currentColor')
@@ -56,7 +64,7 @@ export function axisBottom(args) {
56
64
  return tick;
57
65
  })
58
66
  .attr('transform', function (d) {
59
- return `translate(${position(d) + offset},0)`;
67
+ return `translate(${position(d) + offset}, ${top})`;
60
68
  });
61
69
  // Remove tick that has the same x coordinate like domain
62
70
  selection
@@ -1,5 +1,5 @@
1
- import { AxisDomain, AxisScale, ScaleBand } from 'd3';
2
- import { PreparedAxis } from '../hooks';
1
+ import type { AxisDomain, AxisScale, ScaleBand } from 'd3';
2
+ import type { PreparedAxis, PreparedSplit } from '../hooks';
3
3
  export declare function getTicksCount({ axis, range }: {
4
4
  axis: PreparedAxis;
5
5
  range: number;
@@ -20,3 +20,7 @@ export declare function getMaxTickCount({ axis, width }: {
20
20
  axis: PreparedAxis;
21
21
  width: number;
22
22
  }): number;
23
+ export declare function getAxisHeight(args: {
24
+ split: PreparedSplit;
25
+ boundsHeight: number;
26
+ }): number;
@@ -41,3 +41,10 @@ export function getMaxTickCount({ axis, width }) {
41
41
  const minTickWidth = parseInt(axis.labels.style.fontSize) + axis.labels.padding;
42
42
  return Math.floor(width / minTickWidth);
43
43
  }
44
+ export function getAxisHeight(args) {
45
+ const { split, boundsHeight } = args;
46
+ if (split.plots.length > 1) {
47
+ return split.plots[0].height;
48
+ }
49
+ return boundsHeight;
50
+ }
@@ -8,6 +8,8 @@ export * from './axis';
8
8
  export * from './labels';
9
9
  export * from './symbol';
10
10
  export * from './series';
11
+ export declare const CHART_SERIES_WITH_VOLUME_ON_Y_AXIS: ChartKitWidgetSeries['type'][];
12
+ export declare const CHART_SERIES_WITH_VOLUME_ON_X_AXIS: ChartKitWidgetSeries['type'][];
11
13
  export type AxisDirection = 'x' | 'y';
12
14
  type UnknownSeries = {
13
15
  type: ChartKitWidgetSeries['type'];
@@ -38,7 +40,8 @@ export declare function isSeriesWithCategoryValues(series: UnknownSeries): serie
38
40
  category: string;
39
41
  }[];
40
42
  };
41
- export declare const getDomainDataXBySeries: (series: UnknownSeries[]) => number[];
43
+ export declare const getDomainDataXBySeries: (series: UnknownSeries[]) => unknown[];
44
+ export declare function getDefaultMaxXAxisValue(series: UnknownSeries[]): 0 | undefined;
42
45
  export declare const getDomainDataYBySeries: (series: UnknownSeries[]) => unknown[];
43
46
  export declare const getSeriesNames: (series: ChartKitWidgetSeries[]) => string[];
44
47
  export declare const getOnlyVisibleSeries: <T extends {
@@ -15,6 +15,12 @@ export * from './labels';
15
15
  export * from './symbol';
16
16
  export * from './series';
17
17
  const CHARTS_WITHOUT_AXIS = ['pie', 'treemap'];
18
+ export const CHART_SERIES_WITH_VOLUME_ON_Y_AXIS = [
19
+ 'bar-x',
20
+ 'area',
21
+ 'waterfall',
22
+ ];
23
+ export const CHART_SERIES_WITH_VOLUME_ON_X_AXIS = ['bar-y'];
18
24
  /**
19
25
  * Checks whether the series should be drawn with axes.
20
26
  *
@@ -33,42 +39,62 @@ export function isSeriesWithNumericalYValues(series) {
33
39
  export function isSeriesWithCategoryValues(series) {
34
40
  return isAxisRelatedSeries(series);
35
41
  }
42
+ function getDomainDataForStackedSeries(seriesList, keyAttr = 'x', valueAttr = 'y') {
43
+ const acc = [];
44
+ const stackedSeries = group(seriesList, getSeriesStackId);
45
+ Array.from(stackedSeries).forEach(([_stackId, seriesStack]) => {
46
+ const values = {};
47
+ seriesStack.forEach((singleSeries) => {
48
+ const data = new Map();
49
+ singleSeries.data.forEach((point) => {
50
+ const key = String(point[keyAttr]);
51
+ let value = 0;
52
+ if (valueAttr in point && typeof point[valueAttr] === 'number') {
53
+ value = point[valueAttr];
54
+ }
55
+ if (data.has(key)) {
56
+ value = Math.max(value, data.get(key));
57
+ }
58
+ data.set(key, value);
59
+ });
60
+ Array.from(data).forEach(([key, value]) => {
61
+ values[key] = (values[key] || 0) + value;
62
+ });
63
+ });
64
+ acc.push(...Object.values(values));
65
+ });
66
+ return acc;
67
+ }
36
68
  export const getDomainDataXBySeries = (series) => {
37
- return series.reduce((acc, s) => {
38
- if (isSeriesWithNumericalXValues(s)) {
39
- acc.push(...s.data.map((d) => d.x));
69
+ const groupedSeries = group(series, (item) => item.type);
70
+ return Array.from(groupedSeries).reduce((acc, [type, seriesList]) => {
71
+ switch (type) {
72
+ case 'bar-y': {
73
+ acc.push(...getDomainDataForStackedSeries(seriesList, 'y', 'x'));
74
+ break;
75
+ }
76
+ default: {
77
+ seriesList.filter(isSeriesWithNumericalXValues).forEach((s) => {
78
+ acc.push(...s.data.map((d) => d.x));
79
+ });
80
+ }
40
81
  }
41
82
  return acc;
42
83
  }, []);
43
84
  };
85
+ export function getDefaultMaxXAxisValue(series) {
86
+ if (series.some((s) => s.type === 'bar-y')) {
87
+ return 0;
88
+ }
89
+ return undefined;
90
+ }
44
91
  export const getDomainDataYBySeries = (series) => {
45
92
  const groupedSeries = group(series, (item) => item.type);
46
93
  return Array.from(groupedSeries).reduce((acc, [type, seriesList]) => {
47
94
  switch (type) {
48
95
  case 'area':
49
96
  case 'bar-x': {
50
- const stackedSeries = group(seriesList, getSeriesStackId);
51
- Array.from(stackedSeries).forEach(([_stackId, seriesStack]) => {
52
- const values = {};
53
- seriesStack.forEach((singleSeries) => {
54
- const data = new Map();
55
- singleSeries.data.forEach((point) => {
56
- const key = String(point.x);
57
- let value = 0;
58
- if (point.y && typeof point.y === 'number') {
59
- value = point.y;
60
- }
61
- if (data.has(key)) {
62
- value = Math.max(value, data.get(key));
63
- }
64
- data.set(key, value);
65
- });
66
- Array.from(data).forEach(([key, value]) => {
67
- values[key] = (values[key] || 0) + value;
68
- });
69
- });
70
- acc.push(...Object.values(values));
71
- });
97
+ acc.push(...getDomainDataForStackedSeries(seriesList));
72
98
  break;
73
99
  }
74
100
  case 'waterfall': {
@@ -1328,8 +1328,8 @@ export function prepareConfig(data, options, isMobile, holidays) {
1328
1328
  // Callback setExtremes used because of it obligatory invocation on every zoom event
1329
1329
  // setTimeout used because of absence resetZoomButton node in dom on first zoom event
1330
1330
  setTimeout(() => {
1331
- var _a;
1332
- const text = (_a = this.chart.resetZoomButton) === null || _a === void 0 ? void 0 : _a.text;
1331
+ var _a, _b;
1332
+ const text = (_b = (_a = this.chart) === null || _a === void 0 ? void 0 : _a.resetZoomButton) === null || _b === void 0 ? void 0 : _b.text;
1333
1333
  if (text) {
1334
1334
  text.translate(0, -6);
1335
1335
  }
@@ -65,3 +65,13 @@ export type ChartKitWidgetAxis = {
65
65
  * */
66
66
  maxPadding?: number;
67
67
  };
68
+ export type ChartKitWidgetXAxis = ChartKitWidgetAxis;
69
+ export type ChartKitWidgetYAxis = ChartKitWidgetAxis & {
70
+ /** Axis location.
71
+ * Possible values - 'left' and 'right'.
72
+ * */
73
+ position?: 'left' | 'right';
74
+ /** Property for splitting charts. Determines which area the axis is located in.
75
+ * */
76
+ plotIndex?: number;
77
+ };
@@ -45,7 +45,8 @@ export type BarYSeries<T = any> = BaseSeries & {
45
45
  grouping?: boolean;
46
46
  dataLabels?: ChartKitWidgetSeriesOptions['dataLabels'] & {
47
47
  /**
48
- * Whether to align the data label inside or outside the box
48
+ * Whether to align the data label inside or outside the box.
49
+ * For charts with a percentage stack, it is always true.
49
50
  *
50
51
  * @default false
51
52
  * */
@@ -1,7 +1,8 @@
1
- import type { ChartKitWidgetAxis } from './axis';
1
+ import type { ChartKitWidgetXAxis, ChartKitWidgetYAxis } from './axis';
2
2
  import type { ChartKitWidgetChart } from './chart';
3
3
  import type { ChartKitWidgetLegend } from './legend';
4
4
  import type { ChartKitWidgetSeries, ChartKitWidgetSeriesOptions } from './series';
5
+ import type { ChartKitWidgetSplit } from './split';
5
6
  import type { ChartKitWidgetTitle } from './title';
6
7
  import type { ChartKitWidgetTooltip } from './tooltip';
7
8
  export * from './axis';
@@ -15,6 +16,7 @@ export * from './bar-y';
15
16
  export * from './area';
16
17
  export * from './line';
17
18
  export * from './series';
19
+ export * from './split';
18
20
  export * from './title';
19
21
  export * from './tooltip';
20
22
  export * from './halo';
@@ -29,6 +31,9 @@ export type ChartKitWidgetData<T = any> = {
29
31
  };
30
32
  title?: ChartKitWidgetTitle;
31
33
  tooltip?: ChartKitWidgetTooltip<T>;
32
- xAxis?: ChartKitWidgetAxis;
33
- yAxis?: ChartKitWidgetAxis[];
34
+ xAxis?: ChartKitWidgetXAxis;
35
+ yAxis?: ChartKitWidgetYAxis[];
36
+ /** Setting for displaying charts on different plots.
37
+ * It can be used to visualize related information on multiple charts. */
38
+ split?: ChartKitWidgetSplit;
34
39
  };
@@ -9,6 +9,7 @@ export * from './bar-y';
9
9
  export * from './area';
10
10
  export * from './line';
11
11
  export * from './series';
12
+ export * from './split';
12
13
  export * from './title';
13
14
  export * from './tooltip';
14
15
  export * from './halo';