@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.
- package/NOTICE +6 -7
- package/README.md +1 -1
- package/THEMING.md +1 -1
- package/dist/core/address.d.ts +6 -1
- package/dist/core/address.js +12 -3
- package/dist/core/bloom.d.ts +9 -4
- package/dist/core/bloom.js +18 -49
- package/dist/core/codec/atoms.d.ts +1 -1
- package/dist/core/codec/atoms.js +26 -11
- package/dist/core/codec/blocks.d.ts +5 -5
- package/dist/core/codec/blocks.js +27 -8
- package/dist/core/codec/code.d.ts +4 -4
- package/dist/core/codec/code.js +52 -22
- package/dist/core/codec/decode.d.ts +5 -5
- package/dist/core/codec/decode.js +18 -12
- package/dist/core/codec/encode.js +6 -1
- package/dist/core/codec/field.d.ts +22 -15
- package/dist/core/codec/field.js +66 -26
- package/dist/core/codec/index.d.ts +1 -1
- package/dist/core/codec/index.js +2 -2
- package/dist/core/codec/inputrules.d.ts +4 -0
- package/dist/core/codec/inputrules.js +44 -6
- package/dist/core/codec/link-paste.d.ts +11 -0
- package/dist/core/codec/link-paste.js +32 -0
- package/dist/core/codec/lists.d.ts +5 -4
- package/dist/core/codec/lists.js +87 -31
- package/dist/core/codec/nesting.d.ts +20 -0
- package/dist/core/codec/nesting.js +62 -0
- package/dist/core/codec/prose.css +145 -63
- package/dist/core/codec/schema.d.ts +2 -2
- package/dist/core/codec/schema.js +71 -18
- package/dist/core/codec/slash.js +0 -0
- package/dist/core/codec/table-copy.d.ts +17 -0
- package/dist/core/codec/table-copy.js +84 -0
- package/dist/core/codec/table-view.d.ts +6 -7
- package/dist/core/codec/table-view.js +281 -201
- package/dist/core/codec/table.d.ts +2 -9
- package/dist/core/codec/table.js +10 -19
- package/dist/core/codec/tasks.d.ts +19 -0
- package/dist/core/codec/tasks.js +140 -0
- package/dist/core/errors.d.ts +2 -2
- package/dist/core/rungs.d.ts +6 -0
- package/dist/core/rungs.js +11 -0
- package/dist/core/theme.css +24 -12
- package/dist/visual/ArrayField.svelte +236 -78
- package/dist/visual/ArrayField.svelte.d.ts +0 -2
- package/dist/visual/BooleanField.svelte +12 -1
- package/dist/visual/Card.svelte +41 -23
- package/dist/visual/Card.svelte.d.ts +3 -2
- package/dist/visual/CardControls.svelte +15 -15
- package/dist/visual/DateField.svelte +93 -64
- package/dist/visual/EnumField.svelte +9 -4
- package/dist/visual/Field.svelte +20 -16
- package/dist/visual/Field.svelte.d.ts +4 -2
- package/dist/visual/FieldHint.svelte +22 -5
- package/dist/visual/FieldLabel.svelte +5 -33
- package/dist/visual/FieldLabel.svelte.d.ts +0 -2
- package/dist/visual/FormatPopover.svelte +78 -81
- package/dist/visual/MatrixField.svelte +28 -19
- package/dist/visual/NumberField.svelte +32 -3
- package/dist/visual/ObjectField.svelte +40 -24
- package/dist/visual/ObjectField.svelte.d.ts +6 -0
- package/dist/visual/ProseField.svelte +10 -13
- package/dist/visual/ProseField.svelte.d.ts +5 -2
- package/dist/visual/ProseValue.svelte +25 -19
- package/dist/visual/ProseValue.svelte.d.ts +4 -4
- package/dist/visual/TextField.svelte +3 -2
- package/dist/visual/TipsCard.svelte +7 -7
- package/dist/visual/VariantField.svelte +1 -1
- package/dist/visual/VariantField.svelte.d.ts +1 -1
- package/dist/visual/VisualEditorInner.svelte +121 -39
- package/dist/visual/controls.css +21 -2
- package/dist/visual/icons/Icon.svelte +3 -2
- package/dist/visual/icons/Icon.svelte.d.ts +3 -2
- package/dist/visual/icons/nodes.d.ts +25 -21
- package/dist/visual/icons/nodes.js +9 -7
- package/dist/visual/leaves.d.ts +12 -9
- package/dist/visual/leaves.js +19 -0
- package/dist/visual/motion.d.ts +25 -3
- package/dist/visual/motion.js +58 -9
- package/dist/visual/strings.d.ts +15 -9
- package/dist/visual/strings.js +19 -12
- package/dist/visual/structure.d.ts +21 -31
- package/dist/visual/structure.js +54 -51
- package/package.json +3 -3
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
// (list_item/quote → lists/blockquote); marks mirror the content formatting set.
|
|
4
4
|
// Each set is closed upstream, so the schema names its whole vocabulary and carries
|
|
5
5
|
// nothing inert. `blockSchema` is the full field; `inlineSchema` is the constrained
|
|
6
|
-
// single-textblock form for `richtext
|
|
7
|
-
// containers, no islands) and `plaintextSchema` is that one without marks, for
|
|
8
|
-
// `
|
|
6
|
+
// single-textblock form for a one-line `richtext` (one paragraph, no block split, no
|
|
7
|
+
// containers, no islands) and `plaintextSchema` is that one without marks, for a
|
|
8
|
+
// one-line `text`; `plainSchema` is a `text` field declaring `multiline: true`,
|
|
9
9
|
// paragraphs and hard breaks and nothing else; `cellSchema` is the inline form with
|
|
10
10
|
// hard breaks, for a table cell. Same decode/lower/position machinery,
|
|
11
11
|
// narrower shapes. Anchors are not marks here (decorations).
|
|
@@ -14,19 +14,65 @@
|
|
|
14
14
|
// paste inside one body run the whole document through them (CODEC §"Markdown at the
|
|
15
15
|
// edges"), so every attribute written here is read back here. Foreign HTML spells none
|
|
16
16
|
// of this package's `data-qm-*` names, so what a paste takes off the web is unchanged;
|
|
17
|
-
// the
|
|
18
|
-
// the block leaf's clipboard parser's (`table-paste.ts`).
|
|
17
|
+
// the rules here that widen that door on purpose are the fence's and a span's styled
|
|
18
|
+
// marks, a `<table>` being the block leaf's clipboard parser's (`table-paste.ts`).
|
|
19
19
|
import { Schema } from 'prosemirror-model';
|
|
20
20
|
import { islandBlockSpec, islandInlineSpec } from './islands.js';
|
|
21
|
+
import { taskBody, taskChecked, taskDOM } from './tasks.js';
|
|
21
22
|
import { rendersHref, storableUrl } from './urls.js';
|
|
23
|
+
// ── Marks spelled as style ──────────────────────────────────────────────────
|
|
24
|
+
// A word processor writes formatting as a span's inline style rather than as a tag:
|
|
25
|
+
// Google Docs spells all four marks below that way, and wraps a whole copy in a `<b>`
|
|
26
|
+
// stating a regular weight. Read off a `<span>` alone, since the same style on a cell or
|
|
27
|
+
// a heading is that block's face rather than a run's. An underline inside a link is the
|
|
28
|
+
// link's own: Docs underlines every one, and Chrome inlines the computed decoration on a
|
|
29
|
+
// copy off any page.
|
|
30
|
+
/** Whether `el`'s inline style states a bold weight. */
|
|
31
|
+
const bold = (el) => /^(?:bold(?:er)?|[6-9]\d\d)$/.test(el.style.fontWeight);
|
|
32
|
+
/** The text decoration `el` states, as a shorthand or a longhand. */
|
|
33
|
+
const decoration = (el) => `${el.style.textDecoration} ${el.style.textDecorationLine}`;
|
|
34
|
+
/** A mark a `<span>` carries where `styled` holds of it. Not consuming, so every mark a
|
|
35
|
+
* span states matches, and so do the span's own rules after them. */
|
|
36
|
+
const styledSpan = (styled) => ({
|
|
37
|
+
tag: 'span',
|
|
38
|
+
consuming: false,
|
|
39
|
+
getAttrs: (el) => styled(el) && null
|
|
40
|
+
});
|
|
22
41
|
// ── Marks (the block and inline schemas share them; plaintext declares none) ─
|
|
23
42
|
const marks = {
|
|
24
43
|
// Order matters: it fixes mark-set sort order and parse precedence. `link`
|
|
25
44
|
// last so it wraps outermost.
|
|
26
|
-
strong: {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
45
|
+
strong: {
|
|
46
|
+
parseDOM: [
|
|
47
|
+
{ tag: 'strong' },
|
|
48
|
+
{ tag: 'b', getAttrs: (el) => (!el.style.fontWeight || bold(el)) && null },
|
|
49
|
+
styledSpan(bold)
|
|
50
|
+
],
|
|
51
|
+
toDOM: () => ['strong', 0]
|
|
52
|
+
},
|
|
53
|
+
em: {
|
|
54
|
+
parseDOM: [
|
|
55
|
+
{ tag: 'em' },
|
|
56
|
+
{ tag: 'i' },
|
|
57
|
+
styledSpan((el) => /^(?:italic|oblique)\b/.test(el.style.fontStyle))
|
|
58
|
+
],
|
|
59
|
+
toDOM: () => ['em', 0]
|
|
60
|
+
},
|
|
61
|
+
underline: {
|
|
62
|
+
parseDOM: [
|
|
63
|
+
{ tag: 'u' },
|
|
64
|
+
styledSpan((el) => /\bunderline\b/.test(decoration(el)) && !el.closest('a'))
|
|
65
|
+
],
|
|
66
|
+
toDOM: () => ['u', 0]
|
|
67
|
+
},
|
|
68
|
+
strike: {
|
|
69
|
+
parseDOM: [
|
|
70
|
+
{ tag: 's' },
|
|
71
|
+
{ tag: 'del' },
|
|
72
|
+
styledSpan((el) => /\bline-through\b/.test(decoration(el)))
|
|
73
|
+
],
|
|
74
|
+
toDOM: () => ['s', 0]
|
|
75
|
+
},
|
|
30
76
|
code: { parseDOM: [{ tag: 'code' }], toDOM: () => ['code', 0] },
|
|
31
77
|
link: {
|
|
32
78
|
attrs: { href: { default: '' } },
|
|
@@ -50,11 +96,10 @@ const marks = {
|
|
|
50
96
|
}
|
|
51
97
|
};
|
|
52
98
|
// ── The fence's language ────────────────────────────────────────────────────
|
|
53
|
-
//
|
|
54
|
-
//
|
|
55
|
-
//
|
|
56
|
-
//
|
|
57
|
-
// that page stated, one copied inside the body keeps its own.
|
|
99
|
+
// Minted by the fence shorthand, which fires on Enter with the language typed after the
|
|
100
|
+
// backticks (`inputrules.ts`), and by a paste, which this reads: a fence copied off a
|
|
101
|
+
// highlighted page arrives carrying the language that page stated, one copied inside
|
|
102
|
+
// the body keeps its own.
|
|
58
103
|
/** A `language-x` / `lang-x` token in a class list. */
|
|
59
104
|
const LANG_CLASS = /(?:^|\s)lang(?:uage)?-(\S+)/;
|
|
60
105
|
/** The language a `<pre>` states: `data-lang`, which is what `toDOM` writes, else the
|
|
@@ -149,11 +194,19 @@ const blockNodes = {
|
|
|
149
194
|
parseDOM: [{ tag: 'ul' }],
|
|
150
195
|
toDOM: () => ['ul', 0]
|
|
151
196
|
},
|
|
197
|
+
// `checked` is a task item's, `null` on every other item (`tasks.ts`).
|
|
152
198
|
list_item: {
|
|
153
199
|
content: 'block+',
|
|
154
200
|
defining: true,
|
|
155
|
-
|
|
156
|
-
|
|
201
|
+
attrs: { checked: { default: null } },
|
|
202
|
+
parseDOM: [
|
|
203
|
+
{
|
|
204
|
+
tag: 'li',
|
|
205
|
+
getAttrs: (el) => ({ checked: taskChecked(el) }),
|
|
206
|
+
contentElement: taskBody
|
|
207
|
+
}
|
|
208
|
+
],
|
|
209
|
+
toDOM: (node) => node.attrs.checked === null ? ['li', 0] : taskDOM(node.attrs.checked)
|
|
157
210
|
},
|
|
158
211
|
island_block: islandBlockSpec
|
|
159
212
|
};
|
|
@@ -210,7 +263,7 @@ export const cellSchema = new Schema({
|
|
|
210
263
|
marks
|
|
211
264
|
});
|
|
212
265
|
/**
|
|
213
|
-
* A `
|
|
266
|
+
* A `text` field declaring `multiline: true`: paragraphs and hard breaks, no marks, no
|
|
214
267
|
* islands, no containers, which is the shape `Content::is_plain` admits. Mark-free
|
|
215
268
|
* for the reason {@link plaintextSchema} is.
|
|
216
269
|
*
|
|
@@ -234,7 +287,7 @@ export function leafSchema(opts) {
|
|
|
234
287
|
return opts.inline ? plaintextSchema : plainSchema;
|
|
235
288
|
return opts.inline ? inlineSchema : blockSchema;
|
|
236
289
|
}
|
|
237
|
-
/** Whether `schema` can carry formatting at all: false for the two
|
|
290
|
+
/** Whether `schema` can carry formatting at all: false for the two mark-free schemas.
|
|
238
291
|
* What a mark command asks before it offers itself. */
|
|
239
292
|
export function hasMarks(schema) {
|
|
240
293
|
return Object.keys(schema.marks).length > 0;
|
package/dist/core/codec/slash.js
CHANGED
|
Binary file
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Plugin } from 'prosemirror-state';
|
|
2
|
+
import type { TableProps } from '@quillmark/wasm';
|
|
3
|
+
/** A block of cells, both corners inclusive, in the row space `table.ts` names. */
|
|
4
|
+
export interface CellRange {
|
|
5
|
+
r0: number;
|
|
6
|
+
c0: number;
|
|
7
|
+
r1: number;
|
|
8
|
+
c1: number;
|
|
9
|
+
}
|
|
10
|
+
/** The cells of `range` as a `<table>`, the header row in a `<thead>` where the range
|
|
11
|
+
* holds it and the table has one. The cells carry their marks and breaks, and nothing
|
|
12
|
+
* of the table's layout. */
|
|
13
|
+
export declare function tableElement(props: TableProps, range?: CellRange): HTMLTableElement;
|
|
14
|
+
/** The cells of `range` as tab-separated text, a line a row. */
|
|
15
|
+
export declare function tableText(props: TableProps, range?: CellRange): string;
|
|
16
|
+
/** A block leaf's copy: a table in the slice writes as a table in both spellings. */
|
|
17
|
+
export declare function tableCopyPlugin(): Plugin;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
// A table leaves the editor as the two spellings a table arrives in (`table-paste.ts`):
|
|
2
|
+
// an HTML `<table>` and tab-separated text, what Word, Docs, Excel and Sheets each read.
|
|
3
|
+
// Two copies write them: a held rectangle's cells (`table-view.ts`), and a selection
|
|
4
|
+
// holding a whole island, whose markup keeps the island's own element around the table
|
|
5
|
+
// so a paste inside the editor reads the island back whole.
|
|
6
|
+
import { DOMSerializer } from 'prosemirror-model';
|
|
7
|
+
import { Plugin } from 'prosemirror-state';
|
|
8
|
+
import { decode } from './decode.js';
|
|
9
|
+
import { tablePropsOfNode } from './islands.js';
|
|
10
|
+
import { blockSchema, cellSchema } from './schema.js';
|
|
11
|
+
import { cellAt, cellContent, columnCount, rowCount } from './table.js';
|
|
12
|
+
const whole = (props) => ({
|
|
13
|
+
r0: 0,
|
|
14
|
+
c0: 0,
|
|
15
|
+
r1: rowCount(props) - 1,
|
|
16
|
+
c1: columnCount(props) - 1
|
|
17
|
+
});
|
|
18
|
+
const cellSerializer = DOMSerializer.fromSchema(cellSchema);
|
|
19
|
+
function cellDOM(tag, cell) {
|
|
20
|
+
const dom = document.createElement(tag);
|
|
21
|
+
const paragraph = decode(cellContent(cell), cellSchema).firstChild;
|
|
22
|
+
if (paragraph)
|
|
23
|
+
dom.append(cellSerializer.serializeFragment(paragraph.content));
|
|
24
|
+
return dom;
|
|
25
|
+
}
|
|
26
|
+
/** The cells of `range` as a `<table>`, the header row in a `<thead>` where the range
|
|
27
|
+
* holds it and the table has one. The cells carry their marks and breaks, and nothing
|
|
28
|
+
* of the table's layout. */
|
|
29
|
+
export function tableElement(props, range = whole(props)) {
|
|
30
|
+
const table = document.createElement('table');
|
|
31
|
+
let body;
|
|
32
|
+
for (let r = range.r0; r <= range.r1; r++) {
|
|
33
|
+
const head = r === 0 && !props.headless;
|
|
34
|
+
const section = head ? table.createTHead() : (body ??= table.createTBody());
|
|
35
|
+
const tr = section.insertRow();
|
|
36
|
+
for (let c = range.c0; c <= range.c1; c++)
|
|
37
|
+
tr.append(cellDOM(head ? 'th' : 'td', cellAt(props, r, c)));
|
|
38
|
+
}
|
|
39
|
+
return table;
|
|
40
|
+
}
|
|
41
|
+
/** A cell holding a tab, a line end or a quote goes in quotes with its quotes doubled,
|
|
42
|
+
* as Excel and Sheets write one. */
|
|
43
|
+
function tsvCell(text) {
|
|
44
|
+
return /[\t\n"]/.test(text) ? `"${text.replace(/"/g, '""')}"` : text;
|
|
45
|
+
}
|
|
46
|
+
/** The cells of `range` as tab-separated text, a line a row. */
|
|
47
|
+
export function tableText(props, range = whole(props)) {
|
|
48
|
+
const lines = [];
|
|
49
|
+
for (let r = range.r0; r <= range.r1; r++) {
|
|
50
|
+
const cells = [];
|
|
51
|
+
for (let c = range.c0; c <= range.c1; c++)
|
|
52
|
+
cells.push(tsvCell(cellAt(props, r, c).text));
|
|
53
|
+
lines.push(cells.join('\t'));
|
|
54
|
+
}
|
|
55
|
+
return lines.join('\n');
|
|
56
|
+
}
|
|
57
|
+
const base = DOMSerializer.fromSchema(blockSchema);
|
|
58
|
+
/** The island's own element with the table inside it: a paste in the editor matches the
|
|
59
|
+
* element and reads `props`, and every other reader finds the table. */
|
|
60
|
+
const islandSerializer = new DOMSerializer({
|
|
61
|
+
...base.nodes,
|
|
62
|
+
island_block: (node) => {
|
|
63
|
+
const props = tablePropsOfNode(node);
|
|
64
|
+
const dom = DOMSerializer.renderSpec(document, blockSchema.nodes.island_block.spec.toDOM(node)).dom;
|
|
65
|
+
if (props)
|
|
66
|
+
dom.replaceChildren(tableElement(props));
|
|
67
|
+
return dom;
|
|
68
|
+
}
|
|
69
|
+
}, base.marks);
|
|
70
|
+
function leafText(node) {
|
|
71
|
+
const props = node.type === blockSchema.nodes.island_block ? tablePropsOfNode(node) : undefined;
|
|
72
|
+
if (props)
|
|
73
|
+
return tableText(props);
|
|
74
|
+
return node.type.spec.leafText?.(node) ?? '';
|
|
75
|
+
}
|
|
76
|
+
/** A block leaf's copy: a table in the slice writes as a table in both spellings. */
|
|
77
|
+
export function tableCopyPlugin() {
|
|
78
|
+
return new Plugin({
|
|
79
|
+
props: {
|
|
80
|
+
clipboardSerializer: islandSerializer,
|
|
81
|
+
clipboardTextSerializer: (slice) => slice.content.textBetween(0, slice.content.size, '\n\n', leafText)
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
}
|
|
@@ -21,18 +21,12 @@ export interface TableChromeStrings {
|
|
|
21
21
|
/** The two trailing bars, each of which grows the table along its own axis. */
|
|
22
22
|
tableAddRow: string;
|
|
23
23
|
tableAddColumn: string;
|
|
24
|
-
/** The held column's alignment cluster, a name per toggle
|
|
25
|
-
* cluster, which takes these three and the vertical three beside them. */
|
|
24
|
+
/** The held column's alignment cluster, a name per toggle. */
|
|
26
25
|
tableAlignLeft: string;
|
|
27
26
|
tableAlignCenter: string;
|
|
28
27
|
tableAlignRight: string;
|
|
29
28
|
/** The held first row's toggle, pressed while the row draws as the header. */
|
|
30
29
|
tableHeaderToggle: string;
|
|
31
|
-
tableAlignTop: string;
|
|
32
|
-
tableAlignMiddle: string;
|
|
33
|
-
tableAlignBottom: string;
|
|
34
|
-
/** The held cells' cluster's own name. */
|
|
35
|
-
tableCellAlignment: string;
|
|
36
30
|
}
|
|
37
31
|
/**
|
|
38
32
|
* The package's English for the island chrome. It lives here, beside the surface
|
|
@@ -57,6 +51,11 @@ export interface TableViewDeps {
|
|
|
57
51
|
* which is PM's 5px default. */
|
|
58
52
|
clearance: number | undefined;
|
|
59
53
|
}
|
|
54
|
+
/** The table island mounted at `pos` in `view`, which a caret can step into; `undefined`
|
|
55
|
+
* for any other node, or where no view is mounted. */
|
|
56
|
+
export declare function tableAt(view: EditorView, pos: number): {
|
|
57
|
+
enter(side: -1 | 1): void;
|
|
58
|
+
} | undefined;
|
|
60
59
|
/** The `island_block` node view: a table island's editing surface, and the literal
|
|
61
60
|
* placeholder for every other island type. */
|
|
62
61
|
export declare function tableNodeView(deps: TableViewDeps): NodeViewConstructor;
|