@gravity-ui/charts 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/dist/cjs/components/Axis/AxisX.js +42 -5
  2. package/dist/cjs/components/Axis/AxisY.d.ts +1 -1
  3. package/dist/cjs/components/Axis/AxisY.js +48 -5
  4. package/dist/cjs/components/ChartInner/index.js +1 -1
  5. package/dist/cjs/components/ChartInner/styles.css +2 -0
  6. package/dist/cjs/components/ChartInner/useChartInnerProps.js +3 -3
  7. package/dist/cjs/components/Legend/index.js +2 -2
  8. package/dist/cjs/components/Title/index.js +1 -1
  9. package/dist/cjs/hooks/useChartOptions/types.d.ts +5 -3
  10. package/dist/cjs/hooks/useChartOptions/x-axis.js +7 -0
  11. package/dist/cjs/hooks/useChartOptions/y-axis.js +7 -0
  12. package/dist/cjs/hooks/useShapes/HtmlLayer.js +2 -1
  13. package/dist/cjs/hooks/useShapes/area/prepare-data.js +1 -0
  14. package/dist/cjs/hooks/useShapes/bar-x/prepare-data.js +1 -0
  15. package/dist/cjs/hooks/useShapes/bar-y/prepare-data.js +1 -0
  16. package/dist/cjs/hooks/useShapes/line/prepare-data.js +1 -0
  17. package/dist/cjs/hooks/useShapes/pie/index.js +1 -1
  18. package/dist/cjs/hooks/useShapes/pie/prepare-data.js +1 -0
  19. package/dist/cjs/hooks/useShapes/radar/prepare-data.js +1 -0
  20. package/dist/cjs/hooks/useShapes/treemap/index.js +1 -1
  21. package/dist/cjs/hooks/useShapes/treemap/prepare-data.js +3 -2
  22. package/dist/cjs/i18n/keysets/en.json +3 -1
  23. package/dist/cjs/i18n/keysets/ru.json +3 -1
  24. package/dist/cjs/libs/chart-error/index.d.ts +1 -0
  25. package/dist/cjs/libs/chart-error/index.js +1 -0
  26. package/dist/cjs/types/chart/axis.d.ts +30 -7
  27. package/dist/cjs/types/chart-ui.d.ts +1 -0
  28. package/dist/cjs/utils/chart/axis.d.ts +12 -1
  29. package/dist/cjs/utils/chart/axis.js +35 -0
  30. package/dist/cjs/utils/chart/index.d.ts +2 -1
  31. package/dist/cjs/utils/chart/types.d.ts +1 -0
  32. package/dist/cjs/utils/chart/types.js +1 -0
  33. package/dist/cjs/validation/index.js +144 -0
  34. package/dist/esm/components/Axis/AxisX.js +42 -5
  35. package/dist/esm/components/Axis/AxisY.d.ts +1 -1
  36. package/dist/esm/components/Axis/AxisY.js +48 -5
  37. package/dist/esm/components/ChartInner/index.js +1 -1
  38. package/dist/esm/components/ChartInner/styles.css +2 -0
  39. package/dist/esm/components/ChartInner/useChartInnerProps.js +3 -3
  40. package/dist/esm/components/Legend/index.js +2 -2
  41. package/dist/esm/components/Title/index.js +1 -1
  42. package/dist/esm/hooks/useChartOptions/types.d.ts +5 -3
  43. package/dist/esm/hooks/useChartOptions/x-axis.js +7 -0
  44. package/dist/esm/hooks/useChartOptions/y-axis.js +7 -0
  45. package/dist/esm/hooks/useShapes/HtmlLayer.js +2 -1
  46. package/dist/esm/hooks/useShapes/area/prepare-data.js +1 -0
  47. package/dist/esm/hooks/useShapes/bar-x/prepare-data.js +1 -0
  48. package/dist/esm/hooks/useShapes/bar-y/prepare-data.js +1 -0
  49. package/dist/esm/hooks/useShapes/line/prepare-data.js +1 -0
  50. package/dist/esm/hooks/useShapes/pie/index.js +1 -1
  51. package/dist/esm/hooks/useShapes/pie/prepare-data.js +1 -0
  52. package/dist/esm/hooks/useShapes/radar/prepare-data.js +1 -0
  53. package/dist/esm/hooks/useShapes/treemap/index.js +1 -1
  54. package/dist/esm/hooks/useShapes/treemap/prepare-data.js +3 -2
  55. package/dist/esm/i18n/keysets/en.json +3 -1
  56. package/dist/esm/i18n/keysets/ru.json +3 -1
  57. package/dist/esm/libs/chart-error/index.d.ts +1 -0
  58. package/dist/esm/libs/chart-error/index.js +1 -0
  59. package/dist/esm/types/chart/axis.d.ts +30 -7
  60. package/dist/esm/types/chart-ui.d.ts +1 -0
  61. package/dist/esm/utils/chart/axis.d.ts +12 -1
  62. package/dist/esm/utils/chart/axis.js +35 -0
  63. package/dist/esm/utils/chart/index.d.ts +2 -1
  64. package/dist/esm/utils/chart/types.d.ts +1 -0
  65. package/dist/esm/utils/chart/types.js +1 -0
  66. package/dist/esm/validation/index.js +144 -0
  67. package/package.json +1 -1
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import { line, select } from 'd3';
3
- import { block, formatAxisTickLabel, getAxisTitleRows, getClosestPointsRange, getLineDashArray, getMaxTickCount, getScaleTicks, getTicksCount, handleOverflowingText, } from '../../utils';
3
+ import { block, formatAxisTickLabel, getAxisTitleRows, getBandsPosition, getClosestPointsRange, getLineDashArray, getMaxTickCount, getScaleTicks, getTicksCount, handleOverflowingText, } from '../../utils';
4
4
  import { axisBottom } from '../../utils/chart/axis-generators';
