@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.
Files changed (256) hide show
  1. package/CHANGELOG.md +174 -0
  2. package/CONTRIBUTING.md +9 -1
  3. package/dist/DocxEditor.d.ts +6 -0
  4. package/dist/DocxEditor.js +12 -29
  5. package/dist/core.d.ts +23 -8
  6. package/dist/core.js +9 -4
  7. package/dist/docx/cloning.d.ts +38 -0
  8. package/dist/docx/cloning.js +74 -0
  9. package/dist/docx/commentOnlyChange.d.ts +14 -7
  10. package/dist/docx/commentOnlyChange.js +9 -139
  11. package/dist/docx/comments/constants.d.ts +3 -4
  12. package/dist/docx/comments/constants.js +4 -5
  13. package/dist/docx/comments/grammar.d.ts +86 -0
  14. package/dist/docx/comments/grammar.js +223 -0
  15. package/dist/docx/comments/parts.d.ts +52 -0
  16. package/dist/docx/comments/parts.js +279 -0
  17. package/dist/docx/comments/people.d.ts +11 -2
  18. package/dist/docx/comments/people.js +28 -106
  19. package/dist/docx/comments/policy.d.ts +7 -0
  20. package/dist/docx/comments/policy.js +43 -0
  21. package/dist/docx/comments/reading.d.ts +5 -3
  22. package/dist/docx/comments/reading.js +30 -29
  23. package/dist/docx/comments/writing.d.ts +19 -7
  24. package/dist/docx/comments/writing.js +105 -137
  25. package/dist/docx/documentSettings.d.ts +7 -0
  26. package/dist/docx/documentSettings.js +10 -2
  27. package/dist/docx/exportDocx.d.ts +40 -5
  28. package/dist/docx/exportDocx.js +71 -72
  29. package/dist/docx/exportRefs.d.ts +5 -2
  30. package/dist/docx/exportRefs.js +3 -1
  31. package/dist/docx/fidelity.d.ts +43 -0
  32. package/dist/docx/fidelity.js +64 -0
  33. package/dist/docx/formatting/attrs.d.ts +27 -0
  34. package/dist/docx/formatting/attrs.js +31 -0
  35. package/dist/docx/formatting/context.d.ts +31 -0
  36. package/dist/docx/formatting/context.js +42 -0
  37. package/dist/docx/formatting/direct.d.ts +6 -5
  38. package/dist/docx/formatting/direct.js +52 -81
  39. package/dist/docx/formatting/resolve.d.ts +45 -0
  40. package/dist/docx/formatting/resolve.js +140 -0
  41. package/dist/docx/formatting/runProperties.d.ts +93 -0
  42. package/dist/docx/formatting/runProperties.js +316 -0
  43. package/dist/docx/formatting/styles.js +3 -3
  44. package/dist/docx/formatting/tabStops.js +7 -22
  45. package/dist/docx/formatting.d.ts +4 -1
  46. package/dist/docx/formatting.js +4 -1
  47. package/dist/docx/headersFooters.js +13 -22
  48. package/dist/docx/identities.d.ts +67 -0
  49. package/dist/docx/identities.js +174 -0
  50. package/dist/docx/importDocx.d.ts +12 -1
  51. package/dist/docx/importDocx.js +72 -79
  52. package/dist/docx/importParagraph.d.ts +1 -1
  53. package/dist/docx/importParagraph.js +4 -3
  54. package/dist/docx/importTable.d.ts +1 -1
  55. package/dist/docx/importTable.js +17 -1
  56. package/dist/docx/invariants.d.ts +33 -0
  57. package/dist/docx/invariants.js +256 -0
  58. package/dist/docx/media.d.ts +6 -4
  59. package/dist/docx/media.js +6 -37
  60. package/dist/docx/newLists.d.ts +20 -0
  61. package/dist/docx/newLists.js +36 -0
  62. package/dist/docx/notes.js +6 -12
  63. package/dist/docx/numberingPlanner.d.ts +8 -0
  64. package/dist/docx/numberingPlanner.js +19 -0
  65. package/dist/docx/packageParts.d.ts +42 -0
  66. package/dist/docx/packageParts.js +135 -0
  67. package/dist/docx/pageGeometry.d.ts +2 -0
  68. package/dist/docx/pageGeometry.js +18 -9
  69. package/dist/docx/paraProps.d.ts +10 -22
  70. package/dist/docx/paraProps.js +75 -99
  71. package/dist/docx/partPlan.d.ts +36 -0
  72. package/dist/docx/partPlan.js +59 -0
  73. package/dist/docx/protectionPolicy.d.ts +127 -0
  74. package/dist/docx/protectionPolicy.js +169 -0
  75. package/dist/docx/relationships.d.ts +1 -1
  76. package/dist/docx/relationships.js +15 -15
  77. package/dist/docx/runProps.d.ts +9 -22
  78. package/dist/docx/runProps.js +15 -168
  79. package/dist/docx/scan.js +20 -52
  80. package/dist/docx/sdt.js +14 -37
  81. package/dist/docx/sdtProps.d.ts +8 -1
  82. package/dist/docx/sdtProps.js +10 -0
  83. package/dist/docx/serializeBlock.d.ts +2 -0
  84. package/dist/docx/serializeBlock.js +9 -7
  85. package/dist/docx/serializeParagraph.d.ts +2 -1
  86. package/dist/docx/serializeParagraph.js +25 -14
  87. package/dist/docx/serializeTable.js +63 -32
  88. package/dist/docx/session.d.ts +42 -14
  89. package/dist/docx/session.js +43 -17
  90. package/dist/docx/storyProjection.d.ts +36 -0
  91. package/dist/docx/storyProjection.js +23 -0
  92. package/dist/docx/tableFormatting/editing.js +130 -185
  93. package/dist/docx/tableFormatting/reading.d.ts +16 -6
  94. package/dist/docx/tableFormatting/reading.js +40 -20
  95. package/dist/docx/tableTemplate.js +37 -5
  96. package/dist/docx/theme.d.ts +0 -6
  97. package/dist/docx/theme.js +0 -8
  98. package/dist/download.d.ts +8 -5
  99. package/dist/download.js +2 -0
  100. package/dist/editor/clipboard/images.js +17 -3
  101. package/dist/editor/clipboard/inlineFormatting.d.ts +7 -0
  102. package/dist/editor/clipboard/inlineFormatting.js +20 -30
  103. package/dist/editor/commands/breakCommands.js +4 -3
  104. package/dist/editor/commands/canRunCommand.d.ts +2 -2
  105. package/dist/editor/commands/canRunCommand.js +1 -1
  106. package/dist/editor/commands/comments/editing.d.ts +6 -2
  107. package/dist/editor/commands/comments/editing.js +21 -36
  108. package/dist/editor/commands/exportQueries.d.ts +15 -0
  109. package/dist/editor/commands/exportQueries.js +14 -0
  110. package/dist/editor/commands/fidelityQueries.d.ts +12 -0
  111. package/dist/editor/commands/fidelityQueries.js +8 -0
  112. package/dist/editor/commands/formatting/editing.d.ts +2 -2
  113. package/dist/editor/commands/formatting/editing.js +26 -108
  114. package/dist/editor/commands/formatting/propertyCommands.d.ts +21 -0
  115. package/dist/editor/commands/formatting/propertyCommands.js +103 -0
  116. package/dist/editor/commands/formatting/shared.d.ts +3 -3
  117. package/dist/editor/commands/formatting/shared.js +5 -2
  118. package/dist/editor/commands/historyCommands.js +2 -1
  119. package/dist/editor/commands/indentCommands.js +5 -4
  120. package/dist/editor/commands/index.d.ts +11 -0
  121. package/dist/editor/commands/index.js +5 -0
  122. package/dist/editor/commands/linkCommands.js +5 -6
  123. package/dist/editor/commands/listCommands.js +8 -10
  124. package/dist/editor/commands/lockCommands.d.ts +7 -1
  125. package/dist/editor/commands/lockCommands.js +35 -37
  126. package/dist/editor/commands/paragraphCommands.js +28 -31
  127. package/dist/editor/commands/spacingCommands.js +1 -1
  128. package/dist/editor/commands/tabCommands.js +4 -3
  129. package/dist/editor/createEditor.d.ts +17 -29
  130. package/dist/editor/createEditor.js +61 -67
  131. package/dist/editor/documentStyles.d.ts +11 -35
  132. package/dist/editor/documentStyles.js +9 -52
  133. package/dist/editor/editorDocument.d.ts +56 -0
  134. package/dist/editor/editorDocument.js +74 -0
  135. package/dist/editor/externalClipboard.js +166 -33
  136. package/dist/editor/insertImage.js +4 -3
  137. package/dist/editor/insertTable.js +4 -3
  138. package/dist/editor/paragraphEdits.d.ts +11 -18
  139. package/dist/editor/paragraphEdits.js +7 -16
  140. package/dist/editor/plugins/displayDerivation.d.ts +47 -0
  141. package/dist/editor/plugins/displayDerivation.js +78 -0
  142. package/dist/editor/plugins/documentProtection.d.ts +3 -3
  143. package/dist/editor/plugins/imagePaste.js +4 -3
  144. package/dist/editor/plugins/keymap.js +18 -3
  145. package/dist/editor/plugins/lockedContent.d.ts +4 -4
  146. package/dist/editor/plugins/lockedContent.js +1 -1
  147. package/dist/editor/plugins/numberingDecorations.d.ts +5 -11
  148. package/dist/editor/plugins/numberingDecorations.js +7 -17
  149. package/dist/editor/plugins/paragraphDisplay.d.ts +7 -0
  150. package/dist/editor/plugins/paragraphDisplay.js +51 -0
  151. package/dist/editor/plugins/tabLayout.js +1 -1
  152. package/dist/editor/plugins/tableDisplay.d.ts +6 -0
  153. package/dist/editor/plugins/tableDisplay.js +16 -0
  154. package/dist/index.d.ts +2 -0
  155. package/dist/model/format.d.ts +30 -8
  156. package/dist/model/format.js +35 -22
  157. package/dist/model/tabStops.d.ts +9 -0
  158. package/dist/model/tabStops.js +18 -0
  159. package/dist/numbering/listTemplate.js +49 -10
  160. package/dist/numbering/parseNumbering.d.ts +14 -1
  161. package/dist/numbering/parseNumbering.js +35 -22
  162. package/dist/numbering/writeNumbering.d.ts +3 -4
  163. package/dist/numbering/writeNumbering.js +14 -25
  164. package/dist/ooxml/childOrder.d.ts +34 -0
  165. package/dist/ooxml/childOrder.js +496 -0
  166. package/dist/ooxml/element.d.ts +65 -0
  167. package/dist/ooxml/element.js +68 -0
  168. package/dist/ooxml/errors.d.ts +3 -2
  169. package/dist/ooxml/fragment.d.ts +53 -0
  170. package/dist/ooxml/fragment.js +76 -0
  171. package/dist/ooxml/image.d.ts +4 -0
  172. package/dist/ooxml/image.js +3 -5
  173. package/dist/ooxml/names.d.ts +27 -0
  174. package/dist/ooxml/names.js +29 -0
  175. package/dist/ooxml/partSplice.d.ts +67 -0
  176. package/dist/ooxml/partSplice.js +169 -0
  177. package/dist/ooxml/precedence.d.ts +31 -0
  178. package/dist/ooxml/precedence.js +51 -0
  179. package/dist/ooxml/props.d.ts +112 -0
  180. package/dist/ooxml/props.js +214 -0
  181. package/dist/ooxml/simpleTypes.d.ts +103 -0
  182. package/dist/ooxml/simpleTypes.js +182 -0
  183. package/dist/ooxml/tabStops.js +8 -27
  184. package/dist/ooxml/tagScan.d.ts +34 -0
  185. package/dist/ooxml/tagScan.js +108 -0
  186. package/dist/ooxml/units.d.ts +25 -13
  187. package/dist/ooxml/units.js +65 -31
  188. package/dist/ooxml/xml.d.ts +52 -0
  189. package/dist/ooxml/xml.js +64 -5
  190. package/dist/page/blockKinds.d.ts +103 -0
  191. package/dist/page/blockKinds.js +11 -0
  192. package/dist/page/kinds/index.d.ts +6 -0
  193. package/dist/page/kinds/index.js +10 -0
  194. package/dist/page/kinds/paragraphKind.d.ts +10 -0
  195. package/dist/page/kinds/paragraphKind.js +71 -0
  196. package/dist/page/kinds/tableKind.d.ts +10 -0
  197. package/dist/page/kinds/tableKind.js +178 -0
  198. package/dist/page/measureBlocks.d.ts +3 -1
  199. package/dist/page/measureBlocks.js +24 -25
  200. package/dist/page/pageDecorations.d.ts +27 -28
  201. package/dist/page/pageDecorations.js +70 -151
  202. package/dist/page/pageLayout.d.ts +10 -62
  203. package/dist/page/pageLayout.js +60 -53
  204. package/dist/page/usePageLayout.d.ts +2 -2
  205. package/dist/page/usePageLayout.js +3 -11
  206. package/dist/schema/attrRoles.d.ts +42 -0
  207. package/dist/schema/attrRoles.js +141 -0
  208. package/dist/schema/displayDerivation.d.ts +82 -0
  209. package/dist/schema/displayDerivation.js +130 -0
  210. package/dist/schema/docxSchema.d.ts +9 -0
  211. package/dist/schema/docxSchema.js +237 -114
  212. package/dist/schema/editGuard.d.ts +99 -0
  213. package/dist/schema/editGuard.js +43 -0
  214. package/dist/schema/guards.d.ts +91 -0
  215. package/dist/schema/guards.js +108 -0
  216. package/dist/schema/index.d.ts +2 -0
  217. package/dist/schema/index.js +2 -0
  218. package/dist/schema/locks.d.ts +17 -77
  219. package/dist/schema/locks.js +26 -70
  220. package/dist/schema/preservedGuards.d.ts +50 -0
  221. package/dist/schema/preservedGuards.js +80 -0
  222. package/dist/schema/protection.d.ts +17 -4
  223. package/dist/schema/protection.js +4 -2
  224. package/dist/schema/sourceEquality.d.ts +19 -0
  225. package/dist/schema/sourceEquality.js +39 -0
  226. package/dist/styles/inlineStyle.js +15 -6
  227. package/dist/table/cellFormatting.d.ts +8 -1
  228. package/dist/table/cellFormatting.js +10 -17
  229. package/dist/table/commands.d.ts +2 -2
  230. package/dist/table/commands.js +20 -18
  231. package/dist/table/format.d.ts +3 -0
  232. package/dist/table/format.js +3 -9
  233. package/dist/table/gridBorders.d.ts +14 -16
  234. package/dist/table/gridBorders.js +3 -41
  235. package/dist/table/index.d.ts +2 -2
  236. package/dist/table/merge.d.ts +4 -8
  237. package/dist/table/merge.js +5 -5
  238. package/package.json +10 -3
  239. package/dist/docx/comments/contentTypes.d.ts +0 -7
  240. package/dist/docx/comments/contentTypes.js +0 -38
  241. package/dist/docx/formatting/effectiveParagraph.d.ts +0 -15
  242. package/dist/docx/formatting/effectiveParagraph.js +0 -81
  243. package/dist/docx/propsXml.d.ts +0 -49
  244. package/dist/docx/propsXml.js +0 -302
  245. package/dist/docx/uniqueControls.d.ts +0 -14
  246. package/dist/docx/uniqueControls.js +0 -62
  247. package/dist/editor/plugins/bookmarkProtection.d.ts +0 -3
  248. package/dist/editor/plugins/bookmarkProtection.js +0 -28
  249. package/dist/editor/plugins/commentReservations.d.ts +0 -5
  250. package/dist/editor/plugins/commentReservations.js +0 -26
  251. package/dist/editor/plugins/noteProtection.d.ts +0 -3
  252. package/dist/editor/plugins/noteProtection.js +0 -25
  253. package/dist/editor/plugins/styledParagraphs.d.ts +0 -15
  254. package/dist/editor/plugins/styledParagraphs.js +0 -65
  255. package/dist/page/tableMeasurements.d.ts +0 -10
  256. 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 { MeasuredBlock } from "./pageLayout";
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 { measureTable } from "./tableMeasurements.js";
4
- var PAGE_BREAK_BR = `br[${editorAttributes.breakType}="page"]`;
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((_node, offset) => {
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
- let breakAfterPrevious = false;
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 top = sheetY(rect.top) - applied;
48
- const measuredTable = node ? measureTable(view, node, pos, dom, scale) : null;
49
- const breaks = [];
50
- const spaces = Array.from(dom.querySelectorAll(BREAK_SPACE));
51
- for (const space of spaces) {
52
- const box = space.getBoundingClientRect();
53
- breaks.push(sheetY(box.top) - applied - top);
54
- applied += box.height / scale;
55
- }
56
- applied += measuredTable?.appliedHeight ?? 0;
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: breakAfterPrevious || dom.hasAttribute(editorAttributes.pageBreakBefore),
63
- breaks,
64
- ...measuredTable ? { table: measuredTable.table } : {}
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: block pushes, page-break spaces, and table
3
- * continuation rows.
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 document model is left untouched, so no trace of them is left in
6
- * the exported XML or in the edit history.
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 type { BlockPush, BreakSpace, TableContinuation } from "./pageLayout";
11
- /**
12
- * One break's space while the document changes under it.
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
- spaces: readonly SpaceMark[];
26
- tableContinuations: readonly TableContinuation[];
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
- /** Does nothing when the same values are already applied */
31
- export declare function setPagePushes(view: EditorView, pushes: readonly BlockPush[]): void;
32
- /** Does nothing when the same values are already applied */
33
- export declare function setPageBreakSpaces(view: EditorView, spaces: readonly BreakSpace[]): void;
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 { Plugin, PluginKey } from "prosemirror-state";
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 spaceStyle(height) {
11
- return `display:block;height:${height}px`;
12
- }
13
- function isPageBreakNode(node) {
14
- return node?.type === docxSchema.nodes.hardBreak && isPageBreak(node.attrs.brAttrs);
15
- }
16
- function pageBreaksIn(block, blockPos) {
17
- const found = [];
18
- if (block.type !== docxSchema.nodes.paragraph) return found;
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 spaceMarks(doc, spaces) {
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 heights = new Map(spaces.map((space) => [space.at, space.height]));
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
- breakSpaces(node, offset, heights, decorations);
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, pushes, spaces, tableContinuations) {
49
+ function marksFor(doc, kinds, pushes, cuts) {
111
50
  return {
51
+ kinds,
112
52
  pushes,
113
- spaces,
114
- tableContinuations,
115
- decorations: decorationsFor(doc, pushes, spaces, tableContinuations)
53
+ cuts,
54
+ decorations: decorationsFor(doc, kinds, pushes, cuts)
116
55
  };
117
56
  }
118
- function samePushes(a, b) {
119
- return a.length === b.length && a.every((push, index) => {
120
- const other = b[index];
121
- return other !== void 0 && other.pos === push.pos && other.marginTop === push.marginTop;
122
- });
123
- }
124
- function sameSpaces(a, b) {
125
- return a.length === b.length && a.every((space, index) => {
126
- const other = b[index];
127
- return other !== void 0 && other.at === space.at && other.height === space.height;
128
- });
129
- }
130
- function sameTableContinuations(a, b) {
131
- return a.length === b.length && a.every((continuation, index) => {
132
- const other = b[index];
133
- return other !== void 0 && other.pos === continuation.pos && other.height === continuation.height && other.columns === continuation.columns && other.headerSignature === continuation.headerSignature && other.headerRows.length === continuation.headerRows.length && other.headerRows.every(
134
- (rowPos, rowIndex) => rowPos === continuation.headerRows[rowIndex]
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.spaces.flatMap((space) => {
154
- const at = tr.mapping.map(space.at);
155
- return isPageBreakNode(tr.doc.nodeAt(at)) ? [{ ...space, at }] : [];
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) ?? marksFor(view.state.doc, [], [], []);
110
+ return marksKey.getState(view.state) ?? { pushes: [], cuts: [] };
175
111
  }
176
- function dispatch(view, next) {
112
+ function setPageMarks(view, next) {
113
+ if (samePageMarks(current(view), next)) return;
177
114
  view.dispatch(
178
- view.state.tr.setMeta(marksKey, next).setMeta("addToHistory", false)
179
- );
180
- }
181
- function setPagePushes(view, pushes) {
182
- const marks = current(view);
183
- if (samePushes(marks.pushes, pushes)) return;
184
- dispatch(
185
- view,
186
- marksFor(view.state.doc, pushes, marks.spaces, marks.tableContinuations)
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
- setPageBreakSpaces,
209
- setPagePushes,
210
- setTableContinuations
129
+ setPageMarks
211
130
  };