@gravity-ui/chartkit 3.5.0 → 3.7.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/renderer/components/Chart.js +7 -11
  2. package/build/plugins/d3/renderer/components/Legend.d.ts +3 -2
  3. package/build/plugins/d3/renderer/components/Legend.js +42 -21
  4. package/build/plugins/d3/renderer/constants.d.ts +3 -0
  5. package/build/plugins/d3/renderer/constants.js +3 -0
  6. package/build/plugins/d3/renderer/hooks/index.d.ts +1 -1
  7. package/build/plugins/d3/renderer/hooks/index.js +1 -1
  8. package/build/plugins/d3/renderer/hooks/useAxisScales/index.d.ts +2 -2
  9. package/build/plugins/d3/renderer/hooks/useAxisScales/index.js +2 -2
  10. package/build/plugins/d3/renderer/hooks/useChartDimensions/index.d.ts +2 -2
  11. package/build/plugins/d3/renderer/hooks/useChartDimensions/index.js +2 -4
  12. package/build/plugins/d3/renderer/hooks/useChartOptions/chart.js +5 -6
  13. package/build/plugins/d3/renderer/hooks/useChartOptions/legend.js +7 -2
  14. package/build/plugins/d3/renderer/hooks/useChartOptions/title.js +2 -2
  15. package/build/plugins/d3/renderer/hooks/useChartOptions/types.d.ts +3 -1
  16. package/build/plugins/d3/renderer/hooks/useChartOptions/x-axis.js +3 -3
  17. package/build/plugins/d3/renderer/hooks/useChartOptions/y-axis.js +3 -3
  18. package/build/plugins/d3/renderer/hooks/useSeries/constants.d.ts +1 -0
  19. package/build/plugins/d3/renderer/hooks/useSeries/constants.js +1 -0
  20. package/build/plugins/d3/renderer/hooks/useSeries/index.d.ts +10 -8
  21. package/build/plugins/d3/renderer/hooks/useSeries/index.js +47 -54
  22. package/build/plugins/d3/renderer/hooks/useSeries/prepareSeries.d.ts +10 -0
  23. package/build/plugins/d3/renderer/hooks/useSeries/prepareSeries.js +126 -0
  24. package/build/plugins/d3/renderer/hooks/useSeries/types.d.ts +35 -0
  25. package/build/plugins/d3/renderer/hooks/useSeries/types.js +1 -0
  26. package/build/plugins/d3/renderer/hooks/useSeries/utils.d.ts +3 -0
  27. package/build/plugins/d3/renderer/hooks/useSeries/utils.js +11 -0
  28. package/build/plugins/d3/renderer/hooks/useShapes/bar-x.d.ts +3 -3
  29. package/build/plugins/d3/renderer/hooks/useShapes/bar-x.js +93 -21
  30. package/build/plugins/d3/renderer/hooks/useShapes/index.d.ts +2 -2
  31. package/build/plugins/d3/renderer/hooks/useShapes/index.js +6 -14
  32. package/build/plugins/d3/renderer/hooks/useShapes/pie.d.ts +2 -2
  33. package/build/plugins/d3/renderer/hooks/useShapes/pie.js +87 -12
  34. package/build/plugins/d3/renderer/hooks/useShapes/scatter.d.ts +0 -1
  35. package/build/plugins/d3/renderer/hooks/useShapes/scatter.js +4 -2
  36. package/build/plugins/d3/renderer/hooks/useShapes/styles.css +10 -0
  37. package/build/plugins/d3/renderer/utils/index.d.ts +42 -6
  38. package/build/plugins/d3/renderer/utils/index.js +70 -6
  39. package/build/types/widget-data/bar-x.d.ts +17 -1
  40. package/build/types/widget-data/base.d.ts +15 -3
  41. package/build/types/widget-data/legend.d.ts +32 -0
  42. package/build/types/widget-data/pie.d.ts +23 -4
  43. package/build/types/widget-data/scatter.d.ts +5 -0
  44. package/package.json +1 -1
  45. package/build/plugins/d3/renderer/hooks/useChartOptions/constants.d.ts +0 -3
  46. package/build/plugins/d3/renderer/hooks/useChartOptions/constants.js +0 -3
  47. package/build/plugins/d3/renderer/hooks/useChartOptions/utils.d.ts +0 -5
  48. package/build/plugins/d3/renderer/hooks/useChartOptions/utils.js +0 -18
  49. package/build/plugins/d3/renderer/hooks/useLegend/index.d.ts +0 -13
  50. package/build/plugins/d3/renderer/hooks/useLegend/index.js +0 -59
