@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.
Files changed (167) hide show
  1. package/CHANGELOG.md +132 -0
  2. package/dts/chart/CartesianChart.d.ts +92 -7
  3. package/dts/chart/CartesianChart.d.ts.map +1 -1
  4. package/dts/chart/ChartContextBridge.d.ts.map +1 -1
  5. package/dts/chart/ChartProvider.d.ts +3 -0
  6. package/dts/chart/ChartProvider.d.ts.map +1 -1
  7. package/dts/chart/Path.d.ts +36 -13
  8. package/dts/chart/Path.d.ts.map +1 -1
  9. package/dts/chart/PeriodSelector.d.ts +20 -5
  10. package/dts/chart/PeriodSelector.d.ts.map +1 -1
  11. package/dts/chart/area/Area.d.ts +14 -11
  12. package/dts/chart/area/Area.d.ts.map +1 -1
  13. package/dts/chart/area/AreaChart.d.ts +33 -9
  14. package/dts/chart/area/AreaChart.d.ts.map +1 -1
  15. package/dts/chart/area/DottedArea.d.ts.map +1 -1
  16. package/dts/chart/area/GradientArea.d.ts.map +1 -1
  17. package/dts/chart/area/SolidArea.d.ts.map +1 -1
  18. package/dts/chart/axis/Axis.d.ts +22 -42
  19. package/dts/chart/axis/Axis.d.ts.map +1 -1
  20. package/dts/chart/axis/XAxis.d.ts +6 -0
  21. package/dts/chart/axis/XAxis.d.ts.map +1 -1
  22. package/dts/chart/axis/YAxis.d.ts +1 -0
  23. package/dts/chart/axis/YAxis.d.ts.map +1 -1
  24. package/dts/chart/bar/Bar.d.ts +51 -51
  25. package/dts/chart/bar/Bar.d.ts.map +1 -1
  26. package/dts/chart/bar/BarChart.d.ts +56 -11
  27. package/dts/chart/bar/BarChart.d.ts.map +1 -1
  28. package/dts/chart/bar/BarPlot.d.ts +2 -1
  29. package/dts/chart/bar/BarPlot.d.ts.map +1 -1
  30. package/dts/chart/bar/BarStack.d.ts +45 -20
  31. package/dts/chart/bar/BarStack.d.ts.map +1 -1
  32. package/dts/chart/bar/BarStackGroup.d.ts +2 -1
  33. package/dts/chart/bar/BarStackGroup.d.ts.map +1 -1
  34. package/dts/chart/bar/DefaultBar.d.ts.map +1 -1
  35. package/dts/chart/bar/DefaultBarStack.d.ts.map +1 -1
  36. package/dts/chart/gradient/Gradient.d.ts +5 -0
  37. package/dts/chart/gradient/Gradient.d.ts.map +1 -1
  38. package/dts/chart/index.d.ts +1 -0
  39. package/dts/chart/index.d.ts.map +1 -1
  40. package/dts/chart/legend/DefaultLegendEntry.d.ts +5 -0
  41. package/dts/chart/legend/DefaultLegendEntry.d.ts.map +1 -0
  42. package/dts/chart/legend/DefaultLegendShape.d.ts +5 -0
  43. package/dts/chart/legend/DefaultLegendShape.d.ts.map +1 -0
  44. package/dts/chart/legend/Legend.d.ts +168 -0
  45. package/dts/chart/legend/Legend.d.ts.map +1 -0
  46. package/dts/chart/legend/index.d.ts +4 -0
  47. package/dts/chart/legend/index.d.ts.map +1 -0
  48. package/dts/chart/line/DottedLine.d.ts.map +1 -1
  49. package/dts/chart/line/Line.d.ts +23 -19
  50. package/dts/chart/line/Line.d.ts.map +1 -1
  51. package/dts/chart/line/LineChart.d.ts +26 -9
  52. package/dts/chart/line/LineChart.d.ts.map +1 -1
  53. package/dts/chart/line/ReferenceLine.d.ts +1 -0
  54. package/dts/chart/line/ReferenceLine.d.ts.map +1 -1
  55. package/dts/chart/line/SolidLine.d.ts.map +1 -1
  56. package/dts/chart/point/Point.d.ts +26 -2
  57. package/dts/chart/point/Point.d.ts.map +1 -1
  58. package/dts/chart/scrubber/DefaultScrubberBeacon.d.ts +32 -2
  59. package/dts/chart/scrubber/DefaultScrubberBeacon.d.ts.map +1 -1
  60. package/dts/chart/scrubber/DefaultScrubberLabel.d.ts +2 -1
  61. package/dts/chart/scrubber/DefaultScrubberLabel.d.ts.map +1 -1
  62. package/dts/chart/scrubber/Scrubber.d.ts +86 -17
  63. package/dts/chart/scrubber/Scrubber.d.ts.map +1 -1
  64. package/dts/chart/scrubber/ScrubberAccessibilityView.d.ts +12 -0
  65. package/dts/chart/scrubber/ScrubberAccessibilityView.d.ts.map +1 -0
  66. package/dts/chart/scrubber/ScrubberBeaconGroup.d.ts +10 -0
  67. package/dts/chart/scrubber/ScrubberBeaconGroup.d.ts.map +1 -1
  68. package/dts/chart/scrubber/ScrubberBeaconLabelGroup.d.ts +16 -1
  69. package/dts/chart/scrubber/ScrubberBeaconLabelGroup.d.ts.map +1 -1
  70. package/dts/chart/scrubber/ScrubberProvider.d.ts.map +1 -1
  71. package/dts/chart/utils/axis.d.ts +45 -10
  72. package/dts/chart/utils/axis.d.ts.map +1 -1
  73. package/dts/chart/utils/bar.d.ts +190 -0
  74. package/dts/chart/utils/bar.d.ts.map +1 -1
  75. package/dts/chart/utils/chart.d.ts +32 -0
  76. package/dts/chart/utils/chart.d.ts.map +1 -1
  77. package/dts/chart/utils/context.d.ts +21 -6
  78. package/dts/chart/utils/context.d.ts.map +1 -1
  79. package/dts/chart/utils/gradient.d.ts +3 -1
  80. package/dts/chart/utils/gradient.d.ts.map +1 -1
  81. package/dts/chart/utils/path.d.ts +26 -0
  82. package/dts/chart/utils/path.d.ts.map +1 -1
  83. package/dts/chart/utils/point.d.ts +24 -12
  84. package/dts/chart/utils/point.d.ts.map +1 -1
  85. package/dts/chart/utils/scale.d.ts +11 -0
  86. package/dts/chart/utils/scale.d.ts.map +1 -1
  87. package/dts/chart/utils/scrubber.d.ts +2 -1
  88. package/dts/chart/utils/scrubber.d.ts.map +1 -1
  89. package/dts/chart/utils/transition.d.ts +63 -22
  90. package/dts/chart/utils/transition.d.ts.map +1 -1
  91. package/dts/sparkline/Sparkline.d.ts +2 -1
  92. package/dts/sparkline/Sparkline.d.ts.map +1 -1
  93. package/dts/sparkline/SparklineArea.d.ts +2 -1
  94. package/dts/sparkline/SparklineArea.d.ts.map +1 -1
  95. package/dts/sparkline/SparklineGradient.d.ts +2 -1
  96. package/dts/sparkline/SparklineGradient.d.ts.map +1 -1
  97. package/dts/sparkline/sparkline-interactive/SparklineInteractive.d.ts +2 -1
  98. package/dts/sparkline/sparkline-interactive/SparklineInteractive.d.ts.map +1 -1
  99. package/esm/chart/CartesianChart.js +176 -82
  100. package/esm/chart/ChartContextBridge.js +14 -3
  101. package/esm/chart/ChartProvider.js +2 -2
  102. package/esm/chart/Path.js +34 -29
  103. package/esm/chart/PeriodSelector.js +5 -1
  104. package/esm/chart/__stories__/CartesianChart.stories.js +16 -80
  105. package/esm/chart/__stories__/ChartAccessibility.stories.js +721 -0
  106. package/esm/chart/__stories__/ChartTransitions.stories.js +625 -0
  107. package/esm/chart/__stories__/PeriodSelector.stories.js +99 -1
  108. package/esm/chart/area/Area.js +21 -9
  109. package/esm/chart/area/AreaChart.js +18 -13
  110. package/esm/chart/area/DottedArea.js +28 -18
  111. package/esm/chart/area/GradientArea.js +14 -7
  112. package/esm/chart/area/SolidArea.js +6 -2
  113. package/esm/chart/area/__stories__/AreaChart.stories.js +47 -5
  114. package/esm/chart/axis/Axis.js +5 -41
  115. package/esm/chart/axis/XAxis.js +116 -47
  116. package/esm/chart/axis/YAxis.js +105 -26
  117. package/esm/chart/axis/__stories__/Axis.stories.js +324 -48
  118. package/esm/chart/bar/Bar.js +17 -15
  119. package/esm/chart/bar/BarChart.js +38 -33
  120. package/esm/chart/bar/BarPlot.js +40 -45
  121. package/esm/chart/bar/BarStack.js +92 -475
  122. package/esm/chart/bar/BarStackGroup.js +37 -27
  123. package/esm/chart/bar/DefaultBar.js +27 -18
  124. package/esm/chart/bar/DefaultBarStack.js +25 -9
  125. package/esm/chart/bar/__stories__/BarChart.stories.js +728 -54
  126. package/esm/chart/gradient/Gradient.js +2 -1
  127. package/esm/chart/index.js +1 -0
  128. package/esm/chart/legend/DefaultLegendEntry.js +42 -0
  129. package/esm/chart/legend/DefaultLegendShape.js +64 -0
  130. package/esm/chart/legend/Legend.js +59 -0
  131. package/esm/chart/legend/__stories__/Legend.stories.js +574 -0
  132. package/esm/chart/legend/index.js +3 -0
  133. package/esm/chart/line/DottedLine.js +6 -2
  134. package/esm/chart/line/Line.js +42 -38
  135. package/esm/chart/line/LineChart.js +36 -12
  136. package/esm/chart/line/SolidLine.js +6 -2
  137. package/esm/chart/line/__stories__/LineChart.stories.js +236 -590
  138. package/esm/chart/line/__stories__/ReferenceLine.stories.js +95 -1
  139. package/esm/chart/point/Point.js +35 -36
  140. package/esm/chart/scrubber/DefaultScrubberBeacon.js +41 -38
  141. package/esm/chart/scrubber/DefaultScrubberLabel.js +26 -10
  142. package/esm/chart/scrubber/Scrubber.js +67 -35
  143. package/esm/chart/scrubber/ScrubberAccessibilityView.js +177 -0
  144. package/esm/chart/scrubber/ScrubberBeaconGroup.js +30 -22
  145. package/esm/chart/scrubber/ScrubberBeaconLabelGroup.js +35 -8
  146. package/esm/chart/scrubber/ScrubberProvider.js +29 -24
  147. package/esm/chart/scrubber/__stories__/Scrubber.stories.js +946 -0
  148. package/esm/chart/utils/axis.js +88 -44
  149. package/esm/chart/utils/bar.js +820 -0
  150. package/esm/chart/utils/chart.js +34 -7
  151. package/esm/chart/utils/context.js +7 -0
  152. package/esm/chart/utils/gradient.js +8 -4
  153. package/esm/chart/utils/path.js +91 -61
  154. package/esm/chart/utils/point.js +92 -39
  155. package/esm/chart/utils/scale.js +13 -2
  156. package/esm/chart/utils/scrubber.js +12 -5
  157. package/esm/chart/utils/transition.js +108 -60
  158. package/esm/sparkline/Sparkline.js +2 -1
  159. package/esm/sparkline/SparklineArea.js +2 -1
  160. package/esm/sparkline/SparklineGradient.js +2 -1
  161. package/esm/sparkline/__figma__/Sparkline.figma.js +1 -1
  162. package/esm/sparkline/sparkline-interactive/SparklineInteractive.js +2 -1
  163. package/esm/sparkline/sparkline-interactive/__figma__/SparklineInteractive.figma.js +1 -1
  164. package/esm/sparkline/sparkline-interactive-header/__figma__/SparklineInteractiveHeader.figma.js +1 -1
  165. package/esm/sparkline/sparkline-interactive-header/__stories__/SparklineInteractiveHeader.stories.js +2 -0
  166. package/package.json +5 -6
  167. 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;
