react-native-livechart 4.10.0 → 4.11.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 (73) hide show
  1. package/README.md +24 -0
  2. package/dist/components/CrosshairLine.d.ts +3 -1
  3. package/dist/components/CrosshairLine.d.ts.map +1 -1
  4. package/dist/components/CrosshairOverlay.d.ts +3 -1
  5. package/dist/components/CrosshairOverlay.d.ts.map +1 -1
  6. package/dist/components/CustomReferenceLineOverlay.d.ts +4 -2
  7. package/dist/components/CustomReferenceLineOverlay.d.ts.map +1 -1
  8. package/dist/components/DegenParticlesOverlay.d.ts.map +1 -1
  9. package/dist/components/LeftEdgeFade.d.ts +3 -1
  10. package/dist/components/LeftEdgeFade.d.ts.map +1 -1
  11. package/dist/components/LiveChart.d.ts.map +1 -1
  12. package/dist/components/LiveChartSeries.d.ts.map +1 -1
  13. package/dist/components/LoadingOverlay.d.ts +3 -1
  14. package/dist/components/LoadingOverlay.d.ts.map +1 -1
  15. package/dist/components/MarkerOverlay.d.ts.map +1 -1
  16. package/dist/components/MultiSeriesDots.d.ts +3 -1
  17. package/dist/components/MultiSeriesDots.d.ts.map +1 -1
  18. package/dist/components/MultiSeriesTooltipStack.d.ts.map +1 -1
  19. package/dist/components/MultiSeriesValueLabels.d.ts +3 -1
  20. package/dist/components/MultiSeriesValueLabels.d.ts.map +1 -1
  21. package/dist/components/MultiSeriesValueLines.d.ts +3 -1
  22. package/dist/components/MultiSeriesValueLines.d.ts.map +1 -1
  23. package/dist/components/ThresholdSplitShader.d.ts.map +1 -1
  24. package/dist/components/thresholdSplitShaderSource.d.ts +13 -0
  25. package/dist/components/thresholdSplitShaderSource.d.ts.map +1 -0
  26. package/dist/core/useLiveChartEngine.d.ts +9 -1
  27. package/dist/core/useLiveChartEngine.d.ts.map +1 -1
  28. package/dist/core/useLiveChartSeriesEngine.d.ts +10 -5
  29. package/dist/core/useLiveChartSeriesEngine.d.ts.map +1 -1
  30. package/dist/draw/particleAtlas.d.ts +3 -2
  31. package/dist/draw/particleAtlas.d.ts.map +1 -1
  32. package/dist/hooks/crosshairShared.d.ts +4 -0
  33. package/dist/hooks/crosshairShared.d.ts.map +1 -1
  34. package/dist/hooks/useChartPaths.d.ts.map +1 -1
  35. package/dist/hooks/useCrosshair.d.ts.map +1 -1
  36. package/dist/hooks/useCrosshairSeries.d.ts.map +1 -1
  37. package/dist/hooks/useMultiSeriesLinePaths.d.ts +4 -4
  38. package/dist/hooks/useMultiSeriesLinePaths.d.ts.map +1 -1
  39. package/dist/index.d.ts +1 -1
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/math/squiggly.d.ts +6 -3
  42. package/dist/math/squiggly.d.ts.map +1 -1
  43. package/dist/math/threshold.d.ts +2 -2
  44. package/dist/types.d.ts +28 -2
  45. package/dist/types.d.ts.map +1 -1
  46. package/package.json +1 -1
  47. package/src/components/CrosshairLine.tsx +17 -0
  48. package/src/components/CrosshairOverlay.tsx +48 -35
  49. package/src/components/CustomReferenceLineOverlay.tsx +16 -7
  50. package/src/components/DegenParticlesOverlay.tsx +40 -5
  51. package/src/components/LeftEdgeFade.tsx +12 -6
  52. package/src/components/LiveChart.tsx +29 -16
  53. package/src/components/LiveChartSeries.tsx +94 -21
  54. package/src/components/LoadingOverlay.tsx +26 -12
  55. package/src/components/MarkerOverlay.tsx +20 -3
  56. package/src/components/MultiSeriesDots.tsx +4 -2
  57. package/src/components/MultiSeriesTooltipStack.tsx +4 -2
  58. package/src/components/MultiSeriesValueLabels.tsx +4 -2
  59. package/src/components/MultiSeriesValueLines.tsx +4 -2
  60. package/src/components/ThresholdSplitShader.tsx +5 -37
  61. package/src/components/thresholdSplitShaderSource.ts +70 -0
  62. package/src/core/useLiveChartEngine.ts +110 -42
  63. package/src/core/useLiveChartSeriesEngine.ts +137 -70
  64. package/src/draw/particleAtlas.ts +21 -10
  65. package/src/hooks/crosshairShared.ts +5 -0
  66. package/src/hooks/useChartPaths.ts +11 -0
  67. package/src/hooks/useCrosshair.ts +16 -10
  68. package/src/hooks/useCrosshairSeries.ts +11 -6
  69. package/src/hooks/useMultiSeriesLinePaths.ts +15 -9
  70. package/src/index.ts +1 -0
  71. package/src/math/squiggly.ts +28 -11
  72. package/src/math/threshold.ts +2 -2
  73. package/src/types.ts +33 -3
