@tiptap/react 2.6.6 → 2.7.0

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,6 +1,6 @@
1
- import { Mark as ProseMirrorMark, Node as ProseMirrorNode, NodeType, ParseOptions } from '@tiptap/pm/model';
1
+ import { Mark as ProseMirrorMark, Node as ProseMirrorNode, NodeType, ParseOptions, Slice } from '@tiptap/pm/model';
2
2
  import { EditorState, Transaction } from '@tiptap/pm/state';
3
- import { Decoration, EditorProps, EditorView, NodeView } from '@tiptap/pm/view';
3
+ import { Decoration, EditorProps, EditorView, NodeView, NodeViewConstructor } from '@tiptap/pm/view';
4
4
  import { Editor } from './Editor.js';
5
5
  import { Extension } from './Extension.js';
6
6
  import { Commands, ExtensionConfig, MarkConfig, NodeConfig } from './index.js';
@@ -79,7 +79,24 @@ export interface EditorOptions {
79
79
  };
80
80
  enableInputRules: EnableRules;
81
81
  enablePasteRules: EnableRules;
82
- enableCoreExtensions: boolean;
82
+ /**
83
+ * Determines whether core extensions are enabled.
84
+ *
85
+ * If set to `false`, all core extensions will be disabled.
86
+ * To disable specific core extensions, provide an object where the keys are the extension names and the values are `false`.
87
+ * Extensions not listed in the object will remain enabled.
88
+ *
89
+ * @example
90
+ * // Disable all core extensions
91
+ * enabledCoreExtensions: false
92
+ *
93
+ * @example
94
+ * // Disable only the keymap core extension
95
+ * enabledCoreExtensions: { keymap: false }
96
+ *
97
+ * @default true
98
+ */
99
+ enableCoreExtensions?: boolean | Partial<Record<'editable' | 'clipboardTextSerializer' | 'commands' | 'focusEvents' | 'keymap' | 'tabindex', false>>;
83
100
  /**
84
101
  * If `true`, the editor will check the content for errors on initialization.
85
102
  * Emitting the `contentError` event if the content is invalid.
@@ -100,6 +117,8 @@ export interface EditorOptions {
100
117
  onFocus: (props: EditorEvents['focus']) => void;
101
118
  onBlur: (props: EditorEvents['blur']) => void;
102
119
  onDestroy: (props: EditorEvents['destroy']) => void;
120
+ onPaste: (e: ClipboardEvent, slice: Slice) => void;
121
+ onDrop: (e: DragEvent, slice: Slice, moved: boolean) => void;
103
122
  }
104
123
  export type HTMLContent = string;
105
124
  export type JSONContent = {
@@ -170,19 +189,18 @@ export type ValuesOf<T> = T[keyof T];
170
189
  export type KeysWithTypeOf<T, Type> = {
171
190
  [P in keyof T]: T[P] extends Type ? P : never;
172
191
  }[keyof T];
192
+ export type Simplify<T> = {
193
+ [KeyType in keyof T]: T[KeyType];
194
+ } & {};
173
195
  export type DecorationWithType = Decoration & {
174
196
  type: NodeType;
175
197
  };
176
- export type NodeViewProps = {
177
- editor: Editor;
178
- node: ProseMirrorNode;
179
- decorations: DecorationWithType[];
198
+ export type NodeViewProps = Simplify<Omit<NodeViewRendererProps, 'decorations'> & {
199
+ decorations: readonly DecorationWithType[];
180
200
  selected: boolean;
181
- extension: Node;
182
- getPos: () => number;
183
201
  updateAttributes: (attributes: Record<string, any>) => void;
184
202
  deleteNode: () => void;
185
- };
203
+ }>;
186
204
  export interface NodeViewRendererOptions {
187
205
  stopEvent: ((props: {
188
206
  event: Event;
@@ -196,14 +214,16 @@ export interface NodeViewRendererOptions {
196
214
  contentDOMElementTag: string;
197
215
  }
198
216
  export type NodeViewRendererProps = {
217
+ node: Parameters<NodeViewConstructor>[0];
218
+ view: Parameters<NodeViewConstructor>[1];
219
+ getPos: () => number;
220
+ decorations: Parameters<NodeViewConstructor>[3];
221
+ innerDecorations: Parameters<NodeViewConstructor>[4];
199
222
  editor: Editor;
200
- node: ProseMirrorNode;
201
- getPos: (() => number) | boolean;
202
- HTMLAttributes: Record<string, any>;
203
- decorations: Decoration[];
204
223
  extension: Node;
224
+ HTMLAttributes: Record<string, any>;
205
225
  };
206
- export type NodeViewRenderer = (props: NodeViewRendererProps) => NodeView | {};
226
+ export type NodeViewRenderer = (props: NodeViewRendererProps) => NodeView;
207
227
  export type AnyCommands = Record<string, (...args: any[]) => Command>;
208
228
  export type UnionCommands<T = Command> = UnionToIntersection<ValuesOf<Pick<Commands<T>, KeysWithTypeOf<Commands<T>, {}>>>>;
209
229
  export type RawCommands = {
@@ -1,16 +1,95 @@
1
- import { NodeViewRenderer, NodeViewRendererOptions } from '@tiptap/core';
2
- import { Node as ProseMirrorNode } from '@tiptap/pm/model';
3
- import { Decoration } from '@tiptap/pm/view';
1
+ import { Editor, NodeView, NodeViewProps, NodeViewRenderer, NodeViewRendererOptions } from '@tiptap/core';
2
+ import { Node, Node as ProseMirrorNode } from '@tiptap/pm/model';
3
+ import { Decoration, DecorationSource } from '@tiptap/pm/view';
4
+ import { ComponentType } from 'react';
5
+ import { ReactRenderer } from './ReactRenderer.js';
4
6
  export interface ReactNodeViewRendererOptions extends NodeViewRendererOptions {
7
+ /**
8
+ * This function is called when the node view is updated.
9
+ * It allows you to compare the old node with the new node and decide if the component should update.
10
+ */
5
11
  update: ((props: {
6
12
  oldNode: ProseMirrorNode;
7
- oldDecorations: Decoration[];
13
+ oldDecorations: readonly Decoration[];
14
+ oldInnerDecorations: DecorationSource;
8
15
  newNode: ProseMirrorNode;
9
- newDecorations: Decoration[];
16
+ newDecorations: readonly Decoration[];
17
+ innerDecorations: DecorationSource;
10
18
  updateProps: () => void;
11
19
  }) => boolean) | null;
