@handlewithcare/react-prosemirror 3.0.3 → 3.0.5
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/README.md +26 -0
- package/dist/cjs/components/ChildNodeViews.js +3 -1
- package/dist/cjs/components/CursorWrapper.js +6 -11
- package/dist/cjs/components/ProseMirror.js +2 -2
- package/dist/cjs/hooks/useEditor.js +1 -2
- package/dist/cjs/hooks/useMarkViewDescription.js +7 -0
- package/dist/cjs/hooks/useNodeViewDescription.js +3 -0
- package/dist/cjs/plugins/beforeInputPlugin.js +32 -2
- package/dist/cjs/testing/editorViewTestHelpers.js +1 -1
- package/dist/esm/components/ChildNodeViews.js +3 -1
- package/dist/esm/components/CursorWrapper.js +7 -12
- package/dist/esm/components/ProseMirror.js +2 -2
- package/dist/esm/hooks/useEditor.js +1 -2
- package/dist/esm/hooks/useMarkViewDescription.js +7 -0
- package/dist/esm/hooks/useNodeViewDescription.js +3 -0
- package/dist/esm/plugins/beforeInputPlugin.js +33 -3
- package/dist/esm/testing/editorViewTestHelpers.js +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/contexts/EditorContext.d.ts +1 -2
- package/dist/types/hooks/useEditor.d.ts +1 -1
- package/dist/types/testing/editorViewTestHelpers.d.ts +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -95,6 +95,7 @@ import "prosemirror-view/style/prosemirror.css";
|
|
|
95
95
|
- [When should I use this?](#when-should-i-use-this)
|
|
96
96
|
- [Migrations](#migrations)
|
|
97
97
|
- [Looking for someone to collaborate with?](#looking-for-someone-to-collaborate-with)
|
|
98
|
+
- [Sponsors](#sponsors)
|
|
98
99
|
|
|
99
100
|
<!-- tocstop -->
|
|
100
101
|
|
|
@@ -818,3 +819,28 @@ Reach out to [Handle with Care](https://handlewithcare.dev/#get-in-touch)! We're
|
|
|
818
819
|
a product development collective with years of experience bringing excellent
|
|
819
820
|
ideas to life. We love React and ProseMirror, and we're always looking for new
|
|
820
821
|
folks to work with!
|
|
822
|
+
|
|
823
|
+
<!-- NOTE: This section is autogenerated. Do not manually edit.-->
|
|
824
|
+
<!--sponsorsstart-->
|
|
825
|
+
|
|
826
|
+
## Sponsors
|
|
827
|
+
|
|
828
|
+
The following companies, organizations, and individuals support Pitter Patter's
|
|
829
|
+
ongoing development.
|
|
830
|
+
|
|
831
|
+
Sponsors receive regular updates about development progress, including previews
|
|
832
|
+
of upcoming features, priority getting issues addressed as we release features,
|
|
833
|
+
and a direct line of communication to us for support. Funders above a certain
|
|
834
|
+
threshold join our steering committee — a lightweight governance model where
|
|
835
|
+
significant contributors help shape our roadmap priorities.
|
|
836
|
+
|
|
837
|
+
[Become a Sponsor](https://handlewithcare.dev/pitter-patter/#become-a-sponsor)
|
|
838
|
+
|
|
839
|
+
<h3>Sponsors</h3>
|
|
840
|
+
<p>
|
|
841
|
+
<a href="https://www.moment.dev/"><img src="https://media.githubusercontent.com/media/handlewithcarecollective/pitter-patter-sponsors/main/logos/Moment.png" alt="Moment" height="128"></a>
|
|
842
|
+
<a href="https://www.lingco.io/"><img src="https://media.githubusercontent.com/media/handlewithcarecollective/pitter-patter-sponsors/main/logos/Lingco.png" alt="Lingco" height="128"></a>
|
|
843
|
+
<a href="https://dskrpt.de/"><img src="https://media.githubusercontent.com/media/handlewithcarecollective/pitter-patter-sponsors/main/logos/Dskrpt.png" alt="Dskrpt" height="128"></a>
|
|
844
|
+
<a href="https://github.com/fastrepl/"><img src="https://media.githubusercontent.com/media/handlewithcarecollective/pitter-patter-sponsors/main/logos/Fastrepl.png" alt="Fastrepl" height="128"></a>
|
|
845
|
+
</p>
|
|
846
|
+
<!--sponsorsend-->
|
|
@@ -244,7 +244,9 @@ function adjustWidgetMarksBack(widgetChildren, nodeChild) {
|
|
|
244
244
|
const child = widgetChildren[i];
|
|
245
245
|
if (// Using internal Decoration property, "type"
|
|
246
246
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
247
|
-
child.widget.type.side < 0
|
|
247
|
+
child.widget.type.side < 0 || // Using internal Decoration property, "type"
|
|
248
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
249
|
+
child.widget.type.spec.marks) {
|
|
248
250
|
continue;
|
|
249
251
|
}
|
|
250
252
|
child.marks = child.marks.reduce((acc, mark)=>mark.addToSet(acc), marksToSpread);
|
|
@@ -54,7 +54,6 @@ function _interop_require_wildcard(obj, nodeInterop) {
|
|
|
54
54
|
}
|
|
55
55
|
const CursorWrapper = /*#__PURE__*/ (0, _react.forwardRef)(function CursorWrapper(param, ref) {
|
|
56
56
|
let { widget, getPos, ...props } = param;
|
|
57
|
-
const [shouldRender, setShouldRender] = (0, _react.useState)(true);
|
|
58
57
|
const innerRef = (0, _react.useRef)(null);
|
|
59
58
|
(0, _react.useImperativeHandle)(ref, ()=>{
|
|
60
59
|
return innerRef.current;
|
|
@@ -65,27 +64,23 @@ const CursorWrapper = /*#__PURE__*/ (0, _react.forwardRef)(function CursorWrappe
|
|
|
65
64
|
view.domObserver.disconnectSelection();
|
|
66
65
|
// @ts-expect-error Internal property - domSelection
|
|
67
66
|
const domSel = view.domSelection();
|
|
68
|
-
const range = document.createRange();
|
|
69
67
|
const node = innerRef.current;
|
|
70
68
|
const img = node.nodeName == "IMG";
|
|
71
|
-
if (img
|
|
72
|
-
|
|
69
|
+
if (img) {
|
|
70
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
71
|
+
domSel.collapse(node.parentNode, (0, _dom.domIndex)(node) + 1);
|
|
73
72
|
} else {
|
|
74
|
-
|
|
73
|
+
domSel.collapse(node, 0);
|
|
75
74
|
}
|
|
76
|
-
range.collapse(false);
|
|
77
|
-
domSel.removeAllRanges();
|
|
78
|
-
domSel.addRange(range);
|
|
79
|
-
setShouldRender(false);
|
|
80
75
|
// @ts-expect-error Internal property - domObserver
|
|
81
76
|
view.domObserver.connectSelection();
|
|
82
77
|
}, []);
|
|
83
|
-
return
|
|
78
|
+
return /*#__PURE__*/ _react.default.createElement("img", {
|
|
84
79
|
ref: innerRef,
|
|
85
80
|
className: "ProseMirror-separator",
|
|
86
81
|
// eslint-disable-next-line react/no-unknown-property
|
|
87
82
|
"mark-placeholder": "true",
|
|
88
83
|
alt: "",
|
|
89
84
|
...props
|
|
90
|
-
})
|
|
85
|
+
});
|
|
91
86
|
});
|
|
@@ -73,7 +73,7 @@ const rootChildDescriptionsContextValue = {
|
|
|
73
73
|
function ProseMirrorInner(param) {
|
|
74
74
|
let { children, nodeViewComponents, markViewComponents, ...props } = param;
|
|
75
75
|
const [mount, setMount] = (0, _react.useState)(null);
|
|
76
|
-
const { editor, state } = (0, _useEditor.useEditor)(mount, props);
|
|
76
|
+
const { editor, cursorWrapper, state } = (0, _useEditor.useEditor)(mount, props);
|
|
77
77
|
const nodeViewConstructors = editor.view.nodeViews;
|
|
78
78
|
const nodeViewContextValue = (0, _react.useMemo)(()=>{
|
|
79
79
|
return {
|
|
@@ -90,7 +90,7 @@ function ProseMirrorInner(param) {
|
|
|
90
90
|
]);
|
|
91
91
|
const node = state.doc;
|
|
92
92
|
const decorations = (0, _computeDocDeco.computeDocDeco)(editor.view);
|
|
93
|
-
const innerDecorations = (0, _viewDecorations.viewDecorations)(editor.view,
|
|
93
|
+
const innerDecorations = (0, _viewDecorations.viewDecorations)(editor.view, cursorWrapper);
|
|
94
94
|
const docNodeViewContextValue = (0, _react.useMemo)(()=>({
|
|
95
95
|
setMount,
|
|
96
96
|
node,
|
|
@@ -109,13 +109,11 @@ function useEditor(mount, options) {
|
|
|
109
109
|
view.update(directEditorProps);
|
|
110
110
|
const editor = (0, _react.useMemo)(()=>({
|
|
111
111
|
view,
|
|
112
|
-
cursorWrapper,
|
|
113
112
|
flushSyncRef,
|
|
114
113
|
registerEventListener,
|
|
115
114
|
unregisterEventListener,
|
|
116
115
|
isStatic: options.static ?? false
|
|
117
116
|
}), [
|
|
118
|
-
cursorWrapper,
|
|
119
117
|
options.static,
|
|
120
118
|
registerEventListener,
|
|
121
119
|
unregisterEventListener,
|
|
@@ -123,6 +121,7 @@ function useEditor(mount, options) {
|
|
|
123
121
|
]);
|
|
124
122
|
return {
|
|
125
123
|
editor,
|
|
124
|
+
cursorWrapper,
|
|
126
125
|
state
|
|
127
126
|
};
|
|
128
127
|
}
|
|
@@ -38,6 +38,13 @@ function useMarkViewDescription(getDOM, getContentDOM, constructor, props) {
|
|
|
38
38
|
const children = childrenRef.current;
|
|
39
39
|
const contentDOM = getContentDOM(markView);
|
|
40
40
|
const viewDesc = new _viewdesc.ReactMarkViewDesc(parent, children, getPos, mark, dom, contentDOM ?? markView.dom, markView);
|
|
41
|
+
// When create() runs after a destroy() (either here in a layout
|
|
42
|
+
// effect or in refUpdated), the inherited children still reference
|
|
43
|
+
// the just-destroyed desc. Re-parent them onto this fresh desc
|
|
44
|
+
// before any other code can observe the stale pointer.
|
|
45
|
+
for (const child of children){
|
|
46
|
+
child.parent = viewDesc;
|
|
47
|
+
}
|
|
41
48
|
contentDOMRef.current = contentDOM;
|
|
42
49
|
return viewDesc;
|
|
43
50
|
});
|
|
@@ -39,6 +39,9 @@ function useNodeViewDescription(getDOM, getContentDOM, constructor, props) {
|
|
|
39
39
|
const contentDOM = getContentDOM(nodeView);
|
|
40
40
|
const nodeDOM = nodeView.dom;
|
|
41
41
|
const viewDesc = new _viewdesc.ReactNodeViewDesc(parent, children, getPos, node, decorations, innerDecorations, dom, contentDOM, nodeDOM, nodeView);
|
|
42
|
+
for (const child of children){
|
|
43
|
+
child.parent = viewDesc;
|
|
44
|
+
}
|
|
42
45
|
const siblings = siblingsRef.current;
|
|
43
46
|
if (!siblings.includes(viewDesc)) {
|
|
44
47
|
siblings.push(viewDesc);
|
|
@@ -8,6 +8,7 @@ Object.defineProperty(exports, "beforeInputPlugin", {
|
|
|
8
8
|
return beforeInputPlugin;
|
|
9
9
|
}
|
|
10
10
|
});
|
|
11
|
+
const _prosemirrormodel = require("prosemirror-model");
|
|
11
12
|
const _prosemirrorstate = require("prosemirror-state");
|
|
12
13
|
const _CursorWrapper = require("../components/CursorWrapper.js");
|
|
13
14
|
const _ReactWidgetType = require("../decorations/ReactWidgetType.js");
|
|
@@ -25,6 +26,34 @@ function insertText(view, eventData) {
|
|
|
25
26
|
view.dispatch(tr);
|
|
26
27
|
return true;
|
|
27
28
|
}
|
|
29
|
+
// Taken from https://github.com/ProseMirror/prosemirror-gapcursor/blob/master/src/index.ts#L67-L84
|
|
30
|
+
// This is a hack that, when a composition starts while a gap cursor
|
|
31
|
+
// is active, quickly creates an inline context for the composition to
|
|
32
|
+
// happen in, to avoid it being aborted by the DOM selection being
|
|
33
|
+
// moved into a valid position.
|
|
34
|
+
//
|
|
35
|
+
// We can't rely on the actual hack from prosemirror-gapcursor, because
|
|
36
|
+
// it happens too late. We snapshot the DOM during compositionstart, but
|
|
37
|
+
// the gapcursor hack runs in beforeinput (after compositionstart).
|
|
38
|
+
function handleGapCursorComposition(view) {
|
|
39
|
+
// @ts-expect-error Internal property - jsonID
|
|
40
|
+
if (!(view.state.selection.jsonID === "gapcursor")) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const { $from } = view.state.selection;
|
|
44
|
+
const insert = $from.parent.contentMatchAt($from.index())// All schemas _must_ have a text node type
|
|
45
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
46
|
+
.findWrapping(view.state.schema.nodes.text);
|
|
47
|
+
if (!insert) return;
|
|
48
|
+
let fragment = _prosemirrormodel.Fragment.empty;
|
|
49
|
+
for(let i = insert.length - 1; i >= 0; i--){
|
|
50
|
+
fragment = _prosemirrormodel.Fragment.from(// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
51
|
+
insert[i].createAndFill(null, fragment));
|
|
52
|
+
}
|
|
53
|
+
const tr = view.state.tr.replace($from.pos, $from.pos, new _prosemirrormodel.Slice(fragment, 0, 0));
|
|
54
|
+
tr.setSelection(_prosemirrorstate.TextSelection.near(tr.doc.resolve($from.pos + 1)));
|
|
55
|
+
view.dispatch(tr);
|
|
56
|
+
}
|
|
28
57
|
function beforeInputPlugin(setCursorWrapper) {
|
|
29
58
|
let compositionMarks = null;
|
|
30
59
|
let precompositionSnapshot = null;
|
|
@@ -32,10 +61,11 @@ function beforeInputPlugin(setCursorWrapper) {
|
|
|
32
61
|
props: {
|
|
33
62
|
handleDOMEvents: {
|
|
34
63
|
compositionstart (view) {
|
|
64
|
+
compositionMarks = view.state.storedMarks ?? view.state.selection.$from.marks();
|
|
65
|
+
view.dispatch(view.state.tr.deleteSelection());
|
|
66
|
+
handleGapCursorComposition(view);
|
|
35
67
|
const { state } = view;
|
|
36
|
-
view.dispatch(state.tr.deleteSelection());
|
|
37
68
|
const $pos = state.selection.$from;
|
|
38
|
-
compositionMarks = state.storedMarks ?? $pos.marks();
|
|
39
69
|
if (compositionMarks) {
|
|
40
70
|
setCursorWrapper((0, _ReactWidgetType.widget)(state.selection.from, _CursorWrapper.CursorWrapper, {
|
|
41
71
|
key: "cursor-wrapper",
|
|
@@ -51,7 +51,7 @@ function tempEditor(param) {
|
|
|
51
51
|
const state = _prosemirrorstate.EditorState.create({
|
|
52
52
|
doc: startDoc,
|
|
53
53
|
schema: _prosemirrortestbuilder.schema,
|
|
54
|
-
selection: selection ?? startDoc.tag?.a ? _prosemirrorstate.TextSelection.create(startDoc, startDoc.tag.a, startDoc.tag?.b) : undefined,
|
|
54
|
+
selection: selection ?? (startDoc.tag?.a ? _prosemirrorstate.TextSelection.create(startDoc, startDoc.tag.a, startDoc.tag?.b) : undefined),
|
|
55
55
|
plugins: [
|
|
56
56
|
...plugins ?? [],
|
|
57
57
|
(0, _reactKeys.reactKeys)()
|
|
@@ -185,7 +185,9 @@ function adjustWidgetMarksBack(widgetChildren, nodeChild) {
|
|
|
185
185
|
const child = widgetChildren[i];
|
|
186
186
|
if (// Using internal Decoration property, "type"
|
|
187
187
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
188
|
-
child.widget.type.side < 0
|
|
188
|
+
child.widget.type.side < 0 || // Using internal Decoration property, "type"
|
|
189
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
190
|
+
child.widget.type.spec.marks) {
|
|
189
191
|
continue;
|
|
190
192
|
}
|
|
191
193
|
child.marks = child.marks.reduce((acc, mark)=>mark.addToSet(acc), marksToSpread);
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import React, { forwardRef, useImperativeHandle, useRef
|
|
1
|
+
import React, { forwardRef, useImperativeHandle, useRef } from "react";
|
|
2
2
|
import { domIndex } from "../dom.js";
|
|
3
3
|
import { useEditorEffect } from "../hooks/useEditorEffect.js";
|
|
4
4
|
export const CursorWrapper = /*#__PURE__*/ forwardRef(function CursorWrapper(param, ref) {
|
|
5
5
|
let { widget, getPos, ...props } = param;
|
|
6
|
-
const [shouldRender, setShouldRender] = useState(true);
|
|
7
6
|
const innerRef = useRef(null);
|
|
8
7
|
useImperativeHandle(ref, ()=>{
|
|
9
8
|
return innerRef.current;
|
|
@@ -14,27 +13,23 @@ export const CursorWrapper = /*#__PURE__*/ forwardRef(function CursorWrapper(par
|
|
|
14
13
|
view.domObserver.disconnectSelection();
|
|
15
14
|
// @ts-expect-error Internal property - domSelection
|
|
16
15
|
const domSel = view.domSelection();
|
|
17
|
-
const range = document.createRange();
|
|
18
16
|
const node = innerRef.current;
|
|
19
17
|
const img = node.nodeName == "IMG";
|
|
20
|
-
if (img
|
|
21
|
-
|
|
18
|
+
if (img) {
|
|
19
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
20
|
+
domSel.collapse(node.parentNode, domIndex(node) + 1);
|
|
22
21
|
} else {
|
|
23
|
-
|
|
22
|
+
domSel.collapse(node, 0);
|
|
24
23
|
}
|
|
25
|
-
range.collapse(false);
|
|
26
|
-
domSel.removeAllRanges();
|
|
27
|
-
domSel.addRange(range);
|
|
28
|
-
setShouldRender(false);
|
|
29
24
|
// @ts-expect-error Internal property - domObserver
|
|
30
25
|
view.domObserver.connectSelection();
|
|
31
26
|
}, []);
|
|
32
|
-
return
|
|
27
|
+
return /*#__PURE__*/ React.createElement("img", {
|
|
33
28
|
ref: innerRef,
|
|
34
29
|
className: "ProseMirror-separator",
|
|
35
30
|
// eslint-disable-next-line react/no-unknown-property
|
|
36
31
|
"mark-placeholder": "true",
|
|
37
32
|
alt: "",
|
|
38
33
|
...props
|
|
39
|
-
})
|
|
34
|
+
});
|
|
40
35
|
});
|
|
@@ -22,7 +22,7 @@ const rootChildDescriptionsContextValue = {
|
|
|
22
22
|
function ProseMirrorInner(param) {
|
|
23
23
|
let { children, nodeViewComponents, markViewComponents, ...props } = param;
|
|
24
24
|
const [mount, setMount] = useState(null);
|
|
25
|
-
const { editor, state } = useEditor(mount, props);
|
|
25
|
+
const { editor, cursorWrapper, state } = useEditor(mount, props);
|
|
26
26
|
const nodeViewConstructors = editor.view.nodeViews;
|
|
27
27
|
const nodeViewContextValue = useMemo(()=>{
|
|
28
28
|
return {
|
|
@@ -39,7 +39,7 @@ function ProseMirrorInner(param) {
|
|
|
39
39
|
]);
|
|
40
40
|
const node = state.doc;
|
|
41
41
|
const decorations = computeDocDeco(editor.view);
|
|
42
|
-
const innerDecorations = viewDecorations(editor.view,
|
|
42
|
+
const innerDecorations = viewDecorations(editor.view, cursorWrapper);
|
|
43
43
|
const docNodeViewContextValue = useMemo(()=>({
|
|
44
44
|
setMount,
|
|
45
45
|
node,
|
|
@@ -107,13 +107,11 @@ let didWarnValueDefaultValue = false;
|
|
|
107
107
|
view.update(directEditorProps);
|
|
108
108
|
const editor = useMemo(()=>({
|
|
109
109
|
view,
|
|
110
|
-
cursorWrapper,
|
|
111
110
|
flushSyncRef,
|
|
112
111
|
registerEventListener,
|
|
113
112
|
unregisterEventListener,
|
|
114
113
|
isStatic: options.static ?? false
|
|
115
114
|
}), [
|
|
116
|
-
cursorWrapper,
|
|
117
115
|
options.static,
|
|
118
116
|
registerEventListener,
|
|
119
117
|
unregisterEventListener,
|
|
@@ -121,6 +119,7 @@ let didWarnValueDefaultValue = false;
|
|
|
121
119
|
]);
|
|
122
120
|
return {
|
|
123
121
|
editor,
|
|
122
|
+
cursorWrapper,
|
|
124
123
|
state
|
|
125
124
|
};
|
|
126
125
|
}
|
|
@@ -28,6 +28,13 @@ export function useMarkViewDescription(getDOM, getContentDOM, constructor, props
|
|
|
28
28
|
const children = childrenRef.current;
|
|
29
29
|
const contentDOM = getContentDOM(markView);
|
|
30
30
|
const viewDesc = new ReactMarkViewDesc(parent, children, getPos, mark, dom, contentDOM ?? markView.dom, markView);
|
|
31
|
+
// When create() runs after a destroy() (either here in a layout
|
|
32
|
+
// effect or in refUpdated), the inherited children still reference
|
|
33
|
+
// the just-destroyed desc. Re-parent them onto this fresh desc
|
|
34
|
+
// before any other code can observe the stale pointer.
|
|
35
|
+
for (const child of children){
|
|
36
|
+
child.parent = viewDesc;
|
|
37
|
+
}
|
|
31
38
|
contentDOMRef.current = contentDOM;
|
|
32
39
|
return viewDesc;
|
|
33
40
|
});
|
|
@@ -29,6 +29,9 @@ export function useNodeViewDescription(getDOM, getContentDOM, constructor, props
|
|
|
29
29
|
const contentDOM = getContentDOM(nodeView);
|
|
30
30
|
const nodeDOM = nodeView.dom;
|
|
31
31
|
const viewDesc = new ReactNodeViewDesc(parent, children, getPos, node, decorations, innerDecorations, dom, contentDOM, nodeDOM, nodeView);
|
|
32
|
+
for (const child of children){
|
|
33
|
+
child.parent = viewDesc;
|
|
34
|
+
}
|
|
32
35
|
const siblings = siblingsRef.current;
|
|
33
36
|
if (!siblings.includes(viewDesc)) {
|
|
34
37
|
siblings.push(viewDesc);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Fragment, Slice } from "prosemirror-model";
|
|
2
|
+
import { Plugin, TextSelection } from "prosemirror-state";
|
|
2
3
|
import { CursorWrapper } from "../components/CursorWrapper.js";
|
|
3
4
|
import { widget } from "../decorations/ReactWidgetType.js";
|
|
4
5
|
function insertText(view, eventData) {
|
|
@@ -15,6 +16,34 @@ function insertText(view, eventData) {
|
|
|
15
16
|
view.dispatch(tr);
|
|
16
17
|
return true;
|
|
17
18
|
}
|
|
19
|
+
// Taken from https://github.com/ProseMirror/prosemirror-gapcursor/blob/master/src/index.ts#L67-L84
|
|
20
|
+
// This is a hack that, when a composition starts while a gap cursor
|
|
21
|
+
// is active, quickly creates an inline context for the composition to
|
|
22
|
+
// happen in, to avoid it being aborted by the DOM selection being
|
|
23
|
+
// moved into a valid position.
|
|
24
|
+
//
|
|
25
|
+
// We can't rely on the actual hack from prosemirror-gapcursor, because
|
|
26
|
+
// it happens too late. We snapshot the DOM during compositionstart, but
|
|
27
|
+
// the gapcursor hack runs in beforeinput (after compositionstart).
|
|
28
|
+
function handleGapCursorComposition(view) {
|
|
29
|
+
// @ts-expect-error Internal property - jsonID
|
|
30
|
+
if (!(view.state.selection.jsonID === "gapcursor")) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const { $from } = view.state.selection;
|
|
34
|
+
const insert = $from.parent.contentMatchAt($from.index())// All schemas _must_ have a text node type
|
|
35
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
36
|
+
.findWrapping(view.state.schema.nodes.text);
|
|
37
|
+
if (!insert) return;
|
|
38
|
+
let fragment = Fragment.empty;
|
|
39
|
+
for(let i = insert.length - 1; i >= 0; i--){
|
|
40
|
+
fragment = Fragment.from(// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
41
|
+
insert[i].createAndFill(null, fragment));
|
|
42
|
+
}
|
|
43
|
+
const tr = view.state.tr.replace($from.pos, $from.pos, new Slice(fragment, 0, 0));
|
|
44
|
+
tr.setSelection(TextSelection.near(tr.doc.resolve($from.pos + 1)));
|
|
45
|
+
view.dispatch(tr);
|
|
46
|
+
}
|
|
18
47
|
export function beforeInputPlugin(setCursorWrapper) {
|
|
19
48
|
let compositionMarks = null;
|
|
20
49
|
let precompositionSnapshot = null;
|
|
@@ -22,10 +51,11 @@ export function beforeInputPlugin(setCursorWrapper) {
|
|
|
22
51
|
props: {
|
|
23
52
|
handleDOMEvents: {
|
|
24
53
|
compositionstart (view) {
|
|
54
|
+
compositionMarks = view.state.storedMarks ?? view.state.selection.$from.marks();
|
|
55
|
+
view.dispatch(view.state.tr.deleteSelection());
|
|
56
|
+
handleGapCursorComposition(view);
|
|
25
57
|
const { state } = view;
|
|
26
|
-
view.dispatch(state.tr.deleteSelection());
|
|
27
58
|
const $pos = state.selection.$from;
|
|
28
|
-
compositionMarks = state.storedMarks ?? $pos.marks();
|
|
29
59
|
if (compositionMarks) {
|
|
30
60
|
setCursorWrapper(widget(state.selection.from, CursorWrapper, {
|
|
31
61
|
key: "cursor-wrapper",
|
|
@@ -28,7 +28,7 @@ export function tempEditor(param) {
|
|
|
28
28
|
const state = EditorState.create({
|
|
29
29
|
doc: startDoc,
|
|
30
30
|
schema,
|
|
31
|
-
selection: selection ?? startDoc.tag?.a ? TextSelection.create(startDoc, startDoc.tag.a, startDoc.tag?.b) : undefined,
|
|
31
|
+
selection: selection ?? (startDoc.tag?.a ? TextSelection.create(startDoc, startDoc.tag.a, startDoc.tag?.b) : undefined),
|
|
32
32
|
plugins: [
|
|
33
33
|
...plugins ?? [],
|
|
34
34
|
reactKeys()
|