@portone/docx-editor 0.1.0 → 0.2.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 +29 -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 +45 -0
- package/dist/docx/commentOnlyChange.js +145 -0
- package/dist/docx/comments/constants.d.ts +8 -0
- package/dist/docx/comments/constants.js +6 -0
- package/dist/docx/comments/contentTypes.d.ts +7 -0
- package/dist/docx/comments/contentTypes.js +38 -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 +198 -0
- package/dist/docx/comments/reading.d.ts +16 -1
- package/dist/docx/comments/reading.js +15 -5
- package/dist/docx/comments/writing.d.ts +4 -1
- package/dist/docx/comments/writing.js +12 -32
- package/dist/docx/importParagraph.js +1 -0
- package/dist/editor/commands/breakCommands.js +4 -1
- package/dist/editor/commands/canRunCommand.d.ts +3 -2
- package/dist/editor/commands/canRunCommand.js +1 -1
- package/dist/editor/commands/comments/editing.js +6 -4
- 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/formatting/reading.js +13 -4
- package/dist/editor/commands/historyCommands.js +14 -6
- 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 +2 -0
- package/dist/editor/commands/tabCommands.js +2 -1
- package/dist/editor/createEditor.d.ts +13 -3
- package/dist/editor/createEditor.js +21 -7
- package/dist/editor/insertImage.js +4 -1
- 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 +1 -1
- 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/markers.js +13 -1
- package/dist/schema/docxSchema.js +5 -0
- package/dist/schema/locks.d.ts +31 -3
- package/dist/schema/locks.js +54 -3
- package/dist/schema/protection.d.ts +77 -0
- package/dist/schema/protection.js +170 -0
- package/dist/schema/protectionState.d.ts +20 -0
- package/dist/schema/protectionState.js +37 -0
- package/dist/styles/fontStack.d.ts +9 -0
- package/dist/styles/fontStack.js +9 -8
- package/dist/table/cellFormatting.js +3 -1
- package/dist/table/commands.js +1 -1
- package/dist/table/merge.js +2 -2
- package/dist/ui/CommentsPanel.d.ts +3 -3
- package/dist/ui/CommentsPanel.js +26 -18
- package/dist/ui/FontFamilySelect.js +21 -7
- 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 +2 -2
package/dist/ui/CommentsPanel.js
CHANGED
|
@@ -12,6 +12,8 @@ import {
|
|
|
12
12
|
updateComment,
|
|
13
13
|
updateCommentReply
|
|
14
14
|
} from "../editor/commands/commentCommands.js";
|
|
15
|
+
import { commentOwned } from "../schema/protection.js";
|
|
16
|
+
import { protectionOf } from "../schema/protectionState.js";
|
|
15
17
|
import { editorClassNames } from "../styles/classNames.js";
|
|
16
18
|
import { CommentComposer, FocusedTextarea } from "./comments/CommentComposer.js";
|
|
17
19
|
import {
|
|
@@ -41,7 +43,6 @@ function sameTarget(left, right) {
|
|
|
41
43
|
function CommentsPanel({
|
|
42
44
|
view,
|
|
43
45
|
state,
|
|
44
|
-
readOnly,
|
|
45
46
|
composerOpen,
|
|
46
47
|
author,
|
|
47
48
|
closeComposer,
|
|
@@ -49,6 +50,9 @@ function CommentsPanel({
|
|
|
49
50
|
allCommentsOpen
|
|
50
51
|
}) {
|
|
51
52
|
const comments = useMemo(() => documentComments(state), [state.doc]);
|
|
53
|
+
const rules = protectionOf(state);
|
|
54
|
+
const writer = author != null && rules.protection !== "readOnly" ? author : null;
|
|
55
|
+
const owned = (authorId) => writer !== null && commentOwned(rules, authorId);
|
|
52
56
|
const panel = useRef(null);
|
|
53
57
|
const [editing, setEditing] = useState(null);
|
|
54
58
|
const [draft, setDraft] = useState("");
|
|
@@ -108,7 +112,7 @@ function CommentsPanel({
|
|
|
108
112
|
className: editorClassNames.commentsCanvas,
|
|
109
113
|
style: { height: allCommentsOpen ? void 0 : `${canvasHeight}px` },
|
|
110
114
|
children: [
|
|
111
|
-
composerOpen &&
|
|
115
|
+
composerOpen && writer !== null && /* @__PURE__ */ jsx(
|
|
112
116
|
"div",
|
|
113
117
|
{
|
|
114
118
|
className: editorClassNames.commentPosition,
|
|
@@ -119,7 +123,7 @@ function CommentsPanel({
|
|
|
119
123
|
children: /* @__PURE__ */ jsx(
|
|
120
124
|
CommentComposer,
|
|
121
125
|
{
|
|
122
|
-
author,
|
|
126
|
+
author: writer,
|
|
123
127
|
label: "Comment text",
|
|
124
128
|
submitLabel: "Comment",
|
|
125
129
|
onClose: closeComposer,
|
|
@@ -127,8 +131,9 @@ function CommentsPanel({
|
|
|
127
131
|
view,
|
|
128
132
|
addComment({
|
|
129
133
|
text,
|
|
130
|
-
author:
|
|
131
|
-
|
|
134
|
+
author: writer.name,
|
|
135
|
+
authorId: writer.id,
|
|
136
|
+
initials: writer.initials
|
|
132
137
|
})
|
|
133
138
|
)
|
|
134
139
|
}
|
|
@@ -137,7 +142,8 @@ function CommentsPanel({
|
|
|
137
142
|
),
|
|
138
143
|
visible.map((comment) => {
|
|
139
144
|
const rootTarget = { commentId: comment.id, replyId: null };
|
|
140
|
-
const
|
|
145
|
+
const rootOwned = owned(comment.authorId);
|
|
146
|
+
const rootEditing = rootOwned && sameTarget(editing, rootTarget);
|
|
141
147
|
return /* @__PURE__ */ jsx(
|
|
142
148
|
"article",
|
|
143
149
|
{
|
|
@@ -160,8 +166,8 @@ function CommentsPanel({
|
|
|
160
166
|
]
|
|
161
167
|
}
|
|
162
168
|
),
|
|
163
|
-
|
|
164
|
-
!comment.resolved && /* @__PURE__ */ jsx(
|
|
169
|
+
writer !== null && !rootEditing && /* @__PURE__ */ jsxs("div", { className: editorClassNames.commentIconActions, children: [
|
|
170
|
+
rootOwned && !comment.resolved && /* @__PURE__ */ jsx(
|
|
165
171
|
"button",
|
|
166
172
|
{
|
|
167
173
|
type: "button",
|
|
@@ -190,7 +196,7 @@ function CommentsPanel({
|
|
|
190
196
|
children: comment.resolved ? /* @__PURE__ */ jsx(RotateCcw, { size: 16, "aria-hidden": "true" }) : /* @__PURE__ */ jsx(Check, { size: 16, "aria-hidden": "true" })
|
|
191
197
|
}
|
|
192
198
|
),
|
|
193
|
-
!comment.resolved && /* @__PURE__ */ jsx(
|
|
199
|
+
rootOwned && !comment.resolved && /* @__PURE__ */ jsx(
|
|
194
200
|
"button",
|
|
195
201
|
{
|
|
196
202
|
type: "button",
|
|
@@ -209,7 +215,7 @@ function CommentsPanel({
|
|
|
209
215
|
value: draft,
|
|
210
216
|
onChange: setDraft
|
|
211
217
|
}
|
|
212
|
-
) :
|
|
218
|
+
) : writer === null || comment.resolved ? /* @__PURE__ */ jsx("p", { className: editorClassNames.commentBody, children: comment.text }) : /* @__PURE__ */ jsx(
|
|
213
219
|
"button",
|
|
214
220
|
{
|
|
215
221
|
type: "button",
|
|
@@ -222,7 +228,7 @@ function CommentsPanel({
|
|
|
222
228
|
children: comment.text
|
|
223
229
|
}
|
|
224
230
|
),
|
|
225
|
-
|
|
231
|
+
rootEditing && /* @__PURE__ */ jsxs("div", { className: editorClassNames.commentActions, children: [
|
|
226
232
|
/* @__PURE__ */ jsx("button", { type: "button", onClick: () => setEditing(null), children: "Cancel" }),
|
|
227
233
|
/* @__PURE__ */ jsx(
|
|
228
234
|
"button",
|
|
@@ -234,13 +240,14 @@ function CommentsPanel({
|
|
|
234
240
|
children: "Save"
|
|
235
241
|
}
|
|
236
242
|
)
|
|
237
|
-
] })
|
|
243
|
+
] }),
|
|
238
244
|
comment.replies.map((reply) => {
|
|
239
245
|
const target = {
|
|
240
246
|
commentId: comment.id,
|
|
241
247
|
replyId: reply.id
|
|
242
248
|
};
|
|
243
|
-
const
|
|
249
|
+
const replyOwned = owned(reply.authorId);
|
|
250
|
+
const isEditing = replyOwned && sameTarget(editing, target);
|
|
244
251
|
return /* @__PURE__ */ jsxs(
|
|
245
252
|
"div",
|
|
246
253
|
{
|
|
@@ -258,7 +265,7 @@ function CommentsPanel({
|
|
|
258
265
|
onChange: setDraft
|
|
259
266
|
}
|
|
260
267
|
) : /* @__PURE__ */ jsx("p", { className: editorClassNames.commentBody, children: reply.text }),
|
|
261
|
-
|
|
268
|
+
replyOwned && !comment.resolved && /* @__PURE__ */ jsx("div", { className: editorClassNames.commentActions, children: isEditing ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
262
269
|
/* @__PURE__ */ jsx(
|
|
263
270
|
"button",
|
|
264
271
|
{
|
|
@@ -313,10 +320,10 @@ function CommentsPanel({
|
|
|
313
320
|
reply.id
|
|
314
321
|
);
|
|
315
322
|
}),
|
|
316
|
-
replying === comment.id &&
|
|
323
|
+
replying === comment.id && writer !== null && !comment.resolved && /* @__PURE__ */ jsx(
|
|
317
324
|
CommentComposer,
|
|
318
325
|
{
|
|
319
|
-
author,
|
|
326
|
+
author: writer,
|
|
320
327
|
label: "Reply text",
|
|
321
328
|
submitLabel: "Reply",
|
|
322
329
|
onClose: () => setReplying(null),
|
|
@@ -324,8 +331,9 @@ function CommentsPanel({
|
|
|
324
331
|
view,
|
|
325
332
|
addCommentReply(comment.id, {
|
|
326
333
|
text,
|
|
327
|
-
author:
|
|
328
|
-
|
|
334
|
+
author: writer.name,
|
|
335
|
+
authorId: writer.id,
|
|
336
|
+
initials: writer.initials
|
|
329
337
|
})
|
|
330
338
|
)
|
|
331
339
|
}
|
|
@@ -3,7 +3,10 @@ import {
|
|
|
3
3
|
setFontFamily
|
|
4
4
|
} from "../editor/commands/formattingCommands.js";
|
|
5
5
|
import { editorClassNames } from "../styles/classNames.js";
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
comparableFontName,
|
|
8
|
+
withFontFallback
|
|
9
|
+
} from "../styles/fontStack.js";
|
|
7
10
|
import { DEFAULT_FONTS } from "../styles/fonts.js";
|
|
8
11
|
import { Tooltip } from "./Tooltip.js";
|
|
9
12
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -11,11 +14,21 @@ var LABEL = "Font";
|
|
|
11
14
|
var CLEAR = ";default";
|
|
12
15
|
var MIXED = "";
|
|
13
16
|
function optionNames(current, fonts, documentFonts) {
|
|
14
|
-
const names = [
|
|
15
|
-
|
|
16
|
-
|
|
17
|
+
const names = [
|
|
18
|
+
...fonts,
|
|
19
|
+
...documentFonts,
|
|
20
|
+
...current.kind === "mixed" ? [] : [current.name]
|
|
21
|
+
];
|
|
22
|
+
const byComparable = /* @__PURE__ */ new Map();
|
|
23
|
+
for (const name of names) {
|
|
24
|
+
const comparable = comparableFontName(name);
|
|
25
|
+
if (!byComparable.has(comparable)) byComparable.set(comparable, name);
|
|
17
26
|
}
|
|
18
|
-
return Array.from(
|
|
27
|
+
return Array.from(byComparable.values());
|
|
28
|
+
}
|
|
29
|
+
function selectedValue(options, name) {
|
|
30
|
+
const comparable = comparableFontName(name);
|
|
31
|
+
return options.find((option) => comparableFontName(option) === comparable) ?? name;
|
|
19
32
|
}
|
|
20
33
|
function FontFamilySelect({
|
|
21
34
|
current,
|
|
@@ -25,7 +38,8 @@ function FontFamilySelect({
|
|
|
25
38
|
disabled,
|
|
26
39
|
run
|
|
27
40
|
}) {
|
|
28
|
-
const
|
|
41
|
+
const options = optionNames(current, fonts, documentFonts);
|
|
42
|
+
const value = current.kind === "mixed" ? MIXED : selectedValue(options, current.name);
|
|
29
43
|
return /* @__PURE__ */ jsx(Tooltip, { label: LABEL, children: /* @__PURE__ */ jsxs(
|
|
30
44
|
"select",
|
|
31
45
|
{
|
|
@@ -41,7 +55,7 @@ function FontFamilySelect({
|
|
|
41
55
|
children: [
|
|
42
56
|
current.kind === "mixed" && /* @__PURE__ */ jsx("option", { value: MIXED }),
|
|
43
57
|
/* @__PURE__ */ jsx("option", { value: CLEAR, children: "Default" }),
|
|
44
|
-
|
|
58
|
+
options.map((name) => /* @__PURE__ */ jsx(
|
|
45
59
|
"option",
|
|
46
60
|
{
|
|
47
61
|
value: name,
|
package/dist/ui/LinkCard.d.ts
CHANGED
|
@@ -7,6 +7,5 @@ export interface LinkCardProps {
|
|
|
7
7
|
view: EditorView;
|
|
8
8
|
state: EditorState;
|
|
9
9
|
link: ActiveLinkSpan;
|
|
10
|
-
readOnly?: boolean;
|
|
11
10
|
}
|
|
12
|
-
export declare function LinkCard({ view, state, link,
|
|
11
|
+
export declare function LinkCard({ view, state, link, }: LinkCardProps): ReactElement | null;
|
package/dist/ui/LinkCard.js
CHANGED
|
@@ -49,8 +49,7 @@ function useEscape(onEscape) {
|
|
|
49
49
|
function LinkCard({
|
|
50
50
|
view,
|
|
51
51
|
state,
|
|
52
|
-
link
|
|
53
|
-
readOnly = false
|
|
52
|
+
link
|
|
54
53
|
}) {
|
|
55
54
|
const box = useRef(null);
|
|
56
55
|
const [hidden, setHidden] = useState(false);
|
|
@@ -94,7 +93,7 @@ function LinkCard({
|
|
|
94
93
|
children: /* @__PURE__ */ jsx(ExternalLink, { size: ICON_SIZE, "aria-hidden": "true" })
|
|
95
94
|
}
|
|
96
95
|
),
|
|
97
|
-
|
|
96
|
+
openLinkPanel(state) && /* @__PURE__ */ jsx(
|
|
98
97
|
"button",
|
|
99
98
|
{
|
|
100
99
|
type: "button",
|
|
@@ -106,7 +105,7 @@ function LinkCard({
|
|
|
106
105
|
children: /* @__PURE__ */ jsx(Pencil, { size: ICON_SIZE, "aria-hidden": "true" })
|
|
107
106
|
}
|
|
108
107
|
),
|
|
109
|
-
|
|
108
|
+
removeLink(state) && /* @__PURE__ */ jsx(
|
|
110
109
|
"button",
|
|
111
110
|
{
|
|
112
111
|
type: "button",
|
package/dist/ui/TextMenu.js
CHANGED
|
@@ -21,6 +21,7 @@ import { insertClipboardData } from "../editor/externalClipboard.js";
|
|
|
21
21
|
import {
|
|
22
22
|
closeTextMenu
|
|
23
23
|
} from "../editor/plugins/textContextMenu.js";
|
|
24
|
+
import { editsShut } from "../schema/protectionState.js";
|
|
24
25
|
import { editorClassNames } from "../styles/classNames.js";
|
|
25
26
|
import { usePanelAtPoint } from "./panelPlacement.js";
|
|
26
27
|
import { commandRunner } from "./runCommand.js";
|
|
@@ -103,8 +104,16 @@ function TextMenu({
|
|
|
103
104
|
const keys = useMenuKeyboard({ menu: box, onClose: close });
|
|
104
105
|
const selected = !state.selection.empty;
|
|
105
106
|
const shut = selectionTouchesLocked(state);
|
|
107
|
+
const bodyOpen = !editsShut(state);
|
|
108
|
+
const copy = {
|
|
109
|
+
label: "Copy",
|
|
110
|
+
icon: Copy,
|
|
111
|
+
hint: `${MOD}C`,
|
|
112
|
+
enabled: selected,
|
|
113
|
+
run: () => clipboardCommand(view, "copy")
|
|
114
|
+
};
|
|
106
115
|
const groups = [
|
|
107
|
-
[
|
|
116
|
+
bodyOpen ? [
|
|
108
117
|
{
|
|
109
118
|
label: "Cut",
|
|
110
119
|
icon: Scissors,
|
|
@@ -112,13 +121,7 @@ function TextMenu({
|
|
|
112
121
|
enabled: selected && !shut,
|
|
113
122
|
run: () => clipboardCommand(view, "cut")
|
|
114
123
|
},
|
|
115
|
-
|
|
116
|
-
label: "Copy",
|
|
117
|
-
icon: Copy,
|
|
118
|
-
hint: `${MOD}C`,
|
|
119
|
-
enabled: selected,
|
|
120
|
-
run: () => clipboardCommand(view, "copy")
|
|
121
|
-
},
|
|
124
|
+
copy,
|
|
122
125
|
{
|
|
123
126
|
label: "Paste",
|
|
124
127
|
icon: ClipboardPaste,
|
|
@@ -134,7 +137,7 @@ function TextMenu({
|
|
|
134
137
|
enabled: selected && !shut,
|
|
135
138
|
run: () => run(deleteSelection)
|
|
136
139
|
}
|
|
137
|
-
]
|
|
140
|
+
] : [copy]
|
|
138
141
|
];
|
|
139
142
|
groups.push([
|
|
140
143
|
{
|
|
@@ -144,7 +147,9 @@ function TextMenu({
|
|
|
144
147
|
run: () => onAddComment?.()
|
|
145
148
|
}
|
|
146
149
|
]);
|
|
147
|
-
if (allowLocking
|
|
150
|
+
if (allowLocking && bodyOpen) {
|
|
151
|
+
groups.push([lockItem(selectionLock(state), run)]);
|
|
152
|
+
}
|
|
148
153
|
const choose = (item) => {
|
|
149
154
|
if (!item.enabled) return;
|
|
150
155
|
item.run();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@portone/docx-editor",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "A simple DOCX editor for React, built directly on OOXML.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -79,7 +79,7 @@
|
|
|
79
79
|
"@playwright/test": "^1.62.1",
|
|
80
80
|
"@types/node": "^26.2.0",
|
|
81
81
|
"@types/react": "19.2.18",
|
|
82
|
-
"@types/react-dom": "19.2.
|
|
82
|
+
"@types/react-dom": "19.2.5",
|
|
83
83
|
"@vitejs/plugin-react": "^6.0.5",
|
|
84
84
|
"esbuild": "^0.28.1",
|
|
85
85
|
"jsdom": "^30.0.1",
|