@gravity-ui/charts 1.13.0 → 1.13.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/constants/defaults/series-options.d.ts +3 -1
- package/dist/cjs/constants/defaults/series-options.js +2 -0
- package/dist/cjs/hooks/useAxisScales/index.js +13 -27
- package/dist/cjs/hooks/useShapes/bar-x/prepare-data.js +9 -3
- package/dist/cjs/hooks/useShapes/bar-y/prepare-data.js +28 -20
- package/dist/cjs/hooks/utils/bar-y.d.ts +0 -1
- package/dist/cjs/hooks/utils/bar-y.js +5 -4
- package/dist/cjs/types/chart/series.d.ts +10 -0
- package/dist/esm/constants/defaults/series-options.d.ts +3 -1
- package/dist/esm/constants/defaults/series-options.js +2 -0
- package/dist/esm/hooks/useAxisScales/index.js +13 -27
- package/dist/esm/hooks/useShapes/bar-x/prepare-data.js +9 -3
- package/dist/esm/hooks/useShapes/bar-y/prepare-data.js +28 -20
- package/dist/esm/hooks/utils/bar-y.d.ts +0 -1
- package/dist/esm/hooks/utils/bar-y.js +5 -4
- package/dist/esm/types/chart/series.d.ts +10 -0
- package/package.json +1 -1
|
@@ -4,13 +4,15 @@ type DefaultBarXSeriesOptions = Partial<ChartSeriesOptions['bar-x']> & {
|
|
|
4
4
|
barMaxWidth: number;
|
|
5
5
|
barPadding: number;
|
|
6
6
|
groupPadding: number;
|
|
7
|
+
stackGap: number;
|
|
7
8
|
};
|
|
8
9
|
};
|
|
9
|
-
type DefaultBarYSeriesOptions = Partial<ChartSeriesOptions['bar-
|
|
10
|
+
type DefaultBarYSeriesOptions = Partial<ChartSeriesOptions['bar-y']> & {
|
|
10
11
|
'bar-y': {
|
|
11
12
|
barMaxWidth: number;
|
|
12
13
|
barPadding: number;
|
|
13
14
|
groupPadding: number;
|
|
15
|
+
stackGap: number;
|
|
14
16
|
};
|
|
15
17
|
};
|
|
16
18
|
type DefaultWaterfallSeriesOptions = Partial<ChartSeriesOptions['waterfall']> & {
|
|
@@ -3,6 +3,7 @@ export const seriesOptionsDefaults = {
|
|
|
3
3
|
barMaxWidth: 50,
|
|
4
4
|
barPadding: 0.1,
|
|
5
5
|
groupPadding: 0.2,
|
|
6
|
+
stackGap: 1,
|
|
6
7
|
states: {
|
|
7
8
|
hover: {
|
|
8
9
|
enabled: true,
|
|
@@ -18,6 +19,7 @@ export const seriesOptionsDefaults = {
|
|
|
18
19
|
barMaxWidth: 50,
|
|
19
20
|
barPadding: 0.1,
|
|
20
21
|
groupPadding: 0.2,
|
|
22
|
+
stackGap: 1,
|
|
21
23
|
states: {
|
|
22
24
|
hover: {
|
|
23
25
|
enabled: true,
|
|
@@ -29,39 +29,25 @@ function getYScaleRange(args) {
|
|
|
29
29
|
case 'linear':
|
|
30
30
|
case 'logarithmic': {
|
|
31
31
|
let range = [boundsHeight, boundsHeight * axis.maxPadding];
|
|
32
|
-
switch (axis.order) {
|
|
33
|
-
case 'sortDesc':
|
|
34
|
-
case 'reverse': {
|
|
35
|
-
range.reverse();
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
32
|
const barYSeries = series.filter((s) => s.type === SeriesType.BarY);
|
|
39
33
|
if (barYSeries.length) {
|
|
40
34
|
const groupedData = groupBarYDataByYValue(barYSeries, [axis]);
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const
|
|
48
|
-
const offsetMultiplier = barYSeries.reduce((acc, s) => {
|
|
49
|
-
let count = 0;
|
|
50
|
-
if (s.stackId) {
|
|
51
|
-
if (!alreadyCountedStackingIds.has(s.stackId)) {
|
|
52
|
-
alreadyCountedStackingIds.add(s.stackId);
|
|
53
|
-
count = 1;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
else {
|
|
57
|
-
count = 1;
|
|
58
|
-
}
|
|
59
|
-
return acc + count;
|
|
60
|
-
}, 0);
|
|
61
|
-
const offset = (barSize * Math.max(offsetMultiplier, 1)) / 2;
|
|
35
|
+
if (Object.keys(groupedData).length > 1) {
|
|
36
|
+
const { bandSize } = getBarYLayoutForNumericScale({
|
|
37
|
+
plotHeight: boundsHeight - boundsHeight * axis.maxPadding,
|
|
38
|
+
groupedData,
|
|
39
|
+
seriesOptions: seriesOptions,
|
|
40
|
+
});
|
|
41
|
+
const offset = bandSize / 2;
|
|
62
42
|
range = [range[0] - offset, range[1] + offset];
|
|
63
43
|
}
|
|
64
44
|
}
|
|
45
|
+
switch (axis.order) {
|
|
46
|
+
case 'sortDesc':
|
|
47
|
+
case 'reverse': {
|
|
48
|
+
range.reverse();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
65
51
|
return range;
|
|
66
52
|
}
|
|
67
53
|
case 'category': {
|
|
@@ -32,6 +32,7 @@ async function getLabelData(d) {
|
|
|
32
32
|
}
|
|
33
33
|
export const prepareBarXData = async (args) => {
|
|
34
34
|
const { series, seriesOptions, xAxis, xScale, yScale, boundsHeight: plotHeight } = args;
|
|
35
|
+
const stackGap = seriesOptions['bar-x'].stackGap;
|
|
35
36
|
const categories = get(xAxis, 'categories', []);
|
|
36
37
|
const barMaxWidth = get(seriesOptions, 'bar-x.barMaxWidth');
|
|
37
38
|
const barPadding = get(seriesOptions, 'bar-x.barPadding');
|
|
@@ -124,7 +125,12 @@ export const prepareBarXData = async (args) => {
|
|
|
124
125
|
const yDataValue = yValue.data.y;
|
|
125
126
|
const y = seriesYScale(yDataValue);
|
|
126
127
|
const base = seriesYScale(0);
|
|
128
|
+
const isLastStackItem = yValueIndex === sortedData.length - 1;
|
|
127
129
|
const height = yDataValue > 0 ? base - y : y - base;
|
|
130
|
+
let shapeHeight = height - (stackItems.length ? stackGap : 0);
|
|
131
|
+
if (shapeHeight < 0) {
|
|
132
|
+
shapeHeight = height;
|
|
133
|
+
}
|
|
128
134
|
if (height <= 0) {
|
|
129
135
|
continue;
|
|
130
136
|
}
|
|
@@ -132,12 +138,12 @@ export const prepareBarXData = async (args) => {
|
|
|
132
138
|
x,
|
|
133
139
|
y: yDataValue > 0 ? y - stackHeight : seriesYScale(0),
|
|
134
140
|
width: rectWidth,
|
|
135
|
-
height,
|
|
141
|
+
height: shapeHeight,
|
|
136
142
|
opacity: get(yValue.data, 'opacity', null),
|
|
137
143
|
data: yValue.data,
|
|
138
144
|
series: yValue.series,
|
|
139
145
|
htmlElements: [],
|
|
140
|
-
isLastStackItem
|
|
146
|
+
isLastStackItem,
|
|
141
147
|
};
|
|
142
148
|
const label = await getLabelData(barData);
|
|
143
149
|
if (yValue.series.dataLabels.html && label) {
|
|
@@ -153,7 +159,7 @@ export const prepareBarXData = async (args) => {
|
|
|
153
159
|
barData.label = await getLabelData(barData);
|
|
154
160
|
}
|
|
155
161
|
stackItems.push(barData);
|
|
156
|
-
stackHeight += height
|
|
162
|
+
stackHeight += height;
|
|
157
163
|
}
|
|
158
164
|
if (series.some((s) => s.stacking === 'percent')) {
|
|
159
165
|
let acc = 0;
|
|
@@ -7,10 +7,11 @@ const DEFAULT_LABEL_PADDING = 7;
|
|
|
7
7
|
export const prepareBarYData = async (args) => {
|
|
8
8
|
var _a;
|
|
9
9
|
const { series, seriesOptions, yAxis, xScale, yScale: [yScale], } = args;
|
|
10
|
+
const stackGap = seriesOptions['bar-y'].stackGap;
|
|
10
11
|
const xLinearScale = xScale;
|
|
11
12
|
const yLinearScale = yScale;
|
|
12
|
-
const
|
|
13
|
-
const
|
|
13
|
+
const yScaleRange = yLinearScale.range();
|
|
14
|
+
const plotHeight = Math.abs(yScaleRange[0] - yScaleRange[1]);
|
|
14
15
|
const sortingOptions = get(seriesOptions, 'bar-y.dataSorting');
|
|
15
16
|
const comparator = (sortingOptions === null || sortingOptions === void 0 ? void 0 : sortingOptions.direction) === 'desc' ? descending : ascending;
|
|
16
17
|
const sortKey = (() => {
|
|
@@ -40,12 +41,22 @@ export const prepareBarYData = async (args) => {
|
|
|
40
41
|
const stacks = Object.values(val);
|
|
41
42
|
const currentBarHeight = barSize * stacks.length + barGap * (stacks.length - 1);
|
|
42
43
|
stacks.forEach((measureValues, groupItemIndex) => {
|
|
43
|
-
const base = xLinearScale(0 - measureValues[0].series.borderWidth
|
|
44
|
+
const base = xLinearScale(0) - measureValues[0].series.borderWidth;
|
|
44
45
|
let stackSum = base;
|
|
45
46
|
const stackItems = [];
|
|
46
47
|
const sortedData = sortKey
|
|
47
48
|
? sort(measureValues, (a, b) => comparator(get(a, sortKey), get(b, sortKey)))
|
|
48
49
|
: measureValues;
|
|
50
|
+
let ratio = 1;
|
|
51
|
+
if (series.some((s) => s.stacking === 'percent')) {
|
|
52
|
+
const sum = sortedData.reduce((acc, item) => {
|
|
53
|
+
if (item.data.x) {
|
|
54
|
+
return acc + xLinearScale(Number(item.data.x));
|
|
55
|
+
}
|
|
56
|
+
return acc;
|
|
57
|
+
}, 0);
|
|
58
|
+
ratio = xLinearScale.range()[1] / sum;
|
|
59
|
+
}
|
|
49
60
|
sortedData.forEach(({ data, series: s }, xValueIndex) => {
|
|
50
61
|
let center;
|
|
51
62
|
if (yAxis[0].type === 'category') {
|
|
@@ -58,35 +69,32 @@ export const prepareBarYData = async (args) => {
|
|
|
58
69
|
}
|
|
59
70
|
const y = center - currentBarHeight / 2 + (barSize + barGap) * groupItemIndex;
|
|
60
71
|
const xValue = Number(data.x);
|
|
61
|
-
const
|
|
62
|
-
|
|
72
|
+
const isLastStackItem = xValueIndex === sortedData.length - 1;
|
|
73
|
+
const width = Math.abs(xLinearScale(xValue) * ratio - base);
|
|
74
|
+
let shapeWidth = width - (stackItems.length ? stackGap : 0);
|
|
75
|
+
if (shapeWidth < 0) {
|
|
76
|
+
shapeWidth = width;
|
|
77
|
+
}
|
|
78
|
+
if (shapeWidth <= 0) {
|
|
63
79
|
return;
|
|
64
80
|
}
|
|
81
|
+
const itemStackGap = width - shapeWidth;
|
|
65
82
|
const item = {
|
|
66
|
-
x: xValue > baseRangeValue ? stackSum : stackSum - width,
|
|
67
|
-
y,
|
|
68
|
-
width,
|
|
83
|
+
x: (xValue > baseRangeValue ? stackSum : stackSum - width) + itemStackGap,
|
|
84
|
+
y: y,
|
|
85
|
+
width: shapeWidth,
|
|
69
86
|
height: barSize,
|
|
70
87
|
color: data.color || s.color,
|
|
71
88
|
borderColor: s.borderColor,
|
|
72
|
-
borderWidth: s.borderWidth,
|
|
89
|
+
borderWidth: barSize > s.borderWidth * 2 ? s.borderWidth : 0,
|
|
73
90
|
opacity: get(data, 'opacity', null),
|
|
74
91
|
data,
|
|
75
92
|
series: s,
|
|
76
|
-
isLastStackItem
|
|
93
|
+
isLastStackItem,
|
|
77
94
|
};
|
|
78
95
|
stackItems.push(item);
|
|
79
|
-
stackSum += width
|
|
96
|
+
stackSum += width;
|
|
80
97
|
});
|
|
81
|
-
if (series.some((s) => s.stacking === 'percent')) {
|
|
82
|
-
let acc = 0;
|
|
83
|
-
const ratio = plotWidth / (stackSum - stackItems.length);
|
|
84
|
-
stackItems.forEach((item) => {
|
|
85
|
-
item.width = item.width * ratio;
|
|
86
|
-
item.x = acc;
|
|
87
|
-
acc += item.width;
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
98
|
result.push(...stackItems);
|
|
91
99
|
});
|
|
92
100
|
});
|
|
@@ -14,7 +14,6 @@ export declare function getBarYLayoutForNumericScale(args: {
|
|
|
14
14
|
bandSize: number;
|
|
15
15
|
barGap: number;
|
|
16
16
|
barSize: number;
|
|
17
|
-
dataLength: number;
|
|
18
17
|
};
|
|
19
18
|
export declare function getBarYLayoutForCategoryScale(args: {
|
|
20
19
|
groupedData: ReturnType<typeof groupBarYDataByYValue>;
|
|
@@ -32,13 +32,14 @@ export function getBarYLayoutForNumericScale(args) {
|
|
|
32
32
|
const barMaxWidth = get(seriesOptions, 'bar-y.barMaxWidth');
|
|
33
33
|
const barPadding = get(seriesOptions, 'bar-y.barPadding');
|
|
34
34
|
const groupPadding = get(seriesOptions, 'bar-y.groupPadding');
|
|
35
|
-
const
|
|
36
|
-
const
|
|
35
|
+
const groups = Object.values(groupedData);
|
|
36
|
+
const maxGroupItemCount = groups.reduce((acc, items) => Math.max(acc, Object.keys(items).length), 0);
|
|
37
|
+
const bandSize = plotHeight / groups.length;
|
|
37
38
|
const groupGap = Math.max(bandSize * groupPadding, MIN_BAR_GROUP_GAP);
|
|
38
39
|
const groupSize = bandSize - groupGap;
|
|
39
40
|
const barGap = Math.max(bandSize * barPadding, MIN_BAR_GAP);
|
|
40
|
-
const barSize = Math.max(MIN_BAR_WIDTH, Math.min(groupSize - barGap, barMaxWidth));
|
|
41
|
-
return { bandSize, barGap, barSize
|
|
41
|
+
const barSize = Math.max(MIN_BAR_WIDTH, Math.min((groupSize - barGap) / maxGroupItemCount, barMaxWidth));
|
|
42
|
+
return { bandSize, barGap, barSize };
|
|
42
43
|
}
|
|
43
44
|
export function getBarYLayoutForCategoryScale(args) {
|
|
44
45
|
const { groupedData, seriesOptions, yScale } = args;
|
|
@@ -79,6 +79,11 @@ export interface ChartSeriesOptions {
|
|
|
79
79
|
* @default 0
|
|
80
80
|
*/
|
|
81
81
|
borderRadius?: number;
|
|
82
|
+
/**
|
|
83
|
+
* The distance between the shapes of the stacked values, in pixels.
|
|
84
|
+
* @default 1
|
|
85
|
+
*/
|
|
86
|
+
stackGap?: number;
|
|
82
87
|
dataSorting?: {
|
|
83
88
|
/** Determines what data value should be used to sort by.
|
|
84
89
|
* Possible values are undefined to disable, "name" to sort by series name or "y"
|
|
@@ -130,6 +135,11 @@ export interface ChartSeriesOptions {
|
|
|
130
135
|
* @default 0
|
|
131
136
|
*/
|
|
132
137
|
borderRadius?: number;
|
|
138
|
+
/**
|
|
139
|
+
* The distance between the shapes of the stacked values, in pixels.
|
|
140
|
+
* @default 1
|
|
141
|
+
*/
|
|
142
|
+
stackGap?: number;
|
|
133
143
|
dataSorting?: {
|
|
134
144
|
/** Determines what data value should be used to sort by.
|
|
135
145
|
* Possible values are undefined to disable, "name" to sort by series name or "x"
|
|
@@ -4,13 +4,15 @@ type DefaultBarXSeriesOptions = Partial<ChartSeriesOptions['bar-x']> & {
|
|
|
4
4
|
barMaxWidth: number;
|
|
5
5
|
barPadding: number;
|
|
6
6
|
groupPadding: number;
|
|
7
|
+
stackGap: number;
|
|
7
8
|
};
|
|
8
9
|
};
|
|
9
|
-
type DefaultBarYSeriesOptions = Partial<ChartSeriesOptions['bar-
|
|
10
|
+
type DefaultBarYSeriesOptions = Partial<ChartSeriesOptions['bar-y']> & {
|
|
10
11
|
'bar-y': {
|
|
11
12
|
barMaxWidth: number;
|
|
12
13
|
barPadding: number;
|
|
13
14
|
groupPadding: number;
|
|
15
|
+
stackGap: number;
|
|
14
16
|
};
|
|
15
17
|
};
|
|
16
18
|
type DefaultWaterfallSeriesOptions = Partial<ChartSeriesOptions['waterfall']> & {
|
|
@@ -3,6 +3,7 @@ export const seriesOptionsDefaults = {
|
|
|
3
3
|
barMaxWidth: 50,
|
|
4
4
|
barPadding: 0.1,
|
|
5
5
|
groupPadding: 0.2,
|
|
6
|
+
stackGap: 1,
|
|
6
7
|
states: {
|
|
7
8
|
hover: {
|
|
8
9
|
enabled: true,
|
|
@@ -18,6 +19,7 @@ export const seriesOptionsDefaults = {
|
|
|
18
19
|
barMaxWidth: 50,
|
|
19
20
|
barPadding: 0.1,
|
|
20
21
|
groupPadding: 0.2,
|
|
22
|
+
stackGap: 1,
|
|
21
23
|
states: {
|
|
22
24
|
hover: {
|
|
23
25
|
enabled: true,
|
|
@@ -29,39 +29,25 @@ function getYScaleRange(args) {
|
|
|
29
29
|
case 'linear':
|
|
30
30
|
case 'logarithmic': {
|
|
31
31
|
let range = [boundsHeight, boundsHeight * axis.maxPadding];
|
|
32
|
-
switch (axis.order) {
|
|
33
|
-
case 'sortDesc':
|
|
34
|
-
case 'reverse': {
|
|
35
|
-
range.reverse();
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
32
|
const barYSeries = series.filter((s) => s.type === SeriesType.BarY);
|
|
39
33
|
if (barYSeries.length) {
|
|
40
34
|
const groupedData = groupBarYDataByYValue(barYSeries, [axis]);
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const
|
|
48
|
-
const offsetMultiplier = barYSeries.reduce((acc, s) => {
|
|
49
|
-
let count = 0;
|
|
50
|
-
if (s.stackId) {
|
|
51
|
-
if (!alreadyCountedStackingIds.has(s.stackId)) {
|
|
52
|
-
alreadyCountedStackingIds.add(s.stackId);
|
|
53
|
-
count = 1;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
else {
|
|
57
|
-
count = 1;
|
|
58
|
-
}
|
|
59
|
-
return acc + count;
|
|
60
|
-
}, 0);
|
|
61
|
-
const offset = (barSize * Math.max(offsetMultiplier, 1)) / 2;
|
|
35
|
+
if (Object.keys(groupedData).length > 1) {
|
|
36
|
+
const { bandSize } = getBarYLayoutForNumericScale({
|
|
37
|
+
plotHeight: boundsHeight - boundsHeight * axis.maxPadding,
|
|
38
|
+
groupedData,
|
|
39
|
+
seriesOptions: seriesOptions,
|
|
40
|
+
});
|
|
41
|
+
const offset = bandSize / 2;
|
|
62
42
|
range = [range[0] - offset, range[1] + offset];
|
|
63
43
|
}
|
|
64
44
|
}
|
|
45
|
+
switch (axis.order) {
|
|
46
|
+
case 'sortDesc':
|
|
47
|
+
case 'reverse': {
|
|
48
|
+
range.reverse();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
65
51
|
return range;
|
|
66
52
|
}
|
|
67
53
|
case 'category': {
|
|
@@ -32,6 +32,7 @@ async function getLabelData(d) {
|
|
|
32
32
|
}
|
|
33
33
|
export const prepareBarXData = async (args) => {
|
|
34
34
|
const { series, seriesOptions, xAxis, xScale, yScale, boundsHeight: plotHeight } = args;
|
|
35
|
+
const stackGap = seriesOptions['bar-x'].stackGap;
|
|
35
36
|
const categories = get(xAxis, 'categories', []);
|
|
36
37
|
const barMaxWidth = get(seriesOptions, 'bar-x.barMaxWidth');
|
|
37
38
|
const barPadding = get(seriesOptions, 'bar-x.barPadding');
|
|
@@ -124,7 +125,12 @@ export const prepareBarXData = async (args) => {
|
|
|
124
125
|
const yDataValue = yValue.data.y;
|
|
125
126
|
const y = seriesYScale(yDataValue);
|
|
126
127
|
const base = seriesYScale(0);
|
|
128
|
+
const isLastStackItem = yValueIndex === sortedData.length - 1;
|
|
127
129
|
const height = yDataValue > 0 ? base - y : y - base;
|
|
130
|
+
let shapeHeight = height - (stackItems.length ? stackGap : 0);
|
|
131
|
+
if (shapeHeight < 0) {
|
|
132
|
+
shapeHeight = height;
|
|
133
|
+
}
|
|
128
134
|
if (height <= 0) {
|
|
129
135
|
continue;
|
|
130
136
|
}
|
|
@@ -132,12 +138,12 @@ export const prepareBarXData = async (args) => {
|
|
|
132
138
|
x,
|
|
133
139
|
y: yDataValue > 0 ? y - stackHeight : seriesYScale(0),
|
|
134
140
|
width: rectWidth,
|
|
135
|
-
height,
|
|
141
|
+
height: shapeHeight,
|
|
136
142
|
opacity: get(yValue.data, 'opacity', null),
|
|
137
143
|
data: yValue.data,
|
|
138
144
|
series: yValue.series,
|
|
139
145
|
htmlElements: [],
|
|
140
|
-
isLastStackItem
|
|
146
|
+
isLastStackItem,
|
|
141
147
|
};
|
|
142
148
|
const label = await getLabelData(barData);
|
|
143
149
|
if (yValue.series.dataLabels.html && label) {
|
|
@@ -153,7 +159,7 @@ export const prepareBarXData = async (args) => {
|
|
|
153
159
|
barData.label = await getLabelData(barData);
|
|
154
160
|
}
|
|
155
161
|
stackItems.push(barData);
|
|
156
|
-
stackHeight += height
|
|
162
|
+
stackHeight += height;
|
|
157
163
|
}
|
|
158
164
|
if (series.some((s) => s.stacking === 'percent')) {
|
|
159
165
|
let acc = 0;
|
|
@@ -7,10 +7,11 @@ const DEFAULT_LABEL_PADDING = 7;
|
|
|
7
7
|
export const prepareBarYData = async (args) => {
|
|
8
8
|
var _a;
|
|
9
9
|
const { series, seriesOptions, yAxis, xScale, yScale: [yScale], } = args;
|
|
10
|
+
const stackGap = seriesOptions['bar-y'].stackGap;
|
|
10
11
|
const xLinearScale = xScale;
|
|
11
12
|
const yLinearScale = yScale;
|
|
12
|
-
const
|
|
13
|
-
const
|
|
13
|
+
const yScaleRange = yLinearScale.range();
|
|
14
|
+
const plotHeight = Math.abs(yScaleRange[0] - yScaleRange[1]);
|
|
14
15
|
const sortingOptions = get(seriesOptions, 'bar-y.dataSorting');
|
|
15
16
|
const comparator = (sortingOptions === null || sortingOptions === void 0 ? void 0 : sortingOptions.direction) === 'desc' ? descending : ascending;
|
|
16
17
|
const sortKey = (() => {
|
|
@@ -40,12 +41,22 @@ export const prepareBarYData = async (args) => {
|
|
|
40
41
|
const stacks = Object.values(val);
|
|
41
42
|
const currentBarHeight = barSize * stacks.length + barGap * (stacks.length - 1);
|
|
42
43
|
stacks.forEach((measureValues, groupItemIndex) => {
|
|
43
|
-
const base = xLinearScale(0 - measureValues[0].series.borderWidth
|
|
44
|
+
const base = xLinearScale(0) - measureValues[0].series.borderWidth;
|
|
44
45
|
let stackSum = base;
|
|
45
46
|
const stackItems = [];
|
|
46
47
|
const sortedData = sortKey
|
|
47
48
|
? sort(measureValues, (a, b) => comparator(get(a, sortKey), get(b, sortKey)))
|
|
48
49
|
: measureValues;
|
|
50
|
+
let ratio = 1;
|
|
51
|
+
if (series.some((s) => s.stacking === 'percent')) {
|
|
52
|
+
const sum = sortedData.reduce((acc, item) => {
|
|
53
|
+
if (item.data.x) {
|
|
54
|
+
return acc + xLinearScale(Number(item.data.x));
|
|
55
|
+
}
|
|
56
|
+
return acc;
|
|
57
|
+
}, 0);
|
|
58
|
+
ratio = xLinearScale.range()[1] / sum;
|
|
59
|
+
}
|
|
49
60
|
sortedData.forEach(({ data, series: s }, xValueIndex) => {
|
|
50
61
|
let center;
|
|
51
62
|
if (yAxis[0].type === 'category') {
|
|
@@ -58,35 +69,32 @@ export const prepareBarYData = async (args) => {
|
|
|
58
69
|
}
|
|
59
70
|
const y = center - currentBarHeight / 2 + (barSize + barGap) * groupItemIndex;
|
|
60
71
|
const xValue = Number(data.x);
|
|
61
|
-
const
|
|
62
|
-
|
|
72
|
+
const isLastStackItem = xValueIndex === sortedData.length - 1;
|
|
73
|
+
const width = Math.abs(xLinearScale(xValue) * ratio - base);
|
|
74
|
+
let shapeWidth = width - (stackItems.length ? stackGap : 0);
|
|
75
|
+
if (shapeWidth < 0) {
|
|
76
|
+
shapeWidth = width;
|
|
77
|
+
}
|
|
78
|
+
if (shapeWidth <= 0) {
|
|
63
79
|
return;
|
|
64
80
|
}
|
|
81
|
+
const itemStackGap = width - shapeWidth;
|
|
65
82
|
const item = {
|
|
66
|
-
x: xValue > baseRangeValue ? stackSum : stackSum - width,
|
|
67
|
-
y,
|
|
68
|
-
width,
|
|
83
|
+
x: (xValue > baseRangeValue ? stackSum : stackSum - width) + itemStackGap,
|
|
84
|
+
y: y,
|
|
85
|
+
width: shapeWidth,
|
|
69
86
|
height: barSize,
|
|
70
87
|
color: data.color || s.color,
|
|
71
88
|
borderColor: s.borderColor,
|
|
72
|
-
borderWidth: s.borderWidth,
|
|
89
|
+
borderWidth: barSize > s.borderWidth * 2 ? s.borderWidth : 0,
|
|
73
90
|
opacity: get(data, 'opacity', null),
|
|
74
91
|
data,
|
|
75
92
|
series: s,
|
|
76
|
-
isLastStackItem
|
|
93
|
+
isLastStackItem,
|
|
77
94
|
};
|
|
78
95
|
stackItems.push(item);
|
|
79
|
-
stackSum += width
|
|
96
|
+
stackSum += width;
|
|
80
97
|
});
|
|
81
|
-
if (series.some((s) => s.stacking === 'percent')) {
|
|
82
|
-
let acc = 0;
|
|
83
|
-
const ratio = plotWidth / (stackSum - stackItems.length);
|
|
84
|
-
stackItems.forEach((item) => {
|
|
85
|
-
item.width = item.width * ratio;
|
|
86
|
-
item.x = acc;
|
|
87
|
-
acc += item.width;
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
98
|
result.push(...stackItems);
|
|
91
99
|
});
|
|
92
100
|
});
|
|
@@ -14,7 +14,6 @@ export declare function getBarYLayoutForNumericScale(args: {
|
|
|
14
14
|
bandSize: number;
|
|
15
15
|
barGap: number;
|
|
16
16
|
barSize: number;
|
|
17
|
-
dataLength: number;
|
|
18
17
|
};
|
|
19
18
|
export declare function getBarYLayoutForCategoryScale(args: {
|
|
20
19
|
groupedData: ReturnType<typeof groupBarYDataByYValue>;
|
|
@@ -32,13 +32,14 @@ export function getBarYLayoutForNumericScale(args) {
|
|
|
32
32
|
const barMaxWidth = get(seriesOptions, 'bar-y.barMaxWidth');
|
|
33
33
|
const barPadding = get(seriesOptions, 'bar-y.barPadding');
|
|
34
34
|
const groupPadding = get(seriesOptions, 'bar-y.groupPadding');
|
|
35
|
-
const
|
|
36
|
-
const
|
|
35
|
+
const groups = Object.values(groupedData);
|
|
36
|
+
const maxGroupItemCount = groups.reduce((acc, items) => Math.max(acc, Object.keys(items).length), 0);
|
|
37
|
+
const bandSize = plotHeight / groups.length;
|
|
37
38
|
const groupGap = Math.max(bandSize * groupPadding, MIN_BAR_GROUP_GAP);
|
|
38
39
|
const groupSize = bandSize - groupGap;
|
|
39
40
|
const barGap = Math.max(bandSize * barPadding, MIN_BAR_GAP);
|
|
40
|
-
const barSize = Math.max(MIN_BAR_WIDTH, Math.min(groupSize - barGap, barMaxWidth));
|
|
41
|
-
return { bandSize, barGap, barSize
|
|
41
|
+
const barSize = Math.max(MIN_BAR_WIDTH, Math.min((groupSize - barGap) / maxGroupItemCount, barMaxWidth));
|
|
42
|
+
return { bandSize, barGap, barSize };
|
|
42
43
|
}
|
|
43
44
|
export function getBarYLayoutForCategoryScale(args) {
|
|
44
45
|
const { groupedData, seriesOptions, yScale } = args;
|
|
@@ -79,6 +79,11 @@ export interface ChartSeriesOptions {
|
|
|
79
79
|
* @default 0
|
|
80
80
|
*/
|
|
81
81
|
borderRadius?: number;
|
|
82
|
+
/**
|
|
83
|
+
* The distance between the shapes of the stacked values, in pixels.
|
|
84
|
+
* @default 1
|
|
85
|
+
*/
|
|
86
|
+
stackGap?: number;
|
|
82
87
|
dataSorting?: {
|
|
83
88
|
/** Determines what data value should be used to sort by.
|
|
84
89
|
* Possible values are undefined to disable, "name" to sort by series name or "y"
|
|
@@ -130,6 +135,11 @@ export interface ChartSeriesOptions {
|
|
|
130
135
|
* @default 0
|
|
131
136
|
*/
|
|
132
137
|
borderRadius?: number;
|
|
138
|
+
/**
|
|
139
|
+
* The distance between the shapes of the stacked values, in pixels.
|
|
140
|
+
* @default 1
|
|
141
|
+
*/
|
|
142
|
+
stackGap?: number;
|
|
133
143
|
dataSorting?: {
|
|
134
144
|
/** Determines what data value should be used to sort by.
|
|
135
145
|
* Possible values are undefined to disable, "name" to sort by series name or "x"
|