@portone/docx-editor 0.4.0 → 0.5.1
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 +201 -0
- package/CONTRIBUTING.md +2 -0
- package/assets/editor.png +0 -0
- package/dist/DocxEditor.d.ts +1 -1
- package/dist/DocxEditor.js +44 -25
- package/dist/docx/cloning.js +4 -9
- package/dist/docx/commentOnlyChange.d.ts +5 -3
- package/dist/docx/comments/grammar.d.ts +27 -10
- package/dist/docx/comments/grammar.js +18 -67
- package/dist/docx/comments/model.d.ts +5 -6
- package/dist/docx/comments/model.js +0 -9
- package/dist/docx/comments/parts.d.ts +16 -10
- package/dist/docx/comments/parts.js +65 -13
- package/dist/docx/comments/people.d.ts +11 -3
- package/dist/docx/comments/people.js +20 -6
- package/dist/docx/comments/policy.js +7 -1
- package/dist/docx/comments/reading.d.ts +4 -2
- package/dist/docx/comments/reading.js +1 -14
- package/dist/docx/comments/writing.js +65 -28
- package/dist/docx/exportDocx.js +10 -14
- package/dist/docx/exportRefs.d.ts +8 -3
- package/dist/docx/exportRefs.js +2 -1
- package/dist/docx/fidelity.js +24 -10
- package/dist/docx/fields.d.ts +42 -0
- package/dist/docx/fields.js +76 -0
- package/dist/docx/formatting/styles.d.ts +0 -2
- package/dist/docx/formatting/styles.js +2 -15
- package/dist/docx/headersFooters.d.ts +89 -20
- package/dist/docx/headersFooters.js +163 -172
- package/dist/docx/identities.js +17 -26
- package/dist/docx/importDocx.d.ts +1 -1
- package/dist/docx/importDocx.js +100 -48
- package/dist/docx/importParagraph.d.ts +18 -5
- package/dist/docx/importParagraph.js +75 -128
- package/dist/docx/importPolicy.d.ts +70 -0
- package/dist/docx/importPolicy.js +195 -0
- package/dist/docx/importPreserved.d.ts +30 -0
- package/dist/docx/importPreserved.js +54 -0
- package/dist/docx/importTable.js +56 -12
- package/dist/docx/invariants.js +16 -18
- package/dist/docx/notes.d.ts +6 -1
- package/dist/docx/notes.js +6 -16
- package/dist/docx/pageGeometry.d.ts +5 -14
- package/dist/docx/pageGeometry.js +0 -12
- package/dist/docx/protectionPolicy.d.ts +13 -5
- package/dist/docx/protectionPolicy.js +32 -22
- package/dist/docx/scan.d.ts +18 -8
- package/dist/docx/scan.js +17 -11
- package/dist/docx/sections.d.ts +126 -0
- package/dist/docx/sections.js +207 -0
- package/dist/docx/serializeBlock.d.ts +13 -4
- package/dist/docx/serializeBlock.js +16 -18
- package/dist/docx/serializeParagraph.d.ts +9 -4
- package/dist/docx/serializeParagraph.js +23 -58
- package/dist/docx/serializePreserved.d.ts +14 -0
- package/dist/docx/serializePreserved.js +24 -0
- package/dist/docx/serializeStory.d.ts +17 -0
- package/dist/docx/serializeStory.js +17 -0
- package/dist/docx/serializeTable.js +10 -13
- package/dist/docx/session.d.ts +23 -6
- package/dist/docx/session.js +26 -5
- package/dist/docx/story.d.ts +140 -0
- package/dist/docx/story.js +237 -0
- package/dist/docx/storyProjection.js +6 -1
- package/dist/docx/tableTemplate.d.ts +25 -0
- package/dist/docx/tableTemplate.js +30 -16
- package/dist/docx/wrappers.d.ts +67 -0
- package/dist/docx/wrappers.js +117 -0
- package/dist/editor/clipboard/blockReaders.d.ts +74 -0
- package/dist/editor/clipboard/blockReaders.js +193 -0
- package/dist/editor/clipboard/htmlReader.d.ts +26 -0
- package/dist/editor/clipboard/htmlReader.js +376 -0
- package/dist/editor/clipboard/inlineFormatting.d.ts +0 -7
- package/dist/editor/clipboard/inlineFormatting.js +1 -7
- package/dist/editor/clipboard/internalChannel.d.ts +57 -0
- package/dist/editor/clipboard/internalChannel.js +58 -0
- package/dist/editor/clipboard/normalizers.d.ts +99 -0
- package/dist/editor/clipboard/normalizers.js +199 -0
- package/dist/editor/clipboard/parser.d.ts +34 -0
- package/dist/editor/clipboard/parser.js +58 -0
- package/dist/editor/clipboard/plugin.d.ts +21 -0
- package/dist/editor/clipboard/plugin.js +188 -0
- package/dist/editor/clipboard/readContext.d.ts +34 -0
- package/dist/editor/clipboard/readContext.js +25 -0
- package/dist/editor/clipboard/readers.d.ts +33 -0
- package/dist/editor/clipboard/readers.js +37 -0
- package/dist/editor/clipboard/source.d.ts +12 -0
- package/dist/editor/clipboard/source.js +21 -0
- package/dist/editor/commands/comments/editing.d.ts +26 -6
- package/dist/editor/commands/comments/editing.js +177 -106
- package/dist/editor/commands/comments/model.d.ts +34 -17
- package/dist/editor/commands/comments/model.js +11 -0
- package/dist/editor/commands/comments/reading.d.ts +5 -3
- package/dist/editor/commands/comments/reading.js +8 -58
- package/dist/editor/commands/index.d.ts +2 -2
- package/dist/editor/commands/index.js +2 -0
- package/dist/editor/commands/linkCommands.js +20 -5
- package/dist/editor/commands/listCommands.d.ts +6 -0
- package/dist/editor/commands/listCommands.js +3 -2
- package/dist/editor/commands/lockCommands.js +59 -16
- package/dist/editor/commands/noteQueries.d.ts +22 -5
- package/dist/editor/commands/noteQueries.js +34 -6
- package/dist/editor/commands/paragraphCommands.js +2 -2
- package/dist/editor/createEditor.js +13 -3
- package/dist/editor/documentStyles.d.ts +26 -4
- package/dist/editor/documentStyles.js +9 -4
- package/dist/editor/imageFiles.d.ts +2 -2
- package/dist/editor/imageFiles.js +2 -0
- package/dist/editor/insertTable.d.ts +1 -1
- package/dist/editor/insertTable.js +2 -2
- package/dist/editor/plainText.d.ts +12 -0
- package/dist/editor/plainText.js +2 -1
- package/dist/editor/plugins/columnResize.js +6 -3
- package/dist/editor/plugins/commentComposer.d.ts +28 -0
- package/dist/editor/plugins/commentComposer.js +46 -0
- package/dist/editor/plugins/commentDecorations.d.ts +29 -2
- package/dist/editor/plugins/commentDecorations.js +89 -15
- package/dist/editor/plugins/documentProjection.d.ts +31 -0
- package/dist/editor/plugins/documentProjection.js +22 -0
- package/dist/editor/plugins/imagePaste.js +15 -8
- package/dist/editor/plugins/linkPanel.d.ts +7 -3
- package/dist/editor/plugins/linkPanel.js +13 -27
- package/dist/editor/plugins/panelState.d.ts +67 -0
- package/dist/editor/plugins/panelState.js +54 -0
- package/dist/editor/plugins/tableContextMenu.d.ts +1 -1
- package/dist/editor/plugins/tableContextMenu.js +30 -47
- package/dist/editor/plugins/textContextMenu.d.ts +1 -1
- package/dist/editor/plugins/textContextMenu.js +30 -47
- package/dist/ooxml/fragment.d.ts +11 -0
- package/dist/ooxml/fragment.js +14 -4
- package/dist/ooxml/image.d.ts +3 -4
- package/dist/ooxml/image.js +50 -5
- package/dist/ooxml/names.d.ts +6 -0
- package/dist/ooxml/names.js +2 -0
- package/dist/ooxml/props.d.ts +10 -0
- package/dist/ooxml/props.js +28 -4
- package/dist/ooxml/rangeMarkers.d.ts +5 -0
- package/dist/ooxml/rangeMarkers.js +24 -0
- package/dist/page/PageGuides.d.ts +7 -3
- package/dist/page/PageGuides.js +13 -24
- package/dist/page/pageLayout.d.ts +64 -5
- package/dist/page/pageLayout.js +90 -29
- package/dist/page/usePageLayout.d.ts +14 -4
- package/dist/page/usePageLayout.js +19 -20
- package/dist/schema/attrRoles.js +46 -23
- package/dist/schema/clipboard.d.ts +72 -0
- package/dist/schema/clipboard.js +288 -0
- package/dist/schema/docxSchema.d.ts +9 -1
- package/dist/schema/docxSchema.js +217 -165
- package/dist/schema/editGuard.d.ts +1 -1
- package/dist/schema/guards.js +2 -2
- package/dist/schema/index.d.ts +1 -0
- package/dist/schema/locks.d.ts +3 -0
- package/dist/schema/locks.js +29 -12
- package/dist/schema/preservedFragments.d.ts +12 -0
- package/dist/schema/preservedFragments.js +38 -0
- package/dist/schema/preservedGuards.d.ts +9 -7
- package/dist/schema/preservedGuards.js +112 -11
- package/dist/schema/protection.d.ts +34 -5
- package/dist/schema/protection.js +42 -14
- package/dist/schema/rendering.d.ts +14 -0
- package/dist/schema/rendering.js +27 -12
- package/dist/schema/stories.d.ts +49 -0
- package/dist/schema/stories.js +78 -0
- package/dist/schema/wrappers.d.ts +58 -0
- package/dist/schema/wrappers.js +77 -0
- package/dist/styles/classNames.d.ts +3 -5
- package/dist/styles/classNames.js +3 -5
- package/dist/styles.css +74 -12
- package/dist/table/resize.d.ts +5 -3
- package/dist/ui/CommentsPanel.d.ts +1 -3
- package/dist/ui/CommentsPanel.js +24 -15
- package/dist/ui/NotesPanel.js +2 -2
- package/dist/ui/TextMenu.d.ts +1 -3
- package/dist/ui/TextMenu.js +10 -6
- package/dist/ui/comments/CommentComposer.d.ts +6 -1
- package/dist/ui/comments/CommentComposer.js +26 -3
- package/package.json +5 -4
- package/dist/editor/externalClipboard.d.ts +0 -22
- package/dist/editor/externalClipboard.js +0 -486
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
/** Reads comment threads and anchors from the editor document. */
|
|
2
2
|
import type { EditorState } from "prosemirror-state";
|
|
3
|
-
import type { CommentReplyData } from "../../../docx/comments";
|
|
4
3
|
import type { DocumentComment } from "./model";
|
|
5
|
-
export declare function stringAttr(value: unknown): string | null;
|
|
6
|
-
export declare function repliesAttr(value: unknown): readonly CommentReplyData[];
|
|
7
4
|
/** Comments in document order, including point comments that have no explicit range. */
|
|
8
5
|
export declare function documentComments(state: EditorState): readonly DocumentComment[];
|
|
6
|
+
/**
|
|
7
|
+
* The comment this id names, or undefined where the document holds none. Answered off the lookup
|
|
8
|
+
* the projection built, so a command asking after one comment does not read the whole list.
|
|
9
|
+
*/
|
|
10
|
+
export declare function commentById(state: EditorState, id: string): DocumentComment | undefined;
|
|
9
11
|
/**
|
|
10
12
|
* Whether the body of this comment, or of one of its replies, may be edited or deleted here: the
|
|
11
13
|
* protection lets comments be edited at all, and the body is one of one's own to edit
|
|
@@ -1,72 +1,22 @@
|
|
|
1
1
|
// src/editor/commands/comments/reading.ts
|
|
2
2
|
import { commentOwned } from "../../../schema/protection.js";
|
|
3
3
|
import { protectionOf } from "../../../schema/protectionState.js";
|
|
4
|
-
|
|
5
|
-
return typeof value === "string" ? value : null;
|
|
6
|
-
}
|
|
7
|
-
function repliesAttr(value) {
|
|
8
|
-
return Array.isArray(value) ? value : [];
|
|
9
|
-
}
|
|
10
|
-
function markerPositions(doc) {
|
|
11
|
-
const starts = /* @__PURE__ */ new Map();
|
|
12
|
-
const ends = /* @__PURE__ */ new Map();
|
|
13
|
-
const references = [];
|
|
14
|
-
doc.descendants((node, pos) => {
|
|
15
|
-
const id = stringAttr(node.attrs.id);
|
|
16
|
-
if (id === null) return true;
|
|
17
|
-
if (node.type.name === "commentStart" && !starts.has(id)) {
|
|
18
|
-
starts.set(id, pos);
|
|
19
|
-
} else if (node.type.name === "commentEnd" && !ends.has(id)) {
|
|
20
|
-
ends.set(id, pos);
|
|
21
|
-
} else if (node.type.name === "commentReference") {
|
|
22
|
-
references.push({ node, pos });
|
|
23
|
-
}
|
|
24
|
-
return true;
|
|
25
|
-
});
|
|
26
|
-
return { starts, ends, references };
|
|
27
|
-
}
|
|
4
|
+
import { commentProjection } from "../../plugins/commentDecorations.js";
|
|
28
5
|
function documentComments(state) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const end = markers.ends.get(id);
|
|
34
|
-
const hasRange = start !== void 0 && end !== void 0 && start < end;
|
|
35
|
-
const point = start ?? end ?? pos;
|
|
36
|
-
return {
|
|
37
|
-
id,
|
|
38
|
-
author: stringAttr(node.attrs.author),
|
|
39
|
-
authorId: stringAttr(node.attrs.authorId),
|
|
40
|
-
initials: stringAttr(node.attrs.initials),
|
|
41
|
-
date: stringAttr(node.attrs.date),
|
|
42
|
-
text: stringAttr(node.attrs.text) ?? "",
|
|
43
|
-
from: hasRange ? start + 1 : point,
|
|
44
|
-
to: hasRange ? end : point,
|
|
45
|
-
referencePos: pos,
|
|
46
|
-
resolved: node.attrs.resolved === true,
|
|
47
|
-
replies: repliesAttr(node.attrs.replies).map((reply) => ({
|
|
48
|
-
id: reply.id,
|
|
49
|
-
author: reply.author,
|
|
50
|
-
authorId: reply.authorId,
|
|
51
|
-
initials: reply.initials,
|
|
52
|
-
date: reply.date,
|
|
53
|
-
text: reply.text
|
|
54
|
-
}))
|
|
55
|
-
};
|
|
56
|
-
});
|
|
6
|
+
return commentProjection.read(state).comments;
|
|
7
|
+
}
|
|
8
|
+
function commentById(state, id) {
|
|
9
|
+
return commentProjection.read(state).byId.get(id);
|
|
57
10
|
}
|
|
58
11
|
function canEditComment(state, commentId, replyId = null) {
|
|
59
12
|
const rules = protectionOf(state);
|
|
60
13
|
if (rules.protection === "readOnly") return false;
|
|
61
|
-
const comment =
|
|
62
|
-
(entry) => entry.id === commentId
|
|
63
|
-
);
|
|
14
|
+
const comment = commentById(state, commentId);
|
|
64
15
|
const body = replyId === null ? comment : comment?.replies.find((reply) => reply.id === replyId);
|
|
65
16
|
return body !== void 0 && commentOwned(rules, body.authorId);
|
|
66
17
|
}
|
|
67
18
|
export {
|
|
68
19
|
canEditComment,
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
stringAttr
|
|
20
|
+
commentById,
|
|
21
|
+
documentComments
|
|
72
22
|
};
|
|
@@ -56,8 +56,8 @@ export { insertLineBreak, insertPageBreak } from "./breakCommands";
|
|
|
56
56
|
* question takes are not exported anywhere.
|
|
57
57
|
*/
|
|
58
58
|
export { canRunCommand } from "./canRunCommand";
|
|
59
|
-
export type { CommentAuthor, DocumentComment, DocumentCommentReply, NewComment, } from "./commentCommands";
|
|
60
|
-
export { addComment, addCommentReply, canAddComment, canEditComment, documentComments, removeComment, removeCommentReply, selectComment, setCommentResolved, updateComment, updateCommentReply, } from "./commentCommands";
|
|
59
|
+
export type { CommentAuthor, CommentRange, DocumentComment, DocumentCommentReply, NewComment, } from "./commentCommands";
|
|
60
|
+
export { addComment, addCommentReply, canAddComment, canEditComment, documentComments, removeComment, removeCommentReply, selectComment, setCommentBody, setCommentResolved, updateComment, updateCommentReply, } from "./commentCommands";
|
|
61
61
|
/**
|
|
62
62
|
* Whether the document as it stands can be written back, and every reason it could not, each
|
|
63
63
|
* with the code `exportDocx` would throw it under. `canExport` is what an export control is
|
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
removeComment,
|
|
26
26
|
removeCommentReply,
|
|
27
27
|
selectComment,
|
|
28
|
+
setCommentBody,
|
|
28
29
|
setCommentResolved,
|
|
29
30
|
updateComment,
|
|
30
31
|
updateCommentReply
|
|
@@ -158,6 +159,7 @@ export {
|
|
|
158
159
|
selectComment,
|
|
159
160
|
selectionLock,
|
|
160
161
|
selectionTouchesLocked,
|
|
162
|
+
setCommentBody,
|
|
161
163
|
setCommentResolved,
|
|
162
164
|
setFontFamily,
|
|
163
165
|
setFontSize,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// src/editor/commands/linkCommands.ts
|
|
2
2
|
import { docxSchema } from "../../schema/index.js";
|
|
3
3
|
import { openStretches } from "../../schema/guards.js";
|
|
4
|
+
import { innermostDepth, wrapperMarks, wrapperOf } from "../../schema/wrappers.js";
|
|
4
5
|
var linkType = docxSchema.marks.link;
|
|
5
6
|
function text(value) {
|
|
6
7
|
return typeof value === "string" ? value : null;
|
|
@@ -16,6 +17,15 @@ function settled(address) {
|
|
|
16
17
|
function linkMarkOf(marks) {
|
|
17
18
|
return marks.find((mark) => mark.type === linkType) ?? null;
|
|
18
19
|
}
|
|
20
|
+
function linkOf(node) {
|
|
21
|
+
return wrapperOf(node, linkType);
|
|
22
|
+
}
|
|
23
|
+
function sameLink(a, b) {
|
|
24
|
+
return atDepth(a, 0).eq(atDepth(b, 0));
|
|
25
|
+
}
|
|
26
|
+
function atDepth(mark, depth) {
|
|
27
|
+
return mark.attrs.depth === depth ? mark : mark.type.create({ ...mark.attrs, depth });
|
|
28
|
+
}
|
|
19
29
|
function linkable(node, parent) {
|
|
20
30
|
if (parent === null || !parent.type.allowsMarkType(linkType)) return false;
|
|
21
31
|
return node.isText || node.type.allowsMarkType(linkType);
|
|
@@ -28,7 +38,10 @@ function piecesIn(doc, from, to) {
|
|
|
28
38
|
const start = Math.max(pos, from);
|
|
29
39
|
const end = Math.min(pos + node.nodeSize, to);
|
|
30
40
|
if (start < end) {
|
|
31
|
-
|
|
41
|
+
const link = linkOf(node);
|
|
42
|
+
pieces.push(
|
|
43
|
+
link === null ? { from: start, to: end, link, wrappers: wrapperMarks(node) } : { from: start, to: end, link }
|
|
44
|
+
);
|
|
32
45
|
}
|
|
33
46
|
return false;
|
|
34
47
|
});
|
|
@@ -37,12 +50,12 @@ function piecesIn(doc, from, to) {
|
|
|
37
50
|
function linkSpans(parent, start) {
|
|
38
51
|
const spans = [];
|
|
39
52
|
parent.forEach((child, offset) => {
|
|
40
|
-
const link =
|
|
53
|
+
const link = linkOf(child);
|
|
41
54
|
if (!link) return;
|
|
42
55
|
const from = start + offset;
|
|
43
56
|
const to = from + child.nodeSize;
|
|
44
57
|
const last = spans.at(-1);
|
|
45
|
-
if (last && last.to === from && last.link
|
|
58
|
+
if (last && last.to === from && sameLink(last.link, link)) last.to = to;
|
|
46
59
|
else spans.push({ from, to, link });
|
|
47
60
|
});
|
|
48
61
|
return spans;
|
|
@@ -52,7 +65,7 @@ function caretSpan(state) {
|
|
|
52
65
|
const link = linkMarkOf($from.marks());
|
|
53
66
|
if (!link) return null;
|
|
54
67
|
return linkSpans($from.parent, $from.start()).find(
|
|
55
|
-
(span) => span.link
|
|
68
|
+
(span) => sameLink(span.link, link) && span.from <= $from.pos && $from.pos <= span.to
|
|
56
69
|
) ?? null;
|
|
57
70
|
}
|
|
58
71
|
function selectionSpan(state) {
|
|
@@ -93,7 +106,9 @@ function selectionPieces(state) {
|
|
|
93
106
|
]);
|
|
94
107
|
}
|
|
95
108
|
function linkMarkFor(piece, href) {
|
|
96
|
-
|
|
109
|
+
if (piece.link !== null)
|
|
110
|
+
return linkType.create({ ...piece.link.attrs, href });
|
|
111
|
+
return linkType.create({ href, depth: innermostDepth(piece.wrappers) + 1 });
|
|
97
112
|
}
|
|
98
113
|
function setLink(url) {
|
|
99
114
|
return (state, dispatch) => {
|
|
@@ -33,6 +33,12 @@ export declare function numIdsIn(doc: PMNode): Set<number>;
|
|
|
33
33
|
* of its own stays, putting it back where it was before it joined the list.
|
|
34
34
|
*/
|
|
35
35
|
export declare const removeFromList: Command;
|
|
36
|
+
/**
|
|
37
|
+
* The kind of this list position, which the number format of its level decides.
|
|
38
|
+
* Null where nothing defines the list, which is a position whose kind cannot be told and so is
|
|
39
|
+
* neither of the two buttons.
|
|
40
|
+
*/
|
|
41
|
+
export declare function listKindOf(numbering: Numbering, ref: NumberingRef): ListKind | null;
|
|
36
42
|
/**
|
|
37
43
|
* The kind of list the selected paragraphs belong to. Null when they are not a list, when nothing
|
|
38
44
|
* defines the list they are in, or when the kinds are mixed.
|
|
@@ -122,7 +122,7 @@ var removeFromList = (state, dispatch) => changeParagraphs(
|
|
|
122
122
|
dispatch,
|
|
123
123
|
(node) => listRefOf(node) ? { numbering: null, indent: { kind: "clearHanging" } } : null
|
|
124
124
|
);
|
|
125
|
-
function
|
|
125
|
+
function listKindOf(numbering, ref) {
|
|
126
126
|
const level = listFor(numbering, ref.numId)?.levels.get(ref.ilvl);
|
|
127
127
|
if (!level) return null;
|
|
128
128
|
return level.format === "bullet" ? "bullet" : "numbered";
|
|
@@ -131,7 +131,7 @@ function activeListKind(state) {
|
|
|
131
131
|
const numbering = documentNumbering(state);
|
|
132
132
|
const kinds = editableParagraphs(state).map(({ node }) => {
|
|
133
133
|
const ref = listRefOf(node);
|
|
134
|
-
return ref === null ? null :
|
|
134
|
+
return ref === null ? null : listKindOf(numbering, ref);
|
|
135
135
|
});
|
|
136
136
|
const first = kinds[0] ?? null;
|
|
137
137
|
return kinds.every((kind) => kind === first) ? first : null;
|
|
@@ -174,6 +174,7 @@ export {
|
|
|
174
174
|
increaseListLevel,
|
|
175
175
|
isInList,
|
|
176
176
|
leaveEmptyListItem,
|
|
177
|
+
listKindOf,
|
|
177
178
|
listLevelChange,
|
|
178
179
|
listRefOf,
|
|
179
180
|
numIdsIn,
|
|
@@ -14,9 +14,11 @@ import {
|
|
|
14
14
|
unlockAllowed
|
|
15
15
|
} from "../../schema/locks.js";
|
|
16
16
|
import { editsShut } from "../../schema/protectionState.js";
|
|
17
|
-
|
|
17
|
+
import { innermostDepth, sharedWrappers } from "../../schema/wrappers.js";
|
|
18
|
+
function lockedControlMark(id, depth) {
|
|
18
19
|
return docxSchema.marks.sdt.create({
|
|
19
20
|
sdtPrefix: lockedControlPrefix(id),
|
|
21
|
+
depth,
|
|
20
22
|
contentsLocked: true,
|
|
21
23
|
deletionLocked: true
|
|
22
24
|
});
|
|
@@ -45,11 +47,26 @@ function overlaps(a, b) {
|
|
|
45
47
|
function reaches(a, b) {
|
|
46
48
|
return a.from <= b.to && a.to >= b.from;
|
|
47
49
|
}
|
|
50
|
+
function contains(a, b) {
|
|
51
|
+
return a.from <= b.from && a.to >= b.to;
|
|
52
|
+
}
|
|
48
53
|
function stretchIn(block, reach) {
|
|
49
54
|
const from = Math.max(block.start, reach.from);
|
|
50
55
|
const to = Math.min(block.start + block.node.content.size, reach.to);
|
|
51
56
|
return from < to ? { from, to } : null;
|
|
52
57
|
}
|
|
58
|
+
function wrapperDepthOver(block, stretch, replaced = null) {
|
|
59
|
+
const covered = [];
|
|
60
|
+
block.node.forEach((child, offset) => {
|
|
61
|
+
const from = block.start + offset;
|
|
62
|
+
if (from < stretch.to && stretch.from < from + child.nodeSize) {
|
|
63
|
+
covered.push(child);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
const shared = sharedWrappers(covered);
|
|
67
|
+
const at = replaced === null ? -1 : shared.findIndex((wrapper) => wrapper.eq(replaced));
|
|
68
|
+
return innermostDepth(at === -1 ? shared : shared.slice(0, at)) + 1;
|
|
69
|
+
}
|
|
53
70
|
function gapsIn(stretch, spans) {
|
|
54
71
|
const gaps = [];
|
|
55
72
|
let at = stretch.from;
|
|
@@ -61,38 +78,56 @@ function gapsIn(stretch, spans) {
|
|
|
61
78
|
if (at < stretch.to) gaps.push({ from: at, to: stretch.to });
|
|
62
79
|
return gaps;
|
|
63
80
|
}
|
|
64
|
-
function takeover(met, stretch, open) {
|
|
81
|
+
function takeover(block, met, stretch, open) {
|
|
65
82
|
const only = met.length === 1 ? met[0] : null;
|
|
66
83
|
const prefix = only && prefixOf(only.mark.attrs);
|
|
67
84
|
if (!only || isLocked(only.mark) || !prefix || !namesNothing(prefix)) {
|
|
68
85
|
return null;
|
|
69
86
|
}
|
|
70
|
-
const
|
|
71
|
-
|
|
87
|
+
const locked = open ? withLock(only.mark, true) : null;
|
|
88
|
+
if (!locked) return null;
|
|
89
|
+
const widened = {
|
|
72
90
|
from: Math.min(stretch.from, only.from),
|
|
73
|
-
to: Math.max(stretch.to, only.to)
|
|
74
|
-
|
|
75
|
-
|
|
91
|
+
to: Math.max(stretch.to, only.to)
|
|
92
|
+
};
|
|
93
|
+
const depth = wrapperDepthOver(block, widened, only.mark);
|
|
94
|
+
return {
|
|
95
|
+
...widened,
|
|
96
|
+
mark: depth === locked.attrs.depth ? locked : locked.type.create({ ...locked.attrs, depth }),
|
|
97
|
+
replaces: only.mark
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
function outermost(spans) {
|
|
101
|
+
const kept = [];
|
|
102
|
+
for (const span of spans) {
|
|
103
|
+
if (!kept.some((other) => overlaps(other, span))) kept.push(span);
|
|
104
|
+
}
|
|
105
|
+
return kept;
|
|
76
106
|
}
|
|
77
107
|
function blockLockEdits(state, block, spans, reach) {
|
|
78
108
|
const stretch = stretchIn(block, reach);
|
|
79
109
|
if (!stretch) return [];
|
|
80
110
|
const open = (of) => openStretches(state, of, "mark");
|
|
81
|
-
const met = spans.filter((span) => overlaps(span, stretch));
|
|
82
|
-
const taken = takeover(met, stretch, open([stretch]).length > 0);
|
|
111
|
+
const met = outermost(spans.filter((span) => overlaps(span, stretch)));
|
|
112
|
+
const taken = takeover(block, met, stretch, open([stretch]).length > 0);
|
|
83
113
|
if (taken) return [taken];
|
|
84
114
|
const shut = open(met.filter((span) => !isLocked(span.mark))).flatMap(
|
|
85
115
|
(span) => {
|
|
86
116
|
const mark = withLock(span.mark, true);
|
|
87
|
-
return mark ? [{ from: span.from, to: span.to, mark }] : [];
|
|
117
|
+
return mark ? [{ from: span.from, to: span.to, mark, replaces: span.mark }] : [];
|
|
88
118
|
}
|
|
89
119
|
);
|
|
90
120
|
const fresh = open(gapsIn(stretch, spans)).map((gap) => ({
|
|
91
121
|
...gap,
|
|
92
|
-
mark: lockedControlMark(newControlId())
|
|
122
|
+
mark: lockedControlMark(newControlId(), wrapperDepthOver(block, gap))
|
|
93
123
|
}));
|
|
94
124
|
return [...shut, ...fresh];
|
|
95
125
|
}
|
|
126
|
+
function liftable(locked, reach) {
|
|
127
|
+
return locked.filter(
|
|
128
|
+
(span, index) => contains(reach, span) || !locked.slice(index + 1).some((inner) => contains(span, inner))
|
|
129
|
+
);
|
|
130
|
+
}
|
|
96
131
|
function selectionLockDetail(state) {
|
|
97
132
|
const selection = state.selection;
|
|
98
133
|
const locking = !selection.empty && selection instanceof TextSelection;
|
|
@@ -106,9 +141,14 @@ function selectionLockDetail(state) {
|
|
|
106
141
|
if (!node.isTextblock) return true;
|
|
107
142
|
const block = { node, start: pos + 1 };
|
|
108
143
|
const controls = controlSpans(block);
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
144
|
+
spans.push(
|
|
145
|
+
...liftable(
|
|
146
|
+
controls.filter(
|
|
147
|
+
(span) => isLocked(span.mark) && reaches(span, reach)
|
|
148
|
+
),
|
|
149
|
+
reach
|
|
150
|
+
)
|
|
151
|
+
);
|
|
112
152
|
if (locking) {
|
|
113
153
|
edits.push(...blockLockEdits(state, block, controls, reach));
|
|
114
154
|
}
|
|
@@ -153,7 +193,10 @@ var lockSelection = guardedCommand((state) => {
|
|
|
153
193
|
const edits = lockEditsOf(selectionLockDetail(state));
|
|
154
194
|
if (edits.length === 0) return null;
|
|
155
195
|
const tr = state.tr;
|
|
156
|
-
for (const edit of edits)
|
|
196
|
+
for (const edit of edits) {
|
|
197
|
+
if (edit.replaces) tr.removeMark(edit.from, edit.to, edit.replaces);
|
|
198
|
+
tr.addMark(edit.from, edit.to, edit.mark);
|
|
199
|
+
}
|
|
157
200
|
return tr;
|
|
158
201
|
});
|
|
159
202
|
function unlockCell(tr, cell) {
|
|
@@ -171,8 +214,8 @@ var unlockSelection = guardedCommand((state) => {
|
|
|
171
214
|
const tr = state.tr.setMeta(unlockAllowed, true);
|
|
172
215
|
for (const span of spans) {
|
|
173
216
|
const opened = withLock(span.mark, false);
|
|
217
|
+
tr.removeMark(span.from, span.to, span.mark);
|
|
174
218
|
if (opened) tr.addMark(span.from, span.to, opened);
|
|
175
|
-
else tr.removeMark(span.from, span.to, span.mark);
|
|
176
219
|
}
|
|
177
220
|
for (const cell of cells) unlockCell(tr, cell);
|
|
178
221
|
return tr;
|
|
@@ -1,12 +1,29 @@
|
|
|
1
1
|
/** Document-level readers for imported footnotes and endnotes. */
|
|
2
2
|
import type { EditorState } from "prosemirror-state";
|
|
3
|
+
import { DecorationSet } from "prosemirror-view";
|
|
3
4
|
import type { NoteKind } from "../../docx/notes";
|
|
4
5
|
export interface DocumentNote {
|
|
5
|
-
kind: NoteKind;
|
|
6
|
-
id: string;
|
|
7
|
-
label: string;
|
|
8
|
-
text: string;
|
|
9
|
-
referencePos: number;
|
|
6
|
+
readonly kind: NoteKind;
|
|
7
|
+
readonly id: string;
|
|
8
|
+
readonly label: string;
|
|
9
|
+
readonly text: string;
|
|
10
|
+
readonly referencePos: number;
|
|
10
11
|
}
|
|
12
|
+
interface NoteProjection {
|
|
13
|
+
notes: readonly DocumentNote[];
|
|
14
|
+
/**
|
|
15
|
+
* The body of each note on the marker pointing at it, which is the tooltip a reader gets without
|
|
16
|
+
* looking away from the line. It is drawn rather than written on the node, because what a note
|
|
17
|
+
* says is a story on the document and not an attr of the reference.
|
|
18
|
+
*/
|
|
19
|
+
tooltips: DecorationSet;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Which notes the document refers to is the document's to decide, so the list is worked out once
|
|
23
|
+
* per edit (`editor/plugins/documentProjection`) rather than once per render of the panel under
|
|
24
|
+
* the page.
|
|
25
|
+
*/
|
|
26
|
+
export declare const noteProjection: import("../plugins/documentProjection").DocumentProjection<NoteProjection>;
|
|
11
27
|
/** The distinct notes referenced by the main document story, in first-reference order. */
|
|
12
28
|
export declare function documentNotes(state: EditorState): readonly DocumentNote[];
|
|
29
|
+
export {};
|
|
@@ -1,15 +1,29 @@
|
|
|
1
1
|
// src/editor/commands/noteQueries.ts
|
|
2
|
+
import { Decoration, DecorationSet } from "prosemirror-view";
|
|
3
|
+
import { storyKey, storyOf, storyText } from "../../docx/story.js";
|
|
4
|
+
import { documentProjection } from "../plugins/documentProjection.js";
|
|
2
5
|
function stringAttr(value) {
|
|
3
6
|
return typeof value === "string" ? value : null;
|
|
4
7
|
}
|
|
5
|
-
function
|
|
8
|
+
function noteKindOf(node) {
|
|
9
|
+
return node.attrs.kind === "endnote" ? "endnote" : "footnote";
|
|
10
|
+
}
|
|
11
|
+
function noteText(doc, kind, id) {
|
|
12
|
+
return storyText(storyOf(doc, storyKey(kind, id)));
|
|
13
|
+
}
|
|
14
|
+
function deriveNotes(doc) {
|
|
6
15
|
const notes = [];
|
|
16
|
+
const tooltips = [];
|
|
7
17
|
const seen = /* @__PURE__ */ new Set();
|
|
8
|
-
|
|
18
|
+
doc.descendants((node, pos) => {
|
|
9
19
|
if (node.type.name !== "noteReference") return true;
|
|
10
20
|
const id = stringAttr(node.attrs.id);
|
|
11
21
|
if (id === null) return true;
|
|
12
|
-
const kind = node
|
|
22
|
+
const kind = noteKindOf(node);
|
|
23
|
+
const text = noteText(doc, kind, id);
|
|
24
|
+
if (text !== "") {
|
|
25
|
+
tooltips.push(Decoration.node(pos, pos + node.nodeSize, { title: text }));
|
|
26
|
+
}
|
|
13
27
|
const key = `${kind}:${id}`;
|
|
14
28
|
if (seen.has(key)) return true;
|
|
15
29
|
seen.add(key);
|
|
@@ -17,13 +31,27 @@ function documentNotes(state) {
|
|
|
17
31
|
kind,
|
|
18
32
|
id,
|
|
19
33
|
label: stringAttr(node.attrs.label) ?? "?",
|
|
20
|
-
text
|
|
34
|
+
text,
|
|
21
35
|
referencePos: pos
|
|
22
36
|
});
|
|
23
37
|
return true;
|
|
24
38
|
});
|
|
25
|
-
return notes;
|
|
39
|
+
return { notes, tooltips: DecorationSet.create(doc, tooltips) };
|
|
40
|
+
}
|
|
41
|
+
var noteProjection = documentProjection(
|
|
42
|
+
"docxEditorNotes",
|
|
43
|
+
deriveNotes,
|
|
44
|
+
{
|
|
45
|
+
// The set the last edit derived. Written as a method so that `this` is the plugin holding it
|
|
46
|
+
decorations(state) {
|
|
47
|
+
return this.getState(state)?.tooltips;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
);
|
|
51
|
+
function documentNotes(state) {
|
|
52
|
+
return noteProjection.read(state).notes;
|
|
26
53
|
}
|
|
27
54
|
export {
|
|
28
|
-
documentNotes
|
|
55
|
+
documentNotes,
|
|
56
|
+
noteProjection
|
|
29
57
|
};
|
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
import {
|
|
3
3
|
paragraphAttrsOf,
|
|
4
4
|
resolveParagraph,
|
|
5
|
-
runMarkUnder
|
|
6
|
-
styleIdOf
|
|
5
|
+
runMarkUnder
|
|
7
6
|
} from "../../docx/formatting.js";
|
|
8
7
|
import {
|
|
9
8
|
withParagraphAlign,
|
|
10
9
|
withParagraphStyle
|
|
11
10
|
} from "../../docx/paraProps.js";
|
|
12
11
|
import { toParagraphFormat } from "../../model/format.js";
|
|
12
|
+
import { styleIdOf } from "../../ooxml/props.js";
|
|
13
13
|
import { docxSchema } from "../../schema/index.js";
|
|
14
14
|
import { lockedMarkOf } from "../../schema/locks.js";
|
|
15
15
|
import { documentFormatting } from "../documentStyles.js";
|
|
@@ -14,15 +14,17 @@ import {
|
|
|
14
14
|
DEFAULT_FONT_FALLBACKS
|
|
15
15
|
} from "../styles/fontStack.js";
|
|
16
16
|
import { documentDefaultsStyle } from "../styles/inlineStyle.js";
|
|
17
|
+
import { docxClipboard } from "./clipboard/plugin.js";
|
|
18
|
+
import { noteProjection } from "./commands/noteQueries.js";
|
|
17
19
|
import {
|
|
18
20
|
documentOf,
|
|
19
21
|
editorDocument,
|
|
20
22
|
editorDocumentOf,
|
|
21
23
|
NO_DOCUMENT
|
|
22
24
|
} from "./editorDocument.js";
|
|
23
|
-
import { externalClipboard } from "./externalClipboard.js";
|
|
24
25
|
import { imageFiles } from "./imageFiles.js";
|
|
25
26
|
import { columnResize } from "./plugins/columnResize.js";
|
|
27
|
+
import { commentComposer } from "./plugins/commentComposer.js";
|
|
26
28
|
import { commentDecorations } from "./plugins/commentDecorations.js";
|
|
27
29
|
import {
|
|
28
30
|
displayDerivation,
|
|
@@ -80,17 +82,22 @@ function createEditorState(doc, options = {}) {
|
|
|
80
82
|
// above, is what takes such a conversion back
|
|
81
83
|
listInputRules(),
|
|
82
84
|
dropCursor(),
|
|
83
|
-
//
|
|
85
|
+
// Images get the first reading. The clipboard then handles only empty readings and text
|
|
86
|
+
// fallbacks; tableEditing owns insertion into a cell selection for every nonempty slice.
|
|
84
87
|
imageFiles(),
|
|
85
88
|
imagePaste(),
|
|
86
|
-
|
|
89
|
+
docxClipboard(),
|
|
87
90
|
// A press that grabs a table edge must be intercepted before a cell-selection drag starts.
|
|
88
91
|
// For DOM events the plugin registered first wins, so both resizers precede `tableEditing`.
|
|
89
92
|
columnResize(),
|
|
90
93
|
rowResize(),
|
|
91
94
|
tableEditing(),
|
|
92
95
|
// Comment anchors live in the model; this paints their ranges without changing document XML.
|
|
96
|
+
// The list beside the page, the lookup the comment commands ask, and these ranges are the
|
|
97
|
+
// one walk this plugin holds (`plugins/commentDecorations`)
|
|
93
98
|
commentDecorations(),
|
|
99
|
+
// What the notes under the page are, worked out from the document the same way
|
|
100
|
+
noteProjection.plugin,
|
|
94
101
|
// Adjacent text tabs still need separate DOM ranges for layout and pointer selection.
|
|
95
102
|
tabDecorations(),
|
|
96
103
|
tabPointer(),
|
|
@@ -103,6 +110,9 @@ function createEditorState(doc, options = {}) {
|
|
|
103
110
|
// or fall together with the menus the editor draws. The text menu stands ahead of the table
|
|
104
111
|
// menu, and hands a click with nothing selected inside a cell back to it
|
|
105
112
|
...contextMenus ? [textContextMenu(), tableContextMenu()] : [],
|
|
113
|
+
// Holds the stretch of text the built-in comment composer was opened over. It reads the
|
|
114
|
+
// protection above it, so it stands after the plugin that holds one
|
|
115
|
+
commentComposer(),
|
|
106
116
|
numberingMarkers(),
|
|
107
117
|
// Values only go in when page display is turned on
|
|
108
118
|
pageDecorations()
|
|
@@ -19,10 +19,25 @@ export declare function documentDefaults(state: EditorState): DocumentDefaults;
|
|
|
19
19
|
/** The paragraph styles this document defines. Empty when the editor does not know them */
|
|
20
20
|
export declare function documentParagraphStyles(state: EditorState): ParagraphStyleOption[];
|
|
21
21
|
/**
|
|
22
|
-
* The paper the
|
|
23
|
-
* the
|
|
22
|
+
* The paper the sheet is drawn on: the first section's, since one sheet is drawn at one paper
|
|
23
|
+
* (`page/pageLayout`). A document the editor knows no paper for is drawn on the same A4 every
|
|
24
|
+
* document was drawn on before the geometry was read.
|
|
25
|
+
*
|
|
26
|
+
* Anything deciding a value for one spot in the document - the width of a table put there, the
|
|
27
|
+
* height a pasted image is fitted to - asks `sectionGeometryAt` instead, since a later section may
|
|
28
|
+
* be written on another paper.
|
|
24
29
|
*/
|
|
25
30
|
export declare function documentGeometry(state: EditorState): PageGeometry;
|
|
31
|
+
/**
|
|
32
|
+
* The paper the block at this position is written on: the paper of the section that block belongs
|
|
33
|
+
* to (`docx/sections`), which is the width a table inserted or resized there is fitted to.
|
|
34
|
+
*
|
|
35
|
+
* The section table is read off the document rather than off the snapshot the state carries: a
|
|
36
|
+
* section is where the document says it is, and the snapshot holds the first section's paper
|
|
37
|
+
* alone. Reading it walks every block, so a caller drawing a whole document asks once and looks
|
|
38
|
+
* its positions up (`page/pageLayout`) rather than calling this per block.
|
|
39
|
+
*/
|
|
40
|
+
export declare function sectionGeometryAt(state: EditorState, pos: number): PageGeometry;
|
|
26
41
|
/** The document-wide interval between automatic tab stops, in points. */
|
|
27
42
|
export declare function documentDefaultTabStopPt(state: EditorState): number;
|
|
28
43
|
/**
|
|
@@ -32,5 +47,12 @@ export declare function documentDefaultTabStopPt(state: EditorState): number;
|
|
|
32
47
|
* itself asks for it rather than taking the A4 width `fittedExtent` falls back to.
|
|
33
48
|
*/
|
|
34
49
|
export declare function documentBodyWidthPx(state: EditorState): number;
|
|
35
|
-
/**
|
|
36
|
-
|
|
50
|
+
/**
|
|
51
|
+
* The height one page of body content occupies at this position, in pixels: the height the paper
|
|
52
|
+
* of the section that block sits in leaves.
|
|
53
|
+
*
|
|
54
|
+
* It is the height an image taller than the page is shrunk to when it is pasted there, so a tall
|
|
55
|
+
* image dropped into a section on shorter paper is fitted to that section rather than to the
|
|
56
|
+
* sheet's own.
|
|
57
|
+
*/
|
|
58
|
+
export declare function sectionBodyHeightPx(state: EditorState, pos: number): number;
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
bodyWidth,
|
|
5
5
|
twipsToPx
|
|
6
6
|
} from "../docx/pageGeometry.js";
|
|
7
|
+
import { sectionAt } from "../docx/sections.js";
|
|
7
8
|
import { documentOf } from "./editorDocument.js";
|
|
8
9
|
function documentFormatting(state) {
|
|
9
10
|
return documentOf(state).formatting;
|
|
@@ -17,21 +18,25 @@ function documentParagraphStyles(state) {
|
|
|
17
18
|
function documentGeometry(state) {
|
|
18
19
|
return documentOf(state).geometry;
|
|
19
20
|
}
|
|
21
|
+
function sectionGeometryAt(state, pos) {
|
|
22
|
+
return sectionAt(state.doc, pos).props.geometry;
|
|
23
|
+
}
|
|
20
24
|
function documentDefaultTabStopPt(state) {
|
|
21
25
|
return documentOf(state).defaultTabStopPt;
|
|
22
26
|
}
|
|
23
27
|
function documentBodyWidthPx(state) {
|
|
24
28
|
return bodyWidth(documentGeometry(state)).px;
|
|
25
29
|
}
|
|
26
|
-
function
|
|
27
|
-
return twipsToPx(bodyHeightTwips(
|
|
30
|
+
function sectionBodyHeightPx(state, pos) {
|
|
31
|
+
return twipsToPx(bodyHeightTwips(sectionGeometryAt(state, pos)));
|
|
28
32
|
}
|
|
29
33
|
export {
|
|
30
|
-
documentBodyHeightPx,
|
|
31
34
|
documentBodyWidthPx,
|
|
32
35
|
documentDefaultTabStopPt,
|
|
33
36
|
documentDefaults,
|
|
34
37
|
documentFormatting,
|
|
35
38
|
documentGeometry,
|
|
36
|
-
documentParagraphStyles
|
|
39
|
+
documentParagraphStyles,
|
|
40
|
+
sectionBodyHeightPx,
|
|
41
|
+
sectionGeometryAt
|
|
37
42
|
};
|
|
@@ -65,8 +65,8 @@ export declare function insertImageFiles(view: EditorView, files: readonly File[
|
|
|
65
65
|
/**
|
|
66
66
|
* The plugin that takes image files pasted or dropped onto the paper.
|
|
67
67
|
*
|
|
68
|
-
* It
|
|
69
|
-
*
|
|
68
|
+
* It runs before clipboard fallback and table insertion (see the order in `createEditor.ts`).
|
|
69
|
+
* A paste that chose plain text leaves any accompanying image file alone.
|
|
70
70
|
* An event carrying no image file is passed on untouched, so text keeps coming in exactly
|
|
71
71
|
* as it did.
|
|
72
72
|
*/
|