react-native-vroom-chart 0.5.0 → 0.6.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/src/jsi.d.ts CHANGED
@@ -162,6 +162,54 @@ export interface ChartHandle {
162
162
  fillEnabled: boolean;
163
163
  fillOpacity: number;
164
164
  }): void;
165
+ /**
166
+ * The continuous data coordinate at pixel (x, y) — not snapped to a candle
167
+ * slot. Null when there are no candles or the viewport is degenerate. Cheap to
168
+ * call at gesture rate (no rendering).
169
+ */
170
+ coordAt(x: number, y: number): { timeMs: number; price: number } | null;
171
+ /**
172
+ * Replaces the full set of price status lines (plus their shared style).
173
+ * lineStyle: 0=solid,1=dotted,2=dashed; color/bodyBg are packed 0xAARRGGBB;
174
+ * flags is a bitmask (1=draggable, 2=closable, 4=axis label, 8=extend left);
175
+ * align: 0=left,1=center,2=right. Pass an empty `lines` array to clear.
176
+ */
177
+ setPriceLines(spec: {
178
+ lines: {
179
+ price: number;
180
+ color: number;
181
+ width: number;
182
+ lineStyle: number;
183
+ text: string;
184
+ quantity: string;
185
+ flags: number;
186
+ }[];
187
+ bodyBg: number;
188
+ fontSizePx: number;
189
+ lineLengthFrac: number;
190
+ align: number;
191
+ hoverBoost: number;
192
+ }): void;
193
+ /**
194
+ * Hit-tests pixel (x, y) against the price lines. `part` is 0 for the line or
195
+ * its label body (the drag target) and 1 for the close button; close buttons
196
+ * win over lines and the nearest in y wins among several candidates. Only
197
+ * draggable lines report part 0 and only closable lines report part 1. Cheap
198
+ * to call at gesture rate (no rendering).
199
+ */
200
+ hitTestPriceLine(x: number, y: number): { index: number; part: number } | null;
201
+ /**
202
+ * Marks a price line's segment as hovered so it renders highlighted; -1 clears.
203
+ * `part` matches hitTestPriceLine. Touch has no hover state, so this is here
204
+ * for pointer devices and web parity.
205
+ */
206
+ setPriceLineHover(index: number, part: number): void;
207
+ /**
208
+ * Drives the live drag preview: the line, its label and its badge render at
209
+ * `price` with a ghost at the committed one. Pass -1 to end the preview. The
210
+ * committed price is untouched — restate setPriceLines to apply the move.
211
+ */
212
+ setPriceLineDrag(index: number, price: number): void;
165
213
  /** True while any axis-label fade is still in progress. Drives a RAF loop. */
166
214
  isAnimating(): boolean;
167
215
  render(): SkPicture | null;
package/src/types.ts CHANGED
@@ -17,6 +17,8 @@ export type {
17
17
  BollingerBandsConfig,
18
18
  MACDConfig,
19
19
  ChartType,
20
+ PriceLine,
21
+ PriceLinesStyle,
20
22
  } from '@vroomchart/types';
21
23
 
