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.
- package/README.md +24 -0
- package/dist/components/CrosshairLine.d.ts +3 -1
- package/dist/components/CrosshairLine.d.ts.map +1 -1
- package/dist/components/CrosshairOverlay.d.ts +3 -1
- package/dist/components/CrosshairOverlay.d.ts.map +1 -1
- package/dist/components/CustomReferenceLineOverlay.d.ts +4 -2
- package/dist/components/CustomReferenceLineOverlay.d.ts.map +1 -1
- package/dist/components/DegenParticlesOverlay.d.ts.map +1 -1
- package/dist/components/LeftEdgeFade.d.ts +3 -1
- package/dist/components/LeftEdgeFade.d.ts.map +1 -1
- package/dist/components/LiveChart.d.ts.map +1 -1
- package/dist/components/LiveChartSeries.d.ts.map +1 -1
- package/dist/components/LoadingOverlay.d.ts +3 -1
- package/dist/components/LoadingOverlay.d.ts.map +1 -1
- package/dist/components/MarkerOverlay.d.ts.map +1 -1
- package/dist/components/MultiSeriesDots.d.ts +3 -1
- package/dist/components/MultiSeriesDots.d.ts.map +1 -1
- package/dist/components/MultiSeriesTooltipStack.d.ts.map +1 -1
- package/dist/components/MultiSeriesValueLabels.d.ts +3 -1
- package/dist/components/MultiSeriesValueLabels.d.ts.map +1 -1
- package/dist/components/MultiSeriesValueLines.d.ts +3 -1
- package/dist/components/MultiSeriesValueLines.d.ts.map +1 -1
- package/dist/components/ThresholdSplitShader.d.ts.map +1 -1
- package/dist/components/thresholdSplitShaderSource.d.ts +13 -0
- package/dist/components/thresholdSplitShaderSource.d.ts.map +1 -0
- package/dist/core/useLiveChartEngine.d.ts +9 -1
- package/dist/core/useLiveChartEngine.d.ts.map +1 -1
- package/dist/core/useLiveChartSeriesEngine.d.ts +10 -5
- package/dist/core/useLiveChartSeriesEngine.d.ts.map +1 -1
- package/dist/draw/particleAtlas.d.ts +3 -2
- package/dist/draw/particleAtlas.d.ts.map +1 -1
- package/dist/hooks/crosshairShared.d.ts +4 -0
- package/dist/hooks/crosshairShared.d.ts.map +1 -1
- package/dist/hooks/useChartPaths.d.ts.map +1 -1
- package/dist/hooks/useCrosshair.d.ts.map +1 -1
- package/dist/hooks/useCrosshairSeries.d.ts.map +1 -1
- package/dist/hooks/useMultiSeriesLinePaths.d.ts +4 -4
- package/dist/hooks/useMultiSeriesLinePaths.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/math/squiggly.d.ts +6 -3
- package/dist/math/squiggly.d.ts.map +1 -1
- package/dist/math/threshold.d.ts +2 -2
- package/dist/types.d.ts +28 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/CrosshairLine.tsx +17 -0
- package/src/components/CrosshairOverlay.tsx +48 -35
- package/src/components/CustomReferenceLineOverlay.tsx +16 -7
- package/src/components/DegenParticlesOverlay.tsx +40 -5
- package/src/components/LeftEdgeFade.tsx +12 -6
- package/src/components/LiveChart.tsx +29 -16
- package/src/components/LiveChartSeries.tsx +94 -21
- package/src/components/LoadingOverlay.tsx +26 -12
- package/src/components/MarkerOverlay.tsx +20 -3
- package/src/components/MultiSeriesDots.tsx +4 -2
- package/src/components/MultiSeriesTooltipStack.tsx +4 -2
- package/src/components/MultiSeriesValueLabels.tsx +4 -2
- package/src/components/MultiSeriesValueLines.tsx +4 -2
- package/src/components/ThresholdSplitShader.tsx +5 -37
- package/src/components/thresholdSplitShaderSource.ts +70 -0
- package/src/core/useLiveChartEngine.ts +110 -42
- package/src/core/useLiveChartSeriesEngine.ts +137 -70
- package/src/draw/particleAtlas.ts +21 -10
- package/src/hooks/crosshairShared.ts +5 -0
- package/src/hooks/useChartPaths.ts +11 -0
- package/src/hooks/useCrosshair.ts +16 -10
- package/src/hooks/useCrosshairSeries.ts +11 -6
- package/src/hooks/useMultiSeriesLinePaths.ts +15 -9
- package/src/index.ts +1 -0
- package/src/math/squiggly.ts +28 -11
- package/src/math/threshold.ts +2 -2
- 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
|
-
|
|
624
|
-
|
|
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
|
|
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
|
-
|
|
232
|
-
|
|
233
|
-
|
|
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
|
|
12
|
-
*
|
|
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
|
|
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
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
package/src/math/squiggly.ts
CHANGED
|
@@ -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)
|
|
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
|
-
|
|
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
|
|
82
|
+
const target = out ?? [];
|
|
83
|
+
target.length = n;
|
|
75
84
|
for (let i = 0; i < n; i += 2) {
|
|
76
|
-
|
|
77
|
-
|
|
85
|
+
target[i] = flatPts[i];
|
|
86
|
+
target[i + 1] = squigglyYAt(flatPts[i], centerY, t, base, speed);
|
|
78
87
|
}
|
|
79
|
-
return
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
136
|
+
target[i + 1] = from[i + 1] + (to[i + 1] - from[i + 1]) * weight;
|
|
120
137
|
}
|
|
121
|
-
return
|
|
138
|
+
return target;
|
|
122
139
|
}
|
package/src/math/threshold.ts
CHANGED
|
@@ -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
|
-
*
|
|
16
|
-
*
|
|
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}
|
|
253
|
-
*
|
|
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 =
|
|
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
|
/**
|