@tiptap/react 2.0.0-beta.11 → 2.0.0-beta.112

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 +214 -147
  15. package/dist/tiptap-react.cjs.js.map +1 -1
  16. package/dist/tiptap-react.esm.js +213 -144
  17. package/dist/tiptap-react.esm.js.map +1 -1
  18. package/dist/tiptap-react.umd.js +216 -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 +79 -29
  28. package/src/ReactRenderer.tsx +38 -14
  29. package/src/index.ts +6 -6
  30. package/src/useEditor.ts +11 -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.112",
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.59",
36
+ "@tiptap/extension-floating-menu": "^2.0.0-beta.54",
37
+ "prosemirror-view": "^1.23.6"
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": "591c0807a2ab5c34b4b7fe12c12511fe4f493ebd"
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
+ ...props.style,
21
+ whiteSpace: 'pre-wrap',
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
+ ...props.style,
22
+ whiteSpace: 'normal',
23
+ }}
24
+ />
22
25
  )
23
- }
26
+ })
@@ -1,25 +1,36 @@
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: ((props: {
18
+ oldNode: ProseMirrorNode,
19
+ oldDecorations: Decoration[],
20
+ newNode: ProseMirrorNode,
21
+ newDecorations: Decoration[],
22
+ updateProps: () => void,
23
+ }) => boolean) | null,
24
+ as?: string,
25
+ className?: string,
17
26
  }
18
27
 
