@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.
- package/dist/cjs/AbstractEditorView.js +4 -0
- package/dist/cjs/ReactEditorView.js +156 -0
- package/dist/cjs/StaticEditorView.js +86 -0
- package/dist/cjs/components/ChildNodeViews.js +59 -30
- package/dist/cjs/components/CustomNodeView.js +9 -25
- package/dist/cjs/components/DocNodeView.js +6 -15
- package/dist/cjs/components/MarkView.js +1 -2
- package/dist/cjs/components/NativeWidgetView.js +2 -3
- package/dist/cjs/components/NodeView.js +1 -1
- package/dist/cjs/components/ProseMirror.js +11 -14
- package/dist/cjs/components/ReactNodeView.js +3 -4
- package/dist/cjs/components/SeparatorHackView.js +1 -2
- package/dist/cjs/components/TextNodeView.js +4 -5
- package/dist/cjs/components/TrailingHackView.js +1 -2
- package/dist/cjs/components/WidgetView.js +2 -4
- package/dist/cjs/constants.js +33 -0
- package/dist/cjs/hooks/useEditor.js +32 -228
- package/dist/cjs/hooks/useEditorEffect.js +2 -2
- package/dist/cjs/hooks/useEditorEventCallback.js +8 -5
- package/dist/cjs/hooks/useNodeViewDescriptor.js +10 -10
- package/dist/cjs/hooks/useReactKeys.js +1 -1
- package/dist/cjs/testing/editorViewTestHelpers.js +0 -2
- package/dist/cjs/viewdesc.js +10 -9
- package/dist/esm/AbstractEditorView.js +1 -0
- package/dist/esm/ReactEditorView.js +156 -0
- package/dist/esm/StaticEditorView.js +76 -0
- package/dist/esm/components/ChildNodeViews.js +60 -32
- package/dist/esm/components/CustomNodeView.js +9 -25
- package/dist/esm/components/DocNodeView.js +6 -15
- package/dist/esm/components/MarkView.js +1 -2
- package/dist/esm/components/NativeWidgetView.js +2 -3
- package/dist/esm/components/NodeView.js +1 -1
- package/dist/esm/components/ProseMirror.js +11 -14
- package/dist/esm/components/ReactNodeView.js +3 -4
- package/dist/esm/components/SeparatorHackView.js +1 -2
- package/dist/esm/components/TextNodeView.js +4 -5
- package/dist/esm/components/TrailingHackView.js +1 -2
- package/dist/esm/components/WidgetView.js +2 -4
- package/dist/esm/constants.js +15 -0
- package/dist/esm/hooks/useEditor.js +28 -217
- package/dist/esm/hooks/useEditorEffect.js +2 -2
- package/dist/esm/hooks/useEditorEventCallback.js +8 -5
- package/dist/esm/hooks/useNodeViewDescriptor.js +10 -10
- package/dist/esm/hooks/useReactKeys.js +1 -1
- package/dist/esm/testing/editorViewTestHelpers.js +0 -2
- package/dist/esm/viewdesc.js +3 -2
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/AbstractEditorView.d.ts +27 -0
- package/dist/types/ReactEditorView.d.ts +79 -0
- package/dist/types/StaticEditorView.d.ts +24 -0
- package/dist/types/components/ChildNodeViews.d.ts +2 -2
- package/dist/types/components/CustomNodeView.d.ts +2 -2
- package/dist/types/components/DocNodeView.d.ts +2 -5
- package/dist/types/components/MarkView.d.ts +2 -2
- package/dist/types/components/NativeWidgetView.d.ts +2 -2
- package/dist/types/components/NodeView.d.ts +2 -2
- package/dist/types/components/ReactNodeView.d.ts +2 -2
- package/dist/types/components/SeparatorHackView.d.ts +2 -2
- package/dist/types/components/TextNodeView.d.ts +4 -3
- package/dist/types/components/TrailingHackView.d.ts +2 -2
- package/dist/types/components/WidgetView.d.ts +2 -2
- package/dist/types/constants.d.ts +4 -0
- package/dist/types/contexts/EditorContext.d.ts +6 -4
- package/dist/types/decorations/computeDocDeco.d.ts +3 -2
- package/dist/types/decorations/viewDecorations.d.ts +3 -2
- package/dist/types/hooks/useEditor.d.ts +5 -46
- package/dist/types/hooks/useNodeViewDescriptor.d.ts +1 -1
- package/dist/types/hooks/useReactKeys.d.ts +1 -1
- package/dist/types/props.d.ts +3 -3
- package/dist/types/viewdesc.d.ts +6 -5
- package/package.json +6 -2
- package/dist/cjs/components/Editor.js +0 -28
- package/dist/cjs/components/NodeViews.js +0 -73
- package/dist/cjs/components/__tests__/LayoutGroup.test.js +0 -141
- package/dist/cjs/components/__tests__/ProseMirror.test.js +0 -255
- package/dist/cjs/contexts/NodeViewsContext.js +0 -10
- package/dist/cjs/hooks/__tests__/useEditorViewLayoutEffect.test.js +0 -107
- package/dist/cjs/hooks/__tests__/useNodeViews.test.js +0 -159
- package/dist/cjs/hooks/useEditorView.js +0 -100
- package/dist/cjs/hooks/useNodePos.js +0 -69
- package/dist/cjs/hooks/useNodeViews.js +0 -100
- package/dist/cjs/nodeViews/createReactNodeViewConstructor.js +0 -244
- package/dist/cjs/nodeViews/phrasingContentTags.js +0 -57
- package/dist/cjs/plugins/__tests__/react.test.js +0 -139
- package/dist/cjs/plugins/react.js +0 -71
- package/dist/cjs/selection/SelectionDOMObserver.js +0 -171
- package/dist/cjs/selection/hasFocusAndSelection.js +0 -35
- package/dist/cjs/selection/selectionFromDOM.js +0 -77
- package/dist/cjs/selection/selectionToDOM.js +0 -226
- package/dist/cjs/ssr.js +0 -85
- package/dist/esm/components/Editor.js +0 -15
- package/dist/esm/components/NodeViews.js +0 -26
- package/dist/esm/components/__tests__/LayoutGroup.test.js +0 -98
- package/dist/esm/components/__tests__/ProseMirror.test.js +0 -207
- package/dist/esm/contexts/NodeViewsContext.js +0 -9
- package/dist/esm/hooks/__tests__/useEditorViewLayoutEffect.test.js +0 -98
- package/dist/esm/hooks/__tests__/useNodeViews.test.js +0 -116
- package/dist/esm/hooks/useEditorView.js +0 -99
- package/dist/esm/hooks/useNodePos.js +0 -16
- package/dist/esm/hooks/useNodeViews.js +0 -53
- package/dist/esm/nodeViews/createReactNodeViewConstructor.js +0 -214
- package/dist/esm/nodeViews/phrasingContentTags.js +0 -49
- package/dist/esm/plugins/__tests__/react.test.js +0 -135
- package/dist/esm/plugins/react.js +0 -64
- package/dist/esm/selection/SelectionDOMObserver.js +0 -161
- package/dist/esm/selection/hasFocusAndSelection.js +0 -17
- package/dist/esm/selection/selectionFromDOM.js +0 -59
- package/dist/esm/selection/selectionToDOM.js +0 -196
- package/dist/esm/ssr.js +0 -82
- package/dist/types/components/Editor.d.ts +0 -7
- package/dist/types/components/NodeViews.d.ts +0 -6
- package/dist/types/components/__tests__/LayoutGroup.test.d.ts +0 -1
- package/dist/types/contexts/NodeViewsContext.d.ts +0 -19
- package/dist/types/hooks/__tests__/useEditorViewLayoutEffect.test.d.ts +0 -1
- package/dist/types/hooks/__tests__/useNodeViews.test.d.ts +0 -1
- package/dist/types/hooks/useEditorView.d.ts +0 -23
- package/dist/types/hooks/useNodePos.d.ts +0 -9
- package/dist/types/hooks/useNodeViews.d.ts +0 -5
- package/dist/types/nodeViews/createReactNodeViewConstructor.d.ts +0 -48
- package/dist/types/nodeViews/phrasingContentTags.d.ts +0 -1
- package/dist/types/plugins/__tests__/react.test.d.ts +0 -1
- package/dist/types/plugins/react.d.ts +0 -21
- package/dist/types/selection/SelectionDOMObserver.d.ts +0 -33
- package/dist/types/selection/hasFocusAndSelection.d.ts +0 -3
- package/dist/types/selection/selectionFromDOM.d.ts +0 -4
- package/dist/types/selection/selectionToDOM.d.ts +0 -9
- package/dist/types/ssr.d.ts +0 -19
|
@@ -1,226 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", {
|
|
3
|
-
value: true
|
|
4
|
-
});
|
|
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
|
-
domIndex: function() {
|
|
13
|
-
return domIndex;
|
|
14
|
-
},
|
|
15
|
-
hasBlockDesc: function() {
|
|
16
|
-
return hasBlockDesc;
|
|
17
|
-
},
|
|
18
|
-
hasSelection: function() {
|
|
19
|
-
return hasSelection;
|
|
20
|
-
},
|
|
21
|
-
isEquivalentPosition: function() {
|
|
22
|
-
return isEquivalentPosition;
|
|
23
|
-
},
|
|
24
|
-
nodeSize: function() {
|
|
25
|
-
return nodeSize;
|
|
26
|
-
},
|
|
27
|
-
selectionToDOM: function() {
|
|
28
|
-
return selectionToDOM;
|
|
29
|
-
},
|
|
30
|
-
syncNodeSelection: function() {
|
|
31
|
-
return syncNodeSelection;
|
|
32
|
-
}
|
|
33
|
-
});
|
|
34
|
-
const _prosemirrorstate = require("prosemirror-state");
|
|
35
|
-
const _browser = require("../browser.js");
|
|
36
|
-
const isEquivalentPosition = function(node, off, targetNode, targetOff) {
|
|
37
|
-
return targetNode && (scanFor(node, off, targetNode, targetOff, -1) || scanFor(node, off, targetNode, targetOff, 1));
|
|
38
|
-
};
|
|
39
|
-
function hasBlockDesc(dom) {
|
|
40
|
-
let desc;
|
|
41
|
-
for(let cur = dom; cur; cur = cur.parentNode)if (desc = cur.pmViewDesc) break;
|
|
42
|
-
return desc && desc.node && desc.node.isBlock && (desc.dom == dom || desc.contentDOM == dom);
|
|
43
|
-
}
|
|
44
|
-
const atomElements = /^(img|br|input|textarea|hr)$/i;
|
|
45
|
-
function scanFor(node, off, targetNode, targetOff, dir) {
|
|
46
|
-
for(;;){
|
|
47
|
-
if (node == targetNode && off == targetOff) return true;
|
|
48
|
-
if (off == (dir < 0 ? 0 : nodeSize(node))) {
|
|
49
|
-
const parent = node.parentNode;
|
|
50
|
-
if (!parent || parent.nodeType != 1 || hasBlockDesc(node) || atomElements.test(node.nodeName) || node.contentEditable == "false") return false;
|
|
51
|
-
off = domIndex(node) + (dir < 0 ? 0 : 1);
|
|
52
|
-
node = parent;
|
|
53
|
-
} else if (node.nodeType == 1) {
|
|
54
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
55
|
-
node = node.childNodes[off + (dir < 0 ? -1 : 0)];
|
|
56
|
-
if (node.contentEditable == "false") return false;
|
|
57
|
-
off = dir < 0 ? nodeSize(node) : 0;
|
|
58
|
-
} else {
|
|
59
|
-
return false;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
const domIndex = function(node) {
|
|
64
|
-
let n = node;
|
|
65
|
-
for(let index = 0;; index++){
|
|
66
|
-
n = n.previousSibling;
|
|
67
|
-
if (!n) return index;
|
|
68
|
-
}
|
|
69
|
-
};
|
|
70
|
-
function nodeSize(node) {
|
|
71
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
72
|
-
return node.nodeType == 3 ? node.nodeValue.length : node.childNodes.length;
|
|
73
|
-
}
|
|
74
|
-
function syncNodeSelection(view, sel) {
|
|
75
|
-
const v = view;
|
|
76
|
-
if (sel instanceof _prosemirrorstate.NodeSelection) {
|
|
77
|
-
const desc = v.docView.descAt(sel.from);
|
|
78
|
-
if (desc != v.lastSelectedViewDesc) {
|
|
79
|
-
clearNodeSelection(v);
|
|
80
|
-
if (desc) desc.selectNode();
|
|
81
|
-
v.lastSelectedViewDesc = desc;
|
|
82
|
-
}
|
|
83
|
-
} else {
|
|
84
|
-
clearNodeSelection(v);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
// Clear all DOM statefulness of the last node selection.
|
|
88
|
-
function clearNodeSelection(view) {
|
|
89
|
-
const v = view;
|
|
90
|
-
if (v.lastSelectedViewDesc) {
|
|
91
|
-
if (v.lastSelectedViewDesc.parent) v.lastSelectedViewDesc.deselectNode();
|
|
92
|
-
v.lastSelectedViewDesc = undefined;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
function hasSelection(view) {
|
|
96
|
-
const v = view;
|
|
97
|
-
const sel = v.domSelectionRange();
|
|
98
|
-
if (!sel.anchorNode) return false;
|
|
99
|
-
try {
|
|
100
|
-
// Firefox will raise 'permission denied' errors when accessing
|
|
101
|
-
// properties of `sel.anchorNode` when it's in a generated CSS
|
|
102
|
-
// element.
|
|
103
|
-
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
|
|
104
|
-
sel.focusNode.nodeType == 3 ? sel.focusNode.parentNode : sel.focusNode));
|
|
105
|
-
} catch (_) {
|
|
106
|
-
return false;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
function editorOwnsSelection(view) {
|
|
110
|
-
return view.editable ? view.hasFocus() : hasSelection(view) && document.activeElement && document.activeElement.contains(view.dom);
|
|
111
|
-
}
|
|
112
|
-
function selectCursorWrapper(view) {
|
|
113
|
-
const v = view;
|
|
114
|
-
const domSel = v.domSelection(), range = document.createRange();
|
|
115
|
-
if (!domSel) return;
|
|
116
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
117
|
-
const node = v.cursorWrapper.dom, img = node.nodeName == "IMG";
|
|
118
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
119
|
-
if (img) range.setStart(node.parentNode, domIndex(node) + 1);
|
|
120
|
-
else range.setStart(node, 0);
|
|
121
|
-
range.collapse(true);
|
|
122
|
-
domSel.removeAllRanges();
|
|
123
|
-
domSel.addRange(range);
|
|
124
|
-
// Kludge to kill 'control selection' in IE11 when selecting an
|
|
125
|
-
// invisible cursor wrapper, since that would result in those weird
|
|
126
|
-
// resize handles and a selection that considers the absolutely
|
|
127
|
-
// positioned wrapper, rather than the root editable node, the
|
|
128
|
-
// focused element.
|
|
129
|
-
if (!img && !v.state.selection.visible && _browser.browser.ie && _browser.browser.ie_version <= 11) {
|
|
130
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
131
|
-
node.disabled = true;
|
|
132
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
133
|
-
node.disabled = false;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
function temporarilyEditableNear(view, pos) {
|
|
137
|
-
const v = view;
|
|
138
|
-
const { node, offset } = v.docView.domFromPos(pos, 0);
|
|
139
|
-
const after = offset < node.childNodes.length ? node.childNodes[offset] : null;
|
|
140
|
-
const before = offset ? node.childNodes[offset - 1] : null;
|
|
141
|
-
if (_browser.browser.safari && after && after.contentEditable == "false") return setEditable(after);
|
|
142
|
-
if ((!after || after.contentEditable == "false") && (!before || before.contentEditable == "false")) {
|
|
143
|
-
if (after) return setEditable(after);
|
|
144
|
-
else if (before) return setEditable(before);
|
|
145
|
-
}
|
|
146
|
-
return;
|
|
147
|
-
}
|
|
148
|
-
function setEditable(element) {
|
|
149
|
-
element.contentEditable = "true";
|
|
150
|
-
if (_browser.browser.safari && element.draggable) {
|
|
151
|
-
element.draggable = false;
|
|
152
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
153
|
-
element.wasDraggable = true;
|
|
154
|
-
}
|
|
155
|
-
return element;
|
|
156
|
-
}
|
|
157
|
-
function resetEditable(element) {
|
|
158
|
-
element.contentEditable = "false";
|
|
159
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
160
|
-
if (element.wasDraggable) {
|
|
161
|
-
element.draggable = true;
|
|
162
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
163
|
-
element.wasDraggable = null;
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
function removeClassOnSelectionChange(view) {
|
|
167
|
-
const v = view;
|
|
168
|
-
const doc = v.dom.ownerDocument;
|
|
169
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
170
|
-
doc.removeEventListener("selectionchange", v.input.hideSelectionGuard);
|
|
171
|
-
const domSel = v.domSelectionRange();
|
|
172
|
-
const node = domSel.anchorNode, offset = domSel.anchorOffset;
|
|
173
|
-
doc.addEventListener("selectionchange", v.input.hideSelectionGuard = ()=>{
|
|
174
|
-
if (domSel.anchorNode != node || domSel.anchorOffset != offset) {
|
|
175
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
176
|
-
doc.removeEventListener("selectionchange", v.input.hideSelectionGuard);
|
|
177
|
-
setTimeout(()=>{
|
|
178
|
-
if (!editorOwnsSelection(v) || v.state.selection.visible) v.dom.classList.remove("ProseMirror-hideselection");
|
|
179
|
-
}, 20);
|
|
180
|
-
}
|
|
181
|
-
});
|
|
182
|
-
}
|
|
183
|
-
const brokenSelectBetweenUneditable = _browser.browser.safari || _browser.browser.chrome && _browser.browser.chrome_version < 63;
|
|
184
|
-
function selectionToDOM(view) {
|
|
185
|
-
let force = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : false;
|
|
186
|
-
const v = view;
|
|
187
|
-
const sel = v.state.selection;
|
|
188
|
-
syncNodeSelection(v, sel);
|
|
189
|
-
if (!editorOwnsSelection(v)) return;
|
|
190
|
-
// The delayed drag selection causes issues with Cell Selections
|
|
191
|
-
// in Safari. And the drag selection delay is to workarond issues
|
|
192
|
-
// which only present in Chrome.
|
|
193
|
-
if (!force && v.input.mouseDown && v.input.mouseDown.allowDefault && _browser.browser.chrome) {
|
|
194
|
-
const domSel = v.domSelectionRange(), curSel = v.domObserver.currentSelection;
|
|
195
|
-
if (domSel.anchorNode && curSel.anchorNode && isEquivalentPosition(domSel.anchorNode, domSel.anchorOffset, curSel.anchorNode, curSel.anchorOffset)) {
|
|
196
|
-
v.input.mouseDown.delayedSelectionSync = true;
|
|
197
|
-
v.domObserver.setCurSelection();
|
|
198
|
-
return;
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
v.domObserver.disconnectSelection();
|
|
202
|
-
if (v.cursorWrapper) {
|
|
203
|
-
selectCursorWrapper(v);
|
|
204
|
-
} else {
|
|
205
|
-
const { anchor, head } = sel;
|
|
206
|
-
let resetEditableFrom;
|
|
207
|
-
let resetEditableTo;
|
|
208
|
-
if (brokenSelectBetweenUneditable && !(sel instanceof _prosemirrorstate.TextSelection)) {
|
|
209
|
-
if (!sel.$from.parent.inlineContent) resetEditableFrom = temporarilyEditableNear(v, sel.from);
|
|
210
|
-
if (!sel.empty && !sel.$from.parent.inlineContent) resetEditableTo = temporarilyEditableNear(v, sel.to);
|
|
211
|
-
}
|
|
212
|
-
v.docView.setSelection(anchor, head, v, force);
|
|
213
|
-
if (brokenSelectBetweenUneditable) {
|
|
214
|
-
if (resetEditableFrom) resetEditable(resetEditableFrom);
|
|
215
|
-
if (resetEditableTo) resetEditable(resetEditableTo);
|
|
216
|
-
}
|
|
217
|
-
if (sel.visible) {
|
|
218
|
-
v.dom.classList.remove("ProseMirror-hideselection");
|
|
219
|
-
} else {
|
|
220
|
-
v.dom.classList.add("ProseMirror-hideselection");
|
|
221
|
-
if ("onselectionchange" in document) removeClassOnSelectionChange(v);
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
v.domObserver.setCurSelection();
|
|
225
|
-
v.domObserver.connectSelection();
|
|
226
|
-
}
|
package/dist/cjs/ssr.js
DELETED
|
@@ -1,85 +0,0 @@
|
|
|
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
|
-
*/ "use strict";
|
|
11
|
-
Object.defineProperty(exports, "__esModule", {
|
|
12
|
-
value: true
|
|
13
|
-
});
|
|
14
|
-
Object.defineProperty(exports, "setSsrStubs", {
|
|
15
|
-
enumerable: true,
|
|
16
|
-
get: function() {
|
|
17
|
-
return setSsrStubs;
|
|
18
|
-
}
|
|
19
|
-
});
|
|
20
|
-
let ClassList = class ClassList {
|
|
21
|
-
add() {}
|
|
22
|
-
remove() {}
|
|
23
|
-
};
|
|
24
|
-
let ElementStub = class ElementStub {
|
|
25
|
-
get parent() {
|
|
26
|
-
return new ElementStub();
|
|
27
|
-
}
|
|
28
|
-
get parentNode() {
|
|
29
|
-
return new ElementStub();
|
|
30
|
-
}
|
|
31
|
-
nodeName = "div";
|
|
32
|
-
appendChild() {
|
|
33
|
-
return new ElementStub();
|
|
34
|
-
}
|
|
35
|
-
setAttribute() {}
|
|
36
|
-
hasAttribute() {
|
|
37
|
-
return false;
|
|
38
|
-
}
|
|
39
|
-
insertBefore() {}
|
|
40
|
-
get classList() {
|
|
41
|
-
return new ClassList();
|
|
42
|
-
}
|
|
43
|
-
get ownerDocument() {
|
|
44
|
-
return new DocumentStub();
|
|
45
|
-
}
|
|
46
|
-
style = {};
|
|
47
|
-
addEventListener() {}
|
|
48
|
-
removeEventListener() {}
|
|
49
|
-
replaceChildren() {}
|
|
50
|
-
};
|
|
51
|
-
let DocumentStub = class DocumentStub {
|
|
52
|
-
createElement() {
|
|
53
|
-
return new ElementStub();
|
|
54
|
-
}
|
|
55
|
-
addEventListener() {}
|
|
56
|
-
removeEventListener() {}
|
|
57
|
-
get documentElement() {
|
|
58
|
-
return new ElementStub();
|
|
59
|
-
}
|
|
60
|
-
};
|
|
61
|
-
function setSsrStubs() {
|
|
62
|
-
const prevWindow = globalThis.window;
|
|
63
|
-
// @ts-expect-error HACK - EditorView checks for window.MutationObserver
|
|
64
|
-
// in its constructor, which breaks SSR. We temporarily set window
|
|
65
|
-
// to an empty object to prevent an error from being thrown, and then
|
|
66
|
-
// clean it up so that other isomorphic code doesn't get confused about
|
|
67
|
-
// whether there's a functioning global window object
|
|
68
|
-
globalThis.window ??= {
|
|
69
|
-
visualViewport: null
|
|
70
|
-
};
|
|
71
|
-
const prevDocument = globalThis.document;
|
|
72
|
-
// @ts-expect-error HACK: This is only used during SSR, and only
|
|
73
|
-
// to prevent outright errors when ProseMirror View attempts to
|
|
74
|
-
// access document properties either on import or when constructing
|
|
75
|
-
// the EditorView.
|
|
76
|
-
globalThis.document ??= new DocumentStub();
|
|
77
|
-
return function cleanupSsrStubs() {
|
|
78
|
-
if (globalThis.window !== prevWindow) {
|
|
79
|
-
globalThis.window = prevWindow;
|
|
80
|
-
}
|
|
81
|
-
if (globalThis.document !== prevDocument) {
|
|
82
|
-
globalThis.document = prevDocument;
|
|
83
|
-
}
|
|
84
|
-
};
|
|
85
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import { EditorContext } from "../contexts/EditorContext.js";
|
|
3
|
-
import { useEditorView } from "../hooks/useEditorView.js";
|
|
4
|
-
import { useNodeViews } from "../hooks/useNodeViews.js";
|
|
5
|
-
export function Editor(param) {
|
|
6
|
-
let { mount , children , ...options } = param;
|
|
7
|
-
const { nodeViews , nodeViewsComponent } = useNodeViews(options.nodeViews);
|
|
8
|
-
const value = useEditorView(mount, {
|
|
9
|
-
...options,
|
|
10
|
-
nodeViews
|
|
11
|
-
});
|
|
12
|
-
return /*#__PURE__*/ React.createElement(EditorContext.Provider, {
|
|
13
|
-
value: value
|
|
14
|
-
}, children, nodeViewsComponent);
|
|
15
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import React, { useState } from "react";
|
|
2
|
-
import { NodeViewsContext } from "../contexts/NodeViewsContext.js";
|
|
3
|
-
import { useEditorEffect } from "../hooks/useEditorEffect.js";
|
|
4
|
-
import { ROOT_NODE_KEY } from "../plugins/react.js";
|
|
5
|
-
export function NodeViews(param) {
|
|
6
|
-
let { portals } = param;
|
|
7
|
-
const rootRegisteredPortals = portals[ROOT_NODE_KEY];
|
|
8
|
-
const [rootPortals, setRootPortals] = useState(rootRegisteredPortals?.map((param)=>{
|
|
9
|
-
let { portal } = param;
|
|
10
|
-
return portal;
|
|
11
|
-
}) ?? []);
|
|
12
|
-
// `getPos` is technically derived from the EditorView
|
|
13
|
-
// state, so it's not safe to call until after the EditorView
|
|
14
|
-
// has been updated
|
|
15
|
-
useEditorEffect(()=>{
|
|
16
|
-
setRootPortals(rootRegisteredPortals?.sort((a, b)=>a.getPos() - b.getPos()).map((param)=>{
|
|
17
|
-
let { portal } = param;
|
|
18
|
-
return portal;
|
|
19
|
-
}) ?? []);
|
|
20
|
-
}, [
|
|
21
|
-
rootRegisteredPortals
|
|
22
|
-
]);
|
|
23
|
-
return /*#__PURE__*/ React.createElement(NodeViewsContext.Provider, {
|
|
24
|
-
value: portals
|
|
25
|
-
}, rootPortals);
|
|
26
|
-
}
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import { act, render, screen } from "@testing-library/react";
|
|
2
|
-
import React, { useLayoutEffect, useState } from "react";
|
|
3
|
-
import { useLayoutGroupEffect } from "../../hooks/useLayoutGroupEffect.js";
|
|
4
|
-
import { LayoutGroup } from "../LayoutGroup.js";
|
|
5
|
-
describe("LayoutGroup", ()=>{
|
|
6
|
-
jest.useFakeTimers("modern");
|
|
7
|
-
it("registers multiple effects and runs them", ()=>{
|
|
8
|
-
function Parent() {
|
|
9
|
-
return /*#__PURE__*/ React.createElement(LayoutGroup, null, /*#__PURE__*/ React.createElement(Child, null));
|
|
10
|
-
}
|
|
11
|
-
function Child() {
|
|
12
|
-
const [double, setDouble] = useState(1);
|
|
13
|
-
useLayoutEffect(()=>{
|
|
14
|
-
if (double === 2) {
|
|
15
|
-
setTimeout(()=>{
|
|
16
|
-
setDouble((d)=>d * 2.5);
|
|
17
|
-
}, 500);
|
|
18
|
-
}
|
|
19
|
-
if (double === 20) {
|
|
20
|
-
setDouble((d)=>d * 2.5);
|
|
21
|
-
}
|
|
22
|
-
}, [
|
|
23
|
-
double
|
|
24
|
-
]);
|
|
25
|
-
useLayoutGroupEffect(()=>{
|
|
26
|
-
const timeout = setTimeout(()=>{
|
|
27
|
-
setDouble((d)=>d * 2);
|
|
28
|
-
}, 1000);
|
|
29
|
-
return ()=>{
|
|
30
|
-
clearTimeout(timeout);
|
|
31
|
-
};
|
|
32
|
-
}, [
|
|
33
|
-
double
|
|
34
|
-
]);
|
|
35
|
-
return /*#__PURE__*/ React.createElement("div", null, /*#__PURE__*/ React.createElement("div", {
|
|
36
|
-
"data-testid": "double"
|
|
37
|
-
}, double));
|
|
38
|
-
}
|
|
39
|
-
// The component mounts ...
|
|
40
|
-
// ... the initial value should be 1
|
|
41
|
-
// ... there should be one timeout scheduled by the deferred effect
|
|
42
|
-
render(/*#__PURE__*/ React.createElement(Parent, null));
|
|
43
|
-
expect(screen.getByTestId("double").innerHTML).toBe("1");
|
|
44
|
-
// This block assert that deferred effects run.
|
|
45
|
-
// --------------------------------------------
|
|
46
|
-
// 1000 milliseconds go by ...
|
|
47
|
-
// ... the timeout set by the deferred effect should run
|
|
48
|
-
// ... the timeout should double the new value to 2
|
|
49
|
-
// ... the immediate effect should set a timeout
|
|
50
|
-
// ... the deferred effect should set a timeout
|
|
51
|
-
act(()=>{
|
|
52
|
-
jest.advanceTimersByTime(1000);
|
|
53
|
-
});
|
|
54
|
-
expect(screen.getByTestId("double").innerHTML).toBe("2");
|
|
55
|
-
// The next three blocks assert that cleanup of deferred effects run.
|
|
56
|
-
// ------------------------------------------------------------------
|
|
57
|
-
// 500 milliseconds go by ...
|
|
58
|
-
// ... the timeout set by the immediate effect should run
|
|
59
|
-
// ... the timeout should set the value to 5
|
|
60
|
-
// ... the old deferred effect should cancel its timeout
|
|
61
|
-
// ... the new deferred effect should set a new timeout
|
|
62
|
-
act(()=>{
|
|
63
|
-
jest.advanceTimersByTime(500);
|
|
64
|
-
});
|
|
65
|
-
expect(screen.getByTestId("double").innerHTML).toBe("5");
|
|
66
|
-
// ... 500 more milliseconds go by ...
|
|
67
|
-
// ... the canceled timeout should not run
|
|
68
|
-
// ... the rescheduled timoeut should not yet run
|
|
69
|
-
act(()=>{
|
|
70
|
-
jest.advanceTimersByTime(500);
|
|
71
|
-
});
|
|
72
|
-
expect(screen.getByTestId("double").innerHTML).toBe("5");
|
|
73
|
-
// ... 500 more milliseconds go by ...
|
|
74
|
-
// ... the rescheduled timeout should run
|
|
75
|
-
// ... the timeout should double the value to 10
|
|
76
|
-
// ... the deferred effect should set a new timeout
|
|
77
|
-
act(()=>{
|
|
78
|
-
jest.advanceTimersByTime(500);
|
|
79
|
-
});
|
|
80
|
-
expect(screen.getByTestId("double").innerHTML).toBe("10");
|
|
81
|
-
// The next block asserts that cancelation of deferred effects works.
|
|
82
|
-
// ------------------------------------------------------------------
|
|
83
|
-
// 1000 milliseconds go by ...
|
|
84
|
-
// ... the timeout set by the deferred effect should run
|
|
85
|
-
// ... the timeout should double the value to 20
|
|
86
|
-
// ... the immediate effect should then set the value to 50
|
|
87
|
-
// ... the deferred effect from the first render should not run
|
|
88
|
-
// ... the deferred effect from the second render should run
|
|
89
|
-
// ... the deferred effect that does run should set a new timeout
|
|
90
|
-
act(()=>{
|
|
91
|
-
jest.advanceTimersByTime(1000);
|
|
92
|
-
});
|
|
93
|
-
// For this assertion, we need to clear a timer from the React scheduler.
|
|
94
|
-
jest.advanceTimersByTime(1);
|
|
95
|
-
expect(screen.getByTestId("double").innerHTML).toBe("50");
|
|
96
|
-
expect(jest.getTimerCount()).toBe(1);
|
|
97
|
-
});
|
|
98
|
-
});
|
|
@@ -1,207 +0,0 @@
|
|
|
1
|
-
import { act, render, screen } from "@testing-library/react";
|
|
2
|
-
import userEvent from "@testing-library/user-event";
|
|
3
|
-
import { Schema } from "prosemirror-model";
|
|
4
|
-
import { EditorState } from "prosemirror-state";
|
|
5
|
-
import React, { useEffect, useState } from "react";
|
|
6
|
-
import { react } from "../../plugins/react.js";
|
|
7
|
-
import { setupProseMirrorView, teardownProseMirrorView } from "../../testing/setupProseMirrorView.js";
|
|
8
|
-
import { ProseMirror } from "../ProseMirror.js";
|
|
9
|
-
// Mock `ReactDOM.flushSync` to call `act` to flush updates from DOM mutations.
|
|
10
|
-
jest.mock("react-dom", ()=>({
|
|
11
|
-
...jest.requireActual("react-dom"),
|
|
12
|
-
flushSync: (fn)=>act(fn)
|
|
13
|
-
}));
|
|
14
|
-
describe("ProseMirror", ()=>{
|
|
15
|
-
beforeAll(()=>{
|
|
16
|
-
setupProseMirrorView();
|
|
17
|
-
});
|
|
18
|
-
it("renders a contenteditable", async ()=>{
|
|
19
|
-
const schema = new Schema({
|
|
20
|
-
nodes: {
|
|
21
|
-
text: {},
|
|
22
|
-
doc: {
|
|
23
|
-
content: "text*"
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
});
|
|
27
|
-
const defaultState = EditorState.create({
|
|
28
|
-
schema
|
|
29
|
-
});
|
|
30
|
-
function TestEditor() {
|
|
31
|
-
const [mount, setMount] = useState(null);
|
|
32
|
-
return /*#__PURE__*/ React.createElement(ProseMirror, {
|
|
33
|
-
mount: mount,
|
|
34
|
-
defaultState: defaultState
|
|
35
|
-
}, /*#__PURE__*/ React.createElement("div", {
|
|
36
|
-
"data-testid": "editor",
|
|
37
|
-
ref: setMount
|
|
38
|
-
}));
|
|
39
|
-
}
|
|
40
|
-
const user = userEvent.setup();
|
|
41
|
-
render(/*#__PURE__*/ React.createElement(TestEditor, null));
|
|
42
|
-
const editor = screen.getByTestId("editor");
|
|
43
|
-
await user.type(editor, "Hello, world!");
|
|
44
|
-
expect(editor.textContent).toBe("Hello, world!");
|
|
45
|
-
});
|
|
46
|
-
it("supports observing transaction dispatch", async ()=>{
|
|
47
|
-
const schema = new Schema({
|
|
48
|
-
nodes: {
|
|
49
|
-
text: {},
|
|
50
|
-
doc: {
|
|
51
|
-
content: "text*"
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
});
|
|
55
|
-
const defaultState = EditorState.create({
|
|
56
|
-
schema
|
|
57
|
-
});
|
|
58
|
-
const dispatchTransaction = jest.fn();
|
|
59
|
-
function TestEditor() {
|
|
60
|
-
const [mount, setMount] = useState(null);
|
|
61
|
-
return /*#__PURE__*/ React.createElement(ProseMirror, {
|
|
62
|
-
mount: mount,
|
|
63
|
-
defaultState: defaultState,
|
|
64
|
-
dispatchTransaction: dispatchTransaction
|
|
65
|
-
}, /*#__PURE__*/ React.createElement("div", {
|
|
66
|
-
"data-testid": "editor",
|
|
67
|
-
ref: setMount
|
|
68
|
-
}));
|
|
69
|
-
}
|
|
70
|
-
const user = userEvent.setup();
|
|
71
|
-
render(/*#__PURE__*/ React.createElement(TestEditor, null));
|
|
72
|
-
const editor = screen.getByTestId("editor");
|
|
73
|
-
await user.type(editor, "Hello, world!");
|
|
74
|
-
expect(editor.textContent).toBe("Hello, world!");
|
|
75
|
-
expect(dispatchTransaction).toHaveBeenCalledTimes(13);
|
|
76
|
-
});
|
|
77
|
-
it("supports controlling the editor state", async ()=>{
|
|
78
|
-
const schema = new Schema({
|
|
79
|
-
nodes: {
|
|
80
|
-
text: {},
|
|
81
|
-
doc: {
|
|
82
|
-
content: "text*"
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
});
|
|
86
|
-
let observedState = EditorState.create({
|
|
87
|
-
schema
|
|
88
|
-
});
|
|
89
|
-
function TestEditor() {
|
|
90
|
-
const [state, setState] = useState(observedState);
|
|
91
|
-
const [mount, setMount] = useState(null);
|
|
92
|
-
useEffect(()=>{
|
|
93
|
-
observedState = state;
|
|
94
|
-
}, [
|
|
95
|
-
state
|
|
96
|
-
]);
|
|
97
|
-
return /*#__PURE__*/ React.createElement(ProseMirror, {
|
|
98
|
-
mount: mount,
|
|
99
|
-
state: state,
|
|
100
|
-
dispatchTransaction: (tr)=>{
|
|
101
|
-
setState((s)=>s.apply(tr));
|
|
102
|
-
}
|
|
103
|
-
}, /*#__PURE__*/ React.createElement("div", {
|
|
104
|
-
"data-testid": "editor",
|
|
105
|
-
ref: setMount
|
|
106
|
-
}));
|
|
107
|
-
}
|
|
108
|
-
const user = userEvent.setup();
|
|
109
|
-
render(/*#__PURE__*/ React.createElement(TestEditor, null));
|
|
110
|
-
const editor = screen.getByTestId("editor");
|
|
111
|
-
await user.type(editor, "Hello, world!");
|
|
112
|
-
expect(observedState.doc.textContent).toBe("Hello, world!");
|
|
113
|
-
});
|
|
114
|
-
it("updates props atomically", async ()=>{
|
|
115
|
-
const schema = new Schema({
|
|
116
|
-
nodes: {
|
|
117
|
-
text: {},
|
|
118
|
-
doc: {
|
|
119
|
-
content: "text*"
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
});
|
|
123
|
-
const defaultState = EditorState.create({
|
|
124
|
-
schema
|
|
125
|
-
});
|
|
126
|
-
let allStatesMatched = true;
|
|
127
|
-
function TestEditor() {
|
|
128
|
-
const [state, setState] = useState(defaultState);
|
|
129
|
-
const [mount, setMount] = useState(null);
|
|
130
|
-
// Check that function props get invoked with the latest React state.
|
|
131
|
-
const editable = (viewState)=>{
|
|
132
|
-
allStatesMatched &&= viewState === state;
|
|
133
|
-
return true;
|
|
134
|
-
};
|
|
135
|
-
return /*#__PURE__*/ React.createElement(ProseMirror, {
|
|
136
|
-
mount: mount,
|
|
137
|
-
editable: editable,
|
|
138
|
-
state: state,
|
|
139
|
-
dispatchTransaction: (tr)=>{
|
|
140
|
-
setState((s)=>s.apply(tr));
|
|
141
|
-
}
|
|
142
|
-
}, /*#__PURE__*/ React.createElement("div", {
|
|
143
|
-
"data-testid": "editor",
|
|
144
|
-
ref: setMount
|
|
145
|
-
}));
|
|
146
|
-
}
|
|
147
|
-
const user = userEvent.setup();
|
|
148
|
-
render(/*#__PURE__*/ React.createElement(TestEditor, null));
|
|
149
|
-
const editor = screen.getByTestId("editor");
|
|
150
|
-
await user.type(editor, "Hello, world!");
|
|
151
|
-
expect(allStatesMatched).toBe(true);
|
|
152
|
-
});
|
|
153
|
-
it("supports React NodeViews", async ()=>{
|
|
154
|
-
const schema = new Schema({
|
|
155
|
-
nodes: {
|
|
156
|
-
text: {},
|
|
157
|
-
paragraph: {
|
|
158
|
-
content: "text*"
|
|
159
|
-
},
|
|
160
|
-
doc: {
|
|
161
|
-
content: "paragraph+"
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
});
|
|
165
|
-
const defaultState = EditorState.create({
|
|
166
|
-
schema,
|
|
167
|
-
plugins: [
|
|
168
|
-
react()
|
|
169
|
-
]
|
|
170
|
-
});
|
|
171
|
-
function Paragraph(param) {
|
|
172
|
-
let { children } = param;
|
|
173
|
-
return /*#__PURE__*/ React.createElement("p", {
|
|
174
|
-
"data-testid": "paragraph"
|
|
175
|
-
}, children);
|
|
176
|
-
}
|
|
177
|
-
const nodeViews = {
|
|
178
|
-
paragraph: ()=>({
|
|
179
|
-
component: Paragraph,
|
|
180
|
-
dom: document.createElement("div"),
|
|
181
|
-
contentDOM: document.createElement("span")
|
|
182
|
-
})
|
|
183
|
-
};
|
|
184
|
-
function TestEditor() {
|
|
185
|
-
const [mount, setMount] = useState(null);
|
|
186
|
-
return /*#__PURE__*/ React.createElement(ProseMirror, {
|
|
187
|
-
mount: mount,
|
|
188
|
-
defaultState: defaultState,
|
|
189
|
-
nodeViews: nodeViews
|
|
190
|
-
}, /*#__PURE__*/ React.createElement("div", {
|
|
191
|
-
"data-testid": "editor",
|
|
192
|
-
ref: setMount
|
|
193
|
-
}));
|
|
194
|
-
}
|
|
195
|
-
const user = userEvent.setup();
|
|
196
|
-
render(/*#__PURE__*/ React.createElement(TestEditor, null));
|
|
197
|
-
const editor = screen.getByTestId("editor");
|
|
198
|
-
await user.type(editor, "Hello, world!");
|
|
199
|
-
expect(editor.textContent).toBe("Hello, world!");
|
|
200
|
-
// Ensure that ProseMirror really rendered our Paragraph
|
|
201
|
-
// component, not just any old <p> tag
|
|
202
|
-
expect(screen.getAllByTestId("paragraph").length).toBeGreaterThanOrEqual(1);
|
|
203
|
-
});
|
|
204
|
-
afterAll(()=>{
|
|
205
|
-
teardownProseMirrorView();
|
|
206
|
-
});
|
|
207
|
-
});
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { createContext } from "react";
|
|
2
|
-
/**
|
|
3
|
-
* A context containing a map of node view keys to portals.
|
|
4
|
-
*
|
|
5
|
-
* Each node view registers a portal under its parent's
|
|
6
|
-
* key. Each can then retrieve the list of portals under their
|
|
7
|
-
* key, allowing portals to be rendered with the appropriate
|
|
8
|
-
* hierarchy.
|
|
9
|
-
*/ export const NodeViewsContext = createContext(null);
|