@coinbase/cds-mobile-visualization 3.4.0-beta.9 → 3.4.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 +132 -0
- package/dts/chart/CartesianChart.d.ts +92 -7
- package/dts/chart/CartesianChart.d.ts.map +1 -1
- package/dts/chart/ChartContextBridge.d.ts.map +1 -1
- package/dts/chart/ChartProvider.d.ts +3 -0
- package/dts/chart/ChartProvider.d.ts.map +1 -1
- package/dts/chart/Path.d.ts +36 -13
- package/dts/chart/Path.d.ts.map +1 -1
- package/dts/chart/PeriodSelector.d.ts +20 -5
- package/dts/chart/PeriodSelector.d.ts.map +1 -1
- package/dts/chart/area/Area.d.ts +14 -11
- 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 +22 -42
- 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 +51 -51
- package/dts/chart/bar/Bar.d.ts.map +1 -1
- package/dts/chart/bar/BarChart.d.ts +56 -11
- package/dts/chart/bar/BarChart.d.ts.map +1 -1
- package/dts/chart/bar/BarPlot.d.ts +2 -1
- package/dts/chart/bar/BarPlot.d.ts.map +1 -1
- package/dts/chart/bar/BarStack.d.ts +45 -20
- package/dts/chart/bar/BarStack.d.ts.map +1 -1
- package/dts/chart/bar/BarStackGroup.d.ts +2 -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/index.d.ts +1 -0
- package/dts/chart/index.d.ts.map +1 -1
- package/dts/chart/legend/DefaultLegendEntry.d.ts +5 -0
- package/dts/chart/legend/DefaultLegendEntry.d.ts.map +1 -0
- package/dts/chart/legend/DefaultLegendShape.d.ts +5 -0
- package/dts/chart/legend/DefaultLegendShape.d.ts.map +1 -0
- package/dts/chart/legend/Legend.d.ts +168 -0
- package/dts/chart/legend/Legend.d.ts.map +1 -0
- package/dts/chart/legend/index.d.ts +4 -0
- package/dts/chart/legend/index.d.ts.map +1 -0
- package/dts/chart/line/DottedLine.d.ts.map +1 -1
- package/dts/chart/line/Line.d.ts +23 -19
- package/dts/chart/line/Line.d.ts.map +1 -1
- package/dts/chart/line/LineChart.d.ts +26 -9
- 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 +26 -2
- package/dts/chart/point/Point.d.ts.map +1 -1
- package/dts/chart/scrubber/DefaultScrubberBeacon.d.ts +32 -2
- 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 +86 -17
- package/dts/chart/scrubber/Scrubber.d.ts.map +1 -1
- package/dts/chart/scrubber/ScrubberAccessibilityView.d.ts +12 -0
- package/dts/chart/scrubber/ScrubberAccessibilityView.d.ts.map +1 -0
- package/dts/chart/scrubber/ScrubberBeaconGroup.d.ts +10 -0
- package/dts/chart/scrubber/ScrubberBeaconGroup.d.ts.map +1 -1
- package/dts/chart/scrubber/ScrubberBeaconLabelGroup.d.ts +16 -1
- package/dts/chart/scrubber/ScrubberBeaconLabelGroup.d.ts.map +1 -1
- package/dts/chart/scrubber/ScrubberProvider.d.ts.map +1 -1
- package/dts/chart/utils/axis.d.ts +45 -10
- package/dts/chart/utils/axis.d.ts.map +1 -1
- package/dts/chart/utils/bar.d.ts +190 -0
- package/dts/chart/utils/bar.d.ts.map +1 -1
- package/dts/chart/utils/chart.d.ts +32 -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 +26 -0
- package/dts/chart/utils/path.d.ts.map +1 -1
- package/dts/chart/utils/point.d.ts +24 -12
- package/dts/chart/utils/point.d.ts.map +1 -1
- package/dts/chart/utils/scale.d.ts +11 -0
- package/dts/chart/utils/scale.d.ts.map +1 -1
- package/dts/chart/utils/scrubber.d.ts +2 -1
- package/dts/chart/utils/scrubber.d.ts.map +1 -1
- package/dts/chart/utils/transition.d.ts +63 -22
- package/dts/chart/utils/transition.d.ts.map +1 -1
- package/dts/sparkline/Sparkline.d.ts +2 -1
- package/dts/sparkline/Sparkline.d.ts.map +1 -1
- package/dts/sparkline/SparklineArea.d.ts +2 -1
- package/dts/sparkline/SparklineArea.d.ts.map +1 -1
- package/dts/sparkline/SparklineGradient.d.ts +2 -1
- package/dts/sparkline/SparklineGradient.d.ts.map +1 -1
- package/dts/sparkline/sparkline-interactive/SparklineInteractive.d.ts +2 -1
- package/dts/sparkline/sparkline-interactive/SparklineInteractive.d.ts.map +1 -1
- package/esm/chart/CartesianChart.js +176 -82
- package/esm/chart/ChartContextBridge.js +14 -3
- package/esm/chart/ChartProvider.js +2 -2
- package/esm/chart/Path.js +34 -29
- package/esm/chart/PeriodSelector.js +5 -1
- package/esm/chart/__stories__/CartesianChart.stories.js +16 -80
- package/esm/chart/__stories__/ChartAccessibility.stories.js +721 -0
- package/esm/chart/__stories__/ChartTransitions.stories.js +625 -0
- package/esm/chart/__stories__/PeriodSelector.stories.js +99 -1
- package/esm/chart/area/Area.js +21 -9
- package/esm/chart/area/AreaChart.js +18 -13
- package/esm/chart/area/DottedArea.js +28 -18
- package/esm/chart/area/GradientArea.js +14 -7
- package/esm/chart/area/SolidArea.js +6 -2
- package/esm/chart/area/__stories__/AreaChart.stories.js +47 -5
- package/esm/chart/axis/Axis.js +5 -41
- package/esm/chart/axis/XAxis.js +116 -47
- package/esm/chart/axis/YAxis.js +105 -26
- package/esm/chart/axis/__stories__/Axis.stories.js +324 -48
- package/esm/chart/bar/Bar.js +17 -15
- package/esm/chart/bar/BarChart.js +38 -33
- package/esm/chart/bar/BarPlot.js +40 -45
- package/esm/chart/bar/BarStack.js +92 -475
- package/esm/chart/bar/BarStackGroup.js +37 -27
- package/esm/chart/bar/DefaultBar.js +27 -18
- package/esm/chart/bar/DefaultBarStack.js +25 -9
- package/esm/chart/bar/__stories__/BarChart.stories.js +728 -54
- package/esm/chart/gradient/Gradient.js +2 -1
- package/esm/chart/index.js +1 -0
- package/esm/chart/legend/DefaultLegendEntry.js +42 -0
- package/esm/chart/legend/DefaultLegendShape.js +64 -0
- package/esm/chart/legend/Legend.js +59 -0
- package/esm/chart/legend/__stories__/Legend.stories.js +574 -0
- package/esm/chart/legend/index.js +3 -0
- package/esm/chart/line/DottedLine.js +6 -2
- package/esm/chart/line/Line.js +42 -38
- package/esm/chart/line/LineChart.js +36 -12
- package/esm/chart/line/SolidLine.js +6 -2
- package/esm/chart/line/__stories__/LineChart.stories.js +236 -590
- package/esm/chart/line/__stories__/ReferenceLine.stories.js +95 -1
- package/esm/chart/point/Point.js +35 -36
- package/esm/chart/scrubber/DefaultScrubberBeacon.js +41 -38
- package/esm/chart/scrubber/DefaultScrubberLabel.js +26 -10
- package/esm/chart/scrubber/Scrubber.js +67 -35
- package/esm/chart/scrubber/ScrubberAccessibilityView.js +177 -0
- package/esm/chart/scrubber/ScrubberBeaconGroup.js +30 -22
- package/esm/chart/scrubber/ScrubberBeaconLabelGroup.js +35 -8
- package/esm/chart/scrubber/ScrubberProvider.js +29 -24
- package/esm/chart/scrubber/__stories__/Scrubber.stories.js +946 -0
- package/esm/chart/utils/axis.js +88 -44
- package/esm/chart/utils/bar.js +820 -0
- package/esm/chart/utils/chart.js +34 -7
- package/esm/chart/utils/context.js +7 -0
- package/esm/chart/utils/gradient.js +8 -4
- package/esm/chart/utils/path.js +91 -61
- package/esm/chart/utils/point.js +92 -39
- package/esm/chart/utils/scale.js +13 -2
- package/esm/chart/utils/scrubber.js +12 -5
- package/esm/chart/utils/transition.js +108 -60
- package/esm/sparkline/Sparkline.js +2 -1
- package/esm/sparkline/SparklineArea.js +2 -1
- package/esm/sparkline/SparklineGradient.js +2 -1
- package/esm/sparkline/__figma__/Sparkline.figma.js +1 -1
- package/esm/sparkline/sparkline-interactive/SparklineInteractive.js +2 -1
- package/esm/sparkline/sparkline-interactive/__figma__/SparklineInteractive.figma.js +1 -1
- package/esm/sparkline/sparkline-interactive-header/__figma__/SparklineInteractiveHeader.figma.js +1 -1
- package/esm/sparkline/sparkline-interactive-header/__stories__/SparklineInteractiveHeader.stories.js +2 -0
- package/package.json +5 -6
- package/esm/chart/__stories__/Chart.stories.js +0 -77
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
const _excluded = ["accentColor", "yellowColor"];
|
|
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
|
-
import { memo, useCallback } from 'react';
|
|
4
|
+
import { memo, useCallback, useMemo } from 'react';
|
|
5
|
+
import { useDerivedValue, withTiming } from 'react-native-reanimated';
|
|
6
|
+
import { sparklineInteractiveData } from '@coinbase/cds-common/internal/visualizations/SparklineInteractiveData';
|
|
5
7
|
import { useTheme } from '@coinbase/cds-mobile';
|
|
6
8
|
import { Example, ExampleScreen } from '@coinbase/cds-mobile/examples/ExampleScreen';
|
|
9
|
+
import { useCartesianChartContext } from '../../ChartProvider';
|
|
10
|
+
import { Scrubber } from '../../scrubber';
|
|
11
|
+
import { getPointOnSerializableScale, useScrubberContext } from '../../utils';
|
|
7
12
|
import { DefaultReferenceLineLabel } from '../DefaultReferenceLineLabel';
|
|
8
13
|
import { DottedLine } from '../DottedLine';
|
|
9
14
|
import { LineChart } from '../LineChart';
|
|
@@ -126,7 +131,96 @@ const ReferenceLineStories = () => {
|
|
|
126
131
|
stroke: theme.color.bgWarning
|
|
127
132
|
})
|
|
128
133
|
})
|
|
134
|
+
}), /*#__PURE__*/_jsx(Example, {
|
|
135
|
+
title: "Start Price Reference Line",
|
|
136
|
+
children: /*#__PURE__*/_jsx(StartPriceReferenceLine, {})
|
|
129
137
|
})]
|
|
130
138
|
});
|
|
131
139
|
};
|
|
140
|
+
const FADE_ZONE = 128;
|
|
141
|
+
const StartPriceLabel = /*#__PURE__*/memo(props => {
|
|
142
|
+
const theme = useTheme();
|
|
143
|
+
const {
|
|
144
|
+
scrubberPosition
|
|
145
|
+
} = useScrubberContext();
|
|
146
|
+
const {
|
|
147
|
+
getXSerializableScale,
|
|
148
|
+
drawingArea
|
|
149
|
+
} = useCartesianChartContext();
|
|
150
|
+
const xScale = useMemo(() => getXSerializableScale(), [getXSerializableScale]);
|
|
151
|
+
const opacity = useDerivedValue(() => {
|
|
152
|
+
if (scrubberPosition.value === undefined) return withTiming(0, {
|
|
153
|
+
duration: 250
|
|
154
|
+
});
|
|
155
|
+
if (!xScale) return withTiming(1, {
|
|
156
|
+
duration: 250
|
|
157
|
+
});
|
|
158
|
+
const scrubX = getPointOnSerializableScale(scrubberPosition.value, xScale);
|
|
159
|
+
const rightEdge = drawingArea.x + drawingArea.width;
|
|
160
|
+
const target = rightEdge - scrubX >= FADE_ZONE ? 1 : 0;
|
|
161
|
+
return withTiming(target, {
|
|
162
|
+
duration: 250
|
|
163
|
+
});
|
|
164
|
+
}, [scrubberPosition, xScale, drawingArea]);
|
|
165
|
+
return /*#__PURE__*/_jsx(DefaultReferenceLineLabel, _extends({}, props, {
|
|
166
|
+
background: theme.color.bgSecondary,
|
|
167
|
+
borderRadius: 12.5,
|
|
168
|
+
color: theme.color.fg,
|
|
169
|
+
font: "label1",
|
|
170
|
+
inset: {
|
|
171
|
+
top: 4,
|
|
172
|
+
bottom: 4,
|
|
173
|
+
left: 8,
|
|
174
|
+
right: 8
|
|
175
|
+
},
|
|
176
|
+
opacity: opacity
|
|
177
|
+
}));
|
|
178
|
+
});
|
|
179
|
+
function StartPriceReferenceLine() {
|
|
180
|
+
const theme = useTheme();
|
|
181
|
+
const hourData = useMemo(() => sparklineInteractiveData.hour, []);
|
|
182
|
+
const startPrice = hourData[0].value;
|
|
183
|
+
const endPrice = hourData[hourData.length - 1].value;
|
|
184
|
+
const isPositive = endPrice >= startPrice;
|
|
185
|
+
const seriesColor = isPositive ? theme.color.fgPositive : theme.color.fgNegative;
|
|
186
|
+
return /*#__PURE__*/_jsxs(LineChart, {
|
|
187
|
+
enableScrubbing: true,
|
|
188
|
+
showArea: true,
|
|
189
|
+
areaType: "dotted",
|
|
190
|
+
height: 300,
|
|
191
|
+
inset: 0,
|
|
192
|
+
series: [{
|
|
193
|
+
id: 'hourly-prices',
|
|
194
|
+
data: hourData.map(d => d.value),
|
|
195
|
+
color: seriesColor
|
|
196
|
+
}],
|
|
197
|
+
xAxis: {
|
|
198
|
+
range: _ref2 => {
|
|
199
|
+
let {
|
|
200
|
+
min,
|
|
201
|
+
max
|
|
202
|
+
} = _ref2;
|
|
203
|
+
return {
|
|
204
|
+
min,
|
|
205
|
+
max: max - 24
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
children: [/*#__PURE__*/_jsx(Scrubber, {}), /*#__PURE__*/_jsx(ReferenceLine, {
|
|
210
|
+
LabelComponent: StartPriceLabel,
|
|
211
|
+
LineComponent: props => /*#__PURE__*/_jsx(DottedLine, _extends({}, props, {
|
|
212
|
+
dashIntervals: [0, 16],
|
|
213
|
+
strokeWidth: 3
|
|
214
|
+
})),
|
|
215
|
+
dataY: startPrice,
|
|
216
|
+
label: startPrice.toLocaleString('en-US', {
|
|
217
|
+
minimumFractionDigits: 2,
|
|
218
|
+
maximumFractionDigits: 2
|
|
219
|
+
}),
|
|
220
|
+
labelDx: -12,
|
|
221
|
+
labelHorizontalAlignment: "right",
|
|
222
|
+
stroke: theme.color.fgMuted
|
|
223
|
+
})]
|
|
224
|
+
});
|
|
225
|
+
}
|
|
132
226
|
export default ReferenceLineStories;
|
package/esm/chart/point/Point.js
CHANGED
|
@@ -5,7 +5,7 @@ import { useTheme } from '@coinbase/cds-mobile/hooks/useTheme';
|
|
|
5
5
|
import { Circle, Group, interpolateColors } from '@shopify/react-native-skia';
|
|
6
6
|
import { useCartesianChartContext } from '../ChartProvider';
|
|
7
7
|
import { projectPoint } from '../utils';
|
|
8
|
-
import { buildTransition, defaultTransition } from '../utils/transition';
|
|
8
|
+
import { buildTransition, defaultAccessoryEnterTransition, defaultTransition, getTransition } from '../utils/transition';
|
|
9
9
|
import { DefaultPointLabel } from './DefaultPointLabel';
|
|
10
10
|
|
|
11
11
|
/**
|
|
@@ -16,6 +16,7 @@ export const Point = /*#__PURE__*/memo(_ref => {
|
|
|
16
16
|
let {
|
|
17
17
|
dataX,
|
|
18
18
|
dataY,
|
|
19
|
+
xAxisId,
|
|
19
20
|
yAxisId,
|
|
20
21
|
fill: fillProp,
|
|
21
22
|
radius = 5,
|
|
@@ -27,7 +28,8 @@ export const Point = /*#__PURE__*/memo(_ref => {
|
|
|
27
28
|
labelPosition = 'center',
|
|
28
29
|
labelOffset,
|
|
29
30
|
labelFont,
|
|
30
|
-
|
|
31
|
+
transitions,
|
|
32
|
+
transition,
|
|
31
33
|
animate: animateProp
|
|
32
34
|
} = _ref;
|
|
33
35
|
const theme = useTheme();
|
|
@@ -40,9 +42,11 @@ export const Point = /*#__PURE__*/memo(_ref => {
|
|
|
40
42
|
drawingArea
|
|
41
43
|
} = useCartesianChartContext();
|
|
42
44
|
const animate = animateProp != null ? animateProp : animationEnabled;
|
|
43
|
-
const xScale = getXScale();
|
|
45
|
+
const xScale = getXScale(xAxisId);
|
|
44
46
|
const yScale = getYScale(yAxisId);
|
|
45
47
|
const shouldAnimate = animate != null ? animate : false;
|
|
48
|
+
const updateTransition = useMemo(() => getTransition((transitions == null ? void 0 : transitions.update) !== undefined ? transitions.update : transition, animate, defaultTransition), [animate, transitions == null ? void 0 : transitions.update, transition]);
|
|
49
|
+
const enterTransition = useMemo(() => getTransition(transitions == null ? void 0 : transitions.enter, animate, defaultAccessoryEnterTransition), [animate, transitions == null ? void 0 : transitions.enter]);
|
|
46
50
|
|
|
47
51
|
// Calculate pixel coordinates from data coordinates
|
|
48
52
|
const pixelCoordinate = useMemo(() => {
|
|
@@ -62,9 +66,13 @@ export const Point = /*#__PURE__*/memo(_ref => {
|
|
|
62
66
|
// Animated values for position
|
|
63
67
|
const animatedX = useSharedValue(0);
|
|
64
68
|
const animatedY = useSharedValue(0);
|
|
65
|
-
|
|
66
|
-
// Animated value for color interpolation (0 = old color, 1 = new color)
|
|
69
|
+
const enterOpacity = useSharedValue(shouldAnimate ? 0 : 1);
|
|
67
70
|
const colorProgress = useSharedValue(1);
|
|
71
|
+
const isReady = !!xScale && !!yScale;
|
|
72
|
+
useEffect(() => {
|
|
73
|
+
if (!shouldAnimate || !isReady) return;
|
|
74
|
+
enterOpacity.value = buildTransition(1, enterTransition);
|
|
75
|
+
}, [shouldAnimate, isReady, enterTransition, enterOpacity]);
|
|
68
76
|
|
|
69
77
|
// Update position when coordinates change
|
|
70
78
|
useEffect(() => {
|
|
@@ -72,26 +80,26 @@ export const Point = /*#__PURE__*/memo(_ref => {
|
|
|
72
80
|
return;
|
|
73
81
|
}
|
|
74
82
|
if (shouldAnimate && previousPixelCoordinate) {
|
|
75
|
-
animatedX.value = buildTransition(pixelCoordinate.x,
|
|
76
|
-
animatedY.value = buildTransition(pixelCoordinate.y,
|
|
83
|
+
animatedX.value = buildTransition(pixelCoordinate.x, updateTransition);
|
|
84
|
+
animatedY.value = buildTransition(pixelCoordinate.y, updateTransition);
|
|
77
85
|
} else {
|
|
78
86
|
cancelAnimation(animatedX);
|
|
79
87
|
cancelAnimation(animatedY);
|
|
80
88
|
animatedX.value = pixelCoordinate.x;
|
|
81
89
|
animatedY.value = pixelCoordinate.y;
|
|
82
90
|
}
|
|
83
|
-
}, [pixelCoordinate, shouldAnimate, previousPixelCoordinate, animatedX, animatedY,
|
|
91
|
+
}, [pixelCoordinate, shouldAnimate, previousPixelCoordinate, animatedX, animatedY, updateTransition]);
|
|
84
92
|
|
|
85
93
|
// Update color when fill changes
|
|
86
94
|
useEffect(() => {
|
|
87
95
|
if (shouldAnimate && previousFill && previousFill !== fill) {
|
|
88
96
|
colorProgress.value = 0;
|
|
89
|
-
colorProgress.value = buildTransition(1,
|
|
97
|
+
colorProgress.value = buildTransition(1, updateTransition);
|
|
90
98
|
} else {
|
|
91
99
|
cancelAnimation(colorProgress);
|
|
92
100
|
colorProgress.value = 1;
|
|
93
101
|
}
|
|
94
|
-
}, [fill, shouldAnimate, previousFill, colorProgress,
|
|
102
|
+
}, [fill, shouldAnimate, previousFill, colorProgress, updateTransition]);
|
|
95
103
|
|
|
96
104
|
// Create animated point for circles
|
|
97
105
|
const animatedPoint = useDerivedValue(() => {
|
|
@@ -108,24 +116,19 @@ export const Point = /*#__PURE__*/memo(_ref => {
|
|
|
108
116
|
}
|
|
109
117
|
return interpolateColors(colorProgress.value, [0, 1], [previousFill, fill]);
|
|
110
118
|
}, [colorProgress, previousFill, fill]);
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
}, [animatedX, animatedY, drawingArea]);
|
|
116
|
-
|
|
117
|
-
// Compute effective opacity based on drawing area bounds
|
|
119
|
+
const isWithinDrawingArea = useMemo(() => {
|
|
120
|
+
if (!pixelCoordinate) return false;
|
|
121
|
+
return pixelCoordinate.x >= drawingArea.x && pixelCoordinate.x <= drawingArea.x + drawingArea.width && pixelCoordinate.y >= drawingArea.y && pixelCoordinate.y <= drawingArea.y + drawingArea.height;
|
|
122
|
+
}, [pixelCoordinate, drawingArea]);
|
|
118
123
|
const effectiveOpacity = useDerivedValue(() => {
|
|
119
124
|
const baseOpacity = opacity != null ? opacity : 1;
|
|
120
|
-
return isWithinDrawingArea
|
|
121
|
-
}, [isWithinDrawingArea, opacity]);
|
|
125
|
+
return isWithinDrawingArea ? baseOpacity * enterOpacity.value : 0;
|
|
126
|
+
}, [isWithinDrawingArea, opacity, enterOpacity]);
|
|
122
127
|
const offset = useMemo(() => labelOffset != null ? labelOffset : radius * 2, [labelOffset, radius]);
|
|
123
128
|
if (!pixelCoordinate) {
|
|
124
129
|
return null;
|
|
125
130
|
}
|
|
126
|
-
|
|
127
|
-
// If animation is disabled or on first render, use static rendering
|
|
128
|
-
if (!shouldAnimate || !previousPixelCoordinate) {
|
|
131
|
+
if (!shouldAnimate) {
|
|
129
132
|
const isWithinBounds = pixelCoordinate.x >= drawingArea.x && pixelCoordinate.x <= drawingArea.x + drawingArea.width && pixelCoordinate.y >= drawingArea.y && pixelCoordinate.y <= drawingArea.y + drawingArea.height;
|
|
130
133
|
const staticOpacity = isWithinBounds ? opacity != null ? opacity : 1 : 0;
|
|
131
134
|
return /*#__PURE__*/_jsxs(_Fragment, {
|
|
@@ -159,20 +162,16 @@ export const Point = /*#__PURE__*/memo(_ref => {
|
|
|
159
162
|
})]
|
|
160
163
|
});
|
|
161
164
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
c: animatedPoint,
|
|
173
|
-
color: animatedFillColor,
|
|
174
|
-
r: radius - strokeWidth / 2
|
|
175
|
-
})]
|
|
165
|
+
return /*#__PURE__*/_jsxs(Group, {
|
|
166
|
+
opacity: effectiveOpacity,
|
|
167
|
+
children: [strokeWidth > 0 && /*#__PURE__*/_jsx(Circle, {
|
|
168
|
+
c: animatedPoint,
|
|
169
|
+
color: stroke,
|
|
170
|
+
r: radius + strokeWidth / 2
|
|
171
|
+
}), /*#__PURE__*/_jsx(Circle, {
|
|
172
|
+
c: animatedPoint,
|
|
173
|
+
color: animatedFillColor,
|
|
174
|
+
r: radius - strokeWidth / 2
|
|
176
175
|
}), label && /*#__PURE__*/_jsx(LabelComponent, {
|
|
177
176
|
dataX: dataX,
|
|
178
177
|
dataY: dataY,
|
|
@@ -1,19 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
import { cancelAnimation, Easing, useAnimatedReaction, useDerivedValue, useSharedValue, withRepeat, withSequence } from 'react-native-reanimated';
|
|
1
|
+
import { forwardRef, memo, useEffect, useImperativeHandle, useMemo } from 'react';
|
|
2
|
+
import { cancelAnimation, Easing, useAnimatedReaction, useDerivedValue, useSharedValue, withDelay, withRepeat, withSequence, withTiming } from 'react-native-reanimated';
|
|
4
3
|
import { useTheme } from '@coinbase/cds-mobile';
|
|
5
4
|
import { Circle, Group } from '@shopify/react-native-skia';
|
|
6
5
|
import { useCartesianChartContext } from '../ChartProvider';
|
|
7
6
|
import { unwrapAnimatedValue } from '../utils';
|
|
8
7
|
import { projectPointWithSerializableScale } from '../utils/point';
|
|
9
|
-
import { buildTransition, defaultTransition } from '../utils/transition';
|
|
8
|
+
import { buildTransition, defaultTransition, getTransition } from '../utils/transition';
|
|
10
9
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
11
|
-
const
|
|
12
|
-
const
|
|
10
|
+
const defaultRadius = 5;
|
|
11
|
+
const defaultStrokeWidth = 2;
|
|
13
12
|
const pulseOpacityStart = 0.5;
|
|
14
13
|
const pulseOpacityEnd = 0;
|
|
15
|
-
const
|
|
16
|
-
const
|
|
14
|
+
const pulseRadiusStartMultiplier = 2;
|
|
15
|
+
const pulseRadiusEndMultiplier = 3;
|
|
17
16
|
const defaultPulseTransition = {
|
|
18
17
|
type: 'timing',
|
|
19
18
|
duration: 1600,
|
|
@@ -30,7 +29,10 @@ export const DefaultScrubberBeacon = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((
|
|
|
30
29
|
idlePulse,
|
|
31
30
|
animate = true,
|
|
32
31
|
transitions,
|
|
33
|
-
opacity: opacityProp = 1
|
|
32
|
+
opacity: opacityProp = 1,
|
|
33
|
+
radius = defaultRadius,
|
|
34
|
+
stroke,
|
|
35
|
+
strokeWidth = defaultStrokeWidth
|
|
34
36
|
} = _ref;
|
|
35
37
|
const theme = useTheme();
|
|
36
38
|
const {
|
|
@@ -40,16 +42,13 @@ export const DefaultScrubberBeacon = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((
|
|
|
40
42
|
drawingArea
|
|
41
43
|
} = useCartesianChartContext();
|
|
42
44
|
const targetSeries = useMemo(() => getSeries(seriesId), [getSeries, seriesId]);
|
|
43
|
-
const xScale = useMemo(() => getXSerializableScale(), [getXSerializableScale]);
|
|
45
|
+
const xScale = useMemo(() => getXSerializableScale(targetSeries == null ? void 0 : targetSeries.xAxisId), [getXSerializableScale, targetSeries == null ? void 0 : targetSeries.xAxisId]);
|
|
44
46
|
const yScale = useMemo(() => getYSerializableScale(targetSeries == null ? void 0 : targetSeries.yAxisId), [getYSerializableScale, targetSeries == null ? void 0 : targetSeries.yAxisId]);
|
|
45
47
|
const color = useMemo(() => {
|
|
46
48
|
var _ref2;
|
|
47
49
|
return (_ref2 = colorProp != null ? colorProp : targetSeries == null ? void 0 : targetSeries.color) != null ? _ref2 : theme.color.fgPrimary;
|
|
48
50
|
}, [colorProp, targetSeries == null ? void 0 : targetSeries.color, theme.color.fgPrimary]);
|
|
49
|
-
const updateTransition = useMemo(() =>
|
|
50
|
-
var _transitions$update;
|
|
51
|
-
return (_transitions$update = transitions == null ? void 0 : transitions.update) != null ? _transitions$update : defaultTransition;
|
|
52
|
-
}, [transitions == null ? void 0 : transitions.update]);
|
|
51
|
+
const updateTransition = useMemo(() => getTransition(transitions == null ? void 0 : transitions.update, animate, defaultTransition), [transitions == null ? void 0 : transitions.update, animate]);
|
|
53
52
|
const pulseTransition = useMemo(() => {
|
|
54
53
|
var _transitions$pulse;
|
|
55
54
|
return (_transitions$pulse = transitions == null ? void 0 : transitions.pulse) != null ? _transitions$pulse : defaultPulseTransition;
|
|
@@ -58,10 +57,20 @@ export const DefaultScrubberBeacon = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((
|
|
|
58
57
|
var _transitions$pulseRep;
|
|
59
58
|
return (_transitions$pulseRep = transitions == null ? void 0 : transitions.pulseRepeatDelay) != null ? _transitions$pulseRep : defaultPulseRepeatDelay;
|
|
60
59
|
}, [transitions == null ? void 0 : transitions.pulseRepeatDelay]);
|
|
60
|
+
const pulseRadiusStart = radius * pulseRadiusStartMultiplier;
|
|
61
|
+
const pulseRadiusEnd = radius * pulseRadiusEndMultiplier;
|
|
61
62
|
const pulseOpacity = useSharedValue(0);
|
|
62
63
|
const pulseRadius = useSharedValue(pulseRadiusStart);
|
|
63
|
-
|
|
64
|
-
|
|
64
|
+
|
|
65
|
+
// Convert idlePulse prop to SharedValue so useAnimatedReaction can detect changes.
|
|
66
|
+
// In the new React Native architecture, regular JS props are captured by value in worklets
|
|
67
|
+
// and won't update when the prop changes.
|
|
68
|
+
const idlePulseShared = useSharedValue(idlePulse != null ? idlePulse : false);
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
idlePulseShared.value = idlePulse != null ? idlePulse : false;
|
|
71
|
+
}, [idlePulse, idlePulseShared]);
|
|
72
|
+
const animatedX = useSharedValue(null);
|
|
73
|
+
const animatedY = useSharedValue(null);
|
|
65
74
|
|
|
66
75
|
// Calculate the target point position - project data to pixels
|
|
67
76
|
const targetPoint = useDerivedValue(() => {
|
|
@@ -95,49 +104,43 @@ export const DefaultScrubberBeacon = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((
|
|
|
95
104
|
|
|
96
105
|
// Create animated point using the animated values
|
|
97
106
|
const animatedPoint = useDerivedValue(() => {
|
|
107
|
+
// If the animated values have not been set yet, return the target point
|
|
108
|
+
if (animatedX.value === null || animatedY.value === null) return targetPoint.value;
|
|
98
109
|
return {
|
|
99
110
|
x: animatedX.value,
|
|
100
111
|
y: animatedY.value
|
|
101
112
|
};
|
|
102
|
-
}, [animatedX, animatedY]);
|
|
113
|
+
}, [targetPoint, animatedX, animatedY]);
|
|
103
114
|
useImperativeHandle(ref, () => ({
|
|
104
115
|
pulse: () => {
|
|
105
116
|
// Only trigger manual pulse when idlePulse is not enabled
|
|
106
|
-
if (!
|
|
117
|
+
if (!idlePulseShared.value) {
|
|
107
118
|
cancelAnimation(pulseOpacity);
|
|
108
119
|
cancelAnimation(pulseRadius);
|
|
109
120
|
|
|
110
121
|
// Manual pulse without delay
|
|
111
|
-
const immediatePulseTransition = _extends({}, pulseTransition, {
|
|
112
|
-
delay: 0
|
|
113
|
-
});
|
|
114
122
|
pulseOpacity.value = pulseOpacityStart;
|
|
115
123
|
pulseRadius.value = pulseRadiusStart;
|
|
116
|
-
pulseOpacity.value = buildTransition(pulseOpacityEnd,
|
|
117
|
-
pulseRadius.value = buildTransition(pulseRadiusEnd,
|
|
124
|
+
pulseOpacity.value = buildTransition(pulseOpacityEnd, pulseTransition);
|
|
125
|
+
pulseRadius.value = buildTransition(pulseRadiusEnd, pulseTransition);
|
|
118
126
|
}
|
|
119
127
|
}
|
|
120
|
-
}), [
|
|
128
|
+
}), [idlePulseShared, pulseOpacity, pulseRadius, pulseTransition, pulseRadiusStart, pulseRadiusEnd]);
|
|
121
129
|
|
|
122
130
|
// Watch idlePulse changes and control continuous pulse
|
|
123
|
-
useAnimatedReaction(() =>
|
|
124
|
-
if (!animate) return;
|
|
131
|
+
useAnimatedReaction(() => idlePulseShared.value, current => {
|
|
125
132
|
if (current) {
|
|
126
133
|
// Start continuous pulse when idlePulse is enabled
|
|
127
|
-
// Create instant transition to reset pulse after delay
|
|
128
|
-
const instantTransition = {
|
|
129
|
-
type: 'timing',
|
|
130
|
-
duration: 0
|
|
131
|
-
};
|
|
132
|
-
const resetWithDelay = _extends({}, instantTransition, {
|
|
133
|
-
delay: pulseRepeatDelay
|
|
134
|
-
});
|
|
135
134
|
pulseOpacity.value = pulseOpacityStart;
|
|
136
135
|
pulseRadius.value = pulseRadiusStart;
|
|
137
|
-
pulseOpacity.value = withRepeat(withSequence(buildTransition(pulseOpacityEnd, pulseTransition),
|
|
136
|
+
pulseOpacity.value = withRepeat(withSequence(buildTransition(pulseOpacityEnd, pulseTransition), withDelay(pulseRepeatDelay, withTiming(pulseOpacityStart, {
|
|
137
|
+
duration: 0
|
|
138
|
+
}))), -1,
|
|
138
139
|
// infinite loop
|
|
139
140
|
false);
|
|
140
|
-
pulseRadius.value = withRepeat(withSequence(buildTransition(pulseRadiusEnd, pulseTransition),
|
|
141
|
+
pulseRadius.value = withRepeat(withSequence(buildTransition(pulseRadiusEnd, pulseTransition), withDelay(pulseRepeatDelay, withTiming(pulseRadiusStart, {
|
|
142
|
+
duration: 0
|
|
143
|
+
}))), -1,
|
|
141
144
|
// infinite loop
|
|
142
145
|
false);
|
|
143
146
|
} else {
|
|
@@ -147,7 +150,7 @@ export const DefaultScrubberBeacon = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((
|
|
|
147
150
|
pulseOpacity.value = pulseOpacityEnd;
|
|
148
151
|
pulseRadius.value = pulseRadiusStart;
|
|
149
152
|
}
|
|
150
|
-
}, [
|
|
153
|
+
}, [pulseTransition, pulseRepeatDelay, pulseRadiusStart, pulseRadiusEnd]);
|
|
151
154
|
const pulseVisibility = useDerivedValue(() => {
|
|
152
155
|
// Never pulse when scrubbing
|
|
153
156
|
if (!unwrapAnimatedValue(isIdle)) return 0;
|
|
@@ -168,7 +171,7 @@ export const DefaultScrubberBeacon = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((
|
|
|
168
171
|
r: pulseRadius
|
|
169
172
|
}), /*#__PURE__*/_jsx(Circle, {
|
|
170
173
|
c: animatedPoint,
|
|
171
|
-
color: theme.color.bg,
|
|
174
|
+
color: stroke != null ? stroke : theme.color.bg,
|
|
172
175
|
r: radius + strokeWidth / 2
|
|
173
176
|
}), /*#__PURE__*/_jsx(Circle, {
|
|
174
177
|
c: animatedPoint,
|
|
@@ -1,28 +1,44 @@
|
|
|
1
|
-
const _excluded = ["
|
|
1
|
+
const _excluded = ["dx", "dy"];
|
|
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
|
-
import { memo } from 'react';
|
|
4
|
+
import { memo, useMemo } from 'react';
|
|
5
5
|
import { useCartesianChartContext } from '../ChartProvider';
|
|
6
6
|
import { DefaultReferenceLineLabel } from '../line';
|
|
7
7
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
8
8
|
/**
|
|
9
9
|
* DefaultScrubberLabel is the default label component for the scrubber line.
|
|
10
10
|
* It will automatically add padding around the label when elevated to fit within chart bounds to prevent shadow from being cutoff.
|
|
11
|
-
*
|
|
11
|
+
* In vertical layout, it positions the label above the scrubber line.
|
|
12
|
+
* In horizontal layout, it centers the label in the chart's right inset.
|
|
12
13
|
*/
|
|
13
14
|
export const DefaultScrubberLabel = /*#__PURE__*/memo(_ref => {
|
|
14
15
|
let {
|
|
15
|
-
|
|
16
|
-
dy
|
|
17
|
-
boundsInset
|
|
16
|
+
dx: dxProp,
|
|
17
|
+
dy: dyProp
|
|
18
18
|
} = _ref,
|
|
19
19
|
props = _objectWithoutPropertiesLoose(_ref, _excluded);
|
|
20
20
|
const {
|
|
21
|
-
drawingArea
|
|
21
|
+
drawingArea,
|
|
22
|
+
layout,
|
|
23
|
+
width: chartWidth
|
|
22
24
|
} = useCartesianChartContext();
|
|
25
|
+
const isHorizontalLayout = layout === 'horizontal';
|
|
26
|
+
const dx = useMemo(() => {
|
|
27
|
+
if (dxProp !== undefined) return dxProp;
|
|
28
|
+
if (isHorizontalLayout) {
|
|
29
|
+
const drawingAreaEnd = drawingArea.x + drawingArea.width;
|
|
30
|
+
const rightOffset = chartWidth - drawingAreaEnd;
|
|
31
|
+
return rightOffset / 2;
|
|
32
|
+
}
|
|
33
|
+
return 0;
|
|
34
|
+
}, [drawingArea.width, drawingArea.x, dxProp, isHorizontalLayout, chartWidth]);
|
|
35
|
+
const dy = useMemo(() => {
|
|
36
|
+
if (dyProp !== undefined) return dyProp;
|
|
37
|
+
if (isHorizontalLayout) return 0;
|
|
38
|
+
return -0.5 * drawingArea.y;
|
|
39
|
+
}, [dyProp, isHorizontalLayout, drawingArea.y]);
|
|
23
40
|
return /*#__PURE__*/_jsx(DefaultReferenceLineLabel, _extends({
|
|
24
|
-
|
|
25
|
-
dy: dy
|
|
26
|
-
verticalAlignment: verticalAlignment
|
|
41
|
+
dx: dx,
|
|
42
|
+
dy: dy
|
|
27
43
|
}, props));
|
|
28
44
|
});
|