@stocksharp/trading-controls 1.2.0 → 1.3.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.
Files changed (59) hide show
  1. package/README.md +95 -16
  2. package/dist/esm/black-scholes.js +147 -0
  3. package/dist/esm/black-scholes.js.map +1 -0
  4. package/dist/esm/control-types.js +4 -0
  5. package/dist/esm/control-types.js.map +1 -1
  6. package/dist/esm/index.js +18 -0
  7. package/dist/esm/index.js.map +1 -1
  8. package/dist/esm/log-monitor-widget.js +265 -0
  9. package/dist/esm/log-monitor-widget.js.map +1 -0
  10. package/dist/esm/log-tree.js +96 -0
  11. package/dist/esm/log-tree.js.map +1 -0
  12. package/dist/esm/option-desk-widget.js +322 -0
  13. package/dist/esm/option-desk-widget.js.map +1 -0
  14. package/dist/esm/pnl-curve.js +129 -0
  15. package/dist/esm/pnl-curve.js.map +1 -0
  16. package/dist/esm/statistics-widget.js +194 -0
  17. package/dist/esm/statistics-widget.js.map +1 -0
  18. package/dist/esm/strategies-widget.js +348 -0
  19. package/dist/esm/strategies-widget.js.map +1 -0
  20. package/dist/sstradingcontrols.js +1307 -47
  21. package/dist/sstradingcontrols.js.map +4 -4
  22. package/dist/types/black-scholes.d.ts +28 -0
  23. package/dist/types/black-scholes.d.ts.map +1 -0
  24. package/dist/types/control-types.d.ts +4 -0
  25. package/dist/types/control-types.d.ts.map +1 -1
  26. package/dist/types/index.d.ts +16 -1
  27. package/dist/types/index.d.ts.map +1 -1
  28. package/dist/types/log-monitor-widget.d.ts +42 -0
  29. package/dist/types/log-monitor-widget.d.ts.map +1 -0
  30. package/dist/types/log-tree.d.ts +34 -0
  31. package/dist/types/log-tree.d.ts.map +1 -0
  32. package/dist/types/option-desk-widget.d.ts +68 -0
  33. package/dist/types/option-desk-widget.d.ts.map +1 -0
  34. package/dist/types/pnl-curve.d.ts +43 -0
  35. package/dist/types/pnl-curve.d.ts.map +1 -0
  36. package/dist/types/statistics-widget.d.ts +29 -0
  37. package/dist/types/statistics-widget.d.ts.map +1 -0
  38. package/dist/types/strategies-widget.d.ts +63 -0
  39. package/dist/types/strategies-widget.d.ts.map +1 -0
  40. package/dist/types/trading-data.d.ts +9 -0
  41. package/dist/types/trading-data.d.ts.map +1 -1
  42. package/package.json +27 -2
  43. package/screenshots/log-monitor.png +0 -0
  44. package/screenshots/option-desk.png +0 -0
  45. package/screenshots/panels.jpg +0 -0
  46. package/screenshots/statistics.png +0 -0
  47. package/screenshots/strategies.png +0 -0
  48. package/src/black-scholes.ts +199 -0
  49. package/src/control-types.ts +4 -0
  50. package/src/index.ts +41 -0
  51. package/src/log-monitor-widget.ts +312 -0
  52. package/src/log-tree.ts +131 -0
  53. package/src/option-desk-widget.ts +422 -0
  54. package/src/pnl-curve.ts +204 -0
  55. package/src/statistics-widget.ts +226 -0
  56. package/src/strategies-widget.ts +435 -0
  57. package/src/trading-data.ts +22 -0
  58. package/styles/trading-controls.css +356 -0
  59. package/translation-keys.json +70 -1
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../src/index.ts", "../src/trading-host.ts", "../src/control-types.ts", "../src/formatters.ts", "../src/dom.ts", "../src/grid-menu.ts", "../node_modules/@stocksharp/grids/src/table-sort.ts", "../node_modules/@stocksharp/grids/src/context-menu.ts", "../node_modules/@stocksharp/grids/src/filter-dialog.ts", "../node_modules/@stocksharp/grids/src/table-export.ts", "../node_modules/@stocksharp/grids/src/data-grid.ts", "../src/active-orders-widget.ts", "../src/positions-widget.ts", "../src/trade-history-widget.ts", "../src/watchlist-widget.ts", "../src/order-entry-widget.ts", "../src/orderbook-depth.ts", "../src/orderbook-widget.ts", "../src/tradefeed-aggregator.ts", "../src/tradefeed-bubbles.ts", "../src/tradefeed-widget.ts"],
4
- "sourcesContent": ["export { MarketDataLevels, PRESENTATION_CLASSES, assertHost } from './trading-host.js';\n\nexport type {\n HostStore,\n MarketDataClient,\n MarketDataLevel,\n TickerSink,\n TradingApi,\n TradingContext,\n TradingControl,\n TradingHost,\n TradingPresentation,\n} from './trading-host.js';\n\nexport type {\n BalanceRow,\n InstrumentRow,\n OrderRow,\n OrderSide,\n OrderStatus,\n OrderType,\n PositionRow,\n QuoteStats,\n TradeRow,\n} from './trading-data.js';\n\nexport { ControlTypes } from './control-types.js';\n\nexport type { ControlType } from './control-types.js';\n\nexport { cleanRejectReason, formatPnl, formatPrice, formatQty, formatTime } from './formatters.js';\n\n// A host that renders its own chrome around these controls builds it with the\n// same helpers, so the panel it wraps and the panel it wraps them in agree on\n// class names.\nexport { makeElement, makeIcon, makeIconButton, makePanelId, makePanelRoot } from './dom.js';\n\nexport { ActiveOrdersWidget, OrderStates } from './active-orders-widget.js';\n\nexport type { ActiveOrdersDeps } from './active-orders-widget.js';\n\nexport { PositionsWidget } from './positions-widget.js';\n\nexport type { PositionsDeps } from './positions-widget.js';\n\nexport { TradeHistoryWidget } from './trade-history-widget.js';\n\nexport type { TradeHistoryDeps } from './trade-history-widget.js';\n\nexport { WatchlistWidget } from './watchlist-widget.js';\n\nexport type { WatchlistDeps } from './watchlist-widget.js';\n\nexport { OrderEntrySides, OrderEntryTypes, OrderEntryWidget } from './order-entry-widget.js';\n\nexport type { OrderEntryDeps, OrderEntrySide, OrderEntryType, OrderEntryValues } from './order-entry-widget.js';\n\nexport type { InstrumentSpec } from './trading-data.js';\n\nexport { OrderBookWidget } from './orderbook-widget.js';\n\nexport type { OrderBookDeps, OrderBookView } from './orderbook-widget.js';\n\nexport type { BookLevel, OrderBookFrame, QuoteLevel } from './trading-data.js';\n\n// The palette a host answers with for the parts a control draws on a canvas,\n// where a class name cannot reach.\nexport type { CanvasPalette } from './trading-host.js';\n\nexport { TradeFeedWidget } from './tradefeed-widget.js';\n\nexport type { TradeFeedDeps } from './tradefeed-widget.js';\n\n// The tape's own shapes: one print as the chart reads it, and the bubble a\n// print (or a slice of them) is drawn as. A host that renders its own view of\n// the same feed reads these rather than reinventing the compaction.\nexport { aggregateBubbles } from './tradefeed-aggregator.js';\n\nexport type { FeedBubble, FeedTick } from './tradefeed-aggregator.js';\n\nexport { layoutBubbles } from './tradefeed-bubbles.js';\n\nexport type { BubbleAxisTick, BubbleLane, BubbleLaneShape, BubbleLayout, BubbleLayoutInput, BubbleShape } from './tradefeed-bubbles.js';\n", "// The host port \u2014 the whole of what a trading control needs from whoever\n// embeds it.\n//\n// A control is written against this interface and nothing else: it never\n// imports a translator, a preference singleton, a panel registry or a docking\n// manager, and it never reaches a window global. A host \u2014 a terminal page, a\n// backoffice screen, a demo harness \u2014 supplies one object shaped like\n// `TradingHost` per control instance, and that object is the only way out of\n// the control.\n//\n// This module imports nothing but the row shapes it names. It is the contract,\n// so it must be readable by a host that shares none of the package's internals.\n//\n// Everything here is REQUIRED, and `assertHost` proves it at construction.\n// There is no optional member and no default, because a half-supplied host is\n// how a control half-works: it renders, it looks alive, and the one capability\n// nobody wired is discovered by a user clicking something that does nothing.\n// A missing member has to be a loud failure at wiring time, not a quiet one at\n// click time.\n//\n// Every member here has a caller among the controls this package ships. The\n// seven that once looked speculative \u2014 `spawn`, `persistState`, `saveLayout`,\n// `log`, `trading.pickInstrument`, `marketData.resubscribe` and\n// `marketData.getOrders` \u2014 are the order book's and the trade feed's: a ladder\n// recovers from a sequence gap with `resubscribe` without unsubscribing its\n// neighbours, badges its own resting size from `getOrders`, reports the wire\n// anomalies a user cannot see through `log`, and retargets itself through\n// `pickInstrument`; both of them `spawn` a sibling and record what they\n// remember with `persistState` + `saveLayout`.\n//\n// The cost of \"everything is required\" is still real and worth stating: a host\n// adopting a subset must supply members that subset never calls. They are cheap\n// (a no-op `spawn`, a `log` that forwards to the console) \u2014 but they are not\n// free, and an adopter who supplies stubs is not doing anything wrong.\nimport type { InstrumentRow, OrderRow, OrderSide, OrderStatus, OrderType, QuoteStats, TradeRow } from './trading-data.js';\n\n/// The colours a control has to paint with as values rather than as class\n/// names.\n///\n/// A class name cannot reach a `<canvas>`: the pixels are drawn by the control,\n/// so the palette has to arrive as strings. Every other look in this package is\n/// a class the host's stylesheet gives meaning to, and this is the one place\n/// that arrangement cannot hold \u2014 which is why the colours come from the host\n/// here too, rather than being written into the package as literals the host\n/// never chose. Each value is any CSS colour the host's palette uses; a control\n/// varies the opacity itself, so a solid colour is the expected answer.\nexport interface CanvasPalette {\n /// The rising / bid / buy direction \u2014 the colour behind `side-buy`.\n up: string;\n /// The falling / ask / sell direction \u2014 the colour behind `side-sell`.\n down: string;\n /// Dividers, gridlines and axes drawn on a canvas \u2014 axis labels included.\n grid: string;\n /// Type for the text a control draws on a canvas, as the `font` shorthand\n /// (`'10px monospace'`). A canvas inherits nothing from the page and\n /// resolves no custom property, so a font is the one measurement the host\n /// has to state as a value; the alternative is the package picking a size\n /// and a family the host's design system never chose.\n font: string;\n}\n\n/// How a host words and colours the trading vocabulary. The control owns the\n/// data; the host owns the language and the stylesheet, so it says what a side\n/// or a status reads as and which CSS class carries its colour.\n///\n/// The two class methods are the one place a host has to agree with the shipped\n/// stylesheet rather than merely supply values to it. A control puts whatever\n/// string comes back straight onto the cell without inspecting it, so nothing\n/// in `src/` ever emits these names and `styles/trading-controls.css` cannot be\n/// read to discover them. They are named here, and `PRESENTATION_CLASSES` below\n/// is the same list as data \u2014 `tools/check-style-contract.mjs` asserts the\n/// stylesheet styles every one of them, so the agreement is checked rather than\n/// discovered by a cell that came out unstyled.\nexport interface TradingPresentation {\n /// Localized \"Buy\" / \"Sell\".\n sideText(side: OrderSide): string;\n /// Is this side a buy? The same knowledge `sideText` and `sideClass`\n /// already need, asked for as a predicate: a control that has to put a row\n /// on the bid or the ask half of a book is deciding structure, not wording,\n /// and the alternative is each control spelling out which of `0`, `'Buy'`\n /// and `'BUY'` the wire it happens to be fed uses.\n isBuy(side: OrderSide): boolean;\n /// Localized short order type \u2014 \"LMT\", \"MKT\", \"STP\", \"STP-LMT\". Needs both\n /// prices because a conditional order is only a stop-limit when it carries\n /// a limit price as well as a stop price.\n typeText(type: OrderType, limitPrice: number, stopPrice: number): string;\n /// Localized order status.\n statusText(status: OrderStatus): string;\n /// A moment, worded. The host owns this for the same reason it owns a side or a\n /// status: what a time should read as is not a property of the data. A live tape wants\n /// the time of day in the reader's own zone; a blotter over a finished run wants the\n /// date too, and in the zone the run was recorded in, or every row reads as the same\n /// unplaceable day. `formatTime` is this package's tape answer, exported so a host that\n /// wants exactly that need not restate it.\n timeText(value: string | number | Date): string;\n /// CSS class colouring a cell by side. The shipped stylesheet styles\n /// `side-buy` and `side-sell`; a host on its own palette may answer with\n /// its own names and style those instead.\n sideClass(side: OrderSide): string;\n /// CSS class colouring a cell by the sign of a P&L figure. The shipped\n /// stylesheet styles `pnl-positive` and `pnl-negative`; the empty string is\n /// a legitimate answer for \"no colour\".\n pnlClass(pnl: number): string;\n /// The palette for the parts a control draws itself. Read per paint, so a\n /// host that switches theme repaints in the new colours without the control\n /// being told a theme exists.\n canvasPalette(): CanvasPalette;\n}\n\n/// The class names `TradingPresentation` is expected to return, as data.\n///\n/// Exported because it is a contract, not a detail: a host writing its own\n/// presentation reads this to know which names the shipped stylesheet already\n/// paints, and the style-contract check reads it to prove they are all styled.\nexport const PRESENTATION_CLASSES = {\n sideClass: ['side-buy', 'side-sell'],\n pnlClass: ['pnl-positive', 'pnl-negative'],\n} as const;\n\n/// A string-keyed store. Two of these reach a control and they are NOT\n/// interchangeable \u2014 see `TradingHost.preferences` and `TradingHost.cache`.\nexport interface HostStore {\n /// The stored value, or `fallback` when the key was never written.\n get(key: string, fallback: string | null): string | null;\n /// Write, or clear by writing null.\n set(key: string, value: string | null): void;\n}\n\n/// The read side of the trading account, as far as a control is concerned.\n/// Narrow on purpose: a control asks for the few calls it makes rather than\n/// being handed a whole API client to explore.\nexport interface TradingApi {\n /// Executed trades, newest first, for one portfolio. A null `symbol` means\n /// every instrument \u2014 the argument is stated either way rather than being\n /// left off, so a call site says which of the two it wants.\n getExecutions(portfolioId: number, symbol: string | null, limit: number): Promise<TradeRow[]>;\n /// Instruments matching a query; the empty query means \"everything\".\n searchInstruments(query: string): Promise<InstrumentRow[]>;\n}\n\n/// How much of a symbol a control wants streamed. A quote row needs the last\n/// price, a tape needs every print, a ladder needs the book as well \u2014 and each\n/// costs the wire something different, so a control states which it is asking\n/// for rather than leaving the argument off and taking a default.\nexport const MarketDataLevels = {\n /// Level 1 only: best bid/ask and last price.\n Quotes: 'l1only',\n /// Level 1 plus every tick.\n Tape: 'tape',\n /// Level 1, ticks and market depth.\n Full: 'full',\n} as const;\n\nexport type MarketDataLevel = typeof MarketDataLevels[keyof typeof MarketDataLevels];\n\n/// The live market-data connection. `addSymbol` / `removeSymbol` are\n/// refcounted by the implementation, so two controls watching one symbol hold\n/// one subscription between them and neither can unsubscribe the other.\nexport interface MarketDataClient {\n addSymbol(symbol: string, level: MarketDataLevel): Promise<unknown>;\n removeSymbol(symbol: string): Promise<unknown>;\n /// Make the server resend `symbol` from scratch, without touching the\n /// refcount. A control applying diffs against a sequence cannot recover\n /// from a missed frame on its own \u2014 the only cure is a fresh snapshot, and\n /// asking for one must not unsubscribe the other controls watching the\n /// same symbol.\n resubscribe(symbol: string, level: MarketDataLevel): Promise<void>;\n /// The client's cache of this session's own orders. The order book paints\n /// its \"you have size here\" badge from this rather than subscribing again.\n getOrders(): OrderRow[];\n}\n\n/// What a control reads about the account and the instrument universe.\nexport interface TradingContext {\n readonly api: TradingApi;\n readonly marketData: MarketDataClient;\n /// The portfolio a control should load against, read per use rather than\n /// captured \u2014 the user can switch portfolio under a control that is\n /// already on screen.\n portfolioId(): number | null;\n /// Ask the host to let the user pick an instrument. The host owns the\n /// picker UI; the control only learns what was chosen, and learns nothing\n /// if the user dismisses it.\n pickInstrument(onPicked: (symbol: string) => void): void;\n}\n\n/// Where a control's summary of what it is showing goes. A status-bar ticker is\n/// page chrome outside any control, so a control reports and the host decides\n/// what to do with the report.\nexport interface TickerSink {\n publish(symbols: string[], stats: Map<string, QuoteStats>): void;\n}\n\n/// One live control instance, from the host's side. Deliberately opaque: the\n/// host holds a heterogeneous set and only ever hands instances back to the\n/// control that asked for them, which is what `broadcast<T>` expresses.\nexport type TradingControl = object;\n\n/// Everything a trading control needs from its host.\nexport interface TradingHost {\n // -- this instance's role on the page ---------------------------------\n /// True when this instance is the one that speaks for the page. A page has\n /// one status-bar ticker and a control can be duplicated, so somebody has\n /// to decide which duplicate feeds it \u2014 and that is host policy, not\n /// something a control can work out about itself.\n readonly isPrimary: boolean;\n\n // -- language and wording ---------------------------------------------\n /// Translate. `key` is the English source text; `{0}`, `{1}` \u2026 in the\n /// translation are replaced positionally from `args`.\n t(key: string, ...args: unknown[]): string;\n readonly presentation: TradingPresentation;\n\n // -- storage ------------------------------------------------------------\n /// Settings that belong to the user and are expected to survive \u2014 a chosen\n /// depth, a view mode, a favourites list. May be slow and may be shared\n /// across devices.\n readonly preferences: HostStore;\n /// Local scratch that may be thrown away \u2014 a per-day price baseline, a\n /// memoised computation. Deliberately a different store from\n /// `preferences`: writing high-churn per-symbol keys into a synced\n /// per-user settings blob is how a settings row becomes a cache.\n readonly cache: HostStore;\n\n // -- the trading context it reads ---------------------------------------\n readonly trading: TradingContext;\n\n // -- where its ticker updates go ----------------------------------------\n readonly ticker: TickerSink;\n\n // -- permission and diagnostics -----------------------------------------\n /// May the user do this? `action` is the English name of the attempt\n /// (\"add to favorites\"); the host translates it and may show a sign-in\n /// prompt before answering false.\n allow(action: string): boolean;\n /// Send a diagnostic line wherever this host collects them. Controls use\n /// it for wire anomalies the user cannot see and support has to.\n log(message: string): void;\n\n // -- lifecycle calls it makes back --------------------------------------\n /// This control wants to go away.\n close(): void;\n /// The `+` gesture: another control of the same kind, starting from\n /// `state`. The host knows what kind this one is, so the control does not\n /// name its own type.\n spawn(state: Record<string, unknown>): void;\n /// Remember this control's per-instance state. Recording only \u2014 call\n /// `saveLayout` when the change is worth flushing.\n persistState(patch: Record<string, unknown>): void;\n /// Write the layout out now, bypassing any debounce the host applies.\n saveLayout(): void;\n\n // -- the host's handle on live instances --------------------------------\n /// Announce this instance. A host fans incoming data to every live control\n /// of a kind, so an unregistered control is a control that never updates.\n register(control: TradingControl): void;\n unregister(control: TradingControl): void;\n /// Apply something to every live control of this kind, this one included.\n /// The caller knows what its own siblings are, which is why `T` is its to\n /// name.\n broadcast<T>(apply: (control: T) => void): void;\n}\n\n/// Prove a host object satisfies the port, and say precisely what is missing\n/// when it does not.\n///\n/// Called from every control's constructor before anything else, so a\n/// mis-wired host fails at construction with the member named, instead of at\n/// the first click that needed it.\nexport function assertHost(host: TradingHost, controlName: string): TradingHost {\n _required(host, 'host', 'object', controlName);\n\n _required(host.isPrimary, 'host.isPrimary', 'boolean', controlName);\n _required(host.t, 'host.t', 'function', controlName);\n\n _required(host.presentation, 'host.presentation', 'object', controlName);\n _required(host.presentation.sideText, 'host.presentation.sideText', 'function', controlName);\n _required(host.presentation.isBuy, 'host.presentation.isBuy', 'function', controlName);\n _required(host.presentation.typeText, 'host.presentation.typeText', 'function', controlName);\n _required(host.presentation.statusText, 'host.presentation.statusText', 'function', controlName);\n _required(host.presentation.timeText, 'host.presentation.timeText', 'function', controlName);\n _required(host.presentation.sideClass, 'host.presentation.sideClass', 'function', controlName);\n _required(host.presentation.pnlClass, 'host.presentation.pnlClass', 'function', controlName);\n _required(host.presentation.canvasPalette, 'host.presentation.canvasPalette', 'function', controlName);\n\n _required(host.preferences, 'host.preferences', 'object', controlName);\n _required(host.preferences.get, 'host.preferences.get', 'function', controlName);\n _required(host.preferences.set, 'host.preferences.set', 'function', controlName);\n\n _required(host.cache, 'host.cache', 'object', controlName);\n _required(host.cache.get, 'host.cache.get', 'function', controlName);\n _required(host.cache.set, 'host.cache.set', 'function', controlName);\n\n _required(host.trading, 'host.trading', 'object', controlName);\n _required(host.trading.api, 'host.trading.api', 'object', controlName);\n // The calls, not just the objects holding them. An `api: {}` is the shape a\n // half-wired host actually has \u2014 the object gets built, the methods get\n // forgotten \u2014 so stopping at the object would skip exactly the members this\n // function exists to catch.\n _required(host.trading.api.getExecutions, 'host.trading.api.getExecutions', 'function', controlName);\n _required(host.trading.api.searchInstruments, 'host.trading.api.searchInstruments', 'function', controlName);\n _required(host.trading.marketData, 'host.trading.marketData', 'object', controlName);\n _required(host.trading.marketData.addSymbol, 'host.trading.marketData.addSymbol', 'function', controlName);\n _required(host.trading.marketData.removeSymbol, 'host.trading.marketData.removeSymbol', 'function', controlName);\n _required(host.trading.marketData.resubscribe, 'host.trading.marketData.resubscribe', 'function', controlName);\n _required(host.trading.marketData.getOrders, 'host.trading.marketData.getOrders', 'function', controlName);\n _required(host.trading.portfolioId, 'host.trading.portfolioId', 'function', controlName);\n _required(host.trading.pickInstrument, 'host.trading.pickInstrument', 'function', controlName);\n\n _required(host.ticker, 'host.ticker', 'object', controlName);\n _required(host.ticker.publish, 'host.ticker.publish', 'function', controlName);\n\n _required(host.allow, 'host.allow', 'function', controlName);\n _required(host.log, 'host.log', 'function', controlName);\n\n _required(host.close, 'host.close', 'function', controlName);\n _required(host.spawn, 'host.spawn', 'function', controlName);\n _required(host.persistState, 'host.persistState', 'function', controlName);\n _required(host.saveLayout, 'host.saveLayout', 'function', controlName);\n\n _required(host.register, 'host.register', 'function', controlName);\n _required(host.unregister, 'host.unregister', 'function', controlName);\n _required(host.broadcast, 'host.broadcast', 'function', controlName);\n\n return host;\n}\n\n// `typeof null` is 'object', which would let a null store through the object\n// checks \u2014 the one case worth spelling out, because null is exactly what an\n// unwired dependency looks like.\nfunction _required(value: unknown, path: string, kind: string, controlName: string): void {\n if (value === null || value === undefined || typeof value !== kind)\n throw new Error(`${controlName}: ${path} is required`);\n}\n", "// The identifiers a host and a control agree on for each kind of control this\n// package ships.\n//\n// These strings used to be spelled out independently in four places \u2014 a docking\n// manager's panel table, its default layouts, each control's static TYPE\n// literal and a Razor panels menu \u2014 so a typo in any one of them produced a\n// panel that opened but never received data, with nothing to compare against.\n//\n// The VALUES are load-bearing and must not be edited. A host writes them into\n// whatever it persists its layout as, so renaming one orphans every saved\n// layout that mentions it.\n//\n// Only the kinds this package implements are named here. A host that has more\n// control kinds of its own declares those itself and merges the two sets; that\n// keeps every identifier declared exactly once, by whoever owns the control it\n// names.\nexport const ControlTypes = {\n Watchlist: 'watchlist',\n ActiveOrders: 'activeOrders',\n TradeHistory: 'tradeHistory',\n Positions: 'positions',\n OrderBook: 'orderbook',\n TradeFeed: 'tradefeed',\n OrderEntry: 'orderEntry',\n} as const;\n\nexport type ControlType = typeof ControlTypes[keyof typeof ControlTypes];\n", "// Value formatters \u2014 pure functions over numbers, prices, quantities, times\n// and one venue string.\n//\n// Nothing here reaches a translator, the DOM, a singleton or a window global,\n// and the module has no import and no side effect. That is why it is its own\n// file: this is the half of the presentation a control needs in ANY host. The\n// other half \u2014 a host's own vocabulary for a side, a type, a status \u2014 is\n// `TradingPresentation`, which is handed to a control rather than imported by\n// it.\ntype Numeric = number | string | null | undefined;\n\n/// Digits chosen from the magnitude of the price. A fixed two decimals\n/// collapses distinct ticks of a penny instrument onto the same \"0.44\" \u2014\n/// on the Y axis, in the order book, in the price header, everywhere \u2014 while\n/// six decimals on a $76,000 print is noise.\nexport function formatPrice(price: Numeric): string {\n if (price == null || isNaN(Number(price))) return '--';\n const n = Number(price);\n const a = Math.abs(n);\n let prec = 2;\n if (a < 1) prec = 4;\n if (a < 0.1) prec = 5;\n if (a < 0.001) prec = 6;\n if (a >= 1000) prec = 1;\n if (a >= 10000) prec = 0;\n return n.toFixed(prec);\n}\n\n/// Crypto quantities carry up to 8 decimals, so the cap is 8 rather than the\n/// locale default of 3; trailing zeros are trimmed because a size of \"1\" reads\n/// better than \"1.00000000\" in a ladder.\nexport function formatQty(qty: Numeric): string {\n if (qty == null) return '--';\n const n = Number(qty);\n if (!isFinite(n)) return '--';\n return n.toLocaleString(undefined, { maximumFractionDigits: 8 });\n}\n\n/// Wall-clock time of day, 24h, seconds included \u2014 a trade blotter is read by\n/// eye against the tape, so the format is fixed rather than locale-driven.\nexport function formatTime(dateStr: string | number | Date | null | undefined): string {\n const d = new Date(dateStr as string);\n return d.toLocaleTimeString('en-US', { hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit' });\n}\n\n/// A signed figure: profit carries an explicit `+` so a gain and a loss are\n/// told apart by the number alone, without relying on the colour class.\nexport function formatPnl(pnl: Numeric): string {\n if (pnl == null) return '--';\n const val = Number(pnl);\n const sign = val >= 0 ? '+' : '';\n return sign + val.toFixed(2);\n}\n\n/// Some venue adapters hand us a wrapped error string like\n/// \"failed to complete request (err=Forbidden): {\"code\":40310000,\"message\":\"cost basis must be >= ...\"}\"\n/// The raw form is stored for audit, but users should see the human-readable\n/// message only. Pull `message` out of the embedded JSON when present;\n/// otherwise return the reason as-is.\nexport function cleanRejectReason(reason: string | null | undefined): string {\n if (!reason) return '';\n const i = reason.indexOf('{');\n const j = reason.lastIndexOf('}');\n if (i >= 0 && j > i) {\n try {\n const parsed = JSON.parse(reason.substring(i, j + 1));\n if (parsed && typeof parsed.message === 'string' && parsed.message.length > 0)\n return parsed.message;\n } catch { /* fall through to raw */ }\n }\n return reason;\n}\n", "// Element construction for controls that build their own DOM.\n//\n// A control that clones a <template> out of the page it happens to live on\n// cannot render anywhere else, so the markup has to move into the control. It\n// then has to stay readable as markup, which four lines of createElement /\n// className / setAttribute / appendChild per element does not.\n//\n// Nothing here has an import, touches a global or knows what a trading control\n// is \u2014 it is a spelling of `document.createElement`, and the shape of the call\n// mirrors the shape of the tag it replaces:\n//\n// <div class=\"panel-header\"><span>Positions</span></div>\n// makeElement('div', 'panel-header', {}, [makeElement('span', '', {}, ['Positions'])])\n\n/// One element. `className` may be empty and `attrs` may be empty \u2014 both are\n/// then left off entirely, so the result matches markup that never carried\n/// them. Children are appended in order; a string child becomes a text node.\nexport function makeElement(\n tag: string,\n className: string,\n attrs: Record<string, string>,\n children: Array<Node | string>,\n): HTMLElement {\n const element = document.createElement(tag);\n if (className) element.className = className;\n for (const [name, value] of Object.entries(attrs)) element.setAttribute(name, value);\n for (const child of children)\n element.appendChild(typeof child === 'string' ? document.createTextNode(child) : child);\n return element;\n}\n\n/// A Bootstrap icon glyph \u2014 `<i class=\"bi bi-x\"></i>`. Every icon in a control's\n/// panel chrome is one of these, always with the `bi` base class.\nexport function makeIcon(iconClass: string): HTMLElement {\n return makeElement('i', `bi ${iconClass}`, {}, []);\n}\n\n/// An icon button from the panel chrome: one glyph, and the same text as both\n/// tooltip and accessible name. `className` carries the full class list\n/// because the buttons differ by more than one modifier\n/// (`bt-icon-btn bt-icon-cancel panel-close-btn`).\nexport function makeIconButton(\n className: string,\n label: string,\n iconClass: string,\n attrs: Record<string, string>,\n): HTMLButtonElement {\n return makeElement('button', className, { title: label, 'aria-label': label, ...attrs },\n [makeIcon(iconClass)]) as HTMLButtonElement;\n}\n\n/// The panel root every control builds: `.terminal-panel` plus the control's own\n/// modifier class, carrying the region role and its accessible name.\n///\n/// The structure is a contract in two directions and both are load-bearing. A\n/// host stylesheet targets these class names, and a docking host may lift\n/// `.panel-header`'s children into its own tab strip, which needs\n/// `.terminal-panel` to be the root's only element child.\nexport function makePanelRoot(modifierClass: string, label: string, children: Array<Node | string>): HTMLElement {\n return makeElement('div', `terminal-panel ${modifierClass}`, { role: 'region', 'aria-label': label }, children);\n}\n\n/// A unique element id for one control instance. Time plus a random tail: the\n/// time alone collides when a host restores a saved layout, which creates every\n/// panel inside one millisecond.\nexport function makePanelId(type: string): string {\n return `panel-${type}-${Date.now().toString(36)}-${Math.floor(Math.random() * 1000).toString(36)}`;\n}\n", "// The wording of the grid's context menu and its filter dialog.\n//\n// `@stocksharp/grids` draws the menu and speaks English by default; a control\n// here says nothing in its own words, so every label is taken from `host.t()`\n// and handed over through the grid's `contextMenu` option. Each blotter calls\n// this at construction \u2014 the labels are read once per grid, which is the same\n// lifetime as every other caption a control renders.\n//\n// Every key below is a literal on purpose: `translation-keys.json` is generated\n// by scanning for them, and a computed key would be invisible to that scan and\n// reach a user as itself.\nimport { TradingHost } from './trading-host.js';\nimport type { GridMenuOptions } from '@stocksharp/grids/source/data-grid';\n\n/// The grid's `contextMenu` option, worded by the host. Generic over the row\n/// type only because the option is; no label depends on it.\nexport function makeGridMenu<TRow>(host: TradingHost): GridMenuOptions<TRow> {\n return {\n labels: {\n sortAsc: host.t('SortAscending'),\n sortDesc: host.t('SortDescending'),\n sortClear: host.t('ClearSort'),\n hideColumn: host.t('HideColumn'),\n showAllColumns: host.t('ShowAllColumns'),\n groupBy: host.t('GroupByColumn'),\n ungroup: host.t('Ungroup'),\n filterByValue: (text) => host.t('Filter by this value: {0}', text),\n filterRule: host.t('FilterRule'),\n showFilters: host.t('ShowFilterRow'),\n hideFilters: host.t('HideFilterRow'),\n showHeader: host.t('ShowHeaderRow'),\n hideHeader: host.t('HideHeaderRow'),\n clearFilters: host.t('ClearFilters'),\n copyCell: host.t('CopyCell'),\n copyRow: host.t('CopyRow'),\n copyRows: (count) => host.t('Copy selected rows ({0})', count),\n exportXlsx: host.t('ExportToXlsx'),\n },\n filterDialog: {\n labels: {\n title: (header) => host.t('Filter: {0}', header),\n apply: host.t('Apply'),\n clear: host.t('Clear'),\n cancel: host.t('Cancel'),\n value: host.t('Value'),\n and: host.t('FilterAnd'),\n ops: {\n contains: host.t('FilterOpContains'),\n notContains: host.t('FilterOpNotContains'),\n startsWith: host.t('FilterOpStartsWith'),\n endsWith: host.t('FilterOpEndsWith'),\n eq: host.t('FilterOpEq'),\n ne: host.t('FilterOpNe'),\n gt: host.t('FilterOpGt'),\n ge: host.t('FilterOpGe'),\n lt: host.t('FilterOpLt'),\n le: host.t('FilterOpLe'),\n between: host.t('FilterOpBetween'),\n anyOf: host.t('FilterOpAnyOf'),\n noneOf: host.t('FilterOpNoneOf'),\n empty: host.t('FilterOpEmpty'),\n notEmpty: host.t('FilterOpNotEmpty'),\n },\n },\n },\n };\n}\n", "// Column-sort controller \u2014 the sort state behind DataGrid.\n//\n// Attach to the table's <thead>: clicking a <th data-sort=\"key\"> cycles\n// asc -> desc -> default. There is no \"unsorted\" state: a table always has a\n// deterministic order. Each table declares its default (e.g. orders newest-\n// first, trades most-recent-first) so the resting view is meaningful instead of\n// exposing the raw cache/insertion order (storage hydration + snapshot + live\n// updates land in an order that means nothing to the user). Clicking a header\n// overrides the default; cycling past desc returns to it. The owner re-renders\n// through onChange and passes its row array through apply(), which returns a\n// sorted copy, so live inserts land in the active order on the next render.\n//\n// DataGrid builds the accessors from its column declarations and owns the\n// instance; it is separate from the grid because sort state is a self-contained\n// concern with its own semantics worth pinning down on its own.\n\n/**\n * The two directions a column can be sorted in. Spelled as a const object so the\n * values have names at the call site and the union stays derived from one place.\n */\nexport const SortDirections = {\n Asc: 'asc',\n Desc: 'desc',\n} as const;\n\nexport type SortDir = typeof SortDirections[keyof typeof SortDirections];\n\nexport interface SortSpec { col: string; dir: SortDir; }\n\n// A column with no declared accessor is read off the row by its own key, which\n// needs a shape TRow never promised \u2014 so the widening happens here, once, instead\n// of loosening the row type of the whole controller.\nfunction readByKey(row: unknown, key: string): unknown {\n return row === null || row === undefined ? undefined : (row as Record<string, unknown>)[key];\n}\n\nexport class TableSort<TRow = unknown> {\n col: string | null;\n dir: SortDir;\n private _headerEl: HTMLElement | null;\n private _accessors: Record<string, (row: TRow) => unknown>;\n private _onChange: () => void;\n private _default: SortSpec | null;\n private readonly _collator: Intl.Collator;\n\n // `defaultSort` is the order applied when no header is explicitly selected \u2014 the table's\n // resting sort. Pass null only for a table that genuinely has no meaningful default (raw row\n // order); every real blotter should name one so its first paint is deterministic.\n constructor(\n headerEl: HTMLElement | null,\n accessors: Record<string, (row: TRow) => unknown>,\n onChange: () => void,\n defaultSort: SortSpec | null,\n collator: Intl.Collator,\n ) {\n this.col = null;\n this.dir = SortDirections.Asc;\n this._collator = collator;\n this._headerEl = headerEl;\n this._accessors = accessors || {};\n this._onChange = onChange;\n this._default = defaultSort;\n this._updateHeader();\n\n headerEl?.addEventListener('click', (e) => {\n const tgt = e.target as Element | null;\n const th = tgt?.closest('[data-sort]') as HTMLElement | null;\n if (!th) return;\n const col = th.dataset.sort!;\n if (this.col !== col) { this.col = col; this.dir = SortDirections.Asc; }\n else if (this.dir === SortDirections.Asc) this.dir = SortDirections.Desc;\n else { this.col = null; this.dir = SortDirections.Asc; } // back to the table default\n this._updateHeader();\n this._onChange();\n });\n }\n\n // The order actually in effect: the explicitly-picked column, else the table's default.\n private _effective(): SortSpec | null {\n return this.col ? { col: this.col, dir: this.dir } : this._default;\n }\n\n /**\n * Sorted copy of rows by the effective sort (explicit column or table default). Rows with no\n * value for the sort column group at the end. Always a copy: a caller told its array is never\n * reordered must be able to rely on that for one row and no sort as much as for a sorted many.\n */\n apply(rows: TRow[]): TRow[] {\n if (!Array.isArray(rows)) return rows;\n const eff = this._effective();\n if (!eff || rows.length < 2) return [...rows];\n const col = eff.col;\n const acc = this._accessors[col] || ((r: TRow) => readByKey(r, col));\n const factor = eff.dir === SortDirections.Asc ? 1 : -1;\n return [...rows].sort((a, b) => {\n const av = acc(a);\n const bv = acc(b);\n const aEmpty = av === null || av === undefined || av === '';\n const bEmpty = bv === null || bv === undefined || bv === '';\n if (aEmpty && bEmpty) return 0;\n if (aEmpty) return 1; // empties sink regardless of direction\n if (bEmpty) return -1;\n return this._compare(av, bv) * factor;\n });\n }\n\n private _compare(a: unknown, b: unknown): number {\n const an = typeof a === 'number' ? a : Number(a);\n const bn = typeof b === 'number' ? b : Number(b);\n if (isFinite(an) && isFinite(bn)) return an - bn;\n // Through the collator the caller handed over, so the order matches the\n // language the table is written in rather than whatever the visitor's browser\n // happens to be set to -- and so it is built once instead of per comparison.\n return this._collator.compare(String(a), String(b));\n }\n\n /**\n * The column the user picked, or null while the table's own default is in effect.\n *\n * Deliberately not the effective sort: this is what gets stored and handed back\n * to `set`, and storing the default as though it were a choice would pin it --\n * a later change to the table's default would then never reach anyone who had\n * ever looked at the table.\n */\n current(): SortSpec | null {\n return this.col ? { col: this.col, dir: this.dir } : null;\n }\n\n /** Pick a column, or pass null to fall back to the table's default. */\n set(col: string | null, dir: SortDir | null): void {\n this.col = col;\n this.dir = dir || SortDirections.Asc;\n this._updateHeader();\n this._onChange();\n }\n\n /**\n * Re-mark the header cells. A grid that rebuilds its `<thead>` -- moving a column,\n * hiding one -- throws away the marks with the old cells, and the rows would then\n * sit sorted under a header that says nothing about why.\n */\n refreshHeader(): void {\n this._updateHeader();\n }\n\n private _updateHeader() {\n if (!this._headerEl) return;\n // Reflect the effective sort \u2014 including the table default \u2014 so the resting view shows why\n // the rows are ordered the way they are, not a blank header over a sorted table.\n const eff = this._effective();\n this._headerEl.querySelectorAll('[data-sort]').forEach((el: Element) => {\n const th = el as HTMLElement;\n const active = !!eff && eff.col === th.dataset.sort;\n th.classList.toggle('sort-asc', active && eff!.dir === SortDirections.Asc);\n th.classList.toggle('sort-desc', active && eff!.dir === SortDirections.Desc);\n });\n }\n}\n", "/// A context menu the grid can offer out of the box, and a host can take over.\n///\n/// The package draws no chrome anywhere else -- ColumnSettings hands the picker's\n/// markup to the host precisely so a page can use its own modal. A menu is the one\n/// place where that rule costs more than it buys: every host would write the same\n/// hundred lines to position a list at a pointer, close it on the next click and\n/// on Escape, and keep it inside the window. So the menu is built here, and the\n/// host decides its contents and its class names instead of its existence.\n\n/** One line in the menu. A separator is an item with no label. */\nexport interface GridMenuItem {\n /** Shown to the user. Omit for a separator. */\n label?: string;\n /** What the line does. A separator and a disabled line have none. */\n run?(): void;\n /** Drawn, but inert and dimmed -- so an action stays where the user expects it. */\n disabled?: boolean;\n /** Marks the line as ticked, for the toggles (a hidden column, a set filter). */\n checked?: boolean;\n}\n\n/** Class names for the menu's parts. The grid names elements; a host styles them. */\nexport interface GridMenuClasses {\n menu?: string;\n item?: string;\n separator?: string;\n disabled?: string;\n checked?: string;\n}\n\nconst DEFAULTS: Required<GridMenuClasses> = {\n menu: 'grid-menu',\n item: 'grid-menu-item',\n separator: 'grid-menu-separator',\n disabled: 'is-disabled',\n checked: 'is-checked',\n};\n\n/**\n * Shows one menu at a time for the whole page.\n *\n * One instance rather than one per grid: two menus open at once is never what a\n * user asked for, and a second grid opening its own would leave the first hanging\n * with no way back to it.\n */\nexport class GridContextMenu {\n private static _open: GridContextMenu | null = null;\n\n private readonly _classes: Required<GridMenuClasses>;\n private _el: HTMLElement | null = null;\n private _dismiss: (() => void) | null = null;\n\n constructor(classes?: GridMenuClasses) {\n this._classes = { ...DEFAULTS, ...(classes || {}) };\n }\n\n /** Whether this menu is the one currently on screen. */\n get isOpen(): boolean {\n return this._el !== null;\n }\n\n /**\n * Draw the items at (x, y) in viewport coordinates. Opening a menu closes\n * whichever one was open, including this one -- a right-click inside an open\n * menu means \"somewhere else\", not \"two menus\".\n */\n open(items: GridMenuItem[], x: number, y: number): void {\n GridContextMenu._open?.close();\n if (items.length === 0) return;\n\n const el = document.createElement('div');\n el.className = this._classes.menu;\n el.setAttribute('role', 'menu');\n el.style.position = 'fixed';\n el.style.left = `${x}px`;\n el.style.top = `${y}px`;\n\n for (const item of items) el.appendChild(this._itemEl(item));\n\n document.body.appendChild(el);\n this._el = el;\n GridContextMenu._open = this;\n\n // Bound now rather than on a timer. The gesture that opens a menu is over by\n // this point -- a right-click fires mousedown before contextmenu, and a menu\n // opened from a button is opened on the click that ends one -- so there is no\n // opening event left to dismiss it, and a timer would only make when it starts\n // listening depend on the event loop.\n const dismiss = (event: Event) => {\n if (event.type === 'keydown' && (event as KeyboardEvent).key !== 'Escape') return;\n // A press INSIDE the menu is the user choosing something. This listener runs\n // in the capture phase, before the click reaches the item, so closing here\n // would tear the item out from under the gesture and the menu would look\n // dead: it vanishes and nothing happens.\n const target = (event as unknown as { target?: Node }).target;\n if (event.type === 'mousedown' && target && el.contains(target)) return;\n this.close();\n };\n document.addEventListener('mousedown', dismiss, true);\n document.addEventListener('keydown', dismiss, true);\n window.addEventListener('resize', dismiss);\n window.addEventListener('scroll', dismiss, true);\n this._dismiss = () => {\n document.removeEventListener('mousedown', dismiss, true);\n document.removeEventListener('keydown', dismiss, true);\n window.removeEventListener('resize', dismiss);\n window.removeEventListener('scroll', dismiss, true);\n };\n\n this._keepInside(el, x, y);\n }\n\n close(): void {\n if (!this._el) return;\n this._dismiss?.();\n this._dismiss = null;\n this._el.parentNode?.removeChild(this._el);\n this._el = null;\n if (GridContextMenu._open === this) GridContextMenu._open = null;\n }\n\n private _itemEl(item: GridMenuItem): HTMLElement {\n if (!item.label) {\n const sep = document.createElement('div');\n sep.className = this._classes.separator;\n sep.setAttribute('role', 'separator');\n return sep;\n }\n\n const el = document.createElement('div');\n el.className = this._classes.item;\n if (item.disabled) el.className += ` ${this._classes.disabled}`;\n if (item.checked) el.className += ` ${this._classes.checked}`;\n el.setAttribute('role', 'menuitem');\n el.textContent = item.label;\n\n if (!item.disabled && item.run) {\n el.addEventListener('click', () => {\n // Close first: an action that repaints the grid would otherwise run\n // with a menu still floating over rows that no longer exist.\n this.close();\n item.run!();\n });\n }\n return el;\n }\n\n /**\n * Nudge the menu back inside the window when it was opened near an edge.\n * Measured after it is in the document, because until then it has no size.\n */\n private _keepInside(el: HTMLElement, x: number, y: number): void {\n const rect = typeof el.getBoundingClientRect === 'function' ? el.getBoundingClientRect() : null;\n if (!rect) return;\n\n const width = typeof window !== 'undefined' ? window.innerWidth : 0;\n const height = typeof window !== 'undefined' ? window.innerHeight : 0;\n if (width && x + rect.width > width) el.style.left = `${Math.max(0, width - rect.width)}px`;\n if (height && y + rect.height > height) el.style.top = `${Math.max(0, height - rect.height)}px`;\n }\n}\n", "/// The popup a column's filter rule is built in: an operator and its operand.\n///\n/// The inline filter row stays what it is -- one box per column, a substring or a\n/// range, typed without leaving the table. It cannot express \"not equal\", \"starts\n/// with\" or \"is empty\" without growing a second control per column, and a header\n/// row is the wrong place to grow controls. So the rules live here, one column at\n/// a time, opened from the menu or from the funnel in the filter cell.\n///\n/// Same bargain as the context menu: the package builds the popup because every\n/// host would otherwise write the same positioning and dismissal, and the host\n/// decides its class names and its wording.\n\nimport type { GridFilter, GridFilterKind, GridFilterOp, GridValueOption } from './data-grid.js';\n\n/** Class names for the dialog's parts. */\nexport interface GridFilterDialogClasses {\n dialog?: string;\n title?: string;\n row?: string;\n select?: string;\n input?: string;\n /** The scrolling list of values a set filter is picked from. */\n values?: string;\n /** One tickable value in that list. */\n valuesItem?: string;\n actions?: string;\n apply?: string;\n clear?: string;\n cancel?: string;\n}\n\n/** Every phrase the dialog shows. */\nexport interface GridFilterDialogLabels {\n /**\n * Heads the dialog with the column it is filtering. A function because joining a\n * word to a column name is a language's business, down to the colon.\n */\n title(header: string): string;\n apply: string;\n clear: string;\n cancel: string;\n value: string;\n and: string;\n ops: Record<GridFilterOp, string>;\n}\n\nconst DEFAULT_CLASSES: Required<GridFilterDialogClasses> = {\n dialog: 'grid-filter-dialog',\n title: 'grid-filter-dialog-title',\n row: 'grid-filter-dialog-row',\n select: 'grid-filter-dialog-op',\n input: 'grid-filter-dialog-value',\n values: 'grid-filter-dialog-values',\n valuesItem: 'grid-filter-dialog-values-item',\n actions: 'grid-filter-dialog-actions',\n apply: 'grid-filter-dialog-apply',\n clear: 'grid-filter-dialog-clear',\n cancel: 'grid-filter-dialog-cancel',\n};\n\nconst DEFAULT_LABELS: GridFilterDialogLabels = {\n title: header => `Filter: ${header}`,\n apply: 'Apply',\n clear: 'Clear',\n cancel: 'Cancel',\n value: 'Value',\n and: 'and',\n ops: {\n contains: 'contains',\n notContains: 'does not contain',\n startsWith: 'starts with',\n endsWith: 'ends with',\n eq: '=',\n ne: '\u2260',\n gt: '>',\n ge: '\u2265',\n lt: '<',\n le: '\u2264',\n between: 'between',\n anyOf: 'is any of',\n noneOf: 'is none of',\n empty: 'is empty',\n notEmpty: 'is not empty',\n },\n};\n\n/// Which rules make sense for which kind of column. A number column offering\n/// \"starts with\" invites a filter that reads as a mistake; a text column offering\n/// only comparisons cannot express the thing text filters are actually for.\nconst OPS: Record<GridFilterKind, GridFilterOp[]> = {\n text: ['contains', 'notContains', 'startsWith', 'endsWith', 'eq', 'ne', 'empty', 'notEmpty'],\n number: ['eq', 'ne', 'gt', 'ge', 'lt', 'le', 'between', 'empty', 'notEmpty'],\n // A set column holds one of a handful of values, and the values are on screen to\n // be ticked. Asking such a column to be typed at is how a filter on a side column\n // ends up comparing \"Buy\" against the 0 the column actually holds.\n set: ['anyOf', 'noneOf', 'empty', 'notEmpty'],\n};\n\nconst NEEDS_NO_OPERAND = new Set<GridFilterOp>(['empty', 'notEmpty']);\nconst PICKS_VALUES = new Set<GridFilterOp>(['anyOf', 'noneOf']);\n\nexport class GridFilterDialog {\n private static _open: GridFilterDialog | null = null;\n\n private readonly _classes: Required<GridFilterDialogClasses>;\n private readonly _labels: GridFilterDialogLabels;\n private _el: HTMLElement | null = null;\n private _unbind: (() => void) | null = null;\n\n constructor(classes?: GridFilterDialogClasses, labels?: Partial<GridFilterDialogLabels>) {\n this._classes = { ...DEFAULT_CLASSES, ...(classes || {}) };\n this._labels = { ...DEFAULT_LABELS, ...(labels || {}), ops: { ...DEFAULT_LABELS.ops, ...(labels?.ops || {}) } };\n }\n\n get isOpen(): boolean {\n return this._el !== null;\n }\n\n /**\n * Ask for a filter on one column. `commit` is called with the new filter, or\n * with null when the user clears it; cancelling calls nothing at all, so a\n * dialog dismissed by mistake leaves the table exactly as it was.\n */\n open(\n options: {\n header: string;\n kind: GridFilterKind;\n current: GridFilter | null;\n /** The choices a set column is picked from; empty for every other kind. */\n values: GridValueOption[];\n x: number;\n y: number;\n },\n commit: (filter: GridFilter | null) => void,\n ): void {\n GridFilterDialog._open?.close();\n\n const ops = OPS[options.kind] || OPS.text;\n const current = options.current || {};\n const startOp: GridFilterOp = current.op\n || (current.min !== undefined || current.max !== undefined ? 'between' : ops[0]);\n const picked = new Set(current.values || []);\n\n const el = document.createElement('div');\n el.className = this._classes.dialog;\n el.setAttribute('role', 'dialog');\n el.style.position = 'fixed';\n el.style.left = `${options.x}px`;\n el.style.top = `${options.y}px`;\n\n const title = document.createElement('div');\n title.className = this._classes.title;\n title.textContent = this._labels.title(options.header);\n el.appendChild(title);\n\n const row = document.createElement('div');\n row.className = this._classes.row;\n\n const select = document.createElement('select') as HTMLSelectElement;\n select.className = this._classes.select;\n for (const op of ops) {\n const option = document.createElement('option') as HTMLOptionElement;\n option.value = op;\n option.textContent = this._labels.ops[op];\n select.appendChild(option);\n }\n select.value = startOp;\n\n const value = document.createElement('input') as HTMLInputElement;\n value.className = this._classes.input;\n value.type = options.kind === 'number' ? 'number' : 'text';\n value.placeholder = this._labels.value;\n value.value = current.text !== undefined ? current.text\n : current.min !== undefined ? String(current.min) : '';\n\n const and = document.createElement('span');\n and.textContent = ` ${this._labels.and} `;\n\n const upper = document.createElement('input') as HTMLInputElement;\n upper.className = this._classes.input;\n upper.type = 'number';\n upper.value = current.max !== undefined ? String(current.max) : '';\n\n row.appendChild(select);\n row.appendChild(value);\n row.appendChild(and);\n row.appendChild(upper);\n el.appendChild(row);\n\n // The values a set column is picked from. Each box carries the raw value the\n // filter compares, and shows the label a person reads it by.\n const boxes: HTMLInputElement[] = [];\n const list = document.createElement('div');\n list.className = this._classes.values;\n for (const option of options.values) {\n const item = document.createElement('label');\n item.className = this._classes.valuesItem;\n const box = document.createElement('input') as HTMLInputElement;\n box.type = 'checkbox';\n box.value = option.value;\n box.checked = picked.has(option.value);\n boxes.push(box);\n item.appendChild(box);\n item.appendChild(document.createTextNode(option.label));\n list.appendChild(item);\n }\n if (options.values.length) el.appendChild(list);\n\n // The operand controls follow the rule: \"is empty\" takes none, \"between\"\n // takes two. Showing all three always would ask for values the rule ignores.\n const sync = () => {\n const op = select.value as GridFilterOp;\n const picks = PICKS_VALUES.has(op);\n const none = NEEDS_NO_OPERAND.has(op) || picks;\n value.style.display = none ? 'none' : '';\n and.style.display = op === 'between' ? '' : 'none';\n upper.style.display = op === 'between' ? '' : 'none';\n list.style.display = picks ? '' : 'none';\n };\n select.addEventListener('change', sync);\n sync();\n\n const actions = document.createElement('div');\n actions.className = this._classes.actions;\n const applyButton = this._button(this._labels.apply, this._classes.apply, () => {\n const op = select.value as GridFilterOp;\n if (NEEDS_NO_OPERAND.has(op)) return commit({ op });\n if (PICKS_VALUES.has(op)) {\n const values = boxes.filter(box => box.checked).map(box => box.value);\n // Nothing ticked is not a filter that hides everything; it is no filter.\n return commit(values.length ? { op, values } : null);\n }\n if (op === 'between') {\n const min = value.value.trim() === '' ? undefined : Number(value.value);\n const max = upper.value.trim() === '' ? undefined : Number(upper.value);\n return commit(min === undefined && max === undefined ? null : { op, min, max });\n }\n return commit(value.value.trim() === '' ? null : { op, text: value.value });\n });\n actions.appendChild(applyButton);\n actions.appendChild(this._button(this._labels.clear, this._classes.clear, () => commit(null)));\n actions.appendChild(this._button(this._labels.cancel, this._classes.cancel, () => {}));\n el.appendChild(actions);\n\n document.body.appendChild(el);\n this._el = el;\n GridFilterDialog._open = this;\n\n // Same rule as the menu: a press inside is the user working in the dialog,\n // not dismissing it -- and the capture phase means this runs before the\n // control under the pointer ever sees the press.\n const dismiss = (event: Event) => {\n const target = (event as unknown as { target?: Node }).target;\n if (event.type === 'keydown') {\n const key = (event as KeyboardEvent).key;\n // Enter in the operator list picks an operator. This listener runs in\n // the capture phase, so treating that Enter as Apply would submit the\n // rule the moment the user chose what it should be -- with the operand\n // still empty, which reads as the dialog closing by itself.\n if (key === 'Enter') {\n if (target !== select) applyButton.click();\n return;\n }\n if (key !== 'Escape') return;\n }\n if (event.type === 'mousedown' && target && el.contains(target)) return;\n this.close();\n };\n document.addEventListener('mousedown', dismiss, true);\n document.addEventListener('keydown', dismiss, true);\n this._unbind = () => {\n document.removeEventListener('mousedown', dismiss, true);\n document.removeEventListener('keydown', dismiss, true);\n };\n\n if (typeof value.focus === 'function') value.focus();\n }\n\n close(): void {\n if (!this._el) return;\n this._unbind?.();\n this._unbind = null;\n this._el.parentNode?.removeChild(this._el);\n this._el = null;\n if (GridFilterDialog._open === this) GridFilterDialog._open = null;\n }\n\n private _button(label: string, className: string, run: () => void): HTMLElement {\n const btn = document.createElement('button') as HTMLButtonElement;\n btn.type = 'button';\n btn.className = className;\n btn.textContent = label;\n btn.addEventListener('click', () => {\n // Closed first, so a commit that repaints does not leave a dialog floating\n // over a table that has moved underneath it.\n this.close();\n run();\n });\n return btn;\n }\n}\n", "// Client-side .xlsx export \u2014 the sheet DataGrid hands its rows to, and usable on\n// its own by any web bundle. No dependency on a host app: the caller passes\n// generic (baseName, sheetName, headers, rows) and gets a real workbook.\n//\n// Builds a real OOXML workbook (not CSV renamed): a store-only ZIP with the\n// minimal SpreadsheetML part set. Strings are written as inline strings,\n// finite numbers as native numeric cells, so Excel / LibreOffice / Numbers\n// open the file without conversion warnings. No third-party libraries.\n\nconst XML_DECL = '<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\\r\\n';\n\nconst CONTENT_TYPES = XML_DECL\n + '<Types xmlns=\"http://schemas.openxmlformats.org/package/2006/content-types\">'\n + '<Default Extension=\"rels\" ContentType=\"application/vnd.openxmlformats-package.relationships+xml\"/>'\n + '<Default Extension=\"xml\" ContentType=\"application/xml\"/>'\n + '<Override PartName=\"/xl/workbook.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml\"/>'\n + '<Override PartName=\"/xl/worksheets/sheet1.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml\"/>'\n + '</Types>';\n\nconst ROOT_RELS = XML_DECL\n + '<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\">'\n + '<Relationship Id=\"rId1\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument\" Target=\"xl/workbook.xml\"/>'\n + '</Relationships>';\n\nconst WORKBOOK_RELS = XML_DECL\n + '<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\">'\n + '<Relationship Id=\"rId1\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet\" Target=\"worksheets/sheet1.xml\"/>'\n + '</Relationships>';\n\nfunction workbookXml(sheetName: string): string {\n return XML_DECL\n + '<workbook xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\">'\n + `<sheets><sheet name=\"${escapeXml(sheetName)}\" sheetId=\"1\" r:id=\"rId1\"/></sheets>`\n + '</workbook>';\n}\n\nfunction escapeXml(s: string): string {\n return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\n}\n\n// 0 -> \"A\", 25 -> \"Z\", 26 -> \"AA\", ...\nfunction colRef(i: number): string {\n let ref = '';\n for (let n = i; n >= 0; n = Math.floor(n / 26) - 1)\n ref = String.fromCharCode(65 + (n % 26)) + ref;\n return ref;\n}\n\nfunction cellXml(ref: string, value: unknown): string {\n if (value === null || value === undefined || value === '') return '';\n if (typeof value === 'number' && isFinite(value))\n return `<c r=\"${ref}\"><v>${value}</v></c>`;\n return `<c r=\"${ref}\" t=\"inlineStr\"><is><t xml:space=\"preserve\">${escapeXml(String(value))}</t></is></c>`;\n}\n\nfunction sheetXml(headers: string[], rows: unknown[][]): string {\n let xml = XML_DECL\n + '<worksheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\"><sheetData>';\n const all: unknown[][] = [headers, ...rows];\n for (let r = 0; r < all.length; r++) {\n xml += `<row r=\"${r + 1}\">`;\n for (let c = 0; c < all[r].length; c++)\n xml += cellXml(colRef(c) + (r + 1), all[r][c]);\n xml += '</row>';\n }\n return xml + '</sheetData></worksheet>';\n}\n\n// --- store-only ZIP writer -------------------------------------------------\n\nconst CRC_TABLE = (() => {\n const table = new Uint32Array(256);\n for (let n = 0; n < 256; n++) {\n let c = n;\n for (let k = 0; k < 8; k++)\n c = (c & 1) ? 0xEDB88320 ^ (c >>> 1) : c >>> 1;\n table[n] = c >>> 0;\n }\n return table;\n})();\n\nfunction crc32(data: Uint8Array): number {\n let crc = 0xFFFFFFFF;\n for (let i = 0; i < data.length; i++)\n crc = CRC_TABLE[(crc ^ data[i]) & 0xFF] ^ (crc >>> 8);\n return (crc ^ 0xFFFFFFFF) >>> 0;\n}\n\nfunction zip(entries: { name: string; text: string }[]): Blob {\n const encoder = new TextEncoder();\n const parts: Uint8Array[] = [];\n const central: Uint8Array[] = [];\n let offset = 0;\n\n for (const entry of entries) {\n const name = encoder.encode(entry.name);\n const data = encoder.encode(entry.text);\n const crc = crc32(data);\n\n const local = new Uint8Array(30 + name.length);\n const lv = new DataView(local.buffer);\n lv.setUint32(0, 0x04034b50, true); // local file header signature\n lv.setUint16(4, 20, true); // version needed\n lv.setUint32(14, crc, true);\n lv.setUint32(18, data.length, true); // compressed (stored) size\n lv.setUint32(22, data.length, true); // uncompressed size\n lv.setUint16(26, name.length, true);\n local.set(name, 30);\n parts.push(local, data);\n\n const dir = new Uint8Array(46 + name.length);\n const dv = new DataView(dir.buffer);\n dv.setUint32(0, 0x02014b50, true); // central directory signature\n dv.setUint16(4, 20, true); // version made by\n dv.setUint16(6, 20, true); // version needed\n dv.setUint32(16, crc, true);\n dv.setUint32(20, data.length, true);\n dv.setUint32(24, data.length, true);\n dv.setUint16(28, name.length, true);\n dv.setUint32(42, offset, true); // local header offset\n dir.set(name, 46);\n central.push(dir);\n\n offset += local.length + data.length;\n }\n\n const dirSize = central.reduce((sum, d) => sum + d.length, 0);\n const eocd = new Uint8Array(22);\n const ev = new DataView(eocd.buffer);\n ev.setUint32(0, 0x06054b50, true); // end of central directory\n ev.setUint16(8, entries.length, true);\n ev.setUint16(10, entries.length, true);\n ev.setUint32(12, dirSize, true);\n ev.setUint32(16, offset, true);\n\n return new Blob([...parts, ...central, eocd] as BlobPart[], {\n type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n });\n}\n\n// --- public API ------------------------------------------------------------\n\nexport const TableExport = {\n /**\n * Build the workbook and trigger a browser download as\n * `<baseName>-YYYYMMDD-HHMMSS.xlsx`. Rows are exported exactly as the\n * caller passes them (i.e. in the currently rendered/sorted order).\n */\n download(baseName: string, sheetName: string, headers: string[], rows: unknown[][]): void {\n // Sheet names are capped at 31 chars and reject []:*?/\\ \u2014 sanitize\n // localized captions so a long/exotic translation can't corrupt the file.\n const safeSheet = (sheetName || '').replace(/[\\[\\]:*?/\\\\]/g, ' ').slice(0, 31).trim() || 'Sheet1';\n const blob = zip([\n { name: '[Content_Types].xml', text: CONTENT_TYPES },\n { name: '_rels/.rels', text: ROOT_RELS },\n { name: 'xl/workbook.xml', text: workbookXml(safeSheet) },\n { name: 'xl/_rels/workbook.xml.rels', text: WORKBOOK_RELS },\n { name: 'xl/worksheets/sheet1.xml', text: sheetXml(headers, rows) },\n ]);\n const stamp = new Date().toISOString().slice(0, 19).replace(/[-:]/g, '').replace('T', '-');\n const a = document.createElement('a');\n a.href = URL.createObjectURL(blob);\n a.download = `${baseName}-${stamp}.xlsx`;\n document.body.appendChild(a);\n a.click();\n a.remove();\n setTimeout(() => URL.revokeObjectURL(a.href), 5000);\n },\n};\n", "// DataGrid \u2014 a column-driven table.\n//\n// A blotter used to declare its columns three times: the <thead> cells in the\n// server-rendered markup, the sort accessors in the widget constructor, and the\n// export header/row arrays in its export routine. Nothing kept the three in\n// step, and the body was built by concatenating innerHTML, which forces every\n// interactive cell into an inline `onclick=\"someGlobal.doThing(id)\"` attribute\n// because a string cannot carry a listener.\n//\n// Here a column is declared once \u2014 key, header, how to read the value, how to\n// render it, how to class it, whether it exports \u2014 and the grid owns the header,\n// the body, the sort state and the sheet. Two things a naive column pipeline\n// cannot express are first-class:\n//\n// * `render()` may return a Node. That is how a cell holds a real control with\n// its own addEventListener instead of an inline handler reaching a global.\n// * Pinned rows sit outside the sort and outside the export \u2014 a positions\n// balance summary stays on top no matter which column the user sorts by.\n//\n// The grid renders elements, never HTML strings, so nothing here can be an\n// injection site. Colour stays where it was: the caller returns class names\n// (`cellClass` / `rowClass`) and the host stylesheet decides what they look\n// like.\n//\n// Three class names come out of the package itself, and an adopting site has to\n// style them: `grid-empty` on the cell that spans the table when there is\n// nothing to show, `visually-hidden` on a header whose caption is for screen\n// readers only, and `sort-asc` / `sort-desc` on the sorted header (emitted by\n// TableSort). The first is ours; the other two are spelled the way Bootstrap\n// spells them, which is a coupling worth knowing about before adopting.\nimport { TableSort, type SortSpec } from './table-sort.js';\nimport { GridContextMenu, type GridMenuClasses, type GridMenuItem } from './context-menu.js';\nimport { GridFilterDialog, type GridFilterDialogClasses, type GridFilterDialogLabels } from './filter-dialog.js';\nimport { TableExport } from './table-export.js';\n\n/// A row separator for the clipboard. Named because a bare escape inside a string\n/// is easy to mangle when this file is edited by a tool rather than by hand.\nconst NEWLINE = String.fromCharCode(10);\nconst TAB = String.fromCharCode(9);\n\n/** Where a pinned row sits relative to the sorted body rows. */\nexport const GridPinnedPlacements = {\n Top: 'top',\n Bottom: 'bottom',\n} as const;\n\nexport type GridPinnedPlacement = typeof GridPinnedPlacements[keyof typeof GridPinnedPlacements];\n\nexport interface GridColumn<TRow> {\n /**\n * Stable identity of the column: stamped as `data-sort` on the header cell\n * (which is what TableSort keys off) and as `data-col` on every body cell,\n * and the key `cellElement()` takes.\n */\n key: string;\n\n /**\n * Caption, already localized. The grid does no translation \u2014 a host with a\n * translator resolves the string before declaring the column.\n */\n header: string;\n\n /** Class list for the header cell (alignment, width hints). */\n headerClass?: string;\n\n /**\n * Render the caption into a `.visually-hidden` span instead of visible text:\n * the column is announced to a screen reader but shows no header, which is\n * what an actions column wants.\n */\n headerHidden?: boolean;\n\n /**\n * This row's value for the column. Drives the sort, and is both the default\n * cell content and the default exported value. A column that declares no\n * value has nothing to sort by, so its header is not clickable.\n */\n value?(row: TRow): unknown;\n\n /**\n * Display content. A string becomes the cell's text; a Node is appended as\n * it is, so the cell can hold a button, an icon or any element carrying its\n * own listeners.\n */\n render?(row: TRow): string | Node;\n\n /**\n * Class list for this row's cell \u2014 the colouring third (`side-buy`,\n * `pnl-negative`, `cell-editable`, \u2026). The host stylesheet owns the looks.\n */\n cellClass?(row: TRow): string;\n\n /**\n * Wire behaviour onto the whole cell once its content is in place. For an\n * interaction that belongs to the cell rather than to a control inside it \u2014\n * double-clicking a price cell to edit it. A control gets its own listener\n * in `render()` instead.\n */\n bindCell?(td: HTMLTableCellElement, row: TRow): void;\n\n /**\n * The value as a person reads it, when that is not the value itself: `Buy` for\n * the 0 an order's side is held as, a status name for its code.\n *\n * The grid shows this wherever it has to name a value outside a cell -- a group\n * header, the list a set filter is picked from, the menu line that filters by\n * the value under the pointer -- while `value` goes on being what it sorts,\n * groups and compares by. Without it those places read `0 (14)`, and a filter\n * picked from them would be comparing a label against a number.\n *\n * Unlike `render`, this returns text and nothing but text, so the grid can put\n * it in places that hold no markup.\n */\n text?(row: TRow): string;\n\n /**\n * Draw a filter for this column, of this kind. Omit it and the column is not\n * filterable -- a filter row appears only once some column asks for one.\n */\n filter?: GridFilterKind;\n\n /**\n * Whether the column appears in the exported sheet. An exportable column\n * must be able to produce a value (`value` or `exportValue`).\n */\n exportable: boolean;\n\n /**\n * Exported value, when it differs from `value`: the localized text the user\n * reads rather than the raw enum behind it, or the raw number rather than\n * the formatted string on screen.\n */\n exportValue?(row: TRow): unknown;\n}\n\nexport interface GridPinnedCell {\n content: string | Node;\n className: string;\n}\n\n/**\n * A row the grid renders but does not own the data of: it is not sorted, not\n * exported, and its cells are supplied ready-made because it does not share the\n * shape the columns read (a balance summary under position columns).\n */\nexport interface GridPinnedRow {\n key: string;\n className: string;\n place: GridPinnedPlacement;\n /**\n * One cell per column, in column order. A shorter list leaves the remaining\n * columns empty; a longer one is a declaration mistake and throws.\n */\n cells: GridPinnedCell[];\n}\n\nexport interface GridExportData {\n headers: string[];\n rows: unknown[][];\n}\n\n/**\n * Which control the grid draws for a column, and therefore how its filter reads:\n * a substring box, a min/max pair, or a list of the values present.\n */\nexport type GridFilterKind = 'text' | 'number' | 'set';\n\n/** Where a right-click landed, handed to whoever builds the menu for it. */\nexport interface GridMenuContext<TRow> {\n /** The column under the pointer, or null when the click missed the cells. */\n column: GridColumn<TRow> | null;\n /** The row under the pointer, or null on a header, a group row or empty space. */\n row: TRow | null;\n /** The value of that row in that column, already rendered as text. */\n text: string;\n /** Where the click landed, so anything the menu opens can appear there. */\n x: number;\n y: number;\n}\n\n/** How the grid's own menu is set up, and how a host takes it over. */\nexport interface GridMenuOptions<TRow> {\n classes?: GridMenuClasses;\n /**\n * The final say on what the menu contains. Gets the grid's own items and may\n * return them, add to them, or ignore them entirely; returning an empty array\n * suppresses the menu for that click.\n */\n items?(context: GridMenuContext<TRow>, defaults: GridMenuItem[]): GridMenuItem[];\n /** Wording, for a host that is not in English. */\n labels?: Partial<GridMenuLabels>;\n /** Class names and wording for the filter rule dialog the menu opens. */\n filterDialog?: { classes?: GridFilterDialogClasses; labels?: Partial<GridFilterDialogLabels> };\n}\n\n/** Every phrase the built-in menu can show. */\nexport interface GridMenuLabels {\n sortAsc: string;\n sortDesc: string;\n sortClear: string;\n hideColumn: string;\n showAllColumns: string;\n groupBy: string;\n ungroup: string;\n /**\n * Names the value under the pointer. A function rather than a string because the\n * line is two pieces joined, and what joins them is a language's business: a\n * Chinese UI wants a full-width colon, and some languages want the value first.\n */\n filterByValue(text: string): string;\n filterRule: string;\n showFilters: string;\n hideFilters: string;\n showHeader: string;\n hideHeader: string;\n clearFilters: string;\n copyCell: string;\n copyRow: string;\n /** Names how many rows go. A function for the same reason as `filterByValue`. */\n copyRows(count: number): string;\n exportXlsx: string;\n}\n\nconst MENU_LABELS: GridMenuLabels = {\n sortAsc: 'Sort ascending',\n sortDesc: 'Sort descending',\n sortClear: 'Clear sort',\n hideColumn: 'Hide column',\n showAllColumns: 'Show all columns',\n groupBy: 'Group by this column',\n ungroup: 'Ungroup',\n filterByValue: text => `Filter by this value: ${text}`,\n filterRule: 'Filter\u2026',\n showFilters: 'Show filter row',\n hideFilters: 'Hide filter row',\n showHeader: 'Show header row',\n hideHeader: 'Hide header row',\n clearFilters: 'Clear filters',\n copyCell: 'Copy cell',\n copyRow: 'Copy row',\n copyRows: count => `Copy selected rows (${count})`,\n exportXlsx: 'Export to .xlsx',\n};\n\n/** How many rows a user may have selected at once. */\nexport type GridSelectionMode = 'none' | 'single' | 'multi';\n\n/** One value a set filter can be picked by: what it compares, and what it reads as. */\nexport interface GridValueOption {\n value: string;\n label: string;\n}\n\n/// Rows sharing a value, under the label that value reads as.\ninterface GridGroup<TRow> {\n label: string;\n rows: TRow[];\n}\n\n/**\n * One column's filter. Plain data rather than a predicate, and deliberately so:\n * a closure cannot be written to a store, so a grid filtered by one could never\n * come back filtered the same way. Every field is optional; an object with none\n * of them set means no filter at all.\n */\nexport interface GridFilter {\n /**\n * The rule. Absent means the shape decides: `text` reads as \"contains\", a\n * min/max pair as \"between\" -- which is what the inline row writes, and what a\n * filter stored before operators existed still means.\n */\n op?: GridFilterOp;\n /** The value the rule compares against, for every op but `empty` / `notEmpty`. */\n text?: string;\n /** Inclusive bounds for `between`; `min` alone also carries the operand of ge/gt/eq/ne on numbers. */\n min?: number;\n max?: number;\n /**\n * The values the rule picks from, as the column's `value` produces them -- the\n * raw side, not the \"Buy\" a person picked. `anyOf` keeps them, `noneOf` drops\n * them; with no op at all the list reads as `anyOf`, which is what the single\n * select in the filter row writes.\n */\n values?: string[];\n}\n\n/**\n * What a filter asks of a value. Text and numbers share the comparison ops -- a\n * user filtering an id column expects `>` to mean what it says, and a symbol\n * column compares as text.\n */\nexport type GridFilterOp =\n 'contains' | 'notContains' | 'startsWith' | 'endsWith'\n | 'eq' | 'ne' | 'gt' | 'ge' | 'lt' | 'le' | 'between'\n | 'anyOf' | 'noneOf'\n | 'empty' | 'notEmpty';\n\n/**\n * What the user arranged, and nothing the data decided. This is the whole of what\n * a host has to store to bring a table back the way somebody left it: which\n * columns, in what order, sorted by what.\n *\n * Every field is optional and every unknown column key is ignored on the way in,\n * because a stored layout outlives the table it was stored from -- a column gets\n * removed in a deploy and the saved view must still restore the rest rather than\n * fault.\n */\nexport interface GridState {\n /** Column keys in display order. Keys the caller leaves out keep their relative order, after these. */\n order?: string[];\n /** Column keys the user hid. They keep their place in `order` so unhiding restores the slot. */\n hidden?: string[];\n /** Whether the filter row is on screen. Absent means the table's own default. */\n filtersVisible?: boolean;\n /** Whether the caption row is on screen. Absent means the table's own default. */\n headerVisible?: boolean;\n sort?: SortSpec | null;\n /** By column key. Columns that are not filtered are absent rather than empty. */\n filters?: Record<string, GridFilter>;\n /** Column key the rows are grouped under, or null for a flat table. */\n group?: string | null;\n /** Group values the user collapsed. */\n collapsed?: string[];\n}\n\nexport interface GridOptions<TRow> {\n /** The `<thead>` the grid renders the header row into. Emptied on construction. */\n head: HTMLElement;\n /** The `<tbody>` the grid renders rows into. Emptied on every render. */\n body: HTMLElement;\n columns: GridColumn<TRow>[];\n /**\n * The order in effect while no header is selected. Null only for a table with\n * genuinely no meaningful resting order \u2014 see TableSort.\n */\n defaultSort: SortSpec | null;\n /**\n * Stable identity of a row, used to find its element again after a render\n * (patch a single cell, start an inline edit). Must be unique per row; when\n * two rows share a key the later one wins the lookup.\n */\n rowKey(row: TRow): string;\n /** Localized text for the row shown when there is nothing to display. */\n emptyText: string;\n rowClass?(row: TRow): string;\n /** Wire behaviour onto the whole row (selecting an instrument by clicking it). */\n bindRow?(tr: HTMLTableRowElement, row: TRow): void;\n /** Rows outside the sort, re-read on every render so they can track live data. */\n pinnedRows?(): GridPinnedRow[];\n /**\n * Cap on how many sorted rows are painted. Only rendering is capped \u2014 the\n * export still covers every row the grid holds, which is what a watchlist\n * wants: a screen-sized table over a full sheet.\n */\n renderLimit?: number;\n /**\n * Called once the body has been repainted. The grid owns the header\n * listener, so a sort click re-renders without the caller being involved \u2014\n * this is the only hook a caller has for work that depends on *which rows\n * are now on screen* (a watchlist re-subscribing to the visible symbols).\n *\n * Not called for the paint the constructor does: the grid holds no rows\n * yet, so there is nothing on screen to react to, and the caller does not\n * hold its own reference to the grid until the constructor returns.\n */\n afterRender?(): void;\n /**\n * Let the user drag a header to move its column. Off by default: a grid inside\n * a form or a report has nothing to gain from it, and a draggable header is a\n * mouse target that behaves differently from every other one on the page.\n */\n reorderable?: boolean;\n /**\n * The language the table is written in, as a BCP 47 tag. It decides the order of\n * everything compared as text -- the sort, the groups, a set filter's option list\n * -- so a page rendered in German orders the same way for every visitor instead of\n * following whatever locale their browser happens to be set to.\n *\n * Left out, the collator takes the document's own language.\n */\n locale?: string;\n\n /** A collator of your own, when the default (numeric, case-folding) is not it. */\n collator?: Intl.Collator;\n\n /**\n * The line a group header shows. The default reads `\u25BE Buy (14)`; override it for a\n * language that writes counts differently, or return a Node to put a real chevron\n * element in place of the triangle.\n */\n groupHeader?(label: string, count: number, collapsed: boolean): string | Node;\n\n /**\n * Show the row of filter boxes under the header. Off by default: the rule dialog\n * says everything the row can and more -- an operator, not just a substring -- and\n * a row of boxes costs a line of vertical space on every table that has one. Turn\n * it on for a table where filtering is the main activity.\n */\n filtersVisible?: boolean;\n /**\n * Show the caption row. On by default; a host turns it off for a table that\n * reads as a stream \u2014 a tape \u2014 and the menu's toggle brings it back. The\n * filter row is its own decision (`filtersVisible`) and stays either way.\n */\n headerVisible?: boolean;\n /**\n * Whether clicking a row selects it, and whether more than one can be selected.\n * Defaults to 'none': a table that is read, not acted on, should not respond to\n * a click with a highlight nobody asked for.\n */\n selection?: GridSelectionMode;\n /**\n * Class put on a selected row. The grid names elements but decides nothing about\n * how they look, so the host supplies the name its stylesheet defines.\n */\n selectedClass?: string;\n onSelectionChange?(keys: string[]): void;\n /**\n * Class put on a row whose key was not on screen the render before \u2014 how a live\n * tape flashes its newest prints without the host patching cells after every\n * render. The first paint never flashes: a seeded table is history, not news.\n * The class lives until the next repaint, so the host's CSS animation runs once\n * per arrival. The name is the host's, like every other look here.\n */\n flashNewClass?: string;\n /**\n * Offer a context menu on right-click. `true` takes the grid's own; an object\n * keeps it and lets the host restyle, reword or rewrite the items.\n */\n contextMenu?: boolean | GridMenuOptions<TRow>;\n /**\n * Called whenever the user changes the arrangement -- moved a column, hid one,\n * sorted. This is where a host persists it; `setState` deliberately does not\n * fire it, or restoring a layout would write it straight back on every load.\n */\n onStateChange?(state: GridState): void;\n}\n\nexport class DataGrid<TRow> {\n readonly options: GridOptions<TRow>;\n readonly sort: TableSort<TRow>;\n private _rows: TRow[];\n private _rowEls: Map<string, HTMLTableRowElement>;\n private _cellEls: Map<string, Map<string, HTMLTableCellElement>>;\n /// Display order of every declared column, hidden ones included -- hiding must\n /// not lose the slot a column goes back to.\n private _order: string[];\n private _hidden: Set<string>;\n /// The column a drag started on. Held here rather than in dataTransfer because\n /// the payload is ours, not the page's: dropping a header onto another window\n /// should do nothing, and reading it back out of the event would invite it.\n private _dragKey: string | null;\n private _filters: Record<string, GridFilter>;\n private _group: string | null;\n private _filtersVisible: boolean;\n private _headerVisible: boolean;\n /// By row key, never by index or element: a repaint rebuilds every `<tr>`, and a\n /// filter takes rows off screen and brings them back.\n private _selected: Set<string>;\n /// Where a shift-click measures its range from.\n private _anchorKey: string | null;\n // The keys on screen at the previous paint, for `flashNewClass`. Null until\n // the first paint has happened \u2014 nothing was on screen yet, so nothing is\n // news \u2014 and left null entirely while the option is off.\n private _flashKnown: Set<string> | null;\n private _menu: GridContextMenu | null;\n private _dragging: boolean;\n /// One collator for the whole grid: the sort, the group order, the option list a\n /// set filter offers, and the ordering filter rules. They compare the same strings\n /// and had better agree about what comes first.\n private readonly _collator: Intl.Collator;\n /// What to undo when the grid is destroyed: the listeners it put on the document.\n private readonly _release: (() => void)[] = [];\n /// The grid a keyboard shortcut belongs to: the last one the pointer touched.\n /// Two grids on a page both holding a selection would otherwise both answer Ctrl+C.\n private static _active: DataGrid<unknown> | null = null;\n private _filterDialog: GridFilterDialog | null;\n private _collapsed: Set<string>;\n /// The rendered filter controls, by column key, so a stored filter can be put\n /// back into them without rebuilding the row under the user's cursor.\n private _filterEls: Map<string, HTMLElement[]>;\n /// Suppresses onStateChange while setState applies a stored arrangement.\n private _restoring: boolean;\n\n constructor(options: GridOptions<TRow>) {\n for (const col of options.columns) {\n if (col.exportable && !col.value && !col.exportValue)\n throw new Error(`DataGrid: column \"${col.key}\" is exportable but declares no value to export`);\n }\n\n this.options = options;\n this._rows = [];\n this._rowEls = new Map();\n this._cellEls = new Map();\n this._order = options.columns.map(col => col.key);\n this._hidden = new Set();\n this._dragKey = null;\n this._filters = {};\n this._filtersVisible = options.filtersVisible === true;\n this._headerVisible = options.headerVisible !== false;\n this._group = null;\n this._collapsed = new Set();\n this._selected = new Set();\n this._anchorKey = null;\n this._flashKnown = null;\n this._menu = null;\n this._filterDialog = null;\n this._dragging = false;\n this._collator = options.collator\n || new Intl.Collator(\n options.locale || (typeof document !== 'undefined' ? document.documentElement?.lang : '') || undefined,\n // numeric so a text column of ids reads 1, 2, 10 rather than 1, 10, 2,\n // and accent-sensitive so it folds case the way the text filters do.\n { numeric: true, sensitivity: 'accent' });\n this._filterEls = new Map();\n this._restoring = false;\n\n const accessors: Record<string, (row: TRow) => unknown> = {};\n for (const col of options.columns) {\n if (col.value) accessors[col.key] = col.value;\n }\n\n // The header has to exist before TableSort is constructed: it marks the\n // active column on the cells it finds under `head` right away, so that the\n // resting sort is visible on first paint and not only after a click.\n this._renderHead();\n this.sort = new TableSort<TRow>(options.head, accessors, () => this.render(), options.defaultSort, this._collator);\n this._paint();\n if (options.contextMenu) this._bindContextMenu();\n if ((options.selection || 'none') !== 'none') this._bindCopyShortcut();\n }\n\n /** Every declared column in display order, hidden ones included. */\n columnKeys(): string[] {\n return [...this._order];\n }\n\n /** The columns actually rendered, in the order they are rendered. */\n visibleColumns(): GridColumn<TRow>[] {\n const byKey = new Map(this.options.columns.map(col => [col.key, col]));\n return this._order.filter(key => !this._hidden.has(key)).map(key => byKey.get(key)!);\n }\n\n isColumnHidden(key: string): boolean {\n return this._hidden.has(key);\n }\n\n /**\n * Put the columns in this order. Keys the caller leaves out keep their relative\n * order behind the ones named -- a layout stored before a column existed must\n * not make that column disappear.\n */\n setColumnOrder(keys: string[]): void {\n const known = new Set(this.options.columns.map(col => col.key));\n for (const key of keys) {\n if (!known.has(key)) throw new Error(`DataGrid: no column \"${key}\" to order`);\n }\n this._applyOrder(keys);\n this._rerender();\n }\n\n hideColumn(key: string): void {\n if (this._hidden.has(key)) return;\n this._hidden.add(key);\n this._rerender();\n }\n\n showColumn(key: string): void {\n if (!this._hidden.delete(key)) return;\n this._rerender();\n }\n\n filtersVisible(): boolean {\n return this._filtersVisible;\n }\n\n /** Show or hide the filter row. The filters themselves are kept either way. */\n showFilters(visible: boolean): void {\n if (this._filtersVisible === visible) return;\n this._filtersVisible = visible;\n this._rerender();\n }\n\n headerVisible(): boolean {\n return this._headerVisible;\n }\n\n /** Show or hide the caption row. Sort, columns and filters are kept either way. */\n showHeader(visible: boolean): void {\n if (this._headerVisible === visible) return;\n this._headerVisible = visible;\n this._rerender();\n }\n\n /** The filters in effect, by column key. Columns with none are absent. */\n filters(): Record<string, GridFilter> {\n return { ...this._filters };\n }\n\n /**\n * Filter a column, or pass null to stop filtering it. A filter with nothing set\n * -- a text box the user emptied -- is the same as null, so clearing an input\n * brings the rows back instead of matching nothing.\n */\n setFilter(key: string, filter: GridFilter | null): void {\n const normalized = filter && DataGrid._normalizeFilter(filter);\n if (normalized) this._filters[key] = normalized;\n else delete this._filters[key];\n this._afterFilterChange();\n }\n\n clearFilters(): void {\n if (Object.keys(this._filters).length === 0) return;\n this._filters = {};\n this._afterFilterChange();\n }\n\n /** The held rows that pass every filter, in the order they were handed over. */\n filteredRows(): TRow[] {\n const entries = Object.entries(this._filters);\n if (entries.length === 0) return this._rows;\n\n const byKey = new Map(this.options.columns.map(col => [col.key, col]));\n return this._rows.filter(row => entries.every(([key, filter]) => {\n const col = byKey.get(key);\n // A filter naming a column that is gone matches everything rather than\n // nothing: a stale stored filter must not empty the table.\n if (!col || !col.value) return true;\n return this._passes(col.value(row), filter);\n }));\n }\n\n private _afterFilterChange(): void {\n this.render();\n this._refreshFilterRow();\n if (!this._restoring && this.options.onStateChange) this.options.onStateChange(this.getState());\n }\n\n private static _normalizeFilter(filter: GridFilter): GridFilter | null {\n const out: GridFilter = {};\n // `empty` and `notEmpty` ask about the value itself, so they are a filter even\n // with nothing typed in -- every other op needs an operand to mean anything.\n if (filter.op === 'empty' || filter.op === 'notEmpty') return { op: filter.op };\n if (filter.op) out.op = filter.op;\n const text = typeof filter.text === 'string' ? filter.text.trim() : '';\n if (text) out.text = text;\n if (typeof filter.min === 'number' && Number.isFinite(filter.min)) out.min = filter.min;\n if (typeof filter.max === 'number' && Number.isFinite(filter.max)) out.max = filter.max;\n if (filter.values && filter.values.length) out.values = [...filter.values];\n // An op on its own filters nothing; it needs something to compare against.\n if (out.op && out.text === undefined && out.min === undefined && out.max === undefined && !out.values) return null;\n return Object.keys(out).length ? out : null;\n }\n\n private _passes(value: unknown, filter: GridFilter): boolean {\n const op = DataGrid._effectiveOp(filter);\n if (!op) return true;\n\n const text = DataGrid._text(value);\n // A set filter is picked from the values present rather than compared against\n // an operand, so it answers on its own terms.\n if (op === 'anyOf' || op === 'noneOf') {\n const listed = (filter.values || []).includes(text);\n return op === 'anyOf' ? listed : !listed;\n }\n\n if (op === 'empty') return text === '';\n if (op === 'notEmpty') return text !== '';\n\n if (op === 'between') {\n const n = Number(text);\n if (!Number.isFinite(n)) return false;\n if (filter.min !== undefined && n < filter.min) return false;\n if (filter.max !== undefined && n > filter.max) return false;\n return true;\n }\n\n // Compared as numbers when both sides are numbers, as text otherwise: `>` on a\n // symbol column has to mean something, and on a quantity it has to mean the\n // arithmetic thing rather than '9' > '10'.\n const operandText = filter.text !== undefined ? filter.text : (filter.min !== undefined ? String(filter.min) : '');\n const left = Number(text);\n const right = Number(operandText);\n const numeric = text !== '' && operandText !== '' && Number.isFinite(left) && Number.isFinite(right);\n\n // Ordering compares through the collator, not with `>` on strings: raw\n // comparison is UTF-16 code-unit order, which puts every accented letter after\n // z and every Cyrillic capital before every small letter -- so a column shown\n // in one order would filter in another.\n const order = numeric ? Math.sign(left - right) : this._collator.compare(text, operandText);\n\n switch (op) {\n case 'contains': return text.toLowerCase().includes(operandText.toLowerCase());\n case 'notContains': return !text.toLowerCase().includes(operandText.toLowerCase());\n case 'startsWith': return text.toLowerCase().startsWith(operandText.toLowerCase());\n case 'endsWith': return text.toLowerCase().endsWith(operandText.toLowerCase());\n case 'eq': return numeric ? left === right : text.toLowerCase() === operandText.toLowerCase();\n case 'ne': return numeric ? left !== right : text.toLowerCase() !== operandText.toLowerCase();\n case 'gt': return order > 0;\n case 'ge': return order >= 0;\n case 'lt': return order < 0;\n case 'le': return order <= 0;\n default: return true;\n }\n }\n\n /// The rule actually in force. Absent `op` keeps the old meaning of the shape, so\n /// a filter written by the inline row -- or stored before operators existed --\n /// goes on meaning what it meant.\n private static _effectiveOp(filter: GridFilter): GridFilterOp | null {\n if (filter.op) return filter.op;\n // No op means the shape decides, which is what the controls in the filter row\n // write: a box of text reads as \"contains\", a pair of numbers as \"between\",\n // and a picked value as \"any of\".\n if (filter.values !== undefined) return 'anyOf';\n if (filter.min !== undefined || filter.max !== undefined) return 'between';\n if (filter.text !== undefined) return 'contains';\n return null;\n }\n\n /**\n * The items the grid offers for a right-click. Public so a host that replaces\n * the menu can still reach for the parts of it that it wants to keep.\n */\n menuItems(context: GridMenuContext<TRow>): GridMenuItem[] {\n const labels = { ...MENU_LABELS, ...(this._menuOptions().labels || {}) };\n const items: GridMenuItem[] = [];\n const col = context.column;\n\n if (col && col.value) {\n const sort = this.sort.current();\n items.push(\n { label: labels.sortAsc, checked: sort?.col === col.key && sort.dir === 'asc', run: () => this.sort.set(col.key, 'asc') },\n { label: labels.sortDesc, checked: sort?.col === col.key && sort.dir === 'desc', run: () => this.sort.set(col.key, 'desc') },\n { label: labels.sortClear, disabled: !sort, run: () => this.sort.set(null, null) },\n {},\n this._group === col.key\n ? { label: labels.ungroup, run: () => this.groupBy(null) }\n : { label: labels.groupBy, run: () => this.groupBy(col.key) },\n );\n\n if (col.filter) {\n items.push({\n label: labels.filterRule,\n checked: this._filters[col.key] !== undefined,\n run: () => this.openFilterDialog(col.key, context.x, context.y),\n });\n }\n\n if (context.row !== null) {\n // Named by what the cell reads as, filtering by what the column holds.\n // Taking the cell's text for both matched a label against a raw value,\n // so on a column showing \"Buy\" for 0 this line filtered everything out.\n const value = DataGrid._text(col.value ? col.value(context.row) : null);\n items.push({\n label: labels.filterByValue(this._displayText(col, context.row)),\n run: () => this.setFilter(col.key, { op: 'anyOf', values: [value] }),\n });\n }\n }\n\n if (this.options.columns.some(c => c.filter)) {\n items.push(\n {\n label: this._filtersVisible ? labels.hideFilters : labels.showFilters,\n checked: this._filtersVisible,\n run: () => this.showFilters(!this._filtersVisible),\n },\n { label: labels.clearFilters, disabled: Object.keys(this._filters).length === 0, run: () => this.clearFilters() },\n {},\n );\n }\n\n if (col) {\n items.push({\n label: labels.hideColumn,\n // The last visible column cannot go: a table with no columns is a\n // blank rectangle with no way back to itself.\n disabled: this.visibleColumns().length <= 1,\n run: () => this.hideColumn(col.key),\n });\n }\n items.push({\n label: labels.showAllColumns,\n disabled: this._hidden.size === 0,\n run: () => { for (const key of [...this._hidden]) this.showColumn(key); },\n });\n // From the body as much as from the header \u2014 once the captions are\n // hidden, the body is the only place left to bring them back from.\n items.push({\n label: this._headerVisible ? labels.hideHeader : labels.showHeader,\n checked: this._headerVisible,\n run: () => this.showHeader(!this._headerVisible),\n });\n\n if (context.row !== null) {\n items.push(\n {},\n { label: labels.copyCell, run: () => DataGrid._copy(context.text) },\n {\n // Names how many rows go, because \"Copy row\" over a selection of\n // twelve would be a lie about what the click is about to do.\n label: this._selected.size > 1 ? labels.copyRows(this._selected.size) : labels.copyRow,\n run: () => this.copyRows(context.row),\n },\n );\n }\n\n items.push({}, { label: labels.exportXlsx, run: () => this.download('export', 'Export') });\n return items;\n }\n\n private _menuOptions(): GridMenuOptions<TRow> {\n const option = this.options.contextMenu;\n return typeof option === 'object' && option !== null ? option : {};\n }\n\n private _bindContextMenu(): void {\n this._menu = new GridContextMenu(this._menuOptions().classes);\n\n const onContext = (event: Event) => {\n DataGrid._active = this as unknown as DataGrid<unknown>;\n const target = (event as unknown as { target?: Element }).target;\n const cell = target && typeof target.closest === 'function'\n ? target.closest('[data-col]') as HTMLElement | null\n : null;\n const rowEl = target && typeof target.closest === 'function'\n ? target.closest('[data-row-key]') as HTMLElement | null\n : null;\n\n const column = cell ? this.options.columns.find(c => c.key === cell.dataset.col) || null : null;\n const rowKey = rowEl?.dataset.rowKey;\n const row = rowKey === undefined ? null\n : this.displayRows().find(r => this.options.rowKey(r) === rowKey) || null;\n const text = column && row && column.value ? DataGrid._text(column.value(row)) : '';\n\n const at = event as unknown as { clientX?: number; clientY?: number };\n const context: GridMenuContext<TRow> = { column, row, text, x: at.clientX || 0, y: at.clientY || 0 };\n const build = this._menuOptions().items;\n const items = build ? build(context, this.menuItems(context)) : this.menuItems(context);\n if (items.length === 0) return;\n\n event.preventDefault();\n this._menu!.open(items, context.x, context.y);\n };\n\n this.options.head.addEventListener('contextmenu', onContext);\n this.options.body.addEventListener('contextmenu', onContext);\n }\n\n /// The same columns and the same accessors the sheet uses. A row on the clipboard\n /// and a row in the .xlsx are the same data going to the same place, so a column\n /// that renders a button has nothing to contribute to either, and one that exports\n /// \"Buy\" must not copy the 0 behind it.\n private _rowText(row: TRow): string {\n return this.visibleColumns()\n .filter(col => col.exportable)\n .map(col => DataGrid._text((col.exportValue || col.value)!(row)))\n .join(TAB);\n }\n\n /// Put text on the clipboard, returning whether anything was written.\n /// navigator.clipboard exists only in a secure context, so on a page served over\n /// plain http to anything but localhost it is simply undefined -- which is how\n /// Copy row came to do nothing at all on a LAN address while working locally. The\n /// older execCommand path still works there, so it is the fallback rather than a\n /// silent no-op.\n private static _copy(text: string): boolean {\n const clipboard = typeof navigator !== 'undefined' ? navigator.clipboard : undefined;\n if (clipboard && clipboard.writeText) {\n clipboard.writeText(text).catch(() => DataGrid._copyFallback(text));\n return true;\n }\n return DataGrid._copyFallback(text);\n }\n\n private static _copyFallback(text: string): boolean {\n if (typeof document === 'undefined' || !document.body) return false;\n\n const area = document.createElement('textarea') as HTMLTextAreaElement;\n area.value = text;\n // Off-screen rather than hidden: execCommand copies from a field that is in\n // the layout, and display:none takes it out of the layout.\n area.style.position = 'fixed';\n area.style.left = '-9999px';\n document.body.appendChild(area);\n try {\n area.select();\n return typeof document.execCommand === 'function' && document.execCommand('copy');\n } catch {\n return false;\n } finally {\n area.parentNode?.removeChild(area);\n }\n }\n\n /**\n * What Copy row and Ctrl+C put on the clipboard: the selected rows when there is\n * a selection, otherwise the one row asked for. Tab-separated, one row per line,\n * which is what a spreadsheet reads back as cells.\n */\n copyText(row: TRow | null): string {\n const selected = this.selectedRows();\n const rows = selected.length > 0 ? selected : (row === null ? [] : [row]);\n return rows.map(r => this._rowText(r)).join(NEWLINE);\n }\n\n /** Copy the current selection, or the row given. True when anything was written. */\n copyRows(row: TRow | null): boolean {\n const text = this.copyText(row);\n return text === '' ? false : DataGrid._copy(text);\n }\n\n /** Selected row keys, in the order they appear on screen. */\n selectedKeys(): string[] {\n const onScreen = this.displayRows().map(row => this.options.rowKey(row));\n const known = new Set(onScreen);\n // Keys of rows a filter is currently hiding keep their selection but have no\n // place in the on-screen order, so they follow at the end rather than vanish.\n return [...onScreen.filter(key => this._selected.has(key)),\n ...[...this._selected].filter(key => !known.has(key))];\n }\n\n /** The selected rows that are actually on screen. */\n selectedRows(): TRow[] {\n return this.displayRows().filter(row => this._selected.has(this.options.rowKey(row)));\n }\n\n /**\n * Forget the flash baseline: the next paint reads as a fresh seed and\n * flashes nothing. For a host that replaces its rows wholesale \u2014 a tape\n * moving to another instrument is a new history, not fifty arrivals at\n * once.\n */\n flashReset(): void {\n this._flashKnown = null;\n }\n\n setSelection(keys: string[]): void {\n this._replaceSelection(new Set(keys));\n }\n\n clearSelection(): void {\n this._replaceSelection(new Set());\n }\n\n private _replaceSelection(next: Set<string>): void {\n const before = this.selectedKeys();\n this._selected = next;\n const after = this.selectedKeys();\n if (before.length === after.length && before.every((key, i) => key === after[i])) return;\n this.render();\n if (this.options.onSelectionChange) this.options.onSelectionChange(after);\n }\n\n private _beginDragSelect(): void {\n this._dragging = true;\n const end = () => {\n this._dragging = false;\n document.removeEventListener('mouseup', end, true);\n };\n document.addEventListener('mouseup', end, true);\n }\n\n private _extendTo(key: string): void {\n if (this._anchorKey === null) return;\n const order = this.displayRows().map(row => this.options.rowKey(row));\n const from = order.indexOf(this._anchorKey);\n const to = order.indexOf(key);\n if (from === -1 || to === -1) return;\n this._replaceSelection(new Set(order.slice(Math.min(from, to), Math.max(from, to) + 1)));\n }\n\n /// Ctrl+C over the table copies the selected rows. Bound on the document because\n /// a table has no focus of its own, and answered only by the grid the pointer last\n /// touched -- otherwise every grid on the page would copy at once.\n private _bindCopyShortcut(): void {\n const onKeyDown = (event: Event) => {\n const e = event as unknown as { key?: string; code?: string; ctrlKey?: boolean; metaKey?: boolean; target?: { tagName?: string } };\n if (!(e.ctrlKey || e.metaKey)) return;\n // `code` is the physical key, so this holds on a layout where that key\n // types something other than \"c\" -- on a Russian one it reports \"\u0441\".\n const isC = e.code ? e.code === 'KeyC' : (e.key || '').toLowerCase() === 'c';\n if (!isC) return;\n if (DataGrid._active !== (this as unknown as DataGrid<unknown>)) return;\n if (this._selected.size === 0) return;\n\n // Never steal a copy from a field the user is typing in.\n const tag = e.target && e.target.tagName;\n if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT') return;\n\n if (this.copyRows(null)) event.preventDefault();\n };\n document.addEventListener('keydown', onKeyDown);\n this._release.push(() => document.removeEventListener('keydown', onKeyDown));\n }\n\n /**\n * Let the grid go. Everything it put outside its own two elements comes off: the\n * Ctrl+C binding on the document, and any menu or filter popup still open.\n *\n * A host needs this whenever it replaces a grid rather than updating one -- the\n * columns are declared once, so re-declaring them (translated headers, a different\n * set of columns) means a new grid over the same table, and the old one would go\n * on answering Ctrl+C from beyond the grave.\n */\n destroy(): void {\n for (const release of this._release.splice(0)) release();\n this._menu?.close();\n this._filterDialog?.close();\n if (DataGrid._active === (this as unknown as DataGrid<unknown>)) DataGrid._active = null;\n }\n\n private _clickRow(key: string, event: Event): void {\n const mode = this.options.selection || 'none';\n if (mode === 'none') return;\n\n const modifiers = event as unknown as { ctrlKey?: boolean; metaKey?: boolean; shiftKey?: boolean };\n const next = new Set<string>();\n\n if (mode === 'single' || (!modifiers.ctrlKey && !modifiers.metaKey && !modifiers.shiftKey)) {\n next.add(key);\n this._anchorKey = key;\n } else if (modifiers.shiftKey && this._anchorKey !== null) {\n // The range is what the user sees between the two rows, so it is measured\n // against the order on screen -- sorted, filtered, grouped -- not the order\n // the rows were handed over in.\n const order = this.displayRows().map(row => this.options.rowKey(row));\n const from = order.indexOf(this._anchorKey);\n const to = order.indexOf(key);\n if (from === -1 || to === -1) next.add(key);\n else for (const k of order.slice(Math.min(from, to), Math.max(from, to) + 1)) next.add(k);\n } else {\n for (const k of this._selected) next.add(k);\n if (!next.delete(key)) next.add(key);\n this._anchorKey = key;\n }\n\n this._replaceSelection(next);\n }\n\n /**\n * Open the rule dialog for a column -- an operator and its operand, which is what\n * the inline row cannot express without growing a second control per column.\n */\n openFilterDialog(key: string, x: number, y: number): void {\n const col = this.options.columns.find(c => c.key === key);\n if (!col || !col.filter) return;\n\n const options = this._menuOptions().filterDialog || {};\n if (!this._filterDialog) this._filterDialog = new GridFilterDialog(options.classes, options.labels);\n\n this._filterDialog.open(\n {\n header: col.header,\n kind: col.filter,\n current: this._filters[key] || null,\n // A set column is picked from, not typed into: the dialog needs the\n // values the rows actually hold, each under the label it reads as.\n values: col.filter === 'set' ? this._distinctValues(col) : [],\n x,\n y,\n },\n filter => this.setFilter(key, filter),\n );\n }\n\n /** The column the rows are grouped under, or null. */\n groupedBy(): string | null {\n return this._group;\n }\n\n /** Group by a column, or pass null to go flat. */\n groupBy(key: string | null): void {\n if (key !== null) {\n const col = this.options.columns.find(c => c.key === key);\n if (!col || !col.value) throw new Error(`DataGrid: no column \"${key}\" with a value to group by`);\n }\n this._group = key;\n // Collapse is remembered per group value, and the values belong to the column\n // that produced them -- keeping them across a regroup would collapse whatever\n // happened to share a name.\n this._collapsed.clear();\n this._afterViewChange();\n }\n\n collapsedGroups(): string[] {\n return [...this._collapsed];\n }\n\n toggleGroup(value: string): void {\n if (!this._collapsed.delete(value)) this._collapsed.add(value);\n this._afterViewChange();\n }\n\n private _afterViewChange(): void {\n this.render();\n if (!this._restoring && this.options.onStateChange) this.options.onStateChange(this.getState());\n }\n\n /** The arrangement, as a host would store it. */\n getState(): GridState {\n return {\n order: [...this._order],\n hidden: this._order.filter(key => this._hidden.has(key)),\n sort: this.sort.current(),\n filters: this.filters(),\n filtersVisible: this._filtersVisible,\n headerVisible: this._headerVisible,\n group: this._group,\n collapsed: this.collapsedGroups(),\n };\n }\n\n /**\n * Restore an arrangement. Unknown column keys are dropped rather than rejected:\n * a view stored before a deploy has to survive one.\n */\n setState(state: GridState): void {\n const known = new Set(this.options.columns.map(col => col.key));\n this._restoring = true;\n try {\n if (state.order) this._applyOrder(state.order.filter(key => known.has(key)));\n if (state.hidden) this._hidden = new Set(state.hidden.filter(key => known.has(key)));\n if (state.sort !== undefined) {\n if (state.sort) this.sort.set(state.sort.col, state.sort.dir);\n else this.sort.set(null, null);\n }\n if (state.filtersVisible !== undefined) this._filtersVisible = state.filtersVisible;\n if (state.headerVisible !== undefined) this._headerVisible = state.headerVisible;\n if (state.group !== undefined) {\n const col = state.group && this.options.columns.find(c => c.key === state.group);\n this._group = col && col.value ? state.group! : null;\n this._collapsed.clear();\n }\n if (state.collapsed) this._collapsed = new Set(state.collapsed);\n if (state.filters) {\n this._filters = {};\n for (const [key, filter] of Object.entries(state.filters)) {\n if (!known.has(key)) continue;\n const normalized = DataGrid._normalizeFilter(filter);\n if (normalized) this._filters[key] = normalized;\n }\n }\n this._rerender();\n } finally {\n this._restoring = false;\n }\n }\n\n private _applyOrder(keys: string[]): void {\n const named = keys.filter((key, i) => keys.indexOf(key) === i);\n this._order = [...named, ...this._order.filter(key => !named.includes(key))];\n }\n\n /// One place that repaints and tells the host, so no caller can do one without\n /// the other.\n private _rerender(): void {\n this._renderHead();\n this.sort.refreshHeader();\n this.render();\n if (!this._restoring && this.options.onStateChange) this.options.onStateChange(this.getState());\n }\n\n /**\n * The rows the grid holds, in the order they were handed over. The grid keeps\n * the caller's array by reference: a caller that merges a live delta into it\n * and calls `render()` repaints from the current contents.\n */\n get rows(): TRow[] {\n return this._rows;\n }\n\n /**\n * Rows in the order currently in effect (the picked column, else the table\n * default). A sorted copy \u2014 the held array is never reordered.\n */\n sortedRows(): TRow[] {\n return this.sort.apply(this.filteredRows());\n }\n\n /**\n * Every row that is on screen, in the order it appears there -- filtered, sorted,\n * and gathered into groups when grouping is on. Collapsed groups are included:\n * they are hidden, not excluded, and an export of a collapsed table that silently\n * dropped rows would be worse than one that shows them.\n */\n displayRows(): TRow[] {\n const sorted = this.sortedRows();\n if (this._group === null) return sorted;\n return [...this._groups(sorted).values()].flatMap(group => group.rows);\n }\n\n setRows(rows: TRow[]): void {\n this._rows = rows || [];\n this.render();\n // A set filter offers the values the rows actually hold, so new rows can bring\n // new options. The header is not rebuilt for it -- that would take the caret\n // out of a filter box mid-typing -- only the option list, and only if it moved.\n this._refreshFilterRow();\n }\n\n render(): void {\n this._paint();\n if (this.options.afterRender) this.options.afterRender();\n }\n\n private _paint(): void {\n this._rowEls.clear();\n this._cellEls.clear();\n\n const pinned = this.options.pinnedRows ? this.options.pinnedRows() : [];\n const sorted = this.sortedRows();\n const limit = this.options.renderLimit != null ? this.options.renderLimit : Infinity;\n\n const children: Node[] = [];\n for (const row of pinned) {\n if (row.place === GridPinnedPlacements.Top) children.push(this._pinnedRow(row));\n }\n\n let painted = 0;\n if (this._group === null) {\n for (const row of sorted) {\n if (painted >= limit) break;\n children.push(this._bodyRow(row));\n painted++;\n }\n } else {\n // Groups run in value order while the rows inside each keep the sort the\n // user picked -- sorting by quantity inside \"AAPL\" should not shuffle\n // \"AAPL\" itself against \"BTC\".\n for (const [key, group] of this._groups(sorted)) {\n if (painted >= limit) break;\n children.push(this._groupRow(key, group.label, group.rows.length));\n if (this._collapsed.has(key)) continue;\n for (const row of group.rows) {\n // The limit counts rows of data: a group header is a label for the\n // rows under it, and capping on it would cut a group off at its own title.\n if (painted >= limit) break;\n children.push(this._bodyRow(row));\n painted++;\n }\n }\n }\n\n // A pinned row is content, so a table showing only a balance summary is not\n // empty \u2014 the \"nothing here\" row would read as a contradiction next to it.\n if (painted === 0 && pinned.length === 0 && this._collapsed.size === 0) children.push(this._emptyRow());\n\n for (const row of pinned) {\n if (row.place === GridPinnedPlacements.Bottom) children.push(this._pinnedRow(row));\n }\n\n this.options.body.replaceChildren(...children);\n\n // What this paint showed becomes the baseline the next one flashes\n // against. Display keys rather than painted ones: a row past\n // `renderLimit` is held, not news, and must not flash when a later\n // paint reaches it. Unarmed until something has actually been on\n // screen \u2014 the constructor paints an empty table before any data\n // arrives, and the seed that follows is history, not news.\n if (this.options.flashNewClass) {\n const keys = this.displayRows().map(row => this.options.rowKey(row));\n if (this._flashKnown !== null || keys.length > 0) this._flashKnown = new Set(keys);\n }\n }\n\n /**\n * The rendered row with this key, or null when it is not on screen (filtered\n * out, or past `renderLimit`).\n */\n rowElement(rowKey: string): HTMLTableRowElement | null {\n return this._rowEls.get(rowKey) || null;\n }\n\n /**\n * The rendered cell at (row, column) \u2014 how a caller patches one cell in place\n * instead of repainting the table, and how it finds the cell to edit.\n */\n cellElement(rowKey: string, columnKey: string): HTMLTableCellElement | null {\n return this._cellEls.get(rowKey)?.get(columnKey) || null;\n }\n\n /**\n * Header captions and cell values of the exportable columns, over every held\n * row in the order in effect. Pure \u2014 separate from `download` so the sheet\n * content can be asserted without a browser.\n */\n exportData(): GridExportData {\n const columns = this.visibleColumns().filter(col => col.exportable);\n return {\n headers: columns.map(col => col.header),\n // Grouped, the sheet follows the order on screen but without the header\n // rows: a spreadsheet has its own grouping, and a label row in the middle\n // of the data would break every formula pointed at the range.\n rows: this.displayRows().map(row => columns.map(col => {\n const accessor = col.exportValue || col.value;\n return accessor ? accessor(row) : null;\n })),\n };\n }\n\n /** Export to `<baseName>-<timestamp>.xlsx` in the order on screen. */\n download(baseName: string, sheetName: string): void {\n const data = this.exportData();\n TableExport.download(baseName, sheetName, data.headers, data.rows);\n }\n\n private _renderHead(): void {\n // No caption row \u2014 the filter row, when on, still stands on its own.\n if (!this._headerVisible) {\n const filterRow = this._filterRow();\n if (filterRow) this.options.head.replaceChildren(filterRow);\n else this.options.head.replaceChildren();\n return;\n }\n\n const tr = document.createElement('tr');\n for (const col of this.visibleColumns()) {\n const th = document.createElement('th');\n th.setAttribute('scope', 'col');\n // The same key the body cells carry. Without it a right-click on the header\n // resolves no column, so the menu loses everything about it -- and any host\n // stamping the keys itself loses them on the next repaint.\n th.dataset.col = col.key;\n if (col.headerClass) th.className = col.headerClass;\n // Sortable exactly when there is a value to sort by. TableSort binds to\n // the cells carrying `data-sort`, so a column without one is inert.\n if (col.value) th.dataset.sort = col.key;\n if (col.headerHidden) {\n const label = document.createElement('span');\n label.className = 'visually-hidden';\n label.textContent = col.header;\n th.appendChild(label);\n } else {\n th.textContent = col.header;\n }\n if (this.options.reorderable) this._bindHeaderDrag(th, col.key);\n tr.appendChild(th);\n }\n\n const filters = this._filterRow();\n if (filters) this.options.head.replaceChildren(tr, filters);\n else this.options.head.replaceChildren(tr);\n }\n\n /**\n * The row of filter controls, or null when no visible column declares one.\n *\n * Every visible column gets a cell even when it has no filter, or the row would\n * shear away from the captions above it. The cells carry no `data-sort`, so a\n * click inside one does not reach the sort handler bound on the `<thead>`.\n */\n private _filterRow(): HTMLTableRowElement | null {\n const columns = this.visibleColumns();\n if (!this._filtersVisible || !columns.some(col => col.filter)) return null;\n\n const tr = document.createElement('tr') as HTMLTableRowElement;\n tr.className = 'grid-filters';\n this._filterEls = new Map();\n\n for (const col of columns) {\n const cell = document.createElement('th') as HTMLTableCellElement;\n cell.dataset.filter = col.key;\n if (col.filter) {\n for (const el of this._filterControls(col)) cell.appendChild(el);\n }\n tr.appendChild(cell);\n }\n return tr;\n }\n\n private _filterControls(col: GridColumn<TRow>): HTMLElement[] {\n const current = this._filters[col.key] || {};\n\n if (col.filter === 'text') {\n const input = document.createElement('input') as HTMLInputElement;\n input.type = 'text';\n input.className = 'grid-filter grid-filter-text';\n input.value = current.text || '';\n input.addEventListener('input', () => this.setFilter(col.key, { text: input.value }));\n this._filterEls.set(col.key, [input]);\n return [input];\n }\n\n if (col.filter === 'number') {\n const read = (el: HTMLInputElement) => {\n const n = Number(el.value);\n return el.value.trim() === '' || !Number.isFinite(n) ? undefined : n;\n };\n const make = (className: string, value: number | undefined) => {\n const input = document.createElement('input') as HTMLInputElement;\n input.type = 'number';\n input.className = `grid-filter ${className}`;\n input.value = value === undefined ? '' : String(value);\n return input;\n };\n const min = make('grid-filter-min', current.min);\n const max = make('grid-filter-max', current.max);\n const push = () => this.setFilter(col.key, { min: read(min), max: read(max) });\n min.addEventListener('input', push);\n max.addEventListener('input', push);\n this._filterEls.set(col.key, [min, max]);\n return [min, max];\n }\n\n // A set filter offers the values actually in the held rows -- all of them,\n // not the ones surviving the current filters, or picking one would empty the\n // list it was picked from.\n const select = document.createElement('select') as HTMLSelectElement;\n select.className = 'grid-filter grid-filter-set';\n const blank = document.createElement('option') as HTMLOptionElement;\n blank.value = '';\n blank.textContent = '';\n select.appendChild(blank);\n for (const { value, label } of this._distinctValues(col)) {\n const option = document.createElement('option') as HTMLOptionElement;\n option.value = value;\n option.textContent = label;\n select.appendChild(option);\n }\n select.value = current.values && current.values.length ? current.values[0] : '';\n select.addEventListener('change', () =>\n this.setFilter(col.key, select.value ? { values: [select.value] } : null));\n this._filterEls.set(col.key, [select]);\n return [select];\n }\n\n /// Replaces the option list only when the values behind it changed, so a live\n /// table that repaints on every tick does not rebuild a select the user is using.\n private _syncOptions(select: HTMLSelectElement, values: GridValueOption[]): void {\n const current = Array.from(select.children).slice(1).map(el => (el as HTMLOptionElement).value);\n if (current.length === values.length && current.every((v, i) => v === values[i].value)) return;\n\n const blank = document.createElement('option') as HTMLOptionElement;\n blank.value = '';\n blank.textContent = '';\n const options: HTMLElement[] = [blank];\n for (const { value, label } of values) {\n const option = document.createElement('option') as HTMLOptionElement;\n option.value = value;\n option.textContent = label;\n options.push(option);\n }\n select.replaceChildren(...options);\n }\n\n /// The value as a person reads it. One place, because a group header, a filter's\n /// option list and the menu line that filters by a value all have to agree -- and\n /// all of them have to disagree with `value`, which stays what the grid compares.\n private _displayText(col: GridColumn<TRow>, row: TRow): string {\n if (col.text) return col.text(row);\n return DataGrid._text(col.value ? col.value(row) : null);\n }\n\n /// Every value the column holds, paired with the label it is picked by. Keyed by\n /// the raw value: two rows reading \"Buy\" are one option, and the option carries\n /// the 0 the filter will actually compare against.\n private _distinctValues(col: GridColumn<TRow>): GridValueOption[] {\n const seen = new Map<string, string>();\n for (const row of this._rows) {\n if (!col.value) continue;\n const value = DataGrid._text(col.value(row));\n if (!seen.has(value)) seen.set(value, this._displayText(col, row));\n }\n return [...seen.entries()]\n .map(([value, label]) => ({ value, label }))\n .sort((a, b) => this._collator.compare(a.label, b.label));\n }\n\n /**\n * Put the stored filters back into the controls without rebuilding the row.\n * Rebuilding it would take the focus and the caret out of the box the user is\n * still typing in.\n */\n private _refreshFilterRow(): void {\n for (const [key, els] of this._filterEls) {\n const filter = this._filters[key] || {};\n if (els.length === 2) {\n (els[0] as HTMLInputElement).value = filter.min === undefined ? '' : String(filter.min);\n (els[1] as HTMLInputElement).value = filter.max === undefined ? '' : String(filter.max);\n } else if (els[0].tagName === 'SELECT') {\n const select = els[0] as HTMLSelectElement;\n const col = this.options.columns.find(c => c.key === key);\n if (col) this._syncOptions(select, this._distinctValues(col));\n select.value = filter.values && filter.values.length ? filter.values[0] : '';\n } else {\n const input = els[0] as HTMLInputElement;\n const want = filter.text || '';\n if (input.value !== want) input.value = want;\n }\n }\n }\n\n private _bodyRow(row: TRow): HTMLTableRowElement {\n const tr = document.createElement('tr') as HTMLTableRowElement;\n const key = this.options.rowKey(row);\n const rowClass = this.options.rowClass ? this.options.rowClass(row) : '';\n if (rowClass) tr.className = rowClass;\n tr.dataset.rowKey = key;\n\n const cells = new Map<string, HTMLTableCellElement>();\n for (const col of this.visibleColumns()) {\n const td = document.createElement('td') as HTMLTableCellElement;\n td.dataset.col = col.key;\n const cellClass = col.cellClass ? col.cellClass(row) : '';\n if (cellClass) td.className = cellClass;\n const content = col.render ? col.render(row) : DataGrid._text(col.value ? col.value(row) : null);\n // A renderer is declared to return a string or a Node, but a value that\n // fell through a formatter's branches arrives as whatever it was - a raw\n // status number, say. Passing that to appendChild throws and takes the\n // whole table down over one unexpected row, so anything that is not a Node\n // is rendered as text. nodeType rather than instanceof Node: the tests run\n // against a fake DOM with no Node constructor in scope.\n if (content !== null && typeof content === 'object' && 'nodeType' in content) td.appendChild(content);\n else td.textContent = DataGrid._text(content);\n if (col.bindCell) col.bindCell(td, row);\n tr.appendChild(td);\n cells.set(col.key, td);\n }\n\n if (this._selected.has(key)) {\n const selectedClass = this.options.selectedClass || 'is-selected';\n tr.className = tr.className ? `${tr.className} ${selectedClass}` : selectedClass;\n }\n // News since the previous paint. The check runs against the set as it\n // was BEFORE this paint started \u2014 `_paint` replaces it only after every\n // row is built.\n if (this.options.flashNewClass && this._flashKnown !== null && !this._flashKnown.has(key)) {\n tr.className = tr.className ? `${tr.className} ${this.options.flashNewClass}` : this.options.flashNewClass;\n }\n const mode = this.options.selection || 'none';\n if (mode !== 'none') {\n // Selection happens on mousedown, not on click, for two reasons that are\n // the same reason: that is when a real table responds, and that is when the\n // browser would otherwise start dragging a text selection across the rows.\n // preventDefault claims the gesture for the table.\n tr.addEventListener('mousedown', (event: Event) => {\n const button = (event as unknown as { button?: number }).button;\n if (button !== undefined && button !== 0) return; // right-click belongs to the menu\n event.preventDefault();\n DataGrid._active = this as DataGrid<unknown>;\n this._clickRow(key, event);\n\n const m = event as unknown as { shiftKey?: boolean; ctrlKey?: boolean; metaKey?: boolean };\n if (mode === 'multi' && !m.shiftKey && !m.ctrlKey && !m.metaKey) this._beginDragSelect();\n });\n\n // Dragging across rows extends the selection, which is what the pointer\n // was doing anyway -- it just used to paint text instead of rows.\n tr.addEventListener('mouseenter', (event: Event) => {\n if (!this._dragging) return;\n // A mouseup released outside the window never reaches us, and a grid\n // stuck in drag mode selects whatever the pointer wanders over next.\n // The event carries which buttons are actually down; trust that.\n const buttons = (event as unknown as { buttons?: number }).buttons;\n if (buttons !== undefined && (buttons & 1) === 0) { this._dragging = false; return; }\n this._extendTo(key);\n });\n }\n\n if (this.options.bindRow) this.options.bindRow(tr, row);\n this._rowEls.set(key, tr);\n this._cellEls.set(key, cells);\n return tr;\n }\n\n private _pinnedRow(pinned: GridPinnedRow): HTMLTableRowElement {\n if (pinned.cells.length > this.options.columns.length) {\n throw new Error(`DataGrid: pinned row \"${pinned.key}\" declares ${pinned.cells.length} cells `\n + `for ${this.options.columns.length} columns`);\n }\n\n const tr = document.createElement('tr') as HTMLTableRowElement;\n if (pinned.className) tr.className = pinned.className;\n tr.dataset.rowKey = pinned.key;\n\n // The cells are authored positionally against the DECLARED columns, so they\n // are keyed by that before rendering: a summary cell belongs to its column,\n // and has to travel when the user moves it rather than stay in slot 0.\n const byColumn = new Map<string, GridPinnedCell>();\n this.options.columns.forEach((col, i) => {\n const cell = pinned.cells[i];\n if (cell) byColumn.set(col.key, cell);\n });\n\n const cells = new Map<string, HTMLTableCellElement>();\n for (const col of this.visibleColumns()) {\n const td = document.createElement('td') as HTMLTableCellElement;\n td.dataset.col = col.key;\n const cell = byColumn.get(col.key);\n if (cell) {\n if (cell.className) td.className = cell.className;\n if (typeof cell.content === 'string') td.textContent = cell.content;\n else td.appendChild(cell.content);\n }\n tr.appendChild(td);\n cells.set(col.key, td);\n }\n\n this._rowEls.set(pinned.key, tr);\n this._cellEls.set(pinned.key, cells);\n return tr;\n }\n\n /// Rows by their group value, in value order, each group keeping the row order\n /// it was handed.\n private _groups(rows: TRow[]): Map<string, GridGroup<TRow>> {\n const col = this.options.columns.find(c => c.key === this._group);\n const groups = new Map<string, GridGroup<TRow>>();\n for (const row of rows) {\n // Keyed by the raw value and labelled by the readable one: collapse state\n // and the stored view are written against the key, so a column that starts\n // spelling its values differently must not lose which groups were shut.\n const key = DataGrid._text(col && col.value ? col.value(row) : null);\n const group = groups.get(key);\n if (group) group.rows.push(row);\n else groups.set(key, { label: col ? this._displayText(col, row) : key, rows: [row] });\n }\n // Ordered by the label, so the groups read alphabetically in the language on\n // screen -- which does mean two languages show the same table in a different\n // group order, while the keys underneath, and any saved view, stay put.\n return new Map([...groups.entries()].sort((a, b) => this._collator.compare(a[1].label, b[1].label)));\n }\n\n private _groupRow(key: string, label: string, count: number): HTMLTableRowElement {\n const tr = document.createElement('tr') as HTMLTableRowElement;\n tr.className = this._collapsed.has(key) ? 'grid-group is-collapsed' : 'grid-group';\n tr.dataset.group = key;\n\n const collapsed = this._collapsed.has(key);\n const td = document.createElement('td') as HTMLTableCellElement;\n td.colSpan = this.visibleColumns().length;\n const content = this.options.groupHeader\n ? this.options.groupHeader(label, count, collapsed)\n : `${collapsed ? '\u25B8' : '\u25BE'} ${label} (${count})`;\n if (typeof content === 'string') td.textContent = content;\n else td.appendChild(content);\n tr.appendChild(td);\n\n tr.addEventListener('click', () => this.toggleGroup(key));\n return tr;\n }\n\n private _emptyRow(): HTMLTableRowElement {\n const tr = document.createElement('tr') as HTMLTableRowElement;\n const td = document.createElement('td') as HTMLTableCellElement;\n td.className = 'grid-empty';\n td.colSpan = this.visibleColumns().length;\n td.textContent = this.options.emptyText;\n tr.appendChild(td);\n return tr;\n }\n\n /**\n * Drag a header to move its column in front of the one it is dropped on.\n *\n * The dragged key lives on the instance rather than in dataTransfer: the payload\n * is this grid's, and reading it back out of the event would mean a header\n * dragged from anywhere -- another grid, another window -- could reorder this one.\n */\n private _bindHeaderDrag(th: HTMLElement, key: string): void {\n th.setAttribute('draggable', 'true');\n\n th.addEventListener('dragstart', () => { this._dragKey = key; });\n th.addEventListener('dragend', () => { this._dragKey = null; });\n\n // Without preventDefault the browser refuses the drop, and the header just\n // springs back with nothing to show for the gesture.\n th.addEventListener('dragover', (event: Event) => {\n if (this._dragKey !== null && this._dragKey !== key) event.preventDefault();\n });\n\n th.addEventListener('drop', (event: Event) => {\n event.preventDefault();\n const moved = this._dragKey;\n this._dragKey = null;\n if (moved === null || moved === key) return;\n\n const order = this._order.filter(k => k !== moved);\n order.splice(order.indexOf(key), 0, moved);\n this._applyOrder(order);\n this._rerender();\n });\n }\n\n private static _text(value: unknown): string {\n return value === null || value === undefined ? '' : String(value);\n }\n}\n", "// Active orders \u2014 multi-instance.\n//\n// Builds its own DOM (see `_buildRoot`) rather than cloning a <template> out of\n// the page, so it can be constructed by any host that supplies a `TradingHost`.\n// Every instance shares the same data source (a host refresh / an order update\n// off the socket); each update fans through the host's broadcast so duplicates\n// stay in sync.\n//\n// The table itself is `DataGrid` from `@stocksharp/grids`: the columns below are\n// the single declaration of this blotter \u2014 caption, value, rendering, colour\n// class and export all live together, and the <thead> is left empty for the\n// grid to fill. Cells that hold a control return a real Node with its own\n// listener, so nothing here reaches a global through an inline onclick\n// attribute; the callbacks arrive as deps instead.\nimport { cleanRejectReason, formatPrice, formatQty } from './formatters.js';\nimport { makeElement, makeIconButton, makePanelId, makePanelRoot } from './dom.js';\nimport { ControlTypes } from './control-types.js';\nimport { makeGridMenu } from './grid-menu.js';\nimport { TradingHost, assertHost } from './trading-host.js';\nimport type { OrderRow } from './trading-data.js';\nimport { DataGrid, GridColumn } from '@stocksharp/grids/source/data-grid';\n\n/// StockSharp order states as they arrive on the wire. The blotter branches on\n/// them for row colour, for whether a cell can be edited in place and for what\n/// the \u00D7 button does, so they are named once here instead of appearing as bare\n/// numbers at each branch.\nexport const OrderStates = {\n PendingRisk: 1,\n Sent: 2,\n Active: 3,\n PartiallyFilled: 4,\n Filled: 5,\n Rejected: 6,\n Cancelled: 7,\n} as const;\n\n/// The gestures that reach a venue. All required together: a panel that can\n/// cancel one order but not all of them is a panel half-wired.\nexport interface ActiveOrdersActions {\n cancelOrder(orderId: number): void;\n dismissOrder(orderId: number): void;\n editOrderField(orderId: number, field: string): void;\n replaceOrder(orderId: number, quantity: number, limitPrice: number, stopPrice: number): void;\n cancelAllOrders(): void;\n refreshOrders(): void;\n}\n\n/// What the blotter needs beyond the host port, in one of two shapes.\n///\n/// A live blotter carries every action, because a panel that cannot cancel,\n/// modify or reload its orders is not that panel. A read-only one carries none:\n/// over a finished run - a backtest report, an archived session - there is no\n/// venue left to reach, and the gestures that would reach it are not rendered\n/// rather than rendered and wired to nothing. The union states that, so a live\n/// consumer still cannot omit an action by accident.\nexport type ActiveOrdersDeps =\n | ({ host: TradingHost; readOnly?: false } & ActiveOrdersActions)\n | { host: TradingHost; readOnly: true };\n\nexport class ActiveOrdersWidget {\n static TYPE = ControlTypes.ActiveOrders;\n\n rootEl: HTMLElement;\n el: HTMLElement | null;\n // `//` rather than `///` from here down \u2014 see the note in positions-widget.\n _host: TradingHost;\n _deps: ActiveOrdersDeps;\n _readOnly: boolean;\n _actions: ActiveOrdersActions | null;\n _closeBtn: HTMLElement | null;\n _cancelAllBtn: HTMLElement | null;\n _refreshBtn: HTMLElement | null;\n _exportBtn: HTMLElement | null;\n _orders: OrderRow[];\n _grid: DataGrid<OrderRow> | null;\n\n static create(hostEl: HTMLElement, state: Record<string, unknown>, deps: ActiveOrdersDeps): ActiveOrdersWidget {\n // Assert before building: the markup below is localized through the\n // host, so a missing host has to fail here rather than render a panel\n // captioned with raw English keys.\n const host = assertHost(deps?.host, 'ActiveOrdersWidget');\n const root = ActiveOrdersWidget._buildRoot(host, deps?.readOnly === true);\n root.id = makePanelId(ActiveOrdersWidget.TYPE);\n hostEl.appendChild(root);\n return new ActiveOrdersWidget(root, state || {}, deps);\n }\n\n // The panel's markup. The host stylesheet reads this structure, and a\n // docking host lifts `.panel-header`'s children into its tab strip.\n //\n // The rail's first two buttons are the ones that could not come across\n // unchanged from the terminal's Razor template: they were inline\n // `onclick=\"terminalApp.cancelAll()\"` / `refreshOrders()`, references to a\n // global this control must not have. They carry `.panel-cancel-all-btn` and\n // `.panel-refresh-btn` now \u2014 the latter the name the other blotters' rails\n // already give the identical button \u2014 and are wired to deps. Cancel-all\n // keeps a distinct accessible name from its tooltip, which is why it states\n // `aria-label` rather than taking the tooltip for both.\n static _buildRoot(host: TradingHost, readOnly = false): HTMLElement {\n return makePanelRoot('active-orders-panel', host.t('ActiveOrders'), [\n makeElement('div', 'panel-header', {}, [\n makeElement('span', '', {}, [host.t('OpenOrders')]),\n makeIconButton('bt-icon-btn bt-icon-cancel panel-close-btn', host.t('ClosePanel'), 'bi-x', { type: 'button' }),\n ]),\n makeElement('div', 'panel-body panel-body-with-rail', {}, [\n makeElement('div', 'panel-body-content', {}, [\n makeElement('table', 'terminal-table active-orders-table', { role: 'table', 'aria-label': host.t('ActiveOrdersList') }, [\n makeElement('thead', '', {}, []),\n makeElement('tbody', 'active-orders-body', {}, []),\n ]),\n ]),\n makeElement('div', 'panel-rail', { role: 'toolbar', 'aria-label': host.t('ActiveOrdersActions') }, [\n ...(readOnly ? [] : [\n makeIconButton('bt-icon-btn bt-icon-cancel-all panel-cancel-all-btn', host.t('CancelAll'), 'bi-x-circle',\n { 'aria-label': host.t('CancelAllOrders') }),\n makeIconButton('bt-icon-btn panel-refresh-btn', host.t('Refresh'), 'bi-arrow-clockwise', {}),\n ]),\n makeIconButton('bt-icon-btn panel-export-btn', host.t('ExportToExcel'), 'bi-file-earmark-spreadsheet', {}),\n ]),\n ]),\n ]);\n }\n\n constructor(rootEl: HTMLElement, _state: Record<string, unknown>, deps: ActiveOrdersDeps) {\n this._host = assertHost(deps?.host, 'ActiveOrdersWidget');\n this._readOnly = deps?.readOnly === true;\n if (this._readOnly) {\n this._actions = null;\n } else {\n const actions = deps as ActiveOrdersActions;\n for (const name of ['cancelOrder', 'dismissOrder', 'editOrderField', 'replaceOrder', 'cancelAllOrders', 'refreshOrders'] as const) {\n if (typeof actions?.[name] !== 'function')\n throw new Error(`ActiveOrdersWidget: dep \"${name}\" is required`);\n }\n this._actions = actions;\n }\n\n this.rootEl = rootEl;\n this._deps = deps;\n this.el = this.rootEl.querySelector('.active-orders-body');\n this._closeBtn = this.rootEl.querySelector('.panel-close-btn');\n this._cancelAllBtn = this.rootEl.querySelector('.panel-cancel-all-btn');\n this._refreshBtn = this.rootEl.querySelector('.panel-refresh-btn');\n this._exportBtn = this.rootEl.querySelector('.panel-export-btn');\n this._orders = [];\n\n this._closeBtn?.addEventListener('click', (e) => {\n e.preventDefault();\n this._host.close();\n });\n\n this._cancelAllBtn?.addEventListener('click', (e) => {\n e.preventDefault();\n this._actions?.cancelAllOrders();\n });\n\n this._refreshBtn?.addEventListener('click', (e) => {\n e.preventDefault();\n this._actions?.refreshOrders();\n });\n\n this._exportBtn?.addEventListener('click', (e) => {\n e.preventDefault();\n this._export();\n });\n\n const head = this.rootEl.querySelector('.active-orders-table thead');\n this._grid = head && this.el\n ? new DataGrid<OrderRow>({\n head: head as HTMLElement,\n body: this.el,\n columns: this._columns(),\n // Newest order on top by default (localId is monotonic) \u2014 the resting view puts the\n // user's most recent actions where they look, instead of raw cache order. Natural\n // array order is \"newest first\" too (deltas unshift), but only by accident; any\n // header click re-renders the same array through the sorter.\n defaultSort: { col: 'id', dir: 'desc' },\n // The register tx, not the friendly localId shown in the ID column: it is what\n // cancel / replace / inline edit address an order by.\n rowKey: (o) => String(o.id),\n emptyText: this._host.t('NoActiveOrders'),\n // Terminal rows are greyed out so the live ones stay visually prominent.\n rowClass: (o) => ActiveOrdersWidget._rowClass(o),\n contextMenu: makeGridMenu(this._host),\n // Press to mark, drag or Shift to range, Ctrl to toggle, Ctrl+C\n // to copy \u2014 all the grid's own; the press also claims the\n // gesture from the browser's text selection.\n selection: 'multi',\n })\n : null;\n\n this._host.register(this);\n }\n\n dispose(): void {\n // The grid holds document-level listeners (the copy shortcut) that\n // outlive a removed subtree \u2014 it has to be told, not just detached.\n this._grid?.destroy();\n this._host.unregister(this);\n try { this.rootEl.remove(); } catch { /* already detached */ }\n }\n\n update(orders: OrderRow[]): void {\n this._orders = orders || [];\n this._grid?.setRows(this._orders);\n }\n\n // Export the table to .xlsx in the currently rendered (sorted) order, with the\n // same localized side/type/status texts the user sees on screen \u2014 the columns\n // carry both forms, so the sheet cannot drift from the table.\n _export(): void {\n this._grid?.download('orders', this._host.t('OpenOrders'));\n }\n\n /// Apply a single order delta from the feed. Insert if new, merge if known.\n /// Nothing drops out of the list: Filled and Cancelled rows stay so the user\n /// can watch an order's transitions instead of having a row vanish, and a\n /// Rejected row stays so its reason can be read on hover and dismissed\n /// explicitly through the \u00D7 button.\n applyDelta(order: OrderRow): void {\n if (!order || order.id == null) return;\n const idx = this._orders.findIndex(o => o.id === order.id);\n if (idx >= 0)\n this._orders[idx] = { ...this._orders[idx], ...order };\n else\n this._orders.unshift(order);\n this.update(this._orders);\n }\n\n /// Remove a row from the local view without touching the server. Used by the\n /// \u00D7 button on a terminal order (nothing left to cancel), and in any other\n /// client-side dismiss path.\n removeOrder(orderId: number): void {\n const idx = this._orders.findIndex(o => o.id === orderId);\n if (idx >= 0) {\n this._orders.splice(idx, 1);\n this.update(this._orders);\n }\n }\n\n getOrder(orderId: number): OrderRow | undefined {\n return this._orders.find(o => o.id === orderId);\n }\n\n startInlineEdit(orderId: number, field: 'quantity' | 'limitPrice' | 'stopPrice'): void {\n const order = this.getOrder(orderId);\n if (!order) return;\n // Addressed by the grid's row key (the register tx) and the column key, so\n // the edit lands on the right cell whatever the sort is. A lookup that\n // scanned the rendered ID column for the order id would never match, because\n // that column shows the friendly counter.\n const cell = this._grid?.cellElement(String(orderId), field);\n if (!cell || cell.querySelector('input')) return;\n const currentValue = order[field];\n const input = document.createElement('input');\n input.type = 'number';\n input.className = 'inline-edit-input';\n input.value = String(currentValue ?? '');\n input.step = field === 'quantity' ? '1' : '0.01';\n input.min = field === 'quantity' ? '1' : '0.01';\n cell.textContent = '';\n cell.appendChild(input);\n input.focus();\n input.select();\n const commit = () => {\n const newVal = parseFloat(input.value);\n if (isNaN(newVal) || newVal <= 0) { this.update(this._orders); return; }\n if (newVal === currentValue) { this.update(this._orders); return; }\n const updated = { quantity: order.quantity, limitPrice: order.limitPrice, stopPrice: order.stopPrice };\n updated[field] = newVal;\n this._actions?.replaceOrder(orderId, updated.quantity!, updated.limitPrice!, updated.stopPrice!);\n };\n input.addEventListener('blur', commit);\n input.addEventListener('keydown', (e) => {\n if (e.key === 'Enter') { e.preventDefault(); input.blur(); }\n if (e.key === 'Escape') { this.update(this._orders); }\n });\n }\n\n // The blotter's single column declaration: what the header says, what the\n // grid sorts on, what the cell shows, how it is coloured, and what reaches\n // the exported sheet.\n _columns(): GridColumn<OrderRow>[] {\n // `label`, not `t` \u2014 every row lambda below binds `o` for the order,\n // but keeping the translator under a name of its own reads clearer\n // next to the presentation object it sits beside.\n const label = (key: string) => this._host.t(key);\n const presentation = this._host.presentation;\n return [\n {\n key: 'id',\n header: label('ID'),\n exportable: true,\n // Per-user friendly counter (1, 2, 3, \u2026) issued by the host. `o.id`\n // keeps the register tx for cancel / replace routing; `o.localId` is\n // just the badge the user wants to see. Rows cached from before the\n // counter shipped have no localId \u2014 fall back to id so they don't go\n // blank until the next snapshot replay reassigns one.\n value: (o) => o.localId ?? o.id,\n },\n {\n key: 'instrument',\n header: label('Sym'),\n exportable: true,\n value: (o) => o.instrument,\n },\n {\n key: 'side',\n header: label('Side'),\n exportable: true,\n value: (o) => o.side,\n render: (o) => presentation.sideText(o.side!),\n cellClass: (o) => presentation.sideClass(o.side!),\n exportValue: (o) => presentation.sideText(o.side!),\n },\n {\n key: 'type',\n header: label('Type'),\n exportable: true,\n value: (o) => o.type,\n render: (o) => presentation.typeText(o.type!, o.limitPrice!, o.stopPrice!),\n exportValue: (o) => presentation.typeText(o.type!, o.limitPrice!, o.stopPrice!),\n },\n {\n key: 'quantity',\n header: label('Qty'),\n exportable: true,\n value: (o) => o.quantity,\n render: (o) => formatQty(o.quantity),\n cellClass: (o) => this._readOnly ? '' : ActiveOrdersWidget._editableClass(o, 'quantity'),\n bindCell: (td, o) => this._bindInlineEdit(td, o, 'quantity'),\n },\n {\n key: 'limitPrice',\n header: label('Price'),\n exportable: true,\n value: (o) => o.limitPrice,\n render: (o) => o.limitPrice ? formatPrice(o.limitPrice) : label('MKT'),\n cellClass: (o) => this._readOnly ? '' : ActiveOrdersWidget._editableClass(o, 'limitPrice'),\n bindCell: (td, o) => this._bindInlineEdit(td, o, 'limitPrice'),\n exportValue: (o) => o.limitPrice ?? label('MKT'),\n },\n {\n key: 'stopPrice',\n header: label('Stop'),\n exportable: true,\n value: (o) => o.stopPrice,\n render: (o) => o.stopPrice ? formatPrice(o.stopPrice) : '--',\n cellClass: (o) => this._readOnly ? '' : ActiveOrdersWidget._editableClass(o, 'stopPrice'),\n bindCell: (td, o) => this._bindInlineEdit(td, o, 'stopPrice'),\n exportValue: (o) => o.stopPrice ?? '',\n },\n {\n key: 'status',\n header: label('Status'),\n exportable: true,\n value: (o) => o.status,\n render: (o) => this._statusCell(o),\n exportValue: (o) => presentation.statusText(o.status!),\n },\n ...(this._readOnly ? [] : [{\n key: 'actions',\n header: label('Actions'),\n headerHidden: true,\n exportable: false,\n cellClass: () => 'position-actions',\n render: (o: OrderRow) => this._actionButton(o),\n } as GridColumn<OrderRow>]),\n ];\n }\n\n static _rowClass(order: OrderRow): string {\n if (order.status === OrderStates.Filled) return 'order-filled';\n if (order.status === OrderStates.Rejected) return 'order-rejected';\n if (order.status === OrderStates.Cancelled) return 'order-cancelled';\n return '';\n }\n\n // An order can still be modified while the venue holds it. A market order has\n // no limit price and an unconditional order no stop price, so those cells have\n // nothing to edit even then.\n static _canEdit(order: OrderRow, field: 'quantity' | 'limitPrice' | 'stopPrice'): boolean {\n const modifiable = order.status === OrderStates.Active || order.status === OrderStates.Sent;\n return modifiable && (field === 'quantity' || !!order[field]);\n }\n\n static _editableClass(order: OrderRow, field: 'quantity' | 'limitPrice' | 'stopPrice'): string {\n return ActiveOrdersWidget._canEdit(order, field) ? 'cell-editable' : '';\n }\n\n // Double-click to edit belongs to the whole cell rather than to a control\n // inside it, which is why it is wired here instead of returned by render().\n _bindInlineEdit(td: HTMLTableCellElement, order: OrderRow, field: 'quantity' | 'limitPrice' | 'stopPrice'): void {\n if (this._actions === null || !ActiveOrdersWidget._canEdit(order, field)) return;\n const edit = this._actions.editOrderField;\n td.addEventListener('dblclick', () => edit(order.id!, field));\n }\n\n // Status text, plus a hoverable icon carrying the rejection reason. Some\n // venues wrap the human-readable rejection inside a JSON blob;\n // cleanRejectReason extracts `.message` and falls back to the raw string.\n // The reason goes on the element's title property, so no quote escaping is\n // involved the way it was when this row was built as markup.\n _statusCell(order: OrderRow): string | Node {\n const text = this._host.presentation.statusText(order.status!);\n const reason = order.status === OrderStates.Rejected ? cleanRejectReason(order.rejectReason) : '';\n if (!reason) return text;\n\n const cell = document.createDocumentFragment();\n cell.appendChild(document.createTextNode(text + ' '));\n const icon = document.createElement('i');\n icon.className = 'bi bi-question-circle reject-reason-icon';\n icon.title = reason;\n cell.appendChild(icon);\n return cell;\n }\n\n // The \u00D7 button, wired to the controller through deps. Status decides what it\n // means: Pending / Active is a real cancel over the wire, while Filled /\n // Rejected / Cancelled have nothing left to cancel \u2014 the row is history kept\n // around so the user can read its final state, so the button dismisses it\n // locally.\n _actionButton(order: OrderRow): Node {\n const isTerminal = order.status === OrderStates.Filled\n || order.status === OrderStates.Rejected\n || order.status === OrderStates.Cancelled;\n const title = isTerminal ? this._host.t('Dismiss') : this._host.t('Cancel order');\n\n const button = document.createElement('button');\n button.type = 'button';\n button.className = 'bt-icon-btn bt-icon-cancel';\n button.title = title;\n button.setAttribute('aria-label', `${title} #${order.id}`);\n const icon = document.createElement('i');\n icon.className = 'bi bi-x-circle';\n button.appendChild(icon);\n button.addEventListener('click', () => {\n if (this._actions === null) return;\n if (isTerminal) this._actions.dismissOrder(order.id!);\n else this._actions.cancelOrder(order.id!);\n });\n return button;\n }\n}\n", "// Positions \u2014 multi-instance.\n//\n// Builds its own DOM (see `_buildRoot`) rather than cloning a <template> out of\n// the page it happens to be rendered on, so it can be constructed by any host\n// that supplies a `TradingHost`. Every instance shares the same data source\n// (the host's refresh / a position update off the socket); the host broadcasts\n// each update to every live instance so duplicates of the panel render in\n// lockstep.\n//\n// The table is `DataGrid` from `@stocksharp/grids`: the columns below are this\n// blotter's single declaration and the <thead> is left empty for the grid to\n// fill. The cash balance is a pinned row \u2014 a different shape from a position,\n// so it supplies its own cells, sits outside the sort and stays out of the\n// exported sheet.\nimport { formatPnl, formatPrice } from './formatters.js';\nimport { makeElement, makeIconButton, makePanelId, makePanelRoot } from './dom.js';\nimport { ControlTypes } from './control-types.js';\nimport { makeGridMenu } from './grid-menu.js';\nimport { TradingHost, assertHost } from './trading-host.js';\nimport type { BalanceRow, PositionRow } from './trading-data.js';\nimport { DataGrid, GridColumn, GridPinnedRow, GridPinnedPlacements } from '@stocksharp/grids/source/data-grid';\n\n/// Everything the blotter needs beyond the host port. All required: the action\n/// buttons live in the cells and in the rail now, so a panel that cannot\n/// close, reverse or reload a position is not this panel.\nexport interface PositionsDeps {\n host: TradingHost;\n closePosition(portfolioId: number, instrumentId: number, symbol: string): void;\n reversePosition(portfolioId: number, instrumentId: number, symbol: string): void;\n refreshPositions(): void;\n}\n\nexport class PositionsWidget {\n static TYPE = ControlTypes.Positions;\n\n rootEl: HTMLElement;\n el: HTMLElement | null;\n // Comment style is `//` on purpose below the public members: the declaration\n // emitter keeps a private member's doc comment while dropping its body, so a\n // `///` here would reattach to the next public member in the API snapshot.\n _host: TradingHost;\n _deps: PositionsDeps;\n _closeBtn: HTMLElement | null;\n _refreshBtn: HTMLElement | null;\n _exportBtn: HTMLElement | null;\n _positions: PositionRow[];\n _balance: BalanceRow | null;\n _grid: DataGrid<PositionRow> | null;\n\n static create(hostEl: HTMLElement, state: Record<string, unknown>, deps: PositionsDeps): PositionsWidget {\n // Assert before building: the markup below is localized through the\n // host, so a missing host has to fail here rather than render a panel\n // captioned with raw English keys.\n const host = assertHost(deps?.host, 'PositionsWidget');\n const root = PositionsWidget._buildRoot(host);\n root.id = makePanelId(PositionsWidget.TYPE);\n hostEl.appendChild(root);\n return new PositionsWidget(root, state || {}, deps);\n }\n\n // The panel's markup. The host stylesheet reads this structure, and a\n // docking host lifts `.panel-header`'s children into its tab strip.\n //\n // The rail's refresh button is the one thing that could not come across\n // unchanged from the terminal's Razor template: it used to be an inline\n // `onclick=\"terminalApp.refreshPositions()\"`, which is a reference to a\n // global this control must not have. It carries `.panel-refresh-btn` now \u2014\n // the name the trade-history rail already gave the identical button \u2014 and\n // is wired to the `refreshPositions` dep.\n static _buildRoot(host: TradingHost): HTMLElement {\n const title = host.t('OpenPositions');\n return makePanelRoot('positions-panel', title, [\n makeElement('div', 'panel-header', {}, [\n makeElement('span', '', {}, [host.t('Positions')]),\n makeIconButton('bt-icon-btn bt-icon-cancel panel-close-btn', host.t('ClosePanel'), 'bi-x', { type: 'button' }),\n ]),\n makeElement('div', 'panel-body panel-body-with-rail', {}, [\n makeElement('div', 'panel-body-content', {}, [\n makeElement('table', 'terminal-table positions-table', { role: 'table', 'aria-label': title }, [\n makeElement('thead', '', {}, []),\n makeElement('tbody', 'positions-body', {}, []),\n ]),\n ]),\n makeElement('div', 'panel-rail', { role: 'toolbar', 'aria-label': host.t('PositionsActions') }, [\n makeIconButton('bt-icon-btn panel-refresh-btn', host.t('Refresh'), 'bi-arrow-clockwise', {}),\n makeIconButton('bt-icon-btn panel-export-btn', host.t('ExportToExcel'), 'bi-file-earmark-spreadsheet', {}),\n ]),\n ]),\n ]);\n }\n\n constructor(rootEl: HTMLElement, _state: Record<string, unknown>, deps: PositionsDeps) {\n this._host = assertHost(deps?.host, 'PositionsWidget');\n for (const name of ['closePosition', 'reversePosition', 'refreshPositions'] as const) {\n if (typeof deps?.[name] !== 'function')\n throw new Error(`PositionsWidget: dep \"${name}\" is required`);\n }\n\n this.rootEl = rootEl;\n this._deps = deps;\n this.el = this.rootEl.querySelector('.positions-body');\n this._closeBtn = this.rootEl.querySelector('.panel-close-btn');\n this._refreshBtn = this.rootEl.querySelector('.panel-refresh-btn');\n this._exportBtn = this.rootEl.querySelector('.panel-export-btn');\n this._positions = [];\n this._balance = null;\n\n this._closeBtn?.addEventListener('click', (e) => {\n e.preventDefault();\n this._host.close();\n });\n\n this._refreshBtn?.addEventListener('click', (e) => {\n e.preventDefault();\n this._deps.refreshPositions();\n });\n\n this._exportBtn?.addEventListener('click', (e) => {\n e.preventDefault();\n this._export();\n });\n\n const head = this.rootEl.querySelector('.positions-table thead');\n this._grid = head && this.el\n ? new DataGrid<PositionRow>({\n head: head as HTMLElement,\n body: this.el,\n columns: this._columns(),\n // Alphabetical by instrument at rest \u2014 a stable, predictable order (positions have no\n // natural \"newest\") the user can scan by symbol.\n defaultSort: { col: 'instrument', dir: 'asc' },\n // Same identity applyDelta matches on: id alone isn't unique (id=0 for\n // unsaved snapshots), and a portfolio can hold one position per instrument.\n rowKey: (p) => PositionsWidget._key(p),\n emptyText: this._host.t('No positions'),\n // Re-read on every render, so the cash figures track the live balance\n // without the widget having to repaint the position rows itself.\n pinnedRows: () => this._pinnedRows(),\n contextMenu: makeGridMenu(this._host),\n // Press to mark, drag or Shift to range, Ctrl to toggle, Ctrl+C\n // to copy \u2014 the pinned cash row stays outside the selection the\n // same way it stays outside the sort.\n selection: 'multi',\n })\n : null;\n\n this._host.register(this);\n\n // If the host already has a positions snapshot it will broadcast it on\n // demand; new instances start empty until the first refresh tick.\n }\n\n dispose(): void {\n // The grid holds document-level listeners (the copy shortcut) that\n // outlive a removed subtree \u2014 it has to be told, not just detached.\n this._grid?.destroy();\n this._host.unregister(this);\n try { this.rootEl.remove(); } catch { /* already detached */ }\n }\n\n update(positions: PositionRow[]): void {\n this._positions = positions || [];\n this._grid?.setRows(this._positions);\n }\n\n /// Null clears the pinned row \u2014 which is not the same as an all-zero\n /// balance, and reads differently: no row at all versus a row of zeros.\n updateBalance(balance: BalanceRow | null): void {\n this._balance = balance;\n this._grid?.render();\n }\n\n /// Apply a single position delta. Match by (portfolioId, instrumentId) \u2014 id\n /// alone isn't unique (id=0 for unsaved snapshots). Quantity hitting zero\n /// closes the row.\n applyDelta(position: PositionRow): void {\n if (!position) return;\n const idx = this._positions.findIndex(p =>\n p.portfolioId === position.portfolioId &&\n ((position.instrumentId != null && p.instrumentId === position.instrumentId) ||\n (position.instrumentId == null && p.instrument === position.instrument)));\n const closed = (position.quantity || 0) === 0;\n\n if (idx >= 0) {\n if (closed) this._positions.splice(idx, 1);\n else this._positions[idx] = { ...this._positions[idx], ...position };\n } else if (!closed) {\n this._positions.push(position);\n }\n this.update(this._positions);\n }\n\n // Export the table to .xlsx in the currently rendered (sorted) order. The\n // balance summary is a pinned row \u2014 a different shape from a position \u2014 so\n // the grid keeps it out of the sheet.\n _export(): void {\n this._grid?.download('positions', this._host.t('Positions'));\n }\n\n // The blotter's single column declaration: what the header says, what the\n // grid sorts on, what the cell shows, how it is coloured, and what reaches\n // the exported sheet. Prices are formatted on screen but exported raw, so\n // the sheet stays numeric.\n _columns(): GridColumn<PositionRow>[] {\n const label = (key: string) => this._host.t(key);\n return [\n {\n key: 'instrument',\n header: label('Symbol'),\n exportable: true,\n value: (p) => p.instrument,\n },\n {\n key: 'quantity',\n header: label('Qty'),\n exportable: true,\n value: (p) => p.quantity,\n cellClass: (p) => ((p.quantity ?? 0) > 0 ? 'side-buy' : 'side-sell'),\n },\n {\n key: 'avgPrice',\n header: label('AvgPrice'),\n exportable: true,\n value: (p) => p.avgPrice,\n render: (p) => formatPrice(p.avgPrice),\n },\n {\n key: 'currentPrice',\n header: label('Current'),\n exportable: true,\n value: (p) => p.currentPrice,\n render: (p) => formatPrice(p.currentPrice),\n },\n {\n key: 'pnl',\n header: label('PnL'),\n exportable: true,\n value: (p) => PositionsWidget._totalPnl(p),\n render: (p) => formatPnl(PositionsWidget._totalPnl(p)),\n cellClass: (p) => this._host.presentation.pnlClass(PositionsWidget._totalPnl(p)),\n },\n {\n key: 'actions',\n header: label('Actions'),\n headerHidden: true,\n exportable: false,\n cellClass: () => 'position-actions',\n render: (p) => this._actionButtons(p),\n },\n ];\n }\n\n // The cash balance, pinned above the positions. It does not share the shape\n // the position columns read, so it supplies its own cells; it is not a row\n // of data, so it is neither sorted nor exported. Being content, it also\n // suppresses the \"no positions\" row \u2014 a table showing a balance is not empty.\n _pinnedRows(): GridPinnedRow[] {\n const balance = this._balance;\n if (!balance) return [];\n\n const fmt = (v: number | null | undefined) => (v || 0).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 });\n return [{\n key: 'balance',\n className: 'balance-row',\n place: GridPinnedPlacements.Top,\n cells: [\n { content: this._host.t('USD'), className: 'balance-ccy' },\n { content: '$' + fmt(balance.available), className: 'balance-available' },\n { content: this._host.t('Locked: ${0}', fmt(balance.locked)), className: 'balance-locked' },\n { content: '', className: '' },\n { content: '$' + fmt(balance.total), className: 'balance-total' },\n ],\n }];\n }\n\n // Realized and unrealized together \u2014 what the PnL column shows, sorts and\n // exports, so the three cannot drift apart.\n static _totalPnl(position: PositionRow): number {\n return (position.unrealizedPnl || 0) + (position.realizedPnl || 0);\n }\n\n static _key(position: PositionRow): string {\n return `${position.portfolioId}:${position.instrumentId ?? position.instrument}`;\n }\n\n // Close and reverse, wired to the controller through deps. Real buttons with\n // their own listeners \u2014 the symbol used to be interpolated into an inline\n // onclick attribute, which is why it needed quote-escaping first.\n _actionButtons(position: PositionRow): Node {\n const cell = document.createDocumentFragment();\n cell.appendChild(this._actionButton(\n 'bt-icon-cancel', 'bi-x-circle',\n this._host.t('Close position'), this._host.t('Close position on {0}', position.instrument),\n () => this._deps.closePosition(position.portfolioId!, position.instrumentId!, position.instrument!)));\n cell.appendChild(this._actionButton(\n 'bt-icon-reverse', 'bi-arrow-left-right',\n this._host.t('Reverse position'), this._host.t('Reverse position on {0}', position.instrument),\n () => this._deps.reversePosition(position.portfolioId!, position.instrumentId!, position.instrument!)));\n return cell;\n }\n\n _actionButton(styleClass: string, iconClass: string, title: string, label: string, onClick: () => void): HTMLButtonElement {\n const button = document.createElement('button');\n button.type = 'button';\n button.className = `bt-icon-btn ${styleClass}`;\n button.title = title;\n button.setAttribute('aria-label', label);\n const icon = document.createElement('i');\n icon.className = `bi ${iconClass}`;\n button.appendChild(icon);\n button.addEventListener('click', onClick);\n return button;\n }\n}\n", "// Trade history \u2014 multi-instance.\n//\n// Read-only table of executed trades for the active portfolio. Builds its own\n// DOM (see `_buildRoot`) rather than cloning a <template> out of the page, so\n// it can be constructed by any host that supplies a `TradingHost`.\n//\n// Which portfolio to load and what to load it through both come off the host's\n// trading context rather than being read out of a singleton here: the panel\n// renders what it is given, and the host decides what \"the active portfolio\"\n// means.\n//\n// The table is `DataGrid` from `@stocksharp/grids`: the columns below are this\n// blotter's single declaration and the <thead> is left empty for the grid to\n// fill.\nimport { formatPrice, formatQty } from './formatters.js';\nimport { makeElement, makeIconButton, makePanelId, makePanelRoot } from './dom.js';\nimport { ControlTypes } from './control-types.js';\nimport { makeGridMenu } from './grid-menu.js';\nimport { TradingHost, assertHost } from './trading-host.js';\nimport type { TradeRow } from './trading-data.js';\nimport { DataGrid, GridColumn } from '@stocksharp/grids/source/data-grid';\n\n/// The panel needs nothing beyond the host port: its data source, its\n/// portfolio and its lifecycle all arrive through it.\nexport interface TradeHistoryDeps {\n host: TradingHost;\n /// A blotter over a finished run - a backtest report, an archived session.\n /// There is no account to load from and nothing to reload, so the rows come\n /// in through `update` and the refresh gesture is not rendered.\n readOnly?: boolean;\n}\n\nexport class TradeHistoryWidget {\n static TYPE = ControlTypes.TradeHistory;\n\n rootEl: HTMLElement;\n bodyEl: HTMLElement | null;\n // `//` rather than `///` from here down \u2014 see the note in positions-widget.\n _host: TradingHost;\n _closeBtn: HTMLElement | null;\n _refreshBtn: HTMLElement | null;\n _exportBtn: HTMLElement | null;\n _rows: TradeRow[];\n _grid: DataGrid<TradeRow> | null;\n\n static create(hostEl: HTMLElement, state: Record<string, unknown>, deps: TradeHistoryDeps): TradeHistoryWidget {\n // Assert before building: the markup below is localized through the\n // host, so a missing host has to fail here rather than render a panel\n // captioned with raw English keys.\n const host = assertHost(deps?.host, 'TradeHistoryWidget');\n const root = TradeHistoryWidget._buildRoot(host, deps?.readOnly === true);\n root.id = makePanelId(TradeHistoryWidget.TYPE);\n hostEl.appendChild(root);\n return new TradeHistoryWidget(root, state || {}, deps);\n }\n\n // The panel's markup. The host stylesheet reads this structure, and a\n // docking host lifts `.panel-header`'s children into its tab strip.\n static _buildRoot(host: TradingHost, readOnly = false): HTMLElement {\n const title = host.t('TradeHistory');\n return makePanelRoot('trade-history-panel', title, [\n makeElement('div', 'panel-header', {}, [\n makeElement('span', '', {}, [title]),\n makeIconButton('bt-icon-btn bt-icon-cancel panel-close-btn', host.t('ClosePanel'), 'bi-x', { type: 'button' }),\n ]),\n makeElement('div', 'panel-body panel-body-with-rail', {}, [\n makeElement('div', 'panel-body-content', {}, [\n makeElement('table', 'terminal-table trade-history-table', { role: 'table', 'aria-label': host.t('TradeHistoryList') }, [\n makeElement('thead', '', {}, []),\n makeElement('tbody', 'trade-history-body', {}, []),\n ]),\n ]),\n makeElement('div', 'panel-rail', { role: 'toolbar', 'aria-label': host.t('TradeHistoryActions') }, [\n ...(readOnly ? [] : [makeIconButton('bt-icon-btn panel-refresh-btn', host.t('Refresh'), 'bi-arrow-clockwise', {})]),\n makeIconButton('bt-icon-btn panel-export-btn', host.t('ExportToExcel'), 'bi-file-earmark-spreadsheet', {}),\n ]),\n ]),\n ]);\n }\n\n constructor(rootEl: HTMLElement, _state: Record<string, unknown>, deps: TradeHistoryDeps) {\n this._host = assertHost(deps?.host, 'TradeHistoryWidget');\n\n this.rootEl = rootEl;\n this.bodyEl = this.rootEl.querySelector('.trade-history-body');\n this._closeBtn = this.rootEl.querySelector('.panel-close-btn');\n this._refreshBtn = this.rootEl.querySelector('.panel-refresh-btn');\n this._exportBtn = this.rootEl.querySelector('.panel-export-btn');\n\n this._closeBtn?.addEventListener('click', (e) => {\n e.preventDefault();\n this.dispose();\n this._host.close();\n });\n this._refreshBtn?.addEventListener('click', (e) => {\n e.preventDefault();\n void this.refresh();\n });\n\n this._exportBtn?.addEventListener('click', (e) => {\n e.preventDefault();\n this._export();\n });\n\n this._rows = [];\n\n const head = this.rootEl.querySelector('.trade-history-table thead');\n this._grid = head && this.bodyEl\n ? new DataGrid<TradeRow>({\n head: head as HTMLElement,\n body: this.bodyEl,\n columns: this._columns(),\n // Most recent fill on top by default \u2014 the natural way to read a trade blotter.\n defaultSort: { col: 'time', dir: 'desc' },\n rowKey: (t) => String(t.id),\n emptyText: this._host.t('No trade history'),\n contextMenu: makeGridMenu(this._host),\n // Press to mark, drag or Shift to range, Ctrl to toggle, Ctrl+C\n // to copy \u2014 all the grid's own.\n selection: 'multi',\n })\n : null;\n\n this._host.register(this);\n }\n\n dispose(): void {\n // The grid holds document-level listeners (the copy shortcut) that\n // outlive a removed subtree \u2014 it has to be told, not just detached.\n this._grid?.destroy();\n this._host.unregister(this);\n try { this.rootEl.remove(); } catch { /* already detached */ }\n }\n\n /// Reload against whichever portfolio the host says is active right now.\n ///\n /// Two guards, and they are not the same one. No portfolio means there is\n /// nothing to show \u2014 a guest session has none. A portfolio the host will\n /// not `allow` reading means the session cannot make the call at all: this\n /// is somebody's executed trades, so a page that has a portfolio id lying\n /// around but no live credential must not fire the request and collect a\n /// 401 for it. The panel asks the host rather than inspecting a token,\n /// because what counts as a usable session is the host's to know.\n ///\n /// Order matters: the portfolio check runs first, so a session with no\n /// portfolio is simply quiet \u2014 `allow` is the one that may put a sign-in\n /// prompt on screen, and reaching it means the page did have an account to\n /// load.\n async refresh(): Promise<void> {\n const pf = this._host.trading.portfolioId();\n if (!pf || !this._grid) return;\n if (!this._host.allow('load trade history')) return;\n try {\n this._rows = await this._host.trading.api.getExecutions(pf, null, 200) || [];\n this._grid.setRows(this._rows);\n } catch (err) {\n // Through the port, not the console: this is the diagnostic the\n // user cannot see and support has to, and routing it here is also\n // what makes it assertable.\n this._host.log(`TradeHistoryWidget: failed to load trade history: ${err}`);\n }\n }\n\n /// Show these rows. The push path, for a consumer that already holds the\n /// trades - a finished run's report - rather than an account to load them\n /// from. `refresh` is the pull path and the two do not mix: whichever ran\n /// last is what the grid shows.\n update(rows: TradeRow[]): void {\n this._rows = rows || [];\n this._grid?.setRows(this._rows);\n }\n\n // Export the table to .xlsx in the currently rendered (sorted) order.\n _export(): void {\n this._grid?.download('trades', this._host.t('TradeHistory'));\n }\n\n // The blotter's single column declaration. The two id columns read `#42` on\n // screen and export the bare number, so the sheet stays sortable as a number.\n _columns(): GridColumn<TradeRow>[] {\n // `label`, not `t` \u2014 every row lambda below already binds `t` as the\n // trade it is rendering, and a translator sharing that name would be\n // shadowed inside exactly the callbacks that read it.\n const label = (key: string) => this._host.t(key);\n const presentation = this._host.presentation;\n return [\n {\n key: 'id',\n header: label('ID'),\n exportable: true,\n value: (t) => t.id,\n render: (t) => '#' + t.id,\n cellClass: () => 'mono-id',\n },\n {\n key: 'time',\n header: label('Time'),\n exportable: true,\n value: (t) => t.executedAt || t.time,\n render: (t) => presentation.timeText(t.executedAt || t.time!),\n exportValue: (t) => presentation.timeText(t.executedAt || t.time!),\n },\n {\n key: 'symbol',\n header: label('Sym'),\n exportable: true,\n value: (t) => t.instrumentSymbol || t.symbol,\n },\n {\n key: 'side',\n header: label('Side'),\n exportable: true,\n value: (t) => t.side,\n render: (t) => presentation.sideText(t.side!),\n cellClass: (t) => presentation.sideClass(t.side!),\n exportValue: (t) => presentation.sideText(t.side!),\n },\n {\n key: 'quantity',\n header: label('Qty'),\n exportable: true,\n value: (t) => t.quantity,\n render: (t) => formatQty(t.quantity),\n },\n {\n key: 'price',\n header: label('Price'),\n exportable: true,\n value: (t) => t.price,\n render: (t) => formatPrice(t.price),\n },\n {\n key: 'order',\n header: label('Order'),\n exportable: true,\n value: (t) => t.order || t.orderId,\n render: (t) => '#' + (t.order || t.orderId || ''),\n cellClass: () => 'mono-id',\n },\n ];\n }\n}\n", "// Watchlist \u2014 multi-instance.\n//\n// Builds its own DOM (see `_buildRoot`) rather than cloning a <template> out of\n// the page, so it can be constructed by any host that supplies a `TradingHost`.\n// Each instance has its own search/filter/sort state; favourites are shared,\n// held under one key in the host's preference store.\n//\n// The table is `DataGrid` from `@stocksharp/grids`. The grid holds the FILTERED\n// instruments and does the sorting, which is what makes the two caps mean\n// different things: `renderLimit` caps what gets painted while the export and\n// the subscription sync read the whole filtered set. A live quote patches one\n// cell through the grid's (rowKey, columnKey) lookup instead of repainting \u2014 a\n// repaint would restart the flash animation it just triggered.\nimport { makeElement, makeIconButton, makePanelId, makePanelRoot } from './dom.js';\nimport { ControlTypes } from './control-types.js';\nimport { makeGridMenu } from './grid-menu.js';\nimport { MarketDataClient, MarketDataLevels, TradingApi, TradingHost, assertHost } from './trading-host.js';\nimport type { InstrumentRow, QuoteStats } from './trading-data.js';\nimport { DataGrid, GridColumn } from '@stocksharp/grids/source/data-grid';\n\n/// What the panel needs beyond the host port. Picking an instrument is the one\n/// thing it reports that is not the ticker: the panel does not switch the\n/// host's instrument itself, it says what the user chose and the host acts.\nexport interface WatchlistDeps {\n host: TradingHost;\n onSelect(symbol: string): void;\n}\n\nexport class WatchlistWidget {\n static FAVORITES_KEY = 'terminal_watchlist_favorites';\n // Cache-key prefix for the per-session baseline price used to compute the\n // change %. Day-scoped so the baseline resets every UTC day and the\n // percent matches \"since today open\" intuitively. (Without a REST bars\n // endpoint there is no broker-side day-open available, and the watchlist\n // deliberately avoids REST \u2014 the live stream is its only source.) It goes\n // in the host's cache rather than its preferences: one key per symbol per\n // day is scratch, not a setting worth syncing.\n static BASELINE_KEY_PREFIX = 'wlBaseline:';\n static VISIBLE_CAP = 30;\n static RENDER_CAP = 300;\n static TYPE = ControlTypes.Watchlist;\n\n rootEl: HTMLElement;\n api: TradingApi;\n instruments: InstrumentRow[];\n stats: Map<string, QuoteStats>;\n favorites: Set<string>;\n filter: string;\n search: string;\n currentSymbol: string | null;\n wsClient: MarketDataClient;\n bodyEl: HTMLElement | null;\n searchEl: HTMLInputElement | null;\n tabsEl: HTMLElement | null;\n sortHeaderEl: HTMLElement | null;\n // `//` rather than `///` from here down \u2014 see the note in positions-widget.\n _host: TradingHost;\n _deps: WatchlistDeps;\n // Symbols this widget currently holds a live-quote subscription for.\n // Re-synced from the visible list on every render \u2014 see _syncSubs.\n _subscribedSyms: Set<string>;\n _closeBtn: HTMLElement | null;\n _exportBtn: HTMLElement | null;\n _grid: DataGrid<InstrumentRow> | null;\n\n static create(hostEl: HTMLElement, state: Record<string, unknown>, deps: WatchlistDeps): WatchlistWidget {\n // Assert before building: the markup below is localized through the\n // host, so a missing host has to fail here rather than render a panel\n // captioned with raw English keys.\n const host = assertHost(deps?.host, 'WatchlistWidget');\n const root = WatchlistWidget._buildRoot(host);\n root.id = makePanelId(WatchlistWidget.TYPE);\n hostEl.appendChild(root);\n return new WatchlistWidget(root, state || {}, deps);\n }\n\n // The panel's markup. The host stylesheet reads this structure, and a\n // docking host lifts `.panel-header`'s children \u2014 and, for this panel,\n // `.watchlist-search-row` as well \u2014 into its tab strip, which is why the\n // search row is a sibling of the header rather than a child of it.\n //\n // \"All\" and \"Favorites\" are the built-in tabs. The rest are appended at\n // runtime from the distinct categories of the loaded instruments, so the\n // tab set is configured through whatever assigns those categories rather\n // than being spelled out here \u2014 see _renderCategoryTabs.\n static _buildRoot(host: TradingHost): HTMLElement {\n const search = host.t('SearchInstruments');\n const favorites = host.t('Favorites');\n return makePanelRoot('watchlist-panel', host.t('Watchlist'), [\n makeElement('div', 'panel-header', {}, [\n makeElement('span', '', {}, [host.t('Markets')]),\n makeIconButton('bt-icon-btn panel-export-btn', host.t('ExportToExcel'), 'bi-file-earmark-spreadsheet', { type: 'button' }),\n makeIconButton('bt-icon-btn bt-icon-cancel panel-close-btn', host.t('ClosePanel'), 'bi-x', { type: 'button' }),\n ]),\n makeElement('div', 'watchlist-search-row', {}, [\n makeElement('input', 'form-control form-control-sm watchlist-search-input watchlist-search',\n { type: 'text', placeholder: search, autocomplete: 'off', 'aria-label': search }, []),\n ]),\n makeElement('div', 'watchlist-tabs', { role: 'tablist', 'aria-label': host.t('WatchlistFilter') }, [\n makeElement('button', 'wl-tab active', { 'data-filter': 'all', role: 'tab', 'aria-selected': 'true' }, [host.t('All')]),\n makeElement('button', 'wl-tab', { 'data-filter': 'favorites', role: 'tab', 'aria-selected': 'false', 'aria-label': favorites, title: favorites }, ['\u2605']),\n ]),\n makeElement('div', 'watchlist-pane', {}, [\n makeElement('table', 'watchlist-table', {}, [\n makeElement('thead', 'watchlist-headers', {}, []),\n makeElement('tbody', 'watchlist-body', {}, []),\n ]),\n ]),\n ]);\n }\n\n constructor(rootEl: HTMLElement, _state: Record<string, unknown>, deps: WatchlistDeps) {\n this._host = assertHost(deps?.host, 'WatchlistWidget');\n if (typeof deps?.onSelect !== 'function')\n throw new Error('WatchlistWidget: dep \"onSelect\" is required');\n\n this.rootEl = rootEl;\n this._deps = deps;\n this.api = this._host.trading.api;\n\n this.instruments = [];\n this.stats = new Map();\n this.favorites = new Set(this._loadFavorites());\n this.filter = 'all';\n this.search = '';\n this.currentSymbol = null;\n this._subscribedSyms = new Set();\n this.wsClient = this._host.trading.marketData;\n\n this.bodyEl = this.rootEl.querySelector('.watchlist-body');\n this.searchEl = this.rootEl.querySelector('.watchlist-search');\n this.tabsEl = this.rootEl.querySelector('.watchlist-tabs');\n this.sortHeaderEl = this.rootEl.querySelector('.watchlist-headers');\n this._closeBtn = this.rootEl.querySelector('.panel-close-btn');\n this._exportBtn = this.rootEl.querySelector('.panel-export-btn');\n\n this._closeBtn?.addEventListener('click', (e) => {\n e.preventDefault();\n this.dispose();\n this._host.close();\n });\n this._exportBtn?.addEventListener('click', (e) => {\n e.preventDefault();\n this._export();\n });\n\n this._grid = this.sortHeaderEl && this.bodyEl\n ? new DataGrid<InstrumentRow>({\n head: this.sortHeaderEl,\n body: this.bodyEl,\n columns: this._columns(),\n // Symbol A\u2192Z at rest \u2014 an instrument list has no natural \"newest\",\n // and alphabetical is what the user scans by.\n defaultSort: { col: 'symbol', dir: 'asc' },\n rowKey: (i) => String(i.symbol),\n emptyText: this._host.t('No instruments'),\n rowClass: (i) => (i.symbol === this.currentSymbol ? 'wl-row wl-current' : 'wl-row'),\n bindRow: (tr, i) => tr.addEventListener('click', () => this._deps.onSelect(i.symbol!)),\n // Paint a screenful, export and subscribe over the whole filtered set.\n renderLimit: WatchlistWidget.RENDER_CAP,\n // A header click re-renders through the grid, so this is the only\n // place that sees every change to the visible set \u2014 including a sort\n // the widget was never told about.\n afterRender: () => {\n this._syncSubs();\n this._publishVisible();\n },\n contextMenu: makeGridMenu(this._host),\n })\n : null;\n\n this._bind();\n\n this._host.register(this);\n\n // Auto-init data load: a caller that creates the panel expects a working\n // widget without further ceremony. Unconditional \u2014 the host port\n // guarantees an API client, so there is no \"maybe we can load\" state\n // left to branch on.\n void this.init();\n }\n\n dispose(): void {\n // Release every live subscription this widget held \u2014 the market-data\n // client refcounts them, so another control's subscription survives if\n // it happened to share a symbol with us.\n for (const sym of this._subscribedSyms) {\n try { void this.wsClient?.removeSymbol(sym); } catch { /* socket may be torn down */ }\n }\n this._subscribedSyms.clear();\n this._host.unregister(this);\n try { this.rootEl.remove(); } catch { /* already detached */ }\n }\n\n async init(): Promise<void> {\n try {\n this.instruments = await this.api.searchInstruments('');\n } catch (err) {\n // Through the port, not the console: this is the diagnostic the\n // user cannot see and support has to, and routing it here is also\n // what makes it assertable.\n this._host.log(`WatchlistWidget: failed to load instruments: ${err}`);\n this.instruments = [];\n }\n this._renderCategoryTabs();\n this._render();\n }\n\n // Build the filter tabs from the assigned instrument categories. \"All\" and\n // \"Favorites\" are built into the markup; here we append one tab per distinct\n // non-empty category found in the loaded instruments. Re-runnable: clears\n // any category tabs a previous load added.\n _renderCategoryTabs(): void {\n if (!this.tabsEl) return;\n this.tabsEl.querySelectorAll('.wl-tab[data-category]').forEach(el => el.remove());\n\n const cats = Array.from(new Set(\n this.instruments\n .map(i => (i.category || '').trim())\n .filter(c => c.length > 0)))\n .sort((a, b) => a.localeCompare(b));\n\n for (const cat of cats) {\n const btn = document.createElement('button');\n btn.className = 'wl-tab';\n btn.setAttribute('role', 'tab');\n btn.setAttribute('aria-selected', 'false');\n btn.dataset.filter = cat;\n btn.dataset.category = '1';\n btn.textContent = cat;\n this.tabsEl.appendChild(btn);\n }\n }\n\n /// Highlight the instrument the rest of the page is showing. Patches the two\n /// affected rows rather than repainting: a repaint here would restart any\n /// flash animation currently running in a price cell.\n setCurrentSymbol(symbol: string | null): void {\n const previous = this.currentSymbol;\n this.currentSymbol = symbol;\n if (!this._grid) return;\n if (previous) this._grid.rowElement(String(previous))?.classList.remove('wl-current');\n if (symbol) this._grid.rowElement(String(symbol))?.classList.add('wl-current');\n }\n\n onPriceUpdate(symbol: string, price: number): void {\n if (price == null || !isFinite(price)) return;\n let full = symbol;\n if (!full.includes('@')) {\n const match = this.instruments.find(i => String(i.symbol).split('@')[0] === symbol);\n if (match?.symbol) full = match.symbol;\n }\n const cur = this.stats.get(full);\n const prev = cur?.lastPrice;\n\n // Session-day baseline: first observed live price per (UTC-day, symbol).\n // Kept in the host's cache so reloading the page doesn't jump the\n // baseline mid-day. New UTC day = fresh baseline.\n const baselineKey = WatchlistWidget._baselineKey(full);\n let baseline = cur?.baseline;\n if (baseline == null) {\n const stored = this._host.cache.get(baselineKey, null);\n baseline = stored ? Number(stored) : undefined;\n }\n if (baseline == null || !isFinite(baseline) || baseline === 0) {\n baseline = price;\n this._host.cache.set(baselineKey, String(price));\n }\n const chgPct = baseline ? ((price - baseline) / baseline) * 100 : 0;\n\n this.stats.set(full, { ...(cur || {}), lastPrice: price, baseline, chgPct });\n this._updateRow(full, prev);\n }\n\n static _baselineKey(symbol: string): string {\n const day = new Date().toISOString().slice(0, 10); // YYYY-MM-DD (UTC)\n return WatchlistWidget.BASELINE_KEY_PREFIX + day + ':' + symbol;\n }\n\n _bind(): void {\n if (this.searchEl) {\n this.searchEl.addEventListener('input', () => {\n this.search = (this.searchEl!.value || '').trim().toLowerCase();\n this._render();\n });\n }\n if (this.tabsEl) {\n this.tabsEl.addEventListener('click', (e) => {\n const tgt = e.target as Element | null;\n const btn = tgt?.closest('.wl-tab') as HTMLElement | null;\n if (!btn) return;\n this.filter = btn.dataset.filter || 'all';\n this.tabsEl!.querySelectorAll('.wl-tab').forEach(b => b.classList.toggle('active', b === btn));\n this._render();\n });\n }\n // Sort header clicks are handled by the grid (wired in the constructor).\n }\n\n // The instruments matching the search box and the active tab, unsorted \u2014\n // the grid owns the order.\n _filtered(): InstrumentRow[] {\n const q = this.search;\n const rows: InstrumentRow[] = [];\n for (const inst of this.instruments) {\n const sym = (inst.symbol || '').toUpperCase();\n // Match the \"symbol/exchange\" (and \"symbol@exchange\") display form too, so a\n // full \"BTC/IMEX\" query resolves \u2014 not only the bare symbol \"BTC\".\n const symL = sym.toLowerCase();\n const shortSym = symL.split('@')[0];\n const exch = (inst.exchange || '').toLowerCase();\n const qualified = `${shortSym}/${exch}`;\n const qualifiedAt = `${shortSym}@${exch}`;\n if (q && !symL.includes(q) && !(inst.name || '').toLowerCase().includes(q) && !qualified.includes(q) && !qualifiedAt.includes(q)) continue;\n if (this.filter === 'favorites') {\n if (!this.favorites.has(sym)) continue;\n } else if (this.filter !== 'all') {\n // Any other filter value is an assigned instrument category,\n // matched case-insensitively.\n if ((inst.category || '').toLowerCase() !== this.filter.toLowerCase()) continue;\n }\n rows.push(inst);\n }\n return rows;\n }\n\n _render(): void {\n this._grid?.setRows(this._filtered());\n }\n\n // The blotter's single column declaration. Prices and percentages come off\n // the live stats map rather than the instrument row, so a quote arriving\n // between two renders is picked up by the next one.\n _columns(): GridColumn<InstrumentRow>[] {\n const label = (key: string) => this._host.t(key);\n return [\n {\n key: 'symbol',\n header: label('Symbol'),\n exportable: true,\n value: (i) => i.symbol,\n cellClass: () => 'wl-sym-cell',\n render: (i) => this._symbolCell(i),\n exportValue: (i) => String(i.symbol).split('@')[0],\n },\n {\n key: 'last',\n header: label('Last'),\n headerClass: 'ta-right',\n exportable: true,\n value: (i) => this._statsOf(i).lastPrice,\n cellClass: () => 'wl-last',\n render: (i) => WatchlistWidget._priceText(this._statsOf(i).lastPrice),\n exportValue: (i) => this._statsOf(i).lastPrice ?? '',\n },\n {\n key: 'chgPct',\n header: label('Change24hPct'),\n headerClass: 'ta-right',\n exportable: true,\n value: (i) => this._statsOf(i).chgPct,\n cellClass: (i) => ('wl-chg ' + WatchlistWidget._changeClass(this._statsOf(i).chgPct)).trim(),\n render: (i) => WatchlistWidget._changeText(this._statsOf(i).chgPct),\n exportValue: (i) => this._statsOf(i).chgPct ?? '',\n },\n ];\n }\n\n _statsOf(instrument: InstrumentRow): QuoteStats {\n return this.stats.get(String(instrument.symbol)) ?? {};\n }\n\n // The favourite star plus the short symbol. The star stops the click from\n // reaching the row, whose own listener switches the host's instrument.\n _symbolCell(instrument: InstrumentRow): Node {\n const cell = document.createDocumentFragment();\n\n const star = document.createElement('button');\n star.type = 'button';\n star.className = this.favorites.has(instrument.symbol!) ? 'wl-fav active' : 'wl-fav';\n star.title = this._host.t('Favorite');\n star.textContent = '\u2605';\n star.addEventListener('click', (e) => {\n e.stopPropagation();\n this._toggleFavorite(instrument.symbol!);\n });\n cell.appendChild(star);\n\n const label = document.createElement('span');\n label.className = 'wl-sym';\n label.textContent = String(instrument.symbol).split('@')[0];\n cell.appendChild(label);\n\n return cell;\n }\n\n // Export the current filtered+sorted view to .xlsx \u2014 every matching\n // instrument, without the RENDER_CAP truncation applied to the DOM.\n _export(): void {\n this._grid?.download('watchlist', this._host.t('Markets'));\n }\n\n // Patch the price and change cells of one row in place. A full render would\n // replace the cell element and so cancel the flash animation started here.\n _updateRow(symbol: string, prevPrice: number | null | undefined): void {\n if (!this._grid) return;\n const st = this.stats.get(symbol) || {};\n const lastEl = this._grid.cellElement(String(symbol), 'last');\n const chgEl = this._grid.cellElement(String(symbol), 'chgPct');\n if (lastEl && st.lastPrice != null) {\n lastEl.textContent = WatchlistWidget._priceText(st.lastPrice);\n if (prevPrice != null && st.lastPrice !== prevPrice) {\n lastEl.classList.remove('flash-up', 'flash-down');\n void lastEl.offsetWidth;\n lastEl.classList.add(st.lastPrice > prevPrice ? 'flash-up' : 'flash-down');\n }\n }\n if (chgEl && st.chgPct != null && isFinite(Number(st.chgPct))) {\n chgEl.textContent = WatchlistWidget._changeText(st.chgPct);\n chgEl.classList.remove('up', 'down');\n chgEl.classList.add(WatchlistWidget._changeClass(st.chgPct));\n }\n }\n\n _toggleFavorite(symbol: string): void {\n if (!this._host.allow('add to favorites')) return;\n if (this.favorites.has(symbol)) this.favorites.delete(symbol);\n else this.favorites.add(symbol);\n this._saveFavorites();\n // Broadcast the change to every other watchlist instance so they reflect\n // the new state without a fresh fetch.\n this._host.broadcast<WatchlistWidget>(w => {\n if (w !== this) { w.favorites = new Set(this.favorites); w._render(); }\n });\n this._render();\n }\n\n _loadFavorites(): string[] {\n try {\n const raw = this._host.preferences.get(WatchlistWidget.FAVORITES_KEY, null);\n return raw ? JSON.parse(raw) : [];\n } catch { return []; }\n }\n\n _saveFavorites(): void {\n try { this._host.preferences.set(WatchlistWidget.FAVORITES_KEY, JSON.stringify(Array.from(this.favorites))); }\n catch { /* store may be unavailable */ }\n }\n\n // The symbols at the top of the current order \u2014 the ones a user is actually\n // looking at, and the set both the subscriptions and the host's ticker are\n // sized to.\n _visible(): InstrumentRow[] {\n return this._grid ? this._grid.sortedRows().slice(0, WatchlistWidget.VISIBLE_CAP) : [];\n }\n\n // Diff the desired set of visible symbols against currently-held live-price\n // subscriptions, then add/remove to converge. Called after every render that\n // may have changed the visible list (filter, sort, search). add/removeSymbol\n // refcount internally, so another control's subscription on the same symbol\n // is not affected.\n _syncSubs(): void {\n if (!this.wsClient) return;\n const desired = new Set(this._visible().map(i => String(i.symbol).split('@')[0]));\n\n for (const sym of this._subscribedSyms) {\n if (!desired.has(sym)) {\n try { void this.wsClient.removeSymbol(sym); } catch { /* socket race */ }\n this._subscribedSyms.delete(sym);\n }\n }\n // Stagger the subscribe burst \u2014 firing every watchlist symbol\n // simultaneously while a chart is fetching its history starves the\n // adapter and stalls the chart's snapshot. 80ms between calls keeps the\n // pipe quiet enough for that fetch to return on time, while still\n // feeling instant to the user.\n let i = 0;\n for (const sym of desired) {\n if (this._subscribedSyms.has(sym)) continue;\n this._subscribedSyms.add(sym);\n setTimeout(() => {\n try { void this.wsClient.addSymbol(sym, MarketDataLevels.Quotes); } catch { /* socket race */ }\n }, i * 80);\n i++;\n }\n }\n\n // Tell the host which symbols are on screen. Only the primary instance\n // speaks for the page: duplicated watchlist panels have their own filters,\n // and the page has one ticker for them to fight over.\n _publishVisible(): void {\n if (!this._host.isPrimary) return;\n this._host.ticker.publish(this._visible().map(i => String(i.symbol)), this.stats);\n }\n\n static _priceText(price: number | null | undefined): string {\n const n = Number(price);\n if (price == null || !isFinite(n) || n === 0) return '--';\n if (n >= 1) return n.toFixed(2);\n if (n >= 0.01) return n.toFixed(4);\n return n.toFixed(6);\n }\n\n static _changeText(chgPct: number | null | undefined): string {\n const pct = Number(chgPct);\n if (chgPct == null || !isFinite(pct)) return '--';\n return `${pct >= 0 ? '+' : ''}${pct.toFixed(2)}%`;\n }\n\n static _changeClass(chgPct: number | null | undefined): string {\n const pct = Number(chgPct);\n if (chgPct == null || !isFinite(pct)) return '';\n return pct >= 0 ? 'up' : 'down';\n }\n}\n", "// Order entry \u2014 a split Buy/Sell pad sharing one order-type selector.\n//\n// Builds its own DOM (see `_buildRoot`) rather than cloning a <template> out of\n// the page it happens to be rendered on, and every element it reads is queried\n// under `this.rootEl`, so a host may place more than one of these on a page.\n//\n// It fetches nothing. Reference prices arrive as the host sees them\n// (`setLimitPrice`, `setBbo`), the size and price grid the venue will accept\n// arrives as an `InstrumentSpec` (`setInstrument`), and the balance figures the\n// percent buttons divide arrive through `setAvailable` / `setMaxQuantity`.\n//\n// Placing the order is deliberately not this control's to do: `TradingApi` is\n// read-only, and everything that has to happen before the wire \u2014 the sign-in\n// gate, the connection check, which portfolio the order belongs to \u2014 is the\n// host's. So the pad validates, collects the form and hands both to the\n// `submitOrder` dep.\n//\n// It renders no table, so it is the one control here that does not use\n// `DataGrid`.\nimport { formatPrice, formatQty } from './formatters.js';\nimport { makeElement, makeIconButton, makePanelId, makePanelRoot } from './dom.js';\nimport { ControlTypes } from './control-types.js';\nimport { TradingHost, assertHost } from './trading-host.js';\nimport type { InstrumentSpec } from './trading-data.js';\n\n/// The pad's two columns. Every method that addresses one takes this rather\n/// than a wire side, because a column is the control's own vocabulary: which of\n/// the two forms is being filled in, not what the resulting order will say.\nexport const OrderEntrySides = {\n Buy: 'buy',\n Sell: 'sell',\n} as const;\n\nexport type OrderEntrySide = typeof OrderEntrySides[keyof typeof OrderEntrySides];\n\n/// The order kinds the pad offers. The values are what `data-type` carries on\n/// the tab buttons and what `toApiType` maps onto StockSharp's `OrderTypes`.\nexport const OrderEntryTypes = {\n Market: 'market',\n Limit: 'limit',\n Stop: 'stop',\n StopLimit: 'stoplimit',\n} as const;\n\nexport type OrderEntryType = typeof OrderEntryTypes[keyof typeof OrderEntryTypes];\n\n/// One column of the form, read off its inputs at the moment it is asked for.\n/// A price the chosen type does not use comes back null, so a host can forward\n/// the object without deciding which fields the type made meaningful.\nexport interface OrderEntryValues {\n type: OrderEntryType;\n quantity: number;\n limitPrice: number | null;\n stopPrice: number | null;\n /// Both null while the take-profit / stop-loss block is switched off.\n takeProfit: number | null;\n stopLoss: number | null;\n}\n\n/// Everything the pad needs beyond the host port. `submitOrder` is required: a\n/// form that cannot send is not an order pad, and the port cannot carry it \u2014\n/// `TradingApi` is read-only by design and the send is host policy.\nexport interface OrderEntryDeps {\n host: TradingHost;\n submitOrder(side: OrderEntrySide, values: OrderEntryValues): void;\n}\n\n// StockSharp `Sides`, the spelling `TradingPresentation.sideText` reads. The\n// pad words neither button itself \u2014 the host owns the trading vocabulary.\nconst WIRE_SIDES: Record<OrderEntrySide, number> = { buy: 0, sell: 1 };\n\n// The percentage-of-maximum buttons under the quantity field.\nconst PERCENT_STEPS = [25, 50, 75, 100];\n\n// Where each value lives inside a column. Structure, not looks: the inputs are\n// reached through the field they belong to, so the markup carries one class per\n// field rather than one per input.\nconst PRICE_INPUT = '.oe-field-price input';\nconst STOP_INPUT = '.oe-field-stop input';\nconst QTY_INPUT = '.oe-field-qty input';\nconst TP_INPUT = '.oe-field-tp input';\nconst SL_INPUT = '.oe-field-sl input';\nconst TPSL_CHECK = '.oe-tpsl-toggle input';\nconst AVAILABLE_VALUE = '.oe-avbl .oe-value';\nconst TOTAL_VALUE = '.oe-total .oe-value';\nconst MAX_VALUE = '.oe-foot .oe-value';\n\n// The value fields, in the order a column shows them.\nconst VALUE_INPUTS = [PRICE_INPUT, STOP_INPUT, QTY_INPUT, TP_INPUT, SL_INPUT];\n\nexport class OrderEntryWidget {\n static TYPE = ControlTypes.OrderEntry;\n static SIDES: readonly OrderEntrySide[] = [OrderEntrySides.Buy, OrderEntrySides.Sell];\n\n rootEl: HTMLElement;\n el: HTMLElement | null;\n // Comment style is `//` on purpose below the public members: the declaration\n // emitter keeps a private member's doc comment while dropping its body, so a\n // `///` here would reattach to the next public member in the API snapshot.\n _host: TradingHost;\n _deps: OrderEntryDeps;\n _closeBtn: HTMLElement | null;\n _cols: Record<OrderEntrySide, HTMLElement | null>;\n _orderType: OrderEntryType;\n _instrument: InstrumentSpec | null;\n _lastPrice: number;\n _bestBid: number;\n _bestAsk: number;\n _maxQuantity: Record<OrderEntrySide, number | null>;\n // Which input the user is in. Tracked here rather than read off\n // `document.activeElement`, which is a document-wide lookup a control scoped\n // to its own root has no business making.\n _focused: HTMLElement | null;\n _enabled: boolean;\n\n static create(hostEl: HTMLElement, state: Record<string, unknown>, deps: OrderEntryDeps): OrderEntryWidget {\n // Assert before building: the markup below is localized through the\n // host, so a missing host has to fail here rather than render a pad\n // captioned with raw English keys.\n const host = assertHost(deps?.host, 'OrderEntryWidget');\n const root = OrderEntryWidget._buildRoot(host);\n root.id = makePanelId(OrderEntryWidget.TYPE);\n hostEl.appendChild(root);\n return new OrderEntryWidget(root, state || {}, deps);\n }\n\n // The pad's markup. The host stylesheet reads this structure, and a docking\n // host lifts `.panel-header`'s children into its tab strip.\n //\n // There is no `+` button in the header: a second pad on the same portfolio\n // is a host decision about the account, not a gesture the form offers.\n static _buildRoot(host: TradingHost): HTMLElement {\n const title = host.t('OrderEntry');\n return makePanelRoot('order-entry-panel', title, [\n makeElement('div', 'panel-header', {}, [\n makeElement('span', '', {}, [title]),\n makeIconButton('bt-icon-btn bt-icon-cancel panel-close-btn', host.t('ClosePanel'), 'bi-x', { type: 'button' }),\n ]),\n makeElement('div', 'order-entry-content', {}, [\n OrderEntryWidget._buildTypeTabs(host),\n makeElement('div', 'order-entry-split', {}, [\n OrderEntryWidget._buildColumn(host, OrderEntrySides.Buy),\n OrderEntryWidget._buildColumn(host, OrderEntrySides.Sell),\n ]),\n ]),\n ]);\n }\n\n // Two pairs of type buttons, so each pair lines up with the column beneath\n // it. Which one is active is decided by `setOrderType`, from the constructor\n // and from a later click alike.\n static _buildTypeTabs(host: TradingHost): HTMLElement {\n const tab = (type: OrderEntryType, caption: string) =>\n makeElement('button', 'btn-ot', { type: 'button', 'data-type': type, 'aria-pressed': 'false' }, [caption]);\n\n return makeElement('div', 'order-type-tabs', { role: 'group', 'aria-label': host.t('OrderType') }, [\n makeElement('div', 'ot-pair', {}, [\n tab(OrderEntryTypes.Market, host.t('Market')),\n tab(OrderEntryTypes.Limit, host.t('Limit')),\n ]),\n makeElement('div', 'ot-pair', {}, [\n tab(OrderEntryTypes.Stop, host.t('Stop')),\n tab(OrderEntryTypes.StopLimit, host.t('StopLimit')),\n ]),\n ]);\n }\n\n // One column. The modifier class and the submit button's colour class are\n // assigned as whole literals rather than composed, so the style contract can\n // read this control's class list out of the source.\n static _buildColumn(host: TradingHost, side: OrderEntrySide): HTMLElement {\n const isBuy = side === OrderEntrySides.Buy;\n const sideText = host.presentation.sideText(WIRE_SIDES[side]);\n const maxLabel = isBuy ? host.t('Max Buy') : host.t('Max Sell');\n // The shortcut is the host's, so its letter is a translated string like\n // any other caption rather than a key typed in here.\n const hotkey = isBuy ? host.t('BuyHotkey') : host.t('SellHotkey');\n\n const submit = makeElement('button', 'btn-oe-submit', { type: 'button', 'aria-label': sideText }, [\n sideText,\n makeElement('span', 'hotkey-hint', {}, [`[${hotkey}]`]),\n ]);\n if (isBuy) submit.className = 'btn-oe-submit btn-buy';\n else submit.className = 'btn-oe-submit btn-sell';\n\n const column = makeElement('div', 'oe-col', { 'data-side': side }, [\n makeElement('div', 'oe-avbl', {}, [\n makeElement('span', 'oe-label', {}, [host.t('Avbl')]),\n makeElement('span', 'oe-value', {}, ['--']),\n ]),\n makeElement('div', 'oe-field oe-field-price', {}, [\n makeElement('span', 'oe-label', {}, [host.t('Price')]),\n makeElement('div', 'oe-input-grp', {}, [\n makeElement('input', 'oe-input', { type: 'number', 'aria-label': host.t('Price') }, []),\n makeElement('button', 'btn-bbo', { type: 'button', title: host.t('BestBidOrOffer') }, [host.t('BBO')]),\n ]),\n ]),\n makeElement('div', 'oe-field oe-field-stop', {}, [\n makeElement('span', 'oe-label', {}, [host.t('StopPrice')]),\n makeElement('input', 'oe-input', { type: 'number', 'aria-label': host.t('StopPrice') }, []),\n ]),\n makeElement('div', 'oe-field oe-field-qty', {}, [\n makeElement('span', 'oe-label', {}, [host.t('Amount')]),\n makeElement('input', 'oe-input', { type: 'number', 'aria-label': host.t('OrderQuantity') }, []),\n ]),\n makeElement('div', 'oe-pct-row', { role: 'group', 'aria-label': host.t('PercentageOfBalance') },\n PERCENT_STEPS.map(pct => makeElement('button', 'btn-oe-pct', { type: 'button', 'data-pct': String(pct) }, [`${pct}%`]))),\n makeElement('div', 'oe-total', {}, [\n makeElement('span', 'oe-label', {}, [host.t('Total')]),\n makeElement('span', 'oe-value', {}, ['--']),\n ]),\n makeElement('label', 'oe-tpsl-toggle', {}, [\n makeElement('input', '', { type: 'checkbox' }, []),\n makeElement('span', '', {}, [host.t('TpSl')]),\n ]),\n makeElement('div', 'oe-tpsl oe-field-hidden', {}, [\n makeElement('div', 'oe-field oe-field-tp', {}, [\n makeElement('span', 'oe-label', {}, [host.t('TakeProfit')]),\n makeElement('input', 'oe-input', { type: 'number', 'aria-label': host.t('TakeProfit') }, []),\n ]),\n makeElement('div', 'oe-field oe-field-sl', {}, [\n makeElement('span', 'oe-label', {}, [host.t('StopLoss')]),\n makeElement('input', 'oe-input', { type: 'number', 'aria-label': host.t('StopLoss') }, []),\n ]),\n ]),\n makeElement('div', 'oe-foot', {}, [\n makeElement('span', 'oe-label', {}, [maxLabel]),\n makeElement('span', 'oe-value', {}, ['--']),\n ]),\n submit,\n makeElement('div', 'oe-estimate', { 'aria-live': 'polite' }, []),\n ]);\n if (isBuy) column.className = 'oe-col oe-col-buy';\n else column.className = 'oe-col oe-col-sell';\n return column;\n }\n\n constructor(rootEl: HTMLElement, _state: Record<string, unknown>, deps: OrderEntryDeps) {\n this._host = assertHost(deps?.host, 'OrderEntryWidget');\n for (const name of ['submitOrder'] as const) {\n if (typeof deps?.[name] !== 'function')\n throw new Error(`OrderEntryWidget: dep \"${name}\" is required`);\n }\n\n this.rootEl = rootEl;\n this._deps = deps;\n this.el = this.rootEl.querySelector('.order-entry-content');\n this._closeBtn = this.rootEl.querySelector('.panel-close-btn');\n this._cols = {\n buy: this.rootEl.querySelector('.oe-col-buy'),\n sell: this.rootEl.querySelector('.oe-col-sell'),\n };\n this._orderType = OrderEntryTypes.Market;\n this._instrument = null;\n this._lastPrice = 0;\n this._bestBid = 0;\n this._bestAsk = 0;\n this._maxQuantity = { buy: null, sell: null };\n this._focused = null;\n this._enabled = true;\n\n this._closeBtn?.addEventListener('click', (e) => {\n e.preventDefault();\n this._host.close();\n });\n\n this._bindTypeTabs();\n for (const side of OrderEntryWidget.SIDES) this._bindColumn(side);\n this.setOrderType(OrderEntryTypes.Market);\n\n this._host.register(this);\n }\n\n dispose(): void {\n this._host.unregister(this);\n try { this.rootEl.remove(); } catch { /* already detached */ }\n }\n\n /// The type the form is on. A host reads it before deciding whether a\n /// click-to-trade gesture has to switch the pad over first.\n get orderType(): OrderEntryType {\n return this._orderType;\n }\n\n /// The instrument the form is sized and priced against, or null before the\n /// host has named one. Read for its tick by a host that has to quantize a\n /// price of its own before handing it over.\n getInstrument(): InstrumentSpec | null {\n return this._instrument;\n }\n\n /// Switch the form's order type and mark the matching tab. The tab lives\n /// under this control's own root, so a host switches type by naming the\n /// type rather than by handing over a button it looked up itself.\n setOrderType(type: OrderEntryType): void {\n this._orderType = type;\n for (const button of this.rootEl.querySelectorAll('.btn-ot')) {\n const tab = button as HTMLElement;\n const on = tab.dataset.type === type;\n tab.classList.toggle('active', on);\n tab.setAttribute('aria-pressed', String(on));\n }\n this._applyTypeVisibility();\n for (const side of OrderEntryWidget.SIDES) this._recalculate(side);\n }\n\n /// Adopt the venue's size and price grid: the quantity input steps by the\n /// lot, the price inputs by the tick, and the quantity resets to the minimum\n /// lot \u2014 carrying the previous value across a switch yields nonsense like\n /// \"10 BTC\" after a stock.\n setInstrument(instrument: InstrumentSpec | null): void {\n this._instrument = instrument;\n if (!instrument) return;\n\n const lotSize = Number(instrument.lotSize) || 1;\n const tickSize = Number(instrument.tickSize) || 0.01;\n const minVolume = Number(instrument.minVolume) > 0 ? Number(instrument.minVolume) : lotSize;\n const maxVolume = instrument.maxVolume != null ? Number(instrument.maxVolume) : null;\n\n for (const side of OrderEntryWidget.SIDES) {\n const qty = this._input(side, QTY_INPUT);\n if (qty) {\n qty.step = String(lotSize);\n qty.min = String(minVolume);\n // The empty string is how an input says \"no upper bound\".\n qty.max = maxVolume != null ? String(maxVolume) : '';\n qty.value = OrderEntryWidget._formatQty(minVolume);\n }\n for (const selector of [PRICE_INPUT, STOP_INPUT, TP_INPUT, SL_INPUT]) {\n const input = this._input(side, selector);\n if (input) input.step = String(tickSize);\n }\n }\n\n for (const side of OrderEntryWidget.SIDES) this._recalculate(side);\n }\n\n /// The cash this side may still commit. Null reads as \"not known yet\",\n /// which is not the same as zero.\n setAvailable(side: OrderEntrySide, available: number | null): void {\n const value = this._cols[side]?.querySelector(AVAILABLE_VALUE);\n if (value) value.textContent = formatPrice(available);\n }\n\n /// The largest quantity this side can trade \u2014 what the percent buttons take\n /// a percentage OF. Without it they have no denominator, so they report the\n /// gap through `host.log` rather than inventing one (see `applyPercent`).\n setMaxQuantity(side: OrderEntrySide, max: number | null): void {\n this._maxQuantity[side] = max != null && isFinite(max) ? max : null;\n const value = this._cols[side]?.querySelector(MAX_VALUE);\n if (value) value.textContent = formatQty(this._maxQuantity[side]);\n }\n\n /// A reference price off the tape. Both columns follow the market while the\n /// form is untouched; the field the trader is typing in is left alone.\n setLimitPrice(price: number): void {\n this._lastPrice = price;\n this._writePrices(this._bestBid > 0 ? this._bestBid : price, this._bestAsk > 0 ? this._bestAsk : price, false);\n }\n\n /// The same reference price, force-written: an explicit host gesture\n /// (\"price this order here\") outranks whatever was typed.\n seedLimitPrice(price: number): void {\n this._lastPrice = price;\n this._writePrices(this._bestBid > 0 ? this._bestBid : price, this._bestAsk > 0 ? this._bestAsk : price, true);\n }\n\n /// Best bid and offer. The passive seed for a limit form is Buy = best bid\n /// and Sell = best ask \u2014 joining the book on each side, so the spread in the\n /// form matches the spread in the book. The BBO button next to the field is\n /// the opposite, explicit gesture: take what the other side is showing.\n setBbo(bid: number, ask: number): void {\n if (bid > 0) this._bestBid = bid;\n if (ask > 0) this._bestAsk = ask;\n this._writePrices(\n this._bestBid > 0 ? this._bestBid : this._lastPrice,\n this._bestAsk > 0 ? this._bestAsk : this._lastPrice,\n false);\n }\n\n /// Fill one column's price with the best quote for that direction: Buy takes\n /// the ask, Sell takes the bid.\n applyBbo(side: OrderEntrySide): void {\n const bbo = side === OrderEntrySides.Buy ? this._bestAsk : this._bestBid;\n if (!(bbo > 0)) return;\n this.setPrice(side, bbo);\n }\n\n /// Force one column's price and let ticks resume updating it afterwards \u2014\n /// what a click on a book level or a chart price means. Quantized to the\n /// instrument's tick, so a raw chart coordinate does not fail validation.\n setPrice(side: OrderEntrySide, price: number): void {\n const input = this._input(side, PRICE_INPUT);\n if (!input) return;\n const text = this._formatPriceForInput(price);\n if (!text) return;\n input.dataset.userEdited = '0';\n input.value = text;\n this._recalculate(side);\n }\n\n /// The same quantity in both columns \u2014 a quick-size gesture applies to\n /// whichever side the user goes on to press.\n setQuantity(quantity: number): void {\n for (const side of OrderEntryWidget.SIDES) {\n const input = this._input(side, QTY_INPUT);\n if (!input) continue;\n input.value = OrderEntryWidget._formatQty(quantity);\n this._recalculate(side);\n }\n }\n\n /// Take a percentage of this side's maximum quantity, rounded down to a\n /// whole number of lots.\n applyPercent(side: OrderEntrySide, pct: number): void {\n const max = this._maxQuantity[side];\n if (max == null) {\n this._host.log(`OrderEntryWidget: no maximum quantity for the ${side} side, so ${pct}% of it is unknown`);\n return;\n }\n const qty = this._input(side, QTY_INPUT);\n if (!qty) return;\n\n const step = Number(qty.step) || 0;\n let target = max * (pct / 100);\n if (step > 0) target = Math.floor(target / step) * step;\n qty.value = OrderEntryWidget._formatQty(target);\n this._recalculate(side);\n }\n\n /// Show or hide one column's take-profit / stop-loss block, checkbox\n /// included, so a host can set it without reaching for the input.\n toggleTpSl(side: OrderEntrySide, on: boolean): void {\n const check = this._input(side, TPSL_CHECK);\n if (check) check.checked = on;\n this._show(this._cols[side]?.querySelector('.oe-tpsl') as HTMLElement | null, on);\n }\n\n /// Mark one column's submit button as the one a host-level gesture aimed at.\n /// Null clears both.\n preselect(side: OrderEntrySide | null): void {\n for (const each of OrderEntryWidget.SIDES) {\n const button = this._cols[each]?.querySelector('.btn-oe-submit');\n button?.classList.toggle('btn-preselected', each === side);\n }\n }\n\n /// Turn submitting off while the host cannot send \u2014 a dropped socket, a\n /// portfolio still loading. The form stays readable and editable; only the\n /// two buttons stop.\n setEnabled(enabled: boolean): void {\n this._enabled = enabled;\n this.el?.classList.toggle('oe-offline', !enabled);\n for (const side of OrderEntryWidget.SIDES) this._validateSide(side);\n }\n\n /// The submit button, and whatever host gesture stands in for it. Validates\n /// first, so a form the venue would reject never reaches the account.\n submit(side: OrderEntrySide): void {\n const error = this.validate(side);\n this._validateSide(side);\n if (error || !this._enabled) return;\n this._deps.submitOrder(side, this.getValues(side));\n }\n\n /// What one column currently says.\n getValues(side: OrderEntrySide): OrderEntryValues {\n const type = this._orderType;\n const carriesLimit = type === OrderEntryTypes.Limit || type === OrderEntryTypes.StopLimit;\n const carriesStop = type === OrderEntryTypes.Stop || type === OrderEntryTypes.StopLimit;\n const tpSlOn = this._input(side, TPSL_CHECK)?.checked === true;\n\n return {\n type,\n quantity: this._number(side, QTY_INPUT) ?? 0,\n limitPrice: carriesLimit ? this._number(side, PRICE_INPUT) : null,\n stopPrice: carriesStop ? this._number(side, STOP_INPUT) : null,\n takeProfit: tpSlOn ? this._number(side, TP_INPUT) : null,\n stopLoss: tpSlOn ? this._number(side, SL_INPUT) : null,\n };\n }\n\n /// The reason this column cannot be sent, worded by the host, or null when\n /// it can. Nothing is checked before the instrument is known \u2014 the venue's\n /// grid is what every rule below measures against.\n validate(side: OrderEntrySide): string | null {\n const instrument = this._instrument;\n if (!instrument) return null;\n\n const { quantity, limitPrice, stopPrice, type } = this.getValues(side);\n\n if (quantity <= 0) return this._host.t('Quantity must be positive');\n\n const lotSize = Number(instrument.lotSize) || 0;\n if (lotSize > 0 && !OrderEntryWidget._isMultipleOf(quantity, lotSize))\n return this._host.t('Quantity must be a multiple of {0}', lotSize);\n\n const minVolume = Number(instrument.minVolume) > 0 ? Number(instrument.minVolume) : lotSize;\n if (minVolume > 0 && quantity < minVolume) return this._host.t('Quantity must be >= {0}', minVolume);\n\n if (instrument.maxVolume != null && quantity > Number(instrument.maxVolume))\n return this._host.t('Quantity must be <= {0}', instrument.maxVolume);\n\n const tickSize = Number(instrument.tickSize) || 0;\n\n if (type === OrderEntryTypes.Limit || type === OrderEntryTypes.StopLimit) {\n if (!limitPrice || limitPrice <= 0) return this._host.t('Limit price must be positive');\n if (tickSize > 0 && !OrderEntryWidget._isMultipleOf(limitPrice, tickSize))\n return this._host.t('Limit price must be a multiple of {0}', tickSize);\n }\n\n if (type === OrderEntryTypes.Stop || type === OrderEntryTypes.StopLimit) {\n if (!stopPrice || stopPrice <= 0) return this._host.t('Stop price must be positive');\n if (tickSize > 0 && !OrderEntryWidget._isMultipleOf(stopPrice, tickSize))\n return this._host.t('Stop price must be a multiple of {0}', tickSize);\n }\n\n return null;\n }\n\n /// Map a form type onto StockSharp's `OrderTypes`: Limit=0, Market=1,\n /// Conditional=2. Static because a host needs it to word the order it is\n /// about to send, which it can do before any instance exists.\n static toApiType(uiType: OrderEntryType | string): number {\n switch (uiType) {\n case OrderEntryTypes.Limit: return 0;\n case OrderEntryTypes.Market: return 1;\n case OrderEntryTypes.Stop:\n case OrderEntryTypes.StopLimit: return 2;\n default: return 1;\n }\n }\n\n /// Is `value` a whole number of `step`s? Scaled to integers first: a crypto\n /// lot of 1e-9 against a quantity of 0.001 is not a modulo binary floats can\n /// answer directly.\n static _isMultipleOf(value: number, step: number): boolean {\n if (!(step > 0)) return true;\n if (value === 0) return true;\n const maxDec = Math.max(OrderEntryWidget._decimals(step), OrderEntryWidget._decimals(value));\n const scale = Math.pow(10, maxDec);\n const scaledValue = Math.round(value * scale);\n const scaledStep = Math.round(step * scale);\n if (scaledStep <= 0) return true;\n if (scaledValue === 0) return false;\n return scaledValue % scaledStep === 0;\n }\n\n /// A quantity as an input's `value`: every significant decimal, no thousands\n /// separator and no exponent, because the browser reads this string back as\n /// a number.\n static _formatQty(n: number): string {\n if (!isFinite(n) || n === 0) return '0';\n const d = OrderEntryWidget._decimals(n);\n return n.toFixed(Math.min(d, 20));\n }\n\n /// How many decimals a number is written with, exponent form included.\n static _decimals(n: number): number {\n if (!isFinite(n) || n === 0) return 0;\n const s = Math.abs(n).toString();\n const eIdx = s.indexOf('e-');\n if (eIdx >= 0) {\n const mantissaDec = s.slice(0, eIdx).split('.')[1]?.length ?? 0;\n return parseInt(s.slice(eIdx + 2), 10) + mantissaDec;\n }\n const dot = s.indexOf('.');\n return dot < 0 ? 0 : s.length - dot - 1;\n }\n\n _bindTypeTabs(): void {\n for (const button of this.rootEl.querySelectorAll('.btn-ot')) {\n const tab = button as HTMLElement;\n tab.addEventListener('click', (e) => {\n e.preventDefault();\n const type = tab.dataset.type as OrderEntryType | undefined;\n if (type) this.setOrderType(type);\n });\n }\n }\n\n // Everything one column listens to. Scoped to the column element, which is\n // what lets two pads coexist: an input is found by the field it belongs to\n // under its own root, never by a page-wide id.\n _bindColumn(side: OrderEntrySide): void {\n const column = this._cols[side];\n if (!column) return;\n\n const qty = this._input(side, QTY_INPUT);\n if (qty) {\n qty.value = '1';\n qty.min = '1';\n qty.step = '1';\n }\n\n for (const selector of VALUE_INPUTS) {\n const input = this._input(side, selector);\n if (!input) continue;\n input.addEventListener('input', () => this._recalculate(side));\n input.addEventListener('focus', () => { this._focused = input; });\n input.addEventListener('blur', () => { if (this._focused === input) this._focused = null; });\n // Mark a price the trader typed, so an incoming tick stops\n // overwriting it mid-keystroke.\n if (selector === PRICE_INPUT || selector === STOP_INPUT)\n input.addEventListener('input', () => { input.dataset.userEdited = '1'; });\n }\n\n column.querySelector('.btn-bbo')?.addEventListener('click', (e) => {\n e.preventDefault();\n this.applyBbo(side);\n });\n\n for (const button of column.querySelectorAll('.btn-oe-pct')) {\n const pct = button as HTMLElement;\n pct.addEventListener('click', (e) => {\n e.preventDefault();\n this.applyPercent(side, Number(pct.dataset.pct) || 0);\n });\n }\n\n const check = this._input(side, TPSL_CHECK);\n check?.addEventListener('change', () => this.toggleTpSl(side, check.checked === true));\n\n column.querySelector('.btn-oe-submit')?.addEventListener('click', (e) => {\n e.preventDefault();\n this.submit(side);\n });\n }\n\n // Which price fields the current type makes meaningful. A class toggle, not\n // a display style: the pad emits names and the stylesheet decides.\n _applyTypeVisibility(): void {\n const showLimit = this._orderType === OrderEntryTypes.Limit || this._orderType === OrderEntryTypes.StopLimit;\n const showStop = this._orderType === OrderEntryTypes.Stop || this._orderType === OrderEntryTypes.StopLimit;\n for (const side of OrderEntryWidget.SIDES) {\n this._show(this._cols[side]?.querySelector('.oe-field-price') as HTMLElement | null, showLimit);\n this._show(this._cols[side]?.querySelector('.oe-field-stop') as HTMLElement | null, showStop);\n }\n }\n\n _show(element: HTMLElement | null, visible: boolean): void {\n element?.classList.toggle('oe-field-hidden', !visible);\n }\n\n // Write both price inputs. `reset` is the explicit gesture: it clears the\n // \"typed in\" mark so the field follows the market again. Without it, a field\n // the user is in or has already edited is left alone.\n _writePrices(buyPrice: number, sellPrice: number, reset: boolean): void {\n this._writePrice(OrderEntrySides.Buy, buyPrice, reset);\n this._writePrice(OrderEntrySides.Sell, sellPrice, reset);\n for (const side of OrderEntryWidget.SIDES) this._recalculate(side);\n }\n\n _writePrice(side: OrderEntrySide, price: number, reset: boolean): void {\n const input = this._input(side, PRICE_INPUT);\n if (!input) return;\n const text = this._formatPriceForInput(price);\n if (!text) return;\n if (reset) {\n input.dataset.userEdited = '0';\n input.value = text;\n return;\n }\n if (this._focused === input) return;\n if (input.dataset.userEdited === '1') return;\n input.value = text;\n }\n\n // Round a price to the instrument's tick and cut the float tail: a mid price\n // of (bid+ask)/2 otherwise reaches the field as 0.44625000000000004.\n _formatPriceForInput(price: number): string {\n const n = Number(price);\n if (!isFinite(n)) return '';\n const tickSize = Number(this._instrument?.tickSize) || 0;\n if (tickSize > 0)\n return (Math.round(n / tickSize) * tickSize).toFixed(OrderEntryWidget._decimals(tickSize));\n return String(parseFloat(n.toPrecision(12)));\n }\n\n _recalculate(side: OrderEntrySide): void {\n this._updateEstimate(side);\n this._validateSide(side);\n }\n\n // What the order is worth at the price it would most likely fill at: the\n // limit or stop the form names, else the side-appropriate BBO, else the last\n // print.\n _updateEstimate(side: OrderEntrySide): void {\n const total = this._cols[side]?.querySelector(TOTAL_VALUE);\n if (!total) return;\n\n const quantity = this._number(side, QTY_INPUT) ?? 0;\n let price: number;\n if (this._orderType === OrderEntryTypes.Limit || this._orderType === OrderEntryTypes.StopLimit) {\n price = this._number(side, PRICE_INPUT) ?? 0;\n } else if (this._orderType === OrderEntryTypes.Stop) {\n price = this._number(side, STOP_INPUT) ?? this._lastPrice;\n } else {\n const bbo = side === OrderEntrySides.Buy ? this._bestAsk : this._bestBid;\n price = bbo > 0 ? bbo : this._lastPrice;\n }\n\n total.textContent = quantity > 0 && price > 0\n ? (quantity * price).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })\n : '--';\n }\n\n // The estimate line doubles as the validation message and the submit button\n // follows it. A disabled pad reads as invalid for the same reason: there is\n // nothing the button could do.\n _validateSide(side: OrderEntrySide): void {\n const error = this.validate(side);\n const estimate = this._cols[side]?.querySelector('.oe-estimate');\n const submit = this._cols[side]?.querySelector('.btn-oe-submit') as HTMLButtonElement | null;\n\n if (estimate) {\n estimate.textContent = error || '';\n estimate.classList.toggle('oe-estimate-error', !!error);\n }\n if (submit) submit.disabled = !!error || !this._enabled;\n }\n\n _input(side: OrderEntrySide, selector: string): HTMLInputElement | null {\n return (this._cols[side]?.querySelector(selector) as HTMLInputElement | null) ?? null;\n }\n\n // An input's value as a number, or null when it is blank or unparseable \u2014\n // which is what \"the user has not said\" looks like on a number field.\n _number(side: OrderEntrySide, selector: string): number | null {\n const parsed = parseFloat(this._input(side, selector)?.value ?? '');\n return isFinite(parsed) ? parsed : null;\n }\n}\n", "// The geometry of the order book's depth chart, as pure functions over\n// numbers.\n//\n// The chart is a classic market-depth curve: price runs along X with the mid\n// line in the middle, bids fanning left and asks right, and Y is the cumulative\n// quantity from the best price on that side out to the price under the cursor.\n// It is a step curve because a book is discrete \u2014 a horizontal run between two\n// prices at the same accumulation, then a vertical jump where the next level\n// adds its size.\n//\n// None of that needs a canvas, and keeping it here is what makes it testable:\n// a fake 2D context can only record the calls a control made, so the numbers\n// those calls carry are checked here instead, against arithmetic rather than\n// against pixels.\nimport type { BookLevel } from './trading-data.js';\n\n/// The box one paint fills, in device pixels.\nexport interface DepthGeometry {\n /// X of the mid line, which both sides start from.\n midX: number;\n /// Gap left at the mid line and at the outer edge, so the curve does not\n /// touch either.\n pad: number;\n /// Distance from the mid line to the outer edge \u2014 the width one side has.\n halfWidth: number;\n /// Y of the zero-volume baseline.\n baseY: number;\n /// Height the largest cumulative quantity rises to.\n innerHeight: number;\n}\n\n/// One accumulation point: the pixel a level sits at and the running quantity\n/// up to and including it.\nexport interface DepthPoint {\n x: number;\n cumulative: number;\n}\n\n/// One side of the chart, ready to be stroked.\nexport interface DepthSide {\n points: DepthPoint[];\n /// Cumulative quantity of the whole side \u2014 what the two sides are scaled\n /// against, so the taller one reaches the top and the other keeps its\n /// proportion.\n total: number;\n}\n\n/// Accumulate one side of the book into pixel space.\n///\n/// `direction` is -1 for the side drawn left of the mid line and +1 for the\n/// right. Each side is scaled by its OWN price span, so both fill their half\n/// even when one is quoted far wider than the other \u2014 the chart is about the\n/// shape of the liquidity, not about the two spans being comparable.\n///\n/// Null when there is nothing to draw: no levels, or every level at one price,\n/// which has no span to spread over.\nexport function depthSide(levels: BookLevel[], direction: 1 | -1, geometry: DepthGeometry): DepthSide | null {\n if (levels.length === 0) return null;\n\n const best = levels[0].price;\n const worst = levels[levels.length - 1].price;\n const span = Math.abs(best - worst);\n if (!(span > 0)) return null;\n\n const reach = geometry.halfWidth - geometry.pad;\n const points: DepthPoint[] = [{ x: geometry.midX + direction * geometry.pad, cumulative: 0 }];\n let cumulative = 0;\n for (const level of levels) {\n cumulative += level.quantity;\n const distance = Math.abs(level.price - best);\n points.push({\n x: geometry.midX + direction * (geometry.pad + (distance / span) * reach),\n cumulative,\n });\n }\n return { points, total: cumulative };\n}\n\n/// Turn one side into the step polyline that is both stroked and filled.\n///\n/// Two points per level: the horizontal run arrives at the new price still at\n/// the previous accumulation, then the vertical jump adds the level's size. The\n/// first point sits on the baseline so the filled area closes against it.\n///\n/// `maxTotal` is the scale both sides share \u2014 pass the larger of the two totals\n/// so the halves stay comparable.\nexport function depthPolyline(side: DepthSide, maxTotal: number, geometry: DepthGeometry): [number, number][] {\n const scale = maxTotal > 0 ? maxTotal : 1;\n const yOf = (cumulative: number) => geometry.baseY - (cumulative / scale) * geometry.innerHeight;\n\n const line: [number, number][] = [[side.points[0].x, geometry.baseY]];\n for (let i = 1; i < side.points.length; i++) {\n const previous = side.points[i - 1];\n const current = side.points[i];\n line.push([current.x, yOf(previous.cumulative)]);\n line.push([current.x, yOf(current.cumulative)]);\n }\n return line;\n}\n", "// Order book \u2014 multi-instance.\n//\n// Builds its own DOM (see `_buildRoot`) rather than cloning a <template> out of\n// the page, so it can be constructed by any host that supplies a `TradingHost`.\n// Every instance keeps its own symbol, depth, row layout and side order, and a\n// page may hold as many ladders as it has room for.\n//\n// The ladder is not a table and so not a `DataGrid`: a level is a row of three\n// figures behind a proportional volume bar and a heat tint, both of which are\n// measured per level. The measurements are handed to the stylesheet as custom\n// properties (`--t-ob-bar`, `--t-ob-heat`, `--t-ob-sent`) so the widths and the\n// colours stay in CSS, where every other look in this package lives. The one\n// exception is the depth chart above the ladder: it is drawn on a canvas, which\n// no class name can reach, so its colours come from\n// `host.presentation.canvasPalette()` and its geometry from `orderbook-depth.ts`.\n//\n// Where the data comes from: the host pushes frames in through `applyFrame`,\n// one per server frame, fanned to every live instance \u2014 which is why each\n// instance filters by its own symbol, and why `host.register(this)` at the end\n// of the constructor is what makes a ladder update at all. The first frame of a\n// (re)subscription is a snapshot and replaces the ledger; the rest are diffs\n// (`quantity: 0` deletes a level) carrying a per-symbol sequence. A gap in that\n// sequence means a frame was missed and the diffs after it cannot be trusted,\n// so the ladder asks the market-data client to resend the symbol from scratch\n// rather than drifting. Own resting orders come from\n// `marketData.getOrders()` \u2014 the badge is an overlay on the levels, not a\n// second subscription.\nimport { formatPrice, formatQty } from './formatters.js';\nimport { makeElement, makeIconButton, makePanelId, makePanelRoot } from './dom.js';\nimport { ControlTypes } from './control-types.js';\nimport { MarketDataLevels, TradingHost, assertHost } from './trading-host.js';\nimport type { BookLevel, OrderBookFrame, QuoteLevel } from './trading-data.js';\nimport { depthPolyline, depthSide, type DepthGeometry } from './orderbook-depth.js';\n\n/// What the ladder needs beyond the host port. All required: a click on a price\n/// level is this control's whole reason to exist, and the two measurements are\n/// the page's to answer \u2014 a control that read them off `window` itself could\n/// only ever run in a browser tab.\nexport interface OrderBookDeps {\n host: TradingHost;\n /// Plain click on a level \u2014 prefill an order at this price, do not send it.\n onPriceSelected(price: number, side: number): void;\n /// Ctrl/Cmd click on a level \u2014 send at this price now.\n onPriceExecuted(price: number, side: number): void;\n /// The most levels per side this host has room for, read per depth change.\n /// A phone-sized page answers 5: ten levels a side overflow the panel and\n /// leave the user with two inner scrollbars to dance around. A host with\n /// room answers the largest depth the ladder offers.\n maxDepth(): number;\n /// Backing-store scale for the depth chart's canvas \u2014 `devicePixelRatio` in\n /// a browser. Read per paint, so a window dragged onto a second monitor\n /// repaints sharp.\n pixelRatio(): number;\n}\n\n/// The row layouts the ladder offers.\n///\n/// `stacked` is Price | Qty | Total on every row, the layout every major\n/// exchange ladder uses. `diagonal` centres the price and puts the size cell on\n/// the outside of each side \u2014 the classic two-sided book.\nexport type OrderBookView = 'diagonal' | 'stacked';\n\n// StockSharp's OrderStates.Active. Only a resting order sits on the book, so\n// only a resting order earns the \"your size is here\" badge. The full enum is\n// declared once in `active-orders-widget.ts`; importing it here would pull that\n// blotter \u2014 and the grid it renders with \u2014 into every bundle that wanted a\n// ladder and nothing else.\nconst RESTING_STATE = 3;\n\n// Heat tint per level, as the percentage of the direction colour mixed into the\n// row background. Faint at the shallowest level and still faint at the largest,\n// because the volume bar behind the row is what carries the size \u2014 the tint\n// only separates a wall from its neighbours.\nconst HEAT_FLOOR_PCT = 6;\nconst HEAT_RANGE_PCT = 18;\n\nexport class OrderBookWidget {\n static TYPE = ControlTypes.OrderBook;\n\n /// The depths the ladder offers, in the order the header shows them.\n static DEPTHS: readonly number[] = [5, 10];\n\n /// Persisted depth for the instance that follows the host's active symbol.\n /// Pinned instances carry their depth in their own per-panel state instead,\n /// which is what makes two ladders on one page able to differ.\n static DEPTH_KEY = 'terminal.obDepth';\n\n /// Persisted row layout for the follows-active instance.\n static VIEW_KEY = 'terminal.obView';\n\n /// Persisted \"bids above the mid line\" flag for the follows-active instance.\n static INVERT_KEY = 'terminal.obInvert';\n\n /// Persisted depth-chart visibility for the follows-active instance.\n static DEPTHCHART_KEY = 'terminal.obDepthChart';\n\n rootEl: HTMLElement;\n asksEl: HTMLElement | null;\n bidsEl: HTMLElement | null;\n midEl: HTMLElement | null;\n spreadEl: HTMLElement | null;\n contentEl: HTMLElement | null;\n depthChartEl: HTMLCanvasElement | null;\n // Comment style is `//` on purpose below the public members: the declaration\n // emitter keeps a private member's doc comment while dropping its body, so a\n // `///` here would reattach to the next public member in the API snapshot.\n _host: TradingHost;\n _deps: OrderBookDeps;\n _midSpreadEl: HTMLElement | null;\n _sentimentEl: HTMLElement | null;\n _sentBidPctEl: HTMLElement | null;\n _sentAskPctEl: HTMLElement | null;\n _symbolLabel: HTMLElement | null;\n _depthBtns: HTMLElement[];\n _viewBtns: HTMLElement[];\n _invertBtn: HTMLElement | null;\n _depthToggleBtn: HTMLElement | null;\n _addBtn: HTMLElement | null;\n _closeBtn: HTMLElement | null;\n // Last painted quantity per level, keyed by side and price \u2014 what decides\n // whether a level flashes and in which direction.\n _prevQuantities: Map<string, number>;\n _depth: number;\n _view: OrderBookView;\n _invertSides: boolean;\n _showDepthChart: boolean;\n _bidsByPrice: Map<number, number>;\n _asksByPrice: Map<number, number>;\n _lastSequence: number;\n _currentSymbol: string | null;\n _followsActive: boolean;\n\n static create(hostEl: HTMLElement, state: Record<string, unknown>, deps: OrderBookDeps): OrderBookWidget {\n // Assert before building: the markup below is localized through the\n // host, so a missing host has to fail here rather than render a panel\n // captioned with raw English keys.\n const host = assertHost(deps?.host, 'OrderBookWidget');\n const root = OrderBookWidget._buildRoot(host);\n root.id = makePanelId(OrderBookWidget.TYPE);\n hostEl.appendChild(root);\n return new OrderBookWidget(root, state || {}, deps);\n }\n\n // The panel's markup. The host stylesheet reads this structure, and a\n // docking host lifts `.panel-header`'s children into its tab strip \u2014 which\n // is why the header holds every control the ladder has.\n //\n // The `+` is built here rather than left out: the port documents `spawn` as\n // this control's gesture, and a button nothing renders is a capability\n // nobody can reach.\n static _buildRoot(host: TradingHost): HTMLElement {\n const title = host.t('OrderBook');\n return makePanelRoot('orderbook-panel', title, [\n makeElement('div', 'panel-header', {}, [\n makeElement('span', 'ob-symbol-label',\n { title: host.t('ClickToChangeSymbol'), role: 'button', tabindex: '0' }, ['--']),\n makeElement('div', 'ob-depth-selector', { role: 'group', 'aria-label': host.t('OrderBookDepth') },\n OrderBookWidget.DEPTHS.map(depth => makeElement('button', 'btn-ob-depth', {\n type: 'button',\n 'data-depth': String(depth),\n 'aria-label': host.t('LevelsCount', depth),\n 'aria-pressed': 'false',\n }, [String(depth)]))),\n makeElement('div', 'ob-view-selector', { role: 'group', 'aria-label': host.t('OrderBookView') }, [\n makeIconButton('btn-ob-view', host.t('OrderBookViewDiagonal'), 'bi-distribute-horizontal',\n { type: 'button', 'data-view': 'diagonal', 'aria-pressed': 'false' }),\n makeIconButton('btn-ob-view', host.t('OrderBookViewStacked'), 'bi-table',\n { type: 'button', 'data-view': 'stacked', 'aria-pressed': 'false' }),\n ]),\n makeIconButton('btn-ob-invert', host.t('OrderBookInvertSides'), 'bi-arrow-down-up',\n { type: 'button', 'aria-pressed': 'false' }),\n makeIconButton('btn-ob-depthtoggle', host.t('ToggleDepthChart'), 'bi-graph-up',\n { type: 'button', 'aria-pressed': 'false' }),\n makeElement('span', 'spread-label', { 'aria-live': 'polite' }, ['--']),\n makeIconButton('bt-icon-btn ob-add-btn', host.t('AddOrderbook'), 'bi-plus-lg', { type: 'button' }),\n makeIconButton('bt-icon-btn bt-icon-cancel ob-close-btn', host.t('RemoveOrderbook'), 'bi-x', { type: 'button' }),\n ]),\n makeElement('canvas', 'ob-depth-chart', { 'aria-hidden': 'true' }, []),\n makeElement('div', 'ob-col-headers', { 'aria-hidden': 'true' }, [\n makeElement('span', 'ob-col-price', {}, [host.t('Price')]),\n makeElement('span', 'ob-col-qty', {}, [host.t('Qty')]),\n makeElement('span', 'ob-col-total', {}, [host.t('Total')]),\n ]),\n makeElement('div', 'orderbook-content', { 'aria-live': 'polite', 'aria-atomic': 'false' }, [\n makeElement('div', 'orderbook-asks', { 'aria-label': host.t('AskOrders') }, []),\n makeElement('div', 'orderbook-mid', { 'aria-label': host.t('MidPrice') }, [\n makeElement('span', 'ob-mid-price', {}, ['--']),\n makeElement('span', 'ob-mid-spread', { 'aria-live': 'polite' }, []),\n ]),\n makeElement('div', 'orderbook-bids', { 'aria-label': host.t('BidOrders') }, []),\n ]),\n makeElement('div', 'ob-sentiment', { 'aria-label': host.t('OrderBookSentiment') }, [\n makeElement('div', 'ob-sent-bid', {}, [\n makeElement('span', 'ob-sent-label', {}, [host.t('BidShort')]),\n makeElement('span', 'ob-sent-bid-pct', {}, ['--']),\n ]),\n makeElement('div', 'ob-sent-ask', {}, [\n makeElement('span', 'ob-sent-ask-pct', {}, ['--']),\n makeElement('span', 'ob-sent-label', {}, [host.t('AskShort')]),\n ]),\n ]),\n ]);\n }\n\n constructor(rootEl: HTMLElement, state: Record<string, unknown>, deps: OrderBookDeps) {\n this._host = assertHost(deps?.host, 'OrderBookWidget');\n for (const name of ['onPriceSelected', 'onPriceExecuted', 'maxDepth', 'pixelRatio'] as const) {\n if (typeof deps?.[name] !== 'function')\n throw new Error(`OrderBookWidget: dep \"${name}\" is required`);\n }\n\n this.rootEl = rootEl;\n this._deps = deps;\n\n this.asksEl = this.rootEl.querySelector('.orderbook-asks');\n this.bidsEl = this.rootEl.querySelector('.orderbook-bids');\n this.midEl = this.rootEl.querySelector('.ob-mid-price');\n this.spreadEl = this.rootEl.querySelector('.spread-label');\n this.contentEl = this.rootEl.querySelector('.orderbook-content');\n this.depthChartEl = this.rootEl.querySelector('.ob-depth-chart');\n this._midSpreadEl = this.rootEl.querySelector('.ob-mid-spread');\n this._sentimentEl = this.rootEl.querySelector('.ob-sentiment');\n this._sentBidPctEl = this.rootEl.querySelector('.ob-sent-bid-pct');\n this._sentAskPctEl = this.rootEl.querySelector('.ob-sent-ask-pct');\n this._symbolLabel = this.rootEl.querySelector('.ob-symbol-label');\n this._depthBtns = Array.from(this.rootEl.querySelectorAll('.btn-ob-depth')) as HTMLElement[];\n this._viewBtns = Array.from(this.rootEl.querySelectorAll('.btn-ob-view')) as HTMLElement[];\n this._invertBtn = this.rootEl.querySelector('.btn-ob-invert');\n this._depthToggleBtn = this.rootEl.querySelector('.btn-ob-depthtoggle');\n this._addBtn = this.rootEl.querySelector('.ob-add-btn');\n this._closeBtn = this.rootEl.querySelector('.ob-close-btn');\n\n // Per-panel state is whatever the host persisted, so every value is read\n // defensively and falls back to this control's own default.\n const saved = state as {\n symbol?: string; depth?: number; view?: string;\n invertSides?: boolean; showDepthChart?: boolean; followsActive?: boolean;\n };\n\n this._prevQuantities = new Map();\n this._bidsByPrice = new Map();\n this._asksByPrice = new Map();\n this._lastSequence = 0;\n this._currentSymbol = null;\n this._followsActive = saved.followsActive === true;\n this._view = saved.view === 'diagonal' ? 'diagonal' : 'stacked';\n this._invertSides = saved.invertSides === true;\n // On by default; only an explicit false hides it.\n this._showDepthChart = saved.showDepthChart !== false;\n let depth = OrderBookWidget._isDepth(saved.depth) ? saved.depth : OrderBookWidget._deepest();\n\n // The follows-active instance restores the settings the user chose the\n // last time, which live in the host's preferences rather than in this\n // panel's state \u2014 there is one of it per page and it outlives any\n // particular layout. Pinned instances carry theirs in `state` above.\n if (this._followsActive) {\n const preferences = this._host.preferences;\n const savedDepth = Number(preferences.get(OrderBookWidget.DEPTH_KEY, null));\n if (OrderBookWidget._isDepth(savedDepth)) depth = savedDepth;\n const savedView = preferences.get(OrderBookWidget.VIEW_KEY, null);\n if (savedView === 'diagonal' || savedView === 'stacked') this._view = savedView;\n const savedInvert = preferences.get(OrderBookWidget.INVERT_KEY, null);\n if (savedInvert !== null) this._invertSides = savedInvert === 'true';\n const savedDepthChart = preferences.get(OrderBookWidget.DEPTHCHART_KEY, null);\n if (savedDepthChart !== null) this._showDepthChart = savedDepthChart === 'true';\n }\n this._depth = this._fitDepth(depth);\n\n this._wireHeader();\n this._wireBookClicks();\n\n this._refreshDepthButtons();\n this._applyViewState();\n this._refreshViewButtons();\n this._refreshInvertButton();\n this._refreshDepthToggleButton();\n\n // A follows-active instance ignores any persisted symbol: it inherits\n // the live one from the host on boot, and honouring a stale one here\n // would flash a previous session's instrument until that arrives.\n if (saved.symbol && !this._followsActive) this.setSymbol(saved.symbol);\n\n // Self-register \u2014 this is how incoming frames find their way here, and\n // an unregistered ladder is one that never updates.\n this._host.register(this);\n }\n\n /// Release the market-data subscription, leave the fan-out and remove the\n /// panel. Called by the host when the panel is destroyed.\n dispose(): void {\n if (this._currentSymbol)\n void this._host.trading.marketData.removeSymbol(this._currentSymbol).catch(() => { });\n this._host.unregister(this);\n try { this.rootEl.remove(); } catch { /* already detached */ }\n }\n\n /// Switch to a new instrument: drop the old subscription, take a new one and\n /// blank the ledger, because a diff for the old symbol must never be applied\n /// to the new one.\n setSymbol(symbol: string): void {\n if (!symbol) return;\n const next = String(symbol).toUpperCase();\n if (next === this._currentSymbol) return;\n\n const previous = this._currentSymbol;\n this._currentSymbol = next;\n this._resetLedger();\n if (this._symbolLabel) this._symbolLabel.textContent = next;\n this._render();\n\n // addSymbol / removeSymbol are refcounted by the client, so two ladders\n // on one symbol hold one subscription and neither can drop the other's.\n const marketData = this._host.trading.marketData;\n if (previous) void marketData.removeSymbol(previous).catch(() => { });\n // A ladder needs the depth, and the header reads best bid/ask, so it\n // asks for the whole set rather than for quotes alone.\n void marketData.addSymbol(next, MarketDataLevels.Full).catch(() => { });\n\n // A pinned instance remembers its instrument so a reload restores this\n // exact ladder; the follows-active one deliberately does not, because it\n // tracks whatever the host is showing.\n if (!this._followsActive) this._persistState({ symbol: next });\n }\n\n getSymbol(): string | null { return this._currentSymbol; }\n\n getDepth(): number { return this._depth; }\n\n /// True for the instance that follows the host's active symbol. A page has\n /// at most one, and a host that feeds a best-bid/ask from a ladder asks for\n /// it by this.\n isFollowsActive(): boolean { return this._followsActive; }\n\n /// The ledger as levels, best price first. Public because a host reads the\n /// touch off the ladder rather than keeping a second copy of the book.\n getBids(): BookLevel[] {\n return OrderBookWidget._sorted(this._bidsByPrice, -1);\n }\n\n getAsks(): BookLevel[] {\n return OrderBookWidget._sorted(this._asksByPrice, 1);\n }\n\n // The three setters below repaint BEFORE they persist: `_persistState` ends\n // in a layout flush and the host serializes the live panel to produce it, so\n // persisting first would write out the panel as it looked before the change\n // the user just made.\n setDepth(depth: number): void {\n if (!OrderBookWidget._isDepth(depth)) return;\n this._depth = this._fitDepth(depth);\n this._refreshDepthButtons();\n this._render();\n if (this._followsActive) this._host.preferences.set(OrderBookWidget.DEPTH_KEY, String(this._depth));\n else this._persistState({ depth: this._depth });\n }\n\n setView(view: OrderBookView): void {\n if (view !== 'diagonal' && view !== 'stacked') return;\n this._view = view;\n this._applyViewState();\n this._refreshViewButtons();\n if (this._followsActive) this._host.preferences.set(OrderBookWidget.VIEW_KEY, view);\n else this._persistState({ view });\n }\n\n /// Put the bid block above the mid line and the ask block below it, the\n /// reverse of the convention every Western exchange UI follows.\n setInvertSides(invert: boolean): void {\n this._invertSides = invert === true;\n this._applyViewState();\n this._refreshInvertButton();\n if (this._followsActive)\n this._host.preferences.set(OrderBookWidget.INVERT_KEY, this._invertSides ? 'true' : 'false');\n else this._persistState({ invertSides: this._invertSides });\n }\n\n /// Show or hide the depth chart above the ladder. The canvas keeps its last\n /// bitmap while hidden, so showing it again brings the chart straight back\n /// and the next frame repaints it.\n setShowDepthChart(show: boolean): void {\n this._showDepthChart = show === true;\n this._applyViewState();\n this._refreshDepthToggleButton();\n if (this._followsActive)\n this._host.preferences.set(OrderBookWidget.DEPTHCHART_KEY, this._showDepthChart ? 'true' : 'false');\n this._persistState({ showDepthChart: this._showDepthChart });\n }\n\n /// Apply one frame. A snapshot replaces the ledger; a diff merges into it,\n /// with `quantity: 0` deleting a level. A break in the sequence means a\n /// frame was missed, so the ladder asks for a fresh snapshot instead of\n /// applying a diff to state it can no longer trust.\n ///\n /// Every instance receives every frame, so the symbol filter here is what\n /// keeps a frame for one instrument out of a ladder watching another.\n applyFrame(frame: OrderBookFrame): void {\n if (!frame || !frame.symbol) return;\n if (this._currentSymbol && frame.symbol !== this._currentSymbol) return;\n if (this._currentSymbol !== frame.symbol) {\n this._currentSymbol = frame.symbol;\n // The follows-active instance can receive its first frame before the\n // host calls setSymbol, whose early return on an unchanged symbol\n // would then skip the label.\n if (this._symbolLabel) this._symbolLabel.textContent = frame.symbol;\n }\n\n const sequence = frame.sequence ?? 0;\n if (frame.isSnapshot) {\n this._resetLedger();\n this._applySnapshotSide(this._bidsByPrice, frame.bids, 'bid', frame.symbol, sequence);\n this._applySnapshotSide(this._asksByPrice, frame.asks, 'ask', frame.symbol, sequence);\n this._lastSequence = sequence;\n } else {\n if (this._lastSequence !== 0 && sequence !== this._lastSequence + 1) {\n this._anomaly(`sequence gap: expected ${this._lastSequence + 1}, got ${sequence} sym=${frame.symbol} \u2014 requesting snapshot`);\n this._lastSequence = 0;\n void this._requestResubscribe(frame.symbol);\n return;\n }\n this._applyDiffSide(this._bidsByPrice, frame.bids, 'bid', frame.symbol, sequence);\n this._applyDiffSide(this._asksByPrice, frame.asks, 'ask', frame.symbol, sequence);\n this._lastSequence = sequence || this._lastSequence;\n }\n this._validateBookCross(frame.symbol, sequence, frame.isSnapshot === true);\n this._render();\n }\n\n _wireHeader(): void {\n for (const button of this._depthBtns) {\n button.addEventListener('click', (e) => {\n e.preventDefault();\n this.setDepth(Number(button.dataset.depth));\n });\n }\n for (const button of this._viewBtns) {\n button.addEventListener('click', (e) => {\n e.preventDefault();\n const view = button.dataset.view;\n if (view === 'diagonal' || view === 'stacked') this.setView(view);\n });\n }\n this._invertBtn?.addEventListener('click', (e) => {\n e.preventDefault();\n this.setInvertSides(!this._invertSides);\n });\n this._depthToggleBtn?.addEventListener('click', (e) => {\n e.preventDefault();\n this.setShowDepthChart(!this._showDepthChart);\n });\n\n // Another ladder, starting where this one is \u2014 the most natural copy\n // gesture, and the host decides where it goes.\n this._addBtn?.addEventListener('click', (e) => {\n e.preventDefault();\n e.stopPropagation();\n this._host.spawn({\n symbol: this._currentSymbol,\n depth: this._depth,\n view: this._view,\n invertSides: this._invertSides,\n showDepthChart: this._showDepthChart,\n followsActive: false,\n });\n });\n\n this._closeBtn?.addEventListener('click', (e) => {\n e.preventDefault();\n e.stopPropagation();\n this._host.close();\n });\n\n // Picking an instrument retargets THIS ladder only \u2014 a user who wanted\n // the whole page to follow would have used the host's own instrument\n // selector. Picking on the follows-active instance detaches it: it\n // becomes a pinned ladder on the chosen symbol, identical to any other.\n this._symbolLabel?.addEventListener('click', (e) => {\n e.preventDefault();\n e.stopPropagation();\n this._host.trading.pickInstrument((symbol) => {\n if (this._followsActive) {\n this._followsActive = false;\n this._persistState({ followsActive: false });\n }\n this.setSymbol(symbol);\n });\n });\n }\n\n // A click anywhere on a level, caught once on the container: the rows are\n // rebuilt on every frame, so a listener per row would be re-bound dozens of\n // times a second.\n //\n // Plain click prefills an order at that price and leaves the sending to the\n // user. Ctrl/Cmd click sends at once \u2014 a single click doing that is how a\n // sneeze on the bid becomes a fill.\n _wireBookClicks(): void {\n this.contentEl?.addEventListener('click', (e) => {\n const target = e.target as Element | null;\n const row = target?.closest('.ob-row') as HTMLElement | null;\n if (!row) return;\n const price = Number(row.dataset.price);\n if (!Number.isFinite(price)) return;\n\n // A bid row is where the user sells and an ask row is where they\n // buy, so the row carries the side of the order, not of the level.\n const side = row.dataset.side === 'buy' ? 0 : 1;\n if (e.ctrlKey || e.metaKey) this._deps.onPriceExecuted(price, side);\n else this._deps.onPriceSelected(price, side);\n });\n }\n\n _refreshDepthButtons(): void {\n for (const button of this._depthBtns) {\n const on = Number(button.dataset.depth) === this._depth;\n button.classList.toggle('active', on);\n button.setAttribute('aria-pressed', on ? 'true' : 'false');\n }\n }\n\n _refreshViewButtons(): void {\n for (const button of this._viewBtns) {\n const on = button.dataset.view === this._view;\n button.classList.toggle('active', on);\n button.setAttribute('aria-pressed', on ? 'true' : 'false');\n }\n }\n\n _refreshInvertButton(): void {\n this._invertBtn?.classList.toggle('active', this._invertSides);\n this._invertBtn?.setAttribute('aria-pressed', this._invertSides ? 'true' : 'false');\n }\n\n _refreshDepthToggleButton(): void {\n this._depthToggleBtn?.classList.toggle('active', this._showDepthChart);\n this._depthToggleBtn?.setAttribute('aria-pressed', this._showDepthChart ? 'true' : 'false');\n }\n\n // The variant classes on the panel root the stylesheet keys the two layouts,\n // the side order and the depth chart off.\n _applyViewState(): void {\n this.rootEl.classList.toggle('ob-view-stacked', this._view === 'stacked');\n this.rootEl.classList.toggle('ob-view-diagonal', this._view === 'diagonal');\n this.rootEl.classList.toggle('ob-invert', this._invertSides);\n this.rootEl.classList.toggle('ob-hide-depth', !this._showDepthChart);\n }\n\n // Record what this panel remembers, then ask the host to flush the layout.\n // Two calls because they are two decisions, and every caller here is a\n // setting the user just changed by hand \u2014 so it is worth flushing.\n _persistState(patch: Record<string, unknown>): void {\n this._host.persistState(patch);\n this._host.saveLayout();\n }\n\n _resetLedger(): void {\n this._bidsByPrice.clear();\n this._asksByPrice.clear();\n this._prevQuantities.clear();\n this._lastSequence = 0;\n }\n\n // The host answers how many levels it has room for, so a phone-sized page\n // narrows the ladder without this control knowing what a viewport is.\n _fitDepth(depth: number): number {\n const room = Number(this._deps.maxDepth());\n if (!Number.isFinite(room) || room < 1) return depth;\n return Math.min(depth, room);\n }\n\n static _isDepth(depth: unknown): depth is number {\n return typeof depth === 'number' && OrderBookWidget.DEPTHS.includes(depth);\n }\n\n static _deepest(): number {\n return OrderBookWidget.DEPTHS[OrderBookWidget.DEPTHS.length - 1];\n }\n\n static _sorted(ledger: Map<number, number>, direction: 1 | -1): BookLevel[] {\n return [...ledger.entries()]\n .filter(([price, quantity]) => Number.isFinite(price) && quantity > 0)\n .sort((left, right) => direction * (left[0] - right[0]))\n .map(([price, quantity]) => ({ price, quantity }));\n }\n\n _applySnapshotSide(ledger: Map<number, number>, levels: QuoteLevel[] | undefined, side: string, symbol: string, sequence: number): void {\n for (const level of levels || []) {\n const quantity = level.quantity ?? 0;\n if (level.price == null || !Number.isFinite(level.price) || !(quantity > 0)) {\n this._anomaly(`snapshot ${side} level dropped: price=${level.price} qty=${level.quantity} sym=${symbol} seq=${sequence}`);\n continue;\n }\n ledger.set(level.price, quantity);\n }\n }\n\n _applyDiffSide(ledger: Map<number, number>, levels: QuoteLevel[] | undefined, side: string, symbol: string, sequence: number): void {\n for (const level of levels || []) {\n // A level with a non-finite price is wire corruption: applying it\n // lands a NaN key in the ledger, which sorts unpredictably and\n // renders as a phantom row between two real levels.\n if (level.price == null || !Number.isFinite(level.price)) {\n this._anomaly(`invalid ${side} price: price=${level.price} qty=${level.quantity} sym=${symbol} seq=${sequence} \u2014 ignoring`);\n continue;\n }\n const quantity = level.quantity ?? 0;\n if (quantity < 0) {\n this._anomaly(`negative ${side} qty: price=${level.price} qty=${quantity} sym=${symbol} seq=${sequence} \u2014 ignoring`);\n continue;\n }\n if (quantity === 0) {\n if (!ledger.has(level.price)) {\n this._anomaly(`delete missing ${side}: price=${level.price} sym=${symbol} seq=${sequence}`);\n continue;\n }\n ledger.delete(level.price);\n } else {\n ledger.set(level.price, quantity);\n }\n }\n }\n\n // Best bid below best ask, or something upstream is broken \u2014 usually a diff\n // applied to stale state after a gap the sequence check did not catch.\n _validateBookCross(symbol: string, sequence: number, wasSnapshot: boolean): void {\n if (this._bidsByPrice.size === 0 || this._asksByPrice.size === 0) return;\n let bestBid = -Infinity;\n let bestAsk = Infinity;\n for (const price of this._bidsByPrice.keys()) if (price > bestBid) bestBid = price;\n for (const price of this._asksByPrice.keys()) if (price < bestAsk) bestAsk = price;\n if (bestBid >= bestAsk) {\n this._anomaly(`crossed book: bid=${bestBid} >= ask=${bestAsk} sym=${symbol} seq=${sequence} ${wasSnapshot ? 'snapshot' : 'diff'}`);\n }\n }\n\n // Ask for the symbol from scratch. Idempotent as far as the user is\n // concerned: the ladder blanks for a frame and refills.\n async _requestResubscribe(symbol: string): Promise<void> {\n try {\n await this._host.trading.marketData.resubscribe(symbol, MarketDataLevels.Full);\n } catch (err) {\n this._anomaly(`resubscribe after a gap failed for ${symbol}: ${err}`);\n }\n }\n\n // Wire trouble the user cannot see and support has to, through the port.\n _anomaly(message: string): void {\n this._host.log(`OrderBookWidget: ${message}`);\n }\n\n _render(): void {\n this._paint(this.getBids(), this.getAsks());\n }\n\n _paint(bids: BookLevel[], asks: BookLevel[]): void {\n if (!this.asksEl || !this.bidsEl) return;\n\n const visibleAsks = asks.slice(0, this._depth);\n const visibleBids = bids.slice(0, this._depth);\n\n // The bar is scaled per side \u2014 a 100k bid wall must not flatten a normal\n // 200-lot ask ladder on the other half \u2014 while the heat tint is scaled\n // across both, so the two halves stay perceptually comparable.\n const maxBid = Math.max(...visibleBids.map(level => level.quantity), 1);\n const maxAsk = Math.max(...visibleAsks.map(level => level.quantity), 1);\n const maxBoth = Math.max(maxBid, maxAsk, 1);\n\n const own = this._ownQuantities();\n const painted = new Map<string, number>();\n\n // Asks are rendered worst-first so the best ask ends up next to the mid\n // line, while the cumulative total runs the other way \u2014 the best ask\n // reads `qty == total` and the sum grows as the eye moves away from the\n // spread.\n const askTotals: number[] = [];\n let running = 0;\n for (const level of visibleAsks) {\n running += level.quantity;\n askTotals.push(running);\n }\n const askRows: HTMLElement[] = [];\n for (let i = visibleAsks.length - 1; i >= 0; i--) {\n askRows.push(this._levelRow(visibleAsks[i], {\n rowClass: 'ob-row ask',\n orderSide: 'buy',\n key: `a_${visibleAsks[i].price}`,\n cumulative: askTotals[i],\n barPct: (visibleAsks[i].quantity / maxAsk) * 100,\n heatPct: HEAT_FLOOR_PCT + HEAT_RANGE_PCT * (visibleAsks[i].quantity / maxBoth),\n // More size on the offer is pressure downward, so a growing ask\n // level flashes in the falling colour.\n growthClass: 'flash-red',\n shrinkClass: 'flash-green',\n ownQuantity: own.asks.get(OrderBookWidget._priceKey(visibleAsks[i].price)),\n painted,\n }));\n }\n this.asksEl.replaceChildren(...askRows);\n\n running = 0;\n const bidRows: HTMLElement[] = [];\n for (const level of visibleBids) {\n running += level.quantity;\n bidRows.push(this._levelRow(level, {\n rowClass: 'ob-row bid',\n orderSide: 'sell',\n key: `b_${level.price}`,\n cumulative: running,\n barPct: (level.quantity / maxBid) * 100,\n heatPct: HEAT_FLOOR_PCT + HEAT_RANGE_PCT * (level.quantity / maxBoth),\n growthClass: 'flash-green',\n shrinkClass: 'flash-red',\n ownQuantity: own.bids.get(OrderBookWidget._priceKey(level.price)),\n painted,\n }));\n }\n this.bidsEl.replaceChildren(...bidRows);\n\n this._prevQuantities = painted;\n\n if (asks.length > 0 && bids.length > 0) {\n const bestAsk = asks[0].price;\n const bestBid = bids[0].price;\n const spread = bestAsk - bestBid;\n if (this.midEl) this.midEl.textContent = formatPrice((bestAsk + bestBid) / 2);\n if (this._midSpreadEl) this._midSpreadEl.textContent = formatPrice(spread);\n if (this.spreadEl) this.spreadEl.textContent = this._host.t('Spread: {0}', formatPrice(spread));\n }\n\n this._paintSentiment(visibleBids, visibleAsks);\n this._paintDepthChart(visibleBids, visibleAsks);\n }\n\n // One level. Every measurement it carries \u2014 the bar's share of its side, the\n // heat tint \u2014 reaches the stylesheet as a custom property, so the widths and\n // the colours are still declared in CSS.\n _levelRow(level: BookLevel, options: {\n rowClass: string; orderSide: string; key: string; cumulative: number;\n barPct: number; heatPct: number; growthClass: string; shrinkClass: string;\n ownQuantity: number | undefined; painted: Map<string, number>;\n }): HTMLElement {\n const quantity = makeElement('span', 'qty', {}, [formatQty(level.quantity)]);\n if (options.ownQuantity) {\n quantity.appendChild(makeElement('span', 'own-badge',\n { title: this._host.t('YourOrder') }, [formatQty(options.ownQuantity)]));\n }\n\n const row = makeElement('div', options.rowClass, {\n 'data-side': options.orderSide,\n 'data-price': String(level.price),\n }, [\n makeElement('span', 'price', {}, [formatPrice(level.price)]),\n quantity,\n makeElement('span', 'total', {}, [formatQty(options.cumulative)]),\n makeElement('span', 'bar', {}, []),\n ]);\n row.style.setProperty('--t-ob-bar', `${options.barPct.toFixed(0)}%`);\n row.style.setProperty('--t-ob-heat', `${options.heatPct.toFixed(1)}%`);\n if (options.ownQuantity) row.classList.add('own');\n\n const previous = this._prevQuantities.get(options.key);\n if (previous !== undefined && previous !== level.quantity)\n row.classList.add(level.quantity > previous ? options.growthClass : options.shrinkClass);\n options.painted.set(options.key, level.quantity);\n\n return row;\n }\n\n // This session's resting orders on this symbol, summed per price \u2014 the \"your\n // size is here\" badge is an overlay on the ledger, so it reads the client's\n // own order cache rather than subscribing to anything.\n //\n // Prices are keyed as fixed-point text: two floats that print the same are\n // the same level to a user, and `===` on them is not.\n _ownQuantities(): { bids: Map<string, number>; asks: Map<string, number> } {\n const bids = new Map<string, number>();\n const asks = new Map<string, number>();\n if (!this._currentSymbol) return { bids, asks };\n\n for (const order of this._host.trading.marketData.getOrders() || []) {\n if (!order || order.instrument !== this._currentSymbol) continue;\n if (order.status !== RESTING_STATE) continue;\n if (order.limitPrice == null || order.side == null) continue;\n const side = this._host.presentation.isBuy(order.side) ? bids : asks;\n const key = OrderBookWidget._priceKey(order.limitPrice);\n const left = Number(order.balance ?? order.quantity) || 0;\n side.set(key, (side.get(key) || 0) + left);\n }\n return { bids, asks };\n }\n\n static _priceKey(price: number): string {\n return Number(price).toFixed(8);\n }\n\n // The bid/ask split under the ladder, as one measurement the stylesheet\n // turns into two widths.\n //\n // It sums the VISIBLE levels only, so the figure moves when the user changes\n // depth. A whole-book share needs the server to aggregate one, which the\n // frame does not carry today.\n _paintSentiment(bids: BookLevel[], asks: BookLevel[]): void {\n if (!this._sentimentEl) return;\n const bidSum = bids.reduce((sum, level) => sum + level.quantity, 0);\n const askSum = asks.reduce((sum, level) => sum + level.quantity, 0);\n const total = bidSum + askSum;\n\n if (total <= 0) {\n this._sentimentEl.style.setProperty('--t-ob-sent', '50%');\n if (this._sentBidPctEl) this._sentBidPctEl.textContent = '--';\n if (this._sentAskPctEl) this._sentAskPctEl.textContent = '--';\n return;\n }\n // Rounded so the two halves sum to exactly 100 \u2014 a strip reading 99% is\n // read as a bug rather than as rounding.\n const askPct = Math.round((askSum / total) * 100);\n const bidPct = 100 - askPct;\n this._sentimentEl.style.setProperty('--t-ob-sent', `${bidPct}%`);\n if (this._sentBidPctEl) this._sentBidPctEl.textContent = `${bidPct}%`;\n if (this._sentAskPctEl) this._sentAskPctEl.textContent = `${askPct}%`;\n }\n\n // The depth curve above the ladder. Everything shaped is in\n // `orderbook-depth.ts`; what is left here is the canvas plumbing and the\n // host's palette, because a canvas takes colours as values and a class name\n // cannot reach one.\n _paintDepthChart(bids: BookLevel[], asks: BookLevel[]): void {\n const canvas = this.depthChartEl;\n if (!canvas || !this._showDepthChart) return;\n\n const ratio = Number(this._deps.pixelRatio()) || 1;\n const cssWidth = canvas.clientWidth || 0;\n const cssHeight = canvas.clientHeight || 0;\n if (cssWidth < 4 || cssHeight < 4) return;\n\n const width = Math.floor(cssWidth * ratio);\n const height = Math.floor(cssHeight * ratio);\n if (canvas.width !== width) canvas.width = width;\n if (canvas.height !== height) canvas.height = height;\n\n const ctx = canvas.getContext('2d');\n if (!ctx) return;\n ctx.clearRect(0, 0, width, height);\n if (bids.length === 0 && asks.length === 0) return;\n\n const pad = 2 * ratio;\n const midX = Math.floor(width / 2);\n const geometry: DepthGeometry = {\n midX,\n pad,\n halfWidth: midX - pad,\n baseY: height - pad,\n innerHeight: height - pad * 2,\n };\n\n const bidSide = depthSide(bids, -1, geometry);\n const askSide = depthSide(asks, 1, geometry);\n const maxTotal = Math.max(bidSide ? bidSide.total : 0, askSide ? askSide.total : 0, 1);\n const palette = this._host.presentation.canvasPalette();\n\n if (bidSide) this._strokeDepthSide(ctx, depthPolyline(bidSide, maxTotal, geometry), palette.up, geometry, ratio);\n if (askSide) this._strokeDepthSide(ctx, depthPolyline(askSide, maxTotal, geometry), palette.down, geometry, ratio);\n\n // The mid line, so the two halves read as two sides of one price rather\n // than as two charts.\n ctx.globalAlpha = 0.4;\n ctx.fillStyle = palette.grid;\n ctx.fillRect(midX - Math.max(1, ratio / 2), 0, Math.max(1, ratio), height);\n ctx.globalAlpha = 1;\n }\n\n // Area under the step curve, then the curve on top. Opacity is varied here\n // rather than in the colour, so the host states one solid colour per\n // direction and this decides how much of it a fill is worth.\n _strokeDepthSide(ctx: CanvasRenderingContext2D, line: [number, number][], color: string, geometry: DepthGeometry, ratio: number): void {\n if (line.length === 0) return;\n\n ctx.beginPath();\n ctx.moveTo(line[0][0], geometry.baseY);\n for (const [x, y] of line) ctx.lineTo(x, y);\n ctx.lineTo(line[line.length - 1][0], geometry.baseY);\n ctx.closePath();\n ctx.globalAlpha = 0.18;\n ctx.fillStyle = color;\n ctx.fill();\n\n ctx.beginPath();\n ctx.moveTo(line[0][0], line[0][1]);\n for (let i = 1; i < line.length; i++) ctx.lineTo(line[i][0], line[i][1]);\n ctx.lineWidth = Math.max(1.5, 1.5 * ratio);\n ctx.lineJoin = 'miter';\n ctx.globalAlpha = 0.95;\n ctx.strokeStyle = color;\n ctx.stroke();\n ctx.globalAlpha = 1;\n }\n}\n", "// Bubble compaction for a tape.\n//\n// One lane of a bubble chart is one symbol's ticks, and a busy symbol delivers\n// far more of them than a lane has room for: drawn one circle per print, the\n// lane melts into a solid wall. Bucketing by time slice and side turns that\n// back into something readable \u2014 at most two bubbles per slice, each placed at\n// its slice's volume-weighted average price and sized by its summed volume.\n//\n// Pure: no DOM, no host, no clock. The trade feed renders what comes out of\n// here, which is what lets the arithmetic a busy symbol depends on be covered\n// directly rather than through a canvas.\nimport type { OrderSide } from './trading-data.js';\n\n/// One print, with the two things the tape has already decided about it: which\n/// direction it went, and where it sits on the shared time axis.\n///\n/// `buy` is resolved by the host's presentation before a tick reaches this\n/// module \u2014 the wire spells a side three ways and normalising it is the host's\n/// job, not this file's \u2014 while `side` keeps the raw value, because wording it\n/// is the host's job too and only it can do that. `index` is the tick's\n/// position in the WHOLE feed rather than in its lane, so two symbols with\n/// wildly different prices still flow along one time axis.\nexport interface FeedTick {\n symbol: string;\n side: OrderSide;\n buy: boolean;\n price: number;\n quantity: number;\n time: string;\n index: number;\n}\n\n/// One bubble: a tick, or the slice of ticks it stands for.\nexport interface FeedBubble extends FeedTick {\n /// How many ticks this bubble represents \u2014 1 when it is a tick itself, and\n /// what the tooltip reads to say \"VWAP\" instead of \"Price\".\n count: number;\n}\n\n/// Bucket one lane's ticks into at most `maxBucketsPerSide` bubbles per side.\n///\n/// Below that budget every tick passes through as its own one-tick bubble, so a\n/// sparse symbol still shows every print. Above it, each (side, slice) merges\n/// into one bubble. `ticks` is newest-first, which is how the tape holds them,\n/// so the first slice is the most recent block.\nexport function aggregateBubbles(ticks: FeedTick[], maxBucketsPerSide: number): FeedBubble[] {\n if (!ticks || ticks.length === 0) return [];\n const budget = Math.max(1, Math.floor(maxBucketsPerSide));\n\n // The two directions compete for the same horizontal room, so each gets its\n // own budget rather than the busier one crowding the other out.\n const buys: FeedTick[] = [];\n const sells: FeedTick[] = [];\n for (const tick of ticks) (tick.buy ? buys : sells).push(tick);\n\n const bubbles: FeedBubble[] = [];\n for (const side of [buys, sells]) {\n if (side.length === 0) continue;\n if (side.length <= budget) {\n for (const tick of side) bubbles.push({ ...tick, count: 1 });\n continue;\n }\n const bucketSize = Math.ceil(side.length / budget);\n for (let start = 0; start < side.length; start += bucketSize)\n bubbles.push(merge(side.slice(start, start + bucketSize)));\n }\n return bubbles;\n}\n\n/// One slice as a single bubble: total volume, volume-weighted price, and the\n/// slice's middle tick for its position on the time axis.\nfunction merge(slice: FeedTick[]): FeedBubble {\n let quantity = 0;\n let weighted = 0;\n for (const tick of slice) {\n const q = tick.quantity || 0;\n quantity += q;\n weighted += q * tick.price;\n }\n // A slice can legitimately total zero volume (an all-zero-quantity tape is\n // rare live and ordinary in a fixture), and dividing by it would place the\n // bubble at NaN \u2014 nowhere, silently. Its first price is the honest answer.\n const price = quantity > 0 ? weighted / quantity : slice[0].price;\n const middle = slice[Math.floor(slice.length / 2)];\n return {\n symbol: slice[0].symbol,\n side: slice[0].side,\n buy: slice[0].buy,\n price,\n quantity,\n time: middle.time,\n index: middle.index,\n count: slice.length,\n };\n}\n", "// Where every bubble, rule and label of a bubble chart goes.\n//\n// The trade feed's second rendering is a scatter of prints: time along X,\n// price up Y, volume as the radius, direction as the colour. It is drawn on a\n// canvas, which is the one surface a class name cannot reach \u2014 so the geometry\n// is computed here, as numbers, and the control does nothing but stroke and\n// fill what comes back.\n//\n// Pure, and that is the point: a fake canvas that swallows drawing calls proves\n// nothing, while the arithmetic below \u2014 lanes sharing one time axis, a price\n// scale per symbol, a radius that stays inside its lane \u2014 is exactly what can\n// be wrong. It is covered directly.\nimport { aggregateBubbles, type FeedBubble, type FeedTick } from './tradefeed-aggregator.js';\n\n/// One symbol's prints. A feed following a single symbol has one unnamed lane;\n/// pinning extras splits the height into one lane each so a $76k symbol and a\n/// $270 one both stay visible instead of one flattening the other.\nexport interface BubbleLane {\n symbol: string;\n ticks: FeedTick[];\n}\n\nexport interface BubbleLayoutInput {\n width: number;\n height: number;\n lanes: BubbleLane[];\n /// How many ticks the whole feed holds. The X axis is a tick's index in\n /// that list, so lanes flow along one shared time axis rather than each\n /// stretching its own count across the full width.\n total: number;\n}\n\n/// One bubble, placed.\nexport interface BubbleShape {\n x: number;\n y: number;\n radius: number;\n bubble: FeedBubble;\n}\n\n/// One price label on the right-hand axis, and the rule leading to it.\nexport interface BubbleAxisTick {\n text: string;\n y: number;\n}\n\n/// One lane, placed. `label` is null for a feed following a single symbol \u2014\n/// naming the only lane says nothing \u2014 and `separatorY` is null for the first\n/// lane, which has nothing above it to be divided from.\nexport interface BubbleLaneShape {\n symbol: string;\n label: { x: number; y: number } | null;\n separatorY: number | null;\n ticks: BubbleAxisTick[];\n}\n\nexport interface BubbleLayout {\n /// Left edge of the axis strip: rules stop just short of it, labels start\n /// just past it.\n axisX: number;\n /// Where an axis label's text begins.\n labelX: number;\n lanes: BubbleLaneShape[];\n /// Every bubble in draw order \u2014 oldest first, so the newest print lands on\n /// top of what it overlaps. Hit-testing walks this backwards for the same\n /// reason.\n bubbles: BubbleShape[];\n}\n\n// Geometry. These are the chart's proportions, not the host's looks: a canvas\n// has no cascade to read them from, and moving them into CSS would only mean\n// reading them back out again.\nconst PAD_X = 10;\nconst PAD_Y = 10;\nconst AXIS_WIDTH = 56;\nconst LABEL_GAP = 4;\nconst LANE_GAP = 2;\nconst MIN_LANE_HEIGHT = 20;\nconst AXIS_TICKS = 3;\nconst MIN_RADIUS = 2;\nconst MAX_RADIUS = 22;\n/// A bubble may grow to this share of its lane's height, so a lane with one\n/// enormous print does not draw a circle taller than the lane it sits in.\nconst RADIUS_LANE_SHARE = 0.4;\n/// Horizontal room one bucket wants. Narrower and the lane reads as a wall;\n/// wider and a sparse symbol turns into a handful of lonely dots.\nconst BUCKET_WIDTH = 8;\nconst MIN_BUCKETS = 6;\nconst MAX_BUCKETS = 30;\n\n/// Place every lane and every bubble for one paint.\nexport function layoutBubbles(input: BubbleLayoutInput): BubbleLayout {\n const { width, height, total } = input;\n const axisX = Math.max(PAD_X + 1, width - AXIS_WIDTH);\n const drawWidth = Math.max(1, axisX - PAD_X);\n const layout: BubbleLayout = { axisX, labelX: axisX + LABEL_GAP, lanes: [], bubbles: [] };\n\n const lanes = input.lanes.filter(lane => lane.ticks.length > 0);\n if (lanes.length === 0) return layout;\n\n const named = input.lanes.length > 1;\n const laneHeight = Math.max(\n MIN_LANE_HEIGHT,\n (height - 2 * PAD_Y - LANE_GAP * (input.lanes.length - 1)) / input.lanes.length);\n\n // Placed against the DECLARED lanes, not the non-empty ones: a symbol that\n // has not printed yet keeps its slot rather than letting the lanes below it\n // slide up and back down on its first tick.\n input.lanes.forEach((lane, position) => {\n const laneTop = named ? PAD_Y + position * (laneHeight + LANE_GAP) : PAD_Y;\n const laneSpan = named ? laneHeight : Math.max(MIN_LANE_HEIGHT, height - 2 * PAD_Y);\n const scale = priceScale(lane.ticks, laneTop, laneSpan);\n\n layout.lanes.push({\n symbol: lane.symbol,\n label: named ? { x: LABEL_GAP, y: laneTop + 2 } : null,\n separatorY: named && position > 0 ? laneTop - LANE_GAP / 2 : null,\n ticks: lane.ticks.length === 0 ? [] : axisTicks(scale),\n });\n\n if (lane.ticks.length === 0) return;\n const buckets = Math.max(MIN_BUCKETS, Math.min(MAX_BUCKETS, Math.floor(drawWidth / BUCKET_WIDTH)));\n const bubbles = aggregateBubbles(lane.ticks, buckets);\n let maxQuantity = 0;\n for (const bubble of bubbles) if (bubble.quantity > maxQuantity) maxQuantity = bubble.quantity;\n const quantityScale = Math.max(maxQuantity, Number.EPSILON);\n const radiusCap = Math.min(MAX_RADIUS, laneSpan * RADIUS_LANE_SHARE);\n\n for (const bubble of bubbles) {\n layout.bubbles.push({\n // Newest print (index 0) at the right edge, oldest at the left.\n x: PAD_X + drawWidth * (total <= 1 ? 1 : 1 - bubble.index / (total - 1)),\n y: scale.top + scale.height * (1 - (bubble.price - scale.min) / scale.range),\n radius: MIN_RADIUS + Math.sqrt(Math.max(0, bubble.quantity) / quantityScale) * radiusCap,\n bubble,\n });\n }\n });\n\n // Oldest first: a later print draws over an earlier one, and the hit test\n // reads the same list backwards so the circle on top is the one picked.\n layout.bubbles.sort((left, right) => right.bubble.index - left.bubble.index);\n return layout;\n}\n\ninterface PriceScale {\n top: number;\n height: number;\n min: number;\n range: number;\n}\n\n/// The lane's price range, as the Y axis. `range` is never zero \u2014 a lane whose\n/// prints are all at one price would otherwise divide by it \u2014 so a flat lane\n/// draws along its own top edge rather than nowhere.\nfunction priceScale(ticks: FeedTick[], top: number, height: number): PriceScale {\n let min = Infinity;\n let max = -Infinity;\n for (const tick of ticks) {\n if (tick.price < min) min = tick.price;\n if (tick.price > max) max = tick.price;\n }\n if (!isFinite(min) || !isFinite(max)) { min = 0; max = 0; }\n return { top, height, min, range: Math.max(max - min, Number.EPSILON) };\n}\n\n/// Three labels down the lane: its high, its middle and its low.\nfunction axisTicks(scale: PriceScale): BubbleAxisTick[] {\n const format = priceFormatter(scale.min, scale.min + scale.range);\n const ticks: BubbleAxisTick[] = [];\n for (let step = 0; step < AXIS_TICKS; step++) {\n const fraction = step / (AXIS_TICKS - 1);\n ticks.push({\n text: format((scale.min + scale.range) - scale.range * fraction),\n y: scale.top + scale.height * fraction,\n });\n }\n return ticks;\n}\n\n/// Decimals chosen from the lane's magnitude. A fixed precision is wrong at\n/// both ends: two decimals on a $76,000 print is noise, and none on a $0.04 one\n/// collapses the whole lane onto a single label.\nexport function priceFormatter(min: number, max: number): (price: number) => string {\n const magnitude = Math.max(Math.abs(min), Math.abs(max));\n let digits = 6;\n if (magnitude >= 1000) digits = 0;\n else if (magnitude >= 10) digits = 2;\n else if (magnitude >= 0.1) digits = 4;\n return (price: number) => price.toLocaleString(undefined, {\n minimumFractionDigits: digits,\n maximumFractionDigits: digits,\n });\n}\n", "// Trade feed \u2014 multi-instance.\n//\n// The public tape: every print for the symbol the page is showing, plus any\n// extra instruments this instance has pinned, in one of two renderings. The\n// list is a row per print; the bubble chart is the same prints scattered by\n// time and price, sized by volume and coloured by direction. Which of the two\n// is showing is shared across every feed on the page through the host's\n// preference store; the pinned extras belong to the instance and ride in its\n// persisted state.\n//\n// Builds its own DOM (see `_buildRoot`) rather than cloning a <template> out of\n// the page, so it can be constructed by any host that supplies a `TradingHost`.\n//\n// Ticks are pushed in \u2014 `setTrades` for a fresh symbol, `addTrade` per print \u2014\n// because a host fans one socket to every live feed rather than each of them\n// subscribing again. The one thing the panel pulls is the account's own fills,\n// on the My-trades tab, through `trading.api.getExecutions`.\n//\n// The chart is a <canvas>, the single surface a class name cannot reach: its\n// geometry comes out of `tradefeed-bubbles.ts` as numbers and its colours out\n// of the host's `canvasPalette()`, so the package still paints nothing from a\n// palette of its own.\nimport { formatPrice, formatQty } from './formatters.js';\nimport { makeElement, makeIconButton, makePanelId, makePanelRoot } from './dom.js';\nimport { ControlTypes } from './control-types.js';\nimport { makeGridMenu } from './grid-menu.js';\nimport { MarketDataLevels, TradingHost, assertHost } from './trading-host.js';\nimport type { TradeRow } from './trading-data.js';\nimport type { FeedBubble, FeedTick } from './tradefeed-aggregator.js';\nimport { layoutBubbles, type BubbleLane, type BubbleShape } from './tradefeed-bubbles.js';\nimport { DataGrid, GridColumn } from '@stocksharp/grids/source/data-grid';\n\n// A print carries no id of its own, so the widget stamps one on arrival \u2014 the\n// grid addresses rows by key, and a tape with fifty anonymous rows would have\n// no identities to diff, select or flash by.\ntype KeyedTrade = TradeRow & { _k?: number };\n\n/// The panel needs nothing beyond the host port. It reports no application\n/// action: a tape is read, and the two gestures it does make \u2014 pin an\n/// instrument, open another feed \u2014 are the host's own `pickInstrument` and\n/// `spawn`.\nexport interface TradeFeedDeps {\n host: TradingHost;\n}\n\n// Opacity, not colour. The hue of every stroke below is the host's\n// `canvasPalette`; how solid it is drawn is the chart's own business, and\n// keeping the two apart is what lets one palette serve a light theme and a dark\n// one without the package knowing which it is in.\nconst FILL_ALPHA = 0.55;\nconst STROKE_ALPHA = 0.9;\nconst LABEL_ALPHA = 0.55;\nconst SEPARATOR_ALPHA = 0.12;\nconst RULE_ALPHA = 0.08;\n\n// How far from a bubble's edge a cursor still counts as over it. Small bubbles\n// are otherwise impossible to hit.\nconst HOVER_SLACK = 6;\n// Gap between the cursor and the tooltip, and between the tooltip and the edge\n// it is clamped against.\nconst TOOLTIP_OFFSET = 12;\nconst TOOLTIP_MARGIN = 4;\n\nexport class TradeFeedWidget {\n static TYPE = ControlTypes.TradeFeed;\n /// Shared across every trade feed on the page: which of the two renderings\n /// the user last chose. The value is what a user's stored settings already\n /// hold, so it is spelled exactly as it always was.\n static VIEW_KEY = 'terminal.tradeFeedView';\n /// Rows kept in the list, and prints kept for the chart. The chart holds\n /// far more because it compacts them: a bubble stands for a slice.\n static MAX_ROWS = 50;\n static MAX_BUBBLES = 500;\n\n rootEl: HTMLElement;\n extrasEl: HTMLElement | null;\n bubbleCanvas: HTMLCanvasElement | null;\n trades: TradeRow[];\n bubbleTrades: TradeRow[];\n myTrades: TradeRow[];\n avgQty: number;\n tab: string;\n view: string;\n // `//` rather than `///` from here down \u2014 see the note in positions-widget.\n _host: TradingHost;\n _marketGrid: DataGrid<TradeRow> | null;\n _myGrid: DataGrid<TradeRow> | null;\n // The stamp the next anonymous print gets \u2014 see KeyedTrade.\n _seq: number;\n _tabsEl: HTMLElement | null;\n _viewToggleEl: HTMLElement | null;\n _tooltipEl: HTMLElement | null;\n _addBtn: HTMLElement | null;\n _closeBtn: HTMLElement | null;\n _addSymbolBtn: HTMLElement | null;\n _bubbleCtx: CanvasRenderingContext2D | null;\n // Rebuilt on every paint: where each bubble landed and what it stands for.\n // Hover walks it backwards so the circle drawn last \u2014 the newest print \u2014\n // wins wherever two overlap.\n _bubbleHits: BubbleShape[];\n _canvasSize: { width: number; height: number } | null;\n _resizeObserver: ResizeObserver | null;\n _activeSymbol: string | null;\n _extraSymbols: Set<string>;\n\n static create(hostEl: HTMLElement, state: Record<string, unknown>, deps: TradeFeedDeps): TradeFeedWidget {\n // Assert before building: the markup below is localized through the\n // host, so a missing host has to fail here rather than render a panel\n // captioned with raw English keys.\n const host = assertHost(deps?.host, 'TradeFeedWidget');\n const root = TradeFeedWidget._buildRoot(host);\n root.id = makePanelId(TradeFeedWidget.TYPE);\n hostEl.appendChild(root);\n return new TradeFeedWidget(root, state || {}, deps);\n }\n\n // The panel's markup. The host stylesheet reads this structure, and a\n // docking host lifts `.panel-header`'s children into its tab strip.\n //\n // Two `+` buttons, and they are not the same gesture: the one in the header\n // pins another instrument to THIS feed, the one beside it asks the host for\n // another feed panel. The tooltips are what tell them apart, so neither is\n // an icon on its own.\n static _buildRoot(host: TradingHost): HTMLElement {\n const addInstrument = host.t('AddInstrument');\n const marketTrades = host.t('MarketTrades');\n return makePanelRoot('tradefeed-panel tf-tab-market tf-view-list', host.t('TradeFeed'), [\n makeElement('div', 'panel-header', {}, [\n makeElement('span', '', {}, [marketTrades]),\n makeIconButton('bt-icon-btn tf-add-symbol-btn', addInstrument, 'bi-plus-lg', { type: 'button' }),\n makeIconButton('bt-icon-btn panel-add-btn', host.t('Add trade feed'), 'bi-window-plus', { type: 'button' }),\n makeElement('div', 'tradefeed-view-toggle tf-view-toggle', { role: 'group', 'aria-label': host.t('TradeFeedView') }, [\n makeIconButton('tf-view-btn', host.t('ListView'), 'bi-list-ul', { type: 'button', 'data-view': 'list' }),\n makeIconButton('tf-view-btn', host.t('BubbleChart'), 'bi-circle-fill', { type: 'button', 'data-view': 'bubbles' }),\n ]),\n makeIconButton('bt-icon-btn bt-icon-cancel panel-close-btn', host.t('ClosePanel'), 'bi-x', { type: 'button' }),\n ]),\n makeElement('div', 'tf-tabs', { role: 'tablist', 'aria-label': host.t('Trade feed tabs') }, [\n makeElement('button', 'tf-tab active', { type: 'button', role: 'tab', 'aria-selected': 'true', 'data-tab': 'market' }, [marketTrades]),\n makeElement('button', 'tf-tab', { type: 'button', role: 'tab', 'aria-selected': 'false', 'data-tab': 'my' }, [host.t('My trades')]),\n ]),\n makeElement('div', 'tf-extras', { role: 'list', 'aria-label': host.t('ExtraInstruments'), hidden: '' }, []),\n // Each tape is a DataGrid over the shared table skin: the <thead>\n // is left empty for the grid to fill, which is also what makes the\n // tape sortable and gives it the grid's menu and selection.\n makeElement('div', 'tradefeed-content tf-market', { 'aria-live': 'polite' }, [\n makeElement('table', 'terminal-table tradefeed-table', { role: 'table', 'aria-label': marketTrades }, [\n makeElement('thead', '', {}, []),\n makeElement('tbody', '', {}, []),\n ]),\n ]),\n makeElement('div', 'tradefeed-content tf-my', {}, [\n makeElement('table', 'terminal-table tradefeed-table', { role: 'table', 'aria-label': host.t('My trades') }, [\n makeElement('thead', '', {}, []),\n makeElement('tbody', '', {}, []),\n ]),\n ]),\n makeElement('canvas', 'tradefeed-bubbles tf-bubbles', { 'aria-hidden': 'true' }, []),\n makeElement('div', 'tf-bubble-tooltip', { role: 'tooltip', hidden: '' }, []),\n ]);\n }\n\n constructor(rootEl: HTMLElement, state: Record<string, unknown>, deps: TradeFeedDeps) {\n this._host = assertHost(deps?.host, 'TradeFeedWidget');\n\n this.rootEl = rootEl;\n this.extrasEl = this.rootEl.querySelector('.tf-extras');\n this.bubbleCanvas = this.rootEl.querySelector('.tf-bubbles');\n this._tabsEl = this.rootEl.querySelector('.tf-tabs');\n this._viewToggleEl = this.rootEl.querySelector('.tf-view-toggle');\n this._tooltipEl = this.rootEl.querySelector('.tf-bubble-tooltip');\n this._addBtn = this.rootEl.querySelector('.panel-add-btn');\n this._closeBtn = this.rootEl.querySelector('.panel-close-btn');\n this._addSymbolBtn = this.rootEl.querySelector('.tf-add-symbol-btn');\n this._bubbleCtx = this.bubbleCanvas?.getContext('2d') ?? null;\n\n this.trades = [];\n this.bubbleTrades = [];\n this.myTrades = [];\n // Seed rather than zero: the \"unusually large\" test is a multiple of the\n // running average, and an average starting at zero would flag the first\n // print of the session.\n this.avgQty = 100;\n this.tab = 'market';\n this._seq = 0;\n this._bubbleHits = [];\n this._canvasSize = null;\n this._resizeObserver = null;\n\n this._marketGrid = this._makeGrid('.tf-market');\n this._myGrid = this._makeGrid('.tf-my');\n\n const saved = this._host.preferences.get(TradeFeedWidget.VIEW_KEY, null);\n this.view = saved === 'bubbles' ? 'bubbles' : 'list';\n\n // The page's active symbol drives the primary feed; extras are the\n // instruments this instance pinned on top of it, each refcount-\n // subscribed and shown with a symbol column.\n this._activeSymbol = null;\n this._extraSymbols = new Set(Array.isArray(state.extras) ? state.extras as string[] : []);\n\n this._addBtn?.addEventListener('click', (e) => {\n e.preventDefault();\n this._host.spawn({ extras: [...this._extraSymbols] });\n });\n this._closeBtn?.addEventListener('click', (e) => {\n e.preventDefault();\n this.dispose();\n this._host.close();\n });\n this._addSymbolBtn?.addEventListener('click', (e) => {\n e.preventDefault();\n e.stopPropagation();\n this._host.trading.pickInstrument((symbol) => void this.addExtraSymbol(symbol));\n });\n\n this._bindTabs();\n this._bindViewToggle();\n this._bindBubbleHover();\n this._applyView();\n this._renderExtras();\n\n // Resubscribe the persisted extras up front: without this a reload\n // renders the chip and streams nothing behind it.\n for (const symbol of this._extraSymbols)\n void this._host.trading.marketData.addSymbol(symbol, MarketDataLevels.Tape).catch(() => { });\n\n if (this.bubbleCanvas && typeof ResizeObserver === 'function') {\n this._resizeObserver = new ResizeObserver(() => { this._sizeCanvas(); this._renderBubbles(); });\n this._resizeObserver.observe(this.bubbleCanvas);\n }\n\n this._host.register(this);\n }\n\n dispose(): void {\n try { this._resizeObserver?.disconnect(); } catch { /* already torn down */ }\n // The grids hold document-level listeners (the copy shortcut) that\n // outlive a removed subtree \u2014 they have to be told, not just detached.\n this._marketGrid?.destroy();\n this._myGrid?.destroy();\n // Drop this feed's share of every extra so a symbol nobody else watches\n // stops streaming \u2014 the client refcounts, so a neighbour keeps its own.\n for (const symbol of this._extraSymbols)\n void this._host.trading.marketData.removeSymbol(symbol).catch(() => { });\n this._host.unregister(this);\n try { this.rootEl.remove(); } catch { /* already detached */ }\n }\n\n /// Which symbol the rest of the page is showing. The feed accepts prints\n /// for it without it being pinned, and re-syncs so the symbol column\n /// appears or disappears with the pinned set.\n setActiveSymbol(symbol: string | null): void {\n this._activeSymbol = symbol || null;\n this._renderExtras();\n this._renderBubbles();\n }\n\n /// Replace the tape wholesale \u2014 what a host does when the page moves to a\n /// different instrument. The flash baseline resets with it: a new\n /// instrument's history is history, not fifty arrivals at once.\n setTrades(trades: TradeRow[]): void {\n const rows = (trades || []).map(t => this._stamp(t));\n this.trades = rows.slice(0, TradeFeedWidget.MAX_ROWS);\n this.bubbleTrades = rows.slice(0, TradeFeedWidget.MAX_BUBBLES);\n if (this.trades.length > 0) {\n const total = this.trades.reduce((sum, t) => sum + (t.quantity ?? 0), 0);\n this.avgQty = total / this.trades.length || this.avgQty;\n }\n this._marketGrid?.flashReset();\n this._marketGrid?.setRows([...this.trades]);\n this._renderBubbles();\n }\n\n /// One print off the wire. Ignored unless this feed watches its symbol:\n /// a host fans every tick to every feed and each decides for itself, which\n /// is the only arrangement that supports per-instance extras.\n addTrade(trade: TradeRow): void {\n if (!trade || !this._watches(trade.symbol)) return;\n this._stamp(trade);\n this.trades.unshift(trade);\n if (this.trades.length > TradeFeedWidget.MAX_ROWS) this.trades.length = TradeFeedWidget.MAX_ROWS;\n this.bubbleTrades.unshift(trade);\n if (this.bubbleTrades.length > TradeFeedWidget.MAX_BUBBLES) this.bubbleTrades.length = TradeFeedWidget.MAX_BUBBLES;\n // A rolling average rather than a re-sum: \"unusually large\" should track\n // the recent tape, not the whole session.\n this.avgQty = this.avgQty * 0.95 + (trade.quantity ?? 0) * 0.05;\n\n // The grid diffs by key, and its flashNewClass is what marks the\n // newcomer \u2014 the hand-built prepend this replaces did both by hand.\n this._marketGrid?.setRows([...this.trades]);\n this._renderBubbles();\n }\n\n /// The account's own fills for the My-trades tab. The one thing this panel\n /// pulls rather than being pushed.\n async loadMyTrades(portfolioId: number | null, symbol: string | null): Promise<void> {\n if (!portfolioId) {\n this.myTrades = [];\n this._renderMy();\n return;\n }\n try {\n this.myTrades = await this._host.trading.api.getExecutions(portfolioId, symbol, TradeFeedWidget.MAX_ROWS) || [];\n } catch (err) {\n // Through the port, not the console: this is the diagnostic the\n // user cannot see and support has to, and routing it here is also\n // what makes it assertable.\n this._host.log(`TradeFeedWidget: failed to load own trades: ${err}`);\n this.myTrades = [];\n }\n this._renderMy();\n }\n\n // ---------------------------------------------------------------- the grid\n\n // One construction for both tapes: same columns, same chrome, different\n // tbody. The container holds the scroll; the grid holds the rows.\n _makeGrid(containerSelector: string): DataGrid<TradeRow> | null {\n const head = this.rootEl.querySelector(`${containerSelector} thead`);\n const body = this.rootEl.querySelector(`${containerSelector} tbody`);\n if (!head || !body) return null;\n return new DataGrid<TradeRow>({\n head: head as HTMLElement,\n body: body as HTMLElement,\n columns: this._columns(),\n // Newest print on top \u2014 the only order a tape is read in at rest;\n // a header click can still reorder, and clears back to this.\n defaultSort: { col: 'time', dir: 'desc' },\n rowKey: (t) => TradeFeedWidget._key(t),\n emptyText: this._host.t('No trades yet'),\n rowClass: (t) => this._rowClass(t),\n contextMenu: makeGridMenu(this._host),\n selection: 'multi',\n // The grid marks what is new since the previous paint; the\n // stylesheet decides what a flash looks like.\n flashNewClass: 'flash-new',\n });\n }\n\n // Prints arrive anonymous; executions arrive with ids. Stamp the former\n // once, on arrival, and key both without ever colliding.\n _stamp(trade: TradeRow): TradeRow {\n const keyed = trade as KeyedTrade;\n if (keyed._k == null && keyed.id == null) keyed._k = ++this._seq;\n return trade;\n }\n\n static _key(trade: TradeRow): string {\n const keyed = trade as KeyedTrade;\n return keyed.id != null ? String(keyed.id) : `p${keyed._k}`;\n }\n\n // The direction class sits on the row, so price and side inherit its\n // colour while time and qty state their own \u2014 plus the \"unusually large\"\n // mark, measured against the rolling average as of this paint.\n _rowClass(trade: TradeRow): string {\n const classes = this._host.presentation.sideClass(trade.side!).split(' ').filter(Boolean);\n if ((trade.quantity ?? 0) > this.avgQty * 2) classes.push('large-trade');\n return classes.join(' ');\n }\n\n // The tape's single column declaration, shared by both tabs. `time` sorts\n // by the wire timestamp and shows the clock; the symbol column exists\n // always and is hidden until a second instrument is in the feed.\n _columns(): GridColumn<TradeRow>[] {\n const label = (key: string) => this._host.t(key);\n const presentation = this._host.presentation;\n return [\n {\n key: 'time',\n header: label('Time'),\n exportable: true,\n value: (t) => String(t.time || t.executedAt || ''),\n render: (t) => this._host.presentation.timeText(t.time || t.executedAt!),\n },\n {\n key: 'symbol',\n header: label('Sym'),\n exportable: true,\n value: (t) => t.symbol,\n },\n {\n key: 'price',\n header: label('Price'),\n exportable: true,\n value: (t) => t.price,\n render: (t) => formatPrice(t.price),\n },\n {\n key: 'quantity',\n header: label('Qty'),\n exportable: true,\n value: (t) => t.quantity,\n render: (t) => formatQty(t.quantity),\n },\n {\n key: 'side',\n header: label('Side'),\n exportable: true,\n value: (t) => t.side,\n render: (t) => presentation.sideText(t.side!),\n exportValue: (t) => presentation.sideText(t.side!),\n },\n ];\n }\n\n // Both tapes show the symbol column exactly while a second instrument is\n // in the feed \u2014 through the grid's own column visibility, so the user's\n // hide/show from the menu still works the rest of the time.\n _syncSymbolColumn(): void {\n const multi = this._extraSymbols.size > 0;\n for (const grid of [this._marketGrid, this._myGrid]) {\n if (!grid) continue;\n if (multi) grid.showColumn('symbol');\n else grid.hideColumn('symbol');\n }\n }\n\n /// Pin an extra instrument to this feed: subscribe it, show it alongside\n /// the primary, and remember it in the instance's state.\n async addExtraSymbol(symbol: string): Promise<void> {\n if (!symbol) return;\n const sym = String(symbol).toUpperCase();\n if (sym === this._activeSymbol || this._extraSymbols.has(sym)) return;\n this._extraSymbols.add(sym);\n // A tape needs the prints, and only the prints \u2014 the depth `Full` would\n // add is bandwidth this panel never renders.\n try { await this._host.trading.marketData.addSymbol(sym, MarketDataLevels.Tape); } catch { /* socket race */ }\n this._renderExtras();\n this._renderBubbles();\n this._persistExtras();\n }\n\n /// Unpin one, dropping its subscription and the prints already on screen.\n async removeExtraSymbol(symbol: string): Promise<void> {\n if (!symbol) return;\n const sym = String(symbol).toUpperCase();\n if (!this._extraSymbols.has(sym)) return;\n this._extraSymbols.delete(sym);\n try { await this._host.trading.marketData.removeSymbol(sym); } catch { /* socket race */ }\n this.trades = this.trades.filter(t => t.symbol !== sym);\n this.bubbleTrades = this.bubbleTrades.filter(t => t.symbol !== sym);\n this._marketGrid?.setRows([...this.trades]);\n this._renderExtras();\n this._renderBubbles();\n this._persistExtras();\n }\n\n getExtraSymbols(): string[] {\n return [...this._extraSymbols];\n }\n\n // Record what this instance pins, then ask for the layout to be flushed.\n // Two calls because they are two decisions: what the instance remembers,\n // and whether that is worth writing out now. Pinning is \u2014 losing the pin on\n // the next reload is exactly the complaint.\n _persistExtras(): void {\n this._host.persistState({ extras: [...this._extraSymbols] });\n this._host.saveLayout();\n }\n\n // Does this feed show `symbol` \u2014 as the page's active one, or pinned?\n _watches(symbol: string | undefined): boolean {\n if (!symbol) return false;\n return symbol === this._activeSymbol || this._extraSymbols.has(symbol);\n }\n\n // ---------------------------------------------------------------- the list\n\n _renderMy(): void {\n this._myGrid?.setRows(this.myTrades.map(t => this._stamp(t)));\n }\n\n _renderExtras(): void {\n // Every path that changes the pinned set comes through here, so the\n // symbol column tracks the set without a second call at each site.\n this._syncSymbolColumn();\n if (!this.extrasEl) return;\n if (this._extraSymbols.size === 0) {\n this.extrasEl.replaceChildren();\n this.extrasEl.setAttribute('hidden', '');\n return;\n }\n this.extrasEl.removeAttribute('hidden');\n const remove = this._host.t('Remove');\n const chips = [...this._extraSymbols].map((symbol) => {\n const button = makeIconButton('tf-extra-rm', remove, 'bi-x', { type: 'button' });\n button.addEventListener('click', (e) => {\n e.preventDefault();\n e.stopPropagation();\n void this.removeExtraSymbol(symbol);\n });\n return makeElement('span', 'tf-extra-chip', { role: 'listitem' }, [\n makeElement('span', 'tf-extra-sym', {}, [symbol]),\n button,\n ]);\n });\n this.extrasEl.replaceChildren(...chips);\n }\n\n // --------------------------------------------------------------- the views\n\n _bindTabs(): void {\n this._tabsEl?.addEventListener('click', (e) => {\n const button = (e.target as Element | null)?.closest('.tf-tab') as HTMLElement | null;\n if (!button) return;\n this._selectTab(button.dataset.tab || 'market');\n });\n }\n\n _selectTab(tab: string): void {\n this.tab = tab === 'my' ? 'my' : 'market';\n this._tabsEl?.querySelectorAll('.tf-tab').forEach((b) => {\n const selected = (b as HTMLElement).dataset.tab === this.tab;\n b.classList.toggle('active', selected);\n b.setAttribute('aria-selected', selected ? 'true' : 'false');\n });\n this.rootEl.classList.toggle('tf-tab-market', this.tab === 'market');\n this.rootEl.classList.toggle('tf-tab-my', this.tab === 'my');\n // The tab is also the request: opening it is when the fills are worth\n // fetching, and the portfolio is read per use because the user can have\n // switched account under a panel that is already on screen.\n if (this.tab === 'my') void this.loadMyTrades(this._host.trading.portfolioId(), this._activeSymbol);\n else this._renderBubbles();\n }\n\n _bindViewToggle(): void {\n this._viewToggleEl?.addEventListener('click', (e) => {\n const button = (e.target as Element | null)?.closest('.tf-view-btn') as HTMLElement | null;\n if (!button) return;\n const next = button.dataset.view;\n if ((next !== 'list' && next !== 'bubbles') || next === this.view) return;\n this.view = next;\n this._host.preferences.set(TradeFeedWidget.VIEW_KEY, next);\n this._applyView();\n });\n }\n\n // Which rendering is showing is two class names on the root, not a display\n // written onto elements: a narrow viewport forces the list back on, and\n // that is a stylesheet's decision to make, not this control's.\n _applyView(): void {\n const list = this.view === 'list';\n this.rootEl.classList.toggle('tf-view-list', list);\n this.rootEl.classList.toggle('tf-view-bubbles', !list);\n this._viewToggleEl?.querySelectorAll('.tf-view-btn').forEach((b) => {\n const selected = (b as HTMLElement).dataset.view === this.view;\n b.classList.toggle('active', selected);\n b.setAttribute('aria-pressed', selected ? 'true' : 'false');\n });\n if (!list) {\n this._sizeCanvas();\n this._renderBubbles();\n }\n }\n\n // -------------------------------------------------------------- the chart\n\n // The canvas in CSS pixels, and its backing store in device pixels. Without\n // the second the chart is drawn at a third of the resolution the screen has.\n _sizeCanvas(): void {\n const canvas = this.bubbleCanvas;\n if (!canvas) return;\n const rect = canvas.getBoundingClientRect();\n const ratio = typeof devicePixelRatio === 'number' && devicePixelRatio > 0 ? devicePixelRatio : 1;\n const width = Math.max(1, Math.floor(rect.width));\n const height = Math.max(1, Math.floor(rect.height));\n if (canvas.width !== width * ratio || canvas.height !== height * ratio) {\n canvas.width = width * ratio;\n canvas.height = height * ratio;\n this._bubbleCtx?.setTransform(ratio, 0, 0, ratio, 0, 0);\n }\n this._canvasSize = { width, height };\n }\n\n // The prints as the chart reads them: direction decided once by the host,\n // and a position on the shared time axis. Newest first, as the tape holds\n // them.\n _feedTicks(): FeedTick[] {\n const presentation = this._host.presentation;\n return this.bubbleTrades.map((trade, index) => ({\n symbol: trade.symbol || '',\n side: trade.side!,\n buy: presentation.isBuy(trade.side!),\n price: Number(trade.price ?? 0),\n quantity: Number(trade.quantity ?? 0),\n time: trade.time || trade.executedAt || '',\n index,\n }));\n }\n\n // One lane per watched symbol, active first. A feed with nothing pinned is\n // one lane over everything it holds, which is also the only case where the\n // lane needs no name.\n _lanes(ticks: FeedTick[]): BubbleLane[] {\n if (this._extraSymbols.size === 0)\n return [{ symbol: this._activeSymbol || '', ticks }];\n const symbols = this._activeSymbol ? [this._activeSymbol] : [];\n for (const symbol of this._extraSymbols) if (!symbols.includes(symbol)) symbols.push(symbol);\n return symbols.map(symbol => ({ symbol, ticks: ticks.filter(t => t.symbol === symbol) }));\n }\n\n _renderBubbles(): void {\n const ctx = this._bubbleCtx;\n if (!ctx || this.view !== 'bubbles') return;\n if (!this._canvasSize) this._sizeCanvas();\n const size = this._canvasSize;\n if (!size) return;\n\n ctx.clearRect(0, 0, size.width, size.height);\n this._bubbleHits = [];\n const ticks = this._feedTicks();\n if (ticks.length === 0) return;\n\n const layout = layoutBubbles({ width: size.width, height: size.height, total: ticks.length, lanes: this._lanes(ticks) });\n const palette = this._host.presentation.canvasPalette();\n ctx.font = palette.font;\n ctx.lineWidth = 1;\n\n for (const lane of layout.lanes) {\n if (lane.separatorY !== null) {\n ctx.globalAlpha = SEPARATOR_ALPHA;\n ctx.strokeStyle = palette.grid;\n TradeFeedWidget._rule(ctx, 0, size.width, lane.separatorY);\n }\n if (lane.label) {\n ctx.globalAlpha = LABEL_ALPHA;\n ctx.fillStyle = palette.grid;\n ctx.textBaseline = 'top';\n ctx.textAlign = 'left';\n ctx.fillText(lane.symbol, lane.label.x, lane.label.y);\n }\n for (const tick of lane.ticks) {\n ctx.globalAlpha = RULE_ALPHA;\n ctx.strokeStyle = palette.grid;\n TradeFeedWidget._rule(ctx, 0, layout.axisX - 2, tick.y);\n ctx.globalAlpha = LABEL_ALPHA;\n ctx.fillStyle = palette.grid;\n ctx.textBaseline = 'middle';\n ctx.textAlign = 'left';\n ctx.fillText(tick.text, layout.labelX, tick.y);\n }\n }\n\n for (const shape of layout.bubbles) {\n const colour = shape.bubble.buy ? palette.up : palette.down;\n ctx.fillStyle = colour;\n ctx.strokeStyle = colour;\n ctx.beginPath();\n ctx.arc(shape.x, shape.y, shape.radius, 0, Math.PI * 2);\n // Filled softly and outlined firmly, so overlapping prints stay\n // countable instead of merging into one blob.\n ctx.globalAlpha = FILL_ALPHA;\n ctx.fill();\n ctx.globalAlpha = STROKE_ALPHA;\n ctx.stroke();\n this._bubbleHits.push(shape);\n }\n ctx.globalAlpha = 1;\n }\n\n static _rule(ctx: CanvasRenderingContext2D, from: number, to: number, y: number): void {\n ctx.beginPath();\n ctx.moveTo(from, y);\n ctx.lineTo(to, y);\n ctx.stroke();\n }\n\n _bindBubbleHover(): void {\n const canvas = this.bubbleCanvas;\n if (!canvas) return;\n canvas.addEventListener('mousemove', (e: MouseEvent) => {\n if (this.view !== 'bubbles') { this._hideTooltip(); return; }\n const rect = canvas.getBoundingClientRect();\n const x = e.clientX - rect.left;\n const y = e.clientY - rect.top;\n const hit = this._hitTest(x, y);\n if (hit) this._showTooltip(hit.bubble, x, y);\n else this._hideTooltip();\n });\n canvas.addEventListener('mouseleave', () => this._hideTooltip());\n }\n\n // Backwards, so the circle drawn last \u2014 the newest print, on top \u2014 is the\n // one a cursor over both picks.\n _hitTest(x: number, y: number): BubbleShape | null {\n for (let i = this._bubbleHits.length - 1; i >= 0; i--) {\n const shape = this._bubbleHits[i];\n const dx = x - shape.x;\n const dy = y - shape.y;\n const reach = Math.max(shape.radius, HOVER_SLACK);\n if (dx * dx + dy * dy <= reach * reach) return shape;\n }\n return null;\n }\n\n _showTooltip(bubble: FeedBubble, x: number, y: number): void {\n const tooltip = this._tooltipEl;\n const canvas = this.bubbleCanvas;\n if (!tooltip || !canvas) return;\n const presentation = this._host.presentation;\n const aggregated = bubble.count > 1;\n // The raw side the print carried, not a spelling this file picked:\n // wording and colouring it are the host's, whichever of the three\n // spellings its wire uses.\n const side = bubble.side;\n\n const lines: HTMLElement[] = [];\n if (this._extraSymbols.size > 0) lines.push(this._tooltipLine(this._host.t('Symbol'), bubble.symbol, ''));\n lines.push(this._tooltipLine(this._host.t('Time'), this._host.presentation.timeText(bubble.time), ''));\n lines.push(this._tooltipLine(aggregated ? this._host.t('VWAP') : this._host.t('Price'), formatPrice(bubble.price), ''));\n lines.push(this._tooltipLine(aggregated ? this._host.t('Total qty') : this._host.t('Qty'), formatQty(bubble.quantity), ''));\n lines.push(this._tooltipLine(this._host.t('Side'), presentation.sideText(side), presentation.sideClass(side)));\n if (aggregated) lines.push(this._tooltipLine(this._host.t('Trades'), String(bubble.count), ''));\n tooltip.replaceChildren(...lines);\n\n // Measured, then placed: the size is only known once it is showing, and\n // it is clamped inside the canvas so a print near the right or bottom\n // edge does not push its own tooltip out of view.\n tooltip.removeAttribute('hidden');\n const canvasRect = canvas.getBoundingClientRect();\n const parentRect = tooltip.offsetParent?.getBoundingClientRect() ?? canvasRect;\n const originX = canvasRect.left - parentRect.left;\n const originY = canvasRect.top - parentRect.top;\n const maxX = originX + canvasRect.width - tooltip.offsetWidth - TOOLTIP_MARGIN;\n const maxY = originY + canvasRect.height - tooltip.offsetHeight - TOOLTIP_MARGIN;\n tooltip.style.left = `${Math.max(0, Math.min(originX + x + TOOLTIP_OFFSET, maxX))}px`;\n tooltip.style.top = `${Math.max(0, Math.min(originY + y + TOOLTIP_OFFSET, maxY))}px`;\n }\n\n _tooltipLine(label: string, value: string, valueClass: string): HTMLElement {\n const row = makeElement('div', 'tf-bbtt-row', {}, [\n makeElement('span', 'tf-bbtt-label', {}, [label]),\n makeElement('span', 'tf-bbtt-value', {}, [value]),\n ]);\n for (const name of valueClass.split(' ')) if (name) (row.childNodes[1] as HTMLElement).classList.add(name);\n return row;\n }\n\n _hideTooltip(): void {\n this._tooltipEl?.setAttribute('hidden', '');\n }\n}\n"],
5
- "mappings": "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",
6
- "names": ["i"]
3
+ "sources": ["../src/index.ts", "../src/trading-host.ts", "../src/control-types.ts", "../src/formatters.ts", "../src/dom.ts", "../src/grid-menu.ts", "../node_modules/@stocksharp/grids/src/table-sort.ts", "../node_modules/@stocksharp/grids/src/context-menu.ts", "../node_modules/@stocksharp/grids/src/filter-dialog.ts", "../node_modules/@stocksharp/grids/src/table-export.ts", "../node_modules/@stocksharp/grids/src/data-grid.ts", "../src/active-orders-widget.ts", "../src/positions-widget.ts", "../src/trade-history-widget.ts", "../src/watchlist-widget.ts", "../src/order-entry-widget.ts", "../src/orderbook-depth.ts", "../src/orderbook-widget.ts", "../src/pnl-curve.ts", "../src/black-scholes.ts", "../src/option-desk-widget.ts", "../src/strategies-widget.ts", "../src/log-tree.ts", "../src/log-monitor-widget.ts", "../src/statistics-widget.ts", "../src/tradefeed-aggregator.ts", "../src/tradefeed-bubbles.ts", "../src/tradefeed-widget.ts"],
4
+ "sourcesContent": ["export { MarketDataLevels, PRESENTATION_CLASSES, assertHost } from './trading-host.js';\n\nexport type {\n HostStore,\n MarketDataClient,\n MarketDataLevel,\n TickerSink,\n TradingApi,\n TradingContext,\n TradingControl,\n TradingHost,\n TradingPresentation,\n} from './trading-host.js';\n\nexport type {\n BalanceRow,\n InstrumentRow,\n OrderRow,\n OrderSide,\n OrderStatus,\n OrderType,\n PositionRow,\n QuoteStats,\n StatisticRow,\n TradeRow,\n} from './trading-data.js';\n\nexport { ControlTypes } from './control-types.js';\n\nexport type { ControlType } from './control-types.js';\n\nexport { cleanRejectReason, formatPnl, formatPrice, formatQty, formatTime } from './formatters.js';\n\n// A host that renders its own chrome around these controls builds it with the\n// same helpers, so the panel it wraps and the panel it wraps them in agree on\n// class names.\nexport { makeElement, makeIcon, makeIconButton, makePanelId, makePanelRoot } from './dom.js';\n\nexport { ActiveOrdersWidget, OrderStates } from './active-orders-widget.js';\n\nexport type { ActiveOrdersDeps } from './active-orders-widget.js';\n\nexport { PositionsWidget } from './positions-widget.js';\n\nexport type { PositionsDeps } from './positions-widget.js';\n\nexport { TradeHistoryWidget } from './trade-history-widget.js';\n\nexport type { TradeHistoryDeps } from './trade-history-widget.js';\n\nexport { WatchlistWidget } from './watchlist-widget.js';\n\nexport type { WatchlistDeps } from './watchlist-widget.js';\n\nexport { OrderEntrySides, OrderEntryTypes, OrderEntryWidget } from './order-entry-widget.js';\n\nexport type { OrderEntryDeps, OrderEntrySide, OrderEntryType, OrderEntryValues } from './order-entry-widget.js';\n\nexport type { InstrumentSpec } from './trading-data.js';\n\nexport { OrderBookWidget } from './orderbook-widget.js';\n\nexport type { OrderBookDeps, OrderBookView } from './orderbook-widget.js';\n\nexport type { BookLevel, OrderBookFrame, QuoteLevel } from './trading-data.js';\n\n// The palette a host answers with for the parts a control draws on a canvas,\n// where a class name cannot reach.\nexport type { CanvasPalette } from './trading-host.js';\n\n// The cumulative P&L curve, shared by everything that draws one: a strategy row's\n// sparkline and a statistics card are the same curve at two sizes. A backtest's own\n// equity is not here - that one shares the price axis and belongs to the chart engine.\nexport { compressPnl, drawPnlCurve, pnlCurve } from './pnl-curve.js';\n\nexport type { PnlBox, PnlCurve, PnlCurveContext, PnlCurveStyle, PnlPoint } from './pnl-curve.js';\n\nexport { OptionDeskWidget, greekPlaces, greekScales, scaleChain, sideGreeks } from './option-desk-widget.js';\n\nexport type { OptionChainContext, OptionDeskDeps, OptionSide, OptionStrike } from './option-desk-widget.js';\n\n// Option pricing, for a host that has quotes and an expiry but no pricing service. A host\n// that computes its own greeks sends them instead and none of this is reached.\nexport { OptionTypes, d1, d2, greeks, impliedVolatility, normalCdf, normalPdf, premium } from './black-scholes.js';\n\nexport type { Greeks, OptionInputs, OptionType } from './black-scholes.js';\n\nexport { StrategiesWidget, StrategyStates } from './strategies-widget.js';\n\nexport type { StrategiesActions, StrategiesDeps, StrategyRow, StrategyState } from './strategies-widget.js';\n\nexport { LogMonitorWidget } from './log-monitor-widget.js';\n\nexport type { LogMonitorDeps } from './log-monitor-widget.js';\n\n// The log's own shapes: how sources nest, and which messages a view of them keeps. A host\n// rendering its own view of the same feed reads these rather than reinventing the filter.\nexport { LogLevels, buildLogTree, keepLog, subtreeOf } from './log-tree.js';\n\nexport type { LogLevel, LogMessageRow, LogSourceNode, LogTreeNode, LogView } from './log-tree.js';\n\nexport { StatisticsWidget, formatStatistic } from './statistics-widget.js';\n\nexport type { StatisticsDeps } from './statistics-widget.js';\n\nexport { TradeFeedWidget } from './tradefeed-widget.js';\n\nexport type { TradeFeedDeps } from './tradefeed-widget.js';\n\n// The tape's own shapes: one print as the chart reads it, and the bubble a\n// print (or a slice of them) is drawn as. A host that renders its own view of\n// the same feed reads these rather than reinventing the compaction.\nexport { aggregateBubbles } from './tradefeed-aggregator.js';\n\nexport type { FeedBubble, FeedTick } from './tradefeed-aggregator.js';\n\nexport { layoutBubbles } from './tradefeed-bubbles.js';\n\nexport type { BubbleAxisTick, BubbleLane, BubbleLaneShape, BubbleLayout, BubbleLayoutInput, BubbleShape } from './tradefeed-bubbles.js';\n\n// The grid's own menu, worded by the host. Exported because a host that builds a table of its\n// own - a run's log beside the blotters, say - needs the same menu in the same words, and the\n// alternative is every consumer restating thirty labels the package already knows.\nexport { makeGridMenu } from './grid-menu.js';\n", "// The host port \u2014 the whole of what a trading control needs from whoever\n// embeds it.\n//\n// A control is written against this interface and nothing else: it never\n// imports a translator, a preference singleton, a panel registry or a docking\n// manager, and it never reaches a window global. A host \u2014 a terminal page, a\n// backoffice screen, a demo harness \u2014 supplies one object shaped like\n// `TradingHost` per control instance, and that object is the only way out of\n// the control.\n//\n// This module imports nothing but the row shapes it names. It is the contract,\n// so it must be readable by a host that shares none of the package's internals.\n//\n// Everything here is REQUIRED, and `assertHost` proves it at construction.\n// There is no optional member and no default, because a half-supplied host is\n// how a control half-works: it renders, it looks alive, and the one capability\n// nobody wired is discovered by a user clicking something that does nothing.\n// A missing member has to be a loud failure at wiring time, not a quiet one at\n// click time.\n//\n// Every member here has a caller among the controls this package ships. The\n// seven that once looked speculative \u2014 `spawn`, `persistState`, `saveLayout`,\n// `log`, `trading.pickInstrument`, `marketData.resubscribe` and\n// `marketData.getOrders` \u2014 are the order book's and the trade feed's: a ladder\n// recovers from a sequence gap with `resubscribe` without unsubscribing its\n// neighbours, badges its own resting size from `getOrders`, reports the wire\n// anomalies a user cannot see through `log`, and retargets itself through\n// `pickInstrument`; both of them `spawn` a sibling and record what they\n// remember with `persistState` + `saveLayout`.\n//\n// The cost of \"everything is required\" is still real and worth stating: a host\n// adopting a subset must supply members that subset never calls. They are cheap\n// (a no-op `spawn`, a `log` that forwards to the console) \u2014 but they are not\n// free, and an adopter who supplies stubs is not doing anything wrong.\nimport type { InstrumentRow, OrderRow, OrderSide, OrderStatus, OrderType, QuoteStats, TradeRow } from './trading-data.js';\n\n/// The colours a control has to paint with as values rather than as class\n/// names.\n///\n/// A class name cannot reach a `<canvas>`: the pixels are drawn by the control,\n/// so the palette has to arrive as strings. Every other look in this package is\n/// a class the host's stylesheet gives meaning to, and this is the one place\n/// that arrangement cannot hold \u2014 which is why the colours come from the host\n/// here too, rather than being written into the package as literals the host\n/// never chose. Each value is any CSS colour the host's palette uses; a control\n/// varies the opacity itself, so a solid colour is the expected answer.\nexport interface CanvasPalette {\n /// The rising / bid / buy direction \u2014 the colour behind `side-buy`.\n up: string;\n /// The falling / ask / sell direction \u2014 the colour behind `side-sell`.\n down: string;\n /// Dividers, gridlines and axes drawn on a canvas \u2014 axis labels included.\n grid: string;\n /// Type for the text a control draws on a canvas, as the `font` shorthand\n /// (`'10px monospace'`). A canvas inherits nothing from the page and\n /// resolves no custom property, so a font is the one measurement the host\n /// has to state as a value; the alternative is the package picking a size\n /// and a family the host's design system never chose.\n font: string;\n}\n\n/// How a host words and colours the trading vocabulary. The control owns the\n/// data; the host owns the language and the stylesheet, so it says what a side\n/// or a status reads as and which CSS class carries its colour.\n///\n/// The two class methods are the one place a host has to agree with the shipped\n/// stylesheet rather than merely supply values to it. A control puts whatever\n/// string comes back straight onto the cell without inspecting it, so nothing\n/// in `src/` ever emits these names and `styles/trading-controls.css` cannot be\n/// read to discover them. They are named here, and `PRESENTATION_CLASSES` below\n/// is the same list as data \u2014 `tools/check-style-contract.mjs` asserts the\n/// stylesheet styles every one of them, so the agreement is checked rather than\n/// discovered by a cell that came out unstyled.\nexport interface TradingPresentation {\n /// Localized \"Buy\" / \"Sell\".\n sideText(side: OrderSide): string;\n /// Is this side a buy? The same knowledge `sideText` and `sideClass`\n /// already need, asked for as a predicate: a control that has to put a row\n /// on the bid or the ask half of a book is deciding structure, not wording,\n /// and the alternative is each control spelling out which of `0`, `'Buy'`\n /// and `'BUY'` the wire it happens to be fed uses.\n isBuy(side: OrderSide): boolean;\n /// Localized short order type \u2014 \"LMT\", \"MKT\", \"STP\", \"STP-LMT\". Needs both\n /// prices because a conditional order is only a stop-limit when it carries\n /// a limit price as well as a stop price.\n typeText(type: OrderType, limitPrice: number, stopPrice: number): string;\n /// Localized order status.\n statusText(status: OrderStatus): string;\n /// A moment, worded. The host owns this for the same reason it owns a side or a\n /// status: what a time should read as is not a property of the data. A live tape wants\n /// the time of day in the reader's own zone; a blotter over a finished run wants the\n /// date too, and in the zone the run was recorded in, or every row reads as the same\n /// unplaceable day. `formatTime` is this package's tape answer, exported so a host that\n /// wants exactly that need not restate it.\n timeText(value: string | number | Date): string;\n /// CSS class colouring a cell by side. The shipped stylesheet styles\n /// `side-buy` and `side-sell`; a host on its own palette may answer with\n /// its own names and style those instead.\n sideClass(side: OrderSide): string;\n /// CSS class colouring a cell by the sign of a P&L figure. The shipped\n /// stylesheet styles `pnl-positive` and `pnl-negative`; the empty string is\n /// a legitimate answer for \"no colour\".\n pnlClass(pnl: number): string;\n /// The palette for the parts a control draws itself. Read per paint, so a\n /// host that switches theme repaints in the new colours without the control\n /// being told a theme exists.\n canvasPalette(): CanvasPalette;\n}\n\n/// The class names `TradingPresentation` is expected to return, as data.\n///\n/// Exported because it is a contract, not a detail: a host writing its own\n/// presentation reads this to know which names the shipped stylesheet already\n/// paints, and the style-contract check reads it to prove they are all styled.\nexport const PRESENTATION_CLASSES = {\n sideClass: ['side-buy', 'side-sell'],\n pnlClass: ['pnl-positive', 'pnl-negative'],\n} as const;\n\n/// A string-keyed store. Two of these reach a control and they are NOT\n/// interchangeable \u2014 see `TradingHost.preferences` and `TradingHost.cache`.\nexport interface HostStore {\n /// The stored value, or `fallback` when the key was never written.\n get(key: string, fallback: string | null): string | null;\n /// Write, or clear by writing null.\n set(key: string, value: string | null): void;\n}\n\n/// The read side of the trading account, as far as a control is concerned.\n/// Narrow on purpose: a control asks for the few calls it makes rather than\n/// being handed a whole API client to explore.\nexport interface TradingApi {\n /// Executed trades, newest first, for one portfolio. A null `symbol` means\n /// every instrument \u2014 the argument is stated either way rather than being\n /// left off, so a call site says which of the two it wants.\n getExecutions(portfolioId: number, symbol: string | null, limit: number): Promise<TradeRow[]>;\n /// Instruments matching a query; the empty query means \"everything\".\n searchInstruments(query: string): Promise<InstrumentRow[]>;\n}\n\n/// How much of a symbol a control wants streamed. A quote row needs the last\n/// price, a tape needs every print, a ladder needs the book as well \u2014 and each\n/// costs the wire something different, so a control states which it is asking\n/// for rather than leaving the argument off and taking a default.\nexport const MarketDataLevels = {\n /// Level 1 only: best bid/ask and last price.\n Quotes: 'l1only',\n /// Level 1 plus every tick.\n Tape: 'tape',\n /// Level 1, ticks and market depth.\n Full: 'full',\n} as const;\n\nexport type MarketDataLevel = typeof MarketDataLevels[keyof typeof MarketDataLevels];\n\n/// The live market-data connection. `addSymbol` / `removeSymbol` are\n/// refcounted by the implementation, so two controls watching one symbol hold\n/// one subscription between them and neither can unsubscribe the other.\nexport interface MarketDataClient {\n addSymbol(symbol: string, level: MarketDataLevel): Promise<unknown>;\n removeSymbol(symbol: string): Promise<unknown>;\n /// Make the server resend `symbol` from scratch, without touching the\n /// refcount. A control applying diffs against a sequence cannot recover\n /// from a missed frame on its own \u2014 the only cure is a fresh snapshot, and\n /// asking for one must not unsubscribe the other controls watching the\n /// same symbol.\n resubscribe(symbol: string, level: MarketDataLevel): Promise<void>;\n /// The client's cache of this session's own orders. The order book paints\n /// its \"you have size here\" badge from this rather than subscribing again.\n getOrders(): OrderRow[];\n}\n\n/// What a control reads about the account and the instrument universe.\nexport interface TradingContext {\n readonly api: TradingApi;\n readonly marketData: MarketDataClient;\n /// The portfolio a control should load against, read per use rather than\n /// captured \u2014 the user can switch portfolio under a control that is\n /// already on screen.\n portfolioId(): number | null;\n /// Ask the host to let the user pick an instrument. The host owns the\n /// picker UI; the control only learns what was chosen, and learns nothing\n /// if the user dismisses it.\n pickInstrument(onPicked: (symbol: string) => void): void;\n}\n\n/// Where a control's summary of what it is showing goes. A status-bar ticker is\n/// page chrome outside any control, so a control reports and the host decides\n/// what to do with the report.\nexport interface TickerSink {\n publish(symbols: string[], stats: Map<string, QuoteStats>): void;\n}\n\n/// One live control instance, from the host's side. Deliberately opaque: the\n/// host holds a heterogeneous set and only ever hands instances back to the\n/// control that asked for them, which is what `broadcast<T>` expresses.\nexport type TradingControl = object;\n\n/// Everything a trading control needs from its host.\nexport interface TradingHost {\n // -- this instance's role on the page ---------------------------------\n /// True when this instance is the one that speaks for the page. A page has\n /// one status-bar ticker and a control can be duplicated, so somebody has\n /// to decide which duplicate feeds it \u2014 and that is host policy, not\n /// something a control can work out about itself.\n readonly isPrimary: boolean;\n\n // -- language and wording ---------------------------------------------\n /// Translate. `key` is the English source text; `{0}`, `{1}` \u2026 in the\n /// translation are replaced positionally from `args`.\n t(key: string, ...args: unknown[]): string;\n readonly presentation: TradingPresentation;\n\n // -- storage ------------------------------------------------------------\n /// Settings that belong to the user and are expected to survive \u2014 a chosen\n /// depth, a view mode, a favourites list. May be slow and may be shared\n /// across devices.\n readonly preferences: HostStore;\n /// Local scratch that may be thrown away \u2014 a per-day price baseline, a\n /// memoised computation. Deliberately a different store from\n /// `preferences`: writing high-churn per-symbol keys into a synced\n /// per-user settings blob is how a settings row becomes a cache.\n readonly cache: HostStore;\n\n // -- the trading context it reads ---------------------------------------\n readonly trading: TradingContext;\n\n // -- where its ticker updates go ----------------------------------------\n readonly ticker: TickerSink;\n\n // -- permission and diagnostics -----------------------------------------\n /// May the user do this? `action` is the English name of the attempt\n /// (\"add to favorites\"); the host translates it and may show a sign-in\n /// prompt before answering false.\n allow(action: string): boolean;\n /// Send a diagnostic line wherever this host collects them. Controls use\n /// it for wire anomalies the user cannot see and support has to.\n log(message: string): void;\n\n // -- lifecycle calls it makes back --------------------------------------\n /// This control wants to go away.\n close(): void;\n /// The `+` gesture: another control of the same kind, starting from\n /// `state`. The host knows what kind this one is, so the control does not\n /// name its own type.\n spawn(state: Record<string, unknown>): void;\n /// Remember this control's per-instance state. Recording only \u2014 call\n /// `saveLayout` when the change is worth flushing.\n persistState(patch: Record<string, unknown>): void;\n /// Write the layout out now, bypassing any debounce the host applies.\n saveLayout(): void;\n\n // -- the host's handle on live instances --------------------------------\n /// Announce this instance. A host fans incoming data to every live control\n /// of a kind, so an unregistered control is a control that never updates.\n register(control: TradingControl): void;\n unregister(control: TradingControl): void;\n /// Apply something to every live control of this kind, this one included.\n /// The caller knows what its own siblings are, which is why `T` is its to\n /// name.\n broadcast<T>(apply: (control: T) => void): void;\n}\n\n/// Prove a host object satisfies the port, and say precisely what is missing\n/// when it does not.\n///\n/// Called from every control's constructor before anything else, so a\n/// mis-wired host fails at construction with the member named, instead of at\n/// the first click that needed it.\nexport function assertHost(host: TradingHost, controlName: string): TradingHost {\n _required(host, 'host', 'object', controlName);\n\n _required(host.isPrimary, 'host.isPrimary', 'boolean', controlName);\n _required(host.t, 'host.t', 'function', controlName);\n\n _required(host.presentation, 'host.presentation', 'object', controlName);\n _required(host.presentation.sideText, 'host.presentation.sideText', 'function', controlName);\n _required(host.presentation.isBuy, 'host.presentation.isBuy', 'function', controlName);\n _required(host.presentation.typeText, 'host.presentation.typeText', 'function', controlName);\n _required(host.presentation.statusText, 'host.presentation.statusText', 'function', controlName);\n _required(host.presentation.timeText, 'host.presentation.timeText', 'function', controlName);\n _required(host.presentation.sideClass, 'host.presentation.sideClass', 'function', controlName);\n _required(host.presentation.pnlClass, 'host.presentation.pnlClass', 'function', controlName);\n _required(host.presentation.canvasPalette, 'host.presentation.canvasPalette', 'function', controlName);\n\n _required(host.preferences, 'host.preferences', 'object', controlName);\n _required(host.preferences.get, 'host.preferences.get', 'function', controlName);\n _required(host.preferences.set, 'host.preferences.set', 'function', controlName);\n\n _required(host.cache, 'host.cache', 'object', controlName);\n _required(host.cache.get, 'host.cache.get', 'function', controlName);\n _required(host.cache.set, 'host.cache.set', 'function', controlName);\n\n _required(host.trading, 'host.trading', 'object', controlName);\n _required(host.trading.api, 'host.trading.api', 'object', controlName);\n // The calls, not just the objects holding them. An `api: {}` is the shape a\n // half-wired host actually has \u2014 the object gets built, the methods get\n // forgotten \u2014 so stopping at the object would skip exactly the members this\n // function exists to catch.\n _required(host.trading.api.getExecutions, 'host.trading.api.getExecutions', 'function', controlName);\n _required(host.trading.api.searchInstruments, 'host.trading.api.searchInstruments', 'function', controlName);\n _required(host.trading.marketData, 'host.trading.marketData', 'object', controlName);\n _required(host.trading.marketData.addSymbol, 'host.trading.marketData.addSymbol', 'function', controlName);\n _required(host.trading.marketData.removeSymbol, 'host.trading.marketData.removeSymbol', 'function', controlName);\n _required(host.trading.marketData.resubscribe, 'host.trading.marketData.resubscribe', 'function', controlName);\n _required(host.trading.marketData.getOrders, 'host.trading.marketData.getOrders', 'function', controlName);\n _required(host.trading.portfolioId, 'host.trading.portfolioId', 'function', controlName);\n _required(host.trading.pickInstrument, 'host.trading.pickInstrument', 'function', controlName);\n\n _required(host.ticker, 'host.ticker', 'object', controlName);\n _required(host.ticker.publish, 'host.ticker.publish', 'function', controlName);\n\n _required(host.allow, 'host.allow', 'function', controlName);\n _required(host.log, 'host.log', 'function', controlName);\n\n _required(host.close, 'host.close', 'function', controlName);\n _required(host.spawn, 'host.spawn', 'function', controlName);\n _required(host.persistState, 'host.persistState', 'function', controlName);\n _required(host.saveLayout, 'host.saveLayout', 'function', controlName);\n\n _required(host.register, 'host.register', 'function', controlName);\n _required(host.unregister, 'host.unregister', 'function', controlName);\n _required(host.broadcast, 'host.broadcast', 'function', controlName);\n\n return host;\n}\n\n// `typeof null` is 'object', which would let a null store through the object\n// checks \u2014 the one case worth spelling out, because null is exactly what an\n// unwired dependency looks like.\nfunction _required(value: unknown, path: string, kind: string, controlName: string): void {\n if (value === null || value === undefined || typeof value !== kind)\n throw new Error(`${controlName}: ${path} is required`);\n}\n", "// The identifiers a host and a control agree on for each kind of control this\n// package ships.\n//\n// These strings used to be spelled out independently in four places \u2014 a docking\n// manager's panel table, its default layouts, each control's static TYPE\n// literal and a Razor panels menu \u2014 so a typo in any one of them produced a\n// panel that opened but never received data, with nothing to compare against.\n//\n// The VALUES are load-bearing and must not be edited. A host writes them into\n// whatever it persists its layout as, so renaming one orphans every saved\n// layout that mentions it.\n//\n// Only the kinds this package implements are named here. A host that has more\n// control kinds of its own declares those itself and merges the two sets; that\n// keeps every identifier declared exactly once, by whoever owns the control it\n// names.\nexport const ControlTypes = {\n Watchlist: 'watchlist',\n ActiveOrders: 'activeOrders',\n TradeHistory: 'tradeHistory',\n Positions: 'positions',\n OrderBook: 'orderbook',\n TradeFeed: 'tradefeed',\n OrderEntry: 'orderEntry',\n Statistics: 'statistics',\n LogMonitor: 'logMonitor',\n Strategies: 'strategies',\n OptionDesk: 'optionDesk',\n} as const;\n\nexport type ControlType = typeof ControlTypes[keyof typeof ControlTypes];\n", "// Value formatters \u2014 pure functions over numbers, prices, quantities, times\n// and one venue string.\n//\n// Nothing here reaches a translator, the DOM, a singleton or a window global,\n// and the module has no import and no side effect. That is why it is its own\n// file: this is the half of the presentation a control needs in ANY host. The\n// other half \u2014 a host's own vocabulary for a side, a type, a status \u2014 is\n// `TradingPresentation`, which is handed to a control rather than imported by\n// it.\ntype Numeric = number | string | null | undefined;\n\n/// Digits chosen from the magnitude of the price. A fixed two decimals\n/// collapses distinct ticks of a penny instrument onto the same \"0.44\" \u2014\n/// on the Y axis, in the order book, in the price header, everywhere \u2014 while\n/// six decimals on a $76,000 print is noise.\nexport function formatPrice(price: Numeric): string {\n if (price == null || isNaN(Number(price))) return '--';\n const n = Number(price);\n const a = Math.abs(n);\n let prec = 2;\n if (a < 1) prec = 4;\n if (a < 0.1) prec = 5;\n if (a < 0.001) prec = 6;\n if (a >= 1000) prec = 1;\n if (a >= 10000) prec = 0;\n return n.toFixed(prec);\n}\n\n/// Crypto quantities carry up to 8 decimals, so the cap is 8 rather than the\n/// locale default of 3; trailing zeros are trimmed because a size of \"1\" reads\n/// better than \"1.00000000\" in a ladder.\nexport function formatQty(qty: Numeric): string {\n if (qty == null) return '--';\n const n = Number(qty);\n if (!isFinite(n)) return '--';\n return n.toLocaleString(undefined, { maximumFractionDigits: 8 });\n}\n\n/// Wall-clock time of day, 24h, seconds included \u2014 a trade blotter is read by\n/// eye against the tape, so the format is fixed rather than locale-driven.\nexport function formatTime(dateStr: string | number | Date | null | undefined): string {\n const d = new Date(dateStr as string);\n return d.toLocaleTimeString('en-US', { hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit' });\n}\n\n/// A signed figure: profit carries an explicit `+` so a gain and a loss are\n/// told apart by the number alone, without relying on the colour class.\nexport function formatPnl(pnl: Numeric): string {\n if (pnl == null) return '--';\n const val = Number(pnl);\n const sign = val >= 0 ? '+' : '';\n return sign + val.toFixed(2);\n}\n\n/// Some venue adapters hand us a wrapped error string like\n/// \"failed to complete request (err=Forbidden): {\"code\":40310000,\"message\":\"cost basis must be >= ...\"}\"\n/// The raw form is stored for audit, but users should see the human-readable\n/// message only. Pull `message` out of the embedded JSON when present;\n/// otherwise return the reason as-is.\nexport function cleanRejectReason(reason: string | null | undefined): string {\n if (!reason) return '';\n const i = reason.indexOf('{');\n const j = reason.lastIndexOf('}');\n if (i >= 0 && j > i) {\n try {\n const parsed = JSON.parse(reason.substring(i, j + 1));\n if (parsed && typeof parsed.message === 'string' && parsed.message.length > 0)\n return parsed.message;\n } catch { /* fall through to raw */ }\n }\n return reason;\n}\n", "// Element construction for controls that build their own DOM.\n//\n// A control that clones a <template> out of the page it happens to live on\n// cannot render anywhere else, so the markup has to move into the control. It\n// then has to stay readable as markup, which four lines of createElement /\n// className / setAttribute / appendChild per element does not.\n//\n// Nothing here has an import, touches a global or knows what a trading control\n// is \u2014 it is a spelling of `document.createElement`, and the shape of the call\n// mirrors the shape of the tag it replaces:\n//\n// <div class=\"panel-header\"><span>Positions</span></div>\n// makeElement('div', 'panel-header', {}, [makeElement('span', '', {}, ['Positions'])])\n\n/// One element. `className` may be empty and `attrs` may be empty \u2014 both are\n/// then left off entirely, so the result matches markup that never carried\n/// them. Children are appended in order; a string child becomes a text node.\nexport function makeElement(\n tag: string,\n className: string,\n attrs: Record<string, string>,\n children: Array<Node | string>,\n): HTMLElement {\n const element = document.createElement(tag);\n if (className) element.className = className;\n for (const [name, value] of Object.entries(attrs)) element.setAttribute(name, value);\n for (const child of children)\n element.appendChild(typeof child === 'string' ? document.createTextNode(child) : child);\n return element;\n}\n\n/// A Bootstrap icon glyph \u2014 `<i class=\"bi bi-x\"></i>`. Every icon in a control's\n/// panel chrome is one of these, always with the `bi` base class.\nexport function makeIcon(iconClass: string): HTMLElement {\n return makeElement('i', `bi ${iconClass}`, {}, []);\n}\n\n/// An icon button from the panel chrome: one glyph, and the same text as both\n/// tooltip and accessible name. `className` carries the full class list\n/// because the buttons differ by more than one modifier\n/// (`bt-icon-btn bt-icon-cancel panel-close-btn`).\nexport function makeIconButton(\n className: string,\n label: string,\n iconClass: string,\n attrs: Record<string, string>,\n): HTMLButtonElement {\n return makeElement('button', className, { title: label, 'aria-label': label, ...attrs },\n [makeIcon(iconClass)]) as HTMLButtonElement;\n}\n\n/// The panel root every control builds: `.terminal-panel` plus the control's own\n/// modifier class, carrying the region role and its accessible name.\n///\n/// The structure is a contract in two directions and both are load-bearing. A\n/// host stylesheet targets these class names, and a docking host may lift\n/// `.panel-header`'s children into its own tab strip, which needs\n/// `.terminal-panel` to be the root's only element child.\nexport function makePanelRoot(modifierClass: string, label: string, children: Array<Node | string>): HTMLElement {\n return makeElement('div', `terminal-panel ${modifierClass}`, { role: 'region', 'aria-label': label }, children);\n}\n\n/// A unique element id for one control instance. Time plus a random tail: the\n/// time alone collides when a host restores a saved layout, which creates every\n/// panel inside one millisecond.\nexport function makePanelId(type: string): string {\n return `panel-${type}-${Date.now().toString(36)}-${Math.floor(Math.random() * 1000).toString(36)}`;\n}\n", "// The wording of the grid's context menu and its filter dialog.\n//\n// `@stocksharp/grids` draws the menu and speaks English by default; a control\n// here says nothing in its own words, so every label is taken from `host.t()`\n// and handed over through the grid's `contextMenu` option. Each blotter calls\n// this at construction \u2014 the labels are read once per grid, which is the same\n// lifetime as every other caption a control renders.\n//\n// Every key below is a literal on purpose: `translation-keys.json` is generated\n// by scanning for them, and a computed key would be invisible to that scan and\n// reach a user as itself.\nimport { TradingHost } from './trading-host.js';\nimport type { GridMenuOptions } from '@stocksharp/grids/source/data-grid';\n\n/// The grid's `contextMenu` option, worded by the host. Generic over the row\n/// type only because the option is; no label depends on it.\nexport function makeGridMenu<TRow>(host: TradingHost): GridMenuOptions<TRow> {\n return {\n labels: {\n sortAsc: host.t('SortAscending'),\n sortDesc: host.t('SortDescending'),\n sortClear: host.t('ClearSort'),\n hideColumn: host.t('HideColumn'),\n showAllColumns: host.t('ShowAllColumns'),\n groupBy: host.t('GroupByColumn'),\n ungroup: host.t('Ungroup'),\n filterByValue: (text) => host.t('Filter by this value: {0}', text),\n filterRule: host.t('FilterRule'),\n showFilters: host.t('ShowFilterRow'),\n hideFilters: host.t('HideFilterRow'),\n showHeader: host.t('ShowHeaderRow'),\n hideHeader: host.t('HideHeaderRow'),\n clearFilters: host.t('ClearFilters'),\n copyCell: host.t('CopyCell'),\n copyRow: host.t('CopyRow'),\n copyRows: (count) => host.t('Copy selected rows ({0})', count),\n exportXlsx: host.t('ExportToXlsx'),\n },\n filterDialog: {\n labels: {\n title: (header) => host.t('Filter: {0}', header),\n apply: host.t('Apply'),\n clear: host.t('Clear'),\n cancel: host.t('Cancel'),\n value: host.t('Value'),\n and: host.t('FilterAnd'),\n ops: {\n contains: host.t('FilterOpContains'),\n notContains: host.t('FilterOpNotContains'),\n startsWith: host.t('FilterOpStartsWith'),\n endsWith: host.t('FilterOpEndsWith'),\n eq: host.t('FilterOpEq'),\n ne: host.t('FilterOpNe'),\n gt: host.t('FilterOpGt'),\n ge: host.t('FilterOpGe'),\n lt: host.t('FilterOpLt'),\n le: host.t('FilterOpLe'),\n between: host.t('FilterOpBetween'),\n anyOf: host.t('FilterOpAnyOf'),\n noneOf: host.t('FilterOpNoneOf'),\n empty: host.t('FilterOpEmpty'),\n notEmpty: host.t('FilterOpNotEmpty'),\n },\n },\n },\n };\n}\n", "// Column-sort controller \u2014 the sort state behind DataGrid.\n//\n// Attach to the table's <thead>: clicking a <th data-sort=\"key\"> cycles\n// asc -> desc -> default. There is no \"unsorted\" state: a table always has a\n// deterministic order. Each table declares its default (e.g. orders newest-\n// first, trades most-recent-first) so the resting view is meaningful instead of\n// exposing the raw cache/insertion order (storage hydration + snapshot + live\n// updates land in an order that means nothing to the user). Clicking a header\n// overrides the default; cycling past desc returns to it. The owner re-renders\n// through onChange and passes its row array through apply(), which returns a\n// sorted copy, so live inserts land in the active order on the next render.\n//\n// DataGrid builds the accessors from its column declarations and owns the\n// instance; it is separate from the grid because sort state is a self-contained\n// concern with its own semantics worth pinning down on its own.\n\n/**\n * The two directions a column can be sorted in. Spelled as a const object so the\n * values have names at the call site and the union stays derived from one place.\n */\nexport const SortDirections = {\n Asc: 'asc',\n Desc: 'desc',\n} as const;\n\nexport type SortDir = typeof SortDirections[keyof typeof SortDirections];\n\nexport interface SortSpec { col: string; dir: SortDir; }\n\n// A column with no declared accessor is read off the row by its own key, which\n// needs a shape TRow never promised \u2014 so the widening happens here, once, instead\n// of loosening the row type of the whole controller.\nfunction readByKey(row: unknown, key: string): unknown {\n return row === null || row === undefined ? undefined : (row as Record<string, unknown>)[key];\n}\n\nexport class TableSort<TRow = unknown> {\n col: string | null;\n dir: SortDir;\n private _headerEl: HTMLElement | null;\n private _accessors: Record<string, (row: TRow) => unknown>;\n private _onChange: () => void;\n private _default: SortSpec | null;\n private readonly _collator: Intl.Collator;\n\n // `defaultSort` is the order applied when no header is explicitly selected \u2014 the table's\n // resting sort. Pass null only for a table that genuinely has no meaningful default (raw row\n // order); every real blotter should name one so its first paint is deterministic.\n constructor(\n headerEl: HTMLElement | null,\n accessors: Record<string, (row: TRow) => unknown>,\n onChange: () => void,\n defaultSort: SortSpec | null,\n collator: Intl.Collator,\n ) {\n this.col = null;\n this.dir = SortDirections.Asc;\n this._collator = collator;\n this._headerEl = headerEl;\n this._accessors = accessors || {};\n this._onChange = onChange;\n this._default = defaultSort;\n this._updateHeader();\n\n headerEl?.addEventListener('click', (e) => {\n const tgt = e.target as Element | null;\n const th = tgt?.closest('[data-sort]') as HTMLElement | null;\n if (!th) return;\n const col = th.dataset.sort!;\n if (this.col !== col) { this.col = col; this.dir = SortDirections.Asc; }\n else if (this.dir === SortDirections.Asc) this.dir = SortDirections.Desc;\n else { this.col = null; this.dir = SortDirections.Asc; } // back to the table default\n this._updateHeader();\n this._onChange();\n });\n }\n\n // The order actually in effect: the explicitly-picked column, else the table's default.\n private _effective(): SortSpec | null {\n return this.col ? { col: this.col, dir: this.dir } : this._default;\n }\n\n /**\n * Sorted copy of rows by the effective sort (explicit column or table default). Rows with no\n * value for the sort column group at the end. Always a copy: a caller told its array is never\n * reordered must be able to rely on that for one row and no sort as much as for a sorted many.\n */\n apply(rows: TRow[]): TRow[] {\n if (!Array.isArray(rows)) return rows;\n const eff = this._effective();\n if (!eff || rows.length < 2) return [...rows];\n const col = eff.col;\n const acc = this._accessors[col] || ((r: TRow) => readByKey(r, col));\n const factor = eff.dir === SortDirections.Asc ? 1 : -1;\n return [...rows].sort((a, b) => {\n const av = acc(a);\n const bv = acc(b);\n const aEmpty = av === null || av === undefined || av === '';\n const bEmpty = bv === null || bv === undefined || bv === '';\n if (aEmpty && bEmpty) return 0;\n if (aEmpty) return 1; // empties sink regardless of direction\n if (bEmpty) return -1;\n return this._compare(av, bv) * factor;\n });\n }\n\n private _compare(a: unknown, b: unknown): number {\n const an = typeof a === 'number' ? a : Number(a);\n const bn = typeof b === 'number' ? b : Number(b);\n if (isFinite(an) && isFinite(bn)) return an - bn;\n // Through the collator the caller handed over, so the order matches the\n // language the table is written in rather than whatever the visitor's browser\n // happens to be set to -- and so it is built once instead of per comparison.\n return this._collator.compare(String(a), String(b));\n }\n\n /**\n * The column the user picked, or null while the table's own default is in effect.\n *\n * Deliberately not the effective sort: this is what gets stored and handed back\n * to `set`, and storing the default as though it were a choice would pin it --\n * a later change to the table's default would then never reach anyone who had\n * ever looked at the table.\n */\n current(): SortSpec | null {\n return this.col ? { col: this.col, dir: this.dir } : null;\n }\n\n /** Pick a column, or pass null to fall back to the table's default. */\n set(col: string | null, dir: SortDir | null): void {\n this.col = col;\n this.dir = dir || SortDirections.Asc;\n this._updateHeader();\n this._onChange();\n }\n\n /**\n * Re-mark the header cells. A grid that rebuilds its `<thead>` -- moving a column,\n * hiding one -- throws away the marks with the old cells, and the rows would then\n * sit sorted under a header that says nothing about why.\n */\n refreshHeader(): void {\n this._updateHeader();\n }\n\n private _updateHeader() {\n if (!this._headerEl) return;\n // Reflect the effective sort \u2014 including the table default \u2014 so the resting view shows why\n // the rows are ordered the way they are, not a blank header over a sorted table.\n const eff = this._effective();\n this._headerEl.querySelectorAll('[data-sort]').forEach((el: Element) => {\n const th = el as HTMLElement;\n const active = !!eff && eff.col === th.dataset.sort;\n th.classList.toggle('sort-asc', active && eff!.dir === SortDirections.Asc);\n th.classList.toggle('sort-desc', active && eff!.dir === SortDirections.Desc);\n });\n }\n}\n", "/// A context menu the grid can offer out of the box, and a host can take over.\n///\n/// The package draws no chrome anywhere else -- ColumnSettings hands the picker's\n/// markup to the host precisely so a page can use its own modal. A menu is the one\n/// place where that rule costs more than it buys: every host would write the same\n/// hundred lines to position a list at a pointer, close it on the next click and\n/// on Escape, and keep it inside the window. So the menu is built here, and the\n/// host decides its contents and its class names instead of its existence.\n\n/** One line in the menu. A separator is an item with no label. */\nexport interface GridMenuItem {\n /** Shown to the user. Omit for a separator. */\n label?: string;\n /** What the line does. A separator and a disabled line have none. */\n run?(): void;\n /** Drawn, but inert and dimmed -- so an action stays where the user expects it. */\n disabled?: boolean;\n /** Marks the line as ticked, for the toggles (a hidden column, a set filter). */\n checked?: boolean;\n}\n\n/** Class names for the menu's parts. The grid names elements; a host styles them. */\nexport interface GridMenuClasses {\n menu?: string;\n item?: string;\n separator?: string;\n disabled?: string;\n checked?: string;\n}\n\nconst DEFAULTS: Required<GridMenuClasses> = {\n menu: 'grid-menu',\n item: 'grid-menu-item',\n separator: 'grid-menu-separator',\n disabled: 'is-disabled',\n checked: 'is-checked',\n};\n\n/**\n * Shows one menu at a time for the whole page.\n *\n * One instance rather than one per grid: two menus open at once is never what a\n * user asked for, and a second grid opening its own would leave the first hanging\n * with no way back to it.\n */\nexport class GridContextMenu {\n private static _open: GridContextMenu | null = null;\n\n private readonly _classes: Required<GridMenuClasses>;\n private _el: HTMLElement | null = null;\n private _dismiss: (() => void) | null = null;\n\n constructor(classes?: GridMenuClasses) {\n this._classes = { ...DEFAULTS, ...(classes || {}) };\n }\n\n /** Whether this menu is the one currently on screen. */\n get isOpen(): boolean {\n return this._el !== null;\n }\n\n /**\n * Draw the items at (x, y) in viewport coordinates. Opening a menu closes\n * whichever one was open, including this one -- a right-click inside an open\n * menu means \"somewhere else\", not \"two menus\".\n */\n open(items: GridMenuItem[], x: number, y: number): void {\n GridContextMenu._open?.close();\n if (items.length === 0) return;\n\n const el = document.createElement('div');\n el.className = this._classes.menu;\n el.setAttribute('role', 'menu');\n el.style.position = 'fixed';\n el.style.left = `${x}px`;\n el.style.top = `${y}px`;\n\n for (const item of items) el.appendChild(this._itemEl(item));\n\n document.body.appendChild(el);\n this._el = el;\n GridContextMenu._open = this;\n\n // Bound now rather than on a timer. The gesture that opens a menu is over by\n // this point -- a right-click fires mousedown before contextmenu, and a menu\n // opened from a button is opened on the click that ends one -- so there is no\n // opening event left to dismiss it, and a timer would only make when it starts\n // listening depend on the event loop.\n const dismiss = (event: Event) => {\n if (event.type === 'keydown' && (event as KeyboardEvent).key !== 'Escape') return;\n // A press INSIDE the menu is the user choosing something. This listener runs\n // in the capture phase, before the click reaches the item, so closing here\n // would tear the item out from under the gesture and the menu would look\n // dead: it vanishes and nothing happens.\n const target = (event as unknown as { target?: Node }).target;\n if (event.type === 'mousedown' && target && el.contains(target)) return;\n this.close();\n };\n document.addEventListener('mousedown', dismiss, true);\n document.addEventListener('keydown', dismiss, true);\n window.addEventListener('resize', dismiss);\n window.addEventListener('scroll', dismiss, true);\n this._dismiss = () => {\n document.removeEventListener('mousedown', dismiss, true);\n document.removeEventListener('keydown', dismiss, true);\n window.removeEventListener('resize', dismiss);\n window.removeEventListener('scroll', dismiss, true);\n };\n\n this._keepInside(el, x, y);\n }\n\n close(): void {\n if (!this._el) return;\n this._dismiss?.();\n this._dismiss = null;\n this._el.parentNode?.removeChild(this._el);\n this._el = null;\n if (GridContextMenu._open === this) GridContextMenu._open = null;\n }\n\n private _itemEl(item: GridMenuItem): HTMLElement {\n if (!item.label) {\n const sep = document.createElement('div');\n sep.className = this._classes.separator;\n sep.setAttribute('role', 'separator');\n return sep;\n }\n\n const el = document.createElement('div');\n el.className = this._classes.item;\n if (item.disabled) el.className += ` ${this._classes.disabled}`;\n if (item.checked) el.className += ` ${this._classes.checked}`;\n el.setAttribute('role', 'menuitem');\n el.textContent = item.label;\n\n if (!item.disabled && item.run) {\n el.addEventListener('click', () => {\n // Close first: an action that repaints the grid would otherwise run\n // with a menu still floating over rows that no longer exist.\n this.close();\n item.run!();\n });\n }\n return el;\n }\n\n /**\n * Nudge the menu back inside the window when it was opened near an edge.\n * Measured after it is in the document, because until then it has no size.\n */\n private _keepInside(el: HTMLElement, x: number, y: number): void {\n const rect = typeof el.getBoundingClientRect === 'function' ? el.getBoundingClientRect() : null;\n if (!rect) return;\n\n const width = typeof window !== 'undefined' ? window.innerWidth : 0;\n const height = typeof window !== 'undefined' ? window.innerHeight : 0;\n if (width && x + rect.width > width) el.style.left = `${Math.max(0, width - rect.width)}px`;\n if (height && y + rect.height > height) el.style.top = `${Math.max(0, height - rect.height)}px`;\n }\n}\n", "/// The popup a column's filter rule is built in: an operator and its operand.\n///\n/// The inline filter row stays what it is -- one box per column, a substring or a\n/// range, typed without leaving the table. It cannot express \"not equal\", \"starts\n/// with\" or \"is empty\" without growing a second control per column, and a header\n/// row is the wrong place to grow controls. So the rules live here, one column at\n/// a time, opened from the menu or from the funnel in the filter cell.\n///\n/// Same bargain as the context menu: the package builds the popup because every\n/// host would otherwise write the same positioning and dismissal, and the host\n/// decides its class names and its wording.\n\nimport type { GridFilter, GridFilterKind, GridFilterOp, GridValueOption } from './data-grid.js';\n\n/** Class names for the dialog's parts. */\nexport interface GridFilterDialogClasses {\n dialog?: string;\n title?: string;\n row?: string;\n select?: string;\n input?: string;\n /** The scrolling list of values a set filter is picked from. */\n values?: string;\n /** One tickable value in that list. */\n valuesItem?: string;\n actions?: string;\n apply?: string;\n clear?: string;\n cancel?: string;\n}\n\n/** Every phrase the dialog shows. */\nexport interface GridFilterDialogLabels {\n /**\n * Heads the dialog with the column it is filtering. A function because joining a\n * word to a column name is a language's business, down to the colon.\n */\n title(header: string): string;\n apply: string;\n clear: string;\n cancel: string;\n value: string;\n and: string;\n ops: Record<GridFilterOp, string>;\n}\n\nconst DEFAULT_CLASSES: Required<GridFilterDialogClasses> = {\n dialog: 'grid-filter-dialog',\n title: 'grid-filter-dialog-title',\n row: 'grid-filter-dialog-row',\n select: 'grid-filter-dialog-op',\n input: 'grid-filter-dialog-value',\n values: 'grid-filter-dialog-values',\n valuesItem: 'grid-filter-dialog-values-item',\n actions: 'grid-filter-dialog-actions',\n apply: 'grid-filter-dialog-apply',\n clear: 'grid-filter-dialog-clear',\n cancel: 'grid-filter-dialog-cancel',\n};\n\nconst DEFAULT_LABELS: GridFilterDialogLabels = {\n title: header => `Filter: ${header}`,\n apply: 'Apply',\n clear: 'Clear',\n cancel: 'Cancel',\n value: 'Value',\n and: 'and',\n ops: {\n contains: 'contains',\n notContains: 'does not contain',\n startsWith: 'starts with',\n endsWith: 'ends with',\n eq: '=',\n ne: '\u2260',\n gt: '>',\n ge: '\u2265',\n lt: '<',\n le: '\u2264',\n between: 'between',\n anyOf: 'is any of',\n noneOf: 'is none of',\n empty: 'is empty',\n notEmpty: 'is not empty',\n },\n};\n\n/// Which rules make sense for which kind of column. A number column offering\n/// \"starts with\" invites a filter that reads as a mistake; a text column offering\n/// only comparisons cannot express the thing text filters are actually for.\nconst OPS: Record<GridFilterKind, GridFilterOp[]> = {\n text: ['contains', 'notContains', 'startsWith', 'endsWith', 'eq', 'ne', 'empty', 'notEmpty'],\n number: ['eq', 'ne', 'gt', 'ge', 'lt', 'le', 'between', 'empty', 'notEmpty'],\n // A set column holds one of a handful of values, and the values are on screen to\n // be ticked. Asking such a column to be typed at is how a filter on a side column\n // ends up comparing \"Buy\" against the 0 the column actually holds.\n set: ['anyOf', 'noneOf', 'empty', 'notEmpty'],\n};\n\nconst NEEDS_NO_OPERAND = new Set<GridFilterOp>(['empty', 'notEmpty']);\nconst PICKS_VALUES = new Set<GridFilterOp>(['anyOf', 'noneOf']);\n\nexport class GridFilterDialog {\n private static _open: GridFilterDialog | null = null;\n\n private readonly _classes: Required<GridFilterDialogClasses>;\n private readonly _labels: GridFilterDialogLabels;\n private _el: HTMLElement | null = null;\n private _unbind: (() => void) | null = null;\n\n constructor(classes?: GridFilterDialogClasses, labels?: Partial<GridFilterDialogLabels>) {\n this._classes = { ...DEFAULT_CLASSES, ...(classes || {}) };\n this._labels = { ...DEFAULT_LABELS, ...(labels || {}), ops: { ...DEFAULT_LABELS.ops, ...(labels?.ops || {}) } };\n }\n\n get isOpen(): boolean {\n return this._el !== null;\n }\n\n /**\n * Ask for a filter on one column. `commit` is called with the new filter, or\n * with null when the user clears it; cancelling calls nothing at all, so a\n * dialog dismissed by mistake leaves the table exactly as it was.\n */\n open(\n options: {\n header: string;\n kind: GridFilterKind;\n current: GridFilter | null;\n /** The choices a set column is picked from; empty for every other kind. */\n values: GridValueOption[];\n x: number;\n y: number;\n },\n commit: (filter: GridFilter | null) => void,\n ): void {\n GridFilterDialog._open?.close();\n\n const ops = OPS[options.kind] || OPS.text;\n const current = options.current || {};\n const startOp: GridFilterOp = current.op\n || (current.min !== undefined || current.max !== undefined ? 'between' : ops[0]);\n const picked = new Set(current.values || []);\n\n const el = document.createElement('div');\n el.className = this._classes.dialog;\n el.setAttribute('role', 'dialog');\n el.style.position = 'fixed';\n el.style.left = `${options.x}px`;\n el.style.top = `${options.y}px`;\n\n const title = document.createElement('div');\n title.className = this._classes.title;\n title.textContent = this._labels.title(options.header);\n el.appendChild(title);\n\n const row = document.createElement('div');\n row.className = this._classes.row;\n\n const select = document.createElement('select') as HTMLSelectElement;\n select.className = this._classes.select;\n for (const op of ops) {\n const option = document.createElement('option') as HTMLOptionElement;\n option.value = op;\n option.textContent = this._labels.ops[op];\n select.appendChild(option);\n }\n select.value = startOp;\n\n const value = document.createElement('input') as HTMLInputElement;\n value.className = this._classes.input;\n value.type = options.kind === 'number' ? 'number' : 'text';\n value.placeholder = this._labels.value;\n value.value = current.text !== undefined ? current.text\n : current.min !== undefined ? String(current.min) : '';\n\n const and = document.createElement('span');\n and.textContent = ` ${this._labels.and} `;\n\n const upper = document.createElement('input') as HTMLInputElement;\n upper.className = this._classes.input;\n upper.type = 'number';\n upper.value = current.max !== undefined ? String(current.max) : '';\n\n row.appendChild(select);\n row.appendChild(value);\n row.appendChild(and);\n row.appendChild(upper);\n el.appendChild(row);\n\n // The values a set column is picked from. Each box carries the raw value the\n // filter compares, and shows the label a person reads it by.\n const boxes: HTMLInputElement[] = [];\n const list = document.createElement('div');\n list.className = this._classes.values;\n for (const option of options.values) {\n const item = document.createElement('label');\n item.className = this._classes.valuesItem;\n const box = document.createElement('input') as HTMLInputElement;\n box.type = 'checkbox';\n box.value = option.value;\n box.checked = picked.has(option.value);\n boxes.push(box);\n item.appendChild(box);\n item.appendChild(document.createTextNode(option.label));\n list.appendChild(item);\n }\n if (options.values.length) el.appendChild(list);\n\n // The operand controls follow the rule: \"is empty\" takes none, \"between\"\n // takes two. Showing all three always would ask for values the rule ignores.\n const sync = () => {\n const op = select.value as GridFilterOp;\n const picks = PICKS_VALUES.has(op);\n const none = NEEDS_NO_OPERAND.has(op) || picks;\n value.style.display = none ? 'none' : '';\n and.style.display = op === 'between' ? '' : 'none';\n upper.style.display = op === 'between' ? '' : 'none';\n list.style.display = picks ? '' : 'none';\n };\n select.addEventListener('change', sync);\n sync();\n\n const actions = document.createElement('div');\n actions.className = this._classes.actions;\n const applyButton = this._button(this._labels.apply, this._classes.apply, () => {\n const op = select.value as GridFilterOp;\n if (NEEDS_NO_OPERAND.has(op)) return commit({ op });\n if (PICKS_VALUES.has(op)) {\n const values = boxes.filter(box => box.checked).map(box => box.value);\n // Nothing ticked is not a filter that hides everything; it is no filter.\n return commit(values.length ? { op, values } : null);\n }\n if (op === 'between') {\n const min = value.value.trim() === '' ? undefined : Number(value.value);\n const max = upper.value.trim() === '' ? undefined : Number(upper.value);\n return commit(min === undefined && max === undefined ? null : { op, min, max });\n }\n return commit(value.value.trim() === '' ? null : { op, text: value.value });\n });\n actions.appendChild(applyButton);\n actions.appendChild(this._button(this._labels.clear, this._classes.clear, () => commit(null)));\n actions.appendChild(this._button(this._labels.cancel, this._classes.cancel, () => {}));\n el.appendChild(actions);\n\n document.body.appendChild(el);\n this._el = el;\n GridFilterDialog._open = this;\n\n // Same rule as the menu: a press inside is the user working in the dialog,\n // not dismissing it -- and the capture phase means this runs before the\n // control under the pointer ever sees the press.\n const dismiss = (event: Event) => {\n const target = (event as unknown as { target?: Node }).target;\n if (event.type === 'keydown') {\n const key = (event as KeyboardEvent).key;\n // Enter in the operator list picks an operator. This listener runs in\n // the capture phase, so treating that Enter as Apply would submit the\n // rule the moment the user chose what it should be -- with the operand\n // still empty, which reads as the dialog closing by itself.\n if (key === 'Enter') {\n if (target !== select) applyButton.click();\n return;\n }\n if (key !== 'Escape') return;\n }\n if (event.type === 'mousedown' && target && el.contains(target)) return;\n this.close();\n };\n document.addEventListener('mousedown', dismiss, true);\n document.addEventListener('keydown', dismiss, true);\n this._unbind = () => {\n document.removeEventListener('mousedown', dismiss, true);\n document.removeEventListener('keydown', dismiss, true);\n };\n\n if (typeof value.focus === 'function') value.focus();\n }\n\n close(): void {\n if (!this._el) return;\n this._unbind?.();\n this._unbind = null;\n this._el.parentNode?.removeChild(this._el);\n this._el = null;\n if (GridFilterDialog._open === this) GridFilterDialog._open = null;\n }\n\n private _button(label: string, className: string, run: () => void): HTMLElement {\n const btn = document.createElement('button') as HTMLButtonElement;\n btn.type = 'button';\n btn.className = className;\n btn.textContent = label;\n btn.addEventListener('click', () => {\n // Closed first, so a commit that repaints does not leave a dialog floating\n // over a table that has moved underneath it.\n this.close();\n run();\n });\n return btn;\n }\n}\n", "// Client-side .xlsx export \u2014 the sheet DataGrid hands its rows to, and usable on\n// its own by any web bundle. No dependency on a host app: the caller passes\n// generic (baseName, sheetName, headers, rows) and gets a real workbook.\n//\n// Builds a real OOXML workbook (not CSV renamed): a store-only ZIP with the\n// minimal SpreadsheetML part set. Strings are written as inline strings,\n// finite numbers as native numeric cells, so Excel / LibreOffice / Numbers\n// open the file without conversion warnings. No third-party libraries.\n\nconst XML_DECL = '<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\\r\\n';\n\nconst CONTENT_TYPES = XML_DECL\n + '<Types xmlns=\"http://schemas.openxmlformats.org/package/2006/content-types\">'\n + '<Default Extension=\"rels\" ContentType=\"application/vnd.openxmlformats-package.relationships+xml\"/>'\n + '<Default Extension=\"xml\" ContentType=\"application/xml\"/>'\n + '<Override PartName=\"/xl/workbook.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml\"/>'\n + '<Override PartName=\"/xl/worksheets/sheet1.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml\"/>'\n + '</Types>';\n\nconst ROOT_RELS = XML_DECL\n + '<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\">'\n + '<Relationship Id=\"rId1\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument\" Target=\"xl/workbook.xml\"/>'\n + '</Relationships>';\n\nconst WORKBOOK_RELS = XML_DECL\n + '<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\">'\n + '<Relationship Id=\"rId1\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet\" Target=\"worksheets/sheet1.xml\"/>'\n + '</Relationships>';\n\nfunction workbookXml(sheetName: string): string {\n return XML_DECL\n + '<workbook xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\">'\n + `<sheets><sheet name=\"${escapeXml(sheetName)}\" sheetId=\"1\" r:id=\"rId1\"/></sheets>`\n + '</workbook>';\n}\n\nfunction escapeXml(s: string): string {\n return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\n}\n\n// 0 -> \"A\", 25 -> \"Z\", 26 -> \"AA\", ...\nfunction colRef(i: number): string {\n let ref = '';\n for (let n = i; n >= 0; n = Math.floor(n / 26) - 1)\n ref = String.fromCharCode(65 + (n % 26)) + ref;\n return ref;\n}\n\nfunction cellXml(ref: string, value: unknown): string {\n if (value === null || value === undefined || value === '') return '';\n if (typeof value === 'number' && isFinite(value))\n return `<c r=\"${ref}\"><v>${value}</v></c>`;\n return `<c r=\"${ref}\" t=\"inlineStr\"><is><t xml:space=\"preserve\">${escapeXml(String(value))}</t></is></c>`;\n}\n\nfunction sheetXml(headers: string[], rows: unknown[][]): string {\n let xml = XML_DECL\n + '<worksheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\"><sheetData>';\n const all: unknown[][] = [headers, ...rows];\n for (let r = 0; r < all.length; r++) {\n xml += `<row r=\"${r + 1}\">`;\n for (let c = 0; c < all[r].length; c++)\n xml += cellXml(colRef(c) + (r + 1), all[r][c]);\n xml += '</row>';\n }\n return xml + '</sheetData></worksheet>';\n}\n\n// --- store-only ZIP writer -------------------------------------------------\n\nconst CRC_TABLE = (() => {\n const table = new Uint32Array(256);\n for (let n = 0; n < 256; n++) {\n let c = n;\n for (let k = 0; k < 8; k++)\n c = (c & 1) ? 0xEDB88320 ^ (c >>> 1) : c >>> 1;\n table[n] = c >>> 0;\n }\n return table;\n})();\n\nfunction crc32(data: Uint8Array): number {\n let crc = 0xFFFFFFFF;\n for (let i = 0; i < data.length; i++)\n crc = CRC_TABLE[(crc ^ data[i]) & 0xFF] ^ (crc >>> 8);\n return (crc ^ 0xFFFFFFFF) >>> 0;\n}\n\nfunction zip(entries: { name: string; text: string }[]): Blob {\n const encoder = new TextEncoder();\n const parts: Uint8Array[] = [];\n const central: Uint8Array[] = [];\n let offset = 0;\n\n for (const entry of entries) {\n const name = encoder.encode(entry.name);\n const data = encoder.encode(entry.text);\n const crc = crc32(data);\n\n const local = new Uint8Array(30 + name.length);\n const lv = new DataView(local.buffer);\n lv.setUint32(0, 0x04034b50, true); // local file header signature\n lv.setUint16(4, 20, true); // version needed\n lv.setUint32(14, crc, true);\n lv.setUint32(18, data.length, true); // compressed (stored) size\n lv.setUint32(22, data.length, true); // uncompressed size\n lv.setUint16(26, name.length, true);\n local.set(name, 30);\n parts.push(local, data);\n\n const dir = new Uint8Array(46 + name.length);\n const dv = new DataView(dir.buffer);\n dv.setUint32(0, 0x02014b50, true); // central directory signature\n dv.setUint16(4, 20, true); // version made by\n dv.setUint16(6, 20, true); // version needed\n dv.setUint32(16, crc, true);\n dv.setUint32(20, data.length, true);\n dv.setUint32(24, data.length, true);\n dv.setUint16(28, name.length, true);\n dv.setUint32(42, offset, true); // local header offset\n dir.set(name, 46);\n central.push(dir);\n\n offset += local.length + data.length;\n }\n\n const dirSize = central.reduce((sum, d) => sum + d.length, 0);\n const eocd = new Uint8Array(22);\n const ev = new DataView(eocd.buffer);\n ev.setUint32(0, 0x06054b50, true); // end of central directory\n ev.setUint16(8, entries.length, true);\n ev.setUint16(10, entries.length, true);\n ev.setUint32(12, dirSize, true);\n ev.setUint32(16, offset, true);\n\n return new Blob([...parts, ...central, eocd] as BlobPart[], {\n type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n });\n}\n\n// --- public API ------------------------------------------------------------\n\nexport const TableExport = {\n /**\n * Build the workbook and trigger a browser download as\n * `<baseName>-YYYYMMDD-HHMMSS.xlsx`. Rows are exported exactly as the\n * caller passes them (i.e. in the currently rendered/sorted order).\n */\n download(baseName: string, sheetName: string, headers: string[], rows: unknown[][]): void {\n // Sheet names are capped at 31 chars and reject []:*?/\\ \u2014 sanitize\n // localized captions so a long/exotic translation can't corrupt the file.\n const safeSheet = (sheetName || '').replace(/[\\[\\]:*?/\\\\]/g, ' ').slice(0, 31).trim() || 'Sheet1';\n const blob = zip([\n { name: '[Content_Types].xml', text: CONTENT_TYPES },\n { name: '_rels/.rels', text: ROOT_RELS },\n { name: 'xl/workbook.xml', text: workbookXml(safeSheet) },\n { name: 'xl/_rels/workbook.xml.rels', text: WORKBOOK_RELS },\n { name: 'xl/worksheets/sheet1.xml', text: sheetXml(headers, rows) },\n ]);\n const stamp = new Date().toISOString().slice(0, 19).replace(/[-:]/g, '').replace('T', '-');\n const a = document.createElement('a');\n a.href = URL.createObjectURL(blob);\n a.download = `${baseName}-${stamp}.xlsx`;\n document.body.appendChild(a);\n a.click();\n a.remove();\n setTimeout(() => URL.revokeObjectURL(a.href), 5000);\n },\n};\n", "// DataGrid \u2014 a column-driven table.\n//\n// A blotter used to declare its columns three times: the <thead> cells in the\n// server-rendered markup, the sort accessors in the widget constructor, and the\n// export header/row arrays in its export routine. Nothing kept the three in\n// step, and the body was built by concatenating innerHTML, which forces every\n// interactive cell into an inline `onclick=\"someGlobal.doThing(id)\"` attribute\n// because a string cannot carry a listener.\n//\n// Here a column is declared once \u2014 key, header, how to read the value, how to\n// render it, how to class it, whether it exports \u2014 and the grid owns the header,\n// the body, the sort state and the sheet. Two things a naive column pipeline\n// cannot express are first-class:\n//\n// * `render()` may return a Node. That is how a cell holds a real control with\n// its own addEventListener instead of an inline handler reaching a global.\n// * Pinned rows sit outside the sort and outside the export \u2014 a positions\n// balance summary stays on top no matter which column the user sorts by.\n//\n// The grid renders elements, never HTML strings, so nothing here can be an\n// injection site. Colour stays where it was: the caller returns class names\n// (`cellClass` / `rowClass`) and the host stylesheet decides what they look\n// like.\n//\n// Three class names come out of the package itself, and an adopting site has to\n// style them: `grid-empty` on the cell that spans the table when there is\n// nothing to show, `visually-hidden` on a header whose caption is for screen\n// readers only, and `sort-asc` / `sort-desc` on the sorted header (emitted by\n// TableSort). The first is ours; the other two are spelled the way Bootstrap\n// spells them, which is a coupling worth knowing about before adopting.\nimport { TableSort, type SortSpec } from './table-sort.js';\nimport { GridContextMenu, type GridMenuClasses, type GridMenuItem } from './context-menu.js';\nimport { GridFilterDialog, type GridFilterDialogClasses, type GridFilterDialogLabels } from './filter-dialog.js';\nimport { TableExport } from './table-export.js';\n\n/// A row separator for the clipboard. Named because a bare escape inside a string\n/// is easy to mangle when this file is edited by a tool rather than by hand.\nconst NEWLINE = String.fromCharCode(10);\nconst TAB = String.fromCharCode(9);\n\n/** Where a pinned row sits relative to the sorted body rows. */\nexport const GridPinnedPlacements = {\n Top: 'top',\n Bottom: 'bottom',\n} as const;\n\nexport type GridPinnedPlacement = typeof GridPinnedPlacements[keyof typeof GridPinnedPlacements];\n\nexport interface GridColumn<TRow> {\n /**\n * Stable identity of the column: stamped as `data-sort` on the header cell\n * (which is what TableSort keys off) and as `data-col` on every body cell,\n * and the key `cellElement()` takes.\n */\n key: string;\n\n /**\n * Caption, already localized. The grid does no translation \u2014 a host with a\n * translator resolves the string before declaring the column.\n */\n header: string;\n\n /** Class list for the header cell (alignment, width hints). */\n headerClass?: string;\n\n /**\n * Render the caption into a `.visually-hidden` span instead of visible text:\n * the column is announced to a screen reader but shows no header, which is\n * what an actions column wants.\n */\n headerHidden?: boolean;\n\n /**\n * This row's value for the column. Drives the sort, and is both the default\n * cell content and the default exported value. A column that declares no\n * value has nothing to sort by, so its header is not clickable.\n */\n value?(row: TRow): unknown;\n\n /**\n * Display content. A string becomes the cell's text; a Node is appended as\n * it is, so the cell can hold a button, an icon or any element carrying its\n * own listeners.\n */\n render?(row: TRow): string | Node;\n\n /**\n * Class list for this row's cell \u2014 the colouring third (`side-buy`,\n * `pnl-negative`, `cell-editable`, \u2026). The host stylesheet owns the looks.\n */\n cellClass?(row: TRow): string;\n\n /**\n * Wire behaviour onto the whole cell once its content is in place. For an\n * interaction that belongs to the cell rather than to a control inside it \u2014\n * double-clicking a price cell to edit it. A control gets its own listener\n * in `render()` instead.\n */\n bindCell?(td: HTMLTableCellElement, row: TRow): void;\n\n /**\n * The value as a person reads it, when that is not the value itself: `Buy` for\n * the 0 an order's side is held as, a status name for its code.\n *\n * The grid shows this wherever it has to name a value outside a cell -- a group\n * header, the list a set filter is picked from, the menu line that filters by\n * the value under the pointer -- while `value` goes on being what it sorts,\n * groups and compares by. Without it those places read `0 (14)`, and a filter\n * picked from them would be comparing a label against a number.\n *\n * Unlike `render`, this returns text and nothing but text, so the grid can put\n * it in places that hold no markup.\n */\n text?(row: TRow): string;\n\n /**\n * Draw a filter for this column, of this kind. Omit it and the column is not\n * filterable -- a filter row appears only once some column asks for one.\n */\n filter?: GridFilterKind;\n\n /**\n * Whether the column appears in the exported sheet. An exportable column\n * must be able to produce a value (`value` or `exportValue`).\n */\n exportable: boolean;\n\n /**\n * Exported value, when it differs from `value`: the localized text the user\n * reads rather than the raw enum behind it, or the raw number rather than\n * the formatted string on screen.\n */\n exportValue?(row: TRow): unknown;\n}\n\nexport interface GridPinnedCell {\n content: string | Node;\n className: string;\n}\n\n/**\n * A row the grid renders but does not own the data of: it is not sorted, not\n * exported, and its cells are supplied ready-made because it does not share the\n * shape the columns read (a balance summary under position columns).\n */\nexport interface GridPinnedRow {\n key: string;\n className: string;\n place: GridPinnedPlacement;\n /**\n * One cell per column, in column order. A shorter list leaves the remaining\n * columns empty; a longer one is a declaration mistake and throws.\n */\n cells: GridPinnedCell[];\n}\n\nexport interface GridExportData {\n headers: string[];\n rows: unknown[][];\n}\n\n/**\n * Which control the grid draws for a column, and therefore how its filter reads:\n * a substring box, a min/max pair, or a list of the values present.\n */\nexport type GridFilterKind = 'text' | 'number' | 'set';\n\n/** Where a right-click landed, handed to whoever builds the menu for it. */\nexport interface GridMenuContext<TRow> {\n /** The column under the pointer, or null when the click missed the cells. */\n column: GridColumn<TRow> | null;\n /** The row under the pointer, or null on a header, a group row or empty space. */\n row: TRow | null;\n /** The value of that row in that column, already rendered as text. */\n text: string;\n /** Where the click landed, so anything the menu opens can appear there. */\n x: number;\n y: number;\n}\n\n/** How the grid's own menu is set up, and how a host takes it over. */\nexport interface GridMenuOptions<TRow> {\n classes?: GridMenuClasses;\n /**\n * The final say on what the menu contains. Gets the grid's own items and may\n * return them, add to them, or ignore them entirely; returning an empty array\n * suppresses the menu for that click.\n */\n items?(context: GridMenuContext<TRow>, defaults: GridMenuItem[]): GridMenuItem[];\n /** Wording, for a host that is not in English. */\n labels?: Partial<GridMenuLabels>;\n /** Class names and wording for the filter rule dialog the menu opens. */\n filterDialog?: { classes?: GridFilterDialogClasses; labels?: Partial<GridFilterDialogLabels> };\n}\n\n/** Every phrase the built-in menu can show. */\nexport interface GridMenuLabels {\n sortAsc: string;\n sortDesc: string;\n sortClear: string;\n hideColumn: string;\n showAllColumns: string;\n groupBy: string;\n ungroup: string;\n /**\n * Names the value under the pointer. A function rather than a string because the\n * line is two pieces joined, and what joins them is a language's business: a\n * Chinese UI wants a full-width colon, and some languages want the value first.\n */\n filterByValue(text: string): string;\n filterRule: string;\n showFilters: string;\n hideFilters: string;\n showHeader: string;\n hideHeader: string;\n clearFilters: string;\n copyCell: string;\n copyRow: string;\n /** Names how many rows go. A function for the same reason as `filterByValue`. */\n copyRows(count: number): string;\n exportXlsx: string;\n}\n\nconst MENU_LABELS: GridMenuLabels = {\n sortAsc: 'Sort ascending',\n sortDesc: 'Sort descending',\n sortClear: 'Clear sort',\n hideColumn: 'Hide column',\n showAllColumns: 'Show all columns',\n groupBy: 'Group by this column',\n ungroup: 'Ungroup',\n filterByValue: text => `Filter by this value: ${text}`,\n filterRule: 'Filter\u2026',\n showFilters: 'Show filter row',\n hideFilters: 'Hide filter row',\n showHeader: 'Show header row',\n hideHeader: 'Hide header row',\n clearFilters: 'Clear filters',\n copyCell: 'Copy cell',\n copyRow: 'Copy row',\n copyRows: count => `Copy selected rows (${count})`,\n exportXlsx: 'Export to .xlsx',\n};\n\n/** How many rows a user may have selected at once. */\nexport type GridSelectionMode = 'none' | 'single' | 'multi';\n\n/** One value a set filter can be picked by: what it compares, and what it reads as. */\nexport interface GridValueOption {\n value: string;\n label: string;\n}\n\n/// Rows sharing a value, under the label that value reads as.\ninterface GridGroup<TRow> {\n label: string;\n rows: TRow[];\n}\n\n/**\n * One column's filter. Plain data rather than a predicate, and deliberately so:\n * a closure cannot be written to a store, so a grid filtered by one could never\n * come back filtered the same way. Every field is optional; an object with none\n * of them set means no filter at all.\n */\nexport interface GridFilter {\n /**\n * The rule. Absent means the shape decides: `text` reads as \"contains\", a\n * min/max pair as \"between\" -- which is what the inline row writes, and what a\n * filter stored before operators existed still means.\n */\n op?: GridFilterOp;\n /** The value the rule compares against, for every op but `empty` / `notEmpty`. */\n text?: string;\n /** Inclusive bounds for `between`; `min` alone also carries the operand of ge/gt/eq/ne on numbers. */\n min?: number;\n max?: number;\n /**\n * The values the rule picks from, as the column's `value` produces them -- the\n * raw side, not the \"Buy\" a person picked. `anyOf` keeps them, `noneOf` drops\n * them; with no op at all the list reads as `anyOf`, which is what the single\n * select in the filter row writes.\n */\n values?: string[];\n}\n\n/**\n * What a filter asks of a value. Text and numbers share the comparison ops -- a\n * user filtering an id column expects `>` to mean what it says, and a symbol\n * column compares as text.\n */\nexport type GridFilterOp =\n 'contains' | 'notContains' | 'startsWith' | 'endsWith'\n | 'eq' | 'ne' | 'gt' | 'ge' | 'lt' | 'le' | 'between'\n | 'anyOf' | 'noneOf'\n | 'empty' | 'notEmpty';\n\n/**\n * What the user arranged, and nothing the data decided. This is the whole of what\n * a host has to store to bring a table back the way somebody left it: which\n * columns, in what order, sorted by what.\n *\n * Every field is optional and every unknown column key is ignored on the way in,\n * because a stored layout outlives the table it was stored from -- a column gets\n * removed in a deploy and the saved view must still restore the rest rather than\n * fault.\n */\nexport interface GridState {\n /** Column keys in display order. Keys the caller leaves out keep their relative order, after these. */\n order?: string[];\n /** Column keys the user hid. They keep their place in `order` so unhiding restores the slot. */\n hidden?: string[];\n /** Whether the filter row is on screen. Absent means the table's own default. */\n filtersVisible?: boolean;\n /** Whether the caption row is on screen. Absent means the table's own default. */\n headerVisible?: boolean;\n sort?: SortSpec | null;\n /** By column key. Columns that are not filtered are absent rather than empty. */\n filters?: Record<string, GridFilter>;\n /** Column key the rows are grouped under, or null for a flat table. */\n group?: string | null;\n /** Group values the user collapsed. */\n collapsed?: string[];\n}\n\nexport interface GridOptions<TRow> {\n /** The `<thead>` the grid renders the header row into. Emptied on construction. */\n head: HTMLElement;\n /** The `<tbody>` the grid renders rows into. Emptied on every render. */\n body: HTMLElement;\n columns: GridColumn<TRow>[];\n /**\n * The order in effect while no header is selected. Null only for a table with\n * genuinely no meaningful resting order \u2014 see TableSort.\n */\n defaultSort: SortSpec | null;\n /**\n * Stable identity of a row, used to find its element again after a render\n * (patch a single cell, start an inline edit). Must be unique per row; when\n * two rows share a key the later one wins the lookup.\n */\n rowKey(row: TRow): string;\n /** Localized text for the row shown when there is nothing to display. */\n emptyText: string;\n rowClass?(row: TRow): string;\n /** Wire behaviour onto the whole row (selecting an instrument by clicking it). */\n bindRow?(tr: HTMLTableRowElement, row: TRow): void;\n /** Rows outside the sort, re-read on every render so they can track live data. */\n pinnedRows?(): GridPinnedRow[];\n /**\n * Cap on how many sorted rows are painted. Only rendering is capped \u2014 the\n * export still covers every row the grid holds, which is what a watchlist\n * wants: a screen-sized table over a full sheet.\n */\n renderLimit?: number;\n /**\n * Where a group sits among the others. `label` (the default) reads them\n * alphabetically, which is what a reader expects of a column of names. `rows` leaves\n * them in the order the sorted rows present them, for a table whose order carries\n * meaning of its own -- a strategy's statistics run profit, then trades, then orders,\n * and alphabetising that says nothing and loses something.\n */\n groupOrder?: 'label' | 'rows';\n /**\n * Called once the body has been repainted. The grid owns the header\n * listener, so a sort click re-renders without the caller being involved \u2014\n * this is the only hook a caller has for work that depends on *which rows\n * are now on screen* (a watchlist re-subscribing to the visible symbols).\n *\n * Not called for the paint the constructor does: the grid holds no rows\n * yet, so there is nothing on screen to react to, and the caller does not\n * hold its own reference to the grid until the constructor returns.\n */\n afterRender?(): void;\n /**\n * Let the user drag a header to move its column. Off by default: a grid inside\n * a form or a report has nothing to gain from it, and a draggable header is a\n * mouse target that behaves differently from every other one on the page.\n */\n reorderable?: boolean;\n /**\n * The language the table is written in, as a BCP 47 tag. It decides the order of\n * everything compared as text -- the sort, the groups, a set filter's option list\n * -- so a page rendered in German orders the same way for every visitor instead of\n * following whatever locale their browser happens to be set to.\n *\n * Left out, the collator takes the document's own language.\n */\n locale?: string;\n\n /** A collator of your own, when the default (numeric, case-folding) is not it. */\n collator?: Intl.Collator;\n\n /**\n * The line a group header shows. The default reads `\u25BE Buy (14)`; override it for a\n * language that writes counts differently, or return a Node to put a real chevron\n * element in place of the triangle.\n */\n groupHeader?(label: string, count: number, collapsed: boolean): string | Node;\n\n /**\n * Show the row of filter boxes under the header. Off by default: the rule dialog\n * says everything the row can and more -- an operator, not just a substring -- and\n * a row of boxes costs a line of vertical space on every table that has one. Turn\n * it on for a table where filtering is the main activity.\n */\n filtersVisible?: boolean;\n /**\n * Show the caption row. On by default; a host turns it off for a table that\n * reads as a stream \u2014 a tape \u2014 and the menu's toggle brings it back. The\n * filter row is its own decision (`filtersVisible`) and stays either way.\n */\n headerVisible?: boolean;\n /**\n * Whether clicking a row selects it, and whether more than one can be selected.\n * Defaults to 'none': a table that is read, not acted on, should not respond to\n * a click with a highlight nobody asked for.\n */\n selection?: GridSelectionMode;\n /**\n * Class put on a selected row. The grid names elements but decides nothing about\n * how they look, so the host supplies the name its stylesheet defines.\n */\n selectedClass?: string;\n onSelectionChange?(keys: string[]): void;\n /**\n * Class put on a row whose key was not on screen the render before \u2014 how a live\n * tape flashes its newest prints without the host patching cells after every\n * render. The first paint never flashes: a seeded table is history, not news.\n * The class lives until the next repaint, so the host's CSS animation runs once\n * per arrival. The name is the host's, like every other look here.\n */\n flashNewClass?: string;\n /**\n * Offer a context menu on right-click. `true` takes the grid's own; an object\n * keeps it and lets the host restyle, reword or rewrite the items.\n */\n contextMenu?: boolean | GridMenuOptions<TRow>;\n /**\n * Called whenever the user changes the arrangement -- moved a column, hid one,\n * sorted. This is where a host persists it; `setState` deliberately does not\n * fire it, or restoring a layout would write it straight back on every load.\n */\n onStateChange?(state: GridState): void;\n}\n\n/// Whether a press landed on something that answers a press itself.\n///\n/// Named tags rather than a check for a listener, because there is no way to ask an element\n/// whether it has one - and these are the tags a host puts in a cell when it wants the user to\n/// act on the row rather than merely select it.\nconst CONTROL_TAGS = ['SELECT', 'INPUT', 'BUTTON', 'TEXTAREA', 'A', 'OPTION', 'LABEL'];\n\nfunction isControl(event: { target?: unknown }): boolean {\n const target = event.target as { tagName?: string; isContentEditable?: boolean } | null | undefined;\n if (target === null || target === undefined) return false;\n if (target.isContentEditable === true) return true;\n return CONTROL_TAGS.includes(String(target.tagName ?? '').toUpperCase());\n}\n\n\nexport class DataGrid<TRow> {\n readonly options: GridOptions<TRow>;\n readonly sort: TableSort<TRow>;\n private _rows: TRow[];\n private _rowEls: Map<string, HTMLTableRowElement>;\n private _cellEls: Map<string, Map<string, HTMLTableCellElement>>;\n /// Display order of every declared column, hidden ones included -- hiding must\n /// not lose the slot a column goes back to.\n private _order: string[];\n private _hidden: Set<string>;\n /// The column a drag started on. Held here rather than in dataTransfer because\n /// the payload is ours, not the page's: dropping a header onto another window\n /// should do nothing, and reading it back out of the event would invite it.\n private _dragKey: string | null;\n private _filters: Record<string, GridFilter>;\n private _group: string | null;\n private _filtersVisible: boolean;\n private _headerVisible: boolean;\n /// By row key, never by index or element: a repaint rebuilds every `<tr>`, and a\n /// filter takes rows off screen and brings them back.\n private _selected: Set<string>;\n /// Where a shift-click measures its range from.\n private _anchorKey: string | null;\n // The keys on screen at the previous paint, for `flashNewClass`. Null until\n // the first paint has happened \u2014 nothing was on screen yet, so nothing is\n // news \u2014 and left null entirely while the option is off.\n private _flashKnown: Set<string> | null;\n private _menu: GridContextMenu | null;\n private _dragging: boolean;\n /// One collator for the whole grid: the sort, the group order, the option list a\n /// set filter offers, and the ordering filter rules. They compare the same strings\n /// and had better agree about what comes first.\n private readonly _collator: Intl.Collator;\n /// What to undo when the grid is destroyed: the listeners it put on the document.\n private readonly _release: (() => void)[] = [];\n /// The grid a keyboard shortcut belongs to: the last one the pointer touched.\n /// Two grids on a page both holding a selection would otherwise both answer Ctrl+C.\n private static _active: DataGrid<unknown> | null = null;\n private _filterDialog: GridFilterDialog | null;\n private _collapsed: Set<string>;\n /// The rendered filter controls, by column key, so a stored filter can be put\n /// back into them without rebuilding the row under the user's cursor.\n private _filterEls: Map<string, HTMLElement[]>;\n /// Suppresses onStateChange while setState applies a stored arrangement.\n private _restoring: boolean;\n\n constructor(options: GridOptions<TRow>) {\n for (const col of options.columns) {\n if (col.exportable && !col.value && !col.exportValue)\n throw new Error(`DataGrid: column \"${col.key}\" is exportable but declares no value to export`);\n }\n\n this.options = options;\n this._rows = [];\n this._rowEls = new Map();\n this._cellEls = new Map();\n this._order = options.columns.map(col => col.key);\n this._hidden = new Set();\n this._dragKey = null;\n this._filters = {};\n this._filtersVisible = options.filtersVisible === true;\n this._headerVisible = options.headerVisible !== false;\n this._group = null;\n this._collapsed = new Set();\n this._selected = new Set();\n this._anchorKey = null;\n this._flashKnown = null;\n this._menu = null;\n this._filterDialog = null;\n this._dragging = false;\n this._collator = options.collator\n || new Intl.Collator(\n options.locale || (typeof document !== 'undefined' ? document.documentElement?.lang : '') || undefined,\n // numeric so a text column of ids reads 1, 2, 10 rather than 1, 10, 2,\n // and accent-sensitive so it folds case the way the text filters do.\n { numeric: true, sensitivity: 'accent' });\n this._filterEls = new Map();\n this._restoring = false;\n\n const accessors: Record<string, (row: TRow) => unknown> = {};\n for (const col of options.columns) {\n if (col.value) accessors[col.key] = col.value;\n }\n\n // The header has to exist before TableSort is constructed: it marks the\n // active column on the cells it finds under `head` right away, so that the\n // resting sort is visible on first paint and not only after a click.\n this._renderHead();\n this.sort = new TableSort<TRow>(options.head, accessors, () => this.render(), options.defaultSort, this._collator);\n this._paint();\n if (options.contextMenu) this._bindContextMenu();\n if ((options.selection || 'none') !== 'none') this._bindCopyShortcut();\n }\n\n /** Every declared column in display order, hidden ones included. */\n columnKeys(): string[] {\n return [...this._order];\n }\n\n /** The columns actually rendered, in the order they are rendered. */\n visibleColumns(): GridColumn<TRow>[] {\n const byKey = new Map(this.options.columns.map(col => [col.key, col]));\n return this._order.filter(key => !this._hidden.has(key)).map(key => byKey.get(key)!);\n }\n\n isColumnHidden(key: string): boolean {\n return this._hidden.has(key);\n }\n\n /**\n * Put the columns in this order. Keys the caller leaves out keep their relative\n * order behind the ones named -- a layout stored before a column existed must\n * not make that column disappear.\n */\n setColumnOrder(keys: string[]): void {\n const known = new Set(this.options.columns.map(col => col.key));\n for (const key of keys) {\n if (!known.has(key)) throw new Error(`DataGrid: no column \"${key}\" to order`);\n }\n this._applyOrder(keys);\n this._rerender();\n }\n\n hideColumn(key: string): void {\n if (this._hidden.has(key)) return;\n this._hidden.add(key);\n this._rerender();\n }\n\n showColumn(key: string): void {\n if (!this._hidden.delete(key)) return;\n this._rerender();\n }\n\n filtersVisible(): boolean {\n return this._filtersVisible;\n }\n\n /** Show or hide the filter row. The filters themselves are kept either way. */\n showFilters(visible: boolean): void {\n if (this._filtersVisible === visible) return;\n this._filtersVisible = visible;\n this._rerender();\n }\n\n headerVisible(): boolean {\n return this._headerVisible;\n }\n\n /** Show or hide the caption row. Sort, columns and filters are kept either way. */\n showHeader(visible: boolean): void {\n if (this._headerVisible === visible) return;\n this._headerVisible = visible;\n this._rerender();\n }\n\n /** The filters in effect, by column key. Columns with none are absent. */\n filters(): Record<string, GridFilter> {\n return { ...this._filters };\n }\n\n /**\n * Filter a column, or pass null to stop filtering it. A filter with nothing set\n * -- a text box the user emptied -- is the same as null, so clearing an input\n * brings the rows back instead of matching nothing.\n */\n setFilter(key: string, filter: GridFilter | null): void {\n const normalized = filter && DataGrid._normalizeFilter(filter);\n if (normalized) this._filters[key] = normalized;\n else delete this._filters[key];\n this._afterFilterChange();\n }\n\n clearFilters(): void {\n if (Object.keys(this._filters).length === 0) return;\n this._filters = {};\n this._afterFilterChange();\n }\n\n /** The held rows that pass every filter, in the order they were handed over. */\n filteredRows(): TRow[] {\n const entries = Object.entries(this._filters);\n if (entries.length === 0) return this._rows;\n\n const byKey = new Map(this.options.columns.map(col => [col.key, col]));\n return this._rows.filter(row => entries.every(([key, filter]) => {\n const col = byKey.get(key);\n // A filter naming a column that is gone matches everything rather than\n // nothing: a stale stored filter must not empty the table.\n if (!col || !col.value) return true;\n return this._passes(col.value(row), filter);\n }));\n }\n\n private _afterFilterChange(): void {\n this.render();\n this._refreshFilterRow();\n if (!this._restoring && this.options.onStateChange) this.options.onStateChange(this.getState());\n }\n\n private static _normalizeFilter(filter: GridFilter): GridFilter | null {\n const out: GridFilter = {};\n // `empty` and `notEmpty` ask about the value itself, so they are a filter even\n // with nothing typed in -- every other op needs an operand to mean anything.\n if (filter.op === 'empty' || filter.op === 'notEmpty') return { op: filter.op };\n if (filter.op) out.op = filter.op;\n const text = typeof filter.text === 'string' ? filter.text.trim() : '';\n if (text) out.text = text;\n if (typeof filter.min === 'number' && Number.isFinite(filter.min)) out.min = filter.min;\n if (typeof filter.max === 'number' && Number.isFinite(filter.max)) out.max = filter.max;\n if (filter.values && filter.values.length) out.values = [...filter.values];\n // An op on its own filters nothing; it needs something to compare against.\n if (out.op && out.text === undefined && out.min === undefined && out.max === undefined && !out.values) return null;\n return Object.keys(out).length ? out : null;\n }\n\n private _passes(value: unknown, filter: GridFilter): boolean {\n const op = DataGrid._effectiveOp(filter);\n if (!op) return true;\n\n const text = DataGrid._text(value);\n // A set filter is picked from the values present rather than compared against\n // an operand, so it answers on its own terms.\n if (op === 'anyOf' || op === 'noneOf') {\n const listed = (filter.values || []).includes(text);\n return op === 'anyOf' ? listed : !listed;\n }\n\n if (op === 'empty') return text === '';\n if (op === 'notEmpty') return text !== '';\n\n if (op === 'between') {\n const n = Number(text);\n if (!Number.isFinite(n)) return false;\n if (filter.min !== undefined && n < filter.min) return false;\n if (filter.max !== undefined && n > filter.max) return false;\n return true;\n }\n\n // Compared as numbers when both sides are numbers, as text otherwise: `>` on a\n // symbol column has to mean something, and on a quantity it has to mean the\n // arithmetic thing rather than '9' > '10'.\n const operandText = filter.text !== undefined ? filter.text : (filter.min !== undefined ? String(filter.min) : '');\n const left = Number(text);\n const right = Number(operandText);\n const numeric = text !== '' && operandText !== '' && Number.isFinite(left) && Number.isFinite(right);\n\n // Ordering compares through the collator, not with `>` on strings: raw\n // comparison is UTF-16 code-unit order, which puts every accented letter after\n // z and every Cyrillic capital before every small letter -- so a column shown\n // in one order would filter in another.\n const order = numeric ? Math.sign(left - right) : this._collator.compare(text, operandText);\n\n switch (op) {\n case 'contains': return text.toLowerCase().includes(operandText.toLowerCase());\n case 'notContains': return !text.toLowerCase().includes(operandText.toLowerCase());\n case 'startsWith': return text.toLowerCase().startsWith(operandText.toLowerCase());\n case 'endsWith': return text.toLowerCase().endsWith(operandText.toLowerCase());\n case 'eq': return numeric ? left === right : text.toLowerCase() === operandText.toLowerCase();\n case 'ne': return numeric ? left !== right : text.toLowerCase() !== operandText.toLowerCase();\n case 'gt': return order > 0;\n case 'ge': return order >= 0;\n case 'lt': return order < 0;\n case 'le': return order <= 0;\n default: return true;\n }\n }\n\n /// The rule actually in force. Absent `op` keeps the old meaning of the shape, so\n /// a filter written by the inline row -- or stored before operators existed --\n /// goes on meaning what it meant.\n private static _effectiveOp(filter: GridFilter): GridFilterOp | null {\n if (filter.op) return filter.op;\n // No op means the shape decides, which is what the controls in the filter row\n // write: a box of text reads as \"contains\", a pair of numbers as \"between\",\n // and a picked value as \"any of\".\n if (filter.values !== undefined) return 'anyOf';\n if (filter.min !== undefined || filter.max !== undefined) return 'between';\n if (filter.text !== undefined) return 'contains';\n return null;\n }\n\n /**\n * The items the grid offers for a right-click. Public so a host that replaces\n * the menu can still reach for the parts of it that it wants to keep.\n */\n menuItems(context: GridMenuContext<TRow>): GridMenuItem[] {\n const labels = { ...MENU_LABELS, ...(this._menuOptions().labels || {}) };\n const items: GridMenuItem[] = [];\n const col = context.column;\n\n if (col && col.value) {\n const sort = this.sort.current();\n items.push(\n { label: labels.sortAsc, checked: sort?.col === col.key && sort.dir === 'asc', run: () => this.sort.set(col.key, 'asc') },\n { label: labels.sortDesc, checked: sort?.col === col.key && sort.dir === 'desc', run: () => this.sort.set(col.key, 'desc') },\n { label: labels.sortClear, disabled: !sort, run: () => this.sort.set(null, null) },\n {},\n this._group === col.key\n ? { label: labels.ungroup, run: () => this.groupBy(null) }\n : { label: labels.groupBy, run: () => this.groupBy(col.key) },\n );\n\n if (col.filter) {\n items.push({\n label: labels.filterRule,\n checked: this._filters[col.key] !== undefined,\n run: () => this.openFilterDialog(col.key, context.x, context.y),\n });\n }\n\n if (context.row !== null) {\n // Named by what the cell reads as, filtering by what the column holds.\n // Taking the cell's text for both matched a label against a raw value,\n // so on a column showing \"Buy\" for 0 this line filtered everything out.\n const value = DataGrid._text(col.value ? col.value(context.row) : null);\n items.push({\n label: labels.filterByValue(this._displayText(col, context.row)),\n run: () => this.setFilter(col.key, { op: 'anyOf', values: [value] }),\n });\n }\n }\n\n if (this.options.columns.some(c => c.filter)) {\n items.push(\n {\n label: this._filtersVisible ? labels.hideFilters : labels.showFilters,\n checked: this._filtersVisible,\n run: () => this.showFilters(!this._filtersVisible),\n },\n { label: labels.clearFilters, disabled: Object.keys(this._filters).length === 0, run: () => this.clearFilters() },\n {},\n );\n }\n\n if (col) {\n items.push({\n label: labels.hideColumn,\n // The last visible column cannot go: a table with no columns is a\n // blank rectangle with no way back to itself.\n disabled: this.visibleColumns().length <= 1,\n run: () => this.hideColumn(col.key),\n });\n }\n items.push({\n label: labels.showAllColumns,\n disabled: this._hidden.size === 0,\n run: () => { for (const key of [...this._hidden]) this.showColumn(key); },\n });\n // From the body as much as from the header \u2014 once the captions are\n // hidden, the body is the only place left to bring them back from.\n items.push({\n label: this._headerVisible ? labels.hideHeader : labels.showHeader,\n checked: this._headerVisible,\n run: () => this.showHeader(!this._headerVisible),\n });\n\n if (context.row !== null) {\n items.push(\n {},\n { label: labels.copyCell, run: () => DataGrid._copy(context.text) },\n {\n // Names how many rows go, because \"Copy row\" over a selection of\n // twelve would be a lie about what the click is about to do.\n label: this._selected.size > 1 ? labels.copyRows(this._selected.size) : labels.copyRow,\n run: () => this.copyRows(context.row),\n },\n );\n }\n\n items.push({}, { label: labels.exportXlsx, run: () => this.download('export', 'Export') });\n return items;\n }\n\n private _menuOptions(): GridMenuOptions<TRow> {\n const option = this.options.contextMenu;\n return typeof option === 'object' && option !== null ? option : {};\n }\n\n private _bindContextMenu(): void {\n this._menu = new GridContextMenu(this._menuOptions().classes);\n\n const onContext = (event: Event) => {\n DataGrid._active = this as unknown as DataGrid<unknown>;\n const target = (event as unknown as { target?: Element }).target;\n const cell = target && typeof target.closest === 'function'\n ? target.closest('[data-col]') as HTMLElement | null\n : null;\n const rowEl = target && typeof target.closest === 'function'\n ? target.closest('[data-row-key]') as HTMLElement | null\n : null;\n\n const column = cell ? this.options.columns.find(c => c.key === cell.dataset.col) || null : null;\n const rowKey = rowEl?.dataset.rowKey;\n const row = rowKey === undefined ? null\n : this.displayRows().find(r => this.options.rowKey(r) === rowKey) || null;\n const text = column && row && column.value ? DataGrid._text(column.value(row)) : '';\n\n const at = event as unknown as { clientX?: number; clientY?: number };\n const context: GridMenuContext<TRow> = { column, row, text, x: at.clientX || 0, y: at.clientY || 0 };\n const build = this._menuOptions().items;\n const items = build ? build(context, this.menuItems(context)) : this.menuItems(context);\n if (items.length === 0) return;\n\n event.preventDefault();\n this._menu!.open(items, context.x, context.y);\n };\n\n this.options.head.addEventListener('contextmenu', onContext);\n this.options.body.addEventListener('contextmenu', onContext);\n }\n\n /// The same columns and the same accessors the sheet uses. A row on the clipboard\n /// and a row in the .xlsx are the same data going to the same place, so a column\n /// that renders a button has nothing to contribute to either, and one that exports\n /// \"Buy\" must not copy the 0 behind it.\n private _rowText(row: TRow): string {\n return this.visibleColumns()\n .filter(col => col.exportable)\n .map(col => DataGrid._text((col.exportValue || col.value)!(row)))\n .join(TAB);\n }\n\n /// Put text on the clipboard, returning whether anything was written.\n /// navigator.clipboard exists only in a secure context, so on a page served over\n /// plain http to anything but localhost it is simply undefined -- which is how\n /// Copy row came to do nothing at all on a LAN address while working locally. The\n /// older execCommand path still works there, so it is the fallback rather than a\n /// silent no-op.\n private static _copy(text: string): boolean {\n const clipboard = typeof navigator !== 'undefined' ? navigator.clipboard : undefined;\n if (clipboard && clipboard.writeText) {\n clipboard.writeText(text).catch(() => DataGrid._copyFallback(text));\n return true;\n }\n return DataGrid._copyFallback(text);\n }\n\n private static _copyFallback(text: string): boolean {\n if (typeof document === 'undefined' || !document.body) return false;\n\n const area = document.createElement('textarea') as HTMLTextAreaElement;\n area.value = text;\n // Off-screen rather than hidden: execCommand copies from a field that is in\n // the layout, and display:none takes it out of the layout.\n area.style.position = 'fixed';\n area.style.left = '-9999px';\n document.body.appendChild(area);\n try {\n area.select();\n return typeof document.execCommand === 'function' && document.execCommand('copy');\n } catch {\n return false;\n } finally {\n area.parentNode?.removeChild(area);\n }\n }\n\n /**\n * What Copy row and Ctrl+C put on the clipboard: the selected rows when there is\n * a selection, otherwise the one row asked for. Tab-separated, one row per line,\n * which is what a spreadsheet reads back as cells.\n */\n copyText(row: TRow | null): string {\n const selected = this.selectedRows();\n const rows = selected.length > 0 ? selected : (row === null ? [] : [row]);\n return rows.map(r => this._rowText(r)).join(NEWLINE);\n }\n\n /** Copy the current selection, or the row given. True when anything was written. */\n copyRows(row: TRow | null): boolean {\n const text = this.copyText(row);\n return text === '' ? false : DataGrid._copy(text);\n }\n\n /** Selected row keys, in the order they appear on screen. */\n selectedKeys(): string[] {\n const onScreen = this.displayRows().map(row => this.options.rowKey(row));\n const known = new Set(onScreen);\n // Keys of rows a filter is currently hiding keep their selection but have no\n // place in the on-screen order, so they follow at the end rather than vanish.\n return [...onScreen.filter(key => this._selected.has(key)),\n ...[...this._selected].filter(key => !known.has(key))];\n }\n\n /** The selected rows that are actually on screen. */\n selectedRows(): TRow[] {\n return this.displayRows().filter(row => this._selected.has(this.options.rowKey(row)));\n }\n\n /**\n * Forget the flash baseline: the next paint reads as a fresh seed and\n * flashes nothing. For a host that replaces its rows wholesale \u2014 a tape\n * moving to another instrument is a new history, not fifty arrivals at\n * once.\n */\n flashReset(): void {\n this._flashKnown = null;\n }\n\n setSelection(keys: string[]): void {\n this._replaceSelection(new Set(keys));\n }\n\n clearSelection(): void {\n this._replaceSelection(new Set());\n }\n\n private _replaceSelection(next: Set<string>): void {\n const before = this.selectedKeys();\n this._selected = next;\n const after = this.selectedKeys();\n if (before.length === after.length && before.every((key, i) => key === after[i])) return;\n this.render();\n if (this.options.onSelectionChange) this.options.onSelectionChange(after);\n }\n\n private _beginDragSelect(): void {\n this._dragging = true;\n const end = () => {\n this._dragging = false;\n document.removeEventListener('mouseup', end, true);\n };\n document.addEventListener('mouseup', end, true);\n }\n\n private _extendTo(key: string): void {\n if (this._anchorKey === null) return;\n const order = this.displayRows().map(row => this.options.rowKey(row));\n const from = order.indexOf(this._anchorKey);\n const to = order.indexOf(key);\n if (from === -1 || to === -1) return;\n this._replaceSelection(new Set(order.slice(Math.min(from, to), Math.max(from, to) + 1)));\n }\n\n /// Ctrl+C over the table copies the selected rows. Bound on the document because\n /// a table has no focus of its own, and answered only by the grid the pointer last\n /// touched -- otherwise every grid on the page would copy at once.\n private _bindCopyShortcut(): void {\n const onKeyDown = (event: Event) => {\n const e = event as unknown as { key?: string; code?: string; ctrlKey?: boolean; metaKey?: boolean; target?: { tagName?: string } };\n if (!(e.ctrlKey || e.metaKey)) return;\n // `code` is the physical key, so this holds on a layout where that key\n // types something other than \"c\" -- on a Russian one it reports \"\u0441\".\n const isC = e.code ? e.code === 'KeyC' : (e.key || '').toLowerCase() === 'c';\n if (!isC) return;\n if (DataGrid._active !== (this as unknown as DataGrid<unknown>)) return;\n if (this._selected.size === 0) return;\n\n // Never steal a copy from a field the user is typing in.\n const tag = e.target && e.target.tagName;\n if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT') return;\n\n if (this.copyRows(null)) event.preventDefault();\n };\n document.addEventListener('keydown', onKeyDown);\n this._release.push(() => document.removeEventListener('keydown', onKeyDown));\n }\n\n /**\n * Let the grid go. Everything it put outside its own two elements comes off: the\n * Ctrl+C binding on the document, and any menu or filter popup still open.\n *\n * A host needs this whenever it replaces a grid rather than updating one -- the\n * columns are declared once, so re-declaring them (translated headers, a different\n * set of columns) means a new grid over the same table, and the old one would go\n * on answering Ctrl+C from beyond the grave.\n */\n destroy(): void {\n for (const release of this._release.splice(0)) release();\n this._menu?.close();\n this._filterDialog?.close();\n if (DataGrid._active === (this as unknown as DataGrid<unknown>)) DataGrid._active = null;\n }\n\n private _clickRow(key: string, event: Event): void {\n const mode = this.options.selection || 'none';\n if (mode === 'none') return;\n\n const modifiers = event as unknown as { ctrlKey?: boolean; metaKey?: boolean; shiftKey?: boolean };\n const next = new Set<string>();\n\n if (mode === 'single' || (!modifiers.ctrlKey && !modifiers.metaKey && !modifiers.shiftKey)) {\n next.add(key);\n this._anchorKey = key;\n } else if (modifiers.shiftKey && this._anchorKey !== null) {\n // The range is what the user sees between the two rows, so it is measured\n // against the order on screen -- sorted, filtered, grouped -- not the order\n // the rows were handed over in.\n const order = this.displayRows().map(row => this.options.rowKey(row));\n const from = order.indexOf(this._anchorKey);\n const to = order.indexOf(key);\n if (from === -1 || to === -1) next.add(key);\n else for (const k of order.slice(Math.min(from, to), Math.max(from, to) + 1)) next.add(k);\n } else {\n for (const k of this._selected) next.add(k);\n if (!next.delete(key)) next.add(key);\n this._anchorKey = key;\n }\n\n this._replaceSelection(next);\n }\n\n /**\n * Open the rule dialog for a column -- an operator and its operand, which is what\n * the inline row cannot express without growing a second control per column.\n */\n openFilterDialog(key: string, x: number, y: number): void {\n const col = this.options.columns.find(c => c.key === key);\n if (!col || !col.filter) return;\n\n const options = this._menuOptions().filterDialog || {};\n if (!this._filterDialog) this._filterDialog = new GridFilterDialog(options.classes, options.labels);\n\n this._filterDialog.open(\n {\n header: col.header,\n kind: col.filter,\n current: this._filters[key] || null,\n // A set column is picked from, not typed into: the dialog needs the\n // values the rows actually hold, each under the label it reads as.\n values: col.filter === 'set' ? this._distinctValues(col) : [],\n x,\n y,\n },\n filter => this.setFilter(key, filter),\n );\n }\n\n /** The column the rows are grouped under, or null. */\n groupedBy(): string | null {\n return this._group;\n }\n\n /** Group by a column, or pass null to go flat. */\n groupBy(key: string | null): void {\n if (key !== null) {\n const col = this.options.columns.find(c => c.key === key);\n if (!col || !col.value) throw new Error(`DataGrid: no column \"${key}\" with a value to group by`);\n }\n this._group = key;\n // Collapse is remembered per group value, and the values belong to the column\n // that produced them -- keeping them across a regroup would collapse whatever\n // happened to share a name.\n this._collapsed.clear();\n this._afterViewChange();\n }\n\n collapsedGroups(): string[] {\n return [...this._collapsed];\n }\n\n toggleGroup(value: string): void {\n if (!this._collapsed.delete(value)) this._collapsed.add(value);\n this._afterViewChange();\n }\n\n private _afterViewChange(): void {\n this.render();\n if (!this._restoring && this.options.onStateChange) this.options.onStateChange(this.getState());\n }\n\n /** The arrangement, as a host would store it. */\n getState(): GridState {\n return {\n order: [...this._order],\n hidden: this._order.filter(key => this._hidden.has(key)),\n sort: this.sort.current(),\n filters: this.filters(),\n filtersVisible: this._filtersVisible,\n headerVisible: this._headerVisible,\n group: this._group,\n collapsed: this.collapsedGroups(),\n };\n }\n\n /**\n * Restore an arrangement. Unknown column keys are dropped rather than rejected:\n * a view stored before a deploy has to survive one.\n */\n setState(state: GridState): void {\n const known = new Set(this.options.columns.map(col => col.key));\n this._restoring = true;\n try {\n if (state.order) this._applyOrder(state.order.filter(key => known.has(key)));\n if (state.hidden) this._hidden = new Set(state.hidden.filter(key => known.has(key)));\n if (state.sort !== undefined) {\n if (state.sort) this.sort.set(state.sort.col, state.sort.dir);\n else this.sort.set(null, null);\n }\n if (state.filtersVisible !== undefined) this._filtersVisible = state.filtersVisible;\n if (state.headerVisible !== undefined) this._headerVisible = state.headerVisible;\n if (state.group !== undefined) {\n const col = state.group && this.options.columns.find(c => c.key === state.group);\n this._group = col && col.value ? state.group! : null;\n this._collapsed.clear();\n }\n if (state.collapsed) this._collapsed = new Set(state.collapsed);\n if (state.filters) {\n this._filters = {};\n for (const [key, filter] of Object.entries(state.filters)) {\n if (!known.has(key)) continue;\n const normalized = DataGrid._normalizeFilter(filter);\n if (normalized) this._filters[key] = normalized;\n }\n }\n this._rerender();\n } finally {\n this._restoring = false;\n }\n }\n\n private _applyOrder(keys: string[]): void {\n const named = keys.filter((key, i) => keys.indexOf(key) === i);\n this._order = [...named, ...this._order.filter(key => !named.includes(key))];\n }\n\n /// One place that repaints and tells the host, so no caller can do one without\n /// the other.\n private _rerender(): void {\n this._renderHead();\n this.sort.refreshHeader();\n this.render();\n if (!this._restoring && this.options.onStateChange) this.options.onStateChange(this.getState());\n }\n\n /**\n * The rows the grid holds, in the order they were handed over. The grid keeps\n * the caller's array by reference: a caller that merges a live delta into it\n * and calls `render()` repaints from the current contents.\n */\n get rows(): TRow[] {\n return this._rows;\n }\n\n /**\n * Rows in the order currently in effect (the picked column, else the table\n * default). A sorted copy \u2014 the held array is never reordered.\n */\n sortedRows(): TRow[] {\n return this.sort.apply(this.filteredRows());\n }\n\n /**\n * Every row that is on screen, in the order it appears there -- filtered, sorted,\n * and gathered into groups when grouping is on. Collapsed groups are included:\n * they are hidden, not excluded, and an export of a collapsed table that silently\n * dropped rows would be worse than one that shows them.\n */\n displayRows(): TRow[] {\n const sorted = this.sortedRows();\n if (this._group === null) return sorted;\n return [...this._groups(sorted).values()].flatMap(group => group.rows);\n }\n\n setRows(rows: TRow[]): void {\n this._rows = rows || [];\n this.render();\n // A set filter offers the values the rows actually hold, so new rows can bring\n // new options. The header is not rebuilt for it -- that would take the caret\n // out of a filter box mid-typing -- only the option list, and only if it moved.\n this._refreshFilterRow();\n }\n\n render(): void {\n this._paint();\n if (this.options.afterRender) this.options.afterRender();\n }\n\n private _paint(): void {\n this._rowEls.clear();\n this._cellEls.clear();\n\n const pinned = this.options.pinnedRows ? this.options.pinnedRows() : [];\n const sorted = this.sortedRows();\n const limit = this.options.renderLimit != null ? this.options.renderLimit : Infinity;\n\n const children: Node[] = [];\n for (const row of pinned) {\n if (row.place === GridPinnedPlacements.Top) children.push(this._pinnedRow(row));\n }\n\n let painted = 0;\n if (this._group === null) {\n for (const row of sorted) {\n if (painted >= limit) break;\n children.push(this._bodyRow(row));\n painted++;\n }\n } else {\n // Groups run in value order while the rows inside each keep the sort the\n // user picked -- sorting by quantity inside \"AAPL\" should not shuffle\n // \"AAPL\" itself against \"BTC\".\n for (const [key, group] of this._groups(sorted)) {\n if (painted >= limit) break;\n children.push(this._groupRow(key, group.label, group.rows.length));\n if (this._collapsed.has(key)) continue;\n for (const row of group.rows) {\n // The limit counts rows of data: a group header is a label for the\n // rows under it, and capping on it would cut a group off at its own title.\n if (painted >= limit) break;\n children.push(this._bodyRow(row));\n painted++;\n }\n }\n }\n\n // A pinned row is content, so a table showing only a balance summary is not\n // empty \u2014 the \"nothing here\" row would read as a contradiction next to it.\n if (painted === 0 && pinned.length === 0 && this._collapsed.size === 0) children.push(this._emptyRow());\n\n for (const row of pinned) {\n if (row.place === GridPinnedPlacements.Bottom) children.push(this._pinnedRow(row));\n }\n\n this.options.body.replaceChildren(...children);\n\n // What this paint showed becomes the baseline the next one flashes\n // against. Display keys rather than painted ones: a row past\n // `renderLimit` is held, not news, and must not flash when a later\n // paint reaches it. Unarmed until something has actually been on\n // screen \u2014 the constructor paints an empty table before any data\n // arrives, and the seed that follows is history, not news.\n if (this.options.flashNewClass) {\n const keys = this.displayRows().map(row => this.options.rowKey(row));\n if (this._flashKnown !== null || keys.length > 0) this._flashKnown = new Set(keys);\n }\n }\n\n /**\n * The rendered row with this key, or null when it is not on screen (filtered\n * out, or past `renderLimit`).\n */\n rowElement(rowKey: string): HTMLTableRowElement | null {\n return this._rowEls.get(rowKey) || null;\n }\n\n /**\n * The rendered cell at (row, column) \u2014 how a caller patches one cell in place\n * instead of repainting the table, and how it finds the cell to edit.\n */\n cellElement(rowKey: string, columnKey: string): HTMLTableCellElement | null {\n return this._cellEls.get(rowKey)?.get(columnKey) || null;\n }\n\n /**\n * Header captions and cell values of the exportable columns, over every held\n * row in the order in effect. Pure \u2014 separate from `download` so the sheet\n * content can be asserted without a browser.\n */\n exportData(): GridExportData {\n const columns = this.visibleColumns().filter(col => col.exportable);\n return {\n headers: columns.map(col => col.header),\n // Grouped, the sheet follows the order on screen but without the header\n // rows: a spreadsheet has its own grouping, and a label row in the middle\n // of the data would break every formula pointed at the range.\n rows: this.displayRows().map(row => columns.map(col => {\n const accessor = col.exportValue || col.value;\n return accessor ? accessor(row) : null;\n })),\n };\n }\n\n /** Export to `<baseName>-<timestamp>.xlsx` in the order on screen. */\n download(baseName: string, sheetName: string): void {\n const data = this.exportData();\n TableExport.download(baseName, sheetName, data.headers, data.rows);\n }\n\n private _renderHead(): void {\n // No caption row \u2014 the filter row, when on, still stands on its own.\n if (!this._headerVisible) {\n const filterRow = this._filterRow();\n if (filterRow) this.options.head.replaceChildren(filterRow);\n else this.options.head.replaceChildren();\n return;\n }\n\n const tr = document.createElement('tr');\n for (const col of this.visibleColumns()) {\n const th = document.createElement('th');\n th.setAttribute('scope', 'col');\n // The same key the body cells carry. Without it a right-click on the header\n // resolves no column, so the menu loses everything about it -- and any host\n // stamping the keys itself loses them on the next repaint.\n th.dataset.col = col.key;\n if (col.headerClass) th.className = col.headerClass;\n // Sortable exactly when there is a value to sort by. TableSort binds to\n // the cells carrying `data-sort`, so a column without one is inert.\n if (col.value) th.dataset.sort = col.key;\n if (col.headerHidden) {\n const label = document.createElement('span');\n label.className = 'visually-hidden';\n label.textContent = col.header;\n th.appendChild(label);\n } else {\n th.textContent = col.header;\n }\n if (this.options.reorderable) this._bindHeaderDrag(th, col.key);\n tr.appendChild(th);\n }\n\n const filters = this._filterRow();\n if (filters) this.options.head.replaceChildren(tr, filters);\n else this.options.head.replaceChildren(tr);\n }\n\n /**\n * The row of filter controls, or null when no visible column declares one.\n *\n * Every visible column gets a cell even when it has no filter, or the row would\n * shear away from the captions above it. The cells carry no `data-sort`, so a\n * click inside one does not reach the sort handler bound on the `<thead>`.\n */\n private _filterRow(): HTMLTableRowElement | null {\n const columns = this.visibleColumns();\n if (!this._filtersVisible || !columns.some(col => col.filter)) return null;\n\n const tr = document.createElement('tr') as HTMLTableRowElement;\n tr.className = 'grid-filters';\n this._filterEls = new Map();\n\n for (const col of columns) {\n const cell = document.createElement('th') as HTMLTableCellElement;\n cell.dataset.filter = col.key;\n if (col.filter) {\n for (const el of this._filterControls(col)) cell.appendChild(el);\n }\n tr.appendChild(cell);\n }\n return tr;\n }\n\n private _filterControls(col: GridColumn<TRow>): HTMLElement[] {\n const current = this._filters[col.key] || {};\n\n if (col.filter === 'text') {\n const input = document.createElement('input') as HTMLInputElement;\n input.type = 'text';\n input.className = 'grid-filter grid-filter-text';\n input.value = current.text || '';\n input.addEventListener('input', () => this.setFilter(col.key, { text: input.value }));\n this._filterEls.set(col.key, [input]);\n return [input];\n }\n\n if (col.filter === 'number') {\n const read = (el: HTMLInputElement) => {\n const n = Number(el.value);\n return el.value.trim() === '' || !Number.isFinite(n) ? undefined : n;\n };\n const make = (className: string, value: number | undefined) => {\n const input = document.createElement('input') as HTMLInputElement;\n input.type = 'number';\n input.className = `grid-filter ${className}`;\n input.value = value === undefined ? '' : String(value);\n return input;\n };\n const min = make('grid-filter-min', current.min);\n const max = make('grid-filter-max', current.max);\n const push = () => this.setFilter(col.key, { min: read(min), max: read(max) });\n min.addEventListener('input', push);\n max.addEventListener('input', push);\n this._filterEls.set(col.key, [min, max]);\n return [min, max];\n }\n\n // A set filter offers the values actually in the held rows -- all of them,\n // not the ones surviving the current filters, or picking one would empty the\n // list it was picked from.\n const select = document.createElement('select') as HTMLSelectElement;\n select.className = 'grid-filter grid-filter-set';\n const blank = document.createElement('option') as HTMLOptionElement;\n blank.value = '';\n blank.textContent = '';\n select.appendChild(blank);\n for (const { value, label } of this._distinctValues(col)) {\n const option = document.createElement('option') as HTMLOptionElement;\n option.value = value;\n option.textContent = label;\n select.appendChild(option);\n }\n select.value = current.values && current.values.length ? current.values[0] : '';\n select.addEventListener('change', () =>\n this.setFilter(col.key, select.value ? { values: [select.value] } : null));\n this._filterEls.set(col.key, [select]);\n return [select];\n }\n\n /// Replaces the option list only when the values behind it changed, so a live\n /// table that repaints on every tick does not rebuild a select the user is using.\n private _syncOptions(select: HTMLSelectElement, values: GridValueOption[]): void {\n const current = Array.from(select.children).slice(1).map(el => (el as HTMLOptionElement).value);\n if (current.length === values.length && current.every((v, i) => v === values[i].value)) return;\n\n const blank = document.createElement('option') as HTMLOptionElement;\n blank.value = '';\n blank.textContent = '';\n const options: HTMLElement[] = [blank];\n for (const { value, label } of values) {\n const option = document.createElement('option') as HTMLOptionElement;\n option.value = value;\n option.textContent = label;\n options.push(option);\n }\n select.replaceChildren(...options);\n }\n\n /// The value as a person reads it. One place, because a group header, a filter's\n /// option list and the menu line that filters by a value all have to agree -- and\n /// all of them have to disagree with `value`, which stays what the grid compares.\n private _displayText(col: GridColumn<TRow>, row: TRow): string {\n if (col.text) return col.text(row);\n return DataGrid._text(col.value ? col.value(row) : null);\n }\n\n /// Every value the column holds, paired with the label it is picked by. Keyed by\n /// the raw value: two rows reading \"Buy\" are one option, and the option carries\n /// the 0 the filter will actually compare against.\n private _distinctValues(col: GridColumn<TRow>): GridValueOption[] {\n const seen = new Map<string, string>();\n for (const row of this._rows) {\n if (!col.value) continue;\n const value = DataGrid._text(col.value(row));\n if (!seen.has(value)) seen.set(value, this._displayText(col, row));\n }\n return [...seen.entries()]\n .map(([value, label]) => ({ value, label }))\n .sort((a, b) => this._collator.compare(a.label, b.label));\n }\n\n /**\n * Put the stored filters back into the controls without rebuilding the row.\n * Rebuilding it would take the focus and the caret out of the box the user is\n * still typing in.\n */\n private _refreshFilterRow(): void {\n for (const [key, els] of this._filterEls) {\n const filter = this._filters[key] || {};\n if (els.length === 2) {\n (els[0] as HTMLInputElement).value = filter.min === undefined ? '' : String(filter.min);\n (els[1] as HTMLInputElement).value = filter.max === undefined ? '' : String(filter.max);\n } else if (els[0].tagName === 'SELECT') {\n const select = els[0] as HTMLSelectElement;\n const col = this.options.columns.find(c => c.key === key);\n if (col) this._syncOptions(select, this._distinctValues(col));\n select.value = filter.values && filter.values.length ? filter.values[0] : '';\n } else {\n const input = els[0] as HTMLInputElement;\n const want = filter.text || '';\n if (input.value !== want) input.value = want;\n }\n }\n }\n\n private _bodyRow(row: TRow): HTMLTableRowElement {\n const tr = document.createElement('tr') as HTMLTableRowElement;\n const key = this.options.rowKey(row);\n const rowClass = this.options.rowClass ? this.options.rowClass(row) : '';\n if (rowClass) tr.className = rowClass;\n tr.dataset.rowKey = key;\n\n const cells = new Map<string, HTMLTableCellElement>();\n for (const col of this.visibleColumns()) {\n const td = document.createElement('td') as HTMLTableCellElement;\n td.dataset.col = col.key;\n const cellClass = col.cellClass ? col.cellClass(row) : '';\n if (cellClass) td.className = cellClass;\n const content = col.render ? col.render(row) : DataGrid._text(col.value ? col.value(row) : null);\n // A renderer is declared to return a string or a Node, but a value that\n // fell through a formatter's branches arrives as whatever it was - a raw\n // status number, say. Passing that to appendChild throws and takes the\n // whole table down over one unexpected row, so anything that is not a Node\n // is rendered as text. nodeType rather than instanceof Node: the tests run\n // against a fake DOM with no Node constructor in scope.\n if (content !== null && typeof content === 'object' && 'nodeType' in content) td.appendChild(content);\n else td.textContent = DataGrid._text(content);\n if (col.bindCell) col.bindCell(td, row);\n tr.appendChild(td);\n cells.set(col.key, td);\n }\n\n if (this._selected.has(key)) {\n const selectedClass = this.options.selectedClass || 'is-selected';\n tr.className = tr.className ? `${tr.className} ${selectedClass}` : selectedClass;\n }\n // News since the previous paint. The check runs against the set as it\n // was BEFORE this paint started \u2014 `_paint` replaces it only after every\n // row is built.\n if (this.options.flashNewClass && this._flashKnown !== null && !this._flashKnown.has(key)) {\n tr.className = tr.className ? `${tr.className} ${this.options.flashNewClass}` : this.options.flashNewClass;\n }\n const mode = this.options.selection || 'none';\n if (mode !== 'none') {\n // Selection happens on mousedown, not on click, for two reasons that are\n // the same reason: that is when a real table responds, and that is when the\n // browser would otherwise start dragging a text selection across the rows.\n // preventDefault claims the gesture for the table.\n tr.addEventListener('mousedown', (event: Event) => {\n const button = (event as unknown as { button?: number }).button;\n if (button !== undefined && button !== 0) return; // right-click belongs to the menu\n // A cell may hold a real control, and the press that opens a <select> or focuses\n // an <input> is the same press selection answers. preventDefault below is what\n // stops the browser dragging a text selection across the rows - done to a press\n // that landed on a control, it also stops the control reacting at all, so the\n // row selects and the control reads as dead.\n if (isControl(event as unknown as { target?: unknown })) return;\n event.preventDefault();\n DataGrid._active = this as DataGrid<unknown>;\n this._clickRow(key, event);\n\n const m = event as unknown as { shiftKey?: boolean; ctrlKey?: boolean; metaKey?: boolean };\n if (mode === 'multi' && !m.shiftKey && !m.ctrlKey && !m.metaKey) this._beginDragSelect();\n });\n\n // Dragging across rows extends the selection, which is what the pointer\n // was doing anyway -- it just used to paint text instead of rows.\n tr.addEventListener('mouseenter', (event: Event) => {\n if (!this._dragging) return;\n // A mouseup released outside the window never reaches us, and a grid\n // stuck in drag mode selects whatever the pointer wanders over next.\n // The event carries which buttons are actually down; trust that.\n const buttons = (event as unknown as { buttons?: number }).buttons;\n if (buttons !== undefined && (buttons & 1) === 0) { this._dragging = false; return; }\n this._extendTo(key);\n });\n }\n\n if (this.options.bindRow) this.options.bindRow(tr, row);\n this._rowEls.set(key, tr);\n this._cellEls.set(key, cells);\n return tr;\n }\n\n private _pinnedRow(pinned: GridPinnedRow): HTMLTableRowElement {\n if (pinned.cells.length > this.options.columns.length) {\n throw new Error(`DataGrid: pinned row \"${pinned.key}\" declares ${pinned.cells.length} cells `\n + `for ${this.options.columns.length} columns`);\n }\n\n const tr = document.createElement('tr') as HTMLTableRowElement;\n if (pinned.className) tr.className = pinned.className;\n tr.dataset.rowKey = pinned.key;\n\n // The cells are authored positionally against the DECLARED columns, so they\n // are keyed by that before rendering: a summary cell belongs to its column,\n // and has to travel when the user moves it rather than stay in slot 0.\n const byColumn = new Map<string, GridPinnedCell>();\n this.options.columns.forEach((col, i) => {\n const cell = pinned.cells[i];\n if (cell) byColumn.set(col.key, cell);\n });\n\n const cells = new Map<string, HTMLTableCellElement>();\n for (const col of this.visibleColumns()) {\n const td = document.createElement('td') as HTMLTableCellElement;\n td.dataset.col = col.key;\n const cell = byColumn.get(col.key);\n if (cell) {\n if (cell.className) td.className = cell.className;\n if (typeof cell.content === 'string') td.textContent = cell.content;\n else td.appendChild(cell.content);\n }\n tr.appendChild(td);\n cells.set(col.key, td);\n }\n\n this._rowEls.set(pinned.key, tr);\n this._cellEls.set(pinned.key, cells);\n return tr;\n }\n\n /// Rows by their group value, in value order, each group keeping the row order\n /// it was handed.\n private _groups(rows: TRow[]): Map<string, GridGroup<TRow>> {\n const col = this.options.columns.find(c => c.key === this._group);\n const groups = new Map<string, GridGroup<TRow>>();\n for (const row of rows) {\n // Keyed by the raw value and labelled by the readable one: collapse state\n // and the stored view are written against the key, so a column that starts\n // spelling its values differently must not lose which groups were shut.\n const key = DataGrid._text(col && col.value ? col.value(row) : null);\n const group = groups.get(key);\n if (group) group.rows.push(row);\n else groups.set(key, { label: col ? this._displayText(col, row) : key, rows: [row] });\n }\n // Groups whose order is not their own are left where the sort put them: the first\n // row of each group decides, so the groups appear in the order the rows already\n // read in. A table sorted by something the reader chose keeps that arrangement\n // instead of being re-alphabetised behind their back.\n if (this.options.groupOrder === 'rows') return groups;\n\n // Otherwise by the label, so the groups read alphabetically in the language on\n // screen -- which does mean two languages show the same table in a different\n // group order, while the keys underneath, and any saved view, stay put.\n return new Map([...groups.entries()].sort((a, b) => this._collator.compare(a[1].label, b[1].label)));\n }\n\n private _groupRow(key: string, label: string, count: number): HTMLTableRowElement {\n const tr = document.createElement('tr') as HTMLTableRowElement;\n tr.className = this._collapsed.has(key) ? 'grid-group is-collapsed' : 'grid-group';\n tr.dataset.group = key;\n\n const collapsed = this._collapsed.has(key);\n const td = document.createElement('td') as HTMLTableCellElement;\n td.colSpan = this.visibleColumns().length;\n const content = this.options.groupHeader\n ? this.options.groupHeader(label, count, collapsed)\n : `${collapsed ? '\u25B8' : '\u25BE'} ${label} (${count})`;\n if (typeof content === 'string') td.textContent = content;\n else td.appendChild(content);\n tr.appendChild(td);\n\n tr.addEventListener('click', () => this.toggleGroup(key));\n // Set here rather than left to a stylesheet, because this package ships none: a cursor\n // an adopter has to declare is a cursor the next adopter forgets, and the row would then\n // act on a click while telling the pointer it does nothing. The two properties belong to\n // the handler above, not to a theme - what the row LOOKS like is still the adopter's,\n // through `grid-group`, and a rule there overrides these.\n tr.style.cursor = 'pointer';\n tr.style.userSelect = 'none';\n return tr;\n }\n\n private _emptyRow(): HTMLTableRowElement {\n const tr = document.createElement('tr') as HTMLTableRowElement;\n const td = document.createElement('td') as HTMLTableCellElement;\n td.className = 'grid-empty';\n td.colSpan = this.visibleColumns().length;\n td.textContent = this.options.emptyText;\n tr.appendChild(td);\n return tr;\n }\n\n /**\n * Drag a header to move its column in front of the one it is dropped on.\n *\n * The dragged key lives on the instance rather than in dataTransfer: the payload\n * is this grid's, and reading it back out of the event would mean a header\n * dragged from anywhere -- another grid, another window -- could reorder this one.\n */\n private _bindHeaderDrag(th: HTMLElement, key: string): void {\n th.setAttribute('draggable', 'true');\n\n th.addEventListener('dragstart', () => { this._dragKey = key; });\n th.addEventListener('dragend', () => { this._dragKey = null; });\n\n // Without preventDefault the browser refuses the drop, and the header just\n // springs back with nothing to show for the gesture.\n th.addEventListener('dragover', (event: Event) => {\n if (this._dragKey !== null && this._dragKey !== key) event.preventDefault();\n });\n\n th.addEventListener('drop', (event: Event) => {\n event.preventDefault();\n const moved = this._dragKey;\n this._dragKey = null;\n if (moved === null || moved === key) return;\n\n const order = this._order.filter(k => k !== moved);\n order.splice(order.indexOf(key), 0, moved);\n this._applyOrder(order);\n this._rerender();\n });\n }\n\n private static _text(value: unknown): string {\n return value === null || value === undefined ? '' : String(value);\n }\n}\n", "// Active orders \u2014 multi-instance.\n//\n// Builds its own DOM (see `_buildRoot`) rather than cloning a <template> out of\n// the page, so it can be constructed by any host that supplies a `TradingHost`.\n// Every instance shares the same data source (a host refresh / an order update\n// off the socket); each update fans through the host's broadcast so duplicates\n// stay in sync.\n//\n// The table itself is `DataGrid` from `@stocksharp/grids`: the columns below are\n// the single declaration of this blotter \u2014 caption, value, rendering, colour\n// class and export all live together, and the <thead> is left empty for the\n// grid to fill. Cells that hold a control return a real Node with its own\n// listener, so nothing here reaches a global through an inline onclick\n// attribute; the callbacks arrive as deps instead.\nimport { cleanRejectReason, formatPrice, formatQty } from './formatters.js';\nimport { makeElement, makeIconButton, makePanelId, makePanelRoot } from './dom.js';\nimport { ControlTypes } from './control-types.js';\nimport { makeGridMenu } from './grid-menu.js';\nimport { TradingHost, assertHost } from './trading-host.js';\nimport type { OrderRow } from './trading-data.js';\nimport { DataGrid, GridColumn } from '@stocksharp/grids/source/data-grid';\n\n/// StockSharp order states as they arrive on the wire. The blotter branches on\n/// them for row colour, for whether a cell can be edited in place and for what\n/// the \u00D7 button does, so they are named once here instead of appearing as bare\n/// numbers at each branch.\nexport const OrderStates = {\n PendingRisk: 1,\n Sent: 2,\n Active: 3,\n PartiallyFilled: 4,\n Filled: 5,\n Rejected: 6,\n Cancelled: 7,\n} as const;\n\n/// The gestures that reach a venue. All required together: a panel that can\n/// cancel one order but not all of them is a panel half-wired.\nexport interface ActiveOrdersActions {\n cancelOrder(orderId: number): void;\n dismissOrder(orderId: number): void;\n editOrderField(orderId: number, field: string): void;\n replaceOrder(orderId: number, quantity: number, limitPrice: number, stopPrice: number): void;\n cancelAllOrders(): void;\n refreshOrders(): void;\n}\n\n/// What the blotter needs beyond the host port, in one of two shapes.\n///\n/// A live blotter carries every action, because a panel that cannot cancel,\n/// modify or reload its orders is not that panel. A read-only one carries none:\n/// over a finished run - a backtest report, an archived session - there is no\n/// venue left to reach, and the gestures that would reach it are not rendered\n/// rather than rendered and wired to nothing. The union states that, so a live\n/// consumer still cannot omit an action by accident.\nexport type ActiveOrdersDeps =\n | ({ host: TradingHost; readOnly?: false } & ActiveOrdersActions)\n | { host: TradingHost; readOnly: true };\n\nexport class ActiveOrdersWidget {\n static TYPE = ControlTypes.ActiveOrders;\n\n rootEl: HTMLElement;\n el: HTMLElement | null;\n // `//` rather than `///` from here down \u2014 see the note in positions-widget.\n _host: TradingHost;\n _deps: ActiveOrdersDeps;\n _readOnly: boolean;\n _actions: ActiveOrdersActions | null;\n _closeBtn: HTMLElement | null;\n _cancelAllBtn: HTMLElement | null;\n _refreshBtn: HTMLElement | null;\n _exportBtn: HTMLElement | null;\n _orders: OrderRow[];\n _grid: DataGrid<OrderRow> | null;\n\n static create(hostEl: HTMLElement, state: Record<string, unknown>, deps: ActiveOrdersDeps): ActiveOrdersWidget {\n // Assert before building: the markup below is localized through the\n // host, so a missing host has to fail here rather than render a panel\n // captioned with raw English keys.\n const host = assertHost(deps?.host, 'ActiveOrdersWidget');\n const root = ActiveOrdersWidget._buildRoot(host, deps?.readOnly === true);\n root.id = makePanelId(ActiveOrdersWidget.TYPE);\n hostEl.appendChild(root);\n return new ActiveOrdersWidget(root, state || {}, deps);\n }\n\n // The panel's markup. The host stylesheet reads this structure, and a\n // docking host lifts `.panel-header`'s children into its tab strip.\n //\n // The rail's first two buttons are the ones that could not come across\n // unchanged from the terminal's Razor template: they were inline\n // `onclick=\"terminalApp.cancelAll()\"` / `refreshOrders()`, references to a\n // global this control must not have. They carry `.panel-cancel-all-btn` and\n // `.panel-refresh-btn` now \u2014 the latter the name the other blotters' rails\n // already give the identical button \u2014 and are wired to deps. Cancel-all\n // keeps a distinct accessible name from its tooltip, which is why it states\n // `aria-label` rather than taking the tooltip for both.\n static _buildRoot(host: TradingHost, readOnly = false): HTMLElement {\n return makePanelRoot('active-orders-panel', host.t('ActiveOrders'), [\n makeElement('div', 'panel-header', {}, [\n makeElement('span', '', {}, [host.t('OpenOrders')]),\n makeIconButton('bt-icon-btn bt-icon-cancel panel-close-btn', host.t('ClosePanel'), 'bi-x', { type: 'button' }),\n ]),\n makeElement('div', 'panel-body panel-body-with-rail', {}, [\n makeElement('div', 'panel-body-content', {}, [\n makeElement('table', 'terminal-table active-orders-table', { role: 'table', 'aria-label': host.t('ActiveOrdersList') }, [\n makeElement('thead', '', {}, []),\n makeElement('tbody', 'active-orders-body', {}, []),\n ]),\n ]),\n makeElement('div', 'panel-rail', { role: 'toolbar', 'aria-label': host.t('ActiveOrdersActions') }, [\n ...(readOnly ? [] : [\n makeIconButton('bt-icon-btn bt-icon-cancel-all panel-cancel-all-btn', host.t('CancelAll'), 'bi-x-circle',\n { 'aria-label': host.t('CancelAllOrders') }),\n makeIconButton('bt-icon-btn panel-refresh-btn', host.t('Refresh'), 'bi-arrow-clockwise', {}),\n ]),\n makeIconButton('bt-icon-btn panel-export-btn', host.t('ExportToExcel'), 'bi-file-earmark-spreadsheet', {}),\n ]),\n ]),\n ]);\n }\n\n constructor(rootEl: HTMLElement, _state: Record<string, unknown>, deps: ActiveOrdersDeps) {\n this._host = assertHost(deps?.host, 'ActiveOrdersWidget');\n this._readOnly = deps?.readOnly === true;\n if (this._readOnly) {\n this._actions = null;\n } else {\n const actions = deps as ActiveOrdersActions;\n for (const name of ['cancelOrder', 'dismissOrder', 'editOrderField', 'replaceOrder', 'cancelAllOrders', 'refreshOrders'] as const) {\n if (typeof actions?.[name] !== 'function')\n throw new Error(`ActiveOrdersWidget: dep \"${name}\" is required`);\n }\n this._actions = actions;\n }\n\n this.rootEl = rootEl;\n this._deps = deps;\n this.el = this.rootEl.querySelector('.active-orders-body');\n this._closeBtn = this.rootEl.querySelector('.panel-close-btn');\n this._cancelAllBtn = this.rootEl.querySelector('.panel-cancel-all-btn');\n this._refreshBtn = this.rootEl.querySelector('.panel-refresh-btn');\n this._exportBtn = this.rootEl.querySelector('.panel-export-btn');\n this._orders = [];\n\n this._closeBtn?.addEventListener('click', (e) => {\n e.preventDefault();\n this._host.close();\n });\n\n this._cancelAllBtn?.addEventListener('click', (e) => {\n e.preventDefault();\n this._actions?.cancelAllOrders();\n });\n\n this._refreshBtn?.addEventListener('click', (e) => {\n e.preventDefault();\n this._actions?.refreshOrders();\n });\n\n this._exportBtn?.addEventListener('click', (e) => {\n e.preventDefault();\n this._export();\n });\n\n const head = this.rootEl.querySelector('.active-orders-table thead');\n this._grid = head && this.el\n ? new DataGrid<OrderRow>({\n head: head as HTMLElement,\n body: this.el,\n columns: this._columns(),\n // Newest order on top by default (localId is monotonic) \u2014 the resting view puts the\n // user's most recent actions where they look, instead of raw cache order. Natural\n // array order is \"newest first\" too (deltas unshift), but only by accident; any\n // header click re-renders the same array through the sorter.\n defaultSort: { col: 'id', dir: 'desc' },\n // The register tx, not the friendly localId shown in the ID column: it is what\n // cancel / replace / inline edit address an order by.\n rowKey: (o) => String(o.id),\n emptyText: this._host.t('NoActiveOrders'),\n // Terminal rows are greyed out so the live ones stay visually prominent.\n rowClass: (o) => ActiveOrdersWidget._rowClass(o),\n contextMenu: makeGridMenu(this._host),\n // Press to mark, drag or Shift to range, Ctrl to toggle, Ctrl+C\n // to copy \u2014 all the grid's own; the press also claims the\n // gesture from the browser's text selection.\n selection: 'multi',\n })\n : null;\n\n this._host.register(this);\n }\n\n dispose(): void {\n // The grid holds document-level listeners (the copy shortcut) that\n // outlive a removed subtree \u2014 it has to be told, not just detached.\n this._grid?.destroy();\n this._host.unregister(this);\n try { this.rootEl.remove(); } catch { /* already detached */ }\n }\n\n update(orders: OrderRow[]): void {\n this._orders = orders || [];\n this._grid?.setRows(this._orders);\n }\n\n // Export the table to .xlsx in the currently rendered (sorted) order, with the\n // same localized side/type/status texts the user sees on screen \u2014 the columns\n // carry both forms, so the sheet cannot drift from the table.\n _export(): void {\n this._grid?.download('orders', this._host.t('OpenOrders'));\n }\n\n /// Apply a single order delta from the feed. Insert if new, merge if known.\n /// Nothing drops out of the list: Filled and Cancelled rows stay so the user\n /// can watch an order's transitions instead of having a row vanish, and a\n /// Rejected row stays so its reason can be read on hover and dismissed\n /// explicitly through the \u00D7 button.\n applyDelta(order: OrderRow): void {\n if (!order || order.id == null) return;\n const idx = this._orders.findIndex(o => o.id === order.id);\n if (idx >= 0)\n this._orders[idx] = { ...this._orders[idx], ...order };\n else\n this._orders.unshift(order);\n this.update(this._orders);\n }\n\n /// Remove a row from the local view without touching the server. Used by the\n /// \u00D7 button on a terminal order (nothing left to cancel), and in any other\n /// client-side dismiss path.\n removeOrder(orderId: number): void {\n const idx = this._orders.findIndex(o => o.id === orderId);\n if (idx >= 0) {\n this._orders.splice(idx, 1);\n this.update(this._orders);\n }\n }\n\n getOrder(orderId: number): OrderRow | undefined {\n return this._orders.find(o => o.id === orderId);\n }\n\n startInlineEdit(orderId: number, field: 'quantity' | 'limitPrice' | 'stopPrice'): void {\n const order = this.getOrder(orderId);\n if (!order) return;\n // Addressed by the grid's row key (the register tx) and the column key, so\n // the edit lands on the right cell whatever the sort is. A lookup that\n // scanned the rendered ID column for the order id would never match, because\n // that column shows the friendly counter.\n const cell = this._grid?.cellElement(String(orderId), field);\n if (!cell || cell.querySelector('input')) return;\n const currentValue = order[field];\n const input = document.createElement('input');\n input.type = 'number';\n input.className = 'inline-edit-input';\n input.value = String(currentValue ?? '');\n input.step = field === 'quantity' ? '1' : '0.01';\n input.min = field === 'quantity' ? '1' : '0.01';\n cell.textContent = '';\n cell.appendChild(input);\n input.focus();\n input.select();\n const commit = () => {\n const newVal = parseFloat(input.value);\n if (isNaN(newVal) || newVal <= 0) { this.update(this._orders); return; }\n if (newVal === currentValue) { this.update(this._orders); return; }\n const updated = { quantity: order.quantity, limitPrice: order.limitPrice, stopPrice: order.stopPrice };\n updated[field] = newVal;\n this._actions?.replaceOrder(orderId, updated.quantity!, updated.limitPrice!, updated.stopPrice!);\n };\n input.addEventListener('blur', commit);\n input.addEventListener('keydown', (e) => {\n if (e.key === 'Enter') { e.preventDefault(); input.blur(); }\n if (e.key === 'Escape') { this.update(this._orders); }\n });\n }\n\n // The blotter's single column declaration: what the header says, what the\n // grid sorts on, what the cell shows, how it is coloured, and what reaches\n // the exported sheet.\n _columns(): GridColumn<OrderRow>[] {\n // `label`, not `t` \u2014 every row lambda below binds `o` for the order,\n // but keeping the translator under a name of its own reads clearer\n // next to the presentation object it sits beside.\n const label = (key: string) => this._host.t(key);\n const presentation = this._host.presentation;\n return [\n {\n key: 'id',\n header: label('ID'),\n exportable: true,\n // Per-user friendly counter (1, 2, 3, \u2026) issued by the host. `o.id`\n // keeps the register tx for cancel / replace routing; `o.localId` is\n // just the badge the user wants to see. Rows cached from before the\n // counter shipped have no localId \u2014 fall back to id so they don't go\n // blank until the next snapshot replay reassigns one.\n value: (o) => o.localId ?? o.id,\n },\n {\n key: 'instrument',\n header: label('Sym'),\n exportable: true,\n value: (o) => o.instrument,\n },\n {\n key: 'side',\n header: label('Side'),\n exportable: true,\n value: (o) => o.side,\n render: (o) => presentation.sideText(o.side!),\n cellClass: (o) => presentation.sideClass(o.side!),\n exportValue: (o) => presentation.sideText(o.side!),\n },\n {\n key: 'type',\n header: label('Type'),\n exportable: true,\n value: (o) => o.type,\n render: (o) => presentation.typeText(o.type!, o.limitPrice!, o.stopPrice!),\n exportValue: (o) => presentation.typeText(o.type!, o.limitPrice!, o.stopPrice!),\n },\n {\n key: 'quantity',\n header: label('Qty'),\n exportable: true,\n value: (o) => o.quantity,\n render: (o) => formatQty(o.quantity),\n cellClass: (o) => this._readOnly ? '' : ActiveOrdersWidget._editableClass(o, 'quantity'),\n bindCell: (td, o) => this._bindInlineEdit(td, o, 'quantity'),\n },\n {\n key: 'limitPrice',\n header: label('Price'),\n exportable: true,\n value: (o) => o.limitPrice,\n render: (o) => o.limitPrice ? formatPrice(o.limitPrice) : label('MKT'),\n cellClass: (o) => this._readOnly ? '' : ActiveOrdersWidget._editableClass(o, 'limitPrice'),\n bindCell: (td, o) => this._bindInlineEdit(td, o, 'limitPrice'),\n exportValue: (o) => o.limitPrice ?? label('MKT'),\n },\n {\n key: 'stopPrice',\n header: label('Stop'),\n exportable: true,\n value: (o) => o.stopPrice,\n render: (o) => o.stopPrice ? formatPrice(o.stopPrice) : '--',\n cellClass: (o) => this._readOnly ? '' : ActiveOrdersWidget._editableClass(o, 'stopPrice'),\n bindCell: (td, o) => this._bindInlineEdit(td, o, 'stopPrice'),\n exportValue: (o) => o.stopPrice ?? '',\n },\n {\n key: 'status',\n header: label('Status'),\n exportable: true,\n value: (o) => o.status,\n render: (o) => this._statusCell(o),\n exportValue: (o) => presentation.statusText(o.status!),\n },\n ...(this._readOnly ? [] : [{\n key: 'actions',\n header: label('Actions'),\n headerHidden: true,\n exportable: false,\n cellClass: () => 'position-actions',\n render: (o: OrderRow) => this._actionButton(o),\n } as GridColumn<OrderRow>]),\n ];\n }\n\n static _rowClass(order: OrderRow): string {\n if (order.status === OrderStates.Filled) return 'order-filled';\n if (order.status === OrderStates.Rejected) return 'order-rejected';\n if (order.status === OrderStates.Cancelled) return 'order-cancelled';\n return '';\n }\n\n // An order can still be modified while the venue holds it. A market order has\n // no limit price and an unconditional order no stop price, so those cells have\n // nothing to edit even then.\n static _canEdit(order: OrderRow, field: 'quantity' | 'limitPrice' | 'stopPrice'): boolean {\n const modifiable = order.status === OrderStates.Active || order.status === OrderStates.Sent;\n return modifiable && (field === 'quantity' || !!order[field]);\n }\n\n static _editableClass(order: OrderRow, field: 'quantity' | 'limitPrice' | 'stopPrice'): string {\n return ActiveOrdersWidget._canEdit(order, field) ? 'cell-editable' : '';\n }\n\n // Double-click to edit belongs to the whole cell rather than to a control\n // inside it, which is why it is wired here instead of returned by render().\n _bindInlineEdit(td: HTMLTableCellElement, order: OrderRow, field: 'quantity' | 'limitPrice' | 'stopPrice'): void {\n if (this._actions === null || !ActiveOrdersWidget._canEdit(order, field)) return;\n const edit = this._actions.editOrderField;\n td.addEventListener('dblclick', () => edit(order.id!, field));\n }\n\n // Status text, plus a hoverable icon carrying the rejection reason. Some\n // venues wrap the human-readable rejection inside a JSON blob;\n // cleanRejectReason extracts `.message` and falls back to the raw string.\n // The reason goes on the element's title property, so no quote escaping is\n // involved the way it was when this row was built as markup.\n _statusCell(order: OrderRow): string | Node {\n const text = this._host.presentation.statusText(order.status!);\n const reason = order.status === OrderStates.Rejected ? cleanRejectReason(order.rejectReason) : '';\n if (!reason) return text;\n\n const cell = document.createDocumentFragment();\n cell.appendChild(document.createTextNode(text + ' '));\n const icon = document.createElement('i');\n icon.className = 'bi bi-question-circle reject-reason-icon';\n icon.title = reason;\n cell.appendChild(icon);\n return cell;\n }\n\n // The \u00D7 button, wired to the controller through deps. Status decides what it\n // means: Pending / Active is a real cancel over the wire, while Filled /\n // Rejected / Cancelled have nothing left to cancel \u2014 the row is history kept\n // around so the user can read its final state, so the button dismisses it\n // locally.\n _actionButton(order: OrderRow): Node {\n const isTerminal = order.status === OrderStates.Filled\n || order.status === OrderStates.Rejected\n || order.status === OrderStates.Cancelled;\n const title = isTerminal ? this._host.t('Dismiss') : this._host.t('Cancel order');\n\n const button = document.createElement('button');\n button.type = 'button';\n button.className = 'bt-icon-btn bt-icon-cancel';\n button.title = title;\n button.setAttribute('aria-label', `${title} #${order.id}`);\n const icon = document.createElement('i');\n icon.className = 'bi bi-x-circle';\n button.appendChild(icon);\n button.addEventListener('click', () => {\n if (this._actions === null) return;\n if (isTerminal) this._actions.dismissOrder(order.id!);\n else this._actions.cancelOrder(order.id!);\n });\n return button;\n }\n}\n", "// Positions \u2014 multi-instance.\n//\n// Builds its own DOM (see `_buildRoot`) rather than cloning a <template> out of\n// the page it happens to be rendered on, so it can be constructed by any host\n// that supplies a `TradingHost`. Every instance shares the same data source\n// (the host's refresh / a position update off the socket); the host broadcasts\n// each update to every live instance so duplicates of the panel render in\n// lockstep.\n//\n// The table is `DataGrid` from `@stocksharp/grids`: the columns below are this\n// blotter's single declaration and the <thead> is left empty for the grid to\n// fill. The cash balance is a pinned row \u2014 a different shape from a position,\n// so it supplies its own cells, sits outside the sort and stays out of the\n// exported sheet.\nimport { formatPnl, formatPrice } from './formatters.js';\nimport { makeElement, makeIconButton, makePanelId, makePanelRoot } from './dom.js';\nimport { ControlTypes } from './control-types.js';\nimport { makeGridMenu } from './grid-menu.js';\nimport { TradingHost, assertHost } from './trading-host.js';\nimport type { BalanceRow, PositionRow } from './trading-data.js';\nimport { DataGrid, GridColumn, GridPinnedRow, GridPinnedPlacements } from '@stocksharp/grids/source/data-grid';\n\n/// Everything the blotter needs beyond the host port. All required: the action\n/// buttons live in the cells and in the rail now, so a panel that cannot\n/// close, reverse or reload a position is not this panel.\nexport interface PositionsDeps {\n host: TradingHost;\n closePosition(portfolioId: number, instrumentId: number, symbol: string): void;\n reversePosition(portfolioId: number, instrumentId: number, symbol: string): void;\n refreshPositions(): void;\n}\n\nexport class PositionsWidget {\n static TYPE = ControlTypes.Positions;\n\n rootEl: HTMLElement;\n el: HTMLElement | null;\n // Comment style is `//` on purpose below the public members: the declaration\n // emitter keeps a private member's doc comment while dropping its body, so a\n // `///` here would reattach to the next public member in the API snapshot.\n _host: TradingHost;\n _deps: PositionsDeps;\n _closeBtn: HTMLElement | null;\n _refreshBtn: HTMLElement | null;\n _exportBtn: HTMLElement | null;\n _positions: PositionRow[];\n _balance: BalanceRow | null;\n _grid: DataGrid<PositionRow> | null;\n\n static create(hostEl: HTMLElement, state: Record<string, unknown>, deps: PositionsDeps): PositionsWidget {\n // Assert before building: the markup below is localized through the\n // host, so a missing host has to fail here rather than render a panel\n // captioned with raw English keys.\n const host = assertHost(deps?.host, 'PositionsWidget');\n const root = PositionsWidget._buildRoot(host);\n root.id = makePanelId(PositionsWidget.TYPE);\n hostEl.appendChild(root);\n return new PositionsWidget(root, state || {}, deps);\n }\n\n // The panel's markup. The host stylesheet reads this structure, and a\n // docking host lifts `.panel-header`'s children into its tab strip.\n //\n // The rail's refresh button is the one thing that could not come across\n // unchanged from the terminal's Razor template: it used to be an inline\n // `onclick=\"terminalApp.refreshPositions()\"`, which is a reference to a\n // global this control must not have. It carries `.panel-refresh-btn` now \u2014\n // the name the trade-history rail already gave the identical button \u2014 and\n // is wired to the `refreshPositions` dep.\n static _buildRoot(host: TradingHost): HTMLElement {\n const title = host.t('OpenPositions');\n return makePanelRoot('positions-panel', title, [\n makeElement('div', 'panel-header', {}, [\n makeElement('span', '', {}, [host.t('Positions')]),\n makeIconButton('bt-icon-btn bt-icon-cancel panel-close-btn', host.t('ClosePanel'), 'bi-x', { type: 'button' }),\n ]),\n makeElement('div', 'panel-body panel-body-with-rail', {}, [\n makeElement('div', 'panel-body-content', {}, [\n makeElement('table', 'terminal-table positions-table', { role: 'table', 'aria-label': title }, [\n makeElement('thead', '', {}, []),\n makeElement('tbody', 'positions-body', {}, []),\n ]),\n ]),\n makeElement('div', 'panel-rail', { role: 'toolbar', 'aria-label': host.t('PositionsActions') }, [\n makeIconButton('bt-icon-btn panel-refresh-btn', host.t('Refresh'), 'bi-arrow-clockwise', {}),\n makeIconButton('bt-icon-btn panel-export-btn', host.t('ExportToExcel'), 'bi-file-earmark-spreadsheet', {}),\n ]),\n ]),\n ]);\n }\n\n constructor(rootEl: HTMLElement, _state: Record<string, unknown>, deps: PositionsDeps) {\n this._host = assertHost(deps?.host, 'PositionsWidget');\n for (const name of ['closePosition', 'reversePosition', 'refreshPositions'] as const) {\n if (typeof deps?.[name] !== 'function')\n throw new Error(`PositionsWidget: dep \"${name}\" is required`);\n }\n\n this.rootEl = rootEl;\n this._deps = deps;\n this.el = this.rootEl.querySelector('.positions-body');\n this._closeBtn = this.rootEl.querySelector('.panel-close-btn');\n this._refreshBtn = this.rootEl.querySelector('.panel-refresh-btn');\n this._exportBtn = this.rootEl.querySelector('.panel-export-btn');\n this._positions = [];\n this._balance = null;\n\n this._closeBtn?.addEventListener('click', (e) => {\n e.preventDefault();\n this._host.close();\n });\n\n this._refreshBtn?.addEventListener('click', (e) => {\n e.preventDefault();\n this._deps.refreshPositions();\n });\n\n this._exportBtn?.addEventListener('click', (e) => {\n e.preventDefault();\n this._export();\n });\n\n const head = this.rootEl.querySelector('.positions-table thead');\n this._grid = head && this.el\n ? new DataGrid<PositionRow>({\n head: head as HTMLElement,\n body: this.el,\n columns: this._columns(),\n // Alphabetical by instrument at rest \u2014 a stable, predictable order (positions have no\n // natural \"newest\") the user can scan by symbol.\n defaultSort: { col: 'instrument', dir: 'asc' },\n // Same identity applyDelta matches on: id alone isn't unique (id=0 for\n // unsaved snapshots), and a portfolio can hold one position per instrument.\n rowKey: (p) => PositionsWidget._key(p),\n emptyText: this._host.t('No positions'),\n // Re-read on every render, so the cash figures track the live balance\n // without the widget having to repaint the position rows itself.\n pinnedRows: () => this._pinnedRows(),\n contextMenu: makeGridMenu(this._host),\n // Press to mark, drag or Shift to range, Ctrl to toggle, Ctrl+C\n // to copy \u2014 the pinned cash row stays outside the selection the\n // same way it stays outside the sort.\n selection: 'multi',\n })\n : null;\n\n this._host.register(this);\n\n // If the host already has a positions snapshot it will broadcast it on\n // demand; new instances start empty until the first refresh tick.\n }\n\n dispose(): void {\n // The grid holds document-level listeners (the copy shortcut) that\n // outlive a removed subtree \u2014 it has to be told, not just detached.\n this._grid?.destroy();\n this._host.unregister(this);\n try { this.rootEl.remove(); } catch { /* already detached */ }\n }\n\n update(positions: PositionRow[]): void {\n this._positions = positions || [];\n this._grid?.setRows(this._positions);\n }\n\n /// Null clears the pinned row \u2014 which is not the same as an all-zero\n /// balance, and reads differently: no row at all versus a row of zeros.\n updateBalance(balance: BalanceRow | null): void {\n this._balance = balance;\n this._grid?.render();\n }\n\n /// Apply a single position delta. Match by (portfolioId, instrumentId) \u2014 id\n /// alone isn't unique (id=0 for unsaved snapshots). Quantity hitting zero\n /// closes the row.\n applyDelta(position: PositionRow): void {\n if (!position) return;\n const idx = this._positions.findIndex(p =>\n p.portfolioId === position.portfolioId &&\n ((position.instrumentId != null && p.instrumentId === position.instrumentId) ||\n (position.instrumentId == null && p.instrument === position.instrument)));\n const closed = (position.quantity || 0) === 0;\n\n if (idx >= 0) {\n if (closed) this._positions.splice(idx, 1);\n else this._positions[idx] = { ...this._positions[idx], ...position };\n } else if (!closed) {\n this._positions.push(position);\n }\n this.update(this._positions);\n }\n\n // Export the table to .xlsx in the currently rendered (sorted) order. The\n // balance summary is a pinned row \u2014 a different shape from a position \u2014 so\n // the grid keeps it out of the sheet.\n _export(): void {\n this._grid?.download('positions', this._host.t('Positions'));\n }\n\n // The blotter's single column declaration: what the header says, what the\n // grid sorts on, what the cell shows, how it is coloured, and what reaches\n // the exported sheet. Prices are formatted on screen but exported raw, so\n // the sheet stays numeric.\n _columns(): GridColumn<PositionRow>[] {\n const label = (key: string) => this._host.t(key);\n return [\n {\n key: 'instrument',\n header: label('Symbol'),\n exportable: true,\n value: (p) => p.instrument,\n },\n {\n key: 'quantity',\n header: label('Qty'),\n exportable: true,\n value: (p) => p.quantity,\n cellClass: (p) => ((p.quantity ?? 0) > 0 ? 'side-buy' : 'side-sell'),\n },\n {\n key: 'avgPrice',\n header: label('AvgPrice'),\n exportable: true,\n value: (p) => p.avgPrice,\n render: (p) => formatPrice(p.avgPrice),\n },\n {\n key: 'currentPrice',\n header: label('Current'),\n exportable: true,\n value: (p) => p.currentPrice,\n render: (p) => formatPrice(p.currentPrice),\n },\n {\n key: 'pnl',\n header: label('PnL'),\n exportable: true,\n value: (p) => PositionsWidget._totalPnl(p),\n render: (p) => formatPnl(PositionsWidget._totalPnl(p)),\n cellClass: (p) => this._host.presentation.pnlClass(PositionsWidget._totalPnl(p)),\n },\n {\n key: 'actions',\n header: label('Actions'),\n headerHidden: true,\n exportable: false,\n cellClass: () => 'position-actions',\n render: (p) => this._actionButtons(p),\n },\n ];\n }\n\n // The cash balance, pinned above the positions. It does not share the shape\n // the position columns read, so it supplies its own cells; it is not a row\n // of data, so it is neither sorted nor exported. Being content, it also\n // suppresses the \"no positions\" row \u2014 a table showing a balance is not empty.\n _pinnedRows(): GridPinnedRow[] {\n const balance = this._balance;\n if (!balance) return [];\n\n const fmt = (v: number | null | undefined) => (v || 0).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 });\n return [{\n key: 'balance',\n className: 'balance-row',\n place: GridPinnedPlacements.Top,\n cells: [\n { content: this._host.t('USD'), className: 'balance-ccy' },\n { content: '$' + fmt(balance.available), className: 'balance-available' },\n { content: this._host.t('Locked: ${0}', fmt(balance.locked)), className: 'balance-locked' },\n { content: '', className: '' },\n { content: '$' + fmt(balance.total), className: 'balance-total' },\n ],\n }];\n }\n\n // Realized and unrealized together \u2014 what the PnL column shows, sorts and\n // exports, so the three cannot drift apart.\n static _totalPnl(position: PositionRow): number {\n return (position.unrealizedPnl || 0) + (position.realizedPnl || 0);\n }\n\n static _key(position: PositionRow): string {\n return `${position.portfolioId}:${position.instrumentId ?? position.instrument}`;\n }\n\n // Close and reverse, wired to the controller through deps. Real buttons with\n // their own listeners \u2014 the symbol used to be interpolated into an inline\n // onclick attribute, which is why it needed quote-escaping first.\n _actionButtons(position: PositionRow): Node {\n const cell = document.createDocumentFragment();\n cell.appendChild(this._actionButton(\n 'bt-icon-cancel', 'bi-x-circle',\n this._host.t('Close position'), this._host.t('Close position on {0}', position.instrument),\n () => this._deps.closePosition(position.portfolioId!, position.instrumentId!, position.instrument!)));\n cell.appendChild(this._actionButton(\n 'bt-icon-reverse', 'bi-arrow-left-right',\n this._host.t('Reverse position'), this._host.t('Reverse position on {0}', position.instrument),\n () => this._deps.reversePosition(position.portfolioId!, position.instrumentId!, position.instrument!)));\n return cell;\n }\n\n _actionButton(styleClass: string, iconClass: string, title: string, label: string, onClick: () => void): HTMLButtonElement {\n const button = document.createElement('button');\n button.type = 'button';\n button.className = `bt-icon-btn ${styleClass}`;\n button.title = title;\n button.setAttribute('aria-label', label);\n const icon = document.createElement('i');\n icon.className = `bi ${iconClass}`;\n button.appendChild(icon);\n button.addEventListener('click', onClick);\n return button;\n }\n}\n", "// Trade history \u2014 multi-instance.\n//\n// Read-only table of executed trades for the active portfolio. Builds its own\n// DOM (see `_buildRoot`) rather than cloning a <template> out of the page, so\n// it can be constructed by any host that supplies a `TradingHost`.\n//\n// Which portfolio to load and what to load it through both come off the host's\n// trading context rather than being read out of a singleton here: the panel\n// renders what it is given, and the host decides what \"the active portfolio\"\n// means.\n//\n// The table is `DataGrid` from `@stocksharp/grids`: the columns below are this\n// blotter's single declaration and the <thead> is left empty for the grid to\n// fill.\nimport { formatPrice, formatQty } from './formatters.js';\nimport { makeElement, makeIconButton, makePanelId, makePanelRoot } from './dom.js';\nimport { ControlTypes } from './control-types.js';\nimport { makeGridMenu } from './grid-menu.js';\nimport { TradingHost, assertHost } from './trading-host.js';\nimport type { TradeRow } from './trading-data.js';\nimport { DataGrid, GridColumn } from '@stocksharp/grids/source/data-grid';\n\n/// The panel needs nothing beyond the host port: its data source, its\n/// portfolio and its lifecycle all arrive through it.\nexport interface TradeHistoryDeps {\n host: TradingHost;\n /// A blotter over a finished run - a backtest report, an archived session.\n /// There is no account to load from and nothing to reload, so the rows come\n /// in through `update` and the refresh gesture is not rendered.\n readOnly?: boolean;\n}\n\nexport class TradeHistoryWidget {\n static TYPE = ControlTypes.TradeHistory;\n\n rootEl: HTMLElement;\n bodyEl: HTMLElement | null;\n // `//` rather than `///` from here down \u2014 see the note in positions-widget.\n _host: TradingHost;\n _closeBtn: HTMLElement | null;\n _refreshBtn: HTMLElement | null;\n _exportBtn: HTMLElement | null;\n _rows: TradeRow[];\n _grid: DataGrid<TradeRow> | null;\n\n static create(hostEl: HTMLElement, state: Record<string, unknown>, deps: TradeHistoryDeps): TradeHistoryWidget {\n // Assert before building: the markup below is localized through the\n // host, so a missing host has to fail here rather than render a panel\n // captioned with raw English keys.\n const host = assertHost(deps?.host, 'TradeHistoryWidget');\n const root = TradeHistoryWidget._buildRoot(host, deps?.readOnly === true);\n root.id = makePanelId(TradeHistoryWidget.TYPE);\n hostEl.appendChild(root);\n return new TradeHistoryWidget(root, state || {}, deps);\n }\n\n // The panel's markup. The host stylesheet reads this structure, and a\n // docking host lifts `.panel-header`'s children into its tab strip.\n static _buildRoot(host: TradingHost, readOnly = false): HTMLElement {\n const title = host.t('TradeHistory');\n return makePanelRoot('trade-history-panel', title, [\n makeElement('div', 'panel-header', {}, [\n makeElement('span', '', {}, [title]),\n makeIconButton('bt-icon-btn bt-icon-cancel panel-close-btn', host.t('ClosePanel'), 'bi-x', { type: 'button' }),\n ]),\n makeElement('div', 'panel-body panel-body-with-rail', {}, [\n makeElement('div', 'panel-body-content', {}, [\n makeElement('table', 'terminal-table trade-history-table', { role: 'table', 'aria-label': host.t('TradeHistoryList') }, [\n makeElement('thead', '', {}, []),\n makeElement('tbody', 'trade-history-body', {}, []),\n ]),\n ]),\n makeElement('div', 'panel-rail', { role: 'toolbar', 'aria-label': host.t('TradeHistoryActions') }, [\n ...(readOnly ? [] : [makeIconButton('bt-icon-btn panel-refresh-btn', host.t('Refresh'), 'bi-arrow-clockwise', {})]),\n makeIconButton('bt-icon-btn panel-export-btn', host.t('ExportToExcel'), 'bi-file-earmark-spreadsheet', {}),\n ]),\n ]),\n ]);\n }\n\n constructor(rootEl: HTMLElement, _state: Record<string, unknown>, deps: TradeHistoryDeps) {\n this._host = assertHost(deps?.host, 'TradeHistoryWidget');\n\n this.rootEl = rootEl;\n this.bodyEl = this.rootEl.querySelector('.trade-history-body');\n this._closeBtn = this.rootEl.querySelector('.panel-close-btn');\n this._refreshBtn = this.rootEl.querySelector('.panel-refresh-btn');\n this._exportBtn = this.rootEl.querySelector('.panel-export-btn');\n\n this._closeBtn?.addEventListener('click', (e) => {\n e.preventDefault();\n this.dispose();\n this._host.close();\n });\n this._refreshBtn?.addEventListener('click', (e) => {\n e.preventDefault();\n void this.refresh();\n });\n\n this._exportBtn?.addEventListener('click', (e) => {\n e.preventDefault();\n this._export();\n });\n\n this._rows = [];\n\n const head = this.rootEl.querySelector('.trade-history-table thead');\n this._grid = head && this.bodyEl\n ? new DataGrid<TradeRow>({\n head: head as HTMLElement,\n body: this.bodyEl,\n columns: this._columns(),\n // Most recent fill on top by default \u2014 the natural way to read a trade blotter.\n defaultSort: { col: 'time', dir: 'desc' },\n rowKey: (t) => String(t.id),\n emptyText: this._host.t('No trade history'),\n contextMenu: makeGridMenu(this._host),\n // Press to mark, drag or Shift to range, Ctrl to toggle, Ctrl+C\n // to copy \u2014 all the grid's own.\n selection: 'multi',\n })\n : null;\n\n this._host.register(this);\n }\n\n dispose(): void {\n // The grid holds document-level listeners (the copy shortcut) that\n // outlive a removed subtree \u2014 it has to be told, not just detached.\n this._grid?.destroy();\n this._host.unregister(this);\n try { this.rootEl.remove(); } catch { /* already detached */ }\n }\n\n /// Reload against whichever portfolio the host says is active right now.\n ///\n /// Two guards, and they are not the same one. No portfolio means there is\n /// nothing to show \u2014 a guest session has none. A portfolio the host will\n /// not `allow` reading means the session cannot make the call at all: this\n /// is somebody's executed trades, so a page that has a portfolio id lying\n /// around but no live credential must not fire the request and collect a\n /// 401 for it. The panel asks the host rather than inspecting a token,\n /// because what counts as a usable session is the host's to know.\n ///\n /// Order matters: the portfolio check runs first, so a session with no\n /// portfolio is simply quiet \u2014 `allow` is the one that may put a sign-in\n /// prompt on screen, and reaching it means the page did have an account to\n /// load.\n async refresh(): Promise<void> {\n const pf = this._host.trading.portfolioId();\n if (!pf || !this._grid) return;\n if (!this._host.allow('load trade history')) return;\n try {\n this._rows = await this._host.trading.api.getExecutions(pf, null, 200) || [];\n this._grid.setRows(this._rows);\n } catch (err) {\n // Through the port, not the console: this is the diagnostic the\n // user cannot see and support has to, and routing it here is also\n // what makes it assertable.\n this._host.log(`TradeHistoryWidget: failed to load trade history: ${err}`);\n }\n }\n\n /// Show these rows. The push path, for a consumer that already holds the\n /// trades - a finished run's report - rather than an account to load them\n /// from. `refresh` is the pull path and the two do not mix: whichever ran\n /// last is what the grid shows.\n update(rows: TradeRow[]): void {\n this._rows = rows || [];\n this._grid?.setRows(this._rows);\n }\n\n // Export the table to .xlsx in the currently rendered (sorted) order.\n _export(): void {\n this._grid?.download('trades', this._host.t('TradeHistory'));\n }\n\n // The blotter's single column declaration. The two id columns read `#42` on\n // screen and export the bare number, so the sheet stays sortable as a number.\n _columns(): GridColumn<TradeRow>[] {\n // `label`, not `t` \u2014 every row lambda below already binds `t` as the\n // trade it is rendering, and a translator sharing that name would be\n // shadowed inside exactly the callbacks that read it.\n const label = (key: string) => this._host.t(key);\n const presentation = this._host.presentation;\n return [\n {\n key: 'id',\n header: label('ID'),\n exportable: true,\n value: (t) => t.id,\n render: (t) => '#' + t.id,\n cellClass: () => 'mono-id',\n },\n {\n key: 'time',\n header: label('Time'),\n exportable: true,\n value: (t) => t.executedAt || t.time,\n render: (t) => presentation.timeText(t.executedAt || t.time!),\n exportValue: (t) => presentation.timeText(t.executedAt || t.time!),\n },\n {\n key: 'symbol',\n header: label('Sym'),\n exportable: true,\n value: (t) => t.instrumentSymbol || t.symbol,\n },\n {\n key: 'side',\n header: label('Side'),\n exportable: true,\n value: (t) => t.side,\n render: (t) => presentation.sideText(t.side!),\n cellClass: (t) => presentation.sideClass(t.side!),\n exportValue: (t) => presentation.sideText(t.side!),\n },\n {\n key: 'quantity',\n header: label('Qty'),\n exportable: true,\n value: (t) => t.quantity,\n render: (t) => formatQty(t.quantity),\n },\n {\n key: 'price',\n header: label('Price'),\n exportable: true,\n value: (t) => t.price,\n render: (t) => formatPrice(t.price),\n },\n {\n key: 'order',\n header: label('Order'),\n exportable: true,\n value: (t) => t.order || t.orderId,\n render: (t) => '#' + (t.order || t.orderId || ''),\n cellClass: () => 'mono-id',\n },\n ];\n }\n}\n", "// Watchlist \u2014 multi-instance.\n//\n// Builds its own DOM (see `_buildRoot`) rather than cloning a <template> out of\n// the page, so it can be constructed by any host that supplies a `TradingHost`.\n// Each instance has its own search/filter/sort state; favourites are shared,\n// held under one key in the host's preference store.\n//\n// The table is `DataGrid` from `@stocksharp/grids`. The grid holds the FILTERED\n// instruments and does the sorting, which is what makes the two caps mean\n// different things: `renderLimit` caps what gets painted while the export and\n// the subscription sync read the whole filtered set. A live quote patches one\n// cell through the grid's (rowKey, columnKey) lookup instead of repainting \u2014 a\n// repaint would restart the flash animation it just triggered.\nimport { makeElement, makeIconButton, makePanelId, makePanelRoot } from './dom.js';\nimport { ControlTypes } from './control-types.js';\nimport { makeGridMenu } from './grid-menu.js';\nimport { MarketDataClient, MarketDataLevels, TradingApi, TradingHost, assertHost } from './trading-host.js';\nimport type { InstrumentRow, QuoteStats } from './trading-data.js';\nimport { DataGrid, GridColumn } from '@stocksharp/grids/source/data-grid';\n\n/// What the panel needs beyond the host port. Picking an instrument is the one\n/// thing it reports that is not the ticker: the panel does not switch the\n/// host's instrument itself, it says what the user chose and the host acts.\nexport interface WatchlistDeps {\n host: TradingHost;\n onSelect(symbol: string): void;\n}\n\nexport class WatchlistWidget {\n static FAVORITES_KEY = 'terminal_watchlist_favorites';\n // Cache-key prefix for the per-session baseline price used to compute the\n // change %. Day-scoped so the baseline resets every UTC day and the\n // percent matches \"since today open\" intuitively. (Without a REST bars\n // endpoint there is no broker-side day-open available, and the watchlist\n // deliberately avoids REST \u2014 the live stream is its only source.) It goes\n // in the host's cache rather than its preferences: one key per symbol per\n // day is scratch, not a setting worth syncing.\n static BASELINE_KEY_PREFIX = 'wlBaseline:';\n static VISIBLE_CAP = 30;\n static RENDER_CAP = 300;\n static TYPE = ControlTypes.Watchlist;\n\n rootEl: HTMLElement;\n api: TradingApi;\n instruments: InstrumentRow[];\n stats: Map<string, QuoteStats>;\n favorites: Set<string>;\n filter: string;\n search: string;\n currentSymbol: string | null;\n wsClient: MarketDataClient;\n bodyEl: HTMLElement | null;\n searchEl: HTMLInputElement | null;\n tabsEl: HTMLElement | null;\n sortHeaderEl: HTMLElement | null;\n // `//` rather than `///` from here down \u2014 see the note in positions-widget.\n _host: TradingHost;\n _deps: WatchlistDeps;\n // Symbols this widget currently holds a live-quote subscription for.\n // Re-synced from the visible list on every render \u2014 see _syncSubs.\n _subscribedSyms: Set<string>;\n _closeBtn: HTMLElement | null;\n _exportBtn: HTMLElement | null;\n _grid: DataGrid<InstrumentRow> | null;\n\n static create(hostEl: HTMLElement, state: Record<string, unknown>, deps: WatchlistDeps): WatchlistWidget {\n // Assert before building: the markup below is localized through the\n // host, so a missing host has to fail here rather than render a panel\n // captioned with raw English keys.\n const host = assertHost(deps?.host, 'WatchlistWidget');\n const root = WatchlistWidget._buildRoot(host);\n root.id = makePanelId(WatchlistWidget.TYPE);\n hostEl.appendChild(root);\n return new WatchlistWidget(root, state || {}, deps);\n }\n\n // The panel's markup. The host stylesheet reads this structure, and a\n // docking host lifts `.panel-header`'s children \u2014 and, for this panel,\n // `.watchlist-search-row` as well \u2014 into its tab strip, which is why the\n // search row is a sibling of the header rather than a child of it.\n //\n // \"All\" and \"Favorites\" are the built-in tabs. The rest are appended at\n // runtime from the distinct categories of the loaded instruments, so the\n // tab set is configured through whatever assigns those categories rather\n // than being spelled out here \u2014 see _renderCategoryTabs.\n static _buildRoot(host: TradingHost): HTMLElement {\n const search = host.t('SearchInstruments');\n const favorites = host.t('Favorites');\n return makePanelRoot('watchlist-panel', host.t('Watchlist'), [\n makeElement('div', 'panel-header', {}, [\n makeElement('span', '', {}, [host.t('Markets')]),\n makeIconButton('bt-icon-btn panel-export-btn', host.t('ExportToExcel'), 'bi-file-earmark-spreadsheet', { type: 'button' }),\n makeIconButton('bt-icon-btn bt-icon-cancel panel-close-btn', host.t('ClosePanel'), 'bi-x', { type: 'button' }),\n ]),\n makeElement('div', 'watchlist-search-row', {}, [\n makeElement('input', 'form-control form-control-sm watchlist-search-input watchlist-search',\n { type: 'text', placeholder: search, autocomplete: 'off', 'aria-label': search }, []),\n ]),\n makeElement('div', 'watchlist-tabs', { role: 'tablist', 'aria-label': host.t('WatchlistFilter') }, [\n makeElement('button', 'wl-tab active', { 'data-filter': 'all', role: 'tab', 'aria-selected': 'true' }, [host.t('All')]),\n makeElement('button', 'wl-tab', { 'data-filter': 'favorites', role: 'tab', 'aria-selected': 'false', 'aria-label': favorites, title: favorites }, ['\u2605']),\n ]),\n makeElement('div', 'watchlist-pane', {}, [\n makeElement('table', 'watchlist-table', {}, [\n makeElement('thead', 'watchlist-headers', {}, []),\n makeElement('tbody', 'watchlist-body', {}, []),\n ]),\n ]),\n ]);\n }\n\n constructor(rootEl: HTMLElement, _state: Record<string, unknown>, deps: WatchlistDeps) {\n this._host = assertHost(deps?.host, 'WatchlistWidget');\n if (typeof deps?.onSelect !== 'function')\n throw new Error('WatchlistWidget: dep \"onSelect\" is required');\n\n this.rootEl = rootEl;\n this._deps = deps;\n this.api = this._host.trading.api;\n\n this.instruments = [];\n this.stats = new Map();\n this.favorites = new Set(this._loadFavorites());\n this.filter = 'all';\n this.search = '';\n this.currentSymbol = null;\n this._subscribedSyms = new Set();\n this.wsClient = this._host.trading.marketData;\n\n this.bodyEl = this.rootEl.querySelector('.watchlist-body');\n this.searchEl = this.rootEl.querySelector('.watchlist-search');\n this.tabsEl = this.rootEl.querySelector('.watchlist-tabs');\n this.sortHeaderEl = this.rootEl.querySelector('.watchlist-headers');\n this._closeBtn = this.rootEl.querySelector('.panel-close-btn');\n this._exportBtn = this.rootEl.querySelector('.panel-export-btn');\n\n this._closeBtn?.addEventListener('click', (e) => {\n e.preventDefault();\n this.dispose();\n this._host.close();\n });\n this._exportBtn?.addEventListener('click', (e) => {\n e.preventDefault();\n this._export();\n });\n\n this._grid = this.sortHeaderEl && this.bodyEl\n ? new DataGrid<InstrumentRow>({\n head: this.sortHeaderEl,\n body: this.bodyEl,\n columns: this._columns(),\n // Symbol A\u2192Z at rest \u2014 an instrument list has no natural \"newest\",\n // and alphabetical is what the user scans by.\n defaultSort: { col: 'symbol', dir: 'asc' },\n rowKey: (i) => String(i.symbol),\n emptyText: this._host.t('No instruments'),\n rowClass: (i) => (i.symbol === this.currentSymbol ? 'wl-row wl-current' : 'wl-row'),\n bindRow: (tr, i) => tr.addEventListener('click', () => this._deps.onSelect(i.symbol!)),\n // Paint a screenful, export and subscribe over the whole filtered set.\n renderLimit: WatchlistWidget.RENDER_CAP,\n // A header click re-renders through the grid, so this is the only\n // place that sees every change to the visible set \u2014 including a sort\n // the widget was never told about.\n afterRender: () => {\n this._syncSubs();\n this._publishVisible();\n },\n contextMenu: makeGridMenu(this._host),\n })\n : null;\n\n this._bind();\n\n this._host.register(this);\n\n // Auto-init data load: a caller that creates the panel expects a working\n // widget without further ceremony. Unconditional \u2014 the host port\n // guarantees an API client, so there is no \"maybe we can load\" state\n // left to branch on.\n void this.init();\n }\n\n dispose(): void {\n // Release every live subscription this widget held \u2014 the market-data\n // client refcounts them, so another control's subscription survives if\n // it happened to share a symbol with us.\n for (const sym of this._subscribedSyms) {\n try { void this.wsClient?.removeSymbol(sym); } catch { /* socket may be torn down */ }\n }\n this._subscribedSyms.clear();\n this._host.unregister(this);\n try { this.rootEl.remove(); } catch { /* already detached */ }\n }\n\n async init(): Promise<void> {\n try {\n this.instruments = await this.api.searchInstruments('');\n } catch (err) {\n // Through the port, not the console: this is the diagnostic the\n // user cannot see and support has to, and routing it here is also\n // what makes it assertable.\n this._host.log(`WatchlistWidget: failed to load instruments: ${err}`);\n this.instruments = [];\n }\n this._renderCategoryTabs();\n this._render();\n }\n\n // Build the filter tabs from the assigned instrument categories. \"All\" and\n // \"Favorites\" are built into the markup; here we append one tab per distinct\n // non-empty category found in the loaded instruments. Re-runnable: clears\n // any category tabs a previous load added.\n _renderCategoryTabs(): void {\n if (!this.tabsEl) return;\n this.tabsEl.querySelectorAll('.wl-tab[data-category]').forEach(el => el.remove());\n\n const cats = Array.from(new Set(\n this.instruments\n .map(i => (i.category || '').trim())\n .filter(c => c.length > 0)))\n .sort((a, b) => a.localeCompare(b));\n\n for (const cat of cats) {\n const btn = document.createElement('button');\n btn.className = 'wl-tab';\n btn.setAttribute('role', 'tab');\n btn.setAttribute('aria-selected', 'false');\n btn.dataset.filter = cat;\n btn.dataset.category = '1';\n btn.textContent = cat;\n this.tabsEl.appendChild(btn);\n }\n }\n\n /// Highlight the instrument the rest of the page is showing. Patches the two\n /// affected rows rather than repainting: a repaint here would restart any\n /// flash animation currently running in a price cell.\n setCurrentSymbol(symbol: string | null): void {\n const previous = this.currentSymbol;\n this.currentSymbol = symbol;\n if (!this._grid) return;\n if (previous) this._grid.rowElement(String(previous))?.classList.remove('wl-current');\n if (symbol) this._grid.rowElement(String(symbol))?.classList.add('wl-current');\n }\n\n onPriceUpdate(symbol: string, price: number): void {\n if (price == null || !isFinite(price)) return;\n let full = symbol;\n if (!full.includes('@')) {\n const match = this.instruments.find(i => String(i.symbol).split('@')[0] === symbol);\n if (match?.symbol) full = match.symbol;\n }\n const cur = this.stats.get(full);\n const prev = cur?.lastPrice;\n\n // Session-day baseline: first observed live price per (UTC-day, symbol).\n // Kept in the host's cache so reloading the page doesn't jump the\n // baseline mid-day. New UTC day = fresh baseline.\n const baselineKey = WatchlistWidget._baselineKey(full);\n let baseline = cur?.baseline;\n if (baseline == null) {\n const stored = this._host.cache.get(baselineKey, null);\n baseline = stored ? Number(stored) : undefined;\n }\n if (baseline == null || !isFinite(baseline) || baseline === 0) {\n baseline = price;\n this._host.cache.set(baselineKey, String(price));\n }\n const chgPct = baseline ? ((price - baseline) / baseline) * 100 : 0;\n\n this.stats.set(full, { ...(cur || {}), lastPrice: price, baseline, chgPct });\n this._updateRow(full, prev);\n }\n\n static _baselineKey(symbol: string): string {\n const day = new Date().toISOString().slice(0, 10); // YYYY-MM-DD (UTC)\n return WatchlistWidget.BASELINE_KEY_PREFIX + day + ':' + symbol;\n }\n\n _bind(): void {\n if (this.searchEl) {\n this.searchEl.addEventListener('input', () => {\n this.search = (this.searchEl!.value || '').trim().toLowerCase();\n this._render();\n });\n }\n if (this.tabsEl) {\n this.tabsEl.addEventListener('click', (e) => {\n const tgt = e.target as Element | null;\n const btn = tgt?.closest('.wl-tab') as HTMLElement | null;\n if (!btn) return;\n this.filter = btn.dataset.filter || 'all';\n this.tabsEl!.querySelectorAll('.wl-tab').forEach(b => b.classList.toggle('active', b === btn));\n this._render();\n });\n }\n // Sort header clicks are handled by the grid (wired in the constructor).\n }\n\n // The instruments matching the search box and the active tab, unsorted \u2014\n // the grid owns the order.\n _filtered(): InstrumentRow[] {\n const q = this.search;\n const rows: InstrumentRow[] = [];\n for (const inst of this.instruments) {\n const sym = (inst.symbol || '').toUpperCase();\n // Match the \"symbol/exchange\" (and \"symbol@exchange\") display form too, so a\n // full \"BTC/IMEX\" query resolves \u2014 not only the bare symbol \"BTC\".\n const symL = sym.toLowerCase();\n const shortSym = symL.split('@')[0];\n const exch = (inst.exchange || '').toLowerCase();\n const qualified = `${shortSym}/${exch}`;\n const qualifiedAt = `${shortSym}@${exch}`;\n if (q && !symL.includes(q) && !(inst.name || '').toLowerCase().includes(q) && !qualified.includes(q) && !qualifiedAt.includes(q)) continue;\n if (this.filter === 'favorites') {\n if (!this.favorites.has(sym)) continue;\n } else if (this.filter !== 'all') {\n // Any other filter value is an assigned instrument category,\n // matched case-insensitively.\n if ((inst.category || '').toLowerCase() !== this.filter.toLowerCase()) continue;\n }\n rows.push(inst);\n }\n return rows;\n }\n\n _render(): void {\n this._grid?.setRows(this._filtered());\n }\n\n // The blotter's single column declaration. Prices and percentages come off\n // the live stats map rather than the instrument row, so a quote arriving\n // between two renders is picked up by the next one.\n _columns(): GridColumn<InstrumentRow>[] {\n const label = (key: string) => this._host.t(key);\n return [\n {\n key: 'symbol',\n header: label('Symbol'),\n exportable: true,\n value: (i) => i.symbol,\n cellClass: () => 'wl-sym-cell',\n render: (i) => this._symbolCell(i),\n exportValue: (i) => String(i.symbol).split('@')[0],\n },\n {\n key: 'last',\n header: label('Last'),\n headerClass: 'ta-right',\n exportable: true,\n value: (i) => this._statsOf(i).lastPrice,\n cellClass: () => 'wl-last',\n render: (i) => WatchlistWidget._priceText(this._statsOf(i).lastPrice),\n exportValue: (i) => this._statsOf(i).lastPrice ?? '',\n },\n {\n key: 'chgPct',\n header: label('Change24hPct'),\n headerClass: 'ta-right',\n exportable: true,\n value: (i) => this._statsOf(i).chgPct,\n cellClass: (i) => ('wl-chg ' + WatchlistWidget._changeClass(this._statsOf(i).chgPct)).trim(),\n render: (i) => WatchlistWidget._changeText(this._statsOf(i).chgPct),\n exportValue: (i) => this._statsOf(i).chgPct ?? '',\n },\n ];\n }\n\n _statsOf(instrument: InstrumentRow): QuoteStats {\n return this.stats.get(String(instrument.symbol)) ?? {};\n }\n\n // The favourite star plus the short symbol. The star stops the click from\n // reaching the row, whose own listener switches the host's instrument.\n _symbolCell(instrument: InstrumentRow): Node {\n const cell = document.createDocumentFragment();\n\n const star = document.createElement('button');\n star.type = 'button';\n star.className = this.favorites.has(instrument.symbol!) ? 'wl-fav active' : 'wl-fav';\n star.title = this._host.t('Favorite');\n star.textContent = '\u2605';\n star.addEventListener('click', (e) => {\n e.stopPropagation();\n this._toggleFavorite(instrument.symbol!);\n });\n cell.appendChild(star);\n\n const label = document.createElement('span');\n label.className = 'wl-sym';\n label.textContent = String(instrument.symbol).split('@')[0];\n cell.appendChild(label);\n\n return cell;\n }\n\n // Export the current filtered+sorted view to .xlsx \u2014 every matching\n // instrument, without the RENDER_CAP truncation applied to the DOM.\n _export(): void {\n this._grid?.download('watchlist', this._host.t('Markets'));\n }\n\n // Patch the price and change cells of one row in place. A full render would\n // replace the cell element and so cancel the flash animation started here.\n _updateRow(symbol: string, prevPrice: number | null | undefined): void {\n if (!this._grid) return;\n const st = this.stats.get(symbol) || {};\n const lastEl = this._grid.cellElement(String(symbol), 'last');\n const chgEl = this._grid.cellElement(String(symbol), 'chgPct');\n if (lastEl && st.lastPrice != null) {\n lastEl.textContent = WatchlistWidget._priceText(st.lastPrice);\n if (prevPrice != null && st.lastPrice !== prevPrice) {\n lastEl.classList.remove('flash-up', 'flash-down');\n void lastEl.offsetWidth;\n lastEl.classList.add(st.lastPrice > prevPrice ? 'flash-up' : 'flash-down');\n }\n }\n if (chgEl && st.chgPct != null && isFinite(Number(st.chgPct))) {\n chgEl.textContent = WatchlistWidget._changeText(st.chgPct);\n chgEl.classList.remove('up', 'down');\n chgEl.classList.add(WatchlistWidget._changeClass(st.chgPct));\n }\n }\n\n _toggleFavorite(symbol: string): void {\n if (!this._host.allow('add to favorites')) return;\n if (this.favorites.has(symbol)) this.favorites.delete(symbol);\n else this.favorites.add(symbol);\n this._saveFavorites();\n // Broadcast the change to every other watchlist instance so they reflect\n // the new state without a fresh fetch.\n this._host.broadcast<WatchlistWidget>(w => {\n if (w !== this) { w.favorites = new Set(this.favorites); w._render(); }\n });\n this._render();\n }\n\n _loadFavorites(): string[] {\n try {\n const raw = this._host.preferences.get(WatchlistWidget.FAVORITES_KEY, null);\n return raw ? JSON.parse(raw) : [];\n } catch { return []; }\n }\n\n _saveFavorites(): void {\n try { this._host.preferences.set(WatchlistWidget.FAVORITES_KEY, JSON.stringify(Array.from(this.favorites))); }\n catch { /* store may be unavailable */ }\n }\n\n // The symbols at the top of the current order \u2014 the ones a user is actually\n // looking at, and the set both the subscriptions and the host's ticker are\n // sized to.\n _visible(): InstrumentRow[] {\n return this._grid ? this._grid.sortedRows().slice(0, WatchlistWidget.VISIBLE_CAP) : [];\n }\n\n // Diff the desired set of visible symbols against currently-held live-price\n // subscriptions, then add/remove to converge. Called after every render that\n // may have changed the visible list (filter, sort, search). add/removeSymbol\n // refcount internally, so another control's subscription on the same symbol\n // is not affected.\n _syncSubs(): void {\n if (!this.wsClient) return;\n const desired = new Set(this._visible().map(i => String(i.symbol).split('@')[0]));\n\n for (const sym of this._subscribedSyms) {\n if (!desired.has(sym)) {\n try { void this.wsClient.removeSymbol(sym); } catch { /* socket race */ }\n this._subscribedSyms.delete(sym);\n }\n }\n // Stagger the subscribe burst \u2014 firing every watchlist symbol\n // simultaneously while a chart is fetching its history starves the\n // adapter and stalls the chart's snapshot. 80ms between calls keeps the\n // pipe quiet enough for that fetch to return on time, while still\n // feeling instant to the user.\n let i = 0;\n for (const sym of desired) {\n if (this._subscribedSyms.has(sym)) continue;\n this._subscribedSyms.add(sym);\n setTimeout(() => {\n try { void this.wsClient.addSymbol(sym, MarketDataLevels.Quotes); } catch { /* socket race */ }\n }, i * 80);\n i++;\n }\n }\n\n // Tell the host which symbols are on screen. Only the primary instance\n // speaks for the page: duplicated watchlist panels have their own filters,\n // and the page has one ticker for them to fight over.\n _publishVisible(): void {\n if (!this._host.isPrimary) return;\n this._host.ticker.publish(this._visible().map(i => String(i.symbol)), this.stats);\n }\n\n static _priceText(price: number | null | undefined): string {\n const n = Number(price);\n if (price == null || !isFinite(n) || n === 0) return '--';\n if (n >= 1) return n.toFixed(2);\n if (n >= 0.01) return n.toFixed(4);\n return n.toFixed(6);\n }\n\n static _changeText(chgPct: number | null | undefined): string {\n const pct = Number(chgPct);\n if (chgPct == null || !isFinite(pct)) return '--';\n return `${pct >= 0 ? '+' : ''}${pct.toFixed(2)}%`;\n }\n\n static _changeClass(chgPct: number | null | undefined): string {\n const pct = Number(chgPct);\n if (chgPct == null || !isFinite(pct)) return '';\n return pct >= 0 ? 'up' : 'down';\n }\n}\n", "// Order entry \u2014 a split Buy/Sell pad sharing one order-type selector.\n//\n// Builds its own DOM (see `_buildRoot`) rather than cloning a <template> out of\n// the page it happens to be rendered on, and every element it reads is queried\n// under `this.rootEl`, so a host may place more than one of these on a page.\n//\n// It fetches nothing. Reference prices arrive as the host sees them\n// (`setLimitPrice`, `setBbo`), the size and price grid the venue will accept\n// arrives as an `InstrumentSpec` (`setInstrument`), and the balance figures the\n// percent buttons divide arrive through `setAvailable` / `setMaxQuantity`.\n//\n// Placing the order is deliberately not this control's to do: `TradingApi` is\n// read-only, and everything that has to happen before the wire \u2014 the sign-in\n// gate, the connection check, which portfolio the order belongs to \u2014 is the\n// host's. So the pad validates, collects the form and hands both to the\n// `submitOrder` dep.\n//\n// It renders no table, so it is the one control here that does not use\n// `DataGrid`.\nimport { formatPrice, formatQty } from './formatters.js';\nimport { makeElement, makeIconButton, makePanelId, makePanelRoot } from './dom.js';\nimport { ControlTypes } from './control-types.js';\nimport { TradingHost, assertHost } from './trading-host.js';\nimport type { InstrumentSpec } from './trading-data.js';\n\n/// The pad's two columns. Every method that addresses one takes this rather\n/// than a wire side, because a column is the control's own vocabulary: which of\n/// the two forms is being filled in, not what the resulting order will say.\nexport const OrderEntrySides = {\n Buy: 'buy',\n Sell: 'sell',\n} as const;\n\nexport type OrderEntrySide = typeof OrderEntrySides[keyof typeof OrderEntrySides];\n\n/// The order kinds the pad offers. The values are what `data-type` carries on\n/// the tab buttons and what `toApiType` maps onto StockSharp's `OrderTypes`.\nexport const OrderEntryTypes = {\n Market: 'market',\n Limit: 'limit',\n Stop: 'stop',\n StopLimit: 'stoplimit',\n} as const;\n\nexport type OrderEntryType = typeof OrderEntryTypes[keyof typeof OrderEntryTypes];\n\n/// One column of the form, read off its inputs at the moment it is asked for.\n/// A price the chosen type does not use comes back null, so a host can forward\n/// the object without deciding which fields the type made meaningful.\nexport interface OrderEntryValues {\n type: OrderEntryType;\n quantity: number;\n limitPrice: number | null;\n stopPrice: number | null;\n /// Both null while the take-profit / stop-loss block is switched off.\n takeProfit: number | null;\n stopLoss: number | null;\n}\n\n/// Everything the pad needs beyond the host port. `submitOrder` is required: a\n/// form that cannot send is not an order pad, and the port cannot carry it \u2014\n/// `TradingApi` is read-only by design and the send is host policy.\nexport interface OrderEntryDeps {\n host: TradingHost;\n submitOrder(side: OrderEntrySide, values: OrderEntryValues): void;\n}\n\n// StockSharp `Sides`, the spelling `TradingPresentation.sideText` reads. The\n// pad words neither button itself \u2014 the host owns the trading vocabulary.\nconst WIRE_SIDES: Record<OrderEntrySide, number> = { buy: 0, sell: 1 };\n\n// The percentage-of-maximum buttons under the quantity field.\nconst PERCENT_STEPS = [25, 50, 75, 100];\n\n// Where each value lives inside a column. Structure, not looks: the inputs are\n// reached through the field they belong to, so the markup carries one class per\n// field rather than one per input.\nconst PRICE_INPUT = '.oe-field-price input';\nconst STOP_INPUT = '.oe-field-stop input';\nconst QTY_INPUT = '.oe-field-qty input';\nconst TP_INPUT = '.oe-field-tp input';\nconst SL_INPUT = '.oe-field-sl input';\nconst TPSL_CHECK = '.oe-tpsl-toggle input';\nconst AVAILABLE_VALUE = '.oe-avbl .oe-value';\nconst TOTAL_VALUE = '.oe-total .oe-value';\nconst MAX_VALUE = '.oe-foot .oe-value';\n\n// The value fields, in the order a column shows them.\nconst VALUE_INPUTS = [PRICE_INPUT, STOP_INPUT, QTY_INPUT, TP_INPUT, SL_INPUT];\n\nexport class OrderEntryWidget {\n static TYPE = ControlTypes.OrderEntry;\n static SIDES: readonly OrderEntrySide[] = [OrderEntrySides.Buy, OrderEntrySides.Sell];\n\n rootEl: HTMLElement;\n el: HTMLElement | null;\n // Comment style is `//` on purpose below the public members: the declaration\n // emitter keeps a private member's doc comment while dropping its body, so a\n // `///` here would reattach to the next public member in the API snapshot.\n _host: TradingHost;\n _deps: OrderEntryDeps;\n _closeBtn: HTMLElement | null;\n _cols: Record<OrderEntrySide, HTMLElement | null>;\n _orderType: OrderEntryType;\n _instrument: InstrumentSpec | null;\n _lastPrice: number;\n _bestBid: number;\n _bestAsk: number;\n _maxQuantity: Record<OrderEntrySide, number | null>;\n // Which input the user is in. Tracked here rather than read off\n // `document.activeElement`, which is a document-wide lookup a control scoped\n // to its own root has no business making.\n _focused: HTMLElement | null;\n _enabled: boolean;\n\n static create(hostEl: HTMLElement, state: Record<string, unknown>, deps: OrderEntryDeps): OrderEntryWidget {\n // Assert before building: the markup below is localized through the\n // host, so a missing host has to fail here rather than render a pad\n // captioned with raw English keys.\n const host = assertHost(deps?.host, 'OrderEntryWidget');\n const root = OrderEntryWidget._buildRoot(host);\n root.id = makePanelId(OrderEntryWidget.TYPE);\n hostEl.appendChild(root);\n return new OrderEntryWidget(root, state || {}, deps);\n }\n\n // The pad's markup. The host stylesheet reads this structure, and a docking\n // host lifts `.panel-header`'s children into its tab strip.\n //\n // There is no `+` button in the header: a second pad on the same portfolio\n // is a host decision about the account, not a gesture the form offers.\n static _buildRoot(host: TradingHost): HTMLElement {\n const title = host.t('OrderEntry');\n return makePanelRoot('order-entry-panel', title, [\n makeElement('div', 'panel-header', {}, [\n makeElement('span', '', {}, [title]),\n makeIconButton('bt-icon-btn bt-icon-cancel panel-close-btn', host.t('ClosePanel'), 'bi-x', { type: 'button' }),\n ]),\n makeElement('div', 'order-entry-content', {}, [\n OrderEntryWidget._buildTypeTabs(host),\n makeElement('div', 'order-entry-split', {}, [\n OrderEntryWidget._buildColumn(host, OrderEntrySides.Buy),\n OrderEntryWidget._buildColumn(host, OrderEntrySides.Sell),\n ]),\n ]),\n ]);\n }\n\n // Two pairs of type buttons, so each pair lines up with the column beneath\n // it. Which one is active is decided by `setOrderType`, from the constructor\n // and from a later click alike.\n static _buildTypeTabs(host: TradingHost): HTMLElement {\n const tab = (type: OrderEntryType, caption: string) =>\n makeElement('button', 'btn-ot', { type: 'button', 'data-type': type, 'aria-pressed': 'false' }, [caption]);\n\n return makeElement('div', 'order-type-tabs', { role: 'group', 'aria-label': host.t('OrderType') }, [\n makeElement('div', 'ot-pair', {}, [\n tab(OrderEntryTypes.Market, host.t('Market')),\n tab(OrderEntryTypes.Limit, host.t('Limit')),\n ]),\n makeElement('div', 'ot-pair', {}, [\n tab(OrderEntryTypes.Stop, host.t('Stop')),\n tab(OrderEntryTypes.StopLimit, host.t('StopLimit')),\n ]),\n ]);\n }\n\n // One column. The modifier class and the submit button's colour class are\n // assigned as whole literals rather than composed, so the style contract can\n // read this control's class list out of the source.\n static _buildColumn(host: TradingHost, side: OrderEntrySide): HTMLElement {\n const isBuy = side === OrderEntrySides.Buy;\n const sideText = host.presentation.sideText(WIRE_SIDES[side]);\n const maxLabel = isBuy ? host.t('Max Buy') : host.t('Max Sell');\n // The shortcut is the host's, so its letter is a translated string like\n // any other caption rather than a key typed in here.\n const hotkey = isBuy ? host.t('BuyHotkey') : host.t('SellHotkey');\n\n const submit = makeElement('button', 'btn-oe-submit', { type: 'button', 'aria-label': sideText }, [\n sideText,\n makeElement('span', 'hotkey-hint', {}, [`[${hotkey}]`]),\n ]);\n if (isBuy) submit.className = 'btn-oe-submit btn-buy';\n else submit.className = 'btn-oe-submit btn-sell';\n\n const column = makeElement('div', 'oe-col', { 'data-side': side }, [\n makeElement('div', 'oe-avbl', {}, [\n makeElement('span', 'oe-label', {}, [host.t('Avbl')]),\n makeElement('span', 'oe-value', {}, ['--']),\n ]),\n makeElement('div', 'oe-field oe-field-price', {}, [\n makeElement('span', 'oe-label', {}, [host.t('Price')]),\n makeElement('div', 'oe-input-grp', {}, [\n makeElement('input', 'oe-input', { type: 'number', 'aria-label': host.t('Price') }, []),\n makeElement('button', 'btn-bbo', { type: 'button', title: host.t('BestBidOrOffer') }, [host.t('BBO')]),\n ]),\n ]),\n makeElement('div', 'oe-field oe-field-stop', {}, [\n makeElement('span', 'oe-label', {}, [host.t('StopPrice')]),\n makeElement('input', 'oe-input', { type: 'number', 'aria-label': host.t('StopPrice') }, []),\n ]),\n makeElement('div', 'oe-field oe-field-qty', {}, [\n makeElement('span', 'oe-label', {}, [host.t('Amount')]),\n makeElement('input', 'oe-input', { type: 'number', 'aria-label': host.t('OrderQuantity') }, []),\n ]),\n makeElement('div', 'oe-pct-row', { role: 'group', 'aria-label': host.t('PercentageOfBalance') },\n PERCENT_STEPS.map(pct => makeElement('button', 'btn-oe-pct', { type: 'button', 'data-pct': String(pct) }, [`${pct}%`]))),\n makeElement('div', 'oe-total', {}, [\n makeElement('span', 'oe-label', {}, [host.t('Total')]),\n makeElement('span', 'oe-value', {}, ['--']),\n ]),\n makeElement('label', 'oe-tpsl-toggle', {}, [\n makeElement('input', '', { type: 'checkbox' }, []),\n makeElement('span', '', {}, [host.t('TpSl')]),\n ]),\n makeElement('div', 'oe-tpsl oe-field-hidden', {}, [\n makeElement('div', 'oe-field oe-field-tp', {}, [\n makeElement('span', 'oe-label', {}, [host.t('TakeProfit')]),\n makeElement('input', 'oe-input', { type: 'number', 'aria-label': host.t('TakeProfit') }, []),\n ]),\n makeElement('div', 'oe-field oe-field-sl', {}, [\n makeElement('span', 'oe-label', {}, [host.t('StopLoss')]),\n makeElement('input', 'oe-input', { type: 'number', 'aria-label': host.t('StopLoss') }, []),\n ]),\n ]),\n makeElement('div', 'oe-foot', {}, [\n makeElement('span', 'oe-label', {}, [maxLabel]),\n makeElement('span', 'oe-value', {}, ['--']),\n ]),\n submit,\n makeElement('div', 'oe-estimate', { 'aria-live': 'polite' }, []),\n ]);\n if (isBuy) column.className = 'oe-col oe-col-buy';\n else column.className = 'oe-col oe-col-sell';\n return column;\n }\n\n constructor(rootEl: HTMLElement, _state: Record<string, unknown>, deps: OrderEntryDeps) {\n this._host = assertHost(deps?.host, 'OrderEntryWidget');\n for (const name of ['submitOrder'] as const) {\n if (typeof deps?.[name] !== 'function')\n throw new Error(`OrderEntryWidget: dep \"${name}\" is required`);\n }\n\n this.rootEl = rootEl;\n this._deps = deps;\n this.el = this.rootEl.querySelector('.order-entry-content');\n this._closeBtn = this.rootEl.querySelector('.panel-close-btn');\n this._cols = {\n buy: this.rootEl.querySelector('.oe-col-buy'),\n sell: this.rootEl.querySelector('.oe-col-sell'),\n };\n this._orderType = OrderEntryTypes.Market;\n this._instrument = null;\n this._lastPrice = 0;\n this._bestBid = 0;\n this._bestAsk = 0;\n this._maxQuantity = { buy: null, sell: null };\n this._focused = null;\n this._enabled = true;\n\n this._closeBtn?.addEventListener('click', (e) => {\n e.preventDefault();\n this._host.close();\n });\n\n this._bindTypeTabs();\n for (const side of OrderEntryWidget.SIDES) this._bindColumn(side);\n this.setOrderType(OrderEntryTypes.Market);\n\n this._host.register(this);\n }\n\n dispose(): void {\n this._host.unregister(this);\n try { this.rootEl.remove(); } catch { /* already detached */ }\n }\n\n /// The type the form is on. A host reads it before deciding whether a\n /// click-to-trade gesture has to switch the pad over first.\n get orderType(): OrderEntryType {\n return this._orderType;\n }\n\n /// The instrument the form is sized and priced against, or null before the\n /// host has named one. Read for its tick by a host that has to quantize a\n /// price of its own before handing it over.\n getInstrument(): InstrumentSpec | null {\n return this._instrument;\n }\n\n /// Switch the form's order type and mark the matching tab. The tab lives\n /// under this control's own root, so a host switches type by naming the\n /// type rather than by handing over a button it looked up itself.\n setOrderType(type: OrderEntryType): void {\n this._orderType = type;\n for (const button of this.rootEl.querySelectorAll('.btn-ot')) {\n const tab = button as HTMLElement;\n const on = tab.dataset.type === type;\n tab.classList.toggle('active', on);\n tab.setAttribute('aria-pressed', String(on));\n }\n this._applyTypeVisibility();\n for (const side of OrderEntryWidget.SIDES) this._recalculate(side);\n }\n\n /// Adopt the venue's size and price grid: the quantity input steps by the\n /// lot, the price inputs by the tick, and the quantity resets to the minimum\n /// lot \u2014 carrying the previous value across a switch yields nonsense like\n /// \"10 BTC\" after a stock.\n setInstrument(instrument: InstrumentSpec | null): void {\n this._instrument = instrument;\n if (!instrument) return;\n\n const lotSize = Number(instrument.lotSize) || 1;\n const tickSize = Number(instrument.tickSize) || 0.01;\n const minVolume = Number(instrument.minVolume) > 0 ? Number(instrument.minVolume) : lotSize;\n const maxVolume = instrument.maxVolume != null ? Number(instrument.maxVolume) : null;\n\n for (const side of OrderEntryWidget.SIDES) {\n const qty = this._input(side, QTY_INPUT);\n if (qty) {\n qty.step = String(lotSize);\n qty.min = String(minVolume);\n // The empty string is how an input says \"no upper bound\".\n qty.max = maxVolume != null ? String(maxVolume) : '';\n qty.value = OrderEntryWidget._formatQty(minVolume);\n }\n for (const selector of [PRICE_INPUT, STOP_INPUT, TP_INPUT, SL_INPUT]) {\n const input = this._input(side, selector);\n if (input) input.step = String(tickSize);\n }\n }\n\n for (const side of OrderEntryWidget.SIDES) this._recalculate(side);\n }\n\n /// The cash this side may still commit. Null reads as \"not known yet\",\n /// which is not the same as zero.\n setAvailable(side: OrderEntrySide, available: number | null): void {\n const value = this._cols[side]?.querySelector(AVAILABLE_VALUE);\n if (value) value.textContent = formatPrice(available);\n }\n\n /// The largest quantity this side can trade \u2014 what the percent buttons take\n /// a percentage OF. Without it they have no denominator, so they report the\n /// gap through `host.log` rather than inventing one (see `applyPercent`).\n setMaxQuantity(side: OrderEntrySide, max: number | null): void {\n this._maxQuantity[side] = max != null && isFinite(max) ? max : null;\n const value = this._cols[side]?.querySelector(MAX_VALUE);\n if (value) value.textContent = formatQty(this._maxQuantity[side]);\n }\n\n /// A reference price off the tape. Both columns follow the market while the\n /// form is untouched; the field the trader is typing in is left alone.\n setLimitPrice(price: number): void {\n this._lastPrice = price;\n this._writePrices(this._bestBid > 0 ? this._bestBid : price, this._bestAsk > 0 ? this._bestAsk : price, false);\n }\n\n /// The same reference price, force-written: an explicit host gesture\n /// (\"price this order here\") outranks whatever was typed.\n seedLimitPrice(price: number): void {\n this._lastPrice = price;\n this._writePrices(this._bestBid > 0 ? this._bestBid : price, this._bestAsk > 0 ? this._bestAsk : price, true);\n }\n\n /// Best bid and offer. The passive seed for a limit form is Buy = best bid\n /// and Sell = best ask \u2014 joining the book on each side, so the spread in the\n /// form matches the spread in the book. The BBO button next to the field is\n /// the opposite, explicit gesture: take what the other side is showing.\n setBbo(bid: number, ask: number): void {\n if (bid > 0) this._bestBid = bid;\n if (ask > 0) this._bestAsk = ask;\n this._writePrices(\n this._bestBid > 0 ? this._bestBid : this._lastPrice,\n this._bestAsk > 0 ? this._bestAsk : this._lastPrice,\n false);\n }\n\n /// Fill one column's price with the best quote for that direction: Buy takes\n /// the ask, Sell takes the bid.\n applyBbo(side: OrderEntrySide): void {\n const bbo = side === OrderEntrySides.Buy ? this._bestAsk : this._bestBid;\n if (!(bbo > 0)) return;\n this.setPrice(side, bbo);\n }\n\n /// Force one column's price and let ticks resume updating it afterwards \u2014\n /// what a click on a book level or a chart price means. Quantized to the\n /// instrument's tick, so a raw chart coordinate does not fail validation.\n setPrice(side: OrderEntrySide, price: number): void {\n const input = this._input(side, PRICE_INPUT);\n if (!input) return;\n const text = this._formatPriceForInput(price);\n if (!text) return;\n input.dataset.userEdited = '0';\n input.value = text;\n this._recalculate(side);\n }\n\n /// The same quantity in both columns \u2014 a quick-size gesture applies to\n /// whichever side the user goes on to press.\n setQuantity(quantity: number): void {\n for (const side of OrderEntryWidget.SIDES) {\n const input = this._input(side, QTY_INPUT);\n if (!input) continue;\n input.value = OrderEntryWidget._formatQty(quantity);\n this._recalculate(side);\n }\n }\n\n /// Take a percentage of this side's maximum quantity, rounded down to a\n /// whole number of lots.\n applyPercent(side: OrderEntrySide, pct: number): void {\n const max = this._maxQuantity[side];\n if (max == null) {\n this._host.log(`OrderEntryWidget: no maximum quantity for the ${side} side, so ${pct}% of it is unknown`);\n return;\n }\n const qty = this._input(side, QTY_INPUT);\n if (!qty) return;\n\n const step = Number(qty.step) || 0;\n let target = max * (pct / 100);\n if (step > 0) target = Math.floor(target / step) * step;\n qty.value = OrderEntryWidget._formatQty(target);\n this._recalculate(side);\n }\n\n /// Show or hide one column's take-profit / stop-loss block, checkbox\n /// included, so a host can set it without reaching for the input.\n toggleTpSl(side: OrderEntrySide, on: boolean): void {\n const check = this._input(side, TPSL_CHECK);\n if (check) check.checked = on;\n this._show(this._cols[side]?.querySelector('.oe-tpsl') as HTMLElement | null, on);\n }\n\n /// Mark one column's submit button as the one a host-level gesture aimed at.\n /// Null clears both.\n preselect(side: OrderEntrySide | null): void {\n for (const each of OrderEntryWidget.SIDES) {\n const button = this._cols[each]?.querySelector('.btn-oe-submit');\n button?.classList.toggle('btn-preselected', each === side);\n }\n }\n\n /// Turn submitting off while the host cannot send \u2014 a dropped socket, a\n /// portfolio still loading. The form stays readable and editable; only the\n /// two buttons stop.\n setEnabled(enabled: boolean): void {\n this._enabled = enabled;\n this.el?.classList.toggle('oe-offline', !enabled);\n for (const side of OrderEntryWidget.SIDES) this._validateSide(side);\n }\n\n /// The submit button, and whatever host gesture stands in for it. Validates\n /// first, so a form the venue would reject never reaches the account.\n submit(side: OrderEntrySide): void {\n const error = this.validate(side);\n this._validateSide(side);\n if (error || !this._enabled) return;\n this._deps.submitOrder(side, this.getValues(side));\n }\n\n /// What one column currently says.\n getValues(side: OrderEntrySide): OrderEntryValues {\n const type = this._orderType;\n const carriesLimit = type === OrderEntryTypes.Limit || type === OrderEntryTypes.StopLimit;\n const carriesStop = type === OrderEntryTypes.Stop || type === OrderEntryTypes.StopLimit;\n const tpSlOn = this._input(side, TPSL_CHECK)?.checked === true;\n\n return {\n type,\n quantity: this._number(side, QTY_INPUT) ?? 0,\n limitPrice: carriesLimit ? this._number(side, PRICE_INPUT) : null,\n stopPrice: carriesStop ? this._number(side, STOP_INPUT) : null,\n takeProfit: tpSlOn ? this._number(side, TP_INPUT) : null,\n stopLoss: tpSlOn ? this._number(side, SL_INPUT) : null,\n };\n }\n\n /// The reason this column cannot be sent, worded by the host, or null when\n /// it can. Nothing is checked before the instrument is known \u2014 the venue's\n /// grid is what every rule below measures against.\n validate(side: OrderEntrySide): string | null {\n const instrument = this._instrument;\n if (!instrument) return null;\n\n const { quantity, limitPrice, stopPrice, type } = this.getValues(side);\n\n if (quantity <= 0) return this._host.t('Quantity must be positive');\n\n const lotSize = Number(instrument.lotSize) || 0;\n if (lotSize > 0 && !OrderEntryWidget._isMultipleOf(quantity, lotSize))\n return this._host.t('Quantity must be a multiple of {0}', lotSize);\n\n const minVolume = Number(instrument.minVolume) > 0 ? Number(instrument.minVolume) : lotSize;\n if (minVolume > 0 && quantity < minVolume) return this._host.t('Quantity must be >= {0}', minVolume);\n\n if (instrument.maxVolume != null && quantity > Number(instrument.maxVolume))\n return this._host.t('Quantity must be <= {0}', instrument.maxVolume);\n\n const tickSize = Number(instrument.tickSize) || 0;\n\n if (type === OrderEntryTypes.Limit || type === OrderEntryTypes.StopLimit) {\n if (!limitPrice || limitPrice <= 0) return this._host.t('Limit price must be positive');\n if (tickSize > 0 && !OrderEntryWidget._isMultipleOf(limitPrice, tickSize))\n return this._host.t('Limit price must be a multiple of {0}', tickSize);\n }\n\n if (type === OrderEntryTypes.Stop || type === OrderEntryTypes.StopLimit) {\n if (!stopPrice || stopPrice <= 0) return this._host.t('Stop price must be positive');\n if (tickSize > 0 && !OrderEntryWidget._isMultipleOf(stopPrice, tickSize))\n return this._host.t('Stop price must be a multiple of {0}', tickSize);\n }\n\n return null;\n }\n\n /// Map a form type onto StockSharp's `OrderTypes`: Limit=0, Market=1,\n /// Conditional=2. Static because a host needs it to word the order it is\n /// about to send, which it can do before any instance exists.\n static toApiType(uiType: OrderEntryType | string): number {\n switch (uiType) {\n case OrderEntryTypes.Limit: return 0;\n case OrderEntryTypes.Market: return 1;\n case OrderEntryTypes.Stop:\n case OrderEntryTypes.StopLimit: return 2;\n default: return 1;\n }\n }\n\n /// Is `value` a whole number of `step`s? Scaled to integers first: a crypto\n /// lot of 1e-9 against a quantity of 0.001 is not a modulo binary floats can\n /// answer directly.\n static _isMultipleOf(value: number, step: number): boolean {\n if (!(step > 0)) return true;\n if (value === 0) return true;\n const maxDec = Math.max(OrderEntryWidget._decimals(step), OrderEntryWidget._decimals(value));\n const scale = Math.pow(10, maxDec);\n const scaledValue = Math.round(value * scale);\n const scaledStep = Math.round(step * scale);\n if (scaledStep <= 0) return true;\n if (scaledValue === 0) return false;\n return scaledValue % scaledStep === 0;\n }\n\n /// A quantity as an input's `value`: every significant decimal, no thousands\n /// separator and no exponent, because the browser reads this string back as\n /// a number.\n static _formatQty(n: number): string {\n if (!isFinite(n) || n === 0) return '0';\n const d = OrderEntryWidget._decimals(n);\n return n.toFixed(Math.min(d, 20));\n }\n\n /// How many decimals a number is written with, exponent form included.\n static _decimals(n: number): number {\n if (!isFinite(n) || n === 0) return 0;\n const s = Math.abs(n).toString();\n const eIdx = s.indexOf('e-');\n if (eIdx >= 0) {\n const mantissaDec = s.slice(0, eIdx).split('.')[1]?.length ?? 0;\n return parseInt(s.slice(eIdx + 2), 10) + mantissaDec;\n }\n const dot = s.indexOf('.');\n return dot < 0 ? 0 : s.length - dot - 1;\n }\n\n _bindTypeTabs(): void {\n for (const button of this.rootEl.querySelectorAll('.btn-ot')) {\n const tab = button as HTMLElement;\n tab.addEventListener('click', (e) => {\n e.preventDefault();\n const type = tab.dataset.type as OrderEntryType | undefined;\n if (type) this.setOrderType(type);\n });\n }\n }\n\n // Everything one column listens to. Scoped to the column element, which is\n // what lets two pads coexist: an input is found by the field it belongs to\n // under its own root, never by a page-wide id.\n _bindColumn(side: OrderEntrySide): void {\n const column = this._cols[side];\n if (!column) return;\n\n const qty = this._input(side, QTY_INPUT);\n if (qty) {\n qty.value = '1';\n qty.min = '1';\n qty.step = '1';\n }\n\n for (const selector of VALUE_INPUTS) {\n const input = this._input(side, selector);\n if (!input) continue;\n input.addEventListener('input', () => this._recalculate(side));\n input.addEventListener('focus', () => { this._focused = input; });\n input.addEventListener('blur', () => { if (this._focused === input) this._focused = null; });\n // Mark a price the trader typed, so an incoming tick stops\n // overwriting it mid-keystroke.\n if (selector === PRICE_INPUT || selector === STOP_INPUT)\n input.addEventListener('input', () => { input.dataset.userEdited = '1'; });\n }\n\n column.querySelector('.btn-bbo')?.addEventListener('click', (e) => {\n e.preventDefault();\n this.applyBbo(side);\n });\n\n for (const button of column.querySelectorAll('.btn-oe-pct')) {\n const pct = button as HTMLElement;\n pct.addEventListener('click', (e) => {\n e.preventDefault();\n this.applyPercent(side, Number(pct.dataset.pct) || 0);\n });\n }\n\n const check = this._input(side, TPSL_CHECK);\n check?.addEventListener('change', () => this.toggleTpSl(side, check.checked === true));\n\n column.querySelector('.btn-oe-submit')?.addEventListener('click', (e) => {\n e.preventDefault();\n this.submit(side);\n });\n }\n\n // Which price fields the current type makes meaningful. A class toggle, not\n // a display style: the pad emits names and the stylesheet decides.\n _applyTypeVisibility(): void {\n const showLimit = this._orderType === OrderEntryTypes.Limit || this._orderType === OrderEntryTypes.StopLimit;\n const showStop = this._orderType === OrderEntryTypes.Stop || this._orderType === OrderEntryTypes.StopLimit;\n for (const side of OrderEntryWidget.SIDES) {\n this._show(this._cols[side]?.querySelector('.oe-field-price') as HTMLElement | null, showLimit);\n this._show(this._cols[side]?.querySelector('.oe-field-stop') as HTMLElement | null, showStop);\n }\n }\n\n _show(element: HTMLElement | null, visible: boolean): void {\n element?.classList.toggle('oe-field-hidden', !visible);\n }\n\n // Write both price inputs. `reset` is the explicit gesture: it clears the\n // \"typed in\" mark so the field follows the market again. Without it, a field\n // the user is in or has already edited is left alone.\n _writePrices(buyPrice: number, sellPrice: number, reset: boolean): void {\n this._writePrice(OrderEntrySides.Buy, buyPrice, reset);\n this._writePrice(OrderEntrySides.Sell, sellPrice, reset);\n for (const side of OrderEntryWidget.SIDES) this._recalculate(side);\n }\n\n _writePrice(side: OrderEntrySide, price: number, reset: boolean): void {\n const input = this._input(side, PRICE_INPUT);\n if (!input) return;\n const text = this._formatPriceForInput(price);\n if (!text) return;\n if (reset) {\n input.dataset.userEdited = '0';\n input.value = text;\n return;\n }\n if (this._focused === input) return;\n if (input.dataset.userEdited === '1') return;\n input.value = text;\n }\n\n // Round a price to the instrument's tick and cut the float tail: a mid price\n // of (bid+ask)/2 otherwise reaches the field as 0.44625000000000004.\n _formatPriceForInput(price: number): string {\n const n = Number(price);\n if (!isFinite(n)) return '';\n const tickSize = Number(this._instrument?.tickSize) || 0;\n if (tickSize > 0)\n return (Math.round(n / tickSize) * tickSize).toFixed(OrderEntryWidget._decimals(tickSize));\n return String(parseFloat(n.toPrecision(12)));\n }\n\n _recalculate(side: OrderEntrySide): void {\n this._updateEstimate(side);\n this._validateSide(side);\n }\n\n // What the order is worth at the price it would most likely fill at: the\n // limit or stop the form names, else the side-appropriate BBO, else the last\n // print.\n _updateEstimate(side: OrderEntrySide): void {\n const total = this._cols[side]?.querySelector(TOTAL_VALUE);\n if (!total) return;\n\n const quantity = this._number(side, QTY_INPUT) ?? 0;\n let price: number;\n if (this._orderType === OrderEntryTypes.Limit || this._orderType === OrderEntryTypes.StopLimit) {\n price = this._number(side, PRICE_INPUT) ?? 0;\n } else if (this._orderType === OrderEntryTypes.Stop) {\n price = this._number(side, STOP_INPUT) ?? this._lastPrice;\n } else {\n const bbo = side === OrderEntrySides.Buy ? this._bestAsk : this._bestBid;\n price = bbo > 0 ? bbo : this._lastPrice;\n }\n\n total.textContent = quantity > 0 && price > 0\n ? (quantity * price).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })\n : '--';\n }\n\n // The estimate line doubles as the validation message and the submit button\n // follows it. A disabled pad reads as invalid for the same reason: there is\n // nothing the button could do.\n _validateSide(side: OrderEntrySide): void {\n const error = this.validate(side);\n const estimate = this._cols[side]?.querySelector('.oe-estimate');\n const submit = this._cols[side]?.querySelector('.btn-oe-submit') as HTMLButtonElement | null;\n\n if (estimate) {\n estimate.textContent = error || '';\n estimate.classList.toggle('oe-estimate-error', !!error);\n }\n if (submit) submit.disabled = !!error || !this._enabled;\n }\n\n _input(side: OrderEntrySide, selector: string): HTMLInputElement | null {\n return (this._cols[side]?.querySelector(selector) as HTMLInputElement | null) ?? null;\n }\n\n // An input's value as a number, or null when it is blank or unparseable \u2014\n // which is what \"the user has not said\" looks like on a number field.\n _number(side: OrderEntrySide, selector: string): number | null {\n const parsed = parseFloat(this._input(side, selector)?.value ?? '');\n return isFinite(parsed) ? parsed : null;\n }\n}\n", "// The geometry of the order book's depth chart, as pure functions over\n// numbers.\n//\n// The chart is a classic market-depth curve: price runs along X with the mid\n// line in the middle, bids fanning left and asks right, and Y is the cumulative\n// quantity from the best price on that side out to the price under the cursor.\n// It is a step curve because a book is discrete \u2014 a horizontal run between two\n// prices at the same accumulation, then a vertical jump where the next level\n// adds its size.\n//\n// None of that needs a canvas, and keeping it here is what makes it testable:\n// a fake 2D context can only record the calls a control made, so the numbers\n// those calls carry are checked here instead, against arithmetic rather than\n// against pixels.\nimport type { BookLevel } from './trading-data.js';\n\n/// The box one paint fills, in device pixels.\nexport interface DepthGeometry {\n /// X of the mid line, which both sides start from.\n midX: number;\n /// Gap left at the mid line and at the outer edge, so the curve does not\n /// touch either.\n pad: number;\n /// Distance from the mid line to the outer edge \u2014 the width one side has.\n halfWidth: number;\n /// Y of the zero-volume baseline.\n baseY: number;\n /// Height the largest cumulative quantity rises to.\n innerHeight: number;\n}\n\n/// One accumulation point: the pixel a level sits at and the running quantity\n/// up to and including it.\nexport interface DepthPoint {\n x: number;\n cumulative: number;\n}\n\n/// One side of the chart, ready to be stroked.\nexport interface DepthSide {\n points: DepthPoint[];\n /// Cumulative quantity of the whole side \u2014 what the two sides are scaled\n /// against, so the taller one reaches the top and the other keeps its\n /// proportion.\n total: number;\n}\n\n/// Accumulate one side of the book into pixel space.\n///\n/// `direction` is -1 for the side drawn left of the mid line and +1 for the\n/// right. Each side is scaled by its OWN price span, so both fill their half\n/// even when one is quoted far wider than the other \u2014 the chart is about the\n/// shape of the liquidity, not about the two spans being comparable.\n///\n/// Null when there is nothing to draw: no levels, or every level at one price,\n/// which has no span to spread over.\nexport function depthSide(levels: BookLevel[], direction: 1 | -1, geometry: DepthGeometry): DepthSide | null {\n if (levels.length === 0) return null;\n\n const best = levels[0].price;\n const worst = levels[levels.length - 1].price;\n const span = Math.abs(best - worst);\n if (!(span > 0)) return null;\n\n const reach = geometry.halfWidth - geometry.pad;\n const points: DepthPoint[] = [{ x: geometry.midX + direction * geometry.pad, cumulative: 0 }];\n let cumulative = 0;\n for (const level of levels) {\n cumulative += level.quantity;\n const distance = Math.abs(level.price - best);\n points.push({\n x: geometry.midX + direction * (geometry.pad + (distance / span) * reach),\n cumulative,\n });\n }\n return { points, total: cumulative };\n}\n\n/// Turn one side into the step polyline that is both stroked and filled.\n///\n/// Two points per level: the horizontal run arrives at the new price still at\n/// the previous accumulation, then the vertical jump adds the level's size. The\n/// first point sits on the baseline so the filled area closes against it.\n///\n/// `maxTotal` is the scale both sides share \u2014 pass the larger of the two totals\n/// so the halves stay comparable.\nexport function depthPolyline(side: DepthSide, maxTotal: number, geometry: DepthGeometry): [number, number][] {\n const scale = maxTotal > 0 ? maxTotal : 1;\n const yOf = (cumulative: number) => geometry.baseY - (cumulative / scale) * geometry.innerHeight;\n\n const line: [number, number][] = [[side.points[0].x, geometry.baseY]];\n for (let i = 1; i < side.points.length; i++) {\n const previous = side.points[i - 1];\n const current = side.points[i];\n line.push([current.x, yOf(previous.cumulative)]);\n line.push([current.x, yOf(current.cumulative)]);\n }\n return line;\n}\n", "// Order book \u2014 multi-instance.\n//\n// Builds its own DOM (see `_buildRoot`) rather than cloning a <template> out of\n// the page, so it can be constructed by any host that supplies a `TradingHost`.\n// Every instance keeps its own symbol, depth, row layout and side order, and a\n// page may hold as many ladders as it has room for.\n//\n// The ladder is not a table and so not a `DataGrid`: a level is a row of three\n// figures behind a proportional volume bar and a heat tint, both of which are\n// measured per level. The measurements are handed to the stylesheet as custom\n// properties (`--t-ob-bar`, `--t-ob-heat`, `--t-ob-sent`) so the widths and the\n// colours stay in CSS, where every other look in this package lives. The one\n// exception is the depth chart above the ladder: it is drawn on a canvas, which\n// no class name can reach, so its colours come from\n// `host.presentation.canvasPalette()` and its geometry from `orderbook-depth.ts`.\n//\n// Where the data comes from: the host pushes frames in through `applyFrame`,\n// one per server frame, fanned to every live instance \u2014 which is why each\n// instance filters by its own symbol, and why `host.register(this)` at the end\n// of the constructor is what makes a ladder update at all. The first frame of a\n// (re)subscription is a snapshot and replaces the ledger; the rest are diffs\n// (`quantity: 0` deletes a level) carrying a per-symbol sequence. A gap in that\n// sequence means a frame was missed and the diffs after it cannot be trusted,\n// so the ladder asks the market-data client to resend the symbol from scratch\n// rather than drifting. Own resting orders come from\n// `marketData.getOrders()` \u2014 the badge is an overlay on the levels, not a\n// second subscription.\nimport { formatPrice, formatQty } from './formatters.js';\nimport { makeElement, makeIconButton, makePanelId, makePanelRoot } from './dom.js';\nimport { ControlTypes } from './control-types.js';\nimport { MarketDataLevels, TradingHost, assertHost } from './trading-host.js';\nimport type { BookLevel, OrderBookFrame, QuoteLevel } from './trading-data.js';\nimport { depthPolyline, depthSide, type DepthGeometry } from './orderbook-depth.js';\n\n/// What the ladder needs beyond the host port. All required: a click on a price\n/// level is this control's whole reason to exist, and the two measurements are\n/// the page's to answer \u2014 a control that read them off `window` itself could\n/// only ever run in a browser tab.\nexport interface OrderBookDeps {\n host: TradingHost;\n /// Plain click on a level \u2014 prefill an order at this price, do not send it.\n onPriceSelected(price: number, side: number): void;\n /// Ctrl/Cmd click on a level \u2014 send at this price now.\n onPriceExecuted(price: number, side: number): void;\n /// The most levels per side this host has room for, read per depth change.\n /// A phone-sized page answers 5: ten levels a side overflow the panel and\n /// leave the user with two inner scrollbars to dance around. A host with\n /// room answers the largest depth the ladder offers.\n maxDepth(): number;\n /// Backing-store scale for the depth chart's canvas \u2014 `devicePixelRatio` in\n /// a browser. Read per paint, so a window dragged onto a second monitor\n /// repaints sharp.\n pixelRatio(): number;\n}\n\n/// The row layouts the ladder offers.\n///\n/// `stacked` is Price | Qty | Total on every row, the layout every major\n/// exchange ladder uses. `diagonal` centres the price and puts the size cell on\n/// the outside of each side \u2014 the classic two-sided book.\nexport type OrderBookView = 'diagonal' | 'stacked';\n\n// StockSharp's OrderStates.Active. Only a resting order sits on the book, so\n// only a resting order earns the \"your size is here\" badge. The full enum is\n// declared once in `active-orders-widget.ts`; importing it here would pull that\n// blotter \u2014 and the grid it renders with \u2014 into every bundle that wanted a\n// ladder and nothing else.\nconst RESTING_STATE = 3;\n\n// Heat tint per level, as the percentage of the direction colour mixed into the\n// row background. Faint at the shallowest level and still faint at the largest,\n// because the volume bar behind the row is what carries the size \u2014 the tint\n// only separates a wall from its neighbours.\nconst HEAT_FLOOR_PCT = 6;\nconst HEAT_RANGE_PCT = 18;\n\nexport class OrderBookWidget {\n static TYPE = ControlTypes.OrderBook;\n\n /// The depths the ladder offers, in the order the header shows them.\n static DEPTHS: readonly number[] = [5, 10];\n\n /// Persisted depth for the instance that follows the host's active symbol.\n /// Pinned instances carry their depth in their own per-panel state instead,\n /// which is what makes two ladders on one page able to differ.\n static DEPTH_KEY = 'terminal.obDepth';\n\n /// Persisted row layout for the follows-active instance.\n static VIEW_KEY = 'terminal.obView';\n\n /// Persisted \"bids above the mid line\" flag for the follows-active instance.\n static INVERT_KEY = 'terminal.obInvert';\n\n /// Persisted depth-chart visibility for the follows-active instance.\n static DEPTHCHART_KEY = 'terminal.obDepthChart';\n\n rootEl: HTMLElement;\n asksEl: HTMLElement | null;\n bidsEl: HTMLElement | null;\n midEl: HTMLElement | null;\n spreadEl: HTMLElement | null;\n contentEl: HTMLElement | null;\n depthChartEl: HTMLCanvasElement | null;\n // Comment style is `//` on purpose below the public members: the declaration\n // emitter keeps a private member's doc comment while dropping its body, so a\n // `///` here would reattach to the next public member in the API snapshot.\n _host: TradingHost;\n _deps: OrderBookDeps;\n _midSpreadEl: HTMLElement | null;\n _sentimentEl: HTMLElement | null;\n _sentBidPctEl: HTMLElement | null;\n _sentAskPctEl: HTMLElement | null;\n _symbolLabel: HTMLElement | null;\n _depthBtns: HTMLElement[];\n _viewBtns: HTMLElement[];\n _invertBtn: HTMLElement | null;\n _depthToggleBtn: HTMLElement | null;\n _addBtn: HTMLElement | null;\n _closeBtn: HTMLElement | null;\n // Last painted quantity per level, keyed by side and price \u2014 what decides\n // whether a level flashes and in which direction.\n _prevQuantities: Map<string, number>;\n _depth: number;\n _view: OrderBookView;\n _invertSides: boolean;\n _showDepthChart: boolean;\n _bidsByPrice: Map<number, number>;\n _asksByPrice: Map<number, number>;\n _lastSequence: number;\n _currentSymbol: string | null;\n _followsActive: boolean;\n\n static create(hostEl: HTMLElement, state: Record<string, unknown>, deps: OrderBookDeps): OrderBookWidget {\n // Assert before building: the markup below is localized through the\n // host, so a missing host has to fail here rather than render a panel\n // captioned with raw English keys.\n const host = assertHost(deps?.host, 'OrderBookWidget');\n const root = OrderBookWidget._buildRoot(host);\n root.id = makePanelId(OrderBookWidget.TYPE);\n hostEl.appendChild(root);\n return new OrderBookWidget(root, state || {}, deps);\n }\n\n // The panel's markup. The host stylesheet reads this structure, and a\n // docking host lifts `.panel-header`'s children into its tab strip \u2014 which\n // is why the header holds every control the ladder has.\n //\n // The `+` is built here rather than left out: the port documents `spawn` as\n // this control's gesture, and a button nothing renders is a capability\n // nobody can reach.\n static _buildRoot(host: TradingHost): HTMLElement {\n const title = host.t('OrderBook');\n return makePanelRoot('orderbook-panel', title, [\n makeElement('div', 'panel-header', {}, [\n makeElement('span', 'ob-symbol-label',\n { title: host.t('ClickToChangeSymbol'), role: 'button', tabindex: '0' }, ['--']),\n makeElement('div', 'ob-depth-selector', { role: 'group', 'aria-label': host.t('OrderBookDepth') },\n OrderBookWidget.DEPTHS.map(depth => makeElement('button', 'btn-ob-depth', {\n type: 'button',\n 'data-depth': String(depth),\n 'aria-label': host.t('LevelsCount', depth),\n 'aria-pressed': 'false',\n }, [String(depth)]))),\n makeElement('div', 'ob-view-selector', { role: 'group', 'aria-label': host.t('OrderBookView') }, [\n makeIconButton('btn-ob-view', host.t('OrderBookViewDiagonal'), 'bi-distribute-horizontal',\n { type: 'button', 'data-view': 'diagonal', 'aria-pressed': 'false' }),\n makeIconButton('btn-ob-view', host.t('OrderBookViewStacked'), 'bi-table',\n { type: 'button', 'data-view': 'stacked', 'aria-pressed': 'false' }),\n ]),\n makeIconButton('btn-ob-invert', host.t('OrderBookInvertSides'), 'bi-arrow-down-up',\n { type: 'button', 'aria-pressed': 'false' }),\n makeIconButton('btn-ob-depthtoggle', host.t('ToggleDepthChart'), 'bi-graph-up',\n { type: 'button', 'aria-pressed': 'false' }),\n makeElement('span', 'spread-label', { 'aria-live': 'polite' }, ['--']),\n makeIconButton('bt-icon-btn ob-add-btn', host.t('AddOrderbook'), 'bi-plus-lg', { type: 'button' }),\n makeIconButton('bt-icon-btn bt-icon-cancel ob-close-btn', host.t('RemoveOrderbook'), 'bi-x', { type: 'button' }),\n ]),\n makeElement('canvas', 'ob-depth-chart', { 'aria-hidden': 'true' }, []),\n makeElement('div', 'ob-col-headers', { 'aria-hidden': 'true' }, [\n makeElement('span', 'ob-col-price', {}, [host.t('Price')]),\n makeElement('span', 'ob-col-qty', {}, [host.t('Qty')]),\n makeElement('span', 'ob-col-total', {}, [host.t('Total')]),\n ]),\n makeElement('div', 'orderbook-content', { 'aria-live': 'polite', 'aria-atomic': 'false' }, [\n makeElement('div', 'orderbook-asks', { 'aria-label': host.t('AskOrders') }, []),\n makeElement('div', 'orderbook-mid', { 'aria-label': host.t('MidPrice') }, [\n makeElement('span', 'ob-mid-price', {}, ['--']),\n makeElement('span', 'ob-mid-spread', { 'aria-live': 'polite' }, []),\n ]),\n makeElement('div', 'orderbook-bids', { 'aria-label': host.t('BidOrders') }, []),\n ]),\n makeElement('div', 'ob-sentiment', { 'aria-label': host.t('OrderBookSentiment') }, [\n makeElement('div', 'ob-sent-bid', {}, [\n makeElement('span', 'ob-sent-label', {}, [host.t('BidShort')]),\n makeElement('span', 'ob-sent-bid-pct', {}, ['--']),\n ]),\n makeElement('div', 'ob-sent-ask', {}, [\n makeElement('span', 'ob-sent-ask-pct', {}, ['--']),\n makeElement('span', 'ob-sent-label', {}, [host.t('AskShort')]),\n ]),\n ]),\n ]);\n }\n\n constructor(rootEl: HTMLElement, state: Record<string, unknown>, deps: OrderBookDeps) {\n this._host = assertHost(deps?.host, 'OrderBookWidget');\n for (const name of ['onPriceSelected', 'onPriceExecuted', 'maxDepth', 'pixelRatio'] as const) {\n if (typeof deps?.[name] !== 'function')\n throw new Error(`OrderBookWidget: dep \"${name}\" is required`);\n }\n\n this.rootEl = rootEl;\n this._deps = deps;\n\n this.asksEl = this.rootEl.querySelector('.orderbook-asks');\n this.bidsEl = this.rootEl.querySelector('.orderbook-bids');\n this.midEl = this.rootEl.querySelector('.ob-mid-price');\n this.spreadEl = this.rootEl.querySelector('.spread-label');\n this.contentEl = this.rootEl.querySelector('.orderbook-content');\n this.depthChartEl = this.rootEl.querySelector('.ob-depth-chart');\n this._midSpreadEl = this.rootEl.querySelector('.ob-mid-spread');\n this._sentimentEl = this.rootEl.querySelector('.ob-sentiment');\n this._sentBidPctEl = this.rootEl.querySelector('.ob-sent-bid-pct');\n this._sentAskPctEl = this.rootEl.querySelector('.ob-sent-ask-pct');\n this._symbolLabel = this.rootEl.querySelector('.ob-symbol-label');\n this._depthBtns = Array.from(this.rootEl.querySelectorAll('.btn-ob-depth')) as HTMLElement[];\n this._viewBtns = Array.from(this.rootEl.querySelectorAll('.btn-ob-view')) as HTMLElement[];\n this._invertBtn = this.rootEl.querySelector('.btn-ob-invert');\n this._depthToggleBtn = this.rootEl.querySelector('.btn-ob-depthtoggle');\n this._addBtn = this.rootEl.querySelector('.ob-add-btn');\n this._closeBtn = this.rootEl.querySelector('.ob-close-btn');\n\n // Per-panel state is whatever the host persisted, so every value is read\n // defensively and falls back to this control's own default.\n const saved = state as {\n symbol?: string; depth?: number; view?: string;\n invertSides?: boolean; showDepthChart?: boolean; followsActive?: boolean;\n };\n\n this._prevQuantities = new Map();\n this._bidsByPrice = new Map();\n this._asksByPrice = new Map();\n this._lastSequence = 0;\n this._currentSymbol = null;\n this._followsActive = saved.followsActive === true;\n this._view = saved.view === 'diagonal' ? 'diagonal' : 'stacked';\n this._invertSides = saved.invertSides === true;\n // On by default; only an explicit false hides it.\n this._showDepthChart = saved.showDepthChart !== false;\n let depth = OrderBookWidget._isDepth(saved.depth) ? saved.depth : OrderBookWidget._deepest();\n\n // The follows-active instance restores the settings the user chose the\n // last time, which live in the host's preferences rather than in this\n // panel's state \u2014 there is one of it per page and it outlives any\n // particular layout. Pinned instances carry theirs in `state` above.\n if (this._followsActive) {\n const preferences = this._host.preferences;\n const savedDepth = Number(preferences.get(OrderBookWidget.DEPTH_KEY, null));\n if (OrderBookWidget._isDepth(savedDepth)) depth = savedDepth;\n const savedView = preferences.get(OrderBookWidget.VIEW_KEY, null);\n if (savedView === 'diagonal' || savedView === 'stacked') this._view = savedView;\n const savedInvert = preferences.get(OrderBookWidget.INVERT_KEY, null);\n if (savedInvert !== null) this._invertSides = savedInvert === 'true';\n const savedDepthChart = preferences.get(OrderBookWidget.DEPTHCHART_KEY, null);\n if (savedDepthChart !== null) this._showDepthChart = savedDepthChart === 'true';\n }\n this._depth = this._fitDepth(depth);\n\n this._wireHeader();\n this._wireBookClicks();\n\n this._refreshDepthButtons();\n this._applyViewState();\n this._refreshViewButtons();\n this._refreshInvertButton();\n this._refreshDepthToggleButton();\n\n // A follows-active instance ignores any persisted symbol: it inherits\n // the live one from the host on boot, and honouring a stale one here\n // would flash a previous session's instrument until that arrives.\n if (saved.symbol && !this._followsActive) this.setSymbol(saved.symbol);\n\n // Self-register \u2014 this is how incoming frames find their way here, and\n // an unregistered ladder is one that never updates.\n this._host.register(this);\n }\n\n /// Release the market-data subscription, leave the fan-out and remove the\n /// panel. Called by the host when the panel is destroyed.\n dispose(): void {\n if (this._currentSymbol)\n void this._host.trading.marketData.removeSymbol(this._currentSymbol).catch(() => { });\n this._host.unregister(this);\n try { this.rootEl.remove(); } catch { /* already detached */ }\n }\n\n /// Switch to a new instrument: drop the old subscription, take a new one and\n /// blank the ledger, because a diff for the old symbol must never be applied\n /// to the new one.\n setSymbol(symbol: string): void {\n if (!symbol) return;\n const next = String(symbol).toUpperCase();\n if (next === this._currentSymbol) return;\n\n const previous = this._currentSymbol;\n this._currentSymbol = next;\n this._resetLedger();\n if (this._symbolLabel) this._symbolLabel.textContent = next;\n this._render();\n\n // addSymbol / removeSymbol are refcounted by the client, so two ladders\n // on one symbol hold one subscription and neither can drop the other's.\n const marketData = this._host.trading.marketData;\n if (previous) void marketData.removeSymbol(previous).catch(() => { });\n // A ladder needs the depth, and the header reads best bid/ask, so it\n // asks for the whole set rather than for quotes alone.\n void marketData.addSymbol(next, MarketDataLevels.Full).catch(() => { });\n\n // A pinned instance remembers its instrument so a reload restores this\n // exact ladder; the follows-active one deliberately does not, because it\n // tracks whatever the host is showing.\n if (!this._followsActive) this._persistState({ symbol: next });\n }\n\n getSymbol(): string | null { return this._currentSymbol; }\n\n getDepth(): number { return this._depth; }\n\n /// True for the instance that follows the host's active symbol. A page has\n /// at most one, and a host that feeds a best-bid/ask from a ladder asks for\n /// it by this.\n isFollowsActive(): boolean { return this._followsActive; }\n\n /// The ledger as levels, best price first. Public because a host reads the\n /// touch off the ladder rather than keeping a second copy of the book.\n getBids(): BookLevel[] {\n return OrderBookWidget._sorted(this._bidsByPrice, -1);\n }\n\n getAsks(): BookLevel[] {\n return OrderBookWidget._sorted(this._asksByPrice, 1);\n }\n\n // The three setters below repaint BEFORE they persist: `_persistState` ends\n // in a layout flush and the host serializes the live panel to produce it, so\n // persisting first would write out the panel as it looked before the change\n // the user just made.\n setDepth(depth: number): void {\n if (!OrderBookWidget._isDepth(depth)) return;\n this._depth = this._fitDepth(depth);\n this._refreshDepthButtons();\n this._render();\n if (this._followsActive) this._host.preferences.set(OrderBookWidget.DEPTH_KEY, String(this._depth));\n else this._persistState({ depth: this._depth });\n }\n\n setView(view: OrderBookView): void {\n if (view !== 'diagonal' && view !== 'stacked') return;\n this._view = view;\n this._applyViewState();\n this._refreshViewButtons();\n if (this._followsActive) this._host.preferences.set(OrderBookWidget.VIEW_KEY, view);\n else this._persistState({ view });\n }\n\n /// Put the bid block above the mid line and the ask block below it, the\n /// reverse of the convention every Western exchange UI follows.\n setInvertSides(invert: boolean): void {\n this._invertSides = invert === true;\n this._applyViewState();\n this._refreshInvertButton();\n if (this._followsActive)\n this._host.preferences.set(OrderBookWidget.INVERT_KEY, this._invertSides ? 'true' : 'false');\n else this._persistState({ invertSides: this._invertSides });\n }\n\n /// Show or hide the depth chart above the ladder. The canvas keeps its last\n /// bitmap while hidden, so showing it again brings the chart straight back\n /// and the next frame repaints it.\n setShowDepthChart(show: boolean): void {\n this._showDepthChart = show === true;\n this._applyViewState();\n this._refreshDepthToggleButton();\n if (this._followsActive)\n this._host.preferences.set(OrderBookWidget.DEPTHCHART_KEY, this._showDepthChart ? 'true' : 'false');\n this._persistState({ showDepthChart: this._showDepthChart });\n }\n\n /// Apply one frame. A snapshot replaces the ledger; a diff merges into it,\n /// with `quantity: 0` deleting a level. A break in the sequence means a\n /// frame was missed, so the ladder asks for a fresh snapshot instead of\n /// applying a diff to state it can no longer trust.\n ///\n /// Every instance receives every frame, so the symbol filter here is what\n /// keeps a frame for one instrument out of a ladder watching another.\n applyFrame(frame: OrderBookFrame): void {\n if (!frame || !frame.symbol) return;\n if (this._currentSymbol && frame.symbol !== this._currentSymbol) return;\n if (this._currentSymbol !== frame.symbol) {\n this._currentSymbol = frame.symbol;\n // The follows-active instance can receive its first frame before the\n // host calls setSymbol, whose early return on an unchanged symbol\n // would then skip the label.\n if (this._symbolLabel) this._symbolLabel.textContent = frame.symbol;\n }\n\n const sequence = frame.sequence ?? 0;\n if (frame.isSnapshot) {\n this._resetLedger();\n this._applySnapshotSide(this._bidsByPrice, frame.bids, 'bid', frame.symbol, sequence);\n this._applySnapshotSide(this._asksByPrice, frame.asks, 'ask', frame.symbol, sequence);\n this._lastSequence = sequence;\n } else {\n if (this._lastSequence !== 0 && sequence !== this._lastSequence + 1) {\n this._anomaly(`sequence gap: expected ${this._lastSequence + 1}, got ${sequence} sym=${frame.symbol} \u2014 requesting snapshot`);\n this._lastSequence = 0;\n void this._requestResubscribe(frame.symbol);\n return;\n }\n this._applyDiffSide(this._bidsByPrice, frame.bids, 'bid', frame.symbol, sequence);\n this._applyDiffSide(this._asksByPrice, frame.asks, 'ask', frame.symbol, sequence);\n this._lastSequence = sequence || this._lastSequence;\n }\n this._validateBookCross(frame.symbol, sequence, frame.isSnapshot === true);\n this._render();\n }\n\n _wireHeader(): void {\n for (const button of this._depthBtns) {\n button.addEventListener('click', (e) => {\n e.preventDefault();\n this.setDepth(Number(button.dataset.depth));\n });\n }\n for (const button of this._viewBtns) {\n button.addEventListener('click', (e) => {\n e.preventDefault();\n const view = button.dataset.view;\n if (view === 'diagonal' || view === 'stacked') this.setView(view);\n });\n }\n this._invertBtn?.addEventListener('click', (e) => {\n e.preventDefault();\n this.setInvertSides(!this._invertSides);\n });\n this._depthToggleBtn?.addEventListener('click', (e) => {\n e.preventDefault();\n this.setShowDepthChart(!this._showDepthChart);\n });\n\n // Another ladder, starting where this one is \u2014 the most natural copy\n // gesture, and the host decides where it goes.\n this._addBtn?.addEventListener('click', (e) => {\n e.preventDefault();\n e.stopPropagation();\n this._host.spawn({\n symbol: this._currentSymbol,\n depth: this._depth,\n view: this._view,\n invertSides: this._invertSides,\n showDepthChart: this._showDepthChart,\n followsActive: false,\n });\n });\n\n this._closeBtn?.addEventListener('click', (e) => {\n e.preventDefault();\n e.stopPropagation();\n this._host.close();\n });\n\n // Picking an instrument retargets THIS ladder only \u2014 a user who wanted\n // the whole page to follow would have used the host's own instrument\n // selector. Picking on the follows-active instance detaches it: it\n // becomes a pinned ladder on the chosen symbol, identical to any other.\n this._symbolLabel?.addEventListener('click', (e) => {\n e.preventDefault();\n e.stopPropagation();\n this._host.trading.pickInstrument((symbol) => {\n if (this._followsActive) {\n this._followsActive = false;\n this._persistState({ followsActive: false });\n }\n this.setSymbol(symbol);\n });\n });\n }\n\n // A click anywhere on a level, caught once on the container: the rows are\n // rebuilt on every frame, so a listener per row would be re-bound dozens of\n // times a second.\n //\n // Plain click prefills an order at that price and leaves the sending to the\n // user. Ctrl/Cmd click sends at once \u2014 a single click doing that is how a\n // sneeze on the bid becomes a fill.\n _wireBookClicks(): void {\n this.contentEl?.addEventListener('click', (e) => {\n const target = e.target as Element | null;\n const row = target?.closest('.ob-row') as HTMLElement | null;\n if (!row) return;\n const price = Number(row.dataset.price);\n if (!Number.isFinite(price)) return;\n\n // A bid row is where the user sells and an ask row is where they\n // buy, so the row carries the side of the order, not of the level.\n const side = row.dataset.side === 'buy' ? 0 : 1;\n if (e.ctrlKey || e.metaKey) this._deps.onPriceExecuted(price, side);\n else this._deps.onPriceSelected(price, side);\n });\n }\n\n _refreshDepthButtons(): void {\n for (const button of this._depthBtns) {\n const on = Number(button.dataset.depth) === this._depth;\n button.classList.toggle('active', on);\n button.setAttribute('aria-pressed', on ? 'true' : 'false');\n }\n }\n\n _refreshViewButtons(): void {\n for (const button of this._viewBtns) {\n const on = button.dataset.view === this._view;\n button.classList.toggle('active', on);\n button.setAttribute('aria-pressed', on ? 'true' : 'false');\n }\n }\n\n _refreshInvertButton(): void {\n this._invertBtn?.classList.toggle('active', this._invertSides);\n this._invertBtn?.setAttribute('aria-pressed', this._invertSides ? 'true' : 'false');\n }\n\n _refreshDepthToggleButton(): void {\n this._depthToggleBtn?.classList.toggle('active', this._showDepthChart);\n this._depthToggleBtn?.setAttribute('aria-pressed', this._showDepthChart ? 'true' : 'false');\n }\n\n // The variant classes on the panel root the stylesheet keys the two layouts,\n // the side order and the depth chart off.\n _applyViewState(): void {\n this.rootEl.classList.toggle('ob-view-stacked', this._view === 'stacked');\n this.rootEl.classList.toggle('ob-view-diagonal', this._view === 'diagonal');\n this.rootEl.classList.toggle('ob-invert', this._invertSides);\n this.rootEl.classList.toggle('ob-hide-depth', !this._showDepthChart);\n }\n\n // Record what this panel remembers, then ask the host to flush the layout.\n // Two calls because they are two decisions, and every caller here is a\n // setting the user just changed by hand \u2014 so it is worth flushing.\n _persistState(patch: Record<string, unknown>): void {\n this._host.persistState(patch);\n this._host.saveLayout();\n }\n\n _resetLedger(): void {\n this._bidsByPrice.clear();\n this._asksByPrice.clear();\n this._prevQuantities.clear();\n this._lastSequence = 0;\n }\n\n // The host answers how many levels it has room for, so a phone-sized page\n // narrows the ladder without this control knowing what a viewport is.\n _fitDepth(depth: number): number {\n const room = Number(this._deps.maxDepth());\n if (!Number.isFinite(room) || room < 1) return depth;\n return Math.min(depth, room);\n }\n\n static _isDepth(depth: unknown): depth is number {\n return typeof depth === 'number' && OrderBookWidget.DEPTHS.includes(depth);\n }\n\n static _deepest(): number {\n return OrderBookWidget.DEPTHS[OrderBookWidget.DEPTHS.length - 1];\n }\n\n static _sorted(ledger: Map<number, number>, direction: 1 | -1): BookLevel[] {\n return [...ledger.entries()]\n .filter(([price, quantity]) => Number.isFinite(price) && quantity > 0)\n .sort((left, right) => direction * (left[0] - right[0]))\n .map(([price, quantity]) => ({ price, quantity }));\n }\n\n _applySnapshotSide(ledger: Map<number, number>, levels: QuoteLevel[] | undefined, side: string, symbol: string, sequence: number): void {\n for (const level of levels || []) {\n const quantity = level.quantity ?? 0;\n if (level.price == null || !Number.isFinite(level.price) || !(quantity > 0)) {\n this._anomaly(`snapshot ${side} level dropped: price=${level.price} qty=${level.quantity} sym=${symbol} seq=${sequence}`);\n continue;\n }\n ledger.set(level.price, quantity);\n }\n }\n\n _applyDiffSide(ledger: Map<number, number>, levels: QuoteLevel[] | undefined, side: string, symbol: string, sequence: number): void {\n for (const level of levels || []) {\n // A level with a non-finite price is wire corruption: applying it\n // lands a NaN key in the ledger, which sorts unpredictably and\n // renders as a phantom row between two real levels.\n if (level.price == null || !Number.isFinite(level.price)) {\n this._anomaly(`invalid ${side} price: price=${level.price} qty=${level.quantity} sym=${symbol} seq=${sequence} \u2014 ignoring`);\n continue;\n }\n const quantity = level.quantity ?? 0;\n if (quantity < 0) {\n this._anomaly(`negative ${side} qty: price=${level.price} qty=${quantity} sym=${symbol} seq=${sequence} \u2014 ignoring`);\n continue;\n }\n if (quantity === 0) {\n if (!ledger.has(level.price)) {\n this._anomaly(`delete missing ${side}: price=${level.price} sym=${symbol} seq=${sequence}`);\n continue;\n }\n ledger.delete(level.price);\n } else {\n ledger.set(level.price, quantity);\n }\n }\n }\n\n // Best bid below best ask, or something upstream is broken \u2014 usually a diff\n // applied to stale state after a gap the sequence check did not catch.\n _validateBookCross(symbol: string, sequence: number, wasSnapshot: boolean): void {\n if (this._bidsByPrice.size === 0 || this._asksByPrice.size === 0) return;\n let bestBid = -Infinity;\n let bestAsk = Infinity;\n for (const price of this._bidsByPrice.keys()) if (price > bestBid) bestBid = price;\n for (const price of this._asksByPrice.keys()) if (price < bestAsk) bestAsk = price;\n if (bestBid >= bestAsk) {\n this._anomaly(`crossed book: bid=${bestBid} >= ask=${bestAsk} sym=${symbol} seq=${sequence} ${wasSnapshot ? 'snapshot' : 'diff'}`);\n }\n }\n\n // Ask for the symbol from scratch. Idempotent as far as the user is\n // concerned: the ladder blanks for a frame and refills.\n async _requestResubscribe(symbol: string): Promise<void> {\n try {\n await this._host.trading.marketData.resubscribe(symbol, MarketDataLevels.Full);\n } catch (err) {\n this._anomaly(`resubscribe after a gap failed for ${symbol}: ${err}`);\n }\n }\n\n // Wire trouble the user cannot see and support has to, through the port.\n _anomaly(message: string): void {\n this._host.log(`OrderBookWidget: ${message}`);\n }\n\n _render(): void {\n this._paint(this.getBids(), this.getAsks());\n }\n\n _paint(bids: BookLevel[], asks: BookLevel[]): void {\n if (!this.asksEl || !this.bidsEl) return;\n\n const visibleAsks = asks.slice(0, this._depth);\n const visibleBids = bids.slice(0, this._depth);\n\n // The bar is scaled per side \u2014 a 100k bid wall must not flatten a normal\n // 200-lot ask ladder on the other half \u2014 while the heat tint is scaled\n // across both, so the two halves stay perceptually comparable.\n const maxBid = Math.max(...visibleBids.map(level => level.quantity), 1);\n const maxAsk = Math.max(...visibleAsks.map(level => level.quantity), 1);\n const maxBoth = Math.max(maxBid, maxAsk, 1);\n\n const own = this._ownQuantities();\n const painted = new Map<string, number>();\n\n // Asks are rendered worst-first so the best ask ends up next to the mid\n // line, while the cumulative total runs the other way \u2014 the best ask\n // reads `qty == total` and the sum grows as the eye moves away from the\n // spread.\n const askTotals: number[] = [];\n let running = 0;\n for (const level of visibleAsks) {\n running += level.quantity;\n askTotals.push(running);\n }\n const askRows: HTMLElement[] = [];\n for (let i = visibleAsks.length - 1; i >= 0; i--) {\n askRows.push(this._levelRow(visibleAsks[i], {\n rowClass: 'ob-row ask',\n orderSide: 'buy',\n key: `a_${visibleAsks[i].price}`,\n cumulative: askTotals[i],\n barPct: (visibleAsks[i].quantity / maxAsk) * 100,\n heatPct: HEAT_FLOOR_PCT + HEAT_RANGE_PCT * (visibleAsks[i].quantity / maxBoth),\n // More size on the offer is pressure downward, so a growing ask\n // level flashes in the falling colour.\n growthClass: 'flash-red',\n shrinkClass: 'flash-green',\n ownQuantity: own.asks.get(OrderBookWidget._priceKey(visibleAsks[i].price)),\n painted,\n }));\n }\n this.asksEl.replaceChildren(...askRows);\n\n running = 0;\n const bidRows: HTMLElement[] = [];\n for (const level of visibleBids) {\n running += level.quantity;\n bidRows.push(this._levelRow(level, {\n rowClass: 'ob-row bid',\n orderSide: 'sell',\n key: `b_${level.price}`,\n cumulative: running,\n barPct: (level.quantity / maxBid) * 100,\n heatPct: HEAT_FLOOR_PCT + HEAT_RANGE_PCT * (level.quantity / maxBoth),\n growthClass: 'flash-green',\n shrinkClass: 'flash-red',\n ownQuantity: own.bids.get(OrderBookWidget._priceKey(level.price)),\n painted,\n }));\n }\n this.bidsEl.replaceChildren(...bidRows);\n\n this._prevQuantities = painted;\n\n if (asks.length > 0 && bids.length > 0) {\n const bestAsk = asks[0].price;\n const bestBid = bids[0].price;\n const spread = bestAsk - bestBid;\n if (this.midEl) this.midEl.textContent = formatPrice((bestAsk + bestBid) / 2);\n if (this._midSpreadEl) this._midSpreadEl.textContent = formatPrice(spread);\n if (this.spreadEl) this.spreadEl.textContent = this._host.t('Spread: {0}', formatPrice(spread));\n }\n\n this._paintSentiment(visibleBids, visibleAsks);\n this._paintDepthChart(visibleBids, visibleAsks);\n }\n\n // One level. Every measurement it carries \u2014 the bar's share of its side, the\n // heat tint \u2014 reaches the stylesheet as a custom property, so the widths and\n // the colours are still declared in CSS.\n _levelRow(level: BookLevel, options: {\n rowClass: string; orderSide: string; key: string; cumulative: number;\n barPct: number; heatPct: number; growthClass: string; shrinkClass: string;\n ownQuantity: number | undefined; painted: Map<string, number>;\n }): HTMLElement {\n const quantity = makeElement('span', 'qty', {}, [formatQty(level.quantity)]);\n if (options.ownQuantity) {\n quantity.appendChild(makeElement('span', 'own-badge',\n { title: this._host.t('YourOrder') }, [formatQty(options.ownQuantity)]));\n }\n\n const row = makeElement('div', options.rowClass, {\n 'data-side': options.orderSide,\n 'data-price': String(level.price),\n }, [\n makeElement('span', 'price', {}, [formatPrice(level.price)]),\n quantity,\n makeElement('span', 'total', {}, [formatQty(options.cumulative)]),\n makeElement('span', 'bar', {}, []),\n ]);\n row.style.setProperty('--t-ob-bar', `${options.barPct.toFixed(0)}%`);\n row.style.setProperty('--t-ob-heat', `${options.heatPct.toFixed(1)}%`);\n if (options.ownQuantity) row.classList.add('own');\n\n const previous = this._prevQuantities.get(options.key);\n if (previous !== undefined && previous !== level.quantity)\n row.classList.add(level.quantity > previous ? options.growthClass : options.shrinkClass);\n options.painted.set(options.key, level.quantity);\n\n return row;\n }\n\n // This session's resting orders on this symbol, summed per price \u2014 the \"your\n // size is here\" badge is an overlay on the ledger, so it reads the client's\n // own order cache rather than subscribing to anything.\n //\n // Prices are keyed as fixed-point text: two floats that print the same are\n // the same level to a user, and `===` on them is not.\n _ownQuantities(): { bids: Map<string, number>; asks: Map<string, number> } {\n const bids = new Map<string, number>();\n const asks = new Map<string, number>();\n if (!this._currentSymbol) return { bids, asks };\n\n for (const order of this._host.trading.marketData.getOrders() || []) {\n if (!order || order.instrument !== this._currentSymbol) continue;\n if (order.status !== RESTING_STATE) continue;\n if (order.limitPrice == null || order.side == null) continue;\n const side = this._host.presentation.isBuy(order.side) ? bids : asks;\n const key = OrderBookWidget._priceKey(order.limitPrice);\n const left = Number(order.balance ?? order.quantity) || 0;\n side.set(key, (side.get(key) || 0) + left);\n }\n return { bids, asks };\n }\n\n static _priceKey(price: number): string {\n return Number(price).toFixed(8);\n }\n\n // The bid/ask split under the ladder, as one measurement the stylesheet\n // turns into two widths.\n //\n // It sums the VISIBLE levels only, so the figure moves when the user changes\n // depth. A whole-book share needs the server to aggregate one, which the\n // frame does not carry today.\n _paintSentiment(bids: BookLevel[], asks: BookLevel[]): void {\n if (!this._sentimentEl) return;\n const bidSum = bids.reduce((sum, level) => sum + level.quantity, 0);\n const askSum = asks.reduce((sum, level) => sum + level.quantity, 0);\n const total = bidSum + askSum;\n\n if (total <= 0) {\n this._sentimentEl.style.setProperty('--t-ob-sent', '50%');\n if (this._sentBidPctEl) this._sentBidPctEl.textContent = '--';\n if (this._sentAskPctEl) this._sentAskPctEl.textContent = '--';\n return;\n }\n // Rounded so the two halves sum to exactly 100 \u2014 a strip reading 99% is\n // read as a bug rather than as rounding.\n const askPct = Math.round((askSum / total) * 100);\n const bidPct = 100 - askPct;\n this._sentimentEl.style.setProperty('--t-ob-sent', `${bidPct}%`);\n if (this._sentBidPctEl) this._sentBidPctEl.textContent = `${bidPct}%`;\n if (this._sentAskPctEl) this._sentAskPctEl.textContent = `${askPct}%`;\n }\n\n // The depth curve above the ladder. Everything shaped is in\n // `orderbook-depth.ts`; what is left here is the canvas plumbing and the\n // host's palette, because a canvas takes colours as values and a class name\n // cannot reach one.\n _paintDepthChart(bids: BookLevel[], asks: BookLevel[]): void {\n const canvas = this.depthChartEl;\n if (!canvas || !this._showDepthChart) return;\n\n const ratio = Number(this._deps.pixelRatio()) || 1;\n const cssWidth = canvas.clientWidth || 0;\n const cssHeight = canvas.clientHeight || 0;\n if (cssWidth < 4 || cssHeight < 4) return;\n\n const width = Math.floor(cssWidth * ratio);\n const height = Math.floor(cssHeight * ratio);\n if (canvas.width !== width) canvas.width = width;\n if (canvas.height !== height) canvas.height = height;\n\n const ctx = canvas.getContext('2d');\n if (!ctx) return;\n ctx.clearRect(0, 0, width, height);\n if (bids.length === 0 && asks.length === 0) return;\n\n const pad = 2 * ratio;\n const midX = Math.floor(width / 2);\n const geometry: DepthGeometry = {\n midX,\n pad,\n halfWidth: midX - pad,\n baseY: height - pad,\n innerHeight: height - pad * 2,\n };\n\n const bidSide = depthSide(bids, -1, geometry);\n const askSide = depthSide(asks, 1, geometry);\n const maxTotal = Math.max(bidSide ? bidSide.total : 0, askSide ? askSide.total : 0, 1);\n const palette = this._host.presentation.canvasPalette();\n\n if (bidSide) this._strokeDepthSide(ctx, depthPolyline(bidSide, maxTotal, geometry), palette.up, geometry, ratio);\n if (askSide) this._strokeDepthSide(ctx, depthPolyline(askSide, maxTotal, geometry), palette.down, geometry, ratio);\n\n // The mid line, so the two halves read as two sides of one price rather\n // than as two charts.\n ctx.globalAlpha = 0.4;\n ctx.fillStyle = palette.grid;\n ctx.fillRect(midX - Math.max(1, ratio / 2), 0, Math.max(1, ratio), height);\n ctx.globalAlpha = 1;\n }\n\n // Area under the step curve, then the curve on top. Opacity is varied here\n // rather than in the colour, so the host states one solid colour per\n // direction and this decides how much of it a fill is worth.\n _strokeDepthSide(ctx: CanvasRenderingContext2D, line: [number, number][], color: string, geometry: DepthGeometry, ratio: number): void {\n if (line.length === 0) return;\n\n ctx.beginPath();\n ctx.moveTo(line[0][0], geometry.baseY);\n for (const [x, y] of line) ctx.lineTo(x, y);\n ctx.lineTo(line[line.length - 1][0], geometry.baseY);\n ctx.closePath();\n ctx.globalAlpha = 0.18;\n ctx.fillStyle = color;\n ctx.fill();\n\n ctx.beginPath();\n ctx.moveTo(line[0][0], line[0][1]);\n for (let i = 1; i < line.length; i++) ctx.lineTo(line[i][0], line[i][1]);\n ctx.lineWidth = Math.max(1.5, 1.5 * ratio);\n ctx.lineJoin = 'miter';\n ctx.globalAlpha = 0.95;\n ctx.strokeStyle = color;\n ctx.stroke();\n ctx.globalAlpha = 1;\n }\n}\n", "// The geometry of a cumulative P&L curve, as pure functions over numbers.\n//\n// The same curve is read in three places and drawn at three sizes: a strategy row's sparkline,\n// a statistics card, and a backtest's own chart. Only the last of those is a price chart - it\n// shares the price axis and belongs to the chart engine. The other two are a curve in a box,\n// and this is that curve: where each point lands, where the baseline sits, and which way the\n// run ended. No canvas, so it can be checked against arithmetic rather than against pixels.\n\n/// One sample of a run's cumulative P&L. Time is whatever the caller counts in - seconds, unix\n/// milliseconds - because only the spacing between samples is used, never the absolute value.\nexport interface PnlPoint {\n time: number;\n value: number;\n}\n\n/// The box one curve fills, in device pixels. The padding keeps a curve at its extreme off the\n/// edge, where a stroke would be clipped in half.\nexport interface PnlBox {\n width: number;\n height: number;\n padX: number;\n padY: number;\n}\n\nexport interface PnlCurve {\n /// The curve, in draw order.\n points: [number, number][];\n /// The curve closed down to the baseline, ready to fill.\n area: [number, number][];\n /// Y of zero P&L. Always inside the box: the range is widened to include it.\n zeroY: number;\n /// The value range the box covers, zero included.\n min: number;\n max: number;\n /// Whether the run ended at or above where it started. What the curve is coloured by - and\n /// the last value, not the highest: a run that peaked and gave it all back is a loss.\n positive: boolean;\n}\n\n/// A run reduced to at most `count` samples, spanning the same stretch of time.\n///\n/// A sparkline that keeps only the newest N samples marches sideways: each new sample pushes the\n/// oldest off the left edge, so the shape slides and the run's beginning is lost. Compressing\n/// instead keeps the whole run in the box and re-buckets it, so a new sample changes the curve's\n/// shape rather than its position - which is what makes two readings of the same strategy\n/// comparable.\n///\n/// A bucket is summarised by its LAST sample, not by an average. This is a cumulative figure:\n/// the last value in a bucket is where the run actually stood at that moment, while an average\n/// would draw a rising run below the line it was on. Every point returned is therefore a real\n/// sample, never a computed one, and the first and last are kept exactly - they are where the\n/// run started and where it stands now.\nexport function compressPnl(points: readonly PnlPoint[], count: number): PnlPoint[] {\n if (points.length <= count || count < 2) return points.slice();\n\n const first = points[0];\n const last = points[points.length - 1];\n const span = last.time - first.time;\n\n // Every sample at the same instant: there is no time to bucket by, so fall back to position.\n if (!(span > 0)) {\n const step = (points.length - 1) / (count - 1);\n return Array.from({ length: count }, (_, i) => points[Math.round(i * step)]);\n }\n\n // One bucket per output point, the last sample in each standing for it. Buckets that caught\n // nothing are skipped rather than repeated, so a quiet stretch reads as a long flat segment\n // instead of as a row of identical points.\n // `count - 1` buckets, not `count`: the first sample is seeded and the last is appended, so\n // the buckets between them may contribute at most `count - 2` points. Sized any wider the\n // result can overrun `count`, and trimming it afterwards would drop the run's start - the\n // one point this function exists to keep.\n const buckets = count - 1;\n const out: PnlPoint[] = [first];\n let bucket = 0;\n for (let i = 1; i < points.length; i++) {\n const index = Math.min(buckets - 1, Math.floor(((points[i].time - first.time) / span) * buckets));\n if (index > bucket) {\n out.push(points[i - 1]);\n bucket = index;\n }\n }\n if (out[out.length - 1] !== last) out.push(last);\n\n return out;\n}\n\n/// Lay a run out in a box, or null when there is no curve to draw.\n///\n/// Two rules that are not arbitrary. Zero is always in the range, so a run that only ever won\n/// still shows the line it started from and the height of the curve means something. And the\n/// horizontal axis is time, not sample index: a strategy that traded twice in the morning and\n/// forty times after lunch should look like that, not like a curve with evenly spaced steps.\nexport function pnlCurve(points: readonly PnlPoint[], box: PnlBox): PnlCurve | null {\n const clean = points\n .filter(p => p !== null && p !== undefined && Number.isFinite(p.time) && Number.isFinite(p.value))\n .slice()\n .sort((a, b) => a.time - b.time);\n\n if (clean.length < 2) return null;\n\n // One sample per drawable pixel is as much as a box can show; past that the extra points are\n // strokes on top of strokes. Compressing here rather than asking the caller to do it is what\n // lets a host keep the whole run and still get a curve that does not slide.\n const drawable = Math.max(2, Math.round(box.width - box.padX * 2));\n const shown = compressPnl(clean, drawable);\n\n const values = shown.map(p => p.value);\n const min = Math.min(0, ...values);\n const max = Math.max(0, ...values);\n\n const left = box.padX;\n const right = box.width - box.padX;\n const top = box.padY;\n const bottom = box.height - box.padY;\n\n const firstTime = shown[0].time;\n const lastTime = shown[shown.length - 1].time;\n const timeSpan = lastTime - firstTime;\n const valueSpan = max - min;\n\n // A run with no spread in one axis collapses onto a line rather than dividing by nothing:\n // every sample at the same instant stacks at the left edge, every sample at the same value\n // rests on the top edge, which for a flat run is also its baseline.\n const x = (time: number): number => (timeSpan === 0 ? left : left + ((time - firstTime) / timeSpan) * (right - left));\n const y = (value: number): number => (valueSpan === 0 ? top : bottom - ((value - min) / valueSpan) * (bottom - top));\n\n const curve = shown.map(p => [x(p.time), y(p.value)] as [number, number]);\n const zeroY = y(0);\n\n return {\n points: curve,\n // Closed along the baseline rather than along the foot of the box: a run entirely above\n // water fills the gap between itself and zero, and nothing below it.\n area: [...curve, [curve[curve.length - 1][0], zeroY], [curve[0][0], zeroY]],\n zeroY,\n min,\n max,\n positive: shown[shown.length - 1].value >= 0,\n };\n}\n\n/// What a curve is drawn with. Colours come from the caller because the page owns its palette;\n/// the two directions are the same pair every other control uses for up and down.\nexport interface PnlCurveStyle {\n up: string;\n down: string;\n /// The zero line. A curve is read against it, so it is drawn even when nothing crosses it.\n baseline: string;\n lineWidth: number;\n /// How much of the direction colour the filled area keeps, 0 to 1.\n fillOpacity: number;\n}\n\n/// The 2D calls one paint makes. Narrow on purpose: a test supplies a recorder, and a control\n/// that started reaching for something else shows up here rather than in a screenshot.\nexport interface PnlCurveContext {\n clearRect(x: number, y: number, w: number, h: number): void;\n beginPath(): void;\n moveTo(x: number, y: number): void;\n lineTo(x: number, y: number): void;\n closePath(): void;\n stroke(): void;\n fill(): void;\n setLineDash(segments: number[]): void;\n globalAlpha: number;\n // The browser's own type for these, so a real 2D context satisfies this contract as\n // it stands: it accepts a gradient or a pattern where this only ever writes a colour.\n strokeStyle: string | CanvasGradient | CanvasPattern;\n fillStyle: string | CanvasGradient | CanvasPattern;\n lineWidth: number;\n}\n\n/// Paint a laid-out curve: the baseline, the filled area under it, then the line itself.\nexport function drawPnlCurve(ctx: PnlCurveContext, curve: PnlCurve, box: PnlBox, style: PnlCurveStyle): void {\n const colour = curve.positive ? style.up : style.down;\n\n ctx.clearRect(0, 0, box.width, box.height);\n\n ctx.setLineDash([2, 3]);\n ctx.strokeStyle = style.baseline;\n ctx.lineWidth = 1;\n ctx.beginPath();\n ctx.moveTo(box.padX, curve.zeroY);\n ctx.lineTo(box.width - box.padX, curve.zeroY);\n ctx.stroke();\n ctx.setLineDash([]);\n\n ctx.globalAlpha = style.fillOpacity;\n ctx.fillStyle = colour;\n ctx.beginPath();\n ctx.moveTo(curve.area[0][0], curve.area[0][1]);\n for (const [px, py] of curve.area.slice(1)) ctx.lineTo(px, py);\n ctx.closePath();\n ctx.fill();\n ctx.globalAlpha = 1;\n\n ctx.strokeStyle = colour;\n ctx.lineWidth = style.lineWidth;\n ctx.beginPath();\n ctx.moveTo(curve.points[0][0], curve.points[0][1]);\n for (const [px, py] of curve.points.slice(1)) ctx.lineTo(px, py);\n ctx.stroke();\n}\n", "// Option pricing and the greeks, as pure functions.\n//\n// The desktop desk gets these from StockSharp.Algo.Derivatives, which a browser cannot call. A\n// host could compute them and send them down, and one that already does should - the desk takes\n// them either way. But a host that has bid, ask and an expiry has everything the arithmetic\n// needs, and making it stand up a pricing service before it can draw a chain is a poor trade.\n//\n// So the maths ships, and it ships here, apart from the control: a wrong greek is not visible\n// on screen the way a wrong column is, and the only defence is checking the numbers against\n// values that are known.\n//\n// Conventions follow the desk that reads them, not the textbook: vega is per one point of\n// volatility, theta is per calendar day, and rho is per one point of rate - the units a trader\n// works in, rather than per 1.0 of each, which nobody quotes.\n\nexport const OptionTypes = {\n Call: 'call',\n Put: 'put',\n} as const;\n\nexport type OptionType = typeof OptionTypes[keyof typeof OptionTypes];\n\n/// Everything a price depends on. Time is in years, rates and volatility are fractions - 0.05\n/// is five percent, not five.\nexport interface OptionInputs {\n assetPrice: number;\n strike: number;\n /// Years remaining. Zero once expired, which makes every greek zero and the premium\n /// intrinsic.\n timeToExpiry: number;\n riskFree: number;\n dividend: number;\n /// Volatility, as a fraction.\n deviation: number;\n}\n\nexport interface Greeks {\n delta: number;\n gamma: number;\n /// Per one point of volatility.\n vega: number;\n /// Per calendar day.\n theta: number;\n /// Per one point of rate.\n rho: number;\n}\n\nconst SQRT_2PI = Math.sqrt(2 * Math.PI);\nconst DAYS_IN_YEAR = 365;\n\n/// The standard normal CDF.\n///\n/// Hart's rational approximation, which is what pricing libraries use and what a desk's numbers\n/// are expected to agree with: accurate to about 1e-15 across the range, and exactly a half at\n/// the mean rather than nearly so. The cheaper Abramowitz-and-Stegun fit is good to 1e-7, which\n/// sounds like enough until a greek that should be symmetric is not.\nexport function normalCdf(x: number): number {\n const z = Math.abs(x);\n\n // Beyond this the tail is smaller than double precision can carry anyway.\n if (z > 37) return x > 0 ? 1 : 0;\n\n const e = Math.exp(-(z * z) / 2);\n let tail: number;\n\n if (z < 7.07106781186547) {\n let b = 3.52624965998911e-02 * z + 0.700383064443688;\n b = b * z + 6.37396220353165;\n b = b * z + 33.912866078383;\n b = b * z + 112.079291497871;\n b = b * z + 221.213596169931;\n b = b * z + 220.206867912376;\n\n let d = 8.83883476483184e-02 * z + 1.75566716318264;\n d = d * z + 16.064177579207;\n d = d * z + 86.7807322029461;\n d = d * z + 296.564248779674;\n d = d * z + 637.333633378831;\n d = d * z + 793.826512519948;\n d = d * z + 440.413735824752;\n\n tail = (e * b) / d;\n } else {\n // A continued fraction, which is what stays accurate once the polynomial ratio above\n // starts losing digits to cancellation.\n let b = z + 0.65;\n b = z + 4 / b;\n b = z + 3 / b;\n b = z + 2 / b;\n b = z + 1 / b;\n tail = e / (b * 2.506628274631);\n }\n\n return x > 0 ? 1 - tail : tail;\n}\n\n/// The standard normal density.\nexport function normalPdf(x: number): number {\n return Math.exp(-0.5 * x * x) / SQRT_2PI;\n}\n\n/// d1, or zero when there is no time or no volatility for the division to mean anything.\nexport function d1(inputs: OptionInputs): number {\n const { assetPrice, strike, timeToExpiry, riskFree, dividend, deviation } = inputs;\n const spread = deviation * Math.sqrt(timeToExpiry);\n\n if (spread === 0 || assetPrice <= 0 || strike <= 0) return 0;\n\n return (Math.log(assetPrice / strike) + (riskFree - dividend + (deviation * deviation) / 2) * timeToExpiry) / spread;\n}\n\n/// d2, which is d1 less one standard deviation of the remaining time.\nexport function d2(inputs: OptionInputs): number {\n const spread = inputs.deviation * Math.sqrt(inputs.timeToExpiry);\n return spread === 0 ? 0 : d1(inputs) - spread;\n}\n\n/// What the option is worth.\n///\n/// At expiry, or with no volatility, that is its intrinsic value - the formula degenerates to\n/// exactly that, and saying so explicitly keeps a zero denominator out of the general case.\nexport function premium(type: OptionType, inputs: OptionInputs): number {\n const { assetPrice, strike, timeToExpiry, riskFree, dividend, deviation } = inputs;\n\n if (timeToExpiry <= 0 || deviation <= 0)\n return Math.max(0, type === OptionTypes.Call ? assetPrice - strike : strike - assetPrice);\n\n const a = d1(inputs);\n const b = d2(inputs);\n const carried = assetPrice * Math.exp(-dividend * timeToExpiry);\n const discounted = strike * Math.exp(-riskFree * timeToExpiry);\n\n return type === OptionTypes.Call\n ? carried * normalCdf(a) - discounted * normalCdf(b)\n : discounted * normalCdf(-b) - carried * normalCdf(-a);\n}\n\n/// Every greek at once: they share d1, and computing them together is both cheaper and the only\n/// way they are guaranteed to describe the same moment.\nexport function greeks(type: OptionType, inputs: OptionInputs): Greeks {\n const { assetPrice, strike, timeToExpiry, riskFree, dividend, deviation } = inputs;\n const sign = type === OptionTypes.Call ? 1 : -1;\n\n if (timeToExpiry <= 0 || deviation <= 0 || assetPrice <= 0) {\n // An expired option still has a delta - it is one or nothing, depending on which side of\n // the strike it finished. Everything else has stopped moving.\n const inTheMoney = type === OptionTypes.Call ? assetPrice > strike : assetPrice < strike;\n return { delta: inTheMoney ? sign : 0, gamma: 0, vega: 0, theta: 0, rho: 0 };\n }\n\n const a = d1(inputs);\n const b = d2(inputs);\n const sqrtT = Math.sqrt(timeToExpiry);\n const density = normalPdf(a);\n const carry = Math.exp(-dividend * timeToExpiry);\n const discount = Math.exp(-riskFree * timeToExpiry);\n\n const delta = sign * carry * normalCdf(sign * a);\n const gamma = (carry * density) / (assetPrice * deviation * sqrtT);\n\n // Scaled the way a desk quotes them: vega per one volatility point, rho per one rate point,\n // theta per calendar day.\n const vega = assetPrice * carry * density * sqrtT * 0.01;\n const rho = sign * strike * timeToExpiry * discount * normalCdf(sign * b) * 0.01;\n\n const theta = (\n -(assetPrice * carry * density * deviation) / (2 * sqrtT)\n - sign * riskFree * strike * discount * normalCdf(sign * b)\n + sign * dividend * assetPrice * carry * normalCdf(sign * a)\n ) / DAYS_IN_YEAR;\n\n return { delta, gamma, vega, theta, rho };\n}\n\n/// The volatility that would produce this price, or null when none would.\n///\n/// Bisection rather than Newton: vega collapses far from the money and deep in time, and a\n/// Newton step divided by a vanishing vega walks off to nonsense. Halving cannot, and forty\n/// steps over a range this wide is finer than any quote.\nexport function impliedVolatility(type: OptionType, inputs: OptionInputs, price: number): number | null {\n if (!(price > 0) || inputs.timeToExpiry <= 0) return null;\n\n const at = (deviation: number): number => premium(type, { ...inputs, deviation });\n\n let low = 1e-6;\n let high = 5;\n\n // A price outside what any volatility in that range can produce has no answer, and\n // returning the nearest bound would be a number that means nothing.\n if (price < at(low) || price > at(high)) return null;\n\n for (let step = 0; step < 60; step++) {\n const mid = (low + high) / 2;\n if (at(mid) < price) low = mid;\n else high = mid;\n }\n\n return (low + high) / 2;\n}\n", "// Option desk \u2014 multi-instance.\n//\n// One expiry of a chain, strike by strike: the call's side on the left, the put's mirrored on\n// the right, and the strike between them. Every figure a trader compares across the strip -\n// volume, open interest, volatility - carries a bar as well as a number, because a chain is\n// read by shape first and by value second.\n//\n// Two scales, and the difference matters. Volume and open interest are scaled per side, since\n// calls and puts trade in different sizes and comparing a call's volume against the busiest put\n// says nothing. Volatility is scaled across BOTH sides at once - a skew is exactly the\n// comparison between them, and two independent scales would flatten it.\n//\n// Greeks arrive one of two ways. A host that computes them sends them and the desk shows what\n// it was given; a host that sends volatility instead has them computed here, from the same\n// Black-Scholes the desktop uses. Neither is a fallback for the other going wrong - they are\n// two shapes of host, and which one a row came from is not the desk's business.\nimport { formatPrice, formatQty } from './formatters.js';\nimport { makeElement, makeIconButton, makePanelId, makePanelRoot } from './dom.js';\nimport { ControlTypes } from './control-types.js';\nimport { makeGridMenu } from './grid-menu.js';\nimport { TradingHost, assertHost } from './trading-host.js';\nimport { OptionTypes, greeks as computeGreeks, type Greeks } from './black-scholes.js';\nimport { DataGrid, GridColumn } from '@stocksharp/grids/source/data-grid';\n\n/// One contract's side of a strike.\nexport interface OptionSide {\n symbol?: string;\n bid?: number | null;\n ask?: number | null;\n last?: number | null;\n /// What the venue says it is worth, when it says.\n theoretical?: number | null;\n volume?: number | null;\n openInterest?: number | null;\n /// Implied volatility as a fraction, by the price it was solved from.\n ivBid?: number | null;\n ivAsk?: number | null;\n ivLast?: number | null;\n historicalVolatility?: number | null;\n /// Sent by a host that computes them. Left out, they are computed from `iv` below.\n greeks?: Greeks;\n}\n\n/// One strike, both sides of it.\nexport interface OptionStrike {\n strike: number;\n call: OptionSide;\n put: OptionSide;\n}\n\n/// What the chain is priced against. Without it the desk still shows quotes, and shows no\n/// greeks: they are not a property of the option alone.\nexport interface OptionChainContext {\n /// The underlying's price.\n assetPrice?: number | null;\n /// Years to expiry.\n timeToExpiry?: number | null;\n riskFree?: number;\n dividend?: number;\n}\n\nexport interface OptionDeskDeps {\n host: TradingHost;\n}\n\ninterface DeskRow extends OptionStrike {\n /// Bar scales, resolved once per refresh over the whole chain rather than per row.\n maxCallVolume: number;\n maxPutVolume: number;\n maxCallOpenInterest: number;\n maxPutOpenInterest: number;\n maxVolatility: number;\n /// What the option is worth if exercised now. Zero for a strike out of the money.\n callIntrinsic: number;\n putIntrinsic: number;\n}\n\nexport class OptionDeskWidget {\n static TYPE = ControlTypes.OptionDesk;\n\n rootEl: HTMLElement;\n el: HTMLElement | null;\n // `//` rather than `///` from here down \u2014 see the note in positions-widget.\n _host: TradingHost;\n _closeBtn: HTMLElement | null;\n _exportBtn: HTMLElement | null;\n _rows: DeskRow[];\n _context: OptionChainContext;\n _places: Record<keyof Greeks, number>;\n _grid: DataGrid<DeskRow> | null;\n\n static create(hostEl: HTMLElement, state: Record<string, unknown>, deps: OptionDeskDeps): OptionDeskWidget {\n const host = assertHost(deps?.host, 'OptionDeskWidget');\n const root = OptionDeskWidget._buildRoot(host);\n root.id = makePanelId(OptionDeskWidget.TYPE);\n hostEl.appendChild(root);\n return new OptionDeskWidget(root, state || {}, deps);\n }\n\n static _buildRoot(host: TradingHost): HTMLElement {\n const title = host.t('OptionDesk');\n return makePanelRoot('option-desk-panel', title, [\n makeElement('div', 'panel-header', {}, [\n makeElement('span', '', {}, [title]),\n makeIconButton('bt-icon-btn bt-icon-cancel panel-close-btn', host.t('ClosePanel'), 'bi-x', { type: 'button' }),\n ]),\n makeElement('div', 'panel-body panel-body-with-rail', {}, [\n makeElement('div', 'panel-body-content', {}, [\n makeElement('table', 'terminal-table option-desk-table', { role: 'table', 'aria-label': host.t('OptionChain') }, [\n makeElement('thead', '', {}, []),\n makeElement('tbody', 'option-desk-body', {}, []),\n ]),\n ]),\n makeElement('div', 'panel-rail', { role: 'toolbar', 'aria-label': host.t('OptionDeskActions') }, [\n makeIconButton('bt-icon-btn panel-export-btn', host.t('ExportToExcel'), 'bi-file-earmark-spreadsheet', {}),\n ]),\n ]),\n ]);\n }\n\n constructor(rootEl: HTMLElement, _state: Record<string, unknown>, deps: OptionDeskDeps) {\n this._host = assertHost(deps?.host, 'OptionDeskWidget');\n\n this.rootEl = rootEl;\n this.el = this.rootEl.querySelector('.option-desk-body');\n this._closeBtn = this.rootEl.querySelector('.panel-close-btn');\n this._exportBtn = this.rootEl.querySelector('.panel-export-btn');\n this._rows = [];\n this._context = {};\n this._places = greekScales([], {});\n\n this._closeBtn?.addEventListener('click', (e) => { e.preventDefault(); this._host.close(); });\n this._exportBtn?.addEventListener('click', (e) => { e.preventDefault(); this._export(); });\n\n const head = this.rootEl.querySelector('.option-desk-table thead');\n this._grid = head && this.el\n ? new DataGrid<DeskRow>({\n head: head as HTMLElement,\n body: this.el,\n columns: this._columns(),\n // By strike, ascending. A chain has exactly one order and it is not negotiable:\n // the strip is read as a ladder, and re-sorting it by any column destroys that.\n defaultSort: { col: 'strike', dir: 'asc' },\n rowKey: (r) => String(r.strike),\n emptyText: this._host.t('NoOptions'),\n rowClass: (r) => this._rowClass(r),\n contextMenu: makeGridMenu(this._host),\n selection: 'multi',\n })\n : null;\n\n // The greeks and the far volatilities are there when wanted and out of the way when not:\n // a desk is read across, and thirty-six columns at once cannot be.\n this._grid?.setState({\n hidden: [\n 'callRho', 'callTheta', 'callHv', 'callTheor',\n 'putRho', 'putTheta', 'putHv', 'putTheor',\n ],\n });\n\n this._host.register(this);\n }\n\n dispose(): void {\n this._grid?.destroy();\n this._host.unregister(this);\n try { this.rootEl.remove(); } catch { /* already detached */ }\n }\n\n /// Show this chain, priced against this context.\n ///\n /// Both together, always: a chain and the underlying it is priced off are one observation,\n /// and refreshing them separately shows greeks computed from a price that has moved.\n update(strikes: OptionStrike[], context: OptionChainContext = {}): void {\n this._context = context ?? {};\n this._rows = scaleChain(strikes ?? [], this._context);\n this._places = greekScales(this._rows, this._context);\n this._grid?.setRows(this._rows);\n }\n\n /// The rows as the desk holds them, scales and intrinsic values resolved.\n rows(): readonly DeskRow[] {\n return this._rows;\n }\n\n _export(): void {\n this._grid?.download('option-chain', this._host.t('OptionDesk'));\n }\n\n // Which side of the money this strike is on. A chain is read from the money outwards, and\n // the line between the two halves is what a reader finds first.\n _rowClass(row: DeskRow): string {\n const asset = this._context.assetPrice;\n if (asset === null || asset === undefined) return 'option-row';\n return `option-row ${row.strike < asset ? 'option-itm-call' : 'option-itm-put'}`;\n }\n\n _columns(): GridColumn<DeskRow>[] {\n const label = (key: string) => this._host.t(key);\n\n const side = (which: 'call' | 'put'): GridColumn<DeskRow>[] => {\n const prefix = which;\n const at = (r: DeskRow): OptionSide => r[which];\n const g = (r: DeskRow): Greeks | null => sideGreeks(r, which, this._context);\n\n return [\n { key: `${prefix}Rho`, header: label('Rho'), exportable: true, value: (r) => g(r)?.rho ?? null, render: (r) => decimals(g(r)?.rho, this._places.rho) },\n { key: `${prefix}Theta`, header: label('Theta'), exportable: true, value: (r) => g(r)?.theta ?? null, render: (r) => decimals(g(r)?.theta, this._places.theta) },\n { key: `${prefix}Vega`, header: label('Vega'), exportable: true, value: (r) => g(r)?.vega ?? null, render: (r) => decimals(g(r)?.vega, this._places.vega) },\n { key: `${prefix}Gamma`, header: label('Gamma'), exportable: true, value: (r) => g(r)?.gamma ?? null, render: (r) => decimals(g(r)?.gamma, this._places.gamma) },\n { key: `${prefix}Delta`, header: label('Delta'), exportable: true, value: (r) => g(r)?.delta ?? null, render: (r) => decimals(g(r)?.delta, this._places.delta) },\n { key: `${prefix}Bid`, header: label('Bid'), exportable: true, cellClass: () => 'option-bid', value: (r) => at(r).bid ?? null, render: (r) => price(at(r).bid) },\n { key: `${prefix}Ask`, header: label('Ask'), exportable: true, cellClass: () => 'option-ask', value: (r) => at(r).ask ?? null, render: (r) => price(at(r).ask) },\n { key: `${prefix}Theor`, header: label('TheorPrice'), exportable: true, value: (r) => at(r).theoretical ?? null, render: (r) => price(at(r).theoretical) },\n {\n key: `${prefix}Volume`, header: label('Volume'), exportable: true,\n value: (r) => at(r).volume ?? 0,\n render: (r) => this._bar(at(r).volume, which === 'call' ? r.maxCallVolume : r.maxPutVolume, which, formatQty(at(r).volume ?? 0)),\n exportValue: (r) => at(r).volume ?? 0,\n },\n {\n key: `${prefix}Oi`, header: label('OI'), exportable: true,\n value: (r) => at(r).openInterest ?? 0,\n render: (r) => this._bar(at(r).openInterest, which === 'call' ? r.maxCallOpenInterest : r.maxPutOpenInterest, which, formatQty(at(r).openInterest ?? 0)),\n exportValue: (r) => at(r).openInterest ?? 0,\n },\n { key: `${prefix}Symbol`, header: which === 'call' ? label('Call') : label('Put'), exportable: true, value: (r) => at(r).symbol ?? '' },\n {\n key: `${prefix}IvBid`, header: label('IVBid'), exportable: true,\n value: (r) => at(r).ivBid ?? null,\n render: (r) => this._bar(at(r).ivBid, r.maxVolatility, 'iv', percent(at(r).ivBid)),\n exportValue: (r) => at(r).ivBid ?? '',\n },\n {\n key: `${prefix}IvAsk`, header: label('IVAsk'), exportable: true,\n value: (r) => at(r).ivAsk ?? null,\n render: (r) => this._bar(at(r).ivAsk, r.maxVolatility, 'iv', percent(at(r).ivAsk)),\n exportValue: (r) => at(r).ivAsk ?? '',\n },\n {\n key: `${prefix}IvLast`, header: label('IVLast'), exportable: true,\n value: (r) => at(r).ivLast ?? null,\n render: (r) => this._bar(at(r).ivLast, r.maxVolatility, 'iv', percent(at(r).ivLast)),\n exportValue: (r) => at(r).ivLast ?? '',\n },\n {\n key: `${prefix}Hv`, header: label('HV'), exportable: true,\n value: (r) => at(r).historicalVolatility ?? null,\n render: (r) => this._bar(at(r).historicalVolatility, r.maxVolatility, 'iv', percent(at(r).historicalVolatility)),\n exportValue: (r) => at(r).historicalVolatility ?? '',\n },\n ];\n };\n\n // Declared outward from the strike: volatilities and quotes against the middle, where\n // the two sides are compared, and the greeks out at the edges. The put side is that\n // same order reflected, which is what makes the chain a mirror rather than a repeat.\n const puts = side('put').slice().reverse();\n\n return [\n ...side('call'),\n {\n key: 'strike',\n header: label('Strike'),\n exportable: true,\n cellClass: () => 'option-strike',\n value: (r) => r.strike,\n render: (r) => price(r.strike),\n },\n {\n key: 'intrinsic',\n header: label('IntrinsicValue'),\n exportable: true,\n cellClass: () => 'option-intrinsic',\n // Whichever side is in the money at this strike; the other is worth nothing to\n // exercise, and showing both would be one number and one zero on every row.\n value: (r) => Math.max(r.callIntrinsic, r.putIntrinsic),\n render: (r) => price(Math.max(r.callIntrinsic, r.putIntrinsic)),\n },\n ...puts,\n ];\n }\n\n // A figure with its share of the strip behind it. The bar is a width, not a drawing: it\n // scales with the cell, it prints, and it needs no canvas per row.\n _bar(value: number | null | undefined, max: number, kind: 'call' | 'put' | 'iv', text: string): string | Node {\n if (value === null || value === undefined || !isFinite(value) || value <= 0) return text;\n\n const cell = document.createElement('span');\n cell.className = 'option-bar-cell';\n\n const bar = document.createElement('span');\n bar.className = `option-bar option-bar-${kind}`;\n bar.style.width = `${Math.min(100, (value / (max || 1)) * 100).toFixed(1)}%`;\n cell.appendChild(bar);\n\n const label = document.createElement('span');\n label.className = 'option-bar-text';\n label.textContent = text;\n cell.appendChild(label);\n\n return cell;\n }\n}\n\n/// Resolve the bar scales and the intrinsic values over a whole chain.\n///\n/// One pass over every strike, not one per row: a bar means \"this much of the busiest strike\",\n/// and a scale computed per row would make every row its own maximum.\nexport function scaleChain(strikes: readonly OptionStrike[], context: OptionChainContext): DeskRow[] {\n const asset = context.assetPrice ?? null;\n\n const maxOf = (pick: (s: OptionSide) => number | null | undefined, sides: (r: OptionStrike) => OptionSide[]): number =>\n Math.max(0, ...strikes.flatMap(r => sides(r).map(s => pick(s) ?? 0)).filter(v => isFinite(v)));\n\n const maxCallVolume = maxOf(s => s.volume, r => [r.call]);\n const maxPutVolume = maxOf(s => s.volume, r => [r.put]);\n const maxCallOpenInterest = maxOf(s => s.openInterest, r => [r.call]);\n const maxPutOpenInterest = maxOf(s => s.openInterest, r => [r.put]);\n\n // One scale across both sides: the comparison a skew IS, is the one between them.\n const maxVolatility = Math.max(\n maxOf(s => s.ivBid, r => [r.call, r.put]),\n maxOf(s => s.ivAsk, r => [r.call, r.put]),\n maxOf(s => s.ivLast, r => [r.call, r.put]),\n maxOf(s => s.historicalVolatility, r => [r.call, r.put]),\n );\n\n return strikes.map(row => ({\n ...row,\n maxCallVolume,\n maxPutVolume,\n maxCallOpenInterest,\n maxPutOpenInterest,\n maxVolatility,\n callIntrinsic: asset === null ? 0 : Math.max(0, asset - row.strike),\n putIntrinsic: asset === null ? 0 : Math.max(0, row.strike - asset),\n }));\n}\n\n/// The greeks for one side: the host's if it sent them, otherwise computed from its volatility.\n///\n/// Null when neither is possible - no volatility, or nothing to price against. A blank is the\n/// honest answer there; a zero would read as a measured delta of nothing.\nexport function sideGreeks(row: OptionStrike, which: 'call' | 'put', context: OptionChainContext): Greeks | null {\n const side = row[which];\n if (side.greeks !== undefined) return side.greeks;\n\n const deviation = side.ivLast ?? side.ivBid ?? side.ivAsk ?? side.historicalVolatility ?? null;\n const assetPrice = context.assetPrice ?? null;\n const timeToExpiry = context.timeToExpiry ?? null;\n\n if (deviation === null || assetPrice === null || timeToExpiry === null) return null;\n\n return computeGreeks(which === 'call' ? OptionTypes.Call : OptionTypes.Put, {\n assetPrice,\n strike: row.strike,\n timeToExpiry,\n riskFree: context.riskFree ?? 0,\n dividend: context.dividend ?? 0,\n deviation,\n });\n}\n\n/// Every greek this desk can show. Order is the order a chain is read in, outwards from delta.\nconst GREEK_KEYS = ['delta', 'gamma', 'vega', 'theta', 'rho'] as const;\n\n/// Three significant digits for the smallest figure in a column, within bounds a column can hold.\nconst GREEK_DIGITS = 4;\nconst GREEK_MIN_PLACES = 2;\nconst GREEK_MAX_PLACES = 8;\n\n/// How many decimal places a column of greeks needs.\n///\n/// One count cannot serve every greek: a delta is about one, while a gamma on an underlying at\n/// 60000 is about 0.00003, and the four places that suit the first show every strike of the\n/// second as 0.0000 - a column that is present, aligned, and says nothing. So the count comes\n/// from the numbers, sized to the smallest of them.\n///\n/// One count for the whole column, not per cell: a column of figures is read down its decimal\n/// point, and a ragged one is read a cell at a time.\nexport function greekPlaces(values: readonly (number | null | undefined)[]): number {\n const scale = Math.min(...values\n .filter((v): v is number => typeof v === 'number' && isFinite(v) && v !== 0)\n .map(Math.abs));\n\n // Nothing measurable: a zero column is a zero column at any width.\n if (!isFinite(scale)) return GREEK_MIN_PLACES;\n\n const places = GREEK_DIGITS - 1 - Math.floor(Math.log10(scale));\n return Math.min(GREEK_MAX_PLACES, Math.max(GREEK_MIN_PLACES, places));\n}\n\n/// The places for each greek, measured across both sides of the chain at once.\n///\n/// Across both sides deliberately: the desk mirrors, and a gamma written to eight places on the\n/// left and five on the right stops being a mirror.\nexport function greekScales(rows: readonly OptionStrike[], context: OptionChainContext): Record<keyof Greeks, number> {\n const all = rows\n .flatMap(r => [sideGreeks(r, OptionTypes.Call, context), sideGreeks(r, OptionTypes.Put, context)])\n .filter((g): g is Greeks => g !== null);\n\n const places = {} as Record<keyof Greeks, number>;\n for (const key of GREEK_KEYS) places[key] = greekPlaces(all.map(g => g[key]));\n return places;\n}\n\nfunction decimals(value: number | null | undefined, places: number): string {\n if (value === null || value === undefined || !isFinite(value)) return '';\n return value.toFixed(places);\n}\n\nfunction price(value: number | null | undefined): string {\n if (value === null || value === undefined || !isFinite(value)) return '';\n return formatPrice(value);\n}\n\n/// A volatility, as the points a desk quotes it in rather than the fraction it is held as.\nfunction percent(value: number | null | undefined): string {\n if (value === null || value === undefined || !isFinite(value)) return '';\n return `${(value * 100).toFixed(2)}%`;\n}\n", "// Strategies dashboard \u2014 multi-instance.\n//\n// A row per strategy: what state it is in, what it is trading, what it has done, and the\n// controls to start it, stop it and flatten it. This is the panel a browser client exists for -\n// the strategies run on a server and the page is how someone reaches them.\n//\n// Three deliberate departures from the desktop version, each because copying it would carry a\n// defect across. Its Settings button is wired to a disabled command in both shipped hosts, so a\n// viewer sees a permanently dead control - that button is not here, and the rest are declared\n// through deps, so a host that cannot do a thing does not show a button for it. Its P&L\n// sparkline is filled green unconditionally, which paints a losing run in the winning colour;\n// here the curve is coloured by where the run ended. And its P&L change is measured from the\n// first non-zero P&L and never re-anchored, so a restarted strategy keeps measuring from before\n// the restart; here the consumer states the number it means.\nimport { formatPnl, formatQty } from './formatters.js';\nimport { makeElement, makeIconButton, makePanelId, makePanelRoot } from './dom.js';\nimport { ControlTypes } from './control-types.js';\nimport { makeGridMenu } from './grid-menu.js';\nimport { TradingHost, assertHost } from './trading-host.js';\nimport { drawPnlCurve, pnlCurve, type PnlPoint } from './pnl-curve.js';\nimport { DataGrid, GridColumn } from '@stocksharp/grids/source/data-grid';\n\n/// The states a strategy moves through, as the wire spells them.\nexport const StrategyStates = {\n Stopped: 'stopped',\n Starting: 'starting',\n Started: 'started',\n Stopping: 'stopping',\n} as const;\n\nexport type StrategyState = typeof StrategyStates[keyof typeof StrategyStates];\n\n/// One strategy, as the dashboard reads it.\nexport interface StrategyRow {\n id: string;\n name: string;\n state: StrategyState | string;\n /// Whether it is both formed and connected. Two conditions the desktop ANDs into one\n /// indicator, and the consumer does the same here: a half-online strategy is not online.\n online?: boolean;\n /// What it is allowed to do. The one editable cell.\n tradingMode?: string;\n portfolio?: string;\n security?: string;\n position?: number | null;\n ordersCount?: number | null;\n tradesCount?: number | null;\n /// Change against whatever the consumer anchors to.\n pnlChange?: number | null;\n realized?: number | null;\n unrealized?: number | null;\n /// The cumulative P&L curve, sampled at the points it changed.\n pnl?: PnlPoint[];\n /// The last thing that went wrong, if anything has.\n error?: string;\n}\n\n/// What a host can let someone do to a strategy. Every one optional and independently so: a\n/// read-only dashboard passes none and shows no buttons, and a host that can start and stop but\n/// not open shows exactly those two.\nexport interface StrategiesActions {\n start?(id: string): void;\n stop?(id: string): void;\n closePosition?(id: string): void;\n openStrategy?(id: string): void;\n riskRules?(id: string): void;\n setTradingMode?(id: string, mode: string): void;\n}\n\nexport interface StrategiesDeps extends StrategiesActions {\n host: TradingHost;\n /// The trading modes a strategy can be put in, in the order they should be offered.\n ///\n /// Each is worded through `t()`, so these strings are also translation keys - and they are\n /// the host's own, not the package's: they name what this host can put a run in, so they are\n /// absent from `translation-keys.json` and the host answers for them itself.\n tradingModes?: readonly string[];\n}\n\nconst SPARK_WIDTH = 140;\nconst SPARK_HEIGHT = 26;\n\nexport class StrategiesWidget {\n static TYPE = ControlTypes.Strategies;\n\n rootEl: HTMLElement;\n el: HTMLElement | null;\n // `//` rather than `///` from here down \u2014 see the note in positions-widget.\n _host: TradingHost;\n _deps: StrategiesDeps;\n _closeBtn: HTMLElement | null;\n _exportBtn: HTMLElement | null;\n _rows: StrategyRow[];\n _grid: DataGrid<StrategyRow> | null;\n\n static create(hostEl: HTMLElement, state: Record<string, unknown>, deps: StrategiesDeps): StrategiesWidget {\n const host = assertHost(deps?.host, 'StrategiesWidget');\n const root = StrategiesWidget._buildRoot(host);\n root.id = makePanelId(StrategiesWidget.TYPE);\n hostEl.appendChild(root);\n return new StrategiesWidget(root, state || {}, deps);\n }\n\n static _buildRoot(host: TradingHost): HTMLElement {\n const title = host.t('Strategies');\n return makePanelRoot('strategies-panel', title, [\n makeElement('div', 'panel-header', {}, [\n makeElement('span', '', {}, [title]),\n makeIconButton('bt-icon-btn bt-icon-cancel panel-close-btn', host.t('ClosePanel'), 'bi-x', { type: 'button' }),\n ]),\n makeElement('div', 'panel-body panel-body-with-rail', {}, [\n makeElement('div', 'panel-body-content', {}, [\n makeElement('table', 'terminal-table strategies-table', { role: 'table', 'aria-label': host.t('StrategiesList') }, [\n makeElement('thead', '', {}, []),\n makeElement('tbody', 'strategies-body', {}, []),\n ]),\n ]),\n makeElement('div', 'panel-rail', { role: 'toolbar', 'aria-label': host.t('StrategiesActions') }, [\n makeIconButton('bt-icon-btn panel-export-btn', host.t('ExportToExcel'), 'bi-file-earmark-spreadsheet', {}),\n ]),\n ]),\n ]);\n }\n\n constructor(rootEl: HTMLElement, _state: Record<string, unknown>, deps: StrategiesDeps) {\n this._host = assertHost(deps?.host, 'StrategiesWidget');\n this._deps = deps;\n\n this.rootEl = rootEl;\n this.el = this.rootEl.querySelector('.strategies-body');\n this._closeBtn = this.rootEl.querySelector('.panel-close-btn');\n this._exportBtn = this.rootEl.querySelector('.panel-export-btn');\n this._rows = [];\n\n this._closeBtn?.addEventListener('click', (e) => { e.preventDefault(); this._host.close(); });\n this._exportBtn?.addEventListener('click', (e) => { e.preventDefault(); this._export(); });\n\n const head = this.rootEl.querySelector('.strategies-table thead');\n this._grid = head && this.el\n ? new DataGrid<StrategyRow>({\n head: head as HTMLElement,\n body: this.el,\n columns: this._columns(),\n // By name: a dashboard is a list someone reads down looking for one strategy,\n // and a list that reorders itself as P&L moves cannot be read that way.\n defaultSort: { col: 'name', dir: 'asc' },\n rowKey: (s) => String(s.id),\n emptyText: this._host.t('NoStrategies'),\n rowClass: (s) => `strategy-row strategy-${s.state}${s.error ? ' strategy-failed' : ''}`,\n contextMenu: makeGridMenu(this._host),\n selection: 'multi',\n })\n : null;\n\n this._host.register(this);\n }\n\n dispose(): void {\n this._grid?.destroy();\n this._host.unregister(this);\n try { this.rootEl.remove(); } catch { /* already detached */ }\n }\n\n /// Show these strategies. The whole set: one that is gone from it has been removed, and\n /// leaving its row behind would offer a Stop for something that no longer runs.\n update(rows: StrategyRow[]): void {\n this._rows = rows || [];\n this._grid?.setRows(this._rows);\n }\n\n _export(): void {\n this._grid?.download('strategies', this._host.t('Strategies'));\n }\n\n _columns(): GridColumn<StrategyRow>[] {\n const label = (key: string) => this._host.t(key);\n const presentation = this._host.presentation;\n\n return [\n {\n key: 'state',\n header: label('State'),\n exportable: true,\n value: (s) => s.state,\n render: (s) => this._stateCell(s),\n cellClass: (s) => `strategy-state strategy-state-${s.state}`,\n exportValue: (s) => s.state,\n },\n {\n key: 'actions',\n header: label('Actions'),\n headerHidden: true,\n exportable: false,\n cellClass: () => 'strategy-actions',\n render: (s) => this._actionCell(s),\n },\n {\n key: 'online',\n header: label('Online'),\n exportable: true,\n value: (s) => (s.online ? 1 : 0),\n render: (s) => (s.online ? '\u25CF' : '\u25CB'),\n cellClass: (s) => (s.online ? 'strategy-online is-on' : 'strategy-online'),\n exportValue: (s) => (s.online ? label('Yes') : label('No')),\n },\n {\n key: 'tradingMode',\n header: label('Trading'),\n exportable: true,\n value: (s) => s.tradingMode ?? '',\n render: (s) => this._tradingCell(s),\n exportValue: (s) => s.tradingMode ?? '',\n },\n { key: 'name', header: label('Name'), exportable: true, value: (s) => s.name },\n { key: 'portfolio', header: label('Portfolio'), exportable: true, value: (s) => s.portfolio ?? '' },\n { key: 'security', header: label('Sym'), exportable: true, value: (s) => s.security ?? '' },\n {\n key: 'position',\n header: label('Position'),\n exportable: true,\n value: (s) => s.position ?? 0,\n render: (s) => this._positionCell(s),\n cellClass: (s) => `strategy-position ${presentation.pnlClass(s.position ?? 0)}`,\n exportValue: (s) => s.position ?? 0,\n },\n { key: 'orders', header: label('OrderCount'), exportable: true, value: (s) => s.ordersCount ?? 0 },\n { key: 'trades', header: label('NumOfTrades'), exportable: true, value: (s) => s.tradesCount ?? 0 },\n {\n key: 'pnlChange',\n header: label('PnLChange'),\n exportable: true,\n value: (s) => s.pnlChange ?? 0,\n render: (s) => `${direction(s.pnlChange ?? 0)} ${formatPnl(s.pnlChange ?? 0)}`,\n cellClass: (s) => presentation.pnlClass(s.pnlChange ?? 0),\n exportValue: (s) => s.pnlChange ?? 0,\n },\n {\n key: 'pnlChart',\n header: label('PnLChart'),\n exportable: false,\n cellClass: () => 'strategy-spark',\n render: (s) => this._sparkline(s),\n },\n {\n key: 'realized',\n header: label('RealizedProfit'),\n exportable: true,\n value: (s) => s.realized ?? 0,\n render: (s) => formatPnl(s.realized ?? 0),\n cellClass: (s) => presentation.pnlClass(s.realized ?? 0),\n exportValue: (s) => s.realized ?? 0,\n },\n {\n key: 'unrealized',\n header: label('UnrealizedProfit'),\n exportable: true,\n value: (s) => s.unrealized ?? 0,\n render: (s) => formatPnl(s.unrealized ?? 0),\n cellClass: (s) => presentation.pnlClass(s.unrealized ?? 0),\n exportValue: (s) => s.unrealized ?? 0,\n },\n {\n key: 'error',\n header: label('Error'),\n exportable: true,\n cellClass: () => 'strategy-error',\n value: (s) => s.error ?? '',\n },\n ];\n }\n\n // A dot and the state's own word. The dot is what is read across a list of twenty; the word\n // is what tells Starting from Started, which a colour alone cannot.\n _stateCell(row: StrategyRow): string | Node {\n const cell = document.createDocumentFragment();\n\n const failed = row.error !== undefined && row.error !== null && String(row.error).length > 0;\n\n const dot = document.createElement('span');\n dot.className = 'strategy-dot';\n dot.textContent = '\u25CF';\n // The reason sits on the dot as well as on the word: the dot is what a reader looks at\n // first, and a tooltip only the word carries is one nobody finds.\n if (failed) dot.title = String(row.error);\n cell.appendChild(dot);\n\n const text = document.createElement('span');\n text.className = 'strategy-state-text';\n // A run that stopped because something went wrong says so, rather than reading exactly\n // like one the user stopped and differing only by the colour of the dot. The colour is\n // the same statement made a second time, for a board too narrow to show the error\n // column; the word is what a reader gets first.\n text.textContent = failed && row.state === StrategyStates.Stopped\n ? this._host.t('Error')\n : stateText(this._host, row.state);\n if (failed) text.title = String(row.error);\n cell.appendChild(text);\n\n return cell;\n }\n\n // Only the buttons this host can actually carry out, and only where the state allows them.\n // A strategy that is already running has nothing to start.\n _actionCell(row: StrategyRow): Node {\n const cell = document.createDocumentFragment();\n const deps = this._deps;\n\n const add = (\n action: ((id: string) => void) | undefined,\n when: boolean,\n title: string,\n icon: string,\n cls: string,\n ): void => {\n if (action === undefined) return;\n\n const button = makeIconButton(`bt-icon-btn ${cls}`, title, icon, { type: 'button' }) as HTMLButtonElement;\n if (!when) button.disabled = true;\n else button.addEventListener('click', () => action.call(deps, row.id));\n cell.appendChild(button);\n };\n\n add(deps.start, row.state === StrategyStates.Stopped, this._host.t('Start'), 'bi-play-fill', 'strategy-start-btn');\n add(deps.stop, row.state === StrategyStates.Started, this._host.t('Stop'), 'bi-stop-fill', 'strategy-stop-btn');\n add(deps.riskRules, true, this._host.t('RiskManagement'), 'bi-shield-exclamation', 'strategy-risk-btn');\n add(deps.openStrategy, true, this._host.t('OpenStrategy'), 'bi-box-arrow-up-right', 'strategy-open-btn');\n\n return cell;\n }\n\n // The position, and next to it the one gesture that acts on it. Flattening is only\n // meaningful while the strategy runs and only when it holds something.\n _positionCell(row: StrategyRow): string | Node {\n const position = row.position ?? 0;\n const text = formatQty(position);\n if (this._deps.closePosition === undefined) return text;\n\n const cell = document.createDocumentFragment();\n // The figure sits in a box of a fixed least width, so the button beside it stays put\n // when a minus sign appears or a digit is dropped. Without it the whole column shifts\n // every time a position crosses zero.\n const value = makeElement('span', 'strategy-position-value', {}, [text]);\n cell.appendChild(value);\n\n const button = makeIconButton('bt-icon-btn strategy-flatten-btn', this._host.t('ClosePosition'), 'bi-x-octagon', { type: 'button' }) as HTMLButtonElement;\n if (row.state !== StrategyStates.Started || position === 0) button.disabled = true;\n else button.addEventListener('click', () => this._deps.closePosition?.(row.id));\n cell.appendChild(button);\n\n return cell;\n }\n\n // The one editable cell, and only when the host can carry the change out.\n _tradingCell(row: StrategyRow): string | Node {\n const modes = this._deps.tradingModes ?? [];\n if (this._deps.setTradingMode === undefined || modes.length === 0) return row.tradingMode ?? '';\n\n const select = document.createElement('select');\n select.className = 'form-control form-control-sm strategy-mode';\n // Only on a stopped run, the way start is only on a stopped one and stop only on a\n // started one: the mode is what the run will be started with, not a lever to pull while\n // it is trading.\n select.disabled = row.state !== StrategyStates.Stopped;\n for (const mode of modes) {\n const option = document.createElement('option');\n option.value = mode;\n option.textContent = this._host.t(mode);\n if (mode === row.tradingMode) option.selected = true;\n select.appendChild(option);\n }\n if (!select.disabled)\n select.addEventListener('change', () => this._deps.setTradingMode?.(row.id, select.value));\n return select;\n }\n\n // The run's own curve, at the size a cell has. Coloured by where it ended, which is the\n // whole reason a glance at the column tells one strategy from another.\n _sparkline(row: StrategyRow): string | Node {\n const box = { width: SPARK_WIDTH, height: SPARK_HEIGHT, padX: 1, padY: 2 };\n const curve = pnlCurve(row.pnl ?? [], box);\n if (curve === null) return '';\n\n const canvas = document.createElement('canvas');\n canvas.className = 'strategy-spark-canvas';\n\n // Two sizes, and they are not the same one. The backing store is in device pixels, so\n // a curve on a 2x screen is drawn at 2x; the CSS box stays the size the column has.\n // Sized only in CSS pixels, as this was, every stroke lands on half the pixels it\n // should and the whole sparkline reads as a smudge.\n const ratio = typeof window === 'undefined' ? 1 : (window.devicePixelRatio || 1);\n canvas.width = Math.round(box.width * ratio);\n canvas.height = Math.round(box.height * ratio);\n canvas.style.width = `${box.width}px`;\n canvas.style.height = `${box.height}px`;\n\n const ctx = canvas.getContext('2d');\n if (ctx !== null) {\n const palette = this._host.presentation.canvasPalette();\n const scaled = { width: canvas.width, height: canvas.height, padX: box.padX * ratio, padY: box.padY * ratio };\n drawPnlCurve(ctx, pnlCurve(row.pnl ?? [], scaled) ?? curve, scaled, {\n up: palette.up,\n down: palette.down,\n baseline: palette.grid,\n lineWidth: 1.5 * ratio,\n fillOpacity: 0.35,\n });\n }\n\n return canvas;\n }\n}\n\n/// What a state is called here.\n///\n/// Each key is a literal at the point it is asked for, rather than one value picked and then\n/// translated: `translation-keys.json` is generated by scanning the sources for exactly that\n/// shape, so a key assembled from a value never reaches the list, no host learns to translate\n/// it, and it arrives at the user as itself.\nfunction stateText(host: TradingHost, state: StrategyState | string): string {\n switch (state) {\n case StrategyStates.Started: return host.t('Started');\n case StrategyStates.Starting: return host.t('Starting');\n case StrategyStates.Stopping: return host.t('Stopping');\n default: return host.t('Stopped');\n }\n}\n\n/// Which way a figure moved, as one character. Blank for no change: an arrow that always points\n/// somewhere says a strategy is moving when it is not.\nfunction direction(value: number): string {\n if (value > 0) return '\u25B2';\n if (value < 0) return '\u25BC';\n return '';\n}\n\n", "// The log monitor's shape, as pure functions: how sources nest, and which messages a view of\n// them keeps.\n//\n// A log source announces its parent, not its children, and it can announce itself before its\n// parent has said anything \u2014 so the tree is assembled from whatever has arrived rather than\n// walked from a root that may not exist yet. Keeping that here is what makes it checkable: a\n// cycle, an orphan and an unnamed source are all things a live feed produces, and none of them\n// should be discovered as a hung page.\n\n/// The levels a log message carries. The names are the wire's; the letter is what a narrow\n/// column shows.\nexport const LogLevels = {\n Error: 'error',\n Warning: 'warning',\n Info: 'info',\n Debug: 'debug',\n Verbose: 'verbose',\n} as const;\n\nexport type LogLevel = typeof LogLevels[keyof typeof LogLevels];\n\n/// One source of log messages, as it announces itself.\nexport interface LogSourceNode {\n id: string;\n name: string;\n /// The source this one runs under, when it has one.\n parentId?: string | null;\n}\n\n/// A source placed in the tree.\nexport interface LogTreeNode extends LogSourceNode {\n depth: number;\n children: LogTreeNode[];\n}\n\n/// One line of a log.\nexport interface LogMessageRow {\n id: number | string;\n /// When it was written, in whatever the consumer counts in.\n time: number | string;\n level: LogLevel | string;\n /// Which source wrote it. The tree filters on this.\n sourceId: string;\n /// That source's name, when the consumer sends it rather than looking it up.\n source?: string;\n message: string;\n}\n\n/// Assemble the sources into a forest, roots first, each node carrying its depth.\n///\n/// A source whose parent is not among these is a root: it is the only honest place for it, and\n/// dropping it would silently lose a whole strategy's log because its connector had not spoken\n/// yet. A cycle is broken the same way, by rooting the first node of it \u2014 a wrong tree beats a\n/// walk that never ends.\nexport function buildLogTree(sources: readonly LogSourceNode[]): LogTreeNode[] {\n const byId = new Map<string, LogTreeNode>();\n for (const s of sources)\n byId.set(s.id, { ...s, name: s.name && s.name.length > 0 ? s.name : '\u2014', depth: 0, children: [] });\n\n const roots: LogTreeNode[] = [];\n\n for (const node of byId.values()) {\n const parent = node.parentId != null ? byId.get(node.parentId) : undefined;\n if (parent !== undefined && parent !== node && !descends(byId, parent, node.id)) parent.children.push(node);\n else roots.push(node);\n }\n\n const setDepth = (node: LogTreeNode, depth: number): void => {\n node.depth = depth;\n for (const child of node.children) setDepth(child, depth + 1);\n };\n for (const root of roots) setDepth(root, 0);\n\n return roots;\n}\n\n/// Whether `node` already sits under `id`, which is how a cycle is spotted before it is made.\nfunction descends(byId: Map<string, LogTreeNode>, node: LogTreeNode, id: string): boolean {\n const seen = new Set<string>();\n let at: LogTreeNode | undefined = node;\n while (at !== undefined && !seen.has(at.id)) {\n if (at.id === id) return true;\n seen.add(at.id);\n at = at.parentId != null ? byId.get(at.parentId) : undefined;\n }\n return false;\n}\n\n/// The ids a selected node covers: itself and everything under it, or null for no selection.\n///\n/// Null rather than every id, because \"everything\" and \"this node, which happens to be the\n/// root\" are different questions and only one of them narrows when a new source appears.\nexport function subtreeOf(sources: readonly LogSourceNode[], selectedId: string | null): Set<string> | null {\n if (selectedId === null) return null;\n\n const children = new Map<string, string[]>();\n for (const s of sources) {\n if (s.parentId == null) continue;\n const siblings = children.get(s.parentId);\n if (siblings === undefined) children.set(s.parentId, [s.id]);\n else siblings.push(s.id);\n }\n\n const out = new Set<string>();\n const walk = (id: string): void => {\n if (out.has(id)) return;\n out.add(id);\n for (const child of children.get(id) ?? []) walk(child);\n };\n walk(selectedId);\n return out;\n}\n\n/// What a view of the log is showing.\nexport interface LogView {\n levels: ReadonlySet<string>;\n text: string;\n /// Source ids to keep, or null for all of them.\n sources: ReadonlySet<string> | null;\n}\n\n/// Whether this message belongs in that view. Every filter applies at once.\nexport function keepLog(message: LogMessageRow, view: LogView): boolean {\n if (!view.levels.has(message.level)) return false;\n if (view.sources !== null && !view.sources.has(message.sourceId)) return false;\n\n const text = view.text.trim();\n if (text.length === 0) return true;\n\n return (message.message ?? '').toLowerCase().includes(text.toLowerCase());\n}\n", "// Log monitor \u2014 multi-instance.\n//\n// Two panes: the tree of everything that writes a log on the left, and on the right what the\n// selected source and everything under it has written. The desktop version copies each message\n// into every ancestor's list, so one line is held as many times as it has ancestors and its own\n// counter reports copies rather than messages. Here a message is held once and carries the id\n// of the source that wrote it; selecting a node filters by its subtree.\n//\n// A live log has no end, so this one has a cap. The desktop's grows for the lifetime of the\n// window - nothing trims it - which a browser tab cannot afford.\nimport { makeElement, makeIconButton, makePanelId, makePanelRoot } from './dom.js';\nimport { ControlTypes } from './control-types.js';\nimport { makeGridMenu } from './grid-menu.js';\nimport { TradingHost, assertHost } from './trading-host.js';\nimport {\n LogLevels, buildLogTree, keepLog, subtreeOf,\n type LogLevel, type LogMessageRow, type LogSourceNode, type LogTreeNode,\n} from './log-tree.js';\nimport { DataGrid, GridColumn } from '@stocksharp/grids/source/data-grid';\n\n/// What the monitor needs beyond the host port. Nothing: a log is written to, not acted on.\nexport interface LogMonitorDeps {\n host: TradingHost;\n /// How many messages to keep. Older ones fall off the front. Defaults to 5000 - enough to\n /// scroll back through a session, small enough that a chatty connector cannot fill a tab.\n maxMessages?: number;\n}\n\nconst DEFAULT_MAX = 5_000;\n\n/// The letter a level shows in the narrow column, and the class that colours the row.\n///\n/// Info gets a letter here. The desktop leaves its cell blank - there is simply no trigger for\n/// it among the four that exist - so the commonest level is the one with no marking at all,\n/// which is an omission rather than a decision.\nconst LEVEL_LETTER: Record<string, string> = {\n [LogLevels.Error]: 'E',\n [LogLevels.Warning]: 'W',\n [LogLevels.Info]: 'I',\n [LogLevels.Debug]: 'D',\n [LogLevels.Verbose]: 'V',\n};\n\nconst ALL_LEVELS: LogLevel[] = [LogLevels.Error, LogLevels.Warning, LogLevels.Info, LogLevels.Debug, LogLevels.Verbose];\n\nexport class LogMonitorWidget {\n static TYPE = ControlTypes.LogMonitor;\n\n rootEl: HTMLElement;\n el: HTMLElement | null;\n // `//` rather than `///` from here down \u2014 see the note in positions-widget.\n _host: TradingHost;\n _max: number;\n _closeBtn: HTMLElement | null;\n _clearBtn: HTMLElement | null;\n _exportBtn: HTMLElement | null;\n _treeEl: HTMLElement | null;\n _filterEl: HTMLInputElement | null;\n _sources: LogSourceNode[];\n _messages: LogMessageRow[];\n _levels: Set<string>;\n _text: string;\n _selected: string | null;\n _grid: DataGrid<LogMessageRow> | null;\n\n static create(hostEl: HTMLElement, state: Record<string, unknown>, deps: LogMonitorDeps): LogMonitorWidget {\n const host = assertHost(deps?.host, 'LogMonitorWidget');\n const root = LogMonitorWidget._buildRoot(host);\n root.id = makePanelId(LogMonitorWidget.TYPE);\n hostEl.appendChild(root);\n return new LogMonitorWidget(root, state || {}, deps);\n }\n\n // The panel's markup: a source tree, a toolbar of level toggles and a text box, and the\n // message table. The level toggles are buttons rather than checkboxes because they are read\n // as a row of states, and each carries its letter so the toolbar and the column agree.\n static _buildRoot(host: TradingHost): HTMLElement {\n const title = host.t('LogMonitor');\n return makePanelRoot('log-monitor-panel', title, [\n makeElement('div', 'panel-header', {}, [\n makeElement('span', '', {}, [title]),\n makeIconButton('bt-icon-btn bt-icon-cancel panel-close-btn', host.t('ClosePanel'), 'bi-x', { type: 'button' }),\n ]),\n makeElement('div', 'panel-body log-monitor-body', {}, [\n makeElement('div', 'log-sources', { role: 'tree', 'aria-label': host.t('LogSources') }, []),\n makeElement('div', 'log-messages', {}, [\n makeElement('div', 'log-toolbar', { role: 'toolbar', 'aria-label': host.t('LogMonitorActions') }, [\n ...ALL_LEVELS.map(level => makeElement('button', `log-level-toggle log-level-${level} is-on`, {\n type: 'button',\n 'data-level': level,\n 'aria-pressed': 'true',\n title: levelTitle(host, level),\n }, [LEVEL_LETTER[level]])),\n makeElement('input', 'form-control form-control-sm log-filter', {\n type: 'search',\n placeholder: host.t('Filter'),\n 'aria-label': host.t('Filter'),\n }, []),\n makeIconButton('bt-icon-btn log-clear-btn', host.t('ClearItems'), 'bi-eraser', {}),\n makeIconButton('bt-icon-btn panel-export-btn', host.t('ExportToExcel'), 'bi-file-earmark-spreadsheet', {}),\n ]),\n // The table sits in a box of its own rather than being a flex child: a table\n // told to fill a column stretches its rows to do it, so a log holding one\n // line drew that line a panel tall - and with the toolbar and the table as\n // the only two children there was nowhere for a long log to scroll.\n makeElement('div', 'log-table-scroll', {}, [\n makeElement('table', 'terminal-table log-table', { role: 'table', 'aria-label': host.t('LogMessages') }, [\n makeElement('thead', '', {}, []),\n makeElement('tbody', 'log-body', {}, []),\n ]),\n ]),\n ]),\n ]),\n ]);\n }\n\n constructor(rootEl: HTMLElement, _state: Record<string, unknown>, deps: LogMonitorDeps) {\n this._host = assertHost(deps?.host, 'LogMonitorWidget');\n this._max = deps?.maxMessages && deps.maxMessages > 0 ? deps.maxMessages : DEFAULT_MAX;\n\n this.rootEl = rootEl;\n this.el = this.rootEl.querySelector('.log-body');\n this._treeEl = this.rootEl.querySelector('.log-sources');\n this._filterEl = this.rootEl.querySelector('.log-filter');\n this._closeBtn = this.rootEl.querySelector('.panel-close-btn');\n this._clearBtn = this.rootEl.querySelector('.log-clear-btn');\n this._exportBtn = this.rootEl.querySelector('.panel-export-btn');\n\n this._sources = [];\n this._messages = [];\n this._levels = new Set<string>(ALL_LEVELS);\n this._text = '';\n this._selected = null;\n\n this._closeBtn?.addEventListener('click', (e) => { e.preventDefault(); this._host.close(); });\n this._clearBtn?.addEventListener('click', (e) => { e.preventDefault(); this.clear(); });\n this._exportBtn?.addEventListener('click', (e) => { e.preventDefault(); this._export(); });\n\n for (const toggle of Array.from(this.rootEl.querySelectorAll<HTMLElement>('.log-level-toggle'))) {\n toggle.addEventListener('click', (e) => {\n e.preventDefault();\n const level = toggle.getAttribute('data-level') ?? '';\n if (this._levels.has(level)) this._levels.delete(level);\n else this._levels.add(level);\n toggle.classList.toggle('is-on', this._levels.has(level));\n toggle.setAttribute('aria-pressed', this._levels.has(level) ? 'true' : 'false');\n this._render();\n });\n }\n\n this._filterEl?.addEventListener('input', () => {\n this._text = this._filterEl?.value ?? '';\n this._render();\n });\n\n const head = this.rootEl.querySelector('.log-table thead');\n this._grid = head && this.el\n ? new DataGrid<LogMessageRow>({\n head: head as HTMLElement,\n body: this.el,\n columns: this._columns(),\n // The order it happened in. A log read out of order is not a log.\n defaultSort: { col: 'time', dir: 'asc' },\n rowKey: (m) => String(m.id),\n emptyText: this._host.t('NoLogMessages'),\n rowClass: (m) => `log-row log-row-${m.level}`,\n contextMenu: makeGridMenu(this._host),\n selection: 'multi',\n })\n : null;\n\n this._host.register(this);\n }\n\n dispose(): void {\n this._grid?.destroy();\n this._host.unregister(this);\n try { this.rootEl.remove(); } catch { /* already detached */ }\n }\n\n /// The sources that exist. Sent whole: a source that has gone is gone from the tree, and\n /// the selection falls back to everything rather than to a node nobody can see.\n setSources(sources: LogSourceNode[]): void {\n this._sources = sources || [];\n if (this._selected !== null && !this._sources.some(s => s.id === this._selected)) this._selected = null;\n this._renderTree();\n this._render();\n }\n\n /// Add what has just been written. The oldest fall off the front once the cap is reached.\n append(messages: LogMessageRow[]): void {\n if (!messages || messages.length === 0) return;\n\n this._messages.push(...messages);\n if (this._messages.length > this._max) this._messages.splice(0, this._messages.length - this._max);\n this._render();\n }\n\n /// Forget every message. The sources stay: they still exist, they have just gone quiet.\n clear(): void {\n this._messages = [];\n this._render();\n }\n\n /// Show one source's subtree, or everything when given null.\n select(sourceId: string | null): void {\n this._selected = sourceId;\n this._renderTree();\n this._render();\n }\n\n /// What is on screen, after every filter.\n visible(): LogMessageRow[] {\n const sources = subtreeOf(this._sources, this._selected);\n return this._messages.filter(m => keepLog(m, { levels: this._levels, text: this._text, sources }));\n }\n\n _render(): void {\n this._grid?.setRows(this.visible());\n }\n\n _renderTree(): void {\n const tree = this._treeEl;\n if (tree === null) return;\n\n tree.innerHTML = '';\n tree.appendChild(this._treeRow({ id: '', name: this._host.t('AllSources'), depth: 0, children: [] }, null));\n for (const root of buildLogTree(this._sources)) this._appendNode(tree, root);\n }\n\n _appendNode(into: HTMLElement, node: LogTreeNode): void {\n into.appendChild(this._treeRow(node, node.id));\n for (const child of node.children) this._appendNode(into, child);\n }\n\n // Flat rows with an indent rather than nested lists: the tree is read, not restructured,\n // and one list is what a keyboard walks through in the order the eye does.\n _treeRow(node: LogTreeNode, id: string | null): HTMLElement {\n const selected = this._selected === id;\n const row = makeElement('button', `log-source${selected ? ' is-selected' : ''}`, {\n type: 'button',\n role: 'treeitem',\n 'aria-selected': selected ? 'true' : 'false',\n 'aria-level': String(node.depth + 1),\n style: `padding-left: ${0.5 + node.depth * 0.85}rem`,\n }, [node.name]);\n\n row.addEventListener('click', (e) => { e.preventDefault(); this.select(id); });\n return row;\n }\n\n _export(): void {\n this._grid?.download('log', this._host.t('LogMonitor'));\n }\n\n _columns(): GridColumn<LogMessageRow>[] {\n const label = (key: string) => this._host.t(key);\n return [\n {\n key: 'source',\n header: label('Source'),\n exportable: true,\n value: (m) => m.source ?? this._sourceName(m.sourceId),\n },\n {\n key: 'time',\n header: label('Time'),\n exportable: true,\n value: (m) => m.time,\n render: (m) => this._host.presentation.timeText(m.time),\n exportValue: (m) => this._host.presentation.timeText(m.time),\n },\n {\n key: 'level',\n header: label('Type'),\n headerClass: 'log-level-col',\n exportable: true,\n cellClass: (m) => `log-level-cell log-level-${m.level}`,\n value: (m) => m.level,\n render: (m) => LEVEL_LETTER[m.level] ?? String(m.level ?? '').slice(0, 1).toUpperCase(),\n // The sheet carries the level's name, not the letter the column has room for.\n exportValue: (m) => m.level,\n },\n {\n key: 'message',\n header: label('Message'),\n exportable: true,\n cellClass: () => 'log-message-cell',\n value: (m) => m.message,\n },\n ];\n }\n\n _sourceName(id: string): string {\n return this._sources.find(s => s.id === id)?.name ?? id;\n }\n}\n\n/// What a level's toggle is called.\n///\n/// Literal keys at the point they are asked for - see the note on the same shape in\n/// strategies-widget: a key built from a value is invisible to the scan that generates\n/// `translation-keys.json`, and reaches the user untranslated.\nfunction levelTitle(host: TradingHost, level: LogLevel): string {\n switch (level) {\n case LogLevels.Error: return host.t('Errors');\n case LogLevels.Warning: return host.t('Warnings');\n case LogLevels.Info: return host.t('Messages');\n case LogLevels.Debug: return host.t('Debug');\n default: return host.t('Verbose');\n }\n}\n", "// Strategy statistics \u2014 multi-instance.\n//\n// A read-only table of everything a strategy reports about itself: net profit, drawdown, trade\n// counts, latencies. One row per parameter, grouped by the area it belongs to.\n//\n// The desktop grid derives its rows by reflecting over the strategy's statistic parameters, and\n// a browser has no equivalent - so the rows arrive from the consumer already resolved, name and\n// description translated, with the two things that decide where a row sits: a stable category\n// key and the registry order. Both matter. Grouping on the translated category would regroup\n// the table when the language changes, and sorting by name or by value would scatter parameters\n// that are read together.\nimport { makeElement, makeIconButton, makePanelId, makePanelRoot } from './dom.js';\nimport { ControlTypes } from './control-types.js';\nimport { makeGridMenu } from './grid-menu.js';\nimport { TradingHost, assertHost } from './trading-host.js';\nimport type { StatisticRow } from './trading-data.js';\nimport { DataGrid, GridColumn } from '@stocksharp/grids/source/data-grid';\n\n/// What the table needs beyond the host port. Nothing: there is nothing here to act on.\nexport interface StatisticsDeps {\n host: TradingHost;\n}\n\n/// A row with its category's place worked out.\ninterface RankedRow extends StatisticRow {\n categoryRank: number;\n}\n\n/// Give every category the place of its first parameter.\n///\n/// Taken from the rows rather than assumed: the registry bands its orders by category today\n/// - profit below a hundred, trades from a hundred - but a consumer numbering them some\n/// other way still gets its groups in the order it asked for.\nfunction rank(rows: readonly StatisticRow[]): RankedRow[] {\n const first = new Map<string, number>();\n for (const row of rows) {\n const seen = first.get(row.category);\n if (seen === undefined || row.order < seen) first.set(row.category, row.order);\n }\n\n return rows.map(row => ({ ...row, categoryRank: first.get(row.category) ?? row.order }));\n}\n\nexport class StatisticsWidget {\n static TYPE = ControlTypes.Statistics;\n\n rootEl: HTMLElement;\n el: HTMLElement | null;\n // `//` rather than `///` from here down \u2014 see the note in positions-widget.\n _host: TradingHost;\n _closeBtn: HTMLElement | null;\n _exportBtn: HTMLElement | null;\n _rows: RankedRow[];\n _grid: DataGrid<RankedRow> | null;\n\n static create(hostEl: HTMLElement, state: Record<string, unknown>, deps: StatisticsDeps): StatisticsWidget {\n const host = assertHost(deps?.host, 'StatisticsWidget');\n const root = StatisticsWidget._buildRoot(host);\n root.id = makePanelId(StatisticsWidget.TYPE);\n hostEl.appendChild(root);\n return new StatisticsWidget(root, state || {}, deps);\n }\n\n // The panel's markup. No rail action but the export: a statistic is produced by the\n // strategy and there is nothing here to cancel, reload or edit.\n static _buildRoot(host: TradingHost): HTMLElement {\n const title = host.t('Statistics');\n return makePanelRoot('statistics-panel', title, [\n makeElement('div', 'panel-header', {}, [\n makeElement('span', '', {}, [title]),\n makeIconButton('bt-icon-btn bt-icon-cancel panel-close-btn', host.t('ClosePanel'), 'bi-x', { type: 'button' }),\n ]),\n makeElement('div', 'panel-body panel-body-with-rail', {}, [\n makeElement('div', 'panel-body-content', {}, [\n makeElement('table', 'terminal-table statistics-table', { role: 'table', 'aria-label': host.t('StatisticsList') }, [\n makeElement('thead', '', {}, []),\n makeElement('tbody', 'statistics-body', {}, []),\n ]),\n ]),\n makeElement('div', 'panel-rail', { role: 'toolbar', 'aria-label': host.t('StatisticsActions') }, [\n makeIconButton('bt-icon-btn panel-export-btn', host.t('ExportToExcel'), 'bi-file-earmark-spreadsheet', {}),\n ]),\n ]),\n ]);\n }\n\n constructor(rootEl: HTMLElement, _state: Record<string, unknown>, deps: StatisticsDeps) {\n this._host = assertHost(deps?.host, 'StatisticsWidget');\n\n this.rootEl = rootEl;\n this.el = this.rootEl.querySelector('.statistics-body');\n this._closeBtn = this.rootEl.querySelector('.panel-close-btn');\n this._exportBtn = this.rootEl.querySelector('.panel-export-btn');\n this._rows = [];\n\n this._closeBtn?.addEventListener('click', (e) => {\n e.preventDefault();\n this._host.close();\n });\n\n this._exportBtn?.addEventListener('click', (e) => {\n e.preventDefault();\n this._export();\n });\n\n const head = this.rootEl.querySelector('.statistics-table thead');\n this._grid = head && this.el\n ? new DataGrid<RankedRow>({\n head: head as HTMLElement,\n body: this.el,\n columns: this._columns(),\n // Not a sort the reader chose: the registry hands every parameter an order, and\n // the bands it assigns are what puts profit above drawdown above trade counts.\n defaultSort: { col: 'order', dir: 'asc' },\n rowKey: (r) => String(r.key),\n emptyText: this._host.t('NoStatistics'),\n contextMenu: makeGridMenu(this._host),\n // The groups sit where the registry order puts them, not where the\n // alphabet would: profit, then trades, then positions, then orders.\n groupOrder: 'rows',\n selection: 'multi',\n })\n : null;\n\n // Grouped on the key, never on the caption: the caption is translated and would\n // regroup the table under the reader's language. Neither the key nor the order is a\n // column anyone reads, so both are hidden - declared, so the grid can group and sort\n // on them, and out of the way.\n this._grid?.setState({ group: 'category', hidden: ['category', 'order'] });\n\n this._host.register(this);\n }\n\n dispose(): void {\n this._grid?.destroy();\n this._host.unregister(this);\n try { this.rootEl.remove(); } catch { /* already detached */ }\n }\n\n /// Show these statistics. The whole set at once: a strategy publishes its parameters as one\n /// table and a row that vanished from it has stopped existing, not stopped changing.\n update(rows: StatisticRow[]): void {\n this._rows = rank(rows || []);\n this._grid?.setRows(this._rows);\n }\n\n _export(): void {\n this._grid?.download('statistics', this._host.t('Statistics'));\n }\n\n // Two visible columns and one that only groups. The order column is hidden as well: it\n // decides the sort and means nothing to a reader.\n _columns(): GridColumn<RankedRow>[] {\n const label = (key: string) => this._host.t(key);\n return [\n {\n key: 'category',\n header: label('Category'),\n exportable: false,\n // The group's place, not its name: groups are laid out in the order the\n // registry gives their parameters, so profit comes before trade counts.\n // Grouping on the name would order them alphabetically, and on the\n // translated name would reorder them with the reader's language.\n value: (r) => r.categoryRank,\n text: (r) => r.categoryText || r.category,\n },\n {\n key: 'order',\n header: label('Order'),\n exportable: false,\n value: (r) => r.order,\n },\n {\n key: 'name',\n header: label('Name'),\n exportable: true,\n value: (r) => r.name,\n bindCell: (td, r) => {\n if (r.description) td.setAttribute('title', r.description);\n },\n },\n {\n key: 'value',\n header: label('Value'),\n exportable: true,\n cellClass: () => 'statistic-value',\n // Sorts on the raw value so a number sorts as a number, reads as the text below.\n value: (r) => r.value as string | number | null,\n render: (r) => formatStatistic(r.value),\n exportValue: (r) => r.value ?? '',\n },\n ];\n }\n}\n\n/// A statistic as text.\n///\n/// Three shapes reach this: a number, a moment, and everything else. A number is rounded to two\n/// places and shown without trailing zeros - a profit of 11055.75 and a count of 1340 both read\n/// the way they are meant to. A moment is a date: these are run-level facts (the day of the\n/// maximum drawdown), and the hour would be noise. Anything with no value yet is a blank cell\n/// rather than a zero, which would read as a measured nothing.\nexport function formatStatistic(value: unknown): string {\n if (value === null || value === undefined || value === '') return '';\n\n if (typeof value === 'number') {\n if (!isFinite(value)) return '';\n return String(Math.round(value * 100) / 100);\n }\n\n if (value instanceof Date) return isoDate(value);\n\n if (typeof value === 'string') {\n // A date only when it is unambiguously one: a bare number in a string stays a number,\n // and a name stays a name.\n const at = /^\\d{4}-\\d{2}-\\d{2}([T ]|$)/.test(value) ? new Date(value) : null;\n if (at !== null && !isNaN(at.getTime())) return isoDate(at);\n return value;\n }\n\n return String(value);\n}\n\nfunction isoDate(at: Date): string {\n return at.toISOString().slice(0, 10);\n}\n", "// Bubble compaction for a tape.\n//\n// One lane of a bubble chart is one symbol's ticks, and a busy symbol delivers\n// far more of them than a lane has room for: drawn one circle per print, the\n// lane melts into a solid wall. Bucketing by time slice and side turns that\n// back into something readable \u2014 at most two bubbles per slice, each placed at\n// its slice's volume-weighted average price and sized by its summed volume.\n//\n// Pure: no DOM, no host, no clock. The trade feed renders what comes out of\n// here, which is what lets the arithmetic a busy symbol depends on be covered\n// directly rather than through a canvas.\nimport type { OrderSide } from './trading-data.js';\n\n/// One print, with the two things the tape has already decided about it: which\n/// direction it went, and where it sits on the shared time axis.\n///\n/// `buy` is resolved by the host's presentation before a tick reaches this\n/// module \u2014 the wire spells a side three ways and normalising it is the host's\n/// job, not this file's \u2014 while `side` keeps the raw value, because wording it\n/// is the host's job too and only it can do that. `index` is the tick's\n/// position in the WHOLE feed rather than in its lane, so two symbols with\n/// wildly different prices still flow along one time axis.\nexport interface FeedTick {\n symbol: string;\n side: OrderSide;\n buy: boolean;\n price: number;\n quantity: number;\n time: string;\n index: number;\n}\n\n/// One bubble: a tick, or the slice of ticks it stands for.\nexport interface FeedBubble extends FeedTick {\n /// How many ticks this bubble represents \u2014 1 when it is a tick itself, and\n /// what the tooltip reads to say \"VWAP\" instead of \"Price\".\n count: number;\n}\n\n/// Bucket one lane's ticks into at most `maxBucketsPerSide` bubbles per side.\n///\n/// Below that budget every tick passes through as its own one-tick bubble, so a\n/// sparse symbol still shows every print. Above it, each (side, slice) merges\n/// into one bubble. `ticks` is newest-first, which is how the tape holds them,\n/// so the first slice is the most recent block.\nexport function aggregateBubbles(ticks: FeedTick[], maxBucketsPerSide: number): FeedBubble[] {\n if (!ticks || ticks.length === 0) return [];\n const budget = Math.max(1, Math.floor(maxBucketsPerSide));\n\n // The two directions compete for the same horizontal room, so each gets its\n // own budget rather than the busier one crowding the other out.\n const buys: FeedTick[] = [];\n const sells: FeedTick[] = [];\n for (const tick of ticks) (tick.buy ? buys : sells).push(tick);\n\n const bubbles: FeedBubble[] = [];\n for (const side of [buys, sells]) {\n if (side.length === 0) continue;\n if (side.length <= budget) {\n for (const tick of side) bubbles.push({ ...tick, count: 1 });\n continue;\n }\n const bucketSize = Math.ceil(side.length / budget);\n for (let start = 0; start < side.length; start += bucketSize)\n bubbles.push(merge(side.slice(start, start + bucketSize)));\n }\n return bubbles;\n}\n\n/// One slice as a single bubble: total volume, volume-weighted price, and the\n/// slice's middle tick for its position on the time axis.\nfunction merge(slice: FeedTick[]): FeedBubble {\n let quantity = 0;\n let weighted = 0;\n for (const tick of slice) {\n const q = tick.quantity || 0;\n quantity += q;\n weighted += q * tick.price;\n }\n // A slice can legitimately total zero volume (an all-zero-quantity tape is\n // rare live and ordinary in a fixture), and dividing by it would place the\n // bubble at NaN \u2014 nowhere, silently. Its first price is the honest answer.\n const price = quantity > 0 ? weighted / quantity : slice[0].price;\n const middle = slice[Math.floor(slice.length / 2)];\n return {\n symbol: slice[0].symbol,\n side: slice[0].side,\n buy: slice[0].buy,\n price,\n quantity,\n time: middle.time,\n index: middle.index,\n count: slice.length,\n };\n}\n", "// Where every bubble, rule and label of a bubble chart goes.\n//\n// The trade feed's second rendering is a scatter of prints: time along X,\n// price up Y, volume as the radius, direction as the colour. It is drawn on a\n// canvas, which is the one surface a class name cannot reach \u2014 so the geometry\n// is computed here, as numbers, and the control does nothing but stroke and\n// fill what comes back.\n//\n// Pure, and that is the point: a fake canvas that swallows drawing calls proves\n// nothing, while the arithmetic below \u2014 lanes sharing one time axis, a price\n// scale per symbol, a radius that stays inside its lane \u2014 is exactly what can\n// be wrong. It is covered directly.\nimport { aggregateBubbles, type FeedBubble, type FeedTick } from './tradefeed-aggregator.js';\n\n/// One symbol's prints. A feed following a single symbol has one unnamed lane;\n/// pinning extras splits the height into one lane each so a $76k symbol and a\n/// $270 one both stay visible instead of one flattening the other.\nexport interface BubbleLane {\n symbol: string;\n ticks: FeedTick[];\n}\n\nexport interface BubbleLayoutInput {\n width: number;\n height: number;\n lanes: BubbleLane[];\n /// How many ticks the whole feed holds. The X axis is a tick's index in\n /// that list, so lanes flow along one shared time axis rather than each\n /// stretching its own count across the full width.\n total: number;\n}\n\n/// One bubble, placed.\nexport interface BubbleShape {\n x: number;\n y: number;\n radius: number;\n bubble: FeedBubble;\n}\n\n/// One price label on the right-hand axis, and the rule leading to it.\nexport interface BubbleAxisTick {\n text: string;\n y: number;\n}\n\n/// One lane, placed. `label` is null for a feed following a single symbol \u2014\n/// naming the only lane says nothing \u2014 and `separatorY` is null for the first\n/// lane, which has nothing above it to be divided from.\nexport interface BubbleLaneShape {\n symbol: string;\n label: { x: number; y: number } | null;\n separatorY: number | null;\n ticks: BubbleAxisTick[];\n}\n\nexport interface BubbleLayout {\n /// Left edge of the axis strip: rules stop just short of it, labels start\n /// just past it.\n axisX: number;\n /// Where an axis label's text begins.\n labelX: number;\n lanes: BubbleLaneShape[];\n /// Every bubble in draw order \u2014 oldest first, so the newest print lands on\n /// top of what it overlaps. Hit-testing walks this backwards for the same\n /// reason.\n bubbles: BubbleShape[];\n}\n\n// Geometry. These are the chart's proportions, not the host's looks: a canvas\n// has no cascade to read them from, and moving them into CSS would only mean\n// reading them back out again.\nconst PAD_X = 10;\nconst PAD_Y = 10;\nconst AXIS_WIDTH = 56;\nconst LABEL_GAP = 4;\nconst LANE_GAP = 2;\nconst MIN_LANE_HEIGHT = 20;\nconst AXIS_TICKS = 3;\nconst MIN_RADIUS = 2;\nconst MAX_RADIUS = 22;\n/// A bubble may grow to this share of its lane's height, so a lane with one\n/// enormous print does not draw a circle taller than the lane it sits in.\nconst RADIUS_LANE_SHARE = 0.4;\n/// Horizontal room one bucket wants. Narrower and the lane reads as a wall;\n/// wider and a sparse symbol turns into a handful of lonely dots.\nconst BUCKET_WIDTH = 8;\nconst MIN_BUCKETS = 6;\nconst MAX_BUCKETS = 30;\n\n/// Place every lane and every bubble for one paint.\nexport function layoutBubbles(input: BubbleLayoutInput): BubbleLayout {\n const { width, height, total } = input;\n const axisX = Math.max(PAD_X + 1, width - AXIS_WIDTH);\n const drawWidth = Math.max(1, axisX - PAD_X);\n const layout: BubbleLayout = { axisX, labelX: axisX + LABEL_GAP, lanes: [], bubbles: [] };\n\n const lanes = input.lanes.filter(lane => lane.ticks.length > 0);\n if (lanes.length === 0) return layout;\n\n const named = input.lanes.length > 1;\n const laneHeight = Math.max(\n MIN_LANE_HEIGHT,\n (height - 2 * PAD_Y - LANE_GAP * (input.lanes.length - 1)) / input.lanes.length);\n\n // Placed against the DECLARED lanes, not the non-empty ones: a symbol that\n // has not printed yet keeps its slot rather than letting the lanes below it\n // slide up and back down on its first tick.\n input.lanes.forEach((lane, position) => {\n const laneTop = named ? PAD_Y + position * (laneHeight + LANE_GAP) : PAD_Y;\n const laneSpan = named ? laneHeight : Math.max(MIN_LANE_HEIGHT, height - 2 * PAD_Y);\n const scale = priceScale(lane.ticks, laneTop, laneSpan);\n\n layout.lanes.push({\n symbol: lane.symbol,\n label: named ? { x: LABEL_GAP, y: laneTop + 2 } : null,\n separatorY: named && position > 0 ? laneTop - LANE_GAP / 2 : null,\n ticks: lane.ticks.length === 0 ? [] : axisTicks(scale),\n });\n\n if (lane.ticks.length === 0) return;\n const buckets = Math.max(MIN_BUCKETS, Math.min(MAX_BUCKETS, Math.floor(drawWidth / BUCKET_WIDTH)));\n const bubbles = aggregateBubbles(lane.ticks, buckets);\n let maxQuantity = 0;\n for (const bubble of bubbles) if (bubble.quantity > maxQuantity) maxQuantity = bubble.quantity;\n const quantityScale = Math.max(maxQuantity, Number.EPSILON);\n const radiusCap = Math.min(MAX_RADIUS, laneSpan * RADIUS_LANE_SHARE);\n\n for (const bubble of bubbles) {\n layout.bubbles.push({\n // Newest print (index 0) at the right edge, oldest at the left.\n x: PAD_X + drawWidth * (total <= 1 ? 1 : 1 - bubble.index / (total - 1)),\n y: scale.top + scale.height * (1 - (bubble.price - scale.min) / scale.range),\n radius: MIN_RADIUS + Math.sqrt(Math.max(0, bubble.quantity) / quantityScale) * radiusCap,\n bubble,\n });\n }\n });\n\n // Oldest first: a later print draws over an earlier one, and the hit test\n // reads the same list backwards so the circle on top is the one picked.\n layout.bubbles.sort((left, right) => right.bubble.index - left.bubble.index);\n return layout;\n}\n\ninterface PriceScale {\n top: number;\n height: number;\n min: number;\n range: number;\n}\n\n/// The lane's price range, as the Y axis. `range` is never zero \u2014 a lane whose\n/// prints are all at one price would otherwise divide by it \u2014 so a flat lane\n/// draws along its own top edge rather than nowhere.\nfunction priceScale(ticks: FeedTick[], top: number, height: number): PriceScale {\n let min = Infinity;\n let max = -Infinity;\n for (const tick of ticks) {\n if (tick.price < min) min = tick.price;\n if (tick.price > max) max = tick.price;\n }\n if (!isFinite(min) || !isFinite(max)) { min = 0; max = 0; }\n return { top, height, min, range: Math.max(max - min, Number.EPSILON) };\n}\n\n/// Three labels down the lane: its high, its middle and its low.\nfunction axisTicks(scale: PriceScale): BubbleAxisTick[] {\n const format = priceFormatter(scale.min, scale.min + scale.range);\n const ticks: BubbleAxisTick[] = [];\n for (let step = 0; step < AXIS_TICKS; step++) {\n const fraction = step / (AXIS_TICKS - 1);\n ticks.push({\n text: format((scale.min + scale.range) - scale.range * fraction),\n y: scale.top + scale.height * fraction,\n });\n }\n return ticks;\n}\n\n/// Decimals chosen from the lane's magnitude. A fixed precision is wrong at\n/// both ends: two decimals on a $76,000 print is noise, and none on a $0.04 one\n/// collapses the whole lane onto a single label.\nexport function priceFormatter(min: number, max: number): (price: number) => string {\n const magnitude = Math.max(Math.abs(min), Math.abs(max));\n let digits = 6;\n if (magnitude >= 1000) digits = 0;\n else if (magnitude >= 10) digits = 2;\n else if (magnitude >= 0.1) digits = 4;\n return (price: number) => price.toLocaleString(undefined, {\n minimumFractionDigits: digits,\n maximumFractionDigits: digits,\n });\n}\n", "// Trade feed \u2014 multi-instance.\n//\n// The public tape: every print for the symbol the page is showing, plus any\n// extra instruments this instance has pinned, in one of two renderings. The\n// list is a row per print; the bubble chart is the same prints scattered by\n// time and price, sized by volume and coloured by direction. Which of the two\n// is showing is shared across every feed on the page through the host's\n// preference store; the pinned extras belong to the instance and ride in its\n// persisted state.\n//\n// Builds its own DOM (see `_buildRoot`) rather than cloning a <template> out of\n// the page, so it can be constructed by any host that supplies a `TradingHost`.\n//\n// Ticks are pushed in \u2014 `setTrades` for a fresh symbol, `addTrade` per print \u2014\n// because a host fans one socket to every live feed rather than each of them\n// subscribing again. The one thing the panel pulls is the account's own fills,\n// on the My-trades tab, through `trading.api.getExecutions`.\n//\n// The chart is a <canvas>, the single surface a class name cannot reach: its\n// geometry comes out of `tradefeed-bubbles.ts` as numbers and its colours out\n// of the host's `canvasPalette()`, so the package still paints nothing from a\n// palette of its own.\nimport { formatPrice, formatQty } from './formatters.js';\nimport { makeElement, makeIconButton, makePanelId, makePanelRoot } from './dom.js';\nimport { ControlTypes } from './control-types.js';\nimport { makeGridMenu } from './grid-menu.js';\nimport { MarketDataLevels, TradingHost, assertHost } from './trading-host.js';\nimport type { TradeRow } from './trading-data.js';\nimport type { FeedBubble, FeedTick } from './tradefeed-aggregator.js';\nimport { layoutBubbles, type BubbleLane, type BubbleShape } from './tradefeed-bubbles.js';\nimport { DataGrid, GridColumn } from '@stocksharp/grids/source/data-grid';\n\n// A print carries no id of its own, so the widget stamps one on arrival \u2014 the\n// grid addresses rows by key, and a tape with fifty anonymous rows would have\n// no identities to diff, select or flash by.\ntype KeyedTrade = TradeRow & { _k?: number };\n\n/// The panel needs nothing beyond the host port. It reports no application\n/// action: a tape is read, and the two gestures it does make \u2014 pin an\n/// instrument, open another feed \u2014 are the host's own `pickInstrument` and\n/// `spawn`.\nexport interface TradeFeedDeps {\n host: TradingHost;\n}\n\n// Opacity, not colour. The hue of every stroke below is the host's\n// `canvasPalette`; how solid it is drawn is the chart's own business, and\n// keeping the two apart is what lets one palette serve a light theme and a dark\n// one without the package knowing which it is in.\nconst FILL_ALPHA = 0.55;\nconst STROKE_ALPHA = 0.9;\nconst LABEL_ALPHA = 0.55;\nconst SEPARATOR_ALPHA = 0.12;\nconst RULE_ALPHA = 0.08;\n\n// How far from a bubble's edge a cursor still counts as over it. Small bubbles\n// are otherwise impossible to hit.\nconst HOVER_SLACK = 6;\n// Gap between the cursor and the tooltip, and between the tooltip and the edge\n// it is clamped against.\nconst TOOLTIP_OFFSET = 12;\nconst TOOLTIP_MARGIN = 4;\n\nexport class TradeFeedWidget {\n static TYPE = ControlTypes.TradeFeed;\n /// Shared across every trade feed on the page: which of the two renderings\n /// the user last chose. The value is what a user's stored settings already\n /// hold, so it is spelled exactly as it always was.\n static VIEW_KEY = 'terminal.tradeFeedView';\n /// Rows kept in the list, and prints kept for the chart. The chart holds\n /// far more because it compacts them: a bubble stands for a slice.\n static MAX_ROWS = 50;\n static MAX_BUBBLES = 500;\n\n rootEl: HTMLElement;\n extrasEl: HTMLElement | null;\n bubbleCanvas: HTMLCanvasElement | null;\n trades: TradeRow[];\n bubbleTrades: TradeRow[];\n myTrades: TradeRow[];\n avgQty: number;\n tab: string;\n view: string;\n // `//` rather than `///` from here down \u2014 see the note in positions-widget.\n _host: TradingHost;\n _marketGrid: DataGrid<TradeRow> | null;\n _myGrid: DataGrid<TradeRow> | null;\n // The stamp the next anonymous print gets \u2014 see KeyedTrade.\n _seq: number;\n _tabsEl: HTMLElement | null;\n _viewToggleEl: HTMLElement | null;\n _tooltipEl: HTMLElement | null;\n _addBtn: HTMLElement | null;\n _closeBtn: HTMLElement | null;\n _addSymbolBtn: HTMLElement | null;\n _bubbleCtx: CanvasRenderingContext2D | null;\n // Rebuilt on every paint: where each bubble landed and what it stands for.\n // Hover walks it backwards so the circle drawn last \u2014 the newest print \u2014\n // wins wherever two overlap.\n _bubbleHits: BubbleShape[];\n _canvasSize: { width: number; height: number } | null;\n _resizeObserver: ResizeObserver | null;\n _activeSymbol: string | null;\n _extraSymbols: Set<string>;\n\n static create(hostEl: HTMLElement, state: Record<string, unknown>, deps: TradeFeedDeps): TradeFeedWidget {\n // Assert before building: the markup below is localized through the\n // host, so a missing host has to fail here rather than render a panel\n // captioned with raw English keys.\n const host = assertHost(deps?.host, 'TradeFeedWidget');\n const root = TradeFeedWidget._buildRoot(host);\n root.id = makePanelId(TradeFeedWidget.TYPE);\n hostEl.appendChild(root);\n return new TradeFeedWidget(root, state || {}, deps);\n }\n\n // The panel's markup. The host stylesheet reads this structure, and a\n // docking host lifts `.panel-header`'s children into its tab strip.\n //\n // Two `+` buttons, and they are not the same gesture: the one in the header\n // pins another instrument to THIS feed, the one beside it asks the host for\n // another feed panel. The tooltips are what tell them apart, so neither is\n // an icon on its own.\n static _buildRoot(host: TradingHost): HTMLElement {\n const addInstrument = host.t('AddInstrument');\n const marketTrades = host.t('MarketTrades');\n return makePanelRoot('tradefeed-panel tf-tab-market tf-view-list', host.t('TradeFeed'), [\n makeElement('div', 'panel-header', {}, [\n makeElement('span', '', {}, [marketTrades]),\n makeIconButton('bt-icon-btn tf-add-symbol-btn', addInstrument, 'bi-plus-lg', { type: 'button' }),\n makeIconButton('bt-icon-btn panel-add-btn', host.t('Add trade feed'), 'bi-window-plus', { type: 'button' }),\n makeElement('div', 'tradefeed-view-toggle tf-view-toggle', { role: 'group', 'aria-label': host.t('TradeFeedView') }, [\n makeIconButton('tf-view-btn', host.t('ListView'), 'bi-list-ul', { type: 'button', 'data-view': 'list' }),\n makeIconButton('tf-view-btn', host.t('BubbleChart'), 'bi-circle-fill', { type: 'button', 'data-view': 'bubbles' }),\n ]),\n makeIconButton('bt-icon-btn bt-icon-cancel panel-close-btn', host.t('ClosePanel'), 'bi-x', { type: 'button' }),\n ]),\n makeElement('div', 'tf-tabs', { role: 'tablist', 'aria-label': host.t('Trade feed tabs') }, [\n makeElement('button', 'tf-tab active', { type: 'button', role: 'tab', 'aria-selected': 'true', 'data-tab': 'market' }, [marketTrades]),\n makeElement('button', 'tf-tab', { type: 'button', role: 'tab', 'aria-selected': 'false', 'data-tab': 'my' }, [host.t('My trades')]),\n ]),\n makeElement('div', 'tf-extras', { role: 'list', 'aria-label': host.t('ExtraInstruments'), hidden: '' }, []),\n // Each tape is a DataGrid over the shared table skin: the <thead>\n // is left empty for the grid to fill, which is also what makes the\n // tape sortable and gives it the grid's menu and selection.\n makeElement('div', 'tradefeed-content tf-market', { 'aria-live': 'polite' }, [\n makeElement('table', 'terminal-table tradefeed-table', { role: 'table', 'aria-label': marketTrades }, [\n makeElement('thead', '', {}, []),\n makeElement('tbody', '', {}, []),\n ]),\n ]),\n makeElement('div', 'tradefeed-content tf-my', {}, [\n makeElement('table', 'terminal-table tradefeed-table', { role: 'table', 'aria-label': host.t('My trades') }, [\n makeElement('thead', '', {}, []),\n makeElement('tbody', '', {}, []),\n ]),\n ]),\n makeElement('canvas', 'tradefeed-bubbles tf-bubbles', { 'aria-hidden': 'true' }, []),\n makeElement('div', 'tf-bubble-tooltip', { role: 'tooltip', hidden: '' }, []),\n ]);\n }\n\n constructor(rootEl: HTMLElement, state: Record<string, unknown>, deps: TradeFeedDeps) {\n this._host = assertHost(deps?.host, 'TradeFeedWidget');\n\n this.rootEl = rootEl;\n this.extrasEl = this.rootEl.querySelector('.tf-extras');\n this.bubbleCanvas = this.rootEl.querySelector('.tf-bubbles');\n this._tabsEl = this.rootEl.querySelector('.tf-tabs');\n this._viewToggleEl = this.rootEl.querySelector('.tf-view-toggle');\n this._tooltipEl = this.rootEl.querySelector('.tf-bubble-tooltip');\n this._addBtn = this.rootEl.querySelector('.panel-add-btn');\n this._closeBtn = this.rootEl.querySelector('.panel-close-btn');\n this._addSymbolBtn = this.rootEl.querySelector('.tf-add-symbol-btn');\n this._bubbleCtx = this.bubbleCanvas?.getContext('2d') ?? null;\n\n this.trades = [];\n this.bubbleTrades = [];\n this.myTrades = [];\n // Seed rather than zero: the \"unusually large\" test is a multiple of the\n // running average, and an average starting at zero would flag the first\n // print of the session.\n this.avgQty = 100;\n this.tab = 'market';\n this._seq = 0;\n this._bubbleHits = [];\n this._canvasSize = null;\n this._resizeObserver = null;\n\n this._marketGrid = this._makeGrid('.tf-market');\n this._myGrid = this._makeGrid('.tf-my');\n\n const saved = this._host.preferences.get(TradeFeedWidget.VIEW_KEY, null);\n this.view = saved === 'bubbles' ? 'bubbles' : 'list';\n\n // The page's active symbol drives the primary feed; extras are the\n // instruments this instance pinned on top of it, each refcount-\n // subscribed and shown with a symbol column.\n this._activeSymbol = null;\n this._extraSymbols = new Set(Array.isArray(state.extras) ? state.extras as string[] : []);\n\n this._addBtn?.addEventListener('click', (e) => {\n e.preventDefault();\n this._host.spawn({ extras: [...this._extraSymbols] });\n });\n this._closeBtn?.addEventListener('click', (e) => {\n e.preventDefault();\n this.dispose();\n this._host.close();\n });\n this._addSymbolBtn?.addEventListener('click', (e) => {\n e.preventDefault();\n e.stopPropagation();\n this._host.trading.pickInstrument((symbol) => void this.addExtraSymbol(symbol));\n });\n\n this._bindTabs();\n this._bindViewToggle();\n this._bindBubbleHover();\n this._applyView();\n this._renderExtras();\n\n // Resubscribe the persisted extras up front: without this a reload\n // renders the chip and streams nothing behind it.\n for (const symbol of this._extraSymbols)\n void this._host.trading.marketData.addSymbol(symbol, MarketDataLevels.Tape).catch(() => { });\n\n if (this.bubbleCanvas && typeof ResizeObserver === 'function') {\n this._resizeObserver = new ResizeObserver(() => { this._sizeCanvas(); this._renderBubbles(); });\n this._resizeObserver.observe(this.bubbleCanvas);\n }\n\n this._host.register(this);\n }\n\n dispose(): void {\n try { this._resizeObserver?.disconnect(); } catch { /* already torn down */ }\n // The grids hold document-level listeners (the copy shortcut) that\n // outlive a removed subtree \u2014 they have to be told, not just detached.\n this._marketGrid?.destroy();\n this._myGrid?.destroy();\n // Drop this feed's share of every extra so a symbol nobody else watches\n // stops streaming \u2014 the client refcounts, so a neighbour keeps its own.\n for (const symbol of this._extraSymbols)\n void this._host.trading.marketData.removeSymbol(symbol).catch(() => { });\n this._host.unregister(this);\n try { this.rootEl.remove(); } catch { /* already detached */ }\n }\n\n /// Which symbol the rest of the page is showing. The feed accepts prints\n /// for it without it being pinned, and re-syncs so the symbol column\n /// appears or disappears with the pinned set.\n setActiveSymbol(symbol: string | null): void {\n this._activeSymbol = symbol || null;\n this._renderExtras();\n this._renderBubbles();\n }\n\n /// Replace the tape wholesale \u2014 what a host does when the page moves to a\n /// different instrument. The flash baseline resets with it: a new\n /// instrument's history is history, not fifty arrivals at once.\n setTrades(trades: TradeRow[]): void {\n const rows = (trades || []).map(t => this._stamp(t));\n this.trades = rows.slice(0, TradeFeedWidget.MAX_ROWS);\n this.bubbleTrades = rows.slice(0, TradeFeedWidget.MAX_BUBBLES);\n if (this.trades.length > 0) {\n const total = this.trades.reduce((sum, t) => sum + (t.quantity ?? 0), 0);\n this.avgQty = total / this.trades.length || this.avgQty;\n }\n this._marketGrid?.flashReset();\n this._marketGrid?.setRows([...this.trades]);\n this._renderBubbles();\n }\n\n /// One print off the wire. Ignored unless this feed watches its symbol:\n /// a host fans every tick to every feed and each decides for itself, which\n /// is the only arrangement that supports per-instance extras.\n addTrade(trade: TradeRow): void {\n if (!trade || !this._watches(trade.symbol)) return;\n this._stamp(trade);\n this.trades.unshift(trade);\n if (this.trades.length > TradeFeedWidget.MAX_ROWS) this.trades.length = TradeFeedWidget.MAX_ROWS;\n this.bubbleTrades.unshift(trade);\n if (this.bubbleTrades.length > TradeFeedWidget.MAX_BUBBLES) this.bubbleTrades.length = TradeFeedWidget.MAX_BUBBLES;\n // A rolling average rather than a re-sum: \"unusually large\" should track\n // the recent tape, not the whole session.\n this.avgQty = this.avgQty * 0.95 + (trade.quantity ?? 0) * 0.05;\n\n // The grid diffs by key, and its flashNewClass is what marks the\n // newcomer \u2014 the hand-built prepend this replaces did both by hand.\n this._marketGrid?.setRows([...this.trades]);\n this._renderBubbles();\n }\n\n /// The account's own fills for the My-trades tab. The one thing this panel\n /// pulls rather than being pushed.\n async loadMyTrades(portfolioId: number | null, symbol: string | null): Promise<void> {\n if (!portfolioId) {\n this.myTrades = [];\n this._renderMy();\n return;\n }\n try {\n this.myTrades = await this._host.trading.api.getExecutions(portfolioId, symbol, TradeFeedWidget.MAX_ROWS) || [];\n } catch (err) {\n // Through the port, not the console: this is the diagnostic the\n // user cannot see and support has to, and routing it here is also\n // what makes it assertable.\n this._host.log(`TradeFeedWidget: failed to load own trades: ${err}`);\n this.myTrades = [];\n }\n this._renderMy();\n }\n\n // ---------------------------------------------------------------- the grid\n\n // One construction for both tapes: same columns, same chrome, different\n // tbody. The container holds the scroll; the grid holds the rows.\n _makeGrid(containerSelector: string): DataGrid<TradeRow> | null {\n const head = this.rootEl.querySelector(`${containerSelector} thead`);\n const body = this.rootEl.querySelector(`${containerSelector} tbody`);\n if (!head || !body) return null;\n return new DataGrid<TradeRow>({\n head: head as HTMLElement,\n body: body as HTMLElement,\n columns: this._columns(),\n // Newest print on top \u2014 the only order a tape is read in at rest;\n // a header click can still reorder, and clears back to this.\n defaultSort: { col: 'time', dir: 'desc' },\n rowKey: (t) => TradeFeedWidget._key(t),\n emptyText: this._host.t('No trades yet'),\n rowClass: (t) => this._rowClass(t),\n contextMenu: makeGridMenu(this._host),\n selection: 'multi',\n // The grid marks what is new since the previous paint; the\n // stylesheet decides what a flash looks like.\n flashNewClass: 'flash-new',\n });\n }\n\n // Prints arrive anonymous; executions arrive with ids. Stamp the former\n // once, on arrival, and key both without ever colliding.\n _stamp(trade: TradeRow): TradeRow {\n const keyed = trade as KeyedTrade;\n if (keyed._k == null && keyed.id == null) keyed._k = ++this._seq;\n return trade;\n }\n\n static _key(trade: TradeRow): string {\n const keyed = trade as KeyedTrade;\n return keyed.id != null ? String(keyed.id) : `p${keyed._k}`;\n }\n\n // The direction class sits on the row, so price and side inherit its\n // colour while time and qty state their own \u2014 plus the \"unusually large\"\n // mark, measured against the rolling average as of this paint.\n _rowClass(trade: TradeRow): string {\n const classes = this._host.presentation.sideClass(trade.side!).split(' ').filter(Boolean);\n if ((trade.quantity ?? 0) > this.avgQty * 2) classes.push('large-trade');\n return classes.join(' ');\n }\n\n // The tape's single column declaration, shared by both tabs. `time` sorts\n // by the wire timestamp and shows the clock; the symbol column exists\n // always and is hidden until a second instrument is in the feed.\n _columns(): GridColumn<TradeRow>[] {\n const label = (key: string) => this._host.t(key);\n const presentation = this._host.presentation;\n return [\n {\n key: 'time',\n header: label('Time'),\n exportable: true,\n value: (t) => String(t.time || t.executedAt || ''),\n render: (t) => this._host.presentation.timeText(t.time || t.executedAt!),\n },\n {\n key: 'symbol',\n header: label('Sym'),\n exportable: true,\n value: (t) => t.symbol,\n },\n {\n key: 'price',\n header: label('Price'),\n exportable: true,\n value: (t) => t.price,\n render: (t) => formatPrice(t.price),\n },\n {\n key: 'quantity',\n header: label('Qty'),\n exportable: true,\n value: (t) => t.quantity,\n render: (t) => formatQty(t.quantity),\n },\n {\n key: 'side',\n header: label('Side'),\n exportable: true,\n value: (t) => t.side,\n render: (t) => presentation.sideText(t.side!),\n exportValue: (t) => presentation.sideText(t.side!),\n },\n ];\n }\n\n // Both tapes show the symbol column exactly while a second instrument is\n // in the feed \u2014 through the grid's own column visibility, so the user's\n // hide/show from the menu still works the rest of the time.\n _syncSymbolColumn(): void {\n const multi = this._extraSymbols.size > 0;\n for (const grid of [this._marketGrid, this._myGrid]) {\n if (!grid) continue;\n if (multi) grid.showColumn('symbol');\n else grid.hideColumn('symbol');\n }\n }\n\n /// Pin an extra instrument to this feed: subscribe it, show it alongside\n /// the primary, and remember it in the instance's state.\n async addExtraSymbol(symbol: string): Promise<void> {\n if (!symbol) return;\n const sym = String(symbol).toUpperCase();\n if (sym === this._activeSymbol || this._extraSymbols.has(sym)) return;\n this._extraSymbols.add(sym);\n // A tape needs the prints, and only the prints \u2014 the depth `Full` would\n // add is bandwidth this panel never renders.\n try { await this._host.trading.marketData.addSymbol(sym, MarketDataLevels.Tape); } catch { /* socket race */ }\n this._renderExtras();\n this._renderBubbles();\n this._persistExtras();\n }\n\n /// Unpin one, dropping its subscription and the prints already on screen.\n async removeExtraSymbol(symbol: string): Promise<void> {\n if (!symbol) return;\n const sym = String(symbol).toUpperCase();\n if (!this._extraSymbols.has(sym)) return;\n this._extraSymbols.delete(sym);\n try { await this._host.trading.marketData.removeSymbol(sym); } catch { /* socket race */ }\n this.trades = this.trades.filter(t => t.symbol !== sym);\n this.bubbleTrades = this.bubbleTrades.filter(t => t.symbol !== sym);\n this._marketGrid?.setRows([...this.trades]);\n this._renderExtras();\n this._renderBubbles();\n this._persistExtras();\n }\n\n getExtraSymbols(): string[] {\n return [...this._extraSymbols];\n }\n\n // Record what this instance pins, then ask for the layout to be flushed.\n // Two calls because they are two decisions: what the instance remembers,\n // and whether that is worth writing out now. Pinning is \u2014 losing the pin on\n // the next reload is exactly the complaint.\n _persistExtras(): void {\n this._host.persistState({ extras: [...this._extraSymbols] });\n this._host.saveLayout();\n }\n\n // Does this feed show `symbol` \u2014 as the page's active one, or pinned?\n _watches(symbol: string | undefined): boolean {\n if (!symbol) return false;\n return symbol === this._activeSymbol || this._extraSymbols.has(symbol);\n }\n\n // ---------------------------------------------------------------- the list\n\n _renderMy(): void {\n this._myGrid?.setRows(this.myTrades.map(t => this._stamp(t)));\n }\n\n _renderExtras(): void {\n // Every path that changes the pinned set comes through here, so the\n // symbol column tracks the set without a second call at each site.\n this._syncSymbolColumn();\n if (!this.extrasEl) return;\n if (this._extraSymbols.size === 0) {\n this.extrasEl.replaceChildren();\n this.extrasEl.setAttribute('hidden', '');\n return;\n }\n this.extrasEl.removeAttribute('hidden');\n const remove = this._host.t('Remove');\n const chips = [...this._extraSymbols].map((symbol) => {\n const button = makeIconButton('tf-extra-rm', remove, 'bi-x', { type: 'button' });\n button.addEventListener('click', (e) => {\n e.preventDefault();\n e.stopPropagation();\n void this.removeExtraSymbol(symbol);\n });\n return makeElement('span', 'tf-extra-chip', { role: 'listitem' }, [\n makeElement('span', 'tf-extra-sym', {}, [symbol]),\n button,\n ]);\n });\n this.extrasEl.replaceChildren(...chips);\n }\n\n // --------------------------------------------------------------- the views\n\n _bindTabs(): void {\n this._tabsEl?.addEventListener('click', (e) => {\n const button = (e.target as Element | null)?.closest('.tf-tab') as HTMLElement | null;\n if (!button) return;\n this._selectTab(button.dataset.tab || 'market');\n });\n }\n\n _selectTab(tab: string): void {\n this.tab = tab === 'my' ? 'my' : 'market';\n this._tabsEl?.querySelectorAll('.tf-tab').forEach((b) => {\n const selected = (b as HTMLElement).dataset.tab === this.tab;\n b.classList.toggle('active', selected);\n b.setAttribute('aria-selected', selected ? 'true' : 'false');\n });\n this.rootEl.classList.toggle('tf-tab-market', this.tab === 'market');\n this.rootEl.classList.toggle('tf-tab-my', this.tab === 'my');\n // The tab is also the request: opening it is when the fills are worth\n // fetching, and the portfolio is read per use because the user can have\n // switched account under a panel that is already on screen.\n if (this.tab === 'my') void this.loadMyTrades(this._host.trading.portfolioId(), this._activeSymbol);\n else this._renderBubbles();\n }\n\n _bindViewToggle(): void {\n this._viewToggleEl?.addEventListener('click', (e) => {\n const button = (e.target as Element | null)?.closest('.tf-view-btn') as HTMLElement | null;\n if (!button) return;\n const next = button.dataset.view;\n if ((next !== 'list' && next !== 'bubbles') || next === this.view) return;\n this.view = next;\n this._host.preferences.set(TradeFeedWidget.VIEW_KEY, next);\n this._applyView();\n });\n }\n\n // Which rendering is showing is two class names on the root, not a display\n // written onto elements: a narrow viewport forces the list back on, and\n // that is a stylesheet's decision to make, not this control's.\n _applyView(): void {\n const list = this.view === 'list';\n this.rootEl.classList.toggle('tf-view-list', list);\n this.rootEl.classList.toggle('tf-view-bubbles', !list);\n this._viewToggleEl?.querySelectorAll('.tf-view-btn').forEach((b) => {\n const selected = (b as HTMLElement).dataset.view === this.view;\n b.classList.toggle('active', selected);\n b.setAttribute('aria-pressed', selected ? 'true' : 'false');\n });\n if (!list) {\n this._sizeCanvas();\n this._renderBubbles();\n }\n }\n\n // -------------------------------------------------------------- the chart\n\n // The canvas in CSS pixels, and its backing store in device pixels. Without\n // the second the chart is drawn at a third of the resolution the screen has.\n _sizeCanvas(): void {\n const canvas = this.bubbleCanvas;\n if (!canvas) return;\n const rect = canvas.getBoundingClientRect();\n const ratio = typeof devicePixelRatio === 'number' && devicePixelRatio > 0 ? devicePixelRatio : 1;\n const width = Math.max(1, Math.floor(rect.width));\n const height = Math.max(1, Math.floor(rect.height));\n if (canvas.width !== width * ratio || canvas.height !== height * ratio) {\n canvas.width = width * ratio;\n canvas.height = height * ratio;\n this._bubbleCtx?.setTransform(ratio, 0, 0, ratio, 0, 0);\n }\n this._canvasSize = { width, height };\n }\n\n // The prints as the chart reads them: direction decided once by the host,\n // and a position on the shared time axis. Newest first, as the tape holds\n // them.\n _feedTicks(): FeedTick[] {\n const presentation = this._host.presentation;\n return this.bubbleTrades.map((trade, index) => ({\n symbol: trade.symbol || '',\n side: trade.side!,\n buy: presentation.isBuy(trade.side!),\n price: Number(trade.price ?? 0),\n quantity: Number(trade.quantity ?? 0),\n time: trade.time || trade.executedAt || '',\n index,\n }));\n }\n\n // One lane per watched symbol, active first. A feed with nothing pinned is\n // one lane over everything it holds, which is also the only case where the\n // lane needs no name.\n _lanes(ticks: FeedTick[]): BubbleLane[] {\n if (this._extraSymbols.size === 0)\n return [{ symbol: this._activeSymbol || '', ticks }];\n const symbols = this._activeSymbol ? [this._activeSymbol] : [];\n for (const symbol of this._extraSymbols) if (!symbols.includes(symbol)) symbols.push(symbol);\n return symbols.map(symbol => ({ symbol, ticks: ticks.filter(t => t.symbol === symbol) }));\n }\n\n _renderBubbles(): void {\n const ctx = this._bubbleCtx;\n if (!ctx || this.view !== 'bubbles') return;\n if (!this._canvasSize) this._sizeCanvas();\n const size = this._canvasSize;\n if (!size) return;\n\n ctx.clearRect(0, 0, size.width, size.height);\n this._bubbleHits = [];\n const ticks = this._feedTicks();\n if (ticks.length === 0) return;\n\n const layout = layoutBubbles({ width: size.width, height: size.height, total: ticks.length, lanes: this._lanes(ticks) });\n const palette = this._host.presentation.canvasPalette();\n ctx.font = palette.font;\n ctx.lineWidth = 1;\n\n for (const lane of layout.lanes) {\n if (lane.separatorY !== null) {\n ctx.globalAlpha = SEPARATOR_ALPHA;\n ctx.strokeStyle = palette.grid;\n TradeFeedWidget._rule(ctx, 0, size.width, lane.separatorY);\n }\n if (lane.label) {\n ctx.globalAlpha = LABEL_ALPHA;\n ctx.fillStyle = palette.grid;\n ctx.textBaseline = 'top';\n ctx.textAlign = 'left';\n ctx.fillText(lane.symbol, lane.label.x, lane.label.y);\n }\n for (const tick of lane.ticks) {\n ctx.globalAlpha = RULE_ALPHA;\n ctx.strokeStyle = palette.grid;\n TradeFeedWidget._rule(ctx, 0, layout.axisX - 2, tick.y);\n ctx.globalAlpha = LABEL_ALPHA;\n ctx.fillStyle = palette.grid;\n ctx.textBaseline = 'middle';\n ctx.textAlign = 'left';\n ctx.fillText(tick.text, layout.labelX, tick.y);\n }\n }\n\n for (const shape of layout.bubbles) {\n const colour = shape.bubble.buy ? palette.up : palette.down;\n ctx.fillStyle = colour;\n ctx.strokeStyle = colour;\n ctx.beginPath();\n ctx.arc(shape.x, shape.y, shape.radius, 0, Math.PI * 2);\n // Filled softly and outlined firmly, so overlapping prints stay\n // countable instead of merging into one blob.\n ctx.globalAlpha = FILL_ALPHA;\n ctx.fill();\n ctx.globalAlpha = STROKE_ALPHA;\n ctx.stroke();\n this._bubbleHits.push(shape);\n }\n ctx.globalAlpha = 1;\n }\n\n static _rule(ctx: CanvasRenderingContext2D, from: number, to: number, y: number): void {\n ctx.beginPath();\n ctx.moveTo(from, y);\n ctx.lineTo(to, y);\n ctx.stroke();\n }\n\n _bindBubbleHover(): void {\n const canvas = this.bubbleCanvas;\n if (!canvas) return;\n canvas.addEventListener('mousemove', (e: MouseEvent) => {\n if (this.view !== 'bubbles') { this._hideTooltip(); return; }\n const rect = canvas.getBoundingClientRect();\n const x = e.clientX - rect.left;\n const y = e.clientY - rect.top;\n const hit = this._hitTest(x, y);\n if (hit) this._showTooltip(hit.bubble, x, y);\n else this._hideTooltip();\n });\n canvas.addEventListener('mouseleave', () => this._hideTooltip());\n }\n\n // Backwards, so the circle drawn last \u2014 the newest print, on top \u2014 is the\n // one a cursor over both picks.\n _hitTest(x: number, y: number): BubbleShape | null {\n for (let i = this._bubbleHits.length - 1; i >= 0; i--) {\n const shape = this._bubbleHits[i];\n const dx = x - shape.x;\n const dy = y - shape.y;\n const reach = Math.max(shape.radius, HOVER_SLACK);\n if (dx * dx + dy * dy <= reach * reach) return shape;\n }\n return null;\n }\n\n _showTooltip(bubble: FeedBubble, x: number, y: number): void {\n const tooltip = this._tooltipEl;\n const canvas = this.bubbleCanvas;\n if (!tooltip || !canvas) return;\n const presentation = this._host.presentation;\n const aggregated = bubble.count > 1;\n // The raw side the print carried, not a spelling this file picked:\n // wording and colouring it are the host's, whichever of the three\n // spellings its wire uses.\n const side = bubble.side;\n\n const lines: HTMLElement[] = [];\n if (this._extraSymbols.size > 0) lines.push(this._tooltipLine(this._host.t('Symbol'), bubble.symbol, ''));\n lines.push(this._tooltipLine(this._host.t('Time'), this._host.presentation.timeText(bubble.time), ''));\n lines.push(this._tooltipLine(aggregated ? this._host.t('VWAP') : this._host.t('Price'), formatPrice(bubble.price), ''));\n lines.push(this._tooltipLine(aggregated ? this._host.t('Total qty') : this._host.t('Qty'), formatQty(bubble.quantity), ''));\n lines.push(this._tooltipLine(this._host.t('Side'), presentation.sideText(side), presentation.sideClass(side)));\n if (aggregated) lines.push(this._tooltipLine(this._host.t('Trades'), String(bubble.count), ''));\n tooltip.replaceChildren(...lines);\n\n // Measured, then placed: the size is only known once it is showing, and\n // it is clamped inside the canvas so a print near the right or bottom\n // edge does not push its own tooltip out of view.\n tooltip.removeAttribute('hidden');\n const canvasRect = canvas.getBoundingClientRect();\n const parentRect = tooltip.offsetParent?.getBoundingClientRect() ?? canvasRect;\n const originX = canvasRect.left - parentRect.left;\n const originY = canvasRect.top - parentRect.top;\n const maxX = originX + canvasRect.width - tooltip.offsetWidth - TOOLTIP_MARGIN;\n const maxY = originY + canvasRect.height - tooltip.offsetHeight - TOOLTIP_MARGIN;\n tooltip.style.left = `${Math.max(0, Math.min(originX + x + TOOLTIP_OFFSET, maxX))}px`;\n tooltip.style.top = `${Math.max(0, Math.min(originY + y + TOOLTIP_OFFSET, maxY))}px`;\n }\n\n _tooltipLine(label: string, value: string, valueClass: string): HTMLElement {\n const row = makeElement('div', 'tf-bbtt-row', {}, [\n makeElement('span', 'tf-bbtt-label', {}, [label]),\n makeElement('span', 'tf-bbtt-value', {}, [value]),\n ]);\n for (const name of valueClass.split(' ')) if (name) (row.childNodes[1] as HTMLElement).classList.add(name);\n return row;\n }\n\n _hideTooltip(): void {\n this._tooltipEl?.setAttribute('hidden', '');\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["price", "price", "i", "price", "direction", "price", "direction", "price", "price", "price"]
7
7
  }