5
5
  import './styles.css';
6
6
  const b = block('axis');
@@ -114,14 +114,51 @@ export const AxisX = React.memo(function AxisX(props) {
114
114
  }
115
115
  });
116
116
  }
117
+ // add plot bands
118
+ if (plotContainer && axis.plotBands.length > 0) {
119
+ const plotBandClassName = b('plot-x-band');
120
+ const plotBandsSelection = plotContainer
121
+ .selectAll(`.${plotBandClassName}-x`)
122
+ .data(axis.plotBands)
123
+ .join('g')
124
+ .attr('class', `${plotClassName} ${plotBandClassName}-x`);
125
+ plotBandsSelection
126
+ .append('rect')
127
+ .attr('x', (band) => {
128
+ var _a, _b;
129
+ const { from, to } = getBandsPosition({ band, axisScale, axis: 'x' });
130
+ const halfBandwidth = ((_b = (_a = axisScale.bandwidth) === null || _a === void 0 ? void 0 : _a.call(axisScale)) !== null && _b !== void 0 ? _b : 0) / 2;
131
+ const startPos = halfBandwidth + Math.min(from, to);
132
+ return Math.max(0, startPos);
133
+ })
134
+ .attr('width', (band) => {
135
+ const { from, to } = getBandsPosition({ band, axisScale, axis: 'x' });
136
+ const startPos = width - Math.min(from, to);
137
+ const endPos = Math.min(Math.abs(to - from), startPos);
138
+ return Math.min(endPos, width);
139
+ })
140
+ .attr('y', 0)
141
+ .attr('height', totalHeight)
142
+ .attr('fill', (band) => band.color)
143
+ .attr('opacity', (band) => band.opacity);
144
+ plotBandsSelection.each((plotBandData, i, nodes) => {
145
+ const plotLineSelection = select(nodes[i]);
146
+ if (plotBandData.layerPlacement === 'before') {
147
+ plotLineSelection.lower();
148
+ }
149
+ else {
150
+ plotLineSelection.raise();
151
+ }
152
+ });
153
+ }
117
154
  // add plot lines
118
155
  if (plotContainer && axis.plotLines.length > 0) {
119
- const plotLineClassName = b('plotLine');
156
+ const plotLineClassName = b('plot-x-line');
120
157
  const plotLinesSelection = plotContainer
121
- .selectAll(`.${plotLineClassName}-x`)
158
+ .selectAll(`.${plotLineClassName}`)
122
159
  .data(axis.plotLines)
123
160
  .join('g')
124
- .attr('class', `${plotClassName} ${plotLineClassName}-x`);
161
+ .attr('class', `${plotClassName} ${plotLineClassName}`);
125
162
  const lineGenerator = line();
126
163
  plotLinesSelection
127
164
  .append('path')
@@ -148,6 +185,6 @@ export const AxisX = React.memo(function AxisX(props) {
148
185
  }
149
186
  });
150
187
  }
151
- }, [axis, width, totalHeight, scale, split, plotRef]);
188
+ }, [axis, width, totalHeight, scale, split, plotRef, leftmostLimit]);
152
189
  return React.createElement("g", { ref: ref });
153
190
  });
@@ -8,7 +8,7 @@ type Props = {
8
8
  height: number;
9
9
  split: PreparedSplit;
10
10
  plotRef?: React.MutableRefObject<SVGGElement | null>;
11
- lowerLimit?: number;
11
+ bottomLimit?: number;
12
12
  };
