@stocksharp/trading-controls 0.1.0 → 1.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/README.md +92 -6
- package/dist/esm/active-orders-widget.js +9 -0
- package/dist/esm/active-orders-widget.js.map +1 -1
- package/dist/esm/control-types.js +3 -0
- package/dist/esm/control-types.js.map +1 -1
- package/dist/esm/grid-menu.js +54 -0
- package/dist/esm/grid-menu.js.map +1 -0
- package/dist/esm/index.js +8 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/order-entry-widget.js +654 -0
- package/dist/esm/order-entry-widget.js.map +1 -0
- package/dist/esm/orderbook-depth.js +51 -0
- package/dist/esm/orderbook-depth.js.map +1 -0
- package/dist/esm/orderbook-widget.js +792 -0
- package/dist/esm/orderbook-widget.js.map +1 -0
- package/dist/esm/positions-widget.js +9 -0
- package/dist/esm/positions-widget.js.map +1 -1
- package/dist/esm/trade-history-widget.js +8 -0
- package/dist/esm/trade-history-widget.js.map +1 -1
- package/dist/esm/tradefeed-aggregator.js +58 -0
- package/dist/esm/tradefeed-aggregator.js.map +1 -0
- package/dist/esm/tradefeed-bubbles.js +131 -0
- package/dist/esm/tradefeed-bubbles.js.map +1 -0
- package/dist/esm/tradefeed-widget.js +692 -0
- package/dist/esm/tradefeed-widget.js.map +1 -0
- package/dist/esm/trading-host.js +2 -0
- package/dist/esm/trading-host.js.map +1 -1
- package/dist/esm/watchlist-widget.js +2 -0
- package/dist/esm/watchlist-widget.js.map +1 -1
- package/dist/sstradingcontrols.js +3346 -25
- package/dist/sstradingcontrols.js.map +3 -3
- package/dist/types/active-orders-widget.d.ts.map +1 -1
- package/dist/types/control-types.d.ts +3 -0
- package/dist/types/control-types.d.ts.map +1 -1
- package/dist/types/grid-menu.d.ts +4 -0
- package/dist/types/grid-menu.d.ts.map +1 -0
- package/dist/types/index.d.ts +13 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/order-entry-widget.d.ts +86 -0
- package/dist/types/order-entry-widget.d.ts.map +1 -0
- package/dist/types/orderbook-depth.d.ts +19 -0
- package/dist/types/orderbook-depth.d.ts.map +1 -0
- package/dist/types/orderbook-widget.d.ts +105 -0
- package/dist/types/orderbook-widget.d.ts.map +1 -0
- package/dist/types/positions-widget.d.ts.map +1 -1
- package/dist/types/trade-history-widget.d.ts.map +1 -1
- package/dist/types/tradefeed-aggregator.d.ts +15 -0
- package/dist/types/tradefeed-aggregator.d.ts.map +1 -0
- package/dist/types/tradefeed-bubbles.d.ts +39 -0
- package/dist/types/tradefeed-bubbles.d.ts.map +1 -0
- package/dist/types/tradefeed-widget.d.ts +78 -0
- package/dist/types/tradefeed-widget.d.ts.map +1 -0
- package/dist/types/trading-data.d.ts +22 -0
- package/dist/types/trading-data.d.ts.map +1 -1
- package/dist/types/trading-host.d.ts +8 -0
- package/dist/types/trading-host.d.ts.map +1 -1
- package/dist/types/watchlist-widget.d.ts.map +1 -1
- package/package.json +21 -2
- package/screenshots/panels.jpg +0 -0
- package/src/active-orders-widget.ts +9 -0
- package/src/control-types.ts +3 -0
- package/src/grid-menu.ts +67 -0
- package/src/index.ts +31 -0
- package/src/order-entry-widget.ts +733 -0
- package/src/orderbook-depth.ts +99 -0
- package/src/orderbook-widget.ts +896 -0
- package/src/positions-widget.ts +9 -0
- package/src/trade-history-widget.ts +8 -0
- package/src/tradefeed-aggregator.ts +95 -0
- package/src/tradefeed-bubbles.ts +194 -0
- package/src/tradefeed-widget.ts +744 -0
- package/src/trading-data.ts +168 -121
- package/src/trading-host.ts +326 -300
- package/src/watchlist-widget.ts +2 -0
- package/styles/theme.css +23 -0
- package/styles/trading-controls.css +911 -0
- package/translation-keys.json +103 -2
|
@@ -308,6 +308,12 @@
|
|
|
308
308
|
color: var(--t-text-dim);
|
|
309
309
|
}
|
|
310
310
|
|
|
311
|
+
/* The grid marks selected rows (the blotters switch selection on); a tint
|
|
312
|
+
under the cells' own colours, so a red Sell stays red while marked. */
|
|
313
|
+
.terminal-table tbody tr.is-selected td {
|
|
314
|
+
background: color-mix(in srgb, var(--t-accent) 16%, transparent);
|
|
315
|
+
}
|
|
316
|
+
|
|
311
317
|
/* Spelled the way Bootstrap spells it. A page that already loads Bootstrap has
|
|
312
318
|
this rule twice with the same meaning, which is harmless; a page that does
|
|
313
319
|
not still gets accessible headers. */
|
|
@@ -323,6 +329,140 @@
|
|
|
323
329
|
border: 0;
|
|
324
330
|
}
|
|
325
331
|
|
|
332
|
+
/* The grid's context menu and its filter rule dialog. The grid draws them at
|
|
333
|
+
the pointer and names their parts; the controls are what put them on screen
|
|
334
|
+
(see grid-menu.ts), so their look ships here with everything else the
|
|
335
|
+
controls emit. Position and coordinates are the grid's inline business —
|
|
336
|
+
these rules only dress the box. */
|
|
337
|
+
.grid-menu {
|
|
338
|
+
z-index: 100;
|
|
339
|
+
min-width: 190px;
|
|
340
|
+
padding: 4px;
|
|
341
|
+
background: var(--t-panel);
|
|
342
|
+
border: 1px solid var(--t-border);
|
|
343
|
+
border-radius: var(--t-radius);
|
|
344
|
+
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4);
|
|
345
|
+
font-family: var(--t-font);
|
|
346
|
+
font-size: 12px;
|
|
347
|
+
color: var(--t-text);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
.grid-menu-item {
|
|
351
|
+
padding: 5px 10px;
|
|
352
|
+
border-radius: var(--t-radius);
|
|
353
|
+
cursor: pointer;
|
|
354
|
+
white-space: nowrap;
|
|
355
|
+
overflow: hidden;
|
|
356
|
+
text-overflow: ellipsis;
|
|
357
|
+
max-width: 320px;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
.grid-menu-item:hover { background: var(--t-hover); color: var(--t-text-bright); }
|
|
361
|
+
|
|
362
|
+
.grid-menu-item.is-disabled { color: var(--t-text-dim); opacity: 0.6; cursor: default; }
|
|
363
|
+
.grid-menu-item.is-disabled:hover { background: transparent; color: var(--t-text-dim); }
|
|
364
|
+
|
|
365
|
+
.grid-menu-item.is-checked::before { content: '· '; color: var(--t-accent); }
|
|
366
|
+
|
|
367
|
+
.grid-menu-separator {
|
|
368
|
+
height: 1px;
|
|
369
|
+
margin: 4px 6px;
|
|
370
|
+
background: var(--t-border);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
.grid-filter-dialog {
|
|
374
|
+
z-index: 100;
|
|
375
|
+
display: flex;
|
|
376
|
+
flex-direction: column;
|
|
377
|
+
gap: 8px;
|
|
378
|
+
min-width: 240px;
|
|
379
|
+
padding: 10px;
|
|
380
|
+
background: var(--t-panel);
|
|
381
|
+
border: 1px solid var(--t-border);
|
|
382
|
+
border-radius: var(--t-radius);
|
|
383
|
+
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4);
|
|
384
|
+
font-family: var(--t-font);
|
|
385
|
+
font-size: 12px;
|
|
386
|
+
color: var(--t-text);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
.grid-filter-dialog-title {
|
|
390
|
+
font-size: 11px;
|
|
391
|
+
font-weight: 600;
|
|
392
|
+
color: var(--t-text-dim);
|
|
393
|
+
text-transform: uppercase;
|
|
394
|
+
letter-spacing: 0.05em;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
.grid-filter-dialog-row { display: flex; align-items: center; gap: 6px; }
|
|
398
|
+
|
|
399
|
+
.grid-filter-dialog-op,
|
|
400
|
+
.grid-filter-dialog-value {
|
|
401
|
+
padding: 4px 8px;
|
|
402
|
+
background: var(--t-bg);
|
|
403
|
+
border: 1px solid var(--t-border);
|
|
404
|
+
border-radius: var(--t-radius);
|
|
405
|
+
color: var(--t-text);
|
|
406
|
+
font-family: var(--t-font);
|
|
407
|
+
font-size: 12px;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
.grid-filter-dialog-value { flex: 1; min-width: 0; }
|
|
411
|
+
|
|
412
|
+
.grid-filter-dialog-op:focus,
|
|
413
|
+
.grid-filter-dialog-value:focus {
|
|
414
|
+
outline: none;
|
|
415
|
+
border-color: var(--t-accent);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/* The set filter's pick list: every value the column holds, scrollable when
|
|
419
|
+
the column holds many. */
|
|
420
|
+
.grid-filter-dialog-values {
|
|
421
|
+
display: flex;
|
|
422
|
+
flex-direction: column;
|
|
423
|
+
gap: 2px;
|
|
424
|
+
max-height: 200px;
|
|
425
|
+
overflow-y: auto;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
.grid-filter-dialog-values-item {
|
|
429
|
+
display: flex;
|
|
430
|
+
align-items: center;
|
|
431
|
+
gap: 6px;
|
|
432
|
+
padding: 3px 6px;
|
|
433
|
+
border-radius: var(--t-radius);
|
|
434
|
+
cursor: pointer;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
.grid-filter-dialog-values-item:hover { background: var(--t-hover); }
|
|
438
|
+
|
|
439
|
+
.grid-filter-dialog-actions {
|
|
440
|
+
display: flex;
|
|
441
|
+
justify-content: flex-end;
|
|
442
|
+
gap: 6px;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
.grid-filter-dialog-actions button {
|
|
446
|
+
padding: 4px 12px;
|
|
447
|
+
background: transparent;
|
|
448
|
+
border: 1px solid var(--t-border);
|
|
449
|
+
border-radius: var(--t-radius);
|
|
450
|
+
color: var(--t-text);
|
|
451
|
+
font-family: var(--t-font);
|
|
452
|
+
font-size: 12px;
|
|
453
|
+
cursor: pointer;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
.grid-filter-dialog-actions button:hover { border-color: var(--t-accent); color: var(--t-accent); }
|
|
457
|
+
|
|
458
|
+
.grid-filter-dialog-apply {
|
|
459
|
+
background: var(--t-accent);
|
|
460
|
+
border-color: var(--t-accent);
|
|
461
|
+
color: var(--t-accent-text);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
.grid-filter-dialog-apply:hover { background: var(--t-accent-hover); color: var(--t-accent-text); }
|
|
465
|
+
|
|
326
466
|
/* ---------------------------------------------------------------- watchlist */
|
|
327
467
|
|
|
328
468
|
.watchlist-panel {
|
|
@@ -467,3 +607,774 @@
|
|
|
467
607
|
.wl-chg { text-align: right; font-weight: 600; }
|
|
468
608
|
.wl-chg.up { color: var(--t-green); }
|
|
469
609
|
.wl-chg.down { color: var(--t-red); }
|
|
610
|
+
|
|
611
|
+
/* --------------------------------------------------------------- order book */
|
|
612
|
+
|
|
613
|
+
.orderbook-panel {
|
|
614
|
+
display: flex;
|
|
615
|
+
flex-direction: column;
|
|
616
|
+
min-height: 0;
|
|
617
|
+
overflow: hidden;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
/* Every setting the ladder has lives in its header, so the header packs from
|
|
621
|
+
the left and the two buttons at the end take the room that is left. */
|
|
622
|
+
.orderbook-panel > .panel-header {
|
|
623
|
+
justify-content: flex-start;
|
|
624
|
+
gap: 6px;
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
.ob-symbol-label {
|
|
628
|
+
cursor: pointer;
|
|
629
|
+
font-weight: 600;
|
|
630
|
+
color: var(--t-text-bright);
|
|
631
|
+
border-bottom: 1px dashed var(--t-text-dim);
|
|
632
|
+
padding-bottom: 1px;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
.ob-symbol-label:hover {
|
|
636
|
+
color: var(--t-accent);
|
|
637
|
+
border-bottom-color: var(--t-accent);
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
.ob-add-btn { margin-left: auto; }
|
|
641
|
+
|
|
642
|
+
/* Depth, row layout, side order and the depth chart share one skin, because
|
|
643
|
+
they are one row of settings rather than four kinds of button. */
|
|
644
|
+
.ob-depth-selector,
|
|
645
|
+
.ob-view-selector {
|
|
646
|
+
display: flex;
|
|
647
|
+
gap: 2px;
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
.btn-ob-depth,
|
|
651
|
+
.btn-ob-view,
|
|
652
|
+
.btn-ob-invert,
|
|
653
|
+
.btn-ob-depthtoggle {
|
|
654
|
+
display: inline-flex;
|
|
655
|
+
align-items: center;
|
|
656
|
+
justify-content: center;
|
|
657
|
+
min-width: 22px;
|
|
658
|
+
background: transparent;
|
|
659
|
+
border: 1px solid var(--t-border);
|
|
660
|
+
color: var(--t-text-dim);
|
|
661
|
+
font-family: var(--t-mono);
|
|
662
|
+
font-size: 9px;
|
|
663
|
+
line-height: 1.6;
|
|
664
|
+
padding: 0 5px;
|
|
665
|
+
border-radius: var(--t-radius);
|
|
666
|
+
cursor: pointer;
|
|
667
|
+
transition: all var(--t-transition);
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
.btn-ob-view .bi,
|
|
671
|
+
.btn-ob-invert .bi,
|
|
672
|
+
.btn-ob-depthtoggle .bi { font-size: 11px; line-height: 1; }
|
|
673
|
+
|
|
674
|
+
.btn-ob-depth:hover,
|
|
675
|
+
.btn-ob-view:hover,
|
|
676
|
+
.btn-ob-invert:hover,
|
|
677
|
+
.btn-ob-depthtoggle:hover { color: var(--t-text); border-color: var(--t-text-dim); }
|
|
678
|
+
|
|
679
|
+
.btn-ob-depth.active,
|
|
680
|
+
.btn-ob-view.active,
|
|
681
|
+
.btn-ob-invert.active,
|
|
682
|
+
.btn-ob-depthtoggle.active {
|
|
683
|
+
background: var(--t-accent);
|
|
684
|
+
color: var(--t-accent-text);
|
|
685
|
+
border-color: var(--t-accent);
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
.spread-label { font-weight: 400; font-size: 10px; color: var(--t-text-dim); }
|
|
689
|
+
|
|
690
|
+
/* The depth curve. Decoration, not interaction, so clicks fall through to the
|
|
691
|
+
ladder underneath it. */
|
|
692
|
+
.ob-depth-chart {
|
|
693
|
+
display: block;
|
|
694
|
+
width: 100%;
|
|
695
|
+
height: 80px;
|
|
696
|
+
pointer-events: none;
|
|
697
|
+
background: transparent;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
.ob-hide-depth .ob-depth-chart { display: none; }
|
|
701
|
+
|
|
702
|
+
/* Captions above the levels, on the same three columns as a row so each one
|
|
703
|
+
stays over the figures it names. */
|
|
704
|
+
.ob-col-headers {
|
|
705
|
+
display: grid;
|
|
706
|
+
grid-template-columns: 1fr 1fr 1fr;
|
|
707
|
+
padding: 4px 8px;
|
|
708
|
+
font-size: 10px;
|
|
709
|
+
letter-spacing: 0.5px;
|
|
710
|
+
text-transform: uppercase;
|
|
711
|
+
color: var(--t-text-dim);
|
|
712
|
+
background: var(--t-panel);
|
|
713
|
+
border-bottom: 1px solid var(--t-border);
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
.ob-col-price { text-align: left; }
|
|
717
|
+
.ob-col-qty,
|
|
718
|
+
.ob-col-total { text-align: right; }
|
|
719
|
+
|
|
720
|
+
.orderbook-content {
|
|
721
|
+
flex: 1;
|
|
722
|
+
display: flex;
|
|
723
|
+
flex-direction: column;
|
|
724
|
+
min-height: 0;
|
|
725
|
+
overflow: hidden;
|
|
726
|
+
font-family: var(--t-mono);
|
|
727
|
+
font-size: 11px;
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
/* Natural height, so the best ask sits just above the mid line and the best bid
|
|
731
|
+
just below it. Centring each block inside its half leaves a dead band above a
|
|
732
|
+
shallow book. */
|
|
733
|
+
.orderbook-asks,
|
|
734
|
+
.orderbook-bids {
|
|
735
|
+
flex: 0 0 auto;
|
|
736
|
+
display: flex;
|
|
737
|
+
flex-direction: column;
|
|
738
|
+
min-height: 0;
|
|
739
|
+
max-height: calc(50% - 12px);
|
|
740
|
+
overflow-y: auto;
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
/* One level. The volume bar sits behind the figures, absolutely positioned, so
|
|
744
|
+
its width does not compete with the grid for room; the tint behind the whole
|
|
745
|
+
row separates a wall from its neighbours. Both widths are measured per level
|
|
746
|
+
by the control and arrive as custom properties. */
|
|
747
|
+
.ob-row {
|
|
748
|
+
display: grid;
|
|
749
|
+
grid-template-columns: 1fr 1fr 1fr;
|
|
750
|
+
padding: 1px 8px;
|
|
751
|
+
line-height: 1.7;
|
|
752
|
+
position: relative;
|
|
753
|
+
cursor: pointer;
|
|
754
|
+
transition: background 0.1s ease;
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
.ob-row.ask { background: color-mix(in srgb, var(--t-red) var(--t-ob-heat), transparent); }
|
|
758
|
+
.ob-row.bid { background: color-mix(in srgb, var(--t-green) var(--t-ob-heat), transparent); }
|
|
759
|
+
.ob-row:hover { background: var(--t-hover); }
|
|
760
|
+
|
|
761
|
+
.ob-row .price { text-align: left; font-weight: 600; position: relative; z-index: 1; }
|
|
762
|
+
.ob-row .qty { text-align: right; font-weight: 500; color: var(--t-text-dim); position: relative; z-index: 1; }
|
|
763
|
+
.ob-row .total { text-align: right; font-weight: 500; color: var(--t-text-dim); position: relative; z-index: 1; }
|
|
764
|
+
.ob-row.ask .price { color: var(--t-red); }
|
|
765
|
+
.ob-row.bid .price { color: var(--t-green); }
|
|
766
|
+
|
|
767
|
+
.ob-row .bar {
|
|
768
|
+
position: absolute;
|
|
769
|
+
top: 0;
|
|
770
|
+
bottom: 0;
|
|
771
|
+
width: var(--t-ob-bar);
|
|
772
|
+
opacity: 0.28;
|
|
773
|
+
z-index: 0;
|
|
774
|
+
pointer-events: none;
|
|
775
|
+
transition: width 0.3s ease;
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
.ob-row.ask .bar {
|
|
779
|
+
right: 0;
|
|
780
|
+
background: linear-gradient(to left, color-mix(in srgb, var(--t-red) 55%, transparent), transparent);
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
.ob-row.bid .bar {
|
|
784
|
+
left: 0;
|
|
785
|
+
background: linear-gradient(to right, color-mix(in srgb, var(--t-green) 55%, transparent), transparent);
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
.ob-row.flash-green { animation: flashGreen 0.5s ease; }
|
|
789
|
+
.ob-row.flash-red { animation: flashRed 0.5s ease; }
|
|
790
|
+
|
|
791
|
+
/* A level this session has size resting on: a stripe down the outside of the
|
|
792
|
+
row, and the resting quantity inside the size cell. */
|
|
793
|
+
.ob-row.own.bid { box-shadow: inset 3px 0 0 var(--t-green); }
|
|
794
|
+
.ob-row.own.ask { box-shadow: inset -3px 0 0 var(--t-red); }
|
|
795
|
+
|
|
796
|
+
.ob-row .own-badge {
|
|
797
|
+
display: inline-block;
|
|
798
|
+
margin: 0 4px;
|
|
799
|
+
padding: 0 4px;
|
|
800
|
+
border-radius: 2px;
|
|
801
|
+
background: color-mix(in srgb, var(--t-text-bright) 8%, transparent);
|
|
802
|
+
color: var(--t-text);
|
|
803
|
+
font-size: 10px;
|
|
804
|
+
font-weight: 600;
|
|
805
|
+
line-height: 1.4;
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
.orderbook-mid {
|
|
809
|
+
position: relative;
|
|
810
|
+
text-align: center;
|
|
811
|
+
padding: 4px;
|
|
812
|
+
font-weight: 700;
|
|
813
|
+
font-size: 13px;
|
|
814
|
+
letter-spacing: -0.3px;
|
|
815
|
+
color: var(--t-text-bright);
|
|
816
|
+
background: var(--t-bg);
|
|
817
|
+
border-top: 1px solid var(--t-border);
|
|
818
|
+
border-bottom: 1px solid var(--t-border);
|
|
819
|
+
flex-shrink: 0;
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
.ob-mid-price { font-weight: inherit; }
|
|
823
|
+
|
|
824
|
+
/* The spread reads to the right of the divider while the mid price stays
|
|
825
|
+
centred on it. */
|
|
826
|
+
.ob-mid-spread {
|
|
827
|
+
position: absolute;
|
|
828
|
+
right: 8px;
|
|
829
|
+
top: 50%;
|
|
830
|
+
transform: translateY(-50%);
|
|
831
|
+
font-weight: 400;
|
|
832
|
+
font-size: 10px;
|
|
833
|
+
letter-spacing: 0;
|
|
834
|
+
color: var(--t-text-dim);
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
/* Diagonal layout — the classic two-sided book. Same three columns, but the
|
|
838
|
+
size cell moves to the outside of each side with the price centred between
|
|
839
|
+
them; the cumulative column and the captions go, because neither reads
|
|
840
|
+
unambiguously once the two sides are mirrored. */
|
|
841
|
+
.orderbook-panel.ob-view-diagonal .ob-row { grid-template-areas: "left center right"; }
|
|
842
|
+
.orderbook-panel.ob-view-diagonal .ob-row .price { grid-area: center; text-align: center; }
|
|
843
|
+
.orderbook-panel.ob-view-diagonal .ob-row.ask .qty { grid-area: left; text-align: left; }
|
|
844
|
+
.orderbook-panel.ob-view-diagonal .ob-row.bid .qty { grid-area: right; text-align: right; }
|
|
845
|
+
.orderbook-panel.ob-view-diagonal .ob-row .total { display: none; }
|
|
846
|
+
.orderbook-panel.ob-view-diagonal .ob-col-headers { display: none; }
|
|
847
|
+
|
|
848
|
+
/* Stacked is the layout the rules above already describe; naming it is what
|
|
849
|
+
lets a panel switch back out of diagonal. */
|
|
850
|
+
.orderbook-panel.ob-view-stacked .ob-row { grid-template-areas: none; }
|
|
851
|
+
|
|
852
|
+
/* Inverted sides — the bid block floats above the mid line. Each block reverses
|
|
853
|
+
internally as well, so the levels nearest the spread stay nearest the mid
|
|
854
|
+
line in both orders. */
|
|
855
|
+
.orderbook-panel.ob-invert .orderbook-content { flex-direction: column-reverse; }
|
|
856
|
+
.orderbook-panel.ob-invert .orderbook-asks,
|
|
857
|
+
.orderbook-panel.ob-invert .orderbook-bids { flex-direction: column-reverse; }
|
|
858
|
+
|
|
859
|
+
/* Buy-versus-sell strip along the bottom. One measured property splits it — the
|
|
860
|
+
bid half takes it, the ask half takes what is left — so the two can never
|
|
861
|
+
disagree about how much of the strip is spoken for. */
|
|
862
|
+
.ob-sentiment {
|
|
863
|
+
display: flex;
|
|
864
|
+
height: 18px;
|
|
865
|
+
font-size: 10px;
|
|
866
|
+
font-weight: 600;
|
|
867
|
+
border-top: 1px solid var(--t-border);
|
|
868
|
+
overflow: hidden;
|
|
869
|
+
/* Informational, so it never squeezes the levels above it. */
|
|
870
|
+
flex: 0 0 18px;
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
.ob-sent-bid,
|
|
874
|
+
.ob-sent-ask {
|
|
875
|
+
text-align: center;
|
|
876
|
+
line-height: 18px;
|
|
877
|
+
overflow: hidden;
|
|
878
|
+
white-space: nowrap;
|
|
879
|
+
color: var(--t-text-bright);
|
|
880
|
+
transition: width 0.3s ease;
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
.ob-sent-bid {
|
|
884
|
+
width: var(--t-ob-sent);
|
|
885
|
+
background: color-mix(in srgb, var(--t-green) 55%, transparent);
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
.ob-sent-ask {
|
|
889
|
+
width: calc(100% - var(--t-ob-sent));
|
|
890
|
+
background: color-mix(in srgb, var(--t-red) 55%, transparent);
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
.ob-sent-label { opacity: 0.85; margin: 0 4px; }
|
|
894
|
+
|
|
895
|
+
/* Tabular figures, so a percentage ticking between 9% and 10% does not shove
|
|
896
|
+
the letter beside it sideways. */
|
|
897
|
+
.ob-sent-bid-pct,
|
|
898
|
+
.ob-sent-ask-pct { font-variant-numeric: tabular-nums; }
|
|
899
|
+
|
|
900
|
+
/* On a phone the ladder is most of the screen, so the settings that only tune
|
|
901
|
+
it step aside and the rows tighten. What actually shortens the book is the
|
|
902
|
+
host answering a smaller `maxDepth()`; this makes the rows that remain fit. */
|
|
903
|
+
@media (max-width: 768px), (max-height: 500px) {
|
|
904
|
+
.ob-depth-selector,
|
|
905
|
+
.ob-view-selector,
|
|
906
|
+
.ob-symbol-label { display: none; }
|
|
907
|
+
|
|
908
|
+
.orderbook-content { justify-content: center; }
|
|
909
|
+
.orderbook-asks,
|
|
910
|
+
.orderbook-bids { overflow-y: hidden; }
|
|
911
|
+
.orderbook-content .ob-row { line-height: 1.4; padding: 0 6px; font-size: 10px; }
|
|
912
|
+
.orderbook-content .orderbook-mid { padding: 2px 6px; font-size: 12px; }
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
/* --------------------------------------------------------------- trade feed */
|
|
916
|
+
|
|
917
|
+
/* The panel is the tooltip's containing block: the bubble tooltip is placed in
|
|
918
|
+
canvas coordinates and would otherwise anchor to the page. */
|
|
919
|
+
.tradefeed-panel {
|
|
920
|
+
display: flex;
|
|
921
|
+
flex-direction: column;
|
|
922
|
+
position: relative;
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
/* Market / My trades. Two panes and one canvas share the body, and which of
|
|
926
|
+
them is showing is two class names the control puts on the root — so the
|
|
927
|
+
rules below are the whole of that decision, and a narrow viewport can
|
|
928
|
+
override it further down without the control being told. */
|
|
929
|
+
.tf-tab-market .tf-my,
|
|
930
|
+
.tf-tab-my .tf-market,
|
|
931
|
+
.tf-tab-my .tf-bubbles,
|
|
932
|
+
.tf-tab-my .tf-view-toggle,
|
|
933
|
+
.tf-tab-market.tf-view-bubbles .tf-market,
|
|
934
|
+
.tf-tab-market.tf-view-list .tf-bubbles { display: none; }
|
|
935
|
+
|
|
936
|
+
.tf-tabs {
|
|
937
|
+
display: flex;
|
|
938
|
+
gap: 2px;
|
|
939
|
+
padding: 2px 8px;
|
|
940
|
+
border-bottom: 1px solid var(--t-border);
|
|
941
|
+
flex-shrink: 0;
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
.tf-tab {
|
|
945
|
+
background: transparent;
|
|
946
|
+
border: none;
|
|
947
|
+
border-bottom: 2px solid transparent;
|
|
948
|
+
color: var(--t-text-dim);
|
|
949
|
+
font-family: var(--t-font);
|
|
950
|
+
font-size: 10px;
|
|
951
|
+
font-weight: 600;
|
|
952
|
+
text-transform: uppercase;
|
|
953
|
+
letter-spacing: 0.4px;
|
|
954
|
+
padding: 2px 8px;
|
|
955
|
+
border-radius: var(--t-radius);
|
|
956
|
+
cursor: pointer;
|
|
957
|
+
transition: all var(--t-transition);
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
.tf-tab:hover { color: var(--t-text-bright); }
|
|
961
|
+
|
|
962
|
+
.tf-tab.active {
|
|
963
|
+
color: var(--t-accent);
|
|
964
|
+
border-bottom-color: var(--t-accent);
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
/* List of prints. Scrolls on its own so the header, the tab strip and the
|
|
968
|
+
pinned-extras ribbon stay put. */
|
|
969
|
+
.tradefeed-content {
|
|
970
|
+
flex: 1;
|
|
971
|
+
overflow-y: auto;
|
|
972
|
+
font-family: var(--t-mono);
|
|
973
|
+
font-size: 11px;
|
|
974
|
+
min-height: 0;
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
/* Each tape is a DataGrid over the shared table skin. The direction class the
|
|
978
|
+
host answered with sits on the row, so the price and the side inherit its
|
|
979
|
+
colour while the time and the qty state their own. The grid stamps every
|
|
980
|
+
body cell with its column key — that is what the alignments key off. */
|
|
981
|
+
.tradefeed-table td[data-col="time"] { color: var(--t-text-dim); }
|
|
982
|
+
.tradefeed-table td[data-col="symbol"] { color: var(--t-text-bright); font-weight: 600; font-size: 11px; }
|
|
983
|
+
.tradefeed-table td[data-col="price"] { text-align: right; font-weight: 500; }
|
|
984
|
+
.tradefeed-table td[data-col="quantity"] { text-align: right; color: var(--t-text-dim); }
|
|
985
|
+
.tradefeed-table td[data-col="side"] { text-align: right; font-weight: 600; font-size: 10px; }
|
|
986
|
+
|
|
987
|
+
/* A print that just landed (the grid's flashNewClass names it), and one well
|
|
988
|
+
above the recent average size. */
|
|
989
|
+
.tradefeed-table tbody tr.flash-new { animation: flashGreen 0.6s ease; }
|
|
990
|
+
.tradefeed-table tbody tr.side-sell.flash-new { animation: flashRed 0.6s ease; }
|
|
991
|
+
.tradefeed-table tbody tr.large-trade td { font-size: 12px; font-weight: 600; }
|
|
992
|
+
.tradefeed-table tbody tr.large-trade td[data-col="quantity"] { color: var(--t-text-bright); }
|
|
993
|
+
|
|
994
|
+
/* List | bubbles, in the panel header. */
|
|
995
|
+
.tradefeed-view-toggle {
|
|
996
|
+
display: inline-flex;
|
|
997
|
+
gap: 2px;
|
|
998
|
+
margin-left: auto;
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
.tradefeed-view-toggle .tf-view-btn {
|
|
1002
|
+
background: transparent;
|
|
1003
|
+
border: 1px solid var(--t-border);
|
|
1004
|
+
color: var(--t-text-dim);
|
|
1005
|
+
padding: 1px 6px;
|
|
1006
|
+
height: 18px;
|
|
1007
|
+
line-height: 1;
|
|
1008
|
+
font-size: 10px;
|
|
1009
|
+
border-radius: var(--t-radius);
|
|
1010
|
+
cursor: pointer;
|
|
1011
|
+
transition: all var(--t-transition);
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
.tradefeed-view-toggle .tf-view-btn:hover {
|
|
1015
|
+
color: var(--t-text-bright);
|
|
1016
|
+
border-color: var(--t-text-dim);
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
.tradefeed-view-toggle .tf-view-btn.active {
|
|
1020
|
+
background: var(--t-accent);
|
|
1021
|
+
color: var(--t-accent-text);
|
|
1022
|
+
border-color: var(--t-accent);
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
/* Pinned extras — one chip per instrument this feed watches on top of the
|
|
1026
|
+
page's own. Absent from the markup until there is one. */
|
|
1027
|
+
.tf-extras {
|
|
1028
|
+
display: flex;
|
|
1029
|
+
flex-wrap: wrap;
|
|
1030
|
+
gap: 4px;
|
|
1031
|
+
padding: 4px 8px;
|
|
1032
|
+
border-bottom: 1px solid var(--t-border);
|
|
1033
|
+
flex-shrink: 0;
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
.tf-extras[hidden] { display: none; }
|
|
1037
|
+
|
|
1038
|
+
.tf-extra-chip {
|
|
1039
|
+
display: inline-flex;
|
|
1040
|
+
align-items: center;
|
|
1041
|
+
gap: 4px;
|
|
1042
|
+
padding: 2px 4px 2px 8px;
|
|
1043
|
+
font-size: 11px;
|
|
1044
|
+
font-weight: 600;
|
|
1045
|
+
color: var(--t-text-bright);
|
|
1046
|
+
background: var(--t-hover);
|
|
1047
|
+
border: 1px solid var(--t-border);
|
|
1048
|
+
border-radius: 10px;
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
.tf-extra-sym { letter-spacing: 0.3px; }
|
|
1052
|
+
|
|
1053
|
+
.tf-extra-rm {
|
|
1054
|
+
display: inline-flex;
|
|
1055
|
+
align-items: center;
|
|
1056
|
+
justify-content: center;
|
|
1057
|
+
width: 16px;
|
|
1058
|
+
height: 16px;
|
|
1059
|
+
padding: 0;
|
|
1060
|
+
line-height: 1;
|
|
1061
|
+
color: var(--t-text-dim);
|
|
1062
|
+
background: transparent;
|
|
1063
|
+
border: none;
|
|
1064
|
+
border-radius: 50%;
|
|
1065
|
+
cursor: pointer;
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
.tf-extra-rm i { font-size: 10px; }
|
|
1069
|
+
|
|
1070
|
+
.tf-extra-rm:hover {
|
|
1071
|
+
color: var(--t-red);
|
|
1072
|
+
background: color-mix(in srgb, var(--t-red) 12%, transparent);
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
/* The bubble chart. Takes whatever height is left below the chrome, full
|
|
1076
|
+
width; the control sizes its backing store to the device's pixels. */
|
|
1077
|
+
.tradefeed-bubbles {
|
|
1078
|
+
flex: 1;
|
|
1079
|
+
width: 100%;
|
|
1080
|
+
min-height: 0;
|
|
1081
|
+
display: block;
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
/* Placed by the control in canvas coordinates, which is why this is the one
|
|
1085
|
+
thing here carrying a `left` / `top` it does not declare. */
|
|
1086
|
+
.tf-bubble-tooltip {
|
|
1087
|
+
position: absolute;
|
|
1088
|
+
z-index: 20;
|
|
1089
|
+
min-width: 120px;
|
|
1090
|
+
padding: 6px 8px;
|
|
1091
|
+
background: var(--t-panel);
|
|
1092
|
+
border: 1px solid var(--t-border);
|
|
1093
|
+
border-radius: 4px;
|
|
1094
|
+
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4);
|
|
1095
|
+
font-family: var(--t-mono);
|
|
1096
|
+
font-size: 11px;
|
|
1097
|
+
line-height: 1.5;
|
|
1098
|
+
color: var(--t-text-bright);
|
|
1099
|
+
pointer-events: none;
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
.tf-bubble-tooltip[hidden] { display: none; }
|
|
1103
|
+
.tf-bbtt-row { display: flex; justify-content: space-between; gap: 12px; }
|
|
1104
|
+
.tf-bbtt-label { color: var(--t-text-dim); }
|
|
1105
|
+
.tf-bbtt-value { font-weight: 600; }
|
|
1106
|
+
|
|
1107
|
+
/* A phone has no room for two renderings of the same tape, or for a second
|
|
1108
|
+
instrument pinned beside the first: the list wins and the rest steps aside.
|
|
1109
|
+
Later in the file than the state rules above, and at the same specificity,
|
|
1110
|
+
so it overrides them whatever the control's own state says. */
|
|
1111
|
+
@media (max-width: 768px), (max-height: 500px) {
|
|
1112
|
+
.tf-tab-market.tf-view-bubbles .tf-market { display: block; }
|
|
1113
|
+
.tradefeed-panel .tf-bubbles,
|
|
1114
|
+
.tradefeed-panel .tf-view-toggle,
|
|
1115
|
+
.tradefeed-panel .tf-extras,
|
|
1116
|
+
.tradefeed-panel .tf-add-symbol-btn { display: none; }
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
/* ----------------------------------------------------------- order entry */
|
|
1120
|
+
|
|
1121
|
+
.order-entry-content {
|
|
1122
|
+
padding: 8px;
|
|
1123
|
+
flex: 1;
|
|
1124
|
+
display: flex;
|
|
1125
|
+
flex-direction: column;
|
|
1126
|
+
min-height: 0;
|
|
1127
|
+
gap: 6px;
|
|
1128
|
+
/* Backstop for viewports and locales whose labels push the form past the
|
|
1129
|
+
height the panel was given. */
|
|
1130
|
+
overflow-y: auto;
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
/* Nothing can be sent while the host says the account is out of reach. The form
|
|
1134
|
+
says so by going quiet rather than by disappearing, so the numbers in it stay
|
|
1135
|
+
readable. */
|
|
1136
|
+
.order-entry-content.oe-offline {
|
|
1137
|
+
opacity: 0.5;
|
|
1138
|
+
pointer-events: none;
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
/* Two pairs of type buttons, so each pair sits over the column it applies to:
|
|
1142
|
+
the outer gap matches the column gap and the inner one is tighter. */
|
|
1143
|
+
.order-type-tabs {
|
|
1144
|
+
display: grid;
|
|
1145
|
+
grid-template-columns: 1fr 1fr;
|
|
1146
|
+
gap: 12px;
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
.ot-pair {
|
|
1150
|
+
display: flex;
|
|
1151
|
+
gap: 4px;
|
|
1152
|
+
min-width: 0;
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
.btn-ot {
|
|
1156
|
+
flex: 1;
|
|
1157
|
+
padding: 4px 8px;
|
|
1158
|
+
font-size: 11px;
|
|
1159
|
+
font-weight: 600;
|
|
1160
|
+
background: transparent;
|
|
1161
|
+
border: 1px solid var(--t-border);
|
|
1162
|
+
color: var(--t-text-dim);
|
|
1163
|
+
border-radius: var(--t-radius);
|
|
1164
|
+
cursor: pointer;
|
|
1165
|
+
transition: all var(--t-transition);
|
|
1166
|
+
font-family: var(--t-font);
|
|
1167
|
+
min-width: 0;
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
.btn-ot.active { background: var(--t-accent); color: var(--t-accent-text); border-color: var(--t-accent); }
|
|
1171
|
+
.btn-ot:hover:not(.active) { background: var(--t-hover); color: var(--t-text); border-color: var(--t-text-dim); }
|
|
1172
|
+
|
|
1173
|
+
.order-entry-split {
|
|
1174
|
+
flex: 1;
|
|
1175
|
+
min-height: 0;
|
|
1176
|
+
display: grid;
|
|
1177
|
+
grid-template-columns: 1fr 1fr;
|
|
1178
|
+
gap: 12px;
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
.oe-col {
|
|
1182
|
+
display: flex;
|
|
1183
|
+
flex-direction: column;
|
|
1184
|
+
gap: 5px;
|
|
1185
|
+
padding: 6px 8px;
|
|
1186
|
+
background: var(--t-panel);
|
|
1187
|
+
border: 1px solid var(--t-border);
|
|
1188
|
+
border-radius: var(--t-radius);
|
|
1189
|
+
min-width: 0;
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
/* The direction shows at the edge only. The submit button carries the full
|
|
1193
|
+
colour, and two saturated panels side by side read as an alarm. */
|
|
1194
|
+
.oe-col-buy { border-color: color-mix(in srgb, var(--t-green) 25%, transparent); }
|
|
1195
|
+
.oe-col-sell { border-color: color-mix(in srgb, var(--t-red) 25%, transparent); }
|
|
1196
|
+
|
|
1197
|
+
.oe-label {
|
|
1198
|
+
font-size: 10px;
|
|
1199
|
+
color: var(--t-text-dim);
|
|
1200
|
+
text-transform: uppercase;
|
|
1201
|
+
letter-spacing: 0.4px;
|
|
1202
|
+
font-family: var(--t-font);
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
.oe-value {
|
|
1206
|
+
font-family: var(--t-mono);
|
|
1207
|
+
font-size: 11px;
|
|
1208
|
+
color: var(--t-text);
|
|
1209
|
+
text-align: right;
|
|
1210
|
+
white-space: nowrap;
|
|
1211
|
+
overflow: hidden;
|
|
1212
|
+
text-overflow: ellipsis;
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
.oe-avbl,
|
|
1216
|
+
.oe-total,
|
|
1217
|
+
.oe-foot {
|
|
1218
|
+
display: flex;
|
|
1219
|
+
justify-content: space-between;
|
|
1220
|
+
align-items: baseline;
|
|
1221
|
+
gap: 6px;
|
|
1222
|
+
font-size: 10px;
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
.oe-field {
|
|
1226
|
+
display: flex;
|
|
1227
|
+
flex-direction: column;
|
|
1228
|
+
gap: 2px;
|
|
1229
|
+
min-width: 0;
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
/* A field the current order type does not use, and the take-profit block while
|
|
1233
|
+
it is switched off. The control toggles this class rather than a display
|
|
1234
|
+
style, so what a field looks like when it is away stays here. */
|
|
1235
|
+
.oe-field-hidden { display: none; }
|
|
1236
|
+
|
|
1237
|
+
.oe-input-grp {
|
|
1238
|
+
display: flex;
|
|
1239
|
+
gap: 4px;
|
|
1240
|
+
align-items: stretch;
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
.oe-input-grp .oe-input { flex: 1; min-width: 0; }
|
|
1244
|
+
|
|
1245
|
+
.oe-input {
|
|
1246
|
+
background: var(--t-bg);
|
|
1247
|
+
border: 1px solid var(--t-border);
|
|
1248
|
+
color: var(--t-text-bright);
|
|
1249
|
+
font-family: var(--t-mono);
|
|
1250
|
+
font-size: 12px;
|
|
1251
|
+
padding: 4px 6px;
|
|
1252
|
+
border-radius: var(--t-radius);
|
|
1253
|
+
transition: border-color var(--t-transition);
|
|
1254
|
+
width: 100%;
|
|
1255
|
+
min-width: 0;
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
.oe-input:focus {
|
|
1259
|
+
outline: none;
|
|
1260
|
+
border-color: var(--t-accent);
|
|
1261
|
+
box-shadow: 0 0 0 2px var(--t-accent-glow-soft);
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
.btn-bbo {
|
|
1265
|
+
background: var(--t-bg);
|
|
1266
|
+
border: 1px solid var(--t-border);
|
|
1267
|
+
color: var(--t-text-dim);
|
|
1268
|
+
font-size: 10px;
|
|
1269
|
+
font-weight: 600;
|
|
1270
|
+
padding: 0 8px;
|
|
1271
|
+
border-radius: var(--t-radius);
|
|
1272
|
+
cursor: pointer;
|
|
1273
|
+
font-family: var(--t-font);
|
|
1274
|
+
transition: all var(--t-transition);
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1277
|
+
.btn-bbo:hover {
|
|
1278
|
+
color: var(--t-accent-text);
|
|
1279
|
+
background: var(--t-accent);
|
|
1280
|
+
border-color: var(--t-accent);
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
.oe-pct-row {
|
|
1284
|
+
display: flex;
|
|
1285
|
+
gap: 3px;
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
.btn-oe-pct {
|
|
1289
|
+
flex: 1;
|
|
1290
|
+
padding: 2px 0;
|
|
1291
|
+
font-size: 10px;
|
|
1292
|
+
font-weight: 500;
|
|
1293
|
+
font-family: var(--t-mono);
|
|
1294
|
+
background: var(--t-bg);
|
|
1295
|
+
border: 1px solid var(--t-border);
|
|
1296
|
+
color: var(--t-text-dim);
|
|
1297
|
+
border-radius: 2px;
|
|
1298
|
+
cursor: pointer;
|
|
1299
|
+
transition: all var(--t-transition);
|
|
1300
|
+
}
|
|
1301
|
+
|
|
1302
|
+
.btn-oe-pct:hover {
|
|
1303
|
+
background: var(--t-hover);
|
|
1304
|
+
color: var(--t-text-bright);
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
.oe-tpsl-toggle {
|
|
1308
|
+
display: flex;
|
|
1309
|
+
align-items: center;
|
|
1310
|
+
gap: 6px;
|
|
1311
|
+
font-size: 10px;
|
|
1312
|
+
color: var(--t-text-dim);
|
|
1313
|
+
cursor: pointer;
|
|
1314
|
+
user-select: none;
|
|
1315
|
+
font-family: var(--t-font);
|
|
1316
|
+
text-transform: uppercase;
|
|
1317
|
+
letter-spacing: 0.4px;
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
.oe-tpsl {
|
|
1321
|
+
display: flex;
|
|
1322
|
+
flex-direction: column;
|
|
1323
|
+
gap: 4px;
|
|
1324
|
+
padding: 4px 0;
|
|
1325
|
+
border-top: 1px dashed var(--t-border);
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
/* Pushed to the bottom of the column, so both buttons line up however tall the
|
|
1329
|
+
two forms above them ended up. */
|
|
1330
|
+
.btn-oe-submit {
|
|
1331
|
+
margin-top: auto;
|
|
1332
|
+
flex: none;
|
|
1333
|
+
padding: 10px 6px;
|
|
1334
|
+
font-weight: 700;
|
|
1335
|
+
font-size: 13px;
|
|
1336
|
+
text-transform: uppercase;
|
|
1337
|
+
border: none;
|
|
1338
|
+
border-radius: var(--t-radius);
|
|
1339
|
+
cursor: pointer;
|
|
1340
|
+
color: var(--t-on-direction);
|
|
1341
|
+
transition: all var(--t-transition);
|
|
1342
|
+
font-family: var(--t-font);
|
|
1343
|
+
letter-spacing: 0.3px;
|
|
1344
|
+
position: relative;
|
|
1345
|
+
overflow: hidden;
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
.btn-oe-submit.btn-buy { background: var(--t-green); }
|
|
1349
|
+
.btn-oe-submit.btn-buy:hover { background: var(--t-green-hover); box-shadow: 0 0 12px color-mix(in srgb, var(--t-green) 30%, transparent); }
|
|
1350
|
+
.btn-oe-submit.btn-sell { background: var(--t-red); }
|
|
1351
|
+
.btn-oe-submit.btn-sell:hover { background: var(--t-red-hover); box-shadow: 0 0 12px color-mix(in srgb, var(--t-red) 30%, transparent); }
|
|
1352
|
+
.btn-oe-submit:disabled { opacity: 0.45; cursor: not-allowed; }
|
|
1353
|
+
.btn-oe-submit:active { transform: scale(0.97); }
|
|
1354
|
+
|
|
1355
|
+
/* The side a host-level gesture aimed at — a click on a book level, a chart
|
|
1356
|
+
price. A ring rather than a colour change: the button already says which
|
|
1357
|
+
direction it is. */
|
|
1358
|
+
.btn-oe-submit.btn-buy.btn-preselected { box-shadow: 0 0 0 2px var(--t-green), 0 0 12px color-mix(in srgb, var(--t-green) 30%, transparent); }
|
|
1359
|
+
.btn-oe-submit.btn-sell.btn-preselected { box-shadow: 0 0 0 2px var(--t-red), 0 0 12px color-mix(in srgb, var(--t-red) 30%, transparent); }
|
|
1360
|
+
|
|
1361
|
+
.hotkey-hint {
|
|
1362
|
+
font-size: 9px;
|
|
1363
|
+
font-weight: 400;
|
|
1364
|
+
opacity: 0.6;
|
|
1365
|
+
margin-left: 4px;
|
|
1366
|
+
font-family: var(--t-mono);
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1369
|
+
/* The order's value, and the reason it cannot be sent, in the same line: they
|
|
1370
|
+
are never both true, and giving them one line keeps the button from moving
|
|
1371
|
+
when a message appears. */
|
|
1372
|
+
.oe-estimate {
|
|
1373
|
+
font-size: 10px;
|
|
1374
|
+
font-family: var(--t-mono);
|
|
1375
|
+
color: var(--t-text-dim);
|
|
1376
|
+
min-height: 12px;
|
|
1377
|
+
text-align: center;
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1380
|
+
.oe-estimate.oe-estimate-error { color: var(--t-red); }
|