@handlewithcare/react-prosemirror 2.4.12 → 2.5.0

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 (127) hide show
  1. package/dist/cjs/AbstractEditorView.js +4 -0
  2. package/dist/cjs/ReactEditorView.js +156 -0
  3. package/dist/cjs/StaticEditorView.js +86 -0
  4. package/dist/cjs/components/ChildNodeViews.js +59 -30
  5. package/dist/cjs/components/CustomNodeView.js +9 -25
  6. package/dist/cjs/components/DocNodeView.js +6 -15
  7. package/dist/cjs/components/MarkView.js +1 -2
  8. package/dist/cjs/components/NativeWidgetView.js +2 -3
  9. package/dist/cjs/components/NodeView.js +1 -1
  10. package/dist/cjs/components/ProseMirror.js +11 -14
  11. package/dist/cjs/components/ReactNodeView.js +3 -4
  12. package/dist/cjs/components/SeparatorHackView.js +1 -2
  13. package/dist/cjs/components/TextNodeView.js +4 -5
  14. package/dist/cjs/components/TrailingHackView.js +1 -2
  15. package/dist/cjs/components/WidgetView.js +2 -4
  16. package/dist/cjs/constants.js +33 -0
  17. package/dist/cjs/hooks/useEditor.js +32 -228
  18. package/dist/cjs/hooks/useEditorEffect.js +2 -2
  19. package/dist/cjs/hooks/useEditorEventCallback.js +8 -5
  20. package/dist/cjs/hooks/useNodeViewDescriptor.js +10 -10
  21. package/dist/cjs/hooks/useReactKeys.js +1 -1
  22. package/dist/cjs/testing/editorViewTestHelpers.js +0 -2
  23. package/dist/cjs/viewdesc.js +10 -9
  24. package/dist/esm/AbstractEditorView.js +1 -0
  25. package/dist/esm/ReactEditorView.js +156 -0
  26. package/dist/esm/StaticEditorView.js +76 -0
  27. package/dist/esm/components/ChildNodeViews.js +60 -32
  28. package/dist/esm/components/CustomNodeView.js +9 -25
  29. package/dist/esm/components/DocNodeView.js +6 -15
  30. package/dist/esm/components/MarkView.js +1 -2
  31. package/dist/esm/components/NativeWidgetView.js +2 -3
  32. package/dist/esm/components/NodeView.js +1 -1
  33. package/dist/esm/components/ProseMirror.js +11 -14
  34. package/dist/esm/components/ReactNodeView.js +3 -4
  35. package/dist/esm/components/SeparatorHackView.js +1 -2
  36. package/dist/esm/components/TextNodeView.js +4 -5
  37. package/dist/esm/components/TrailingHackView.js +1 -2
  38. package/dist/esm/components/WidgetView.js +2 -4
  39. package/dist/esm/constants.js +15 -0
  40. package/dist/esm/hooks/useEditor.js +28 -217
  41. package/dist/esm/hooks/useEditorEffect.js +2 -2
  42. package/dist/esm/hooks/useEditorEventCallback.js +8 -5
  43. package/dist/esm/hooks/useNodeViewDescriptor.js +10 -10
  44. package/dist/esm/hooks/useReactKeys.js +1 -1
  45. package/dist/esm/testing/editorViewTestHelpers.js +0 -2
  46. package/dist/esm/viewdesc.js +3 -2
  47. package/dist/tsconfig.tsbuildinfo +1 -1
  48. package/dist/types/AbstractEditorView.d.ts +27 -0
  49. package/dist/types/ReactEditorView.d.ts +79 -0
  50. package/dist/types/StaticEditorView.d.ts +24 -0
  51. package/dist/types/components/ChildNodeViews.d.ts +2 -2
  52. package/dist/types/components/CustomNodeView.d.ts +2 -2
  53. package/dist/types/components/DocNodeView.d.ts +2 -5
  54. package/dist/types/components/MarkView.d.ts +2 -2
  55. package/dist/types/components/NativeWidgetView.d.ts +2 -2
  56. package/dist/types/components/NodeView.d.ts +2 -2
  57. package/dist/types/components/ReactNodeView.d.ts +2 -2
  58. package/dist/types/components/SeparatorHackView.d.ts +2 -2
  59. package/dist/types/components/TextNodeView.d.ts +4 -3
  60. package/dist/types/components/TrailingHackView.d.ts +2 -2
  61. package/dist/types/components/WidgetView.d.ts +2 -2
  62. package/dist/types/constants.d.ts +4 -0
  63. package/dist/types/contexts/EditorContext.d.ts +6 -4
  64. package/dist/types/decorations/computeDocDeco.d.ts +3 -2
  65. package/dist/types/decorations/viewDecorations.d.ts +3 -2
  66. package/dist/types/hooks/useEditor.d.ts +5 -46
  67. package/dist/types/hooks/useNodeViewDescriptor.d.ts +1 -1
  68. package/dist/types/hooks/useReactKeys.d.ts +1 -1
  69. package/dist/types/props.d.ts +3 -3
  70. package/dist/types/viewdesc.d.ts +6 -5
  71. package/package.json +6 -2
  72. package/dist/cjs/components/Editor.js +0 -28
  73. package/dist/cjs/components/NodeViews.js +0 -73
  74. package/dist/cjs/components/__tests__/LayoutGroup.test.js +0 -141
  75. package/dist/cjs/components/__tests__/ProseMirror.test.js +0 -255
  76. package/dist/cjs/contexts/NodeViewsContext.js +0 -10
  77. package/dist/cjs/hooks/__tests__/useEditorViewLayoutEffect.test.js +0 -107
  78. package/dist/cjs/hooks/__tests__/useNodeViews.test.js +0 -159
  79. package/dist/cjs/hooks/useEditorView.js +0 -100
  80. package/dist/cjs/hooks/useNodePos.js +0 -69
  81. package/dist/cjs/hooks/useNodeViews.js +0 -100
  82. package/dist/cjs/nodeViews/createReactNodeViewConstructor.js +0 -244
  83. package/dist/cjs/nodeViews/phrasingContentTags.js +0 -57
  84. package/dist/cjs/plugins/__tests__/react.test.js +0 -139
  85. package/dist/cjs/plugins/react.js +0 -71
  86. package/dist/cjs/selection/SelectionDOMObserver.js +0 -171
  87. package/dist/cjs/selection/hasFocusAndSelection.js +0 -35
  88. package/dist/cjs/selection/selectionFromDOM.js +0 -77
  89. package/dist/cjs/selection/selectionToDOM.js +0 -226
  90. package/dist/cjs/ssr.js +0 -85
  91. package/dist/esm/components/Editor.js +0 -15
  92. package/dist/esm/components/NodeViews.js +0 -26
  93. package/dist/esm/components/__tests__/LayoutGroup.test.js +0 -98
  94. package/dist/esm/components/__tests__/ProseMirror.test.js +0 -207
  95. package/dist/esm/contexts/NodeViewsContext.js +0 -9
  96. package/dist/esm/hooks/__tests__/useEditorViewLayoutEffect.test.js +0 -98
  97. package/dist/esm/hooks/__tests__/useNodeViews.test.js +0 -116
  98. package/dist/esm/hooks/useEditorView.js +0 -99
  99. package/dist/esm/hooks/useNodePos.js +0 -16
  100. package/dist/esm/hooks/useNodeViews.js +0 -53
  101. package/dist/esm/nodeViews/createReactNodeViewConstructor.js +0 -214
  102. package/dist/esm/nodeViews/phrasingContentTags.js +0 -49
  103. package/dist/esm/plugins/__tests__/react.test.js +0 -135
  104. package/dist/esm/plugins/react.js +0 -64
  105. package/dist/esm/selection/SelectionDOMObserver.js +0 -161
  106. package/dist/esm/selection/hasFocusAndSelection.js +0 -17
  107. package/dist/esm/selection/selectionFromDOM.js +0 -59
  108. package/dist/esm/selection/selectionToDOM.js +0 -196
  109. package/dist/esm/ssr.js +0 -82
  110. package/dist/types/components/Editor.d.ts +0 -7
  111. package/dist/types/components/NodeViews.d.ts +0 -6
  112. package/dist/types/components/__tests__/LayoutGroup.test.d.ts +0 -1
  113. package/dist/types/contexts/NodeViewsContext.d.ts +0 -19
  114. package/dist/types/hooks/__tests__/useEditorViewLayoutEffect.test.d.ts +0 -1
  115. package/dist/types/hooks/__tests__/useNodeViews.test.d.ts +0 -1
  116. package/dist/types/hooks/useEditorView.d.ts +0 -23
  117. package/dist/types/hooks/useNodePos.d.ts +0 -9
  118. package/dist/types/hooks/useNodeViews.d.ts +0 -5
  119. package/dist/types/nodeViews/createReactNodeViewConstructor.d.ts +0 -48
  120. package/dist/types/nodeViews/phrasingContentTags.d.ts +0 -1
  121. package/dist/types/plugins/__tests__/react.test.d.ts +0 -1
  122. package/dist/types/plugins/react.d.ts +0 -21
  123. package/dist/types/selection/SelectionDOMObserver.d.ts +0 -33
  124. package/dist/types/selection/hasFocusAndSelection.d.ts +0 -3
  125. package/dist/types/selection/selectionFromDOM.d.ts +0 -4
  126. package/dist/types/selection/selectionToDOM.d.ts +0 -9
  127. package/dist/types/ssr.d.ts +0 -19
