@tiptap/react 2.0.0-beta.17 → 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.
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 +232 -151
  14. package/dist/tiptap-react.cjs.js.map +1 -1
  15. package/dist/tiptap-react.esm.js +232 -149
  16. package/dist/tiptap-react.esm.js.map +1 -1
  17. package/dist/tiptap-react.umd.js +234 -153
  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 +87 -25
  27. package/src/ReactRenderer.tsx +56 -27
  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 -151
  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.17",
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.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.194",
36
+ "@tiptap/extension-floating-menu": "^2.0.0-beta.194",
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": "113d50b814c8860465c8bb16ae2bee3097689016"
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,25 +1,41 @@
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
 
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,15 +53,20 @@ class ReactNodeView extends NodeView<React.FunctionComponent, Editor> {
36
53
  return string.charAt(0).toUpperCase() + string.substring(1)
37
54
  }
38
55
 
39
- this.component.displayName = capitalizeFirstChar(this.extension.config.name)
56
+ this.component.displayName = capitalizeFirstChar(this.extension.name)
40
57
  }
41
58
 
42
59
  const ReactNodeViewProvider: React.FunctionComponent = componentProps => {
43
- const onDragStart = this.onDragStart.bind(this)
44
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
+ }
45
67
 
46
68
  return (
47
- <ReactNodeViewContext.Provider value={{ onDragStart }}>
69
+ <ReactNodeViewContext.Provider value={{ onDragStart, nodeViewContentRef }}>
48
70
  <Component {...componentProps} />
49
71
  </ReactNodeViewContext.Provider>
50
72
  )
@@ -52,21 +74,42 @@ class ReactNodeView extends NodeView<React.FunctionComponent, Editor> {
52
74
 
53
75
  ReactNodeViewProvider.displayName = 'ReactNodeView'
54
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
+
55
96
  this.renderer = new ReactRenderer(ReactNodeViewProvider, {
56
97
  editor: this.editor,
57
98
  props,
58
- as: this.node.isInline
59
- ? 'span'
60
- : 'div',
99
+ as,
100
+ className: `node-${this.node.type.name} ${className}`.trim(),
61
101
  })
62
102
  }
63
103
 
64
104
  get dom() {
65
- if (!this.renderer.element.firstElementChild?.hasAttribute('data-node-view-wrapper')) {
105
+ if (
106
+ this.renderer.element.firstElementChild
107
+ && !this.renderer.element.firstElementChild?.hasAttribute('data-node-view-wrapper')
108
+ ) {
66
109
  throw Error('Please use the NodeViewWrapper component for your node view.')
67
110
  }
68
111
 
69
- return this.renderer.element
112
+ return this.renderer.element as HTMLElement
70
113
  }
71
114
 
72
115
  get contentDOM() {
@@ -74,27 +117,42 @@ class ReactNodeView extends NodeView<React.FunctionComponent, Editor> {
74
117
  return null
75
118
  }
76
119
 
77
- const contentElement = this.dom.querySelector('[data-node-view-content]')
78
-
79
- return contentElement || this.dom
120
+ return this.contentDOMElement
80
121
  }
81
122
 
82
123
  update(node: ProseMirrorNode, decorations: Decoration[]) {
83
- if (typeof this.options.update === 'function') {
84
- return this.options.update(node, decorations)
124
+ const updateProps = (props?: Record<string, any>) => {
125
+ this.renderer.updateProps(props)
85
126
  }
86
127
 
87
128
  if (node.type !== this.node.type) {
88
129
  return false
89
130
  }
90
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
+
91
148
  if (node === this.node && this.decorations === decorations) {
92
149
  return true
93
150
  }
94
151
 
95
152
  this.node = node
96
153
  this.decorations = decorations
97
- this.renderer.updateProps({ node, decorations })
154
+
155
+ updateProps({ node, decorations })
98
156
 
99
157
  return true
100
158
  }
@@ -113,10 +171,14 @@ class ReactNodeView extends NodeView<React.FunctionComponent, Editor> {
113
171
 
114
172
  destroy() {
115
173
  this.renderer.destroy()
174
+ this.contentDOMElement = null
116
175
  }
117
176
  }
118
177
 
119
- export function ReactNodeViewRenderer(component: any, options?: Partial<ReactNodeViewRendererOptions>): NodeViewRenderer {
178
+ export function ReactNodeViewRenderer(
179
+ component: any,
180
+ options?: Partial<ReactNodeViewRendererOptions>,
181
+ ): NodeViewRenderer {
120
182
  return (props: NodeViewRendererProps) => {
121
183
  // try to get the parent component
122
184
  // this is important for vue devtools to show the component hierarchy correctly
@@ -125,6 +187,6 @@ export function ReactNodeViewRenderer(component: any, options?: Partial<ReactNod
125
187
  return {}
126
188
  }
127
189
 
128
- return new ReactNodeView(component, props, options) as ProseMirrorNodeView
190
+ return new ReactNodeView(component, props, options) as unknown as ProseMirrorNodeView
129
191
  }
130
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,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: 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
- ),
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: AnyObject = {}): void {
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
- if (this.editor?.contentComponent) {
77
- const { renderers } = this.editor.contentComponent.state
103
+ flushSync(() => {
104
+ if (this.editor?.contentComponent) {
105
+ const { renderers } = this.editor.contentComponent.state
78
106
 
79
- renderers.delete(this.id)
107
+ renderers.delete(this.id)
80
108
 
81
- this.editor.contentComponent.setState({
82
- renderers,
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 './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'