@tiptap/react 3.0.0-next.0 → 3.0.0-next.2

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 (201) hide show
  1. package/dist/index.cjs +996 -1126
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.d.cts +334 -0
  4. package/dist/index.d.ts +334 -0
  5. package/dist/index.js +960 -1098
  6. package/dist/index.js.map +1 -1
  7. package/package.json +14 -15
  8. package/src/BubbleMenu.tsx +70 -50
  9. package/src/Context.tsx +15 -7
  10. package/src/Editor.ts +7 -8
  11. package/src/EditorContent.tsx +98 -50
  12. package/src/FloatingMenu.tsx +51 -45
  13. package/src/NodeViewContent.tsx +1 -0
  14. package/src/NodeViewWrapper.tsx +1 -0
  15. package/src/ReactNodeViewRenderer.tsx +163 -53
  16. package/src/ReactRenderer.tsx +42 -23
  17. package/src/index.ts +0 -1
  18. package/src/useEditor.ts +253 -92
  19. package/src/useEditorState.ts +122 -69
  20. package/dist/index.umd.js +0 -1176
  21. package/dist/index.umd.js.map +0 -1
  22. package/dist/packages/core/src/CommandManager.d.ts +0 -20
  23. package/dist/packages/core/src/Editor.d.ts +0 -159
  24. package/dist/packages/core/src/EventEmitter.d.ts +0 -11
  25. package/dist/packages/core/src/Extension.d.ts +0 -343
  26. package/dist/packages/core/src/ExtensionManager.d.ts +0 -55
  27. package/dist/packages/core/src/InputRule.d.ts +0 -42
  28. package/dist/packages/core/src/Mark.d.ts +0 -451
  29. package/dist/packages/core/src/Node.d.ts +0 -611
  30. package/dist/packages/core/src/NodePos.d.ts +0 -44
  31. package/dist/packages/core/src/NodeView.d.ts +0 -31
  32. package/dist/packages/core/src/PasteRule.d.ts +0 -50
  33. package/dist/packages/core/src/Tracker.d.ts +0 -11
  34. package/dist/packages/core/src/commands/blur.d.ts +0 -13
  35. package/dist/packages/core/src/commands/clearContent.d.ts +0 -14
  36. package/dist/packages/core/src/commands/clearNodes.d.ts +0 -13
  37. package/dist/packages/core/src/commands/command.d.ts +0 -18
  38. package/dist/packages/core/src/commands/createParagraphNear.d.ts +0 -13
  39. package/dist/packages/core/src/commands/cut.d.ts +0 -20
  40. package/dist/packages/core/src/commands/deleteCurrentNode.d.ts +0 -13
  41. package/dist/packages/core/src/commands/deleteNode.d.ts +0 -15
  42. package/dist/packages/core/src/commands/deleteRange.d.ts +0 -14
  43. package/dist/packages/core/src/commands/deleteSelection.d.ts +0 -13
  44. package/dist/packages/core/src/commands/enter.d.ts +0 -13
  45. package/dist/packages/core/src/commands/exitCode.d.ts +0 -13
  46. package/dist/packages/core/src/commands/extendMarkRange.d.ts +0 -25
  47. package/dist/packages/core/src/commands/first.d.ts +0 -14
  48. package/dist/packages/core/src/commands/focus.d.ts +0 -27
  49. package/dist/packages/core/src/commands/forEach.d.ts +0 -14
  50. package/dist/packages/core/src/commands/index.d.ts +0 -55
  51. package/dist/packages/core/src/commands/insertContent.d.ts +0 -34
  52. package/dist/packages/core/src/commands/insertContentAt.d.ts +0 -47
  53. package/dist/packages/core/src/commands/join.d.ts +0 -41
  54. package/dist/packages/core/src/commands/joinItemBackward.d.ts +0 -13
  55. package/dist/packages/core/src/commands/joinItemForward.d.ts +0 -13
  56. package/dist/packages/core/src/commands/joinTextblockBackward.d.ts +0 -12
  57. package/dist/packages/core/src/commands/joinTextblockForward.d.ts +0 -12
  58. package/dist/packages/core/src/commands/keyboardShortcut.d.ts +0 -14
  59. package/dist/packages/core/src/commands/lift.d.ts +0 -17
  60. package/dist/packages/core/src/commands/liftEmptyBlock.d.ts +0 -13
  61. package/dist/packages/core/src/commands/liftListItem.d.ts +0 -15
  62. package/dist/packages/core/src/commands/newlineInCode.d.ts +0 -13
  63. package/dist/packages/core/src/commands/resetAttributes.d.ts +0 -16
  64. package/dist/packages/core/src/commands/scrollIntoView.d.ts +0 -13
  65. package/dist/packages/core/src/commands/selectAll.d.ts +0 -13
  66. package/dist/packages/core/src/commands/selectNodeBackward.d.ts +0 -13
  67. package/dist/packages/core/src/commands/selectNodeForward.d.ts +0 -13
  68. package/dist/packages/core/src/commands/selectParentNode.d.ts +0 -13
  69. package/dist/packages/core/src/commands/selectTextblockEnd.d.ts +0 -13
  70. package/dist/packages/core/src/commands/selectTextblockStart.d.ts +0 -13
  71. package/dist/packages/core/src/commands/setContent.d.ts +0 -40
  72. package/dist/packages/core/src/commands/setMark.d.ts +0 -15
  73. package/dist/packages/core/src/commands/setMeta.d.ts +0 -15
  74. package/dist/packages/core/src/commands/setNode.d.ts +0 -16
  75. package/dist/packages/core/src/commands/setNodeSelection.d.ts +0 -14
  76. package/dist/packages/core/src/commands/setTextSelection.d.ts +0 -14
  77. package/dist/packages/core/src/commands/sinkListItem.d.ts +0 -15
  78. package/dist/packages/core/src/commands/splitBlock.d.ts +0 -17
  79. package/dist/packages/core/src/commands/splitListItem.d.ts +0 -15
  80. package/dist/packages/core/src/commands/toggleList.d.ts +0 -18
  81. package/dist/packages/core/src/commands/toggleMark.d.ts +0 -30
  82. package/dist/packages/core/src/commands/toggleNode.d.ts +0 -17
  83. package/dist/packages/core/src/commands/toggleWrap.d.ts +0 -16
  84. package/dist/packages/core/src/commands/undoInputRule.d.ts +0 -13
  85. package/dist/packages/core/src/commands/unsetAllMarks.d.ts +0 -13
  86. package/dist/packages/core/src/commands/unsetMark.d.ts +0 -25
  87. package/dist/packages/core/src/commands/updateAttributes.d.ts +0 -24
  88. package/dist/packages/core/src/commands/wrapIn.d.ts +0 -16
  89. package/dist/packages/core/src/commands/wrapInList.d.ts +0 -16
  90. package/dist/packages/core/src/extensions/clipboardTextSerializer.d.ts +0 -5
  91. package/dist/packages/core/src/extensions/commands.d.ts +0 -3
  92. package/dist/packages/core/src/extensions/editable.d.ts +0 -2
  93. package/dist/packages/core/src/extensions/focusEvents.d.ts +0 -2
  94. package/dist/packages/core/src/extensions/index.d.ts +0 -6
  95. package/dist/packages/core/src/extensions/keymap.d.ts +0 -2
  96. package/dist/packages/core/src/extensions/tabindex.d.ts +0 -2
  97. package/dist/packages/core/src/helpers/combineTransactionSteps.d.ts +0 -10
  98. package/dist/packages/core/src/helpers/createChainableState.d.ts +0 -10
  99. package/dist/packages/core/src/helpers/createDocument.d.ts +0 -12
  100. package/dist/packages/core/src/helpers/createNodeFromContent.d.ts +0 -15
  101. package/dist/packages/core/src/helpers/defaultBlockAt.d.ts +0 -7
  102. package/dist/packages/core/src/helpers/findChildren.d.ts +0 -9
  103. package/dist/packages/core/src/helpers/findChildrenInRange.d.ts +0 -10
  104. package/dist/packages/core/src/helpers/findParentNode.d.ts +0 -16
  105. package/dist/packages/core/src/helpers/findParentNodeClosestToPos.d.ts +0 -17
  106. package/dist/packages/core/src/helpers/generateHTML.d.ts +0 -8
  107. package/dist/packages/core/src/helpers/generateJSON.d.ts +0 -8
  108. package/dist/packages/core/src/helpers/generateText.d.ts +0 -12
  109. package/dist/packages/core/src/helpers/getAttributes.d.ts +0 -9
  110. package/dist/packages/core/src/helpers/getAttributesFromExtensions.d.ts +0 -6
  111. package/dist/packages/core/src/helpers/getChangedRanges.d.ts +0 -11
  112. package/dist/packages/core/src/helpers/getDebugJSON.d.ts +0 -8
  113. package/dist/packages/core/src/helpers/getExtensionField.d.ts +0 -9
  114. package/dist/packages/core/src/helpers/getHTMLFromFragment.d.ts +0 -2
  115. package/dist/packages/core/src/helpers/getMarkAttributes.d.ts +0 -3
  116. package/dist/packages/core/src/helpers/getMarkRange.d.ts +0 -3
  117. package/dist/packages/core/src/helpers/getMarkType.d.ts +0 -2
  118. package/dist/packages/core/src/helpers/getMarksBetween.d.ts +0 -3
  119. package/dist/packages/core/src/helpers/getNodeAtPosition.d.ts +0 -11
  120. package/dist/packages/core/src/helpers/getNodeAttributes.d.ts +0 -3
  121. package/dist/packages/core/src/helpers/getNodeType.d.ts +0 -2
  122. package/dist/packages/core/src/helpers/getRenderedAttributes.d.ts +0 -3
  123. package/dist/packages/core/src/helpers/getSchema.d.ts +0 -4
  124. package/dist/packages/core/src/helpers/getSchemaByResolvedExtensions.d.ts +0 -10
  125. package/dist/packages/core/src/helpers/getSchemaTypeByName.d.ts +0 -8
  126. package/dist/packages/core/src/helpers/getSchemaTypeNameByName.d.ts +0 -8
  127. package/dist/packages/core/src/helpers/getSplittedAttributes.d.ts +0 -9
  128. package/dist/packages/core/src/helpers/getText.d.ts +0 -15
  129. package/dist/packages/core/src/helpers/getTextBetween.d.ts +0 -14
  130. package/dist/packages/core/src/helpers/getTextContentFromNodes.d.ts +0 -8
  131. package/dist/packages/core/src/helpers/getTextSerializersFromSchema.d.ts +0 -8
  132. package/dist/packages/core/src/helpers/index.d.ts +0 -50
  133. package/dist/packages/core/src/helpers/injectExtensionAttributesToParseRule.d.ts +0 -9
  134. package/dist/packages/core/src/helpers/isActive.d.ts +0 -2
  135. package/dist/packages/core/src/helpers/isAtEndOfNode.d.ts +0 -2
  136. package/dist/packages/core/src/helpers/isAtStartOfNode.d.ts +0 -2
  137. package/dist/packages/core/src/helpers/isExtensionRulesEnabled.d.ts +0 -2
  138. package/dist/packages/core/src/helpers/isList.d.ts +0 -2
  139. package/dist/packages/core/src/helpers/isMarkActive.d.ts +0 -3
  140. package/dist/packages/core/src/helpers/isNodeActive.d.ts +0 -3
  141. package/dist/packages/core/src/helpers/isNodeEmpty.d.ts +0 -8
  142. package/dist/packages/core/src/helpers/isNodeSelection.d.ts +0 -2
  143. package/dist/packages/core/src/helpers/isTextSelection.d.ts +0 -2
  144. package/dist/packages/core/src/helpers/posToDOMRect.d.ts +0 -2
  145. package/dist/packages/core/src/helpers/resolveFocusPosition.d.ts +0 -4
  146. package/dist/packages/core/src/helpers/selectionToInsertionEnd.d.ts +0 -2
  147. package/dist/packages/core/src/helpers/splitExtensions.d.ts +0 -9
  148. package/dist/packages/core/src/index.d.ts +0 -24
  149. package/dist/packages/core/src/inputRules/index.d.ts +0 -5
  150. package/dist/packages/core/src/inputRules/markInputRule.d.ts +0 -13
  151. package/dist/packages/core/src/inputRules/nodeInputRule.d.ts +0 -23
  152. package/dist/packages/core/src/inputRules/textInputRule.d.ts +0 -10
  153. package/dist/packages/core/src/inputRules/textblockTypeInputRule.d.ts +0 -15
  154. package/dist/packages/core/src/inputRules/wrappingInputRule.d.ts +0 -28
  155. package/dist/packages/core/src/pasteRules/index.d.ts +0 -3
  156. package/dist/packages/core/src/pasteRules/markPasteRule.d.ts +0 -13
  157. package/dist/packages/core/src/pasteRules/nodePasteRule.d.ts +0 -13
  158. package/dist/packages/core/src/pasteRules/textPasteRule.d.ts +0 -10
  159. package/dist/packages/core/src/style.d.ts +0 -1
  160. package/dist/packages/core/src/types.d.ts +0 -253
  161. package/dist/packages/core/src/utilities/callOrReturn.d.ts +0 -9
  162. package/dist/packages/core/src/utilities/createStyleTag.d.ts +0 -1
  163. package/dist/packages/core/src/utilities/deleteProps.d.ts +0 -6
  164. package/dist/packages/core/src/utilities/elementFromString.d.ts +0 -1
  165. package/dist/packages/core/src/utilities/escapeForRegEx.d.ts +0 -1
  166. package/dist/packages/core/src/utilities/findDuplicates.d.ts +0 -1
  167. package/dist/packages/core/src/utilities/fromString.d.ts +0 -1
  168. package/dist/packages/core/src/utilities/index.d.ts +0 -20
  169. package/dist/packages/core/src/utilities/isAndroid.d.ts +0 -1
  170. package/dist/packages/core/src/utilities/isEmptyObject.d.ts +0 -1
  171. package/dist/packages/core/src/utilities/isFunction.d.ts +0 -1
  172. package/dist/packages/core/src/utilities/isMacOS.d.ts +0 -1
  173. package/dist/packages/core/src/utilities/isNumber.d.ts +0 -1
  174. package/dist/packages/core/src/utilities/isPlainObject.d.ts +0 -1
  175. package/dist/packages/core/src/utilities/isRegExp.d.ts +0 -1
  176. package/dist/packages/core/src/utilities/isString.d.ts +0 -1
  177. package/dist/packages/core/src/utilities/isiOS.d.ts +0 -1
  178. package/dist/packages/core/src/utilities/mergeAttributes.d.ts +0 -1
  179. package/dist/packages/core/src/utilities/mergeDeep.d.ts +0 -1
  180. package/dist/packages/core/src/utilities/minMax.d.ts +0 -1
  181. package/dist/packages/core/src/utilities/objectIncludes.d.ts +0 -8
  182. package/dist/packages/core/src/utilities/removeDuplicates.d.ts +0 -8
  183. package/dist/packages/extension-bubble-menu/src/bubble-menu-plugin.d.ts +0 -99
  184. package/dist/packages/extension-bubble-menu/src/bubble-menu.d.ts +0 -15
  185. package/dist/packages/extension-bubble-menu/src/index.d.ts +0 -4
  186. package/dist/packages/extension-floating-menu/src/floating-menu-plugin.d.ts +0 -81
  187. package/dist/packages/extension-floating-menu/src/floating-menu.d.ts +0 -15
  188. package/dist/packages/extension-floating-menu/src/index.d.ts +0 -4
  189. package/dist/packages/react/src/BubbleMenu.d.ts +0 -13
  190. package/dist/packages/react/src/Context.d.ts +0 -23
  191. package/dist/packages/react/src/Editor.d.ts +0 -12
  192. package/dist/packages/react/src/EditorContent.d.ts +0 -24
  193. package/dist/packages/react/src/FloatingMenu.d.ts +0 -11
  194. package/dist/packages/react/src/NodeViewContent.d.ts +0 -6
  195. package/dist/packages/react/src/NodeViewWrapper.d.ts +0 -6
  196. package/dist/packages/react/src/ReactNodeViewRenderer.d.ts +0 -16
  197. package/dist/packages/react/src/ReactRenderer.d.ts +0 -62
  198. package/dist/packages/react/src/index.d.ts +0 -13
  199. package/dist/packages/react/src/useEditor.d.ts +0 -39
  200. package/dist/packages/react/src/useEditorState.d.ts +0 -22
  201. package/dist/packages/react/src/useReactNodeView.d.ts +0 -6