@@ -0,0 +1,126 @@
1
+ import { scaleOrdinal } from 'd3';
2
+ import cloneDeep from 'lodash/cloneDeep';
3
+ import get from 'lodash/get';
4
+ import { DEFAULT_PALETTE } from '../../constants';
5
+ import { DEFAULT_LEGEND_SYMBOL_SIZE } from './constants';
6
+ import { getRandomCKId } from '../../../../../utils';
7
+ const DEFAULT_DATALABELS_STYLE = {
8
+ fontSize: '11px',
9
+ fontWeight: 'bold',
10
+ };
11
+ function prepareLegendSymbol(series) {
12
+ var _a;
13
+ switch (series.type) {
14
+ default: {
15
+ const symbolOptions = ((_a = series.legend) === null || _a === void 0 ? void 0 : _a.symbol) || {};
16
+ const symbolHeight = (symbolOptions === null || symbolOptions === void 0 ? void 0 : symbolOptions.height) || DEFAULT_LEGEND_SYMBOL_SIZE;
17
+ return {
18
+ shape: 'rect',
19
+ width: (symbolOptions === null || symbolOptions === void 0 ? void 0 : symbolOptions.width) || DEFAULT_LEGEND_SYMBOL_SIZE,
20
+ height: symbolHeight,
21
+ radius: (symbolOptions === null || symbolOptions === void 0 ? void 0 : symbolOptions.radius) || symbolHeight / 2,
22
+ padding: (symbolOptions === null || symbolOptions === void 0 ? void 0 : symbolOptions.padding) || 5,
23
+ };
24
+ }
25
+ }
26
+ }
27
+ function prepareAxisRelatedSeries(args) {
28
+ const { colorScale, series, legend } = args;
29
+ const preparedSeries = cloneDeep(series);
30
+ const name = 'name' in series && series.name ? series.name : '';
31
+ const color = 'color' in series && series.color ? series.color : colorScale(name);
32
+ preparedSeries.color = color;
33
+ preparedSeries.name = name;
34
+ preparedSeries.visible = get(preparedSeries, 'visible', true);
35
+ preparedSeries.legend = {
36
+ enabled: get(preparedSeries, 'legend.enabled', legend.enabled),
37
+ symbol: prepareLegendSymbol(series),
38
+ };
39
+ return [preparedSeries];
40
+ }
41
+ function prepareBarXSeries(args) {
42
+ const { colorScale, series, legend } = args;
43
+ const commonStackId = getRandomCKId();
44
+ return series.map((singleSeries) => {
45
+ var _a, _b, _c, _d;
46
+ const name = singleSeries.name || '';
47
+ const color = singleSeries.color || colorScale(name);
48
+ return {
49
+ type: singleSeries.type,
50
+ color: color,
51
+ name: name,
52
+ visible: get(singleSeries, 'visible', true),
53
+ legend: {
54
+ enabled: get(singleSeries, 'legend.enabled', legend.enabled),
55
+ symbol: prepareLegendSymbol(singleSeries),
56
+ },
57
+ data: singleSeries.data,
58
+ stacking: singleSeries.stacking,
59
+ stackId: singleSeries.stacking === 'normal' ? commonStackId : getRandomCKId(),
60
+ 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
64
+ : false,
65
+ style: Object.assign({}, DEFAULT_DATALABELS_STYLE, (_d = singleSeries.dataLabels) === null || _d === void 0 ? void 0 : _d.style),
66
+ },
67
+ };
68
+ }, []);
69
+ }
70
+ function preparePieSeries(args) {
71
+ const { series, legend } = args;
72
+ const dataNames = series.data.map((d) => d.name);
73
+ const colorScale = scaleOrdinal(dataNames, DEFAULT_PALETTE);
74
+ const stackId = getRandomCKId();
75
+ const preparedSeries = series.data.map((dataItem) => {
76
+ var _a, _b;
77
+ const result = {
78
+ type: 'pie',
79
+ data: dataItem.value,
80
+ dataLabels: {
81
+ enabled: get(series, 'dataLabels.enabled', true),
82
+ },
83
+ label: dataItem.label,
84
+ visible: typeof dataItem.visible === 'boolean' ? dataItem.visible : true,
85
+ name: dataItem.name,
86
+ color: dataItem.color || colorScale(dataItem.name),
87
+ legend: {
88
+ enabled: get(series, 'legend.enabled', legend.enabled),
89
+ symbol: prepareLegendSymbol(series),
90
+ },
91
+ center: series.center || ['50%', '50%'],
92
+ borderColor: series.borderColor || '',
93
+ borderRadius: (_a = series.borderRadius) !== null && _a !== void 0 ? _a : 0,
94
+ borderWidth: (_b = series.borderWidth) !== null && _b !== void 0 ? _b : 1,
95
+ radius: series.radius || '100%',
96
+ innerRadius: series.innerRadius || 0,
97
+ stackId,
98
+ };
99
+ return result;
100
+ });
101
+ return preparedSeries;
102
+ }
103
+ export function prepareSeries(args) {
104
+ const { type, series, legend, colorScale } = args;
105
+ switch (type) {
106
+ case 'pie': {
107
+ return series.reduce((acc, singleSeries) => {
108
+ acc.push(...preparePieSeries({ series: singleSeries, legend }));
109
+ return acc;
110
+ }, []);
111
+ }
112
+ case 'bar-x': {
113
+ return prepareBarXSeries({ series: series, legend, colorScale });
114
+ }
115
+ case 'scatter': {
116
+ return series.reduce((acc, singleSeries) => {
117
+ acc.push(...prepareAxisRelatedSeries({ series: singleSeries, legend, colorScale }));
118
+ return acc;
119
+ }, []);
120
+ }
121
+ default: {
122
+ const seriesType = get(series, 'type');
123
+ throw new Error(`Series type ${seriesType} does not support data preparation for series that do not support the presence of axes`);
124
+ }
125
+ }
126
+ }
@@ -0,0 +1,35 @@
1
+ import { BarXSeries, BarXSeriesData, BaseTextStyle, PieSeries, PieSeriesData, RectLegendSymbolOptions, ScatterSeries, ScatterSeriesData } from '../../../../../types/widget-data';
2
+ export type RectLegendSymbol = {
3
+ shape: 'rect';
4
+ } & Required<RectLegendSymbolOptions>;
5
+ export type PreparedLegendSymbol = RectLegendSymbol;
6
+ type BasePreparedSeries = {
7
+ color: string;
8
+ name: string;
9
+ visible: boolean;
10
+ legend: {
11
+ enabled: boolean;
12
+ symbol: PreparedLegendSymbol;
13
+ };
14
+ };
15
+ export type PreparedScatterSeries = {
16
+ type: ScatterSeries['type'];
17
+ data: ScatterSeriesData[];
18
+ } & BasePreparedSeries;
19
+ export type PreparedBarXSeries = {
20
+ type: BarXSeries['type'];
21
+ data: BarXSeriesData[];
22
+ stackId: string;
23
+ dataLabels: {
24
+ enabled: boolean;
25
+ inside: boolean;
26
+ style: BaseTextStyle;
27
+ };
28
+ } & BasePreparedSeries;
29
+ export type PreparedPieSeries = BasePreparedSeries & Required<Omit<PieSeries, 'data'>> & {
30
+ data: PieSeriesData['value'];
31
+ stackId: string;
32
+ label?: PieSeriesData['label'];
33
+ };
34
+ export type PreparedSeries = PreparedScatterSeries | PreparedBarXSeries | PreparedPieSeries;
35
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,3 @@
1
+ import { PreparedSeries } from './types';
2
+ export declare const getActiveLegendItems: (series: PreparedSeries[]) => string[];
3
+ export declare const getAllLegendItems: (series: PreparedSeries[]) => string[];
@@ -0,0 +1,11 @@
1
+ export const getActiveLegendItems = (series) => {
2
+ return series.reduce((acc, s) => {
3
+ if (s.legend.enabled && s.visible) {
4
+ acc.push(s.name);
5
+ }
6
+ return acc;
7
+ }, []);
8
+ };
9
+ export const getAllLegendItems = (series) => {
10
+ return series.map((s) => s.name);
11
+ };
@@ -2,11 +2,11 @@ import React from 'react';
2
2
  import { ChartOptions } from '../useChartOptions/types';
