@portone/docx-editor 0.5.1 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +47 -0
- package/CONTRIBUTING.md +8 -2
- package/README.md +1 -1
- package/dist/DocxEditor.js +87 -29
- package/dist/docx/exportDocx.d.ts +2 -1
- package/dist/docx/exportDocx.js +3 -9
- package/dist/docx/headersFooters.js +35 -23
- package/dist/docx/identities.d.ts +28 -0
- package/dist/docx/identities.js +42 -10
- package/dist/docx/importDocx.js +25 -26
- package/dist/docx/importParagraph.d.ts +1 -4
- package/dist/docx/importParagraph.js +4 -18
- package/dist/docx/invariants.d.ts +1 -1
- package/dist/docx/invariants.js +45 -4
- package/dist/docx/notes/newNote.d.ts +39 -0
- package/dist/docx/notes/newNote.js +122 -0
- package/dist/docx/notes/numbering.d.ts +27 -0
- package/dist/docx/notes/numbering.js +120 -0
- package/dist/docx/notes/reading.d.ts +58 -0
- package/dist/docx/notes/reading.js +98 -0
- package/dist/docx/notes/writing.d.ts +19 -0
- package/dist/docx/notes/writing.js +96 -0
- package/dist/docx/partPlan.d.ts +3 -0
- package/dist/docx/partPlanners.d.ts +13 -0
- package/dist/docx/partPlanners.js +48 -0
- package/dist/docx/sections.d.ts +5 -0
- package/dist/docx/sections.js +7 -2
- package/dist/docx/serializeParagraph.js +7 -2
- package/dist/docx/session.d.ts +5 -0
- package/dist/docx/session.js +6 -0
- package/dist/docx/storyParts.d.ts +112 -0
- package/dist/docx/storyParts.js +289 -0
- package/dist/editor/clipboard/htmlReader.d.ts +24 -4
- package/dist/editor/clipboard/htmlReader.js +39 -4
- package/dist/editor/clipboard/internalChannel.d.ts +30 -3
- package/dist/editor/clipboard/internalChannel.js +43 -3
- package/dist/editor/clipboard/normalizers.d.ts +35 -8
- package/dist/editor/clipboard/normalizers.js +60 -11
- package/dist/editor/clipboard/plugin.js +42 -22
- package/dist/editor/commands/comments/editing.d.ts +4 -1
- package/dist/editor/commands/comments/editing.js +1 -3
- package/dist/editor/commands/comments/model.d.ts +7 -0
- package/dist/editor/commands/index.d.ts +1 -0
- package/dist/editor/commands/index.js +18 -0
- package/dist/editor/commands/noteCommands.d.ts +47 -0
- package/dist/editor/commands/noteCommands.js +95 -0
- package/dist/editor/commands/noteQueries.d.ts +22 -3
- package/dist/editor/commands/noteQueries.js +21 -13
- package/dist/editor/createEditor.js +25 -3
- package/dist/editor/documentStyles.d.ts +3 -0
- package/dist/editor/documentStyles.js +2 -1
- package/dist/editor/editorDocument.d.ts +24 -0
- package/dist/editor/editorDocument.js +34 -4
- package/dist/editor/notes/noteSurface.d.ts +53 -0
- package/dist/editor/notes/noteSurface.js +246 -0
- package/dist/editor/plugins/commentDecorations.js +5 -4
- package/dist/editor/plugins/commentRestoration.d.ts +20 -0
- package/dist/editor/plugins/commentRestoration.js +168 -0
- package/dist/editor/plugins/compositionSelection.d.ts +20 -0
- package/dist/editor/plugins/compositionSelection.js +19 -0
- package/dist/editor/plugins/keymap.d.ts +14 -0
- package/dist/editor/plugins/keymap.js +13 -1
- package/dist/editor/plugins/noteLifecycle.d.ts +18 -0
- package/dist/editor/plugins/noteLifecycle.js +117 -0
- package/dist/editor/plugins/noteNavigation.d.ts +45 -0
- package/dist/editor/plugins/noteNavigation.js +86 -0
- package/dist/editor/plugins/noteNumbering.d.ts +13 -0
- package/dist/editor/plugins/noteNumbering.js +58 -0
- package/dist/editor/plugins/rowResize.js +2 -7
- package/dist/editor/plugins/tabLayout.js +2 -7
- package/dist/editor/stories/storyMarkup.d.ts +24 -0
- package/dist/editor/stories/storyMarkup.js +35 -0
- package/dist/editor/stories/storyState.d.ts +34 -0
- package/dist/editor/stories/storyState.js +74 -0
- package/dist/editor/stories/storyView.d.ts +115 -0
- package/dist/editor/stories/storyView.js +144 -0
- package/dist/numbering/spellers.d.ts +1 -1
- package/dist/numbering/spellers.js +6 -1
- package/dist/page/blockKinds.d.ts +23 -4
- package/dist/page/demands/footnoteDemands.d.ts +18 -0
- package/dist/page/demands/footnoteDemands.js +51 -0
- package/dist/page/demands/index.d.ts +43 -0
- package/dist/page/demands/index.js +8 -0
- package/dist/page/kinds/paragraphKind.js +7 -1
- package/dist/page/kinds/tableKind.js +9 -1
- package/dist/page/measureBlocks.d.ts +2 -0
- package/dist/page/measureBlocks.js +31 -10
- package/dist/page/pageDecorations.d.ts +9 -2
- package/dist/page/pageDecorations.js +18 -5
- package/dist/page/pageLayout.d.ts +77 -1
- package/dist/page/pageLayout.js +186 -9
- package/dist/page/usePageLayout.d.ts +57 -2
- package/dist/page/usePageLayout.js +50 -5
- package/dist/schema/attrRoles.js +3 -2
- package/dist/schema/docxSchema.js +5 -2
- package/dist/schema/editGuard.d.ts +1 -1
- package/dist/schema/guards.js +1 -2
- package/dist/schema/preservedGuards.d.ts +4 -6
- package/dist/schema/preservedGuards.js +0 -9
- package/dist/schema/stories.d.ts +34 -2
- package/dist/schema/stories.js +22 -5
- package/dist/styles/classNames.d.ts +23 -6
- package/dist/styles/classNames.js +23 -6
- package/dist/styles/inlineStyle.d.ts +7 -0
- package/dist/styles/inlineStyle.js +11 -5
- package/dist/styles/visualScale.d.ts +19 -0
- package/dist/styles/visualScale.js +26 -0
- package/dist/styles.css +96 -36
- package/dist/ui/CommentsPanel.d.ts +6 -1
- package/dist/ui/CommentsPanel.js +26 -8
- package/dist/ui/InsertImageButton.d.ts +3 -1
- package/dist/ui/InsertImageButton.js +3 -2
- package/dist/ui/TableMenu.d.ts +5 -2
- package/dist/ui/TableMenu.js +18 -1
- package/dist/ui/TextMenu.d.ts +5 -2
- package/dist/ui/TextMenu.js +21 -6
- package/dist/ui/Toolbar.d.ts +17 -2
- package/dist/ui/Toolbar.js +23 -10
- package/dist/ui/comments/useCommentRailLayout.js +2 -4
- package/dist/ui/noteItems.d.ts +21 -0
- package/dist/ui/noteItems.js +30 -0
- package/dist/ui/notes/NoteAreas.d.ts +73 -0
- package/dist/ui/notes/NoteAreas.js +131 -0
- package/dist/ui/notes/NoteList.d.ts +25 -0
- package/dist/ui/notes/NoteList.js +58 -0
- package/dist/ui/notes/StoryRow.d.ts +75 -0
- package/dist/ui/notes/StoryRow.js +122 -0
- package/dist/ui/notes/noteBands.d.ts +100 -0
- package/dist/ui/notes/noteBands.js +142 -0
- package/dist/ui/notes/noteHeights.d.ts +10 -0
- package/dist/ui/notes/noteHeights.js +24 -0
- package/dist/ui/notes/noteSeparator.d.ts +12 -0
- package/dist/ui/notes/noteSeparator.js +9 -0
- package/dist/ui/notes/useStorySurface.d.ts +68 -0
- package/dist/ui/notes/useStorySurface.js +95 -0
- package/dist/ui/runCommand.d.ts +8 -1
- package/dist/ui/runCommand.js +2 -2
- package/dist/ui/shortcutLabels.d.ts +16 -0
- package/dist/ui/shortcutLabels.js +9 -0
- package/dist/ui/usePageRoom.d.ts +13 -0
- package/dist/ui/usePageRoom.js +31 -0
- package/package.json +2 -1
- package/dist/docx/notes.d.ts +0 -27
- package/dist/docx/notes.js +0 -55
- package/dist/ui/NotesPanel.d.ts +0 -6
- package/dist/ui/NotesPanel.js +0 -33
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 {
|
|
@@ -358,48 +361,82 @@ button.docx-editor-comment-body {
|
|
|
358
361
|
cursor: help;
|
|
359
362
|
}
|
|
360
363
|
|
|
361
|
-
|
|
364
|
+
/*
|
|
365
|
+
* Notes drawn from their stories (`ui/notes`): footnotes over the paper at the foot of the page
|
|
366
|
+
* that refers to them, and notes listed after the last page. The editor hands both boxes the
|
|
367
|
+
* document's own text defaults, and the text is set as `prosemirror-view` sets an editor's, so a
|
|
368
|
+
* note wraps where it would in one.
|
|
369
|
+
*/
|
|
370
|
+
.docx-editor-note-areas {
|
|
371
|
+
position: absolute;
|
|
372
|
+
pointer-events: none;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
.docx-editor-footnote-area {
|
|
376
|
+
position: absolute;
|
|
377
|
+
overflow: hidden;
|
|
378
|
+
pointer-events: auto;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/* The endnotes after the last paragraph, drawn over the room the layout kept for them */
|
|
382
|
+
.docx-editor-endnote-area {
|
|
383
|
+
position: absolute;
|
|
384
|
+
overflow: hidden;
|
|
385
|
+
pointer-events: auto;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
.docx-editor-footnote-area,
|
|
389
|
+
.docx-editor-endnote-area,
|
|
390
|
+
.docx-editor-note-list {
|
|
362
391
|
box-sizing: border-box;
|
|
392
|
+
color: #000000;
|
|
393
|
+
font-family: var(--docx-editor-font-family, sans-serif);
|
|
394
|
+
font-size: var(--docx-editor-font-size, 10pt);
|
|
395
|
+
line-height: var(--docx-editor-line-height, 1.3);
|
|
396
|
+
white-space: break-spaces;
|
|
397
|
+
word-wrap: break-word;
|
|
398
|
+
font-variant-ligatures: none;
|
|
399
|
+
font-feature-settings: "liga" 0;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
.docx-editor-note-list {
|
|
363
403
|
margin: 16px auto 24px;
|
|
364
|
-
padding:
|
|
365
|
-
border-top: 1px solid rgb(0 0 0 / 18%);
|
|
404
|
+
padding-block: 24px;
|
|
366
405
|
background: #ffffff;
|
|
367
406
|
box-shadow: 0 1px 6px rgb(0 0 0 / 18%);
|
|
368
|
-
color: #242424;
|
|
369
407
|
}
|
|
370
408
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
409
|
+
/* A one-pixel rule across the middle of the height the layout keeps for it */
|
|
410
|
+
.docx-editor-note-separator {
|
|
411
|
+
background:
|
|
412
|
+
linear-gradient(#000000, #000000) left center / 100% 1px no-repeat;
|
|
374
413
|
}
|
|
375
414
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
gap: 8px;
|
|
380
|
-
padding: 0;
|
|
381
|
-
margin: 0;
|
|
382
|
-
list-style: none;
|
|
415
|
+
/* Holds the margins of the paragraphs inside it, so a row measures as tall as it stands */
|
|
416
|
+
.docx-editor-note-row {
|
|
417
|
+
display: flow-root;
|
|
383
418
|
}
|
|
384
419
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
420
|
+
/*
|
|
421
|
+
* The note the caret is in.
|
|
422
|
+
*
|
|
423
|
+
* Neither the tint nor the ring around it takes any room, so the row measures the same height
|
|
424
|
+
* whether it is markup or the editing view, and the page keeps the room it already kept.
|
|
425
|
+
*/
|
|
426
|
+
.docx-editor-note-row-open {
|
|
427
|
+
border-radius: 2px;
|
|
428
|
+
background: rgb(49 93 168 / 7%);
|
|
429
|
+
box-shadow: 0 0 0 3px rgb(49 93 168 / 7%);
|
|
390
430
|
}
|
|
391
431
|
|
|
392
|
-
.
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
font-weight: 600;
|
|
396
|
-
white-space: nowrap;
|
|
432
|
+
/* The view one story is edited in. It sets its text from the box it is drawn in */
|
|
433
|
+
.docx-editor-story-body {
|
|
434
|
+
outline: none;
|
|
397
435
|
}
|
|
398
436
|
|
|
399
|
-
.docx-editor-note-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
overflow-wrap: anywhere;
|
|
437
|
+
.docx-editor-note-mark {
|
|
438
|
+
font-size: 0.72em;
|
|
439
|
+
line-height: 0;
|
|
403
440
|
}
|
|
404
441
|
|
|
405
442
|
/* The fallback for browsers that do not know the two properties above (Safari) */
|
|
@@ -434,20 +471,43 @@ button.docx-editor-comment-body {
|
|
|
434
471
|
background: transparent;
|
|
435
472
|
}
|
|
436
473
|
|
|
474
|
+
/*
|
|
475
|
+
* The reader's zoom scales the page layer with a transform and not with the `zoom`
|
|
476
|
+
* property, so the paper is laid out at its own size whatever it is being read at and only
|
|
477
|
+
* the painting is scaled. Under `zoom` Chromium lays every box out on whole device pixels
|
|
478
|
+
* of the scaled rendering, so a table row came out a fraction taller at one zoom than at
|
|
479
|
+
* another and a document sitting near a page boundary broke into a different number of
|
|
480
|
+
* pages for each reader.
|
|
481
|
+
*
|
|
482
|
+
* A transform leaves the layout box at its unscaled size, and a scroll box takes in both
|
|
483
|
+
* that box and the transformed one, so a shrunk layer left in the flow would keep the
|
|
484
|
+
* scroll area of a full-size one. The layer is therefore taken out of the flow, and this
|
|
485
|
+
* box stands in the flow in its place, holding the height the layer is drawn at
|
|
486
|
+
* (`ui/usePageRoom`).
|
|
487
|
+
*/
|
|
488
|
+
.docx-editor-page-box {
|
|
489
|
+
position: relative;
|
|
490
|
+
width: 100%;
|
|
491
|
+
}
|
|
492
|
+
|
|
437
493
|
/*
|
|
438
494
|
* The paper and the boundary guides overlaid on it share one positioning origin.
|
|
439
495
|
*
|
|
440
496
|
* The side padding lives here rather than on the scroll box, because Chromium does not
|
|
441
497
|
* draw a scroll box's own padding past the right edge of sideways-scrolled content.
|
|
442
498
|
* Shrink-wrapping puts the padding beyond the paper even then, and min-width keeps the
|
|
443
|
-
* layer filling the box on wide screens so the paper stays centered
|
|
499
|
+
* layer filling the box on wide screens so the paper stays centered - widened by the
|
|
500
|
+
* reciprocal of the scale, since it is the scaled layer that has to fill the box.
|
|
444
501
|
*/
|
|
445
502
|
.docx-editor-page-layer {
|
|
446
|
-
position:
|
|
447
|
-
|
|
448
|
-
|
|
503
|
+
position: absolute;
|
|
504
|
+
inset: 0 auto auto 0;
|
|
505
|
+
width: max-content;
|
|
506
|
+
min-width: calc(100% / var(--docx-editor-zoom, 1));
|
|
449
507
|
box-sizing: border-box;
|
|
450
508
|
padding: 0 16px;
|
|
509
|
+
transform: scale(var(--docx-editor-zoom, 1));
|
|
510
|
+
transform-origin: 0 0;
|
|
451
511
|
}
|
|
452
512
|
|
|
453
513
|
@media (max-width: 840px) {
|
|
@@ -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
|
};
|
|
@@ -5,5 +5,7 @@ import { type ReactElement } from "react";
|
|
|
5
5
|
export interface InsertImageButtonProps {
|
|
6
6
|
view: EditorView;
|
|
7
7
|
state: EditorState;
|
|
8
|
+
/** Turned off whatever the state says, which is what a surface taking no image sets */
|
|
9
|
+
disabled?: boolean;
|
|
8
10
|
}
|
|
9
|
-
export declare function InsertImageButton({ view, state, }: InsertImageButtonProps): ReactElement;
|
|
11
|
+
export declare function InsertImageButton({ view, state, disabled, }: InsertImageButtonProps): ReactElement;
|
|
@@ -11,7 +11,8 @@ import { ToolbarButton } from "./ToolbarButton.js";
|
|
|
11
11
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
12
12
|
function InsertImageButton({
|
|
13
13
|
view,
|
|
14
|
-
state
|
|
14
|
+
state,
|
|
15
|
+
disabled = false
|
|
15
16
|
}) {
|
|
16
17
|
const pickerRef = useRef(null);
|
|
17
18
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
@@ -20,7 +21,7 @@ function InsertImageButton({
|
|
|
20
21
|
{
|
|
21
22
|
label: "Insert image",
|
|
22
23
|
icon: Image,
|
|
23
|
-
disabled: !view.editable || !canInsertImage(state),
|
|
24
|
+
disabled: disabled || !view.editable || !canInsertImage(state),
|
|
24
25
|
onRun: () => pickerRef.current?.click()
|
|
25
26
|
}
|
|
26
27
|
),
|
package/dist/ui/TableMenu.d.ts
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
|
-
/** Table context menu with an optional Unlock action for whole-cell locks. */
|
|
1
|
+
/** Table context menu with footnote insertion and an optional Unlock action for whole-cell locks. */
|
|
2
2
|
import type { EditorState } from "prosemirror-state";
|
|
3
3
|
import type { EditorView } from "prosemirror-view";
|
|
4
4
|
import { type ReactElement } from "react";
|
|
5
5
|
import { type TableMenuAnchor } from "../editor/plugins/tableContextMenu";
|
|
6
|
+
import type { SurfaceCapabilities } from "../editor/stories/storyView";
|
|
6
7
|
export interface TableMenuProps {
|
|
7
8
|
view: EditorView;
|
|
8
9
|
state: EditorState;
|
|
9
10
|
anchor: TableMenuAnchor;
|
|
11
|
+
/** What the surface holding the caret takes, which the entries that put something in ask */
|
|
12
|
+
takes: SurfaceCapabilities;
|
|
10
13
|
allowLocking?: boolean;
|
|
11
14
|
}
|
|
12
|
-
export declare function TableMenu({ view, state, anchor, allowLocking, }: TableMenuProps): ReactElement;
|
|
15
|
+
export declare function TableMenu({ view, state, anchor, takes, allowLocking, }: TableMenuProps): ReactElement;
|
package/dist/ui/TableMenu.js
CHANGED
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
mergeCells,
|
|
17
17
|
splitCell
|
|
18
18
|
} from "../table/index.js";
|
|
19
|
+
import { noteItems } from "./noteItems.js";
|
|
19
20
|
import { usePanelAtPoint } from "./panelPlacement.js";
|
|
20
21
|
import { commandRunner } from "./runCommand.js";
|
|
21
22
|
import { useDismiss } from "./useDismiss.js";
|
|
@@ -54,10 +55,21 @@ var UNLOCK_GROUP = {
|
|
|
54
55
|
name: "lock",
|
|
55
56
|
items: [{ label: "Unlock", command: unlockSelection }]
|
|
56
57
|
};
|
|
58
|
+
function noteGroup(takes) {
|
|
59
|
+
const notes = noteItems(takes);
|
|
60
|
+
return notes.length === 0 ? null : {
|
|
61
|
+
name: "note",
|
|
62
|
+
items: notes.map((note) => ({
|
|
63
|
+
label: note.label,
|
|
64
|
+
command: note.command
|
|
65
|
+
}))
|
|
66
|
+
};
|
|
67
|
+
}
|
|
57
68
|
function TableMenu({
|
|
58
69
|
view,
|
|
59
70
|
state,
|
|
60
71
|
anchor,
|
|
72
|
+
takes,
|
|
61
73
|
allowLocking = false
|
|
62
74
|
}) {
|
|
63
75
|
const box = useRef(null);
|
|
@@ -74,7 +86,12 @@ function TableMenu({
|
|
|
74
86
|
run(command);
|
|
75
87
|
close();
|
|
76
88
|
};
|
|
77
|
-
const
|
|
89
|
+
const note = noteGroup(takes);
|
|
90
|
+
const groups = [
|
|
91
|
+
...MENU_GROUPS,
|
|
92
|
+
...note === null ? [] : [note],
|
|
93
|
+
...allowLocking ? [UNLOCK_GROUP] : []
|
|
94
|
+
];
|
|
78
95
|
return /* @__PURE__ */ jsx(
|
|
79
96
|
"div",
|
|
80
97
|
{
|
package/dist/ui/TextMenu.d.ts
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
|
-
/** Custom text context menu with clipboard, comment, and optional locking actions. */
|
|
1
|
+
/** Custom text context menu with clipboard, comment, footnote, and optional locking actions. */
|
|
2
2
|
import type { EditorState } from "prosemirror-state";
|
|
3
3
|
import type { EditorView } from "prosemirror-view";
|
|
4
4
|
import { type ReactElement } from "react";
|
|
5
5
|
import { type TextMenuAnchor } from "../editor/plugins/textContextMenu";
|
|
6
|
+
import type { SurfaceCapabilities } from "../editor/stories/storyView";
|
|
6
7
|
export interface TextMenuProps {
|
|
7
8
|
view: EditorView;
|
|
8
9
|
state: EditorState;
|
|
9
10
|
anchor: TextMenuAnchor;
|
|
11
|
+
/** What the surface holding the caret takes, which the entries that put something in ask */
|
|
12
|
+
takes: SurfaceCapabilities;
|
|
10
13
|
/** Whether the entries that lock and unlock a stretch of text are offered */
|
|
11
14
|
allowLocking?: boolean;
|
|
12
15
|
}
|
|
13
|
-
export declare function TextMenu({ view, state, anchor, allowLocking, }: TextMenuProps): ReactElement;
|
|
16
|
+
export declare function TextMenu({ view, state, anchor, takes, allowLocking, }: TextMenuProps): ReactElement;
|
package/dist/ui/TextMenu.js
CHANGED
|
@@ -23,13 +23,14 @@ import {
|
|
|
23
23
|
} from "../editor/plugins/textContextMenu.js";
|
|
24
24
|
import { editsShut } from "../schema/protectionState.js";
|
|
25
25
|
import { editorClassNames } from "../styles/classNames.js";
|
|
26
|
+
import { noteItems } from "./noteItems.js";
|
|
26
27
|
import { usePanelAtPoint } from "./panelPlacement.js";
|
|
27
28
|
import { commandRunner } from "./runCommand.js";
|
|
29
|
+
import { modifierLabels } from "./shortcutLabels.js";
|
|
28
30
|
import { ICON_SIZE } from "./ToolbarButton.js";
|
|
29
31
|
import { useDismiss } from "./useDismiss.js";
|
|
30
32
|
import { useMenuKeyboard } from "./useMenuKeyboard.js";
|
|
31
33
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
32
|
-
var MOD = typeof navigator !== "undefined" && navigator.platform.includes("Mac") ? "\u2318" : "Ctrl+";
|
|
33
34
|
function clipboardCommand(view, command) {
|
|
34
35
|
view.focus();
|
|
35
36
|
document.execCommand(command);
|
|
@@ -95,6 +96,7 @@ function TextMenu({
|
|
|
95
96
|
view,
|
|
96
97
|
state,
|
|
97
98
|
anchor,
|
|
99
|
+
takes,
|
|
98
100
|
allowLocking = false
|
|
99
101
|
}) {
|
|
100
102
|
const box = useRef(null);
|
|
@@ -108,11 +110,12 @@ function TextMenu({
|
|
|
108
110
|
const keys = useMenuKeyboard({ menu: box, onClose: close });
|
|
109
111
|
const selected = !state.selection.empty;
|
|
110
112
|
const shut = selectionTouchesLocked(state);
|
|
113
|
+
const { mod } = modifierLabels();
|
|
111
114
|
const bodyOpen = !editsShut(state);
|
|
112
115
|
const copy = {
|
|
113
116
|
label: "Copy",
|
|
114
117
|
icon: Copy,
|
|
115
|
-
hint: `${
|
|
118
|
+
hint: `${mod}C`,
|
|
116
119
|
enabled: selected,
|
|
117
120
|
run: () => clipboardCommand(view, "copy")
|
|
118
121
|
};
|
|
@@ -121,7 +124,7 @@ function TextMenu({
|
|
|
121
124
|
{
|
|
122
125
|
label: "Cut",
|
|
123
126
|
icon: Scissors,
|
|
124
|
-
hint: `${
|
|
127
|
+
hint: `${mod}X`,
|
|
125
128
|
enabled: selected && !shut,
|
|
126
129
|
run: () => clipboardCommand(view, "cut")
|
|
127
130
|
},
|
|
@@ -129,7 +132,7 @@ function TextMenu({
|
|
|
129
132
|
{
|
|
130
133
|
label: "Paste",
|
|
131
134
|
icon: ClipboardPaste,
|
|
132
|
-
hint: `${
|
|
135
|
+
hint: `${mod}V`,
|
|
133
136
|
enabled: !shut,
|
|
134
137
|
run: () => {
|
|
135
138
|
void pasteFromClipboard(view);
|
|
@@ -143,14 +146,26 @@ function TextMenu({
|
|
|
143
146
|
}
|
|
144
147
|
] : [copy]
|
|
145
148
|
];
|
|
146
|
-
|
|
149
|
+
const anchored = [
|
|
147
150
|
{
|
|
148
151
|
label: "Add comment",
|
|
149
152
|
icon: MessageSquarePlus,
|
|
150
153
|
enabled: canAddComment(state),
|
|
151
154
|
run: () => run(openCommentComposer)
|
|
152
155
|
}
|
|
153
|
-
]
|
|
156
|
+
];
|
|
157
|
+
if (bodyOpen) {
|
|
158
|
+
for (const note of noteItems(takes)) {
|
|
159
|
+
anchored.push({
|
|
160
|
+
label: note.label,
|
|
161
|
+
icon: note.icon,
|
|
162
|
+
hint: note.hint,
|
|
163
|
+
enabled: note.command(state),
|
|
164
|
+
run: () => run(note.command)
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
groups.push(anchored);
|
|
154
169
|
if (allowLocking && bodyOpen) {
|
|
155
170
|
groups.push([lockItem(selectionLock(state), run)]);
|
|
156
171
|
}
|
package/dist/ui/Toolbar.d.ts
CHANGED
|
@@ -2,12 +2,27 @@
|
|
|
2
2
|
import type { EditorState } from "prosemirror-state";
|
|
3
3
|
import type { EditorView } from "prosemirror-view";
|
|
4
4
|
import { type ReactElement } from "react";
|
|
5
|
+
import type { ActiveSurface } from "../editor/stories/storyView";
|
|
5
6
|
import type { FontFallbacks } from "../styles/fontStack";
|
|
6
7
|
import type { DocxEditorPresets } from "./presets";
|
|
7
8
|
import type { DocxEditorZoom } from "./zoom";
|
|
8
|
-
|
|
9
|
+
/** One editing surface the toolbar can act on: the document's own, or a story's */
|
|
10
|
+
export interface ToolbarSurface {
|
|
9
11
|
view: EditorView;
|
|
10
12
|
state: EditorState;
|
|
13
|
+
}
|
|
14
|
+
export interface ToolbarProps {
|
|
15
|
+
/**
|
|
16
|
+
* The document itself, which undo and redo always read: a story edit is written into the
|
|
17
|
+
* document as one change, so the document's history is the one both surfaces share
|
|
18
|
+
* (`editor/stories`).
|
|
19
|
+
*/
|
|
20
|
+
main: ToolbarSurface;
|
|
21
|
+
/**
|
|
22
|
+
* Where the caret is. Everything but undo and redo is asked of it and dispatched to it, so the
|
|
23
|
+
* formatting controls act on the note being edited rather than on the text behind it.
|
|
24
|
+
*/
|
|
25
|
+
active: ActiveSurface;
|
|
11
26
|
fontFallbacks?: FontFallbacks;
|
|
12
27
|
/** Optional values offered by toolbar pickers. */
|
|
13
28
|
presets?: DocxEditorPresets;
|
|
@@ -16,4 +31,4 @@ export interface ToolbarProps {
|
|
|
16
31
|
zoom: DocxEditorZoom;
|
|
17
32
|
onZoomChange: (zoom: DocxEditorZoom) => void;
|
|
18
33
|
}
|
|
19
|
-
export declare function Toolbar({
|
|
34
|
+
export declare function Toolbar({ main, active, fontFallbacks, presets, onToggleComments, commentsOpen, zoom, onZoomChange, }: ToolbarProps): ReactElement;
|
package/dist/ui/Toolbar.js
CHANGED
|
@@ -272,7 +272,11 @@ function ParagraphLayoutGroup({
|
|
|
272
272
|
)
|
|
273
273
|
] });
|
|
274
274
|
}
|
|
275
|
-
function ListGroup({
|
|
275
|
+
function ListGroup({
|
|
276
|
+
state,
|
|
277
|
+
run,
|
|
278
|
+
disabled
|
|
279
|
+
}) {
|
|
276
280
|
const kind = activeListKind(state);
|
|
277
281
|
return /* @__PURE__ */ jsx("div", { className: editorClassNames.toolbarGroup, children: LIST_TOGGLES.map((toggle) => /* @__PURE__ */ jsx(
|
|
278
282
|
ToolbarButton,
|
|
@@ -280,7 +284,7 @@ function ListGroup({ state, run }) {
|
|
|
280
284
|
label: toggle.label,
|
|
281
285
|
icon: toggle.icon,
|
|
282
286
|
pressed: kind === toggle.kind,
|
|
283
|
-
disabled: !toggle.command(state),
|
|
287
|
+
disabled: disabled || !toggle.command(state),
|
|
284
288
|
onRun: () => run(toggle.command)
|
|
285
289
|
},
|
|
286
290
|
toggle.kind
|
|
@@ -299,8 +303,8 @@ function IndentGroup({ state, run }) {
|
|
|
299
303
|
)) });
|
|
300
304
|
}
|
|
301
305
|
function Toolbar({
|
|
302
|
-
|
|
303
|
-
|
|
306
|
+
main,
|
|
307
|
+
active,
|
|
304
308
|
fontFallbacks,
|
|
305
309
|
presets,
|
|
306
310
|
onToggleComments,
|
|
@@ -308,7 +312,9 @@ function Toolbar({
|
|
|
308
312
|
zoom,
|
|
309
313
|
onZoomChange
|
|
310
314
|
}) {
|
|
315
|
+
const { view, state, takes } = active;
|
|
311
316
|
const run = commandRunner(view);
|
|
317
|
+
const runOnMain = commandRunner(main.view, view);
|
|
312
318
|
const bar = useRef(null);
|
|
313
319
|
const keys = useLinearWalk({
|
|
314
320
|
container: bar,
|
|
@@ -340,7 +346,7 @@ function Toolbar({
|
|
|
340
346
|
onKeyDown,
|
|
341
347
|
...hushedAttribute(hushed),
|
|
342
348
|
children: [
|
|
343
|
-
/* @__PURE__ */ jsx(HistoryGroup, { state, run }),
|
|
349
|
+
/* @__PURE__ */ jsx(HistoryGroup, { state: main.state, run: runOnMain }),
|
|
344
350
|
/* @__PURE__ */ jsx(ZoomSelect, { zoom, onZoomChange }),
|
|
345
351
|
/* @__PURE__ */ jsx(Separator, {}),
|
|
346
352
|
/* @__PURE__ */ jsxs("div", { className: editorClassNames.toolbarGroup, children: [
|
|
@@ -394,7 +400,7 @@ function Toolbar({
|
|
|
394
400
|
}
|
|
395
401
|
),
|
|
396
402
|
/* @__PURE__ */ jsx(Separator, {}),
|
|
397
|
-
/* @__PURE__ */ jsx(ListGroup, { state, run }),
|
|
403
|
+
/* @__PURE__ */ jsx(ListGroup, { state, run, disabled: !takes.has("list") }),
|
|
398
404
|
/* @__PURE__ */ jsx(IndentGroup, { state, run }),
|
|
399
405
|
/* @__PURE__ */ jsx(Separator, {}),
|
|
400
406
|
/* @__PURE__ */ jsx(
|
|
@@ -414,17 +420,24 @@ function Toolbar({
|
|
|
414
420
|
label: "Insert table",
|
|
415
421
|
icon: Table,
|
|
416
422
|
panel: "dialog",
|
|
417
|
-
disabled: !canInsertTable(state),
|
|
423
|
+
disabled: !takes.has("table") || !canInsertTable(state),
|
|
418
424
|
children: ({ close, takeFocus }) => /* @__PURE__ */ jsx(TableSizePicker, { run, close, takeFocus })
|
|
419
425
|
}
|
|
420
426
|
),
|
|
421
|
-
/* @__PURE__ */ jsx(
|
|
427
|
+
/* @__PURE__ */ jsx(
|
|
428
|
+
InsertImageButton,
|
|
429
|
+
{
|
|
430
|
+
view,
|
|
431
|
+
state,
|
|
432
|
+
disabled: !takes.has("image")
|
|
433
|
+
}
|
|
434
|
+
),
|
|
422
435
|
/* @__PURE__ */ jsx(
|
|
423
436
|
ToolbarButton,
|
|
424
437
|
{
|
|
425
438
|
label: "Link",
|
|
426
439
|
icon: Link,
|
|
427
|
-
disabled: !openLinkPanel(state),
|
|
440
|
+
disabled: !takes.has("link") || !openLinkPanel(state),
|
|
428
441
|
onRun: () => run(openLinkPanel)
|
|
429
442
|
}
|
|
430
443
|
),
|
|
@@ -434,7 +447,7 @@ function Toolbar({
|
|
|
434
447
|
label: commentsOpen ? "Hide comments" : "Show comments",
|
|
435
448
|
icon: MessagesSquare,
|
|
436
449
|
pressed: commentsOpen,
|
|
437
|
-
disabled: !onToggleComments,
|
|
450
|
+
disabled: !takes.has("comment") || !onToggleComments,
|
|
438
451
|
onRun: () => onToggleComments?.()
|
|
439
452
|
}
|
|
440
453
|
)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// src/ui/comments/useCommentRailLayout.ts
|
|
2
2
|
import { useLayoutEffect, useRef, useState } from "react";
|
|
3
3
|
import { editorClassNames } from "../../styles/classNames.js";
|
|
4
|
+
import { visualScaleOf } from "../../styles/visualScale.js";
|
|
4
5
|
var COMPOSER_POSITION = "composer";
|
|
5
6
|
var VERTICAL_SCROLLBAR_CLEARANCE = 16;
|
|
6
7
|
function estimatedCardHeight(comment) {
|
|
@@ -90,10 +91,7 @@ function useCommentRailLayout({
|
|
|
90
91
|
const reserveCommentWidth = () => {
|
|
91
92
|
if (!pageLayer) return;
|
|
92
93
|
const drawnWidth = rail.getBoundingClientRect().width || rail.offsetWidth;
|
|
93
|
-
const
|
|
94
|
-
frameWindow?.getComputedStyle(pageLayer).zoom ?? ""
|
|
95
|
-
);
|
|
96
|
-
const scale = Number.isFinite(computedZoom) && computedZoom > 0 ? computedZoom : 1;
|
|
94
|
+
const scale = visualScaleOf(pageLayer);
|
|
97
95
|
pageLayer.style.paddingRight = `${(drawnWidth + 16 + VERTICAL_SCROLLBAR_CLEARANCE) / scale}px`;
|
|
98
96
|
};
|
|
99
97
|
const placeBesidePaper = () => {
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The controls that put a note in, which every menu over the text draws its rows from.
|
|
3
|
+
*
|
|
4
|
+
* A right click is answered by one of two menus - the text menu, and the table menu wherever the
|
|
5
|
+
* click lands in a cell with nothing selected (`editor/plugins/textContextMenu`) - and a note goes
|
|
6
|
+
* into a table cell as readily as into a paragraph, so each row is written down once here. The
|
|
7
|
+
* command asked without a dispatch is what each menu draws a dead row from, and whether the rows
|
|
8
|
+
* are there at all is the surface's own declaration (`editor/stories/storyView`).
|
|
9
|
+
*/
|
|
10
|
+
import type { LucideIcon } from "lucide-react";
|
|
11
|
+
import type { Command } from "prosemirror-state";
|
|
12
|
+
import type { SurfaceCapabilities } from "../editor/stories/storyView";
|
|
13
|
+
export interface NoteItem {
|
|
14
|
+
readonly label: string;
|
|
15
|
+
readonly icon: LucideIcon;
|
|
16
|
+
/** The shortcut for the same action, for a menu that labels its rows with theirs */
|
|
17
|
+
readonly hint: string;
|
|
18
|
+
readonly command: Command;
|
|
19
|
+
}
|
|
20
|
+
/** The rows a menu draws, and none for a surface that takes no note */
|
|
21
|
+
export declare function noteItems(takes: SurfaceCapabilities): readonly NoteItem[];
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// src/ui/noteItems.ts
|
|
2
|
+
import { ListEnd, Superscript } from "lucide-react";
|
|
3
|
+
import { insertEndnote, insertFootnote } from "../editor/commands/noteCommands.js";
|
|
4
|
+
import { NOTE_KEYS } from "../editor/plugins/keymap.js";
|
|
5
|
+
import { modifierLabels } from "./shortcutLabels.js";
|
|
6
|
+
function keyLetter(binding) {
|
|
7
|
+
return (binding.split("-").at(-1) ?? "").toUpperCase();
|
|
8
|
+
}
|
|
9
|
+
var NO_ITEMS = [];
|
|
10
|
+
function noteItems(takes) {
|
|
11
|
+
if (!takes.has("note")) return NO_ITEMS;
|
|
12
|
+
const { mod, alt } = modifierLabels();
|
|
13
|
+
return [
|
|
14
|
+
{
|
|
15
|
+
label: "Insert footnote",
|
|
16
|
+
icon: Superscript,
|
|
17
|
+
hint: `${mod}${alt}${keyLetter(NOTE_KEYS.footnote)}`,
|
|
18
|
+
command: insertFootnote
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
label: "Insert endnote",
|
|
22
|
+
icon: ListEnd,
|
|
23
|
+
hint: `${mod}${alt}${keyLetter(NOTE_KEYS.endnote)}`,
|
|
24
|
+
command: insertEndnote
|
|
25
|
+
}
|
|
26
|
+
];
|
|
27
|
+
}
|
|
28
|
+
export {
|
|
29
|
+
noteItems
|
|
30
|
+
};
|