@stocksharp/trading-controls 0.1.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/LICENSE +30 -0
  2. package/NOTICE +21 -0
  3. package/README.md +240 -0
  4. package/dist/esm/active-orders-widget.js +382 -0
  5. package/dist/esm/active-orders-widget.js.map +1 -0
  6. package/dist/esm/control-types.js +23 -0
  7. package/dist/esm/control-types.js.map +1 -0
  8. package/dist/esm/dom.js +55 -0
  9. package/dist/esm/dom.js.map +1 -0
  10. package/dist/esm/formatters.js +69 -0
  11. package/dist/esm/formatters.js.map +1 -0
  12. package/dist/esm/index.js +12 -0
  13. package/dist/esm/index.js.map +1 -0
  14. package/dist/esm/positions-widget.js +258 -0
  15. package/dist/esm/positions-widget.js.map +1 -0
  16. package/dist/esm/trade-history-widget.js +197 -0
  17. package/dist/esm/trade-history-widget.js.map +1 -0
  18. package/dist/esm/trading-data.js +26 -0
  19. package/dist/esm/trading-data.js.map +1 -0
  20. package/dist/esm/trading-host.js +79 -0
  21. package/dist/esm/trading-host.js.map +1 -0
  22. package/dist/esm/watchlist-widget.js +481 -0
  23. package/dist/esm/watchlist-widget.js.map +1 -0
  24. package/dist/sstradingcontrols.js +1740 -0
  25. package/dist/sstradingcontrols.js.map +7 -0
  26. package/dist/types/active-orders-widget.d.ts +52 -0
  27. package/dist/types/active-orders-widget.d.ts.map +1 -0
  28. package/dist/types/control-types.d.ts +8 -0
  29. package/dist/types/control-types.d.ts.map +1 -0
  30. package/dist/types/dom.d.ts +6 -0
  31. package/dist/types/dom.d.ts.map +1 -0
  32. package/dist/types/formatters.d.ts +8 -0
  33. package/dist/types/formatters.d.ts.map +1 -0
  34. package/dist/types/index.d.ts +16 -0
  35. package/dist/types/index.d.ts.map +1 -0
  36. package/dist/types/positions-widget.d.ts +37 -0
  37. package/dist/types/positions-widget.d.ts.map +1 -0
  38. package/dist/types/trade-history-widget.d.ts +25 -0
  39. package/dist/types/trade-history-widget.d.ts.map +1 -0
  40. package/dist/types/trading-data.d.ts +55 -0
  41. package/dist/types/trading-data.d.ts.map +1 -0
  42. package/dist/types/trading-host.d.ts +62 -0
  43. package/dist/types/trading-host.d.ts.map +1 -0
  44. package/dist/types/watchlist-widget.d.ts +60 -0
  45. package/dist/types/watchlist-widget.d.ts.map +1 -0
  46. package/package.json +129 -0
  47. package/src/active-orders-widget.ts +411 -0
  48. package/src/control-types.ts +24 -0
  49. package/src/dom.ts +68 -0
  50. package/src/formatters.ts +72 -0
  51. package/src/index.ts +52 -0
  52. package/src/positions-widget.ts +305 -0
  53. package/src/trade-history-widget.ts +221 -0
  54. package/src/trading-data.ts +121 -0
  55. package/src/trading-host.ts +300 -0
  56. package/src/watchlist-widget.ts +513 -0
  57. package/styles/theme.css +86 -0
  58. package/styles/trading-controls.css +469 -0
  59. package/translation-keys.json +58 -0