20
+ /**
21
+ * The tag name of the element wrapping the React component.
22
+ */
12
23
  as?: string;
24
+ /**
25
+ * The class name of the element wrapping the React component.
26
+ */
13
27
  className?: string;
14
- attrs?: Record<string, string>;
28
+ /**
29
+ * Attributes that should be applied to the element wrapping the React component.
30
+ * If this is a function, it will be called each time the node view is updated.
31
+ * If this is an object, it will be applied once when the node view is mounted.
32
+ */
33
+ attrs?: Record<string, string> | ((props: {
34
+ node: ProseMirrorNode;
35
+ HTMLAttributes: Record<string, any>;
36
+ }) => Record<string, string>);
15
37
  }
16
- export declare function ReactNodeViewRenderer(component: any, options?: Partial<ReactNodeViewRendererOptions>): NodeViewRenderer;
38
+ export declare class ReactNodeView<Component extends ComponentType<NodeViewProps> = ComponentType<NodeViewProps>, NodeEditor extends Editor = Editor, Options extends ReactNodeViewRendererOptions = ReactNodeViewRendererOptions> extends NodeView<Component, NodeEditor, Options> {
39
+ /**
40
+ * The renderer instance.
41
+ */
42
+ renderer: ReactRenderer<unknown, NodeViewProps>;
43
+ /**
44
+ * The element that holds the rich-text content of the node.
45
+ */
46
+ contentDOMElement: HTMLElement | null;
47
+ /**
48
+ * Setup the React component.
49
+ * Called on initialization.
50
+ */
51
+ mount(): void;
52
+ /**
53
+ * Return the DOM element.
54
+ * This is the element that will be used to display the node view.
55
+ */
56
+ get dom(): HTMLElement;
57
+ /**
58
+ * Return the content DOM element.
59
+ * This is the element that will be used to display the rich-text content of the node.
60
+ */
61
+ get contentDOM(): HTMLElement | null;
62
+ /**
63
+ * On editor selection update, check if the node is selected.
64
+ * If it is, call `selectNode`, otherwise call `deselectNode`.
65
+ */
66
+ handleSelectionUpdate(): void;
67
+ /**
68
+ * On update, update the React component.
69
+ * To prevent unnecessary updates, the `update` option can be used.
70
+ */
71
+ update(node: Node, decorations: readonly Decoration[], innerDecorations: DecorationSource): boolean;
72
+ /**
73
+ * Select the node.
74
+ * Add the `selected` prop and the `ProseMirror-selectednode` class.
75
+ */
76
+ selectNode(): void;
77
+ /**
78
+ * Deselect the node.
79
+ * Remove the `selected` prop and the `ProseMirror-selectednode` class.
80
+ */
81
+ deselectNode(): void;
82
+ /**
83
+ * Destroy the React component instance.
84
+ */
85
+ destroy(): void;
86
+ /**
87
+ * Update the attributes of the top-level element that holds the React component.
88
+ * Applying the attributes defined in the `attrs` option.
89
+ */
90
+ updateElementAttributes(): void;
91
+ }
92
+ /**
93
+ * Create a React node view renderer.
94
+ */
95
+ export declare function ReactNodeViewRenderer(component: ComponentType<NodeViewProps>, options?: Partial<ReactNodeViewRendererOptions>): NodeViewRenderer;
@@ -25,13 +25,6 @@ export interface ReactRendererOptions {
25
25
  * @example 'foo bar'
26
26
  */
27
27
  className?: string;
28
- /**
29
- * The attributes of the element.
30
- * @type {Record<string, string>}
31
- * @default {}
32
- * @example { 'data-foo': 'bar' }
33
- */
34
- attrs?: Record<string, string>;
35
28
  }
