acttrader-charts 1.3.0-beta.19 → 1.3.0-beta.20
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 +27 -6
- package/dist/{MACD-DDqzsv_V.d.cts → MACD-BM9VDMeg.d.cts} +23 -9
- package/dist/{MACD-DDqzsv_V.d.ts → MACD-BM9VDMeg.d.ts} +23 -9
- package/dist/index.cjs +160 -19
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +33 -6
- package/dist/index.d.ts +33 -6
- package/dist/index.js +160 -19
- package/dist/index.js.map +1 -1
- package/dist/indicators/index.d.cts +2 -2
- package/dist/indicators/index.d.ts +2 -2
- package/dist/webview/chart.html +3 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -346,7 +346,7 @@ When `enableTrading` is on and live BID/ASK data is streaming, hovering / activa
|
|
|
346
346
|
|
|
347
347
|
### Horizontal (time-axis) order-line dragging
|
|
348
348
|
|
|
349
|
-
Opt-in via `features.orderLineTimeDrag` (off by default; enable it per broker from the host app — e.g. only for Hankotrade users). When enabled, any level flagged `timeDraggable: true` can have its **info-box badge** dragged left/right to re-anchor the order to a different candle.
|
|
349
|
+
Opt-in via `features.orderLineTimeDrag` (off by default; enable it per broker from the host app — e.g. only for Hankotrade users). When enabled, any level flagged `timeDraggable: true` — open positions **and pending orders** alike — can have its **info-box badge** dragged left/right to re-anchor the order to a different candle. During a horizontal drag the **price line stays locked** — only the time anchor moves. A fixed-price position's badge grabs horizontally at once (`grab` / `grabbing` cursor). A pending order (or entry-editable position) keeps its price drag: its badge shows a `move` cursor and the **first movement decides the axis** — sideways moves the time anchor, up/down moves the entry price exactly as before, and the full-width line still drags vertically.
|
|
350
350
|
|
|
351
351
|
```ts
|
|
352
352
|
const chart = new ChartEngine({
|
|
@@ -369,6 +369,7 @@ chart.on('orderLineMoved', ({ label, fromTimestamp, toTimestamp, data }) => {
|
|
|
369
369
|
```
|
|
370
370
|
|
|
371
371
|
- **Bounds:** the badge is clamped to the visible viewport — it can't be dragged past the left/right edge.
|
|
372
|
+
- **Pending orders:** flag them `timeDraggable: true` too. The pill follows the drag direction — a sideways pull re-anchors the order in time (price locked, `orderLineMoved` on release), a vertical pull moves the entry price and its SL/TP brackets as usual (`tradeLevelDrag` / `tradeLevelEdit`). The axis is fixed after about 6px of movement and does not change mid-drag. Touch follows the same rule, and a tap that never moves still opens the edit form.
|
|
372
373
|
- **Snap:** controlled by `orderLineDragSnap` (default `true`); set `false` to keep the exact released time.
|
|
373
374
|
- **Escape / touch-cancel:** reverts the badge to its starting candle without emitting `orderLineMoved`.
|
|
374
375
|
- **Stays put after release:** the chart holds the dropped position across `setLevels` refreshes until you echo the new `timestamp` back (auto-released on match).
|
|
@@ -530,9 +531,10 @@ use `{symbol}` and `{count}`. The maths behind the pill is exported as
|
|
|
530
531
|
| `tradeLevelBadgeAnchor` | | `"auto"` | `"left"` homes the tag at the plot's left edge like an axis label, whatever the open price or time anchor, so it never covers candles until the trader drags it. With `features.orderLineTimeDrag` the tag stays draggable along the time axis and the drop is remembered as before |
|
|
531
532
|
| `tfcEnabled` | | `true` | Enable the TFC toggle button in the top bar. When `false`, TFC is completely disabled — the toggle button is hidden and all trade levels, draft orders, and the floating trade button are suppressed |
|
|
532
533
|
| `crosshairEnabled` | | `true` | Draw the mouse crosshair. `false` hides it **and** the floating "place order at this price" button that rides on its horizontal line, ignores mirrored `setCrosshair()` calls and keeps the mobile long-press crosshair from arming. Trade levels, drawings and TFC are unaffected. Runtime: `chart.setCrosshairEnabled(enabled)` |
|
|
533
|
-
| `
|
|
534
|
+
| `enableCrossHairHeader` | | `false` | Crosshair on/off switch in the header (advanced toolbar, default top bar and the compact per-pane strip): the reference `rounded-sm` button. The crosshair is on by default, so the icon starts tinted; a click hides the crosshair (and the trade button riding on it) and drops the icon to its plain state, the next click brings both back. Calls `setCrosshairEnabled()` and emits `crosshairToggle` so the host can persist the choice — see [Header crosshair switch](#header-crosshair-switch--enablecrosshairheader) |
|
|
535
|
+
| `enableCrosshairToggle` | | `false` | **Deprecated** — alias of `enableCrossHairHeader`; either flag renders the switch |
|
|
534
536
|
| `headerOverflowMenu` | | `false` | Advanced header only: the reference Hanko header. A 32px bar with six lowercase timeframe pills (`1m 5m 15m 1h 4h 1d`) and a ⌄ for the rest, then crosshair · P/L · Layout (grid icon) · ⋯ · fullscreen at 24px. Draw, Chart type, Indicators, Compare, Snapshot and Chart settings fold into the ⋯ menu; each keeps its behaviour |
|
|
535
|
-
| `features.orderLineTimeDrag` | | `false` | Enable **horizontal (time-axis) order-line dragging**. A level with `timeDraggable: true` can have its info-box badge dragged left/right to re-anchor the order to a different candle — **price stays locked
|
|
537
|
+
| `features.orderLineTimeDrag` | | `false` | Enable **horizontal (time-axis) order-line dragging**. A level with `timeDraggable: true` (position or pending order) can have its info-box badge dragged left/right to re-anchor the order to a different candle — **price stays locked** while dragging sideways. Pending orders keep their vertical price drag: the badge's first movement picks the axis. Emits `orderLineMoved` on release. Off by default; intended to be enabled per broker (e.g. Hankotrade) by the host app |
|
|
536
538
|
| `orderLineDragSnap` | | `true` | When horizontal dragging is enabled, snap the badge to the nearest candle on release. Set `false` to keep the exact released time |
|
|
537
539
|
| `orderLineAnchorPersistence` | | `true` | When horizontal dragging is enabled, remember where each badge was dropped in `localStorage` (key `finchart.orderLineTimeAnchors`, keyed by level label) so it comes back to the same candle after a reload. A persisted anchor overrides the level's `timestamp` until the badge is dragged again. Set `false` to persist anchors yourself |
|
|
538
540
|
| `orderLineDefaultAnchor` | | `'timestamp'` | Where an un-dragged `timeDraggable` badge sits: `'timestamp'` = over the candle at the level's `timestamp`; `'center'` = the chart's horizontal center (ignores `timestamp`), so a new market order lands mid-chart. Non-draggable levels always honor `timestamp` |
|
|
@@ -617,7 +619,7 @@ const chart = new ChartEngine({
|
|
|
617
619
|
| Flag | Default | What it changes |
|
|
618
620
|
|---|---|---|
|
|
619
621
|
| `enableForecasting` | `false` | The reworked drawing tools, as one switch — see below |
|
|
620
|
-
| `orderLineTimeDrag` | `false` | Horizontal (time-axis) order-line dragging — see [Horizontal order-line dragging](#horizontal-time-axis-order-line-dragging) |
|
|
622
|
+
| `orderLineTimeDrag` | `false` | Horizontal (time-axis) order-line dragging for positions and pending orders — see [Horizontal order-line dragging](#horizontal-time-axis-order-line-dragging) |
|
|
621
623
|
| `pnlPill` | `false` | Floating on-chart P/L pill — count and summed P/L of the positions on the chart — see [On-chart P/L pill](#on-chart-pl-pill--featurespnlpill) |
|
|
622
624
|
| `tradeActionBar` | `false` | The trade button opens a segmented `Alert · Pending · SL · TP · TSL · ×` bar at the clicked price instead of the two-row Buy/Sell popup — see [Trade action bar](#trade-action-bar--featurestradeactionbar) |
|
|
623
625
|
|
|
@@ -778,7 +780,7 @@ new ChartEngine({
|
|
|
778
780
|
| `topBar.series` | `area` | `'Area'` | |
|
|
779
781
|
| `topBar` | `indicatorsBtn` | `'Indicators'` | Indicators button (no active) |
|
|
780
782
|
| `topBar` | `indicatorsBtnActive` | `'Indicators ({count})'` | `{count}` replaced at runtime |
|
|
781
|
-
| `topBar` | `disableCrosshairTitle` | `'Disable crosshair'` | Header crosshair switch tooltip while on (`
|
|
783
|
+
| `topBar` | `disableCrosshairTitle` | `'Disable crosshair'` | Header crosshair switch tooltip while on (`enableCrossHairHeader`) |
|
|
782
784
|
| `topBar` | `enableCrosshairTitle` | `'Enable crosshair'` | Header crosshair switch tooltip while off |
|
|
783
785
|
| `topBar` | `pnlPillBtn` | `'P/L'` | Header P/L pill switch label (`features.pnlPill`) |
|
|
784
786
|
| `topBar` | `hidePnlPillTitle` | `'Hide P/L pill for {symbol}'` | P/L switch tooltip while the pill is shown |
|
|
@@ -984,6 +986,25 @@ crosshair is (`crosshairEnabled`, default `true`). Hosts with a crosshair on/off
|
|
|
984
986
|
should call `setCrosshairEnabled()` rather than painting the crosshair transparent — the
|
|
985
987
|
latter leaves the button riding an invisible line.
|
|
986
988
|
|
|
989
|
+
### Header crosshair switch — `enableCrossHairHeader`
|
|
990
|
+
|
|
991
|
+
```ts
|
|
992
|
+
const chart = new ChartEngine({
|
|
993
|
+
container,
|
|
994
|
+
enableCrossHairHeader: true, // crosshair icon in the header; the crosshair itself starts on
|
|
995
|
+
});
|
|
996
|
+
chart.on('crosshairToggle', ({ enabled }) => saveUserPref('crosshair', enabled));
|
|
997
|
+
```
|
|
998
|
+
|
|
999
|
+
With the flag on, a crosshair icon sits in the header's right cluster (advanced toolbar and
|
|
1000
|
+
default top bar) or at the end of the compact per-pane strip (`headerLayout: 'compact'`). The chart crosshair is shown as usual on load and the icon is tinted. Clicking
|
|
1001
|
+
the icon hides the crosshair — and the floating trade button that rides on it — and the icon
|
|
1002
|
+
drops to its plain state; clicking again brings both back. Each click goes through
|
|
1003
|
+
`setCrosshairEnabled()` and emits `crosshairToggle`, so a host can persist the choice and seed
|
|
1004
|
+
it on the next load with `crosshairEnabled: false`. `enableCrosshairToggle` is the deprecated
|
|
1005
|
+
name of the same flag and still works. Native wrappers get the same switch via their
|
|
1006
|
+
`enableCrossHairHeader` init option, the `onCrosshairToggle` callback and `setCrosshairEnabled()`.
|
|
1007
|
+
|
|
987
1008
|
### Viewport
|
|
988
1009
|
|
|
989
1010
|
```ts
|
|
@@ -1332,7 +1353,7 @@ chart.on('tradeLevelEditCancelled', ({ label, type, isFullscreen }) => {}); // E
|
|
|
1332
1353
|
chart.on('draftInitiated', ({ side, price, orderType, isFullscreen }) => {}); // new draft order shown — open buy/sell form
|
|
1333
1354
|
chart.on('draftCancelled', ({ label, isFullscreen }) => {}); // draft order dismissed without confirming
|
|
1334
1355
|
chart.on('tfcToggle', ({ enabled }) => {}); // TFC toggled on or off via top bar button or setTfcActive()
|
|
1335
|
-
chart.on('crosshairToggle', ({ enabled }) => {}); // crosshair switched on or off — header switch (
|
|
1356
|
+
chart.on('crosshairToggle', ({ enabled }) => {}); // crosshair switched on or off — header switch (enableCrossHairHeader) or setCrosshairEnabled(); persist it here
|
|
1336
1357
|
chart.on('pnlPillCloseAll', ({ count, pnl, labels, data, isFullscreen }) => {}); // the on-chart P/L pill's ✕ — close these positions (features.pnlPill)
|
|
1337
1358
|
chart.on('pnlPillToggle', ({ visible }) => {}); // the header's P/L switch showed or hid the pill — persist it here (features.pnlPill)
|
|
1338
1359
|
chart.on('bulkLevelsRequested', ({ kind, price, source, isFullscreen }) => {}); // action-bar chip (price) or the P/L pill's ◎ (price null) — open your Bulk Levels dialog for `kind`
|
|
@@ -1289,11 +1289,18 @@ interface ChartConfig {
|
|
|
1289
1289
|
*/
|
|
1290
1290
|
enableSnapshot?: boolean;
|
|
1291
1291
|
/**
|
|
1292
|
-
* Put a crosshair on/off switch in the header (advanced toolbar
|
|
1293
|
-
*
|
|
1294
|
-
* the
|
|
1295
|
-
*
|
|
1296
|
-
*
|
|
1292
|
+
* Put a crosshair on/off switch in the header (advanced toolbar, default top
|
|
1293
|
+
* bar and the compact per-pane strip). The crosshair itself stays on by default (`crosshairEnabled`), so
|
|
1294
|
+
* the icon starts tinted; clicking it calls `setCrosshairEnabled(false)` — the
|
|
1295
|
+
* icon drops to its plain state and the crosshair (with the floating "place
|
|
1296
|
+
* order at this price" button that rides on it) disappears until the icon is
|
|
1297
|
+
* clicked again. Emits `crosshairToggle` for the host to persist the choice.
|
|
1298
|
+
* Default: false.
|
|
1299
|
+
*/
|
|
1300
|
+
enableCrossHairHeader?: boolean;
|
|
1301
|
+
/**
|
|
1302
|
+
* @deprecated Use `enableCrossHairHeader`. Kept as an alias so existing
|
|
1303
|
+
* consumers keep their header switch — either flag being `true` renders it.
|
|
1297
1304
|
*/
|
|
1298
1305
|
enableCrosshairToggle?: boolean;
|
|
1299
1306
|
/**
|
|
@@ -1328,6 +1335,9 @@ interface ChartConfig {
|
|
|
1328
1335
|
* Enable horizontal (time-axis) order-line dragging: a level whose `timeDraggable`
|
|
1329
1336
|
* is true can have its info-box badge dragged left/right to re-anchor the order to
|
|
1330
1337
|
* a different candle (price stays locked), emitting `orderLineMoved` on release.
|
|
1338
|
+
* Levels that can also be price-dragged (pending orders, entry-editable positions)
|
|
1339
|
+
* keep that: the first movement of a badge grab decides the axis — sideways moves
|
|
1340
|
+
* the time anchor, up/down moves the price as before.
|
|
1331
1341
|
* This is the broker-gating seam — hosts (e.g. the Hankotrade app) enable it only
|
|
1332
1342
|
* for their flagged users. When `false` / omitted, behavior is unchanged.
|
|
1333
1343
|
* Default: false.
|
|
@@ -1881,7 +1891,7 @@ type ChartEventMap = {
|
|
|
1881
1891
|
tfcToggle: {
|
|
1882
1892
|
enabled: boolean;
|
|
1883
1893
|
};
|
|
1884
|
-
/** Crosshair switched on or off — via the header switch (`
|
|
1894
|
+
/** Crosshair switched on or off — via the header switch (`enableCrossHairHeader`) or `setCrosshairEnabled()`. */
|
|
1885
1895
|
crosshairToggle: {
|
|
1886
1896
|
enabled: boolean;
|
|
1887
1897
|
};
|
|
@@ -2152,7 +2162,9 @@ interface PositionLevel {
|
|
|
2152
2162
|
/**
|
|
2153
2163
|
* When true (and `ChartConfig.features.orderLineTimeDrag` is enabled), the badge can be
|
|
2154
2164
|
* dragged horizontally to re-anchor the order to a different candle (price stays locked).
|
|
2155
|
-
* Emits `orderLineMoved` on release.
|
|
2165
|
+
* Emits `orderLineMoved` on release. An `entryPriceEditable` position keeps its vertical
|
|
2166
|
+
* price drag too: the badge follows the drag direction (sideways = time, up/down = price).
|
|
2167
|
+
* Default: false.
|
|
2156
2168
|
*/
|
|
2157
2169
|
timeDraggable?: boolean;
|
|
2158
2170
|
/**
|
|
@@ -2208,8 +2220,10 @@ interface PendingOrderLevel {
|
|
|
2208
2220
|
timestamp?: number;
|
|
2209
2221
|
/**
|
|
2210
2222
|
* When true (and `ChartConfig.features.orderLineTimeDrag` is enabled), the badge can be
|
|
2211
|
-
* dragged horizontally to re-anchor the order to a different candle
|
|
2212
|
-
*
|
|
2223
|
+
* dragged horizontally to re-anchor the order to a different candle, emitting
|
|
2224
|
+
* `orderLineMoved` on release. The order stays price-draggable: the badge follows the
|
|
2225
|
+
* drag direction — sideways moves the time anchor (price locked), up/down moves the
|
|
2226
|
+
* entry price exactly as without this flag. Default: false.
|
|
2213
2227
|
*/
|
|
2214
2228
|
timeDraggable?: boolean;
|
|
2215
2229
|
/** Your original order object. Returned as-is in events. The library never inspects this. */
|
|
@@ -1289,11 +1289,18 @@ interface ChartConfig {
|
|
|
1289
1289
|
*/
|
|
1290
1290
|
enableSnapshot?: boolean;
|
|
1291
1291
|
/**
|
|
1292
|
-
* Put a crosshair on/off switch in the header (advanced toolbar
|
|
1293
|
-
*
|
|
1294
|
-
* the
|
|
1295
|
-
*
|
|
1296
|
-
*
|
|
1292
|
+
* Put a crosshair on/off switch in the header (advanced toolbar, default top
|
|
1293
|
+
* bar and the compact per-pane strip). The crosshair itself stays on by default (`crosshairEnabled`), so
|
|
1294
|
+
* the icon starts tinted; clicking it calls `setCrosshairEnabled(false)` — the
|
|
1295
|
+
* icon drops to its plain state and the crosshair (with the floating "place
|
|
1296
|
+
* order at this price" button that rides on it) disappears until the icon is
|
|
1297
|
+
* clicked again. Emits `crosshairToggle` for the host to persist the choice.
|
|
1298
|
+
* Default: false.
|
|
1299
|
+
*/
|
|
1300
|
+
enableCrossHairHeader?: boolean;
|
|
1301
|
+
/**
|
|
1302
|
+
* @deprecated Use `enableCrossHairHeader`. Kept as an alias so existing
|
|
1303
|
+
* consumers keep their header switch — either flag being `true` renders it.
|
|
1297
1304
|
*/
|
|
1298
1305
|
enableCrosshairToggle?: boolean;
|
|
1299
1306
|
/**
|
|
@@ -1328,6 +1335,9 @@ interface ChartConfig {
|
|
|
1328
1335
|
* Enable horizontal (time-axis) order-line dragging: a level whose `timeDraggable`
|
|
1329
1336
|
* is true can have its info-box badge dragged left/right to re-anchor the order to
|
|
1330
1337
|
* a different candle (price stays locked), emitting `orderLineMoved` on release.
|
|
1338
|
+
* Levels that can also be price-dragged (pending orders, entry-editable positions)
|
|
1339
|
+
* keep that: the first movement of a badge grab decides the axis — sideways moves
|
|
1340
|
+
* the time anchor, up/down moves the price as before.
|
|
1331
1341
|
* This is the broker-gating seam — hosts (e.g. the Hankotrade app) enable it only
|
|
1332
1342
|
* for their flagged users. When `false` / omitted, behavior is unchanged.
|
|
1333
1343
|
* Default: false.
|
|
@@ -1881,7 +1891,7 @@ type ChartEventMap = {
|
|
|
1881
1891
|
tfcToggle: {
|
|
1882
1892
|
enabled: boolean;
|
|
1883
1893
|
};
|
|
1884
|
-
/** Crosshair switched on or off — via the header switch (`
|
|
1894
|
+
/** Crosshair switched on or off — via the header switch (`enableCrossHairHeader`) or `setCrosshairEnabled()`. */
|
|
1885
1895
|
crosshairToggle: {
|
|
1886
1896
|
enabled: boolean;
|
|
1887
1897
|
};
|
|
@@ -2152,7 +2162,9 @@ interface PositionLevel {
|
|
|
2152
2162
|
/**
|
|
2153
2163
|
* When true (and `ChartConfig.features.orderLineTimeDrag` is enabled), the badge can be
|
|
2154
2164
|
* dragged horizontally to re-anchor the order to a different candle (price stays locked).
|
|
2155
|
-
* Emits `orderLineMoved` on release.
|
|
2165
|
+
* Emits `orderLineMoved` on release. An `entryPriceEditable` position keeps its vertical
|
|
2166
|
+
* price drag too: the badge follows the drag direction (sideways = time, up/down = price).
|
|
2167
|
+
* Default: false.
|
|
2156
2168
|
*/
|
|
2157
2169
|
timeDraggable?: boolean;
|
|
2158
2170
|
/**
|
|
@@ -2208,8 +2220,10 @@ interface PendingOrderLevel {
|
|
|
2208
2220
|
timestamp?: number;
|
|
2209
2221
|
/**
|
|
2210
2222
|
* When true (and `ChartConfig.features.orderLineTimeDrag` is enabled), the badge can be
|
|
2211
|
-
* dragged horizontally to re-anchor the order to a different candle
|
|
2212
|
-
*
|
|
2223
|
+
* dragged horizontally to re-anchor the order to a different candle, emitting
|
|
2224
|
+
* `orderLineMoved` on release. The order stays price-draggable: the badge follows the
|
|
2225
|
+
* drag direction — sideways moves the time anchor (price locked), up/down moves the
|
|
2226
|
+
* entry price exactly as without this flag. Default: false.
|
|
2213
2227
|
*/
|
|
2214
2228
|
timeDraggable?: boolean;
|
|
2215
2229
|
/** Your original order object. Returned as-is in events. The library never inspects this. */
|
package/dist/index.cjs
CHANGED
|
@@ -2201,7 +2201,7 @@ function levelColor(level, colors, theme) {
|
|
|
2201
2201
|
}
|
|
2202
2202
|
return colors.tradeLine;
|
|
2203
2203
|
}
|
|
2204
|
-
function
|
|
2204
|
+
function isPriceDraggableLevel(level) {
|
|
2205
2205
|
if (level.type === "pending") return true;
|
|
2206
2206
|
if (level.type === "position")
|
|
2207
2207
|
return !!level.entryPriceEditable;
|
|
@@ -2761,7 +2761,7 @@ function drawLevel(ctx, level, layout, chartW, colors, theme, precision, hovered
|
|
|
2761
2761
|
const pnl = level.type === "position" ? level.pnl : void 0;
|
|
2762
2762
|
const isPending = level.type === "pending";
|
|
2763
2763
|
const isMarketDraft = isPending && level.orderType === "market" && level.data?._isDraft === true;
|
|
2764
|
-
const draggable =
|
|
2764
|
+
const draggable = isPriceDraggableLevel(level);
|
|
2765
2765
|
const groupW = (draggable ? metrics.gripW + 2 : 0) + boxW;
|
|
2766
2766
|
const pinnedLeft = badgeAnchor === "left";
|
|
2767
2767
|
const groupX = anchorX !== null ? Math.max(0, Math.min(chartW - groupW, Math.round(anchorX - groupW / 2))) : pinnedLeft ? metrics.leftMargin : Math.round((chartW - groupW) / 2);
|
|
@@ -6569,9 +6569,13 @@ function injectFlyoutScrollbarStyles3() {
|
|
|
6569
6569
|
document.head.appendChild(style);
|
|
6570
6570
|
}
|
|
6571
6571
|
var CompactToolbar = class {
|
|
6572
|
-
constructor(onSeriesChange, onTimeframeChange, onAddIndicator, initialSeries, initialTf, theme) {
|
|
6572
|
+
constructor(onSeriesChange, onTimeframeChange, onAddIndicator, initialSeries, initialTf, theme, showCrosshairBtn = false, initialCrosshairEnabled = true, onCrosshairToggle = null) {
|
|
6573
6573
|
this.onSeriesChange = onSeriesChange;
|
|
6574
6574
|
this.onAddIndicator = onAddIndicator;
|
|
6575
|
+
this.onCrosshairToggle = onCrosshairToggle;
|
|
6576
|
+
/** Crosshair on/off switch (`enableCrossHairHeader`) — null when not requested. */
|
|
6577
|
+
this.crosshairBtn = null;
|
|
6578
|
+
this.crosshairEnabled = true;
|
|
6575
6579
|
this.tfBtns = /* @__PURE__ */ new Map();
|
|
6576
6580
|
this.indicatorOptions = [];
|
|
6577
6581
|
this.activeIndicators = /* @__PURE__ */ new Set();
|
|
@@ -6614,6 +6618,26 @@ var CompactToolbar = class {
|
|
|
6614
6618
|
this.toggleFlyout("indicators", this.indBtn);
|
|
6615
6619
|
});
|
|
6616
6620
|
this.el.appendChild(this.indBtn);
|
|
6621
|
+
if (showCrosshairBtn) {
|
|
6622
|
+
this.crosshairEnabled = initialCrosshairEnabled;
|
|
6623
|
+
this.crosshairBtn = document.createElement("button");
|
|
6624
|
+
this.crosshairBtn.type = "button";
|
|
6625
|
+
this.crosshairBtn.dataset.testid = "chart-crosshair-toggle";
|
|
6626
|
+
this.crosshairBtn.innerHTML = ICON_CROSSHAIR;
|
|
6627
|
+
this.applyCrosshairBtnStyles(this.crosshairBtn, theme, this.crosshairEnabled);
|
|
6628
|
+
this.crosshairBtn.addEventListener("click", (e) => {
|
|
6629
|
+
e.stopPropagation();
|
|
6630
|
+
this.setCrosshairActive(!this.crosshairEnabled);
|
|
6631
|
+
this.onCrosshairToggle?.(this.crosshairEnabled);
|
|
6632
|
+
});
|
|
6633
|
+
this.crosshairBtn.addEventListener("mouseenter", () => {
|
|
6634
|
+
if (this.crosshairBtn && !this.crosshairEnabled) this.crosshairBtn.style.background = this.theme.ui.accentBg;
|
|
6635
|
+
});
|
|
6636
|
+
this.crosshairBtn.addEventListener("mouseleave", () => {
|
|
6637
|
+
if (this.crosshairBtn && !this.crosshairEnabled) this.crosshairBtn.style.background = "transparent";
|
|
6638
|
+
});
|
|
6639
|
+
this.el.appendChild(this.crosshairBtn);
|
|
6640
|
+
}
|
|
6617
6641
|
this.docClickHandler = () => this.closeDropdown();
|
|
6618
6642
|
document.addEventListener("click", this.docClickHandler);
|
|
6619
6643
|
}
|
|
@@ -6641,6 +6665,12 @@ var CompactToolbar = class {
|
|
|
6641
6665
|
setVolumeControl(_show, hasVol) {
|
|
6642
6666
|
this.volHasData = hasVol;
|
|
6643
6667
|
}
|
|
6668
|
+
/** Reflect the crosshair on/off state in the header switch. No-op when the button is not rendered. */
|
|
6669
|
+
setCrosshairActive(enabled) {
|
|
6670
|
+
this.crosshairEnabled = enabled;
|
|
6671
|
+
if (!this.crosshairBtn) return;
|
|
6672
|
+
this.applyCrosshairBtnStyles(this.crosshairBtn, this.theme, enabled);
|
|
6673
|
+
}
|
|
6644
6674
|
setTheme(theme) {
|
|
6645
6675
|
this.theme = theme;
|
|
6646
6676
|
this.applyBarStyles(theme);
|
|
@@ -6648,6 +6678,7 @@ var CompactToolbar = class {
|
|
|
6648
6678
|
for (const [tf, btn] of this.tfBtns) this.applyTfPillStyles(btn, theme, tf === this.currentTf);
|
|
6649
6679
|
this.applyIconBtnStyles(this.seriesBtn, theme);
|
|
6650
6680
|
this.applyIconBtnStyles(this.indBtn, theme);
|
|
6681
|
+
if (this.crosshairBtn) this.applyCrosshairBtnStyles(this.crosshairBtn, theme, this.crosshairEnabled);
|
|
6651
6682
|
this.flyout.style.setProperty("--act-compact-row-hover", theme.ui.accentBg);
|
|
6652
6683
|
}
|
|
6653
6684
|
closeDropdown() {
|
|
@@ -6858,6 +6889,31 @@ var CompactToolbar = class {
|
|
|
6858
6889
|
this.addHover(btn, theme);
|
|
6859
6890
|
return btn;
|
|
6860
6891
|
}
|
|
6892
|
+
/* The reference `rounded-sm` switch — tinted `bg-primary/10 text-primary border-primary/30`
|
|
6893
|
+
* while on — at this strip's 20px icon-button size. */
|
|
6894
|
+
applyCrosshairBtnStyles(btn, theme, active) {
|
|
6895
|
+
btn.dataset.active = String(active);
|
|
6896
|
+
const title = active ? "Disable crosshair" : "Enable crosshair";
|
|
6897
|
+
btn.title = title;
|
|
6898
|
+
btn.setAttribute("aria-label", title);
|
|
6899
|
+
btn.setAttribute("aria-pressed", String(active));
|
|
6900
|
+
Object.assign(btn.style, {
|
|
6901
|
+
height: "20px",
|
|
6902
|
+
width: "20px",
|
|
6903
|
+
boxSizing: "border-box",
|
|
6904
|
+
display: "flex",
|
|
6905
|
+
alignItems: "center",
|
|
6906
|
+
justifyContent: "center",
|
|
6907
|
+
borderRadius: "2px",
|
|
6908
|
+
border: `1px solid ${active ? withAlpha4(theme.ui.accent, 0.3) : "transparent"}`,
|
|
6909
|
+
background: active ? withAlpha4(theme.ui.accent, 0.1) : "transparent",
|
|
6910
|
+
color: active ? theme.ui.accent : theme.topBar.btnColor,
|
|
6911
|
+
cursor: "pointer",
|
|
6912
|
+
padding: "0",
|
|
6913
|
+
flexShrink: "0",
|
|
6914
|
+
transition: "color 0.15s, background-color 0.15s, border-color 0.15s"
|
|
6915
|
+
});
|
|
6916
|
+
}
|
|
6861
6917
|
applyIconBtnStyles(btn, theme) {
|
|
6862
6918
|
Object.assign(btn.style, {
|
|
6863
6919
|
height: "20px",
|
|
@@ -21086,6 +21142,17 @@ var OrderLineTimeDragHandler = class _OrderLineTimeDragHandler {
|
|
|
21086
21142
|
*/
|
|
21087
21143
|
this._pixelOffset = 0;
|
|
21088
21144
|
}
|
|
21145
|
+
/**
|
|
21146
|
+
* Decides which axis a badge grab commits to once the pointer has moved past `threshold`
|
|
21147
|
+
* pixels (Euclidean) from where it went down: `'time'` when the movement is mostly
|
|
21148
|
+
* sideways, `'price'` when mostly vertical, `null` while still within the threshold.
|
|
21149
|
+
* Pure — shared by the mouse and touch paths in ChartEngine so a `timeDraggable` pending
|
|
21150
|
+
* order can be dragged either way from the same pill.
|
|
21151
|
+
*/
|
|
21152
|
+
static resolveDragAxis(dx, dy, threshold) {
|
|
21153
|
+
if (Math.sqrt(dx * dx + dy * dy) <= threshold) return null;
|
|
21154
|
+
return Math.abs(dx) >= Math.abs(dy) ? "time" : "price";
|
|
21155
|
+
}
|
|
21089
21156
|
/**
|
|
21090
21157
|
* Starts a horizontal drag.
|
|
21091
21158
|
* @param level The level being dragged.
|
|
@@ -25211,16 +25278,8 @@ var _ChartEngine = class _ChartEngine {
|
|
|
25211
25278
|
return;
|
|
25212
25279
|
}
|
|
25213
25280
|
if (this._pendingTradeDrag) {
|
|
25214
|
-
|
|
25215
|
-
|
|
25216
|
-
if (Math.sqrt(dx * dx + dy * dy) > _ChartEngine.TRADE_DRAG_PIXEL_THRESHOLD) {
|
|
25217
|
-
const { dragHit, level, hitY, startX } = this._pendingTradeDrag;
|
|
25218
|
-
this._pendingTradeDrag = null;
|
|
25219
|
-
this._startTradeDragFromHit(dragHit, level, hitY, startX);
|
|
25220
|
-
this.drawCanvas.style.cursor = this.orderTimeDragHandler?.active ? "grabbing" : "ns-resize";
|
|
25221
|
-
} else {
|
|
25222
|
-
return;
|
|
25223
|
-
}
|
|
25281
|
+
if (!this._upgradePendingTradeDrag(x, y)) return;
|
|
25282
|
+
this.drawCanvas.style.cursor = this.orderTimeDragHandler?.active ? "grabbing" : "ns-resize";
|
|
25224
25283
|
}
|
|
25225
25284
|
if (this.orderTimeDragHandler?.active) {
|
|
25226
25285
|
const idx = this.orderTimeDragHandler.onMove(x, this.scaleManager, this.viewport, chartW);
|
|
@@ -25278,7 +25337,8 @@ var _ChartEngine = class _ChartEngine {
|
|
|
25278
25337
|
}
|
|
25279
25338
|
const dragHover = hitTestTradeLevelDrag(x, y, this.tradeLevelDragAreas);
|
|
25280
25339
|
if (dragHover) {
|
|
25281
|
-
|
|
25340
|
+
const hoverLevel = this.levels.find((l) => l.label === dragHover.label);
|
|
25341
|
+
this.drawCanvas.style.cursor = this._orderTimeDragEnabled && dragHover.timeDrag === true ? hoverLevel && this._needsDragAxisLock(dragHover, hoverLevel) ? "move" : "grab" : "ns-resize";
|
|
25282
25342
|
if (dragHover.label !== this.hoveredTradeLabel) {
|
|
25283
25343
|
this.hoveredTradeLabel = dragHover.label;
|
|
25284
25344
|
this.renderTradeLayer();
|
|
@@ -25586,6 +25646,17 @@ var _ChartEngine = class _ChartEngine {
|
|
|
25586
25646
|
this._openTradeLevelEditForm(level);
|
|
25587
25647
|
return;
|
|
25588
25648
|
}
|
|
25649
|
+
if (this._needsDragAxisLock(dragHit, level)) {
|
|
25650
|
+
this._pendingTradeDrag = {
|
|
25651
|
+
dragHit,
|
|
25652
|
+
level,
|
|
25653
|
+
hitY: y,
|
|
25654
|
+
startX: x,
|
|
25655
|
+
startY: y,
|
|
25656
|
+
axisLock: true
|
|
25657
|
+
};
|
|
25658
|
+
return;
|
|
25659
|
+
}
|
|
25589
25660
|
this._startTradeDragFromHit(dragHit, level, y, x);
|
|
25590
25661
|
this.scheduleRender();
|
|
25591
25662
|
return;
|
|
@@ -25700,6 +25771,16 @@ var _ChartEngine = class _ChartEngine {
|
|
|
25700
25771
|
this.scheduleRender();
|
|
25701
25772
|
return;
|
|
25702
25773
|
}
|
|
25774
|
+
if (this._pendingTradeDrag) {
|
|
25775
|
+
e.preventDefault();
|
|
25776
|
+
const touch = e.touches[0];
|
|
25777
|
+
const rect = this._cachedDrawRect ?? this.drawCanvas.getBoundingClientRect();
|
|
25778
|
+
const started = this._upgradePendingTradeDrag(
|
|
25779
|
+
touch.clientX - rect.left,
|
|
25780
|
+
touch.clientY - rect.top
|
|
25781
|
+
);
|
|
25782
|
+
if (!started) return;
|
|
25783
|
+
}
|
|
25703
25784
|
if (this.orderTimeDragHandler?.active) {
|
|
25704
25785
|
e.preventDefault();
|
|
25705
25786
|
const touch = e.touches[0];
|
|
@@ -25822,6 +25903,7 @@ var _ChartEngine = class _ChartEngine {
|
|
|
25822
25903
|
this._longPressPoint = null;
|
|
25823
25904
|
}
|
|
25824
25905
|
let dirty = false;
|
|
25906
|
+
this._pendingTradeDrag = null;
|
|
25825
25907
|
if (this.orderTimeDragHandler?.active) {
|
|
25826
25908
|
this.orderTimeDragHandler.cancel();
|
|
25827
25909
|
this.tradeDragNewBarIndex = null;
|
|
@@ -25845,6 +25927,17 @@ var _ChartEngine = class _ChartEngine {
|
|
|
25845
25927
|
this._longPressTimer = null;
|
|
25846
25928
|
this._longPressPoint = null;
|
|
25847
25929
|
}
|
|
25930
|
+
if (this._pendingTradeDrag) {
|
|
25931
|
+
const { level } = this._pendingTradeDrag;
|
|
25932
|
+
this._pendingTradeDrag = null;
|
|
25933
|
+
const hasPending = this.pendingLevelDrags.has(level.label);
|
|
25934
|
+
if (this.selectedTradeLabel === level.label && !hasPending) {
|
|
25935
|
+
this._cancelEditAndDeselect(level.label);
|
|
25936
|
+
return;
|
|
25937
|
+
}
|
|
25938
|
+
this._openTradeLevelEditForm(level);
|
|
25939
|
+
return;
|
|
25940
|
+
}
|
|
25848
25941
|
if (this._endOrderTimeDrag()) return;
|
|
25849
25942
|
if (this.tradeDragHandler?.active) {
|
|
25850
25943
|
const result = this.tradeDragHandler.endDrag();
|
|
@@ -25980,7 +26073,14 @@ var _ChartEngine = class _ChartEngine {
|
|
|
25980
26073
|
const level = this.levels.find((l) => l.label === dragHit.label);
|
|
25981
26074
|
if (level) {
|
|
25982
26075
|
if (!(this.hideLevelConfirmCancel && !dragHit.isBracket)) {
|
|
25983
|
-
this._pendingTradeDrag = {
|
|
26076
|
+
this._pendingTradeDrag = {
|
|
26077
|
+
dragHit,
|
|
26078
|
+
level,
|
|
26079
|
+
hitY: y,
|
|
26080
|
+
startX: x,
|
|
26081
|
+
startY: y,
|
|
26082
|
+
axisLock: this._needsDragAxisLock(dragHit, level)
|
|
26083
|
+
};
|
|
25984
26084
|
return;
|
|
25985
26085
|
}
|
|
25986
26086
|
}
|
|
@@ -26402,6 +26502,7 @@ var _ChartEngine = class _ChartEngine {
|
|
|
26402
26502
|
const hideHeader = config.hideHeader === true;
|
|
26403
26503
|
const enableMultipleLayouts = config.enableMultipleLayouts === true;
|
|
26404
26504
|
const enableSnapshot = config.enableSnapshot === true;
|
|
26505
|
+
const showCrosshairHeaderBtn = config.enableCrossHairHeader === true || config.enableCrosshairToggle === true;
|
|
26405
26506
|
if (config.layoutSync) {
|
|
26406
26507
|
this.currentSync = { ...DEFAULT_LAYOUT_SYNC, ...config.layoutSync };
|
|
26407
26508
|
}
|
|
@@ -26486,7 +26587,12 @@ var _ChartEngine = class _ChartEngine {
|
|
|
26486
26587
|
(name) => this.addIndicatorByName(name),
|
|
26487
26588
|
this.series,
|
|
26488
26589
|
this.timeframe,
|
|
26489
|
-
this.theme
|
|
26590
|
+
this.theme,
|
|
26591
|
+
showCrosshairHeaderBtn,
|
|
26592
|
+
this.crosshairEnabled,
|
|
26593
|
+
(enabled) => {
|
|
26594
|
+
this.setCrosshairEnabled(enabled);
|
|
26595
|
+
}
|
|
26490
26596
|
);
|
|
26491
26597
|
this.compactToolbar.setIndicatorOptions(indicatorOptions);
|
|
26492
26598
|
this.wrapper.appendChild(this.compactToolbar.el);
|
|
@@ -26516,7 +26622,7 @@ var _ChartEngine = class _ChartEngine {
|
|
|
26516
26622
|
config.priceSourceSelector ?? null,
|
|
26517
26623
|
this.tickClosePriceSource,
|
|
26518
26624
|
(source) => this._applyPriceSource(source, true),
|
|
26519
|
-
|
|
26625
|
+
showCrosshairHeaderBtn,
|
|
26520
26626
|
this.crosshairEnabled,
|
|
26521
26627
|
(enabled) => {
|
|
26522
26628
|
this.setCrosshairEnabled(enabled);
|
|
@@ -26575,7 +26681,7 @@ var _ChartEngine = class _ChartEngine {
|
|
|
26575
26681
|
config.priceSourceSelector ?? null,
|
|
26576
26682
|
this.tickClosePriceSource,
|
|
26577
26683
|
(source) => this._applyPriceSource(source, true),
|
|
26578
|
-
|
|
26684
|
+
showCrosshairHeaderBtn,
|
|
26579
26685
|
this.crosshairEnabled,
|
|
26580
26686
|
(enabled) => {
|
|
26581
26687
|
this.setCrosshairEnabled(enabled);
|
|
@@ -27079,7 +27185,7 @@ var _ChartEngine = class _ChartEngine {
|
|
|
27079
27185
|
},
|
|
27080
27186
|
() => this.dataStore.length,
|
|
27081
27187
|
() => this.scheduleRender(),
|
|
27082
|
-
() => 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._mobileCrosshairActive,
|
|
27188
|
+
() => 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,
|
|
27083
27189
|
(deltaY) => {
|
|
27084
27190
|
if (this.priceScaleFactor === 1) return;
|
|
27085
27191
|
const { priceH } = this.getChartLayout();
|
|
@@ -28145,6 +28251,7 @@ var _ChartEngine = class _ChartEngine {
|
|
|
28145
28251
|
this.crosshairEnabled = enabled;
|
|
28146
28252
|
this.advancedToolbar?.setCrosshairActive(enabled);
|
|
28147
28253
|
this.topBar?.setCrosshairActive(enabled);
|
|
28254
|
+
this.compactToolbar?.setCrosshairActive(enabled);
|
|
28148
28255
|
if (!enabled) {
|
|
28149
28256
|
if (this.crosshair !== null) {
|
|
28150
28257
|
this.crosshair = null;
|
|
@@ -31605,11 +31712,42 @@ var _ChartEngine = class _ChartEngine {
|
|
|
31605
31712
|
isFullscreen: this.isFullscreen
|
|
31606
31713
|
});
|
|
31607
31714
|
}
|
|
31715
|
+
/**
|
|
31716
|
+
* Whether grabbing `dragHit` must wait for the drag direction before choosing an axis:
|
|
31717
|
+
* the badge of a `timeDraggable` level that can ALSO be price-dragged (pending orders,
|
|
31718
|
+
* entry-editable positions). Fixed-price positions keep the immediate horizontal drag.
|
|
31719
|
+
*/
|
|
31720
|
+
_needsDragAxisLock(dragHit, level) {
|
|
31721
|
+
return this._orderTimeDragEnabled && !dragHit.isBracket && dragHit.timeDrag === true && isPriceDraggableLevel(level);
|
|
31722
|
+
}
|
|
31723
|
+
/**
|
|
31724
|
+
* Upgrades an armed `_pendingTradeDrag` to a real drag once the pointer has moved far
|
|
31725
|
+
* enough from where it went down. A dual-axis badge (`axisLock`) commits to the dominant
|
|
31726
|
+
* direction: sideways → horizontal time-drag (price locked), up/down → the default
|
|
31727
|
+
* vertical price-drag, started from the same hit with its `timeDrag` flag cleared.
|
|
31728
|
+
* Returns true when a drag started, false while still waiting for movement.
|
|
31729
|
+
*/
|
|
31730
|
+
_upgradePendingTradeDrag(x, y) {
|
|
31731
|
+
const pending = this._pendingTradeDrag;
|
|
31732
|
+
if (!pending) return false;
|
|
31733
|
+
const axis = OrderLineTimeDragHandler.resolveDragAxis(
|
|
31734
|
+
x - pending.startX,
|
|
31735
|
+
y - pending.startY,
|
|
31736
|
+
pending.axisLock ? _ChartEngine.AXIS_LOCK_PIXEL_THRESHOLD : _ChartEngine.TRADE_DRAG_PIXEL_THRESHOLD
|
|
31737
|
+
);
|
|
31738
|
+
if (axis === null) return false;
|
|
31739
|
+
this._pendingTradeDrag = null;
|
|
31740
|
+
const dragHit = pending.axisLock && axis === "price" ? { ...pending.dragHit, timeDrag: false } : pending.dragHit;
|
|
31741
|
+
this._startTradeDragFromHit(dragHit, pending.level, pending.hitY, pending.startX);
|
|
31742
|
+
return true;
|
|
31743
|
+
}
|
|
31608
31744
|
/**
|
|
31609
31745
|
* Shared helper: initiate a trade level drag from a hit area.
|
|
31610
31746
|
* Starts a HORIZONTAL (time-axis) drag when the hit area is the badge of a `timeDraggable`
|
|
31611
31747
|
* level and `features.orderLineTimeDrag` is on; otherwise starts the default vertical
|
|
31612
31748
|
* price drag. `hitX` is required for the horizontal path (defaults to the badge anchor).
|
|
31749
|
+
* Dual-axis badges never reach this directly — `_upgradePendingTradeDrag` resolves the
|
|
31750
|
+
* axis first and clears `timeDrag` on the hit when the drag is vertical.
|
|
31613
31751
|
*/
|
|
31614
31752
|
_startTradeDragFromHit(dragHit, level, hitY, hitX) {
|
|
31615
31753
|
if (!this.tradeDragHandler) return;
|
|
@@ -32112,6 +32250,9 @@ _ChartEngine.DRAW_GESTURE_THRESHOLD = 4;
|
|
|
32112
32250
|
*/
|
|
32113
32251
|
_ChartEngine.TOUCH_HANDLE_SLOP_PX = 18;
|
|
32114
32252
|
_ChartEngine.TRADE_DRAG_PIXEL_THRESHOLD = 3;
|
|
32253
|
+
// px
|
|
32254
|
+
/** Movement a dual-axis badge grab needs before it commits to time vs price. */
|
|
32255
|
+
_ChartEngine.AXIS_LOCK_PIXEL_THRESHOLD = 6;
|
|
32115
32256
|
var ChartEngine = _ChartEngine;
|
|
32116
32257
|
|
|
32117
32258
|
// src/core/ChartGroup.ts
|