@gravity-ui/chartkit 5.14.1 → 5.16.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 (67) hide show
  1. package/build/plugins/d3/renderer/components/Chart.js +5 -0
  2. package/build/plugins/d3/renderer/components/Legend.js +129 -66
  3. package/build/plugins/d3/renderer/components/styles.css +16 -0
  4. package/build/plugins/d3/renderer/constants/defaults/legend.d.ts +12 -4
  5. package/build/plugins/d3/renderer/constants/defaults/legend.js +4 -0
  6. package/build/plugins/d3/renderer/hooks/useSeries/prepare-area.js +1 -0
  7. package/build/plugins/d3/renderer/hooks/useSeries/prepare-bar-x.js +1 -0
  8. package/build/plugins/d3/renderer/hooks/useSeries/prepare-bar-y.js +8 -6
  9. package/build/plugins/d3/renderer/hooks/useSeries/prepare-legend.js +58 -11
  10. package/build/plugins/d3/renderer/hooks/useSeries/prepare-line.js +1 -0
  11. package/build/plugins/d3/renderer/hooks/useSeries/prepare-pie.js +1 -0
  12. package/build/plugins/d3/renderer/hooks/useSeries/prepare-treemap.js +1 -0
  13. package/build/plugins/d3/renderer/hooks/useSeries/prepare-waterfall.js +1 -0
  14. package/build/plugins/d3/renderer/hooks/useSeries/types.d.ts +24 -1
  15. package/build/plugins/d3/renderer/hooks/useShapes/HtmlLayer.d.ts +8 -0
  16. package/build/plugins/d3/renderer/hooks/useShapes/HtmlLayer.js +22 -0
  17. package/build/plugins/d3/renderer/hooks/useShapes/area/index.d.ts +1 -0
  18. package/build/plugins/d3/renderer/hooks/useShapes/area/index.js +5 -2
  19. package/build/plugins/d3/renderer/hooks/useShapes/area/prepare-data.js +18 -3
  20. package/build/plugins/d3/renderer/hooks/useShapes/area/types.d.ts +2 -1
  21. package/build/plugins/d3/renderer/hooks/useShapes/bar-x/index.d.ts +1 -0
  22. package/build/plugins/d3/renderer/hooks/useShapes/bar-x/index.js +5 -2
  23. package/build/plugins/d3/renderer/hooks/useShapes/bar-x/prepare-data.js +21 -4
  24. package/build/plugins/d3/renderer/hooks/useShapes/bar-x/types.d.ts +2 -1
  25. package/build/plugins/d3/renderer/hooks/useShapes/bar-y/index.d.ts +1 -0
  26. package/build/plugins/d3/renderer/hooks/useShapes/bar-y/index.js +18 -23
  27. package/build/plugins/d3/renderer/hooks/useShapes/bar-y/prepare-data.js +44 -3
  28. package/build/plugins/d3/renderer/hooks/useShapes/bar-y/types.d.ts +3 -0
  29. package/build/plugins/d3/renderer/hooks/useShapes/index.d.ts +1 -0
  30. package/build/plugins/d3/renderer/hooks/useShapes/index.js +9 -9
  31. package/build/plugins/d3/renderer/hooks/useShapes/line/index.d.ts +1 -0
  32. package/build/plugins/d3/renderer/hooks/useShapes/line/index.js +5 -2
  33. package/build/plugins/d3/renderer/hooks/useShapes/line/prepare-data.js +17 -1
  34. package/build/plugins/d3/renderer/hooks/useShapes/line/types.d.ts +2 -1
  35. package/build/plugins/d3/renderer/hooks/useShapes/pie/index.d.ts +1 -0
  36. package/build/plugins/d3/renderer/hooks/useShapes/pie/index.js +10 -14
  37. package/build/plugins/d3/renderer/hooks/useShapes/pie/prepare-data.js +30 -12
  38. package/build/plugins/d3/renderer/hooks/useShapes/pie/types.d.ts +7 -5
  39. package/build/plugins/d3/renderer/hooks/useShapes/scatter/index.d.ts +1 -0
  40. package/build/plugins/d3/renderer/hooks/useShapes/scatter/index.js +5 -2
  41. package/build/plugins/d3/renderer/hooks/useShapes/scatter/prepare-data.js +1 -0
  42. package/build/plugins/d3/renderer/hooks/useShapes/scatter/types.d.ts +2 -0
  43. package/build/plugins/d3/renderer/hooks/useShapes/treemap/index.d.ts +1 -0
  44. package/build/plugins/d3/renderer/hooks/useShapes/treemap/index.js +5 -2
  45. package/build/plugins/d3/renderer/hooks/useShapes/treemap/prepare-data.js +1 -1
  46. package/build/plugins/d3/renderer/hooks/useShapes/treemap/types.d.ts +2 -0
  47. package/build/plugins/d3/renderer/hooks/useShapes/waterfall/index.d.ts +1 -0
  48. package/build/plugins/d3/renderer/hooks/useShapes/waterfall/index.js +5 -2
  49. package/build/plugins/d3/renderer/hooks/useShapes/waterfall/prepare-data.js +1 -0
  50. package/build/plugins/d3/renderer/hooks/useShapes/waterfall/types.d.ts +2 -1
  51. package/build/plugins/d3/renderer/types/index.d.ts +8 -0
  52. package/build/plugins/d3/renderer/utils/axis-generators/bottom.d.ts +5 -4
  53. package/build/plugins/d3/renderer/utils/axis-generators/bottom.js +11 -7
  54. package/build/plugins/d3/renderer/utils/axis.d.ts +1 -1
  55. package/build/plugins/d3/renderer/utils/axis.js +1 -1
  56. package/build/plugins/d3/renderer/utils/color.d.ts +10 -0
  57. package/build/plugins/d3/renderer/utils/color.js +43 -0
  58. package/build/plugins/d3/renderer/utils/index.d.ts +2 -0
  59. package/build/plugins/d3/renderer/utils/index.js +2 -0
  60. package/build/plugins/d3/renderer/utils/legend.d.ts +8 -0
  61. package/build/plugins/d3/renderer/utils/legend.js +23 -0
  62. package/build/plugins/d3/renderer/utils/text.d.ts +2 -1
  63. package/build/plugins/d3/renderer/utils/text.js +32 -10
  64. package/build/types/widget-data/bar-x.d.ts +1 -1
  65. package/build/types/widget-data/base.d.ts +7 -0
  66. package/build/types/widget-data/legend.d.ts +24 -0
  67. package/package.json +2 -2
