@quillmark/svelte 0.1.1 → 0.3.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 +16 -12
- package/THEMING.md +48 -25
- package/dist/core/address.d.ts +42 -7
- package/dist/core/address.js +49 -21
- package/dist/core/bloom.d.ts +5 -5
- package/dist/core/bloom.js +8 -8
- 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 +14 -11
- package/dist/core/codec/field.d.ts +22 -27
- 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 +266 -228
- 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 +73 -27
- package/dist/core/errors.d.ts +17 -7
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.js +6 -5
- package/dist/core/lifecycle.d.ts +21 -5
- package/dist/core/lifecycle.js +34 -8
- package/dist/core/rebind.svelte.d.ts +18 -4
- package/dist/core/rebind.svelte.js +30 -12
- package/dist/core/teardown.d.ts +1 -1
- package/dist/core/teardown.js +1 -1
- package/dist/core/theme.css +173 -58
- package/dist/preset/index.css +1 -1
- package/dist/preset/recipes.css +79 -30
- package/dist/preset/scale.css +40 -28
- package/dist/preview/Preview.svelte +22 -19
- package/dist/preview/Preview.svelte.d.ts +8 -8
- package/dist/preview/bridge.d.ts +6 -4
- package/dist/preview/bridge.js +19 -9
- package/dist/preview/controller.d.ts +13 -6
- package/dist/preview/controller.js +8 -5
- package/dist/preview/geometry.d.ts +17 -2
- package/dist/preview/geometry.js +21 -1
- package/dist/preview/index.d.ts +1 -0
- package/dist/preview/index.js +9 -2
- package/dist/preview/overlay.d.ts +2 -1
- package/dist/preview/overlay.js +26 -11
- package/dist/preview/paint.js +19 -17
- package/dist/visual/ArrayField.svelte +103 -83
- package/dist/visual/ArrayField.svelte.d.ts +5 -4
- package/dist/visual/BooleanField.svelte +8 -6
- package/dist/visual/BooleanField.svelte.d.ts +2 -2
- package/dist/visual/Card.svelte +161 -333
- package/dist/visual/Card.svelte.d.ts +5 -4
- 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 +2 -2
- package/dist/visual/EnumField.svelte +48 -28
- package/dist/visual/EnumField.svelte.d.ts +5 -4
- package/dist/visual/Field.svelte +207 -132
- package/dist/visual/Field.svelte.d.ts +11 -7
- package/dist/visual/FieldHint.svelte +79 -48
- package/dist/visual/FieldHint.svelte.d.ts +1 -1
- package/dist/visual/FieldLabel.svelte +8 -8
- package/dist/visual/FormatPopover.svelte +82 -35
- package/dist/visual/NumberField.svelte +5 -5
- package/dist/visual/NumberField.svelte.d.ts +1 -1
- package/dist/visual/ObjectField.svelte +28 -9
- package/dist/visual/ObjectField.svelte.d.ts +4 -2
- package/dist/visual/ProseArrayElement.svelte +12 -19
- package/dist/visual/ProseArrayElement.svelte.d.ts +1 -2
- package/dist/visual/ProseField.svelte +62 -85
- package/dist/visual/ProseField.svelte.d.ts +14 -17
- 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 +43 -28
- package/dist/visual/VisualEditor.svelte +18 -19
- package/dist/visual/VisualEditor.svelte.d.ts +2 -3
- package/dist/visual/VisualEditorInner.svelte +251 -184
- package/dist/visual/VisualEditorInner.svelte.d.ts +2 -3
- package/dist/visual/controls.css +85 -38
- package/dist/visual/diagnostics.d.ts +13 -21
- package/dist/visual/diagnostics.js +7 -19
- 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 +3 -0
- package/dist/visual/icons/nodes.js +2 -1
- package/dist/visual/index.d.ts +1 -0
- package/dist/visual/index.js +10 -3
- package/dist/visual/leaves.d.ts +47 -0
- package/dist/visual/leaves.js +31 -0
- package/dist/visual/links.d.ts +24 -0
- package/dist/visual/links.js +67 -0
- package/dist/visual/props.d.ts +33 -12
- 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 +4 -2
- package/dist/visual/tips.js +7 -4
- package/package.json +2 -2
package/dist/visual/Field.svelte
CHANGED
|
@@ -3,20 +3,20 @@
|
|
|
3
3
|
{@link FieldModel} and its live value, render the label + the control the type
|
|
4
4
|
maps to. Array controls own their label (paired with the add affordance in
|
|
5
5
|
{@link ArrayField}); other types render the label here. Prose leaves take a
|
|
6
|
-
parent-built
|
|
6
|
+
parent-built live `addr` (its `card` a getter over the stable-id→index map) so
|
|
7
7
|
a reorder re-targets without a remount; scalars, arrays, and objects commit
|
|
8
|
-
their value
|
|
8
|
+
their value up through `onCommitScalar`, which the parent lowers to the typed
|
|
9
9
|
writer.
|
|
10
10
|
|
|
11
11
|
`diagnostics` is the routed `Diagnostic[]` for this field (VisualEditor's
|
|
12
12
|
`diagByKey`, merging `quill.validate`, local commit errors, and the external
|
|
13
13
|
`diagnostics` prop (VISUAL_EDITOR §Diagnostics)) rendered via the shared
|
|
14
|
-
`DiagnosticList`, severity-styled,
|
|
14
|
+
`DiagnosticList`, severity-styled, non-gating.
|
|
15
15
|
-->
|
|
16
16
|
<script lang="ts">
|
|
17
17
|
import type { Document, Quill, Addr, Diagnostic, ResolvedField } from '@quillmark/wasm';
|
|
18
18
|
import type { EditorErrorHandler } from '../core/errors.js';
|
|
19
|
-
import type {
|
|
19
|
+
import type { LeafRegistry } from './leaves.js';
|
|
20
20
|
import type { FieldModel, FieldSpan } from './structure.js';
|
|
21
21
|
import { enumValues, ghostDefault, stringifyGhost } from './structure.js';
|
|
22
22
|
import type { FieldDomIds } from './domid.js';
|
|
@@ -42,22 +42,26 @@
|
|
|
42
42
|
doc: Document;
|
|
43
43
|
/** The schema a prose leaf reads its content through (`ProseField`). */
|
|
44
44
|
quill: Quill;
|
|
45
|
-
/**
|
|
46
|
-
|
|
45
|
+
/** This field's live address (getter-`card`, so a card reorder re-targets in
|
|
46
|
+
* place): the prose leaf commits to it, and a focus reports it. */
|
|
47
|
+
addr: Addr;
|
|
47
48
|
leafKey: string;
|
|
48
49
|
/** This field's three DOM names, derived from `leafKey` (see `domid.ts`): how
|
|
49
50
|
* the label and the control find each other. */
|
|
50
51
|
domIds: FieldDomIds;
|
|
51
52
|
onCommitScalar: (value: unknown) => void;
|
|
52
|
-
/** Enum-option policy: `false`
|
|
53
|
-
* control reads
|
|
53
|
+
/** Enum-option policy: `false` marks that option unavailable. Only the enum
|
|
54
|
+
* control reads this pair. */
|
|
54
55
|
optionAllowed?: (value: string) => boolean;
|
|
56
|
+
/** How a refused option draws: greyed (default) or left out. */
|
|
57
|
+
enumDisallowed?: 'hide' | 'disable';
|
|
55
58
|
onFocus?: (addr: Addr) => void;
|
|
56
59
|
onCaretMove?: (addr: Addr, pos: number) => void;
|
|
57
60
|
onChange?: (addr: Addr) => void;
|
|
58
61
|
onError?: EditorErrorHandler;
|
|
59
|
-
|
|
60
|
-
|
|
62
|
+
/** The editor's leaf registry (`leaves.ts`): a form control registers its
|
|
63
|
+
* landing handle here, a prose leaf its controller from inside `ProseField`. */
|
|
64
|
+
leaves?: LeafRegistry;
|
|
61
65
|
diagnostics?: Diagnostic[];
|
|
62
66
|
}
|
|
63
67
|
let {
|
|
@@ -67,17 +71,17 @@
|
|
|
67
71
|
provenance,
|
|
68
72
|
doc,
|
|
69
73
|
quill,
|
|
70
|
-
|
|
74
|
+
addr,
|
|
71
75
|
leafKey,
|
|
72
76
|
domIds,
|
|
73
77
|
onCommitScalar,
|
|
74
78
|
optionAllowed,
|
|
79
|
+
enumDisallowed,
|
|
75
80
|
onFocus,
|
|
76
81
|
onCaretMove,
|
|
77
82
|
onChange,
|
|
78
83
|
onError,
|
|
79
|
-
|
|
80
|
-
unregister,
|
|
84
|
+
leaves,
|
|
81
85
|
diagnostics
|
|
82
86
|
}: Props = $props();
|
|
83
87
|
|
|
@@ -88,7 +92,7 @@
|
|
|
88
92
|
const ghost = $derived(ghostDefault(provenance));
|
|
89
93
|
const defaultStr = $derived(stringifyGhost(ghost));
|
|
90
94
|
|
|
91
|
-
// `for` reaches a
|
|
95
|
+
// `for` reaches a labelable control and the browser does the rest. The other four
|
|
92
96
|
// are not labelable (the prose leaf's `contenteditable`, the date field's segment
|
|
93
97
|
// container, the object subform, an array's N inputs) so `for` there would be
|
|
94
98
|
// both inert and invalid markup; they take `aria-labelledby` and a click handoff.
|
|
@@ -103,18 +107,66 @@
|
|
|
103
107
|
// nothing, and silently.
|
|
104
108
|
const describedBy = $derived(field.description ? domIds.description : undefined);
|
|
105
109
|
|
|
106
|
-
// Focus
|
|
107
|
-
//
|
|
108
|
-
//
|
|
110
|
+
// ── Focus: one answer, two callers (`leaves.ts`) ─────────────────────────────
|
|
111
|
+
// A label click and the editor's `focusField`/`setCaret` ask the same question, so
|
|
112
|
+
// they read the same function and cannot land in different places. The four
|
|
113
|
+
// labelable controls are reached through the DOM id `for` already points at; the
|
|
114
|
+
// rest own what focusing means and expose `focus()`, because it differs: a PM view
|
|
115
|
+
// restores a selection, a date field lands on its first segment, an array lands on
|
|
116
|
+
// its first element or on the add affordance that is all an empty one has, an
|
|
117
|
+
// object on its first property.
|
|
109
118
|
let proseEl = $state<{ focus: () => void } | undefined>();
|
|
110
119
|
let dateEl = $state<{ focus: () => void } | undefined>();
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
120
|
+
let arrayEl = $state<{ focus: () => void; focusElement: (k: number) => void } | undefined>();
|
|
121
|
+
let objectEl = $state<{ focus: () => void } | undefined>();
|
|
122
|
+
function focusControl(): void {
|
|
123
|
+
const owner = proseEl ?? dateEl ?? arrayEl ?? objectEl;
|
|
124
|
+
if (owner) return owner.focus();
|
|
125
|
+
document.getElementById(domIds.control)?.focus();
|
|
126
|
+
}
|
|
127
|
+
/** The array's per-ELEMENT landing, for the addresses the preview mints under an
|
|
128
|
+
* array field (`leaves.ts`); read at the call, so it tracks the mounted repeater. */
|
|
129
|
+
function focusElement(k: number): void {
|
|
130
|
+
arrayEl?.focusElement(k);
|
|
131
|
+
}
|
|
132
|
+
// Only where `for` cannot reach; the labelable four are the browser's own, and a
|
|
133
|
+
// second handler over them would be a focus the label already placed.
|
|
134
|
+
const onActivate = $derived(labelable ? undefined : focusControl);
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* This field's landing handle. The wrapper is the bloom host rather than the
|
|
138
|
+
* control: `bloomInside` appends an inset child and an `<input>` holds none.
|
|
139
|
+
*
|
|
140
|
+
* A prose leaf is absent here — it registers its own controller from inside
|
|
141
|
+
* `ProseField`, carrying the codec seam this handle has no half of — and reactive
|
|
142
|
+
* rather than mount-once, because a retype can swap the control under a leaf key
|
|
143
|
+
* that does not remount.
|
|
144
|
+
*
|
|
145
|
+
* An array carries the per-element lane as well; no other control has elements for
|
|
146
|
+
* an address to name.
|
|
147
|
+
*/
|
|
148
|
+
let controlEl = $state<HTMLElement | undefined>();
|
|
149
|
+
$effect(() => {
|
|
150
|
+
if (field.control === 'prose' || !controlEl || !leaves) return;
|
|
151
|
+
const key = leafKey;
|
|
152
|
+
const registry = leaves;
|
|
153
|
+
registry.registerControl(key, {
|
|
154
|
+
focus: focusControl,
|
|
155
|
+
focusElement: field.control === 'array' ? focusElement : undefined,
|
|
156
|
+
el: controlEl
|
|
157
|
+
});
|
|
158
|
+
return () => registry.unregisterControl(key);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* A form control has no controller to report its focus through, so the wrapper
|
|
163
|
+
* reports: `focusin` bubbles, so one handler covers a plain input, an array's N
|
|
164
|
+
* elements and an object's properties alike, and the active leaf names a scalar
|
|
165
|
+
* field the way it names a prose one. A prose leaf reports through its own
|
|
166
|
+
* controller — the source that drives its caret signals too — and is excluded here
|
|
167
|
+
* rather than counted twice.
|
|
168
|
+
*/
|
|
169
|
+
const reportFocus = $derived(field.control === 'prose' ? undefined : () => onFocus?.(addr));
|
|
118
170
|
</script>
|
|
119
171
|
|
|
120
172
|
<div class="qm-field" class:cell={span === 'cell'} class:lone={span === 'lone'}>
|
|
@@ -129,139 +181,162 @@
|
|
|
129
181
|
description={field.description}
|
|
130
182
|
/>
|
|
131
183
|
{/if}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
184
|
+
<!-- The control and what hangs under it, in one grid cell: the diagnostics stack
|
|
185
|
+
inside the control's track rather than claiming a track of their own (the subgrid
|
|
186
|
+
rule below holds why). One markup for both spans; a field owning its row nests a
|
|
187
|
+
level and measures the same. -->
|
|
188
|
+
<div class="qm-field-stack">
|
|
189
|
+
<div class="qm-field-control" bind:this={controlEl} onfocusin={reportFocus}>
|
|
190
|
+
{#if field.control === 'prose'}
|
|
191
|
+
<ProseField
|
|
192
|
+
{quill}
|
|
193
|
+
bind:this={proseEl}
|
|
194
|
+
{doc}
|
|
195
|
+
{addr}
|
|
196
|
+
inline={field.inline}
|
|
197
|
+
plaintext={field.plaintext}
|
|
198
|
+
labelledBy={domIds.label}
|
|
199
|
+
{describedBy}
|
|
200
|
+
{leafKey}
|
|
201
|
+
{onFocus}
|
|
202
|
+
{onCaretMove}
|
|
203
|
+
{onChange}
|
|
204
|
+
{onError}
|
|
205
|
+
{leaves}
|
|
206
|
+
/>
|
|
207
|
+
{:else if field.control === 'enum'}
|
|
208
|
+
<EnumField
|
|
209
|
+
value={value as string | undefined}
|
|
210
|
+
values={enumValues(field.schema) ?? []}
|
|
211
|
+
fallback={ghost as string | undefined}
|
|
212
|
+
id={domIds.control}
|
|
213
|
+
{describedBy}
|
|
214
|
+
onCommit={onCommitScalar}
|
|
215
|
+
{optionAllowed}
|
|
216
|
+
{enumDisallowed}
|
|
217
|
+
/>
|
|
218
|
+
{:else if field.control === 'number'}
|
|
219
|
+
<NumberField
|
|
220
|
+
value={value as number | undefined}
|
|
221
|
+
integer={field.schema.type === 'integer'}
|
|
222
|
+
fallback={ghost as number | undefined}
|
|
223
|
+
id={domIds.control}
|
|
224
|
+
{describedBy}
|
|
225
|
+
onCommit={onCommitScalar}
|
|
226
|
+
/>
|
|
227
|
+
{:else if field.control === 'boolean'}
|
|
228
|
+
<BooleanField
|
|
229
|
+
value={value as boolean | undefined}
|
|
230
|
+
fallback={ghost as boolean | undefined}
|
|
231
|
+
id={domIds.control}
|
|
232
|
+
{describedBy}
|
|
233
|
+
onCommit={onCommitScalar}
|
|
234
|
+
/>
|
|
235
|
+
{:else if field.control === 'date'}
|
|
236
|
+
<DateField
|
|
237
|
+
bind:this={dateEl}
|
|
238
|
+
value={value as string | undefined}
|
|
239
|
+
fallback={defaultStr}
|
|
240
|
+
labelledBy={domIds.label}
|
|
241
|
+
{describedBy}
|
|
242
|
+
onCommit={onCommitScalar}
|
|
243
|
+
/>
|
|
244
|
+
{:else if field.control === 'array'}
|
|
245
|
+
<ArrayField
|
|
246
|
+
bind:this={arrayEl}
|
|
247
|
+
value={value as unknown[] | undefined}
|
|
248
|
+
items={field.schema.items}
|
|
249
|
+
label={field.label}
|
|
250
|
+
required={field.required}
|
|
251
|
+
description={field.description}
|
|
252
|
+
labelId={domIds.label}
|
|
253
|
+
descriptionId={domIds.description}
|
|
254
|
+
onCommit={onCommitScalar}
|
|
255
|
+
/>
|
|
256
|
+
{:else if field.control === 'object'}
|
|
257
|
+
<ObjectField
|
|
258
|
+
bind:this={objectEl}
|
|
259
|
+
value={value as Record<string, unknown> | undefined}
|
|
260
|
+
properties={field.schema.properties}
|
|
261
|
+
label={field.label}
|
|
262
|
+
labelledBy={domIds.label}
|
|
263
|
+
{describedBy}
|
|
264
|
+
onCommit={onCommitScalar}
|
|
265
|
+
/>
|
|
266
|
+
{:else}
|
|
267
|
+
<TextField
|
|
268
|
+
value={value as string | undefined}
|
|
269
|
+
placeholder={defaultStr}
|
|
270
|
+
id={domIds.control}
|
|
271
|
+
{describedBy}
|
|
272
|
+
onCommit={onCommitScalar}
|
|
273
|
+
/>
|
|
274
|
+
{/if}
|
|
275
|
+
</div>
|
|
218
276
|
|
|
219
|
-
|
|
277
|
+
<DiagnosticList {diagnostics} />
|
|
278
|
+
</div>
|
|
220
279
|
</div>
|
|
221
280
|
|
|
222
281
|
<style>
|
|
223
282
|
/* The `full` span, and the base every field starts from: its own row, a plain
|
|
224
283
|
stack. Nothing shares the row, so there are no internals to align against.
|
|
225
284
|
|
|
226
|
-
|
|
227
|
-
a row action
|
|
228
|
-
|
|
229
|
-
for the whole grid, which reaches only a field that stops short of it. The array
|
|
230
|
-
carries the only row action there is, and it always owns its row (structure.ts
|
|
231
|
-
`packable`), so it spans that reservation with every other column: the one field
|
|
232
|
-
a section could reserve for is the one it would miss. Per field the reservation
|
|
233
|
-
travels with the thing that uses it: one right edge per track, every control in a
|
|
234
|
-
row ending on it, and an array reaching back across its own to put its remove
|
|
235
|
-
past it (ArrayField). Every field pays the width, including one with no action in
|
|
236
|
-
it; a right edge that moved with a field's contents is the raggedness this
|
|
237
|
-
removes. */
|
|
285
|
+
No inset at the end: the right edge is the track's and every control in a row ends
|
|
286
|
+
on it, a row action sitting inside the element that carries it (ArrayField) rather
|
|
287
|
+
than in a column every field would hold clear. */
|
|
238
288
|
.qm-field {
|
|
239
289
|
display: flex;
|
|
240
290
|
flex-direction: column;
|
|
241
291
|
gap: var(--_qm-space);
|
|
242
292
|
grid-column: 1 / -1;
|
|
243
293
|
min-width: 0;
|
|
244
|
-
padding-right: var(--action-col);
|
|
245
294
|
}
|
|
246
295
|
/* A row-sharing field subgrids onto the section's row tracks instead of sizing its
|
|
247
|
-
own:
|
|
248
|
-
control in a visual row starts at the same y however tall a neighbour's
|
|
249
|
-
wrapped
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
296
|
+
own: two tracks, the label and the control's stack, taken from the parent, so
|
|
297
|
+
every control in a visual row starts at the same y however tall a neighbour's
|
|
298
|
+
label wrapped. Source order is track order; `align-items: start` keeps a short
|
|
299
|
+
control from stretching to a taller sibling's track. `row-gap` overrides the
|
|
300
|
+
section's inter-row gutter for the tracks this field spans: inside a field the
|
|
301
|
+
rhythm is tighter than between rows.
|
|
302
|
+
|
|
303
|
+
Two tracks, and not a third for the diagnostics: a track is permanent where a
|
|
304
|
+
diagnostic is occasional, and an empty track still costs the gutter above it, so
|
|
305
|
+
a third stands a rung of dead space under every field on the path where nothing
|
|
306
|
+
is wrong. That rung is the one place a field's box outruns its ink. It is what
|
|
307
|
+
makes a run of row-sharing fields read looser than the arrays and prose leaves
|
|
308
|
+
beside them, whose boxes end where their ink does. What a third track buys is the
|
|
309
|
+
diagnostics of one row starting at one y; the alignment a row is read by is the
|
|
310
|
+
controls', and the label track holds that. A diagnostic grows its row instead. */
|
|
254
311
|
.qm-field.cell,
|
|
255
312
|
.qm-field.lone {
|
|
256
313
|
display: grid;
|
|
257
|
-
grid-row: span
|
|
314
|
+
grid-row: span 2;
|
|
258
315
|
grid-template-rows: subgrid;
|
|
259
316
|
row-gap: var(--_qm-space);
|
|
260
317
|
align-items: start;
|
|
261
318
|
}
|
|
319
|
+
/* The stack the control's track holds: the control, and the diagnostics when
|
|
320
|
+
there are any. Its gap is the field's own, so a diagnostic sits the same rung
|
|
321
|
+
under its control that the control sits under its label. */
|
|
322
|
+
.qm-field-stack {
|
|
323
|
+
display: flex;
|
|
324
|
+
flex-direction: column;
|
|
325
|
+
gap: var(--_qm-space);
|
|
326
|
+
min-width: 0;
|
|
327
|
+
}
|
|
262
328
|
.qm-field.cell {
|
|
263
329
|
grid-column: span 1;
|
|
264
330
|
}
|
|
331
|
+
/* Positioned for the arrival wash a landing inserts (`core/bloom.ts`), the way
|
|
332
|
+
`.qm-prose` is: an inset child over the control, since an `<input>` takes none.
|
|
333
|
+
The radius is the control's own, so the wash's corners are the box's rather than
|
|
334
|
+
square over a rounded one; on an array or a subform it bounds a group, where
|
|
335
|
+
there is no single box for it to disagree with. */
|
|
336
|
+
.qm-field-control {
|
|
337
|
+
position: relative;
|
|
338
|
+
border-radius: var(--_qm-radius-inner);
|
|
339
|
+
}
|
|
265
340
|
/* A run of one takes half the capacity from column 1: `--cols-half` is the section's
|
|
266
341
|
capacity halved, so the edge lands on a track boundary at every capacity. */
|
|
267
342
|
.qm-field.lone {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Document, Quill, Addr, Diagnostic, ResolvedField } from '@quillmark/wasm';
|
|
2
2
|
import type { EditorErrorHandler } from '../core/errors.js';
|
|
3
|
-
import type {
|
|
3
|
+
import type { LeafRegistry } from './leaves.js';
|
|
4
4
|
import type { FieldModel, FieldSpan } from './structure.js';
|
|
5
5
|
import type { FieldDomIds } from './domid.js';
|
|
6
6
|
interface Props {
|
|
@@ -14,22 +14,26 @@ interface Props {
|
|
|
14
14
|
doc: Document;
|
|
15
15
|
/** The schema a prose leaf reads its content through (`ProseField`). */
|
|
16
16
|
quill: Quill;
|
|
17
|
-
/**
|
|
18
|
-
|
|
17
|
+
/** This field's live address (getter-`card`, so a card reorder re-targets in
|
|
18
|
+
* place): the prose leaf commits to it, and a focus reports it. */
|
|
19
|
+
addr: Addr;
|
|
19
20
|
leafKey: string;
|
|
20
21
|
/** This field's three DOM names, derived from `leafKey` (see `domid.ts`): how
|
|
21
22
|
* the label and the control find each other. */
|
|
22
23
|
domIds: FieldDomIds;
|
|
23
24
|
onCommitScalar: (value: unknown) => void;
|
|
24
|
-
/** Enum-option policy: `false`
|
|
25
|
-
* control reads
|
|
25
|
+
/** Enum-option policy: `false` marks that option unavailable. Only the enum
|
|
26
|
+
* control reads this pair. */
|
|
26
27
|
optionAllowed?: (value: string) => boolean;
|
|
28
|
+
/** How a refused option draws: greyed (default) or left out. */
|
|
29
|
+
enumDisallowed?: 'hide' | 'disable';
|
|
27
30
|
onFocus?: (addr: Addr) => void;
|
|
28
31
|
onCaretMove?: (addr: Addr, pos: number) => void;
|
|
29
32
|
onChange?: (addr: Addr) => void;
|
|
30
33
|
onError?: EditorErrorHandler;
|
|
31
|
-
|
|
32
|
-
|
|
34
|
+
/** The editor's leaf registry (`leaves.ts`): a form control registers its
|
|
35
|
+
* landing handle here, a prose leaf its controller from inside `ProseField`. */
|
|
36
|
+
leaves?: LeafRegistry;
|
|
33
37
|
diagnostics?: Diagnostic[];
|
|
34
38
|
}
|
|
35
39
|
declare const Field: import("svelte").Component<Props, {}, "">;
|