@handlewithcare/react-prosemirror 2.0.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 (209) hide show
  1. package/LICENSE.txt +12 -0
  2. package/README.md +705 -0
  3. package/dist/cjs/browser.js +53 -0
  4. package/dist/cjs/components/ChildNodeViews.js +376 -0
  5. package/dist/cjs/components/CursorWrapper.js +91 -0
  6. package/dist/cjs/components/CustomNodeView.js +79 -0
  7. package/dist/cjs/components/DocNodeView.js +104 -0
  8. package/dist/cjs/components/LayoutGroup.js +111 -0
  9. package/dist/cjs/components/MarkView.js +115 -0
  10. package/dist/cjs/components/NativeWidgetView.js +109 -0
  11. package/dist/cjs/components/NodeView.js +196 -0
  12. package/dist/cjs/components/NodeViewComponentProps.js +4 -0
  13. package/dist/cjs/components/OutputSpec.js +88 -0
  14. package/dist/cjs/components/ProseMirror.js +103 -0
  15. package/dist/cjs/components/ProseMirrorDoc.js +92 -0
  16. package/dist/cjs/components/SeparatorHackView.js +100 -0
  17. package/dist/cjs/components/TextNodeView.js +112 -0
  18. package/dist/cjs/components/TrailingHackView.js +90 -0
  19. package/dist/cjs/components/WidgetView.js +95 -0
  20. package/dist/cjs/components/WidgetViewComponentProps.js +4 -0
  21. package/dist/cjs/components/__tests__/ProseMirror.composition.test.js +398 -0
  22. package/dist/cjs/components/__tests__/ProseMirror.domchange.test.js +270 -0
  23. package/dist/cjs/components/__tests__/ProseMirror.draw-decoration.test.js +1010 -0
  24. package/dist/cjs/components/__tests__/ProseMirror.draw.test.js +337 -0
  25. package/dist/cjs/components/__tests__/ProseMirror.node-view.test.js +315 -0
  26. package/dist/cjs/components/__tests__/ProseMirror.selection.test.js +444 -0
  27. package/dist/cjs/components/__tests__/ProseMirror.test.js +382 -0
  28. package/dist/cjs/contexts/ChildDescriptorsContext.js +19 -0
  29. package/dist/cjs/contexts/EditorContext.js +12 -0
  30. package/dist/cjs/contexts/EditorStateContext.js +12 -0
  31. package/dist/cjs/contexts/LayoutGroupContext.js +12 -0
  32. package/dist/cjs/contexts/NodeViewContext.js +12 -0
  33. package/dist/cjs/contexts/SelectNodeContext.js +12 -0
  34. package/dist/cjs/contexts/StopEventContext.js +12 -0
  35. package/dist/cjs/contexts/__tests__/DeferredLayoutEffects.test.js +141 -0
  36. package/dist/cjs/decorations/ReactWidgetType.js +58 -0
  37. package/dist/cjs/decorations/computeDocDeco.js +44 -0
  38. package/dist/cjs/decorations/internalTypes.js +4 -0
  39. package/dist/cjs/decorations/iterDeco.js +79 -0
  40. package/dist/cjs/decorations/viewDecorations.js +163 -0
  41. package/dist/cjs/dom.js +142 -0
  42. package/dist/cjs/hooks/__tests__/useEditorViewLayoutEffect.test.js +108 -0
  43. package/dist/cjs/hooks/useClientOnly.js +18 -0
  44. package/dist/cjs/hooks/useComponentEventListeners.js +39 -0
  45. package/dist/cjs/hooks/useEditor.js +287 -0
  46. package/dist/cjs/hooks/useEditorEffect.js +35 -0
  47. package/dist/cjs/hooks/useEditorEventCallback.js +33 -0
  48. package/dist/cjs/hooks/useEditorEventListener.js +34 -0
  49. package/dist/cjs/hooks/useEditorState.js +16 -0
  50. package/dist/cjs/hooks/useForceUpdate.js +15 -0
  51. package/dist/cjs/hooks/useLayoutGroupEffect.js +19 -0
  52. package/dist/cjs/hooks/useNodeViewDescriptor.js +115 -0
  53. package/dist/cjs/hooks/useReactKeys.js +17 -0
  54. package/dist/cjs/hooks/useSelectNode.js +28 -0
  55. package/dist/cjs/hooks/useStopEvent.js +24 -0
  56. package/dist/cjs/index.js +53 -0
  57. package/dist/cjs/package.json +3 -0
  58. package/dist/cjs/plugins/__tests__/reactKeys.test.js +81 -0
  59. package/dist/cjs/plugins/beforeInputPlugin.js +143 -0
  60. package/dist/cjs/plugins/componentEventListeners.js +35 -0
  61. package/dist/cjs/plugins/componentEventListenersPlugin.js +35 -0
  62. package/dist/cjs/plugins/reactKeys.js +96 -0
  63. package/dist/cjs/props.js +269 -0
  64. package/dist/cjs/selection/SelectionDOMObserver.js +174 -0
  65. package/dist/cjs/selection/hasFocusAndSelection.js +35 -0
  66. package/dist/cjs/selection/selectionFromDOM.js +77 -0
  67. package/dist/cjs/selection/selectionToDOM.js +226 -0
  68. package/dist/cjs/ssr.js +85 -0
  69. package/dist/cjs/testing/editorViewTestHelpers.js +111 -0
  70. package/dist/cjs/testing/setupProseMirrorView.js +94 -0
  71. package/dist/cjs/viewdesc.js +664 -0
  72. package/dist/esm/browser.js +43 -0
  73. package/dist/esm/components/ChildNodeViews.js +318 -0
  74. package/dist/esm/components/CursorWrapper.js +40 -0
  75. package/dist/esm/components/CustomNodeView.js +28 -0
  76. package/dist/esm/components/DocNodeView.js +53 -0
  77. package/dist/esm/components/LayoutGroup.js +66 -0
  78. package/dist/esm/components/MarkView.js +64 -0
  79. package/dist/esm/components/NativeWidgetView.js +58 -0
  80. package/dist/esm/components/NodeView.js +145 -0
  81. package/dist/esm/components/NodeViewComponentProps.js +1 -0
  82. package/dist/esm/components/OutputSpec.js +38 -0
  83. package/dist/esm/components/ProseMirror.js +52 -0
  84. package/dist/esm/components/ProseMirrorDoc.js +34 -0
  85. package/dist/esm/components/SeparatorHackView.js +49 -0
  86. package/dist/esm/components/TextNodeView.js +102 -0
  87. package/dist/esm/components/TrailingHackView.js +39 -0
  88. package/dist/esm/components/WidgetView.js +44 -0
  89. package/dist/esm/components/WidgetViewComponentProps.js +1 -0
  90. package/dist/esm/components/__tests__/ProseMirror.composition.test.js +395 -0
  91. package/dist/esm/components/__tests__/ProseMirror.domchange.test.js +266 -0
  92. package/dist/esm/components/__tests__/ProseMirror.draw-decoration.test.js +967 -0
  93. package/dist/esm/components/__tests__/ProseMirror.draw.test.js +294 -0
  94. package/dist/esm/components/__tests__/ProseMirror.node-view.test.js +272 -0
  95. package/dist/esm/components/__tests__/ProseMirror.selection.test.js +440 -0
  96. package/dist/esm/components/__tests__/ProseMirror.test.js +339 -0
  97. package/dist/esm/contexts/ChildDescriptorsContext.js +9 -0
  98. package/dist/esm/contexts/EditorContext.js +7 -0
  99. package/dist/esm/contexts/EditorStateContext.js +2 -0
  100. package/dist/esm/contexts/LayoutGroupContext.js +2 -0
  101. package/dist/esm/contexts/NodeViewContext.js +2 -0
  102. package/dist/esm/contexts/SelectNodeContext.js +2 -0
  103. package/dist/esm/contexts/StopEventContext.js +2 -0
  104. package/dist/esm/contexts/__tests__/DeferredLayoutEffects.test.js +98 -0
  105. package/dist/esm/decorations/ReactWidgetType.js +40 -0
  106. package/dist/esm/decorations/computeDocDeco.js +44 -0
  107. package/dist/esm/decorations/internalTypes.js +1 -0
  108. package/dist/esm/decorations/iterDeco.js +73 -0
  109. package/dist/esm/decorations/viewDecorations.js +163 -0
  110. package/dist/esm/dom.js +105 -0
  111. package/dist/esm/hooks/__tests__/useEditorViewLayoutEffect.test.js +99 -0
  112. package/dist/esm/hooks/useClientOnly.js +8 -0
  113. package/dist/esm/hooks/useComponentEventListeners.js +54 -0
  114. package/dist/esm/hooks/useEditor.js +278 -0
  115. package/dist/esm/hooks/useEditorEffect.js +38 -0
  116. package/dist/esm/hooks/useEditorEventCallback.js +35 -0
  117. package/dist/esm/hooks/useEditorEventListener.js +28 -0
  118. package/dist/esm/hooks/useEditorState.js +8 -0
  119. package/dist/esm/hooks/useForceUpdate.js +8 -0
  120. package/dist/esm/hooks/useLayoutGroupEffect.js +9 -0
  121. package/dist/esm/hooks/useNodeViewDescriptor.js +105 -0
  122. package/dist/esm/hooks/useReactKeys.js +7 -0
  123. package/dist/esm/hooks/useSelectNode.js +18 -0
  124. package/dist/esm/hooks/useStopEvent.js +14 -0
  125. package/dist/esm/index.js +11 -0
  126. package/dist/esm/plugins/__tests__/reactKeys.test.js +77 -0
  127. package/dist/esm/plugins/beforeInputPlugin.js +133 -0
  128. package/dist/esm/plugins/componentEventListeners.js +25 -0
  129. package/dist/esm/plugins/componentEventListenersPlugin.js +25 -0
  130. package/dist/esm/plugins/reactKeys.js +81 -0
  131. package/dist/esm/props.js +251 -0
  132. package/dist/esm/selection/SelectionDOMObserver.js +164 -0
  133. package/dist/esm/selection/hasFocusAndSelection.js +17 -0
  134. package/dist/esm/selection/selectionFromDOM.js +59 -0
  135. package/dist/esm/selection/selectionToDOM.js +196 -0
  136. package/dist/esm/ssr.js +82 -0
  137. package/dist/esm/testing/editorViewTestHelpers.js +88 -0
  138. package/dist/esm/testing/setupProseMirrorView.js +76 -0
  139. package/dist/esm/viewdesc.js +654 -0
  140. package/dist/tsconfig.tsbuildinfo +1 -0
  141. package/dist/types/browser.d.ts +15 -0
  142. package/dist/types/components/ChildNodeViews.d.ts +9 -0
  143. package/dist/types/components/CursorWrapper.d.ts +5 -0
  144. package/dist/types/components/CustomNodeView.d.ts +21 -0
  145. package/dist/types/components/DocNodeView.d.ts +20 -0
  146. package/dist/types/components/LayoutGroup.d.ts +12 -0
  147. package/dist/types/components/MarkView.d.ts +9 -0
  148. package/dist/types/components/NativeWidgetView.d.ts +8 -0
  149. package/dist/types/components/NodeView.d.ts +11 -0
  150. package/dist/types/components/NodeViewComponentProps.d.ts +12 -0
  151. package/dist/types/components/OutputSpec.d.ts +8 -0
  152. package/dist/types/components/ProseMirror.d.ts +15 -0
  153. package/dist/types/components/ProseMirrorDoc.d.ts +10 -0
  154. package/dist/types/components/SeparatorHackView.d.ts +6 -0
  155. package/dist/types/components/TextNodeView.d.ts +23 -0
  156. package/dist/types/components/TrailingHackView.d.ts +6 -0
  157. package/dist/types/components/WidgetView.d.ts +8 -0
  158. package/dist/types/components/WidgetViewComponentProps.d.ts +6 -0
  159. package/dist/types/components/__tests__/ProseMirror.composition.test.d.ts +1 -0
  160. package/dist/types/components/__tests__/ProseMirror.domchange.test.d.ts +1 -0
  161. package/dist/types/components/__tests__/ProseMirror.draw-decoration.test.d.ts +1 -0
  162. package/dist/types/components/__tests__/ProseMirror.draw.test.d.ts +1 -0
  163. package/dist/types/components/__tests__/ProseMirror.node-view.test.d.ts +1 -0
  164. package/dist/types/components/__tests__/ProseMirror.selection.test.d.ts +1 -0
  165. package/dist/types/components/__tests__/ProseMirror.test.d.ts +1 -0
  166. package/dist/types/contexts/ChildDescriptorsContext.d.ts +6 -0
  167. package/dist/types/contexts/EditorContext.d.ts +14 -0
  168. package/dist/types/contexts/EditorStateContext.d.ts +2 -0
  169. package/dist/types/contexts/LayoutGroupContext.d.ts +5 -0
  170. package/dist/types/contexts/NodeViewContext.d.ts +6 -0
  171. package/dist/types/contexts/SelectNodeContext.d.ts +3 -0
  172. package/dist/types/contexts/StopEventContext.d.ts +3 -0
  173. package/dist/types/contexts/__tests__/DeferredLayoutEffects.test.d.ts +1 -0
  174. package/dist/types/decorations/ReactWidgetType.d.ts +39 -0
  175. package/dist/types/decorations/computeDocDeco.d.ts +13 -0
  176. package/dist/types/decorations/internalTypes.d.ts +16 -0
  177. package/dist/types/decorations/iterDeco.d.ts +3 -0
  178. package/dist/types/decorations/viewDecorations.d.ts +13 -0
  179. package/dist/types/dom.d.ts +22 -0
  180. package/dist/types/hooks/__tests__/useEditorViewLayoutEffect.test.d.ts +1 -0
  181. package/dist/types/hooks/useClientOnly.d.ts +1 -0
  182. package/dist/types/hooks/useComponentEventListeners.d.ts +33 -0
  183. package/dist/types/hooks/useEditor.d.ts +66 -0
  184. package/dist/types/hooks/useEditorEffect.d.ts +17 -0
  185. package/dist/types/hooks/useEditorEventCallback.d.ts +15 -0
  186. package/dist/types/hooks/useEditorEventListener.d.ts +8 -0
  187. package/dist/types/hooks/useEditorState.d.ts +5 -0
  188. package/dist/types/hooks/useForceUpdate.d.ts +5 -0
  189. package/dist/types/hooks/useLayoutGroupEffect.d.ts +3 -0
  190. package/dist/types/hooks/useNodeViewDescriptor.d.ts +11 -0
  191. package/dist/types/hooks/useReactKeys.d.ts +5 -0
  192. package/dist/types/hooks/useSelectNode.d.ts +1 -0
  193. package/dist/types/hooks/useStopEvent.d.ts +2 -0
  194. package/dist/types/index.d.ts +12 -0
  195. package/dist/types/plugins/__tests__/reactKeys.test.d.ts +1 -0
  196. package/dist/types/plugins/beforeInputPlugin.d.ts +3 -0
  197. package/dist/types/plugins/componentEventListeners.d.ts +4 -0
  198. package/dist/types/plugins/componentEventListenersPlugin.d.ts +4 -0
  199. package/dist/types/plugins/reactKeys.d.ts +19 -0
  200. package/dist/types/props.d.ts +1174 -0
  201. package/dist/types/selection/SelectionDOMObserver.d.ts +34 -0
  202. package/dist/types/selection/hasFocusAndSelection.d.ts +3 -0
  203. package/dist/types/selection/selectionFromDOM.d.ts +4 -0
  204. package/dist/types/selection/selectionToDOM.d.ts +9 -0
  205. package/dist/types/ssr.d.ts +19 -0
  206. package/dist/types/testing/editorViewTestHelpers.d.ts +23 -0
  207. package/dist/types/testing/setupProseMirrorView.d.ts +2 -0
  208. package/dist/types/viewdesc.d.ts +131 -0
  209. package/package.json +113 -0
