react-native-vroom-chart 0.16.0 → 0.18.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 +1 -1
- package/android/CMakeLists.txt +12 -2
- package/android/README.md +16 -6
- package/android/build.gradle +10 -0
- package/android/src/main/cpp/OnLoad.cpp +10 -0
- package/android/src/main/cpp/VroomChartJsiBindings.cpp +35 -0
- package/android/src/main/cpp/VroomChartJsiBindings.h +27 -0
- package/android/src/main/java/com/vroom/chart/VroomChartModule.kt +15 -11
- package/cpp/VroomChartHostObject.cpp +81 -0
- package/cpp/VroomJsiInstaller.cpp +6 -0
- package/cpp/_core_include/vroom/vroom_chart.h +41 -0
- package/cpp/_core_src/candles.cpp +15 -14
- package/cpp/_core_src/chart.cpp +297 -28
- package/cpp/_core_src/chart.h +71 -2
- package/cpp/_core_src/chart_facade.cpp +27 -0
- package/cpp/_core_src/color_lerp.h +28 -0
- package/cpp/_core_src/loading_line.cpp +183 -0
- package/cpp/_core_src/loading_line.h +38 -0
- package/cpp/_core_src/loading_wave.h +140 -0
- package/cpp/_core_src/ma_overlay.cpp +15 -10
- package/cpp/_core_src/ma_overlay.h +7 -0
- package/cpp/_core_src/price_indicator.cpp +21 -7
- package/cpp/_core_src/price_indicator.h +11 -1
- package/cpp/_core_src/price_indicator_anim.h +81 -0
- package/cpp/_core_src/theme.cpp +5 -0
- package/cpp/_core_src/tip_geometry.h +55 -0
- package/cpp/_core_src/viewport.h +33 -0
- package/ios/README.md +17 -5
- package/ios/VroomChartModule.h +3 -6
- package/ios/VroomChartModule.mm +17 -23
- package/lib/index.d.mts +30 -0
- package/lib/index.d.ts +30 -0
- package/lib/index.js +75 -14
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +75 -14
- package/lib/index.mjs.map +1 -1
- package/package.json +9 -9
- package/react-native-vroom-chart.podspec +1 -1
- package/src/NativeVroomChart.ts +7 -4
- package/src/VroomChart.tsx +12 -0
- package/src/jsi.d.ts +42 -0
- package/src/theme.ts +1 -0
- package/src/useChartCore.ts +129 -7
- package/android/src/main/cpp/VroomChartJni.cpp +0 -24
package/lib/index.mjs
CHANGED
|
@@ -158,8 +158,10 @@ var COLOR_KEYS = {
|
|
|
158
158
|
// VROOM_COLOR_ACCENT_BULL
|
|
159
159
|
accentBear: 15,
|
|
160
160
|
// VROOM_COLOR_ACCENT_BEAR
|
|
161
|
-
lineColor: 16
|
|
161
|
+
lineColor: 16,
|
|
162
162
|
// VROOM_COLOR_LINE
|
|
163
|
+
skeleton: 17
|
|
164
|
+
// VROOM_COLOR_SKELETON
|
|
163
165
|
};
|
|
164
166
|
var FLOAT_KEYS = {
|
|
165
167
|
wickWidth: 1,
|
|
@@ -452,14 +454,15 @@ var EMPTY_FOOTPRINTS = footprintsToSpec({ prints: [] });
|
|
|
452
454
|
var installed = false;
|
|
453
455
|
function ensureInstalled() {
|
|
454
456
|
if (installed) return;
|
|
455
|
-
|
|
456
|
-
if (!ok) throw new Error("VroomChartModule.install() returned false");
|
|
457
|
+
NativeVroomChart_default.install();
|
|
457
458
|
if (typeof globalThis.VroomChartJSI === "undefined") {
|
|
458
|
-
throw new Error(
|
|
459
|
+
throw new Error(
|
|
460
|
+
"global.VroomChartJSI is undefined \u2014 VroomChartModule did not install its JSI bindings. This requires react-native >= 0.78 with the New Architecture enabled."
|
|
461
|
+
);
|
|
459
462
|
}
|
|
460
463
|
installed = true;
|
|
461
464
|
}
|
|
462
|
-
function useChartCore(candles, size, visibleRange, defaultCandleWidth, chartType, theme, rsi, macd, atr, movingAverages, vwap, bollingerBands, ichimoku, fairValueGaps, volume, priceLines, footprints, transition) {
|
|
465
|
+
function useChartCore(candles, size, visibleRange, defaultCandleWidth, chartType, theme, rsi, macd, atr, movingAverages, vwap, bollingerBands, ichimoku, fairValueGaps, volume, priceLines, footprints, transition, loading) {
|
|
463
466
|
const handleRef = useRef(null);
|
|
464
467
|
const defaultWidthAppliedRef = useRef(false);
|
|
465
468
|
const volumeCollapseRef = useRef(null);
|
|
@@ -494,18 +497,26 @@ function useChartCore(candles, size, visibleRange, defaultCandleWidth, chartType
|
|
|
494
497
|
const onFrameRef = useRef(transition?.onFrame);
|
|
495
498
|
onFrameRef.current = transition?.onFrame;
|
|
496
499
|
const seriesKey = transition?.seriesKey;
|
|
500
|
+
const loadingHandoffRef = useRef(false);
|
|
497
501
|
const endIntervalMorph = useCallback(() => {
|
|
498
502
|
if (intervalMorphRaf.current != null) {
|
|
499
503
|
cancelAnimationFrame(intervalMorphRaf.current);
|
|
500
504
|
intervalMorphRaf.current = null;
|
|
501
505
|
}
|
|
502
|
-
handleRef.current
|
|
506
|
+
const h = handleRef.current;
|
|
507
|
+
if (loadingHandoffRef.current) {
|
|
508
|
+
loadingHandoffRef.current = false;
|
|
509
|
+
h?.setLoadingMorph(1);
|
|
510
|
+
h?.beginLoadingReveal();
|
|
511
|
+
}
|
|
512
|
+
h?.setIntervalMorph(1);
|
|
503
513
|
}, []);
|
|
504
|
-
const startIntervalMorph = useCallback((h) => {
|
|
514
|
+
const startIntervalMorph = useCallback((h, durationMs) => {
|
|
505
515
|
const { ms, easing } = animRef.current;
|
|
516
|
+
const dur = durationMs ?? ms;
|
|
506
517
|
const start = performance.now();
|
|
507
518
|
const step = (now) => {
|
|
508
|
-
const p = Math.min(1, (now - start) /
|
|
519
|
+
const p = Math.min(1, (now - start) / dur);
|
|
509
520
|
h.setIntervalMorph(p < 1 ? ease(easing, p) : 1);
|
|
510
521
|
const pic = h.render();
|
|
511
522
|
if (pic) onFrameRef.current?.(pic);
|
|
@@ -513,6 +524,31 @@ function useChartCore(candles, size, visibleRange, defaultCandleWidth, chartType
|
|
|
513
524
|
};
|
|
514
525
|
intervalMorphRaf.current = requestAnimationFrame(step);
|
|
515
526
|
}, []);
|
|
527
|
+
const startLoadingHandoff = useCallback(
|
|
528
|
+
(h) => {
|
|
529
|
+
const { ms, easing } = animRef.current;
|
|
530
|
+
const half = ms / 2;
|
|
531
|
+
loadingHandoffRef.current = true;
|
|
532
|
+
h.beginLoadingMorph();
|
|
533
|
+
const start = performance.now();
|
|
534
|
+
const step = (now) => {
|
|
535
|
+
const p = Math.min(1, (now - start) / half);
|
|
536
|
+
h.setLoadingMorph(p < 1 ? ease(easing, p) : 1);
|
|
537
|
+
const pic = h.render();
|
|
538
|
+
if (pic) onFrameRef.current?.(pic);
|
|
539
|
+
if (p < 1) {
|
|
540
|
+
intervalMorphRaf.current = requestAnimationFrame(step);
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
intervalMorphRaf.current = null;
|
|
544
|
+
loadingHandoffRef.current = false;
|
|
545
|
+
h.beginLoadingReveal();
|
|
546
|
+
startIntervalMorph(h, half);
|
|
547
|
+
};
|
|
548
|
+
intervalMorphRaf.current = requestAnimationFrame(step);
|
|
549
|
+
},
|
|
550
|
+
[startIntervalMorph]
|
|
551
|
+
);
|
|
516
552
|
const settleStream = useCallback((keepMorph = false) => {
|
|
517
553
|
if (streamRaf.current != null) {
|
|
518
554
|
cancelAnimationFrame(streamRaf.current);
|
|
@@ -582,6 +618,8 @@ function useChartCore(candles, size, visibleRange, defaultCandleWidth, chartType
|
|
|
582
618
|
const explicit = visibleRange != null;
|
|
583
619
|
const startMs = visibleRange?.startMs ?? 0;
|
|
584
620
|
const endMs = visibleRange?.endMs ?? 0;
|
|
621
|
+
const showLoadingLine = loading === true && candles.length === 0;
|
|
622
|
+
const lineUpRef = useRef(false);
|
|
585
623
|
const themeKey = theme ? JSON.stringify(theme) : "";
|
|
586
624
|
const rsiKey = rsi ? JSON.stringify(rsi) : "";
|
|
587
625
|
const macdKey = macd ? JSON.stringify(macd) : "";
|
|
@@ -604,6 +642,19 @@ function useChartCore(candles, size, visibleRange, defaultCandleWidth, chartType
|
|
|
604
642
|
defaultWidthAppliedRef.current = true;
|
|
605
643
|
}
|
|
606
644
|
let morphing = false;
|
|
645
|
+
if (showLoadingLine) {
|
|
646
|
+
if (!lineUpRef.current) {
|
|
647
|
+
endIntervalMorph();
|
|
648
|
+
settleStream();
|
|
649
|
+
h.setCandles(packCandles([]));
|
|
650
|
+
prevDataRef.current = null;
|
|
651
|
+
}
|
|
652
|
+
h.setLoading(true, !animRef.current.reduceMotion);
|
|
653
|
+
lineUpRef.current = true;
|
|
654
|
+
} else if (lineUpRef.current && candles.length === 0) {
|
|
655
|
+
h.setLoading(false, true);
|
|
656
|
+
lineUpRef.current = false;
|
|
657
|
+
}
|
|
607
658
|
if (candles.length > 0) {
|
|
608
659
|
const prev = prevDataRef.current;
|
|
609
660
|
const freshHandle = prev == null || prev.handle !== h;
|
|
@@ -611,6 +662,7 @@ function useChartCore(candles, size, visibleRange, defaultCandleWidth, chartType
|
|
|
611
662
|
const transitionKind = freshHandle ? "initial" : explicit ? "stream" : classifyTransition(prev.candles, candles, seriesKey !== prev.seriesKey);
|
|
612
663
|
let tfArgs = null;
|
|
613
664
|
let prevEnvelope = null;
|
|
665
|
+
let handOff = false;
|
|
614
666
|
let stream = null;
|
|
615
667
|
if (transitionKind === "stream" && prev != null && !explicit) {
|
|
616
668
|
const { stream: mode, streamMs, reduceMotion } = animRef.current;
|
|
@@ -660,6 +712,11 @@ function useChartCore(candles, size, visibleRange, defaultCandleWidth, chartType
|
|
|
660
712
|
} else if (transitionKind === "initial" || transitionKind === "reset") {
|
|
661
713
|
endIntervalMorph();
|
|
662
714
|
}
|
|
715
|
+
if (lineUpRef.current) {
|
|
716
|
+
lineUpRef.current = false;
|
|
717
|
+
handOff = animRef.current.ms > 0 && !animRef.current.reduceMotion && onFrameRef.current != null;
|
|
718
|
+
if (!handOff) h.setLoading(false, true);
|
|
719
|
+
}
|
|
663
720
|
h.setCandles(packCandles(candles));
|
|
664
721
|
if (transitionKind === "timeframe") {
|
|
665
722
|
const newStepMs = inferStepMs(candles);
|
|
@@ -681,6 +738,7 @@ function useChartCore(candles, size, visibleRange, defaultCandleWidth, chartType
|
|
|
681
738
|
} else if (transitionKind === "reset") {
|
|
682
739
|
h.resetView();
|
|
683
740
|
}
|
|
741
|
+
if (handOff) startLoadingHandoff(h);
|
|
684
742
|
prevDataRef.current = { handle: h, candles, seriesKey };
|
|
685
743
|
}
|
|
686
744
|
}
|
|
@@ -710,7 +768,7 @@ function useChartCore(candles, size, visibleRange, defaultCandleWidth, chartType
|
|
|
710
768
|
footprints?.prints.length ? footprintsToSpec(footprints) : EMPTY_FOOTPRINTS
|
|
711
769
|
);
|
|
712
770
|
if (!morphing) setPicture(h.render());
|
|
713
|
-
}, [candles, seriesKey, size.width, size.height, size.pxRatio, explicit, startMs, endMs, defaultCandleWidth, themeKey, rsiKey, macdKey, atrKey, maKey, vwapKey, bollingerKey, ichimokuKey, fvgKey, volumeKey, priceLinesKey, footprintsKey, startIntervalMorph, endIntervalMorph, startStreamAnim, settleStream]);
|
|
771
|
+
}, [candles, showLoadingLine, seriesKey, size.width, size.height, size.pxRatio, explicit, startMs, endMs, defaultCandleWidth, themeKey, rsiKey, macdKey, atrKey, maKey, vwapKey, bollingerKey, ichimokuKey, fvgKey, volumeKey, priceLinesKey, footprintsKey, startIntervalMorph, startLoadingHandoff, endIntervalMorph, startStreamAnim, settleStream]);
|
|
714
772
|
return { handle: handleRef.current, picture, volumeCollapseRef };
|
|
715
773
|
}
|
|
716
774
|
|
|
@@ -722,6 +780,7 @@ function isSkImage(frame) {
|
|
|
722
780
|
function VroomChart(props) {
|
|
723
781
|
const {
|
|
724
782
|
candles,
|
|
783
|
+
loading,
|
|
725
784
|
seriesKey,
|
|
726
785
|
width: widthProp,
|
|
727
786
|
height: heightProp,
|
|
@@ -839,8 +898,10 @@ function VroomChart(props) {
|
|
|
839
898
|
streamTransitionMs,
|
|
840
899
|
reduceMotion,
|
|
841
900
|
onFrame
|
|
842
|
-
}
|
|
901
|
+
},
|
|
902
|
+
loading
|
|
843
903
|
);
|
|
904
|
+
const showLoadingLine = loading === true && candles.length === 0;
|
|
844
905
|
const crosshairActive = useRef2(false);
|
|
845
906
|
const footprintActive = useRef2(false);
|
|
846
907
|
const lastCrosshairTime = useRef2(null);
|
|
@@ -1120,7 +1181,7 @@ function VroomChart(props) {
|
|
|
1120
1181
|
pane: null
|
|
1121
1182
|
});
|
|
1122
1183
|
};
|
|
1123
|
-
const pan = Gesture.Pan().runOnJS(true).maxPointers(1).onStart((e) => {
|
|
1184
|
+
const pan = Gesture.Pan().enabled(!showLoadingLine).runOnJS(true).maxPointers(1).onStart((e) => {
|
|
1124
1185
|
cancelDecay();
|
|
1125
1186
|
panMode.current = hitAxis(e.x, e.y);
|
|
1126
1187
|
priceDrag.current = null;
|
|
@@ -1215,7 +1276,7 @@ function VroomChart(props) {
|
|
|
1215
1276
|
enableX: false,
|
|
1216
1277
|
enableY: false
|
|
1217
1278
|
});
|
|
1218
|
-
const pinch = Gesture.Pinch().runOnJS(true).onTouchesDown((e) => {
|
|
1279
|
+
const pinch = Gesture.Pinch().enabled(!showLoadingLine).runOnJS(true).onTouchesDown((e) => {
|
|
1219
1280
|
if (e.numberOfTouches < 2) return;
|
|
1220
1281
|
dismissFootprint();
|
|
1221
1282
|
const [a, b] = e.allTouches;
|
|
@@ -1253,7 +1314,7 @@ function VroomChart(props) {
|
|
|
1253
1314
|
if (next) applyFrame(next);
|
|
1254
1315
|
maybeStartAnim();
|
|
1255
1316
|
});
|
|
1256
|
-
const longPress = Gesture.LongPress().runOnJS(true).onStart((e) => {
|
|
1317
|
+
const longPress = Gesture.LongPress().enabled(!showLoadingLine).runOnJS(true).onStart((e) => {
|
|
1257
1318
|
if (!handle) return;
|
|
1258
1319
|
if (hitAxis(e.x, e.y) !== "chart") return;
|
|
1259
1320
|
if (hitPriceLine(e.x, e.y)) return;
|
|
@@ -1273,7 +1334,7 @@ function VroomChart(props) {
|
|
|
1273
1334
|
reason: "show"
|
|
1274
1335
|
});
|
|
1275
1336
|
});
|
|
1276
|
-
const tap = Gesture.Tap().runOnJS(true).onStart((e) => {
|
|
1337
|
+
const tap = Gesture.Tap().enabled(!showLoadingLine).runOnJS(true).onStart((e) => {
|
|
1277
1338
|
if (!handle) return;
|
|
1278
1339
|
const prints = footprints ?? [];
|
|
1279
1340
|
const fp = prints.length ? handle.hitTestFootprint(e.x, e.y) : null;
|