@handlewithcare/react-prosemirror 3.1.0-tiptap.34 → 3.1.0-tiptap.36
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/components/CustomNodeView.js +132 -0
- package/dist/cjs/components/DefaultNodeView.js +67 -0
- package/dist/cjs/components/DocNodeView.js +96 -0
- package/dist/cjs/components/MarkView.js +119 -0
- package/dist/cjs/components/NodeView.js +86 -0
- package/dist/cjs/components/NodeViewComponentProps.js +4 -0
- package/dist/cjs/components/ReactNodeView.js +174 -0
- package/dist/cjs/components/marks/CustomMarkView.js +110 -0
- package/dist/cjs/components/marks/OldMarkView.js +120 -0
- package/dist/cjs/components/nodes/CustomNodeView.js +135 -0
- package/dist/cjs/components/nodes/ReactNodeView.js +1 -1
- package/dist/cjs/contexts/ChildDescriptorsContext.js +19 -0
- package/dist/cjs/hooks/useNodeViewDescriptor.js +156 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/tiptap/TiptapNodeView.js +26 -0
- package/dist/cjs/tiptap/tiptapNodeView.js +10 -7
- package/dist/esm/components/CustomNodeView.js +81 -0
- package/dist/esm/components/DefaultNodeView.js +16 -0
- package/dist/esm/components/DocNodeView.js +45 -0
- package/dist/esm/components/MarkView.js +68 -0
- package/dist/esm/components/NodeView.js +35 -0
- package/dist/esm/components/NodeViewComponentProps.js +1 -0
- package/dist/esm/components/ReactNodeView.js +123 -0
- package/dist/esm/components/marks/CustomMarkView.js +59 -0
- package/dist/esm/components/marks/OldMarkView.js +69 -0
- package/dist/esm/components/nodes/CustomNodeView.js +84 -0
- package/dist/esm/components/nodes/ReactNodeView.js +1 -1
- package/dist/esm/contexts/ChildDescriptorsContext.js +9 -0
- package/dist/esm/hooks/useNodeViewDescriptor.js +146 -0
- package/dist/esm/tiptap/TiptapNodeView.js +22 -0
- package/dist/esm/tiptap/tiptapNodeView.js +11 -8
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/components/CustomNodeView.d.ts +12 -0
- package/dist/types/components/DefaultNodeView.d.ts +3 -0
- package/dist/types/components/DocNodeView.d.ts +12 -0
- package/dist/types/components/MarkView.d.ts +9 -0
- package/dist/types/components/NodeView.d.ts +11 -0
- package/dist/types/components/NodeViewComponentProps.d.ts +12 -0
- package/dist/types/components/ReactNodeView.d.ts +13 -0
- package/dist/types/components/marks/CustomMarkView.d.ts +12 -0
- package/dist/types/components/marks/OldMarkView.d.ts +10 -0
- package/dist/types/components/nodes/CustomNodeView.d.ts +12 -0
- package/dist/types/constants.d.ts +1 -1
- package/dist/types/contexts/ChildDescriptorsContext.d.ts +6 -0
- package/dist/types/hooks/useNodeViewDescriptor.d.ts +20 -0
- package/dist/types/props.d.ts +26 -26
- package/dist/types/tiptap/TiptapNodeView.d.ts +15 -0
- package/package.json +1 -1
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { useContext, useMemo, useRef, useState } from "react";
|
|
2
|
+
import { ReactEditorView } from "../ReactEditorView.js";
|
|
3
|
+
import { ChildDescriptorsContext } from "../contexts/ChildDescriptorsContext.js";
|
|
4
|
+
import { EditorContext } from "../contexts/EditorContext.js";
|
|
5
|
+
import { CompositionViewDesc, ReactNodeViewDesc, sortViewDescs } from "../viewdesc.js";
|
|
6
|
+
import { useClientLayoutEffect } from "./useClientLayoutEffect.js";
|
|
7
|
+
import { useEffectEvent } from "./useEffectEvent.js";
|
|
8
|
+
function findContentDOM(view, source, children) {
|
|
9
|
+
// When a composition is happening, we want to essentially freeze everything,
|
|
10
|
+
// so we maintain the current contentDOM.
|
|
11
|
+
return view.composing ? source?.contentDOM ?? null : children[0]?.dom?.parentElement ?? null;
|
|
12
|
+
}
|
|
13
|
+
export function useNodeViewDescriptor(ref, constructor, props) {
|
|
14
|
+
const { view } = useContext(EditorContext);
|
|
15
|
+
const { parentRef, siblingsRef } = useContext(ChildDescriptorsContext);
|
|
16
|
+
const [dom, setDOM] = useState(null);
|
|
17
|
+
const [nodeDOM, setNodeDOM] = useState(null);
|
|
18
|
+
const [contentDOM, setContentDOM] = useState(null);
|
|
19
|
+
const viewDescRef = useRef();
|
|
20
|
+
const childrenRef = useRef([]);
|
|
21
|
+
const create = useEffectEvent(()=>{
|
|
22
|
+
if (!(view instanceof ReactEditorView)) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const dom = ref.current;
|
|
26
|
+
if (!dom) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const { node, getPos, decorations, innerDecorations } = props;
|
|
30
|
+
const nodeView = constructor(node, view, getPos, decorations, innerDecorations);
|
|
31
|
+
if (!nodeView) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const parent = parentRef.current;
|
|
35
|
+
const children = childrenRef.current;
|
|
36
|
+
const contentDOM = findContentDOM(view, nodeView, children);
|
|
37
|
+
const nodeDOM = nodeView.dom;
|
|
38
|
+
const viewDesc = new ReactNodeViewDesc(parent, children, getPos, node, decorations, innerDecorations, dom, contentDOM, nodeDOM, nodeView);
|
|
39
|
+
setDOM(dom);
|
|
40
|
+
setContentDOM(contentDOM);
|
|
41
|
+
setNodeDOM(nodeDOM);
|
|
42
|
+
return viewDesc;
|
|
43
|
+
});
|
|
44
|
+
const update = useEffectEvent(()=>{
|
|
45
|
+
if (!(view instanceof ReactEditorView)) {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
const viewDesc = viewDescRef.current;
|
|
49
|
+
if (!viewDesc) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
const dom = ref.current;
|
|
53
|
+
if (!dom || dom !== viewDesc.dom) {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
const contentDOM = findContentDOM(view, viewDesc, viewDesc.children);
|
|
57
|
+
if (contentDOM !== viewDesc.contentDOM) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
if (!dom.contains(viewDesc.nodeDOM)) {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
const { node, decorations, innerDecorations } = props;
|
|
64
|
+
return viewDesc.matchesNode(node, decorations, innerDecorations) || viewDesc.update(node, decorations, innerDecorations, view);
|
|
65
|
+
});
|
|
66
|
+
const destroy = useEffectEvent(()=>{
|
|
67
|
+
const viewDesc = viewDescRef.current;
|
|
68
|
+
if (!viewDesc) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
viewDesc.destroy();
|
|
72
|
+
const siblings = siblingsRef.current;
|
|
73
|
+
if (siblings.includes(viewDesc)) {
|
|
74
|
+
const index = siblings.indexOf(viewDesc);
|
|
75
|
+
siblings.splice(index, 1);
|
|
76
|
+
}
|
|
77
|
+
setDOM(null);
|
|
78
|
+
setContentDOM(null);
|
|
79
|
+
setNodeDOM(null);
|
|
80
|
+
});
|
|
81
|
+
useClientLayoutEffect(()=>{
|
|
82
|
+
viewDescRef.current = create();
|
|
83
|
+
return ()=>{
|
|
84
|
+
destroy();
|
|
85
|
+
};
|
|
86
|
+
}, [
|
|
87
|
+
create,
|
|
88
|
+
destroy
|
|
89
|
+
]);
|
|
90
|
+
useClientLayoutEffect(()=>{
|
|
91
|
+
if (!update()) {
|
|
92
|
+
destroy();
|
|
93
|
+
viewDescRef.current = create();
|
|
94
|
+
}
|
|
95
|
+
const viewDesc = viewDescRef.current;
|
|
96
|
+
if (!viewDesc) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
if (view.dom === viewDesc.dom && view instanceof ReactEditorView) {
|
|
100
|
+
view.docView = viewDesc;
|
|
101
|
+
}
|
|
102
|
+
const parent = parentRef.current;
|
|
103
|
+
const siblings = siblingsRef.current;
|
|
104
|
+
const children = childrenRef.current;
|
|
105
|
+
viewDesc.parent = parent;
|
|
106
|
+
if (!siblings.includes(viewDesc)) {
|
|
107
|
+
siblings.push(viewDesc);
|
|
108
|
+
}
|
|
109
|
+
siblings.sort(sortViewDescs);
|
|
110
|
+
for (const child of children){
|
|
111
|
+
child.parent = viewDesc;
|
|
112
|
+
// Because TextNodeViews can't locate the DOM nodes
|
|
113
|
+
// for compositions, we need to override them here
|
|
114
|
+
if (child instanceof CompositionViewDesc) {
|
|
115
|
+
const compositionTopDOM = viewDesc?.contentDOM?.firstChild;
|
|
116
|
+
if (!compositionTopDOM) throw new Error(`Started a composition but couldn't find the text node it belongs to.`);
|
|
117
|
+
let textDOM = compositionTopDOM;
|
|
118
|
+
while(textDOM.firstChild){
|
|
119
|
+
textDOM = textDOM.firstChild;
|
|
120
|
+
}
|
|
121
|
+
if (!textDOM || !(textDOM instanceof Text)) throw new Error(`Started a composition but couldn't find the text node it belongs to.`);
|
|
122
|
+
child.dom = compositionTopDOM;
|
|
123
|
+
child.textDOM = textDOM;
|
|
124
|
+
child.text = textDOM.data;
|
|
125
|
+
child.textDOM.pmViewDesc = child;
|
|
126
|
+
// It should not be possible to be in a composition because one could
|
|
127
|
+
// not start between the renders that switch the view type.
|
|
128
|
+
view.input.compositionNodes.push(child);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
const childContextValue = useMemo(()=>({
|
|
133
|
+
parentRef: viewDescRef,
|
|
134
|
+
siblingsRef: childrenRef
|
|
135
|
+
}), [
|
|
136
|
+
childrenRef,
|
|
137
|
+
viewDescRef
|
|
138
|
+
]);
|
|
139
|
+
return {
|
|
140
|
+
childContextValue,
|
|
141
|
+
dom,
|
|
142
|
+
contentDOM,
|
|
143
|
+
nodeDOM,
|
|
144
|
+
ref
|
|
145
|
+
};
|
|
146
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { NodeView } from "@tiptap/core";
|
|
2
|
+
/**
|
|
3
|
+
* Subclass of Tiptap's NodeView to be used in tiptapNodeView.
|
|
4
|
+
*
|
|
5
|
+
* Allows us to pass in an existing dom and contentODM from React ProseMirror's
|
|
6
|
+
* ViewDesc, so that we can call Tiptap's default stopEvent and ignoreMutation
|
|
7
|
+
* methods
|
|
8
|
+
*/ export class ReactProseMirrorNodeView extends NodeView {
|
|
9
|
+
_dom;
|
|
10
|
+
_contentDOM;
|
|
11
|
+
constructor(component, props, dom, contentDOM, options){
|
|
12
|
+
super(component, props, options);
|
|
13
|
+
this._dom = dom;
|
|
14
|
+
this._contentDOM = contentDOM;
|
|
15
|
+
}
|
|
16
|
+
get dom() {
|
|
17
|
+
return this._dom;
|
|
18
|
+
}
|
|
19
|
+
get contentDOM() {
|
|
20
|
+
return this._contentDOM;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { getAttributesFromExtensions, getRenderedAttributes } from "@tiptap/core";
|
|
2
2
|
import { ReactNodeViewContext, useCurrentEditor } from "@tiptap/react";
|
|
3
3
|
import cx from "classnames";
|
|
4
|
-
import React, { forwardRef, memo, useMemo } from "react";
|
|
4
|
+
import React, { forwardRef, memo, useMemo, useRef } from "react";
|
|
5
5
|
import { useEditorEventCallback } from "../hooks/useEditorEventCallback.js";
|
|
6
6
|
import { useIgnoreMutation } from "../hooks/useIgnoreMutation.js";
|
|
7
7
|
import { useIsNodeSelected } from "../hooks/useIsNodeSelected.js";
|
|
8
8
|
import { useStopEvent } from "../hooks/useStopEvent.js";
|
|
9
9
|
import { htmlAttrsToReactProps } from "../props.js";
|
|
10
|
+
import { useMergedDOMRefs } from "../refs.js";
|
|
10
11
|
import { ReactProseMirrorNodeView } from "./ReactProseMirrorNodeView.js";
|
|
11
12
|
import { useTiptapEditorEventCallback } from "./hooks/useTiptapEditorEventCallback.js";
|
|
12
13
|
/**
|
|
@@ -43,6 +44,7 @@ import { useTiptapEditorEventCallback } from "./hooks/useTiptapEditorEventCallba
|
|
|
43
44
|
const extensionManager = editor?.extensionManager ?? null;
|
|
44
45
|
const extensions = extensionManager?.extensions ?? null;
|
|
45
46
|
const selected = useIsNodeSelected();
|
|
47
|
+
const isDraggingRef = useRef(false);
|
|
46
48
|
const htmlAttributes = useMemo(()=>{
|
|
47
49
|
if (!extensions) return {};
|
|
48
50
|
const attributes = getAttributesFromExtensions(extensions);
|
|
@@ -73,7 +75,10 @@ import { useTiptapEditorEventCallback } from "./hooks/useTiptapEditorEventCallba
|
|
|
73
75
|
node,
|
|
74
76
|
view: editor.view
|
|
75
77
|
}, this.dom, this.contentDOM);
|
|
76
|
-
|
|
78
|
+
nodeView.isDragging = isDraggingRef.current;
|
|
79
|
+
const result = nodeView.stopEvent(event) ?? false;
|
|
80
|
+
isDraggingRef.current = nodeView.isDragging;
|
|
81
|
+
return result;
|
|
77
82
|
});
|
|
78
83
|
useIgnoreMutation(function(_, mutation) {
|
|
79
84
|
if (ignoreMutation) {
|
|
@@ -152,12 +157,10 @@ import { useTiptapEditorEventCallback } from "./hooks/useTiptapEditorEventCallba
|
|
|
152
157
|
contentDOMRef,
|
|
153
158
|
node.type.name
|
|
154
159
|
]);
|
|
160
|
+
const innerRef = useRef(null);
|
|
161
|
+
const finalRef = useMergedDOMRefs(ref, innerRef);
|
|
155
162
|
const onDragStart = useTiptapEditorEventCallback((editor, event)=>{
|
|
156
|
-
|
|
157
|
-
// ref, I'm being lazy since we are providing this
|
|
158
|
-
// ref in the first place (in ReactNodeView), so we know
|
|
159
|
-
// it's an object
|
|
160
|
-
const dom = typeof ref === "object" ? ref?.current : null;
|
|
163
|
+
const dom = innerRef.current;
|
|
161
164
|
if (!dom) return;
|
|
162
165
|
const viewDesc = dom.pmViewDesc;
|
|
163
166
|
if (!viewDesc) return;
|
|
@@ -184,7 +187,7 @@ import { useTiptapEditorEventCallback } from "./hooks/useTiptapEditorEventCallba
|
|
|
184
187
|
return /*#__PURE__*/ React.createElement(ReactNodeViewContext.Provider, {
|
|
185
188
|
value: nodeViewContext
|
|
186
189
|
}, /*#__PURE__*/ React.createElement(OuterTag, {
|
|
187
|
-
ref:
|
|
190
|
+
ref: finalRef,
|
|
188
191
|
...props,
|
|
189
192
|
...htmlProps,
|
|
190
193
|
className: finalClassName
|