react-native-vroom-chart 0.6.0 → 0.8.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 +281 -33
- package/cpp/_core_include/vroom/vroom_chart.h +231 -36
- 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 +69 -29
- package/cpp/_core_src/chart_facade.cpp +247 -69
- package/cpp/_core_src/drawings.cpp +147 -4
- package/cpp/_core_src/drawings.h +10 -5
- 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 +206 -31
- package/lib/index.d.ts +206 -31
- package/lib/index.js +324 -44
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +329 -52
- package/lib/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/VroomChart.tsx +100 -17
- package/src/dataTransitions.ts +148 -0
- package/src/easing.ts +40 -0
- package/src/index.ts +9 -0
- package/src/jsi.d.ts +126 -19
- package/src/theme.ts +1 -0
- package/src/types.ts +3 -0
- package/src/useChartCore.ts +273 -30
package/lib/index.js
CHANGED
|
@@ -30,7 +30,10 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
-
VroomChart: () => VroomChart
|
|
33
|
+
VroomChart: () => VroomChart,
|
|
34
|
+
classifyTransition: () => classifyTransition,
|
|
35
|
+
inferStepMs: () => inferStepMs,
|
|
36
|
+
timeframeWindow: () => timeframeWindow
|
|
34
37
|
});
|
|
35
38
|
module.exports = __toCommonJS(index_exports);
|
|
36
39
|
|
|
@@ -48,6 +51,86 @@ var import_react = require("react");
|
|
|
48
51
|
var import_react_native = require("react-native");
|
|
49
52
|
var NativeVroomChart_default = import_react_native.TurboModuleRegistry.getEnforcing("VroomChartModule");
|
|
50
53
|
|
|
54
|
+
// src/dataTransitions.ts
|
|
55
|
+
var STEP_TOLERANCE = 0.01;
|
|
56
|
+
var MAX_SAME_ASSET_CLOSE_RATIO = 1.25;
|
|
57
|
+
var MAX_END_DRIFT_STEPS = 3;
|
|
58
|
+
var MAX_STREAM_ADVANCE_STEPS = 5;
|
|
59
|
+
function inferStepMs(candles) {
|
|
60
|
+
if (candles.length < 2) return null;
|
|
61
|
+
const k = Math.min(candles.length - 1, 8);
|
|
62
|
+
const diffs = [];
|
|
63
|
+
for (let i = 0; i < k; i++) diffs.push(candles[i + 1].timeMs - candles[i].timeMs);
|
|
64
|
+
diffs.sort((a, b) => a - b);
|
|
65
|
+
const median = diffs[Math.floor(diffs.length / 2)];
|
|
66
|
+
return median > 0 ? median : null;
|
|
67
|
+
}
|
|
68
|
+
function indexByTime(candles, t) {
|
|
69
|
+
let lo = 0;
|
|
70
|
+
let hi = candles.length - 1;
|
|
71
|
+
while (lo <= hi) {
|
|
72
|
+
const mid = lo + hi >>> 1;
|
|
73
|
+
const v = candles[mid].timeMs;
|
|
74
|
+
if (v === t) return mid;
|
|
75
|
+
if (v < t) lo = mid + 1;
|
|
76
|
+
else hi = mid - 1;
|
|
77
|
+
}
|
|
78
|
+
return -1;
|
|
79
|
+
}
|
|
80
|
+
function classifyTransition(prev, next, seriesKeyChanged) {
|
|
81
|
+
if (!prev || prev.length === 0) return "initial";
|
|
82
|
+
if (next.length === 0) return "stream";
|
|
83
|
+
if (seriesKeyChanged) return "reset";
|
|
84
|
+
const prevStep = inferStepMs(prev);
|
|
85
|
+
const nextStep = inferStepMs(next);
|
|
86
|
+
if (prevStep == null || nextStep == null) return "reset";
|
|
87
|
+
const prevLast = prev[prev.length - 1];
|
|
88
|
+
const nextLast = next[next.length - 1];
|
|
89
|
+
if (Math.abs(nextStep - prevStep) <= prevStep * STEP_TOLERANCE) {
|
|
90
|
+
const idx = indexByTime(next, prevLast.timeMs);
|
|
91
|
+
const aligned = idx >= 0;
|
|
92
|
+
const sharedBarRatio = aligned && next[idx].close > 0 && prevLast.close > 0 ? Math.max(next[idx].close / prevLast.close, prevLast.close / next[idx].close) : Infinity;
|
|
93
|
+
const advanced = nextLast.timeMs >= prevLast.timeMs && nextLast.timeMs - prevLast.timeMs <= MAX_STREAM_ADVANCE_STEPS * nextStep;
|
|
94
|
+
return sharedBarRatio <= MAX_SAME_ASSET_CLOSE_RATIO && advanced ? "stream" : "reset";
|
|
95
|
+
}
|
|
96
|
+
const closeRatio = prevLast.close > 0 && nextLast.close > 0 ? Math.max(nextLast.close / prevLast.close, prevLast.close / nextLast.close) : Infinity;
|
|
97
|
+
const prevEnd = prevLast.timeMs + prevStep;
|
|
98
|
+
const nextEnd = nextLast.timeMs + nextStep;
|
|
99
|
+
const endsTogether = Math.abs(nextEnd - prevEnd) <= MAX_END_DRIFT_STEPS * Math.max(prevStep, nextStep);
|
|
100
|
+
return closeRatio <= MAX_SAME_ASSET_CLOSE_RATIO && endsTogether ? "timeframe" : "reset";
|
|
101
|
+
}
|
|
102
|
+
function timeframeWindow(oldWindow, oldStepMs, oldLastMs, newStepMs, newLastMs) {
|
|
103
|
+
const slots = (oldWindow.endMs - oldWindow.startMs) / oldStepMs;
|
|
104
|
+
const offsetRaw = (oldWindow.endMs - oldLastMs) / oldStepMs;
|
|
105
|
+
const offsetSlots = Math.min(Math.max(offsetRaw, 0), slots * 0.75);
|
|
106
|
+
const endMs = Math.round(newLastMs + offsetSlots * newStepMs);
|
|
107
|
+
return { startMs: Math.round(endMs - slots * newStepMs), endMs };
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// src/easing.ts
|
|
111
|
+
function ease(kind, p) {
|
|
112
|
+
switch (kind) {
|
|
113
|
+
case "linear":
|
|
114
|
+
return p;
|
|
115
|
+
case "ease-in":
|
|
116
|
+
return p * p;
|
|
117
|
+
case "ease-out":
|
|
118
|
+
return p * (2 - p);
|
|
119
|
+
default:
|
|
120
|
+
return p * p * (3 - 2 * p);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
var EASINGS = [
|
|
124
|
+
"linear",
|
|
125
|
+
"ease-in",
|
|
126
|
+
"ease-out",
|
|
127
|
+
"ease-in-out"
|
|
128
|
+
];
|
|
129
|
+
function easingIndex(kind) {
|
|
130
|
+
const i = kind ? EASINGS.indexOf(kind) : -1;
|
|
131
|
+
return i < 0 ? EASINGS.indexOf("ease-in-out") : i;
|
|
132
|
+
}
|
|
133
|
+
|
|
51
134
|
// src/packCandles.ts
|
|
52
135
|
var BYTES_PER_CANDLE = 48;
|
|
53
136
|
function packCandles(candles) {
|
|
@@ -104,8 +187,10 @@ var FLOAT_KEYS = {
|
|
|
104
187
|
// VROOM_FLOAT_CANDLE_RADIUS_PX
|
|
105
188
|
volumeRadius: 10,
|
|
106
189
|
// VROOM_FLOAT_VOLUME_RADIUS_PX
|
|
107
|
-
lineWidth: 11
|
|
190
|
+
lineWidth: 11,
|
|
108
191
|
// VROOM_FLOAT_LINE_WIDTH_PX
|
|
192
|
+
lineGradientOpacity: 12
|
|
193
|
+
// VROOM_FLOAT_LINE_GRADIENT_OPACITY
|
|
109
194
|
};
|
|
110
195
|
var BOOL_KEYS = {
|
|
111
196
|
wickRoundCap: 9
|
|
@@ -151,16 +236,43 @@ var MA_SOURCES = [
|
|
|
151
236
|
"hlc3",
|
|
152
237
|
"ohlc4"
|
|
153
238
|
];
|
|
239
|
+
var inheritColor = (v) => (v != null ? parseColor(v) : null) ?? 0;
|
|
154
240
|
function overlayToNumeric(o) {
|
|
155
241
|
const srcIdx = o.source ? MA_SOURCES.indexOf(o.source) : 0;
|
|
156
242
|
return {
|
|
157
|
-
kind: o.
|
|
158
|
-
period: o.
|
|
243
|
+
kind: o.maType === "ema" ? 1 : 0,
|
|
244
|
+
period: o.period,
|
|
159
245
|
source: srcIdx < 0 ? 0 : srcIdx,
|
|
160
246
|
color: (o.color != null ? parseColor(o.color) : null) ?? 4280902399,
|
|
161
247
|
width: o.width ?? 1.5
|
|
162
248
|
};
|
|
163
249
|
}
|
|
250
|
+
function rsiToSpec(cfg) {
|
|
251
|
+
return {
|
|
252
|
+
enabled: cfg?.enabled ?? false,
|
|
253
|
+
period: cfg?.period ?? 14,
|
|
254
|
+
upperBand: cfg?.upperBand ?? 70,
|
|
255
|
+
lowerBand: cfg?.lowerBand ?? 30,
|
|
256
|
+
maPeriod: cfg?.maPeriod ?? 14,
|
|
257
|
+
maKind: cfg?.maType === "ema" ? 1 : 0,
|
|
258
|
+
maVisible: cfg?.maVisible ?? true,
|
|
259
|
+
lineColor: inheritColor(cfg?.lineColor),
|
|
260
|
+
lineWidth: cfg?.lineWidth ?? -1,
|
|
261
|
+
lineVisible: cfg?.lineVisible ?? true,
|
|
262
|
+
maColor: inheritColor(cfg?.maColor),
|
|
263
|
+
maWidth: cfg?.maWidth ?? -1,
|
|
264
|
+
bandColor: inheritColor(cfg?.bandColor),
|
|
265
|
+
bandsVisible: cfg?.bandsVisible ?? true
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
function vwapToSpec(cfg) {
|
|
269
|
+
return {
|
|
270
|
+
enabled: cfg?.enabled ?? false,
|
|
271
|
+
resetOffsetMin: cfg?.resetMinutes ?? 0,
|
|
272
|
+
color: inheritColor(cfg?.color),
|
|
273
|
+
width: cfg?.width ?? -1
|
|
274
|
+
};
|
|
275
|
+
}
|
|
164
276
|
var DEFAULT_BB_BAND_COLOR = 4280902399;
|
|
165
277
|
var DEFAULT_BB_BASIS_COLOR = 4294929664;
|
|
166
278
|
function bollingerToSpec(cfg) {
|
|
@@ -170,17 +282,52 @@ function bollingerToSpec(cfg) {
|
|
|
170
282
|
period: cfg?.period ?? 20,
|
|
171
283
|
mult: cfg?.stdDev ?? 2,
|
|
172
284
|
source: srcIdx < 0 ? 0 : srcIdx,
|
|
173
|
-
basisKind: cfg?.
|
|
285
|
+
basisKind: cfg?.maType === "ema" ? 1 : 0,
|
|
174
286
|
upperColor: (cfg?.upperColor != null ? parseColor(cfg.upperColor) : null) ?? DEFAULT_BB_BAND_COLOR,
|
|
175
287
|
upperWidth: cfg?.upperWidth ?? 1,
|
|
176
288
|
middleColor: (cfg?.middleColor != null ? parseColor(cfg.middleColor) : null) ?? DEFAULT_BB_BASIS_COLOR,
|
|
177
289
|
middleWidth: cfg?.middleWidth ?? 1,
|
|
178
290
|
lowerColor: (cfg?.lowerColor != null ? parseColor(cfg.lowerColor) : null) ?? DEFAULT_BB_BAND_COLOR,
|
|
179
291
|
lowerWidth: cfg?.lowerWidth ?? 1,
|
|
180
|
-
fillEnabled: cfg?.
|
|
292
|
+
fillEnabled: cfg?.fillVisible ?? true,
|
|
181
293
|
fillOpacity: cfg?.fillOpacity ?? 0.1
|
|
182
294
|
};
|
|
183
295
|
}
|
|
296
|
+
function macdToSpec(cfg) {
|
|
297
|
+
const srcIdx = cfg?.source ? MA_SOURCES.indexOf(cfg.source) : 0;
|
|
298
|
+
return {
|
|
299
|
+
enabled: cfg?.enabled ?? false,
|
|
300
|
+
fast: cfg?.fast ?? 12,
|
|
301
|
+
slow: cfg?.slow ?? 26,
|
|
302
|
+
signal: cfg?.signal ?? 9,
|
|
303
|
+
source: srcIdx < 0 ? 0 : srcIdx,
|
|
304
|
+
maKind: cfg?.maType === "sma" ? 0 : 1,
|
|
305
|
+
signalMaKind: cfg?.signalMaType === "sma" ? 0 : 1,
|
|
306
|
+
lineColor: inheritColor(cfg?.lineColor),
|
|
307
|
+
lineWidth: cfg?.lineWidth ?? -1,
|
|
308
|
+
lineVisible: cfg?.lineVisible ?? true,
|
|
309
|
+
signalColor: inheritColor(cfg?.signalColor),
|
|
310
|
+
signalWidth: cfg?.signalWidth ?? -1,
|
|
311
|
+
signalVisible: cfg?.signalVisible ?? true,
|
|
312
|
+
histVisible: cfg?.histogramVisible ?? true,
|
|
313
|
+
histUpColor: inheritColor(cfg?.histogramUpColor),
|
|
314
|
+
histUpFadingColor: inheritColor(cfg?.histogramUpFadingColor),
|
|
315
|
+
histDownColor: inheritColor(cfg?.histogramDownColor),
|
|
316
|
+
histDownFadingColor: inheritColor(cfg?.histogramDownFadingColor),
|
|
317
|
+
zeroColor: inheritColor(cfg?.zeroLineColor),
|
|
318
|
+
zeroVisible: cfg?.zeroLineVisible ?? true
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
function volumeToSpec(cfg) {
|
|
322
|
+
return {
|
|
323
|
+
enabled: cfg?.enabled ?? true,
|
|
324
|
+
heightFrac: cfg?.height ?? -1,
|
|
325
|
+
opacity: cfg?.opacity ?? -1,
|
|
326
|
+
radiusPx: cfg?.radius ?? -1,
|
|
327
|
+
upColor: (cfg?.upColor != null ? parseColor(cfg.upColor) : null) ?? 0,
|
|
328
|
+
downColor: (cfg?.downColor != null ? parseColor(cfg.downColor) : null) ?? 0
|
|
329
|
+
};
|
|
330
|
+
}
|
|
184
331
|
var DEFAULT_PRICE_LINE_COLOR = 4293874512;
|
|
185
332
|
var DEFAULT_PRICE_LINE_BODY_BG = 3642499368;
|
|
186
333
|
var DEFAULT_PRICE_LINE_HOVER_BOOST = 1.25;
|
|
@@ -218,14 +365,53 @@ function ensureInstalled() {
|
|
|
218
365
|
}
|
|
219
366
|
installed = true;
|
|
220
367
|
}
|
|
221
|
-
function useChartCore(candles, size, visibleRange, defaultCandleWidth, chartType, theme, rsi, macd, movingAverages, vwap, bollingerBands, priceLines) {
|
|
368
|
+
function useChartCore(candles, size, visibleRange, defaultCandleWidth, chartType, theme, rsi, macd, movingAverages, vwap, bollingerBands, volume, priceLines, transition) {
|
|
222
369
|
const handleRef = (0, import_react.useRef)(null);
|
|
223
370
|
const defaultWidthAppliedRef = (0, import_react.useRef)(false);
|
|
371
|
+
const volumeCollapseRef = (0, import_react.useRef)(null);
|
|
372
|
+
const prevDataRef = (0, import_react.useRef)(null);
|
|
373
|
+
const intervalMorphRaf = (0, import_react.useRef)(null);
|
|
224
374
|
const [picture, setPicture] = (0, import_react.useState)(null);
|
|
225
375
|
if (!handleRef.current && size.width > 0 && size.height > 0) {
|
|
226
376
|
ensureInstalled();
|
|
227
377
|
handleRef.current = globalThis.VroomChartJSI.create();
|
|
228
378
|
}
|
|
379
|
+
const animRef = (0, import_react.useRef)({ ms: 300, easing: void 0, reduceMotion: false });
|
|
380
|
+
animRef.current = {
|
|
381
|
+
ms: Math.max(0, transition?.transitionMs ?? 300),
|
|
382
|
+
easing: transition?.transitionEasing,
|
|
383
|
+
reduceMotion: transition?.reduceMotion ?? false
|
|
384
|
+
};
|
|
385
|
+
const onFrameRef = (0, import_react.useRef)(transition?.onFrame);
|
|
386
|
+
onFrameRef.current = transition?.onFrame;
|
|
387
|
+
const seriesKey = transition?.seriesKey;
|
|
388
|
+
const endIntervalMorph = (0, import_react.useCallback)(() => {
|
|
389
|
+
if (intervalMorphRaf.current != null) {
|
|
390
|
+
cancelAnimationFrame(intervalMorphRaf.current);
|
|
391
|
+
intervalMorphRaf.current = null;
|
|
392
|
+
}
|
|
393
|
+
handleRef.current?.setIntervalMorph(1);
|
|
394
|
+
}, []);
|
|
395
|
+
const startIntervalMorph = (0, import_react.useCallback)((h) => {
|
|
396
|
+
const { ms, easing } = animRef.current;
|
|
397
|
+
const start = performance.now();
|
|
398
|
+
const step = (now) => {
|
|
399
|
+
const p = Math.min(1, (now - start) / ms);
|
|
400
|
+
h.setIntervalMorph(p < 1 ? ease(easing, p) : 1);
|
|
401
|
+
const pic = h.render();
|
|
402
|
+
if (pic) onFrameRef.current?.(pic);
|
|
403
|
+
intervalMorphRaf.current = p < 1 ? requestAnimationFrame(step) : null;
|
|
404
|
+
};
|
|
405
|
+
intervalMorphRaf.current = requestAnimationFrame(step);
|
|
406
|
+
}, []);
|
|
407
|
+
(0, import_react.useEffect)(() => {
|
|
408
|
+
return () => {
|
|
409
|
+
if (intervalMorphRaf.current != null) {
|
|
410
|
+
cancelAnimationFrame(intervalMorphRaf.current);
|
|
411
|
+
intervalMorphRaf.current = null;
|
|
412
|
+
}
|
|
413
|
+
};
|
|
414
|
+
}, []);
|
|
229
415
|
const explicit = visibleRange != null;
|
|
230
416
|
const startMs = visibleRange?.startMs ?? 0;
|
|
231
417
|
const endMs = visibleRange?.endMs ?? 0;
|
|
@@ -235,6 +421,7 @@ function useChartCore(candles, size, visibleRange, defaultCandleWidth, chartType
|
|
|
235
421
|
const maKey = movingAverages ? JSON.stringify(movingAverages) : "";
|
|
236
422
|
const vwapKey = vwap ? JSON.stringify(vwap) : "";
|
|
237
423
|
const bollingerKey = bollingerBands ? JSON.stringify(bollingerBands) : "";
|
|
424
|
+
const volumeKey = volume ? JSON.stringify(volume) : "";
|
|
238
425
|
const priceLinesKey = priceLines ? JSON.stringify(priceLines) : "";
|
|
239
426
|
(0, import_react.useEffect)(() => {
|
|
240
427
|
const h = handleRef.current;
|
|
@@ -244,8 +431,54 @@ function useChartCore(candles, size, visibleRange, defaultCandleWidth, chartType
|
|
|
244
431
|
h.setDefaultCandleWidth(defaultCandleWidth);
|
|
245
432
|
defaultWidthAppliedRef.current = true;
|
|
246
433
|
}
|
|
434
|
+
let morphing = false;
|
|
247
435
|
if (candles.length > 0) {
|
|
248
|
-
|
|
436
|
+
const prev = prevDataRef.current;
|
|
437
|
+
const freshHandle = prev == null || prev.handle !== h;
|
|
438
|
+
if (freshHandle || prev.candles !== candles || prev.seriesKey !== seriesKey) {
|
|
439
|
+
const transitionKind = freshHandle ? "initial" : explicit ? "stream" : classifyTransition(prev.candles, candles, seriesKey !== prev.seriesKey);
|
|
440
|
+
let tfArgs = null;
|
|
441
|
+
let prevEnvelope = null;
|
|
442
|
+
if (transitionKind === "timeframe" && prev != null) {
|
|
443
|
+
const oldWindow = h.getVisibleRange();
|
|
444
|
+
const oldStepMs = inferStepMs(prev.candles);
|
|
445
|
+
if (oldWindow.endMs > oldWindow.startMs && oldStepMs != null) {
|
|
446
|
+
tfArgs = {
|
|
447
|
+
oldWindow,
|
|
448
|
+
oldStepMs,
|
|
449
|
+
oldLastMs: prev.candles[prev.candles.length - 1].timeMs
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
prevEnvelope = h.getVisiblePriceEnvelope();
|
|
453
|
+
morphing = animRef.current.ms > 0 && !animRef.current.reduceMotion && onFrameRef.current != null;
|
|
454
|
+
if (morphing) {
|
|
455
|
+
endIntervalMorph();
|
|
456
|
+
h.beginIntervalMorph();
|
|
457
|
+
}
|
|
458
|
+
} else if (transitionKind === "initial" || transitionKind === "reset") {
|
|
459
|
+
endIntervalMorph();
|
|
460
|
+
}
|
|
461
|
+
h.setCandles(packCandles(candles));
|
|
462
|
+
if (transitionKind === "timeframe") {
|
|
463
|
+
const newStepMs = inferStepMs(candles);
|
|
464
|
+
if (tfArgs && newStepMs != null) {
|
|
465
|
+
const w = timeframeWindow(
|
|
466
|
+
tfArgs.oldWindow,
|
|
467
|
+
tfArgs.oldStepMs,
|
|
468
|
+
tfArgs.oldLastMs,
|
|
469
|
+
newStepMs,
|
|
470
|
+
candles[candles.length - 1].timeMs
|
|
471
|
+
);
|
|
472
|
+
h.setVisibleRange(w.startMs, w.endMs);
|
|
473
|
+
}
|
|
474
|
+
if (prevEnvelope) h.preservePriceEnvelope(prevEnvelope.low, prevEnvelope.high);
|
|
475
|
+
else h.resetPriceScale();
|
|
476
|
+
if (morphing) startIntervalMorph(h);
|
|
477
|
+
} else if (transitionKind === "reset") {
|
|
478
|
+
h.resetView();
|
|
479
|
+
}
|
|
480
|
+
prevDataRef.current = { handle: h, candles, seriesKey };
|
|
481
|
+
}
|
|
249
482
|
}
|
|
250
483
|
if (explicit) {
|
|
251
484
|
h.setVisibleRange(startMs, endMs);
|
|
@@ -253,40 +486,27 @@ function useChartCore(candles, size, visibleRange, defaultCandleWidth, chartType
|
|
|
253
486
|
if (theme) {
|
|
254
487
|
applyTheme(h, theme);
|
|
255
488
|
}
|
|
256
|
-
h.setRSI(
|
|
257
|
-
|
|
258
|
-
rsi?.period ?? 14,
|
|
259
|
-
rsi?.upperBand ?? 70,
|
|
260
|
-
rsi?.lowerBand ?? 30,
|
|
261
|
-
rsi?.maEnabled ?? true,
|
|
262
|
-
rsi?.maPeriod ?? 14
|
|
263
|
-
);
|
|
264
|
-
h.setMACD(
|
|
265
|
-
macd?.enabled ?? false,
|
|
266
|
-
macd?.fast ?? 12,
|
|
267
|
-
macd?.slow ?? 26,
|
|
268
|
-
macd?.signal ?? 9
|
|
269
|
-
);
|
|
489
|
+
h.setRSI(rsiToSpec(rsi));
|
|
490
|
+
h.setMACD(macdToSpec(macd));
|
|
270
491
|
h.setOverlays((movingAverages ?? []).map(overlayToNumeric));
|
|
271
|
-
h.setVWAP(
|
|
272
|
-
vwap?.enabled ?? false,
|
|
273
|
-
vwap?.resetMinutes ?? 0,
|
|
274
|
-
(vwap?.color != null ? parseColor(vwap.color) : null) ?? 4278238420,
|
|
275
|
-
vwap?.width ?? 1.5
|
|
276
|
-
);
|
|
492
|
+
h.setVWAP(vwapToSpec(vwap));
|
|
277
493
|
h.setBollinger(bollingerToSpec(bollingerBands));
|
|
494
|
+
h.setVolume(volumeToSpec(volume));
|
|
495
|
+
const collapse = volumeCollapseRef.current;
|
|
496
|
+
if (collapse) h.setVolumeCollapse(collapse.t, collapse.easing);
|
|
278
497
|
h.setPriceLines(
|
|
279
498
|
priceLines?.lines.length ? priceLinesToSpec(priceLines) : EMPTY_PRICE_LINES
|
|
280
499
|
);
|
|
281
|
-
setPicture(h.render());
|
|
282
|
-
}, [candles, size.width, size.height, size.pxRatio, explicit, startMs, endMs, defaultCandleWidth, themeKey, rsiKey, macdKey, maKey, vwapKey, bollingerKey, priceLinesKey]);
|
|
283
|
-
return { handle: handleRef.current, picture };
|
|
500
|
+
if (!morphing) setPicture(h.render());
|
|
501
|
+
}, [candles, seriesKey, size.width, size.height, size.pxRatio, explicit, startMs, endMs, defaultCandleWidth, themeKey, rsiKey, macdKey, maKey, vwapKey, bollingerKey, volumeKey, priceLinesKey, startIntervalMorph, endIntervalMorph]);
|
|
502
|
+
return { handle: handleRef.current, picture, volumeCollapseRef };
|
|
284
503
|
}
|
|
285
504
|
|
|
286
505
|
// src/VroomChart.tsx
|
|
287
506
|
function VroomChart(props) {
|
|
288
507
|
const {
|
|
289
508
|
candles,
|
|
509
|
+
seriesKey,
|
|
290
510
|
width: widthProp,
|
|
291
511
|
height: heightProp,
|
|
292
512
|
style,
|
|
@@ -294,12 +514,14 @@ function VroomChart(props) {
|
|
|
294
514
|
defaultCandleWidth,
|
|
295
515
|
chartType,
|
|
296
516
|
transitionMs,
|
|
517
|
+
transitionEasing,
|
|
297
518
|
theme,
|
|
298
519
|
rsi,
|
|
299
520
|
macd,
|
|
300
521
|
movingAverages,
|
|
301
522
|
vwap,
|
|
302
523
|
bollingerBands,
|
|
524
|
+
volume,
|
|
303
525
|
crosshairOffset = 40,
|
|
304
526
|
onCrosshair,
|
|
305
527
|
onViewportChange,
|
|
@@ -327,7 +549,20 @@ function VroomChart(props) {
|
|
|
327
549
|
} : void 0,
|
|
328
550
|
[priceLines, priceLinesStyle, onPriceLineClose]
|
|
329
551
|
);
|
|
330
|
-
const
|
|
552
|
+
const emptyPicture = (0, import_react2.useMemo)(() => {
|
|
553
|
+
const rec = import_react_native_skia.Skia.PictureRecorder();
|
|
554
|
+
rec.beginRecording(import_react_native_skia.Skia.XYWHRect(0, 0, 1, 1));
|
|
555
|
+
return rec.finishRecordingAsPicture();
|
|
556
|
+
}, []);
|
|
557
|
+
const pictureSV = (0, import_react_native_reanimated.useSharedValue)(emptyPicture);
|
|
558
|
+
const reduceMotion = (0, import_react_native_reanimated.useReducedMotion)();
|
|
559
|
+
const onFrame = (0, import_react2.useCallback)(
|
|
560
|
+
(p) => {
|
|
561
|
+
pictureSV.value = p;
|
|
562
|
+
},
|
|
563
|
+
[pictureSV]
|
|
564
|
+
);
|
|
565
|
+
const { handle, picture, volumeCollapseRef } = useChartCore(
|
|
331
566
|
candles,
|
|
332
567
|
{ width, height },
|
|
333
568
|
visibleRange,
|
|
@@ -339,14 +574,10 @@ function VroomChart(props) {
|
|
|
339
574
|
movingAverages,
|
|
340
575
|
vwap,
|
|
341
576
|
bollingerBands,
|
|
342
|
-
|
|
577
|
+
volume,
|
|
578
|
+
priceLinesProp,
|
|
579
|
+
{ seriesKey, transitionMs, transitionEasing, reduceMotion, onFrame }
|
|
343
580
|
);
|
|
344
|
-
const emptyPicture = (0, import_react2.useMemo)(() => {
|
|
345
|
-
const rec = import_react_native_skia.Skia.PictureRecorder();
|
|
346
|
-
rec.beginRecording(import_react_native_skia.Skia.XYWHRect(0, 0, 1, 1));
|
|
347
|
-
return rec.finishRecordingAsPicture();
|
|
348
|
-
}, []);
|
|
349
|
-
const pictureSV = (0, import_react_native_reanimated.useSharedValue)(emptyPicture);
|
|
350
581
|
const crosshairActive = (0, import_react2.useRef)(false);
|
|
351
582
|
const lastCrosshairTime = (0, import_react2.useRef)(null);
|
|
352
583
|
(0, import_react2.useEffect)(() => {
|
|
@@ -386,6 +617,8 @@ function VroomChart(props) {
|
|
|
386
617
|
const morphRaf = (0, import_react2.useRef)(null);
|
|
387
618
|
const morphFade = (0, import_react2.useRef)(null);
|
|
388
619
|
const morphHandle = (0, import_react2.useRef)(null);
|
|
620
|
+
const easingRef = (0, import_react2.useRef)(transitionEasing);
|
|
621
|
+
easingRef.current = transitionEasing;
|
|
389
622
|
(0, import_react2.useEffect)(() => {
|
|
390
623
|
if (!handle) return void 0;
|
|
391
624
|
const target = chartType === "line" ? 1 : 0;
|
|
@@ -415,10 +648,9 @@ function VroomChart(props) {
|
|
|
415
648
|
const step = (now) => {
|
|
416
649
|
if (startTs == null) startTs = now;
|
|
417
650
|
const prog = Math.min(1, (now - startTs) / dur);
|
|
418
|
-
const
|
|
419
|
-
const fade = from + (target - from) * e;
|
|
651
|
+
const fade = from + (target - from) * ease(easingRef.current, prog);
|
|
420
652
|
morphFade.current = fade;
|
|
421
|
-
handle.setMorph(fade, fade);
|
|
653
|
+
handle.setMorph(reduceMotion ? 0 : fade, fade);
|
|
422
654
|
const p = handle.render();
|
|
423
655
|
if (p) pictureSV.value = p;
|
|
424
656
|
if (prog < 1) {
|
|
@@ -438,7 +670,52 @@ function VroomChart(props) {
|
|
|
438
670
|
morphRaf.current = null;
|
|
439
671
|
}
|
|
440
672
|
};
|
|
441
|
-
}, [handle, chartType, transitionMs, pictureSV]);
|
|
673
|
+
}, [handle, chartType, transitionMs, reduceMotion, pictureSV]);
|
|
674
|
+
const volumeRaf = (0, import_react2.useRef)(null);
|
|
675
|
+
const volumeHandle = (0, import_react2.useRef)(null);
|
|
676
|
+
(0, import_react2.useEffect)(() => {
|
|
677
|
+
if (!handle) return void 0;
|
|
678
|
+
const target = volume?.enabled ?? true ? 0 : 1;
|
|
679
|
+
const easing = easingIndex(easingRef.current);
|
|
680
|
+
if (volumeHandle.current !== handle || volumeCollapseRef.current == null) {
|
|
681
|
+
volumeHandle.current = handle;
|
|
682
|
+
volumeCollapseRef.current = { t: target, easing };
|
|
683
|
+
return void 0;
|
|
684
|
+
}
|
|
685
|
+
if (volumeCollapseRef.current.t === target) return void 0;
|
|
686
|
+
if (volumeRaf.current != null) {
|
|
687
|
+
cancelAnimationFrame(volumeRaf.current);
|
|
688
|
+
volumeRaf.current = null;
|
|
689
|
+
}
|
|
690
|
+
const dur = Math.max(0, transitionMs ?? 300);
|
|
691
|
+
if (dur === 0 || reduceMotion) {
|
|
692
|
+
volumeCollapseRef.current = { t: target, easing };
|
|
693
|
+
handle.setVolumeCollapse(target, easing);
|
|
694
|
+
const p = handle.render();
|
|
695
|
+
if (p) pictureSV.value = p;
|
|
696
|
+
return void 0;
|
|
697
|
+
}
|
|
698
|
+
const from = volumeCollapseRef.current.t;
|
|
699
|
+
let startTs = null;
|
|
700
|
+
const step = (now) => {
|
|
701
|
+
if (startTs == null) startTs = now;
|
|
702
|
+
const prog = Math.min(1, (now - startTs) / dur);
|
|
703
|
+
const t = prog < 1 ? from + (target - from) * prog : target;
|
|
704
|
+
const kind = easingIndex(easingRef.current);
|
|
705
|
+
volumeCollapseRef.current = { t, easing: kind };
|
|
706
|
+
handle.setVolumeCollapse(t, kind);
|
|
707
|
+
const p = handle.render();
|
|
708
|
+
if (p) pictureSV.value = p;
|
|
709
|
+
volumeRaf.current = prog < 1 ? requestAnimationFrame(step) : null;
|
|
710
|
+
};
|
|
711
|
+
volumeRaf.current = requestAnimationFrame(step);
|
|
712
|
+
return () => {
|
|
713
|
+
if (volumeRaf.current != null) {
|
|
714
|
+
cancelAnimationFrame(volumeRaf.current);
|
|
715
|
+
volumeRaf.current = null;
|
|
716
|
+
}
|
|
717
|
+
};
|
|
718
|
+
}, [handle, volume?.enabled, transitionMs, reduceMotion, pictureSV, volumeCollapseRef]);
|
|
442
719
|
const hitAxis = (0, import_react2.useCallback)(
|
|
443
720
|
(x, y) => {
|
|
444
721
|
if (!handle) return "chart";
|
|
@@ -650,6 +927,9 @@ function VroomChart(props) {
|
|
|
650
927
|
}
|
|
651
928
|
// Annotate the CommonJS export names for ESM import in node:
|
|
652
929
|
0 && (module.exports = {
|
|
653
|
-
VroomChart
|
|
930
|
+
VroomChart,
|
|
931
|
+
classifyTransition,
|
|
932
|
+
inferStepMs,
|
|
933
|
+
timeframeWindow
|
|
654
934
|
});
|
|
655
935
|
//# sourceMappingURL=index.js.map
|