22
24
  /**
@@ -11,6 +11,8 @@ import type {
11
11
  ChartType,
12
12
  MACDConfig,
13
13
  MovingAverageOverlay,
14
+ PriceLine,
15
+ PriceLinesStyle,
14
16
  RSIConfig,
15
17
  VisibleRange,
16
18
  VroomTheme,
@@ -68,6 +70,61 @@ function bollingerToSpec(cfg: BollingerBandsConfig | undefined) {
68
70
  };
69
71
  }
70
72
 
73
+ // Price-line defaults: a soft red dotted rule with a dark translucent label,
74
+ // close in weight to the current-price indicator it sits beside.
75
+ const DEFAULT_PRICE_LINE_COLOR = 0xffef5350;
76
+ const DEFAULT_PRICE_LINE_BODY_BG = 0xd91c2128;
77
+ const DEFAULT_PRICE_LINE_HOVER_BOOST = 1.25;
78
+
79
+ const LINE_STYLES = { solid: 0, dotted: 1, dashed: 2 } as const;
80
+
81
+ // Mirrors VroomPriceLineFlags in packages/core/include/vroom/vroom_chart.h.
82
+ const PRICE_LINE_DRAGGABLE = 1 << 0;
83
+ const PRICE_LINE_CLOSABLE = 1 << 1;
84
+ const PRICE_LINE_AXIS_LABEL = 1 << 2;
85
+ const PRICE_LINE_EXTEND_LEFT = 1 << 3;
86
+
87
+ /** The price lines + their shared style, as the chart's props express them. */
88
+ export type PriceLinesProp = {
89
+ lines: PriceLine[];
90
+ style?: PriceLinesStyle;
91
+ /**
92
+ * Whether the host supplied a close handler. The close button is
93
+ * callback-gated, so with nothing for it to do it isn't drawn at all.
94
+ */
95
+ hasCloseHandler: boolean;
96
+ };
97
+
98
+ function priceLinesToSpec(cfg: PriceLinesProp) {
99
+ return {
100
+ lines: cfg.lines.map((l) => ({
101
+ price: l.price,
102
+ color:
103
+ (l.color != null ? parseColor(l.color) : null) ?? DEFAULT_PRICE_LINE_COLOR,
104
+ width: l.width ?? 1,
105
+ lineStyle: LINE_STYLES[l.lineStyle ?? 'dotted'],
106
+ text: l.text ?? '',
107
+ quantity: l.quantity ?? '',
108
+ flags:
109
+ (l.draggable ? PRICE_LINE_DRAGGABLE : 0) |
110
+ (cfg.hasCloseHandler && l.closable !== false ? PRICE_LINE_CLOSABLE : 0) |
111
+ (l.axisLabel !== false ? PRICE_LINE_AXIS_LABEL : 0) |
112
+ (l.extendLeft !== false ? PRICE_LINE_EXTEND_LEFT : 0),
113
+ })),
114
+ bodyBg:
115
+ (cfg.style?.bodyBackground != null ? parseColor(cfg.style.bodyBackground) : null) ??
116
+ DEFAULT_PRICE_LINE_BODY_BG,
117
+ fontSizePx: cfg.style?.fontSize ?? 0,
118
+ lineLengthFrac: cfg.style?.inset ?? 0,
119
+ align: cfg.style?.align === 'left' ? 0 : cfg.style?.align === 'center' ? 1 : 2,
120
+ hoverBoost: cfg.style?.hoverBoost ?? DEFAULT_PRICE_LINE_HOVER_BOOST,
121
+ };
122
+ }
123
+
124
+ // Cleared overlay: no lines (the style values are irrelevant, but the spec shape
125
+ // requires them).
126
+ const EMPTY_PRICE_LINES = priceLinesToSpec({ lines: [], hasCloseHandler: false });
127
+
71
128
  let installed = false;
72
129
  function ensureInstalled(): void {
73
130
  if (installed) return;
@@ -101,6 +158,7 @@ export function useChartCore(
101
158
  movingAverages?: MovingAverageOverlay[],
102
159
  vwap?: VWAPConfig,
103
160
  bollingerBands?: BollingerBandsConfig,
161
+ priceLines?: PriceLinesProp,
104
162
  ): ChartCoreState {
105
163
  const handleRef = useRef<ChartHandle | null>(null);
106
164
  // Push setDefaultCandleWidth only once (first load): setCandles re-runs on
@@ -130,6 +188,7 @@ export function useChartCore(
130
188
  const maKey = movingAverages ? JSON.stringify(movingAverages) : '';
131
189
  const vwapKey = vwap ? JSON.stringify(vwap) : '';
132
190
  const bollingerKey = bollingerBands ? JSON.stringify(bollingerBands) : '';
191
+ const priceLinesKey = priceLines ? JSON.stringify(priceLines) : '';
133
192
 
134
193
  useEffect(() => {
135
194
  const h = handleRef.current;
@@ -180,13 +239,16 @@ export function useChartCore(
180
239
  vwap?.width ?? 1.5,
181
240
  );
182
241
  h.setBollinger(bollingerToSpec(bollingerBands));
242
+ h.setPriceLines(
243
+ priceLines?.lines.length ? priceLinesToSpec(priceLines) : EMPTY_PRICE_LINES,
244
+ );
183
245
  // TODO(rn-parity): mirror the web `liquidity` overlay here (setLiquidity +
184
246
  // the VroomBand structs in the JSI handle) — web-only for now.
185
247
  setPicture(h.render());
186
- // theme/rsi/macd/movingAverages/vwap/bollingerBands are represented by
187
- // their *Key deps.
248
+ // theme/rsi/macd/movingAverages/vwap/bollingerBands/priceLines are
249
+ // represented by their *Key deps.
188
250
  // eslint-disable-next-line react-hooks/exhaustive-deps
189
- }, [candles, size.width, size.height, size.pxRatio, explicit, startMs, endMs, defaultCandleWidth, themeKey, rsiKey, macdKey, maKey, vwapKey, bollingerKey]);
251
+ }, [candles, size.width, size.height, size.pxRatio, explicit, startMs, endMs, defaultCandleWidth, themeKey, rsiKey, macdKey, maKey, vwapKey, bollingerKey, priceLinesKey]);
190
252
 
191
253
  return { handle: handleRef.current, picture };
192
254
  }