@pradeepjadhav/trading-reload-chart 1.3.0 → 1.4.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/README.md CHANGED
@@ -253,6 +253,46 @@ export function ChartPanel() {
253
253
  - `TradingReload` fills **100%** of that container.
254
254
  - Do **not** import chart CSS manually; styles ship with the component (`src/styles/chart.css`).
255
255
 
256
+ ### Watermark
257
+
258
+ Add a text watermark (symbol / timeframe label, chart id, etc.) through `config.watermark`.
259
+ It renders on its own canvas **beneath** candles, shapes, trades, and the crosshair, so it
260
+ never intercepts pointer events.
261
+
262
+ ```tsx
263
+ import {
264
+ TradingReload,
265
+ type ChartConfig,
266
+ type DeepPartial,
267
+ } from "@pradeepjadhav/trading-reload-chart";
268
+
269
+ const config = {
270
+ watermark: {
271
+ text: `${symbol}, ${timeframe}`,
272
+ position: "top-center",
273
+ fontSize: 24,
274
+ fontWeight: 700,
275
+ color: "#FFFFFF",
276
+ opacity: 0.35,
277
+ },
278
+ } satisfies DeepPartial<ChartConfig>;
279
+
280
+ <TradingReload config={config} /* … */ />;
281
+ ```
282
+
283
+ | Option | Type | Default | Description |
284
+ | ------------- | ---------------------------------------------------------------------------------------- | ------------------------------------------- | ---------------------------------------- |
285
+ | `text` | `string` | `""` | Empty text hides the watermark |
286
+ | `position` | `"center" \| "top-center" \| "bottom-center" \| "left-center" \| "right-center"` | `"center"` | Anchor inside the plot area |
287
+ | `fontSize` | `number` | `48` | Font size in CSS pixels |
288
+ | `fontWeight` | `number` | `700` | Numeric font weight |
289
+ | `fontFamily` | `string` | `"Inter, system-ui, -apple-system, sans-serif"` | CSS font stack |
290
+ | `color` | `string` | `COLORS.white` | Text color |
291
+ | `opacity` | `number` | `0.25` | Clamped to `0` - `1` |
292
+ | `padding` | `number` | `16` | Inset from the plot edge |
293
+
294
+ > Because `config` is deep-merged, you can pass only the keys you want to change.
295
+
256
296
  ### 3. Vite / Create React App
257
297
 
258
298
  ```tsx
@@ -286,6 +326,7 @@ Import from the package root:
286
326
  | `Candle`, `OpenTrade`, `ClosedTrade`, … | Domain types |
287
327
  | `Shape`, `ShapeToolType`, shape payloads | Drawing types |
288
328
  | `ChartConfig`, `CHART_CONFIG`, `DeepPartial` | Configuration |
329
+ | `WatermarkConfig`, `WatermarkPosition` | Watermark types |
289
330
  | `ClosedTradeIndicator`, trade handle types | Trade overlay types |
290
331
 
291
332
  Controlled props (parent owns state):
@@ -319,7 +360,7 @@ trading-reload-chart/
319
360
  │ ├── index.ts # Public exports
320
361
  │ ├── react/ # TradingReload component
321
362
  │ ├── chart/ # ChartController, DOM, utils
322
- │ ├── canvas/layers/ # Canvas layers (candles, shapes, trades, …)
363
+ │ ├── canvas/layers/ # Canvas layers (candles, volume, watermark, shapes, trades, …)
323
364
  │ ├── config/ # CHART_CONFIG and types
324
365
  │ ├── models/ # Candle, Trade, ChartViewport, … (*.types.ts)
325
366
  │ ├── helpers/ # Shared pure utilities
@@ -336,11 +377,12 @@ Type declarations live in `*.types.ts` files next to implementation code (see AG
336
377
 
337
378
  ## Features
338
379
 
339
- - Layered canvas rendering (volume, candles, shapes, trades, axes, crosshair)
380
+ - Layered canvas rendering (volume, watermark, candles, shapes, trades, axes, crosshair)
340
381
  - Pan, horizontal wheel zoom, Ctrl+wheel vertical zoom
341
382
  - Drawing tools (trendline, rectangle, path, fib, long/short position)
342
383
  - Open trade SL/TP drag with `onTradeModify` on release
343
384
  - Closed trade markers
385
+ - Configurable text watermark layer
344
386
  - Deep-merge chart `config` over defaults
345
387
 
346
388
  ---
@@ -1,4 +1,4 @@
1
- import { ShapeCoordinateConverter, CommonShapeConfig, ShapeHandleHitbox, ShapePoint, ShapeVertex, TrendlineShape } from './ShapesLayer.types';
1
+ import { CommonShapeConfig, ShapeCoordinateConverter, ShapeHandleHitbox, ShapePoint, ShapeVertex, TrendlineShape } from './ShapesLayer.types';
2
2
  export declare class LineShape {
3
3
  static defaultConfig: CommonShapeConfig;
4
4
  static draw({ ctx, shape, converter, config, selected, hovered, }: {
@@ -1,4 +1,4 @@
1
- import { PathShapeData, ShapeCoordinateConverter, CommonShapeConfig, ShapeHandleHitbox, ShapePoint, ShapeVertex } from './ShapesLayer.types';
1
+ import { CommonShapeConfig, PathShapeData, ShapeCoordinateConverter, ShapeHandleHitbox, ShapePoint, ShapeVertex } from './ShapesLayer.types';
2
2
  export declare class PathShape {
3
3
  static defaultConfig: CommonShapeConfig;
4
4
  static draw({ ctx, shape, converter, config, selected, hovered, }: {
@@ -1,4 +1,4 @@
1
- import { RectangleShapeData, ShapeCoordinateConverter, ShapeHandleHitbox, ShapePoint, ShapeVertex, RectangleShapeConfig } from './ShapesLayer.types';
1
+ import { RectangleShapeConfig, RectangleShapeData, ShapeCoordinateConverter, ShapeHandleHitbox, ShapePoint, ShapeVertex } from './ShapesLayer.types';
2
2
  export declare class RectangleShape {
3
3
  static defaultConfig: RectangleShapeConfig;
4
4
  static draw({ ctx, shape, converter, config, selected, hovered, }: {
@@ -1,5 +1,5 @@
1
1
  import { Candle } from '../../../models/Candle.types';
2
- import { FibLevelValue, Shape, ShapeBoundingBox, ShapeCoordinateConverter, CommonShapeConfig, ShapeHandleHitbox, ShapeLineStyle, ShapePoint, ShapeVertex } from './ShapesLayer.types';
2
+ import { CommonShapeConfig, FibLevelValue, Shape, ShapeBoundingBox, ShapeCoordinateConverter, ShapeHandleHitbox, ShapeLineStyle, ShapePoint, ShapeVertex } from './ShapesLayer.types';
3
3
  export declare const SHAPE_HIT_TOLERANCE = 8;
4
4
  export declare const SHAPE_HANDLE_RADIUS = 5;
5
5
  export declare const generateShapeId: () => string;
@@ -1,4 +1,4 @@
1
- import { MissingTradeProtectionHandleRectConfig, ClosedTradeIndicatorArrowConfig as ClosedTradeIndicatorArrowConfig, TemporaryTradeProtectionHandleRectConfig, TradeHandleLineConfig, TradeHandleRectConfig, TradeHandleStyleConfig } from '../../../config/chartConfig.types';
1
+ import { ClosedTradeIndicatorArrowConfig, MissingTradeProtectionHandleRectConfig, TemporaryTradeProtectionHandleRectConfig, TradeHandleLineConfig, TradeHandleRectConfig, TradeHandleStyleConfig } from '../../../config/chartConfig.types';
2
2
  import { ChartViewport } from '../../../models/ChartViewport.types';
3
3
  import { ClosedTrade, OpenTrade, TradeDirection } from '../../../models/Trade.types';
4
4
  /**
@@ -0,0 +1,40 @@
1
+ import { WatermarkConfig } from '../../../config/chartConfig.types';
2
+ import { WatermarkLayerOptions } from './WatermarkLayer.types';
3
+ /**
4
+ * Text watermark drawn on its own canvas beneath the candlestick and
5
+ * interaction layers, so it never intercepts pointer events and is always
6
+ * painted over by candles, shapes, trades, and the crosshair.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * const layer = new WatermarkLayer({
11
+ * canvas,
12
+ * config: { ...CHART_CONFIG.watermark, text: "USDJPY, 15m" },
13
+ * });
14
+ * layer.resize(800, 600);
15
+ * layer.render();
16
+ * ```
17
+ */
18
+ export declare class WatermarkLayer {
19
+ #private;
20
+ constructor(options: WatermarkLayerOptions);
21
+ setConfig(config: WatermarkConfig): void;
22
+ /**
23
+ * Resizes the canvas and stores the plot size used for placement.
24
+ *
25
+ * @example
26
+ * ```ts
27
+ * layer.resize(800, 600);
28
+ * ```
29
+ */
30
+ resize(width: number, height: number): void;
31
+ /**
32
+ * Clears the canvas and paints the watermark when it has text.
33
+ *
34
+ * @example
35
+ * ```ts
36
+ * layer.render();
37
+ * ```
38
+ */
39
+ render(): void;
40
+ }
@@ -0,0 +1,27 @@
1
+ import { ResolveWatermarkTextPlacementOptions, WatermarkTextPlacement } from './WatermarkLayer.types';
2
+ /**
3
+ * Resolves the canvas text anchor for a watermark position.
4
+ *
5
+ * `textAlign` and `textBaseline` are returned alongside the coordinates so the
6
+ * caller can apply them to the context before measuring or drawing the text.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * const { x, y, textAlign, textBaseline } = resolveWatermarkTextPlacement({
11
+ * position: "bottom-center",
12
+ * width: 800,
13
+ * height: 600,
14
+ * padding: 16,
15
+ * });
16
+ * ```
17
+ */
18
+ export declare const resolveWatermarkTextPlacement: (options: ResolveWatermarkTextPlacementOptions) => WatermarkTextPlacement;
19
+ /**
20
+ * Clamps an opacity into the `0` - `1` range accepted by the canvas context.
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * clampWatermarkOpacity(1.4); // 1
25
+ * ```
26
+ */
27
+ export declare const clampWatermarkOpacity: (opacity: number) => number;
@@ -0,0 +1,66 @@
1
+ import { WatermarkConfig } from '../../../config/chartConfig.types';
2
+ /**
3
+ * Constructor options for {@link WatermarkLayer}.
4
+ *
5
+ * @example
6
+ * ```ts
7
+ * const layer = new WatermarkLayer({
8
+ * canvas,
9
+ * config: { ...CHART_CONFIG.watermark, text: "USDJPY, 15m" },
10
+ * });
11
+ * ```
12
+ */
13
+ export type WatermarkLayerOptions = {
14
+ /** Canvas the watermark is painted on. */
15
+ canvas: HTMLCanvasElement;
16
+ /** Resolved watermark configuration. */
17
+ config: WatermarkConfig;
18
+ };
19
+ /**
20
+ * Text anchor resolved from a {@link WatermarkPosition} and the plot size.
21
+ *
22
+ * All values are in CSS pixels.
23
+ *
24
+ * @example
25
+ * ```ts
26
+ * const placement: WatermarkTextPlacement = {
27
+ * x: 400,
28
+ * y: 16,
29
+ * textAlign: "center",
30
+ * textBaseline: "top",
31
+ * };
32
+ * ```
33
+ */
34
+ export type WatermarkTextPlacement = {
35
+ /** Horizontal anchor point. */
36
+ x: number;
37
+ /** Vertical anchor point. */
38
+ y: number;
39
+ /** Canvas text alignment applied before measuring. */
40
+ textAlign: CanvasTextAlign;
41
+ /** Canvas text baseline applied before measuring. */
42
+ textBaseline: CanvasTextBaseline;
43
+ };
44
+ /**
45
+ * Inputs used to resolve the watermark text anchor.
46
+ *
47
+ * @example
48
+ * ```ts
49
+ * const placement = resolveWatermarkTextPlacement({
50
+ * position: "top-center",
51
+ * width: 800,
52
+ * height: 600,
53
+ * padding: 16,
54
+ * });
55
+ * ```
56
+ */
57
+ export type ResolveWatermarkTextPlacementOptions = {
58
+ /** Configured watermark position. */
59
+ position: WatermarkConfig["position"];
60
+ /** Plot width in CSS pixels. */
61
+ width: number;
62
+ /** Plot height in CSS pixels. */
63
+ height: number;
64
+ /** Padding between the watermark and the plot edge. */
65
+ padding: number;
66
+ };
@@ -3,6 +3,7 @@ export type ChartDomElements = {
3
3
  layout: HTMLDivElement;
4
4
  stack: HTMLDivElement;
5
5
  volumeCanvas: HTMLCanvasElement;
6
+ watermarkCanvas: HTMLCanvasElement;
6
7
  candleCanvas: HTMLCanvasElement;
7
8
  shapesCanvas: HTMLCanvasElement;
8
9
  tradesCanvas: HTMLCanvasElement;
@@ -0,0 +1,7 @@
1
+ import { WheelZoomAxis } from './resolveWheelZoomAxis.types';
2
+ /**
3
+ * Horizontal for a plain wheel, and horizontal for a trackpad pinch so the
4
+ * gesture zooms the time axis. Vertical stays reserved for a real Ctrl/Cmd +
5
+ * mouse wheel so existing mouse and keyboard-driven zoom is untouched.
6
+ */
7
+ export declare const resolveWheelZoomAxis: (event: WheelEvent) => WheelZoomAxis;
@@ -0,0 +1 @@
1
+ export type WheelZoomAxis = "x" | "y";
@@ -116,6 +116,46 @@ export type VolumeConfig = {
116
116
  bottomOffset: number;
117
117
  minBarHeight: number;
118
118
  };
119
+ /**
120
+ * Placement of the watermark inside the plot area.
121
+ *
122
+ * @example `"top-center"`
123
+ */
124
+ export type WatermarkPosition = "center" | "top-center" | "bottom-center" | "left-center" | "right-center";
125
+ /**
126
+ * Text watermark rendered on a dedicated canvas beneath the candlestick and
127
+ * interaction layers.
128
+ *
129
+ * @example
130
+ * ```ts
131
+ * const watermark: WatermarkConfig = {
132
+ * text: "USDJPY, 15m",
133
+ * position: "top-center",
134
+ * fontSize: 24,
135
+ * fontWeight: 700,
136
+ * color: "#ffffff",
137
+ * opacity: 0.35,
138
+ * };
139
+ * ```
140
+ */
141
+ export type WatermarkConfig = {
142
+ /** Watermark text. An empty string hides the watermark. */
143
+ text: string;
144
+ /** Watermark placement inside the plot area. */
145
+ position: WatermarkPosition;
146
+ /** Font size in CSS pixels, before device pixel ratio scaling. */
147
+ fontSize: number;
148
+ /** Numeric CSS font weight. */
149
+ fontWeight: number;
150
+ /** CSS font family used to measure and paint the watermark. */
151
+ fontFamily: string;
152
+ /** Watermark color, any CSS color value. */
153
+ color: string;
154
+ /** Watermark opacity between `0` and `1`. */
155
+ opacity: number;
156
+ /** Padding in CSS pixels between the watermark and the plot edge. */
157
+ padding: number;
158
+ };
119
159
  /**
120
160
  * Crosshair line style.
121
161
  *
@@ -633,6 +673,7 @@ export type ChartConfig = {
633
673
  zoom: ZoomConfig;
634
674
  candles: CandlesConfig;
635
675
  volume: VolumeConfig;
676
+ watermark: WatermarkConfig;
636
677
  crosshair: CrosshairConfig;
637
678
  tradeHandles: TradeHandlesConfig;
638
679
  closedTradeIndicators: ClosedTradeIndicatorsConfig;
package/dist/index.d.ts CHANGED
@@ -4,7 +4,7 @@ export type { ChartControllerProps, PreviousDay, TradeModifyPayload, } from './c
4
4
  export type { DeepPartial } from './chart/utils/deepPartial.types';
5
5
  export type { CanvasPoint } from './chart/utils/getCanvasPoint.types';
6
6
  export { CHART_CONFIG } from './config/chartConfig';
7
- export type { ChartConfig, PreviousDayHighLowConfig } from './config/chartConfig.types';
7
+ export type { ChartConfig, PreviousDayHighLowConfig, WatermarkConfig, WatermarkPosition, } from './config/chartConfig.types';
8
8
  export type { Candle } from './models/Candle.types';
9
9
  export type { ChartCursorState, ChartViewState } from './models/ChartSync.types';
10
10
  export type { ChartViewport } from './models/ChartViewport.types';
@@ -55,9 +55,12 @@ export type Candle = {
55
55
  /**
56
56
  * Current ask price during this candle.
57
57
  *
58
+ * Only the forming candle carries it; feeds that omit a live tick leave it
59
+ * undefined, so the ask line falls back to this candle's close.
60
+ *
58
61
  * @example `1.0853`
59
62
  */
60
- ask: number;
63
+ ask?: number;
61
64
  /**
62
65
  * `false` while the realtime candle is still forming.
63
66
  *
@@ -1,2 +1,2 @@
1
- .trading-reload-chart{box-sizing:border-box;outline:none;width:100%;height:100%;overflow:hidden}.trading-reload-chart *,.trading-reload-chart :before,.trading-reload-chart :after{box-sizing:border-box}.trading-reload-chart__layout{--axis-x-height:30px;width:100%;height:100%;display:flex;position:relative;overflow:hidden}.trading-reload-chart__stack{flex:1;height:100%;position:relative;overflow:hidden}.trading-reload-chart canvas{display:block}.trading-reload-chart__volume,.trading-reload-chart__plot,.trading-reload-chart__shapes,.trading-reload-chart__trades,.trading-reload-chart__overlay{width:100%;height:calc(100% - var(--axis-x-height));position:absolute;top:0;left:0}.trading-reload-chart__volume{z-index:0;pointer-events:none}.trading-reload-chart__plot{z-index:1}.trading-reload-chart__shapes{z-index:2}.trading-reload-chart__trades{z-index:3}.trading-reload-chart__overlay{z-index:4}.trading-reload-chart__axis-x{width:100%;height:var(--axis-x-height);z-index:5;position:absolute;bottom:0;left:0}.trading-reload-chart__axis-y{height:calc(100% - var(--axis-x-height));z-index:4;flex-shrink:0;position:relative}
1
+ .trading-reload-chart{box-sizing:border-box;outline:none;width:100%;height:100%;overflow:hidden}.trading-reload-chart *,.trading-reload-chart :before,.trading-reload-chart :after{box-sizing:border-box}.trading-reload-chart__layout{--axis-x-height:30px;width:100%;height:100%;display:flex;position:relative;overflow:hidden}.trading-reload-chart__stack{flex:1;height:100%;position:relative;overflow:hidden}.trading-reload-chart canvas{display:block}.trading-reload-chart__volume,.trading-reload-chart__watermark,.trading-reload-chart__plot,.trading-reload-chart__shapes,.trading-reload-chart__trades,.trading-reload-chart__overlay{width:100%;height:calc(100% - var(--axis-x-height));position:absolute;top:0;left:0}.trading-reload-chart__volume{z-index:0;pointer-events:none}.trading-reload-chart__watermark{z-index:1;pointer-events:none}.trading-reload-chart__plot{z-index:2}.trading-reload-chart__shapes{z-index:3}.trading-reload-chart__trades{z-index:4}.trading-reload-chart__overlay{z-index:5}.trading-reload-chart__axis-x{width:100%;height:var(--axis-x-height);z-index:6;position:absolute;bottom:0;left:0}.trading-reload-chart__axis-y{height:calc(100% - var(--axis-x-height));z-index:4;flex-shrink:0;position:relative}
2
2
  /*$vite$:1*/