@gravity-ui/charts 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. package/README.md +1 -1
  2. package/dist/cjs/components/Axis/AxisY.d.ts +1 -0
  3. package/dist/cjs/components/Axis/AxisY.js +55 -13
  4. package/dist/cjs/components/ChartInner/index.js +3 -2
  5. package/dist/cjs/components/ChartInner/useChartInnerHandlers.js +4 -0
  6. package/dist/cjs/components/Legend/index.js +1 -2
  7. package/dist/cjs/components/PlotTitle/index.js +1 -1
  8. package/dist/cjs/components/PlotTitle/styles.css +1 -1
  9. package/dist/cjs/components/Tooltip/DefaultContent.js +19 -3
  10. package/dist/cjs/constants/index.d.ts +1 -0
  11. package/dist/cjs/constants/index.js +1 -0
  12. package/dist/cjs/hooks/useChartOptions/types.d.ts +11 -1
  13. package/dist/cjs/hooks/useChartOptions/x-axis.js +1 -0
  14. package/dist/cjs/hooks/useChartOptions/y-axis.js +9 -1
  15. package/dist/cjs/hooks/useSeries/prepare-bar-x.d.ts +2 -1
  16. package/dist/cjs/hooks/useSeries/prepare-bar-x.js +2 -1
  17. package/dist/cjs/hooks/useSeries/prepare-bar-y.d.ts +2 -1
  18. package/dist/cjs/hooks/useSeries/prepare-bar-y.js +3 -1
  19. package/dist/cjs/hooks/useSeries/prepare-pie.js +2 -2
  20. package/dist/cjs/hooks/useSeries/prepare-sankey.d.ts +11 -0
  21. package/dist/cjs/hooks/useSeries/prepare-sankey.js +38 -0
  22. package/dist/cjs/hooks/useSeries/prepareSeries.js +21 -2
  23. package/dist/cjs/hooks/useSeries/types.d.ts +12 -2
  24. package/dist/cjs/hooks/useSeries/utils.js +1 -1
  25. package/dist/cjs/hooks/useShapes/bar-x/index.js +16 -2
  26. package/dist/cjs/hooks/useShapes/bar-x/prepare-data.js +2 -1
  27. package/dist/cjs/hooks/useShapes/bar-x/types.d.ts +1 -0
  28. package/dist/cjs/hooks/useShapes/bar-y/index.js +16 -2
  29. package/dist/cjs/hooks/useShapes/bar-y/prepare-data.js +2 -1
  30. package/dist/cjs/hooks/useShapes/bar-y/types.d.ts +1 -0
  31. package/dist/cjs/hooks/useShapes/index.d.ts +2 -1
  32. package/dist/cjs/hooks/useShapes/index.js +19 -0
  33. package/dist/cjs/hooks/useShapes/line/index.js +2 -2
  34. package/dist/cjs/hooks/useShapes/pie/index.js +3 -3
  35. package/dist/cjs/hooks/useShapes/pie/prepare-data.js +37 -35
  36. package/dist/cjs/hooks/useShapes/pie/types.d.ts +1 -1
  37. package/dist/cjs/hooks/useShapes/sankey/index.d.ts +12 -0
  38. package/dist/cjs/hooks/useShapes/sankey/index.js +67 -0
  39. package/dist/cjs/hooks/useShapes/sankey/prepare-data.d.ts +7 -0
  40. package/dist/cjs/hooks/useShapes/sankey/prepare-data.js +72 -0
  41. package/dist/cjs/hooks/useShapes/sankey/types.d.ts +33 -0
  42. package/dist/cjs/hooks/useShapes/sankey/types.js +1 -0
  43. package/dist/cjs/hooks/useShapes/styles.css +2 -2
  44. package/dist/cjs/hooks/useShapes/utils.d.ts +7 -2
  45. package/dist/cjs/hooks/useShapes/utils.js +22 -17
  46. package/dist/cjs/hooks/useShapes/waterfall/index.js +1 -2
  47. package/dist/cjs/types/chart/axis.d.ts +24 -0
  48. package/dist/cjs/types/chart/bar-x.d.ts +5 -0
  49. package/dist/cjs/types/chart/bar-y.d.ts +5 -0
  50. package/dist/cjs/types/chart/pie.d.ts +2 -0
  51. package/dist/cjs/types/chart/sankey.d.ts +22 -0
  52. package/dist/cjs/types/chart/sankey.js +1 -0
  53. package/dist/cjs/types/chart/series.d.ts +13 -2
  54. package/dist/cjs/types/chart/tooltip.d.ts +7 -1
  55. package/dist/cjs/types/index.d.ts +1 -0
  56. package/dist/cjs/types/index.js +1 -0
  57. package/dist/cjs/utils/chart/get-closest-data.d.ts +2 -0
  58. package/dist/cjs/utils/chart/get-closest-data.js +39 -3
  59. package/dist/cjs/utils/chart/index.js +1 -1
  60. package/dist/cjs/utils/chart/series/index.d.ts +1 -0
  61. package/dist/cjs/utils/chart/series/index.js +1 -0
  62. package/dist/cjs/utils/chart/series/line.d.ts +2 -0
  63. package/dist/cjs/utils/chart/series/line.js +17 -0
  64. package/dist/esm/components/Axis/AxisY.d.ts +1 -0
  65. package/dist/esm/components/Axis/AxisY.js +55 -13
  66. package/dist/esm/components/ChartInner/index.js +3 -2
  67. package/dist/esm/components/ChartInner/useChartInnerHandlers.js +4 -0
  68. package/dist/esm/components/Legend/index.js +1 -2
  69. package/dist/esm/components/PlotTitle/index.js +1 -1
  70. package/dist/esm/components/PlotTitle/styles.css +1 -1
  71. package/dist/esm/components/Tooltip/DefaultContent.js +19 -3
  72. package/dist/esm/constants/index.d.ts +1 -0
  73. package/dist/esm/constants/index.js +1 -0
  74. package/dist/esm/hooks/useChartOptions/types.d.ts +11 -1
  75. package/dist/esm/hooks/useChartOptions/x-axis.js +1 -0
  76. package/dist/esm/hooks/useChartOptions/y-axis.js +9 -1
  77. package/dist/esm/hooks/useSeries/prepare-bar-x.d.ts +2 -1
  78. package/dist/esm/hooks/useSeries/prepare-bar-x.js +2 -1
  79. package/dist/esm/hooks/useSeries/prepare-bar-y.d.ts +2 -1
  80. package/dist/esm/hooks/useSeries/prepare-bar-y.js +3 -1
  81. package/dist/esm/hooks/useSeries/prepare-pie.js +2 -2
  82. package/dist/esm/hooks/useSeries/prepare-sankey.d.ts +11 -0
  83. package/dist/esm/hooks/useSeries/prepare-sankey.js +38 -0
  84. package/dist/esm/hooks/useSeries/prepareSeries.js +21 -2
  85. package/dist/esm/hooks/useSeries/types.d.ts +12 -2
  86. package/dist/esm/hooks/useSeries/utils.js +1 -1
  87. package/dist/esm/hooks/useShapes/bar-x/index.js +16 -2
  88. package/dist/esm/hooks/useShapes/bar-x/prepare-data.js +2 -1
  89. package/dist/esm/hooks/useShapes/bar-x/types.d.ts +1 -0
  90. package/dist/esm/hooks/useShapes/bar-y/index.js +16 -2
  91. package/dist/esm/hooks/useShapes/bar-y/prepare-data.js +2 -1
  92. package/dist/esm/hooks/useShapes/bar-y/types.d.ts +1 -0
  93. package/dist/esm/hooks/useShapes/index.d.ts +2 -1
  94. package/dist/esm/hooks/useShapes/index.js +19 -0
  95. package/dist/esm/hooks/useShapes/line/index.js +2 -2
  96. package/dist/esm/hooks/useShapes/pie/index.js +3 -3
  97. package/dist/esm/hooks/useShapes/pie/prepare-data.js +37 -35
  98. package/dist/esm/hooks/useShapes/pie/types.d.ts +1 -1
  99. package/dist/esm/hooks/useShapes/sankey/index.d.ts +12 -0
  100. package/dist/esm/hooks/useShapes/sankey/index.js +67 -0
  101. package/dist/esm/hooks/useShapes/sankey/prepare-data.d.ts +7 -0
  102. package/dist/esm/hooks/useShapes/sankey/prepare-data.js +72 -0
  103. package/dist/esm/hooks/useShapes/sankey/types.d.ts +33 -0
  104. package/dist/esm/hooks/useShapes/sankey/types.js +1 -0
  105. package/dist/esm/hooks/useShapes/styles.css +2 -2
  106. package/dist/esm/hooks/useShapes/utils.d.ts +7 -2
  107. package/dist/esm/hooks/useShapes/utils.js +22 -17
  108. package/dist/esm/hooks/useShapes/waterfall/index.js +1 -2
  109. package/dist/esm/types/chart/axis.d.ts +24 -0
  110. package/dist/esm/types/chart/bar-x.d.ts +5 -0
  111. package/dist/esm/types/chart/bar-y.d.ts +5 -0
  112. package/dist/esm/types/chart/pie.d.ts +2 -0
  113. package/dist/esm/types/chart/sankey.d.ts +22 -0
  114. package/dist/esm/types/chart/sankey.js +1 -0
  115. package/dist/esm/types/chart/series.d.ts +13 -2
  116. package/dist/esm/types/chart/tooltip.d.ts +7 -1
  117. package/dist/esm/types/index.d.ts +1 -0
  118. package/dist/esm/types/index.js +1 -0
  119. package/dist/esm/utils/chart/get-closest-data.d.ts +2 -0
  120. package/dist/esm/utils/chart/get-closest-data.js +39 -3
  121. package/dist/esm/utils/chart/index.js +1 -1
  122. package/dist/esm/utils/chart/series/index.d.ts +1 -0
  123. package/dist/esm/utils/chart/series/index.js +1 -0
  124. package/dist/esm/utils/chart/series/line.d.ts +2 -0
  125. package/dist/esm/utils/chart/series/line.js +17 -0
  126. package/package.json +3 -1
