@portone/docx-editor 0.5.1 → 0.6.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 +47 -0
- package/CONTRIBUTING.md +8 -2
- package/README.md +1 -1
- package/dist/DocxEditor.js +87 -29
- package/dist/docx/exportDocx.d.ts +2 -1
- package/dist/docx/exportDocx.js +3 -9
- package/dist/docx/headersFooters.js +35 -23
- package/dist/docx/identities.d.ts +28 -0
- package/dist/docx/identities.js +42 -10
- package/dist/docx/importDocx.js +25 -26
- package/dist/docx/importParagraph.d.ts +1 -4
- package/dist/docx/importParagraph.js +4 -18
- package/dist/docx/invariants.d.ts +1 -1
- package/dist/docx/invariants.js +45 -4
- package/dist/docx/notes/newNote.d.ts +39 -0
- package/dist/docx/notes/newNote.js +122 -0
- package/dist/docx/notes/numbering.d.ts +27 -0
- package/dist/docx/notes/numbering.js +120 -0
- package/dist/docx/notes/reading.d.ts +58 -0
- package/dist/docx/notes/reading.js +98 -0
- package/dist/docx/notes/writing.d.ts +19 -0
- package/dist/docx/notes/writing.js +96 -0
- package/dist/docx/partPlan.d.ts +3 -0
- package/dist/docx/partPlanners.d.ts +13 -0
- package/dist/docx/partPlanners.js +48 -0
- package/dist/docx/sections.d.ts +5 -0
- package/dist/docx/sections.js +7 -2
- package/dist/docx/serializeParagraph.js +7 -2
- package/dist/docx/session.d.ts +5 -0
- package/dist/docx/session.js +6 -0
- package/dist/docx/storyParts.d.ts +112 -0
- package/dist/docx/storyParts.js +289 -0
- package/dist/editor/clipboard/htmlReader.d.ts +24 -4
- package/dist/editor/clipboard/htmlReader.js +39 -4
- package/dist/editor/clipboard/internalChannel.d.ts +30 -3
- package/dist/editor/clipboard/internalChannel.js +43 -3
- package/dist/editor/clipboard/normalizers.d.ts +35 -8
- package/dist/editor/clipboard/normalizers.js +60 -11
- package/dist/editor/clipboard/plugin.js +42 -22
- package/dist/editor/commands/comments/editing.d.ts +4 -1
- package/dist/editor/commands/comments/editing.js +1 -3
- package/dist/editor/commands/comments/model.d.ts +7 -0
- package/dist/editor/commands/index.d.ts +1 -0
- package/dist/editor/commands/index.js +18 -0
- package/dist/editor/commands/noteCommands.d.ts +47 -0
- package/dist/editor/commands/noteCommands.js +95 -0
- package/dist/editor/commands/noteQueries.d.ts +22 -3
- package/dist/editor/commands/noteQueries.js +21 -13
- package/dist/editor/createEditor.js +25 -3
- package/dist/editor/documentStyles.d.ts +3 -0
- package/dist/editor/documentStyles.js +2 -1
- package/dist/editor/editorDocument.d.ts +24 -0
- package/dist/editor/editorDocument.js +34 -4
- package/dist/editor/notes/noteSurface.d.ts +53 -0
- package/dist/editor/notes/noteSurface.js +246 -0
- package/dist/editor/plugins/commentDecorations.js +5 -4
- package/dist/editor/plugins/commentRestoration.d.ts +20 -0
- package/dist/editor/plugins/commentRestoration.js +168 -0
- package/dist/editor/plugins/compositionSelection.d.ts +20 -0
- package/dist/editor/plugins/compositionSelection.js +19 -0
- package/dist/editor/plugins/keymap.d.ts +14 -0
- package/dist/editor/plugins/keymap.js +13 -1
- package/dist/editor/plugins/noteLifecycle.d.ts +18 -0
- package/dist/editor/plugins/noteLifecycle.js +117 -0
- package/dist/editor/plugins/noteNavigation.d.ts +45 -0
- package/dist/editor/plugins/noteNavigation.js +86 -0
- package/dist/editor/plugins/noteNumbering.d.ts +13 -0
- package/dist/editor/plugins/noteNumbering.js +58 -0
- package/dist/editor/plugins/rowResize.js +2 -7
- package/dist/editor/plugins/tabLayout.js +2 -7
- package/dist/editor/stories/storyMarkup.d.ts +24 -0
- package/dist/editor/stories/storyMarkup.js +35 -0
- package/dist/editor/stories/storyState.d.ts +34 -0
- package/dist/editor/stories/storyState.js +74 -0
- package/dist/editor/stories/storyView.d.ts +115 -0
- package/dist/editor/stories/storyView.js +144 -0
- package/dist/numbering/spellers.d.ts +1 -1
- package/dist/numbering/spellers.js +6 -1
- package/dist/page/blockKinds.d.ts +23 -4
- package/dist/page/demands/footnoteDemands.d.ts +18 -0
- package/dist/page/demands/footnoteDemands.js +51 -0
- package/dist/page/demands/index.d.ts +43 -0
- package/dist/page/demands/index.js +8 -0
- package/dist/page/kinds/paragraphKind.js +7 -1
- package/dist/page/kinds/tableKind.js +9 -1
- package/dist/page/measureBlocks.d.ts +2 -0
- package/dist/page/measureBlocks.js +31 -10
- package/dist/page/pageDecorations.d.ts +9 -2
- package/dist/page/pageDecorations.js +18 -5
- package/dist/page/pageLayout.d.ts +77 -1
- package/dist/page/pageLayout.js +186 -9
- package/dist/page/usePageLayout.d.ts +57 -2
- package/dist/page/usePageLayout.js +50 -5
- package/dist/schema/attrRoles.js +3 -2
- package/dist/schema/docxSchema.js +5 -2
- package/dist/schema/editGuard.d.ts +1 -1
- package/dist/schema/guards.js +1 -2
- package/dist/schema/preservedGuards.d.ts +4 -6
- package/dist/schema/preservedGuards.js +0 -9
- package/dist/schema/stories.d.ts +34 -2
- package/dist/schema/stories.js +22 -5
- package/dist/styles/classNames.d.ts +23 -6
- package/dist/styles/classNames.js +23 -6
- package/dist/styles/inlineStyle.d.ts +7 -0
- package/dist/styles/inlineStyle.js +11 -5
- package/dist/styles/visualScale.d.ts +19 -0
- package/dist/styles/visualScale.js +26 -0
- package/dist/styles.css +96 -36
- package/dist/ui/CommentsPanel.d.ts +6 -1
- package/dist/ui/CommentsPanel.js +26 -8
- package/dist/ui/InsertImageButton.d.ts +3 -1
- package/dist/ui/InsertImageButton.js +3 -2
- package/dist/ui/TableMenu.d.ts +5 -2
- package/dist/ui/TableMenu.js +18 -1
- package/dist/ui/TextMenu.d.ts +5 -2
- package/dist/ui/TextMenu.js +21 -6
- package/dist/ui/Toolbar.d.ts +17 -2
- package/dist/ui/Toolbar.js +23 -10
- package/dist/ui/comments/useCommentRailLayout.js +2 -4
- package/dist/ui/noteItems.d.ts +21 -0
- package/dist/ui/noteItems.js +30 -0
- package/dist/ui/notes/NoteAreas.d.ts +73 -0
- package/dist/ui/notes/NoteAreas.js +131 -0
- package/dist/ui/notes/NoteList.d.ts +25 -0
- package/dist/ui/notes/NoteList.js +58 -0
- package/dist/ui/notes/StoryRow.d.ts +75 -0
- package/dist/ui/notes/StoryRow.js +122 -0
- package/dist/ui/notes/noteBands.d.ts +100 -0
- package/dist/ui/notes/noteBands.js +142 -0
- package/dist/ui/notes/noteHeights.d.ts +10 -0
- package/dist/ui/notes/noteHeights.js +24 -0
- package/dist/ui/notes/noteSeparator.d.ts +12 -0
- package/dist/ui/notes/noteSeparator.js +9 -0
- package/dist/ui/notes/useStorySurface.d.ts +68 -0
- package/dist/ui/notes/useStorySurface.js +95 -0
- package/dist/ui/runCommand.d.ts +8 -1
- package/dist/ui/runCommand.js +2 -2
- package/dist/ui/shortcutLabels.d.ts +16 -0
- package/dist/ui/shortcutLabels.js +9 -0
- package/dist/ui/usePageRoom.d.ts +13 -0
- package/dist/ui/usePageRoom.js +31 -0
- package/package.json +2 -1
- package/dist/docx/notes.d.ts +0 -27
- package/dist/docx/notes.js +0 -55
- package/dist/ui/NotesPanel.d.ts +0 -6
- package/dist/ui/NotesPanel.js +0 -33
|
@@ -9,6 +9,11 @@ import {
|
|
|
9
9
|
resolveParagraph,
|
|
10
10
|
resolveRun
|
|
11
11
|
} from "../../docx/formatting.js";
|
|
12
|
+
import {
|
|
13
|
+
copiedNoteStory,
|
|
14
|
+
nextNoteId,
|
|
15
|
+
takenNoteIds
|
|
16
|
+
} from "../../docx/notes/newNote.js";
|
|
12
17
|
import { withListNumbering } from "../../docx/paraProps.js";
|
|
13
18
|
import {
|
|
14
19
|
allocateList,
|
|
@@ -20,13 +25,21 @@ import {
|
|
|
20
25
|
RANGE_MARKERS
|
|
21
26
|
} from "../../ooxml/rangeMarkers.js";
|
|
22
27
|
import { docxSchema } from "../../schema/index.js";
|
|
28
|
+
import {
|
|
29
|
+
EDITABLE_NOTE_KINDS,
|
|
30
|
+
storyKey
|
|
31
|
+
} from "../../schema/stories.js";
|
|
23
32
|
import { listRefOf, numIdsIn } from "../commands/listCommands.js";
|
|
24
33
|
import { documentFormatting } from "../documentStyles.js";
|
|
34
|
+
import { documentOf } from "../editorDocument.js";
|
|
25
35
|
import {
|
|
26
36
|
canStartNewList,
|
|
27
37
|
documentNumbering
|
|
28
38
|
} from "../plugins/numberingDecorations.js";
|
|
29
|
-
import {
|
|
39
|
+
import {
|
|
40
|
+
NO_LIST_KINDS,
|
|
41
|
+
NO_NOTE_STORIES
|
|
42
|
+
} from "./internalChannel.js";
|
|
30
43
|
function xmlAttr(value) {
|
|
31
44
|
return typeof value === "string" ? value : null;
|
|
32
45
|
}
|
|
@@ -90,22 +103,25 @@ var rekeyNumbering = (slice, context) => {
|
|
|
90
103
|
return props === null ? node : withAttrs(node, { ...node.attrs, pPr: props.pPr });
|
|
91
104
|
});
|
|
92
105
|
};
|
|
93
|
-
var
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
]);
|
|
106
|
+
var ANCHORS = {
|
|
107
|
+
commentStart: () => true,
|
|
108
|
+
commentEnd: () => true,
|
|
109
|
+
commentReference: () => true
|
|
110
|
+
};
|
|
99
111
|
var REFERENCE_ELEMENTS = [
|
|
100
112
|
"commentReference",
|
|
101
113
|
"footnoteReference",
|
|
102
|
-
"endnoteReference"
|
|
114
|
+
"endnoteReference",
|
|
115
|
+
"footnoteRef",
|
|
116
|
+
"endnoteRef"
|
|
103
117
|
];
|
|
118
|
+
var NOTE_OWN_MARKS = ["footnoteRef", "endnoteRef"];
|
|
104
119
|
var ANCHOR_ELEMENTS = /* @__PURE__ */ new Set([
|
|
105
120
|
...RANGE_MARKERS,
|
|
106
121
|
...PERMISSION_MARKERS,
|
|
107
122
|
...COMMENT_RANGE_MARKERS,
|
|
108
|
-
...REFERENCE_ELEMENTS
|
|
123
|
+
...REFERENCE_ELEMENTS,
|
|
124
|
+
...NOTE_OWN_MARKS
|
|
109
125
|
]);
|
|
110
126
|
var PRESERVED_INLINE = /* @__PURE__ */ new Set([
|
|
111
127
|
"rawInline",
|
|
@@ -119,11 +135,26 @@ function preservedName(node) {
|
|
|
119
135
|
return typeof element === "string" ? element : null;
|
|
120
136
|
}
|
|
121
137
|
var detachAnchors = (slice, { move }) => move ? slice : mapSliceNodes(slice, (node) => {
|
|
122
|
-
if (
|
|
138
|
+
if (ANCHORS[node.type.name]?.(node) === true) return null;
|
|
123
139
|
if (!PRESERVED_INLINE.has(node.type.name)) return node;
|
|
124
140
|
const name = preservedName(node);
|
|
125
141
|
return name !== null && ANCHOR_ELEMENTS.has(name) ? null : node;
|
|
126
142
|
});
|
|
143
|
+
var duplicateNotes = (slice, { move, noteStories, startNote }) => move ? slice : mapSliceNodes(slice, (node) => {
|
|
144
|
+
if (node.type !== docxSchema.nodes.noteReference) return node;
|
|
145
|
+
const kind = EDITABLE_NOTE_KINDS.find(
|
|
146
|
+
(candidate) => candidate === node.attrs.kind
|
|
147
|
+
);
|
|
148
|
+
const id = node.attrs.id;
|
|
149
|
+
if (kind === void 0 || typeof id !== "string") return null;
|
|
150
|
+
const story = noteStories.get(storyKey(kind, id));
|
|
151
|
+
if (story === void 0) return null;
|
|
152
|
+
return withAttrs(node, {
|
|
153
|
+
...node.attrs,
|
|
154
|
+
id: startNote(kind, story),
|
|
155
|
+
referenceXml: null
|
|
156
|
+
});
|
|
157
|
+
});
|
|
127
158
|
var rederiveDisplay = (slice, { state }) => {
|
|
128
159
|
const formatting = documentFormatting(state);
|
|
129
160
|
return mapSliceNodes(slice, (node) => {
|
|
@@ -152,6 +183,7 @@ var rederiveDisplay = (slice, { state }) => {
|
|
|
152
183
|
var DEFAULT_NORMALIZERS = [
|
|
153
184
|
dropSourceIdentity,
|
|
154
185
|
detachAnchors,
|
|
186
|
+
duplicateNotes,
|
|
155
187
|
rekeyNumbering,
|
|
156
188
|
rederiveDisplay
|
|
157
189
|
];
|
|
@@ -164,11 +196,26 @@ function normalizePasted(content, state, move, normalizers = DEFAULT_NORMALIZERS
|
|
|
164
196
|
...content.newLists.keys()
|
|
165
197
|
]);
|
|
166
198
|
const started = /* @__PURE__ */ new Map();
|
|
199
|
+
const document = documentOf(state);
|
|
200
|
+
const takenNotes = /* @__PURE__ */ new Map();
|
|
201
|
+
const startedNotes = /* @__PURE__ */ new Map();
|
|
167
202
|
const context = {
|
|
168
203
|
state,
|
|
169
204
|
move,
|
|
170
205
|
knownLists: known,
|
|
171
206
|
listKinds: content.listKinds ?? NO_LIST_KINDS,
|
|
207
|
+
noteStories: content.noteStories ?? NO_NOTE_STORIES,
|
|
208
|
+
startNote(kind, story) {
|
|
209
|
+
const ids = takenNotes.get(kind) ?? takenNoteIds(state.doc, kind, document.reservedNoteKeys);
|
|
210
|
+
takenNotes.set(kind, ids);
|
|
211
|
+
const id = nextNoteId(ids);
|
|
212
|
+
ids.add(id);
|
|
213
|
+
startedNotes.set(
|
|
214
|
+
storyKey(kind, id),
|
|
215
|
+
copiedNoteStory(story, document.session)
|
|
216
|
+
);
|
|
217
|
+
return id;
|
|
218
|
+
},
|
|
172
219
|
startList(kind) {
|
|
173
220
|
if (!canStartNewList(state)) return null;
|
|
174
221
|
const list = templateList(kind);
|
|
@@ -185,13 +232,15 @@ function normalizePasted(content, state, move, normalizers = DEFAULT_NORMALIZERS
|
|
|
185
232
|
);
|
|
186
233
|
return {
|
|
187
234
|
slice,
|
|
188
|
-
newLists: started.size === 0 ? content.newLists : new Map([...content.newLists, ...started])
|
|
235
|
+
newLists: started.size === 0 ? content.newLists : new Map([...content.newLists, ...started]),
|
|
236
|
+
newStories: startedNotes.size === 0 ? content.newStories : startedNotes
|
|
189
237
|
};
|
|
190
238
|
}
|
|
191
239
|
export {
|
|
192
240
|
DEFAULT_NORMALIZERS,
|
|
193
241
|
detachAnchors,
|
|
194
242
|
dropSourceIdentity,
|
|
243
|
+
duplicateNotes,
|
|
195
244
|
mapSliceNodes,
|
|
196
245
|
normalizePasted,
|
|
197
246
|
rederiveDisplay,
|
|
@@ -13,7 +13,9 @@ import { documentOf } from "../editorDocument.js";
|
|
|
13
13
|
import { insertPlainText } from "../plainText.js";
|
|
14
14
|
import { moveCaretToDrop } from "../plugins/dropCaret.js";
|
|
15
15
|
import { documentNumbering } from "../plugins/numberingDecorations.js";
|
|
16
|
+
import { withPastedStories } from "./htmlReader.js";
|
|
16
17
|
import {
|
|
18
|
+
draggedNotes,
|
|
17
19
|
INTERNAL_TOKEN_ATTRIBUTE,
|
|
18
20
|
internalTokenOf,
|
|
19
21
|
rememberCopied
|
|
@@ -78,6 +80,7 @@ function docxClipboard(options = {}) {
|
|
|
78
80
|
}
|
|
79
81
|
);
|
|
80
82
|
let started = null;
|
|
83
|
+
let startedStories = null;
|
|
81
84
|
let dropMove = false;
|
|
82
85
|
let draggingOut = false;
|
|
83
86
|
return new Plugin({
|
|
@@ -90,26 +93,33 @@ function docxClipboard(options = {}) {
|
|
|
90
93
|
};
|
|
91
94
|
},
|
|
92
95
|
/**
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
+
* What a paste brought beside its text, registered on the document once the paste is in: the
|
|
97
|
+
* definitions of the lists it began and the notes its references call. A list the pasted
|
|
98
|
+
* markup started is numbered as it is read, and only the edit that lands says whether that
|
|
99
|
+
* number is worn in the end.
|
|
96
100
|
*/
|
|
97
101
|
appendTransaction(transactions, _before, after) {
|
|
98
102
|
const lists = started;
|
|
103
|
+
const stories = startedStories;
|
|
99
104
|
started = null;
|
|
100
|
-
|
|
101
|
-
|
|
105
|
+
startedStories = null;
|
|
106
|
+
if (!transactions.some((tr2) => tr2.docChanged)) return null;
|
|
107
|
+
const tr = after.tr;
|
|
108
|
+
if (lists !== null && lists.size > 0) {
|
|
109
|
+
const worn = numIdsIn(after.doc);
|
|
110
|
+
const registered = newListsOf(after.doc.attrs[NEW_LISTS_ATTR]);
|
|
111
|
+
const missing = [...lists].filter(
|
|
112
|
+
([numId]) => worn.has(numId) && !registered.has(numId)
|
|
113
|
+
);
|
|
114
|
+
if (missing.length > 0) {
|
|
115
|
+
tr.setDocAttribute(
|
|
116
|
+
NEW_LISTS_ATTR,
|
|
117
|
+
newListsValue(new Map([...registered, ...missing]))
|
|
118
|
+
);
|
|
119
|
+
}
|
|
102
120
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
const missing = [...lists].filter(
|
|
106
|
-
([numId]) => worn.has(numId) && !registered.has(numId)
|
|
107
|
-
);
|
|
108
|
-
if (missing.length === 0) return null;
|
|
109
|
-
return after.tr.setDocAttribute(
|
|
110
|
-
NEW_LISTS_ATTR,
|
|
111
|
-
newListsValue(new Map([...registered, ...missing]))
|
|
112
|
-
);
|
|
121
|
+
if (stories !== null) withPastedStories(tr, stories);
|
|
122
|
+
return tr.steps.length === 0 ? null : tr;
|
|
113
123
|
},
|
|
114
124
|
props: {
|
|
115
125
|
handleDOMEvents: {
|
|
@@ -145,24 +155,34 @@ function docxClipboard(options = {}) {
|
|
|
145
155
|
clipboardTextSerializer: clipboardText,
|
|
146
156
|
transformCopied(slice, view) {
|
|
147
157
|
const route = draggingOut ? "drag" : "clipboard";
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
documentNumbering(view.state)
|
|
153
|
-
|
|
158
|
+
const document = documentOf(view.state);
|
|
159
|
+
copyToken = rememberCopied(route, slice, {
|
|
160
|
+
sessionId: document.session?.sessionId ?? null,
|
|
161
|
+
doc: view.state.doc,
|
|
162
|
+
numbering: documentNumbering(view.state),
|
|
163
|
+
specialNotes: document.specialNotes
|
|
164
|
+
});
|
|
154
165
|
return copiedSlice(slice);
|
|
155
166
|
},
|
|
156
167
|
transformPasted(slice, view, plain) {
|
|
157
168
|
if (!plain) parser.setPlainText(false);
|
|
158
169
|
const read = parser.takeRead();
|
|
159
170
|
const content = normalizePasted(
|
|
160
|
-
|
|
171
|
+
// A drop is handed the dragged slice itself rather than markup, so what that slice
|
|
172
|
+
// carried is looked up by the route it left on rather than by a name written into markup
|
|
173
|
+
read ?? {
|
|
174
|
+
slice,
|
|
175
|
+
newLists: NO_NEW_LISTS,
|
|
176
|
+
noteStories: draggedNotes(
|
|
177
|
+
documentOf(view.state).session?.sessionId ?? null
|
|
178
|
+
)
|
|
179
|
+
},
|
|
161
180
|
view.state,
|
|
162
181
|
dropMove,
|
|
163
182
|
normalizers
|
|
164
183
|
);
|
|
165
184
|
started = content.newLists;
|
|
185
|
+
startedStories = content.newStories ?? null;
|
|
166
186
|
return content.slice;
|
|
167
187
|
},
|
|
168
188
|
handlePaste(view, event, slice) {
|
|
@@ -41,5 +41,8 @@ export declare function removeCommentReply(commentId: string, replyId: string):
|
|
|
41
41
|
* document without standing for anything.
|
|
42
42
|
*/
|
|
43
43
|
export declare function removeComment(id: string): Command;
|
|
44
|
-
/**
|
|
44
|
+
/**
|
|
45
|
+
* Selects the text a comment is anchored to and scrolls it into view. A comment marking no stretch
|
|
46
|
+
* - one written at a point, or one whose text was deleted - takes the caret to where it stands.
|
|
47
|
+
*/
|
|
45
48
|
export declare function selectComment(id: string): Command;
|
|
@@ -342,9 +342,7 @@ function selectComment(id) {
|
|
|
342
342
|
const comment = commentById(state, id);
|
|
343
343
|
if (!comment) return false;
|
|
344
344
|
dispatch?.(
|
|
345
|
-
state.tr.setSelection(
|
|
346
|
-
TextSelection.create(state.doc, comment.from, comment.to)
|
|
347
|
-
)
|
|
345
|
+
state.tr.setSelection(TextSelection.create(state.doc, comment.from, comment.to)).scrollIntoView()
|
|
348
346
|
);
|
|
349
347
|
return true;
|
|
350
348
|
};
|
|
@@ -42,6 +42,13 @@ export interface DocumentComment {
|
|
|
42
42
|
readonly from: number;
|
|
43
43
|
readonly to: number;
|
|
44
44
|
readonly referencePos: number;
|
|
45
|
+
/**
|
|
46
|
+
* Whether the comment still marks a stretch of the document: both range markers stand, and the
|
|
47
|
+
* opening one stands before the closing one. A comment that lost both markers with the text an
|
|
48
|
+
* edit deleted is detached - `from` and `to` are then both where its reference stands - and is
|
|
49
|
+
* drawn nowhere on the page.
|
|
50
|
+
*/
|
|
51
|
+
readonly anchored: boolean;
|
|
45
52
|
readonly resolved: boolean;
|
|
46
53
|
readonly replies: readonly DocumentCommentReply[];
|
|
47
54
|
}
|
|
@@ -90,6 +90,7 @@ export { activeLink, activeLinkSpan, canSetLink, removeLink, setLink, } from "./
|
|
|
90
90
|
export { activeListKind, decreaseListLevel, increaseListLevel, isInList, toggleBulletList, toggleNumberedList, } from "./listCommands";
|
|
91
91
|
export type { SelectionLock } from "./lockCommands";
|
|
92
92
|
export { documentHasLocked, lockSelection, selectionLock, selectionTouchesLocked, unlockSelection, } from "./lockCommands";
|
|
93
|
+
export { canInsertEndnote, canInsertFootnote, insertEndnote, insertFootnote, openEndnote, openFootnote, setEndnoteBody, setFootnoteBody, } from "./noteCommands";
|
|
93
94
|
export type { DocumentNote } from "./noteQueries";
|
|
94
95
|
export { documentNotes } from "./noteQueries";
|
|
95
96
|
export type { ActiveParagraphAlign, ActiveParagraphStyle, } from "./paragraphCommands";
|
|
@@ -81,6 +81,16 @@ import {
|
|
|
81
81
|
selectionTouchesLocked,
|
|
82
82
|
unlockSelection
|
|
83
83
|
} from "./lockCommands.js";
|
|
84
|
+
import {
|
|
85
|
+
canInsertEndnote,
|
|
86
|
+
canInsertFootnote,
|
|
87
|
+
insertEndnote,
|
|
88
|
+
insertFootnote,
|
|
89
|
+
openEndnote,
|
|
90
|
+
openFootnote,
|
|
91
|
+
setEndnoteBody,
|
|
92
|
+
setFootnoteBody
|
|
93
|
+
} from "./noteCommands.js";
|
|
84
94
|
import { documentNotes } from "./noteQueries.js";
|
|
85
95
|
import {
|
|
86
96
|
activeParagraphAlign,
|
|
@@ -117,6 +127,8 @@ export {
|
|
|
117
127
|
canExport,
|
|
118
128
|
canFormatText,
|
|
119
129
|
canIncreaseIndent,
|
|
130
|
+
canInsertEndnote,
|
|
131
|
+
canInsertFootnote,
|
|
120
132
|
canInsertImage,
|
|
121
133
|
canInsertTable,
|
|
122
134
|
canRunCommand,
|
|
@@ -139,6 +151,8 @@ export {
|
|
|
139
151
|
imageFilesIn,
|
|
140
152
|
increaseIndent,
|
|
141
153
|
increaseListLevel,
|
|
154
|
+
insertEndnote,
|
|
155
|
+
insertFootnote,
|
|
142
156
|
insertImage,
|
|
143
157
|
insertImageFiles,
|
|
144
158
|
insertLineBreak,
|
|
@@ -151,6 +165,8 @@ export {
|
|
|
151
165
|
isStrikeActive,
|
|
152
166
|
isUnderlineActive,
|
|
153
167
|
lockSelection,
|
|
168
|
+
openEndnote,
|
|
169
|
+
openFootnote,
|
|
154
170
|
readImageFile,
|
|
155
171
|
redo,
|
|
156
172
|
removeComment,
|
|
@@ -161,8 +177,10 @@ export {
|
|
|
161
177
|
selectionTouchesLocked,
|
|
162
178
|
setCommentBody,
|
|
163
179
|
setCommentResolved,
|
|
180
|
+
setEndnoteBody,
|
|
164
181
|
setFontFamily,
|
|
165
182
|
setFontSize,
|
|
183
|
+
setFootnoteBody,
|
|
166
184
|
setLineSpacing,
|
|
167
185
|
setLink,
|
|
168
186
|
setParagraphAlign,
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Puts a note into the document and writes what one says, exported once per kind the way
|
|
3
|
+
* `toggleBold` and `toggleItalic` are rather than as one command taking a kind. Deleting and
|
|
4
|
+
* copying a reference need no command: the edit that does either settles the note it calls
|
|
5
|
+
* (`editor/plugins/noteLifecycle`).
|
|
6
|
+
*/
|
|
7
|
+
import type { Node as PMNode } from "prosemirror-model";
|
|
8
|
+
import type { Command, EditorState } from "prosemirror-state";
|
|
9
|
+
import type { NoteKind } from "../../schema/stories";
|
|
10
|
+
/** Writes what one note of this kind says; an editing view's edit reaches the document this way */
|
|
11
|
+
export declare function noteBodyCommand(kind: NoteKind, id: string, body: PMNode): Command;
|
|
12
|
+
/**
|
|
13
|
+
* Puts a footnote reference at the end of the selection, calling a new, empty footnote, and asks
|
|
14
|
+
* for that footnote to be opened. The footnote is written in the document's footnote styles where
|
|
15
|
+
* it defines them, and in superscript where it does not.
|
|
16
|
+
*/
|
|
17
|
+
export declare const insertFootnote: Command;
|
|
18
|
+
/** Whether a footnote can go in at the end of the selection */
|
|
19
|
+
export declare function canInsertFootnote(state: EditorState): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Replaces what one footnote says with a `doc` node of `docxSchema`. It applies where an edit at
|
|
22
|
+
* the footnote's first reference would, and never to a separator, a footnote the text does not
|
|
23
|
+
* refer to, or a body saying what the footnote already says.
|
|
24
|
+
*/
|
|
25
|
+
export declare function setFootnoteBody(id: string, body: PMNode): Command;
|
|
26
|
+
/**
|
|
27
|
+
* Puts the caret just after a footnote's reference and opens the footnote for editing, which is
|
|
28
|
+
* what a press on its number runs. It applies under every mode, since opening a note is reading
|
|
29
|
+
* it; a footnote opened where the body is shut takes no typing.
|
|
30
|
+
*/
|
|
31
|
+
export declare function openFootnote(id: string): Command;
|
|
32
|
+
/**
|
|
33
|
+
* Puts an endnote reference at the end of the selection, calling a new, empty endnote, and asks
|
|
34
|
+
* for that endnote to be opened; `insertFootnote` for an endnote, which is drawn after the last
|
|
35
|
+
* paragraph of the document rather than at the foot of a page.
|
|
36
|
+
*/
|
|
37
|
+
export declare const insertEndnote: Command;
|
|
38
|
+
/** Whether an endnote can go in at the end of the selection */
|
|
39
|
+
export declare function canInsertEndnote(state: EditorState): boolean;
|
|
40
|
+
/** Replaces what one endnote says with a `doc` node of `docxSchema`, as `setFootnoteBody` does */
|
|
41
|
+
export declare function setEndnoteBody(id: string, body: PMNode): Command;
|
|
42
|
+
/**
|
|
43
|
+
* Puts the caret just after an endnote's reference and opens the endnote for editing;
|
|
44
|
+
* `openFootnote` for an endnote, which stands at the end of the document, so this is also what
|
|
45
|
+
* takes a reader to it.
|
|
46
|
+
*/
|
|
47
|
+
export declare function openEndnote(id: string): Command;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// src/editor/commands/noteCommands.ts
|
|
2
|
+
import {
|
|
3
|
+
newNoteStory,
|
|
4
|
+
nextNoteId,
|
|
5
|
+
noteNumberRunProps,
|
|
6
|
+
takenNoteIds
|
|
7
|
+
} from "../../docx/notes/newNote.js";
|
|
8
|
+
import { sameStory, setStory, storyKey, storyOf } from "../../docx/story.js";
|
|
9
|
+
import { docxSchema } from "../../schema/index.js";
|
|
10
|
+
import { guardedCommand } from "../../schema/guards.js";
|
|
11
|
+
import { isWrapperType } from "../../schema/wrappers.js";
|
|
12
|
+
import { documentOf } from "../editorDocument.js";
|
|
13
|
+
import {
|
|
14
|
+
noteReferenceAt,
|
|
15
|
+
openNoteCommand,
|
|
16
|
+
requestNote
|
|
17
|
+
} from "../plugins/noteNavigation.js";
|
|
18
|
+
function wrappersBefore(doc, pos) {
|
|
19
|
+
const $pos = doc.resolve(pos);
|
|
20
|
+
return ($pos.nodeBefore?.marks ?? $pos.marks()).filter(
|
|
21
|
+
(mark) => isWrapperType(mark.type)
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
function insertNoteTransaction(state, kind) {
|
|
25
|
+
const at = state.selection.to;
|
|
26
|
+
const $at = state.doc.resolve(at);
|
|
27
|
+
const type = docxSchema.nodes.noteReference;
|
|
28
|
+
if (!$at.parent.canReplaceWith($at.index(), $at.index(), type)) return null;
|
|
29
|
+
const { formatting, reservedNoteKeys } = documentOf(state);
|
|
30
|
+
const id = nextNoteId(takenNoteIds(state.doc, kind, reservedNoteKeys));
|
|
31
|
+
const reference = type.create({ kind, id }, null, [
|
|
32
|
+
docxSchema.marks.run.create({ rPr: noteNumberRunProps(kind, formatting) }),
|
|
33
|
+
...wrappersBefore(state.doc, at)
|
|
34
|
+
]);
|
|
35
|
+
const key = storyKey(kind, id);
|
|
36
|
+
return requestNote(
|
|
37
|
+
setStory(
|
|
38
|
+
state.tr.insert(at, reference),
|
|
39
|
+
key,
|
|
40
|
+
newNoteStory(kind, formatting)
|
|
41
|
+
),
|
|
42
|
+
key
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
function insertNoteCommand(kind) {
|
|
46
|
+
return guardedCommand((state) => insertNoteTransaction(state, kind));
|
|
47
|
+
}
|
|
48
|
+
function noteBodyTransaction(state, kind, id, body) {
|
|
49
|
+
const key = storyKey(kind, id);
|
|
50
|
+
if (body.type !== docxSchema.nodes.doc) return null;
|
|
51
|
+
if (documentOf(state).specialNotes.has(key)) return null;
|
|
52
|
+
const found = noteReferenceAt(state.doc, kind, id);
|
|
53
|
+
if (found === null) return null;
|
|
54
|
+
if (sameStory(storyOf(state.doc, key), body)) return null;
|
|
55
|
+
return setStory(state.tr, key, body).setNodeMarkup(
|
|
56
|
+
found.pos,
|
|
57
|
+
null,
|
|
58
|
+
found.node.attrs,
|
|
59
|
+
found.node.marks
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
function noteBodyCommand(kind, id, body) {
|
|
63
|
+
return guardedCommand((state) => noteBodyTransaction(state, kind, id, body));
|
|
64
|
+
}
|
|
65
|
+
var insertFootnote = insertNoteCommand("footnote");
|
|
66
|
+
function canInsertFootnote(state) {
|
|
67
|
+
return insertFootnote(state);
|
|
68
|
+
}
|
|
69
|
+
function setFootnoteBody(id, body) {
|
|
70
|
+
return noteBodyCommand("footnote", id, body);
|
|
71
|
+
}
|
|
72
|
+
function openFootnote(id) {
|
|
73
|
+
return openNoteCommand("footnote", id);
|
|
74
|
+
}
|
|
75
|
+
var insertEndnote = insertNoteCommand("endnote");
|
|
76
|
+
function canInsertEndnote(state) {
|
|
77
|
+
return insertEndnote(state);
|
|
78
|
+
}
|
|
79
|
+
function setEndnoteBody(id, body) {
|
|
80
|
+
return noteBodyCommand("endnote", id, body);
|
|
81
|
+
}
|
|
82
|
+
function openEndnote(id) {
|
|
83
|
+
return openNoteCommand("endnote", id);
|
|
84
|
+
}
|
|
85
|
+
export {
|
|
86
|
+
canInsertEndnote,
|
|
87
|
+
canInsertFootnote,
|
|
88
|
+
insertEndnote,
|
|
89
|
+
insertFootnote,
|
|
90
|
+
noteBodyCommand,
|
|
91
|
+
openEndnote,
|
|
92
|
+
openFootnote,
|
|
93
|
+
setEndnoteBody,
|
|
94
|
+
setFootnoteBody
|
|
95
|
+
};
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
/** Document-level readers for imported footnotes and endnotes. */
|
|
2
|
+
import type { Node as PMNode } from "prosemirror-model";
|
|
2
3
|
import type { EditorState } from "prosemirror-state";
|
|
3
4
|
import { DecorationSet } from "prosemirror-view";
|
|
4
|
-
import type
|
|
5
|
+
import { type StoryKey } from "../../docx/story";
|
|
6
|
+
import type { NoteKind } from "../../schema/stories";
|
|
5
7
|
export interface DocumentNote {
|
|
6
8
|
readonly kind: NoteKind;
|
|
7
9
|
readonly id: string;
|
|
@@ -9,6 +11,17 @@ export interface DocumentNote {
|
|
|
9
11
|
readonly text: string;
|
|
10
12
|
readonly referencePos: number;
|
|
11
13
|
}
|
|
14
|
+
/** One note the body refers to, as the notes drawn around the page draw it */
|
|
15
|
+
export interface NoteRow {
|
|
16
|
+
readonly key: StoryKey;
|
|
17
|
+
readonly kind: NoteKind;
|
|
18
|
+
readonly id: string;
|
|
19
|
+
readonly label: string;
|
|
20
|
+
/** The note's story, the same node for as long as it says the same thing (`schema/stories`) */
|
|
21
|
+
readonly story: PMNode;
|
|
22
|
+
/** Where the first reference to it stands, which is the place in the text it is called from */
|
|
23
|
+
readonly referencePos: number;
|
|
24
|
+
}
|
|
12
25
|
interface NoteProjection {
|
|
13
26
|
notes: readonly DocumentNote[];
|
|
14
27
|
/**
|
|
@@ -17,11 +30,17 @@ interface NoteProjection {
|
|
|
17
30
|
* says is a story on the document and not an attr of the reference.
|
|
18
31
|
*/
|
|
19
32
|
tooltips: DecorationSet;
|
|
33
|
+
/** The footnotes whose story the document holds, by story key, in first-reference order */
|
|
34
|
+
footnotes: ReadonlyMap<StoryKey, NoteRow>;
|
|
35
|
+
/** The endnotes whose story the document holds, in first-reference order */
|
|
36
|
+
endnotes: readonly NoteRow[];
|
|
37
|
+
/** Every note of both kinds by story key, which the surface over the open note reads rows from */
|
|
38
|
+
rows: ReadonlyMap<StoryKey, NoteRow>;
|
|
20
39
|
}
|
|
21
40
|
/**
|
|
22
41
|
* 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
|
|
24
|
-
* the page.
|
|
42
|
+
* per edit (`editor/plugins/documentProjection`) rather than once per render of the notes drawn
|
|
43
|
+
* around the page.
|
|
25
44
|
*/
|
|
26
45
|
export declare const noteProjection: import("../plugins/documentProjection").DocumentProjection<NoteProjection>;
|
|
27
46
|
/** The distinct notes referenced by the main document story, in first-reference order. */
|
|
@@ -8,35 +8,43 @@ function stringAttr(value) {
|
|
|
8
8
|
function noteKindOf(node) {
|
|
9
9
|
return node.attrs.kind === "endnote" ? "endnote" : "footnote";
|
|
10
10
|
}
|
|
11
|
-
function noteText(doc, kind, id) {
|
|
12
|
-
return storyText(storyOf(doc, storyKey(kind, id)));
|
|
13
|
-
}
|
|
14
11
|
function deriveNotes(doc) {
|
|
15
12
|
const notes = [];
|
|
16
13
|
const tooltips = [];
|
|
14
|
+
const footnotes = /* @__PURE__ */ new Map();
|
|
15
|
+
const endnotes = [];
|
|
16
|
+
const rows = /* @__PURE__ */ new Map();
|
|
17
17
|
const seen = /* @__PURE__ */ new Set();
|
|
18
18
|
doc.descendants((node, pos) => {
|
|
19
19
|
if (node.type.name !== "noteReference") return true;
|
|
20
20
|
const id = stringAttr(node.attrs.id);
|
|
21
21
|
if (id === null) return true;
|
|
22
22
|
const kind = noteKindOf(node);
|
|
23
|
-
const
|
|
23
|
+
const key = storyKey(kind, id);
|
|
24
|
+
const story = storyOf(doc, key);
|
|
25
|
+
const text = storyText(story);
|
|
24
26
|
if (text !== "") {
|
|
25
27
|
tooltips.push(Decoration.node(pos, pos + node.nodeSize, { title: text }));
|
|
26
28
|
}
|
|
27
|
-
const key = `${kind}:${id}`;
|
|
28
29
|
if (seen.has(key)) return true;
|
|
29
30
|
seen.add(key);
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
label:
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
31
|
+
const label = stringAttr(node.attrs.label) ?? "?";
|
|
32
|
+
notes.push({ kind, id, label, text, referencePos: pos });
|
|
33
|
+
if (story !== null) {
|
|
34
|
+
const row = { key, kind, id, label, story, referencePos: pos };
|
|
35
|
+
rows.set(key, row);
|
|
36
|
+
if (kind === "footnote") footnotes.set(key, row);
|
|
37
|
+
else endnotes.push(row);
|
|
38
|
+
}
|
|
37
39
|
return true;
|
|
38
40
|
});
|
|
39
|
-
return {
|
|
41
|
+
return {
|
|
42
|
+
notes,
|
|
43
|
+
tooltips: DecorationSet.create(doc, tooltips),
|
|
44
|
+
footnotes,
|
|
45
|
+
endnotes,
|
|
46
|
+
rows
|
|
47
|
+
};
|
|
40
48
|
}
|
|
41
49
|
var noteProjection = documentProjection(
|
|
42
50
|
"docxEditorNotes",
|