@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.
- package/LICENSE +30 -0
- package/NOTICE +21 -0
- package/README.md +240 -0
- package/dist/esm/active-orders-widget.js +382 -0
- package/dist/esm/active-orders-widget.js.map +1 -0
- package/dist/esm/control-types.js +23 -0
- package/dist/esm/control-types.js.map +1 -0
- package/dist/esm/dom.js +55 -0
- package/dist/esm/dom.js.map +1 -0
- package/dist/esm/formatters.js +69 -0
- package/dist/esm/formatters.js.map +1 -0
- package/dist/esm/index.js +12 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/positions-widget.js +258 -0
- package/dist/esm/positions-widget.js.map +1 -0
- package/dist/esm/trade-history-widget.js +197 -0
- package/dist/esm/trade-history-widget.js.map +1 -0
- package/dist/esm/trading-data.js +26 -0
- package/dist/esm/trading-data.js.map +1 -0
- package/dist/esm/trading-host.js +79 -0
- package/dist/esm/trading-host.js.map +1 -0
- package/dist/esm/watchlist-widget.js +481 -0
- package/dist/esm/watchlist-widget.js.map +1 -0
- package/dist/sstradingcontrols.js +1740 -0
- package/dist/sstradingcontrols.js.map +7 -0
- package/dist/types/active-orders-widget.d.ts +52 -0
- package/dist/types/active-orders-widget.d.ts.map +1 -0
- package/dist/types/control-types.d.ts +8 -0
- package/dist/types/control-types.d.ts.map +1 -0
- package/dist/types/dom.d.ts +6 -0
- package/dist/types/dom.d.ts.map +1 -0
- package/dist/types/formatters.d.ts +8 -0
- package/dist/types/formatters.d.ts.map +1 -0
- package/dist/types/index.d.ts +16 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/positions-widget.d.ts +37 -0
- package/dist/types/positions-widget.d.ts.map +1 -0
- package/dist/types/trade-history-widget.d.ts +25 -0
- package/dist/types/trade-history-widget.d.ts.map +1 -0
- package/dist/types/trading-data.d.ts +55 -0
- package/dist/types/trading-data.d.ts.map +1 -0
- package/dist/types/trading-host.d.ts +62 -0
- package/dist/types/trading-host.d.ts.map +1 -0
- package/dist/types/watchlist-widget.d.ts +60 -0
- package/dist/types/watchlist-widget.d.ts.map +1 -0
- package/package.json +129 -0
- package/src/active-orders-widget.ts +411 -0
- package/src/control-types.ts +24 -0
- package/src/dom.ts +68 -0
- package/src/formatters.ts +72 -0
- package/src/index.ts +52 -0
- package/src/positions-widget.ts +305 -0
- package/src/trade-history-widget.ts +221 -0
- package/src/trading-data.ts +121 -0
- package/src/trading-host.ts +300 -0
- package/src/watchlist-widget.ts +513 -0
- package/styles/theme.css +86 -0
- package/styles/trading-controls.css +469 -0
- 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
|
+
}
|