@quillmark/svelte 0.20.1 → 0.22.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.
Files changed (85) hide show
  1. package/NOTICE +6 -7
  2. package/README.md +1 -1
  3. package/THEMING.md +1 -1
  4. package/dist/core/address.d.ts +6 -1
  5. package/dist/core/address.js +12 -3
  6. package/dist/core/bloom.d.ts +9 -4
  7. package/dist/core/bloom.js +18 -49
  8. package/dist/core/codec/atoms.d.ts +1 -1
  9. package/dist/core/codec/atoms.js +26 -11
  10. package/dist/core/codec/blocks.d.ts +5 -5
  11. package/dist/core/codec/blocks.js +27 -8
  12. package/dist/core/codec/code.d.ts +4 -4
  13. package/dist/core/codec/code.js +52 -22
  14. package/dist/core/codec/decode.d.ts +5 -5
  15. package/dist/core/codec/decode.js +18 -12
  16. package/dist/core/codec/encode.js +6 -1
  17. package/dist/core/codec/field.d.ts +22 -15
  18. package/dist/core/codec/field.js +66 -26
  19. package/dist/core/codec/index.d.ts +1 -1
  20. package/dist/core/codec/index.js +2 -2
  21. package/dist/core/codec/inputrules.d.ts +4 -0
  22. package/dist/core/codec/inputrules.js +44 -6
  23. package/dist/core/codec/link-paste.d.ts +11 -0
  24. package/dist/core/codec/link-paste.js +32 -0
  25. package/dist/core/codec/lists.d.ts +5 -4
  26. package/dist/core/codec/lists.js +87 -31
  27. package/dist/core/codec/nesting.d.ts +20 -0
  28. package/dist/core/codec/nesting.js +62 -0
  29. package/dist/core/codec/prose.css +145 -63
  30. package/dist/core/codec/schema.d.ts +2 -2
  31. package/dist/core/codec/schema.js +71 -18
  32. package/dist/core/codec/slash.js +0 -0
  33. package/dist/core/codec/table-copy.d.ts +17 -0
  34. package/dist/core/codec/table-copy.js +84 -0
  35. package/dist/core/codec/table-view.d.ts +6 -7
  36. package/dist/core/codec/table-view.js +281 -201
  37. package/dist/core/codec/table.d.ts +2 -9
  38. package/dist/core/codec/table.js +10 -19
  39. package/dist/core/codec/tasks.d.ts +19 -0
  40. package/dist/core/codec/tasks.js +140 -0
  41. package/dist/core/errors.d.ts +2 -2
  42. package/dist/core/rungs.d.ts +6 -0
  43. package/dist/core/rungs.js +11 -0
  44. package/dist/core/theme.css +24 -12
  45. package/dist/visual/ArrayField.svelte +236 -78
  46. package/dist/visual/ArrayField.svelte.d.ts +0 -2
  47. package/dist/visual/BooleanField.svelte +12 -1
  48. package/dist/visual/Card.svelte +41 -23
  49. package/dist/visual/Card.svelte.d.ts +3 -2
  50. package/dist/visual/CardControls.svelte +15 -15
  51. package/dist/visual/DateField.svelte +93 -64
  52. package/dist/visual/EnumField.svelte +9 -4
  53. package/dist/visual/Field.svelte +20 -16
  54. package/dist/visual/Field.svelte.d.ts +4 -2
  55. package/dist/visual/FieldHint.svelte +22 -5
  56. package/dist/visual/FieldLabel.svelte +5 -33
  57. package/dist/visual/FieldLabel.svelte.d.ts +0 -2
  58. package/dist/visual/FormatPopover.svelte +78 -81
  59. package/dist/visual/MatrixField.svelte +28 -19
  60. package/dist/visual/NumberField.svelte +32 -3
  61. package/dist/visual/ObjectField.svelte +40 -24
  62. package/dist/visual/ObjectField.svelte.d.ts +6 -0
  63. package/dist/visual/ProseField.svelte +10 -13
  64. package/dist/visual/ProseField.svelte.d.ts +5 -2
  65. package/dist/visual/ProseValue.svelte +25 -19
  66. package/dist/visual/ProseValue.svelte.d.ts +4 -4
  67. package/dist/visual/TextField.svelte +3 -2
  68. package/dist/visual/TipsCard.svelte +7 -7
  69. package/dist/visual/VariantField.svelte +1 -1
  70. package/dist/visual/VariantField.svelte.d.ts +1 -1
  71. package/dist/visual/VisualEditorInner.svelte +121 -39
  72. package/dist/visual/controls.css +21 -2
  73. package/dist/visual/icons/Icon.svelte +3 -2
  74. package/dist/visual/icons/Icon.svelte.d.ts +3 -2
  75. package/dist/visual/icons/nodes.d.ts +25 -21
  76. package/dist/visual/icons/nodes.js +9 -7
  77. package/dist/visual/leaves.d.ts +12 -9
  78. package/dist/visual/leaves.js +19 -0
  79. package/dist/visual/motion.d.ts +25 -3
  80. package/dist/visual/motion.js +58 -9
  81. package/dist/visual/strings.d.ts +15 -9
  82. package/dist/visual/strings.js +19 -12
  83. package/dist/visual/structure.d.ts +21 -31
  84. package/dist/visual/structure.js +54 -51
  85. package/package.json +3 -3