3
3
  import { ChartScale } from '../useAxisScales';
4
4
  import { OnSeriesMouseLeave, OnSeriesMouseMove } from '../useTooltip/types';
5
- import { BarXSeries } from '../../../../../types/widget-data';
5
+ import { PreparedBarXSeries } from '../useSeries/types';
6
6
  type Args = {
7
7
  top: number;
8
8
  left: number;
9
- series: BarXSeries[];
9
+ series: PreparedBarXSeries[];
10
10
  xAxis: ChartOptions['xAxis'];
11
11
  xScale: ChartScale;
12
12
  yAxis: ChartOptions['yAxis'];
@@ -15,5 +15,5 @@ type Args = {
15
15
  onSeriesMouseLeave?: OnSeriesMouseLeave;
16
16
  svgContainer: SVGSVGElement | null;
17
17
  };
18
- export declare function prepareBarXSeries(args: Args): React.ReactElement<any, string | React.JSXElementConstructor<any>>[];
18
+ export declare function BarXSeriesShapes(args: Args): React.JSX.Element;
19
19
  export {};
@@ -1,10 +1,11 @@
1
1
  import React from 'react';
2
2
  import { block } from '../../../../../utils/cn';
3
- import { pointer } from 'd3';
3
+ import { group, pointer, select } from 'd3';
4
4
  const DEFAULT_BAR_RECT_WIDTH = 50;
5
5
  const DEFAULT_LINEAR_BAR_RECT_WIDTH = 20;
6
6
  const MIN_RECT_GAP = 1;
7
- const b = block('d3-bar');
7
+ const DEFAULT_LABEL_PADDING = 7;
8
+ const b = block('d3-bar-x');
8
9
  const getRectProperties = (args) => {
9
10
  const { point, xAxis, xScale, yAxis, yScale, minPointDistance } = args;
10
11
  let cx;
@@ -47,32 +48,103 @@ function minDiff(arr) {
47
48
  }
48
49
  return result;
49
50
  }
50
- export function prepareBarXSeries(args) {
51
+ export function BarXSeriesShapes(args) {
51
52
  const { top, left, series, xAxis, xScale, yAxis, yScale, onSeriesMouseMove, onSeriesMouseLeave, svgContainer, } = args;
52
- const seriesData = series.map(({ data }) => data).flat(2);
53
- const minPointDistance = minDiff(seriesData.map((item) => Number(item.x)));
54
- return series.reduce((result, item) => {
55
- const randomKey = Math.random().toString();
56
- item.data.forEach((point, i) => {
57
- const rectProps = getRectProperties({
58
- point,
59
- xAxis,
60
- xScale,
61
- yAxis,
62
- yScale,
63
- minPointDistance,
64
- });
65
- result.push(React.createElement("rect", Object.assign({ key: `${i}-${randomKey}`, className: b('rect'), fill: item.color }, rectProps, { onMouseMove: function (e) {
53
+ const ref = React.useRef(null);
54
+ React.useEffect(() => {
55
+ if (!ref.current) {
56
+ return;
57
+ }
58
+ const svgElement = select(ref.current);
59
+ 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) => {
66
98
  const [x, y] = pointer(e, svgContainer);
67
99
  onSeriesMouseMove === null || onSeriesMouseMove === void 0 ? void 0 : onSeriesMouseMove({
68
100
  hovered: {
69
- data: point,
101
+ data: point.data,
70
102
  series: item,
71
103
  },
72
104
  pointerPosition: [x - left, y - top],
73
105
  });
74
- }, onMouseLeave: onSeriesMouseLeave })));
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
+ }
135
+ });
75
136
  });
76
- return result;
77
- }, []);
137
+ }, [
138
+ onSeriesMouseMove,
139
+ onSeriesMouseLeave,
140
+ svgContainer,
141
+ xAxis,
142
+ xScale,
143
+ yAxis,
144
+ yScale,
145
+ series,
146
+ left,
147
+ top,
148
+ ]);
149
+ return React.createElement("g", { ref: ref, className: b() });
78
150
  }
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
2
  import type { ChartOptions } from '../useChartOptions/types';