@@ -14,6 +14,11 @@ const TOOLTIP_EDGE_GAP = 4;
14
14
  const TOOLTIP_TOP_MARGIN = 8;
15
15
  const FADE_ZONE = 4;
16
16
 
17
+ /** Horizontal travel required before a plain scrub claims the touch. */
18
+ export const SCRUB_ACTIVATE_X_PX = 20;
19
+ /** Vertical travel that fails a plain scrub so a parent scroll gesture can win. */
20
+ export const SCRUB_FAIL_Y_PX = 10;
21
+
17
22
  /** Measure rendered text so the pill and its content share the same centre. */
18
23
  function measureTooltipTextWidth(
19
24
  font: SkFont,
@@ -55,6 +55,10 @@ export function useChartPaths(
55
55
  ptsA: number[];
56
56
  ptsB: number[];
57
57
  ptsTick: boolean;
58
+ squigglePts: number[];
59
+ morphA: number[];
60
+ morphB: number[];
61
+ morphTick: boolean;
58
62
  scratch: ReturnType<typeof makeSplineScratch>;
59
63
  } | null>(null);
60
64
  if (cacheRef.current === null) {
@@ -63,6 +67,10 @@ export function useChartPaths(
63
67
  ptsA: [] as number[],
64
68
  ptsB: [] as number[],
65
69
  ptsTick: false,
70
+ squigglePts: [] as number[],
71
+ morphA: [] as number[],
72
+ morphB: [] as number[],
73
+ morphTick: false,
66
74
  scratch: makeSplineScratch(),
67
75
  };
68
76
  }
@@ -102,15 +110,18 @@ export function useChartPaths(
102
110
  centerY,
103
111
  squiggleAmplitude,
104
112
  squiggleSpeed,
113
+ cache.squigglePts,
105
114
  );
106
115
 
107
116
  // Blend center-out: centre of chart reveals first, edges last
117
+ cache.morphTick = !cache.morphTick;
108
118
  return blendPtsY(
109
119
  squigglyPts,
110
120
  realPts,
111
121
  t,
112
122
  padding,
113
123
  engine.canvasWidth.get(),
124
+ cache.morphTick ? cache.morphA : cache.morphB,
114
125
  );
115
126
  });
116
127
 
@@ -1,5 +1,4 @@
1
1
  import { type SkFont } from "@shopify/react-native-skia";
2
- import { Platform } from "react-native";
3
2
  import { Gesture } from "react-native-gesture-handler";
