@portone/docx-editor 0.3.0 → 0.5.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 (217) hide show
  1. package/CHANGELOG.md +205 -0
  2. package/assets/editor.png +0 -0
  3. package/dist/DocxEditor.d.ts +1 -1
  4. package/dist/DocxEditor.js +45 -25
  5. package/dist/core.d.ts +1 -1
  6. package/dist/docx/cloning.js +4 -9
  7. package/dist/docx/commentOnlyChange.d.ts +5 -3
  8. package/dist/docx/comments/grammar.d.ts +27 -10
  9. package/dist/docx/comments/grammar.js +18 -67
  10. package/dist/docx/comments/model.d.ts +5 -6
  11. package/dist/docx/comments/model.js +0 -9
  12. package/dist/docx/comments/parts.d.ts +16 -10
  13. package/dist/docx/comments/parts.js +65 -13
  14. package/dist/docx/comments/people.d.ts +11 -3
  15. package/dist/docx/comments/people.js +20 -6
  16. package/dist/docx/comments/policy.js +7 -1
  17. package/dist/docx/comments/reading.d.ts +4 -2
  18. package/dist/docx/comments/reading.js +1 -14
  19. package/dist/docx/comments/writing.js +78 -35
  20. package/dist/docx/exportDocx.js +30 -18
  21. package/dist/docx/exportRefs.d.ts +8 -3
  22. package/dist/docx/exportRefs.js +2 -1
  23. package/dist/docx/fidelity.js +24 -10
  24. package/dist/docx/fields.d.ts +42 -0
  25. package/dist/docx/fields.js +76 -0
  26. package/dist/docx/formatting/attrs.d.ts +20 -2
  27. package/dist/docx/formatting/attrs.js +57 -2
  28. package/dist/docx/formatting/context.d.ts +10 -2
  29. package/dist/docx/formatting/context.js +23 -5
  30. package/dist/docx/formatting/resolve.d.ts +10 -4
  31. package/dist/docx/formatting/resolve.js +31 -11
  32. package/dist/docx/formatting/styles.d.ts +25 -2
  33. package/dist/docx/formatting/styles.js +84 -3
  34. package/dist/docx/headersFooters.d.ts +89 -20
  35. package/dist/docx/headersFooters.js +163 -172
  36. package/dist/docx/hyperlink.d.ts +14 -4
  37. package/dist/docx/hyperlink.js +4 -1
  38. package/dist/docx/importDocx.d.ts +1 -1
  39. package/dist/docx/importDocx.js +135 -74
  40. package/dist/docx/importParagraph.d.ts +16 -4
  41. package/dist/docx/importParagraph.js +87 -93
  42. package/dist/docx/importPolicy.d.ts +70 -0
  43. package/dist/docx/importPolicy.js +190 -0
  44. package/dist/docx/importPreserved.d.ts +30 -0
  45. package/dist/docx/importPreserved.js +54 -0
  46. package/dist/docx/importTable.d.ts +2 -2
  47. package/dist/docx/importTable.js +107 -66
  48. package/dist/docx/invariants.js +42 -31
  49. package/dist/docx/newLists.d.ts +30 -0
  50. package/dist/docx/newLists.js +24 -1
  51. package/dist/docx/notes.d.ts +6 -1
  52. package/dist/docx/notes.js +6 -16
  53. package/dist/docx/numberingPlanner.d.ts +6 -3
  54. package/dist/docx/numberingPlanner.js +38 -7
  55. package/dist/docx/pageGeometry.d.ts +5 -14
  56. package/dist/docx/pageGeometry.js +0 -12
  57. package/dist/docx/protectionPolicy.d.ts +13 -5
  58. package/dist/docx/protectionPolicy.js +32 -22
  59. package/dist/docx/scan.d.ts +18 -8
  60. package/dist/docx/scan.js +17 -11
  61. package/dist/docx/sections.d.ts +126 -0
  62. package/dist/docx/sections.js +207 -0
  63. package/dist/docx/serializeBlock.d.ts +13 -4
  64. package/dist/docx/serializeBlock.js +16 -18
  65. package/dist/docx/serializeParagraph.d.ts +4 -0
  66. package/dist/docx/serializeParagraph.js +1 -0
  67. package/dist/docx/serializePreserved.d.ts +14 -0
  68. package/dist/docx/serializePreserved.js +24 -0
  69. package/dist/docx/serializeStory.d.ts +17 -0
  70. package/dist/docx/serializeStory.js +17 -0
  71. package/dist/docx/serializeTable.js +10 -13
  72. package/dist/docx/session.d.ts +24 -7
  73. package/dist/docx/session.js +37 -6
  74. package/dist/docx/story.d.ts +140 -0
  75. package/dist/docx/story.js +237 -0
  76. package/dist/docx/storyProjection.js +6 -1
  77. package/dist/docx/tableFormatting/conditions.d.ts +76 -0
  78. package/dist/docx/tableFormatting/conditions.js +196 -0
  79. package/dist/docx/tableFormatting/editing.d.ts +7 -3
  80. package/dist/docx/tableFormatting/editing.js +4 -5
  81. package/dist/docx/tableFormatting/reading.d.ts +49 -19
  82. package/dist/docx/tableFormatting/reading.js +109 -35
  83. package/dist/docx/tableFormatting.d.ts +1 -0
  84. package/dist/docx/tableFormatting.js +1 -0
  85. package/dist/docx/tableTemplate.js +12 -12
  86. package/dist/editor/clipboard/htmlReader.d.ts +30 -0
  87. package/dist/editor/clipboard/htmlReader.js +302 -0
  88. package/dist/editor/clipboard/internalChannel.d.ts +57 -0
  89. package/dist/editor/clipboard/internalChannel.js +58 -0
  90. package/dist/editor/clipboard/normalizers.d.ts +99 -0
  91. package/dist/editor/clipboard/normalizers.js +199 -0
  92. package/dist/editor/clipboard/parser.d.ts +34 -0
  93. package/dist/editor/clipboard/parser.js +58 -0
  94. package/dist/editor/clipboard/plugin.d.ts +21 -0
  95. package/dist/editor/clipboard/plugin.js +320 -0
  96. package/dist/editor/clipboard/readContext.d.ts +24 -0
  97. package/dist/editor/clipboard/readContext.js +19 -0
  98. package/dist/editor/clipboard/readers.d.ts +33 -0
  99. package/dist/editor/clipboard/readers.js +37 -0
  100. package/dist/editor/commands/comments/editing.d.ts +26 -6
  101. package/dist/editor/commands/comments/editing.js +175 -105
  102. package/dist/editor/commands/comments/model.d.ts +34 -17
  103. package/dist/editor/commands/comments/model.js +11 -0
  104. package/dist/editor/commands/comments/reading.d.ts +5 -3
  105. package/dist/editor/commands/comments/reading.js +8 -58
  106. package/dist/editor/commands/formatting/propertyCommands.js +6 -1
  107. package/dist/editor/commands/index.d.ts +2 -2
  108. package/dist/editor/commands/index.js +2 -0
  109. package/dist/editor/commands/listCommands.d.ts +12 -3
  110. package/dist/editor/commands/listCommands.js +76 -30
  111. package/dist/editor/commands/noteQueries.d.ts +22 -5
  112. package/dist/editor/commands/noteQueries.js +34 -6
  113. package/dist/editor/commands/paragraphCommands.js +6 -1
  114. package/dist/editor/createEditor.js +14 -4
  115. package/dist/editor/documentStyles.d.ts +26 -4
  116. package/dist/editor/documentStyles.js +9 -4
  117. package/dist/editor/editorDocument.d.ts +7 -6
  118. package/dist/editor/editorDocument.js +18 -6
  119. package/dist/editor/imageFiles.d.ts +2 -2
  120. package/dist/editor/imageFiles.js +2 -0
  121. package/dist/editor/insertTable.d.ts +1 -1
  122. package/dist/editor/insertTable.js +2 -2
  123. package/dist/editor/paragraphEdits.d.ts +16 -1
  124. package/dist/editor/paragraphEdits.js +13 -5
  125. package/dist/editor/paragraphPlacement.d.ts +11 -0
  126. package/dist/editor/paragraphPlacement.js +18 -0
  127. package/dist/editor/plainText.d.ts +12 -0
  128. package/dist/editor/plainText.js +2 -1
  129. package/dist/editor/plugins/columnResize.js +6 -3
  130. package/dist/editor/plugins/commentComposer.d.ts +28 -0
  131. package/dist/editor/plugins/commentComposer.js +46 -0
  132. package/dist/editor/plugins/commentDecorations.d.ts +29 -2
  133. package/dist/editor/plugins/commentDecorations.js +89 -15
  134. package/dist/editor/plugins/documentProjection.d.ts +31 -0
  135. package/dist/editor/plugins/documentProjection.js +22 -0
  136. package/dist/editor/plugins/imagePaste.js +20 -10
  137. package/dist/editor/plugins/linkPanel.d.ts +7 -3
  138. package/dist/editor/plugins/linkPanel.js +13 -27
  139. package/dist/editor/plugins/numberingDecorations.d.ts +10 -4
  140. package/dist/editor/plugins/numberingDecorations.js +43 -2
  141. package/dist/editor/plugins/panelState.d.ts +67 -0
  142. package/dist/editor/plugins/panelState.js +54 -0
  143. package/dist/editor/plugins/paragraphDisplay.js +9 -3
  144. package/dist/editor/plugins/tableContextMenu.d.ts +1 -1
  145. package/dist/editor/plugins/tableContextMenu.js +30 -47
  146. package/dist/editor/plugins/tableDisplay.js +19 -5
  147. package/dist/editor/plugins/textContextMenu.d.ts +1 -1
  148. package/dist/editor/plugins/textContextMenu.js +30 -47
  149. package/dist/model/format.d.ts +50 -0
  150. package/dist/model/format.js +65 -0
  151. package/dist/numbering/listRegistry.d.ts +37 -0
  152. package/dist/numbering/listRegistry.js +112 -0
  153. package/dist/numbering/listTemplate.d.ts +26 -17
  154. package/dist/numbering/listTemplate.js +66 -20
  155. package/dist/numbering/markers.d.ts +15 -6
  156. package/dist/numbering/markers.js +26 -59
  157. package/dist/numbering/parseNumbering.d.ts +70 -5
  158. package/dist/numbering/parseNumbering.js +100 -26
  159. package/dist/numbering/spellers.d.ts +15 -0
  160. package/dist/numbering/spellers.js +96 -0
  161. package/dist/numbering/writeNumbering.d.ts +5 -3
  162. package/dist/numbering/writeNumbering.js +15 -15
  163. package/dist/ooxml/conformance.d.ts +28 -0
  164. package/dist/ooxml/conformance.js +20 -0
  165. package/dist/ooxml/errors.d.ts +5 -5
  166. package/dist/ooxml/fragment.d.ts +11 -0
  167. package/dist/ooxml/fragment.js +14 -4
  168. package/dist/ooxml/image.d.ts +3 -4
  169. package/dist/ooxml/image.js +50 -5
  170. package/dist/ooxml/names.d.ts +15 -0
  171. package/dist/ooxml/names.js +10 -1
  172. package/dist/ooxml/partSplice.d.ts +1 -1
  173. package/dist/ooxml/partSplice.js +10 -3
  174. package/dist/ooxml/props.d.ts +8 -0
  175. package/dist/ooxml/props.js +14 -4
  176. package/dist/ooxml/rangeMarkers.d.ts +5 -0
  177. package/dist/ooxml/rangeMarkers.js +24 -0
  178. package/dist/ooxml/xml.d.ts +8 -9
  179. package/dist/ooxml/xml.js +6 -6
  180. package/dist/page/PageGuides.d.ts +8 -4
  181. package/dist/page/PageGuides.js +13 -37
  182. package/dist/page/pageLayout.d.ts +62 -13
  183. package/dist/page/pageLayout.js +91 -37
  184. package/dist/page/usePageLayout.d.ts +14 -11
  185. package/dist/page/usePageLayout.js +21 -29
  186. package/dist/schema/attrRoles.js +44 -19
  187. package/dist/schema/docxSchema.d.ts +9 -1
  188. package/dist/schema/docxSchema.js +220 -139
  189. package/dist/schema/editGuard.d.ts +1 -1
  190. package/dist/schema/guards.js +2 -2
  191. package/dist/schema/index.d.ts +1 -0
  192. package/dist/schema/preservedFragments.d.ts +12 -0
  193. package/dist/schema/preservedFragments.js +38 -0
  194. package/dist/schema/preservedGuards.d.ts +9 -7
  195. package/dist/schema/preservedGuards.js +112 -11
  196. package/dist/schema/protection.d.ts +34 -5
  197. package/dist/schema/protection.js +42 -14
  198. package/dist/schema/rendering.js +3 -1
  199. package/dist/schema/stories.d.ts +49 -0
  200. package/dist/schema/stories.js +78 -0
  201. package/dist/styles/classNames.d.ts +16 -7
  202. package/dist/styles/classNames.js +16 -7
  203. package/dist/styles.css +90 -30
  204. package/dist/table/cellFormatting.js +1 -2
  205. package/dist/table/gridBorders.d.ts +11 -15
  206. package/dist/table/gridBorders.js +32 -18
  207. package/dist/table/resize.d.ts +5 -3
  208. package/dist/ui/CommentsPanel.d.ts +1 -3
  209. package/dist/ui/CommentsPanel.js +24 -15
  210. package/dist/ui/NotesPanel.js +2 -2
  211. package/dist/ui/TextMenu.d.ts +1 -3
  212. package/dist/ui/TextMenu.js +10 -6
  213. package/dist/ui/comments/CommentComposer.d.ts +6 -1
  214. package/dist/ui/comments/CommentComposer.js +26 -3
  215. package/package.json +1 -1
  216. package/dist/editor/externalClipboard.d.ts +0 -11
  217. package/dist/editor/externalClipboard.js +0 -456
