@tiptap/react 2.5.9 → 2.6.1

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.
@@ -1,12 +1,13 @@
1
- import { Editor as CoreEditor } from '@tiptap/core';
2
- import React from 'react';
3
- import { EditorContentProps, EditorContentState } from './EditorContent.js';
1
+ import { Editor } from '@tiptap/core';
2
+ import { ReactPortal } from 'react';
4
3
  import { ReactRenderer } from './ReactRenderer.js';
5
- type ContentComponent = React.Component<EditorContentProps, EditorContentState> & {
4
+ export type EditorWithContentComponent = Editor & {
5
+ contentComponent: ContentComponent | null;
6
+ };
7
+ export type ContentComponent = {
6
8
  setRenderer(id: string, renderer: ReactRenderer): void;
7
9
  removeRenderer(id: string): void;
10
+ subscribe: (callback: () => void) => () => void;
11
+ getSnapshot: () => Record<string, ReactPortal>;
12
+ getServerSnapshot: () => Record<string, ReactPortal>;
8
13
  };
9
- export declare class Editor extends CoreEditor {
10
- contentComponent: ContentComponent | null;
11
- }
12
- export {};
@@ -1,23 +1,19 @@
1
+ import { Editor } from '@tiptap/core';
1
2
  import React, { ForwardedRef, HTMLProps } from 'react';
2
- import { Editor } from './Editor.js';
3
- import { ReactRenderer } from './ReactRenderer.js';
4
3
  export interface EditorContentProps extends HTMLProps<HTMLDivElement> {
5
4
  editor: Editor | null;
6
5
  innerRef?: ForwardedRef<HTMLDivElement | null>;
7
6
  }
8
- export interface EditorContentState {
9
- renderers: Record<string, ReactRenderer>;
10
- }
11
- export declare class PureEditorContent extends React.Component<EditorContentProps, EditorContentState> {
7
+ export declare class PureEditorContent extends React.Component<EditorContentProps, {
8
+ hasContentComponentInitialized: boolean;
9
+ }> {
12
10
  editorContentRef: React.RefObject<any>;
13
11
  initialized: boolean;
12
+ unsubscribeToContentComponent?: () => void;
14
13
  constructor(props: EditorContentProps);
15
14
  componentDidMount(): void;
16
15
  componentDidUpdate(): void;
17
16
  init(): void;
18
- maybeFlushSync(fn: () => void): void;
19
- setRenderer(id: string, renderer: ReactRenderer): void;
20
- removeRenderer(id: string): void;
21
17
  componentWillUnmount(): void;
22
18
  render(): React.JSX.Element;
23
19
  }
@@ -1,6 +1,5 @@
1
1
  import { Editor } from '@tiptap/core';
2
2
  import React from 'react';
3
- import { Editor as ExtendedEditor } from './Editor.js';
4
3
  export interface ReactRendererOptions {
5
4
  /**
6
5
  * The editor instance.
@@ -48,7 +47,7 @@ type ComponentType<R, P> = React.ComponentClass<P> | React.FunctionComponent<P>
48
47
  */
49
48
  export declare class ReactRenderer<R = unknown, P = unknown> {
50
49
  id: string;
51
- editor: ExtendedEditor;
50
+ editor: Editor;
52
51
  component: any;
53
52
  element: Element;
54
53
  props: Record<string, any>;
@@ -1,6 +1,5 @@
1
1
  export * from './BubbleMenu.js';
2
2
  export * from './Context.js';
3
- export { Editor } from './Editor.js';
4
3
  export * from './EditorContent.js';
5
4
  export * from './FloatingMenu.js';
6
5
  export * from './NodeViewContent.js';
@@ -10,4 +9,5 @@ export * from './ReactRenderer.js';
10
9
  export * from './useEditor.js';
11
10
  export * from './useEditorState.js';
12
11
  export * from './useReactNodeView.js';
12
+ export { Editor } from '@tiptap/core';
13
13
  export * from '@tiptap/core';
@@ -1,6 +1,5 @@
1
- import { EditorOptions } from '@tiptap/core';
1
+ import { type EditorOptions, Editor } from '@tiptap/core';
2
2
  import { DependencyList } from 'react';
3
- import { Editor } from './Editor.js';
4
3
  /**
5
4
  * The options for the `useEditor` hook.
6
5
  */
@@ -1,4 +1,4 @@
1
- import type { Editor } from './Editor.js';
1
+ import type { Editor } from '@tiptap/core';
2
2
  export type EditorStateSnapshot<TEditor extends Editor | null = Editor | null> = {
3
3
  editor: TEditor;
4
4
  transactionNumber: number;
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.5.9",
4
+ "version": "2.6.1",
5
5
  "homepage": "https://tiptap.dev",
6
6
  "keywords": [
7
7
  "tiptap",
@@ -29,22 +29,22 @@
29
29
  "dist"
30
30
  ],
31
31
  "dependencies": {
32
- "@tiptap/extension-bubble-menu": "^2.5.9",
33
- "@tiptap/extension-floating-menu": "^2.5.9",
32
+ "@tiptap/extension-bubble-menu": "^2.6.1",
33
+ "@tiptap/extension-floating-menu": "^2.6.1",
34
34
  "@types/use-sync-external-store": "^0.0.6",
35
35
  "use-sync-external-store": "^1.2.2"
36
36
  },
37
37
  "devDependencies": {
38
- "@tiptap/core": "^2.5.9",
39
- "@tiptap/pm": "^2.5.9",
38
+ "@tiptap/core": "^2.6.1",
39
+ "@tiptap/pm": "^2.6.1",
40
40
  "@types/react": "^18.2.14",
41
41
  "@types/react-dom": "^18.2.6",
42
42
  "react": "^18.0.0",
43
43
  "react-dom": "^18.0.0"
44
44
  },
45
45
  "peerDependencies": {
46
- "@tiptap/core": "^2.5.9",
47
- "@tiptap/pm": "^2.5.9",
46
+ "@tiptap/core": "^2.6.1",
47
+ "@tiptap/pm": "^2.6.1",
48
48
  "react": "^17.0.0 || ^18.0.0",
49
49
  "react-dom": "^17.0.0 || ^18.0.0"
50
50
  },
package/src/Context.tsx CHANGED
@@ -1,6 +1,6 @@
1
+ import { Editor } from '@tiptap/core'
1
2
  import React, { createContext, ReactNode, useContext } from 'react'
2
3
 
3
- import { Editor } from './Editor.js'
4
4
  import { EditorContent } from './EditorContent.js'
5
5
  import { useEditor, UseEditorOptions } from './useEditor.js'
6
6
 
package/src/Editor.ts CHANGED
@@ -1,14 +1,13 @@
1
- import { Editor as CoreEditor } from '@tiptap/core'
2
- import React from 'react'
1
+ import { Editor } from '@tiptap/core'
2
+ import { ReactPortal } from 'react'
3
3
 
4
- import { EditorContentProps, EditorContentState } from './EditorContent.js'
5
4
  import { ReactRenderer } from './ReactRenderer.js'
6
5
 
7
- type ContentComponent = React.Component<EditorContentProps, EditorContentState> & {
6
+ export type EditorWithContentComponent = Editor & { contentComponent: ContentComponent | null }
7
+ export type ContentComponent = {
8
8
  setRenderer(id: string, renderer: ReactRenderer): void;
9
9
  removeRenderer(id: string): void;
10
- }
11
-
12
- export class Editor extends CoreEditor {
13
- public contentComponent: ContentComponent | null = null
10
+ subscribe: (callback: () => void) => () => void;
11
+ getSnapshot: () => Record<string, ReactPortal>;
12
+ getServerSnapshot: () => Record<string, ReactPortal>;
14
13
  }
@@ -1,9 +1,11 @@
1
+ import { Editor } from '@tiptap/core'
1
2
  import React, {
2
3
  ForwardedRef, forwardRef, HTMLProps, LegacyRef, MutableRefObject,
3
4
  } from 'react'
4
- import ReactDOM, { flushSync } from 'react-dom'
5
+ import ReactDOM from 'react-dom'
6
+ import { useSyncExternalStore } from 'use-sync-external-store/shim'
5
7
 
6
- import { Editor } from './Editor.js'
8
+ import { ContentComponent, EditorWithContentComponent } from './Editor.js'
7
9
  import { ReactRenderer } from './ReactRenderer.js'
8
10
 
9
11
  const mergeRefs = <T extends HTMLDivElement>(
@@ -20,12 +22,23 @@ const mergeRefs = <T extends HTMLDivElement>(
20
22
  }
21
23
  }
22
24
 
23
- const Portals: React.FC<{ renderers: Record<string, ReactRenderer> }> = ({ renderers }) => {
25
+ /**
26
+ * This component renders all of the editor's node views.
27
+ */
28
+ const Portals: React.FC<{ contentComponent: ContentComponent }> = ({
29
+ contentComponent,
30
+ }) => {
31
+ // For performance reasons, we render the node view portals on state changes only
32
+ const renderers = useSyncExternalStore(
33
+ contentComponent.subscribe,
34
+ contentComponent.getSnapshot,
35
+ contentComponent.getServerSnapshot,
36
+ )
37
+
38
+ // This allows us to directly render the portals without any additional wrapper
24
39
  return (
25
40
  <>
26
- {Object.entries(renderers).map(([key, renderer]) => {
27
- return ReactDOM.createPortal(renderer.reactElement, renderer.element, key)
28
- })}
41
+ {Object.values(renderers)}
29
42
  </>
30
43
  )
31
44
  }
@@ -35,22 +48,67 @@ export interface EditorContentProps extends HTMLProps<HTMLDivElement> {
35
48
  innerRef?: ForwardedRef<HTMLDivElement | null>;
36
49
  }
37
50
 
38
- export interface EditorContentState {
39
- renderers: Record<string, ReactRenderer>;
51
+ function getInstance(): ContentComponent {
52
+ const subscribers = new Set<() => void>()
53
+ let renderers: Record<string, React.ReactPortal> = {}
54
+
55
+ return {
56
+ /**
57
+ * Subscribe to the editor instance's changes.
58
+ */
59
+ subscribe(callback: () => void) {
60
+ subscribers.add(callback)
61
+ return () => {
62
+ subscribers.delete(callback)
63
+ }
64
+ },
65
+ getSnapshot() {
66
+ return renderers
67
+ },
68
+ getServerSnapshot() {
69
+ return renderers
70
+ },
71
+ /**
72
+ * Adds a new NodeView Renderer to the editor.
73
+ */
74
+ setRenderer(id: string, renderer: ReactRenderer) {
75
+ renderers = {
76
+ ...renderers,
77
+ [id]: ReactDOM.createPortal(renderer.reactElement, renderer.element, id),
78
+ }
79
+
80
+ subscribers.forEach(subscriber => subscriber())
81
+ },
82
+ /**
83
+ * Removes a NodeView Renderer from the editor.
84
+ */
85
+ removeRenderer(id: string) {
86
+ const nextRenderers = { ...renderers }
87
+
88
+ delete nextRenderers[id]
89
+ renderers = nextRenderers
90
+ subscribers.forEach(subscriber => subscriber())
91
+ },
92
+ }
40
93
  }
41
94
 
42
- export class PureEditorContent extends React.Component<EditorContentProps, EditorContentState> {
95
+ export class PureEditorContent extends React.Component<
96
+ EditorContentProps,
97
+ { hasContentComponentInitialized: boolean }
98
+ > {
43
99
  editorContentRef: React.RefObject<any>
44
100
 
45
101
  initialized: boolean
46
102
 
103
+ unsubscribeToContentComponent?: () => void
104
+
47
105
  constructor(props: EditorContentProps) {
48
106
  super(props)
49
107
  this.editorContentRef = React.createRef()
50
108
  this.initialized = false
51
109
 
52
110
  this.state = {
53
- renderers: {},
111
+ hasContentComponentInitialized: Boolean((props.editor as EditorWithContentComponent).contentComponent),
54
112
  }
55
113
  }
56
114
 
@@ -63,7 +121,7 @@ export class PureEditorContent extends React.Component<EditorContentProps, Edito
63
121
  }
64
122
 
65
123
  init() {
66
- const { editor } = this.props
124
+ const editor = this.props.editor as EditorWithContentComponent
67
125
 
68
126
  if (editor && !editor.isDestroyed && editor.options.element) {
69
127
  if (editor.contentComponent) {
@@ -78,7 +136,27 @@ export class PureEditorContent extends React.Component<EditorContentProps, Edito
78
136
  element,
79
137
  })
80
138
 
81
- editor.contentComponent = this
139
+ editor.contentComponent = getInstance()
140
+
141
+ // Has the content component been initialized?
142
+ if (!this.state.hasContentComponentInitialized) {
143
+ // Subscribe to the content component
144
+ this.unsubscribeToContentComponent = editor.contentComponent.subscribe(() => {
145
+ this.setState(prevState => {
146
+ if (!prevState.hasContentComponentInitialized) {
147
+ return {
148
+ hasContentComponentInitialized: true,
149
+ }
150
+ }
151
+ return prevState
152
+ })
153
+
154
+ // Unsubscribe to previous content component
155
+ if (this.unsubscribeToContentComponent) {
156
+ this.unsubscribeToContentComponent()
157
+ }
158
+ })
159
+ }
82
160
 
83
161
  editor.createNodeViews()
84
162
 
@@ -86,43 +164,8 @@ export class PureEditorContent extends React.Component<EditorContentProps, Edito
86
164
  }
87
165
  }
88
166
 
89
- maybeFlushSync(fn: () => void) {
90
- // Avoid calling flushSync until the editor is initialized.
91
- // Initialization happens during the componentDidMount or componentDidUpdate
92
- // lifecycle methods, and React doesn't allow calling flushSync from inside
93
- // a lifecycle method.
94
- if (this.initialized) {
95
- flushSync(fn)
96
- } else {
97
- fn()
98
- }
99
- }
100
-
101
- setRenderer(id: string, renderer: ReactRenderer) {
102
- this.maybeFlushSync(() => {
103
- this.setState(({ renderers }) => ({
104
- renderers: {
105
- ...renderers,
106
- [id]: renderer,
107
- },
108
- }))
109
- })
110
- }
111
-
112
- removeRenderer(id: string) {
113
- this.maybeFlushSync(() => {
114
- this.setState(({ renderers }) => {
115
- const nextRenderers = { ...renderers }
116
-
117
- delete nextRenderers[id]
118
-
119
- return { renderers: nextRenderers }
120
- })
121
- })
122
- }
123
-
124
167
  componentWillUnmount() {
125
- const { editor } = this.props
168
+ const editor = this.props.editor as EditorWithContentComponent
126
169
 
127
170
  if (!editor) {
128
171
  return
@@ -136,6 +179,10 @@ export class PureEditorContent extends React.Component<EditorContentProps, Edito
136
179
  })
137
180
  }
138
181
 
182
+ if (this.unsubscribeToContentComponent) {
183
+ this.unsubscribeToContentComponent()
184
+ }
185
+
139
186
  editor.contentComponent = null
140
187
 
141
188
  if (!editor.options.element.firstChild) {
@@ -158,7 +205,7 @@ export class PureEditorContent extends React.Component<EditorContentProps, Edito
158
205
  <>
159
206
  <div ref={mergeRefs(innerRef, this.editorContentRef)} {...rest} />
160
207
  {/* @ts-ignore */}
161
- <Portals renderers={this.state.renderers} />
208
+ {editor?.contentComponent && <Portals contentComponent={editor.contentComponent} />}
162
209
  </>
163
210
  )
164
211
  }
@@ -168,7 +215,8 @@ export class PureEditorContent extends React.Component<EditorContentProps, Edito
168
215
  const EditorContentWithKey = forwardRef<HTMLDivElement, EditorContentProps>(
169
216
  (props: Omit<EditorContentProps, 'innerRef'>, ref) => {
170
217
  const key = React.useMemo(() => {
171
- return Math.floor(Math.random() * 0xFFFFFFFF).toString()
218
+ return Math.floor(Math.random() * 0xffffffff).toString()
219
+ // eslint-disable-next-line react-hooks/exhaustive-deps
172
220
  }, [props.editor])
173
221
 
174
222
  // Can't use JSX here because it conflicts with the type definition of Vue's JSX, so use createElement
@@ -12,6 +12,7 @@ export const NodeViewContent: React.FC<NodeViewContentProps> = props => {
12
12
  const { nodeViewContentRef } = useReactNodeView()
13
13
 
14
14
  return (
15
+ // @ts-ignore
15
16
  <Tag
16
17
  {...props}
17
18
  ref={nodeViewContentRef}
@@ -12,6 +12,7 @@ export const NodeViewWrapper: React.FC<NodeViewWrapperProps> = React.forwardRef(
12
12
  const Tag = props.as || 'div'
13
13
 
14
14
  return (
15
+ // @ts-ignore
15
16
  <Tag
16
17
  {...props}
17
18
  ref={ref}
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  DecorationWithType,
3
+ Editor,
3
4
  NodeView,
4
5
  NodeViewProps,
5
6
  NodeViewRenderer,
@@ -10,7 +11,7 @@ import { Node as ProseMirrorNode } from '@tiptap/pm/model'
10
11
  import { Decoration, NodeView as ProseMirrorNodeView } from '@tiptap/pm/view'
11
12
  import React from 'react'
12
13
 
13
- import { Editor } from './Editor.js'
14
+ import { EditorWithContentComponent } from './Editor.js'
14
15
  import { ReactRenderer } from './ReactRenderer.js'
15
16
  import { ReactNodeViewContext, ReactNodeViewContextProps } from './useReactNodeView.js'
16
17
 
@@ -58,25 +59,23 @@ class ReactNodeView extends NodeView<
58
59
  this.component.displayName = capitalizeFirstChar(this.extension.name)
59
60
  }
60
61
 
61
- const ReactNodeViewProvider: React.FunctionComponent = componentProps => {
62
- const Component = this.component
63
- const onDragStart = this.onDragStart.bind(this)
64
- const nodeViewContentRef: ReactNodeViewContextProps['nodeViewContentRef'] = element => {
65
- if (element && this.contentDOMElement && element.firstChild !== this.contentDOMElement) {
66
- element.appendChild(this.contentDOMElement)
67
- }
62
+ const onDragStart = this.onDragStart.bind(this)
63
+ const nodeViewContentRef: ReactNodeViewContextProps['nodeViewContentRef'] = element => {
64
+ if (element && this.contentDOMElement && element.firstChild !== this.contentDOMElement) {
65
+ element.appendChild(this.contentDOMElement)
68
66
  }
69
-
67
+ }
68
+ const context = { onDragStart, nodeViewContentRef }
69
+ const Component = this.component
70
+ // For performance reasons, we memoize the provider component
71
+ // And all of the things it requires are declared outside of the component, so it doesn't need to re-render
72
+ const ReactNodeViewProvider: React.FunctionComponent = React.memo(componentProps => {
70
73
  return (
71
- <>
72
- {/* @ts-ignore */}
73
- <ReactNodeViewContext.Provider value={{ onDragStart, nodeViewContentRef }}>
74
- {/* @ts-ignore */}
75
- <Component {...componentProps} />
76
- </ReactNodeViewContext.Provider>
77
- </>
74
+ <ReactNodeViewContext.Provider value={context}>
75
+ {React.createElement(Component, componentProps)}
76
+ </ReactNodeViewContext.Provider>
78
77
  )
79
- }
78
+ })
80
79
 
81
80
  ReactNodeViewProvider.displayName = 'ReactNodeView'
82
81
 
@@ -218,7 +217,7 @@ export function ReactNodeViewRenderer(
218
217
  // try to get the parent component
219
218
  // this is important for vue devtools to show the component hierarchy correctly
220
219
  // maybe it’s `undefined` because <editor-content> isn’t rendered yet
221
- if (!(props.editor as Editor).contentComponent) {
220
+ if (!(props.editor as EditorWithContentComponent).contentComponent) {
222
221
  return {}
223
222
  }
224
223
 
@@ -1,7 +1,8 @@
1
1
  import { Editor } from '@tiptap/core'
2
2
  import React from 'react'
3
+ import { flushSync } from 'react-dom'
3
4
 
4
- import { Editor as ExtendedEditor } from './Editor.js'
5
+ import { EditorWithContentComponent } from './Editor.js'
5
6
 
6
7
  /**
7
8
  * Check if a component is a class component.
@@ -85,7 +86,7 @@ type ComponentType<R, P> =
85
86
  export class ReactRenderer<R = unknown, P = unknown> {
86
87
  id: string
87
88
 
88
- editor: ExtendedEditor
89
+ editor: Editor
89
90
 
90
91
  component: any
91
92
 
@@ -106,7 +107,7 @@ export class ReactRenderer<R = unknown, P = unknown> {
106
107
  }: ReactRendererOptions) {
107
108
  this.id = Math.floor(Math.random() * 0xFFFFFFFF).toString()
108
109
  this.component = component
109
- this.editor = editor as ExtendedEditor
110
+ this.editor = editor as EditorWithContentComponent
110
111
  this.props = props
111
112
  this.element = document.createElement(as)
112
113
  this.element.classList.add('react-renderer')
@@ -121,12 +122,21 @@ export class ReactRenderer<R = unknown, P = unknown> {
121
122
  })
122
123
  }
123
124
 
124
- this.render()
125
+ if (this.editor.isInitialized) {
126
+ // On first render, we need to flush the render synchronously
127
+ // Renders afterwards can be async, but this fixes a cursor positioning issue
128
+ flushSync(() => {
129
+ this.render()
130
+ })
131
+ } else {
132
+ this.render()
133
+ }
125
134
  }
126
135
 
127
136
  render(): void {
128
137
  const Component = this.component
129
138
  const props = this.props
139
+ const editor = this.editor as EditorWithContentComponent
130
140
 
131
141
  if (isClassComponent(Component) || isForwardRefComponent(Component)) {
132
142
  props.ref = (ref: R) => {
@@ -134,9 +144,9 @@ export class ReactRenderer<R = unknown, P = unknown> {
134
144
  }
135
145
  }
136
146
 
137
- this.reactElement = <Component {...props } />
147
+ this.reactElement = React.createElement(Component, props)
138
148
 
139
- this.editor?.contentComponent?.setRenderer(this.id, this)
149
+ editor?.contentComponent?.setRenderer(this.id, this)
140
150
  }
141
151
 
142
152
  updateProps(props: Record<string, any> = {}): void {
@@ -149,6 +159,8 @@ export class ReactRenderer<R = unknown, P = unknown> {
149
159
  }
150
160
 
151
161
  destroy(): void {
152
- this.editor?.contentComponent?.removeRenderer(this.id)
162
+ const editor = this.editor as EditorWithContentComponent
163
+
164
+ editor?.contentComponent?.removeRenderer(this.id)
153
165
  }
154
166
  }
package/src/index.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  export * from './BubbleMenu.js'
2
2
  export * from './Context.js'
3
- export { Editor } from './Editor.js'
4
3
  export * from './EditorContent.js'
5
4
  export * from './FloatingMenu.js'
6
5
  export * from './NodeViewContent.js'
@@ -10,4 +9,5 @@ export * from './ReactRenderer.js'
10
9
  export * from './useEditor.js'
11
10
  export * from './useEditorState.js'
12
11
  export * from './useReactNodeView.js'
12
+ export { Editor } from '@tiptap/core'
13
13
  export * from '@tiptap/core'
package/src/useEditor.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { EditorOptions } from '@tiptap/core'
1
+ import { type EditorOptions, Editor } from '@tiptap/core'
2
2
  import {
3
3
  DependencyList,
4
4
  MutableRefObject,
@@ -9,7 +9,6 @@ import {
9
9
  } from 'react'
10
10
  import { useSyncExternalStore } from 'use-sync-external-store/shim'
11
11
 
12
- import { Editor } from './Editor.js'
13
12
  import { useEditorState } from './useEditorState.js'
14
13
 
15
14
  const isDev = process.env.NODE_ENV !== 'production'
@@ -137,18 +136,20 @@ class EditorInstanceManager {
137
136
  * Create a new editor instance. And attach event listeners.
138
137
  */
139
138
  private createEditor(): Editor {
140
- const editor = new Editor(this.options.current)
141
-
142
- // Always call the most recent version of the callback function by default
143
- editor.on('beforeCreate', (...args) => this.options.current.onBeforeCreate?.(...args))
144
- editor.on('blur', (...args) => this.options.current.onBlur?.(...args))
145
- editor.on('create', (...args) => this.options.current.onCreate?.(...args))
146
- editor.on('destroy', (...args) => this.options.current.onDestroy?.(...args))
147
- editor.on('focus', (...args) => this.options.current.onFocus?.(...args))
148
- editor.on('selectionUpdate', (...args) => this.options.current.onSelectionUpdate?.(...args))
149
- editor.on('transaction', (...args) => this.options.current.onTransaction?.(...args))
150
- editor.on('update', (...args) => this.options.current.onUpdate?.(...args))
151
- editor.on('contentError', (...args) => this.options.current.onContentError?.(...args))
139
+ const optionsToApply: Partial<EditorOptions> = {
140
+ ...this.options.current,
141
+ // Always call the most recent version of the callback function by default
142
+ onBeforeCreate: (...args) => this.options.current.onBeforeCreate?.(...args),
143
+ onBlur: (...args) => this.options.current.onBlur?.(...args),
144
+ onCreate: (...args) => this.options.current.onCreate?.(...args),
145
+ onDestroy: (...args) => this.options.current.onDestroy?.(...args),
146
+ onFocus: (...args) => this.options.current.onFocus?.(...args),
147
+ onSelectionUpdate: (...args) => this.options.current.onSelectionUpdate?.(...args),
148
+ onTransaction: (...args) => this.options.current.onTransaction?.(...args),
149
+ onUpdate: (...args) => this.options.current.onUpdate?.(...args),
150
+ onContentError: (...args) => this.options.current.onContentError?.(...args),
151
+ }
152
+ const editor = new Editor(optionsToApply)
152
153
 
153
154
  // no need to keep track of the event listeners, they will be removed when the editor is destroyed
154
155
 
@@ -216,7 +217,6 @@ class EditorInstanceManager {
216
217
  * Recreate the editor instance if the dependencies have changed.
217
218
  */
218
219
  private refreshEditorInstance(deps: DependencyList) {
219
-
220
220
  if (this.editor && !this.editor.isDestroyed) {
221
221
  // Editor instance already exists
222
222
  if (this.previousDeps === null) {
@@ -1,8 +1,7 @@
1
+ import type { Editor } from '@tiptap/core'
1
2
  import { useDebugValue, useEffect, useState } from 'react'
2
3
  import { useSyncExternalStoreWithSelector } from 'use-sync-external-store/shim/with-selector'
3
4
 
4
- import type { Editor } from './Editor.js'
5
-
6
5
  export type EditorStateSnapshot<TEditor extends Editor | null = Editor | null> = {
7
6
  editor: TEditor;
8
7
  transactionNumber: number;