13
13
  export declare const AxisY: (props: Props) => React.JSX.Element;
14
14
  export {};
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import { axisLeft, axisRight, line, select } from 'd3';
3
- import { block, calculateCos, calculateSin, formatAxisTickLabel, getAxisHeight, getAxisTitleRows, getClosestPointsRange, getLineDashArray, getScaleTicks, getTicksCount, handleOverflowingText, parseTransformStyle, setEllipsisForOverflowTexts, wrapText, } from '../../utils';
3
+ import { block, calculateCos, calculateSin, formatAxisTickLabel, getAxisHeight, getAxisTitleRows, getBandsPosition, getClosestPointsRange, getLineDashArray, getScaleTicks, getTicksCount, handleOverflowingText, parseTransformStyle, setEllipsisForOverflowTexts, wrapText, } from '../../utils';
4
4
  import './styles.css';
5
5
  const b = block('axis');
6
6
  function transformLabel(args) {
@@ -85,7 +85,7 @@ function getTitlePosition(args) {
85
85
  return { x, y };
86
86
  }
87
87
  export const AxisY = (props) => {
88
- const { axes: allAxes, width, height: totalHeight, scale, split, plotRef, lowerLimit = 0, } = props;
88
+ const { axes: allAxes, width, height: totalHeight, scale, split, plotRef, bottomLimit = 0, } = props;
89
89
  const height = getAxisHeight({ split, boundsHeight: totalHeight });
90
90
  const ref = React.useRef(null);
91
91
  const lineGenerator = line();
@@ -118,6 +118,12 @@ export const AxisY = (props) => {
118
118
  }
119
119
  return acc;
120
120
  }, []);
121
+ const plotBands = axes.reduce((acc, axis) => {
122
+ if (axis.plotBands.length) {
123
+ acc.push(...axis.plotBands.map((plotBand) => (Object.assign(Object.assign({}, plotBand), { transform: getAxisPosition(axis) }))));
124
+ }
125
+ return acc;
126
+ }, []);
121
127
  const axisSelection = svgElement
122
128
  .selectAll('axis')
123
129
  .data(axes)
@@ -153,7 +159,7 @@ export const AxisY = (props) => {
153
159
  const currentElement = this;
154
160
  const currentElementPosition = currentElement.getBoundingClientRect();
155
161
  const text = select(currentElement);
156
- if (currentElementPosition.bottom > lowerLimit) {
162
+ if (currentElementPosition.bottom > bottomLimit) {
157
163
  const transform = transformLabel({
158
164
  node: this,
159
165
  axis: d,
@@ -190,8 +196,45 @@ export const AxisY = (props) => {
190
196
  })
191
197
  .remove();
192
198
  }
199
+ if (plotContainer && d.plotBands.length > 0) {
200
+ const plotBandClassName = b('plot-y-band');
201
+ const plotBandsSelection = plotContainer
202
+ .selectAll(`.${plotBandClassName}`)
203
+ .data(plotBands)
204
+ .join('g')
205
+ .attr('class', `${plotClassName} ${plotBandClassName}`)
206
+ .style('transform', (plotBand) => plotBand.transform);
207
+ plotBandsSelection
208
+ .append('rect')
209
+ .attr('x', 0)
210
+ .attr('width', width)
211
+ .attr('y', (band) => {
212
+ var _a, _b;
213
+ const { from, to } = getBandsPosition({ band, axisScale, axis: 'y' });
214
+ const halfBandwidth = ((_b = (_a = axisScale.bandwidth) === null || _a === void 0 ? void 0 : _a.call(axisScale)) !== null && _b !== void 0 ? _b : 0) / 2;
215
+ const startPos = halfBandwidth + Math.min(from, to);
216
+ return Math.max(0, startPos);
217
+ })
218
+ .attr('height', (band) => {
219
+ const { from, to } = getBandsPosition({ band, axisScale, axis: 'y' });
220
+ const startPos = height - Math.min(from, to);
221
+ const endPos = Math.min(Math.abs(to - from), startPos);
222
+ return Math.min(endPos, height);
223
+ })
224
+ .attr('fill', (band) => band.color)
225
+ .attr('opacity', (band) => band.opacity);
226
+ plotBandsSelection.each((plotBandData, i, nodes) => {
227
+ const plotLineSelection = select(nodes[i]);
228
+ if (plotBandData.layerPlacement === 'before') {
229
+ plotLineSelection.lower();
230
+ }
231
+ else {
232
+ plotLineSelection.raise();
233
+ }
234
+ });
235
+ }
193
236
  if (plotContainer && d.plotLines.length > 0) {
194
- const plotLineClassName = b('plotLine');
237
+ const plotLineClassName = b('plot-y-line');
195
238
  const plotLinesSelection = plotContainer
196
239
  .selectAll(`.${plotLineClassName}`)
197
240
  .data(plotLines)
@@ -268,6 +311,6 @@ export const AxisY = (props) => {
268
311
  handleOverflowingText(nodes[index], height);
269
312
  }
270
313
  });
271
- }, [allAxes, width, height, scale, split]);
314
+ }, [allAxes, width, height, scale, split, bottomLimit]);
272
315
  return React.createElement("g", { ref: ref, className: b('container') });