@@ -1,6 +1,6 @@
1
- import { arc, group } from 'd3';
1
+ import { arc, group, line as lineGenerator } from 'd3';
2
2
  import { calculateNumericProperty, getLabelsSize, getLeftPosition, isLabelsOverlapping, } from '../../../utils';
3
- import { pieGenerator } from './utils';
3
+ import { getCurveFactory, pieGenerator } from './utils';
4
4
  const FULL_CIRCLE = Math.PI * 2;
5
5
  const getCenter = (boundsWidth, boundsHeight, center) => {
6
6
  var _a, _b;
@@ -30,6 +30,7 @@ export function preparePieData(args) {
30
30
  radius,
31
31
  segments: [],
32
32
  labels: [],
33
+ connectors: [],
33
34
  borderColor,
34
35
  borderWidth,
35
36
  borderRadius,
@@ -40,6 +41,7 @@ export function preparePieData(args) {
40
41
  opacity: series.states.hover.halo.opacity,
41
42
  size: series.states.hover.halo.size,
42
43
  },
44
+ htmlElements: [],
43
45
  };
44
46
  const segments = items.map((item) => {
45
47
  return {
@@ -53,6 +55,11 @@ export function preparePieData(args) {
53
55
  };
54
56
  });
55
57
  data.segments = pieGenerator(segments);
58
+ let line = lineGenerator();
59
+ const curveFactory = getCurveFactory(data);
60
+ if (curveFactory) {
61
+ line = line.curve(curveFactory);
62
+ }
56
63
  if (dataLabels.enabled) {
57
64
  const { style, connectorPadding, distance } = dataLabels;
58
65
  const { maxHeight: labelHeight } = getLabelsSize({ labels: ['Some Label'], style });
@@ -81,15 +88,17 @@ export function preparePieData(args) {
81
88
  items.forEach((d, index) => {
82
89
  const prevLabel = labels[labels.length - 1];
83
90
  const text = String(d.data.label || d.data.value);
84
- const labelSize = getLabelsSize({ labels: [text], style });
91
+ const shouldUseHtml = dataLabels.html;
92
+ const labelSize = getLabelsSize({ labels: [text], style, html: shouldUseHtml });
85
93
  const labelWidth = labelSize.maxWidth;
86
94
  const relatedSegment = data.segments[index];
87
95
  const getLabelPosition = (angle) => {
88
96
  let [x, y] = labelArcGenerator.centroid(Object.assign(Object.assign({}, relatedSegment), { startAngle: angle, endAngle: angle }));
89
- x = Math.max(-boundsWidth / 2, x);
90
- if (y < 0) {
91
- y -= labelHeight;
97
+ y = y < 0 ? y - labelHeight : y;
98
+ if (shouldUseHtml) {
99
+ x = x < 0 ? x - labelWidth : x;
92
100
  }
101
+ x = Math.max(-boundsWidth / 2, x);
93
102
  return [x, y];
94
103
  };
95
104
  const getConnectorPoints = (angle) => {
@@ -114,10 +123,6 @@ export function preparePieData(args) {
114
123
  textAnchor: midAngle < Math.PI ? 'start' : 'end',
115
124
  series: { id: d.id },
116
125
  active: true,
117
- connector: {
118
- points: getConnectorPoints(midAngle),
119
- color: relatedSegment.data.color,
120
- },
121
126
  segment: relatedSegment.data,
122
127
  angle: midAngle,
123
128
  };
@@ -138,7 +143,6 @@ export function preparePieData(args) {
138
143
  const [newX, newY] = getLabelPosition(newAngle);
139
144
  label.x = newX;
140
145
  label.y = newY;
141
- label.connector.points = getConnectorPoints(newAngle);
142
146
  if (!isLabelsOverlapping(prevLabel, label, dataLabels.padding)) {
143
147
  shouldAdjustAngle = false;
144
148
  overlap = false;
@@ -158,7 +162,21 @@ export function preparePieData(args) {
158
162
  label.maxWidth = label.size.width - (right - boundsWidth / 2);
159
163
  }
160
164
  }
161
- labels.push(label);
165
+ if (shouldUseHtml) {
166
+ data.htmlElements.push({
167
+ x: boundsWidth / 2 + label.x,
168
+ y: boundsHeight / 2 + label.y,
169
+ content: label.text,
170
+ });
171
+ }
172
+ else {
173
+ labels.push(label);
174
+ }
175
+ const connector = {
176
+ path: line(getConnectorPoints(midAngle)),
177
+ color: relatedSegment.data.color,
178
+ };
179
+ data.connectors.push(connector);
162
180
  }
163
181
  });
164
182
  data.labels = labels;
@@ -1,6 +1,6 @@
1
1
  import type { PieArcDatum } from 'd3';
2
2
  import { ConnectorCurve } from '../../../../../../types';
3
- import { LabelData } from '../../../types';
3
+ import { HtmlItem, LabelData } from '../../../types';
4
4
  import { PreparedPieSeries } from '../../useSeries/types';
5
5
  export type SegmentData = {
6
6
  value: number;
@@ -12,18 +12,19 @@ export type SegmentData = {
12
12
  pie: PreparedPieData;
13
13
  };
14
14
  export type PieLabelData = LabelData & {
15
- connector: {
16
- points: [number, number][];
17
- color: string;
18
- };
19
15
  segment: SegmentData;
20
16
  angle: number;
21
17
  maxWidth: number;
22
18
  };
19
+ export type PieConnectorData = {
20
+ path: string | null;
21
+ color: string;
22
+ };
23
23
  export type PreparedPieData = {
24
24
  id: string;
25
25
  segments: PieArcDatum<SegmentData>[];
26
26
  labels: PieLabelData[];
27
+ connectors: PieConnectorData[];
27
28
  center: [number, number];
28
29
  radius: number;
29
30
  innerRadius: number;
@@ -37,4 +38,5 @@ export type PreparedPieData = {
37
38
  opacity: number;
38
39
  size: number;
39
40
  };
41
+ htmlElements: HtmlItem[];
40
42
  };
@@ -7,5 +7,6 @@ type ScatterSeriesShapeProps = {
7
7
  dispatcher: Dispatch<object>;
8
8
  preparedData: PreparedScatterData[];
9
9
  seriesOptions: PreparedSeriesOptions;
10
+ htmlLayout: HTMLElement | null;
10
11
  };
11
12
  export declare function ScatterSeriesShape(props: ScatterSeriesShapeProps): React.JSX.Element;
@@ -2,12 +2,13 @@ import React from 'react';
2
2
  import { select } from 'd3';
3
3
  import get from 'lodash/get';
4
4
  import { block } from '../../../../../../utils/cn';
5
+ import { HtmlLayer } from '../HtmlLayer';
5
6
  import { getMarkerHaloVisibility, renderMarker, selectMarkerHalo, selectMarkerSymbol, setMarker, } from '../marker';
6
7
  import { setActiveState, shapeKey } from '../utils';
7
8
  export { prepareScatterData } from './prepare-data';
8
9
  const b = block('d3-scatter');
9
10
  export function ScatterSeriesShape(props) {
10
- const { dispatcher, preparedData, seriesOptions } = props;
11
+ const { dispatcher, preparedData, seriesOptions, htmlLayout } = props;
11
12
  const ref = React.useRef(null);
12
13
  React.useEffect(() => {
13
14
  if (!ref.current) {
@@ -62,5 +63,7 @@ export function ScatterSeriesShape(props) {
62
63
  dispatcher.on('hover-shape.scatter', null);
63
64
  };
64
65
  }, [dispatcher, preparedData, seriesOptions]);
65
- return React.createElement("g", { ref: ref, className: b() });
66
+ return (React.createElement(React.Fragment, null,
67
+ React.createElement("g", { ref: ref, className: b() }),
68
+ React.createElement(HtmlLayer, { preparedData: preparedData, htmlLayout: htmlLayout })));
66
69
  }
@@ -23,6 +23,7 @@ export const prepareScatterData = (args) => {
23
23
  },
24
24
  hovered: false,
25
25
  active: true,
26
+ htmlElements: [],
26
27
  });
27
28
  });
28
29
  return acc;
@@ -1,4 +1,5 @@
1
1
  import { ScatterSeriesData } from '../../../../../../types';
2
+ import { HtmlItem } from '../../../types';
2
3
  import { PreparedScatterSeries } from '../../useSeries/types';
3
4
  type PointData = {
4
5
  x: number;
@@ -11,6 +12,7 @@ export type MarkerData = {
11
12
  point: PointData;
12
13
  active: boolean;
13
14
  hovered: boolean;
15
+ htmlElements: HtmlItem[];
14
16
  };
15
17
  export type PreparedScatterData = MarkerData;
16
18
  export {};
@@ -6,6 +6,7 @@ type ShapeProps = {
6
6
  dispatcher: Dispatch<object>;
7
7
  preparedData: PreparedTreemapData;
8
8
  seriesOptions: PreparedSeriesOptions;
9
+ htmlLayout: HTMLElement | null;
9
10
  };
10
11
  export declare const TreemapSeriesShape: (props: ShapeProps) => React.JSX.Element;
11
12
  export {};
@@ -3,9 +3,10 @@ import { color, select } from 'd3';
3
3
  import get from 'lodash/get';
4
4
  import { block } from '../../../../../../utils/cn';
5
5
  import { setEllipsisForOverflowTexts } from '../../../utils';
6
+ import { HtmlLayer } from '../HtmlLayer';
6
7
  const b = block('d3-treemap');
7
8
  export const TreemapSeriesShape = (props) => {
8
- const { dispatcher, preparedData, seriesOptions } = props;
9
+ const { dispatcher, preparedData, seriesOptions, htmlLayout } = props;
9
10
  const ref = React.useRef(null);
10
11
  React.useEffect(() => {
11
12
  if (!ref.current) {
@@ -92,5 +93,7 @@ export const TreemapSeriesShape = (props) => {
92
93
  dispatcher.on(eventName, null);
93
94
  };
94
95
  }, [dispatcher, preparedData, seriesOptions]);
95
- return React.createElement("g", { ref: ref, className: b() });
96
+ return (React.createElement(React.Fragment, null,
97
+ React.createElement("g", { ref: ref, className: b() }),
98
+ React.createElement(HtmlLayer, { preparedData: preparedData, htmlLayout: htmlLayout })));
96
99
  };
@@ -51,7 +51,7 @@ export function prepareTreemapData(args) {
51
51
  })(hierarchy);
52
52
  const leaves = root.leaves();
53
53
  const labelData = ((_a = series.dataLabels) === null || _a === void 0 ? void 0 : _a.enabled) ? getLabelData(leaves) : [];
54
- return { labelData, leaves, series };
54
+ return { labelData, leaves, series, htmlElements: [] };
55
55
  }
56
56
  function getSeriesDataWithRootNode(series) {
57
57
  return series.data.reduce((acc, d) => {
@@ -1,5 +1,6 @@
1
1
  import type { HierarchyRectangularNode } from 'd3';
2
2
  import type { TreemapSeriesData } from '../../../../../../types';
3
+ import { HtmlItem } from '../../../types';
3
4
  import type { PreparedTreemapSeries } from '../../useSeries/types';
4
5
  export type TreemapLabelData = {
5
6
  text: string;
@@ -12,4 +13,5 @@ export type PreparedTreemapData = {
12
13
  labelData: TreemapLabelData[];
13
14
  leaves: HierarchyRectangularNode<TreemapSeriesData<any>>[];
14
15
  series: PreparedTreemapSeries;
16
+ htmlElements: HtmlItem[];
15
17
  };
@@ -8,5 +8,6 @@ type Args = {
8
8
  dispatcher: Dispatch<object>;
9
9
  preparedData: PreparedWaterfallData[];
10
10
  seriesOptions: PreparedSeriesOptions;
11
+ htmlLayout: HTMLElement | null;
11
12
  };
12
13
  export declare const WaterfallSeriesShapes: (args: Args) => React.JSX.Element;
@@ -4,12 +4,13 @@ import get from 'lodash/get';
4
4
  import { DashStyle } from '../../../../../../constants';
5
5
  import { block } from '../../../../../../utils/cn';
6
6
  import { filterOverlappingLabels, getWaterfallPointColor } from '../../../utils';
7
+ import { HtmlLayer } from '../HtmlLayer';
7
8
  import { getLineDashArray } from '../utils';
8
9
  export { prepareWaterfallData } from './prepare-data';
9
10
  export * from './types';
10
11
  const b = block('d3-waterfall');
11
12
  export const WaterfallSeriesShapes = (args) => {
12
- const { dispatcher, preparedData, seriesOptions } = args;
13
+ const { dispatcher, preparedData, seriesOptions, htmlLayout } = args;
13
14
  const ref = React.useRef(null);
14
15
  const connectorSelector = `.${b('connector')}`;
15
16
  React.useEffect(() => {
@@ -121,5 +122,7 @@ export const WaterfallSeriesShapes = (args) => {
121
122
  dispatcher.on('hover-shape.waterfall', null);
122
123
  };
123
124
  }, [dispatcher, preparedData, seriesOptions]);
124
- return React.createElement("g", { ref: ref, className: b() });
125
+ return (React.createElement(React.Fragment, null,
126
+ React.createElement("g", { ref: ref, className: b() }),
127
+ React.createElement(HtmlLayer, { preparedData: preparedData, htmlLayout: htmlLayout })));
125
128
  };
@@ -124,6 +124,7 @@ export const prepareWaterfallData = (args) => {
124
124
  data: item.data,
125
125
  series: item.series,
126
126
  subTotal: totalValue,
127
+ htmlElements: [],
127
128
  };
128
129
  preparedData.label = getLabelData(preparedData, plotHeight);
129
130
  result.push(preparedData);
@@ -1,5 +1,5 @@
1
1
  import { WaterfallSeriesData } from '../../../../../../types';
2
- import { LabelData } from '../../../types';
2
+ import { HtmlItem, LabelData } from '../../../types';
3
3
  import { PreparedWaterfallSeries } from '../../useSeries/types';
4
4
  export type PreparedWaterfallData = {
5
5
  x: number;
@@ -11,4 +11,5 @@ export type PreparedWaterfallData = {
11
11
  data: WaterfallSeriesData;
12
12
  label?: LabelData;
13
13
  subTotal: number;
14
+ htmlElements: HtmlItem[];
14
15
  };
@@ -14,3 +14,11 @@ export type LabelData = {
14
14
  };
15
15
  active?: boolean;
16
16
  };
17
+ export type HtmlItem = {
18
+ x: number;
19
+ y: number;
20
+ content: string;
21
+ };
22
+ export type ShapeDataWithHtmlItems = {
23
+ htmlElements: HtmlItem[];
24
+ };
@@ -4,15 +4,16 @@ type AxisBottomArgs = {
4
4
  scale: AxisScale<AxisDomain>;
5
5
  ticks: {
6
6
  count?: number;
7
- maxTickCount: number;
8
- labelFormat: (value: any) => string;
7
+ maxTickCount?: number;
8
+ labelFormat?: (value: any) => string;
9
9
  labelsPaddings?: number;
10
10
  labelsMargin?: number;
11
11
  labelsStyle?: BaseTextStyle;
12
12
  labelsMaxWidth?: number;
13
13
  labelsLineHeight: number;
14
- items: [number, number][];
15
- rotation: number;
14
+ items?: [number, number][];
15
+ rotation?: number;
16
+ tickColor?: string;
16
17
  };
17
18
  domain: {
18
19
  size: number;
@@ -16,7 +16,7 @@ function addDomain(selection, options) {
16
16
  }
17
17
  }
18
18
  export function axisBottom(args) {
19
- const { scale, ticks: { labelFormat, labelsPaddings = 0, labelsMargin = 0, labelsMaxWidth = Infinity, labelsStyle, labelsLineHeight, items: tickItems, count: ticksCount, maxTickCount, rotation, }, domain: { size: domainSize, color: domainColor }, } = args;
19
+ const { scale, ticks: { labelFormat = (value) => String(value), labelsPaddings = 0, labelsMargin = 0, labelsMaxWidth = Infinity, labelsStyle, labelsLineHeight, items: tickItems, count: ticksCount, maxTickCount, rotation = 0, tickColor, }, domain, } = args;
20
20
  const offset = getXAxisOffset();
21
21
  const position = getXTickPosition({ scale, offset });
22
22
  const values = getXAxisItems({ scale, count: ticksCount, maxCount: maxTickCount });
@@ -25,10 +25,11 @@ export function axisBottom(args) {
25
25
  style: labelsStyle,
26
26
  }).maxHeight;
27
27
  return function (selection) {
28
- var _a, _b;
29
- const x = ((_b = (_a = selection.node()) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect()) === null || _b === void 0 ? void 0 : _b.x) || 0;
30
- const right = x + domainSize;
31
- const top = -tickItems[0][0] || 0;
28
+ var _a, _b, _c;
29
+ const rect = (_a = selection.node()) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect();
30
+ const x = (rect === null || rect === void 0 ? void 0 : rect.x) || 0;
31
+ const right = x + domain.size;
32
+ const top = -((_c = (_b = tickItems === null || tickItems === void 0 ? void 0 : tickItems[0]) === null || _b === void 0 ? void 0 : _b[0]) !== null && _c !== void 0 ? _c : 0);
32
33
  let transform = `translate(0, ${labelHeight + labelsMargin - top}px)`;
33
34
  if (rotation) {
34
35
  const labelsOffsetTop = labelHeight * calculateCos(rotation) + labelsMargin - top;
@@ -39,7 +40,7 @@ export function axisBottom(args) {
39
40
  transform = `translate(${-labelsOffsetLeft}px, ${labelsOffsetTop}px) rotate(${rotation}deg)`;
40
41
  }
41
42
  const tickPath = path();
42
- tickItems.forEach(([start, end]) => {
43
+ tickItems === null || tickItems === void 0 ? void 0 : tickItems.forEach(([start, end]) => {
43
44
  tickPath.moveTo(0, start);
44
45
  tickPath.lineTo(0, end);
45
46
  });
@@ -49,7 +50,9 @@ export function axisBottom(args) {
49
50
  .order()
50
51
  .join((el) => {
51
52
  const tick = el.append('g').attr('class', 'tick');
52
- tick.append('path').attr('d', tickPath.toString()).attr('stroke', 'currentColor');
53
+ tick.append('path')
54
+ .attr('d', tickPath.toString())
55
+ .attr('stroke', tickColor !== null && tickColor !== void 0 ? tickColor : 'currentColor');
53
56
  tick.append('text')
54
57
  .text(labelFormat)
55
58
  .attr('fill', 'currentColor')
@@ -115,6 +118,7 @@ export function axisBottom(args) {
115
118
  }
116
119
  });
117
120
  }
121
+ const { size: domainSize, color: domainColor } = domain;
118
122
  selection
119
123
  .call(addDomain, { size: domainSize, color: domainColor })
120
124
  .style('font-size', (labelsStyle === null || labelsStyle === void 0 ? void 0 : labelsStyle.fontSize) || '');
@@ -15,7 +15,7 @@ export declare function getXTickPosition({ scale, offset }: {
15
15
  export declare function getXAxisItems({ scale, count, maxCount, }: {
16
16
  scale: AxisScale<AxisDomain>;
17
17
  count?: number;
18
- maxCount: number;
18
+ maxCount?: number;
19
19
  }): any;
20
20
  export declare function getMaxTickCount({ axis, width }: {
21
21
  axis: PreparedAxis;
@@ -32,7 +32,7 @@ export function getXTickPosition({ scale, offset }) {
32
32
  }
33
33
  export function getXAxisItems({ scale, count, maxCount, }) {
34
34
  let values = getScaleTicks(scale, count);
35
- if (values.length > maxCount) {
35
+ if (maxCount && values.length > maxCount) {
36
36
  const step = Math.ceil(values.length / maxCount);
37
37
  values = values.filter((_, i) => i % step === 0);
38
38
  }
@@ -0,0 +1,10 @@
1
+ import { ChartKitWidgetData } from '../../../../types';
2
+ export declare function getDomainForContinuousColorScale(args: {
3
+ series: ChartKitWidgetData['series']['data'];
4
+ }): number[];
5
+ export declare function getDefaultColorStops(size: number): number[];
6
+ export declare function getContinuesColorFn(args: {
7
+ values: number[];
8
+ colors: string[];
9
+ stops?: number[];
10
+ }): (value: number) => string;
@@ -0,0 +1,43 @@
1
+ import { range, scaleLinear } from 'd3';
2
+ export function getDomainForContinuousColorScale(args) {
3
+ const { series } = args;
4
+ const values = series.reduce((acc, s) => {
5
+ switch (s.type) {
6
+ case 'pie': {
7
+ acc.push(...s.data.map((d) => d.value));
8
+ break;
9
+ }
10
+ case 'bar-y': {
11
+ acc.push(...s.data.map((d) => Number(d.x)));
12
+ break;
13
+ }
14
+ case 'scatter':
15
+ case 'bar-x':
16
+ case 'waterfall':
17
+ case 'line':
18
+ case 'area': {
19
+ acc.push(...s.data.map((d) => Number(d.y)));
20
+ break;
21
+ }
22
+ default: {
23
+ throw Error(`The method for calculation a domain for a continuous color scale for the "${s.type}" series is not defined`);
24
+ }
25
+ }
26
+ return acc;
27
+ }, []);
28
+ return [Math.min(...values), Math.max(...values)];
29
+ }
30
+ export function getDefaultColorStops(size) {
31
+ return range(size).map((d) => d / size);
32
+ }
33
+ export function getContinuesColorFn(args) {
34
+ const { values, colors, stops: customStops } = args;
35
+ const min = Math.min(...values);
36
+ const max = Math.max(...values);
37
+ const stops = customStops !== null && customStops !== void 0 ? customStops : getDefaultColorStops(colors.length);
38
+ const color = scaleLinear(stops, colors);
39
+ return (value) => {
40
+ const colorValue = (value - min) / (max - min);
41
+ return color(colorValue);
42
+ };
43
+ }
@@ -6,8 +6,10 @@ export * from './text';
6
6
  export * from './time';
7
7
  export * from './axis';
8
8
  export * from './labels';
9
+ export * from './legend';
9
10
  export * from './symbol';
10
11
  export * from './series';
12
+ export * from './color';
11
13
  export declare const CHART_SERIES_WITH_VOLUME_ON_Y_AXIS: ChartKitWidgetSeries['type'][];
12
14
  export declare const CHART_SERIES_WITH_VOLUME_ON_X_AXIS: ChartKitWidgetSeries['type'][];
13
15
  export type AxisDirection = 'x' | 'y';
@@ -12,8 +12,10 @@ export * from './text';
12
12
  export * from './time';
13
13
  export * from './axis';
14
14
  export * from './labels';
15
+ export * from './legend';
15
16
  export * from './symbol';
16
17
  export * from './series';
18
+ export * from './color';
17
19
  const CHARTS_WITHOUT_AXIS = ['pie', 'treemap'];
18
20
  export const CHART_SERIES_WITH_VOLUME_ON_Y_AXIS = [
19
21
  'bar-x',
@@ -0,0 +1,8 @@
1
+ import { Selection } from 'd3';
2
+ export declare function createGradientRect(container: Selection<SVGGElement, unknown, null, undefined>, args: {
3
+ x?: number;
4
+ y?: number;
5
+ width: number;
6
+ height: number;
7
+ interpolator: (value: number) => string;
8
+ }): Selection<SVGImageElement, unknown, null, undefined>;
@@ -0,0 +1,23 @@
1
+ export function createGradientRect(container, args) {
2
+ const { x = 0, y = 0, width, height, interpolator } = args;
3
+ const n = 256;
4
+ const canvas = document.createElement('canvas');
5
+ canvas.width = n;
6
+ canvas.height = 1;
7
+ const context = canvas.getContext('2d');
8
+ if (!context) {
9
+ throw Error("Couldn't get canvas context");
10
+ }
11
+ for (let i = 0, j = n - 1; i < n; ++i) {
12
+ context.fillStyle = interpolator(i / j);
13
+ context.fillRect(i, 0, 1, height);
14
+ }
15
+ return container
16
+ .append('image')
17
+ .attr('preserveAspectRatio', 'none')
18
+ .attr('height', height)
19
+ .attr('width', width)
20
+ .attr('x', x)
21
+ .attr('y', y)
22
+ .attr('xlink:href', canvas.toDataURL());
23
+ }
@@ -9,10 +9,11 @@ export declare function hasOverlappingLabels({ width, labels, padding, style, }:
9
9
  style?: BaseTextStyle;
10
10
  padding?: number;
11
11
  }): boolean;
12
- export declare function getLabelsSize({ labels, style, rotation, }: {
12
+ export declare function getLabelsSize({ labels, style, rotation, html, }: {
13
13
  labels: string[];
14
14
  style?: BaseTextStyle;
15
15
  rotation?: number;
16
+ html?: boolean;
16
17
  }): {
17
18
  maxHeight: number;
18
19
  maxWidth: number;
@@ -63,24 +63,46 @@ function renderLabels(selection, { labels, style = {}, attrs = {}, }) {
63
63
  .text((d) => d);
64
64
  return text;
65
65
  }
66
- export function getLabelsSize({ labels, style, rotation, }) {
67
- var _a;
66
+ export function getLabelsSize({ labels, style, rotation, html, }) {
67
+ var _a, _b, _c;
68
68
  if (!labels.filter(Boolean).length) {
69
69
  return { maxHeight: 0, maxWidth: 0 };
70
70
  }
71
71
  const container = select(document.body)
72
72
  .append('div')
73
73
  .attr('class', 'chartkit chartkit-theme_common');
74
- const svg = container.append('svg');
75
- const textSelection = renderLabels(svg, { labels, style });
76
- if (rotation) {
77
- textSelection
78
- .attr('text-anchor', rotation > 0 ? 'start' : 'end')
79
- .style('transform', `rotate(${rotation}deg)`);
74
+ const result = { maxHeight: 0, maxWidth: 0 };
75
+ let labelWrapper;
76
+ if (html) {
77
+ labelWrapper = container.append('div').style('position', 'absolute').node();
78
+ const { height, width } = labels.reduce((acc, l) => {
79
+ var _a, _b;
80
+ if (labelWrapper) {
81
+ labelWrapper.innerHTML = l;
82
+ }
83
+ const rect = labelWrapper === null || labelWrapper === void 0 ? void 0 : labelWrapper.getBoundingClientRect();
84
+ return {
85
+ width: Math.max(acc.width, (_a = rect === null || rect === void 0 ? void 0 : rect.width) !== null && _a !== void 0 ? _a : 0),
86
+ height: Math.max(acc.height, (_b = rect === null || rect === void 0 ? void 0 : rect.height) !== null && _b !== void 0 ? _b : 0),
87
+ };
88
+ }, { height: 0, width: 0 });
89
+ result.maxWidth = width;
90
+ result.maxHeight = height;
91
+ }
92
+ else {
93
+ const svg = container.append('svg');
94
+ const textSelection = renderLabels(svg, { labels, style });
95
+ if (rotation) {
96
+ textSelection
97
+ .attr('text-anchor', rotation > 0 ? 'start' : 'end')
98
+ .style('transform', `rotate(${rotation}deg)`);
99
+ }
100
+ const rect = (_a = svg.select('g').node()) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect();
101
+ result.maxWidth = (_b = rect === null || rect === void 0 ? void 0 : rect.width) !== null && _b !== void 0 ? _b : 0;
102
+ result.maxHeight = (_c = rect === null || rect === void 0 ? void 0 : rect.height) !== null && _c !== void 0 ? _c : 0;
80
103
  }
81
- const { height = 0, width = 0 } = ((_a = svg.select('g').node()) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect()) || {};
82
104
  container.remove();
83
- return { maxHeight: height, maxWidth: width };
105
+ return result;
84
106
  }
85
107
  export function wrapText(args) {
86
108
  const { text, style, width } = args;
@@ -49,7 +49,7 @@ export type BarXSeries<T = any> = BaseSeries & {
49
49
  * @default true
50
50
  * */
51
51
  grouping?: boolean;
52
- dataLabels?: ChartKitWidgetSeriesOptions['dataLabels'] & {
52
+ dataLabels?: BaseSeries['dataLabels'] & ChartKitWidgetSeriesOptions['dataLabels'] & {
53
53
  /**
54
54
  * Whether to align the data label inside or outside the box
55
55
  *
@@ -21,6 +21,13 @@ export type BaseSeries = {
21
21
  * @default false
22
22
  * */
23
23
  allowOverlap?: boolean;
24
+ /**
25
+ * Allows to use any html-tags to display the content.
26
+ * The element will be displayed outside the box of the SVG element.
27
+ *
28
+ * @default false
29
+ * */
30
+ html?: boolean;
24
31
  };
25
32
  /** You can set the cursor to "pointer" if you have click events attached to the series, to signal to the user that the points and lines can be clicked. */
26
33
  cursor?: string;