@tiptap/react 2.6.5 → 2.7.0-pre.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.
@@ -57,14 +57,6 @@ export interface ReactRendererOptions {
57
57
  * @example 'foo bar'
58
58
  */
59
59
  className?: string,
60
-
61
- /**
62
- * The attributes of the element.
63
- * @type {Record<string, string>}
64
- * @default {}
65
- * @example { 'data-foo': 'bar' }
66
- */
67
- attrs?: Record<string, string>,
68
60
  }
69
61
 
70
62
  type ComponentType<R, P> =
@@ -83,7 +75,7 @@ type ComponentType<R, P> =
83
75
  * as: 'span',
84
76
  * })
85
77
  */
86
- export class ReactRenderer<R = unknown, P = unknown> {
78
+ export class ReactRenderer<R = unknown, P extends Record<string, any> = {}> {
87
79
  id: string
88
80
 
89
81
  editor: Editor
@@ -92,23 +84,25 @@ export class ReactRenderer<R = unknown, P = unknown> {
92
84
 
93
85
  element: Element
94
86
 
95
- props: Record<string, any>
87
+ props: P
96
88
 
97
89
  reactElement: React.ReactNode
98
90
 
99
91
  ref: R | null = null
100
92
 
93
+ /**
94
+ * Immediately creates element and renders the provided React component.
95
+ */
101
96
  constructor(component: ComponentType<R, P>, {
102
97
  editor,
103
98
  props = {},
104
99
  as = 'div',
105
100
  className = '',
106
- attrs,
107
101
  }: ReactRendererOptions) {
108
102
  this.id = Math.floor(Math.random() * 0xFFFFFFFF).toString()
109
103
  this.component = component
110
104
  this.editor = editor as EditorWithContentComponent
111
- this.props = props
105
+ this.props = props as P
112
106
  this.element = document.createElement(as)
113
107
  this.element.classList.add('react-renderer')
114
108
 
@@ -116,12 +110,6 @@ export class ReactRenderer<R = unknown, P = unknown> {
116
110
  this.element.classList.add(...className.split(' '))
117
111
  }
118
112
 
119
- if (attrs) {
120
- Object.keys(attrs).forEach(key => {
121
- this.element.setAttribute(key, attrs[key])
122
- })
123
- }
124
-
125
113
  if (this.editor.isInitialized) {
126
114
  // On first render, we need to flush the render synchronously
127
115
  // Renders afterwards can be async, but this fixes a cursor positioning issue
@@ -133,12 +121,16 @@ export class ReactRenderer<R = unknown, P = unknown> {
133
121
  }
134
122
  }
135
123
 
124
+ /**
125
+ * Render the React component.
126
+ */
136
127
  render(): void {
137
128
  const Component = this.component
138
129
  const props = this.props
139
130
  const editor = this.editor as EditorWithContentComponent
140
131
 
141
132
  if (isClassComponent(Component) || isForwardRefComponent(Component)) {
133
+ // @ts-ignore This is a hack to make the ref work
142
134
  props.ref = (ref: R) => {
143
135
  this.ref = ref
144
136
  }
@@ -149,6 +141,9 @@ export class ReactRenderer<R = unknown, P = unknown> {
149
141
  editor?.contentComponent?.setRenderer(this.id, this)
150
142
  }
151
143
 
144
+ /**
145
+ * Re-renders the React component with new props.
146
+ */
152
147
  updateProps(props: Record<string, any> = {}): void {
153
148
  this.props = {
154
149
  ...this.props,
@@ -158,9 +153,21 @@ export class ReactRenderer<R = unknown, P = unknown> {
158
153
  this.render()
159
154
  }
160
155
 
156
+ /**
157
+ * Destroy the React component.
158
+ */
161
159
  destroy(): void {
162
160
  const editor = this.editor as EditorWithContentComponent
163
161
 
164
162
  editor?.contentComponent?.removeRenderer(this.id)
165
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
+ })
172
+ }
166
173
  }
package/src/useEditor.ts CHANGED
@@ -149,6 +149,8 @@ class EditorInstanceManager {
149
149
  onTransaction: (...args) => this.options.current.onTransaction?.(...args),
150
150
  onUpdate: (...args) => this.options.current.onUpdate?.(...args),
151
151
  onContentError: (...args) => this.options.current.onContentError?.(...args),
152
+ onDrop: (...args) => this.options.current.onDrop?.(...args),
153
+ onPaste: (...args) => this.options.current.onPaste?.(...args),
152
154
  }
153
155
  const editor = new Editor(optionsToApply)
154
156
 
@@ -1,4 +1,5 @@
1
1
  import type { Editor } from '@tiptap/core'
2
+ import deepEqual from 'fast-deep-equal/es6/react'
2
3
  import { useDebugValue, useEffect, useState } from 'react'
3
4
  import { useSyncExternalStoreWithSelector } from 'use-sync-external-store/shim/with-selector'
4
5
 
@@ -6,6 +7,7 @@ export type EditorStateSnapshot<TEditor extends Editor | null = Editor | null> =
6
7
  editor: TEditor;
7
8
  transactionNumber: number;
8
9
  };
10
+
9
11
  export type UseEditorStateOptions<
10
12
  TSelectorResult,
11
13
  TEditor extends Editor | null = Editor | null,
@@ -20,7 +22,7 @@ export type UseEditorStateOptions<
20
22
  selector: (context: EditorStateSnapshot<TEditor>) => TSelectorResult;
21
23
  /**
22
24
  * A custom equality function to determine if the editor should re-render.
23
- * @default `(a, b) => a === b`
25
+ * @default `deepEqual` from `fast-deep-equal`
24
26
  */
25
27
  equalityFn?: (a: TSelectorResult, b: TSelectorResult | null) => boolean;
26
28
  };
@@ -108,30 +110,63 @@ class EditorStateManager<TEditor extends Editor | null = Editor | null> {
108
110
  }
109
111
  }
110
112
 
113
+ /**
114
+ * This hook allows you to watch for changes on the editor instance.
115
+ * It will allow you to select a part of the editor state and re-render the component when it changes.
116
+ * @example
117
+ * ```tsx
118
+ * const editor = useEditor({...options})
119
+ * const { currentSelection } = useEditorState({
120
+ * editor,
121
+ * selector: snapshot => ({ currentSelection: snapshot.editor.state.selection }),
122
+ * })
123
+ */
111
124
  export function useEditorState<TSelectorResult>(
112
125
  options: UseEditorStateOptions<TSelectorResult, Editor>
113
126
  ): TSelectorResult;
127
+ /**
128
+ * This hook allows you to watch for changes on the editor instance.
129
+ * It will allow you to select a part of the editor state and re-render the component when it changes.
130
+ * @example
131
+ * ```tsx
132
+ * const editor = useEditor({...options})
133
+ * const { currentSelection } = useEditorState({
134
+ * editor,
135
+ * selector: snapshot => ({ currentSelection: snapshot.editor.state.selection }),
136
+ * })
137
+ */
114
138
  export function useEditorState<TSelectorResult>(
115
139
  options: UseEditorStateOptions<TSelectorResult, Editor | null>
116
140
  ): TSelectorResult | null;
117
141
 
142
+ /**
143
+ * This hook allows you to watch for changes on the editor instance.
144
+ * It will allow you to select a part of the editor state and re-render the component when it changes.
145
+ * @example
146
+ * ```tsx
147
+ * const editor = useEditor({...options})
148
+ * const { currentSelection } = useEditorState({
149
+ * editor,
150
+ * selector: snapshot => ({ currentSelection: snapshot.editor.state.selection }),
151
+ * })
152
+ */
118
153
  export function useEditorState<TSelectorResult>(
119
154
  options: UseEditorStateOptions<TSelectorResult, Editor> | UseEditorStateOptions<TSelectorResult, Editor | null>,
120
155
  ): TSelectorResult | null {
121
- const [editorInstance] = useState(() => new EditorStateManager(options.editor))
156
+ const [editorStateManager] = useState(() => new EditorStateManager(options.editor))
122
157
 
123
158
  // Using the `useSyncExternalStore` hook to sync the editor instance with the component state
124
159
  const selectedState = useSyncExternalStoreWithSelector(
125
- editorInstance.subscribe,
126
- editorInstance.getSnapshot,
127
- editorInstance.getServerSnapshot,
160
+ editorStateManager.subscribe,
161
+ editorStateManager.getSnapshot,
162
+ editorStateManager.getServerSnapshot,
128
163
  options.selector as UseEditorStateOptions<TSelectorResult, Editor | null>['selector'],
129
- options.equalityFn,
164
+ options.equalityFn ?? deepEqual,
130
165
  )
131
166
 
132
167
  useEffect(() => {
133
- return editorInstance.watch(options.editor)
134
- }, [options.editor, editorInstance])
168
+ return editorStateManager.watch(options.editor)
169
+ }, [options.editor, editorStateManager])
135
170
 
136
171
  useDebugValue(selectedState)
137
172