@tiptap/react 2.0.0-beta.19 → 2.0.0-beta.194
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/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 +1 -1
- package/dist/packages/react/src/NodeViewWrapper.d.ts +1 -1
- package/dist/packages/react/src/ReactNodeViewRenderer.d.ts +12 -6
- package/dist/packages/react/src/ReactRenderer.d.ts +12 -9
- 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 -0
- package/dist/tiptap-react.cjs.js +225 -154
- package/dist/tiptap-react.cjs.js.map +1 -1
- package/dist/tiptap-react.esm.js +225 -152
- package/dist/tiptap-react.esm.js.map +1 -1
- package/dist/tiptap-react.umd.js +227 -156
- package/dist/tiptap-react.umd.js.map +1 -1
- package/package.json +18 -12
- 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 -3
- package/src/NodeViewWrapper.tsx +11 -8
- package/src/ReactNodeViewRenderer.tsx +76 -32
- package/src/ReactRenderer.tsx +56 -27
- package/src/index.ts +6 -6
- package/src/useEditor.ts +16 -4
- package/src/useReactNodeView.ts +1 -0
- package/CHANGELOG.md +0 -170
- package/LICENSE.md +0 -21
- 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.194",
|
|
5
5
|
"homepage": "https://tiptap.dev",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"tiptap",
|
|
@@ -15,25 +15,31 @@
|
|
|
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
|
-
"@tiptap/core": "^2.0.0-beta.
|
|
26
|
-
"react": "^17.0.
|
|
27
|
-
"react-dom": "^17.0.
|
|
30
|
+
"@tiptap/core": "^2.0.0-beta.193",
|
|
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.194",
|
|
36
|
+
"@tiptap/extension-floating-menu": "^2.0.0-beta.194",
|
|
37
|
+
"prosemirror-view": "1.26.2"
|
|
33
38
|
},
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "https://github.com/ueberdosis/tiptap",
|
|
42
|
+
"directory": "packages/react"
|
|
37
43
|
},
|
|
38
|
-
"
|
|
44
|
+
"sideEffects": false
|
|
39
45
|
}
|
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,18 +1,25 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
|
|
3
|
+
import { useReactNodeView } from './useReactNodeView'
|
|
4
|
+
|
|
3
5
|
export interface NodeViewContentProps {
|
|
4
|
-
|
|
6
|
+
[key: string]: any,
|
|
5
7
|
as?: React.ElementType,
|
|
6
8
|
}
|
|
7
9
|
|
|
8
10
|
export const NodeViewContent: React.FC<NodeViewContentProps> = props => {
|
|
9
11
|
const Tag = props.as || 'div'
|
|
12
|
+
const { nodeViewContentRef } = useReactNodeView()
|
|
10
13
|
|
|
11
14
|
return (
|
|
12
15
|
<Tag
|
|
13
|
-
|
|
16
|
+
{...props}
|
|
17
|
+
ref={nodeViewContentRef}
|
|
14
18
|
data-node-view-content=""
|
|
15
|
-
style={{
|
|
19
|
+
style={{
|
|
20
|
+
whiteSpace: 'pre-wrap',
|
|
21
|
+
...props.style,
|
|
22
|
+
}}
|
|
16
23
|
/>
|
|
17
24
|
)
|
|
18
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
|
+
[key: string]: any,
|
|
6
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,26 +1,40 @@
|
|
|
1
|
-
import React 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
|
|
|
23
|
-
contentDOMElement!:
|
|
37
|
+
contentDOMElement!: HTMLElement | null
|
|
24
38
|
|
|
25
39
|
mount() {
|
|
26
40
|
const props: NodeViewProps = {
|
|
@@ -31,6 +45,7 @@ class ReactNodeView extends NodeView<React.FunctionComponent, Editor> {
|
|
|
31
45
|
extension: this.extension,
|
|
32
46
|
getPos: () => this.getPos(),
|
|
33
47
|
updateAttributes: (attributes = {}) => this.updateAttributes(attributes),
|
|
48
|
+
deleteNode: () => this.deleteNode(),
|
|
34
49
|
}
|
|
35
50
|
|
|
36
51
|
if (!(this.component as any).displayName) {
|
|
@@ -38,15 +53,20 @@ class ReactNodeView extends NodeView<React.FunctionComponent, Editor> {
|
|
|
38
53
|
return string.charAt(0).toUpperCase() + string.substring(1)
|
|
39
54
|
}
|
|
40
55
|
|
|
41
|
-
this.component.displayName = capitalizeFirstChar(this.extension.
|
|
56
|
+
this.component.displayName = capitalizeFirstChar(this.extension.name)
|
|
42
57
|
}
|
|
43
58
|
|
|
44
59
|
const ReactNodeViewProvider: React.FunctionComponent = componentProps => {
|
|
45
|
-
const onDragStart = this.onDragStart.bind(this)
|
|
46
60
|
const Component = this.component
|
|
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)
|
|
65
|
+
}
|
|
66
|
+
}
|
|
47
67
|
|
|
48
68
|
return (
|
|
49
|
-
<ReactNodeViewContext.Provider value={{ onDragStart }}>
|
|
69
|
+
<ReactNodeViewContext.Provider value={{ onDragStart, nodeViewContentRef }}>
|
|
50
70
|
<Component {...componentProps} />
|
|
51
71
|
</ReactNodeViewContext.Provider>
|
|
52
72
|
)
|
|
@@ -58,12 +78,26 @@ class ReactNodeView extends NodeView<React.FunctionComponent, Editor> {
|
|
|
58
78
|
? null
|
|
59
79
|
: document.createElement(this.node.isInline ? 'span' : 'div')
|
|
60
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
|
+
|
|
61
96
|
this.renderer = new ReactRenderer(ReactNodeViewProvider, {
|
|
62
97
|
editor: this.editor,
|
|
63
98
|
props,
|
|
64
|
-
as
|
|
65
|
-
|
|
66
|
-
: 'div',
|
|
99
|
+
as,
|
|
100
|
+
className: `node-${this.node.type.name} ${className}`.trim(),
|
|
67
101
|
})
|
|
68
102
|
}
|
|
69
103
|
|
|
@@ -75,7 +109,7 @@ class ReactNodeView extends NodeView<React.FunctionComponent, Editor> {
|
|
|
75
109
|
throw Error('Please use the NodeViewWrapper component for your node view.')
|
|
76
110
|
}
|
|
77
111
|
|
|
78
|
-
return this.renderer.element
|
|
112
|
+
return this.renderer.element as HTMLElement
|
|
79
113
|
}
|
|
80
114
|
|
|
81
115
|
get contentDOM() {
|
|
@@ -83,35 +117,42 @@ class ReactNodeView extends NodeView<React.FunctionComponent, Editor> {
|
|
|
83
117
|
return null
|
|
84
118
|
}
|
|
85
119
|
|
|
86
|
-
const contentElement = this.dom.querySelector('[data-node-view-content]')
|
|
87
|
-
|
|
88
|
-
if (
|
|
89
|
-
this.contentDOMElement
|
|
90
|
-
&& contentElement
|
|
91
|
-
&& !contentElement.contains(this.contentDOMElement)
|
|
92
|
-
) {
|
|
93
|
-
contentElement.appendChild(this.contentDOMElement)
|
|
94
|
-
}
|
|
95
|
-
|
|
96
120
|
return this.contentDOMElement
|
|
97
121
|
}
|
|
98
122
|
|
|
99
123
|
update(node: ProseMirrorNode, decorations: Decoration[]) {
|
|
100
|
-
|
|
101
|
-
|
|
124
|
+
const updateProps = (props?: Record<string, any>) => {
|
|
125
|
+
this.renderer.updateProps(props)
|
|
102
126
|
}
|
|
103
127
|
|
|
104
128
|
if (node.type !== this.node.type) {
|
|
105
129
|
return false
|
|
106
130
|
}
|
|
107
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
|
+
|
|
108
148
|
if (node === this.node && this.decorations === decorations) {
|
|
109
149
|
return true
|
|
110
150
|
}
|
|
111
151
|
|
|
112
152
|
this.node = node
|
|
113
153
|
this.decorations = decorations
|
|
114
|
-
|
|
154
|
+
|
|
155
|
+
updateProps({ node, decorations })
|
|
115
156
|
|
|
116
157
|
return true
|
|
117
158
|
}
|
|
@@ -134,7 +175,10 @@ class ReactNodeView extends NodeView<React.FunctionComponent, Editor> {
|
|
|
134
175
|
}
|
|
135
176
|
}
|
|
136
177
|
|
|
137
|
-
export function ReactNodeViewRenderer(
|
|
178
|
+
export function ReactNodeViewRenderer(
|
|
179
|
+
component: any,
|
|
180
|
+
options?: Partial<ReactNodeViewRendererOptions>,
|
|
181
|
+
): NodeViewRenderer {
|
|
138
182
|
return (props: NodeViewRendererProps) => {
|
|
139
183
|
// try to get the parent component
|
|
140
184
|
// this is important for vue devtools to show the component hierarchy correctly
|
|
@@ -143,6 +187,6 @@ export function ReactNodeViewRenderer(component: any, options?: Partial<ReactNod
|
|
|
143
187
|
return {}
|
|
144
188
|
}
|
|
145
189
|
|
|
146
|
-
return new ReactNodeView(component, props, options) as ProseMirrorNodeView
|
|
190
|
+
return new ReactNodeView(component, props, options) as unknown as ProseMirrorNodeView
|
|
147
191
|
}
|
|
148
192
|
}
|
package/src/ReactRenderer.tsx
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { Editor } from '@tiptap/core'
|
|
1
2
|
import React from 'react'
|
|
2
|
-
import {
|
|
3
|
-
|
|
3
|
+
import { flushSync } from 'react-dom'
|
|
4
|
+
|
|
5
|
+
import { Editor as ExtendedEditor } from './Editor'
|
|
4
6
|
|
|
5
7
|
function isClassComponent(Component: any) {
|
|
6
8
|
return !!(
|
|
@@ -10,34 +12,57 @@ function isClassComponent(Component: any) {
|
|
|
10
12
|
)
|
|
11
13
|
}
|
|
12
14
|
|
|
15
|
+
function isForwardRefComponent(Component: any) {
|
|
16
|
+
return !!(
|
|
17
|
+
typeof Component === 'object'
|
|
18
|
+
&& Component.$$typeof?.toString() === 'Symbol(react.forward_ref)'
|
|
19
|
+
)
|
|
20
|
+
}
|
|
21
|
+
|
|
13
22
|
export interface ReactRendererOptions {
|
|
14
23
|
editor: Editor,
|
|
15
|
-
props?:
|
|
24
|
+
props?: Record<string, any>,
|
|
16
25
|
as?: string,
|
|
26
|
+
className?: string,
|
|
17
27
|
}
|
|
18
28
|
|
|
19
|
-
|
|
29
|
+
type ComponentType<R, P> =
|
|
30
|
+
React.ComponentClass<P> |
|
|
31
|
+
React.FunctionComponent<P> |
|
|
32
|
+
React.ForwardRefExoticComponent<React.PropsWithoutRef<P> & React.RefAttributes<R>>;
|
|
33
|
+
|
|
34
|
+
export class ReactRenderer<R = unknown, P = unknown> {
|
|
20
35
|
id: string
|
|
21
36
|
|
|
22
|
-
editor:
|
|
37
|
+
editor: ExtendedEditor
|
|
23
38
|
|
|
24
39
|
component: any
|
|
25
40
|
|
|
26
41
|
element: Element
|
|
27
42
|
|
|
28
|
-
props:
|
|
43
|
+
props: Record<string, any>
|
|
29
44
|
|
|
30
45
|
reactElement: React.ReactNode
|
|
31
46
|
|
|
32
|
-
ref:
|
|
47
|
+
ref: R | null = null
|
|
33
48
|
|
|
34
|
-
constructor(component:
|
|
49
|
+
constructor(component: ComponentType<R, P>, {
|
|
50
|
+
editor,
|
|
51
|
+
props = {},
|
|
52
|
+
as = 'div',
|
|
53
|
+
className = '',
|
|
54
|
+
}: ReactRendererOptions) {
|
|
35
55
|
this.id = Math.floor(Math.random() * 0xFFFFFFFF).toString()
|
|
36
56
|
this.component = component
|
|
37
|
-
this.editor = editor
|
|
57
|
+
this.editor = editor as ExtendedEditor
|
|
38
58
|
this.props = props
|
|
39
59
|
this.element = document.createElement(as)
|
|
40
60
|
this.element.classList.add('react-renderer')
|
|
61
|
+
|
|
62
|
+
if (className) {
|
|
63
|
+
this.element.classList.add(...className.split(' '))
|
|
64
|
+
}
|
|
65
|
+
|
|
41
66
|
this.render()
|
|
42
67
|
}
|
|
43
68
|
|
|
@@ -45,25 +70,27 @@ export class ReactRenderer {
|
|
|
45
70
|
const Component = this.component
|
|
46
71
|
const props = this.props
|
|
47
72
|
|
|
48
|
-
if (isClassComponent(Component)) {
|
|
49
|
-
props.ref = (ref:
|
|
73
|
+
if (isClassComponent(Component) || isForwardRefComponent(Component)) {
|
|
74
|
+
props.ref = (ref: R) => {
|
|
50
75
|
this.ref = ref
|
|
51
76
|
}
|
|
52
77
|
}
|
|
53
78
|
|
|
54
79
|
this.reactElement = <Component {...props } />
|
|
55
80
|
|
|
56
|
-
|
|
57
|
-
this.editor
|
|
58
|
-
|
|
59
|
-
this.
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
81
|
+
flushSync(() => {
|
|
82
|
+
if (this.editor?.contentComponent) {
|
|
83
|
+
this.editor.contentComponent.setState({
|
|
84
|
+
renderers: this.editor.contentComponent.state.renderers.set(
|
|
85
|
+
this.id,
|
|
86
|
+
this,
|
|
87
|
+
),
|
|
88
|
+
})
|
|
89
|
+
}
|
|
90
|
+
})
|
|
64
91
|
}
|
|
65
92
|
|
|
66
|
-
updateProps(props:
|
|
93
|
+
updateProps(props: Record<string, any> = {}): void {
|
|
67
94
|
this.props = {
|
|
68
95
|
...this.props,
|
|
69
96
|
...props,
|
|
@@ -73,14 +100,16 @@ export class ReactRenderer {
|
|
|
73
100
|
}
|
|
74
101
|
|
|
75
102
|
destroy(): void {
|
|
76
|
-
|
|
77
|
-
|
|
103
|
+
flushSync(() => {
|
|
104
|
+
if (this.editor?.contentComponent) {
|
|
105
|
+
const { renderers } = this.editor.contentComponent.state
|
|
78
106
|
|
|
79
|
-
|
|
107
|
+
renderers.delete(this.id)
|
|
80
108
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
109
|
+
this.editor.contentComponent.setState({
|
|
110
|
+
renderers,
|
|
111
|
+
})
|
|
112
|
+
}
|
|
113
|
+
})
|
|
85
114
|
}
|
|
86
115
|
}
|
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'
|