@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
|
@@ -32,7 +32,7 @@ export const MarkView = /*#__PURE__*/ memo(/*#__PURE__*/ forwardRef(function Mar
|
|
|
32
32
|
if (!domRef.current) return;
|
|
33
33
|
const firstChildDesc = childDescriptors.current[0];
|
|
34
34
|
if (!viewDescRef.current) {
|
|
35
|
-
viewDescRef.current = new MarkViewDesc(parentRef.current, childDescriptors.current,
|
|
35
|
+
viewDescRef.current = new MarkViewDesc(parentRef.current, childDescriptors.current, getPos, mark, domRef.current, firstChildDesc?.dom.parentElement ?? domRef.current, {
|
|
36
36
|
dom: domRef.current,
|
|
37
37
|
contentDOM: firstChildDesc?.dom.parentElement ?? domRef.current
|
|
38
38
|
});
|
|
@@ -42,7 +42,6 @@ export const MarkView = /*#__PURE__*/ memo(/*#__PURE__*/ forwardRef(function Mar
|
|
|
42
42
|
viewDescRef.current.children = childDescriptors.current;
|
|
43
43
|
viewDescRef.current.spec.contentDOM = viewDescRef.current.contentDOM = firstChildDesc?.dom.parentElement ?? domRef.current;
|
|
44
44
|
viewDescRef.current.mark = mark;
|
|
45
|
-
viewDescRef.current.getPos = ()=>getPos.current();
|
|
46
45
|
}
|
|
47
46
|
if (!siblingsRef.current.includes(viewDescRef.current)) {
|
|
48
47
|
siblingsRef.current.push(viewDescRef.current);
|
|
@@ -24,7 +24,7 @@ export function NativeWidgetView(param) {
|
|
|
24
24
|
if (!rootDomRef.current) return;
|
|
25
25
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
26
26
|
const toDOM = widget.type.toDOM;
|
|
27
|
-
let dom = typeof toDOM === "function" ? toDOM(view,
|
|
27
|
+
let dom = typeof toDOM === "function" ? toDOM(view, getPos) : toDOM;
|
|
28
28
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
29
29
|
if (!widget.type.spec.raw) {
|
|
30
30
|
if (dom.nodeType != 1) {
|
|
@@ -41,11 +41,10 @@ export function NativeWidgetView(param) {
|
|
|
41
41
|
useClientLayoutEffect(()=>{
|
|
42
42
|
if (!rootDomRef.current) return;
|
|
43
43
|
if (!viewDescRef.current) {
|
|
44
|
-
viewDescRef.current = new WidgetViewDesc(parentRef.current,
|
|
44
|
+
viewDescRef.current = new WidgetViewDesc(parentRef.current, getPos, widget, rootDomRef.current);
|
|
45
45
|
} else {
|
|
46
46
|
viewDescRef.current.parent = parentRef.current;
|
|
47
47
|
viewDescRef.current.widget = widget;
|
|
48
|
-
viewDescRef.current.getPos = ()=>getPos.current();
|
|
49
48
|
viewDescRef.current.dom = rootDomRef.current;
|
|
50
49
|
}
|
|
51
50
|
if (!siblingsRef.current.includes(viewDescRef.current)) {
|
|
@@ -5,7 +5,7 @@ import { ReactNodeView } from "./ReactNodeView.js";
|
|
|
5
5
|
export const NodeView = /*#__PURE__*/ memo(function NodeView(param) {
|
|
6
6
|
let { outerDeco, getPos, node, innerDeco, ...props } = param;
|
|
7
7
|
const { view } = useContext(EditorContext);
|
|
8
|
-
const customNodeView = view
|
|
8
|
+
const customNodeView = view.nodeViews[node.type.name];
|
|
9
9
|
if (customNodeView) {
|
|
10
10
|
return /*#__PURE__*/ React.createElement(CustomNodeView, {
|
|
11
11
|
customNodeView: customNodeView,
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { DecorationSet } from "prosemirror-view";
|
|
2
1
|
import React, { useMemo, useState } from "react";
|
|
3
2
|
import { EditorContext } from "../contexts/EditorContext.js";
|
|
4
3
|
import { EditorStateContext } from "../contexts/EditorStateContext.js";
|
|
@@ -8,7 +7,6 @@ import { viewDecorations } from "../decorations/viewDecorations.js";
|
|
|
8
7
|
import { useEditor } from "../hooks/useEditor.js";
|
|
9
8
|
import { LayoutGroup } from "./LayoutGroup.js";
|
|
10
9
|
import { DocNodeViewContext } from "./ProseMirrorDoc.js";
|
|
11
|
-
const EMPTY_OUTER_DECOS = [];
|
|
12
10
|
function ProseMirrorInner(param) {
|
|
13
11
|
let { className, children, nodeViews, customNodeViews, ...props } = param;
|
|
14
12
|
const [mount, setMount] = useState(null);
|
|
@@ -16,26 +14,25 @@ function ProseMirrorInner(param) {
|
|
|
16
14
|
...props,
|
|
17
15
|
nodeViews: customNodeViews
|
|
18
16
|
});
|
|
19
|
-
const innerDecos = editor.view ? viewDecorations(editor.view, editor.cursorWrapper) : DecorationSet.empty;
|
|
20
|
-
const outerDecos = editor.view ? computeDocDeco(editor.view) : EMPTY_OUTER_DECOS;
|
|
21
17
|
const nodeViewContextValue = useMemo(()=>({
|
|
22
18
|
nodeViews: nodeViews ?? {}
|
|
23
19
|
}), [
|
|
24
20
|
nodeViews
|
|
25
21
|
]);
|
|
22
|
+
const node = state.doc;
|
|
23
|
+
const innerDeco = viewDecorations(editor.view, editor.cursorWrapper);
|
|
24
|
+
const outerDeco = computeDocDeco(editor.view);
|
|
26
25
|
const docNodeViewContextValue = useMemo(()=>({
|
|
27
|
-
className
|
|
28
|
-
setMount
|
|
29
|
-
node
|
|
30
|
-
innerDeco
|
|
31
|
-
outerDeco
|
|
32
|
-
viewDesc: editor.docViewDescRef.current
|
|
26
|
+
className,
|
|
27
|
+
setMount,
|
|
28
|
+
node,
|
|
29
|
+
innerDeco,
|
|
30
|
+
outerDeco
|
|
33
31
|
}), [
|
|
34
32
|
className,
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
outerDecos
|
|
33
|
+
node,
|
|
34
|
+
innerDeco,
|
|
35
|
+
outerDeco
|
|
39
36
|
]);
|
|
40
37
|
return /*#__PURE__*/ React.createElement(EditorContext.Provider, {
|
|
41
38
|
value: editor
|
|
@@ -12,14 +12,13 @@ export const ReactNodeView = /*#__PURE__*/ memo(function ReactNodeView(param) {
|
|
|
12
12
|
const domRef = useRef(null);
|
|
13
13
|
const nodeDomRef = useRef(null);
|
|
14
14
|
const contentDomRef = useRef(null);
|
|
15
|
-
const getPosFunc = useRef(()=>getPos.current()).current;
|
|
16
15
|
const { nodeViews } = useContext(NodeViewContext);
|
|
17
16
|
let element = null;
|
|
18
17
|
const Component = nodeViews[node.type.name];
|
|
19
18
|
const outputSpec = useMemo(()=>node.type.spec.toDOM?.(node), [
|
|
20
19
|
node
|
|
21
20
|
]);
|
|
22
|
-
const { hasContentDOM, childDescriptors, setStopEvent, setSelectNode, setIgnoreMutation, nodeViewDescRef } = useNodeViewDescriptor(node,
|
|
21
|
+
const { hasContentDOM, childDescriptors, setStopEvent, setSelectNode, setIgnoreMutation, nodeViewDescRef } = useNodeViewDescriptor(node, getPos, domRef, nodeDomRef, innerDeco, outerDeco, contentDomRef);
|
|
23
22
|
const finalProps = {
|
|
24
23
|
...props,
|
|
25
24
|
...!hasContentDOM && nodeDomRef.current?.tagName !== "BR" && {
|
|
@@ -28,11 +27,11 @@ export const ReactNodeView = /*#__PURE__*/ memo(function ReactNodeView(param) {
|
|
|
28
27
|
};
|
|
29
28
|
const nodeProps = useMemo(()=>({
|
|
30
29
|
node: node,
|
|
31
|
-
getPos:
|
|
30
|
+
getPos: getPos,
|
|
32
31
|
decorations: outerDeco,
|
|
33
32
|
innerDecorations: innerDeco
|
|
34
33
|
}), [
|
|
35
|
-
|
|
34
|
+
getPos,
|
|
36
35
|
innerDeco,
|
|
37
36
|
node,
|
|
38
37
|
outerDeco
|
|
@@ -32,11 +32,10 @@ export function SeparatorHackView(param) {
|
|
|
32
32
|
}
|
|
33
33
|
if (!ref.current) return;
|
|
34
34
|
if (!viewDescRef.current) {
|
|
35
|
-
viewDescRef.current = new TrailingHackViewDesc(parentRef.current, [],
|
|
35
|
+
viewDescRef.current = new TrailingHackViewDesc(parentRef.current, [], getPos, ref.current, null);
|
|
36
36
|
} else {
|
|
37
37
|
viewDescRef.current.parent = parentRef.current;
|
|
38
38
|
viewDescRef.current.dom = ref.current;
|
|
39
|
-
viewDescRef.current.getPos = ()=>getPos.current();
|
|
40
39
|
}
|
|
41
40
|
if (!siblingsRef.current.includes(viewDescRef.current)) {
|
|
42
41
|
siblingsRef.current.push(viewDescRef.current);
|
|
@@ -37,8 +37,8 @@ export class TextNodeView extends Component {
|
|
|
37
37
|
// when a composition was started that produces a new text node.
|
|
38
38
|
// Otherwise we just rely on re-rendering the renderRef
|
|
39
39
|
if (!dom) {
|
|
40
|
-
if (!view
|
|
41
|
-
this.viewDescRef = new CompositionViewDesc(parentRef.current,
|
|
40
|
+
if (!view.composing) return;
|
|
41
|
+
this.viewDescRef = new CompositionViewDesc(parentRef.current, getPos, // These are just placeholders/dummies. We can't
|
|
42
42
|
// actually find the correct DOM nodes from here,
|
|
43
43
|
// so we let our parent do it.
|
|
44
44
|
// Passing a valid element here just so that the
|
|
@@ -51,12 +51,11 @@ export class TextNodeView extends Component {
|
|
|
51
51
|
textNode = textNode.firstChild;
|
|
52
52
|
}
|
|
53
53
|
if (!this.viewDescRef || this.viewDescRef instanceof CompositionViewDesc) {
|
|
54
|
-
this.viewDescRef = new TextViewDesc(undefined, [],
|
|
54
|
+
this.viewDescRef = new TextViewDesc(undefined, [], getPos, node, decorations, DecorationSet.empty, dom, textNode);
|
|
55
55
|
} else {
|
|
56
56
|
this.viewDescRef.parent = parentRef.current;
|
|
57
57
|
this.viewDescRef.children = [];
|
|
58
58
|
this.viewDescRef.node = node;
|
|
59
|
-
this.viewDescRef.getPos = ()=>getPos.current();
|
|
60
59
|
this.viewDescRef.outerDeco = decorations;
|
|
61
60
|
this.viewDescRef.innerDeco = DecorationSet.empty;
|
|
62
61
|
this.viewDescRef.dom = dom;
|
|
@@ -92,7 +91,7 @@ export class TextNodeView extends Component {
|
|
|
92
91
|
// an active composition and the selection is in this node,
|
|
93
92
|
// we freeze the DOM of this element so that it doesn't
|
|
94
93
|
// interrupt the composition
|
|
95
|
-
if (view
|
|
94
|
+
if (view.composing && view.state.selection.from >= getPos() && view.state.selection.from <= getPos() + node.nodeSize) {
|
|
96
95
|
return this.renderRef;
|
|
97
96
|
}
|
|
98
97
|
this.renderRef = decorations.reduce(wrapInDeco, node.text);
|
|
@@ -22,11 +22,10 @@ export function TrailingHackView(param) {
|
|
|
22
22
|
useClientLayoutEffect(()=>{
|
|
23
23
|
if (!ref.current) return;
|
|
24
24
|
if (!viewDescRef.current) {
|
|
25
|
-
viewDescRef.current = new TrailingHackViewDesc(parentRef.current, [],
|
|
25
|
+
viewDescRef.current = new TrailingHackViewDesc(parentRef.current, [], getPos, ref.current, null);
|
|
26
26
|
} else {
|
|
27
27
|
viewDescRef.current.parent = parentRef.current;
|
|
28
28
|
viewDescRef.current.dom = ref.current;
|
|
29
|
-
viewDescRef.current.getPos = ()=>getPos.current();
|
|
30
29
|
}
|
|
31
30
|
if (!siblingsRef.current.includes(viewDescRef.current)) {
|
|
32
31
|
siblingsRef.current.push(viewDescRef.current);
|
|
@@ -6,7 +6,6 @@ export function WidgetView(param) {
|
|
|
6
6
|
let { widget, getPos } = param;
|
|
7
7
|
const { siblingsRef, parentRef } = useContext(ChildDescriptorsContext);
|
|
8
8
|
const viewDescRef = useRef(null);
|
|
9
|
-
const getPosFunc = useRef(()=>getPos.current()).current;
|
|
10
9
|
const domRef = useRef(null);
|
|
11
10
|
useClientLayoutEffect(()=>{
|
|
12
11
|
const siblings = siblingsRef.current;
|
|
@@ -23,11 +22,10 @@ export function WidgetView(param) {
|
|
|
23
22
|
useClientLayoutEffect(()=>{
|
|
24
23
|
if (!domRef.current) return;
|
|
25
24
|
if (!viewDescRef.current) {
|
|
26
|
-
viewDescRef.current = new WidgetViewDesc(parentRef.current,
|
|
25
|
+
viewDescRef.current = new WidgetViewDesc(parentRef.current, getPos, widget, domRef.current);
|
|
27
26
|
} else {
|
|
28
27
|
viewDescRef.current.parent = parentRef.current;
|
|
29
28
|
viewDescRef.current.widget = widget;
|
|
30
|
-
viewDescRef.current.getPos = ()=>getPos.current();
|
|
31
29
|
viewDescRef.current.dom = domRef.current;
|
|
32
30
|
}
|
|
33
31
|
if (!siblingsRef.current.includes(viewDescRef.current)) {
|
|
@@ -39,7 +37,7 @@ export function WidgetView(param) {
|
|
|
39
37
|
return Component && /*#__PURE__*/ React.createElement(Component, {
|
|
40
38
|
ref: domRef,
|
|
41
39
|
widget: widget,
|
|
42
|
-
getPos:
|
|
40
|
+
getPos: getPos,
|
|
43
41
|
contentEditable: false
|
|
44
42
|
});
|
|
45
43
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Schema } from "prosemirror-model";
|
|
2
|
+
import { EditorState } from "prosemirror-state";
|
|
3
|
+
export const EMPTY_SCHEMA = new Schema({
|
|
4
|
+
nodes: {
|
|
5
|
+
doc: {
|
|
6
|
+
content: "text*"
|
|
7
|
+
},
|
|
8
|
+
text: {
|
|
9
|
+
inline: true
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
export const EMPTY_STATE = EditorState.create({
|
|
14
|
+
schema: EMPTY_SCHEMA
|
|
15
|
+
});
|
|
@@ -1,178 +1,12 @@
|
|
|
1
|
-
import { Schema } from "prosemirror-model";
|
|
2
|
-
import { EditorState } from "prosemirror-state";
|
|
3
|
-
import { DecorationSet, EditorView } from "prosemirror-view";
|
|
4
1
|
import { useCallback, useMemo, useRef, useState } from "react";
|
|
5
2
|
import { flushSync } from "react-dom";
|
|
3
|
+
import { ReactEditorView } from "../ReactEditorView.js";
|
|
4
|
+
import { StaticEditorView } from "../StaticEditorView.js";
|
|
5
|
+
import { EMPTY_STATE } from "../constants.js";
|
|
6
6
|
import { beforeInputPlugin } from "../plugins/beforeInputPlugin.js";
|
|
7
|
-
import { SelectionDOMObserver } from "../selection/SelectionDOMObserver.js";
|
|
8
|
-
import { setSsrStubs } from "../ssr.js";
|
|
9
|
-
import { NodeViewDesc } from "../viewdesc.js";
|
|
10
7
|
import { useClientLayoutEffect } from "./useClientLayoutEffect.js";
|
|
11
8
|
import { useComponentEventListeners } from "./useComponentEventListeners.js";
|
|
12
9
|
import { useForceUpdate } from "./useForceUpdate.js";
|
|
13
|
-
function buildNodeViews(view) {
|
|
14
|
-
const result = Object.create(null);
|
|
15
|
-
function add(obj) {
|
|
16
|
-
for(const prop in obj)if (!Object.prototype.hasOwnProperty.call(result, prop)) // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
17
|
-
result[prop] = obj[prop];
|
|
18
|
-
}
|
|
19
|
-
view.someProp("nodeViews", add);
|
|
20
|
-
view.someProp("markViews", add);
|
|
21
|
-
return result;
|
|
22
|
-
}
|
|
23
|
-
function changedNodeViews(a, b) {
|
|
24
|
-
let nA = 0, nB = 0;
|
|
25
|
-
for(const prop in a){
|
|
26
|
-
if (a[prop] != b[prop]) return true;
|
|
27
|
-
nA++;
|
|
28
|
-
}
|
|
29
|
-
for(const _ in b)nB++;
|
|
30
|
-
return nA != nB;
|
|
31
|
-
}
|
|
32
|
-
function changedProps(a, b) {
|
|
33
|
-
for (const prop of Object.keys(a)){
|
|
34
|
-
if (a[prop] !== b[prop]) return true;
|
|
35
|
-
}
|
|
36
|
-
return false;
|
|
37
|
-
}
|
|
38
|
-
function getEditable(view) {
|
|
39
|
-
return !view.someProp("editable", (value)=>value(view.state) === false);
|
|
40
|
-
}
|
|
41
|
-
// @ts-expect-error We're making use of knowledge of internal methods here
|
|
42
|
-
export class ReactEditorView extends EditorView {
|
|
43
|
-
shouldUpdatePluginViews = false;
|
|
44
|
-
oldProps;
|
|
45
|
-
_props;
|
|
46
|
-
ready;
|
|
47
|
-
constructor(place, props){
|
|
48
|
-
// Call the superclass constructor with an empty
|
|
49
|
-
// document and limited props. We'll set everything
|
|
50
|
-
// else ourselves.
|
|
51
|
-
const cleanup = setSsrStubs();
|
|
52
|
-
super(place, {
|
|
53
|
-
state: EditorState.create({
|
|
54
|
-
schema: props.state.schema
|
|
55
|
-
})
|
|
56
|
-
});
|
|
57
|
-
cleanup();
|
|
58
|
-
this.ready = props.ready;
|
|
59
|
-
this.shouldUpdatePluginViews = true;
|
|
60
|
-
this._props = props;
|
|
61
|
-
this.oldProps = {
|
|
62
|
-
state: props.state
|
|
63
|
-
};
|
|
64
|
-
this.state = props.state;
|
|
65
|
-
// @ts-expect-error We're making use of knowledge of internal attributes here
|
|
66
|
-
this.domObserver.stop();
|
|
67
|
-
// @ts-expect-error We're making use of knowledge of internal attributes here
|
|
68
|
-
this.domObserver = new SelectionDOMObserver(this);
|
|
69
|
-
// @ts-expect-error We're making use of knowledge of internal attributes here
|
|
70
|
-
this.domObserver.start();
|
|
71
|
-
this.editable = getEditable(this);
|
|
72
|
-
// Destroy the DOM created by the default
|
|
73
|
-
// ProseMirror ViewDesc implementation; we
|
|
74
|
-
// have a NodeViewDesc from React instead.
|
|
75
|
-
// @ts-expect-error We're making use of knowledge of internal attributes here
|
|
76
|
-
this.docView.dom.replaceChildren();
|
|
77
|
-
// @ts-expect-error We're making use of knowledge of internal attributes here
|
|
78
|
-
this.nodeViews = buildNodeViews(this);
|
|
79
|
-
// @ts-expect-error We're making use of knowledge of internal attributes here
|
|
80
|
-
this.docView = props.docView;
|
|
81
|
-
}
|
|
82
|
-
/**
|
|
83
|
-
* Whether the EditorView's updateStateInner method thinks that the
|
|
84
|
-
* docView needs to be blown away and redrawn.
|
|
85
|
-
*
|
|
86
|
-
* @privateremarks
|
|
87
|
-
*
|
|
88
|
-
* When ProseMirror View detects that the EditorState has been reconfigured
|
|
89
|
-
* to provide new custom node views, it calls an internal function that
|
|
90
|
-
* we can't override in order to recreate the entire editor DOM.
|
|
91
|
-
*
|
|
92
|
-
* This property mimics that check, so that we can replace the EditorView
|
|
93
|
-
* with another of our own, preventing ProseMirror View from taking over
|
|
94
|
-
* DOM management responsibility.
|
|
95
|
-
*/ get needsRedraw() {
|
|
96
|
-
if (this.oldProps.state.plugins === this._props.state.plugins && this._props.plugins === this.oldProps.plugins) {
|
|
97
|
-
return false;
|
|
98
|
-
}
|
|
99
|
-
const newNodeViews = buildNodeViews(this);
|
|
100
|
-
// @ts-expect-error Internal property
|
|
101
|
-
return changedNodeViews(this.nodeViews, newNodeViews);
|
|
102
|
-
}
|
|
103
|
-
/**
|
|
104
|
-
* Like setProps, but without executing any side effects.
|
|
105
|
-
* Safe to use in a component render method.
|
|
106
|
-
*/ pureSetProps(props) {
|
|
107
|
-
// this.oldProps = this.props;
|
|
108
|
-
this._props = {
|
|
109
|
-
...this._props,
|
|
110
|
-
...props
|
|
111
|
-
};
|
|
112
|
-
this.state = this._props.state;
|
|
113
|
-
this.editable = getEditable(this);
|
|
114
|
-
}
|
|
115
|
-
/**
|
|
116
|
-
* Triggers any side effects that have been queued by previous
|
|
117
|
-
* calls to pureSetProps.
|
|
118
|
-
*/ runPendingEffects() {
|
|
119
|
-
if (changedProps(this.props, this.oldProps)) {
|
|
120
|
-
const newProps = this.props;
|
|
121
|
-
this._props = this.oldProps;
|
|
122
|
-
this.state = this._props.state;
|
|
123
|
-
this.update(newProps);
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
update(props) {
|
|
127
|
-
super.update(props);
|
|
128
|
-
// Ensure that side effects aren't re-triggered until
|
|
129
|
-
// pureSetProps is called again
|
|
130
|
-
this.oldProps = props;
|
|
131
|
-
}
|
|
132
|
-
updatePluginViews(prevState) {
|
|
133
|
-
if (this.shouldUpdatePluginViews) {
|
|
134
|
-
// @ts-expect-error We're making use of knowledge of internal methods here
|
|
135
|
-
super.updatePluginViews(prevState);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
// We want to trigger the default EditorView cleanup, but without
|
|
139
|
-
// the actual view.dom cleanup (which React will have already handled).
|
|
140
|
-
// So we give the EditorView a dummy DOM element and ask it to clean up
|
|
141
|
-
destroy() {
|
|
142
|
-
// If needsRedraw returns true, then we will destroy and recreate
|
|
143
|
-
// the EditorView, but will likely leave the ProseMirrorDoc node as-is.
|
|
144
|
-
// In this case, this.dom will still be connected when destroy runs, and
|
|
145
|
-
// it will be reused for the next EditorView, so we need to manually reset
|
|
146
|
-
// it.
|
|
147
|
-
if (this.dom.isConnected) {
|
|
148
|
-
// We need to manually execute this code from super.destroy(),
|
|
149
|
-
// because when super.destroy() runs, it will have been given a dummy
|
|
150
|
-
// dom node
|
|
151
|
-
// @ts-expect-error Internal property - input
|
|
152
|
-
for(const type in this.input.eventHandlers){
|
|
153
|
-
// @ts-expect-error Internal property - input
|
|
154
|
-
this.dom.removeEventListener(type, this.input.eventHandlers[type]);
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
// @ts-expect-error we're intentionally overwriting this property
|
|
158
|
-
// to prevent side effects
|
|
159
|
-
this.dom = document.createElement("div");
|
|
160
|
-
super.destroy();
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
const EMPTY_SCHEMA = new Schema({
|
|
164
|
-
nodes: {
|
|
165
|
-
doc: {
|
|
166
|
-
content: "text*"
|
|
167
|
-
},
|
|
168
|
-
text: {
|
|
169
|
-
inline: true
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
});
|
|
173
|
-
const EMPTY_STATE = EditorState.create({
|
|
174
|
-
schema: EMPTY_SCHEMA
|
|
175
|
-
});
|
|
176
10
|
let didWarnValueDefaultValue = false;
|
|
177
11
|
/**
|
|
178
12
|
* Creates, mounts, and manages a ProseMirror `EditorView`.
|
|
@@ -232,30 +66,18 @@ let didWarnValueDefaultValue = false;
|
|
|
232
66
|
options.dispatchTransaction,
|
|
233
67
|
options.state
|
|
234
68
|
]);
|
|
235
|
-
const cleanup = setSsrStubs();
|
|
236
|
-
const tempDom = document.createElement("div");
|
|
237
|
-
cleanup();
|
|
238
|
-
const docViewDescRef = useRef(new NodeViewDesc(undefined, [], ()=>-1, state.doc, [], DecorationSet.empty, tempDom, null, tempDom, ()=>false, ()=>{
|
|
239
|
-
/* The doc node can't have a node selection*/ }, ()=>{
|
|
240
|
-
/* The doc node can't have a node selection*/ }, ()=>false));
|
|
241
69
|
const directEditorProps = {
|
|
242
70
|
...options,
|
|
243
71
|
state,
|
|
244
72
|
plugins,
|
|
245
|
-
dispatchTransaction
|
|
246
|
-
docView: docViewDescRef.current,
|
|
247
|
-
ready: true
|
|
73
|
+
dispatchTransaction
|
|
248
74
|
};
|
|
249
|
-
const [view, setView] = useState(
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
()=>new ReactEditorView(null, {
|
|
253
|
-
...directEditorProps,
|
|
254
|
-
ready: false
|
|
255
|
-
}));
|
|
75
|
+
const [view, setView] = useState(()=>{
|
|
76
|
+
return new StaticEditorView(directEditorProps);
|
|
77
|
+
});
|
|
256
78
|
useClientLayoutEffect(()=>{
|
|
257
79
|
return ()=>{
|
|
258
|
-
view
|
|
80
|
+
view.destroy();
|
|
259
81
|
};
|
|
260
82
|
}, [
|
|
261
83
|
view
|
|
@@ -265,44 +87,33 @@ let didWarnValueDefaultValue = false;
|
|
|
265
87
|
// so this is not a concern.
|
|
266
88
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
267
89
|
useClientLayoutEffect(()=>{
|
|
268
|
-
if (
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
// but we need to convince EditorView's constructor not to
|
|
282
|
-
// clear out the DOM when passed a mount that already has
|
|
283
|
-
// content in it, otherwise React blows up when it tries
|
|
284
|
-
// to clean up.
|
|
285
|
-
if (view.needsRedraw) {
|
|
286
|
-
setView(null);
|
|
287
|
-
return;
|
|
90
|
+
if (mount !== view.dom) {
|
|
91
|
+
if (mount) {
|
|
92
|
+
const view = new ReactEditorView({
|
|
93
|
+
mount
|
|
94
|
+
}, directEditorProps);
|
|
95
|
+
view.dom.addEventListener("compositionend", forceUpdate);
|
|
96
|
+
setView(view);
|
|
97
|
+
} else {
|
|
98
|
+
const view = new StaticEditorView(directEditorProps);
|
|
99
|
+
setView(view);
|
|
100
|
+
}
|
|
101
|
+
} else if (view instanceof ReactEditorView) {
|
|
102
|
+
view.commitPendingEffects();
|
|
288
103
|
}
|
|
289
|
-
// @ts-expect-error Internal property - domObserver
|
|
290
|
-
view?.domObserver.selectionToDOM();
|
|
291
|
-
view?.runPendingEffects();
|
|
292
104
|
});
|
|
293
|
-
view
|
|
105
|
+
view.update(directEditorProps);
|
|
294
106
|
const editor = useMemo(()=>({
|
|
295
|
-
view
|
|
296
|
-
registerEventListener,
|
|
297
|
-
unregisterEventListener,
|
|
107
|
+
view,
|
|
298
108
|
cursorWrapper,
|
|
299
|
-
|
|
300
|
-
|
|
109
|
+
flushSyncRef,
|
|
110
|
+
registerEventListener,
|
|
111
|
+
unregisterEventListener
|
|
301
112
|
}), [
|
|
302
|
-
|
|
113
|
+
cursorWrapper,
|
|
303
114
|
registerEventListener,
|
|
304
115
|
unregisterEventListener,
|
|
305
|
-
|
|
116
|
+
view
|
|
306
117
|
]);
|
|
307
118
|
return {
|
|
308
119
|
editor,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/* Copyright (c) The New York Times Company */ import { useContext } from "react";
|
|
2
|
+
import { ReactEditorView } from "../ReactEditorView.js";
|
|
2
3
|
import { EditorContext } from "../contexts/EditorContext.js";
|
|
3
4
|
import { useLayoutGroupEffect } from "./useLayoutGroupEffect.js";
|
|
4
5
|
/**
|
|
@@ -24,8 +25,7 @@ import { useLayoutGroupEffect } from "./useLayoutGroupEffect.js";
|
|
|
24
25
|
// every time it changes, because it will most likely
|
|
25
26
|
// be defined inline and run on every re-render.
|
|
26
27
|
useLayoutGroupEffect(()=>{
|
|
27
|
-
|
|
28
|
-
if (view?.docView && view.ready) {
|
|
28
|
+
if (view instanceof ReactEditorView) {
|
|
29
29
|
flushSyncRef.current = false;
|
|
30
30
|
const result = effect(view);
|
|
31
31
|
flushSyncRef.current = true;
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
/* Copyright (c) The New York Times Company */ import { useCallback, useContext, useRef } from "react";
|
|
2
|
+
import { ReactEditorView } from "../ReactEditorView.js";
|
|
2
3
|
import { EditorContext } from "../contexts/EditorContext.js";
|
|
3
4
|
import { useEditorEffect } from "./useEditorEffect.js";
|
|
5
|
+
function assertIsReactEditorView(view) {
|
|
6
|
+
if (view instanceof ReactEditorView) {
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
throw new DOMException("ProseMirror document is not mounted", "InvalidStateError");
|
|
10
|
+
}
|
|
4
11
|
/**
|
|
5
12
|
* Returns a stable function reference to be used as an
|
|
6
13
|
* event handler callback.
|
|
@@ -25,11 +32,7 @@ import { useEditorEffect } from "./useEditorEffect.js";
|
|
|
25
32
|
for(var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++){
|
|
26
33
|
args[_key] = arguments[_key];
|
|
27
34
|
}
|
|
28
|
-
|
|
29
|
-
// while view is null, since view is only ever set to
|
|
30
|
-
// null in a layout effect that then immediately triggers
|
|
31
|
-
// a re-render which sets view to a new EditorView
|
|
32
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
35
|
+
assertIsReactEditorView(view);
|
|
33
36
|
return ref.current(view, ...args);
|
|
34
37
|
}, [
|
|
35
38
|
view
|
|
@@ -3,10 +3,10 @@ import { ChildDescriptorsContext } from "../contexts/ChildDescriptorsContext.js"
|
|
|
3
3
|
import { EditorContext } from "../contexts/EditorContext.js";
|
|
4
4
|
import { CompositionViewDesc, NodeViewDesc, sortViewDescs } from "../viewdesc.js";
|
|
5
5
|
import { useClientLayoutEffect } from "./useClientLayoutEffect.js";
|
|
6
|
-
export function useNodeViewDescriptor(node, getPos, domRef, nodeDomRef, innerDecorations, outerDecorations,
|
|
6
|
+
export function useNodeViewDescriptor(node, getPos, domRef, nodeDomRef, innerDecorations, outerDecorations, contentDOMRef) {
|
|
7
7
|
const { view } = useContext(EditorContext);
|
|
8
8
|
const [hasContentDOM, setHasContentDOM] = useState(true);
|
|
9
|
-
const nodeViewDescRef = useRef(
|
|
9
|
+
const nodeViewDescRef = useRef();
|
|
10
10
|
const stopEvent = useRef(()=>false);
|
|
11
11
|
const setStopEvent = useCallback((newStopEvent)=>{
|
|
12
12
|
stopEvent.current = newStopEvent;
|
|
@@ -16,12 +16,12 @@ export function useNodeViewDescriptor(node, getPos, domRef, nodeDomRef, innerDec
|
|
|
16
16
|
ignoreMutation.current = newIgnoreMutation;
|
|
17
17
|
}, []);
|
|
18
18
|
const selectNode = useRef(()=>{
|
|
19
|
-
if (!nodeDomRef.current
|
|
19
|
+
if (!nodeDomRef.current) return;
|
|
20
20
|
if (nodeDomRef.current.nodeType == 1) nodeDomRef.current.classList.add("ProseMirror-selectednode");
|
|
21
21
|
if (contentDOMRef?.current || !node.type.spec.draggable) (domRef?.current ?? nodeDomRef.current).draggable = true;
|
|
22
22
|
});
|
|
23
23
|
const deselectNode = useRef(()=>{
|
|
24
|
-
if (!nodeDomRef.current
|
|
24
|
+
if (!nodeDomRef.current) return;
|
|
25
25
|
if (nodeDomRef.current.nodeType == 1) {
|
|
26
26
|
nodeDomRef.current.classList.remove("ProseMirror-selectednode");
|
|
27
27
|
if (contentDOMRef?.current || !node.type.spec.draggable) (domRef?.current ?? nodeDomRef.current).removeAttribute("draggable");
|
|
@@ -47,15 +47,14 @@ export function useNodeViewDescriptor(node, getPos, domRef, nodeDomRef, innerDec
|
|
|
47
47
|
]);
|
|
48
48
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
49
49
|
useClientLayoutEffect(()=>{
|
|
50
|
-
if (!
|
|
50
|
+
if (!nodeDomRef.current) return;
|
|
51
51
|
const firstChildDesc = childDescriptors.current[0];
|
|
52
52
|
if (!nodeViewDescRef.current) {
|
|
53
|
-
nodeViewDescRef.current = new NodeViewDesc(parentRef.current, childDescriptors.current, getPos, node, outerDecorations, innerDecorations, domRef?.current ?? nodeDomRef.current, firstChildDesc?.dom.parentElement ?? null, nodeDomRef.current, (event)=>!!stopEvent.current(event), ()=>selectNode.current(), ()=>deselectNode.current(), (mutation)=>ignoreMutation.current(mutation));
|
|
53
|
+
nodeViewDescRef.current = new NodeViewDesc(parentRef.current, childDescriptors.current, getPos, node, outerDecorations, innerDecorations, domRef?.current ?? nodeDomRef.current, contentDOMRef?.current ?? firstChildDesc?.dom.parentElement ?? null, nodeDomRef.current, (event)=>!!stopEvent.current(event), ()=>selectNode.current(), ()=>deselectNode.current(), (mutation)=>ignoreMutation.current(mutation));
|
|
54
54
|
} else {
|
|
55
55
|
nodeViewDescRef.current.parent = parentRef.current;
|
|
56
56
|
nodeViewDescRef.current.children = childDescriptors.current;
|
|
57
57
|
nodeViewDescRef.current.node = node;
|
|
58
|
-
nodeViewDescRef.current.getPos = getPos;
|
|
59
58
|
nodeViewDescRef.current.outerDeco = outerDecorations;
|
|
60
59
|
nodeViewDescRef.current.innerDeco = innerDecorations;
|
|
61
60
|
nodeViewDescRef.current.dom = domRef?.current ?? nodeDomRef.current;
|
|
@@ -88,12 +87,13 @@ export function useNodeViewDescriptor(node, getPos, domRef, nodeDomRef, innerDec
|
|
|
88
87
|
childDesc.textDOM = textDOM;
|
|
89
88
|
childDesc.text = textDOM.data;
|
|
90
89
|
childDesc.textDOM.pmViewDesc = childDesc;
|
|
91
|
-
//
|
|
92
|
-
view
|
|
90
|
+
// It should not be possible to be in a composition because one could
|
|
91
|
+
// not start between the renders that switch the view type.
|
|
92
|
+
view.input.compositionNodes.push(childDesc);
|
|
93
93
|
}
|
|
94
94
|
}
|
|
95
95
|
return ()=>{
|
|
96
|
-
if (nodeViewDescRef.current?.children[0] instanceof CompositionViewDesc && !view
|
|
96
|
+
if (nodeViewDescRef.current?.children[0] instanceof CompositionViewDesc && !view.composing) {
|
|
97
97
|
nodeViewDescRef.current?.children[0].dom.parentNode?.removeChild(nodeViewDescRef.current?.children[0].dom);
|
|
98
98
|
}
|
|
99
99
|
};
|
|
@@ -3,5 +3,5 @@ import { EditorContext } from "../contexts/EditorContext.js";
|
|
|
3
3
|
import { reactKeysPluginKey } from "../plugins/reactKeys.js";
|
|
4
4
|
export function useReactKeys() {
|
|
5
5
|
const { view } = useContext(EditorContext);
|
|
6
|
-
return
|
|
6
|
+
return reactKeysPluginKey.getState(view.state);
|
|
7
7
|
}
|
|
@@ -62,8 +62,6 @@ export function tempEditor(param) {
|
|
|
62
62
|
}, /*#__PURE__*/ React.createElement(Test, null), /*#__PURE__*/ React.createElement(ProseMirrorDoc, null)));
|
|
63
63
|
return view;
|
|
64
64
|
}
|
|
65
|
-
// We need two renders for the hasContentDOM state to settle
|
|
66
|
-
rerenderEditor();
|
|
67
65
|
return {
|
|
68
66
|
view: view,
|
|
69
67
|
rerender: rerenderEditor,
|