4
3
  import {
5
4
  useAnimatedReaction,
@@ -33,6 +32,8 @@ import {
33
32
  HIDDEN_TIME_BADGE,
34
33
  HIDDEN_TOOLTIP,
35
34
  pointInRect,
35
+ SCRUB_ACTIVATE_X_PX,
36
+ SCRUB_FAIL_Y_PX,
36
37
  snapPrice,
37
38
  type CrosshairState,
38
39
  } from "./crosshairShared";
@@ -501,12 +502,6 @@ export function useCrosshair(
501
502
  : panGestureDelay;
502
503
 
503
504
  let gesture = Gesture.Pan()
504
- // Scrub-action: minDistance 0 — the press-hold above is the sole activator
505
- // (movement during the hold fails the pan), so the crosshair never appears
506
- // from a tap's travel. The Tap (maxDistance SCRUB_ACTION_TAP_SLOP) owns quick
507
- // gestures; only a held press becomes a scrub/adjust drag.
508
- .minDistance(!hasScrubAction && Platform.OS === "android" ? 10 : 0)
509
- .activateAfterLongPress(longPressMs)
510
505
  .maxPointers(1)
511
506
  .shouldCancelWhenOutside(false)
512
507
  .onTouchesDown(
@@ -620,11 +615,22 @@ export function useCrosshair(
620
615
  },
621
616
  );
622
617
 
623
- /* istanbul ignore next -- Android-only gesture axis config */
624
- if (Platform.OS === "android" && !hasScrubAction) {
618
+ // Passing zero-valued activation modifiers lets RNGH's iOS pan recognizer
619
+ // activate before the axis constraints classify the drag. Only configure a
620
+ // hold when one is requested, and reserve minDistance(0) for scrub-action's
621
+ // deliberate press-hold interaction.
622
+ if (longPressMs > 0) {
623
+ gesture = gesture.activateAfterLongPress(longPressMs);
624
+ }
625
+
626
+ if (hasScrubAction) {
627
+ gesture = gesture.minDistance(0);
628
+ } else {
625
629
  // Lock mode needs free vertical drag (Y = price), so the failOffsetY clamp —
626
630
  // which would kill a vertical adjust — is applied only outside scrub-action.
627
- gesture = gesture.activeOffsetX([-25, 25]).failOffsetY([-25, 25]);
631
+ gesture = gesture
632
+ .activeOffsetX([-SCRUB_ACTIVATE_X_PX, SCRUB_ACTIVATE_X_PX])
633
+ .failOffsetY([-SCRUB_FAIL_Y_PX, SCRUB_FAIL_Y_PX]);
628
634
  }
629
635
 
630
636
  // Axis-drag time-scroll: carve the bottom "time ruler" band out of the scrub's
@@ -1,4 +1,3 @@
1
- import { Platform } from "react-native";
2
1
  import { Gesture } from "react-native-gesture-handler";
3
2
  import {
4
3
  useAnimatedReaction,
@@ -18,6 +17,8 @@ import {
18
17
  computeScrubDotY,
19
18
  computeScrubTime,
20
19
  HIDDEN_TOOLTIP,
20
+ SCRUB_ACTIVATE_X_PX,
21
+ SCRUB_FAIL_Y_PX,
21
22
  type CrosshairState,
22
23
  } from "./crosshairShared";
23
24
  import {
@@ -165,8 +166,6 @@ export function useCrosshairSeries(
165
166
  );
166
167
 
167
168
  let gesture = Gesture.Pan()
168
- .minDistance(Platform.OS === "android" ? 10 : 0)
169
- .activateAfterLongPress(panGestureDelay)
170
169
  .maxPointers(1)
171
170
  .shouldCancelWhenOutside(false)
172
171
  .onTouchesDown(
@@ -228,11 +227,17 @@ export function useCrosshairSeries(
228
227
  },
229
228
  );
230
229
 
231
- /* istanbul ignore next */
232
- if (Platform.OS === "android") {
233
- gesture = gesture.activeOffsetX([-25, 25]).failOffsetY([-25, 25]);
230
+ // Omit zero-valued activation modifiers so RNGH can classify direction before
231
+ // the pan activates on iOS. A vertical drag then remains available to a parent
232
+ // ScrollView/list, while a clear horizontal drag starts scrubbing.
233
+ if (panGestureDelay > 0) {
234
+ gesture = gesture.activateAfterLongPress(panGestureDelay);
234
235
  }
235
236
 
237
+ gesture = gesture
238
+ .activeOffsetX([-SCRUB_ACTIVATE_X_PX, SCRUB_ACTIVATE_X_PX])
239
+ .failOffsetY([-SCRUB_FAIL_Y_PX, SCRUB_FAIL_Y_PX]);
240
+
236
241
  return {
237
242
  scrubX,
238
243
  scrubActive,
@@ -8,30 +8,37 @@ import { drawSpline, makeSplineScratch } from "../math/spline";
8
8
  import { usePathBuilders } from "./usePathBuilder";
9
9
 
10
10
  /**
11
- * One derived shared value holding up to `MAX_MULTI_SERIES` Skia paths (unused
12
- * slots reuse one cached empty path).
11
+ * One derived shared value holding only the active series' Skia paths, capped at
12
+ * `MAX_MULTI_SERIES`.
13
13
  *
14
14
  * Each visible series' path is built into a per-slot `Skia.PathBuilder` (reused
15
15
  * across frames via a SharedValue) and finalized with `detach()` — a fresh
16
16
  * immutable `SkPath` per frame, so `MultiSeriesStroke`'s per-slot
17
17
  * `useDerivedValue(() => paths.value[index])` repaints without the two-SkPath
18
- * ping-pong. Only visible series allocate a path.
18
+ * ping-pong. Only active series allocate and publish a path.
19
19
  */
20
20
  export function useMultiSeriesLinePaths(
21
21
  engine: MultiEngineState,
22
22
  padding: ChartPadding,
23
+ activeSeriesCount = MAX_MULTI_SERIES,
23
24
  ): SharedValue<SkPath[]> {
24
25
  const builders = usePathBuilders(MAX_MULTI_SERIES);
25
26
 
26
27
  const poolRef = useRef<{
27
28
  empty: SkPath;
28
29
  ptsBuf: number[];
30
+ pathsA: SkPath[];
31
+ pathsB: SkPath[];
32
+ pathsTick: boolean;
29
33
  scratch: ReturnType<typeof makeSplineScratch>;
30
34
  } | null>(null);
31
35
  if (poolRef.current === null) {
32
36
  poolRef.current = {
33
37
  empty: Skia.Path.Make(),
34
38
  ptsBuf: [] as number[],
39
+ pathsA: [] as SkPath[],
40
+ pathsB: [] as SkPath[],
41
+ pathsTick: false,
35
42
  scratch: makeSplineScratch(),
36
43
  };
37
44
  }
@@ -41,12 +48,11 @@ export function useMultiSeriesLinePaths(
41
48
  const slots = builders.value;
42
49
  const s = engine.series.get();
43
50
  const displays = engine.displaySeriesValues.get();
44
- const out: SkPath[] = [];
45
- for (let i = 0; i < MAX_MULTI_SERIES; i++) {
46
- if (i >= s.length) {
47
- out.push(pool.empty);
48
- continue;
49
- }
51
+ pool.pathsTick = !pool.pathsTick;
52
+ const out = pool.pathsTick ? pool.pathsA : pool.pathsB;
53
+ out.length = 0;
54
+ const count = Math.min(activeSeriesCount, s.length, MAX_MULTI_SERIES);
55
+ for (let i = 0; i < count; i++) {
50
56
  const pts = buildLinePoints(
51
57
  s[i].data,
52
58
  displays[i] ?? s[i].value,
package/src/index.ts CHANGED
@@ -40,6 +40,7 @@ export type {
40
40
  BadgeVariant,
41
41
  CandleMetrics,
42
42
  CandlePoint,
43
+ CanvasMode,
43
44
  ChartInsets,
44
45
  ChartOverlayContext,
45
46
  ChartPlotRect,
@@ -31,6 +31,7 @@ export function squigglyYAt(
31
31
  * Build a flat [x0,y0,x1,y1,...] point array for a standalone squiggly line
32
32
  * spanning the full chart area (used in loading / empty state).
33
33
  * Points are spaced ~4px apart for smooth curves.
34
+ * Pass `out` to reuse a caller-owned frame scratch.
34
35
  */
35
36
  export function buildSquigglyPts(
36
37
  canvasWidth: number,
@@ -39,17 +40,22 @@ export function buildSquigglyPts(
39
40
  t: number,
40
41
  base = 14,
41
42
  speed = 1,
43
+ out?: number[],
42
44
  ): number[] {
43
45
  "worklet";
46
+ const pts = out ?? [];
44
47
  const leftEdge = padding.left;
45
48
  const rightEdge = canvasWidth - padding.right;
46
49
  const chartW = rightEdge - leftEdge;
47
- if (chartW <= 0 || canvasHeight <= 0) return [];
50
+ if (chartW <= 0 || canvasHeight <= 0) {
51
+ pts.length = 0;
52
+ return pts;
53
+ }
48
54
 
49
55
  const centerY = (canvasHeight - padding.bottom + padding.top) / 2;
50
56
  const step = 4;
51
57
  const count = Math.ceil(chartW / step) + 1;
52
- const pts: number[] = new Array(count * 2);
58
+ pts.length = count * 2;
53
59
  for (let i = 0; i < count; i++) {
54
60
  const x = leftEdge + Math.min(i * step, chartW);
55
61
  pts[i * 2] = x;
@@ -61,6 +67,7 @@ export function buildSquigglyPts(
61
67
  /**
62
68
  * Given a real flat-pts array (from buildLinePoints), replace each Y with the
63
69
  * squiggly Y at the same X. Used during the morph reveal.
70
+ * Pass `out` to reuse a caller-owned frame scratch.
64
71
  */
65
72
  export function squigglifyPts(
66
73
  flatPts: number[],
@@ -68,15 +75,17 @@ export function squigglifyPts(
68
75
  centerY: number,
69
76
  base = 14,
70
77
  speed = 1,
78
+ out?: number[],
71
79
  ): number[] {
72
80
  "worklet";
73
81
  const n = flatPts.length;
74
- const out: number[] = new Array(n);
82
+ const target = out ?? [];
83
+ target.length = n;
75
84
  for (let i = 0; i < n; i += 2) {
76
- out[i] = flatPts[i];
77
- out[i + 1] = squigglyYAt(flatPts[i], centerY, t, base, speed);
85
+ target[i] = flatPts[i];
86
+ target[i + 1] = squigglyYAt(flatPts[i], centerY, t, base, speed);
78
87
  }
79
- return out;
88
+ return target;
80
89
  }
81
90
 
82
91
  /**
@@ -97,6 +106,7 @@ export function smoothstep(t: number): number {
97
106
  * distFromCentre = |x - chartCentreX| / (chartW / 2) // 0=centre, 1=edge
98
107
  * weight = smoothstep((morphT - distFromCentre * 0.5) / 0.5)
99
108
  * blendedY = fromY + (toY - fromY) * weight
109
+ * Pass `out` to reuse a caller-owned frame scratch.
100
110
  */
101
111
  export function blendPtsY(
102
112
  from: number[],
@@ -104,19 +114,26 @@ export function blendPtsY(
104
114
  morphT: number,
105
115
  padding: ChartPadding,
106
116
  canvasWidth: number,
117
+ out?: number[],
107
118
  ): number[] {
108
119
  "worklet";
109
120
  const n = from.length;
110
- if (n === 0) return to;
121
+ if (n === 0 && !out) return to;
111
122
  const chartCentreX = (padding.left + canvasWidth - padding.right) / 2;
112
123
  const halfChartW = (canvasWidth - padding.left - padding.right) / 2;
113
- const out: number[] = new Array(n);
124
+ const target = out ?? [];
125
+ const outputLength = n === 0 ? to.length : n;
126
+ target.length = outputLength;
127
+ if (n === 0) {
128
+ for (let i = 0; i < to.length; i++) target[i] = to[i];
129
+ return target;
130
+ }
114
131
  for (let i = 0; i < n; i += 2) {
115
- out[i] = to[i]; // X always from real pts
132
+ target[i] = to[i]; // X always from real pts
116
133
  const distFromCentre =
117
134
  halfChartW > 0 ? Math.abs(from[i] - chartCentreX) / halfChartW : 0;
118
135
  const weight = smoothstep((morphT - distFromCentre * 0.5) / 0.5);
119
- out[i + 1] = from[i + 1] + (to[i + 1] - from[i + 1]) * weight;
136
+ target[i + 1] = from[i + 1] + (to[i + 1] - from[i + 1]) * weight;
120
137
  }
121
- return out;
138
+ return target;
122
139
  }
@@ -12,8 +12,8 @@ import type { LiveChartPoint } from "../types";
12
12
  * projected to this many evenly-spaced pixel-Y values across the plot, which the
13
13
  * shader linearly interpolates between. ~one sample per 6px on a phone plot —
14
14
  * fine enough that the line's crossing point is coloured accurately. The shader
15
- * walks these in an unrolled loop (`THRESHOLD_SAMPLE_COUNT - 1` iterations), so
16
- * keep it modest to stay within SkSL's unroll limits.
15
+ * selects between them through a balanced constant-index branch tree, so 64
16
+ * samples require at most six comparisons per covered fragment.
17
17
  */
18
18
  export const THRESHOLD_SAMPLE_COUNT = 64;
19
19
 
package/src/types.ts CHANGED
@@ -44,6 +44,10 @@ export type FontWeight =
44
44
  /** Color scheme for the chart background, grid, and derived palette colors. */
45
45
  export type ThemeMode = "light" | "dark";
46
46
 
47
+ /** Skia canvas composition mode. Android uses TextureView for `"transparent"`
48
+ * and an opaque SurfaceView for `"opaque"`. */
49
+ export type CanvasMode = "transparent" | "opaque";
50
+
47
51
  /**
48
52
  * Badge pill style.
49
53
  * - `"default"` — accent-colored background with white text.
@@ -249,8 +253,9 @@ export interface ReferenceLineBadgeConfig extends BadgeStyleConfig {
249
253
  }
250
254
 
251
255
  /**
252
- * Context passed to a custom {@link LiveChartProps.renderReferenceLine}. The chart
253
- * floats the element you return over the canvas and pins it to the line's value on
256
+ * Context passed to a custom {@link LiveChartProps.renderReferenceLine} or
257
+ * {@link LiveChartSeriesProps.renderReferenceLine}. The chart floats the element
258
+ * you return over the canvas and pins it to the line's value on
254
259
  * the UI thread (vertically centered on the line, horizontally at the badge /
255
260
  * label position) — so it tracks the rescaling axis and any drag smoothly without
256
261
  * JS re-renders, just like {@link TooltipRenderProps}. Replaces the built-in pill
@@ -1122,7 +1127,8 @@ export interface TradeEvent {
1122
1127
  }
1123
1128
 
1124
1129
  /** Built-in marker glyph kinds drawn into the chart canvas. */
1125
- export type MarkerKind = "trade" | "boost" | "graduation" | "winner" | "clawback";
1130
+ export type MarkerKind =
1131
+ "trade" | "boost" | "graduation" | "winner" | "clawback";
1126
1132
 
1127
1133
  /**
1128
1134
  * A marker rendered into the chart at `(time, y)`. Exactly one of `seriesId`
@@ -1748,6 +1754,18 @@ export interface LiveChartCoreProps {
1748
1754
  insets?: ChartInsets;
1749
1755
  /** Container View style. */
1750
1756
  style?: ViewStyle;
1757
+ /**
1758
+ * Canvas composition mode. `"transparent"` keeps Skia's default compositing
1759
+ * path (TextureView on Android) and can reveal content behind the chart.
1760
+ * `"opaque"` makes the canvas own and paint its palette background; on Android
1761
+ * this selects SurfaceView and may reduce RenderThread work, but it cannot be
1762
+ * used when the chart must reveal content behind it. Default `"transparent"`.
1763
+ *
1764
+ * @experimental Profile on representative physical Android hardware before
1765
+ * enabling broadly; SurfaceView ordering, clipping, transforms, screenshots,
1766
+ * and parent scrolling should be validated in the consuming layout.
1767
+ */
1768
+ canvasMode?: CanvasMode;
1751
1769
  /** Visible time window in seconds. Default `30`. */
1752
1770
  timeWindow?: number;
1753
1771
  /** Freeze chart scrolling. Resume catches up to real time. Default `false`. */
@@ -2177,6 +2195,18 @@ export interface LiveChartProps extends LiveChartCoreProps {
2177
2195
  export interface LiveChartSeriesProps extends LiveChartCoreProps {
2178
2196
  /** Array of series definitions. Must be a SharedValue for UI-thread reads. */
2179
2197
  series: SharedValue<SeriesConfig[]>;
2198
+ /**
2199
+ * Render a Form-A reference line's tag as a custom **React Native** element
2200
+ * instead of the built-in Skia pill / gutter label. The chart pins the returned
2201
+ * element to the line's live Y position on the UI thread (see
2202
+ * {@link ReferenceLineRenderProps}), including off-axis edge pinning. Return
2203
+ * `null`/`undefined` to keep that line's built-in tag. The line stroke always
2204
+ * remains visible, and `badge.position` / `labelPosition` controls the custom
2205
+ * tag's left, center, or right anchor.
2206
+ */
2207
+ renderReferenceLine?: (
2208
+ ctx: ReferenceLineRenderProps,
2209
+ ) => ReactElement | null | undefined;
2180
2210
  /** Called when a series toggle chip is tapped. */
2181
2211
  onSeriesToggle?: (id: string, visible: boolean) => void;
2182
2212
  /**