acttrader-charts 1.4.0-beta.4 → 1.4.0-beta.5
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 +16 -0
- package/dist/index.cjs +107 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +20 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +107 -6
- package/dist/index.js.map +1 -1
- package/dist/webview/chart.html +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2266,6 +2266,22 @@ Two details worth knowing:
|
|
|
2266
2266
|
in hand. It deletes via select-then-delete, so it shares the undo history with
|
|
2267
2267
|
every other delete.
|
|
2268
2268
|
|
|
2269
|
+
### Touch gestures (iOS / Android)
|
|
2270
|
+
|
|
2271
|
+
| Gesture | Effect |
|
|
2272
|
+
| --- | --- |
|
|
2273
|
+
| One-finger drag on the chart | Scrolls through time (with momentum) |
|
|
2274
|
+
| Pinch | Zooms the time axis |
|
|
2275
|
+
| One-finger drag along the time (x) axis | Zooms the time axis: drag right to show more bars, left to show fewer |
|
|
2276
|
+
| One-finger drag on the price (y) axis | Zooms the price axis |
|
|
2277
|
+
| One-finger **double tap** on the chart or price axis | Zooms the price axis in one step around the tapped price |
|
|
2278
|
+
| One-finger **double tap, then drag** up/down | Zooms the price axis continuously: up zooms in, down zooms out (the same direction as the price-axis drag) |
|
|
2279
|
+
| Long press | Shows the crosshair |
|
|
2280
|
+
|
|
2281
|
+
Once the view has moved away from its default, a round **Reset view** button
|
|
2282
|
+
appears just above the time axis. It is shown even when `showUI: false`, so a
|
|
2283
|
+
bare chart with no toolbar always has a way back. No configuration is needed.
|
|
2284
|
+
|
|
2269
2285
|
### Value tooltip on long press
|
|
2270
2286
|
|
|
2271
2287
|
```ts
|
package/dist/index.cjs
CHANGED
|
@@ -37080,6 +37080,11 @@ function initialVisibleBars(mobileBarDivisor = 2, forceApply = false, defaultBar
|
|
|
37080
37080
|
}
|
|
37081
37081
|
var LOOKAHEAD = 4;
|
|
37082
37082
|
var LONG_PRESS_CANCEL_ARM_MS = 300;
|
|
37083
|
+
var DOUBLE_TAP_MS = 300;
|
|
37084
|
+
var DOUBLE_TAP_SLOP_PX = 30;
|
|
37085
|
+
var TAP_MAX_MS = 250;
|
|
37086
|
+
var TAP_MAX_MOVE_PX = 10;
|
|
37087
|
+
var DOUBLE_TAP_ZOOM_STEP = 0.7;
|
|
37083
37088
|
function lastIndexAtOrBefore(bars, time) {
|
|
37084
37089
|
let lo = 0, hi = bars.length - 1, ans = -1;
|
|
37085
37090
|
while (lo <= hi) {
|
|
@@ -37382,6 +37387,16 @@ var _ChartEngine = class _ChartEngine {
|
|
|
37382
37387
|
*/
|
|
37383
37388
|
this._viewportPersistTimer = null;
|
|
37384
37389
|
this.axisDrag = null;
|
|
37390
|
+
/**
|
|
37391
|
+
* One-finger double-tap zoom on the price scale (the TradingView app's
|
|
37392
|
+
* gesture). Second tap down arms it; dragging up/down zooms Y continuously,
|
|
37393
|
+
* and lifting without a drag steps one zoom-in around the tapped price.
|
|
37394
|
+
*/
|
|
37395
|
+
this.tapZoom = null;
|
|
37396
|
+
/** Where and when the last quick one-finger tap ended — the first half of a double tap. */
|
|
37397
|
+
this._lastTap = null;
|
|
37398
|
+
/** Down position/time of the current one-finger touch, to tell a tap from a drag. */
|
|
37399
|
+
this._touchDown = null;
|
|
37385
37400
|
/** Per-instance sub-pane Y-scale factors, keyed by ActiveIndicatorState.instanceId. */
|
|
37386
37401
|
this.subPaneScaleFactors = /* @__PURE__ */ new Map();
|
|
37387
37402
|
this._mobileCrosshairActive = false;
|
|
@@ -37999,6 +38014,16 @@ var _ChartEngine = class _ChartEngine {
|
|
|
37999
38014
|
const y = touch.clientY - rect.top;
|
|
38000
38015
|
const { chartW, priceH } = this.getChartLayout();
|
|
38001
38016
|
const priceRange = this.getScaledPriceRange(this.viewport);
|
|
38017
|
+
const now = performance.now();
|
|
38018
|
+
const lastTap = this._lastTap;
|
|
38019
|
+
this._lastTap = null;
|
|
38020
|
+
this._touchDown = null;
|
|
38021
|
+
if (lastTap && now - lastTap.at < DOUBLE_TAP_MS && Math.hypot(x - lastTap.x, y - lastTap.y) < DOUBLE_TAP_SLOP_PX && y < priceH && !this._mobileCrosshairActive) {
|
|
38022
|
+
e.preventDefault();
|
|
38023
|
+
this.panHandler.cancel();
|
|
38024
|
+
this.startTapZoom(y, priceH);
|
|
38025
|
+
return;
|
|
38026
|
+
}
|
|
38002
38027
|
const axisTapHit = hitTestAxisTagCluster(x, y, this.axisTagClusterAreas);
|
|
38003
38028
|
if (axisTapHit) {
|
|
38004
38029
|
e.preventDefault();
|
|
@@ -38015,6 +38040,7 @@ var _ChartEngine = class _ChartEngine {
|
|
|
38015
38040
|
e.preventDefault();
|
|
38016
38041
|
if (y <= priceH) {
|
|
38017
38042
|
this.axisDrag = { axis: "price", startPos: y, startSize: this.priceScaleFactor };
|
|
38043
|
+
this._touchDown = { x, y, at: now };
|
|
38018
38044
|
} else {
|
|
38019
38045
|
const subPaneStates = this.activeIndicators.filter(
|
|
38020
38046
|
(s) => s.indicator.pane === "sub"
|
|
@@ -38197,6 +38223,7 @@ var _ChartEngine = class _ChartEngine {
|
|
|
38197
38223
|
}
|
|
38198
38224
|
}
|
|
38199
38225
|
}
|
|
38226
|
+
this._touchDown = { x, y, at: now };
|
|
38200
38227
|
this.drawingManager.selectDrawing(null);
|
|
38201
38228
|
this.hideMobileDrawingFlyout();
|
|
38202
38229
|
if (this.expandedClusters.size > 0) {
|
|
@@ -38234,6 +38261,11 @@ var _ChartEngine = class _ChartEngine {
|
|
|
38234
38261
|
const point = this.touchToDrawingPoint(e.touches[0]);
|
|
38235
38262
|
const tp = this.touchToPixels(e.touches[0]);
|
|
38236
38263
|
this._lastDrawingTouchPoint = point;
|
|
38264
|
+
if (this.tapZoom) {
|
|
38265
|
+
e.preventDefault();
|
|
38266
|
+
this.moveTapZoom(tp.y);
|
|
38267
|
+
return;
|
|
38268
|
+
}
|
|
38237
38269
|
if (this.axisDrag) {
|
|
38238
38270
|
e.preventDefault();
|
|
38239
38271
|
const touch = e.touches[0];
|
|
@@ -38266,6 +38298,7 @@ var _ChartEngine = class _ChartEngine {
|
|
|
38266
38298
|
startIndex: newStart,
|
|
38267
38299
|
endIndex: newStart + newVisible
|
|
38268
38300
|
};
|
|
38301
|
+
this._targetViewport = { ...this.viewport };
|
|
38269
38302
|
}
|
|
38270
38303
|
this.scheduleRender();
|
|
38271
38304
|
return;
|
|
@@ -38409,6 +38442,9 @@ var _ChartEngine = class _ChartEngine {
|
|
|
38409
38442
|
* back to its start; an in-progress price drag is also cancelled defensively.
|
|
38410
38443
|
*/
|
|
38411
38444
|
this.onDrawingTouchCancel = (_e) => {
|
|
38445
|
+
this._touchDown = null;
|
|
38446
|
+
this._lastTap = null;
|
|
38447
|
+
if (this.tapZoom) this.endTapZoom();
|
|
38412
38448
|
if (this._longPressTimer !== null) {
|
|
38413
38449
|
clearTimeout(this._longPressTimer);
|
|
38414
38450
|
this._longPressTimer = null;
|
|
@@ -38442,6 +38478,22 @@ var _ChartEngine = class _ChartEngine {
|
|
|
38442
38478
|
this._longPressTimer = null;
|
|
38443
38479
|
this._longPressPoint = null;
|
|
38444
38480
|
}
|
|
38481
|
+
if (this.tapZoom) {
|
|
38482
|
+
this.endTapZoom();
|
|
38483
|
+
return;
|
|
38484
|
+
}
|
|
38485
|
+
const down = this._touchDown;
|
|
38486
|
+
this._touchDown = null;
|
|
38487
|
+
const lifted = _e.changedTouches[0];
|
|
38488
|
+
if (down && lifted && _e.touches.length === 0) {
|
|
38489
|
+
const rect = this._cachedDrawRect ?? this.drawCanvas.getBoundingClientRect();
|
|
38490
|
+
const ux = lifted.clientX - rect.left;
|
|
38491
|
+
const uy = lifted.clientY - rect.top;
|
|
38492
|
+
const now = performance.now();
|
|
38493
|
+
if (now - down.at < TAP_MAX_MS && Math.hypot(ux - down.x, uy - down.y) < TAP_MAX_MOVE_PX) {
|
|
38494
|
+
this._lastTap = { x: down.x, y: down.y, at: now };
|
|
38495
|
+
}
|
|
38496
|
+
}
|
|
38445
38497
|
if (this._pendingTradeDrag) {
|
|
38446
38498
|
const { level } = this._pendingTradeDrag;
|
|
38447
38499
|
this._pendingTradeDrag = null;
|
|
@@ -39696,12 +39748,12 @@ var _ChartEngine = class _ChartEngine {
|
|
|
39696
39748
|
this.applyScrollToEndBtnStyles();
|
|
39697
39749
|
this.scrollToEndBtn.addEventListener("click", () => this.scrollToEnd());
|
|
39698
39750
|
this.canvasWrap.appendChild(this.scrollToEndBtn);
|
|
39699
|
-
this.resetViewBtn = document.createElement("button");
|
|
39700
|
-
this.resetViewBtn.title = "Reset view";
|
|
39701
|
-
this.applyResetViewBtnStyles();
|
|
39702
|
-
this.resetViewBtn.addEventListener("click", () => this.resetView());
|
|
39703
|
-
this.canvasWrap.appendChild(this.resetViewBtn);
|
|
39704
39751
|
}
|
|
39752
|
+
this.resetViewBtn = document.createElement("button");
|
|
39753
|
+
this.resetViewBtn.title = "Reset view";
|
|
39754
|
+
this.applyResetViewBtnStyles();
|
|
39755
|
+
this.resetViewBtn.addEventListener("click", () => this.resetView());
|
|
39756
|
+
this.canvasWrap.appendChild(this.resetViewBtn);
|
|
39705
39757
|
this.tradeDragHandler = new TradeDragHandler();
|
|
39706
39758
|
this.orderTimeDragHandler = new OrderLineTimeDragHandler();
|
|
39707
39759
|
if (config.onLevelDragEnd) {
|
|
@@ -39975,7 +40027,7 @@ var _ChartEngine = class _ChartEngine {
|
|
|
39975
40027
|
},
|
|
39976
40028
|
() => this.dataStore.length,
|
|
39977
40029
|
() => this.scheduleRender(),
|
|
39978
|
-
() => this.drawingManager.getActiveTool() !== null || this.drawingManager.isDraggingDrawing() || this.drawingManager.isResizingDrawing() || this.separatorDrag !== null || this.axisDrag !== null || (this.tradeDragHandler?.active ?? false) || (this.orderTimeDragHandler?.active ?? false) || this._pendingTradeDrag !== null || this._mobileCrosshairActive,
|
|
40030
|
+
() => this.drawingManager.getActiveTool() !== null || this.drawingManager.isDraggingDrawing() || this.drawingManager.isResizingDrawing() || this.separatorDrag !== null || this.axisDrag !== null || this.tapZoom !== null || (this.tradeDragHandler?.active ?? false) || (this.orderTimeDragHandler?.active ?? false) || this._pendingTradeDrag !== null || this._mobileCrosshairActive,
|
|
39979
40031
|
(deltaY) => {
|
|
39980
40032
|
if (this.priceScaleFactor === 1) return;
|
|
39981
40033
|
const { priceH } = this.getChartLayout();
|
|
@@ -46886,6 +46938,55 @@ var _ChartEngine = class _ChartEngine {
|
|
|
46886
46938
|
const all = this.dataStore.raw;
|
|
46887
46939
|
return all[all.length - 1]?.close ?? 0;
|
|
46888
46940
|
}
|
|
46941
|
+
/**
|
|
46942
|
+
* Arms a one-finger double-tap zoom at pane-local `y`. Locks the current
|
|
46943
|
+
* auto-fit range as the manual base so the zoom is relative to what is on
|
|
46944
|
+
* screen, and anchors on the tapped price so it stays under the finger.
|
|
46945
|
+
*/
|
|
46946
|
+
startTapZoom(y, priceH) {
|
|
46947
|
+
if (this._longPressTimer !== null) {
|
|
46948
|
+
clearTimeout(this._longPressTimer);
|
|
46949
|
+
this._longPressTimer = null;
|
|
46950
|
+
this._longPressPoint = null;
|
|
46951
|
+
}
|
|
46952
|
+
const displayed = this._displayedPriceRange ?? this.getScaledPriceRange(this.viewport);
|
|
46953
|
+
if (!this._lockedAutoFitBase) this._lockedAutoFitBase = { ...displayed };
|
|
46954
|
+
const t = Math.max(0, Math.min(1, y / Math.max(1, priceH)));
|
|
46955
|
+
this.tapZoom = {
|
|
46956
|
+
startY: y,
|
|
46957
|
+
startFactor: this.priceScaleFactor,
|
|
46958
|
+
anchorPrice: displayed.max - t * (displayed.max - displayed.min),
|
|
46959
|
+
moved: false
|
|
46960
|
+
};
|
|
46961
|
+
}
|
|
46962
|
+
/** Sets the price-scale factor, keeping the tap-zoom anchor price at the same pixel. */
|
|
46963
|
+
applyTapZoomFactor(factor) {
|
|
46964
|
+
const tz = this.tapZoom;
|
|
46965
|
+
const base = this._lockedAutoFitBase;
|
|
46966
|
+
if (!tz || !base) return;
|
|
46967
|
+
const clamped = Math.max(0.1, Math.min(10, factor));
|
|
46968
|
+
const baseMid = (base.min + base.max) / 2;
|
|
46969
|
+
const mid = baseMid + this.pricePanOffset;
|
|
46970
|
+
const newMid = tz.anchorPrice + (mid - tz.anchorPrice) * (clamped / this.priceScaleFactor);
|
|
46971
|
+
this.priceScaleFactor = clamped;
|
|
46972
|
+
this.pricePanOffset = newMid - baseMid;
|
|
46973
|
+
this.scheduleRender();
|
|
46974
|
+
}
|
|
46975
|
+
moveTapZoom(y) {
|
|
46976
|
+
const tz = this.tapZoom;
|
|
46977
|
+
if (!tz) return;
|
|
46978
|
+
const dy = y - tz.startY;
|
|
46979
|
+
if (!tz.moved && Math.abs(dy) < TAP_MAX_MOVE_PX) return;
|
|
46980
|
+
tz.moved = true;
|
|
46981
|
+
this.applyTapZoomFactor(tz.startFactor * Math.pow(1.01, dy));
|
|
46982
|
+
}
|
|
46983
|
+
endTapZoom() {
|
|
46984
|
+
const tz = this.tapZoom;
|
|
46985
|
+
if (!tz) return;
|
|
46986
|
+
if (!tz.moved) this.applyTapZoomFactor(tz.startFactor * DOUBLE_TAP_ZOOM_STEP);
|
|
46987
|
+
this.tapZoom = null;
|
|
46988
|
+
this.scheduleViewportPersist();
|
|
46989
|
+
}
|
|
46889
46990
|
resetPriceAxis() {
|
|
46890
46991
|
this.priceScaleFactor = 1;
|
|
46891
46992
|
this.pricePanOffset = 0;
|