@@ -36,7 +36,7 @@ _export(exports, {
36
36
  });
37
37
  const _prosemirrormodel = require("prosemirror-model");
38
38
  const _browser = require("./browser.js");
39
- const _selectionToDOM = require("./selection/selectionToDOM.js");
39
+ const _dom = require("./dom.js");
40
40
  function sortViewDescs(a, b) {
41
41
  if (a instanceof TrailingHackViewDesc) return 1;
42
42
  if (b instanceof TrailingHackViewDesc) return -1;
@@ -157,7 +157,7 @@ let ViewDesc = class ViewDesc {
157
157
  // start or at the end of this view desc.
158
158
  let atEnd;
159
159
  if (dom == this.dom && this.contentDOM) {
160
- atEnd = offset > (0, _selectionToDOM.domIndex)(this.contentDOM);
160
+ atEnd = offset > (0, _dom.domIndex)(this.contentDOM);
161
161
  } else if (this.contentDOM && this.contentDOM != this.dom && this.dom.contains(this.contentDOM)) {
162
162
  atEnd = dom.compareDocumentPosition(this.contentDOM) & 2;
163
163
  } else if (this.dom.firstChild) {
@@ -258,7 +258,7 @@ let ViewDesc = class ViewDesc {
258
258
  if (prev && side && enter && !prev.border && !prev.domAtom) return prev.domFromPos(prev.size, side);
259
259
  return {
260
260
  node: this.contentDOM,
261
- offset: prev ? (0, _selectionToDOM.domIndex)(prev.dom) + 1 : 0
261
+ offset: prev ? (0, _dom.domIndex)(prev.dom) + 1 : 0
262
262
  };
263
263
  } else {
264
264
  let next, enter = true;
@@ -269,7 +269,7 @@ let ViewDesc = class ViewDesc {
269
269
  if (next && enter && !next.border && !next.domAtom) return next.domFromPos(0, side);
270
270
  return {
271
271
  node: this.contentDOM,
272
- offset: next ? (0, _selectionToDOM.domIndex)(next.dom) : this.contentDOM.childNodes.length
272
+ offset: next ? (0, _dom.domIndex)(next.dom) : this.contentDOM.childNodes.length
273
273
  };
274
274
  }
275
275
  }
@@ -295,7 +295,7 @@ let ViewDesc = class ViewDesc {
295
295
  for(let j = i; j > 0; j--){
296
296
  const prev = this.children[j - 1];
297
297
  if (prev.size && prev.dom.parentNode == this.contentDOM && !prev.emptyChildAt(1)) {
298
- fromOffset = (0, _selectionToDOM.domIndex)(prev.dom) + 1;
298
+ fromOffset = (0, _dom.domIndex)(prev.dom) + 1;
299
299
  break;
300
300
  }
301
301
  from -= prev.size;
@@ -307,7 +307,7 @@ let ViewDesc = class ViewDesc {
307
307
  for(let j = i + 1; j < this.children.length; j++){
308
308
  const next = this.children[j];
309
309
  if (next.size && next.dom.parentNode == this.contentDOM && !next.emptyChildAt(-1)) {
310
- toOffset = (0, _selectionToDOM.domIndex)(next.dom);
310
+ toOffset = (0, _dom.domIndex)(next.dom);
311
311
  break;
312
312
  }
313
313
  to += next.size;
@@ -354,7 +354,6 @@ let ViewDesc = class ViewDesc {
354
354
  let anchorDOM = this.domFromPos(anchor, anchor ? -1 : 1);
355
355
  let headDOM = head == anchor ? anchorDOM : this.domFromPos(head, head ? -1 : 1);
356
356
  const domSel = view.root.getSelection();
357
- // @ts-expect-error - Internal method domSelectionRange
358
357
  const selRange = view.domSelectionRange();
359
358
  let brKludge = false;
360
359
  // On Firefox, using Selection.collapse to put the cursor after a
@@ -371,7 +370,7 @@ let ViewDesc = class ViewDesc {
371
370
  if (after = scan.nextSibling) {
372
371
  if (after.nodeName == "BR") anchorDOM = headDOM = {
373
372
  node: after.parentNode,
374
- offset: (0, _selectionToDOM.domIndex)(after) + 1
373
+ offset: (0, _dom.domIndex)(after) + 1
375
374
  };
376
375
  break;
377
376
  }
@@ -391,7 +390,7 @@ let ViewDesc = class ViewDesc {
391
390
  const after = selRange.focusNode.childNodes[selRange.focusOffset];
392
391
  if (after && after.contentEditable == "false") force = true;
393
392
  }
394
- if (!(force || brKludge && _browser.browser.safari) && (0, _selectionToDOM.isEquivalentPosition)(anchorDOM.node, anchorDOM.offset, selRange.anchorNode, selRange.anchorOffset) && (0, _selectionToDOM.isEquivalentPosition)(headDOM.node, headDOM.offset, selRange.focusNode, selRange.focusOffset)) return;
393
+ if (!(force || brKludge && _browser.browser.safari) && (0, _dom.isEquivalentPosition)(anchorDOM.node, anchorDOM.offset, selRange.anchorNode, selRange.anchorOffset) && (0, _dom.isEquivalentPosition)(headDOM.node, headDOM.offset, selRange.focusNode, selRange.focusOffset)) return;
395
394
  // Selection.extend can be used to create an 'inverted' selection
396
395
  // (one where the focus is before the anchor), but not all
397
396
  // browsers support it yet.
@@ -609,6 +608,7 @@ let NodeViewDesc = class NodeViewDesc extends ViewDesc {
609
608
  // If this desc must be updated to match the given node decoration,
610
609
  // do so and return true.
611
610
  update(_node, _outerDeco, _innerDeco, _view) {
611
+ this.dirty = NOT_DIRTY;
612
612
  return true;
613
613
  }
614
614
  get domAtom() {
@@ -631,6 +631,7 @@ let TextViewDesc = class TextViewDesc extends NodeViewDesc {
631
631
  };
632
632
  }
633
633
  update(_node, _outerDeco, _innerDeco, _view) {
634
+ this.dirty = NOT_DIRTY;
634
635
  return true;
635
636
  }
636
637
  inParent() {
@@ -0,0 +1 @@
1
+ export { };
@@ -0,0 +1,156 @@
1
+ import { EditorView } from "prosemirror-view";
2
+ import { EMPTY_STATE } from "./constants.js";
3
+ function buildNodeViews(view) {
4
+ const result = Object.create(null);
5
+ function add(obj) {
6
+ for(const prop in obj)if (!Object.prototype.hasOwnProperty.call(result, prop)) // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
7
+ result[prop] = obj[prop];
8
+ }
9
+ view.someProp("nodeViews", add);
10
+ view.someProp("markViews", add);
11
+ return result;
12
+ }
13
+ function changedNodeViews(a, b) {
14
+ let nA = 0, nB = 0;
15
+ for(const prop in a){
16
+ if (a[prop] != b[prop]) return true;
17
+ nA++;
18
+ }
19
+ for(const _ in b)nB++;
20
+ return nA != nB;
21
+ }
22
+ /**
23
+ * Extends EditorView to make prop and state updates pure, remove the DOM
24
+ * Mutation Observer, and use a custom document view managed by React.
25
+ *
26
+ * @privateRemarks
27
+ *
28
+ * The implementation relies on the base class using a private member to store
29
+ * the committed props and having a public getter that we override to return the
30
+ * latest, uncommitted props. The base class can then be told to update when the
31
+ * React effects are commit an update, applying the pending, uncommitted props.
32
+ */ export class ReactEditorView extends EditorView {
33
+ nextProps;
34
+ prevState;
35
+ constructor(place, props){
36
+ // By the time the editor view mounts this should exist.
37
+ // We assume it is not possible to set the mount point otherwise.
38
+ const docView = place.mount.pmViewDesc;
39
+ // Prevent the base class from destroying the React-managed nodes.
40
+ // Restore them below after invoking the base class constructor.
41
+ const reactContent = [
42
+ ...place.mount.childNodes
43
+ ];
44
+ // Prevent the base class from mutating the React-managed attributes.
45
+ // Restore them below after invoking the base class constructor.
46
+ const reactAttrs = [
47
+ ...place.mount.attributes
48
+ ];
49
+ for (const attr of reactAttrs){
50
+ place.mount.removeAttributeNode(attr);
51
+ }
52
+ try {
53
+ // Call the superclass constructor with only a state and no plugins.
54
+ // We'll set everything else ourselves and apply props during layout.
55
+ super(place, {
56
+ state: EMPTY_STATE
57
+ });
58
+ this.domObserver.stop();
59
+ this.domObserver.observer = null;
60
+ this.domObserver.queue = [];
61
+ } finally{
62
+ place.mount.replaceChildren(...reactContent);
63
+ for (const attr of place.mount.attributes){
64
+ place.mount.removeAttributeNode(attr);
65
+ }
66
+ for (const attr of reactAttrs){
67
+ place.mount.setAttributeNode(attr);
68
+ }
69
+ }
70
+ this.prevState = EMPTY_STATE;
71
+ this.nextProps = props;
72
+ this.state = props.state;
73
+ this.nodeViews = buildNodeViews(this);
74
+ this.docView = docView;
75
+ this.dom.pmViewDesc = docView;
76
+ }
77
+ get props() {
78
+ return this.nextProps;
79
+ }
80
+ setProps(props) {
81
+ this.update({
82
+ ...this.props,
83
+ ...props
84
+ });
85
+ }
86
+ update(props) {
87
+ const prevProps = this.nextProps;
88
+ this.nextProps = props;
89
+ this.state = props.state;
90
+ if (prevProps.state.plugins !== props.state.plugins || prevProps.plugins !== props.plugins) {
91
+ const nodeViews = buildNodeViews(this);
92
+ if (changedNodeViews(this.nodeViews, nodeViews)) {
93
+ this.nodeViews = nodeViews;
94
+ }
95
+ }
96
+ this.editable = !this.someProp("editable", (value)=>value(this.state) === false);
97
+ }
98
+ updateState(state) {
99
+ this.setProps({
100
+ state
101
+ });
102
+ }
103
+ someProp(propName, f) {
104
+ if (!this.props) {
105
+ // The base class constructor calls this method before props are set.
106
+ return undefined;
107
+ }
108
+ const prop = this.props[propName];
109
+ if (prop) {
110
+ const result = f ? f(prop) : prop;
111
+ if (result) {
112
+ return result;
113
+ }
114
+ }
115
+ for (const plugin of this.props.plugins ?? []){
116
+ const prop = plugin.props[propName];
117
+ if (prop) {
118
+ const result = f ? f(prop) : prop;
119
+ if (result) {
120
+ return result;
121
+ }
122
+ }
123
+ }
124
+ for (const plugin of this.state.plugins){
125
+ const prop = plugin.props[propName];
126
+ if (prop) {
127
+ const result = f ? f(prop) : prop;
128
+ if (result) {
129
+ return result;
130
+ }
131
+ }
132
+ }
133
+ return undefined;
134
+ }
135
+ /**
136
+ * Commit effects by appling the pending props and state.
137
+ *
138
+ * Ensures the DOM selection is correct and updates plugin views.
139
+ *
140
+ * @privateRemarks
141
+ *
142
+ * The correctness of this depends on the pure update function ensuring that
143
+ * the node view set is up to date so that it does not try to redraw.
144
+ */ commitPendingEffects() {
145
+ // This class tracks state eagerly but the base class does it lazily.
146
+ // Temporarily roll it back so the base class can handle the updates.
147
+ this.state = this.prevState;
148
+ // Force the base class to try to update the document. React updated it, but
149
+ // this ensures that the base class validates the DOM selection and invokes
150
+ // node view selection callbacks.
151
+ this.docView.markDirty(-1, -1);
152
+ super.update(this.nextProps);
153
+ // Store the new previous state.
154
+ this.prevState = this.state;
155
+ }
156
+ }
@@ -0,0 +1,76 @@
1
+ export class StaticEditorView {
2
+ props;
3
+ nodeViews;
4
+ constructor(props){
5
+ this.props = props;
6
+ this.nodeViews = {};
7
+ }
8
+ get composing() {
9
+ return false;
10
+ }
11
+ get dom() {
12
+ return null;
13
+ }
14
+ get editable() {
15
+ return false;
16
+ }
17
+ get state() {
18
+ return this.props.state;
19
+ }
20
+ setProps(props) {
21
+ return this.update({
22
+ ...this.props,
23
+ ...props
24
+ });
25
+ }
26
+ update(props) {
27
+ this.props = props;
28
+ }
29
+ updateState(state) {
30
+ this.setProps({
31
+ state
32
+ });
33
+ }
34
+ someProp(propName, f) {
35
+ const prop = this.props[propName];
36
+ if (prop) {
37
+ const result = f ? f(prop) : prop;
38
+ if (result) {
39
+ return result;
40
+ }
41
+ }
42
+ for (const plugin of this.props.plugins ?? []){
43
+ const prop = plugin.props[propName];
44
+ if (prop) {
45
+ const result = f ? f(prop) : prop;
46
+ if (result) {
47
+ return result;
48
+ }
49
+ }
50
+ }
51
+ for (const plugin of this.state.plugins){
52
+ const prop = plugin.props[propName];
53
+ if (prop) {
54
+ const result = f ? f(prop) : prop;
55
+ if (result) {
56
+ return result;
57
+ }
58
+ }
59
+ }
60
+ return undefined;
61
+ }
62
+ destroy() {
63
+ // pass
64
+ }
65
+ domSelectionRange() {
66
+ return {
67
+ anchorNode: null,
68
+ anchorOffset: 0,
69
+ focusNode: null,
70
+ focusOffset: 0
71
+ };
72
+ }
73
+ domSelection() {
74
+ return null;
75
+ }
76
+ }
@@ -1,8 +1,7 @@
1
- import React, { cloneElement, createElement, memo, useContext, useRef } from "react";
1
+ import React, { cloneElement, createElement, memo, useCallback, useContext, useRef } from "react";
2
2
  import { ChildDescriptorsContext } from "../contexts/ChildDescriptorsContext.js";
3
3
  import { EditorContext } from "../contexts/EditorContext.js";
4
4
  import { iterDeco } from "../decorations/iterDeco.js";
5
- // import { useEditorState } from "../hooks/useEditorState.js";
6
5
  import { useReactKeys } from "../hooks/useReactKeys.js";
7
6
  import { htmlAttrsToReactProps, mergeReactProps } from "../props.js";
8
7
  import { MarkView } from "./MarkView.js";
@@ -24,21 +23,30 @@ export function wrapInDeco(reactNode, deco) {
24
23
  return /*#__PURE__*/ cloneElement(reactNode, mergeReactProps(reactNode.props, props));
25
24
  }
26
25
  function areChildrenEqual(a, b) {
27
- return a.type === b.type && a.marks.every((mark)=>mark.isInSet(b.marks)) && b.marks.every((mark)=>mark.isInSet(a.marks)) && a.key === b.key && (a.type === "node" ? a.outerDeco?.length === b.outerDeco?.length && a.outerDeco?.every((prevDeco)=>b.outerDeco?.some((nextDeco)=>prevDeco.from === nextDeco.from && prevDeco.to && nextDeco.to && prevDeco.type.eq(nextDeco.type))) && a.innerDeco.eq(b.innerDeco) : true) && a.node === b.node && a.widget === b.widget;
26
+ return a.type === b.type && a.marks.every((mark)=>mark.isInSet(b.marks)) && b.marks.every((mark)=>mark.isInSet(a.marks)) && a.key === b.key && (a.type === "node" ? a.outerDeco?.length === b.outerDeco?.length && a.outerDeco?.every((prevDeco)=>b.outerDeco?.some((nextDeco)=>prevDeco.from === nextDeco.from && prevDeco.to && nextDeco.to && // eslint-disable-next-line @typescript-eslint/no-explicit-any
27
+ prevDeco.type.eq(nextDeco.type))) && a.innerDeco.eq(b.innerDeco) : true) && a.node === b.node && a.widget === b.widget;
28
28
  }
29
29
  const ChildView = /*#__PURE__*/ memo(function ChildView(param) {
30
30
  let { child, getInnerPos } = param;
31
31
  const { view } = useContext(EditorContext);
32
- const getChildPos = useRef(()=>getInnerPos.current() + child.offset);
33
- getChildPos.current = ()=>getInnerPos.current() + child.offset;
32
+ const childRef = useRef(child);
33
+ childRef.current = child;
34
+ const getPos = useCallback(()=>{
35
+ return getInnerPos() + childRef.current.offset;
36
+ }, [
37
+ getInnerPos
38
+ ]);
34
39
  return child.type === "widget" ? /*#__PURE__*/ React.createElement(WidgetView, {
35
40
  key: child.key,
36
41
  widget: child.widget,
37
- getPos: getChildPos
42
+ getPos: getPos
38
43
  }) : child.type === "native-widget" ? /*#__PURE__*/ React.createElement(NativeWidgetView, {
39
44
  key: child.key,
40
45
  widget: child.widget,
41
- getPos: getChildPos
46
+ getPos: getPos
47
+ }) : child.type === "hack" ? /*#__PURE__*/ React.createElement(child.component, {
48
+ key: child.key,
49
+ getPos: getPos
42
50
  }) : child.node.isText ? /*#__PURE__*/ React.createElement(ChildDescriptorsContext.Consumer, {
43
51
  key: child.key
44
52
  }, (param)=>{
@@ -46,7 +54,7 @@ const ChildView = /*#__PURE__*/ memo(function ChildView(param) {
46
54
  return /*#__PURE__*/ React.createElement(TextNodeView, {
47
55
  view: view,
48
56
  node: child.node,
49
- getPos: getChildPos,
57
+ getPos: getPos,
50
58
  siblingsRef: siblingsRef,
51
59
  parentRef: parentRef,
52
60
  decorations: child.outerDeco
@@ -54,7 +62,7 @@ const ChildView = /*#__PURE__*/ memo(function ChildView(param) {
54
62
  }) : /*#__PURE__*/ React.createElement(NodeView, {
55
63
  key: child.key,
56
64
  node: child.node,
57
- getPos: getChildPos,
65
+ getPos: getPos,
58
66
  outerDeco: child.outerDeco,
59
67
  innerDeco: child.innerDeco
60
68
  });
@@ -62,8 +70,13 @@ const ChildView = /*#__PURE__*/ memo(function ChildView(param) {
62
70
  const InlinePartition = /*#__PURE__*/ memo(function InlinePartition(param) {
63
71
  let { childViews, getInnerPos } = param;
64
72
  const firstChild = childViews[0];
65
- const getFirstChildPos = useRef(()=>getInnerPos.current() + firstChild.offset);
66
- getFirstChildPos.current = ()=>getInnerPos.current() + firstChild.offset;
73
+ const firstChildRef = useRef(firstChild);
74
+ firstChildRef.current = firstChild;
75
+ const getPos = useCallback(()=>{
76
+ return getInnerPos() + firstChildRef.current.offset;
77
+ }, [
78
+ getInnerPos
79
+ ]);
67
80
  const firstMark = firstChild.marks[0];
68
81
  if (!firstMark) {
69
82
  return /*#__PURE__*/ React.createElement(React.Fragment, null, childViews.map((child)=>{
@@ -75,9 +88,9 @@ const InlinePartition = /*#__PURE__*/ memo(function InlinePartition(param) {
75
88
  }));
76
89
  }
77
90
  return /*#__PURE__*/ React.createElement(MarkView, {
78
- getPos: getFirstChildPos,
79
91
  key: firstChild.key,
80
- mark: firstMark
92
+ mark: firstMark,
93
+ getPos: getPos
81
94
  }, /*#__PURE__*/ React.createElement(InlineView, {
82
95
  key: firstChild.key,
83
96
  getInnerPos: getInnerPos,
@@ -135,7 +148,7 @@ const InlineView = /*#__PURE__*/ memo(function InlineView(param) {
135
148
  });
136
149
  }));
137
150
  });
138
- function createKey(innerPos, offset, type, posToKey, widget, index) {
151
+ function createKey(innerPos, offset, index, type, posToKey, widget) {
139
152
  const pos = innerPos + offset;
140
153
  const key = posToKey?.get(pos);
141
154
  if (type === "widget" || type === "native-widget") {
@@ -179,18 +192,23 @@ function adjustWidgetMarksBack(widgetChildren, nodeChild) {
179
192
  }
180
193
  const ChildElement = /*#__PURE__*/ memo(function ChildElement(param) {
181
194
  let { child, getInnerPos } = param;
182
- const getNodePos = useRef(()=>getInnerPos.current() + child.offset);
183
- getNodePos.current = ()=>getInnerPos.current() + child.offset;
195
+ const childRef = useRef(child);
196
+ childRef.current = child;
197
+ const getPos = useCallback(()=>{
198
+ return getInnerPos() + childRef.current.offset;
199
+ }, [
200
+ getInnerPos
201
+ ]);
184
202
  if (child.type === "node") {
185
203
  return child.marks.reduce((element, mark)=>/*#__PURE__*/ React.createElement(MarkView, {
186
- getPos: getNodePos,
187
- mark: mark
204
+ mark: mark,
205
+ getPos: getPos
188
206
  }, element), /*#__PURE__*/ React.createElement(NodeView, {
189
207
  key: child.key,
190
208
  outerDeco: child.outerDeco,
191
209
  node: child.node,
192
210
  innerDeco: child.innerDeco,
193
- getPos: getNodePos
211
+ getPos: getPos
194
212
  }));
195
213
  } else {
196
214
  return /*#__PURE__*/ React.createElement(InlineView, {
@@ -224,9 +242,10 @@ function createChildElements(children, getInnerPos) {
224
242
  }
225
243
  export const ChildNodeViews = /*#__PURE__*/ memo(function ChildNodeViews(param) {
226
244
  let { getPos, node, innerDecorations } = param;
227
- // const editorState = useEditorState();
228
245
  const reactKeys = useReactKeys();
229
- const getInnerPos = useRef(()=>getPos.current() + 1);
246
+ const getInnerPos = useCallback(()=>getPos() + 1, [
247
+ getPos
248
+ ]);
230
249
  const childMap = useRef(new Map()).current;
231
250
  if (!node) return null;
232
251
  const keysSeen = new Map();
@@ -237,7 +256,7 @@ export const ChildNodeViews = /*#__PURE__*/ memo(function ChildNodeViews(param)
237
256
  const widgetMarks = widget.type.spec.marks ?? [];
238
257
  let key;
239
258
  if (isNative) {
240
- key = createKey(getInnerPos.current(), offset, "native-widget", reactKeys?.posToKey, widget, index);
259
+ key = createKey(getInnerPos(), offset, index, "native-widget", reactKeys?.posToKey, widget);
241
260
  const child = {
242
261
  type: "native-widget",
243
262
  widget,
@@ -254,7 +273,7 @@ export const ChildNodeViews = /*#__PURE__*/ memo(function ChildNodeViews(param)
254
273
  }
255
274
  keysSeen.set(key, keysSeen.size);
256
275
  } else {
257
- key = createKey(getInnerPos.current(), offset, "widget", reactKeys?.posToKey, widget, index);
276
+ key = createKey(getInnerPos(), offset, index, "widget", reactKeys?.posToKey, widget);
258
277
  const child = {
259
278
  type: "widget",
260
279
  widget: widget,
@@ -274,8 +293,8 @@ export const ChildNodeViews = /*#__PURE__*/ memo(function ChildNodeViews(param)
274
293
  const child = childMap.get(key);
275
294
  widgetChildren.push(child);
276
295
  adjustWidgetMarksForward(lastNodeChild, childMap.get(key));
277
- }, (childNode, outerDeco, innerDeco, offset)=>{
278
- const key = createKey(getInnerPos.current(), offset, "node", reactKeys?.posToKey);
296
+ }, (childNode, outerDeco, innerDeco, offset, index)=>{
297
+ const key = createKey(getInnerPos(), offset, index, "node", reactKeys?.posToKey);
279
298
  const child = {
280
299
  type: "node",
281
300
  node: childNode,
@@ -283,6 +302,7 @@ export const ChildNodeViews = /*#__PURE__*/ memo(function ChildNodeViews(param)
283
302
  innerDeco,
284
303
  outerDeco,
285
304
  offset,
305
+ index,
286
306
  key
287
307
  };
288
308
  const prevChild = childMap.get(key);
@@ -306,20 +326,28 @@ export const ChildNodeViews = /*#__PURE__*/ memo(function ChildNodeViews(param)
306
326
  // step
307
327
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
308
328
  (a, b)=>keysSeen.get(a.key) - keysSeen.get(b.key));
309
- const childElements = createChildElements(children, getInnerPos);
310
329
  if (node.isTextblock) {
311
330
  const lastChild = children[children.length - 1];
312
331
  if (!lastChild || lastChild.type !== "node" || lastChild.node.isInline && !lastChild.node.isText || // RegExp.test actually handles undefined just fine
313
332
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
314
333
  /\n$/.test(lastChild.node.text)) {
315
- childElements.push(/*#__PURE__*/ React.createElement(SeparatorHackView, {
316
- getPos: getInnerPos,
317
- key: "trailing-hack-img"
318
- }), /*#__PURE__*/ React.createElement(TrailingHackView, {
319
- getPos: getInnerPos,
334
+ children.push({
335
+ type: "hack",
336
+ component: TrailingHackView,
337
+ marks: [],
338
+ offset: lastChild?.offset ?? 0,
339
+ index: (lastChild?.index ?? 0) + 1,
320
340
  key: "trailing-hack-br"
321
- }));
341
+ }, {
342
+ type: "hack",
343
+ component: SeparatorHackView,
344
+ marks: [],
345
+ offset: lastChild?.offset ?? 0,
346
+ index: (lastChild?.index ?? 0) + 2,
347
+ key: "trailing-hack-img"
348
+ });
322
349
  }
323
350
  }
351
+ const childElements = createChildElements(children, getInnerPos);
324
352
  return /*#__PURE__*/ React.createElement(React.Fragment, null, childElements);
325
353
  });
@@ -1,4 +1,3 @@
1
- import { NodeSelection } from "prosemirror-state";
2
1
  import React, { cloneElement, createElement, memo, useContext, useMemo, useRef } from "react";
3
2
  import { createPortal } from "react-dom";
4
3
  import { ChildDescriptorsContext } from "../contexts/ChildDescriptorsContext.js";
@@ -10,11 +9,12 @@ import { useNodeViewDescriptor } from "../hooks/useNodeViewDescriptor.js";
10
9
  import { ChildNodeViews, wrapInDeco } from "./ChildNodeViews.js";
11
10
  export const CustomNodeView = /*#__PURE__*/ memo(function CustomNodeView(param) {
12
11
  let { customNodeView, node, getPos, innerDeco, outerDeco } = param;
13
- const { view } = useContext(EditorContext);
12
+ const editor = useContext(EditorContext);
13
+ // Only ReactEditorView supports custom node views.
14
+ const view = editor.view;
14
15
  const domRef = useRef(null);
15
16
  const nodeDomRef = useRef(null);
16
17
  const contentDomRef = useRef(null);
17
- const getPosFunc = useRef(()=>getPos.current()).current;
18
18
  const nodeRef = useRef(node);
19
19
  nodeRef.current = node;
20
20
  const outerDecoRef = useRef(outerDeco);
@@ -31,10 +31,7 @@ export const CustomNodeView = /*#__PURE__*/ memo(function CustomNodeView(param)
31
31
  // the node view when the layout effect is re-run.
32
32
  useClientLayoutEffect(()=>{
33
33
  if (!customNodeViewRef.current) {
34
- customNodeViewRef.current = customNodeView(nodeRef.current, // customNodeView will only be set if view is set, and we can only reach
35
- // this line if customNodeView is set
36
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
37
- view, getPosFunc, outerDecoRef.current, innerDecoRef.current);
34
+ customNodeViewRef.current = customNodeView(nodeRef.current, view, getPos, outerDecoRef.current, innerDecoRef.current);
38
35
  if (customNodeViewRef.current.stopEvent) {
39
36
  setStopEvent(customNodeViewRef.current.stopEvent.bind(customNodeViewRef.current));
40
37
  }
@@ -56,12 +53,6 @@ export const CustomNodeView = /*#__PURE__*/ memo(function CustomNodeView(param)
56
53
  }
57
54
  nodeDomRef.current = customNodeViewRootRef.current;
58
55
  customNodeViewRootRef.current.appendChild(dom);
59
- // Layout effects can run multiple times — if this effect
60
- // destroyed and recreated this node view, then we need to
61
- // resync the selectNode state
62
- if (view?.state.selection instanceof NodeSelection && view.state.selection.node === nodeRef.current) {
63
- customNodeViewRef.current.selectNode?.();
64
- }
65
56
  const nodeView = customNodeViewRef.current;
66
57
  return ()=>{
67
58
  nodeView.destroy?.();
@@ -75,7 +66,7 @@ export const CustomNodeView = /*#__PURE__*/ memo(function CustomNodeView(param)
75
66
  // eslint-disable-next-line react-hooks/exhaustive-deps
76
67
  }, [
77
68
  customNodeView,
78
- getPosFunc,
69
+ getPos,
79
70
  view
80
71
  ]);
81
72
  useClientLayoutEffect(()=>{
@@ -85,10 +76,7 @@ export const CustomNodeView = /*#__PURE__*/ memo(function CustomNodeView(param)
85
76
  if (updated) return;
86
77
  destroy?.call(customNodeViewRef.current);
87
78
  if (!customNodeViewRootRef.current) return;
88
- customNodeViewRef.current = customNodeView(nodeRef.current, // customNodeView will only be set if view is set, and we can only reach
89
- // this line if customNodeView is set
90
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
91
- view, getPosFunc, outerDecoRef.current, innerDecoRef.current);
79
+ customNodeViewRef.current = customNodeView(nodeRef.current, view, getPos, outerDecoRef.current, innerDecoRef.current);
92
80
  const { dom } = customNodeViewRef.current;
93
81
  nodeDomRef.current = customNodeViewRootRef.current;
94
82
  customNodeViewRootRef.current.appendChild(dom);
@@ -98,10 +86,9 @@ export const CustomNodeView = /*#__PURE__*/ memo(function CustomNodeView(param)
98
86
  innerDeco,
99
87
  node,
100
88
  outerDeco,
101
- getPos,
102
- getPosFunc
89
+ getPos
103
90
  ]);
104
- const { childDescriptors, nodeViewDescRef, setStopEvent, setSelectNode, setIgnoreMutation } = useNodeViewDescriptor(node, getPosFunc, domRef, nodeDomRef, innerDeco, outerDeco, undefined, contentDomRef);
91
+ const { childDescriptors, nodeViewDescRef, setStopEvent, setSelectNode, setIgnoreMutation } = useNodeViewDescriptor(node, getPos, domRef, nodeDomRef, innerDeco, outerDeco, contentDomRef);
105
92
  const childContextValue = useMemo(()=>({
106
93
  parentRef: nodeViewDescRef,
107
94
  siblingsRef: childDescriptors
@@ -116,10 +103,7 @@ export const CustomNodeView = /*#__PURE__*/ memo(function CustomNodeView(param)
116
103
  // results are stored in a ref but not actually appended
117
104
  // to the DOM until a client effect
118
105
  if (!customNodeViewRef.current) {
119
- customNodeViewRef.current = customNodeView(nodeRef.current, // customNodeView will only be set if view is set, and we can only reach
120
- // this line if customNodeView is set
121
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
122
- view, ()=>getPos.current(), outerDecoRef.current, innerDecoRef.current);
106
+ customNodeViewRef.current = customNodeView(nodeRef.current, view, getPos, outerDecoRef.current, innerDecoRef.current);
123
107
  if (customNodeViewRef.current.stopEvent) {
124
108
  setStopEvent(customNodeViewRef.current.stopEvent.bind(customNodeViewRef.current));
125
109
  }
@@ -4,18 +4,16 @@
4
4
  import { ChildDescriptorsContext } from "../contexts/ChildDescriptorsContext.js";
5
5
  import { useNodeViewDescriptor } from "../hooks/useNodeViewDescriptor.js";
6
6
  import { ChildNodeViews, wrapInDeco } from "./ChildNodeViews.js";
7
- const getPos = {
8
- current () {
9
- return -1;
10
- }
11
- };
7
+ function getPos() {
8
+ return -1;
9
+ }
12
10
  export const DocNodeView = /*#__PURE__*/ memo(/*#__PURE__*/ forwardRef(function DocNodeView(param, ref) {
13
- let { className, node, innerDeco, outerDeco, as, viewDesc, ...elementProps } = param;
11
+ let { className, node, innerDeco, outerDeco, as, ...elementProps } = param;
14
12
  const innerRef = useRef(null);
15
13
  useImperativeHandle(ref, ()=>{
16
14
  return innerRef.current;
17
15
  }, []);
18
- const { childDescriptors, nodeViewDescRef } = useNodeViewDescriptor(node, ()=>getPos.current(), innerRef, innerRef, innerDeco, outerDeco, viewDesc);
16
+ const { childDescriptors, nodeViewDescRef } = useNodeViewDescriptor(node, getPos, innerRef, innerRef, innerDeco, outerDeco, innerRef);
19
17
  const childContextValue = useMemo(()=>({
20
18
  parentRef: nodeViewDescRef,
21
19
  siblingsRef: childDescriptors
@@ -42,12 +40,5 @@ export const DocNodeView = /*#__PURE__*/ memo(/*#__PURE__*/ forwardRef(function
42
40
  node: node,
43
41
  innerDecorations: innerDeco
44
42
  })));
45
- if (!node) return element;
46
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
47
- const nodeDecorations = outerDeco.filter((deco)=>!deco.inline);
48
- if (!nodeDecorations.length) {
49
- return element;
50
- }
51
- const wrapped = nodeDecorations.reduce(wrapInDeco, element);
52
- return wrapped;
43
+ return outerDeco.reduce(wrapInDeco, element);
53
44
  }));