@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.
- package/dist/cjs/components/Axis/AxisX.d.ts +1 -0
- package/dist/cjs/components/Axis/AxisX.js +16 -8
- package/dist/cjs/components/Axis/AxisY.d.ts +1 -0
- package/dist/cjs/components/Axis/AxisY.js +38 -10
- package/dist/cjs/components/ChartInner/index.js +3 -3
- package/dist/cjs/components/ChartInner/useChartInnerProps.d.ts +3 -0
- package/dist/cjs/components/ChartInner/useChartInnerProps.js +14 -3
- package/dist/cjs/components/Tooltip/DefaultContent.js +41 -23
- package/dist/cjs/hooks/useChartDimensions/index.js +3 -0
- package/dist/cjs/hooks/useChartDimensions/utils.js +3 -0
- package/dist/cjs/hooks/useChartOptions/x-axis.js +1 -0
- package/dist/cjs/hooks/useChartOptions/y-axis.js +1 -0
- package/dist/cjs/hooks/useSeries/prepare-waterfall.js +40 -28
- package/dist/cjs/hooks/useSeries/types.d.ts +4 -3
- package/dist/cjs/hooks/useShapes/waterfall/prepare-data.js +4 -2
- package/dist/cjs/types/chart/axis.d.ts +2 -0
- package/dist/cjs/types/chart/waterfall.d.ts +9 -0
- package/dist/cjs/utils/chart/axis-generators/bottom.d.ts +1 -0
- package/dist/cjs/utils/chart/axis-generators/bottom.js +16 -1
- package/dist/cjs/utils/chart/get-closest-data.js +23 -13
- package/dist/cjs/utils/chart/index.js +5 -5
- package/dist/cjs/utils/chart/series/waterfall.d.ts +2 -2
- package/dist/cjs/utils/chart/series/waterfall.js +1 -7
- package/dist/cjs/utils/chart-ui/pie-center-text.d.ts +1 -0
- package/dist/cjs/utils/chart-ui/pie-center-text.js +3 -1
- package/dist/esm/components/Axis/AxisX.d.ts +1 -0
- package/dist/esm/components/Axis/AxisX.js +16 -8
- package/dist/esm/components/Axis/AxisY.d.ts +1 -0
- package/dist/esm/components/Axis/AxisY.js +38 -10
- package/dist/esm/components/ChartInner/index.js +3 -3
- package/dist/esm/components/ChartInner/useChartInnerProps.d.ts +3 -0
- package/dist/esm/components/ChartInner/useChartInnerProps.js +14 -3
- package/dist/esm/components/Tooltip/DefaultContent.js +41 -23
- package/dist/esm/hooks/useChartDimensions/index.js +3 -0
- package/dist/esm/hooks/useChartDimensions/utils.js +3 -0
- package/dist/esm/hooks/useChartOptions/x-axis.js +1 -0
- package/dist/esm/hooks/useChartOptions/y-axis.js +1 -0
- package/dist/esm/hooks/useSeries/prepare-waterfall.js +40 -28
- package/dist/esm/hooks/useSeries/types.d.ts +4 -3
- package/dist/esm/hooks/useShapes/waterfall/prepare-data.js +4 -2
- package/dist/esm/types/chart/axis.d.ts +2 -0
- package/dist/esm/types/chart/waterfall.d.ts +9 -0
- package/dist/esm/utils/chart/axis-generators/bottom.d.ts +1 -0
- package/dist/esm/utils/chart/axis-generators/bottom.js +16 -1
- package/dist/esm/utils/chart/get-closest-data.js +23 -13
- package/dist/esm/utils/chart/index.js +5 -5
- package/dist/esm/utils/chart/series/waterfall.d.ts +2 -2
- package/dist/esm/utils/chart/series/waterfall.js +1 -7
- package/dist/esm/utils/chart-ui/pie-center-text.d.ts +1 -0
- package/dist/esm/utils/chart-ui/pie-center-text.js +3 -1
- 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:
|
|
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
|
|
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.
|
|
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
|
}
|
|
@@ -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
|
|
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 =
|
|
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 =
|
|
18
|
-
closestYIndex =
|
|
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 =
|
|
14
|
+
closestYIndex = items.findIndex((p) => y > p.y0 && y < p.y1);
|
|
22
15
|
if (closestYIndex === -1) {
|
|
23
|
-
const sortedY = sort(
|
|
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.
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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:
|
|
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
|
-
|
|
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);
|
|
@@ -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
|
}
|