@@ -124,7 +124,7 @@ export const prepareBarYData = (args) => {
124
124
  const sortedData = sortKey
125
125
  ? sort(measureValues, (a, b) => comparator(get(a, sortKey), get(b, sortKey)))
126
126
  : measureValues;
127
- sortedData.forEach(({ data, series: s }) => {
127
+ sortedData.forEach(({ data, series: s }, xValueIndex) => {
128
128
  let center;
129
129
  if (yAxis[0].type === 'category') {
130
130
  const bandScale = yScale;
@@ -147,6 +147,7 @@ export const prepareBarYData = (args) => {
147
147
  data,
148
148
  series: s,
149
149
  htmlElements: [],
150
+ isLastStackItem: xValueIndex === sortedData.length - 1,
150
151
  };
151
152
  stackItems.push(item);
152
153
  stackSum += width + 1;
@@ -10,4 +10,5 @@ export type PreparedBarYData = Omit<TooltipDataChunkBarX, 'series'> & {
10
10
  series: PreparedBarYSeries;
11
11
  label?: LabelData;
12
12
  htmlElements: HtmlItem[];
13
+ isLastStackItem: boolean;
13
14
  };
@@ -8,12 +8,13 @@ import type { PreparedBarXData } from './bar-x';
8
8
  import type { PreparedBarYData } from './bar-y/types';
9
9
  import type { PreparedLineData } from './line/types';
10
10
  import type { PreparedPieData } from './pie/types';
11
+ import type { PreparedSankeyData } from './sankey/types';
11
12
  import type { PreparedScatterData } from './scatter/types';
12
13
  export type { PreparedBarXData } from './bar-x';
13
14
  export type { PreparedScatterData } from './scatter/types';
14
15
  import type { PreparedWaterfallData } from './waterfall';
15
16
  import './styles.css';
16
- export type ShapeData = PreparedBarXData | PreparedBarYData | PreparedScatterData | PreparedLineData | PreparedPieData | PreparedAreaData | PreparedWaterfallData;
17
+ export type ShapeData = PreparedBarXData | PreparedBarYData | PreparedScatterData | PreparedLineData | PreparedPieData | PreparedAreaData | PreparedWaterfallData | PreparedSankeyData;
17
18
  type Args = {
18
19
  boundsWidth: number;
19
20
  boundsHeight: number;
@@ -1,5 +1,6 @@
1
1
  import React from 'react';
2
2
  import { group } from 'd3';
3
+ import { ChartError } from '../../libs';
3
4
  import { getOnlyVisibleSeries } from '../../utils';
4
5
  import { AreaSeriesShapes } from './area';
5
6
  import { prepareAreaData } from './area/prepare-data';
@@ -9,6 +10,8 @@ import { LineSeriesShapes } from './line';
9
10
  import { prepareLineData } from './line/prepare-data';
10
11
  import { PieSeriesShapes } from './pie';
11
12
  import { preparePieData } from './pie/prepare-data';
13
+ import { SankeySeriesShape } from './sankey';
14
+ import { prepareSankeyData } from './sankey/prepare-data';
12
15
  import { ScatterSeriesShape, prepareScatterData } from './scatter';
13
16
  import { TreemapSeriesShape } from './treemap';
14
17
  import { prepareTreemapData } from './treemap/prepare-data';
@@ -134,6 +137,22 @@ export const useShapes = (args) => {
134
137
  });
135
138
  acc.push(React.createElement(TreemapSeriesShape, { key: "treemap", dispatcher: dispatcher, preparedData: preparedData, seriesOptions: seriesOptions, htmlLayout: htmlLayout }));
136
139
  shapesData.push(preparedData);
140
+ break;
141
+ }
142
+ case 'sankey': {
143
+ const preparedData = prepareSankeyData({
144
+ series: chartSeries[0],
145
+ width: boundsWidth,
146
+ height: boundsHeight,
147
+ });
148
+ acc.push(React.createElement(SankeySeriesShape, { key: "sankey", dispatcher: dispatcher, preparedData: preparedData, seriesOptions: seriesOptions, htmlLayout: htmlLayout }));
149
+ shapesData.push(preparedData);
150
+ break;
151
+ }
152
+ default: {
153
+ throw new ChartError({
154
+ message: `The display method is not defined for a series with type "${seriesType}"`,
155
+ });
137
156
  }
138
157
  }
139
158
  return acc;
@@ -1,10 +1,10 @@
1
1
  import React from 'react';
2
2
  import { color, line as lineGenerator, select } from 'd3';
3
3
  import get from 'lodash/get';
4
- import { block, filterOverlappingLabels } from '../../../utils';
4
+ import { block, filterOverlappingLabels, getLineDashArray } from '../../../utils';
5
5
  import { HtmlLayer } from '../HtmlLayer';
6
6
  import { getMarkerHaloVisibility, getMarkerVisibility, renderMarker, selectMarkerHalo, selectMarkerSymbol, setMarker, } from '../marker';
7
- import { getLineDashArray, setActiveState } from '../utils';
7
+ import { setActiveState } from '../utils';
8
8
  const b = block('d3-line');
9
9
  export const LineSeriesShapes = (args) => {
10
10
  const { dispatcher, preparedData, seriesOptions, htmlLayout } = args;
@@ -30,7 +30,6 @@ export function PieSeriesShapes(args) {
30
30
  const [x, y] = pieData.center;
31
31
  return `translate(${x}, ${y})`;
32
32
  })
33
- .style('stroke', (pieData) => pieData.borderColor)
34
33
  .style('stroke-width', (pieData) => pieData.borderWidth)
35
34
  .attr('cursor', (pieData) => pieData.series.cursor);
36
35
  // Render halo appearing outside the hovered slice
@@ -46,7 +45,7 @@ export function PieSeriesShapes(args) {
46
45
  .attr('d', (d) => {
47
46
  const arcGenerator = arc()
48
47
  .innerRadius(d.data.pie.innerRadius)
49
- .outerRadius(d.data.pie.radius + d.data.pie.halo.size)
48
+ .outerRadius(d.data.radius + d.data.pie.halo.size)
50
49
  .cornerRadius(d.data.pie.borderRadius);
51
50
  return arcGenerator(d);
52
51
  })
@@ -63,11 +62,12 @@ export function PieSeriesShapes(args) {
63
62
  .attr('d', (d) => {
64
63
  const arcGenerator = arc()
65
64
  .innerRadius(d.data.pie.innerRadius)
66
- .outerRadius(d.data.pie.radius)
65
+ .outerRadius(d.data.radius)
67
66
  .cornerRadius(d.data.pie.borderRadius);
68
67
  return arcGenerator(d);
69
68
  })
70
69
  .attr('class', b('segment'))
70
+ .style('stroke', (d) => d.data.series.borderColor)
71
71
  .attr('fill', (d) => d.data.color)
72
72
  .attr('opacity', (d) => d.data.opacity);
73
73
  // render Labels
@@ -19,15 +19,13 @@ export function preparePieData(args) {
19
19
  const maxRadius = Math.min(boundsWidth, boundsHeight) / 2;
20
20
  const groupedPieSeries = group(preparedSeries, (pieSeries) => pieSeries.stackId);
21
21
  const prepareItem = (stackId, items) => {
22
- var _a, _b, _c;
22
+ var _a;
23
23
  const series = items[0];
24
- const { center, borderWidth, borderColor, borderRadius, radius: seriesRadius, innerRadius: seriesInnerRadius, dataLabels, } = series;
25
- const radius = (_a = calculateNumericProperty({ value: seriesRadius, base: maxRadius })) !== null && _a !== void 0 ? _a : maxRadius;
24
+ const { center, borderWidth, borderColor, borderRadius, innerRadius: seriesInnerRadius, dataLabels, } = series;
26
25
  const data = {
27
26
  id: stackId,
28
27
  center: getCenter(boundsWidth, boundsHeight, center),
29
- innerRadius: (_b = calculateNumericProperty({ value: seriesInnerRadius, base: radius })) !== null && _b !== void 0 ? _b : 0,
30
- radius,
28
+ innerRadius: 0,
31
29
  segments: [],
32
30
  labels: [],
33
31
  htmlLabels: [],
@@ -43,7 +41,19 @@ export function preparePieData(args) {
43
41
  size: series.states.hover.halo.size,
44
42
  },
45
43
  };
44
+ const { maxHeight: labelHeight } = getLabelsSize({
45
+ labels: ['Some Label'],
46
+ style: dataLabels.style,
47
+ });
48
+ let segmentMaxRadius = 0;
46
49
  const segments = items.map((item) => {
50
+ var _a;
51
+ let maxSegmentRadius = maxRadius;
52
+ if (dataLabels.enabled) {
53
+ maxSegmentRadius -= dataLabels.distance + dataLabels.connectorPadding + labelHeight;
54
+ }
55
+ const segmentRadius = (_a = calculateNumericProperty({ value: item.radius, base: maxSegmentRadius })) !== null && _a !== void 0 ? _a : maxSegmentRadius;
56
+ segmentMaxRadius = Math.max(segmentMaxRadius, segmentRadius);
47
57
  return {
48
58
  value: item.value,
49
59
  color: item.color,
@@ -52,19 +62,12 @@ export function preparePieData(args) {
52
62
  hovered: false,
53
63
  active: true,
54
64
  pie: data,
65
+ radius: segmentRadius,
55
66
  };
56
67
  });
57
68
  data.segments = pieGenerator(segments);
58
- if (dataLabels.enabled) {
59
- const { style, connectorPadding, distance } = dataLabels;
60
- const { maxHeight: labelHeight } = getLabelsSize({ labels: ['Some Label'], style });
61
- const minSegmentRadius = maxRadius - distance - connectorPadding - labelHeight;
62
- if (data.radius > minSegmentRadius) {
63
- data.radius = minSegmentRadius;
64
- data.innerRadius =
65
- (_c = calculateNumericProperty({ value: seriesInnerRadius, base: data.radius })) !== null && _c !== void 0 ? _c : 0;
66
- }
67
- }
69
+ data.innerRadius =
70
+ (_a = calculateNumericProperty({ value: seriesInnerRadius, base: segmentMaxRadius })) !== null && _a !== void 0 ? _a : 0;
68
71
  return data;
69
72
  };
70
73
  const prepareLabels = (prepareLabelsArgs) => {
@@ -84,20 +87,17 @@ export function preparePieData(args) {
84
87
  const { style, connectorPadding, distance } = dataLabels;
85
88
  const { maxHeight: labelHeight } = getLabelsSize({ labels: ['Some Label'], style });
86
89
  const connectorStartPointGenerator = arc()
87
- .innerRadius(data.radius)
88
- .outerRadius(data.radius);
89
- const connectorMidPointRadius = data.radius + distance / 2;
90
+ .innerRadius((d) => d.data.radius)
91
+ .outerRadius((d) => d.data.radius);
90
92
  const connectorMidPointGenerator = arc()
91
- .innerRadius(connectorMidPointRadius)
92
- .outerRadius(connectorMidPointRadius);
93
- const connectorArcRadius = data.radius + distance;
93
+ .innerRadius((d) => d.data.radius + distance / 2)
94
+ .outerRadius((d) => d.data.radius + distance / 2);
94
95
  const connectorEndPointGenerator = arc()
95
- .innerRadius(connectorArcRadius)
96
- .outerRadius(connectorArcRadius);
97
- const labelArcRadius = connectorArcRadius + connectorPadding;
96
+ .innerRadius((d) => d.data.radius + distance)
97
+ .outerRadius((d) => d.data.radius + distance);
98
98
  const labelArcGenerator = arc()
99
- .innerRadius(labelArcRadius)
100
- .outerRadius(labelArcRadius);
99
+ .innerRadius((d) => d.data.radius + distance + connectorPadding)
100
+ .outerRadius((d) => d.data.radius + distance + connectorPadding);
101
101
  series.forEach((d, index) => {
102
102
  const prevLabel = labels[labels.length - 1];
103
103
  const text = String(d.data.label || d.data.value);
@@ -179,8 +179,8 @@ export function preparePieData(args) {
179
179
  }
180
180
  if (shouldUseHtml) {
181
181
  htmlLabels.push({
182
- x: boundsWidth / 2 + label.x,
183
- y: boundsHeight / 2 + label.y,
182
+ x: data.center[0] + label.x,
183
+ y: Math.max(0, data.center[1] + label.y),
184
184
  content: label.text,
185
185
  size: label.size,
186
186
  });
@@ -207,19 +207,21 @@ export function preparePieData(args) {
207
207
  data,
208
208
  series: items,
209
209
  });
210
- const allPreparedLabels = [...preparedLabels.labels, ...preparedLabels.htmlLabels];
211
- const top = Math.min(data.center[1] - data.radius, ...allPreparedLabels.map((l) => l.y + data.center[1]));
212
- const bottom = Math.max(data.center[1] + data.radius, ...allPreparedLabels.map((l) => data.center[1] + l.y + l.size.height));
210
+ const segmentMaxRadius = Math.max(...data.segments.map((s) => s.data.radius));
211
+ const top = Math.min(data.center[1] - segmentMaxRadius, ...preparedLabels.labels.map((l) => l.y + data.center[1]), ...preparedLabels.htmlLabels.map((l) => l.y));
212
+ const bottom = Math.max(data.center[1] + segmentMaxRadius, ...preparedLabels.labels.map((l) => l.y + data.center[1] + l.size.height), ...preparedLabels.htmlLabels.map((l) => l.y + l.size.height));
213
213
  const topAdjustment = Math.floor(top - data.halo.size);
214
214
  if (topAdjustment > 0) {
215
- // should adjust top position and height
216
- data.radius += topAdjustment / 2;
215
+ data.segments.forEach((s) => {
216
+ s.data.radius += topAdjustment / 2;
217
+ });
217
218
  data.center[1] -= topAdjustment / 2;
218
219
  }
219
220
  const bottomAdjustment = Math.floor(boundsHeight - bottom - data.halo.size);
220
221
  if (bottomAdjustment > 0) {
221
- // should adjust position and radius
222
- data.radius += bottomAdjustment / 2;
222
+ data.segments.forEach((s) => {
223
+ s.data.radius += bottomAdjustment / 2;
224
+ });
223
225
  data.center[1] += bottomAdjustment / 2;
224
226
  }
225
227
  const { labels, htmlLabels, connectors } = prepareLabels({
@@ -9,6 +9,7 @@ export type SegmentData = {
9
9
  hovered: boolean;
10
10
  active: boolean;
11
11
  pie: PreparedPieData;
12
+ radius: number;
12
13
  };
13
14
  export type PieLabelData = LabelData & {
14
15
  segment: SegmentData;
@@ -25,7 +26,6 @@ export type PreparedPieData = {
25
26
  labels: PieLabelData[];
26
27
  connectors: PieConnectorData[];
27
28
  center: [number, number];
28
- radius: number;
29
29
  innerRadius: number;
30
30
  borderRadius: number;
31
31
  borderWidth: number;
@@ -0,0 +1,12 @@
1
+ import React from 'react';
2
+ import type { Dispatch } from 'd3';
3
+ import type { PreparedSeriesOptions } from '../../useSeries/types';
4
+ import type { PreparedSankeyData } from './types';
5
+ type ShapeProps = {
6
+ dispatcher: Dispatch<object>;
7
+ preparedData: PreparedSankeyData;
8
+ seriesOptions: PreparedSeriesOptions;
9
+ htmlLayout: HTMLElement | null;
10
+ };
11
+ export declare const SankeySeriesShape: (props: ShapeProps) => React.JSX.Element;
12
+ export {};
@@ -0,0 +1,67 @@
1
+ import React from 'react';
2
+ import { select } from 'd3';
3
+ import { block } from '../../../utils';
4
+ import { HtmlLayer } from '../HtmlLayer';
5
+ const b = block('sankey');
6
+ export const SankeySeriesShape = (props) => {
7
+ const { dispatcher, preparedData, seriesOptions, htmlLayout } = props;
8
+ const hoveredDataRef = React.useRef(null);
9
+ const ref = React.useRef(null);
10
+ React.useEffect(() => {
11
+ if (!ref.current) {
12
+ return () => { };
13
+ }
14
+ const svgElement = select(ref.current);
15
+ svgElement.selectAll('*').remove();
16
+ // nodes
17
+ svgElement
18
+ .append('g')
19
+ .selectAll()
20
+ .data(preparedData.nodes)
21
+ .join('rect')
22
+ .attr('x', (d) => d.x0)
23
+ .attr('y', (d) => d.y0)
24
+ .attr('height', (d) => d.y1 - d.y0)
25
+ .attr('width', (d) => d.x1 - d.x0)
26
+ .attr('fill', (d) => d.color);
27
+ // links
28
+ svgElement
29
+ .append('g')
30
+ .attr('fill', 'none')
31
+ .selectAll()
32
+ .data(preparedData.links)
33
+ .join('g')
34
+ .append('path')
35
+ .attr('stroke-opacity', (d) => d.opacity)
36
+ .attr('d', (d) => d.path)
37
+ .attr('stroke', (d) => d.color)
38
+ .attr('stroke-width', (d) => d.strokeWidth);
39
+ // dataLabels
40
+ svgElement
41
+ .append('g')
42
+ .selectAll()
43
+ .data(preparedData.labels)
44
+ .join('text')
45
+ .text((d) => d.text)
46
+ .attr('class', b('label'))
47
+ .attr('x', (d) => d.x)
48
+ .attr('y', (d) => d.y)
49
+ .attr('dy', '0.35em')
50
+ .attr('text-anchor', (d) => d.textAnchor)
51
+ .attr('fill', (d) => { var _a; return (_a = d.style.fontColor) !== null && _a !== void 0 ? _a : null; });
52
+ const eventName = `hover-shape.sankey`;
53
+ function handleShapeHover(data) {
54
+ hoveredDataRef.current = data;
55
+ }
56
+ if (hoveredDataRef.current !== null) {
57
+ handleShapeHover(hoveredDataRef.current);
58
+ }
59
+ dispatcher.on(eventName, handleShapeHover);
60
+ return () => {
61
+ dispatcher.on(eventName, null);
62
+ };
63
+ }, [dispatcher, preparedData, seriesOptions]);
64
+ return (React.createElement(React.Fragment, null,
65
+ React.createElement("g", { ref: ref, className: b() }),
66
+ React.createElement(HtmlLayer, { preparedData: preparedData, htmlLayout: htmlLayout })));
67
+ };
@@ -0,0 +1,7 @@
1
+ import type { PreparedSankeySeries } from '../../useSeries/types';
2
+ import type { PreparedSankeyData } from './types';
3
+ export declare function prepareSankeyData(args: {
4
+ series: PreparedSankeySeries;
5
+ width: number;
6
+ height: number;
7
+ }): PreparedSankeyData;
@@ -0,0 +1,72 @@
1
+ import { sankey, sankeyLinkHorizontal } from 'd3-sankey';
2
+ export function prepareSankeyData(args) {
3
+ const { series, width, height } = args;
4
+ const htmlElements = [];
5
+ const sankeyGenerator = sankey()
6
+ .nodeId((d) => d.name)
7
+ .nodeSort((d) => d.index)
8
+ .nodeWidth(15)
9
+ .nodePadding(10)
10
+ .extent([
11
+ [1, 5],
12
+ [width - 1, height - 5],
13
+ ]);
14
+ const { nodes, links } = sankeyGenerator({
15
+ nodes: series.data,
16
+ links: series.data.reduce((acc, item) => {
17
+ item.links.forEach((l) => {
18
+ const target = series.data.find((d) => d.name === l.name);
19
+ if (target) {
20
+ acc.push({
21
+ source: item,
22
+ target,
23
+ value: l.value,
24
+ });
25
+ }
26
+ });
27
+ return acc;
28
+ }, []),
29
+ });
30
+ const sankeyNodes = nodes.map((node) => {
31
+ var _a, _b, _c, _d, _e, _f;
32
+ return {
33
+ x0: (_a = node.x0) !== null && _a !== void 0 ? _a : 0,
34
+ x1: (_b = node.x1) !== null && _b !== void 0 ? _b : 0,
35
+ y0: (_c = node.y0) !== null && _c !== void 0 ? _c : 0,
36
+ y1: (_d = node.y1) !== null && _d !== void 0 ? _d : 0,
37
+ color: (_e = node.color) !== null && _e !== void 0 ? _e : '',
38
+ data: series.data[(_f = node.index) !== null && _f !== void 0 ? _f : 0],
39
+ };
40
+ });
41
+ const sankeyLinks = links.map((d) => {
42
+ var _a, _b;
43
+ return {
44
+ opacity: 0.75,
45
+ color: (_a = d.source.color) !== null && _a !== void 0 ? _a : '',
46
+ path: sankeyLinkHorizontal()(d),
47
+ strokeWidth: Math.max(1, (_b = d.width) !== null && _b !== void 0 ? _b : 0),
48
+ source: d.source,
49
+ target: d.target,
50
+ value: d.value,
51
+ };
52
+ });
53
+ const dataLabels = [];
54
+ if (series.dataLabels.enabled) {
55
+ const labels = nodes.map((d) => {
56
+ var _a, _b, _c, _d;
57
+ const x0 = (_a = d.x0) !== null && _a !== void 0 ? _a : 0;
58
+ const x1 = (_b = d.x1) !== null && _b !== void 0 ? _b : 0;
59
+ const y0 = (_c = d.y0) !== null && _c !== void 0 ? _c : 0;
60
+ const y1 = (_d = d.y1) !== null && _d !== void 0 ? _d : 0;
61
+ return {
62
+ text: d.name,
63
+ x: x0 < width / 2 ? x1 + 6 : x0 - 6,
64
+ y: (y1 + y0) / 2,
65
+ textAnchor: x0 < width / 2 ? 'start' : 'end',
66
+ style: series.dataLabels.style,
67
+ };
68
+ });
69
+ dataLabels.push(...labels);
70
+ }
71
+ return { series, nodes: sankeyNodes, links: sankeyLinks, htmlElements, labels: dataLabels };
72
+ }
@@ -0,0 +1,33 @@
1
+ import type { BaseTextStyle, HtmlItem, SankeySeriesData } from '../../../types';
2
+ import type { PreparedSankeySeries } from '../../useSeries/types';
3
+ export type SankeyDataLabel = {
4
+ text: string;
5
+ x: number;
6
+ y: number;
7
+ textAnchor: 'start' | 'end';
8
+ style: BaseTextStyle;
9
+ };
10
+ export type SankeyNode = {
11
+ x0: number;
12
+ x1: number;
13
+ y0: number;
14
+ y1: number;
15
+ color: string;
16
+ data: SankeySeriesData;
17
+ };
18
+ export type SankeyLink = {
19
+ opacity: number;
20
+ color: string;
21
+ path: string | null;
22
+ strokeWidth: number;
23
+ source: SankeySeriesData;
24
+ target: SankeySeriesData;
25
+ value: number;
26
+ };
27
+ export type PreparedSankeyData = {
28
+ series: PreparedSankeySeries;
29
+ htmlElements: HtmlItem[];
30
+ nodes: SankeyNode[];
31
+ links: SankeyLink[];
32
+ labels: SankeyDataLabel[];
33
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -12,12 +12,12 @@
12
12
  alignment-baseline: before-edge;
13
13
  }
14
14
 
15
- .gcharts-d3-bar-x__label {
15
+ .gcharts-bar-x__label {
16
16
  user-select: none;
17
17
  fill: var(--g-color-text-complementary);
18
18
  }
19
19
 
20
- .gcharts-d3-bar-y__label {
20
+ .gcharts-bar-y__label {
21
21
  user-select: none;
22
22
  fill: var(--g-color-text-complementary);
23
23
  alignment-baseline: after-edge;
@@ -1,5 +1,4 @@
1
1
  import type { BaseType } from 'd3';
2
- import type { DashStyle } from '../../constants';
3
2
  import type { BasicInactiveState } from '../../types';
4
3
  import type { ChartScale } from '../useAxisScales';
5
4
  import type { PreparedAxis } from '../useChartOptions/types';
@@ -26,4 +25,10 @@ export declare function setActiveState<T extends {
26
25
  state: BasicInactiveState | undefined;
27
26
  active: boolean;
28
27
  }): T;
29
- export declare function getLineDashArray(dashStyle: DashStyle, strokeWidth?: number): string;
28
+ export declare function getRectPath(args: {
29
+ x: number;
30
+ y: number;
31
+ width: number;
32
+ height: number;
33
+ borderRadius?: number | number[];
34
+ }): import("d3-path").Path;
@@ -1,4 +1,4 @@
1
- import { select } from 'd3';
1
+ import { path, select } from 'd3';
2
2
  import get from 'lodash/get';
3
3
  import { getDataCategoryValue } from '../../utils';
4
4
  export function getXValue(args) {
@@ -36,20 +36,25 @@ export function setActiveState(args) {
36
36
  }
37
37
  return datum;
38
38
  }
39
- export function getLineDashArray(dashStyle, strokeWidth = 2) {
40
- const value = dashStyle.toLowerCase();
41
- const arrayValue = value
42
- .replace('shortdashdotdot', '3,1,1,1,1,1,')
43
- .replace('shortdashdot', '3,1,1,1')
44
- .replace('shortdot', '1,1,')
45
- .replace('shortdash', '3,1,')
46
- .replace('longdash', '8,3,')
47
- .replace(/dot/g, '1,3,')
48
- .replace('dash', '4,3,')
49
- .replace(/,$/, '')
50
- .split(',')
51
- .map((part) => {
52
- return `${parseInt(part, 10) * strokeWidth}`;
53
- });
54
- return arrayValue.join(',').replace(/NaN/g, 'none');
39
+ export function getRectPath(args) {
40
+ const { x, y, width, height, borderRadius = 0 } = args;
41
+ const borderRadiuses = typeof borderRadius === 'number' ? new Array(4).fill(borderRadius) : borderRadius;
42
+ const [borderRadiusTopLeft = 0, borderRadiusTopRight = 0, borderRadiusBottomRight = 0, borderRadiusBottomLeft = 0,] = borderRadiuses !== null && borderRadiuses !== void 0 ? borderRadiuses : [];
43
+ const p = path();
44
+ let startAngle = -Math.PI / 2;
45
+ const angle = Math.PI / 2;
46
+ p.moveTo(x + borderRadiusTopLeft, y);
47
+ p.lineTo(x + width - borderRadiusTopRight, y);
48
+ p.arc(x + width - borderRadiusTopRight, y + borderRadiusTopRight, borderRadiusTopRight, startAngle, startAngle + angle);
49
+ startAngle += angle;
50
+ p.lineTo(x + width, y + height - borderRadiusBottomRight);
51
+ p.arc(x + width - borderRadiusBottomRight, y + height - borderRadiusBottomRight, borderRadiusBottomRight, startAngle, startAngle + angle);
52
+ startAngle += angle;
53
+ p.lineTo(x + borderRadiusBottomLeft, y + height);
54
+ p.arc(x + borderRadiusBottomLeft, y + height - borderRadiusBottomLeft, borderRadiusBottomLeft, startAngle, startAngle + angle);
55
+ startAngle += angle;
56
+ p.lineTo(x, y + borderRadiusTopLeft);
57
+ p.arc(x + borderRadiusTopLeft, y + borderRadiusTopLeft, borderRadiusTopLeft, startAngle, startAngle + angle);
58
+ p.closePath();
59
+ return p;
55
60
  }
@@ -2,9 +2,8 @@ import React from 'react';
2
2
  import { color, line as lineGenerator, select } from 'd3';
3
3
  import get from 'lodash/get';
4
4
  import { DashStyle } from '../../../constants';
5
- import { block, filterOverlappingLabels, getWaterfallPointColor } from '../../../utils';
5
+ import { block, filterOverlappingLabels, getLineDashArray, getWaterfallPointColor, } from '../../../utils';
6
6
  import { HtmlLayer } from '../HtmlLayer';
7
- import { getLineDashArray } from '../utils';
8
7
  export { prepareWaterfallData } from './prepare-data';
9
8
  export * from './types';
10
9
  const b = block('d3-waterfall');
@@ -1,3 +1,4 @@
1
+ import type { DashStyle } from 'src/constants';
1
2
  import type { FormatNumberOptions } from '../formatter';
2
3
  import type { BaseTextStyle } from './base';
3
4
  export type ChartAxisType = 'category' | 'datetime' | 'linear' | 'logarithmic';
@@ -75,6 +76,27 @@ export interface ChartAxis {
75
76
  }
76
77
  export interface ChartXAxis extends ChartAxis {
77
78
  }
79
+ export interface AxisPlotLine {
80
+ /** The position of the line in axis units. */
81
+ value?: number;
82
+ /** The color of the plot line (hex, rgba). */
83
+ color?: string;
84
+ /** Pixel width of the plot line.
85
+ *
86
+ * @default 1
87
+ * */
88
+ width?: number;
89
+ /** Option for line stroke style. */
90
+ dashStyle?: `${DashStyle}`;
91
+ /**
92
+ * Individual opacity for the line.
93
+ *
94
+ * @default 1
95
+ * */
96
+ opacity?: number;
97
+ /** Place the line behind or above the chart. */
98
+ layerPlacement?: 'before' | 'after';
99
+ }
78
100
  export interface ChartYAxis extends ChartAxis {
79
101
  /** Axis location.
80
102
  * Possible values - 'left' and 'right'.
@@ -83,4 +105,6 @@ export interface ChartYAxis extends ChartAxis {
83
105
  /** Property for splitting charts. Determines which area the axis is located in.
84
106
  * */
85
107
  plotIndex?: number;
108
+ /** An array of lines stretching across the plot area, marking a specific value */
109
+ plotLines?: AxisPlotLine[];
86
110
  }
@@ -36,6 +36,11 @@ export interface BarXSeries<T = MeaningfulAny> extends BaseSeries {
36
36
  name: string;
37
37
  /** The main color of the series (hex, rgba) */
38
38
  color?: string;
39
+ /**
40
+ * The corner radius of the border surrounding each bar.
41
+ * @default 0
42
+ */
43
+ borderRadius?: number;
39
44
  /** Whether to stack the values of each series on top of each other.
40
45
  * Possible values are undefined to disable, "normal" to stack by value or "percent"
41
46
  *
@@ -29,6 +29,11 @@ export interface BarYSeries<T = MeaningfulAny> extends BaseSeries {
29
29
  name: string;
30
30
  /** The main color of the series (hex, rgba) */
31
31
  color?: string;
32
+ /**
33
+ * The corner radius of the border surrounding each bar.
34
+ * @default 0
35
+ */
36
+ borderRadius?: number;
32
37
  /** Whether to stack the values of each series on top of each other.
33
38
  * Possible values are undefined to disable, "normal" to stack by value or "percent"
34
39
  *
@@ -14,6 +14,8 @@ export interface PieSeriesData<T = MeaningfulAny> extends BaseSeriesData<T> {
14
14
  label?: string;
15
15
  /** Individual opacity for the pie segment. */
16
16
  opacity?: number;
17
+ /** The individual radius of the pie segment. The default value is series.radius */
18
+ radius?: string | number;
17
19
  }
18
20
  export type ConnectorShape = 'straight-line' | 'polyline';
19
21
  export type ConnectorCurve = 'linear' | 'basic';