@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,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether two nodes would be written back as the same XML.
|
|
3
|
+
*
|
|
4
|
+
* `Node.eq` answers a different question: whether they are the same node, display values and all.
|
|
5
|
+
* That is the right question inside an editing session, where both sides came from one import and
|
|
6
|
+
* a difference can only be an edit. It is the wrong one across a re-derivation, because the values
|
|
7
|
+
* worked out from the surroundings (`./attrRoles`) move without the document moving, and a block
|
|
8
|
+
* judged changed is a block rewritten, which costs it the markup the writer does not model.
|
|
9
|
+
*
|
|
10
|
+
* So the comparison is `Node.eq` with the display attrs left out, and nothing else left out: the
|
|
11
|
+
* type, the text, the marks and the source and session attrs all have to agree, and the recursion
|
|
12
|
+
* runs the same way, so a node that says the same thing in a different shape is still a change.
|
|
13
|
+
*/
|
|
14
|
+
import { type Node as PMNode } from "prosemirror-model";
|
|
15
|
+
/**
|
|
16
|
+
* Whether the two would write the same XML: the same type, text, marks and source attrs, all the
|
|
17
|
+
* way down. What the editor works out for the screen is not compared.
|
|
18
|
+
*/
|
|
19
|
+
export declare function sameSource(a: PMNode, b: PMNode): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* The node with every worked-out attr back at the value the schema starts it on, on itself and on
|
|
22
|
+
* everything inside it.
|
|
23
|
+
*
|
|
24
|
+
* `sameSource` needs no such copy, and takes none. This is for a caller that has to hand a whole
|
|
25
|
+
* document to something comparing the ordinary way.
|
|
26
|
+
*/
|
|
27
|
+
export declare function withoutDisplayAttrs(node: PMNode): PMNode;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// src/schema/sourceEquality.ts
|
|
2
|
+
import {
|
|
3
|
+
Fragment
|
|
4
|
+
} from "prosemirror-model";
|
|
5
|
+
import { displayAttrsOf } from "./attrRoles.js";
|
|
6
|
+
function sameValue(a, b) {
|
|
7
|
+
if (a === b) return true;
|
|
8
|
+
if (Array.isArray(a) || Array.isArray(b)) {
|
|
9
|
+
return Array.isArray(a) && Array.isArray(b) && a.length === b.length && a.every((item, at) => sameValue(item, b[at]));
|
|
10
|
+
}
|
|
11
|
+
if (typeof a !== "object" || typeof b !== "object" || a === null || b === null) {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
const left = Object.entries(a);
|
|
15
|
+
return left.length === Object.keys(b).length && left.every(
|
|
16
|
+
([key, value]) => key in b && sameValue(value, b[key])
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
function sameAttrs(display, a, b) {
|
|
20
|
+
return Object.keys(a).filter((name) => !display.includes(name)).every((name) => sameValue(a[name], b[name]));
|
|
21
|
+
}
|
|
22
|
+
function sameMarks(a, b) {
|
|
23
|
+
return a.length === b.length && a.every((mark, at) => {
|
|
24
|
+
const other = b[at];
|
|
25
|
+
return other !== void 0 && mark.type === other.type && sameAttrs(displayAttrsOf(mark.type), mark.attrs, other.attrs);
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
function sameSource(a, b) {
|
|
29
|
+
if (a === b) return true;
|
|
30
|
+
if (a.type !== b.type || !sameMarks(a.marks, b.marks) || !sameAttrs(displayAttrsOf(a.type), a.attrs, b.attrs)) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
if (a.isText) return a.text === b.text;
|
|
34
|
+
if (a.childCount !== b.childCount) return false;
|
|
35
|
+
for (let at = 0; at < a.childCount; at += 1) {
|
|
36
|
+
if (!sameSource(a.child(at), b.child(at))) return false;
|
|
37
|
+
}
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
function withoutDisplay(type, attrs) {
|
|
41
|
+
return Object.fromEntries(
|
|
42
|
+
Object.entries(attrs).map(
|
|
43
|
+
([name, value]) => displayAttrsOf(type).includes(name) ? [name, type.spec.attrs?.[name]?.default ?? null] : [name, value]
|
|
44
|
+
)
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
function withoutDisplayMarkAttrs(mark) {
|
|
48
|
+
return displayAttrsOf(mark.type).length === 0 ? mark : mark.type.create(withoutDisplay(mark.type, mark.attrs));
|
|
49
|
+
}
|
|
50
|
+
var childrenOf = (node) => Array.from({ length: node.childCount }, (_, at) => node.child(at));
|
|
51
|
+
function withoutDisplayAttrs(node) {
|
|
52
|
+
const marks = node.marks.map(withoutDisplayMarkAttrs);
|
|
53
|
+
if (node.isText) return node.mark(marks);
|
|
54
|
+
return node.type.create(
|
|
55
|
+
withoutDisplay(node.type, node.attrs),
|
|
56
|
+
Fragment.fromArray(
|
|
57
|
+
childrenOf(node).map((child) => withoutDisplayAttrs(child))
|
|
58
|
+
),
|
|
59
|
+
marks
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
export {
|
|
63
|
+
sameSource,
|
|
64
|
+
withoutDisplayAttrs
|
|
65
|
+
};
|
|
@@ -8,7 +8,9 @@ import {
|
|
|
8
8
|
NO_FILL,
|
|
9
9
|
toCellFormat
|
|
10
10
|
} from "../model/format.js";
|
|
11
|
-
import {
|
|
11
|
+
import { transactionAllowed } from "../schema/guards.js";
|
|
12
|
+
import { isLockedCell } from "../schema/locks.js";
|
|
13
|
+
import { editsShut } from "../schema/protectionState.js";
|
|
12
14
|
import { cellDefaultsAt, tableCellSources } from "./gridBorders.js";
|
|
13
15
|
function text(value) {
|
|
14
16
|
return typeof value === "string" ? value : null;
|
|
@@ -59,7 +61,7 @@ function cellFormatCommand(edit) {
|
|
|
59
61
|
const changes = planChanges(rect, edit);
|
|
60
62
|
if (changes.length === 0) return false;
|
|
61
63
|
const transaction = applyChanges(state.tr, rect.tableStart, changes);
|
|
62
|
-
if (!transactionAllowed(transaction, state
|
|
64
|
+
if (!transactionAllowed(transaction, state)) return false;
|
|
63
65
|
dispatch?.(transaction);
|
|
64
66
|
return true;
|
|
65
67
|
};
|
|
@@ -118,6 +120,7 @@ function cellFormats(state) {
|
|
|
118
120
|
return selectedCells(state).map((cell) => toCellFormat(cell.attrs.format));
|
|
119
121
|
}
|
|
120
122
|
function canSetCellFormatting(state) {
|
|
123
|
+
if (editsShut(state)) return false;
|
|
121
124
|
const cells = selectedCells(state);
|
|
122
125
|
return cells.length > 0 && cells.every((cell) => !isLockedCell(cell));
|
|
123
126
|
}
|
package/dist/table/commands.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Building a transaction and running it are kept apart.
|
|
3
3
|
* Following the ProseMirror convention, calling a command without `dispatch` only
|
|
4
|
-
* reports whether it can run right now, and that answer takes in
|
|
5
|
-
* (`schema/
|
|
4
|
+
* reports whether it can run right now, and that answer takes in every guard an edit
|
|
5
|
+
* is judged by (`schema/guards`), so no caller has to ask about them separately.
|
|
6
6
|
*/
|
|
7
7
|
import type { EditorState, Transaction } from "prosemirror-state";
|
|
8
8
|
export type TableCommand = (state: EditorState, dispatch?: (tr: Transaction) => void) => boolean;
|
package/dist/table/commands.js
CHANGED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
deleteTable as pmDeleteTable,
|
|
4
4
|
isInTable as pmIsInTable
|
|
5
5
|
} from "prosemirror-tables";
|
|
6
|
-
import { transactionAllowed } from "../schema/
|
|
6
|
+
import { transactionAllowed } from "../schema/guards.js";
|
|
7
7
|
import {
|
|
8
8
|
buildAddColumnAfterTransaction,
|
|
9
9
|
buildAddColumnBeforeTransaction,
|
|
@@ -21,7 +21,7 @@ function isInTable(state) {
|
|
|
21
21
|
function toCommand(build) {
|
|
22
22
|
return (state, dispatch) => {
|
|
23
23
|
const tr = build(state);
|
|
24
|
-
if (!tr || !transactionAllowed(tr, state
|
|
24
|
+
if (!tr || !transactionAllowed(tr, state)) return false;
|
|
25
25
|
dispatch?.(tr);
|
|
26
26
|
return true;
|
|
27
27
|
};
|
package/dist/table/index.d.ts
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Only what the toolbar needs is exported. Following the ProseMirror convention,
|
|
5
5
|
* calling a command without `dispatch` only reports whether it can run right now, and that answer
|
|
6
|
-
* takes in
|
|
7
|
-
* apply, rather than being refused after the click
|
|
6
|
+
* takes in every guard an edit is judged by (`schema/guards`): a structural edit that would carry a
|
|
7
|
+
* locked cell off reports that it does not apply, rather than being refused after the click.
|
|
8
8
|
*
|
|
9
9
|
* Everything here works on a table already in the document; putting a new one into the body is
|
|
10
10
|
* `insertTable` in `./commands`, which reads the page geometry the editor holds.
|
package/dist/table/merge.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ import type { EditorState, Transaction } from "prosemirror-state";
|
|
|
6
6
|
/**
|
|
7
7
|
* Whether several cells can be merged into one.
|
|
8
8
|
* The selection has to be a rectangle spanning more than one cell, no merged cell may stick out
|
|
9
|
-
* past that rectangle, and the
|
|
9
|
+
* past that rectangle, and the guards have to let the merge through: a locked cell may not be
|
|
10
10
|
* swallowed by another (`schema/locks`).
|
|
11
11
|
*
|
|
12
12
|
* The two queries are defined from the very transaction the commands run, so they cannot drift
|
|
@@ -16,7 +16,7 @@ import type { EditorState, Transaction } from "prosemirror-state";
|
|
|
16
16
|
export declare function canMergeCells(state: EditorState): boolean;
|
|
17
17
|
/**
|
|
18
18
|
* Whether a split is possible. The cursor has to sit inside a cell that is merged horizontally or
|
|
19
|
-
* vertically, and the
|
|
19
|
+
* vertically, and the guards have to let the split through: the cells a split makes would each
|
|
20
20
|
* carry the original's lock, which is a lock planted in places it was never put (`schema/locks`).
|
|
21
21
|
*/
|
|
22
22
|
export declare function canSplitCell(state: EditorState): boolean;
|
package/dist/table/merge.js
CHANGED
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
widthNumber,
|
|
12
12
|
withWidthNumber
|
|
13
13
|
} from "../model/format.js";
|
|
14
|
-
import { transactionAllowed } from "../schema/
|
|
14
|
+
import { transactionAllowed } from "../schema/guards.js";
|
|
15
15
|
import { inheritCellAttrs } from "./format.js";
|
|
16
16
|
import { cellWidthForGridCol, gridSpanWidth, tableGridCols } from "./widths.js";
|
|
17
17
|
function tableAfter(tr, tableStart) {
|
|
@@ -26,11 +26,11 @@ function cellAt(rect, row, col) {
|
|
|
26
26
|
}
|
|
27
27
|
function canMergeCells(state) {
|
|
28
28
|
const tr = buildMergeCellsTransaction(state);
|
|
29
|
-
return tr !== null && transactionAllowed(tr, state
|
|
29
|
+
return tr !== null && transactionAllowed(tr, state);
|
|
30
30
|
}
|
|
31
31
|
function canSplitCell(state) {
|
|
32
32
|
const tr = buildSplitCellTransaction(state);
|
|
33
|
-
return tr !== null && transactionAllowed(tr, state
|
|
33
|
+
return tr !== null && transactionAllowed(tr, state);
|
|
34
34
|
}
|
|
35
35
|
function coveredWidthSum(rect, type) {
|
|
36
36
|
let sum = 0;
|
|
@@ -5,11 +5,11 @@ import { type CommentAuthor } from "../editor/commands/commentCommands";
|
|
|
5
5
|
export interface CommentsPanelProps {
|
|
6
6
|
view: EditorView;
|
|
7
7
|
state: EditorState;
|
|
8
|
-
readOnly: boolean;
|
|
9
8
|
composerOpen: boolean;
|
|
10
|
-
|
|
9
|
+
/** Whose comments the composers write. Null for a reader, who is offered none */
|
|
10
|
+
author: CommentAuthor | null;
|
|
11
11
|
closeComposer: () => void;
|
|
12
12
|
scrollContainer: HTMLElement | null;
|
|
13
13
|
allCommentsOpen: boolean;
|
|
14
14
|
}
|
|
15
|
-
export declare function CommentsPanel({ view, state,
|
|
15
|
+
export declare function CommentsPanel({ view, state, composerOpen, author, closeComposer, scrollContainer, allCommentsOpen, }: CommentsPanelProps): ReactElement;
|
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
|
}
|
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.1",
|
|
4
4
|
"description": "A simple DOCX editor for React, built directly on OOXML.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -76,10 +76,12 @@
|
|
|
76
76
|
"@biomejs/biome": "2.5.10",
|
|
77
77
|
"@changesets/changelog-github": "^1.0.0",
|
|
78
78
|
"@changesets/cli": "^3.0.1",
|
|
79
|
+
"@microsoft/api-extractor": "7.59.0",
|
|
79
80
|
"@playwright/test": "^1.62.1",
|
|
81
|
+
"@types/jsdom": "^30.0.0",
|
|
80
82
|
"@types/node": "^26.2.0",
|
|
81
83
|
"@types/react": "19.2.18",
|
|
82
|
-
"@types/react-dom": "19.2.
|
|
84
|
+
"@types/react-dom": "19.2.5",
|
|
83
85
|
"@vitejs/plugin-react": "^6.0.5",
|
|
84
86
|
"esbuild": "^0.28.1",
|
|
85
87
|
"jsdom": "^30.0.1",
|
|
@@ -111,6 +113,7 @@
|
|
|
111
113
|
"test": "vitest run",
|
|
112
114
|
"test:package": "vitest run --dir packaging --no-file-parallelism",
|
|
113
115
|
"verify:package": "node scripts/verify-package.mjs",
|
|
116
|
+
"api:update": "pnpm build && node scripts/api-report.mjs --local",
|
|
114
117
|
"spec": "node scripts/index-ooxml-spec.mjs",
|
|
115
118
|
"changeset": "changeset",
|
|
116
119
|
"changeset:version": "changeset version",
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
// src/editor/plugins/bookmarkProtection.ts
|
|
2
|
-
import { Plugin } from "prosemirror-state";
|
|
3
|
-
var BOOKMARK_XML = /<(?:[\w.-]+:)?bookmark(?:Start|End)\b/;
|
|
4
|
-
function signatures(doc) {
|
|
5
|
-
const found = [];
|
|
6
|
-
doc.descendants((node) => {
|
|
7
|
-
if (node.type.name === "bookmarkBlock") {
|
|
8
|
-
found.push(`block:${node.attrs.srcId}:${node.attrs.name}`);
|
|
9
|
-
} else if (node.type.name === "rawInline" && typeof node.attrs.xml === "string" && BOOKMARK_XML.test(node.attrs.xml)) {
|
|
10
|
-
found.push(`inline:${node.attrs.xml}`);
|
|
11
|
-
}
|
|
12
|
-
return true;
|
|
13
|
-
});
|
|
14
|
-
return found;
|
|
15
|
-
}
|
|
16
|
-
function same(a, b) {
|
|
17
|
-
return a.length === b.length && a.every((value, index) => value === b[index]);
|
|
18
|
-
}
|
|
19
|
-
function bookmarkProtection() {
|
|
20
|
-
return new Plugin({
|
|
21
|
-
filterTransaction(transaction, state) {
|
|
22
|
-
return !transaction.docChanged || same(signatures(state.doc), signatures(transaction.doc));
|
|
23
|
-
}
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
export {
|
|
27
|
-
bookmarkProtection
|
|
28
|
-
};
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
// src/editor/plugins/noteProtection.ts
|
|
2
|
-
import { Plugin } from "prosemirror-state";
|
|
3
|
-
function signatures(doc) {
|
|
4
|
-
const found = [];
|
|
5
|
-
doc.descendants((node) => {
|
|
6
|
-
if (node.type.name === "noteReference") {
|
|
7
|
-
found.push(JSON.stringify(node.attrs));
|
|
8
|
-
}
|
|
9
|
-
return true;
|
|
10
|
-
});
|
|
11
|
-
return found;
|
|
12
|
-
}
|
|
13
|
-
function same(a, b) {
|
|
14
|
-
return a.length === b.length && a.every((value, index) => value === b[index]);
|
|
15
|
-
}
|
|
16
|
-
function noteProtection() {
|
|
17
|
-
return new Plugin({
|
|
18
|
-
filterTransaction(transaction, state) {
|
|
19
|
-
return !transaction.docChanged || same(signatures(state.doc), signatures(transaction.doc));
|
|
20
|
-
}
|
|
21
|
-
});
|
|
22
|
-
}
|
|
23
|
-
export {
|
|
24
|
-
noteProtection
|
|
25
|
-
};
|