@quillmark/svelte 0.2.0 → 0.3.1
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/NOTICE +6 -6
- package/README.md +3 -3
- package/THEMING.md +12 -8
- package/dist/core/address.d.ts +7 -7
- package/dist/core/address.js +6 -6
- package/dist/core/bloom.d.ts +3 -32
- package/dist/core/bloom.js +19 -51
- package/dist/core/codec/anchor.d.ts +1 -1
- package/dist/core/codec/anchor.js +1 -1
- package/dist/core/codec/atoms.d.ts +10 -0
- package/dist/core/codec/atoms.js +78 -0
- package/dist/core/codec/clipboard.d.ts +59 -0
- package/dist/core/codec/clipboard.js +202 -0
- package/dist/core/codec/code.d.ts +1 -1
- package/dist/core/codec/code.js +4 -4
- package/dist/core/codec/decode.d.ts +1 -1
- package/dist/core/codec/decode.js +8 -8
- package/dist/core/codec/encode.d.ts +4 -4
- package/dist/core/codec/encode.js +10 -10
- package/dist/core/codec/field.d.ts +21 -26
- package/dist/core/codec/field.js +33 -32
- package/dist/core/codec/index.js +5 -8
- package/dist/core/codec/inputrules.js +56 -18
- package/dist/core/codec/islands.d.ts +11 -7
- package/dist/core/codec/islands.js +16 -10
- package/dist/core/codec/keymap.d.ts +5 -6
- package/dist/core/codec/keymap.js +8 -7
- package/dist/core/codec/lists.d.ts +3 -3
- package/dist/core/codec/lists.js +32 -13
- package/dist/core/codec/marks.d.ts +1 -1
- package/dist/core/codec/marks.js +1 -1
- package/dist/core/codec/positions.js +1 -1
- package/dist/core/codec/prose.css +235 -221
- package/dist/core/codec/schema.js +4 -4
- package/dist/core/codec/slash.d.ts +39 -41
- package/dist/core/codec/slash.js +0 -0
- package/dist/core/codec/table-view.d.ts +12 -16
- package/dist/core/codec/table-view.js +488 -292
- package/dist/core/codec/table.d.ts +42 -15
- package/dist/core/codec/table.js +70 -26
- package/dist/core/errors.d.ts +5 -5
- package/dist/core/index.js +2 -2
- package/dist/core/lifecycle.js +1 -1
- package/dist/core/rebind.svelte.d.ts +2 -2
- package/dist/core/rebind.svelte.js +4 -4
- package/dist/core/teardown.d.ts +1 -1
- package/dist/core/teardown.js +1 -1
- package/dist/core/theme.css +170 -64
- package/dist/preset/index.css +1 -1
- package/dist/preset/recipes.css +31 -29
- package/dist/preset/scale.css +37 -26
- package/dist/preview/Preview.svelte +17 -27
- package/dist/preview/Preview.svelte.d.ts +4 -5
- package/dist/preview/bridge.js +6 -6
- package/dist/preview/controller.d.ts +8 -10
- package/dist/preview/controller.js +19 -44
- package/dist/preview/geometry.d.ts +8 -10
- package/dist/preview/geometry.js +8 -10
- package/dist/preview/index.js +4 -4
- package/dist/preview/paint.d.ts +1 -1
- package/dist/preview/paint.js +17 -19
- package/dist/visual/ArrayField.svelte +80 -66
- package/dist/visual/ArrayField.svelte.d.ts +1 -1
- package/dist/visual/BooleanField.svelte +8 -6
- package/dist/visual/BooleanField.svelte.d.ts +2 -2
- package/dist/visual/Card.svelte +160 -329
- package/dist/visual/Card.svelte.d.ts +2 -7
- package/dist/visual/CardControls.svelte +1 -1
- package/dist/visual/DateField.svelte +19 -19
- package/dist/visual/DateField.svelte.d.ts +3 -3
- package/dist/visual/DiagnosticList.svelte +1 -1
- package/dist/visual/EnumField.svelte +16 -16
- package/dist/visual/EnumField.svelte.d.ts +2 -2
- package/dist/visual/Field.svelte +128 -115
- package/dist/visual/Field.svelte.d.ts +1 -1
- package/dist/visual/FieldHint.svelte +24 -42
- package/dist/visual/FieldHint.svelte.d.ts +1 -1
- package/dist/visual/FieldLabel.svelte +8 -8
- package/dist/visual/FormatPopover.svelte +80 -35
- package/dist/visual/NumberField.svelte +5 -5
- package/dist/visual/NumberField.svelte.d.ts +1 -1
- package/dist/visual/ObjectField.svelte +9 -9
- package/dist/visual/ObjectField.svelte.d.ts +1 -1
- package/dist/visual/ProseArrayElement.svelte +11 -13
- package/dist/visual/ProseArrayElement.svelte.d.ts +1 -1
- package/dist/visual/ProseField.svelte +53 -87
- package/dist/visual/ProseField.svelte.d.ts +13 -19
- package/dist/visual/SlashMenu.svelte +42 -16
- package/dist/visual/TextField.svelte +5 -5
- package/dist/visual/TextField.svelte.d.ts +1 -1
- package/dist/visual/TipsCard.svelte +68 -56
- package/dist/visual/VisualEditor.svelte +6 -6
- package/dist/visual/VisualEditorInner.svelte +203 -290
- package/dist/visual/controls.css +74 -30
- package/dist/visual/diagnostics.d.ts +5 -5
- package/dist/visual/diagnostics.js +3 -3
- package/dist/visual/domid.js +3 -3
- package/dist/visual/ext.d.ts +1 -1
- package/dist/visual/ext.js +1 -1
- package/dist/visual/icons/nodes.d.ts +19 -0
- package/dist/visual/icons/nodes.js +13 -1
- package/dist/visual/index.js +4 -4
- package/dist/visual/leaves.d.ts +3 -3
- package/dist/visual/leaves.js +1 -1
- package/dist/visual/links.d.ts +24 -0
- package/dist/visual/links.js +67 -0
- package/dist/visual/props.d.ts +8 -8
- package/dist/visual/signals.d.ts +6 -6
- package/dist/visual/strings.d.ts +14 -19
- package/dist/visual/strings.js +5 -7
- package/dist/visual/structure.d.ts +24 -17
- package/dist/visual/structure.js +8 -8
- package/dist/visual/synced.svelte.d.ts +1 -1
- package/dist/visual/synced.svelte.js +4 -4
- package/dist/visual/tips.d.ts +2 -2
- package/dist/visual/tips.js +3 -3
- package/package.json +2 -2
- package/dist/preview/overlay.d.ts +0 -11
- package/dist/preview/overlay.js +0 -97
package/dist/visual/Card.svelte
CHANGED
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
<!--
|
|
2
|
-
One card block
|
|
3
|
-
a
|
|
4
|
-
|
|
5
|
-
the body prose leaf. `main` is headerless with no controls. Every prose leaf
|
|
6
|
-
takes a parent-built LIVE address so a card reorder re-targets its commits
|
|
7
|
-
without a remount.
|
|
2
|
+
One card block, its fields grouped by `ui.group`/`ui.compact`. `main` is headerless
|
|
3
|
+
and takes no controls. Every prose leaf takes a parent-built live address, so a card
|
|
4
|
+
reorder re-targets its commits without a remount.
|
|
8
5
|
-->
|
|
9
6
|
<script lang="ts">
|
|
10
7
|
import { wording } from './strings.js';
|
|
11
8
|
|
|
12
|
-
//
|
|
13
|
-
//
|
|
9
|
+
// Ambient from the editor root, with the package's English off-tree, so this
|
|
10
|
+
// component renders standalone too.
|
|
14
11
|
const t = wording();
|
|
15
12
|
import { untrack } from 'svelte';
|
|
16
13
|
import Icon from './icons/Icon.svelte';
|
|
@@ -44,30 +41,24 @@
|
|
|
44
41
|
interface Props {
|
|
45
42
|
card: CardModel;
|
|
46
43
|
doc: Document;
|
|
47
|
-
/** The schema a prose leaf reads its content through (`ProseField`). */
|
|
48
44
|
quill: Quill;
|
|
49
|
-
index: number;
|
|
50
45
|
isFirst: boolean;
|
|
51
46
|
isLast: boolean;
|
|
52
47
|
kinds: string[];
|
|
53
48
|
ops: CardOps;
|
|
54
|
-
/** How an option `ops.enumAllowed` refuses draws. Nothing about it is per-card,
|
|
55
|
-
* so it sits beside the bundle rather than in it. */
|
|
56
49
|
enumDisallowed?: 'hide' | 'disable';
|
|
57
50
|
onFocus?: (addr: Addr) => void;
|
|
58
51
|
onCaretMove?: (addr: Addr, pos: number) => void;
|
|
59
52
|
onChange?: (addr: Addr) => void;
|
|
60
53
|
onError?: EditorErrorHandler;
|
|
61
|
-
/**
|
|
62
|
-
*
|
|
63
|
-
* resolve a `DocPath` to. */
|
|
54
|
+
/** Every field in this card registers its landing handle here, which is what the
|
|
55
|
+
* editor's `focusField`/`setCaret` resolve a `DocPath` to. */
|
|
64
56
|
leaves?: LeafRegistry;
|
|
65
57
|
}
|
|
66
58
|
let {
|
|
67
59
|
card,
|
|
68
60
|
doc,
|
|
69
61
|
quill,
|
|
70
|
-
index,
|
|
71
62
|
isFirst,
|
|
72
63
|
isLast,
|
|
73
64
|
kinds,
|
|
@@ -90,18 +81,15 @@
|
|
|
90
81
|
});
|
|
91
82
|
});
|
|
92
83
|
|
|
93
|
-
//
|
|
94
|
-
// text you replace, and Enter/Escape as commit/revert. Rename stays LIVE on
|
|
95
|
-
// input: `titleAtFocus` is the pre-edit value Escape rolls back to.
|
|
84
|
+
// The pre-edit value Escape rolls back to; the rename itself stays live on input.
|
|
96
85
|
let titleAtFocus = '';
|
|
97
86
|
function onTitleFocus(e: FocusEvent): void {
|
|
98
87
|
titleAtFocus = localTitle;
|
|
99
88
|
(e.currentTarget as HTMLInputElement).select();
|
|
100
89
|
}
|
|
101
|
-
// A
|
|
102
|
-
//
|
|
103
|
-
//
|
|
104
|
-
// clicking mid-title to place the caret still works.
|
|
90
|
+
// A press on an unfocused title would place a caret on mouseup, collapsing the
|
|
91
|
+
// focus-time select-all. A press while already focused stays normal, so clicking
|
|
92
|
+
// mid-title to place the caret still works.
|
|
105
93
|
function onTitleMousedown(e: MouseEvent): void {
|
|
106
94
|
const el = e.currentTarget as HTMLInputElement;
|
|
107
95
|
if (document.activeElement !== el) {
|
|
@@ -109,11 +97,8 @@
|
|
|
109
97
|
el.focus();
|
|
110
98
|
}
|
|
111
99
|
}
|
|
112
|
-
//
|
|
113
|
-
//
|
|
114
|
-
// same click-to-enter the title itself gets) and preventDefault keeps the press
|
|
115
|
-
// from starting a text selection on the wrapper. A press ON the input is left to
|
|
116
|
-
// `onTitleMousedown`, which owns the already-focused caret-placement case.
|
|
100
|
+
// A press on the region's empty width enters the title edit, and starts no text
|
|
101
|
+
// selection on the wrapper. A press on the input itself is `onTitleMousedown`'s.
|
|
117
102
|
function onRenameMousedown(e: MouseEvent): void {
|
|
118
103
|
const input = (e.currentTarget as HTMLElement).querySelector('input');
|
|
119
104
|
if (!input || e.target === input) return;
|
|
@@ -135,36 +120,32 @@
|
|
|
135
120
|
}
|
|
136
121
|
}
|
|
137
122
|
|
|
138
|
-
|
|
139
|
-
|
|
123
|
+
/** Not `--_qm-glyph-control`: that rung centres a glyph in a tap target, where this
|
|
124
|
+
* one stands in a run of text at the body rung and is sized to sit level with it. */
|
|
125
|
+
const CHEVRON = 16;
|
|
126
|
+
|
|
140
127
|
const ungrouped = $derived(card.sections.filter((s) => s.group == null));
|
|
141
128
|
const grouped = $derived(card.sections.filter((s) => s.group != null));
|
|
142
129
|
|
|
143
|
-
//
|
|
144
|
-
//
|
|
145
|
-
//
|
|
146
|
-
// horizontals land on one y and paint as a stroke of twice the width, which is the
|
|
147
|
-
// one thing a figure claiming one width cannot do.
|
|
130
|
+
// A schema that declares no fields still gets a body, and a bracket around nothing is
|
|
131
|
+
// worse than none: with no content between them the two horizontals land on one y and
|
|
132
|
+
// paint as a single stroke of twice the width.
|
|
148
133
|
const hasMeta = $derived(ungrouped.length > 0 || grouped.length > 0);
|
|
149
134
|
|
|
150
|
-
//
|
|
151
|
-
//
|
|
152
|
-
//
|
|
153
|
-
//
|
|
154
|
-
// a retype is the one reshape, and the open group stays where it still exists.
|
|
135
|
+
// Seeded once from the card's shape. Card is keyed by stable id, so this survives the
|
|
136
|
+
// VisualEditor re-derive that reassigns `card` and resets only on a remount. Not
|
|
137
|
+
// reconciled to later section changes: a retype is the one reshape, and the open
|
|
138
|
+
// group stays where it still exists.
|
|
155
139
|
// svelte-ignore state_referenced_locally
|
|
156
140
|
let expanded = $state<string | null>(initialExpandedGroup(card.sections));
|
|
157
|
-
// The accordion's two elements per group, by group id. A closing panel goes
|
|
158
|
-
// `inert` with whatever it held still focused, and `inert` does not say where
|
|
159
|
-
// focus should go: the browser drops it on the body, losing the user's place in
|
|
160
|
-
// the card. So the close hands focus to the header, which is what reopens the
|
|
161
|
-
// section.
|
|
162
141
|
let headers = $state<Record<string, HTMLButtonElement | undefined>>({});
|
|
163
142
|
let panels = $state<Record<string, HTMLElement | undefined>>({});
|
|
164
143
|
/**
|
|
165
|
-
* Move to `next` (`null` = all closed)
|
|
166
|
-
*
|
|
167
|
-
*
|
|
144
|
+
* Move to `next` (`null` = all closed). A closing panel goes `inert` with whatever it
|
|
145
|
+
* held still focused, and `inert` does not say where focus should go — the browser
|
|
146
|
+
* drops it on the body — so the close hands focus to the header, which is what
|
|
147
|
+
* reopens the section. Every write to `expanded` goes through here: a group also
|
|
148
|
+
* closes when another opens.
|
|
168
149
|
*/
|
|
169
150
|
function setExpanded(next: string | null): void {
|
|
170
151
|
const closing = expanded;
|
|
@@ -177,13 +158,10 @@
|
|
|
177
158
|
}
|
|
178
159
|
|
|
179
160
|
/**
|
|
180
|
-
* Open the group holding leaf `key
|
|
181
|
-
*
|
|
182
|
-
*
|
|
183
|
-
* `
|
|
184
|
-
* landing: `inert` clears when the panel renders open, not when `expanded` is
|
|
185
|
-
* assigned. The card keeps owning `expanded`, and a card that does not hold the
|
|
186
|
-
* key does nothing.
|
|
161
|
+
* Open the group holding leaf `key`: a caret placed in a collapsed panel does not
|
|
162
|
+
* land at all. `VisualEditor.setCaret` calls this on every card and then waits a
|
|
163
|
+
* flush before landing, since `inert` clears when the panel renders open, not when
|
|
164
|
+
* `expanded` is assigned.
|
|
187
165
|
*
|
|
188
166
|
* A call, not a prop: a reveal is an event, and modelling it as state needs a
|
|
189
167
|
* fresh-identity wrapper to re-fire and an `untrack` to keep the per-keystroke
|
|
@@ -196,13 +174,10 @@
|
|
|
196
174
|
|
|
197
175
|
let el = $state<HTMLElement | undefined>(undefined);
|
|
198
176
|
/**
|
|
199
|
-
* Bring this card into view after the structure mutation that placed it:
|
|
200
|
-
*
|
|
201
|
-
*
|
|
202
|
-
*
|
|
203
|
-
*
|
|
204
|
-
* The reduced-motion guard is JS rather than the CSS the accordion uses: a
|
|
205
|
-
* scroll's behaviour is an argument, not a transition a media query can cancel.
|
|
177
|
+
* Bring this card into view after the structure mutation that placed it: `center` for
|
|
178
|
+
* an insert, `nearest` for a reorder. The reduced-motion guard is JS rather than the
|
|
179
|
+
* CSS the accordion uses: a scroll's behaviour is an argument, not a transition a
|
|
180
|
+
* media query can cancel.
|
|
206
181
|
*/
|
|
207
182
|
export function scrollIntoViewCard(block: ScrollLogicalPosition): void {
|
|
208
183
|
const reduce = window.matchMedia?.('(prefers-reduced-motion: reduce)').matches;
|
|
@@ -210,9 +185,6 @@
|
|
|
210
185
|
}
|
|
211
186
|
</script>
|
|
212
187
|
|
|
213
|
-
<!-- `qm-main` is a structural marker and carries no fill: a tone here would bet on
|
|
214
|
-
what the consumer put behind the column, and structure already says which card
|
|
215
|
-
this is. -->
|
|
216
188
|
<section
|
|
217
189
|
bind:this={el}
|
|
218
190
|
class="qm-card"
|
|
@@ -224,19 +196,9 @@
|
|
|
224
196
|
{:else}
|
|
225
197
|
{#if !card.isMain}
|
|
226
198
|
<header class="qm-card-header">
|
|
227
|
-
<!-- The rename target is the header's whole free width, not the title's text
|
|
228
|
-
box. The autosize keeps that box exactly as wide as its
|
|
229
|
-
text; this wrapper makes the rest of the row enter the edit too. -->
|
|
230
199
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
231
200
|
<div class="qm-card-rename" onmousedown={onRenameMousedown}>
|
|
232
|
-
<!-- Autosize: the sizer span's ::after mirrors the text and dictates the grid
|
|
233
|
-
cell width, so the overlaid input grows with content and reads as text,
|
|
234
|
-
not a persistent box. `data-value` falls back to the
|
|
235
|
-
placeholder so an empty title still reserves its resolved-title width. -->
|
|
236
201
|
<span class="qm-card-title-sizer" data-value={localTitle || card.titlePlaceholder}>
|
|
237
|
-
<!-- `qm-focus-ring` is this control's ONLY focus indicator: the ink step
|
|
238
|
-
cannot be one, since hover already takes that step. `:focus-visible`, so
|
|
239
|
-
a press enters the edit unringed while a tab into it draws one. -->
|
|
240
202
|
<input
|
|
241
203
|
class="qm-card-title qm-focus-ring"
|
|
242
204
|
value={localTitle}
|
|
@@ -266,45 +228,25 @@
|
|
|
266
228
|
{/if}
|
|
267
229
|
|
|
268
230
|
<div class="qm-card-body">
|
|
269
|
-
<!--
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
section's vertical actually reach them: a rule drawn in the card body's
|
|
274
|
-
gap instead would stand clear of where that vertical stops, and the corner
|
|
275
|
-
it exists to close would miss. It also survives the shapes the accordion
|
|
276
|
-
does not have: a card with no groups still terminates above its body,
|
|
277
|
-
because the rule belongs to the fields, not to the sections.
|
|
278
|
-
|
|
279
|
-
Rendered only when there ARE fields: the bracket is a statement about a
|
|
280
|
-
block, so with no block there is nothing to say, and an empty wrapper
|
|
281
|
-
between two rules is what collapses them onto each other. -->
|
|
231
|
+
<!-- One element whose top and bottom edges are the bracket's two horizontals.
|
|
232
|
+
Anchored here rather than on the header and on the accordion so an open section's
|
|
233
|
+
vertical reaches them, and so a card with no groups still terminates above its
|
|
234
|
+
body. -->
|
|
282
235
|
{#if hasMeta}
|
|
283
236
|
<div
|
|
284
237
|
class="qm-card-meta"
|
|
285
238
|
class:qm-meta-top={!card.isMain}
|
|
286
239
|
class:qm-meta-bottom={card.hasBody}
|
|
287
240
|
>
|
|
288
|
-
<!-- Ungrouped fields render above the accordion, always visible:
|
|
289
|
-
a label-less section has no header to toggle, and these read as the card's
|
|
290
|
-
primary fields. -->
|
|
291
241
|
{#each ungrouped as section (section.group ?? '_ungrouped')}
|
|
292
242
|
<div class="qm-section">
|
|
293
243
|
{@render sectionFields(section.fields)}
|
|
294
244
|
</div>
|
|
295
245
|
{/each}
|
|
296
246
|
|
|
297
|
-
<!--
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
with no gap of its own: each header's symmetric padding is the whole
|
|
301
|
-
inter-group rhythm. The card body's gap still separates this
|
|
302
|
-
block from the ungrouped fields and the body leaf.
|
|
303
|
-
|
|
304
|
-
Rendered only when a card HAS groups, so an empty accordion is not the
|
|
305
|
-
bracket's last child: the bottom rule's inset belongs to whichever block
|
|
306
|
-
actually abuts it, and a zero-height wrapper standing there would take it
|
|
307
|
-
from the ungrouped fields that do. -->
|
|
247
|
+
<!-- One open at a time. Rendered only when a card has groups, so an empty
|
|
248
|
+
accordion is not the bracket's last child: the bottom rule's inset belongs
|
|
249
|
+
to whichever block abuts it. -->
|
|
308
250
|
{#if grouped.length}
|
|
309
251
|
<div class="qm-groups">
|
|
310
252
|
{#each grouped as section (section.group)}
|
|
@@ -320,13 +262,15 @@
|
|
|
320
262
|
aria-controls={panelId}
|
|
321
263
|
onclick={() => toggleGroup(group)}
|
|
322
264
|
>
|
|
323
|
-
<Icon
|
|
265
|
+
<Icon
|
|
266
|
+
name="chevron-right"
|
|
267
|
+
class="qm-group-chevron"
|
|
268
|
+
size={CHEVRON}
|
|
269
|
+
/>{section.label}
|
|
324
270
|
</button>
|
|
325
|
-
<!--
|
|
326
|
-
unmounted, so without it every field
|
|
271
|
+
<!-- The panel is clipped, not unmounted, so without `inert` every field
|
|
327
272
|
in a hidden section keeps its place in the tab order and in the a11y
|
|
328
|
-
tree, under a header announcing `aria-expanded="false"`.
|
|
329
|
-
untouched, so the 0fr↔1fr slide is the same slide. -->
|
|
273
|
+
tree, under a header announcing `aria-expanded="false"`. -->
|
|
330
274
|
<div
|
|
331
275
|
class="qm-group-panel"
|
|
332
276
|
id={panelId}
|
|
@@ -344,13 +288,9 @@
|
|
|
344
288
|
</div>
|
|
345
289
|
{/if}
|
|
346
290
|
|
|
347
|
-
<!--
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
is still announced. Unframed is the point rather than a saving: the body is
|
|
351
|
-
the ONLY surface in the card without an edge, which is what makes it read
|
|
352
|
-
as paper instead of as one more field, and the bracket's bottom rule
|
|
353
|
-
already does the separating a box was doing. -->
|
|
291
|
+
<!-- Unframed: the body is the only surface at the card's own tone, where every
|
|
292
|
+
other typed value sits in a well cut into it, and that is what makes it read as
|
|
293
|
+
paper instead of as one more field. -->
|
|
354
294
|
{#if card.hasBody}
|
|
355
295
|
<div class="qm-body-leaf">
|
|
356
296
|
<ProseField
|
|
@@ -374,10 +314,6 @@
|
|
|
374
314
|
{/if}
|
|
375
315
|
</section>
|
|
376
316
|
|
|
377
|
-
<!-- A section's fields as ONE grid: shared by the ungrouped block and the accordion
|
|
378
|
-
panels so both render a group's fields identically. Rows are the
|
|
379
|
-
grid's business: fields carry a span and auto-place, so nothing here re-derives
|
|
380
|
-
structure on resize and a prose leaf's key never moves. -->
|
|
381
317
|
{#snippet sectionFields(fields: FieldModel[])}
|
|
382
318
|
<div class="qm-fields">
|
|
383
319
|
{#each placeFields(fields) as { field: f, span } (f.name)}
|
|
@@ -405,12 +341,10 @@
|
|
|
405
341
|
</div>
|
|
406
342
|
{/snippet}
|
|
407
343
|
|
|
408
|
-
<!--
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
derive: `setCardKind` swaps the kind and leaves payload and body untouched, so
|
|
413
|
-
no field is dropped; with no kinds to offer, delete is the only exit. -->
|
|
344
|
+
<!-- A card whose `kind` has no schema stays visible and removable, its fields, body and
|
|
345
|
+
`$ext` intact in the Document, so it is never a data trap. `setCardKind` swaps the kind
|
|
346
|
+
and leaves payload and body untouched, so retyping drops no field; with no kinds to
|
|
347
|
+
offer, delete is the only exit. -->
|
|
414
348
|
{#snippet recoveryShell()}
|
|
415
349
|
<header class="qm-card-header">
|
|
416
350
|
<span class="qm-card-title-static">{humanize(card.kind)}</span>
|
|
@@ -449,8 +383,6 @@
|
|
|
449
383
|
</div>
|
|
450
384
|
{/snippet}
|
|
451
385
|
|
|
452
|
-
<!-- The declared card kinds as `<option>`s for the recovery shell's retype,
|
|
453
|
-
the one place a card's kind changes after insert. -->
|
|
454
386
|
{#snippet kindOptions()}
|
|
455
387
|
{#each kinds as k (k)}
|
|
456
388
|
<option value={k}>{humanize(k)}</option>
|
|
@@ -458,16 +390,14 @@
|
|
|
458
390
|
{/snippet}
|
|
459
391
|
|
|
460
392
|
<style>
|
|
393
|
+
/* An island, and the tone is the whole of what makes it one: the card is the base
|
|
394
|
+
plane and the column behind it is the sunken one (ARCHITECTURE §Styling). The inset
|
|
395
|
+
is the same rung as the gap, so what separates the header from the fields is what
|
|
396
|
+
separates the fields from the card's edge. */
|
|
461
397
|
.qm-card {
|
|
462
|
-
border: var(--_qm-border-width) solid var(--_qm-border);
|
|
463
398
|
border-radius: var(--_qm-radius);
|
|
464
|
-
/* Uniform inset on every side: a body-shown and a
|
|
465
|
-
body-hidden card stay symmetric, every left edge on one gutter. The SAME rung
|
|
466
|
-
as the gap between the card's stacked regions below: the card's inset and its
|
|
467
|
-
internal rhythm are one number, so what separates the header from the fields is
|
|
468
|
-
what separates the fields from the card's edge. */
|
|
469
399
|
padding: var(--_qm-space-3);
|
|
470
|
-
background: var(--_qm-surface
|
|
400
|
+
background: var(--_qm-surface);
|
|
471
401
|
display: flex;
|
|
472
402
|
flex-direction: column;
|
|
473
403
|
gap: var(--_qm-space-3);
|
|
@@ -478,46 +408,25 @@
|
|
|
478
408
|
justify-content: space-between;
|
|
479
409
|
gap: var(--_qm-space-2);
|
|
480
410
|
}
|
|
481
|
-
/* The
|
|
482
|
-
card's gutter
|
|
483
|
-
|
|
484
|
-
difference between `--_qm-tap-min` and the glyph — which is a target, not a
|
|
485
|
-
rhythm, and so is exactly what a gutter should not be measured from. Both ends
|
|
486
|
-
therefore hang by the chrome they carry and by nothing else; the title's is now
|
|
487
|
-
zero, and this one is the arithmetic of two rungs rather than a rung of its own,
|
|
488
|
-
because the amount is off the space scale (5px at the shipped dials) and a scale
|
|
489
|
-
that could express it would be a scale with a step nothing else uses.
|
|
490
|
-
|
|
491
|
-
It is the fill, not the box, that overhangs the gutter here: an icon button rests
|
|
492
|
-
unboxed and fills only under the pointer, so the
|
|
493
|
-
thing crossing the line is a transient target wash, not an edge the card claims. */
|
|
411
|
+
/* The overhang is the tap target's own centring given back, so the glyphs sit on the
|
|
412
|
+
card's gutter rather than the targets around them. It is the fill that crosses the
|
|
413
|
+
line, not a box: an icon button rests unboxed and fills only under the pointer. */
|
|
494
414
|
.qm-card-header-right {
|
|
495
415
|
display: flex;
|
|
496
416
|
align-items: center;
|
|
497
417
|
gap: var(--_qm-space-2);
|
|
498
418
|
margin-right: calc(-1 * (var(--_qm-tap-min) - var(--_qm-glyph-control)) / 2);
|
|
499
419
|
}
|
|
500
|
-
/*
|
|
501
|
-
|
|
502
|
-
caret in any leaf, the title, or a chevron itself holds the reveal: hover
|
|
503
|
-
alone would strand the pair on keyboard and touch. This is the card's whole
|
|
504
|
-
active treatment; nothing marks the section itself. */
|
|
420
|
+
/* Focus as well as hover, so a caret in any leaf, the title, or a chevron holds the
|
|
421
|
+
reveal: hover alone would strand the pair on keyboard and touch. */
|
|
505
422
|
.qm-card:hover :global(.qm-card-reorder),
|
|
506
423
|
.qm-card:focus-within :global(.qm-card-reorder) {
|
|
507
424
|
opacity: 1;
|
|
508
425
|
}
|
|
509
|
-
/* The rename hit region
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
than the input's.
|
|
514
|
-
|
|
515
|
-
It hangs by NOTHING, which is the whole of what the title carrying no box buys:
|
|
516
|
-
the first character lands on the card's gutter with the field list, the
|
|
517
|
-
bracket's rules and the body's first character, because nothing stands between
|
|
518
|
-
the region's edge and the glyph. The stretch is also what
|
|
519
|
-
keeps the target legal with the padding gone: WCAG 2.5.8's floor is the ROW's,
|
|
520
|
-
which the controls hold at `--_qm-tap-min`, so the input never carried it. */
|
|
426
|
+
/* The rename hit region is the header's free width at full height, so a press
|
|
427
|
+
anywhere left of the controls enters the title edit. `align-self: stretch` beats the
|
|
428
|
+
header's centring, which is also what keeps the target legal with no padding: WCAG
|
|
429
|
+
2.5.8's floor is the row's, which the controls hold at `--_qm-tap-min`. */
|
|
521
430
|
.qm-card-rename {
|
|
522
431
|
flex: 1;
|
|
523
432
|
min-width: 0;
|
|
@@ -526,25 +435,23 @@
|
|
|
526
435
|
align-items: center;
|
|
527
436
|
cursor: text;
|
|
528
437
|
}
|
|
529
|
-
/* Autosize
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
438
|
+
/* Autosize: the ::after mirrors the text into the same grid cell as the input, so the
|
|
439
|
+
overlaid input tracks its content width. Both carry the same nothing for a box model
|
|
440
|
+
and inherit the same type tokens, which is what makes the two measure alike.
|
|
441
|
+
|
|
442
|
+
The type is the group header's, down to the ink and its hover step below: a card
|
|
443
|
+
title and a section header both name a block of fields, so they read as one register
|
|
444
|
+
and what ranks them is position rather than a size step. */
|
|
535
445
|
.qm-card-title-sizer {
|
|
536
446
|
display: inline-grid;
|
|
537
447
|
align-items: center;
|
|
538
448
|
min-width: 0;
|
|
539
449
|
max-width: 100%;
|
|
540
|
-
font-size: var(--_qm-text-
|
|
541
|
-
font-weight: var(--_qm-weight-
|
|
450
|
+
font-size: var(--_qm-text-body);
|
|
451
|
+
font-weight: var(--_qm-weight-mid);
|
|
542
452
|
line-height: var(--_qm-leading-tight);
|
|
543
453
|
font-family: inherit;
|
|
544
454
|
}
|
|
545
|
-
/* The mirror has no box model to match, because the input has none: with the
|
|
546
|
-
padding and the hairline gone from both, the two measure alike by carrying the
|
|
547
|
-
same nothing. */
|
|
548
455
|
.qm-card-title-sizer::after {
|
|
549
456
|
content: attr(data-value) ' ';
|
|
550
457
|
grid-area: 1 / 1;
|
|
@@ -552,28 +459,17 @@
|
|
|
552
459
|
white-space: pre;
|
|
553
460
|
min-width: 2ch;
|
|
554
461
|
}
|
|
555
|
-
/* The title draws
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
other control follows, where the box rests drawn and hover changes nothing, and
|
|
560
|
-
it filled to `--_qm-surface` — the page rung,
|
|
561
|
-
BELOW the card it sits on, so the one hover on this surface that receded instead
|
|
562
|
-
of lifting.
|
|
563
|
-
|
|
564
|
-
What replaces it is an ink step, the same cue the group header takes for the same
|
|
565
|
-
reason: a wide borderless target drawing no box has no other way to answer the
|
|
566
|
-
pointer. Rest is `--_qm-ink-label`, which is where the recovery shell's static
|
|
567
|
-
title already sits, so the editable title and its unschemable twin finally agree
|
|
568
|
-
on tone as well as on the three type rungs they share. */
|
|
462
|
+
/* The title draws no box in any state: a card title is already the one line that
|
|
463
|
+
reads as the card's name, and the region's `cursor: text` says the rest. The ink step
|
|
464
|
+
replaces it, the same cue the group header takes — a wide borderless target has no
|
|
465
|
+
other way to answer the pointer. */
|
|
569
466
|
.qm-card-title {
|
|
570
467
|
grid-area: 1 / 1;
|
|
571
468
|
width: 100%;
|
|
572
469
|
min-width: 0;
|
|
573
470
|
font: inherit;
|
|
574
|
-
/* `font: inherit` does not carry colour:
|
|
575
|
-
|
|
576
|
-
header's ink without this line. */
|
|
471
|
+
/* `font: inherit` does not carry colour: a form control defaults to the UA's own
|
|
472
|
+
text colour rather than the header's ink without this line. */
|
|
577
473
|
color: var(--_qm-ink-label);
|
|
578
474
|
border: none;
|
|
579
475
|
padding: 0;
|
|
@@ -584,121 +480,79 @@
|
|
|
584
480
|
.qm-card-title:focus {
|
|
585
481
|
color: var(--_qm-ink);
|
|
586
482
|
}
|
|
587
|
-
/* One rhythm for the card's stacked regions and for the metadata block inside it:
|
|
588
|
-
the gap between a field list and the body is the gap between two field lists. */
|
|
589
483
|
.qm-card-body,
|
|
590
484
|
.qm-card-meta {
|
|
591
485
|
display: flex;
|
|
592
486
|
flex-direction: column;
|
|
593
487
|
gap: var(--_qm-space-3);
|
|
594
488
|
}
|
|
595
|
-
/* The metadata bracket
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
one figure. That sameness is the whole effect; it is why the vertical takes
|
|
600
|
-
`--_qm-border` and not `--_qm-accent`, which would be ornament with the chevron
|
|
601
|
-
and the header's ink step already saying open.
|
|
489
|
+
/* The metadata bracket, in three strokes at one rung: these two horizontals and the
|
|
490
|
+
open section's vertical below. A bracket whose sides disagree on width or tone reads
|
|
491
|
+
as three unrelated lines rather than one figure, which is why the vertical takes
|
|
492
|
+
`--_qm-border` and not `--_qm-accent`.
|
|
602
493
|
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
sometimes two strokes and sometimes one: an open figure by construction, which
|
|
608
|
-
is what distinguishes it from a second box inside the card. */
|
|
494
|
+
Each condition is what its stroke means: a top rule is the line under a card title,
|
|
495
|
+
so the headerless `main` has none; a bottom rule divides fields from body, so a card
|
|
496
|
+
with no body has nothing to divide. Sometimes two strokes and sometimes one — an open
|
|
497
|
+
figure, which is what distinguishes it from a second box inside the card. */
|
|
609
498
|
.qm-meta-top {
|
|
610
499
|
border-top: var(--_qm-border-width) solid var(--_qm-border);
|
|
611
500
|
}
|
|
612
501
|
.qm-meta-bottom {
|
|
613
502
|
border-bottom: var(--_qm-border-width) solid var(--_qm-border);
|
|
614
503
|
}
|
|
615
|
-
/*
|
|
616
|
-
vertical has to
|
|
617
|
-
is the distance by which it would miss.
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
A block of FIELDS has no padding of its own, so it takes the inset wherever it
|
|
621
|
-
abuts a rule; three places, since three blocks can: an ungrouped block under the
|
|
622
|
-
top rule, an ungrouped block over the bottom rule (only reachable because the
|
|
623
|
-
accordion's `{#if}` keeps an empty wrapper out of last position), and the last
|
|
624
|
-
section's panel when it is open, whose inset is otherwise zero at the bottom and
|
|
625
|
-
would stand its final row of controls directly on the stroke. The panel's padding
|
|
626
|
-
is inside `.qm-group`, so the vertical still runs the full height to the corner.
|
|
627
|
-
Ungrouped fields always render first, so the top selector needs no qualifier. */
|
|
504
|
+
/* The inset sits on the fields rather than on the block that holds them: an open
|
|
505
|
+
section's vertical has to reach the rule to close a corner against it, and padding
|
|
506
|
+
between the two is the distance by which it would miss. Unqualified by open — a shut
|
|
507
|
+
panel clips it. */
|
|
628
508
|
.qm-meta-top > .qm-section {
|
|
629
509
|
padding-top: var(--_qm-space-3);
|
|
630
510
|
}
|
|
631
511
|
.qm-meta-bottom > .qm-section:last-child,
|
|
632
|
-
.qm-meta-bottom .qm-group:last-child
|
|
512
|
+
.qm-meta-bottom .qm-group:last-child .qm-group-panel-inner > .qm-fields {
|
|
633
513
|
padding-bottom: var(--_qm-space-3);
|
|
634
514
|
}
|
|
635
|
-
/*
|
|
636
|
-
|
|
637
|
-
inset makes that narrower than the card.
|
|
638
|
-
|
|
639
|
-
The ACTION COLUMN is minted here and spent one level down, on the field
|
|
640
|
-
(`Field.svelte` holds why it is the field's inset and not this container's). It
|
|
641
|
-
is declared on the query container because both of its consumers hang off one:
|
|
642
|
-
the fields inside, and the capacity ramp below, which carries the column in its
|
|
643
|
-
own arithmetic because the query reads a box the reservation has not narrowed. */
|
|
515
|
+
/* Two query containers, one per kind of section, so capacity follows the width the
|
|
516
|
+
fields actually get: a panel's inset makes that narrower than the card. */
|
|
644
517
|
.qm-section,
|
|
645
518
|
.qm-group-panel-inner {
|
|
646
|
-
--action-col: calc(var(--_qm-tap-min) + var(--_qm-space-2));
|
|
647
519
|
container-type: inline-size;
|
|
648
520
|
}
|
|
649
|
-
/*
|
|
650
|
-
|
|
651
|
-
wrong capacity, and no re-packing to restructure the DOM under a prose leaf.
|
|
652
|
-
Fields auto-place, which is what keeps a trailing orphan at its column width
|
|
653
|
-
instead of growing to fill the line.
|
|
654
|
-
|
|
655
|
-
Capacity steps 1 → 2 → 4, skipping 3: each rung is the width at which a track
|
|
656
|
-
still clears the comfortable field minimum (2 needs 32rem, 4 needs 65rem), and
|
|
657
|
-
an even capacity is what lets `lone`'s half land on a track boundary. Each rung
|
|
658
|
-
pays for a track's own action column as well as the gutters between tracks, since
|
|
659
|
-
the reservation is per field now: a rung is `n * (220px + action) + gaps`.
|
|
521
|
+
/* Capacity is the container's, not JavaScript's: nothing measures, so there is no
|
|
522
|
+
observer to loop and no re-packing to restructure the DOM under a prose leaf.
|
|
660
523
|
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
524
|
+
It steps 1 → 2 → 4, skipping 3: each rung is the width at which a track still clears
|
|
525
|
+
the comfortable field minimum, and an even capacity is what lets `lone`'s half land
|
|
526
|
+
on a track boundary. A field ends on its track's own edge, so a rung is
|
|
527
|
+
`n * 220px + gaps`. */
|
|
665
528
|
.qm-fields {
|
|
666
529
|
--cols: 1;
|
|
667
530
|
--cols-half: 1;
|
|
668
531
|
display: grid;
|
|
669
532
|
grid-template-columns: repeat(var(--cols), 1fr);
|
|
670
533
|
column-gap: var(--_qm-space-2);
|
|
671
|
-
row-gap: var(--_qm-space-
|
|
534
|
+
row-gap: var(--_qm-space-3);
|
|
672
535
|
}
|
|
673
|
-
@container (min-width:
|
|
536
|
+
@container (min-width: 28rem) {
|
|
674
537
|
.qm-fields {
|
|
675
538
|
--cols: 2;
|
|
676
539
|
--cols-half: 1;
|
|
677
540
|
}
|
|
678
541
|
}
|
|
679
|
-
@container (min-width:
|
|
542
|
+
@container (min-width: 57rem) {
|
|
680
543
|
.qm-fields {
|
|
681
544
|
--cols: 4;
|
|
682
545
|
--cols-half: 2;
|
|
683
546
|
}
|
|
684
547
|
}
|
|
685
|
-
/* Group accordion. The header is a toggle
|
|
686
|
-
at the field-label rung; the panel slides via a 0fr↔1fr grid row so the height
|
|
687
|
-
animates without a magic max-height. */
|
|
688
|
-
/* The wrapper carries NO gap: the headers' own padding is the rhythm (see the
|
|
689
|
-
markup note). */
|
|
690
548
|
.qm-groups,
|
|
691
549
|
.qm-group {
|
|
692
550
|
display: flex;
|
|
693
551
|
flex-direction: column;
|
|
694
552
|
}
|
|
695
|
-
/* The bracket's third stroke, on the
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
One rung with the two horizontals, held there by `check:style`'s border-width axis.
|
|
699
|
-
|
|
700
|
-
Drawn transparent when closed rather than absent, so the 1px it occupies is the
|
|
701
|
-
same open or shut and a toggle moves no text. */
|
|
553
|
+
/* The bracket's third stroke, on the section rather than on its panel: spanning the
|
|
554
|
+
header is what gives it a corner to close into at the block's edge. Drawn transparent
|
|
555
|
+
when closed rather than absent, so a toggle moves no text. */
|
|
702
556
|
.qm-group {
|
|
703
557
|
border-left: var(--_qm-border-width) solid transparent;
|
|
704
558
|
transition: border-color var(--_qm-duration-slow) var(--_qm-ease-reverse);
|
|
@@ -706,33 +560,17 @@
|
|
|
706
560
|
.qm-group.qm-open {
|
|
707
561
|
border-left-color: var(--_qm-border);
|
|
708
562
|
}
|
|
709
|
-
/*
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
A button by tag and NEITHER button family by recipe: it reads no
|
|
722
|
-
`--_qm-tap-min` (the padding above already clears the floor) and takes no hover
|
|
723
|
-
fill (an ink step, below); so the type a family would have carried is declared
|
|
724
|
-
here. `font: inherit` because a UA button inherits no face and `--qm-font` would
|
|
725
|
-
stop at this row; then the field-label rung, at the tight leading a single line
|
|
726
|
-
takes. Sentence case at that rung, reused rather than a fifth size minted: a
|
|
727
|
-
section name is structurally a heading, and uppercase costs it twice: the word
|
|
728
|
-
shape a column of them is scanned by, and apparent width, so a long label crowds
|
|
729
|
-
sooner. Size, weight and leading all sit after `font`, which is a shorthand that
|
|
730
|
-
carries `line-height`.
|
|
731
|
-
|
|
732
|
-
On the BUTTON rather than on a span inside it: the row's type is one decision,
|
|
733
|
-
and a wrapper that carries nothing else is indirection between the header and
|
|
734
|
-
its own label. The label is a text run beside the chevron, and flex gives it an
|
|
735
|
-
anonymous item either way. */
|
|
563
|
+
/* Symmetric vertical padding at the tightest rung that still clears WCAG 2.5.8's
|
|
564
|
+
24×24 floor: the header is the whole row, so adjacent labels share one rhythm with no
|
|
565
|
+
dead strip outside the button. Horizontal is one rung left and zero right — left,
|
|
566
|
+
because at zero the chevron stands on the section's vertical with only the icon box's
|
|
567
|
+
own bearing between them, and the glyph's rotation swaps which bearing faces the
|
|
568
|
+
stroke; right, because the row is the target and an inset there is target given back.
|
|
569
|
+
|
|
570
|
+
`font: inherit` because a UA button inherits no face, then the body rung, one step
|
|
571
|
+
over the field labels beneath it: at the label rung the two read as one register and
|
|
572
|
+
the accordion stops looking like structure. Size, weight and leading sit after
|
|
573
|
+
`font`, a shorthand that carries `line-height`. */
|
|
736
574
|
.qm-group-header {
|
|
737
575
|
display: flex;
|
|
738
576
|
align-items: center;
|
|
@@ -742,8 +580,8 @@
|
|
|
742
580
|
background: transparent;
|
|
743
581
|
padding: var(--_qm-space) 0 var(--_qm-space) var(--_qm-space);
|
|
744
582
|
font: inherit;
|
|
745
|
-
font-size: var(--_qm-text-
|
|
746
|
-
font-weight: var(--_qm-weight-
|
|
583
|
+
font-size: var(--_qm-text-body);
|
|
584
|
+
font-weight: var(--_qm-weight-mid);
|
|
747
585
|
line-height: var(--_qm-leading-tight);
|
|
748
586
|
cursor: pointer;
|
|
749
587
|
color: var(--_qm-ink-label);
|
|
@@ -752,28 +590,21 @@
|
|
|
752
590
|
}
|
|
753
591
|
.qm-group-header :global(.qm-group-chevron) {
|
|
754
592
|
flex-shrink: 0;
|
|
593
|
+
display: block;
|
|
594
|
+
transform: rotate(0deg);
|
|
595
|
+
transform-origin: center;
|
|
755
596
|
transition: transform var(--_qm-duration-slow) var(--_qm-ease-reverse);
|
|
756
597
|
}
|
|
757
|
-
/*
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
share a rung and one of them is wrong: an ink step at the slow rung lags the
|
|
763
|
-
pointer, and at the fast one it settles 80ms before the panel it belongs to.
|
|
764
|
-
Hover keeps the property because open has another cue in the
|
|
765
|
-
chevron's rotation and hover has none, a big borderless target drawing no box. */
|
|
766
|
-
.qm-group-header:hover {
|
|
598
|
+
/* Open shares hover's ink step: a big borderless target draws no box, so the ink is
|
|
599
|
+
the only cue an open section keeps once the pointer leaves it. The chevron's
|
|
600
|
+
rotation is the other cue, and stays open-only below. */
|
|
601
|
+
.qm-group-header:hover,
|
|
602
|
+
.qm-group.qm-open .qm-group-header {
|
|
767
603
|
color: var(--_qm-ink);
|
|
768
604
|
}
|
|
769
605
|
.qm-group.qm-open .qm-group-header :global(.qm-group-chevron) {
|
|
770
606
|
transform: rotate(90deg);
|
|
771
607
|
}
|
|
772
|
-
/* Sliding panel: the grid track goes 0fr→1fr; the inner clips at min-height 0.
|
|
773
|
-
Clipping is the whole of what CSS does here: what a closed panel is OUT of is
|
|
774
|
-
`inert`'s to say, on the element. The panel
|
|
775
|
-
draws no rule of its own: the vertical is the section's (`.qm-group`), which is
|
|
776
|
-
what spans the header too. */
|
|
777
608
|
.qm-group-panel {
|
|
778
609
|
display: grid;
|
|
779
610
|
grid-template-rows: 0fr;
|
|
@@ -782,42 +613,41 @@
|
|
|
782
613
|
.qm-group.qm-open .qm-group-panel {
|
|
783
614
|
grid-template-rows: 1fr;
|
|
784
615
|
}
|
|
785
|
-
/*
|
|
786
|
-
|
|
616
|
+
/* Two boxes because the axes clip differently, and the track is the only thing that
|
|
617
|
+
animates: a field sits at its open position from the first frame of the reveal.
|
|
618
|
+
|
|
619
|
+
The clip box takes the inline inset, which costs a shut panel no height and holds the
|
|
620
|
+
queried width still. The block inset goes on the content, where a `0fr` track clips
|
|
621
|
+
it: declared one box out it collapses the content but not itself, and stands under a
|
|
622
|
+
shut header as dead space. Neither is animated, and neither is qualified by open.
|
|
623
|
+
|
|
624
|
+
A field ends on the clip box at the end edge, where no inset hides its ring the way
|
|
625
|
+
the start edge's does. The pad carries the clip past it and the margin takes the same
|
|
626
|
+
distance back, so the width the fields are packed and queried at is unmoved. */
|
|
787
627
|
.qm-group-panel-inner {
|
|
788
|
-
display: flex;
|
|
789
|
-
flex-direction: column;
|
|
790
|
-
gap: var(--_qm-space-2);
|
|
791
628
|
min-height: 0;
|
|
792
629
|
overflow: hidden;
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
panel animates its width along with them, and the capacity ramp reads that
|
|
797
|
-
width: a closed panel is `inert` and unmeasured either way, so what a rung
|
|
798
|
-
crosses mid-slide is a hidden grid. */
|
|
799
|
-
padding: 0;
|
|
800
|
-
transition: padding var(--_qm-duration-slow) var(--_qm-ease-reverse);
|
|
630
|
+
padding-inline-start: var(--_qm-space-4);
|
|
631
|
+
padding-inline-end: var(--_qm-ring-reach);
|
|
632
|
+
margin-inline-end: calc(-1 * var(--_qm-ring-reach));
|
|
801
633
|
}
|
|
802
|
-
.qm-group
|
|
803
|
-
padding: var(--_qm-space-2)
|
|
634
|
+
.qm-group-panel-inner > .qm-fields {
|
|
635
|
+
padding-block: var(--_qm-space-2);
|
|
804
636
|
}
|
|
805
637
|
.qm-body-leaf {
|
|
806
638
|
display: flex;
|
|
807
639
|
flex-direction: column;
|
|
808
640
|
gap: var(--_qm-space);
|
|
809
641
|
}
|
|
810
|
-
/*
|
|
811
|
-
|
|
642
|
+
/* The one card that draws an edge at all: an ordinary card is a plane, so a stroke
|
|
643
|
+
around this one is a state rather than heavier chrome. */
|
|
812
644
|
.qm-card.qm-unschemable {
|
|
813
|
-
border
|
|
645
|
+
border: var(--_qm-border-width) dashed var(--_qm-border);
|
|
814
646
|
}
|
|
815
|
-
/* One typographic role with the editable title above
|
|
816
|
-
rungs: including the tight leading a card title takes over the root's reading
|
|
817
|
-
rhythm. */
|
|
647
|
+
/* One typographic role with the editable title above. */
|
|
818
648
|
.qm-card-title-static {
|
|
819
|
-
font-size: var(--_qm-text-
|
|
820
|
-
font-weight: var(--_qm-weight-
|
|
649
|
+
font-size: var(--_qm-text-body);
|
|
650
|
+
font-weight: var(--_qm-weight-mid);
|
|
821
651
|
line-height: var(--_qm-leading-tight);
|
|
822
652
|
color: var(--_qm-ink-label);
|
|
823
653
|
}
|
|
@@ -841,11 +671,12 @@
|
|
|
841
671
|
font-size: var(--_qm-text-label);
|
|
842
672
|
color: var(--_qm-ink-label);
|
|
843
673
|
}
|
|
674
|
+
/* The one native control the surface draws, on the box's own recipe (controls.css). */
|
|
844
675
|
.qm-recovery-retype select {
|
|
845
676
|
font: inherit;
|
|
846
|
-
border:
|
|
677
|
+
border: none;
|
|
847
678
|
border-radius: var(--_qm-radius-inner);
|
|
848
|
-
background: var(--_qm-surface);
|
|
679
|
+
background: var(--_qm-surface-well);
|
|
849
680
|
padding: var(--_qm-space-half) var(--_qm-space-2);
|
|
850
681
|
}
|
|
851
682
|
</style>
|