@coinbase/cds-mobile-visualization 3.6.1 → 3.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/dts/chart/CartesianChart.d.ts +4 -8
- package/dts/chart/CartesianChart.d.ts.map +1 -1
- package/dts/chart/area/Area.d.ts +3 -0
- package/dts/chart/area/Area.d.ts.map +1 -1
- 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/bar/BarChart.d.ts.map +1 -1
- package/dts/chart/bar/BarPlot.d.ts.map +1 -1
- package/dts/chart/bar/BarStack.d.ts.map +1 -1
- package/dts/chart/bar/DefaultBarStack.d.ts.map +1 -1
- package/dts/chart/gradient/Gradient.d.ts +14 -3
- 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 +3 -0
- package/dts/chart/line/Line.d.ts.map +1 -1
- package/dts/chart/line/LineChart.d.ts.map +1 -1
- package/dts/chart/line/SolidLine.d.ts.map +1 -1
- package/dts/chart/utils/axis.d.ts +18 -8
- package/dts/chart/utils/axis.d.ts.map +1 -1
- package/dts/chart/utils/bar.d.ts +17 -7
- package/dts/chart/utils/bar.d.ts.map +1 -1
- package/dts/chart/utils/chart.d.ts +9 -0
- package/dts/chart/utils/chart.d.ts.map +1 -1
- package/dts/chart/utils/context.d.ts +3 -3
- package/dts/chart/utils/context.d.ts.map +1 -1
- package/dts/chart/utils/gradient.d.ts +14 -4
- package/dts/chart/utils/gradient.d.ts.map +1 -1
- package/esm/chart/CartesianChart.js +6 -4
- package/esm/chart/__stories__/ChartTransitions.stories.js +68 -0
- package/esm/chart/area/Area.js +0 -2
- package/esm/chart/area/AreaChart.js +15 -19
- package/esm/chart/area/DottedArea.js +6 -4
- package/esm/chart/area/GradientArea.js +6 -4
- package/esm/chart/area/SolidArea.js +3 -0
- package/esm/chart/area/__stories__/AreaChart.stories.js +189 -3
- package/esm/chart/bar/BarChart.js +14 -22
- package/esm/chart/bar/BarPlot.js +11 -2
- package/esm/chart/bar/BarStack.js +15 -10
- package/esm/chart/bar/DefaultBarStack.js +9 -4
- package/esm/chart/bar/__stories__/BarChart.stories.js +84 -2
- package/esm/chart/gradient/Gradient.js +119 -26
- package/esm/chart/line/DottedLine.js +3 -0
- package/esm/chart/line/Line.js +1 -3
- package/esm/chart/line/LineChart.js +8 -4
- package/esm/chart/line/SolidLine.js +3 -0
- package/esm/chart/utils/axis.js +32 -4
- package/esm/chart/utils/bar.js +129 -76
- package/esm/chart/utils/chart.js +53 -21
- package/esm/chart/utils/gradient.js +15 -5
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { memo, useMemo } from 'react';
|
|
2
2
|
import { useTheme } from '@coinbase/cds-mobile/hooks/useTheme';
|
|
3
3
|
import { useCartesianChartContext } from '../ChartProvider';
|
|
4
|
-
import { EPSILON, getBars,
|
|
4
|
+
import { EPSILON, getBars, getBaselinePx, getStackOrigin } from '../utils/bar';
|
|
5
5
|
import { getGradientStops } from '../utils/gradient';
|
|
6
6
|
import { convertToSerializableScale } from '../utils/scale';
|
|
7
7
|
import { Bar } from './Bar';
|
|
@@ -48,7 +48,11 @@ export const BarStack = /*#__PURE__*/memo(_ref => {
|
|
|
48
48
|
} = useCartesianChartContext();
|
|
49
49
|
const xAxis = getXAxis(xAxisId);
|
|
50
50
|
const yAxis = getYAxis(yAxisId);
|
|
51
|
-
const baseline = useMemo(() =>
|
|
51
|
+
const baseline = useMemo(() => {
|
|
52
|
+
var _ref2;
|
|
53
|
+
return (_ref2 = layout === 'vertical' ? yAxis : xAxis) == null ? void 0 : _ref2.baseline;
|
|
54
|
+
}, [layout, yAxis, xAxis]);
|
|
55
|
+
const baselinePx = useMemo(() => getBaselinePx(valueScale, rect, layout, baseline), [rect, valueScale, layout, baseline]);
|
|
52
56
|
const seriesGradients = useMemo(() => {
|
|
53
57
|
return series.map(s => {
|
|
54
58
|
if (!s.gradient) return;
|
|
@@ -87,6 +91,7 @@ export const BarStack = /*#__PURE__*/memo(_ref => {
|
|
|
87
91
|
roundBaseline,
|
|
88
92
|
layout,
|
|
89
93
|
baseline,
|
|
94
|
+
baselinePx,
|
|
90
95
|
stackGap,
|
|
91
96
|
barMinSize,
|
|
92
97
|
stackMinSize,
|
|
@@ -96,12 +101,12 @@ export const BarStack = /*#__PURE__*/memo(_ref => {
|
|
|
96
101
|
defaultStroke,
|
|
97
102
|
defaultStrokeWidth,
|
|
98
103
|
defaultBarComponent
|
|
99
|
-
}), [series, seriesData, indexPos, thickness,
|
|
104
|
+
}), [series, seriesData, categoryIndex, categoryValue, indexPos, thickness, valueScale, seriesGradients, roundBaseline, layout, baseline, baselinePx, stackGap, barMinSize, stackMinSize, theme.color.fgPrimary, borderRadius, defaultFillOpacity, defaultStroke, defaultStrokeWidth, defaultBarComponent]);
|
|
100
105
|
const stackRect = useMemo(() => {
|
|
101
106
|
if (bars.length === 0) {
|
|
102
107
|
return {
|
|
103
|
-
x: layout === 'vertical' ? indexPos :
|
|
104
|
-
y: layout === 'vertical' ?
|
|
108
|
+
x: layout === 'vertical' ? indexPos : baselinePx,
|
|
109
|
+
y: layout === 'vertical' ? baselinePx : indexPos,
|
|
105
110
|
width: layout === 'vertical' ? thickness : 0,
|
|
106
111
|
height: layout === 'vertical' ? 0 : thickness
|
|
107
112
|
};
|
|
@@ -116,14 +121,14 @@ export const BarStack = /*#__PURE__*/memo(_ref => {
|
|
|
116
121
|
width: maxX - minX,
|
|
117
122
|
height: maxY - minY
|
|
118
123
|
};
|
|
119
|
-
}, [bars,
|
|
124
|
+
}, [bars, baselinePx, indexPos, layout, thickness]);
|
|
120
125
|
const stackOrigin = useMemo(() => {
|
|
121
126
|
var _getStackOrigin;
|
|
122
127
|
return (_getStackOrigin = getStackOrigin(bars.map(b => b.origin), bars.map(b => {
|
|
123
128
|
var _b$minSize;
|
|
124
129
|
return (_b$minSize = b.minSize) != null ? _b$minSize : 0;
|
|
125
|
-
}))) != null ? _getStackOrigin :
|
|
126
|
-
}, [bars,
|
|
130
|
+
}))) != null ? _getStackOrigin : baselinePx;
|
|
131
|
+
}, [bars, baselinePx]);
|
|
127
132
|
const barElements = bars.map((bar, index) => /*#__PURE__*/_jsx(Bar, {
|
|
128
133
|
BarComponent: bar.BarComponent,
|
|
129
134
|
borderRadius: bar.borderRadius,
|
|
@@ -147,8 +152,8 @@ export const BarStack = /*#__PURE__*/memo(_ref => {
|
|
|
147
152
|
}, bar.seriesId + "-" + categoryIndex + "-" + index));
|
|
148
153
|
const edge = layout === 'vertical' ? stackRect.y : stackRect.x;
|
|
149
154
|
const size = layout === 'vertical' ? stackRect.height : stackRect.width;
|
|
150
|
-
const stackRoundLower = roundBaseline || Math.abs(edge -
|
|
151
|
-
const stackRoundHigher = roundBaseline || Math.abs(edge + size -
|
|
155
|
+
const stackRoundLower = roundBaseline || Math.abs(edge - baselinePx) >= EPSILON;
|
|
156
|
+
const stackRoundHigher = roundBaseline || Math.abs(edge + size - baselinePx) >= EPSILON;
|
|
152
157
|
const stackRoundTop = layout === 'vertical' ? stackRoundLower : stackRoundHigher;
|
|
153
158
|
const stackRoundBottom = layout === 'vertical' ? stackRoundHigher : stackRoundLower;
|
|
154
159
|
return /*#__PURE__*/_jsx(BarStackComponent, {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { memo, useMemo } from 'react';
|
|
2
|
-
import { Group } from '@shopify/react-native-skia';
|
|
2
|
+
import { Group, Skia } from '@shopify/react-native-skia';
|
|
3
3
|
import { useCartesianChartContext } from '../ChartProvider';
|
|
4
4
|
import { getBarPath } from '../utils';
|
|
5
5
|
import { defaultBarEnterTransition, getNormalizedStagger, getStackInitialClipRect, withStaggerDelayTransition } from '../utils/bar';
|
|
@@ -28,7 +28,8 @@ export const DefaultBarStack = /*#__PURE__*/memo(_ref => {
|
|
|
28
28
|
layout
|
|
29
29
|
} = useCartesianChartContext();
|
|
30
30
|
const normalizedStagger = useMemo(() => getNormalizedStagger(layout, x, y, drawingArea), [layout, x, y, drawingArea]);
|
|
31
|
-
const enterTransition = useMemo(() =>
|
|
31
|
+
const enterTransition = useMemo(() => getTransition(transitions == null ? void 0 : transitions.enter, animate, defaultBarEnterTransition), [transitions == null ? void 0 : transitions.enter, animate]);
|
|
32
|
+
const enterTransitionWithStagger = useMemo(() => withStaggerDelayTransition(enterTransition, normalizedStagger), [enterTransition, normalizedStagger]);
|
|
32
33
|
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]);
|
|
33
34
|
|
|
34
35
|
// Generate target clip path (full bar)
|
|
@@ -51,11 +52,15 @@ export const DefaultBarStack = /*#__PURE__*/memo(_ref => {
|
|
|
51
52
|
currentPath: targetPath,
|
|
52
53
|
initialPath,
|
|
53
54
|
transitions: {
|
|
54
|
-
enter:
|
|
55
|
+
enter: enterTransitionWithStagger,
|
|
55
56
|
update: updateTransition
|
|
56
57
|
}
|
|
57
58
|
});
|
|
58
|
-
const
|
|
59
|
+
const staticClipPath = useMemo(() => {
|
|
60
|
+
var _Skia$Path$MakeFromSV;
|
|
61
|
+
return (_Skia$Path$MakeFromSV = Skia.Path.MakeFromSVGString(targetPath)) != null ? _Skia$Path$MakeFromSV : Skia.Path.Make();
|
|
62
|
+
}, [targetPath]);
|
|
63
|
+
const clipPath = animate ? animatedClipPath : staticClipPath;
|
|
59
64
|
return /*#__PURE__*/_jsx(Group, {
|
|
60
65
|
clip: clipPath,
|
|
61
66
|
children: children
|
|
@@ -31,6 +31,7 @@ const ThinSolidLine = /*#__PURE__*/memo(props => /*#__PURE__*/_jsx(SolidLine, _e
|
|
|
31
31
|
strokeWidth: 1
|
|
32
32
|
})));
|
|
33
33
|
const defaultChartHeight = 250;
|
|
34
|
+
const baselineThresholdData = [40, 28, 21, 5, 48, 5, 28, 2, 29, 48, 18, 30, 29, 8].map(value => value + 50);
|
|
34
35
|
const PositiveAndNegativeCashFlow = () => {
|
|
35
36
|
const theme = useTheme();
|
|
36
37
|
const categories = Array.from({
|
|
@@ -665,7 +666,7 @@ const BandGridPositionExample = _ref6 => {
|
|
|
665
666
|
|
|
666
667
|
// --- Composed Examples ---
|
|
667
668
|
|
|
668
|
-
const candlestickStockData = btcCandles.slice(0, 90)
|
|
669
|
+
const candlestickStockData = [...btcCandles].reverse().slice(0, 90);
|
|
669
670
|
const CandlesticksHeader = /*#__PURE__*/memo(_ref7 => {
|
|
670
671
|
let {
|
|
671
672
|
currentIndex
|
|
@@ -1004,7 +1005,7 @@ const SunlightChart = () => {
|
|
|
1004
1005
|
});
|
|
1005
1006
|
};
|
|
1006
1007
|
const PriceRange = () => {
|
|
1007
|
-
const candles = btcCandles.slice(0, 180)
|
|
1008
|
+
const candles = [...btcCandles].reverse().slice(0, 180);
|
|
1008
1009
|
const data = useMemo(() => candles.map(candle => [parseFloat(candle.low), parseFloat(candle.high)]), [candles]);
|
|
1009
1010
|
const min = useMemo(() => Math.min(...data.map(_ref13 => {
|
|
1010
1011
|
let [low] = _ref13;
|
|
@@ -1064,6 +1065,81 @@ const HorizontalBarChart = () => {
|
|
|
1064
1065
|
}
|
|
1065
1066
|
});
|
|
1066
1067
|
};
|
|
1068
|
+
const AxisBaselineExample = () => {
|
|
1069
|
+
return /*#__PURE__*/_jsx(BarChart, {
|
|
1070
|
+
showXAxis: true,
|
|
1071
|
+
showYAxis: true,
|
|
1072
|
+
accessibilityLabel: "Bar chart with custom axis baseline at 100.",
|
|
1073
|
+
height: defaultChartHeight,
|
|
1074
|
+
series: [{
|
|
1075
|
+
id: 'net-flow',
|
|
1076
|
+
data: [112, 97, 121, 103, 129, 118, 94]
|
|
1077
|
+
}],
|
|
1078
|
+
xAxis: {
|
|
1079
|
+
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
|
|
1080
|
+
},
|
|
1081
|
+
yAxis: {
|
|
1082
|
+
baseline: 100,
|
|
1083
|
+
domain: {
|
|
1084
|
+
min: 80,
|
|
1085
|
+
max: 140
|
|
1086
|
+
},
|
|
1087
|
+
showGrid: true
|
|
1088
|
+
}
|
|
1089
|
+
});
|
|
1090
|
+
};
|
|
1091
|
+
const AxisBaselineThresholdExample = () => {
|
|
1092
|
+
const theme = useTheme();
|
|
1093
|
+
return /*#__PURE__*/_jsxs(VStack, {
|
|
1094
|
+
gap: 2,
|
|
1095
|
+
children: [/*#__PURE__*/_jsx(BarChart, {
|
|
1096
|
+
showYAxis: true,
|
|
1097
|
+
accessibilityLabel: "Bar chart with threshold baseline at 30.",
|
|
1098
|
+
height: 220,
|
|
1099
|
+
inset: 0,
|
|
1100
|
+
series: [{
|
|
1101
|
+
id: 'axis-baseline-threshold-vertical',
|
|
1102
|
+
data: baselineThresholdData,
|
|
1103
|
+
gradient: {
|
|
1104
|
+
stops: [{
|
|
1105
|
+
offset: 30,
|
|
1106
|
+
color: theme.color.fgNegative
|
|
1107
|
+
}, {
|
|
1108
|
+
offset: 30,
|
|
1109
|
+
color: theme.color.fgPositive
|
|
1110
|
+
}]
|
|
1111
|
+
}
|
|
1112
|
+
}],
|
|
1113
|
+
yAxis: {
|
|
1114
|
+
showGrid: true,
|
|
1115
|
+
baseline: 30
|
|
1116
|
+
}
|
|
1117
|
+
}), /*#__PURE__*/_jsx(BarChart, {
|
|
1118
|
+
showXAxis: true,
|
|
1119
|
+
accessibilityLabel: "Horizontal bar chart with threshold baseline at 30.",
|
|
1120
|
+
height: 220,
|
|
1121
|
+
inset: 0,
|
|
1122
|
+
layout: "horizontal",
|
|
1123
|
+
series: [{
|
|
1124
|
+
id: 'axis-baseline-threshold-horizontal',
|
|
1125
|
+
data: baselineThresholdData,
|
|
1126
|
+
gradient: {
|
|
1127
|
+
stops: [{
|
|
1128
|
+
offset: 30,
|
|
1129
|
+
color: theme.color.fgNegative
|
|
1130
|
+
}, {
|
|
1131
|
+
offset: 30,
|
|
1132
|
+
color: theme.color.fgPositive
|
|
1133
|
+
}]
|
|
1134
|
+
}
|
|
1135
|
+
}],
|
|
1136
|
+
xAxis: {
|
|
1137
|
+
showGrid: true,
|
|
1138
|
+
baseline: 30
|
|
1139
|
+
}
|
|
1140
|
+
})]
|
|
1141
|
+
});
|
|
1142
|
+
};
|
|
1067
1143
|
function BuyVsSellExample() {
|
|
1068
1144
|
function BuyVsSellLegend(_ref15) {
|
|
1069
1145
|
let {
|
|
@@ -1224,6 +1300,12 @@ function ExampleNavigator() {
|
|
|
1224
1300
|
}, {
|
|
1225
1301
|
title: 'Negative Values with Top Axis',
|
|
1226
1302
|
component: /*#__PURE__*/_jsx(NegativeValuesWithTopAxis, {})
|
|
1303
|
+
}, {
|
|
1304
|
+
title: 'Axis Baseline',
|
|
1305
|
+
component: /*#__PURE__*/_jsx(AxisBaselineExample, {})
|
|
1306
|
+
}, {
|
|
1307
|
+
title: 'Axis Baseline Threshold',
|
|
1308
|
+
component: /*#__PURE__*/_jsx(AxisBaselineThresholdExample, {})
|
|
1227
1309
|
}, {
|
|
1228
1310
|
title: 'Positive and Negative Cash Flow',
|
|
1229
1311
|
component: /*#__PURE__*/_jsx(PositiveAndNegativeCashFlow, {})
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { memo, useMemo } from 'react';
|
|
2
|
-
import {
|
|
1
|
+
import { memo, useEffect, useMemo, useRef } from 'react';
|
|
2
|
+
import { useDerivedValue, useSharedValue } from 'react-native-reanimated';
|
|
3
|
+
import { LinearGradient } from '@shopify/react-native-skia';
|
|
3
4
|
import { useCartesianChartContext } from '../ChartProvider';
|
|
4
|
-
import {
|
|
5
|
+
import { buildTransition, defaultTransition, instantTransition } from '../utils';
|
|
6
|
+
import { getColorWithOpacity, getGradientAxis, getGradientConfig } from '../utils/gradient';
|
|
5
7
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
6
8
|
/**
|
|
7
9
|
* Renders a Skia LinearGradient element based on a GradientDefinition.
|
|
@@ -13,38 +15,129 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
13
15
|
* </Path>
|
|
14
16
|
*/
|
|
15
17
|
export const Gradient = /*#__PURE__*/memo(_ref => {
|
|
16
|
-
var
|
|
18
|
+
var _scale$range;
|
|
17
19
|
let {
|
|
18
20
|
gradient,
|
|
19
21
|
xAxisId,
|
|
20
|
-
yAxisId
|
|
22
|
+
yAxisId,
|
|
23
|
+
animate: animateProp,
|
|
24
|
+
transition: transitionProp
|
|
21
25
|
} = _ref;
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
26
|
+
const {
|
|
27
|
+
animate: animateContext,
|
|
28
|
+
getXScale,
|
|
29
|
+
getYScale,
|
|
30
|
+
drawingArea,
|
|
31
|
+
layout
|
|
32
|
+
} = useCartesianChartContext();
|
|
33
|
+
const animate = animateProp != null ? animateProp : animateContext;
|
|
34
|
+
const transition = useMemo(() => {
|
|
35
|
+
if (!animate) return instantTransition;
|
|
36
|
+
return transitionProp != null ? transitionProp : defaultTransition;
|
|
37
|
+
}, [transitionProp, animate]);
|
|
38
|
+
const xScale = getXScale(xAxisId);
|
|
39
|
+
const yScale = getYScale(yAxisId);
|
|
27
40
|
|
|
28
41
|
// Process gradient definition into stops
|
|
29
42
|
const stops = useMemo(() => {
|
|
30
43
|
if (!xScale || !yScale) return;
|
|
31
|
-
return getGradientConfig(gradient, xScale, yScale);
|
|
32
|
-
}, [gradient, xScale, yScale]);
|
|
33
|
-
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
44
|
+
return getGradientConfig(gradient, xScale, yScale, layout);
|
|
45
|
+
}, [gradient, xScale, yScale, layout]);
|
|
46
|
+
const axis = getGradientAxis(gradient, layout);
|
|
47
|
+
const scale = axis === 'x' ? xScale : yScale;
|
|
48
|
+
const shouldRender = !!stops && !!scale;
|
|
49
|
+
const range = (_scale$range = scale == null ? void 0 : scale.range()) != null ? _scale$range : [0, 0];
|
|
50
|
+
const [rangeStart = 0, rangeEnd = 0] = range;
|
|
51
|
+
const targetStart = axis === 'x' ? {
|
|
52
|
+
x: rangeStart,
|
|
53
|
+
y: drawingArea.y
|
|
54
|
+
} : {
|
|
55
|
+
x: drawingArea.x,
|
|
56
|
+
y: rangeStart
|
|
57
|
+
};
|
|
58
|
+
const targetEnd = axis === 'x' ? {
|
|
59
|
+
x: rangeEnd,
|
|
60
|
+
y: drawingArea.y
|
|
61
|
+
} : {
|
|
62
|
+
x: drawingArea.x,
|
|
63
|
+
y: rangeEnd
|
|
64
|
+
};
|
|
41
65
|
|
|
42
|
-
// Extract colors and positions for LinearGradient
|
|
43
|
-
const colors = stops.map(
|
|
44
|
-
var
|
|
45
|
-
return getColorWithOpacity(
|
|
46
|
-
});
|
|
47
|
-
const
|
|
66
|
+
// Extract colors and positions for LinearGradient.
|
|
67
|
+
const colors = useMemo(() => (stops != null ? stops : []).map(stop => {
|
|
68
|
+
var _stop$opacity;
|
|
69
|
+
return getColorWithOpacity(stop.color, (_stop$opacity = stop.opacity) != null ? _stop$opacity : 1);
|
|
70
|
+
}), [stops]);
|
|
71
|
+
const targetPositions = useMemo(() => (stops != null ? stops : []).map(stop => stop.offset), [stops]);
|
|
72
|
+
const startX = useSharedValue(targetStart.x);
|
|
73
|
+
const startY = useSharedValue(targetStart.y);
|
|
74
|
+
const endX = useSharedValue(targetEnd.x);
|
|
75
|
+
const endY = useSharedValue(targetEnd.y);
|
|
76
|
+
const fromPositions = useSharedValue(targetPositions);
|
|
77
|
+
const toPositions = useSharedValue(targetPositions);
|
|
78
|
+
const positionsProgress = useSharedValue(1);
|
|
79
|
+
const hasRendered = useRef(false);
|
|
80
|
+
useEffect(() => {
|
|
81
|
+
if (!shouldRender) {
|
|
82
|
+
hasRendered.current = false;
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
if (!hasRendered.current) {
|
|
86
|
+
hasRendered.current = true;
|
|
87
|
+
startX.value = targetStart.x;
|
|
88
|
+
startY.value = targetStart.y;
|
|
89
|
+
endX.value = targetEnd.x;
|
|
90
|
+
endY.value = targetEnd.y;
|
|
91
|
+
fromPositions.value = [...targetPositions];
|
|
92
|
+
toPositions.value = [...targetPositions];
|
|
93
|
+
positionsProgress.value = 1;
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
startX.value = buildTransition(targetStart.x, transition);
|
|
97
|
+
startY.value = buildTransition(targetStart.y, transition);
|
|
98
|
+
endX.value = buildTransition(targetEnd.x, transition);
|
|
99
|
+
endY.value = buildTransition(targetEnd.y, transition);
|
|
100
|
+
const canAnimatePositions = toPositions.value.length === targetPositions.length;
|
|
101
|
+
if (canAnimatePositions) {
|
|
102
|
+
fromPositions.value = [...toPositions.value];
|
|
103
|
+
toPositions.value = [...targetPositions];
|
|
104
|
+
positionsProgress.value = 0;
|
|
105
|
+
positionsProgress.value = buildTransition(1, transition);
|
|
106
|
+
} else {
|
|
107
|
+
fromPositions.value = [...targetPositions];
|
|
108
|
+
toPositions.value = [...targetPositions];
|
|
109
|
+
positionsProgress.value = 1;
|
|
110
|
+
}
|
|
111
|
+
}, [transition, targetStart.x, targetStart.y, targetEnd.x, targetEnd.y, targetPositions, startX, startY, endX, endY, fromPositions, toPositions, positionsProgress, shouldRender]);
|
|
112
|
+
const start = useDerivedValue(() => {
|
|
113
|
+
return {
|
|
114
|
+
x: startX.value,
|
|
115
|
+
y: startY.value
|
|
116
|
+
};
|
|
117
|
+
}, [startX, startY]);
|
|
118
|
+
const end = useDerivedValue(() => {
|
|
119
|
+
return {
|
|
120
|
+
x: endX.value,
|
|
121
|
+
y: endY.value
|
|
122
|
+
};
|
|
123
|
+
}, [endX, endY]);
|
|
124
|
+
const positions = useDerivedValue(() => {
|
|
125
|
+
const from = fromPositions.value;
|
|
126
|
+
const to = toPositions.value;
|
|
127
|
+
const progress = positionsProgress.value;
|
|
128
|
+
if (to.length === 0) return [];
|
|
129
|
+
const count = Math.max(from.length, to.length);
|
|
130
|
+
const interpolated = Array.from({
|
|
131
|
+
length: count
|
|
132
|
+
}, (_, index) => {
|
|
133
|
+
var _from$Math$min, _to$Math$min;
|
|
134
|
+
const fromValue = (_from$Math$min = from[Math.min(index, from.length - 1)]) != null ? _from$Math$min : 0;
|
|
135
|
+
const toValue = (_to$Math$min = to[Math.min(index, to.length - 1)]) != null ? _to$Math$min : fromValue;
|
|
136
|
+
return fromValue + (toValue - fromValue) * progress;
|
|
137
|
+
});
|
|
138
|
+
return interpolated;
|
|
139
|
+
}, [fromPositions, toPositions, positionsProgress]);
|
|
140
|
+
if (!shouldRender) return null;
|
|
48
141
|
return /*#__PURE__*/_jsx(LinearGradient, {
|
|
49
142
|
colors: colors,
|
|
50
143
|
end: end,
|
|
@@ -12,6 +12,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
12
12
|
* Supports gradient for gradient effects on the dots and smooth data transitions via AnimatedPath.
|
|
13
13
|
*/
|
|
14
14
|
export const DottedLine = /*#__PURE__*/memo(_ref => {
|
|
15
|
+
var _transitions$update;
|
|
15
16
|
let {
|
|
16
17
|
fill = 'none',
|
|
17
18
|
stroke,
|
|
@@ -46,7 +47,9 @@ export const DottedLine = /*#__PURE__*/memo(_ref => {
|
|
|
46
47
|
children: [/*#__PURE__*/_jsx(DashPathEffect, {
|
|
47
48
|
intervals: dashIntervals
|
|
48
49
|
}), gradient && /*#__PURE__*/_jsx(Gradient, {
|
|
50
|
+
animate: animate,
|
|
49
51
|
gradient: gradient,
|
|
52
|
+
transition: (_transitions$update = transitions == null ? void 0 : transitions.update) != null ? _transitions$update : transition,
|
|
50
53
|
xAxisId: xAxisId,
|
|
51
54
|
yAxisId: yAxisId
|
|
52
55
|
})]
|
package/esm/chart/line/Line.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const _excluded = ["seriesId", "curve", "type", "areaType", "
|
|
1
|
+
const _excluded = ["seriesId", "curve", "type", "areaType", "stroke", "strokeOpacity", "showArea", "LineComponent", "AreaComponent", "opacity", "points", "connectNulls", "transitions", "transition", "gradient"];
|
|
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 React, { memo, useMemo } from 'react';
|
|
@@ -20,7 +20,6 @@ export const Line = /*#__PURE__*/memo(_ref => {
|
|
|
20
20
|
curve = 'bump',
|
|
21
21
|
type = 'solid',
|
|
22
22
|
areaType = 'gradient',
|
|
23
|
-
areaBaseline,
|
|
24
23
|
stroke: strokeProp,
|
|
25
24
|
strokeOpacity,
|
|
26
25
|
showArea,
|
|
@@ -114,7 +113,6 @@ export const Line = /*#__PURE__*/memo(_ref => {
|
|
|
114
113
|
return /*#__PURE__*/_jsxs(_Fragment, {
|
|
115
114
|
children: [showArea && /*#__PURE__*/_jsx(Area, {
|
|
116
115
|
AreaComponent: AreaComponent,
|
|
117
|
-
baseline: areaBaseline,
|
|
118
116
|
connectNulls: connectNulls,
|
|
119
117
|
curve: curve,
|
|
120
118
|
fill: stroke,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const _excluded = ["series", "showArea", "areaType", "type", "LineComponent", "AreaComponent", "curve", "points", "strokeWidth", "strokeOpacity", "connectNulls", "transition", "transitions", "opacity", "showXAxis", "showYAxis", "xAxis", "yAxis", "inset", "scrubberAccessibilityLabelStep", "layout", "children"],
|
|
2
|
-
_excluded2 = ["scaleType", "data", "categoryPadding", "domain", "domainLimit", "range", "id"],
|
|
3
|
-
_excluded3 = ["scaleType", "data", "categoryPadding", "domain", "domainLimit", "range", "id"],
|
|
2
|
+
_excluded2 = ["scaleType", "data", "categoryPadding", "domain", "domainLimit", "range", "baseline", "id"],
|
|
3
|
+
_excluded3 = ["scaleType", "data", "categoryPadding", "domain", "domainLimit", "range", "baseline", "id"],
|
|
4
4
|
_excluded4 = ["id", "data", "label", "color", "xAxisId", "yAxisId"];
|
|
5
5
|
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); }
|
|
6
6
|
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; }
|
|
@@ -67,6 +67,7 @@ export const LineChart = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, ref) =
|
|
|
67
67
|
domain: xDomain,
|
|
68
68
|
domainLimit: xDomainLimit,
|
|
69
69
|
range: xRange,
|
|
70
|
+
baseline: xBaseline,
|
|
70
71
|
id: xAxisId
|
|
71
72
|
} = _ref2,
|
|
72
73
|
xAxisVisualProps = _objectWithoutPropertiesLoose(_ref2, _excluded2);
|
|
@@ -78,6 +79,7 @@ export const LineChart = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, ref) =
|
|
|
78
79
|
domain: yDomain,
|
|
79
80
|
domainLimit: yDomainLimit,
|
|
80
81
|
range: yRange,
|
|
82
|
+
baseline: yBaseline,
|
|
81
83
|
id: yAxisId
|
|
82
84
|
} = _ref3,
|
|
83
85
|
yAxisVisualProps = _objectWithoutPropertiesLoose(_ref3, _excluded3);
|
|
@@ -87,7 +89,8 @@ export const LineChart = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, ref) =
|
|
|
87
89
|
categoryPadding: xCategoryPadding,
|
|
88
90
|
domain: xDomain,
|
|
89
91
|
domainLimit: xDomainLimit,
|
|
90
|
-
range: xRange
|
|
92
|
+
range: xRange,
|
|
93
|
+
baseline: xBaseline
|
|
91
94
|
};
|
|
92
95
|
const yAxisConfig = {
|
|
93
96
|
scaleType: yScaleType,
|
|
@@ -95,7 +98,8 @@ export const LineChart = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, ref) =
|
|
|
95
98
|
categoryPadding: yCategoryPadding,
|
|
96
99
|
domain: yDomain,
|
|
97
100
|
domainLimit: yDomainLimit,
|
|
98
|
-
range: yRange
|
|
101
|
+
range: yRange,
|
|
102
|
+
baseline: yBaseline
|
|
99
103
|
};
|
|
100
104
|
const categoryAxisData = layout === 'horizontal' ? yData : xData;
|
|
101
105
|
const lineChartDataLength = useMemo(() => {
|
|
@@ -11,6 +11,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
11
11
|
* Supports gradient for gradient effects and smooth data transitions via AnimatedPath.
|
|
12
12
|
*/
|
|
13
13
|
export const SolidLine = /*#__PURE__*/memo(_ref => {
|
|
14
|
+
var _transitions$update;
|
|
14
15
|
let {
|
|
15
16
|
fill = 'none',
|
|
16
17
|
stroke,
|
|
@@ -42,7 +43,9 @@ export const SolidLine = /*#__PURE__*/memo(_ref => {
|
|
|
42
43
|
transitions: transitions
|
|
43
44
|
}, props, {
|
|
44
45
|
children: gradient && /*#__PURE__*/_jsx(Gradient, {
|
|
46
|
+
animate: animate,
|
|
45
47
|
gradient: gradient,
|
|
48
|
+
transition: (_transitions$update = transitions == null ? void 0 : transitions.update) != null ? _transitions$update : transition,
|
|
46
49
|
xAxisId: xAxisId,
|
|
47
50
|
yAxisId: yAxisId
|
|
48
51
|
})
|
package/esm/chart/utils/axis.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const _excluded = ["id"];
|
|
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
2
|
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; }
|
|
3
|
+
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); }
|
|
4
4
|
import { useCallback, useMemo, useState } from 'react';
|
|
5
5
|
import { getChartDomain, getChartRange, isValidBounds } from './chart';
|
|
6
6
|
import { getPointOnScale } from './point';
|
|
@@ -46,6 +46,36 @@ export const toPointAnchor = placement => {
|
|
|
46
46
|
* Axis configuration without computed bounds (used for input)
|
|
47
47
|
*/
|
|
48
48
|
|
|
49
|
+
const includeBaselineInBounds = (bounds, baseline) => {
|
|
50
|
+
if (baseline < bounds.min) return _extends({}, bounds, {
|
|
51
|
+
min: baseline
|
|
52
|
+
});
|
|
53
|
+
if (baseline > bounds.max) return _extends({}, bounds, {
|
|
54
|
+
max: baseline
|
|
55
|
+
});
|
|
56
|
+
return bounds;
|
|
57
|
+
};
|
|
58
|
+
export const withBaselineDomain = function (domain, baseline) {
|
|
59
|
+
if (baseline === void 0) {
|
|
60
|
+
baseline = 0;
|
|
61
|
+
}
|
|
62
|
+
if (typeof domain === 'function') return domain;
|
|
63
|
+
if ((domain == null ? void 0 : domain.min) !== undefined && (domain == null ? void 0 : domain.max) !== undefined) return domain;
|
|
64
|
+
const hasExplicitMin = (domain == null ? void 0 : domain.min) !== undefined;
|
|
65
|
+
const hasExplicitMax = (domain == null ? void 0 : domain.max) !== undefined;
|
|
66
|
+
return bounds => {
|
|
67
|
+
const resolvedBounds = {
|
|
68
|
+
min: hasExplicitMin ? domain == null ? void 0 : domain.min : bounds.min,
|
|
69
|
+
max: hasExplicitMax ? domain == null ? void 0 : domain.max : bounds.max
|
|
70
|
+
};
|
|
71
|
+
const baselineAdjustedBounds = includeBaselineInBounds(resolvedBounds, baseline);
|
|
72
|
+
return {
|
|
73
|
+
min: hasExplicitMin ? resolvedBounds.min : baselineAdjustedBounds.min,
|
|
74
|
+
max: hasExplicitMax ? resolvedBounds.max : baselineAdjustedBounds.max
|
|
75
|
+
};
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
|
|
49
79
|
/**
|
|
50
80
|
* Gets a D3 scale based on the cartesian axis configuration.
|
|
51
81
|
* Handles both numeric (linear/log) and categorical (band) scales.
|
|
@@ -221,7 +251,7 @@ export const getCartesianAxisDomain = function (axisParam, series, axisType, lay
|
|
|
221
251
|
// In vertical layout: X is category (index), Y is value (value)
|
|
222
252
|
// In horizontal layout: Y is category (index), X is value (value)
|
|
223
253
|
const isCategoryAxis = layout !== 'horizontal' && axisType === 'x' || layout === 'horizontal' && axisType === 'y';
|
|
224
|
-
const seriesDomain = isCategoryAxis ? getChartDomain(series) : getChartRange(series);
|
|
254
|
+
const seriesDomain = isCategoryAxis ? getChartDomain(series) : getChartRange(series, layout, axisType === 'x' ? [axisParam] : [], axisType === 'y' ? [axisParam] : []);
|
|
225
255
|
|
|
226
256
|
// If data sets the domain, use that instead of the series domain
|
|
227
257
|
const preferredDataDomain = dataDomain != null ? dataDomain : seriesDomain;
|
|
@@ -246,8 +276,6 @@ export const getCartesianAxisDomain = function (axisParam, series, axisType, lay
|
|
|
246
276
|
// Use the base domain as-is
|
|
247
277
|
finalDomain = preferredDataDomain;
|
|
248
278
|
}
|
|
249
|
-
|
|
250
|
-
// Ensure we always return valid bounds with no undefined values
|
|
251
279
|
return {
|
|
252
280
|
min: (_finalDomain$min = finalDomain.min) != null ? _finalDomain$min : 0,
|
|
253
281
|
max: (_finalDomain$max = finalDomain.max) != null ? _finalDomain$max : 0
|