acttrader-charts 1.0.17 → 1.0.19

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
@@ -13,6 +13,17 @@ Dual ESM/CJS output, TypeScript-first.
13
13
  npm install acttrader-charts
14
14
  ```
15
15
 
16
+ ### Beta releases
17
+
18
+ Pre-release builds are published under the `beta` dist-tag and use prerelease semver (`X.Y.Z-beta.N`). They never move the default `latest` tag, so existing consumers are unaffected.
19
+
20
+ ```bash
21
+ npm install acttrader-charts@beta # opt in to the latest beta
22
+ npm install acttrader-charts@1.1.0-beta.1 # pin a specific beta
23
+ ```
24
+
25
+ `npm install acttrader-charts` and existing semver ranges (`^1.0.0`, `~1.0.0`) continue to resolve only to stable releases — by spec, semver ranges exclude prerelease versions.
26
+
16
27
  ---
17
28
 
18
29
  ## Basic Usage
@@ -164,6 +175,10 @@ chart.setLevels([], 'label', 'price', 'pending'); // clear all of one type
164
175
 
165
176
  **Visual differentiation:** pending orders and ES/EL entry working orders render as **dashed** lines tinted by side (`pendingBuyLine` green / `pendingSellLine` red). True open positions render as **solid** lines: green/red when `pnl` is set (sign-colored), otherwise `positionLine` (purple/indigo). The info-box border matches the line color. Each true open position also gets a small colored price tag on the right-side price axis showing the entry price — same visual language as the Bid/Ask tag — so you can read the entry price without hunting for the info box.
166
177
 
178
+ **Brackets follow entry on drag:** when the entry line of a pending order, draft order, or an open position with `entryPriceEditable: true` is dragged, any existing `stopLossPrice` / `takeProfitPrice` brackets translate along with it by the same price delta. The distance between entry and each bracket is whatever the user currently sees — if they manually drag SL or TP to a new price, that new distance becomes the anchor for the next entry drag. Missing brackets aren't auto-created. On confirm, `tradeLevelEdit` carries all translated fields together in one `changes[]` array; on mobile (`hideLevelConfirmCancel`) the three changes are applied as one atomic event.
179
+
180
+ **Bracket pill auto-offset:** when an SL or TP price sits within about one pill-height of the entry price, the bracket's label pill is pushed vertically away from the entry pill and connected back to its real price line by a dashed leader. The horizontal bracket line itself stays at the true price; only the pill and its `×` button move, and drag targets follow the displaced pill — so grabbing the pill always drags the bracket, grabbing the entry pill always drags the entry, and grabbing the real bracket line still drags the bracket. Works for both buy and sell orders without configuration.
181
+
167
182
  **TFC events:**
168
183
 
169
184
  ```ts
