wickchart 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 +58 -0
- package/package.json +3 -3
- package/src/wick-chart.js +244 -0
- package/types/wick-chart.d.ts +75 -0
package/README.md
CHANGED
|
@@ -633,6 +633,60 @@ WickChart.registerIndicator('vwap', {
|
|
|
633
633
|
chart.indicators = 'vwap';
|
|
634
634
|
```
|
|
635
635
|
|
|
636
|
+
### Plugin layers — extend without forking
|
|
637
|
+
|
|
638
|
+
`addLayer()` is the whole extension surface: an external draw hook that paints
|
|
639
|
+
into the render pipeline (above chart content, under the crosshair) and can
|
|
640
|
+
claim pointer gestures so drags reach your code instead of panning the chart.
|
|
641
|
+
Four public coordinate transforms — `timeToX`/`xToTime` (extrapolating past
|
|
642
|
+
the last bar into future space) and `priceToY`/`yToPrice` — anchor your
|
|
643
|
+
content in data space so it rides along with zoom and pan:
|
|
644
|
+
|
|
645
|
+
```js
|
|
646
|
+
chart.addLayer({
|
|
647
|
+
id: 'flags',
|
|
648
|
+
draw(api) {
|
|
649
|
+
const x = api.timeToX(t), y = api.priceToY(p); // anchors, not pixels
|
|
650
|
+
api.ctx.fillStyle = api.palette.accent;
|
|
651
|
+
// …paint in CSS pixels
|
|
652
|
+
},
|
|
653
|
+
onPointer(ev) {
|
|
654
|
+
if (ev.type === 'down' && hitsMyContent(ev)) return true; // claim the drag
|
|
655
|
+
},
|
|
656
|
+
});
|
|
657
|
+
chart.requestDraw(); // repaint hook for interactive layers
|
|
658
|
+
chart.removeLayer('flags'); // detach by handle or id
|
|
659
|
+
```
|
|
660
|
+
|
|
661
|
+
A claimed gesture delivers `move`/`up` (and `cancel` on Escape) to the layer
|
|
662
|
+
while the chart suppresses pan/brush/measure. Markers, watermarks, signal
|
|
663
|
+
badges — or a whole drawing toolkit — plug in without the core growing a
|
|
664
|
+
single tool. The main entry is covered by a CI gzip budget (64 KB) so it
|
|
665
|
+
stays that way.
|
|
666
|
+
|
|
667
|
+
### Drawings — the `wickchart-draw` plugin
|
|
668
|
+
|
|
669
|
+
The first official plugin: TradingView-style drawing tools as opt-in bytes
|
|
670
|
+
(~8 KB gz, own CI budget). Trendlines (segment/ray), horizontal levels,
|
|
671
|
+
rectangles, fibonacci retracements and text — all plain `{ time, price }`
|
|
672
|
+
data that rides zoom & pan, survives reloads, extrapolates into future
|
|
673
|
+
space, and serializes to JSON. Anchors magnet-snap to bar times and OHLC.
|
|
674
|
+
|
|
675
|
+
```js
|
|
676
|
+
import { attachDrawings } from 'wickchart-draw';
|
|
677
|
+
|
|
678
|
+
const draw = attachDrawings(chart);
|
|
679
|
+
draw.setTool('trendline'); // drag to draw; setTool(null) = select/move mode
|
|
680
|
+
draw.getDrawings(); // → JSON array (save it); setDrawings(saved)
|
|
681
|
+
draw.undo(); draw.clear();
|
|
682
|
+
chart.addEventListener('wick:drawings', (e) => save(e.detail.drawings));
|
|
683
|
+
```
|
|
684
|
+
|
|
685
|
+
Select mode: click a drawing to select it, drag to move, drag the square
|
|
686
|
+
handles to re-anchor, `Delete` removes, `Esc` cancels a gesture; clicks on
|
|
687
|
+
empty space fall through to the chart. Peer dependency: wickchart ≥ 1.4.
|
|
688
|
+
See the live playground in the docs (Drawings section).
|
|
689
|
+
|
|
636
690
|
## Methods
|
|
637
691
|
|
|
638
692
|
| Method | Description |
|
|
@@ -647,6 +701,10 @@ chart.indicators = 'vwap';
|
|
|
647
701
|
| `getDataWindow()` | → AI-ready summary of the visible window (see below) |
|
|
648
702
|
| `getState()` | → serializable snapshot (type, indicators, view, positions, alerts) |
|
|
649
703
|
| `setState(state)` | Apply a snapshot; a pending view applies after the next `setData()` |
|
|
704
|
+
| `addLayer(layer)` / `removeLayer(idOrHandle)` | Register/detach a plugin layer (draw hook + optional pointer claim) |
|
|
705
|
+
| `requestDraw()` | Repaint on the next frame (interactive layers) |
|
|
706
|
+
| `timeToX(t)` / `xToTime(x)` | Bar time ⇄ x-pixel; extrapolates into future space |
|
|
707
|
+
| `priceToY(p)` / `yToPrice(y)` | Price ⇄ y-pixel in the main pane (log-aware) |
|
|
650
708
|
|
|
651
709
|
### Infinite history (`loadMore`)
|
|
652
710
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wickchart",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "<wick-chart> — a modern, dependency-free financial charting web component. Candles, line & area charts, crosshair, zoom/pan, indicators (incl. a safe expression mini-language), live streaming via <wick-feed>, theming.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/wick-chart.js",
|
|
@@ -42,10 +42,10 @@
|
|
|
42
42
|
},
|
|
43
43
|
"scripts": {
|
|
44
44
|
"dev": "npx --yes serve . -l 5173",
|
|
45
|
-
"test": "node --test \"tests/*.test.mjs\"",
|
|
45
|
+
"test": "node --test \"tests/*.test.mjs\" \"plugins/draw/tests/*.test.mjs\"",
|
|
46
46
|
"build:types": "node -e \"require('fs').rmSync('types', { recursive: true, force: true });\" && tsc -p tsconfig.json",
|
|
47
47
|
"prepack": "npm run build:types",
|
|
48
|
-
"ci": "npm run build:types && npm test && node --check src/wick-chart.js && node --check src/wick-feed.js && node --check src/core.js && node --check src/react.js && node --check src/react-core.js && node --check demo/app.js"
|
|
48
|
+
"ci": "npm run build:types && npm test && node --check src/wick-chart.js && node --check src/wick-feed.js && node --check src/core.js && node --check src/react.js && node --check src/react-core.js && node --check demo/app.js && node --check plugins/draw/core.mjs && node --check plugins/draw/draw.mjs"
|
|
49
49
|
},
|
|
50
50
|
"keywords": [
|
|
51
51
|
"chart",
|
package/src/wick-chart.js
CHANGED
|
@@ -235,6 +235,11 @@ class WickChart extends HTMLElementBase {
|
|
|
235
235
|
this._pan = null;
|
|
236
236
|
this._pinch = null;
|
|
237
237
|
|
|
238
|
+
// plugin layers: external draw hooks + pointer claims (see addLayer)
|
|
239
|
+
this._layers = [];
|
|
240
|
+
this._layerClaim = null; // { layer, pointerId } while a layer owns a drag
|
|
241
|
+
this._layerSeq = 0;
|
|
242
|
+
|
|
238
243
|
// history backfill state (onloadmore declared as a class field above)
|
|
239
244
|
this._loadingMore = false;
|
|
240
245
|
this._noMore = false;
|
|
@@ -303,6 +308,7 @@ class WickChart extends HTMLElementBase {
|
|
|
303
308
|
this._connected = false;
|
|
304
309
|
this.stopWalk(true);
|
|
305
310
|
this.stopStory(true);
|
|
311
|
+
this._layerClaim = null;
|
|
306
312
|
if (this._coviewCh) {
|
|
307
313
|
this._coviewSend({ type: 'bye' });
|
|
308
314
|
try {
|
|
@@ -2532,6 +2538,9 @@ class WickChart extends HTMLElementBase {
|
|
|
2532
2538
|
);
|
|
2533
2539
|
}
|
|
2534
2540
|
|
|
2541
|
+
/* plugin layers — above chart content, under the pointer-following UI */
|
|
2542
|
+
if (this._layers.length) this._drawLayers(ctx, pal, ly, d);
|
|
2543
|
+
|
|
2535
2544
|
/* crosshair */
|
|
2536
2545
|
if (this._hover && this._hover.index < d.length) {
|
|
2537
2546
|
const h = this._hover;
|
|
@@ -2912,6 +2921,207 @@ class WickChart extends HTMLElementBase {
|
|
|
2912
2921
|
poss.innerHTML = html;
|
|
2913
2922
|
}
|
|
2914
2923
|
|
|
2924
|
+
/* ------------------------------------------------------------ *
|
|
2925
|
+
* Plugin layers — external draw hooks + pointer claims
|
|
2926
|
+
* ------------------------------------------------------------ */
|
|
2927
|
+
|
|
2928
|
+
/**
|
|
2929
|
+
* Register a plugin layer. `layer.draw(api)` runs on every render, above
|
|
2930
|
+
* chart content and under the crosshair; `layer.onPointer(pev)` is asked
|
|
2931
|
+
* first on pointerdown and claims the gesture by returning true — the
|
|
2932
|
+
* layer then receives that pointer's move/up/cancel events (plus a
|
|
2933
|
+
* 'cancel' on Escape) and the chart suppresses its own pan/measure/brush
|
|
2934
|
+
* for the duration.
|
|
2935
|
+
* @param {{id?: string, draw: Function, onPointer?: Function}} layer
|
|
2936
|
+
* @returns {object|null} the normalized layer handle (with `id`), or null
|
|
2937
|
+
* if the layer was rejected (no draw fn, or 16 layers already added)
|
|
2938
|
+
*/
|
|
2939
|
+
addLayer(layer) {
|
|
2940
|
+
if (!layer || typeof layer !== 'object' || typeof layer.draw !== 'function') return null;
|
|
2941
|
+
if (this._layers.length >= 16) return null;
|
|
2942
|
+
const id =
|
|
2943
|
+
layer.id != null && String(layer.id).trim()
|
|
2944
|
+
? String(layer.id).slice(0, 64)
|
|
2945
|
+
: 'layer-' + ++this._layerSeq;
|
|
2946
|
+
const entry = {
|
|
2947
|
+
id,
|
|
2948
|
+
draw: layer.draw,
|
|
2949
|
+
onPointer: typeof layer.onPointer === 'function' ? layer.onPointer : null,
|
|
2950
|
+
};
|
|
2951
|
+
const at = this._layers.findIndex((l) => l.id === id);
|
|
2952
|
+
if (at >= 0) this._layers[at] = entry; // same id replaces
|
|
2953
|
+
else this._layers.push(entry);
|
|
2954
|
+
this._invalidate();
|
|
2955
|
+
return entry;
|
|
2956
|
+
}
|
|
2957
|
+
|
|
2958
|
+
/**
|
|
2959
|
+
* Remove a layer added via addLayer (pass the returned handle or its id).
|
|
2960
|
+
* @param {object|string} idOrLayer
|
|
2961
|
+
* @returns {boolean} true if a layer was removed
|
|
2962
|
+
*/
|
|
2963
|
+
removeLayer(idOrLayer) {
|
|
2964
|
+
const id = idOrLayer != null && typeof idOrLayer === 'object' ? idOrLayer.id : idOrLayer;
|
|
2965
|
+
if (id == null) return false;
|
|
2966
|
+
const at = this._layers.findIndex((l) => l.id === String(id));
|
|
2967
|
+
if (at < 0) return false;
|
|
2968
|
+
const [gone] = this._layers.splice(at, 1);
|
|
2969
|
+
if (this._layerClaim && this._layerClaim.layer === gone) this._layerClaim = null;
|
|
2970
|
+
this._invalidate();
|
|
2971
|
+
return true;
|
|
2972
|
+
}
|
|
2973
|
+
|
|
2974
|
+
/** Ask for a repaint on the next frame (interactive layers call this). */
|
|
2975
|
+
requestDraw() {
|
|
2976
|
+
this._invalidate();
|
|
2977
|
+
}
|
|
2978
|
+
|
|
2979
|
+
/** Paint every registered layer. Called from _render with live state. */
|
|
2980
|
+
_drawLayers(ctx, pal, ly, d) {
|
|
2981
|
+
for (const layer of this._layers) {
|
|
2982
|
+
try {
|
|
2983
|
+
layer.draw({
|
|
2984
|
+
ctx, // 2D context, already DPR-scaled — draw in CSS pixels
|
|
2985
|
+
layout: ly,
|
|
2986
|
+
palette: pal,
|
|
2987
|
+
data: d,
|
|
2988
|
+
view: this._view,
|
|
2989
|
+
timeToX: (t) => this.timeToX(t),
|
|
2990
|
+
xToTime: (x) => this.xToTime(x),
|
|
2991
|
+
priceToY: (p) => this.priceToY(p),
|
|
2992
|
+
yToPrice: (y) => this.yToPrice(y),
|
|
2993
|
+
});
|
|
2994
|
+
} catch (err) {
|
|
2995
|
+
console.warn('wick-chart: layer "' + layer.id + '" threw in draw', err);
|
|
2996
|
+
}
|
|
2997
|
+
}
|
|
2998
|
+
}
|
|
2999
|
+
|
|
3000
|
+
/** Ask layers, in order, whether one claims this pointerdown. */
|
|
3001
|
+
_layerHit(e, pt) {
|
|
3002
|
+
for (const layer of this._layers) {
|
|
3003
|
+
if (!layer.onPointer) continue;
|
|
3004
|
+
let claimed = false;
|
|
3005
|
+
try {
|
|
3006
|
+
claimed = layer.onPointer(this._layerPointerEvent(e, pt, 'down')) === true;
|
|
3007
|
+
} catch (err) {
|
|
3008
|
+
console.warn('wick-chart: layer "' + layer.id + '" threw in onPointer', err);
|
|
3009
|
+
}
|
|
3010
|
+
if (claimed) return layer;
|
|
3011
|
+
}
|
|
3012
|
+
return null;
|
|
3013
|
+
}
|
|
3014
|
+
|
|
3015
|
+
/** Deliver a pointer event to a claiming layer; never throws outward. */
|
|
3016
|
+
_routeLayer(layer, e, pt, type) {
|
|
3017
|
+
const ev = pt
|
|
3018
|
+
? this._layerPointerEvent(e, pt, type)
|
|
3019
|
+
: {
|
|
3020
|
+
type,
|
|
3021
|
+
x: null,
|
|
3022
|
+
y: null,
|
|
3023
|
+
pointerId: e.pointerId == null ? 0 : e.pointerId,
|
|
3024
|
+
button: 0,
|
|
3025
|
+
shiftKey: !!e.shiftKey,
|
|
3026
|
+
ctrlKey: !!e.ctrlKey,
|
|
3027
|
+
altKey: !!e.altKey,
|
|
3028
|
+
metaKey: !!e.metaKey,
|
|
3029
|
+
};
|
|
3030
|
+
try {
|
|
3031
|
+
if (layer.onPointer) layer.onPointer(ev);
|
|
3032
|
+
} catch (err) {
|
|
3033
|
+
console.warn('wick-chart: layer "' + layer.id + '" threw in onPointer', err);
|
|
3034
|
+
}
|
|
3035
|
+
}
|
|
3036
|
+
|
|
3037
|
+
_layerPointerEvent(e, pt, type) {
|
|
3038
|
+
return {
|
|
3039
|
+
type,
|
|
3040
|
+
x: pt.x,
|
|
3041
|
+
y: pt.y,
|
|
3042
|
+
pointerId: e.pointerId,
|
|
3043
|
+
button: e.button == null ? 0 : e.button,
|
|
3044
|
+
shiftKey: !!e.shiftKey,
|
|
3045
|
+
ctrlKey: !!e.ctrlKey,
|
|
3046
|
+
altKey: !!e.altKey,
|
|
3047
|
+
metaKey: !!e.metaKey,
|
|
3048
|
+
};
|
|
3049
|
+
}
|
|
3050
|
+
|
|
3051
|
+
/**
|
|
3052
|
+
* x-pixel for a bar time (ms or s, auto-detected). Extrapolates past the
|
|
3053
|
+
* last bar into future space using the median bar interval, so layers can
|
|
3054
|
+
* anchor trendlines to tomorrow, not just to history.
|
|
3055
|
+
* @param {number} time
|
|
3056
|
+
* @returns {number|null}
|
|
3057
|
+
*/
|
|
3058
|
+
timeToX(time) {
|
|
3059
|
+
const ly = this._ly;
|
|
3060
|
+
const d = this._data;
|
|
3061
|
+
if (!ly || !d.length || !isNum(time)) return null;
|
|
3062
|
+
const t = time < 1e12 ? time * 1000 : time;
|
|
3063
|
+
const last = d.length - 1;
|
|
3064
|
+
if (t >= d[last].time) {
|
|
3065
|
+
return this._xFor(last + (t - d[last].time) / (this._dt || HOUR));
|
|
3066
|
+
}
|
|
3067
|
+
const i = WickChart._indexForTime(d, t);
|
|
3068
|
+
if (i === 0 && t < d[0].time) {
|
|
3069
|
+
return this._xFor((t - d[0].time) / (this._dt || HOUR));
|
|
3070
|
+
}
|
|
3071
|
+
if (i > 0 && t < d[i].time) {
|
|
3072
|
+
// between two bars: fractional index
|
|
3073
|
+
const a = d[i - 1];
|
|
3074
|
+
const b = d[i];
|
|
3075
|
+
return this._xFor(i - 1 + (t - a.time) / (b.time - a.time || 1));
|
|
3076
|
+
}
|
|
3077
|
+
return this._xFor(i);
|
|
3078
|
+
}
|
|
3079
|
+
|
|
3080
|
+
/**
|
|
3081
|
+
* Bar time (ms) at an x-pixel — the inverse of timeToX, interpolating
|
|
3082
|
+
* between bars and extrapolating beyond both data edges.
|
|
3083
|
+
* @param {number} x
|
|
3084
|
+
* @returns {number|null}
|
|
3085
|
+
*/
|
|
3086
|
+
xToTime(x) {
|
|
3087
|
+
const ly = this._ly;
|
|
3088
|
+
const d = this._data;
|
|
3089
|
+
if (!ly || !d.length || !isNum(x)) return null;
|
|
3090
|
+
const idx = this._indexForX(x);
|
|
3091
|
+
const last = d.length - 1;
|
|
3092
|
+
if (idx >= last) return d[last].time + (idx - last) * (this._dt || HOUR);
|
|
3093
|
+
if (idx <= 0) return d[0].time + idx * (this._dt || HOUR);
|
|
3094
|
+
const i0 = Math.floor(idx);
|
|
3095
|
+
const a = d[i0];
|
|
3096
|
+
const b = d[Math.min(i0 + 1, last)];
|
|
3097
|
+
return a.time + (b.time - a.time) * (idx - i0);
|
|
3098
|
+
}
|
|
3099
|
+
|
|
3100
|
+
/**
|
|
3101
|
+
* y-pixel for a price in the main pane (current scale; log-aware).
|
|
3102
|
+
* Unclamped — values off-screen still map, layers decide how to clip.
|
|
3103
|
+
* @param {number} price
|
|
3104
|
+
* @returns {number|null}
|
|
3105
|
+
*/
|
|
3106
|
+
priceToY(price) {
|
|
3107
|
+
const ly = this._ly;
|
|
3108
|
+
const scale = this._lastScale;
|
|
3109
|
+
if (!ly || !scale || !isNum(price)) return null;
|
|
3110
|
+
const { min, max, useLog } = scale;
|
|
3111
|
+
const v = useLog ? Math.log10(Math.max(price, 1e-12)) : price;
|
|
3112
|
+
return ly.main.y0 + ((max - v) / (max - min)) * ly.main.h;
|
|
3113
|
+
}
|
|
3114
|
+
|
|
3115
|
+
/**
|
|
3116
|
+
* Price at a y-pixel in the main pane — the inverse of priceToY.
|
|
3117
|
+
* @param {number} y
|
|
3118
|
+
* @returns {number|null}
|
|
3119
|
+
*/
|
|
3120
|
+
yToPrice(y) {
|
|
3121
|
+
if (!isNum(y)) return null;
|
|
3122
|
+
return this._yToPrice(y);
|
|
3123
|
+
}
|
|
3124
|
+
|
|
2915
3125
|
/* ------------------------------------------------------------ *
|
|
2916
3126
|
* Interaction
|
|
2917
3127
|
* ------------------------------------------------------------ */
|
|
@@ -2927,6 +3137,19 @@ class WickChart extends HTMLElementBase {
|
|
|
2927
3137
|
this._canvas.setPointerCapture(e.pointerId);
|
|
2928
3138
|
const pt = this._localPoint(e);
|
|
2929
3139
|
this._pointers.set(e.pointerId, pt);
|
|
3140
|
+
if (this._layerClaim) return; // a layer owns a gesture: extra pointers are inert
|
|
3141
|
+
if (this._layers.length) {
|
|
3142
|
+
// layers get first claim on the pointer (hit-test their content);
|
|
3143
|
+
// a claim suppresses pinch/measure/brush/pan for this gesture
|
|
3144
|
+
const layer = this._layerHit(e, pt);
|
|
3145
|
+
if (layer) {
|
|
3146
|
+
this._pan = null;
|
|
3147
|
+
this._measuring = false;
|
|
3148
|
+
this._brushDrag = null;
|
|
3149
|
+
this._layerClaim = { layer, pointerId: e.pointerId };
|
|
3150
|
+
return;
|
|
3151
|
+
}
|
|
3152
|
+
}
|
|
2930
3153
|
if (this._pointers.size === 2) {
|
|
2931
3154
|
const [a, b] = [...this._pointers.values()];
|
|
2932
3155
|
const mid = { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 };
|
|
@@ -2967,6 +3190,12 @@ class WickChart extends HTMLElementBase {
|
|
|
2967
3190
|
}
|
|
2968
3191
|
|
|
2969
3192
|
_pointerMove(e) {
|
|
3193
|
+
if (this._layerClaim) {
|
|
3194
|
+
if (this._layerClaim.pointerId === e.pointerId) {
|
|
3195
|
+
this._routeLayer(this._layerClaim.layer, e, this._localPoint(e), 'move');
|
|
3196
|
+
}
|
|
3197
|
+
return; // inert while a layer owns the gesture
|
|
3198
|
+
}
|
|
2970
3199
|
const pt = this._localPoint(e);
|
|
2971
3200
|
if (this._pointers.has(e.pointerId)) this._pointers.set(e.pointerId, pt);
|
|
2972
3201
|
const ly = this._ly;
|
|
@@ -3026,6 +3255,14 @@ class WickChart extends HTMLElementBase {
|
|
|
3026
3255
|
}
|
|
3027
3256
|
|
|
3028
3257
|
_pointerUp(e) {
|
|
3258
|
+
const claim = this._layerClaim;
|
|
3259
|
+
if (claim && claim.pointerId === e.pointerId) {
|
|
3260
|
+
this._layerClaim = null;
|
|
3261
|
+
this._pointers.delete(e.pointerId);
|
|
3262
|
+
this._canvas.classList.remove('grabbing');
|
|
3263
|
+
this._routeLayer(claim.layer, e, this._localPoint(e), e.type === 'pointercancel' ? 'cancel' : 'up');
|
|
3264
|
+
return;
|
|
3265
|
+
}
|
|
3029
3266
|
const had = this._pointers.delete(e.pointerId);
|
|
3030
3267
|
if (this._pointers.size < 2) this._pinch = null;
|
|
3031
3268
|
if (this._pointers.size === 0) {
|
|
@@ -3113,6 +3350,13 @@ class WickChart extends HTMLElementBase {
|
|
|
3113
3350
|
const ly = this._ly;
|
|
3114
3351
|
if (!ly || !this._data.length) return;
|
|
3115
3352
|
this._stopPlayback();
|
|
3353
|
+
if (e.key === 'Escape' && this._layerClaim) {
|
|
3354
|
+
const claim = this._layerClaim;
|
|
3355
|
+
this._layerClaim = null;
|
|
3356
|
+
this._routeLayer(claim.layer, { pointerId: claim.pointerId }, null, 'cancel');
|
|
3357
|
+
this._invalidate();
|
|
3358
|
+
return;
|
|
3359
|
+
}
|
|
3116
3360
|
if (e.key === 'Escape' && (this._brushSel || this._brushDrag)) {
|
|
3117
3361
|
this.clearBrush();
|
|
3118
3362
|
return;
|
package/types/wick-chart.d.ts
CHANGED
|
@@ -172,6 +172,12 @@ declare class WickChart extends HTMLElementBase {
|
|
|
172
172
|
idxAtMid: number;
|
|
173
173
|
midX: number;
|
|
174
174
|
};
|
|
175
|
+
_layers: any[];
|
|
176
|
+
_layerClaim: {
|
|
177
|
+
layer: any;
|
|
178
|
+
pointerId: any;
|
|
179
|
+
};
|
|
180
|
+
_layerSeq: number;
|
|
175
181
|
_loadingMore: boolean;
|
|
176
182
|
_noMore: boolean;
|
|
177
183
|
_positions: any[];
|
|
@@ -623,6 +629,75 @@ declare class WickChart extends HTMLElementBase {
|
|
|
623
629
|
_updateLegend(): void;
|
|
624
630
|
/** Position P&L chips (top-right HTML overlay). */
|
|
625
631
|
_updateHud(): void;
|
|
632
|
+
/**
|
|
633
|
+
* Register a plugin layer. `layer.draw(api)` runs on every render, above
|
|
634
|
+
* chart content and under the crosshair; `layer.onPointer(pev)` is asked
|
|
635
|
+
* first on pointerdown and claims the gesture by returning true — the
|
|
636
|
+
* layer then receives that pointer's move/up/cancel events (plus a
|
|
637
|
+
* 'cancel' on Escape) and the chart suppresses its own pan/measure/brush
|
|
638
|
+
* for the duration.
|
|
639
|
+
* @param {{id?: string, draw: Function, onPointer?: Function}} layer
|
|
640
|
+
* @returns {object|null} the normalized layer handle (with `id`), or null
|
|
641
|
+
* if the layer was rejected (no draw fn, or 16 layers already added)
|
|
642
|
+
*/
|
|
643
|
+
addLayer(layer: {
|
|
644
|
+
id?: string;
|
|
645
|
+
draw: Function;
|
|
646
|
+
onPointer?: Function;
|
|
647
|
+
}): object | null;
|
|
648
|
+
/**
|
|
649
|
+
* Remove a layer added via addLayer (pass the returned handle or its id).
|
|
650
|
+
* @param {object|string} idOrLayer
|
|
651
|
+
* @returns {boolean} true if a layer was removed
|
|
652
|
+
*/
|
|
653
|
+
removeLayer(idOrLayer: object | string): boolean;
|
|
654
|
+
/** Ask for a repaint on the next frame (interactive layers call this). */
|
|
655
|
+
requestDraw(): void;
|
|
656
|
+
/** Paint every registered layer. Called from _render with live state. */
|
|
657
|
+
_drawLayers(ctx: any, pal: any, ly: any, d: any): void;
|
|
658
|
+
/** Ask layers, in order, whether one claims this pointerdown. */
|
|
659
|
+
_layerHit(e: any, pt: any): any;
|
|
660
|
+
/** Deliver a pointer event to a claiming layer; never throws outward. */
|
|
661
|
+
_routeLayer(layer: any, e: any, pt: any, type: any): void;
|
|
662
|
+
_layerPointerEvent(e: any, pt: any, type: any): {
|
|
663
|
+
type: any;
|
|
664
|
+
x: any;
|
|
665
|
+
y: any;
|
|
666
|
+
pointerId: any;
|
|
667
|
+
button: any;
|
|
668
|
+
shiftKey: boolean;
|
|
669
|
+
ctrlKey: boolean;
|
|
670
|
+
altKey: boolean;
|
|
671
|
+
metaKey: boolean;
|
|
672
|
+
};
|
|
673
|
+
/**
|
|
674
|
+
* x-pixel for a bar time (ms or s, auto-detected). Extrapolates past the
|
|
675
|
+
* last bar into future space using the median bar interval, so layers can
|
|
676
|
+
* anchor trendlines to tomorrow, not just to history.
|
|
677
|
+
* @param {number} time
|
|
678
|
+
* @returns {number|null}
|
|
679
|
+
*/
|
|
680
|
+
timeToX(time: number): number | null;
|
|
681
|
+
/**
|
|
682
|
+
* Bar time (ms) at an x-pixel — the inverse of timeToX, interpolating
|
|
683
|
+
* between bars and extrapolating beyond both data edges.
|
|
684
|
+
* @param {number} x
|
|
685
|
+
* @returns {number|null}
|
|
686
|
+
*/
|
|
687
|
+
xToTime(x: number): number | null;
|
|
688
|
+
/**
|
|
689
|
+
* y-pixel for a price in the main pane (current scale; log-aware).
|
|
690
|
+
* Unclamped — values off-screen still map, layers decide how to clip.
|
|
691
|
+
* @param {number} price
|
|
692
|
+
* @returns {number|null}
|
|
693
|
+
*/
|
|
694
|
+
priceToY(price: number): number | null;
|
|
695
|
+
/**
|
|
696
|
+
* Price at a y-pixel in the main pane — the inverse of priceToY.
|
|
697
|
+
* @param {number} y
|
|
698
|
+
* @returns {number|null}
|
|
699
|
+
*/
|
|
700
|
+
yToPrice(y: number): number | null;
|
|
626
701
|
_localPoint(e: any): {
|
|
627
702
|
x: number;
|
|
628
703
|
y: number;
|