@@ -73,19 +73,12 @@ export declare function moveColumn(props: TableProps, c: number, by: number): Ta
73
73
  * empties is text.
74
74
  */
75
75
  export declare function clearCells(props: TableProps, r0: number, c0: number, r1: number, c1: number): TableProps;
76
- /** Set column `c`'s alignment: what the held column's cluster and Shift+arrow write
77
- * (`table-view.ts`). */
76
+ /** Set column `c`'s alignment and clear its cells' own `align`, which would outrank it:
77
+ * what the held column's cluster and Shift+arrow write (`table-view.ts`). */
78
78
  export declare function setAlign(props: TableProps, c: number, align: TableAlign): TableProps;
79
79
  /** Draw the first row as a header or as a body row: what the header toggle writes
80
80
  * (`table-view.ts`). */
81
81
  export declare function setHeadless(props: TableProps, headless: boolean): TableProps;
82
- /** A cell's horizontal alignment, and its vertical one: the keys a cell holds beside
83
- * its text, each absent at the default the column or the quill supplies. */
84
- export type CellAlign = NonNullable<TableCell['align']>;
85
- export type CellValign = NonNullable<TableCell['valign']>;
86
- /** Every cell in the inclusive rectangle given `key`'s `value`, or cleared of it where
87
- * `value` is absent, which returns the cell to its column's alignment or the quill's. */
88
- export declare function setCellLayout<K extends 'align' | 'valign'>(props: TableProps, r0: number, c0: number, r1: number, c1: number, key: K, value: TableCell[K] | undefined): TableProps;
89
82
  /** Set or clear the column weights: what a border drag and its double-click write
90
83
  * (`table-view.ts`). `undefined` returns every column to auto-fit. */
91
84
  export declare function setWidths(props: TableProps, widths: (number | null)[] | undefined): TableProps;
@@ -184,11 +184,19 @@ export function clearCells(props, r0, c0, r1, c1) {
184
184
  rows: props.rows.map((row, i) => blank(row, i + 1))
185
185
  });
186
186
  }
187
- /** Set column `c`'s alignment: what the held column's cluster and Shift+arrow write
188
- * (`table-view.ts`). */
187
+ /** Set column `c`'s alignment and clear its cells' own `align`, which would outrank it:
188
+ * what the held column's cluster and Shift+arrow write (`table-view.ts`). */
189
189
  export function setAlign(props, c, align) {
190
+ const release = (cells) => cells.map((cell, i) => {
191
+ if (i !== c || cell.align === undefined)
192
+ return cell;
193
+ const { align: _align, ...rest } = cell;
194
+ return rest;
195
+ });
190
196
  return normalizeTable({
191
197
  ...props,
198
+ header: release(props.header),
199
+ rows: props.rows.map(release),
192
200
  aligns: props.aligns.map((a, i) => (i === c ? align : a))
193
201
  });
194
202
  }