36
29
  type ComponentType<R, P> = React.ComponentClass<P> | React.FunctionComponent<P> | React.ForwardRefExoticComponent<React.PropsWithoutRef<P> & React.RefAttributes<R>>;
37
30
  /**
@@ -45,17 +38,33 @@ type ComponentType<R, P> = React.ComponentClass<P> | React.FunctionComponent<P>
45
38
  * as: 'span',
46
39
  * })
47
40
  */
48
- export declare class ReactRenderer<R = unknown, P = unknown> {
41
+ export declare class ReactRenderer<R = unknown, P extends Record<string, any> = {}> {
49
42
  id: string;
50
43
  editor: Editor;
51
44
  component: any;
52
45
  element: Element;
53
- props: Record<string, any>;
46
+ props: P;
54
47
  reactElement: React.ReactNode;
55
48
  ref: R | null;
56
- constructor(component: ComponentType<R, P>, { editor, props, as, className, attrs, }: ReactRendererOptions);
49
+ /**
50
+ * Immediately creates element and renders the provided React component.
51
+ */
52
+ constructor(component: ComponentType<R, P>, { editor, props, as, className, }: ReactRendererOptions);
53
+ /**
54
+ * Render the React component.
55
+ */
57
56
  render(): void;
57
+ /**
58
+ * Re-renders the React component with new props.
59
+ */
58
60
  updateProps(props?: Record<string, any>): void;
61
+ /**
62
+ * Destroy the React component.
63
+ */
59
64
  destroy(): void;
65
+ /**
66
+ * Update the attributes of the element that holds the React component.
67
+ */
68
+ updateAttributes(attributes: Record<string, string>): void;
60
69
  }
61
70
  export {};
@@ -14,9 +14,31 @@ export type UseEditorStateOptions<TSelectorResult, TEditor extends Editor | null
14
14
  selector: (context: EditorStateSnapshot<TEditor>) => TSelectorResult;
15
15
  /**
16
16
  * A custom equality function to determine if the editor should re-render.
17
- * @default `(a, b) => a === b`
17
+ * @default `deepEqual` from `fast-deep-equal`
18
18
  */
19
19
  equalityFn?: (a: TSelectorResult, b: TSelectorResult | null) => boolean;
20
20
  };
