@tiptap/react 2.11.7 → 3.0.0-beta.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.
- package/LICENSE.md +21 -0
- package/README.md +5 -1
- package/dist/index.cjs +967 -1473
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +345 -0
- package/dist/index.d.ts +344 -12
- package/dist/index.js +916 -1452
- package/dist/index.js.map +1 -1
- package/dist/menus/index.cjs +142 -0
- package/dist/menus/index.cjs.map +1 -0
- package/dist/menus/index.d.cts +19 -0
- package/dist/menus/index.d.ts +19 -0
- package/dist/menus/index.js +104 -0
- package/dist/menus/index.js.map +1 -0
- package/package.json +30 -18
- package/src/Context.tsx +18 -15
- package/src/Editor.ts +8 -8
- package/src/EditorContent.tsx +14 -22
- package/src/NodeViewContent.tsx +12 -8
- package/src/NodeViewWrapper.tsx +2 -2
- package/src/ReactMarkViewRenderer.tsx +109 -0
- package/src/ReactNodeViewRenderer.tsx +32 -50
- package/src/ReactRenderer.tsx +17 -26
- package/src/index.ts +1 -2
- package/src/menus/BubbleMenu.tsx +68 -0
- package/src/menus/FloatingMenu.tsx +68 -0
- package/src/menus/index.ts +2 -0
- package/src/useEditor.ts +32 -26
- package/src/useEditorState.ts +14 -19
- package/src/useReactNodeView.ts +21 -5
- package/dist/BubbleMenu.d.ts +0 -12
- package/dist/BubbleMenu.d.ts.map +0 -1
- package/dist/Context.d.ts +0 -25
- package/dist/Context.d.ts.map +0 -1
- package/dist/Editor.d.ts +0 -14
- package/dist/Editor.d.ts.map +0 -1
- package/dist/EditorContent.d.ts +0 -21
- package/dist/EditorContent.d.ts.map +0 -1
- package/dist/FloatingMenu.d.ts +0 -11
- package/dist/FloatingMenu.d.ts.map +0 -1
- package/dist/NodeViewContent.d.ts +0 -7
- package/dist/NodeViewContent.d.ts.map +0 -1
- package/dist/NodeViewWrapper.d.ts +0 -7
- package/dist/NodeViewWrapper.d.ts.map +0 -1
- package/dist/ReactNodeViewRenderer.d.ts +0 -96
- package/dist/ReactNodeViewRenderer.d.ts.map +0 -1
- package/dist/ReactRenderer.d.ts +0 -71
- package/dist/ReactRenderer.d.ts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.umd.js +0 -1540
- package/dist/index.umd.js.map +0 -1
- package/dist/useEditor.d.ts +0 -39
- package/dist/useEditor.d.ts.map +0 -1
- package/dist/useEditorState.d.ts +0 -45
- package/dist/useEditorState.d.ts.map +0 -1
- package/dist/useReactNodeView.d.ts +0 -7
- package/dist/useReactNodeView.d.ts.map +0 -1
- package/src/BubbleMenu.tsx +0 -57
- package/src/FloatingMenu.tsx +0 -64
|
@@ -1,19 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
} from '@tiptap/core'
|
|
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
|
-
|
|
14
|
-
import { EditorWithContentComponent } from './Editor.js'
|
|
1
|
+
import type { DecorationWithType, Editor, NodeViewProps, NodeViewRenderer, NodeViewRendererOptions } from '@tiptap/core'
|
|
2
|
+
import { getRenderedAttributes, NodeView } from '@tiptap/core'
|
|
3
|
+
import type { Node, Node as ProseMirrorNode } from '@tiptap/pm/model'
|
|
4
|
+
import type { Decoration, DecorationSource, NodeView as ProseMirrorNodeView } from '@tiptap/pm/view'
|
|
5
|
+
import type { ComponentType } from 'react'
|
|
6
|
+
import React from 'react'
|
|
7
|
+
|
|
8
|
+
import type { EditorWithContentComponent } from './Editor.js'
|
|
15
9
|
import { ReactRenderer } from './ReactRenderer.js'
|
|
16
|
-
import {
|
|
10
|
+
import type { ReactNodeViewContextProps } from './useReactNodeView.js'
|
|
11
|
+
import { ReactNodeViewContext } from './useReactNodeView.js'
|
|
17
12
|
|
|
18
13
|
export interface ReactNodeViewRendererOptions extends NodeViewRendererOptions {
|
|
19
14
|
/**
|
|
@@ -22,23 +17,23 @@ export interface ReactNodeViewRendererOptions extends NodeViewRendererOptions {
|
|
|
22
17
|
*/
|
|
23
18
|
update:
|
|
24
19
|
| ((props: {
|
|
25
|
-
oldNode: ProseMirrorNode
|
|
26
|
-
oldDecorations: readonly Decoration[]
|
|
27
|
-
oldInnerDecorations: DecorationSource
|
|
28
|
-
newNode: ProseMirrorNode
|
|
29
|
-
newDecorations: readonly Decoration[]
|
|
30
|
-
innerDecorations: DecorationSource
|
|
31
|
-
updateProps: () => void
|
|
20
|
+
oldNode: ProseMirrorNode
|
|
21
|
+
oldDecorations: readonly Decoration[]
|
|
22
|
+
oldInnerDecorations: DecorationSource
|
|
23
|
+
newNode: ProseMirrorNode
|
|
24
|
+
newDecorations: readonly Decoration[]
|
|
25
|
+
innerDecorations: DecorationSource
|
|
26
|
+
updateProps: () => void
|
|
32
27
|
}) => boolean)
|
|
33
|
-
| null
|
|
28
|
+
| null
|
|
34
29
|
/**
|
|
35
30
|
* The tag name of the element wrapping the React component.
|
|
36
31
|
*/
|
|
37
|
-
as?: string
|
|
32
|
+
as?: string
|
|
38
33
|
/**
|
|
39
34
|
* The class name of the element wrapping the React component.
|
|
40
35
|
*/
|
|
41
|
-
className?: string
|
|
36
|
+
className?: string
|
|
42
37
|
/**
|
|
43
38
|
* Attributes that should be applied to the element wrapping the React component.
|
|
44
39
|
* If this is a function, it will be called each time the node view is updated.
|
|
@@ -46,10 +41,7 @@ export interface ReactNodeViewRendererOptions extends NodeViewRendererOptions {
|
|
|
46
41
|
*/
|
|
47
42
|
attrs?:
|
|
48
43
|
| Record<string, string>
|
|
49
|
-
| ((props: {
|
|
50
|
-
node: ProseMirrorNode;
|
|
51
|
-
HTMLAttributes: Record<string, any>;
|
|
52
|
-
}) => Record<string, string>);
|
|
44
|
+
| ((props: { node: ProseMirrorNode; HTMLAttributes: Record<string, any> }) => Record<string, string>)
|
|
53
45
|
}
|
|
54
46
|
|
|
55
47
|
export class ReactNodeView<
|
|
@@ -104,15 +96,13 @@ export class ReactNodeView<
|
|
|
104
96
|
const Component = this.component
|
|
105
97
|
// For performance reasons, we memoize the provider component
|
|
106
98
|
// 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
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
},
|
|
115
|
-
)
|
|
99
|
+
const ReactNodeViewProvider: React.FunctionComponent<NodeViewProps> = React.memo(componentProps => {
|
|
100
|
+
return (
|
|
101
|
+
<ReactNodeViewContext.Provider value={context}>
|
|
102
|
+
{React.createElement(Component, componentProps)}
|
|
103
|
+
</ReactNodeViewContext.Provider>
|
|
104
|
+
)
|
|
105
|
+
})
|
|
116
106
|
|
|
117
107
|
ReactNodeViewProvider.displayName = 'ReactNodeView'
|
|
118
108
|
|
|
@@ -159,8 +149,8 @@ export class ReactNodeView<
|
|
|
159
149
|
*/
|
|
160
150
|
get dom() {
|
|
161
151
|
if (
|
|
162
|
-
this.renderer.element.firstElementChild
|
|
163
|
-
|
|
152
|
+
this.renderer.element.firstElementChild &&
|
|
153
|
+
!this.renderer.element.firstElementChild?.hasAttribute('data-node-view-wrapper')
|
|
164
154
|
) {
|
|
165
155
|
throw Error('Please use the NodeViewWrapper component for your node view.')
|
|
166
156
|
}
|
|
@@ -211,11 +201,7 @@ export class ReactNodeView<
|
|
|
211
201
|
* On update, update the React component.
|
|
212
202
|
* To prevent unnecessary updates, the `update` option can be used.
|
|
213
203
|
*/
|
|
214
|
-
update(
|
|
215
|
-
node: Node,
|
|
216
|
-
decorations: readonly Decoration[],
|
|
217
|
-
innerDecorations: DecorationSource,
|
|
218
|
-
): boolean {
|
|
204
|
+
update(node: Node, decorations: readonly Decoration[], innerDecorations: DecorationSource): boolean {
|
|
219
205
|
const rerenderComponent = (props?: Record<string, any>) => {
|
|
220
206
|
this.renderer.updateProps(props)
|
|
221
207
|
if (typeof this.options.attrs === 'function') {
|
|
@@ -247,11 +233,7 @@ export class ReactNodeView<
|
|
|
247
233
|
})
|
|
248
234
|
}
|
|
249
235
|
|
|
250
|
-
if (
|
|
251
|
-
node === this.node
|
|
252
|
-
&& this.decorations === decorations
|
|
253
|
-
&& this.innerDecorations === innerDecorations
|
|
254
|
-
) {
|
|
236
|
+
if (node === this.node && this.decorations === decorations && this.innerDecorations === innerDecorations) {
|
|
255
237
|
return true
|
|
256
238
|
}
|
|
257
239
|
|
package/src/ReactRenderer.tsx
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { Editor } from '@tiptap/core'
|
|
1
|
+
import type { Editor } from '@tiptap/core'
|
|
2
2
|
import React from 'react'
|
|
3
3
|
import { flushSync } from 'react-dom'
|
|
4
4
|
|
|
5
|
-
import { EditorWithContentComponent } from './Editor.js'
|
|
5
|
+
import type { EditorWithContentComponent } from './Editor.js'
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Check if a component is a class component.
|
|
@@ -10,11 +10,7 @@ import { EditorWithContentComponent } from './Editor.js'
|
|
|
10
10
|
* @returns {boolean}
|
|
11
11
|
*/
|
|
12
12
|
function isClassComponent(Component: any) {
|
|
13
|
-
return !!(
|
|
14
|
-
typeof Component === 'function'
|
|
15
|
-
&& Component.prototype
|
|
16
|
-
&& Component.prototype.isReactComponent
|
|
17
|
-
)
|
|
13
|
+
return !!(typeof Component === 'function' && Component.prototype && Component.prototype.isReactComponent)
|
|
18
14
|
}
|
|
19
15
|
|
|
20
16
|
/**
|
|
@@ -23,10 +19,7 @@ function isClassComponent(Component: any) {
|
|
|
23
19
|
* @returns {boolean}
|
|
24
20
|
*/
|
|
25
21
|
function isForwardRefComponent(Component: any) {
|
|
26
|
-
return !!(
|
|
27
|
-
typeof Component === 'object'
|
|
28
|
-
&& Component.$$typeof?.toString() === 'Symbol(react.forward_ref)'
|
|
29
|
-
)
|
|
22
|
+
return !!(typeof Component === 'object' && Component.$$typeof?.toString() === 'Symbol(react.forward_ref)')
|
|
30
23
|
}
|
|
31
24
|
|
|
32
25
|
export interface ReactRendererOptions {
|
|
@@ -34,21 +27,21 @@ export interface ReactRendererOptions {
|
|
|
34
27
|
* The editor instance.
|
|
35
28
|
* @type {Editor}
|
|
36
29
|
*/
|
|
37
|
-
editor: Editor
|
|
30
|
+
editor: Editor
|
|
38
31
|
|
|
39
32
|
/**
|
|
40
33
|
* The props for the component.
|
|
41
34
|
* @type {Record<string, any>}
|
|
42
35
|
* @default {}
|
|
43
36
|
*/
|
|
44
|
-
props?: Record<string, any
|
|
37
|
+
props?: Record<string, any>
|
|
45
38
|
|
|
46
39
|
/**
|
|
47
40
|
* The tag name of the element.
|
|
48
41
|
* @type {string}
|
|
49
42
|
* @default 'div'
|
|
50
43
|
*/
|
|
51
|
-
as?: string
|
|
44
|
+
as?: string
|
|
52
45
|
|
|
53
46
|
/**
|
|
54
47
|
* The class name of the element.
|
|
@@ -56,13 +49,13 @@ export interface ReactRendererOptions {
|
|
|
56
49
|
* @default ''
|
|
57
50
|
* @example 'foo bar'
|
|
58
51
|
*/
|
|
59
|
-
className?: string
|
|
52
|
+
className?: string
|
|
60
53
|
}
|
|
61
54
|
|
|
62
55
|
type ComponentType<R, P> =
|
|
63
|
-
React.ComponentClass<P>
|
|
64
|
-
React.FunctionComponent<P>
|
|
65
|
-
React.ForwardRefExoticComponent<React.PropsWithoutRef<P> & React.RefAttributes<R
|
|
56
|
+
| React.ComponentClass<P>
|
|
57
|
+
| React.FunctionComponent<P>
|
|
58
|
+
| React.ForwardRefExoticComponent<React.PropsWithoutRef<P> & React.RefAttributes<R>>
|
|
66
59
|
|
|
67
60
|
/**
|
|
68
61
|
* The ReactRenderer class. It's responsible for rendering React components inside the editor.
|
|
@@ -74,7 +67,7 @@ type ComponentType<R, P> =
|
|
|
74
67
|
* },
|
|
75
68
|
* as: 'span',
|
|
76
69
|
* })
|
|
77
|
-
*/
|
|
70
|
+
*/
|
|
78
71
|
export class ReactRenderer<R = unknown, P extends Record<string, any> = object> {
|
|
79
72
|
id: string
|
|
80
73
|
|
|
@@ -93,13 +86,11 @@ export class ReactRenderer<R = unknown, P extends Record<string, any> = object>
|
|
|
93
86
|
/**
|
|
94
87
|
* Immediately creates element and renders the provided React component.
|
|
95
88
|
*/
|
|
96
|
-
constructor(
|
|
97
|
-
|
|
98
|
-
props = {},
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
}: ReactRendererOptions) {
|
|
102
|
-
this.id = Math.floor(Math.random() * 0xFFFFFFFF).toString()
|
|
89
|
+
constructor(
|
|
90
|
+
component: ComponentType<R, P>,
|
|
91
|
+
{ editor, props = {}, as = 'div', className = '' }: ReactRendererOptions,
|
|
92
|
+
) {
|
|
93
|
+
this.id = Math.floor(Math.random() * 0xffffffff).toString()
|
|
103
94
|
this.component = component
|
|
104
95
|
this.editor = editor as EditorWithContentComponent
|
|
105
96
|
this.props = props as P
|
package/src/index.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
export * from './BubbleMenu.js'
|
|
2
1
|
export * from './Context.js'
|
|
3
2
|
export * from './EditorContent.js'
|
|
4
|
-
export * from './FloatingMenu.js'
|
|
5
3
|
export * from './NodeViewContent.js'
|
|
6
4
|
export * from './NodeViewWrapper.js'
|
|
5
|
+
export * from './ReactMarkViewRenderer.js'
|
|
7
6
|
export * from './ReactNodeViewRenderer.js'
|
|
8
7
|
export * from './ReactRenderer.js'
|
|
9
8
|
export * from './useEditor.js'
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { type BubbleMenuPluginProps, BubbleMenuPlugin } from '@tiptap/extension-bubble-menu'
|
|
2
|
+
import { useCurrentEditor } from '@tiptap/react'
|
|
3
|
+
import React, { useEffect, useRef } from 'react'
|
|
4
|
+
import { createPortal } from 'react-dom'
|
|
5
|
+
|
|
6
|
+
type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>
|
|
7
|
+
|
|
8
|
+
export type BubbleMenuProps = Optional<Omit<Optional<BubbleMenuPluginProps, 'pluginKey'>, 'element'>, 'editor'> &
|
|
9
|
+
React.HTMLAttributes<HTMLDivElement>
|
|
10
|
+
|
|
11
|
+
export const BubbleMenu = React.forwardRef<HTMLDivElement, BubbleMenuProps>(
|
|
12
|
+
(
|
|
13
|
+
{ pluginKey = 'bubbleMenu', editor, updateDelay, resizeDelay, shouldShow = null, options, children, ...restProps },
|
|
14
|
+
ref,
|
|
15
|
+
) => {
|
|
16
|
+
const menuEl = useRef(document.createElement('div'))
|
|
17
|
+
|
|
18
|
+
if (typeof ref === 'function') {
|
|
19
|
+
ref(menuEl.current)
|
|
20
|
+
} else if (ref) {
|
|
21
|
+
ref.current = menuEl.current
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const { editor: currentEditor } = useCurrentEditor()
|
|
25
|
+
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
const bubbleMenuElement = menuEl.current
|
|
28
|
+
|
|
29
|
+
bubbleMenuElement.style.visibility = 'hidden'
|
|
30
|
+
bubbleMenuElement.style.position = 'absolute'
|
|
31
|
+
|
|
32
|
+
if (editor?.isDestroyed || (currentEditor as any)?.isDestroyed) {
|
|
33
|
+
return
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const attachToEditor = editor || currentEditor
|
|
37
|
+
|
|
38
|
+
if (!attachToEditor) {
|
|
39
|
+
console.warn('BubbleMenu component is not rendered inside of an editor component or does not have editor prop.')
|
|
40
|
+
return
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const plugin = BubbleMenuPlugin({
|
|
44
|
+
updateDelay,
|
|
45
|
+
resizeDelay,
|
|
46
|
+
editor: attachToEditor,
|
|
47
|
+
element: bubbleMenuElement,
|
|
48
|
+
pluginKey,
|
|
49
|
+
shouldShow,
|
|
50
|
+
options,
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
attachToEditor.registerPlugin(plugin)
|
|
54
|
+
|
|
55
|
+
return () => {
|
|
56
|
+
attachToEditor.unregisterPlugin(pluginKey)
|
|
57
|
+
window.requestAnimationFrame(() => {
|
|
58
|
+
if (bubbleMenuElement.parentNode) {
|
|
59
|
+
bubbleMenuElement.parentNode.removeChild(bubbleMenuElement)
|
|
60
|
+
}
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
64
|
+
}, [editor, currentEditor])
|
|
65
|
+
|
|
66
|
+
return createPortal(<div {...restProps}>{children}</div>, menuEl.current)
|
|
67
|
+
},
|
|
68
|
+
)
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { FloatingMenuPluginProps } from '@tiptap/extension-floating-menu'
|
|
2
|
+
import { FloatingMenuPlugin } from '@tiptap/extension-floating-menu'
|
|
3
|
+
import { useCurrentEditor } from '@tiptap/react'
|
|
4
|
+
import React, { useEffect, useRef } from 'react'
|
|
5
|
+
import { createPortal } from 'react-dom'
|
|
6
|
+
|
|
7
|
+
type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>
|
|
8
|
+
|
|
9
|
+
export type FloatingMenuProps = Omit<Optional<FloatingMenuPluginProps, 'pluginKey'>, 'element' | 'editor'> & {
|
|
10
|
+
editor: FloatingMenuPluginProps['editor'] | null
|
|
11
|
+
options?: FloatingMenuPluginProps['options']
|
|
12
|
+
} & React.HTMLAttributes<HTMLDivElement>
|
|
13
|
+
|
|
14
|
+
export const FloatingMenu = React.forwardRef<HTMLDivElement, FloatingMenuProps>(
|
|
15
|
+
({ pluginKey = 'floatingMenu', editor, shouldShow = null, options, children, ...restProps }, ref) => {
|
|
16
|
+
const menuEl = useRef(document.createElement('div'))
|
|
17
|
+
|
|
18
|
+
if (typeof ref === 'function') {
|
|
19
|
+
ref(menuEl.current)
|
|
20
|
+
} else if (ref) {
|
|
21
|
+
ref.current = menuEl.current
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const { editor: currentEditor } = useCurrentEditor()
|
|
25
|
+
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
const floatingMenuElement = menuEl.current
|
|
28
|
+
|
|
29
|
+
floatingMenuElement.style.visibility = 'hidden'
|
|
30
|
+
floatingMenuElement.style.position = 'absolute'
|
|
31
|
+
|
|
32
|
+
if (editor?.isDestroyed || (currentEditor as any)?.isDestroyed) {
|
|
33
|
+
return
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const attachToEditor = editor || currentEditor
|
|
37
|
+
|
|
38
|
+
if (!attachToEditor) {
|
|
39
|
+
console.warn(
|
|
40
|
+
'FloatingMenu component is not rendered inside of an editor component or does not have editor prop.',
|
|
41
|
+
)
|
|
42
|
+
return
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const plugin = FloatingMenuPlugin({
|
|
46
|
+
editor: attachToEditor,
|
|
47
|
+
element: floatingMenuElement,
|
|
48
|
+
pluginKey,
|
|
49
|
+
shouldShow,
|
|
50
|
+
options,
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
attachToEditor.registerPlugin(plugin)
|
|
54
|
+
|
|
55
|
+
return () => {
|
|
56
|
+
attachToEditor.unregisterPlugin(pluginKey)
|
|
57
|
+
window.requestAnimationFrame(() => {
|
|
58
|
+
if (floatingMenuElement.parentNode) {
|
|
59
|
+
floatingMenuElement.parentNode.removeChild(floatingMenuElement)
|
|
60
|
+
}
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
64
|
+
}, [editor, currentEditor])
|
|
65
|
+
|
|
66
|
+
return createPortal(<div {...restProps}>{children}</div>, menuEl.current)
|
|
67
|
+
},
|
|
68
|
+
)
|
package/src/useEditor.ts
CHANGED
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
import { type EditorOptions, Editor } from '@tiptap/core'
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
MutableRefObject,
|
|
5
|
-
useDebugValue,
|
|
6
|
-
useEffect,
|
|
7
|
-
useRef,
|
|
8
|
-
useState,
|
|
9
|
-
} from 'react'
|
|
2
|
+
import type { DependencyList, MutableRefObject } from 'react'
|
|
3
|
+
import { useDebugValue, useEffect, useRef, useState } from 'react'
|
|
10
4
|
import { useSyncExternalStore } from 'use-sync-external-store/shim'
|
|
11
5
|
|
|
12
6
|
import { useEditorState } from './useEditorState.js'
|
|
13
7
|
|
|
8
|
+
// @ts-ignore
|
|
14
9
|
const isDev = process.env.NODE_ENV !== 'production'
|
|
15
10
|
const isSSR = typeof window === 'undefined'
|
|
16
11
|
const isNext = isSSR || Boolean(typeof window !== 'undefined' && (window as any).next)
|
|
@@ -25,14 +20,14 @@ export type UseEditorOptions = Partial<EditorOptions> & {
|
|
|
25
20
|
* If server-side rendering, set this to `false`.
|
|
26
21
|
* @default true
|
|
27
22
|
*/
|
|
28
|
-
immediatelyRender?: boolean
|
|
23
|
+
immediatelyRender?: boolean
|
|
29
24
|
/**
|
|
30
25
|
* Whether to re-render the editor on each transaction.
|
|
31
26
|
* This is legacy behavior that will be removed in future versions.
|
|
32
|
-
* @default
|
|
27
|
+
* @default false
|
|
33
28
|
*/
|
|
34
|
-
shouldRerenderOnTransaction?: boolean
|
|
35
|
-
}
|
|
29
|
+
shouldRerenderOnTransaction?: boolean
|
|
30
|
+
}
|
|
36
31
|
|
|
37
32
|
/**
|
|
38
33
|
* This class handles the creation, destruction, and re-creation of the editor instance.
|
|
@@ -100,13 +95,12 @@ class EditorInstanceManager {
|
|
|
100
95
|
private getInitialEditor() {
|
|
101
96
|
if (this.options.current.immediatelyRender === undefined) {
|
|
102
97
|
if (isSSR || isNext) {
|
|
103
|
-
// TODO in the next major release, we should throw an error here
|
|
104
98
|
if (isDev) {
|
|
105
99
|
/**
|
|
106
100
|
* Throw an error in development, to make sure the developer is aware that tiptap cannot be SSR'd
|
|
107
101
|
* and that they need to set `immediatelyRender` to `false` to avoid hydration mismatches.
|
|
108
102
|
*/
|
|
109
|
-
|
|
103
|
+
throw new Error(
|
|
110
104
|
'Tiptap Error: SSR has been detected, please set `immediatelyRender` explicitly to `false` to avoid hydration mismatches.',
|
|
111
105
|
)
|
|
112
106
|
}
|
|
@@ -151,6 +145,7 @@ class EditorInstanceManager {
|
|
|
151
145
|
onContentError: (...args) => this.options.current.onContentError?.(...args),
|
|
152
146
|
onDrop: (...args) => this.options.current.onDrop?.(...args),
|
|
153
147
|
onPaste: (...args) => this.options.current.onPaste?.(...args),
|
|
148
|
+
onDelete: (...args) => this.options.current.onDelete?.(...args),
|
|
154
149
|
}
|
|
155
150
|
const editor = new Editor(optionsToApply)
|
|
156
151
|
|
|
@@ -186,7 +181,21 @@ class EditorInstanceManager {
|
|
|
186
181
|
|
|
187
182
|
static compareOptions(a: UseEditorOptions, b: UseEditorOptions) {
|
|
188
183
|
return (Object.keys(a) as (keyof UseEditorOptions)[]).every(key => {
|
|
189
|
-
if (
|
|
184
|
+
if (
|
|
185
|
+
[
|
|
186
|
+
'onCreate',
|
|
187
|
+
'onBeforeCreate',
|
|
188
|
+
'onDestroy',
|
|
189
|
+
'onUpdate',
|
|
190
|
+
'onTransaction',
|
|
191
|
+
'onFocus',
|
|
192
|
+
'onBlur',
|
|
193
|
+
'onSelectionUpdate',
|
|
194
|
+
'onContentError',
|
|
195
|
+
'onDrop',
|
|
196
|
+
'onPaste',
|
|
197
|
+
].includes(key)
|
|
198
|
+
) {
|
|
190
199
|
// we don't want to compare callbacks, they are always different and only registered once
|
|
191
200
|
return true
|
|
192
201
|
}
|
|
@@ -260,8 +269,8 @@ class EditorInstanceManager {
|
|
|
260
269
|
this.previousDeps = deps
|
|
261
270
|
return
|
|
262
271
|
}
|
|
263
|
-
const depsAreEqual =
|
|
264
|
-
&& this.previousDeps.every((dep, index) => dep === deps[index])
|
|
272
|
+
const depsAreEqual =
|
|
273
|
+
this.previousDeps.length === deps.length && this.previousDeps.every((dep, index) => dep === deps[index])
|
|
265
274
|
|
|
266
275
|
if (depsAreEqual) {
|
|
267
276
|
// deps exist and are equal, no need to recreate
|
|
@@ -319,9 +328,9 @@ class EditorInstanceManager {
|
|
|
319
328
|
* @example const editor = useEditor({ extensions: [...] })
|
|
320
329
|
*/
|
|
321
330
|
export function useEditor(
|
|
322
|
-
options: UseEditorOptions & { immediatelyRender:
|
|
323
|
-
deps?: DependencyList
|
|
324
|
-
): Editor
|
|
331
|
+
options: UseEditorOptions & { immediatelyRender: false },
|
|
332
|
+
deps?: DependencyList,
|
|
333
|
+
): Editor | null
|
|
325
334
|
|
|
326
335
|
/**
|
|
327
336
|
* This hook allows you to create an editor instance.
|
|
@@ -330,12 +339,9 @@ export function useEditor(
|
|
|
330
339
|
* @returns The editor instance
|
|
331
340
|
* @example const editor = useEditor({ extensions: [...] })
|
|
332
341
|
*/
|
|
333
|
-
export function useEditor(options
|
|
342
|
+
export function useEditor(options: UseEditorOptions, deps?: DependencyList): Editor
|
|
334
343
|
|
|
335
|
-
export function useEditor(
|
|
336
|
-
options: UseEditorOptions = {},
|
|
337
|
-
deps: DependencyList = [],
|
|
338
|
-
): Editor | null {
|
|
344
|
+
export function useEditor(options: UseEditorOptions = {}, deps: DependencyList = []): Editor | null {
|
|
339
345
|
const mostRecentOptions = useRef(options)
|
|
340
346
|
|
|
341
347
|
mostRecentOptions.current = options
|
|
@@ -359,7 +365,7 @@ export function useEditor(
|
|
|
359
365
|
useEditorState({
|
|
360
366
|
editor,
|
|
361
367
|
selector: ({ transactionNumber }) => {
|
|
362
|
-
if (options.shouldRerenderOnTransaction === false) {
|
|
368
|
+
if (options.shouldRerenderOnTransaction === false || options.shouldRerenderOnTransaction === undefined) {
|
|
363
369
|
// This will prevent the editor from re-rendering on each transaction
|
|
364
370
|
return null
|
|
365
371
|
}
|
package/src/useEditorState.ts
CHANGED
|
@@ -1,35 +1,30 @@
|
|
|
1
1
|
import type { Editor } from '@tiptap/core'
|
|
2
2
|
import deepEqual from 'fast-deep-equal/es6/react'
|
|
3
|
-
import {
|
|
4
|
-
useDebugValue, useEffect, useLayoutEffect, useState,
|
|
5
|
-
} from 'react'
|
|
3
|
+
import { useDebugValue, useEffect, useLayoutEffect, useState } from 'react'
|
|
6
4
|
import { useSyncExternalStoreWithSelector } from 'use-sync-external-store/shim/with-selector'
|
|
7
5
|
|
|
8
6
|
const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect
|
|
9
7
|
|
|
10
8
|
export type EditorStateSnapshot<TEditor extends Editor | null = Editor | null> = {
|
|
11
|
-
editor: TEditor
|
|
12
|
-
transactionNumber: number
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export type UseEditorStateOptions<
|
|
16
|
-
TSelectorResult,
|
|
17
|
-
TEditor extends Editor | null = Editor | null,
|
|
18
|
-
> = {
|
|
9
|
+
editor: TEditor
|
|
10
|
+
transactionNumber: number
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export type UseEditorStateOptions<TSelectorResult, TEditor extends Editor | null = Editor | null> = {
|
|
19
14
|
/**
|
|
20
15
|
* The editor instance.
|
|
21
16
|
*/
|
|
22
|
-
editor: TEditor
|
|
17
|
+
editor: TEditor
|
|
23
18
|
/**
|
|
24
19
|
* A selector function to determine the value to compare for re-rendering.
|
|
25
20
|
*/
|
|
26
|
-
selector: (context: EditorStateSnapshot<TEditor>) => TSelectorResult
|
|
21
|
+
selector: (context: EditorStateSnapshot<TEditor>) => TSelectorResult
|
|
27
22
|
/**
|
|
28
23
|
* A custom equality function to determine if the editor should re-render.
|
|
29
24
|
* @default `deepEqual` from `fast-deep-equal`
|
|
30
25
|
*/
|
|
31
|
-
equalityFn?: (a: TSelectorResult, b: TSelectorResult | null) => boolean
|
|
32
|
-
}
|
|
26
|
+
equalityFn?: (a: TSelectorResult, b: TSelectorResult | null) => boolean
|
|
27
|
+
}
|
|
33
28
|
|
|
34
29
|
/**
|
|
35
30
|
* To synchronize the editor instance with the component state,
|
|
@@ -126,8 +121,8 @@ class EditorStateManager<TEditor extends Editor | null = Editor | null> {
|
|
|
126
121
|
* })
|
|
127
122
|
*/
|
|
128
123
|
export function useEditorState<TSelectorResult>(
|
|
129
|
-
options: UseEditorStateOptions<TSelectorResult, Editor
|
|
130
|
-
): TSelectorResult
|
|
124
|
+
options: UseEditorStateOptions<TSelectorResult, Editor>,
|
|
125
|
+
): TSelectorResult
|
|
131
126
|
/**
|
|
132
127
|
* This hook allows you to watch for changes on the editor instance.
|
|
133
128
|
* It will allow you to select a part of the editor state and re-render the component when it changes.
|
|
@@ -140,8 +135,8 @@ export function useEditorState<TSelectorResult>(
|
|
|
140
135
|
* })
|
|
141
136
|
*/
|
|
142
137
|
export function useEditorState<TSelectorResult>(
|
|
143
|
-
options: UseEditorStateOptions<TSelectorResult, Editor | null
|
|
144
|
-
): TSelectorResult | null
|
|
138
|
+
options: UseEditorStateOptions<TSelectorResult, Editor | null>,
|
|
139
|
+
): TSelectorResult | null
|
|
145
140
|
|
|
146
141
|
/**
|
|
147
142
|
* This hook allows you to watch for changes on the editor instance.
|
package/src/useReactNodeView.ts
CHANGED
|
@@ -1,12 +1,28 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { ReactNode } from 'react'
|
|
2
|
+
import { createContext, createElement, useContext } from 'react'
|
|
2
3
|
|
|
3
4
|
export interface ReactNodeViewContextProps {
|
|
4
|
-
onDragStart
|
|
5
|
-
nodeViewContentRef
|
|
5
|
+
onDragStart?: (event: DragEvent) => void
|
|
6
|
+
nodeViewContentRef?: (element: HTMLElement | null) => void
|
|
7
|
+
/**
|
|
8
|
+
* This allows you to add children into the NodeViewContent component.
|
|
9
|
+
* This is useful when statically rendering the content of a node view.
|
|
10
|
+
*/
|
|
11
|
+
nodeViewContentChildren?: ReactNode
|
|
6
12
|
}
|
|
7
13
|
|
|
8
|
-
export const ReactNodeViewContext = createContext<
|
|
9
|
-
onDragStart:
|
|
14
|
+
export const ReactNodeViewContext = createContext<ReactNodeViewContextProps>({
|
|
15
|
+
onDragStart: () => {
|
|
16
|
+
// no-op
|
|
17
|
+
},
|
|
18
|
+
nodeViewContentChildren: undefined,
|
|
19
|
+
nodeViewContentRef: () => {
|
|
20
|
+
// no-op
|
|
21
|
+
},
|
|
10
22
|
})
|
|
11
23
|
|
|
24
|
+
export const ReactNodeViewContentProvider = ({ children, content }: { children: ReactNode; content: ReactNode }) => {
|
|
25
|
+
return createElement(ReactNodeViewContext.Provider, { value: { nodeViewContentChildren: content } }, children)
|
|
26
|
+
}
|
|
27
|
+
|
|
12
28
|
export const useReactNodeView = () => useContext(ReactNodeViewContext)
|
package/dist/BubbleMenu.d.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { BubbleMenuPluginProps } from '@tiptap/extension-bubble-menu';
|
|
2
|
-
import React from 'react';
|
|
3
|
-
type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>;
|
|
4
|
-
export type BubbleMenuProps = Omit<Optional<BubbleMenuPluginProps, 'pluginKey'>, 'element' | 'editor'> & {
|
|
5
|
-
editor: BubbleMenuPluginProps['editor'] | null;
|
|
6
|
-
className?: string;
|
|
7
|
-
children: React.ReactNode;
|
|
8
|
-
updateDelay?: number;
|
|
9
|
-
};
|
|
10
|
-
export declare const BubbleMenu: (props: BubbleMenuProps) => React.JSX.Element;
|
|
11
|
-
export {};
|
|
12
|
-
//# sourceMappingURL=BubbleMenu.d.ts.map
|
package/dist/BubbleMenu.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"BubbleMenu.d.ts","sourceRoot":"","sources":["../src/BubbleMenu.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAoB,qBAAqB,EAAE,MAAM,+BAA+B,CAAA;AACvF,OAAO,KAA8B,MAAM,OAAO,CAAA;AAIlD,KAAK,QAAQ,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAEvE,MAAM,MAAM,eAAe,GAAG,IAAI,CAAC,QAAQ,CAAC,qBAAqB,EAAE,WAAW,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAC,GAAG;IACvG,MAAM,EAAE,qBAAqB,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC;IAC/C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,eAAO,MAAM,UAAU,UAAW,eAAe,sBA0ChD,CAAA"}
|
package/dist/Context.d.ts
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { Editor } from '@tiptap/core';
|
|
2
|
-
import React, { HTMLAttributes, ReactNode } from 'react';
|
|
3
|
-
import { UseEditorOptions } from './useEditor.js';
|
|
4
|
-
export type EditorContextValue = {
|
|
5
|
-
editor: Editor | null;
|
|
6
|
-
};
|
|
7
|
-
export declare const EditorContext: React.Context<EditorContextValue>;
|
|
8
|
-
export declare const EditorConsumer: React.Consumer<EditorContextValue>;
|
|
9
|
-
/**
|
|
10
|
-
* A hook to get the current editor instance.
|
|
11
|
-
*/
|
|
12
|
-
export declare const useCurrentEditor: () => EditorContextValue;
|
|
13
|
-
export type EditorProviderProps = {
|
|
14
|
-
children?: ReactNode;
|
|
15
|
-
slotBefore?: ReactNode;
|
|
16
|
-
slotAfter?: ReactNode;
|
|
17
|
-
editorContainerProps?: HTMLAttributes<HTMLDivElement>;
|
|
18
|
-
} & UseEditorOptions;
|
|
19
|
-
/**
|
|
20
|
-
* This is the provider component for the editor.
|
|
21
|
-
* It allows the editor to be accessible across the entire component tree
|
|
22
|
-
* with `useCurrentEditor`.
|
|
23
|
-
*/
|
|
24
|
-
export declare function EditorProvider({ children, slotAfter, slotBefore, editorContainerProps, ...editorOptions }: EditorProviderProps): React.JSX.Element | null;
|
|
25
|
-
//# sourceMappingURL=Context.d.ts.map
|
package/dist/Context.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Context.d.ts","sourceRoot":"","sources":["../src/Context.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AACrC,OAAO,KAAK,EAAE,EACG,cAAc,EAAE,SAAS,EACzC,MAAM,OAAO,CAAA;AAGd,OAAO,EAAa,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AAE5D,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB,CAAA;AAED,eAAO,MAAM,aAAa,mCAExB,CAAA;AAEF,eAAO,MAAM,cAAc,oCAAyB,CAAA;AAEpD;;GAEG;AACH,eAAO,MAAM,gBAAgB,0BAAkC,CAAA;AAE/D,MAAM,MAAM,mBAAmB,GAAG;IAChC,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,UAAU,CAAC,EAAE,SAAS,CAAC;IACvB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,oBAAoB,CAAC,EAAE,cAAc,CAAC,cAAc,CAAC,CAAC;CACvD,GAAG,gBAAgB,CAAA;AAEpB;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,EAC7B,QAAQ,EAAE,SAAS,EAAE,UAAU,EAAE,oBAAyB,EAAE,GAAG,aAAa,EAC7E,EAAE,mBAAmB,4BAmBrB"}
|