@portone/docx-editor 0.3.0 → 0.4.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/CHANGELOG.md +46 -0
- package/dist/DocxEditor.js +1 -0
- package/dist/core.d.ts +1 -1
- package/dist/docx/comments/writing.js +14 -8
- package/dist/docx/exportDocx.js +20 -4
- package/dist/docx/formatting/attrs.d.ts +20 -2
- package/dist/docx/formatting/attrs.js +57 -2
- package/dist/docx/formatting/context.d.ts +10 -2
- package/dist/docx/formatting/context.js +23 -5
- package/dist/docx/formatting/resolve.d.ts +10 -4
- package/dist/docx/formatting/resolve.js +31 -11
- package/dist/docx/formatting/styles.d.ts +25 -2
- package/dist/docx/formatting/styles.js +84 -3
- package/dist/docx/hyperlink.d.ts +14 -4
- package/dist/docx/hyperlink.js +4 -1
- package/dist/docx/importDocx.js +46 -37
- package/dist/docx/importTable.d.ts +2 -2
- package/dist/docx/importTable.js +52 -55
- package/dist/docx/invariants.js +26 -13
- package/dist/docx/newLists.d.ts +30 -0
- package/dist/docx/newLists.js +24 -1
- package/dist/docx/numberingPlanner.d.ts +6 -3
- package/dist/docx/numberingPlanner.js +38 -7
- package/dist/docx/session.d.ts +1 -1
- package/dist/docx/session.js +11 -1
- package/dist/docx/tableFormatting/conditions.d.ts +76 -0
- package/dist/docx/tableFormatting/conditions.js +196 -0
- package/dist/docx/tableFormatting/editing.d.ts +7 -3
- package/dist/docx/tableFormatting/editing.js +4 -5
- package/dist/docx/tableFormatting/reading.d.ts +49 -19
- package/dist/docx/tableFormatting/reading.js +109 -35
- package/dist/docx/tableFormatting.d.ts +1 -0
- package/dist/docx/tableFormatting.js +1 -0
- package/dist/docx/tableTemplate.js +12 -12
- package/dist/editor/commands/formatting/propertyCommands.js +6 -1
- package/dist/editor/commands/listCommands.d.ts +6 -3
- package/dist/editor/commands/listCommands.js +73 -28
- package/dist/editor/commands/paragraphCommands.js +6 -1
- package/dist/editor/createEditor.js +1 -1
- package/dist/editor/editorDocument.d.ts +7 -6
- package/dist/editor/editorDocument.js +18 -6
- package/dist/editor/externalClipboard.d.ts +13 -2
- package/dist/editor/externalClipboard.js +64 -34
- package/dist/editor/paragraphEdits.d.ts +16 -1
- package/dist/editor/paragraphEdits.js +13 -5
- package/dist/editor/paragraphPlacement.d.ts +11 -0
- package/dist/editor/paragraphPlacement.js +18 -0
- package/dist/editor/plugins/imagePaste.js +7 -4
- package/dist/editor/plugins/numberingDecorations.d.ts +10 -4
- package/dist/editor/plugins/numberingDecorations.js +43 -2
- package/dist/editor/plugins/paragraphDisplay.js +9 -3
- package/dist/editor/plugins/tableDisplay.js +19 -5
- package/dist/model/format.d.ts +50 -0
- package/dist/model/format.js +65 -0
- package/dist/numbering/listRegistry.d.ts +37 -0
- package/dist/numbering/listRegistry.js +112 -0
- package/dist/numbering/listTemplate.d.ts +26 -17
- package/dist/numbering/listTemplate.js +66 -20
- package/dist/numbering/markers.d.ts +15 -6
- package/dist/numbering/markers.js +26 -59
- package/dist/numbering/parseNumbering.d.ts +70 -5
- package/dist/numbering/parseNumbering.js +100 -26
- package/dist/numbering/spellers.d.ts +15 -0
- package/dist/numbering/spellers.js +96 -0
- package/dist/numbering/writeNumbering.d.ts +5 -3
- package/dist/numbering/writeNumbering.js +15 -15
- package/dist/ooxml/conformance.d.ts +28 -0
- package/dist/ooxml/conformance.js +20 -0
- package/dist/ooxml/errors.d.ts +5 -5
- package/dist/ooxml/names.d.ts +9 -0
- package/dist/ooxml/names.js +8 -1
- package/dist/ooxml/partSplice.d.ts +1 -1
- package/dist/ooxml/partSplice.js +10 -3
- package/dist/ooxml/xml.d.ts +8 -9
- package/dist/ooxml/xml.js +6 -6
- package/dist/page/PageGuides.d.ts +1 -1
- package/dist/page/PageGuides.js +0 -13
- package/dist/page/pageLayout.d.ts +0 -10
- package/dist/page/pageLayout.js +3 -10
- package/dist/page/usePageLayout.d.ts +0 -7
- package/dist/page/usePageLayout.js +2 -9
- package/dist/schema/attrRoles.js +8 -1
- package/dist/schema/docxSchema.js +32 -3
- package/dist/styles/classNames.d.ts +13 -2
- package/dist/styles/classNames.js +13 -2
- package/dist/styles.css +16 -18
- package/dist/table/cellFormatting.js +1 -2
- package/dist/table/gridBorders.d.ts +11 -15
- package/dist/table/gridBorders.js +32 -18
- package/package.json +1 -1
|
@@ -5,12 +5,24 @@ import {
|
|
|
5
5
|
Slice
|
|
6
6
|
} from "prosemirror-model";
|
|
7
7
|
import { Plugin } from "prosemirror-state";
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
paragraphAttrsFor,
|
|
10
|
+
styleIdOf
|
|
11
|
+
} from "../docx/formatting.js";
|
|
9
12
|
import {
|
|
10
13
|
withListNumbering,
|
|
11
14
|
withParagraphStyle
|
|
12
15
|
} from "../docx/paraProps.js";
|
|
13
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
listsWorn,
|
|
18
|
+
NEW_LISTS_ATTR,
|
|
19
|
+
newListsValue
|
|
20
|
+
} from "../numbering/listRegistry.js";
|
|
21
|
+
import {
|
|
22
|
+
allocateList,
|
|
23
|
+
MAX_ILVL,
|
|
24
|
+
templateList
|
|
25
|
+
} from "../numbering/listTemplate.js";
|
|
14
26
|
import { docxSchema, isPageBreak } from "../schema/index.js";
|
|
15
27
|
import { editorClassNames } from "../styles/classNames.js";
|
|
16
28
|
import { PASTED_IMAGE_ATTRIBUTE } from "./clipboard/images.js";
|
|
@@ -21,14 +33,11 @@ import {
|
|
|
21
33
|
safeHref,
|
|
22
34
|
withInlineStyle
|
|
23
35
|
} from "./clipboard/inlineFormatting.js";
|
|
24
|
-
import {
|
|
36
|
+
import { numIdsIn } from "./commands/listCommands.js";
|
|
25
37
|
import { documentFormatting, documentParagraphStyles } from "./documentStyles.js";
|
|
26
38
|
import { insertPlainText } from "./plainText.js";
|
|
27
39
|
import { moveCaretToDrop } from "./plugins/dropCaret.js";
|
|
28
|
-
import {
|
|
29
|
-
canStartNewList,
|
|
30
|
-
documentNumbering
|
|
31
|
-
} from "./plugins/numberingDecorations.js";
|
|
40
|
+
import { canStartNewList } from "./plugins/numberingDecorations.js";
|
|
32
41
|
var BLOCK_TAGS = /* @__PURE__ */ new Set([
|
|
33
42
|
"ADDRESS",
|
|
34
43
|
"ARTICLE",
|
|
@@ -256,29 +265,17 @@ function listParagraphProps(list) {
|
|
|
256
265
|
indent: { kind: "keep" }
|
|
257
266
|
});
|
|
258
267
|
}
|
|
259
|
-
function paragraphAttrs(
|
|
268
|
+
function paragraphAttrs(formatting, list, paragraph) {
|
|
260
269
|
const props = listParagraphProps(list) ?? paragraph;
|
|
261
|
-
return props ? {
|
|
262
|
-
pPr: props.pPr,
|
|
263
|
-
...paragraphAttrsFor(props.pPr, documentFormatting(state))
|
|
264
|
-
} : null;
|
|
265
|
-
}
|
|
266
|
-
function usedNumIds(state) {
|
|
267
|
-
const used = new Set(documentNumbering(state).lists.keys());
|
|
268
|
-
state.doc.descendants((node) => {
|
|
269
|
-
if (node.type !== docxSchema.nodes.paragraph) return true;
|
|
270
|
-
const ref = listRefOf(node);
|
|
271
|
-
if (ref) used.add(ref.numId);
|
|
272
|
-
return false;
|
|
273
|
-
});
|
|
274
|
-
return used;
|
|
270
|
+
return props ? { pPr: props.pPr, ...paragraphAttrsFor(props.pPr, formatting) } : null;
|
|
275
271
|
}
|
|
276
272
|
var HtmlReader = class {
|
|
277
273
|
constructor(state, preserveParagraphStyles, images) {
|
|
278
274
|
this.state = state;
|
|
279
275
|
this.preserveParagraphStyles = preserveParagraphStyles;
|
|
280
276
|
this.images = images;
|
|
281
|
-
this.
|
|
277
|
+
this.formatting = documentFormatting(state);
|
|
278
|
+
this.used = numIdsIn(state.doc);
|
|
282
279
|
this.canCreateLists = canStartNewList(state);
|
|
283
280
|
}
|
|
284
281
|
state;
|
|
@@ -287,6 +284,16 @@ var HtmlReader = class {
|
|
|
287
284
|
blocks = [];
|
|
288
285
|
used;
|
|
289
286
|
canCreateLists;
|
|
287
|
+
/**
|
|
288
|
+
* What the pasted paragraphs are resolved against: the document's context, holding the
|
|
289
|
+
* definition of each pasted list as it is read, so that an item is drawn against the list it
|
|
290
|
+
* is joining rather than against a list nothing yet defines.
|
|
291
|
+
*/
|
|
292
|
+
formatting;
|
|
293
|
+
/** The definitions of every list started while editing, the pasted ones among them */
|
|
294
|
+
get newLists() {
|
|
295
|
+
return this.formatting.numbering.added;
|
|
296
|
+
}
|
|
290
297
|
read(root) {
|
|
291
298
|
this.readFlow(root, { style: {}, href: null, preserveWhitespace: false });
|
|
292
299
|
return this.blocks;
|
|
@@ -318,7 +325,7 @@ var HtmlReader = class {
|
|
|
318
325
|
}
|
|
319
326
|
this.blocks.push(
|
|
320
327
|
docxSchema.nodes.paragraph.create(
|
|
321
|
-
paragraphAttrs(this.
|
|
328
|
+
paragraphAttrs(this.formatting, list, paragraph),
|
|
322
329
|
content
|
|
323
330
|
)
|
|
324
331
|
);
|
|
@@ -354,11 +361,20 @@ var HtmlReader = class {
|
|
|
354
361
|
}
|
|
355
362
|
flush();
|
|
356
363
|
}
|
|
364
|
+
/**
|
|
365
|
+
* The number a pasted list takes, registered with the definition that list is drawn and written
|
|
366
|
+
* with. A pasted list is started here just as the list button starts one.
|
|
367
|
+
*/
|
|
357
368
|
takeNumId(kind) {
|
|
358
369
|
if (!this.canCreateLists) return null;
|
|
359
|
-
const
|
|
360
|
-
|
|
361
|
-
|
|
370
|
+
const started = allocateList(
|
|
371
|
+
this.formatting.numbering,
|
|
372
|
+
this.used,
|
|
373
|
+
templateList(kind)
|
|
374
|
+
);
|
|
375
|
+
this.formatting = { ...this.formatting, numbering: started.numbering };
|
|
376
|
+
this.used.add(started.numId);
|
|
377
|
+
return started.numId;
|
|
362
378
|
}
|
|
363
379
|
readList(list, context, level, inherited) {
|
|
364
380
|
const kind = list.tagName === "UL" ? "bullet" : "numbered";
|
|
@@ -407,15 +423,28 @@ function richHtmlSlice(state, document, source, images = /* @__PURE__ */ new Map
|
|
|
407
423
|
const template = document.createElement("template");
|
|
408
424
|
template.innerHTML = source;
|
|
409
425
|
const open = sliceDepth(template.content);
|
|
410
|
-
const
|
|
411
|
-
|
|
426
|
+
const reader = new HtmlReader(state, open === 0, images);
|
|
427
|
+
const blocks = reader.read(template.content);
|
|
428
|
+
return blocks.length === 0 ? null : {
|
|
429
|
+
slice: new Slice(Fragment.fromArray([...blocks]), open, open),
|
|
430
|
+
newLists: reader.newLists
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
function withPastedContent(tr, content) {
|
|
434
|
+
return tr.setDocAttribute(
|
|
435
|
+
NEW_LISTS_ATTR,
|
|
436
|
+
newListsValue(listsWorn(content.newLists, numIdsIn(tr.doc)))
|
|
412
437
|
);
|
|
413
|
-
return blocks.length === 0 ? null : new Slice(Fragment.fromArray([...blocks]), open, open);
|
|
414
438
|
}
|
|
415
439
|
function insertRichHtml(view, source) {
|
|
416
|
-
const
|
|
417
|
-
if (
|
|
418
|
-
view.dispatch(
|
|
440
|
+
const content = richHtmlSlice(view.state, view.dom.ownerDocument, source);
|
|
441
|
+
if (content === null) return false;
|
|
442
|
+
view.dispatch(
|
|
443
|
+
withPastedContent(
|
|
444
|
+
view.state.tr.replaceSelection(content.slice),
|
|
445
|
+
content
|
|
446
|
+
).scrollIntoView()
|
|
447
|
+
);
|
|
419
448
|
return true;
|
|
420
449
|
}
|
|
421
450
|
function insertClipboardData(view, data) {
|
|
@@ -452,5 +481,6 @@ export {
|
|
|
452
481
|
externalClipboard,
|
|
453
482
|
insertClipboardData,
|
|
454
483
|
insertRichHtml,
|
|
455
|
-
richHtmlSlice
|
|
484
|
+
richHtmlSlice,
|
|
485
|
+
withPastedContent
|
|
456
486
|
};
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import type { Node as PMNode } from "prosemirror-model";
|
|
10
10
|
import type { EditorState, Transaction } from "prosemirror-state";
|
|
11
|
+
import { type FormattingContext } from "../docx/formatting";
|
|
11
12
|
import type { ParagraphProps } from "../docx/paraProps";
|
|
12
13
|
export interface ParagraphSpot {
|
|
13
14
|
pos: number;
|
|
@@ -33,5 +34,19 @@ export declare function editableParagraphs(state: EditorState): ParagraphSpot[];
|
|
|
33
34
|
export declare function paragraphPPr(node: PMNode): string | null;
|
|
34
35
|
/** How to edit a single paragraph. Null skips that paragraph */
|
|
35
36
|
export type ParagraphSurgery = (node: PMNode) => ParagraphProps | null;
|
|
37
|
+
/** What an edit does beyond swapping the fragments of the paragraphs it names */
|
|
38
|
+
export interface ParagraphEditExtras {
|
|
39
|
+
/**
|
|
40
|
+
* The context the new display values are resolved against, which is the state's own unless the
|
|
41
|
+
* edit is itself changing it. An edit that starts a list hands in the context that already
|
|
42
|
+
* holds it, so the paragraph is drawn against the list it is joining in that same transaction.
|
|
43
|
+
*/
|
|
44
|
+
formatting?: FormattingContext;
|
|
45
|
+
/**
|
|
46
|
+
* What else the edit records in the same transaction, such as the definition of the list the
|
|
47
|
+
* paragraphs are joining, so that undo takes the two back together.
|
|
48
|
+
*/
|
|
49
|
+
alongside?: (tr: Transaction) => void;
|
|
50
|
+
}
|
|
36
51
|
/** Edits the selected paragraphs in a single transaction. Does nothing when there is nothing to edit */
|
|
37
|
-
export declare function editParagraphs(state: EditorState, dispatch: ((tr: Transaction) => void) | undefined, surgery: ParagraphSurgery): boolean;
|
|
52
|
+
export declare function editParagraphs(state: EditorState, dispatch: ((tr: Transaction) => void) | undefined, surgery: ParagraphSurgery, extras?: ParagraphEditExtras): boolean;
|
|
@@ -3,6 +3,7 @@ import { paragraphAttrsFor } from "../docx/formatting.js";
|
|
|
3
3
|
import { docxSchema } from "../schema/index.js";
|
|
4
4
|
import { editShut } from "../schema/guards.js";
|
|
5
5
|
import { documentFormatting } from "./documentStyles.js";
|
|
6
|
+
import { paragraphPlacementAt } from "./paragraphPlacement.js";
|
|
6
7
|
function selectedParagraphs(state) {
|
|
7
8
|
const spots = /* @__PURE__ */ new Map();
|
|
8
9
|
for (const range of state.selection.ranges) {
|
|
@@ -23,25 +24,32 @@ function paragraphPPr(node) {
|
|
|
23
24
|
const pPr = node.attrs.pPr;
|
|
24
25
|
return typeof pPr === "string" ? pPr : null;
|
|
25
26
|
}
|
|
26
|
-
function writeChanges(state, changed) {
|
|
27
|
+
function writeChanges(state, changed, extras) {
|
|
27
28
|
const tr = state.tr;
|
|
28
|
-
const formatting = documentFormatting(state);
|
|
29
|
+
const formatting = extras.formatting ?? documentFormatting(state);
|
|
29
30
|
for (const { spot, props } of changed) {
|
|
30
31
|
tr.setNodeMarkup(tr.mapping.map(spot.pos), void 0, {
|
|
31
32
|
...spot.node.attrs,
|
|
32
33
|
pPr: props.pPr,
|
|
33
|
-
...paragraphAttrsFor(
|
|
34
|
+
...paragraphAttrsFor(
|
|
35
|
+
props.pPr,
|
|
36
|
+
formatting,
|
|
37
|
+
paragraphPlacementAt(state.doc, spot.pos)
|
|
38
|
+
)
|
|
34
39
|
});
|
|
35
40
|
}
|
|
41
|
+
extras.alongside?.(tr);
|
|
36
42
|
return tr;
|
|
37
43
|
}
|
|
38
|
-
function editParagraphs(state, dispatch, surgery) {
|
|
44
|
+
function editParagraphs(state, dispatch, surgery, extras = {}) {
|
|
39
45
|
const changed = editableParagraphs(state).flatMap((spot) => {
|
|
40
46
|
const props = surgery(spot.node);
|
|
41
47
|
return props ? [{ spot, props }] : [];
|
|
42
48
|
});
|
|
43
49
|
if (changed.length === 0) return false;
|
|
44
|
-
if (dispatch)
|
|
50
|
+
if (dispatch) {
|
|
51
|
+
dispatch(writeChanges(state, changed, extras).scrollIntoView());
|
|
52
|
+
}
|
|
45
53
|
return true;
|
|
46
54
|
}
|
|
47
55
|
export {
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** Finds the table-style conditions at an editor position for both reads and writes. */
|
|
2
|
+
import type { Node as PMNode } from "prosemirror-model";
|
|
3
|
+
import type { ParagraphPlacement } from "../docx/formatting";
|
|
4
|
+
/**
|
|
5
|
+
* The part of a table the paragraph stands in, which its table style dresses it by. null for a
|
|
6
|
+
* paragraph of the body, and for one in a cell of a table this editor did not build.
|
|
7
|
+
*
|
|
8
|
+
* The cell is found by walking out of the paragraph rather than passed down, because the walk that
|
|
9
|
+
* runs the derivers goes over the blocks and hands each of them nothing but its own position.
|
|
10
|
+
*/
|
|
11
|
+
export declare function paragraphPlacementAt(doc: PMNode, pos: number): ParagraphPlacement | null;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// src/editor/paragraphPlacement.ts
|
|
2
|
+
import { TableMap } from "prosemirror-tables";
|
|
3
|
+
import { docxSchema } from "../schema/index.js";
|
|
4
|
+
import { cellDefaultsAt, tableCellSources } from "../table/gridBorders.js";
|
|
5
|
+
function paragraphPlacementAt(doc, pos) {
|
|
6
|
+
const $pos = doc.resolve(pos);
|
|
7
|
+
for (let depth = $pos.depth; depth >= 2; depth -= 1) {
|
|
8
|
+
if ($pos.node(depth).type !== docxSchema.nodes.tableCell) continue;
|
|
9
|
+
const table = $pos.node(depth - 2);
|
|
10
|
+
if (table.type !== docxSchema.nodes.table) return null;
|
|
11
|
+
const cell = $pos.before(depth) - $pos.start(depth - 2);
|
|
12
|
+
return cellDefaultsAt(TableMap.get(table), cell, tableCellSources(table)).placement;
|
|
13
|
+
}
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
export {
|
|
17
|
+
paragraphPlacementAt
|
|
18
|
+
};
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
resolveClipboardImages
|
|
9
9
|
} from "../clipboard/images.js";
|
|
10
10
|
import { documentBodyHeightPx, documentBodyWidthPx } from "../documentStyles.js";
|
|
11
|
-
import { richHtmlSlice } from "../externalClipboard.js";
|
|
11
|
+
import { richHtmlSlice, withPastedContent } from "../externalClipboard.js";
|
|
12
12
|
import { insertPlainTextAt } from "../plainText.js";
|
|
13
13
|
var LOAD_TIMEOUT_MS = 15e3;
|
|
14
14
|
var imagePasteKey = new PluginKey("docxImagePaste");
|
|
@@ -87,15 +87,18 @@ function imagePaste() {
|
|
|
87
87
|
if (!range) return;
|
|
88
88
|
removeMarker(task);
|
|
89
89
|
if (task.view.isDestroyed) return;
|
|
90
|
-
const
|
|
90
|
+
const content = resolved === null || resolved.images.size === 0 ? null : richHtmlSlice(
|
|
91
91
|
task.view.state,
|
|
92
92
|
task.view.dom.ownerDocument,
|
|
93
93
|
resolved.source,
|
|
94
94
|
resolved.images
|
|
95
95
|
);
|
|
96
|
-
if (
|
|
96
|
+
if (content !== null) {
|
|
97
97
|
task.view.dispatch(
|
|
98
|
-
|
|
98
|
+
withPastedContent(
|
|
99
|
+
task.view.state.tr.replaceRange(range.from, range.to, content.slice),
|
|
100
|
+
content
|
|
101
|
+
).scrollIntoView()
|
|
99
102
|
);
|
|
100
103
|
} else {
|
|
101
104
|
insertPlainTextAt(task.view, task.text, range.from, range.to);
|
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
import type { Node as PMNode } from "prosemirror-model";
|
|
7
7
|
import { type EditorState, Plugin } from "prosemirror-state";
|
|
8
8
|
import { DecorationSet } from "prosemirror-view";
|
|
9
|
-
import type
|
|
9
|
+
import { type RunFormat } from "../../model/format";
|
|
10
|
+
import type { LevelAlign, LevelSuffix, Numbering } from "../../numbering/parseNumbering";
|
|
10
11
|
/** A number to draw on screen and the paragraph position it attaches to */
|
|
11
12
|
export interface PlacedMarker {
|
|
12
13
|
from: number;
|
|
@@ -19,6 +20,12 @@ export interface PlacedMarker {
|
|
|
19
20
|
indentEndPt: number | null;
|
|
20
21
|
/** The first-line indentation, under the same rule */
|
|
21
22
|
textIndentPt: number | null;
|
|
23
|
+
/** What the level puts between the number and the text (`w:suff`) */
|
|
24
|
+
suffix: LevelSuffix;
|
|
25
|
+
/** Where the number sits inside the width kept for it (`w:lvlJc`) */
|
|
26
|
+
align: LevelAlign;
|
|
27
|
+
/** The formatting the level puts on the number alone (`lvl/rPr`) */
|
|
28
|
+
run: RunFormat | null;
|
|
22
29
|
}
|
|
23
30
|
/** Walks a document to find the paragraphs that get numbers and what those numbers are */
|
|
24
31
|
export declare function paragraphMarkers(doc: PMNode, numbering: Numbering): PlacedMarker[];
|
|
@@ -27,9 +34,8 @@ export declare function markerDecorations(doc: PMNode, numbering: Numbering): De
|
|
|
27
34
|
export declare function documentNumbering(state: EditorState): Numbering;
|
|
28
35
|
/**
|
|
29
36
|
* Whether a new list can be started in this document.
|
|
30
|
-
* A document
|
|
31
|
-
*
|
|
32
|
-
* in the first place.
|
|
37
|
+
* A document with nowhere to write a new definition would be blocked at export time, so in such
|
|
38
|
+
* a document the commands that start a list do not apply in the first place.
|
|
33
39
|
*/
|
|
34
40
|
export declare function canStartNewList(state: EditorState): boolean;
|
|
35
41
|
/** Draws the numbers this document's lists put in front of their paragraphs */
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
// src/editor/plugins/numberingDecorations.ts
|
|
2
2
|
import { Plugin, PluginKey } from "prosemirror-state";
|
|
3
3
|
import { Decoration, DecorationSet } from "prosemirror-view";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
toParagraphFormat
|
|
6
|
+
} from "../../model/format.js";
|
|
5
7
|
import { computeMarkers } from "../../numbering/markers.js";
|
|
6
8
|
import { editorAttributes, editorCssVariables } from "../../styles/classNames.js";
|
|
7
9
|
import { documentOf } from "../editorDocument.js";
|
|
@@ -45,13 +47,52 @@ function paragraphMarkers(doc, numbering) {
|
|
|
45
47
|
from: spot.pos,
|
|
46
48
|
to: spot.pos + spot.nodeSize,
|
|
47
49
|
text: marker.text,
|
|
50
|
+
suffix: marker.suffix,
|
|
51
|
+
align: marker.align,
|
|
52
|
+
run: marker.run,
|
|
48
53
|
...markerPlacement(spot.format, marker.indent)
|
|
49
54
|
}
|
|
50
55
|
];
|
|
51
56
|
});
|
|
52
57
|
}
|
|
58
|
+
function markerSpacing(marker) {
|
|
59
|
+
if (marker.suffix === "tab") {
|
|
60
|
+
return [`${editorCssVariables.markerWidth}:${marker.widthPt}pt`];
|
|
61
|
+
}
|
|
62
|
+
return [
|
|
63
|
+
`${editorCssVariables.markerWidth}:0`,
|
|
64
|
+
`${editorCssVariables.markerGap}:0`
|
|
65
|
+
];
|
|
66
|
+
}
|
|
67
|
+
function markerFormat(run) {
|
|
68
|
+
if (!run) return [];
|
|
69
|
+
const css = [];
|
|
70
|
+
if (run.bold !== void 0) {
|
|
71
|
+
css.push(
|
|
72
|
+
`${editorCssVariables.markerFontWeight}:${run.bold ? "bold" : "normal"}`
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
if (run.italic !== void 0) {
|
|
76
|
+
css.push(
|
|
77
|
+
`${editorCssVariables.markerFontStyle}:${run.italic ? "italic" : "normal"}`
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
if (run.color !== void 0) {
|
|
81
|
+
css.push(`${editorCssVariables.markerColor}:${run.color}`);
|
|
82
|
+
}
|
|
83
|
+
if (run.fontSizePt !== void 0) {
|
|
84
|
+
css.push(`${editorCssVariables.markerFontSize}:${run.fontSizePt}pt`);
|
|
85
|
+
}
|
|
86
|
+
if (run.fontFamily !== void 0) {
|
|
87
|
+
css.push(`${editorCssVariables.markerFontFamily}:${run.fontFamily}`);
|
|
88
|
+
}
|
|
89
|
+
return css;
|
|
90
|
+
}
|
|
53
91
|
function markerStyle(marker) {
|
|
54
|
-
const css = [
|
|
92
|
+
const css = [...markerSpacing(marker), ...markerFormat(marker.run)];
|
|
93
|
+
if (marker.align !== "left") {
|
|
94
|
+
css.push(`${editorCssVariables.markerAlign}:${marker.align}`);
|
|
95
|
+
}
|
|
55
96
|
if (marker.indentStartPt !== null) {
|
|
56
97
|
css.push(`margin-inline-start:${marker.indentStartPt}pt`);
|
|
57
98
|
}
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
} from "../../docx/formatting.js";
|
|
7
7
|
import { docxSchema } from "../../schema/index.js";
|
|
8
8
|
import { paragraphPPr } from "../paragraphEdits.js";
|
|
9
|
+
import { paragraphPlacementAt } from "../paragraphPlacement.js";
|
|
9
10
|
function carriesNothing(node) {
|
|
10
11
|
return node.attrs.format === null && node.attrs.styleRun === null;
|
|
11
12
|
}
|
|
@@ -18,12 +19,17 @@ function holdsComposition(node, pos, composing) {
|
|
|
18
19
|
var paragraphDisplay = {
|
|
19
20
|
name: "paragraph",
|
|
20
21
|
nodeTypes: ["paragraph"],
|
|
21
|
-
derive(node, pos,
|
|
22
|
-
|
|
22
|
+
derive(node, pos, doc, context, previous) {
|
|
23
|
+
const placement = paragraphPlacementAt(doc, pos);
|
|
24
|
+
if (placement === null && stands(node, previous) || holdsComposition(node, pos, context.composingAt)) {
|
|
23
25
|
return [];
|
|
24
26
|
}
|
|
25
27
|
const formatting = context.document.formatting;
|
|
26
|
-
const paragraph = resolveParagraph(
|
|
28
|
+
const paragraph = resolveParagraph(
|
|
29
|
+
paragraphPPr(node),
|
|
30
|
+
formatting,
|
|
31
|
+
placement
|
|
32
|
+
);
|
|
27
33
|
const derived = [
|
|
28
34
|
{ pos, attrs: paragraphAttrsOf(paragraph) }
|
|
29
35
|
];
|
|
@@ -1,14 +1,28 @@
|
|
|
1
1
|
// src/editor/plugins/tableDisplay.ts
|
|
2
|
+
import { tableStyleAttrs } from "../../docx/formatting.js";
|
|
3
|
+
import { parsePropsXml } from "../../ooxml/props.js";
|
|
2
4
|
import { cellFixes, sameFormattingInputs } from "../../table/gridBorders.js";
|
|
3
5
|
var tableDisplay = {
|
|
4
6
|
name: "table",
|
|
5
7
|
nodeTypes: ["table"],
|
|
6
|
-
derive(table, pos, _doc,
|
|
8
|
+
derive(table, pos, _doc, context, previous) {
|
|
7
9
|
if (previous !== null && sameFormattingInputs(previous, table)) return [];
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
const tblPr = table.attrs.tblPr;
|
|
11
|
+
const attrs = {
|
|
12
|
+
...table.attrs,
|
|
13
|
+
...tableStyleAttrs(
|
|
14
|
+
typeof tblPr === "string" ? parsePropsXml(tblPr) : null,
|
|
15
|
+
context.document.formatting
|
|
16
|
+
)
|
|
17
|
+
};
|
|
18
|
+
const current = table.hasMarkup(table.type, attrs, table.marks) ? table : table.type.create(attrs, table.content, table.marks);
|
|
19
|
+
return [
|
|
20
|
+
{ pos, attrs },
|
|
21
|
+
...cellFixes(current).map((fix) => ({
|
|
22
|
+
pos: pos + 1 + fix.pos,
|
|
23
|
+
attrs: fix.attrs
|
|
24
|
+
}))
|
|
25
|
+
];
|
|
12
26
|
}
|
|
13
27
|
};
|
|
14
28
|
export {
|
package/dist/model/format.d.ts
CHANGED
|
@@ -188,6 +188,45 @@ export interface CellMargins {
|
|
|
188
188
|
bottomPt: number | null;
|
|
189
189
|
leftPt: number | null;
|
|
190
190
|
}
|
|
191
|
+
/**
|
|
192
|
+
* The parts of a table a table style formats conditionally (`w:tblStylePr/@w:type`,
|
|
193
|
+
* ST_TblStyleOverrideType), in the order ECMA-376 Part 1 §17.7.6 applies them: a format standing
|
|
194
|
+
* later in this list is laid over one standing earlier.
|
|
195
|
+
*/
|
|
196
|
+
export declare const TABLE_STYLE_CONDITIONS: readonly ["wholeTable", "band1Vert", "band2Vert", "band1Horz", "band2Horz", "firstRow", "lastRow", "firstCol", "lastCol", "nwCell", "neCell", "swCell", "seCell"];
|
|
197
|
+
export type TableStyleOverrideType = (typeof TABLE_STYLE_CONDITIONS)[number];
|
|
198
|
+
/**
|
|
199
|
+
* How many rows one row band and how many columns one column band are made of
|
|
200
|
+
* (`w:tblStyleRowBandSize`, `w:tblStyleColBandSize`). A style that says nothing leaves the side
|
|
201
|
+
* null, and its bands are then one row or one column wide.
|
|
202
|
+
*/
|
|
203
|
+
export interface BandSizes {
|
|
204
|
+
row: number | null;
|
|
205
|
+
col: number | null;
|
|
206
|
+
}
|
|
207
|
+
/** The lines one conditional format draws around the region it covers. A side it says nothing about is null */
|
|
208
|
+
export interface CellStyleBorders {
|
|
209
|
+
top: string | null;
|
|
210
|
+
bottom: string | null;
|
|
211
|
+
left: string | null;
|
|
212
|
+
right: string | null;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* What one conditional format of a table style lays down for the cells it covers.
|
|
216
|
+
*
|
|
217
|
+
* Its lines are those of the region: `borders` is drawn where a cell lies on the edge of the
|
|
218
|
+
* region and `inside` between the cells within it, the same way a table's own lines are drawn
|
|
219
|
+
* around and between its cells.
|
|
220
|
+
*/
|
|
221
|
+
export interface CellStyleFormat {
|
|
222
|
+
background: string | null;
|
|
223
|
+
borders: CellStyleBorders;
|
|
224
|
+
inside: InsideBorders;
|
|
225
|
+
margins: CellMargins;
|
|
226
|
+
verticalAlign: CellVerticalAlign | null;
|
|
227
|
+
}
|
|
228
|
+
/** The conditional formats a table style lays down for its cells, by the part of the table each covers */
|
|
229
|
+
export type TableStyleConditions = Readonly<Partial<Record<TableStyleOverrideType, CellStyleFormat>>>;
|
|
191
230
|
/**
|
|
192
231
|
* How high a row stands, in points.
|
|
193
232
|
* `atLeast` is a floor the row grows past when its text needs the room, and `exact` pins
|
|
@@ -251,6 +290,17 @@ export declare function toInsideBorders(value: unknown): InsideBorders | null;
|
|
|
251
290
|
* there at all, so it is null.
|
|
252
291
|
*/
|
|
253
292
|
export declare function toCellMargins(value: unknown): CellMargins | null;
|
|
293
|
+
/**
|
|
294
|
+
* The band sizes a table style laid down, as they come back in from `data-style-bands`.
|
|
295
|
+
* A style that said nothing about either is the same as the attribute not being there at all.
|
|
296
|
+
*/
|
|
297
|
+
export declare function toBandSizes(value: unknown): BandSizes | null;
|
|
298
|
+
/**
|
|
299
|
+
* The conditional formats a table style laid down for its cells, as they come back in from
|
|
300
|
+
* `data-style-conditions`. A part no table has is not one this editor draws, so it is dropped;
|
|
301
|
+
* a table whose style dresses no part at all is the same as the attribute not being there.
|
|
302
|
+
*/
|
|
303
|
+
export declare function toTableStyleConditions(value: unknown): TableStyleConditions | null;
|
|
254
304
|
export declare function toRowFormat(value: unknown): RowFormat | null;
|
|
255
305
|
export declare function toCellFormat(value: unknown): CellFormat | null;
|
|
256
306
|
/**
|
package/dist/model/format.js
CHANGED
|
@@ -74,6 +74,21 @@ var RUN_FORMAT_KEYS = [
|
|
|
74
74
|
"verticalAlign",
|
|
75
75
|
"lang"
|
|
76
76
|
];
|
|
77
|
+
var TABLE_STYLE_CONDITIONS = [
|
|
78
|
+
"wholeTable",
|
|
79
|
+
"band1Vert",
|
|
80
|
+
"band2Vert",
|
|
81
|
+
"band1Horz",
|
|
82
|
+
"band2Horz",
|
|
83
|
+
"firstRow",
|
|
84
|
+
"lastRow",
|
|
85
|
+
"firstCol",
|
|
86
|
+
"lastCol",
|
|
87
|
+
"nwCell",
|
|
88
|
+
"neCell",
|
|
89
|
+
"swCell",
|
|
90
|
+
"seCell"
|
|
91
|
+
];
|
|
77
92
|
function isRecord(value) {
|
|
78
93
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
79
94
|
}
|
|
@@ -261,6 +276,53 @@ function toCellMargins(value) {
|
|
|
261
276
|
];
|
|
262
277
|
return sides.every((side) => side === null) ? null : margins;
|
|
263
278
|
}
|
|
279
|
+
function toBandSizes(value) {
|
|
280
|
+
if (!isRecord(value)) return null;
|
|
281
|
+
const size = (side) => typeof side === "number" && Number.isInteger(side) && side > 0 ? side : null;
|
|
282
|
+
const bands = { row: size(value.row), col: size(value.col) };
|
|
283
|
+
return bands.row === null && bands.col === null ? null : bands;
|
|
284
|
+
}
|
|
285
|
+
function toCellStyleBorders(value) {
|
|
286
|
+
if (!isRecord(value)) {
|
|
287
|
+
return { top: null, bottom: null, left: null, right: null };
|
|
288
|
+
}
|
|
289
|
+
return {
|
|
290
|
+
top: matching(BORDER_CSS, value.top) ?? null,
|
|
291
|
+
bottom: matching(BORDER_CSS, value.bottom) ?? null,
|
|
292
|
+
left: matching(BORDER_CSS, value.left) ?? null,
|
|
293
|
+
right: matching(BORDER_CSS, value.right) ?? null
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
function toCellStyleFormat(value) {
|
|
297
|
+
if (!isRecord(value)) return null;
|
|
298
|
+
return {
|
|
299
|
+
background: matching(FILL, value.background) ?? null,
|
|
300
|
+
borders: toCellStyleBorders(value.borders),
|
|
301
|
+
inside: toInsideBorders(value.inside) ?? {
|
|
302
|
+
horizontal: null,
|
|
303
|
+
vertical: null
|
|
304
|
+
},
|
|
305
|
+
margins: toCellMargins(value.margins) ?? {
|
|
306
|
+
topPt: null,
|
|
307
|
+
rightPt: null,
|
|
308
|
+
bottomPt: null,
|
|
309
|
+
leftPt: null
|
|
310
|
+
},
|
|
311
|
+
verticalAlign: isCellVerticalAlign(value.verticalAlign) ? value.verticalAlign : null
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
function toTableStyleConditions(value) {
|
|
315
|
+
if (!isRecord(value)) return null;
|
|
316
|
+
const conditions = {};
|
|
317
|
+
let found = false;
|
|
318
|
+
for (const type of TABLE_STYLE_CONDITIONS) {
|
|
319
|
+
const format = toCellStyleFormat(value[type]);
|
|
320
|
+
if (format === null) continue;
|
|
321
|
+
conditions[type] = format;
|
|
322
|
+
found = true;
|
|
323
|
+
}
|
|
324
|
+
return found ? conditions : null;
|
|
325
|
+
}
|
|
264
326
|
function toRowHeight(value) {
|
|
265
327
|
if (!isRecord(value) || typeof value.pt !== "number") return null;
|
|
266
328
|
if (value.rule === "exact") return { rule: "exact", pt: value.pt };
|
|
@@ -300,9 +362,11 @@ function spanCount(value) {
|
|
|
300
362
|
export {
|
|
301
363
|
NO_FILL,
|
|
302
364
|
RUN_FORMAT_KEYS,
|
|
365
|
+
TABLE_STYLE_CONDITIONS,
|
|
303
366
|
isHighlightName,
|
|
304
367
|
isUnderlineKind,
|
|
305
368
|
spanCount,
|
|
369
|
+
toBandSizes,
|
|
306
370
|
toCellFormat,
|
|
307
371
|
toCellMargins,
|
|
308
372
|
toColWidth,
|
|
@@ -312,6 +376,7 @@ export {
|
|
|
312
376
|
toRowFormat,
|
|
313
377
|
toRunFormat,
|
|
314
378
|
toTableFormat,
|
|
379
|
+
toTableStyleConditions,
|
|
315
380
|
toTableWidth,
|
|
316
381
|
widthNumber,
|
|
317
382
|
withWidthNumber
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The register of list definitions a document node carries.
|
|
3
|
+
*
|
|
4
|
+
* A list started while editing is registered with the definition it was started with, and the
|
|
5
|
+
* register rides on the document node itself: undo takes a registration back with the edit that
|
|
6
|
+
* made it, and the export writes out what was registered rather than working a definition out for
|
|
7
|
+
* itself. Only what `abstractNumXml` writes in full may be registered, and the value is read back
|
|
8
|
+
* through this file so that nothing else can end up in the register.
|
|
9
|
+
*/
|
|
10
|
+
import { type NewList } from "./parseNumbering";
|
|
11
|
+
/** The lists registered on one document, by the number each was given */
|
|
12
|
+
export type NewLists = ReadonlyMap<number, NewList>;
|
|
13
|
+
export declare const NO_NEW_LISTS: NewLists;
|
|
14
|
+
/** The document attr the register travels on */
|
|
15
|
+
export declare const NEW_LISTS_ATTR = "newLists";
|
|
16
|
+
/**
|
|
17
|
+
* What the document node carries for a register.
|
|
18
|
+
*
|
|
19
|
+
* The entries run in the order of the numbers they define so that the same register is always the
|
|
20
|
+
* same value, whichever order the lists were started in, and an empty register is the null a
|
|
21
|
+
* document that never started a list carries: a list left is then a list never started.
|
|
22
|
+
*/
|
|
23
|
+
export declare function newListsValue(lists: NewLists): unknown;
|
|
24
|
+
/**
|
|
25
|
+
* The register a document node carries. Empty for a node carrying none, and for one carrying an
|
|
26
|
+
* entry that is not a list definition: a definition the writer cannot read back in full is a
|
|
27
|
+
* definition it must not write half of.
|
|
28
|
+
*/
|
|
29
|
+
export declare function newListsOf(value: unknown): NewLists;
|
|
30
|
+
/**
|
|
31
|
+
* The register with the definitions of the lists nothing wears any more taken out.
|
|
32
|
+
*
|
|
33
|
+
* A number goes back into circulation with its definition, so leaving a list is exactly what
|
|
34
|
+
* joining it was not: pressing the list button twice gives back the document that was there
|
|
35
|
+
* before the first press.
|
|
36
|
+
*/
|
|
37
|
+
export declare function listsWorn(lists: NewLists, worn: ReadonlySet<number>): NewLists;
|