@quillmark/svelte 0.10.1 → 0.12.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 +1 -1
- package/THEMING.md +2 -2
- package/dist/core/address.d.ts +17 -10
- package/dist/core/address.js +26 -12
- package/dist/core/bloom.js +4 -3
- package/dist/core/codec/blocks.js +21 -1
- package/dist/core/codec/breaks.d.ts +10 -2
- package/dist/core/codec/breaks.js +65 -9
- package/dist/core/codec/code.d.ts +6 -4
- package/dist/core/codec/code.js +7 -4
- package/dist/core/codec/decode.js +103 -68
- package/dist/core/codec/encode.d.ts +5 -8
- package/dist/core/codec/encode.js +56 -86
- package/dist/core/codec/field.js +43 -24
- package/dist/core/codec/index.d.ts +2 -1
- package/dist/core/codec/index.js +6 -3
- package/dist/core/codec/islands.d.ts +16 -11
- package/dist/core/codec/islands.js +70 -59
- package/dist/core/codec/keymap.js +2 -1
- package/dist/core/codec/marks.d.ts +17 -13
- package/dist/core/codec/marks.js +39 -42
- package/dist/core/codec/positions.d.ts +4 -1
- package/dist/core/codec/positions.js +5 -1
- package/dist/core/codec/reconcile.d.ts +6 -0
- package/dist/core/codec/reconcile.js +19 -0
- package/dist/core/codec/schema.d.ts +5 -8
- package/dist/core/codec/schema.js +30 -156
- package/dist/core/codec/slash.js +0 -0
- package/dist/core/codec/table-view.js +77 -26
- package/dist/core/codec/table.d.ts +6 -5
- package/dist/core/codec/table.js +14 -31
- package/dist/core/codec/urls.d.ts +20 -0
- package/dist/core/codec/urls.js +45 -0
- package/dist/core/rungs.d.ts +8 -0
- package/dist/core/rungs.js +24 -0
- package/dist/core/theme.css +10 -0
- package/dist/preset/recipes.css +1 -1
- package/dist/preset/scale.css +7 -4
- package/dist/preview/bridge.d.ts +8 -1
- package/dist/preview/bridge.js +31 -25
- package/dist/preview/controller.js +13 -13
- package/dist/preview/geometry.d.ts +12 -1
- package/dist/preview/geometry.js +14 -1
- package/dist/preview/paint.js +6 -4
- package/dist/preview/strings.d.ts +0 -2
- package/dist/preview/strings.js +5 -6
- package/dist/visual/ArrayField.svelte +600 -173
- package/dist/visual/ArrayField.svelte.d.ts +14 -1
- package/dist/visual/BooleanField.svelte +10 -7
- package/dist/visual/Card.svelte +8 -30
- package/dist/visual/DateField.svelte +2 -2
- package/dist/visual/Field.svelte +76 -32
- package/dist/visual/FieldHint.svelte +1 -1
- package/dist/visual/MatrixField.svelte +352 -0
- package/dist/visual/MatrixField.svelte.d.ts +36 -0
- package/dist/visual/ObjectField.svelte +356 -133
- package/dist/visual/ObjectField.svelte.d.ts +17 -2
- package/dist/visual/VariantField.svelte +55 -2
- package/dist/visual/VariantField.svelte.d.ts +10 -1
- package/dist/visual/VisualEditor.svelte +1 -1
- package/dist/visual/VisualEditorInner.svelte +118 -67
- package/dist/visual/controls.css +39 -9
- package/dist/visual/diagnostics.d.ts +26 -2
- package/dist/visual/diagnostics.js +52 -5
- package/dist/visual/ext.d.ts +12 -13
- package/dist/visual/ext.js +22 -19
- package/dist/visual/hold.js +2 -2
- package/dist/visual/icons/nodes.d.ts +3 -0
- package/dist/visual/icons/nodes.js +2 -1
- package/dist/visual/index.js +2 -3
- package/dist/visual/leaves.d.ts +25 -12
- package/dist/visual/links.d.ts +4 -0
- package/dist/visual/links.js +15 -4
- package/dist/visual/motion.d.ts +14 -5
- package/dist/visual/motion.js +13 -11
- package/dist/visual/strings.d.ts +26 -13
- package/dist/visual/strings.js +7 -2
- package/dist/visual/structure.d.ts +101 -8
- package/dist/visual/structure.js +252 -13
- package/package.json +2 -2
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
import type { Content, PathStep, QuillFieldSchema } from '@quillmark/wasm';
|
|
2
|
+
import { type ArrayLayout } from './structure.js';
|
|
3
|
+
import { type DeepDiagnostic } from './diagnostics.js';
|
|
4
|
+
import type { LandingBox } from './leaves.js';
|
|
2
5
|
import './controls.css';
|
|
3
6
|
interface Props {
|
|
4
7
|
value: unknown[] | undefined;
|
|
5
8
|
items: QuillFieldSchema | undefined;
|
|
9
|
+
/** How the elements draw: the record list, or the table an `array<object>` of
|
|
10
|
+
* short cells asks for (`arrayLayout`). */
|
|
11
|
+
layout?: ArrayLayout;
|
|
12
|
+
/** The schema's `max:`, past which the add chip disables and Enter stops
|
|
13
|
+
* inserting; `undefined` is no cap. */
|
|
14
|
+
max?: number;
|
|
6
15
|
/** Accessible-name prefix for the element controls (`label` + 1-based index). */
|
|
7
16
|
label?: string;
|
|
8
17
|
/** No-default field → a persistent required `*` on the label. */
|
|
@@ -28,11 +37,15 @@ interface Props {
|
|
|
28
37
|
* control has spliced in and whose commit has yet to land, or was refused. */
|
|
29
38
|
contentAt: (path: PathStep[]) => Content | undefined;
|
|
30
39
|
onCommit: (arr: unknown[]) => void;
|
|
40
|
+
/** The diagnostics routed into this array, each with its steps still to walk: one
|
|
41
|
+
* naming a row draws under that row — under its head while it is collapsed, under
|
|
42
|
+
* the cell once it is open — and one naming no row draws at the list's foot. */
|
|
43
|
+
diagnostics?: readonly DeepDiagnostic[];
|
|
31
44
|
}
|
|
32
45
|
declare const ArrayField: import("svelte").Component<Props, {
|
|
33
46
|
focus: () => void;
|
|
34
47
|
washBox: () => HTMLElement | undefined;
|
|
35
|
-
|
|
48
|
+
focusPath: (steps: readonly PathStep[], pos?: number) => LandingBox;
|
|
36
49
|
}, "">;
|
|
37
50
|
type ArrayField = ReturnType<typeof ArrayField>;
|
|
38
51
|
export default ArrayField;
|
|
@@ -36,9 +36,12 @@
|
|
|
36
36
|
const local = syncedLocal(() => value ?? fallback ?? false);
|
|
37
37
|
</script>
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
<!-- Not `.qm-switch`: that name is the preset's pane band, whose narrow-viewport rule
|
|
40
|
+
stands every child of it at `--qmh-tap`, so a track wearing it grows a thumb three times
|
|
41
|
+
its own height over the labels around it (`preset/recipes.css`, held by `check:style`). -->
|
|
42
|
+
<span class="qm-toggle-wrap">
|
|
40
43
|
<Switch.Root
|
|
41
|
-
class="qm-
|
|
44
|
+
class="qm-toggle qm-focus-ring qm-tap-floor"
|
|
42
45
|
checked={local.value}
|
|
43
46
|
{id}
|
|
44
47
|
aria-label={id ? undefined : label}
|
|
@@ -48,7 +51,7 @@
|
|
|
48
51
|
onCommit(v);
|
|
49
52
|
}}
|
|
50
53
|
>
|
|
51
|
-
<Switch.Thumb class="qm-
|
|
54
|
+
<Switch.Thumb class="qm-toggle-thumb" />
|
|
52
55
|
</Switch.Root>
|
|
53
56
|
</span>
|
|
54
57
|
|
|
@@ -60,7 +63,7 @@
|
|
|
60
63
|
2.5.8's floor is half again that. The `<label for>` beside it forwards a press but
|
|
61
64
|
stands at the label rung, so it is no taller: without the floor no region on the
|
|
62
65
|
surface reaches this control at the size every other control on it holds. */
|
|
63
|
-
.qm-
|
|
66
|
+
.qm-toggle-wrap :global(.qm-toggle) {
|
|
64
67
|
display: inline-flex;
|
|
65
68
|
align-items: center;
|
|
66
69
|
width: 1.75rem;
|
|
@@ -75,21 +78,21 @@
|
|
|
75
78
|
cursor: pointer;
|
|
76
79
|
transition: background var(--_qm-duration-fast) var(--_qm-ease-reverse);
|
|
77
80
|
}
|
|
78
|
-
.qm-
|
|
81
|
+
.qm-toggle-wrap :global(.qm-toggle[data-state='checked']) {
|
|
79
82
|
background: var(--_qm-accent);
|
|
80
83
|
}
|
|
81
84
|
/* The focus ring rides `.qm-focus-ring` on the switch (controls.css). */
|
|
82
85
|
/* The thumb reads on tone alone: the base surface over the track's `hover` rung
|
|
83
86
|
unchecked, and over `--_qm-accent` checked. No edge of its own: a hairline around
|
|
84
87
|
a 12px pill is a second box inside the track's. */
|
|
85
|
-
.qm-
|
|
88
|
+
.qm-toggle-wrap :global(.qm-toggle-thumb) {
|
|
86
89
|
width: 0.75rem;
|
|
87
90
|
height: 0.75rem;
|
|
88
91
|
border-radius: var(--_qm-radius-pill);
|
|
89
92
|
background: var(--_qm-surface);
|
|
90
93
|
transition: translate var(--_qm-duration-fast) var(--_qm-ease-reverse);
|
|
91
94
|
}
|
|
92
|
-
.qm-
|
|
95
|
+
.qm-toggle-wrap :global(.qm-toggle-thumb[data-state='checked']) {
|
|
93
96
|
translate: 0.75rem;
|
|
94
97
|
}
|
|
95
98
|
</style>
|
package/dist/visual/Card.svelte
CHANGED
|
@@ -315,7 +315,7 @@
|
|
|
315
315
|
{quill}
|
|
316
316
|
addr={ops.makeAddr(undefined)}
|
|
317
317
|
unframed
|
|
318
|
-
label=
|
|
318
|
+
label={t.strings.bodyLabel}
|
|
319
319
|
placeholder={card.bodyGhost}
|
|
320
320
|
leafKey={ops.leafKey(undefined)}
|
|
321
321
|
{onFocus}
|
|
@@ -332,7 +332,7 @@
|
|
|
332
332
|
</section>
|
|
333
333
|
|
|
334
334
|
{#snippet sectionFields(fields: FieldModel[])}
|
|
335
|
-
<div class="qm-fields">
|
|
335
|
+
<div class="qm-fields qm-tracks">
|
|
336
336
|
{#each placeFields(fields) as { field: f, span } (f.name)}
|
|
337
337
|
<Field
|
|
338
338
|
field={f}
|
|
@@ -359,9 +359,9 @@
|
|
|
359
359
|
{/snippet}
|
|
360
360
|
|
|
361
361
|
<!-- A card whose `kind` has no schema stays visible and removable, its fields, body and
|
|
362
|
-
`$ext` intact in the Document, so it is never a data trap.
|
|
363
|
-
|
|
364
|
-
|
|
362
|
+
`$ext` intact in the Document, so it is never a data trap. A retype swaps the kind and
|
|
363
|
+
leaves payload and body untouched, so it drops no field; with no kinds to offer, delete
|
|
364
|
+
is the only exit. -->
|
|
365
365
|
{#snippet recoveryShell()}
|
|
366
366
|
<header class="qm-card-header">
|
|
367
367
|
<span class="qm-card-title-static">{humanize(card.kind)}</span>
|
|
@@ -393,7 +393,7 @@
|
|
|
393
393
|
</select>
|
|
394
394
|
</label>
|
|
395
395
|
{:else}
|
|
396
|
-
<p class="qm-recovery-note
|
|
396
|
+
<p class="qm-recovery-note">
|
|
397
397
|
{t.strings.noCardKinds}
|
|
398
398
|
</p>
|
|
399
399
|
{/if}
|
|
@@ -584,33 +584,14 @@
|
|
|
584
584
|
.qm-group-panel-inner {
|
|
585
585
|
container-type: inline-size;
|
|
586
586
|
}
|
|
587
|
-
/*
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
It steps 1 → 2 → 4, skipping 3: each rung is the width at which a track still clears
|
|
591
|
-
the comfortable field minimum, and an even capacity is what lets the half a subform
|
|
592
|
-
of one takes (`--cols-half`, `ObjectField`) land on a track boundary. A field ends on
|
|
593
|
-
its track's own edge, so a rung is `n * 220px + gaps`. */
|
|
587
|
+
/* The count is the ladder's (`.qm-tracks`, controls.css), stepped by the container
|
|
588
|
+
above; what this block owns is the grid the count fills. */
|
|
594
589
|
.qm-fields {
|
|
595
|
-
--cols: 1;
|
|
596
|
-
--cols-half: 1;
|
|
597
590
|
display: grid;
|
|
598
591
|
grid-template-columns: repeat(var(--cols), 1fr);
|
|
599
592
|
column-gap: var(--_qm-space-2);
|
|
600
593
|
row-gap: var(--_qm-space-3);
|
|
601
594
|
}
|
|
602
|
-
@container (min-width: 28rem) {
|
|
603
|
-
.qm-fields {
|
|
604
|
-
--cols: 2;
|
|
605
|
-
--cols-half: 1;
|
|
606
|
-
}
|
|
607
|
-
}
|
|
608
|
-
@container (min-width: 57rem) {
|
|
609
|
-
.qm-fields {
|
|
610
|
-
--cols: 4;
|
|
611
|
-
--cols-half: 2;
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
595
|
/* The nesting vertical, one `--_qm-nest` in from the card's edge with the panel's
|
|
615
596
|
fields the same rung off it (ARCHITECTURE §"A plane is a tone"). `--_qm-border` and
|
|
616
597
|
not `--_qm-accent`: a card's edge and the verticals inside it are one stroke at three
|
|
@@ -780,9 +761,6 @@
|
|
|
780
761
|
font-size: var(--_qm-text-label);
|
|
781
762
|
color: var(--_qm-ink-label);
|
|
782
763
|
}
|
|
783
|
-
.qm-recovery-muted {
|
|
784
|
-
color: var(--_qm-ink-label);
|
|
785
|
-
}
|
|
786
764
|
.qm-recovery-retype {
|
|
787
765
|
display: inline-flex;
|
|
788
766
|
align-items: center;
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
calendar) rather than `DatePicker`: the segments are the entry affordance, and a
|
|
12
12
|
calendar is a second surface this field does not need.
|
|
13
13
|
|
|
14
|
-
The boundary is
|
|
14
|
+
The boundary is a string, and the local is too. The primitive speaks
|
|
15
15
|
`CalendarDate`; the document speaks `YYYY-MM-DD`. `CalendarDate` carries no time
|
|
16
16
|
and no zone, so the round-trip is lossless and no local-midnight shift can occur:
|
|
17
17
|
the hazard that makes `new Date('2026-07-25')` the wrong tool here. Authored
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
parsed value: a fresh `CalendarDate` is never `===` the last one, which would
|
|
23
23
|
make every reconcile fire and re-render all seven segments on each commit.
|
|
24
24
|
|
|
25
|
-
The ghost is the default'
|
|
25
|
+
The ghost is the default's digits, not a format hint. An unset field
|
|
26
26
|
carrying a `default:` prints the default's digits in the segments, ghost-toned,
|
|
27
27
|
instead of the primitive's `mm`/`dd`/`yyyy` hints, which say "empty" where the
|
|
28
28
|
rung says "will render 2026-01-01". The ghost is painted in the segment snippet,
|
package/dist/visual/Field.svelte
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
<!--
|
|
2
2
|
Type dispatch (VISUAL_EDITOR §"Structure mirrors the schema"). Given one projected
|
|
3
3
|
{@link FieldModel} and its live value, render the label + the control the type
|
|
4
|
-
maps to. Array controls own their label (paired with the add affordance in
|
|
5
|
-
{@link ArrayField}
|
|
4
|
+
maps to. Array and matrix controls own their label (paired with the add affordance in
|
|
5
|
+
{@link ArrayField}, with the held count in {@link MatrixField}); other types render
|
|
6
|
+
the label here. A container control also takes the walk a nested address makes into
|
|
7
|
+
it ({@link FieldControl.focusPath}) and the diagnostics anchored inside it. Prose leaves take a
|
|
6
8
|
parent-built live `addr` (its `card` a getter over the stable-id→index map) so
|
|
7
9
|
a reorder re-targets without a remount; scalars, arrays, and objects commit
|
|
8
10
|
their value up through `onCommitScalar`, which the parent lowers to the typed
|
|
@@ -24,9 +26,16 @@
|
|
|
24
26
|
ResolvedField
|
|
25
27
|
} from '@quillmark/wasm';
|
|
26
28
|
import type { EditorErrorHandler } from '../core/errors.js';
|
|
27
|
-
import type { LeafRegistry } from './leaves.js';
|
|
29
|
+
import type { LandingBox, LeafRegistry } from './leaves.js';
|
|
28
30
|
import type { FieldModel, FieldSpan } from './structure.js';
|
|
29
|
-
import {
|
|
31
|
+
import {
|
|
32
|
+
VARIANT_DISCRIMINANT,
|
|
33
|
+
arrayLayout,
|
|
34
|
+
ghostDefault,
|
|
35
|
+
isContainer,
|
|
36
|
+
stringifyGhost
|
|
37
|
+
} from './structure.js';
|
|
38
|
+
import { deepen } from './diagnostics.js';
|
|
30
39
|
import type { FieldDomIds } from './domid.js';
|
|
31
40
|
import ProseField from './ProseField.svelte';
|
|
32
41
|
import TextField from './TextField.svelte';
|
|
@@ -37,6 +46,7 @@
|
|
|
37
46
|
import ArrayField from './ArrayField.svelte';
|
|
38
47
|
import ObjectField from './ObjectField.svelte';
|
|
39
48
|
import VariantField from './VariantField.svelte';
|
|
49
|
+
import MatrixField from './MatrixField.svelte';
|
|
40
50
|
import DiagnosticList from './DiagnosticList.svelte';
|
|
41
51
|
import FieldLabel from './FieldLabel.svelte';
|
|
42
52
|
|
|
@@ -123,15 +133,25 @@
|
|
|
123
133
|
// reference must vanish with it: `aria-describedby` pointing at nothing describes
|
|
124
134
|
// nothing, and silently.
|
|
125
135
|
const describedBy = $derived(field.description ? domIds.description : undefined);
|
|
136
|
+
// Two controls own their label row, the add chip and the held count sharing it.
|
|
137
|
+
const ownsLabel = $derived(field.control === 'array' || field.control === 'matrix');
|
|
138
|
+
|
|
139
|
+
// A container walks its diagnostics down to the cell each names and draws the rest
|
|
140
|
+
// at its own foot (`diagnostics.ts`); every other control draws them here, under
|
|
141
|
+
// itself, there being nothing inside it for a path to name.
|
|
142
|
+
const deep = $derived(deepen(diagnostics));
|
|
126
143
|
|
|
127
144
|
/** The boundary's nested content read with this field's address already bound: an
|
|
128
145
|
* array's elements, an object's properties, a variant's cells, and a subform's cells
|
|
129
146
|
* under an open element. One door rather than one per depth, since `getContentAt`
|
|
130
147
|
* takes the whole path and the walk down is each container prefixing its own step.
|
|
131
|
-
*
|
|
132
|
-
*
|
|
148
|
+
*
|
|
149
|
+
* The reader is held, as the codec's is (`field.ts`): a `{quill, doc}` pair reads
|
|
150
|
+
* live, so every call through it sees the commit before it, and an array of prose
|
|
151
|
+
* elements is one handle across the boundary rather than one per element. */
|
|
152
|
+
const reader = $derived(quill.reader(doc));
|
|
133
153
|
function contentAt(path: PathStep[]): Content | undefined {
|
|
134
|
-
return
|
|
154
|
+
return reader.getContentAt(addr, path);
|
|
135
155
|
}
|
|
136
156
|
|
|
137
157
|
// ── Focus: one answer, two callers (`leaves.ts`) ─────────────────────────────
|
|
@@ -142,28 +162,29 @@
|
|
|
142
162
|
// restores a selection, a date field lands on its first segment, an array lands on
|
|
143
163
|
// its first element or on the add affordance that is all an empty one has, an
|
|
144
164
|
// object on its first property.
|
|
165
|
+
/** What a container control exports: its focus, and the walk into it. */
|
|
166
|
+
type Inside = {
|
|
167
|
+
focus: () => void;
|
|
168
|
+
focusPath: (steps: readonly PathStep[], pos?: number) => LandingBox;
|
|
169
|
+
washBox?: () => HTMLElement | undefined;
|
|
170
|
+
};
|
|
145
171
|
let proseEl = $state<{ focus: () => void } | undefined>();
|
|
146
172
|
let dateEl = $state<{ focus: () => void } | undefined>();
|
|
147
|
-
let arrayEl = $state<
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
}
|
|
153
|
-
| undefined
|
|
154
|
-
>();
|
|
155
|
-
let objectEl = $state<{ focus: () => void } | undefined>();
|
|
173
|
+
let arrayEl = $state<Inside | undefined>();
|
|
174
|
+
let objectEl = $state<Inside | undefined>();
|
|
175
|
+
let variantEl = $state<Inside | undefined>();
|
|
176
|
+
let matrixEl = $state<Inside | undefined>();
|
|
177
|
+
const inside = (): Inside | undefined => arrayEl ?? objectEl ?? variantEl ?? matrixEl;
|
|
156
178
|
function focusControl(): void {
|
|
157
|
-
const owner = proseEl ?? dateEl ??
|
|
179
|
+
const owner = proseEl ?? dateEl ?? inside();
|
|
158
180
|
if (owner) return owner.focus();
|
|
159
181
|
document.getElementById(domIds.control)?.focus();
|
|
160
182
|
}
|
|
161
|
-
/** The
|
|
162
|
-
*
|
|
163
|
-
*
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
return arrayEl?.focusElement(k, pos);
|
|
183
|
+
/** The landing at depth, for the addresses the boundary mints under a container
|
|
184
|
+
* (`leaves.ts`); read at the call, so it tracks the mounted control. The innermost
|
|
185
|
+
* row it settled in comes back with it, the wash being the landing's own granularity. */
|
|
186
|
+
function focusPath(steps: readonly PathStep[], pos?: number): LandingBox {
|
|
187
|
+
return inside()?.focusPath(steps, pos);
|
|
167
188
|
}
|
|
168
189
|
// Only where `for` cannot reach; the labelable four are the browser's own, and a
|
|
169
190
|
// second handler over them would be a focus the label already placed.
|
|
@@ -173,17 +194,17 @@
|
|
|
173
194
|
* This field's landing handle. The wrapper is the bloom host rather than the
|
|
174
195
|
* control: `bloomInside` appends an inset child and an `<input>` holds none. The
|
|
175
196
|
* label is outside it, and stays out of the wash: an arrival marks where the caret
|
|
176
|
-
* landed, which is the control.
|
|
177
|
-
*
|
|
178
|
-
* `
|
|
197
|
+
* landed, which is the control. A control that owns its label (`ownsLabel`: the array
|
|
198
|
+
* and the matrix) holds that row inside itself, so it names its own box
|
|
199
|
+
* (`washBox`) and the bloom reads that in the wrapper's place.
|
|
179
200
|
*
|
|
180
201
|
* A prose leaf is absent here — it registers its own controller from inside
|
|
181
202
|
* `ProseField`, carrying the codec seam this handle has no half of — and reactive
|
|
182
203
|
* rather than mount-once, because a retype can swap the control under a leaf key
|
|
183
204
|
* that does not remount.
|
|
184
205
|
*
|
|
185
|
-
*
|
|
186
|
-
* an address to name. `el` is the field-wide box, which
|
|
206
|
+
* A container carries the walk into it as well; no other control has an inside for
|
|
207
|
+
* an address to name. `el` is the field-wide box, which a landing at depth does not
|
|
187
208
|
* wash: that lane hands back its own row (`leaves.ts`).
|
|
188
209
|
*/
|
|
189
210
|
let controlEl = $state<HTMLElement | undefined>();
|
|
@@ -194,9 +215,9 @@
|
|
|
194
215
|
const wrapper = controlEl;
|
|
195
216
|
registry.registerControl(key, {
|
|
196
217
|
focus: focusControl,
|
|
197
|
-
|
|
218
|
+
focusPath: isContainer(field.control) ? focusPath : undefined,
|
|
198
219
|
get el() {
|
|
199
|
-
return
|
|
220
|
+
return inside()?.washBox?.() ?? wrapper;
|
|
200
221
|
}
|
|
201
222
|
});
|
|
202
223
|
return () => registry.unregisterControl(key);
|
|
@@ -214,7 +235,7 @@
|
|
|
214
235
|
</script>
|
|
215
236
|
|
|
216
237
|
<div class="qm-field" class:cell={span === 'cell'}>
|
|
217
|
-
{#if
|
|
238
|
+
{#if !ownsLabel}
|
|
218
239
|
<FieldLabel
|
|
219
240
|
label={field.label}
|
|
220
241
|
controlId={labelable ? domIds.control : undefined}
|
|
@@ -239,6 +260,7 @@
|
|
|
239
260
|
{addr}
|
|
240
261
|
inline={field.inline}
|
|
241
262
|
plaintext={field.plaintext}
|
|
263
|
+
placeholder={defaultStr}
|
|
242
264
|
labelledBy={domIds.label}
|
|
243
265
|
{describedBy}
|
|
244
266
|
{leafKey}
|
|
@@ -261,6 +283,7 @@
|
|
|
261
283
|
/>
|
|
262
284
|
{:else if field.control === 'variant'}
|
|
263
285
|
<VariantField
|
|
286
|
+
bind:this={variantEl}
|
|
264
287
|
value={value as Record<string, unknown> | undefined}
|
|
265
288
|
schema={field.schema}
|
|
266
289
|
{ghostMember}
|
|
@@ -272,6 +295,7 @@
|
|
|
272
295
|
onCommit={onCommitScalar}
|
|
273
296
|
{optionAllowed}
|
|
274
297
|
{enumDisallowed}
|
|
298
|
+
diagnostics={deep}
|
|
275
299
|
/>
|
|
276
300
|
{:else if field.control === 'number'}
|
|
277
301
|
<NumberField
|
|
@@ -304,6 +328,8 @@
|
|
|
304
328
|
bind:this={arrayEl}
|
|
305
329
|
value={value as unknown[] | undefined}
|
|
306
330
|
items={field.schema.items}
|
|
331
|
+
layout={arrayLayout(field.schema)}
|
|
332
|
+
max={field.schema.max}
|
|
307
333
|
label={field.label}
|
|
308
334
|
required={field.required}
|
|
309
335
|
description={field.description}
|
|
@@ -312,6 +338,7 @@
|
|
|
312
338
|
idBase={domIds.control}
|
|
313
339
|
{contentAt}
|
|
314
340
|
onCommit={onCommitScalar}
|
|
341
|
+
diagnostics={deep}
|
|
315
342
|
/>
|
|
316
343
|
{:else if field.control === 'object'}
|
|
317
344
|
<ObjectField
|
|
@@ -324,6 +351,21 @@
|
|
|
324
351
|
{describedBy}
|
|
325
352
|
{contentAt}
|
|
326
353
|
onCommit={onCommitScalar}
|
|
354
|
+
diagnostics={deep}
|
|
355
|
+
/>
|
|
356
|
+
{:else if field.control === 'matrix'}
|
|
357
|
+
<MatrixField
|
|
358
|
+
bind:this={matrixEl}
|
|
359
|
+
value={value as Record<string, unknown> | undefined}
|
|
360
|
+
schema={field.schema}
|
|
361
|
+
label={field.label}
|
|
362
|
+
description={field.description}
|
|
363
|
+
labelId={domIds.label}
|
|
364
|
+
descriptionId={domIds.description}
|
|
365
|
+
idBase={domIds.control}
|
|
366
|
+
{contentAt}
|
|
367
|
+
onCommit={onCommitScalar}
|
|
368
|
+
diagnostics={deep}
|
|
327
369
|
/>
|
|
328
370
|
{:else}
|
|
329
371
|
<TextField
|
|
@@ -336,7 +378,9 @@
|
|
|
336
378
|
{/if}
|
|
337
379
|
</div>
|
|
338
380
|
|
|
339
|
-
|
|
381
|
+
{#if !isContainer(field.control)}
|
|
382
|
+
<DiagnosticList {diagnostics} />
|
|
383
|
+
{/if}
|
|
340
384
|
</div>
|
|
341
385
|
</div>
|
|
342
386
|
|
|
@@ -131,7 +131,7 @@
|
|
|
131
131
|
}}><Icon name="info" size={13} /></button
|
|
132
132
|
>
|
|
133
133
|
|
|
134
|
-
<!-- Placement is the description'
|
|
134
|
+
<!-- Placement is the description's height. One line rides the label rung beside its own
|
|
135
135
|
glyph, where the overhang past the rung's line box falls in the label-to-control gaps.
|
|
136
136
|
Anything taller stands over the field: beside the glyph it would hang onto the control
|
|
137
137
|
it describes, the one overlap a reader cannot clear by reaching for what is hidden,
|