@coinbase/cds-mobile-visualization 3.4.0-beta.10 → 3.4.0-beta.12

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.
@@ -1,15 +1,14 @@
1
- const _excluded = ["axisId", "position", "showGrid", "requestedTickCount", "ticks", "tickLabelFormatter", "TickLabelComponent", "GridLineComponent", "LineComponent", "TickMarkLineComponent", "tickMarkLabelGap", "minTickLabelGap", "showTickMarks", "showLine", "tickMarkSize", "tickInterval", "label", "labelGap", "width"];
1
+ const _excluded = ["axisId", "position", "showGrid", "requestedTickCount", "ticks", "tickLabelFormatter", "TickLabelComponent", "GridLineComponent", "LineComponent", "TickMarkLineComponent", "tickMarkLabelGap", "minTickLabelGap", "showTickMarks", "showLine", "tickMarkSize", "tickInterval", "label", "labelGap", "width", "bandGridLinePlacement", "bandTickMarkPlacement"];
2
2
  function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
3
3
  import { memo, useCallback, useEffect, useId, useMemo } from 'react';
4
4
  import { useTheme } from '@coinbase/cds-mobile/hooks/useTheme';
5
5
  import { Group, vec } from '@shopify/react-native-skia';
6
6
  import { useCartesianChartContext } from '../ChartProvider';
7
7
  import { DottedLine } from '../line/DottedLine';
8
- import { ReferenceLine } from '../line/ReferenceLine';
9
8
  import { SolidLine } from '../line/SolidLine';
10
9
  import { ChartText } from '../text/ChartText';
11
10
  import { ChartTextGroup } from '../text/ChartTextGroup';
12
- import { getAxisTicksData, isCategoricalScale, lineToPath } from '../utils';
11
+ import { getAxisTicksData, getPointOnScale, isCategoricalScale, lineToPath, toPointAnchor } from '../utils';
13
12
  import { DefaultAxisTickLabel } from './DefaultAxisTickLabel';
14
13
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
15
14
  const AXIS_WIDTH = 44;
