react-native-livechart 4.21.0 → 4.22.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/dist/components/AxisLabelOverlay.d.ts +5 -1
- package/dist/components/AxisLabelOverlay.d.ts.map +1 -1
- package/dist/components/CrosshairOverlay.d.ts +4 -2
- package/dist/components/CrosshairOverlay.d.ts.map +1 -1
- package/dist/components/CustomMarkerOverlay.d.ts.map +1 -1
- package/dist/components/CustomThresholdBadgeOverlay.d.ts +3 -1
- package/dist/components/CustomThresholdBadgeOverlay.d.ts.map +1 -1
- package/dist/components/DegenParticlesOverlay.d.ts.map +1 -1
- package/dist/components/ExtremaConnectorOverlay.d.ts +5 -1
- package/dist/components/ExtremaConnectorOverlay.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/MarkerOverlay.d.ts +4 -2
- package/dist/components/MarkerOverlay.d.ts.map +1 -1
- package/dist/components/ReferenceLineOverlay.d.ts +1 -1
- package/dist/components/ReferenceLineOverlay.d.ts.map +1 -1
- package/dist/components/ReferenceLineSeriesOverlay.d.ts.map +1 -1
- package/dist/components/ThresholdLineOverlay.d.ts +3 -1
- package/dist/components/ThresholdLineOverlay.d.ts.map +1 -1
- package/dist/components/thresholdSplitShaderSource.d.ts.map +1 -1
- package/dist/core/chartDataPresence.d.ts +8 -0
- package/dist/core/chartDataPresence.d.ts.map +1 -0
- package/dist/core/liveChartEngineTick.d.ts +3 -0
- package/dist/core/liveChartEngineTick.d.ts.map +1 -1
- package/dist/core/resolveConfig.d.ts +2 -0
- package/dist/core/resolveConfig.d.ts.map +1 -1
- package/dist/core/useLiveChartEngine.d.ts +4 -0
- package/dist/core/useLiveChartEngine.d.ts.map +1 -1
- package/dist/core/useLiveChartSeriesEngine.d.ts.map +1 -1
- package/dist/draw/markerAtlas.d.ts +3 -2
- package/dist/draw/markerAtlas.d.ts.map +1 -1
- package/dist/hooks/useChartReveal.d.ts +1 -1
- package/dist/hooks/useChartReveal.d.ts.map +1 -1
- package/dist/hooks/useCrosshair.d.ts.map +1 -1
- package/dist/hooks/useDegen.d.ts.map +1 -1
- package/dist/hooks/useLiveChartHasData.d.ts +2 -2
- package/dist/hooks/useLiveChartHasData.d.ts.map +1 -1
- package/dist/hooks/useReferenceLineSeries.d.ts +1 -1
- package/dist/hooks/useReferenceLineSeries.d.ts.map +1 -1
- package/dist/hooks/useReverseMorphEngineInputs.d.ts.map +1 -1
- package/dist/hooks/useThreshold.d.ts +10 -5
- package/dist/hooks/useThreshold.d.ts.map +1 -1
- package/dist/math/referenceLineSeries.d.ts +1 -1
- package/dist/math/referenceLineSeries.d.ts.map +1 -1
- package/dist/math/threshold.d.ts +4 -3
- package/dist/math/threshold.d.ts.map +1 -1
- package/dist/types.d.ts +23 -8
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/AxisLabelOverlay.tsx +162 -81
- package/src/components/CrosshairOverlay.tsx +394 -147
- package/src/components/CustomMarkerOverlay.tsx +9 -6
- package/src/components/CustomThresholdBadgeOverlay.tsx +11 -4
- package/src/components/DegenParticlesOverlay.tsx +34 -23
- package/src/components/ExtremaConnectorOverlay.tsx +43 -3
- package/src/components/LiveChart.tsx +1709 -1025
- package/src/components/LiveChartSeries.tsx +445 -296
- package/src/components/MarkerOverlay.tsx +215 -156
- package/src/components/ReferenceLineOverlay.tsx +77 -50
- package/src/components/ReferenceLineSeriesOverlay.tsx +2 -0
- package/src/components/ThresholdLineOverlay.tsx +16 -6
- package/src/components/thresholdSplitShaderSource.ts +4 -1
- package/src/core/chartDataPresence.ts +24 -0
- package/src/core/liveChartEngineTick.ts +4 -0
- package/src/core/resolveConfig.ts +3 -0
- package/src/core/useLiveChartEngine.ts +16 -3
- package/src/core/useLiveChartSeriesEngine.ts +2 -0
- package/src/draw/markerAtlas.ts +8 -3
- package/src/hooks/useChartReveal.ts +1 -1
- package/src/hooks/useCrosshair.ts +69 -46
- package/src/hooks/useDegen.ts +82 -53
- package/src/hooks/useLiveChartHasData.ts +8 -4
- package/src/hooks/useReferenceLineSeries.ts +5 -0
- package/src/hooks/useReverseMorphEngineInputs.ts +11 -20
- package/src/hooks/useThreshold.ts +41 -15
- package/src/math/referenceLineSeries.ts +9 -4
- package/src/math/threshold.ts +10 -3
- package/src/types.ts +23 -8
|
@@ -22,6 +22,8 @@ export type ThresholdValue = SharedValue<number> | LiveChartPoint[];
|
|
|
22
22
|
|
|
23
23
|
/** `clipRight` sentinel when the threshold extends to "now" (no forward cutoff). */
|
|
24
24
|
export const THRESHOLD_NO_CLIP = 1e9;
|
|
25
|
+
/** `clipLeft` sentinel when the threshold extends to the window start. */
|
|
26
|
+
export const THRESHOLD_NO_CLIP_LEFT = -1e9;
|
|
25
27
|
|
|
26
28
|
export interface ThresholdGeometry {
|
|
27
29
|
/** Threshold pixel-Y within the canvas, or NaN before layout / degenerate range. */
|
|
@@ -104,6 +106,9 @@ export interface ThresholdSeriesGeometry {
|
|
|
104
106
|
* part of the threshold. (`visible` can still be true when that one endpoint
|
|
105
107
|
* is off-plot.) */
|
|
106
108
|
badgeVisible: SharedValue<boolean>;
|
|
109
|
+
/** Pixel-X where the threshold starts: the first point's X with
|
|
110
|
+
* `extendToStart` off, else {@link THRESHOLD_NO_CLIP_LEFT}. */
|
|
111
|
+
clipLeftX: SharedValue<number>;
|
|
107
112
|
/** Pixel-X where the threshold ends: the last point's X with `extendToNow`
|
|
108
113
|
* off, else {@link THRESHOLD_NO_CLIP}. The shader paints its plain
|
|
109
114
|
* `restColor` right of it; the marker polyline stops there. */
|
|
@@ -119,7 +124,7 @@ const EMPTY_SAMPLES: number[] = new Array(THRESHOLD_SAMPLE_COUNT).fill(0);
|
|
|
119
124
|
* `LiveChartPoint[]` `value` or a live `SharedValue<LiveChartPoint[]>` `series`
|
|
120
125
|
* (which wins when both are given): the screen polyline (marker line +
|
|
121
126
|
* fill-band bottom), the shader's pixel-Y `samples[]`, the configured endpoint
|
|
122
|
-
* value/anchor for the badge, and the
|
|
127
|
+
* value/anchor for the badge, and the endpoint cutoff X values. The array buffers
|
|
123
128
|
* ping-pong (Reanimated only re-notifies subscribers when the returned reference
|
|
124
129
|
* changes). When the threshold is a constant `SharedValue<number>` every
|
|
125
130
|
* worklet short-circuits cheaply and {@link useThreshold} drives the render
|
|
@@ -131,6 +136,7 @@ export function useThresholdSeries(
|
|
|
131
136
|
value: ThresholdValue,
|
|
132
137
|
series: SharedValue<LiveChartPoint[]> | null = null,
|
|
133
138
|
extendToNow = true,
|
|
139
|
+
extendToStart = true,
|
|
134
140
|
labelAnchor: "first" | "last" = "last",
|
|
135
141
|
): ThresholdSeriesGeometry {
|
|
136
142
|
const cacheRef = useRef<{
|
|
@@ -176,6 +182,19 @@ export function useThresholdSeries(
|
|
|
176
182
|
);
|
|
177
183
|
});
|
|
178
184
|
|
|
185
|
+
const clipLeftX = useDerivedValue(() => {
|
|
186
|
+
if (extendToStart) return THRESHOLD_NO_CLIP_LEFT;
|
|
187
|
+
const pts = series ? series.get() : Array.isArray(value) ? value : null;
|
|
188
|
+
if (pts === null || pts.length === 0) return THRESHOLD_NO_CLIP_LEFT;
|
|
189
|
+
const win = engine.displayWindow.get();
|
|
190
|
+
const plotLeft = padding.left;
|
|
191
|
+
const plotRight = engine.canvasWidth.get() - padding.right;
|
|
192
|
+
if (!(win > 0) || plotRight <= plotLeft) return THRESHOLD_NO_CLIP_LEFT;
|
|
193
|
+
const winStart = engine.timestamp.get() - win;
|
|
194
|
+
const firstT = pts[0].time;
|
|
195
|
+
return plotLeft + ((firstT - winStart) / win) * (plotRight - plotLeft);
|
|
196
|
+
});
|
|
197
|
+
|
|
179
198
|
const clipRightX = useDerivedValue(() => {
|
|
180
199
|
if (extendToNow) return THRESHOLD_NO_CLIP;
|
|
181
200
|
const pts = series ? series.get() : Array.isArray(value) ? value : null;
|
|
@@ -206,8 +225,9 @@ export function useThresholdSeries(
|
|
|
206
225
|
if (n < 2) return EMPTY_PTS;
|
|
207
226
|
const plotLeft = padding.left;
|
|
208
227
|
const plotRight = engine.canvasWidth.get() - padding.right;
|
|
228
|
+
const startX = Math.max(plotLeft, clipLeftX.get());
|
|
209
229
|
const endX = Math.min(plotRight, clipRightX.get());
|
|
210
|
-
if (endX <=
|
|
230
|
+
if (endX <= startX) return EMPTY_PTS;
|
|
211
231
|
const cache = cacheRef.current!;
|
|
212
232
|
cache.ptsTick = !cache.ptsTick;
|
|
213
233
|
const buf = cache.ptsTick ? cache.ptsA : cache.ptsB;
|
|
@@ -219,11 +239,11 @@ export function useThresholdSeries(
|
|
|
219
239
|
plotRight,
|
|
220
240
|
n,
|
|
221
241
|
);
|
|
222
|
-
buf.push(
|
|
242
|
+
buf.push(startX, sampleThresholdYAt(s, x0, x1, startX));
|
|
223
243
|
const step = (x1 - x0) / (n - 1);
|
|
224
244
|
for (let i = 0; i < n; i++) {
|
|
225
245
|
const sx = x0 + step * i;
|
|
226
|
-
if (sx >
|
|
246
|
+
if (sx > startX && sx < endX) buf.push(sx, s[i]);
|
|
227
247
|
}
|
|
228
248
|
buf.push(endX, sampleThresholdYAt(s, x0, x1, endX));
|
|
229
249
|
return buf;
|
|
@@ -242,10 +262,13 @@ export function useThresholdSeries(
|
|
|
242
262
|
const badgeValue = useDerivedValue(() => {
|
|
243
263
|
const pts = series ? series.get() : Array.isArray(value) ? value : null;
|
|
244
264
|
if (pts === null) return NaN;
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
265
|
+
let time = engine.timestamp.get();
|
|
266
|
+
if (labelAnchor === "first") {
|
|
267
|
+
time -= engine.displayWindow.get();
|
|
268
|
+
if (!extendToStart && pts.length > 0 && pts[0].time > time) {
|
|
269
|
+
time = pts[0].time;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
249
272
|
return interpolateAtTime(pts, time) ?? NaN;
|
|
250
273
|
});
|
|
251
274
|
|
|
@@ -261,11 +284,11 @@ export function useThresholdSeries(
|
|
|
261
284
|
);
|
|
262
285
|
|
|
263
286
|
const badgeVisible = useDerivedValue(() => {
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
287
|
+
// There is no endpoint to label when the series does not intersect the
|
|
288
|
+
// effective [first point, last point] window, even though interpolation can
|
|
289
|
+
// still clamp a value outside it.
|
|
290
|
+
if (screenPts.get().length < 4) return false;
|
|
291
|
+
if (labelAnchor === "last" && !extendToNow) {
|
|
269
292
|
// The threshold ends at its last point — no badge past it.
|
|
270
293
|
const pts = series ? series.get() : Array.isArray(value) ? value : null;
|
|
271
294
|
if (pts === null || pts.length === 0) return false;
|
|
@@ -286,6 +309,7 @@ export function useThresholdSeries(
|
|
|
286
309
|
badgeValue,
|
|
287
310
|
badgeLineY,
|
|
288
311
|
badgeVisible,
|
|
312
|
+
clipLeftX,
|
|
289
313
|
clipRightX,
|
|
290
314
|
};
|
|
291
315
|
}
|
|
@@ -295,8 +319,8 @@ export function useThresholdSeries(
|
|
|
295
319
|
* uniforms (one `SharedValue` per paint — stroke vs. the alpha-reduced fill band).
|
|
296
320
|
* The object is rebuilt each frame so the shader re-paints as `samples` advance;
|
|
297
321
|
* `sampleLeft`/`sampleRight` are the gliding pixel-X span of the sample grid and
|
|
298
|
-
* `clipRight`/`restColor` implement the
|
|
299
|
-
* color for the stroke, transparent for the band).
|
|
322
|
+
* `clipLeft`/`clipRight`/`restColor` implement the series endpoint cutoffs
|
|
323
|
+
* (plain line color for the stroke, transparent for the band).
|
|
300
324
|
*/
|
|
301
325
|
export function useThresholdSplitUniforms(
|
|
302
326
|
samples: SharedValue<number[]>,
|
|
@@ -305,6 +329,7 @@ export function useThresholdSplitUniforms(
|
|
|
305
329
|
aboveColor: number[],
|
|
306
330
|
belowColor: number[],
|
|
307
331
|
restColor: number[],
|
|
332
|
+
clipLeftX: SharedValue<number>,
|
|
308
333
|
clipRightX: SharedValue<number>,
|
|
309
334
|
): SharedValue<Uniforms> {
|
|
310
335
|
return useDerivedValue<Uniforms>(() => {
|
|
@@ -319,6 +344,7 @@ export function useThresholdSplitUniforms(
|
|
|
319
344
|
return {
|
|
320
345
|
sampleLeft: x0,
|
|
321
346
|
sampleRight: x1,
|
|
347
|
+
clipLeft: clipLeftX.get(),
|
|
322
348
|
clipRight: clipRightX.get(),
|
|
323
349
|
aboveColor,
|
|
324
350
|
belowColor,
|
|
@@ -20,6 +20,7 @@ export function buildReferenceLineSeriesPoints(
|
|
|
20
20
|
canvasWidth: number,
|
|
21
21
|
canvasHeight: number,
|
|
22
22
|
padding: ChartPadding,
|
|
23
|
+
extendToStart: boolean,
|
|
23
24
|
extendToNow: boolean,
|
|
24
25
|
out?: number[],
|
|
25
26
|
): number[] {
|
|
@@ -43,11 +44,15 @@ export function buildReferenceLineSeriesPoints(
|
|
|
43
44
|
}
|
|
44
45
|
|
|
45
46
|
const windowStart = now - windowSecs;
|
|
47
|
+
const firstTime = points[0].time;
|
|
46
48
|
const lastTime = points[points.length - 1].time;
|
|
49
|
+
const startTime = extendToStart
|
|
50
|
+
? windowStart
|
|
51
|
+
: Math.max(windowStart, firstTime);
|
|
47
52
|
const endTime = extendToNow ? now : Math.min(now, lastTime);
|
|
48
|
-
if (endTime <
|
|
53
|
+
if (endTime < startTime) return result;
|
|
49
54
|
|
|
50
|
-
const startValue = interpolateAtTime(points,
|
|
55
|
+
const startValue = interpolateAtTime(points, startTime);
|
|
51
56
|
const endValue = interpolateAtTime(points, endTime);
|
|
52
57
|
if (
|
|
53
58
|
startValue === null ||
|
|
@@ -70,10 +75,10 @@ export function buildReferenceLineSeriesPoints(
|
|
|
70
75
|
padding.bottom,
|
|
71
76
|
);
|
|
72
77
|
|
|
73
|
-
result.push(
|
|
78
|
+
result.push(toX(startTime), toY(startValue));
|
|
74
79
|
for (let i = 0; i < points.length; i++) {
|
|
75
80
|
const point = points[i];
|
|
76
|
-
if (point.time <=
|
|
81
|
+
if (point.time <= startTime) continue;
|
|
77
82
|
if (point.time >= endTime) break;
|
|
78
83
|
if (!Number.isFinite(point.time) || !Number.isFinite(point.value)) continue;
|
|
79
84
|
result.push(toX(point.time), toY(point.value));
|
package/src/math/threshold.ts
CHANGED
|
@@ -237,8 +237,9 @@ export function thresholdDashPhase(
|
|
|
237
237
|
* Min/max of a threshold series over the visible window `[now - windowSecs,
|
|
238
238
|
* now]` — the values folded into the engine's Y-range fit when
|
|
239
239
|
* `threshold.includeInRange` is set (like reference-line values). Uses the
|
|
240
|
-
* clamped window-edge values plus every interior point
|
|
241
|
-
* the window
|
|
240
|
+
* clamped window-edge values plus every interior point. With `extendToStart`
|
|
241
|
+
* off, the window start clamps to the series' first point; with `extendToNow`
|
|
242
|
+
* off, the window end clamps to its last point.
|
|
242
243
|
* Writes `[min, max]` into `out` and returns it, or returns null when the
|
|
243
244
|
* series is empty / entirely outside the effective window / non-finite.
|
|
244
245
|
*/
|
|
@@ -246,18 +247,24 @@ export function thresholdRangeMinMax(
|
|
|
246
247
|
points: LiveChartPoint[],
|
|
247
248
|
now: number,
|
|
248
249
|
windowSecs: number,
|
|
250
|
+
extendToStart: boolean,
|
|
249
251
|
extendToNow: boolean,
|
|
250
252
|
out: [number, number],
|
|
251
253
|
): [number, number] | null {
|
|
252
254
|
"worklet";
|
|
253
255
|
if (points.length === 0 || !(windowSecs > 0)) return null;
|
|
254
|
-
|
|
256
|
+
let tStart = now - windowSecs;
|
|
255
257
|
let tEnd = now;
|
|
258
|
+
if (!extendToStart) {
|
|
259
|
+
const firstT = points[0].time;
|
|
260
|
+
if (firstT > tStart) tStart = firstT;
|
|
261
|
+
}
|
|
256
262
|
if (!extendToNow) {
|
|
257
263
|
const lastT = points[points.length - 1].time;
|
|
258
264
|
if (lastT < tStart) return null;
|
|
259
265
|
if (lastT < tEnd) tEnd = lastT;
|
|
260
266
|
}
|
|
267
|
+
if (tEnd < tStart) return null;
|
|
261
268
|
let mn = interpolateAtTime(points, tStart)!;
|
|
262
269
|
let mx = mn;
|
|
263
270
|
const vEnd = interpolateAtTime(points, tEnd)!;
|
package/src/types.ts
CHANGED
|
@@ -74,10 +74,16 @@ export interface ReferenceLine {
|
|
|
74
74
|
/**
|
|
75
75
|
* Form B — a time-varying reference line. Points use unix-second timestamps
|
|
76
76
|
* and should be sorted oldest to newest. The first value extends to the left
|
|
77
|
-
* edge
|
|
78
|
-
* `false`. Supported by line and
|
|
77
|
+
* edge unless {@link extendToStart} is `false`; the last value extends to the
|
|
78
|
+
* live edge unless {@link extendToNow} is `false`. Supported by line and
|
|
79
|
+
* candle charts.
|
|
79
80
|
*/
|
|
80
81
|
series?: LiveChartPoint[];
|
|
82
|
+
/**
|
|
83
|
+
* Form B — extend the series' first value flat to the chart's left edge.
|
|
84
|
+
* Set `false` to start at the first point. Default `true`.
|
|
85
|
+
*/
|
|
86
|
+
extendToStart?: boolean;
|
|
81
87
|
/**
|
|
82
88
|
* Form B — extend the series' last value flat to the chart's live edge.
|
|
83
89
|
* Set `false` to stop at the last point. Default `true`.
|
|
@@ -493,8 +499,8 @@ export interface ThresholdConfig {
|
|
|
493
499
|
* - **`LiveChartPoint[]`** — a *time-varying* threshold (e.g. a historical
|
|
494
500
|
* break-even that steps up as you average in). The stroke split, fill band
|
|
495
501
|
* and marker line follow the series point-for-point. The series clamps to its
|
|
496
|
-
* first/last value outside its own time range, so
|
|
497
|
-
*
|
|
502
|
+
* first/last value outside its own time range, so it extends flat to the
|
|
503
|
+
* visible edges by default (see {@link extendToStart} and
|
|
498
504
|
* {@link extendToNow}). Flows in on re-render — pass a stable (memoized)
|
|
499
505
|
* array and reserve it for thresholds that change occasionally; for a
|
|
500
506
|
* threshold series that updates live, use {@link series} instead.
|
|
@@ -531,10 +537,19 @@ export interface ThresholdConfig {
|
|
|
531
537
|
* Fold the threshold into the Y-axis range fit — like reference lines — so a
|
|
532
538
|
* benchmark outside the data's own range stays on-plot instead of rendering
|
|
533
539
|
* invisibly (marker off-plot, whole line one color). For a series, the values
|
|
534
|
-
* visible in the current window count (respecting {@link
|
|
540
|
+
* visible in the current window count (respecting {@link extendToStart} and
|
|
541
|
+
* {@link extendToNow}).
|
|
535
542
|
* Default `false` (range fits the data only).
|
|
536
543
|
*/
|
|
537
544
|
includeInRange?: boolean;
|
|
545
|
+
/**
|
|
546
|
+
* Series forms only: extend the threshold **flat before its first point to the
|
|
547
|
+
* visible window's left edge**, carrying the first known benchmark backward.
|
|
548
|
+
* Set `false` when the benchmark did not exist yet (for example, a break-even
|
|
549
|
+
* before the first trade) — left of the first point the stroke keeps its plain
|
|
550
|
+
* line color and the band / marker do not begin. Default `true`.
|
|
551
|
+
*/
|
|
552
|
+
extendToStart?: boolean;
|
|
538
553
|
/**
|
|
539
554
|
* Series forms only: extend the threshold **flat past its last point to
|
|
540
555
|
* "now"**, carrying the last known benchmark forward. Set `false` for a
|
|
@@ -2261,7 +2276,7 @@ export interface LiveChartCoreProps {
|
|
|
2261
2276
|
transitions?: boolean | TransitionConfig;
|
|
2262
2277
|
/**
|
|
2263
2278
|
* Breathing-line loading shell. When this becomes `false`, the chart reveals
|
|
2264
|
-
* only if there is data (≥
|
|
2279
|
+
* only if there is data (≥1 line point or ≥1 committed candle).
|
|
2265
2280
|
*
|
|
2266
2281
|
* `true` shows the shell with the defaults; pass a {@link LoadingConfig} to
|
|
2267
2282
|
* restyle it — `color` / `strokeWidth` for the squiggle + skeleton, `amplitude`
|
|
@@ -2330,8 +2345,8 @@ export interface LiveChartCoreProps {
|
|
|
2330
2345
|
*/
|
|
2331
2346
|
yRangeScale?: SharedValue<number>;
|
|
2332
2347
|
/**
|
|
2333
|
-
* Label in the empty state when `loading` is false and there are
|
|
2334
|
-
*
|
|
2348
|
+
* Label in the empty state when `loading` is false and there are no line
|
|
2349
|
+
* points or committed candles. Default `"No data"`.
|
|
2335
2350
|
*/
|
|
2336
2351
|
emptyText?: string;
|
|
2337
2352
|
/** Custom formatter for value labels (axes, badge, tooltips). Default `v => v.toFixed(2)`. */
|