@portone/docx-editor 0.2.0 → 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/CHANGELOG.md +174 -0
- package/CONTRIBUTING.md +9 -1
- package/dist/DocxEditor.d.ts +6 -0
- package/dist/DocxEditor.js +12 -29
- package/dist/core.d.ts +23 -8
- package/dist/core.js +9 -4
- package/dist/docx/cloning.d.ts +38 -0
- package/dist/docx/cloning.js +74 -0
- package/dist/docx/commentOnlyChange.d.ts +14 -7
- package/dist/docx/commentOnlyChange.js +9 -139
- package/dist/docx/comments/constants.d.ts +3 -4
- package/dist/docx/comments/constants.js +4 -5
- package/dist/docx/comments/grammar.d.ts +86 -0
- package/dist/docx/comments/grammar.js +223 -0
- package/dist/docx/comments/parts.d.ts +52 -0
- package/dist/docx/comments/parts.js +279 -0
- package/dist/docx/comments/people.d.ts +11 -2
- package/dist/docx/comments/people.js +28 -106
- package/dist/docx/comments/policy.d.ts +7 -0
- package/dist/docx/comments/policy.js +43 -0
- package/dist/docx/comments/reading.d.ts +5 -3
- package/dist/docx/comments/reading.js +30 -29
- package/dist/docx/comments/writing.d.ts +19 -7
- package/dist/docx/comments/writing.js +105 -137
- package/dist/docx/documentSettings.d.ts +7 -0
- package/dist/docx/documentSettings.js +10 -2
- package/dist/docx/exportDocx.d.ts +40 -5
- package/dist/docx/exportDocx.js +71 -72
- package/dist/docx/exportRefs.d.ts +5 -2
- package/dist/docx/exportRefs.js +3 -1
- package/dist/docx/fidelity.d.ts +43 -0
- package/dist/docx/fidelity.js +64 -0
- package/dist/docx/formatting/attrs.d.ts +27 -0
- package/dist/docx/formatting/attrs.js +31 -0
- package/dist/docx/formatting/context.d.ts +31 -0
- package/dist/docx/formatting/context.js +42 -0
- package/dist/docx/formatting/direct.d.ts +6 -5
- package/dist/docx/formatting/direct.js +52 -81
- package/dist/docx/formatting/resolve.d.ts +45 -0
- package/dist/docx/formatting/resolve.js +140 -0
- package/dist/docx/formatting/runProperties.d.ts +93 -0
- package/dist/docx/formatting/runProperties.js +316 -0
- package/dist/docx/formatting/styles.js +3 -3
- package/dist/docx/formatting/tabStops.js +7 -22
- package/dist/docx/formatting.d.ts +4 -1
- package/dist/docx/formatting.js +4 -1
- package/dist/docx/headersFooters.js +13 -22
- package/dist/docx/identities.d.ts +67 -0
- package/dist/docx/identities.js +174 -0
- package/dist/docx/importDocx.d.ts +12 -1
- package/dist/docx/importDocx.js +72 -79
- package/dist/docx/importParagraph.d.ts +1 -1
- package/dist/docx/importParagraph.js +4 -3
- package/dist/docx/importTable.d.ts +1 -1
- package/dist/docx/importTable.js +17 -1
- package/dist/docx/invariants.d.ts +33 -0
- package/dist/docx/invariants.js +256 -0
- package/dist/docx/media.d.ts +6 -4
- package/dist/docx/media.js +6 -37
- package/dist/docx/newLists.d.ts +20 -0
- package/dist/docx/newLists.js +36 -0
- package/dist/docx/notes.js +6 -12
- package/dist/docx/numberingPlanner.d.ts +8 -0
- package/dist/docx/numberingPlanner.js +19 -0
- package/dist/docx/packageParts.d.ts +42 -0
- package/dist/docx/packageParts.js +135 -0
- package/dist/docx/pageGeometry.d.ts +2 -0
- package/dist/docx/pageGeometry.js +18 -9
- package/dist/docx/paraProps.d.ts +10 -22
- package/dist/docx/paraProps.js +75 -99
- package/dist/docx/partPlan.d.ts +36 -0
- package/dist/docx/partPlan.js +59 -0
- package/dist/docx/protectionPolicy.d.ts +127 -0
- package/dist/docx/protectionPolicy.js +169 -0
- package/dist/docx/relationships.d.ts +1 -1
- package/dist/docx/relationships.js +15 -15
- package/dist/docx/runProps.d.ts +9 -22
- package/dist/docx/runProps.js +15 -168
- package/dist/docx/scan.js +20 -52
- package/dist/docx/sdt.js +14 -37
- package/dist/docx/sdtProps.d.ts +8 -1
- package/dist/docx/sdtProps.js +10 -0
- package/dist/docx/serializeBlock.d.ts +2 -0
- package/dist/docx/serializeBlock.js +9 -7
- package/dist/docx/serializeParagraph.d.ts +2 -1
- package/dist/docx/serializeParagraph.js +25 -14
- package/dist/docx/serializeTable.js +63 -32
- package/dist/docx/session.d.ts +42 -14
- package/dist/docx/session.js +43 -17
- package/dist/docx/storyProjection.d.ts +36 -0
- package/dist/docx/storyProjection.js +23 -0
- package/dist/docx/tableFormatting/editing.js +130 -185
- package/dist/docx/tableFormatting/reading.d.ts +16 -6
- package/dist/docx/tableFormatting/reading.js +40 -20
- package/dist/docx/tableTemplate.js +37 -5
- package/dist/docx/theme.d.ts +0 -6
- package/dist/docx/theme.js +0 -8
- package/dist/download.d.ts +8 -5
- package/dist/download.js +2 -0
- package/dist/editor/clipboard/images.js +17 -3
- package/dist/editor/clipboard/inlineFormatting.d.ts +7 -0
- package/dist/editor/clipboard/inlineFormatting.js +20 -30
- package/dist/editor/commands/breakCommands.js +4 -3
- package/dist/editor/commands/canRunCommand.d.ts +2 -2
- package/dist/editor/commands/canRunCommand.js +1 -1
- package/dist/editor/commands/comments/editing.d.ts +6 -2
- package/dist/editor/commands/comments/editing.js +21 -36
- package/dist/editor/commands/exportQueries.d.ts +15 -0
- package/dist/editor/commands/exportQueries.js +14 -0
- package/dist/editor/commands/fidelityQueries.d.ts +12 -0
- package/dist/editor/commands/fidelityQueries.js +8 -0
- package/dist/editor/commands/formatting/editing.d.ts +2 -2
- package/dist/editor/commands/formatting/editing.js +26 -108
- package/dist/editor/commands/formatting/propertyCommands.d.ts +21 -0
- package/dist/editor/commands/formatting/propertyCommands.js +103 -0
- package/dist/editor/commands/formatting/shared.d.ts +3 -3
- package/dist/editor/commands/formatting/shared.js +5 -2
- package/dist/editor/commands/historyCommands.js +2 -1
- package/dist/editor/commands/indentCommands.js +5 -4
- package/dist/editor/commands/index.d.ts +11 -0
- package/dist/editor/commands/index.js +5 -0
- package/dist/editor/commands/linkCommands.js +5 -6
- package/dist/editor/commands/listCommands.js +8 -10
- package/dist/editor/commands/lockCommands.d.ts +7 -1
- package/dist/editor/commands/lockCommands.js +35 -37
- package/dist/editor/commands/paragraphCommands.js +28 -31
- package/dist/editor/commands/spacingCommands.js +1 -1
- package/dist/editor/commands/tabCommands.js +4 -3
- package/dist/editor/createEditor.d.ts +17 -29
- package/dist/editor/createEditor.js +61 -67
- package/dist/editor/documentStyles.d.ts +11 -35
- package/dist/editor/documentStyles.js +9 -52
- package/dist/editor/editorDocument.d.ts +56 -0
- package/dist/editor/editorDocument.js +74 -0
- package/dist/editor/externalClipboard.js +166 -33
- package/dist/editor/insertImage.js +4 -3
- package/dist/editor/insertTable.js +4 -3
- package/dist/editor/paragraphEdits.d.ts +11 -18
- package/dist/editor/paragraphEdits.js +7 -16
- package/dist/editor/plugins/displayDerivation.d.ts +47 -0
- package/dist/editor/plugins/displayDerivation.js +78 -0
- package/dist/editor/plugins/documentProtection.d.ts +3 -3
- package/dist/editor/plugins/imagePaste.js +4 -3
- package/dist/editor/plugins/keymap.js +18 -3
- package/dist/editor/plugins/lockedContent.d.ts +4 -4
- package/dist/editor/plugins/lockedContent.js +1 -1
- package/dist/editor/plugins/numberingDecorations.d.ts +5 -11
- package/dist/editor/plugins/numberingDecorations.js +7 -17
- package/dist/editor/plugins/paragraphDisplay.d.ts +7 -0
- package/dist/editor/plugins/paragraphDisplay.js +51 -0
- package/dist/editor/plugins/tabLayout.js +1 -1
- package/dist/editor/plugins/tableDisplay.d.ts +6 -0
- package/dist/editor/plugins/tableDisplay.js +16 -0
- package/dist/index.d.ts +2 -0
- package/dist/model/format.d.ts +30 -8
- package/dist/model/format.js +35 -22
- package/dist/model/tabStops.d.ts +9 -0
- package/dist/model/tabStops.js +18 -0
- package/dist/numbering/listTemplate.js +49 -10
- package/dist/numbering/parseNumbering.d.ts +14 -1
- package/dist/numbering/parseNumbering.js +35 -22
- package/dist/numbering/writeNumbering.d.ts +3 -4
- package/dist/numbering/writeNumbering.js +14 -25
- package/dist/ooxml/childOrder.d.ts +34 -0
- package/dist/ooxml/childOrder.js +496 -0
- package/dist/ooxml/element.d.ts +65 -0
- package/dist/ooxml/element.js +68 -0
- package/dist/ooxml/errors.d.ts +3 -2
- package/dist/ooxml/fragment.d.ts +53 -0
- package/dist/ooxml/fragment.js +76 -0
- package/dist/ooxml/image.d.ts +4 -0
- package/dist/ooxml/image.js +3 -5
- package/dist/ooxml/names.d.ts +27 -0
- package/dist/ooxml/names.js +29 -0
- package/dist/ooxml/partSplice.d.ts +67 -0
- package/dist/ooxml/partSplice.js +169 -0
- package/dist/ooxml/precedence.d.ts +31 -0
- package/dist/ooxml/precedence.js +51 -0
- package/dist/ooxml/props.d.ts +112 -0
- package/dist/ooxml/props.js +214 -0
- package/dist/ooxml/simpleTypes.d.ts +103 -0
- package/dist/ooxml/simpleTypes.js +182 -0
- package/dist/ooxml/tabStops.js +8 -27
- package/dist/ooxml/tagScan.d.ts +34 -0
- package/dist/ooxml/tagScan.js +108 -0
- package/dist/ooxml/units.d.ts +25 -13
- package/dist/ooxml/units.js +65 -31
- package/dist/ooxml/xml.d.ts +52 -0
- package/dist/ooxml/xml.js +64 -5
- package/dist/page/blockKinds.d.ts +103 -0
- package/dist/page/blockKinds.js +11 -0
- package/dist/page/kinds/index.d.ts +6 -0
- package/dist/page/kinds/index.js +10 -0
- package/dist/page/kinds/paragraphKind.d.ts +10 -0
- package/dist/page/kinds/paragraphKind.js +71 -0
- package/dist/page/kinds/tableKind.d.ts +10 -0
- package/dist/page/kinds/tableKind.js +178 -0
- package/dist/page/measureBlocks.d.ts +3 -1
- package/dist/page/measureBlocks.js +24 -25
- package/dist/page/pageDecorations.d.ts +27 -28
- package/dist/page/pageDecorations.js +70 -151
- package/dist/page/pageLayout.d.ts +10 -62
- package/dist/page/pageLayout.js +60 -53
- package/dist/page/usePageLayout.d.ts +2 -2
- package/dist/page/usePageLayout.js +3 -11
- package/dist/schema/attrRoles.d.ts +42 -0
- package/dist/schema/attrRoles.js +141 -0
- package/dist/schema/displayDerivation.d.ts +82 -0
- package/dist/schema/displayDerivation.js +130 -0
- package/dist/schema/docxSchema.d.ts +9 -0
- package/dist/schema/docxSchema.js +237 -114
- package/dist/schema/editGuard.d.ts +99 -0
- package/dist/schema/editGuard.js +43 -0
- package/dist/schema/guards.d.ts +91 -0
- package/dist/schema/guards.js +108 -0
- package/dist/schema/index.d.ts +2 -0
- package/dist/schema/index.js +2 -0
- package/dist/schema/locks.d.ts +17 -77
- package/dist/schema/locks.js +26 -70
- package/dist/schema/preservedGuards.d.ts +50 -0
- package/dist/schema/preservedGuards.js +80 -0
- package/dist/schema/protection.d.ts +17 -4
- package/dist/schema/protection.js +4 -2
- package/dist/schema/sourceEquality.d.ts +19 -0
- package/dist/schema/sourceEquality.js +39 -0
- package/dist/styles/inlineStyle.js +15 -6
- package/dist/table/cellFormatting.d.ts +8 -1
- package/dist/table/cellFormatting.js +10 -17
- package/dist/table/commands.d.ts +2 -2
- package/dist/table/commands.js +20 -18
- package/dist/table/format.d.ts +3 -0
- package/dist/table/format.js +3 -9
- package/dist/table/gridBorders.d.ts +14 -16
- package/dist/table/gridBorders.js +3 -41
- package/dist/table/index.d.ts +2 -2
- package/dist/table/merge.d.ts +4 -8
- package/dist/table/merge.js +5 -5
- package/package.json +10 -3
- package/dist/docx/comments/contentTypes.d.ts +0 -7
- package/dist/docx/comments/contentTypes.js +0 -38
- package/dist/docx/formatting/effectiveParagraph.d.ts +0 -15
- package/dist/docx/formatting/effectiveParagraph.js +0 -81
- package/dist/docx/propsXml.d.ts +0 -49
- package/dist/docx/propsXml.js +0 -302
- package/dist/docx/uniqueControls.d.ts +0 -14
- package/dist/docx/uniqueControls.js +0 -62
- package/dist/editor/plugins/bookmarkProtection.d.ts +0 -3
- package/dist/editor/plugins/bookmarkProtection.js +0 -28
- package/dist/editor/plugins/commentReservations.d.ts +0 -5
- package/dist/editor/plugins/commentReservations.js +0 -26
- package/dist/editor/plugins/noteProtection.d.ts +0 -3
- package/dist/editor/plugins/noteProtection.js +0 -25
- package/dist/editor/plugins/styledParagraphs.d.ts +0 -15
- package/dist/editor/plugins/styledParagraphs.js +0 -65
- package/dist/page/tableMeasurements.d.ts +0 -10
- package/dist/page/tableMeasurements.js +0 -115
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
// src/page/kinds/tableKind.ts
|
|
2
|
+
import { Decoration } from "prosemirror-view";
|
|
3
|
+
import { toRowFormat } from "../../model/format.js";
|
|
4
|
+
import { editorAttributes } from "../../styles/classNames.js";
|
|
5
|
+
var PAGE_BREAK_BR = `br[${editorAttributes.breakType}="page"]`;
|
|
6
|
+
function span(value) {
|
|
7
|
+
return typeof value === "number" && Number.isInteger(value) && value > 0 ? value : 1;
|
|
8
|
+
}
|
|
9
|
+
function paginationRows(table) {
|
|
10
|
+
return Array.from(
|
|
11
|
+
table.querySelectorAll(
|
|
12
|
+
`[${editorAttributes.tablePageSpace}],[${editorAttributes.tableRepeatedHeader}]`
|
|
13
|
+
)
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
function before(candidate, row) {
|
|
17
|
+
return Boolean(
|
|
18
|
+
candidate.compareDocumentPosition(row) & Node.DOCUMENT_POSITION_FOLLOWING
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
function rowEntries(view, tableNode, tablePos, tableDom, appliedRows, scale) {
|
|
22
|
+
const entries = [];
|
|
23
|
+
const tableTop = tableDom.getBoundingClientRect().top;
|
|
24
|
+
tableNode.forEach((row, offset) => {
|
|
25
|
+
const pos = tablePos + 1 + offset;
|
|
26
|
+
const dom = view.nodeDOM(pos);
|
|
27
|
+
if (!(dom instanceof HTMLElement)) return;
|
|
28
|
+
const rect = dom.getBoundingClientRect();
|
|
29
|
+
const appliedBefore = appliedRows.reduce(
|
|
30
|
+
(total, candidate) => total + (before(candidate, dom) ? candidate.getBoundingClientRect().height / scale : 0),
|
|
31
|
+
0
|
|
32
|
+
);
|
|
33
|
+
entries.push({
|
|
34
|
+
node: row,
|
|
35
|
+
pos,
|
|
36
|
+
top: (rect.top - tableTop) / scale - appliedBefore,
|
|
37
|
+
height: rect.height / scale
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
return entries;
|
|
41
|
+
}
|
|
42
|
+
function unsafeBoundaries(rows) {
|
|
43
|
+
const unsafe = /* @__PURE__ */ new Set();
|
|
44
|
+
rows.forEach((row, rowIndex) => {
|
|
45
|
+
row.node.forEach((cell) => {
|
|
46
|
+
const bottom = Math.min(rows.length, rowIndex + span(cell.attrs.rowspan));
|
|
47
|
+
for (let boundary = rowIndex + 1; boundary < bottom; boundary += 1) {
|
|
48
|
+
unsafe.add(boundary);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
return unsafe;
|
|
53
|
+
}
|
|
54
|
+
function repeatsHeader(row) {
|
|
55
|
+
return toRowFormat(row?.attrs.format)?.repeatHeader === true;
|
|
56
|
+
}
|
|
57
|
+
function headerCount(rows) {
|
|
58
|
+
let count = 0;
|
|
59
|
+
while (count < rows.length && repeatsHeader(rows[count]?.node)) {
|
|
60
|
+
count += 1;
|
|
61
|
+
}
|
|
62
|
+
return count;
|
|
63
|
+
}
|
|
64
|
+
function headerRowsOf(tableNode, tablePos) {
|
|
65
|
+
const found = [];
|
|
66
|
+
let offset = 0;
|
|
67
|
+
for (let index = 0; index < tableNode.childCount; index += 1) {
|
|
68
|
+
const row = tableNode.child(index);
|
|
69
|
+
if (!repeatsHeader(row)) break;
|
|
70
|
+
found.push(tablePos + 1 + offset);
|
|
71
|
+
offset += row.nodeSize;
|
|
72
|
+
}
|
|
73
|
+
return found;
|
|
74
|
+
}
|
|
75
|
+
function columnCount(tableNode) {
|
|
76
|
+
const grid = tableNode.attrs.gridCols;
|
|
77
|
+
if (Array.isArray(grid) && grid.length > 0) return grid.length;
|
|
78
|
+
const first = tableNode.firstChild;
|
|
79
|
+
if (!first) return 1;
|
|
80
|
+
let columns = 0;
|
|
81
|
+
first.forEach((cell) => {
|
|
82
|
+
columns += span(cell.attrs.colspan);
|
|
83
|
+
});
|
|
84
|
+
return Math.max(1, columns);
|
|
85
|
+
}
|
|
86
|
+
function tableSpace(height, columns) {
|
|
87
|
+
const row = document.createElement("tr");
|
|
88
|
+
row.setAttribute(editorAttributes.tablePageSpace, `${height}`);
|
|
89
|
+
row.setAttribute("aria-hidden", "true");
|
|
90
|
+
row.setAttribute("contenteditable", "false");
|
|
91
|
+
row.style.height = `${height}px`;
|
|
92
|
+
const cell = document.createElement("td");
|
|
93
|
+
cell.colSpan = columns;
|
|
94
|
+
cell.style.height = `${height}px`;
|
|
95
|
+
row.append(cell);
|
|
96
|
+
return row;
|
|
97
|
+
}
|
|
98
|
+
function repeatedHeader(view, rowPos) {
|
|
99
|
+
const source = view.nodeDOM(rowPos);
|
|
100
|
+
const row = source instanceof HTMLElement && source.tagName === "TR" ? source.cloneNode(true) : document.createElement("tr");
|
|
101
|
+
row.setAttribute(editorAttributes.tableRepeatedHeader, "");
|
|
102
|
+
row.setAttribute("aria-hidden", "true");
|
|
103
|
+
row.setAttribute("contenteditable", "false");
|
|
104
|
+
row.removeAttribute("id");
|
|
105
|
+
row.querySelectorAll("[id]").forEach((element) => {
|
|
106
|
+
element.removeAttribute("id");
|
|
107
|
+
});
|
|
108
|
+
return row;
|
|
109
|
+
}
|
|
110
|
+
var tableKind = {
|
|
111
|
+
name: "table",
|
|
112
|
+
matches: (node) => node.type.spec.tableRole === "table",
|
|
113
|
+
measure({ view, node, pos, dom, scale }) {
|
|
114
|
+
const appliedRows = paginationRows(dom);
|
|
115
|
+
const rows = rowEntries(view, node, pos, dom, appliedRows, scale);
|
|
116
|
+
const headers = headerCount(rows);
|
|
117
|
+
const unsafe = unsafeBoundaries(rows);
|
|
118
|
+
const headerRows = rows.slice(0, headers);
|
|
119
|
+
const repeatHeaderHeight = headerRows.reduce(
|
|
120
|
+
(total, row) => total + row.height,
|
|
121
|
+
0
|
|
122
|
+
);
|
|
123
|
+
const candidates = [];
|
|
124
|
+
for (let rowIndex = Math.max(1, headers); rowIndex < rows.length; rowIndex += 1) {
|
|
125
|
+
const row = rows[rowIndex];
|
|
126
|
+
if (row && !unsafe.has(rowIndex)) {
|
|
127
|
+
candidates.push({
|
|
128
|
+
at: row.pos,
|
|
129
|
+
offset: row.top,
|
|
130
|
+
forced: false,
|
|
131
|
+
repeatHeight: repeatHeaderHeight
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
const firstBodyBoundary = candidates.find(
|
|
136
|
+
(candidate) => candidate.offset > repeatHeaderHeight + 0.5
|
|
137
|
+
);
|
|
138
|
+
const appliedHeight = appliedRows.reduce(
|
|
139
|
+
(total, row) => total + row.getBoundingClientRect().height / scale,
|
|
140
|
+
0
|
|
141
|
+
);
|
|
142
|
+
return {
|
|
143
|
+
candidates,
|
|
144
|
+
// The smallest useful first piece: the headers followed by one body row group
|
|
145
|
+
minFirstPiece: firstBodyBoundary?.offset ?? dom.getBoundingClientRect().height / scale - appliedHeight,
|
|
146
|
+
// A space cannot be opened inside a cell, so a break standing in one is answered after the
|
|
147
|
+
// whole table
|
|
148
|
+
breakAfter: dom.querySelectorAll(PAGE_BREAK_BR).length > 0,
|
|
149
|
+
appliedHeight
|
|
150
|
+
};
|
|
151
|
+
},
|
|
152
|
+
holdsCut: (doc, at) => doc.nodeAt(at)?.type.spec.tableRole === "row",
|
|
153
|
+
decorate(pos, node, cuts, into) {
|
|
154
|
+
if (cuts.length === 0) return;
|
|
155
|
+
const headerRows = headerRowsOf(node, pos);
|
|
156
|
+
const columns = columnCount(node);
|
|
157
|
+
for (const cut of cuts) {
|
|
158
|
+
into.push(
|
|
159
|
+
Decoration.widget(cut.at, () => tableSpace(cut.height, columns), {
|
|
160
|
+
key: `table-page-space-${cut.at}-${cut.height}-${columns}`,
|
|
161
|
+
side: -100
|
|
162
|
+
})
|
|
163
|
+
);
|
|
164
|
+
headerRows.forEach((rowPos, headerIndex) => {
|
|
165
|
+
into.push(
|
|
166
|
+
// No `key`: a keyed widget is held to be the same one and left alone, and this one is a
|
|
167
|
+
// copy of a row that the very edit rebuilding these decorations may have just changed
|
|
168
|
+
Decoration.widget(cut.at, (view) => repeatedHeader(view, rowPos), {
|
|
169
|
+
side: -90 + headerIndex
|
|
170
|
+
})
|
|
171
|
+
);
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
export {
|
|
177
|
+
tableKind
|
|
178
|
+
};
|
|
@@ -2,13 +2,15 @@
|
|
|
2
2
|
* Measures the sheet as drawn on screen: the height of each body block, the gap above it, and
|
|
3
3
|
* where the page breaks inside it stand, with nothing computed line by line.
|
|
4
4
|
*
|
|
5
|
+
* Every place a block may be parted at is read as a break candidate (`page/blockKinds`) by the
|
|
6
|
+
* block's own kind (`page/kinds`), so this pass names no shape of block itself.
|
|
5
7
|
* The engine's own marks are taken back off as they are read, so a block already pushed and a
|
|
6
8
|
* break already given its space read as they would with neither applied. A measurement that read
|
|
7
9
|
* them in would add to what is already there, and the layout would creep on every pass.
|
|
8
10
|
* Where there is no layout (in tests) everything is 0, so the result is a single page.
|
|
9
11
|
*/
|
|
10
12
|
import type { EditorView } from "prosemirror-view";
|
|
11
|
-
import type
|
|
13
|
+
import { type MeasuredBlock } from "./blockKinds";
|
|
12
14
|
/**
|
|
13
15
|
* The measurements taken in order to draw the page overlay. Positions are relative to
|
|
14
16
|
* the overlay box
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
// src/page/measureBlocks.ts
|
|
2
2
|
import { editorAttributes } from "../styles/classNames.js";
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
var BREAK_SPACE = `[${editorAttributes.pageBreakSpace}]`;
|
|
3
|
+
import { blockKindFor } from "./blockKinds.js";
|
|
4
|
+
import { blockKindsOf } from "./pageDecorations.js";
|
|
6
5
|
function pixels(value) {
|
|
7
6
|
const parsed = Number.parseFloat(value);
|
|
8
7
|
return Number.isFinite(parsed) ? parsed : 0;
|
|
@@ -16,17 +15,14 @@ function appliedPush(element) {
|
|
|
16
15
|
}
|
|
17
16
|
function drawnBlocks(view) {
|
|
18
17
|
const found = [];
|
|
19
|
-
view.state.doc.forEach((
|
|
18
|
+
view.state.doc.forEach((node, offset) => {
|
|
20
19
|
const dom = view.nodeDOM(offset);
|
|
21
20
|
if (dom instanceof HTMLElement && dom.getBoundingClientRect().height > 0) {
|
|
22
|
-
found.push({ pos: offset, dom });
|
|
21
|
+
found.push({ node, pos: offset, dom });
|
|
23
22
|
}
|
|
24
23
|
});
|
|
25
24
|
return found;
|
|
26
25
|
}
|
|
27
|
-
function breakWithoutSpace(dom, spaces) {
|
|
28
|
-
return dom.querySelectorAll(PAGE_BREAK_BR).length > spaces;
|
|
29
|
-
}
|
|
30
26
|
function measureSheet(view, layer) {
|
|
31
27
|
const sheet = view.dom;
|
|
32
28
|
const sheetRect = sheet.getBoundingClientRect();
|
|
@@ -36,35 +32,38 @@ function measureSheet(view, layer) {
|
|
|
36
32
|
const contentTop = pixels(style.paddingTop);
|
|
37
33
|
const contentBottom = pixels(style.paddingBottom);
|
|
38
34
|
const sheetY = (viewportY) => (viewportY - sheetRect.top) / scale;
|
|
35
|
+
const kinds = blockKindsOf(view.state);
|
|
39
36
|
const blocks = [];
|
|
40
37
|
let previousBottom = contentTop;
|
|
41
38
|
let applied = 0;
|
|
42
|
-
|
|
43
|
-
for (const { pos, dom } of drawnBlocks(view)) {
|
|
44
|
-
const node = view.state.doc.nodeAt(pos);
|
|
39
|
+
for (const { node, pos, dom } of drawnBlocks(view)) {
|
|
45
40
|
const rect = dom.getBoundingClientRect();
|
|
46
41
|
applied += appliedPush(dom);
|
|
47
|
-
const
|
|
48
|
-
const
|
|
49
|
-
const
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
42
|
+
const above = applied;
|
|
43
|
+
const blockY = (viewportY) => sheetY(viewportY) - above;
|
|
44
|
+
const top = blockY(rect.top);
|
|
45
|
+
const measured = blockKindFor(kinds, node).measure({
|
|
46
|
+
view,
|
|
47
|
+
node,
|
|
48
|
+
pos,
|
|
49
|
+
dom,
|
|
50
|
+
sheetY: blockY,
|
|
51
|
+
top,
|
|
52
|
+
scale
|
|
53
|
+
});
|
|
54
|
+
applied += measured.appliedHeight;
|
|
57
55
|
const bottom = sheetY(rect.bottom) - applied;
|
|
58
56
|
blocks.push({
|
|
59
57
|
pos,
|
|
60
58
|
gap: top - previousBottom,
|
|
61
59
|
height: bottom - top,
|
|
62
|
-
breakBefore:
|
|
63
|
-
|
|
64
|
-
|
|
60
|
+
breakBefore: dom.hasAttribute(editorAttributes.pageBreakBefore),
|
|
61
|
+
breakAfter: measured.breakAfter,
|
|
62
|
+
candidates: measured.candidates,
|
|
63
|
+
minFirstPiece: measured.minFirstPiece,
|
|
64
|
+
keepWithNext: measured.keepWithNext ?? false
|
|
65
65
|
});
|
|
66
66
|
previousBottom = bottom;
|
|
67
|
-
breakAfterPrevious = breakWithoutSpace(dom, spaces.length);
|
|
68
67
|
}
|
|
69
68
|
return {
|
|
70
69
|
left: (sheetRect.left - layerRect.left) / scale,
|
|
@@ -1,36 +1,35 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Decorations that move content to the next page:
|
|
3
|
-
*
|
|
2
|
+
* Decorations that move content to the next page: the push above a block, and whatever the block's
|
|
3
|
+
* own kind draws the cuts inside it as (`page/kinds`).
|
|
4
4
|
*
|
|
5
|
-
* The
|
|
6
|
-
*
|
|
5
|
+
* The kinds the editor was built with are held here, so the measurement and the decorations read
|
|
6
|
+
* one registry and a block is measured by the same kind that draws it.
|
|
7
|
+
* The document model is left untouched, so no trace of any of it is left in the exported XML or
|
|
8
|
+
* in the edit history.
|
|
7
9
|
*/
|
|
8
|
-
import { Plugin } from "prosemirror-state";
|
|
10
|
+
import { type EditorState, Plugin } from "prosemirror-state";
|
|
9
11
|
import { DecorationSet, type EditorView } from "prosemirror-view";
|
|
10
|
-
import
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
* The layout counts breaks inside the block it measured (`page/pageLayout`), and an edit anywhere
|
|
15
|
-
* before a break shifts that count, so what is kept here is the position of the `br` the space
|
|
16
|
-
* belongs to: a position maps through an edit, an ordinal does not.
|
|
17
|
-
*/
|
|
18
|
-
interface SpaceMark {
|
|
19
|
-
/** Where the `br` this space is put on stands */
|
|
20
|
-
at: number;
|
|
21
|
-
height: number;
|
|
22
|
-
}
|
|
23
|
-
interface PageMarks {
|
|
12
|
+
import { type BlockKind, type PageCut } from "./blockKinds";
|
|
13
|
+
import type { BlockPush } from "./pageLayout";
|
|
14
|
+
/** Everything one measurement has to say about the page (`page/pageLayout`) */
|
|
15
|
+
export interface PageMarksInput {
|
|
24
16
|
pushes: readonly BlockPush[];
|
|
25
|
-
|
|
26
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Where the layout parted a block, keyed by the position the continued piece starts at.
|
|
19
|
+
*
|
|
20
|
+
* The layout counts within the block it measured, and an edit anywhere before a cut shifts that
|
|
21
|
+
* count, so what is kept here is a position: a position maps through an edit, an ordinal does
|
|
22
|
+
* not.
|
|
23
|
+
*/
|
|
24
|
+
cuts: readonly PageCut[];
|
|
25
|
+
}
|
|
26
|
+
interface PageMarks extends PageMarksInput {
|
|
27
|
+
kinds: readonly BlockKind[];
|
|
27
28
|
decorations: DecorationSet;
|
|
28
29
|
}
|
|
29
|
-
export declare function pageDecorations(): Plugin<PageMarks>;
|
|
30
|
-
/**
|
|
31
|
-
export declare function
|
|
32
|
-
/**
|
|
33
|
-
export declare function
|
|
34
|
-
/** Does nothing when the same table continuations are already applied */
|
|
35
|
-
export declare function setTableContinuations(view: EditorView, continuations: readonly TableContinuation[]): void;
|
|
30
|
+
export declare function pageDecorations(kinds?: readonly BlockKind[]): Plugin<PageMarks>;
|
|
31
|
+
/** The kinds the editor was built with, which the measurement reads to match them */
|
|
32
|
+
export declare function blockKindsOf(state: EditorState): readonly BlockKind[];
|
|
33
|
+
/** One transaction, or none when the same marks are already applied */
|
|
34
|
+
export declare function setPageMarks(view: EditorView, next: PageMarksInput): void;
|
|
36
35
|
export {};
|
|
@@ -1,96 +1,31 @@
|
|
|
1
1
|
// src/page/pageDecorations.ts
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
Plugin,
|
|
4
|
+
PluginKey
|
|
5
|
+
} from "prosemirror-state";
|
|
3
6
|
import { Decoration, DecorationSet } from "prosemirror-view";
|
|
4
|
-
import { docxSchema, isPageBreak } from "../schema/index.js";
|
|
5
7
|
import { editorAttributes } from "../styles/classNames.js";
|
|
8
|
+
import { blockKindFor } from "./blockKinds.js";
|
|
9
|
+
import { DEFAULT_BLOCK_KINDS } from "./kinds/index.js";
|
|
6
10
|
var marksKey = new PluginKey("docxPageDecorations");
|
|
7
11
|
function pushStyle(marginTop) {
|
|
8
12
|
return `margin-block-start:${marginTop}px`;
|
|
9
13
|
}
|
|
10
|
-
function
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
block.forEach((child, offset) => {
|
|
20
|
-
if (isPageBreakNode(child)) {
|
|
21
|
-
found.push({ at: blockPos + 1 + offset, size: child.nodeSize });
|
|
22
|
-
}
|
|
23
|
-
});
|
|
24
|
-
return found;
|
|
25
|
-
}
|
|
26
|
-
function breakSpaces(block, blockPos, heights, into) {
|
|
27
|
-
for (const { at, size } of pageBreaksIn(block, blockPos)) {
|
|
28
|
-
const height = heights.get(at) ?? 0;
|
|
29
|
-
into.push(
|
|
30
|
-
Decoration.inline(at, at + size, {
|
|
31
|
-
nodeName: "span",
|
|
32
|
-
style: spaceStyle(height),
|
|
33
|
-
[editorAttributes.pageBreakSpace]: `${height}`
|
|
34
|
-
})
|
|
35
|
-
);
|
|
14
|
+
function cutsByBlock(doc, cuts) {
|
|
15
|
+
const byBlock = /* @__PURE__ */ new Map();
|
|
16
|
+
for (const cut of cuts) {
|
|
17
|
+
const $at = doc.resolve(cut.at);
|
|
18
|
+
if ($at.depth === 0) continue;
|
|
19
|
+
const blockPos = $at.before(1);
|
|
20
|
+
const found = byBlock.get(blockPos);
|
|
21
|
+
if (found) found.push(cut);
|
|
22
|
+
else byBlock.set(blockPos, [cut]);
|
|
36
23
|
}
|
|
24
|
+
return byBlock;
|
|
37
25
|
}
|
|
38
|
-
function
|
|
39
|
-
return spaces.flatMap((space) => {
|
|
40
|
-
const block = doc.nodeAt(space.pos);
|
|
41
|
-
const found = block ? pageBreaksIn(block, space.pos)[space.index] : null;
|
|
42
|
-
return found ? [{ at: found.at, height: space.height }] : [];
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
function tableSpace(continuation) {
|
|
46
|
-
const row = document.createElement("tr");
|
|
47
|
-
row.setAttribute(editorAttributes.tablePageSpace, `${continuation.height}`);
|
|
48
|
-
row.setAttribute("aria-hidden", "true");
|
|
49
|
-
row.setAttribute("contenteditable", "false");
|
|
50
|
-
row.style.height = `${continuation.height}px`;
|
|
51
|
-
const cell = document.createElement("td");
|
|
52
|
-
cell.colSpan = continuation.columns;
|
|
53
|
-
cell.style.height = `${continuation.height}px`;
|
|
54
|
-
row.append(cell);
|
|
55
|
-
return row;
|
|
56
|
-
}
|
|
57
|
-
function repeatedHeader(view, rowPos) {
|
|
58
|
-
const source = view.nodeDOM(rowPos);
|
|
59
|
-
const row = source instanceof HTMLElement && source.tagName === "TR" ? source.cloneNode(true) : document.createElement("tr");
|
|
60
|
-
row.setAttribute(editorAttributes.tableRepeatedHeader, "");
|
|
61
|
-
row.setAttribute("aria-hidden", "true");
|
|
62
|
-
row.setAttribute("contenteditable", "false");
|
|
63
|
-
row.removeAttribute("id");
|
|
64
|
-
row.querySelectorAll("[id]").forEach((element) => {
|
|
65
|
-
element.removeAttribute("id");
|
|
66
|
-
});
|
|
67
|
-
return row;
|
|
68
|
-
}
|
|
69
|
-
function tableContinuationDecorations(continuations, into) {
|
|
70
|
-
continuations.forEach((continuation) => {
|
|
71
|
-
into.push(
|
|
72
|
-
Decoration.widget(continuation.pos, () => tableSpace(continuation), {
|
|
73
|
-
key: `table-page-space-${continuation.pos}-${continuation.height}-${continuation.columns}`,
|
|
74
|
-
side: -100
|
|
75
|
-
})
|
|
76
|
-
);
|
|
77
|
-
continuation.headerRows.forEach((rowPos, headerIndex) => {
|
|
78
|
-
into.push(
|
|
79
|
-
Decoration.widget(
|
|
80
|
-
continuation.pos,
|
|
81
|
-
(view) => repeatedHeader(view, rowPos),
|
|
82
|
-
{
|
|
83
|
-
key: `table-repeated-header-${continuation.pos}-${headerIndex}-` + continuation.headerSignature,
|
|
84
|
-
side: -90 + headerIndex
|
|
85
|
-
}
|
|
86
|
-
)
|
|
87
|
-
);
|
|
88
|
-
});
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
function decorationsFor(doc, pushes, spaces, tableContinuations) {
|
|
26
|
+
function decorationsFor(doc, kinds, pushes, cuts) {
|
|
92
27
|
const byPos = new Map(pushes.map((push) => [push.pos, push]));
|
|
93
|
-
const
|
|
28
|
+
const byBlock = cutsByBlock(doc, cuts);
|
|
94
29
|
const decorations = [];
|
|
95
30
|
doc.forEach((node, offset) => {
|
|
96
31
|
const push = byPos.get(offset);
|
|
@@ -102,65 +37,63 @@ function decorationsFor(doc, pushes, spaces, tableContinuations) {
|
|
|
102
37
|
})
|
|
103
38
|
);
|
|
104
39
|
}
|
|
105
|
-
|
|
40
|
+
blockKindFor(kinds, node).decorate(
|
|
41
|
+
offset,
|
|
42
|
+
node,
|
|
43
|
+
byBlock.get(offset) ?? [],
|
|
44
|
+
decorations
|
|
45
|
+
);
|
|
106
46
|
});
|
|
107
|
-
tableContinuationDecorations(tableContinuations, decorations);
|
|
108
47
|
return DecorationSet.create(doc, decorations);
|
|
109
48
|
}
|
|
110
|
-
function marksFor(doc,
|
|
49
|
+
function marksFor(doc, kinds, pushes, cuts) {
|
|
111
50
|
return {
|
|
51
|
+
kinds,
|
|
112
52
|
pushes,
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
decorations: decorationsFor(doc, pushes, spaces, tableContinuations)
|
|
53
|
+
cuts,
|
|
54
|
+
decorations: decorationsFor(doc, kinds, pushes, cuts)
|
|
116
55
|
};
|
|
117
56
|
}
|
|
118
|
-
function
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
}
|
|
124
|
-
function
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
}
|
|
130
|
-
function
|
|
131
|
-
return a.length === b.length && a.every((
|
|
132
|
-
const other = b[index];
|
|
133
|
-
return other !== void 0 && other.pos ===
|
|
134
|
-
|
|
135
|
-
|
|
57
|
+
function stillCuts(kinds, doc, at) {
|
|
58
|
+
const $at = doc.resolve(at);
|
|
59
|
+
if ($at.depth === 0) return false;
|
|
60
|
+
const block = doc.nodeAt($at.before(1));
|
|
61
|
+
return block !== null && blockKindFor(kinds, block).holdsCut(doc, at);
|
|
62
|
+
}
|
|
63
|
+
function retainsContent(tr, at, mapped) {
|
|
64
|
+
const node = tr.before.nodeAt(at);
|
|
65
|
+
if (!node || node.isLeaf) return false;
|
|
66
|
+
const content = tr.mapping.mapResult(at + 1, -1);
|
|
67
|
+
return !content.deletedAcross && content.pos === mapped + 1;
|
|
68
|
+
}
|
|
69
|
+
function samePageMarks(a, b) {
|
|
70
|
+
return a.pushes.length === b.pushes.length && a.cuts.length === b.cuts.length && a.pushes.every((push, index) => {
|
|
71
|
+
const other = b.pushes[index];
|
|
72
|
+
return other !== void 0 && other.pos === push.pos && other.marginTop === push.marginTop && other.push === push.push;
|
|
73
|
+
}) && a.cuts.every((cut, index) => {
|
|
74
|
+
const other = b.cuts[index];
|
|
75
|
+
return other !== void 0 && other.at === cut.at && other.height === cut.height;
|
|
136
76
|
});
|
|
137
77
|
}
|
|
138
|
-
function pageDecorations() {
|
|
78
|
+
function pageDecorations(kinds = DEFAULT_BLOCK_KINDS) {
|
|
139
79
|
return new Plugin({
|
|
140
80
|
key: marksKey,
|
|
141
81
|
state: {
|
|
142
|
-
init: (_config, state) => marksFor(state.doc,
|
|
82
|
+
init: (_config, state) => marksFor(state.doc, kinds, [], []),
|
|
143
83
|
apply(tr, value) {
|
|
144
84
|
const next = tr.getMeta(marksKey);
|
|
145
85
|
if (next) return next;
|
|
146
86
|
if (!tr.docChanged) return value;
|
|
147
87
|
return marksFor(
|
|
148
88
|
tr.doc,
|
|
89
|
+
value.kinds,
|
|
149
90
|
value.pushes.map((push) => ({
|
|
150
91
|
...push,
|
|
151
92
|
pos: tr.mapping.map(push.pos)
|
|
152
93
|
})),
|
|
153
|
-
value.
|
|
154
|
-
const
|
|
155
|
-
return
|
|
156
|
-
}),
|
|
157
|
-
value.tableContinuations.flatMap((continuation) => {
|
|
158
|
-
const pos = tr.mapping.map(continuation.pos);
|
|
159
|
-
if (tr.doc.nodeAt(pos)?.type.spec.tableRole !== "row") return [];
|
|
160
|
-
const headerRows = continuation.headerRows.map((rowPos) => tr.mapping.map(rowPos)).filter(
|
|
161
|
-
(rowPos) => tr.doc.nodeAt(rowPos)?.type.spec.tableRole === "row"
|
|
162
|
-
);
|
|
163
|
-
return [{ ...continuation, pos, headerRows }];
|
|
94
|
+
value.cuts.flatMap((cut) => {
|
|
95
|
+
const mapped = tr.mapping.mapResult(cut.at, 1);
|
|
96
|
+
return !stillCuts(value.kinds, tr.doc, mapped.pos) || mapped.deleted && !retainsContent(tr, cut.at, mapped.pos) ? [] : [{ ...cut, at: mapped.pos }];
|
|
164
97
|
})
|
|
165
98
|
);
|
|
166
99
|
}
|
|
@@ -170,42 +103,28 @@ function pageDecorations() {
|
|
|
170
103
|
}
|
|
171
104
|
});
|
|
172
105
|
}
|
|
106
|
+
function blockKindsOf(state) {
|
|
107
|
+
return marksKey.getState(state)?.kinds ?? DEFAULT_BLOCK_KINDS;
|
|
108
|
+
}
|
|
173
109
|
function current(view) {
|
|
174
|
-
return marksKey.getState(view.state) ??
|
|
110
|
+
return marksKey.getState(view.state) ?? { pushes: [], cuts: [] };
|
|
175
111
|
}
|
|
176
|
-
function
|
|
112
|
+
function setPageMarks(view, next) {
|
|
113
|
+
if (samePageMarks(current(view), next)) return;
|
|
177
114
|
view.dispatch(
|
|
178
|
-
view.state.tr.setMeta(
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
);
|
|
188
|
-
}
|
|
189
|
-
function setPageBreakSpaces(view, spaces) {
|
|
190
|
-
const marks = current(view);
|
|
191
|
-
const next = spaceMarks(view.state.doc, spaces);
|
|
192
|
-
if (sameSpaces(marks.spaces, next)) return;
|
|
193
|
-
dispatch(
|
|
194
|
-
view,
|
|
195
|
-
marksFor(view.state.doc, marks.pushes, next, marks.tableContinuations)
|
|
196
|
-
);
|
|
197
|
-
}
|
|
198
|
-
function setTableContinuations(view, continuations) {
|
|
199
|
-
const marks = current(view);
|
|
200
|
-
if (sameTableContinuations(marks.tableContinuations, continuations)) return;
|
|
201
|
-
dispatch(
|
|
202
|
-
view,
|
|
203
|
-
marksFor(view.state.doc, marks.pushes, marks.spaces, continuations)
|
|
115
|
+
view.state.tr.setMeta(
|
|
116
|
+
marksKey,
|
|
117
|
+
marksFor(
|
|
118
|
+
view.state.doc,
|
|
119
|
+
blockKindsOf(view.state),
|
|
120
|
+
next.pushes,
|
|
121
|
+
next.cuts
|
|
122
|
+
)
|
|
123
|
+
).setMeta("addToHistory", false)
|
|
204
124
|
);
|
|
205
125
|
}
|
|
206
126
|
export {
|
|
127
|
+
blockKindsOf,
|
|
207
128
|
pageDecorations,
|
|
208
|
-
|
|
209
|
-
setPagePushes,
|
|
210
|
-
setTableContinuations
|
|
129
|
+
setPageMarks
|
|
211
130
|
};
|