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/cpp/_core_src/viewport.h
CHANGED
|
@@ -92,6 +92,30 @@ struct CandleSnapshot {
|
|
|
92
92
|
float x;
|
|
93
93
|
float open, high, low, close;
|
|
94
94
|
bool bull; // close >= open; selects the fill / wick / border color
|
|
95
|
+
// Set when the capture came from the loading skeleton. Such a slot starts
|
|
96
|
+
// out grey and semi-transparent rather than in its own bull/bear color, so
|
|
97
|
+
// the draw path has to blend from `skeleton_alpha`-scaled grey instead —
|
|
98
|
+
// that color blend is what makes the hand-off read as the placeholder
|
|
99
|
+
// *becoming* the data (see candles::draw).
|
|
100
|
+
bool skeleton = false;
|
|
101
|
+
// The wave's alpha for this bar at capture time. Carried so the morph's
|
|
102
|
+
// first frame matches the skeleton frame it replaced; without it every bar
|
|
103
|
+
// would jump to full opacity the instant data landed.
|
|
104
|
+
float skeleton_alpha = 1.f;
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
// One vertex of the loading line's morph, holding both ends of the animation
|
|
108
|
+
// so a frame is a plain lerp between them.
|
|
109
|
+
//
|
|
110
|
+
// The two y's are in different spaces on purpose. The sine knows nothing about
|
|
111
|
+
// prices, so it's a fraction of the pane; the candle centre is a fraction of
|
|
112
|
+
// the price band, the same space CandleSnapshot uses — which is what keeps the
|
|
113
|
+
// line sitting exactly where the bars emerge from, since both resolve through
|
|
114
|
+
// y_at_fraction.
|
|
115
|
+
struct LinePoint {
|
|
116
|
+
float x; // fraction of the candle-area width
|
|
117
|
+
float from_y; // fraction of the pane height — the sine, frozen at capture
|
|
118
|
+
float to_y; // fraction of the price band — the candle's vertical centre
|
|
95
119
|
};
|
|
96
120
|
|
|
97
121
|
// How many captured slots still contribute to a frame — 0 once the morph is
|
|
@@ -140,6 +164,15 @@ inline void blend_candle_snapshots(CandleSnapshot* dst, std::size_t dst_n,
|
|
|
140
164
|
// `bull` stays the fresh one: the draw path colors a paired slot from
|
|
141
165
|
// the live candle and only reads the capture's flag for a slot the next
|
|
142
166
|
// update drops, where the newer direction is the better answer.
|
|
167
|
+
//
|
|
168
|
+
// The skeleton state, by contrast, carries over from the interrupted
|
|
169
|
+
// capture: a tick landing mid-hand-off must not abandon the grey blend
|
|
170
|
+
// partway, or the bar would snap to full color while its geometry is
|
|
171
|
+
// still moving. Its alpha follows the same lerp as the geometry.
|
|
172
|
+
if (from.skeleton) {
|
|
173
|
+
to.skeleton = true;
|
|
174
|
+
to.skeleton_alpha = mix(from.skeleton_alpha, 1.f);
|
|
175
|
+
}
|
|
143
176
|
}
|
|
144
177
|
}
|
|
145
178
|
|
package/ios/README.md
CHANGED
|
@@ -1,7 +1,19 @@
|
|
|
1
|
-
# iOS
|
|
1
|
+
# iOS bridge
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
A single TurboModule, [`VroomChartModule`](VroomChartModule.mm), conforming to
|
|
4
|
+
`RCTTurboModuleWithJSIBindings`. `RCTTurboModuleManager` calls
|
|
5
|
+
`-installJSIBindingsWithRuntime:callInvoker:` as soon as it instantiates the
|
|
6
|
+
module — before the module object reaches JS — and that installs
|
|
7
|
+
`global.VroomChartJSI` via the platform-agnostic
|
|
8
|
+
[`../cpp/VroomJsiInstaller.cpp`](../cpp/VroomJsiInstaller.cpp) Android also
|
|
9
|
+
uses. There is no native view: the chart renders into an `SkPicture` that
|
|
10
|
+
`<Canvas>` from `@shopify/react-native-skia` paints.
|
|
5
11
|
|
|
6
|
-
|
|
7
|
-
|
|
12
|
+
This replaced a `[RCTBridge currentBridge]` → `RCTCxxBridge.runtime` lookup,
|
|
13
|
+
which stopped working in React Native 0.85. That release enables
|
|
14
|
+
`RCT_REMOVE_LEGACY_ARCH` by default, under which `RCTBridge` compiles down to
|
|
15
|
+
a stub whose `+currentBridge` returns `nil`.
|
|
16
|
+
|
|
17
|
+
[`../react-native-vroom-chart.podspec`](../react-native-vroom-chart.podspec)
|
|
18
|
+
builds `cpp/` + `ios/` plus the core sources mirrored in from
|
|
19
|
+
`packages/core/`.
|
package/ios/VroomChartModule.h
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
#import <Foundation/Foundation.h>
|
|
2
2
|
#import <React/RCTBridgeModule.h>
|
|
3
|
-
|
|
4
|
-
#ifdef RCT_NEW_ARCH_ENABLED
|
|
3
|
+
#import <ReactCommon/RCTTurboModuleWithJSIBindings.h>
|
|
5
4
|
#import <VroomChartSpec/VroomChartSpec.h>
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
@interface VroomChartModule : NSObject <RCTBridgeModule>
|
|
9
|
-
#endif
|
|
5
|
+
|
|
6
|
+
@interface VroomChartModule : NSObject <NativeVroomChartSpec, RCTTurboModuleWithJSIBindings>
|
|
10
7
|
|
|
11
8
|
@end
|
package/ios/VroomChartModule.mm
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
#import "VroomChartModule.h"
|
|
2
2
|
|
|
3
|
-
#import <React/RCTBridge+Private.h>
|
|
4
3
|
#import <ReactCommon/CallInvoker.h>
|
|
5
4
|
#import <jsi/jsi.h>
|
|
6
5
|
|
|
@@ -8,37 +7,32 @@
|
|
|
8
7
|
|
|
9
8
|
using namespace facebook;
|
|
10
9
|
|
|
11
|
-
@implementation VroomChartModule
|
|
10
|
+
@implementation VroomChartModule {
|
|
11
|
+
BOOL _didInstall;
|
|
12
|
+
}
|
|
12
13
|
|
|
13
14
|
RCT_EXPORT_MODULE(VroomChartModule)
|
|
14
15
|
|
|
15
|
-
//
|
|
16
|
-
//
|
|
16
|
+
// RCTTurboModuleManager calls this as soon as it instantiates the module —
|
|
17
|
+
// before the module object reaches JS — so global.VroomChartJSI always exists
|
|
18
|
+
// by the time anything imports NativeVroomChart.
|
|
17
19
|
//
|
|
18
|
-
//
|
|
20
|
+
// callInvoker is unused: every chart call is synchronous on the JS thread.
|
|
21
|
+
- (void)installJSIBindingsWithRuntime:(jsi::Runtime &)runtime
|
|
22
|
+
callInvoker:(const std::shared_ptr<react::CallInvoker> &)callInvoker
|
|
23
|
+
{
|
|
24
|
+
vroom::installJsi(runtime);
|
|
25
|
+
_didInstall = YES;
|
|
26
|
+
}
|
|
27
|
+
|
|
19
28
|
- (NSNumber *)install
|
|
20
29
|
{
|
|
21
|
-
|
|
22
|
-
RCTCxxBridge *cxxBridge = (RCTCxxBridge *)bridge;
|
|
23
|
-
if (cxxBridge == nil) {
|
|
24
|
-
return @NO;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
jsi::Runtime *runtime = (jsi::Runtime *)cxxBridge.runtime;
|
|
28
|
-
if (runtime == nullptr) {
|
|
29
|
-
return @NO;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
vroom::installJsi(*runtime);
|
|
33
|
-
return @YES;
|
|
30
|
+
return @(_didInstall);
|
|
34
31
|
}
|
|
35
32
|
|
|
36
|
-
|
|
37
|
-
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
|
|
38
|
-
(const facebook::react::ObjCTurboModule::InitParams &)params
|
|
33
|
+
- (std::shared_ptr<react::TurboModule>)getTurboModule:(const react::ObjCTurboModule::InitParams &)params
|
|
39
34
|
{
|
|
40
|
-
return std::make_shared<
|
|
35
|
+
return std::make_shared<react::NativeVroomChartSpecJSI>(params);
|
|
41
36
|
}
|
|
42
|
-
#endif
|
|
43
37
|
|
|
44
38
|
@end
|
package/lib/index.d.mts
CHANGED
|
@@ -106,6 +106,19 @@ type VroomTheme = {
|
|
|
106
106
|
crosshairTarget?: VroomColor;
|
|
107
107
|
/** Line-chart-mode close polyline color. Defaults to violet, matching the RSI line. */
|
|
108
108
|
lineColor?: VroomColor;
|
|
109
|
+
/**
|
|
110
|
+
* The line drawn across the plot while loading (see the `loading` prop).
|
|
111
|
+
* Defaults to inheriting `grid`: the gridlines are already the chart's tone
|
|
112
|
+
* for structure rather than data, which is what the line is, so matching
|
|
113
|
+
* them keeps it from being read as a series.
|
|
114
|
+
*
|
|
115
|
+
* The line breathes between roughly 60% and 100% of whatever color it ends
|
|
116
|
+
* up with — a pulse, not a dimmer, so a recessive color stays legible. Any
|
|
117
|
+
* alpha given here multiplies into that, so an opaque color is the usual
|
|
118
|
+
* choice. Pass `lineColor` to make the line read as the chart's own series
|
|
119
|
+
* warming up instead.
|
|
120
|
+
*/
|
|
121
|
+
skeleton?: VroomColor;
|
|
109
122
|
/** Line-chart-mode polyline stroke width in px. Defaults to 1.5. */
|
|
110
123
|
lineWidth?: number;
|
|
111
124
|
/**
|
|
@@ -1059,6 +1072,23 @@ type ATRConfig = {
|
|
|
1059
1072
|
type VroomChartCoreProps = {
|
|
1060
1073
|
/** OHLCV bars to render. The only required prop. */
|
|
1061
1074
|
candles: Candle[];
|
|
1075
|
+
/**
|
|
1076
|
+
* Whether the series is still being fetched. While this is true *and*
|
|
1077
|
+
* `candles` is empty, the chart draws a single grey line across the plot,
|
|
1078
|
+
* drifting in a slow sine wave, in place of the scene. Gestures, the
|
|
1079
|
+
* crosshair, the price badge, the axis labels and any indicator panes are all
|
|
1080
|
+
* suppressed for the duration.
|
|
1081
|
+
*
|
|
1082
|
+
* When the data arrives the line doesn't cut away — it becomes the chart, in
|
|
1083
|
+
* two steps that split `transitionMs`: it reshapes to pass through the
|
|
1084
|
+
* vertical centre of every candle about to be drawn, then fades out while
|
|
1085
|
+
* those candles grow outward from it and their color fades up.
|
|
1086
|
+
*
|
|
1087
|
+
* Passing `candles` alongside `loading` leaves the real chart up, so a
|
|
1088
|
+
* background refresh of an already-loaded series won't blank out. Default
|
|
1089
|
+
* false.
|
|
1090
|
+
*/
|
|
1091
|
+
loading?: boolean;
|
|
1062
1092
|
/**
|
|
1063
1093
|
* Identity of the data series (e.g. "BTC-USD"). When it changes between
|
|
1064
1094
|
* renders the chart resets to the default view (most recent candles + price
|
package/lib/index.d.ts
CHANGED
|
@@ -106,6 +106,19 @@ type VroomTheme = {
|
|
|
106
106
|
crosshairTarget?: VroomColor;
|
|
107
107
|
/** Line-chart-mode close polyline color. Defaults to violet, matching the RSI line. */
|
|
108
108
|
lineColor?: VroomColor;
|
|
109
|
+
/**
|
|
110
|
+
* The line drawn across the plot while loading (see the `loading` prop).
|
|
111
|
+
* Defaults to inheriting `grid`: the gridlines are already the chart's tone
|
|
112
|
+
* for structure rather than data, which is what the line is, so matching
|
|
113
|
+
* them keeps it from being read as a series.
|
|
114
|
+
*
|
|
115
|
+
* The line breathes between roughly 60% and 100% of whatever color it ends
|
|
116
|
+
* up with — a pulse, not a dimmer, so a recessive color stays legible. Any
|
|
117
|
+
* alpha given here multiplies into that, so an opaque color is the usual
|
|
118
|
+
* choice. Pass `lineColor` to make the line read as the chart's own series
|
|
119
|
+
* warming up instead.
|
|
120
|
+
*/
|
|
121
|
+
skeleton?: VroomColor;
|
|
109
122
|
/** Line-chart-mode polyline stroke width in px. Defaults to 1.5. */
|
|
110
123
|
lineWidth?: number;
|
|
111
124
|
/**
|
|
@@ -1059,6 +1072,23 @@ type ATRConfig = {
|
|
|
1059
1072
|
type VroomChartCoreProps = {
|
|
1060
1073
|
/** OHLCV bars to render. The only required prop. */
|
|
1061
1074
|
candles: Candle[];
|
|
1075
|
+
/**
|
|
1076
|
+
* Whether the series is still being fetched. While this is true *and*
|
|
1077
|
+
* `candles` is empty, the chart draws a single grey line across the plot,
|
|
1078
|
+
* drifting in a slow sine wave, in place of the scene. Gestures, the
|
|
1079
|
+
* crosshair, the price badge, the axis labels and any indicator panes are all
|
|
1080
|
+
* suppressed for the duration.
|
|
1081
|
+
*
|
|
1082
|
+
* When the data arrives the line doesn't cut away — it becomes the chart, in
|
|
1083
|
+
* two steps that split `transitionMs`: it reshapes to pass through the
|
|
1084
|
+
* vertical centre of every candle about to be drawn, then fades out while
|
|
1085
|
+
* those candles grow outward from it and their color fades up.
|
|
1086
|
+
*
|
|
1087
|
+
* Passing `candles` alongside `loading` leaves the real chart up, so a
|
|
1088
|
+
* background refresh of an already-loaded series won't blank out. Default
|
|
1089
|
+
* false.
|
|
1090
|
+
*/
|
|
1091
|
+
loading?: boolean;
|
|
1062
1092
|
/**
|
|
1063
1093
|
* Identity of the data series (e.g. "BTC-USD"). When it changes between
|
|
1064
1094
|
* renders the chart resets to the default view (most recent candles + price
|
package/lib/index.js
CHANGED
|
@@ -186,8 +186,10 @@ var COLOR_KEYS = {
|
|
|
186
186
|
// VROOM_COLOR_ACCENT_BULL
|
|
187
187
|
accentBear: 15,
|
|
188
188
|
// VROOM_COLOR_ACCENT_BEAR
|
|
189
|
-
lineColor: 16
|
|
189
|
+
lineColor: 16,
|
|
190
190
|
// VROOM_COLOR_LINE
|
|
191
|
+
skeleton: 17
|
|
192
|
+
// VROOM_COLOR_SKELETON
|
|
191
193
|
};
|
|
192
194
|
var FLOAT_KEYS = {
|
|
193
195
|
wickWidth: 1,
|
|
@@ -480,14 +482,15 @@ var EMPTY_FOOTPRINTS = footprintsToSpec({ prints: [] });
|
|
|
480
482
|
var installed = false;
|
|
481
483
|
function ensureInstalled() {
|
|
482
484
|
if (installed) return;
|
|
483
|
-
|
|
484
|
-
if (!ok) throw new Error("VroomChartModule.install() returned false");
|
|
485
|
+
NativeVroomChart_default.install();
|
|
485
486
|
if (typeof globalThis.VroomChartJSI === "undefined") {
|
|
486
|
-
throw new Error(
|
|
487
|
+
throw new Error(
|
|
488
|
+
"global.VroomChartJSI is undefined \u2014 VroomChartModule did not install its JSI bindings. This requires react-native >= 0.78 with the New Architecture enabled."
|
|
489
|
+
);
|
|
487
490
|
}
|
|
488
491
|
installed = true;
|
|
489
492
|
}
|
|
490
|
-
function useChartCore(candles, size, visibleRange, defaultCandleWidth, chartType, theme, rsi, macd, atr, movingAverages, vwap, bollingerBands, ichimoku, fairValueGaps, volume, priceLines, footprints, transition) {
|
|
493
|
+
function useChartCore(candles, size, visibleRange, defaultCandleWidth, chartType, theme, rsi, macd, atr, movingAverages, vwap, bollingerBands, ichimoku, fairValueGaps, volume, priceLines, footprints, transition, loading) {
|
|
491
494
|
const handleRef = (0, import_react.useRef)(null);
|
|
492
495
|
const defaultWidthAppliedRef = (0, import_react.useRef)(false);
|
|
493
496
|
const volumeCollapseRef = (0, import_react.useRef)(null);
|
|
@@ -522,18 +525,26 @@ function useChartCore(candles, size, visibleRange, defaultCandleWidth, chartType
|
|
|
522
525
|
const onFrameRef = (0, import_react.useRef)(transition?.onFrame);
|
|
523
526
|
onFrameRef.current = transition?.onFrame;
|
|
524
527
|
const seriesKey = transition?.seriesKey;
|
|
528
|
+
const loadingHandoffRef = (0, import_react.useRef)(false);
|
|
525
529
|
const endIntervalMorph = (0, import_react.useCallback)(() => {
|
|
526
530
|
if (intervalMorphRaf.current != null) {
|
|
527
531
|
cancelAnimationFrame(intervalMorphRaf.current);
|
|
528
532
|
intervalMorphRaf.current = null;
|
|
529
533
|
}
|
|
530
|
-
handleRef.current
|
|
534
|
+
const h = handleRef.current;
|
|
535
|
+
if (loadingHandoffRef.current) {
|
|
536
|
+
loadingHandoffRef.current = false;
|
|
537
|
+
h?.setLoadingMorph(1);
|
|
538
|
+
h?.beginLoadingReveal();
|
|
539
|
+
}
|
|
540
|
+
h?.setIntervalMorph(1);
|
|
531
541
|
}, []);
|
|
532
|
-
const startIntervalMorph = (0, import_react.useCallback)((h) => {
|
|
542
|
+
const startIntervalMorph = (0, import_react.useCallback)((h, durationMs) => {
|
|
533
543
|
const { ms, easing } = animRef.current;
|
|
544
|
+
const dur = durationMs ?? ms;
|
|
534
545
|
const start = performance.now();
|
|
535
546
|
const step = (now) => {
|
|
536
|
-
const p = Math.min(1, (now - start) /
|
|
547
|
+
const p = Math.min(1, (now - start) / dur);
|
|
537
548
|
h.setIntervalMorph(p < 1 ? ease(easing, p) : 1);
|
|
538
549
|
const pic = h.render();
|
|
539
550
|
if (pic) onFrameRef.current?.(pic);
|
|
@@ -541,6 +552,31 @@ function useChartCore(candles, size, visibleRange, defaultCandleWidth, chartType
|
|
|
541
552
|
};
|
|
542
553
|
intervalMorphRaf.current = requestAnimationFrame(step);
|
|
543
554
|
}, []);
|
|
555
|
+
const startLoadingHandoff = (0, import_react.useCallback)(
|
|
556
|
+
(h) => {
|
|
557
|
+
const { ms, easing } = animRef.current;
|
|
558
|
+
const half = ms / 2;
|
|
559
|
+
loadingHandoffRef.current = true;
|
|
560
|
+
h.beginLoadingMorph();
|
|
561
|
+
const start = performance.now();
|
|
562
|
+
const step = (now) => {
|
|
563
|
+
const p = Math.min(1, (now - start) / half);
|
|
564
|
+
h.setLoadingMorph(p < 1 ? ease(easing, p) : 1);
|
|
565
|
+
const pic = h.render();
|
|
566
|
+
if (pic) onFrameRef.current?.(pic);
|
|
567
|
+
if (p < 1) {
|
|
568
|
+
intervalMorphRaf.current = requestAnimationFrame(step);
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
intervalMorphRaf.current = null;
|
|
572
|
+
loadingHandoffRef.current = false;
|
|
573
|
+
h.beginLoadingReveal();
|
|
574
|
+
startIntervalMorph(h, half);
|
|
575
|
+
};
|
|
576
|
+
intervalMorphRaf.current = requestAnimationFrame(step);
|
|
577
|
+
},
|
|
578
|
+
[startIntervalMorph]
|
|
579
|
+
);
|
|
544
580
|
const settleStream = (0, import_react.useCallback)((keepMorph = false) => {
|
|
545
581
|
if (streamRaf.current != null) {
|
|
546
582
|
cancelAnimationFrame(streamRaf.current);
|
|
@@ -610,6 +646,8 @@ function useChartCore(candles, size, visibleRange, defaultCandleWidth, chartType
|
|
|
610
646
|
const explicit = visibleRange != null;
|
|
611
647
|
const startMs = visibleRange?.startMs ?? 0;
|
|
612
648
|
const endMs = visibleRange?.endMs ?? 0;
|
|
649
|
+
const showLoadingLine = loading === true && candles.length === 0;
|
|
650
|
+
const lineUpRef = (0, import_react.useRef)(false);
|
|
613
651
|
const themeKey = theme ? JSON.stringify(theme) : "";
|
|
614
652
|
const rsiKey = rsi ? JSON.stringify(rsi) : "";
|
|
615
653
|
const macdKey = macd ? JSON.stringify(macd) : "";
|
|
@@ -632,6 +670,19 @@ function useChartCore(candles, size, visibleRange, defaultCandleWidth, chartType
|
|
|
632
670
|
defaultWidthAppliedRef.current = true;
|
|
633
671
|
}
|
|
634
672
|
let morphing = false;
|
|
673
|
+
if (showLoadingLine) {
|
|
674
|
+
if (!lineUpRef.current) {
|
|
675
|
+
endIntervalMorph();
|
|
676
|
+
settleStream();
|
|
677
|
+
h.setCandles(packCandles([]));
|
|
678
|
+
prevDataRef.current = null;
|
|
679
|
+
}
|
|
680
|
+
h.setLoading(true, !animRef.current.reduceMotion);
|
|
681
|
+
lineUpRef.current = true;
|
|
682
|
+
} else if (lineUpRef.current && candles.length === 0) {
|
|
683
|
+
h.setLoading(false, true);
|
|
684
|
+
lineUpRef.current = false;
|
|
685
|
+
}
|
|
635
686
|
if (candles.length > 0) {
|
|
636
687
|
const prev = prevDataRef.current;
|
|
637
688
|
const freshHandle = prev == null || prev.handle !== h;
|
|
@@ -639,6 +690,7 @@ function useChartCore(candles, size, visibleRange, defaultCandleWidth, chartType
|
|
|
639
690
|
const transitionKind = freshHandle ? "initial" : explicit ? "stream" : classifyTransition(prev.candles, candles, seriesKey !== prev.seriesKey);
|
|
640
691
|
let tfArgs = null;
|
|
641
692
|
let prevEnvelope = null;
|
|
693
|
+
let handOff = false;
|
|
642
694
|
let stream = null;
|
|
643
695
|
if (transitionKind === "stream" && prev != null && !explicit) {
|
|
644
696
|
const { stream: mode, streamMs, reduceMotion } = animRef.current;
|
|
@@ -688,6 +740,11 @@ function useChartCore(candles, size, visibleRange, defaultCandleWidth, chartType
|
|
|
688
740
|
} else if (transitionKind === "initial" || transitionKind === "reset") {
|
|
689
741
|
endIntervalMorph();
|
|
690
742
|
}
|
|
743
|
+
if (lineUpRef.current) {
|
|
744
|
+
lineUpRef.current = false;
|
|
745
|
+
handOff = animRef.current.ms > 0 && !animRef.current.reduceMotion && onFrameRef.current != null;
|
|
746
|
+
if (!handOff) h.setLoading(false, true);
|
|
747
|
+
}
|
|
691
748
|
h.setCandles(packCandles(candles));
|
|
692
749
|
if (transitionKind === "timeframe") {
|
|
693
750
|
const newStepMs = inferStepMs(candles);
|
|
@@ -709,6 +766,7 @@ function useChartCore(candles, size, visibleRange, defaultCandleWidth, chartType
|
|
|
709
766
|
} else if (transitionKind === "reset") {
|
|
710
767
|
h.resetView();
|
|
711
768
|
}
|
|
769
|
+
if (handOff) startLoadingHandoff(h);
|
|
712
770
|
prevDataRef.current = { handle: h, candles, seriesKey };
|
|
713
771
|
}
|
|
714
772
|
}
|
|
@@ -738,7 +796,7 @@ function useChartCore(candles, size, visibleRange, defaultCandleWidth, chartType
|
|
|
738
796
|
footprints?.prints.length ? footprintsToSpec(footprints) : EMPTY_FOOTPRINTS
|
|
739
797
|
);
|
|
740
798
|
if (!morphing) setPicture(h.render());
|
|
741
|
-
}, [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]);
|
|
799
|
+
}, [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]);
|
|
742
800
|
return { handle: handleRef.current, picture, volumeCollapseRef };
|
|
743
801
|
}
|
|
744
802
|
|
|
@@ -750,6 +808,7 @@ function isSkImage(frame) {
|
|
|
750
808
|
function VroomChart(props) {
|
|
751
809
|
const {
|
|
752
810
|
candles,
|
|
811
|
+
loading,
|
|
753
812
|
seriesKey,
|
|
754
813
|
width: widthProp,
|
|
755
814
|
height: heightProp,
|
|
@@ -867,8 +926,10 @@ function VroomChart(props) {
|
|
|
867
926
|
streamTransitionMs,
|
|
868
927
|
reduceMotion,
|
|
869
928
|
onFrame
|
|
870
|
-
}
|
|
929
|
+
},
|
|
930
|
+
loading
|
|
871
931
|
);
|
|
932
|
+
const showLoadingLine = loading === true && candles.length === 0;
|
|
872
933
|
const crosshairActive = (0, import_react2.useRef)(false);
|
|
873
934
|
const footprintActive = (0, import_react2.useRef)(false);
|
|
874
935
|
const lastCrosshairTime = (0, import_react2.useRef)(null);
|
|
@@ -1148,7 +1209,7 @@ function VroomChart(props) {
|
|
|
1148
1209
|
pane: null
|
|
1149
1210
|
});
|
|
1150
1211
|
};
|
|
1151
|
-
const pan = import_react_native_gesture_handler.Gesture.Pan().runOnJS(true).maxPointers(1).onStart((e) => {
|
|
1212
|
+
const pan = import_react_native_gesture_handler.Gesture.Pan().enabled(!showLoadingLine).runOnJS(true).maxPointers(1).onStart((e) => {
|
|
1152
1213
|
cancelDecay();
|
|
1153
1214
|
panMode.current = hitAxis(e.x, e.y);
|
|
1154
1215
|
priceDrag.current = null;
|
|
@@ -1243,7 +1304,7 @@ function VroomChart(props) {
|
|
|
1243
1304
|
enableX: false,
|
|
1244
1305
|
enableY: false
|
|
1245
1306
|
});
|
|
1246
|
-
const pinch = import_react_native_gesture_handler.Gesture.Pinch().runOnJS(true).onTouchesDown((e) => {
|
|
1307
|
+
const pinch = import_react_native_gesture_handler.Gesture.Pinch().enabled(!showLoadingLine).runOnJS(true).onTouchesDown((e) => {
|
|
1247
1308
|
if (e.numberOfTouches < 2) return;
|
|
1248
1309
|
dismissFootprint();
|
|
1249
1310
|
const [a, b] = e.allTouches;
|
|
@@ -1281,7 +1342,7 @@ function VroomChart(props) {
|
|
|
1281
1342
|
if (next) applyFrame(next);
|
|
1282
1343
|
maybeStartAnim();
|
|
1283
1344
|
});
|
|
1284
|
-
const longPress = import_react_native_gesture_handler.Gesture.LongPress().runOnJS(true).onStart((e) => {
|
|
1345
|
+
const longPress = import_react_native_gesture_handler.Gesture.LongPress().enabled(!showLoadingLine).runOnJS(true).onStart((e) => {
|
|
1285
1346
|
if (!handle) return;
|
|
1286
1347
|
if (hitAxis(e.x, e.y) !== "chart") return;
|
|
1287
1348
|
if (hitPriceLine(e.x, e.y)) return;
|
|
@@ -1301,7 +1362,7 @@ function VroomChart(props) {
|
|
|
1301
1362
|
reason: "show"
|
|
1302
1363
|
});
|
|
1303
1364
|
});
|
|
1304
|
-
const tap = import_react_native_gesture_handler.Gesture.Tap().runOnJS(true).onStart((e) => {
|
|
1365
|
+
const tap = import_react_native_gesture_handler.Gesture.Tap().enabled(!showLoadingLine).runOnJS(true).onStart((e) => {
|
|
1305
1366
|
if (!handle) return;
|
|
1306
1367
|
const prints = footprints ?? [];
|
|
1307
1368
|
const fp = prints.length ? handle.hitTestFootprint(e.x, e.y) : null;
|