@coinbase/cds-mobile-visualization 3.4.0-beta.21 → 3.4.0-beta.23
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/CHANGELOG.md +14 -0
- package/dts/chart/CartesianChart.d.ts +39 -7
- package/dts/chart/CartesianChart.d.ts.map +1 -1
- package/dts/chart/Path.d.ts.map +1 -1
- package/dts/chart/PeriodSelector.d.ts +18 -6
- package/dts/chart/PeriodSelector.d.ts.map +1 -1
- package/dts/chart/area/Area.d.ts +7 -0
- package/dts/chart/area/Area.d.ts.map +1 -1
- package/dts/chart/area/AreaChart.d.ts +33 -9
- package/dts/chart/area/AreaChart.d.ts.map +1 -1
- package/dts/chart/area/DottedArea.d.ts.map +1 -1
- package/dts/chart/area/GradientArea.d.ts.map +1 -1
- package/dts/chart/area/SolidArea.d.ts.map +1 -1
- package/dts/chart/axis/Axis.d.ts +3 -1
- package/dts/chart/axis/Axis.d.ts.map +1 -1
- package/dts/chart/axis/XAxis.d.ts +6 -0
- package/dts/chart/axis/XAxis.d.ts.map +1 -1
- package/dts/chart/axis/YAxis.d.ts +1 -0
- package/dts/chart/axis/YAxis.d.ts.map +1 -1
- package/dts/chart/bar/Bar.d.ts +4 -2
- package/dts/chart/bar/Bar.d.ts.map +1 -1
- package/dts/chart/bar/BarChart.d.ts +49 -9
- package/dts/chart/bar/BarChart.d.ts.map +1 -1
- package/dts/chart/bar/BarPlot.d.ts.map +1 -1
- package/dts/chart/bar/BarStack.d.ts +30 -9
- package/dts/chart/bar/BarStack.d.ts.map +1 -1
- package/dts/chart/bar/BarStackGroup.d.ts +1 -1
- package/dts/chart/bar/BarStackGroup.d.ts.map +1 -1
- package/dts/chart/bar/DefaultBar.d.ts.map +1 -1
- package/dts/chart/bar/DefaultBarStack.d.ts.map +1 -1
- package/dts/chart/gradient/Gradient.d.ts +5 -0
- package/dts/chart/gradient/Gradient.d.ts.map +1 -1
- package/dts/chart/line/DottedLine.d.ts.map +1 -1
- package/dts/chart/line/Line.d.ts +7 -0
- package/dts/chart/line/Line.d.ts.map +1 -1
- package/dts/chart/line/LineChart.d.ts +8 -5
- package/dts/chart/line/LineChart.d.ts.map +1 -1
- package/dts/chart/line/ReferenceLine.d.ts +1 -0
- package/dts/chart/line/ReferenceLine.d.ts.map +1 -1
- package/dts/chart/line/SolidLine.d.ts.map +1 -1
- package/dts/chart/point/Point.d.ts +7 -0
- package/dts/chart/point/Point.d.ts.map +1 -1
- package/dts/chart/scrubber/DefaultScrubberBeacon.d.ts.map +1 -1
- package/dts/chart/scrubber/DefaultScrubberLabel.d.ts +2 -1
- package/dts/chart/scrubber/DefaultScrubberLabel.d.ts.map +1 -1
- package/dts/chart/scrubber/Scrubber.d.ts +8 -0
- package/dts/chart/scrubber/Scrubber.d.ts.map +1 -1
- package/dts/chart/scrubber/ScrubberBeaconGroup.d.ts.map +1 -1
- package/dts/chart/scrubber/ScrubberProvider.d.ts.map +1 -1
- package/dts/chart/utils/axis.d.ts +20 -9
- package/dts/chart/utils/axis.d.ts.map +1 -1
- package/dts/chart/utils/bar.d.ts +6 -5
- package/dts/chart/utils/bar.d.ts.map +1 -1
- package/dts/chart/utils/chart.d.ts +13 -0
- package/dts/chart/utils/chart.d.ts.map +1 -1
- package/dts/chart/utils/context.d.ts +21 -6
- package/dts/chart/utils/context.d.ts.map +1 -1
- package/dts/chart/utils/gradient.d.ts +3 -1
- package/dts/chart/utils/gradient.d.ts.map +1 -1
- package/dts/chart/utils/path.d.ts +20 -0
- package/dts/chart/utils/path.d.ts.map +1 -1
- package/dts/chart/utils/point.d.ts +7 -0
- package/dts/chart/utils/point.d.ts.map +1 -1
- package/dts/chart/utils/transition.d.ts +7 -4
- package/dts/chart/utils/transition.d.ts.map +1 -1
- package/esm/chart/CartesianChart.js +107 -68
- package/esm/chart/Path.js +18 -14
- package/esm/chart/__stories__/ChartTransitions.stories.js +6 -10
- package/esm/chart/area/Area.js +19 -9
- package/esm/chart/area/AreaChart.js +18 -13
- package/esm/chart/area/DottedArea.js +23 -17
- package/esm/chart/area/GradientArea.js +11 -6
- package/esm/chart/area/SolidArea.js +3 -1
- package/esm/chart/area/__stories__/AreaChart.stories.js +30 -2
- package/esm/chart/axis/XAxis.js +14 -21
- package/esm/chart/axis/YAxis.js +4 -3
- package/esm/chart/bar/Bar.js +9 -5
- package/esm/chart/bar/BarChart.js +34 -31
- package/esm/chart/bar/BarPlot.js +7 -5
- package/esm/chart/bar/BarStack.js +176 -36
- package/esm/chart/bar/BarStackGroup.js +37 -27
- package/esm/chart/bar/DefaultBar.js +24 -8
- package/esm/chart/bar/DefaultBarStack.js +24 -10
- package/esm/chart/bar/__stories__/BarChart.stories.js +99 -3
- package/esm/chart/gradient/Gradient.js +2 -1
- package/esm/chart/line/DottedLine.js +3 -1
- package/esm/chart/line/Line.js +36 -21
- package/esm/chart/line/LineChart.js +13 -11
- package/esm/chart/line/SolidLine.js +3 -1
- package/esm/chart/line/__stories__/LineChart.stories.js +31 -0
- package/esm/chart/point/Point.js +2 -1
- package/esm/chart/scrubber/DefaultScrubberBeacon.js +1 -1
- package/esm/chart/scrubber/DefaultScrubberLabel.js +26 -10
- package/esm/chart/scrubber/Scrubber.js +47 -21
- package/esm/chart/scrubber/ScrubberBeaconGroup.js +24 -20
- package/esm/chart/scrubber/ScrubberProvider.js +29 -24
- package/esm/chart/scrubber/__stories__/Scrubber.stories.js +135 -1
- package/esm/chart/utils/axis.js +42 -14
- package/esm/chart/utils/bar.js +6 -4
- package/esm/chart/utils/chart.js +18 -5
- package/esm/chart/utils/context.js +7 -0
- package/esm/chart/utils/gradient.js +8 -4
- package/esm/chart/utils/path.js +90 -61
- package/esm/chart/utils/point.js +28 -18
- package/esm/chart/utils/transition.js +28 -10
- package/package.json +5 -5
package/esm/chart/bar/BarPlot.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { memo,
|
|
1
|
+
import { memo, useMemo } from 'react';
|
|
2
2
|
import { Group, Skia } from '@shopify/react-native-skia';
|
|
3
3
|
import { useCartesianChartContext } from '../ChartProvider';
|
|
4
4
|
import { defaultAxisId } from '../utils';
|
|
@@ -31,7 +31,6 @@ export const BarPlot = /*#__PURE__*/memo(_ref => {
|
|
|
31
31
|
series: allSeries,
|
|
32
32
|
drawingArea
|
|
33
33
|
} = useCartesianChartContext();
|
|
34
|
-
const clipPathId = useId();
|
|
35
34
|
const targetSeries = useMemo(() => {
|
|
36
35
|
// Then filter by seriesIds if provided
|
|
37
36
|
if (seriesIds !== undefined) {
|
|
@@ -42,16 +41,18 @@ export const BarPlot = /*#__PURE__*/memo(_ref => {
|
|
|
42
41
|
const stackGroups = useMemo(() => {
|
|
43
42
|
const groups = new Map();
|
|
44
43
|
|
|
45
|
-
// Group series into stacks based on stackId +
|
|
44
|
+
// Group series into stacks based on stackId + axis ID combination
|
|
46
45
|
targetSeries.forEach(series => {
|
|
47
|
-
var _series$yAxisId;
|
|
46
|
+
var _series$xAxisId, _series$yAxisId;
|
|
47
|
+
const xAxisId = (_series$xAxisId = series.xAxisId) != null ? _series$xAxisId : defaultAxisId;
|
|
48
48
|
const yAxisId = (_series$yAxisId = series.yAxisId) != null ? _series$yAxisId : defaultAxisId;
|
|
49
49
|
const stackId = series.stackId || "individual-" + series.id;
|
|
50
|
-
const stackKey = stackId + ":" + yAxisId;
|
|
50
|
+
const stackKey = stackId + ":" + xAxisId + ":" + yAxisId;
|
|
51
51
|
if (!groups.has(stackKey)) {
|
|
52
52
|
groups.set(stackKey, {
|
|
53
53
|
stackId: stackKey,
|
|
54
54
|
series: [],
|
|
55
|
+
xAxisId: series.xAxisId,
|
|
55
56
|
yAxisId: series.yAxisId
|
|
56
57
|
});
|
|
57
58
|
}
|
|
@@ -98,6 +99,7 @@ export const BarPlot = /*#__PURE__*/memo(_ref => {
|
|
|
98
99
|
totalStacks: stackGroups.length,
|
|
99
100
|
transition: transition,
|
|
100
101
|
transitions: transitions,
|
|
102
|
+
xAxisId: group.xAxisId,
|
|
101
103
|
yAxisId: group.yAxisId
|
|
102
104
|
}, group.stackId))
|
|
103
105
|
});
|
|
@@ -21,10 +21,13 @@ export const BarStack = /*#__PURE__*/memo(_ref => {
|
|
|
21
21
|
let {
|
|
22
22
|
series,
|
|
23
23
|
categoryIndex,
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
indexPos,
|
|
25
|
+
thickness,
|
|
26
|
+
indexScale,
|
|
27
|
+
valueScale,
|
|
27
28
|
rect,
|
|
29
|
+
xAxisId,
|
|
30
|
+
yAxisId,
|
|
28
31
|
BarComponent: defaultBarComponent,
|
|
29
32
|
fillOpacity: defaultFillOpacity,
|
|
30
33
|
stroke: defaultStroke,
|
|
@@ -40,24 +43,30 @@ export const BarStack = /*#__PURE__*/memo(_ref => {
|
|
|
40
43
|
} = _ref;
|
|
41
44
|
const theme = useTheme();
|
|
42
45
|
const {
|
|
46
|
+
layout,
|
|
43
47
|
getSeriesData,
|
|
44
48
|
getXAxis,
|
|
45
|
-
|
|
49
|
+
getYAxis
|
|
46
50
|
} = useCartesianChartContext();
|
|
47
|
-
const xAxis = getXAxis();
|
|
48
|
-
const
|
|
51
|
+
const xAxis = getXAxis(xAxisId);
|
|
52
|
+
const yAxis = getYAxis(yAxisId);
|
|
53
|
+
const barsGrowVertically = layout !== 'horizontal';
|
|
49
54
|
const baseline = useMemo(() => {
|
|
50
|
-
var
|
|
51
|
-
const domain =
|
|
55
|
+
var _valueScale;
|
|
56
|
+
const domain = valueScale.domain();
|
|
52
57
|
const [domainMin, domainMax] = domain;
|
|
53
58
|
const baselineValue = domainMin >= 0 ? domainMin : domainMax <= 0 ? domainMax : 0;
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
59
|
+
const fallback = barsGrowVertically ? rect.y + rect.height : rect.x;
|
|
60
|
+
const baselinePos = (_valueScale = valueScale(baselineValue)) != null ? _valueScale : fallback;
|
|
61
|
+
if (barsGrowVertically) {
|
|
62
|
+
return Math.max(rect.y, Math.min(baselinePos, rect.y + rect.height));
|
|
63
|
+
}
|
|
64
|
+
return Math.max(rect.x, Math.min(baselinePos, rect.x + rect.width));
|
|
65
|
+
}, [rect, valueScale, barsGrowVertically]);
|
|
57
66
|
const seriesGradients = useMemo(() => {
|
|
58
67
|
return series.map(s => {
|
|
59
|
-
if (!s.gradient
|
|
60
|
-
const gradientScale = s.gradient.axis === 'x' ?
|
|
68
|
+
if (!s.gradient) return;
|
|
69
|
+
const gradientScale = s.gradient.axis === 'x' ? barsGrowVertically ? indexScale : valueScale : barsGrowVertically ? valueScale : indexScale;
|
|
61
70
|
const serializableScale = convertToSerializableScale(gradientScale);
|
|
62
71
|
if (!serializableScale) return;
|
|
63
72
|
const domain = {
|
|
@@ -72,14 +81,140 @@ export const BarStack = /*#__PURE__*/memo(_ref => {
|
|
|
72
81
|
stops
|
|
73
82
|
};
|
|
74
83
|
});
|
|
75
|
-
}, [series,
|
|
84
|
+
}, [series, indexScale, valueScale, barsGrowVertically]);
|
|
76
85
|
|
|
77
86
|
// Calculate bars for this specific category
|
|
78
87
|
const {
|
|
79
88
|
bars,
|
|
80
89
|
stackRect
|
|
81
90
|
} = useMemo(() => {
|
|
91
|
+
const x = indexPos;
|
|
92
|
+
const width = thickness;
|
|
93
|
+
const yScale = valueScale;
|
|
82
94
|
let allBars = [];
|
|
95
|
+
if (!barsGrowVertically) {
|
|
96
|
+
let minX = Infinity;
|
|
97
|
+
let maxX = -Infinity;
|
|
98
|
+
series.forEach(s => {
|
|
99
|
+
var _yScale, _yScale2;
|
|
100
|
+
const data = getSeriesData(s.id);
|
|
101
|
+
if (!data) return;
|
|
102
|
+
const value = data[categoryIndex];
|
|
103
|
+
if (value === null || value === undefined) return;
|
|
104
|
+
const originalData = s.data;
|
|
105
|
+
const originalValue = originalData == null ? void 0 : originalData[categoryIndex];
|
|
106
|
+
const shouldApplyGap = !Array.isArray(originalValue);
|
|
107
|
+
const [bottom, top] = value.sort((a, b) => a - b);
|
|
108
|
+
const edgeBottom = (_yScale = yScale(bottom)) != null ? _yScale : baseline;
|
|
109
|
+
const edgeTop = (_yScale2 = yScale(top)) != null ? _yScale2 : baseline;
|
|
110
|
+
const length = Math.abs(edgeBottom - edgeTop);
|
|
111
|
+
const barX = Math.min(edgeBottom, edgeTop);
|
|
112
|
+
if (length <= 0) return;
|
|
113
|
+
minX = Math.min(minX, barX);
|
|
114
|
+
maxX = Math.max(maxX, barX + length);
|
|
115
|
+
let barFill = s.color || theme.color.fgPrimary;
|
|
116
|
+
const seriesGradientConfig = seriesGradients.find(g => (g == null ? void 0 : g.seriesId) === s.id);
|
|
117
|
+
if (seriesGradientConfig && originalValue !== null && originalValue !== undefined) {
|
|
118
|
+
var _seriesGradientConfig;
|
|
119
|
+
const axis = (_seriesGradientConfig = seriesGradientConfig.gradient.axis) != null ? _seriesGradientConfig : 'y';
|
|
120
|
+
const evalValue = axis === 'x' ? Array.isArray(originalValue) ? originalValue[1] : originalValue : categoryIndex;
|
|
121
|
+
const evaluatedColor = evaluateGradientAtValue(seriesGradientConfig.stops, evalValue, seriesGradientConfig.scale);
|
|
122
|
+
if (evaluatedColor) {
|
|
123
|
+
barFill = evaluatedColor;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
const roundTop = roundBaseline || Math.abs(edgeTop - baseline) >= EPSILON;
|
|
127
|
+
const roundBottom = roundBaseline || Math.abs(edgeBottom - baseline) >= EPSILON;
|
|
128
|
+
allBars.push({
|
|
129
|
+
seriesId: s.id,
|
|
130
|
+
x: barX,
|
|
131
|
+
y: x,
|
|
132
|
+
width: length,
|
|
133
|
+
height: width,
|
|
134
|
+
dataY: value,
|
|
135
|
+
fill: barFill,
|
|
136
|
+
roundTop,
|
|
137
|
+
roundBottom,
|
|
138
|
+
BarComponent: s.BarComponent,
|
|
139
|
+
shouldApplyGap
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
if (stackGap && allBars.length > 1) {
|
|
143
|
+
const barsAboveBaseline = allBars.filter(bar => {
|
|
144
|
+
const [bottom, top] = bar.dataY.sort((a, b) => a - b);
|
|
145
|
+
return bottom >= 0 && top !== bottom && bar.shouldApplyGap;
|
|
146
|
+
});
|
|
147
|
+
const barsBelowBaseline = allBars.filter(bar => {
|
|
148
|
+
const [bottom, top] = bar.dataY.sort((a, b) => a - b);
|
|
149
|
+
return bottom <= 0 && bottom !== top && bar.shouldApplyGap;
|
|
150
|
+
});
|
|
151
|
+
if (barsAboveBaseline.length > 1) {
|
|
152
|
+
const totalGapSpace = stackGap * (barsAboveBaseline.length - 1);
|
|
153
|
+
const totalDataLength = barsAboveBaseline.reduce((sum, bar) => sum + bar.width, 0);
|
|
154
|
+
const lengthReduction = totalGapSpace / totalDataLength;
|
|
155
|
+
const sortedBars = barsAboveBaseline.sort((a, b) => a.x - b.x);
|
|
156
|
+
let currentEdge = baseline;
|
|
157
|
+
sortedBars.forEach((bar, index) => {
|
|
158
|
+
const newLength = bar.width * (1 - lengthReduction);
|
|
159
|
+
const newX = currentEdge;
|
|
160
|
+
currentEdge = newX + newLength + (index < sortedBars.length - 1 ? stackGap : 0);
|
|
161
|
+
const barIndex = allBars.findIndex(b => b.seriesId === bar.seriesId);
|
|
162
|
+
if (barIndex !== -1) {
|
|
163
|
+
allBars[barIndex] = _extends({}, allBars[barIndex], {
|
|
164
|
+
width: newLength,
|
|
165
|
+
x: newX
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
if (barsBelowBaseline.length > 1) {
|
|
171
|
+
const totalGapSpace = stackGap * (barsBelowBaseline.length - 1);
|
|
172
|
+
const totalDataLength = barsBelowBaseline.reduce((sum, bar) => sum + bar.width, 0);
|
|
173
|
+
const lengthReduction = totalGapSpace / totalDataLength;
|
|
174
|
+
const sortedBars = barsBelowBaseline.sort((a, b) => b.x - a.x);
|
|
175
|
+
let currentEdge = baseline;
|
|
176
|
+
sortedBars.forEach((bar, index) => {
|
|
177
|
+
const newLength = bar.width * (1 - lengthReduction);
|
|
178
|
+
const newX = currentEdge - newLength;
|
|
179
|
+
currentEdge = newX - (index < sortedBars.length - 1 ? stackGap : 0);
|
|
180
|
+
const barIndex = allBars.findIndex(b => b.seriesId === bar.seriesId);
|
|
181
|
+
if (barIndex !== -1) {
|
|
182
|
+
allBars[barIndex] = _extends({}, allBars[barIndex], {
|
|
183
|
+
width: newLength,
|
|
184
|
+
x: newX
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
if (allBars.length > 0) {
|
|
190
|
+
minX = Math.min(...allBars.map(bar => bar.x));
|
|
191
|
+
maxX = Math.max(...allBars.map(bar => bar.x + bar.width));
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Horizontal border radius logic: left-to-right sorting.
|
|
196
|
+
const sortedBars = [...allBars].sort((a, b) => a.x - b.x);
|
|
197
|
+
const roundedBars = sortedBars.map((bar, index) => {
|
|
198
|
+
const barBefore = index > 0 ? sortedBars[index - 1] : null;
|
|
199
|
+
const barAfter = index < sortedBars.length - 1 ? sortedBars[index + 1] : null;
|
|
200
|
+
const shouldRoundLower = index === 0 || bar.shouldApplyGap && stackGap || !bar.shouldApplyGap && barAfter && barAfter.x + barAfter.width !== bar.x;
|
|
201
|
+
const shouldRoundHigher = index === sortedBars.length - 1 || bar.shouldApplyGap && stackGap || !bar.shouldApplyGap && barBefore && barBefore.x !== bar.x + bar.width;
|
|
202
|
+
return _extends({}, bar, {
|
|
203
|
+
roundTop: Boolean(bar.roundTop && shouldRoundHigher),
|
|
204
|
+
roundBottom: Boolean(bar.roundBottom && shouldRoundLower)
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
const stackBounds = {
|
|
208
|
+
x: minX === Infinity ? baseline : minX,
|
|
209
|
+
y: x,
|
|
210
|
+
width: maxX === -Infinity ? 0 : maxX - minX,
|
|
211
|
+
height: width
|
|
212
|
+
};
|
|
213
|
+
return {
|
|
214
|
+
bars: roundedBars,
|
|
215
|
+
stackRect: stackBounds
|
|
216
|
+
};
|
|
217
|
+
}
|
|
83
218
|
|
|
84
219
|
// Track how many bars we've stacked in each direction for gap calculation
|
|
85
220
|
let positiveBarCount = 0;
|
|
@@ -91,7 +226,7 @@ export const BarStack = /*#__PURE__*/memo(_ref => {
|
|
|
91
226
|
|
|
92
227
|
// Process each series in the stack
|
|
93
228
|
series.forEach(s => {
|
|
94
|
-
var
|
|
229
|
+
var _yScale3, _yScale4;
|
|
95
230
|
const data = getSeriesData(s.id);
|
|
96
231
|
if (!data) return;
|
|
97
232
|
const value = data[categoryIndex];
|
|
@@ -105,8 +240,8 @@ export const BarStack = /*#__PURE__*/memo(_ref => {
|
|
|
105
240
|
const [bottom, top] = value.sort((a, b) => a - b);
|
|
106
241
|
const isAboveBaseline = bottom >= 0 && top !== bottom;
|
|
107
242
|
const isBelowBaseline = bottom <= 0 && bottom !== top;
|
|
108
|
-
const barBottom = (
|
|
109
|
-
const barTop = (
|
|
243
|
+
const barBottom = (_yScale3 = yScale(bottom)) != null ? _yScale3 : baseline;
|
|
244
|
+
const barTop = (_yScale4 = yScale(top)) != null ? _yScale4 : baseline;
|
|
110
245
|
|
|
111
246
|
// Track bar counts for later gap calculations
|
|
112
247
|
if (shouldApplyGap) {
|
|
@@ -136,8 +271,8 @@ export const BarStack = /*#__PURE__*/memo(_ref => {
|
|
|
136
271
|
// Evaluate gradient if provided (using precomputed stops)
|
|
137
272
|
const seriesGradientConfig = seriesGradients.find(g => (g == null ? void 0 : g.seriesId) === s.id);
|
|
138
273
|
if (seriesGradientConfig) {
|
|
139
|
-
var
|
|
140
|
-
const axis = (
|
|
274
|
+
var _seriesGradientConfig2;
|
|
275
|
+
const axis = (_seriesGradientConfig2 = seriesGradientConfig.gradient.axis) != null ? _seriesGradientConfig2 : 'y';
|
|
141
276
|
// For x-axis gradient, use the categoryIndex
|
|
142
277
|
// For y-axis gradient, use the actual data value
|
|
143
278
|
const dataValue = axis === 'x' ? categoryIndex : top;
|
|
@@ -243,14 +378,14 @@ export const BarStack = /*#__PURE__*/memo(_ref => {
|
|
|
243
378
|
// First, expand bars that need it and track the expansion
|
|
244
379
|
const expandedBars = allBars.map((bar, index) => {
|
|
245
380
|
if (bar.height < barMinSize) {
|
|
246
|
-
var
|
|
381
|
+
var _yScale5, _yScale6, _yScale7, _yScale8;
|
|
247
382
|
const heightIncrease = barMinSize - bar.height;
|
|
248
383
|
const [bottom, top] = bar.dataY.sort((a, b) => a - b);
|
|
249
384
|
|
|
250
385
|
// Determine how to expand the bar
|
|
251
386
|
let newBottom = bottom;
|
|
252
387
|
let newTop = top;
|
|
253
|
-
const scaleUnit = Math.abs(((
|
|
388
|
+
const scaleUnit = Math.abs(((_yScale5 = yScale(1)) != null ? _yScale5 : 0) - ((_yScale6 = yScale(0)) != null ? _yScale6 : 0));
|
|
254
389
|
if (bottom === 0) {
|
|
255
390
|
// Expand away from baseline (upward for positive)
|
|
256
391
|
newTop = top + heightIncrease / scaleUnit;
|
|
@@ -265,8 +400,8 @@ export const BarStack = /*#__PURE__*/memo(_ref => {
|
|
|
265
400
|
}
|
|
266
401
|
|
|
267
402
|
// Recalculate bar position with new data values
|
|
268
|
-
const newBarBottom = (
|
|
269
|
-
const newBarTop = (
|
|
403
|
+
const newBarBottom = (_yScale7 = yScale(newBottom)) != null ? _yScale7 : baseline;
|
|
404
|
+
const newBarTop = (_yScale8 = yScale(newTop)) != null ? _yScale8 : baseline;
|
|
270
405
|
const newHeight = Math.abs(newBarBottom - newBarTop);
|
|
271
406
|
const newY = Math.min(newBarBottom, newBarTop);
|
|
272
407
|
return _extends({}, bar, {
|
|
@@ -383,7 +518,7 @@ export const BarStack = /*#__PURE__*/memo(_ref => {
|
|
|
383
518
|
// Apply stackMinSize constraints
|
|
384
519
|
if (stackMinSize) {
|
|
385
520
|
if (allBars.length === 1 && stackBounds.height < stackMinSize) {
|
|
386
|
-
var
|
|
521
|
+
var _yScale9, _yScale0, _yScale1, _yScale10;
|
|
387
522
|
// For single bars (non-stacked), treat stackMinSize like barMinSize
|
|
388
523
|
|
|
389
524
|
const bar = allBars[0];
|
|
@@ -393,7 +528,7 @@ export const BarStack = /*#__PURE__*/memo(_ref => {
|
|
|
393
528
|
// Determine how to expand the bar (same logic as barMinSize)
|
|
394
529
|
let newBottom = bottom;
|
|
395
530
|
let newTop = top;
|
|
396
|
-
const scaleUnit = Math.abs(((
|
|
531
|
+
const scaleUnit = Math.abs(((_yScale9 = yScale(1)) != null ? _yScale9 : 0) - ((_yScale0 = yScale(0)) != null ? _yScale0 : 0));
|
|
397
532
|
if (bottom === 0) {
|
|
398
533
|
// Expand away from baseline (upward for positive)
|
|
399
534
|
newTop = top + heightIncrease / scaleUnit;
|
|
@@ -408,8 +543,8 @@ export const BarStack = /*#__PURE__*/memo(_ref => {
|
|
|
408
543
|
}
|
|
409
544
|
|
|
410
545
|
// Recalculate bar position with new data values
|
|
411
|
-
const newBarBottom = (
|
|
412
|
-
const newBarTop = (
|
|
546
|
+
const newBarBottom = (_yScale1 = yScale(newBottom)) != null ? _yScale1 : baseline;
|
|
547
|
+
const newBarTop = (_yScale10 = yScale(newTop)) != null ? _yScale10 : baseline;
|
|
413
548
|
const newHeight = Math.abs(newBarBottom - newBarTop);
|
|
414
549
|
const newY = Math.min(newBarBottom, newBarTop);
|
|
415
550
|
allBars[0] = _extends({}, bar, {
|
|
@@ -517,18 +652,19 @@ export const BarStack = /*#__PURE__*/memo(_ref => {
|
|
|
517
652
|
bars: allBars,
|
|
518
653
|
stackRect: stackBounds
|
|
519
654
|
};
|
|
520
|
-
}, [series,
|
|
521
|
-
const
|
|
522
|
-
const
|
|
655
|
+
}, [series, indexPos, thickness, getSeriesData, categoryIndex, roundBaseline, baseline, stackGap, barMinSize, stackMinSize, valueScale, seriesGradients, theme.color.fgPrimary, barsGrowVertically]);
|
|
656
|
+
const categoryAxis = barsGrowVertically ? xAxis : yAxis;
|
|
657
|
+
const categoryData = categoryAxis != null && categoryAxis.data && Array.isArray(categoryAxis.data) && typeof categoryAxis.data[0] === 'number' ? categoryAxis.data : undefined;
|
|
658
|
+
const categoryValue = categoryData ? categoryData[categoryIndex] : categoryIndex;
|
|
523
659
|
const barElements = bars.map((bar, index) => /*#__PURE__*/_jsx(Bar, {
|
|
524
660
|
BarComponent: bar.BarComponent || defaultBarComponent,
|
|
525
661
|
borderRadius: borderRadius,
|
|
526
|
-
dataX:
|
|
527
|
-
dataY: bar.dataY,
|
|
662
|
+
dataX: barsGrowVertically ? categoryValue : bar.dataY,
|
|
663
|
+
dataY: barsGrowVertically ? bar.dataY : categoryValue,
|
|
528
664
|
fill: bar.fill,
|
|
529
665
|
fillOpacity: defaultFillOpacity,
|
|
530
666
|
height: bar.height,
|
|
531
|
-
|
|
667
|
+
origin: baseline,
|
|
532
668
|
roundBottom: bar.roundBottom,
|
|
533
669
|
roundTop: bar.roundTop,
|
|
534
670
|
seriesId: bar.seriesId,
|
|
@@ -541,9 +677,13 @@ export const BarStack = /*#__PURE__*/memo(_ref => {
|
|
|
541
677
|
y: bar.y
|
|
542
678
|
}, bar.seriesId + "-" + categoryIndex + "-" + index));
|
|
543
679
|
|
|
544
|
-
// Check if the
|
|
545
|
-
const
|
|
546
|
-
const
|
|
680
|
+
// Check if the stack should be rounded based on baseline, across both orientations.
|
|
681
|
+
const edge = barsGrowVertically ? stackRect.y : stackRect.x;
|
|
682
|
+
const size = barsGrowVertically ? stackRect.height : stackRect.width;
|
|
683
|
+
const stackRoundLower = roundBaseline || Math.abs(edge - baseline) >= EPSILON;
|
|
684
|
+
const stackRoundHigher = roundBaseline || Math.abs(edge + size - baseline) >= EPSILON;
|
|
685
|
+
const stackRoundTop = barsGrowVertically ? stackRoundLower : stackRoundHigher;
|
|
686
|
+
const stackRoundBottom = barsGrowVertically ? stackRoundHigher : stackRoundLower;
|
|
547
687
|
return /*#__PURE__*/_jsx(BarStackComponent, {
|
|
548
688
|
borderRadius: borderRadius,
|
|
549
689
|
categoryIndex: categoryIndex,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const _excluded = ["series", "yAxisId", "stackIndex", "totalStacks", "barPadding"];
|
|
1
|
+
const _excluded = ["series", "xAxisId", "yAxisId", "stackIndex", "totalStacks", "barPadding"];
|
|
2
2
|
function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
|
|
3
3
|
function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
|
|
4
4
|
import { memo, useMemo, createElement as _createElement } from 'react';
|
|
@@ -12,6 +12,7 @@ import { BarStack } from './BarStack';
|
|
|
12
12
|
export const BarStackGroup = /*#__PURE__*/memo(_ref => {
|
|
13
13
|
let {
|
|
14
14
|
series,
|
|
15
|
+
xAxisId = defaultAxisId,
|
|
15
16
|
yAxisId = defaultAxisId,
|
|
16
17
|
stackIndex,
|
|
17
18
|
totalStacks,
|
|
@@ -19,61 +20,70 @@ export const BarStackGroup = /*#__PURE__*/memo(_ref => {
|
|
|
19
20
|
} = _ref,
|
|
20
21
|
props = _objectWithoutPropertiesLoose(_ref, _excluded);
|
|
21
22
|
const {
|
|
23
|
+
layout,
|
|
22
24
|
getXScale,
|
|
23
25
|
getYScale,
|
|
24
26
|
drawingArea,
|
|
25
27
|
dataLength
|
|
26
28
|
} = useCartesianChartContext();
|
|
27
|
-
const xScale = getXScale();
|
|
29
|
+
const xScale = getXScale(xAxisId);
|
|
28
30
|
const yScale = getYScale(yAxisId);
|
|
29
31
|
const stackConfigs = useMemo(() => {
|
|
30
32
|
if (!xScale || !yScale || !drawingArea || dataLength === 0) return [];
|
|
31
|
-
|
|
33
|
+
const indexScale = layout !== 'horizontal' ? xScale : yScale;
|
|
34
|
+
if (!isCategoricalScale(indexScale)) {
|
|
32
35
|
return [];
|
|
33
36
|
}
|
|
34
|
-
const categoryWidth =
|
|
37
|
+
const categoryWidth = indexScale.bandwidth();
|
|
35
38
|
|
|
36
|
-
// Calculate
|
|
37
|
-
|
|
38
|
-
const
|
|
39
|
-
const barWidth = categoryWidth / totalStacks - getBarSizeAdjustment(totalStacks, gapWidth);
|
|
39
|
+
// Calculate thickness for each stack within a category.
|
|
40
|
+
const gapSize = totalStacks > 1 ? categoryWidth * barPadding / (totalStacks - 1) : 0;
|
|
41
|
+
const stackThickness = categoryWidth / totalStacks - getBarSizeAdjustment(totalStacks, gapSize);
|
|
40
42
|
const configs = [];
|
|
41
43
|
|
|
42
|
-
// Calculate position for each category
|
|
43
|
-
// todo: look at using xDomain for this instead of dataLength
|
|
44
|
+
// Calculate position for each category.
|
|
44
45
|
for (let categoryIndex = 0; categoryIndex < dataLength; categoryIndex++) {
|
|
45
|
-
// Get
|
|
46
|
-
const
|
|
47
|
-
if (
|
|
48
|
-
// Calculate
|
|
49
|
-
const
|
|
46
|
+
// Get position for this category along the index axis.
|
|
47
|
+
const categoryPos = indexScale(categoryIndex);
|
|
48
|
+
if (categoryPos !== undefined) {
|
|
49
|
+
// Calculate position for this specific stack within the category.
|
|
50
|
+
const stackPos = categoryPos + stackIndex * (stackThickness + gapSize);
|
|
50
51
|
configs.push({
|
|
51
52
|
categoryIndex,
|
|
52
|
-
|
|
53
|
-
|
|
53
|
+
indexPos: stackPos,
|
|
54
|
+
thickness: stackThickness
|
|
54
55
|
});
|
|
55
56
|
}
|
|
56
57
|
}
|
|
57
58
|
return configs;
|
|
58
|
-
}, [xScale, yScale, drawingArea, dataLength,
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
}, [xScale, yScale, drawingArea, dataLength, layout, totalStacks, barPadding, stackIndex]);
|
|
60
|
+
const indexScaleComputed = layout !== 'horizontal' ? xScale : yScale;
|
|
61
|
+
const valueScaleComputed = layout !== 'horizontal' ? yScale : xScale;
|
|
62
|
+
if (indexScaleComputed && !isCategoricalScale(indexScaleComputed)) {
|
|
63
|
+
throw new Error("BarStackGroup requires a band scale for " + (layout !== 'horizontal' ? 'x-axis' : 'y-axis') + ". See https://cds.coinbase.com/components/charts/" + (layout !== 'horizontal' ? 'XAxis' : 'YAxis') + "/#scale-type");
|
|
61
64
|
}
|
|
62
|
-
if (!
|
|
63
|
-
|
|
65
|
+
if (!indexScaleComputed || !valueScaleComputed || !drawingArea || stackConfigs.length === 0) return;
|
|
66
|
+
|
|
67
|
+
// In horizontal layout, render stacks in reverse order so top rows (lower categoryIndex)
|
|
68
|
+
// appear on top. Otherwise bottom rows would overlap and obscure top rows during animation.
|
|
69
|
+
const orderedConfigs = layout === 'horizontal' ? [...stackConfigs].reverse() : stackConfigs;
|
|
70
|
+
return orderedConfigs.map(_ref2 => {
|
|
64
71
|
let {
|
|
65
72
|
categoryIndex,
|
|
66
|
-
|
|
67
|
-
|
|
73
|
+
indexPos,
|
|
74
|
+
thickness
|
|
68
75
|
} = _ref2;
|
|
69
76
|
return /*#__PURE__*/_createElement(BarStack, _extends({}, props, {
|
|
70
77
|
key: "stack-" + stackIndex + "-category-" + categoryIndex,
|
|
71
78
|
categoryIndex: categoryIndex,
|
|
79
|
+
indexPos: indexPos,
|
|
80
|
+
indexScale: indexScaleComputed,
|
|
72
81
|
rect: drawingArea,
|
|
73
82
|
series: series,
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
83
|
+
thickness: thickness,
|
|
84
|
+
valueScale: valueScaleComputed,
|
|
85
|
+
xAxisId: xAxisId,
|
|
86
|
+
yAxisId: yAxisId
|
|
77
87
|
}));
|
|
78
88
|
});
|
|
79
89
|
});
|
|
@@ -22,23 +22,39 @@ export const DefaultBar = /*#__PURE__*/memo(_ref => {
|
|
|
22
22
|
fillOpacity = 1,
|
|
23
23
|
stroke,
|
|
24
24
|
strokeWidth,
|
|
25
|
-
|
|
25
|
+
origin,
|
|
26
26
|
transitions,
|
|
27
27
|
transition
|
|
28
28
|
} = _ref;
|
|
29
29
|
const {
|
|
30
30
|
animate,
|
|
31
|
-
drawingArea
|
|
31
|
+
drawingArea,
|
|
32
|
+
layout
|
|
32
33
|
} = useCartesianChartContext();
|
|
33
34
|
const theme = useTheme();
|
|
34
35
|
const defaultFill = fill || theme.color.fgPrimary;
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const
|
|
36
|
+
|
|
37
|
+
// For vertical layout, stagger by x (category axis). For horizontal, stagger by y (category axis).
|
|
38
|
+
const normalizedStagger = useMemo(() => {
|
|
39
|
+
const barsGrowVertically = layout !== 'horizontal';
|
|
40
|
+
if (barsGrowVertically) {
|
|
41
|
+
return drawingArea.width > 0 ? (x - drawingArea.x) / drawingArea.width : 0;
|
|
42
|
+
}
|
|
43
|
+
return drawingArea.height > 0 ? (y - drawingArea.y) / drawingArea.height : 0;
|
|
44
|
+
}, [layout, x, y, drawingArea.x, drawingArea.y, drawingArea.width, drawingArea.height]);
|
|
45
|
+
const enterTransition = useMemo(() => withStaggerDelayTransition(getTransition(transitions == null ? void 0 : transitions.enter, animate, defaultBarEnterTransition), normalizedStagger), [transitions == null ? void 0 : transitions.enter, animate, normalizedStagger]);
|
|
46
|
+
const updateTransition = useMemo(() => withStaggerDelayTransition(getTransition((transitions == null ? void 0 : transitions.update) !== undefined ? transitions.update : transition, animate, defaultTransition), normalizedStagger), [transitions == null ? void 0 : transitions.update, transition, animate, normalizedStagger]);
|
|
38
47
|
const initialPath = useMemo(() => {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
48
|
+
if (!animate) return undefined;
|
|
49
|
+
const minSize = 1;
|
|
50
|
+
const barsGrowVertically = layout !== 'horizontal';
|
|
51
|
+
const baseline = origin != null ? origin : barsGrowVertically ? y + height : x;
|
|
52
|
+
const initialX = barsGrowVertically ? x : baseline;
|
|
53
|
+
const initialY = barsGrowVertically ? baseline : y;
|
|
54
|
+
const initialWidth = barsGrowVertically ? width : minSize;
|
|
55
|
+
const initialHeight = barsGrowVertically ? minSize : height;
|
|
56
|
+
return getBarPath(initialX, initialY, initialWidth, initialHeight, borderRadius, !!roundTop, !!roundBottom, layout);
|
|
57
|
+
}, [animate, layout, x, y, origin, width, height, borderRadius, roundTop, roundBottom]);
|
|
42
58
|
return /*#__PURE__*/_jsx(Path, {
|
|
43
59
|
animate: animate,
|
|
44
60
|
clipPath: null,
|
|
@@ -24,24 +24,38 @@ export const DefaultBarStack = /*#__PURE__*/memo(_ref => {
|
|
|
24
24
|
} = _ref;
|
|
25
25
|
const {
|
|
26
26
|
animate,
|
|
27
|
-
drawingArea
|
|
27
|
+
drawingArea,
|
|
28
|
+
layout
|
|
28
29
|
} = useCartesianChartContext();
|
|
29
30
|
|
|
30
|
-
//
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
// For vertical layout, stagger by x (category axis). For horizontal, stagger by y (category axis).
|
|
32
|
+
const normalizedStagger = useMemo(() => {
|
|
33
|
+
const barsGrowVertically = layout !== 'horizontal';
|
|
34
|
+
if (barsGrowVertically) {
|
|
35
|
+
return drawingArea.width > 0 ? (x - drawingArea.x) / drawingArea.width : 0;
|
|
36
|
+
}
|
|
37
|
+
return drawingArea.height > 0 ? (y - drawingArea.y) / drawingArea.height : 0;
|
|
38
|
+
}, [layout, x, y, drawingArea.x, drawingArea.y, drawingArea.width, drawingArea.height]);
|
|
39
|
+
const enterTransition = useMemo(() => withStaggerDelayTransition(getTransition(transitions == null ? void 0 : transitions.enter, animate, defaultBarEnterTransition), normalizedStagger), [animate, transitions == null ? void 0 : transitions.enter, normalizedStagger]);
|
|
40
|
+
const updateTransition = useMemo(() => withStaggerDelayTransition(getTransition((transitions == null ? void 0 : transitions.update) !== undefined ? transitions.update : transition, animate, defaultTransition), normalizedStagger), [animate, transitions == null ? void 0 : transitions.update, transition, normalizedStagger]);
|
|
34
41
|
|
|
35
42
|
// Generate target clip path (full bar)
|
|
36
43
|
const targetPath = useMemo(() => {
|
|
37
|
-
return getBarPath(x, y, width, height, borderRadius, roundTop, roundBottom);
|
|
38
|
-
}, [x, y, width, height, borderRadius, roundTop, roundBottom]);
|
|
44
|
+
return getBarPath(x, y, width, height, borderRadius, roundTop, roundBottom, layout);
|
|
45
|
+
}, [x, y, width, height, borderRadius, roundTop, roundBottom, layout]);
|
|
39
46
|
|
|
40
47
|
// Initial clip path for entry animation (bar at baseline with minimal height)
|
|
41
48
|
const initialPath = useMemo(() => {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
49
|
+
if (!animate) return undefined;
|
|
50
|
+
const barsGrowVertically = layout !== 'horizontal';
|
|
51
|
+
const baseline = yOrigin != null ? yOrigin : barsGrowVertically ? y + height : x;
|
|
52
|
+
const minSize = 1;
|
|
53
|
+
const initialX = barsGrowVertically ? x : baseline;
|
|
54
|
+
const initialY = barsGrowVertically ? baseline : y;
|
|
55
|
+
const initialWidth = barsGrowVertically ? width : minSize;
|
|
56
|
+
const initialHeight = barsGrowVertically ? minSize : height;
|
|
57
|
+
return getBarPath(initialX, initialY, initialWidth, initialHeight, borderRadius, roundTop, roundBottom, layout);
|
|
58
|
+
}, [animate, layout, x, yOrigin, y, height, width, borderRadius, roundTop, roundBottom]);
|
|
45
59
|
const animatedClipPath = usePathTransition({
|
|
46
60
|
currentPath: targetPath,
|
|
47
61
|
initialPath,
|