@@ -197,23 +205,6 @@ export function setAlign(props, c, align) {
197
205
  export function setHeadless(props, headless) {
198
206
  return normalizeTable({ ...props, headless: headless || undefined });
199
207
  }
200
- /** Every cell in the inclusive rectangle given `key`'s `value`, or cleared of it where
201
- * `value` is absent, which returns the cell to its column's alignment or the quill's. */
202
- export function setCellLayout(props, r0, c0, r1, c1, key, value) {
203
- const lay = (cells, r) => cells.map((cell, c) => {
204
- if (r < r0 || r > r1 || c < c0 || c > c1)
205
- return cell;
206
- const next = { ...cell, [key]: value };
207
- if (value === undefined)
208
- delete next[key];
209
- return next;
210
- });
211
- return normalizeTable({
212
- ...props,
213
- header: lay(props.header, 0),
214
- rows: props.rows.map((row, i) => lay(row, i + 1))
215
- });
216
- }
217
208
  /** Set or clear the column weights: what a border drag and its double-click write
218
209
  * (`table-view.ts`). `undefined` returns every column to auto-fit. */
219
210
  export function setWidths(props, widths) {
@@ -0,0 +1,19 @@
1
+ import { type DOMOutputSpec, type NodeType } from 'prosemirror-model';
2
+ import { Plugin, type Command } from 'prosemirror-state';
3
+ /** The `checked` an `<li>` states: `data-qm-checked`, which `toDOM` writes, else none. */
4
+ export declare function taskChecked(el: HTMLElement): boolean | null;
5
+ /** Where an `<li>`'s blocks are: a task item's body, past its checkbox; the item itself
6
+ * otherwise. */
7
+ export declare function taskBody(el: Node): HTMLElement;
8
+ /** A task item's `<li>`: the checkbox, unreachable by the caret, then the body holding
9
+ * the item's blocks, which names it. The mark is the check glyph a matrix tick draws. */
10
+ export declare function taskDOM(checked: boolean): DOMOutputSpec;
11
+ /** The node view a block leaf mounts its list items through. */
12
+ export declare function taskPlugin(): Plugin;
13
+ /**
14
+ * `splitListItem`, leaving the item that takes none of a task item's text an open task:
15
+ * the one below, or the one above where Enter is pressed at the head of the item's text.
16
+ * An empty item the split lifts out of a nested list keeps its own `checked`, as a
17
+ * Shift-Tab lift keeps it.
18
+ */
19
+ export declare function splitTaskItem(itemType: NodeType): Command;
@@ -0,0 +1,140 @@
1
+ // A task item is a `list_item` whose `checked` is a boolean (CODEC §"Task items"). Its
2
+ // `toDOM` draws a checkbox ahead of the item's blocks, disabled, which is the drawing a
3
+ // held leaf, a copy and `renderContent` take; a leaf that edits mounts the item through
4
+ // `taskItemView`, whose checkbox toggles. A task item is minted by source mode and by a
5
+ // paste alone: no shorthand, command or key makes one.
6
+ import { DOMSerializer } from 'prosemirror-model';
7
+ import { Plugin } from 'prosemirror-state';
8
+ import { splitListItem } from 'prosemirror-schema-list';
9
+ const SVG = 'http://www.w3.org/2000/svg';
10
+ /** The id the checkbox names its item's blocks by, fresh per drawing. */
11
+ let drawn = 0;
12
+ /** The `checked` an `<li>` states: `data-qm-checked`, which `toDOM` writes, else none. */
13
+ export function taskChecked(el) {
14
+ const stated = el.getAttribute('data-qm-checked');
15
+ return stated === null ? null : stated === 'true';
16
+ }
17
+ /** Where an `<li>`'s blocks are: a task item's body, past its checkbox; the item itself
18
+ * otherwise. */
19
+ export function taskBody(el) {
20
+ const li = el;
21
+ const body = li.hasAttribute('data-qm-checked')
22
+ ? li.querySelector(':scope > [data-qm-task-body]')
23
+ : null;
24
+ return body ?? li;
25
+ }
26
+ /** A task item's `<li>`: the checkbox, unreachable by the caret, then the body holding
27
+ * the item's blocks, which names it. The mark is the check glyph a matrix tick draws. */
28
+ export function taskDOM(checked) {
29
+ const body = `qm-task-${++drawn}`;
30
+ return [
31
+ 'li',
32
+ { 'data-qm-checked': String(checked) },
33
+ [
34
+ 'span',
35
+ { class: 'qm-task', contenteditable: 'false' },
36
+ [
37
+ 'input',
38
+ {
39
+ type: 'checkbox',
40
+ class: 'qm-task-box',
41
+ 'aria-labelledby': body,
42
+ checked: checked ? '' : null,
43
+ disabled: ''
44
+ }
45
+ ],
46
+ [
47
+ `${SVG} svg`,
48
+ {
49
+ class: 'qm-task-mark',
50
+ viewBox: '0 0 24 24',
51
+ fill: 'none',
52
+ stroke: 'currentColor',
53
+ 'stroke-width': '2',
54
+ 'stroke-linecap': 'round',
55
+ 'stroke-linejoin': 'round',
56
+ 'aria-hidden': 'true'
57
+ },
58
+ [`${SVG} path`, { d: 'M20 6 9 17l-5-5' }]
59
+ ]
60
+ ],
61
+ ['div', { id: body, 'data-qm-task-body': '' }, 0]
62
+ ];
63
+ }
64
+ const isTask = (node) => node.attrs.checked !== null;
65
+ /**
66
+ * Every `list_item` in a leaf that edits, drawn by its own `toDOM`. A task item's checkbox
67
+ * is enabled, and a change to it is one `checked` transaction, so one `Mod-z` takes it
68
+ * back; where the leaf declines the edit the box returns to the item's value. The box
69
+ * keeps the press from the caret: a mousedown leaves the selection and the focus where
70
+ * they were, and ProseMirror reads no event aimed at it.
71
+ */
72
+ function taskItemView(node, view, getPos) {
73
+ const { dom, contentDOM } = DOMSerializer.renderSpec(document, node.type.spec.toDOM(node));
74
+ const box = isTask(node)
75
+ ? dom.querySelector(':scope > .qm-task > input')
76
+ : null;
77
+ if (box) {
78
+ box.disabled = false;
79
+ box.addEventListener('mousedown', (e) => e.preventDefault());
80
+ box.addEventListener('change', () => {
81
+ const pos = getPos();
82
+ if (pos === undefined || !view.editable) {
83
+ box.checked = node.attrs.checked;
84
+ return;
85
+ }
86
+ view.dispatch(view.state.tr.setNodeAttribute(pos, 'checked', box.checked));
87
+ });
88
+ }
89
+ return {
90
+ dom,
91
+ contentDOM,
92
+ update(next) {
93
+ if (next.type !== node.type || isTask(next) !== isTask(node))
94
+ return false;
95
+ node = next;
96
+ if (box) {
97
+ box.checked = next.attrs.checked;
98
+ dom.setAttribute('data-qm-checked', String(next.attrs.checked));
99
+ }
100
+ return true;
101
+ },
102
+ stopEvent: (e) => box !== null && e.target === box,
103
+ ignoreMutation: (m) => m.type !== 'selection' && !contentDOM.contains(m.target)
104
+ };
105
+ }
106
+ /** The node view a block leaf mounts its list items through. */
107
+ export function taskPlugin() {
108
+ return new Plugin({ props: { nodeViews: { list_item: taskItemView } } });
109
+ }
110
+ /**
111
+ * `splitListItem`, leaving the item that takes none of a task item's text an open task:
112
+ * the one below, or the one above where Enter is pressed at the head of the item's text.
113
+ * An empty item the split lifts out of a nested list keeps its own `checked`, as a
114
+ * Shift-Tab lift keeps it.
115
+ */
116
+ export function splitTaskItem(itemType) {
117
+ const split = splitListItem(itemType);
118
+ return (state, dispatch, view) => {
119
+ const { $from, empty } = state.selection;
120
+ const item = $from.depth >= 2 ? $from.node(-1) : null;
121
+ if (!dispatch || item?.type !== itemType || !isTask(item))
122
+ return split(state, dispatch, view);
123
+ const lifted = $from.parent.content.size === 0 && item.childCount === 1;
124
+ const atHead = empty && $from.parentOffset === 0 && $from.index(-1) === 0;
125
+ return split(state, (tr) => {
126
+ const $at = tr.selection.$from;
127
+ const below = $at.before($at.depth - 1);
128
+ if (lifted)
129
+ tr.setNodeAttribute(below, 'checked', item.attrs.checked);
130
+ else if (atHead) {
131
+ const above = tr.doc.resolve(below).nodeBefore;
132
+ if (above)
133
+ tr.setNodeAttribute(below - above.nodeSize, 'checked', false);
134
+ }
135
+ else
136
+ tr.setNodeAttribute(below, 'checked', false);
137
+ dispatch(tr);
138
+ });
139
+ };
140
+ }
@@ -11,8 +11,8 @@ export type EditorErrorCode =
11
11
  * the whole projection instead, so the store is correct and this field's
12
12
  * identity anchors were paid. */
13
13
  | 'commit-fallback'
14
- /** The overwrite fallback also failed, or the typed writer refused a `plaintext`
15
- * leaf's text: the optimistic PM state stands and the store is stale for this
14
+ /** The overwrite fallback also failed, or the typed writer refused a `text` leaf's
15
+ * text: the optimistic PM state stands and the store is stale for this
16
16
  * field. The one code here that leaves damage. */
17
17
  | 'commit-lost'
18
18
  /** A card operation (add, move, remove, retype) that threw. The document is
@@ -6,3 +6,9 @@ export declare function rungStyle(node: Element): CSSStyleDeclaration;
6
6
  * empty). No rung, no motion: a fallback here would be the scale restated in the one
7
7
  * place `check:style` cannot read. */
8
8
  export declare function rungMs(style: CSSStyleDeclaration, rung: string): number | undefined;
9
+ /** `steps` of the space rung off `node`'s root, in px: a floating surface's offset from
10
+ * its anchor. `--_qm-space` is registered, so it computes to px where the rungs derived
11
+ * from it compute to their expressions; hence the multiple rather than a read of
12
+ * `--_qm-space-2`. `undefined` where the derivation is out of reach, as for
13
+ * {@link rungMs}. */
14
+ export declare function spacePx(node: Element | undefined, steps?: number): number | undefined;
@@ -22,3 +22,14 @@ export function rungMs(style, rung) {
22
22
  return undefined;
23
23
  return raw.endsWith('ms') ? n : n * 1000;
24
24
  }
25
+ /** `steps` of the space rung off `node`'s root, in px: a floating surface's offset from
26
+ * its anchor. `--_qm-space` is registered, so it computes to px where the rungs derived
27
+ * from it compute to their expressions; hence the multiple rather than a read of
28
+ * `--_qm-space-2`. `undefined` where the derivation is out of reach, as for
29
+ * {@link rungMs}. */
30
+ export function spacePx(node, steps = 1) {
31
+ if (!node)
32
+ return undefined;
33
+ const n = Number.parseFloat(rungStyle(node).getPropertyValue('--_qm-space'));
34
+ return Number.isFinite(n) ? n * steps : undefined;
35
+ }
@@ -117,9 +117,9 @@
117
117
 
118
118
  Two planes sink. `sunken` is the ground — the column behind the cards, the
119
119
  desk behind the sheets — and lifting the plane above it takes very little
120
- at the light pole. `well` is the hole cut into a card: a field, a table's
121
- header band, the one native select, none of which draw an edge, so the
122
- step is the whole of what says a value is typed there. The order at the
120
+ at the light pole. `well` is the hole cut into a card: a field, the one
121
+ native select, neither of which draws an edge, so the step is the whole of
122
+ what says a value is typed there. The order at the
123
123
  light pole is card, ground, well; at the dark pole the two sink to within
124
124
  a channel of each other, which is where a card with little light left to
125
125
  give up leaves them.
@@ -148,6 +148,10 @@
148
148
  dark. */
149
149
  --_qm-surface-raised: color-mix(in oklab, var(--_qm-bg), #fff 8%);
150
150
  --_qm-surface-hover: color-mix(in oklab, var(--_qm-bg), var(--_qm-fg) 8%);
151
+ /* A band laid on the card rather than cut into it: a table's header, and the pill a
152
+ grip draws. It steps toward the ink at both poles, a shade under white in light
153
+ and a shade over the card in dark, where `well` sinks to a black stripe. */
154
+ --_qm-surface-band: color-mix(in oklab, var(--_qm-bg), var(--_qm-fg) 6%);
151
155
  /* The one rung whose arms are not a single ratio: a stroke is read against what it
152
156
  separates, and the mix that reads as a hairline on the dark default draws the
153
157
  light card in a line heavier than the type it frames. The light arm is
@@ -157,12 +161,11 @@
157
161
  color-mix(in oklab, var(--_qm-bg), var(--_qm-fg) 6%),
158
162
  color-mix(in oklab, var(--_qm-bg), var(--_qm-fg) 17%)
159
163
  );
160
- /* A rule that separates without structuring: the lines between a table's cells,
161
- where the frame around them is the structure. Half the border, and read off
162
- it rather than restated as a percentage of its own, so the two hold their
163
- relationship at any mix: a stroke against the surface behind it, not a fill
164
- stacked on one. */
165
- --_qm-border-faint: color-mix(in oklab, var(--_qm-bg), var(--_qm-border) 50%);
164
+ /* A stroke ruled through content rather than round it: a table's frame and every
165
+ line between its cells. The light arm of `border` is a card's edge against the
166
+ ground, and drawn between lines of type it vanishes, so a grid takes one ratio at
167
+ both poles, which lands within a few points of `border` in dark. */
168
+ --_qm-rule: color-mix(in oklab, var(--_qm-bg), var(--_qm-fg) 14%);
166
169
  /* Three rungs, and nothing under the last: a step below `label` lands under 3:1 on
167
170
  the well a field draws it against, and what would take it — an unset value's word,
168
171
  an invitation — is content a reader has to read (WCAG 1.4.3), not decoration.
@@ -186,6 +189,11 @@
186
189
  stacks over whichever surface rung the cell already draws instead of replacing
187
190
  it. */
188
191
  --_qm-accent-tint: color-mix(in oklab, var(--_qm-accent) 12%, transparent);
192
+ /* The accent at the default rung: a mark's fill where the `default:` supplies it, a
193
+ switch's checked track and a held tick's box, stepped toward the card by the
194
+ ratio `default` steps the ink. What is drawn over it in the card's colour, the
195
+ thumb and the check, holds 3:1 at both poles, at 3.3:1 and 4.2:1. */
196
+ --_qm-accent-default: color-mix(in oklab, var(--_qm-accent), var(--_qm-bg) 20%);
189
197
  /* The danger hue held: what a destructive control fills with where the family's
190
198
  neutral hover rung would read as one more button. Mixed at the accent tint's
191
199
  strength, so a held tint is a held tint whichever hue it carries. */
@@ -281,6 +289,10 @@
281
289
  arrival settles into its rest instead of stopping at it, and long enough that a
282
290
  control resting at nothing reads as arriving rather than as switching. */
283
291
  --_qm-reveal: 500ms cubic-bezier(0.4, 0, 0.2, 1);
292
+ /* A wait rather than a trip: how long a caret rests on an empty line before the
293
+ slash hint comes up. Longer than the gap between two keystrokes, so typing on
294
+ through an Enter never draws it. */
295
+ --_qm-hint-wait: 500ms;
284
296
 
285
297
  /* ── Geometry ──────────────────────────────────────────────────────────*/
286
298
  /* One stroke width for every edge the chrome draws and every rule it ranks
@@ -342,9 +354,9 @@
342
354
  it does not move when `--qm-space` does. Every button family reads it, so the
343
355
  floor is one number instead of one expression per control. */
344
356
  --_qm-tap-min: 24px;
345
- /* The chrome glyph: a chip's, and the in-well remove's. Sized in CSS rather than
346
- through the icon's `size`, so a stylesheet beats the SVG presentation attribute
347
- and one rung is the whole of what a control draws. */
357
+ /* The chrome glyph: a chip's, a glyph button's, the enum's chevron. Sized in CSS
358
+ rather than through the icon's `size`, so a stylesheet beats the SVG presentation
359
+ attribute and one rung is the whole of what a control draws. */
348
360
  --_qm-glyph-control: 14px;
349
361
  /* The narrowest track a control that fills its width stays usable in: a table's
350
362
  column floors at it, rises no further than its content, and a row of them scrolls