@gravity-ui/charts 1.0.1 → 1.1.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 (51) hide show
  1. package/dist/cjs/components/Axis/AxisX.d.ts +1 -0
  2. package/dist/cjs/components/Axis/AxisX.js +16 -8
  3. package/dist/cjs/components/Axis/AxisY.d.ts +1 -0
  4. package/dist/cjs/components/Axis/AxisY.js +38 -10
  5. package/dist/cjs/components/ChartInner/index.js +3 -3
  6. package/dist/cjs/components/ChartInner/useChartInnerProps.d.ts +3 -0
  7. package/dist/cjs/components/ChartInner/useChartInnerProps.js +14 -3
  8. package/dist/cjs/components/Tooltip/DefaultContent.js +41 -23
  9. package/dist/cjs/hooks/useChartDimensions/index.js +3 -0
  10. package/dist/cjs/hooks/useChartDimensions/utils.js +3 -0
  11. package/dist/cjs/hooks/useChartOptions/x-axis.js +1 -0
  12. package/dist/cjs/hooks/useChartOptions/y-axis.js +1 -0
  13. package/dist/cjs/hooks/useSeries/prepare-waterfall.js +40 -28
  14. package/dist/cjs/hooks/useSeries/types.d.ts +4 -3
  15. package/dist/cjs/hooks/useShapes/waterfall/prepare-data.js +4 -2
  16. package/dist/cjs/types/chart/axis.d.ts +2 -0
  17. package/dist/cjs/types/chart/waterfall.d.ts +9 -0
  18. package/dist/cjs/utils/chart/axis-generators/bottom.d.ts +1 -0
  19. package/dist/cjs/utils/chart/axis-generators/bottom.js +16 -1
  20. package/dist/cjs/utils/chart/get-closest-data.js +23 -13
  21. package/dist/cjs/utils/chart/index.js +5 -5
  22. package/dist/cjs/utils/chart/series/waterfall.d.ts +2 -2
  23. package/dist/cjs/utils/chart/series/waterfall.js +1 -7
  24. package/dist/cjs/utils/chart-ui/pie-center-text.d.ts +1 -0
  25. package/dist/cjs/utils/chart-ui/pie-center-text.js +3 -1
  26. package/dist/esm/components/Axis/AxisX.d.ts +1 -0
  27. package/dist/esm/components/Axis/AxisX.js +16 -8
  28. package/dist/esm/components/Axis/AxisY.d.ts +1 -0
  29. package/dist/esm/components/Axis/AxisY.js +38 -10
  30. package/dist/esm/components/ChartInner/index.js +3 -3
  31. package/dist/esm/components/ChartInner/useChartInnerProps.d.ts +3 -0
  32. package/dist/esm/components/ChartInner/useChartInnerProps.js +14 -3
  33. package/dist/esm/components/Tooltip/DefaultContent.js +41 -23
  34. package/dist/esm/hooks/useChartDimensions/index.js +3 -0
  35. package/dist/esm/hooks/useChartDimensions/utils.js +3 -0
  36. package/dist/esm/hooks/useChartOptions/x-axis.js +1 -0
  37. package/dist/esm/hooks/useChartOptions/y-axis.js +1 -0
  38. package/dist/esm/hooks/useSeries/prepare-waterfall.js +40 -28
  39. package/dist/esm/hooks/useSeries/types.d.ts +4 -3
  40. package/dist/esm/hooks/useShapes/waterfall/prepare-data.js +4 -2
  41. package/dist/esm/types/chart/axis.d.ts +2 -0
  42. package/dist/esm/types/chart/waterfall.d.ts +9 -0
  43. package/dist/esm/utils/chart/axis-generators/bottom.d.ts +1 -0
  44. package/dist/esm/utils/chart/axis-generators/bottom.js +16 -1
  45. package/dist/esm/utils/chart/get-closest-data.js +23 -13
  46. package/dist/esm/utils/chart/index.js +5 -5
  47. package/dist/esm/utils/chart/series/waterfall.d.ts +2 -2
  48. package/dist/esm/utils/chart/series/waterfall.js +1 -7
  49. package/dist/esm/utils/chart-ui/pie-center-text.d.ts +1 -0
  50. package/dist/esm/utils/chart-ui/pie-center-text.js +3 -1
  51. package/package.json +1 -1