21
+ /**
22
+ * This hook allows you to watch for changes on the editor instance.
23
+ * It will allow you to select a part of the editor state and re-render the component when it changes.
24
+ * @example
25
+ * ```tsx
26
+ * const editor = useEditor({...options})
27
+ * const { currentSelection } = useEditorState({
28
+ * editor,
29
+ * selector: snapshot => ({ currentSelection: snapshot.editor.state.selection }),
30
+ * })
31
+ */
21
32
  export declare function useEditorState<TSelectorResult>(options: UseEditorStateOptions<TSelectorResult, Editor>): TSelectorResult;
33
+ /**
34
+ * This hook allows you to watch for changes on the editor instance.
35
+ * It will allow you to select a part of the editor state and re-render the component when it changes.
36
+ * @example
37
+ * ```tsx
38
+ * const editor = useEditor({...options})
39
+ * const { currentSelection } = useEditorState({
40
+ * editor,
41
+ * selector: snapshot => ({ currentSelection: snapshot.editor.state.selection }),
42
+ * })
43
+ */
22
44
  export declare function useEditorState<TSelectorResult>(options: UseEditorStateOptions<TSelectorResult, Editor | null>): TSelectorResult | null;
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.6.6",
4
+ "version": "2.7.0",
5
5
  "homepage": "https://tiptap.dev",
6
6
  "keywords": [
7
7
  "tiptap",
@@ -29,22 +29,23 @@
29
29
  "dist"
30
30
  ],
31
31
  "dependencies": {
32
- "@tiptap/extension-bubble-menu": "^2.6.6",
33
- "@tiptap/extension-floating-menu": "^2.6.6",
32
+ "@tiptap/extension-bubble-menu": "^2.7.0",
33
+ "@tiptap/extension-floating-menu": "^2.7.0",
34
34
  "@types/use-sync-external-store": "^0.0.6",
35
+ "fast-deep-equal": "^3",
35
36
  "use-sync-external-store": "^1.2.2"
36
37
  },
37
38
  "devDependencies": {
38
- "@tiptap/core": "^2.6.6",
39
- "@tiptap/pm": "^2.6.6",
39
+ "@tiptap/core": "^2.7.0",
40
+ "@tiptap/pm": "^2.7.0",
40
41
  "@types/react": "^18.2.14",
41
42
  "@types/react-dom": "^18.2.6",
42
43
  "react": "^18.0.0",
43
44
  "react-dom": "^18.0.0"
44
45
  },
45
46
  "peerDependencies": {
46
- "@tiptap/core": "^2.6.6",
47
- "@tiptap/pm": "^2.6.6",
47
+ "@tiptap/core": "^2.7.0-pre.0",
48
+ "@tiptap/pm": "^2.7.0-pre.0",
48
49
  "react": "^17.0.0 || ^18.0.0",
49
50
  "react-dom": "^17.0.0 || ^18.0.0"
50
51
  },
@@ -1,55 +1,90 @@
1
1
  import {
2
2
  DecorationWithType,
3
3
  Editor,
4
+ getRenderedAttributes,
4
5
  NodeView,
5
6
  NodeViewProps,
6
7
  NodeViewRenderer,
7
8
  NodeViewRendererOptions,
8
- NodeViewRendererProps,
9
9
  } from '@tiptap/core'
10
- import { Node as ProseMirrorNode } from '@tiptap/pm/model'
11
- import { Decoration, NodeView as ProseMirrorNodeView } from '@tiptap/pm/view'
12
- import React from 'react'
10
+ import { Node, Node as ProseMirrorNode } from '@tiptap/pm/model'
11
+ import { Decoration, DecorationSource, NodeView as ProseMirrorNodeView } from '@tiptap/pm/view'
12
+ import React, { ComponentType } from 'react'
13
13
 