@@ -0,0 +1,17 @@
1
+ export function hasFocusAndSelection(view) {
2
+ if (view.editable && !view.hasFocus()) return false;
3
+ return hasSelection(view);
4
+ }
5
+ export function hasSelection(view) {
6
+ // @ts-expect-error Internal method
7
+ const sel = view.domSelectionRange();
8
+ if (!sel.anchorNode) return false;
9
+ try {
10
+ // Firefox will raise 'permission denied' errors when accessing
11
+ // properties of `sel.anchorNode` when it's in a generated CSS
12
+ // element.
13
+ return view.dom.contains(sel.anchorNode.nodeType == 3 ? sel.anchorNode.parentNode : sel.anchorNode) && (view.editable || view.dom.contains(sel.focusNode?.nodeType == 3 ? sel.focusNode?.parentNode : sel.focusNode));
14
+ } catch (_) {
15
+ return false;
16
+ }
17
+ }
@@ -0,0 +1,59 @@
1
+ import { NodeSelection, TextSelection } from "prosemirror-state";
2
+ import { isOnEdge, selectionCollapsed } from "../dom.js";
3
+ export function selectionBetween(view, $anchor, $head, bias) {
4
+ return view.someProp("createSelectionBetween", (f)=>f(view, $anchor, $head)) || TextSelection.between($anchor, $head, bias);
5
+ }
6
+ export function selectionFromDOM(view) {
7
+ let origin = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : null;
8
+ // @ts-expect-error Internal method
9
+ const domSel = view.domSelectionRange(), doc = view.state.doc;
10
+ if (!domSel.focusNode) return null;
11
+ // @ts-expect-error Internal method
12
+ let nearestDesc = view.docView.nearestDesc(domSel.focusNode);
13
+ const inWidget = nearestDesc && nearestDesc.size == 0;
14
+ // @ts-expect-error Internal method
15
+ let head = view.docView.posFromDOM(domSel.focusNode, domSel.focusOffset, 1);
16
+ if (head < 0) return null;
17
+ let $head = doc.resolve(head), anchor, selection;
18
+ if (selectionCollapsed(domSel)) {
19
+ anchor = head;
20
+ while(nearestDesc && !nearestDesc.node)nearestDesc = nearestDesc.parent;
21
+ const nearestDescNode = nearestDesc.node;
22
+ if (nearestDesc && nearestDescNode.isAtom && NodeSelection.isSelectable(nearestDescNode) && nearestDesc.parent && !(nearestDescNode.isInline && isOnEdge(domSel.focusNode, domSel.focusOffset, nearestDesc.dom))) {
23
+ const pos = nearestDesc.posBefore;
24
+ selection = new NodeSelection(head == pos ? $head : doc.resolve(pos));
25
+ }
26
+ } else {
27
+ if (// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
28
+ domSel instanceof view.dom.ownerDocument.defaultView.Selection && domSel.rangeCount > 1) {
29
+ let min = head, max = head;
30
+ for(let i = 0; i < domSel.rangeCount; i++){
31
+ const range = domSel.getRangeAt(i);
32
+ min = Math.min(min, // @ts-expect-error Internal method
33
+ view.docView.posFromDOM(range.startContainer, range.startOffset, 1));
34
+ max = Math.max(max, // @ts-expect-error Internal method
35
+ view.docView.posFromDOM(range.endContainer, range.endOffset, -1));
36
+ }
37
+ if (min < 0) return null;
38
+ [anchor, head] = max == view.state.selection.anchor ? [
39
+ max,
40
+ min
41
+ ] : [
42
+ min,
43
+ max
44
+ ];
45
+ $head = doc.resolve(head);
46
+ } else {
47
+ // @ts-expect-error Internal method
48
+ anchor = view.docView.posFromDOM(// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
49
+ domSel.anchorNode, domSel.anchorOffset, 1);
50
+ }
51
+ if (anchor < 0) return null;
52
+ }
53
+ const $anchor = doc.resolve(anchor);
54
+ if (!selection) {
55
+ const bias = origin == "pointer" || view.state.selection.head < $head.pos && !inWidget ? 1 : -1;
56
+ selection = selectionBetween(view, $anchor, $head, bias);
57
+ }
58
+ return selection;
59
+ }
@@ -0,0 +1,196 @@
1
+ import { NodeSelection, TextSelection } from "prosemirror-state";
2
+ import { browser } from "../browser.js";
3
+ // Scans forward and backward through DOM positions equivalent to the
4
+ // given one to see if the two are in the same place (i.e. after a
5
+ // text node vs at the end of that text node)
6
+ export const isEquivalentPosition = function(node, off, targetNode, targetOff) {
7
+ return targetNode && (scanFor(node, off, targetNode, targetOff, -1) || scanFor(node, off, targetNode, targetOff, 1));
8
+ };
9
+ export function hasBlockDesc(dom) {
10
+ let desc;
11
+ for(let cur = dom; cur; cur = cur.parentNode)if (desc = cur.pmViewDesc) break;
12
+ return desc && desc.node && desc.node.isBlock && (desc.dom == dom || desc.contentDOM == dom);
13
+ }
14
+ const atomElements = /^(img|br|input|textarea|hr)$/i;
15
+ function scanFor(node, off, targetNode, targetOff, dir) {
16
+ for(;;){
17
+ if (node == targetNode && off == targetOff) return true;
18
+ if (off == (dir < 0 ? 0 : nodeSize(node))) {
19
+ const parent = node.parentNode;
20
+ if (!parent || parent.nodeType != 1 || hasBlockDesc(node) || atomElements.test(node.nodeName) || node.contentEditable == "false") return false;
21
+ off = domIndex(node) + (dir < 0 ? 0 : 1);
22
+ node = parent;
23
+ } else if (node.nodeType == 1) {
24
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
25
+ node = node.childNodes[off + (dir < 0 ? -1 : 0)];
26
+ if (node.contentEditable == "false") return false;
27
+ off = dir < 0 ? nodeSize(node) : 0;
28
+ } else {
29
+ return false;
30
+ }
31
+ }
32
+ }
33
+ export const domIndex = function(node) {
34
+ let n = node;
35
+ for(let index = 0;; index++){
36
+ n = n.previousSibling;
37
+ if (!n) return index;
38
+ }
39
+ };
40
+ export function nodeSize(node) {
41
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
42
+ return node.nodeType == 3 ? node.nodeValue.length : node.childNodes.length;
43
+ }
44
+ export function syncNodeSelection(view, sel) {
45
+ const v = view;
46
+ if (sel instanceof NodeSelection) {
47
+ const desc = v.docView.descAt(sel.from);
48
+ if (desc != v.lastSelectedViewDesc) {
49
+ clearNodeSelection(v);
50
+ if (desc) desc.selectNode();
51
+ v.lastSelectedViewDesc = desc;
52
+ }
53
+ } else {
54
+ clearNodeSelection(v);
55
+ }
56
+ }
57
+ // Clear all DOM statefulness of the last node selection.
58
+ function clearNodeSelection(view) {
59
+ const v = view;
60
+ if (v.lastSelectedViewDesc) {
61
+ if (v.lastSelectedViewDesc.parent) v.lastSelectedViewDesc.deselectNode();
62
+ v.lastSelectedViewDesc = undefined;
63
+ }
64
+ }
65
+ export function hasSelection(view) {
66
+ const v = view;
67
+ const sel = v.domSelectionRange();
68
+ if (!sel.anchorNode) return false;
69
+ try {
70
+ // Firefox will raise 'permission denied' errors when accessing
71
+ // properties of `sel.anchorNode` when it's in a generated CSS
72
+ // element.
73
+ return v.dom.contains(sel.anchorNode.nodeType == 3 ? sel.anchorNode.parentNode : sel.anchorNode) && (v.editable || v.dom.contains(// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
74
+ sel.focusNode.nodeType == 3 ? sel.focusNode.parentNode : sel.focusNode));
75
+ } catch (_) {
76
+ return false;
77
+ }
78
+ }
79
+ function editorOwnsSelection(view) {
80
+ return view.editable ? view.hasFocus() : hasSelection(view) && document.activeElement && document.activeElement.contains(view.dom);
81
+ }
82
+ function selectCursorWrapper(view) {
83
+ const v = view;
84
+ const domSel = v.domSelection(), range = document.createRange();
85
+ if (!domSel) return;
86
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
87
+ const node = v.cursorWrapper.dom, img = node.nodeName == "IMG";
88
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
89
+ if (img) range.setStart(node.parentNode, domIndex(node) + 1);
90
+ else range.setStart(node, 0);
91
+ range.collapse(true);
92
+ domSel.removeAllRanges();
93
+ domSel.addRange(range);
94
+ // Kludge to kill 'control selection' in IE11 when selecting an
95
+ // invisible cursor wrapper, since that would result in those weird
96
+ // resize handles and a selection that considers the absolutely
97
+ // positioned wrapper, rather than the root editable node, the
98
+ // focused element.
99
+ if (!img && !v.state.selection.visible && browser.ie && browser.ie_version <= 11) {
100
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
101
+ node.disabled = true;
102
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
103
+ node.disabled = false;
104
+ }
105
+ }
106
+ function temporarilyEditableNear(view, pos) {
107
+ const v = view;
108
+ const { node, offset } = v.docView.domFromPos(pos, 0);
109
+ const after = offset < node.childNodes.length ? node.childNodes[offset] : null;
110
+ const before = offset ? node.childNodes[offset - 1] : null;
111
+ if (browser.safari && after && after.contentEditable == "false") return setEditable(after);
112
+ if ((!after || after.contentEditable == "false") && (!before || before.contentEditable == "false")) {
113
+ if (after) return setEditable(after);
114
+ else if (before) return setEditable(before);
115
+ }
116
+ return;
117
+ }
118
+ function setEditable(element) {
119
+ element.contentEditable = "true";
120
+ if (browser.safari && element.draggable) {
121
+ element.draggable = false;
122
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
123
+ element.wasDraggable = true;
124
+ }
125
+ return element;
126
+ }
127
+ function resetEditable(element) {
128
+ element.contentEditable = "false";
129
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
130
+ if (element.wasDraggable) {
131
+ element.draggable = true;
132
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
133
+ element.wasDraggable = null;
134
+ }
135
+ }
136
+ function removeClassOnSelectionChange(view) {
137
+ const v = view;
138
+ const doc = v.dom.ownerDocument;
139
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
140
+ doc.removeEventListener("selectionchange", v.input.hideSelectionGuard);
141
+ const domSel = v.domSelectionRange();
142
+ const node = domSel.anchorNode, offset = domSel.anchorOffset;
143
+ doc.addEventListener("selectionchange", v.input.hideSelectionGuard = ()=>{
144
+ if (domSel.anchorNode != node || domSel.anchorOffset != offset) {
145
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
146
+ doc.removeEventListener("selectionchange", v.input.hideSelectionGuard);
147
+ setTimeout(()=>{
148
+ if (!editorOwnsSelection(v) || v.state.selection.visible) v.dom.classList.remove("ProseMirror-hideselection");
149
+ }, 20);
150
+ }
151
+ });
152
+ }
153
+ const brokenSelectBetweenUneditable = browser.safari || browser.chrome && browser.chrome_version < 63;
154
+ export function selectionToDOM(view) {
155
+ let force = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : false;
156
+ const v = view;
157
+ const sel = v.state.selection;
158
+ syncNodeSelection(v, sel);
159
+ if (!editorOwnsSelection(v)) return;
160
+ // The delayed drag selection causes issues with Cell Selections
161
+ // in Safari. And the drag selection delay is to workarond issues
162
+ // which only present in Chrome.
163
+ if (!force && v.input.mouseDown && v.input.mouseDown.allowDefault && browser.chrome) {
164
+ const domSel = v.domSelectionRange(), curSel = v.domObserver.currentSelection;
165
+ if (domSel.anchorNode && curSel.anchorNode && isEquivalentPosition(domSel.anchorNode, domSel.anchorOffset, curSel.anchorNode, curSel.anchorOffset)) {
166
+ v.input.mouseDown.delayedSelectionSync = true;
167
+ v.domObserver.setCurSelection();
168
+ return;
169
+ }
170
+ }
171
+ v.domObserver.disconnectSelection();
172
+ if (v.cursorWrapper) {
173
+ selectCursorWrapper(v);
174
+ } else {
175
+ const { anchor, head } = sel;
176
+ let resetEditableFrom;
177
+ let resetEditableTo;
178
+ if (brokenSelectBetweenUneditable && !(sel instanceof TextSelection)) {
179
+ if (!sel.$from.parent.inlineContent) resetEditableFrom = temporarilyEditableNear(v, sel.from);
180
+ if (!sel.empty && !sel.$from.parent.inlineContent) resetEditableTo = temporarilyEditableNear(v, sel.to);
181
+ }
182
+ v.docView.setSelection(anchor, head, v.root, force);
183
+ if (brokenSelectBetweenUneditable) {
184
+ if (resetEditableFrom) resetEditable(resetEditableFrom);
185
+ if (resetEditableTo) resetEditable(resetEditableTo);
186
+ }
187
+ if (sel.visible) {
188
+ v.dom.classList.remove("ProseMirror-hideselection");
189
+ } else {
190
+ v.dom.classList.add("ProseMirror-hideselection");
191
+ if ("onselectionchange" in document) removeClassOnSelectionChange(v);
192
+ }
193
+ }
194
+ v.domObserver.setCurSelection();
195
+ v.domObserver.connectSelection();
196
+ }
@@ -0,0 +1,82 @@
1
+ /* eslint-disable @typescript-eslint/no-empty-function */ /**
2
+ * @fileoverview
3
+ *
4
+ * Stubs for ProseMirror View during SSR. These are extremely
5
+ * barebones, because they _do not need to actually work_. They
6
+ * just need to prevent errors from being thrown when ProseMirror
7
+ * View attemps to access these APIs while constructing the
8
+ * initial EditorView. None of these APIs are necessary for SSR to
9
+ * work properly, so it's fine that they're all no-ops.
10
+ */ let ClassList = class ClassList {
11
+ add() {}
12
+ remove() {}
13
+ };
14
+ let ElementStub = class ElementStub {
15
+ get parent() {
16
+ return new ElementStub();
17
+ }
18
+ get parentNode() {
19
+ return new ElementStub();
20
+ }
21
+ nodeName = "div";
22
+ appendChild() {
23
+ return new ElementStub();
24
+ }
25
+ setAttribute() {}
26
+ hasAttribute() {
27
+ return false;
28
+ }
29
+ insertBefore() {}
30
+ get classList() {
31
+ return new ClassList();
32
+ }
33
+ get ownerDocument() {
34
+ return new DocumentStub();
35
+ }
36
+ style = {};
37
+ addEventListener() {}
38
+ removeEventListener() {}
39
+ replaceChildren() {}
40
+ };
41
+ let DocumentStub = class DocumentStub {
42
+ createElement() {
43
+ return new ElementStub();
44
+ }
45
+ addEventListener() {}
46
+ removeEventListener() {}
47
+ get documentElement() {
48
+ return new ElementStub();
49
+ }
50
+ };
51
+ /**
52
+ * Sets up tiny no-op stubs for the global window and document.
53
+ * These are used to prevent errors from being thrown when ProseMirror's
54
+ * EditorView attempts to access the DOM in its constructor during SSR.
55
+ *
56
+ * Returns a cleanup function that resets the window and document back
57
+ * to their original values (undefined).
58
+ */ export function setSsrStubs() {
59
+ const prevWindow = globalThis.window;
60
+ // @ts-expect-error HACK - EditorView checks for window.MutationObserver
61
+ // in its constructor, which breaks SSR. We temporarily set window
62
+ // to an empty object to prevent an error from being thrown, and then
63
+ // clean it up so that other isomorphic code doesn't get confused about
64
+ // whether there's a functioning global window object
65
+ globalThis.window ??= {
66
+ visualViewport: null
67
+ };
68
+ const prevDocument = globalThis.document;
69
+ // @ts-expect-error HACK: This is only used during SSR, and only
70
+ // to prevent outright errors when ProseMirror View attempts to
71
+ // access document properties either on import or when constructing
72
+ // the EditorView.
73
+ globalThis.document ??= new DocumentStub();
74
+ return function cleanupSsrStubs() {
75
+ if (globalThis.window !== prevWindow) {
76
+ globalThis.window = prevWindow;
77
+ }
78
+ if (globalThis.document !== prevDocument) {
79
+ globalThis.document = prevDocument;
80
+ }
81
+ };
82
+ }
@@ -0,0 +1,88 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */ import { render } from "@testing-library/react";
2
+ import { expect } from "expect";
3
+ import { Node } from "prosemirror-model";
4
+ import { EditorState, TextSelection } from "prosemirror-state";
5
+ import { doc, eq, p, schema } from "prosemirror-test-builder";
6
+ import React from "react";
7
+ import { ProseMirror } from "../components/ProseMirror.js";
8
+ import { ProseMirrorDoc } from "../components/ProseMirrorDoc.js";
9
+ import { useEditorEffect } from "../hooks/useEditorEffect.js";
10
+ import { reactKeys } from "../plugins/reactKeys.js";
11
+ const toEqualNode = function(actual, expected) {
12
+ if (!(actual instanceof Node && expected instanceof Node)) {
13
+ throw new Error("Must be comparing nodes");
14
+ }
15
+ const pass = eq(actual, expected);
16
+ return {
17
+ message: ()=>// `this` context will have correct typings
18
+ `expected ${this.utils.printReceived(actual)} ${pass ? "not " : ""}to equal ${this.utils.printExpected(expected)}`,
19
+ pass
20
+ };
21
+ };
22
+ expect.extend({
23
+ toEqualNode
24
+ });
25
+ export function tempEditor(param) {
26
+ let { doc: startDoc, selection, controlled, plugins, ...props } = param;
27
+ startDoc = startDoc ?? doc(p());
28
+ const state = EditorState.create({
29
+ doc: startDoc,
30
+ schema,
31
+ selection: selection ?? startDoc.tag?.a ? TextSelection.create(startDoc, startDoc.tag.a, startDoc.tag?.b) : undefined,
32
+ plugins: [
33
+ ...plugins ?? [],
34
+ reactKeys()
35
+ ]
36
+ });
37
+ let view = null;
38
+ function Test() {
39
+ useEditorEffect((v)=>{
40
+ view = v;
41
+ });
42
+ return null;
43
+ }
44
+ const { rerender, unmount } = render(/*#__PURE__*/ React.createElement(ProseMirror, {
45
+ ...controlled ? {
46
+ state
47
+ } : {
48
+ defaultState: state
49
+ },
50
+ ...props
51
+ }, /*#__PURE__*/ React.createElement(Test, null), /*#__PURE__*/ React.createElement(ProseMirrorDoc, null)));
52
+ function rerenderEditor() {
53
+ let { ...newProps } = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {};
54
+ rerender(/*#__PURE__*/ React.createElement(ProseMirror, {
55
+ ...controlled ? {
56
+ state
57
+ } : {
58
+ defaultState: state
59
+ },
60
+ ...props,
61
+ ...newProps
62
+ }, /*#__PURE__*/ React.createElement(Test, null), /*#__PURE__*/ React.createElement(ProseMirrorDoc, null)));
63
+ return view;
64
+ }
65
+ // We need two renders for the hasContentDOM state to settle
66
+ rerenderEditor();
67
+ return {
68
+ view: view,
69
+ rerender: rerenderEditor,
70
+ unmount
71
+ };
72
+ }
73
+ function findTextNodeInner(node, text) {
74
+ if (node.nodeType == 3) {
75
+ if (node.nodeValue == text) return node;
76
+ } else if (node.nodeType == 1) {
77
+ for(let ch = node.firstChild; ch; ch = ch.nextSibling){
78
+ const found = findTextNodeInner(ch, text);
79
+ if (found) return found;
80
+ }
81
+ }
82
+ return undefined;
83
+ }
84
+ export function findTextNode(node, text) {
85
+ const found = findTextNodeInner(node, text);
86
+ if (found) return found;
87
+ throw new Error("Unable to find matching text node");
88
+ }
@@ -0,0 +1,76 @@
1
+ /* Copyright (c) The New York Times Company */ let oldElementFromPoint;
2
+ let oldGetClientRects;
3
+ let oldGetBoundingClientRect;
4
+ const mockElementFromPoint = ()=>globalThis.document.body;
5
+ const mockGetBoundingClientRect = ()=>{
6
+ return {
7
+ bottom: 0,
8
+ height: 0,
9
+ left: 0,
10
+ right: 0,
11
+ top: 0,
12
+ width: 0,
13
+ x: 0,
14
+ y: 0,
15
+ toJSON () {
16
+ return {
17
+ bottom: 0,
18
+ height: 0,
19
+ left: 0,
20
+ right: 0,
21
+ top: 0,
22
+ width: 0,
23
+ x: 0,
24
+ y: 0
25
+ };
26
+ }
27
+ };
28
+ };
29
+ const mockGetClientRects = ()=>{
30
+ const list = [
31
+ {
32
+ bottom: 0,
33
+ height: 0,
34
+ left: 0,
35
+ right: 0,
36
+ top: 0,
37
+ width: 0,
38
+ x: 0,
39
+ y: 0,
40
+ toJSON () {
41
+ return {
42
+ bottom: 0,
43
+ height: 0,
44
+ left: 0,
45
+ right: 0,
46
+ top: 0,
47
+ width: 0,
48
+ x: 0,
49
+ y: 0
50
+ };
51
+ }
52
+ }
53
+ ];
54
+ const domRectList = Object.assign(list, {
55
+ item (index) {
56
+ return list[index] ?? null;
57
+ }
58
+ });
59
+ return domRectList;
60
+ };
61
+ export function setupProseMirrorView() {
62
+ oldElementFromPoint = Document.prototype.elementFromPoint;
63
+ Document.prototype.elementFromPoint = mockElementFromPoint;
64
+ oldGetClientRects = Range.prototype.getClientRects;
65
+ Range.prototype.getClientRects = mockGetClientRects;
66
+ oldGetBoundingClientRect = Range.prototype.getBoundingClientRect;
67
+ Range.prototype.getBoundingClientRect = mockGetBoundingClientRect;
68
+ }
69
+ export function teardownProseMirrorView() {
70
+ // @ts-expect-error jsdom actually doesn't implement these, so they might be undefined
71
+ Document.prototype.elementFromPoint = oldElementFromPoint;
72
+ // @ts-expect-error jsdom actually doesn't implement these, so they might be undefined
73
+ Range.prototype.getClientRects = oldGetClientRects;
74
+ // @ts-expect-error jsdom actually doesn't implement these, so they might be undefined
75
+ Range.prototype.getBoundingClientRect = oldGetBoundingClientRect;
76
+ }