@@ -1,54 +1,90 @@
1
1
  import {
2
2
  DecorationWithType,
3
+ Editor,
4
+ getRenderedAttributes,
3
5
  NodeView,
4
6
  NodeViewProps,
5
7
  NodeViewRenderer,
6
8
  NodeViewRendererOptions,
7
- NodeViewRendererProps,
8
9
  } from '@tiptap/core'
9
- import { Node as ProseMirrorNode } from '@tiptap/pm/model'
10
- import { Decoration, NodeView as ProseMirrorNodeView } from '@tiptap/pm/view'
11
- 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'
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
 
17
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
+ */
18
23
  update:
19
24
  | ((props: {
20
- oldNode: ProseMirrorNode
21
- oldDecorations: Decoration[]
22
- newNode: ProseMirrorNode
23
- newDecorations: Decoration[]
24
- 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;
25
32
  }) => boolean)
26
- | null
27
- as?: string
28
- className?: string
29
- 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>);
30
53
  }
31
54
 
32
- class ReactNodeView extends NodeView<
33
- React.FunctionComponent,
34
- Editor,
35
- ReactNodeViewRendererOptions
36
- > {
37
- renderer!: ReactRenderer
38
-
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
+ */
39
68
  contentDOMElement!: HTMLElement | null
40
69
 
70
+ /**
71
+ * Setup the React component.
72
+ * Called on initialization.
73
+ */
41
74
  mount() {
42
- const props: NodeViewProps = {
75
+ const props = {
43
76
  editor: this.editor,
44
77
  node: this.node,
45
- decorations: this.decorations,
78
+ decorations: this.decorations as DecorationWithType[],
79
+ innerDecorations: this.innerDecorations,
80
+ view: this.view,
46
81
  selected: false,
47
82
  extension: this.extension,
83
+ HTMLAttributes: this.HTMLAttributes,
48
84
  getPos: () => this.getPos(),
49
85
  updateAttributes: (attributes = {}) => this.updateAttributes(attributes),
50
86
  deleteNode: () => this.deleteNode(),
51
- }
87
+ } satisfies NodeViewProps
52
88
 
53
89
  if (!(this.component as any).displayName) {
54
90
  const capitalizeFirstChar = (string: string): string => {
@@ -58,25 +94,25 @@ class ReactNodeView extends NodeView<
58
94
  this.component.displayName = capitalizeFirstChar(this.extension.name)
59
95
  }
60
96
 
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
- }
97
+ const onDragStart = this.onDragStart.bind(this)
98
+ const nodeViewContentRef: ReactNodeViewContextProps['nodeViewContentRef'] = element => {
99
+ if (element && this.contentDOMElement && element.firstChild !== this.contentDOMElement) {
100
+ element.appendChild(this.contentDOMElement)
68
101
  }
69
-
70
- return (
71
- <>
72
- {/* @ts-ignore */}
73
- <ReactNodeViewContext.Provider value={{ onDragStart, nodeViewContentRef }}>
74
- {/* @ts-ignore */}
75
- <Component {...componentProps} />
76
- </ReactNodeViewContext.Provider>
77
- </>
78
- )
79
102
  }
103
+ const context = { onDragStart, nodeViewContentRef }
104
+ const Component = this.component
105
+ // For performance reasons, we memoize the provider component
106
+ // And all of the things it requires are declared outside of the component, so it doesn't need to re-render
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
+ )
80
116
 
81
117
  ReactNodeViewProvider.displayName = 'ReactNodeView'
82
118
 
@@ -89,6 +125,7 @@ class ReactNodeView extends NodeView<
89
125
  }