@@ -0,0 +1,469 @@
1
+ /* Looks for every element @stocksharp/trading-controls renders.
2
+ *
3
+ * The controls emit class names and nothing else — no inline style, no colour
4
+ * literal, no measurement in TypeScript. This file is the other half of that
5
+ * bargain, and shipping it is what lets the package render outside the
6
+ * application it was extracted from.
7
+ *
8
+ * It reads its colours, fonts and metrics from `--t-*` custom properties and
9
+ * declares none of them. `styles/theme.css` carries a working default; a host
10
+ * with a design system declares the same names itself and skips that file. The
11
+ * full list is in the README, and `tools/check-style-contract.mjs` fails if a
12
+ * rule here reads a property the theme does not declare.
13
+ *
14
+ * Load order matters in one direction only: a host that wants to override a
15
+ * rule (not just a token) must load its own stylesheet AFTER this one.
16
+ */
17
+
18
+ /* ---------------------------------------------------------------- keyframes */
19
+
20
+ /* The panel fades in as it is docked, so a restored layout does not appear as
21
+ a hard flash of four tables at once. */
22
+ @keyframes fadeIn {
23
+ from { opacity: 0; transform: translateY(4px); }
24
+ to { opacity: 1; transform: translateY(0); }
25
+ }
26
+
27
+ /* A price cell that changed. The background is what animates, not the text,
28
+ because the text is the number the user is trying to read. */
29
+ @keyframes flashGreen {
30
+ 0% { background: var(--t-green-flash); }
31
+ 100% { background: transparent; }
32
+ }
33
+
34
+ @keyframes flashRed {
35
+ 0% { background: var(--t-red-flash); }
36
+ 100% { background: transparent; }
37
+ }
38
+
39
+ /* ------------------------------------------------------------- panel shell */
40
+
41
+ /* Emitted by `makePanelRoot`. Every control is one of these, and the modifier
42
+ class next to it (`positions-panel`, `watchlist-panel`, …) is what a host
43
+ keys any per-control override off. */
44
+ .terminal-panel {
45
+ background: var(--t-panel);
46
+ overflow: hidden;
47
+ display: flex;
48
+ flex-direction: column;
49
+ min-height: 0;
50
+ animation: fadeIn 0.3s ease both;
51
+ }
52
+
53
+ .panel-header {
54
+ display: flex;
55
+ align-items: center;
56
+ justify-content: space-between;
57
+ padding: 4px 10px;
58
+ font-size: 10px;
59
+ font-weight: 600;
60
+ text-transform: uppercase;
61
+ letter-spacing: 0.8px;
62
+ color: var(--t-text-dim);
63
+ background: var(--t-header);
64
+ border-bottom: 1px solid var(--t-border);
65
+ flex-shrink: 0;
66
+ /* A docking host commonly uses the header as a drag handle. Without
67
+ user-select:none the browser starts a text selection on press and the
68
+ drag never takes off. */
69
+ user-select: none;
70
+ -webkit-user-select: none;
71
+ }
72
+
73
+ /* An input lifted into the header (the watchlist's search box) still has to be
74
+ selectable, so it opts back in. */
75
+ .panel-header input,
76
+ .panel-header textarea {
77
+ user-select: auto;
78
+ -webkit-user-select: auto;
79
+ }
80
+
81
+ /* The body soaks up whatever the header left and scrolls when the table
82
+ overflows. `panel-body-with-rail` turns it into a row — table on the left,
83
+ action rail pinned right — and moves the overflow inward so the rail stays
84
+ put while the table scrolls under it. */
85
+ .terminal-panel > .panel-body {
86
+ flex: 1;
87
+ min-height: 0;
88
+ overflow: auto;
89
+ padding: 0;
90
+ position: relative;
91
+ }
92
+
93
+ .terminal-panel > .panel-body.panel-body-with-rail {
94
+ display: flex;
95
+ flex-direction: row;
96
+ overflow: hidden;
97
+ }
98
+
99
+ .panel-body-content {
100
+ flex: 1;
101
+ min-width: 0;
102
+ overflow: auto;
103
+ position: relative;
104
+ }
105
+
106
+ .panel-rail {
107
+ display: flex;
108
+ flex-direction: column;
109
+ gap: 6px;
110
+ padding: 8px 6px;
111
+ align-items: center;
112
+ flex-shrink: 0;
113
+ background: var(--t-header);
114
+ border-left: 1px solid var(--t-border);
115
+ }
116
+
117
+ /* On a phone the rail is too cramped and squeezes the table further. Hide it:
118
+ the per-row × in orders and positions still lets the user act on a single
119
+ row, and a rail that only refreshes simply goes away.
120
+
121
+ Two clauses so it fires for portrait phones (≤768px wide) and for landscape
122
+ phones as well — one of those is ~932×430, wider than 768 but too short for
123
+ the desktop layout. */
124
+ @media (max-width: 768px), (max-height: 500px) {
125
+ .panel-rail { display: none; }
126
+ }
127
+
128
+ /* --------------------------------------------------------------- icon button */
129
+
130
+ /* Emitted by `makeIconButton`: the close, refresh, export and cancel-all
131
+ buttons in the panel chrome, and the per-row actions inside the tables. */
132
+ .bt-icon-btn {
133
+ width: 22px;
134
+ height: 22px;
135
+ display: inline-flex;
136
+ align-items: center;
137
+ justify-content: center;
138
+ background: transparent;
139
+ border: 1px solid var(--t-border);
140
+ color: var(--t-text-dim);
141
+ border-radius: var(--t-radius);
142
+ cursor: pointer;
143
+ padding: 0;
144
+ transition: all var(--t-transition);
145
+ }
146
+
147
+ .bt-icon-btn i { font-size: 12px; line-height: 1; }
148
+
149
+ .bt-icon-btn:hover:not(:disabled) {
150
+ color: var(--t-text-bright);
151
+ border-color: var(--t-text-dim);
152
+ }
153
+
154
+ .bt-icon-btn:disabled {
155
+ opacity: 0.35;
156
+ cursor: not-allowed;
157
+ }
158
+
159
+ .bt-icon-btn.bt-icon-cancel:hover:not(:disabled),
160
+ .bt-icon-btn.bt-icon-cancel-all:hover {
161
+ color: var(--t-red);
162
+ border-color: var(--t-red);
163
+ }
164
+
165
+ /* Reverse a position — amber, to read as "destructive but not a cancel". */
166
+ .bt-icon-btn.bt-icon-reverse:hover {
167
+ color: var(--t-orange);
168
+ border-color: var(--t-orange);
169
+ }
170
+
171
+ /* The per-row action cell in the positions table: two icon buttons side by
172
+ side, centred. */
173
+ .position-actions {
174
+ display: flex;
175
+ gap: 4px;
176
+ justify-content: center;
177
+ }
178
+
179
+ /* ------------------------------------------------------------------- tables */
180
+
181
+ /* The blotter table skin — active orders, trade history, positions. The
182
+ watchlist is deliberately not one of these; it is denser and has its own
183
+ skin further down. */
184
+ .terminal-table {
185
+ width: 100%;
186
+ font-size: 11px;
187
+ font-family: var(--t-mono);
188
+ border-collapse: collapse;
189
+ }
190
+
191
+ .terminal-table thead th {
192
+ padding: 4px 6px;
193
+ font-weight: 600;
194
+ color: var(--t-text-dim);
195
+ text-transform: uppercase;
196
+ font-size: 10px;
197
+ letter-spacing: 0.3px;
198
+ border-bottom: 1px solid var(--t-border);
199
+ position: sticky;
200
+ top: 0;
201
+ background: var(--t-panel);
202
+ white-space: nowrap;
203
+ }
204
+
205
+ /* A column the grid declared a `value` for is sortable, and it says so by
206
+ stamping `data-sort`. Clicking cycles asc -> desc -> natural. */
207
+ .terminal-table thead th[data-sort] { cursor: pointer; user-select: none; }
208
+ .terminal-table thead th[data-sort]:hover { color: var(--t-text-bright); }
209
+ .terminal-table thead th.sort-asc::after { content: ' \25B2'; font-size: 7px; }
210
+ .terminal-table thead th.sort-desc::after { content: ' \25BC'; font-size: 7px; }
211
+
212
+ .terminal-table tbody td {
213
+ padding: 4px 6px;
214
+ /* --t-border at half strength. Spelled as a mix rather than the resolved
215
+ rgba() so it follows the token into a light theme; the literal it
216
+ replaces stayed dark-grey on white. */
217
+ border-bottom: 1px solid color-mix(in srgb, var(--t-border) 50%, transparent);
218
+ white-space: nowrap;
219
+ }
220
+
221
+ .terminal-table tbody tr { transition: background var(--t-transition); }
222
+ .terminal-table tbody tr:hover { background: var(--t-hover); }
223
+
224
+ /* Numeric id columns (#42) — dim, monospace, tabular so they line up. */
225
+ .terminal-table .mono-id {
226
+ color: var(--t-text-dim);
227
+ font-variant-numeric: tabular-nums;
228
+ }
229
+
230
+ /* The cash balance pinned above the positions. A pinned grid row supplies its
231
+ own cells with class names rather than inline styles, so its emphasis lives
232
+ here with the rest of the table. */
233
+ .terminal-table tbody tr.balance-row .balance-ccy { color: var(--t-accent); font-weight: 600; }
234
+ .terminal-table tbody tr.balance-row .balance-available { color: var(--t-text-bright); }
235
+ .terminal-table tbody tr.balance-row .balance-locked { color: var(--t-text-dim); }
236
+ .terminal-table tbody tr.balance-row .balance-total { color: var(--t-text-bright); font-weight: 600; }
237
+
238
+ /* A cell the user can double-click to edit in place. */
239
+ .cell-editable { cursor: pointer; border-bottom: 1px dashed var(--t-text-dim); }
240
+ .cell-editable:hover { color: var(--t-accent); }
241
+
242
+ .inline-edit-input {
243
+ width: 80px;
244
+ background: var(--t-bg);
245
+ border: 1px solid var(--t-accent);
246
+ color: var(--t-text-bright);
247
+ font-family: var(--t-mono);
248
+ font-size: 11px;
249
+ padding: 1px 4px;
250
+ outline: none;
251
+ border-radius: var(--t-radius);
252
+ }
253
+
254
+ /* Terminal-state rows in active orders. The blotter keeps a row for the
255
+ order's whole lifetime so the user can read its final state instead of
256
+ guessing why it vanished, and these dim it so the live rows stay prominent.
257
+ The tints are deliberately their own hues rather than the direction colours:
258
+ a filled order is not "up" and a cancelled one is not "down". */
259
+ .terminal-table tbody tr.order-rejected td {
260
+ color: var(--t-text-dim);
261
+ background: rgba(220, 53, 69, 0.07);
262
+ }
263
+
264
+ .terminal-table tbody tr.order-cancelled td {
265
+ color: var(--t-text-dim);
266
+ background: rgba(108, 117, 125, 0.10);
267
+ }
268
+
269
+ .terminal-table tbody tr.order-filled td {
270
+ color: var(--t-text-dim);
271
+ background: rgba(40, 167, 69, 0.07);
272
+ }
273
+
274
+ /* The ? next to a rejected order's status; the reason is its tooltip. */
275
+ .reject-reason-icon {
276
+ color: var(--t-warning);
277
+ cursor: help;
278
+ margin-left: 4px;
279
+ }
280
+
281
+ .reject-reason-icon:hover { color: var(--t-accent); }
282
+
283
+ /* ------------------------------------------------------- direction and sign */
284
+
285
+ /* These four are the only class names in this file that no control emits.
286
+ They come back out of the host's own `TradingPresentation.sideClass` /
287
+ `pnlClass`, and a control puts the answer on the cell without looking at it —
288
+ so the agreement is between the host and this file, with the control only
289
+ carrying the string between them. The names are stated as the port's
290
+ `PRESENTATION_CLASSES`, and `tools/check-style-contract.mjs` asserts that
291
+ every one of them is styled here. A host on its own palette may answer with
292
+ different names, and then styles those itself. */
293
+ .pnl-positive { color: var(--t-green); font-weight: 600; }
294
+ .pnl-negative { color: var(--t-red); font-weight: 600; }
295
+ .side-buy { color: var(--t-green); font-weight: 600; }
296
+ .side-sell { color: var(--t-red); font-weight: 600; }
297
+
298
+ /* ------------------------------------------------------ classes from the grid */
299
+
300
+ /* Two class names come out of @stocksharp/grids rather than out of a column
301
+ declaration, so a page that styles the controls has to style them too:
302
+ `grid-empty` on the cell that spans an empty table, and `visually-hidden` on
303
+ a header whose caption is for screen readers only (an actions column). Both
304
+ table skins need the first. */
305
+ .terminal-table tbody td.grid-empty,
306
+ .watchlist-table tbody td.grid-empty {
307
+ text-align: center;
308
+ color: var(--t-text-dim);
309
+ }
310
+
311
+ /* Spelled the way Bootstrap spells it. A page that already loads Bootstrap has
312
+ this rule twice with the same meaning, which is harmless; a page that does
313
+ not still gets accessible headers. */
314
+ .visually-hidden {
315
+ position: absolute;
316
+ width: 1px;
317
+ height: 1px;
318
+ padding: 0;
319
+ margin: -1px;
320
+ overflow: hidden;
321
+ clip: rect(0, 0, 0, 0);
322
+ white-space: nowrap;
323
+ border: 0;
324
+ }
325
+
326
+ /* ---------------------------------------------------------------- watchlist */
327
+
328
+ .watchlist-panel {
329
+ display: flex;
330
+ flex-direction: column;
331
+ min-height: 0;
332
+ overflow: hidden;
333
+ }
334
+
335
+ /* A row of its own rather than a child of the header, because a docking host
336
+ may lift it into a tab strip separately. */
337
+ .watchlist-search-row {
338
+ padding: 4px 8px;
339
+ border-bottom: 1px solid var(--t-border);
340
+ flex-shrink: 0;
341
+ }
342
+
343
+ .watchlist-search-input {
344
+ width: 100%;
345
+ background: var(--t-bg);
346
+ border: 1px solid var(--t-border);
347
+ color: var(--t-text-bright);
348
+ font-size: 11px;
349
+ font-family: var(--t-font);
350
+ padding: 3px 8px;
351
+ }
352
+
353
+ .watchlist-search-input:focus {
354
+ outline: none;
355
+ border-color: var(--t-accent);
356
+ box-shadow: 0 0 0 2px var(--t-accent-glow-soft);
357
+ }
358
+
359
+ /* Two built-in tabs plus one per instrument category, so the strip scrolls
360
+ rather than wraps. */
361
+ .watchlist-tabs {
362
+ display: flex;
363
+ gap: 2px;
364
+ padding: 3px 6px;
365
+ background: var(--t-header);
366
+ border-bottom: 1px solid var(--t-border);
367
+ flex-shrink: 0;
368
+ overflow-x: auto;
369
+ -webkit-overflow-scrolling: touch;
370
+ }
371
+
372
+ .wl-tab {
373
+ background: transparent;
374
+ border: 1px solid transparent;
375
+ color: var(--t-text-dim);
376
+ font-size: 10px;
377
+ font-weight: 600;
378
+ padding: 2px 8px;
379
+ border-radius: var(--t-radius);
380
+ cursor: pointer;
381
+ font-family: var(--t-font);
382
+ transition: all var(--t-transition);
383
+ white-space: nowrap;
384
+ }
385
+
386
+ .wl-tab:hover { color: var(--t-text-bright); background: var(--t-hover); }
387
+
388
+ .wl-tab.active {
389
+ color: var(--t-accent-text);
390
+ background: var(--t-accent);
391
+ border-color: var(--t-accent);
392
+ }
393
+
394
+ .watchlist-body {
395
+ flex: 1;
396
+ min-height: 0;
397
+ overflow-y: auto;
398
+ overflow-x: hidden;
399
+ }
400
+
401
+ .watchlist-table {
402
+ width: 100%;
403
+ font-size: 11px;
404
+ font-family: var(--t-mono);
405
+ border-collapse: collapse;
406
+ }
407
+
408
+ .watchlist-table thead th {
409
+ position: sticky;
410
+ top: 0;
411
+ background: var(--t-panel);
412
+ padding: 3px 6px;
413
+ font-weight: 600;
414
+ color: var(--t-text-dim);
415
+ text-transform: uppercase;
416
+ font-size: 9px;
417
+ letter-spacing: 0.3px;
418
+ border-bottom: 1px solid var(--t-border);
419
+ cursor: pointer;
420
+ user-select: none;
421
+ font-family: var(--t-font);
422
+ white-space: nowrap;
423
+ }
424
+
425
+ .watchlist-table thead th.ta-right { text-align: right; }
426
+ .watchlist-table thead th:hover { color: var(--t-text-bright); }
427
+ .watchlist-table thead th.sort-asc::after { content: ' \25B2'; font-size: 7px; }
428
+ .watchlist-table thead th.sort-desc::after { content: ' \25BC'; font-size: 7px; }
429
+
430
+ .watchlist-table tbody tr {
431
+ cursor: pointer;
432
+ transition: background var(--t-transition);
433
+ }
434
+
435
+ .watchlist-table tbody tr:hover { background: var(--t-hover); }
436
+ .watchlist-table tbody tr.wl-current { background: var(--t-accent-glow-soft); }
437
+
438
+ .watchlist-table tbody td {
439
+ padding: 2px 6px;
440
+ /* --t-border at about a third, for the same reason as the blotter rule. */
441
+ border-bottom: 1px solid color-mix(in srgb, var(--t-border) 35%, transparent);
442
+ line-height: 1.6;
443
+ white-space: nowrap;
444
+ }
445
+
446
+ .wl-sym-cell { display: flex; align-items: center; gap: 4px; }
447
+
448
+ .wl-fav {
449
+ background: transparent;
450
+ border: none;
451
+ color: var(--t-border);
452
+ font-size: 11px;
453
+ padding: 0 2px;
454
+ line-height: 1;
455
+ cursor: pointer;
456
+ transition: color var(--t-transition);
457
+ }
458
+
459
+ .wl-fav:hover { color: var(--t-accent-hover); }
460
+ .wl-fav.active { color: var(--t-accent); }
461
+ .wl-sym { color: var(--t-text-bright); font-weight: 500; }
462
+
463
+ .wl-last { text-align: right; color: var(--t-text); transition: background 0.3s ease; }
464
+ .wl-last.flash-up { animation: flashGreen 0.5s ease; }
465
+ .wl-last.flash-down { animation: flashRed 0.5s ease; }
466
+
467
+ .wl-chg { text-align: right; font-weight: 600; }
468
+ .wl-chg.up { color: var(--t-green); }
469
+ .wl-chg.down { color: var(--t-red); }
@@ -0,0 +1,58 @@
1
+ {
2
+ "$comment": "Generated from src/ by tools/check-translation-keys.mjs -- do not edit by hand. Every string here is passed to TradingHost.t(); a host that answers none of them renders these literally to the user. See README.md (\"Text arrives through t()\").",
3
+ "count": 52,
4
+ "keys": [
5
+ "Actions",
6
+ "ActiveOrders",
7
+ "ActiveOrdersActions",
8
+ "ActiveOrdersList",
9
+ "All",
10
+ "AvgPrice",
11
+ "Cancel order",
12
+ "CancelAll",
13
+ "CancelAllOrders",
14
+ "Change24hPct",
15
+ "Close position",
16
+ "Close position on {0}",
17
+ "ClosePanel",
18
+ "Current",
19
+ "Dismiss",
20
+ "ExportToExcel",
21
+ "Favorite",
22
+ "Favorites",
23
+ "ID",
24
+ "Last",
25
+ "Locked: ${0}",
26
+ "Markets",
27
+ "MKT",
28
+ "No instruments",
29
+ "No positions",
30
+ "No trade history",
31
+ "NoActiveOrders",
32
+ "OpenOrders",
33
+ "OpenPositions",
34
+ "Order",
35
+ "PnL",
36
+ "Positions",
37
+ "PositionsActions",
38
+ "Price",
39
+ "Qty",
40
+ "Refresh",
41
+ "Reverse position",
42
+ "Reverse position on {0}",
43
+ "SearchInstruments",
44
+ "Side",
45
+ "Status",
46
+ "Stop",
47
+ "Sym",
48
+ "Symbol",
49
+ "Time",
50
+ "TradeHistory",
51
+ "TradeHistoryActions",
52
+ "TradeHistoryList",
53
+ "Type",
54
+ "USD",
55
+ "Watchlist",
56
+ "WatchlistFilter"
57
+ ]
58
+ }