@tiptap/react 2.0.0-beta.11 → 2.0.0-beta.114
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/LICENSE.md +1 -1
- package/README.md +2 -2
- package/dist/packages/react/src/BubbleMenu.d.ts +6 -3
- package/dist/packages/react/src/Editor.d.ts +1 -1
- package/dist/packages/react/src/EditorContent.d.ts +2 -2
- package/dist/packages/react/src/FloatingMenu.d.ts +6 -3
- package/dist/packages/react/src/NodeViewContent.d.ts +2 -2
- package/dist/packages/react/src/NodeViewWrapper.d.ts +2 -2
- package/dist/packages/react/src/ReactNodeViewRenderer.d.ts +12 -6
- package/dist/packages/react/src/ReactRenderer.d.ts +13 -10
- package/dist/packages/react/src/index.d.ts +6 -6
- package/dist/packages/react/src/useEditor.d.ts +2 -1
- package/dist/packages/react/src/useReactNodeView.d.ts +1 -1
- package/dist/tiptap-react.cjs.js +216 -147
- package/dist/tiptap-react.cjs.js.map +1 -1
- package/dist/tiptap-react.esm.js +215 -144
- package/dist/tiptap-react.esm.js.map +1 -1
- package/dist/tiptap-react.umd.js +218 -149
- package/dist/tiptap-react.umd.js.map +1 -1
- package/package.json +18 -10
- package/src/BubbleMenu.tsx +37 -15
- package/src/Editor.ts +2 -1
- package/src/EditorContent.tsx +9 -7
- package/src/FloatingMenu.tsx +37 -14
- package/src/NodeViewContent.tsx +10 -6
- package/src/NodeViewWrapper.tsx +12 -9
- package/src/ReactNodeViewRenderer.tsx +87 -33
- package/src/ReactRenderer.tsx +38 -14
- package/src/index.ts +6 -6
- package/src/useEditor.ts +16 -4
- package/src/useReactNodeView.ts +1 -2
- package/CHANGELOG.md +0 -96
- package/dist/tiptap-react.bundle.umd.min.js +0 -54
- package/dist/tiptap-react.bundle.umd.min.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tiptap/react",
|
|
3
3
|
"description": "React components for tiptap",
|
|
4
|
-
"version": "2.0.0-beta.
|
|
4
|
+
"version": "2.0.0-beta.114",
|
|
5
5
|
"homepage": "https://tiptap.dev",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"tiptap",
|
|
@@ -15,24 +15,32 @@
|
|
|
15
15
|
"main": "dist/tiptap-react.cjs.js",
|
|
16
16
|
"umd": "dist/tiptap-react.umd.js",
|
|
17
17
|
"module": "dist/tiptap-react.esm.js",
|
|
18
|
-
"unpkg": "dist/tiptap-react.bundle.umd.min.js",
|
|
19
18
|
"types": "dist/packages/react/src/index.d.ts",
|
|
20
19
|
"files": [
|
|
21
20
|
"src",
|
|
22
21
|
"dist"
|
|
23
22
|
],
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@types/react": "^18.0.1",
|
|
25
|
+
"@types/react-dom": "^18.0.0",
|
|
26
|
+
"react": "^18.0.0",
|
|
27
|
+
"react-dom": "^18.0.0"
|
|
28
|
+
},
|
|
24
29
|
"peerDependencies": {
|
|
25
30
|
"@tiptap/core": "^2.0.0-beta.1",
|
|
26
|
-
"react": "^17.0.
|
|
27
|
-
"react-dom": "^17.0.
|
|
31
|
+
"react": "^17.0.0 || ^18.0.0",
|
|
32
|
+
"react-dom": "^17.0.0 || ^18.0.0"
|
|
28
33
|
},
|
|
29
34
|
"dependencies": {
|
|
30
|
-
"@tiptap/extension-bubble-menu": "^2.0.0-beta.
|
|
31
|
-
"@tiptap/extension-floating-menu": "^2.0.0-beta.
|
|
32
|
-
"prosemirror-view": "
|
|
35
|
+
"@tiptap/extension-bubble-menu": "^2.0.0-beta.61",
|
|
36
|
+
"@tiptap/extension-floating-menu": "^2.0.0-beta.56",
|
|
37
|
+
"prosemirror-view": "1.26.2"
|
|
33
38
|
},
|
|
34
|
-
"
|
|
35
|
-
"
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "https://github.com/ueberdosis/tiptap",
|
|
42
|
+
"directory": "packages/react"
|
|
36
43
|
},
|
|
37
|
-
"
|
|
44
|
+
"sideEffects": false,
|
|
45
|
+
"gitHead": "090c5a44566bcd936096574fa62bf301ab4a1285"
|
|
38
46
|
}
|
package/src/BubbleMenu.tsx
CHANGED
|
@@ -1,29 +1,51 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import { BubbleMenuPlugin, BubbleMenuPluginProps } from '@tiptap/extension-bubble-menu'
|
|
2
|
+
import React, {
|
|
3
|
+
useEffect, useState,
|
|
4
|
+
} from 'react'
|
|
3
5
|
|
|
4
|
-
|
|
6
|
+
type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>
|
|
7
|
+
|
|
8
|
+
export type BubbleMenuProps = Omit<Optional<BubbleMenuPluginProps, 'pluginKey'>, 'element'> & {
|
|
5
9
|
className?: string,
|
|
10
|
+
children: React.ReactNode
|
|
6
11
|
}
|
|
7
12
|
|
|
8
|
-
export const BubbleMenu
|
|
9
|
-
const element =
|
|
13
|
+
export const BubbleMenu = (props: BubbleMenuProps) => {
|
|
14
|
+
const [element, setElement] = useState<HTMLDivElement | null>(null)
|
|
10
15
|
|
|
11
16
|
useEffect(() => {
|
|
12
|
-
|
|
17
|
+
if (!element) {
|
|
18
|
+
return
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (props.editor.isDestroyed) {
|
|
22
|
+
return
|
|
23
|
+
}
|
|
13
24
|
|
|
14
|
-
|
|
25
|
+
const {
|
|
26
|
+
pluginKey = 'bubbleMenu',
|
|
15
27
|
editor,
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
28
|
+
tippyOptions = {},
|
|
29
|
+
shouldShow = null,
|
|
30
|
+
} = props
|
|
19
31
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
32
|
+
const plugin = BubbleMenuPlugin({
|
|
33
|
+
pluginKey,
|
|
34
|
+
editor,
|
|
35
|
+
element,
|
|
36
|
+
tippyOptions,
|
|
37
|
+
shouldShow,
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
editor.registerPlugin(plugin)
|
|
41
|
+
return () => editor.unregisterPlugin(pluginKey)
|
|
42
|
+
}, [
|
|
43
|
+
props.editor,
|
|
44
|
+
element,
|
|
45
|
+
])
|
|
24
46
|
|
|
25
47
|
return (
|
|
26
|
-
<div ref={
|
|
48
|
+
<div ref={setElement} className={props.className} style={{ visibility: 'hidden' }}>
|
|
27
49
|
{props.children}
|
|
28
50
|
</div>
|
|
29
51
|
)
|
package/src/Editor.ts
CHANGED
package/src/EditorContent.tsx
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import React from 'react'
|
|
1
|
+
import React, { HTMLProps } from 'react'
|
|
2
2
|
import ReactDOM from 'react-dom'
|
|
3
|
+
|
|
3
4
|
import { Editor } from './Editor'
|
|
4
5
|
import { ReactRenderer } from './ReactRenderer'
|
|
5
6
|
|
|
@@ -17,7 +18,7 @@ const Portals: React.FC<{ renderers: Map<string, ReactRenderer> }> = ({ renderer
|
|
|
17
18
|
)
|
|
18
19
|
}
|
|
19
20
|
|
|
20
|
-
export interface EditorContentProps {
|
|
21
|
+
export interface EditorContentProps extends HTMLProps<HTMLDivElement> {
|
|
21
22
|
editor: Editor | null,
|
|
22
23
|
}
|
|
23
24
|
|
|
@@ -55,7 +56,7 @@ export class PureEditorContent extends React.Component<EditorContentProps, Edito
|
|
|
55
56
|
|
|
56
57
|
const element = this.editorContentRef.current
|
|
57
58
|
|
|
58
|
-
element.
|
|
59
|
+
element.append(...editor.options.element.childNodes)
|
|
59
60
|
|
|
60
61
|
editor.setOptions({
|
|
61
62
|
element,
|
|
@@ -63,8 +64,7 @@ export class PureEditorContent extends React.Component<EditorContentProps, Edito
|
|
|
63
64
|
|
|
64
65
|
editor.contentComponent = this
|
|
65
66
|
|
|
66
|
-
|
|
67
|
-
setTimeout(() => editor.createNodeViews(), 0)
|
|
67
|
+
editor.createNodeViews()
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
70
|
|
|
@@ -89,7 +89,7 @@ export class PureEditorContent extends React.Component<EditorContentProps, Edito
|
|
|
89
89
|
|
|
90
90
|
const newElement = document.createElement('div')
|
|
91
91
|
|
|
92
|
-
newElement.
|
|
92
|
+
newElement.append(...editor.options.element.childNodes)
|
|
93
93
|
|
|
94
94
|
editor.setOptions({
|
|
95
95
|
element: newElement,
|
|
@@ -97,9 +97,11 @@ export class PureEditorContent extends React.Component<EditorContentProps, Edito
|
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
render() {
|
|
100
|
+
const { editor, ...rest } = this.props
|
|
101
|
+
|
|
100
102
|
return (
|
|
101
103
|
<>
|
|
102
|
-
<div ref={this.editorContentRef} />
|
|
104
|
+
<div ref={this.editorContentRef} {...rest} />
|
|
103
105
|
<Portals renderers={this.state.renderers} />
|
|
104
106
|
</>
|
|
105
107
|
)
|
package/src/FloatingMenu.tsx
CHANGED
|
@@ -1,28 +1,51 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import { FloatingMenuPlugin, FloatingMenuPluginProps } from '@tiptap/extension-floating-menu'
|
|
2
|
+
import React, {
|
|
3
|
+
useEffect, useState,
|
|
4
|
+
} from 'react'
|
|
3
5
|
|
|
4
|
-
|
|
6
|
+
type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>
|
|
7
|
+
|
|
8
|
+
export type FloatingMenuProps = Omit<Optional<FloatingMenuPluginProps, 'pluginKey'>, 'element'> & {
|
|
5
9
|
className?: string,
|
|
10
|
+
children: React.ReactNode
|
|
6
11
|
}
|
|
7
12
|
|
|
8
|
-
export const FloatingMenu
|
|
9
|
-
const element =
|
|
13
|
+
export const FloatingMenu = (props: FloatingMenuProps) => {
|
|
14
|
+
const [element, setElement] = useState<HTMLDivElement | null>(null)
|
|
10
15
|
|
|
11
16
|
useEffect(() => {
|
|
12
|
-
|
|
17
|
+
if (!element) {
|
|
18
|
+
return
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (props.editor.isDestroyed) {
|
|
22
|
+
return
|
|
23
|
+
}
|
|
13
24
|
|
|
14
|
-
|
|
25
|
+
const {
|
|
26
|
+
pluginKey = 'floatingMenu',
|
|
15
27
|
editor,
|
|
16
|
-
|
|
17
|
-
|
|
28
|
+
tippyOptions = {},
|
|
29
|
+
shouldShow = null,
|
|
30
|
+
} = props
|
|
18
31
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
32
|
+
const plugin = FloatingMenuPlugin({
|
|
33
|
+
pluginKey,
|
|
34
|
+
editor,
|
|
35
|
+
element,
|
|
36
|
+
tippyOptions,
|
|
37
|
+
shouldShow,
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
editor.registerPlugin(plugin)
|
|
41
|
+
return () => editor.unregisterPlugin(pluginKey)
|
|
42
|
+
}, [
|
|
43
|
+
props.editor,
|
|
44
|
+
element,
|
|
45
|
+
])
|
|
23
46
|
|
|
24
47
|
return (
|
|
25
|
-
<div ref={
|
|
48
|
+
<div ref={setElement} className={props.className} style={{ visibility: 'hidden' }}>
|
|
26
49
|
{props.children}
|
|
27
50
|
</div>
|
|
28
51
|
)
|
package/src/NodeViewContent.tsx
CHANGED
|
@@ -1,21 +1,25 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
|
+
|
|
2
3
|
import { useReactNodeView } from './useReactNodeView'
|
|
3
4
|
|
|
4
5
|
export interface NodeViewContentProps {
|
|
5
|
-
|
|
6
|
-
as
|
|
6
|
+
[key: string]: any,
|
|
7
|
+
as?: React.ElementType,
|
|
7
8
|
}
|
|
8
9
|
|
|
9
10
|
export const NodeViewContent: React.FC<NodeViewContentProps> = props => {
|
|
10
|
-
const { isEditable } = useReactNodeView()
|
|
11
11
|
const Tag = props.as || 'div'
|
|
12
|
+
const { nodeViewContentRef } = useReactNodeView()
|
|
12
13
|
|
|
13
14
|
return (
|
|
14
15
|
<Tag
|
|
15
|
-
|
|
16
|
+
{...props}
|
|
17
|
+
ref={nodeViewContentRef}
|
|
16
18
|
data-node-view-content=""
|
|
17
|
-
|
|
18
|
-
|
|
19
|
+
style={{
|
|
20
|
+
whiteSpace: 'pre-wrap',
|
|
21
|
+
...props.style,
|
|
22
|
+
}}
|
|
19
23
|
/>
|
|
20
24
|
)
|
|
21
25
|
}
|
package/src/NodeViewWrapper.tsx
CHANGED
|
@@ -1,23 +1,26 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
|
+
|
|
2
3
|
import { useReactNodeView } from './useReactNodeView'
|
|
3
4
|
|
|
4
5
|
export interface NodeViewWrapperProps {
|
|
5
|
-
|
|
6
|
-
as
|
|
6
|
+
[key: string]: any,
|
|
7
|
+
as?: React.ElementType,
|
|
7
8
|
}
|
|
8
9
|
|
|
9
|
-
export const NodeViewWrapper: React.FC<NodeViewWrapperProps> = props => {
|
|
10
|
+
export const NodeViewWrapper: React.FC<NodeViewWrapperProps> = React.forwardRef((props, ref) => {
|
|
10
11
|
const { onDragStart } = useReactNodeView()
|
|
11
12
|
const Tag = props.as || 'div'
|
|
12
13
|
|
|
13
14
|
return (
|
|
14
15
|
<Tag
|
|
15
|
-
|
|
16
|
+
{...props}
|
|
17
|
+
ref={ref}
|
|
16
18
|
data-node-view-wrapper=""
|
|
17
19
|
onDragStart={onDragStart}
|
|
18
|
-
style={{
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
style={{
|
|
21
|
+
whiteSpace: 'normal',
|
|
22
|
+
...props.style,
|
|
23
|
+
}}
|
|
24
|
+
/>
|
|
22
25
|
)
|
|
23
|
-
}
|
|
26
|
+
})
|
|
@@ -1,25 +1,41 @@
|
|
|
1
|
-
import React, { useState, useEffect } from 'react'
|
|
2
1
|
import {
|
|
3
2
|
NodeView,
|
|
4
3
|
NodeViewProps,
|
|
5
4
|
NodeViewRenderer,
|
|
5
|
+
NodeViewRendererOptions,
|
|
6
6
|
NodeViewRendererProps,
|
|
7
7
|
} from '@tiptap/core'
|
|
8
|
-
import { Decoration, NodeView as ProseMirrorNodeView } from 'prosemirror-view'
|
|
9
8
|
import { Node as ProseMirrorNode } from 'prosemirror-model'
|
|
9
|
+
import { Decoration, NodeView as ProseMirrorNodeView } from 'prosemirror-view'
|
|
10
|
+
import React from 'react'
|
|
11
|
+
|
|
10
12
|
import { Editor } from './Editor'
|
|
11
13
|
import { ReactRenderer } from './ReactRenderer'
|
|
12
|
-
import { ReactNodeViewContext } from './useReactNodeView'
|
|
13
|
-
|
|
14
|
-
interface ReactNodeViewRendererOptions {
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
import { ReactNodeViewContext, ReactNodeViewContextProps } from './useReactNodeView'
|
|
15
|
+
|
|
16
|
+
export interface ReactNodeViewRendererOptions extends NodeViewRendererOptions {
|
|
17
|
+
update:
|
|
18
|
+
| ((props: {
|
|
19
|
+
oldNode: ProseMirrorNode;
|
|
20
|
+
oldDecorations: Decoration[];
|
|
21
|
+
newNode: ProseMirrorNode;
|
|
22
|
+
newDecorations: Decoration[];
|
|
23
|
+
updateProps: () => void;
|
|
24
|
+
}) => boolean)
|
|
25
|
+
| null;
|
|
26
|
+
as?: string;
|
|
27
|
+
className?: string;
|
|
17
28
|
}
|
|
18
29
|
|
|
19
|
-
class ReactNodeView extends NodeView<
|
|
20
|
-
|
|
30
|
+
class ReactNodeView extends NodeView<
|
|
31
|
+
React.FunctionComponent,
|
|
32
|
+
Editor,
|
|
33
|
+
ReactNodeViewRendererOptions
|
|
34
|
+
> {
|
|
21
35
|
renderer!: ReactRenderer
|
|
22
36
|
|
|
37
|
+
contentDOMElement!: HTMLElement | null
|
|
38
|
+
|
|
23
39
|
mount() {
|
|
24
40
|
const props: NodeViewProps = {
|
|
25
41
|
editor: this.editor,
|
|
@@ -29,6 +45,7 @@ class ReactNodeView extends NodeView<React.FunctionComponent, Editor> {
|
|
|
29
45
|
extension: this.extension,
|
|
30
46
|
getPos: () => this.getPos(),
|
|
31
47
|
updateAttributes: (attributes = {}) => this.updateAttributes(attributes),
|
|
48
|
+
deleteNode: () => this.deleteNode(),
|
|
32
49
|
}
|
|
33
50
|
|
|
34
51
|
if (!(this.component as any).displayName) {
|
|
@@ -36,26 +53,20 @@ class ReactNodeView extends NodeView<React.FunctionComponent, Editor> {
|
|
|
36
53
|
return string.charAt(0).toUpperCase() + string.substring(1)
|
|
37
54
|
}
|
|
38
55
|
|
|
39
|
-
|
|
40
|
-
this.component.displayName = capitalizeFirstChar(this.extension.config.name)
|
|
56
|
+
this.component.displayName = capitalizeFirstChar(this.extension.name)
|
|
41
57
|
}
|
|
42
58
|
|
|
43
59
|
const ReactNodeViewProvider: React.FunctionComponent = componentProps => {
|
|
44
|
-
const [isEditable, setIsEditable] = useState(this.editor.isEditable)
|
|
45
|
-
const onDragStart = this.onDragStart.bind(this)
|
|
46
|
-
const onViewUpdate = () => setIsEditable(this.editor.isEditable)
|
|
47
60
|
const Component = this.component
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
this.
|
|
51
|
-
|
|
52
|
-
return () => {
|
|
53
|
-
this.editor.off('viewUpdate', onViewUpdate)
|
|
61
|
+
const onDragStart = this.onDragStart.bind(this)
|
|
62
|
+
const nodeViewContentRef: ReactNodeViewContextProps['nodeViewContentRef'] = element => {
|
|
63
|
+
if (element && this.contentDOMElement && element.firstChild !== this.contentDOMElement) {
|
|
64
|
+
element.appendChild(this.contentDOMElement)
|
|
54
65
|
}
|
|
55
|
-
}
|
|
66
|
+
}
|
|
56
67
|
|
|
57
68
|
return (
|
|
58
|
-
<ReactNodeViewContext.Provider value={{ onDragStart,
|
|
69
|
+
<ReactNodeViewContext.Provider value={{ onDragStart, nodeViewContentRef }}>
|
|
59
70
|
<Component {...componentProps} />
|
|
60
71
|
</ReactNodeViewContext.Provider>
|
|
61
72
|
)
|
|
@@ -63,18 +74,42 @@ class ReactNodeView extends NodeView<React.FunctionComponent, Editor> {
|
|
|
63
74
|
|
|
64
75
|
ReactNodeViewProvider.displayName = 'ReactNodeView'
|
|
65
76
|
|
|
77
|
+
this.contentDOMElement = this.node.isLeaf
|
|
78
|
+
? null
|
|
79
|
+
: document.createElement(this.node.isInline ? 'span' : 'div')
|
|
80
|
+
|
|
81
|
+
if (this.contentDOMElement) {
|
|
82
|
+
// For some reason the whiteSpace prop is not inherited properly in Chrome and Safari
|
|
83
|
+
// With this fix it seems to work fine
|
|
84
|
+
// See: https://github.com/ueberdosis/tiptap/issues/1197
|
|
85
|
+
this.contentDOMElement.style.whiteSpace = 'inherit'
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
let as = this.node.isInline ? 'span' : 'div'
|
|
89
|
+
|
|
90
|
+
if (this.options.as) {
|
|
91
|
+
as = this.options.as
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const { className = '' } = this.options
|
|
95
|
+
|
|
66
96
|
this.renderer = new ReactRenderer(ReactNodeViewProvider, {
|
|
67
97
|
editor: this.editor,
|
|
68
98
|
props,
|
|
99
|
+
as,
|
|
100
|
+
className: `node-${this.node.type.name} ${className}`.trim(),
|
|
69
101
|
})
|
|
70
102
|
}
|
|
71
103
|
|
|
72
104
|
get dom() {
|
|
73
|
-
if (
|
|
74
|
-
|
|
105
|
+
if (
|
|
106
|
+
this.renderer.element.firstElementChild
|
|
107
|
+
&& !this.renderer.element.firstElementChild?.hasAttribute('data-node-view-wrapper')
|
|
108
|
+
) {
|
|
109
|
+
throw Error('Please use the NodeViewWrapper component for your node view.')
|
|
75
110
|
}
|
|
76
111
|
|
|
77
|
-
return this.renderer.element
|
|
112
|
+
return this.renderer.element as HTMLElement
|
|
78
113
|
}
|
|
79
114
|
|
|
80
115
|
get contentDOM() {
|
|
@@ -82,27 +117,42 @@ class ReactNodeView extends NodeView<React.FunctionComponent, Editor> {
|
|
|
82
117
|
return null
|
|
83
118
|
}
|
|
84
119
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
return contentElement || this.dom
|
|
120
|
+
return this.contentDOMElement
|
|
88
121
|
}
|
|
89
122
|
|
|
90
123
|
update(node: ProseMirrorNode, decorations: Decoration[]) {
|
|
91
|
-
|
|
92
|
-
|
|
124
|
+
const updateProps = (props?: Record<string, any>) => {
|
|
125
|
+
this.renderer.updateProps(props)
|
|
93
126
|
}
|
|
94
127
|
|
|
95
128
|
if (node.type !== this.node.type) {
|
|
96
129
|
return false
|
|
97
130
|
}
|
|
98
131
|
|
|
132
|
+
if (typeof this.options.update === 'function') {
|
|
133
|
+
const oldNode = this.node
|
|
134
|
+
const oldDecorations = this.decorations
|
|
135
|
+
|
|
136
|
+
this.node = node
|
|
137
|
+
this.decorations = decorations
|
|
138
|
+
|
|
139
|
+
return this.options.update({
|
|
140
|
+
oldNode,
|
|
141
|
+
oldDecorations,
|
|
142
|
+
newNode: node,
|
|
143
|
+
newDecorations: decorations,
|
|
144
|
+
updateProps: () => updateProps({ node, decorations }),
|
|
145
|
+
})
|
|
146
|
+
}
|
|
147
|
+
|
|
99
148
|
if (node === this.node && this.decorations === decorations) {
|
|
100
149
|
return true
|
|
101
150
|
}
|
|
102
151
|
|
|
103
152
|
this.node = node
|
|
104
153
|
this.decorations = decorations
|
|
105
|
-
|
|
154
|
+
|
|
155
|
+
updateProps({ node, decorations })
|
|
106
156
|
|
|
107
157
|
return true
|
|
108
158
|
}
|
|
@@ -121,10 +171,14 @@ class ReactNodeView extends NodeView<React.FunctionComponent, Editor> {
|
|
|
121
171
|
|
|
122
172
|
destroy() {
|
|
123
173
|
this.renderer.destroy()
|
|
174
|
+
this.contentDOMElement = null
|
|
124
175
|
}
|
|
125
176
|
}
|
|
126
177
|
|
|
127
|
-
export function ReactNodeViewRenderer(
|
|
178
|
+
export function ReactNodeViewRenderer(
|
|
179
|
+
component: any,
|
|
180
|
+
options?: Partial<ReactNodeViewRendererOptions>,
|
|
181
|
+
): NodeViewRenderer {
|
|
128
182
|
return (props: NodeViewRendererProps) => {
|
|
129
183
|
// try to get the parent component
|
|
130
184
|
// this is important for vue devtools to show the component hierarchy correctly
|
|
@@ -133,6 +187,6 @@ export function ReactNodeViewRenderer(component: any, options?: Partial<ReactNod
|
|
|
133
187
|
return {}
|
|
134
188
|
}
|
|
135
189
|
|
|
136
|
-
return new ReactNodeView(component, props, options) as ProseMirrorNodeView
|
|
190
|
+
return new ReactNodeView(component, props, options) as unknown as ProseMirrorNodeView
|
|
137
191
|
}
|
|
138
192
|
}
|
package/src/ReactRenderer.tsx
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { Editor } from '@tiptap/core'
|
|
1
2
|
import React from 'react'
|
|
2
|
-
|
|
3
|
-
import { Editor } from './Editor'
|
|
3
|
+
|
|
4
|
+
import { Editor as ExtendedEditor } from './Editor'
|
|
4
5
|
|
|
5
6
|
function isClassComponent(Component: any) {
|
|
6
7
|
return !!(
|
|
@@ -10,34 +11,57 @@ function isClassComponent(Component: any) {
|
|
|
10
11
|
)
|
|
11
12
|
}
|
|
12
13
|
|
|
14
|
+
function isForwardRefComponent(Component: any) {
|
|
15
|
+
return !!(
|
|
16
|
+
typeof Component === 'object'
|
|
17
|
+
&& Component.$$typeof?.toString() === 'Symbol(react.forward_ref)'
|
|
18
|
+
)
|
|
19
|
+
}
|
|
20
|
+
|
|
13
21
|
export interface ReactRendererOptions {
|
|
14
|
-
as?: string,
|
|
15
22
|
editor: Editor,
|
|
16
|
-
props?:
|
|
23
|
+
props?: Record<string, any>,
|
|
24
|
+
as?: string,
|
|
25
|
+
className?: string,
|
|
17
26
|
}
|
|
18
27
|
|
|
19
|
-
|
|
28
|
+
type ComponentType<R, P> =
|
|
29
|
+
React.ComponentClass<P> |
|
|
30
|
+
React.FunctionComponent<P> |
|
|
31
|
+
React.ForwardRefExoticComponent<React.PropsWithoutRef<P> & React.RefAttributes<R>>;
|
|
32
|
+
|
|
33
|
+
export class ReactRenderer<R = unknown, P = unknown> {
|
|
20
34
|
id: string
|
|
21
35
|
|
|
22
|
-
editor:
|
|
36
|
+
editor: ExtendedEditor
|
|
23
37
|
|
|
24
38
|
component: any
|
|
25
39
|
|
|
26
40
|
element: Element
|
|
27
41
|
|
|
28
|
-
props:
|
|
42
|
+
props: Record<string, any>
|
|
29
43
|
|
|
30
44
|
reactElement: React.ReactNode
|
|
31
45
|
|
|
32
|
-
ref:
|
|
46
|
+
ref: R | null = null
|
|
33
47
|
|
|
34
|
-
constructor(component:
|
|
48
|
+
constructor(component: ComponentType<R, P>, {
|
|
49
|
+
editor,
|
|
50
|
+
props = {},
|
|
51
|
+
as = 'div',
|
|
52
|
+
className = '',
|
|
53
|
+
}: ReactRendererOptions) {
|
|
35
54
|
this.id = Math.floor(Math.random() * 0xFFFFFFFF).toString()
|
|
36
55
|
this.component = component
|
|
37
|
-
this.editor = editor
|
|
56
|
+
this.editor = editor as ExtendedEditor
|
|
38
57
|
this.props = props
|
|
39
|
-
this.element = document.createElement(
|
|
58
|
+
this.element = document.createElement(as)
|
|
40
59
|
this.element.classList.add('react-renderer')
|
|
60
|
+
|
|
61
|
+
if (className) {
|
|
62
|
+
this.element.classList.add(...className.split(' '))
|
|
63
|
+
}
|
|
64
|
+
|
|
41
65
|
this.render()
|
|
42
66
|
}
|
|
43
67
|
|
|
@@ -45,8 +69,8 @@ export class ReactRenderer {
|
|
|
45
69
|
const Component = this.component
|
|
46
70
|
const props = this.props
|
|
47
71
|
|
|
48
|
-
if (isClassComponent(Component)) {
|
|
49
|
-
props.ref = (ref:
|
|
72
|
+
if (isClassComponent(Component) || isForwardRefComponent(Component)) {
|
|
73
|
+
props.ref = (ref: R) => {
|
|
50
74
|
this.ref = ref
|
|
51
75
|
}
|
|
52
76
|
}
|
|
@@ -63,7 +87,7 @@ export class ReactRenderer {
|
|
|
63
87
|
}
|
|
64
88
|
}
|
|
65
89
|
|
|
66
|
-
updateProps(props:
|
|
90
|
+
updateProps(props: Record<string, any> = {}): void {
|
|
67
91
|
this.props = {
|
|
68
92
|
...this.props,
|
|
69
93
|
...props,
|
package/src/index.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
export * from '@tiptap/core'
|
|
2
1
|
export * from './BubbleMenu'
|
|
3
2
|
export { Editor } from './Editor'
|
|
4
|
-
export * from './FloatingMenu'
|
|
5
|
-
export * from './useEditor'
|
|
6
|
-
export * from './ReactRenderer'
|
|
7
|
-
export * from './ReactNodeViewRenderer'
|
|
8
3
|
export * from './EditorContent'
|
|
9
|
-
export * from './
|
|
4
|
+
export * from './FloatingMenu'
|
|
10
5
|
export * from './NodeViewContent'
|
|
6
|
+
export * from './NodeViewWrapper'
|
|
7
|
+
export * from './ReactNodeViewRenderer'
|
|
8
|
+
export * from './ReactRenderer'
|
|
9
|
+
export * from './useEditor'
|
|
10
|
+
export * from '@tiptap/core'
|