@@ -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 { toParagraphFormat } from "../../model/format.js";
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 = [`${editorCssVariables.markerWidth}:${marker.widthPt}pt`];
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
  }
@@ -0,0 +1,67 @@
1
+ /**
2
+ * One convention for every panel the editor opens over a spot in the document: a link panel, a
3
+ * right click menu, the comment composer.
4
+ *
5
+ * Each of them holds the same thing - where it stands, or nothing when it is shut - and answers the
6
+ * same three questions: open it here, close it, where does it stand. What differs between them is
7
+ * what "here" is and what an edit or a change of protection does to a panel already open, and those
8
+ * are the options below rather than four copies of a plugin.
9
+ *
10
+ * The anchor is whatever the panel is about: a point on the screen for a menu, a stretch of the
11
+ * document for the composer. It is read back out of the state as it was put in, so a panel that
12
+ * needs nothing but "open" carries an anchor that says just that.
13
+ *
14
+ * A panel that reads other plugins in `canOpen` or `closeWhen` must be registered after them:
15
+ * the state a plugin is handed while the transaction is being applied holds the fields of the
16
+ * plugins ahead of it and nothing of the ones behind it.
17
+ */
18
+ import { type Command, type EditorState, Plugin, type PluginSpec, type Transaction } from "prosemirror-state";
19
+ /**
20
+ * What a document change does to an open panel: shut it, or move the anchor along with the change.
21
+ * Answering null from the mapping shuts it, which is what a stretch the edit swallowed comes to.
22
+ */
23
+ export type PanelDocChange<Anchor> = "close" | ((anchor: Anchor, tr: Transaction, state: EditorState) => Anchor | null);
24
+ export interface PanelOptions<Anchor> {
25
+ /** The plugin key's name, which is what a state debugger and a duplicate registration report */
26
+ name: string;
27
+ /**
28
+ * Whether this is an anchor of this panel's. The opening goes through the transaction metadata,
29
+ * which anything may write, so what is read back is judged rather than trusted
30
+ */
31
+ isAnchor(value: unknown): value is Anchor;
32
+ onDocChange: PanelDocChange<Anchor>;
33
+ /** Whether the panel may open here. `open` reports false where it says no */
34
+ canOpen?(state: EditorState, anchor: Anchor): boolean;
35
+ /**
36
+ * Whether the panel must shut where the state now stands, asked after every transaction rather
37
+ * than after an edit alone: a protection switch changes no text (`./documentProtection`), so
38
+ * `onDocChange` never sees it, and a panel that only a commenter is offered has to go with it.
39
+ */
40
+ closeWhen?(state: EditorState, anchor: Anchor): boolean;
41
+ /**
42
+ * The view props the panel needs of its own, which is how a panel opened by a DOM event - a
43
+ * right click - stays one plugin. What that handler does with the event is the panel's business;
44
+ * all this factory asks is that the opening go through `opening` so the state stays this one.
45
+ */
46
+ props?: PluginSpec<Anchor | null>["props"];
47
+ }
48
+ export interface PanelPlugin<Anchor> {
49
+ plugin: Plugin<Anchor | null>;
50
+ /**
51
+ * Opens the panel over this anchor, or moves an open one there. False where `canOpen` says the
52
+ * panel has nothing to do here, so that a control drawn from it is drawn dead and a key bound to
53
+ * it is a key this editor did not take.
54
+ */
55
+ open(anchor: Anchor): Command;
56
+ /**
57
+ * The same opening recorded on a transaction already being built, for an opener that moves the
58
+ * selection in the same step. `canOpen` is not asked here: an opener holding the event that
59
+ * asked - a right click landing where the menu belongs - has decided that already.
60
+ */
61
+ opening(tr: Transaction, anchor: Anchor): Transaction;
62
+ /** Closes it. False where it stands closed already, so a key press falls through to what is behind it */
63
+ close: Command;
64
+ /** Where the panel stands. Null when it is shut, and for a state built without the plugin */
65
+ anchor(state: EditorState): Anchor | null;
66
+ }
67
+ export declare function panelPlugin<Anchor>({ name, isAnchor, onDocChange, canOpen, closeWhen, props, }: PanelOptions<Anchor>): PanelPlugin<Anchor>;
@@ -0,0 +1,54 @@
1
+ // src/editor/plugins/panelState.ts
2
+ import {
3
+ Plugin,
4
+ PluginKey
5
+ } from "prosemirror-state";
6
+ var CLOSE = /* @__PURE__ */ Symbol("panelClose");
7
+ function panelPlugin({
8
+ name,
9
+ isAnchor,
10
+ onDocChange,
11
+ canOpen,
12
+ closeWhen,
13
+ props
14
+ }) {
15
+ const key = new PluginKey(name);
16
+ const anchor = (state) => key.getState(state) ?? null;
17
+ const moved = (tr, held, next) => {
18
+ const meta = tr.getMeta(key);
19
+ if (meta === CLOSE) return null;
20
+ if (isAnchor(meta)) return meta;
21
+ if (held === null || !tr.docChanged) return held;
22
+ return onDocChange === "close" ? null : onDocChange(held, tr, next);
23
+ };
24
+ const opening = (tr, over) => tr.setMeta(key, over);
25
+ return {
26
+ plugin: new Plugin({
27
+ key,
28
+ state: {
29
+ init: () => null,
30
+ apply: (tr, held, _before, next) => {
31
+ const standing = moved(tr, held, next);
32
+ if (standing === null) return null;
33
+ return closeWhen?.(next, standing) ? null : standing;
34
+ }
35
+ },
36
+ props
37
+ }),
38
+ open: (over) => (state, dispatch) => {
39
+ if (canOpen && !canOpen(state, over)) return false;
40
+ dispatch?.(opening(state.tr, over));
41
+ return true;
42
+ },
43
+ opening,
44
+ close: (state, dispatch) => {
45
+ if (anchor(state) === null) return false;
46
+ dispatch?.(state.tr.setMeta(key, CLOSE));
47
+ return true;
48
+ },
49
+ anchor
50
+ };
51
+ }
52
+ export {
53
+ panelPlugin
54
+ };
@@ -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, _doc, context, previous) {
22
- if (stands(node, previous) || holdsComposition(node, pos, context.composingAt)) {
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(paragraphPPr(node), formatting);
28
+ const paragraph = resolveParagraph(
29
+ paragraphPPr(node),
30
+ formatting,
31
+ placement
32
+ );
27
33
  const derived = [
28
34
  { pos, attrs: paragraphAttrsOf(paragraph) }
29
35
  ];
@@ -8,7 +8,7 @@
8
8
  * selected text is taken first by the text menu (`editor/textContextMenu`), which stands ahead
9
9
  * of this one and hands back a click with nothing selected inside a cell.
10
10
  */
11
- import { type Command, type EditorState, Plugin } from "prosemirror-state";
11
+ import { type Command, type EditorState, type Plugin } from "prosemirror-state";
12
12
  /** Where the menu should stand (viewport coordinates) */
13
13
  export interface TableMenuAnchor {
14
14
  clientX: number;
@@ -1,29 +1,14 @@
1
1
  // src/editor/plugins/tableContextMenu.ts
2
2
  import {
3
- Plugin,
4
- PluginKey,
5
3
  TextSelection
6
4
  } from "prosemirror-state";
7
5
  import { editsShut } from "../../schema/protectionState.js";
8
- var CLOSE = "close";
9
- var menuKey = new PluginKey("docxEditorTableMenu");
10
- function isRecord(value) {
11
- return typeof value === "object" && value !== null;
12
- }
13
- function toAnchor(value) {
14
- if (!isRecord(value)) return null;
6
+ import { panelPlugin } from "./panelState.js";
7
+ function isTableMenuAnchor(value) {
8
+ if (typeof value !== "object" || value === null) return false;
15
9
  const { clientX, clientY } = value;
16
- if (typeof clientX !== "number" || typeof clientY !== "number") return null;
17
- return { clientX, clientY };
18
- }
19
- function tableMenuAnchor(state) {
20
- return menuKey.getState(state) ?? null;
10
+ return typeof clientX === "number" && typeof clientY === "number";
21
11
  }
22
- var closeTableMenu = (state, dispatch) => {
23
- if (tableMenuAnchor(state) === null) return false;
24
- if (dispatch) dispatch(state.tr.setMeta(menuKey, CLOSE));
25
- return true;
26
- };
27
12
  function cellStart($pos) {
28
13
  for (let depth = $pos.depth; depth > 0; depth -= 1) {
29
14
  if ($pos.node(depth).type.spec.tableRole === "cell") {
@@ -46,41 +31,39 @@ function selectionCovers(state, cellPos) {
46
31
  }
47
32
  function openMenu(view, cellPos, anchor) {
48
33
  const state = view.state;
49
- const tr = state.tr.setMeta(menuKey, anchor);
34
+ const tr = menu.opening(state.tr, anchor);
50
35
  if (!selectionCovers(state, cellPos)) {
51
36
  tr.setSelection(TextSelection.near(tr.doc.resolve(cellPos + 1)));
52
37
  }
53
38
  view.dispatch(tr);
54
39
  }
55
- function tableContextMenu() {
56
- return new Plugin({
57
- key: menuKey,
58
- state: {
59
- init: () => null,
60
- apply: (tr, current) => {
61
- const meta = tr.getMeta(menuKey);
62
- if (meta === CLOSE) return null;
63
- const anchor = toAnchor(meta);
64
- if (anchor) return anchor;
65
- return tr.docChanged ? null : current;
66
- }
67
- },
68
- props: {
69
- handleDOMEvents: {
70
- contextmenu(view, event) {
71
- if (editsShut(view.state)) return false;
72
- const cellPos = cellAtEvent(view, event.target);
73
- if (cellPos === null) return false;
74
- event.preventDefault();
75
- openMenu(view, cellPos, {
76
- clientX: event.clientX,
77
- clientY: event.clientY
78
- });
79
- return true;
80
- }
40
+ var menu = panelPlugin({
41
+ name: "docxEditorTableMenu",
42
+ isAnchor: isTableMenuAnchor,
43
+ // Once the document changes, the cell the menu pointed at may no longer be there
44
+ onDocChange: "close",
45
+ props: {
46
+ handleDOMEvents: {
47
+ contextmenu(view, event) {
48
+ if (editsShut(view.state)) return false;
49
+ const cellPos = cellAtEvent(view, event.target);
50
+ if (cellPos === null) return false;
51
+ event.preventDefault();
52
+ openMenu(view, cellPos, {
53
+ clientX: event.clientX,
54
+ clientY: event.clientY
55
+ });
56
+ return true;
81
57
  }
82
58
  }
83
- });
59
+ }
60
+ });
61
+ function tableMenuAnchor(state) {
62
+ return menu.anchor(state);
63
+ }
64
+ var closeTableMenu = menu.close;
65
+ function tableContextMenu() {
66
+ return menu.plugin;
84
67
  }
85
68
  export {
86
69
  closeTableMenu,
@@ -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, _context, previous) {
8
+ derive(table, pos, _doc, context, previous) {
7
9
  if (previous !== null && sameFormattingInputs(previous, table)) return [];
8
- return cellFixes(table).map((fix) => ({
9
- pos: pos + 1 + fix.pos,
10
- attrs: fix.attrs
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 {
@@ -2,7 +2,7 @@
2
2
  * Replaces the native text context menu and moves the selection to the clicked position. Unselected
3
3
  * table cells are left to the table context-menu plugin.
4
4
  */
5
- import { type Command, type EditorState, Plugin } from "prosemirror-state";
5
+ import { type Command, type EditorState, type Plugin } from "prosemirror-state";
6
6
  /** Where the menu should stand (viewport coordinates) */
7
7
  export interface TextMenuAnchor {
8
8
  clientX: number;
@@ -1,30 +1,15 @@
1
1
  // src/editor/plugins/textContextMenu.ts
2
2
  import {
3
- Plugin,
4
- PluginKey,
5
3
  TextSelection
6
4
  } from "prosemirror-state";
7
5
  import { CellSelection } from "prosemirror-tables";
8
6
  import { editingProtection, editsShut } from "../../schema/protectionState.js";
9
- var CLOSE = "close";
10
- var menuKey = new PluginKey("docxEditorTextMenu");
11
- function isRecord(value) {
12
- return typeof value === "object" && value !== null;
13
- }
14
- function toAnchor(value) {
15
- if (!isRecord(value)) return null;
7
+ import { panelPlugin } from "./panelState.js";
8
+ function isTextMenuAnchor(value) {
9
+ if (typeof value !== "object" || value === null) return false;
16
10
  const { clientX, clientY } = value;
17
- if (typeof clientX !== "number" || typeof clientY !== "number") return null;
18
- return { clientX, clientY };
19
- }
20
- function textMenuAnchor(state) {
21
- return menuKey.getState(state) ?? null;
11
+ return typeof clientX === "number" && typeof clientY === "number";
22
12
  }
23
- var closeTextMenu = (state, dispatch) => {
24
- if (textMenuAnchor(state) === null) return false;
25
- if (dispatch) dispatch(state.tr.setMeta(menuKey, CLOSE));
26
- return true;
27
- };
28
13
  function isInEditor(view, target) {
29
14
  return target instanceof Node && view.dom.contains(target);
30
15
  }
@@ -50,43 +35,41 @@ function forTableMenu(view, target, spot) {
50
35
  return aboutCells && isInTableCell(view, target);
51
36
  }
52
37
  function openMenu(state, event, spot) {
53
- const tr = state.tr.setMeta(menuKey, {
38
+ const tr = menu.opening(state.tr, {
54
39
  clientX: event.clientX,
55
40
  clientY: event.clientY
56
41
  });
57
42
  if (isInSelection(state, spot)) return tr;
58
43
  return tr.setSelection(TextSelection.near(tr.doc.resolve(spot)));
59
44
  }
60
- function textContextMenu() {
61
- return new Plugin({
62
- key: menuKey,
63
- state: {
64
- init: () => null,
65
- apply: (tr, current) => {
66
- const meta = tr.getMeta(menuKey);
67
- if (meta === CLOSE) return null;
68
- const anchor = toAnchor(meta);
69
- if (anchor) return anchor;
70
- return tr.docChanged ? null : current;
71
- }
72
- },
73
- props: {
74
- handleDOMEvents: {
75
- contextmenu(view, event) {
76
- if (editingProtection(view.state) === "readOnly") return false;
77
- if (!isInEditor(view, event.target)) return false;
78
- const spot = clickedSpot(view, event);
79
- if (editsShut(view.state) && !isInSelection(view.state, spot)) {
80
- return false;
81
- }
82
- if (forTableMenu(view, event.target, spot)) return false;
83
- event.preventDefault();
84
- view.dispatch(openMenu(view.state, event, spot));
85
- return true;
45
+ var menu = panelPlugin({
46
+ name: "docxEditorTextMenu",
47
+ isAnchor: isTextMenuAnchor,
48
+ // Once the document changes, what the menu was aimed at may no longer be there
49
+ onDocChange: "close",
50
+ props: {
51
+ handleDOMEvents: {
52
+ contextmenu(view, event) {
53
+ if (editingProtection(view.state) === "readOnly") return false;
54
+ if (!isInEditor(view, event.target)) return false;
55
+ const spot = clickedSpot(view, event);
56
+ if (editsShut(view.state) && !isInSelection(view.state, spot)) {
57
+ return false;
86
58
  }
59
+ if (forTableMenu(view, event.target, spot)) return false;
60
+ event.preventDefault();
61
+ view.dispatch(openMenu(view.state, event, spot));
62
+ return true;
87
63
  }
88
64
  }
89
- });
65
+ }
66
+ });
67
+ function textMenuAnchor(state) {
68
+ return menu.anchor(state);
69
+ }
70
+ var closeTextMenu = menu.close;
71
+ function textContextMenu() {
72
+ return menu.plugin;
90
73
  }
91
74
  export {
92
75
  closeTextMenu,
@@ -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
  /**
@@ -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;