@portone/docx-editor 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +65 -0
- package/CONTRIBUTING.md +10 -2
- package/README.md +1 -1
- package/dist/DocxEditor.js +3 -3
- package/dist/docx/formatting/styles.d.ts +0 -2
- package/dist/docx/formatting/styles.js +2 -15
- package/dist/docx/identities.js +17 -26
- package/dist/docx/importParagraph.d.ts +6 -5
- package/dist/docx/importParagraph.js +15 -62
- package/dist/docx/importPolicy.js +10 -5
- package/dist/docx/serializeParagraph.d.ts +5 -4
- package/dist/docx/serializeParagraph.js +22 -58
- package/dist/docx/tableTemplate.d.ts +25 -0
- package/dist/docx/tableTemplate.js +30 -16
- package/dist/docx/wrappers.d.ts +67 -0
- package/dist/docx/wrappers.js +117 -0
- package/dist/editor/clipboard/blockReaders.d.ts +74 -0
- package/dist/editor/clipboard/blockReaders.js +193 -0
- package/dist/editor/clipboard/htmlReader.d.ts +2 -6
- package/dist/editor/clipboard/htmlReader.js +98 -24
- package/dist/editor/clipboard/inlineFormatting.d.ts +0 -7
- package/dist/editor/clipboard/inlineFormatting.js +1 -7
- package/dist/editor/clipboard/plugin.js +7 -139
- package/dist/editor/clipboard/readContext.d.ts +10 -0
- package/dist/editor/clipboard/readContext.js +8 -2
- package/dist/editor/clipboard/source.d.ts +12 -0
- package/dist/editor/clipboard/source.js +21 -0
- package/dist/editor/commands/comments/editing.d.ts +4 -1
- package/dist/editor/commands/comments/editing.js +3 -4
- package/dist/editor/commands/comments/model.d.ts +7 -0
- package/dist/editor/commands/linkCommands.js +20 -5
- package/dist/editor/commands/lockCommands.js +59 -16
- package/dist/editor/commands/paragraphCommands.js +2 -2
- package/dist/editor/createEditor.js +4 -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/ooxml/props.d.ts +2 -0
- package/dist/ooxml/props.js +14 -0
- package/dist/schema/attrRoles.js +8 -3
- package/dist/schema/clipboard.d.ts +72 -0
- package/dist/schema/clipboard.js +288 -0
- package/dist/schema/docxSchema.js +27 -27
- package/dist/schema/locks.d.ts +3 -0
- package/dist/schema/locks.js +29 -12
- package/dist/schema/rendering.d.ts +14 -0
- package/dist/schema/rendering.js +24 -11
- package/dist/schema/wrappers.d.ts +58 -0
- package/dist/schema/wrappers.js +77 -0
- package/dist/styles/classNames.d.ts +2 -0
- package/dist/styles/classNames.js +2 -0
- package/dist/styles.css +7 -4
- package/dist/ui/CommentsPanel.d.ts +6 -1
- package/dist/ui/CommentsPanel.js +26 -8
- package/package.json +6 -4
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// src/schema/wrappers.ts
|
|
2
|
+
var WRAPPER_GROUP = "wrapper";
|
|
3
|
+
var WRAPPER_ATTRS = {
|
|
4
|
+
/** Outer-to-inner order as read from the file; equal depths fall back to declaration rank */
|
|
5
|
+
depth: { default: 0 },
|
|
6
|
+
/** Which occurrence this is in the opened document, so two wrappers written alike stay apart */
|
|
7
|
+
key: { default: 0 }
|
|
8
|
+
};
|
|
9
|
+
var FACTS = /* @__PURE__ */ new WeakMap();
|
|
10
|
+
var NO_WRAPPERS = [];
|
|
11
|
+
function factsOf(type) {
|
|
12
|
+
const known = FACTS.get(type);
|
|
13
|
+
if (known) return known;
|
|
14
|
+
const facts = {
|
|
15
|
+
wrapper: (type.spec.group ?? "").split(" ").includes(WRAPPER_GROUP),
|
|
16
|
+
rank: Object.keys(type.schema.marks).indexOf(type.name)
|
|
17
|
+
};
|
|
18
|
+
FACTS.set(type, facts);
|
|
19
|
+
return facts;
|
|
20
|
+
}
|
|
21
|
+
function isWrapperType(type) {
|
|
22
|
+
return factsOf(type).wrapper;
|
|
23
|
+
}
|
|
24
|
+
function depthOf(mark) {
|
|
25
|
+
const depth = mark.attrs.depth;
|
|
26
|
+
return typeof depth === "number" ? depth : 0;
|
|
27
|
+
}
|
|
28
|
+
function wrapperMarks(node) {
|
|
29
|
+
const marks = node.marks;
|
|
30
|
+
let found = 0;
|
|
31
|
+
for (const mark of marks) {
|
|
32
|
+
if (factsOf(mark.type).wrapper) found += 1;
|
|
33
|
+
}
|
|
34
|
+
if (found === 0) return NO_WRAPPERS;
|
|
35
|
+
const wrappers = found === marks.length ? [...marks] : marks.filter((mark) => factsOf(mark.type).wrapper);
|
|
36
|
+
if (found < 2) return wrappers;
|
|
37
|
+
return wrappers.sort(
|
|
38
|
+
(a, b) => depthOf(a) - depthOf(b) || factsOf(a.type).rank - factsOf(b.type).rank
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
function wrappersOf(node, kind) {
|
|
42
|
+
return wrapperMarks(node).filter((mark) => mark.type === kind);
|
|
43
|
+
}
|
|
44
|
+
function wrapperOf(node, kind) {
|
|
45
|
+
return wrapperMarks(node).find((mark) => mark.type === kind) ?? null;
|
|
46
|
+
}
|
|
47
|
+
function innermostDepth(marks) {
|
|
48
|
+
return marks.reduce(
|
|
49
|
+
(deepest, mark) => factsOf(mark.type).wrapper ? Math.max(deepest, depthOf(mark)) : deepest,
|
|
50
|
+
-1
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
function sharedWrappers(nodes) {
|
|
54
|
+
const first = nodes.at(0);
|
|
55
|
+
if (first === void 0) return [];
|
|
56
|
+
let shared = wrapperMarks(first);
|
|
57
|
+
for (const node of nodes.slice(1)) {
|
|
58
|
+
const marks = wrapperMarks(node);
|
|
59
|
+
const differs = shared.findIndex((mark, index) => {
|
|
60
|
+
const beside = marks[index];
|
|
61
|
+
return beside === void 0 || !mark.eq(beside);
|
|
62
|
+
});
|
|
63
|
+
if (differs !== -1) shared = shared.slice(0, differs);
|
|
64
|
+
if (shared.length === 0) break;
|
|
65
|
+
}
|
|
66
|
+
return shared;
|
|
67
|
+
}
|
|
68
|
+
export {
|
|
69
|
+
WRAPPER_ATTRS,
|
|
70
|
+
WRAPPER_GROUP,
|
|
71
|
+
innermostDepth,
|
|
72
|
+
isWrapperType,
|
|
73
|
+
sharedWrappers,
|
|
74
|
+
wrapperMarks,
|
|
75
|
+
wrapperOf,
|
|
76
|
+
wrappersOf
|
|
77
|
+
};
|
|
@@ -140,6 +140,8 @@ export declare const editorClassNames: {
|
|
|
140
140
|
readonly commentMeta: "docx-editor-comment-meta";
|
|
141
141
|
readonly commentAuthor: "docx-editor-comment-author";
|
|
142
142
|
readonly commentDate: "docx-editor-comment-date";
|
|
143
|
+
/** The note on a card whose comment lost the text it was written for */
|
|
144
|
+
readonly commentDetached: "docx-editor-comment-detached";
|
|
143
145
|
readonly commentBody: "docx-editor-comment-body";
|
|
144
146
|
readonly commentActions: "docx-editor-comment-actions";
|
|
145
147
|
readonly commentPrimaryAction: "docx-editor-comment-primary-action";
|
|
@@ -136,6 +136,8 @@ var editorClassNames = {
|
|
|
136
136
|
commentMeta: `${PREFIX}-comment-meta`,
|
|
137
137
|
commentAuthor: `${PREFIX}-comment-author`,
|
|
138
138
|
commentDate: `${PREFIX}-comment-date`,
|
|
139
|
+
/** The note on a card whose comment lost the text it was written for */
|
|
140
|
+
commentDetached: `${PREFIX}-comment-detached`,
|
|
139
141
|
commentBody: `${PREFIX}-comment-body`,
|
|
140
142
|
commentActions: `${PREFIX}-comment-actions`,
|
|
141
143
|
commentPrimaryAction: `${PREFIX}-comment-primary-action`,
|
package/dist/styles.css
CHANGED
|
@@ -270,12 +270,15 @@
|
|
|
270
270
|
margin-top: 2px;
|
|
271
271
|
}
|
|
272
272
|
|
|
273
|
-
|
|
274
|
-
|
|
273
|
+
.docx-editor-comment-detached {
|
|
274
|
+
display: block;
|
|
275
|
+
margin-top: 2px;
|
|
276
|
+
color: #8a8a8a;
|
|
277
|
+
font-style: italic;
|
|
275
278
|
}
|
|
276
279
|
|
|
277
|
-
button.docx-editor-comment-meta
|
|
278
|
-
cursor:
|
|
280
|
+
button.docx-editor-comment-meta {
|
|
281
|
+
cursor: pointer;
|
|
279
282
|
}
|
|
280
283
|
|
|
281
284
|
.docx-editor-comment-body {
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import type { EditorState } from "prosemirror-state";
|
|
2
2
|
import type { EditorView } from "prosemirror-view";
|
|
3
3
|
import { type ReactElement } from "react";
|
|
4
|
-
import { type CommentAuthor } from "../editor/commands/commentCommands";
|
|
4
|
+
import { type CommentAuthor, type DocumentComment } from "../editor/commands/commentCommands";
|
|
5
|
+
/**
|
|
6
|
+
* Whether the rail beside the page carries this comment: the thread is unsettled, and the text it
|
|
7
|
+
* was written for is still there to draw it next to.
|
|
8
|
+
*/
|
|
9
|
+
export declare function shownBesideThePage(comment: DocumentComment): boolean;
|
|
5
10
|
export interface CommentsPanelProps {
|
|
6
11
|
view: EditorView;
|
|
7
12
|
state: EditorState;
|
package/dist/ui/CommentsPanel.js
CHANGED
|
@@ -43,6 +43,19 @@ function run(view, command) {
|
|
|
43
43
|
}
|
|
44
44
|
var REFUSED_COMMENT = "This comment could not be added. The text it was written for may have been changed or locked since.";
|
|
45
45
|
var REFUSED_REPLY = "This reply could not be added. The comment it answers may have been changed or deleted since.";
|
|
46
|
+
var DETACHED_LABEL = "Original content deleted";
|
|
47
|
+
function shownBesideThePage(comment) {
|
|
48
|
+
return !comment.resolved && comment.anchored;
|
|
49
|
+
}
|
|
50
|
+
function CommentIdentity({
|
|
51
|
+
comment
|
|
52
|
+
}) {
|
|
53
|
+
const date = shownDate(comment.date);
|
|
54
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
55
|
+
/* @__PURE__ */ jsx("span", { className: editorClassNames.commentAuthor, children: comment.author ?? "Unknown author" }),
|
|
56
|
+
date && /* @__PURE__ */ jsx("span", { className: editorClassNames.commentDate, children: date })
|
|
57
|
+
] });
|
|
58
|
+
}
|
|
46
59
|
function sameTarget(left, right) {
|
|
47
60
|
return left?.commentId === right.commentId && left.replyId === right.replyId;
|
|
48
61
|
}
|
|
@@ -74,7 +87,7 @@ function CommentsPanel({
|
|
|
74
87
|
if (Number.isNaN(leftTime)) return 1;
|
|
75
88
|
if (Number.isNaN(rightTime)) return -1;
|
|
76
89
|
return rightTime - leftTime;
|
|
77
|
-
}) : comments.filter(
|
|
90
|
+
}) : comments.filter(shownBesideThePage),
|
|
78
91
|
[allCommentsOpen, comments]
|
|
79
92
|
);
|
|
80
93
|
const { positions, canvasHeight } = useCommentRailLayout({
|
|
@@ -158,22 +171,26 @@ function CommentsPanel({
|
|
|
158
171
|
{
|
|
159
172
|
className: editorClassNames.commentPosition,
|
|
160
173
|
"data-resolved": comment.resolved ? "true" : void 0,
|
|
174
|
+
"data-detached": comment.anchored ? void 0 : "true",
|
|
161
175
|
"data-comment-position": comment.id,
|
|
162
176
|
style: allCommentsOpen ? void 0 : { top: `${positions.get(comment.id) ?? 64}px` },
|
|
163
177
|
children: /* @__PURE__ */ jsxs("div", { className: editorClassNames.commentCard, children: [
|
|
164
178
|
/* @__PURE__ */ jsxs("div", { className: editorClassNames.commentHeader, children: [
|
|
165
|
-
/* @__PURE__ */
|
|
179
|
+
comment.anchored ? /* @__PURE__ */ jsx(
|
|
166
180
|
"button",
|
|
167
181
|
{
|
|
168
182
|
type: "button",
|
|
169
183
|
className: editorClassNames.commentMeta,
|
|
170
184
|
onClick: () => run(view, selectComment(comment.id)),
|
|
171
|
-
|
|
172
|
-
children: [
|
|
173
|
-
/* @__PURE__ */ jsx("span", { className: editorClassNames.commentAuthor, children: comment.author ?? "Unknown author" }),
|
|
174
|
-
shownDate(comment.date) && /* @__PURE__ */ jsx("span", { className: editorClassNames.commentDate, children: shownDate(comment.date) })
|
|
175
|
-
]
|
|
185
|
+
children: /* @__PURE__ */ jsx(CommentIdentity, { comment })
|
|
176
186
|
}
|
|
187
|
+
) : (
|
|
188
|
+
// Nothing on the page to go to, so the identity is text rather than a button:
|
|
189
|
+
// a click here leaves the caret and the scroll where the reader left them
|
|
190
|
+
/* @__PURE__ */ jsxs("div", { className: editorClassNames.commentMeta, children: [
|
|
191
|
+
/* @__PURE__ */ jsx(CommentIdentity, { comment }),
|
|
192
|
+
/* @__PURE__ */ jsx("span", { className: editorClassNames.commentDetached, children: DETACHED_LABEL })
|
|
193
|
+
] })
|
|
177
194
|
),
|
|
178
195
|
writer !== null && !rootEditing && /* @__PURE__ */ jsxs("div", { className: editorClassNames.commentIconActions, children: [
|
|
179
196
|
rootOwned && !comment.resolved && /* @__PURE__ */ jsx(
|
|
@@ -361,5 +378,6 @@ function CommentsPanel({
|
|
|
361
378
|
);
|
|
362
379
|
}
|
|
363
380
|
export {
|
|
364
|
-
CommentsPanel
|
|
381
|
+
CommentsPanel,
|
|
382
|
+
shownBesideThePage
|
|
365
383
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@portone/docx-editor",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "A simple DOCX editor for React, built directly on OOXML.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -107,7 +107,8 @@
|
|
|
107
107
|
"build:demo": "vite build --config demo/vite.config.ts",
|
|
108
108
|
"dev:site": "pnpm --filter @portone/docx-editor-site dev",
|
|
109
109
|
"build:site": "pnpm --filter @portone/docx-editor-site build",
|
|
110
|
-
"
|
|
110
|
+
"bench": "vitest run --config vitest.bench.config.ts --disableConsoleIntercept",
|
|
111
|
+
"check": "pnpm lint && pnpm typecheck && pnpm test && pnpm test:fixtures && pnpm test:site-release && pnpm test:release && pnpm check:demo-library",
|
|
111
112
|
"typecheck": "tsc --noEmit && tsc --noEmit -p e2e && tsc --noEmit -p demo && pnpm --filter @portone/docx-editor-site typecheck",
|
|
112
113
|
"lint": "pnpm biome check . --diagnostic-level=error",
|
|
113
114
|
"test": "vitest run",
|
|
@@ -119,9 +120,10 @@
|
|
|
119
120
|
"api:update": "pnpm build && node scripts/api-report.mjs --local",
|
|
120
121
|
"spec": "node scripts/index-ooxml-spec.mjs",
|
|
121
122
|
"changeset": "changeset",
|
|
122
|
-
"
|
|
123
|
+
"release:pr": "node scripts/release.mjs pr",
|
|
123
124
|
"test:e2e": "playwright test",
|
|
124
|
-
"test:site-release": "node --test scripts/site-release.test.mjs"
|
|
125
|
+
"test:site-release": "node --test scripts/site-release.test.mjs",
|
|
126
|
+
"test:release": "node --test scripts/release.test.mjs"
|
|
125
127
|
},
|
|
126
128
|
"types": "./dist/index.d.ts"
|
|
127
129
|
}
|