react-native-vroom-chart 0.7.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 +131 -1
- package/cpp/_core_include/vroom/vroom_chart.h +51 -14
- package/cpp/_core_src/chart.h +14 -9
- package/cpp/_core_src/chart_facade.cpp +41 -3
- package/cpp/_core_src/drawings.cpp +147 -4
- package/cpp/_core_src/drawings.h +10 -5
- package/lib/index.d.mts +46 -4
- package/lib/index.d.ts +46 -4
- package/lib/index.js +196 -41
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +201 -49
- package/lib/index.mjs.map +1 -1
- package/package.json +3 -3
- package/src/VroomChart.tsx +33 -16
- package/src/dataTransitions.ts +148 -0
- package/src/index.ts +6 -0
- package/src/jsi.d.ts +51 -0
- package/src/useChartCore.ts +171 -4
package/lib/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/VroomChart.tsx
|
|
2
|
-
import React, { useEffect as useEffect2, useRef as useRef2, useCallback, useState as useState2, useMemo } from "react";
|
|
2
|
+
import React, { useEffect as useEffect2, useRef as useRef2, useCallback as useCallback2, useState as useState2, useMemo } from "react";
|
|
3
3
|
import { View } from "react-native";
|
|
4
4
|
import { Canvas, Picture, Skia } from "@shopify/react-native-skia";
|
|
5
5
|
import {
|
|
@@ -7,15 +7,95 @@ import {
|
|
|
7
7
|
GestureDetector,
|
|
8
8
|
GestureHandlerRootView
|
|
9
9
|
} from "react-native-gesture-handler";
|
|
10
|
-
import { useSharedValue } from "react-native-reanimated";
|
|
10
|
+
import { useReducedMotion, useSharedValue } from "react-native-reanimated";
|
|
11
11
|
|
|
12
12
|
// src/useChartCore.ts
|
|
13
|
-
import { useEffect, useRef, useState } from "react";
|
|
13
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
14
14
|
|
|
15
15
|
// src/NativeVroomChart.ts
|
|
16
16
|
import { TurboModuleRegistry } from "react-native";
|
|
17
17
|
var NativeVroomChart_default = TurboModuleRegistry.getEnforcing("VroomChartModule");
|
|
18
18
|
|
|
19
|
+
// src/dataTransitions.ts
|
|
20
|
+
var STEP_TOLERANCE = 0.01;
|
|
21
|
+
var MAX_SAME_ASSET_CLOSE_RATIO = 1.25;
|
|
22
|
+
var MAX_END_DRIFT_STEPS = 3;
|
|
23
|
+
var MAX_STREAM_ADVANCE_STEPS = 5;
|
|
24
|
+
function inferStepMs(candles) {
|
|
25
|
+
if (candles.length < 2) return null;
|
|
26
|
+
const k = Math.min(candles.length - 1, 8);
|
|
27
|
+
const diffs = [];
|
|
28
|
+
for (let i = 0; i < k; i++) diffs.push(candles[i + 1].timeMs - candles[i].timeMs);
|
|
29
|
+
diffs.sort((a, b) => a - b);
|
|
30
|
+
const median = diffs[Math.floor(diffs.length / 2)];
|
|
31
|
+
return median > 0 ? median : null;
|
|
32
|
+
}
|
|
33
|
+
function indexByTime(candles, t) {
|
|
34
|
+
let lo = 0;
|
|
35
|
+
let hi = candles.length - 1;
|
|
36
|
+
while (lo <= hi) {
|
|
37
|
+
const mid = lo + hi >>> 1;
|
|
38
|
+
const v = candles[mid].timeMs;
|
|
39
|
+
if (v === t) return mid;
|
|
40
|
+
if (v < t) lo = mid + 1;
|
|
41
|
+
else hi = mid - 1;
|
|
42
|
+
}
|
|
43
|
+
return -1;
|
|
44
|
+
}
|
|
45
|
+
function classifyTransition(prev, next, seriesKeyChanged) {
|
|
46
|
+
if (!prev || prev.length === 0) return "initial";
|
|
47
|
+
if (next.length === 0) return "stream";
|
|
48
|
+
if (seriesKeyChanged) return "reset";
|
|
49
|
+
const prevStep = inferStepMs(prev);
|
|
50
|
+
const nextStep = inferStepMs(next);
|
|
51
|
+
if (prevStep == null || nextStep == null) return "reset";
|
|
52
|
+
const prevLast = prev[prev.length - 1];
|
|
53
|
+
const nextLast = next[next.length - 1];
|
|
54
|
+
if (Math.abs(nextStep - prevStep) <= prevStep * STEP_TOLERANCE) {
|
|
55
|
+
const idx = indexByTime(next, prevLast.timeMs);
|
|
56
|
+
const aligned = idx >= 0;
|
|
57
|
+
const sharedBarRatio = aligned && next[idx].close > 0 && prevLast.close > 0 ? Math.max(next[idx].close / prevLast.close, prevLast.close / next[idx].close) : Infinity;
|
|
58
|
+
const advanced = nextLast.timeMs >= prevLast.timeMs && nextLast.timeMs - prevLast.timeMs <= MAX_STREAM_ADVANCE_STEPS * nextStep;
|
|
59
|
+
return sharedBarRatio <= MAX_SAME_ASSET_CLOSE_RATIO && advanced ? "stream" : "reset";
|
|
60
|
+
}
|
|
61
|
+
const closeRatio = prevLast.close > 0 && nextLast.close > 0 ? Math.max(nextLast.close / prevLast.close, prevLast.close / nextLast.close) : Infinity;
|
|
62
|
+
const prevEnd = prevLast.timeMs + prevStep;
|
|
63
|
+
const nextEnd = nextLast.timeMs + nextStep;
|
|
64
|
+
const endsTogether = Math.abs(nextEnd - prevEnd) <= MAX_END_DRIFT_STEPS * Math.max(prevStep, nextStep);
|
|
65
|
+
return closeRatio <= MAX_SAME_ASSET_CLOSE_RATIO && endsTogether ? "timeframe" : "reset";
|
|
66
|
+
}
|
|
67
|
+
function timeframeWindow(oldWindow, oldStepMs, oldLastMs, newStepMs, newLastMs) {
|
|
68
|
+
const slots = (oldWindow.endMs - oldWindow.startMs) / oldStepMs;
|
|
69
|
+
const offsetRaw = (oldWindow.endMs - oldLastMs) / oldStepMs;
|
|
70
|
+
const offsetSlots = Math.min(Math.max(offsetRaw, 0), slots * 0.75);
|
|
71
|
+
const endMs = Math.round(newLastMs + offsetSlots * newStepMs);
|
|
72
|
+
return { startMs: Math.round(endMs - slots * newStepMs), endMs };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// src/easing.ts
|
|
76
|
+
function ease(kind, p) {
|
|
77
|
+
switch (kind) {
|
|
78
|
+
case "linear":
|
|
79
|
+
return p;
|
|
80
|
+
case "ease-in":
|
|
81
|
+
return p * p;
|
|
82
|
+
case "ease-out":
|
|
83
|
+
return p * (2 - p);
|
|
84
|
+
default:
|
|
85
|
+
return p * p * (3 - 2 * p);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
var EASINGS = [
|
|
89
|
+
"linear",
|
|
90
|
+
"ease-in",
|
|
91
|
+
"ease-out",
|
|
92
|
+
"ease-in-out"
|
|
93
|
+
];
|
|
94
|
+
function easingIndex(kind) {
|
|
95
|
+
const i = kind ? EASINGS.indexOf(kind) : -1;
|
|
96
|
+
return i < 0 ? EASINGS.indexOf("ease-in-out") : i;
|
|
97
|
+
}
|
|
98
|
+
|
|
19
99
|
// src/packCandles.ts
|
|
20
100
|
var BYTES_PER_CANDLE = 48;
|
|
21
101
|
function packCandles(candles) {
|
|
@@ -250,15 +330,53 @@ function ensureInstalled() {
|
|
|
250
330
|
}
|
|
251
331
|
installed = true;
|
|
252
332
|
}
|
|
253
|
-
function useChartCore(candles, size, visibleRange, defaultCandleWidth, chartType, theme, rsi, macd, movingAverages, vwap, bollingerBands, volume, priceLines) {
|
|
333
|
+
function useChartCore(candles, size, visibleRange, defaultCandleWidth, chartType, theme, rsi, macd, movingAverages, vwap, bollingerBands, volume, priceLines, transition) {
|
|
254
334
|
const handleRef = useRef(null);
|
|
255
335
|
const defaultWidthAppliedRef = useRef(false);
|
|
256
336
|
const volumeCollapseRef = useRef(null);
|
|
337
|
+
const prevDataRef = useRef(null);
|
|
338
|
+
const intervalMorphRaf = useRef(null);
|
|
257
339
|
const [picture, setPicture] = useState(null);
|
|
258
340
|
if (!handleRef.current && size.width > 0 && size.height > 0) {
|
|
259
341
|
ensureInstalled();
|
|
260
342
|
handleRef.current = globalThis.VroomChartJSI.create();
|
|
261
343
|
}
|
|
344
|
+
const animRef = useRef({ ms: 300, easing: void 0, reduceMotion: false });
|
|
345
|
+
animRef.current = {
|
|
346
|
+
ms: Math.max(0, transition?.transitionMs ?? 300),
|
|
347
|
+
easing: transition?.transitionEasing,
|
|
348
|
+
reduceMotion: transition?.reduceMotion ?? false
|
|
349
|
+
};
|
|
350
|
+
const onFrameRef = useRef(transition?.onFrame);
|
|
351
|
+
onFrameRef.current = transition?.onFrame;
|
|
352
|
+
const seriesKey = transition?.seriesKey;
|
|
353
|
+
const endIntervalMorph = useCallback(() => {
|
|
354
|
+
if (intervalMorphRaf.current != null) {
|
|
355
|
+
cancelAnimationFrame(intervalMorphRaf.current);
|
|
356
|
+
intervalMorphRaf.current = null;
|
|
357
|
+
}
|
|
358
|
+
handleRef.current?.setIntervalMorph(1);
|
|
359
|
+
}, []);
|
|
360
|
+
const startIntervalMorph = useCallback((h) => {
|
|
361
|
+
const { ms, easing } = animRef.current;
|
|
362
|
+
const start = performance.now();
|
|
363
|
+
const step = (now) => {
|
|
364
|
+
const p = Math.min(1, (now - start) / ms);
|
|
365
|
+
h.setIntervalMorph(p < 1 ? ease(easing, p) : 1);
|
|
366
|
+
const pic = h.render();
|
|
367
|
+
if (pic) onFrameRef.current?.(pic);
|
|
368
|
+
intervalMorphRaf.current = p < 1 ? requestAnimationFrame(step) : null;
|
|
369
|
+
};
|
|
370
|
+
intervalMorphRaf.current = requestAnimationFrame(step);
|
|
371
|
+
}, []);
|
|
372
|
+
useEffect(() => {
|
|
373
|
+
return () => {
|
|
374
|
+
if (intervalMorphRaf.current != null) {
|
|
375
|
+
cancelAnimationFrame(intervalMorphRaf.current);
|
|
376
|
+
intervalMorphRaf.current = null;
|
|
377
|
+
}
|
|
378
|
+
};
|
|
379
|
+
}, []);
|
|
262
380
|
const explicit = visibleRange != null;
|
|
263
381
|
const startMs = visibleRange?.startMs ?? 0;
|
|
264
382
|
const endMs = visibleRange?.endMs ?? 0;
|
|
@@ -278,8 +396,54 @@ function useChartCore(candles, size, visibleRange, defaultCandleWidth, chartType
|
|
|
278
396
|
h.setDefaultCandleWidth(defaultCandleWidth);
|
|
279
397
|
defaultWidthAppliedRef.current = true;
|
|
280
398
|
}
|
|
399
|
+
let morphing = false;
|
|
281
400
|
if (candles.length > 0) {
|
|
282
|
-
|
|
401
|
+
const prev = prevDataRef.current;
|
|
402
|
+
const freshHandle = prev == null || prev.handle !== h;
|
|
403
|
+
if (freshHandle || prev.candles !== candles || prev.seriesKey !== seriesKey) {
|
|
404
|
+
const transitionKind = freshHandle ? "initial" : explicit ? "stream" : classifyTransition(prev.candles, candles, seriesKey !== prev.seriesKey);
|
|
405
|
+
let tfArgs = null;
|
|
406
|
+
let prevEnvelope = null;
|
|
407
|
+
if (transitionKind === "timeframe" && prev != null) {
|
|
408
|
+
const oldWindow = h.getVisibleRange();
|
|
409
|
+
const oldStepMs = inferStepMs(prev.candles);
|
|
410
|
+
if (oldWindow.endMs > oldWindow.startMs && oldStepMs != null) {
|
|
411
|
+
tfArgs = {
|
|
412
|
+
oldWindow,
|
|
413
|
+
oldStepMs,
|
|
414
|
+
oldLastMs: prev.candles[prev.candles.length - 1].timeMs
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
prevEnvelope = h.getVisiblePriceEnvelope();
|
|
418
|
+
morphing = animRef.current.ms > 0 && !animRef.current.reduceMotion && onFrameRef.current != null;
|
|
419
|
+
if (morphing) {
|
|
420
|
+
endIntervalMorph();
|
|
421
|
+
h.beginIntervalMorph();
|
|
422
|
+
}
|
|
423
|
+
} else if (transitionKind === "initial" || transitionKind === "reset") {
|
|
424
|
+
endIntervalMorph();
|
|
425
|
+
}
|
|
426
|
+
h.setCandles(packCandles(candles));
|
|
427
|
+
if (transitionKind === "timeframe") {
|
|
428
|
+
const newStepMs = inferStepMs(candles);
|
|
429
|
+
if (tfArgs && newStepMs != null) {
|
|
430
|
+
const w = timeframeWindow(
|
|
431
|
+
tfArgs.oldWindow,
|
|
432
|
+
tfArgs.oldStepMs,
|
|
433
|
+
tfArgs.oldLastMs,
|
|
434
|
+
newStepMs,
|
|
435
|
+
candles[candles.length - 1].timeMs
|
|
436
|
+
);
|
|
437
|
+
h.setVisibleRange(w.startMs, w.endMs);
|
|
438
|
+
}
|
|
439
|
+
if (prevEnvelope) h.preservePriceEnvelope(prevEnvelope.low, prevEnvelope.high);
|
|
440
|
+
else h.resetPriceScale();
|
|
441
|
+
if (morphing) startIntervalMorph(h);
|
|
442
|
+
} else if (transitionKind === "reset") {
|
|
443
|
+
h.resetView();
|
|
444
|
+
}
|
|
445
|
+
prevDataRef.current = { handle: h, candles, seriesKey };
|
|
446
|
+
}
|
|
283
447
|
}
|
|
284
448
|
if (explicit) {
|
|
285
449
|
h.setVisibleRange(startMs, endMs);
|
|
@@ -298,39 +462,16 @@ function useChartCore(candles, size, visibleRange, defaultCandleWidth, chartType
|
|
|
298
462
|
h.setPriceLines(
|
|
299
463
|
priceLines?.lines.length ? priceLinesToSpec(priceLines) : EMPTY_PRICE_LINES
|
|
300
464
|
);
|
|
301
|
-
setPicture(h.render());
|
|
302
|
-
}, [candles, size.width, size.height, size.pxRatio, explicit, startMs, endMs, defaultCandleWidth, themeKey, rsiKey, macdKey, maKey, vwapKey, bollingerKey, volumeKey, priceLinesKey]);
|
|
465
|
+
if (!morphing) setPicture(h.render());
|
|
466
|
+
}, [candles, seriesKey, size.width, size.height, size.pxRatio, explicit, startMs, endMs, defaultCandleWidth, themeKey, rsiKey, macdKey, maKey, vwapKey, bollingerKey, volumeKey, priceLinesKey, startIntervalMorph, endIntervalMorph]);
|
|
303
467
|
return { handle: handleRef.current, picture, volumeCollapseRef };
|
|
304
468
|
}
|
|
305
469
|
|
|
306
|
-
// src/easing.ts
|
|
307
|
-
function ease(kind, p) {
|
|
308
|
-
switch (kind) {
|
|
309
|
-
case "linear":
|
|
310
|
-
return p;
|
|
311
|
-
case "ease-in":
|
|
312
|
-
return p * p;
|
|
313
|
-
case "ease-out":
|
|
314
|
-
return p * (2 - p);
|
|
315
|
-
default:
|
|
316
|
-
return p * p * (3 - 2 * p);
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
var EASINGS = [
|
|
320
|
-
"linear",
|
|
321
|
-
"ease-in",
|
|
322
|
-
"ease-out",
|
|
323
|
-
"ease-in-out"
|
|
324
|
-
];
|
|
325
|
-
function easingIndex(kind) {
|
|
326
|
-
const i = kind ? EASINGS.indexOf(kind) : -1;
|
|
327
|
-
return i < 0 ? EASINGS.indexOf("ease-in-out") : i;
|
|
328
|
-
}
|
|
329
|
-
|
|
330
470
|
// src/VroomChart.tsx
|
|
331
471
|
function VroomChart(props) {
|
|
332
472
|
const {
|
|
333
473
|
candles,
|
|
474
|
+
seriesKey,
|
|
334
475
|
width: widthProp,
|
|
335
476
|
height: heightProp,
|
|
336
477
|
style,
|
|
@@ -358,7 +499,7 @@ function VroomChart(props) {
|
|
|
358
499
|
const [measured, setMeasured] = useState2({ width: 0, height: 0 });
|
|
359
500
|
const width = widthProp ?? measured.width;
|
|
360
501
|
const height = heightProp ?? measured.height;
|
|
361
|
-
const onLayout =
|
|
502
|
+
const onLayout = useCallback2((e) => {
|
|
362
503
|
const w = Math.round(e.nativeEvent.layout.width);
|
|
363
504
|
const h = Math.round(e.nativeEvent.layout.height);
|
|
364
505
|
setMeasured(
|
|
@@ -373,6 +514,19 @@ function VroomChart(props) {
|
|
|
373
514
|
} : void 0,
|
|
374
515
|
[priceLines, priceLinesStyle, onPriceLineClose]
|
|
375
516
|
);
|
|
517
|
+
const emptyPicture = useMemo(() => {
|
|
518
|
+
const rec = Skia.PictureRecorder();
|
|
519
|
+
rec.beginRecording(Skia.XYWHRect(0, 0, 1, 1));
|
|
520
|
+
return rec.finishRecordingAsPicture();
|
|
521
|
+
}, []);
|
|
522
|
+
const pictureSV = useSharedValue(emptyPicture);
|
|
523
|
+
const reduceMotion = useReducedMotion();
|
|
524
|
+
const onFrame = useCallback2(
|
|
525
|
+
(p) => {
|
|
526
|
+
pictureSV.value = p;
|
|
527
|
+
},
|
|
528
|
+
[pictureSV]
|
|
529
|
+
);
|
|
376
530
|
const { handle, picture, volumeCollapseRef } = useChartCore(
|
|
377
531
|
candles,
|
|
378
532
|
{ width, height },
|
|
@@ -386,21 +540,16 @@ function VroomChart(props) {
|
|
|
386
540
|
vwap,
|
|
387
541
|
bollingerBands,
|
|
388
542
|
volume,
|
|
389
|
-
priceLinesProp
|
|
543
|
+
priceLinesProp,
|
|
544
|
+
{ seriesKey, transitionMs, transitionEasing, reduceMotion, onFrame }
|
|
390
545
|
);
|
|
391
|
-
const emptyPicture = useMemo(() => {
|
|
392
|
-
const rec = Skia.PictureRecorder();
|
|
393
|
-
rec.beginRecording(Skia.XYWHRect(0, 0, 1, 1));
|
|
394
|
-
return rec.finishRecordingAsPicture();
|
|
395
|
-
}, []);
|
|
396
|
-
const pictureSV = useSharedValue(emptyPicture);
|
|
397
546
|
const crosshairActive = useRef2(false);
|
|
398
547
|
const lastCrosshairTime = useRef2(null);
|
|
399
548
|
useEffect2(() => {
|
|
400
549
|
if (picture) pictureSV.value = picture;
|
|
401
550
|
}, [picture, pictureSV]);
|
|
402
551
|
const decayRaf = useRef2(null);
|
|
403
|
-
const cancelDecay =
|
|
552
|
+
const cancelDecay = useCallback2(() => {
|
|
404
553
|
if (decayRaf.current != null) {
|
|
405
554
|
cancelAnimationFrame(decayRaf.current);
|
|
406
555
|
decayRaf.current = null;
|
|
@@ -408,7 +557,7 @@ function VroomChart(props) {
|
|
|
408
557
|
}, []);
|
|
409
558
|
useEffect2(() => cancelDecay, [cancelDecay]);
|
|
410
559
|
const animRaf = useRef2(null);
|
|
411
|
-
const animTick =
|
|
560
|
+
const animTick = useCallback2(() => {
|
|
412
561
|
animRaf.current = null;
|
|
413
562
|
if (!handle) return;
|
|
414
563
|
const next = handle.render();
|
|
@@ -417,7 +566,7 @@ function VroomChart(props) {
|
|
|
417
566
|
animRaf.current = requestAnimationFrame(animTick);
|
|
418
567
|
}
|
|
419
568
|
}, [handle, pictureSV]);
|
|
420
|
-
const maybeStartAnim =
|
|
569
|
+
const maybeStartAnim = useCallback2(() => {
|
|
421
570
|
if (animRaf.current != null) return;
|
|
422
571
|
if (!handle?.isAnimating()) return;
|
|
423
572
|
animRaf.current = requestAnimationFrame(animTick);
|
|
@@ -466,7 +615,7 @@ function VroomChart(props) {
|
|
|
466
615
|
const prog = Math.min(1, (now - startTs) / dur);
|
|
467
616
|
const fade = from + (target - from) * ease(easingRef.current, prog);
|
|
468
617
|
morphFade.current = fade;
|
|
469
|
-
handle.setMorph(fade, fade);
|
|
618
|
+
handle.setMorph(reduceMotion ? 0 : fade, fade);
|
|
470
619
|
const p = handle.render();
|
|
471
620
|
if (p) pictureSV.value = p;
|
|
472
621
|
if (prog < 1) {
|
|
@@ -486,7 +635,7 @@ function VroomChart(props) {
|
|
|
486
635
|
morphRaf.current = null;
|
|
487
636
|
}
|
|
488
637
|
};
|
|
489
|
-
}, [handle, chartType, transitionMs, pictureSV]);
|
|
638
|
+
}, [handle, chartType, transitionMs, reduceMotion, pictureSV]);
|
|
490
639
|
const volumeRaf = useRef2(null);
|
|
491
640
|
const volumeHandle = useRef2(null);
|
|
492
641
|
useEffect2(() => {
|
|
@@ -504,7 +653,7 @@ function VroomChart(props) {
|
|
|
504
653
|
volumeRaf.current = null;
|
|
505
654
|
}
|
|
506
655
|
const dur = Math.max(0, transitionMs ?? 300);
|
|
507
|
-
if (dur === 0) {
|
|
656
|
+
if (dur === 0 || reduceMotion) {
|
|
508
657
|
volumeCollapseRef.current = { t: target, easing };
|
|
509
658
|
handle.setVolumeCollapse(target, easing);
|
|
510
659
|
const p = handle.render();
|
|
@@ -531,8 +680,8 @@ function VroomChart(props) {
|
|
|
531
680
|
volumeRaf.current = null;
|
|
532
681
|
}
|
|
533
682
|
};
|
|
534
|
-
}, [handle, volume?.enabled, transitionMs, pictureSV, volumeCollapseRef]);
|
|
535
|
-
const hitAxis =
|
|
683
|
+
}, [handle, volume?.enabled, transitionMs, reduceMotion, pictureSV, volumeCollapseRef]);
|
|
684
|
+
const hitAxis = useCallback2(
|
|
536
685
|
(x, y) => {
|
|
537
686
|
if (!handle) return "chart";
|
|
538
687
|
const { yAxisWidth, xAxisHeight, indicatorHeight } = handle.getAxisMetrics();
|
|
@@ -545,7 +694,7 @@ function VroomChart(props) {
|
|
|
545
694
|
},
|
|
546
695
|
[handle, width, height]
|
|
547
696
|
);
|
|
548
|
-
const hitPriceLine =
|
|
697
|
+
const hitPriceLine = useCallback2(
|
|
549
698
|
(x, y) => {
|
|
550
699
|
if (!handle || !priceLines?.length) return null;
|
|
551
700
|
const hit = handle.hitTestPriceLine(x, y);
|
|
@@ -742,6 +891,9 @@ function VroomChart(props) {
|
|
|
742
891
|
);
|
|
743
892
|
}
|
|
744
893
|
export {
|
|
745
|
-
VroomChart
|
|
894
|
+
VroomChart,
|
|
895
|
+
classifyTransition,
|
|
896
|
+
inferStepMs,
|
|
897
|
+
timeframeWindow
|
|
746
898
|
};
|
|
747
899
|
//# sourceMappingURL=index.mjs.map
|