@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.
Files changed (58) hide show
  1. package/dist/cjs/ReactEditorView.js +6 -2
  2. package/dist/cjs/components/ChildNodeViews.js +3 -2
  3. package/dist/cjs/components/CursorWrapper.js +3 -4
  4. package/dist/cjs/components/ProseMirror.js +5 -3
  5. package/dist/cjs/components/TextNodeView.js +176 -34
  6. package/dist/cjs/components/TrailingHackView.js +42 -1
  7. package/dist/cjs/components/WidgetView.js +4 -1
  8. package/dist/cjs/contexts/ChildDescriptionsContext.js +3 -1
  9. package/dist/cjs/decorations/viewDecorations.js +1 -6
  10. package/dist/cjs/hooks/useComponentEventListeners.js +6 -14
  11. package/dist/cjs/hooks/useEditor.js +2 -10
  12. package/dist/cjs/hooks/useMarkViewDescription.js +61 -3
  13. package/dist/cjs/hooks/useNodeViewDescription.js +45 -23
  14. package/dist/cjs/plugins/beforeInputPlugin.js +116 -12
  15. package/dist/cjs/plugins/componentEventListeners.js +2 -9
  16. package/dist/cjs/plugins/reactKeys.js +21 -14
  17. package/dist/cjs/tiptap/utils/ssrJSDOMPatch.js +59 -0
  18. package/dist/cjs/viewdesc.js +43 -2
  19. package/dist/esm/ReactEditorView.js +6 -2
  20. package/dist/esm/components/ChildNodeViews.js +4 -3
  21. package/dist/esm/components/CursorWrapper.js +4 -5
  22. package/dist/esm/components/ProseMirror.js +5 -3
  23. package/dist/esm/components/TextNodeView.js +125 -32
  24. package/dist/esm/components/TrailingHackView.js +42 -1
  25. package/dist/esm/components/WidgetView.js +4 -1
  26. package/dist/esm/contexts/ChildDescriptionsContext.js +3 -1
  27. package/dist/esm/decorations/viewDecorations.js +1 -6
  28. package/dist/esm/hooks/useComponentEventListeners.js +6 -14
  29. package/dist/esm/hooks/useEditor.js +2 -10
  30. package/dist/esm/hooks/useMarkViewDescription.js +62 -4
  31. package/dist/esm/hooks/useNodeViewDescription.js +46 -24
  32. package/dist/esm/plugins/beforeInputPlugin.js +116 -12
  33. package/dist/esm/plugins/componentEventListeners.js +2 -9
  34. package/dist/esm/plugins/reactKeys.js +21 -14
  35. package/dist/esm/tiptap/hooks/useTiptapEditor.js +7 -1
  36. package/dist/esm/tiptap/utils/ssrJSDOMPatch.js +56 -0
  37. package/dist/esm/viewdesc.js +42 -2
  38. package/dist/tsconfig.tsbuildinfo +1 -1
  39. package/dist/types/ReactEditorView.d.ts +3 -2
  40. package/dist/types/components/CursorWrapper.d.ts +2 -4
  41. package/dist/types/components/TextNodeView.d.ts +11 -6
  42. package/dist/types/components/WidgetViewComponentProps.d.ts +4 -3
  43. package/dist/types/components/__tests__/ProseMirror.composition.test.d.ts +17 -1
  44. package/dist/types/constants.d.ts +1 -1
  45. package/dist/types/contexts/ChildDescriptionsContext.d.ts +5 -3
  46. package/dist/types/decorations/viewDecorations.d.ts +2 -2
  47. package/dist/types/hooks/useComponentEventListeners.d.ts +1 -1
  48. package/dist/types/hooks/useEditor.d.ts +1 -2
  49. package/dist/types/hooks/useMarkViewDescription.d.ts +2 -1
  50. package/dist/types/hooks/useNodeViewDescription.d.ts +2 -1
  51. package/dist/types/plugins/beforeInputPlugin.d.ts +1 -2
  52. package/dist/types/plugins/componentEventListeners.d.ts +2 -3
  53. package/dist/types/plugins/reactKeys.d.ts +9 -8
  54. package/dist/types/props.d.ts +26 -26
  55. package/dist/types/tiptap/hooks/useTiptapEditor.d.ts +7 -0
  56. package/dist/types/tiptap/utils/ssrJSDOMPatch.d.ts +1 -0
  57. package/dist/types/viewdesc.d.ts +3 -2
  58. 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
- deferPendingEffects;
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.deferPendingEffects = false;
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.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 shouldRender ? /*#__PURE__*/ _react.default.createElement("img", {
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
- }) : null;
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, cursorWrapper, state } = (0, _useEditor.useEditor)(mount, props);
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, cursorWrapper);
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
- Object.defineProperty(exports, "TextNodeView", {
6
- enumerable: true,
7
- get: function() {
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 = null;
42
- renderRef = null;
43
- wasProtecting = false;
44
- containsCompositionNodeText = true;
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 pos = getPos();
57
- const { from, to } = view.state.selection;
58
- if (!(view.state.selection instanceof _prosemirrorstate.TextSelection) || from < pos || to > pos + node.nodeSize) {
59
- return false;
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.forceUpdate();
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
- if (this.shouldProtect(this.props) !== viewDesc instanceof _viewdesc.CompositionViewDesc) {
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, node } = this.props;
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
- // 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;
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.viewDescRef = null;
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.composing) return;
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
- contentEditable: false
93
+ ...!widget.type.spec.raw && {
94
+ contentEditable: false
95
+ }
93
96
  });
94
97
  }
@@ -15,5 +15,7 @@ const ChildDescriptionsContext = (0, _react.createContext)({
15
15
  },
16
16
  siblingsRef: {
17
17
  current: []
18
- }
18
+ },
19
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
20
+ findCompositionDOM: ()=>{}
19
21
  });
@@ -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, cursorWrapper) {
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(existingHandlers) {
14
- const [registry, setRegistry] = (0, _react.useState)(new Map(Object.entries(existingHandlers ?? {}).map((param)=>{
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 (!existingHandlers) return;
41
- for (const [eventType, handler] of Object.entries(existingHandlers)){
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(existingHandlers)){
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
- existingHandlers,
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)(setCursorWrapper)
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
  }