@coinbase/cds-mobile-visualization 3.3.0 → 3.4.0-beta.10
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 +80 -0
- package/dts/chart/CartesianChart.d.ts +125 -0
- package/dts/chart/CartesianChart.d.ts.map +1 -0
- package/dts/chart/ChartContextBridge.d.ts +28 -0
- package/dts/chart/ChartContextBridge.d.ts.map +1 -0
- package/dts/chart/ChartProvider.d.ts +6 -0
- package/dts/chart/ChartProvider.d.ts.map +1 -0
- package/dts/chart/Path.d.ts +91 -0
- package/dts/chart/Path.d.ts.map +1 -0
- package/dts/chart/PeriodSelector.d.ts +85 -0
- package/dts/chart/PeriodSelector.d.ts.map +1 -0
- package/dts/chart/area/Area.d.ts +77 -0
- package/dts/chart/area/Area.d.ts.map +1 -0
- package/dts/chart/area/AreaChart.d.ts +131 -0
- package/dts/chart/area/AreaChart.d.ts.map +1 -0
- package/dts/chart/area/DottedArea.d.ts +46 -0
- package/dts/chart/area/DottedArea.d.ts.map +1 -0
- package/dts/chart/area/GradientArea.d.ts +36 -0
- package/dts/chart/area/GradientArea.d.ts.map +1 -0
- package/dts/chart/area/SolidArea.d.ts +23 -0
- package/dts/chart/area/SolidArea.d.ts.map +1 -0
- package/dts/chart/area/index.d.ts +6 -0
- package/dts/chart/area/index.d.ts.map +1 -0
- package/dts/chart/axis/Axis.d.ts +194 -0
- package/dts/chart/axis/Axis.d.ts.map +1 -0
- package/dts/chart/axis/DefaultAxisTickLabel.d.ts +8 -0
- package/dts/chart/axis/DefaultAxisTickLabel.d.ts.map +1 -0
- package/dts/chart/axis/XAxis.d.ts +16 -0
- package/dts/chart/axis/XAxis.d.ts.map +1 -0
- package/dts/chart/axis/YAxis.d.ts +21 -0
- package/dts/chart/axis/YAxis.d.ts.map +1 -0
- package/dts/chart/axis/index.d.ts +5 -0
- package/dts/chart/axis/index.d.ts.map +1 -0
- package/dts/chart/bar/Bar.d.ts +92 -0
- package/dts/chart/bar/Bar.d.ts.map +1 -0
- package/dts/chart/bar/BarChart.d.ts +113 -0
- package/dts/chart/bar/BarChart.d.ts.map +1 -0
- package/dts/chart/bar/BarPlot.d.ts +30 -0
- package/dts/chart/bar/BarPlot.d.ts.map +1 -0
- package/dts/chart/bar/BarStack.d.ts +102 -0
- package/dts/chart/bar/BarStack.d.ts.map +1 -0
- package/dts/chart/bar/BarStackGroup.d.ts +36 -0
- package/dts/chart/bar/BarStackGroup.d.ts.map +1 -0
- package/dts/chart/bar/DefaultBar.d.ts +7 -0
- package/dts/chart/bar/DefaultBar.d.ts.map +1 -0
- package/dts/chart/bar/DefaultBarStack.d.ts +7 -0
- package/dts/chart/bar/DefaultBarStack.d.ts.map +1 -0
- package/dts/chart/bar/index.d.ts +8 -0
- package/dts/chart/bar/index.d.ts.map +1 -0
- package/dts/chart/gradient/Gradient.d.ts +25 -0
- package/dts/chart/gradient/Gradient.d.ts.map +1 -0
- package/dts/chart/gradient/index.d.ts +2 -0
- package/dts/chart/gradient/index.d.ts.map +1 -0
- package/dts/chart/index.d.ts +15 -0
- package/dts/chart/index.d.ts.map +1 -0
- package/dts/chart/line/DefaultReferenceLineLabel.d.ts +9 -0
- package/dts/chart/line/DefaultReferenceLineLabel.d.ts.map +1 -0
- package/dts/chart/line/DottedLine.d.ts +20 -0
- package/dts/chart/line/DottedLine.d.ts.map +1 -0
- package/dts/chart/line/Line.d.ts +115 -0
- package/dts/chart/line/Line.d.ts.map +1 -0
- package/dts/chart/line/LineChart.d.ts +118 -0
- package/dts/chart/line/LineChart.d.ts.map +1 -0
- package/dts/chart/line/ReferenceLine.d.ts +139 -0
- package/dts/chart/line/ReferenceLine.d.ts.map +1 -0
- package/dts/chart/line/SolidLine.d.ts +15 -0
- package/dts/chart/line/SolidLine.d.ts.map +1 -0
- package/dts/chart/line/index.d.ts +7 -0
- package/dts/chart/line/index.d.ts.map +1 -0
- package/dts/chart/point/DefaultPointLabel.d.ts +10 -0
- package/dts/chart/point/DefaultPointLabel.d.ts.map +1 -0
- package/dts/chart/point/Point.d.ts +120 -0
- package/dts/chart/point/Point.d.ts.map +1 -0
- package/dts/chart/point/index.d.ts +3 -0
- package/dts/chart/point/index.d.ts.map +1 -0
- package/dts/chart/scrubber/DefaultScrubberBeacon.d.ts +8 -0
- package/dts/chart/scrubber/DefaultScrubberBeacon.d.ts.map +1 -0
- package/dts/chart/scrubber/DefaultScrubberBeaconLabel.d.ts +12 -0
- package/dts/chart/scrubber/DefaultScrubberBeaconLabel.d.ts.map +1 -0
- package/dts/chart/scrubber/DefaultScrubberLabel.d.ts +11 -0
- package/dts/chart/scrubber/DefaultScrubberLabel.d.ts.map +1 -0
- package/dts/chart/scrubber/Scrubber.d.ts +233 -0
- package/dts/chart/scrubber/Scrubber.d.ts.map +1 -0
- package/dts/chart/scrubber/ScrubberBeaconGroup.d.ts +44 -0
- package/dts/chart/scrubber/ScrubberBeaconGroup.d.ts.map +1 -0
- package/dts/chart/scrubber/ScrubberBeaconLabelGroup.d.ts +31 -0
- package/dts/chart/scrubber/ScrubberBeaconLabelGroup.d.ts.map +1 -0
- package/dts/chart/scrubber/ScrubberProvider.d.ts +20 -0
- package/dts/chart/scrubber/ScrubberProvider.d.ts.map +1 -0
- package/dts/chart/scrubber/index.d.ts +5 -0
- package/dts/chart/scrubber/index.d.ts.map +1 -0
- package/dts/chart/text/ChartText.d.ts +164 -0
- package/dts/chart/text/ChartText.d.ts.map +1 -0
- package/dts/chart/text/ChartTextGroup.d.ts +61 -0
- package/dts/chart/text/ChartTextGroup.d.ts.map +1 -0
- package/dts/chart/text/index.d.ts +3 -0
- package/dts/chart/text/index.d.ts.map +1 -0
- package/dts/chart/utils/axis.d.ts +342 -0
- package/dts/chart/utils/axis.d.ts.map +1 -0
- package/dts/chart/utils/bar.d.ts +20 -0
- package/dts/chart/utils/bar.d.ts.map +1 -0
- package/dts/chart/utils/chart.d.ts +124 -0
- package/dts/chart/utils/chart.d.ts.map +1 -0
- package/dts/chart/utils/context.d.ts +116 -0
- package/dts/chart/utils/context.d.ts.map +1 -0
- package/dts/chart/utils/gradient.d.ts +117 -0
- package/dts/chart/utils/gradient.d.ts.map +1 -0
- package/dts/chart/utils/index.d.ts +11 -0
- package/dts/chart/utils/index.d.ts.map +1 -0
- package/dts/chart/utils/path.d.ts +160 -0
- package/dts/chart/utils/path.d.ts.map +1 -0
- package/dts/chart/utils/point.d.ts +134 -0
- package/dts/chart/utils/point.d.ts.map +1 -0
- package/dts/chart/utils/scale.d.ts +134 -0
- package/dts/chart/utils/scale.d.ts.map +1 -0
- package/dts/chart/utils/scrubber.d.ts +39 -0
- package/dts/chart/utils/scrubber.d.ts.map +1 -0
- package/dts/chart/utils/transition.d.ts +140 -0
- package/dts/chart/utils/transition.d.ts.map +1 -0
- package/dts/index.d.ts +2 -1
- package/dts/index.d.ts.map +1 -1
- package/dts/sparkline/Counter.d.ts +7 -2
- package/dts/sparkline/Sparkline.d.ts +67 -16
- package/dts/sparkline/Sparkline.d.ts.map +1 -1
- package/dts/sparkline/SparklineArea.d.ts +10 -4
- package/dts/sparkline/SparklineArea.d.ts.map +1 -1
- package/dts/sparkline/SparklineAreaPattern.d.ts +12 -4
- package/dts/sparkline/SparklineAreaPattern.d.ts.map +1 -1
- package/dts/sparkline/SparklineGradient.d.ts +21 -10
- package/dts/sparkline/SparklineGradient.d.ts.map +1 -1
- package/dts/sparkline/__figma__/Sparkline.figma.d.ts +1 -1
- package/dts/sparkline/generateSparklineWithId.d.ts +8 -2
- package/dts/sparkline/generateSparklineWithId.d.ts.map +1 -1
- package/dts/sparkline/index.d.ts +1 -1
- package/dts/sparkline/sparkline-interactive/SparklineAccessibleView.d.ts +8 -3
- package/dts/sparkline/sparkline-interactive/SparklineInteractive.d.ts +132 -110
- package/dts/sparkline/sparkline-interactive/SparklineInteractive.d.ts.map +1 -1
- package/dts/sparkline/sparkline-interactive/SparklineInteractiveAnimatedPath.d.ts +22 -9
- package/dts/sparkline/sparkline-interactive/SparklineInteractiveAnimatedPath.d.ts.map +1 -1
- package/dts/sparkline/sparkline-interactive/SparklineInteractiveHoverDate.d.ts +18 -7
- package/dts/sparkline/sparkline-interactive/SparklineInteractiveLineVertical.d.ts +9 -4
- package/dts/sparkline/sparkline-interactive/SparklineInteractiveMarkerDates.d.ts +11 -6
- package/dts/sparkline/sparkline-interactive/SparklineInteractiveMinMax.d.ts +7 -5
- package/dts/sparkline/sparkline-interactive/SparklineInteractivePanGestureHandler.d.ts +22 -10
- package/dts/sparkline/sparkline-interactive/SparklineInteractivePaths.d.ts +21 -7
- package/dts/sparkline/sparkline-interactive/SparklineInteractivePaths.d.ts.map +1 -1
- package/dts/sparkline/sparkline-interactive/SparklineInteractivePeriodSelector.d.ts +21 -16
- package/dts/sparkline/sparkline-interactive/SparklineInteractiveProvider.d.ts +29 -23
- package/dts/sparkline/sparkline-interactive/SparklineInteractiveTimeseriesPaths.d.ts +22 -14
- package/dts/sparkline/sparkline-interactive/__figma__/SparklineInteractive.figma.d.ts +1 -1
- package/dts/sparkline/sparkline-interactive/useInterruptiblePathAnimation.d.ts +9 -5
- package/dts/sparkline/sparkline-interactive/useMinMaxTransform.d.ts +11 -6
- package/dts/sparkline/sparkline-interactive/useOpacityAnimation.d.ts +5 -2
- package/dts/sparkline/sparkline-interactive/useSparklineInteractiveConstants.d.ts +17 -17
- package/dts/sparkline/sparkline-interactive/useSparklineInteractiveLineStyles.d.ts +16 -13
- package/dts/sparkline/sparkline-interactive-header/SparklineInteractiveHeader.d.ts +106 -98
- package/dts/sparkline/sparkline-interactive-header/__figma__/SparklineInteractiveHeader.figma.d.ts +1 -1
- package/dts/sparkline/sparkline-interactive-header/useSparklineInteractiveHeaderStyles.d.ts +22 -19
- package/esm/chart/CartesianChart.js +335 -0
- package/esm/chart/ChartContextBridge.js +148 -0
- package/esm/chart/ChartProvider.js +10 -0
- package/esm/chart/Path.js +218 -0
- package/esm/chart/PeriodSelector.js +136 -0
- package/esm/chart/__stories__/CartesianChart.stories.js +723 -0
- package/esm/chart/__stories__/Chart.stories.js +77 -0
- package/esm/chart/__stories__/PeriodSelector.stories.js +322 -0
- package/esm/chart/area/Area.js +75 -0
- package/esm/chart/area/AreaChart.js +151 -0
- package/esm/chart/area/DottedArea.js +80 -0
- package/esm/chart/area/GradientArea.js +54 -0
- package/esm/chart/area/SolidArea.js +38 -0
- package/esm/chart/area/__stories__/AreaChart.stories.js +100 -0
- package/esm/chart/area/index.js +7 -0
- package/esm/chart/axis/Axis.js +45 -0
- package/esm/chart/axis/DefaultAxisTickLabel.js +11 -0
- package/esm/chart/axis/XAxis.js +188 -0
- package/esm/chart/axis/YAxis.js +177 -0
- package/esm/chart/axis/__stories__/Axis.stories.js +276 -0
- package/esm/chart/axis/index.js +6 -0
- package/esm/chart/bar/Bar.js +69 -0
- package/esm/chart/bar/BarChart.js +125 -0
- package/esm/chart/bar/BarPlot.js +102 -0
- package/esm/chart/bar/BarStack.js +551 -0
- package/esm/chart/bar/BarStackGroup.js +79 -0
- package/esm/chart/bar/DefaultBar.js +56 -0
- package/esm/chart/bar/DefaultBarStack.js +47 -0
- package/esm/chart/bar/__stories__/BarChart.stories.js +668 -0
- package/esm/chart/bar/index.js +9 -0
- package/esm/chart/gradient/Gradient.js +53 -0
- package/esm/chart/gradient/index.js +1 -0
- package/esm/chart/index.js +16 -0
- package/esm/chart/line/DefaultReferenceLineLabel.js +66 -0
- package/esm/chart/line/DottedLine.js +50 -0
- package/esm/chart/line/Line.js +178 -0
- package/esm/chart/line/LineChart.js +121 -0
- package/esm/chart/line/ReferenceLine.js +132 -0
- package/esm/chart/line/SolidLine.js +46 -0
- package/esm/chart/line/__stories__/LineChart.stories.js +2372 -0
- package/esm/chart/line/__stories__/ReferenceLine.stories.js +132 -0
- package/esm/chart/line/index.js +8 -0
- package/esm/chart/point/DefaultPointLabel.js +39 -0
- package/esm/chart/point/Point.js +188 -0
- package/esm/chart/point/index.js +2 -0
- package/esm/chart/scrubber/DefaultScrubberBeacon.js +179 -0
- package/esm/chart/scrubber/DefaultScrubberBeaconLabel.js +43 -0
- package/esm/chart/scrubber/DefaultScrubberLabel.js +28 -0
- package/esm/chart/scrubber/Scrubber.js +166 -0
- package/esm/chart/scrubber/ScrubberBeaconGroup.js +161 -0
- package/esm/chart/scrubber/ScrubberBeaconLabelGroup.js +185 -0
- package/esm/chart/scrubber/ScrubberProvider.js +135 -0
- package/esm/chart/scrubber/index.js +4 -0
- package/esm/chart/text/ChartText.js +305 -0
- package/esm/chart/text/ChartTextGroup.js +211 -0
- package/esm/chart/text/index.js +4 -0
- package/esm/chart/utils/axis.js +592 -0
- package/esm/chart/utils/bar.js +24 -0
- package/esm/chart/utils/chart.js +270 -0
- package/esm/chart/utils/context.js +15 -0
- package/esm/chart/utils/gradient.js +305 -0
- package/esm/chart/utils/index.js +12 -0
- package/esm/chart/utils/path.js +274 -0
- package/esm/chart/utils/point.js +229 -0
- package/esm/chart/utils/scale.js +277 -0
- package/esm/chart/utils/scrubber.js +139 -0
- package/esm/chart/utils/transition.js +185 -0
- package/esm/index.js +4 -1
- package/esm/sparkline/Sparkline.js +129 -16
- package/esm/sparkline/SparklineArea.js +7 -2
- package/esm/sparkline/SparklineAreaPattern.js +4 -2
- package/esm/sparkline/SparklineGradient.js +4 -0
- package/esm/sparkline/__stories__/Sparkline.stories.js +11 -7
- package/esm/sparkline/__stories__/SparklineGradient.stories.js +7 -4
- package/esm/sparkline/generateSparklineWithId.js +3 -2
- package/esm/sparkline/sparkline-interactive/SparklineInteractive.js +5 -1
- package/esm/sparkline/sparkline-interactive/SparklineInteractiveAnimatedPath.js +5 -2
- package/esm/sparkline/sparkline-interactive/SparklineInteractivePaths.js +4 -0
- package/esm/sparkline/sparkline-interactive/__stories__/SparklineInteractive.stories.js +76 -24
- package/esm/sparkline/sparkline-interactive-header/__stories__/SparklineInteractiveHeader.stories.js +17 -9
- package/package.json +17 -11
- package/dts/sparkline/__stories__/Sparkline.stories.d.ts +0 -3
- package/dts/sparkline/__stories__/Sparkline.stories.d.ts.map +0 -1
- package/dts/sparkline/__stories__/SparklineGradient.stories.d.ts +0 -3
- package/dts/sparkline/__stories__/SparklineGradient.stories.d.ts.map +0 -1
- package/dts/sparkline/sparkline-interactive/__stories__/SparklineInteractive.stories.d.ts +0 -3
- package/dts/sparkline/sparkline-interactive/__stories__/SparklineInteractive.stories.d.ts.map +0 -1
- package/dts/sparkline/sparkline-interactive/__tests__/SparklineInteractive.test.d.ts +0 -2
- package/dts/sparkline/sparkline-interactive/__tests__/SparklineInteractive.test.d.ts.map +0 -1
- package/dts/sparkline/sparkline-interactive/__tests__/SparklineInteractiveHoverDate.test.d.ts +0 -2
- package/dts/sparkline/sparkline-interactive/__tests__/SparklineInteractiveHoverDate.test.d.ts.map +0 -1
- package/dts/sparkline/sparkline-interactive/__tests__/SparklineInteractivePanGestureHandler.test.d.ts +0 -2
- package/dts/sparkline/sparkline-interactive/__tests__/SparklineInteractivePanGestureHandler.test.d.ts.map +0 -1
- package/dts/sparkline/sparkline-interactive/__tests__/SparklineInteractivePeriodSelector.test.d.ts +0 -2
- package/dts/sparkline/sparkline-interactive/__tests__/SparklineInteractivePeriodSelector.test.d.ts.map +0 -1
- package/dts/sparkline/sparkline-interactive/__tests__/SparklineInteractiveTimeseriesPaths.test.d.ts +0 -2
- package/dts/sparkline/sparkline-interactive/__tests__/SparklineInteractiveTimeseriesPaths.test.d.ts.map +0 -1
- package/dts/sparkline/sparkline-interactive/__tests__/useMinMaxTransform.test.d.ts +0 -2
- package/dts/sparkline/sparkline-interactive/__tests__/useMinMaxTransform.test.d.ts.map +0 -1
- package/dts/sparkline/sparkline-interactive/useInterruptiblePathAnimation.test.disable.d.ts +0 -2
- package/dts/sparkline/sparkline-interactive/useInterruptiblePathAnimation.test.disable.d.ts.map +0 -1
- package/dts/sparkline/sparkline-interactive-header/__stories__/SparklineInteractiveHeader.stories.d.ts +0 -4
- package/dts/sparkline/sparkline-interactive-header/__stories__/SparklineInteractiveHeader.stories.d.ts.map +0 -1
- package/dts/sparkline/sparkline-interactive-header/__tests__/SparklineInteractiveHeader.test.d.ts +0 -2
- package/dts/sparkline/sparkline-interactive-header/__tests__/SparklineInteractiveHeader.test.d.ts.map +0 -1
- package/dts/sparkline/sparkline-interactive-header/__tests__/useSparklineInteractiveHeaderStyles.test.d.ts +0 -2
- package/dts/sparkline/sparkline-interactive-header/__tests__/useSparklineInteractiveHeaderStyles.test.d.ts.map +0 -1
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { memo, useId, useMemo } from 'react';
|
|
2
|
+
import { Group, Skia } from '@shopify/react-native-skia';
|
|
3
|
+
import { useCartesianChartContext } from '../ChartProvider';
|
|
4
|
+
import { defaultAxisId } from '../utils';
|
|
5
|
+
import { BarStackGroup } from './BarStackGroup';
|
|
6
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
7
|
+
/**
|
|
8
|
+
* BarPlot component that handles multiple series with proper stacking coordination.
|
|
9
|
+
* Groups series by stack ID + y-axis ID combination and renders BarStackGroup for each group.
|
|
10
|
+
* This allows series with different y-axes to be rendered side by side while preventing
|
|
11
|
+
* cross-axis stacking (e.g., comparing $1M vs $1B companies on different scales).
|
|
12
|
+
*/
|
|
13
|
+
export const BarPlot = /*#__PURE__*/memo(_ref => {
|
|
14
|
+
let {
|
|
15
|
+
seriesIds,
|
|
16
|
+
barPadding = 0.1,
|
|
17
|
+
BarComponent: defaultBarComponent,
|
|
18
|
+
fillOpacity: defaultFillOpacity,
|
|
19
|
+
stroke: defaultStroke,
|
|
20
|
+
strokeWidth: defaultStrokeWidth,
|
|
21
|
+
borderRadius: defaultBorderRadius,
|
|
22
|
+
roundBaseline,
|
|
23
|
+
BarStackComponent,
|
|
24
|
+
stackGap,
|
|
25
|
+
barMinSize,
|
|
26
|
+
stackMinSize,
|
|
27
|
+
transition
|
|
28
|
+
} = _ref;
|
|
29
|
+
const {
|
|
30
|
+
series: allSeries,
|
|
31
|
+
drawingArea
|
|
32
|
+
} = useCartesianChartContext();
|
|
33
|
+
const clipPathId = useId();
|
|
34
|
+
const targetSeries = useMemo(() => {
|
|
35
|
+
// Then filter by seriesIds if provided
|
|
36
|
+
if (seriesIds !== undefined) {
|
|
37
|
+
return allSeries.filter(s => seriesIds.includes(s.id));
|
|
38
|
+
}
|
|
39
|
+
return allSeries;
|
|
40
|
+
}, [allSeries, seriesIds]);
|
|
41
|
+
const stackGroups = useMemo(() => {
|
|
42
|
+
const groups = new Map();
|
|
43
|
+
|
|
44
|
+
// Group series into stacks based on stackId + yAxisId combination
|
|
45
|
+
targetSeries.forEach(series => {
|
|
46
|
+
var _series$yAxisId;
|
|
47
|
+
const yAxisId = (_series$yAxisId = series.yAxisId) != null ? _series$yAxisId : defaultAxisId;
|
|
48
|
+
const stackId = series.stackId || "individual-" + series.id;
|
|
49
|
+
const stackKey = stackId + ":" + yAxisId;
|
|
50
|
+
if (!groups.has(stackKey)) {
|
|
51
|
+
groups.set(stackKey, {
|
|
52
|
+
stackId: stackKey,
|
|
53
|
+
series: [],
|
|
54
|
+
yAxisId: series.yAxisId
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
const group = groups.get(stackKey);
|
|
58
|
+
group.series.push(series);
|
|
59
|
+
});
|
|
60
|
+
return Array.from(groups.values());
|
|
61
|
+
}, [targetSeries]);
|
|
62
|
+
|
|
63
|
+
// Create clip path for the entire chart area (shared by all bars)
|
|
64
|
+
const clipPath = useMemo(() => {
|
|
65
|
+
if (!drawingArea) return null;
|
|
66
|
+
const clip = Skia.Path.Make();
|
|
67
|
+
clip.addRect({
|
|
68
|
+
x: drawingArea.x,
|
|
69
|
+
y: drawingArea.y,
|
|
70
|
+
width: drawingArea.width,
|
|
71
|
+
height: drawingArea.height
|
|
72
|
+
});
|
|
73
|
+
return clip;
|
|
74
|
+
}, [drawingArea]);
|
|
75
|
+
if (!clipPath) {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Note: Clipping is now handled here at the BarPlot level (one clip path for all bars!)
|
|
80
|
+
// This is much more efficient than creating a clip path for each individual bar
|
|
81
|
+
return /*#__PURE__*/_jsx(Group, {
|
|
82
|
+
clip: clipPath,
|
|
83
|
+
children: stackGroups.map((group, stackIndex) => /*#__PURE__*/_jsx(BarStackGroup, {
|
|
84
|
+
BarComponent: defaultBarComponent,
|
|
85
|
+
BarStackComponent: BarStackComponent,
|
|
86
|
+
barMinSize: barMinSize,
|
|
87
|
+
barPadding: barPadding,
|
|
88
|
+
borderRadius: defaultBorderRadius,
|
|
89
|
+
fillOpacity: defaultFillOpacity,
|
|
90
|
+
roundBaseline: roundBaseline,
|
|
91
|
+
series: group.series,
|
|
92
|
+
stackGap: stackGap,
|
|
93
|
+
stackIndex: stackIndex,
|
|
94
|
+
stackMinSize: stackMinSize,
|
|
95
|
+
stroke: defaultStroke,
|
|
96
|
+
strokeWidth: defaultStrokeWidth,
|
|
97
|
+
totalStacks: stackGroups.length,
|
|
98
|
+
transition: transition,
|
|
99
|
+
yAxisId: group.yAxisId
|
|
100
|
+
}, group.stackId))
|
|
101
|
+
});
|
|
102
|
+
});
|
|
@@ -0,0 +1,551 @@
|
|
|
1
|
+
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); }
|
|
2
|
+
import React, { memo, useMemo } from 'react';
|
|
3
|
+
import { useTheme } from '@coinbase/cds-mobile/hooks/useTheme';
|
|
4
|
+
import { useCartesianChartContext } from '../ChartProvider';
|
|
5
|
+
import { evaluateGradientAtValue, getGradientStops } from '../utils/gradient';
|
|
6
|
+
import { convertToSerializableScale } from '../utils/scale';
|
|
7
|
+
import { Bar } from './Bar';
|
|
8
|
+
import { DefaultBarStack } from './DefaultBarStack';
|
|
9
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
10
|
+
const EPSILON = 1e-4;
|
|
11
|
+
/**
|
|
12
|
+
* BarStack component that renders a single stack of bars at a specific category index.
|
|
13
|
+
* Handles the stacking logic for bars within a single category.
|
|
14
|
+
*/
|
|
15
|
+
export const BarStack = /*#__PURE__*/memo(_ref => {
|
|
16
|
+
let {
|
|
17
|
+
series,
|
|
18
|
+
categoryIndex,
|
|
19
|
+
x,
|
|
20
|
+
width,
|
|
21
|
+
yScale,
|
|
22
|
+
rect,
|
|
23
|
+
BarComponent: defaultBarComponent,
|
|
24
|
+
fillOpacity: defaultFillOpacity,
|
|
25
|
+
stroke: defaultStroke,
|
|
26
|
+
strokeWidth: defaultStrokeWidth,
|
|
27
|
+
borderRadius = 4,
|
|
28
|
+
BarStackComponent = DefaultBarStack,
|
|
29
|
+
stackGap,
|
|
30
|
+
barMinSize,
|
|
31
|
+
stackMinSize,
|
|
32
|
+
roundBaseline,
|
|
33
|
+
transition
|
|
34
|
+
} = _ref;
|
|
35
|
+
const theme = useTheme();
|
|
36
|
+
const {
|
|
37
|
+
getSeriesData,
|
|
38
|
+
getXAxis,
|
|
39
|
+
getXScale
|
|
40
|
+
} = useCartesianChartContext();
|
|
41
|
+
const xAxis = getXAxis();
|
|
42
|
+
const xScale = getXScale();
|
|
43
|
+
const baseline = useMemo(() => {
|
|
44
|
+
var _yScale;
|
|
45
|
+
const domain = yScale.domain();
|
|
46
|
+
const [domainMin, domainMax] = domain;
|
|
47
|
+
const baselineValue = domainMin >= 0 ? domainMin : domainMax <= 0 ? domainMax : 0;
|
|
48
|
+
const baseline = (_yScale = yScale(baselineValue)) != null ? _yScale : rect.y + rect.height;
|
|
49
|
+
return Math.max(rect.y, Math.min(baseline, rect.y + rect.height));
|
|
50
|
+
}, [rect.height, rect.y, yScale]);
|
|
51
|
+
const seriesGradients = useMemo(() => {
|
|
52
|
+
return series.map(s => {
|
|
53
|
+
if (!s.gradient || !xScale || !yScale) return;
|
|
54
|
+
const gradientScale = s.gradient.axis === 'x' ? xScale : yScale;
|
|
55
|
+
const serializableScale = convertToSerializableScale(gradientScale);
|
|
56
|
+
if (!serializableScale) return;
|
|
57
|
+
const domain = {
|
|
58
|
+
min: serializableScale.domain[0],
|
|
59
|
+
max: serializableScale.domain[1]
|
|
60
|
+
};
|
|
61
|
+
const stops = getGradientStops(s.gradient.stops, domain);
|
|
62
|
+
return {
|
|
63
|
+
seriesId: s.id,
|
|
64
|
+
gradient: s.gradient,
|
|
65
|
+
scale: serializableScale,
|
|
66
|
+
stops
|
|
67
|
+
};
|
|
68
|
+
});
|
|
69
|
+
}, [series, xScale, yScale]);
|
|
70
|
+
|
|
71
|
+
// Calculate bars for this specific category
|
|
72
|
+
const {
|
|
73
|
+
bars,
|
|
74
|
+
stackRect
|
|
75
|
+
} = useMemo(() => {
|
|
76
|
+
let allBars = [];
|
|
77
|
+
|
|
78
|
+
// Track how many bars we've stacked in each direction for gap calculation
|
|
79
|
+
let positiveBarCount = 0;
|
|
80
|
+
let negativeBarCount = 0;
|
|
81
|
+
|
|
82
|
+
// Track stack bounds for clipping
|
|
83
|
+
let minY = Infinity;
|
|
84
|
+
let maxY = -Infinity;
|
|
85
|
+
|
|
86
|
+
// Process each series in the stack
|
|
87
|
+
series.forEach(s => {
|
|
88
|
+
var _yScale2, _yScale3;
|
|
89
|
+
const data = getSeriesData(s.id);
|
|
90
|
+
if (!data) return;
|
|
91
|
+
const value = data[categoryIndex];
|
|
92
|
+
if (value === null || value === undefined) return;
|
|
93
|
+
const originalData = s.data;
|
|
94
|
+
const originalValue = originalData == null ? void 0 : originalData[categoryIndex];
|
|
95
|
+
// Only apply gap logic if the original data wasn't tuple format
|
|
96
|
+
const shouldApplyGap = !Array.isArray(originalValue);
|
|
97
|
+
|
|
98
|
+
// Sort to be in ascending order
|
|
99
|
+
const [bottom, top] = value.sort((a, b) => a - b);
|
|
100
|
+
const isAboveBaseline = bottom >= 0 && top !== bottom;
|
|
101
|
+
const isBelowBaseline = bottom <= 0 && bottom !== top;
|
|
102
|
+
const barBottom = (_yScale2 = yScale(bottom)) != null ? _yScale2 : baseline;
|
|
103
|
+
const barTop = (_yScale3 = yScale(top)) != null ? _yScale3 : baseline;
|
|
104
|
+
|
|
105
|
+
// Track bar counts for later gap calculations
|
|
106
|
+
if (shouldApplyGap) {
|
|
107
|
+
if (isAboveBaseline) {
|
|
108
|
+
positiveBarCount++;
|
|
109
|
+
} else if (isBelowBaseline) {
|
|
110
|
+
negativeBarCount++;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Calculate height (remember SVG y coordinates are inverted)
|
|
115
|
+
const height = Math.abs(barBottom - barTop);
|
|
116
|
+
const y = Math.min(barBottom, barTop);
|
|
117
|
+
|
|
118
|
+
// Skip bars that would have zero or negative height
|
|
119
|
+
if (height <= 0) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Update stack bounds
|
|
124
|
+
minY = Math.min(minY, y);
|
|
125
|
+
maxY = Math.max(maxY, y + height);
|
|
126
|
+
|
|
127
|
+
// Determine fill color, respecting gradient if present
|
|
128
|
+
let barFill = s.color || theme.color.fgPrimary;
|
|
129
|
+
|
|
130
|
+
// Evaluate gradient if provided (using precomputed stops)
|
|
131
|
+
const seriesGradientConfig = seriesGradients.find(g => (g == null ? void 0 : g.seriesId) === s.id);
|
|
132
|
+
if (seriesGradientConfig) {
|
|
133
|
+
var _seriesGradientConfig;
|
|
134
|
+
const axis = (_seriesGradientConfig = seriesGradientConfig.gradient.axis) != null ? _seriesGradientConfig : 'y';
|
|
135
|
+
// For x-axis gradient, use the categoryIndex
|
|
136
|
+
// For y-axis gradient, use the actual data value
|
|
137
|
+
const dataValue = axis === 'x' ? categoryIndex : top;
|
|
138
|
+
const evaluatedColor = evaluateGradientAtValue(seriesGradientConfig.stops, dataValue, seriesGradientConfig.scale);
|
|
139
|
+
if (evaluatedColor) {
|
|
140
|
+
// Only apply gradient color if fill is not explicitly set
|
|
141
|
+
barFill = evaluatedColor;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
allBars.push({
|
|
145
|
+
seriesId: s.id,
|
|
146
|
+
x,
|
|
147
|
+
y,
|
|
148
|
+
width,
|
|
149
|
+
height,
|
|
150
|
+
dataY: value,
|
|
151
|
+
// Store the actual data value
|
|
152
|
+
fill: barFill,
|
|
153
|
+
// Check if the bar should be rounded based on the baseline, with an epsilon to handle floating-point rounding
|
|
154
|
+
roundTop: roundBaseline || Math.abs(barTop - baseline) >= EPSILON,
|
|
155
|
+
roundBottom: roundBaseline || Math.abs(barBottom - baseline) >= EPSILON,
|
|
156
|
+
shouldApplyGap
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// Apply proportional gap distribution to maintain total stack height
|
|
161
|
+
if (stackGap && allBars.length > 1) {
|
|
162
|
+
// Separate bars by baseline side
|
|
163
|
+
const barsAboveBaseline = allBars.filter(bar => {
|
|
164
|
+
const [bottom, top] = bar.dataY.sort((a, b) => a - b);
|
|
165
|
+
return bottom >= 0 && top !== bottom && bar.shouldApplyGap;
|
|
166
|
+
});
|
|
167
|
+
const barsBelowBaseline = allBars.filter(bar => {
|
|
168
|
+
const [bottom, top] = bar.dataY.sort((a, b) => a - b);
|
|
169
|
+
return bottom <= 0 && bottom !== top && bar.shouldApplyGap;
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// Apply proportional gaps to bars above baseline
|
|
173
|
+
if (barsAboveBaseline.length > 1) {
|
|
174
|
+
const totalGapSpace = stackGap * (barsAboveBaseline.length - 1);
|
|
175
|
+
const totalDataHeight = barsAboveBaseline.reduce((sum, bar) => sum + bar.height, 0);
|
|
176
|
+
const heightReduction = totalGapSpace / totalDataHeight;
|
|
177
|
+
|
|
178
|
+
// Sort bars by position (from baseline upward)
|
|
179
|
+
const sortedBars = barsAboveBaseline.sort((a, b) => b.y - a.y);
|
|
180
|
+
let currentY = baseline;
|
|
181
|
+
sortedBars.forEach((bar, index) => {
|
|
182
|
+
// Reduce bar height proportionally
|
|
183
|
+
const newHeight = bar.height * (1 - heightReduction);
|
|
184
|
+
const newY = currentY - newHeight;
|
|
185
|
+
|
|
186
|
+
// Update the bar in allBars array
|
|
187
|
+
const barIndex = allBars.findIndex(b => b.seriesId === bar.seriesId);
|
|
188
|
+
if (barIndex !== -1) {
|
|
189
|
+
allBars[barIndex] = _extends({}, allBars[barIndex], {
|
|
190
|
+
height: newHeight,
|
|
191
|
+
y: newY
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Move to next position (include gap for next bar)
|
|
196
|
+
currentY = newY - (index < sortedBars.length - 1 ? stackGap : 0);
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Apply proportional gaps to bars below baseline
|
|
201
|
+
if (barsBelowBaseline.length > 1) {
|
|
202
|
+
const totalGapSpace = stackGap * (barsBelowBaseline.length - 1);
|
|
203
|
+
const totalDataHeight = barsBelowBaseline.reduce((sum, bar) => sum + bar.height, 0);
|
|
204
|
+
const heightReduction = totalGapSpace / totalDataHeight;
|
|
205
|
+
|
|
206
|
+
// Sort bars by position (from baseline downward)
|
|
207
|
+
const sortedBars = barsBelowBaseline.sort((a, b) => a.y - b.y);
|
|
208
|
+
let currentY = baseline;
|
|
209
|
+
sortedBars.forEach((bar, index) => {
|
|
210
|
+
// Reduce bar height proportionally
|
|
211
|
+
const newHeight = bar.height * (1 - heightReduction);
|
|
212
|
+
|
|
213
|
+
// Update the bar in allBars array
|
|
214
|
+
const barIndex = allBars.findIndex(b => b.seriesId === bar.seriesId);
|
|
215
|
+
if (barIndex !== -1) {
|
|
216
|
+
allBars[barIndex] = _extends({}, allBars[barIndex], {
|
|
217
|
+
height: newHeight,
|
|
218
|
+
y: currentY
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Move to next position (include gap for next bar)
|
|
223
|
+
currentY = currentY + newHeight + (index < sortedBars.length - 1 ? stackGap : 0);
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Recalculate stack bounds after gap adjustments
|
|
228
|
+
if (allBars.length > 0) {
|
|
229
|
+
minY = Math.min(...allBars.map(bar => bar.y));
|
|
230
|
+
maxY = Math.max(...allBars.map(bar => bar.y + bar.height));
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Apply barMinSize constraints
|
|
235
|
+
if (barMinSize) {
|
|
236
|
+
// First, expand bars that need it and track the expansion
|
|
237
|
+
const expandedBars = allBars.map((bar, index) => {
|
|
238
|
+
if (bar.height < barMinSize) {
|
|
239
|
+
var _yScale4, _yScale5, _yScale6, _yScale7;
|
|
240
|
+
const heightIncrease = barMinSize - bar.height;
|
|
241
|
+
const [bottom, top] = bar.dataY.sort((a, b) => a - b);
|
|
242
|
+
|
|
243
|
+
// Determine how to expand the bar
|
|
244
|
+
let newBottom = bottom;
|
|
245
|
+
let newTop = top;
|
|
246
|
+
const scaleUnit = Math.abs(((_yScale4 = yScale(1)) != null ? _yScale4 : 0) - ((_yScale5 = yScale(0)) != null ? _yScale5 : 0));
|
|
247
|
+
if (bottom === 0) {
|
|
248
|
+
// Expand away from baseline (upward for positive)
|
|
249
|
+
newTop = top + heightIncrease / scaleUnit;
|
|
250
|
+
} else if (top === 0) {
|
|
251
|
+
// Expand away from baseline (downward for negative)
|
|
252
|
+
newBottom = bottom - heightIncrease / scaleUnit;
|
|
253
|
+
} else {
|
|
254
|
+
// Expand in both directions
|
|
255
|
+
const halfIncrease = heightIncrease / scaleUnit / 2;
|
|
256
|
+
newBottom = bottom - halfIncrease;
|
|
257
|
+
newTop = top + halfIncrease;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Recalculate bar position with new data values
|
|
261
|
+
const newBarBottom = (_yScale6 = yScale(newBottom)) != null ? _yScale6 : baseline;
|
|
262
|
+
const newBarTop = (_yScale7 = yScale(newTop)) != null ? _yScale7 : baseline;
|
|
263
|
+
const newHeight = Math.abs(newBarBottom - newBarTop);
|
|
264
|
+
const newY = Math.min(newBarBottom, newBarTop);
|
|
265
|
+
return _extends({}, bar, {
|
|
266
|
+
height: newHeight,
|
|
267
|
+
y: newY,
|
|
268
|
+
wasExpanded: true
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
return _extends({}, bar, {
|
|
272
|
+
wasExpanded: false
|
|
273
|
+
});
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
// Now reposition all bars to avoid overlaps, similar to stackMinSize logic
|
|
277
|
+
|
|
278
|
+
// Sort bars by position to maintain order
|
|
279
|
+
const sortedExpandedBars = [...expandedBars].sort((a, b) => a.y - b.y);
|
|
280
|
+
|
|
281
|
+
// Determine if we have bars above and below baseline
|
|
282
|
+
const barsAboveBaseline = sortedExpandedBars.filter(bar => bar.y + bar.height <= baseline);
|
|
283
|
+
const barsBelowBaseline = sortedExpandedBars.filter(bar => bar.y >= baseline);
|
|
284
|
+
|
|
285
|
+
// Create a map of new positions
|
|
286
|
+
const newPositions = new Map();
|
|
287
|
+
|
|
288
|
+
// Start positioning from the baseline and work outward
|
|
289
|
+
let currentYAbove = baseline; // Start at baseline, work upward (decreasing Y)
|
|
290
|
+
let currentYBelow = baseline; // Start at baseline, work downward (increasing Y)
|
|
291
|
+
|
|
292
|
+
// Position bars above baseline (positive values, decreasing Y)
|
|
293
|
+
for (let i = barsAboveBaseline.length - 1; i >= 0; i--) {
|
|
294
|
+
const bar = barsAboveBaseline[i];
|
|
295
|
+
const newY = currentYAbove - bar.height;
|
|
296
|
+
newPositions.set(bar.seriesId, {
|
|
297
|
+
y: newY,
|
|
298
|
+
height: bar.height
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
// Update currentYAbove for next bar (preserve gaps)
|
|
302
|
+
if (i > 0) {
|
|
303
|
+
const currentBar = barsAboveBaseline[i];
|
|
304
|
+
const nextBar = barsAboveBaseline[i - 1];
|
|
305
|
+
// Find original bars to get original gap
|
|
306
|
+
const originalCurrent = allBars.find(b => b.seriesId === currentBar.seriesId);
|
|
307
|
+
const originalNext = allBars.find(b => b.seriesId === nextBar.seriesId);
|
|
308
|
+
const originalGap = originalCurrent.y - (originalNext.y + originalNext.height);
|
|
309
|
+
currentYAbove = newY - originalGap;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Position bars below baseline (negative values, increasing Y)
|
|
314
|
+
for (let i = 0; i < barsBelowBaseline.length; i++) {
|
|
315
|
+
const bar = barsBelowBaseline[i];
|
|
316
|
+
const newY = currentYBelow;
|
|
317
|
+
newPositions.set(bar.seriesId, {
|
|
318
|
+
y: newY,
|
|
319
|
+
height: bar.height
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
// Update currentYBelow for next bar (preserve gaps)
|
|
323
|
+
if (i < barsBelowBaseline.length - 1) {
|
|
324
|
+
const currentBar = barsBelowBaseline[i];
|
|
325
|
+
const nextBar = barsBelowBaseline[i + 1];
|
|
326
|
+
// Find original bars to get original gap
|
|
327
|
+
const originalCurrent = allBars.find(b => b.seriesId === currentBar.seriesId);
|
|
328
|
+
const originalNext = allBars.find(b => b.seriesId === nextBar.seriesId);
|
|
329
|
+
const originalGap = originalNext.y - (originalCurrent.y + originalCurrent.height);
|
|
330
|
+
currentYBelow = newY + bar.height + originalGap;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// Apply new positions to all bars
|
|
335
|
+
allBars = expandedBars.map(bar => {
|
|
336
|
+
const newPos = newPositions.get(bar.seriesId);
|
|
337
|
+
if (newPos) {
|
|
338
|
+
return _extends({}, bar, {
|
|
339
|
+
y: newPos.y,
|
|
340
|
+
height: newPos.height
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
return bar;
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
// Recalculate stack bounds after barMinSize expansion and repositioning
|
|
347
|
+
if (allBars.length > 0) {
|
|
348
|
+
minY = Math.min(...allBars.map(bar => bar.y));
|
|
349
|
+
maxY = Math.max(...allBars.map(bar => bar.y + bar.height));
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// Apply border radius logic (will be reapplied after stackMinSize if needed)
|
|
354
|
+
const applyBorderRadiusLogic = bars => {
|
|
355
|
+
return bars.sort((a, b) => b.y - a.y).map((a, index) => {
|
|
356
|
+
const barBefore = index > 0 ? bars[index - 1] : null;
|
|
357
|
+
const barAfter = index < bars.length - 1 ? bars[index + 1] : null;
|
|
358
|
+
const shouldRoundTop = index === bars.length - 1 || a.shouldApplyGap && stackGap || !a.shouldApplyGap && barAfter && barAfter.y + barAfter.height !== a.y;
|
|
359
|
+
const shouldRoundBottom = index === 0 || a.shouldApplyGap && stackGap || !a.shouldApplyGap && barBefore && barBefore.y !== a.y + a.height;
|
|
360
|
+
return _extends({}, a, {
|
|
361
|
+
roundTop: Boolean(a.roundTop && shouldRoundTop),
|
|
362
|
+
roundBottom: Boolean(a.roundBottom && shouldRoundBottom)
|
|
363
|
+
});
|
|
364
|
+
});
|
|
365
|
+
};
|
|
366
|
+
allBars = applyBorderRadiusLogic(allBars);
|
|
367
|
+
|
|
368
|
+
// Calculate the bounding rect for the entire stack
|
|
369
|
+
let stackBounds = {
|
|
370
|
+
x,
|
|
371
|
+
y: minY === Infinity ? baseline : minY,
|
|
372
|
+
width,
|
|
373
|
+
height: maxY === -Infinity ? 0 : maxY - minY
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
// Apply stackMinSize constraints
|
|
377
|
+
if (stackMinSize) {
|
|
378
|
+
if (allBars.length === 1 && stackBounds.height < stackMinSize) {
|
|
379
|
+
var _yScale8, _yScale9, _yScale0, _yScale1;
|
|
380
|
+
// For single bars (non-stacked), treat stackMinSize like barMinSize
|
|
381
|
+
|
|
382
|
+
const bar = allBars[0];
|
|
383
|
+
const heightIncrease = stackMinSize - bar.height;
|
|
384
|
+
const [bottom, top] = bar.dataY.sort((a, b) => a - b);
|
|
385
|
+
|
|
386
|
+
// Determine how to expand the bar (same logic as barMinSize)
|
|
387
|
+
let newBottom = bottom;
|
|
388
|
+
let newTop = top;
|
|
389
|
+
const scaleUnit = Math.abs(((_yScale8 = yScale(1)) != null ? _yScale8 : 0) - ((_yScale9 = yScale(0)) != null ? _yScale9 : 0));
|
|
390
|
+
if (bottom === 0) {
|
|
391
|
+
// Expand away from baseline (upward for positive)
|
|
392
|
+
newTop = top + heightIncrease / scaleUnit;
|
|
393
|
+
} else if (top === 0) {
|
|
394
|
+
// Expand away from baseline (downward for negative)
|
|
395
|
+
newBottom = bottom - heightIncrease / scaleUnit;
|
|
396
|
+
} else {
|
|
397
|
+
// Expand in both directions
|
|
398
|
+
const halfIncrease = heightIncrease / scaleUnit / 2;
|
|
399
|
+
newBottom = bottom - halfIncrease;
|
|
400
|
+
newTop = top + halfIncrease;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// Recalculate bar position with new data values
|
|
404
|
+
const newBarBottom = (_yScale0 = yScale(newBottom)) != null ? _yScale0 : baseline;
|
|
405
|
+
const newBarTop = (_yScale1 = yScale(newTop)) != null ? _yScale1 : baseline;
|
|
406
|
+
const newHeight = Math.abs(newBarBottom - newBarTop);
|
|
407
|
+
const newY = Math.min(newBarBottom, newBarTop);
|
|
408
|
+
allBars[0] = _extends({}, bar, {
|
|
409
|
+
height: newHeight,
|
|
410
|
+
y: newY
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
// Recalculate stack bounds
|
|
414
|
+
stackBounds = {
|
|
415
|
+
x,
|
|
416
|
+
y: newY,
|
|
417
|
+
width,
|
|
418
|
+
height: newHeight
|
|
419
|
+
};
|
|
420
|
+
} else if (allBars.length > 1 && stackBounds.height < stackMinSize) {
|
|
421
|
+
// For multiple bars (stacked), scale heights while preserving gaps
|
|
422
|
+
|
|
423
|
+
// Calculate total bar height (excluding gaps)
|
|
424
|
+
const totalBarHeight = allBars.reduce((sum, bar) => sum + bar.height, 0);
|
|
425
|
+
const totalGapHeight = stackBounds.height - totalBarHeight;
|
|
426
|
+
|
|
427
|
+
// Calculate how much we need to increase bar heights
|
|
428
|
+
const requiredBarHeight = stackMinSize - totalGapHeight;
|
|
429
|
+
const barScaleFactor = requiredBarHeight / totalBarHeight;
|
|
430
|
+
|
|
431
|
+
// Sort bars by position to maintain order
|
|
432
|
+
const sortedBars = [...allBars].sort((a, b) => a.y - b.y);
|
|
433
|
+
|
|
434
|
+
// Determine if we have bars above and below baseline
|
|
435
|
+
const barsAboveBaseline = sortedBars.filter(bar => bar.y + bar.height <= baseline);
|
|
436
|
+
const barsBelowBaseline = sortedBars.filter(bar => bar.y >= baseline);
|
|
437
|
+
|
|
438
|
+
// Create a map of new positions
|
|
439
|
+
const newPositions = new Map();
|
|
440
|
+
|
|
441
|
+
// Start positioning from the baseline and work outward
|
|
442
|
+
let currentYAbove = baseline; // Start at baseline, work upward (decreasing Y)
|
|
443
|
+
let currentYBelow = baseline; // Start at baseline, work downward (increasing Y)
|
|
444
|
+
|
|
445
|
+
// Position bars above baseline (positive values, decreasing Y)
|
|
446
|
+
for (let i = barsAboveBaseline.length - 1; i >= 0; i--) {
|
|
447
|
+
const bar = barsAboveBaseline[i];
|
|
448
|
+
const newHeight = bar.height * barScaleFactor;
|
|
449
|
+
const newY = currentYAbove - newHeight;
|
|
450
|
+
newPositions.set(bar.seriesId, {
|
|
451
|
+
y: newY,
|
|
452
|
+
height: newHeight
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
// Update currentYAbove for next bar (preserve gaps)
|
|
456
|
+
if (i > 0) {
|
|
457
|
+
const currentBar = barsAboveBaseline[i];
|
|
458
|
+
const nextBar = barsAboveBaseline[i - 1];
|
|
459
|
+
const originalGap = currentBar.y - (nextBar.y + nextBar.height);
|
|
460
|
+
currentYAbove = newY - originalGap;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// Position bars below baseline (negative values, increasing Y)
|
|
465
|
+
for (let i = 0; i < barsBelowBaseline.length; i++) {
|
|
466
|
+
const bar = barsBelowBaseline[i];
|
|
467
|
+
const newHeight = bar.height * barScaleFactor;
|
|
468
|
+
const newY = currentYBelow;
|
|
469
|
+
newPositions.set(bar.seriesId, {
|
|
470
|
+
y: newY,
|
|
471
|
+
height: newHeight
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
// Update currentYBelow for next bar (preserve gaps)
|
|
475
|
+
if (i < barsBelowBaseline.length - 1) {
|
|
476
|
+
const currentBar = barsBelowBaseline[i];
|
|
477
|
+
const nextBar = barsBelowBaseline[i + 1];
|
|
478
|
+
const originalGap = nextBar.y - (currentBar.y + currentBar.height);
|
|
479
|
+
currentYBelow = newY + newHeight + originalGap;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
// Apply new positions to all bars
|
|
484
|
+
allBars = allBars.map(bar => {
|
|
485
|
+
const newPos = newPositions.get(bar.seriesId);
|
|
486
|
+
if (!newPos) return bar;
|
|
487
|
+
return _extends({}, bar, {
|
|
488
|
+
height: newPos.height,
|
|
489
|
+
y: newPos.y
|
|
490
|
+
});
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
// Recalculate stack bounds
|
|
494
|
+
const newMinY = Math.min(...allBars.map(bar => bar.y));
|
|
495
|
+
const newMaxY = Math.max(...allBars.map(bar => bar.y + bar.height));
|
|
496
|
+
stackBounds = {
|
|
497
|
+
x,
|
|
498
|
+
y: newMinY,
|
|
499
|
+
width,
|
|
500
|
+
height: newMaxY - newMinY
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
// Reapply border radius logic only if we actually scaled
|
|
505
|
+
if (stackBounds.height < stackMinSize) {
|
|
506
|
+
allBars = applyBorderRadiusLogic(allBars);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
return {
|
|
510
|
+
bars: allBars,
|
|
511
|
+
stackRect: stackBounds
|
|
512
|
+
};
|
|
513
|
+
}, [series, x, width, getSeriesData, categoryIndex, roundBaseline, baseline, stackGap, barMinSize, stackMinSize, yScale, seriesGradients, theme.color.fgPrimary]);
|
|
514
|
+
const xData = xAxis != null && xAxis.data && Array.isArray(xAxis.data) && typeof xAxis.data[0] === 'number' ? xAxis.data : undefined;
|
|
515
|
+
const dataX = xData ? xData[categoryIndex] : categoryIndex;
|
|
516
|
+
const barElements = bars.map((bar, index) => /*#__PURE__*/_jsx(Bar, {
|
|
517
|
+
BarComponent: defaultBarComponent,
|
|
518
|
+
borderRadius: borderRadius,
|
|
519
|
+
dataX: dataX,
|
|
520
|
+
dataY: bar.dataY,
|
|
521
|
+
fill: bar.fill,
|
|
522
|
+
fillOpacity: defaultFillOpacity,
|
|
523
|
+
height: bar.height,
|
|
524
|
+
originY: baseline,
|
|
525
|
+
roundBottom: bar.roundBottom,
|
|
526
|
+
roundTop: bar.roundTop,
|
|
527
|
+
stroke: defaultStroke,
|
|
528
|
+
strokeWidth: defaultStrokeWidth,
|
|
529
|
+
transition: transition,
|
|
530
|
+
width: bar.width,
|
|
531
|
+
x: bar.x,
|
|
532
|
+
y: bar.y
|
|
533
|
+
}, bar.seriesId + "-" + categoryIndex + "-" + index));
|
|
534
|
+
|
|
535
|
+
// Check if the bar should be rounded based on the baseline, with an epsilon to handle floating-point rounding
|
|
536
|
+
const stackRoundBottom = roundBaseline || Math.abs(stackRect.y + stackRect.height - baseline) >= EPSILON;
|
|
537
|
+
const stackRoundTop = roundBaseline || Math.abs(stackRect.y - baseline) >= EPSILON;
|
|
538
|
+
return /*#__PURE__*/_jsx(BarStackComponent, {
|
|
539
|
+
borderRadius: borderRadius,
|
|
540
|
+
categoryIndex: categoryIndex,
|
|
541
|
+
height: stackRect.height,
|
|
542
|
+
roundBottom: stackRoundBottom,
|
|
543
|
+
roundTop: stackRoundTop,
|
|
544
|
+
transition: transition,
|
|
545
|
+
width: stackRect.width,
|
|
546
|
+
x: stackRect.x,
|
|
547
|
+
y: stackRect.y,
|
|
548
|
+
yOrigin: baseline,
|
|
549
|
+
children: barElements
|
|
550
|
+
});
|
|
551
|
+
});
|