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.d.mts
CHANGED
|
@@ -134,7 +134,7 @@ type ChartType = 'candles' | 'line';
|
|
|
134
134
|
*/
|
|
135
135
|
type TransitionEasing = 'linear' | 'ease-in' | 'ease-out' | 'ease-in-out';
|
|
136
136
|
/** Active drawing tool while in `draw` mode. `null` draws nothing. */
|
|
137
|
-
type DrawTool = null | 'line' | 'box' | 'pencil';
|
|
137
|
+
type DrawTool = null | 'line' | 'box' | 'pencil' | 'path';
|
|
138
138
|
/** A drawing anchor in data space, so it stays glued to the candles on pan/zoom. */
|
|
139
139
|
type DrawPoint = {
|
|
140
140
|
/** Anchor time as Unix epoch milliseconds (not snapped to a candle slot). */
|
|
@@ -176,16 +176,27 @@ type PencilDrawing = DrawingBase & {
|
|
|
176
176
|
/** The path's points in draw order (at least 2), in data space. */
|
|
177
177
|
points: DrawPoint[];
|
|
178
178
|
};
|
|
179
|
+
/**
|
|
180
|
+
* A multi-segment path: straight segments through `points`, in order, ending in
|
|
181
|
+
* an arrowhead on the last vertex. Like a pencil stroke it holds a variable
|
|
182
|
+
* number of points, but every one was placed deliberately (one click each), so
|
|
183
|
+
* each is an individually draggable handle once the path is committed.
|
|
184
|
+
*/
|
|
185
|
+
type PathDrawing = DrawingBase & {
|
|
186
|
+
type: 'path';
|
|
187
|
+
/** The path's vertices in draw order (at least 2), in data space. */
|
|
188
|
+
points: DrawPoint[];
|
|
189
|
+
};
|
|
179
190
|
/**
|
|
180
191
|
* A committed drawing. Pass an array of these via the `drawings` prop to render
|
|
181
192
|
* persisted annotations; the chart appends a new one (via `onDrawingComplete`)
|
|
182
193
|
* each time the user finishes drawing.
|
|
183
194
|
*
|
|
184
195
|
* This is a discriminated union on `type` — narrow on it before reading
|
|
185
|
-
* `points[1]`, since
|
|
196
|
+
* `points[1]`, since `'pencil'` and `'path'` have variable-length arrays while
|
|
186
197
|
* `'line'` and `'box'` are always exactly two points.
|
|
187
198
|
*/
|
|
188
|
-
type Drawing = LineDrawing | BoxDrawing | PencilDrawing;
|
|
199
|
+
type Drawing = LineDrawing | BoxDrawing | PencilDrawing | PathDrawing;
|
|
189
200
|
/**
|
|
190
201
|
* Storage adapter for **managed** drawing persistence. Provide it via the
|
|
191
202
|
* `drawingStore` prop and the chart owns the drawings array itself — loading and
|
|
@@ -783,4 +794,35 @@ declare global {
|
|
|
783
794
|
*/
|
|
784
795
|
declare function VroomChart(props: VroomChartProps): React.JSX.Element;
|
|
785
796
|
|
|
786
|
-
|
|
797
|
+
/**
|
|
798
|
+
* How a new `candles` array relates to the one the chart already holds:
|
|
799
|
+
* `'initial'` is the first data, `'stream'` a live update to the same series,
|
|
800
|
+
* `'timeframe'` the same asset re-bucketed into a different interval, and
|
|
801
|
+
* `'reset'` a different series entirely.
|
|
802
|
+
*/
|
|
803
|
+
type DataTransition = 'initial' | 'stream' | 'timeframe' | 'reset';
|
|
804
|
+
/**
|
|
805
|
+
* The candle period in ms, inferred as the median of the first few intervals
|
|
806
|
+
* (robust to a single gap). Null when there are fewer than two candles.
|
|
807
|
+
*/
|
|
808
|
+
declare function inferStepMs(candles: Candle[]): number | null;
|
|
809
|
+
/**
|
|
810
|
+
* Classify a candles-prop change. `prev` is the previously rendered array
|
|
811
|
+
* (null on first render); `seriesKeyChanged` forces `reset` regardless of the
|
|
812
|
+
* data (the explicit escape hatch).
|
|
813
|
+
*
|
|
814
|
+
* Constraint: detection compares two immutable snapshots. An array mutated in
|
|
815
|
+
* place (same reference) never reaches this code — React props must change
|
|
816
|
+
* identity to re-render.
|
|
817
|
+
*/
|
|
818
|
+
declare function classifyTransition(prev: Candle[] | null, next: Candle[], seriesKeyChanged: boolean): DataTransition;
|
|
819
|
+
/**
|
|
820
|
+
* The visible window to apply after a timeframe switch so each candle keeps
|
|
821
|
+
* the exact pixel width it had before: the visible slot count is preserved and
|
|
822
|
+
* the right edge re-anchors on the newest candle (any future-gap overshoot is
|
|
823
|
+
* carried over in slots, clamped to the core's 3/4-window cap). The new start
|
|
824
|
+
* may precede the first candle — that gap is intentional, width wins.
|
|
825
|
+
*/
|
|
826
|
+
declare function timeframeWindow(oldWindow: VisibleRange, oldStepMs: number, oldLastMs: number, newStepMs: number, newLastMs: number): VisibleRange;
|
|
827
|
+
|
|
828
|
+
export { type BollingerBandsConfig, type Candle, type ChartType, type CrosshairEvent, type DataTransition, type MACDConfig, type MAKind, type MASource, type MovingAverageOverlay, type PriceLine, type PriceLinesStyle, type RSIConfig, type TransitionEasing, type VWAPConfig, type VisibleRange, type VolumeConfig, VroomChart, type VroomChartProps, type VroomColor, type VroomTheme, classifyTransition, inferStepMs, timeframeWindow };
|
package/lib/index.d.ts
CHANGED
|
@@ -134,7 +134,7 @@ type ChartType = 'candles' | 'line';
|
|
|
134
134
|
*/
|
|
135
135
|
type TransitionEasing = 'linear' | 'ease-in' | 'ease-out' | 'ease-in-out';
|
|
136
136
|
/** Active drawing tool while in `draw` mode. `null` draws nothing. */
|
|
137
|
-
type DrawTool = null | 'line' | 'box' | 'pencil';
|
|
137
|
+
type DrawTool = null | 'line' | 'box' | 'pencil' | 'path';
|
|
138
138
|
/** A drawing anchor in data space, so it stays glued to the candles on pan/zoom. */
|
|
139
139
|
type DrawPoint = {
|
|
140
140
|
/** Anchor time as Unix epoch milliseconds (not snapped to a candle slot). */
|
|
@@ -176,16 +176,27 @@ type PencilDrawing = DrawingBase & {
|
|
|
176
176
|
/** The path's points in draw order (at least 2), in data space. */
|
|
177
177
|
points: DrawPoint[];
|
|
178
178
|
};
|
|
179
|
+
/**
|
|
180
|
+
* A multi-segment path: straight segments through `points`, in order, ending in
|
|
181
|
+
* an arrowhead on the last vertex. Like a pencil stroke it holds a variable
|
|
182
|
+
* number of points, but every one was placed deliberately (one click each), so
|
|
183
|
+
* each is an individually draggable handle once the path is committed.
|
|
184
|
+
*/
|
|
185
|
+
type PathDrawing = DrawingBase & {
|
|
186
|
+
type: 'path';
|
|
187
|
+
/** The path's vertices in draw order (at least 2), in data space. */
|
|
188
|
+
points: DrawPoint[];
|
|
189
|
+
};
|
|
179
190
|
/**
|
|
180
191
|
* A committed drawing. Pass an array of these via the `drawings` prop to render
|
|
181
192
|
* persisted annotations; the chart appends a new one (via `onDrawingComplete`)
|
|
182
193
|
* each time the user finishes drawing.
|
|
183
194
|
*
|
|
184
195
|
* This is a discriminated union on `type` — narrow on it before reading
|
|
185
|
-
* `points[1]`, since
|
|
196
|
+
* `points[1]`, since `'pencil'` and `'path'` have variable-length arrays while
|
|
186
197
|
* `'line'` and `'box'` are always exactly two points.
|
|
187
198
|
*/
|
|
188
|
-
type Drawing = LineDrawing | BoxDrawing | PencilDrawing;
|
|
199
|
+
type Drawing = LineDrawing | BoxDrawing | PencilDrawing | PathDrawing;
|
|
189
200
|
/**
|
|
190
201
|
* Storage adapter for **managed** drawing persistence. Provide it via the
|
|
191
202
|
* `drawingStore` prop and the chart owns the drawings array itself — loading and
|
|
@@ -783,4 +794,35 @@ declare global {
|
|
|
783
794
|
*/
|
|
784
795
|
declare function VroomChart(props: VroomChartProps): React.JSX.Element;
|
|
785
796
|
|
|
786
|
-
|
|
797
|
+
/**
|
|
798
|
+
* How a new `candles` array relates to the one the chart already holds:
|
|
799
|
+
* `'initial'` is the first data, `'stream'` a live update to the same series,
|
|
800
|
+
* `'timeframe'` the same asset re-bucketed into a different interval, and
|
|
801
|
+
* `'reset'` a different series entirely.
|
|
802
|
+
*/
|
|
803
|
+
type DataTransition = 'initial' | 'stream' | 'timeframe' | 'reset';
|
|
804
|
+
/**
|
|
805
|
+
* The candle period in ms, inferred as the median of the first few intervals
|
|
806
|
+
* (robust to a single gap). Null when there are fewer than two candles.
|
|
807
|
+
*/
|
|
808
|
+
declare function inferStepMs(candles: Candle[]): number | null;
|
|
809
|
+
/**
|
|
810
|
+
* Classify a candles-prop change. `prev` is the previously rendered array
|
|
811
|
+
* (null on first render); `seriesKeyChanged` forces `reset` regardless of the
|
|
812
|
+
* data (the explicit escape hatch).
|
|
813
|
+
*
|
|
814
|
+
* Constraint: detection compares two immutable snapshots. An array mutated in
|
|
815
|
+
* place (same reference) never reaches this code — React props must change
|
|
816
|
+
* identity to re-render.
|
|
817
|
+
*/
|
|
818
|
+
declare function classifyTransition(prev: Candle[] | null, next: Candle[], seriesKeyChanged: boolean): DataTransition;
|
|
819
|
+
/**
|
|
820
|
+
* The visible window to apply after a timeframe switch so each candle keeps
|
|
821
|
+
* the exact pixel width it had before: the visible slot count is preserved and
|
|
822
|
+
* the right edge re-anchors on the newest candle (any future-gap overshoot is
|
|
823
|
+
* carried over in slots, clamped to the core's 3/4-window cap). The new start
|
|
824
|
+
* may precede the first candle — that gap is intentional, width wins.
|
|
825
|
+
*/
|
|
826
|
+
declare function timeframeWindow(oldWindow: VisibleRange, oldStepMs: number, oldLastMs: number, newStepMs: number, newLastMs: number): VisibleRange;
|
|
827
|
+
|
|
828
|
+
export { type BollingerBandsConfig, type Candle, type ChartType, type CrosshairEvent, type DataTransition, type MACDConfig, type MAKind, type MASource, type MovingAverageOverlay, type PriceLine, type PriceLinesStyle, type RSIConfig, type TransitionEasing, type VWAPConfig, type VisibleRange, type VolumeConfig, VroomChart, type VroomChartProps, type VroomColor, type VroomTheme, classifyTransition, inferStepMs, timeframeWindow };
|
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) {
|
|
@@ -282,15 +365,53 @@ function ensureInstalled() {
|
|
|
282
365
|
}
|
|
283
366
|
installed = true;
|
|
284
367
|
}
|
|
285
|
-
function useChartCore(candles, size, visibleRange, defaultCandleWidth, chartType, theme, rsi, macd, movingAverages, vwap, bollingerBands, volume, priceLines) {
|
|
368
|
+
function useChartCore(candles, size, visibleRange, defaultCandleWidth, chartType, theme, rsi, macd, movingAverages, vwap, bollingerBands, volume, priceLines, transition) {
|
|
286
369
|
const handleRef = (0, import_react.useRef)(null);
|
|
287
370
|
const defaultWidthAppliedRef = (0, import_react.useRef)(false);
|
|
288
371
|
const volumeCollapseRef = (0, import_react.useRef)(null);
|
|
372
|
+
const prevDataRef = (0, import_react.useRef)(null);
|
|
373
|
+
const intervalMorphRaf = (0, import_react.useRef)(null);
|
|
289
374
|
const [picture, setPicture] = (0, import_react.useState)(null);
|
|
290
375
|
if (!handleRef.current && size.width > 0 && size.height > 0) {
|
|
291
376
|
ensureInstalled();
|
|
292
377
|
handleRef.current = globalThis.VroomChartJSI.create();
|
|
293
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
|
+
}, []);
|
|
294
415
|
const explicit = visibleRange != null;
|
|
295
416
|
const startMs = visibleRange?.startMs ?? 0;
|
|
296
417
|
const endMs = visibleRange?.endMs ?? 0;
|
|
@@ -310,8 +431,54 @@ function useChartCore(candles, size, visibleRange, defaultCandleWidth, chartType
|
|
|
310
431
|
h.setDefaultCandleWidth(defaultCandleWidth);
|
|
311
432
|
defaultWidthAppliedRef.current = true;
|
|
312
433
|
}
|
|
434
|
+
let morphing = false;
|
|
313
435
|
if (candles.length > 0) {
|
|
314
|
-
|
|
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
|
+
}
|
|
315
482
|
}
|
|
316
483
|
if (explicit) {
|
|
317
484
|
h.setVisibleRange(startMs, endMs);
|
|
@@ -330,39 +497,16 @@ function useChartCore(candles, size, visibleRange, defaultCandleWidth, chartType
|
|
|
330
497
|
h.setPriceLines(
|
|
331
498
|
priceLines?.lines.length ? priceLinesToSpec(priceLines) : EMPTY_PRICE_LINES
|
|
332
499
|
);
|
|
333
|
-
setPicture(h.render());
|
|
334
|
-
}, [candles, size.width, size.height, size.pxRatio, explicit, startMs, endMs, defaultCandleWidth, themeKey, rsiKey, macdKey, maKey, vwapKey, bollingerKey, volumeKey, priceLinesKey]);
|
|
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]);
|
|
335
502
|
return { handle: handleRef.current, picture, volumeCollapseRef };
|
|
336
503
|
}
|
|
337
504
|
|
|
338
|
-
// src/easing.ts
|
|
339
|
-
function ease(kind, p) {
|
|
340
|
-
switch (kind) {
|
|
341
|
-
case "linear":
|
|
342
|
-
return p;
|
|
343
|
-
case "ease-in":
|
|
344
|
-
return p * p;
|
|
345
|
-
case "ease-out":
|
|
346
|
-
return p * (2 - p);
|
|
347
|
-
default:
|
|
348
|
-
return p * p * (3 - 2 * p);
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
var EASINGS = [
|
|
352
|
-
"linear",
|
|
353
|
-
"ease-in",
|
|
354
|
-
"ease-out",
|
|
355
|
-
"ease-in-out"
|
|
356
|
-
];
|
|
357
|
-
function easingIndex(kind) {
|
|
358
|
-
const i = kind ? EASINGS.indexOf(kind) : -1;
|
|
359
|
-
return i < 0 ? EASINGS.indexOf("ease-in-out") : i;
|
|
360
|
-
}
|
|
361
|
-
|
|
362
505
|
// src/VroomChart.tsx
|
|
363
506
|
function VroomChart(props) {
|
|
364
507
|
const {
|
|
365
508
|
candles,
|
|
509
|
+
seriesKey,
|
|
366
510
|
width: widthProp,
|
|
367
511
|
height: heightProp,
|
|
368
512
|
style,
|
|
@@ -405,6 +549,19 @@ function VroomChart(props) {
|
|
|
405
549
|
} : void 0,
|
|
406
550
|
[priceLines, priceLinesStyle, onPriceLineClose]
|
|
407
551
|
);
|
|
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
|
+
);
|
|
408
565
|
const { handle, picture, volumeCollapseRef } = useChartCore(
|
|
409
566
|
candles,
|
|
410
567
|
{ width, height },
|
|
@@ -418,14 +575,9 @@ function VroomChart(props) {
|
|
|
418
575
|
vwap,
|
|
419
576
|
bollingerBands,
|
|
420
577
|
volume,
|
|
421
|
-
priceLinesProp
|
|
578
|
+
priceLinesProp,
|
|
579
|
+
{ seriesKey, transitionMs, transitionEasing, reduceMotion, onFrame }
|
|
422
580
|
);
|
|
423
|
-
const emptyPicture = (0, import_react2.useMemo)(() => {
|
|
424
|
-
const rec = import_react_native_skia.Skia.PictureRecorder();
|
|
425
|
-
rec.beginRecording(import_react_native_skia.Skia.XYWHRect(0, 0, 1, 1));
|
|
426
|
-
return rec.finishRecordingAsPicture();
|
|
427
|
-
}, []);
|
|
428
|
-
const pictureSV = (0, import_react_native_reanimated.useSharedValue)(emptyPicture);
|
|
429
581
|
const crosshairActive = (0, import_react2.useRef)(false);
|
|
430
582
|
const lastCrosshairTime = (0, import_react2.useRef)(null);
|
|
431
583
|
(0, import_react2.useEffect)(() => {
|
|
@@ -498,7 +650,7 @@ function VroomChart(props) {
|
|
|
498
650
|
const prog = Math.min(1, (now - startTs) / dur);
|
|
499
651
|
const fade = from + (target - from) * ease(easingRef.current, prog);
|
|
500
652
|
morphFade.current = fade;
|
|
501
|
-
handle.setMorph(fade, fade);
|
|
653
|
+
handle.setMorph(reduceMotion ? 0 : fade, fade);
|
|
502
654
|
const p = handle.render();
|
|
503
655
|
if (p) pictureSV.value = p;
|
|
504
656
|
if (prog < 1) {
|
|
@@ -518,7 +670,7 @@ function VroomChart(props) {
|
|
|
518
670
|
morphRaf.current = null;
|
|
519
671
|
}
|
|
520
672
|
};
|
|
521
|
-
}, [handle, chartType, transitionMs, pictureSV]);
|
|
673
|
+
}, [handle, chartType, transitionMs, reduceMotion, pictureSV]);
|
|
522
674
|
const volumeRaf = (0, import_react2.useRef)(null);
|
|
523
675
|
const volumeHandle = (0, import_react2.useRef)(null);
|
|
524
676
|
(0, import_react2.useEffect)(() => {
|
|
@@ -536,7 +688,7 @@ function VroomChart(props) {
|
|
|
536
688
|
volumeRaf.current = null;
|
|
537
689
|
}
|
|
538
690
|
const dur = Math.max(0, transitionMs ?? 300);
|
|
539
|
-
if (dur === 0) {
|
|
691
|
+
if (dur === 0 || reduceMotion) {
|
|
540
692
|
volumeCollapseRef.current = { t: target, easing };
|
|
541
693
|
handle.setVolumeCollapse(target, easing);
|
|
542
694
|
const p = handle.render();
|
|
@@ -563,7 +715,7 @@ function VroomChart(props) {
|
|
|
563
715
|
volumeRaf.current = null;
|
|
564
716
|
}
|
|
565
717
|
};
|
|
566
|
-
}, [handle, volume?.enabled, transitionMs, pictureSV, volumeCollapseRef]);
|
|
718
|
+
}, [handle, volume?.enabled, transitionMs, reduceMotion, pictureSV, volumeCollapseRef]);
|
|
567
719
|
const hitAxis = (0, import_react2.useCallback)(
|
|
568
720
|
(x, y) => {
|
|
569
721
|
if (!handle) return "chart";
|
|
@@ -775,6 +927,9 @@ function VroomChart(props) {
|
|
|
775
927
|
}
|
|
776
928
|
// Annotate the CommonJS export names for ESM import in node:
|
|
777
929
|
0 && (module.exports = {
|
|
778
|
-
VroomChart
|
|
930
|
+
VroomChart,
|
|
931
|
+
classifyTransition,
|
|
932
|
+
inferStepMs,
|
|
933
|
+
timeframeWindow
|
|
779
934
|
});
|
|
780
935
|
//# sourceMappingURL=index.js.map
|