@@ -243,9 +243,12 @@ export type PreparedTreemapSeries = {
243
243
  };
244
244
  layoutAlgorithm: `${LayoutAlgorithm}`;
245
245
  } & BasePreparedSeries & Omit<TreemapSeries, keyof BasePreparedSeries>;
246
+ export type PreparedWaterfallSeriesData = WaterfallSeriesData & {
247
+ index: number;
248
+ };
246
249
  export type PreparedWaterfallSeries = {
247
250
  type: WaterfallSeries['type'];
248
- data: WaterfallSeriesData[];
251
+ data: PreparedWaterfallSeriesData[];
249
252
  dataLabels: {
250
253
  enabled: boolean;
251
254
  style: BaseTextStyle;
@@ -254,8 +257,6 @@ export type PreparedWaterfallSeries = {
254
257
  html: boolean;
255
258
  format?: ValueFormat;
256
259
  };
257
- positiveColor: string;
258
- negativeColor: string;
259
260
  } & BasePreparedSeries;
260
261
  export type PreparedSankeySeries = {
261
262
  type: SankeySeries['type'];
@@ -5,10 +5,12 @@ import { getFormattedValue } from '../../../utils/chart/format';
5
5
  import { MIN_BAR_GAP, MIN_BAR_WIDTH } from '../constants';
6
6
  import { getXValue, getYValue } from '../utils';
7
7
  function getLabelData(d, plotHeight) {
8
+ var _a, _b;
8
9
  if (!d.series.dataLabels.enabled) {
9
10
  return undefined;
10
11
  }
11
- const text = getFormattedValue(Object.assign({ value: d.data.label || d.subTotal }, d.series.dataLabels));
12
+ const labelValue = (_b = (_a = d.data.label) !== null && _a !== void 0 ? _a : d.data.y) !== null && _b !== void 0 ? _b : d.subTotal;
13
+ const text = getFormattedValue(Object.assign({ value: labelValue }, d.series.dataLabels));
12
14
  const style = d.series.dataLabels.style;
13
15
  const { maxHeight: height, maxWidth: width } = getLabelsSize({ labels: [text], style });
14
16
  let y;
@@ -60,7 +62,7 @@ export const prepareWaterfallData = (args) => {
60
62
  acc.push(...s.data.map((d) => ({ data: d, series: s })));
61
63
  return acc;
62
64
  }, []);
63
- const data = sortBy(flattenData, (d) => d.data.x);
65
+ const data = sortBy(flattenData, (d) => d.data.index);
64
66
  const bandWidth = getBandWidth({
65
67
  series,
66
68
  xAxis,
@@ -75,6 +75,8 @@ 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
+ /** Whether axis, including axis title, line, ticks and labels, should be visible. */
79
+ visible?: boolean;
78
80
  }
79
81
  export interface ChartXAxis extends ChartAxis {
80
82
  }
@@ -36,5 +36,14 @@ export interface WaterfallSeries<T = MeaningfulAny> extends BaseSeries {
36
36
  /** Individual series legend options. Has higher priority than legend options in widget data. */
37
37
  legend?: ChartLegend & {
38
38
  symbol?: RectLegendSymbolOptions;
39
+ /** The legend item text for positive, negative values and totals. */
40
+ itemText?: {
41
+ /** The legend item text for positive values. */
42
+ positive?: string;
43
+ /** The legend item text for negative values. */
44
+ negative?: string;
45
+ /** The legend item text for totals. */
46
+ totals?: string;
47
+ };
39
48
  };
40
49
  }
@@ -19,6 +19,7 @@ type AxisBottomArgs = {
19
19
  size: number;
20
20
  color?: string;
21
21
  };
22
+ leftmostLimit?: number;
22
23
  };
23
24
  export declare function axisBottom(args: AxisBottomArgs): (selection: Selection<SVGGElement, unknown, null, undefined>) => void;
24
25
  export {};
@@ -16,7 +16,7 @@ function addDomain(selection, options) {
16
16
  }
17
17
  }
18
18
  export function axisBottom(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;
19
+ const { leftmostLimit = 0, 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 });
@@ -103,6 +103,21 @@ export function axisBottom(args) {
103
103
  .remove();
104
104
  // add an ellipsis to the labels that go beyond the boundaries of the chart
105
105
  labels.each(function (_d, i, nodes) {
106
+ if (i === 0) {
107
+ const currentElement = this;
108
+ const text = select(currentElement);
109
+ const currentElementPosition = currentElement.getBoundingClientRect();
110
+ const nextElement = nodes[i + 1];
111
+ const nextElementPosition = nextElement === null || nextElement === void 0 ? void 0 : nextElement.getBoundingClientRect();
112
+ if (currentElementPosition.left < leftmostLimit) {
113
+ const remainSpace = nextElementPosition.left -
114
+ currentElementPosition.right +
115
+ x -
116
+ labelsMargin;
117
+ text.attr('text-anchor', 'start');
118
+ setEllipsisForOverflowText(text, remainSpace);
119
+ }
120
+ }
106
121
  if (i === nodes.length - 1) {
107
122
  const currentElement = this;
108
123
  const prevElement = nodes[i - 1];
@@ -1,30 +1,40 @@
1
1
  import { Delaunay, bisector, sort } from 'd3';
2
2
  import get from 'lodash/get';
3
3
  import groupBy from 'lodash/groupBy';
4
- function getClosestPointsByXValue(x, y, points) {
4
+ function getClosestYIndex(items, y) {
5
5
  var _a, _b, _c, _d;
6
- const sorted = sort(points, (p) => p.x);
7
- const closestXIndex = bisector((p) => p.x).center(sorted, x);
8
- if (closestXIndex === -1) {
9
- return [];
10
- }
11
- const closestX = sorted[closestXIndex].x;
12
- const closestPoints = sort(points.filter((p) => p.x === closestX), (p) => p.y0);
13
6
  let closestYIndex = -1;
14
- if (y < ((_a = closestPoints[0]) === null || _a === void 0 ? void 0 : _a.y0)) {
7
+ if (y < ((_a = items[0]) === null || _a === void 0 ? void 0 : _a.y0)) {
15
8
  closestYIndex = 0;
16
9
  }
17
- else if (y > ((_b = closestPoints[closestPoints.length - 1]) === null || _b === void 0 ? void 0 : _b.y1)) {
18
- closestYIndex = closestPoints.length - 1;
10
+ else if (y > ((_b = items[items.length - 1]) === null || _b === void 0 ? void 0 : _b.y1)) {
11
+ closestYIndex = items.length - 1;
19
12
  }
20
13
  else {
21
- closestYIndex = closestPoints.findIndex((p) => y > p.y0 && y < p.y1);
14
+ closestYIndex = items.findIndex((p) => y > p.y0 && y < p.y1);
22
15
  if (closestYIndex === -1) {
23
- const sortedY = sort(closestPoints.map((p, index) => ({ index, y: p.y1 + (p.y0 - p.y1) / 2 })), (p) => p.y);
16
+ const sortedY = sort(items.map((p, index) => ({ index, y: p.y1 + (p.y0 - p.y1) / 2 })), (p) => p.y);
24
17
  const sortedYIndex = bisector((p) => p.y).center(sortedY, y);
25
18
  closestYIndex = (_d = (_c = sortedY[sortedYIndex]) === null || _c === void 0 ? void 0 : _c.index) !== null && _d !== void 0 ? _d : -1;
26
19
  }
27
20
  }
21
+ return closestYIndex;
22
+ }
23
+ function getClosestPointsByXValue(x, y, points) {
24
+ const sorted = sort(points, (p) => p.x);
25
+ const closestXIndex = bisector((p) => p.x).center(sorted, x);
26
+ if (closestXIndex === -1) {
27
+ return [];
28
+ }
29
+ const closestX = sorted[closestXIndex].x;
30
+ const filtered = points.filter((p) => p.x === closestX);
31
+ const groupedBySeries = Object.values(groupBy(filtered, (p) => get(p.series, 'id'))).map((items) => {
32
+ const sortedByY = sort(items, (p) => p.y0);
33
+ const index = getClosestYIndex(sortedByY, y);
34
+ return sortedByY[index === -1 ? 0 : index];
35
+ });
36
+ const closestPoints = sort(groupedBySeries, (p) => p.y0);
37
+ const closestYIndex = getClosestYIndex(closestPoints, y);
28
38
  return closestPoints.map((p, i) => ({
29
39
  data: p.data,
30
40
  series: p.series,
@@ -2,6 +2,7 @@ import { dateTime } from '@gravity-ui/date-utils';
2
2
  import { group, select } from 'd3';
3
3
  import get from 'lodash/get';
4
4
  import isNil from 'lodash/isNil';
5
+ import sortBy from 'lodash/sortBy';
5
6
  import { DEFAULT_AXIS_LABEL_FONT_SIZE } from '../../constants';
6
7
  import { getSeriesStackId } from '../../hooks/useSeries/utils';
7
8
  import { formatNumber, getNumberUnitRate } from '../../libs/format-number';
@@ -100,11 +101,10 @@ export const getDomainDataYBySeries = (series) => {
100
101
  }
101
102
  case 'waterfall': {
102
103
  let yValue = 0;
103
- seriesList.forEach((s) => {
104
- s.data.forEach((d) => {
105
- yValue += Number(d.y) || 0;
106
- acc.push(yValue);
107
- });
104
+ const points = seriesList.map((s) => s.data).flat();
105
+ sortBy(points, (p) => p.index).forEach((d) => {
106
+ yValue += Number(d.y) || 0;
107
+ acc.push(yValue);
108
108
  });
109
109
  break;
110
110
  }
@@ -1,4 +1,4 @@
1
- import type { PreparedWaterfallSeries } from '../../../hooks';
1
+ import type { PreparedWaterfallSeries, PreparedWaterfallSeriesData } from '../../../hooks';
2
2
  import type { WaterfallSeriesData } from '../../../types';
3
3
  export declare function getWaterfallPointColor(point: WaterfallSeriesData, series: PreparedWaterfallSeries): string;
4
- export declare function getWaterfallPointSubtotal(point: WaterfallSeriesData, series: PreparedWaterfallSeries): number | null;
4
+ export declare function getWaterfallPointSubtotal(point: PreparedWaterfallSeriesData, series: PreparedWaterfallSeries): number | null;
@@ -2,13 +2,7 @@ export function getWaterfallPointColor(point, series) {
2
2
  if (point.color) {
3
3
  return point.color;
4
4
  }
5
- if (point.total) {
6
- return series.color;
7
- }
8
- if (Number(point.y) > 0) {
9
- return series.positiveColor;
10
- }
11
- return series.negativeColor;
5
+ return series.color;
12
6
  }
13
7
  export function getWaterfallPointSubtotal(point, series) {
14
8
  const pointIndex = series.data.indexOf(point);
@@ -1,5 +1,6 @@
1
1
  export declare function pieCenterText(text: string, options?: {
2
2
  padding?: number;
3
+ color?: string;
3
4
  }): ((args: {
4
5
  series: {
5
6
  innerRadius: number;
@@ -7,6 +7,7 @@ export function pieCenterText(text, options) {
7
7
  return undefined;
8
8
  }
9
9
  const padding = get(options, 'padding', 12);
10
+ const color = get(options, 'color', 'currentColor');
10
11
  return function (args) {
11
12
  let fontSize = MAX_FONT_SIZE;
12
13
  const textSize = getLabelsSize({ labels: [text], style: { fontSize: `${fontSize}px` } });
@@ -17,7 +18,8 @@ export function pieCenterText(text, options) {
17
18
  .text(text)
18
19
  .attr('text-anchor', 'middle')
19
20
  .attr('alignment-baseline', 'middle')
20
- .style('font-size', `${fontSize}px`);
21
+ .style('font-size', `${fontSize}px`)
22
+ .style('fill', color);
21
23
  return container.node();
22
24
  };
23
25
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gravity-ui/charts",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "description": "React component used to render charts",
5
5
  "license": "MIT",
6
6
  "main": "dist/cjs/index.js",