@gravity-ui/chartkit 4.0.0 → 4.2.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 (24) hide show
  1. package/README.md +2 -1
  2. package/build/plugins/d3/renderer/D3Widget.js +14 -5
  3. package/build/plugins/d3/renderer/components/Chart.js +1 -7
  4. package/build/plugins/d3/renderer/components/Tooltip/DefaultContent.d.ts +1 -1
  5. package/build/plugins/d3/renderer/components/Tooltip/DefaultContent.js +28 -6
  6. package/build/plugins/d3/renderer/hooks/useAxisScales/index.js +18 -5
  7. package/build/plugins/d3/renderer/hooks/useChartDimensions/index.d.ts +1 -5
  8. package/build/plugins/d3/renderer/hooks/useChartDimensions/index.js +3 -5
  9. package/build/plugins/d3/renderer/hooks/useChartOptions/chart.d.ts +3 -1
  10. package/build/plugins/d3/renderer/hooks/useChartOptions/chart.js +47 -10
  11. package/build/plugins/d3/renderer/hooks/useChartOptions/index.js +2 -0
  12. package/build/plugins/d3/renderer/hooks/useSeries/prepareSeries.js +19 -15
  13. package/build/plugins/d3/renderer/hooks/useShapes/bar-x.d.ts +4 -4
  14. package/build/plugins/d3/renderer/hooks/useShapes/bar-x.js +126 -111
  15. package/build/plugins/d3/renderer/hooks/useShapes/index.js +11 -15
  16. package/build/plugins/d3/renderer/hooks/useShapes/scatter.d.ts +9 -9
  17. package/build/plugins/d3/renderer/hooks/useShapes/scatter.js +66 -39
  18. package/build/plugins/d3/renderer/utils/index.d.ts +7 -1
  19. package/build/plugins/d3/renderer/utils/index.js +28 -1
  20. package/build/types/widget-data/bar-x.d.ts +19 -5
  21. package/build/types/widget-data/base.d.ts +2 -0
  22. package/build/types/widget-data/pie.d.ts +0 -2
  23. package/build/types/widget-data/scatter.d.ts +19 -5
  24. package/package.json +2 -2
