@portone/docx-editor 0.1.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 +7 -0
- package/CONTRIBUTING.md +63 -0
- package/LICENSE +203 -0
- package/README.md +48 -0
- package/assets/editor.png +0 -0
- package/dist/DocxEditor.d.ts +123 -0
- package/dist/DocxEditor.js +348 -0
- package/dist/core.d.ts +36 -0
- package/dist/core.js +38 -0
- package/dist/docx/comments/constants.d.ts +8 -0
- package/dist/docx/comments/constants.js +20 -0
- package/dist/docx/comments/model.d.ts +37 -0
- package/dist/docx/comments/model.js +104 -0
- package/dist/docx/comments/reading.d.ts +36 -0
- package/dist/docx/comments/reading.js +149 -0
- package/dist/docx/comments/writing.d.ts +11 -0
- package/dist/docx/comments/writing.js +327 -0
- package/dist/docx/comments.d.ts +3 -0
- package/dist/docx/comments.js +4 -0
- package/dist/docx/container.d.ts +12 -0
- package/dist/docx/container.js +130 -0
- package/dist/docx/documentSettings.d.ts +4 -0
- package/dist/docx/documentSettings.js +14 -0
- package/dist/docx/exportDocx.d.ts +15 -0
- package/dist/docx/exportDocx.js +163 -0
- package/dist/docx/exportRefs.d.ts +16 -0
- package/dist/docx/exportRefs.js +10 -0
- package/dist/docx/formatting/direct.d.ts +30 -0
- package/dist/docx/formatting/direct.js +201 -0
- package/dist/docx/formatting/effectiveParagraph.d.ts +15 -0
- package/dist/docx/formatting/effectiveParagraph.js +81 -0
- package/dist/docx/formatting/styles.d.ts +67 -0
- package/dist/docx/formatting/styles.js +165 -0
- package/dist/docx/formatting/tabStops.d.ts +8 -0
- package/dist/docx/formatting/tabStops.js +65 -0
- package/dist/docx/formatting.d.ts +4 -0
- package/dist/docx/formatting.js +4 -0
- package/dist/docx/headersFooters.d.ts +36 -0
- package/dist/docx/headersFooters.js +218 -0
- package/dist/docx/hyperlink.d.ts +75 -0
- package/dist/docx/hyperlink.js +71 -0
- package/dist/docx/importDocx.d.ts +21 -0
- package/dist/docx/importDocx.js +263 -0
- package/dist/docx/importParagraph.d.ts +32 -0
- package/dist/docx/importParagraph.js +308 -0
- package/dist/docx/importTable.d.ts +9 -0
- package/dist/docx/importTable.js +275 -0
- package/dist/docx/media.d.ts +60 -0
- package/dist/docx/media.js +213 -0
- package/dist/docx/notes.d.ts +22 -0
- package/dist/docx/notes.js +71 -0
- package/dist/docx/pageGeometry.d.ts +75 -0
- package/dist/docx/pageGeometry.js +88 -0
- package/dist/docx/paraProps.d.ts +74 -0
- package/dist/docx/paraProps.js +167 -0
- package/dist/docx/propsXml.d.ts +49 -0
- package/dist/docx/propsXml.js +302 -0
- package/dist/docx/relationships.d.ts +42 -0
- package/dist/docx/relationships.js +94 -0
- package/dist/docx/runProps.d.ts +51 -0
- package/dist/docx/runProps.js +207 -0
- package/dist/docx/scan.d.ts +20 -0
- package/dist/docx/scan.js +110 -0
- package/dist/docx/sdt.d.ts +56 -0
- package/dist/docx/sdt.js +104 -0
- package/dist/docx/sdtProps.d.ts +18 -0
- package/dist/docx/sdtProps.js +11 -0
- package/dist/docx/serializeBlock.d.ts +4 -0
- package/dist/docx/serializeBlock.js +30 -0
- package/dist/docx/serializeParagraph.d.ts +15 -0
- package/dist/docx/serializeParagraph.js +208 -0
- package/dist/docx/serializeTable.d.ts +12 -0
- package/dist/docx/serializeTable.js +134 -0
- package/dist/docx/session.d.ts +81 -0
- package/dist/docx/session.js +79 -0
- package/dist/docx/tableFormatting/editing.d.ts +64 -0
- package/dist/docx/tableFormatting/editing.js +318 -0
- package/dist/docx/tableFormatting/reading.d.ts +96 -0
- package/dist/docx/tableFormatting/reading.js +221 -0
- package/dist/docx/tableFormatting.d.ts +2 -0
- package/dist/docx/tableFormatting.js +3 -0
- package/dist/docx/tableTemplate.d.ts +22 -0
- package/dist/docx/tableTemplate.js +111 -0
- package/dist/docx/theme.d.ts +40 -0
- package/dist/docx/theme.js +61 -0
- package/dist/docx/uniqueControls.d.ts +14 -0
- package/dist/docx/uniqueControls.js +62 -0
- package/dist/download.d.ts +57 -0
- package/dist/download.js +44 -0
- package/dist/editor/clipboard/images.d.ts +14 -0
- package/dist/editor/clipboard/images.js +158 -0
- package/dist/editor/clipboard/inlineFormatting.d.ts +21 -0
- package/dist/editor/clipboard/inlineFormatting.js +232 -0
- package/dist/editor/commands/breakCommands.d.ts +8 -0
- package/dist/editor/commands/breakCommands.js +19 -0
- package/dist/editor/commands/canRunCommand.d.ts +6 -0
- package/dist/editor/commands/canRunCommand.js +10 -0
- package/dist/editor/commands/commentCommands.d.ts +3 -0
- package/dist/editor/commands/commentCommands.js +4 -0
- package/dist/editor/commands/comments/editing.d.ts +21 -0
- package/dist/editor/commands/comments/editing.js +305 -0
- package/dist/editor/commands/comments/model.d.ts +31 -0
- package/dist/editor/commands/comments/model.js +0 -0
- package/dist/editor/commands/comments/reading.d.ts +8 -0
- package/dist/editor/commands/comments/reading.js +58 -0
- package/dist/editor/commands/formatting/editing.d.ts +32 -0
- package/dist/editor/commands/formatting/editing.js +138 -0
- package/dist/editor/commands/formatting/reading.d.ts +57 -0
- package/dist/editor/commands/formatting/reading.js +82 -0
- package/dist/editor/commands/formatting/shared.d.ts +32 -0
- package/dist/editor/commands/formatting/shared.js +53 -0
- package/dist/editor/commands/formattingCommands.d.ts +2 -0
- package/dist/editor/commands/formattingCommands.js +3 -0
- package/dist/editor/commands/historyCommands.d.ts +11 -0
- package/dist/editor/commands/historyCommands.js +16 -0
- package/dist/editor/commands/indentCommands.d.ts +15 -0
- package/dist/editor/commands/indentCommands.js +44 -0
- package/dist/editor/commands/index.d.ts +82 -0
- package/dist/editor/commands/index.js +171 -0
- package/dist/editor/commands/linkCommands.d.ts +56 -0
- package/dist/editor/commands/linkCommands.js +146 -0
- package/dist/editor/commands/listCommands.d.ts +58 -0
- package/dist/editor/commands/listCommands.js +140 -0
- package/dist/editor/commands/lockCommands.d.ts +47 -0
- package/dist/editor/commands/lockCommands.js +198 -0
- package/dist/editor/commands/noteQueries.d.ts +12 -0
- package/dist/editor/commands/noteQueries.js +29 -0
- package/dist/editor/commands/paragraphCommands.d.ts +59 -0
- package/dist/editor/commands/paragraphCommands.js +113 -0
- package/dist/editor/commands/spacingCommands.d.ts +23 -0
- package/dist/editor/commands/spacingCommands.js +48 -0
- package/dist/editor/commands/tabCommands.d.ts +5 -0
- package/dist/editor/commands/tabCommands.js +65 -0
- package/dist/editor/createEditor.d.ts +59 -0
- package/dist/editor/createEditor.js +160 -0
- package/dist/editor/documentStyles.d.ts +60 -0
- package/dist/editor/documentStyles.js +80 -0
- package/dist/editor/externalClipboard.d.ts +11 -0
- package/dist/editor/externalClipboard.js +323 -0
- package/dist/editor/imageFiles.d.ts +74 -0
- package/dist/editor/imageFiles.js +173 -0
- package/dist/editor/insertImage.d.ts +31 -0
- package/dist/editor/insertImage.js +35 -0
- package/dist/editor/insertTable.d.ts +18 -0
- package/dist/editor/insertTable.js +44 -0
- package/dist/editor/paragraphEdits.d.ts +42 -0
- package/dist/editor/paragraphEdits.js +59 -0
- package/dist/editor/plainText.d.ts +3 -0
- package/dist/editor/plainText.js +50 -0
- package/dist/editor/plugins/bookmarkProtection.d.ts +3 -0
- package/dist/editor/plugins/bookmarkProtection.js +28 -0
- package/dist/editor/plugins/columnResize.d.ts +27 -0
- package/dist/editor/plugins/columnResize.js +192 -0
- package/dist/editor/plugins/commentDecorations.d.ts +3 -0
- package/dist/editor/plugins/commentDecorations.js +23 -0
- package/dist/editor/plugins/commentReservations.d.ts +5 -0
- package/dist/editor/plugins/commentReservations.js +26 -0
- package/dist/editor/plugins/dropCaret.d.ts +10 -0
- package/dist/editor/plugins/dropCaret.js +11 -0
- package/dist/editor/plugins/imagePaste.d.ts +8 -0
- package/dist/editor/plugins/imagePaste.js +265 -0
- package/dist/editor/plugins/keymap.d.ts +5 -0
- package/dist/editor/plugins/keymap.js +94 -0
- package/dist/editor/plugins/linkPanel.d.ts +23 -0
- package/dist/editor/plugins/linkPanel.js +39 -0
- package/dist/editor/plugins/listInputRules.d.ts +14 -0
- package/dist/editor/plugins/listInputRules.js +36 -0
- package/dist/editor/plugins/lockedContent.d.ts +6 -0
- package/dist/editor/plugins/lockedContent.js +41 -0
- package/dist/editor/plugins/noteProtection.d.ts +3 -0
- package/dist/editor/plugins/noteProtection.js +25 -0
- package/dist/editor/plugins/numberingDecorations.d.ts +42 -0
- package/dist/editor/plugins/numberingDecorations.js +110 -0
- package/dist/editor/plugins/rowResize.d.ts +15 -0
- package/dist/editor/plugins/rowResize.js +196 -0
- package/dist/editor/plugins/styledParagraphs.d.ts +15 -0
- package/dist/editor/plugins/styledParagraphs.js +65 -0
- package/dist/editor/plugins/tabCaret.d.ts +3 -0
- package/dist/editor/plugins/tabCaret.js +126 -0
- package/dist/editor/plugins/tabDecorations.d.ts +11 -0
- package/dist/editor/plugins/tabDecorations.js +87 -0
- package/dist/editor/plugins/tabLayout.d.ts +16 -0
- package/dist/editor/plugins/tabLayout.js +328 -0
- package/dist/editor/plugins/tabPointer.d.ts +7 -0
- package/dist/editor/plugins/tabPointer.js +95 -0
- package/dist/editor/plugins/tableContextMenu.d.ts +21 -0
- package/dist/editor/plugins/tableContextMenu.js +88 -0
- package/dist/editor/plugins/textContextMenu.d.ts +15 -0
- package/dist/editor/plugins/textContextMenu.js +90 -0
- package/dist/editor/views/imageResize.d.ts +53 -0
- package/dist/editor/views/imageResize.js +167 -0
- package/dist/editor/views/runMarkView.d.ts +15 -0
- package/dist/editor/views/runMarkView.js +9 -0
- package/dist/editor/views/tableView.d.ts +24 -0
- package/dist/editor/views/tableView.js +32 -0
- package/dist/index.d.ts +44 -0
- package/dist/index.js +38 -0
- package/dist/model/format.d.ts +240 -0
- package/dist/model/format.js +305 -0
- package/dist/model/tabStops.d.ts +17 -0
- package/dist/model/tabStops.js +22 -0
- package/dist/numbering/listTemplate.d.ts +29 -0
- package/dist/numbering/listTemplate.js +83 -0
- package/dist/numbering/markers.d.ts +24 -0
- package/dist/numbering/markers.js +93 -0
- package/dist/numbering/parseNumbering.d.ts +65 -0
- package/dist/numbering/parseNumbering.js +116 -0
- package/dist/numbering/writeNumbering.d.ts +13 -0
- package/dist/numbering/writeNumbering.js +49 -0
- package/dist/ooxml/errors.d.ts +39 -0
- package/dist/ooxml/errors.js +21 -0
- package/dist/ooxml/image.d.ts +88 -0
- package/dist/ooxml/image.js +106 -0
- package/dist/ooxml/tabStops.d.ts +3 -0
- package/dist/ooxml/tabStops.js +51 -0
- package/dist/ooxml/units.d.ts +37 -0
- package/dist/ooxml/units.js +116 -0
- package/dist/ooxml/xml.d.ts +36 -0
- package/dist/ooxml/xml.js +89 -0
- package/dist/page/PageGuides.d.ts +16 -0
- package/dist/page/PageGuides.js +114 -0
- package/dist/page/measureBlocks.d.ts +25 -0
- package/dist/page/measureBlocks.js +80 -0
- package/dist/page/pageDecorations.d.ts +36 -0
- package/dist/page/pageDecorations.js +211 -0
- package/dist/page/pageLayout.d.ts +176 -0
- package/dist/page/pageLayout.js +157 -0
- package/dist/page/tableMeasurements.d.ts +10 -0
- package/dist/page/tableMeasurements.js +115 -0
- package/dist/page/usePageLayout.d.ts +57 -0
- package/dist/page/usePageLayout.js +141 -0
- package/dist/schema/docxSchema.d.ts +9 -0
- package/dist/schema/docxSchema.js +863 -0
- package/dist/schema/index.d.ts +2 -0
- package/dist/schema/index.js +9 -0
- package/dist/schema/locks.d.ts +112 -0
- package/dist/schema/locks.js +211 -0
- package/dist/schema/rendering.d.ts +6 -0
- package/dist/schema/rendering.js +46 -0
- package/dist/styles/classNames.d.ts +214 -0
- package/dist/styles/classNames.js +208 -0
- package/dist/styles/colors.d.ts +11 -0
- package/dist/styles/colors.js +14 -0
- package/dist/styles/fontSizes.d.ts +6 -0
- package/dist/styles/fontSizes.js +20 -0
- package/dist/styles/fontStack.d.ts +55 -0
- package/dist/styles/fontStack.js +262 -0
- package/dist/styles/fonts.d.ts +6 -0
- package/dist/styles/fonts.js +11 -0
- package/dist/styles/inlineStyle.d.ts +67 -0
- package/dist/styles/inlineStyle.js +212 -0
- package/dist/styles/lineSpacings.d.ts +11 -0
- package/dist/styles/lineSpacings.js +10 -0
- package/dist/styles.css +1650 -0
- package/dist/table/cellBorders.d.ts +12 -0
- package/dist/table/cellBorders.js +9 -0
- package/dist/table/cellFormatting.d.ts +57 -0
- package/dist/table/cellFormatting.js +173 -0
- package/dist/table/columns.d.ts +25 -0
- package/dist/table/columns.js +155 -0
- package/dist/table/commands.d.ts +19 -0
- package/dist/table/commands.js +63 -0
- package/dist/table/format.d.ts +35 -0
- package/dist/table/format.js +26 -0
- package/dist/table/gridBorders.d.ts +42 -0
- package/dist/table/gridBorders.js +192 -0
- package/dist/table/index.d.ts +16 -0
- package/dist/table/index.js +52 -0
- package/dist/table/merge.d.ts +36 -0
- package/dist/table/merge.js +125 -0
- package/dist/table/resize.d.ts +55 -0
- package/dist/table/resize.js +66 -0
- package/dist/table/rowResize.d.ts +14 -0
- package/dist/table/rowResize.js +44 -0
- package/dist/table/rows.d.ts +32 -0
- package/dist/table/rows.js +89 -0
- package/dist/table/widths.d.ts +69 -0
- package/dist/table/widths.js +97 -0
- package/dist/ui/AlignMenu.d.ts +15 -0
- package/dist/ui/AlignMenu.js +64 -0
- package/dist/ui/CellStyleGroup.d.ts +15 -0
- package/dist/ui/CellStyleGroup.js +232 -0
- package/dist/ui/ColorPicker.d.ts +13 -0
- package/dist/ui/ColorPicker.js +165 -0
- package/dist/ui/CommentsPanel.d.ts +15 -0
- package/dist/ui/CommentsPanel.js +348 -0
- package/dist/ui/FontFamilySelect.d.ts +14 -0
- package/dist/ui/FontFamilySelect.js +59 -0
- package/dist/ui/FontSizeSelect.d.ts +11 -0
- package/dist/ui/FontSizeSelect.js +49 -0
- package/dist/ui/InsertImageButton.d.ts +9 -0
- package/dist/ui/InsertImageButton.js +47 -0
- package/dist/ui/LinkCard.d.ts +12 -0
- package/dist/ui/LinkCard.js +127 -0
- package/dist/ui/LinkPanel.d.ts +9 -0
- package/dist/ui/LinkPanel.js +121 -0
- package/dist/ui/MenuChoice.d.ts +11 -0
- package/dist/ui/MenuChoice.js +32 -0
- package/dist/ui/NotesPanel.d.ts +6 -0
- package/dist/ui/NotesPanel.js +33 -0
- package/dist/ui/ParagraphStyleSelect.d.ts +11 -0
- package/dist/ui/ParagraphStyleSelect.js +62 -0
- package/dist/ui/Popover.d.ts +22 -0
- package/dist/ui/Popover.js +74 -0
- package/dist/ui/SpacingMenu.d.ts +12 -0
- package/dist/ui/SpacingMenu.js +54 -0
- package/dist/ui/TableMenu.d.ts +12 -0
- package/dist/ui/TableMenu.js +112 -0
- package/dist/ui/TableSizePicker.d.ts +9 -0
- package/dist/ui/TableSizePicker.js +53 -0
- package/dist/ui/TextMenu.d.ts +15 -0
- package/dist/ui/TextMenu.js +194 -0
- package/dist/ui/Toolbar.d.ts +19 -0
- package/dist/ui/Toolbar.js +448 -0
- package/dist/ui/ToolbarButton.d.ts +23 -0
- package/dist/ui/ToolbarButton.js +56 -0
- package/dist/ui/Tooltip.d.ts +19 -0
- package/dist/ui/Tooltip.js +47 -0
- package/dist/ui/ZoomSelect.d.ts +7 -0
- package/dist/ui/ZoomSelect.js +44 -0
- package/dist/ui/comments/CommentComposer.d.ts +17 -0
- package/dist/ui/comments/CommentComposer.js +59 -0
- package/dist/ui/comments/useCommentRailLayout.d.ts +18 -0
- package/dist/ui/comments/useCommentRailLayout.js +183 -0
- package/dist/ui/keyboardWalk.d.ts +22 -0
- package/dist/ui/keyboardWalk.js +70 -0
- package/dist/ui/openAddress.d.ts +11 -0
- package/dist/ui/openAddress.js +16 -0
- package/dist/ui/panelPlacement.d.ts +51 -0
- package/dist/ui/panelPlacement.js +71 -0
- package/dist/ui/presets.d.ts +11 -0
- package/dist/ui/presets.js +0 -0
- package/dist/ui/rovingFocus.d.ts +20 -0
- package/dist/ui/rovingFocus.js +61 -0
- package/dist/ui/runCommand.d.ts +5 -0
- package/dist/ui/runCommand.js +13 -0
- package/dist/ui/useDismiss.d.ts +6 -0
- package/dist/ui/useDismiss.js +31 -0
- package/dist/ui/useFitWidthZoom.d.ts +2 -0
- package/dist/ui/useFitWidthZoom.js +26 -0
- package/dist/ui/useGridKeyboard.d.ts +12 -0
- package/dist/ui/useGridKeyboard.js +90 -0
- package/dist/ui/useMenuKeyboard.d.ts +12 -0
- package/dist/ui/useMenuKeyboard.js +56 -0
- package/dist/ui/zoom.d.ts +4 -0
- package/dist/ui/zoom.js +21 -0
- package/package.json +120 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// src/ui/comments/CommentComposer.tsx
|
|
2
|
+
import {
|
|
3
|
+
useLayoutEffect,
|
|
4
|
+
useRef,
|
|
5
|
+
useState
|
|
6
|
+
} from "react";
|
|
7
|
+
import { editorClassNames } from "../../styles/classNames.js";
|
|
8
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
9
|
+
function FocusedTextarea({
|
|
10
|
+
label,
|
|
11
|
+
value,
|
|
12
|
+
onChange
|
|
13
|
+
}) {
|
|
14
|
+
const input = useRef(null);
|
|
15
|
+
useLayoutEffect(() => input.current?.focus(), []);
|
|
16
|
+
return /* @__PURE__ */ jsx(
|
|
17
|
+
"textarea",
|
|
18
|
+
{
|
|
19
|
+
ref: input,
|
|
20
|
+
className: editorClassNames.commentInput,
|
|
21
|
+
"aria-label": label,
|
|
22
|
+
value,
|
|
23
|
+
onChange: (event) => onChange(event.target.value)
|
|
24
|
+
}
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
function CommentComposer({
|
|
28
|
+
author,
|
|
29
|
+
label,
|
|
30
|
+
submitLabel,
|
|
31
|
+
onSubmit,
|
|
32
|
+
onClose
|
|
33
|
+
}) {
|
|
34
|
+
const [text, setText] = useState("");
|
|
35
|
+
const submit = (event) => {
|
|
36
|
+
event.preventDefault();
|
|
37
|
+
if (onSubmit(text)) onClose();
|
|
38
|
+
};
|
|
39
|
+
return /* @__PURE__ */ jsxs("form", { className: editorClassNames.commentComposer, onSubmit: submit, children: [
|
|
40
|
+
/* @__PURE__ */ jsx("div", { className: editorClassNames.commentMeta, children: author.name }),
|
|
41
|
+
/* @__PURE__ */ jsx(FocusedTextarea, { label, value: text, onChange: setText }),
|
|
42
|
+
/* @__PURE__ */ jsxs("div", { className: editorClassNames.commentActions, children: [
|
|
43
|
+
/* @__PURE__ */ jsx("button", { type: "button", onClick: onClose, children: "Cancel" }),
|
|
44
|
+
/* @__PURE__ */ jsx(
|
|
45
|
+
"button",
|
|
46
|
+
{
|
|
47
|
+
type: "submit",
|
|
48
|
+
className: editorClassNames.commentPrimaryAction,
|
|
49
|
+
disabled: text.trim().length === 0,
|
|
50
|
+
children: submitLabel
|
|
51
|
+
}
|
|
52
|
+
)
|
|
53
|
+
] })
|
|
54
|
+
] });
|
|
55
|
+
}
|
|
56
|
+
export {
|
|
57
|
+
CommentComposer,
|
|
58
|
+
FocusedTextarea
|
|
59
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { EditorView } from "prosemirror-view";
|
|
2
|
+
import { type RefObject } from "react";
|
|
3
|
+
import type { DocumentComment } from "../../editor/commands/commentCommands";
|
|
4
|
+
export declare const COMPOSER_POSITION = "composer";
|
|
5
|
+
interface CommentRailLayoutOptions {
|
|
6
|
+
panel: RefObject<HTMLElement | null>;
|
|
7
|
+
view: EditorView;
|
|
8
|
+
scrollContainer: HTMLElement | null;
|
|
9
|
+
visible: readonly DocumentComment[];
|
|
10
|
+
composerOpen: boolean;
|
|
11
|
+
composerSelectionPos: number;
|
|
12
|
+
allCommentsOpen: boolean;
|
|
13
|
+
}
|
|
14
|
+
export declare function useCommentRailLayout({ panel, view, scrollContainer, visible, composerOpen, composerSelectionPos, allCommentsOpen, }: CommentRailLayoutOptions): {
|
|
15
|
+
positions: ReadonlyMap<string, number>;
|
|
16
|
+
canvasHeight: number;
|
|
17
|
+
};
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
// src/ui/comments/useCommentRailLayout.ts
|
|
2
|
+
import { useLayoutEffect, useRef, useState } from "react";
|
|
3
|
+
import { editorClassNames } from "../../styles/classNames.js";
|
|
4
|
+
var COMPOSER_POSITION = "composer";
|
|
5
|
+
var VERTICAL_SCROLLBAR_CLEARANCE = 16;
|
|
6
|
+
function estimatedCardHeight(comment) {
|
|
7
|
+
return 150 + comment.replies.length * 88;
|
|
8
|
+
}
|
|
9
|
+
function topAt(view, pos) {
|
|
10
|
+
try {
|
|
11
|
+
return view.coordsAtPos(pos).top;
|
|
12
|
+
} catch {
|
|
13
|
+
return 0;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
function anchorPositions(comments, view, scrollContainer, rail, composerOpen, selectionPos) {
|
|
17
|
+
const root = scrollContainer.getBoundingClientRect();
|
|
18
|
+
const heights = /* @__PURE__ */ new Map();
|
|
19
|
+
for (const element of rail.querySelectorAll(
|
|
20
|
+
"[data-comment-position]"
|
|
21
|
+
)) {
|
|
22
|
+
const key = element.dataset.commentPosition;
|
|
23
|
+
if (key) heights.set(key, element.getBoundingClientRect().height);
|
|
24
|
+
}
|
|
25
|
+
const items = comments.map((comment) => ({
|
|
26
|
+
key: comment.id,
|
|
27
|
+
anchor: topAt(view, comment.from) - root.top + scrollContainer.scrollTop,
|
|
28
|
+
fallbackHeight: estimatedCardHeight(comment)
|
|
29
|
+
}));
|
|
30
|
+
if (composerOpen) {
|
|
31
|
+
items.push({
|
|
32
|
+
key: COMPOSER_POSITION,
|
|
33
|
+
anchor: topAt(view, selectionPos) - root.top + scrollContainer.scrollTop,
|
|
34
|
+
fallbackHeight: 150
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
items.sort((left, right) => left.anchor - right.anchor);
|
|
38
|
+
const positions = /* @__PURE__ */ new Map();
|
|
39
|
+
let next = 64;
|
|
40
|
+
for (const item of items) {
|
|
41
|
+
const top = Math.max(item.anchor, next);
|
|
42
|
+
positions.set(item.key, top);
|
|
43
|
+
next = top + (heights.get(item.key) || item.fallbackHeight) + 12;
|
|
44
|
+
}
|
|
45
|
+
return { positions, bottom: next };
|
|
46
|
+
}
|
|
47
|
+
function useCommentRailLayout({
|
|
48
|
+
panel,
|
|
49
|
+
view,
|
|
50
|
+
scrollContainer,
|
|
51
|
+
visible,
|
|
52
|
+
composerOpen,
|
|
53
|
+
composerSelectionPos,
|
|
54
|
+
allCommentsOpen
|
|
55
|
+
}) {
|
|
56
|
+
const wasAllCommentsOpen = useRef(false);
|
|
57
|
+
const [positions, setPositions] = useState(
|
|
58
|
+
/* @__PURE__ */ new Map()
|
|
59
|
+
);
|
|
60
|
+
const [canvasHeight, setCanvasHeight] = useState(0);
|
|
61
|
+
useLayoutEffect(() => {
|
|
62
|
+
if (allCommentsOpen && !wasAllCommentsOpen.current && panel.current) {
|
|
63
|
+
panel.current.scrollTop = 0;
|
|
64
|
+
}
|
|
65
|
+
wasAllCommentsOpen.current = allCommentsOpen;
|
|
66
|
+
}, [allCommentsOpen, panel]);
|
|
67
|
+
useLayoutEffect(() => {
|
|
68
|
+
const rail = panel.current;
|
|
69
|
+
if (!rail || !scrollContainer) return;
|
|
70
|
+
const frameWindow = scrollContainer.ownerDocument.defaultView;
|
|
71
|
+
const pageLayer = view.dom.closest(
|
|
72
|
+
`.${editorClassNames.pageLayer}`
|
|
73
|
+
);
|
|
74
|
+
if (allCommentsOpen) {
|
|
75
|
+
pageLayer?.style.removeProperty("padding-right");
|
|
76
|
+
rail.style.removeProperty("left");
|
|
77
|
+
setPositions(/* @__PURE__ */ new Map());
|
|
78
|
+
setCanvasHeight(0);
|
|
79
|
+
const wheelAllComments = (event) => {
|
|
80
|
+
event.preventDefault();
|
|
81
|
+
const before = rail.scrollTop;
|
|
82
|
+
const max = Math.max(0, rail.scrollHeight - rail.clientHeight);
|
|
83
|
+
rail.scrollTop = Math.min(max, Math.max(0, before + event.deltaY));
|
|
84
|
+
scrollContainer.scrollTop += event.deltaY - (rail.scrollTop - before);
|
|
85
|
+
scrollContainer.scrollLeft += event.deltaX;
|
|
86
|
+
};
|
|
87
|
+
rail.addEventListener("wheel", wheelAllComments, { passive: false });
|
|
88
|
+
return () => rail.removeEventListener("wheel", wheelAllComments);
|
|
89
|
+
}
|
|
90
|
+
const reserveCommentWidth = () => {
|
|
91
|
+
if (!pageLayer) return;
|
|
92
|
+
const drawnWidth = rail.getBoundingClientRect().width || rail.offsetWidth;
|
|
93
|
+
const computedZoom = Number.parseFloat(
|
|
94
|
+
frameWindow?.getComputedStyle(pageLayer).zoom ?? ""
|
|
95
|
+
);
|
|
96
|
+
const scale = Number.isFinite(computedZoom) && computedZoom > 0 ? computedZoom : 1;
|
|
97
|
+
pageLayer.style.paddingRight = `${(drawnWidth + 16 + VERTICAL_SCROLLBAR_CLEARANCE) / scale}px`;
|
|
98
|
+
};
|
|
99
|
+
const placeBesidePaper = () => {
|
|
100
|
+
reserveCommentWidth();
|
|
101
|
+
const viewport = scrollContainer.getBoundingClientRect();
|
|
102
|
+
const paper = view.dom.getBoundingClientRect();
|
|
103
|
+
const desired = paper.right - viewport.left + scrollContainer.scrollLeft + 16;
|
|
104
|
+
rail.style.left = `${Math.max(16, desired)}px`;
|
|
105
|
+
};
|
|
106
|
+
const update = () => {
|
|
107
|
+
const layout = anchorPositions(
|
|
108
|
+
visible,
|
|
109
|
+
view,
|
|
110
|
+
scrollContainer,
|
|
111
|
+
rail,
|
|
112
|
+
composerOpen,
|
|
113
|
+
composerSelectionPos
|
|
114
|
+
);
|
|
115
|
+
setPositions(layout.positions);
|
|
116
|
+
const bottom = visible.length > 0 || composerOpen ? layout.bottom + 12 : 0;
|
|
117
|
+
setCanvasHeight(Math.max(scrollContainer.scrollHeight, bottom));
|
|
118
|
+
};
|
|
119
|
+
const wheelDocument = (event) => {
|
|
120
|
+
event.preventDefault();
|
|
121
|
+
scrollContainer.scrollTop += event.deltaY;
|
|
122
|
+
scrollContainer.scrollLeft += event.deltaX;
|
|
123
|
+
};
|
|
124
|
+
const placeAndUpdate = () => {
|
|
125
|
+
placeBesidePaper();
|
|
126
|
+
update();
|
|
127
|
+
};
|
|
128
|
+
placeAndUpdate();
|
|
129
|
+
let settledFrame;
|
|
130
|
+
const frame = frameWindow?.requestAnimationFrame(() => {
|
|
131
|
+
placeAndUpdate();
|
|
132
|
+
settledFrame = frameWindow.requestAnimationFrame(() => {
|
|
133
|
+
placeAndUpdate();
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
frameWindow?.addEventListener("resize", placeAndUpdate, {
|
|
137
|
+
passive: true
|
|
138
|
+
});
|
|
139
|
+
rail.addEventListener("wheel", wheelDocument, { passive: false });
|
|
140
|
+
const resize = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(placeAndUpdate);
|
|
141
|
+
resize?.observe(scrollContainer);
|
|
142
|
+
resize?.observe(view.dom);
|
|
143
|
+
resize?.observe(rail);
|
|
144
|
+
for (const element of rail.querySelectorAll(
|
|
145
|
+
"[data-comment-position]"
|
|
146
|
+
)) {
|
|
147
|
+
resize?.observe(element);
|
|
148
|
+
}
|
|
149
|
+
const mutations = typeof MutationObserver === "undefined" ? null : new MutationObserver(() => {
|
|
150
|
+
placeAndUpdate();
|
|
151
|
+
for (const element of rail.querySelectorAll(
|
|
152
|
+
"[data-comment-position]"
|
|
153
|
+
)) {
|
|
154
|
+
resize?.observe(element);
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
mutations?.observe(rail, { childList: true, subtree: true });
|
|
158
|
+
return () => {
|
|
159
|
+
if (frame !== void 0) frameWindow?.cancelAnimationFrame(frame);
|
|
160
|
+
if (settledFrame !== void 0) {
|
|
161
|
+
frameWindow?.cancelAnimationFrame(settledFrame);
|
|
162
|
+
}
|
|
163
|
+
frameWindow?.removeEventListener("resize", placeAndUpdate);
|
|
164
|
+
rail.removeEventListener("wheel", wheelDocument);
|
|
165
|
+
mutations?.disconnect();
|
|
166
|
+
resize?.disconnect();
|
|
167
|
+
pageLayer?.style.removeProperty("padding-right");
|
|
168
|
+
};
|
|
169
|
+
}, [
|
|
170
|
+
allCommentsOpen,
|
|
171
|
+
composerOpen,
|
|
172
|
+
composerSelectionPos,
|
|
173
|
+
panel,
|
|
174
|
+
scrollContainer,
|
|
175
|
+
view,
|
|
176
|
+
visible
|
|
177
|
+
]);
|
|
178
|
+
return { positions, canvasHeight };
|
|
179
|
+
}
|
|
180
|
+
export {
|
|
181
|
+
COMPOSER_POSITION,
|
|
182
|
+
useCommentRailLayout
|
|
183
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** One-dimensional arrow/Home/End navigation backed by roving focus. */
|
|
2
|
+
import { type KeyboardEvent, type RefObject } from "react";
|
|
3
|
+
/** Prevent these keys from scrolling a screen-anchored panel out of place. */
|
|
4
|
+
export declare const SCROLLING_KEYS: Set<string>;
|
|
5
|
+
export type WalkOrientation = "vertical" | "horizontal";
|
|
6
|
+
export interface LinearWalkOptions {
|
|
7
|
+
container: RefObject<HTMLElement | null>;
|
|
8
|
+
selector: string;
|
|
9
|
+
orientation: WalkOrientation;
|
|
10
|
+
/** Filters which controls the walk may land on; all of them by default. */
|
|
11
|
+
navigable?: (control: HTMLElement) => boolean;
|
|
12
|
+
/** The control to open on; the first by default. */
|
|
13
|
+
start?: (controls: readonly HTMLElement[]) => HTMLElement | undefined;
|
|
14
|
+
takeFocus?: boolean;
|
|
15
|
+
}
|
|
16
|
+
export interface LinearWalk {
|
|
17
|
+
controls: () => HTMLElement[];
|
|
18
|
+
moveTo: (control: HTMLElement | undefined) => void;
|
|
19
|
+
/** Handles one key; returns whether it was the walk's own so the caller can handle the rest. */
|
|
20
|
+
walk: (event: KeyboardEvent) => boolean;
|
|
21
|
+
}
|
|
22
|
+
export declare function useLinearWalk({ container, selector, orientation, navigable, start, takeFocus, }: LinearWalkOptions): LinearWalk;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// src/ui/keyboardWalk.ts
|
|
2
|
+
import {
|
|
3
|
+
useCallback,
|
|
4
|
+
useMemo
|
|
5
|
+
} from "react";
|
|
6
|
+
import { useRovingFocus } from "./rovingFocus.js";
|
|
7
|
+
var SCROLLING_KEYS = /* @__PURE__ */ new Set(["PageUp", "PageDown"]);
|
|
8
|
+
var WALK_KEYS = {
|
|
9
|
+
vertical: { previous: "ArrowUp", next: "ArrowDown" },
|
|
10
|
+
horizontal: { previous: "ArrowLeft", next: "ArrowRight" }
|
|
11
|
+
};
|
|
12
|
+
function walkedTo(controls, from, key, orientation) {
|
|
13
|
+
const { previous, next } = WALK_KEYS[orientation];
|
|
14
|
+
switch (key) {
|
|
15
|
+
case next:
|
|
16
|
+
return controls[(from + 1) % controls.length];
|
|
17
|
+
case previous:
|
|
18
|
+
return controls[(from - 1 + controls.length) % controls.length];
|
|
19
|
+
case "Home":
|
|
20
|
+
return controls[0];
|
|
21
|
+
case "End":
|
|
22
|
+
return controls[controls.length - 1];
|
|
23
|
+
default:
|
|
24
|
+
return void 0;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function useLinearWalk({
|
|
28
|
+
container,
|
|
29
|
+
selector,
|
|
30
|
+
orientation,
|
|
31
|
+
navigable,
|
|
32
|
+
start,
|
|
33
|
+
takeFocus
|
|
34
|
+
}) {
|
|
35
|
+
const roving = useRovingFocus({
|
|
36
|
+
container,
|
|
37
|
+
selector,
|
|
38
|
+
navigable,
|
|
39
|
+
start,
|
|
40
|
+
takeFocus
|
|
41
|
+
});
|
|
42
|
+
const walk = useCallback(
|
|
43
|
+
(event) => {
|
|
44
|
+
const controls = roving.controls();
|
|
45
|
+
if (controls.length === 0) return false;
|
|
46
|
+
const found = controls.findIndex(
|
|
47
|
+
(control) => control === document.activeElement
|
|
48
|
+
);
|
|
49
|
+
const to = walkedTo(
|
|
50
|
+
controls,
|
|
51
|
+
found === -1 ? 0 : found,
|
|
52
|
+
event.key,
|
|
53
|
+
orientation
|
|
54
|
+
);
|
|
55
|
+
if (!to) return false;
|
|
56
|
+
roving.moveTo(to);
|
|
57
|
+
event.preventDefault();
|
|
58
|
+
return true;
|
|
59
|
+
},
|
|
60
|
+
[roving, orientation]
|
|
61
|
+
);
|
|
62
|
+
return useMemo(
|
|
63
|
+
() => ({ controls: roving.controls, moveTo: roving.moveTo, walk }),
|
|
64
|
+
[roving, walk]
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
export {
|
|
68
|
+
SCROLLING_KEYS,
|
|
69
|
+
useLinearWalk
|
|
70
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** Validates document-provided addresses before the link card hands them to the browser. */
|
|
2
|
+
/**
|
|
3
|
+
* Whether this address is one to hand a browser.
|
|
4
|
+
*
|
|
5
|
+
* A scheme-less address is not, and a leading `www.` does not make it one: nothing in the URI rules
|
|
6
|
+
* says a bare host means https, and a relationship target with no scheme is read as relative to the
|
|
7
|
+
* package, which is to say a file beside the document rather than a site. Guessing here would be
|
|
8
|
+
* guessing what the document meant, which is `setLink`'s policy not to do.
|
|
9
|
+
*/
|
|
10
|
+
export declare function openable(address: string): boolean;
|
|
11
|
+
export declare function openAddress(address: string): void;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// src/ui/openAddress.ts
|
|
2
|
+
var OPENABLE_SCHEMES = ["http:", "https:", "mailto:"];
|
|
3
|
+
function openable(address) {
|
|
4
|
+
try {
|
|
5
|
+
return OPENABLE_SCHEMES.includes(new URL(address).protocol);
|
|
6
|
+
} catch {
|
|
7
|
+
return false;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
function openAddress(address) {
|
|
11
|
+
window.open(address, "_blank", "noopener,noreferrer");
|
|
12
|
+
}
|
|
13
|
+
export {
|
|
14
|
+
openAddress,
|
|
15
|
+
openable
|
|
16
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Positions fixed panels within the viewport so host overflow cannot clip them. Panels that do
|
|
3
|
+
* not remeasure themselves are dismissed on scroll by `useDismiss`.
|
|
4
|
+
*/
|
|
5
|
+
import type { EditorView } from "prosemirror-view";
|
|
6
|
+
import { type RefObject } from "react";
|
|
7
|
+
export interface PanelPosition {
|
|
8
|
+
left: number;
|
|
9
|
+
top: number;
|
|
10
|
+
}
|
|
11
|
+
interface Size {
|
|
12
|
+
width: number;
|
|
13
|
+
height: number;
|
|
14
|
+
}
|
|
15
|
+
interface Viewport {
|
|
16
|
+
width: number;
|
|
17
|
+
height: number;
|
|
18
|
+
}
|
|
19
|
+
export interface ControlBox {
|
|
20
|
+
left: number;
|
|
21
|
+
top: number;
|
|
22
|
+
bottom: number;
|
|
23
|
+
}
|
|
24
|
+
export interface ScreenPoint {
|
|
25
|
+
clientX: number;
|
|
26
|
+
clientY: number;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The panel hangs under the control, lined up with its left edge.
|
|
30
|
+
* When it does not fit under the control but does fit over it, it flips above instead;
|
|
31
|
+
* when it fits neither way it slides in from the bottom edge of the screen.
|
|
32
|
+
*/
|
|
33
|
+
export declare function positionUnderControl(control: ControlBox, size: Size, viewport: Viewport): PanelPosition;
|
|
34
|
+
export declare function positionAtPoint(point: ScreenPoint, size: Size, viewport: Viewport): PanelPosition;
|
|
35
|
+
/**
|
|
36
|
+
* The bottom left corner of what is drawn at this position in the document, which is where a panel
|
|
37
|
+
* about that spot hangs from.
|
|
38
|
+
*
|
|
39
|
+
* A position the browser has not laid out has no coordinates to ask for, and the panel then hangs
|
|
40
|
+
* against the top of the paper rather than not at all.
|
|
41
|
+
*/
|
|
42
|
+
export declare function pointBelowPos(view: EditorView, pos: number): ScreenPoint;
|
|
43
|
+
/**
|
|
44
|
+
* Places a panel under the control it opens from.
|
|
45
|
+
*
|
|
46
|
+
* The panel has to be measured, so this is decided right after it renders, before the
|
|
47
|
+
* browser paints. It is `null` for that one render, which the panel spends invisible.
|
|
48
|
+
*/
|
|
49
|
+
export declare function usePanelUnderControl(panel: RefObject<HTMLElement | null>, control: RefObject<HTMLElement | null>, open: boolean): PanelPosition | null;
|
|
50
|
+
export declare function usePanelAtPoint(panel: RefObject<HTMLElement | null>, point: ScreenPoint): PanelPosition | null;
|
|
51
|
+
export {};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// src/ui/panelPlacement.ts
|
|
2
|
+
import { useLayoutEffect, useState } from "react";
|
|
3
|
+
var EDGE_GAP = 8;
|
|
4
|
+
var CONTROL_GAP = 4;
|
|
5
|
+
function slideInside(start, length, limit) {
|
|
6
|
+
return Math.max(EDGE_GAP, Math.min(start, limit - EDGE_GAP - length));
|
|
7
|
+
}
|
|
8
|
+
function positionUnderControl(control, size, viewport) {
|
|
9
|
+
const under = control.bottom + CONTROL_GAP;
|
|
10
|
+
const over = control.top - CONTROL_GAP - size.height;
|
|
11
|
+
const fitsUnder = under + size.height + EDGE_GAP <= viewport.height;
|
|
12
|
+
return {
|
|
13
|
+
left: slideInside(control.left, size.width, viewport.width),
|
|
14
|
+
top: fitsUnder || over < EDGE_GAP ? slideInside(under, size.height, viewport.height) : over
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function positionAtPoint(point, size, viewport) {
|
|
18
|
+
return {
|
|
19
|
+
left: slideInside(point.clientX, size.width, viewport.width),
|
|
20
|
+
top: slideInside(point.clientY, size.height, viewport.height)
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
function pointBelowPos(view, pos) {
|
|
24
|
+
try {
|
|
25
|
+
const at = view.coordsAtPos(pos);
|
|
26
|
+
return { clientX: at.left, clientY: at.bottom };
|
|
27
|
+
} catch {
|
|
28
|
+
const paper = view.dom.getBoundingClientRect();
|
|
29
|
+
return { clientX: paper.left, clientY: paper.top };
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function measure(panel) {
|
|
33
|
+
return {
|
|
34
|
+
size: { width: panel.offsetWidth, height: panel.offsetHeight },
|
|
35
|
+
viewport: { width: window.innerWidth, height: window.innerHeight }
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function usePanelUnderControl(panel, control, open) {
|
|
39
|
+
const [position, setPosition] = useState(null);
|
|
40
|
+
useLayoutEffect(() => {
|
|
41
|
+
const box = panel.current;
|
|
42
|
+
const from = control.current;
|
|
43
|
+
if (!open || !box || !from) {
|
|
44
|
+
setPosition(null);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const { size, viewport } = measure(box);
|
|
48
|
+
setPosition(
|
|
49
|
+
positionUnderControl(from.getBoundingClientRect(), size, viewport)
|
|
50
|
+
);
|
|
51
|
+
}, [panel, control, open]);
|
|
52
|
+
return position;
|
|
53
|
+
}
|
|
54
|
+
function usePanelAtPoint(panel, point) {
|
|
55
|
+
const [position, setPosition] = useState(null);
|
|
56
|
+
const { clientX, clientY } = point;
|
|
57
|
+
useLayoutEffect(() => {
|
|
58
|
+
const box = panel.current;
|
|
59
|
+
if (!box) return;
|
|
60
|
+
const { size, viewport } = measure(box);
|
|
61
|
+
setPosition(positionAtPoint({ clientX, clientY }, size, viewport));
|
|
62
|
+
}, [panel, clientX, clientY]);
|
|
63
|
+
return position;
|
|
64
|
+
}
|
|
65
|
+
export {
|
|
66
|
+
pointBelowPos,
|
|
67
|
+
positionAtPoint,
|
|
68
|
+
positionUnderControl,
|
|
69
|
+
usePanelAtPoint,
|
|
70
|
+
usePanelUnderControl
|
|
71
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** Optional picker values; omitted fields use the package's exported defaults. */
|
|
2
|
+
import type { ColorRow } from "../styles/colors";
|
|
3
|
+
import type { LineSpacingOption } from "../styles/lineSpacings";
|
|
4
|
+
import type { CellBorderOption } from "../table/cellBorders";
|
|
5
|
+
export interface DocxEditorPresets {
|
|
6
|
+
fonts?: readonly string[];
|
|
7
|
+
colors?: readonly ColorRow[];
|
|
8
|
+
fontSizes?: readonly number[];
|
|
9
|
+
lineSpacings?: readonly LineSpacingOption[];
|
|
10
|
+
cellBorders?: readonly CellBorderOption[];
|
|
11
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Maintains one tab stop in a composite widget. It writes `tabIndex` onto discovered controls and
|
|
3
|
+
* focuses without scrolling because scrolling dismisses floating panels.
|
|
4
|
+
*/
|
|
5
|
+
import { type RefObject } from "react";
|
|
6
|
+
export interface RovingFocusOptions {
|
|
7
|
+
container: RefObject<HTMLElement | null>;
|
|
8
|
+
selector: string;
|
|
9
|
+
/** Limits which discovered controls can hold the tab stop. */
|
|
10
|
+
navigable?: (control: HTMLElement) => boolean;
|
|
11
|
+
/** Initial control; defaults to the first navigable control. */
|
|
12
|
+
start?: (controls: readonly HTMLElement[]) => HTMLElement | undefined;
|
|
13
|
+
/** Moves focus to the initial control when the widget appears. */
|
|
14
|
+
takeFocus?: boolean;
|
|
15
|
+
}
|
|
16
|
+
export interface RovingFocus {
|
|
17
|
+
controls: () => HTMLElement[];
|
|
18
|
+
moveTo: (control: HTMLElement | undefined) => void;
|
|
19
|
+
}
|
|
20
|
+
export declare function useRovingFocus({ container, selector, navigable, start, takeFocus, }: RovingFocusOptions): RovingFocus;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// src/ui/rovingFocus.ts
|
|
2
|
+
import {
|
|
3
|
+
useCallback,
|
|
4
|
+
useLayoutEffect,
|
|
5
|
+
useMemo,
|
|
6
|
+
useRef
|
|
7
|
+
} from "react";
|
|
8
|
+
function useRovingFocus({
|
|
9
|
+
container,
|
|
10
|
+
selector,
|
|
11
|
+
navigable,
|
|
12
|
+
start,
|
|
13
|
+
takeFocus = false
|
|
14
|
+
}) {
|
|
15
|
+
const stop = useRef(null);
|
|
16
|
+
const took = useRef(false);
|
|
17
|
+
const all = useCallback(
|
|
18
|
+
() => Array.from(
|
|
19
|
+
container.current?.querySelectorAll(selector) ?? []
|
|
20
|
+
),
|
|
21
|
+
[container, selector]
|
|
22
|
+
);
|
|
23
|
+
const controls = useCallback(
|
|
24
|
+
() => navigable ? all().filter(navigable) : all(),
|
|
25
|
+
[all, navigable]
|
|
26
|
+
);
|
|
27
|
+
const write = useCallback(
|
|
28
|
+
(onto) => {
|
|
29
|
+
for (const control of all()) control.tabIndex = control === onto ? 0 : -1;
|
|
30
|
+
stop.current = onto;
|
|
31
|
+
},
|
|
32
|
+
[all]
|
|
33
|
+
);
|
|
34
|
+
const moveTo = useCallback(
|
|
35
|
+
(control) => {
|
|
36
|
+
if (!control || !controls().includes(control)) return;
|
|
37
|
+
write(control);
|
|
38
|
+
control.focus({ preventScroll: true });
|
|
39
|
+
},
|
|
40
|
+
[controls, write]
|
|
41
|
+
);
|
|
42
|
+
useLayoutEffect(() => {
|
|
43
|
+
const found = controls();
|
|
44
|
+
if (found.length === 0) {
|
|
45
|
+
write(null);
|
|
46
|
+
took.current = false;
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const focused = found.find((control) => control === document.activeElement);
|
|
50
|
+
const held = stop.current;
|
|
51
|
+
const onto = focused ?? (held && found.includes(held) ? held : start?.(found) ?? found[0]) ?? null;
|
|
52
|
+
write(onto);
|
|
53
|
+
if (!takeFocus || took.current || !onto) return;
|
|
54
|
+
onto.focus({ preventScroll: true });
|
|
55
|
+
took.current = document.activeElement === onto;
|
|
56
|
+
});
|
|
57
|
+
return useMemo(() => ({ controls, moveTo }), [controls, moveTo]);
|
|
58
|
+
}
|
|
59
|
+
export {
|
|
60
|
+
useRovingFocus
|
|
61
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Runs editor commands and restores focus for continued typing. */
|
|
2
|
+
import type { Command } from "prosemirror-state";
|
|
3
|
+
import type { EditorView } from "prosemirror-view";
|
|
4
|
+
export type RunCommand = (command: Command) => void;
|
|
5
|
+
export declare function commandRunner(view: EditorView): RunCommand;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// src/ui/runCommand.ts
|
|
2
|
+
function apply(view, command) {
|
|
3
|
+
command(view.state, (tr) => view.dispatch(tr), view);
|
|
4
|
+
}
|
|
5
|
+
function commandRunner(view) {
|
|
6
|
+
return (command) => {
|
|
7
|
+
apply(view, command);
|
|
8
|
+
view.focus();
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
export {
|
|
12
|
+
commandRunner
|
|
13
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dismisses a panel on outside press, Escape, scroll, or resize. Scroll uses capture to catch
|
|
3
|
+
* nested containers; events inside the panel are ignored so its own scrolling stays open.
|
|
4
|
+
*/
|
|
5
|
+
import { type RefObject } from "react";
|
|
6
|
+
export declare function useDismiss(box: RefObject<HTMLElement | null>, open: boolean, onDismiss: () => void): void;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// src/ui/useDismiss.ts
|
|
2
|
+
import { useEffect } from "react";
|
|
3
|
+
function useDismiss(box, open, onDismiss) {
|
|
4
|
+
useEffect(() => {
|
|
5
|
+
if (!open) return;
|
|
6
|
+
const inside = (event) => {
|
|
7
|
+
const target = event.target;
|
|
8
|
+
return target instanceof Node && box.current?.contains(target) === true;
|
|
9
|
+
};
|
|
10
|
+
const onOutside = (event) => {
|
|
11
|
+
if (inside(event)) return;
|
|
12
|
+
onDismiss();
|
|
13
|
+
};
|
|
14
|
+
const onKeyDown = (event) => {
|
|
15
|
+
if (event.key === "Escape") onDismiss();
|
|
16
|
+
};
|
|
17
|
+
document.addEventListener("mousedown", onOutside, true);
|
|
18
|
+
document.addEventListener("keydown", onKeyDown, true);
|
|
19
|
+
document.addEventListener("scroll", onOutside, true);
|
|
20
|
+
window.addEventListener("resize", onDismiss);
|
|
21
|
+
return () => {
|
|
22
|
+
document.removeEventListener("mousedown", onOutside, true);
|
|
23
|
+
document.removeEventListener("keydown", onKeyDown, true);
|
|
24
|
+
document.removeEventListener("scroll", onOutside, true);
|
|
25
|
+
window.removeEventListener("resize", onDismiss);
|
|
26
|
+
};
|
|
27
|
+
}, [box, open, onDismiss]);
|
|
28
|
+
}
|
|
29
|
+
export {
|
|
30
|
+
useDismiss
|
|
31
|
+
};
|