@quillmark/svelte 0.16.0 → 0.18.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 +4 -2
- package/dist/core/codec/index.d.ts +0 -3
- package/dist/core/rebind.svelte.js +5 -8
- package/dist/core/theme.css +12 -1
- package/dist/preset/index.d.ts +1 -0
- package/dist/preview/Preview.svelte +14 -19
- package/dist/preview/Preview.svelte.d.ts +3 -3
- package/dist/preview/index.d.ts +0 -1
- package/dist/visual/ArrayField.svelte +44 -41
- package/dist/visual/ArrayField.svelte.d.ts +0 -1
- package/dist/visual/BooleanField.svelte +16 -14
- package/dist/visual/BooleanField.svelte.d.ts +0 -1
- package/dist/visual/Card.svelte +65 -35
- package/dist/visual/CardControls.svelte.d.ts +0 -1
- package/dist/visual/DateField.svelte +14 -17
- package/dist/visual/DateField.svelte.d.ts +0 -1
- package/dist/visual/EnumField.svelte +9 -12
- package/dist/visual/EnumField.svelte.d.ts +0 -1
- package/dist/visual/Field.svelte +17 -10
- package/dist/visual/Field.svelte.d.ts +3 -0
- package/dist/visual/FieldHint.svelte.d.ts +0 -1
- package/dist/visual/FieldLabel.svelte +1 -2
- package/dist/visual/FormatPopover.svelte +22 -27
- package/dist/visual/FormatPopover.svelte.d.ts +0 -1
- package/dist/visual/MatrixAdd.svelte +210 -0
- package/dist/visual/MatrixAdd.svelte.d.ts +15 -0
- package/dist/visual/MatrixField.svelte +329 -84
- package/dist/visual/MatrixField.svelte.d.ts +8 -6
- package/dist/visual/NumberField.svelte +6 -10
- package/dist/visual/NumberField.svelte.d.ts +0 -1
- package/dist/visual/ObjectField.svelte +30 -17
- package/dist/visual/ObjectField.svelte.d.ts +3 -1
- package/dist/visual/ProseField.svelte +42 -41
- package/dist/visual/ProseField.svelte.d.ts +0 -1
- package/dist/visual/ProseValue.svelte +56 -56
- package/dist/visual/ProseValue.svelte.d.ts +0 -1
- package/dist/visual/SlashMenu.svelte +5 -15
- package/dist/visual/SlashMenu.svelte.d.ts +0 -1
- package/dist/visual/TextField.svelte +5 -9
- package/dist/visual/TextField.svelte.d.ts +0 -1
- package/dist/visual/TipsCard.svelte +10 -11
- package/dist/visual/TipsCard.svelte.d.ts +0 -1
- package/dist/visual/VisualEditorInner.svelte +21 -14
- package/dist/visual/VisualEditorInner.svelte.d.ts +0 -1
- package/dist/visual/checklists.d.ts +82 -0
- package/dist/visual/checklists.js +171 -0
- package/dist/visual/index.d.ts +0 -1
- package/dist/visual/strings.d.ts +15 -1
- package/dist/visual/strings.js +6 -0
- package/dist/visual/structure.d.ts +63 -31
- package/dist/visual/structure.js +122 -44
- package/package.json +20 -16
- package/dist/visual/synced.svelte.d.ts +0 -15
- package/dist/visual/synced.svelte.js +0 -34
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
An open matrix's add box (VISUAL_EDITOR §"The matrix"): a combobox whose options are
|
|
3
|
+
what the typed words name across the document's checklists, best first, and last the
|
|
4
|
+
item they would add to this one. The list stands in flow under the input rather than
|
|
5
|
+
floating, so a closing group panel clips nothing of it, and it closes while the focus
|
|
6
|
+
is elsewhere. The input keeps the caret throughout and the active option is
|
|
7
|
+
`aria-activedescendant`, the combobox pattern: ↑/↓ walk, Enter picks, Escape clears.
|
|
8
|
+
Unwalked, the active option is the first result the words name outright, else the add
|
|
9
|
+
option: a result named only by abbreviation, which may stand in another card, is
|
|
10
|
+
taken by an arrow or a press and never by Enter alone.
|
|
11
|
+
-->
|
|
12
|
+
<script lang="ts">
|
|
13
|
+
import Icon from './icons/Icon.svelte';
|
|
14
|
+
import type { AddOption } from './checklists.js';
|
|
15
|
+
import './controls.css';
|
|
16
|
+
|
|
17
|
+
interface Props {
|
|
18
|
+
/** The input's id, and the base its listbox and options derive theirs from. */
|
|
19
|
+
id: string;
|
|
20
|
+
/** The input's accessible name and its words at rest. */
|
|
21
|
+
placeholder: string;
|
|
22
|
+
/** The options `query` offers, best first; the add option, where there is one, last. */
|
|
23
|
+
options: (query: string) => AddOption[];
|
|
24
|
+
onChoose: (option: AddOption, query: string) => void;
|
|
25
|
+
}
|
|
26
|
+
let { id, placeholder, options, onChoose }: Props = $props();
|
|
27
|
+
|
|
28
|
+
let query = $state('');
|
|
29
|
+
/** The walked option, `undefined` until an arrow or the pointer moves it. */
|
|
30
|
+
let walked = $state<number | undefined>();
|
|
31
|
+
let focused = $state(false);
|
|
32
|
+
let inputEl = $state<HTMLInputElement | undefined>();
|
|
33
|
+
const shown = $derived(query.trim() ? options(query.trim()) : []);
|
|
34
|
+
const open = $derived(focused && shown.length > 0);
|
|
35
|
+
const active = $derived.by(() => {
|
|
36
|
+
if (walked !== undefined) return Math.min(walked, shown.length - 1);
|
|
37
|
+
const strong = shown.findIndex((o) => o.strong);
|
|
38
|
+
if (strong >= 0) return strong;
|
|
39
|
+
const add = shown.findIndex((o) => o.add);
|
|
40
|
+
return add >= 0 ? add : 0;
|
|
41
|
+
});
|
|
42
|
+
const listId = $derived(`${id}-list`);
|
|
43
|
+
const optionId = (k: number): string => `${id}-opt-${k}`;
|
|
44
|
+
|
|
45
|
+
export function focus(): void {
|
|
46
|
+
inputEl?.focus();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function choose(k: number): void {
|
|
50
|
+
const option = shown[k];
|
|
51
|
+
if (!option) return;
|
|
52
|
+
const typed = query.trim();
|
|
53
|
+
query = '';
|
|
54
|
+
walked = undefined;
|
|
55
|
+
onChoose(option, typed);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function onkeydown(e: KeyboardEvent): void {
|
|
59
|
+
if (e.isComposing) return;
|
|
60
|
+
if (e.key === 'ArrowDown' && open) {
|
|
61
|
+
e.preventDefault();
|
|
62
|
+
walked = (active + 1) % shown.length;
|
|
63
|
+
} else if (e.key === 'ArrowUp' && open) {
|
|
64
|
+
e.preventDefault();
|
|
65
|
+
walked = (active - 1 + shown.length) % shown.length;
|
|
66
|
+
} else if (e.key === 'Enter' && open) {
|
|
67
|
+
e.preventDefault();
|
|
68
|
+
choose(active);
|
|
69
|
+
} else if (e.key === 'Escape' && query) {
|
|
70
|
+
e.preventDefault();
|
|
71
|
+
e.stopPropagation();
|
|
72
|
+
query = '';
|
|
73
|
+
walked = undefined;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
</script>
|
|
77
|
+
|
|
78
|
+
<div class="qm-matrix-add">
|
|
79
|
+
<span class="qm-matrix-add-field">
|
|
80
|
+
<Icon name="plus" class="qm-matrix-add-glyph" />
|
|
81
|
+
<input
|
|
82
|
+
bind:this={inputEl}
|
|
83
|
+
{id}
|
|
84
|
+
class="qm-input qm-focus-ring"
|
|
85
|
+
type="text"
|
|
86
|
+
role="combobox"
|
|
87
|
+
autocomplete="off"
|
|
88
|
+
aria-label={placeholder}
|
|
89
|
+
aria-autocomplete="list"
|
|
90
|
+
aria-expanded={open}
|
|
91
|
+
aria-controls={listId}
|
|
92
|
+
aria-activedescendant={open ? optionId(active) : undefined}
|
|
93
|
+
{placeholder}
|
|
94
|
+
value={query}
|
|
95
|
+
oninput={(e) => {
|
|
96
|
+
query = e.currentTarget.value;
|
|
97
|
+
walked = undefined;
|
|
98
|
+
}}
|
|
99
|
+
onfocus={() => (focused = true)}
|
|
100
|
+
onblur={() => (focused = false)}
|
|
101
|
+
{onkeydown}
|
|
102
|
+
/>
|
|
103
|
+
</span>
|
|
104
|
+
<!-- Always mounted, so `aria-controls` names an element at rest too; empty, it draws
|
|
105
|
+
nothing. -->
|
|
106
|
+
<div
|
|
107
|
+
class="qm-menu-surface qm-matrix-add-list"
|
|
108
|
+
role="listbox"
|
|
109
|
+
id={listId}
|
|
110
|
+
aria-label={placeholder}
|
|
111
|
+
hidden={!open}
|
|
112
|
+
>
|
|
113
|
+
{#each shown as option, k (option.key)}
|
|
114
|
+
<!-- The input keeps focus and owns the keys; a press picks without taking it. -->
|
|
115
|
+
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
116
|
+
<div
|
|
117
|
+
class={['qm-menu-item qm-matrix-add-option', { add: option.add }]}
|
|
118
|
+
role="option"
|
|
119
|
+
id={optionId(k)}
|
|
120
|
+
tabindex="-1"
|
|
121
|
+
aria-selected={k === active}
|
|
122
|
+
data-highlighted={k === active ? '' : undefined}
|
|
123
|
+
onpointerdown={(e) => e.preventDefault()}
|
|
124
|
+
onpointermove={() => (walked = k)}
|
|
125
|
+
onclick={() => choose(k)}
|
|
126
|
+
>
|
|
127
|
+
{#if option.add}<Icon name="plus" class="qm-matrix-add-glyph" />{/if}
|
|
128
|
+
<span class="qm-matrix-add-title">{option.title}</span>
|
|
129
|
+
{#if option.note}<span class="qm-matrix-add-note">{option.note}</span>{/if}
|
|
130
|
+
</div>
|
|
131
|
+
{/each}
|
|
132
|
+
</div>
|
|
133
|
+
</div>
|
|
134
|
+
|
|
135
|
+
<style>
|
|
136
|
+
/* The box's text, and every option's, starts past the glyph slot at its leading edge,
|
|
137
|
+
so a result reads in the column the typed words stand in and the add option's `+`
|
|
138
|
+
stands under the box's. */
|
|
139
|
+
.qm-matrix-add {
|
|
140
|
+
--_slot: calc(var(--_qm-space-2) * 2 + var(--_qm-glyph-control));
|
|
141
|
+
display: flex;
|
|
142
|
+
flex-direction: column;
|
|
143
|
+
gap: var(--_qm-space-half);
|
|
144
|
+
min-width: 0;
|
|
145
|
+
}
|
|
146
|
+
.qm-matrix-add-field {
|
|
147
|
+
position: relative;
|
|
148
|
+
display: flex;
|
|
149
|
+
align-items: center;
|
|
150
|
+
min-width: 0;
|
|
151
|
+
}
|
|
152
|
+
.qm-matrix-add :global(.qm-matrix-add-glyph) {
|
|
153
|
+
position: absolute;
|
|
154
|
+
inset-inline-start: var(--_qm-space-2);
|
|
155
|
+
width: var(--_qm-glyph-control);
|
|
156
|
+
height: var(--_qm-glyph-control);
|
|
157
|
+
color: var(--_qm-ink-label);
|
|
158
|
+
pointer-events: none;
|
|
159
|
+
}
|
|
160
|
+
/* At rest the box is the add chip it stands in for: no well, its words at the label
|
|
161
|
+
ink. The well comes up under the pointer, and stays while it holds the focus or
|
|
162
|
+
typed words. */
|
|
163
|
+
.qm-matrix-add-field .qm-input {
|
|
164
|
+
flex: 1;
|
|
165
|
+
min-width: 0;
|
|
166
|
+
padding-inline-start: var(--_slot);
|
|
167
|
+
transition: background-color var(--_qm-duration-fast) var(--_qm-ease-reverse);
|
|
168
|
+
}
|
|
169
|
+
.qm-matrix-add-field .qm-input:placeholder-shown:not(:focus) {
|
|
170
|
+
background: transparent;
|
|
171
|
+
}
|
|
172
|
+
.qm-matrix-add-field .qm-input:placeholder-shown:not(:focus):hover {
|
|
173
|
+
background: var(--_qm-surface-hover);
|
|
174
|
+
}
|
|
175
|
+
/* The list is a menu, raised over the card rather than cut into it, so its options
|
|
176
|
+
read as choices and not as a second field. Its edge and inset come off each
|
|
177
|
+
option's, which keeps the column. */
|
|
178
|
+
.qm-matrix-add-list {
|
|
179
|
+
display: flex;
|
|
180
|
+
flex-direction: column;
|
|
181
|
+
}
|
|
182
|
+
.qm-matrix-add-list[hidden] {
|
|
183
|
+
display: none;
|
|
184
|
+
}
|
|
185
|
+
.qm-matrix-add-option {
|
|
186
|
+
position: relative;
|
|
187
|
+
display: flex;
|
|
188
|
+
align-items: baseline;
|
|
189
|
+
justify-content: space-between;
|
|
190
|
+
gap: var(--_qm-space-2);
|
|
191
|
+
padding-inline-start: calc(var(--_slot) - var(--_qm-space-half) - var(--_qm-border-width));
|
|
192
|
+
font-size: var(--_qm-text-body);
|
|
193
|
+
color: var(--_qm-ink);
|
|
194
|
+
}
|
|
195
|
+
.qm-matrix-add-option :global(.qm-matrix-add-glyph) {
|
|
196
|
+
inset-inline-start: calc(var(--_qm-space-2) - var(--_qm-space-half) - var(--_qm-border-width));
|
|
197
|
+
inset-block-start: 50%;
|
|
198
|
+
translate: 0 -50%;
|
|
199
|
+
}
|
|
200
|
+
/* The add option reads at the label ink: it offers a new item, where a result names
|
|
201
|
+
one the document already knows. */
|
|
202
|
+
.qm-matrix-add-option.add {
|
|
203
|
+
color: var(--_qm-ink-label);
|
|
204
|
+
}
|
|
205
|
+
.qm-matrix-add-note {
|
|
206
|
+
flex-shrink: 0;
|
|
207
|
+
font-size: var(--_qm-text-label);
|
|
208
|
+
color: var(--_qm-ink-label);
|
|
209
|
+
}
|
|
210
|
+
</style>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { AddOption } from './checklists.js';
|
|
2
|
+
interface Props {
|
|
3
|
+
/** The input's id, and the base its listbox and options derive theirs from. */
|
|
4
|
+
id: string;
|
|
5
|
+
/** The input's accessible name and its words at rest. */
|
|
6
|
+
placeholder: string;
|
|
7
|
+
/** The options `query` offers, best first; the add option, where there is one, last. */
|
|
8
|
+
options: (query: string) => AddOption[];
|
|
9
|
+
onChoose: (option: AddOption, query: string) => void;
|
|
10
|
+
}
|
|
11
|
+
declare const MatrixAdd: import("svelte").Component<Props, {
|
|
12
|
+
focus: () => void;
|
|
13
|
+
}, "">;
|
|
14
|
+
type MatrixAdd = ReturnType<typeof MatrixAdd>;
|
|
15
|
+
export default MatrixAdd;
|