@handlewithcare/react-prosemirror 2.5.0 → 2.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/ReactEditorView.js +6 -6
- package/dist/cjs/components/ChildNodeViews.js +6 -6
- package/dist/cjs/components/CustomNodeView.js +77 -121
- package/dist/cjs/components/DefaultNodeView.js +67 -0
- package/dist/cjs/components/DocNodeView.js +33 -32
- package/dist/cjs/components/NodeView.js +31 -21
- package/dist/cjs/components/ProseMirror.js +20 -9
- package/dist/cjs/components/ProseMirrorDoc.js +7 -27
- package/dist/cjs/components/ReactNodeView.js +96 -58
- package/dist/cjs/hooks/useEditor.js +1 -1
- package/dist/cjs/hooks/useIgnoreMutation.js +1 -1
- package/dist/cjs/hooks/useNodeViewDescriptor.js +121 -78
- package/dist/cjs/hooks/useSelectNode.js +9 -7
- package/dist/cjs/hooks/useStopEvent.js +1 -1
- package/dist/cjs/plugins/beforeInputPlugin.js +12 -0
- package/dist/cjs/viewdesc.js +95 -17
- package/dist/esm/ReactEditorView.js +6 -6
- package/dist/esm/components/ChildNodeViews.js +6 -6
- package/dist/esm/components/CustomNodeView.js +78 -122
- package/dist/esm/components/DefaultNodeView.js +16 -0
- package/dist/esm/components/DocNodeView.js +33 -32
- package/dist/esm/components/NodeView.js +32 -22
- package/dist/esm/components/ProseMirror.js +20 -9
- package/dist/esm/components/ProseMirrorDoc.js +7 -28
- package/dist/esm/components/ReactNodeView.js +97 -59
- package/dist/esm/hooks/useEditor.js +1 -1
- package/dist/esm/hooks/useIgnoreMutation.js +1 -1
- package/dist/esm/hooks/useNodeViewDescriptor.js +123 -80
- package/dist/esm/hooks/useSelectNode.js +9 -7
- package/dist/esm/hooks/useStopEvent.js +1 -1
- package/dist/esm/plugins/beforeInputPlugin.js +12 -0
- package/dist/esm/viewdesc.js +92 -17
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/ReactEditorView.d.ts +3 -2
- package/dist/types/components/CustomNodeView.d.ts +1 -1
- package/dist/types/components/DefaultNodeView.d.ts +3 -0
- package/dist/types/components/DocNodeView.d.ts +8 -13
- package/dist/types/components/NodeView.d.ts +4 -4
- package/dist/types/components/NodeViewComponentProps.d.ts +3 -4
- package/dist/types/components/ProseMirrorDoc.d.ts +14 -8
- package/dist/types/components/ReactNodeView.d.ts +3 -1
- package/dist/types/contexts/IgnoreMutationContext.d.ts +2 -1
- package/dist/types/contexts/NodeViewContext.d.ts +3 -1
- package/dist/types/contexts/SelectNodeContext.d.ts +3 -1
- package/dist/types/contexts/StopEventContext.d.ts +2 -1
- package/dist/types/hooks/useEditor.d.ts +1 -1
- package/dist/types/hooks/useNodeViewDescriptor.d.ts +18 -10
- package/dist/types/hooks/useSelectNode.d.ts +2 -1
- package/dist/types/viewdesc.d.ts +25 -8
- package/package.json +3 -3
- package/dist/cjs/hooks/useClientOnly.js +0 -19
- package/dist/esm/hooks/useClientOnly.js +0 -9
- package/dist/types/hooks/useClientOnly.d.ts +0 -1
|
@@ -1,30 +1,45 @@
|
|
|
1
|
-
import React, { cloneElement, memo,
|
|
1
|
+
import React, { cloneElement, memo, useCallback, useMemo, useRef, useState } from "react";
|
|
2
2
|
import { ChildDescriptorsContext } from "../contexts/ChildDescriptorsContext.js";
|
|
3
3
|
import { IgnoreMutationContext } from "../contexts/IgnoreMutationContext.js";
|
|
4
|
-
import { NodeViewContext } from "../contexts/NodeViewContext.js";
|
|
5
4
|
import { SelectNodeContext } from "../contexts/SelectNodeContext.js";
|
|
6
5
|
import { StopEventContext } from "../contexts/StopEventContext.js";
|
|
7
6
|
import { useNodeViewDescriptor } from "../hooks/useNodeViewDescriptor.js";
|
|
8
7
|
import { ChildNodeViews, wrapInDeco } from "./ChildNodeViews.js";
|
|
9
|
-
import { OutputSpec } from "./OutputSpec.js";
|
|
10
8
|
export const ReactNodeView = /*#__PURE__*/ memo(function ReactNodeView(param) {
|
|
11
|
-
let { outerDeco, getPos, node, innerDeco
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
9
|
+
let { component: Component, outerDeco, getPos, node, innerDeco } = param;
|
|
10
|
+
const [controlSelected, setControlSelected] = useState(false);
|
|
11
|
+
const [selected, setSelected] = useState(false);
|
|
12
|
+
const ref = useRef(null);
|
|
13
|
+
const innerRef = useRef(null);
|
|
14
|
+
const selectNodeRef = useRef(null);
|
|
15
|
+
const deselectNodeRef = useRef(null);
|
|
16
|
+
const stopEventRef = useRef(null);
|
|
17
|
+
const ignoreMutationRef = useRef(null);
|
|
18
|
+
const setSelectNode = useCallback((selectHandler, deselectHandler)=>{
|
|
19
|
+
selectNodeRef.current = selectHandler;
|
|
20
|
+
deselectNodeRef.current = deselectHandler;
|
|
21
|
+
setControlSelected(true);
|
|
22
|
+
return ()=>{
|
|
23
|
+
selectNodeRef.current = null;
|
|
24
|
+
deselectNodeRef.current = null;
|
|
25
|
+
setControlSelected(false);
|
|
26
|
+
};
|
|
27
|
+
}, []);
|
|
28
|
+
const setStopEvent = useCallback((handler)=>{
|
|
29
|
+
stopEventRef.current = handler;
|
|
30
|
+
return ()=>{
|
|
31
|
+
stopEventRef.current = null;
|
|
32
|
+
};
|
|
33
|
+
}, []);
|
|
34
|
+
const setIgnoreMutation = useCallback((handler)=>{
|
|
35
|
+
ignoreMutationRef.current = handler;
|
|
36
|
+
return ()=>{
|
|
37
|
+
ignoreMutationRef.current = null;
|
|
38
|
+
return ()=>{
|
|
39
|
+
ignoreMutationRef.current = null;
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
}, []);
|
|
28
43
|
const nodeProps = useMemo(()=>({
|
|
29
44
|
node: node,
|
|
30
45
|
getPos: getPos,
|
|
@@ -36,45 +51,68 @@ export const ReactNodeView = /*#__PURE__*/ memo(function ReactNodeView(param) {
|
|
|
36
51
|
node,
|
|
37
52
|
outerDeco
|
|
38
53
|
]);
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
54
|
+
const { childContextValue, contentDOM, nodeDOM } = useNodeViewDescriptor(ref, ()=>{
|
|
55
|
+
setSelected(false);
|
|
56
|
+
return {
|
|
57
|
+
dom: innerRef.current ?? ref.current,
|
|
58
|
+
update () {
|
|
59
|
+
return true;
|
|
60
|
+
},
|
|
61
|
+
multiType: true,
|
|
62
|
+
selectNode () {
|
|
63
|
+
const selectNode = selectNodeRef.current;
|
|
64
|
+
if (selectNode) {
|
|
65
|
+
selectNode();
|
|
66
|
+
}
|
|
67
|
+
setSelected(true);
|
|
68
|
+
},
|
|
69
|
+
deselectNode () {
|
|
70
|
+
const deselectNode = deselectNodeRef.current;
|
|
71
|
+
if (deselectNode) {
|
|
72
|
+
deselectNode();
|
|
73
|
+
}
|
|
74
|
+
setSelected(false);
|
|
75
|
+
},
|
|
76
|
+
stopEvent (event) {
|
|
77
|
+
const stopEvent = stopEventRef.current;
|
|
78
|
+
if (stopEvent) {
|
|
79
|
+
return stopEvent(event);
|
|
80
|
+
}
|
|
81
|
+
return false;
|
|
82
|
+
},
|
|
83
|
+
ignoreMutation (mutation) {
|
|
84
|
+
const ignoreMutation = ignoreMutationRef.current;
|
|
85
|
+
if (ignoreMutation) {
|
|
86
|
+
return ignoreMutation(mutation);
|
|
87
|
+
}
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
}, nodeProps);
|
|
92
|
+
const children = !node.isLeaf ? /*#__PURE__*/ React.createElement(ChildNodeViews, {
|
|
93
|
+
getPos: getPos,
|
|
94
|
+
node: node,
|
|
95
|
+
innerDecorations: innerDeco
|
|
96
|
+
}) : null;
|
|
97
|
+
const innerProps = {
|
|
98
|
+
nodeProps,
|
|
99
|
+
...!contentDOM && !nodeProps.node.isText && nodeDOM?.nodeName !== "BR" ? {
|
|
100
|
+
contentEditable: false,
|
|
101
|
+
suppressContentEditableWarning: true
|
|
102
|
+
} : null,
|
|
103
|
+
...controlSelected && selected ? {
|
|
104
|
+
className: "ProseMirror-selectednode"
|
|
105
|
+
} : null,
|
|
106
|
+
ref: innerRef
|
|
107
|
+
};
|
|
108
|
+
const innerElement = /*#__PURE__*/ React.createElement(Component, innerProps, children);
|
|
109
|
+
const props = {
|
|
110
|
+
...controlSelected && selected || node.type.spec.draggable ? {
|
|
111
|
+
draggable: true
|
|
112
|
+
} : null,
|
|
113
|
+
ref
|
|
114
|
+
};
|
|
115
|
+
const decoratedElement = /*#__PURE__*/ cloneElement(outerDeco.reduce(wrapInDeco, innerElement), props);
|
|
78
116
|
return /*#__PURE__*/ React.createElement(SelectNodeContext.Provider, {
|
|
79
117
|
value: setSelectNode
|
|
80
118
|
}, /*#__PURE__*/ React.createElement(StopEventContext.Provider, {
|
|
@@ -19,7 +19,7 @@ let didWarnValueDefaultValue = false;
|
|
|
19
19
|
*/ export function useEditor(mount, options) {
|
|
20
20
|
if (process.env.NODE_ENV !== "production") {
|
|
21
21
|
if (options.defaultState !== undefined && options.state !== undefined && !didWarnValueDefaultValue) {
|
|
22
|
-
console.error("A component contains a ProseMirror editor with both
|
|
22
|
+
console.error("A component contains a ProseMirror editor with both state and defaultState props. " + "ProseMirror editors must be either controlled or uncontrolled " + "(specify either the state prop, or the defaultState prop, but not both). " + "Decide between using a controlled or uncontrolled ProseMirror editor " + "and remove one of these props. More info: " + "https://reactjs.org/link/controlled-components");
|
|
23
23
|
didWarnValueDefaultValue = true;
|
|
24
24
|
}
|
|
25
25
|
}
|
|
@@ -6,7 +6,7 @@ export function useIgnoreMutation(ignoreMutation) {
|
|
|
6
6
|
const register = useContext(IgnoreMutationContext);
|
|
7
7
|
const ignoreMutationMemo = useEditorEventCallback(ignoreMutation);
|
|
8
8
|
useEditorEffect(()=>{
|
|
9
|
-
register(ignoreMutationMemo);
|
|
9
|
+
return register(ignoreMutationMemo);
|
|
10
10
|
}, [
|
|
11
11
|
register,
|
|
12
12
|
ignoreMutationMemo
|
|
@@ -1,109 +1,152 @@
|
|
|
1
|
-
import { useCallback, useContext, useRef, useState } from "react";
|
|
1
|
+
import { useCallback, useContext, useMemo, useRef, useState } from "react";
|
|
2
|
+
import { ReactEditorView } from "../ReactEditorView.js";
|
|
2
3
|
import { ChildDescriptorsContext } from "../contexts/ChildDescriptorsContext.js";
|
|
3
4
|
import { EditorContext } from "../contexts/EditorContext.js";
|
|
4
|
-
import { CompositionViewDesc,
|
|
5
|
+
import { CompositionViewDesc, ReactNodeViewDesc, sortViewDescs } from "../viewdesc.js";
|
|
5
6
|
import { useClientLayoutEffect } from "./useClientLayoutEffect.js";
|
|
6
|
-
|
|
7
|
+
function findContentDOM(source, children) {
|
|
8
|
+
return source?.contentDOM ?? children[0]?.dom?.parentElement ?? null;
|
|
9
|
+
}
|
|
10
|
+
export function useNodeViewDescriptor(ref, constructor, props) {
|
|
7
11
|
const { view } = useContext(EditorContext);
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}, []);
|
|
18
|
-
const selectNode = useRef(()=>{
|
|
19
|
-
if (!nodeDomRef.current) return;
|
|
20
|
-
if (nodeDomRef.current.nodeType == 1) nodeDomRef.current.classList.add("ProseMirror-selectednode");
|
|
21
|
-
if (contentDOMRef?.current || !node.type.spec.draggable) (domRef?.current ?? nodeDomRef.current).draggable = true;
|
|
22
|
-
});
|
|
23
|
-
const deselectNode = useRef(()=>{
|
|
24
|
-
if (!nodeDomRef.current) return;
|
|
25
|
-
if (nodeDomRef.current.nodeType == 1) {
|
|
26
|
-
nodeDomRef.current.classList.remove("ProseMirror-selectednode");
|
|
27
|
-
if (contentDOMRef?.current || !node.type.spec.draggable) (domRef?.current ?? nodeDomRef.current).removeAttribute("draggable");
|
|
12
|
+
const { parentRef, siblingsRef } = useContext(ChildDescriptorsContext);
|
|
13
|
+
const [dom, setDOM] = useState(null);
|
|
14
|
+
const [nodeDOM, setNodeDOM] = useState(null);
|
|
15
|
+
const [contentDOM, setContentDOM] = useState(null);
|
|
16
|
+
const viewDescRef = useRef();
|
|
17
|
+
const childrenRef = useRef([]);
|
|
18
|
+
const create = useCallback((props)=>{
|
|
19
|
+
if (!(view instanceof ReactEditorView)) {
|
|
20
|
+
return;
|
|
28
21
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
22
|
+
const dom = ref.current;
|
|
23
|
+
if (!dom) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const { node, getPos, decorations, innerDecorations } = props;
|
|
27
|
+
const nodeView = constructor(node, view, getPos, decorations, innerDecorations);
|
|
28
|
+
if (!nodeView) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const parent = parentRef.current;
|
|
32
|
+
const children = childrenRef.current;
|
|
33
|
+
const contentDOM = findContentDOM(nodeView, children);
|
|
34
|
+
const nodeDOM = nodeView.dom;
|
|
35
|
+
const viewDesc = new ReactNodeViewDesc(parent, children, getPos, node, decorations, innerDecorations, dom, contentDOM, nodeDOM, nodeView);
|
|
36
|
+
setDOM(dom);
|
|
37
|
+
setContentDOM(contentDOM);
|
|
38
|
+
setNodeDOM(nodeDOM);
|
|
39
|
+
return viewDesc;
|
|
40
|
+
}, [
|
|
41
|
+
ref,
|
|
42
|
+
parentRef,
|
|
43
|
+
constructor,
|
|
44
|
+
view
|
|
45
|
+
]);
|
|
46
|
+
const update = useCallback((props)=>{
|
|
47
|
+
if (!(view instanceof ReactEditorView)) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
const viewDesc = viewDescRef.current;
|
|
51
|
+
if (!viewDesc) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
const dom = ref.current;
|
|
55
|
+
if (!dom || dom !== viewDesc.dom) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
const contentDOM = findContentDOM(viewDesc, viewDesc.children);
|
|
59
|
+
if (contentDOM !== viewDesc.contentDOM) {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
if (!dom.contains(viewDesc.nodeDOM)) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
const { node, decorations, innerDecorations } = props;
|
|
66
|
+
return viewDesc.matchesNode(node, decorations, innerDecorations) || viewDesc.update(node, decorations, innerDecorations, view);
|
|
67
|
+
}, [
|
|
68
|
+
ref,
|
|
69
|
+
view
|
|
70
|
+
]);
|
|
71
|
+
const destroy = useCallback(()=>{
|
|
72
|
+
const viewDesc = viewDescRef.current;
|
|
73
|
+
if (!viewDesc) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
viewDesc.destroy();
|
|
37
77
|
const siblings = siblingsRef.current;
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
78
|
+
if (siblings.includes(viewDesc)) {
|
|
79
|
+
const index = siblings.indexOf(viewDesc);
|
|
80
|
+
siblings.splice(index, 1);
|
|
81
|
+
}
|
|
82
|
+
setDOM(null);
|
|
83
|
+
setContentDOM(null);
|
|
84
|
+
setNodeDOM(null);
|
|
45
85
|
}, [
|
|
46
86
|
siblingsRef
|
|
47
87
|
]);
|
|
48
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
49
88
|
useClientLayoutEffect(()=>{
|
|
50
|
-
if (!
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
nodeViewDescRef.current.node = node;
|
|
58
|
-
nodeViewDescRef.current.outerDeco = outerDecorations;
|
|
59
|
-
nodeViewDescRef.current.innerDeco = innerDecorations;
|
|
60
|
-
nodeViewDescRef.current.dom = domRef?.current ?? nodeDomRef.current;
|
|
61
|
-
nodeViewDescRef.current.dom.pmViewDesc = nodeViewDescRef.current;
|
|
62
|
-
nodeViewDescRef.current.contentDOM = // If there's already a contentDOM, we can just
|
|
63
|
-
// keep it; it won't have changed. This is especially
|
|
64
|
-
// important during compositions, where the
|
|
65
|
-
// firstChildDesc might not have a correct dom node set yet.
|
|
66
|
-
contentDOMRef?.current ?? nodeViewDescRef.current.contentDOM ?? firstChildDesc?.dom.parentElement ?? null;
|
|
67
|
-
nodeViewDescRef.current.nodeDOM = nodeDomRef.current;
|
|
89
|
+
if (!update(props)) {
|
|
90
|
+
destroy();
|
|
91
|
+
viewDescRef.current = create(props);
|
|
92
|
+
}
|
|
93
|
+
const viewDesc = viewDescRef.current;
|
|
94
|
+
if (!viewDesc) {
|
|
95
|
+
return;
|
|
68
96
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
97
|
+
if (view.dom === viewDesc.dom && view instanceof ReactEditorView) {
|
|
98
|
+
view.docView = viewDesc;
|
|
99
|
+
}
|
|
100
|
+
const parent = parentRef.current;
|
|
101
|
+
const siblings = siblingsRef.current;
|
|
102
|
+
const children = childrenRef.current;
|
|
103
|
+
viewDesc.parent = parent;
|
|
104
|
+
if (!siblings.includes(viewDesc)) {
|
|
105
|
+
siblings.push(viewDesc);
|
|
72
106
|
}
|
|
73
|
-
|
|
74
|
-
for (const
|
|
75
|
-
|
|
107
|
+
siblings.sort(sortViewDescs);
|
|
108
|
+
for (const child of children){
|
|
109
|
+
child.parent = viewDesc;
|
|
76
110
|
// Because TextNodeViews can't locate the DOM nodes
|
|
77
111
|
// for compositions, we need to override them here
|
|
78
|
-
if (
|
|
79
|
-
const compositionTopDOM =
|
|
112
|
+
if (child instanceof CompositionViewDesc) {
|
|
113
|
+
const compositionTopDOM = viewDesc?.contentDOM?.firstChild;
|
|
80
114
|
if (!compositionTopDOM) throw new Error(`Started a composition but couldn't find the text node it belongs to.`);
|
|
81
115
|
let textDOM = compositionTopDOM;
|
|
82
116
|
while(textDOM.firstChild){
|
|
83
117
|
textDOM = textDOM.firstChild;
|
|
84
118
|
}
|
|
85
119
|
if (!textDOM || !(textDOM instanceof Text)) throw new Error(`Started a composition but couldn't find the text node it belongs to.`);
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
120
|
+
child.dom = compositionTopDOM;
|
|
121
|
+
child.textDOM = textDOM;
|
|
122
|
+
child.text = textDOM.data;
|
|
123
|
+
child.textDOM.pmViewDesc = child;
|
|
90
124
|
// It should not be possible to be in a composition because one could
|
|
91
125
|
// not start between the renders that switch the view type.
|
|
92
|
-
view.input.compositionNodes.push(
|
|
126
|
+
view.input.compositionNodes.push(child);
|
|
93
127
|
}
|
|
94
128
|
}
|
|
129
|
+
});
|
|
130
|
+
useClientLayoutEffect(()=>{
|
|
95
131
|
return ()=>{
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
132
|
+
destroy();
|
|
133
|
+
viewDescRef.current = undefined;
|
|
99
134
|
};
|
|
100
|
-
}
|
|
135
|
+
}, [
|
|
136
|
+
destroy
|
|
137
|
+
]);
|
|
138
|
+
const childContextValue = useMemo(()=>({
|
|
139
|
+
parentRef: viewDescRef,
|
|
140
|
+
siblingsRef: childrenRef
|
|
141
|
+
}), [
|
|
142
|
+
childrenRef,
|
|
143
|
+
viewDescRef
|
|
144
|
+
]);
|
|
101
145
|
return {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
setIgnoreMutation
|
|
146
|
+
childContextValue,
|
|
147
|
+
dom,
|
|
148
|
+
contentDOM,
|
|
149
|
+
nodeDOM,
|
|
150
|
+
ref
|
|
108
151
|
};
|
|
109
152
|
}
|
|
@@ -2,17 +2,19 @@ import { useContext } from "react";
|
|
|
2
2
|
import { SelectNodeContext } from "../contexts/SelectNodeContext.js";
|
|
3
3
|
import { useEditorEffect } from "./useEditorEffect.js";
|
|
4
4
|
import { useEditorEventCallback } from "./useEditorEventCallback.js";
|
|
5
|
-
|
|
5
|
+
function noop() {
|
|
6
|
+
// empty
|
|
7
|
+
}
|
|
8
|
+
export function useSelectNode(selectNode) {
|
|
9
|
+
let deselectNode = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : noop;
|
|
6
10
|
const register = useContext(SelectNodeContext);
|
|
7
11
|
const selectNodeMemo = useEditorEventCallback(selectNode);
|
|
8
|
-
const deselectNodeMemo = useEditorEventCallback(deselectNode
|
|
9
|
-
// empty
|
|
10
|
-
}));
|
|
12
|
+
const deselectNodeMemo = useEditorEventCallback(deselectNode);
|
|
11
13
|
return useEditorEffect(()=>{
|
|
12
|
-
register(selectNodeMemo, deselectNodeMemo);
|
|
14
|
+
return register(selectNodeMemo, deselectNodeMemo);
|
|
13
15
|
}, [
|
|
14
|
-
deselectNodeMemo,
|
|
15
16
|
register,
|
|
16
|
-
selectNodeMemo
|
|
17
|
+
selectNodeMemo,
|
|
18
|
+
deselectNodeMemo
|
|
17
19
|
]);
|
|
18
20
|
}
|
|
@@ -6,7 +6,7 @@ export function useStopEvent(stopEvent) {
|
|
|
6
6
|
const register = useContext(StopEventContext);
|
|
7
7
|
const stopEventMemo = useEditorEventCallback(stopEvent);
|
|
8
8
|
useEditorEffect(()=>{
|
|
9
|
-
register(stopEventMemo);
|
|
9
|
+
return register(stopEventMemo);
|
|
10
10
|
}, [
|
|
11
11
|
register,
|
|
12
12
|
stopEventMemo
|
|
@@ -86,6 +86,14 @@ export function beforeInputPlugin(setCursorWrapper) {
|
|
|
86
86
|
case "insertParagraph":
|
|
87
87
|
case "insertLineBreak":
|
|
88
88
|
{
|
|
89
|
+
// ProseMirror-view has a hack that runs the Enter event handlers
|
|
90
|
+
// on iOS, to avoid a bug in Safari with calling event.preventDefault() on
|
|
91
|
+
// Enter events.
|
|
92
|
+
//
|
|
93
|
+
// We want to prevent that hack, because we run the Enter event handlers
|
|
94
|
+
// here, where there is no such bug. So we set this flag, which prosemirror-view
|
|
95
|
+
// uses to check whether it should run the deferred event handlers.
|
|
96
|
+
view.input.lastIOSEnter = 0;
|
|
89
97
|
// Fire a synthetic keydown event to trigger ProseMirror's keymap
|
|
90
98
|
const keyEvent = new KeyboardEvent("keydown", {
|
|
91
99
|
bubbles: true,
|
|
@@ -119,8 +127,12 @@ export function beforeInputPlugin(setCursorWrapper) {
|
|
|
119
127
|
break;
|
|
120
128
|
}
|
|
121
129
|
case "deleteWordBackward":
|
|
130
|
+
case "deleteHardLineBackward":
|
|
131
|
+
case "deleteSoftLineBackward":
|
|
122
132
|
case "deleteContentBackward":
|
|
123
133
|
case "deleteWordForward":
|
|
134
|
+
case "deleteHardLineForward":
|
|
135
|
+
case "deleteSoftLineForward":
|
|
124
136
|
case "deleteContentForward":
|
|
125
137
|
case "deleteContent":
|
|
126
138
|
{
|
package/dist/esm/viewdesc.js
CHANGED
|
@@ -82,7 +82,9 @@ export class ViewDesc {
|
|
|
82
82
|
return 0;
|
|
83
83
|
}
|
|
84
84
|
destroy() {
|
|
85
|
-
|
|
85
|
+
this.parent = undefined;
|
|
86
|
+
if (this.dom.pmViewDesc == this) this.dom.pmViewDesc = undefined;
|
|
87
|
+
for(let i = 0; i < this.children.length; i++)this.children[i].destroy();
|
|
86
88
|
}
|
|
87
89
|
posBeforeChild(child) {
|
|
88
90
|
for(let i = 0, pos = this.posAtStart;; i++){
|
|
@@ -541,15 +543,8 @@ export class NodeViewDesc extends ViewDesc {
|
|
|
541
543
|
outerDeco;
|
|
542
544
|
innerDeco;
|
|
543
545
|
nodeDOM;
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
deselectNode;
|
|
547
|
-
ignoreMutation;
|
|
548
|
-
constructor(parent, children, getPos, node, outerDeco, innerDeco, dom, contentDOM, nodeDOM, stopEvent, selectNode, deselectNode, ignoreMutation){
|
|
549
|
-
super(parent, children, getPos, dom, contentDOM), this.node = node, this.outerDeco = outerDeco, this.innerDeco = innerDeco, this.nodeDOM = nodeDOM, this.stopEvent = stopEvent, this.selectNode = selectNode, this.deselectNode = deselectNode, this.ignoreMutation = ignoreMutation;
|
|
550
|
-
}
|
|
551
|
-
updateOuterDeco() {
|
|
552
|
-
// pass
|
|
546
|
+
constructor(parent, children, getPos, node, outerDeco, innerDeco, dom, contentDOM, nodeDOM){
|
|
547
|
+
super(parent, children, getPos, dom, contentDOM), this.node = node, this.outerDeco = outerDeco, this.innerDeco = innerDeco, this.nodeDOM = nodeDOM;
|
|
553
548
|
}
|
|
554
549
|
parseRule() {
|
|
555
550
|
// Experimental kludge to allow opt-in re-parsing of nodes
|
|
@@ -593,23 +588,46 @@ export class NodeViewDesc extends ViewDesc {
|
|
|
593
588
|
get border() {
|
|
594
589
|
return this.node.isLeaf ? 0 : 1;
|
|
595
590
|
}
|
|
591
|
+
updateChildren(_view, _pos) {
|
|
592
|
+
// Omitted to avoid reproducing lots of unused code.
|
|
593
|
+
// Overidden elsewhere in case this is ever vendored.
|
|
594
|
+
}
|
|
596
595
|
// If this desc must be updated to match the given node decoration,
|
|
597
596
|
// do so and return true.
|
|
598
|
-
update(
|
|
599
|
-
this.dirty
|
|
597
|
+
update(node, outerDeco, innerDeco, view) {
|
|
598
|
+
if (this.dirty == NODE_DIRTY || !node.sameMarkup(this.node)) return false;
|
|
599
|
+
this.updateInner(node, outerDeco, innerDeco, view);
|
|
600
600
|
return true;
|
|
601
601
|
}
|
|
602
|
+
updateInner(node, outerDeco, innerDeco, view) {
|
|
603
|
+
this.updateOuterDeco(outerDeco);
|
|
604
|
+
this.node = node;
|
|
605
|
+
this.innerDeco = innerDeco;
|
|
606
|
+
if (this.contentDOM) this.updateChildren(view, this.posAtStart);
|
|
607
|
+
this.dirty = NOT_DIRTY;
|
|
608
|
+
}
|
|
609
|
+
updateOuterDeco(outerDeco) {
|
|
610
|
+
this.outerDeco = outerDeco;
|
|
611
|
+
}
|
|
612
|
+
// Mark this node as being the selected node.
|
|
613
|
+
selectNode() {
|
|
614
|
+
if (this.nodeDOM.nodeType == 1) this.nodeDOM.classList.add("ProseMirror-selectednode");
|
|
615
|
+
if (this.contentDOM || !this.node.type.spec.draggable) this.dom.draggable = true;
|
|
616
|
+
}
|
|
617
|
+
// Remove selected node marking from this node.
|
|
618
|
+
deselectNode() {
|
|
619
|
+
if (this.nodeDOM.nodeType == 1) {
|
|
620
|
+
this.nodeDOM.classList.remove("ProseMirror-selectednode");
|
|
621
|
+
if (this.contentDOM || !this.node.type.spec.draggable) this.dom.removeAttribute("draggable");
|
|
622
|
+
}
|
|
623
|
+
}
|
|
602
624
|
get domAtom() {
|
|
603
625
|
return this.node.isAtom;
|
|
604
626
|
}
|
|
605
627
|
}
|
|
606
628
|
export class TextViewDesc extends NodeViewDesc {
|
|
607
629
|
constructor(parent, children, getPos, node, outerDeco, innerDeco, dom, nodeDOM){
|
|
608
|
-
super(parent, children, getPos, node, outerDeco, innerDeco, dom, null, nodeDOM
|
|
609
|
-
/* Text nodes can't have node selections */ }, ()=>{
|
|
610
|
-
/* Text nodes can't have node selections */ }, (mutation)=>{
|
|
611
|
-
return mutation.type != "characterData" && mutation.type != "selection";
|
|
612
|
-
});
|
|
630
|
+
super(parent, children, getPos, node, outerDeco, innerDeco, dom, null, nodeDOM);
|
|
613
631
|
}
|
|
614
632
|
parseRule() {
|
|
615
633
|
let skip = this.nodeDOM.parentNode;
|
|
@@ -663,6 +681,63 @@ export class TrailingHackViewDesc extends ViewDesc {
|
|
|
663
681
|
return this.dom.nodeName == "IMG";
|
|
664
682
|
}
|
|
665
683
|
}
|
|
684
|
+
// A separate subclass is used for customized node views, so that the
|
|
685
|
+
// extra checks only have to be made for nodes that are actually
|
|
686
|
+
// customized.
|
|
687
|
+
let CustomNodeViewDesc = class CustomNodeViewDesc extends NodeViewDesc {
|
|
688
|
+
spec;
|
|
689
|
+
constructor(parent, children, getPos, node, outerDeco, innerDeco, dom, contentDOM, nodeDOM, spec){
|
|
690
|
+
super(parent, children, getPos, node, outerDeco, innerDeco, dom, contentDOM, nodeDOM), this.spec = spec;
|
|
691
|
+
}
|
|
692
|
+
// A custom `update` method gets to decide whether the update goes
|
|
693
|
+
// through. If it does, and there's a `contentDOM` node, our logic
|
|
694
|
+
// updates the children.
|
|
695
|
+
update(node, outerDeco, innerDeco, view) {
|
|
696
|
+
if (this.dirty == NODE_DIRTY) return false;
|
|
697
|
+
if (this.spec.update && (this.node.type == node.type || this.spec.multiType)) {
|
|
698
|
+
const result = this.spec.update(node, outerDeco, innerDeco);
|
|
699
|
+
if (result) this.updateInner(node, outerDeco, innerDeco, view);
|
|
700
|
+
return result;
|
|
701
|
+
} else if (!this.contentDOM && !node.isLeaf) {
|
|
702
|
+
return false;
|
|
703
|
+
} else {
|
|
704
|
+
return super.update(node, outerDeco, innerDeco, view);
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
selectNode() {
|
|
708
|
+
this.spec.selectNode ? this.spec.selectNode() : super.selectNode();
|
|
709
|
+
}
|
|
710
|
+
deselectNode() {
|
|
711
|
+
this.spec.deselectNode ? this.spec.deselectNode() : super.deselectNode();
|
|
712
|
+
}
|
|
713
|
+
setSelection(anchor, head, view, force) {
|
|
714
|
+
this.spec.setSelection ? this.spec.setSelection(anchor, head, view.root) : super.setSelection(anchor, head, view, force);
|
|
715
|
+
}
|
|
716
|
+
destroy() {
|
|
717
|
+
if (this.spec.destroy) this.spec.destroy();
|
|
718
|
+
super.destroy();
|
|
719
|
+
}
|
|
720
|
+
stopEvent(event) {
|
|
721
|
+
return this.spec.stopEvent ? this.spec.stopEvent(event) : false;
|
|
722
|
+
}
|
|
723
|
+
ignoreMutation(mutation) {
|
|
724
|
+
return this.spec.ignoreMutation ? this.spec.ignoreMutation(mutation) : super.ignoreMutation(mutation);
|
|
725
|
+
}
|
|
726
|
+
};
|
|
727
|
+
export class ReactNodeViewDesc extends CustomNodeViewDesc {
|
|
728
|
+
updateChildren(_view, _pos) {
|
|
729
|
+
// React has already updated the children.
|
|
730
|
+
}
|
|
731
|
+
updateOuterDeco(outerDeco) {
|
|
732
|
+
// React has already updated the decorations.
|
|
733
|
+
this.outerDeco = outerDeco;
|
|
734
|
+
}
|
|
735
|
+
destroy() {
|
|
736
|
+
// React has already destroyed the children (if needed).
|
|
737
|
+
this.children = [];
|
|
738
|
+
super.destroy();
|
|
739
|
+
}
|
|
740
|
+
}
|
|
666
741
|
function sameOuterDeco(a, b) {
|
|
667
742
|
if (a.length != b.length) return false;
|
|
668
743
|
// @ts-expect-error ...
|