273
316
  };
@@ -67,7 +67,7 @@ export const ChartInner = (props) => {
67
67
  })),
68
68
  React.createElement("g", { width: boundsWidth, height: boundsHeight, transform: `translate(${[boundsOffsetLeft, boundsOffsetTop].join(',')})`, ref: plotRef },
69
69
  xScale && (yScale === null || yScale === void 0 ? void 0 : yScale.length) && (React.createElement(React.Fragment, null,
70
- React.createElement(AxisY, { lowerLimit: svgBottomPos, axes: yAxis, width: boundsWidth, height: boundsHeight, scale: yScale, split: preparedSplit, plotRef: plotRef }),
70
+ React.createElement(AxisY, { bottomLimit: svgBottomPos, axes: yAxis, width: boundsWidth, height: boundsHeight, scale: yScale, split: preparedSplit, plotRef: plotRef }),
71
71
  React.createElement("g", { transform: `translate(0, ${boundsHeight})` },
72
72
  React.createElement(AxisX, { leftmostLimit: svgXPos, axis: xAxis, width: boundsWidth, height: boundsHeight, scale: xScale, split: preparedSplit, plotRef: plotRef })))),
73
73
  shapes),
@@ -1,6 +1,8 @@
1
1
  .gcharts-chart {
2
2
  --gcharts-data-labels: var(--g-color-text-secondary);
3
3
  position: absolute;
4
+ inset-block-start: 0;
5
+ inset-inline-start: 0;
4
6
  }
