react-native-vroom-chart 0.11.1 → 0.13.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 +22 -0
- package/cpp/_core_include/vroom/vroom_chart.h +19 -2
- package/cpp/_core_src/chart.cpp +5 -2
- package/cpp/_core_src/chart.h +7 -0
- package/cpp/_core_src/chart_facade.cpp +9 -0
- package/cpp/_core_src/crosshair.cpp +10 -5
- package/cpp/_core_src/labels.cpp +7 -2
- package/cpp/_core_src/macd_pane.cpp +3 -2
- package/cpp/_core_src/price_format.cpp +55 -4
- package/cpp/_core_src/price_format.h +32 -5
- package/cpp/_core_src/price_indicator.cpp +9 -5
- package/cpp/_core_src/price_lines.cpp +9 -4
- package/cpp/_core_src/rsi_pane.cpp +3 -1
- package/cpp/_core_src/theme.cpp +2 -2
- package/cpp/_core_src/viewport.h +32 -0
- package/lib/index.d.mts +27 -0
- package/lib/index.d.ts +27 -0
- package/lib/index.js +102 -5
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +102 -5
- package/lib/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/VroomChart.tsx +129 -5
- package/src/jsi.d.ts +7 -0
- package/src/theme.ts +5 -0
package/src/VroomChart.tsx
CHANGED
|
@@ -212,6 +212,13 @@ export function VroomChart(props: VroomChartProps) {
|
|
|
212
212
|
if (!handle) return undefined;
|
|
213
213
|
const target = chartType === 'line' ? 1 : 0;
|
|
214
214
|
|
|
215
|
+
// Every exit below hands off to maybeStartAnim. Landing in line mode turns
|
|
216
|
+
// tip_pulse_active() on, and the pulse only moves while that loop is
|
|
217
|
+
// requeueing frames — this loop's own clock stops here. Without the handoff
|
|
218
|
+
// the ring sits frozen until some gesture happens to restart the other loop.
|
|
219
|
+
// It no-ops when a frame is already queued or nothing is animating, so
|
|
220
|
+
// landing in candle mode costs nothing.
|
|
221
|
+
|
|
215
222
|
// Fresh handle (first load / recreate): snap, don't animate.
|
|
216
223
|
if (morphHandle.current !== handle || morphFade.current == null) {
|
|
217
224
|
morphHandle.current = handle;
|
|
@@ -219,9 +226,13 @@ export function VroomChart(props: VroomChartProps) {
|
|
|
219
226
|
handle.setChartType(target);
|
|
220
227
|
const p = handle.render();
|
|
221
228
|
if (p) pictureSV.value = p;
|
|
229
|
+
maybeStartAnim();
|
|
230
|
+
return undefined;
|
|
231
|
+
}
|
|
232
|
+
if (morphFade.current === target) {
|
|
233
|
+
maybeStartAnim();
|
|
222
234
|
return undefined;
|
|
223
235
|
}
|
|
224
|
-
if (morphFade.current === target) return undefined;
|
|
225
236
|
|
|
226
237
|
if (morphRaf.current != null) {
|
|
227
238
|
cancelAnimationFrame(morphRaf.current);
|
|
@@ -233,6 +244,7 @@ export function VroomChart(props: VroomChartProps) {
|
|
|
233
244
|
handle.setChartType(target);
|
|
234
245
|
const p = handle.render();
|
|
235
246
|
if (p) pictureSV.value = p;
|
|
247
|
+
maybeStartAnim();
|
|
236
248
|
return undefined;
|
|
237
249
|
}
|
|
238
250
|
|
|
@@ -255,6 +267,7 @@ export function VroomChart(props: VroomChartProps) {
|
|
|
255
267
|
handle.setChartType(target); // lock the exact endpoint
|
|
256
268
|
const q = handle.render();
|
|
257
269
|
if (q) pictureSV.value = q;
|
|
270
|
+
maybeStartAnim();
|
|
258
271
|
}
|
|
259
272
|
};
|
|
260
273
|
morphRaf.current = requestAnimationFrame(step);
|
|
@@ -265,7 +278,10 @@ export function VroomChart(props: VroomChartProps) {
|
|
|
265
278
|
morphRaf.current = null;
|
|
266
279
|
}
|
|
267
280
|
};
|
|
268
|
-
|
|
281
|
+
// maybeStartAnim is memoized on [handle, animTick] and animTick on
|
|
282
|
+
// [handle, pictureSV], both already deps here — so it adds no new restarts
|
|
283
|
+
// of this clock.
|
|
284
|
+
}, [handle, chartType, transitionMs, reduceMotion, pictureSV, maybeStartAnim]);
|
|
269
285
|
|
|
270
286
|
// Volume-bar collapse. The core staggers the bars itself — tallest falling
|
|
271
287
|
// first, all landing together — so unlike the loop above this one hands it
|
|
@@ -281,12 +297,19 @@ export function VroomChart(props: VroomChartProps) {
|
|
|
281
297
|
|
|
282
298
|
// Fresh handle (first load / recreate): the data effect's setVolume already
|
|
283
299
|
// snapped it, so a chart that mounts with bars doesn't animate them in.
|
|
300
|
+
// Every exit below hands off to maybeStartAnim, for the same reason the
|
|
301
|
+
// morph loop does: this clock stops here, and anything the core is still
|
|
302
|
+
// animating (the line-tip pulse) needs the other loop requeueing frames.
|
|
284
303
|
if (volumeHandle.current !== handle || volumeCollapseRef.current == null) {
|
|
285
304
|
volumeHandle.current = handle;
|
|
286
305
|
volumeCollapseRef.current = { t: target, easing };
|
|
306
|
+
maybeStartAnim();
|
|
307
|
+
return undefined;
|
|
308
|
+
}
|
|
309
|
+
if (volumeCollapseRef.current.t === target) {
|
|
310
|
+
maybeStartAnim();
|
|
287
311
|
return undefined;
|
|
288
312
|
}
|
|
289
|
-
if (volumeCollapseRef.current.t === target) return undefined;
|
|
290
313
|
|
|
291
314
|
if (volumeRaf.current != null) {
|
|
292
315
|
cancelAnimationFrame(volumeRaf.current);
|
|
@@ -299,6 +322,7 @@ export function VroomChart(props: VroomChartProps) {
|
|
|
299
322
|
handle.setVolumeCollapse(target, easing);
|
|
300
323
|
const p = handle.render();
|
|
301
324
|
if (p) pictureSV.value = p;
|
|
325
|
+
maybeStartAnim();
|
|
302
326
|
return undefined;
|
|
303
327
|
}
|
|
304
328
|
|
|
@@ -315,7 +339,12 @@ export function VroomChart(props: VroomChartProps) {
|
|
|
315
339
|
handle.setVolumeCollapse(t, kind);
|
|
316
340
|
const p = handle.render();
|
|
317
341
|
if (p) pictureSV.value = p;
|
|
318
|
-
|
|
342
|
+
if (prog < 1) {
|
|
343
|
+
volumeRaf.current = requestAnimationFrame(step);
|
|
344
|
+
} else {
|
|
345
|
+
volumeRaf.current = null;
|
|
346
|
+
maybeStartAnim();
|
|
347
|
+
}
|
|
319
348
|
};
|
|
320
349
|
volumeRaf.current = requestAnimationFrame(step);
|
|
321
350
|
|
|
@@ -325,7 +354,102 @@ export function VroomChart(props: VroomChartProps) {
|
|
|
325
354
|
volumeRaf.current = null;
|
|
326
355
|
}
|
|
327
356
|
};
|
|
328
|
-
}, [
|
|
357
|
+
}, [
|
|
358
|
+
handle,
|
|
359
|
+
volume?.enabled,
|
|
360
|
+
transitionMs,
|
|
361
|
+
reduceMotion,
|
|
362
|
+
pictureSV,
|
|
363
|
+
volumeCollapseRef,
|
|
364
|
+
maybeStartAnim,
|
|
365
|
+
]);
|
|
366
|
+
|
|
367
|
+
// Axis-strip collapse. Unlike the volume bars there is no per-element stagger
|
|
368
|
+
// for the core to distribute, so this pre-eases in JS and hands over the eased
|
|
369
|
+
// scalar. Both axes ride one clock so toggling them together stays in step.
|
|
370
|
+
// This one moves the *layout* — the plot reflows into the reclaimed space
|
|
371
|
+
// every frame. Mirrors the web driver in react/src/useChartCore.ts.
|
|
372
|
+
const axisRaf = useRef<number | null>(null);
|
|
373
|
+
const axisHandle = useRef<typeof handle>(null);
|
|
374
|
+
const axisCollapse = useRef<{ y: number; x: number } | null>(null);
|
|
375
|
+
const showYAxis = theme?.showYAxis ?? true;
|
|
376
|
+
const showXAxis = theme?.showXAxis ?? true;
|
|
377
|
+
useEffect(() => {
|
|
378
|
+
if (!handle) return undefined;
|
|
379
|
+
const targetY = showYAxis ? 0 : 1;
|
|
380
|
+
const targetX = showXAxis ? 0 : 1;
|
|
381
|
+
|
|
382
|
+
// Fresh handle (first load / recreate): snap, so a chart that mounts with an
|
|
383
|
+
// axis already hidden doesn't play it out.
|
|
384
|
+
if (axisHandle.current !== handle || axisCollapse.current == null) {
|
|
385
|
+
axisHandle.current = handle;
|
|
386
|
+
axisCollapse.current = { y: targetY, x: targetX };
|
|
387
|
+
handle.setAxisCollapse(targetY, targetX);
|
|
388
|
+
const p = handle.render();
|
|
389
|
+
if (p) pictureSV.value = p;
|
|
390
|
+
maybeStartAnim();
|
|
391
|
+
return undefined;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
const fromY = axisCollapse.current.y;
|
|
395
|
+
const fromX = axisCollapse.current.x;
|
|
396
|
+
if (fromY === targetY && fromX === targetX) {
|
|
397
|
+
maybeStartAnim();
|
|
398
|
+
return undefined;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
if (axisRaf.current != null) {
|
|
402
|
+
cancelAnimationFrame(axisRaf.current);
|
|
403
|
+
axisRaf.current = null;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
const dur = Math.max(0, transitionMs ?? 300);
|
|
407
|
+
if (dur === 0 || reduceMotion) {
|
|
408
|
+
axisCollapse.current = { y: targetY, x: targetX };
|
|
409
|
+
handle.setAxisCollapse(targetY, targetX);
|
|
410
|
+
const p = handle.render();
|
|
411
|
+
if (p) pictureSV.value = p;
|
|
412
|
+
maybeStartAnim();
|
|
413
|
+
return undefined;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
// From wherever the last frame left off, so toggling mid-flight reverses
|
|
417
|
+
// instead of jumping.
|
|
418
|
+
let startTs: number | null = null;
|
|
419
|
+
const step = (now: number) => {
|
|
420
|
+
if (startTs == null) startTs = now;
|
|
421
|
+
const prog = Math.min(1, (now - startTs) / dur);
|
|
422
|
+
const e = ease(easingRef.current, prog);
|
|
423
|
+
const y = prog < 1 ? fromY + (targetY - fromY) * e : targetY;
|
|
424
|
+
const x = prog < 1 ? fromX + (targetX - fromX) * e : targetX;
|
|
425
|
+
axisCollapse.current = { y, x };
|
|
426
|
+
handle.setAxisCollapse(y, x);
|
|
427
|
+
const p = handle.render();
|
|
428
|
+
if (p) pictureSV.value = p;
|
|
429
|
+
if (prog < 1) {
|
|
430
|
+
axisRaf.current = requestAnimationFrame(step);
|
|
431
|
+
} else {
|
|
432
|
+
axisRaf.current = null;
|
|
433
|
+
maybeStartAnim();
|
|
434
|
+
}
|
|
435
|
+
};
|
|
436
|
+
axisRaf.current = requestAnimationFrame(step);
|
|
437
|
+
|
|
438
|
+
return () => {
|
|
439
|
+
if (axisRaf.current != null) {
|
|
440
|
+
cancelAnimationFrame(axisRaf.current);
|
|
441
|
+
axisRaf.current = null;
|
|
442
|
+
}
|
|
443
|
+
};
|
|
444
|
+
}, [
|
|
445
|
+
handle,
|
|
446
|
+
showYAxis,
|
|
447
|
+
showXAxis,
|
|
448
|
+
transitionMs,
|
|
449
|
+
reduceMotion,
|
|
450
|
+
pictureSV,
|
|
451
|
+
maybeStartAnim,
|
|
452
|
+
]);
|
|
329
453
|
|
|
330
454
|
// Classifies a touch point into the candle area vs. an axis strip. Axis
|
|
331
455
|
// strips always own their gesture (scale price/time) and take priority over
|
package/src/jsi.d.ts
CHANGED
|
@@ -269,6 +269,13 @@ export interface ChartHandle {
|
|
|
269
269
|
* match its `enabled`, so it's only needed while animating.
|
|
270
270
|
*/
|
|
271
271
|
setVolumeCollapse(t: number, easing: number): void;
|
|
272
|
+
/**
|
|
273
|
+
* Collapses the axis strips: 0 leaves a strip at full size, 1 hides it. The
|
|
274
|
+
* plot grows into whatever the strips give up, and their contents fade out
|
|
275
|
+
* over the first half of the collapse so text is never squeezed into a strip
|
|
276
|
+
* too narrow for it. Unlike setVolumeCollapse, both take **eased** progress.
|
|
277
|
+
*/
|
|
278
|
+
setAxisCollapse(yT: number, xT: number): void;
|
|
272
279
|
/**
|
|
273
280
|
* The continuous data coordinate at pixel (x, y) — not snapped to a candle
|
|
274
281
|
* slot. Null when there are no candles or the viewport is degenerate. Cheap to
|
package/src/theme.ts
CHANGED
|
@@ -11,6 +11,7 @@ export const COLOR_KEYS: Partial<Record<keyof VroomTheme, number>> = {
|
|
|
11
11
|
grid: 4, // VROOM_COLOR_GRID
|
|
12
12
|
axisText: 5, // VROOM_COLOR_AXIS_TEXT
|
|
13
13
|
crosshair: 6, // VROOM_COLOR_CROSSHAIR
|
|
14
|
+
badgeText: 8, // VROOM_COLOR_BADGE_TEXT (7 is a retired slot)
|
|
14
15
|
crosshairTarget: 9, // VROOM_COLOR_CROSSHAIR_TARGET
|
|
15
16
|
borderBull: 10, // VROOM_COLOR_BORDER_BULL
|
|
16
17
|
borderBear: 11, // VROOM_COLOR_BORDER_BEAR
|
|
@@ -36,6 +37,10 @@ export const FLOAT_KEYS: Partial<Record<keyof VroomTheme, number>> = {
|
|
|
36
37
|
export const FLOAT_LINE_TIP_PULSE = 15; // VROOM_FLOAT_LINE_TIP_PULSE
|
|
37
38
|
|
|
38
39
|
// Maps each boolean VroomTheme field to its VroomFloatKey index (pushed as 0/1).
|
|
40
|
+
//
|
|
41
|
+
// `showXAxis` / `showYAxis` are absent on purpose: they animate, so VroomChart
|
|
42
|
+
// drives them as a collapse scalar through setAxisCollapse. A float slot here
|
|
43
|
+
// would let this sweep snap them behind the animation's back.
|
|
39
44
|
export const BOOL_KEYS: Partial<Record<keyof VroomTheme, number>> = {
|
|
40
45
|
wickRoundCap: 9, // VROOM_FLOAT_WICK_ROUND_CAP
|
|
41
46
|
lineTipDot: 14, // VROOM_FLOAT_LINE_TIP_DOT
|