@tiptap/react 2.0.0-beta.19 → 2.0.0-beta.195

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.
Files changed (34) hide show
  1. package/README.md +2 -2
  2. package/dist/packages/react/src/BubbleMenu.d.ts +6 -3
  3. package/dist/packages/react/src/Editor.d.ts +1 -1
  4. package/dist/packages/react/src/EditorContent.d.ts +2 -2
  5. package/dist/packages/react/src/FloatingMenu.d.ts +6 -3
  6. package/dist/packages/react/src/NodeViewContent.d.ts +1 -1
  7. package/dist/packages/react/src/NodeViewWrapper.d.ts +1 -1
  8. package/dist/packages/react/src/ReactNodeViewRenderer.d.ts +12 -6
  9. package/dist/packages/react/src/ReactRenderer.d.ts +12 -9
  10. package/dist/packages/react/src/index.d.ts +6 -6
  11. package/dist/packages/react/src/useEditor.d.ts +2 -1
  12. package/dist/packages/react/src/useReactNodeView.d.ts +1 -0
  13. package/dist/tiptap-react.cjs.js +227 -152
  14. package/dist/tiptap-react.cjs.js.map +1 -1
  15. package/dist/tiptap-react.esm.js +227 -150
  16. package/dist/tiptap-react.esm.js.map +1 -1
  17. package/dist/tiptap-react.umd.js +229 -154
  18. package/dist/tiptap-react.umd.js.map +1 -1
  19. package/package.json +18 -12
  20. package/src/BubbleMenu.tsx +37 -15
  21. package/src/Editor.ts +2 -1
  22. package/src/EditorContent.tsx +9 -7
  23. package/src/FloatingMenu.tsx +37 -14
  24. package/src/NodeViewContent.tsx +10 -3
  25. package/src/NodeViewWrapper.tsx +11 -8
  26. package/src/ReactNodeViewRenderer.tsx +76 -32
  27. package/src/ReactRenderer.tsx +58 -25
  28. package/src/index.ts +6 -6
  29. package/src/useEditor.ts +16 -4
  30. package/src/useReactNodeView.ts +1 -0
  31. package/CHANGELOG.md +0 -170
  32. package/LICENSE.md +0 -21
  33. package/dist/tiptap-react.bundle.umd.min.js +0 -54
  34. 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.19",
4
+ "version": "2.0.0-beta.195",
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.1",
26
- "react": "^17.0.1",
27
- "react-dom": "^17.0.1"
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.5",
31
- "@tiptap/extension-floating-menu": "^2.0.0-beta.2",
32
- "prosemirror-view": "^1.18.2"
35
+ "@tiptap/extension-bubble-menu": "^2.0.0-beta.195",
36
+ "@tiptap/extension-floating-menu": "^2.0.0-beta.195",
37
+ "prosemirror-view": "1.26.2"
33
38
  },
34
- "devDependencies": {
35
- "@types/react": "^17.0.3",
36
- "@types/react-dom": "^17.0.3"
39
+ "repository": {
40
+ "type": "git",
41
+ "url": "https://github.com/ueberdosis/tiptap",
42
+ "directory": "packages/react"
37
43
  },
38
- "gitHead": "65ee85d6cce533c3f5dc0fed26d74eff00932e28"
44
+ "sideEffects": false
39
45
  }
@@ -1,29 +1,51 @@
1
- import React, { useEffect, useRef } from 'react'
2
- import { BubbleMenuPlugin, BubbleMenuPluginKey, BubbleMenuPluginProps } from '@tiptap/extension-bubble-menu'
1
+ import { BubbleMenuPlugin, BubbleMenuPluginProps } from '@tiptap/extension-bubble-menu'
2
+ import React, {
3
+ useEffect, useState,
4
+ } from 'react'
3
5
 