5
7
  .gcharts-chart__html-layer {
6
8
  display: contents;
@@ -58,9 +58,9 @@ export function useChartInnerProps(props) {
58
58
  // We need to calculate the width of each axis because the first axis can be hidden
59
59
  const boundsOffsetLeft = chart.margin.left +
60
60
  yAxis.reduce((acc, axis) => {
61
- const width = getYAxisWidth(axis);
62
- if (acc < width) {
63
- acc = width;
61
+ const axisWidth = getYAxisWidth(axis);
62
+ if (acc < axisWidth) {
63
+ acc = axisWidth;
64
64
  }
65
65
  return acc;
66
66
  }, 0);
@@ -185,7 +185,7 @@ export const Legend = (props) => {
185
185
  const mods = { selected: d.visible, unselected: !d.visible };
186
186
  return b('item-text', mods);
187
187
  })
188
- .text(function (d) {
188
+ .html(function (d) {
189
189
  return ('name' in d && d.name);
190
190
  })
191
191
  .style('font-size', legend.itemStyle.fontSize);
@@ -283,7 +283,7 @@ export const Legend = (props) => {
283
283
  .attr('font-size', (_d = legend.title.style.fontSize) !== null && _d !== void 0 ? _d : null)
284
284
  .attr('fill', (_e = legend.title.style.fontColor) !== null && _e !== void 0 ? _e : null)
285
285
  .style('alignment-baseline', 'before-edge')
286
- .text(legend.title.text);
286
+ .html(legend.title.text);
287
287
  }
288
288
  const { left } = getLegendPosition({
289
289
  align: legend.align,
@@ -5,5 +5,5 @@ const b = block('title');
5
5
  export const Title = (props) => {
6
6
  const { chartWidth, text, height, style } = props;
7
7
  return (React.createElement("text", { className: b(), dx: chartWidth / 2, dy: height / 2, dominantBaseline: "middle", textAnchor: "middle", style: Object.assign({ lineHeight: `${height}px` }, style) },
8
- React.createElement("tspan", null, text)));
8
+ React.createElement("tspan", { dangerouslySetInnerHTML: { __html: text } })));
9
9
  };
@@ -1,5 +1,5 @@
1
1
  import type { DashStyle } from 'src/constants';
2
- import type { AxisPlotLine, BaseTextStyle, ChartAxis, ChartAxisLabels, ChartAxisTitleAlignment, ChartAxisType, ChartData, ChartMargin } from '../../types';
2
+ import type { AxisPlotBand, BaseTextStyle, ChartAxis, ChartAxisLabels, ChartAxisTitleAlignment, ChartAxisType, ChartData, ChartMargin, PlotLayerPlacement } from '../../types';
3
3
  type PreparedAxisLabels = Omit<ChartAxisLabels, 'enabled' | 'padding' | 'style' | 'autoRotation'> & Required<Pick<ChartAxisLabels, 'enabled' | 'padding' | 'margin' | 'rotation'>> & {
4
4
  style: BaseTextStyle;
5
5
  rotation: number;
@@ -11,15 +11,16 @@ type PreparedAxisLabels = Omit<ChartAxisLabels, 'enabled' | 'padding' | 'style'
11
11
  export type PreparedChart = {
12
12
  margin: ChartMargin;
13
13
  };
14
+ export type PreparedAxisPlotBand = Required<AxisPlotBand>;
14
15
  export type PreparedAxisPlotLine = {
15
16
  value: number;
16
17
  color: string;
17
18
  width: number;
18
19
  dashStyle: DashStyle;
19
20
  opacity: number;
20
- layerPlacement: AxisPlotLine['layerPlacement'];
21
+ layerPlacement: PlotLayerPlacement;
21
22
  };
22
- export type PreparedAxis = Omit<ChartAxis, 'type' | 'labels' | 'plotLines'> & {
23
+ export type PreparedAxis = Omit<ChartAxis, 'type' | 'labels' | 'plotLines' | 'plotBands'> & {
23
24
  type: ChartAxisType;
24
25
  labels: PreparedAxisLabels;
25
26
  title: {
@@ -42,6 +43,7 @@ export type PreparedAxis = Omit<ChartAxis, 'type' | 'labels' | 'plotLines'> & {
42
43
  position: 'left' | 'right' | 'top' | 'bottom';
43
44
  plotIndex: number;
44
45
  plotLines: PreparedAxisPlotLine[];
46
+ plotBands: PreparedAxisPlotBand[];
45
47
  };
46
48
  export type PreparedTitle = ChartData['title'] & {
47
49
  height: number;
@@ -110,6 +110,13 @@ export const getPreparedXAxis = ({ xAxis, series, width, }) => {
110
110
  opacity: get(d, 'opacity', 1),
111
111
  layerPlacement: get(d, 'layerPlacement', 'before'),
112
112
  })),
113
+ plotBands: get(xAxis, 'plotBands', []).map((d) => ({
114
+ color: get(d, 'color', 'var(--g-color-base-brand)'),
115
+ opacity: get(d, 'opacity', 1),
116
+ from: get(d, 'from', 0),
117
+ to: get(d, 'to', 0),
118
+ layerPlacement: get(d, 'layerPlacement', 'before'),
119
+ })),
113
120
  visible: get(xAxis, 'visible', true),
114
121
  };
115
122
  const { height, rotation } = getLabelSettings({
@@ -116,6 +116,13 @@ export const getPreparedYAxis = ({ series, yAxis, height, }) => {
116
116
  opacity: get(d, 'opacity', 1),
117
117
  layerPlacement: get(d, 'layerPlacement', 'before'),
118
118
  })),
119
+ plotBands: get(axisItem, 'plotBands', []).map((d) => ({
120
+ color: get(d, 'color', 'var(--g-color-base-brand)'),
121
+ opacity: get(d, 'opacity', 1),
122
+ from: get(d, 'from', 0),
123
+ to: get(d, 'to', 0),
124
+ layerPlacement: get(d, 'layerPlacement', 'before'),
125
+ })),
119
126
  visible: get(axisItem, 'visible', true),
120
127
  };
121
128
  if (labelsEnabled) {
@@ -17,6 +17,7 @@ export const HtmlLayer = (props) => {
17
17
  return null;
18
18
  }
19
19
  return (React.createElement(Portal, { container: htmlLayout }, items.map((item, index) => {
20
- return (React.createElement("div", { key: index, dangerouslySetInnerHTML: { __html: item.content }, style: { position: 'absolute', left: item.x, top: item.y } }));
20
+ const style = Object.assign(Object.assign({}, item.style), { position: 'absolute', left: item.x, top: item.y });
21
+ return (React.createElement("div", { key: index, dangerouslySetInnerHTML: { __html: item.content }, style: style }));
21
22
  })));
22
23
  };
@@ -111,6 +111,7 @@ export const prepareAreaData = (args) => {
111
111
  y: l.y,
112
112
  content: l.text,
113
113
  size: { width: labelSize.maxWidth, height: labelSize.maxHeight },
114
+ style,
114
115
  };
115
116
  });
116
117
  htmlElements.push(...htmlLabels);
@@ -139,6 +139,7 @@ export const prepareBarXData = (args) => {
139
139
  y: label.y,
140
140
  content: label.text,
141
141
  size: label.size,
142
+ style: label.style,
142
143
  });
143
144
  }
144
145
  else {
@@ -71,6 +71,7 @@ function setLabel(prepared) {
71
71
  y: y - height / 2,
72
72
  content,
73
73
  size: { width, height },
74
+ style: dataLabels.style,
74
75
  });
75
76
  }
