@gravity-ui/chartkit 4.17.0 → 4.18.1

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 (55) hide show
  1. package/build/i18n/keysets/en.json +2 -1
  2. package/build/i18n/keysets/ru.json +2 -1
  3. package/build/plugins/d3/examples/area/PercentStacking.d.ts +2 -0
  4. package/build/plugins/d3/examples/area/PercentStacking.js +47 -0
  5. package/build/plugins/d3/examples/bar-x/PercentStack.d.ts +2 -0
  6. package/build/plugins/d3/examples/bar-x/PercentStack.js +47 -0
  7. package/build/plugins/d3/examples/bar-y/PercentStacking.d.ts +2 -0
  8. package/build/plugins/d3/examples/bar-y/PercentStacking.js +49 -0
  9. package/build/plugins/d3/examples/pie/DonutWithTotals.d.ts +2 -0
  10. package/build/plugins/d3/examples/pie/DonutWithTotals.js +36 -0
  11. package/build/plugins/d3/examples/scatter/Basic.js +0 -21
  12. package/build/plugins/d3/index.d.ts +1 -0
  13. package/build/plugins/d3/index.js +1 -0
  14. package/build/plugins/d3/renderer/D3Widget.js +4 -2
  15. package/build/plugins/d3/renderer/components/Tooltip/TooltipTriggerArea.js +5 -4
  16. package/build/plugins/d3/renderer/hooks/useSeries/constants.d.ts +2 -0
  17. package/build/plugins/d3/renderer/hooks/useSeries/constants.js +7 -1
  18. package/build/plugins/d3/renderer/hooks/useSeries/prepare-area.d.ts +2 -2
  19. package/build/plugins/d3/renderer/hooks/useSeries/prepare-area.js +2 -8
  20. package/build/plugins/d3/renderer/hooks/useSeries/{prepare-line-series.d.ts → prepare-line.d.ts} +2 -2
  21. package/build/plugins/d3/renderer/hooks/useSeries/{prepare-line-series.js → prepare-line.js} +2 -8
  22. package/build/plugins/d3/renderer/hooks/useSeries/prepare-pie.js +1 -0
  23. package/build/plugins/d3/renderer/hooks/useSeries/prepare-scatter.d.ts +11 -0
  24. package/build/plugins/d3/renderer/hooks/useSeries/prepare-scatter.js +45 -0
  25. package/build/plugins/d3/renderer/hooks/useSeries/prepareSeries.js +3 -24
  26. package/build/plugins/d3/renderer/hooks/useSeries/types.d.ts +24 -4
  27. package/build/plugins/d3/renderer/hooks/useSeries/utils.d.ts +1 -1
  28. package/build/plugins/d3/renderer/hooks/useSeries/utils.js +1 -1
  29. package/build/plugins/d3/renderer/hooks/useShapes/area/prepare-data.js +18 -0
  30. package/build/plugins/d3/renderer/hooks/useShapes/bar-x/prepare-data.js +15 -3
  31. package/build/plugins/d3/renderer/hooks/useShapes/bar-y/prepare-data.js +15 -4
  32. package/build/plugins/d3/renderer/hooks/useShapes/index.d.ts +2 -2
  33. package/build/plugins/d3/renderer/hooks/useShapes/marker.d.ts +4 -3
  34. package/build/plugins/d3/renderer/hooks/useShapes/marker.js +18 -19
  35. package/build/plugins/d3/renderer/hooks/useShapes/pie/index.js +14 -0
  36. package/build/plugins/d3/renderer/hooks/useShapes/scatter/index.d.ts +1 -2
  37. package/build/plugins/d3/renderer/hooks/useShapes/scatter/index.js +44 -56
  38. package/build/plugins/d3/renderer/hooks/useShapes/scatter/prepare-data.d.ts +1 -10
  39. package/build/plugins/d3/renderer/hooks/useShapes/scatter/prepare-data.js +6 -8
  40. package/build/plugins/d3/renderer/hooks/useShapes/scatter/types.d.ts +15 -0
  41. package/build/plugins/d3/renderer/hooks/useShapes/scatter/types.js +1 -0
  42. package/build/plugins/d3/renderer/hooks/useShapes/utils.d.ts +1 -1
  43. package/build/plugins/d3/renderer/utils/symbol.d.ts +1 -3
  44. package/build/plugins/d3/renderer/validation/index.js +18 -1
  45. package/build/plugins/d3/utils/index.d.ts +4 -0
  46. package/build/plugins/d3/utils/index.js +4 -0
  47. package/build/plugins/d3/utils/pie-center-text.d.ts +7 -0
  48. package/build/plugins/d3/utils/pie-center-text.js +23 -0
  49. package/build/types/widget-data/area.d.ts +2 -2
  50. package/build/types/widget-data/line.d.ts +1 -5
  51. package/build/types/widget-data/marker.d.ts +2 -0
  52. package/build/types/widget-data/pie.d.ts +11 -0
  53. package/build/types/widget-data/series.d.ts +9 -4
  54. package/build/types/widget-data/tooltip.d.ts +5 -1
  55. package/package.json +5 -3
