@handlewithcare/react-prosemirror 3.1.0-tiptap.51 → 3.1.0-tiptap.53
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/ReactEditorView.js +6 -2
- package/dist/cjs/components/ChildNodeViews.js +3 -2
- package/dist/cjs/components/CursorWrapper.js +3 -4
- package/dist/cjs/components/ProseMirror.js +5 -3
- package/dist/cjs/components/TextNodeView.js +176 -34
- package/dist/cjs/components/TrailingHackView.js +42 -1
- package/dist/cjs/components/WidgetView.js +4 -1
- package/dist/cjs/contexts/ChildDescriptionsContext.js +3 -1
- package/dist/cjs/decorations/viewDecorations.js +1 -6
- package/dist/cjs/hooks/useComponentEventListeners.js +6 -14
- package/dist/cjs/hooks/useEditor.js +2 -10
- package/dist/cjs/hooks/useMarkViewDescription.js +61 -3
- package/dist/cjs/hooks/useNodeViewDescription.js +45 -23
- package/dist/cjs/plugins/beforeInputPlugin.js +116 -12
- package/dist/cjs/plugins/componentEventListeners.js +2 -9
- package/dist/cjs/plugins/reactKeys.js +21 -14
- package/dist/cjs/tiptap/utils/ssrJSDOMPatch.js +59 -0
- package/dist/cjs/viewdesc.js +43 -2
- package/dist/esm/ReactEditorView.js +6 -2
- package/dist/esm/components/ChildNodeViews.js +4 -3
- package/dist/esm/components/CursorWrapper.js +4 -5
- package/dist/esm/components/ProseMirror.js +5 -3
- package/dist/esm/components/TextNodeView.js +125 -32
- package/dist/esm/components/TrailingHackView.js +42 -1
- package/dist/esm/components/WidgetView.js +4 -1
- package/dist/esm/contexts/ChildDescriptionsContext.js +3 -1
- package/dist/esm/decorations/viewDecorations.js +1 -6
- package/dist/esm/hooks/useComponentEventListeners.js +6 -14
- package/dist/esm/hooks/useEditor.js +2 -10
- package/dist/esm/hooks/useMarkViewDescription.js +62 -4
- package/dist/esm/hooks/useNodeViewDescription.js +46 -24
- package/dist/esm/plugins/beforeInputPlugin.js +116 -12
- package/dist/esm/plugins/componentEventListeners.js +2 -9
- package/dist/esm/plugins/reactKeys.js +21 -14
- package/dist/esm/tiptap/hooks/useTiptapEditor.js +7 -1
- package/dist/esm/tiptap/utils/ssrJSDOMPatch.js +56 -0
- package/dist/esm/viewdesc.js +42 -2
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/ReactEditorView.d.ts +3 -2
- package/dist/types/components/CursorWrapper.d.ts +2 -4
- package/dist/types/components/TextNodeView.d.ts +11 -6
- package/dist/types/components/WidgetViewComponentProps.d.ts +4 -3
- package/dist/types/components/__tests__/ProseMirror.composition.test.d.ts +17 -1
- package/dist/types/constants.d.ts +1 -1
- package/dist/types/contexts/ChildDescriptionsContext.d.ts +5 -3
- package/dist/types/decorations/viewDecorations.d.ts +2 -2
- package/dist/types/hooks/useComponentEventListeners.d.ts +1 -1
- package/dist/types/hooks/useEditor.d.ts +1 -2
- package/dist/types/hooks/useMarkViewDescription.d.ts +2 -1
- package/dist/types/hooks/useNodeViewDescription.d.ts +2 -1
- package/dist/types/plugins/beforeInputPlugin.d.ts +1 -2
- package/dist/types/plugins/componentEventListeners.d.ts +2 -3
- package/dist/types/plugins/reactKeys.d.ts +9 -8
- package/dist/types/props.d.ts +26 -26
- package/dist/types/tiptap/hooks/useTiptapEditor.d.ts +7 -0
- package/dist/types/tiptap/utils/ssrJSDOMPatch.d.ts +1 -0
- package/dist/types/viewdesc.d.ts +3 -2
- package/package.json +2 -1
|
@@ -33,7 +33,10 @@ let ReactEditorView = class ReactEditorView extends _prosemirrorview.EditorView
|
|
|
33
33
|
nextProps;
|
|
34
34
|
prevState;
|
|
35
35
|
_destroyed;
|
|
36
|
-
|
|
36
|
+
// TODO: Probably refactor? It's used in TrailingHackView to detect
|
|
37
|
+
// whether it was mounted during a compositionstart event handler
|
|
38
|
+
compositionStarting;
|
|
39
|
+
displacedNodes;
|
|
37
40
|
constructor(place, props){
|
|
38
41
|
// Prevent the base class from destroying the React-managed nodes.
|
|
39
42
|
// Restore them below after invoking the base class constructor.
|
|
@@ -86,7 +89,8 @@ let ReactEditorView = class ReactEditorView extends _prosemirrorview.EditorView
|
|
|
86
89
|
// @ts-expect-error this violates the typing but class does it, too.
|
|
87
90
|
this.docView = null;
|
|
88
91
|
this._destroyed = false;
|
|
89
|
-
this.
|
|
92
|
+
this.compositionStarting = false;
|
|
93
|
+
this.displacedNodes = [];
|
|
90
94
|
}
|
|
91
95
|
get props() {
|
|
92
96
|
return this.nextProps;
|
|
@@ -109,15 +109,16 @@ const ChildView = /*#__PURE__*/ (0, _react.memo)(function ChildView(param) {
|
|
|
109
109
|
}) : child.node.isText ? /*#__PURE__*/ _react.default.createElement(_ChildDescriptionsContext.ChildDescriptionsContext.Consumer, {
|
|
110
110
|
key: child.key
|
|
111
111
|
}, (param)=>{
|
|
112
|
-
let { siblingsRef, parentRef } = param;
|
|
112
|
+
let { siblingsRef, parentRef, findCompositionDOM } = param;
|
|
113
113
|
return /*#__PURE__*/ _react.default.createElement(_EditorContext.EditorContext.Consumer, null, (param)=>{
|
|
114
114
|
let { registerEventListener, unregisterEventListener } = param;
|
|
115
|
-
return /*#__PURE__*/ _react.default.createElement(_TextNodeView.
|
|
115
|
+
return /*#__PURE__*/ _react.default.createElement(_TextNodeView.RemountableTextNodeView, {
|
|
116
116
|
view: view,
|
|
117
117
|
node: child.node,
|
|
118
118
|
getPos: getPos,
|
|
119
119
|
siblingsRef: siblingsRef,
|
|
120
120
|
parentRef: parentRef,
|
|
121
|
+
findCompositionDOM: findCompositionDOM,
|
|
121
122
|
decorations: child.outerDeco,
|
|
122
123
|
registerEventListener: registerEventListener,
|
|
123
124
|
unregisterEventListener: unregisterEventListener
|
|
@@ -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,19 +64,19 @@ 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();
|
|
67
|
+
if (!domSel.isCollapsed) return;
|
|
68
68
|
const node = innerRef.current;
|
|
69
69
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
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
|
-
setShouldRender(false);
|
|
74
73
|
}, []);
|
|
75
|
-
return
|
|
74
|
+
return /*#__PURE__*/ _react.default.createElement("img", {
|
|
76
75
|
ref: innerRef,
|
|
77
76
|
className: "ProseMirror-separator",
|
|
78
77
|
// eslint-disable-next-line react/no-unknown-property
|
|
79
78
|
"mark-placeholder": "true",
|
|
80
79
|
alt: "",
|
|
81
80
|
...props
|
|
82
|
-
})
|
|
81
|
+
});
|
|
83
82
|
});
|
|
@@ -68,12 +68,14 @@ const rootChildDescriptionsContextValue = {
|
|
|
68
68
|
},
|
|
69
69
|
siblingsRef: {
|
|
70
70
|
current: []
|
|
71
|
-
}
|
|
71
|
+
},
|
|
72
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
73
|
+
findCompositionDOM: ()=>{}
|
|
72
74
|
};
|
|
73
75
|
function ProseMirrorInner(param) {
|
|
74
76
|
let { children, nodeViewComponents, markViewComponents, ...props } = param;
|
|
75
77
|
const [mount, setMount] = (0, _react.useState)(null);
|
|
76
|
-
const { editor,
|
|
78
|
+
const { editor, state } = (0, _useEditor.useEditor)(mount, props);
|
|
77
79
|
const nodeViewConstructors = editor.view.nodeViews;
|
|
78
80
|
const nodeViewContextValue = (0, _react.useMemo)(()=>{
|
|
79
81
|
return {
|
|
@@ -90,7 +92,7 @@ function ProseMirrorInner(param) {
|
|
|
90
92
|
]);
|
|
91
93
|
const node = state.doc;
|
|
92
94
|
const decorations = (0, _computeDocDeco.computeDocDeco)(editor.view);
|
|
93
|
-
const innerDecorations = (0, _viewDecorations.viewDecorations)(editor.view
|
|
95
|
+
const innerDecorations = (0, _viewDecorations.viewDecorations)(editor.view);
|
|
94
96
|
const docNodeViewContextValue = (0, _react.useMemo)(()=>({
|
|
95
97
|
setMount,
|
|
96
98
|
node,
|
|
@@ -2,19 +2,68 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", {
|
|
3
3
|
value: true
|
|
4
4
|
});
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
function _export(target, all) {
|
|
6
|
+
for(var name in all)Object.defineProperty(target, name, {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: all[name]
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
_export(exports, {
|
|
12
|
+
RemountableTextNodeView: function() {
|
|
13
|
+
return RemountableTextNodeView;
|
|
14
|
+
},
|
|
15
|
+
TextNodeView: function() {
|
|
8
16
|
return TextNodeView;
|
|
9
17
|
}
|
|
10
18
|
});
|
|
11
19
|
const _prosemirrorstate = require("prosemirror-state");
|
|
12
20
|
const _prosemirrorview = require("prosemirror-view");
|
|
13
|
-
const _react = require("react");
|
|
21
|
+
const _react = /*#__PURE__*/ _interop_require_wildcard(require("react"));
|
|
14
22
|
const _ReactEditorView = require("../ReactEditorView.js");
|
|
15
23
|
const _findDOMNode = require("../findDOMNode.js");
|
|
16
24
|
const _viewdesc = require("../viewdesc.js");
|
|
17
25
|
const _ChildNodeViews = require("./ChildNodeViews.js");
|
|
26
|
+
function _getRequireWildcardCache(nodeInterop) {
|
|
27
|
+
if (typeof WeakMap !== "function") return null;
|
|
28
|
+
var cacheBabelInterop = new WeakMap();
|
|
29
|
+
var cacheNodeInterop = new WeakMap();
|
|
30
|
+
return (_getRequireWildcardCache = function(nodeInterop) {
|
|
31
|
+
return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
|
|
32
|
+
})(nodeInterop);
|
|
33
|
+
}
|
|
34
|
+
function _interop_require_wildcard(obj, nodeInterop) {
|
|
35
|
+
if (!nodeInterop && obj && obj.__esModule) {
|
|
36
|
+
return obj;
|
|
37
|
+
}
|
|
38
|
+
if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
|
|
39
|
+
return {
|
|
40
|
+
default: obj
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
var cache = _getRequireWildcardCache(nodeInterop);
|
|
44
|
+
if (cache && cache.has(obj)) {
|
|
45
|
+
return cache.get(obj);
|
|
46
|
+
}
|
|
47
|
+
var newObj = {
|
|
48
|
+
__proto__: null
|
|
49
|
+
};
|
|
50
|
+
var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
|
|
51
|
+
for(var key in obj){
|
|
52
|
+
if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
53
|
+
var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
|
|
54
|
+
if (desc && (desc.get || desc.set)) {
|
|
55
|
+
Object.defineProperty(newObj, key, desc);
|
|
56
|
+
} else {
|
|
57
|
+
newObj[key] = obj[key];
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
newObj.default = obj;
|
|
62
|
+
if (cache) {
|
|
63
|
+
cache.set(obj, newObj);
|
|
64
|
+
}
|
|
65
|
+
return newObj;
|
|
66
|
+
}
|
|
18
67
|
function shallowEqual(objA, objB) {
|
|
19
68
|
if (objA === objB) {
|
|
20
69
|
return true;
|
|
@@ -38,10 +87,10 @@ function shallowEqual(objA, objB) {
|
|
|
38
87
|
return true;
|
|
39
88
|
}
|
|
40
89
|
let TextNodeView = class TextNodeView extends _react.Component {
|
|
41
|
-
viewDescRef =
|
|
42
|
-
renderRef =
|
|
43
|
-
wasProtecting =
|
|
44
|
-
containsCompositionNodeText =
|
|
90
|
+
viewDescRef = createMutRef();
|
|
91
|
+
renderRef = createMutRef();
|
|
92
|
+
wasProtecting = createMutRef();
|
|
93
|
+
containsCompositionNodeText = createMutRef();
|
|
45
94
|
// This is basically NodeViewDesc.localCompositionInfo
|
|
46
95
|
// from prosemirror-view. It's been slightly adjusted so that
|
|
47
96
|
// it can be used accurately during render, before we've
|
|
@@ -53,17 +102,42 @@ let TextNodeView = class TextNodeView extends _react.Component {
|
|
|
53
102
|
if (!view.composing) {
|
|
54
103
|
return false;
|
|
55
104
|
}
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
105
|
+
const viewDesc = this.viewDescRef.current;
|
|
106
|
+
// If our DOM text node IS the IME's composition node, protect regardless
|
|
107
|
+
// of where the PM selection currently is. The IME may have replaced a
|
|
108
|
+
// selection that included us — moving the PM selection past us — but our
|
|
109
|
+
// DOM is still part of the in-progress composition. Until another
|
|
110
|
+
// TextNodeView's findCompositionDOM displaces us into a comp desc, only
|
|
111
|
+
// our own protect/no-update is preventing React from rewriting the IME's
|
|
112
|
+
// text. (When we *are* displaced, viewDesc is already a CompositionViewDesc
|
|
113
|
+
// and the existing position-based logic doesn't apply anyway.)
|
|
114
|
+
const ownsCompositionNode = viewDesc instanceof _viewdesc.TextViewDesc && viewDesc.nodeDOM === view.input.compositionNode;
|
|
115
|
+
if (!ownsCompositionNode) {
|
|
116
|
+
const pos = getPos();
|
|
117
|
+
const { from, to } = view.state.selection;
|
|
118
|
+
if (!(view.state.selection instanceof _prosemirrorstate.TextSelection) || from <= pos || to > pos + node.nodeSize) {
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
60
121
|
}
|
|
61
|
-
return this.containsCompositionNodeText;
|
|
122
|
+
return !!this.containsCompositionNodeText.current;
|
|
62
123
|
}
|
|
63
124
|
handleCompositionEnd = ()=>{
|
|
64
|
-
if (!this.wasProtecting) return;
|
|
65
|
-
this.
|
|
66
|
-
return;
|
|
125
|
+
if (!this.wasProtecting.current) return;
|
|
126
|
+
const { view } = this.props;
|
|
127
|
+
if (!(view instanceof _ReactEditorView.ReactEditorView)) return;
|
|
128
|
+
// If the IME detached our DOM during composition, React's fiber is now
|
|
129
|
+
// wired to a detached node and will silently send all subsequent updates
|
|
130
|
+
// into the void. Re-attach the orphan (so the upcoming unmount's
|
|
131
|
+
// removeChild has something to remove), then ask our wrapper to mint a
|
|
132
|
+
// new key — that forces React to drop this fiber and mount a fresh one
|
|
133
|
+
// whose stateNode it creates from the current render output.
|
|
134
|
+
const dom = (0, _findDOMNode.findDOMNode)(this);
|
|
135
|
+
if (dom instanceof HTMLElement && !view.dom.contains(dom)) {
|
|
136
|
+
this.reattachAtCorrectPosition(dom);
|
|
137
|
+
this.props.forceRemount();
|
|
138
|
+
} else {
|
|
139
|
+
this.forceUpdate();
|
|
140
|
+
}
|
|
67
141
|
};
|
|
68
142
|
create() {
|
|
69
143
|
const { view, decorations, siblingsRef, parentRef, getPos, node } = this.props;
|
|
@@ -91,14 +165,25 @@ let TextNodeView = class TextNodeView extends _react.Component {
|
|
|
91
165
|
}
|
|
92
166
|
siblingsRef.current.push(viewDesc);
|
|
93
167
|
siblingsRef.current.sort(_viewdesc.sortViewDescs);
|
|
168
|
+
if (viewDesc instanceof _viewdesc.CompositionViewDesc) {
|
|
169
|
+
this.props.findCompositionDOM(viewDesc);
|
|
170
|
+
}
|
|
94
171
|
return viewDesc;
|
|
95
172
|
}
|
|
96
173
|
update() {
|
|
97
174
|
const { view, node, decorations } = this.props;
|
|
98
175
|
if (!(view instanceof _ReactEditorView.ReactEditorView)) return false;
|
|
99
|
-
const viewDesc = this.viewDescRef;
|
|
176
|
+
const viewDesc = this.viewDescRef.current;
|
|
100
177
|
if (!viewDesc) return false;
|
|
101
|
-
|
|
178
|
+
// Don't force destroy/recreate just because we transitioned into protect
|
|
179
|
+
// mode. If our DOM text node is the IME's composition node, we want to
|
|
180
|
+
// keep the TextViewDesc alive so the new composition-text TextNodeView's
|
|
181
|
+
// findCompositionDOM second pass can find us, validate the size mismatch,
|
|
182
|
+
// and displace us into a properly-sized CompositionViewDesc. If we
|
|
183
|
+
// destroyed here, create() would put a wrong-size CompositionViewDesc on
|
|
184
|
+
// T and pre-empt that displacement.
|
|
185
|
+
const ownsCompositionNode = viewDesc instanceof _viewdesc.TextViewDesc && viewDesc.nodeDOM === view.input.compositionNode;
|
|
186
|
+
if (!ownsCompositionNode && this.shouldProtect(this.props) !== viewDesc instanceof _viewdesc.CompositionViewDesc) {
|
|
102
187
|
return false;
|
|
103
188
|
}
|
|
104
189
|
if (viewDesc instanceof _viewdesc.CompositionViewDesc) return false;
|
|
@@ -108,7 +193,7 @@ let TextNodeView = class TextNodeView extends _react.Component {
|
|
|
108
193
|
return viewDesc.matchesNode(node, decorations, _prosemirrorview.DecorationSet.empty) || viewDesc.update(node, decorations, _prosemirrorview.DecorationSet.empty, view);
|
|
109
194
|
}
|
|
110
195
|
destroy() {
|
|
111
|
-
const viewDesc = this.viewDescRef;
|
|
196
|
+
const viewDesc = this.viewDescRef.current;
|
|
112
197
|
if (!viewDesc) return;
|
|
113
198
|
viewDesc.destroy();
|
|
114
199
|
const siblings = this.props.siblingsRef.current;
|
|
@@ -120,43 +205,85 @@ let TextNodeView = class TextNodeView extends _react.Component {
|
|
|
120
205
|
updateEffect() {
|
|
121
206
|
if (!this.update()) {
|
|
122
207
|
this.destroy();
|
|
123
|
-
this.viewDescRef = this.create();
|
|
208
|
+
this.viewDescRef.current = this.create();
|
|
124
209
|
}
|
|
125
|
-
const { view
|
|
210
|
+
const { view } = this.props;
|
|
126
211
|
if (!(view instanceof _ReactEditorView.ReactEditorView)) {
|
|
127
|
-
this.containsCompositionNodeText = true;
|
|
212
|
+
this.containsCompositionNodeText.current = true;
|
|
128
213
|
return;
|
|
129
214
|
}
|
|
130
215
|
const textNode = view.input.compositionNode;
|
|
131
216
|
if (!textNode) {
|
|
132
|
-
this.containsCompositionNodeText = true;
|
|
217
|
+
this.containsCompositionNodeText.current = true;
|
|
133
218
|
return;
|
|
134
219
|
}
|
|
135
|
-
//
|
|
136
|
-
|
|
137
|
-
//
|
|
138
|
-
|
|
220
|
+
// Resolve the parent textblock containing this text node and ask
|
|
221
|
+
// findTextInFragment whether the IME text node's *current* content can be
|
|
222
|
+
// placed somewhere in the textblock's PM content overlapping the
|
|
223
|
+
// selection. If it can, the composition is still consistent with PM state
|
|
224
|
+
// and we should protect. If it can't (e.g. a remote change overwrote the
|
|
225
|
+
// composing region), PM and the DOM have diverged — abandon protection
|
|
226
|
+
// so the re-render can rewrite the DOM and cancel the composition.
|
|
227
|
+
const $pos = view.state.doc.resolve(this.props.getPos());
|
|
228
|
+
const parent = $pos.parent;
|
|
229
|
+
if (!parent.inlineContent) {
|
|
230
|
+
this.containsCompositionNodeText.current = false;
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
const parentStart = $pos.start();
|
|
234
|
+
const { from, to } = view.state.selection;
|
|
235
|
+
const textPos = (0, _viewdesc.findTextInFragment)(parent.content, // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
236
|
+
textNode.nodeValue, from - parentStart, to - parentStart);
|
|
237
|
+
this.containsCompositionNodeText.current = textPos >= 0;
|
|
139
238
|
}
|
|
140
239
|
shouldComponentUpdate(nextProps) {
|
|
141
240
|
// When leaving the protected state, force a re-render so React's
|
|
142
241
|
// virtual DOM resyncs with whatever the IME wrote into the real DOM
|
|
143
242
|
// while we were returning a stale renderRef.
|
|
144
|
-
if (this.wasProtecting && !this.shouldProtect(nextProps)) {
|
|
243
|
+
if (this.wasProtecting.current && !this.shouldProtect(nextProps)) {
|
|
145
244
|
return true;
|
|
146
245
|
}
|
|
147
246
|
return !shallowEqual(this.props, nextProps);
|
|
148
247
|
}
|
|
248
|
+
constructor(props){
|
|
249
|
+
super(props);
|
|
250
|
+
this.viewDescRef.current = null;
|
|
251
|
+
this.renderRef.current = null;
|
|
252
|
+
this.wasProtecting.current = false;
|
|
253
|
+
this.containsCompositionNodeText.current = true;
|
|
254
|
+
}
|
|
149
255
|
componentDidMount() {
|
|
150
|
-
this.
|
|
256
|
+
this.containsCompositionNodeText.current = true;
|
|
151
257
|
// After a composition, force an update so that we re-check whether we need
|
|
152
258
|
// to be protecting our rendered content and allow React to re-sync with the
|
|
153
259
|
// DOM.
|
|
154
260
|
const { registerEventListener } = this.props;
|
|
155
261
|
registerEventListener("compositionend", this.handleCompositionEnd);
|
|
262
|
+
this.viewDescRef.current = this.create();
|
|
156
263
|
this.updateEffect();
|
|
157
264
|
}
|
|
158
265
|
componentDidUpdate() {
|
|
159
266
|
this.updateEffect();
|
|
267
|
+
const { view } = this.props;
|
|
268
|
+
if (!(view instanceof _ReactEditorView.ReactEditorView)) return;
|
|
269
|
+
}
|
|
270
|
+
reattachAtCorrectPosition(dom) {
|
|
271
|
+
const viewDesc = this.viewDescRef.current;
|
|
272
|
+
if (!viewDesc) return;
|
|
273
|
+
let host = viewDesc.parent;
|
|
274
|
+
while(host && !host.contentDOM)host = host.parent;
|
|
275
|
+
if (!host?.contentDOM) return;
|
|
276
|
+
const siblings = viewDesc.parent?.children ?? [];
|
|
277
|
+
const idx = siblings.indexOf(viewDesc);
|
|
278
|
+
let nextDom = null;
|
|
279
|
+
for(let i = idx + 1; i < siblings.length; i++){
|
|
280
|
+
const sib = siblings[i];
|
|
281
|
+
if (sib?.dom && sib.dom.parentNode === host.contentDOM) {
|
|
282
|
+
nextDom = sib.dom;
|
|
283
|
+
break;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
host.contentDOM.insertBefore(dom, nextDom);
|
|
160
287
|
}
|
|
161
288
|
componentWillUnmount() {
|
|
162
289
|
const { unregisterEventListener } = this.props;
|
|
@@ -171,11 +298,26 @@ let TextNodeView = class TextNodeView extends _react.Component {
|
|
|
171
298
|
// we freeze the DOM of this element so that it doesn't
|
|
172
299
|
// interrupt the composition
|
|
173
300
|
if (this.shouldProtect(this.props)) {
|
|
174
|
-
this.wasProtecting = true;
|
|
175
|
-
return this.renderRef;
|
|
301
|
+
this.wasProtecting.current = true;
|
|
302
|
+
return this.renderRef.current;
|
|
176
303
|
}
|
|
177
|
-
this.wasProtecting = false;
|
|
178
|
-
this.renderRef = decorations.reduce(_ChildNodeViews.wrapInDeco, node.text);
|
|
179
|
-
return this.renderRef;
|
|
304
|
+
this.wasProtecting.current = false;
|
|
305
|
+
this.renderRef.current = decorations.reduce(_ChildNodeViews.wrapInDeco, node.text);
|
|
306
|
+
return this.renderRef.current;
|
|
180
307
|
}
|
|
181
308
|
};
|
|
309
|
+
/**
|
|
310
|
+
* createRef returns a RefObject, even though the docs
|
|
311
|
+
* say that it's acceptible to manage the ref's value
|
|
312
|
+
* yourself.
|
|
313
|
+
*/ function createMutRef() {
|
|
314
|
+
return /*#__PURE__*/ (0, _react.createRef)();
|
|
315
|
+
}
|
|
316
|
+
function RemountableTextNodeView(props) {
|
|
317
|
+
const [key, forceRemount] = (0, _react.useReducer)((x)=>x + 1, 0);
|
|
318
|
+
return /*#__PURE__*/ _react.default.createElement(TextNodeView, {
|
|
319
|
+
key: key,
|
|
320
|
+
forceRemount: forceRemount,
|
|
321
|
+
...props
|
|
322
|
+
});
|
|
323
|
+
}
|
|
@@ -9,6 +9,7 @@ Object.defineProperty(exports, "TrailingHackView", {
|
|
|
9
9
|
}
|
|
10
10
|
});
|
|
11
11
|
const _react = /*#__PURE__*/ _interop_require_wildcard(require("react"));
|
|
12
|
+
const _ReactEditorView = require("../ReactEditorView.js");
|
|
12
13
|
const _ChildDescriptionsContext = require("../contexts/ChildDescriptionsContext.js");
|
|
13
14
|
const _useClientLayoutEffect = require("../hooks/useClientLayoutEffect.js");
|
|
14
15
|
const _useEditorEffect = require("../hooks/useEditorEffect.js");
|
|
@@ -58,9 +59,14 @@ function _interop_require_wildcard(obj, nodeInterop) {
|
|
|
58
59
|
function TrailingHackView(param) {
|
|
59
60
|
let { getPos } = param;
|
|
60
61
|
const [shouldRender, setShouldRender] = (0, _react.useState)(true);
|
|
62
|
+
const [shouldReinsert, setShouldReinsert] = (0, _react.useState)(false);
|
|
61
63
|
const { siblingsRef, parentRef } = (0, _react.useContext)(_ChildDescriptionsContext.ChildDescriptionsContext);
|
|
62
64
|
const viewDescRef = (0, _react.useRef)(null);
|
|
63
65
|
const ref = (0, _react.useRef)(null);
|
|
66
|
+
const preservedRef = (0, _react.useRef)(ref.current);
|
|
67
|
+
if (ref.current) {
|
|
68
|
+
preservedRef.current = ref.current;
|
|
69
|
+
}
|
|
64
70
|
(0, _useClientLayoutEffect.useClientLayoutEffect)(()=>{
|
|
65
71
|
const siblings = siblingsRef.current;
|
|
66
72
|
return ()=>{
|
|
@@ -94,13 +100,47 @@ function TrailingHackView(param) {
|
|
|
94
100
|
const { from } = view.state.selection;
|
|
95
101
|
if (from === getPos()) {
|
|
96
102
|
setShouldRender(false);
|
|
103
|
+
setShouldReinsert(true);
|
|
97
104
|
}
|
|
98
105
|
});
|
|
106
|
+
// Chrome and Safari will cancel/mangle the composition if the br element isn't
|
|
107
|
+
// still in the DOM after the compositionstart event. We manually add it
|
|
108
|
+
// back to the DOM, without React managing it, so that it can be removed
|
|
109
|
+
// again by the browser when it starts the composition.
|
|
110
|
+
(0, _useClientLayoutEffect.useClientLayoutEffect)(()=>{
|
|
111
|
+
if (!shouldReinsert) return;
|
|
112
|
+
const preservedHack = preservedRef.current;
|
|
113
|
+
if (!preservedHack) return;
|
|
114
|
+
if (!viewDescRef.current) return;
|
|
115
|
+
const { parent } = viewDescRef.current;
|
|
116
|
+
if (!parent) return;
|
|
117
|
+
const dom = parent.contentDOM;
|
|
118
|
+
if (!dom) return;
|
|
119
|
+
preservedHack.pmViewDesc = undefined;
|
|
120
|
+
const index = parent.children.indexOf(viewDescRef.current);
|
|
121
|
+
if (index === 0) {
|
|
122
|
+
dom.appendChild(preservedHack);
|
|
123
|
+
} else {
|
|
124
|
+
dom.insertBefore(preservedHack, dom.childNodes.item(index));
|
|
125
|
+
}
|
|
126
|
+
return ()=>{
|
|
127
|
+
try {
|
|
128
|
+
dom.removeChild(preservedHack);
|
|
129
|
+
} catch {
|
|
130
|
+
// It may have already been removed by the browser during
|
|
131
|
+
// the composition, but if we get unmounted before that happens,
|
|
132
|
+
// we need to remove it ourselves
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
}, [
|
|
136
|
+
shouldReinsert
|
|
137
|
+
]);
|
|
99
138
|
// We need to run the same composition check when we first get mounted,
|
|
100
139
|
// in case we got mounted in the same render batch as the beginning of
|
|
101
140
|
// a composition
|
|
102
141
|
(0, _useEditorEffect.useEditorEffect)((view)=>{
|
|
103
|
-
if (!view.
|
|
142
|
+
if (!(view instanceof _ReactEditorView.ReactEditorView)) return;
|
|
143
|
+
if (!view.compositionStarting) return;
|
|
104
144
|
const { from } = view.state.selection;
|
|
105
145
|
if (from === getPos()) {
|
|
106
146
|
setShouldRender(false);
|
|
@@ -110,6 +150,7 @@ function TrailingHackView(param) {
|
|
|
110
150
|
]);
|
|
111
151
|
(0, _useEditorEventListener.useEditorEventListener)("compositionend", ()=>{
|
|
112
152
|
setShouldRender(true);
|
|
153
|
+
setShouldReinsert(false);
|
|
113
154
|
});
|
|
114
155
|
if (!shouldRender) return null;
|
|
115
156
|
return /*#__PURE__*/ _react.default.createElement("br", {
|
|
@@ -78,6 +78,7 @@ function WidgetView(param) {
|
|
|
78
78
|
viewDescRef.current.parent = parentRef.current;
|
|
79
79
|
viewDescRef.current.widget = widget;
|
|
80
80
|
viewDescRef.current.dom = domRef.current;
|
|
81
|
+
viewDescRef.current.dom.pmViewDesc = viewDescRef.current;
|
|
81
82
|
}
|
|
82
83
|
if (!siblingsRef.current.includes(viewDescRef.current)) {
|
|
83
84
|
siblingsRef.current.push(viewDescRef.current);
|
|
@@ -89,6 +90,8 @@ function WidgetView(param) {
|
|
|
89
90
|
ref: domRef,
|
|
90
91
|
widget: widget,
|
|
91
92
|
getPos: getPos,
|
|
92
|
-
|
|
93
|
+
...!widget.type.spec.raw && {
|
|
94
|
+
contentEditable: false
|
|
95
|
+
}
|
|
93
96
|
});
|
|
94
97
|
}
|
|
@@ -123,17 +123,12 @@ function insertAhead(array, i, deco) {
|
|
|
123
123
|
array.splice(i, 0, deco);
|
|
124
124
|
}
|
|
125
125
|
const ViewDecorationsCache = new WeakMap();
|
|
126
|
-
function viewDecorations(view
|
|
126
|
+
function viewDecorations(view) {
|
|
127
127
|
const found = [];
|
|
128
128
|
view.someProp("decorations", (f)=>{
|
|
129
129
|
const result = f(view.state);
|
|
130
130
|
if (result && result != empty) found.push(result);
|
|
131
131
|
});
|
|
132
|
-
if (cursorWrapper) {
|
|
133
|
-
found.push(_prosemirrorview.DecorationSet.create(view.state.doc, [
|
|
134
|
-
cursorWrapper
|
|
135
|
-
]));
|
|
136
|
-
}
|
|
137
132
|
const previous = ViewDecorationsCache.get(view);
|
|
138
133
|
if (!previous) {
|
|
139
134
|
const result = DecorationGroup.from(found);
|
|
@@ -10,16 +10,8 @@ Object.defineProperty(exports, "useComponentEventListeners", {
|
|
|
10
10
|
});
|
|
11
11
|
const _react = require("react");
|
|
12
12
|
const _reactdom = require("react-dom");
|
|
13
|
-
function useComponentEventListeners(
|
|
14
|
-
const [registry, setRegistry] = (0, _react.useState)(new Map(
|
|
15
|
-
let [eventName, handler] = param;
|
|
16
|
-
return [
|
|
17
|
-
eventName,
|
|
18
|
-
handler ? [
|
|
19
|
-
handler
|
|
20
|
-
] : []
|
|
21
|
-
];
|
|
22
|
-
})));
|
|
13
|
+
function useComponentEventListeners(handleDOMEventsProp) {
|
|
14
|
+
const [registry, setRegistry] = (0, _react.useState)(new Map());
|
|
23
15
|
const registerEventListener = (0, _react.useCallback)((eventType, handler)=>{
|
|
24
16
|
const handlers = registry.get(eventType) ?? [];
|
|
25
17
|
handlers.unshift(handler);
|
|
@@ -37,19 +29,19 @@ function useComponentEventListeners(existingHandlers) {
|
|
|
37
29
|
registry
|
|
38
30
|
]);
|
|
39
31
|
(0, _react.useLayoutEffect)(()=>{
|
|
40
|
-
if (!
|
|
41
|
-
for (const [eventType, handler] of Object.entries(
|
|
32
|
+
if (!handleDOMEventsProp) return;
|
|
33
|
+
for (const [eventType, handler] of Object.entries(handleDOMEventsProp)){
|
|
42
34
|
if (!handler) return;
|
|
43
35
|
registerEventListener(eventType, handler);
|
|
44
36
|
}
|
|
45
37
|
return ()=>{
|
|
46
|
-
for (const [eventType, handler] of Object.entries(
|
|
38
|
+
for (const [eventType, handler] of Object.entries(handleDOMEventsProp)){
|
|
47
39
|
if (!handler) return;
|
|
48
40
|
unregisterEventListener(eventType, handler);
|
|
49
41
|
}
|
|
50
42
|
};
|
|
51
43
|
}, [
|
|
52
|
-
|
|
44
|
+
handleDOMEventsProp,
|
|
53
45
|
registerEventListener,
|
|
54
46
|
unregisterEventListener
|
|
55
47
|
]);
|
|
@@ -27,23 +27,16 @@ function useEditor(mount, options) {
|
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
const flushSyncRef = (0, _react.useRef)(true);
|
|
30
|
-
const [cursorWrapper, _setCursorWrapper] = (0, _react.useState)(null);
|
|
31
30
|
const forceUpdate = (0, _useForceUpdate.useForceUpdate)();
|
|
32
31
|
const defaultState = options.defaultState ?? _constants.EMPTY_STATE;
|
|
33
32
|
const [_state, setState] = (0, _react.useState)(defaultState);
|
|
34
33
|
const state = options.state ?? _state;
|
|
35
34
|
const { handleDOMEvents, registerEventListener, unregisterEventListener } = (0, _useComponentEventListeners.useComponentEventListeners)(options.handleDOMEvents);
|
|
36
|
-
const setCursorWrapper = (0, _react.useCallback)((deco)=>{
|
|
37
|
-
(0, _reactdom.flushSync)(()=>{
|
|
38
|
-
_setCursorWrapper(deco);
|
|
39
|
-
});
|
|
40
|
-
}, []);
|
|
41
35
|
const plugins = (0, _react.useMemo)(()=>[
|
|
42
36
|
...options.plugins ?? [],
|
|
43
|
-
(0, _beforeInputPlugin.beforeInputPlugin)(
|
|
37
|
+
(0, _beforeInputPlugin.beforeInputPlugin)()
|
|
44
38
|
], [
|
|
45
|
-
options.plugins
|
|
46
|
-
setCursorWrapper
|
|
39
|
+
options.plugins
|
|
47
40
|
]);
|
|
48
41
|
const dispatchTransaction = (0, _react.useCallback)(function dispatchTransaction(tr) {
|
|
49
42
|
if (flushSyncRef.current) {
|
|
@@ -122,7 +115,6 @@ function useEditor(mount, options) {
|
|
|
122
115
|
]);
|
|
123
116
|
return {
|
|
124
117
|
editor,
|
|
125
|
-
cursorWrapper,
|
|
126
118
|
state
|
|
127
119
|
};
|
|
128
120
|
}
|