@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
@@ -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 {};
@@ -95,6 +95,148 @@ const validateXYSeries = (args) => {
95
95
  }
96
96
  });
97
97
  };
98
+ const validateAxisPlotValues = (args) => {
99
+ const { series, xAxis, yAxis = [] } = args;
100
+ const yAxisIndex = get(series, 'yAxis', 0);
101
+ const seriesYAxis = yAxis[yAxisIndex];
102
+ if (yAxisIndex !== 0 && typeof seriesYAxis === 'undefined') {
103
+ throw new ChartError({
104
+ code: CHART_ERROR_CODE.INVALID_DATA,
105
+ message: i18n('error', 'label_invalid-y-axis-index', {
106
+ index: yAxisIndex,
107
+ }),
108
+ });
109
+ }
110
+ const xPlotBands = get(xAxis, 'plotBands', []);
111
+ const yPlotBands = get(yAxis, 'plotBands', []);
112
+ if (!xPlotBands.length && !yPlotBands.length) {
113
+ return;
114
+ }
115
+ const xType = get(xAxis, 'type', DEFAULT_AXIS_TYPE);
116
+ const yType = get(seriesYAxis, 'type', DEFAULT_AXIS_TYPE);
117
+ xPlotBands.forEach(({ from = 0, to = 0 }) => {
118
+ const fromNotEqualTo = typeof to !== typeof from;
119
+ if (fromNotEqualTo) {
120
+ throw new ChartError({
121
+ code: CHART_ERROR_CODE.INVALID_OPTION_TYPE,
122
+ message: i18n('error', 'label_axis-plot-band-options-not-equal', {
123
+ axis: 'x',
124
+ option: 'from',
125
+ }),
126
+ });
127
+ }
128
+ switch (xType) {
129
+ case 'category': {
130
+ const invalidFrom = typeof from !== 'string' && typeof from !== 'number';
131
+ const invalidTo = typeof to !== 'string' && typeof to !== 'number';
132
+ if (invalidFrom) {
133
+ throw new ChartError({
134
+ code: CHART_ERROR_CODE.INVALID_OPTION_TYPE,
135
+ message: i18n('error', 'label_invalid-axis-plot-band-option', {
136
+ axis: 'x',
137
+ option: 'from',
138
+ }),
139
+ });
140
+ }
141
+ if (invalidTo) {
142
+ throw new ChartError({
143
+ code: CHART_ERROR_CODE.INVALID_OPTION_TYPE,
144
+ message: i18n('error', 'label_invalid-axis-plot-band-option', {
145
+ axis: 'x',
146
+ option: 'to',
147
+ }),
148
+ });
149
+ }
150
+ break;
151
+ }
152
+ case 'linear':
153
+ case 'datetime': {
154
+ const invalidFrom = typeof from !== 'number';
155
+ const invalidTo = typeof to !== 'number';
156
+ if (invalidFrom) {
157
+ throw new ChartError({
158
+ code: CHART_ERROR_CODE.INVALID_OPTION_TYPE,
159
+ message: i18n('error', 'label_invalid-axis-plot-band-option', {
160
+ axis: 'x',
161
+ option: 'from',
162
+ }),
163
+ });
164
+ }
165
+ if (invalidTo) {
166
+ throw new ChartError({
167
+ code: CHART_ERROR_CODE.INVALID_OPTION_TYPE,
168
+ message: i18n('error', 'label_invalid-axis-plot-band-option', {
169
+ axis: 'x',
170
+ option: 'to',
171
+ }),
172
+ });
173
+ }
174
+ break;
175
+ }
176
+ }
177
+ });
178
+ yPlotBands.forEach(({ from = 0, to = 0 }) => {
179
+ const fromNotEqualTo = typeof to !== typeof from;
180
+ if (fromNotEqualTo) {
181
+ throw new ChartError({
182
+ code: CHART_ERROR_CODE.INVALID_OPTION_TYPE,
183
+ message: i18n('error', 'label_axis-plot-band-options-not-equal', {
184
+ axis: 'x',
185
+ option: 'from',
186
+ }),
187
+ });
188
+ }
189
+ switch (yType) {
190
+ case 'category': {
191
+ const invalidFrom = typeof from !== 'string' && typeof from !== 'number';
192
+ const invalidTo = typeof to !== 'string' && typeof to !== 'number';
193
+ if (invalidFrom) {
194
+ throw new ChartError({
195
+ code: CHART_ERROR_CODE.INVALID_OPTION_TYPE,
196
+ message: i18n('error', 'label_invalid-axis-plot-band-option', {
197
+ axis: 'y',
198
+ option: 'from',
199
+ }),
200
+ });
201
+ }
202
+ if (invalidTo) {
203
+ throw new ChartError({
204
+ code: CHART_ERROR_CODE.INVALID_OPTION_TYPE,
205
+ message: i18n('error', 'label_invalid-axis-plot-band-option', {
206
+ axis: 'y',
207
+ option: 'to',
208
+ }),
209
+ });
210
+ }
211
+ break;
212
+ }
213
+ case 'linear':
214
+ case 'datetime': {
215
+ const invalidFrom = typeof from !== 'number';
216
+ const invalidTo = typeof to !== 'number';
217
+ if (invalidFrom) {
218
+ throw new ChartError({
219
+ code: CHART_ERROR_CODE.INVALID_OPTION_TYPE,
220
+ message: i18n('error', 'label_invalid-axis-plot-band-option', {
221
+ axis: 'y',
222
+ option: 'from',
223
+ }),
224
+ });
225
+ }
226
+ if (invalidTo) {
227
+ throw new ChartError({
228
+ code: CHART_ERROR_CODE.INVALID_OPTION_TYPE,
229
+ message: i18n('error', 'label_invalid-axis-plot-band-option', {
230
+ axis: 'y',
231
+ option: 'to',
232
+ }),
233
+ });
234
+ }
235
+ break;
236
+ }
237
+ }
238
+ });
239
+ };
98
240
  const validatePieSeries = ({ series }) => {
99
241
  series.data.forEach(({ value }) => {
100
242
  if (typeof value !== 'number') {
@@ -163,12 +305,14 @@ const validateSeries = (args) => {
163
305
  case 'area':
164
306
  case 'bar-y':
165
307
  case 'bar-x': {
308
+ validateAxisPlotValues({ series, xAxis, yAxis });
166
309
  validateXYSeries({ series, xAxis, yAxis });
167
310
  validateStacking({ series });
168
311
  break;
169
312
  }
170
313
  case 'line':
171
314
  case 'scatter': {
315
+ validateAxisPlotValues({ series, xAxis, yAxis });
172
316
  validateXYSeries({ series, xAxis, yAxis });
173
317
  break;
174
318
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gravity-ui/charts",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "React component used to render charts",
5
5
  "license": "MIT",
6
6
  "main": "dist/cjs/index.js",