@visns-studio/visns-components 6.32.2 → 6.34.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 +504 -0
- package/package.json +1 -1
- package/src/components/DataGrid.jsx +44 -2
- package/src/components/Field.jsx +32 -0
- package/src/components/Form.jsx +39 -2
- package/src/components/Navigation.jsx +353 -43
- package/src/components/Notification.jsx +13 -2
- package/src/components/TableFilter.jsx +14 -2
- package/src/components/auth/Login.jsx +9 -2
- package/src/components/auth/PasskeyEnrolPrompt.jsx +269 -0
- package/src/components/auth/Profile.jsx +422 -26
- package/src/components/auth/authEndpoints.js +43 -0
- package/src/components/auth/passkeyClient.js +102 -0
- package/src/components/auth/passkeyPrompt.js +408 -0
- package/src/components/auth/profileLayout.js +45 -0
- package/src/components/controls/DataGridSearch.jsx +16 -2
- package/src/components/controls/DataGridSortSheet.jsx +2 -0
- package/src/components/generic/ActionButtons.jsx +1 -1
- package/src/components/generic/GenericAuth.jsx +197 -21
- package/src/components/generic/GenericDetail.jsx +95 -3
- package/src/components/generic/GenericMain.jsx +7 -0
- package/src/components/generic/StandardModal.jsx +71 -158
- package/src/components/sms/SmsCampaigns.jsx +2487 -0
- package/src/components/sms/SmsInbox.jsx +18 -0
- package/src/components/sms/SmsThreadPanel.jsx +20 -0
- package/src/components/sms/smsEndpoints.js +23 -0
- package/src/components/styles/DataGrid.module.scss +22 -0
- package/src/components/styles/Form.module.scss +100 -2
- package/src/components/styles/Navigation.module.scss +354 -15
- package/src/components/styles/Notification.module.scss +140 -8
- package/src/components/styles/PasskeyEnrolPrompt.module.scss +105 -0
- package/src/components/styles/Profile.module.scss +157 -0
- package/src/components/styles/Sms.module.scss +451 -0
- package/src/components/styles/StandardModal.module.scss +273 -0
- package/src/components/styles/global.css +24 -5
- package/src/components/utils/navCollapsed.js +201 -0
- package/src/components/utils/rowActionSettings.js +76 -0
- package/src/components/utils/usePasskeysEnabled.js +84 -0
- package/src/components/utils/useShellLayout.js +131 -0
- package/src/index.js +103 -1
- package/src/utils/rememberTab.js +70 -0
|
@@ -511,27 +511,123 @@
|
|
|
511
511
|
transform: translateY(-50%);
|
|
512
512
|
}
|
|
513
513
|
|
|
514
|
-
|
|
514
|
+
/* Beside the rail, bottom-aligned with the row that opened it.
|
|
515
|
+
|
|
516
|
+
`top: 100%; right: 0` is a panel hanging off the bottom-right corner of
|
|
517
|
+
a header chip. From a row pinned to the FOOT of a full-height rail that
|
|
518
|
+
is the bottom-left corner of the viewport: the panel drops off the
|
|
519
|
+
screen and lies half on the rail, half on the page. Anchored to the
|
|
520
|
+
row's right edge instead, it opens over the content the way a rail's
|
|
521
|
+
flyout is expected to — the gap is the only thing between it and the
|
|
522
|
+
rail, so the two read as one surface hinged at the row.
|
|
523
|
+
|
|
524
|
+
`z-index` is stated because `.rail-actions` sits at 996 (above the
|
|
525
|
+
content header's sticky 995); a panel opening from a row inside it has
|
|
526
|
+
to clear both. */
|
|
515
527
|
.dropdownWrapper {
|
|
516
528
|
top: auto;
|
|
517
529
|
right: auto;
|
|
518
530
|
bottom: 0;
|
|
519
|
-
left: calc(100% + 10px);
|
|
531
|
+
left: calc(100% + var(--nav-action-panel-gap, 10px));
|
|
520
532
|
margin-top: 0;
|
|
533
|
+
z-index: 1200;
|
|
534
|
+
/* The rail's own shadow language, rather than the chip's flat drop —
|
|
535
|
+
this floats over the page, not over a bar. */
|
|
536
|
+
filter: drop-shadow(0 12px 32px rgba(0, 0, 0, 0.22));
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
/* A column, so the list is the only part that scrolls.
|
|
540
|
+
|
|
541
|
+
The box used to be as tall as its contents with a 320px scroller inside
|
|
542
|
+
it, which from the foot of the rail grew upward past the top of the
|
|
543
|
+
viewport before the scroller ever engaged. A height cap on the BOX and
|
|
544
|
+
`min-height: 0` on the list is what makes the header, the alerts row
|
|
545
|
+
and the footer button stay put while the notifications move. */
|
|
546
|
+
.notibox {
|
|
547
|
+
display: flex;
|
|
548
|
+
flex-direction: column;
|
|
549
|
+
max-height: min(70vh, 640px);
|
|
550
|
+
/* The radius the rail's own rows wear. */
|
|
551
|
+
border-radius: var(--br, 10px);
|
|
552
|
+
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.14);
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
.notiscrollwrap {
|
|
556
|
+
flex: 1 1 auto;
|
|
557
|
+
/* Without this a flex item floors at its content height and the cap
|
|
558
|
+
above does nothing. */
|
|
559
|
+
min-height: 0;
|
|
560
|
+
max-height: none;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
/* The three fixed bands around it. `flex-shrink` only — every other
|
|
564
|
+
property they own is already `!important` in the rules above and is
|
|
565
|
+
deliberately left alone. */
|
|
566
|
+
.notiboxHeader,
|
|
567
|
+
.alertsRow,
|
|
568
|
+
.notiboxActions {
|
|
569
|
+
flex: 0 0 auto;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
/* The footer button was mid-grey on a grey band: the same colour as a
|
|
573
|
+
disabled control, for the one action in the panel. It is the primary
|
|
574
|
+
button the rest of the product uses, on the project's own token. */
|
|
575
|
+
.notiboxActions {
|
|
576
|
+
background-color: transparent;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
.viewAllBtn {
|
|
580
|
+
background-color: var(--primary-color, #0056b3) !important;
|
|
581
|
+
color: var(--tertiary-color, #fff) !important;
|
|
582
|
+
border: 1px solid var(--primary-color, #0056b3) !important;
|
|
583
|
+
border-radius: var(--br, 8px) !important;
|
|
584
|
+
font-weight: 600 !important;
|
|
585
|
+
height: 40px !important;
|
|
586
|
+
padding: 0 20px !important;
|
|
587
|
+
max-width: none !important;
|
|
588
|
+
box-shadow: none !important;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
.viewAllBtn:hover:not(:disabled) {
|
|
592
|
+
/* No second colour token to reach for — every project sets
|
|
593
|
+
`--primary-color` and not all set a darker partner — so the hover
|
|
594
|
+
is the same ink with the page showing through it. */
|
|
595
|
+
background-color: color-mix(
|
|
596
|
+
in srgb,
|
|
597
|
+
var(--primary-color, #0056b3) 88%,
|
|
598
|
+
#000
|
|
599
|
+
) !important;
|
|
600
|
+
border-color: color-mix(
|
|
601
|
+
in srgb,
|
|
602
|
+
var(--primary-color, #0056b3) 88%,
|
|
603
|
+
#000
|
|
604
|
+
) !important;
|
|
605
|
+
box-shadow: 0 2px 8px
|
|
606
|
+
rgba(var(--primary-color-rgb, 0, 86, 179), 0.35) !important;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
.viewAllBtn:focus:not(:disabled) {
|
|
610
|
+
box-shadow: 0 0 0 2px
|
|
611
|
+
rgba(var(--primary-color-rgb, 0, 86, 179), 0.35) !important;
|
|
521
612
|
}
|
|
522
613
|
|
|
523
614
|
@media (max-width: 1024px) {
|
|
524
|
-
|
|
525
|
-
|
|
615
|
+
/* Inside the drawer there is nothing to the right, so the panel
|
|
616
|
+
opens upward from the row — and takes the drawer's full width
|
|
617
|
+
rather than a 360px card floating inside a 300px column. */
|
|
526
618
|
.dropdownWrapper {
|
|
527
619
|
left: 0;
|
|
620
|
+
right: 0;
|
|
528
621
|
bottom: calc(100% + 8px);
|
|
622
|
+
filter: drop-shadow(0 8px 24px rgba(0, 0, 0, 0.3));
|
|
529
623
|
}
|
|
530
624
|
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
625
|
+
.notibox {
|
|
626
|
+
width: 100%;
|
|
627
|
+
max-width: none;
|
|
628
|
+
/* It grows from the foot of the drawer towards the header, so it
|
|
629
|
+
gets less of the viewport than it does beside a rail. */
|
|
630
|
+
max-height: min(60vh, 520px);
|
|
535
631
|
}
|
|
536
632
|
}
|
|
537
633
|
}
|
|
@@ -638,3 +734,39 @@
|
|
|
638
734
|
transition: none;
|
|
639
735
|
}
|
|
640
736
|
}
|
|
737
|
+
|
|
738
|
+
/* ----------------------------------------------------------------------------
|
|
739
|
+
…and the rail collapsed to an icon strip
|
|
740
|
+
----------------------------------------------------------------------------
|
|
741
|
+
`<html data-nav-collapsed="true">` (Navigation writes it; see
|
|
742
|
+
utils/navCollapsed.js) means the rail is 64px wide and every row shows its
|
|
743
|
+
icon alone, with the tooltip doing the labelling. The bell draws its own
|
|
744
|
+
trigger, so the two things the rail cannot do for it are done here: drop the
|
|
745
|
+
`::after` label, and centre what is left.
|
|
746
|
+
|
|
747
|
+
Desktop only, exactly as the rail's own collapsed rules are — below 1024px
|
|
748
|
+
the rail is a drawer and the attribute is never written.
|
|
749
|
+
|
|
750
|
+
The panel itself needs nothing: it is anchored to the ROW's right edge
|
|
751
|
+
(`left: calc(100% + gap)`), so it follows the rail in and out without a
|
|
752
|
+
figure of its own.
|
|
753
|
+
------------------------------------------------------------------------- */
|
|
754
|
+
@media (min-width: 1025px) {
|
|
755
|
+
:root[data-nav-collapsed='true'] [data-nav-actions='sidebar'] {
|
|
756
|
+
.notificationTrigger {
|
|
757
|
+
justify-content: center;
|
|
758
|
+
|
|
759
|
+
&::after {
|
|
760
|
+
display: none;
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
/* Back on the icon's shoulder: there is no line for it to sit at the
|
|
765
|
+
end of any more. */
|
|
766
|
+
.badge {
|
|
767
|
+
top: 2px;
|
|
768
|
+
right: 8px;
|
|
769
|
+
transform: none;
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
@use 'controls' as *;
|
|
2
|
+
|
|
3
|
+
/* ============================================================================
|
|
4
|
+
PasskeyEnrolPrompt — the offer made once, after signing in
|
|
5
|
+
----------------------------------------------------------------------------
|
|
6
|
+
Inside a StandardModal at `size="small"`, so the panel's width, its phone
|
|
7
|
+
geometry and its z-index all come from StandardModal.module.scss and none of
|
|
8
|
+
it is restated here. What is here is the inside of the panel: a line of
|
|
9
|
+
explanation and a row of three buttons whose weights say what they are.
|
|
10
|
+
|
|
11
|
+
The three are deliberately NOT three equal buttons. "Add passkey" is the
|
|
12
|
+
thing being offered; "Not now" is the way out anybody can take without
|
|
13
|
+
thinking about it; "Don't ask again" is quieter still and reads as a link,
|
|
14
|
+
because a permanent decision should not be the easiest button on the screen
|
|
15
|
+
to hit by accident.
|
|
16
|
+
|
|
17
|
+
Every measurement is `var(--hook, <value>)` so an app can retune it from
|
|
18
|
+
`:root` without forking the component, the same way Profile.module.scss
|
|
19
|
+
does.
|
|
20
|
+
========================================================================= */
|
|
21
|
+
|
|
22
|
+
.body {
|
|
23
|
+
display: flex;
|
|
24
|
+
flex-direction: column;
|
|
25
|
+
gap: var(--spacing-md, 1rem);
|
|
26
|
+
padding: var(--spacing-md, 1rem) 0 0;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/* The mark. A key in a tinted disc — enough to say "this is about signing in"
|
|
30
|
+
without an illustration nobody asked to download. */
|
|
31
|
+
.mark {
|
|
32
|
+
display: flex;
|
|
33
|
+
align-items: center;
|
|
34
|
+
justify-content: center;
|
|
35
|
+
/* A column flex parent stretches its children; without this the disc
|
|
36
|
+
becomes a lozenge the width of the panel. */
|
|
37
|
+
align-self: flex-start;
|
|
38
|
+
width: 2.75rem;
|
|
39
|
+
height: 2.75rem;
|
|
40
|
+
border-radius: 50%;
|
|
41
|
+
background: var(--primary-color-light, rgba(0, 0, 0, 0.06));
|
|
42
|
+
color: var(--primary-color, #1b3a57);
|
|
43
|
+
flex: 0 0 auto;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.text {
|
|
47
|
+
margin: 0;
|
|
48
|
+
font-size: var(--font-size-sm, 0.875rem);
|
|
49
|
+
line-height: 1.55;
|
|
50
|
+
color: var(--text-color, #333);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/* The row of choices. It wraps rather than scrolls: on a 320px phone three
|
|
54
|
+
buttons do not fit side by side, and a row that overflows puts the way out
|
|
55
|
+
off the edge of the sheet. */
|
|
56
|
+
.actions {
|
|
57
|
+
display: flex;
|
|
58
|
+
flex-wrap: wrap;
|
|
59
|
+
align-items: center;
|
|
60
|
+
gap: var(--spacing-sm, 0.5rem);
|
|
61
|
+
padding-top: var(--spacing-sm, 0.5rem);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.primary {
|
|
65
|
+
@include button-primary;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.later {
|
|
69
|
+
@include button-secondary;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/* Pushed to the far end of the row, and styled as text: the permanent answer
|
|
73
|
+
should be available, findable, and never the thing a thumb lands on. */
|
|
74
|
+
.never {
|
|
75
|
+
@include button-base;
|
|
76
|
+
|
|
77
|
+
margin-left: auto;
|
|
78
|
+
background: none;
|
|
79
|
+
border-color: transparent;
|
|
80
|
+
color: var(--muted-color, #6b7280);
|
|
81
|
+
text-decoration: underline;
|
|
82
|
+
text-underline-offset: 0.2em;
|
|
83
|
+
|
|
84
|
+
&:hover:not(:disabled) {
|
|
85
|
+
background: none;
|
|
86
|
+
color: var(--text-color, #333);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/* Phones: one full-width button per line, in the order they are read, and the
|
|
91
|
+
quiet one loses its auto margin — `margin-left: auto` in a column pushes it
|
|
92
|
+
to the right edge of a stack, which is where nothing else is. */
|
|
93
|
+
@media (max-width: 640px) {
|
|
94
|
+
.actions {
|
|
95
|
+
flex-direction: column;
|
|
96
|
+
align-items: stretch;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.primary,
|
|
100
|
+
.later,
|
|
101
|
+
.never {
|
|
102
|
+
width: 100%;
|
|
103
|
+
margin-left: 0;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -580,6 +580,44 @@
|
|
|
580
580
|
}
|
|
581
581
|
}
|
|
582
582
|
|
|
583
|
+
/* The passkey list. A stack of rows rather than a table: each row is a name,
|
|
584
|
+
a quiet line of context and the one control that acts on it, and at phone
|
|
585
|
+
width a table of three columns is a table that scrolls sideways. The row
|
|
586
|
+
wraps instead, so the Remove button drops under the name rather than off
|
|
587
|
+
the edge. */
|
|
588
|
+
.passkeyList {
|
|
589
|
+
display: flex;
|
|
590
|
+
flex-direction: column;
|
|
591
|
+
gap: var(--spacing-sm, 0.5rem);
|
|
592
|
+
width: 100%;
|
|
593
|
+
margin: 0;
|
|
594
|
+
padding: 0;
|
|
595
|
+
list-style: none;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
.passkeyItem {
|
|
599
|
+
display: flex;
|
|
600
|
+
flex-wrap: wrap;
|
|
601
|
+
align-items: center;
|
|
602
|
+
gap: var(--spacing-sm, 0.5rem);
|
|
603
|
+
padding: var(--spacing-sm, 0.5rem) var(--spacing-md, 1rem);
|
|
604
|
+
border: 1px solid var(--border-color, #e1e1e1);
|
|
605
|
+
border-radius: var(--radius-sm, 8px);
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
.passkeyName {
|
|
609
|
+
font-weight: var(--font-weight-semibold, 600);
|
|
610
|
+
/* Takes the slack so the button sits at the far edge of the row. */
|
|
611
|
+
flex: 1 1 auto;
|
|
612
|
+
min-width: 0;
|
|
613
|
+
overflow-wrap: anywhere;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
.passkeyMeta {
|
|
617
|
+
font-size: var(--font-size-xs, 0.75rem);
|
|
618
|
+
color: var(--muted-color, #6b7280);
|
|
619
|
+
}
|
|
620
|
+
|
|
583
621
|
/* The setup walkthrough. It used to be a bordered, shadowed card inside the
|
|
584
622
|
panel card, carrying a heading that repeated the heading above it. The
|
|
585
623
|
section owns the title and the separation now, so this is only the steps. */
|
|
@@ -902,3 +940,122 @@
|
|
|
902
940
|
color: rgba(var(--paragraph-color-rgb, 43, 43, 43), 0.65);
|
|
903
941
|
}
|
|
904
942
|
}
|
|
943
|
+
|
|
944
|
+
/* ============================================================================
|
|
945
|
+
* Tabs as a strip
|
|
946
|
+
* ----------------------------------------------------------------------------
|
|
947
|
+
* The rail above is the layout this screen has always had, and on a top-bar
|
|
948
|
+
* app it is still the one it gets. In a SIDEBAR app the page already has a
|
|
949
|
+
* vertical navigation column two centimetres to the left, and a second one
|
|
950
|
+
* beside it spends a fifth of the content width to show two words. Profile
|
|
951
|
+
* asks for the strip there (see profileLayout.js for the rule, Profile.jsx
|
|
952
|
+
* for where the classes below are applied) and for the same three shapes
|
|
953
|
+
* GenericIndex and GenericDetail use for it, so the three pages agree:
|
|
954
|
+
*
|
|
955
|
+
* `.crmtitle--flat` the page title as type on the page ground.
|
|
956
|
+
* `.grid__subrow--stacked` one column instead of rail + panel.
|
|
957
|
+
* `.grid__subnav--underline` the rail's card undone; TableFilter's
|
|
958
|
+
* `variant="underline"` draws what is inside.
|
|
959
|
+
*
|
|
960
|
+
* Appended rather than merged into the rules above, and every selector here
|
|
961
|
+
* carries a modifier class: with none of them applied — which is every
|
|
962
|
+
* `layout="full"` app, and any app that passes `tabLayout="rail"` — not one
|
|
963
|
+
* declaration in this block matches anything.
|
|
964
|
+
* ==========================================================================*/
|
|
965
|
+
|
|
966
|
+
/* `.crmtitle` is applied to the same element as `.grid__full`, so this has to
|
|
967
|
+
undo that card's surface, border, radius, shadow and 2px padding as well as
|
|
968
|
+
the title card's own. Matched to `.crmtitle` in GenericIndex.module.scss —
|
|
969
|
+
the same inset, the same type size — so the account screen sits on the same
|
|
970
|
+
baseline as every index page. */
|
|
971
|
+
.crmtitle--flat {
|
|
972
|
+
padding: 0 0.25rem 0.85rem;
|
|
973
|
+
margin: 8px 0 0;
|
|
974
|
+
background: none;
|
|
975
|
+
border: 0;
|
|
976
|
+
border-radius: 0;
|
|
977
|
+
box-shadow: none;
|
|
978
|
+
|
|
979
|
+
h1 {
|
|
980
|
+
font-size: 1.375rem;
|
|
981
|
+
font-weight: 700;
|
|
982
|
+
letter-spacing: -0.01em;
|
|
983
|
+
line-height: 1.25;
|
|
984
|
+
color: var(--heading-color, var(--primary-color, #1b3933));
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
/* Two grid tracks become one flow column: `grid-template-columns: none` is
|
|
989
|
+
what hands the rail's 14rem back to the panel rather than leaving a hole
|
|
990
|
+
beside it. */
|
|
991
|
+
.grid__subrow--stacked {
|
|
992
|
+
display: flex;
|
|
993
|
+
flex-direction: column;
|
|
994
|
+
flex-wrap: nowrap;
|
|
995
|
+
grid-template-columns: none;
|
|
996
|
+
align-items: stretch;
|
|
997
|
+
gap: 0;
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
.grid__subrow--stacked > .grid__subnav,
|
|
1001
|
+
.grid__subrow--stacked > .grid__subcontent {
|
|
1002
|
+
flex: 0 0 auto;
|
|
1003
|
+
width: 100%;
|
|
1004
|
+
/* Without this the strip's min-content width floors the flex item, so a
|
|
1005
|
+
row of tabs widens the page instead of scrolling inside it. */
|
|
1006
|
+
min-width: 0;
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
/* The strip and the card it labels are one object: no gap, and the card's top
|
|
1010
|
+
corners squared off under it. The `8px 0` margin `.grid__subcontent` carries
|
|
1011
|
+
for the rail layout would put a hairline gap between the two. */
|
|
1012
|
+
.grid__subrow--stacked > .grid__subcontent {
|
|
1013
|
+
margin: 0;
|
|
1014
|
+
border-top-left-radius: 0;
|
|
1015
|
+
border-top-right-radius: 0;
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
.grid__subnav--underline {
|
|
1019
|
+
/* Not a card. */
|
|
1020
|
+
padding: 0;
|
|
1021
|
+
margin: 0;
|
|
1022
|
+
background: none;
|
|
1023
|
+
border: none;
|
|
1024
|
+
border-radius: 0;
|
|
1025
|
+
box-shadow: none;
|
|
1026
|
+
/* The one hairline under the whole strip; the selected tab's accent edge
|
|
1027
|
+
sits on top of it, and the content card below continues it. */
|
|
1028
|
+
border-bottom: 1px solid var(--border-color, #e1e1e1);
|
|
1029
|
+
display: block;
|
|
1030
|
+
min-width: 0;
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
/* `.grid__subnav > div` above only zeroes a margin; the strip needs the
|
|
1034
|
+
wrapper to stay a plain block, which is what TableFilter's own stylesheet
|
|
1035
|
+
expects around its scroller. */
|
|
1036
|
+
.grid__subnav--underline > div {
|
|
1037
|
+
display: block;
|
|
1038
|
+
min-width: 0;
|
|
1039
|
+
margin-bottom: 0;
|
|
1040
|
+
padding: 0;
|
|
1041
|
+
background: none;
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
/* The panel header named the tab — "Overview" under a rail whose selected
|
|
1045
|
+
item also said "Overview". Beside a strip that is the same word twice, half
|
|
1046
|
+
a centimetre apart, so the strip is the only place the tab is named.
|
|
1047
|
+
|
|
1048
|
+
Hidden rather than dropped from the markup: the rail layout renders this
|
|
1049
|
+
element and must keep rendering it, the heading is the same string the
|
|
1050
|
+
selected tab already publishes (with real tab semantics — see TableFilter's
|
|
1051
|
+
underline variant), and one class in one place is easier to be sure about
|
|
1052
|
+
than a branch inside a memoised renderer with sixteen dependencies. */
|
|
1053
|
+
.grid__subrow--stacked .gridtxt__header {
|
|
1054
|
+
display: none;
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
/* With the header gone the sections would start hard against the card's top
|
|
1058
|
+
edge. The header's own vertical padding, given back to the body. */
|
|
1059
|
+
.grid__subrow--stacked .form {
|
|
1060
|
+
padding-top: var(--spacing-md, 1rem);
|
|
1061
|
+
}
|