14
14
  import { EditorWithContentComponent } from './Editor.js'
15
15
  import { ReactRenderer } from './ReactRenderer.js'
16
16
  import { ReactNodeViewContext, ReactNodeViewContextProps } from './useReactNodeView.js'
17
17
 
18
18
  export interface ReactNodeViewRendererOptions extends NodeViewRendererOptions {
19
+ /**
20
+ * This function is called when the node view is updated.
21
+ * It allows you to compare the old node with the new node and decide if the component should update.
22
+ */
19
23
  update:
20
24
  | ((props: {
21
- oldNode: ProseMirrorNode
22
- oldDecorations: Decoration[]
23
- newNode: ProseMirrorNode
24
- newDecorations: Decoration[]
25
- updateProps: () => void
25
+ oldNode: ProseMirrorNode;
26
+ oldDecorations: readonly Decoration[];
27
+ oldInnerDecorations: DecorationSource;
28
+ newNode: ProseMirrorNode;
29
+ newDecorations: readonly Decoration[];
30
+ innerDecorations: DecorationSource;
31
+ updateProps: () => void;
26
32
  }) => boolean)
27
- | null
28
- as?: string
29
- className?: string
30
- attrs?: Record<string, string>
33
+ | null;
34
+ /**
35
+ * The tag name of the element wrapping the React component.
36
+ */
37
+ as?: string;
38
+ /**
39
+ * The class name of the element wrapping the React component.
40
+ */
41
+ className?: string;
42
+ /**
43
+ * Attributes that should be applied to the element wrapping the React component.
44
+ * If this is a function, it will be called each time the node view is updated.
45
+ * If this is an object, it will be applied once when the node view is mounted.
46
+ */
47
+ attrs?:
48
+ | Record<string, string>
49
+ | ((props: {
50
+ node: ProseMirrorNode;
51
+ HTMLAttributes: Record<string, any>;
52
+ }) => Record<string, string>);
31
53
  }
32
54
 
33
- class ReactNodeView extends NodeView<
34
- React.FunctionComponent,
35
- Editor,
36
- ReactNodeViewRendererOptions
37
- > {
38
- renderer!: ReactRenderer
39
-
55
+ export class ReactNodeView<
56
+ Component extends ComponentType<NodeViewProps> = ComponentType<NodeViewProps>,
57
+ NodeEditor extends Editor = Editor,
58
+ Options extends ReactNodeViewRendererOptions = ReactNodeViewRendererOptions,
59
+ > extends NodeView<Component, NodeEditor, Options> {
60
+ /**
61
+ * The renderer instance.
62
+ */
63
+ renderer!: ReactRenderer<unknown, NodeViewProps>
64
+
65
+ /**
66
+ * The element that holds the rich-text content of the node.
67
+ */
40
68
  contentDOMElement!: HTMLElement | null
41
69
 
70
+ /**
71
+ * Setup the React component.
72
+ * Called on initialization.
73
+ */
42
74
  mount() {
43
- const props: NodeViewProps = {
75
+ const props = {
44
76
  editor: this.editor,
45
77
  node: this.node,
46
- decorations: this.decorations,
78
+ decorations: this.decorations as DecorationWithType[],
79
+ innerDecorations: this.innerDecorations,
80
+ view: this.view,
47
81
  selected: false,
48
82
  extension: this.extension,
83
+ HTMLAttributes: this.HTMLAttributes,
49
84
  getPos: () => this.getPos(),
50
85
  updateAttributes: (attributes = {}) => this.updateAttributes(attributes),
51
86
  deleteNode: () => this.deleteNode(),
52
- }
87
+ } satisfies NodeViewProps
53
88
 
54
89
  if (!(this.component as any).displayName) {
55
90
  const capitalizeFirstChar = (string: string): string => {
@@ -69,13 +104,15 @@ class ReactNodeView extends NodeView<
69
104
  const Component = this.component
70
105
  // For performance reasons, we memoize the provider component
71
106
  // 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 => {
73
- return (
74
- <ReactNodeViewContext.Provider value={context}>
75
- {React.createElement(Component, componentProps)}
76
- </ReactNodeViewContext.Provider>
77
- )
78
- })
107
+ const ReactNodeViewProvider: React.FunctionComponent<NodeViewProps> = React.memo(
108
+ componentProps => {
109
+ return (
110
+ <ReactNodeViewContext.Provider value={context}>
111
+ {React.createElement(Component, componentProps)}
112
+ </ReactNodeViewContext.Provider>
113
+ )
114
+ },
115
+ )
79
116
 
80
117
  ReactNodeViewProvider.displayName = 'ReactNodeView'
81
118
 
@@ -88,6 +125,7 @@ class ReactNodeView extends NodeView<
88
125
  }
89
126
 
90
127
  if (this.contentDOMElement) {
128
+ this.contentDOMElement.dataset.nodeViewContentReact = ''
91
129
  // For some reason the whiteSpace prop is not inherited properly in Chrome and Safari
92
130
  // With this fix it seems to work fine
93
131
  // See: https://github.com/ueberdosis/tiptap/issues/1197
@@ -110,10 +148,15 @@ class ReactNodeView extends NodeView<
110
148
  props,
111
149
  as,
112
150
  className: `node-${this.node.type.name} ${className}`.trim(),
113
- attrs: this.options.attrs,
114
151
  })
152
+
153
+ this.updateElementAttributes()
115
154
  }
