@handlewithcare/react-prosemirror 3.1.0-tiptap.50 → 3.1.0-tiptap.51
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/dist/cjs/components/ChildNodeViews.js +2 -2
- package/dist/cjs/components/CursorWrapper.js +1 -3
- package/dist/cjs/components/TextNodeView.js +15 -18
- package/dist/cjs/components/TrailingHackView.js +29 -0
- package/dist/cjs/hooks/useEditor.js +2 -13
- package/dist/cjs/hooks/useNodeViewDescription.js +35 -37
- package/dist/cjs/plugins/beforeInputPlugin.js +9 -17
- package/dist/esm/components/ChildNodeViews.js +2 -2
- package/dist/esm/components/CursorWrapper.js +1 -3
- package/dist/esm/components/TextNodeView.js +15 -18
- package/dist/esm/components/TrailingHackView.js +30 -1
- package/dist/esm/hooks/useEditor.js +2 -13
- package/dist/esm/hooks/useNodeViewDescription.js +35 -37
- package/dist/esm/plugins/beforeInputPlugin.js +9 -17
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/ReactEditorView.d.ts +1 -1
- package/dist/types/components/TrailingHackView.d.ts +1 -1
- package/package.json +1 -1
|
@@ -404,14 +404,14 @@ const ChildNodeViews = /*#__PURE__*/ (0, _react.memo)(function ChildNodeViews(pa
|
|
|
404
404
|
component: _SeparatorHackView.SeparatorHackView,
|
|
405
405
|
marks: [],
|
|
406
406
|
offset: lastChild?.offset ?? 0,
|
|
407
|
-
index: (lastChild?.index ?? 0) +
|
|
407
|
+
index: (lastChild?.index ?? 0) + 1,
|
|
408
408
|
key: "trailing-hack-img"
|
|
409
409
|
}, {
|
|
410
410
|
type: "hack",
|
|
411
411
|
component: _TrailingHackView.TrailingHackView,
|
|
412
412
|
marks: [],
|
|
413
413
|
offset: lastChild?.offset ?? 0,
|
|
414
|
-
index: (lastChild?.index ?? 0) +
|
|
414
|
+
index: (lastChild?.index ?? 0) + 2,
|
|
415
415
|
key: "trailing-hack-br"
|
|
416
416
|
});
|
|
417
417
|
}
|
|
@@ -70,9 +70,7 @@ const CursorWrapper = /*#__PURE__*/ (0, _react.forwardRef)(function CursorWrappe
|
|
|
70
70
|
domSel.collapse(node.parentNode, (0, _dom.domIndex)(node) + 1);
|
|
71
71
|
// @ts-expect-error Internal property - domObserver
|
|
72
72
|
view.domObserver.connectSelection();
|
|
73
|
-
|
|
74
|
-
setShouldRender(false);
|
|
75
|
-
});
|
|
73
|
+
setShouldRender(false);
|
|
76
74
|
}, []);
|
|
77
75
|
return shouldRender ? /*#__PURE__*/ _react.default.createElement("img", {
|
|
78
76
|
ref: innerRef,
|
|
@@ -55,8 +55,7 @@ let TextNodeView = class TextNodeView extends _react.Component {
|
|
|
55
55
|
}
|
|
56
56
|
const pos = getPos();
|
|
57
57
|
const { from, to } = view.state.selection;
|
|
58
|
-
if (!(view.state.selection instanceof _prosemirrorstate.TextSelection) || from < pos ||
|
|
59
|
-
to > pos + node.nodeSize) {
|
|
58
|
+
if (!(view.state.selection instanceof _prosemirrorstate.TextSelection) || from < pos || to > pos + node.nodeSize) {
|
|
60
59
|
return false;
|
|
61
60
|
}
|
|
62
61
|
return this.containsCompositionNodeText;
|
|
@@ -123,22 +122,20 @@ let TextNodeView = class TextNodeView extends _react.Component {
|
|
|
123
122
|
this.destroy();
|
|
124
123
|
this.viewDescRef = this.create();
|
|
125
124
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
this.containsCompositionNodeText = node.text === text;
|
|
141
|
-
});
|
|
125
|
+
const { view, node } = this.props;
|
|
126
|
+
if (!(view instanceof _ReactEditorView.ReactEditorView)) {
|
|
127
|
+
this.containsCompositionNodeText = true;
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
const textNode = view.input.compositionNode;
|
|
131
|
+
if (!textNode) {
|
|
132
|
+
this.containsCompositionNodeText = true;
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
136
|
+
const text = textNode.nodeValue;
|
|
137
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
138
|
+
this.containsCompositionNodeText = node.text === text;
|
|
142
139
|
}
|
|
143
140
|
shouldComponentUpdate(nextProps) {
|
|
144
141
|
// When leaving the protected state, force a re-render so React's
|
|
@@ -11,6 +11,8 @@ Object.defineProperty(exports, "TrailingHackView", {
|
|
|
11
11
|
const _react = /*#__PURE__*/ _interop_require_wildcard(require("react"));
|
|
12
12
|
const _ChildDescriptionsContext = require("../contexts/ChildDescriptionsContext.js");
|
|
13
13
|
const _useClientLayoutEffect = require("../hooks/useClientLayoutEffect.js");
|
|
14
|
+
const _useEditorEffect = require("../hooks/useEditorEffect.js");
|
|
15
|
+
const _useEditorEventListener = require("../hooks/useEditorEventListener.js");
|
|
14
16
|
const _viewdesc = require("../viewdesc.js");
|
|
15
17
|
function _getRequireWildcardCache(nodeInterop) {
|
|
16
18
|
if (typeof WeakMap !== "function") return null;
|
|
@@ -55,6 +57,7 @@ function _interop_require_wildcard(obj, nodeInterop) {
|
|
|
55
57
|
}
|
|
56
58
|
function TrailingHackView(param) {
|
|
57
59
|
let { getPos } = param;
|
|
60
|
+
const [shouldRender, setShouldRender] = (0, _react.useState)(true);
|
|
58
61
|
const { siblingsRef, parentRef } = (0, _react.useContext)(_ChildDescriptionsContext.ChildDescriptionsContext);
|
|
59
62
|
const viewDescRef = (0, _react.useRef)(null);
|
|
60
63
|
const ref = (0, _react.useRef)(null);
|
|
@@ -83,6 +86,32 @@ function TrailingHackView(param) {
|
|
|
83
86
|
}
|
|
84
87
|
siblingsRef.current.sort(_viewdesc.sortViewDescs);
|
|
85
88
|
});
|
|
89
|
+
// At the start of a composition, the browser will automatically delete
|
|
90
|
+
// the trailing hack br element. We need to unmount ourselves _before_
|
|
91
|
+
// that happens, so that React doesn't try to remove the already-removed
|
|
92
|
+
// br node when this component gets unmounted
|
|
93
|
+
(0, _useEditorEventListener.useEditorEventListener)("compositionstart", (view)=>{
|
|
94
|
+
const { from } = view.state.selection;
|
|
95
|
+
if (from === getPos()) {
|
|
96
|
+
setShouldRender(false);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
// We need to run the same composition check when we first get mounted,
|
|
100
|
+
// in case we got mounted in the same render batch as the beginning of
|
|
101
|
+
// a composition
|
|
102
|
+
(0, _useEditorEffect.useEditorEffect)((view)=>{
|
|
103
|
+
if (!view.composing) return;
|
|
104
|
+
const { from } = view.state.selection;
|
|
105
|
+
if (from === getPos()) {
|
|
106
|
+
setShouldRender(false);
|
|
107
|
+
}
|
|
108
|
+
}, [
|
|
109
|
+
getPos
|
|
110
|
+
]);
|
|
111
|
+
(0, _useEditorEventListener.useEditorEventListener)("compositionend", ()=>{
|
|
112
|
+
setShouldRender(true);
|
|
113
|
+
});
|
|
114
|
+
if (!shouldRender) return null;
|
|
86
115
|
return /*#__PURE__*/ _react.default.createElement("br", {
|
|
87
116
|
ref: ref,
|
|
88
117
|
className: "ProseMirror-trailingBreak"
|
|
@@ -102,20 +102,9 @@ function useEditor(mount, options) {
|
|
|
102
102
|
// running effects. Running effects will reattach selection
|
|
103
103
|
// change listeners if the EditorView has been destroyed.
|
|
104
104
|
if (view instanceof _ReactEditorView.ReactEditorView && !view.isDestroyed) {
|
|
105
|
-
|
|
106
|
-
setTimeout(()=>{
|
|
107
|
-
// Plugins might dispatch transactions from their
|
|
108
|
-
// view update lifecycle hooks
|
|
109
|
-
flushSyncRef.current = false;
|
|
110
|
-
view.commitPendingEffects();
|
|
111
|
-
flushSyncRef.current = true;
|
|
112
|
-
});
|
|
113
|
-
} else {
|
|
114
|
-
flushSyncRef.current = false;
|
|
115
|
-
view.commitPendingEffects();
|
|
116
|
-
flushSyncRef.current = true;
|
|
117
|
-
}
|
|
105
|
+
flushSyncRef.current = false;
|
|
118
106
|
view.commitPendingEffects();
|
|
107
|
+
flushSyncRef.current = true;
|
|
119
108
|
}
|
|
120
109
|
});
|
|
121
110
|
view.update(directEditorProps);
|
|
@@ -144,45 +144,43 @@ function useNodeViewDescription(getDOM, getContentDOM, constructor, props) {
|
|
|
144
144
|
for (const child of children){
|
|
145
145
|
child.parent = viewDesc;
|
|
146
146
|
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
break;
|
|
168
|
-
}
|
|
147
|
+
if (!props.node.isTextblock) return;
|
|
148
|
+
// Because TextNodeViews can't locate the DOM nodes
|
|
149
|
+
// for compositions, we need to override them here
|
|
150
|
+
if (!viewDescRef.current?.contentDOM) return;
|
|
151
|
+
const compositionChildIndex = children.findIndex((child)=>child instanceof _viewdesc.CompositionViewDesc);
|
|
152
|
+
if (compositionChildIndex === -1) return;
|
|
153
|
+
const compositionViewDesc = children[compositionChildIndex];
|
|
154
|
+
if (!(compositionViewDesc instanceof _viewdesc.CompositionViewDesc)) return;
|
|
155
|
+
let compositionTopDOM = null;
|
|
156
|
+
let search = children[compositionChildIndex - 1];
|
|
157
|
+
while(search instanceof _viewdesc.MarkViewDesc){
|
|
158
|
+
search = search.children[0];
|
|
159
|
+
}
|
|
160
|
+
if (search instanceof _viewdesc.WidgetViewDesc && search.widget.type instanceof _ReactWidgetType.ReactWidgetType && search.widget.type.Component === _CursorWrapper.CursorWrapper) {
|
|
161
|
+
compositionTopDOM = search.dom.nextSibling;
|
|
162
|
+
} else {
|
|
163
|
+
for (const childNode of viewDescRef.current.contentDOM.childNodes){
|
|
164
|
+
if (children.every((child)=>child.dom !== childNode)) {
|
|
165
|
+
compositionTopDOM = childNode;
|
|
166
|
+
break;
|
|
169
167
|
}
|
|
170
168
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
169
|
+
}
|
|
170
|
+
if (!compositionTopDOM) return;
|
|
171
|
+
let textDOM = compositionTopDOM;
|
|
172
|
+
while(textDOM.firstChild){
|
|
173
|
+
textDOM = textDOM.firstChild;
|
|
174
|
+
}
|
|
175
|
+
if (!textDOM || !(textDOM instanceof Text)) {
|
|
176
|
+
console.error(compositionTopDOM, textDOM);
|
|
177
|
+
throw new Error(`Started a composition but couldn't find the text node it belongs to.`);
|
|
178
|
+
}
|
|
179
|
+
compositionViewDesc.dom = compositionTopDOM;
|
|
180
|
+
compositionViewDesc.textDOM = textDOM;
|
|
181
|
+
compositionViewDesc.text = textDOM.data;
|
|
182
|
+
compositionViewDesc.textDOM.pmViewDesc = compositionViewDesc;
|
|
183
|
+
view.input.compositionNodes.push(compositionViewDesc);
|
|
186
184
|
});
|
|
187
185
|
const childContextValue = (0, _react.useMemo)(()=>({
|
|
188
186
|
parentRef: viewDescRef,
|
|
@@ -62,11 +62,12 @@ function beforeInputPlugin(setCursorWrapper) {
|
|
|
62
62
|
handleDOMEvents: {
|
|
63
63
|
compositionstart (view) {
|
|
64
64
|
if (!(view instanceof _ReactEditorView.ReactEditorView)) return false;
|
|
65
|
+
view.input.composing = true;
|
|
65
66
|
compositionMarks = view.state.storedMarks;
|
|
66
|
-
view.
|
|
67
|
+
const tr = view.state.tr.deleteSelection().setStoredMarks(null);
|
|
68
|
+
view.dispatch(tr);
|
|
67
69
|
handleGapCursorComposition(view);
|
|
68
70
|
const { state } = view;
|
|
69
|
-
// const $pos = state.selection.$from;
|
|
70
71
|
if (compositionMarks?.length) {
|
|
71
72
|
setCursorWrapper((0, _ReactWidgetType.widget)(state.selection.from, _CursorWrapper.CursorWrapper, {
|
|
72
73
|
key: "cursor-wrapper",
|
|
@@ -74,7 +75,6 @@ function beforeInputPlugin(setCursorWrapper) {
|
|
|
74
75
|
side: 1
|
|
75
76
|
}));
|
|
76
77
|
}
|
|
77
|
-
view.input.composing = true;
|
|
78
78
|
return true;
|
|
79
79
|
},
|
|
80
80
|
compositionupdate () {
|
|
@@ -86,7 +86,7 @@ function beforeInputPlugin(setCursorWrapper) {
|
|
|
86
86
|
view.input.composing = false;
|
|
87
87
|
compositionMarks = null;
|
|
88
88
|
setCursorWrapper(null);
|
|
89
|
-
if (view.input.compositionNode && !view.input.compositionNode.pmViewDesc
|
|
89
|
+
if (view.input.compositionNode && !view.input.compositionNode.pmViewDesc) {
|
|
90
90
|
view.input.compositionNode.remove();
|
|
91
91
|
}
|
|
92
92
|
view.input.compositionEndedAt = event.timeStamp;
|
|
@@ -157,19 +157,11 @@ function beforeInputPlugin(setCursorWrapper) {
|
|
|
157
157
|
} else {
|
|
158
158
|
tr.delete(start, end);
|
|
159
159
|
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
// macro task
|
|
166
|
-
if (view instanceof _ReactEditorView.ReactEditorView) {
|
|
167
|
-
view.deferPendingEffects = true;
|
|
168
|
-
}
|
|
169
|
-
view.dispatch(tr);
|
|
170
|
-
if (view instanceof _ReactEditorView.ReactEditorView) {
|
|
171
|
-
view.deferPendingEffects = false;
|
|
172
|
-
}
|
|
160
|
+
view.dom.addEventListener("input", ()=>{
|
|
161
|
+
view.dispatch(tr);
|
|
162
|
+
}, {
|
|
163
|
+
once: true
|
|
164
|
+
});
|
|
173
165
|
break;
|
|
174
166
|
}
|
|
175
167
|
case "deleteWordBackward":
|
|
@@ -345,14 +345,14 @@ export const ChildNodeViews = /*#__PURE__*/ memo(function ChildNodeViews(param)
|
|
|
345
345
|
component: SeparatorHackView,
|
|
346
346
|
marks: [],
|
|
347
347
|
offset: lastChild?.offset ?? 0,
|
|
348
|
-
index: (lastChild?.index ?? 0) +
|
|
348
|
+
index: (lastChild?.index ?? 0) + 1,
|
|
349
349
|
key: "trailing-hack-img"
|
|
350
350
|
}, {
|
|
351
351
|
type: "hack",
|
|
352
352
|
component: TrailingHackView,
|
|
353
353
|
marks: [],
|
|
354
354
|
offset: lastChild?.offset ?? 0,
|
|
355
|
-
index: (lastChild?.index ?? 0) +
|
|
355
|
+
index: (lastChild?.index ?? 0) + 2,
|
|
356
356
|
key: "trailing-hack-br"
|
|
357
357
|
});
|
|
358
358
|
}
|
|
@@ -19,9 +19,7 @@ export const CursorWrapper = /*#__PURE__*/ forwardRef(function CursorWrapper(par
|
|
|
19
19
|
domSel.collapse(node.parentNode, domIndex(node) + 1);
|
|
20
20
|
// @ts-expect-error Internal property - domObserver
|
|
21
21
|
view.domObserver.connectSelection();
|
|
22
|
-
|
|
23
|
-
setShouldRender(false);
|
|
24
|
-
});
|
|
22
|
+
setShouldRender(false);
|
|
25
23
|
}, []);
|
|
26
24
|
return shouldRender ? /*#__PURE__*/ React.createElement("img", {
|
|
27
25
|
ref: innerRef,
|
|
@@ -45,8 +45,7 @@ export class TextNodeView extends Component {
|
|
|
45
45
|
}
|
|
46
46
|
const pos = getPos();
|
|
47
47
|
const { from, to } = view.state.selection;
|
|
48
|
-
if (!(view.state.selection instanceof TextSelection) || from < pos ||
|
|
49
|
-
to > pos + node.nodeSize) {
|
|
48
|
+
if (!(view.state.selection instanceof TextSelection) || from < pos || to > pos + node.nodeSize) {
|
|
50
49
|
return false;
|
|
51
50
|
}
|
|
52
51
|
return this.containsCompositionNodeText;
|
|
@@ -113,22 +112,20 @@ export class TextNodeView extends Component {
|
|
|
113
112
|
this.destroy();
|
|
114
113
|
this.viewDescRef = this.create();
|
|
115
114
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
this.containsCompositionNodeText = node.text === text;
|
|
131
|
-
});
|
|
115
|
+
const { view, node } = this.props;
|
|
116
|
+
if (!(view instanceof ReactEditorView)) {
|
|
117
|
+
this.containsCompositionNodeText = true;
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const textNode = view.input.compositionNode;
|
|
121
|
+
if (!textNode) {
|
|
122
|
+
this.containsCompositionNodeText = true;
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
126
|
+
const text = textNode.nodeValue;
|
|
127
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
128
|
+
this.containsCompositionNodeText = node.text === text;
|
|
132
129
|
}
|
|
133
130
|
shouldComponentUpdate(nextProps) {
|
|
134
131
|
// When leaving the protected state, force a re-render so React's
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
-
import React, { useContext, useRef } from "react";
|
|
1
|
+
import React, { useContext, useRef, useState } from "react";
|
|
2
2
|
import { ChildDescriptionsContext } from "../contexts/ChildDescriptionsContext.js";
|
|
3
3
|
import { useClientLayoutEffect } from "../hooks/useClientLayoutEffect.js";
|
|
4
|
+
import { useEditorEffect } from "../hooks/useEditorEffect.js";
|
|
5
|
+
import { useEditorEventListener } from "../hooks/useEditorEventListener.js";
|
|
4
6
|
import { TrailingHackViewDesc, sortViewDescs } from "../viewdesc.js";
|
|
5
7
|
export function TrailingHackView(param) {
|
|
6
8
|
let { getPos } = param;
|
|
9
|
+
const [shouldRender, setShouldRender] = useState(true);
|
|
7
10
|
const { siblingsRef, parentRef } = useContext(ChildDescriptionsContext);
|
|
8
11
|
const viewDescRef = useRef(null);
|
|
9
12
|
const ref = useRef(null);
|
|
@@ -32,6 +35,32 @@ export function TrailingHackView(param) {
|
|
|
32
35
|
}
|
|
33
36
|
siblingsRef.current.sort(sortViewDescs);
|
|
34
37
|
});
|
|
38
|
+
// At the start of a composition, the browser will automatically delete
|
|
39
|
+
// the trailing hack br element. We need to unmount ourselves _before_
|
|
40
|
+
// that happens, so that React doesn't try to remove the already-removed
|
|
41
|
+
// br node when this component gets unmounted
|
|
42
|
+
useEditorEventListener("compositionstart", (view)=>{
|
|
43
|
+
const { from } = view.state.selection;
|
|
44
|
+
if (from === getPos()) {
|
|
45
|
+
setShouldRender(false);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
// We need to run the same composition check when we first get mounted,
|
|
49
|
+
// in case we got mounted in the same render batch as the beginning of
|
|
50
|
+
// a composition
|
|
51
|
+
useEditorEffect((view)=>{
|
|
52
|
+
if (!view.composing) return;
|
|
53
|
+
const { from } = view.state.selection;
|
|
54
|
+
if (from === getPos()) {
|
|
55
|
+
setShouldRender(false);
|
|
56
|
+
}
|
|
57
|
+
}, [
|
|
58
|
+
getPos
|
|
59
|
+
]);
|
|
60
|
+
useEditorEventListener("compositionend", ()=>{
|
|
61
|
+
setShouldRender(true);
|
|
62
|
+
});
|
|
63
|
+
if (!shouldRender) return null;
|
|
35
64
|
return /*#__PURE__*/ React.createElement("br", {
|
|
36
65
|
ref: ref,
|
|
37
66
|
className: "ProseMirror-trailingBreak"
|
|
@@ -100,20 +100,9 @@ let didWarnValueDefaultValue = false;
|
|
|
100
100
|
// running effects. Running effects will reattach selection
|
|
101
101
|
// change listeners if the EditorView has been destroyed.
|
|
102
102
|
if (view instanceof ReactEditorView && !view.isDestroyed) {
|
|
103
|
-
|
|
104
|
-
setTimeout(()=>{
|
|
105
|
-
// Plugins might dispatch transactions from their
|
|
106
|
-
// view update lifecycle hooks
|
|
107
|
-
flushSyncRef.current = false;
|
|
108
|
-
view.commitPendingEffects();
|
|
109
|
-
flushSyncRef.current = true;
|
|
110
|
-
});
|
|
111
|
-
} else {
|
|
112
|
-
flushSyncRef.current = false;
|
|
113
|
-
view.commitPendingEffects();
|
|
114
|
-
flushSyncRef.current = true;
|
|
115
|
-
}
|
|
103
|
+
flushSyncRef.current = false;
|
|
116
104
|
view.commitPendingEffects();
|
|
105
|
+
flushSyncRef.current = true;
|
|
117
106
|
}
|
|
118
107
|
});
|
|
119
108
|
view.update(directEditorProps);
|
|
@@ -134,45 +134,43 @@ export function useNodeViewDescription(getDOM, getContentDOM, constructor, props
|
|
|
134
134
|
for (const child of children){
|
|
135
135
|
child.parent = viewDesc;
|
|
136
136
|
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
break;
|
|
158
|
-
}
|
|
137
|
+
if (!props.node.isTextblock) return;
|
|
138
|
+
// Because TextNodeViews can't locate the DOM nodes
|
|
139
|
+
// for compositions, we need to override them here
|
|
140
|
+
if (!viewDescRef.current?.contentDOM) return;
|
|
141
|
+
const compositionChildIndex = children.findIndex((child)=>child instanceof CompositionViewDesc);
|
|
142
|
+
if (compositionChildIndex === -1) return;
|
|
143
|
+
const compositionViewDesc = children[compositionChildIndex];
|
|
144
|
+
if (!(compositionViewDesc instanceof CompositionViewDesc)) return;
|
|
145
|
+
let compositionTopDOM = null;
|
|
146
|
+
let search = children[compositionChildIndex - 1];
|
|
147
|
+
while(search instanceof MarkViewDesc){
|
|
148
|
+
search = search.children[0];
|
|
149
|
+
}
|
|
150
|
+
if (search instanceof WidgetViewDesc && search.widget.type instanceof ReactWidgetType && search.widget.type.Component === CursorWrapper) {
|
|
151
|
+
compositionTopDOM = search.dom.nextSibling;
|
|
152
|
+
} else {
|
|
153
|
+
for (const childNode of viewDescRef.current.contentDOM.childNodes){
|
|
154
|
+
if (children.every((child)=>child.dom !== childNode)) {
|
|
155
|
+
compositionTopDOM = childNode;
|
|
156
|
+
break;
|
|
159
157
|
}
|
|
160
158
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
159
|
+
}
|
|
160
|
+
if (!compositionTopDOM) return;
|
|
161
|
+
let textDOM = compositionTopDOM;
|
|
162
|
+
while(textDOM.firstChild){
|
|
163
|
+
textDOM = textDOM.firstChild;
|
|
164
|
+
}
|
|
165
|
+
if (!textDOM || !(textDOM instanceof Text)) {
|
|
166
|
+
console.error(compositionTopDOM, textDOM);
|
|
167
|
+
throw new Error(`Started a composition but couldn't find the text node it belongs to.`);
|
|
168
|
+
}
|
|
169
|
+
compositionViewDesc.dom = compositionTopDOM;
|
|
170
|
+
compositionViewDesc.textDOM = textDOM;
|
|
171
|
+
compositionViewDesc.text = textDOM.data;
|
|
172
|
+
compositionViewDesc.textDOM.pmViewDesc = compositionViewDesc;
|
|
173
|
+
view.input.compositionNodes.push(compositionViewDesc);
|
|
176
174
|
});
|
|
177
175
|
const childContextValue = useMemo(()=>({
|
|
178
176
|
parentRef: viewDescRef,
|
|
@@ -52,11 +52,12 @@ export function beforeInputPlugin(setCursorWrapper) {
|
|
|
52
52
|
handleDOMEvents: {
|
|
53
53
|
compositionstart (view) {
|
|
54
54
|
if (!(view instanceof ReactEditorView)) return false;
|
|
55
|
+
view.input.composing = true;
|
|
55
56
|
compositionMarks = view.state.storedMarks;
|
|
56
|
-
view.
|
|
57
|
+
const tr = view.state.tr.deleteSelection().setStoredMarks(null);
|
|
58
|
+
view.dispatch(tr);
|
|
57
59
|
handleGapCursorComposition(view);
|
|
58
60
|
const { state } = view;
|
|
59
|
-
// const $pos = state.selection.$from;
|
|
60
61
|
if (compositionMarks?.length) {
|
|
61
62
|
setCursorWrapper(widget(state.selection.from, CursorWrapper, {
|
|
62
63
|
key: "cursor-wrapper",
|
|
@@ -64,7 +65,6 @@ export function beforeInputPlugin(setCursorWrapper) {
|
|
|
64
65
|
side: 1
|
|
65
66
|
}));
|
|
66
67
|
}
|
|
67
|
-
view.input.composing = true;
|
|
68
68
|
return true;
|
|
69
69
|
},
|
|
70
70
|
compositionupdate () {
|
|
@@ -76,7 +76,7 @@ export function beforeInputPlugin(setCursorWrapper) {
|
|
|
76
76
|
view.input.composing = false;
|
|
77
77
|
compositionMarks = null;
|
|
78
78
|
setCursorWrapper(null);
|
|
79
|
-
if (view.input.compositionNode && !view.input.compositionNode.pmViewDesc
|
|
79
|
+
if (view.input.compositionNode && !view.input.compositionNode.pmViewDesc) {
|
|
80
80
|
view.input.compositionNode.remove();
|
|
81
81
|
}
|
|
82
82
|
view.input.compositionEndedAt = event.timeStamp;
|
|
@@ -147,19 +147,11 @@ export function beforeInputPlugin(setCursorWrapper) {
|
|
|
147
147
|
} else {
|
|
148
148
|
tr.delete(start, end);
|
|
149
149
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
// macro task
|
|
156
|
-
if (view instanceof ReactEditorView) {
|
|
157
|
-
view.deferPendingEffects = true;
|
|
158
|
-
}
|
|
159
|
-
view.dispatch(tr);
|
|
160
|
-
if (view instanceof ReactEditorView) {
|
|
161
|
-
view.deferPendingEffects = false;
|
|
162
|
-
}
|
|
150
|
+
view.dom.addEventListener("input", ()=>{
|
|
151
|
+
view.dispatch(tr);
|
|
152
|
+
}, {
|
|
153
|
+
once: true
|
|
154
|
+
});
|
|
163
155
|
break;
|
|
164
156
|
}
|
|
165
157
|
case "deleteWordBackward":
|