76
77
  else {
@@ -35,6 +35,7 @@ function getHtmlLabel(point, series, xMax) {
35
35
  y: Math.max(0, point.y - series.dataLabels.padding - size.maxHeight),
36
36
  content,
37
37
  size: { width: size.maxWidth, height: size.maxHeight },
38
+ style: series.dataLabels.style,
38
39
  };
39
40
  }
40
41
  export const prepareLineData = (args) => {
@@ -75,7 +75,7 @@ export function PieSeriesShapes(args) {
75
75
  .selectAll('text')
76
76
  .data((pieData) => pieData.labels)
77
77
  .join('text')
78
- .text((d) => d.text)
78
+ .html((d) => d.text)
79
79
  .attr('class', b('label'))
80
80
  .attr('x', (d) => d.x)
81
81
  .attr('y', (d) => d.y)
@@ -184,6 +184,7 @@ export function preparePieData(args) {
184
184
  y: Math.max(0, data.center[1] + label.y),
185
185
  content: label.text,
186
186
  size: label.size,
187
+ style: label.style,
187
188
  });
188
189
  }
189
190
  else {
@@ -146,6 +146,7 @@ export function prepareRadarData(args) {
146
146
  y: label.y,
147
147
  content: label.text,
148
148
  size: label.size,
149
+ style: label.style,
149
150
  }));
150
151
  }
151
152
  }
@@ -38,7 +38,7 @@ export const TreemapSeriesShape = (props) => {
38
38
  .selectAll('tspan')
39
39
  .data(labelData)
40
40
  .join('text')
41
- .text((d) => d.text)
41
+ .html((d) => d.text)
42
42
  .attr('class', b('label'))
43
43
  .attr('x', (d) => d.x)
44
44
  .attr('y', (d) => d.y)
@@ -94,10 +94,11 @@ export function prepareTreemapData(args) {
94
94
  let labelData = [];
95
95
  const htmlElements = [];
96
96
  if ((_a = series.dataLabels) === null || _a === void 0 ? void 0 : _a.enabled) {
97
- const { html } = series.dataLabels;
97
+ const { html, style: dataLabelsStyle } = series.dataLabels;
98
98
  const labels = getLabels({ data: leaves, options: series.dataLabels });
99
99
  if (html) {
100
- htmlElements.push(...labels);
100
+ const htmlItems = labels.map((l) => (Object.assign({ style: dataLabelsStyle }, l)));
101
+ htmlElements.push(...htmlItems);
101
102
  }
102
103
  else {
103
104
  labelData = labels;
@@ -11,6 +11,8 @@
11
11
  "label_invalid-series-property": "It seems you are trying to use inappropriate value for \"{{key}}\", or defined it incorrectly. Available values: [{{values}}].",
12
12
  "label_invalid-treemap-redundant-value": "It seems you are trying to set \"value\" for container node. Check node with this properties: { id: \"{{id}}\", name: \"{{name}}\" }",
13
13
  "label_invalid-treemap-missing-value": "It seems you are trying to use node without \"value\". Check node with this properties: { id: \"{{id}}\", name: \"{{name}}\" }",
14
- "label_invalid-y-axis-index": "It seems you are trying to use inappropriate index for Y axis: \"{{index}}\""
14
+ "label_invalid-y-axis-index": "It seems you are trying to use inappropriate index for Y axis: \"{{index}}\"",
15
+ "label_invalid-axis-plot-band-option": "It seems you are trying to use inappropriate type for \"{{axis}}\" axis plot band option: \"{{option}}\"",
16
+ "label_axis-plot-band-options-not-equal": "It seems you are trying to use different type for \"{{axis}}\" axis plot band options"
15
17
  }
16
18
  }
@@ -11,6 +11,8 @@
11
11
  "label_invalid-series-property": "Похоже, что вы пытаетесь использовать недопустимое значение для \"{{key}}\", или указали его неверно. Доступные значения: [{{values}}].",
12
12
  "label_invalid-treemap-redundant-value": "Похоже, что вы пытаетесь установить значение \"value\" для узла, используемого в качестве контейнера. Проверьте узел с этими свойствами: { id: \"{{id}}\", name: \"{{name}}\" }",
13
13
  "label_invalid-treemap-missing-value": "Похоже, что вы пытаетесь использовать узел без значения \"value\". Проверьте узел с этими свойствами: { id: \"{{id}}\", name: \"{{name}}\" }",
14
- "label_invalid-y-axis-index": "Похоже, что вы пытаетесь использовать некорректный индекс для оси Y: \"{{index}}\""
14
+ "label_invalid-y-axis-index": "Похоже, что вы пытаетесь использовать некорректный индекс для оси Y: \"{{index}}\"",
15
+ "label_invalid-axis-plot-band-option": "Похоже, что вы пытаетесь использовать некорректный тип для параметра полосы: \"{{option}}\" для оси \"{{axis}}\"",
16
+ "label_axis-plot-band-options-not-equal": "Похоже, что вы пытаетесь использовать разные типы для для параметра полосы для оси \"{{axis}}\""
15
17
  }
16
18
  }