package/README.md CHANGED
@@ -19,7 +19,8 @@ import '@gravity-ui/uikit/styles/styles.scss';
19
19
  ```typescript
20
20
  import {ThemeProvider} from '@gravity-ui/uikit';
21
21
  import ChartKit, {settings} from '@gravity-ui/chartkit';
22
- import {YagrPlugin, YagrWidgetData} from '@gravity-ui/chartkit/build/plugins';
22
+ import {YagrPlugin} from '@gravity-ui/chartkit/yagr';
23
+ import type {YagrWidgetData} from '@gravity-ui/chartkit/yagr';
23
24
 
24
25
  import '@gravity-ui/uikit/styles/styles.scss';
25
26
 
@@ -1,14 +1,17 @@
1
1
  import React from 'react';
2
2
  import { select } from 'd3';
3
3
  import debounce from 'lodash/debounce';
4
+ import { getRandomCKId } from '../../../utils';
4
5
  import { Chart } from './components';
5
6
  const D3Widget = React.forwardRef(function D3Widget(props, forwardedRef) {
6
7
  const ref = React.useRef(null);
7
8
  const debounced = React.useRef();
8
9
  const [dimensions, setDimensions] = React.useState();
9
10
  const handleResize = React.useCallback(() => {
10
- if (ref.current) {
11
- const { top, left, width, height } = ref.current.getBoundingClientRect();
11
+ var _a;
12
+ const parentElement = (_a = ref.current) === null || _a === void 0 ? void 0 : _a.parentElement;
13
+ if (parentElement) {
14
+ const { top, left, width, height } = parentElement.getBoundingClientRect();
12
15
  setDimensions({ top, left, width, height });
13
16
  }
14
17
  }, []);
@@ -25,16 +28,22 @@ const D3Widget = React.forwardRef(function D3Widget(props, forwardedRef) {
25
28
  }), [handleResize]);
26
29
  React.useEffect(() => {
27
30
  const selection = select(window);
28
- selection.on('resize', debuncedHandleResize);
31
+ // https://github.com/d3/d3-selection/blob/main/README.md#handling-events
32
+ const eventName = `resize.${getRandomCKId()}`;
33
+ selection.on(eventName, debuncedHandleResize);
29
34
  return () => {
30
35
  // https://d3js.org/d3-selection/events#selection_on
31
- selection.on('resize', null);
36
+ selection.on(eventName, null);
32
37
  };
33
38
  }, [debuncedHandleResize]);
34
39
  React.useEffect(() => {
35
40
  // dimensions initialize
36
41
  handleResize();
37
42
  }, [handleResize]);
38
- return (React.createElement("div", { ref: ref, style: { width: '100%', height: '100%', position: 'relative' } }, (dimensions === null || dimensions === void 0 ? void 0 : dimensions.width) && (dimensions === null || dimensions === void 0 ? void 0 : dimensions.height) && (React.createElement(Chart, { top: (dimensions === null || dimensions === void 0 ? void 0 : dimensions.top) || 0, left: dimensions.left || 0, width: dimensions.width, height: dimensions.height, data: props.data }))));
43
+ return (React.createElement("div", { ref: ref, style: {
44
+ width: (dimensions === null || dimensions === void 0 ? void 0 : dimensions.width) || '100%',
45
+ height: (dimensions === null || dimensions === void 0 ? void 0 : dimensions.height) || '100%',
46
+ position: 'relative',
47
+ } }, (dimensions === null || dimensions === void 0 ? void 0 : dimensions.width) && (dimensions === null || dimensions === void 0 ? void 0 : dimensions.height) && (React.createElement(Chart, { top: (dimensions === null || dimensions === void 0 ? void 0 : dimensions.top) || 0, left: dimensions.left || 0, width: dimensions.width, height: dimensions.height, data: props.data }))));
39
48
  });
40
49
  export default D3Widget;
@@ -18,9 +18,6 @@ export const Chart = (props) => {
18
18
  width,
19
19
  height,
20
20
  margin: chart.margin,
21
- legend,
22
- title,
23
- xAxis,
24
21
  yAxis,
25
22
  });
26
23
  const { preparedSeries, handleLegendItemClick } = useSeries({ series: data.series, legend });
@@ -51,10 +48,7 @@ export const Chart = (props) => {
51
48
  return (React.createElement(React.Fragment, null,
52
49
  React.createElement("svg", { ref: svgRef, className: b({ hovered: chartHovered }), width: width, height: height, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave },
53
50
  title && React.createElement(Title, Object.assign({}, title, { chartWidth: width })),
54
- React.createElement("g", { width: boundsWidth, height: boundsHeight, transform: `translate(${[
55
- chart.margin.left,
56
- chart.margin.top + ((title === null || title === void 0 ? void 0 : title.height) || 0),
57
- ].join(',')})` },
51
+ React.createElement("g", { width: boundsWidth, height: boundsHeight, transform: `translate(${[chart.margin.left, chart.margin.top].join(',')})` },
58
52
  xScale && yScale && (React.createElement(React.Fragment, null,
59
53
  React.createElement(AxisY, { axises: yAxis, width: boundsWidth, height: boundsHeight, scale: yScale }),
60
54
  React.createElement("g", { transform: `translate(0, ${boundsHeight})` },
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import type { TooltipHoveredData } from '../../../../../types/widget-data';
2
+ import type { TooltipHoveredData } from '../../../../../types';
3
3
  import type { PreparedAxis } from '../../hooks';
4
4
  type Props = {
5
5
  hovered: TooltipHoveredData;
@@ -1,11 +1,34 @@
1
1
  import React from 'react';
2
+ import get from 'lodash/get';
3
+ import { dateTime } from '@gravity-ui/date-utils';
4
+ import { formatNumber } from '../../../../shared';
5
+ import { getDataCategoryValue } from '../../utils';
6
+ const DEFAULT_DATE_FORMAT = 'DD.MM.YY';
7
+ const getRowData = (fieldName, axis, data) => {
8
+ const categories = get(axis, 'categories', []);
9
+ switch (axis.type) {
10
+ case 'category': {
11
+ return getDataCategoryValue({ axisDirection: fieldName, categories, data });
12
+ }
13
+ case 'datetime': {
14
+ const value = get(data, fieldName);
15
+ return dateTime({ input: value }).format(DEFAULT_DATE_FORMAT);
16
+ }
17
+ case 'linear':
18
+ default: {
19
+ const value = get(data, fieldName);
20
+ return formatNumber(value);
21
+ }
22
+ }
23
+ };
24
+ const getXRowData = (xAxis, data) => getRowData('x', xAxis, data);
25
+ const getYRowData = (yAxis, data) => getRowData('y', yAxis, data);
2
26
  export const DefaultContent = ({ hovered, xAxis, yAxis }) => {
3
27
  const { data, series } = hovered;
4
28
  switch (series.type) {
5
29
  case 'scatter': {
6
- const scatterData = data;
7
- const xRow = xAxis.type === 'category' ? scatterData.category : scatterData.x;
8
- const yRow = yAxis.type === 'category' ? scatterData.category : scatterData.y;
30
+ const xRow = getXRowData(xAxis, data);
31
+ const yRow = getYRowData(yAxis, data);
9
32
  return (React.createElement("div", null,
10
33
  React.createElement("div", null,
11
34
  React.createElement("span", null, "X:\u00A0"),
@@ -15,9 +38,8 @@ export const DefaultContent = ({ hovered, xAxis, yAxis }) => {
15
38
  React.createElement("b", null, yRow))));
16
39
  }
17
40
  case 'bar-x': {
18
- const barXData = data;
19
- const xRow = xAxis.type === 'category' ? barXData.category : barXData.x;
20
- const yRow = yAxis.type === 'category' ? barXData.category : barXData.y;
41
+ const xRow = getXRowData(xAxis, data);
42
+ const yRow = getYRowData(yAxis, data);
21
43
  return (React.createElement("div", null,
22
44
  React.createElement("div", null, xRow),
23
45
  React.createElement("div", null,
@@ -1,14 +1,19 @@
1
1
  import React from 'react';
2
2
  import { scaleBand, scaleLinear, scaleUtc, extent } from 'd3';
3
3
  import get from 'lodash/get';
4
- import { getOnlyVisibleSeries, getDomainDataYBySeries, isAxisRelatedSeries, getDomainDataXBySeries, isSeriesWithCategoryValues, } from '../../utils';
4
+ import { getOnlyVisibleSeries, getDataCategoryValue, getDomainDataYBySeries, getDomainDataXBySeries, isAxisRelatedSeries, isSeriesWithCategoryValues, } from '../../utils';
5
5
  const isNumericalArrayData = (data) => {
6
6
  return data.every((d) => typeof d === 'number' || d === null);
7
7
  };
8
- const filterCategoriesByVisibleSeries = (categories, series) => {
8
+ const filterCategoriesByVisibleSeries = (args) => {
9
+ const { axisDirection, categories, series } = args;
9
10
  return categories.filter((category) => {
10
11
  return series.some((s) => {
11
- return isSeriesWithCategoryValues(s) && s.data.some((d) => d.category === category);
12
+ return (isSeriesWithCategoryValues(s) &&
13
+ s.data.some((d) => {
14
+ const dataCategory = getDataCategoryValue({ axisDirection, categories, data: d });
15
+ return dataCategory === category;
16
+ }));
12
17
  });
13
18
  });
14
19
  };
@@ -41,7 +46,11 @@ const createScales = (args) => {
41
46
  }
42
47
  case 'category': {
43
48
  if (xCategories) {
44
- const filteredCategories = filterCategoriesByVisibleSeries(xCategories, visibleSeries);
49
+ const filteredCategories = filterCategoriesByVisibleSeries({
50
+ axisDirection: 'x',
51
+ categories: xCategories,
52
+ series: visibleSeries,
53
+ });
45
54
  xScale = scaleBand().domain(filteredCategories).range([0, boundsWidth]);
46
55
  }
47
56
  break;
@@ -78,7 +87,11 @@ const createScales = (args) => {
78
87
  }
79
88
  case 'category': {
80
89
  if (yCategories) {
81
- const filteredCategories = filterCategoriesByVisibleSeries(yCategories, visibleSeries);
90
+ const filteredCategories = filterCategoriesByVisibleSeries({
91
+ axisDirection: 'y',
92
+ categories: yCategories,
93
+ series: visibleSeries,
94
+ });
82
95
  yScale = scaleBand().domain(filteredCategories).range([boundsHeight, 0]);
83
96
  }
84
97
  break;
@@ -1,17 +1,13 @@
1
1
  import type { ChartMargin } from '../../../../../types/widget-data';
2
- import type { PreparedAxis, PreparedLegend, PreparedTitle } from '../useChartOptions/types';
2
+ import type { PreparedAxis } from '../useChartOptions/types';
3
3
  type Args = {
4
4
  width: number;
5
5
  height: number;
6
6
  margin: ChartMargin;
7
- legend: PreparedLegend;
8
- title?: PreparedTitle;
9
- xAxis?: PreparedAxis;
10
7
  yAxis?: PreparedAxis[];
11
8
  };
12
9
  export declare const useChartDimensions: (args: Args) => {
13
10
  boundsWidth: number;
14
11
  boundsHeight: number;
15
- legendHeight: number;
16
12
  };
17
13
  export {};
@@ -1,11 +1,9 @@
1
1
  export const useChartDimensions = (args) => {
2
- const { margin, legend, title, width, height, xAxis, yAxis } = args;
3
- const titleHeight = (title === null || title === void 0 ? void 0 : title.height) || 0;
4
- const xAxisTitleHeight = (xAxis === null || xAxis === void 0 ? void 0 : xAxis.title.height) || 0;
2
+ const { margin, width, height, yAxis } = args;
5
3
  const yAxisTitleHeight = (yAxis === null || yAxis === void 0 ? void 0 : yAxis.reduce((acc, axis) => {
6
4
  return acc + (axis.title.height || 0);
7
5
  }, 0)) || 0;
8
6
  const boundsWidth = width - margin.right - margin.left - yAxisTitleHeight;
9
- const boundsHeight = height - margin.top - margin.bottom - legend.height - titleHeight - xAxisTitleHeight;
10
- return { boundsWidth, boundsHeight, legendHeight: legend.height };
7
+ const boundsHeight = height - margin.top - margin.bottom;
8
+ return { boundsWidth, boundsHeight };
11
9
  };
@@ -1,8 +1,10 @@
1
1
  import type { ChartKitWidgetData } from '../../../../../types/widget-data';
2
- import type { PreparedAxis, PreparedChart } from './types';
2
+ import type { PreparedAxis, PreparedChart, PreparedTitle, PreparedLegend } from './types';
3
3
  export declare const getPreparedChart: (args: {
4
4
  chart: ChartKitWidgetData['chart'];
5
5
  series: ChartKitWidgetData['series'];
6
+ preparedLegend: PreparedLegend;
6
7
  preparedXAxis: PreparedAxis;
7
8
  preparedY1Axis: PreparedAxis;
9
+ preparedTitle?: PreparedTitle;
8
10
  }) => PreparedChart;
@@ -42,26 +42,63 @@ const getAxisLabelMaxWidth = (args) => {
42
42
  .remove();
43
43
  return width;
44
44
  };
45
- export const getPreparedChart = (args) => {
46
- const { chart, series, preparedXAxis, preparedY1Axis } = args;
47
- const hasAxisRelatedSeries = series.data.some(isAxisRelatedSeries);
48
- let marginBottom = get(chart, 'margin.bottom', 0);
49
- let marginLeft = get(chart, 'margin.left', 0);
45
+ const getMarginTop = (args) => {
46
+ const { chart, hasAxisRelatedSeries, preparedY1Axis, preparedTitle } = args;
50
47
  let marginTop = get(chart, 'margin.top', 0);
51
- let marginRight = get(chart, 'margin.right', 0);
48
+ if (hasAxisRelatedSeries) {
49
+ marginTop +=
50
+ getHorisontalSvgTextHeight({ text: 'Tmp', style: preparedY1Axis.labels.style }) / 2;
51
+ }
52
+ if (preparedTitle === null || preparedTitle === void 0 ? void 0 : preparedTitle.height) {
53
+ marginTop += preparedTitle.height;
54
+ }
55
+ return marginTop;
56
+ };
57
+ const getMarginBottom = (args) => {
58
+ const { chart, hasAxisRelatedSeries, preparedLegend, preparedXAxis } = args;
59
+ let marginBottom = get(chart, 'margin.bottom', 0) + preparedLegend.height;
52
60
  if (hasAxisRelatedSeries) {
53
61
  marginBottom +=
54
- preparedXAxis.labels.padding +
62
+ preparedXAxis.title.height +
55
63
  getHorisontalSvgTextHeight({ text: 'Tmp', style: preparedXAxis.labels.style });
64
+ if (preparedXAxis.labels.enabled) {
65
+ marginBottom += preparedXAxis.labels.padding;
66
+ }
67
+ }
68
+ return marginBottom;
69
+ };
70
+ const getMarginLeft = (args) => {
71
+ const { chart, hasAxisRelatedSeries, series, preparedY1Axis } = args;
72
+ let marginLeft = get(chart, 'margin.left', 0);
73
+ if (hasAxisRelatedSeries) {
56
74
  marginLeft +=
57
75
  AXIS_WIDTH +
58
76
  preparedY1Axis.labels.padding +
59
77
  getAxisLabelMaxWidth({ axis: preparedY1Axis, series: series.data }) +
60
- (preparedY1Axis.title.height || 0);
61
- marginTop +=
62
- getHorisontalSvgTextHeight({ text: 'Tmp', style: preparedY1Axis.labels.style }) / 2;
78
+ preparedY1Axis.title.height;
79
+ }
80
+ return marginLeft;
81
+ };
82
+ const getMarginRight = (args) => {
83
+ const { chart, hasAxisRelatedSeries, series, preparedXAxis } = args;
84
+ let marginRight = get(chart, 'margin.right', 0);
85
+ if (hasAxisRelatedSeries) {
63
86
  marginRight += getAxisLabelMaxWidth({ axis: preparedXAxis, series: series.data }) / 2;
64
87
  }
88
+ return marginRight;
89
+ };
90
+ export const getPreparedChart = (args) => {
91
+ const { chart, series, preparedLegend, preparedXAxis, preparedY1Axis, preparedTitle } = args;
92
+ const hasAxisRelatedSeries = series.data.some(isAxisRelatedSeries);
93
+ const marginTop = getMarginTop({ chart, hasAxisRelatedSeries, preparedY1Axis, preparedTitle });
94
+ const marginBottom = getMarginBottom({
95
+ chart,
96
+ hasAxisRelatedSeries,
97
+ preparedLegend,
98
+ preparedXAxis,
99
+ });
100
+ const marginLeft = getMarginLeft({ chart, hasAxisRelatedSeries, series, preparedY1Axis });
101
+ const marginRight = getMarginRight({ chart, hasAxisRelatedSeries, series, preparedXAxis });
65
102
  return {
66
103
  margin: {
67
104
  top: marginTop,
@@ -16,6 +16,8 @@ export const useChartOptions = (args) => {
16
16
  const preparedChart = getPreparedChart({
17
17
  chart,
18
18
  series,
19
+ preparedTitle,
20
+ preparedLegend,
19
21
  preparedXAxis,
20
22
  preparedY1Axis: preparedYAxis[0],
21
23
  });
@@ -39,30 +39,34 @@ function prepareAxisRelatedSeries(args) {
39
39
  return [preparedSeries];
40
40
  }
41
41
  function prepareBarXSeries(args) {
42
- const { colorScale, series, legend } = args;
42
+ const { colorScale, series: seriesList, legend } = args;
43
43
  const commonStackId = getRandomCKId();
44
- return series.map((singleSeries) => {
44
+ return seriesList.map((series) => {
45
45
  var _a, _b, _c, _d;
46
- const name = singleSeries.name || '';
47
- const color = singleSeries.color || colorScale(name);
46
+ const name = series.name || '';
47
+ const color = series.color || colorScale(name);
48
+ let stackId = series.stackId;
49
+ if (!stackId) {
50
+ stackId = series.stacking === 'normal' ? commonStackId : getRandomCKId();
51
+ }
48
52
  return {
49
- type: singleSeries.type,
53
+ type: series.type,
50
54
  color: color,
51
55
  name: name,
52
- visible: get(singleSeries, 'visible', true),
56
+ visible: get(series, 'visible', true),
53
57
  legend: {
54
- enabled: get(singleSeries, 'legend.enabled', legend.enabled),
55
- symbol: prepareLegendSymbol(singleSeries),
58
+ enabled: get(series, 'legend.enabled', legend.enabled),
59
+ symbol: prepareLegendSymbol(series),
56
60
  },
57
- data: singleSeries.data,
58
- stacking: singleSeries.stacking,
59
- stackId: singleSeries.stacking === 'normal' ? commonStackId : getRandomCKId(),
61
+ data: series.data,
62
+ stacking: series.stacking,
63
+ stackId,
60
64
  dataLabels: {
61
- enabled: ((_a = singleSeries.dataLabels) === null || _a === void 0 ? void 0 : _a.enabled) || false,
62
- inside: typeof ((_b = singleSeries.dataLabels) === null || _b === void 0 ? void 0 : _b.inside) === 'boolean'
63
- ? (_c = singleSeries.dataLabels) === null || _c === void 0 ? void 0 : _c.inside
65
+ enabled: ((_a = series.dataLabels) === null || _a === void 0 ? void 0 : _a.enabled) || false,
66
+ inside: typeof ((_b = series.dataLabels) === null || _b === void 0 ? void 0 : _b.inside) === 'boolean'
67
+ ? (_c = series.dataLabels) === null || _c === void 0 ? void 0 : _c.inside
64
68
  : false,
65
- style: Object.assign({}, DEFAULT_DATALABELS_STYLE, (_d = singleSeries.dataLabels) === null || _d === void 0 ? void 0 : _d.style),
69
+ style: Object.assign({}, DEFAULT_DATALABELS_STYLE, (_d = series.dataLabels) === null || _d === void 0 ? void 0 : _d.style),
66
70
  },
67
71
  };
68
72
  }, []);
@@ -1,8 +1,8 @@
1
1
  import React from 'react';
2
- import { ChartOptions } from '../useChartOptions/types';
3
- import { ChartScale } from '../useAxisScales';
4
- import { OnSeriesMouseLeave, OnSeriesMouseMove } from '../useTooltip/types';
5
- import { PreparedBarXSeries } from '../useSeries/types';
2
+ import type { ChartScale } from '../useAxisScales';
3
+ import type { ChartOptions } from '../useChartOptions/types';
4
+ import type { OnSeriesMouseLeave, OnSeriesMouseMove } from '../useTooltip/types';
5
+ import type { PreparedBarXSeries } from '../useSeries/types';
6
6
  type Args = {
7
7
  top: number;
8
8
  left: number;
@@ -1,51 +1,93 @@
1
+ import { max, pointer, select } from 'd3';
1
2
  import React from 'react';
3
+ import get from 'lodash/get';
2
4
  import { block } from '../../../../../utils/cn';
3
- import { group, pointer, select } from 'd3';
4
- const DEFAULT_BAR_RECT_WIDTH = 50;
5
- const DEFAULT_LINEAR_BAR_RECT_WIDTH = 20;
5
+ import { getDataCategoryValue } from '../../utils';
6
+ const RECT_PADDING = 0.1;
6
7
  const MIN_RECT_GAP = 1;
8
+ const MAX_RECT_WIDTH = 50;
9
+ const GROUP_PADDING = 0.1;
10
+ const MIN_GROUP_GAP = 1;
7
11
  const DEFAULT_LABEL_PADDING = 7;
8
12
  const b = block('d3-bar-x');
9
- const getRectProperties = (args) => {
10
- const { point, xAxis, xScale, yAxis, yScale, minPointDistance } = args;
11
- let cx;
12
- let cy;
13
- let width;
14
- let height;
13
+ function prepareData(args) {
14
+ const { series, xAxis, xScale, yScale } = args;
15
+ const categories = get(xAxis, 'categories', []);
16
+ const data = {};
17
+ series.forEach((s) => {
18
+ s.data.forEach((d) => {
19
+ const xValue = xAxis.type === 'category'
20
+ ? getDataCategoryValue({ axisDirection: 'x', categories, data: d })
21
+ : d.x;
22
+ if (xValue) {
23
+ if (!data[xValue]) {
24
+ data[xValue] = {};
25
+ }
26
+ const xGroup = data[xValue];
27
+ if (!xGroup[s.stackId]) {
28
+ xGroup[s.stackId] = [];
29
+ }
30
+ xGroup[s.stackId].push({ data: d, series: s });
31
+ }
32
+ });
33
+ });
34
+ let bandWidth = Infinity;
15
35
  if (xAxis.type === 'category') {
16
36
  const xBandScale = xScale;
17
- const maxWidth = xBandScale.bandwidth() - MIN_RECT_GAP;
18
- width = Math.min(maxWidth, DEFAULT_BAR_RECT_WIDTH);
19
- cx = (xBandScale(point.category) || 0) + xBandScale.step() / 2 - width / 2;
37
+ bandWidth = xBandScale.bandwidth();
20
38
  }
21
39
  else {
22
40
  const xLinearScale = xScale;
23
- const [min, max] = xLinearScale.domain();
24
- const range = xLinearScale.range();
25
- const maxWidth = ((range[1] - range[0]) * minPointDistance) / (Number(max) - Number(min)) - MIN_RECT_GAP;
26
- width = Math.min(Math.max(maxWidth, 1), DEFAULT_LINEAR_BAR_RECT_WIDTH);
27
- cx = xLinearScale(point.x) - width / 2;
28
- }
29
- if (yAxis[0].type === 'linear') {
30
- const yLinearScale = yScale;
31
- cy = yLinearScale(point.y);
32
- height = yLinearScale(yLinearScale.domain()[0]) - cy;
33
- }
34
- else {
35
- throw Error(`The "${yAxis[0].type}" type for the Y axis is not supported`);
36
- }
37
- return { x: cx, y: cy, width, height };
38
- };
39
- function minDiff(arr) {
40
- let result = Infinity;
41
- for (let i = 0; i < arr.length - 1; i++) {
42
- for (let j = i + 1; j < arr.length; j++) {
43
- const diff = Math.abs(arr[i] - arr[j]);
44
- if (diff < result) {
45
- result = diff;
41
+ const xValues = series.reduce((acc, s) => {
42
+ s.data.forEach((dataItem) => acc.push(Number(dataItem.x)));
43
+ return acc;
44
+ }, []);
45
+ xValues.sort().forEach((xValue, index) => {
46
+ if (index > 0 && xValue !== xValues[index - 1]) {
47
+ const dist = xLinearScale(xValue) - xLinearScale(xValues[index - 1]);
48
+ if (dist < bandWidth) {
49
+ bandWidth = dist;
50
+ }
46
51
  }
47
- }
52
+ });
48
53
  }
54
+ const maxGroupSize = max(Object.values(data), (d) => Object.values(d).length) || 1;
55
+ const groupGap = Math.max(bandWidth * GROUP_PADDING, MIN_GROUP_GAP);
56
+ const maxGroupWidth = bandWidth - groupGap;
57
+ const rectGap = Math.max((maxGroupWidth / maxGroupSize) * RECT_PADDING, MIN_RECT_GAP);
58
+ const rectWidth = Math.min(maxGroupWidth / maxGroupSize - rectGap, MAX_RECT_WIDTH);
59
+ const result = [];
60
+ Object.entries(data).forEach(([xValue, val]) => {
61
+ const stacks = Object.values(val);
62
+ const currentGroupWidth = rectWidth * stacks.length + rectGap * (stacks.length - 1);
63
+ stacks.forEach((yValues, groupItemIndex) => {
64
+ let stackHeight = 0;
65
+ yValues.forEach((yValue) => {
66
+ let xCenter;
67
+ if (xAxis.type === 'category') {
68
+ const xBandScale = xScale;
69
+ xCenter = (xBandScale(xValue) || 0) + xBandScale.bandwidth() / 2;
70
+ }
71
+ else {
72
+ const xLinearScale = xScale;
73
+ xCenter = xLinearScale(Number(xValue));
74
+ }
75
+ const x = xCenter - currentGroupWidth / 2 + (rectWidth + rectGap) * groupItemIndex;
76
+ const yLinearScale = yScale;
77
+ const y = yLinearScale(yValue.data.y);
78
+ const height = yLinearScale(yLinearScale.domain()[0]) - y;
79
+ result.push({
80
+ x,
81
+ y: y - stackHeight,
82
+ width: rectWidth,
83
+ height,
84
+ data: yValue.data,
85
+ series: yValue.series,
86
+ });
87
+ stackHeight += height + 1;
88
+ });
89
+ });
90
+ });
49
91
  return result;
50
92
  }
51
93
  export function BarXSeriesShapes(args) {
@@ -57,83 +99,56 @@ export function BarXSeriesShapes(args) {
57
99
  }
58
100
  const svgElement = select(ref.current);
59
101
  svgElement.selectAll('*').remove();
60
- const xValues = xAxis.type === 'category'
61
- ? []
62
- : series.reduce((acc, { data }) => {
63
- data.forEach((dataItem) => acc.push(Number(dataItem.x)));
64
- return acc;
65
- }, []);
66
- const minPointDistance = minDiff(xValues);
67
- const stackedSeriesMap = group(series, (item) => item.stackId);
68
- Array.from(stackedSeriesMap).forEach(([, stackedSeries]) => {
69
- const stackHeights = {};
70
- stackedSeries.forEach((item) => {
71
- const shapes = item.data.map((dataItem) => {
72
- const rectProps = getRectProperties({
73
- point: dataItem,
74
- xAxis,
75
- xScale,
76
- yAxis,
77
- yScale,
78
- minPointDistance,
79
- });
80
- if (!stackHeights[rectProps.x]) {
81
- stackHeights[rectProps.x] = 0;
82
- }
83
- const rectY = rectProps.y - stackHeights[rectProps.x];
84
- stackHeights[rectProps.x] += rectProps.height + 1;
85
- return Object.assign(Object.assign({}, rectProps), { y: rectY, data: dataItem });
86
- });
87
- svgElement
88
- .selectAll('allRects')
89
- .data(shapes)
90
- .join('rect')
91
- .attr('class', b('segment'))
92
- .attr('x', (d) => d.x)
93
- .attr('y', (d) => d.y)
94
- .attr('height', (d) => d.height)
95
- .attr('width', (d) => d.width)
96
- .attr('fill', item.color)
97
- .on('mousemove', (e, point) => {
98
- const [x, y] = pointer(e, svgContainer);
99
- onSeriesMouseMove === null || onSeriesMouseMove === void 0 ? void 0 : onSeriesMouseMove({
100
- hovered: {
101
- data: point.data,
102
- series: item,
103
- },
104
- pointerPosition: [x - left, y - top],
105
- });
106
- })
107
- .on('mouseleave', () => {
108
- if (onSeriesMouseLeave) {
109
- onSeriesMouseLeave();
110
- }
111
- });
112
- if (item.dataLabels.enabled) {
113
- const selection = svgElement
114
- .selectAll('allLabels')
115
- .data(shapes)
116
- .join('text')
117
- .text((d) => String(d.data.label || d.data.y))
118
- .attr('class', b('label'))
119
- .attr('x', (d) => d.x + d.width / 2)
120
- .attr('y', (d) => {
121
- if (item.dataLabels.inside) {
122
- return d.y + d.height / 2;
123
- }
124
- return d.y - DEFAULT_LABEL_PADDING;
125
- })
126
- .attr('text-anchor', 'middle')
127
- .style('font-size', item.dataLabels.style.fontSize);
128
- if (item.dataLabels.style.fontWeight) {
129
- selection.style('font-weight', item.dataLabels.style.fontWeight);
130
- }
131
- if (item.dataLabels.style.fontColor) {
132
- selection.style('fill', item.dataLabels.style.fontColor);
133
- }
134
- }
102
+ const shapes = prepareData({
103
+ series,
104
+ xAxis,
105
+ xScale,
106
+ yAxis,
107
+ yScale,
108
+ });
109
+ svgElement
110
+ .selectAll('allRects')
111
+ .data(shapes)
112
+ .join('rect')
113
+ .attr('class', b('segment'))
114
+ .attr('x', (d) => d.x)
115
+ .attr('y', (d) => d.y)
116
+ .attr('height', (d) => d.height)
117
+ .attr('width', (d) => d.width)
118
+ .attr('fill', (d) => d.data.color || d.series.color)
119
+ .on('mousemove', (e, d) => {
120
+ const [x, y] = pointer(e, svgContainer);
121
+ onSeriesMouseMove === null || onSeriesMouseMove === void 0 ? void 0 : onSeriesMouseMove({
122
+ hovered: {
123
+ data: d.data,
124
+ series: d.series,
125
+ },
126
+ pointerPosition: [x - left, y - top],
135
127
  });
128
+ })
129
+ .on('mouseleave', () => {
130
+ if (onSeriesMouseLeave) {
131
+ onSeriesMouseLeave();
132
+ }
136
133
  });
134
+ const dataLabels = shapes.filter((s) => s.series.dataLabels.enabled);
135
+ svgElement
136
+ .selectAll('allLabels')
137
+ .data(dataLabels)
138
+ .join('text')
139
+ .text((d) => String(d.data.label || d.data.y))
140
+ .attr('class', b('label'))
141
+ .attr('x', (d) => d.x + d.width / 2)
142
+ .attr('y', (d) => {
143
+ if (d.series.dataLabels.inside) {
144
+ return d.y + d.height / 2;
145
+ }
146
+ return d.y - DEFAULT_LABEL_PADDING;
147
+ })
148
+ .attr('text-anchor', 'middle')
149
+ .style('font-size', (d) => d.series.dataLabels.style.fontSize)
150
+ .style('font-weight', (d) => d.series.dataLabels.style.fontWeight || null)
151
+ .style('fill', (d) => d.series.dataLabels.style.fontColor || null);
137
152
  }, [
138
153
  onSeriesMouseMove,
139
154
  onSeriesMouseLeave,
@@ -1,8 +1,9 @@
1
1
  import React from 'react';
2
2
  import { group } from 'd3';
3
+ import { getRandomCKId } from '../../../../../utils';
3
4
  import { getOnlyVisibleSeries } from '../../utils';
4
5
  import { BarXSeriesShapes } from './bar-x';
5
- import { prepareScatterSeries } from './scatter';
6
+ import { ScatterSeriesShape } from './scatter';
6
7
  import { PieSeriesComponent } from './pie';
7
8
  import './styles.css';
8
9
  export const useShapes = (args) => {
@@ -15,29 +16,22 @@ export const useShapes = (args) => {
15
16
  switch (seriesType) {
16
17
  case 'bar-x': {
17
18
  if (xScale && yScale) {
18
- acc.push(React.createElement(BarXSeriesShapes, Object.assign({}, args, { key: "bar-x", series: chartSeries, xScale: xScale, yScale: yScale })));
19
+ acc.push(React.createElement(BarXSeriesShapes, { key: "bar-x", series: chartSeries, xAxis: xAxis, xScale: xScale, yAxis: yAxis, yScale: yScale, top: top, left: left, svgContainer: svgContainer, onSeriesMouseMove: onSeriesMouseMove, onSeriesMouseLeave: onSeriesMouseLeave }));
19
20
  }
20
21
  break;
21
22
  }
22
23
  case 'scatter': {
23
24
  if (xScale && yScale) {
24
- acc.push(...prepareScatterSeries({
25
- top,
26
- left,
27
- series: chartSeries,
28
- xAxis,
29
- xScale,
30
- yAxis,
31
- yScale,
32
- onSeriesMouseMove,
33
- onSeriesMouseLeave,
34
- svgContainer,
35
- }));
25
+ const scatterShapes = chartSeries.map((scatterSeries, i) => {
26
+ const id = getRandomCKId();
27
+ return (React.createElement(ScatterSeriesShape, { key: `${i}-${id}`, top: top, left: left, series: scatterSeries, xAxis: xAxis, xScale: xScale, yAxis: yAxis, yScale: yScale, onSeriesMouseMove: onSeriesMouseMove, onSeriesMouseLeave: onSeriesMouseLeave, svgContainer: svgContainer }));
28
+ });
29
+ acc.push(...scatterShapes);
36
30
  }
37
31
  break;
38
32
  }
39
33
  case 'pie': {
40
- const groupedPieSeries = group(chartSeries, (item) => item.stackId);
34
+ const groupedPieSeries = group(chartSeries, (pieSeries) => pieSeries.stackId);
41
35
  acc.push(...Array.from(groupedPieSeries).map(([key, pieSeries]) => {
42
36
  return (React.createElement(PieSeriesComponent, { key: `pie-${key}`, boundsWidth: boundsWidth, boundsHeight: boundsHeight, series: pieSeries, onSeriesMouseMove: onSeriesMouseMove, onSeriesMouseLeave: onSeriesMouseLeave, svgContainer: svgContainer }));
43
37
  }));
@@ -54,6 +48,8 @@ export const useShapes = (args) => {
54
48
  yAxis,
55
49
  yScale,
56
50
  svgContainer,
51
+ left,
52
+ top,
57
53
  onSeriesMouseMove,
58
54
  onSeriesMouseLeave,
59
55
  ]);
@@ -1,19 +1,19 @@
1
1
  import React from 'react';
2
- import { ChartOptions } from '../useChartOptions/types';
3
- import { ChartScale } from '../useAxisScales';
4
- import { OnSeriesMouseLeave, OnSeriesMouseMove } from '../useTooltip/types';
5
- import { ScatterSeries } from '../../../../../types/widget-data';
6
- type PrepareScatterSeriesArgs = {
2
+ import type { ScatterSeries } from '../../../../../types/widget-data';
3
+ import type { ChartScale } from '../useAxisScales';
4
+ import type { PreparedAxis } from '../useChartOptions/types';
5
+ import type { OnSeriesMouseLeave, OnSeriesMouseMove } from '../useTooltip/types';
6
+ type ScatterSeriesShapeProps = {
7
7
  top: number;
8
8
  left: number;
9
- series: ScatterSeries[];
10
- xAxis: ChartOptions['xAxis'];
9
+ series: ScatterSeries;
10
+ xAxis: PreparedAxis;
11
11
  xScale: ChartScale;
12
- yAxis: ChartOptions['yAxis'];
12
+ yAxis: PreparedAxis[];
13
13
  yScale: ChartScale;
14
14
  svgContainer: SVGSVGElement | null;
15
15
  onSeriesMouseMove?: OnSeriesMouseMove;
16
16
  onSeriesMouseLeave?: OnSeriesMouseLeave;
17
17
  };
18
- export declare function prepareScatterSeries(args: PrepareScatterSeriesArgs): React.ReactElement<any, string | React.JSXElementConstructor<any>>[];
18
+ export declare function ScatterSeriesShape(props: ScatterSeriesShapeProps): React.JSX.Element;
19
19
  export {};
@@ -1,65 +1,92 @@
1
- import { pointer } from 'd3';
2
1
  import React from 'react';
2
+ import { pointer, select } from 'd3';
3
+ import get from 'lodash/get';
3
4
  import { block } from '../../../../../utils/cn';
4
- import { getRandomCKId } from '../../../../../utils';
5
+ import { getDataCategoryValue } from '../../utils';
5
6
  const b = block('d3-scatter');
6
7
  const DEFAULT_SCATTER_POINT_RADIUS = 4;
7
- const prepareCategoricalScatterData = (data) => {
8
- return data.filter((d) => typeof d.category === 'string');
9
- };
10
8
  const prepareLinearScatterData = (data) => {
11
9
  return data.filter((d) => typeof d.x === 'number' && typeof d.y === 'number');
12
10
  };
13
- const getPointProperties = (args) => {
14
- const { point, xAxis, xScale, yAxis, yScale } = args;
15
- const r = point.radius || DEFAULT_SCATTER_POINT_RADIUS;
11
+ const getCxAttr = (args) => {
12
+ const { point, xAxis, xScale } = args;
16
13
  let cx;
17
- let cy;
18
14
  if (xAxis.type === 'category') {
19
15
  const xBandScale = xScale;
20
- cx = (xBandScale(point.category) || 0) + xBandScale.step() / 2;
16
+ const categories = get(xAxis, 'categories', []);
17
+ const dataCategory = getDataCategoryValue({ axisDirection: 'x', categories, data: point });
18
+ cx = (xBandScale(dataCategory) || 0) + xBandScale.step() / 2;
21
19
  }
22
20
  else {
23
21
  const xLinearScale = xScale;
24
22
  cx = xLinearScale(point.x);
25
23
  }
26
- if (yAxis[0].type === 'category') {
24
+ return cx;
25
+ };
26
+ const getCyAttr = (args) => {
27
+ const { point, yAxis, yScale } = args;
28
+ let cy;
29
+ if (yAxis.type === 'category') {
27
30
  const yBandScale = yScale;
28
- cy = (yBandScale(point.category) || 0) + yBandScale.step() / 2;
31
+ const categories = get(yAxis, 'categories', []);
32
+ const dataCategory = getDataCategoryValue({ axisDirection: 'y', categories, data: point });
33
+ cy = (yBandScale(dataCategory) || 0) + yBandScale.step() / 2;
29
34
  }
30
35
  else {
31
36
  const yLinearScale = yScale;
32
37
  cy = yLinearScale(point.y);
33
38
  }
34
- return { r, cx, cy };
39
+ return cy;
35
40
  };
36
- export function prepareScatterSeries(args) {
37
- const { top, left, series, xAxis, xScale, yAxis, yScale, onSeriesMouseMove, onSeriesMouseLeave, svgContainer, } = args;
38
- return series.reduce((result, s) => {
41
+ export function ScatterSeriesShape(props) {
42
+ const { series, xAxis, xScale, yAxis, yScale, svgContainer, left, top, onSeriesMouseMove, onSeriesMouseLeave, } = props;
43
+ const ref = React.useRef(null);
44
+ React.useEffect(() => {
39
45
  var _a;
40
- const randomKey = getRandomCKId();
46
+ if (!ref.current) {
47
+ return;
48
+ }
49
+ const svgElement = select(ref.current);
50
+ svgElement.selectAll('*').remove();
41
51
  const preparedData = xAxis.type === 'category' || ((_a = yAxis[0]) === null || _a === void 0 ? void 0 : _a.type) === 'category'
42
- ? prepareCategoricalScatterData(s.data)
43
- : prepareLinearScatterData(s.data);
44
- result.push(...preparedData.map((point, i) => {
45
- const pointProps = getPointProperties({
46
- point,
47
- xAxis,
48
- xScale,
49
- yAxis,
50
- yScale,
52
+ ? series.data
53
+ : prepareLinearScatterData(series.data);
54
+ svgElement
55
+ .selectAll('allPoints')
56
+ .data(preparedData)
57
+ .enter()
58
+ .append('circle')
59
+ .attr('class', b('point'))
60
+ .attr('fill', (d) => d.color || series.color || '')
61
+ .attr('r', (d) => d.radius || DEFAULT_SCATTER_POINT_RADIUS)
62
+ .attr('cx', (d) => getCxAttr({ point: d, xAxis, xScale }))
63
+ .attr('cy', (d) => getCyAttr({ point: d, yAxis: yAxis[0], yScale }))
64
+ .on('mousemove', (e, d) => {
65
+ const [x, y] = pointer(e, svgContainer);
66
+ onSeriesMouseMove === null || onSeriesMouseMove === void 0 ? void 0 : onSeriesMouseMove({
67
+ hovered: {
68
+ data: d,
69
+ series,
70
+ },
71
+ pointerPosition: [x - left, y - top],
51
72
  });
52
- return (React.createElement("circle", Object.assign({ key: `${i}-${randomKey}`, className: b('point'), fill: s.color }, pointProps, { onMouseMove: function (e) {
53
- const [x, y] = pointer(e, svgContainer);
54
- onSeriesMouseMove === null || onSeriesMouseMove === void 0 ? void 0 : onSeriesMouseMove({
55
- hovered: {
56
- data: point,
57
- series: s,
58
- },
59
- pointerPosition: [x - left, y - top],
60
- });
61
- }, onMouseLeave: onSeriesMouseLeave })));
62
- }));
63
- return result;
64
- }, []);
73
+ })
74
+ .on('mouseleave', () => {
75
+ if (onSeriesMouseLeave) {
76
+ onSeriesMouseLeave();
77
+ }
78
+ });
79
+ }, [
80
+ series,
81
+ xAxis,
82
+ xScale,
83
+ yAxis,
84
+ yScale,
85
+ svgContainer,
86
+ left,
87
+ top,
88
+ onSeriesMouseMove,
89
+ onSeriesMouseLeave,
90
+ ]);
91
+ return React.createElement("g", { ref: ref, className: b() });
65
92
  }
@@ -1,6 +1,7 @@
1
1
  import { AxisDomain } from 'd3';
2
- import type { BaseTextStyle, ChartKitWidgetSeries, ChartKitWidgetAxisType, ChartKitWidgetAxisLabels } from '../../../../types/widget-data';
2
+ import type { BaseTextStyle, ChartKitWidgetSeries, ChartKitWidgetSeriesData, ChartKitWidgetAxisType, ChartKitWidgetAxisLabels } from '../../../../types/widget-data';
3
3
  export * from './math';
4
+ export type AxisDirection = 'x' | 'y';
4
5
  type UnknownSeries = {
5
6
  type: ChartKitWidgetSeries['type'];
6
7
  data: unknown;
@@ -58,3 +59,8 @@ export declare const getHorisontalSvgTextHeight: (args: {
58
59
  text: string;
59
60
  style?: Partial<BaseTextStyle>;
60
61
  }) => number;
62
+ export declare const getDataCategoryValue: (args: {
63
+ axisDirection: AxisDirection;
64
+ categories: string[];
65
+ data: ChartKitWidgetSeriesData;
66
+ }) => string;
@@ -1,5 +1,6 @@
1
1
  import { group, select } from 'd3';
2
2
  import get from 'lodash/get';
3
+ import isNil from 'lodash/isNil';
3
4
  import { dateTime } from '@gravity-ui/date-utils';
4
5
  import { formatNumber } from '../../../shared';
5
6
  import { DEFAULT_AXIS_LABEL_FONT_SIZE } from '../constants';
@@ -46,7 +47,7 @@ export const getDomainDataYBySeries = (series) => {
46
47
  if (typeof values[key] === 'undefined') {
47
48
  values[key] = 0;
48
49
  }
49
- if (point.y) {
50
+ if (point.y && typeof point.y === 'number') {
50
51
  values[key] += point.y;
51
52
  }
52
53
  });
@@ -128,3 +129,29 @@ export const getHorisontalSvgTextHeight = (args) => {
128
129
  .remove();
129
130
  return height;
130
131
  };
132
+ const extractCategoryValue = (args) => {
133
+ const { axisDirection, categories, data } = args;
134
+ const dataCategory = get(data, axisDirection);
135
+ let categoryValue;
136
+ if ('category' in data && data.category) {
137
+ categoryValue = data.category;
138
+ }
139
+ if (typeof dataCategory === 'string') {
140
+ categoryValue = dataCategory;
141
+ }
142
+ if (typeof dataCategory === 'number') {
143
+ categoryValue = categories[dataCategory];
144
+ }
145
+ if (isNil(categoryValue)) {
146
+ throw new Error('It seems you are trying to get non-existing category value');
147
+ }
148
+ return categoryValue;
149
+ };
150
+ export const getDataCategoryValue = (args) => {
151
+ const { axisDirection, categories, data } = args;
152
+ const categoryValue = extractCategoryValue({ axisDirection, categories, data });
153
+ if (!categories.includes(categoryValue)) {
154
+ throw new Error('It seems you are trying to use category value that is not in categories array');
155
+ }
156
+ return categoryValue;
157
+ };
@@ -2,11 +2,25 @@ import type { BaseSeries, BaseSeriesData } from './base';
2
2
  import type { ChartKitWidgetSeriesOptions } from './series';
3
3
  import { ChartKitWidgetLegend, RectLegendSymbolOptions } from './legend';
4
4
  export type BarXSeriesData<T = any> = BaseSeriesData<T> & {
5
- /** The x value of the point */
6
- x?: number;
7
- /** The y value of the point */
8
- y?: number;
9
- /** Corresponding value of axis category */
5
+ /**
6
+ * The `x` value of the bar. Depending on the context , it may represents:
7
+ * - numeric value (for `linear` x axis)
8
+ * - timestamp value (for `datetime` x axis)
9
+ * - x axis category value (for `category` x axis). If the type is a string, then it is a category value itself. If the type is a number, then it is the index of an element in the array of categories described in `xAxis.categories`
10
+ */
11
+ x?: string | number;
12
+ /**
13
+ * The `y` value of the bar. Depending on the context , it may represents:
14
+ * - numeric value (for `linear` y axis)
15
+ * - timestamp value (for `datetime` y axis)
16
+ * - y axis category value (for `category` y axis). If the type is a string, then it is a category value itself. If the type is a number, then it is the index of an element in the array of categories described in `yAxis[0].categories`
17
+ */
18
+ y?: string | number;
19
+ /**
20
+ * Corresponding value of axis category.
21
+ *
22
+ * @deprecated use `x` or `y` instead
23
+ */
10
24
  category?: string;
11
25
  /** Data label value of the bar-x column. If not specified, the y value is used. */
12
26
  label?: string | number;
@@ -22,6 +22,8 @@ export type BaseSeriesData<T = any> = {
22
22
  * Here you can add additional data for your own event callbacks and formatter callbacks
23
23
  */
24
24
  custom?: T;
25
+ /** Individual color for the data chunk (point in scatter, segment in pie, bar etc) */
26
+ color?: string;
25
27
  };
26
28
  export type BaseTextStyle = {
27
29
  fontSize: string;
@@ -5,8 +5,6 @@ export type PieSeriesData<T = any> = BaseSeriesData<T> & {
5
5
  value: number;
6
6
  /** The name of the pie segment (used in legend, tooltip etc). */
7
7
  name: string;
8
- /** Individual color for the pie segment. */
9
- color?: string;
10
8
  /** Initial visibility of the pie segment. */
11
9
  visible?: boolean;
12
10
  /** Initial data label of the pie segment. If not specified, the value is used. */
@@ -1,11 +1,25 @@
1
1
  import type { BaseSeries, BaseSeriesData } from './base';
2
2
  import type { ChartKitWidgetLegend, RectLegendSymbolOptions } from './legend';
3
3
  export type ScatterSeriesData<T = any> = BaseSeriesData<T> & {
4
- /** The x value of the point */
5
- x?: number;
6
- /** The y value of the point */
7
- y?: number;
8
- /** Corresponding value of axis category */
4
+ /**
5
+ * The `x` value of the point. Depending on the context , it may represents:
6
+ * - numeric value (for `linear` x axis)
7
+ * - timestamp value (for `datetime` x axis)
8
+ * - x axis category value (for `category` x axis). If the type is a string, then it is a category value itself. If the type is a number, then it is the index of an element in the array of categories described in `xAxis.categories`
9
+ */
10
+ x?: string | number;
11
+ /**
12
+ * The `y` value of the point. Depending on the context , it may represents:
13
+ * - numeric value (for `linear` y axis)
14
+ * - timestamp value (for `datetime` y axis)
15
+ * - y axis category value (for `category` y axis). If the type is a string, then it is a category value itself. If the type is a number, then it is the index of an element in the array of categories described in `yAxis[0].categories`
16
+ */
17
+ y?: string | number;
18
+ /**
19
+ * Corresponding value of axis category.
20
+ *
21
+ * @deprecated use `x` or `y` instead
22
+ */
9
23
  category?: string;
10
24
  radius?: number;
11
25
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gravity-ui/chartkit",
3
- "version": "4.0.0",
3
+ "version": "4.2.0",
4
4
  "description": "React component used to render charts based on any sources you need",
5
5
  "license": "MIT",
6
6
  "repository": "git@github.com:gravity-ui/ChartKit.git",
@@ -48,7 +48,7 @@
48
48
  "dependencies": {
49
49
  "@bem-react/classname": "^1.6.0",
50
50
  "@gravity-ui/date-utils": "^1.4.1",
51
- "@gravity-ui/yagr": "^3.7.10",
51
+ "@gravity-ui/yagr": "^3.7.13",
52
52
  "d3": "^7.8.5",
53
53
  "lodash": "^4.17.21",
54
54
  "react-split-pane": "^0.1.92"