portal-mojo 0.2.7 → 0.2.9
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 +59 -11
- package/docs/DetailView.md +3 -2
- package/docs/README.md +7 -2
- package/docs/account.md +261 -0
- package/docs/admin-bouncer.md +3 -3
- package/docs/admin-credentials.md +4 -3
- package/docs/admin-devices-geoip.md +1 -1
- package/docs/admin-dns-certificates.md +2 -2
- package/docs/admin-dns-records.md +1 -1
- package/docs/admin-edge.md +210 -0
- package/docs/admin-identity-directories-sources.json +2 -2
- package/docs/admin-identity-directories.md +9 -0
- package/docs/admin-identity-users.md +2 -2
- package/docs/admin-jobs.md +4 -4
- package/docs/admin-media-rendering.md +142 -0
- package/docs/admin-modals.md +7 -0
- package/docs/admin-monitoring.md +4 -4
- package/docs/admin-network-security.md +2 -2
- package/docs/admin-phonehub.md +4 -1
- package/docs/admin-storage.md +5 -2
- package/docs/auth-pages.md +8 -3
- package/docs/auth.md +45 -2
- package/docs/calendar.md +1 -1
- package/docs/client.md +159 -26
- package/docs/collection-multiselect.md +2 -2
- package/docs/combobox.md +2 -2
- package/docs/dataview.md +1 -1
- package/docs/datepicker.md +3 -3
- package/docs/daterange.md +1 -1
- package/docs/datetimepicker.md +1 -1
- package/docs/defineModel.md +40 -7
- package/docs/detail-primitives.md +1 -1
- package/docs/feedback.md +18 -8
- package/docs/file-upload-ux.md +2 -2
- package/docs/idioms.md +13 -1
- package/docs/loading.md +2 -2
- package/docs/location-address.md +2 -1
- package/docs/markdown.md +3 -3
- package/docs/menus-and-access.md +21 -1
- package/docs/multiselect-dropdown.md +1 -1
- package/docs/popover.md +1 -1
- package/docs/record-feed.md +2 -2
- package/docs/taginput.md +2 -1
- package/docs/timepicker.md +1 -1
- package/docs/timezone-select.md +4 -3
- package/docs/user-menu.md +4 -3
- package/docs/worldmap.md +2 -2
- package/package.json +4 -2
- package/src/account/AccountModal.tsx +276 -0
- package/src/account/AccountSections.tsx +370 -0
- package/src/account/NotificationPreferences.tsx +152 -0
- package/src/account/PasskeyList.tsx +144 -0
- package/src/account/api.ts +236 -0
- package/src/account/credential-models.ts +153 -0
- package/src/account/dialogs.tsx +822 -0
- package/src/account/index.ts +16 -0
- package/src/account/models.ts +76 -0
- package/src/account/sections/ApiKeysSection.tsx +138 -0
- package/src/admin/core/index.ts +1 -1
- package/src/admin/credentials/secret-dialog.tsx +3 -77
- package/src/admin/dns/models.ts +1 -1
- package/src/admin/domains/infrastructure.ts +12 -2
- package/src/admin/domains/security.ts +3 -1
- package/src/admin/edge/BlocklistPage.tsx +110 -0
- package/src/admin/edge/UpstreamsPage.tsx +113 -0
- package/src/admin/edge/VhostDetail.tsx +166 -0
- package/src/admin/edge/VhostWizard.tsx +273 -0
- package/src/admin/edge/VhostsPage.tsx +47 -0
- package/src/admin/edge/api.ts +56 -0
- package/src/admin/edge/index.ts +15 -0
- package/src/admin/edge/models.ts +395 -0
- package/src/admin/edge/permissions.ts +13 -0
- package/src/admin/identity/users/PersonalApiKeysPage.tsx +10 -0
- package/src/admin/identity/users/models.ts +16 -128
- package/src/admin/identity/users/sections/ApiKeysSection.tsx +3 -119
- package/src/admin/identity/users/sections/NotificationsSection.tsx +23 -102
- package/src/admin/identity/users/sections/PersonalSection.tsx +18 -5
- package/src/admin/identity/users/sections/actions.tsx +10 -103
- package/src/admin/index.ts +1 -0
- package/src/admin/phonehub/api.ts +5 -3
- package/src/admin/public/infrastructure.ts +1 -0
- package/src/admin/registry.ts +2 -1
- package/src/admin/storage/MediaRenderingPage.tsx +258 -0
- package/src/admin/storage/file-renditions.ts +12 -0
- package/src/admin/storage/index.ts +2 -0
- package/src/admin/storage/rendition-settings.ts +264 -0
- package/src/charts/exportChart.ts +1 -1
- package/src/client/action-result.ts +23 -61
- package/src/client/active-group.ts +9 -3
- package/src/client/auth.ts +207 -6
- package/src/client/client.ts +49 -10
- package/src/client/errors.ts +57 -2
- package/src/client/group-context.ts +2 -0
- package/src/client/group.tsx +112 -20
- package/src/client/hooks.ts +7 -3
- package/src/client/mock.ts +1429 -20
- package/src/client/model.ts +15 -3
- package/src/client/renditions-mock.ts +212 -0
- package/src/client/scoped.ts +19 -5
- package/src/styles/components/account.css +304 -0
- package/src/styles/components/admin-assistant.css +28 -0
- package/src/styles/components/admin-bouncer.css +53 -0
- package/src/styles/components/admin-cloudwatch.css +23 -0
- package/src/styles/components/admin-dashboard.css +16 -0
- package/src/styles/components/admin-devices.css +260 -0
- package/src/styles/components/admin-dns-certificates.css +28 -0
- package/src/styles/components/admin-dns-registrar.css +1 -0
- package/src/styles/components/admin-dns.css +43 -0
- package/src/styles/components/admin-edge.css +1 -0
- package/src/styles/components/admin-incidents.css +14 -0
- package/src/styles/components/admin-jobs.css +237 -0
- package/src/styles/components/admin-media-rendering.css +38 -0
- package/src/styles/components/admin-messaging.css +3 -0
- package/src/styles/components/admin-monitoring.css +152 -0
- package/src/styles/components/admin-network.css +406 -0
- package/src/styles/components/admin-phonehub.css +61 -0
- package/src/styles/components/admin-push.css +30 -0
- package/src/styles/components/admin-rules.css +23 -0
- package/src/styles/components/admin-security.css +37 -0
- package/src/styles/components/admin-shortlinks.css +8 -0
- package/src/styles/components/admin-signin.css +64 -0
- package/src/styles/components/admin-storage.css +4 -0
- package/src/styles/components/auth.css +162 -0
- package/src/styles/components/calendar.css +228 -0
- package/src/styles/components/charts-c2.css +249 -0
- package/src/styles/components/collection-multiselect.css +114 -0
- package/src/styles/components/collection-select.css +63 -0
- package/src/styles/components/combobox.css +42 -0
- package/src/styles/components/dataview.css +139 -0
- package/src/styles/components/datepicker.css +106 -0
- package/src/styles/components/daterange.css +119 -0
- package/src/styles/components/datetimepicker.css +230 -0
- package/src/styles/components/detail-primitives.css +246 -0
- package/src/styles/components/detailview.css +69 -0
- package/src/styles/components/drawer.css +111 -0
- package/src/styles/components/file-upload.css +112 -0
- package/src/styles/components/form-wizard.css +11 -0
- package/src/styles/components/formview.css +74 -0
- package/src/styles/components/group-admin.css +302 -0
- package/src/styles/components/guardrail.css +29 -0
- package/src/styles/components/idioms.css +84 -0
- package/src/styles/components/image-editor.css +80 -0
- package/src/styles/components/loading.css +200 -0
- package/src/styles/components/location-address.css +32 -0
- package/src/styles/components/markdown.css +192 -0
- package/src/styles/components/multiselect-dropdown.css +92 -0
- package/src/styles/components/popover.css +35 -0
- package/src/styles/components/record-feed.css +161 -0
- package/src/styles/components/right-panel.css +37 -0
- package/src/styles/components/tabs.css +26 -0
- package/src/styles/components/taginput.css +132 -0
- package/src/styles/components/timepicker.css +282 -0
- package/src/styles/components/user-admin.css +189 -0
- package/src/styles/components/user-menu.css +98 -0
- package/src/styles/components/worldmap.css +97 -0
- package/src/styles/core.css +807 -0
- package/src/styles/index.css +69 -0
- package/src/styles/required-tokens.json +32 -0
- package/src/ui/FileField.tsx +12 -7
- package/src/ui/FormWizard.tsx +9 -3
- package/src/ui/ThemeProvider.tsx +1 -1
- package/src/ui/UserMenu.tsx +6 -2
- package/src/ui/detail/index.ts +2 -2
- package/src/ui/index.ts +4 -2
- package/src/ui/loading.tsx +1 -1
- package/src/ui/modal.tsx +76 -0
- package/src/ui/secret-dialog.tsx +83 -0
- package/src/ui/toast.tsx +21 -3
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/* ======================================================== DateTimePicker
|
|
2
|
+
Layout glue for portal-mojo <DateTimePicker>
|
|
3
|
+
(packages/portal-mojo/src/ui/date/DateTimePicker.tsx). Ported from the
|
|
4
|
+
`.mojo-datetime-*` block of web-mojo calendar.css; Bootstrap vars map onto
|
|
5
|
+
theme.css tokens (--bs-secondary-color→--mute, --bs-border-color→--line,
|
|
6
|
+
--bs-tertiary-bg→--surface, --bs-primary→--accent, --bs-danger→--bad).
|
|
7
|
+
|
|
8
|
+
Deliberately thin: theme/calendar.css already styles the grid,
|
|
9
|
+
theme/timepicker.css the steppers, theme/combobox.css the zone picker and
|
|
10
|
+
theme/popover.css the floating shell. What lives here is the two-column
|
|
11
|
+
popover, the timezone row, the footer, and the trigger.
|
|
12
|
+
|
|
13
|
+
NOT imported by theme.css yet — the orchestrator adds
|
|
14
|
+
`@import "./theme/datetimepicker.css";` at merge. */
|
|
15
|
+
|
|
16
|
+
.mojo-datetime-picker {
|
|
17
|
+
position: relative; /* the clear ✕ is absolutely placed over the trigger */
|
|
18
|
+
display: inline-block;
|
|
19
|
+
width: 100%;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/* ── Trigger ──────────────────────────────────────────────────── */
|
|
23
|
+
/* Same house control metrics as the Date/Time triggers so the three line up
|
|
24
|
+
in one form grid. */
|
|
25
|
+
|
|
26
|
+
.mojo-datetime-trigger {
|
|
27
|
+
display: flex;
|
|
28
|
+
align-items: center;
|
|
29
|
+
gap: 10px;
|
|
30
|
+
box-sizing: border-box;
|
|
31
|
+
width: 100%;
|
|
32
|
+
min-height: 36px;
|
|
33
|
+
padding: 7px 10px;
|
|
34
|
+
background: var(--field-bg);
|
|
35
|
+
color: var(--ink-strong);
|
|
36
|
+
border: 1px solid var(--field-line);
|
|
37
|
+
border-radius: 8px;
|
|
38
|
+
font-size: 13.5px;
|
|
39
|
+
text-align: left;
|
|
40
|
+
cursor: pointer;
|
|
41
|
+
transition: border-color 0.15s ease, box-shadow 0.15s ease;
|
|
42
|
+
}
|
|
43
|
+
.mojo-datetime-trigger:hover:not(:disabled):not(.is-readonly) { border-color: var(--mute); }
|
|
44
|
+
/* Focus ring on the WRAPPER so it also shows while the clear ✕ holds focus. */
|
|
45
|
+
.mojo-datetime-picker:focus-within .mojo-datetime-trigger {
|
|
46
|
+
border-color: var(--accent);
|
|
47
|
+
box-shadow: 0 0 0 3px var(--accent-soft);
|
|
48
|
+
}
|
|
49
|
+
.mojo-datetime-trigger:disabled { opacity: 0.55; cursor: not-allowed; }
|
|
50
|
+
.mojo-datetime-trigger.is-readonly { cursor: default; }
|
|
51
|
+
.mojo-datetime-trigger.is-invalid { border-color: var(--bad); }
|
|
52
|
+
.mojo-datetime-trigger.has-clear { padding-right: 32px; }
|
|
53
|
+
|
|
54
|
+
.mojo-datetime-trigger > i {
|
|
55
|
+
flex: none;
|
|
56
|
+
color: var(--mute);
|
|
57
|
+
font-size: 14.5px;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.mojo-datetime-trigger-text {
|
|
61
|
+
flex: 1;
|
|
62
|
+
min-width: 0;
|
|
63
|
+
overflow: hidden;
|
|
64
|
+
text-overflow: ellipsis;
|
|
65
|
+
white-space: nowrap;
|
|
66
|
+
font-feature-settings: "tnum" on;
|
|
67
|
+
font-variant-numeric: tabular-nums;
|
|
68
|
+
}
|
|
69
|
+
.mojo-datetime-trigger-text.is-empty { color: var(--mute); }
|
|
70
|
+
|
|
71
|
+
/* Sibling of the trigger (nesting buttons is invalid HTML), overlaid on its
|
|
72
|
+
right edge; the wrapper carries the reveal so the pointer sitting on the ✕
|
|
73
|
+
keeps it visible. */
|
|
74
|
+
.mojo-datetime-trigger-clear {
|
|
75
|
+
position: absolute;
|
|
76
|
+
top: 50%;
|
|
77
|
+
right: 5px;
|
|
78
|
+
transform: translateY(-50%);
|
|
79
|
+
display: none;
|
|
80
|
+
align-items: center;
|
|
81
|
+
justify-content: center;
|
|
82
|
+
width: 22px;
|
|
83
|
+
height: 22px;
|
|
84
|
+
padding: 0;
|
|
85
|
+
background: none;
|
|
86
|
+
border: 0;
|
|
87
|
+
border-radius: 50%;
|
|
88
|
+
color: var(--mute);
|
|
89
|
+
font-size: 11px;
|
|
90
|
+
line-height: 1;
|
|
91
|
+
cursor: pointer;
|
|
92
|
+
}
|
|
93
|
+
.mojo-datetime-picker:hover .mojo-datetime-trigger-clear,
|
|
94
|
+
.mojo-datetime-picker:focus-within .mojo-datetime-trigger-clear {
|
|
95
|
+
display: inline-flex;
|
|
96
|
+
}
|
|
97
|
+
.mojo-datetime-trigger-clear:hover { background: var(--line-soft); color: var(--ink-strong); }
|
|
98
|
+
|
|
99
|
+
/* ── Panel shell ──────────────────────────────────────────────── */
|
|
100
|
+
/* The shared .mojo-popover shell is unpadded/unsized on purpose — the content
|
|
101
|
+
brings the padding and the floor that keeps calendar + time side by side
|
|
102
|
+
(~260 calendar + ~230 time + gap, source's numbers). */
|
|
103
|
+
|
|
104
|
+
.mojo-popover.mojo-datetime-popover .mojo-datetime-popover-inner {
|
|
105
|
+
padding: 16px 18px;
|
|
106
|
+
min-width: 520px;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.mojo-datetime-picker-inline {
|
|
110
|
+
display: block;
|
|
111
|
+
background: var(--surface);
|
|
112
|
+
border: 1px solid var(--line);
|
|
113
|
+
border-radius: 10px;
|
|
114
|
+
box-shadow: var(--shadow);
|
|
115
|
+
}
|
|
116
|
+
.mojo-datetime-picker-inline .mojo-datetime-popover-inner {
|
|
117
|
+
padding: 16px 18px;
|
|
118
|
+
min-width: 520px;
|
|
119
|
+
}
|
|
120
|
+
.mojo-datetime-picker-inline.is-invalid { border-color: var(--bad); }
|
|
121
|
+
.mojo-datetime-picker-inline.is-disabled { opacity: 0.55; }
|
|
122
|
+
|
|
123
|
+
/* ── Two-column row: calendar left, time right ────────────────── */
|
|
124
|
+
|
|
125
|
+
.mojo-datetime-row {
|
|
126
|
+
display: flex;
|
|
127
|
+
flex-direction: row;
|
|
128
|
+
align-items: stretch;
|
|
129
|
+
gap: 18px;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.mojo-datetime-cal-col {
|
|
133
|
+
flex: 0 0 auto;
|
|
134
|
+
min-width: 250px;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.mojo-datetime-time-col {
|
|
138
|
+
flex: 1 1 auto;
|
|
139
|
+
min-width: 220px;
|
|
140
|
+
display: flex;
|
|
141
|
+
flex-direction: column;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.mojo-datetime-time-head {
|
|
145
|
+
margin-bottom: 8px;
|
|
146
|
+
padding-bottom: 6px;
|
|
147
|
+
border-bottom: 1px solid var(--line);
|
|
148
|
+
font-size: 11px;
|
|
149
|
+
font-weight: 600;
|
|
150
|
+
letter-spacing: 0.08em;
|
|
151
|
+
text-transform: uppercase;
|
|
152
|
+
color: var(--mute);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/* The embedded TimePicker is a card in its own right — drop its chrome so it
|
|
156
|
+
reads as this popover's right-hand column, and let it fill the column so
|
|
157
|
+
the steppers sit against the calendar grid rather than at its top edge. */
|
|
158
|
+
.mojo-datetime-time-col .mojo-time-picker {
|
|
159
|
+
flex: 1;
|
|
160
|
+
display: flex;
|
|
161
|
+
align-items: center;
|
|
162
|
+
}
|
|
163
|
+
.mojo-datetime-time-col .mojo-time-picker-inline {
|
|
164
|
+
flex: 1;
|
|
165
|
+
display: block;
|
|
166
|
+
width: 100%;
|
|
167
|
+
min-width: 0;
|
|
168
|
+
padding: 0;
|
|
169
|
+
background: transparent;
|
|
170
|
+
border: 0;
|
|
171
|
+
box-shadow: none;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/* ── Full-width timezone row ──────────────────────────────────── */
|
|
175
|
+
/* Below the pair so the IANA combobox can use the whole popover width. */
|
|
176
|
+
|
|
177
|
+
.mojo-datetime-tz-row {
|
|
178
|
+
display: flex;
|
|
179
|
+
flex-direction: column;
|
|
180
|
+
gap: 5px;
|
|
181
|
+
margin-top: 12px;
|
|
182
|
+
padding-top: 12px;
|
|
183
|
+
border-top: 1px solid var(--line);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
.mojo-datetime-tz-label {
|
|
187
|
+
font-size: 11px;
|
|
188
|
+
font-weight: 600;
|
|
189
|
+
letter-spacing: 0.08em;
|
|
190
|
+
text-transform: uppercase;
|
|
191
|
+
color: var(--mute);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
.mojo-datetime-tz-host { width: 100%; }
|
|
195
|
+
/* The zone picker's own dropdown spans the row (it is locally positioned;
|
|
196
|
+
.mojo-popover is overflow:visible so it is never clipped). */
|
|
197
|
+
.mojo-datetime-tz-host .combo-pop { min-width: 100%; }
|
|
198
|
+
|
|
199
|
+
/* ── Footer ───────────────────────────────────────────────────── */
|
|
200
|
+
|
|
201
|
+
.mojo-datetime-foot {
|
|
202
|
+
margin-top: 14px;
|
|
203
|
+
padding-top: 10px;
|
|
204
|
+
border-top: 1px solid var(--line);
|
|
205
|
+
display: flex;
|
|
206
|
+
align-items: center;
|
|
207
|
+
justify-content: space-between;
|
|
208
|
+
gap: 8px;
|
|
209
|
+
}
|
|
210
|
+
/* "Now" is the source's btn-link: quiet until hovered. */
|
|
211
|
+
.mojo-datetime-now {
|
|
212
|
+
background: transparent;
|
|
213
|
+
border-color: transparent;
|
|
214
|
+
color: var(--accent);
|
|
215
|
+
}
|
|
216
|
+
.mojo-datetime-now:hover { background: var(--accent-soft); border-color: transparent; }
|
|
217
|
+
.mojo-datetime-now:disabled:hover { background: transparent; }
|
|
218
|
+
|
|
219
|
+
/* Narrow viewports: stack the columns rather than overflow the screen. The
|
|
220
|
+
popover is portal-mounted and sizes to its own content, so this is the only
|
|
221
|
+
place the layout can break. */
|
|
222
|
+
@media (max-width: 620px) {
|
|
223
|
+
.mojo-popover.mojo-datetime-popover .mojo-datetime-popover-inner,
|
|
224
|
+
.mojo-datetime-picker-inline .mojo-datetime-popover-inner {
|
|
225
|
+
min-width: 0;
|
|
226
|
+
}
|
|
227
|
+
.mojo-datetime-row { flex-direction: column; gap: 14px; }
|
|
228
|
+
.mojo-datetime-cal-col { min-width: 0; }
|
|
229
|
+
.mojo-datetime-time-col { min-width: 0; }
|
|
230
|
+
}
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
/* ============================================================
|
|
2
|
+
Detail primitives — StatusPanel, FlowStrip, Timeline,
|
|
3
|
+
KnownFieldsCard, MetadataSection, StackTraceView.
|
|
4
|
+
|
|
5
|
+
Ported from web-mojo src/core/css/core.css (the "StatusPanel",
|
|
6
|
+
"Timeline", "KnownFieldsCard", "FlowStrip" blocks) and the
|
|
7
|
+
inline <style> blocks AdminMetadataSection/StackTraceView
|
|
8
|
+
shipped. Bootstrap variables map onto mojo tokens:
|
|
9
|
+
--bs-tertiary-bg → --surface2
|
|
10
|
+
--bs-secondary-color → --mute
|
|
11
|
+
--bs-emphasis-color → --ink-strong
|
|
12
|
+
--bs-body-color → --ink
|
|
13
|
+
--bs-border-color → --line
|
|
14
|
+
--bs-border-color-translucent → --line-soft
|
|
15
|
+
rgba(--bs-<tone>-rgb, .10) → --<tone>-soft
|
|
16
|
+
so everything reads correctly under BOTH data-theme values —
|
|
17
|
+
the two ported <style> blocks were hardcoded light-only.
|
|
18
|
+
============================================================ */
|
|
19
|
+
|
|
20
|
+
/* ============================================================ StatusPanel */
|
|
21
|
+
/* Borderless flat surface; the tone-tinted bg + colored state eyebrow carry
|
|
22
|
+
the whole signal. Container queries (not media queries) stack the actions
|
|
23
|
+
below the headline under ~480px — the panel is routinely nested in a modal. */
|
|
24
|
+
.detail-status-panel {
|
|
25
|
+
container-type: inline-size;
|
|
26
|
+
border: 0;
|
|
27
|
+
border-radius: 12px;
|
|
28
|
+
padding: 14px 18px;
|
|
29
|
+
display: flex;
|
|
30
|
+
align-items: center;
|
|
31
|
+
justify-content: space-between;
|
|
32
|
+
gap: 18px;
|
|
33
|
+
background: var(--surface2);
|
|
34
|
+
}
|
|
35
|
+
@container (max-width: 480px) {
|
|
36
|
+
.detail-status-panel { flex-direction: column; align-items: stretch; gap: 12px; }
|
|
37
|
+
.detail-status-panel .detail-status-actions { flex-wrap: wrap; }
|
|
38
|
+
}
|
|
39
|
+
.detail-status-panel .detail-status-headline { min-width: 0; flex: 1; }
|
|
40
|
+
.detail-status-panel .detail-status-state {
|
|
41
|
+
font-family: ui-monospace, "SF Mono", Menlo, monospace;
|
|
42
|
+
font-size: 10px; letter-spacing: 0.12em; text-transform: uppercase; font-weight: 600;
|
|
43
|
+
margin-bottom: 3px; color: var(--mute);
|
|
44
|
+
display: inline-flex; align-items: center; gap: 6px;
|
|
45
|
+
}
|
|
46
|
+
.detail-status-panel .detail-status-dot {
|
|
47
|
+
width: 8px; height: 8px; border-radius: 999px;
|
|
48
|
+
background: currentColor; box-shadow: 0 0 8px currentColor;
|
|
49
|
+
}
|
|
50
|
+
.detail-status-panel .detail-status-icon { font-size: 14px; line-height: 1; }
|
|
51
|
+
.detail-status-panel .detail-status-line {
|
|
52
|
+
font-size: 16px; font-weight: 650; line-height: 1.25; letter-spacing: -0.01em;
|
|
53
|
+
margin: 0 0 3px; color: var(--ink-strong);
|
|
54
|
+
}
|
|
55
|
+
.detail-status-panel .detail-status-meta { color: var(--mute); font-size: 12.5px; line-height: 1.45; }
|
|
56
|
+
.detail-status-panel .detail-status-meta code {
|
|
57
|
+
font-size: 12px; background: var(--surface); border-radius: 5px; padding: 1px 5px; color: var(--ink);
|
|
58
|
+
}
|
|
59
|
+
.detail-status-panel .detail-status-actions { display: flex; gap: 6px; flex-shrink: 0; }
|
|
60
|
+
|
|
61
|
+
.detail-status-panel.tone-danger { background: var(--bad-soft); }
|
|
62
|
+
.detail-status-panel.tone-warning { background: var(--warn-soft); }
|
|
63
|
+
.detail-status-panel.tone-success { background: var(--ok-soft); }
|
|
64
|
+
.detail-status-panel.tone-info { background: var(--info-soft); }
|
|
65
|
+
.detail-status-panel.tone-primary { background: var(--accent-soft); }
|
|
66
|
+
.detail-status-panel.tone-muted { background: var(--surface2); }
|
|
67
|
+
.detail-status-panel.tone-danger .detail-status-state { color: var(--bad); }
|
|
68
|
+
.detail-status-panel.tone-warning .detail-status-state { color: var(--warn); }
|
|
69
|
+
.detail-status-panel.tone-success .detail-status-state { color: var(--ok); }
|
|
70
|
+
.detail-status-panel.tone-info .detail-status-state { color: var(--info); }
|
|
71
|
+
.detail-status-panel.tone-primary .detail-status-state { color: var(--accent); }
|
|
72
|
+
.detail-status-panel.tone-muted .detail-status-state { color: var(--mute); }
|
|
73
|
+
|
|
74
|
+
/* ============================================================ FlowStrip */
|
|
75
|
+
/* Borderless tinted surface, hairlines between steps. Column count rides
|
|
76
|
+
--flow-strip-cols (set inline from steps.length); container queries reflow
|
|
77
|
+
4 → 2 → 1 off the strip's own width. */
|
|
78
|
+
.detail-flow-strip {
|
|
79
|
+
container-type: inline-size;
|
|
80
|
+
display: grid;
|
|
81
|
+
grid-template-columns: repeat(var(--flow-strip-cols, 4), 1fr);
|
|
82
|
+
background: var(--surface2);
|
|
83
|
+
border: 0; border-radius: 12px; overflow: hidden;
|
|
84
|
+
}
|
|
85
|
+
.detail-flow-strip .flow-strip-step { position: relative; padding: 14px 16px; min-width: 0; background: transparent; }
|
|
86
|
+
.detail-flow-strip .flow-strip-step + .flow-strip-step { border-left: 1px solid var(--line-soft); }
|
|
87
|
+
/* Per-step tone: a 2px cap over the step (muted = no cap). */
|
|
88
|
+
.detail-flow-strip .flow-strip-step::before {
|
|
89
|
+
content: ''; position: absolute; left: 0; right: 0; top: 0; height: 2px; background: transparent;
|
|
90
|
+
}
|
|
91
|
+
.detail-flow-strip .flow-strip-step.tone-danger::before { background: var(--bad); }
|
|
92
|
+
.detail-flow-strip .flow-strip-step.tone-warning::before { background: var(--warn); }
|
|
93
|
+
.detail-flow-strip .flow-strip-step.tone-success::before { background: var(--ok); }
|
|
94
|
+
.detail-flow-strip .flow-strip-step.tone-info::before { background: var(--info); }
|
|
95
|
+
.detail-flow-strip .flow-strip-step.tone-primary::before { background: var(--accent); }
|
|
96
|
+
.detail-flow-strip .flow-strip-num {
|
|
97
|
+
font-family: ui-monospace, "SF Mono", Menlo, monospace;
|
|
98
|
+
font-size: 10px; font-weight: 600; letter-spacing: 0.09em; color: var(--mute);
|
|
99
|
+
}
|
|
100
|
+
.detail-flow-strip .flow-strip-step.tone-danger .flow-strip-num { color: var(--bad); }
|
|
101
|
+
.detail-flow-strip .flow-strip-step.tone-warning .flow-strip-num { color: var(--warn); }
|
|
102
|
+
.detail-flow-strip .flow-strip-step.tone-success .flow-strip-num { color: var(--ok); }
|
|
103
|
+
.detail-flow-strip .flow-strip-step.tone-info .flow-strip-num { color: var(--info); }
|
|
104
|
+
.detail-flow-strip .flow-strip-step.tone-primary .flow-strip-num { color: var(--accent); }
|
|
105
|
+
.detail-flow-strip .flow-strip-title {
|
|
106
|
+
display: flex; align-items: center; justify-content: space-between; gap: 8px;
|
|
107
|
+
margin: 2px 0 5px; font-size: 13px; font-weight: 650; color: var(--ink-strong);
|
|
108
|
+
}
|
|
109
|
+
.detail-flow-strip .flow-strip-action { opacity: 0.5; }
|
|
110
|
+
.detail-flow-strip .flow-strip-step:hover .flow-strip-action { opacity: 1; }
|
|
111
|
+
.detail-flow-strip .flow-strip-value { font-size: 13px; font-weight: 550; line-height: 1.35; color: var(--ink); }
|
|
112
|
+
.detail-flow-strip .flow-strip-value code {
|
|
113
|
+
font-size: 12px; background: var(--surface); border-radius: 5px; padding: 1px 5px;
|
|
114
|
+
}
|
|
115
|
+
.detail-flow-strip .flow-strip-empty { color: var(--mute); font-style: italic; font-weight: 400; }
|
|
116
|
+
.detail-flow-strip .flow-strip-hint { font-size: 11.5px; color: var(--mute); margin-top: 4px; line-height: 1.4; }
|
|
117
|
+
@container (max-width: 760px) { .detail-flow-strip { grid-template-columns: 1fr 1fr; } }
|
|
118
|
+
@container (max-width: 460px) {
|
|
119
|
+
.detail-flow-strip { grid-template-columns: 1fr; }
|
|
120
|
+
.detail-flow-strip .flow-strip-step + .flow-strip-step { border-left: 0; border-top: 1px solid var(--line-soft); }
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/* ============================================================ Timeline */
|
|
124
|
+
.detail-timeline { list-style: none; margin: 0; padding: 0 0 0 18px; position: relative; }
|
|
125
|
+
.detail-timeline::before {
|
|
126
|
+
content: ''; position: absolute; left: 5px; top: 8px; bottom: 8px; width: 1px;
|
|
127
|
+
background: linear-gradient(180deg, var(--line), transparent);
|
|
128
|
+
}
|
|
129
|
+
.detail-timeline-item {
|
|
130
|
+
position: relative; padding: 6px 0 13px;
|
|
131
|
+
display: grid; grid-template-columns: 1fr auto; gap: 8px;
|
|
132
|
+
}
|
|
133
|
+
.detail-timeline-item::before {
|
|
134
|
+
content: ''; position: absolute; left: -18px; top: 12px;
|
|
135
|
+
width: 9px; height: 9px; border-radius: 999px;
|
|
136
|
+
background: var(--surface); border: 1.5px solid var(--mute);
|
|
137
|
+
}
|
|
138
|
+
.detail-timeline-item.tone-danger::before { border-color: var(--bad); }
|
|
139
|
+
.detail-timeline-item.tone-warning::before { border-color: var(--warn); }
|
|
140
|
+
.detail-timeline-item.tone-success::before { border-color: var(--ok); }
|
|
141
|
+
.detail-timeline-item.tone-info::before { border-color: var(--info); }
|
|
142
|
+
.detail-timeline-item.tone-primary::before { border-color: var(--accent); }
|
|
143
|
+
.detail-timeline-item.tone-muted::before { border-color: var(--mute); }
|
|
144
|
+
.detail-timeline-title { font-size: 13.5px; font-weight: 600; color: var(--ink-strong); }
|
|
145
|
+
.detail-timeline-body { font-size: 12.5px; color: var(--mute); margin-top: 2px; }
|
|
146
|
+
.detail-timeline-body code {
|
|
147
|
+
font-size: 12px; background: var(--surface2); border-radius: 5px; padding: 1px 5px; color: var(--ink);
|
|
148
|
+
}
|
|
149
|
+
.detail-timeline-meta {
|
|
150
|
+
font-family: ui-monospace, "SF Mono", Menlo, monospace;
|
|
151
|
+
font-size: 11px; color: var(--mute); font-variant-numeric: tabular-nums;
|
|
152
|
+
white-space: nowrap; align-self: start; padding-top: 2px;
|
|
153
|
+
}
|
|
154
|
+
.detail-timeline-empty { list-style: none; padding: 8px 0; font-size: 13px; }
|
|
155
|
+
|
|
156
|
+
/* ============================================================ KnownFieldsCard */
|
|
157
|
+
.detail-known-fields-card { display: block; }
|
|
158
|
+
.detail-known-fields-grid { margin-bottom: 4px; }
|
|
159
|
+
/* Reuses .flat-row (theme.css) — the 3rd (action) grid column stays empty. */
|
|
160
|
+
.detail-known-fields-card .flat-value code { font-size: 12px; }
|
|
161
|
+
.detail-known-fields-empty { font-size: 13px; padding: 6px 0; }
|
|
162
|
+
.detail-known-fields-raw {
|
|
163
|
+
margin-top: 14px; border: 1px solid var(--line); border-radius: 10px;
|
|
164
|
+
background: var(--surface2); overflow: hidden;
|
|
165
|
+
}
|
|
166
|
+
.detail-known-fields-raw-summary {
|
|
167
|
+
cursor: pointer; list-style: none; user-select: none;
|
|
168
|
+
display: flex; align-items: center; gap: 7px;
|
|
169
|
+
padding: 8px 13px; font-size: 12.5px; font-weight: 600; color: var(--mute);
|
|
170
|
+
}
|
|
171
|
+
.detail-known-fields-raw-summary::-webkit-details-marker { display: none; }
|
|
172
|
+
.detail-known-fields-raw-summary::before {
|
|
173
|
+
content: '\25B6'; /* ▶ */
|
|
174
|
+
display: inline-block; font-size: 9px; color: var(--mute); transition: transform 0.15s ease;
|
|
175
|
+
}
|
|
176
|
+
.detail-known-fields-raw[open] > .detail-known-fields-raw-summary::before { transform: rotate(90deg); }
|
|
177
|
+
.detail-known-fields-raw-summary:hover { color: var(--ink-strong); }
|
|
178
|
+
.detail-known-fields-raw-body {
|
|
179
|
+
margin: 0; padding: 11px 13px 13px; border-top: 1px solid var(--line-soft);
|
|
180
|
+
font-family: ui-monospace, "SF Mono", Menlo, monospace;
|
|
181
|
+
font-size: 12px; line-height: 1.5; color: var(--ink);
|
|
182
|
+
white-space: pre-wrap; word-break: break-word;
|
|
183
|
+
max-height: 28rem; overflow: auto; background: var(--surface);
|
|
184
|
+
}
|
|
185
|
+
@media (prefers-reduced-motion: reduce) {
|
|
186
|
+
.detail-known-fields-raw-summary::before { transition: none; }
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/* ============================================================ MetadataSection */
|
|
190
|
+
.admin-metadata-section { display: block; }
|
|
191
|
+
.amd-header { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 10px; }
|
|
192
|
+
.amd-title { margin: 0; font-size: 14px; font-weight: 700; color: var(--ink-strong); }
|
|
193
|
+
.amd-error {
|
|
194
|
+
display: flex; align-items: center; gap: 8px; margin-bottom: 10px;
|
|
195
|
+
background: var(--bad-soft); color: var(--bad);
|
|
196
|
+
border: 1px solid var(--bad); border-radius: 9px;
|
|
197
|
+
padding: 8px 12px; font-size: 13px; font-weight: 550;
|
|
198
|
+
}
|
|
199
|
+
.amd-list { border: 1px solid var(--line); border-radius: 10px; overflow: hidden; background: var(--surface); }
|
|
200
|
+
.amd-item { display: flex; align-items: center; gap: 12px; padding: 9px 13px; border-bottom: 1px solid var(--line-soft); }
|
|
201
|
+
.amd-item:last-child { border-bottom: none; }
|
|
202
|
+
.amd-editor { background: var(--surface2); }
|
|
203
|
+
.amd-key {
|
|
204
|
+
font-family: ui-monospace, "SF Mono", Menlo, monospace;
|
|
205
|
+
font-size: 12px; font-weight: 650; color: var(--ink-strong);
|
|
206
|
+
min-width: 140px; flex-shrink: 0; word-break: break-all;
|
|
207
|
+
}
|
|
208
|
+
.amd-value { flex: 1; min-width: 0; font-size: 13px; color: var(--ink); word-break: break-word; }
|
|
209
|
+
.amd-blob { font-size: 12px; color: var(--mute); background: var(--surface2); border-radius: 5px; padding: 1px 6px; }
|
|
210
|
+
.amd-actions { flex-shrink: 0; display: flex; align-items: center; gap: 4px; }
|
|
211
|
+
.amd-remove:hover:not(:disabled) { color: var(--bad); }
|
|
212
|
+
.amd-key-input { min-width: 140px; width: 140px; flex: none; font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: 12px; }
|
|
213
|
+
.amd-value-input { flex: 1; min-width: 0; width: auto; }
|
|
214
|
+
.amd-empty { padding: 26px 20px; text-align: center; color: var(--mute); font-size: 13px; }
|
|
215
|
+
.amd-empty i { font-size: 22px; display: block; margin-bottom: 7px; }
|
|
216
|
+
|
|
217
|
+
/* ============================================================ StackTraceView */
|
|
218
|
+
.stack-trace-view {
|
|
219
|
+
background: var(--surface2); border: 1px solid var(--line); border-radius: 10px; overflow: hidden;
|
|
220
|
+
}
|
|
221
|
+
.stack-trace-body { max-height: 600px; overflow: auto; padding: 8px 0; }
|
|
222
|
+
.stack-trace-line {
|
|
223
|
+
font-family: ui-monospace, "SF Mono", Menlo, "Ubuntu Mono", Consolas, monospace;
|
|
224
|
+
font-size: 12.5px; line-height: 1.6; padding: 2px 12px; margin: 0;
|
|
225
|
+
border-left: 3px solid transparent; white-space: pre-wrap; word-break: break-word;
|
|
226
|
+
}
|
|
227
|
+
.stack-trace-line:hover { background: var(--line-soft); }
|
|
228
|
+
.stack-trace-error {
|
|
229
|
+
color: var(--bad); font-weight: 650;
|
|
230
|
+
border-left-color: var(--bad); background: var(--bad-soft);
|
|
231
|
+
}
|
|
232
|
+
.stack-trace-error:hover { background: var(--bad-soft); filter: brightness(1.06); }
|
|
233
|
+
.stack-trace-frame { border-left-color: var(--accent); }
|
|
234
|
+
.stack-trace-fn { color: var(--accent); font-weight: 600; }
|
|
235
|
+
.stack-trace-loc { color: var(--mute); font-size: 12px; }
|
|
236
|
+
.stack-trace-lineno { color: var(--warn); font-weight: 650; }
|
|
237
|
+
.stack-trace-context { color: var(--ink); }
|
|
238
|
+
.stack-trace-empty { padding: 14px; font-size: 13px; }
|
|
239
|
+
.stack-trace-toggle {
|
|
240
|
+
display: flex; align-items: center; justify-content: center; gap: 7px;
|
|
241
|
+
width: 100%; padding: 8px 12px; border: 0; border-top: 1px solid var(--line);
|
|
242
|
+
background: var(--surface); color: var(--mute);
|
|
243
|
+
font-size: 12.5px; font-weight: 600; cursor: pointer;
|
|
244
|
+
}
|
|
245
|
+
.stack-trace-toggle:hover { color: var(--ink-strong); background: var(--surface2); }
|
|
246
|
+
.stack-trace-toggle i { font-size: 11px; }
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/* ============================================================ DetailView C1
|
|
2
|
+
Chrome for the C1 additions ONLY — rail badges, the header kebab menu,
|
|
3
|
+
and the keep-alive wrappers. The base .detail / .detail-rail / .rail-item
|
|
4
|
+
styles stay in theme.css. Tokens only; both themes come from the token
|
|
5
|
+
flip. NOT imported by theme.css yet — the orchestrator adds the @import
|
|
6
|
+
at merge time. */
|
|
7
|
+
|
|
8
|
+
/* Rail badges — a placement-only wrapper pinned to the rail item's right
|
|
9
|
+
edge. Primitive badge values arrive pre-wrapped in .chip.chip-muted
|
|
10
|
+
(web-mojo's muted default); rich nodes style themselves. */
|
|
11
|
+
.rail-badge { margin-left: auto; display: inline-flex; align-items: center; flex: none; }
|
|
12
|
+
.rail-badge .chip { font-size: 10px; padding: 1px 7px; font-weight: 650; }
|
|
13
|
+
/* The dot idiom — attention without a count. */
|
|
14
|
+
.rail-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--accent); }
|
|
15
|
+
|
|
16
|
+
/* Header kebab — .btn-icon carries the base look; hold the hover state
|
|
17
|
+
while the menu is open so the trigger reads as "on". */
|
|
18
|
+
.dv-kebab[aria-expanded="true"] { background: var(--surface2); color: var(--ink-strong); }
|
|
19
|
+
|
|
20
|
+
/* Kebab menu (inside the unpadded .mojo-popover shell — content brings its
|
|
21
|
+
own padding/width, per the Popover contract). */
|
|
22
|
+
.dv-menu { padding: 5px; min-width: 200px; max-height: calc(100dvh - 24px); overflow-y: auto; }
|
|
23
|
+
.dv-menu-item {
|
|
24
|
+
display: flex; align-items: center; gap: 9px; width: 100%;
|
|
25
|
+
padding: 7px 9px; border: none; background: none; border-radius: 7px;
|
|
26
|
+
text-align: left; cursor: pointer;
|
|
27
|
+
font-size: 13.5px; font-weight: 550; color: var(--ink);
|
|
28
|
+
}
|
|
29
|
+
.dv-menu-item:hover { background: var(--surface2); color: var(--ink-strong); }
|
|
30
|
+
.dv-menu-item i { color: var(--mute); font-size: 13.5px; width: 16px; text-align: center; flex: none; }
|
|
31
|
+
.dv-menu-item.dv-danger, .dv-menu-item.dv-danger i { color: var(--bad); }
|
|
32
|
+
.dv-menu-item.dv-danger:hover { background: var(--bad-soft); }
|
|
33
|
+
.dv-menu-sep { height: 1px; background: var(--line); margin: 5px 0; }
|
|
34
|
+
|
|
35
|
+
/* Keep-alive wrappers: inactive sections stay mounted but hidden. The
|
|
36
|
+
[hidden] guard is explicit + !important so no future display rule on
|
|
37
|
+
.dv-keep can accidentally resurrect a hidden section's pixels. */
|
|
38
|
+
.dv-keep[hidden] { display: none !important; }
|
|
39
|
+
|
|
40
|
+
/* Responsive detail navigation uses the host width, including embedded views. */
|
|
41
|
+
.detail-section-toggle { display: none; }
|
|
42
|
+
.detail-title, .detail-sub { overflow-wrap: anywhere; }
|
|
43
|
+
.detail-gutter { flex: none; }
|
|
44
|
+
.dv-menu-item:disabled { opacity: .5; cursor: default; }
|
|
45
|
+
@container (max-width: 560px) {
|
|
46
|
+
.detail .detail-header { padding: 12px; gap: 9px; }
|
|
47
|
+
.detail .detail-avatar { width: 34px; height: 34px; flex: none; }
|
|
48
|
+
.detail .detail-title { font-size: 16px; }
|
|
49
|
+
.detail .detail-sub { font-size: 12px; gap: 4px; }
|
|
50
|
+
.detail .detail-gutter { gap: 4px; }
|
|
51
|
+
.detail .detail-body { flex-direction: column; }
|
|
52
|
+
.detail .detail-section-toggle { display: flex; align-items: center; gap: 8px; flex: none; width: 100%; padding: 10px 14px; background: var(--surface2); color: var(--ink); border: 0; border-bottom: 1px solid var(--line); text-align: left; }
|
|
53
|
+
.detail .detail-rail { display: none; width: auto; flex: none; max-height: 32%; overflow-y: auto; border-right: 0; border-bottom: 1px solid var(--line); }
|
|
54
|
+
.detail .detail-rail.detail-rail-expanded { display: block; }
|
|
55
|
+
.detail .detail-content { padding: 14px; }
|
|
56
|
+
}
|
|
57
|
+
/* Dialog-only constraints never cap page or embedded DetailView hosts. */
|
|
58
|
+
.mojo-modal.mojo-modal { max-height: calc(100dvh - 24px); }
|
|
59
|
+
.mojo-modal .modal-pad { overflow-wrap: anywhere; }
|
|
60
|
+
.mojo-modal .modal-title { position: sticky; top: -20px; background: var(--surface); z-index: 1; padding-block: 5px; }
|
|
61
|
+
.mojo-modal .modal-actions { position: sticky; bottom: -22px; background: var(--surface); padding-block: 12px; flex-wrap: wrap; z-index: 1; }
|
|
62
|
+
@media (max-width: 560px) {
|
|
63
|
+
.mojo-modal .modal-pad { padding: 16px; }
|
|
64
|
+
.mojo-modal .modal-title { top: -16px; }
|
|
65
|
+
.mojo-modal .modal-actions { bottom: -16px; }
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.modal-fallback-close { position: absolute; top: 12px; right: 12px; z-index: 2; }
|
|
69
|
+
.mojo-modal:has(.detail-gutter [aria-label="Close"]) > .modal-fallback-close { display: none; }
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/* ============================================================
|
|
2
|
+
Drawer — the right slide-over variant of the native-<dialog>
|
|
3
|
+
modal manager (packages/portal-mojo/src/ui/modal.tsx).
|
|
4
|
+
|
|
5
|
+
TRANSIENT surface: modal, top-layer, awaitable, one at a time
|
|
6
|
+
per call. NOT the persistent shell right-panel slot (tickets /
|
|
7
|
+
assistant) — that is a non-modal layout region, a different
|
|
8
|
+
thing entirely.
|
|
9
|
+
|
|
10
|
+
Tokens only, both themes. Header shape (eyebrow · title · meta)
|
|
11
|
+
is web-mojo's Modal.drawer() header restyled onto mojo tokens.
|
|
12
|
+
============================================================ */
|
|
13
|
+
|
|
14
|
+
.mojo-drawer {
|
|
15
|
+
/* The UA gives dialog:modal `inset:0; margin:auto` plus
|
|
16
|
+
max-width/max-height: calc(100% - 6px - 2em). All four have to be
|
|
17
|
+
overridden to pin the panel right and let it run full-bleed. */
|
|
18
|
+
margin: 0 0 0 auto;
|
|
19
|
+
/* % (not vw) — the dialog is fixed-positioned, so % excludes the page
|
|
20
|
+
scrollbar; 100vw would push the panel that far off the right edge. */
|
|
21
|
+
width: min(var(--drawer-w, 480px), 100%);
|
|
22
|
+
max-width: 100%;
|
|
23
|
+
height: 100%;
|
|
24
|
+
height: 100dvh; /* dvh keeps the panel honest under mobile browser chrome */
|
|
25
|
+
max-height: 100%;
|
|
26
|
+
max-height: 100dvh;
|
|
27
|
+
padding: 0;
|
|
28
|
+
border: none;
|
|
29
|
+
border-left: 1px solid var(--line);
|
|
30
|
+
border-radius: 0;
|
|
31
|
+
background: var(--surface);
|
|
32
|
+
color: var(--ink);
|
|
33
|
+
box-shadow: var(--shadow-pop);
|
|
34
|
+
overflow: hidden;
|
|
35
|
+
/* Keyframes, not a transition: a <dialog> is display:none until
|
|
36
|
+
showModal(), so there is no "from" state for a transition to run. */
|
|
37
|
+
animation: drawer-in 220ms cubic-bezier(0.32, 0.72, 0, 1);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.mojo-drawer::backdrop {
|
|
41
|
+
background: rgba(6, 9, 14, 0.55);
|
|
42
|
+
backdrop-filter: blur(2px);
|
|
43
|
+
animation: drawer-backdrop-in 220ms ease-out;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/* Exit: the manager adds this class, waits DRAWER_EXIT_MS, and only then
|
|
47
|
+
drops the item (unmount → dialog.close()) — so the slide-out plays. */
|
|
48
|
+
.mojo-drawer-closing {
|
|
49
|
+
animation: drawer-out 200ms cubic-bezier(0.32, 0.72, 0, 1) forwards;
|
|
50
|
+
pointer-events: none; /* a panel on its way out is not a click target */
|
|
51
|
+
}
|
|
52
|
+
.mojo-drawer-closing::backdrop { animation: drawer-backdrop-out 200ms ease-in forwards; }
|
|
53
|
+
|
|
54
|
+
@keyframes drawer-in { from { transform: translateX(100%); } }
|
|
55
|
+
@keyframes drawer-out { to { transform: translateX(100%); } }
|
|
56
|
+
@keyframes drawer-backdrop-in { from { opacity: 0; } }
|
|
57
|
+
@keyframes drawer-backdrop-out { to { opacity: 0; } }
|
|
58
|
+
|
|
59
|
+
/* The panel fills the dialog so the dialog element itself is only ever the
|
|
60
|
+
event target for ::backdrop clicks — which is how dismiss-on-backdrop
|
|
61
|
+
distinguishes "outside" from "inside". */
|
|
62
|
+
.drawer-panel {
|
|
63
|
+
display: flex;
|
|
64
|
+
flex-direction: column;
|
|
65
|
+
height: 100%;
|
|
66
|
+
min-height: 0;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.drawer-head {
|
|
70
|
+
display: flex; align-items: flex-start; gap: 12px;
|
|
71
|
+
flex: 0 0 auto;
|
|
72
|
+
padding: 16px 18px 13px;
|
|
73
|
+
border-bottom: 1px solid var(--line);
|
|
74
|
+
}
|
|
75
|
+
.drawer-head-text { flex: 1; min-width: 0; }
|
|
76
|
+
.drawer-eyebrow {
|
|
77
|
+
display: block;
|
|
78
|
+
font-family: ui-monospace, "SF Mono", Menlo, monospace;
|
|
79
|
+
font-size: 10.5px; letter-spacing: 0.12em; text-transform: uppercase;
|
|
80
|
+
color: var(--mute); margin-bottom: 3px;
|
|
81
|
+
}
|
|
82
|
+
.drawer-title {
|
|
83
|
+
margin: 0;
|
|
84
|
+
font-size: 17px; font-weight: 750; line-height: 1.25; letter-spacing: -0.015em;
|
|
85
|
+
color: var(--ink-strong);
|
|
86
|
+
overflow-wrap: anywhere;
|
|
87
|
+
}
|
|
88
|
+
.drawer-meta {
|
|
89
|
+
display: flex; flex-wrap: wrap; gap: 3px 14px;
|
|
90
|
+
margin-top: 6px;
|
|
91
|
+
font-size: 12px; color: var(--mute);
|
|
92
|
+
}
|
|
93
|
+
.drawer-meta span { display: inline-flex; align-items: center; gap: 5px; }
|
|
94
|
+
.drawer-meta i { font-size: 11px; }
|
|
95
|
+
|
|
96
|
+
.drawer-body {
|
|
97
|
+
flex: 1; min-height: 0;
|
|
98
|
+
overflow: auto;
|
|
99
|
+
padding: 16px 18px 20px;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/* Full-bleed on phones — the min() clamp already caps the width; this drops
|
|
103
|
+
the seam so the panel reads as a page rather than a sliver. */
|
|
104
|
+
@media (max-width: 560px) {
|
|
105
|
+
.mojo-drawer { width: 100%; border-left: none; }
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
@media (prefers-reduced-motion: reduce) {
|
|
109
|
+
.mojo-drawer, .mojo-drawer::backdrop,
|
|
110
|
+
.mojo-drawer-closing, .mojo-drawer-closing::backdrop { animation: none; }
|
|
111
|
+
}
|