@portone/docx-editor 0.6.0 → 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 +24 -0
- package/dist/DocxEditor.js +84 -26
- 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/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 +21 -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/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 +21 -6
- package/dist/styles/classNames.js +21 -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 +89 -32
- 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 +1 -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) {
|
|
@@ -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",
|
|
@@ -6,6 +6,7 @@ import { keymap } from "prosemirror-keymap";
|
|
|
6
6
|
import { EditorState } from "prosemirror-state";
|
|
7
7
|
import { tableEditing } from "prosemirror-tables";
|
|
8
8
|
import { EditorView } from "prosemirror-view";
|
|
9
|
+
import { withNoteLabels } from "../docx/notes/numbering.js";
|
|
9
10
|
import { pageDecorations } from "../page/pageDecorations.js";
|
|
10
11
|
import { pageGeometryStyle, pagePixels } from "../page/pageLayout.js";
|
|
11
12
|
import { editsShut } from "../schema/protectionState.js";
|
|
@@ -27,6 +28,7 @@ import { columnResize } from "./plugins/columnResize.js";
|
|
|
27
28
|
import { commentComposer } from "./plugins/commentComposer.js";
|
|
28
29
|
import { commentDecorations } from "./plugins/commentDecorations.js";
|
|
29
30
|
import { commentRestoration } from "./plugins/commentRestoration.js";
|
|
31
|
+
import { compositionSelection } from "./plugins/compositionSelection.js";
|
|
30
32
|
import {
|
|
31
33
|
displayDerivation,
|
|
32
34
|
withDerivedDisplay
|
|
@@ -37,6 +39,9 @@ import { docxKeymap, historyKeys } from "./plugins/keymap.js";
|
|
|
37
39
|
import { linkPanel } from "./plugins/linkPanel.js";
|
|
38
40
|
import { listInputRules } from "./plugins/listInputRules.js";
|
|
39
41
|
import { lockedContent } from "./plugins/lockedContent.js";
|
|
42
|
+
import { noteLifecycle } from "./plugins/noteLifecycle.js";
|
|
43
|
+
import { noteNavigation } from "./plugins/noteNavigation.js";
|
|
44
|
+
import { noteNumbering } from "./plugins/noteNumbering.js";
|
|
40
45
|
import { numberingMarkers } from "./plugins/numberingDecorations.js";
|
|
41
46
|
import { rowResize } from "./plugins/rowResize.js";
|
|
42
47
|
import { tabCaret } from "./plugins/tabCaret.js";
|
|
@@ -58,8 +63,13 @@ function createEditorState(doc, options = {}) {
|
|
|
58
63
|
} = options;
|
|
59
64
|
return EditorState.create({
|
|
60
65
|
// The values the document arrived with were worked out against whatever opened it; the
|
|
61
|
-
// state's are worked out against its own snapshot, tables' shared lines
|
|
62
|
-
|
|
66
|
+
// state's are worked out against its own snapshot, tables' shared lines and note labels
|
|
67
|
+
// included
|
|
68
|
+
doc: withNoteLabels(
|
|
69
|
+
withDerivedDisplay(doc, document),
|
|
70
|
+
document.noteNumbering,
|
|
71
|
+
document.specialNotes
|
|
72
|
+
),
|
|
63
73
|
plugins: [
|
|
64
74
|
// Consumer plugins lead the array. ProseMirror walks the plugins in order and takes the
|
|
65
75
|
// first answer for a keypress, a paste, a drop or any other DOM event, so this is the
|
|
@@ -70,9 +80,12 @@ function createEditorState(doc, options = {}) {
|
|
|
70
80
|
editorDocument(document),
|
|
71
81
|
// Refuses every edit no guard in `schema/guards` lets through, whoever asked for it. It is
|
|
72
82
|
// not optional: a document that locked a part of itself stays locked in every consumer, and
|
|
73
|
-
// the preserved bookmark markers
|
|
83
|
+
// the preserved bookmark markers stay where the file put them.
|
|
74
84
|
lockedContent(),
|
|
75
85
|
documentProtection({ protection, author, editableComments }),
|
|
86
|
+
// Ahead of the keys and the clipboard: the plugin registered first sees a DOM event first,
|
|
87
|
+
// and this one hands every composition on
|
|
88
|
+
compositionSelection(),
|
|
76
89
|
history(),
|
|
77
90
|
keymap(docxKeymap),
|
|
78
91
|
historyKeys(),
|
|
@@ -100,8 +113,13 @@ function createEditorState(doc, options = {}) {
|
|
|
100
113
|
// A comment outlives the text it was written for: an edit that sweeps its reference away has
|
|
101
114
|
// it put back where the deletion left, detached from the page (`plugins/commentRestoration`)
|
|
102
115
|
commentRestoration(),
|
|
116
|
+
// A note goes with the last reference to it, and a copied reference gets a note of its own.
|
|
117
|
+
// It stands ahead of the numbering, which then labels what it settled
|
|
118
|
+
noteLifecycle(),
|
|
119
|
+
noteNumbering(),
|
|
103
120
|
// What the notes under the page are, worked out from the document the same way
|
|
104
121
|
noteProjection.plugin,
|
|
122
|
+
noteNavigation(),
|
|
105
123
|
// Adjacent text tabs still need separate DOM ranges for layout and pointer selection.
|
|
106
124
|
tabDecorations(),
|
|
107
125
|
tabPointer(),
|
|
@@ -36,6 +36,9 @@ export declare function documentGeometry(state: EditorState): PageGeometry;
|
|
|
36
36
|
* section is where the document says it is, and the snapshot holds the first section's paper
|
|
37
37
|
* alone. Reading it walks every block, so a caller drawing a whole document asks once and looks
|
|
38
38
|
* its positions up (`page/pageLayout`) rather than calling this per block.
|
|
39
|
+
*
|
|
40
|
+
* A side story writes down no section, so its own blocks say nothing about the paper: what it is
|
|
41
|
+
* drawn on is the paper of the place it is called from, which its snapshot carries.
|
|
39
42
|
*/
|
|
40
43
|
export declare function sectionGeometryAt(state: EditorState, pos: number): PageGeometry;
|
|
41
44
|
/** The document-wide interval between automatic tab stops, in points. */
|
|
@@ -19,7 +19,8 @@ function documentGeometry(state) {
|
|
|
19
19
|
return documentOf(state).geometry;
|
|
20
20
|
}
|
|
21
21
|
function sectionGeometryAt(state, pos) {
|
|
22
|
-
|
|
22
|
+
const document = documentOf(state);
|
|
23
|
+
return document.sideStory ? document.geometry : sectionAt(state.doc, pos).props.geometry;
|
|
23
24
|
}
|
|
24
25
|
function documentDefaultTabStopPt(state) {
|
|
25
26
|
return documentOf(state).defaultTabStopPt;
|