@@ -34,13 +33,16 @@ export const YAxis = /*#__PURE__*/memo(_ref => {
34
33
  tickInterval,
35
34
  label,
36
35
  labelGap = 4,
37
- width = label ? AXIS_WIDTH + LABEL_SIZE : AXIS_WIDTH
36
+ width = label ? AXIS_WIDTH + LABEL_SIZE : AXIS_WIDTH,
37
+ bandGridLinePlacement = 'edges',
38
+ bandTickMarkPlacement = 'middle'
38
39
  } = _ref,
39
40
  props = _objectWithoutPropertiesLoose(_ref, _excluded);
40
41
  const theme = useTheme();
41
42
  const registrationId = useId();
42
43
  const {
43
44
  animate,
45
+ drawingArea,
44
46
  getYScale,
45
47
  getYAxis,
46
48
  registerAxis,
@@ -50,10 +52,6 @@ export const YAxis = /*#__PURE__*/memo(_ref => {
50
52
  const yScale = getYScale(axisId);
51
53
  const yAxis = getYAxis(axisId);
52
54
  const axisBounds = getAxisBounds(registrationId);
53
-
54
- // Note: gridOpacity not currently used in Skia version
55
- // const gridOpacity = useSharedValue(1);
56
-
57
55
  useEffect(() => {
58
56
  registerAxis(registrationId, position, width);
59
57
  return () => unregisterAxis(registrationId);
@@ -102,6 +100,72 @@ export const YAxis = /*#__PURE__*/memo(_ref => {
102
100
  tickInterval: tickInterval
103
101
  });
104
102
  }, [ticks, yScale, requestedTickCount, tickInterval, yAxis == null ? void 0 : yAxis.data]);
103
+ const isBandScale = useMemo(() => {
104
+ if (!yScale) return false;
105
+ return isCategoricalScale(yScale);
106
+ }, [yScale]);
107
+
108
+ // Compute grid line positions (including bounds closing line for band scales)
109
+ const gridLinePositions = useMemo(() => {
110
+ if (!yScale) return [];
111
+ return ticksData.flatMap((tick, index) => {
112
+ if (!isBandScale) {
113
+ return [{
114
+ y: tick.position,
115
+ key: "grid-" + tick.tick + "-" + index
116
+ }];
117
+ }
118
+ const bandScale = yScale;
119
+ const isLastTick = index === ticksData.length - 1;
120
+ const isEdges = bandGridLinePlacement === 'edges';
121
+ const startY = getPointOnScale(tick.tick, bandScale, toPointAnchor(bandGridLinePlacement));
122
+ const positions = [{
123
+ y: startY,
124
+ key: "grid-" + tick.tick + "-" + index
125
+ }];
126
+
127
+ // For edges on last tick, add the closing line at stepEnd
128
+ if (isLastTick && isEdges) {
129
+ const endY = getPointOnScale(tick.tick, bandScale, 'stepEnd');
130
+ positions.push({
131
+ y: endY,
132
+ key: "grid-" + tick.tick + "-" + index + "-end"
133
+ });
134
+ }
135
+ return positions;
136
+ });
137
+ }, [ticksData, yScale, isBandScale, bandGridLinePlacement]);
138
+
139
+ // Compute tick mark positions (including bounds closing tick for band scales)
140
+ const tickMarkPositions = useMemo(() => {
141
+ if (!yScale) return [];
142
+ return ticksData.flatMap((tick, index) => {
143
+ if (!isBandScale) {
144
+ return [{
145
+ y: tick.position,
146
+ key: "tick-mark-" + tick.tick + "-" + index
147
+ }];
148
+ }
149
+ const bandScale = yScale;
150
+ const isLastTick = index === ticksData.length - 1;
151
+ const isEdges = bandTickMarkPlacement === 'edges';
152
+ const startY = getPointOnScale(tick.tick, bandScale, toPointAnchor(bandTickMarkPlacement));
153
+ const positions = [{
154
+ y: startY,
155
+ key: "tick-mark-" + tick.tick + "-" + index
156
+ }];
157
+
158
+ // For edges on last tick, add the closing tick mark at stepEnd
159
+ if (isLastTick && isEdges) {
160
+ const endY = getPointOnScale(tick.tick, bandScale, 'stepEnd');
161
+ positions.push({
162
+ y: endY,
163
+ key: "tick-mark-" + tick.tick + "-" + index + "-end"
164
+ });
165
+ }
166
+ return positions;
167
+ });
168
+ }, [ticksData, yScale, isBandScale, bandTickMarkPlacement]);
105
169
  const chartTextData = useMemo(() => {
106
170
  if (!axisBounds) return null;
107
171
  return ticksData.map(tick => {
@@ -122,17 +186,29 @@ export const YAxis = /*#__PURE__*/memo(_ref => {
122
186
  if (!yScale || !axisBounds) return;
123
187
  const labelX = position === 'left' ? axisBounds.x + LABEL_SIZE / 2 : axisBounds.x + axisBounds.width - LABEL_SIZE / 2;
124
188
  const labelY = axisBounds.y + axisBounds.height / 2;
189
+
190
+ // Pre-compute tick mark X coordinates
191
+ const tickXLeft = axisBounds.x;
192
+ const tickXRight = axisBounds.x + axisBounds.width;
193
+ const tickXStart = position === 'left' ? tickXRight : tickXLeft;
194
+ const tickXEnd = position === 'left' ? tickXRight - tickMarkSize : tickXLeft + tickMarkSize;
195
+
196
+ // Note: Unlike web, mobile renders grid lines and tick marks immediately without fade animation.
197
+ // This is because Skia can measure text dimensions synchronously, so there's no need to hide
198
+ // elements while waiting for measurements (web uses async ResizeObserver).
125
199
  return /*#__PURE__*/_jsxs(Group, {
126
200
  children: [showGrid && /*#__PURE__*/_jsx(Group, {
127
- children: ticksData.map((tick, index) => {
128
- const horizontalLine = /*#__PURE__*/_jsx(ReferenceLine, {
129
- LineComponent: GridLineComponent,
130
- dataY: tick.tick,
131
- yAxisId: axisId
132
- });
133
- return /*#__PURE__*/_jsx(Group, {
134
- children: horizontalLine
135
- }, "grid-" + tick.tick + "-" + index);
201
+ children: gridLinePositions.map(_ref2 => {
202
+ let {
203
+ y,
204
+ key
205
+ } = _ref2;
206
+ return /*#__PURE__*/_jsx(GridLineComponent, {
207
+ animate: false,
208
+ clipPath: null,
209
+ d: lineToPath(drawingArea.x, y, drawingArea.x + drawingArea.width, y),
210
+ stroke: theme.color.bgLine
211
+ }, key);
136
212
  })
137
213
  }), chartTextData && /*#__PURE__*/_jsx(ChartTextGroup, {
138
214
  prioritizeEndLabels: true,
@@ -140,18 +216,19 @@ export const YAxis = /*#__PURE__*/memo(_ref => {
140
216
  labels: chartTextData,
141
217
  minGap: minTickLabelGap
142
218
  }), axisBounds && showTickMarks && /*#__PURE__*/_jsx(Group, {
143
- children: ticksData.map((tick, index) => {
144
- const tickX = position === 'left' ? axisBounds.x + axisBounds.width : axisBounds.x;
145
- const tickMarkSizePixels = tickMarkSize;
146
- const tickX2 = position === 'left' ? axisBounds.x + axisBounds.width - tickMarkSizePixels : axisBounds.x + tickMarkSizePixels;
219
+ children: tickMarkPositions.map(_ref3 => {
220
+ let {
221
+ y,
222
+ key
223
+ } = _ref3;
147
224
  return /*#__PURE__*/_jsx(TickMarkLineComponent, {
148
225
  animate: false,
149
226
  clipPath: null,
150
- d: lineToPath(tickX, tick.position, tickX2, tick.position),
227
+ d: lineToPath(tickXStart, y, tickXEnd, y),
151
228
  stroke: theme.color.fg,
152
229
  strokeCap: "square",
153
230
  strokeWidth: 1
154
- }, "tick-mark-" + tick.tick + "-" + index);
231
+ }, key);
155
232
  })
156
233
  }), showLine && /*#__PURE__*/_jsx(LineComponent, {
157
234
  animate: false,
@@ -2,6 +2,7 @@ function _extends() { return _extends = Object.assign ? Object.assign.bind() : f
2
2
  import { memo, useCallback, useMemo } from 'react';
3
3
  import { Example, ExampleScreen } from '@coinbase/cds-mobile/examples/ExampleScreen';
4
4
  import { useTheme } from '@coinbase/cds-mobile/hooks/useTheme';
5
+ import { BarPlot } from '../../bar';
5
6
  import { CartesianChart } from '../../CartesianChart';
6
7
  import { LineChart, SolidLine } from '../../line';
7
8
  import { Line } from '../../line/Line';
@@ -194,6 +195,105 @@ const MultipleYAxesExample = () => /*#__PURE__*/_jsxs(CartesianChart, {
194
195
  seriesId: "log"
195
196
  }), /*#__PURE__*/_jsx(Scrubber, {})]
196
197
  });
198
+ const AxesOnAllSides = () => {
199
+ const theme = useTheme();
200
+ const data = [30, 45, 60, 80, 55, 40, 65];
201
+ const labels = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
202
+ return /*#__PURE__*/_jsxs(CartesianChart, {
203
+ height: defaultChartHeight,
204
+ series: [{
205
+ id: 'data',
206
+ data,
207
+ color: theme.color.accentBoldBlue
208
+ }],
209
+ xAxis: {
210
+ data: labels
211
+ },
212
+ yAxis: {
213
+ domain: {
214
+ min: 0,
215
+ max: 100
216
+ }
217
+ },
218
+ children: [/*#__PURE__*/_jsx(XAxis, {
219
+ showLine: true,
220
+ showTickMarks: true,
221
+ label: "Bottom Axis",
222
+ position: "bottom",
223
+ ticks: labels.map((label, index) => index)
224
+ }), /*#__PURE__*/_jsx(XAxis, {
225
+ showLine: true,
226
+ showTickMarks: true,
227
+ label: "Top Axis",
228
+ position: "top",
229
+ ticks: labels.map((label, index) => index)
230
+ }), /*#__PURE__*/_jsx(YAxis, {
231
+ showLine: true,
232
+ showTickMarks: true,
233
+ label: "Left Axis",
234
+ position: "left"
235
+ }), /*#__PURE__*/_jsx(YAxis, {
236
+ showLine: true,
237
+ showTickMarks: true,
238
+ label: "Right Axis",
239
+ position: "right"
240
+ }), /*#__PURE__*/_jsx(Line, {
241
+ curve: "natural",
242
+ seriesId: "data"
243
+ })]
244
+ });
245
+ };
246
+ const CustomTickMarkSizes = () => {
247
+ const theme = useTheme();
248
+ const data = [25, 50, 75, 60, 45, 80, 35];
249
+ return /*#__PURE__*/_jsxs(CartesianChart, {
250
+ height: 300,
251
+ series: [{
252
+ id: 'data',
253
+ data,
254
+ color: theme.color.accentBoldGreen
255
+ }],
256
+ xAxis: {
257
+ data: ['A', 'B', 'C', 'D', 'E', 'F', 'G']
258
+ },
259
+ yAxis: {
260
+ domain: {
261
+ min: 0,
262
+ max: 100
263
+ }
264
+ },
265
+ children: [/*#__PURE__*/_jsx(XAxis, {
266
+ showLine: true,
267
+ showTickMarks: true,
268
+ label: "tickMarkSize=4 (default)",
269
+ tickMarkSize: 4
270
+ }), /*#__PURE__*/_jsx(XAxis, {
271
+ showLine: true,
272
+ showTickMarks: true,
273
+ height: 60,
274
+ label: "tickMarkSize=8",
275
+ position: "top",
276
+ tickMarkSize: 8
277
+ }), /*#__PURE__*/_jsx(YAxis, {
278
+ showLine: true,
279
+ showTickMarks: true,
280
+ label: "tickMarkSize=16",
281
+ position: "left",
282
+ tickMarkSize: 16,
283
+ width: 76
284
+ }), /*#__PURE__*/_jsx(YAxis, {
285
+ showLine: true,
286
+ showTickMarks: true,
287
+ label: "tickMarkSize=24",
288
+ position: "right",
289
+ tickMarkSize: 24,
290
+ width: 84
291
+ }), /*#__PURE__*/_jsx(Line, {
292
+ curve: "monotone",
293
+ seriesId: "data"
294
+ })]
295
+ });
296
+ };
197
297
  const DomainLimitType = _ref => {
198
298
  let {
199
299
  limit
@@ -249,6 +349,97 @@ const DomainLimitType = _ref => {
249
349
  }), /*#__PURE__*/_jsx(Scrubber, {})]
250
350
  });
251
351
  };
352
+
353
+ // Band scale with tick filtering - show every other tick
354
+ const BandScaleTickFiltering = () => /*#__PURE__*/_jsxs(CartesianChart, {
355
+ height: defaultChartHeight,
356
+ inset: 8,
357
+ series: [{
358
+ id: 'data',
359
+ data: [10, 22, 29, 45, 98, 45, 22, 35, 42, 18, 55, 67]
360
+ }],
361
+ xAxis: {
362
+ scaleType: 'band',
363
+ data: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
364
+ },
365
+ yAxis: {
366
+ domain: {
367
+ min: 0
368
+ }
369
+ },
370
+ children: [/*#__PURE__*/_jsx(XAxis, {
371
+ showGrid: true,
372
+ showLine: true,
373
+ showTickMarks: true,
374
+ label: "ticks={(i) => i % 2 === 0}",
375
+ ticks: i => i % 2 === 0
376
+ }), /*#__PURE__*/_jsx(BarPlot, {})]
377
+ });
378
+
379
+ // Band scale with explicit ticks array
380
+ const BandScaleExplicitTicks = () => /*#__PURE__*/_jsxs(CartesianChart, {
381
+ height: defaultChartHeight,
382
+ inset: 8,
383
+ series: [{
384
+ id: 'data',
385
+ data: [10, 22, 29, 45, 98, 45, 22]
386
+ }],
387
+ xAxis: {
388
+ scaleType: 'band',
389
+ data: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
390
+ },
391
+ yAxis: {
392
+ domain: {
393
+ min: 0
394
+ }
395
+ },
396
+ children: [/*#__PURE__*/_jsx(XAxis, {
397
+ showGrid: true,
398
+ showLine: true,
399
+ showTickMarks: true,
400
+ label: "ticks={[0, 3, 6]} (first, middle, last)",
401
+ ticks: [0, 3, 6]
402
+ }), /*#__PURE__*/_jsx(BarPlot, {})]
403
+ });
404
+
405
+ // Line chart on band scale - comparing grid placements
406
+ const LineChartOnBandScale = _ref2 => {
407
+ let {
408
+ bandGridLinePlacement
409
+ } = _ref2;
410
+ const theme = useTheme();
411
+ return /*#__PURE__*/_jsxs(CartesianChart, {
412
+ height: 180,
413
+ inset: 8,
414
+ series: [{
415
+ id: 'line1',
416
+ data: [10, 22, 29, 45, 98, 45, 22],
417
+ color: theme.color.accentBoldBlue
418
+ }],
419
+ xAxis: {
420
+ scaleType: 'band',
421
+ data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
422
+ },
423
+ yAxis: {
424
+ domain: {
425
+ min: 0
426
+ }
427
+ },
428
+ children: [/*#__PURE__*/_jsx(XAxis, {
429
+ showGrid: true,
430
+ showLine: true,
431
+ showTickMarks: true,
432
+ bandGridLinePlacement: bandGridLinePlacement,
433
+ bandTickMarkPlacement: bandGridLinePlacement,
434
+ label: "bandGridLinePlacement: " + bandGridLinePlacement
435
+ }), /*#__PURE__*/_jsx(YAxis, {
436
+ showGrid: true,
437
+ position: "left"
438
+ }), /*#__PURE__*/_jsx(Line, {
439
+ seriesId: "line1"
440
+ })]
441
+ });
442
+ };
252
443
  const AxisStories = () => {
253
444
  return /*#__PURE__*/_jsxs(ExampleScreen, {
254
445
  children: [/*#__PURE__*/_jsx(Example, {
@@ -270,6 +461,74 @@ const AxisStories = () => {
270
461
  children: /*#__PURE__*/_jsx(DomainLimitType, {
271
462
  limit: "nice"
272
463
  })
464
+ }), /*#__PURE__*/_jsx(Example, {
465
+ title: "Band Axis Grid Alignment",
466
+ children: /*#__PURE__*/_jsxs(CartesianChart, {
467
+ height: 350,
468
+ inset: 8,
469
+ series: [{
470
+ id: 'prices',
471
+ data: [10, 22, 29, 45, 98, 45, 22]
472
+ }],
473
+ xAxis: {
474
+ scaleType: 'band',
475
+ data: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
476
+ },
477
+ yAxis: {
478
+ domain: {
479
+ min: 0
480
+ }
481
+ },
482
+ children: [/*#__PURE__*/_jsx(XAxis, {
483
+ showGrid: true,
484
+ showLine: true,
485
+ showTickMarks: true,
486
+ label: "Default"
487
+ }), /*#__PURE__*/_jsx(XAxis, {
488
+ showLine: true,
489
+ showTickMarks: true,
490
+ bandTickMarkPlacement: "start",
491
+ label: "Start"
492
+ }), /*#__PURE__*/_jsx(XAxis, {
493
+ showLine: true,
494
+ showTickMarks: true,
495
+ bandTickMarkPlacement: "end",
496
+ label: "End"
497
+ }), /*#__PURE__*/_jsx(XAxis, {
498
+ showLine: true,
499
+ showTickMarks: true,
500
+ bandTickMarkPlacement: "middle",
501
+ label: "Middle"
502
+ }), /*#__PURE__*/_jsx(XAxis, {
503
+ showLine: true,
504
+ showTickMarks: true,
505
+ bandTickMarkPlacement: "edges",
506
+ label: "Edges"
507
+ }), /*#__PURE__*/_jsx(BarPlot, {})]
508
+ })
509
+ }), /*#__PURE__*/_jsx(Example, {
510
+ title: "Band Scale - Tick Filtering",
511
+ children: /*#__PURE__*/_jsx(BandScaleTickFiltering, {})
512
+ }), /*#__PURE__*/_jsx(Example, {
513
+ title: "Band Scale - Explicit Ticks",
514
+ children: /*#__PURE__*/_jsx(BandScaleExplicitTicks, {})
515
+ }), /*#__PURE__*/_jsxs(Example, {
516
+ title: "Line Chart on Band Scale - Grid Positions",
517
+ children: [/*#__PURE__*/_jsx(LineChartOnBandScale, {
518
+ bandGridLinePlacement: "edges"
519
+ }), /*#__PURE__*/_jsx(LineChartOnBandScale, {
520
+ bandGridLinePlacement: "start"
521
+ }), /*#__PURE__*/_jsx(LineChartOnBandScale, {
522
+ bandGridLinePlacement: "middle"
523
+ }), /*#__PURE__*/_jsx(LineChartOnBandScale, {
524
+ bandGridLinePlacement: "end"
525
+ })]
526
+ }), /*#__PURE__*/_jsx(Example, {
527
+ title: "Axes on All Sides",
528
+ children: /*#__PURE__*/_jsx(AxesOnAllSides, {})
529
+ }), /*#__PURE__*/_jsx(Example, {
530
+ title: "Custom Tick Mark Sizes",
531
+ children: /*#__PURE__*/_jsx(CustomTickMarkSizes, {})
273
532
  })]
274
533
  });
275
534
  };
@@ -621,6 +621,34 @@ const ColorMapWithOpacity = () => {
621
621
  }
622
622
  });
623
623
  };
624
+ const BandGridPositionExample = _ref6 => {
625
+ let {
626
+ position
627
+ } = _ref6;
628
+ return /*#__PURE__*/_jsxs(CartesianChart, {
629
+ height: 180,
630
+ inset: 4,
631
+ series: [{
632
+ id: 'data',
633
+ data: [30, 50, 40, 60, 35]
634
+ }],
635
+ xAxis: {
636
+ scaleType: 'band',
637
+ data: ['A', 'B', 'C', 'D', 'E']
638
+ },
639
+ yAxis: {
640
+ domain: {
641
+ min: 0
642
+ }
643
+ },
644
+ children: [/*#__PURE__*/_jsx(XAxis, {
645
+ showGrid: true,
646
+ showLine: true,
647
+ bandGridLinePlacement: position,
648
+ label: position
649
+ }), /*#__PURE__*/_jsx(BarPlot, {})]
650
+ });
651
+ };
624
652
  const BarChartStories = () => {
625
653
  return /*#__PURE__*/_jsxs(ExampleScreen, {
626
654
  children: [/*#__PURE__*/_jsx(Example, {
@@ -662,6 +690,17 @@ const BarChartStories = () => {
662
690
  }), /*#__PURE__*/_jsx(Example, {
663
691
  title: "ColorMap with Opacity",
664
692
  children: /*#__PURE__*/_jsx(ColorMapWithOpacity, {})
693
+ }), /*#__PURE__*/_jsxs(Example, {
694
+ title: "Band Grid Position",
695
+ children: [/*#__PURE__*/_jsx(BandGridPositionExample, {
696
+ position: "edges"
697
+ }), /*#__PURE__*/_jsx(BandGridPositionExample, {
698
+ position: "start"
699
+ }), /*#__PURE__*/_jsx(BandGridPositionExample, {
700
+ position: "middle"
701
+ }), /*#__PURE__*/_jsx(BandGridPositionExample, {
702
+ position: "end"
703
+ })]
665
704
  })]
666
705
  });
667
706
  };
@@ -1,6 +1,5 @@
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';
@@ -60,6 +59,14 @@ export const DefaultScrubberBeacon = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((
60
59
  }, [transitions == null ? void 0 : transitions.pulseRepeatDelay]);
61
60
  const pulseOpacity = useSharedValue(0);
62
61
  const pulseRadius = useSharedValue(pulseRadiusStart);
62
+
63
+ // Convert idlePulse prop to SharedValue so useAnimatedReaction can detect changes.
64
+ // In the new React Native architecture, regular JS props are captured by value in worklets
65
+ // and won't update when the prop changes.
66
+ const idlePulseShared = useSharedValue(idlePulse != null ? idlePulse : false);
67
+ useEffect(() => {
68
+ idlePulseShared.value = idlePulse != null ? idlePulse : false;
69
+ }, [idlePulse, idlePulseShared]);
63
70
  const animatedX = useSharedValue(0);
64
71
  const animatedY = useSharedValue(0);
65
72
 
@@ -103,41 +110,34 @@ export const DefaultScrubberBeacon = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((
103
110
  useImperativeHandle(ref, () => ({
104
111
  pulse: () => {
105
112
  // Only trigger manual pulse when idlePulse is not enabled
106
- if (!idlePulse) {
113
+ if (!idlePulseShared.value) {
107
114
  cancelAnimation(pulseOpacity);
108
115
  cancelAnimation(pulseRadius);
109
116
 
110
117
  // Manual pulse without delay
111
- const immediatePulseTransition = _extends({}, pulseTransition, {
112
- delay: 0
113
- });
114
118
  pulseOpacity.value = pulseOpacityStart;
115
119
  pulseRadius.value = pulseRadiusStart;
116
- pulseOpacity.value = buildTransition(pulseOpacityEnd, immediatePulseTransition);
117
- pulseRadius.value = buildTransition(pulseRadiusEnd, immediatePulseTransition);
120
+ pulseOpacity.value = buildTransition(pulseOpacityEnd, pulseTransition);
121
+ pulseRadius.value = buildTransition(pulseRadiusEnd, pulseTransition);
118
122
  }
119
123
  }
120
- }), [idlePulse, pulseOpacity, pulseRadius, pulseTransition]);
124
+ }), [idlePulseShared, pulseOpacity, pulseRadius, pulseTransition]);
121
125
 
122
126
  // Watch idlePulse changes and control continuous pulse
123
- useAnimatedReaction(() => idlePulse, (current, previous) => {
127
+ useAnimatedReaction(() => idlePulseShared.value, (current, previous) => {
124
128
  if (!animate) return;
125
129
  if (current) {
126
130
  // 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
131
  pulseOpacity.value = pulseOpacityStart;
136
132
  pulseRadius.value = pulseRadiusStart;
137
- pulseOpacity.value = withRepeat(withSequence(buildTransition(pulseOpacityEnd, pulseTransition), buildTransition(pulseOpacityStart, resetWithDelay)), -1,
133
+ pulseOpacity.value = withRepeat(withSequence(buildTransition(pulseOpacityEnd, pulseTransition), withDelay(pulseRepeatDelay, withTiming(pulseOpacityStart, {
134
+ duration: 0
135
+ }))), -1,
138
136
  // infinite loop
139
137
  false);
140
- pulseRadius.value = withRepeat(withSequence(buildTransition(pulseRadiusEnd, pulseTransition), buildTransition(pulseRadiusStart, resetWithDelay)), -1,
138
+ pulseRadius.value = withRepeat(withSequence(buildTransition(pulseRadiusEnd, pulseTransition), withDelay(pulseRepeatDelay, withTiming(pulseRadiusStart, {
139
+ duration: 0
140
+ }))), -1,
141
141
  // infinite loop
142
142
  false);
143
143
  } else {