@visns-studio/visns-components 6.24.4 → 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 +163 -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
|
@@ -7,8 +7,46 @@
|
|
|
7
7
|
display: block;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
+
/* ============================================================================
|
|
11
|
+
* THE FORM GRID — four tracks, and the two it gives up when there is no room
|
|
12
|
+
* ----------------------------------------------------------------------------
|
|
13
|
+
* The grid was a flat `1fr 1fr 1fr 1fr` with nothing underneath it, so a
|
|
14
|
+
* `size: "quarter"` field held one of four tracks at EVERY width: about 80px
|
|
15
|
+
* on a 390px phone, which is a label that cannot be read over an input that
|
|
16
|
+
* cannot be typed in. Two tracks at 768 and one at 640 is the collapse.
|
|
17
|
+
*
|
|
18
|
+
* WHY A CONTAINER QUERY AND NOT A MEDIA QUERY. The window's width is not the
|
|
19
|
+
* question. The same `<Form>` renders in a full-page panel, in a 560px sheet,
|
|
20
|
+
* in a modal, and in a narrow side rail beside a grid — and a form in a 420px
|
|
21
|
+
* rail on a 2560px monitor has exactly the problem a form on a phone has. A
|
|
22
|
+
* media query answers "how big is the glass"; this layout needs "how much room
|
|
23
|
+
* do I actually have", and only the container knows that. The library already
|
|
24
|
+
* reaches for this where the same argument holds (Integrations' `.fields`,
|
|
25
|
+
* global-datagrid's native date filter), so it is the house pattern rather
|
|
26
|
+
* than a new dependency. Nothing here pins a browser floor that would forbid
|
|
27
|
+
* it; the same three engines that ship `color-mix()` — used freely across this
|
|
28
|
+
* library — ship container queries.
|
|
29
|
+
*
|
|
30
|
+
* ONE CONSEQUENCE WORTH KNOWING, because it is not obvious from the one line
|
|
31
|
+
* that causes it: `container-type: inline-size` also applies LAYOUT
|
|
32
|
+
* containment, which makes this element the containing block for any
|
|
33
|
+
* `position: fixed` descendant. Inside a form that is the colour picker's
|
|
34
|
+
* click-away scrim (Field.jsx's `cover`), which now covers the form rather
|
|
35
|
+
* than the whole window — clicking another field still dismisses the picker,
|
|
36
|
+
* clicking the page header no longer does. The scrim is the only fixed thing
|
|
37
|
+
* that renders in here; `.polActions` in GenericDynamic is a SIBLING of the
|
|
38
|
+
* container, not a child, so it is untouched.
|
|
39
|
+
*
|
|
40
|
+
* The item classes that answer these queries live in Field.module.scss
|
|
41
|
+
* (`.halfItem` / `.qtrItem`, written by Field.jsx from `settings.size`) —
|
|
42
|
+
* a different module, but the query is unnamed, so it resolves against the
|
|
43
|
+
* nearest ancestor container whichever file declared it. Keep the two ends in
|
|
44
|
+
* step: the breakpoints below and the ones in Field.module.scss are one rule
|
|
45
|
+
* written in two files because the class names are hashed per module.
|
|
46
|
+
* ==========================================================================*/
|
|
10
47
|
.formcontainer {
|
|
11
48
|
width: 100%;
|
|
49
|
+
container-type: inline-size;
|
|
12
50
|
|
|
13
51
|
form {
|
|
14
52
|
width: 100%;
|
|
@@ -24,6 +62,50 @@
|
|
|
24
62
|
}
|
|
25
63
|
}
|
|
26
64
|
|
|
65
|
+
/* Two tracks. A half-width field is then the full row and a quarter is half of
|
|
66
|
+
one, which is the same proportion the four-track layout gave them — the
|
|
67
|
+
shape survives, it just has less of it.
|
|
68
|
+
*
|
|
69
|
+
* WHERE THESE TWO NUMBERS BITE, since they are container widths and not window
|
|
70
|
+
* widths and the two do not land in the same places. StandardModal's sizes are
|
|
71
|
+
* 420 / 560 / 800 and `.modal__content` spends 1rem a side, so:
|
|
72
|
+
*
|
|
73
|
+
* large (768 content) — exactly on this boundary, and `max-width` is
|
|
74
|
+
* inclusive, so a large dialog draws TWO tracks on a
|
|
75
|
+
* desktop where it used to draw four. Quarters go from
|
|
76
|
+
* ~180px to ~370px, which is roomier rather than
|
|
77
|
+
* wrong, but it is a visible change and this is where
|
|
78
|
+
* to look when somebody asks about it.
|
|
79
|
+
* medium (528 content) — one track.
|
|
80
|
+
* small (388 content) — one track.
|
|
81
|
+
* a 22rem side rail — one track.
|
|
82
|
+
*
|
|
83
|
+
* If a form is wanted back at four tracks in a large dialog, the fix is this
|
|
84
|
+
* number and only this number — but it has to move in Field.module.scss and
|
|
85
|
+
* GenericDynamic.module.scss with it. */
|
|
86
|
+
@container (max-width: 768px) {
|
|
87
|
+
.formcontainer form {
|
|
88
|
+
grid-template-columns: 1fr 1fr;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.halfBuilderItem {
|
|
92
|
+
grid-column: 1 / -1;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* One track. Below this a two-up row is two ~150px fields, which is where a
|
|
97
|
+
date input starts clipping its own year. */
|
|
98
|
+
@container (max-width: 640px) {
|
|
99
|
+
.formcontainer form {
|
|
100
|
+
grid-template-columns: 1fr;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.qtrBuilderItem,
|
|
104
|
+
.halfBuilderItem {
|
|
105
|
+
grid-column: 1 / -1;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
27
109
|
.formItem {
|
|
28
110
|
width: 100%;
|
|
29
111
|
position: relative;
|
|
@@ -25,71 +25,123 @@
|
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
// ============================================================================
|
|
29
|
+
// Header chrome — tokenised, NOT restyled.
|
|
30
|
+
// ----------------------------------------------------------------------------
|
|
31
|
+
// Every `--client-portal-*` property below falls back to the value this file
|
|
32
|
+
// carried before, so a host that defines none of them renders byte-identically
|
|
33
|
+
// to the version that shipped. They exist because the header is the one piece
|
|
34
|
+
// of a client portal that has to look like the company whose portal it is, and
|
|
35
|
+
// the markup for it lives in this library rather than in the host app — so
|
|
36
|
+
// without a hook the only ways to brand it are forking the component or an
|
|
37
|
+
// attribute selector against a generated class name, and both of those break
|
|
38
|
+
// the next time this file is touched.
|
|
39
|
+
//
|
|
40
|
+
// Nothing here reads a host token directly. A host brands the header by
|
|
41
|
+
// defining these properties (typically on `:root`), and can undo it by not.
|
|
42
|
+
// ============================================================================
|
|
43
|
+
|
|
28
44
|
.portalHeader {
|
|
29
45
|
display: flex;
|
|
30
46
|
justify-content: space-between;
|
|
31
47
|
align-items: center;
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
48
|
+
gap: var(--client-portal-header-gap, 1rem);
|
|
49
|
+
padding: var(--client-portal-header-padding, 0.5rem 1.25rem);
|
|
50
|
+
background: var(--client-portal-header-bg, var(--tertiary-color, white));
|
|
51
|
+
color: var(--client-portal-header-ink, inherit);
|
|
52
|
+
border-bottom: var(--client-portal-header-border, 0 solid transparent);
|
|
53
|
+
box-shadow: var(--client-portal-header-shadow, 0 1px 3px rgba(0, 0, 0, 0.05));
|
|
35
54
|
z-index: 10;
|
|
36
|
-
|
|
55
|
+
// Increased height to accommodate larger logo
|
|
56
|
+
height: var(--client-portal-header-height, 64px);
|
|
57
|
+
flex: none;
|
|
37
58
|
}
|
|
38
59
|
|
|
39
60
|
.logoContainer {
|
|
40
61
|
display: flex;
|
|
41
62
|
align-items: center;
|
|
42
|
-
margin-left: -1.25rem;
|
|
63
|
+
margin-left: var(--client-portal-logo-offset, -1.25rem);
|
|
43
64
|
padding-left: 0.5rem;
|
|
65
|
+
min-width: 0;
|
|
44
66
|
}
|
|
45
67
|
|
|
46
68
|
.logo {
|
|
47
|
-
height: 52px;
|
|
48
|
-
max-width: 250px;
|
|
69
|
+
height: var(--client-portal-logo-height, 52px);
|
|
70
|
+
max-width: var(--client-portal-logo-max-width, 250px);
|
|
49
71
|
object-fit: contain;
|
|
72
|
+
// `none` unless the host asks otherwise. A dark header needs the reverse of
|
|
73
|
+
// a logo drawn for a white one, and the host cannot swap the file from
|
|
74
|
+
// here — `brightness(0) invert(1)` is the usual answer.
|
|
75
|
+
filter: var(--client-portal-logo-filter, none);
|
|
50
76
|
}
|
|
51
77
|
|
|
52
78
|
.userInfo {
|
|
53
79
|
display: flex;
|
|
54
80
|
align-items: center;
|
|
55
|
-
gap: 0.75rem;
|
|
81
|
+
gap: var(--client-portal-user-gap, 0.75rem);
|
|
82
|
+
min-width: 0;
|
|
56
83
|
}
|
|
57
84
|
|
|
58
85
|
.welcomeText {
|
|
59
|
-
font-size: 0.8rem;
|
|
60
|
-
color: var(--paragraph-color, #555);
|
|
86
|
+
font-size: var(--client-portal-welcome-size, 0.8rem);
|
|
87
|
+
color: var(--client-portal-header-ink, var(--paragraph-color, #555));
|
|
88
|
+
white-space: nowrap;
|
|
89
|
+
overflow: hidden;
|
|
90
|
+
text-overflow: ellipsis;
|
|
61
91
|
}
|
|
62
92
|
|
|
63
93
|
.logoutButton {
|
|
64
94
|
display: flex;
|
|
65
95
|
align-items: center;
|
|
66
96
|
justify-content: center;
|
|
67
|
-
|
|
68
|
-
|
|
97
|
+
flex: none;
|
|
98
|
+
width: var(--client-portal-logout-size, 28px);
|
|
99
|
+
height: var(--client-portal-logout-size, 28px);
|
|
69
100
|
padding: 0;
|
|
70
101
|
background-color: transparent;
|
|
71
|
-
border: 1px solid
|
|
72
|
-
|
|
73
|
-
|
|
102
|
+
border: 1px solid
|
|
103
|
+
var(
|
|
104
|
+
--client-portal-header-line,
|
|
105
|
+
rgba(var(--paragraph-color-rgb, 0, 0, 0), 0.15)
|
|
106
|
+
);
|
|
107
|
+
border-radius: var(--client-portal-logout-radius, var(--radius, 4px));
|
|
108
|
+
color: var(--client-portal-header-ink, var(--paragraph-color, #555));
|
|
74
109
|
cursor: pointer;
|
|
75
110
|
transition: all 0.2s ease;
|
|
76
111
|
|
|
77
112
|
&:hover {
|
|
78
|
-
background-color:
|
|
79
|
-
|
|
80
|
-
|
|
113
|
+
background-color: var(
|
|
114
|
+
--client-portal-logout-hover-bg,
|
|
115
|
+
rgba(var(--primary-rgb, 0, 123, 255), 0.05)
|
|
116
|
+
);
|
|
117
|
+
border-color: var(
|
|
118
|
+
--client-portal-logout-hover-line,
|
|
119
|
+
var(--primary-color, #0f4229)
|
|
120
|
+
);
|
|
121
|
+
color: var(
|
|
122
|
+
--client-portal-logout-hover-ink,
|
|
123
|
+
var(--primary-color, #0f4229)
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
&:focus-visible {
|
|
128
|
+
outline: 2px solid
|
|
129
|
+
var(--client-portal-focus-ring, var(--primary-color, #0f4229));
|
|
130
|
+
outline-offset: 2px;
|
|
81
131
|
}
|
|
82
132
|
}
|
|
83
133
|
|
|
84
134
|
.portalContent {
|
|
85
135
|
display: flex;
|
|
86
136
|
flex: 1;
|
|
87
|
-
height
|
|
137
|
+
// Subtract header height — the same property the header is sized from, so
|
|
138
|
+
// a host that changes one cannot leave the two disagreeing.
|
|
139
|
+
height: calc(100vh - var(--client-portal-header-height, 64px));
|
|
88
140
|
}
|
|
89
141
|
|
|
90
142
|
.mainContent {
|
|
91
143
|
flex: 1;
|
|
92
|
-
padding: 1.25rem;
|
|
144
|
+
padding: var(--client-portal-content-padding, 1.25rem);
|
|
93
145
|
overflow-y: auto;
|
|
94
146
|
color: var(--paragraph-color, #333);
|
|
95
147
|
}
|
|
@@ -756,3 +756,53 @@
|
|
|
756
756
|
}
|
|
757
757
|
|
|
758
758
|
.modal__content { padding: 1.25rem; }
|
|
759
|
+
|
|
760
|
+
// ============================================================================
|
|
761
|
+
// The card, on a phone
|
|
762
|
+
// ----------------------------------------------------------------------------
|
|
763
|
+
// LAST IN THE FILE ON PURPOSE. Every declaration below overrides one written
|
|
764
|
+
// earlier at the same specificity — `.widgetNo` is a single class here and a
|
|
765
|
+
// single class there — so the same block sitting up beside the `grid__*` span
|
|
766
|
+
// collapse it belongs with would be beaten by the rules it exists to override
|
|
767
|
+
// and do nothing at all. Media queries carry no weight of their own.
|
|
768
|
+
//
|
|
769
|
+
// What changes and why: below 700px every card is one column wide, and the
|
|
770
|
+
// reserved heights in this sheet exist so that cards SIDE BY SIDE line their
|
|
771
|
+
// labels, values and buttons up with one another. Nothing sits beside a card
|
|
772
|
+
// at this width. A stat tile is a label, a number and a button — three lines
|
|
773
|
+
// — and it was reserving 180px for them: 40 of padding, 24 of gaps, 24 held
|
|
774
|
+
// open under a 17px label and 48 held open under a 32px number. That is one
|
|
775
|
+
// and a half cards to a 402px screen, on the surface whose whole job is to
|
|
776
|
+
// answer three questions at a glance.
|
|
777
|
+
//
|
|
778
|
+
// Nothing here shrinks the number itself. The value is the content; the empty
|
|
779
|
+
// space around it was the problem.
|
|
780
|
+
// ============================================================================
|
|
781
|
+
|
|
782
|
+
@media (max-width: 700px) {
|
|
783
|
+
// The token's own default rather than a flat value, so a project that has
|
|
784
|
+
// set --dash-card-padding keeps its figure at every width; only the
|
|
785
|
+
// shipped default tightens.
|
|
786
|
+
.grid {
|
|
787
|
+
--dash-pad: var(--dash-card-padding, 0.9rem);
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
.widgetItem {
|
|
791
|
+
gap: 0.5rem;
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
// Both floors go: each element is still exactly as tall as its content,
|
|
795
|
+
// and there is no neighbouring card to hold a line for.
|
|
796
|
+
.widgetTitle {
|
|
797
|
+
min-height: 0;
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
.widgetNo,
|
|
801
|
+
.widgetNo-alt {
|
|
802
|
+
min-height: 0;
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
.widgetLink {
|
|
806
|
+
padding-top: 0.25rem;
|
|
807
|
+
}
|
|
808
|
+
}
|
|
@@ -1207,10 +1207,15 @@
|
|
|
1207
1207
|
z-index: 1000;
|
|
1208
1208
|
}
|
|
1209
1209
|
|
|
1210
|
+
/* See DataGrid.module.scss's `.modalWide` for the whole note. Short version:
|
|
1211
|
+
`min-width` outranks both `width` and `max-width`, so a flat 600px floor put
|
|
1212
|
+
210px of the dialog — its right edge and its close button — off the side of
|
|
1213
|
+
a 390px phone. 90vw is the width this rule already asks for, so the floor
|
|
1214
|
+
can never outrank the line above it. */
|
|
1210
1215
|
.modalWide {
|
|
1211
1216
|
width: 90vw;
|
|
1212
1217
|
max-width: var(--detail-max-width, 1400px);
|
|
1213
|
-
min-width: 600px;
|
|
1218
|
+
min-width: min(600px, 90vw);
|
|
1214
1219
|
}
|
|
1215
1220
|
|
|
1216
1221
|
.modal {
|
|
@@ -1384,6 +1389,63 @@
|
|
|
1384
1389
|
0 12px 24px -12px color-mix(in srgb, var(--primary-color, #1b3933) 35%, transparent);
|
|
1385
1390
|
}
|
|
1386
1391
|
|
|
1392
|
+
/* -- the same cluster, in the title row ----------------------------------- */
|
|
1393
|
+
|
|
1394
|
+
/* `editPlacement: "header"` (see utils/editPlacement.js) hangs the SAME
|
|
1395
|
+
cluster off the breadcrumb row instead of pinning it to the viewport. It is
|
|
1396
|
+
a modifier rather than an edit to `.polActions` on purpose: the floating bar
|
|
1397
|
+
is still the default on every other detail page in every other application,
|
|
1398
|
+
and the two `:has(> :nth-child(2))` rules above are the whole of its
|
|
1399
|
+
treatment.
|
|
1400
|
+
|
|
1401
|
+
Which is exactly what has to come off here. A pill with a blurred backdrop
|
|
1402
|
+
is how a control says "I am floating over the page"; sitting in a header bar
|
|
1403
|
+
it reads as a second bar drawn inside the first, and the elevation a lone
|
|
1404
|
+
button carries reads as a button that has come loose. In the title row the
|
|
1405
|
+
ground is the header, so the cluster is nothing but its buttons — `.btn`
|
|
1406
|
+
itself is untouched, so they are the same buttons as everywhere else.
|
|
1407
|
+
|
|
1408
|
+
Both selectors are doubled (`.polActions.polActionsHeader`) to out-specify
|
|
1409
|
+
the two rules above, which `:has()` lifts to two classes' worth. Source
|
|
1410
|
+
order after them is not enough on its own. */
|
|
1411
|
+
.polActions.polActionsHeader {
|
|
1412
|
+
position: static;
|
|
1413
|
+
inset: auto;
|
|
1414
|
+
z-index: auto;
|
|
1415
|
+
padding: 0;
|
|
1416
|
+
border: 0;
|
|
1417
|
+
border-radius: 0;
|
|
1418
|
+
background: none;
|
|
1419
|
+
backdrop-filter: none;
|
|
1420
|
+
box-shadow: none;
|
|
1421
|
+
flex-wrap: wrap;
|
|
1422
|
+
justify-content: flex-end;
|
|
1423
|
+
}
|
|
1424
|
+
|
|
1425
|
+
.polActions.polActionsHeader > * {
|
|
1426
|
+
box-shadow: none;
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1429
|
+
/* The right-hand end of the title row: the total, a host's `actions`, and —
|
|
1430
|
+
under this placement — the cluster. `margin-left: auto` does nothing while
|
|
1431
|
+
the row fits (the bar is already `justify-content: space-between`); it is
|
|
1432
|
+
what keeps the group on the right once it has WRAPPED onto its own line. */
|
|
1433
|
+
.titleActions {
|
|
1434
|
+
margin-left: auto;
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1437
|
+
/* Wrapping is opt-in, and only for a header that actually received a cluster.
|
|
1438
|
+
A record title is long ("Verkada Camera & Access Control" under a client
|
|
1439
|
+
name) and a row of buttons is not compressible, so without this the title
|
|
1440
|
+
is squeezed to a word per line to hold a shape that stopped fitting. The
|
|
1441
|
+
buttons take their own line instead. Left alone for every page that has not
|
|
1442
|
+
opted in, so no existing header can start wrapping. */
|
|
1443
|
+
.crmtitle:has(.polActionsHeader),
|
|
1444
|
+
.crmtitle--alternate:has(.polActionsHeader) {
|
|
1445
|
+
flex-wrap: wrap;
|
|
1446
|
+
row-gap: var(--spacing-sm, 0.5rem);
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1387
1449
|
/* -- states --------------------------------------------------------------- */
|
|
1388
1450
|
|
|
1389
1451
|
.noDataMessage {
|
|
@@ -176,8 +176,17 @@
|
|
|
176
176
|
font-size: 1rem;
|
|
177
177
|
}
|
|
178
178
|
|
|
179
|
+
/* A third copy of the four-track form grid — this one wraps Field.jsx directly
|
|
180
|
+
(see GenericDynamic.jsx), so it has to declare the container too, or the
|
|
181
|
+
collapse written in Field.module.scss finds nothing to measure and the
|
|
182
|
+
fields stay four-up on a phone. `container-type` is the whole of what is
|
|
183
|
+
added here; the reasoning is over Form.module.scss's `.formcontainer`.
|
|
184
|
+
`.polActions` in this module is `position: fixed`, but it is a SIBLING of
|
|
185
|
+
this element rather than a descendant, so the layout containment that comes
|
|
186
|
+
with `container-type` does not move it. */
|
|
179
187
|
.formcontainer {
|
|
180
188
|
width: 100%;
|
|
189
|
+
container-type: inline-size;
|
|
181
190
|
|
|
182
191
|
form {
|
|
183
192
|
width: 100%;
|
|
@@ -188,6 +197,20 @@
|
|
|
188
197
|
}
|
|
189
198
|
}
|
|
190
199
|
|
|
200
|
+
/* The track list, narrowed. What a "half" and a "quarter" mean against the
|
|
201
|
+
narrower list is re-stated in Field.module.scss, which owns those classes. */
|
|
202
|
+
@container (max-width: 768px) {
|
|
203
|
+
.formcontainer form {
|
|
204
|
+
grid-template-columns: 1fr 1fr;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
@container (max-width: 640px) {
|
|
209
|
+
.formcontainer form {
|
|
210
|
+
grid-template-columns: 1fr;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
191
214
|
.formItem {
|
|
192
215
|
width: 100%;
|
|
193
216
|
position: relative;
|
|
@@ -2929,6 +2929,17 @@ select:not(:placeholder-shown) + .fi__span {
|
|
|
2929
2929
|
opacity: 1;
|
|
2930
2930
|
}
|
|
2931
2931
|
|
|
2932
|
+
/* No pointer means no hover, and a hidden control that is only revealed by an
|
|
2933
|
+
event the device cannot generate is not hidden, it is GONE — on a tablet
|
|
2934
|
+
these rows had no edit and no delete at all. `focus-within` above already
|
|
2935
|
+
covers the keyboard; this covers the finger. The same pattern, and the same
|
|
2936
|
+
sentence, as `_associationManager.scss`'s `.actions`. */
|
|
2937
|
+
@media (hover: none), (pointer: coarse) {
|
|
2938
|
+
.cActions {
|
|
2939
|
+
opacity: 1;
|
|
2940
|
+
}
|
|
2941
|
+
}
|
|
2942
|
+
|
|
2932
2943
|
.cDelete:hover {
|
|
2933
2944
|
color: var(--secondary-color) !important;
|
|
2934
2945
|
background: rgba(var(--secondary-rgb, 60, 191, 125), 0.1) !important;
|
|
@@ -1208,10 +1208,15 @@
|
|
|
1208
1208
|
z-index: 1000;
|
|
1209
1209
|
}
|
|
1210
1210
|
|
|
1211
|
+
/* See DataGrid.module.scss's `.modalWide` for the whole note. Short version:
|
|
1212
|
+
`min-width` outranks both `width` and `max-width`, so a flat 600px floor put
|
|
1213
|
+
210px of the dialog — its right edge and its close button — off the side of
|
|
1214
|
+
a 390px phone. 90vw is the width this rule already asks for, so the floor
|
|
1215
|
+
can never outrank the line above it. */
|
|
1211
1216
|
.modalWide {
|
|
1212
1217
|
width: 90vw;
|
|
1213
1218
|
max-width: var(--detail-max-width, 1400px);
|
|
1214
|
-
min-width: 600px;
|
|
1219
|
+
min-width: min(600px, 90vw);
|
|
1215
1220
|
}
|
|
1216
1221
|
|
|
1217
1222
|
.modal {
|