116
155
 
156
+ /**
157
+ * Return the DOM element.
158
+ * This is the element that will be used to display the node view.
159
+ */
117
160
  get dom() {
118
161
  if (
119
162
  this.renderer.element.firstElementChild
@@ -125,6 +168,10 @@ class ReactNodeView extends NodeView<
125
168
  return this.renderer.element as HTMLElement
126
169
  }
127
170
 
171
+ /**
172
+ * Return the content DOM element.
173
+ * This is the element that will be used to display the rich-text content of the node.
174
+ */
128
175
  get contentDOM() {
129
176
  if (this.node.isLeaf) {
130
177
  return null
@@ -133,10 +180,19 @@ class ReactNodeView extends NodeView<
133
180
  return this.contentDOMElement
134
181
  }
135
182
 
183
+ /**
184
+ * On editor selection update, check if the node is selected.
185
+ * If it is, call `selectNode`, otherwise call `deselectNode`.
186
+ */
136
187
  handleSelectionUpdate() {
137
188
  const { from, to } = this.editor.state.selection
189
+ const pos = this.getPos()
190
+
191
+ if (typeof pos !== 'number') {
192
+ return
193
+ }
138
194
 
139
- if (from <= this.getPos() && to >= this.getPos() + this.node.nodeSize) {
195
+ if (from <= pos && to >= pos + this.node.nodeSize) {
140
196
  if (this.renderer.props.selected) {
141
197
  return
142
198
  }
@@ -151,9 +207,20 @@ class ReactNodeView extends NodeView<
151
207
  }
152
208
  }
153
209
 
154
- update(node: ProseMirrorNode, decorations: DecorationWithType[]) {
155
- const updateProps = (props?: Record<string, any>) => {
210
+ /**
211
+ * On update, update the React component.
212
+ * To prevent unnecessary updates, the `update` option can be used.
213
+ */
214
+ update(
215
+ node: Node,
216
+ decorations: readonly Decoration[],
217
+ innerDecorations: DecorationSource,
218
+ ): boolean {
219
+ const rerenderComponent = (props?: Record<string, any>) => {
156
220
  this.renderer.updateProps(props)
221
+ if (typeof this.options.attrs === 'function') {
222
+ this.updateElementAttributes()
223
+ }
157
224
  }
158
225
 
159
226
  if (node.type !== this.node.type) {
@@ -163,31 +230,44 @@ class ReactNodeView extends NodeView<
163
230
  if (typeof this.options.update === 'function') {
164
231
  const oldNode = this.node
165
232
  const oldDecorations = this.decorations
233
+ const oldInnerDecorations = this.innerDecorations
166
234
 
167
235
  this.node = node
168
236
  this.decorations = decorations
237
+ this.innerDecorations = innerDecorations
169
238
 
170
239
  return this.options.update({
171
240
  oldNode,
172
241
  oldDecorations,
173
242
  newNode: node,
174
243
  newDecorations: decorations,
175
- updateProps: () => updateProps({ node, decorations }),
244
+ oldInnerDecorations,
245
+ innerDecorations,
246
+ updateProps: () => rerenderComponent({ node, decorations, innerDecorations }),
176
247
  })
177
248
  }
178
249
 
179
- if (node === this.node && this.decorations === decorations) {
250
+ if (
251
+ node === this.node
252
+ && this.decorations === decorations
253
+ && this.innerDecorations === innerDecorations
254
+ ) {
180
255
  return true
181
256
  }
182
257
 
183
258
  this.node = node
184
259
  this.decorations = decorations
260
+ this.innerDecorations = innerDecorations
185
261
 
186
- updateProps({ node, decorations })
262
+ rerenderComponent({ node, decorations, innerDecorations })
187
263
 
188
264
  return true
189
265
  }
190
266
 
267
+ /**
268
+ * Select the node.
269
+ * Add the `selected` prop and the `ProseMirror-selectednode` class.
270
+ */
191
271
  selectNode() {
192
272
  this.renderer.updateProps({
193
273
  selected: true,
@@ -195,6 +275,10 @@ class ReactNodeView extends NodeView<
195
275
  this.renderer.element.classList.add('ProseMirror-selectednode')
196
276
  }
197
277
 
278
+ /**
279
+ * Deselect the node.
280
+ * Remove the `selected` prop and the `ProseMirror-selectednode` class.
281
+ */
198
282
  deselectNode() {
199
283
  this.renderer.updateProps({
200
284
  selected: false,
@@ -202,25 +286,52 @@ class ReactNodeView extends NodeView<
202
286
  this.renderer.element.classList.remove('ProseMirror-selectednode')
203
287
  }
204
288
 
289
+ /**
290
+ * Destroy the React component instance.
291
+ */
205
292
  destroy() {
206
293
  this.renderer.destroy()
207
294
  this.editor.off('selectionUpdate', this.handleSelectionUpdate)
208
295
  this.contentDOMElement = null
209
296
  }
297
+
298
+ /**
299
+ * Update the attributes of the top-level element that holds the React component.
300
+ * Applying the attributes defined in the `attrs` option.
301
+ */
302
+ updateElementAttributes() {
303
+ if (this.options.attrs) {
304
+ let attrsObj: Record<string, string> = {}
305
+
306
+ if (typeof this.options.attrs === 'function') {
307
+ const extensionAttributes = this.editor.extensionManager.attributes
308
+ const HTMLAttributes = getRenderedAttributes(this.node, extensionAttributes)
309
+
310
+ attrsObj = this.options.attrs({ node: this.node, HTMLAttributes })
311
+ } else {
312
+ attrsObj = this.options.attrs
313
+ }
314
+
315
+ this.renderer.updateAttributes(attrsObj)
316
+ }
317
+ }
210
318
  }
211
319
 
320
+ /**
321
+ * Create a React node view renderer.
322
+ */
212
323
  export function ReactNodeViewRenderer(
213
- component: any,
324
+ component: ComponentType<NodeViewProps>,
214
325
  options?: Partial<ReactNodeViewRendererOptions>,
215
326
  ): NodeViewRenderer {
216
- return (props: NodeViewRendererProps) => {
327
+ return props => {
217
328
  // try to get the parent component
218
329
  // this is important for vue devtools to show the component hierarchy correctly
219
330
  // maybe it’s `undefined` because <editor-content> isn’t rendered yet
220
331
  if (!(props.editor as EditorWithContentComponent).contentComponent) {
221
- return {}
332
+ return {} as unknown as ProseMirrorNodeView
222
333
  }
223
334
 
224
- return new ReactNodeView(component, props, options) as unknown as ProseMirrorNodeView
335
+ return new ReactNodeView(component, props, options)
225
336
  }
226
337
  }