@visns-studio/visns-components 6.24.3 → 6.25.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/package.json +4 -2
- package/src/components/Autocomplete.jsx +189 -119
- package/src/components/DataGrid.jsx +472 -31
- package/src/components/Navigation.jsx +475 -51
- package/src/components/auth/ClientAuthFrame.jsx +5 -0
- package/src/components/auth/ClientAuthScreen.jsx +29 -0
- package/src/components/columns/ColumnRenderers.jsx +3 -46
- package/src/components/columns/StackedRow.jsx +186 -0
- package/src/components/controls/DataGridSearch.jsx +110 -2
- package/src/components/controls/DataGridSortSheet.jsx +155 -0
- package/src/components/generic/GenericAuth.jsx +50 -18
- package/src/components/generic/GenericDashboard.jsx +20 -1
- package/src/components/generic/GenericDetail.jsx +446 -259
- package/src/components/mapboxSearchBox.js +640 -0
- package/src/components/navBadges.js +63 -1
- package/src/components/navDrawer.js +147 -0
- package/src/components/sms/SmsThreadPanel.jsx +34 -6
- package/src/components/sms/smsHelpers.js +15 -0
- package/src/components/styles/ClientAuth.module.scss +39 -0
- package/src/components/styles/DataGrid.module.scss +158 -5
- package/src/components/styles/Field.module.scss +52 -1
- package/src/components/styles/Form.module.scss +82 -0
- package/src/components/styles/GenericClientPortal.module.scss +72 -20
- package/src/components/styles/GenericDashboard.module.scss +50 -0
- package/src/components/styles/GenericDetail.module.scss +63 -1
- package/src/components/styles/GenericDynamic.module.scss +23 -0
- package/src/components/styles/GenericFormBuilder.module.scss +11 -0
- package/src/components/styles/GenericIndex.module.scss +6 -1
- package/src/components/styles/Navigation.module.scss +460 -7
- package/src/components/styles/Sms.module.scss +92 -0
- package/src/components/styles/StackedRow.module.scss +182 -0
- package/src/components/styles/TicketConversation.module.scss +76 -0
- package/src/components/styles/Vault.module.scss +192 -0
- package/src/components/styles/density.css +10 -0
- package/src/components/styles/global-datagrid.css +220 -0
- package/src/components/styles/global.css +20 -0
- package/src/components/tickets/TicketConversation.jsx +13 -8
- package/src/components/utils/ConfirmDialog.js +22 -3
- package/src/components/utils/cardLayout.js +666 -0
- package/src/components/utils/contactChannels.js +130 -0
- package/src/components/utils/editPlacement.js +95 -0
- package/src/components/utils/useDensity.js +303 -7
- package/src/index.js +32 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* StackedRow — one grid row, stacked, for a screen too narrow to lay columns
|
|
3
|
+
* out side by side.
|
|
4
|
+
*
|
|
5
|
+
* ROWS, NOT CARDS. The vocabulary the mapper uses is a card's (title, subtitle,
|
|
6
|
+
* meta, badge) but nothing here is a Material card: no elevation, no inner
|
|
7
|
+
* border, no rounded panel, no margin between rows. The row keeps the grid's
|
|
8
|
+
* own zebra striping and its own divider, because a card per record is four
|
|
9
|
+
* times the height for the same four facts and a phone has the least height of
|
|
10
|
+
* anything we ship to.
|
|
11
|
+
*
|
|
12
|
+
* EVERY SIZE IS rem, AND THAT IS LOAD-BEARING. `styles/density.css` takes the
|
|
13
|
+
* root font-size to 112.5% under `data-density="large"`, so every line, gap and
|
|
14
|
+
* pad below grows by 1.125 with no rule in this file mentioning density. The px
|
|
15
|
+
* table in `utils/cardLayout.js` that fixes the ROW's height is deliberately a
|
|
16
|
+
* shade larger than 1.125× at every entry, so the computed height is always at
|
|
17
|
+
* or above what these rules need — over-allocating leaves a hairline of slack,
|
|
18
|
+
* under-allocating clips the last line, and only one of those is noticed.
|
|
19
|
+
*
|
|
20
|
+
* NO HOVER RULES ANYWHERE IN THIS FILE. It renders on a coarse pointer, where
|
|
21
|
+
* hover is either absent or sticky, and an action that appears on hover is an
|
|
22
|
+
* action a finger cannot find.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
.card {
|
|
26
|
+
display: flex;
|
|
27
|
+
gap: var(--spacing-sm, 0.5rem);
|
|
28
|
+
align-items: center;
|
|
29
|
+
box-sizing: border-box;
|
|
30
|
+
|
|
31
|
+
/* The cell is sized by the grid's fixed rowHeight; the row fills it and
|
|
32
|
+
clips rather than pushing it. Nothing inside may change the height —
|
|
33
|
+
a blank field collapses its line's CONTENT and leaves the row exactly
|
|
34
|
+
as tall as its neighbours. */
|
|
35
|
+
width: 100%;
|
|
36
|
+
height: 100%;
|
|
37
|
+
padding: 0.625rem 0;
|
|
38
|
+
overflow: hidden;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/* `min-width: 0` is the whole reason the ellipses work: a flex child's default
|
|
42
|
+
`min-width: auto` is its content's intrinsic width, so a long client name
|
|
43
|
+
would push the rail off the screen instead of being clipped. */
|
|
44
|
+
.main {
|
|
45
|
+
display: flex;
|
|
46
|
+
flex: 1 1 auto;
|
|
47
|
+
flex-direction: column;
|
|
48
|
+
gap: 0.125rem;
|
|
49
|
+
min-width: 0;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.titleRow {
|
|
53
|
+
display: flex;
|
|
54
|
+
gap: 0.375rem;
|
|
55
|
+
align-items: baseline;
|
|
56
|
+
min-width: 0;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/* The record's own number, where it has one: a quote number, a ticket number.
|
|
60
|
+
Mono because it is read digit by digit and compared, not scanned. */
|
|
61
|
+
.reference {
|
|
62
|
+
flex: 0 0 auto;
|
|
63
|
+
padding: 0 0.25rem;
|
|
64
|
+
color: var(--muted-color, #6b7280);
|
|
65
|
+
font-family: ui-monospace, 'SF Mono', Menlo, Consolas, monospace;
|
|
66
|
+
font-size: var(--font-size-xs, 0.75rem);
|
|
67
|
+
line-height: 1.375rem;
|
|
68
|
+
background: var(--border-color, #e1e1e1);
|
|
69
|
+
border-radius: var(--radius-sm, 4px);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.title,
|
|
73
|
+
.subtitle,
|
|
74
|
+
.line {
|
|
75
|
+
/* Fixed height, so nothing may take a second line. Repeated on each rather
|
|
76
|
+
than shared through a parent because the renderers put their own
|
|
77
|
+
elements inside these and a rule on the parent does not reach them. */
|
|
78
|
+
overflow: hidden;
|
|
79
|
+
min-width: 0;
|
|
80
|
+
white-space: nowrap;
|
|
81
|
+
text-overflow: ellipsis;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.title {
|
|
85
|
+
color: var(--heading-color, #1b3933);
|
|
86
|
+
font-size: 0.9375rem;
|
|
87
|
+
font-weight: var(--font-weight-semibold, 600);
|
|
88
|
+
line-height: 1.375rem;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.subtitle {
|
|
92
|
+
color: var(--muted-color, #6b7280);
|
|
93
|
+
font-size: var(--font-size-sm, 0.8125rem);
|
|
94
|
+
line-height: 1.125rem;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.line {
|
|
98
|
+
display: flex;
|
|
99
|
+
gap: 0.375rem;
|
|
100
|
+
align-items: baseline;
|
|
101
|
+
color: var(--paragraph-color, #443c2b);
|
|
102
|
+
font-size: var(--font-size-sm, 0.8125rem);
|
|
103
|
+
line-height: 1.125rem;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.lineLabel {
|
|
107
|
+
flex: 0 0 auto;
|
|
108
|
+
color: var(--muted-color, #6b7280);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.lineValue {
|
|
112
|
+
overflow: hidden;
|
|
113
|
+
min-width: 0;
|
|
114
|
+
white-space: nowrap;
|
|
115
|
+
text-overflow: ellipsis;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/* The click-to-call and click-to-mail anchors — the reason this treatment
|
|
119
|
+
exists. Accent-coloured because they are the only thing on the row that is
|
|
120
|
+
not the row's own link, and underlined so that reads as a decision rather
|
|
121
|
+
than as a colour scheme. */
|
|
122
|
+
.channel {
|
|
123
|
+
overflow: hidden;
|
|
124
|
+
color: var(--primary-color, #1b3933);
|
|
125
|
+
text-decoration: underline;
|
|
126
|
+
text-underline-offset: 0.125rem;
|
|
127
|
+
white-space: nowrap;
|
|
128
|
+
text-overflow: ellipsis;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/* Right-hand rail: the state chip over the row's action icons.
|
|
132
|
+
`auto` width, never flexed — the main column is what gives way. */
|
|
133
|
+
.rail {
|
|
134
|
+
display: flex;
|
|
135
|
+
flex: 0 0 auto;
|
|
136
|
+
flex-direction: column;
|
|
137
|
+
gap: 0.25rem;
|
|
138
|
+
align-items: flex-end;
|
|
139
|
+
justify-content: center;
|
|
140
|
+
min-height: 2.75rem;
|
|
141
|
+
max-width: 45%;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.badge {
|
|
145
|
+
overflow: hidden;
|
|
146
|
+
max-width: 100%;
|
|
147
|
+
color: var(--paragraph-color, #443c2b);
|
|
148
|
+
font-size: var(--font-size-xs, 0.75rem);
|
|
149
|
+
white-space: nowrap;
|
|
150
|
+
text-overflow: ellipsis;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/* Always drawn, never on hover. The icons inside are the grid's own
|
|
154
|
+
`renderRowAction` / `renderSetting` output and already stopPropagation, so
|
|
155
|
+
the row's link and the rail's controls cannot both fire. */
|
|
156
|
+
.actions {
|
|
157
|
+
display: flex;
|
|
158
|
+
gap: 0.125rem;
|
|
159
|
+
align-items: center;
|
|
160
|
+
justify-content: flex-end;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/* THE HIT AREA, not the glyph.
|
|
164
|
+
*
|
|
165
|
+
* `.tdaction` in DataGrid.module.scss is a 28px square, and in the table that
|
|
166
|
+
* is the whole target — the action COLUMN reserves 44px per icon of width, but
|
|
167
|
+
* nothing widens the icon itself. 28px is under every touch guideline there
|
|
168
|
+
* is, and on a card the icons sit at the end of a row whose whole body is a
|
|
169
|
+
* link, so a near miss does not do nothing: it opens the record.
|
|
170
|
+
*
|
|
171
|
+
* So the WRAPPER is grown instead of the icon. Both of the grid's action
|
|
172
|
+
* entry points put their `onClick` on the wrapper — `.rowActionTrigger` for an
|
|
173
|
+
* inline row action, the `setting-*` span for a standard one — so the target
|
|
174
|
+
* grows and nothing about the drawn icon changes. Scoped to this module, so
|
|
175
|
+
* the table's own action column is untouched. */
|
|
176
|
+
.actions > * {
|
|
177
|
+
display: inline-flex;
|
|
178
|
+
align-items: center;
|
|
179
|
+
justify-content: center;
|
|
180
|
+
min-width: 2.75rem;
|
|
181
|
+
min-height: 2.75rem;
|
|
182
|
+
}
|
|
@@ -407,6 +407,82 @@
|
|
|
407
407
|
.when { margin-left: 0; width: 100%; }
|
|
408
408
|
}
|
|
409
409
|
|
|
410
|
+
/* -- the phone ------------------------------------------------------------- */
|
|
411
|
+
|
|
412
|
+
/*
|
|
413
|
+
* At 390px the 759 block above is doing the right thing and not enough of it.
|
|
414
|
+
* It fixes the header — one column, the timestamp on its own line — and leaves
|
|
415
|
+
* the two boxes the page is actually FOR at their desktop insets.
|
|
416
|
+
*
|
|
417
|
+
* Count the gutters an entry's text sits behind on a 390px screen: the CRM's
|
|
418
|
+
* page padding, then TicketWorkspace's `.main` card at 1rem a side, then this
|
|
419
|
+
* module's `.entry` at another 1rem. That is roughly 96px of chrome, a quarter
|
|
420
|
+
* of the glass, spent on nested padding around a line of email — and the
|
|
421
|
+
* composer pays it twice over, because its textarea then has its own 0.7rem
|
|
422
|
+
* inside that. Nothing overflows; it is simply the wrong split between frame
|
|
423
|
+
* and content at a width where content is scarce.
|
|
424
|
+
*
|
|
425
|
+
* So the two inner boxes give up half their side padding. The card edges still
|
|
426
|
+
* read as edges — they are drawn with a border, not with space — and the text
|
|
427
|
+
* gets about 28px of its width back per side.
|
|
428
|
+
*
|
|
429
|
+
* READ-AND-TRIAGE IS THE BAR for this pass: the thread has to be readable and
|
|
430
|
+
* the composer has to be usable. There is deliberately NO pinned action bar —
|
|
431
|
+
* a sticky Send over a thread this narrow costs a line of the conversation
|
|
432
|
+
* permanently, and the composer is already at the end of the page where the
|
|
433
|
+
* thumb is.
|
|
434
|
+
*/
|
|
435
|
+
@media (max-width: 480px) {
|
|
436
|
+
.entry,
|
|
437
|
+
.composer {
|
|
438
|
+
padding: 0.7rem;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/* The message's own scroll cap. 24rem is 384px — on an 844px-tall phone,
|
|
442
|
+
most of what can be seen at once, so a long forwarded chain fills the
|
|
443
|
+
screen and the reply under it is a scroll away with no sign it is there.
|
|
444
|
+
16rem still shows a real message and keeps the next entry in view. */
|
|
445
|
+
.body,
|
|
446
|
+
.bodyHtml {
|
|
447
|
+
max-height: 16rem;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/* Full-bleed Send, which is the phone convention for the one action a
|
|
451
|
+
screen exists to perform — and the only place in this library where a
|
|
452
|
+
full-width primary is right rather than a banner (see the note over
|
|
453
|
+
Form.module.scss's `.buttonContainer` for the desktop argument against
|
|
454
|
+
it). Nothing shares the row with it. */
|
|
455
|
+
.composerActions {
|
|
456
|
+
justify-content: stretch;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
.send {
|
|
460
|
+
width: 100%;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/* 34px is a mouse target. Under a finger the Send button sits directly under a
|
|
465
|
+
textarea the reader has just been typing in, which is the worst place on the
|
|
466
|
+
page to miss — the miss lands in the textarea and looks like nothing
|
|
467
|
+
happened. 44px is the floor. Applied on the pointer rather than on a width,
|
|
468
|
+
because a tablet with a keyboard case is 1024px wide and still driven by a
|
|
469
|
+
thumb. */
|
|
470
|
+
@media (pointer: coarse) {
|
|
471
|
+
.send {
|
|
472
|
+
min-height: 44px;
|
|
473
|
+
height: auto;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
/* The image-unblock and quote-collapse chips in `.bodyBar` are 0.68rem
|
|
477
|
+
text in a pill; give them a real box rather than a line of type to aim
|
|
478
|
+
at. They stay small — they are qualifiers on a message, not actions on
|
|
479
|
+
the ticket — but they stop being a 16px-tall target. */
|
|
480
|
+
.bodyAction {
|
|
481
|
+
min-height: 32px;
|
|
482
|
+
padding: 0.25rem 0.6rem;
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
410
486
|
/* -- composer -------------------------------------------------------------- */
|
|
411
487
|
|
|
412
488
|
.composer {
|
|
@@ -1106,6 +1106,66 @@ input.checkControl {
|
|
|
1106
1106
|
font-size: 0.75rem; // 12px at a 16px root
|
|
1107
1107
|
}
|
|
1108
1108
|
|
|
1109
|
+
// ---------------------------------------------------------------------------
|
|
1110
|
+
// Mounted in the sidebar rail, and in the mobile drawer
|
|
1111
|
+
// ---------------------------------------------------------------------------
|
|
1112
|
+
// `actionsPlacement: 'sidebar'` — and, below 1024px, the header layout's
|
|
1113
|
+
// drawer — draws the account actions as full-width rows instead of chips. The
|
|
1114
|
+
// block that holds them advertises itself with `data-nav-actions="sidebar"`
|
|
1115
|
+
// (Navigation.jsx / Navigation.module.scss `.rail-actions`), which is all this
|
|
1116
|
+
// file needs to know. Notification.module.scss and Sms.module.scss carry the
|
|
1117
|
+
// same block; a chip and a row are two different things in three places.
|
|
1118
|
+
//
|
|
1119
|
+
// The row's box — full width, the rail's padding, left-aligned, and the LABEL
|
|
1120
|
+
// beside the icon — all come from `.rail-actions`. The trigger publishes no
|
|
1121
|
+
// label of its own, so the row publishes one as `--nav-action-label` and the
|
|
1122
|
+
// rail hangs it off the trigger's `::after`. Only what this file would
|
|
1123
|
+
// otherwise force back is restated here.
|
|
1124
|
+
[data-nav-actions='sidebar'] {
|
|
1125
|
+
// The chip's lift and scale read as a stray button in a list of rows.
|
|
1126
|
+
// The focus ring is left alone — it is the only keyboard affordance the
|
|
1127
|
+
// row has.
|
|
1128
|
+
.trigger {
|
|
1129
|
+
&:hover,
|
|
1130
|
+
&:focus,
|
|
1131
|
+
&:active {
|
|
1132
|
+
transform: none;
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
&:hover {
|
|
1136
|
+
background-color: rgba(var(--tertiary-color-rgb, 255, 255, 255), 0.05);
|
|
1137
|
+
color: var(--secondary-color);
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
// `top: 100%` drops the panel below the row. These rows are pinned to the
|
|
1142
|
+
// FOOT of the rail, so that is below the foot of the viewport. Beside the
|
|
1143
|
+
// rail, bottom-aligned with the row that opened it.
|
|
1144
|
+
.popoverWrapper {
|
|
1145
|
+
top: auto;
|
|
1146
|
+
right: auto;
|
|
1147
|
+
bottom: 0;
|
|
1148
|
+
left: calc(100% + 10px);
|
|
1149
|
+
margin-top: 0;
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
@media (max-width: 1024px) {
|
|
1153
|
+
// Inside the drawer there is nothing to the right, so the panel opens
|
|
1154
|
+
// upward from the row.
|
|
1155
|
+
.popoverWrapper {
|
|
1156
|
+
left: 0;
|
|
1157
|
+
bottom: calc(100% + 8px);
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
// Keeps the panel inside a phone's viewport: 348px of rows plus a
|
|
1161
|
+
// head, a search bar and a footer is taller than the room above the
|
|
1162
|
+
// drawer's footer.
|
|
1163
|
+
.rows {
|
|
1164
|
+
max-height: 40vh;
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1109
1169
|
// ---------------------------------------------------------------------------
|
|
1110
1170
|
// Generator
|
|
1111
1171
|
// ---------------------------------------------------------------------------
|
|
@@ -2006,6 +2066,138 @@ input.checkControl {
|
|
|
2006
2066
|
}
|
|
2007
2067
|
}
|
|
2008
2068
|
|
|
2069
|
+
// ---------------------------------------------------------------------------
|
|
2070
|
+
// The phone: six columns become two lines
|
|
2071
|
+
// ---------------------------------------------------------------------------
|
|
2072
|
+
//
|
|
2073
|
+
// The table sheds a column at a time down to 900px, which is the right answer
|
|
2074
|
+
// for a tablet and runs out at a phone: what is left below 640 is Title,
|
|
2075
|
+
// Username, Visibility, 2FA and five action buttons across roughly 350px of
|
|
2076
|
+
// glass. Every one of those tracks is then narrower than the word in its own
|
|
2077
|
+
// header, so the table stops being a table — it is six columns of hyphenated
|
|
2078
|
+
// fragments over a horizontal scrollbar.
|
|
2079
|
+
//
|
|
2080
|
+
// So each row becomes a small card of two lines: the title (with the host and
|
|
2081
|
+
// the client already sitting under it from the 1200px step) on the first, and
|
|
2082
|
+
// everything that qualifies it — username, visibility, the live 2FA code — on
|
|
2083
|
+
// the second, followed by the actions. This is SharePointGrid's `620` idea
|
|
2084
|
+
// carried one step past where columns can go: that grid drops a column at each
|
|
2085
|
+
// width until only the name and the date are left, and this is what happens
|
|
2086
|
+
// when even two tracks will not fit.
|
|
2087
|
+
//
|
|
2088
|
+
// THE SORT CONTROLS SURVIVE, which is the part that is easy to get wrong. The
|
|
2089
|
+
// obvious `thead { display: none }` takes the only sort control on the screen
|
|
2090
|
+
// with it — there is no sort menu in the toolbar to fall back on. Instead the
|
|
2091
|
+
// header row collapses to a wrapped strip of just the two sortable headings
|
|
2092
|
+
// (`SORTS` in VaultManager.jsx is Title and Username, and they are the first
|
|
2093
|
+
// two `th`), which reads as the row of chips it now looks like. The four
|
|
2094
|
+
// non-sortable headings are labels for cells that no longer sit under them, so
|
|
2095
|
+
// those do go.
|
|
2096
|
+
@media (max-width: 640px) {
|
|
2097
|
+
// The header strip. `display: block` on the `tr` and `inline-block` on the
|
|
2098
|
+
// `th` rather than flex, because a table row is not a flex container in
|
|
2099
|
+
// every engine without also restating `display` on the `table` and the
|
|
2100
|
+
// `tbody`, and doing that throws away the browser's row model for nothing.
|
|
2101
|
+
.table thead tr {
|
|
2102
|
+
display: block;
|
|
2103
|
+
padding: 0.35rem 0.7rem;
|
|
2104
|
+
// The rule under the header moves from the cells to the row, because
|
|
2105
|
+
// the cells are now a wrapped strip and a hairline under each of them
|
|
2106
|
+
// draws two short underlines instead of one line.
|
|
2107
|
+
border-bottom: 1px solid var(--v-line);
|
|
2108
|
+
background: var(--v-surface);
|
|
2109
|
+
}
|
|
2110
|
+
|
|
2111
|
+
.table thead th {
|
|
2112
|
+
display: inline-block;
|
|
2113
|
+
padding: 0.25rem 0.5rem 0.25rem 0;
|
|
2114
|
+
border-bottom: 0;
|
|
2115
|
+
}
|
|
2116
|
+
|
|
2117
|
+
// Everything after the two sortable columns. `nth-child` and not a class,
|
|
2118
|
+
// because these are the toolkit-free `<th>`s the component writes inline
|
|
2119
|
+
// and giving four of them a class each to hide them all is noise.
|
|
2120
|
+
.table thead th:nth-child(n + 3) {
|
|
2121
|
+
display: none;
|
|
2122
|
+
}
|
|
2123
|
+
|
|
2124
|
+
// A row is a card. The border that separated rows becomes the card's own
|
|
2125
|
+
// bottom edge, which is the same hairline in the same place.
|
|
2126
|
+
.table tbody tr {
|
|
2127
|
+
display: block;
|
|
2128
|
+
padding: 0.6rem 0.7rem;
|
|
2129
|
+
}
|
|
2130
|
+
|
|
2131
|
+
.table tbody td {
|
|
2132
|
+
display: inline;
|
|
2133
|
+
padding: 0;
|
|
2134
|
+
vertical-align: baseline;
|
|
2135
|
+
}
|
|
2136
|
+
|
|
2137
|
+
// Line one: the title block, which already carries the host and the client
|
|
2138
|
+
// underneath it from the 1200px step.
|
|
2139
|
+
.table tbody td:first-child {
|
|
2140
|
+
display: block;
|
|
2141
|
+
margin-bottom: 0.35rem;
|
|
2142
|
+
}
|
|
2143
|
+
|
|
2144
|
+
// Line two: username, visibility and the 2FA chip, spaced by a real gap
|
|
2145
|
+
// rather than by the padding a table cell would have had. Wrapped in a
|
|
2146
|
+
// flow of inline boxes so an em dash placeholder costs nothing.
|
|
2147
|
+
.table tbody td:nth-child(2),
|
|
2148
|
+
.table tbody td:nth-child(4),
|
|
2149
|
+
.table tbody td:nth-child(5) {
|
|
2150
|
+
margin-right: 0.6rem;
|
|
2151
|
+
color: var(--v-muted);
|
|
2152
|
+
font-size: 0.8rem;
|
|
2153
|
+
}
|
|
2154
|
+
|
|
2155
|
+
// The actions get a line of their own: five 29px buttons plus their gaps is
|
|
2156
|
+
// 165px, which is half the width of the glass and would push the facts
|
|
2157
|
+
// beside them down to nothing.
|
|
2158
|
+
.table tbody td.actionsCell {
|
|
2159
|
+
display: block;
|
|
2160
|
+
margin-top: 0.5rem;
|
|
2161
|
+
}
|
|
2162
|
+
|
|
2163
|
+
// Fitted mode pins every row to a single 58px band so the page size is
|
|
2164
|
+
// arithmetic (see `.tableWrapFitted` above). A two-line card does not fit
|
|
2165
|
+
// in 58px and would be clipped mid-row, so the pin comes off here and the
|
|
2166
|
+
// list scrolls instead. The pager still works; it just no longer gets a
|
|
2167
|
+
// whole number of rows, which is a trade a phone was never going to win.
|
|
2168
|
+
.tableWrapFitted {
|
|
2169
|
+
overflow-y: auto;
|
|
2170
|
+
|
|
2171
|
+
tbody tr,
|
|
2172
|
+
tbody td {
|
|
2173
|
+
height: auto;
|
|
2174
|
+
max-height: none;
|
|
2175
|
+
white-space: normal;
|
|
2176
|
+
}
|
|
2177
|
+
}
|
|
2178
|
+
}
|
|
2179
|
+
|
|
2180
|
+
// The ⌘K quick-search popover's rows, at the same width and for the same
|
|
2181
|
+
// reason. `.rows > .row` and not `.row`, because the geometry above is stated
|
|
2182
|
+
// at 0,2,0 precisely so a host cannot outrank it — and a 0,1,0 override in
|
|
2183
|
+
// here would not outrank it either.
|
|
2184
|
+
@media (max-width: 640px) {
|
|
2185
|
+
.rows > .row {
|
|
2186
|
+
grid-template-columns: minmax(0, 1fr);
|
|
2187
|
+
gap: 0.4rem;
|
|
2188
|
+
}
|
|
2189
|
+
|
|
2190
|
+
// Hard left on the second line rather than flush right against an edge the
|
|
2191
|
+
// title no longer reaches: the cluster now starts under the title it
|
|
2192
|
+
// belongs to, which is what keeps the pairing readable once they are two
|
|
2193
|
+
// lines rather than one.
|
|
2194
|
+
.rowActions {
|
|
2195
|
+
justify-self: start;
|
|
2196
|
+
margin-left: 0;
|
|
2197
|
+
flex-wrap: wrap;
|
|
2198
|
+
}
|
|
2199
|
+
}
|
|
2200
|
+
|
|
2009
2201
|
// ---------------------------------------------------------------------------
|
|
2010
2202
|
// Access log
|
|
2011
2203
|
// ---------------------------------------------------------------------------
|
|
@@ -83,3 +83,13 @@
|
|
|
83
83
|
:root[data-density='large'] body.tablet-mode {
|
|
84
84
|
--grid-checkbox-col-width: 80px;
|
|
85
85
|
}
|
|
86
|
+
|
|
87
|
+
/* And again for a coarse primary pointer, which switches the grid into the
|
|
88
|
+
same tablet geometry with no view config — see utils/useDensity.js, which
|
|
89
|
+
ORs the two signals for the JS half. Restated rather than shared: this is
|
|
90
|
+
plain CSS, so a class and a media query cannot be one selector. */
|
|
91
|
+
@media (pointer: coarse) {
|
|
92
|
+
:root[data-density='large'] body {
|
|
93
|
+
--grid-checkbox-col-width: 80px;
|
|
94
|
+
}
|
|
95
|
+
}
|