4
- export type BubbleMenuProps = Omit<BubbleMenuPluginProps, 'element'> & {
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: React.FC<BubbleMenuProps> = props => {
9
- const element = useRef<HTMLDivElement>(null)
13
+ export const BubbleMenu = (props: BubbleMenuProps) => {
14
+ const [element, setElement] = useState<HTMLDivElement | null>(null)
10
15
 
11
16
  useEffect(() => {
12
- const { editor, keepInBounds = true } = props
17
+ if (!element) {
18
+ return
19
+ }
20
+
21
+ if (props.editor.isDestroyed) {
22
+ return
23
+ }
13
24
 
14
- editor.registerPlugin(BubbleMenuPlugin({
25
+ const {
26
+ pluginKey = 'bubbleMenu',
15
27
  editor,
16
- element: element.current as HTMLElement,
17
- keepInBounds,
18
- }))
28
+ tippyOptions = {},
29
+ shouldShow = null,
30
+ } = props
19
31
 
20
- return () => {
21
- editor.unregisterPlugin(BubbleMenuPluginKey)
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={element} className={props.className} style={{ visibility: 'hidden' }}>
48
+ <div ref={setElement} className={props.className} style={{ visibility: 'hidden' }}>
27
49
  {props.children}
28
50
  </div>
29
51
  )
package/src/Editor.ts CHANGED
@@ -1,5 +1,6 @@
1
- import React from 'react'
2
1
  import { Editor as CoreEditor } from '@tiptap/core'
2
+ import React from 'react'
3
+
3
4
  import { EditorContentProps, EditorContentState } from './EditorContent'
4
5
 
5
6
  export class Editor extends CoreEditor {
@@ -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.appendChild(editor.options.element.firstChild)
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
- // TODO: alternative to setTimeout?
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.appendChild(editor.options.element.firstChild)
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
  )
@@ -1,28 +1,51 @@
1
- import React, { useEffect, useRef } from 'react'
2
- import { FloatingMenuPlugin, FloatingMenuPluginKey, FloatingMenuPluginProps } from '@tiptap/extension-floating-menu'
1
+ import { FloatingMenuPlugin, FloatingMenuPluginProps } from '@tiptap/extension-floating-menu'
2
+ import React, {
3
+ useEffect, useState,
4
+ } from 'react'
3
5
 
4
- export type FloatingMenuProps = Omit<FloatingMenuPluginProps, 'element'> & {
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: React.FC<FloatingMenuProps> = props => {
9
- const element = useRef<HTMLDivElement>(null)
13
+ export const FloatingMenu = (props: FloatingMenuProps) => {
14
+ const [element, setElement] = useState<HTMLDivElement | null>(null)
10
15
 
11
16
  useEffect(() => {
12
- const { editor } = props
17
+ if (!element) {
18
+ return
19
+ }
20
+
21
+ if (props.editor.isDestroyed) {
22
+ return
23
+ }
13
24
 
14
- editor.registerPlugin(FloatingMenuPlugin({
25
+ const {
26
+ pluginKey = 'floatingMenu',
15
27
  editor,
16
- element: element.current as HTMLElement,
17
- }))
28
+ tippyOptions = {},
29
+ shouldShow = null,
30
+ } = props
18
31
 
19
- return () => {
20
- editor.unregisterPlugin(FloatingMenuPluginKey)
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={element} className={props.className} style={{ visibility: 'hidden' }}>
48
+ <div ref={setElement} className={props.className} style={{ visibility: 'hidden' }}>
26
49
  {props.children}
27
50
  </div>
28
51
  )
@@ -1,18 +1,25 @@
1
1
  import React from 'react'
2
2
 
3
+ import { useReactNodeView } from './useReactNodeView'
4
+
3
5
  export interface NodeViewContentProps {
4
- className?: string,
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
- className={props.className}
16
+ {...props}
17
+ ref={nodeViewContentRef}
14
18
  data-node-view-content=""
15
- style={{ whiteSpace: 'pre-wrap' }}
19
+ style={{
20
+ whiteSpace: 'pre-wrap',
21
+ ...props.style,
22
+ }}
16
23
  />
17
24
  )
18
25
  }
@@ -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
- className?: string,
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
- className={props.className}
16
+ {...props}
17
+ ref={ref}
16
18
  data-node-view-wrapper=""
17
19
  onDragStart={onDragStart}
18
- style={{ whiteSpace: 'normal' }}
19
- >
20
- {props.children}
21
- </Tag>
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
- stopEvent: ((event: Event) => boolean) | null,
16
- update: ((node: ProseMirrorNode, decorations: Decoration[]) => boolean) | null,
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<React.FunctionComponent, Editor> {
20
-
30
+ class ReactNodeView extends NodeView<
31
+ React.FunctionComponent,
32
+ Editor,
33
+ ReactNodeViewRendererOptions
34
+ > {
21
35
  renderer!: ReactRenderer
22
36
 
23
- contentDOMElement!: Element | null
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.config.name)
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: this.node.isInline
65
- ? 'span'
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
- if (typeof this.options.update === 'function') {
101
- return this.options.update(node, decorations)
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
- this.renderer.updateProps({ node, decorations })
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(component: any, options?: Partial<ReactNodeViewRendererOptions>): NodeViewRenderer {
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
  }
@@ -1,6 +1,8 @@
1
+ import { Editor } from '@tiptap/core'
1
2
  import React from 'react'
2
- import { AnyObject } from '@tiptap/core'
3
- import { Editor } from './Editor'
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?: AnyObject,
24
+ props?: Record<string, any>,
16
25
  as?: string,
26
+ className?: string,
17
27
  }
18
28
 
19
- export class ReactRenderer {
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: Editor
37
+ editor: ExtendedEditor
23
38
 
24
39
  component: any
25
40
 
26
41
  element: Element
27
42
 
28
- props: AnyObject
43
+ props: Record<string, any>
29
44
 
30
45
  reactElement: React.ReactNode
31
46
 
32
- ref: React.Component | null = null
47
+ ref: R | null = null
33
48
 
34
- constructor(component: React.Component | React.FunctionComponent, { editor, props = {}, as = 'div' }: ReactRendererOptions) {
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,29 @@ 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: React.Component) => {
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
- if (this.editor?.contentComponent) {
57
- this.editor.contentComponent.setState({
58
- renderers: this.editor.contentComponent.state.renderers.set(
59
- this.id,
60
- this,
61
- ),
81
+ queueMicrotask(() => {
82
+ flushSync(() => {
83
+ if (this.editor?.contentComponent) {
84
+ this.editor.contentComponent.setState({
85
+ renderers: this.editor.contentComponent.state.renderers.set(
86
+ this.id,
87
+ this,
88
+ ),
89
+ })
90
+ }
62
91
  })
63
- }
92
+ })
64
93
  }
65
94
 
66
- updateProps(props: AnyObject = {}): void {
95
+ updateProps(props: Record<string, any> = {}): void {
67
96
  this.props = {
68
97
  ...this.props,
69
98
  ...props,
@@ -73,14 +102,18 @@ export class ReactRenderer {
73
102
  }
74
103
 
75
104
  destroy(): void {
76
- if (this.editor?.contentComponent) {
77
- const { renderers } = this.editor.contentComponent.state
105
+ queueMicrotask(() => {
106
+ flushSync(() => {
107
+ if (this.editor?.contentComponent) {
108
+ const { renderers } = this.editor.contentComponent.state
78
109
 
79
- renderers.delete(this.id)
110
+ renderers.delete(this.id)
80
111
 
81
- this.editor.contentComponent.setState({
82
- renderers,
112
+ this.editor.contentComponent.setState({
113
+ renderers,
114
+ })
115
+ }
83
116
  })
84
- }
117
+ })
85
118
  }
86
119
  }
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 './NodeViewWrapper'
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'