react-native-livechart 4.22.2 → 4.23.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/DotOverlay.d.ts +8 -1
- package/dist/components/DotOverlay.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/YAxisOverlay.d.ts +3 -1
- package/dist/components/YAxisOverlay.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/resolveConfig.d.ts +1 -0
- package/dist/core/resolveConfig.d.ts.map +1 -1
- package/dist/core/useLiveChartEngine.d.ts +5 -1
- package/dist/core/useLiveChartEngine.d.ts.map +1 -1
- package/dist/hooks/useBadge.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/useDegen.d.ts +1 -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/useReferenceDrag.d.ts.map +1 -1
- package/dist/hooks/useReferenceLine.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/lib/measureFontTextWidth.d.ts +10 -1
- package/dist/lib/measureFontTextWidth.d.ts.map +1 -1
- package/dist/math/referenceDrag.d.ts +9 -0
- package/dist/math/referenceDrag.d.ts.map +1 -1
- package/dist/types.d.ts +29 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/DotOverlay.tsx +36 -1
- package/src/components/LiveChart.tsx +26 -3
- package/src/components/LiveChartSeries.tsx +51 -23
- package/src/components/YAxisOverlay.tsx +16 -10
- package/src/core/liveChartEngineTick.ts +99 -6
- package/src/core/resolveConfig.ts +2 -0
- package/src/core/useLiveChartEngine.ts +108 -41
- package/src/hooks/useBadge.ts +7 -2
- package/src/hooks/useCandlePaths.ts +10 -8
- package/src/hooks/useDegen.ts +2 -0
- package/src/hooks/useMarkers.ts +3 -0
- package/src/hooks/useReferenceDrag.ts +12 -7
- package/src/hooks/useReferenceLine.ts +30 -4
- package/src/hooks/useTradeStream.ts +3 -0
- package/src/index.ts +1 -0
- package/src/lib/measureFontTextWidth.ts +26 -2
- package/src/math/referenceDrag.ts +19 -0
- package/src/types.ts +30 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useDerivedValue, type SharedValue } from "react-native-reanimated";
|
|
2
|
+
import { useRef } from "react";
|
|
2
3
|
|
|
3
4
|
import type { SkFont } from "@shopify/react-native-skia";
|
|
4
5
|
import type { ChartEngineLayout } from "../core/useLiveChartEngine";
|
|
@@ -7,7 +8,10 @@ import {
|
|
|
7
8
|
type YAxisEntry,
|
|
8
9
|
} from "../draw/grid";
|
|
9
10
|
import type { ChartPadding } from "../draw/line";
|
|
10
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
measureFontTextWidth,
|
|
13
|
+
type TextWidthCache,
|
|
14
|
+
} from "../lib/measureFontTextWidth";
|
|
11
15
|
import {
|
|
12
16
|
classifyReferenceEdge,
|
|
13
17
|
referenceLineForm,
|
|
@@ -120,6 +124,7 @@ function badgeGeometry(
|
|
|
120
124
|
x1: number,
|
|
121
125
|
x2: number,
|
|
122
126
|
font: SkFont,
|
|
127
|
+
textWidthCache?: TextWidthCache,
|
|
123
128
|
): BadgeGeometry {
|
|
124
129
|
"worklet";
|
|
125
130
|
// Measure the icon's visual bounds (not just width): `SkiaText` draws from the
|
|
@@ -129,7 +134,9 @@ function badgeGeometry(
|
|
|
129
134
|
// price-pill centering in computeActionBadgeLayout.
|
|
130
135
|
const iconBounds = icon ? font.measureText(icon) : null;
|
|
131
136
|
const iconW = iconBounds ? iconBounds.width : 0;
|
|
132
|
-
const textW = text
|
|
137
|
+
const textW = text
|
|
138
|
+
? measureFontTextWidth(font, text, textWidthCache)
|
|
139
|
+
: 0;
|
|
133
140
|
|
|
134
141
|
let contentW = 0;
|
|
135
142
|
let count = 0;
|
|
@@ -310,6 +317,7 @@ export function useReferenceLine(
|
|
|
310
317
|
const form = line ? referenceLineForm(line) : "none";
|
|
311
318
|
// Badge presentation depends only on the (stable) line props — resolve once.
|
|
312
319
|
const badge = line ? resolveReferenceBadge(line) : null;
|
|
320
|
+
const textWidthCacheRef = useRef<TextWidthCache>({});
|
|
313
321
|
|
|
314
322
|
return useDerivedValue<ReferenceLineLayout>(() => {
|
|
315
323
|
if (!line || form === "none") return INVISIBLE;
|
|
@@ -447,7 +455,16 @@ export function useReferenceLine(
|
|
|
447
455
|
? chartTop + OFF_AXIS_EDGE_INSET
|
|
448
456
|
: chartBottom - OFF_AXIS_EDGE_INSET;
|
|
449
457
|
const text = referenceBadgeText(line, badge, v, formatValue, true);
|
|
450
|
-
const g = badgeGeometry(
|
|
458
|
+
const g = badgeGeometry(
|
|
459
|
+
badge.position,
|
|
460
|
+
badge.icon,
|
|
461
|
+
text,
|
|
462
|
+
true,
|
|
463
|
+
x1,
|
|
464
|
+
x2,
|
|
465
|
+
font,
|
|
466
|
+
textWidthCacheRef.current,
|
|
467
|
+
);
|
|
451
468
|
return {
|
|
452
469
|
...INVISIBLE,
|
|
453
470
|
visible: true,
|
|
@@ -476,7 +493,16 @@ export function useReferenceLine(
|
|
|
476
493
|
// In-range pill badge (the `badge` config, not the legacy off-axis-only flag).
|
|
477
494
|
if (badge && badge.inRange) {
|
|
478
495
|
const text = referenceBadgeText(line, badge, v, formatValue, false);
|
|
479
|
-
const g = badgeGeometry(
|
|
496
|
+
const g = badgeGeometry(
|
|
497
|
+
badge.position,
|
|
498
|
+
badge.icon,
|
|
499
|
+
text,
|
|
500
|
+
false,
|
|
501
|
+
x1,
|
|
502
|
+
x2,
|
|
503
|
+
font,
|
|
504
|
+
textWidthCacheRef.current,
|
|
505
|
+
);
|
|
480
506
|
return {
|
|
481
507
|
...INVISIBLE,
|
|
482
508
|
visible: true,
|
|
@@ -28,6 +28,8 @@ export function useTradeStream(
|
|
|
28
28
|
active: boolean,
|
|
29
29
|
/** Static charts run no loops: register without starting. Default `true`. */
|
|
30
30
|
autostart = true,
|
|
31
|
+
/** Runtime gate shared with the chart engine. */
|
|
32
|
+
isFrameLoopActive?: SharedValue<boolean>,
|
|
31
33
|
): SharedValue<TradeMarker[]> {
|
|
32
34
|
const markers = useSharedValue<TradeMarker[]>([]);
|
|
33
35
|
const state = useSharedValue<TradeStreamState>(createTradeStreamState());
|
|
@@ -37,6 +39,7 @@ export function useTradeStream(
|
|
|
37
39
|
frameInfo,
|
|
38
40
|
) => {
|
|
39
41
|
"worklet";
|
|
42
|
+
if (isFrameLoopActive?.get() === false) return;
|
|
40
43
|
if (!active) {
|
|
41
44
|
if (markers.get().length > 0) markers.set([]);
|
|
42
45
|
return;
|
package/src/index.ts
CHANGED
|
@@ -1,10 +1,34 @@
|
|
|
1
1
|
import type { SkFont } from "@shopify/react-native-skia";
|
|
2
2
|
|
|
3
|
+
/** Caller-owned last-value cache for repeated UI-thread text measurement. */
|
|
4
|
+
export interface TextWidthCache {
|
|
5
|
+
/** Font used for the cached measurement. */
|
|
6
|
+
font?: SkFont;
|
|
7
|
+
/** Text used for the cached measurement. */
|
|
8
|
+
text?: string;
|
|
9
|
+
/** Cached width. */
|
|
10
|
+
width?: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
3
13
|
/**
|
|
4
14
|
* Horizontal layout width for `text` via Skia `measureText` (replaces deprecated `getTextWidth`).
|
|
5
15
|
* Marked worklet for use inside Reanimated `useDerivedValue`.
|
|
6
16
|
*/
|
|
7
|
-
export function measureFontTextWidth(
|
|
17
|
+
export function measureFontTextWidth(
|
|
18
|
+
font: SkFont,
|
|
19
|
+
text: string,
|
|
20
|
+
cache?: TextWidthCache,
|
|
21
|
+
): number {
|
|
8
22
|
"worklet";
|
|
9
|
-
|
|
23
|
+
if (cache?.font === font && cache.text === text && cache.width !== undefined) {
|
|
24
|
+
return cache.width;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const width = font.measureText(text).width;
|
|
28
|
+
if (cache) {
|
|
29
|
+
cache.font = font;
|
|
30
|
+
cache.text = text;
|
|
31
|
+
cache.width = width;
|
|
32
|
+
}
|
|
33
|
+
return width;
|
|
10
34
|
}
|
|
@@ -43,6 +43,25 @@ export function nearestDraggableIndex(
|
|
|
43
43
|
return best;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
/**
|
|
47
|
+
* Whether the reference-drag gesture owns the current touch — the answer the
|
|
48
|
+
* scrub's activation guard needs. A **grabbed** line (`dragIndex >= 0`, pressed
|
|
49
|
+
* or already dragging) owns the touch outright, wherever the asking gesture says
|
|
50
|
+
* the finger is: the scrub pan can activate mid-drag and asks with its
|
|
51
|
+
* touch-DOWN point, which a longer drag has carried the line away from. With no
|
|
52
|
+
* line grabbed, it is the geometric reach test around the handles.
|
|
53
|
+
*/
|
|
54
|
+
export function referenceDragOwnsTouch(
|
|
55
|
+
dragIndex: number,
|
|
56
|
+
handleYs: number[],
|
|
57
|
+
y: number,
|
|
58
|
+
slop: number,
|
|
59
|
+
): boolean {
|
|
60
|
+
"worklet";
|
|
61
|
+
if (dragIndex >= 0) return true;
|
|
62
|
+
return nearestDraggableIndex(handleYs, y, slop) >= 0;
|
|
63
|
+
}
|
|
64
|
+
|
|
46
65
|
/**
|
|
47
66
|
* Resolve a grabbed line's drag intent from the touch travel (px) since the grab.
|
|
48
67
|
* Once a line is grabbed it **owns** the touch: any drag past `threshold` px in
|
package/src/types.ts
CHANGED
|
@@ -16,6 +16,16 @@ export interface LiveChartPoint {
|
|
|
16
16
|
value: number;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
/** Development counters for continuous engine-frame state updates. */
|
|
20
|
+
export interface LiveChartFrameStats {
|
|
21
|
+
/** Frame callbacks that ran while the chart was live. */
|
|
22
|
+
frames: number;
|
|
23
|
+
/** Frames that changed at least one tracked engine value. */
|
|
24
|
+
published: number;
|
|
25
|
+
/** Frames whose tracked engine values were unchanged. */
|
|
26
|
+
skipped: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
19
29
|
/** Direction of recent price movement, used for dot/badge coloring and degen effects. */
|
|
20
30
|
export type Momentum = "up" | "down" | "flat";
|
|
21
31
|
|
|
@@ -707,6 +717,9 @@ export interface VolumeConfig {
|
|
|
707
717
|
|
|
708
718
|
/** Y-axis grid configuration. */
|
|
709
719
|
export interface YAxisConfig {
|
|
720
|
+
/** Price-label side. For left placement, reserve the label gutter with insets.left.
|
|
721
|
+
* Right-side float/labelRightMargin options do not affect left labels. Default "right". */
|
|
722
|
+
side?: "left" | "right";
|
|
710
723
|
/** Minimum pixel gap between grid lines. Default `36`. */
|
|
711
724
|
minGap?: number;
|
|
712
725
|
/**
|
|
@@ -2664,6 +2677,23 @@ export interface LiveChartProps extends LiveChartCoreProps {
|
|
|
2664
2677
|
* switching back to live restarts the suspended loops and catches up. Frame the data
|
|
2665
2678
|
* with `timeWindow` + `nowOverride` (see the historical-data-fill pattern). */
|
|
2666
2679
|
static?: boolean;
|
|
2680
|
+
/**
|
|
2681
|
+
* Runtime gate for continuous frame work. Set this SharedValue to `false`
|
|
2682
|
+
* while a parent scroll gesture needs the UI thread; data keeps updating and
|
|
2683
|
+
* the chart catches up when it becomes `true`. Unlike {@link static}, this
|
|
2684
|
+
* keeps pan, zoom, scrub, and draggable reference-line gestures mounted.
|
|
2685
|
+
*
|
|
2686
|
+
* @experimental
|
|
2687
|
+
*/
|
|
2688
|
+
isFrameLoopActive?: SharedValue<boolean>;
|
|
2689
|
+
/**
|
|
2690
|
+
* Optional development counter for engine frames that did or did not change
|
|
2691
|
+
* tracked engine values. It is not a count of Skia redraws. Supplying it adds
|
|
2692
|
+
* one SharedValue write per active engine frame.
|
|
2693
|
+
*
|
|
2694
|
+
* @experimental
|
|
2695
|
+
*/
|
|
2696
|
+
debugFrameStats?: SharedValue<LiveChartFrameStats>;
|
|
2667
2697
|
/**
|
|
2668
2698
|
* Render a custom overlay floated over the chart canvas, handed a price↔pixel /
|
|
2669
2699
|
* time↔pixel {@link ChartOverlayContext} so it can track the auto-rescaling axis
|