@@ -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
- transition = defaultTransition,
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, transition);
76
- animatedY.value = buildTransition(pixelCoordinate.y, transition);
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, transition]);
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, transition);
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, transition]);
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
- // Check if point is within drawing area
113
- const isWithinDrawingArea = useDerivedValue(() => {
114
- return animatedX.value >= drawingArea.x && animatedX.value <= drawingArea.x + drawingArea.width && animatedY.value >= drawingArea.y && animatedY.value <= drawingArea.y + drawingArea.height;
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.value ? baseOpacity : 0;
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
- // Animated rendering
164
- return /*#__PURE__*/_jsxs(_Fragment, {
165
- children: [/*#__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
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
- 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 { forwardRef, memo, useImperativeHandle, useMemo } from 'react';
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 radius = 5;
12
- const strokeWidth = 2;
10
+ const defaultRadius = 5;
11
+ const defaultStrokeWidth = 2;
13
12
  const pulseOpacityStart = 0.5;
14
13
  const pulseOpacityEnd = 0;
15
- const pulseRadiusStart = 10;
16
- const pulseRadiusEnd = 15;
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
- const animatedX = useSharedValue(0);
64
- const animatedY = useSharedValue(0);
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 (!idlePulse) {
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, immediatePulseTransition);
117
- pulseRadius.value = buildTransition(pulseRadiusEnd, immediatePulseTransition);
124
+ pulseOpacity.value = buildTransition(pulseOpacityEnd, pulseTransition);
125
+ pulseRadius.value = buildTransition(pulseRadiusEnd, pulseTransition);
118
126
  }
119
127
  }
120
- }), [idlePulse, pulseOpacity, pulseRadius, pulseTransition]);
128
+ }), [idlePulseShared, pulseOpacity, pulseRadius, pulseTransition, pulseRadiusStart, pulseRadiusEnd]);
121
129
 
122
130
  // Watch idlePulse changes and control continuous pulse
123
- useAnimatedReaction(() => idlePulse, (current, previous) => {
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), buildTransition(pulseOpacityStart, resetWithDelay)), -1,
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), buildTransition(pulseRadiusStart, resetWithDelay)), -1,
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
- }, [animate, pulseTransition, pulseRepeatDelay]);
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 = ["verticalAlignment", "dy", "boundsInset"];
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
- * It will also center the label vertically with the top available area.
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
- verticalAlignment = 'middle',
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
- boundsInset: boundsInset,
25
- dy: dy != null ? dy : -0.5 * drawingArea.y,
26
- verticalAlignment: verticalAlignment
41
+ dx: dx,
42
+ dy: dy
27
43
  }, props));
28
44
  });