@gravity-ui/charts 1.18.0 → 1.18.2
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/AxisY/prepare-axis-data.d.ts +3 -2
- package/dist/cjs/components/AxisY/prepare-axis-data.js +6 -6
- package/dist/cjs/components/AxisY/utils.d.ts +4 -2
- package/dist/cjs/components/AxisY/utils.js +47 -14
- package/dist/cjs/components/ChartInner/index.js +2 -1
- package/dist/cjs/components/ChartInner/useChartInnerProps.js +1 -2
- package/dist/cjs/components/Legend/index.js +1 -1
- package/dist/cjs/hooks/useAxisScales/index.d.ts +0 -1
- package/dist/cjs/hooks/useAxisScales/index.js +42 -21
- package/dist/cjs/hooks/useChartOptions/utils.d.ts +5 -2
- package/dist/cjs/hooks/useChartOptions/utils.js +2 -3
- package/dist/cjs/hooks/useChartOptions/y-axis.d.ts +1 -3
- package/dist/cjs/hooks/useChartOptions/y-axis.js +3 -5
- package/dist/cjs/hooks/useSeries/prepare-bar-y.js +3 -1
- package/dist/cjs/hooks/useShapes/bar-y/prepare-data.d.ts +4 -2
- package/dist/cjs/hooks/useShapes/bar-y/prepare-data.js +46 -24
- package/dist/cjs/hooks/useShapes/index.js +2 -0
- package/dist/cjs/hooks/useZoom/utils.js +24 -8
- package/dist/cjs/hooks/utils/bar-y.d.ts +7 -10
- package/dist/cjs/hooks/utils/bar-y.js +33 -18
- package/dist/cjs/utils/chart/array.js +3 -0
- package/dist/cjs/utils/chart/format.js +2 -2
- package/dist/cjs/utils/chart/index.js +2 -1
- package/dist/cjs/utils/chart/labels.d.ts +27 -5
- package/dist/cjs/utils/chart/labels.js +39 -3
- package/dist/cjs/validation/index.js +4 -1
- package/dist/esm/components/AxisY/prepare-axis-data.d.ts +3 -2
- package/dist/esm/components/AxisY/prepare-axis-data.js +6 -6
- package/dist/esm/components/AxisY/utils.d.ts +4 -2
- package/dist/esm/components/AxisY/utils.js +47 -14
- package/dist/esm/components/ChartInner/index.js +2 -1
- package/dist/esm/components/ChartInner/useChartInnerProps.js +1 -2
- package/dist/esm/components/Legend/index.js +1 -1
- package/dist/esm/hooks/useAxisScales/index.d.ts +0 -1
- package/dist/esm/hooks/useAxisScales/index.js +42 -21
- package/dist/esm/hooks/useChartOptions/utils.d.ts +5 -2
- package/dist/esm/hooks/useChartOptions/utils.js +2 -3
- package/dist/esm/hooks/useChartOptions/y-axis.d.ts +1 -3
- package/dist/esm/hooks/useChartOptions/y-axis.js +3 -5
- package/dist/esm/hooks/useSeries/prepare-bar-y.js +3 -1
- package/dist/esm/hooks/useShapes/bar-y/prepare-data.d.ts +4 -2
- package/dist/esm/hooks/useShapes/bar-y/prepare-data.js +46 -24
- package/dist/esm/hooks/useShapes/index.js +2 -0
- package/dist/esm/hooks/useZoom/utils.js +24 -8
- package/dist/esm/hooks/utils/bar-y.d.ts +7 -10
- package/dist/esm/hooks/utils/bar-y.js +33 -18
- package/dist/esm/utils/chart/array.js +3 -0
- package/dist/esm/utils/chart/format.js +2 -2
- package/dist/esm/utils/chart/index.js +2 -1
- package/dist/esm/utils/chart/labels.d.ts +27 -5
- package/dist/esm/utils/chart/labels.js +39 -3
- package/dist/esm/validation/index.js +4 -1
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AxisPlot,
|
|
1
|
+
import type { AxisPlot, ChartAxis } from '../../types';
|
|
2
2
|
export declare function prepareAxisPlotLabel(d: AxisPlot): {
|
|
3
3
|
text: string;
|
|
4
4
|
style: {
|
|
@@ -8,4 +8,7 @@ export declare function prepareAxisPlotLabel(d: AxisPlot): {
|
|
|
8
8
|
};
|
|
9
9
|
padding: number;
|
|
10
10
|
};
|
|
11
|
-
export declare function getAxisCategories(
|
|
11
|
+
export declare function getAxisCategories({ categories, order, }?: {
|
|
12
|
+
categories?: string[];
|
|
13
|
+
order?: ChartAxis['order'];
|
|
14
|
+
}): string[] | undefined;
|
|
@@ -8,10 +8,9 @@ export function prepareAxisPlotLabel(d) {
|
|
|
8
8
|
padding: (_e = (_d = d.label) === null || _d === void 0 ? void 0 : _d.padding) !== null && _e !== void 0 ? _e : 5,
|
|
9
9
|
};
|
|
10
10
|
}
|
|
11
|
-
export function getAxisCategories(
|
|
12
|
-
const categories = axis === null || axis === void 0 ? void 0 : axis.categories;
|
|
11
|
+
export function getAxisCategories({ categories, order, } = {}) {
|
|
13
12
|
if (categories) {
|
|
14
|
-
switch (
|
|
13
|
+
switch (order) {
|
|
15
14
|
case 'reverse': {
|
|
16
15
|
return reverse(categories);
|
|
17
16
|
}
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import type { ChartSeries, ChartYAxis } from '../../types';
|
|
2
|
-
import type { PreparedSeriesOptions } from '../useSeries/types';
|
|
3
2
|
import type { PreparedAxis } from './types';
|
|
4
|
-
export declare const getPreparedYAxis: ({ height, boundsHeight, width, seriesData,
|
|
3
|
+
export declare const getPreparedYAxis: ({ height, boundsHeight, width, seriesData, yAxis, }: {
|
|
5
4
|
height: number;
|
|
6
5
|
boundsHeight: number;
|
|
7
6
|
width: number;
|
|
8
7
|
seriesData: ChartSeries[];
|
|
9
|
-
seriesOptions: PreparedSeriesOptions;
|
|
10
8
|
yAxis: ChartYAxis[] | undefined;
|
|
11
9
|
}) => Promise<PreparedAxis[]>;
|
|
@@ -5,7 +5,7 @@ import { calculateNumericProperty, formatAxisTickLabel, getClosestPointsRange, g
|
|
|
5
5
|
import { createYScale } from '../useAxisScales';
|
|
6
6
|
import { getAxisCategories, prepareAxisPlotLabel } from './utils';
|
|
7
7
|
const getAxisLabelMaxWidth = async (args) => {
|
|
8
|
-
const { axis, seriesData,
|
|
8
|
+
const { axis, seriesData, height } = args;
|
|
9
9
|
if (!axis.labels.enabled) {
|
|
10
10
|
return { height: 0, width: 0 };
|
|
11
11
|
}
|
|
@@ -13,14 +13,13 @@ const getAxisLabelMaxWidth = async (args) => {
|
|
|
13
13
|
axis,
|
|
14
14
|
boundsHeight: height,
|
|
15
15
|
series: seriesData,
|
|
16
|
-
seriesOptions,
|
|
17
16
|
});
|
|
18
17
|
if (!scale) {
|
|
19
18
|
return { height: 0, width: 0 };
|
|
20
19
|
}
|
|
21
20
|
const getTextSize = getTextSizeFn({ style: axis.labels.style });
|
|
22
21
|
const labelLineHeight = (await getTextSize('Tmp')).height;
|
|
23
|
-
const tickValues = getTickValues({ axis, scale, labelLineHeight });
|
|
22
|
+
const tickValues = getTickValues({ axis, scale, labelLineHeight, series: seriesData });
|
|
24
23
|
const ticks = getScaleTicks(scale);
|
|
25
24
|
const tickStep = getClosestPointsRange(axis, ticks);
|
|
26
25
|
if (axis.type === 'datetime' && !axis.labels.dateFormat) {
|
|
@@ -39,7 +38,7 @@ const getAxisLabelMaxWidth = async (args) => {
|
|
|
39
38
|
});
|
|
40
39
|
return { height: size.maxHeight, width: size.maxWidth };
|
|
41
40
|
};
|
|
42
|
-
export const getPreparedYAxis = ({ height, boundsHeight, width, seriesData,
|
|
41
|
+
export const getPreparedYAxis = ({ height, boundsHeight, width, seriesData, yAxis, }) => {
|
|
43
42
|
const axisByPlot = [];
|
|
44
43
|
const axisItems = yAxis || [{}];
|
|
45
44
|
const hasAxisRelatedSeries = seriesData.some(isAxisRelatedSeries);
|
|
@@ -153,7 +152,6 @@ export const getPreparedYAxis = ({ height, boundsHeight, width, seriesData, seri
|
|
|
153
152
|
const { height: labelsHeight, width: labelsWidth } = await getAxisLabelMaxWidth({
|
|
154
153
|
axis: preparedAxis,
|
|
155
154
|
seriesData,
|
|
156
|
-
seriesOptions,
|
|
157
155
|
height: boundsHeight,
|
|
158
156
|
});
|
|
159
157
|
preparedAxis.labels.height = labelsHeight;
|
|
@@ -9,7 +9,9 @@ async function prepareDataLabels(series) {
|
|
|
9
9
|
const style = Object.assign({}, DEFAULT_DATALABELS_STYLE, (_a = series.dataLabels) === null || _a === void 0 ? void 0 : _a.style);
|
|
10
10
|
const html = get(series, 'dataLabels.html', false);
|
|
11
11
|
const labels = enabled
|
|
12
|
-
? series.data
|
|
12
|
+
? series.data
|
|
13
|
+
.filter((d) => Boolean(d.x || d.label))
|
|
14
|
+
.map((d) => getFormattedValue(Object.assign({ value: d.x || d.label }, series.dataLabels)))
|
|
13
15
|
: [];
|
|
14
16
|
const { maxHeight = 0, maxWidth = 0 } = await getLabelsSize({
|
|
15
17
|
labels,
|
|
@@ -2,11 +2,13 @@ import type { ChartScale } from '../../useAxisScales';
|
|
|
2
2
|
import type { PreparedAxis } from '../../useChartOptions/types';
|
|
3
3
|
import type { PreparedBarYSeries, PreparedSeriesOptions } from '../../useSeries/types';
|
|
4
4
|
import type { BarYShapesArgs } from './types';
|
|
5
|
-
export declare
|
|
5
|
+
export declare function prepareBarYData(args: {
|
|
6
|
+
boundsHeight: number;
|
|
7
|
+
boundsWidth: number;
|
|
6
8
|
series: PreparedBarYSeries[];
|
|
7
9
|
seriesOptions: PreparedSeriesOptions;
|
|
8
10
|
xAxis: PreparedAxis;
|
|
9
11
|
xScale: ChartScale;
|
|
10
12
|
yAxis: PreparedAxis[];
|
|
11
13
|
yScale: (ChartScale | undefined)[];
|
|
12
|
-
})
|
|
14
|
+
}): Promise<BarYShapesArgs>;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { ascending, descending, sort } from 'd3';
|
|
2
2
|
import get from 'lodash/get';
|
|
3
|
-
import { filterOverlappingLabels, getLabelsSize, getTextSizeFn } from '../../../utils';
|
|
3
|
+
import { filterOverlappingLabels, getHtmlLabelConstraintedPosition, getLabelsSize, getSvgLabelConstraintedPosition, getTextSizeFn, } from '../../../utils';
|
|
4
4
|
import { getFormattedValue } from '../../../utils/chart/format';
|
|
5
|
-
import {
|
|
5
|
+
import { getBarYLayout, groupBarYDataByYValue } from '../../utils';
|
|
6
6
|
const DEFAULT_LABEL_PADDING = 7;
|
|
7
|
-
export
|
|
7
|
+
export async function prepareBarYData(args) {
|
|
8
8
|
var _a;
|
|
9
|
-
const { series, seriesOptions, yAxis, xScale, yScale: [yScale], } = args;
|
|
9
|
+
const { boundsHeight, boundsWidth, series, seriesOptions, yAxis, xScale, yScale: [yScale], } = args;
|
|
10
10
|
const stackGap = seriesOptions['bar-y'].stackGap;
|
|
11
11
|
const xLinearScale = xScale;
|
|
12
12
|
const yLinearScale = yScale;
|
|
@@ -18,7 +18,6 @@ export const prepareBarYData = async (args) => {
|
|
|
18
18
|
};
|
|
19
19
|
}
|
|
20
20
|
const yScaleRange = yLinearScale.range();
|
|
21
|
-
const plotHeight = Math.abs(yScaleRange[0] - yScaleRange[1]);
|
|
22
21
|
const sortingOptions = get(seriesOptions, 'bar-y.dataSorting');
|
|
23
22
|
const comparator = (sortingOptions === null || sortingOptions === void 0 ? void 0 : sortingOptions.direction) === 'desc' ? descending : ascending;
|
|
24
23
|
const sortKey = (() => {
|
|
@@ -35,13 +34,13 @@ export const prepareBarYData = async (args) => {
|
|
|
35
34
|
}
|
|
36
35
|
})();
|
|
37
36
|
const groupedData = groupBarYDataByYValue(series, yAxis);
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
37
|
+
const plotHeight = Math.abs(yScaleRange[0] - yScaleRange[1]);
|
|
38
|
+
const { bandSize, barGap, barSize } = getBarYLayout({
|
|
39
|
+
groupedData,
|
|
40
|
+
seriesOptions,
|
|
41
|
+
plotHeight,
|
|
42
|
+
scale: yScale,
|
|
43
|
+
});
|
|
45
44
|
const result = [];
|
|
46
45
|
const baseRangeValue = xLinearScale.range()[0];
|
|
47
46
|
Object.entries(groupedData).forEach(([yValue, val]) => {
|
|
@@ -106,7 +105,7 @@ export const prepareBarYData = async (args) => {
|
|
|
106
105
|
});
|
|
107
106
|
});
|
|
108
107
|
let labels = [];
|
|
109
|
-
|
|
108
|
+
let htmlElements = [];
|
|
110
109
|
const map = new Map();
|
|
111
110
|
for (let i = 0; i < result.length; i++) {
|
|
112
111
|
const prepared = result[i];
|
|
@@ -114,9 +113,6 @@ export const prepareBarYData = async (args) => {
|
|
|
114
113
|
if (dataLabels.enabled) {
|
|
115
114
|
const data = prepared.data;
|
|
116
115
|
const content = getFormattedValue(Object.assign({ value: data.label || data.x }, dataLabels));
|
|
117
|
-
const x = dataLabels.inside
|
|
118
|
-
? prepared.x + prepared.width / 2
|
|
119
|
-
: prepared.x + prepared.width + DEFAULT_LABEL_PADDING;
|
|
120
116
|
const y = prepared.y + prepared.height / 2;
|
|
121
117
|
if (dataLabels.html) {
|
|
122
118
|
const { maxHeight: height, maxWidth: width } = await getLabelsSize({
|
|
@@ -124,12 +120,23 @@ export const prepareBarYData = async (args) => {
|
|
|
124
120
|
style: dataLabels.style,
|
|
125
121
|
html: dataLabels.html,
|
|
126
122
|
});
|
|
127
|
-
|
|
123
|
+
const x = dataLabels.inside
|
|
124
|
+
? prepared.x + prepared.width / 2 - width / 2
|
|
125
|
+
: prepared.x + prepared.width + DEFAULT_LABEL_PADDING;
|
|
126
|
+
const constrainedPosition = getHtmlLabelConstraintedPosition({
|
|
127
|
+
boundsHeight,
|
|
128
|
+
boundsWidth,
|
|
129
|
+
height,
|
|
130
|
+
width,
|
|
128
131
|
x,
|
|
129
132
|
y: y - height / 2,
|
|
133
|
+
});
|
|
134
|
+
htmlElements.push({
|
|
130
135
|
content,
|
|
131
136
|
size: { width, height },
|
|
132
137
|
style: dataLabels.style,
|
|
138
|
+
x: constrainedPosition.x,
|
|
139
|
+
y: constrainedPosition.y,
|
|
133
140
|
});
|
|
134
141
|
}
|
|
135
142
|
else {
|
|
@@ -138,24 +145,39 @@ export const prepareBarYData = async (args) => {
|
|
|
138
145
|
}
|
|
139
146
|
const getTextSize = map.get(dataLabels.style);
|
|
140
147
|
const { width, height } = await getTextSize(content);
|
|
141
|
-
|
|
148
|
+
const x = dataLabels.inside
|
|
149
|
+
? prepared.x + prepared.width / 2 - width / 2
|
|
150
|
+
: prepared.x + prepared.width + DEFAULT_LABEL_PADDING;
|
|
151
|
+
const constrainedPosition = getSvgLabelConstraintedPosition({
|
|
152
|
+
boundsHeight,
|
|
153
|
+
boundsWidth,
|
|
154
|
+
height,
|
|
155
|
+
width,
|
|
142
156
|
x,
|
|
143
157
|
y: y + height / 2,
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
style: dataLabels.style,
|
|
147
|
-
series: prepared.series,
|
|
158
|
+
});
|
|
159
|
+
labels.push({
|
|
148
160
|
size: { width, height },
|
|
161
|
+
series: prepared.series,
|
|
162
|
+
style: dataLabels.style,
|
|
163
|
+
text: content,
|
|
164
|
+
textAnchor: 'start',
|
|
165
|
+
x: constrainedPosition.x,
|
|
166
|
+
y: constrainedPosition.y,
|
|
149
167
|
});
|
|
150
168
|
}
|
|
151
169
|
}
|
|
152
170
|
}
|
|
153
|
-
|
|
171
|
+
const allowOverlap = (_a = result[0]) === null || _a === void 0 ? void 0 : _a.series.dataLabels.allowOverlap;
|
|
172
|
+
if (labels.length && !allowOverlap) {
|
|
154
173
|
labels = filterOverlappingLabels(labels);
|
|
155
174
|
}
|
|
175
|
+
else if (htmlElements.length && !allowOverlap) {
|
|
176
|
+
htmlElements = filterOverlappingLabels(htmlElements);
|
|
177
|
+
}
|
|
156
178
|
return {
|
|
157
179
|
shapes: result,
|
|
158
180
|
labels,
|
|
159
181
|
htmlElements,
|
|
160
182
|
};
|
|
161
|
-
}
|
|
183
|
+
}
|
|
@@ -56,6 +56,8 @@ export const useShapes = (args) => {
|
|
|
56
56
|
case 'bar-y': {
|
|
57
57
|
if (xAxis && xScale && (yScale === null || yScale === void 0 ? void 0 : yScale.length)) {
|
|
58
58
|
const preparedData = await prepareBarYData({
|
|
59
|
+
boundsHeight,
|
|
60
|
+
boundsWidth,
|
|
59
61
|
series: chartSeries,
|
|
60
62
|
seriesOptions,
|
|
61
63
|
xAxis,
|
|
@@ -95,6 +95,22 @@ function selectionXToZoomBounds(args) {
|
|
|
95
95
|
}
|
|
96
96
|
}
|
|
97
97
|
}
|
|
98
|
+
function getYMinMaxFromSelection(args) {
|
|
99
|
+
const { selection, yAxis } = args;
|
|
100
|
+
let yMin;
|
|
101
|
+
let yMax;
|
|
102
|
+
switch (yAxis.order) {
|
|
103
|
+
case 'reverse':
|
|
104
|
+
case 'sortDesc': {
|
|
105
|
+
[yMin, yMax] = selection;
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
default: {
|
|
109
|
+
[yMax, yMin] = selection;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return [yMin, yMax];
|
|
113
|
+
}
|
|
98
114
|
function selectionYToZoomBounds(args) {
|
|
99
115
|
const { yAxis, yScale, selection } = args;
|
|
100
116
|
switch (yAxis.type) {
|
|
@@ -104,8 +120,8 @@ function selectionYToZoomBounds(args) {
|
|
|
104
120
|
const categories = yAxis.categories || [];
|
|
105
121
|
const currentDomain = bandScale.domain();
|
|
106
122
|
const step = bandScale.step();
|
|
107
|
-
let startIndex = currentDomain.length - 1 - Math.floor(y0 / step);
|
|
108
|
-
let endIndex = currentDomain.length - 1 - Math.floor(y1 / step);
|
|
123
|
+
let startIndex = Math.max(0, currentDomain.length - 1 - Math.floor(y0 / step));
|
|
124
|
+
let endIndex = Math.min(currentDomain.length - 1, currentDomain.length - 1 - Math.floor(y1 / step));
|
|
109
125
|
const startCategory = currentDomain[startIndex];
|
|
110
126
|
const endCategory = currentDomain[endIndex];
|
|
111
127
|
startIndex = categories.indexOf(startCategory);
|
|
@@ -119,18 +135,18 @@ function selectionYToZoomBounds(args) {
|
|
|
119
135
|
return [startIndex, endIndex];
|
|
120
136
|
}
|
|
121
137
|
case 'datetime': {
|
|
122
|
-
const [
|
|
138
|
+
const [yMin, yMax] = getYMinMaxFromSelection({ selection, yAxis });
|
|
123
139
|
const timeScale = yScale;
|
|
124
|
-
const minTimestamp = timeScale.invert(
|
|
125
|
-
const maxTimestamp = timeScale.invert(
|
|
140
|
+
const minTimestamp = timeScale.invert(yMin).getTime();
|
|
141
|
+
const maxTimestamp = timeScale.invert(yMax).getTime();
|
|
126
142
|
return [minTimestamp, maxTimestamp];
|
|
127
143
|
}
|
|
128
144
|
case 'linear':
|
|
129
145
|
case 'logarithmic': {
|
|
130
|
-
const [
|
|
146
|
+
const [yMin, yMax] = getYMinMaxFromSelection({ selection, yAxis });
|
|
131
147
|
const linearScale = yScale;
|
|
132
|
-
const minValue = linearScale.invert(
|
|
133
|
-
const maxValue = linearScale.invert(
|
|
148
|
+
const minValue = linearScale.invert(yMin);
|
|
149
|
+
const maxValue = linearScale.invert(yMax);
|
|
134
150
|
return [minValue, maxValue];
|
|
135
151
|
}
|
|
136
152
|
default: {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { AxisDomain, AxisScale } from 'd3';
|
|
1
2
|
import type { BarYSeries, BarYSeriesData } from '../../types';
|
|
2
3
|
import type { ChartScale } from '../useAxisScales';
|
|
3
4
|
import type { PreparedAxis } from '../useChartOptions/types';
|
|
@@ -6,19 +7,15 @@ export declare function groupBarYDataByYValue<T extends BarYSeries | PreparedBar
|
|
|
6
7
|
data: BarYSeriesData;
|
|
7
8
|
series: T;
|
|
8
9
|
}[]>>;
|
|
9
|
-
export declare function
|
|
10
|
+
export declare function getBandSize({ domain, scale, }: {
|
|
11
|
+
domain: AxisDomain[];
|
|
12
|
+
scale: AxisScale<AxisDomain> | undefined;
|
|
13
|
+
}): number;
|
|
14
|
+
export declare function getBarYLayout(args: {
|
|
10
15
|
plotHeight: number;
|
|
11
16
|
seriesOptions: PreparedSeriesOptions;
|
|
12
17
|
groupedData: ReturnType<typeof groupBarYDataByYValue>;
|
|
13
|
-
|
|
14
|
-
bandSize: number;
|
|
15
|
-
barGap: number;
|
|
16
|
-
barSize: number;
|
|
17
|
-
};
|
|
18
|
-
export declare function getBarYLayoutForCategoryScale(args: {
|
|
19
|
-
groupedData: ReturnType<typeof groupBarYDataByYValue>;
|
|
20
|
-
seriesOptions: PreparedSeriesOptions;
|
|
21
|
-
yScale: ChartScale;
|
|
18
|
+
scale: ChartScale | undefined;
|
|
22
19
|
}): {
|
|
23
20
|
bandSize: number;
|
|
24
21
|
barGap: number;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { max } from 'd3';
|
|
2
2
|
import get from 'lodash/get';
|
|
3
|
-
import { getDataCategoryValue } from '../../utils';
|
|
3
|
+
import { getDataCategoryValue, isBandScale } from '../../utils';
|
|
4
4
|
import { MIN_BAR_GAP, MIN_BAR_GROUP_GAP, MIN_BAR_WIDTH } from '../constants';
|
|
5
5
|
import { getSeriesStackId } from '../useSeries/utils';
|
|
6
6
|
export function groupBarYDataByYValue(series, yAxis) {
|
|
@@ -27,28 +27,43 @@ export function groupBarYDataByYValue(series, yAxis) {
|
|
|
27
27
|
});
|
|
28
28
|
return data;
|
|
29
29
|
}
|
|
30
|
-
export function
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
30
|
+
export function getBandSize({ domain, scale, }) {
|
|
31
|
+
if (!scale || !domain.length) {
|
|
32
|
+
return 0;
|
|
33
|
+
}
|
|
34
|
+
if (isBandScale(scale)) {
|
|
35
|
+
return scale.bandwidth();
|
|
36
|
+
}
|
|
37
|
+
const range = scale.range();
|
|
38
|
+
const plotHeight = Math.abs(range[0] - range[1]);
|
|
39
|
+
if (domain.length === 1) {
|
|
40
|
+
return plotHeight;
|
|
41
|
+
}
|
|
42
|
+
// for the numeric or datetime axes, you first need to count domain.length + 1,
|
|
43
|
+
// since the extreme points are located not in the center of the bar, but along the edges of the axes
|
|
44
|
+
let bandWidth = plotHeight / (domain.length - 1);
|
|
45
|
+
domain.forEach((current, index) => {
|
|
46
|
+
if (index > 0) {
|
|
47
|
+
const prev = domain[index - 1];
|
|
48
|
+
const prevY = scale(prev);
|
|
49
|
+
const currentY = scale(current);
|
|
50
|
+
if (typeof prevY === 'number' && typeof currentY === 'number') {
|
|
51
|
+
const distance = Math.abs(prevY - currentY);
|
|
52
|
+
bandWidth = Math.min(bandWidth, distance);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
return bandWidth;
|
|
43
57
|
}
|
|
44
|
-
export function
|
|
45
|
-
const { groupedData, seriesOptions,
|
|
58
|
+
export function getBarYLayout(args) {
|
|
59
|
+
const { groupedData, seriesOptions, scale } = args;
|
|
46
60
|
const barMaxWidth = get(seriesOptions, 'bar-y.barMaxWidth');
|
|
47
61
|
const barPadding = get(seriesOptions, 'bar-y.barPadding');
|
|
48
62
|
const groupPadding = get(seriesOptions, 'bar-y.groupPadding');
|
|
49
|
-
const
|
|
50
|
-
const
|
|
63
|
+
const domain = Object.keys(groupedData);
|
|
64
|
+
const bandSize = getBandSize({ domain, scale: scale });
|
|
51
65
|
const groupGap = Math.max(bandSize * groupPadding, MIN_BAR_GROUP_GAP);
|
|
66
|
+
const maxGroupSize = max(Object.values(groupedData), (d) => Object.values(d).length) || 1;
|
|
52
67
|
const groupSize = bandSize - groupGap;
|
|
53
68
|
const barGap = Math.max(bandSize * barPadding, MIN_BAR_GAP);
|
|
54
69
|
const barSize = Math.max(MIN_BAR_WIDTH, Math.min(groupSize / maxGroupSize - barGap, barMaxWidth));
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { dateTimeUtc } from '@gravity-ui/date-utils';
|
|
2
2
|
import capitalize from 'lodash/capitalize';
|
|
3
3
|
import { DEFAULT_DATE_FORMAT } from '../../constants';
|
|
4
4
|
import { formatNumber, getDefaultUnit } from '../../libs';
|
|
@@ -6,7 +6,7 @@ import { getDefaultDateFormat } from './time';
|
|
|
6
6
|
const LETTER_MOUNTH_AT_START_FORMAT_REGEXP = /^M{3,}/;
|
|
7
7
|
function getFormattedDate(args) {
|
|
8
8
|
const { value, format = DEFAULT_DATE_FORMAT } = args;
|
|
9
|
-
const date =
|
|
9
|
+
const date = dateTimeUtc({ input: value });
|
|
10
10
|
if (date === null || date === void 0 ? void 0 : date.isValid()) {
|
|
11
11
|
const formattedDate = date.format(format);
|
|
12
12
|
if (LETTER_MOUNTH_AT_START_FORMAT_REGEXP.test(format)) {
|
|
@@ -128,7 +128,7 @@ export function getDefaultMinYAxisValue(series) {
|
|
|
128
128
|
}
|
|
129
129
|
export const getDomainDataYBySeries = (series) => {
|
|
130
130
|
const groupedSeries = group(series, (item) => item.type);
|
|
131
|
-
|
|
131
|
+
const items = Array.from(groupedSeries).reduce((acc, [type, seriesList]) => {
|
|
132
132
|
switch (type) {
|
|
133
133
|
case 'area':
|
|
134
134
|
case 'bar-x': {
|
|
@@ -152,6 +152,7 @@ export const getDomainDataYBySeries = (series) => {
|
|
|
152
152
|
}
|
|
153
153
|
return acc;
|
|
154
154
|
}, []);
|
|
155
|
+
return Array.from(new Set(items));
|
|
155
156
|
};
|
|
156
157
|
// Uses to get all series names array (except `pie` charts)
|
|
157
158
|
export const getSeriesNames = (series) => {
|
|
@@ -1,6 +1,28 @@
|
|
|
1
|
-
import type { LabelData } from '../../types';
|
|
1
|
+
import type { HtmlItem, LabelData } from '../../types';
|
|
2
2
|
export declare function getLeftPosition(label: LabelData): number;
|
|
3
|
-
export declare function getOverlappingByX(rect1: LabelData, rect2: LabelData, gap?: number): number;
|
|
4
|
-
export declare function getOverlappingByY(rect1: LabelData, rect2: LabelData, gap?: number): number;
|
|
5
|
-
export declare function isLabelsOverlapping<T extends LabelData>(label1: T, label2: T, padding?: number): boolean;
|
|
6
|
-
export declare function filterOverlappingLabels<T extends LabelData>(labels: T[]): T[];
|
|
3
|
+
export declare function getOverlappingByX(rect1: LabelData | HtmlItem, rect2: LabelData | HtmlItem, gap?: number): number;
|
|
4
|
+
export declare function getOverlappingByY(rect1: LabelData | HtmlItem, rect2: LabelData | HtmlItem, gap?: number): number;
|
|
5
|
+
export declare function isLabelsOverlapping<T extends LabelData | HtmlItem>(label1: T, label2: T, padding?: number): boolean;
|
|
6
|
+
export declare function filterOverlappingLabels<T extends LabelData | HtmlItem>(labels: T[]): T[];
|
|
7
|
+
export declare function getSvgLabelConstraintedPosition(args: {
|
|
8
|
+
boundsHeight: number;
|
|
9
|
+
boundsWidth: number;
|
|
10
|
+
height: number;
|
|
11
|
+
width: number;
|
|
12
|
+
x: number;
|
|
13
|
+
y: number;
|
|
14
|
+
}): {
|
|
15
|
+
x: number;
|
|
16
|
+
y: number;
|
|
17
|
+
};
|
|
18
|
+
export declare function getHtmlLabelConstraintedPosition(args: {
|
|
19
|
+
boundsHeight: number;
|
|
20
|
+
boundsWidth: number;
|
|
21
|
+
height: number;
|
|
22
|
+
width: number;
|
|
23
|
+
x: number;
|
|
24
|
+
y: number;
|
|
25
|
+
}): {
|
|
26
|
+
x: number;
|
|
27
|
+
y: number;
|
|
28
|
+
};
|
|
@@ -16,9 +16,9 @@ export function getLeftPosition(label) {
|
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
18
|
export function getOverlappingByX(rect1, rect2, gap = 0) {
|
|
19
|
-
const left1 = getLeftPosition(rect1);
|
|
19
|
+
const left1 = 'textAnchor' in rect1 ? getLeftPosition(rect1) : rect1.x;
|
|
20
20
|
const right1 = left1 + rect1.size.width;
|
|
21
|
-
const left2 = getLeftPosition(rect2);
|
|
21
|
+
const left2 = 'textAnchor' in rect2 ? getLeftPosition(rect2) : rect2.x;
|
|
22
22
|
const right2 = left2 + rect2.size.width;
|
|
23
23
|
return Math.max(0, Math.min(right1, right2) - Math.max(left1, left2) + gap);
|
|
24
24
|
}
|
|
@@ -34,7 +34,7 @@ export function isLabelsOverlapping(label1, label2, padding = 0) {
|
|
|
34
34
|
}
|
|
35
35
|
export function filterOverlappingLabels(labels) {
|
|
36
36
|
const result = [];
|
|
37
|
-
const sorted = sortBy(labels, (d) => d.y, getLeftPosition);
|
|
37
|
+
const sorted = sortBy(labels, (d) => d.y, (d) => ('textAnchor' in d ? getLeftPosition(d) : d.x));
|
|
38
38
|
sorted.forEach((label) => {
|
|
39
39
|
if (!result.some((l) => isLabelsOverlapping(label, l))) {
|
|
40
40
|
result.push(label);
|
|
@@ -42,3 +42,39 @@ export function filterOverlappingLabels(labels) {
|
|
|
42
42
|
});
|
|
43
43
|
return result;
|
|
44
44
|
}
|
|
45
|
+
export function getSvgLabelConstraintedPosition(args) {
|
|
46
|
+
const { boundsHeight, boundsWidth, height, width, x, y } = args;
|
|
47
|
+
let resultX = x;
|
|
48
|
+
let resultY = y;
|
|
49
|
+
if (x < 0) {
|
|
50
|
+
resultX = 0;
|
|
51
|
+
}
|
|
52
|
+
if (x + width > boundsWidth) {
|
|
53
|
+
resultX = boundsWidth - width;
|
|
54
|
+
}
|
|
55
|
+
if (y - height < 0) {
|
|
56
|
+
resultY = 0;
|
|
57
|
+
}
|
|
58
|
+
if (y > boundsHeight) {
|
|
59
|
+
resultY = boundsHeight;
|
|
60
|
+
}
|
|
61
|
+
return { x: resultX, y: resultY };
|
|
62
|
+
}
|
|
63
|
+
export function getHtmlLabelConstraintedPosition(args) {
|
|
64
|
+
const { boundsHeight, boundsWidth, height, width, x, y } = args;
|
|
65
|
+
let resultX = x;
|
|
66
|
+
let resultY = y;
|
|
67
|
+
if (x < 0) {
|
|
68
|
+
resultX = 0;
|
|
69
|
+
}
|
|
70
|
+
if (x + width > boundsWidth) {
|
|
71
|
+
resultX = boundsWidth - width;
|
|
72
|
+
}
|
|
73
|
+
if (y < 0) {
|
|
74
|
+
resultY = 0;
|
|
75
|
+
}
|
|
76
|
+
if (y + height > boundsHeight) {
|
|
77
|
+
resultY = boundsHeight - height;
|
|
78
|
+
}
|
|
79
|
+
return { x: resultX, y: resultY };
|
|
80
|
+
}
|
|
@@ -368,7 +368,10 @@ function validateTooltip({ tooltip }) {
|
|
|
368
368
|
}
|
|
369
369
|
}
|
|
370
370
|
export function validateData(data) {
|
|
371
|
-
if (isEmpty(data) ||
|
|
371
|
+
if (isEmpty(data) ||
|
|
372
|
+
isEmpty(data.series) ||
|
|
373
|
+
isEmpty(data.series.data) ||
|
|
374
|
+
data.series.data.every((s) => isEmpty(s.data))) {
|
|
372
375
|
throw new ChartError({
|
|
373
376
|
code: CHART_ERROR_CODE.NO_DATA,
|
|
374
377
|
message: i18n('error', 'label_no-data'),
|