@@ -191,6 +206,14 @@ chart.on('tradeLevelClose', ({ label, type, action, isFullscreen }) => {
191
206
  chart.on('tradeLevelDrag', ({ label, newPrice, bracketType, isFullscreen }) => {
192
207
  updatePricePreview(label, newPrice);
193
208
  });
209
+
210
+ // Live qty while the user edits the QTY pill flyout — fires before the level edit is
211
+ // confirmed, so host-side Estimated PNL for SL/TP brackets can recompute immediately
212
+ // instead of waiting for `tradeLevelEdit` at ✓ confirm. `type` is `'draft'` for draft
213
+ // orders, otherwise the parent level's type.
214
+ chart.on('tradeLevelQtyChange', ({ label, type, newLots, previousLots }) => {
215
+ updateEstimatedPnl(label, newLots);
216
+ });
194
217
  ```
195
218
 
196
219
  **Off-viewport level indicators:**
@@ -263,6 +286,7 @@ When `enableTrading` is on and live BID/ASK data is streaming, hovering / activa
263
286
  | `tradeDisplayFilter` | | `"all"` | Which TFC levels are visible: `"all"` · `"positions"` · `"orders"` · `"none"` |
264
287
  | `positionRenderStyle` | | auto | Force position render style: `"line"` or `"dot"` |
265
288
  | `hideLevelConfirmCancel` | | `false` | Hide on-canvas ✓/✗ confirm-cancel buttons for TFC level edits |
289
+ | `tradeLevelButtonScale` | | `1` | Multiplier for trade-level Confirm/Cancel/Edit/Close button radii and gaps. Scales visuals **and** hit/drag areas together — raise it on touch devices for larger tap targets. Clamped to `[1, 3]`. Also settable at runtime via `chart.setTradeLevelButtonScale(scale)` |
266
290
  | `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 |
267
291
  | `levelClusteringEnabled` | | `true` | Enable trade-level fan-out clustering; overlapping levels group into expandable badges |
268
292
  | `clusterThresholdDistance` | | `20` | Pixel proximity threshold for clustering (only when `levelClusteringEnabled` is `true`) |
@@ -604,6 +628,53 @@ if (saved) chart.setState(JSON.parse(saved) as ChartState);
604
628
  chart.destroy(): void
605
629
  ```
606
630
 
631
+ ### Dismissing open UI
632
+
633
+ Closes any open flyout/modal/dropdown/popover (quantity edit, symbol picker,
634
+ indicator settings, chart settings, trade popover, topbar dropdowns, mobile
635
+ drawing flyout) in a single call. Designed for wiring to the system back
636
+ button / gesture in PWAs, installed web apps, and native wrapper hosts.
637
+
638
+ ```ts
639
+ chart.dismissAllUI(): boolean // true if anything was dismissed, false if nothing was open
640
+ ```
641
+
642
+ Paired with the `uiStateChange` event so you can track whether a back action
643
+ should dismiss chart UI or fall through to your own navigation:
644
+
645
+ ```ts
646
+ chart.on('uiStateChange', ({ hasOpenUI }) => {
647
+ // hasOpenUI === true when at least one flyout/modal/dropdown is open
648
+ });
649
+ ```
650
+
651
+ **PWA — proper History API wiring** (back press dismisses flyout without
652
+ navigating away from the page):
653
+
654
+ ```ts
655
+ let pushedStateForUI = false;
656
+
657
+ chart.on('uiStateChange', ({ hasOpenUI }) => {
658
+ if (hasOpenUI && !pushedStateForUI) {
659
+ history.pushState({ chartUI: true }, '');
660
+ pushedStateForUI = true;
661
+ } else if (!hasOpenUI) {
662
+ pushedStateForUI = false;
663
+ }
664
+ });
665
+
666
+ window.addEventListener('popstate', () => {
667
+ if (chart.dismissAllUI()) pushedStateForUI = false;
668
+ });
669
+ ```
670
+
671
+ **Minimum wiring** (back press closes flyout AND advances browser history —
672
+ fine for single-screen apps without client routing):
673
+
674
+ ```ts
675
+ window.addEventListener('popstate', () => chart.dismissAllUI());
676
+ ```
677
+
607
678
  ---
608
679
 
609
680
  ## Built-in Indicators
@@ -723,8 +794,10 @@ chart.on('stateChange', ({ symbol, state }) => {
723
794
  chart.on('tradeLevelEdit', ({ label, type, data, changes, isFullscreen }) => {});
724
795
  chart.on('tradeLevelClose', ({ label, type, action, data, isFullscreen }) => {});
725
796
  chart.on('tradeLevelDrag', ({ label, newPrice, data, bracketType, isFullscreen }) => {}); // fires on every mouse-move during drag
797
+ chart.on('tradeLevelQtyChange', ({ label, type, newLots, previousLots, isFullscreen }) => {}); // live qty edit via QTY pill — use to refresh Estimated PNL on SL/TP brackets
726
798
  chart.on('tradeLevelEditOpen', ({ label, type, price, side, stopLossPrice, takeProfitPrice, data, isFullscreen }) => {});
727
799
  chart.on('tradeLevelConfirmed',({ label, type, isFullscreen }) => {});
800
+ chart.on('tradeLevelEditCancelled', ({ label, type, isFullscreen }) => {}); // ESC / inline ✕ aborted an edit — reset external modify-order panel
728
801
  chart.on('draftInitiated', ({ side, price, orderType, isFullscreen }) => {}); // new draft order shown — open buy/sell form
729
802
  chart.on('draftCancelled', ({ label, isFullscreen }) => {}); // draft order dismissed without confirming
730
803
  chart.on('tfcToggle', ({ enabled }) => {}); // TFC toggled on or off via top bar button or setTfcActive()
@@ -865,6 +865,14 @@ interface ChartConfig {
865
865
  * Default: `false`.
866
866
  */
867
867
  hideLevelConfirmCancel?: boolean;
868
+ /**
869
+ * Multiplier applied to the Confirm (✓), Cancel (✗), Edit (✎), and Close (×)
870
+ * buttons on trade-level overlays (both main-level and SL/TP bracket boxes).
871
+ * Scales button radii and inter-button gaps so visual size AND hit/drag areas
872
+ * grow together — useful for touch targets.
873
+ * Clamped to `[1, 3]`. Default: `1`.
874
+ */
875
+ tradeLevelButtonScale?: number;
868
876
  /**
869
877
  * When `true`, a theme toggle (Dark / Light) is shown in the Settings dialog.
870
878
  * Default: `false` — the toggle is hidden.
@@ -1093,12 +1101,16 @@ type ChartEventMap = {
1093
1101
  isFullscreen: boolean;
1094
1102
  };
1095
1103
  /** Emitted on every mouse-move while dragging a trade level line (before confirm).
1096
- * `bracketType` is present when dragging an SL/TP bracket line; absent for main entry drag. */
1104
+ * `bracketType` is present when dragging an SL/TP bracket line; absent for main entry drag.
1105
+ * `derivedSLPrice` / `derivedTPPrice` are present during a main-entry drag in follow-brackets mode
1106
+ * so external panels can mirror the SL/TP translation live (keeps validation from flickering). */
1097
1107
  tradeLevelDrag: {
1098
1108
  label: string;
1099
1109
  newPrice: number;
1100
1110
  data: unknown;
1101
1111
  bracketType?: 'stopLoss' | 'takeProfit';
1112
+ derivedSLPrice?: number;
1113
+ derivedTPPrice?: number;
1102
1114
  isFullscreen: boolean;
1103
1115
  };
1104
1116
  /** Emitted at the instant a bracket (SL/TP) drag begins — before any movement.
@@ -1114,6 +1126,14 @@ type ChartEventMap = {
1114
1126
  type: TradeLevelType | 'draft';
1115
1127
  isFullscreen: boolean;
1116
1128
  };
1129
+ /** Emitted when an in-progress level edit is cancelled from the chart (ESC key or inline ✕ cancel button).
1130
+ * Mirrors `tradeLevelConfirmed` but for the revert path. Not fired for draft orders (those emit `draftCancelled`).
1131
+ * External panels listen to reset their modify-order form when the user aborts the edit from the chart side. */
1132
+ tradeLevelEditCancelled: {
1133
+ label: string;
1134
+ type: TradeLevelType;
1135
+ isFullscreen: boolean;
1136
+ };
1117
1137
  /** Emitted when a draft order is cancelled (Escape, ✕ button, or external revert). */
1118
1138
  draftCancelled: {
1119
1139
  label: string;
@@ -1155,6 +1175,25 @@ type ChartEventMap = {
1155
1175
  tfcToggle: {
1156
1176
  enabled: boolean;
1157
1177
  };
1178
+ /** Emitted whenever any dismissible UI (flyout, modal, dropdown, popover) opens or closes.
1179
+ * Hosts (native wrappers, PWAs) listen to track whether a back button / gesture should
1180
+ * dismiss chart UI vs. navigate. Pair with `dismissAllUI()` to wire platform back events. */
1181
+ uiStateChange: {
1182
+ hasOpenUI: boolean;
1183
+ };
1184
+ /** Emitted live while the user edits lot qty via the QTY pill flyout, before the
1185
+ * level edit is confirmed. Use this to recompute qty-derived UI (e.g. Estimated
1186
+ * PNL on SL/TP brackets) in real time. Final committed qty still arrives on
1187
+ * `tradeLevelEdit.newLots` at ✓ confirm (or immediately for draft orders).
1188
+ * `type` is `'draft'` for the in-progress draft order, otherwise the parent level's type.
1189
+ * `previousLots` is the qty at edit-session start (useful for revert-aware previews). */
1190
+ tradeLevelQtyChange: {
1191
+ label: string;
1192
+ type: TradeLevelType | 'draft';
1193
+ newLots: number;
1194
+ previousLots: number;
1195
+ isFullscreen: boolean;
1196
+ };
1158
1197
  /** Emitted when the user confirms all edits to a level — replaces separate tradeLevelDragEnd / tradeLevelBracketDrag events. */
1159
1198
  tradeLevelEdit: {
1160
1199
  label: string;
@@ -865,6 +865,14 @@ interface ChartConfig {
865
865
  * Default: `false`.
866
866
  */
867
867
  hideLevelConfirmCancel?: boolean;
868
+ /**
869
+ * Multiplier applied to the Confirm (✓), Cancel (✗), Edit (✎), and Close (×)
870
+ * buttons on trade-level overlays (both main-level and SL/TP bracket boxes).
871
+ * Scales button radii and inter-button gaps so visual size AND hit/drag areas
872
+ * grow together — useful for touch targets.
873
+ * Clamped to `[1, 3]`. Default: `1`.
874
+ */
875
+ tradeLevelButtonScale?: number;
868
876
  /**
869
877
  * When `true`, a theme toggle (Dark / Light) is shown in the Settings dialog.
870
878
  * Default: `false` — the toggle is hidden.
@@ -1093,12 +1101,16 @@ type ChartEventMap = {
1093
1101
  isFullscreen: boolean;
1094
1102
  };
1095
1103
  /** Emitted on every mouse-move while dragging a trade level line (before confirm).
1096
- * `bracketType` is present when dragging an SL/TP bracket line; absent for main entry drag. */
1104
+ * `bracketType` is present when dragging an SL/TP bracket line; absent for main entry drag.
1105
+ * `derivedSLPrice` / `derivedTPPrice` are present during a main-entry drag in follow-brackets mode
1106
+ * so external panels can mirror the SL/TP translation live (keeps validation from flickering). */
1097
1107
  tradeLevelDrag: {
1098
1108
  label: string;
1099
1109
  newPrice: number;
1100
1110
  data: unknown;
1101
1111
  bracketType?: 'stopLoss' | 'takeProfit';
1112
+ derivedSLPrice?: number;
1113
+ derivedTPPrice?: number;
1102
1114
  isFullscreen: boolean;
1103
1115
  };
1104
1116
  /** Emitted at the instant a bracket (SL/TP) drag begins — before any movement.
@@ -1114,6 +1126,14 @@ type ChartEventMap = {
1114
1126
  type: TradeLevelType | 'draft';
1115
1127
  isFullscreen: boolean;
1116
1128
  };
1129
+ /** Emitted when an in-progress level edit is cancelled from the chart (ESC key or inline ✕ cancel button).
1130
+ * Mirrors `tradeLevelConfirmed` but for the revert path. Not fired for draft orders (those emit `draftCancelled`).
1131
+ * External panels listen to reset their modify-order form when the user aborts the edit from the chart side. */
1132
+ tradeLevelEditCancelled: {
1133
+ label: string;
1134
+ type: TradeLevelType;
1135
+ isFullscreen: boolean;
1136
+ };
1117
1137
  /** Emitted when a draft order is cancelled (Escape, ✕ button, or external revert). */
1118
1138
  draftCancelled: {
1119
1139
  label: string;
@@ -1155,6 +1175,25 @@ type ChartEventMap = {
1155
1175
  tfcToggle: {
1156
1176
  enabled: boolean;
1157
1177
  };
1178
+ /** Emitted whenever any dismissible UI (flyout, modal, dropdown, popover) opens or closes.
1179
+ * Hosts (native wrappers, PWAs) listen to track whether a back button / gesture should
1180
+ * dismiss chart UI vs. navigate. Pair with `dismissAllUI()` to wire platform back events. */
1181
+ uiStateChange: {
1182
+ hasOpenUI: boolean;
1183
+ };
1184
+ /** Emitted live while the user edits lot qty via the QTY pill flyout, before the
1185
+ * level edit is confirmed. Use this to recompute qty-derived UI (e.g. Estimated
1186
+ * PNL on SL/TP brackets) in real time. Final committed qty still arrives on
1187
+ * `tradeLevelEdit.newLots` at ✓ confirm (or immediately for draft orders).
1188
+ * `type` is `'draft'` for the in-progress draft order, otherwise the parent level's type.
1189
+ * `previousLots` is the qty at edit-session start (useful for revert-aware previews). */
1190
+ tradeLevelQtyChange: {
1191
+ label: string;
1192
+ type: TradeLevelType | 'draft';
1193
+ newLots: number;
1194
+ previousLots: number;
1195
+ isFullscreen: boolean;
1196
+ };
1158
1197
  /** Emitted when the user confirms all edits to a level — replaces separate tradeLevelDragEnd / tradeLevelBracketDrag events. */
1159
1198
  tradeLevelEdit: {
1160
1199
  label: string;