react-native-vroom-chart 0.14.0 → 0.16.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.
Files changed (47) hide show
  1. package/cpp/VroomChartHostObject.cpp +289 -2
  2. package/cpp/_core_include/vroom/vroom_chart.h +221 -4
  3. package/cpp/_core_src/atr.cpp +67 -0
  4. package/cpp/_core_src/atr.h +44 -0
  5. package/cpp/_core_src/atr_pane.cpp +162 -0
  6. package/cpp/_core_src/atr_pane.h +48 -0
  7. package/cpp/_core_src/chart.cpp +477 -172
  8. package/cpp/_core_src/chart.h +173 -1
  9. package/cpp/_core_src/chart_facade.cpp +317 -23
  10. package/cpp/_core_src/fair_value_gaps.cpp +76 -0
  11. package/cpp/_core_src/fair_value_gaps.h +54 -0
  12. package/cpp/_core_src/footprints.cpp +226 -0
  13. package/cpp/_core_src/footprints.h +45 -0
  14. package/cpp/_core_src/footprints_layout.cpp +140 -0
  15. package/cpp/_core_src/footprints_layout.h +94 -0
  16. package/cpp/_core_src/fvg_overlay.cpp +262 -0
  17. package/cpp/_core_src/fvg_overlay.h +43 -0
  18. package/cpp/_core_src/ichimoku.cpp +65 -0
  19. package/cpp/_core_src/ichimoku.h +45 -0
  20. package/cpp/_core_src/labels.cpp +3 -0
  21. package/cpp/_core_src/line_morph.h +159 -0
  22. package/cpp/_core_src/ma_overlay.cpp +259 -36
  23. package/cpp/_core_src/ma_overlay.h +57 -2
  24. package/cpp/_core_src/macd.cpp +24 -1
  25. package/cpp/_core_src/macd.h +16 -0
  26. package/cpp/_core_src/macd_pane.cpp +71 -62
  27. package/cpp/_core_src/macd_pane.h +13 -1
  28. package/cpp/_core_src/pane_series.h +169 -0
  29. package/cpp/_core_src/rsi.cpp +4 -0
  30. package/cpp/_core_src/rsi.h +7 -0
  31. package/cpp/_core_src/rsi_pane.cpp +85 -29
  32. package/cpp/_core_src/rsi_pane.h +11 -1
  33. package/cpp/_core_src/tip_pulse.h +4 -4
  34. package/cpp/_core_src/viewport.h +35 -0
  35. package/lib/index.d.mts +385 -3
  36. package/lib/index.d.ts +385 -3
  37. package/lib/index.js +305 -7
  38. package/lib/index.js.map +1 -1
  39. package/lib/index.mjs +305 -7
  40. package/lib/index.mjs.map +1 -1
  41. package/package.json +1 -1
  42. package/src/VroomChart.tsx +108 -7
  43. package/src/dataTransitions.ts +47 -0
  44. package/src/index.ts +10 -0
  45. package/src/jsi.d.ts +136 -1
  46. package/src/types.ts +10 -0
  47. package/src/useChartCore.ts +330 -5
@@ -36,9 +36,12 @@ import { useReducedMotion, useSharedValue } from 'react-native-reanimated';
36
36
  import { useChartCore } from './useChartCore';
37
37
  import { ease, easingIndex } from './easing';
38
38
  import type { ChartFrame } from './jsi.d';
39
- import type { VroomChartProps } from './types';
39
+ import type { Footprint, VroomChartProps } from './types';
40
40
  import './jsi.d';
41
41
 
42
+ // Mirrors VroomFootprintSide in packages/core/include/vroom/vroom_chart.h.
43
+ const FOOTPRINT_SELL = 1;
44
+
42
45
  function isSkImage(frame: ChartFrame): frame is SkImage {
43
46
  return typeof (frame as SkImage).getImageInfo === 'function';
44
47
  }
