react-native-vroom-chart 0.11.0 → 0.12.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.
@@ -13,8 +13,8 @@ constexpr uint32_t kDefaultColors[VROOM_COLOR_COUNT_] = {
13
13
  0xff1a1e24, // GRID — ~20% darker than the crosshair so it recedes
14
14
  0xffc9d1d9, // AXIS_TEXT
15
15
  0xff303741, // CROSSHAIR — grid tone lightened ~44%; reads above GRID
16
- 0xff161b22, // TOOLTIP_BG
17
- 0xffc9d1d9, // TOOLTIP_TEXT
16
+ 0x00000000, // RESERVED_7_ — retired slot, never read
17
+ 0xffffffff, // BADGE_TEXT — text on filled badges/pills
18
18
  0xff3e4855, // CROSSHAIR_TARGET — CROSSHAIR lightened 30% (prior derived ring)
19
19
  0x00000000, // BORDER_BULL — transparent sentinel: inherit BULL fill
20
20
  0x00000000, // BORDER_BEAR — transparent sentinel: inherit BEAR fill
@@ -20,8 +20,40 @@ struct Layout {
20
20
  float top_padding_frac; // fraction of candle area; keeps prices off edges
21
21
  float bottom_padding_frac;
22
22
  float indicator_area_h; // height reserved for below-chart indicator panes
23
+ // Opacity of everything drawn *inside* an axis strip, 1 = shown, 0 = hidden.
24
+ // Separate from the strip's extent because the two ramp at different rates
25
+ // while an axis is being hidden or revealed (see axis_opacity).
26
+ float y_axis_opacity;
27
+ float x_axis_opacity;
23
28
  };
24
29
 
30
+ // --- Axis show/hide ---------------------------------------------------------
31
+ // An axis is hidden by collapsing its strip to nothing rather than by zeroing
32
+ // the underlying dimension: the y-axis width is *measured* from the widest price
33
+ // label (labels::recompute_axis_width) and gets recomputed constantly, so a zero
34
+ // written there would not survive. `t` is the collapse progress the host eases,
35
+ // 0 = fully shown, 1 = fully hidden.
36
+
37
+ inline float clamp_collapse(float t) {
38
+ return t < 0.f ? 0.f : (t > 1.f ? 1.f : t);
39
+ }
40
+
41
+ // The strip's on-screen extent. Everything downstream (candle_area_width,
42
+ // price_pane_bottom) is derived from this, so the plot reclaims the space as
43
+ // the strip shrinks.
44
+ inline float axis_extent(float base, float t) {
45
+ return base * (1.f - clamp_collapse(t));
46
+ }
47
+
48
+ // Opacity of the strip's contents. Deliberately front-loaded: the labels are
49
+ // fully gone by the time the strip is halfway collapsed, so text never sits in
50
+ // a strip too narrow to hold it.
51
+ inline float axis_opacity(float t) {
52
+ constexpr float kFadeBy = 0.5f; // fraction of the collapse spent fading
53
+ const float p = clamp_collapse(t) / kFadeBy;
54
+ return p >= 1.f ? 0.f : 1.f - p;
55
+ }
56
+
25
57
  // Bottom of the price (candle) pane = top of the indicator band. Shrinks when
26
58
  // an indicator pane is present so candles, volume, and price labels reflow.
27
59
  inline float price_pane_bottom(const Layout& l) {
package/lib/index.d.mts CHANGED
@@ -93,6 +93,13 @@ type VroomTheme = {
93
93
  grid?: VroomColor;
94
94
  /** Axis label text (price + time). */
95
95
  axisText?: VroomColor;
96
+ /**
97
+ * Text drawn on a filled badge — the current-price indicator, the crosshair's
98
+ * price and time badges, and price-line pills. Defaults to white, which reads
99
+ * against the saturated fills those badges use on a dark theme; light themes
100
+ * generally want a dark value here.
101
+ */
102
+ badgeText?: VroomColor;
96
103
  /** Crosshair dashed lines. */
97
104
  crosshair?: VroomColor;
98
105
  /** Crosshair target — the hollow ring/dot at the intersection. */
@@ -134,6 +141,26 @@ type VroomTheme = {
134
141
  * continuously — leave it off for charts that should be able to go idle.
135
142
  */
136
143
  lineTipPulse?: boolean;
144
+ /**
145
+ * Show the price (y) axis strip down the right edge. Defaults to `true`.
146
+ *
147
+ * Hiding it collapses the strip and hands the reclaimed width to the plot, so
148
+ * the candles widen to fill it. Its contents — price labels, the current-price
149
+ * badge, the crosshair price badge, price-line axis badges — fade out ahead of
150
+ * the collapse; gridlines and the price indicator's dotted line stay. Animated
151
+ * over `transitionMs` unless the OS asks for reduced motion.
152
+ *
153
+ * The axis drag-to-scale region goes with the strip, so a hidden axis can't be
154
+ * scaled by dragging it.
155
+ */
156
+ showYAxis?: boolean;
157
+ /**
158
+ * Show the time (x) axis strip along the bottom. Defaults to `true`.
159
+ *
160
+ * Behaves like {@link VroomTheme.showYAxis}: the strip collapses, the plot
161
+ * grows into it, and the time labels and crosshair time badge fade first.
162
+ */
163
+ showXAxis?: boolean;
137
164
  };
138
165
  /** A time window over the candle data, as Unix epoch milliseconds. */
139
166
  type VisibleRange = {
@@ -177,6 +204,12 @@ type DrawingBase = {
177
204
  color?: VroomColor;
178
205
  /** Stroke width in px. Default 2. */
179
206
  width?: number;
207
+ /**
208
+ * Protect the drawing from editing. A locked drawing still renders and can
209
+ * still be selected — so a toolbar can offer to unlock it — but it can't be
210
+ * dragged, reshaped or deleted, and its grab handles aren't drawn. Web only.
211
+ */
212
+ locked?: boolean;
180
213
  };
181
214
  /** A two-point trendline from `points[0]` to `points[1]`. */
182
215
  type LineDrawing = DrawingBase & {
@@ -192,6 +225,15 @@ type BoxDrawing = DrawingBase & {
192
225
  type: 'box';
193
226
  /** Two opposite corners, in data space. */
194
227
  points: [DrawPoint, DrawPoint];
228
+ /**
229
+ * Interior fill, painted beneath the stroke. Omitted, the interior keeps its
230
+ * default tint of the stroke color at 10% alpha.
231
+ *
232
+ * A solid fill hides the candles the box was drawn around, so this usually
233
+ * wants an alpha — as the high byte of an 8-digit hex (`'#5400ce2c'` is a
234
+ * green at 33%), since vroom reads hex as `#aarrggbb`, not CSS `#rrggbbaa`.
235
+ */
236
+ fill?: VroomColor;
195
237
  };
196
238
  /**
197
239
  * A freehand pencil stroke: an open path through `points`, in order. Unlike the
@@ -224,6 +266,35 @@ type PathDrawing = DrawingBase & {
224
266
  * `'line'` and `'box'` are always exactly two points.
225
267
  */
226
268
  type Drawing = LineDrawing | BoxDrawing | PencilDrawing | PathDrawing;
269
+ /** An axis-aligned rectangle in CSS px, relative to the chart container. */
270
+ type DrawingRect = {
271
+ x: number;
272
+ y: number;
273
+ width: number;
274
+ height: number;
275
+ };
276
+ /** The currently selected drawing and where it sits on screen. */
277
+ type DrawingSelection = {
278
+ /**
279
+ * The selected drawing. While a drag or reshape is in flight this holds the
280
+ * last committed geometry — the edit reaches you through `onDrawingChange`
281
+ * on release — whereas `rect` tracks the shape live.
282
+ */
283
+ drawing: Drawing;
284
+ /** The drawing's bounds in CSS px, relative to the chart container. */
285
+ rect: DrawingRect;
286
+ };
287
+ /**
288
+ * The subset of a drawing's appearance a host can change after the fact,
289
+ * through `restyle`. Fields left out are kept as they were.
290
+ */
291
+ type DrawingStyle = {
292
+ color?: VroomColor;
293
+ width?: number;
294
+ /** Box interior fill; ignored by the other drawing types. */
295
+ fill?: VroomColor;
296
+ locked?: boolean;
297
+ };
227
298
  /**
228
299
  * Storage adapter for **managed** drawing persistence. Provide it via the
229
300
  * `drawingStore` prop and the chart owns the drawings array itself — loading and
@@ -264,11 +335,11 @@ type UndoRedoState = {
264
335
  canRedo: boolean;
265
336
  };
266
337
  /**
267
- * Programmatic undo/redo controls, published through the `historyRef` prop in
268
- * managed mode — for toolbar buttons and other UI outside the chart. The
269
- * keyboard shortcuts (⌘Z / ⇧⌘Z / Ctrl+Y) work without this.
338
+ * Programmatic controls over the chart's drawings, published through the
339
+ * `historyRef` prop in managed mode — for toolbar buttons and other UI outside
340
+ * the chart. The keyboard shortcuts (⌘Z / ⇧⌘Z / Ctrl+Y) work without this.
270
341
  */
271
- type UndoRedoControls = {
342
+ type DrawingControls = {
272
343
  /** Roll back the most recent committed drawing action. No-op when empty. */
273
344
  undo: () => void;
274
345
  /** Re-apply the most recently undone action. No-op when empty. */
@@ -279,6 +350,16 @@ type UndoRedoControls = {
279
350
  * chart for what the user perceives as a brand-new context.
280
351
  */
281
352
  clearHistory: () => void;
353
+ /**
354
+ * Restyle the drawing with this `id`, merging `patch` over its current
355
+ * appearance. No-op for an unknown id.
356
+ *
357
+ * In managed mode the chart owns the drawings array, so this is the only way
358
+ * to say "this drawing's color is now X" — the array is otherwise mutated by
359
+ * user gestures alone. The change records an undo step and persists like any
360
+ * other edit.
361
+ */
362
+ restyle: (id: string, patch: DrawingStyle) => void;
282
363
  };
283
364
  /** Price source for a moving average. */
284
365
  type MASource = 'close' | 'open' | 'high' | 'low' | 'hl2' | 'hlc3' | 'ohlc4';
@@ -749,6 +830,17 @@ type VroomChartCoreProps = {
749
830
  * drawing with this `id` from your controlled `drawings` state. Web only.
750
831
  */
751
832
  onDrawingDelete?: (id: string) => void;
833
+ /**
834
+ * Fired when the selected drawing changes, and again whenever its position on
835
+ * screen moves — panning, zooming, resizing, or dragging the shape itself.
836
+ * `null` on deselect.
837
+ *
838
+ * Anchor a floating toolbar to `selection.rect` and it will stay attached to
839
+ * the drawing: the rect is recomputed from live chart state once per painted
840
+ * frame, so it tracks 1:1 rather than settling after the gesture. Works in
841
+ * both controlled and managed mode. Web only.
842
+ */
843
+ onSelectionChange?: (selection: DrawingSelection | null) => void;
752
844
  /**
753
845
  * Fired when the chart wants the mode changed — e.g. it requests `'pan'` after
754
846
  * the user clicks away from a just-drawn line. Since `mode` is controlled, the
@@ -769,13 +861,13 @@ type VroomChartCoreProps = {
769
861
  */
770
862
  onHistoryChange?: (state: UndoRedoState) => void;
771
863
  /**
772
- * Receives programmatic `undo`/`redo`/`clearHistory` controls in managed mode
773
- * (e.g. `useRef<UndoRedoControls | null>(null)` passed here, then
864
+ * Receives programmatic `undo`/`redo`/`clearHistory`/`restyle` controls in
865
+ * managed mode (e.g. `useRef<DrawingControls | null>(null)` passed here, then
774
866
  * `historyRef.current?.undo()` from a toolbar button). Set to `null` while
775
867
  * unmounted or when no `drawingStore` is present. Web only.
776
868
  */
777
869
  historyRef?: {
778
- current: UndoRedoControls | null;
870
+ current: DrawingControls | null;
779
871
  };
780
872
  /**
781
873
  * Fired when the user presses the undo shortcut (⌘Z / Ctrl+Z) in controlled
package/lib/index.d.ts CHANGED
@@ -93,6 +93,13 @@ type VroomTheme = {
93
93
  grid?: VroomColor;
94
94
  /** Axis label text (price + time). */
95
95
  axisText?: VroomColor;
96
+ /**
97
+ * Text drawn on a filled badge — the current-price indicator, the crosshair's
98
+ * price and time badges, and price-line pills. Defaults to white, which reads
99
+ * against the saturated fills those badges use on a dark theme; light themes
100
+ * generally want a dark value here.
101
+ */
102
+ badgeText?: VroomColor;
96
103
  /** Crosshair dashed lines. */
97
104
  crosshair?: VroomColor;
98
105
  /** Crosshair target — the hollow ring/dot at the intersection. */
@@ -134,6 +141,26 @@ type VroomTheme = {
134
141
  * continuously — leave it off for charts that should be able to go idle.
135
142
  */
136
143
  lineTipPulse?: boolean;
144
+ /**
145
+ * Show the price (y) axis strip down the right edge. Defaults to `true`.
146
+ *
147
+ * Hiding it collapses the strip and hands the reclaimed width to the plot, so
148
+ * the candles widen to fill it. Its contents — price labels, the current-price
149
+ * badge, the crosshair price badge, price-line axis badges — fade out ahead of
150
+ * the collapse; gridlines and the price indicator's dotted line stay. Animated
151
+ * over `transitionMs` unless the OS asks for reduced motion.
152
+ *
153
+ * The axis drag-to-scale region goes with the strip, so a hidden axis can't be
154
+ * scaled by dragging it.
155
+ */
156
+ showYAxis?: boolean;
157
+ /**
158
+ * Show the time (x) axis strip along the bottom. Defaults to `true`.
159
+ *
160
+ * Behaves like {@link VroomTheme.showYAxis}: the strip collapses, the plot
161
+ * grows into it, and the time labels and crosshair time badge fade first.
162
+ */
163
+ showXAxis?: boolean;
137
164
  };
138
165
  /** A time window over the candle data, as Unix epoch milliseconds. */
139
166
  type VisibleRange = {
@@ -177,6 +204,12 @@ type DrawingBase = {
177
204
  color?: VroomColor;
178
205
  /** Stroke width in px. Default 2. */
179
206
  width?: number;
207
+ /**
208
+ * Protect the drawing from editing. A locked drawing still renders and can
209
+ * still be selected — so a toolbar can offer to unlock it — but it can't be
210
+ * dragged, reshaped or deleted, and its grab handles aren't drawn. Web only.
211
+ */
212
+ locked?: boolean;
180
213
  };
181
214
  /** A two-point trendline from `points[0]` to `points[1]`. */
182
215
  type LineDrawing = DrawingBase & {
@@ -192,6 +225,15 @@ type BoxDrawing = DrawingBase & {
192
225
  type: 'box';
193
226
  /** Two opposite corners, in data space. */
194
227
  points: [DrawPoint, DrawPoint];
228
+ /**
229
+ * Interior fill, painted beneath the stroke. Omitted, the interior keeps its
230
+ * default tint of the stroke color at 10% alpha.
231
+ *
232
+ * A solid fill hides the candles the box was drawn around, so this usually
233
+ * wants an alpha — as the high byte of an 8-digit hex (`'#5400ce2c'` is a
234
+ * green at 33%), since vroom reads hex as `#aarrggbb`, not CSS `#rrggbbaa`.
235
+ */
236
+ fill?: VroomColor;
195
237
  };
196
238
  /**
197
239
  * A freehand pencil stroke: an open path through `points`, in order. Unlike the
@@ -224,6 +266,35 @@ type PathDrawing = DrawingBase & {
224
266
  * `'line'` and `'box'` are always exactly two points.
225
267
  */
226
268
  type Drawing = LineDrawing | BoxDrawing | PencilDrawing | PathDrawing;
269
+ /** An axis-aligned rectangle in CSS px, relative to the chart container. */
270
+ type DrawingRect = {
271
+ x: number;
272
+ y: number;
273
+ width: number;
274
+ height: number;
275
+ };
276
+ /** The currently selected drawing and where it sits on screen. */
277
+ type DrawingSelection = {
278
+ /**
279
+ * The selected drawing. While a drag or reshape is in flight this holds the
280
+ * last committed geometry — the edit reaches you through `onDrawingChange`
281
+ * on release — whereas `rect` tracks the shape live.
282
+ */
283
+ drawing: Drawing;
284
+ /** The drawing's bounds in CSS px, relative to the chart container. */
285
+ rect: DrawingRect;
286
+ };
287
+ /**
288
+ * The subset of a drawing's appearance a host can change after the fact,
289
+ * through `restyle`. Fields left out are kept as they were.
290
+ */
291
+ type DrawingStyle = {
292
+ color?: VroomColor;
293
+ width?: number;
294
+ /** Box interior fill; ignored by the other drawing types. */
295
+ fill?: VroomColor;
296
+ locked?: boolean;
297
+ };
227
298
  /**
228
299
  * Storage adapter for **managed** drawing persistence. Provide it via the
229
300
  * `drawingStore` prop and the chart owns the drawings array itself — loading and
@@ -264,11 +335,11 @@ type UndoRedoState = {
264
335
  canRedo: boolean;
265
336
  };
266
337
  /**
267
- * Programmatic undo/redo controls, published through the `historyRef` prop in
268
- * managed mode — for toolbar buttons and other UI outside the chart. The
269
- * keyboard shortcuts (⌘Z / ⇧⌘Z / Ctrl+Y) work without this.
338
+ * Programmatic controls over the chart's drawings, published through the
339
+ * `historyRef` prop in managed mode — for toolbar buttons and other UI outside
340
+ * the chart. The keyboard shortcuts (⌘Z / ⇧⌘Z / Ctrl+Y) work without this.
270
341
  */
271
- type UndoRedoControls = {
342
+ type DrawingControls = {
272
343
  /** Roll back the most recent committed drawing action. No-op when empty. */
273
344
  undo: () => void;
274
345
  /** Re-apply the most recently undone action. No-op when empty. */
@@ -279,6 +350,16 @@ type UndoRedoControls = {
279
350
  * chart for what the user perceives as a brand-new context.
280
351
  */
281
352
  clearHistory: () => void;
353
+ /**
354
+ * Restyle the drawing with this `id`, merging `patch` over its current
355
+ * appearance. No-op for an unknown id.
356
+ *
357
+ * In managed mode the chart owns the drawings array, so this is the only way
358
+ * to say "this drawing's color is now X" — the array is otherwise mutated by
359
+ * user gestures alone. The change records an undo step and persists like any
360
+ * other edit.
361
+ */
362
+ restyle: (id: string, patch: DrawingStyle) => void;
282
363
  };
283
364
  /** Price source for a moving average. */
284
365
  type MASource = 'close' | 'open' | 'high' | 'low' | 'hl2' | 'hlc3' | 'ohlc4';
@@ -749,6 +830,17 @@ type VroomChartCoreProps = {
749
830
  * drawing with this `id` from your controlled `drawings` state. Web only.
750
831
  */
751
832
  onDrawingDelete?: (id: string) => void;
833
+ /**
834
+ * Fired when the selected drawing changes, and again whenever its position on
835
+ * screen moves — panning, zooming, resizing, or dragging the shape itself.
836
+ * `null` on deselect.
837
+ *
838
+ * Anchor a floating toolbar to `selection.rect` and it will stay attached to
839
+ * the drawing: the rect is recomputed from live chart state once per painted
840
+ * frame, so it tracks 1:1 rather than settling after the gesture. Works in
841
+ * both controlled and managed mode. Web only.
842
+ */
843
+ onSelectionChange?: (selection: DrawingSelection | null) => void;
752
844
  /**
753
845
  * Fired when the chart wants the mode changed — e.g. it requests `'pan'` after
754
846
  * the user clicks away from a just-drawn line. Since `mode` is controlled, the
@@ -769,13 +861,13 @@ type VroomChartCoreProps = {
769
861
  */
770
862
  onHistoryChange?: (state: UndoRedoState) => void;
771
863
  /**
772
- * Receives programmatic `undo`/`redo`/`clearHistory` controls in managed mode
773
- * (e.g. `useRef<UndoRedoControls | null>(null)` passed here, then
864
+ * Receives programmatic `undo`/`redo`/`clearHistory`/`restyle` controls in
865
+ * managed mode (e.g. `useRef<DrawingControls | null>(null)` passed here, then
774
866
  * `historyRef.current?.undo()` from a toolbar button). Set to `null` while
775
867
  * unmounted or when no `drawingStore` is present. Web only.
776
868
  */
777
869
  historyRef?: {
778
- current: UndoRedoControls | null;
870
+ current: DrawingControls | null;
779
871
  };
780
872
  /**
781
873
  * Fired when the user presses the undo shortcut (⌘Z / Ctrl+Z) in controlled
package/lib/index.js CHANGED
@@ -163,6 +163,8 @@ var COLOR_KEYS = {
163
163
  // VROOM_COLOR_AXIS_TEXT
164
164
  crosshair: 6,
165
165
  // VROOM_COLOR_CROSSHAIR
166
+ badgeText: 8,
167
+ // VROOM_COLOR_BADGE_TEXT (7 is a retired slot)
166
168
  crosshairTarget: 9,
167
169
  // VROOM_COLOR_CROSSHAIR_TARGET
168
170
  borderBull: 10,
@@ -638,9 +640,13 @@ function VroomChart(props) {
638
640
  handle.setChartType(target);
639
641
  const p = handle.render();
640
642
  if (p) pictureSV.value = p;
643
+ maybeStartAnim();
644
+ return void 0;
645
+ }
646
+ if (morphFade.current === target) {
647
+ maybeStartAnim();
641
648
  return void 0;
642
649
  }
643
- if (morphFade.current === target) return void 0;
644
650
  if (morphRaf.current != null) {
645
651
  cancelAnimationFrame(morphRaf.current);
646
652
  morphRaf.current = null;
@@ -651,6 +657,7 @@ function VroomChart(props) {
651
657
  handle.setChartType(target);
652
658
  const p = handle.render();
653
659
  if (p) pictureSV.value = p;
660
+ maybeStartAnim();
654
661
  return void 0;
655
662
  }
656
663
  const from = morphFade.current;
@@ -671,6 +678,7 @@ function VroomChart(props) {
671
678
  handle.setChartType(target);
672
679
  const q = handle.render();
673
680
  if (q) pictureSV.value = q;
681
+ maybeStartAnim();
674
682
  }
675
683
  };
676
684
  morphRaf.current = requestAnimationFrame(step);
@@ -680,7 +688,7 @@ function VroomChart(props) {
680
688
  morphRaf.current = null;
681
689
  }
682
690
  };
683
- }, [handle, chartType, transitionMs, reduceMotion, pictureSV]);
691
+ }, [handle, chartType, transitionMs, reduceMotion, pictureSV, maybeStartAnim]);
684
692
  const volumeRaf = (0, import_react2.useRef)(null);
685
693
  const volumeHandle = (0, import_react2.useRef)(null);
686
694
  (0, import_react2.useEffect)(() => {
@@ -690,9 +698,13 @@ function VroomChart(props) {
690
698
  if (volumeHandle.current !== handle || volumeCollapseRef.current == null) {
691
699
  volumeHandle.current = handle;
692
700
  volumeCollapseRef.current = { t: target, easing };
701
+ maybeStartAnim();
702
+ return void 0;
703
+ }
704
+ if (volumeCollapseRef.current.t === target) {
705
+ maybeStartAnim();
693
706
  return void 0;
694
707
  }
695
- if (volumeCollapseRef.current.t === target) return void 0;
696
708
  if (volumeRaf.current != null) {
697
709
  cancelAnimationFrame(volumeRaf.current);
698
710
  volumeRaf.current = null;
@@ -703,6 +715,7 @@ function VroomChart(props) {
703
715
  handle.setVolumeCollapse(target, easing);
704
716
  const p = handle.render();
705
717
  if (p) pictureSV.value = p;
718
+ maybeStartAnim();
706
719
  return void 0;
707
720
  }
708
721
  const from = volumeCollapseRef.current.t;
@@ -716,7 +729,12 @@ function VroomChart(props) {
716
729
  handle.setVolumeCollapse(t, kind);
717
730
  const p = handle.render();
718
731
  if (p) pictureSV.value = p;
719
- volumeRaf.current = prog < 1 ? requestAnimationFrame(step) : null;
732
+ if (prog < 1) {
733
+ volumeRaf.current = requestAnimationFrame(step);
734
+ } else {
735
+ volumeRaf.current = null;
736
+ maybeStartAnim();
737
+ }
720
738
  };
721
739
  volumeRaf.current = requestAnimationFrame(step);
722
740
  return () => {
@@ -725,7 +743,86 @@ function VroomChart(props) {
725
743
  volumeRaf.current = null;
726
744
  }
727
745
  };
728
- }, [handle, volume?.enabled, transitionMs, reduceMotion, pictureSV, volumeCollapseRef]);
746
+ }, [
747
+ handle,
748
+ volume?.enabled,
749
+ transitionMs,
750
+ reduceMotion,
751
+ pictureSV,
752
+ volumeCollapseRef,
753
+ maybeStartAnim
754
+ ]);
755
+ const axisRaf = (0, import_react2.useRef)(null);
756
+ const axisHandle = (0, import_react2.useRef)(null);
757
+ const axisCollapse = (0, import_react2.useRef)(null);
758
+ const showYAxis = theme?.showYAxis ?? true;
759
+ const showXAxis = theme?.showXAxis ?? true;
760
+ (0, import_react2.useEffect)(() => {
761
+ if (!handle) return void 0;
762
+ const targetY = showYAxis ? 0 : 1;
763
+ const targetX = showXAxis ? 0 : 1;
764
+ if (axisHandle.current !== handle || axisCollapse.current == null) {
765
+ axisHandle.current = handle;
766
+ axisCollapse.current = { y: targetY, x: targetX };
767
+ handle.setAxisCollapse(targetY, targetX);
768
+ const p = handle.render();
769
+ if (p) pictureSV.value = p;
770
+ maybeStartAnim();
771
+ return void 0;
772
+ }
773
+ const fromY = axisCollapse.current.y;
774
+ const fromX = axisCollapse.current.x;
775
+ if (fromY === targetY && fromX === targetX) {
776
+ maybeStartAnim();
777
+ return void 0;
778
+ }
779
+ if (axisRaf.current != null) {
780
+ cancelAnimationFrame(axisRaf.current);
781
+ axisRaf.current = null;
782
+ }
783
+ const dur = Math.max(0, transitionMs ?? 300);
784
+ if (dur === 0 || reduceMotion) {
785
+ axisCollapse.current = { y: targetY, x: targetX };
786
+ handle.setAxisCollapse(targetY, targetX);
787
+ const p = handle.render();
788
+ if (p) pictureSV.value = p;
789
+ maybeStartAnim();
790
+ return void 0;
791
+ }
792
+ let startTs = null;
793
+ const step = (now) => {
794
+ if (startTs == null) startTs = now;
795
+ const prog = Math.min(1, (now - startTs) / dur);
796
+ const e = ease(easingRef.current, prog);
797
+ const y = prog < 1 ? fromY + (targetY - fromY) * e : targetY;
798
+ const x = prog < 1 ? fromX + (targetX - fromX) * e : targetX;
799
+ axisCollapse.current = { y, x };
800
+ handle.setAxisCollapse(y, x);
801
+ const p = handle.render();
802
+ if (p) pictureSV.value = p;
803
+ if (prog < 1) {
804
+ axisRaf.current = requestAnimationFrame(step);
805
+ } else {
806
+ axisRaf.current = null;
807
+ maybeStartAnim();
808
+ }
809
+ };
810
+ axisRaf.current = requestAnimationFrame(step);
811
+ return () => {
812
+ if (axisRaf.current != null) {
813
+ cancelAnimationFrame(axisRaf.current);
814
+ axisRaf.current = null;
815
+ }
816
+ };
817
+ }, [
818
+ handle,
819
+ showYAxis,
820
+ showXAxis,
821
+ transitionMs,
822
+ reduceMotion,
823
+ pictureSV,
824
+ maybeStartAnim
825
+ ]);
729
826
  const hitAxis = (0, import_react2.useCallback)(
730
827
  (x, y) => {
731
828
  if (!handle) return "chart";