react-native-livechart 3.0.0 → 3.2.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 +9 -3
- package/dist/components/AxisLabelOverlay.d.ts +29 -0
- package/dist/components/AxisLabelOverlay.d.ts.map +1 -0
- package/dist/components/CrosshairLine.d.ts +12 -1
- package/dist/components/CrosshairLine.d.ts.map +1 -1
- package/dist/components/CrosshairOverlay.d.ts +15 -1
- package/dist/components/CrosshairOverlay.d.ts.map +1 -1
- package/dist/components/DegenParticlesOverlay.d.ts +17 -0
- package/dist/components/DegenParticlesOverlay.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/SelectionDot.d.ts +32 -0
- package/dist/components/SelectionDot.d.ts.map +1 -0
- package/dist/core/resolveConfig.d.ts +46 -1
- package/dist/core/resolveConfig.d.ts.map +1 -1
- package/dist/core/useLiveChartEngine.d.ts +6 -0
- package/dist/core/useLiveChartEngine.d.ts.map +1 -1
- package/dist/draw/particleAtlas.d.ts +39 -0
- package/dist/draw/particleAtlas.d.ts.map +1 -0
- package/dist/hooks/crosshairShared.d.ts +10 -0
- package/dist/hooks/crosshairShared.d.ts.map +1 -1
- package/dist/hooks/useCandlePaths.d.ts +3 -1
- package/dist/hooks/useCandlePaths.d.ts.map +1 -1
- package/dist/hooks/useCanvasLayout.d.ts +3 -2
- package/dist/hooks/useCanvasLayout.d.ts.map +1 -1
- package/dist/hooks/useChartColors.d.ts +5 -0
- package/dist/hooks/useChartColors.d.ts.map +1 -1
- package/dist/hooks/useChartReveal.d.ts +3 -1
- package/dist/hooks/useChartReveal.d.ts.map +1 -1
- package/dist/hooks/useCrosshair.d.ts +1 -1
- package/dist/hooks/useCrosshair.d.ts.map +1 -1
- package/dist/hooks/useCrosshairSeries.d.ts +1 -1
- package/dist/hooks/useCrosshairSeries.d.ts.map +1 -1
- package/dist/hooks/useDegen.d.ts +3 -1
- package/dist/hooks/useDegen.d.ts.map +1 -1
- package/dist/hooks/useMarkers.d.ts +3 -1
- package/dist/hooks/useMarkers.d.ts.map +1 -1
- package/dist/hooks/useTradeStream.d.ts +3 -1
- package/dist/hooks/useTradeStream.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/types.d.ts +102 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/AxisLabelOverlay.tsx +133 -0
- package/src/components/CrosshairLine.tsx +27 -0
- package/src/components/CrosshairOverlay.tsx +50 -18
- package/src/components/DegenParticlesOverlay.tsx +93 -109
- package/src/components/LiveChart.tsx +79 -13
- package/src/components/LiveChartSeries.tsx +40 -0
- package/src/components/SelectionDot.tsx +92 -0
- package/src/core/resolveConfig.ts +98 -0
- package/src/core/useLiveChartEngine.ts +78 -27
- package/src/draw/particleAtlas.ts +95 -0
- package/src/hooks/crosshairShared.ts +26 -0
- package/src/hooks/useCandlePaths.ts +3 -1
- package/src/hooks/useCanvasLayout.ts +5 -3
- package/src/hooks/useChartColors.ts +19 -0
- package/src/hooks/useChartReveal.ts +7 -4
- package/src/hooks/useCrosshair.ts +46 -7
- package/src/hooks/useCrosshairSeries.ts +47 -8
- package/src/hooks/useDegen.ts +6 -0
- package/src/hooks/useMarkers.ts +3 -0
- package/src/hooks/useTradeStream.ts +3 -0
- package/src/index.ts +4 -0
- package/src/types.ts +106 -0
|
@@ -16,6 +16,7 @@ import type { CandlePoint, LiveChartPalette, ScrubPoint } from "../types";
|
|
|
16
16
|
import {
|
|
17
17
|
computeCandleTooltipLayout,
|
|
18
18
|
computeCrosshairOpacity,
|
|
19
|
+
computeScrubDotY,
|
|
19
20
|
computeScrubTime,
|
|
20
21
|
deriveCrosshairTooltipSingle,
|
|
21
22
|
type CrosshairState,
|
|
@@ -57,9 +58,14 @@ export function useCrosshair(
|
|
|
57
58
|
candleOpts?: CrosshairCandleOpts,
|
|
58
59
|
/** Press-and-hold delay (ms) before scrubbing activates. 0 = immediate. */
|
|
59
60
|
panGestureDelay = 0,
|
|
61
|
+
onGestureStart?: () => void,
|
|
62
|
+
onGestureEnd?: () => void,
|
|
60
63
|
): CrosshairState {
|
|
61
64
|
const scrubX = useSharedValue(-1);
|
|
62
65
|
const scrubActive = useSharedValue(false);
|
|
66
|
+
// Tracks whether the active scrub phase actually began, so a tap that never
|
|
67
|
+
// activates doesn't emit a spurious onGestureEnd.
|
|
68
|
+
const gestureStarted = useSharedValue(false);
|
|
63
69
|
|
|
64
70
|
const scrubTime = useDerivedValue(() =>
|
|
65
71
|
computeScrubTime(
|
|
@@ -112,6 +118,19 @@ export function useCrosshair(
|
|
|
112
118
|
),
|
|
113
119
|
);
|
|
114
120
|
|
|
121
|
+
// Y pixel of the scrub intersection — used by the selection dot. -1 when
|
|
122
|
+
// there's no value to mark.
|
|
123
|
+
const scrubDotY = useDerivedValue(() =>
|
|
124
|
+
computeScrubDotY(
|
|
125
|
+
scrubValue.get(),
|
|
126
|
+
engine.displayMin.get(),
|
|
127
|
+
engine.displayMax.get(),
|
|
128
|
+
engine.canvasHeight.get(),
|
|
129
|
+
padding.top,
|
|
130
|
+
padding.bottom,
|
|
131
|
+
),
|
|
132
|
+
);
|
|
133
|
+
|
|
115
134
|
// Monospace advance width, measured once per render (not per scrub frame) so
|
|
116
135
|
// the tooltip layout worklet can size text by character count instead of a
|
|
117
136
|
// per-frame Skia measureText.
|
|
@@ -165,7 +184,19 @@ export function useCrosshair(
|
|
|
165
184
|
onScrub?.(null);
|
|
166
185
|
}
|
|
167
186
|
|
|
187
|
+
/* istanbul ignore next */
|
|
188
|
+
function handleGestureStart() {
|
|
189
|
+
onGestureStart?.();
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/* istanbul ignore next */
|
|
193
|
+
function handleGestureEnd() {
|
|
194
|
+
onGestureEnd?.();
|
|
195
|
+
}
|
|
196
|
+
|
|
168
197
|
const hasOnScrub = onScrub != null;
|
|
198
|
+
const hasOnGestureStart = onGestureStart != null;
|
|
199
|
+
const hasOnGestureEnd = onGestureEnd != null;
|
|
169
200
|
|
|
170
201
|
useAnimatedReaction(
|
|
171
202
|
() => {
|
|
@@ -178,13 +209,14 @@ export function useCrosshair(
|
|
|
178
209
|
if (val === null || time < 0) return "__pending__";
|
|
179
210
|
const chartW = engine.canvasWidth.get() - padding.left - padding.right;
|
|
180
211
|
if (chartW <= 0) return "__pending__";
|
|
181
|
-
const
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
212
|
+
const dotY = computeScrubDotY(
|
|
213
|
+
val,
|
|
214
|
+
engine.displayMin.get(),
|
|
215
|
+
engine.displayMax.get(),
|
|
216
|
+
engine.canvasHeight.get(),
|
|
217
|
+
padding.top,
|
|
218
|
+
padding.bottom,
|
|
219
|
+
);
|
|
188
220
|
let candleJson: string | null = null;
|
|
189
221
|
if (isCandleMode) {
|
|
190
222
|
const c = scrubCandle.get();
|
|
@@ -229,6 +261,8 @@ export function useCrosshair(
|
|
|
229
261
|
if (!enabled) return;
|
|
230
262
|
scrubX.set(e.x);
|
|
231
263
|
scrubActive.set(true);
|
|
264
|
+
gestureStarted.set(true);
|
|
265
|
+
if (hasOnGestureStart) scheduleOnRN(handleGestureStart);
|
|
232
266
|
},
|
|
233
267
|
)
|
|
234
268
|
.onUpdate(
|
|
@@ -243,6 +277,10 @@ export function useCrosshair(
|
|
|
243
277
|
"worklet";
|
|
244
278
|
scrubActive.set(false);
|
|
245
279
|
if (hasOnScrub) scheduleOnRN(handleScrubEnd);
|
|
280
|
+
if (gestureStarted.get()) {
|
|
281
|
+
gestureStarted.set(false);
|
|
282
|
+
if (hasOnGestureEnd) scheduleOnRN(handleGestureEnd);
|
|
283
|
+
}
|
|
246
284
|
},
|
|
247
285
|
);
|
|
248
286
|
|
|
@@ -258,6 +296,7 @@ export function useCrosshair(
|
|
|
258
296
|
scrubValue,
|
|
259
297
|
crosshairOpacity,
|
|
260
298
|
tooltipLayout,
|
|
299
|
+
scrubDotY,
|
|
261
300
|
gesture,
|
|
262
301
|
};
|
|
263
302
|
}
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
useDerivedValue,
|
|
6
6
|
useSharedValue,
|
|
7
7
|
} from "react-native-reanimated";
|
|
8
|
+
import { scheduleOnRN } from "react-native-worklets";
|
|
8
9
|
import type { MultiEngineState } from "../core/useLiveChartEngine";
|
|
9
10
|
import { type ChartPadding } from "../draw/line";
|
|
10
11
|
import type { ScrubPointMulti } from "../types";
|
|
@@ -14,6 +15,7 @@ import {
|
|
|
14
15
|
} from "./crosshairSeries";
|
|
15
16
|
import {
|
|
16
17
|
computeCrosshairOpacity,
|
|
18
|
+
computeScrubDotY,
|
|
17
19
|
computeScrubTime,
|
|
18
20
|
HIDDEN_TOOLTIP,
|
|
19
21
|
type CrosshairState,
|
|
@@ -30,9 +32,14 @@ export function useCrosshairSeries(
|
|
|
30
32
|
onScrub?: (point: ScrubPointMulti | null) => void,
|
|
31
33
|
/** Press-and-hold delay (ms) before scrubbing activates. 0 = immediate. */
|
|
32
34
|
panGestureDelay = 0,
|
|
35
|
+
onGestureStart?: () => void,
|
|
36
|
+
onGestureEnd?: () => void,
|
|
33
37
|
): CrosshairState {
|
|
34
38
|
const scrubX = useSharedValue(-1);
|
|
35
39
|
const scrubActive = useSharedValue(false);
|
|
40
|
+
// Tracks whether the active scrub phase actually began, so a tap that never
|
|
41
|
+
// activates doesn't emit a spurious onGestureEnd.
|
|
42
|
+
const gestureStarted = useSharedValue(false);
|
|
36
43
|
|
|
37
44
|
const scrubTime = useDerivedValue(() =>
|
|
38
45
|
computeScrubTime(
|
|
@@ -62,9 +69,34 @@ export function useCrosshairSeries(
|
|
|
62
69
|
),
|
|
63
70
|
);
|
|
64
71
|
|
|
72
|
+
// Y pixel of the scrub intersection (leading-series value) — used by the
|
|
73
|
+
// selection dot. -1 when there's no value to mark.
|
|
74
|
+
const scrubDotY = useDerivedValue(() =>
|
|
75
|
+
computeScrubDotY(
|
|
76
|
+
scrubValue.get(),
|
|
77
|
+
engine.displayMin.get(),
|
|
78
|
+
engine.displayMax.get(),
|
|
79
|
+
engine.canvasHeight.get(),
|
|
80
|
+
padding.top,
|
|
81
|
+
padding.bottom,
|
|
82
|
+
),
|
|
83
|
+
);
|
|
84
|
+
|
|
65
85
|
const tooltipLayout = useSharedValue(HIDDEN_TOOLTIP);
|
|
66
86
|
|
|
87
|
+
/* istanbul ignore next */
|
|
88
|
+
function handleGestureStart() {
|
|
89
|
+
onGestureStart?.();
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/* istanbul ignore next */
|
|
93
|
+
function handleGestureEnd() {
|
|
94
|
+
onGestureEnd?.();
|
|
95
|
+
}
|
|
96
|
+
|
|
67
97
|
const hasOnScrub = onScrub != null;
|
|
98
|
+
const hasOnGestureStart = onGestureStart != null;
|
|
99
|
+
const hasOnGestureEnd = onGestureEnd != null;
|
|
68
100
|
|
|
69
101
|
useAnimatedReaction(
|
|
70
102
|
() => {
|
|
@@ -77,14 +109,14 @@ export function useCrosshairSeries(
|
|
|
77
109
|
if (chartW <= 0) return "__pending__";
|
|
78
110
|
const r = interpolateSeriesAtTime(engine.series.get(), time);
|
|
79
111
|
if (r.primary === null) return "__pending__";
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
112
|
+
const dotY = computeScrubDotY(
|
|
113
|
+
r.primary,
|
|
114
|
+
engine.displayMin.get(),
|
|
115
|
+
engine.displayMax.get(),
|
|
116
|
+
engine.canvasHeight.get(),
|
|
117
|
+
padding.top,
|
|
118
|
+
padding.bottom,
|
|
119
|
+
);
|
|
88
120
|
return JSON.stringify({
|
|
89
121
|
time,
|
|
90
122
|
x,
|
|
@@ -136,6 +168,8 @@ export function useCrosshairSeries(
|
|
|
136
168
|
if (!enabled) return;
|
|
137
169
|
scrubX.set(e.x);
|
|
138
170
|
scrubActive.set(true);
|
|
171
|
+
gestureStarted.set(true);
|
|
172
|
+
if (hasOnGestureStart) scheduleOnRN(handleGestureStart);
|
|
139
173
|
},
|
|
140
174
|
)
|
|
141
175
|
.onUpdate(
|
|
@@ -149,6 +183,10 @@ export function useCrosshairSeries(
|
|
|
149
183
|
/* istanbul ignore next */ () => {
|
|
150
184
|
"worklet";
|
|
151
185
|
scrubActive.set(false);
|
|
186
|
+
if (gestureStarted.get()) {
|
|
187
|
+
gestureStarted.set(false);
|
|
188
|
+
if (hasOnGestureEnd) scheduleOnRN(handleGestureEnd);
|
|
189
|
+
}
|
|
152
190
|
},
|
|
153
191
|
);
|
|
154
192
|
|
|
@@ -164,6 +202,7 @@ export function useCrosshairSeries(
|
|
|
164
202
|
scrubValue,
|
|
165
203
|
crosshairOpacity,
|
|
166
204
|
tooltipLayout,
|
|
205
|
+
scrubDotY,
|
|
167
206
|
gesture,
|
|
168
207
|
};
|
|
169
208
|
}
|
package/src/hooks/useDegen.ts
CHANGED
|
@@ -20,6 +20,8 @@ export function useDegen(
|
|
|
20
20
|
momentumSV: SharedValue<Momentum>,
|
|
21
21
|
cfg: ResolvedDegenConfig | null,
|
|
22
22
|
onShake?: (payload: DegenShakePayload) => void,
|
|
23
|
+
/** Static charts run no loops: register the frame callback without starting it. */
|
|
24
|
+
isStatic = false,
|
|
23
25
|
): {
|
|
24
26
|
pack: SharedValue<Float64Array<ArrayBuffer>>;
|
|
25
27
|
packRevision: SharedValue<number>;
|
|
@@ -248,6 +250,10 @@ export function useDegen(
|
|
|
248
250
|
}
|
|
249
251
|
prevActiveCount.set(activeCount);
|
|
250
252
|
},
|
|
253
|
+
// Static charts register the callback but never start it, so no degen frame
|
|
254
|
+
// loop runs. Live charts keep the default (autostart) loop unchanged. The
|
|
255
|
+
// controller also forces `cfg` to null in static, so degen is doubly off.
|
|
256
|
+
!isStatic,
|
|
251
257
|
);
|
|
252
258
|
|
|
253
259
|
const shakeTransform = useDerivedValue(() => {
|
package/src/hooks/useMarkers.ts
CHANGED
|
@@ -30,6 +30,8 @@ export function useMarkers(
|
|
|
30
30
|
onMarkerHover?: (event: MarkerHoverEvent | null) => void,
|
|
31
31
|
seriesSV?: SharedValue<SeriesConfig[]>,
|
|
32
32
|
lineData?: SharedValue<LiveChartPoint[]>,
|
|
33
|
+
/** Static charts run no loops: register without starting. Default `true`. */
|
|
34
|
+
autostart = true,
|
|
33
35
|
): { projected: SharedValue<ProjectedMarker[]>; tapGesture: ReturnType<typeof Gesture.Tap> } {
|
|
34
36
|
const projected = useSharedValue<ProjectedMarker[]>([]);
|
|
35
37
|
const cacheRef = useRef<{
|
|
@@ -73,6 +75,7 @@ export function useMarkers(
|
|
|
73
75
|
});
|
|
74
76
|
projected.set(buf);
|
|
75
77
|
},
|
|
78
|
+
autostart,
|
|
76
79
|
);
|
|
77
80
|
|
|
78
81
|
const tapGesture = Gesture.Tap().onEnd(
|
|
@@ -25,6 +25,8 @@ export function useTradeStream(
|
|
|
25
25
|
tradeStream: SharedValue<TradeEvent[]>,
|
|
26
26
|
padding: ChartPadding,
|
|
27
27
|
active: boolean,
|
|
28
|
+
/** Static charts run no loops: register without starting. Default `true`. */
|
|
29
|
+
autostart = true,
|
|
28
30
|
): SharedValue<TradeMarker[]> {
|
|
29
31
|
const markers = useSharedValue<TradeMarker[]>([]);
|
|
30
32
|
const state = useSharedValue<TradeStreamState>(createTradeStreamState());
|
|
@@ -58,6 +60,7 @@ export function useTradeStream(
|
|
|
58
60
|
const chartH = chartBottom - chartTop;
|
|
59
61
|
markers.set(projectLabels(s, chartTop, chartH));
|
|
60
62
|
},
|
|
63
|
+
autostart,
|
|
61
64
|
);
|
|
62
65
|
|
|
63
66
|
return markers;
|
package/src/index.ts
CHANGED
|
@@ -28,6 +28,7 @@ export { useTradeStream } from "./hooks/useTradeStream";
|
|
|
28
28
|
// ── Types ────────────────────────────────────────────────────────────────────
|
|
29
29
|
|
|
30
30
|
export type {
|
|
31
|
+
AxisLabelConfig,
|
|
31
32
|
BadgeConfig,
|
|
32
33
|
BadgeMetrics,
|
|
33
34
|
BadgeVariant,
|
|
@@ -67,6 +68,9 @@ export type {
|
|
|
67
68
|
ScrubPointCore,
|
|
68
69
|
ScrubPointMulti,
|
|
69
70
|
ScrubSeriesValue,
|
|
71
|
+
SelectionDotConfig,
|
|
72
|
+
SelectionDotProps,
|
|
73
|
+
SelectionDotRingConfig,
|
|
70
74
|
SeriesConfig,
|
|
71
75
|
ThemeMode,
|
|
72
76
|
TradeEvent,
|
package/src/types.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ComponentType, ReactElement } from "react";
|
|
1
2
|
import type { ViewStyle } from "react-native";
|
|
2
3
|
import type { SharedValue } from "react-native-reanimated";
|
|
3
4
|
|
|
@@ -142,6 +143,11 @@ export interface LineConfig {
|
|
|
142
143
|
width?: number;
|
|
143
144
|
/** Line color override. Defaults to palette-derived accent. */
|
|
144
145
|
color?: string;
|
|
146
|
+
/**
|
|
147
|
+
* Two or more CSS color strings → horizontal gradient along the stroke
|
|
148
|
+
* (left → right). Takes precedence over `color` when set.
|
|
149
|
+
*/
|
|
150
|
+
colors?: string[];
|
|
145
151
|
}
|
|
146
152
|
|
|
147
153
|
/** Area fill gradient beneath the chart line. */
|
|
@@ -150,6 +156,11 @@ export interface GradientConfig {
|
|
|
150
156
|
topOpacity?: number;
|
|
151
157
|
/** Opacity at the bottom of the gradient. Default `0`. */
|
|
152
158
|
bottomOpacity?: number;
|
|
159
|
+
/** Explicit gradient color stops (top → bottom) for the area fill. Overrides
|
|
160
|
+
* topOpacity/bottomOpacity when provided. Must have at least 2 entries. */
|
|
161
|
+
colors?: string[];
|
|
162
|
+
/** Optional stop positions (0..1, ascending) matching `colors` length. */
|
|
163
|
+
positions?: number[];
|
|
153
164
|
}
|
|
154
165
|
|
|
155
166
|
/** Value badge pill configuration. */
|
|
@@ -170,6 +181,24 @@ export interface YAxisConfig {
|
|
|
170
181
|
minGap?: number;
|
|
171
182
|
}
|
|
172
183
|
|
|
184
|
+
/**
|
|
185
|
+
* Axis edge label — the value floated at the plot's top or bottom edge
|
|
186
|
+
* (Robinhood-style high/low). Pass `topLabel`/`bottomLabel` as `true` for the
|
|
187
|
+
* batteries-included value label (the chart's current top/bottom Y-axis bound,
|
|
188
|
+
* updated each frame), an object to configure it, or `{ render }` to float a
|
|
189
|
+
* fully custom element instead.
|
|
190
|
+
*/
|
|
191
|
+
export interface AxisLabelConfig {
|
|
192
|
+
/** Formatter for the built-in value. Defaults to the chart's `formatValue`. */
|
|
193
|
+
format?: (v: number) => string;
|
|
194
|
+
/** Text color. Defaults to a muted label color (`palette.gridLabel`). */
|
|
195
|
+
color?: string;
|
|
196
|
+
/** Horizontal alignment within the plot width. Default `"right"`. */
|
|
197
|
+
position?: "left" | "right";
|
|
198
|
+
/** Full custom element, floated at the edge. Overrides the built-in value label. */
|
|
199
|
+
render?: () => ReactElement | null;
|
|
200
|
+
}
|
|
201
|
+
|
|
173
202
|
/** X-axis (time) configuration. */
|
|
174
203
|
export interface XAxisConfig {
|
|
175
204
|
/** Minimum pixel gap between time labels. Default `60`. */
|
|
@@ -213,6 +242,55 @@ export interface ScrubConfig {
|
|
|
213
242
|
panGestureDelay?: number;
|
|
214
243
|
}
|
|
215
244
|
|
|
245
|
+
/**
|
|
246
|
+
* Props passed to a custom {@link SelectionDotConfig.component} — the dot drawn
|
|
247
|
+
* at the scrub intersection while scrubbing. All positional inputs are
|
|
248
|
+
* SharedValues so the dot animates on the UI thread without re-renders.
|
|
249
|
+
*/
|
|
250
|
+
export interface SelectionDotProps {
|
|
251
|
+
/** Scrub X in canvas px. */
|
|
252
|
+
x: SharedValue<number>;
|
|
253
|
+
/** Scrub Y in canvas px (the line/value intersection). */
|
|
254
|
+
y: SharedValue<number>;
|
|
255
|
+
/** Whether scrubbing is active. */
|
|
256
|
+
active: SharedValue<boolean>;
|
|
257
|
+
/** Crosshair fade opacity (0..1), already ramped. */
|
|
258
|
+
opacity: SharedValue<number>;
|
|
259
|
+
/** Resolved dot color (accent/series color). */
|
|
260
|
+
color: string;
|
|
261
|
+
/** Suggested dot radius in px. */
|
|
262
|
+
size: number;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/** Outer ring drawn around the built-in selection dot (the subtle halo). */
|
|
266
|
+
export interface SelectionDotRingConfig {
|
|
267
|
+
/** Ring color. Defaults to the dot color. */
|
|
268
|
+
color?: string;
|
|
269
|
+
/** Ring thickness in pixels — how far the halo extends past the dot. Default `2`. */
|
|
270
|
+
width?: number;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Selection-dot styling — the dot drawn at the scrub intersection while
|
|
275
|
+
* scrubbing. Pass `selectionDot={false}` to hide it, an object to configure the
|
|
276
|
+
* built-in dot, or `{ component }` to supply a fully custom Skia dot.
|
|
277
|
+
*/
|
|
278
|
+
export interface SelectionDotConfig {
|
|
279
|
+
/** Dot radius in px. Default `4`. */
|
|
280
|
+
size?: number;
|
|
281
|
+
/** Dot color. Defaults to the line / leading-series color. */
|
|
282
|
+
color?: string;
|
|
283
|
+
/**
|
|
284
|
+
* Outer ring around the dot. `true`/config = on, `false` = off. Default on.
|
|
285
|
+
*/
|
|
286
|
+
ring?: boolean | SelectionDotRingConfig;
|
|
287
|
+
/**
|
|
288
|
+
* Fully custom Skia dot — receives the scrub position as SharedValues. When
|
|
289
|
+
* set, the `size` / `color` / `ring` knobs are ignored.
|
|
290
|
+
*/
|
|
291
|
+
component?: ComponentType<SelectionDotProps>;
|
|
292
|
+
}
|
|
293
|
+
|
|
216
294
|
/** Left-edge fade — soft erase so the chart blends into the left gutter (web liveline parity). */
|
|
217
295
|
export interface LeftEdgeFadeConfig {
|
|
218
296
|
/**
|
|
@@ -695,6 +773,20 @@ export interface LiveChartCoreProps {
|
|
|
695
773
|
yAxis?: boolean | YAxisConfig;
|
|
696
774
|
/** X-axis time labels. `true` = defaults, `false` = hidden, or pass `XAxisConfig`. Default `true`. */
|
|
697
775
|
xAxis?: boolean | XAxisConfig;
|
|
776
|
+
/**
|
|
777
|
+
* Label floated at the top edge of the plot area. `true` = the built-in value
|
|
778
|
+
* label showing the chart's current TOP Y-axis bound (updated each frame),
|
|
779
|
+
* `false`/omitted = none, or pass `AxisLabelConfig` to configure it (or supply
|
|
780
|
+
* a custom `render`). Default off.
|
|
781
|
+
*/
|
|
782
|
+
topLabel?: boolean | AxisLabelConfig;
|
|
783
|
+
/**
|
|
784
|
+
* Label floated at the bottom edge of the plot area. `true` = the built-in
|
|
785
|
+
* value label showing the chart's current BOTTOM Y-axis bound (updated each
|
|
786
|
+
* frame), `false`/omitted = none, or pass `AxisLabelConfig` (or a custom
|
|
787
|
+
* `render`). Default off.
|
|
788
|
+
*/
|
|
789
|
+
bottomLabel?: boolean | AxisLabelConfig;
|
|
698
790
|
/** Reference lines / bands drawn into the chart. Supports all three `ReferenceLine` forms. */
|
|
699
791
|
referenceLines?: ReferenceLine[];
|
|
700
792
|
/** Per-instance grid-line styling. Pass an object to override color / width / dash / opacity. */
|
|
@@ -737,6 +829,16 @@ export interface LiveChartCoreProps {
|
|
|
737
829
|
accessibilityRole?: "image" | "none" | "adjustable" | "summary";
|
|
738
830
|
/** Crosshair scrubbing on hover/drag. `true` = defaults, `false` = disabled, or pass `ScrubConfig`. Default `true`. */
|
|
739
831
|
scrub?: boolean | ScrubConfig;
|
|
832
|
+
/**
|
|
833
|
+
* Selection dot drawn at the scrub intersection while scrubbing. `true`/omitted
|
|
834
|
+
* = built-in dot, `false` = hidden, or pass `SelectionDotConfig` (`size`,
|
|
835
|
+
* `color`, `ring`, or a custom `component`). Default `true`.
|
|
836
|
+
*/
|
|
837
|
+
selectionDot?: boolean | SelectionDotConfig;
|
|
838
|
+
/** Called once when the user starts scrubbing/panning the chart. */
|
|
839
|
+
onGestureStart?: () => void;
|
|
840
|
+
/** Called once when the user stops scrubbing/panning the chart. */
|
|
841
|
+
onGestureEnd?: () => void;
|
|
740
842
|
/**
|
|
741
843
|
* Fade out chart content on the left (destination-out style). `true` = defaults, `false` = off,
|
|
742
844
|
* or pass `LeftEdgeFadeConfig`. Default `true`.
|
|
@@ -796,6 +898,10 @@ export interface LiveChartProps extends LiveChartCoreProps {
|
|
|
796
898
|
data: SharedValue<LiveChartPoint[]>;
|
|
797
899
|
/** Latest live value for smooth interpolation between data updates. */
|
|
798
900
|
value: SharedValue<number>;
|
|
901
|
+
/** Render once with no per-frame animation loop — for many small charts (sparklines)
|
|
902
|
+
* in a list. Pulse/scrub/degen and the entry animation are disabled. Frame the data
|
|
903
|
+
* with `timeWindow` + `nowOverride` (see the historical-data-fill pattern). */
|
|
904
|
+
static?: boolean;
|
|
799
905
|
/** Called when the user scrubs the crosshair. `null` when scrub ends. */
|
|
800
906
|
onScrub?: (point: ScrubPoint | null) => void;
|
|
801
907
|
/**
|