@@ -47,8 +50,9 @@ function isSkImage(frame: ChartFrame): frame is SkImage {
47
50
  * Skia-rendered candlestick chart. Pass OHLCV `candles` and size it via `style`
48
51
  * (it fills its parent by default). Pan to scroll, pinch to zoom, drag the
49
52
  * price/time axes to rescale, and long-press for the crosshair. Optional
50
- * indicators (`rsi`, `macd`, `movingAverages`, `vwap`), colors (`theme`), and
51
- * events (`onCrosshair`, `onViewportChange`) are configured through props.
53
+ * indicators (`rsi`, `macd`, `movingAverages`, `vwap`, `bollingerBands`,
54
+ * `ichimoku`, and more), colors (`theme`), and events (`onCrosshair`,
55
+ * `onViewportChange`) are configured through props.
52
56
  *
53
57
  * @see {@link VroomChartProps} for the full prop reference.
54
58
  */
@@ -65,12 +69,17 @@ export function VroomChart(props: VroomChartProps) {
65
69
  transitionMs,
66
70
  transitionEasing,
67
71
  intervalTransition,
72
+ streamTransition,
73
+ streamTransitionMs,
68
74
  theme,
69
75
  rsi,
70
76
  macd,
77
+ atr,
71
78
  movingAverages,
72
79
  vwap,
73
80
  bollingerBands,
81
+ ichimoku,
82
+ fairValueGaps,
74
83
  volume,
75
84
  crosshairOffset = 40,
76
85
  onCrosshair,
@@ -80,6 +89,9 @@ export function VroomChart(props: VroomChartProps) {
80
89
  onPriceLineDrag,
81
90
  onPriceLineDragEnd,
82
91
  onPriceLineClose,
92
+ footprints,
93
+ footprintsStyle,
94
+ onFootprint,
83
95
  } = props;
84
96
 
85
97
  // Fill the parent by default: measure via onLayout. Explicit width/height
@@ -111,6 +123,11 @@ export function VroomChart(props: VroomChartProps) {
111
123
  [priceLines, priceLinesStyle, onPriceLineClose],
112
124
  );
113
125
 
126
+ const footprintsProp = useMemo(
127
+ () => (footprints ? { prints: footprints, style: footprintsStyle } : undefined),
128
+ [footprints, footprintsStyle],
129
+ );
130
+
114
131
  // RN-Skia's recorder reads these SharedValues on the UI/render runtime, a
115
132
  // beat behind JS-thread writes. If it ever reads null it throws ("Invalid
116
133
  // prop value for SkTextBlob received" — RN-Skia's mislabeled SkPicture
@@ -168,12 +185,25 @@ export function VroomChart(props: VroomChartProps) {
168
185
  theme,
169
186
  rsi,
170
187
  macd,
188
+ atr,
171
189
  movingAverages,
172
190
  vwap,
173
191
  bollingerBands,
192
+ ichimoku,
193
+ fairValueGaps,
174
194
  volume,
175
195
  priceLinesProp,
176
- { seriesKey, transitionMs, transitionEasing, intervalTransition, reduceMotion, onFrame },
196
+ footprintsProp,
197
+ {
198
+ seriesKey,
199
+ transitionMs,
200
+ transitionEasing,
201
+ intervalTransition,
202
+ streamTransition,
203
+ streamTransitionMs,
204
+ reduceMotion,
205
+ onFrame,
206
+ },
177
207
  );
178
208
 
179
209
  // When the crosshair is showing, pan moves it (instead of scrolling) and
@@ -181,6 +211,11 @@ export function VroomChart(props: VroomChartProps) {
181
211
  // synchronously without re-subscribing. Tap dismisses it.
182
212
  const crosshairActive = useRef(false);
183
213
 
214
+ // Whether a footprint badge is currently open, so a tap that misses every badge
215
+ // knows whether it has a tooltip to dismiss. A ref for the same reason as
216
+ // crosshairActive: gesture callbacks read it synchronously.
217
+ const footprintActive = useRef(false);
218
+
184
219
  // timeMs of the candle last reported through onCrosshair, so a drag fires a
185
220
  // 'move' event only when it crosses into a *different* candle (one per
186
221
  // candle, not per frame). Null while the crosshair is hidden.
@@ -539,6 +574,31 @@ export function VroomChart(props: VroomChartProps) {
539
574
  'chart' | 'price-axis' | 'time-axis' | 'indicator' | 'price-line'
540
575
  >('chart');
541
576
 
577
+ // Closes an open footprint tooltip. Any viewport change slides the candles out
578
+ // from under it and the crosshair replaces it outright, so the host is told to
579
+ // take it down rather than left holding a position the badge has moved away
580
+ // from. `redraw` is false for callers that render a frame of their own right
581
+ // after — on Android that render rasterizes pixels, so the duplicate is worth
582
+ // skipping.
583
+ const dismissFootprint = (redraw = true) => {
584
+ if (!handle || !footprintActive.current) return;
585
+ footprintActive.current = false;
586
+ handle.setFootprintHover(0, -1);
587
+ if (redraw) {
588
+ const frame = handle.render();
589
+ if (frame) applyFrame(frame);
590
+ }
591
+ onFootprint?.({
592
+ active: false,
593
+ reason: 'hide',
594
+ side: null,
595
+ timeMs: null,
596
+ footprints: [],
597
+ badge: null,
598
+ pane: null,
599
+ });
600
+ };
601
+
542
602
  const pan = Gesture.Pan()
543
603
  .runOnJS(true)
544
604
  .maxPointers(1) // don't fight Pinch's two-finger gesture
@@ -561,6 +621,10 @@ export function VroomChart(props: VroomChartProps) {
561
621
  if (p) applyFrame(p);
562
622
  }
563
623
  }
624
+ // Every mode but a price-line drag moves the viewport, and this one call
625
+ // covers the momentum fling too — decay only ever starts from a pan that
626
+ // already began here.
627
+ if (panMode.current !== 'price-line') dismissFootprint();
564
628
  })
565
629
  .onChange((e) => {
566
630
  if (!handle) return;
@@ -684,6 +748,7 @@ export function VroomChart(props: VroomChartProps) {
684
748
  .runOnJS(true)
685
749
  .onTouchesDown((e) => {
686
750
  if (e.numberOfTouches < 2) return;
751
+ dismissFootprint();
687
752
  const [a, b] = e.allTouches;
688
753
  const spanX = Math.abs(a.x - b.x);
689
754
  const spanY = Math.abs(a.y - b.y);
@@ -739,6 +804,10 @@ export function VroomChart(props: VroomChartProps) {
739
804
  // close button — so it must not raise the crosshair over the top.
740
805
  if (hitPriceLine(e.x, e.y)) return;
741
806
  cancelDecay();
807
+ // The crosshair takes the pane over, so it can't share it with a tooltip.
808
+ // No redraw: setCrosshair below returns a frame that already has the badge
809
+ // un-highlighted.
810
+ dismissFootprint(false);
742
811
  crosshairActive.current = true;
743
812
  const ch = handle.setCrosshair(e.x, e.y - crosshairOffset);
744
813
  if (ch) applyFrame(ch);
@@ -753,13 +822,45 @@ export function VroomChart(props: VroomChartProps) {
753
822
  });
754
823
  });
755
824
 
756
- // A tap activates a price line's close button, and otherwise dismisses the
757
- // crosshair while it's up. Any other tap is a no-op, so it never interferes
758
- // with normal pan/pinch.
825
+ // A tap activates a price line's close button, selects or dismisses a footprint
826
+ // badge, and otherwise dismisses the crosshair while it's up. Any other tap is a
827
+ // no-op, so it never interferes with normal pan/pinch.
759
828
  const tap = Gesture.Tap()
760
829
  .runOnJS(true)
761
830
  .onStart((e) => {
762
831
  if (!handle) return;
832
+
833
+ // Badges get first refusal: one is a ~9px circle, while a price line's grab
834
+ // band spans the pane and would otherwise swallow any badge it crosses.
835
+ // Touch has no hover, so a tap is what opens a footprint here, and the next
836
+ // tap anywhere closes it.
837
+ const prints = footprints ?? [];
838
+ const fp = prints.length ? handle.hitTestFootprint(e.x, e.y) : null;
839
+ if (fp) {
840
+ handle.setFootprintHover(fp.candleTimeMs, fp.side);
841
+ const frame = handle.render();
842
+ if (frame) applyFrame(frame);
843
+ const wasActive = footprintActive.current;
844
+ footprintActive.current = true;
845
+ onFootprint?.({
846
+ active: true,
847
+ reason: wasActive ? 'move' : 'show',
848
+ side: fp.side === FOOTPRINT_SELL ? 'sell' : 'buy',
849
+ timeMs: fp.candleTimeMs,
850
+ // The core reports indices into the array we last pushed, which is this
851
+ // same prop — so this rejoins each badge to the consumer's own objects.
852
+ footprints: fp.indices
853
+ .map((i) => prints[i])
854
+ .filter((f): f is Footprint => f != null),
855
+ badge: { x: fp.x, y: fp.y, radius: fp.radius },
856
+ pane: fp.pane,
857
+ });
858
+ return;
859
+ }
860
+ // A tap that missed every badge dismisses the open one, so the host tooltip
861
+ // goes away the same way the crosshair does.
862
+ dismissFootprint();
863
+
763
864
  // The close button is a tap target whether or not the crosshair is up.
764
865
  const pl = hitPriceLine(e.x, e.y);
765
866
  if (pl && pl.part === 1) {
@@ -126,6 +126,53 @@ export function classifyTransition(
126
126
  return closeRatio <= MAX_SAME_ASSET_CLOSE_RATIO && endsTogether ? 'timeframe' : 'reset';
127
127
  }
128
128
 
129
+ /**
130
+ * What a `'stream'` update did to the series: `'tick'` revised the bar already
131
+ * on screen, `'append'` brought at least one new one.
132
+ *
133
+ * The two animate by different means. A tick keeps the bar count, so the morph
134
+ * capture's slots still pair one-to-one and the last bar can reshape in place.
135
+ * An append can't use that capture at all — slots pair from the right edge, so
136
+ * a new bar shifts every candle onto its neighbour's geometry — and instead
137
+ * advances the visible window, which translates the series left and lets the
138
+ * new bar in at the right edge.
139
+ */
140
+ export type StreamKind = 'tick' | 'append';
141
+
142
+ /**
143
+ * Which of the two a `'stream'` transition is. Read from the newest timestamp
144
+ * rather than a length comparison, so a rolling buffer that drops a bar from
145
+ * the front as it adds one to the back still reads as an append.
146
+ *
147
+ * An update that both appends and revises the bar that just closed counts as an
148
+ * append: the translation is the dominant motion, and the revision is a final
149
+ * print that has nowhere to slot-pair to.
150
+ */
151
+ export function classifyStream(prev: Candle[], next: Candle[]): StreamKind {
152
+ if (prev.length === 0 || next.length === 0) return 'tick';
153
+ return next[next.length - 1].timeMs > prev[prev.length - 1].timeMs
154
+ ? 'append'
155
+ : 'tick';
156
+ }
157
+
158
+ /**
159
+ * Whether the view is still following the newest bar, which is what decides if
160
+ * an appended bar should pull the window along with it.
161
+ *
162
+ * True when the right edge sits at or past the newest bar's slot *end* — where
163
+ * the default framing leaves it, plus whatever gap it reserved. Someone who has
164
+ * panned back into history falls below that and is left where they are: nothing
165
+ * is more disorienting than the chart walking out from under you while you read
166
+ * it.
167
+ */
168
+ export function isPinnedToLatest(
169
+ window: VisibleRange,
170
+ lastMs: number,
171
+ stepMs: number,
172
+ ): boolean {
173
+ return window.endMs >= lastMs + stepMs;
174
+ }
175
+
129
176
  /**
130
177
  * The visible window to apply after a timeframe switch so each candle keeps
131
178
  * the exact pixel width it had before: the visible slot count is preserved and
package/src/index.ts CHANGED
@@ -14,16 +14,26 @@ export type {
14
14
  VisibleRange,
15
15
  RSIConfig,
16
16
  MACDConfig,
17
+ ATRConfig,
18
+ ATRSmoothing,
17
19
  MASource,
18
20
  MAKind,
19
21
  MovingAverageOverlay,
20
22
  VWAPConfig,
21
23
  BollingerBandsConfig,
24
+ IchimokuConfig,
25
+ FairValueGapsConfig,
22
26
  VolumeConfig,
23
27
  ChartType,
24
28
  TransitionEasing,
25
29
  IntervalTransition,
30
+ StreamTransition,
26
31
  PriceLine,
27
32
  PriceLinesStyle,
33
+ Footprint,
34
+ FootprintSide,
35
+ FootprintsStyle,
36
+ FootprintEvent,
37
+ PlotRect,
28
38
  DefaultDrawingStyle,
29
39
  } from './types';
package/src/jsi.d.ts CHANGED
@@ -73,7 +73,20 @@ export interface ChartHandle {
73
73
  */
74
74
  beginIntervalMorph(mode?: IntervalTransition): void;
75
75
  /**
76
- * Advance the interval morph started by beginIntervalMorph. `t` (clamped to
76
+ * Capture the visible geometry so the next setCandles can ease a live tick
77
+ * into place. Always a transform, and unlike beginIntervalMorph it leaves the
78
+ * axes alone — the interval hasn't changed, so its ticks must not fade.
79
+ * Restarting one still in flight continues from the shape on screen, so ticks
80
+ * arriving faster than the animation lands stay smooth. Call before
81
+ * setCandles, then drive setIntervalMorph from 0 to 1.
82
+ *
83
+ * Not for an update that appends a bar: slots pair from the right edge, so a
84
+ * new bar would shift every candle onto its neighbour's geometry. Advance the
85
+ * visible range instead and let the series translate.
86
+ */
87
+ beginStreamMorph(): void;
88
+ /**
89
+ * Advance the morph started by either begin method above. `t` (clamped to
77
90
  * 0..1) is the eased progress: 0 renders the captured geometry pixel-
78
91
  * identically to the pre-swap frame, 1 renders the new candles and releases
79
92
  * the capture. Driven per-frame by the host animation loop.
@@ -173,6 +186,7 @@ export interface ChartHandle {
173
186
  maWidth: number;
174
187
  bandColor: number;
175
188
  bandsVisible: boolean;
189
+ extremeFill: boolean;
176
190
  }): void;
177
191
  /**
178
192
  * Configures the MACD pane. source/maKind mirror setOverlays' encodings;
@@ -201,6 +215,18 @@ export interface ChartHandle {
201
215
  zeroColor: number;
202
216
  zeroVisible: boolean;
203
217
  }): void;
218
+ /**
219
+ * Configures the ATR pane. smoothing: 0=RMA (Wilder), 1=SMA, 2=EMA;
220
+ * lineColor is packed 0xAARRGGBB where 0 means inherit, and a non-positive
221
+ * width inherits the default stroke.
222
+ */
223
+ setATR(spec: {
224
+ enabled: boolean;
225
+ period: number;
226
+ smoothing: number;
227
+ lineColor: number;
228
+ lineWidth: number;
229
+ }): void;
204
230
  /**
205
231
  * Replaces the full set of MA/EMA overlay lines drawn on the price pane.
206
232
  * kind: 0=SMA, 1=EMA; source: 0=close,1=open,2=high,3=low,4=hl2,5=hlc3,6=ohlc4;
@@ -245,6 +271,76 @@ export interface ChartHandle {
245
271
  fillEnabled: boolean;
246
272
  fillOpacity: number;
247
273
  }): void;
274
+ /**
275
+ * Configures the Ichimoku overlay (five price-pane lines + the cloud between
276
+ * the leading spans). Colors are packed 0xAARRGGBB; cloudOpacity is 0..1.
277
+ *
278
+ * `displacement` is in candle slots and applies at draw time: the leading
279
+ * spans plot that many slots ahead of the bar they came from, past the newest
280
+ * candle, and chikou that many behind. Enabling the overlay also pulls the
281
+ * view forward far enough to show them.
282
+ */
283
+ setIchimoku(spec: {
284
+ enabled: boolean;
285
+ tenkanPeriod: number;
286
+ kijunPeriod: number;
287
+ senkouBPeriod: number;
288
+ displacement: number;
289
+ tenkanColor: number;
290
+ tenkanWidth: number;
291
+ tenkanEnabled: boolean;
292
+ kijunColor: number;
293
+ kijunWidth: number;
294
+ kijunEnabled: boolean;
295
+ senkouAColor: number;
296
+ senkouAWidth: number;
297
+ senkouAEnabled: boolean;
298
+ senkouBColor: number;
299
+ senkouBWidth: number;
300
+ senkouBEnabled: boolean;
301
+ chikouColor: number;
302
+ chikouWidth: number;
303
+ chikouEnabled: boolean;
304
+ cloudEnabled: boolean;
305
+ bullishCloudColor: number;
306
+ bearishCloudColor: number;
307
+ cloudOpacity: number;
308
+ }): void;
309
+ /**
310
+ * Configures the Fair Value Gap overlay (shaded imbalance boxes on the price
311
+ * pane). Colors are packed 0xAARRGGBB; opacity is 0..1.
312
+ *
313
+ * `maxBarsBack`, `boxLength` and `labelDistance` are all counted in candle
314
+ * slots. `fillType` is 0 for a close past the far edge or 1 for a wick
315
+ * reaching it, and `borderStyle` is 0 solid / 1 dotted / 2 dashed. Only
316
+ * enabled, maxBarsBack, waitForClose and fillType rescan for gaps.
317
+ */
318
+ setFairValueGaps(spec: {
319
+ enabled: boolean;
320
+ maxBarsBack: number;
321
+ waitForClose: boolean;
322
+ fillType: number;
323
+ deleteAfterFill: boolean;
324
+ extendBoxes: boolean;
325
+ boxLength: number;
326
+ bullishColor: number;
327
+ bearishColor: number;
328
+ opacity: number;
329
+ borderEnabled: boolean;
330
+ borderStyle: number;
331
+ borderWidth: number;
332
+ bullishBorderColor: number;
333
+ bearishBorderColor: number;
334
+ labelsEnabled: boolean;
335
+ label: string;
336
+ labelDistance: number;
337
+ labelColor: number;
338
+ labelFontSize: number;
339
+ showInverse: boolean;
340
+ inverseBullishColor: number;
341
+ inverseBearishColor: number;
342
+ inverseLabel: string;
343
+ }): void;
248
344
  /**
249
345
  * Configures the volume bars under the candles. `heightFrac` is the tallest
250
346
  * bar as a fraction of the price pane. The style fields carry an inherit
@@ -325,6 +421,45 @@ export interface ChartHandle {
325
421
  * committed price is untouched — restate setPriceLines to apply the move.
326
422
  */
327
423
  setPriceLineDrag(index: number, price: number): void;
424
+ /**
425
+ * Replaces the full set of footprints (plus their shared style). `side` is
426
+ * 0=buy, 1=sell; `timeMs` is the raw execution time — the core buckets each
427
+ * trade onto whichever candle's window contains it and regroups whenever the
428
+ * candles change. Geometry fields at 0 take the core's defaults. Pass an empty
429
+ * `prints` array to clear.
430
+ */
431
+ setFootprints(spec: {
432
+ prints: { timeMs: number; side: number }[];
433
+ radiusPx: number;
434
+ gapPx: number;
435
+ marginPx: number;
436
+ hoverBoost: number;
437
+ }): void;
438
+ /**
439
+ * Hit-tests pixel (x, y) against the footprint badges; null on a miss, nearest
440
+ * center wins when two overlap. `indices` addresses the array last passed to
441
+ * setFootprints and covers *both* sides of that candle, ascending by time, so
442
+ * one call is enough to fill a tooltip; `pane` is the plot rect, for deciding
443
+ * which side of the badge that tooltip fits on. Cheap to call at gesture rate.
444
+ */
445
+ hitTestFootprint(
446
+ x: number,
447
+ y: number,
448
+ ): {
449
+ side: number;
450
+ candleTimeMs: number;
451
+ x: number;
452
+ y: number;
453
+ radius: number;
454
+ pane: { left: number; top: number; right: number; bottom: number };
455
+ indices: number[];
456
+ } | null;
457
+ /**
458
+ * Marks a footprint badge as hovered so it renders highlighted; side -1 clears.
459
+ * The arguments match hitTestFootprint. Touch has no hover, so on RN this
460
+ * tracks the badge the user last tapped.
461
+ */
462
+ setFootprintHover(candleTimeMs: number, side: number): void;
328
463
  /** True while any axis-label fade is still in progress. Drives a RAF loop. */
329
464
  isAnimating(): boolean;
330
465
  render(): ChartFrame | null;
package/src/types.ts CHANGED
@@ -16,13 +16,23 @@ export type {
16
16
  MovingAverageOverlay,
17
17
  VWAPConfig,
18
18
  BollingerBandsConfig,
19
+ IchimokuConfig,
20
+ FairValueGapsConfig,
19
21
  VolumeConfig,
20
22
  MACDConfig,
23
+ ATRConfig,
24
+ ATRSmoothing,
21
25
  ChartType,
22
26
  TransitionEasing,
23
27
  IntervalTransition,
28
+ StreamTransition,
24
29
  PriceLine,
25
30
  PriceLinesStyle,
31
+ Footprint,
32
+ FootprintSide,
33
+ FootprintsStyle,
34
+ FootprintEvent,
35
+ PlotRect,
26
36
  DefaultDrawingStyle,
27
37
  } from '@vroomchart/types';
28
38