@@ -50,6 +50,8 @@ function getXValues(series, xAxis, xScale) {
50
50
  }
51
51
  export const prepareAreaData = (args) => {
52
52
  const { series, xAxis, xScale, yScale } = args;
53
+ const yLinearScale = yScale;
54
+ const plotHeight = yLinearScale(yLinearScale.domain()[0]);
53
55
  const yAxis = args.yAxis[0];
54
56
  const [_xMin, xRangeMax] = xScale.range();
55
57
  const xMax = xRangeMax / (1 - xAxis.maxPadding);
@@ -109,6 +111,22 @@ export const prepareAreaData = (args) => {
109
111
  });
110
112
  return acc;
111
113
  }, []);
114
+ if (series.some((s) => s.stacking === 'percent')) {
115
+ xValues.forEach(([x], index) => {
116
+ const stackHeight = accumulatedYValues.get(x) || 0;
117
+ let acc = 0;
118
+ const ratio = plotHeight / stackHeight;
119
+ seriesStackData.forEach((item) => {
120
+ const point = item.points[index];
121
+ if (point) {
122
+ const height = (point.y0 - point.y) * ratio;
123
+ point.y0 = plotHeight - height - acc;
124
+ point.y = point.y0 + height;
125
+ acc += height;
126
+ }
127
+ });
128
+ });
129
+ }
112
130
  return result.concat(seriesStackData);
113
131
  }, []);
114
132
  };
@@ -25,6 +25,8 @@ function getLabelData(d) {
25
25
  }
26
26
  export const prepareBarXData = (args) => {
27
27
  const { series, seriesOptions, xAxis, xScale, yScale } = args;
28
+ const yLinearScale = yScale;
29
+ const plotHeight = yLinearScale(yLinearScale.domain()[0]);
28
30
  const categories = get(xAxis, 'categories', []);
29
31
  const barMaxWidth = get(seriesOptions, 'bar-x.barMaxWidth');
30
32
  const barPadding = get(seriesOptions, 'bar-x.barPadding');
@@ -93,6 +95,7 @@ export const prepareBarXData = (args) => {
93
95
  const currentGroupWidth = rectWidth * stacks.length + rectGap * (stacks.length - 1);
94
96
  stacks.forEach((yValues, groupItemIndex) => {
95
97
  let stackHeight = 0;
98
+ const stackItems = [];
96
99
  const sortedData = sortKey
97
100
  ? sort(yValues, (a, b) => comparator(get(a, sortKey), get(b, sortKey)))
98
101
  : yValues;
@@ -107,9 +110,8 @@ export const prepareBarXData = (args) => {
107
110
  xCenter = xLinearScale(Number(xValue));
108
111
  }
109
112
  const x = xCenter - currentGroupWidth / 2 + (rectWidth + rectGap) * groupItemIndex;
110
- const yLinearScale = yScale;
111
113
  const y = yLinearScale(yValue.data.y);
112
- const height = yLinearScale(yLinearScale.domain()[0]) - y;
114
+ const height = plotHeight - y;
113
115
  const barData = {
114
116
  x,
115
117
  y: y - stackHeight,
@@ -119,9 +121,19 @@ export const prepareBarXData = (args) => {
119
121
  series: yValue.series,
120
122
  };
121
123
  barData.label = getLabelData(barData);
122
- result.push(barData);
124
+ stackItems.push(barData);
123
125
  stackHeight += height + 1;
124
126
  });
127
+ if (series.some((s) => s.stacking === 'percent')) {
128
+ let acc = 0;
129
+ const ratio = plotHeight / (stackHeight - stackItems.length);
130
+ stackItems.forEach((item) => {
131
+ item.height = item.height * ratio;
132
+ item.y = plotHeight - item.height - acc;
133
+ acc += item.height + 1;
134
+ });
135
+ }
136
+ result.push(...stackItems);
125
137
  });
126
138
  });
127
139
  return result;
@@ -49,6 +49,8 @@ function getBandWidth(series, yAxis, yScale) {
49
49
  }