@@ -7,6 +7,7 @@ export declare const CHART_ERROR_CODE: {
7
7
  NO_DATA: string;
8
8
  INVALID_DATA: string;
9
9
  UNKNOWN: string;
10
+ INVALID_OPTION_TYPE: string;
10
11
  };
11
12
  export declare class ChartError extends Error {
12
13
  readonly code: number | string;
@@ -2,6 +2,7 @@ export const CHART_ERROR_CODE = {
2
2
  NO_DATA: 'ERR.CK.NO_DATA',
3
3
  INVALID_DATA: 'ERR.CK.INVALID_DATA',
4
4
  UNKNOWN: 'ERR.CK.UNKNOWN_ERROR',
5
+ INVALID_OPTION_TYPE: 'ERR.CK.INVALID_OPTION_TYPE',
5
6
  };
6
7
  export class ChartError extends Error {
7
8
  constructor({ originalError, message, code = CHART_ERROR_CODE.UNKNOWN } = {}) {
@@ -75,12 +75,27 @@ export interface ChartAxis {
75
75
  maxPadding?: number;
76
76
  /** An array of lines stretching across the plot area, marking a specific value */
77
77
  plotLines?: AxisPlotLine[];
78
+ /** An array of colored bands stretching across the plot area marking an interval on the axis. */
79
+ plotBands?: AxisPlotBand[];
78
80
  /** Whether axis, including axis title, line, ticks and labels, should be visible. */
79
81
  visible?: boolean;
80
82
  }
81
83
  export interface ChartXAxis extends ChartAxis {
82
84
  }
83
- export interface AxisPlotLine {
85
+ export type PlotLayerPlacement = 'before' | 'after';
86
+ export interface AxisPlot {
87
+ /** Place the line behind or above the chart. */
88
+ layerPlacement?: PlotLayerPlacement;
89
+ /** The color of the plot line (hex, rgba). */
90
+ color?: string;
91
+ /**
92
+ * Individual opacity for the line.
93
+ *
94
+ * @default 1
95
+ * */
96
+ opacity?: number;
97
+ }
98
+ export interface AxisPlotLine extends AxisPlot {
84
99
  /** The position of the line in axis units. */
85
100
  value?: number;
86
101
  /** The color of the plot line (hex, rgba). */
@@ -92,14 +107,22 @@ export interface AxisPlotLine {
92
107
  width?: number;
93
108
  /** Option for line stroke style. */
94
109
  dashStyle?: `${DashStyle}`;
110
+ }
111
+ export interface AxisPlotBand extends AxisPlot {
95
112
  /**
96
- * Individual opacity for the line.
113
+ * The start position of the plot band in axis units.
97
114
  *
98
- * @default 1
99
- * */
100
- opacity?: number;
101
- /** Place the line behind or above the chart. */
102
- layerPlacement?: 'before' | 'after';
115
+ * Can be a number, a string (e.g., a category), or a timestamp if representing a date.
116
+ * When representing a date, the value **must be a timestamp** (number of milliseconds since Unix epoch).
117
+ */
118
+ from: number | string;
119
+ /**
120
+ * The end position of the plot band in axis units.
121
+ *
122
+ * Can be a number, a string (e.g., a category), or a timestamp if representing a date.
123
+ * When representing a date, the value **must be a timestamp** (number of milliseconds since Unix epoch).
124
+ */
125
+ to: number | string;
103
126
  }
104
127
  export interface ChartYAxis extends ChartAxis {
105
128
  /** Axis location.
@@ -22,6 +22,7 @@ export interface HtmlItem {
22
22
  width: number;
23
23
  height: number;
24
24
  };
25
+ style?: BaseTextStyle;
25
26
  }
26
27
  export interface ShapeDataWithHtmlItems {
27
28
  htmlElements: HtmlItem[];
@@ -1,6 +1,7 @@
1
1
  import type { AxisDomain, AxisScale, ScaleBand } from 'd3';
2
- import type { PreparedAxis, PreparedSplit } from '../../hooks';
2
+ import type { PreparedAxis, PreparedAxisPlotBand, PreparedSplit } from '../../hooks';
3
3
  import type { TextRow } from './text';
4
+ import type { AxisDirection } from './types';
4
5
  export declare function getTicksCount({ axis, range }: {
5
6
  axis: PreparedAxis;
6
7
  range: number;
@@ -29,3 +30,13 @@ export declare function getAxisTitleRows(args: {
29
30
  axis: PreparedAxis;
30
31
  textMaxWidth: number;
31
32
  }): TextRow[];
33
+ interface GetBandsPositionArgs {
34
+ band: PreparedAxisPlotBand;
35
+ axisScale: AxisScale<AxisDomain>;
36
+ axis: AxisDirection;
37
+ }
38
+ export declare function getBandsPosition(args: GetBandsPositionArgs): {
39
+ from: number;
40
+ to: number;
41
+ };
42
+ export {};
@@ -1,3 +1,4 @@
1
+ import clamp from 'lodash/clamp';
1
2
  import { wrapText } from './text';
2
3
  export function getTicksCount({ axis, range }) {
3
4
  let ticksCount;
@@ -69,3 +70,37 @@ export function getAxisTitleRows(args) {
69
70
  return acc;
70
71
  }, []);
71
72
  }
73
+ export function getBandsPosition(args) {
74
+ var _a, _b, _c;
75
+ const { band, axisScale } = args;
76
+ const scalePosTo = axisScale(band.to);
77
+ const scalePosFrom = axisScale(band.from);
78
+ const isX = args.axis === 'x';
79
+ if (scalePosTo !== undefined && scalePosFrom !== undefined) {
80
+ return {
81
+ from: Math.max(scalePosFrom, 0),
82
+ to: Math.max(scalePosTo, 0),
83
+ };
84
+ }
85
+ if (typeof band.from !== 'number' || typeof band.to !== 'number') {
86
+ throw new Error('Filed to create plot band');
87
+ }
88
+ const category = axisScale.domain();
89
+ const bandwidth = (_b = (_a = axisScale.bandwidth) === null || _a === void 0 ? void 0 : _a.call(axisScale)) !== null && _b !== void 0 ? _b : 1;
90
+ const halfBandwidth = bandwidth / 2;
91
+ const calcPosition = (value) => {
92
+ var _a, _b;
93
+ if (value >= category.length) {
94
+ return ((_a = axisScale(category[category.length - 1])) !== null && _a !== void 0 ? _a : 0) + halfBandwidth * (isX ? 1 : -1);
95
+ }
96
+ return (((_b = axisScale(category[clamp(Math.floor(value), 0, category.length - 1)])) !== null && _b !== void 0 ? _b : 0) +
97
+ bandwidth * (value - Math.floor(Math.abs(value))) * (isX ? 1 : -1));
98
+ };
99
+ const to = calcPosition(band.to);
100
+ const from = calcPosition(band.from);
101
+ const maxPos = ((_c = axisScale(category[isX ? category.length - 1 : 0])) !== null && _c !== void 0 ? _c : 0) + halfBandwidth;
102
+ return {
103
+ from: clamp(from, -halfBandwidth, maxPos),
104
+ to: clamp(to, -halfBandwidth, maxPos),
105
+ };
106
+ }
@@ -1,6 +1,7 @@
1
1
  import type { AxisDomain } from 'd3';
2
2
  import type { PreparedAxis } from '../../hooks';
3
3
  import type { BaseTextStyle, ChartSeries, ChartSeriesData } from '../../types';
4
+ import type { AxisDirection } from './types';
4
5
  export * from './math';
5
6
  export * from './text';
6
7
  export * from './time';
@@ -12,7 +13,6 @@ export * from './series';
12
13
  export * from './color';
13
14
  export declare const CHART_SERIES_WITH_VOLUME_ON_Y_AXIS: ChartSeries['type'][];
14
15
  export declare const CHART_SERIES_WITH_VOLUME_ON_X_AXIS: ChartSeries['type'][];
15
- export type AxisDirection = 'x' | 'y';
16
16
  type UnknownSeries = {
17
17
  type: ChartSeries['type'];
18
18
  data: unknown;
@@ -76,3 +76,4 @@ export declare const getDataCategoryValue: (args: {
76
76
  data: ChartSeriesData;
77
77
  }) => string;
78
78
  export declare function getClosestPointsRange(axis: PreparedAxis, points: AxisDomain[]): number | undefined;
79
+ export { AxisDirection };
@@ -0,0 +1 @@
1
+ export type AxisDirection = 'x' | 'y';
@@ -0,0 +1 @@
1
+ export {};