90
126
 
91
127
  if (this.contentDOMElement) {
128
+ this.contentDOMElement.dataset.nodeViewContentReact = ''
92
129
  // For some reason the whiteSpace prop is not inherited properly in Chrome and Safari
93
130
  // With this fix it seems to work fine
94
131
  // See: https://github.com/ueberdosis/tiptap/issues/1197
@@ -111,10 +148,15 @@ class ReactNodeView extends NodeView<
111
148
  props,
112
149
  as,
113
150
  className: `node-${this.node.type.name} ${className}`.trim(),
114
- attrs: this.options.attrs,
115
151
  })
152
+
153
+ this.updateElementAttributes()
116
154
  }
117
155
 
156
+ /**
157
+ * Return the DOM element.
158
+ * This is the element that will be used to display the node view.
159
+ */
118
160
  get dom() {
119
161
  if (
120
162
  this.renderer.element.firstElementChild
@@ -126,6 +168,10 @@ class ReactNodeView extends NodeView<
126
168
  return this.renderer.element as HTMLElement
127
169
  }
128
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
+ */
129
175
  get contentDOM() {
130
176
  if (this.node.isLeaf) {
131
177
  return null
@@ -134,10 +180,19 @@ class ReactNodeView extends NodeView<
134
180
  return this.contentDOMElement
135
181
  }
136
182
 
183
+ /**
184
+ * On editor selection update, check if the node is selected.
185
+ * If it is, call `selectNode`, otherwise call `deselectNode`.
186
+ */
137
187
  handleSelectionUpdate() {
138
188
  const { from, to } = this.editor.state.selection
189
+ const pos = this.getPos()
139
190
 
140
- if (from <= this.getPos() && to >= this.getPos() + this.node.nodeSize) {
191
+ if (typeof pos !== 'number') {
192
+ return
193
+ }
194
+
195
+ if (from <= pos && to >= pos + this.node.nodeSize) {
141
196
  if (this.renderer.props.selected) {
142
197
  return
143
198
  }
@@ -152,9 +207,20 @@ class ReactNodeView extends NodeView<
152
207
  }
153
208
  }
154
209
 
155
- update(node: ProseMirrorNode, decorations: DecorationWithType[]) {
156
- 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>) => {
157
220
  this.renderer.updateProps(props)
221
+ if (typeof this.options.attrs === 'function') {
222
+ this.updateElementAttributes()
223
+ }
158
224
  }
159
225
 
160
226
  if (node.type !== this.node.type) {
@@ -164,31 +230,44 @@ class ReactNodeView extends NodeView<
164
230
  if (typeof this.options.update === 'function') {
165
231
  const oldNode = this.node
166
232
  const oldDecorations = this.decorations
233
+ const oldInnerDecorations = this.innerDecorations
167
234
 
168
235
  this.node = node
169
236
  this.decorations = decorations
237
+ this.innerDecorations = innerDecorations
170
238
 
171
239
  return this.options.update({
172
240
  oldNode,
173
241
  oldDecorations,
174
242
  newNode: node,
175
243
  newDecorations: decorations,
176
- updateProps: () => updateProps({ node, decorations }),
244
+ oldInnerDecorations,
245
+ innerDecorations,
246
+ updateProps: () => rerenderComponent({ node, decorations, innerDecorations }),
177
247
  })
178
248
  }
179
249
 
180
- if (node === this.node && this.decorations === decorations) {
250
+ if (
251
+ node === this.node
252
+ && this.decorations === decorations
253
+ && this.innerDecorations === innerDecorations
254
+ ) {
181
255
  return true
182
256
  }
183
257
 
184
258
  this.node = node
185
259
  this.decorations = decorations
260
+ this.innerDecorations = innerDecorations
186
261
 
187
- updateProps({ node, decorations })
262
+ rerenderComponent({ node, decorations, innerDecorations })
188
263
 
189
264
  return true
190
265
  }
191
266
 
267
+ /**
268
+ * Select the node.
269
+ * Add the `selected` prop and the `ProseMirror-selectednode` class.
270
+ */
192
271
  selectNode() {
193
272
  this.renderer.updateProps({
194
273
  selected: true,
@@ -196,6 +275,10 @@ class ReactNodeView extends NodeView<
196
275
  this.renderer.element.classList.add('ProseMirror-selectednode')
197
276
  }
198
277
 
278
+ /**
279
+ * Deselect the node.
280
+ * Remove the `selected` prop and the `ProseMirror-selectednode` class.
281
+ */
199
282
  deselectNode() {
200
283
  this.renderer.updateProps({
201
284
  selected: false,
@@ -203,25 +286,52 @@ class ReactNodeView extends NodeView<
203
286
  this.renderer.element.classList.remove('ProseMirror-selectednode')
204
287
  }
205
288
 
289
+ /**
290
+ * Destroy the React component instance.
291
+ */
206
292
  destroy() {
207
293
  this.renderer.destroy()
208
294
  this.editor.off('selectionUpdate', this.handleSelectionUpdate)
209
295
  this.contentDOMElement = null
210
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
+ }
211
318
  }
212
319
 
320
+ /**
321
+ * Create a React node view renderer.
322
+ */
213
323
  export function ReactNodeViewRenderer(
214
- component: any,
324
+ component: ComponentType<NodeViewProps>,
215
325
  options?: Partial<ReactNodeViewRendererOptions>,
216
326
  ): NodeViewRenderer {
217
- return (props: NodeViewRendererProps) => {
327
+ return props => {
218
328
  // try to get the parent component
219
329
  // this is important for vue devtools to show the component hierarchy correctly
220
330
  // maybe it’s `undefined` because <editor-content> isn’t rendered yet
221
- if (!(props.editor as Editor).contentComponent) {
222
- return {}
331
+ if (!(props.editor as EditorWithContentComponent).contentComponent) {
332
+ return {} as unknown as ProseMirrorNodeView
223
333
  }
224
334
 
225
- return new ReactNodeView(component, props, options) as unknown as ProseMirrorNodeView
335
+ return new ReactNodeView(component, props, options)
226
336
  }
227
337
  }
@@ -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.
@@ -56,14 +57,6 @@ export interface ReactRendererOptions {
56
57
  * @example 'foo bar'
57
58
  */
58
59
  className?: string,
59
-
60
- /**
61
- * The attributes of the element.
62
- * @type {Record<string, string>}
63
- * @default {}
64
- * @example { 'data-foo': 'bar' }
65
- */
66
- attrs?: Record<string, string>,
67
60
  }
68
61
 
69
62
  type ComponentType<R, P> =
@@ -82,32 +75,34 @@ type ComponentType<R, P> =
82
75
  * as: 'span',
83
76
  * })
84
77
  */
85
- export class ReactRenderer<R = unknown, P = unknown> {
78
+ export class ReactRenderer<R = unknown, P extends Record<string, any> = {}> {
86
79
  id: string
87
80
 
88
- editor: ExtendedEditor
81
+ editor: Editor
89
82
 
90
83
  component: any
91
84
 
92
85
  element: Element
93
86
 
94
- props: Record<string, any>
87
+ props: P
95
88
 
96
89
  reactElement: React.ReactNode
97
90
 
98
91
  ref: R | null = null
99
92
 
93
+ /**
94
+ * Immediately creates element and renders the provided React component.
95
+ */
100
96
  constructor(component: ComponentType<R, P>, {
101
97
  editor,
102
98
  props = {},
103
99
  as = 'div',
104
100
  className = '',
105
- attrs,
106
101
  }: ReactRendererOptions) {
107
102
  this.id = Math.floor(Math.random() * 0xFFFFFFFF).toString()
108
103
  this.component = component
109
- this.editor = editor as ExtendedEditor
110
- this.props = props
104
+ this.editor = editor as EditorWithContentComponent
105
+ this.props = props as P
111
106
  this.element = document.createElement(as)
112
107
  this.element.classList.add('react-renderer')
113
108
 
@@ -115,30 +110,40 @@ export class ReactRenderer<R = unknown, P = unknown> {
115
110
  this.element.classList.add(...className.split(' '))
116
111
  }
117
112
 
118
- if (attrs) {
119
- Object.keys(attrs).forEach(key => {
120
- this.element.setAttribute(key, attrs[key])
113
+ if (this.editor.isInitialized) {
114
+ // On first render, we need to flush the render synchronously
115
+ // Renders afterwards can be async, but this fixes a cursor positioning issue
116
+ flushSync(() => {
117
+ this.render()
121
118
  })
119
+ } else {
120
+ this.render()
122
121
  }
123
-
124
- this.render()
125
122
  }
126
123
 
124
+ /**
125
+ * Render the React component.
126
+ */
127
127
  render(): void {
128
128
  const Component = this.component
129
129
  const props = this.props
130
+ const editor = this.editor as EditorWithContentComponent
130
131
 
131
132
  if (isClassComponent(Component) || isForwardRefComponent(Component)) {
133
+ // @ts-ignore This is a hack to make the ref work
132
134
  props.ref = (ref: R) => {
133
135
  this.ref = ref
134
136
  }
135
137
  }
136
138
 
137
- this.reactElement = <Component {...props } />
139
+ this.reactElement = <Component {...props} />
138
140
 
139
- this.editor?.contentComponent?.setRenderer(this.id, this)
141
+ editor?.contentComponent?.setRenderer(this.id, this)
140
142
  }
141
143
 
144
+ /**
145
+ * Re-renders the React component with new props.
146
+ */
142
147
  updateProps(props: Record<string, any> = {}): void {
143
148
  this.props = {
144
149
  ...this.props,
@@ -148,7 +153,21 @@ export class ReactRenderer<R = unknown, P = unknown> {
148
153
  this.render()
149
154
  }
150
155
 
156
+ /**
157
+ * Destroy the React component.
158
+ */
151
159
  destroy(): void {
152
- this.editor?.contentComponent?.removeRenderer(this.id)
160
+ const editor = this.editor as EditorWithContentComponent
161
+
162
+ editor?.contentComponent?.removeRenderer(this.id)
163
+ }
164
+
165
+ /**
166
+ * Update the attributes of the element that holds the React component.
167
+ */
168
+ updateAttributes(attributes: Record<string, string>): void {
169
+ Object.keys(attributes).forEach(key => {
170
+ this.element.setAttribute(key, attributes[key])
171
+ })
153
172
  }
154
173
  }
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'