@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,166 @@
|
|
|
1
|
+
import React, { forwardRef, memo, useCallback, useEffect, useImperativeHandle, useMemo } from 'react';
|
|
2
|
+
import { runOnJS, useAnimatedReaction, useDerivedValue, useSharedValue, withDelay, withTiming } from 'react-native-reanimated';
|
|
3
|
+
import { useTheme } from '@coinbase/cds-mobile';
|
|
4
|
+
import { Group, Rect } from '@shopify/react-native-skia';
|
|
5
|
+
import { useCartesianChartContext } from '../ChartProvider';
|
|
6
|
+
import { ReferenceLine } from '../line';
|
|
7
|
+
import { accessoryFadeTransitionDelay, accessoryFadeTransitionDuration, getPointOnSerializableScale, useScrubberContext } from '../utils';
|
|
8
|
+
import { DefaultScrubberBeacon } from './DefaultScrubberBeacon';
|
|
9
|
+
import { DefaultScrubberLabel } from './DefaultScrubberLabel';
|
|
10
|
+
import { ScrubberBeaconGroup } from './ScrubberBeaconGroup';
|
|
11
|
+
import { ScrubberBeaconLabelGroup } from './ScrubberBeaconLabelGroup';
|
|
12
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
13
|
+
/**
|
|
14
|
+
* Unified component that manages all scrubber elements (beacons, line, labels).
|
|
15
|
+
*/
|
|
16
|
+
export const Scrubber = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, ref) => {
|
|
17
|
+
let {
|
|
18
|
+
seriesIds,
|
|
19
|
+
hideLine,
|
|
20
|
+
label,
|
|
21
|
+
lineStroke,
|
|
22
|
+
BeaconComponent = DefaultScrubberBeacon,
|
|
23
|
+
BeaconLabelComponent,
|
|
24
|
+
LineComponent,
|
|
25
|
+
LabelComponent = DefaultScrubberLabel,
|
|
26
|
+
labelElevated,
|
|
27
|
+
hideOverlay,
|
|
28
|
+
overlayOffset = 2,
|
|
29
|
+
beaconLabelMinGap,
|
|
30
|
+
beaconLabelHorizontalOffset,
|
|
31
|
+
labelFont,
|
|
32
|
+
labelBoundsInset,
|
|
33
|
+
beaconLabelFont,
|
|
34
|
+
idlePulse,
|
|
35
|
+
beaconTransitions
|
|
36
|
+
} = _ref;
|
|
37
|
+
const theme = useTheme();
|
|
38
|
+
const beaconGroupRef = React.useRef(null);
|
|
39
|
+
const {
|
|
40
|
+
scrubberPosition
|
|
41
|
+
} = useScrubberContext();
|
|
42
|
+
const {
|
|
43
|
+
getXSerializableScale,
|
|
44
|
+
getXAxis,
|
|
45
|
+
series,
|
|
46
|
+
drawingArea,
|
|
47
|
+
animate,
|
|
48
|
+
dataLength
|
|
49
|
+
} = useCartesianChartContext();
|
|
50
|
+
const xAxis = useMemo(() => getXAxis(), [getXAxis]);
|
|
51
|
+
const xScale = useMemo(() => getXSerializableScale(), [getXSerializableScale]);
|
|
52
|
+
|
|
53
|
+
// Animation state for delayed scrubber rendering (matches web timing)
|
|
54
|
+
const scrubberOpacity = useSharedValue(animate ? 0 : 1);
|
|
55
|
+
|
|
56
|
+
// Delay scrubber appearance until after path enter animation completes
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
if (animate) {
|
|
59
|
+
scrubberOpacity.value = withDelay(accessoryFadeTransitionDelay, withTiming(1, {
|
|
60
|
+
duration: accessoryFadeTransitionDuration
|
|
61
|
+
}));
|
|
62
|
+
}
|
|
63
|
+
}, [animate, scrubberOpacity]);
|
|
64
|
+
|
|
65
|
+
// Expose imperative handle with pulse method
|
|
66
|
+
useImperativeHandle(ref, () => ({
|
|
67
|
+
pulse: () => {
|
|
68
|
+
var _beaconGroupRef$curre;
|
|
69
|
+
(_beaconGroupRef$curre = beaconGroupRef.current) == null || _beaconGroupRef$curre.pulse();
|
|
70
|
+
}
|
|
71
|
+
}));
|
|
72
|
+
const filteredSeriesIds = useMemo(() => {
|
|
73
|
+
if (seriesIds === undefined) {
|
|
74
|
+
var _series$map;
|
|
75
|
+
return (_series$map = series == null ? void 0 : series.map(s => s.id)) != null ? _series$map : [];
|
|
76
|
+
}
|
|
77
|
+
return seriesIds;
|
|
78
|
+
}, [series, seriesIds]);
|
|
79
|
+
const dataIndex = useDerivedValue(() => {
|
|
80
|
+
var _scrubberPosition$val;
|
|
81
|
+
return (_scrubberPosition$val = scrubberPosition.value) != null ? _scrubberPosition$val : Math.max(0, dataLength - 1);
|
|
82
|
+
}, [scrubberPosition, dataLength]);
|
|
83
|
+
const dataX = useDerivedValue(() => {
|
|
84
|
+
if (xAxis != null && xAxis.data && Array.isArray(xAxis.data) && xAxis.data[dataIndex.value] !== undefined) {
|
|
85
|
+
const dataValue = xAxis.data[dataIndex.value];
|
|
86
|
+
return typeof dataValue === 'string' ? dataIndex.value : dataValue;
|
|
87
|
+
}
|
|
88
|
+
return dataIndex.value;
|
|
89
|
+
}, [xAxis, dataIndex]);
|
|
90
|
+
const lineOpacity = useDerivedValue(() => {
|
|
91
|
+
return scrubberPosition.value !== undefined ? 1 : 0;
|
|
92
|
+
}, [scrubberPosition]);
|
|
93
|
+
const overlayOpacity = useDerivedValue(() => {
|
|
94
|
+
return scrubberPosition.value !== undefined ? 0.8 : 0;
|
|
95
|
+
}, [scrubberPosition]);
|
|
96
|
+
const overlayWidth = useDerivedValue(() => {
|
|
97
|
+
const pixelX = dataX.value !== undefined && xScale ? getPointOnSerializableScale(dataX.value, xScale) : 0;
|
|
98
|
+
return drawingArea.x + drawingArea.width - pixelX + overlayOffset;
|
|
99
|
+
}, [dataX, xScale]);
|
|
100
|
+
const overlayX = useDerivedValue(() => {
|
|
101
|
+
const xValue = dataX.value !== undefined && xScale ? getPointOnSerializableScale(dataX.value, xScale) : 0;
|
|
102
|
+
return xValue;
|
|
103
|
+
}, [dataX, xScale]);
|
|
104
|
+
const resolvedLabelValue = useSharedValue('');
|
|
105
|
+
const updateResolvedLabel = useCallback(index => {
|
|
106
|
+
if (!label) {
|
|
107
|
+
resolvedLabelValue.value = '';
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
if (typeof label === 'function') {
|
|
111
|
+
const result = label(index);
|
|
112
|
+
resolvedLabelValue.value = result != null ? result : '';
|
|
113
|
+
} else if (typeof label === 'string') {
|
|
114
|
+
resolvedLabelValue.value = label;
|
|
115
|
+
}
|
|
116
|
+
}, [label, resolvedLabelValue]);
|
|
117
|
+
|
|
118
|
+
// Update resolved label when dataIndex changes
|
|
119
|
+
useAnimatedReaction(() => dataIndex.value, currentIndex => {
|
|
120
|
+
'worklet';
|
|
121
|
+
|
|
122
|
+
runOnJS(updateResolvedLabel)(currentIndex);
|
|
123
|
+
}, [updateResolvedLabel]);
|
|
124
|
+
const beaconLabels = useMemo(() => {
|
|
125
|
+
var _series$filter$filter;
|
|
126
|
+
return (_series$filter$filter = series == null ? void 0 : series.filter(s => filteredSeriesIds.includes(s.id)).filter(s => s.label !== undefined && s.label.length > 0).map(s => ({
|
|
127
|
+
seriesId: s.id,
|
|
128
|
+
label: s.label,
|
|
129
|
+
color: s.color
|
|
130
|
+
}))) != null ? _series$filter$filter : [];
|
|
131
|
+
}, [series, filteredSeriesIds]);
|
|
132
|
+
if (!xScale) return;
|
|
133
|
+
return /*#__PURE__*/_jsxs(Group, {
|
|
134
|
+
opacity: scrubberOpacity,
|
|
135
|
+
children: [!hideOverlay && /*#__PURE__*/_jsx(Rect, {
|
|
136
|
+
color: theme.color.bg,
|
|
137
|
+
height: drawingArea.height + overlayOffset * 2,
|
|
138
|
+
opacity: overlayOpacity,
|
|
139
|
+
width: overlayWidth,
|
|
140
|
+
x: overlayX,
|
|
141
|
+
y: drawingArea.y - overlayOffset
|
|
142
|
+
}), !hideLine && /*#__PURE__*/_jsx(ReferenceLine, {
|
|
143
|
+
LabelComponent: LabelComponent,
|
|
144
|
+
LineComponent: LineComponent,
|
|
145
|
+
dataX: dataX,
|
|
146
|
+
label: resolvedLabelValue,
|
|
147
|
+
labelBoundsInset: labelBoundsInset,
|
|
148
|
+
labelElevated: labelElevated,
|
|
149
|
+
labelFont: labelFont,
|
|
150
|
+
opacity: lineOpacity,
|
|
151
|
+
stroke: lineStroke
|
|
152
|
+
}), /*#__PURE__*/_jsx(ScrubberBeaconGroup, {
|
|
153
|
+
ref: beaconGroupRef,
|
|
154
|
+
BeaconComponent: BeaconComponent,
|
|
155
|
+
idlePulse: idlePulse,
|
|
156
|
+
seriesIds: filteredSeriesIds,
|
|
157
|
+
transitions: beaconTransitions
|
|
158
|
+
}), beaconLabels.length > 0 && /*#__PURE__*/_jsx(ScrubberBeaconLabelGroup, {
|
|
159
|
+
BeaconLabelComponent: BeaconLabelComponent,
|
|
160
|
+
labelFont: beaconLabelFont,
|
|
161
|
+
labelHorizontalOffset: beaconLabelHorizontalOffset,
|
|
162
|
+
labelMinGap: beaconLabelMinGap,
|
|
163
|
+
labels: beaconLabels
|
|
164
|
+
})]
|
|
165
|
+
});
|
|
166
|
+
}));
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { forwardRef, memo, useCallback, useImperativeHandle, useMemo } from 'react';
|
|
2
|
+
import { useDerivedValue } from 'react-native-reanimated';
|
|
3
|
+
import { useRefMap } from '@coinbase/cds-common/hooks/useRefMap';
|
|
4
|
+
import { useTheme } from '@coinbase/cds-mobile';
|
|
5
|
+
import { useCartesianChartContext } from '../ChartProvider';
|
|
6
|
+
import { evaluateGradientAtValue, getGradientStops, useScrubberContext } from '../utils';
|
|
7
|
+
import { convertToSerializableScale } from '../utils/scale';
|
|
8
|
+
import { DefaultScrubberBeacon } from './DefaultScrubberBeacon';
|
|
9
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
10
|
+
// Helper component to calculate beacon data for a specific series
|
|
11
|
+
const BeaconWithData = /*#__PURE__*/memo(_ref => {
|
|
12
|
+
let {
|
|
13
|
+
seriesId,
|
|
14
|
+
dataIndex,
|
|
15
|
+
dataX,
|
|
16
|
+
isIdle,
|
|
17
|
+
BeaconComponent,
|
|
18
|
+
idlePulse,
|
|
19
|
+
animate,
|
|
20
|
+
transitions,
|
|
21
|
+
beaconRef
|
|
22
|
+
} = _ref;
|
|
23
|
+
const {
|
|
24
|
+
getSeries,
|
|
25
|
+
getSeriesData,
|
|
26
|
+
getXScale,
|
|
27
|
+
getYScale
|
|
28
|
+
} = useCartesianChartContext();
|
|
29
|
+
const theme = useTheme();
|
|
30
|
+
const series = useMemo(() => getSeries(seriesId), [getSeries, seriesId]);
|
|
31
|
+
const sourceData = useMemo(() => getSeriesData(seriesId), [getSeriesData, seriesId]);
|
|
32
|
+
const gradient = series == null ? void 0 : series.gradient;
|
|
33
|
+
const dataY = useDerivedValue(() => {
|
|
34
|
+
if (sourceData && dataIndex.value !== undefined && dataIndex.value >= 0 && dataIndex.value < sourceData.length) {
|
|
35
|
+
const dataValue = sourceData[dataIndex.value];
|
|
36
|
+
if (typeof dataValue === 'number') {
|
|
37
|
+
return dataValue;
|
|
38
|
+
} else if (Array.isArray(dataValue)) {
|
|
39
|
+
const validValues = dataValue.filter(val => val !== null);
|
|
40
|
+
if (validValues.length >= 1) {
|
|
41
|
+
return validValues[validValues.length - 1];
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return 0;
|
|
46
|
+
}, [sourceData, dataIndex]);
|
|
47
|
+
|
|
48
|
+
// Get scales for gradient evaluation
|
|
49
|
+
const gradientScale = useMemo(() => {
|
|
50
|
+
if (!gradient) return undefined;
|
|
51
|
+
const scale = gradient.axis === 'x' ? getXScale() : getYScale(series == null ? void 0 : series.yAxisId);
|
|
52
|
+
if (!scale) return undefined;
|
|
53
|
+
return convertToSerializableScale(scale);
|
|
54
|
+
}, [gradient, getXScale, getYScale, series == null ? void 0 : series.yAxisId]);
|
|
55
|
+
const gradientStops = useMemo(() => {
|
|
56
|
+
if (!gradient || !gradientScale) return undefined;
|
|
57
|
+
const domain = {
|
|
58
|
+
min: gradientScale.domain[0],
|
|
59
|
+
max: gradientScale.domain[1]
|
|
60
|
+
};
|
|
61
|
+
return getGradientStops(gradient.stops, domain);
|
|
62
|
+
}, [gradient, gradientScale]);
|
|
63
|
+
|
|
64
|
+
// Evaluate gradient color on UI thread
|
|
65
|
+
const color = useDerivedValue(() => {
|
|
66
|
+
'worklet';
|
|
67
|
+
|
|
68
|
+
// Evaluate gradient if present
|
|
69
|
+
var _series$color;
|
|
70
|
+
if (gradient && gradientScale && gradientStops) {
|
|
71
|
+
var _gradient$axis;
|
|
72
|
+
const axis = (_gradient$axis = gradient.axis) != null ? _gradient$axis : 'y';
|
|
73
|
+
const dataValue = axis === 'x' ? dataX.value : dataY.value;
|
|
74
|
+
if (dataValue !== undefined) {
|
|
75
|
+
const evaluatedColor = evaluateGradientAtValue(gradientStops, dataValue, gradientScale);
|
|
76
|
+
if (evaluatedColor) {
|
|
77
|
+
return evaluatedColor;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Fallback to series color
|
|
83
|
+
return (_series$color = series == null ? void 0 : series.color) != null ? _series$color : theme.color.fgPrimary;
|
|
84
|
+
}, [gradient, gradientScale, gradientStops, dataX, dataY, series == null ? void 0 : series.color, theme.color.fgPrimary]);
|
|
85
|
+
return /*#__PURE__*/_jsx(BeaconComponent, {
|
|
86
|
+
ref: beaconRef,
|
|
87
|
+
animate: animate,
|
|
88
|
+
color: color,
|
|
89
|
+
dataX: dataX,
|
|
90
|
+
dataY: dataY,
|
|
91
|
+
idlePulse: idlePulse,
|
|
92
|
+
isIdle: isIdle,
|
|
93
|
+
seriesId: seriesId,
|
|
94
|
+
transitions: transitions
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
export const ScrubberBeaconGroup = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref2, ref) => {
|
|
98
|
+
let {
|
|
99
|
+
seriesIds,
|
|
100
|
+
idlePulse,
|
|
101
|
+
transitions,
|
|
102
|
+
BeaconComponent = DefaultScrubberBeacon
|
|
103
|
+
} = _ref2;
|
|
104
|
+
const ScrubberBeaconRefs = useRefMap();
|
|
105
|
+
const {
|
|
106
|
+
scrubberPosition
|
|
107
|
+
} = useScrubberContext();
|
|
108
|
+
const {
|
|
109
|
+
getXAxis,
|
|
110
|
+
series,
|
|
111
|
+
dataLength,
|
|
112
|
+
animate
|
|
113
|
+
} = useCartesianChartContext();
|
|
114
|
+
const xAxis = useMemo(() => getXAxis(), [getXAxis]);
|
|
115
|
+
|
|
116
|
+
// Expose imperative handle with pulse method
|
|
117
|
+
useImperativeHandle(ref, () => ({
|
|
118
|
+
pulse: () => {
|
|
119
|
+
Object.values(ScrubberBeaconRefs.refs).forEach(beaconRef => {
|
|
120
|
+
beaconRef == null || beaconRef.pulse();
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
}));
|
|
124
|
+
const filteredSeries = useMemo(() => {
|
|
125
|
+
var _series$filter;
|
|
126
|
+
return (_series$filter = series == null ? void 0 : series.filter(s => seriesIds.includes(s.id))) != null ? _series$filter : [];
|
|
127
|
+
}, [series, seriesIds]);
|
|
128
|
+
const dataIndex = useDerivedValue(() => {
|
|
129
|
+
var _scrubberPosition$val;
|
|
130
|
+
return (_scrubberPosition$val = scrubberPosition.value) != null ? _scrubberPosition$val : Math.max(0, dataLength - 1);
|
|
131
|
+
}, [scrubberPosition, dataLength]);
|
|
132
|
+
const dataX = useDerivedValue(() => {
|
|
133
|
+
// Convert index to actual x value if axis has data
|
|
134
|
+
if (xAxis != null && xAxis.data && Array.isArray(xAxis.data) && xAxis.data[dataIndex.value] !== undefined) {
|
|
135
|
+
const dataValue = xAxis.data[dataIndex.value];
|
|
136
|
+
return typeof dataValue === 'string' ? dataIndex.value : dataValue;
|
|
137
|
+
}
|
|
138
|
+
return dataIndex.value;
|
|
139
|
+
}, [xAxis, dataIndex]);
|
|
140
|
+
const isIdle = useDerivedValue(() => {
|
|
141
|
+
return scrubberPosition.value === undefined;
|
|
142
|
+
}, [scrubberPosition]);
|
|
143
|
+
const createBeaconRef = useCallback(seriesId => {
|
|
144
|
+
return beaconRef => {
|
|
145
|
+
if (beaconRef) {
|
|
146
|
+
ScrubberBeaconRefs.registerRef(seriesId, beaconRef);
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
}, [ScrubberBeaconRefs]);
|
|
150
|
+
return filteredSeries.map(s => /*#__PURE__*/_jsx(BeaconWithData, {
|
|
151
|
+
BeaconComponent: BeaconComponent,
|
|
152
|
+
animate: animate,
|
|
153
|
+
beaconRef: createBeaconRef(s.id),
|
|
154
|
+
dataIndex: dataIndex,
|
|
155
|
+
dataX: dataX,
|
|
156
|
+
idlePulse: idlePulse,
|
|
157
|
+
isIdle: isIdle,
|
|
158
|
+
seriesId: s.id,
|
|
159
|
+
transitions: transitions
|
|
160
|
+
}, s.id));
|
|
161
|
+
}));
|
|
@@ -0,0 +1,185 @@
|
|
|
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 { memo, useCallback, useMemo, useState } from 'react';
|
|
3
|
+
import { useDerivedValue } from 'react-native-reanimated';
|
|
4
|
+
import { useCartesianChartContext } from '../ChartProvider';
|
|
5
|
+
import { applySerializableScale, useScrubberContext } from '../utils';
|
|
6
|
+
import { calculateLabelYPositions, getLabelPosition } from '../utils/scrubber';
|
|
7
|
+
import { DefaultScrubberBeaconLabel } from './DefaultScrubberBeaconLabel';
|
|
8
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
9
|
+
const PositionedLabel = /*#__PURE__*/memo(_ref => {
|
|
10
|
+
let {
|
|
11
|
+
index,
|
|
12
|
+
positions,
|
|
13
|
+
position,
|
|
14
|
+
label,
|
|
15
|
+
color,
|
|
16
|
+
seriesId,
|
|
17
|
+
onDimensionsChange,
|
|
18
|
+
BeaconLabelComponent,
|
|
19
|
+
labelHorizontalOffset,
|
|
20
|
+
labelFont
|
|
21
|
+
} = _ref;
|
|
22
|
+
const opacity = useDerivedValue(() => positions.value[index] !== null ? 1 : 0, [positions, index]);
|
|
23
|
+
const x = useDerivedValue(() => {
|
|
24
|
+
var _positions$value$inde, _positions$value$inde2;
|
|
25
|
+
return (_positions$value$inde = (_positions$value$inde2 = positions.value[index]) == null ? void 0 : _positions$value$inde2.x) != null ? _positions$value$inde : 0;
|
|
26
|
+
}, [positions, index]);
|
|
27
|
+
const y = useDerivedValue(() => {
|
|
28
|
+
var _positions$value$inde3, _positions$value$inde4;
|
|
29
|
+
return (_positions$value$inde3 = (_positions$value$inde4 = positions.value[index]) == null ? void 0 : _positions$value$inde4.y) != null ? _positions$value$inde3 : 0;
|
|
30
|
+
}, [positions, index]);
|
|
31
|
+
const dx = useDerivedValue(() => {
|
|
32
|
+
return position.value === 'right' ? labelHorizontalOffset : -labelHorizontalOffset;
|
|
33
|
+
}, [position, labelHorizontalOffset]);
|
|
34
|
+
const horizontalAlignment = useDerivedValue(() => position.value === 'right' ? 'left' : 'right', [position]);
|
|
35
|
+
return /*#__PURE__*/_jsx(BeaconLabelComponent, {
|
|
36
|
+
color: color,
|
|
37
|
+
dx: dx,
|
|
38
|
+
font: labelFont,
|
|
39
|
+
horizontalAlignment: horizontalAlignment,
|
|
40
|
+
label: label,
|
|
41
|
+
onDimensionsChange: d => onDimensionsChange(seriesId, d),
|
|
42
|
+
opacity: opacity,
|
|
43
|
+
seriesId: seriesId,
|
|
44
|
+
x: x,
|
|
45
|
+
y: y
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
export const ScrubberBeaconLabelGroup = /*#__PURE__*/memo(_ref2 => {
|
|
49
|
+
let {
|
|
50
|
+
labels,
|
|
51
|
+
labelMinGap = 4,
|
|
52
|
+
labelHorizontalOffset = 16,
|
|
53
|
+
labelFont,
|
|
54
|
+
BeaconLabelComponent = DefaultScrubberBeaconLabel
|
|
55
|
+
} = _ref2;
|
|
56
|
+
const {
|
|
57
|
+
getSeries,
|
|
58
|
+
getSeriesData,
|
|
59
|
+
getXSerializableScale,
|
|
60
|
+
getYSerializableScale,
|
|
61
|
+
getXAxis,
|
|
62
|
+
drawingArea,
|
|
63
|
+
dataLength
|
|
64
|
+
} = useCartesianChartContext();
|
|
65
|
+
const {
|
|
66
|
+
scrubberPosition
|
|
67
|
+
} = useScrubberContext();
|
|
68
|
+
const [labelDimensions, setLabelDimensions] = useState({});
|
|
69
|
+
const handleDimensionsChange = useCallback((id, dimensions) => {
|
|
70
|
+
setLabelDimensions(prev => {
|
|
71
|
+
const existing = prev[id];
|
|
72
|
+
if (existing && existing.width === dimensions.width && existing.height === dimensions.height) {
|
|
73
|
+
return prev;
|
|
74
|
+
}
|
|
75
|
+
return _extends({}, prev, {
|
|
76
|
+
[id]: dimensions
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
}, []);
|
|
80
|
+
const seriesInfo = useMemo(() => {
|
|
81
|
+
return labels.map(label => {
|
|
82
|
+
const series = getSeries(label.seriesId);
|
|
83
|
+
if (!series) return null;
|
|
84
|
+
const sourceData = getSeriesData(label.seriesId);
|
|
85
|
+
const yScale = getYSerializableScale(series.yAxisId);
|
|
86
|
+
return {
|
|
87
|
+
seriesId: label.seriesId,
|
|
88
|
+
sourceData,
|
|
89
|
+
yScale
|
|
90
|
+
};
|
|
91
|
+
}).filter(info => info !== null);
|
|
92
|
+
}, [labels, getSeries, getSeriesData, getYSerializableScale]);
|
|
93
|
+
const xScale = getXSerializableScale();
|
|
94
|
+
const xAxis = getXAxis();
|
|
95
|
+
const dataIndex = useDerivedValue(() => {
|
|
96
|
+
var _scrubberPosition$val;
|
|
97
|
+
return (_scrubberPosition$val = scrubberPosition.value) != null ? _scrubberPosition$val : Math.max(0, dataLength - 1);
|
|
98
|
+
}, [scrubberPosition, dataLength]);
|
|
99
|
+
const dataX = useDerivedValue(() => {
|
|
100
|
+
if (xAxis != null && xAxis.data && Array.isArray(xAxis.data) && xAxis.data[dataIndex.value] !== undefined) {
|
|
101
|
+
const dataValue = xAxis.data[dataIndex.value];
|
|
102
|
+
return typeof dataValue === 'string' ? dataIndex.value : dataValue;
|
|
103
|
+
}
|
|
104
|
+
return dataIndex.value;
|
|
105
|
+
}, [xAxis, dataIndex]);
|
|
106
|
+
const allLabelPositions = useDerivedValue(() => {
|
|
107
|
+
const sharedPixelX = dataX.value !== undefined && xScale ? applySerializableScale(dataX.value, xScale) : 0;
|
|
108
|
+
const desiredPositions = seriesInfo.map(info => {
|
|
109
|
+
let dataY;
|
|
110
|
+
if (xScale && info.yScale) {
|
|
111
|
+
if (info.sourceData && dataIndex.value !== undefined && dataIndex.value >= 0 && dataIndex.value < info.sourceData.length) {
|
|
112
|
+
const dataValue = info.sourceData[dataIndex.value];
|
|
113
|
+
if (Array.isArray(dataValue)) {
|
|
114
|
+
const validValues = dataValue.filter(val => val !== null);
|
|
115
|
+
if (validValues.length >= 1) {
|
|
116
|
+
dataY = validValues[validValues.length - 1];
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (dataY !== undefined && info.yScale) {
|
|
122
|
+
return {
|
|
123
|
+
seriesId: info.seriesId,
|
|
124
|
+
x: sharedPixelX,
|
|
125
|
+
desiredY: applySerializableScale(dataY, info.yScale)
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Return null for invalid data
|
|
130
|
+
return null;
|
|
131
|
+
});
|
|
132
|
+
const maxLabelHeight = Math.max(...Object.values(labelDimensions).map(dim => dim.height));
|
|
133
|
+
const maxLabelWidth = Math.max(...Object.values(labelDimensions).map(dim => dim.width));
|
|
134
|
+
const validPositions = desiredPositions.filter(pos => pos !== null);
|
|
135
|
+
|
|
136
|
+
// Convert to LabelDimension format expected by utility
|
|
137
|
+
const dimensions = validPositions.map(pos => {
|
|
138
|
+
var _trackedDimensions$wi, _trackedDimensions$he;
|
|
139
|
+
const trackedDimensions = labelDimensions[pos.seriesId];
|
|
140
|
+
return {
|
|
141
|
+
seriesId: pos.seriesId,
|
|
142
|
+
width: (_trackedDimensions$wi = trackedDimensions == null ? void 0 : trackedDimensions.width) != null ? _trackedDimensions$wi : maxLabelWidth,
|
|
143
|
+
height: (_trackedDimensions$he = trackedDimensions == null ? void 0 : trackedDimensions.height) != null ? _trackedDimensions$he : maxLabelHeight,
|
|
144
|
+
preferredX: pos.x,
|
|
145
|
+
preferredY: pos.desiredY
|
|
146
|
+
};
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
// Calculate Y positions with collision resolution for valid positions only
|
|
150
|
+
const yPositions = calculateLabelYPositions(dimensions, drawingArea, maxLabelHeight, labelMinGap);
|
|
151
|
+
|
|
152
|
+
// Return all positions (including null ones)
|
|
153
|
+
return desiredPositions.map(pos => {
|
|
154
|
+
var _yPositions$get;
|
|
155
|
+
if (!pos) return null;
|
|
156
|
+
return {
|
|
157
|
+
seriesId: pos.seriesId,
|
|
158
|
+
x: pos.x,
|
|
159
|
+
y: (_yPositions$get = yPositions.get(pos.seriesId)) != null ? _yPositions$get : pos.desiredY
|
|
160
|
+
};
|
|
161
|
+
});
|
|
162
|
+
}, [seriesInfo, dataIndex, dataX, xScale, labelDimensions, labelMinGap]);
|
|
163
|
+
const currentPosition = useDerivedValue(() => {
|
|
164
|
+
const pixelX = dataX.value !== undefined && xScale ? applySerializableScale(dataX.value, xScale) : 0;
|
|
165
|
+
const maxWidth = Math.max(...Object.values(labelDimensions).map(dim => dim.width));
|
|
166
|
+
const position = getLabelPosition(pixelX, maxWidth, drawingArea, labelHorizontalOffset);
|
|
167
|
+
return position;
|
|
168
|
+
}, [dataX, xScale, labelDimensions, drawingArea, labelHorizontalOffset]);
|
|
169
|
+
return seriesInfo.map((info, index) => {
|
|
170
|
+
const labelInfo = labels.find(label => label.seriesId === info.seriesId);
|
|
171
|
+
if (!labelInfo) return;
|
|
172
|
+
return /*#__PURE__*/_jsx(PositionedLabel, {
|
|
173
|
+
BeaconLabelComponent: BeaconLabelComponent,
|
|
174
|
+
color: labelInfo.color,
|
|
175
|
+
index: index,
|
|
176
|
+
label: labelInfo.label,
|
|
177
|
+
labelFont: labelFont,
|
|
178
|
+
labelHorizontalOffset: labelHorizontalOffset,
|
|
179
|
+
onDimensionsChange: handleDimensionsChange,
|
|
180
|
+
position: currentPosition,
|
|
181
|
+
positions: allLabelPositions,
|
|
182
|
+
seriesId: info.seriesId
|
|
183
|
+
}, info.seriesId);
|
|
184
|
+
});
|
|
185
|
+
});
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import React, { useCallback, useMemo } from 'react';
|
|
2
|
+
import { Platform } from 'react-native';
|
|
3
|
+
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
|
|
4
|
+
import { runOnJS, useAnimatedReaction, useSharedValue } from 'react-native-reanimated';
|
|
5
|
+
import { Haptics } from '@coinbase/cds-mobile/utils/haptics';
|
|
6
|
+
import { useCartesianChartContext } from '../ChartProvider';
|
|
7
|
+
import { invertSerializableScale, ScrubberContext } from '../utils';
|
|
8
|
+
import { getPointOnSerializableScale } from '../utils/point';
|
|
9
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
10
|
+
/**
|
|
11
|
+
* A component which encapsulates the ScrubberContext.
|
|
12
|
+
* It depends on a ChartContext in order to provide accurate touch tracking.
|
|
13
|
+
*/
|
|
14
|
+
export const ScrubberProvider = _ref => {
|
|
15
|
+
let {
|
|
16
|
+
children,
|
|
17
|
+
enableScrubbing,
|
|
18
|
+
onScrubberPositionChange,
|
|
19
|
+
allowOverflowGestures
|
|
20
|
+
} = _ref;
|
|
21
|
+
const chartContext = useCartesianChartContext();
|
|
22
|
+
if (!chartContext) {
|
|
23
|
+
throw new Error('ScrubberProvider must be used within a ChartContext');
|
|
24
|
+
}
|
|
25
|
+
const {
|
|
26
|
+
getXSerializableScale,
|
|
27
|
+
getXAxis
|
|
28
|
+
} = chartContext;
|
|
29
|
+
const scrubberPosition = useSharedValue(undefined);
|
|
30
|
+
const xAxis = useMemo(() => getXAxis(), [getXAxis]);
|
|
31
|
+
const xScale = useMemo(() => getXSerializableScale(), [getXSerializableScale]);
|
|
32
|
+
const getDataIndexFromX = useCallback(touchX => {
|
|
33
|
+
'worklet';
|
|
34
|
+
|
|
35
|
+
if (!xScale || !xAxis) return 0;
|
|
36
|
+
if (xScale.type === 'band') {
|
|
37
|
+
const [domainMin, domainMax] = xScale.domain;
|
|
38
|
+
const categoryCount = domainMax - domainMin + 1;
|
|
39
|
+
let closestIndex = 0;
|
|
40
|
+
let closestDistance = Infinity;
|
|
41
|
+
for (let i = 0; i < categoryCount; i++) {
|
|
42
|
+
const xPos = getPointOnSerializableScale(i, xScale);
|
|
43
|
+
if (xPos !== undefined) {
|
|
44
|
+
const distance = Math.abs(touchX - xPos);
|
|
45
|
+
if (distance < closestDistance) {
|
|
46
|
+
closestDistance = distance;
|
|
47
|
+
closestIndex = i;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return closestIndex;
|
|
52
|
+
} else {
|
|
53
|
+
// For numeric scales with axis data, find the nearest data point
|
|
54
|
+
const axisData = xAxis.data;
|
|
55
|
+
if (axisData && Array.isArray(axisData) && typeof axisData[0] === 'number') {
|
|
56
|
+
// We have numeric axis data - find the closest data point
|
|
57
|
+
const numericData = axisData;
|
|
58
|
+
let closestIndex = 0;
|
|
59
|
+
let closestDistance = Infinity;
|
|
60
|
+
for (let i = 0; i < numericData.length; i++) {
|
|
61
|
+
const xValue = numericData[i];
|
|
62
|
+
const xPos = getPointOnSerializableScale(xValue, xScale);
|
|
63
|
+
if (xPos !== undefined) {
|
|
64
|
+
const distance = Math.abs(touchX - xPos);
|
|
65
|
+
if (distance < closestDistance) {
|
|
66
|
+
closestDistance = distance;
|
|
67
|
+
closestIndex = i;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return closestIndex;
|
|
72
|
+
} else {
|
|
73
|
+
var _domain$min, _domain$max;
|
|
74
|
+
const xValue = invertSerializableScale(touchX, xScale);
|
|
75
|
+
const dataIndex = Math.round(xValue);
|
|
76
|
+
const domain = xAxis.domain;
|
|
77
|
+
return Math.max((_domain$min = domain.min) != null ? _domain$min : 0, Math.min(dataIndex, (_domain$max = domain.max) != null ? _domain$max : 0));
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}, [xAxis, xScale]);
|
|
81
|
+
const handleStartEndHaptics = useCallback(() => {
|
|
82
|
+
void Haptics.lightImpact();
|
|
83
|
+
}, []);
|
|
84
|
+
useAnimatedReaction(() => scrubberPosition.value, (currentValue, previousValue) => {
|
|
85
|
+
// Confirm changes here and inside of our gesture handler before calling JS thread
|
|
86
|
+
// To prevent any rerenders
|
|
87
|
+
if (onScrubberPositionChange !== undefined && currentValue !== previousValue) {
|
|
88
|
+
runOnJS(onScrubberPositionChange)(currentValue);
|
|
89
|
+
}
|
|
90
|
+
}, [onScrubberPositionChange]);
|
|
91
|
+
|
|
92
|
+
// Create the long press pan gesture
|
|
93
|
+
const longPressGesture = useMemo(() => Gesture.Pan().activateAfterLongPress(110).shouldCancelWhenOutside(!allowOverflowGestures).onStart(function onStart(event) {
|
|
94
|
+
runOnJS(handleStartEndHaptics)();
|
|
95
|
+
|
|
96
|
+
// Android does not trigger onUpdate when the gesture starts. This achieves consistent behavior across both iOS and Android
|
|
97
|
+
if (Platform.OS === 'android') {
|
|
98
|
+
const newScrubberPosition = getDataIndexFromX(event.x);
|
|
99
|
+
if (newScrubberPosition !== scrubberPosition.value) {
|
|
100
|
+
scrubberPosition.value = newScrubberPosition;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}).onUpdate(function onUpdate(event) {
|
|
104
|
+
const newScrubberPosition = getDataIndexFromX(event.x);
|
|
105
|
+
if (newScrubberPosition !== scrubberPosition.value) {
|
|
106
|
+
scrubberPosition.value = newScrubberPosition;
|
|
107
|
+
}
|
|
108
|
+
}).onEnd(function onEnd() {
|
|
109
|
+
if (enableScrubbing) {
|
|
110
|
+
runOnJS(handleStartEndHaptics)();
|
|
111
|
+
scrubberPosition.value = undefined;
|
|
112
|
+
}
|
|
113
|
+
}).onTouchesCancelled(function onTouchesCancelled() {
|
|
114
|
+
if (enableScrubbing) {
|
|
115
|
+
scrubberPosition.value = undefined;
|
|
116
|
+
}
|
|
117
|
+
}), [allowOverflowGestures, handleStartEndHaptics, getDataIndexFromX, scrubberPosition, enableScrubbing]);
|
|
118
|
+
const contextValue = useMemo(() => ({
|
|
119
|
+
enableScrubbing: !!enableScrubbing,
|
|
120
|
+
scrubberPosition
|
|
121
|
+
}), [enableScrubbing, scrubberPosition]);
|
|
122
|
+
const content = /*#__PURE__*/_jsx(ScrubberContext.Provider, {
|
|
123
|
+
value: contextValue,
|
|
124
|
+
children: children
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// Wrap with gesture handler only if scrubbing is enabled
|
|
128
|
+
if (enableScrubbing) {
|
|
129
|
+
return /*#__PURE__*/_jsx(GestureDetector, {
|
|
130
|
+
gesture: longPressGesture,
|
|
131
|
+
children: content
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
return content;
|
|
135
|
+
};
|