react-native-livechart 4.13.1 → 4.15.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 +4 -0
- package/dist/components/LiveChart.d.ts.map +1 -1
- package/dist/components/LiveChartSeries.d.ts.map +1 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/core/liveChartEngineTick.d.ts +13 -0
- package/dist/core/liveChartEngineTick.d.ts.map +1 -1
- package/dist/core/liveChartSeriesEngineTick.d.ts +11 -0
- package/dist/core/liveChartSeriesEngineTick.d.ts.map +1 -1
- package/dist/core/resolveConfig.d.ts +8 -1
- package/dist/core/resolveConfig.d.ts.map +1 -1
- package/dist/core/useLiveChartEngine.d.ts +11 -0
- package/dist/core/useLiveChartEngine.d.ts.map +1 -1
- package/dist/core/useLiveChartSeriesEngine.d.ts +11 -0
- package/dist/core/useLiveChartSeriesEngine.d.ts.map +1 -1
- package/dist/draw/candle.d.ts.map +1 -1
- package/dist/draw/line.d.ts +1 -1
- package/dist/draw/line.d.ts.map +1 -1
- package/dist/draw/volume.d.ts.map +1 -1
- package/dist/hooks/useChartPaths.d.ts.map +1 -1
- package/dist/hooks/usePanScroll.d.ts +41 -6
- package/dist/hooks/usePanScroll.d.ts.map +1 -1
- package/dist/hooks/usePinchZoom.d.ts +8 -1
- package/dist/hooks/usePinchZoom.d.ts.map +1 -1
- package/dist/types.d.ts +24 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/LiveChart.tsx +11 -0
- package/src/components/LiveChartSeries.tsx +11 -0
- package/src/constants.ts +1 -0
- package/src/core/liveChartEngineTick.ts +53 -4
- package/src/core/liveChartSeriesEngineTick.ts +46 -3
- package/src/core/resolveConfig.ts +22 -0
- package/src/core/useLiveChartEngine.ts +19 -0
- package/src/core/useLiveChartSeriesEngine.ts +19 -0
- package/src/draw/candle.ts +5 -1
- package/src/draw/line.ts +13 -0
- package/src/draw/volume.ts +5 -1
- package/src/hooks/useChartPaths.ts +4 -0
- package/src/hooks/usePanScroll.ts +109 -11
- package/src/hooks/usePinchZoom.ts +25 -4
- package/src/types.ts +24 -0
package/src/draw/candle.ts
CHANGED
|
@@ -144,7 +144,11 @@ export function buildCandleGeometry(
|
|
|
144
144
|
const slotPx = (candleWidthSecs / windowSecs) * chartW;
|
|
145
145
|
const bodyW = Math.max(
|
|
146
146
|
1,
|
|
147
|
-
Math.min(
|
|
147
|
+
Math.min(
|
|
148
|
+
slotPx * metrics.bodyWidthRatio,
|
|
149
|
+
slotPx - metrics.minGapPx,
|
|
150
|
+
metrics.maxBodyPx,
|
|
151
|
+
),
|
|
148
152
|
);
|
|
149
153
|
|
|
150
154
|
const bodies: CandleRect[] = [];
|
package/src/draw/line.ts
CHANGED
|
@@ -229,6 +229,7 @@ export function buildLinePoints(
|
|
|
229
229
|
canvasHeight: number,
|
|
230
230
|
padding: ChartPadding,
|
|
231
231
|
out?: number[],
|
|
232
|
+
omitTipBeyondData = false,
|
|
232
233
|
): number[] {
|
|
233
234
|
"worklet";
|
|
234
235
|
const pts: number[] = out ?? [];
|
|
@@ -328,6 +329,18 @@ export function buildLinePoints(
|
|
|
328
329
|
}
|
|
329
330
|
}
|
|
330
331
|
|
|
332
|
+
// A scrolled-back / overscrolled window can end after the last data point;
|
|
333
|
+
// the synthetic tip would then fabricate a flat segment from that point to
|
|
334
|
+
// the right edge. Callers pass `omitTipBeyondData` to end the line at the
|
|
335
|
+
// last real point instead.
|
|
336
|
+
if (
|
|
337
|
+
omitTipBeyondData &&
|
|
338
|
+
endIdx === data.length &&
|
|
339
|
+
data[endIdx - 1].time < now
|
|
340
|
+
) {
|
|
341
|
+
return pts;
|
|
342
|
+
}
|
|
343
|
+
|
|
331
344
|
// Live tip at current time with smoothed value
|
|
332
345
|
pts.push(
|
|
333
346
|
padding.left + chartW,
|
package/src/draw/volume.ts
CHANGED
|
@@ -113,7 +113,11 @@ export function buildVolumeGeometry(
|
|
|
113
113
|
const slotPx = (candleWidthSecs / windowSecs) * chartW;
|
|
114
114
|
const bodyW = Math.max(
|
|
115
115
|
1,
|
|
116
|
-
Math.min(
|
|
116
|
+
Math.min(
|
|
117
|
+
slotPx * metrics.bodyWidthRatio,
|
|
118
|
+
slotPx - metrics.minGapPx,
|
|
119
|
+
metrics.maxBodyPx,
|
|
120
|
+
),
|
|
117
121
|
);
|
|
118
122
|
|
|
119
123
|
const winEnd = winStart + windowSecs;
|
|
@@ -105,6 +105,10 @@ export function useChartPaths(
|
|
|
105
105
|
engine.canvasHeight.get(),
|
|
106
106
|
padding,
|
|
107
107
|
buf,
|
|
108
|
+
// Only a live-following chart may extend the line to the right edge; a
|
|
109
|
+
// parked (scrolled-back / overscrolled) window ends at its last real
|
|
110
|
+
// point rather than fabricating a flat line into dataless space.
|
|
111
|
+
engine.viewEnd.get() != null,
|
|
108
112
|
);
|
|
109
113
|
|
|
110
114
|
// Skip blending when fully revealed or no morphT provided
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { Gesture } from "react-native-gesture-handler";
|
|
2
2
|
import {
|
|
3
3
|
cancelAnimation,
|
|
4
|
+
useAnimatedReaction,
|
|
5
|
+
useDerivedValue,
|
|
4
6
|
useSharedValue,
|
|
5
7
|
withDecay,
|
|
6
8
|
type SharedValue,
|
|
@@ -71,26 +73,85 @@ export interface UsePanScrollOptions {
|
|
|
71
73
|
* price indicator. Cleared when the finger lifts (the scrub pan's finalize).
|
|
72
74
|
*/
|
|
73
75
|
scrubActive?: SharedValue<boolean>;
|
|
76
|
+
/**
|
|
77
|
+
* Fraction of the visible window (0–1) the pan may travel past the data
|
|
78
|
+
* bounds — into blank future space beyond the live edge and blank history
|
|
79
|
+
* before the oldest point (TradingView-style free dragging). `0` (default)
|
|
80
|
+
* keeps the classic hard stops at the data. Resolved from
|
|
81
|
+
* `timeScroll.overscroll` (see `resolveOverscroll`).
|
|
82
|
+
*/
|
|
83
|
+
overscroll?: number;
|
|
74
84
|
}
|
|
75
85
|
|
|
86
|
+
/**
|
|
87
|
+
* Snap-to-follow zone around the live edge, as a fraction of the window. With
|
|
88
|
+
* overscroll a released drag/fling that settles within this zone re-attaches to
|
|
89
|
+
* live; anything further out stays parked where it stopped.
|
|
90
|
+
*/
|
|
91
|
+
export const FOLLOW_SNAP = 0.02;
|
|
92
|
+
|
|
76
93
|
/**
|
|
77
94
|
* Smallest valid right-edge time: keeps the window's left edge
|
|
78
95
|
* (`rightEdge - window`) from passing `minTime`, and never exceeds the live edge
|
|
79
|
-
* so the `[lo, liveEdge]` clamp range stays valid when history is short.
|
|
96
|
+
* so the `[lo, liveEdge]` clamp range stays valid when history is short. With
|
|
97
|
+
* `overscroll` the left edge may pass `minTime` by that fraction of the window,
|
|
98
|
+
* exposing blank space before the oldest data.
|
|
80
99
|
*/
|
|
81
100
|
export function panLowerBound(
|
|
82
101
|
minTime: number,
|
|
83
102
|
windowSecs: number,
|
|
84
103
|
liveEdge: number,
|
|
104
|
+
overscroll: number = 0,
|
|
105
|
+
): number {
|
|
106
|
+
"worklet";
|
|
107
|
+
return Math.min(minTime + windowSecs * (1 - overscroll), liveEdge);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Largest valid right-edge time: the live edge, pushed past it by `overscroll`
|
|
112
|
+
* (a fraction of the window) so the latest data can be dragged toward the middle
|
|
113
|
+
* of the plot, leaving blank future space on the right. `0` = the live edge.
|
|
114
|
+
*/
|
|
115
|
+
export function panUpperBound(
|
|
116
|
+
windowSecs: number,
|
|
117
|
+
liveEdge: number,
|
|
118
|
+
overscroll: number,
|
|
85
119
|
): number {
|
|
86
120
|
"worklet";
|
|
87
|
-
return
|
|
121
|
+
return liveEdge + windowSecs * overscroll;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Re-clamp a parked right edge after the overscroll setting changes. Reducing
|
|
126
|
+
* the allowance must not leave a stale future/history position outside the new
|
|
127
|
+
* bounds; at the classic live-edge hard stop, `null` resumes following live.
|
|
128
|
+
*/
|
|
129
|
+
export function clampViewEndForOverscroll(
|
|
130
|
+
viewEnd: number,
|
|
131
|
+
minTime: number,
|
|
132
|
+
windowSecs: number,
|
|
133
|
+
liveEdge: number,
|
|
134
|
+
overscroll: number,
|
|
135
|
+
): number | null {
|
|
136
|
+
"worklet";
|
|
137
|
+
const lo = panLowerBound(minTime, windowSecs, liveEdge, overscroll);
|
|
138
|
+
if (viewEnd < lo) return lo;
|
|
139
|
+
const hi = panUpperBound(windowSecs, liveEdge, overscroll);
|
|
140
|
+
if (viewEnd >= hi) return overscroll > 0 ? hi : null;
|
|
141
|
+
return viewEnd;
|
|
88
142
|
}
|
|
89
143
|
|
|
90
144
|
/**
|
|
91
145
|
* Next right-edge time after dragging `changeX` px (drag right ⇒ reveal earlier
|
|
92
|
-
* time ⇒ smaller right edge), clamped to `[lo,
|
|
93
|
-
* the drag reaches the live edge — the signal to
|
|
146
|
+
* time ⇒ smaller right edge), clamped to `[lo, panUpperBound]`. Without
|
|
147
|
+
* overscroll, returns `null` once the drag reaches the live edge — the signal to
|
|
148
|
+
* resume following.
|
|
149
|
+
*
|
|
150
|
+
* With overscroll it must NOT return `null` mid-drag: the pan's `onChange`
|
|
151
|
+
* re-derives `cur` from `viewEnd ?? liveEdge` each frame, so snapping to follow
|
|
152
|
+
* re-anchors every per-frame delta at the live edge and the drag can't escape it
|
|
153
|
+
* in either direction. Re-attaching to live happens only in the release decay
|
|
154
|
+
* callback (see `usePanScroll`), inside the {@link FOLLOW_SNAP} zone.
|
|
94
155
|
*/
|
|
95
156
|
export function nextViewEnd(
|
|
96
157
|
cur: number,
|
|
@@ -99,10 +160,16 @@ export function nextViewEnd(
|
|
|
99
160
|
windowSecs: number,
|
|
100
161
|
liveEdge: number,
|
|
101
162
|
lo: number,
|
|
163
|
+
overscroll: number = 0,
|
|
102
164
|
): number | null {
|
|
103
165
|
"worklet";
|
|
104
166
|
let next = cur - (changeX / chartW) * windowSecs;
|
|
105
167
|
if (next < lo) next = lo;
|
|
168
|
+
if (overscroll > 0) {
|
|
169
|
+
const hi = panUpperBound(windowSecs, liveEdge, overscroll);
|
|
170
|
+
if (next > hi) next = hi;
|
|
171
|
+
return next;
|
|
172
|
+
}
|
|
106
173
|
if (next > liveEdge) next = liveEdge;
|
|
107
174
|
return next >= liveEdge ? null : next;
|
|
108
175
|
}
|
|
@@ -150,6 +217,7 @@ export function usePanScroll({
|
|
|
150
217
|
onScrollStart,
|
|
151
218
|
scrollActive,
|
|
152
219
|
scrubActive,
|
|
220
|
+
overscroll = 0,
|
|
153
221
|
}: UsePanScrollOptions): ReturnType<typeof Gesture.Pan> {
|
|
154
222
|
const { viewEnd, liveEdge, displayWindow, canvasWidth, canvasHeight } = engine;
|
|
155
223
|
const padLeft = padding.left;
|
|
@@ -161,6 +229,31 @@ export function usePanScroll({
|
|
|
161
229
|
const startX = useSharedValue(0);
|
|
162
230
|
const startY = useSharedValue(0);
|
|
163
231
|
const armed = useSharedValue(false);
|
|
232
|
+
const overscrollSV = useDerivedValue(() => overscroll);
|
|
233
|
+
|
|
234
|
+
// A runtime config change (the demo exposes Off / 50% / 90%) applies to an
|
|
235
|
+
// already parked window immediately. Without this, disabling overscroll while
|
|
236
|
+
// parked in the future leaves a numeric `viewEnd`; re-enabling it later revives
|
|
237
|
+
// that stale future position and makes the chart jump without a gesture.
|
|
238
|
+
useAnimatedReaction(
|
|
239
|
+
() => overscrollSV.value,
|
|
240
|
+
/* istanbul ignore next -- UI-thread config reaction; pure clamp is unit-tested */
|
|
241
|
+
(nextOverscroll) => {
|
|
242
|
+
const cur = viewEnd.get();
|
|
243
|
+
if (cur == null) return;
|
|
244
|
+
const next = clampViewEndForOverscroll(
|
|
245
|
+
cur,
|
|
246
|
+
minTime.get(),
|
|
247
|
+
displayWindow.get(),
|
|
248
|
+
liveEdge.get(),
|
|
249
|
+
nextOverscroll,
|
|
250
|
+
);
|
|
251
|
+
if (next !== cur) {
|
|
252
|
+
cancelAnimation(viewEnd);
|
|
253
|
+
viewEnd.set(next);
|
|
254
|
+
}
|
|
255
|
+
},
|
|
256
|
+
);
|
|
164
257
|
|
|
165
258
|
const onStart =
|
|
166
259
|
/* istanbul ignore next -- gesture worklet runs on the UI thread, not in Jest */
|
|
@@ -194,8 +287,8 @@ export function usePanScroll({
|
|
|
194
287
|
if (chartW <= 0) return;
|
|
195
288
|
const edge = liveEdge.get();
|
|
196
289
|
const cur = viewEnd.get() ?? edge;
|
|
197
|
-
const lo = panLowerBound(minTime.get(), win, edge);
|
|
198
|
-
viewEnd.set(nextViewEnd(cur, e.changeX, chartW, win, edge, lo));
|
|
290
|
+
const lo = panLowerBound(minTime.get(), win, edge, overscroll);
|
|
291
|
+
viewEnd.set(nextViewEnd(cur, e.changeX, chartW, win, edge, lo, overscroll));
|
|
199
292
|
};
|
|
200
293
|
|
|
201
294
|
const onEnd =
|
|
@@ -208,15 +301,20 @@ export function usePanScroll({
|
|
|
208
301
|
const chartW = canvasWidth.get() - padLeft - padRight;
|
|
209
302
|
if (chartW <= 0) return;
|
|
210
303
|
const edge = liveEdge.get();
|
|
211
|
-
const lo = panLowerBound(minTime.get(), win, edge);
|
|
304
|
+
const lo = panLowerBound(minTime.get(), win, edge, overscroll);
|
|
305
|
+
const hi = overscroll > 0 ? panUpperBound(win, edge, overscroll) : edge;
|
|
306
|
+
// With overscroll the snap-to-follow zone widens from the exact live edge
|
|
307
|
+
// to FOLLOW_SNAP of the window around it — this release callback is the
|
|
308
|
+
// ONLY place that re-attaches to live (never mid-drag, see nextViewEnd).
|
|
309
|
+
const snapZone = overscroll > 0 ? win * FOLLOW_SNAP : 1e-3;
|
|
212
310
|
const velocity = flingVelocity(e.velocityX, chartW, win);
|
|
213
311
|
cancelAnimation(viewEnd);
|
|
214
312
|
viewEnd.set(
|
|
215
|
-
withDecay({ velocity, clamp: [lo,
|
|
313
|
+
withDecay({ velocity, clamp: [lo, hi] }, (finished) => {
|
|
216
314
|
"worklet";
|
|
217
|
-
// Landed
|
|
218
|
-
// stay frozen where inertia died.
|
|
219
|
-
if (finished && (viewEnd.get() ?? edge)
|
|
315
|
+
// Landed near the live edge → resume following; stopped short (or
|
|
316
|
+
// past, with overscroll) → stay frozen where inertia died.
|
|
317
|
+
if (finished && Math.abs((viewEnd.get() ?? edge) - edge) <= snapZone) {
|
|
220
318
|
viewEnd.set(null);
|
|
221
319
|
}
|
|
222
320
|
}),
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
} from "react-native-reanimated";
|
|
7
7
|
|
|
8
8
|
import type { ChartPadding } from "../draw/line";
|
|
9
|
-
import { panLowerBound } from "./usePanScroll";
|
|
9
|
+
import { FOLLOW_SNAP, panLowerBound, panUpperBound } from "./usePanScroll";
|
|
10
10
|
|
|
11
11
|
/** Engine SharedValues the pinch-zoom gesture reads/writes. */
|
|
12
12
|
export interface PinchZoomEngineRefs {
|
|
@@ -44,6 +44,13 @@ export interface UsePinchZoomOptions {
|
|
|
44
44
|
maxTimeWindow?: number;
|
|
45
45
|
/** Worklet fired when a pinch activates — e.g. to clear a lingering crosshair. */
|
|
46
46
|
onZoomStart?: () => void;
|
|
47
|
+
/**
|
|
48
|
+
* Fraction of the visible window (0–1) the zoomed window may travel past the
|
|
49
|
+
* data bounds — blank future space beyond the live edge, blank history before
|
|
50
|
+
* the oldest point. `0` (default) keeps the classic hard stops. Shared with
|
|
51
|
+
* {@link usePanScroll} (resolved from `timeScroll.overscroll`).
|
|
52
|
+
*/
|
|
53
|
+
overscroll?: number;
|
|
47
54
|
}
|
|
48
55
|
|
|
49
56
|
/** Clamp a window width to `[minWin, maxWin]`. */
|
|
@@ -131,6 +138,7 @@ export function usePinchZoom({
|
|
|
131
138
|
minTimeWindow,
|
|
132
139
|
maxTimeWindow,
|
|
133
140
|
onZoomStart,
|
|
141
|
+
overscroll = 0,
|
|
134
142
|
}: UsePinchZoomOptions): ReturnType<typeof Gesture.Pinch> {
|
|
135
143
|
const { viewWindow, viewEnd, liveEdge, displayWindow, canvasWidth } = engine;
|
|
136
144
|
const padLeft = padding.left;
|
|
@@ -175,14 +183,27 @@ export function usePinchZoom({
|
|
|
175
183
|
// doesn't compound; the right edge then keeps it under the (live) focal.
|
|
176
184
|
const focalT = focalTime(e.focalX, padLeft, chartW, sEnd - sWin, sWin);
|
|
177
185
|
let newEnd = zoomViewEnd(focalT, e.focalX, padLeft, chartW, newWin);
|
|
178
|
-
const lo = panLowerBound(minTime.get(), newWin, edge);
|
|
186
|
+
const lo = panLowerBound(minTime.get(), newWin, edge, overscroll);
|
|
179
187
|
if (newEnd < lo) newEnd = lo;
|
|
180
|
-
|
|
188
|
+
const hi = overscroll > 0 ? panUpperBound(newWin, edge, overscroll) : edge;
|
|
189
|
+
if (newEnd > hi) newEnd = hi;
|
|
181
190
|
viewWindow.set(newWin);
|
|
182
191
|
// Track the displayed window 1:1 (bypass the frame-loop lerp lag) so the
|
|
183
192
|
// focal anchor is pixel-accurate during the gesture.
|
|
184
193
|
displayWindow.set(newWin);
|
|
185
|
-
|
|
194
|
+
// Unlike the pan (see nextViewEnd), snapping to `null` mid-gesture is safe
|
|
195
|
+
// here: the pinch maps cumulative scale/focal from a gesture-START snapshot
|
|
196
|
+
// (`startViewEnd`), not per-frame deltas, so a snap can't re-anchor it.
|
|
197
|
+
// With overscroll, snap within the FOLLOW_SNAP zone; without, at the edge.
|
|
198
|
+
viewEnd.set(
|
|
199
|
+
overscroll > 0
|
|
200
|
+
? Math.abs(newEnd - edge) <= newWin * FOLLOW_SNAP
|
|
201
|
+
? null
|
|
202
|
+
: newEnd
|
|
203
|
+
: newEnd >= edge
|
|
204
|
+
? null
|
|
205
|
+
: newEnd,
|
|
206
|
+
);
|
|
186
207
|
};
|
|
187
208
|
|
|
188
209
|
return Gesture.Pinch().enabled(enabled).onStart(onStart).onChange(onChange);
|
package/src/types.ts
CHANGED
|
@@ -1700,6 +1700,8 @@ export interface CandleMetrics {
|
|
|
1700
1700
|
maxBodyPx: number;
|
|
1701
1701
|
/** Body width as a fraction of the per-candle slot width (0–1). Default `0.8`. */
|
|
1702
1702
|
bodyWidthRatio: number;
|
|
1703
|
+
/** Minimum horizontal gap (px) enforced between adjacent candle bodies. `0` lets `bodyWidthRatio` alone control body width on dense charts. Default `2`. */
|
|
1704
|
+
minGapPx: number;
|
|
1703
1705
|
/** Corner radius (px) of candle bodies. `0` = sharp corners. Default `0`. */
|
|
1704
1706
|
bodyRadius: number;
|
|
1705
1707
|
/** Wick (high–low line) stroke width in px. Default `1`. */
|
|
@@ -1778,6 +1780,17 @@ export interface TimeScrollConfig {
|
|
|
1778
1780
|
* visible edge price, which IS in view.
|
|
1779
1781
|
*/
|
|
1780
1782
|
hideLiveOnScrollBack?: boolean;
|
|
1783
|
+
/**
|
|
1784
|
+
* How far pan / fling / pinch may travel past the data bounds, as a fraction
|
|
1785
|
+
* of the visible window (`0`–`1`). With overscroll you can drag the latest
|
|
1786
|
+
* candle toward the middle of the plot (blank future space on the right) or
|
|
1787
|
+
* pan left past the oldest point into blank history — TradingView-style free
|
|
1788
|
+
* dragging. A released drag that settles within a small zone of the live edge
|
|
1789
|
+
* re-attaches to live. Default `0`: the window hard-stops at the oldest data
|
|
1790
|
+
* and the live edge (the classic behavior). Values outside `[0, 1)` are
|
|
1791
|
+
* clamped. Applies to the pinch-zoom (`zoom`) clamps too.
|
|
1792
|
+
*/
|
|
1793
|
+
overscroll?: number;
|
|
1781
1794
|
}
|
|
1782
1795
|
|
|
1783
1796
|
/**
|
|
@@ -1993,6 +2006,17 @@ export interface LiveChartCoreProps {
|
|
|
1993
2006
|
* axes (prices, market caps).
|
|
1994
2007
|
*/
|
|
1995
2008
|
maxValue?: number;
|
|
2009
|
+
/**
|
|
2010
|
+
* Live manual Y-range multiplier around the fitted midpoint (`1` = auto-fit).
|
|
2011
|
+
* Drive it from a gesture (e.g. dragging the price axis) to stretch or
|
|
2012
|
+
* compress the price scale TradingView-style; the auto-fit keeps tracking
|
|
2013
|
+
* the visible window underneath. Values must be positive and finite; invalid
|
|
2014
|
+
* values fall back to `1`. Read on the UI thread each frame. Supported by
|
|
2015
|
+
* both `LiveChart` and `LiveChartSeries`. Default `1`.
|
|
2016
|
+
*
|
|
2017
|
+
* @experimental
|
|
2018
|
+
*/
|
|
2019
|
+
yRangeScale?: SharedValue<number>;
|
|
1996
2020
|
/**
|
|
1997
2021
|
* Label in the empty state when `loading` is false and there are fewer than
|
|
1998
2022
|
* two samples (line points or committed candles). Default `"No data"`.
|