@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.
Files changed (34) hide show
  1. package/LICENSE.md +1 -1
  2. package/README.md +2 -2
  3. package/dist/packages/react/src/BubbleMenu.d.ts +6 -3
  4. package/dist/packages/react/src/Editor.d.ts +1 -1
  5. package/dist/packages/react/src/EditorContent.d.ts +2 -2
  6. package/dist/packages/react/src/FloatingMenu.d.ts +6 -3
  7. package/dist/packages/react/src/NodeViewContent.d.ts +2 -2
  8. package/dist/packages/react/src/NodeViewWrapper.d.ts +2 -2
  9. package/dist/packages/react/src/ReactNodeViewRenderer.d.ts +12 -6
  10. package/dist/packages/react/src/ReactRenderer.d.ts +13 -10
  11. package/dist/packages/react/src/index.d.ts +6 -6
  12. package/dist/packages/react/src/useEditor.d.ts +2 -1
  13. package/dist/packages/react/src/useReactNodeView.d.ts +1 -1
  14. package/dist/tiptap-react.cjs.js +216 -147
  15. package/dist/tiptap-react.cjs.js.map +1 -1
  16. package/dist/tiptap-react.esm.js +215 -144
  17. package/dist/tiptap-react.esm.js.map +1 -1
  18. package/dist/tiptap-react.umd.js +218 -149
  19. package/dist/tiptap-react.umd.js.map +1 -1
  20. package/package.json +18 -10
  21. package/src/BubbleMenu.tsx +37 -15
  22. package/src/Editor.ts +2 -1
  23. package/src/EditorContent.tsx +9 -7
  24. package/src/FloatingMenu.tsx +37 -14
  25. package/src/NodeViewContent.tsx +10 -6
  26. package/src/NodeViewWrapper.tsx +12 -9
  27. package/src/ReactNodeViewRenderer.tsx +87 -33
  28. package/src/ReactRenderer.tsx +38 -14
  29. package/src/index.ts +6 -6
  30. package/src/useEditor.ts +16 -4
  31. package/src/useReactNodeView.ts +1 -2
  32. package/CHANGELOG.md +0 -96
  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.11",
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.1",
27
- "react-dom": "^17.0.1"
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.4",
31
- "@tiptap/extension-floating-menu": "^2.0.0-beta.1",
32
- "prosemirror-view": "^1.18.2"
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
- "devDependencies": {
35
- "@types/react-dom": "^17.0.3"
39
+ "repository": {
40
+ "type": "git",
41
+ "url": "https://github.com/ueberdosis/tiptap",
42
+ "directory": "packages/react"
36
43
  },
37
- "gitHead": "b9ee5555e5a72153c7d0ef90560be7cbef1fe567"
44
+ "sideEffects": false,
45
+ "gitHead": "090c5a44566bcd936096574fa62bf301ab4a1285"
38
46
  }
@@ -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}>
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}>
48
+ <div ref={setElement} className={props.className} style={{ visibility: 'hidden' }}>
26
49
  {props.children}
27
50
  </div>
28
51
  )
@@ -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
- className?: string,
6
- as: React.ElementType,
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
- className={props.className}
16
+ {...props}
17
+ ref={nodeViewContentRef}
16
18
  data-node-view-content=""
17
- contentEditable={isEditable}
18
- style={{ whiteSpace: 'pre-wrap' }}
19
+ style={{
20
+ whiteSpace: 'pre-wrap',
21
+ ...props.style,
22
+ }}
19
23
  />
20
24
  )
21
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
- as: React.ElementType,
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
- 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, { 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
- 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,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
- // @ts-ignore
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
- useEffect(() => {
50
- this.editor.on('viewUpdate', onViewUpdate)
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, isEditable }}>
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 (!this.renderer.element.firstElementChild?.hasAttribute('data-node-view-wrapper')) {
74
- throw Error('Please use the ReactViewWrapper component for your node view.')
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
- const contentElement = this.dom.querySelector('[data-node-view-content]')
86
-
87
- return contentElement || this.dom
120
+ return this.contentDOMElement
88
121
  }
89
122
 
90
123
  update(node: ProseMirrorNode, decorations: Decoration[]) {
91
- if (typeof this.options.update === 'function') {
92
- return this.options.update(node, decorations)
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
- this.renderer.updateProps({ node, decorations })
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(component: any, options?: Partial<ReactNodeViewRendererOptions>): NodeViewRenderer {
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
  }
@@ -1,6 +1,7 @@
1
+ import { Editor } from '@tiptap/core'
1
2
  import React from 'react'
2
- import { AnyObject } from '@tiptap/core'
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?: AnyObject,
23
+ props?: Record<string, any>,
24
+ as?: string,
25
+ className?: string,
17
26
  }
18
27
 
19
- export class ReactRenderer {
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: Editor
36
+ editor: ExtendedEditor
23
37
 
24
38
  component: any
25
39
 
26
40
  element: Element
27
41
 
28
- props: AnyObject
42
+ props: Record<string, any>
29
43
 
30
44
  reactElement: React.ReactNode
31
45
 
32
- ref: React.Component | null = null
46
+ ref: R | null = null
33
47
 
34
- constructor(component: React.Component | React.FunctionComponent, { props = {}, editor }: ReactRendererOptions) {
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('div')
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: React.Component) => {
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: AnyObject = {}): void {
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 './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'