react-native-vroom-chart 0.6.0 → 0.7.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/cpp/VroomChartHostObject.cpp +150 -32
- package/cpp/_core_include/vroom/vroom_chart.h +180 -22
- package/cpp/_core_src/bollinger.h +1 -1
- package/cpp/_core_src/candles.cpp +138 -41
- package/cpp/_core_src/candles.h +11 -1
- package/cpp/_core_src/chart.cpp +91 -40
- package/cpp/_core_src/chart.h +55 -20
- package/cpp/_core_src/chart_facade.cpp +206 -66
- package/cpp/_core_src/gradient.cpp +46 -0
- package/cpp/_core_src/gradient.h +31 -0
- package/cpp/_core_src/labels.cpp +49 -16
- package/cpp/_core_src/labels.h +33 -4
- package/cpp/_core_src/liquidity.cpp +3 -37
- package/cpp/_core_src/ma_overlay.cpp +174 -12
- package/cpp/_core_src/ma_overlay.h +55 -3
- package/cpp/_core_src/macd.cpp +13 -42
- package/cpp/_core_src/macd.h +11 -8
- package/cpp/_core_src/macd_pane.cpp +57 -27
- package/cpp/_core_src/price_line_layout.h +1 -1
- package/cpp/_core_src/rsi.cpp +4 -18
- package/cpp/_core_src/rsi.h +6 -6
- package/cpp/_core_src/rsi_pane.cpp +34 -19
- package/cpp/_core_src/series_ma.cpp +64 -0
- package/cpp/_core_src/series_ma.h +30 -0
- package/cpp/_core_src/style_inherit.h +35 -0
- package/cpp/_core_src/theme.cpp +2 -1
- package/cpp/_core_src/viewport.cpp +36 -12
- package/cpp/_core_src/viewport.h +50 -0
- package/cpp/_core_src/volume.cpp +33 -8
- package/cpp/_core_src/volume.h +12 -1
- package/cpp/_core_src/volume_anim.cpp +32 -0
- package/cpp/_core_src/volume_anim.h +41 -0
- package/lib/index.d.mts +161 -28
- package/lib/index.d.ts +161 -28
- package/lib/index.js +156 -31
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +156 -31
- package/lib/index.mjs.map +1 -1
- package/package.json +3 -3
- package/src/VroomChart.tsx +69 -3
- package/src/easing.ts +40 -0
- package/src/index.ts +3 -0
- package/src/jsi.d.ts +76 -20
- package/src/theme.ts +1 -0
- package/src/types.ts +3 -0
- package/src/useChartCore.ts +103 -27
package/src/useChartCore.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useEffect, useRef, useState } from 'react';
|
|
2
|
+
import type { MutableRefObject } from 'react';
|
|
2
3
|
import type { SkPicture } from '@shopify/react-native-skia';
|
|
3
4
|
|
|
4
5
|
import NativeVroomChart from './NativeVroomChart';
|
|
@@ -15,6 +16,7 @@ import type {
|
|
|
15
16
|
PriceLinesStyle,
|
|
16
17
|
RSIConfig,
|
|
17
18
|
VisibleRange,
|
|
19
|
+
VolumeConfig,
|
|
18
20
|
VroomTheme,
|
|
19
21
|
VWAPConfig,
|
|
20
22
|
} from './types';
|
|
@@ -30,17 +32,49 @@ const MA_SOURCES = [
|
|
|
30
32
|
'ohlc4',
|
|
31
33
|
] as const;
|
|
32
34
|
|
|
35
|
+
// An unset style color marshals as the core's transparent inherit sentinel.
|
|
36
|
+
const inheritColor = (v: string | number | undefined): number =>
|
|
37
|
+
(v != null ? parseColor(v) : null) ?? 0;
|
|
38
|
+
|
|
33
39
|
function overlayToNumeric(o: MovingAverageOverlay) {
|
|
34
40
|
const srcIdx = o.source ? MA_SOURCES.indexOf(o.source) : 0;
|
|
35
41
|
return {
|
|
36
|
-
kind: o.
|
|
37
|
-
period: o.
|
|
42
|
+
kind: o.maType === 'ema' ? 1 : 0,
|
|
43
|
+
period: o.period,
|
|
38
44
|
source: srcIdx < 0 ? 0 : srcIdx,
|
|
39
45
|
color: (o.color != null ? parseColor(o.color) : null) ?? 0xff2962ff,
|
|
40
46
|
width: o.width ?? 1.5,
|
|
41
47
|
};
|
|
42
48
|
}
|
|
43
49
|
|
|
50
|
+
function rsiToSpec(cfg: RSIConfig | undefined) {
|
|
51
|
+
return {
|
|
52
|
+
enabled: cfg?.enabled ?? false,
|
|
53
|
+
period: cfg?.period ?? 14,
|
|
54
|
+
upperBand: cfg?.upperBand ?? 70,
|
|
55
|
+
lowerBand: cfg?.lowerBand ?? 30,
|
|
56
|
+
maPeriod: cfg?.maPeriod ?? 14,
|
|
57
|
+
maKind: cfg?.maType === 'ema' ? 1 : 0,
|
|
58
|
+
maVisible: cfg?.maVisible ?? true,
|
|
59
|
+
lineColor: inheritColor(cfg?.lineColor),
|
|
60
|
+
lineWidth: cfg?.lineWidth ?? -1,
|
|
61
|
+
lineVisible: cfg?.lineVisible ?? true,
|
|
62
|
+
maColor: inheritColor(cfg?.maColor),
|
|
63
|
+
maWidth: cfg?.maWidth ?? -1,
|
|
64
|
+
bandColor: inheritColor(cfg?.bandColor),
|
|
65
|
+
bandsVisible: cfg?.bandsVisible ?? true,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function vwapToSpec(cfg: VWAPConfig | undefined) {
|
|
70
|
+
return {
|
|
71
|
+
enabled: cfg?.enabled ?? false,
|
|
72
|
+
resetOffsetMin: cfg?.resetMinutes ?? 0,
|
|
73
|
+
color: inheritColor(cfg?.color),
|
|
74
|
+
width: cfg?.width ?? -1,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
44
78
|
// Bollinger defaults: blue bands / orange basis, matching the repo palette.
|
|
45
79
|
const DEFAULT_BB_BAND_COLOR = 0xff2962ff;
|
|
46
80
|
const DEFAULT_BB_BASIS_COLOR = 0xffff6d00;
|
|
@@ -52,7 +86,7 @@ function bollingerToSpec(cfg: BollingerBandsConfig | undefined) {
|
|
|
52
86
|
period: cfg?.period ?? 20,
|
|
53
87
|
mult: cfg?.stdDev ?? 2,
|
|
54
88
|
source: srcIdx < 0 ? 0 : srcIdx,
|
|
55
|
-
basisKind: cfg?.
|
|
89
|
+
basisKind: cfg?.maType === 'ema' ? 1 : 0,
|
|
56
90
|
upperColor:
|
|
57
91
|
(cfg?.upperColor != null ? parseColor(cfg.upperColor) : null) ??
|
|
58
92
|
DEFAULT_BB_BAND_COLOR,
|
|
@@ -65,11 +99,51 @@ function bollingerToSpec(cfg: BollingerBandsConfig | undefined) {
|
|
|
65
99
|
(cfg?.lowerColor != null ? parseColor(cfg.lowerColor) : null) ??
|
|
66
100
|
DEFAULT_BB_BAND_COLOR,
|
|
67
101
|
lowerWidth: cfg?.lowerWidth ?? 1,
|
|
68
|
-
fillEnabled: cfg?.
|
|
102
|
+
fillEnabled: cfg?.fillVisible ?? true,
|
|
69
103
|
fillOpacity: cfg?.fillOpacity ?? 0.1,
|
|
70
104
|
};
|
|
71
105
|
}
|
|
72
106
|
|
|
107
|
+
function macdToSpec(cfg: MACDConfig | undefined) {
|
|
108
|
+
const srcIdx = cfg?.source ? MA_SOURCES.indexOf(cfg.source) : 0;
|
|
109
|
+
return {
|
|
110
|
+
enabled: cfg?.enabled ?? false,
|
|
111
|
+
fast: cfg?.fast ?? 12,
|
|
112
|
+
slow: cfg?.slow ?? 26,
|
|
113
|
+
signal: cfg?.signal ?? 9,
|
|
114
|
+
source: srcIdx < 0 ? 0 : srcIdx,
|
|
115
|
+
maKind: cfg?.maType === 'sma' ? 0 : 1,
|
|
116
|
+
signalMaKind: cfg?.signalMaType === 'sma' ? 0 : 1,
|
|
117
|
+
lineColor: inheritColor(cfg?.lineColor),
|
|
118
|
+
lineWidth: cfg?.lineWidth ?? -1,
|
|
119
|
+
lineVisible: cfg?.lineVisible ?? true,
|
|
120
|
+
signalColor: inheritColor(cfg?.signalColor),
|
|
121
|
+
signalWidth: cfg?.signalWidth ?? -1,
|
|
122
|
+
signalVisible: cfg?.signalVisible ?? true,
|
|
123
|
+
histVisible: cfg?.histogramVisible ?? true,
|
|
124
|
+
histUpColor: inheritColor(cfg?.histogramUpColor),
|
|
125
|
+
histUpFadingColor: inheritColor(cfg?.histogramUpFadingColor),
|
|
126
|
+
histDownColor: inheritColor(cfg?.histogramDownColor),
|
|
127
|
+
histDownFadingColor: inheritColor(cfg?.histogramDownFadingColor),
|
|
128
|
+
zeroColor: inheritColor(cfg?.zeroLineColor),
|
|
129
|
+
zeroVisible: cfg?.zeroLineVisible ?? true,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Unset style fields go down as the core's inherit sentinels (negative float,
|
|
134
|
+
// transparent color) rather than as literal defaults, so the theme keys stay in
|
|
135
|
+
// charge of anything the consumer didn't set.
|
|
136
|
+
function volumeToSpec(cfg: VolumeConfig | undefined) {
|
|
137
|
+
return {
|
|
138
|
+
enabled: cfg?.enabled ?? true,
|
|
139
|
+
heightFrac: cfg?.height ?? -1,
|
|
140
|
+
opacity: cfg?.opacity ?? -1,
|
|
141
|
+
radiusPx: cfg?.radius ?? -1,
|
|
142
|
+
upColor: (cfg?.upColor != null ? parseColor(cfg.upColor) : null) ?? 0,
|
|
143
|
+
downColor: (cfg?.downColor != null ? parseColor(cfg.downColor) : null) ?? 0,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
73
147
|
// Price-line defaults: a soft red dotted rule with a dark translucent label,
|
|
74
148
|
// close in weight to the current-price indicator it sits beside.
|
|
75
149
|
const DEFAULT_PRICE_LINE_COLOR = 0xffef5350;
|
|
@@ -136,10 +210,19 @@ function ensureInstalled(): void {
|
|
|
136
210
|
installed = true;
|
|
137
211
|
}
|
|
138
212
|
|
|
213
|
+
/** Progress + curve of the staggered volume-bar collapse. See setVolumeCollapse. */
|
|
214
|
+
export type VolumeCollapse = { t: number; easing: number };
|
|
215
|
+
|
|
139
216
|
export type ChartCoreState = {
|
|
140
217
|
handle: ChartHandle | null;
|
|
141
218
|
/** Picture freshly rendered after the latest data/size/range push. */
|
|
142
219
|
picture: SkPicture | null;
|
|
220
|
+
/**
|
|
221
|
+
* The last volume collapse handed to the core, or null before the first push.
|
|
222
|
+
* VroomChart's animation loop owns this — it lives here only so the data effect
|
|
223
|
+
* can restore it, since setVolume snaps the scalar (see below).
|
|
224
|
+
*/
|
|
225
|
+
volumeCollapseRef: MutableRefObject<VolumeCollapse | null>;
|
|
143
226
|
};
|
|
144
227
|
|
|
145
228
|
// Owns a ChartHandle and produces an "initial" picture whenever data, size,
|
|
@@ -158,6 +241,7 @@ export function useChartCore(
|
|
|
158
241
|
movingAverages?: MovingAverageOverlay[],
|
|
159
242
|
vwap?: VWAPConfig,
|
|
160
243
|
bollingerBands?: BollingerBandsConfig,
|
|
244
|
+
volume?: VolumeConfig,
|
|
161
245
|
priceLines?: PriceLinesProp,
|
|
162
246
|
): ChartCoreState {
|
|
163
247
|
const handleRef = useRef<ChartHandle | null>(null);
|
|
@@ -165,6 +249,7 @@ export function useChartCore(
|
|
|
165
249
|
// every data change, and the core setter re-frames when candles are present,
|
|
166
250
|
// so re-pushing would snap the view away from the user's pan/zoom.
|
|
167
251
|
const defaultWidthAppliedRef = useRef(false);
|
|
252
|
+
const volumeCollapseRef = useRef<VolumeCollapse | null>(null);
|
|
168
253
|
const [picture, setPicture] = useState<SkPicture | null>(null);
|
|
169
254
|
|
|
170
255
|
if (!handleRef.current && size.width > 0 && size.height > 0) {
|
|
@@ -188,6 +273,7 @@ export function useChartCore(
|
|
|
188
273
|
const maKey = movingAverages ? JSON.stringify(movingAverages) : '';
|
|
189
274
|
const vwapKey = vwap ? JSON.stringify(vwap) : '';
|
|
190
275
|
const bollingerKey = bollingerBands ? JSON.stringify(bollingerBands) : '';
|
|
276
|
+
const volumeKey = volume ? JSON.stringify(volume) : '';
|
|
191
277
|
const priceLinesKey = priceLines ? JSON.stringify(priceLines) : '';
|
|
192
278
|
|
|
193
279
|
useEffect(() => {
|
|
@@ -217,38 +303,28 @@ export function useChartCore(
|
|
|
217
303
|
if (theme) {
|
|
218
304
|
applyTheme(h, theme);
|
|
219
305
|
}
|
|
220
|
-
h.setRSI(
|
|
221
|
-
|
|
222
|
-
rsi?.period ?? 14,
|
|
223
|
-
rsi?.upperBand ?? 70,
|
|
224
|
-
rsi?.lowerBand ?? 30,
|
|
225
|
-
rsi?.maEnabled ?? true,
|
|
226
|
-
rsi?.maPeriod ?? 14,
|
|
227
|
-
);
|
|
228
|
-
h.setMACD(
|
|
229
|
-
macd?.enabled ?? false,
|
|
230
|
-
macd?.fast ?? 12,
|
|
231
|
-
macd?.slow ?? 26,
|
|
232
|
-
macd?.signal ?? 9,
|
|
233
|
-
);
|
|
306
|
+
h.setRSI(rsiToSpec(rsi));
|
|
307
|
+
h.setMACD(macdToSpec(macd));
|
|
234
308
|
h.setOverlays((movingAverages ?? []).map(overlayToNumeric));
|
|
235
|
-
h.setVWAP(
|
|
236
|
-
vwap?.enabled ?? false,
|
|
237
|
-
vwap?.resetMinutes ?? 0,
|
|
238
|
-
(vwap?.color != null ? parseColor(vwap.color) : null) ?? 0xff00bcd4,
|
|
239
|
-
vwap?.width ?? 1.5,
|
|
240
|
-
);
|
|
309
|
+
h.setVWAP(vwapToSpec(vwap));
|
|
241
310
|
h.setBollinger(bollingerToSpec(bollingerBands));
|
|
311
|
+
h.setVolume(volumeToSpec(volume));
|
|
312
|
+
// setVolume snaps the collapse scalar to its `enabled`, which would cut a
|
|
313
|
+
// toggle animation short whenever this effect re-runs (a streaming candle, a
|
|
314
|
+
// resize). Hand the in-flight value back; VroomChart's loop drives it from
|
|
315
|
+
// there.
|
|
316
|
+
const collapse = volumeCollapseRef.current;
|
|
317
|
+
if (collapse) h.setVolumeCollapse(collapse.t, collapse.easing);
|
|
242
318
|
h.setPriceLines(
|
|
243
319
|
priceLines?.lines.length ? priceLinesToSpec(priceLines) : EMPTY_PRICE_LINES,
|
|
244
320
|
);
|
|
245
321
|
// TODO(rn-parity): mirror the web `liquidity` overlay here (setLiquidity +
|
|
246
322
|
// the VroomBand structs in the JSI handle) — web-only for now.
|
|
247
323
|
setPicture(h.render());
|
|
248
|
-
// theme/rsi/macd/movingAverages/vwap/bollingerBands/priceLines are
|
|
324
|
+
// theme/rsi/macd/movingAverages/vwap/bollingerBands/volume/priceLines are
|
|
249
325
|
// represented by their *Key deps.
|
|
250
326
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
251
|
-
}, [candles, size.width, size.height, size.pxRatio, explicit, startMs, endMs, defaultCandleWidth, themeKey, rsiKey, macdKey, maKey, vwapKey, bollingerKey, priceLinesKey]);
|
|
327
|
+
}, [candles, size.width, size.height, size.pxRatio, explicit, startMs, endMs, defaultCandleWidth, themeKey, rsiKey, macdKey, maKey, vwapKey, bollingerKey, volumeKey, priceLinesKey]);
|
|
252
328
|
|
|
253
|
-
return { handle: handleRef.current, picture };
|
|
329
|
+
return { handle: handleRef.current, picture, volumeCollapseRef };
|
|
254
330
|
}
|