@portone/docx-editor 0.1.1 → 0.2.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 +82 -0
- package/CONTRIBUTING.md +4 -0
- package/README.md +17 -5
- package/dist/DocxEditor.d.ts +34 -10
- package/dist/DocxEditor.js +69 -24
- package/dist/core.d.ts +2 -0
- package/dist/core.js +2 -0
- package/dist/docx/commentOnlyChange.d.ts +47 -0
- package/dist/docx/commentOnlyChange.js +162 -0
- package/dist/docx/comments/constants.d.ts +11 -3
- package/dist/docx/comments/constants.js +10 -3
- package/dist/docx/comments/contentTypes.d.ts +7 -0
- package/dist/docx/comments/contentTypes.js +38 -0
- package/dist/docx/comments/grammar.d.ts +80 -0
- package/dist/docx/comments/grammar.js +213 -0
- package/dist/docx/comments/model.d.ts +2 -0
- package/dist/docx/comments/model.js +3 -0
- package/dist/docx/comments/people.d.ts +39 -0
- package/dist/docx/comments/people.js +196 -0
- package/dist/docx/comments/reading.d.ts +18 -1
- package/dist/docx/comments/reading.js +33 -20
- package/dist/docx/comments/verifying.d.ts +44 -0
- package/dist/docx/comments/verifying.js +206 -0
- package/dist/docx/comments/writing.d.ts +4 -1
- package/dist/docx/comments/writing.js +84 -77
- package/dist/docx/exportDocx.js +2 -1
- package/dist/docx/formatting/direct.js +3 -6
- package/dist/docx/headersFooters.js +8 -10
- package/dist/docx/importParagraph.js +1 -0
- package/dist/docx/notes.js +3 -5
- package/dist/docx/paraProps.js +51 -46
- package/dist/docx/propsXml.d.ts +20 -2
- package/dist/docx/propsXml.js +72 -32
- package/dist/docx/relationships.js +8 -3
- package/dist/docx/runProps.js +34 -34
- package/dist/docx/sdt.js +16 -14
- package/dist/docx/serializeBlock.js +1 -2
- package/dist/docx/serializeParagraph.d.ts +2 -1
- package/dist/docx/serializeParagraph.js +25 -14
- package/dist/docx/serializeTable.js +38 -24
- package/dist/docx/storyProjection.d.ts +36 -0
- package/dist/docx/storyProjection.js +23 -0
- package/dist/docx/tableFormatting/editing.js +49 -73
- package/dist/docx/tableTemplate.js +24 -3
- package/dist/docx/theme.d.ts +0 -6
- package/dist/docx/theme.js +0 -8
- package/dist/editor/clipboard/images.js +17 -3
- package/dist/editor/clipboard/inlineFormatting.d.ts +7 -0
- package/dist/editor/clipboard/inlineFormatting.js +1 -0
- package/dist/editor/commands/breakCommands.js +6 -2
- package/dist/editor/commands/canRunCommand.d.ts +3 -2
- package/dist/editor/commands/canRunCommand.js +2 -2
- package/dist/editor/commands/comments/editing.js +9 -10
- package/dist/editor/commands/comments/model.d.ts +9 -0
- package/dist/editor/commands/comments/reading.d.ts +7 -0
- package/dist/editor/commands/comments/reading.js +14 -0
- package/dist/editor/commands/formatting/editing.js +6 -1
- package/dist/editor/commands/historyCommands.js +14 -5
- package/dist/editor/commands/index.d.ts +7 -1
- package/dist/editor/commands/index.js +4 -0
- package/dist/editor/commands/linkCommands.js +2 -0
- package/dist/editor/commands/lockCommands.js +12 -1
- package/dist/editor/commands/tabCommands.js +4 -2
- package/dist/editor/createEditor.d.ts +13 -3
- package/dist/editor/createEditor.js +23 -15
- package/dist/editor/externalClipboard.js +166 -4
- package/dist/editor/insertImage.js +6 -2
- package/dist/editor/insertTable.js +2 -1
- package/dist/editor/paragraphEdits.d.ts +2 -0
- package/dist/editor/paragraphEdits.js +2 -0
- package/dist/editor/plugins/documentProtection.d.ts +21 -0
- package/dist/editor/plugins/documentProtection.js +44 -0
- package/dist/editor/plugins/imagePaste.js +4 -1
- package/dist/editor/plugins/keymap.d.ts +11 -1
- package/dist/editor/plugins/keymap.js +24 -4
- package/dist/editor/plugins/lockedContent.d.ts +4 -2
- package/dist/editor/plugins/lockedContent.js +2 -2
- package/dist/editor/plugins/tableContextMenu.js +2 -1
- package/dist/editor/plugins/textContextMenu.js +6 -1
- package/dist/index.d.ts +2 -0
- package/dist/numbering/listTemplate.js +32 -10
- package/dist/ooxml/element.d.ts +52 -0
- package/dist/ooxml/element.js +49 -0
- package/dist/ooxml/fragment.d.ts +53 -0
- package/dist/ooxml/fragment.js +76 -0
- package/dist/ooxml/image.js +4 -3
- package/dist/ooxml/names.d.ts +27 -0
- package/dist/ooxml/names.js +29 -0
- package/dist/ooxml/precedence.d.ts +31 -0
- package/dist/ooxml/precedence.js +51 -0
- package/dist/ooxml/xml.d.ts +31 -0
- package/dist/ooxml/xml.js +27 -0
- package/dist/page/blockKinds.d.ts +42 -0
- package/dist/page/blockKinds.js +0 -0
- package/dist/page/measureBlocks.d.ts +14 -1
- package/dist/page/measureBlocks.js +31 -11
- package/dist/page/pageDecorations.d.ts +16 -0
- package/dist/page/pageDecorations.js +1 -0
- package/dist/page/pageLayout.d.ts +8 -43
- package/dist/page/pageLayout.js +28 -51
- package/dist/page/tableMeasurements.d.ts +10 -2
- package/dist/page/tableMeasurements.js +20 -16
- package/dist/page/usePageLayout.d.ts +15 -0
- package/dist/page/usePageLayout.js +30 -2
- package/dist/schema/attrRoles.d.ts +35 -0
- package/dist/schema/attrRoles.js +106 -0
- package/dist/schema/docxSchema.d.ts +6 -0
- package/dist/schema/docxSchema.js +216 -100
- package/dist/schema/editGuard.d.ts +99 -0
- package/dist/schema/editGuard.js +43 -0
- package/dist/schema/guards.d.ts +68 -0
- package/dist/schema/guards.js +82 -0
- package/dist/schema/locks.d.ts +9 -8
- package/dist/schema/locks.js +32 -16
- package/dist/schema/preservedGuards.d.ts +30 -0
- package/dist/schema/preservedGuards.js +50 -0
- package/dist/schema/protection.d.ts +84 -0
- package/dist/schema/protection.js +171 -0
- package/dist/schema/protectionState.d.ts +20 -0
- package/dist/schema/protectionState.js +37 -0
- package/dist/schema/sourceEquality.d.ts +27 -0
- package/dist/schema/sourceEquality.js +65 -0
- package/dist/table/cellFormatting.js +5 -2
- package/dist/table/commands.d.ts +2 -2
- package/dist/table/commands.js +2 -2
- package/dist/table/index.d.ts +2 -2
- package/dist/table/merge.d.ts +2 -2
- package/dist/table/merge.js +3 -3
- package/dist/ui/CommentsPanel.d.ts +3 -3
- package/dist/ui/CommentsPanel.js +26 -18
- package/dist/ui/LinkCard.d.ts +1 -2
- package/dist/ui/LinkCard.js +3 -4
- package/dist/ui/TextMenu.js +15 -10
- package/package.json +5 -2
- package/dist/editor/plugins/bookmarkProtection.d.ts +0 -3
- package/dist/editor/plugins/bookmarkProtection.js +0 -28
- package/dist/editor/plugins/noteProtection.d.ts +0 -3
- package/dist/editor/plugins/noteProtection.js +0 -25
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The document story as the parts of the package see it: which blocks the editor models and
|
|
3
|
+
* writes back itself, and the text those blocks go back out as.
|
|
4
|
+
*
|
|
5
|
+
* Two files saying the same thing can spell it differently. A block carries the XML it arrived
|
|
6
|
+
* as on its attrs, so a table Word wrote (`w:w` before `w:type` inside a `w:tcW`) and the same
|
|
7
|
+
* table this editor wrote (the other way round) compare unequal attr for attr, and a comparison
|
|
8
|
+
* over the model would call that a body edit. Writing both stories out through the one writer
|
|
9
|
+
* takes the wording out of the comparison and leaves what the story says, because the writer
|
|
10
|
+
* reaches a fixed point on its own output: opening what it wrote and writing it again gives the
|
|
11
|
+
* same text (the test beside this file holds that over every fixture).
|
|
12
|
+
*
|
|
13
|
+
* What falls out of the comparison is of two kinds, and the test beside this file keeps them
|
|
14
|
+
* apart. One is two spellings of a single thing, which the writer settles on one of; forgiving
|
|
15
|
+
* those is the point, and the set of them is open. The other is content the writer builds afresh
|
|
16
|
+
* instead of reading, a table's grid and a drawing's extent among it, which the comparison cannot
|
|
17
|
+
* report because it can no longer see it. That second set is enumerated and meant to shrink: a
|
|
18
|
+
* case joining it is a preservation defect in the writer, not a detail of the comparison.
|
|
19
|
+
*/
|
|
20
|
+
import type { Node as PMNode } from "prosemirror-model";
|
|
21
|
+
import type { SessionStore } from "./session";
|
|
22
|
+
/** A document and the package it was opened from, which is what writing it back out takes */
|
|
23
|
+
export interface Story {
|
|
24
|
+
readonly doc: PMNode;
|
|
25
|
+
readonly session: SessionStore;
|
|
26
|
+
}
|
|
27
|
+
/** Whether this is a block the editor takes apart and writes back itself, rather than one it keeps as it came */
|
|
28
|
+
export declare function isModelledBlock(node: PMNode): boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Every top-level block as the writer puts it out, with the markup that is allowed to have
|
|
31
|
+
* changed taken away first.
|
|
32
|
+
*
|
|
33
|
+
* null where a block cannot be written back at all, which is a story no comparison can be held
|
|
34
|
+
* over: a caller judging two files answers such a story the way it answers a changed one.
|
|
35
|
+
*/
|
|
36
|
+
export declare function comparableStory(story: Story, strip: (node: PMNode) => PMNode): readonly string[] | null;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// src/docx/storyProjection.ts
|
|
2
|
+
import { DocxExportError } from "../ooxml/errors.js";
|
|
3
|
+
import { NO_EXPORT_REFS } from "./exportRefs.js";
|
|
4
|
+
import { serializeBlock } from "./serializeBlock.js";
|
|
5
|
+
function isModelledBlock(node) {
|
|
6
|
+
return node.type.isInGroup("modelled");
|
|
7
|
+
}
|
|
8
|
+
function comparableStory(story, strip) {
|
|
9
|
+
const blocks = [];
|
|
10
|
+
try {
|
|
11
|
+
story.doc.forEach((block) => {
|
|
12
|
+
blocks.push(serializeBlock(strip(block), story.session, NO_EXPORT_REFS));
|
|
13
|
+
});
|
|
14
|
+
} catch (error) {
|
|
15
|
+
if (error instanceof DocxExportError) return null;
|
|
16
|
+
throw error;
|
|
17
|
+
}
|
|
18
|
+
return blocks;
|
|
19
|
+
}
|
|
20
|
+
export {
|
|
21
|
+
comparableStory,
|
|
22
|
+
isModelledBlock
|
|
23
|
+
};
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
// src/docx/tableFormatting/editing.ts
|
|
2
|
+
import { attrPairs, elementXml } from "../../ooxml/element.js";
|
|
3
|
+
import { wName } from "../../ooxml/names.js";
|
|
4
|
+
import { setAttr } from "../../ooxml/precedence.js";
|
|
2
5
|
import { normalizeHex, wAttr } from "../../ooxml/units.js";
|
|
3
|
-
import { childByLocalName
|
|
6
|
+
import { childByLocalName } from "../../ooxml/xml.js";
|
|
4
7
|
import {
|
|
5
8
|
parseProps,
|
|
6
9
|
parsePropsXml,
|
|
@@ -41,55 +44,34 @@ var SIDE_NAMES = {
|
|
|
41
44
|
right: ["right", "end"]
|
|
42
45
|
};
|
|
43
46
|
var PRESET_BORDER_EIGHTHS = 4;
|
|
44
|
-
var THEME_COLOR_ATTRS = ["themeColor", "themeTint", "themeShade"];
|
|
45
|
-
var THEME_FILL_ATTRS = ["themeFill", "themeFillTint", "themeFillShade"];
|
|
46
47
|
var EMPTY_TC_PR = { tag: "w:tcPr", attrs: null, children: [] };
|
|
47
|
-
function attrsOf(el) {
|
|
48
|
-
return el ? Array.from(el.attributes).map((attr) => [attr.name, attr.value]) : [];
|
|
49
|
-
}
|
|
50
|
-
function withAttr(attrs, name, value, overridden = []) {
|
|
51
|
-
const kept = attrs.filter(([attr]) => !overridden.includes(localPart(attr)));
|
|
52
|
-
const at = kept.findIndex(([attr]) => localPart(attr) === name);
|
|
53
|
-
if (at === -1) return [...kept, [`w:${name}`, value]];
|
|
54
|
-
return kept.map(
|
|
55
|
-
(attr, index) => index === at ? [attr[0], value] : attr
|
|
56
|
-
);
|
|
57
|
-
}
|
|
58
|
-
function elementXml(tag, attrs) {
|
|
59
|
-
const text = attrs.map(([name, value]) => `${name}="${escapeXml(value)}"`).join(" ");
|
|
60
|
-
return `<${tag} ${text}/>`;
|
|
61
|
-
}
|
|
62
48
|
function drawsLine(side) {
|
|
63
49
|
if (!side) return false;
|
|
64
50
|
const val = wAttr(side, "val");
|
|
65
51
|
return val !== null && val !== "nil" && val !== "none";
|
|
66
52
|
}
|
|
67
|
-
function sideAttrs(current, edit, fallback = null) {
|
|
53
|
+
function sideAttrs(name, current, edit, fallback = null) {
|
|
68
54
|
if (edit.kind === "color") {
|
|
69
55
|
if (!drawsLine(current)) {
|
|
70
56
|
if (current || !fallback || fallback === "none") return null;
|
|
71
57
|
return inheritedBorderAttrs(fallback, edit.hex);
|
|
72
58
|
}
|
|
73
|
-
return
|
|
74
|
-
attrsOf(current),
|
|
75
|
-
"color",
|
|
76
|
-
edit.hex ?? "auto",
|
|
77
|
-
THEME_COLOR_ATTRS
|
|
78
|
-
);
|
|
59
|
+
return setAttr(attrPairs(current), name, "color", edit.hex ?? "auto");
|
|
79
60
|
}
|
|
80
61
|
if (edit.line === "none") {
|
|
81
|
-
return current ?
|
|
62
|
+
return current ? setAttr(attrPairs(current), name, "val", "none") : [[wName("val"), "none"]];
|
|
82
63
|
}
|
|
83
64
|
if (!current) {
|
|
84
65
|
return [
|
|
85
|
-
["
|
|
86
|
-
["
|
|
87
|
-
["
|
|
88
|
-
["
|
|
66
|
+
[wName("val"), "single"],
|
|
67
|
+
[wName("sz"), `${PRESET_BORDER_EIGHTHS}`],
|
|
68
|
+
[wName("space"), "0"],
|
|
69
|
+
[wName("color"), "auto"]
|
|
89
70
|
];
|
|
90
71
|
}
|
|
91
|
-
return
|
|
92
|
-
|
|
72
|
+
return setAttr(
|
|
73
|
+
setAttr(attrPairs(current), name, "val", "single"),
|
|
74
|
+
name,
|
|
93
75
|
"sz",
|
|
94
76
|
`${PRESET_BORDER_EIGHTHS}`
|
|
95
77
|
);
|
|
@@ -110,10 +92,10 @@ function inheritedBorderAttrs(border, hex) {
|
|
|
110
92
|
const eighths = Math.round(Number.parseFloat(widthText) * 8);
|
|
111
93
|
if (!Number.isSafeInteger(eighths) || eighths <= 0) return null;
|
|
112
94
|
return [
|
|
113
|
-
["
|
|
114
|
-
["
|
|
115
|
-
["
|
|
116
|
-
["
|
|
95
|
+
[wName("val"), BORDER_VAL_BY_CSS_STYLE[style.toLowerCase()]],
|
|
96
|
+
[wName("sz"), `${eighths}`],
|
|
97
|
+
[wName("space"), "0"],
|
|
98
|
+
[wName("color"), hex ?? "auto"]
|
|
117
99
|
];
|
|
118
100
|
}
|
|
119
101
|
function sideNames(borders, side) {
|
|
@@ -127,12 +109,12 @@ function editedBorders(current, edit, defaults = NO_BORDER_DEFAULTS) {
|
|
|
127
109
|
const borders = current === null ? null : parsePropsXml(current);
|
|
128
110
|
if (!props || current !== null && !borders) return null;
|
|
129
111
|
const sides = edit.kind === "line" ? edit.sides : ALL_CELL_SIDES;
|
|
130
|
-
const
|
|
112
|
+
const edited = sides.reduce((kept, side) => {
|
|
131
113
|
const { write, drop } = sideNames(borders, side);
|
|
132
114
|
const currentSide = borders ? childByLocalName(borders, write) : null;
|
|
133
|
-
const attrs = sideAttrs(currentSide, edit, defaults[side]);
|
|
115
|
+
const attrs = sideAttrs(write, currentSide, edit, defaults[side]);
|
|
134
116
|
if (!attrs) return kept;
|
|
135
|
-
const tag = currentSide?.nodeName ??
|
|
117
|
+
const tag = currentSide?.nodeName ?? wName(write);
|
|
136
118
|
const written = setPropsChild(
|
|
137
119
|
kept,
|
|
138
120
|
write,
|
|
@@ -140,8 +122,8 @@ function editedBorders(current, edit, defaults = NO_BORDER_DEFAULTS) {
|
|
|
140
122
|
TC_BORDERS_ORDER
|
|
141
123
|
);
|
|
142
124
|
return drop === null ? written : setPropsChild(written, drop, null, TC_BORDERS_ORDER);
|
|
143
|
-
}, props
|
|
144
|
-
const xml = renderProps(
|
|
125
|
+
}, props);
|
|
126
|
+
const xml = renderProps(edited);
|
|
145
127
|
return { xml: xml === "" ? null : xml };
|
|
146
128
|
}
|
|
147
129
|
function hasPattern(shd) {
|
|
@@ -149,23 +131,20 @@ function hasPattern(shd) {
|
|
|
149
131
|
return val !== null && val !== "clear" && val !== "nil";
|
|
150
132
|
}
|
|
151
133
|
function editedShading(current, fill) {
|
|
152
|
-
const tag = current?.nodeName ?? "
|
|
134
|
+
const tag = current?.nodeName ?? wName("shd");
|
|
153
135
|
if (fill === null) {
|
|
154
136
|
if (!hasPattern(current)) return null;
|
|
155
|
-
return elementXml(
|
|
156
|
-
tag,
|
|
157
|
-
withAttr(attrsOf(current), "fill", "auto", THEME_FILL_ATTRS)
|
|
158
|
-
);
|
|
137
|
+
return elementXml(tag, setAttr(attrPairs(current), "shd", "fill", "auto"));
|
|
159
138
|
}
|
|
160
139
|
if (!current) {
|
|
161
140
|
return elementXml(tag, [
|
|
162
|
-
["
|
|
163
|
-
["
|
|
164
|
-
["
|
|
141
|
+
[wName("val"), "clear"],
|
|
142
|
+
[wName("color"), "auto"],
|
|
143
|
+
[wName("fill"), fill]
|
|
165
144
|
]);
|
|
166
145
|
}
|
|
167
|
-
const painting = hasPattern(current) ?
|
|
168
|
-
return elementXml(tag,
|
|
146
|
+
const painting = hasPattern(current) ? attrPairs(current) : setAttr(attrPairs(current), "shd", "val", "clear");
|
|
147
|
+
return elementXml(tag, setAttr(painting, "shd", "fill", fill));
|
|
169
148
|
}
|
|
170
149
|
function bordersChange(props, edit, defaults) {
|
|
171
150
|
const current = propsChild(props.children, "tcBorders")?.xml ?? null;
|
|
@@ -191,7 +170,7 @@ function paddingChange(props, values) {
|
|
|
191
170
|
const margins = current === null ? null : parsePropsXml(current);
|
|
192
171
|
const parsed = current === null ? { tag: "w:tcMar", attrs: null, children: [] } : parseProps(current);
|
|
193
172
|
if (!parsed || current !== null && !margins) return null;
|
|
194
|
-
let
|
|
173
|
+
let edited = parsed;
|
|
195
174
|
let wrote = false;
|
|
196
175
|
for (const side of ALL_CELL_SIDES) {
|
|
197
176
|
const points = values[side];
|
|
@@ -202,21 +181,22 @@ function paddingChange(props, values) {
|
|
|
202
181
|
}
|
|
203
182
|
const existing = margins ? MARGIN_SIDE_NAMES[side].map((name2) => childByLocalName(margins, name2)).find((element) => element !== null) ?? null : null;
|
|
204
183
|
const name = existing?.localName ?? side;
|
|
205
|
-
const attrs =
|
|
206
|
-
|
|
184
|
+
const attrs = setAttr(
|
|
185
|
+
setAttr(attrPairs(existing), name, "w", `${twips}`),
|
|
186
|
+
name,
|
|
207
187
|
"type",
|
|
208
188
|
"dxa"
|
|
209
189
|
);
|
|
210
|
-
|
|
211
|
-
|
|
190
|
+
edited = setPropsChild(
|
|
191
|
+
edited,
|
|
212
192
|
name,
|
|
213
|
-
elementXml(existing?.nodeName ??
|
|
193
|
+
elementXml(existing?.nodeName ?? wName(side), attrs),
|
|
214
194
|
TC_MAR_ORDER
|
|
215
195
|
);
|
|
216
196
|
wrote = true;
|
|
217
197
|
}
|
|
218
198
|
if (!wrote) return null;
|
|
219
|
-
return { name: "tcMar", xml: renderProps(
|
|
199
|
+
return { name: "tcMar", xml: renderProps(edited) };
|
|
220
200
|
}
|
|
221
201
|
function childChange(edit, props, defaults) {
|
|
222
202
|
switch (edit.kind) {
|
|
@@ -250,8 +230,8 @@ function childChange(edit, props, defaults) {
|
|
|
250
230
|
return {
|
|
251
231
|
name: "vAlign",
|
|
252
232
|
xml: elementXml(
|
|
253
|
-
element?.nodeName ?? "
|
|
254
|
-
|
|
233
|
+
element?.nodeName ?? wName("vAlign"),
|
|
234
|
+
setAttr(attrPairs(element), "vAlign", "val", edit.align)
|
|
255
235
|
)
|
|
256
236
|
};
|
|
257
237
|
}
|
|
@@ -264,13 +244,9 @@ function editCellProps(tcPr, edit, defaults = NO_BORDER_DEFAULTS, margins = NO_C
|
|
|
264
244
|
if (!props) return null;
|
|
265
245
|
const change = childChange(edit, props, defaults);
|
|
266
246
|
if (!change) return null;
|
|
267
|
-
const
|
|
268
|
-
props.
|
|
269
|
-
change.name,
|
|
270
|
-
change.xml,
|
|
271
|
-
TC_PR_ORDER
|
|
247
|
+
const rendered = renderProps(
|
|
248
|
+
setPropsChild(props, change.name, change.xml, TC_PR_ORDER)
|
|
272
249
|
);
|
|
273
|
-
const rendered = renderProps({ ...props, children });
|
|
274
250
|
const next = rendered === "" ? null : rendered;
|
|
275
251
|
if (next === tcPr) return null;
|
|
276
252
|
return { tcPr: next, format: readCellProps(next, defaults, margins) };
|
|
@@ -294,17 +270,17 @@ function editRowHeight(trPr, heightPt) {
|
|
|
294
270
|
const element = current === null ? null : parsePropsXml(current);
|
|
295
271
|
if (current !== null && !element) return null;
|
|
296
272
|
const writtenRule = element ? wAttr(element, "hRule") : null;
|
|
297
|
-
const attrs =
|
|
298
|
-
const nextAttrs =
|
|
273
|
+
const attrs = setAttr(attrPairs(element), "trHeight", "val", `${twips}`);
|
|
274
|
+
const nextAttrs = setAttr(
|
|
299
275
|
attrs,
|
|
276
|
+
"trHeight",
|
|
300
277
|
"hRule",
|
|
301
278
|
writtenRule === "exact" ? "exact" : "atLeast"
|
|
302
279
|
);
|
|
303
|
-
const child = elementXml(element?.nodeName ?? "
|
|
304
|
-
const rendered = renderProps(
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
});
|
|
280
|
+
const child = elementXml(element?.nodeName ?? wName("trHeight"), nextAttrs);
|
|
281
|
+
const rendered = renderProps(
|
|
282
|
+
setPropsChild(props, "trHeight", child, TR_PR_ORDER)
|
|
283
|
+
);
|
|
308
284
|
if (rendered === trPr) return null;
|
|
309
285
|
const parsed = parsePropsXml(rendered);
|
|
310
286
|
const format = parsed ? readRowFormat(parsed) : null;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
// src/docx/tableTemplate.ts
|
|
2
|
+
import { elementXml } from "../ooxml/element.js";
|
|
3
|
+
import { wName } from "../ooxml/names.js";
|
|
2
4
|
import { docxSchema } from "../schema/index.js";
|
|
3
5
|
import { A4_PORTRAIT, bodyWidth } from "./pageGeometry.js";
|
|
4
6
|
import { parsePropsXml } from "./propsXml.js";
|
|
@@ -25,14 +27,33 @@ var BORDER_SIDES = [
|
|
|
25
27
|
"insideV"
|
|
26
28
|
];
|
|
27
29
|
function borderXml(side) {
|
|
28
|
-
return
|
|
30
|
+
return elementXml(wName(side), [
|
|
31
|
+
[wName("val"), "single"],
|
|
32
|
+
[wName("sz"), `${BORDER_EIGHTHS}`],
|
|
33
|
+
[wName("space"), "0"],
|
|
34
|
+
[wName("color"), "000000"]
|
|
35
|
+
]);
|
|
29
36
|
}
|
|
30
37
|
function tablePropsXml(width) {
|
|
31
38
|
const borders = BORDER_SIDES.map(borderXml).join("");
|
|
32
|
-
return
|
|
39
|
+
return elementXml(
|
|
40
|
+
wName("tblPr"),
|
|
41
|
+
[],
|
|
42
|
+
[
|
|
43
|
+
widthXml("tblW", width),
|
|
44
|
+
elementXml(wName("tblBorders"), [], [borders]),
|
|
45
|
+
elementXml(wName("tblLayout"), [[wName("type"), "fixed"]])
|
|
46
|
+
]
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
function widthXml(name, width) {
|
|
50
|
+
return elementXml(wName(name), [
|
|
51
|
+
[wName("w"), `${width}`],
|
|
52
|
+
[wName("type"), "dxa"]
|
|
53
|
+
]);
|
|
33
54
|
}
|
|
34
55
|
function cellPropsXml(width) {
|
|
35
|
-
return
|
|
56
|
+
return elementXml(wName("tcPr"), [], [widthXml("tcW", width)]);
|
|
36
57
|
}
|
|
37
58
|
function evenGridCols(cols, total) {
|
|
38
59
|
const base = Math.floor(total / cols);
|
package/dist/docx/theme.d.ts
CHANGED
|
@@ -21,12 +21,6 @@ export interface ThemeFonts {
|
|
|
21
21
|
}
|
|
22
22
|
/** What applies to a document with no theme part, or one whose theme names no font */
|
|
23
23
|
export declare const NO_THEME_FONTS: ThemeFonts;
|
|
24
|
-
/**
|
|
25
|
-
* The theme attribute paired with each font slot.
|
|
26
|
-
* A theme reference and a name in the same slot cannot both apply, so the name wins,
|
|
27
|
-
* the way Word reads them.
|
|
28
|
-
*/
|
|
29
|
-
export declare const THEME_ATTRS: Record<string, readonly string[]>;
|
|
30
24
|
/** The font one theme reference stands for. null for a reference we cannot resolve */
|
|
31
25
|
export declare function themeFontName(fonts: ThemeFonts, value: string | null): string | null;
|
|
32
26
|
/**
|
package/dist/docx/theme.js
CHANGED
|
@@ -9,13 +9,6 @@ var NO_THEME_FONTS = {
|
|
|
9
9
|
major: NO_FONT_SCHEME,
|
|
10
10
|
minor: NO_FONT_SCHEME
|
|
11
11
|
};
|
|
12
|
-
var THEME_ATTRS = {
|
|
13
|
-
ascii: ["asciiTheme"],
|
|
14
|
-
hAnsi: ["hAnsiTheme"],
|
|
15
|
-
eastAsia: ["eastAsiaTheme"],
|
|
16
|
-
// This is the slot whose capitalization varies from document to document
|
|
17
|
-
cs: ["cstheme", "csTheme"]
|
|
18
|
-
};
|
|
19
12
|
var THEME_SLOTS = {
|
|
20
13
|
majorascii: { scheme: "major", script: "latin" },
|
|
21
14
|
majorhansi: { scheme: "major", script: "latin" },
|
|
@@ -55,7 +48,6 @@ function readThemeFonts(theme) {
|
|
|
55
48
|
}
|
|
56
49
|
export {
|
|
57
50
|
NO_THEME_FONTS,
|
|
58
|
-
THEME_ATTRS,
|
|
59
51
|
readThemeFonts,
|
|
60
52
|
themeFontName
|
|
61
53
|
};
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
// src/editor/clipboard/images.ts
|
|
2
2
|
import { MAX_IMAGE_BYTES } from "../../docx/media.js";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
isImageMime,
|
|
5
|
+
pxToEmu,
|
|
6
|
+
toImageExtent,
|
|
7
|
+
toImageSrc
|
|
8
|
+
} from "../../ooxml/image.js";
|
|
4
9
|
import { editorClassNames } from "../../styles/classNames.js";
|
|
5
10
|
import {
|
|
6
11
|
boundedImageSrc,
|
|
@@ -9,8 +14,7 @@ import {
|
|
|
9
14
|
readImageSource
|
|
10
15
|
} from "../imageFiles.js";
|
|
11
16
|
var PASTED_IMAGE_ATTRIBUTE = "data-docx-pasted-image";
|
|
12
|
-
function
|
|
13
|
-
if (!element.classList.contains(editorClassNames.image)) return null;
|
|
17
|
+
function declaredExtent(element) {
|
|
14
18
|
try {
|
|
15
19
|
return toImageExtent(
|
|
16
20
|
JSON.parse(element.getAttribute("data-extent") ?? "null")
|
|
@@ -19,6 +23,16 @@ function extentAttribute(element) {
|
|
|
19
23
|
return null;
|
|
20
24
|
}
|
|
21
25
|
}
|
|
26
|
+
function renderedExtent(element) {
|
|
27
|
+
const width = Number.parseInt(element.getAttribute("width") ?? "", 10);
|
|
28
|
+
const height = Number.parseInt(element.getAttribute("height") ?? "", 10);
|
|
29
|
+
if (!Number.isFinite(width) || !Number.isFinite(height)) return null;
|
|
30
|
+
return toImageExtent({ cx: pxToEmu(width), cy: pxToEmu(height) });
|
|
31
|
+
}
|
|
32
|
+
function extentAttribute(element) {
|
|
33
|
+
if (!element.classList.contains(editorClassNames.image)) return null;
|
|
34
|
+
return declaredExtent(element) ?? renderedExtent(element);
|
|
35
|
+
}
|
|
22
36
|
function fetchableUrl(source) {
|
|
23
37
|
try {
|
|
24
38
|
const url = new URL(source);
|
|
@@ -15,6 +15,13 @@ export interface InlineContext {
|
|
|
15
15
|
preserveWhitespace: boolean;
|
|
16
16
|
}
|
|
17
17
|
export type InlineElementReader = (target: PMNode[], element: HTMLElement, context: InlineContext) => boolean;
|
|
18
|
+
/**
|
|
19
|
+
* The address a link may carry, whichever way it travels.
|
|
20
|
+
*
|
|
21
|
+
* A copy writes one out and a paste reads one in, and an address this rule turns down is one
|
|
22
|
+
* neither end should act on, so both ask here.
|
|
23
|
+
*/
|
|
24
|
+
export declare function safeHref(value: string | null): string | null;
|
|
18
25
|
export declare function contextFor(parent: InlineContext, element: HTMLElement, readElementStyle?: boolean): InlineContext;
|
|
19
26
|
export declare function withInlineStyle(context: InlineContext, style: InlineStyle): InlineContext;
|
|
20
27
|
export declare function marksFor(context: InlineContext): Mark[];
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
// src/editor/commands/breakCommands.ts
|
|
2
2
|
import { docxSchema } from "../../schema/index.js";
|
|
3
|
-
import {
|
|
3
|
+
import { editShut, selectionIntents } from "../../schema/guards.js";
|
|
4
4
|
function insertBreak(brAttrs) {
|
|
5
5
|
return (state, dispatch) => {
|
|
6
|
-
if (
|
|
6
|
+
if (selectionIntents(state.selection, "replace").some(
|
|
7
|
+
(intent) => editShut(state, intent)
|
|
8
|
+
)) {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
7
11
|
if (dispatch) {
|
|
8
12
|
const br = docxSchema.nodes.hardBreak.create({ brAttrs });
|
|
9
13
|
dispatch(state.tr.replaceSelectionWith(br).scrollIntoView());
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Dry-runs external commands and checks every produced transaction against
|
|
3
|
-
* dispatching them or triggering the locked-content plugin's side
|
|
2
|
+
* Dry-runs external commands and checks every produced transaction against every guard
|
|
3
|
+
* (`schema/guards`) without dispatching them or triggering the locked-content plugin's side
|
|
4
|
+
* effects.
|
|
4
5
|
*/
|
|
5
6
|
import type { Command, EditorState } from "prosemirror-state";
|
|
6
7
|
export declare function canRunCommand(command: Command, state: EditorState): boolean;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// src/editor/commands/canRunCommand.ts
|
|
2
|
-
import { transactionAllowed } from "../../schema/
|
|
2
|
+
import { transactionAllowed } from "../../schema/guards.js";
|
|
3
3
|
function canRunCommand(command, state) {
|
|
4
4
|
const built = [];
|
|
5
5
|
if (!command(state, (tr) => built.push(tr))) return false;
|
|
6
|
-
return built.every((tr) => transactionAllowed(tr, state
|
|
6
|
+
return built.every((tr) => transactionAllowed(tr, state));
|
|
7
7
|
}
|
|
8
8
|
export {
|
|
9
9
|
canRunCommand
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
} from "prosemirror-state";
|
|
5
5
|
import { commentParaId } from "../../../docx/comments.js";
|
|
6
6
|
import { docxSchema } from "../../../schema/index.js";
|
|
7
|
-
import { transactionAllowed } from "../../../schema/
|
|
7
|
+
import { transactionAllowed } from "../../../schema/guards.js";
|
|
8
8
|
import {
|
|
9
9
|
reservedCommentIds,
|
|
10
10
|
reservedCommentParaIds
|
|
@@ -93,6 +93,7 @@ function addCommentTransaction(state, comment) {
|
|
|
93
93
|
id,
|
|
94
94
|
referenceXml: null,
|
|
95
95
|
author: comment.author,
|
|
96
|
+
authorId: comment.authorId ?? null,
|
|
96
97
|
initials: comment.initials ?? null,
|
|
97
98
|
date,
|
|
98
99
|
text: comment.text,
|
|
@@ -113,7 +114,7 @@ function addCommentTransaction(state, comment) {
|
|
|
113
114
|
startMarks
|
|
114
115
|
);
|
|
115
116
|
const transaction = state.tr.insert(to, end).insert(to + 1, reference).insert(from, start);
|
|
116
|
-
return transactionAllowed(transaction, state
|
|
117
|
+
return transactionAllowed(transaction, state) ? transaction : null;
|
|
117
118
|
}
|
|
118
119
|
function canAddComment(state) {
|
|
119
120
|
return addCommentTransaction(state, {
|
|
@@ -147,7 +148,7 @@ function updateComment(id, text) {
|
|
|
147
148
|
}
|
|
148
149
|
return true;
|
|
149
150
|
});
|
|
150
|
-
if (!changed || !transactionAllowed(transaction, state
|
|
151
|
+
if (!changed || !transactionAllowed(transaction, state)) return false;
|
|
151
152
|
dispatch?.(transaction);
|
|
152
153
|
return true;
|
|
153
154
|
};
|
|
@@ -167,7 +168,7 @@ function updateReference(id, change) {
|
|
|
167
168
|
}
|
|
168
169
|
return false;
|
|
169
170
|
});
|
|
170
|
-
if (!changed || !transactionAllowed(transaction, state
|
|
171
|
+
if (!changed || !transactionAllowed(transaction, state)) return false;
|
|
171
172
|
dispatch?.(transaction);
|
|
172
173
|
return true;
|
|
173
174
|
};
|
|
@@ -177,11 +178,9 @@ function setCommentResolved(id, resolved) {
|
|
|
177
178
|
if (node.attrs.resolved === resolved) return null;
|
|
178
179
|
return {
|
|
179
180
|
...node.attrs,
|
|
180
|
-
paraId: node.attrs.threadImported === true && node.attrs.extensionXml === null ? nextCommentParaId(state, `comment-${id}`) : node.attrs.paraId,
|
|
181
181
|
resolved,
|
|
182
182
|
extensionXml: null,
|
|
183
|
-
threadImported: false
|
|
184
|
-
imported: node.attrs.commentXml !== null && node.attrs.extensionXml === null ? false : node.attrs.imported
|
|
183
|
+
threadImported: false
|
|
185
184
|
};
|
|
186
185
|
})(state, dispatch);
|
|
187
186
|
}
|
|
@@ -191,7 +190,7 @@ function addCommentReply(id, reply) {
|
|
|
191
190
|
const replyId = nextCommentId(state);
|
|
192
191
|
const date = reply.date ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
193
192
|
return updateReference(id, (node) => {
|
|
194
|
-
const parentParaId =
|
|
193
|
+
const parentParaId = stringAttr(node.attrs.paraId) ?? nextCommentParaId(state, `comment-${id}`);
|
|
195
194
|
const paraId = nextCommentParaId(state, `comment-${replyId}-${date}`, [
|
|
196
195
|
parentParaId
|
|
197
196
|
]);
|
|
@@ -201,12 +200,12 @@ function addCommentReply(id, reply) {
|
|
|
201
200
|
resolved: false,
|
|
202
201
|
extensionXml: null,
|
|
203
202
|
threadImported: false,
|
|
204
|
-
imported: node.attrs.commentXml !== null && node.attrs.extensionXml === null ? false : node.attrs.imported,
|
|
205
203
|
replies: [
|
|
206
204
|
...repliesAttr(node.attrs.replies),
|
|
207
205
|
{
|
|
208
206
|
id: replyId,
|
|
209
207
|
author: reply.author,
|
|
208
|
+
authorId: reply.authorId ?? null,
|
|
210
209
|
initials: reply.initials ?? null,
|
|
211
210
|
date,
|
|
212
211
|
text: reply.text,
|
|
@@ -275,7 +274,7 @@ function removeComment(id) {
|
|
|
275
274
|
for (const marker of positions.sort((a, b) => b.pos - a.pos)) {
|
|
276
275
|
transaction.delete(marker.pos, marker.pos + marker.size);
|
|
277
276
|
}
|
|
278
|
-
if (!transactionAllowed(transaction, state
|
|
277
|
+
if (!transactionAllowed(transaction, state)) return false;
|
|
279
278
|
dispatch?.(transaction);
|
|
280
279
|
return true;
|
|
281
280
|
};
|
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
/** Public comment values and shared attribute readers. */
|
|
2
2
|
export interface CommentAuthor {
|
|
3
|
+
/**
|
|
4
|
+
* The identity behind the name, an opaque string the host application chooses. Recorded in the
|
|
5
|
+
* document's people part, and what decides whose comment a comment is (`schema/protection`).
|
|
6
|
+
*/
|
|
7
|
+
id: string;
|
|
3
8
|
name: string;
|
|
4
9
|
initials?: string;
|
|
5
10
|
}
|
|
6
11
|
export interface NewComment {
|
|
7
12
|
text: string;
|
|
8
13
|
author: string;
|
|
14
|
+
/** The identity behind `author`. A comment written without one belongs to nobody in particular */
|
|
15
|
+
authorId?: string;
|
|
9
16
|
initials?: string;
|
|
10
17
|
/** ISO 8601 timestamp. The current time is used when omitted. */
|
|
11
18
|
date?: string;
|
|
@@ -13,6 +20,7 @@ export interface NewComment {
|
|
|
13
20
|
export interface DocumentComment {
|
|
14
21
|
id: string;
|
|
15
22
|
author: string | null;
|
|
23
|
+
authorId: string | null;
|
|
16
24
|
initials: string | null;
|
|
17
25
|
date: string | null;
|
|
18
26
|
text: string;
|
|
@@ -25,6 +33,7 @@ export interface DocumentComment {
|
|
|
25
33
|
export interface DocumentCommentReply {
|
|
26
34
|
id: string;
|
|
27
35
|
author: string | null;
|
|
36
|
+
authorId: string | null;
|
|
28
37
|
initials: string | null;
|
|
29
38
|
date: string | null;
|
|
30
39
|
text: string;
|
|
@@ -6,3 +6,10 @@ export declare function stringAttr(value: unknown): string | null;
|
|
|
6
6
|
export declare function repliesAttr(value: unknown): readonly CommentReplyData[];
|
|
7
7
|
/** Comments in document order, including point comments that have no explicit range. */
|
|
8
8
|
export declare function documentComments(state: EditorState): readonly DocumentComment[];
|
|
9
|
+
/**
|
|
10
|
+
* Whether the body of this comment, or of one of its replies, may be edited or deleted here: the
|
|
11
|
+
* protection lets comments be edited at all, and the body is one of one's own to edit
|
|
12
|
+
* (`schema/protection`). Replying and settling a thread are not governed by this; a comment command
|
|
13
|
+
* asked without `dispatch` answers for those.
|
|
14
|
+
*/
|
|
15
|
+
export declare function canEditComment(state: EditorState, commentId: string, replyId?: string | null): boolean;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
// src/editor/commands/comments/reading.ts
|
|
2
|
+
import { commentOwned } from "../../../schema/protection.js";
|
|
3
|
+
import { protectionOf } from "../../../schema/protectionState.js";
|
|
2
4
|
function stringAttr(value) {
|
|
3
5
|
return typeof value === "string" ? value : null;
|
|
4
6
|
}
|
|
@@ -34,6 +36,7 @@ function documentComments(state) {
|
|
|
34
36
|
return {
|
|
35
37
|
id,
|
|
36
38
|
author: stringAttr(node.attrs.author),
|
|
39
|
+
authorId: stringAttr(node.attrs.authorId),
|
|
37
40
|
initials: stringAttr(node.attrs.initials),
|
|
38
41
|
date: stringAttr(node.attrs.date),
|
|
39
42
|
text: stringAttr(node.attrs.text) ?? "",
|
|
@@ -44,6 +47,7 @@ function documentComments(state) {
|
|
|
44
47
|
replies: repliesAttr(node.attrs.replies).map((reply) => ({
|
|
45
48
|
id: reply.id,
|
|
46
49
|
author: reply.author,
|
|
50
|
+
authorId: reply.authorId,
|
|
47
51
|
initials: reply.initials,
|
|
48
52
|
date: reply.date,
|
|
49
53
|
text: reply.text
|
|
@@ -51,7 +55,17 @@ function documentComments(state) {
|
|
|
51
55
|
};
|
|
52
56
|
});
|
|
53
57
|
}
|
|
58
|
+
function canEditComment(state, commentId, replyId = null) {
|
|
59
|
+
const rules = protectionOf(state);
|
|
60
|
+
if (rules.protection === "readOnly") return false;
|
|
61
|
+
const comment = documentComments(state).find(
|
|
62
|
+
(entry) => entry.id === commentId
|
|
63
|
+
);
|
|
64
|
+
const body = replyId === null ? comment : comment?.replies.find((reply) => reply.id === replyId);
|
|
65
|
+
return body !== void 0 && commentOwned(rules, body.authorId);
|
|
66
|
+
}
|
|
54
67
|
export {
|
|
68
|
+
canEditComment,
|
|
55
69
|
documentComments,
|
|
56
70
|
repliesAttr,
|
|
57
71
|
stringAttr
|