@visns-studio/visns-components 6.6.4 → 6.15.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 +1623 -18
- package/package.json +10 -4
- package/src/components/DataGrid.jsx +171 -37
- package/src/components/Fetch.jsx +80 -3
- package/src/components/Form.jsx +9 -0
- package/src/components/Navigation.jsx +109 -50
- package/src/components/Notification.jsx +279 -9
- package/src/components/TableFilter.jsx +14 -1
- package/src/components/auth/AuthBrandPanel.jsx +43 -0
- package/src/components/auth/AuthLoading.jsx +78 -0
- package/src/components/auth/AuthShell.jsx +59 -0
- package/src/components/auth/ClientAuth.jsx +161 -0
- package/src/components/auth/ClientAuthFrame.jsx +59 -0
- package/src/components/auth/ClientLogin.jsx +266 -55
- package/src/components/auth/ClientOTPVerify.jsx +587 -115
- package/src/components/auth/ImpersonateGate.jsx +254 -0
- package/src/components/auth/Login.jsx +134 -41
- package/src/components/auth/LogoutScreen.jsx +112 -0
- package/src/components/auth/Reset.jsx +134 -77
- package/src/components/auth/TwoFactorAuth.jsx +475 -297
- package/src/components/auth/Verify.jsx +237 -126
- package/src/components/auth/authEndpoints.js +105 -0
- package/src/components/auth/authFont.js +23 -0
- package/src/components/auth/authHelpers.js +465 -0
- package/src/components/auth/clientAuthProtocols.js +240 -0
- package/src/components/auth/useOptionalRouter.js +49 -0
- package/src/components/callQueue/CallQueuePop.jsx +1502 -0
- package/src/components/callQueue/CallQueueSettings.jsx +508 -0
- package/src/components/callQueue/callQueueHelpers.js +280 -0
- package/src/components/callQueue/callQueueSettingsHelpers.js +75 -0
- package/src/components/columns/ColumnRenderers.jsx +53 -7
- package/src/components/generic/GenericAuth.jsx +163 -96
- package/src/components/generic/GenericDetail.jsx +215 -90
- package/src/components/generic/GenericIndex.jsx +20 -47
- package/src/components/generic/GenericMain.jsx +5 -0
- package/src/components/generic/GroupedReportRenderer.jsx +1 -5
- package/src/components/generic/StandardModal.jsx +17 -5
- package/src/components/generic/reportSemanticSteps/SemanticEntityStep.jsx +1 -6
- package/src/components/generic/reportSemanticSteps/SemanticFieldsStep.jsx +4 -11
- package/src/components/generic/reportSemanticSteps/SemanticFiltersStep.jsx +9 -27
- package/src/components/generic/reportSemanticSteps/SemanticGroupingStep.jsx +5 -13
- package/src/components/generic/reportSemanticSteps/SemanticParameterPrompt.jsx +5 -13
- package/src/components/generic/reportSemanticSteps/SemanticPreviewStep.jsx +14 -31
- package/src/components/generic/reportSemanticSteps/SemanticRelationsStep.jsx +3 -9
- package/src/components/generic/reportSemanticSteps/SemanticValueInput.jsx +2 -15
- package/src/components/navActive.js +60 -0
- package/src/components/notify/desktopNotifications.js +256 -0
- package/src/components/sketch/SketchField.jsx +12 -2
- package/src/components/sms/SmsComposeModal.jsx +495 -0
- package/src/components/sms/SmsInbox.jsx +596 -0
- package/src/components/sms/SmsInboxBadge.jsx +468 -0
- package/src/components/sms/SmsLineSettings.jsx +854 -0
- package/src/components/sms/SmsThreadPanel.jsx +1184 -0
- package/src/components/sms/smsEndpoints.js +56 -0
- package/src/components/sms/smsHelpers.js +1075 -0
- package/src/components/sms/smsLiveState.js +132 -0
- package/src/components/sms/useSmsLive.js +307 -0
- package/src/components/styles/CallQueuePop.module.scss +646 -0
- package/src/components/styles/CallQueueSettings.module.scss +460 -0
- package/src/components/styles/ClientAuth.module.scss +229 -0
- package/src/components/styles/GenericDetail.module.scss +153 -26
- package/src/components/styles/GenericIndex.module.scss +13 -0
- package/src/components/styles/GenericMain.module.scss +19 -2
- package/src/components/styles/ImpersonateGate.module.scss +85 -0
- package/src/components/styles/Login.module.scss +13 -122
- package/src/components/styles/LogoutScreen.module.scss +89 -0
- package/src/components/styles/Navigation.module.scss +509 -223
- package/src/components/styles/Notification.module.scss +103 -0
- package/src/components/styles/Reset.module.scss +52 -186
- package/src/components/styles/Sms.module.scss +2382 -0
- package/src/components/styles/TableFilter.module.scss +118 -98
- package/src/components/styles/TwoFactorAuth.module.scss +115 -176
- package/src/components/styles/Vault.module.scss +1983 -0
- package/src/components/styles/Verify.module.scss +57 -180
- package/src/components/styles/_authBrandPanel.scss +163 -0
- package/src/components/styles/_authShell.scss +369 -0
- package/src/components/utils/buildEnv.js +67 -0
- package/src/components/utils/displayValue.js +94 -0
- package/src/components/utils/useDensity.js +345 -9
- package/src/components/vault/OtpChip.jsx +208 -0
- package/src/components/vault/PasswordGenerator.jsx +145 -0
- package/src/components/vault/QrScanner.jsx +326 -0
- package/src/components/vault/VaultAccessLog.jsx +225 -0
- package/src/components/vault/VaultConfirmPanel.jsx +113 -0
- package/src/components/vault/VaultEntryForm.jsx +740 -0
- package/src/components/vault/VaultManager.jsx +1000 -0
- package/src/components/vault/VaultQuickSearch.jsx +681 -0
- package/src/components/vault/qrDecode.js +257 -0
- package/src/components/vault/useDebouncedValue.js +22 -0
- package/src/components/vault/useVaultReveal.js +160 -0
- package/src/components/vault/vaultClipboard.js +33 -0
- package/src/components/vault/vaultEndpoints.js +40 -0
- package/src/components/vault/vaultFit.js +116 -0
- package/src/components/vault/vaultHelpers.js +547 -0
- package/src/components/vault/vaultNavigation.jsx +58 -0
- package/src/components/vault/vaultOtp.js +105 -0
- package/src/index.js +233 -1
|
@@ -0,0 +1,2382 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Messaging (components/sms/*)
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
// The SMS inbox: a popover in the navigation's action pill (SmsInboxBadge),
|
|
5
|
+
// the two-pane page behind it (SmsInbox), the conversation pane on its own
|
|
6
|
+
// (SmsThreadPanel), the compose modal, and the settings screen.
|
|
7
|
+
//
|
|
8
|
+
// Every brand value is a CSS custom property with a neutral fallback, exactly
|
|
9
|
+
// as in Vault.module.scss, so the library carries no palette of its own.
|
|
10
|
+
//
|
|
11
|
+
// IMPORTANT — why every control below restates its colour, background,
|
|
12
|
+
// border, font-size AND box size:
|
|
13
|
+
//
|
|
14
|
+
// The popover mounts INSIDE `.hactions > ul`, whose chip rules reach every
|
|
15
|
+
// descendant `a` and `button` and hand it white text, a fixed 36px box and a
|
|
16
|
+
// pill radius. On a white panel that renders as blank boxes. Navigation.module
|
|
17
|
+
// .scss excludes `[data-nav-overlay] *` from those rules, which is the real
|
|
18
|
+
// fix, but a component that survives only because its host is well behaved is
|
|
19
|
+
// a component that breaks the next time it is mounted somewhere new. So
|
|
20
|
+
// nothing here inherits anything it actually needs, and no `!important` was
|
|
21
|
+
// required to do it.
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
|
|
24
|
+
$sms-mono: 'SFMono-Regular', 'Menlo', 'Consolas', monospace;
|
|
25
|
+
|
|
26
|
+
@mixin sms-tokens {
|
|
27
|
+
--s-primary: var(--primary-color, #00386c);
|
|
28
|
+
--s-secondary: var(--secondary-color, #404040);
|
|
29
|
+
--s-paragraph: var(--paragraph-color, #2b2b2b);
|
|
30
|
+
--s-background: var(--background-color, #fff);
|
|
31
|
+
--s-br: var(--br, 6px);
|
|
32
|
+
--s-btn-br: var(--btn-br, 6px);
|
|
33
|
+
|
|
34
|
+
--s-line: color-mix(in srgb, var(--s-paragraph) 18%, transparent);
|
|
35
|
+
// 76% of the body colour over white lands around #5b5b5b — roughly 6:1,
|
|
36
|
+
// comfortably past 4.5:1 on the small type this file is full of.
|
|
37
|
+
--s-muted: color-mix(in srgb, var(--s-paragraph) 76%, transparent);
|
|
38
|
+
--s-surface: color-mix(in srgb, var(--s-paragraph) 4%, var(--s-background));
|
|
39
|
+
--s-hover: color-mix(in srgb, var(--s-primary) 9%, var(--s-background));
|
|
40
|
+
--s-tint: color-mix(in srgb, var(--s-primary) 12%, var(--s-background));
|
|
41
|
+
--s-ok: rgb(31, 122, 71);
|
|
42
|
+
--s-warn: rgb(163, 106, 20);
|
|
43
|
+
--s-bad: rgb(190, 62, 62);
|
|
44
|
+
// A selected row. Deliberately fainter than `--s-tint`: on a list where
|
|
45
|
+
// one row is always selected, a strong fill reads as an alert.
|
|
46
|
+
--s-select: color-mix(in srgb, var(--s-primary) 8%, var(--s-background));
|
|
47
|
+
// One height for every control that sits beside another control.
|
|
48
|
+
--s-control: 34px;
|
|
49
|
+
|
|
50
|
+
// The two bubble skins. Incoming is a neutral card; outgoing is the brand
|
|
51
|
+
// colour, which is the only place in the module white text is correct.
|
|
52
|
+
--s-in-bg: color-mix(in srgb, var(--s-paragraph) 7%, var(--s-background));
|
|
53
|
+
--s-in-fg: var(--s-paragraph);
|
|
54
|
+
--s-out-bg: var(--s-primary);
|
|
55
|
+
--s-out-fg: #fff;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Undo anything a host may have imposed on a generic element. Applied to every
|
|
59
|
+
// interactive element in the module — see the note at the top of the file.
|
|
60
|
+
@mixin sms-reset {
|
|
61
|
+
box-sizing: border-box;
|
|
62
|
+
width: auto;
|
|
63
|
+
height: auto;
|
|
64
|
+
min-width: 0;
|
|
65
|
+
min-height: 0;
|
|
66
|
+
max-width: none;
|
|
67
|
+
margin: 0;
|
|
68
|
+
font-family: inherit;
|
|
69
|
+
font-weight: 400;
|
|
70
|
+
line-height: 1.3;
|
|
71
|
+
letter-spacing: normal;
|
|
72
|
+
text-transform: none;
|
|
73
|
+
text-decoration: none;
|
|
74
|
+
text-align: left;
|
|
75
|
+
appearance: none;
|
|
76
|
+
outline: none;
|
|
77
|
+
transform: none;
|
|
78
|
+
box-shadow: none;
|
|
79
|
+
// The host's chip rules transition `all`, which makes a control visibly
|
|
80
|
+
// resize on hover once its box is no longer fixed.
|
|
81
|
+
transition: background-color 0.15s ease, color 0.15s ease,
|
|
82
|
+
border-color 0.15s ease;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
@mixin sms-focus {
|
|
86
|
+
&:focus-visible {
|
|
87
|
+
outline: 2px solid var(--s-primary);
|
|
88
|
+
outline-offset: 2px;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// A checkbox or a radio, and nothing else.
|
|
93
|
+
//
|
|
94
|
+
// The host CRM styles bare `input` — padding, a min-width, a text-field
|
|
95
|
+
// height, a pill radius — none of it typed, so it lands on `input[type=radio]`
|
|
96
|
+
// too and renders a choice as a pair of wide ovals with a dot floating in
|
|
97
|
+
// them. `sms-reset` cannot help: it deliberately unsets `appearance`, which
|
|
98
|
+
// for these two is what draws the control at all. So this is the inverse of
|
|
99
|
+
// the reset — the native box restated, and the native painting asked for back.
|
|
100
|
+
@mixin sms-check {
|
|
101
|
+
@include sms-focus;
|
|
102
|
+
|
|
103
|
+
flex: 0 0 auto;
|
|
104
|
+
box-sizing: border-box;
|
|
105
|
+
width: 16px;
|
|
106
|
+
height: 16px;
|
|
107
|
+
min-width: 16px;
|
|
108
|
+
min-height: 16px;
|
|
109
|
+
max-width: 16px;
|
|
110
|
+
padding: 0;
|
|
111
|
+
margin: 0;
|
|
112
|
+
border: 0;
|
|
113
|
+
border-radius: 0;
|
|
114
|
+
background: none;
|
|
115
|
+
box-shadow: none;
|
|
116
|
+
appearance: auto;
|
|
117
|
+
-webkit-appearance: auto;
|
|
118
|
+
accent-color: var(--s-primary);
|
|
119
|
+
cursor: pointer;
|
|
120
|
+
vertical-align: middle;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
@keyframes smsSpin {
|
|
124
|
+
to {
|
|
125
|
+
transform: rotate(360deg);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
@keyframes smsPopIn {
|
|
130
|
+
from {
|
|
131
|
+
opacity: 0;
|
|
132
|
+
transform: translateY(-4px);
|
|
133
|
+
}
|
|
134
|
+
to {
|
|
135
|
+
opacity: 1;
|
|
136
|
+
transform: translateY(0);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.spin {
|
|
141
|
+
animation: smsSpin 1s linear infinite;
|
|
142
|
+
flex-shrink: 0;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.muted {
|
|
146
|
+
color: var(--s-muted);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.visuallyHidden {
|
|
150
|
+
position: absolute;
|
|
151
|
+
width: 1px;
|
|
152
|
+
height: 1px;
|
|
153
|
+
padding: 0;
|
|
154
|
+
margin: -1px;
|
|
155
|
+
overflow: hidden;
|
|
156
|
+
clip: rect(0, 0, 0, 0);
|
|
157
|
+
white-space: nowrap;
|
|
158
|
+
border: 0;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// ---------------------------------------------------------------------------
|
|
162
|
+
// Shared controls
|
|
163
|
+
// ---------------------------------------------------------------------------
|
|
164
|
+
|
|
165
|
+
.primaryButton {
|
|
166
|
+
@include sms-reset;
|
|
167
|
+
@include sms-focus;
|
|
168
|
+
|
|
169
|
+
display: inline-flex;
|
|
170
|
+
flex-shrink: 0;
|
|
171
|
+
gap: 0.4rem;
|
|
172
|
+
align-items: center;
|
|
173
|
+
justify-content: center;
|
|
174
|
+
padding: 0.5rem 0.85rem;
|
|
175
|
+
border: 1px solid var(--s-primary);
|
|
176
|
+
border-radius: var(--s-btn-br);
|
|
177
|
+
background: var(--s-primary);
|
|
178
|
+
color: #fff;
|
|
179
|
+
font-size: 0.82rem;
|
|
180
|
+
font-weight: 600;
|
|
181
|
+
white-space: nowrap;
|
|
182
|
+
cursor: pointer;
|
|
183
|
+
|
|
184
|
+
svg {
|
|
185
|
+
flex-shrink: 0;
|
|
186
|
+
color: currentColor;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
&:hover:not(:disabled) {
|
|
190
|
+
border-color: color-mix(in srgb, var(--s-primary) 85%, #000);
|
|
191
|
+
background: color-mix(in srgb, var(--s-primary) 85%, #000);
|
|
192
|
+
color: #fff;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
&:disabled {
|
|
196
|
+
opacity: 0.55;
|
|
197
|
+
cursor: default;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
.ghostButton {
|
|
202
|
+
@include sms-reset;
|
|
203
|
+
@include sms-focus;
|
|
204
|
+
|
|
205
|
+
display: inline-flex;
|
|
206
|
+
flex-shrink: 0;
|
|
207
|
+
gap: 0.4rem;
|
|
208
|
+
align-items: center;
|
|
209
|
+
justify-content: center;
|
|
210
|
+
padding: 0.45rem 0.75rem;
|
|
211
|
+
border: 1px solid var(--s-line);
|
|
212
|
+
border-radius: var(--s-btn-br);
|
|
213
|
+
background: var(--s-background);
|
|
214
|
+
color: var(--s-paragraph);
|
|
215
|
+
font-size: 0.8rem;
|
|
216
|
+
font-weight: 500;
|
|
217
|
+
white-space: nowrap;
|
|
218
|
+
cursor: pointer;
|
|
219
|
+
|
|
220
|
+
svg {
|
|
221
|
+
flex-shrink: 0;
|
|
222
|
+
color: currentColor;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
&:hover:not(:disabled) {
|
|
226
|
+
border-color: var(--s-primary);
|
|
227
|
+
background: var(--s-hover);
|
|
228
|
+
color: var(--s-primary);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
&:disabled {
|
|
232
|
+
opacity: 0.5;
|
|
233
|
+
cursor: default;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
.iconButton {
|
|
238
|
+
@include sms-reset;
|
|
239
|
+
@include sms-focus;
|
|
240
|
+
|
|
241
|
+
display: inline-flex;
|
|
242
|
+
flex: 0 0 auto;
|
|
243
|
+
align-items: center;
|
|
244
|
+
justify-content: center;
|
|
245
|
+
width: 30px;
|
|
246
|
+
height: 30px;
|
|
247
|
+
min-width: 30px;
|
|
248
|
+
padding: 0;
|
|
249
|
+
border: 1px solid var(--s-line);
|
|
250
|
+
border-radius: var(--s-btn-br);
|
|
251
|
+
background: var(--s-background);
|
|
252
|
+
color: var(--s-secondary);
|
|
253
|
+
cursor: pointer;
|
|
254
|
+
|
|
255
|
+
svg {
|
|
256
|
+
flex-shrink: 0;
|
|
257
|
+
color: currentColor;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
&:hover:not(:disabled) {
|
|
261
|
+
border-color: var(--s-primary);
|
|
262
|
+
background: var(--s-tint);
|
|
263
|
+
color: var(--s-primary);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
&:disabled {
|
|
267
|
+
opacity: 0.55;
|
|
268
|
+
cursor: default;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
.linkButton {
|
|
273
|
+
@include sms-reset;
|
|
274
|
+
@include sms-focus;
|
|
275
|
+
|
|
276
|
+
display: inline-flex;
|
|
277
|
+
gap: 0.3rem;
|
|
278
|
+
align-items: center;
|
|
279
|
+
padding: 0;
|
|
280
|
+
border: none;
|
|
281
|
+
background: none;
|
|
282
|
+
color: var(--s-primary);
|
|
283
|
+
font-size: 0.8rem;
|
|
284
|
+
font-weight: 600;
|
|
285
|
+
cursor: pointer;
|
|
286
|
+
|
|
287
|
+
svg {
|
|
288
|
+
flex-shrink: 0;
|
|
289
|
+
color: currentColor;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
&:hover:not(:disabled) {
|
|
293
|
+
color: var(--s-primary);
|
|
294
|
+
text-decoration: underline;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
&:disabled {
|
|
298
|
+
opacity: 0.5;
|
|
299
|
+
cursor: default;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
.dangerLink {
|
|
304
|
+
@extend .linkButton;
|
|
305
|
+
|
|
306
|
+
color: var(--s-bad);
|
|
307
|
+
|
|
308
|
+
&:hover:not(:disabled) {
|
|
309
|
+
color: var(--s-bad);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
.input,
|
|
314
|
+
.textarea,
|
|
315
|
+
.select {
|
|
316
|
+
@include sms-reset;
|
|
317
|
+
@include sms-focus;
|
|
318
|
+
|
|
319
|
+
width: 100%;
|
|
320
|
+
padding: 0.5rem 0.6rem;
|
|
321
|
+
border: 1px solid var(--s-line);
|
|
322
|
+
border-radius: var(--s-btn-br);
|
|
323
|
+
background: var(--s-background);
|
|
324
|
+
color: var(--s-paragraph);
|
|
325
|
+
font-size: 0.87rem;
|
|
326
|
+
|
|
327
|
+
&::placeholder {
|
|
328
|
+
color: var(--s-muted);
|
|
329
|
+
opacity: 1;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
&:focus {
|
|
333
|
+
border-color: var(--s-primary);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
&:disabled {
|
|
337
|
+
background: var(--s-surface);
|
|
338
|
+
opacity: 0.7;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
.textarea {
|
|
343
|
+
line-height: 1.5;
|
|
344
|
+
resize: vertical;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
.select {
|
|
348
|
+
height: var(--s-control);
|
|
349
|
+
min-height: var(--s-control);
|
|
350
|
+
padding: 0 1.8rem 0 0.6rem;
|
|
351
|
+
// The native arrow goes with `appearance: none` in the reset, so it is
|
|
352
|
+
// drawn back on rather than left missing.
|
|
353
|
+
background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
|
|
354
|
+
linear-gradient(135deg, currentColor 50%, transparent 50%);
|
|
355
|
+
background-position: calc(100% - 15px) 52%, calc(100% - 10px) 52%;
|
|
356
|
+
background-size: 5px 5px, 5px 5px;
|
|
357
|
+
background-repeat: no-repeat;
|
|
358
|
+
cursor: pointer;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
.field {
|
|
362
|
+
display: flex;
|
|
363
|
+
flex-direction: column;
|
|
364
|
+
gap: 0.3rem;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
.label {
|
|
368
|
+
color: var(--s-muted);
|
|
369
|
+
font-size: 0.74rem;
|
|
370
|
+
font-weight: 700;
|
|
371
|
+
letter-spacing: 0.04em;
|
|
372
|
+
text-transform: uppercase;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
.help {
|
|
376
|
+
margin: 0;
|
|
377
|
+
color: var(--s-muted);
|
|
378
|
+
font-size: 0.75rem;
|
|
379
|
+
line-height: 1.5;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
.fieldError {
|
|
383
|
+
display: flex;
|
|
384
|
+
gap: 0.3rem;
|
|
385
|
+
align-items: center;
|
|
386
|
+
margin: 0;
|
|
387
|
+
color: var(--s-bad);
|
|
388
|
+
font-size: 0.76rem;
|
|
389
|
+
font-weight: 500;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
.checkRow {
|
|
393
|
+
display: flex;
|
|
394
|
+
flex-wrap: wrap;
|
|
395
|
+
gap: 1rem;
|
|
396
|
+
|
|
397
|
+
label {
|
|
398
|
+
display: inline-flex;
|
|
399
|
+
gap: 0.5rem;
|
|
400
|
+
align-items: center;
|
|
401
|
+
color: var(--s-paragraph);
|
|
402
|
+
font-size: 0.84rem;
|
|
403
|
+
line-height: 1.2;
|
|
404
|
+
cursor: pointer;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
input[type='radio'],
|
|
408
|
+
input[type='checkbox'] {
|
|
409
|
+
@include sms-check;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// Square for a checkbox, round for a radio — restated because the reset
|
|
413
|
+
// above zeroes the radius, and a radio with a 6px radius is an oval.
|
|
414
|
+
input[type='radio'] {
|
|
415
|
+
border-radius: 50%;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
.inlineCheck {
|
|
420
|
+
display: inline-flex;
|
|
421
|
+
gap: 0.45rem;
|
|
422
|
+
align-items: center;
|
|
423
|
+
color: var(--s-paragraph);
|
|
424
|
+
font-size: 0.82rem;
|
|
425
|
+
line-height: 1.2;
|
|
426
|
+
cursor: pointer;
|
|
427
|
+
|
|
428
|
+
input[type='checkbox'],
|
|
429
|
+
input[type='radio'] {
|
|
430
|
+
@include sms-check;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
input[type='radio'] {
|
|
434
|
+
border-radius: 50%;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// ONE field with the icon inside it — not a bordered strip wrapped around a
|
|
439
|
+
// bordered input. The input below carries no border of its own, and the height
|
|
440
|
+
// is the same `--s-control` every select and button beside it uses, so a row
|
|
441
|
+
// of controls lines up on both edges.
|
|
442
|
+
.searchBar {
|
|
443
|
+
@include sms-reset;
|
|
444
|
+
|
|
445
|
+
display: flex;
|
|
446
|
+
gap: 0.45rem;
|
|
447
|
+
align-items: center;
|
|
448
|
+
box-sizing: border-box;
|
|
449
|
+
width: 100%;
|
|
450
|
+
height: var(--s-control);
|
|
451
|
+
min-height: var(--s-control);
|
|
452
|
+
padding: 0 0.55rem;
|
|
453
|
+
border: 1px solid var(--s-line);
|
|
454
|
+
border-radius: var(--s-btn-br);
|
|
455
|
+
background: var(--s-background);
|
|
456
|
+
color: var(--s-muted);
|
|
457
|
+
|
|
458
|
+
&:focus-within {
|
|
459
|
+
border-color: var(--s-primary);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
svg {
|
|
463
|
+
flex-shrink: 0;
|
|
464
|
+
color: var(--s-muted);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
.searchInput {
|
|
469
|
+
@include sms-reset;
|
|
470
|
+
|
|
471
|
+
flex: 1;
|
|
472
|
+
// The host styles bare `input` with a border, a padding and a height of
|
|
473
|
+
// its own; every one of those is restated away here, which is what keeps
|
|
474
|
+
// this a field rather than a field inside a field.
|
|
475
|
+
height: 100%;
|
|
476
|
+
min-width: 0;
|
|
477
|
+
padding: 0;
|
|
478
|
+
border: none;
|
|
479
|
+
border-radius: 0;
|
|
480
|
+
background: transparent;
|
|
481
|
+
color: var(--s-paragraph);
|
|
482
|
+
font-size: 0.86rem;
|
|
483
|
+
|
|
484
|
+
&::placeholder {
|
|
485
|
+
color: var(--s-muted);
|
|
486
|
+
opacity: 1;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
.pill {
|
|
491
|
+
display: inline-flex;
|
|
492
|
+
gap: 0.25rem;
|
|
493
|
+
align-items: center;
|
|
494
|
+
padding: 0.12rem 0.5rem;
|
|
495
|
+
border-radius: 999px;
|
|
496
|
+
font-size: 0.72rem;
|
|
497
|
+
font-weight: 700;
|
|
498
|
+
white-space: nowrap;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
.pillOk {
|
|
502
|
+
background: color-mix(in srgb, var(--s-ok) 15%, var(--s-background));
|
|
503
|
+
color: var(--s-ok);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
.pillWarn {
|
|
507
|
+
background: color-mix(in srgb, var(--s-warn) 16%, var(--s-background));
|
|
508
|
+
color: var(--s-warn);
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
.pillBad {
|
|
512
|
+
background: color-mix(in srgb, var(--s-bad) 15%, var(--s-background));
|
|
513
|
+
color: var(--s-bad);
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
.pillMuted {
|
|
517
|
+
background: color-mix(in srgb, var(--s-paragraph) 12%, var(--s-background));
|
|
518
|
+
color: var(--s-muted);
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// A person, or a number, as at most two letters in a circle.
|
|
522
|
+
//
|
|
523
|
+
// One fixed size wherever it appears: the avatar is the left edge of every
|
|
524
|
+
// list in this module, and a circle that resizes with its contents is what
|
|
525
|
+
// makes a column of rows zig-zag.
|
|
526
|
+
.avatar {
|
|
527
|
+
display: inline-flex;
|
|
528
|
+
flex: 0 0 auto;
|
|
529
|
+
align-items: center;
|
|
530
|
+
justify-content: center;
|
|
531
|
+
box-sizing: border-box;
|
|
532
|
+
width: 32px;
|
|
533
|
+
height: 32px;
|
|
534
|
+
min-width: 32px;
|
|
535
|
+
max-width: 32px;
|
|
536
|
+
border-radius: 50%;
|
|
537
|
+
background: var(--s-tint);
|
|
538
|
+
color: var(--s-primary);
|
|
539
|
+
font-size: 0.72rem;
|
|
540
|
+
font-weight: 700;
|
|
541
|
+
letter-spacing: 0.02em;
|
|
542
|
+
// Two digits and two letters must occupy the same width, or a list of
|
|
543
|
+
// mixed rows looks ragged.
|
|
544
|
+
font-variant-numeric: tabular-nums;
|
|
545
|
+
text-transform: uppercase;
|
|
546
|
+
// A helper that ever returned three characters must not widen the circle:
|
|
547
|
+
// the layout is the contract, not the string.
|
|
548
|
+
overflow: hidden;
|
|
549
|
+
white-space: nowrap;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
.avatarLg {
|
|
553
|
+
width: 36px;
|
|
554
|
+
height: 36px;
|
|
555
|
+
font-size: 0.8rem;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
// ---------------------------------------------------------------------------
|
|
559
|
+
// Status notes and banners
|
|
560
|
+
// ---------------------------------------------------------------------------
|
|
561
|
+
|
|
562
|
+
// One line, and it stays one line.
|
|
563
|
+
//
|
|
564
|
+
// This was a two-line block with a heading and a paragraph, and on a page
|
|
565
|
+
// whose whole job is a list of conversations it dominated the screen — a
|
|
566
|
+
// standing notice about a connection nobody can do anything about from here.
|
|
567
|
+
// Icon, the fact, a short note that ellipses. Nothing wraps.
|
|
568
|
+
.banner {
|
|
569
|
+
display: flex;
|
|
570
|
+
gap: 0.5rem;
|
|
571
|
+
align-items: center;
|
|
572
|
+
box-sizing: border-box;
|
|
573
|
+
width: 100%;
|
|
574
|
+
padding: 0.4rem 0.7rem;
|
|
575
|
+
border: 1px solid var(--s-line);
|
|
576
|
+
border-radius: var(--s-br);
|
|
577
|
+
background: var(--s-surface);
|
|
578
|
+
color: var(--s-paragraph);
|
|
579
|
+
font-size: 0.78rem;
|
|
580
|
+
line-height: 1.4;
|
|
581
|
+
|
|
582
|
+
svg {
|
|
583
|
+
flex-shrink: 0;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
strong {
|
|
587
|
+
flex-shrink: 0;
|
|
588
|
+
color: inherit;
|
|
589
|
+
font-weight: 700;
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
.bannerNote {
|
|
594
|
+
min-width: 0;
|
|
595
|
+
overflow: hidden;
|
|
596
|
+
color: color-mix(in srgb, currentColor 72%, transparent);
|
|
597
|
+
text-overflow: ellipsis;
|
|
598
|
+
white-space: nowrap;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
.bannerWarn {
|
|
602
|
+
border-color: color-mix(in srgb, var(--s-warn) 45%, transparent);
|
|
603
|
+
background: color-mix(in srgb, var(--s-warn) 10%, var(--s-background));
|
|
604
|
+
color: color-mix(in srgb, var(--s-warn) 80%, var(--s-paragraph));
|
|
605
|
+
|
|
606
|
+
svg {
|
|
607
|
+
color: var(--s-warn);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
.bannerBad {
|
|
612
|
+
border-color: color-mix(in srgb, var(--s-bad) 45%, transparent);
|
|
613
|
+
background: color-mix(in srgb, var(--s-bad) 9%, var(--s-background));
|
|
614
|
+
color: color-mix(in srgb, var(--s-bad) 80%, var(--s-paragraph));
|
|
615
|
+
|
|
616
|
+
svg {
|
|
617
|
+
color: var(--s-bad);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
.bannerOk {
|
|
622
|
+
border-color: color-mix(in srgb, var(--s-ok) 40%, transparent);
|
|
623
|
+
background: color-mix(in srgb, var(--s-ok) 9%, var(--s-background));
|
|
624
|
+
color: color-mix(in srgb, var(--s-ok) 80%, var(--s-paragraph));
|
|
625
|
+
|
|
626
|
+
svg {
|
|
627
|
+
color: var(--s-ok);
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
// The one-line version, for the popover.
|
|
632
|
+
.statusNote {
|
|
633
|
+
display: flex;
|
|
634
|
+
gap: 0.4rem;
|
|
635
|
+
align-items: center;
|
|
636
|
+
padding: 0.45rem 0.85rem;
|
|
637
|
+
border-top: 1px solid var(--s-line);
|
|
638
|
+
background: color-mix(in srgb, var(--s-warn) 8%, var(--s-background));
|
|
639
|
+
color: color-mix(in srgb, var(--s-warn) 85%, var(--s-paragraph));
|
|
640
|
+
font-size: 0.75rem;
|
|
641
|
+
line-height: 1.4;
|
|
642
|
+
|
|
643
|
+
svg {
|
|
644
|
+
flex-shrink: 0;
|
|
645
|
+
color: var(--s-warn);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
// ---------------------------------------------------------------------------
|
|
650
|
+
// SmsInboxBadge — the header trigger and its popover
|
|
651
|
+
// ---------------------------------------------------------------------------
|
|
652
|
+
|
|
653
|
+
.quickSms {
|
|
654
|
+
@include sms-tokens;
|
|
655
|
+
|
|
656
|
+
position: relative;
|
|
657
|
+
align-items: center;
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
// Deliberately the same shape as Notification's and the vault's triggers:
|
|
661
|
+
// these sit side by side in the action pill and must read as one row of chips,
|
|
662
|
+
// not three designs. This one DOES inherit the header's colour, which is
|
|
663
|
+
// correct — it is a chip.
|
|
664
|
+
.trigger {
|
|
665
|
+
@include sms-focus;
|
|
666
|
+
|
|
667
|
+
position: relative;
|
|
668
|
+
display: flex;
|
|
669
|
+
align-items: center;
|
|
670
|
+
justify-content: center;
|
|
671
|
+
width: 40px;
|
|
672
|
+
height: 40px;
|
|
673
|
+
padding: 8px;
|
|
674
|
+
border: none;
|
|
675
|
+
border-radius: 50%;
|
|
676
|
+
background: transparent;
|
|
677
|
+
color: var(--paragraph-color, #333);
|
|
678
|
+
cursor: pointer;
|
|
679
|
+
transition: all 0.2s ease;
|
|
680
|
+
|
|
681
|
+
&:hover {
|
|
682
|
+
background-color: rgba(var(--primary-color-rgb, 0, 86, 179), 0.1);
|
|
683
|
+
color: var(--primary-color, #0056b3);
|
|
684
|
+
transform: scale(1.05);
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
&:active {
|
|
688
|
+
transform: scale(0.95);
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
// Matches Notification's badge, down to the offsets: two unread counts side by
|
|
693
|
+
// side in the header have to sit on the same line as each other.
|
|
694
|
+
.badge {
|
|
695
|
+
position: absolute;
|
|
696
|
+
top: -2px;
|
|
697
|
+
right: -2px;
|
|
698
|
+
display: flex;
|
|
699
|
+
align-items: center;
|
|
700
|
+
justify-content: center;
|
|
701
|
+
box-sizing: border-box;
|
|
702
|
+
min-width: 16px;
|
|
703
|
+
height: 16px;
|
|
704
|
+
padding: 0 4px;
|
|
705
|
+
border-radius: 8px;
|
|
706
|
+
background-color: var(--secondary-color, #c1272d);
|
|
707
|
+
color: #fff;
|
|
708
|
+
font-size: 10px;
|
|
709
|
+
font-weight: 600;
|
|
710
|
+
font-variant-numeric: tabular-nums;
|
|
711
|
+
line-height: 1;
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
.popoverWrapper {
|
|
715
|
+
position: absolute;
|
|
716
|
+
top: 100%;
|
|
717
|
+
right: 0;
|
|
718
|
+
z-index: 1100;
|
|
719
|
+
margin-top: 10px;
|
|
720
|
+
// The host's chip `li` is a centring flex container. The popover is
|
|
721
|
+
// absolutely positioned so it escapes the flexbox, but `text-align` is
|
|
722
|
+
// inherited and would centre every line of prose inside it.
|
|
723
|
+
text-align: left;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
.popover {
|
|
727
|
+
@include sms-tokens;
|
|
728
|
+
|
|
729
|
+
display: flex;
|
|
730
|
+
flex-direction: column;
|
|
731
|
+
box-sizing: border-box;
|
|
732
|
+
width: 420px;
|
|
733
|
+
max-width: calc(100vw - 2rem);
|
|
734
|
+
border: 1px solid var(--s-line);
|
|
735
|
+
border-radius: 10px;
|
|
736
|
+
// Opaque, and stated: the header behind this is a dark brand bar, and a
|
|
737
|
+
// panel that inherits any of it is unreadable.
|
|
738
|
+
background: var(--s-background);
|
|
739
|
+
color: var(--s-paragraph);
|
|
740
|
+
font-size: 0.85rem;
|
|
741
|
+
text-align: left;
|
|
742
|
+
box-shadow: 0 12px 32px rgba(0, 0, 0, 0.22), 0 2px 6px rgba(0, 0, 0, 0.12);
|
|
743
|
+
overflow: hidden;
|
|
744
|
+
animation: smsPopIn 0.18s ease-out;
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
.popoverHead {
|
|
748
|
+
display: flex;
|
|
749
|
+
gap: 0.75rem;
|
|
750
|
+
align-items: center;
|
|
751
|
+
justify-content: space-between;
|
|
752
|
+
padding: 0.7rem 0.85rem;
|
|
753
|
+
border-bottom: 1px solid var(--s-line);
|
|
754
|
+
background: var(--s-surface);
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
.popoverTitle {
|
|
758
|
+
display: inline-flex;
|
|
759
|
+
gap: 0.4rem;
|
|
760
|
+
align-items: center;
|
|
761
|
+
color: var(--s-paragraph);
|
|
762
|
+
font-size: 0.95rem;
|
|
763
|
+
font-weight: 700;
|
|
764
|
+
|
|
765
|
+
svg {
|
|
766
|
+
color: var(--s-primary);
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
.rows {
|
|
771
|
+
max-height: 360px;
|
|
772
|
+
margin: 0;
|
|
773
|
+
padding: 0;
|
|
774
|
+
list-style: none;
|
|
775
|
+
overflow-y: auto;
|
|
776
|
+
|
|
777
|
+
// Two hosts reach in here with bare element selectors: Navigation's
|
|
778
|
+
// `.hactions > ul li` (now scoped to `> li`, but this file does not get
|
|
779
|
+
// to assume its host has been fixed) and the CRM's own
|
|
780
|
+
// `ul { padding-left: … }` in main.scss. Restated so the list is a list
|
|
781
|
+
// wherever it is mounted.
|
|
782
|
+
> li {
|
|
783
|
+
min-width: 0;
|
|
784
|
+
min-height: 0;
|
|
785
|
+
margin: 0;
|
|
786
|
+
padding: 0;
|
|
787
|
+
position: static;
|
|
788
|
+
list-style: none;
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
// Specificity, deliberately.
|
|
793
|
+
//
|
|
794
|
+
// `.hactions > ul li` was 0,1,2 — two element selectors and a class — and no
|
|
795
|
+
// single class can outrank it. Everything below is the row's BOX, restated at
|
|
796
|
+
// 0,2,0 and 0,3,0 where it wins outright: the grid that a stray `display:
|
|
797
|
+
// flex` was flattening, and the text cell whose `display: block` is what makes
|
|
798
|
+
// the ellipsis engage. Paint stays in `.row` below; only geometry is repeated.
|
|
799
|
+
.rows > .row {
|
|
800
|
+
display: grid;
|
|
801
|
+
grid-template-columns: 3px 32px minmax(0, 1fr) auto;
|
|
802
|
+
column-gap: 0.6rem;
|
|
803
|
+
align-items: center;
|
|
804
|
+
justify-content: normal;
|
|
805
|
+
box-sizing: border-box;
|
|
806
|
+
min-width: 0;
|
|
807
|
+
min-height: 0;
|
|
808
|
+
margin: 0;
|
|
809
|
+
padding: 0.5rem 0.85rem 0.5rem 0;
|
|
810
|
+
position: static;
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
.rows > .row > .rowMain {
|
|
814
|
+
display: block;
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
// One thread, one line: accent, avatar, name + preview, time.
|
|
818
|
+
//
|
|
819
|
+
// FOUR columns, and the first one is the whole point. The unread marker used
|
|
820
|
+
// to be a `border-left`, and a border is layout: an unread row was three
|
|
821
|
+
// pixels narrower than a read one and its avatar sat three pixels further
|
|
822
|
+
// right, so a list of mixed rows zig-zagged down the left edge. The accent is
|
|
823
|
+
// now a fixed 3px track that is simply transparent when there is nothing to
|
|
824
|
+
// say, so every row in the list is measured identically whatever its state.
|
|
825
|
+
//
|
|
826
|
+
// The avatar track is fixed too, for the same reason. Only the text column
|
|
827
|
+
// gives way — it is the only part of the row that can be any length.
|
|
828
|
+
.row {
|
|
829
|
+
display: grid;
|
|
830
|
+
grid-template-columns: 3px 32px minmax(0, 1fr) auto;
|
|
831
|
+
column-gap: 0.6rem;
|
|
832
|
+
align-items: center;
|
|
833
|
+
// No left padding: the accent sits flush against the edge of the panel,
|
|
834
|
+
// and the gap after it lands the avatar on the same 13px the popover
|
|
835
|
+
// header's title starts at.
|
|
836
|
+
padding: 0.5rem 0.85rem 0.5rem 0;
|
|
837
|
+
border: none;
|
|
838
|
+
border-bottom: 1px solid color-mix(in srgb, var(--s-line) 55%, transparent);
|
|
839
|
+
background: var(--s-background);
|
|
840
|
+
cursor: pointer;
|
|
841
|
+
|
|
842
|
+
&:last-child {
|
|
843
|
+
border-bottom: none;
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
// The 3px track. Always present, coloured only when the row is unread.
|
|
848
|
+
.rowAccent {
|
|
849
|
+
align-self: stretch;
|
|
850
|
+
width: 3px;
|
|
851
|
+
border-radius: 0 2px 2px 0;
|
|
852
|
+
background: transparent;
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
.rowUnread .rowAccent {
|
|
856
|
+
background: var(--s-primary);
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
// Highlight is a background and nothing else — anything that touches the box
|
|
860
|
+
// model here is what the four-column grid above exists to prevent.
|
|
861
|
+
.rowActive {
|
|
862
|
+
background: var(--s-hover);
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
.rowMain {
|
|
866
|
+
// `min-width: 0` is the load-bearing declaration: without it a grid item
|
|
867
|
+
// refuses to shrink below its content and the ellipsis never engages.
|
|
868
|
+
min-width: 0;
|
|
869
|
+
// `stretch`, not `start`: the content is `nowrap`, so under `start` the
|
|
870
|
+
// cell would size to its content, overflow its track and slide under the
|
|
871
|
+
// time instead of ellipsing.
|
|
872
|
+
justify-self: stretch;
|
|
873
|
+
overflow: hidden;
|
|
874
|
+
color: var(--s-paragraph);
|
|
875
|
+
font-size: 0.85rem;
|
|
876
|
+
line-height: 1.35;
|
|
877
|
+
text-align: left;
|
|
878
|
+
text-overflow: ellipsis;
|
|
879
|
+
white-space: nowrap;
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
// The dot between the name and the preview.
|
|
883
|
+
//
|
|
884
|
+
// Its own element with its own margins, because a literal " · " in JSX is at
|
|
885
|
+
// the mercy of whitespace trimming — which is how "(Mrs)· Who" reached a
|
|
886
|
+
// screenshot.
|
|
887
|
+
.rowSep {
|
|
888
|
+
margin: 0 0.35rem;
|
|
889
|
+
color: var(--s-muted);
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
.rowName {
|
|
893
|
+
color: var(--s-paragraph);
|
|
894
|
+
font-size: 0.87rem;
|
|
895
|
+
font-weight: 600;
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
.rowPreview {
|
|
899
|
+
color: var(--s-muted);
|
|
900
|
+
font-size: 0.82rem;
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
.rowUnread {
|
|
904
|
+
.rowName {
|
|
905
|
+
font-weight: 800;
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
.rowPreview {
|
|
909
|
+
color: var(--s-paragraph);
|
|
910
|
+
font-weight: 600;
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
.rowTime {
|
|
915
|
+
justify-self: end;
|
|
916
|
+
display: inline-flex;
|
|
917
|
+
gap: 0.35rem;
|
|
918
|
+
align-items: center;
|
|
919
|
+
color: var(--s-muted);
|
|
920
|
+
font-size: 0.74rem;
|
|
921
|
+
font-variant-numeric: tabular-nums;
|
|
922
|
+
white-space: nowrap;
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
.unreadDot {
|
|
926
|
+
display: inline-block;
|
|
927
|
+
flex: 0 0 auto;
|
|
928
|
+
width: 8px;
|
|
929
|
+
height: 8px;
|
|
930
|
+
border-radius: 50%;
|
|
931
|
+
background: var(--s-primary);
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
.empty {
|
|
935
|
+
padding: 1.75rem 1rem;
|
|
936
|
+
text-align: center;
|
|
937
|
+
|
|
938
|
+
p {
|
|
939
|
+
margin: 0 0 0.85rem;
|
|
940
|
+
color: var(--s-muted);
|
|
941
|
+
font-size: 0.85rem;
|
|
942
|
+
line-height: 1.5;
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
.footer {
|
|
947
|
+
display: flex;
|
|
948
|
+
gap: 0.5rem;
|
|
949
|
+
align-items: center;
|
|
950
|
+
justify-content: center;
|
|
951
|
+
padding: 0.55rem 0.85rem;
|
|
952
|
+
border-top: 1px solid var(--s-line);
|
|
953
|
+
background: var(--s-surface);
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
.footerHint {
|
|
957
|
+
color: var(--s-muted);
|
|
958
|
+
font-size: 0.75rem; // 12px at a 16px root
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
// ---------------------------------------------------------------------------
|
|
962
|
+
// SmsInbox — the page
|
|
963
|
+
// ---------------------------------------------------------------------------
|
|
964
|
+
|
|
965
|
+
.inbox {
|
|
966
|
+
@include sms-tokens;
|
|
967
|
+
|
|
968
|
+
display: flex;
|
|
969
|
+
flex-direction: column;
|
|
970
|
+
gap: 0.75rem;
|
|
971
|
+
box-sizing: border-box;
|
|
972
|
+
padding: 1rem 1.25rem 1.25rem;
|
|
973
|
+
color: var(--s-paragraph);
|
|
974
|
+
font-size: 0.85rem;
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
.inboxHead {
|
|
978
|
+
display: flex;
|
|
979
|
+
flex-wrap: wrap;
|
|
980
|
+
gap: 0.75rem 1rem;
|
|
981
|
+
align-items: flex-start;
|
|
982
|
+
justify-content: space-between;
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
.inboxHeading {
|
|
986
|
+
min-width: 0;
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
.pageTitle {
|
|
990
|
+
display: flex;
|
|
991
|
+
gap: 0.5rem;
|
|
992
|
+
align-items: center;
|
|
993
|
+
margin: 0;
|
|
994
|
+
color: var(--s-paragraph);
|
|
995
|
+
font-size: 1.5rem;
|
|
996
|
+
font-weight: 700;
|
|
997
|
+
line-height: 1.2;
|
|
998
|
+
|
|
999
|
+
svg {
|
|
1000
|
+
color: var(--s-primary);
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
.headActions {
|
|
1005
|
+
display: flex;
|
|
1006
|
+
flex-wrap: wrap;
|
|
1007
|
+
gap: 0.5rem;
|
|
1008
|
+
align-items: center;
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
// Two panes, ONE card.
|
|
1012
|
+
//
|
|
1013
|
+
// They were two bordered cards with a gap between them, which reads as two
|
|
1014
|
+
// unrelated tables that happen to sit side by side. A list and the thing the
|
|
1015
|
+
// list opens are one object; the divider between them is a hairline, not a
|
|
1016
|
+
// margin.
|
|
1017
|
+
//
|
|
1018
|
+
// The height is pinned so the panes scroll inside the card rather than the
|
|
1019
|
+
// page scrolling underneath them — a timeline that scrolls the document takes
|
|
1020
|
+
// the composer off the bottom of an iPad with it. `--s-inbox-offset` is what
|
|
1021
|
+
// a host subtracts for its own chrome: this module cannot know how tall the
|
|
1022
|
+
// CRM's header is.
|
|
1023
|
+
.panes {
|
|
1024
|
+
display: grid;
|
|
1025
|
+
grid-template-columns: 340px minmax(0, 1fr);
|
|
1026
|
+
box-sizing: border-box;
|
|
1027
|
+
height: calc(100vh - var(--s-inbox-offset, 230px));
|
|
1028
|
+
min-height: 420px;
|
|
1029
|
+
border: 1px solid var(--s-line);
|
|
1030
|
+
border-radius: var(--s-br);
|
|
1031
|
+
background: var(--s-background);
|
|
1032
|
+
overflow: hidden;
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
.listPane {
|
|
1036
|
+
display: flex;
|
|
1037
|
+
flex-direction: column;
|
|
1038
|
+
box-sizing: border-box;
|
|
1039
|
+
min-height: 0;
|
|
1040
|
+
border: 1px solid var(--s-line);
|
|
1041
|
+
border-radius: var(--s-br);
|
|
1042
|
+
background: var(--s-background);
|
|
1043
|
+
overflow: hidden;
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
// Inside the card the panes lose their own frames and share one divider. Kept
|
|
1047
|
+
// as a descendant rule so a pane mounted on its own — the empty state, an
|
|
1048
|
+
// embedded conversation — still comes with a border of its own.
|
|
1049
|
+
.panes .listPane {
|
|
1050
|
+
border: none;
|
|
1051
|
+
border-right: 1px solid var(--s-line);
|
|
1052
|
+
border-radius: 0;
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
.panes .convPane {
|
|
1056
|
+
border: none;
|
|
1057
|
+
border-radius: 0;
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
.listHead {
|
|
1061
|
+
display: flex;
|
|
1062
|
+
flex-direction: column;
|
|
1063
|
+
gap: 0.45rem;
|
|
1064
|
+
padding: 0.6rem;
|
|
1065
|
+
border-bottom: 1px solid var(--s-line);
|
|
1066
|
+
// Flat. A filled strip wrapped around a bordered field is the "double
|
|
1067
|
+
// frame" from the review: a box inside a box, neither of which is the
|
|
1068
|
+
// control.
|
|
1069
|
+
background: var(--s-background);
|
|
1070
|
+
|
|
1071
|
+
// The select's label wrapper adds nothing here — the option text carries
|
|
1072
|
+
// the meaning.
|
|
1073
|
+
.field {
|
|
1074
|
+
gap: 0;
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
.chips {
|
|
1079
|
+
display: flex;
|
|
1080
|
+
flex-wrap: wrap;
|
|
1081
|
+
gap: 0.35rem;
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
.chip {
|
|
1085
|
+
@include sms-reset;
|
|
1086
|
+
@include sms-focus;
|
|
1087
|
+
|
|
1088
|
+
display: inline-flex;
|
|
1089
|
+
gap: 0.3rem;
|
|
1090
|
+
align-items: center;
|
|
1091
|
+
padding: 0.25rem 0.6rem;
|
|
1092
|
+
border: 1px solid var(--s-line);
|
|
1093
|
+
border-radius: 999px;
|
|
1094
|
+
background: var(--s-background);
|
|
1095
|
+
color: var(--s-muted);
|
|
1096
|
+
font-size: 0.75rem;
|
|
1097
|
+
font-weight: 600;
|
|
1098
|
+
cursor: pointer;
|
|
1099
|
+
|
|
1100
|
+
&:hover:not(:disabled) {
|
|
1101
|
+
border-color: var(--s-primary);
|
|
1102
|
+
color: var(--s-primary);
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
.chipActive {
|
|
1107
|
+
border-color: var(--s-primary);
|
|
1108
|
+
background: var(--s-primary);
|
|
1109
|
+
color: #fff;
|
|
1110
|
+
|
|
1111
|
+
&:hover:not(:disabled) {
|
|
1112
|
+
border-color: color-mix(in srgb, var(--s-primary) 85%, #000);
|
|
1113
|
+
background: color-mix(in srgb, var(--s-primary) 85%, #000);
|
|
1114
|
+
color: #fff;
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
.chipCount {
|
|
1119
|
+
font-variant-numeric: tabular-nums;
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
.threadList {
|
|
1123
|
+
flex: 1;
|
|
1124
|
+
// `min-height: 0` is what lets this scroll instead of growing the flex
|
|
1125
|
+
// column past the bottom of the card.
|
|
1126
|
+
min-height: 0;
|
|
1127
|
+
margin: 0;
|
|
1128
|
+
padding: 0;
|
|
1129
|
+
list-style: none;
|
|
1130
|
+
overflow-y: auto;
|
|
1131
|
+
|
|
1132
|
+
// The same host-proofing as `.rows` — the CRM styles bare `ul` and `li`.
|
|
1133
|
+
> li {
|
|
1134
|
+
min-width: 0;
|
|
1135
|
+
min-height: 0;
|
|
1136
|
+
margin: 0;
|
|
1137
|
+
padding: 0;
|
|
1138
|
+
position: static;
|
|
1139
|
+
list-style: none;
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
// Two lines: name + time, then the preview. Wider than the popover row, so it
|
|
1144
|
+
// can afford the second line the popover cannot.
|
|
1145
|
+
//
|
|
1146
|
+
// Same three-column contract as `.row`, and for the same reason: the accent is
|
|
1147
|
+
// a fixed track rather than a border, so an unread row and a read row are
|
|
1148
|
+
// measured identically and the avatars line up.
|
|
1149
|
+
.threadRow {
|
|
1150
|
+
@include sms-reset;
|
|
1151
|
+
@include sms-focus;
|
|
1152
|
+
|
|
1153
|
+
display: grid;
|
|
1154
|
+
grid-template-columns: 3px 32px minmax(0, 1fr);
|
|
1155
|
+
column-gap: 0.6rem;
|
|
1156
|
+
align-items: center;
|
|
1157
|
+
box-sizing: border-box;
|
|
1158
|
+
width: 100%;
|
|
1159
|
+
padding: 0.55rem 0.7rem 0.55rem 0;
|
|
1160
|
+
border: none;
|
|
1161
|
+
border-bottom: 1px solid color-mix(in srgb, var(--s-line) 55%, transparent);
|
|
1162
|
+
border-radius: 0;
|
|
1163
|
+
background: var(--s-background);
|
|
1164
|
+
color: var(--s-paragraph);
|
|
1165
|
+
font-size: 0.85rem;
|
|
1166
|
+
cursor: pointer;
|
|
1167
|
+
|
|
1168
|
+
&:hover {
|
|
1169
|
+
background: var(--s-hover);
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
.threadAccent {
|
|
1174
|
+
align-self: stretch;
|
|
1175
|
+
width: 3px;
|
|
1176
|
+
border-radius: 0 2px 2px 0;
|
|
1177
|
+
background: transparent;
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
.threadUnread .threadAccent {
|
|
1181
|
+
background: var(--s-primary);
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
// Selection is a background, never a border — and a faint one: exactly one
|
|
1185
|
+
// row in this list is always selected, so a strong fill reads as an alert.
|
|
1186
|
+
.threadRowActive {
|
|
1187
|
+
background: var(--s-select);
|
|
1188
|
+
|
|
1189
|
+
&:hover {
|
|
1190
|
+
background: var(--s-select);
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
.threadAccent {
|
|
1194
|
+
background: var(--s-primary);
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
.threadBody {
|
|
1199
|
+
min-width: 0;
|
|
1200
|
+
overflow: hidden;
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
.threadTop {
|
|
1204
|
+
display: flex;
|
|
1205
|
+
gap: 0.5rem;
|
|
1206
|
+
align-items: baseline;
|
|
1207
|
+
justify-content: space-between;
|
|
1208
|
+
min-width: 0;
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
// Regular by default. Bold is information here — it means unread — so a list
|
|
1212
|
+
// where every name is bold says nothing at all.
|
|
1213
|
+
.threadName {
|
|
1214
|
+
overflow: hidden;
|
|
1215
|
+
color: var(--s-paragraph);
|
|
1216
|
+
font-size: 0.87rem;
|
|
1217
|
+
font-weight: 500;
|
|
1218
|
+
text-overflow: ellipsis;
|
|
1219
|
+
white-space: nowrap;
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
.threadTime {
|
|
1223
|
+
flex: 0 0 auto;
|
|
1224
|
+
color: var(--s-muted);
|
|
1225
|
+
font-size: 0.72rem;
|
|
1226
|
+
font-variant-numeric: tabular-nums;
|
|
1227
|
+
white-space: nowrap;
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
.threadPreview {
|
|
1231
|
+
display: flex;
|
|
1232
|
+
gap: 0.4rem;
|
|
1233
|
+
align-items: center;
|
|
1234
|
+
min-width: 0;
|
|
1235
|
+
margin-top: 0.15rem;
|
|
1236
|
+
color: var(--s-muted);
|
|
1237
|
+
font-size: 0.79rem;
|
|
1238
|
+
|
|
1239
|
+
span {
|
|
1240
|
+
overflow: hidden;
|
|
1241
|
+
text-overflow: ellipsis;
|
|
1242
|
+
white-space: nowrap;
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
.threadUnread {
|
|
1247
|
+
.threadName {
|
|
1248
|
+
font-weight: 700;
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
.threadPreview {
|
|
1252
|
+
color: var(--s-paragraph);
|
|
1253
|
+
font-weight: 600;
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
.threadCount {
|
|
1258
|
+
display: inline-flex;
|
|
1259
|
+
flex: 0 0 auto;
|
|
1260
|
+
align-items: center;
|
|
1261
|
+
justify-content: center;
|
|
1262
|
+
box-sizing: border-box;
|
|
1263
|
+
min-width: 18px;
|
|
1264
|
+
height: 18px;
|
|
1265
|
+
padding: 0 5px;
|
|
1266
|
+
border-radius: 9px;
|
|
1267
|
+
background: var(--s-primary);
|
|
1268
|
+
color: #fff;
|
|
1269
|
+
font-size: 0.66rem;
|
|
1270
|
+
font-weight: 700;
|
|
1271
|
+
font-variant-numeric: tabular-nums;
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
.listFoot {
|
|
1275
|
+
display: flex;
|
|
1276
|
+
justify-content: center;
|
|
1277
|
+
padding: 0.5rem;
|
|
1278
|
+
border-top: 1px solid var(--s-line);
|
|
1279
|
+
background: var(--s-surface);
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1282
|
+
// ---------------------------------------------------------------------------
|
|
1283
|
+
// SmsThreadPanel — the conversation
|
|
1284
|
+
// ---------------------------------------------------------------------------
|
|
1285
|
+
|
|
1286
|
+
.convPane {
|
|
1287
|
+
@include sms-tokens;
|
|
1288
|
+
|
|
1289
|
+
display: flex;
|
|
1290
|
+
flex-direction: column;
|
|
1291
|
+
box-sizing: border-box;
|
|
1292
|
+
min-width: 0;
|
|
1293
|
+
min-height: 0;
|
|
1294
|
+
border: 1px solid var(--s-line);
|
|
1295
|
+
border-radius: var(--s-br);
|
|
1296
|
+
background: var(--s-background);
|
|
1297
|
+
color: var(--s-paragraph);
|
|
1298
|
+
overflow: hidden;
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
.convHead {
|
|
1302
|
+
display: flex;
|
|
1303
|
+
flex-wrap: wrap;
|
|
1304
|
+
gap: 0.5rem;
|
|
1305
|
+
align-items: center;
|
|
1306
|
+
padding: 0.5rem 0.7rem;
|
|
1307
|
+
border-bottom: 1px solid var(--s-line);
|
|
1308
|
+
background: var(--s-surface);
|
|
1309
|
+
|
|
1310
|
+
// Every action in this row is the same height, whatever kind of control
|
|
1311
|
+
// it is. A chip, a worded button and an icon button at three heights is
|
|
1312
|
+
// what made the header look unfinished.
|
|
1313
|
+
.ghostButton,
|
|
1314
|
+
.iconButton,
|
|
1315
|
+
.clientChip {
|
|
1316
|
+
box-sizing: border-box;
|
|
1317
|
+
height: 30px;
|
|
1318
|
+
min-height: 30px;
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
.convIdentity {
|
|
1323
|
+
display: flex;
|
|
1324
|
+
gap: 0.5rem;
|
|
1325
|
+
align-items: center;
|
|
1326
|
+
min-width: 0;
|
|
1327
|
+
// Takes the slack, so the actions sit hard right.
|
|
1328
|
+
margin-right: auto;
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
.convName {
|
|
1332
|
+
display: block;
|
|
1333
|
+
overflow: hidden;
|
|
1334
|
+
color: var(--s-paragraph);
|
|
1335
|
+
font-size: 0.95rem;
|
|
1336
|
+
font-weight: 700;
|
|
1337
|
+
text-overflow: ellipsis;
|
|
1338
|
+
white-space: nowrap;
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1341
|
+
.convNumber {
|
|
1342
|
+
display: block;
|
|
1343
|
+
color: var(--s-muted);
|
|
1344
|
+
font-size: 0.76rem;
|
|
1345
|
+
font-variant-numeric: tabular-nums;
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
.convActions {
|
|
1349
|
+
display: flex;
|
|
1350
|
+
flex-wrap: wrap;
|
|
1351
|
+
gap: 0.35rem;
|
|
1352
|
+
align-items: center;
|
|
1353
|
+
// Hard right, as one group.
|
|
1354
|
+
justify-content: flex-end;
|
|
1355
|
+
margin-left: auto;
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
.clientChip {
|
|
1359
|
+
display: inline-flex;
|
|
1360
|
+
gap: 0.3rem;
|
|
1361
|
+
align-items: center;
|
|
1362
|
+
padding: 0 0.6rem;
|
|
1363
|
+
border: 1px solid color-mix(in srgb, var(--s-primary) 35%, transparent);
|
|
1364
|
+
border-radius: 999px;
|
|
1365
|
+
background: var(--s-tint);
|
|
1366
|
+
color: var(--s-primary);
|
|
1367
|
+
font-size: 0.74rem;
|
|
1368
|
+
font-weight: 700;
|
|
1369
|
+
text-decoration: none;
|
|
1370
|
+
|
|
1371
|
+
&:hover {
|
|
1372
|
+
border-color: var(--s-primary);
|
|
1373
|
+
color: var(--s-primary);
|
|
1374
|
+
text-decoration: underline;
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1377
|
+
svg {
|
|
1378
|
+
flex-shrink: 0;
|
|
1379
|
+
color: currentColor;
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
.timeline {
|
|
1384
|
+
display: flex;
|
|
1385
|
+
flex: 1;
|
|
1386
|
+
flex-direction: column;
|
|
1387
|
+
gap: 0.15rem;
|
|
1388
|
+
box-sizing: border-box;
|
|
1389
|
+
min-height: 0;
|
|
1390
|
+
padding: 0.85rem 0.9rem;
|
|
1391
|
+
background: var(--s-background);
|
|
1392
|
+
overflow-y: auto;
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
.dayHeading {
|
|
1396
|
+
display: flex;
|
|
1397
|
+
gap: 0.6rem;
|
|
1398
|
+
align-items: center;
|
|
1399
|
+
margin: 0.7rem 0 0.5rem;
|
|
1400
|
+
color: var(--s-muted);
|
|
1401
|
+
font-size: 0.72rem;
|
|
1402
|
+
font-weight: 700;
|
|
1403
|
+
letter-spacing: 0.04em;
|
|
1404
|
+
text-transform: uppercase;
|
|
1405
|
+
|
|
1406
|
+
&::before,
|
|
1407
|
+
&::after {
|
|
1408
|
+
flex: 1;
|
|
1409
|
+
height: 1px;
|
|
1410
|
+
background: color-mix(in srgb, var(--s-line) 70%, transparent);
|
|
1411
|
+
content: '';
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1414
|
+
&:first-child {
|
|
1415
|
+
margin-top: 0;
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
// One message. Consecutive messages from the same side sit 4px apart and read
|
|
1420
|
+
// as one utterance; a change of side opens the gap up. The timestamp is drawn
|
|
1421
|
+
// only under the last of a run, because six identical times stacked down the
|
|
1422
|
+
// margin is noise, not information.
|
|
1423
|
+
.bubbleRow {
|
|
1424
|
+
display: flex;
|
|
1425
|
+
flex-direction: column;
|
|
1426
|
+
max-width: 100%;
|
|
1427
|
+
margin-bottom: 4px;
|
|
1428
|
+
}
|
|
1429
|
+
|
|
1430
|
+
.bubbleRunEnd {
|
|
1431
|
+
margin-bottom: 0.7rem;
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1434
|
+
.bubbleIn {
|
|
1435
|
+
align-items: flex-start;
|
|
1436
|
+
}
|
|
1437
|
+
|
|
1438
|
+
.bubbleOut {
|
|
1439
|
+
align-items: flex-end;
|
|
1440
|
+
}
|
|
1441
|
+
|
|
1442
|
+
.bubble {
|
|
1443
|
+
box-sizing: border-box;
|
|
1444
|
+
// 64% — wide enough for a paragraph, narrow enough that the two sides of
|
|
1445
|
+
// the conversation stay visibly two sides.
|
|
1446
|
+
max-width: min(60ch, 64%);
|
|
1447
|
+
padding: 0.5rem 0.7rem;
|
|
1448
|
+
border: 1px solid transparent;
|
|
1449
|
+
border-radius: 12px;
|
|
1450
|
+
font-size: 0.875rem; // 14px at a 16px root
|
|
1451
|
+
line-height: 1.5;
|
|
1452
|
+
// A pasted account number has no spaces in it and must not push the pane
|
|
1453
|
+
// wider than the pane.
|
|
1454
|
+
overflow-wrap: anywhere;
|
|
1455
|
+
white-space: pre-wrap;
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
.bubbleBodyIn {
|
|
1459
|
+
border-color: color-mix(in srgb, var(--s-paragraph) 12%, transparent);
|
|
1460
|
+
border-bottom-left-radius: 4px;
|
|
1461
|
+
background: var(--s-in-bg);
|
|
1462
|
+
color: var(--s-in-fg);
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1465
|
+
.bubbleBodyOut {
|
|
1466
|
+
border-color: var(--s-out-bg);
|
|
1467
|
+
border-bottom-right-radius: 4px;
|
|
1468
|
+
background: var(--s-out-bg);
|
|
1469
|
+
color: var(--s-out-fg);
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1472
|
+
// A message that is queued behind a transport that cannot send it yet. Not an
|
|
1473
|
+
// error skin: dashed and pale, because the message is genuinely saved.
|
|
1474
|
+
.bubbleHeld {
|
|
1475
|
+
border-style: dashed;
|
|
1476
|
+
border-color: color-mix(in srgb, var(--s-warn) 60%, transparent);
|
|
1477
|
+
background: color-mix(in srgb, var(--s-warn) 12%, var(--s-background));
|
|
1478
|
+
color: var(--s-paragraph);
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
.bubbleFailed {
|
|
1482
|
+
border-color: color-mix(in srgb, var(--s-bad) 60%, transparent);
|
|
1483
|
+
background: color-mix(in srgb, var(--s-bad) 10%, var(--s-background));
|
|
1484
|
+
color: var(--s-paragraph);
|
|
1485
|
+
}
|
|
1486
|
+
|
|
1487
|
+
.bubblePending {
|
|
1488
|
+
opacity: 0.72;
|
|
1489
|
+
}
|
|
1490
|
+
|
|
1491
|
+
// Time, status and sender on ONE small muted line, under the bubble and on
|
|
1492
|
+
// the same side as it.
|
|
1493
|
+
.bubbleMeta {
|
|
1494
|
+
display: flex;
|
|
1495
|
+
flex-wrap: wrap;
|
|
1496
|
+
gap: 0.3rem;
|
|
1497
|
+
align-items: center;
|
|
1498
|
+
margin-top: 0.2rem;
|
|
1499
|
+
padding: 0 0.25rem;
|
|
1500
|
+
color: var(--s-muted);
|
|
1501
|
+
font-size: 0.7rem;
|
|
1502
|
+
font-variant-numeric: tabular-nums;
|
|
1503
|
+
|
|
1504
|
+
svg {
|
|
1505
|
+
flex-shrink: 0;
|
|
1506
|
+
}
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1509
|
+
.bubbleOut .bubbleMeta {
|
|
1510
|
+
justify-content: flex-end;
|
|
1511
|
+
text-align: right;
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
.metaFailed {
|
|
1515
|
+
color: var(--s-bad);
|
|
1516
|
+
font-weight: 600;
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1519
|
+
.metaHeld {
|
|
1520
|
+
color: var(--s-warn);
|
|
1521
|
+
font-weight: 600;
|
|
1522
|
+
}
|
|
1523
|
+
|
|
1524
|
+
.retryButton {
|
|
1525
|
+
@include sms-reset;
|
|
1526
|
+
@include sms-focus;
|
|
1527
|
+
|
|
1528
|
+
padding: 0;
|
|
1529
|
+
border: none;
|
|
1530
|
+
background: none;
|
|
1531
|
+
color: var(--s-bad);
|
|
1532
|
+
font-size: 0.7rem;
|
|
1533
|
+
font-weight: 700;
|
|
1534
|
+
text-decoration: underline;
|
|
1535
|
+
cursor: pointer;
|
|
1536
|
+
}
|
|
1537
|
+
|
|
1538
|
+
.attachments {
|
|
1539
|
+
display: flex;
|
|
1540
|
+
flex-wrap: wrap;
|
|
1541
|
+
gap: 0.3rem;
|
|
1542
|
+
margin-top: 0.35rem;
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1545
|
+
.attachment {
|
|
1546
|
+
display: inline-flex;
|
|
1547
|
+
gap: 0.3rem;
|
|
1548
|
+
align-items: center;
|
|
1549
|
+
max-width: 100%;
|
|
1550
|
+
padding: 0.15rem 0.5rem;
|
|
1551
|
+
border: 1px solid color-mix(in srgb, currentColor 40%, transparent);
|
|
1552
|
+
border-radius: 999px;
|
|
1553
|
+
color: inherit;
|
|
1554
|
+
font-size: 0.72rem;
|
|
1555
|
+
font-weight: 600;
|
|
1556
|
+
text-decoration: none;
|
|
1557
|
+
|
|
1558
|
+
&:hover {
|
|
1559
|
+
color: inherit;
|
|
1560
|
+
text-decoration: underline;
|
|
1561
|
+
}
|
|
1562
|
+
|
|
1563
|
+
svg {
|
|
1564
|
+
flex-shrink: 0;
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1567
|
+
span {
|
|
1568
|
+
overflow: hidden;
|
|
1569
|
+
text-overflow: ellipsis;
|
|
1570
|
+
white-space: nowrap;
|
|
1571
|
+
}
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1574
|
+
.timelineTop {
|
|
1575
|
+
display: flex;
|
|
1576
|
+
justify-content: center;
|
|
1577
|
+
padding-bottom: 0.5rem;
|
|
1578
|
+
}
|
|
1579
|
+
|
|
1580
|
+
.timelineEmpty {
|
|
1581
|
+
display: flex;
|
|
1582
|
+
flex: 1;
|
|
1583
|
+
flex-direction: column;
|
|
1584
|
+
gap: 0.4rem;
|
|
1585
|
+
align-items: center;
|
|
1586
|
+
justify-content: center;
|
|
1587
|
+
padding: 2rem 1rem;
|
|
1588
|
+
color: var(--s-muted);
|
|
1589
|
+
font-size: 0.85rem;
|
|
1590
|
+
text-align: center;
|
|
1591
|
+
}
|
|
1592
|
+
|
|
1593
|
+
// ------------------------------------------------------------- the composer
|
|
1594
|
+
|
|
1595
|
+
.composer {
|
|
1596
|
+
box-sizing: border-box;
|
|
1597
|
+
padding: 0.6rem 0.7rem 0.7rem;
|
|
1598
|
+
border-top: 1px solid var(--s-line);
|
|
1599
|
+
background: var(--s-surface);
|
|
1600
|
+
}
|
|
1601
|
+
|
|
1602
|
+
// One card: the text on top, every control in a bar along its bottom edge.
|
|
1603
|
+
// Buttons beside a textarea line up with nothing once the box grows; a bar
|
|
1604
|
+
// inside the same border always reads as part of the message being written.
|
|
1605
|
+
.composerBox {
|
|
1606
|
+
display: flex;
|
|
1607
|
+
flex-direction: column;
|
|
1608
|
+
box-sizing: border-box;
|
|
1609
|
+
border: 1px solid var(--s-line);
|
|
1610
|
+
border-radius: calc(var(--s-btn-br) + 4px);
|
|
1611
|
+
background: var(--s-background);
|
|
1612
|
+
transition: border-color 0.15s ease, box-shadow 0.15s ease;
|
|
1613
|
+
|
|
1614
|
+
&:focus-within {
|
|
1615
|
+
border-color: var(--s-primary);
|
|
1616
|
+
box-shadow: 0 0 0 3px color-mix(in srgb, var(--s-primary) 14%, transparent);
|
|
1617
|
+
}
|
|
1618
|
+
}
|
|
1619
|
+
|
|
1620
|
+
.composerInput {
|
|
1621
|
+
@include sms-reset;
|
|
1622
|
+
|
|
1623
|
+
flex: 1;
|
|
1624
|
+
box-sizing: border-box;
|
|
1625
|
+
min-width: 0;
|
|
1626
|
+
max-height: 180px;
|
|
1627
|
+
padding: 0.7rem 0.85rem 0.4rem;
|
|
1628
|
+
border: 0;
|
|
1629
|
+
border-radius: inherit;
|
|
1630
|
+
background: transparent;
|
|
1631
|
+
color: var(--s-paragraph);
|
|
1632
|
+
font-size: 0.9rem;
|
|
1633
|
+
line-height: 1.5;
|
|
1634
|
+
// Auto-grow is driven from JS; this stops the manual handle fighting it.
|
|
1635
|
+
resize: none;
|
|
1636
|
+
overflow-y: auto;
|
|
1637
|
+
outline: none;
|
|
1638
|
+
|
|
1639
|
+
&::placeholder {
|
|
1640
|
+
color: var(--s-muted);
|
|
1641
|
+
opacity: 1;
|
|
1642
|
+
}
|
|
1643
|
+
|
|
1644
|
+
&:disabled {
|
|
1645
|
+
opacity: 0.7;
|
|
1646
|
+
}
|
|
1647
|
+
}
|
|
1648
|
+
|
|
1649
|
+
.composerBar {
|
|
1650
|
+
display: flex;
|
|
1651
|
+
flex-wrap: wrap;
|
|
1652
|
+
gap: 0.5rem 0.75rem;
|
|
1653
|
+
align-items: center;
|
|
1654
|
+
justify-content: space-between;
|
|
1655
|
+
padding: 0.4rem 0.5rem 0.5rem 0.6rem;
|
|
1656
|
+
}
|
|
1657
|
+
|
|
1658
|
+
.composerTools,
|
|
1659
|
+
.composerActions {
|
|
1660
|
+
display: inline-flex;
|
|
1661
|
+
gap: 0.6rem;
|
|
1662
|
+
align-items: center;
|
|
1663
|
+
min-width: 0;
|
|
1664
|
+
}
|
|
1665
|
+
|
|
1666
|
+
// The templates button sits at the LEFT of the bar, so its menu opens to the
|
|
1667
|
+
// right of it: a right-anchored menu would spill past the pane's left edge
|
|
1668
|
+
// and be clipped by .convPane's overflow.
|
|
1669
|
+
.composerTools .menu {
|
|
1670
|
+
right: auto;
|
|
1671
|
+
left: 0;
|
|
1672
|
+
}
|
|
1673
|
+
|
|
1674
|
+
.composerTools .ghostButton {
|
|
1675
|
+
padding: 0.3rem 0.6rem;
|
|
1676
|
+
border-color: transparent;
|
|
1677
|
+
background: transparent;
|
|
1678
|
+
color: var(--s-secondary);
|
|
1679
|
+
font-weight: 500;
|
|
1680
|
+
|
|
1681
|
+
&:hover:not(:disabled) {
|
|
1682
|
+
border-color: transparent;
|
|
1683
|
+
background: var(--s-hover);
|
|
1684
|
+
color: var(--s-primary);
|
|
1685
|
+
}
|
|
1686
|
+
}
|
|
1687
|
+
|
|
1688
|
+
// Send is the one filled control in the bar, and a pill so it reads as the
|
|
1689
|
+
// action rather than as another field.
|
|
1690
|
+
.sendButton {
|
|
1691
|
+
@include sms-reset;
|
|
1692
|
+
@include sms-focus;
|
|
1693
|
+
|
|
1694
|
+
display: inline-flex;
|
|
1695
|
+
flex-shrink: 0;
|
|
1696
|
+
gap: 0.45rem;
|
|
1697
|
+
align-items: center;
|
|
1698
|
+
justify-content: center;
|
|
1699
|
+
padding: 0.45rem 1rem 0.45rem 0.85rem;
|
|
1700
|
+
border: 1px solid var(--s-primary);
|
|
1701
|
+
border-radius: 999px;
|
|
1702
|
+
background: var(--s-primary);
|
|
1703
|
+
color: #fff;
|
|
1704
|
+
font-size: 0.82rem;
|
|
1705
|
+
font-weight: 600;
|
|
1706
|
+
white-space: nowrap;
|
|
1707
|
+
cursor: pointer;
|
|
1708
|
+
transition: background-color 0.15s ease, transform 0.1s ease, opacity 0.15s ease;
|
|
1709
|
+
|
|
1710
|
+
svg {
|
|
1711
|
+
flex-shrink: 0;
|
|
1712
|
+
color: currentColor;
|
|
1713
|
+
}
|
|
1714
|
+
|
|
1715
|
+
&:hover:not(:disabled) {
|
|
1716
|
+
border-color: color-mix(in srgb, var(--s-primary) 85%, #000);
|
|
1717
|
+
background: color-mix(in srgb, var(--s-primary) 85%, #000);
|
|
1718
|
+
}
|
|
1719
|
+
|
|
1720
|
+
&:active:not(:disabled) {
|
|
1721
|
+
transform: translateY(1px);
|
|
1722
|
+
}
|
|
1723
|
+
|
|
1724
|
+
&:disabled {
|
|
1725
|
+
border-color: transparent;
|
|
1726
|
+
background: color-mix(in srgb, var(--s-paragraph) 18%, transparent);
|
|
1727
|
+
color: var(--s-background);
|
|
1728
|
+
cursor: default;
|
|
1729
|
+
}
|
|
1730
|
+
}
|
|
1731
|
+
|
|
1732
|
+
.counter {
|
|
1733
|
+
display: inline-flex;
|
|
1734
|
+
gap: 0.4rem;
|
|
1735
|
+
align-items: center;
|
|
1736
|
+
color: var(--s-muted);
|
|
1737
|
+
font-size: 0.72rem;
|
|
1738
|
+
// The whole point of a counter is that it does not jitter as it counts.
|
|
1739
|
+
font-variant-numeric: tabular-nums;
|
|
1740
|
+
}
|
|
1741
|
+
|
|
1742
|
+
.counterOver {
|
|
1743
|
+
color: var(--s-bad);
|
|
1744
|
+
font-weight: 700;
|
|
1745
|
+
}
|
|
1746
|
+
|
|
1747
|
+
// A small switch, not a checkbox with a sentence beside it.
|
|
1748
|
+
//
|
|
1749
|
+
// It is a preference about one key, read at a glance and flipped rarely; a
|
|
1750
|
+
// labelled checkbox plus an explanation of what the other state does took
|
|
1751
|
+
// more room than the counter it sits beside.
|
|
1752
|
+
.toggle {
|
|
1753
|
+
@include sms-reset;
|
|
1754
|
+
@include sms-focus;
|
|
1755
|
+
|
|
1756
|
+
display: inline-flex;
|
|
1757
|
+
flex-shrink: 0;
|
|
1758
|
+
gap: 0.4rem;
|
|
1759
|
+
align-items: center;
|
|
1760
|
+
padding: 0.15rem 0.45rem 0.15rem 0.3rem;
|
|
1761
|
+
border: 1px solid var(--s-line);
|
|
1762
|
+
border-radius: 999px;
|
|
1763
|
+
background: var(--s-background);
|
|
1764
|
+
color: var(--s-muted);
|
|
1765
|
+
font-size: 0.7rem;
|
|
1766
|
+
font-weight: 600;
|
|
1767
|
+
white-space: nowrap;
|
|
1768
|
+
cursor: pointer;
|
|
1769
|
+
|
|
1770
|
+
&:hover {
|
|
1771
|
+
border-color: var(--s-primary);
|
|
1772
|
+
color: var(--s-primary);
|
|
1773
|
+
}
|
|
1774
|
+
}
|
|
1775
|
+
|
|
1776
|
+
.toggleTrack {
|
|
1777
|
+
position: relative;
|
|
1778
|
+
display: inline-block;
|
|
1779
|
+
flex: 0 0 auto;
|
|
1780
|
+
width: 22px;
|
|
1781
|
+
height: 12px;
|
|
1782
|
+
border-radius: 999px;
|
|
1783
|
+
background: color-mix(in srgb, var(--s-paragraph) 28%, transparent);
|
|
1784
|
+
transition: background-color 0.15s ease;
|
|
1785
|
+
}
|
|
1786
|
+
|
|
1787
|
+
.toggleKnob {
|
|
1788
|
+
position: absolute;
|
|
1789
|
+
top: 1px;
|
|
1790
|
+
left: 1px;
|
|
1791
|
+
width: 10px;
|
|
1792
|
+
height: 10px;
|
|
1793
|
+
border-radius: 50%;
|
|
1794
|
+
background: #fff;
|
|
1795
|
+
transition: transform 0.15s ease;
|
|
1796
|
+
}
|
|
1797
|
+
|
|
1798
|
+
.toggleOn {
|
|
1799
|
+
border-color: var(--s-primary);
|
|
1800
|
+
color: var(--s-primary);
|
|
1801
|
+
|
|
1802
|
+
.toggleTrack {
|
|
1803
|
+
background: var(--s-primary);
|
|
1804
|
+
}
|
|
1805
|
+
|
|
1806
|
+
.toggleKnob {
|
|
1807
|
+
transform: translateX(10px);
|
|
1808
|
+
}
|
|
1809
|
+
}
|
|
1810
|
+
|
|
1811
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1812
|
+
.toggleTrack,
|
|
1813
|
+
.toggleKnob {
|
|
1814
|
+
transition: none;
|
|
1815
|
+
}
|
|
1816
|
+
}
|
|
1817
|
+
|
|
1818
|
+
// The templates menu, anchored to its button.
|
|
1819
|
+
.menuWrap {
|
|
1820
|
+
position: relative;
|
|
1821
|
+
display: inline-flex;
|
|
1822
|
+
}
|
|
1823
|
+
|
|
1824
|
+
.menu {
|
|
1825
|
+
position: absolute;
|
|
1826
|
+
right: 0;
|
|
1827
|
+
bottom: calc(100% + 6px);
|
|
1828
|
+
z-index: 40;
|
|
1829
|
+
box-sizing: border-box;
|
|
1830
|
+
width: 300px;
|
|
1831
|
+
max-width: calc(100vw - 2rem);
|
|
1832
|
+
max-height: 260px;
|
|
1833
|
+
padding: 0.25rem;
|
|
1834
|
+
border: 1px solid var(--s-line);
|
|
1835
|
+
border-radius: var(--s-br);
|
|
1836
|
+
background: var(--s-background);
|
|
1837
|
+
box-shadow: 0 10px 26px rgba(0, 0, 0, 0.18);
|
|
1838
|
+
overflow-y: auto;
|
|
1839
|
+
animation: smsPopIn 0.14s ease-out;
|
|
1840
|
+
}
|
|
1841
|
+
|
|
1842
|
+
.menuItem {
|
|
1843
|
+
@include sms-reset;
|
|
1844
|
+
@include sms-focus;
|
|
1845
|
+
|
|
1846
|
+
display: block;
|
|
1847
|
+
box-sizing: border-box;
|
|
1848
|
+
width: 100%;
|
|
1849
|
+
padding: 0.4rem 0.5rem;
|
|
1850
|
+
border: none;
|
|
1851
|
+
border-radius: var(--s-btn-br);
|
|
1852
|
+
background: transparent;
|
|
1853
|
+
color: var(--s-paragraph);
|
|
1854
|
+
font-size: 0.82rem;
|
|
1855
|
+
cursor: pointer;
|
|
1856
|
+
|
|
1857
|
+
strong {
|
|
1858
|
+
display: block;
|
|
1859
|
+
color: var(--s-paragraph);
|
|
1860
|
+
font-size: 0.82rem;
|
|
1861
|
+
font-weight: 700;
|
|
1862
|
+
}
|
|
1863
|
+
|
|
1864
|
+
small {
|
|
1865
|
+
display: block;
|
|
1866
|
+
overflow: hidden;
|
|
1867
|
+
color: var(--s-muted);
|
|
1868
|
+
font-size: 0.74rem;
|
|
1869
|
+
text-overflow: ellipsis;
|
|
1870
|
+
white-space: nowrap;
|
|
1871
|
+
}
|
|
1872
|
+
|
|
1873
|
+
&:hover {
|
|
1874
|
+
background: var(--s-hover);
|
|
1875
|
+
}
|
|
1876
|
+
}
|
|
1877
|
+
|
|
1878
|
+
.menuEmpty {
|
|
1879
|
+
padding: 0.6rem 0.5rem;
|
|
1880
|
+
color: var(--s-muted);
|
|
1881
|
+
font-size: 0.78rem;
|
|
1882
|
+
text-align: center;
|
|
1883
|
+
}
|
|
1884
|
+
|
|
1885
|
+
// ---------------------------------------------------------------------------
|
|
1886
|
+
// Compose modal and the client typeahead
|
|
1887
|
+
// ---------------------------------------------------------------------------
|
|
1888
|
+
|
|
1889
|
+
.modalForm {
|
|
1890
|
+
@include sms-tokens;
|
|
1891
|
+
|
|
1892
|
+
display: flex;
|
|
1893
|
+
flex-direction: column;
|
|
1894
|
+
gap: 0.85rem;
|
|
1895
|
+
box-sizing: border-box;
|
|
1896
|
+
padding: 1rem;
|
|
1897
|
+
color: var(--s-paragraph);
|
|
1898
|
+
font-size: 0.85rem;
|
|
1899
|
+
text-align: left;
|
|
1900
|
+
}
|
|
1901
|
+
|
|
1902
|
+
.modalHead {
|
|
1903
|
+
display: flex;
|
|
1904
|
+
gap: 0.6rem;
|
|
1905
|
+
align-items: center;
|
|
1906
|
+
justify-content: space-between;
|
|
1907
|
+
}
|
|
1908
|
+
|
|
1909
|
+
.modalTitle {
|
|
1910
|
+
display: flex;
|
|
1911
|
+
gap: 0.45rem;
|
|
1912
|
+
align-items: center;
|
|
1913
|
+
margin: 0;
|
|
1914
|
+
color: var(--s-paragraph);
|
|
1915
|
+
font-size: 1.05rem;
|
|
1916
|
+
font-weight: 700;
|
|
1917
|
+
|
|
1918
|
+
svg {
|
|
1919
|
+
color: var(--s-primary);
|
|
1920
|
+
}
|
|
1921
|
+
}
|
|
1922
|
+
|
|
1923
|
+
.modalActions {
|
|
1924
|
+
display: flex;
|
|
1925
|
+
gap: 0.6rem;
|
|
1926
|
+
align-items: center;
|
|
1927
|
+
justify-content: flex-end;
|
|
1928
|
+
padding-top: 0.6rem;
|
|
1929
|
+
border-top: 1px solid var(--s-line);
|
|
1930
|
+
}
|
|
1931
|
+
|
|
1932
|
+
.typeahead {
|
|
1933
|
+
position: relative;
|
|
1934
|
+
}
|
|
1935
|
+
|
|
1936
|
+
.typeaheadList {
|
|
1937
|
+
position: absolute;
|
|
1938
|
+
top: calc(100% + 4px);
|
|
1939
|
+
right: 0;
|
|
1940
|
+
left: 0;
|
|
1941
|
+
z-index: 30;
|
|
1942
|
+
box-sizing: border-box;
|
|
1943
|
+
max-height: 240px;
|
|
1944
|
+
margin: 0;
|
|
1945
|
+
padding: 0.2rem;
|
|
1946
|
+
border: 1px solid var(--s-line);
|
|
1947
|
+
border-radius: var(--s-br);
|
|
1948
|
+
background: var(--s-background);
|
|
1949
|
+
list-style: none;
|
|
1950
|
+
box-shadow: 0 10px 26px rgba(0, 0, 0, 0.18);
|
|
1951
|
+
overflow-y: auto;
|
|
1952
|
+
}
|
|
1953
|
+
|
|
1954
|
+
.typeaheadRow {
|
|
1955
|
+
@include sms-reset;
|
|
1956
|
+
@include sms-focus;
|
|
1957
|
+
|
|
1958
|
+
display: flex;
|
|
1959
|
+
gap: 0.5rem;
|
|
1960
|
+
align-items: center;
|
|
1961
|
+
box-sizing: border-box;
|
|
1962
|
+
width: 100%;
|
|
1963
|
+
padding: 0.4rem 0.5rem;
|
|
1964
|
+
border: none;
|
|
1965
|
+
border-radius: var(--s-btn-br);
|
|
1966
|
+
background: transparent;
|
|
1967
|
+
color: var(--s-paragraph);
|
|
1968
|
+
font-size: 0.83rem;
|
|
1969
|
+
cursor: pointer;
|
|
1970
|
+
|
|
1971
|
+
&:hover {
|
|
1972
|
+
background: var(--s-hover);
|
|
1973
|
+
}
|
|
1974
|
+
|
|
1975
|
+
strong {
|
|
1976
|
+
color: var(--s-paragraph);
|
|
1977
|
+
font-weight: 700;
|
|
1978
|
+
}
|
|
1979
|
+
|
|
1980
|
+
small {
|
|
1981
|
+
color: var(--s-muted);
|
|
1982
|
+
font-size: 0.75rem;
|
|
1983
|
+
font-variant-numeric: tabular-nums;
|
|
1984
|
+
}
|
|
1985
|
+
}
|
|
1986
|
+
|
|
1987
|
+
.typeaheadActive {
|
|
1988
|
+
background: var(--s-tint);
|
|
1989
|
+
}
|
|
1990
|
+
|
|
1991
|
+
.numberChoices {
|
|
1992
|
+
display: flex;
|
|
1993
|
+
flex-wrap: wrap;
|
|
1994
|
+
gap: 0.35rem;
|
|
1995
|
+
margin-top: 0.3rem;
|
|
1996
|
+
}
|
|
1997
|
+
|
|
1998
|
+
// ---------------------------------------------------------------------------
|
|
1999
|
+
// SmsLineSettings
|
|
2000
|
+
// ---------------------------------------------------------------------------
|
|
2001
|
+
|
|
2002
|
+
.settings {
|
|
2003
|
+
@include sms-tokens;
|
|
2004
|
+
|
|
2005
|
+
box-sizing: border-box;
|
|
2006
|
+
padding: 1.25rem 1.5rem 2rem;
|
|
2007
|
+
color: var(--s-paragraph);
|
|
2008
|
+
font-size: 0.85rem;
|
|
2009
|
+
}
|
|
2010
|
+
|
|
2011
|
+
.settingsHead {
|
|
2012
|
+
display: flex;
|
|
2013
|
+
flex-wrap: wrap;
|
|
2014
|
+
gap: 1rem;
|
|
2015
|
+
align-items: flex-start;
|
|
2016
|
+
justify-content: space-between;
|
|
2017
|
+
margin-bottom: 1rem;
|
|
2018
|
+
}
|
|
2019
|
+
|
|
2020
|
+
.pageIntro {
|
|
2021
|
+
max-width: 62ch;
|
|
2022
|
+
margin: 0.35rem 0 0;
|
|
2023
|
+
color: var(--s-muted);
|
|
2024
|
+
font-size: 0.83rem;
|
|
2025
|
+
line-height: 1.5;
|
|
2026
|
+
}
|
|
2027
|
+
|
|
2028
|
+
/* The practice's texting numbers, always in view: a client asking "what number
|
|
2029
|
+
do I text?" is answered from the page header, not from Settings. */
|
|
2030
|
+
.lineNumbers {
|
|
2031
|
+
display: flex;
|
|
2032
|
+
flex-wrap: wrap;
|
|
2033
|
+
gap: 0.4rem 0.6rem;
|
|
2034
|
+
margin: 0.55rem 0 0;
|
|
2035
|
+
padding: 0;
|
|
2036
|
+
list-style: none;
|
|
2037
|
+
}
|
|
2038
|
+
|
|
2039
|
+
.lineNumber {
|
|
2040
|
+
display: inline-flex;
|
|
2041
|
+
align-items: baseline;
|
|
2042
|
+
gap: 0.4rem;
|
|
2043
|
+
padding: 0.22rem 0.6rem;
|
|
2044
|
+
border: 1px solid var(--s-line);
|
|
2045
|
+
border-radius: 999px;
|
|
2046
|
+
background: var(--s-surface);
|
|
2047
|
+
font-size: 0.8rem;
|
|
2048
|
+
line-height: 1.3;
|
|
2049
|
+
}
|
|
2050
|
+
|
|
2051
|
+
.lineNumberLabel {
|
|
2052
|
+
color: var(--s-muted);
|
|
2053
|
+
}
|
|
2054
|
+
|
|
2055
|
+
.lineNumberValue {
|
|
2056
|
+
font-variant-numeric: tabular-nums;
|
|
2057
|
+
letter-spacing: 0.02em;
|
|
2058
|
+
color: var(--s-paragraph);
|
|
2059
|
+
user-select: all;
|
|
2060
|
+
}
|
|
2061
|
+
|
|
2062
|
+
.statusCard {
|
|
2063
|
+
display: flex;
|
|
2064
|
+
flex-wrap: wrap;
|
|
2065
|
+
gap: 0.75rem 1.5rem;
|
|
2066
|
+
box-sizing: border-box;
|
|
2067
|
+
margin-bottom: 1.25rem;
|
|
2068
|
+
padding: 0.85rem 1rem;
|
|
2069
|
+
border: 1px solid var(--s-line);
|
|
2070
|
+
border-radius: var(--s-br);
|
|
2071
|
+
background: var(--s-surface);
|
|
2072
|
+
}
|
|
2073
|
+
|
|
2074
|
+
.statusItem {
|
|
2075
|
+
display: flex;
|
|
2076
|
+
flex-direction: column;
|
|
2077
|
+
gap: 0.2rem;
|
|
2078
|
+
min-width: 140px;
|
|
2079
|
+
}
|
|
2080
|
+
|
|
2081
|
+
.statusLabel {
|
|
2082
|
+
color: var(--s-muted);
|
|
2083
|
+
font-size: 0.72rem;
|
|
2084
|
+
font-weight: 700;
|
|
2085
|
+
letter-spacing: 0.04em;
|
|
2086
|
+
text-transform: uppercase;
|
|
2087
|
+
}
|
|
2088
|
+
|
|
2089
|
+
.statusValue {
|
|
2090
|
+
margin: 0;
|
|
2091
|
+
color: var(--s-paragraph);
|
|
2092
|
+
font-size: 0.88rem;
|
|
2093
|
+
font-weight: 600;
|
|
2094
|
+
}
|
|
2095
|
+
|
|
2096
|
+
.statusMeaning {
|
|
2097
|
+
flex: 1 1 260px;
|
|
2098
|
+
min-width: 220px;
|
|
2099
|
+
color: var(--s-muted);
|
|
2100
|
+
font-size: 0.8rem;
|
|
2101
|
+
line-height: 1.5;
|
|
2102
|
+
}
|
|
2103
|
+
|
|
2104
|
+
.section {
|
|
2105
|
+
margin-bottom: 1.75rem;
|
|
2106
|
+
}
|
|
2107
|
+
|
|
2108
|
+
.sectionHead {
|
|
2109
|
+
display: flex;
|
|
2110
|
+
flex-wrap: wrap;
|
|
2111
|
+
gap: 0.75rem;
|
|
2112
|
+
align-items: center;
|
|
2113
|
+
justify-content: space-between;
|
|
2114
|
+
margin-bottom: 0.6rem;
|
|
2115
|
+
}
|
|
2116
|
+
|
|
2117
|
+
.sectionTitle {
|
|
2118
|
+
margin: 0;
|
|
2119
|
+
color: var(--s-paragraph);
|
|
2120
|
+
font-size: 1.05rem;
|
|
2121
|
+
font-weight: 700;
|
|
2122
|
+
}
|
|
2123
|
+
|
|
2124
|
+
.tableWrap {
|
|
2125
|
+
overflow-x: auto;
|
|
2126
|
+
border: 1px solid var(--s-line);
|
|
2127
|
+
border-radius: var(--s-br);
|
|
2128
|
+
background: var(--s-background);
|
|
2129
|
+
}
|
|
2130
|
+
|
|
2131
|
+
.table {
|
|
2132
|
+
width: 100%;
|
|
2133
|
+
border-collapse: collapse;
|
|
2134
|
+
color: var(--s-paragraph);
|
|
2135
|
+
font-size: 0.84rem;
|
|
2136
|
+
text-align: left;
|
|
2137
|
+
|
|
2138
|
+
th,
|
|
2139
|
+
td {
|
|
2140
|
+
padding: 0.6rem 0.7rem;
|
|
2141
|
+
text-align: left;
|
|
2142
|
+
vertical-align: middle;
|
|
2143
|
+
}
|
|
2144
|
+
|
|
2145
|
+
thead th {
|
|
2146
|
+
border-bottom: 1px solid var(--s-line);
|
|
2147
|
+
background: var(--s-surface);
|
|
2148
|
+
color: var(--s-muted);
|
|
2149
|
+
font-size: 0.72rem;
|
|
2150
|
+
font-weight: 700;
|
|
2151
|
+
letter-spacing: 0.05em;
|
|
2152
|
+
text-transform: uppercase;
|
|
2153
|
+
white-space: nowrap;
|
|
2154
|
+
}
|
|
2155
|
+
|
|
2156
|
+
tbody tr {
|
|
2157
|
+
border-bottom: 1px solid color-mix(in srgb, var(--s-line) 55%, transparent);
|
|
2158
|
+
|
|
2159
|
+
&:last-child {
|
|
2160
|
+
border-bottom: none;
|
|
2161
|
+
}
|
|
2162
|
+
|
|
2163
|
+
&:hover {
|
|
2164
|
+
background: var(--s-hover);
|
|
2165
|
+
}
|
|
2166
|
+
}
|
|
2167
|
+
|
|
2168
|
+
tbody td strong {
|
|
2169
|
+
color: var(--s-paragraph);
|
|
2170
|
+
font-size: 0.88rem;
|
|
2171
|
+
font-weight: 700;
|
|
2172
|
+
}
|
|
2173
|
+
}
|
|
2174
|
+
|
|
2175
|
+
.numberCell {
|
|
2176
|
+
font-variant-numeric: tabular-nums;
|
|
2177
|
+
white-space: nowrap;
|
|
2178
|
+
}
|
|
2179
|
+
|
|
2180
|
+
.actionsHead,
|
|
2181
|
+
.actionsCell {
|
|
2182
|
+
text-align: right;
|
|
2183
|
+
white-space: nowrap;
|
|
2184
|
+
}
|
|
2185
|
+
|
|
2186
|
+
.actionsCell > * {
|
|
2187
|
+
margin-left: 0.5rem;
|
|
2188
|
+
}
|
|
2189
|
+
|
|
2190
|
+
.chipRow {
|
|
2191
|
+
display: flex;
|
|
2192
|
+
flex-wrap: wrap;
|
|
2193
|
+
gap: 0.25rem;
|
|
2194
|
+
}
|
|
2195
|
+
|
|
2196
|
+
.staffChip {
|
|
2197
|
+
display: inline-flex;
|
|
2198
|
+
align-items: center;
|
|
2199
|
+
padding: 0.1rem 0.45rem;
|
|
2200
|
+
border-radius: 999px;
|
|
2201
|
+
background: var(--s-tint);
|
|
2202
|
+
color: var(--s-primary);
|
|
2203
|
+
font-size: 0.7rem;
|
|
2204
|
+
font-weight: 700;
|
|
2205
|
+
}
|
|
2206
|
+
|
|
2207
|
+
.tableEmpty {
|
|
2208
|
+
padding: 2.25rem 1rem;
|
|
2209
|
+
color: var(--s-muted);
|
|
2210
|
+
text-align: center;
|
|
2211
|
+
|
|
2212
|
+
p {
|
|
2213
|
+
margin: 0 0 0.9rem;
|
|
2214
|
+
color: var(--s-muted);
|
|
2215
|
+
font-size: 0.88rem;
|
|
2216
|
+
}
|
|
2217
|
+
}
|
|
2218
|
+
|
|
2219
|
+
.staffPicker {
|
|
2220
|
+
display: flex;
|
|
2221
|
+
flex-direction: column;
|
|
2222
|
+
gap: 0.3rem;
|
|
2223
|
+
box-sizing: border-box;
|
|
2224
|
+
max-height: 190px;
|
|
2225
|
+
padding: 0.5rem 0.6rem;
|
|
2226
|
+
border: 1px solid var(--s-line);
|
|
2227
|
+
border-radius: var(--s-btn-br);
|
|
2228
|
+
background: var(--s-background);
|
|
2229
|
+
overflow-y: auto;
|
|
2230
|
+
}
|
|
2231
|
+
|
|
2232
|
+
.deletedRow {
|
|
2233
|
+
opacity: 0.65;
|
|
2234
|
+
|
|
2235
|
+
strong {
|
|
2236
|
+
text-decoration: line-through;
|
|
2237
|
+
}
|
|
2238
|
+
}
|
|
2239
|
+
|
|
2240
|
+
// ---------------------------------------------------------------------------
|
|
2241
|
+
// Narrow screens
|
|
2242
|
+
// ---------------------------------------------------------------------------
|
|
2243
|
+
|
|
2244
|
+
// An iPad in landscape is 1024px and the CRM's chrome takes some of that: the
|
|
2245
|
+
// list pane loses 60px rather than the conversation losing its bubbles.
|
|
2246
|
+
@media (max-width: 1180px) {
|
|
2247
|
+
.panes {
|
|
2248
|
+
grid-template-columns: 280px minmax(0, 1fr);
|
|
2249
|
+
}
|
|
2250
|
+
}
|
|
2251
|
+
|
|
2252
|
+
// Below 900 the two panes stop being two panes: one is on screen at a time,
|
|
2253
|
+
// and the conversation carries a back button. Which one shows is a class the
|
|
2254
|
+
// component sets, not a media query guess.
|
|
2255
|
+
@media (max-width: 900px) {
|
|
2256
|
+
.inbox {
|
|
2257
|
+
padding: 0.85rem 0.85rem 1rem;
|
|
2258
|
+
}
|
|
2259
|
+
|
|
2260
|
+
.panes {
|
|
2261
|
+
grid-template-columns: minmax(0, 1fr);
|
|
2262
|
+
height: calc(100vh - 170px);
|
|
2263
|
+
}
|
|
2264
|
+
|
|
2265
|
+
.paneHiddenNarrow {
|
|
2266
|
+
display: none;
|
|
2267
|
+
}
|
|
2268
|
+
|
|
2269
|
+
.bubble {
|
|
2270
|
+
max-width: 88%;
|
|
2271
|
+
}
|
|
2272
|
+
|
|
2273
|
+
.settings {
|
|
2274
|
+
padding: 1rem 0.85rem 2rem;
|
|
2275
|
+
}
|
|
2276
|
+
|
|
2277
|
+
.actionsCell {
|
|
2278
|
+
text-align: left;
|
|
2279
|
+
|
|
2280
|
+
> * {
|
|
2281
|
+
margin: 0.15rem 0.5rem 0.15rem 0;
|
|
2282
|
+
}
|
|
2283
|
+
}
|
|
2284
|
+
}
|
|
2285
|
+
|
|
2286
|
+
// The back button only exists in the collapsed layout.
|
|
2287
|
+
.backButton {
|
|
2288
|
+
display: none;
|
|
2289
|
+
}
|
|
2290
|
+
|
|
2291
|
+
@media (max-width: 900px) {
|
|
2292
|
+
.backButton {
|
|
2293
|
+
display: inline-flex;
|
|
2294
|
+
}
|
|
2295
|
+
}
|
|
2296
|
+
|
|
2297
|
+
// ---------------------------------------------------------------------------
|
|
2298
|
+
// The compose modal's header row
|
|
2299
|
+
// ---------------------------------------------------------------------------
|
|
2300
|
+
|
|
2301
|
+
// From and To on one line. Two full-width blocks stacked above the message
|
|
2302
|
+
// pushed the thing anyone actually came here to write below the fold, and
|
|
2303
|
+
// "From" is not a decision on a one-line system - it is a fact.
|
|
2304
|
+
.composeRow {
|
|
2305
|
+
display: grid;
|
|
2306
|
+
gap: 0.5rem 0.75rem;
|
|
2307
|
+
grid-template-columns: minmax(0, 220px) 1fr;
|
|
2308
|
+
align-items: start;
|
|
2309
|
+
}
|
|
2310
|
+
|
|
2311
|
+
.composeField {
|
|
2312
|
+
display: flex;
|
|
2313
|
+
flex-direction: column;
|
|
2314
|
+
gap: 0.2rem;
|
|
2315
|
+
min-width: 0;
|
|
2316
|
+
}
|
|
2317
|
+
|
|
2318
|
+
// Quieter and smaller than .label: at this density the labels are wayfinding,
|
|
2319
|
+
// not headings.
|
|
2320
|
+
.labelSmall {
|
|
2321
|
+
color: var(--s-muted);
|
|
2322
|
+
font-size: 0.68rem;
|
|
2323
|
+
font-weight: 700;
|
|
2324
|
+
letter-spacing: 0.05em;
|
|
2325
|
+
text-transform: uppercase;
|
|
2326
|
+
}
|
|
2327
|
+
|
|
2328
|
+
// The single-line case: a select with one option is a question with one
|
|
2329
|
+
// answer, so the line is stated rather than asked.
|
|
2330
|
+
.fromChip {
|
|
2331
|
+
display: flex;
|
|
2332
|
+
gap: 0.4rem;
|
|
2333
|
+
align-items: baseline;
|
|
2334
|
+
box-sizing: border-box;
|
|
2335
|
+
min-height: var(--s-control);
|
|
2336
|
+
padding: 0.35rem 0.6rem;
|
|
2337
|
+
border: 1px solid var(--s-line);
|
|
2338
|
+
border-radius: var(--s-btn-br);
|
|
2339
|
+
background: var(--s-surface);
|
|
2340
|
+
color: var(--s-paragraph);
|
|
2341
|
+
font-size: 0.83rem;
|
|
2342
|
+
line-height: 1.3;
|
|
2343
|
+
|
|
2344
|
+
strong {
|
|
2345
|
+
overflow: hidden;
|
|
2346
|
+
font-weight: 700;
|
|
2347
|
+
text-overflow: ellipsis;
|
|
2348
|
+
white-space: nowrap;
|
|
2349
|
+
}
|
|
2350
|
+
|
|
2351
|
+
small {
|
|
2352
|
+
color: var(--s-muted);
|
|
2353
|
+
font-size: 0.75rem;
|
|
2354
|
+
font-variant-numeric: tabular-nums;
|
|
2355
|
+
white-space: nowrap;
|
|
2356
|
+
}
|
|
2357
|
+
}
|
|
2358
|
+
|
|
2359
|
+
// The modal's own copy of the thread composer's card, which wants a little
|
|
2360
|
+
// more room to write in than the bar at the bottom of a conversation.
|
|
2361
|
+
.composeBody {
|
|
2362
|
+
min-height: 92px;
|
|
2363
|
+
max-height: 220px;
|
|
2364
|
+
padding-bottom: 0.5rem;
|
|
2365
|
+
resize: vertical;
|
|
2366
|
+
}
|
|
2367
|
+
|
|
2368
|
+
.composeForm {
|
|
2369
|
+
gap: 0.7rem;
|
|
2370
|
+
}
|
|
2371
|
+
|
|
2372
|
+
// The counter sits beside Templates in the bar rather than under the box, so
|
|
2373
|
+
// it needs to stop looking like a field label and start looking like a note.
|
|
2374
|
+
.composeBar {
|
|
2375
|
+
justify-content: flex-start;
|
|
2376
|
+
}
|
|
2377
|
+
|
|
2378
|
+
@media (max-width: 639px) {
|
|
2379
|
+
.composeRow {
|
|
2380
|
+
grid-template-columns: 1fr;
|
|
2381
|
+
}
|
|
2382
|
+
}
|