19
- class ReactNodeView extends NodeView<React.FunctionComponent, Editor> {
28
+ class ReactNodeView extends NodeView<React.FunctionComponent, Editor, ReactNodeViewRendererOptions> {
20
29
 
21
30
  renderer!: ReactRenderer
22
31
 
32
+ contentDOMElement!: HTMLElement | null
33
+
23
34
  mount() {
24
35
  const props: NodeViewProps = {
25
36
  editor: this.editor,
@@ -29,6 +40,7 @@ class ReactNodeView extends NodeView<React.FunctionComponent, Editor> {
29
40
  extension: this.extension,
30
41
  getPos: () => this.getPos(),
31
42
  updateAttributes: (attributes = {}) => this.updateAttributes(attributes),
43
+ deleteNode: () => this.deleteNode(),
32
44
  }
33
45
 
34
46
  if (!(this.component as any).displayName) {
@@ -36,26 +48,24 @@ class ReactNodeView extends NodeView<React.FunctionComponent, Editor> {
36
48
  return string.charAt(0).toUpperCase() + string.substring(1)
37
49
  }
38
50
 
39
- // @ts-ignore
40
- this.component.displayName = capitalizeFirstChar(this.extension.config.name)
51
+ this.component.displayName = capitalizeFirstChar(this.extension.name)
41
52
  }
42
53
 
43
54
  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
55
  const Component = this.component
48
-
49
- useEffect(() => {
50
- this.editor.on('viewUpdate', onViewUpdate)
51
-
52
- return () => {
53
- this.editor.off('viewUpdate', onViewUpdate)
56
+ const onDragStart = this.onDragStart.bind(this)
57
+ const nodeViewContentRef: ReactNodeViewContextProps['nodeViewContentRef'] = element => {
58
+ if (
59
+ element
60
+ && this.contentDOMElement
61
+ && element.firstChild !== this.contentDOMElement
62
+ ) {
63
+ element.appendChild(this.contentDOMElement)
54
64
  }
55
- }, [])
65
+ }
56
66
 
57
67
  return (
58
- <ReactNodeViewContext.Provider value={{ onDragStart, isEditable }}>
68
+ <ReactNodeViewContext.Provider value={{ onDragStart, nodeViewContentRef }}>
59
69
  <Component {...componentProps} />
60
70
  </ReactNodeViewContext.Provider>
61
71
  )
@@ -63,15 +73,39 @@ class ReactNodeView extends NodeView<React.FunctionComponent, Editor> {
63
73
 
64
74
  ReactNodeViewProvider.displayName = 'ReactNodeView'
65
75
 
76
+ this.contentDOMElement = this.node.isLeaf
77
+ ? null
78
+ : document.createElement(this.node.isInline ? 'span' : 'div')
79
+
80
+ if (this.contentDOMElement) {
81
+ // For some reason the whiteSpace prop is not inherited properly in Chrome and Safari
82
+ // With this fix it seems to work fine
83
+ // See: https://github.com/ueberdosis/tiptap/issues/1197
84
+ this.contentDOMElement.style.whiteSpace = 'inherit'
85
+ }
86
+
87
+ let as = this.node.isInline ? 'span' : 'div'
88
+
89
+ if (this.options.as) {
90
+ as = this.options.as
91
+ }
92
+
93
+ const { className = '' } = this.options
94
+
66
95
  this.renderer = new ReactRenderer(ReactNodeViewProvider, {
67
96
  editor: this.editor,
68
97
  props,
98
+ as,
99
+ className: `node-${this.node.type.name} ${className}`.trim(),
69
100
  })
70
101
  }
71
102
 
72
103
  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.')
104
+ if (
105
+ this.renderer.element.firstElementChild
106
+ && !this.renderer.element.firstElementChild?.hasAttribute('data-node-view-wrapper')
107
+ ) {
108
+ throw Error('Please use the NodeViewWrapper component for your node view.')
75
109
  }
76
110
 
77
111
  return this.renderer.element
@@ -82,27 +116,42 @@ class ReactNodeView extends NodeView<React.FunctionComponent, Editor> {
82
116
  return null
83
117
  }
84
118
 
85
- const contentElement = this.dom.querySelector('[data-node-view-content]')
86
-
87
- return contentElement || this.dom
119
+ return this.contentDOMElement
88
120
  }
89
121
 
90
122
  update(node: ProseMirrorNode, decorations: Decoration[]) {
91
- if (typeof this.options.update === 'function') {
92
- return this.options.update(node, decorations)
123
+ const updateProps = (props?: Record<string, any>) => {
124
+ this.renderer.updateProps(props)
93
125
  }
94
126
 
95
127
  if (node.type !== this.node.type) {
96
128
  return false
97
129
  }
98
130
 
131
+ if (typeof this.options.update === 'function') {
132
+ const oldNode = this.node
133
+ const oldDecorations = this.decorations
134
+
135
+ this.node = node
136
+ this.decorations = decorations
137
+
138
+ return this.options.update({
139
+ oldNode,
140
+ oldDecorations,
141
+ newNode: node,
142
+ newDecorations: decorations,
143
+ updateProps: () => updateProps({ node, decorations }),
144
+ })
145
+ }
146
+
99
147
  if (node === this.node && this.decorations === decorations) {
100
148
  return true
101
149
  }
102
150
 
103
151
  this.node = node
104
152
  this.decorations = decorations
105
- this.renderer.updateProps({ node, decorations })
153
+
154
+ updateProps({ node, decorations })
106
155
 
107
156
  return true
108
157
  }
@@ -121,6 +170,7 @@ class ReactNodeView extends NodeView<React.FunctionComponent, Editor> {
121
170
 
122
171
  destroy() {
123
172
  this.renderer.destroy()
173
+ this.contentDOMElement = null
124
174
  }
125
175
  }
126
176
 
@@ -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'
package/src/useEditor.ts CHANGED
@@ -1,5 +1,6 @@
1
- import { useState, useEffect } from 'react'
2
1
  import { EditorOptions } from '@tiptap/core'
2
+ import { DependencyList, useEffect, useState } from 'react'
3
+
3
4
  import { Editor } from './Editor'
4
5
 
5
6
  function useForceUpdate() {
@@ -8,7 +9,7 @@ function useForceUpdate() {
8
9
  return () => setValue(value => value + 1)
9
10
  }
10
11
 
11
- export const useEditor = (options: Partial<EditorOptions> = {}) => {
12
+ export const useEditor = (options: Partial<EditorOptions> = {}, deps: DependencyList = []) => {
12
13
  const [editor, setEditor] = useState<Editor | null>(null)
13
14
  const forceUpdate = useForceUpdate()
14
15
 
@@ -17,12 +18,18 @@ export const useEditor = (options: Partial<EditorOptions> = {}) => {
17
18
 
18
19
  setEditor(instance)
19
20
 
20
- instance.on('transaction', forceUpdate)
21
+ instance.on('transaction', () => {
22
+ requestAnimationFrame(() => {
23
+ requestAnimationFrame(() => {
24
+ forceUpdate()
25
+ })
26
+ })
27
+ })
21
28
 
22
29
  return () => {
23
30
  instance.destroy()
24
31
  }
25
- }, [])
32
+ }, deps)
26
33
 
27
34
  return editor
28
35
  }
@@ -1,12 +1,11 @@
1
1
  import { createContext, useContext } from 'react'
2
2
 
3
3
  export interface ReactNodeViewContextProps {
4
- isEditable: boolean,
5
4
  onDragStart: (event: DragEvent) => void,
5
+ nodeViewContentRef: (element: HTMLElement | null) => void,
6
6
  }
7
7
 
8
8
  export const ReactNodeViewContext = createContext<Partial<ReactNodeViewContextProps>>({
9
- isEditable: undefined,
10
9
  onDragStart: undefined,
11
10
  })
12
11