@stesura/react-prosemirror 3.2.11-stesura.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/LICENSE.txt +21 -0
- package/README.md +939 -0
- package/dist/cjs/AbstractEditorView.js +4 -0
- package/dist/cjs/ReactEditorView.js +194 -0
- package/dist/cjs/StaticEditorView.js +89 -0
- package/dist/cjs/browser.js +53 -0
- package/dist/cjs/commands/reorderSiblings.js +72 -0
- package/dist/cjs/components/ChildNodeViews.js +418 -0
- package/dist/cjs/components/CursorWrapper.js +84 -0
- package/dist/cjs/components/EditorStateSelectorsProvider.js +90 -0
- package/dist/cjs/components/LayoutGroup.js +112 -0
- package/dist/cjs/components/NativeWidgetView.js +108 -0
- package/dist/cjs/components/OutputSpec.js +121 -0
- package/dist/cjs/components/ProseMirror.js +146 -0
- package/dist/cjs/components/ProseMirrorDoc.js +74 -0
- package/dist/cjs/components/SeparatorHackView.js +99 -0
- package/dist/cjs/components/TextNodeView.js +113 -0
- package/dist/cjs/components/TrailingHackView.js +87 -0
- package/dist/cjs/components/WidgetView.js +94 -0
- package/dist/cjs/components/WidgetViewComponentProps.js +4 -0
- package/dist/cjs/components/marks/DefaultMarkView.js +70 -0
- package/dist/cjs/components/marks/MarkView.js +85 -0
- package/dist/cjs/components/marks/MarkViewComponentProps.js +4 -0
- package/dist/cjs/components/marks/MarkViewConstructorView.js +116 -0
- package/dist/cjs/components/marks/ReactMarkView.js +109 -0
- package/dist/cjs/components/nodes/DefaultNodeView.js +68 -0
- package/dist/cjs/components/nodes/DocNodeView.js +97 -0
- package/dist/cjs/components/nodes/NodeView.js +129 -0
- package/dist/cjs/components/nodes/NodeViewComponentProps.js +4 -0
- package/dist/cjs/components/nodes/NodeViewConstructorView.js +145 -0
- package/dist/cjs/components/nodes/ReactNodeView.js +189 -0
- package/dist/cjs/constants.js +33 -0
- package/dist/cjs/contexts/ChildDescriptionsContext.js +19 -0
- package/dist/cjs/contexts/EditorContext.js +12 -0
- package/dist/cjs/contexts/EditorStateContext.js +12 -0
- package/dist/cjs/contexts/EditorStateStoreContext.js +45 -0
- package/dist/cjs/contexts/IgnoreMutationContext.js +12 -0
- package/dist/cjs/contexts/LayoutGroupContext.js +12 -0
- package/dist/cjs/contexts/NodeViewContext.js +12 -0
- package/dist/cjs/contexts/RenderStoreContext.js +54 -0
- package/dist/cjs/contexts/SelectNodeContext.js +12 -0
- package/dist/cjs/contexts/StopEventContext.js +12 -0
- package/dist/cjs/decorations/ReactWidgetType.js +58 -0
- package/dist/cjs/decorations/computeDocDeco.js +44 -0
- package/dist/cjs/decorations/internalTypes.js +4 -0
- package/dist/cjs/decorations/iterDeco.js +91 -0
- package/dist/cjs/decorations/viewDecorations.js +155 -0
- package/dist/cjs/dom.js +147 -0
- package/dist/cjs/findDOMNode.js +46 -0
- package/dist/cjs/hooks/useClientLayoutEffect.js +19 -0
- package/dist/cjs/hooks/useComponentEventListeners.js +72 -0
- package/dist/cjs/hooks/useEditor.js +130 -0
- package/dist/cjs/hooks/useEditorEffect.js +39 -0
- package/dist/cjs/hooks/useEditorEventCallback.js +38 -0
- package/dist/cjs/hooks/useEditorEventListener.js +34 -0
- package/dist/cjs/hooks/useEditorState.js +18 -0
- package/dist/cjs/hooks/useEditorStateSelector.js +17 -0
- package/dist/cjs/hooks/useEffectEvent.js +34 -0
- package/dist/cjs/hooks/useForceUpdate.js +15 -0
- package/dist/cjs/hooks/useIgnoreMutation.js +24 -0
- package/dist/cjs/hooks/useIsComposingIn.js +25 -0
- package/dist/cjs/hooks/useIsEditorStatic.js +15 -0
- package/dist/cjs/hooks/useIsNodeSelected.js +21 -0
- package/dist/cjs/hooks/useLayoutGroupEffect.js +20 -0
- package/dist/cjs/hooks/useMarkViewDescription.js +134 -0
- package/dist/cjs/hooks/useNodePos.js +18 -0
- package/dist/cjs/hooks/useNodeViewDescription.js +154 -0
- package/dist/cjs/hooks/useReactKeys.js +17 -0
- package/dist/cjs/hooks/useSelectNode.js +30 -0
- package/dist/cjs/hooks/useSetDom.js +57 -0
- package/dist/cjs/hooks/useStopEvent.js +24 -0
- package/dist/cjs/index.js +85 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/plugins/beforeInputPlugin.js +356 -0
- package/dist/cjs/plugins/reactKeys.js +121 -0
- package/dist/cjs/props.js +283 -0
- package/dist/cjs/refs.js +28 -0
- package/dist/cjs/testing/editorViewTestHelpers.js +109 -0
- package/dist/cjs/testing/setupProseMirrorView.js +94 -0
- package/dist/cjs/tiptap/ReactProseMirrorNodeView.js +26 -0
- package/dist/cjs/tiptap/TiptapEditor.js +34 -0
- package/dist/cjs/tiptap/TiptapEditorContent.js +143 -0
- package/dist/cjs/tiptap/TiptapEditorView.js +130 -0
- package/dist/cjs/tiptap/contexts/TiptapEditorContext.js +12 -0
- package/dist/cjs/tiptap/extensions/ReactProseMirror.js +40 -0
- package/dist/cjs/tiptap/extensions/ReactProseMirrorCommands.js +21 -0
- package/dist/cjs/tiptap/extensions/commands/updateAttributes.js +104 -0
- package/dist/cjs/tiptap/hooks/useEditor.js +318 -0
- package/dist/cjs/tiptap/hooks/useIsInReactProseMirror.js +15 -0
- package/dist/cjs/tiptap/hooks/useTiptapEditor.js +35 -0
- package/dist/cjs/tiptap/hooks/useTiptapEditorEffect.js +35 -0
- package/dist/cjs/tiptap/hooks/useTiptapEditorEventCallback.js +35 -0
- package/dist/cjs/tiptap/index.js +48 -0
- package/dist/cjs/tiptap/tiptapNodeView.js +245 -0
- package/dist/cjs/viewdesc.js +862 -0
- package/dist/esm/AbstractEditorView.js +1 -0
- package/dist/esm/ReactEditorView.js +194 -0
- package/dist/esm/StaticEditorView.js +79 -0
- package/dist/esm/browser.js +43 -0
- package/dist/esm/commands/reorderSiblings.js +63 -0
- package/dist/esm/components/ChildNodeViews.js +359 -0
- package/dist/esm/components/CursorWrapper.js +33 -0
- package/dist/esm/components/EditorStateSelectorsProvider.js +31 -0
- package/dist/esm/components/LayoutGroup.js +67 -0
- package/dist/esm/components/NativeWidgetView.js +57 -0
- package/dist/esm/components/OutputSpec.js +71 -0
- package/dist/esm/components/ProseMirror.js +95 -0
- package/dist/esm/components/ProseMirrorDoc.js +15 -0
- package/dist/esm/components/SeparatorHackView.js +48 -0
- package/dist/esm/components/TextNodeView.js +103 -0
- package/dist/esm/components/TrailingHackView.js +36 -0
- package/dist/esm/components/WidgetView.js +43 -0
- package/dist/esm/components/WidgetViewComponentProps.js +1 -0
- package/dist/esm/components/marks/DefaultMarkView.js +19 -0
- package/dist/esm/components/marks/MarkView.js +34 -0
- package/dist/esm/components/marks/MarkViewComponentProps.js +1 -0
- package/dist/esm/components/marks/MarkViewConstructorView.js +65 -0
- package/dist/esm/components/marks/ReactMarkView.js +58 -0
- package/dist/esm/components/nodes/DefaultNodeView.js +17 -0
- package/dist/esm/components/nodes/DocNodeView.js +46 -0
- package/dist/esm/components/nodes/NodeView.js +67 -0
- package/dist/esm/components/nodes/NodeViewComponentProps.js +1 -0
- package/dist/esm/components/nodes/NodeViewConstructorView.js +94 -0
- package/dist/esm/components/nodes/ReactNodeView.js +138 -0
- package/dist/esm/constants.js +15 -0
- package/dist/esm/contexts/ChildDescriptionsContext.js +9 -0
- package/dist/esm/contexts/EditorContext.js +7 -0
- package/dist/esm/contexts/EditorStateContext.js +2 -0
- package/dist/esm/contexts/EditorStateStoreContext.js +27 -0
- package/dist/esm/contexts/IgnoreMutationContext.js +2 -0
- package/dist/esm/contexts/LayoutGroupContext.js +2 -0
- package/dist/esm/contexts/NodeViewContext.js +2 -0
- package/dist/esm/contexts/RenderStoreContext.js +33 -0
- package/dist/esm/contexts/SelectNodeContext.js +2 -0
- package/dist/esm/contexts/StopEventContext.js +2 -0
- package/dist/esm/decorations/ReactWidgetType.js +40 -0
- package/dist/esm/decorations/computeDocDeco.js +44 -0
- package/dist/esm/decorations/internalTypes.js +1 -0
- package/dist/esm/decorations/iterDeco.js +85 -0
- package/dist/esm/decorations/viewDecorations.js +155 -0
- package/dist/esm/dom.js +110 -0
- package/dist/esm/findDOMNode.js +31 -0
- package/dist/esm/hooks/useClientLayoutEffect.js +9 -0
- package/dist/esm/hooks/useComponentEventListeners.js +87 -0
- package/dist/esm/hooks/useEditor.js +128 -0
- package/dist/esm/hooks/useEditorEffect.js +46 -0
- package/dist/esm/hooks/useEditorEventCallback.js +38 -0
- package/dist/esm/hooks/useEditorEventListener.js +28 -0
- package/dist/esm/hooks/useEditorState.js +10 -0
- package/dist/esm/hooks/useEditorStateSelector.js +14 -0
- package/dist/esm/hooks/useEffectEvent.js +24 -0
- package/dist/esm/hooks/useForceUpdate.js +8 -0
- package/dist/esm/hooks/useIgnoreMutation.js +14 -0
- package/dist/esm/hooks/useIsComposingIn.js +17 -0
- package/dist/esm/hooks/useIsEditorStatic.js +8 -0
- package/dist/esm/hooks/useIsNodeSelected.js +11 -0
- package/dist/esm/hooks/useLayoutGroupEffect.js +10 -0
- package/dist/esm/hooks/useMarkViewDescription.js +124 -0
- package/dist/esm/hooks/useNodePos.js +15 -0
- package/dist/esm/hooks/useNodeViewDescription.js +144 -0
- package/dist/esm/hooks/useReactKeys.js +7 -0
- package/dist/esm/hooks/useSelectNode.js +20 -0
- package/dist/esm/hooks/useSetDom.js +47 -0
- package/dist/esm/hooks/useStopEvent.js +14 -0
- package/dist/esm/index.js +19 -0
- package/dist/esm/plugins/beforeInputPlugin.js +346 -0
- package/dist/esm/plugins/reactKeys.js +106 -0
- package/dist/esm/props.js +267 -0
- package/dist/esm/refs.js +18 -0
- package/dist/esm/testing/editorViewTestHelpers.js +86 -0
- package/dist/esm/testing/setupProseMirrorView.js +76 -0
- package/dist/esm/tiptap/ReactProseMirrorNodeView.js +22 -0
- package/dist/esm/tiptap/TiptapEditor.js +24 -0
- package/dist/esm/tiptap/TiptapEditorContent.js +99 -0
- package/dist/esm/tiptap/TiptapEditorView.js +76 -0
- package/dist/esm/tiptap/contexts/TiptapEditorContext.js +2 -0
- package/dist/esm/tiptap/extensions/ReactProseMirror.js +30 -0
- package/dist/esm/tiptap/extensions/ReactProseMirrorCommands.js +11 -0
- package/dist/esm/tiptap/extensions/commands/updateAttributes.js +96 -0
- package/dist/esm/tiptap/hooks/useEditor.js +308 -0
- package/dist/esm/tiptap/hooks/useIsInReactProseMirror.js +9 -0
- package/dist/esm/tiptap/hooks/useTiptapEditor.js +31 -0
- package/dist/esm/tiptap/hooks/useTiptapEditorEffect.js +42 -0
- package/dist/esm/tiptap/hooks/useTiptapEditorEventCallback.js +35 -0
- package/dist/esm/tiptap/index.js +9 -0
- package/dist/esm/tiptap/tiptapNodeView.js +211 -0
- package/dist/esm/viewdesc.js +844 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/types/AbstractEditorView.d.ts +28 -0
- package/dist/types/ReactEditorView.d.ts +102 -0
- package/dist/types/StaticEditorView.d.ts +26 -0
- package/dist/types/__tests__/viewdesc.test.d.ts +1 -0
- package/dist/types/browser.d.ts +15 -0
- package/dist/types/commands/__tests__/reorderSiblings.test.d.ts +1 -0
- package/dist/types/commands/reorderSiblings.d.ts +14 -0
- package/dist/types/components/ChildNodeViews.d.ts +9 -0
- package/dist/types/components/CursorWrapper.d.ts +3 -0
- package/dist/types/components/EditorStateSelectorsProvider.d.ts +10 -0
- package/dist/types/components/LayoutGroup.d.ts +12 -0
- package/dist/types/components/NativeWidgetView.d.ts +8 -0
- package/dist/types/components/OutputSpec.d.ts +10 -0
- package/dist/types/components/ProseMirror.d.ts +14 -0
- package/dist/types/components/ProseMirrorDoc.d.ts +16 -0
- package/dist/types/components/SeparatorHackView.d.ts +6 -0
- package/dist/types/components/TextNodeView.d.ts +27 -0
- package/dist/types/components/TrailingHackView.d.ts +6 -0
- package/dist/types/components/WidgetView.d.ts +8 -0
- package/dist/types/components/WidgetViewComponentProps.d.ts +7 -0
- package/dist/types/components/__tests__/ProseMirror.composition.test.d.ts +17 -0
- package/dist/types/components/__tests__/ProseMirror.domchange.test.d.ts +1 -0
- package/dist/types/components/__tests__/ProseMirror.draw-decoration.test.d.ts +1 -0
- package/dist/types/components/__tests__/ProseMirror.draw.test.d.ts +1 -0
- package/dist/types/components/__tests__/ProseMirror.memory-leak.test.d.ts +6 -0
- package/dist/types/components/__tests__/ProseMirror.mobile.test.d.ts +1 -0
- package/dist/types/components/__tests__/ProseMirror.node-view.test.d.ts +1 -0
- package/dist/types/components/__tests__/ProseMirror.selection.test.d.ts +1 -0
- package/dist/types/components/__tests__/ProseMirror.test.d.ts +1 -0
- package/dist/types/components/__tests__/stableContexts.test.d.ts +10 -0
- package/dist/types/components/marks/DefaultMarkView.d.ts +3 -0
- package/dist/types/components/marks/MarkView.d.ts +10 -0
- package/dist/types/components/marks/MarkViewComponentProps.d.ts +11 -0
- package/dist/types/components/marks/MarkViewConstructorView.d.ts +12 -0
- package/dist/types/components/marks/ReactMarkView.d.ts +12 -0
- package/dist/types/components/nodes/DefaultNodeView.d.ts +3 -0
- package/dist/types/components/nodes/DocNodeView.d.ts +12 -0
- package/dist/types/components/nodes/NodeView.d.ts +14 -0
- package/dist/types/components/nodes/NodeViewComponentProps.d.ts +13 -0
- package/dist/types/components/nodes/NodeViewConstructorView.d.ts +12 -0
- package/dist/types/components/nodes/ReactNodeView.d.ts +13 -0
- package/dist/types/constants.d.ts +4 -0
- package/dist/types/contexts/ChildDescriptionsContext.d.ts +7 -0
- package/dist/types/contexts/EditorContext.d.ts +18 -0
- package/dist/types/contexts/EditorStateContext.d.ts +2 -0
- package/dist/types/contexts/EditorStateStoreContext.d.ts +9 -0
- package/dist/types/contexts/IgnoreMutationContext.d.ts +5 -0
- package/dist/types/contexts/LayoutGroupContext.d.ts +5 -0
- package/dist/types/contexts/NodeViewContext.d.ts +9 -0
- package/dist/types/contexts/RenderStoreContext.d.ts +24 -0
- package/dist/types/contexts/SelectNodeContext.d.ts +5 -0
- package/dist/types/contexts/StopEventContext.d.ts +5 -0
- package/dist/types/contexts/__tests__/DeferredLayoutEffects.test.d.ts +1 -0
- package/dist/types/decorations/ReactWidgetType.d.ts +40 -0
- package/dist/types/decorations/computeDocDeco.d.ts +14 -0
- package/dist/types/decorations/internalTypes.d.ts +20 -0
- package/dist/types/decorations/iterDeco.d.ts +3 -0
- package/dist/types/decorations/viewDecorations.d.ts +14 -0
- package/dist/types/dom.d.ts +22 -0
- package/dist/types/findDOMNode.d.ts +3 -0
- package/dist/types/hooks/__tests__/useEditorEffect.test.d.ts +1 -0
- package/dist/types/hooks/useClientLayoutEffect.d.ts +2 -0
- package/dist/types/hooks/useComponentEventListeners.d.ts +34 -0
- package/dist/types/hooks/useEditor.d.ts +37 -0
- package/dist/types/hooks/useEditorEffect.d.ts +21 -0
- package/dist/types/hooks/useEditorEventCallback.d.ts +13 -0
- package/dist/types/hooks/useEditorEventListener.d.ts +8 -0
- package/dist/types/hooks/useEditorState.d.ts +5 -0
- package/dist/types/hooks/useEditorStateSelector.d.ts +10 -0
- package/dist/types/hooks/useEffectEvent.d.ts +1 -0
- package/dist/types/hooks/useForceUpdate.d.ts +5 -0
- package/dist/types/hooks/useIgnoreMutation.d.ts +2 -0
- package/dist/types/hooks/useIsComposingIn.d.ts +4 -0
- package/dist/types/hooks/useIsEditorStatic.d.ts +5 -0
- package/dist/types/hooks/useIsNodeSelected.d.ts +1 -0
- package/dist/types/hooks/useLayoutGroupEffect.d.ts +3 -0
- package/dist/types/hooks/useMarkViewDescription.d.ts +15 -0
- package/dist/types/hooks/useNodePos.d.ts +9 -0
- package/dist/types/hooks/useNodeViewDescription.d.ts +16 -0
- package/dist/types/hooks/useReactKeys.d.ts +2 -0
- package/dist/types/hooks/useSelectNode.d.ts +2 -0
- package/dist/types/hooks/useSetDom.d.ts +2 -0
- package/dist/types/hooks/useStopEvent.d.ts +2 -0
- package/dist/types/index.d.ts +21 -0
- package/dist/types/plugins/__tests__/reactKeys.test.d.ts +1 -0
- package/dist/types/plugins/beforeInputPlugin.d.ts +2 -0
- package/dist/types/plugins/reactKeys.d.ts +23 -0
- package/dist/types/props.d.ts +1173 -0
- package/dist/types/refs.d.ts +2 -0
- package/dist/types/testing/editorViewTestHelpers.d.ts +24 -0
- package/dist/types/testing/setupProseMirrorView.d.ts +2 -0
- package/dist/types/tiptap/ReactProseMirrorNodeView.d.ts +15 -0
- package/dist/types/tiptap/TiptapEditor.d.ts +6 -0
- package/dist/types/tiptap/TiptapEditorContent.d.ts +28 -0
- package/dist/types/tiptap/TiptapEditorView.d.ts +16 -0
- package/dist/types/tiptap/contexts/TiptapEditorContext.d.ts +6 -0
- package/dist/types/tiptap/extensions/ReactProseMirror.d.ts +9 -0
- package/dist/types/tiptap/extensions/ReactProseMirrorCommands.d.ts +2 -0
- package/dist/types/tiptap/extensions/commands/updateAttributes.d.ts +24 -0
- package/dist/types/tiptap/hooks/useEditor.d.ts +38 -0
- package/dist/types/tiptap/hooks/useIsInReactProseMirror.d.ts +6 -0
- package/dist/types/tiptap/hooks/useTiptapEditor.d.ts +11 -0
- package/dist/types/tiptap/hooks/useTiptapEditorEffect.d.ts +21 -0
- package/dist/types/tiptap/hooks/useTiptapEditorEventCallback.d.ts +13 -0
- package/dist/types/tiptap/index.d.ts +9 -0
- package/dist/types/tiptap/tiptapNodeView.d.ts +50 -0
- package/dist/types/viewdesc.d.ts +179 -0
- package/package.json +167 -0
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
import { Fragment, Slice } from "prosemirror-model";
|
|
2
|
+
import { Plugin, TextSelection } from "prosemirror-state";
|
|
3
|
+
import { ReactEditorView } from "../ReactEditorView.js";
|
|
4
|
+
import { browser } from "../browser.js";
|
|
5
|
+
import { CursorWrapper } from "../components/CursorWrapper.js";
|
|
6
|
+
import { widget } from "../decorations/ReactWidgetType.js";
|
|
7
|
+
import { CompositionViewDesc, TextViewDesc, findTextInFragment } from "../viewdesc.js";
|
|
8
|
+
import { reactKeysPluginKey } from "./reactKeys.js";
|
|
9
|
+
function insertText(view, eventData) {
|
|
10
|
+
let options = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : {};
|
|
11
|
+
if (eventData === null) return false;
|
|
12
|
+
const from = options.from ?? view.state.selection.from;
|
|
13
|
+
const to = options.to ?? view.state.selection.to;
|
|
14
|
+
if (view.someProp("handleTextInput", (f)=>f(view, from, to, eventData, ()=>view.state.tr.insertText(eventData, from, to)))) {
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
const { tr } = view.state;
|
|
18
|
+
if (options.marks) tr.ensureMarks(options.marks);
|
|
19
|
+
tr.insertText(eventData, from, to);
|
|
20
|
+
view.dispatch(tr);
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
// Taken from https://github.com/ProseMirror/prosemirror-gapcursor/blob/master/src/index.ts#L67-L84
|
|
24
|
+
// This is a hack that, when a composition starts while a gap cursor
|
|
25
|
+
// is active, quickly creates an inline context for the composition to
|
|
26
|
+
// happen in, to avoid it being aborted by the DOM selection being
|
|
27
|
+
// moved into a valid position.
|
|
28
|
+
//
|
|
29
|
+
// We can't rely on the actual hack from prosemirror-gapcursor, because
|
|
30
|
+
// it happens too late. We snapshot the DOM during compositionstart, but
|
|
31
|
+
// the gapcursor hack runs in beforeinput (after compositionstart).
|
|
32
|
+
function handleGapCursorComposition(view) {
|
|
33
|
+
// @ts-expect-error Internal property - jsonID
|
|
34
|
+
if (!(view.state.selection.jsonID === "gapcursor")) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const { $from } = view.state.selection;
|
|
38
|
+
const insert = $from.parent.contentMatchAt($from.index())// All schemas _must_ have a text node type
|
|
39
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
40
|
+
.findWrapping(view.state.schema.nodes.text);
|
|
41
|
+
if (!insert) return;
|
|
42
|
+
let fragment = Fragment.empty;
|
|
43
|
+
for(let i = insert.length - 1; i >= 0; i--){
|
|
44
|
+
fragment = Fragment.from(// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
45
|
+
insert[i].createAndFill(null, fragment));
|
|
46
|
+
}
|
|
47
|
+
const tr = view.state.tr.replace($from.pos, $from.pos, new Slice(fragment, 0, 0));
|
|
48
|
+
tr.setSelection(TextSelection.near(tr.doc.resolve($from.pos + 1)));
|
|
49
|
+
view.dispatch(tr);
|
|
50
|
+
}
|
|
51
|
+
const observeOptions = {
|
|
52
|
+
childList: true,
|
|
53
|
+
characterData: true,
|
|
54
|
+
characterDataOldValue: true,
|
|
55
|
+
attributes: true,
|
|
56
|
+
attributeOldValue: true,
|
|
57
|
+
subtree: true
|
|
58
|
+
};
|
|
59
|
+
export function beforeInputPlugin() {
|
|
60
|
+
let observer = null;
|
|
61
|
+
let preCompositionSnapshot = null;
|
|
62
|
+
function teardownComposition(view, endedAt) {
|
|
63
|
+
view.input.composing = false;
|
|
64
|
+
if (observer) {
|
|
65
|
+
if (view.input.compositionNode && view.dom.contains(view.input.compositionNode)) {
|
|
66
|
+
view.domObserver.queue.push(...observer.takeRecords());
|
|
67
|
+
view.domObserver.flush();
|
|
68
|
+
} else {
|
|
69
|
+
const freezeFrom = reactKeysPluginKey.getState(view.state)?.freezeFrom;
|
|
70
|
+
const frozenNode = freezeFrom == null ? null : view.state.doc.nodeAt(freezeFrom);
|
|
71
|
+
if (freezeFrom != null && frozenNode != null && preCompositionSnapshot) {
|
|
72
|
+
// This is a little hacky — it only works because we always abort
|
|
73
|
+
// compositions if the node after freezeFrom changes, so we can
|
|
74
|
+
// be sure that if a composition was canceled by the user/browser,
|
|
75
|
+
// the content hasn't changed since the composition started
|
|
76
|
+
view.dispatch(view.state.tr.replaceWith(freezeFrom + 1, freezeFrom + 1 + frozenNode.content.size, preCompositionSnapshot));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
observer.disconnect();
|
|
80
|
+
observer = null;
|
|
81
|
+
}
|
|
82
|
+
view.input.compositionEndedAt = endedAt;
|
|
83
|
+
view.input.compositionNode = null;
|
|
84
|
+
view.input.compositionNodes = [];
|
|
85
|
+
view.input.compositionID++;
|
|
86
|
+
}
|
|
87
|
+
function teardownCompositionAndUnfreeze(view, event) {
|
|
88
|
+
teardownComposition(view, event.timeStamp);
|
|
89
|
+
view.dispatch(view.state.tr.setMeta(reactKeysPluginKey, {
|
|
90
|
+
cursorWrapper: null,
|
|
91
|
+
freezeFrom: null
|
|
92
|
+
}));
|
|
93
|
+
}
|
|
94
|
+
return new Plugin({
|
|
95
|
+
view () {
|
|
96
|
+
return {
|
|
97
|
+
update (view) {
|
|
98
|
+
if (!(view instanceof ReactEditorView)) return;
|
|
99
|
+
const frozen = reactKeysPluginKey.getState(view.state)?.freezeFrom != null;
|
|
100
|
+
if (observer && view.composing && !frozen) {
|
|
101
|
+
teardownComposition(view, Date.now());
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
},
|
|
106
|
+
props: {
|
|
107
|
+
handleDOMEvents: {
|
|
108
|
+
compositionstart (view) {
|
|
109
|
+
if (!(view instanceof ReactEditorView)) return false;
|
|
110
|
+
const storedMarks = view.state.selection.empty ? view.state.storedMarks : view.state.storedMarks ?? (view.state.selection instanceof TextSelection ? view.state.selection.$from.marksAcross(view.state.selection.$to) : null);
|
|
111
|
+
view.dispatch(view.state.tr.deleteSelection().setStoredMarks(storedMarks));
|
|
112
|
+
handleGapCursorComposition(view);
|
|
113
|
+
const { selection } = view.state;
|
|
114
|
+
const tr = view.state.tr.delete(selection.from, selection.to);
|
|
115
|
+
const $from = tr.doc.resolve(tr.mapping.map(selection.from));
|
|
116
|
+
const isEmptyTextblock = $from.parent.isTextblock && $from.parent.childCount === 0;
|
|
117
|
+
if (storedMarks != null || browser.safari && isEmptyTextblock) {
|
|
118
|
+
view.dispatch(view.state.tr.setMeta(reactKeysPluginKey, {
|
|
119
|
+
cursorWrapper: widget(view.state.selection.from, CursorWrapper, {
|
|
120
|
+
key: "cursor-wrapper",
|
|
121
|
+
...storedMarks !== null && {
|
|
122
|
+
marks: storedMarks
|
|
123
|
+
},
|
|
124
|
+
side: 0,
|
|
125
|
+
raw: true
|
|
126
|
+
})
|
|
127
|
+
}));
|
|
128
|
+
// Pin the DOM cursor to PM's canonical position before the IME
|
|
129
|
+
// captures wherever the browser happened to leave it. Without this,
|
|
130
|
+
// a cursor at a mark boundary lands in either the left or right text
|
|
131
|
+
// node depending on the user's last navigation direction, and the
|
|
132
|
+
// IME composes into whichever one it found.
|
|
133
|
+
} else if (view.state.selection.empty && view.domSelection()) {
|
|
134
|
+
view.domObserver.disconnectSelection();
|
|
135
|
+
try {
|
|
136
|
+
view.docView.setSelection(view.state.selection.anchor, view.state.selection.head, view, true // force — skip the isEquivalentPosition early-return
|
|
137
|
+
);
|
|
138
|
+
} finally{
|
|
139
|
+
view.domObserver.setCurSelection();
|
|
140
|
+
view.domObserver.connectSelection();
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
const freezeFrom = view.state.selection.$from.before();
|
|
144
|
+
view.dispatch(view.state.tr.setMeta(reactKeysPluginKey, {
|
|
145
|
+
freezeFrom
|
|
146
|
+
}));
|
|
147
|
+
const frozenDom = view.nodeDOM(freezeFrom);
|
|
148
|
+
if (!frozenDom) {
|
|
149
|
+
view.dispatch(view.state.tr.setMeta(reactKeysPluginKey, {
|
|
150
|
+
cursorWrapper: null,
|
|
151
|
+
freezeFrom: null
|
|
152
|
+
}));
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
preCompositionSnapshot = view.state.doc.nodeAt(freezeFrom)?.content ?? null;
|
|
156
|
+
view.input.composing = true;
|
|
157
|
+
observer = new MutationObserver((records)=>{
|
|
158
|
+
if (reactKeysPluginKey.getState(view.state)?.freezeFrom == null) {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
view.domObserver.queue.push(...records);
|
|
162
|
+
view.domObserver.flush();
|
|
163
|
+
syncCompositionViewDescs(view);
|
|
164
|
+
});
|
|
165
|
+
observer.observe(frozenDom, observeOptions);
|
|
166
|
+
return true;
|
|
167
|
+
},
|
|
168
|
+
compositionupdate () {
|
|
169
|
+
return true;
|
|
170
|
+
},
|
|
171
|
+
compositionend (view, event) {
|
|
172
|
+
if (!(view instanceof ReactEditorView)) return false;
|
|
173
|
+
if (!view.composing) return false;
|
|
174
|
+
teardownCompositionAndUnfreeze(view, event);
|
|
175
|
+
return true;
|
|
176
|
+
},
|
|
177
|
+
contextmenu (view, event) {
|
|
178
|
+
if (!(view instanceof ReactEditorView)) return false;
|
|
179
|
+
if (!view.composing) return false;
|
|
180
|
+
teardownCompositionAndUnfreeze(view, event);
|
|
181
|
+
return false;
|
|
182
|
+
},
|
|
183
|
+
mousedown (view, event) {
|
|
184
|
+
if (!(view instanceof ReactEditorView)) return false;
|
|
185
|
+
if (!view.composing) return false;
|
|
186
|
+
teardownCompositionAndUnfreeze(view, event);
|
|
187
|
+
return false;
|
|
188
|
+
},
|
|
189
|
+
touchstart (view, event) {
|
|
190
|
+
if (!(view instanceof ReactEditorView)) return false;
|
|
191
|
+
if (!view.composing) return false;
|
|
192
|
+
teardownCompositionAndUnfreeze(view, event);
|
|
193
|
+
return false;
|
|
194
|
+
},
|
|
195
|
+
beforeinput (view, event) {
|
|
196
|
+
if (event.inputType !== "insertFromComposition") {
|
|
197
|
+
event.preventDefault();
|
|
198
|
+
}
|
|
199
|
+
switch(event.inputType){
|
|
200
|
+
case "insertParagraph":
|
|
201
|
+
case "insertLineBreak":
|
|
202
|
+
{
|
|
203
|
+
// ProseMirror-view has a hack that runs the Enter event handlers
|
|
204
|
+
// on iOS, to avoid a bug in Safari with calling event.preventDefault() on
|
|
205
|
+
// Enter events.
|
|
206
|
+
//
|
|
207
|
+
// We want to prevent that hack, because we run the Enter event handlers
|
|
208
|
+
// here, where there is no such bug. So we set this flag, which prosemirror-view
|
|
209
|
+
// uses to check whether it should run the deferred event handlers.
|
|
210
|
+
view.input.lastIOSEnter = 0;
|
|
211
|
+
// Fire a synthetic keydown event to trigger ProseMirror's keymap
|
|
212
|
+
const keyEvent = new KeyboardEvent("keydown", {
|
|
213
|
+
bubbles: true,
|
|
214
|
+
cancelable: true,
|
|
215
|
+
key: "Enter",
|
|
216
|
+
code: "Enter",
|
|
217
|
+
keyCode: 13,
|
|
218
|
+
shiftKey: event.inputType === "insertLineBreak"
|
|
219
|
+
});
|
|
220
|
+
// Use someProp to directly call ProseMirror handlers
|
|
221
|
+
return view.someProp("handleKeyDown", (f)=>f(view, keyEvent)) ?? false;
|
|
222
|
+
}
|
|
223
|
+
case "insertReplacementText":
|
|
224
|
+
{
|
|
225
|
+
const ranges = event.getTargetRanges();
|
|
226
|
+
event.dataTransfer?.items[0]?.getAsString((data)=>{
|
|
227
|
+
for (const range of ranges){
|
|
228
|
+
const from = view.posAtDOM(range.startContainer, range.startOffset, 1);
|
|
229
|
+
const to = view.posAtDOM(range.endContainer, range.endOffset, 1);
|
|
230
|
+
insertText(view, data, {
|
|
231
|
+
from,
|
|
232
|
+
to
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
break;
|
|
237
|
+
}
|
|
238
|
+
case "insertText":
|
|
239
|
+
{
|
|
240
|
+
const ranges = event.getTargetRanges();
|
|
241
|
+
if (ranges.length === 0 || ranges.length === 1 && ranges[0] && ranges[0].collapsed) {
|
|
242
|
+
insertText(view, event.data);
|
|
243
|
+
} else {
|
|
244
|
+
for (const range of ranges){
|
|
245
|
+
const from = view.posAtDOM(range.startContainer, range.startOffset, 1);
|
|
246
|
+
const to = view.posAtDOM(range.endContainer, range.endOffset, 1);
|
|
247
|
+
insertText(view, event.data, {
|
|
248
|
+
from,
|
|
249
|
+
to
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
break;
|
|
254
|
+
}
|
|
255
|
+
case "deleteWordBackward":
|
|
256
|
+
case "deleteHardLineBackward":
|
|
257
|
+
case "deleteSoftLineBackward":
|
|
258
|
+
case "deleteContentBackward":
|
|
259
|
+
case "deleteWordForward":
|
|
260
|
+
case "deleteHardLineForward":
|
|
261
|
+
case "deleteSoftLineForward":
|
|
262
|
+
case "deleteContentForward":
|
|
263
|
+
case "deleteContent":
|
|
264
|
+
{
|
|
265
|
+
const targetRanges = event.getTargetRanges();
|
|
266
|
+
const { tr } = view.state;
|
|
267
|
+
for (const range of targetRanges){
|
|
268
|
+
const start = view.posAtDOM(range.startContainer, range.startOffset);
|
|
269
|
+
const end = view.posAtDOM(range.endContainer, range.endOffset);
|
|
270
|
+
const { doc } = view.state;
|
|
271
|
+
const marks = doc.resolve(start).marksAcross(doc.resolve(end));
|
|
272
|
+
tr.delete(start, end);
|
|
273
|
+
if (marks) {
|
|
274
|
+
tr.ensureMarks(marks);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
view.dispatch(tr);
|
|
278
|
+
break;
|
|
279
|
+
}
|
|
280
|
+
default:
|
|
281
|
+
{
|
|
282
|
+
break;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
return true;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
function syncCompositionViewDescs(view) {
|
|
292
|
+
const compositionNode = view.domObserver.lastChangedTextNode;
|
|
293
|
+
if (!compositionNode) return;
|
|
294
|
+
const freezeFrom = reactKeysPluginKey.getState(view.state)?.freezeFrom;
|
|
295
|
+
if (freezeFrom == null) return;
|
|
296
|
+
const compositionBlock = view.state.doc.nodeAt(freezeFrom);
|
|
297
|
+
if (!compositionBlock) return;
|
|
298
|
+
const compositionBlockDesc = view.docView.descAt(freezeFrom);
|
|
299
|
+
if (!compositionBlockDesc) return;
|
|
300
|
+
const desc = view.docView.nearestDesc(compositionNode);
|
|
301
|
+
compositionBlockDesc.node = compositionBlock;
|
|
302
|
+
if (desc instanceof TextViewDesc) {
|
|
303
|
+
if (compositionNode.nodeValue && desc.node.text !== compositionNode.nodeValue) {
|
|
304
|
+
desc.node = view.state.schema.text(compositionNode.nodeValue, desc.node.marks);
|
|
305
|
+
desc.nodeDOM = compositionNode;
|
|
306
|
+
compositionNode.pmViewDesc = desc;
|
|
307
|
+
}
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
if (desc instanceof CompositionViewDesc) {
|
|
311
|
+
if (compositionNode.nodeValue != null && desc.text !== compositionNode.nodeValue) {
|
|
312
|
+
desc.dom = compositionNode;
|
|
313
|
+
desc.textDOM = compositionNode;
|
|
314
|
+
desc.text = compositionNode.nodeValue;
|
|
315
|
+
compositionNode.pmViewDesc = desc;
|
|
316
|
+
}
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
const parentDesc = desc?.contentDOM ? desc : compositionBlockDesc;
|
|
320
|
+
const children = parentDesc.children;
|
|
321
|
+
// Drop any text or composition desc in this container whose DOM the
|
|
322
|
+
// IME has detached. This covers two cases: a TextViewDesc the IME subsumed
|
|
323
|
+
// into the composition node, and (on Safari, which replaces the whole text
|
|
324
|
+
// node on each composition update) any orphaned composition view
|
|
325
|
+
// desc(s) left over from the previous composition steps.
|
|
326
|
+
for(let i = children.length - 1; i >= 0; i--){
|
|
327
|
+
const c = children[i];
|
|
328
|
+
if (!(c instanceof TextViewDesc) && !(c instanceof CompositionViewDesc)) {
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
331
|
+
const dom = c.dom;
|
|
332
|
+
if (view.dom.contains(dom)) continue;
|
|
333
|
+
children.splice(i, 1);
|
|
334
|
+
}
|
|
335
|
+
const contentStart = freezeFrom + 1;
|
|
336
|
+
const { from, to } = view.state.selection;
|
|
337
|
+
const textPos = findTextInFragment(compositionBlock.content, compositionNode.nodeValue ?? "", from - contentStart, to - contentStart);
|
|
338
|
+
if (textPos < 0) return;
|
|
339
|
+
const startPos = contentStart + textPos;
|
|
340
|
+
let topDOM = compositionNode;
|
|
341
|
+
while(topDOM.parentNode && topDOM.parentNode !== parentDesc.contentDOM){
|
|
342
|
+
topDOM = topDOM.parentNode;
|
|
343
|
+
}
|
|
344
|
+
const insertIndex = children.findLastIndex((c)=>c.posBefore <= startPos) + 1;
|
|
345
|
+
children.splice(insertIndex, 0, new CompositionViewDesc(parentDesc, ()=>startPos, topDOM, compositionNode, compositionNode.nodeValue ?? ""));
|
|
346
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { Plugin, PluginKey } from "prosemirror-state";
|
|
2
|
+
import { AddNodeMarkStep, AttrStep, DocAttrStep, RemoveNodeMarkStep } from "prosemirror-transform";
|
|
3
|
+
import { DecorationSet } from "prosemirror-view";
|
|
4
|
+
import { widget } from "../decorations/ReactWidgetType.js";
|
|
5
|
+
// These steps keep every node's size, so no node moves, appears or
|
|
6
|
+
// disappears. Mark steps are not among them: they can split text nodes,
|
|
7
|
+
// which then need keys.
|
|
8
|
+
const keepsNodePositions = (step)=>step instanceof AttrStep || step instanceof DocAttrStep || step instanceof AddNodeMarkStep || step instanceof RemoveNodeMarkStep;
|
|
9
|
+
export function createNodeKey() {
|
|
10
|
+
const key = Math.floor(Math.random() * 0xffffffffffff).toString(16);
|
|
11
|
+
return key;
|
|
12
|
+
}
|
|
13
|
+
export const reactKeysPluginKey = new PluginKey("@handlewithcare/react-prosemirror/reactKeys");
|
|
14
|
+
/**
|
|
15
|
+
* Tracks a unique key for each (non-text) node in the
|
|
16
|
+
* document, identified by its current position. Keys are
|
|
17
|
+
* (mostly) stable across transaction applications. The
|
|
18
|
+
* key for a given node can be accessed by that node's
|
|
19
|
+
* current position in the document, and vice versa.
|
|
20
|
+
*/ export function reactKeys() {
|
|
21
|
+
return new Plugin({
|
|
22
|
+
key: reactKeysPluginKey,
|
|
23
|
+
state: {
|
|
24
|
+
init (_, state) {
|
|
25
|
+
const next = {
|
|
26
|
+
posToKey: new Map(),
|
|
27
|
+
keyToPos: new Map(),
|
|
28
|
+
cursorWrapper: null,
|
|
29
|
+
freezeFrom: null
|
|
30
|
+
};
|
|
31
|
+
state.doc.descendants((_, pos)=>{
|
|
32
|
+
const key = createNodeKey();
|
|
33
|
+
next.posToKey.set(pos, key);
|
|
34
|
+
next.keyToPos.set(key, pos);
|
|
35
|
+
return true;
|
|
36
|
+
});
|
|
37
|
+
return next;
|
|
38
|
+
},
|
|
39
|
+
/**
|
|
40
|
+
* Keeps node keys stable across transactions.
|
|
41
|
+
*
|
|
42
|
+
* To accomplish this, we map each node position forwards
|
|
43
|
+
* through the transaction to identify its current position,
|
|
44
|
+
* and assign its key to that new position, dropping it if the
|
|
45
|
+
* node was deleted.
|
|
46
|
+
*/ apply (tr, value, oldState, newState) {
|
|
47
|
+
const meta = tr.getMeta(reactKeysPluginKey);
|
|
48
|
+
const overrides = meta && "overrides" in meta ? meta.overrides : {};
|
|
49
|
+
const cursorWrapper = meta && "cursorWrapper" in meta ? meta.cursorWrapper : undefined;
|
|
50
|
+
const freezeFrom = meta && "freezeFrom" in meta ? meta.freezeFrom : undefined;
|
|
51
|
+
const next = {
|
|
52
|
+
posToKey: new Map(),
|
|
53
|
+
keyToPos: new Map(),
|
|
54
|
+
cursorWrapper: cursorWrapper === undefined ? value.cursorWrapper ? widget(tr.mapping.map(value.cursorWrapper.from, -1), value.cursorWrapper.type.Component, value.cursorWrapper.spec) : null : cursorWrapper,
|
|
55
|
+
freezeFrom: freezeFrom === undefined ? value.freezeFrom !== null ? tr.mapping.map(value.freezeFrom, -1) : null : freezeFrom
|
|
56
|
+
};
|
|
57
|
+
if (value.freezeFrom !== null && next.freezeFrom !== null && tr.getMeta("composition") == null) {
|
|
58
|
+
const oldBlock = oldState.doc.nodeAt(value.freezeFrom);
|
|
59
|
+
const newBlock = newState.doc.nodeAt(next.freezeFrom);
|
|
60
|
+
if (newBlock && !oldBlock?.eq(newBlock)) {
|
|
61
|
+
next.freezeFrom = null;
|
|
62
|
+
next.cursorWrapper = null;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
if (!tr.docChanged || !meta?.overrides && tr.steps.every(keepsNodePositions)) {
|
|
66
|
+
return {
|
|
67
|
+
...value,
|
|
68
|
+
cursorWrapper: next.cursorWrapper,
|
|
69
|
+
freezeFrom: next.freezeFrom
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
const posToKeyEntries = Array.from(value.posToKey.entries()).sort((param, param1)=>{
|
|
73
|
+
let [a] = param, [b] = param1;
|
|
74
|
+
return a - b;
|
|
75
|
+
});
|
|
76
|
+
for (const [pos, key] of posToKeyEntries){
|
|
77
|
+
const override = overrides?.[pos];
|
|
78
|
+
const { pos: newPos, deleted } = override === undefined ? tr.mapping.mapResult(pos) : {
|
|
79
|
+
pos: override,
|
|
80
|
+
deleted: false
|
|
81
|
+
};
|
|
82
|
+
if (deleted) continue;
|
|
83
|
+
next.posToKey.set(newPos, key);
|
|
84
|
+
next.keyToPos.set(key, newPos);
|
|
85
|
+
}
|
|
86
|
+
newState.doc.descendants((_, pos)=>{
|
|
87
|
+
if (next.posToKey.has(pos)) return true;
|
|
88
|
+
const key = createNodeKey();
|
|
89
|
+
next.posToKey.set(pos, key);
|
|
90
|
+
next.keyToPos.set(key, pos);
|
|
91
|
+
return true;
|
|
92
|
+
});
|
|
93
|
+
return next;
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
props: {
|
|
97
|
+
decorations (state) {
|
|
98
|
+
const deco = reactKeysPluginKey.getState(state)?.cursorWrapper;
|
|
99
|
+
if (!deco) return DecorationSet.empty;
|
|
100
|
+
return DecorationSet.create(state.doc, [
|
|
101
|
+
deco
|
|
102
|
+
]);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
}
|