3
3
  import type { ChartScale } from '../useAxisScales';
4
- import type { ChartSeries } from '../useSeries';
4
+ import type { PreparedSeries } from '../';
5
5
  import type { OnSeriesMouseMove, OnSeriesMouseLeave } from '../useTooltip/types';
6
6
  import './styles.css';
7
7
  type Args = {
@@ -9,7 +9,7 @@ type Args = {
9
9
  left: number;
10
10
  boundsWidth: number;
11
11
  boundsHeight: number;
12
- series: ChartSeries[];
12
+ series: PreparedSeries[];
13
13
  xAxis: ChartOptions['xAxis'];
14
14
  yAxis: ChartOptions['yAxis'];
15
15
  svgContainer: SVGSVGElement | null;
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
2
  import { group } from 'd3';
3
3
  import { getOnlyVisibleSeries } from '../../utils';
4
- import { prepareBarXSeries } from './bar-x';
4
+ import { BarXSeriesShapes } from './bar-x';
5
5
  import { prepareScatterSeries } from './scatter';
6
6
  import { PieSeriesComponent } from './pie';
7
7
  import './styles.css';
@@ -15,18 +15,7 @@ export const useShapes = (args) => {
15
15
  switch (seriesType) {
16
16
  case 'bar-x': {
17
17
  if (xScale && yScale) {
18
- acc.push(...prepareBarXSeries({
19
- top,
20
- left,
21
- series: chartSeries,
22
- xAxis,
23
- xScale,
24
- yAxis,
25
- yScale,
26
- onSeriesMouseMove,
27
- onSeriesMouseLeave,
28
- svgContainer,
29
- }));
18
+ acc.push(React.createElement(BarXSeriesShapes, Object.assign({}, args, { key: "bar-x", series: chartSeries, xScale: xScale, yScale: yScale })));
30
19
  }
31
20
  break;
32
21
  }
@@ -48,7 +37,10 @@ export const useShapes = (args) => {
48
37
  break;
49
38
  }
50
39
  case 'pie': {
51
- acc.push(...chartSeries.map((cs, i) => (React.createElement(PieSeriesComponent, { key: `pie-${i}`, boundsWidth: boundsWidth, boundsHeight: boundsHeight, series: cs, onSeriesMouseMove: onSeriesMouseMove, onSeriesMouseLeave: onSeriesMouseLeave, svgContainer: svgContainer }))));
40
+ const groupedPieSeries = group(chartSeries, (item) => item.stackId);
41
+ acc.push(...Array.from(groupedPieSeries).map(([key, pieSeries]) => {
42
+ return (React.createElement(PieSeriesComponent, { key: `pie-${key}`, boundsWidth: boundsWidth, boundsHeight: boundsHeight, series: pieSeries, onSeriesMouseMove: onSeriesMouseMove, onSeriesMouseLeave: onSeriesMouseLeave, svgContainer: svgContainer }));
43
+ }));
52
44
  }
53
45
  }
54
46
  return acc;
@@ -1,10 +1,10 @@
1
1
  import React from 'react';
2
- import type { PieSeries } from '../../../../../types/widget-data';
3
2
  import type { OnSeriesMouseLeave, OnSeriesMouseMove } from '../useTooltip/types';
3
+ import { PreparedPieSeries } from '../useSeries/types';
4
4
  type PreparePieSeriesArgs = {
5
5
  boundsWidth: number;
6
6
  boundsHeight: number;
7
- series: PieSeries;
7
+ series: PreparedPieSeries[];
8
8
  svgContainer: SVGSVGElement | null;
9
9
  onSeriesMouseMove?: OnSeriesMouseMove;
10
10
  onSeriesMouseLeave?: OnSeriesMouseLeave;
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
2
  import { arc, pie, select } from 'd3';
3
3
  import { block } from '../../../../../utils/cn';
4
- import { calculateNumericProperty } from '../../utils';
4
+ import { calculateNumericProperty, getHorisontalSvgTextHeight } from '../../utils';
5
5
  const b = block('d3-pie');
6
6
  const getCenter = (boundsWidth, boundsHeight, center) => {
7
7
  var _a, _b;
@@ -16,36 +16,111 @@ const getCenter = (boundsWidth, boundsHeight, center) => {
16
16
  return [resultX, resultY];
17
17
  };
18
18
  export function PieSeriesComponent(args) {
19
+ var _a;
19
20
  const { boundsWidth, boundsHeight, series, onSeriesMouseMove, onSeriesMouseLeave, svgContainer } = args;
20
21
  const ref = React.useRef(null);
21
- const [x, y] = getCenter(boundsWidth, boundsHeight, series.center);
22
+ const [x, y] = getCenter(boundsWidth, boundsHeight, (_a = series[0]) === null || _a === void 0 ? void 0 : _a.center);
22
23
  React.useEffect(() => {
23
- var _a, _b, _c, _d;
24
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
24
25
  if (!ref.current) {
25
26
  return;
26
27
  }
27
28
  const svgElement = select(ref.current);
28
- const radiusRelatedToChart = Math.min(boundsWidth, boundsHeight) / 2;
29
- const radius = (_a = calculateNumericProperty({ value: series.radius, base: radiusRelatedToChart })) !== null && _a !== void 0 ? _a : radiusRelatedToChart;
30
- const innerRadius = (_b = calculateNumericProperty({ value: series.innerRadius, base: radius })) !== null && _b !== void 0 ? _b : 0;
31
- const pieGenerator = pie().value((d) => d.value);
32
- const visibleData = series.data.filter((d) => d.visible);
29
+ const isLabelsEnabled = (_b = (_a = series[0]) === null || _a === void 0 ? void 0 : _a.dataLabels) === null || _b === void 0 ? void 0 : _b.enabled;
30
+ let radiusRelatedToChart = Math.min(boundsWidth, boundsHeight) / 2;
31
+ if (isLabelsEnabled) {
32
+ // To have enough space for labels
33
+ radiusRelatedToChart *= 0.9;
34
+ }
35
+ let radius = (_d = calculateNumericProperty({ value: (_c = series[0]) === null || _c === void 0 ? void 0 : _c.radius, base: radiusRelatedToChart })) !== null && _d !== void 0 ? _d : radiusRelatedToChart;
36
+ const labelsArcRadius = ((_e = series[0]) === null || _e === void 0 ? void 0 : _e.radius) ? radius : radiusRelatedToChart;
37
+ if (isLabelsEnabled) {
38
+ // To have enough space for labels lines
39
+ radius *= 0.9;
40
+ }
41
+ const innerRadius = (_f = calculateNumericProperty({ value: series[0].innerRadius, base: radius })) !== null && _f !== void 0 ? _f : 0;
42
+ const pieGenerator = pie().value((d) => d.data);
43
+ const visibleData = series.filter((d) => d.visible);
33
44
  const dataReady = pieGenerator(visibleData);
34
45
  const arcGenerator = arc()
35
46
  .innerRadius(innerRadius)
36
47
  .outerRadius(radius)
37
- .cornerRadius((_c = series.borderRadius) !== null && _c !== void 0 ? _c : 0);
48
+ .cornerRadius((d) => d.data.borderRadius);
38
49
  svgElement.selectAll('*').remove();
39
50
  svgElement
40
- .selectAll('*')
51
+ .selectAll('allSlices')
41
52
  .data(dataReady)
42
53
  .enter()
43
54
  .append('path')
44
55
  .attr('d', arcGenerator)
45
56
  .attr('class', b('segment'))
46
57
  .attr('fill', (d) => d.data.color || '')
47
- .style('stroke', series.borderColor || '')
48
- .style('stroke-width', (_d = series.borderWidth) !== null && _d !== void 0 ? _d : 1);
58
+ .style('stroke', ((_g = series[0]) === null || _g === void 0 ? void 0 : _g.borderColor) || '')
59
+ .style('stroke-width', (_j = (_h = series[0]) === null || _h === void 0 ? void 0 : _h.borderWidth) !== null && _j !== void 0 ? _j : 1);
60
+ if ((_l = (_k = series[0]) === null || _k === void 0 ? void 0 : _k.dataLabels) === null || _l === void 0 ? void 0 : _l.enabled) {
61
+ const labelHeight = getHorisontalSvgTextHeight({ text: 'tmp' });
62
+ const outerArc = arc()
63
+ .innerRadius(labelsArcRadius)
64
+ .outerRadius(labelsArcRadius);
65
+ // Add the polylines between chart and labels
66
+ svgElement
67
+ .selectAll('allPolylines')
68
+ .data(dataReady)
69
+ .enter()
70
+ .append('polyline')
71
+ .attr('stroke', (d) => d.data.color || '')
72
+ .style('fill', 'none')
73
+ .attr('stroke-width', 1)
74
+ .attr('points', (d) => {
75
+ // Line insertion in the slice
76
+ const posA = arcGenerator.centroid(d);
77
+ // Line break: we use the other arc generator that has been built only for that
78
+ const posB = outerArc.centroid(d);
79
+ const posC = outerArc.centroid(d);
80
+ // We need the angle to see if the X position will be at the extreme right or extreme left
81
+ const midangle = d.startAngle + (d.endAngle - d.startAngle) / 2;
82
+ const result = [posA, posB, posC];
83
+ if (midangle < Math.PI) {
84
+ // polylines located to the right
85
+ const nextCx = radiusRelatedToChart * 0.95;
86
+ if (nextCx > result[1][0]) {
87
+ result[2][0] = nextCx;
88
+ }
89
+ else {
90
+ result.splice(2, 1);
91
+ }
92
+ }
93
+ else {
94
+ // polylines located to the left
95
+ const nextCx = radiusRelatedToChart * 0.95 * -1;
96
+ if (nextCx < result[1][0]) {
97
+ result[2][0] = nextCx;
98
+ }
99
+ else {
100
+ result.splice(2, 1);
101
+ }
102
+ }
103
+ return result.join(' ');
104
+ });
105
+ // Add the polylines between chart and labels
106
+ svgElement
107
+ .selectAll('allLabels')
108
+ .data(dataReady)
109
+ .join('text')
110
+ .text((d) => d.data.label || d.value)
111
+ .attr('class', b('label'))
112
+ .attr('transform', (d) => {
113
+ const pos = outerArc.centroid(d);
114
+ const midangle = d.startAngle + (d.endAngle - d.startAngle) / 2;
115
+ pos[0] = radiusRelatedToChart * 0.99 * (midangle < Math.PI ? 1 : -1);
116
+ pos[1] += labelHeight / 4;
117
+ return `translate(${pos})`;
118
+ })
119
+ .style('text-anchor', (d) => {
120
+ const midangle = d.startAngle + (d.endAngle - d.startAngle) / 2;
121
+ return midangle < Math.PI ? 'start' : 'end';
122
+ });
123
+ }
49
124
  }, [boundsWidth, boundsHeight, series, onSeriesMouseMove, onSeriesMouseLeave, svgContainer]);
50
125
  return React.createElement("g", { ref: ref, className: b(), transform: `translate(${x}, ${y})` });
51
126
  }
@@ -14,7 +14,6 @@ type PrepareScatterSeriesArgs = {
14
14
  svgContainer: SVGSVGElement | null;
15
15
  onSeriesMouseMove?: OnSeriesMouseMove;
16
16
  onSeriesMouseLeave?: OnSeriesMouseLeave;
17
- key?: string;
18
17
  };
19
18
  export declare function prepareScatterSeries(args: PrepareScatterSeriesArgs): React.ReactElement<any, string | React.JSXElementConstructor<any>>[];
20
19
  export {};
@@ -1,6 +1,7 @@
1
1
  import { pointer } from 'd3';
2
2
  import React from 'react';
3
3
  import { block } from '../../../../../utils/cn';
4
+ import { getRandomCKId } from '../../../../../utils';
4
5
  const b = block('d3-scatter');
5
6
  const DEFAULT_SCATTER_POINT_RADIUS = 4;
6
7
  const prepareCategoricalScatterData = (data) => {
@@ -33,9 +34,10 @@ const getPointProperties = (args) => {
33
34
  return { r, cx, cy };
34
35
  };
35
36
  export function prepareScatterSeries(args) {
36
- const { top, left, series, xAxis, xScale, yAxis, yScale, onSeriesMouseMove, onSeriesMouseLeave, key, svgContainer, } = args;
37
+ const { top, left, series, xAxis, xScale, yAxis, yScale, onSeriesMouseMove, onSeriesMouseLeave, svgContainer, } = args;
37
38
  return series.reduce((result, s) => {
38
39
  var _a;
40
+ const randomKey = getRandomCKId();
39
41
  const preparedData = xAxis.type === 'category' || ((_a = yAxis[0]) === null || _a === void 0 ? void 0 : _a.type) === 'category'
40
42
  ? prepareCategoricalScatterData(s.data)
41
43
  : prepareLinearScatterData(s.data);
@@ -47,7 +49,7 @@ export function prepareScatterSeries(args) {
47
49
  yAxis,
48
50
  yScale,
49
51
  });
50
- return (React.createElement("circle", Object.assign({ key: `${i}-${key}`, className: b('point'), fill: s.color }, pointProps, { onMouseMove: function (e) {
52
+ return (React.createElement("circle", Object.assign({ key: `${i}-${randomKey}`, className: b('point'), fill: s.color }, pointProps, { onMouseMove: function (e) {
51
53
  const [x, y] = pointer(e, svgContainer);
52
54
  onSeriesMouseMove === null || onSeriesMouseMove === void 0 ? void 0 : onSeriesMouseMove({
53
55
  hovered: {
@@ -13,4 +13,14 @@
13
13
 
14
14
  .chartkit-d3-pie__segment {
15
15
  stroke: var(--g-color-base-background);
16
+ }
17
+
18
+ .chartkit-d3-pie__label {
19
+ fill: var(--g-color-text-complementary);
20
+ font-size: 11px;
21
+ font-weight: bold;
22
+ }
23
+
24
+ .chartkit-d3-bar-x__label {
25
+ fill: var(--g-color-text-complementary);
16
26
  }