50
50
  export const prepareBarYData = (args) => {
51
51
  const { series, seriesOptions, yAxis, xScale, yScale } = args;
52
+ const xLinearScale = xScale;
53
+ const plotWidth = xLinearScale(xLinearScale.domain()[1]);
52
54
  const barMaxWidth = get(seriesOptions, 'bar-y.barMaxWidth');
53
55
  const barPadding = get(seriesOptions, 'bar-y.barPadding');
54
56
  const groupPadding = get(seriesOptions, 'bar-y.groupPadding');
@@ -80,6 +82,7 @@ export const prepareBarYData = (args) => {
80
82
  const currentBarHeight = barHeight * stacks.length + rectGap * (stacks.length - 1);
81
83
  stacks.forEach((measureValues, groupItemIndex) => {
82
84
  let stackSum = 0;
85
+ const stackItems = [];
83
86
  const sortedData = sortKey
84
87
  ? sort(measureValues, (a, b) => comparator(get(a, sortKey), get(b, sortKey)))
85
88
  : measureValues;
@@ -94,10 +97,8 @@ export const prepareBarYData = (args) => {
94
97
  center = scale(Number(yValue));
95
98
  }
96
99
  const y = center - currentBarHeight / 2 + (barHeight + rectGap) * groupItemIndex;
97
- const xLinearScale = xScale;
98
- const x = xLinearScale(data.x);
99
- const width = x - xLinearScale(xLinearScale.domain()[0]);
100
- result.push({
100
+ const width = xLinearScale(data.x);
101
+ stackItems.push({
101
102
  x: stackSum,
102
103
  y,
103
104
  width,
@@ -108,6 +109,16 @@ export const prepareBarYData = (args) => {
108
109
  });
109
110
  stackSum += width + 1;
110
111
  });
112
+ if (series.some((s) => s.stacking === 'percent')) {
113
+ let acc = 0;
114
+ const ratio = plotWidth / (stackSum - stackItems.length);
115
+ stackItems.forEach((item) => {
116
+ item.width = item.width * ratio;
117
+ item.x = acc;
118
+ acc += item.width;
119
+ });
120
+ }
121
+ result.push(...stackItems);
111
122
  });
112
123
  });
113
124
  return result;
@@ -4,12 +4,12 @@ import type { PreparedAxis } from '../useChartOptions/types';
4
4
  import type { ChartScale } from '../useAxisScales';
5
5
  import type { PreparedSeries, PreparedSeriesOptions } from '../';
6
6
  import type { PreparedBarXData } from './bar-x';
7
- import type { PreparedScatterData } from './scatter';
7
+ import type { PreparedScatterData } from './scatter/types';
8
8
  import type { PreparedPieData } from './pie/types';
9
9
  import type { PreparedLineData } from './line/types';
10
10
  import type { PreparedBarYData } from './bar-y/types';
11
11
  export type { PreparedBarXData } from './bar-x';
12
- export type { PreparedScatterData } from './scatter';
12
+ export type { PreparedScatterData } from './scatter/types';
13
13
  import type { PreparedAreaData } from './area/types';
14
14
  import './styles.css';
15
15
  export type ShapeData = PreparedBarXData | PreparedBarYData | PreparedScatterData | PreparedLineData | PreparedPieData | PreparedAreaData;
@@ -1,12 +1,13 @@
1
1
  import { BaseType, Selection } from 'd3';
2
2
  import { MarkerData as LineMarkerData } from './line/types';
3
3
  import { MarkerData as AreaMarkerData } from './area/types';
4
- type MarkerData = LineMarkerData | AreaMarkerData;
5
- export declare function renderMarker<T extends MarkerData>(selection: Selection<BaseType | SVGGElement, T, SVGGElement, unknown>): Selection<SVGGElement | BaseType, T, SVGGElement, unknown>;
4
+ import { MarkerData as ScatterMarkerData } from './scatter/types';
5
+ type MarkerData = LineMarkerData | AreaMarkerData | ScatterMarkerData;
6
+ export declare function renderMarker<T extends MarkerData>(selection: Selection<BaseType | SVGGElement, T, SVGGElement, unknown>): Selection<BaseType | SVGGElement, T, SVGGElement, unknown>;
6
7
  export declare function getMarkerVisibility(d: MarkerData): "" | "hidden";
7
8
  export declare function getMarkerHaloVisibility(d: MarkerData): "" | "hidden";
8
9
  export declare function setMarker<T extends BaseType, D extends MarkerData>(selection: Selection<T, D, BaseType | null, unknown>, state: 'normal' | 'hover'): void;
9
- export declare function getMarkerSymbol(type: string, radius: number): string | null;
10
+ export declare function getMarkerSymbol(type: "circle" | "diamond" | "square" | "triangle" | "triangle-down" | undefined, radius: number): string | null;
10
11
  export declare function selectMarkerHalo<T>(parentSelection: Selection<BaseType, T, null, undefined>): Selection<BaseType, T, null, undefined>;
11
12
  export declare function selectMarkerSymbol<T>(parentSelection: Selection<BaseType, T, null, undefined>): Selection<BaseType, T, null, undefined>;
12
13
  export {};
@@ -1,5 +1,8 @@
1
- import { symbol, symbolCircle, symbolSquare } from 'd3';
1
+ import { symbol } from 'd3';
2
2
  import { block } from '../../../../../utils/cn';
3
+ import { SymbolType } from '../../../../../constants';
4
+ import { getSymbol } from '../../utils';
5
+ import get from 'lodash/get';
3
6
  const b = block('d3-marker');
4
7
  const haloClassName = b('halo');
5
8
  const symbolClassName = b('symbol');
@@ -14,9 +17,11 @@ export function renderMarker(selection) {
14
17
  .append('path')
15
18
  .attr('class', haloClassName)
16
19
  .attr('d', (d) => {
17
- const type = d.point.series.marker.states.normal.symbol;
18
- const radius = d.point.series.marker.states.hover.halo.size;
19
- return getMarkerSymbol(type, radius);
20
+ const series = d.point.series;
21
+ const type = series.marker.states.normal.symbol;
22
+ const radius = get(d.point.data, 'radius', series.marker.states.hover.radius);
23
+ const haloSize = series.marker.states.hover.halo.size;
24
+ return getMarkerSymbol(type, radius + haloSize);
20
25
  })
21
26
  .attr('fill', (d) => d.point.series.color)
22
27
  .attr('opacity', (d) => d.point.series.marker.states.hover.halo.opacity)
@@ -42,25 +47,19 @@ export function getMarkerHaloVisibility(d) {
42
47
  export function setMarker(selection, state) {
43
48
  selection
44
49
  .attr('d', (d) => {
45
- const radius = d.point.series.marker.states[state].radius +
46
- d.point.series.marker.states[state].borderWidth;
47
- return getMarkerSymbol(d.point.series.marker.states.normal.symbol, radius);
50
+ const series = d.point.series;
51
+ const type = series.marker.states.normal.symbol;
52
+ const radius = get(d.point.data, 'radius', series.marker.states[state].radius);
53
+ const size = radius + series.marker.states[state].borderWidth;
54
+ return getMarkerSymbol(type, size);
48
55
  })
49
56
  .attr('stroke-width', (d) => d.point.series.marker.states[state].borderWidth)
50
57
  .attr('stroke', (d) => d.point.series.marker.states[state].borderColor);
51
58
  }
52
- export function getMarkerSymbol(type, radius) {
53
- switch (type) {
54
- case 'square': {
55
- const size = Math.pow(radius, 2) * Math.PI;
56
- return symbol(symbolSquare, size)();
57
- }
58
- case 'circle':
59
- default: {
60
- const size = Math.pow(radius, 2) * Math.PI;
61
- return symbol(symbolCircle, size)();
62
- }
63
- }
59
+ export function getMarkerSymbol(type = SymbolType.Circle, radius) {
60
+ const symbolFn = getSymbol(type);
61
+ const size = Math.pow(radius, 2) * Math.PI;
62
+ return symbol(symbolFn, size)();
64
63
  }
65
64
  export function selectMarkerHalo(parentSelection) {
66
65
  return parentSelection.select(`.${haloClassName}`);
@@ -34,6 +34,7 @@ export function PieSeriesShapes(args) {
34
34
  })
35
35
  .style('stroke', (pieData) => pieData.borderColor)
36
36
  .style('stroke-width', (pieData) => pieData.borderWidth);
37
+ // Render halo appearing outside the hovered slice
37
38
  shapesSelection
38
39
  .selectAll('halo')
39
40
  .data((pieData) => {
@@ -55,6 +56,7 @@ export function PieSeriesShapes(args) {
55
56
  .attr('opacity', (d) => d.data.pie.halo.opacity)
56
57
  .attr('z-index', -1)
57
58
  .attr('visibility', getHaloVisibility);
59
+ // Render segments
58
60
  shapesSelection
59
61
  .selectAll(segmentSelector)
60
62
  .data((pieData) => pieData.segments)
@@ -101,6 +103,18 @@ export function PieSeriesShapes(args) {
101
103
  .attr('stroke-linejoin', 'round')
102
104
  .attr('stroke-linecap', 'round')
103
105
  .style('fill', 'none');
106
+ // Render custom shapes if defined
107
+ shapesSelection.each(function (d, index, nodes) {
108
+ var _a, _b;
109
+ const customShape = (_b = (_a = d.series).renderCustomShape) === null || _b === void 0 ? void 0 : _b.call(_a, {
110
+ series: {
111
+ innerRadius: d.innerRadius,
112
+ },
113
+ });
114
+ if (customShape) {
115
+ nodes[index].append(customShape);
116
+ }
117
+ });
104
118
  const eventName = `hover-shape.pie`;
105
119
  const hoverOptions = get(seriesOptions, 'pie.states.hover');
106
120
  const inactiveOptions = get(seriesOptions, 'pie.states.inactive');
@@ -1,9 +1,8 @@
1
1
  import React from 'react';
2
2
  import type { Dispatch } from 'd3';
3
3
  import { PreparedSeriesOptions } from '../../useSeries/types';
4
- import type { PreparedScatterData } from './prepare-data';
4
+ import type { PreparedScatterData } from './types';
5
5
  export { prepareScatterData } from './prepare-data';
6
- export type { PreparedScatterData } from './prepare-data';
7
6
  type ScatterSeriesShapeProps = {
8
7
  dispatcher: Dispatch<object>;
9
8
  preparedData: PreparedScatterData[];
@@ -1,16 +1,11 @@
1
1
  import React from 'react';
2
2
  import get from 'lodash/get';
3
- import { symbol, color, pointer, select } from 'd3';
3
+ import { pointer, select } from 'd3';
4
4
  import { block } from '../../../../../../utils/cn';
5
- import { extractD3DataFromNode, isNodeContainsD3Data, getSymbol } from '../../../utils';
6
- import { shapeKey } from '../utils';
7
- import { SymbolType } from '../../../../../../constants';
5
+ import { setActiveState, shapeKey } from '../utils';
6
+ import { getMarkerHaloVisibility, renderMarker, selectMarkerHalo, selectMarkerSymbol, setMarker, } from '../marker';
8
7
  export { prepareScatterData } from './prepare-data';
9
8
  const b = block('d3-scatter');
10
- const EMPTY_SELECTION = null;
11
- const isNodeContainsScatterData = (node) => {
12
- return isNodeContainsD3Data(node);
13
- };
14
9
  export function ScatterSeriesShape(props) {
15
10
  const { dispatcher, preparedData, seriesOptions, svgContainer } = props;
16
11
  const ref = React.useRef(null);
@@ -21,30 +16,29 @@ export function ScatterSeriesShape(props) {
21
16
  const svgElement = select(ref.current);
22
17
  const hoverOptions = get(seriesOptions, 'scatter.states.hover');
23
18
  const inactiveOptions = get(seriesOptions, 'scatter.states.inactive');
19
+ svgElement.selectAll('*').remove();
24
20
  const selection = svgElement
25
21
  .selectAll('path')
26
22
  .data(preparedData, shapeKey)
27
- .join((enter) => enter.append('path').attr('class', b('point')), (update) => update, (exit) => exit.remove())
28
- .attr('d', (d) => {
29
- const symbolType = d.series.symbolType || SymbolType.Circle;
30
- const scatterSymbol = getSymbol(symbolType);
31
- // D3 takes size as square pixels, so we need to make square pixels size by multiplying
32
- // https://d3js.org/d3-shape/symbol#symbol
33
- return symbol(scatterSymbol, d.size * d.size)();
34
- })
35
- .attr('transform', (d) => {
36
- return 'translate(' + d.cx + ',' + d.cy + ')';
37
- })
38
- .attr('fill', (d) => d.data.color || d.series.color || '');
23
+ .join('g')
24
+ .call(renderMarker)
25
+ .attr('fill', (d) => d.point.data.color || d.point.series.color || '');
39
26
  svgElement
40
27
  .on('mousemove', (e) => {
41
- const point = e.target;
42
- if (!isNodeContainsScatterData(point)) {
28
+ const datum = select(e.target).datum();
29
+ if (!datum) {
43
30
  return;
44
31
  }
45
32
  const [pointerX, pointerY] = pointer(e, svgContainer);
46
- const segmentData = extractD3DataFromNode(point);
47
- dispatcher.call('hover-shape', {}, [segmentData], [pointerX, pointerY]);
33
+ const data = {
34
+ series: {
35
+ id: datum.point.series.id,
36
+ type: 'scatter',
37
+ name: datum.point.series.name,
38
+ },
39
+ data: datum.point.data,
40
+ };
41
+ dispatcher.call('hover-shape', {}, [data], [pointerX, pointerY]);
48
42
  })
49
43
  .on('mouseleave', () => {
50
44
  dispatcher.call('hover-shape', {}, undefined);
@@ -52,41 +46,35 @@ export function ScatterSeriesShape(props) {
52
46
  const hoverEnabled = hoverOptions === null || hoverOptions === void 0 ? void 0 : hoverOptions.enabled;
53
47
  const inactiveEnabled = inactiveOptions === null || inactiveOptions === void 0 ? void 0 : inactiveOptions.enabled;
54
48
  dispatcher.on('hover-shape.scatter', (data) => {
55
- const selectedPoint = data === null || data === void 0 ? void 0 : data[0];
56
- const updates = [];
57
- preparedData.forEach((p) => {
58
- const hovered = Boolean(hoverEnabled &&
59
- selectedPoint &&
60
- p.cx === selectedPoint.cx &&
61
- p.cy === selectedPoint.cy);
62
- if (p.hovered !== hovered) {
63
- p.hovered = hovered;
64
- updates.push(p);
49
+ var _a;
50
+ const selected = data === null || data === void 0 ? void 0 : data.find((d) => d.series.type === 'scatter');
51
+ const selectedDataItem = selected === null || selected === void 0 ? void 0 : selected.data;
52
+ const selectedSeriesId = (_a = selected === null || selected === void 0 ? void 0 : selected.series) === null || _a === void 0 ? void 0 : _a.id;
53
+ selection.datum((d, index, list) => {
54
+ const elementSelection = select(list[index]);
55
+ const hovered = Boolean(hoverEnabled && d.point.data === selectedDataItem);
56
+ if (d.hovered !== hovered) {
57
+ d.hovered = hovered;
58
+ elementSelection.attr('z-index', hovered ? 999 : null);
59
+ selectMarkerHalo(elementSelection).attr('visibility', getMarkerHaloVisibility);
60
+ selectMarkerSymbol(elementSelection).call(setMarker, hovered ? 'hover' : 'normal');
61
+ }
62
+ if (hovered) {
63
+ elementSelection.raise();
65
64
  }
66
- const active = Boolean(!inactiveEnabled || !selectedPoint || selectedPoint.series.id === p.series.id);
67
- if (p.active !== active) {
68
- p.active = active;
69
- updates.push(p);
65
+ if (d.point.series.marker.states.normal.enabled) {
66
+ const isActive = Boolean(!inactiveEnabled ||
67
+ !selectedSeriesId ||
68
+ selectedSeriesId === d.point.series.id);
69
+ setActiveState({
70
+ element: list[index],
71
+ state: inactiveOptions,
72
+ active: isActive,
73
+ datum: d,
74
+ });
70
75
  }
76
+ return d;
71
77
  });
72
- selection.data(updates, shapeKey).join(() => EMPTY_SELECTION, (update) => {
73
- update
74
- .attr('fill', (d) => {
75
- var _a;
76
- const initialColor = d.data.color || d.series.color || '';
77
- if (d.hovered) {
78
- return (((_a = color(initialColor)) === null || _a === void 0 ? void 0 : _a.brighter(hoverOptions === null || hoverOptions === void 0 ? void 0 : hoverOptions.brightness).toString()) || initialColor);
79
- }
80
- return initialColor;
81
- })
82
- .attr('opacity', function (d) {
83
- if (!d.active) {
84
- return (inactiveOptions === null || inactiveOptions === void 0 ? void 0 : inactiveOptions.opacity) || null;
85
- }
86
- return null;
87
- });
88
- return update;
89
- }, (exit) => exit);
90
78
  });
91
79
  return () => {
92
80
  dispatcher.on('hover-shape.scatter', null);
@@ -1,16 +1,7 @@
1
- import type { TooltipDataChunkScatter } from '../../../../../../types';
2
1
  import type { ChartScale } from '../../useAxisScales';
3
2
  import type { PreparedAxis } from '../../useChartOptions/types';
4
3
  import { PreparedScatterSeries } from '../../useSeries/types';
5
- export type PreparedScatterData = Omit<TooltipDataChunkScatter, 'series'> & {
6
- cx: number;
7
- cy: number;
8
- series: PreparedScatterSeries;
9
- hovered: boolean;
10
- active: boolean;
11
- id: number;
12
- size: number;
13
- };
4
+ import { PreparedScatterData } from './types';
14
5
  export declare const prepareScatterData: (args: {
15
6
  series: PreparedScatterSeries[];
16
7
  xAxis: PreparedAxis;
@@ -1,5 +1,4 @@
1
1
  import { getXValue, getYValue } from '../utils';
2
- const DEFAULT_SCATTER_POINT_SIZE = 7;
3
2
  const getFilteredLinearScatterData = (data) => {
4
3
  return data.filter((d) => typeof d.x === 'number' && typeof d.y === 'number');
5
4
  };
@@ -10,16 +9,15 @@ export const prepareScatterData = (args) => {
10
9
  ? s.data
11
10
  : getFilteredLinearScatterData(s.data);
12
11
  filteredData.forEach((d) => {
13
- const size = d.radius ? d.radius * 2 : DEFAULT_SCATTER_POINT_SIZE;
14
12
  acc.push({
15
- data: d,
16
- series: s,
17
- cx: getXValue({ point: d, xAxis, xScale }),
18
- cy: getYValue({ point: d, yAxis, yScale }),
13
+ point: {
14
+ data: d,
15
+ series: s,
16
+ x: getXValue({ point: d, xAxis, xScale }),
17
+ y: getYValue({ point: d, yAxis, yScale }),
18
+ },
19
19
  hovered: false,
20
20
  active: true,
21
- id: acc.length - 1,
22
- size,
23
21
  });
24
22
  });
25
23
  return acc;
@@ -0,0 +1,15 @@
1
+ import { ScatterSeriesData } from '../../../../../../types';
2
+ import { PreparedScatterSeries } from '../../useSeries/types';
3
+ type PointData = {
4
+ x: number;
5
+ y: number;
6
+ data: ScatterSeriesData;
7
+ series: PreparedScatterSeries;
8
+ };
9
+ export type MarkerData = {
10
+ point: PointData;
11
+ active: boolean;
12
+ hovered: boolean;
13
+ };
14
+ export type PreparedScatterData = MarkerData;
15
+ export {};
@@ -17,7 +17,7 @@ export declare function getYValue(args: {
17
17
  yAxis: PreparedAxis;
18
18
  yScale: ChartScale;
19
19
  }): number;
20
- export declare const shapeKey: (d: unknown) => string | number;
20
+ export declare const shapeKey: (d: unknown) => string | -1;
21
21
  export declare function setActiveState<T extends {
22
22
  active?: boolean;
23
23
  }>(args: {
@@ -1,5 +1,3 @@
1
1
  import { SymbolType } from '../../../../constants';
2
2
  export declare const getSymbolType: (index: number) => SymbolType;
3
- export declare const getSymbol: (symbolType: SymbolType) => {
4
- draw: (context: CanvasPath, size: number) => void;
5
- };
3
+ export declare const getSymbol: (symbolType: `${SymbolType}`) => import("d3-shape").SymbolType;
@@ -96,6 +96,18 @@ const validatePieSeries = ({ series }) => {
96
96
  }
97
97
  });
98
98
  };
99
+ const validateStacking = ({ series }) => {
100
+ const availableStackingValues = ['normal', 'percent'];
101
+ if (series.stacking && !availableStackingValues.includes(series.stacking)) {
102
+ throw new ChartKitError({
103
+ code: CHARTKIT_ERROR_CODE.INVALID_DATA,
104
+ message: i18n('error', 'label_invalid-series-property', {
105
+ key: 'stacking',
106
+ values: availableStackingValues,
107
+ }),
108
+ });
109
+ }
110
+ };
99
111
  const validateSeries = (args) => {
100
112
  const { series, xAxis, yAxis } = args;
101
113
  if (!AVAILABLE_SERIES_TYPES.includes(series.type)) {
@@ -107,8 +119,13 @@ const validateSeries = (args) => {
107
119
  });
108
120
  }
109
121
  switch (series.type) {
110
- case 'bar-x':
122
+ case 'area':
111
123
  case 'bar-y':
124
+ case 'bar-x': {
125
+ validateXYSeries({ series, xAxis, yAxis });
126
+ validateStacking({ series });
127
+ break;
128
+ }
112
129
  case 'line':
113
130
  case 'scatter': {
114
131
  validateXYSeries({ series, xAxis, yAxis });
@@ -0,0 +1,4 @@
1
+ import { pieCenterText } from './pie-center-text';
2
+ export declare const CustomShapeRenderer: {
3
+ pieCenterText: typeof pieCenterText;
4
+ };
@@ -0,0 +1,4 @@
1
+ import { pieCenterText } from './pie-center-text';
2
+ export const CustomShapeRenderer = {
3
+ pieCenterText,
4
+ };
@@ -0,0 +1,7 @@
1
+ export declare function pieCenterText(text: string, options?: {
2
+ padding?: number;
3
+ }): ((args: {
4
+ series: {
5
+ innerRadius: number;
6
+ };
7
+ }) => Element | null) | undefined;
@@ -0,0 +1,23 @@
1
+ import { create } from 'd3';
2
+ import get from 'lodash/get';
3
+ import { getLabelsSize } from '../renderer/utils';
4
+ const MAX_FONT_SIZE = 64;
5
+ export function pieCenterText(text, options) {
6
+ if (!text) {
7
+ return undefined;
8
+ }
9
+ const padding = get(options, 'padding', 12);
10
+ return function (args) {
11
+ let fontSize = MAX_FONT_SIZE;
12
+ const textSize = getLabelsSize({ labels: [text], style: { fontSize: `${fontSize}px` } });
13
+ fontSize = (fontSize * (args.series.innerRadius - padding) * 2) / textSize.maxWidth;
14
+ const container = create('svg:g');
15
+ container
16
+ .append('text')
17
+ .text(text)
18
+ .attr('text-anchor', 'middle')
19
+ .attr('alignment-baseline', 'middle')
20
+ .style('font-size', `${fontSize}px`);
21
+ return container.node();
22
+ };
23
+ }
@@ -30,11 +30,11 @@ export type AreaSeries<T = any> = BaseSeries & {
30
30
  /** The name of the series (used in legend, tooltip etc) */
31
31
  name: string;
32
32
  /** Whether to stack the values of each series on top of each other.
33
- * Possible values are undefined to disable, "normal" to stack by value
33
+ * Possible values are undefined to disable, "normal" to stack by value or "percent"
34
34
  *
35
35
  * @default undefined
36
36
  * */
37
- stacking?: 'normal';
37
+ stacking?: 'normal' | 'percent';
38
38
  /** This option allows grouping series in a stacked chart */
39
39
  stackId?: string;
40
40
  /** The main color of the series (hex, rgba) */
@@ -20,10 +20,6 @@ export type LineSeriesData<T = any> = BaseSeriesData<T> & {
20
20
  /** Data label value of the point. If not specified, the y value is used. */
21
21
  label?: string | number;
22
22
  };
23
- export type LineMarkerSymbol = 'circle' | 'square';
24
- export type LineMarkerOptions = PointMarkerOptions & {
25
- symbol?: LineMarkerSymbol;
26
- };
27
23
  export type LineSeries<T = any> = BaseSeries & {
28
24
  type: typeof SeriesType.Line;
29
25
  data: LineSeriesData<T>[];
@@ -41,7 +37,7 @@ export type LineSeries<T = any> = BaseSeries & {
41
37
  symbol?: RectLegendSymbolOptions;
42
38
  };
43
39
  /** Options for the point markers of line series */
44
- marker?: LineMarkerOptions;
40
+ marker?: PointMarkerOptions;
45
41
  /** Option for line stroke style */
46
42
  dashStyle?: `${DashStyle}`;
47
43
  /** Option for line cap style */
@@ -1,3 +1,4 @@
1
+ import { SymbolType } from '../../constants';
1
2
  export type PointMarkerOptions = {
2
3
  /** Enable or disable the point marker */
3
4
  enabled?: boolean;
@@ -7,4 +8,5 @@ export type PointMarkerOptions = {
7
8
  borderColor?: string;
8
9
  /** The width of the point marker's border */
9
10
  borderWidth?: number;
11
+ symbol?: `${SymbolType}`;
10
12
  };
@@ -1,3 +1,4 @@
1
+ import { BaseType } from 'd3';
1
2
  import { SeriesType } from '../../constants';
2
3
  import type { BaseSeries, BaseSeriesData } from './base';
3
4
  import { ChartKitWidgetLegend, RectLegendSymbolOptions } from './legend';
@@ -71,4 +72,14 @@ export type PieSeries<T = any> = BaseSeries & {
71
72
  * */
72
73
  connectorCurve?: ConnectorCurve;
73
74
  };
75
+ /**
76
+ * Function for adding custom svg nodes for a series
77
+ *
78
+ * @return BaseType
79
+ * */
80
+ renderCustomShape?: (args: {
81
+ series: {
82
+ innerRadius: number;
83
+ };
84
+ }) => BaseType;
74
85
  };