@tiptap/react 3.0.0-next.3 → 3.0.0-next.4
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 +72 -102
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +24 -26
- package/dist/index.d.ts +24 -26
- package/dist/index.js +72 -119
- package/dist/index.js.map +1 -1
- package/package.json +18 -14
- package/src/BubbleMenu.tsx +7 -32
- package/src/Context.tsx +9 -13
- package/src/Editor.ts +5 -5
- package/src/EditorContent.tsx +8 -18
- package/src/FloatingMenu.tsx +49 -64
- package/src/NodeViewContent.tsx +12 -9
- package/src/NodeViewWrapper.tsx +2 -2
- package/src/ReactNodeViewRenderer.tsx +22 -35
- package/src/ReactRenderer.tsx +16 -25
- package/src/useEditor.ts +11 -20
- package/src/useEditorState.ts +14 -19
- package/src/useReactNodeView.ts +20 -5
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tiptap/react",
|
|
3
3
|
"description": "React components for tiptap",
|
|
4
|
-
"version": "3.0.0-next.
|
|
4
|
+
"version": "3.0.0-next.4",
|
|
5
5
|
"homepage": "https://tiptap.dev",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"tiptap",
|
|
@@ -14,7 +14,10 @@
|
|
|
14
14
|
},
|
|
15
15
|
"exports": {
|
|
16
16
|
".": {
|
|
17
|
-
"types":
|
|
17
|
+
"types": {
|
|
18
|
+
"import": "./dist/index.d.ts",
|
|
19
|
+
"require": "./dist/index.d.cts"
|
|
20
|
+
},
|
|
18
21
|
"import": "./dist/index.js",
|
|
19
22
|
"require": "./dist/index.cjs"
|
|
20
23
|
}
|
|
@@ -28,19 +31,19 @@
|
|
|
28
31
|
"dist"
|
|
29
32
|
],
|
|
30
33
|
"dependencies": {
|
|
31
|
-
"@tiptap/extension-bubble-menu": "^3.0.0-next.
|
|
32
|
-
"@tiptap/extension-floating-menu": "^3.0.0-next.
|
|
34
|
+
"@tiptap/extension-bubble-menu": "^3.0.0-next.4",
|
|
35
|
+
"@tiptap/extension-floating-menu": "^3.0.0-next.4",
|
|
33
36
|
"@types/use-sync-external-store": "^0.0.6",
|
|
34
|
-
"fast-deep-equal": "^3",
|
|
35
|
-
"use-sync-external-store": "^1"
|
|
37
|
+
"fast-deep-equal": "^3.1.3",
|
|
38
|
+
"use-sync-external-store": "^1.4.0"
|
|
36
39
|
},
|
|
37
40
|
"devDependencies": {
|
|
38
|
-
"@tiptap/core": "^3.0.0-next.
|
|
39
|
-
"@tiptap/pm": "^3.0.0-next.
|
|
40
|
-
"@types/react": "^18.
|
|
41
|
-
"@types/react-dom": "^18.
|
|
42
|
-
"react": "^18.
|
|
43
|
-
"react-dom": "^18.
|
|
41
|
+
"@tiptap/core": "^3.0.0-next.4",
|
|
42
|
+
"@tiptap/pm": "^3.0.0-next.4",
|
|
43
|
+
"@types/react": "^18.3.18",
|
|
44
|
+
"@types/react-dom": "^18.3.5",
|
|
45
|
+
"react": "^18.3.1",
|
|
46
|
+
"react-dom": "^18.3.1"
|
|
44
47
|
},
|
|
45
48
|
"peerDependencies": {
|
|
46
49
|
"@tiptap/core": "^3.0.0-next.1",
|
|
@@ -55,6 +58,7 @@
|
|
|
55
58
|
},
|
|
56
59
|
"sideEffects": false,
|
|
57
60
|
"scripts": {
|
|
58
|
-
"build": "tsup"
|
|
61
|
+
"build": "tsup",
|
|
62
|
+
"lint": "prettier ./src/ --check && eslint --cache --quiet --no-error-on-unmatched-pattern ./src/"
|
|
59
63
|
}
|
|
60
|
-
}
|
|
64
|
+
}
|
package/src/BubbleMenu.tsx
CHANGED
|
@@ -1,33 +1,17 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { type BubbleMenuPluginProps, BubbleMenuPlugin } from '@tiptap/extension-bubble-menu'
|
|
2
2
|
import React, { useEffect, useRef } from 'react'
|
|
3
3
|
import { createPortal } from 'react-dom'
|
|
4
4
|
|
|
5
5
|
import { useCurrentEditor } from './Context.js'
|
|
6
6
|
|
|
7
|
-
type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K
|
|
7
|
+
type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>
|
|
8
8
|
|
|
9
|
-
export type BubbleMenuProps = Omit<
|
|
10
|
-
|
|
11
|
-
'element' | 'editor'
|
|
12
|
-
> & {
|
|
13
|
-
editor: BubbleMenuPluginProps['editor'] | null;
|
|
14
|
-
updateDelay?: number;
|
|
15
|
-
resizeDelay?: number;
|
|
16
|
-
options?: BubbleMenuPluginProps['options'];
|
|
17
|
-
} & React.HTMLAttributes<HTMLDivElement>;
|
|
9
|
+
export type BubbleMenuProps = Optional<Omit<Optional<BubbleMenuPluginProps, 'pluginKey'>, 'element'>, 'editor'> &
|
|
10
|
+
React.HTMLAttributes<HTMLDivElement>
|
|
18
11
|
|
|
19
12
|
export const BubbleMenu = React.forwardRef<HTMLDivElement, BubbleMenuProps>(
|
|
20
13
|
(
|
|
21
|
-
{
|
|
22
|
-
pluginKey = 'bubbleMenu',
|
|
23
|
-
editor,
|
|
24
|
-
updateDelay,
|
|
25
|
-
resizeDelay,
|
|
26
|
-
shouldShow = null,
|
|
27
|
-
options,
|
|
28
|
-
children,
|
|
29
|
-
...restProps
|
|
30
|
-
},
|
|
14
|
+
{ pluginKey = 'bubbleMenu', editor, updateDelay, resizeDelay, shouldShow = null, options, children, ...restProps },
|
|
31
15
|
ref,
|
|
32
16
|
) => {
|
|
33
17
|
const menuEl = useRef(document.createElement('div'))
|
|
@@ -53,9 +37,7 @@ export const BubbleMenu = React.forwardRef<HTMLDivElement, BubbleMenuProps>(
|
|
|
53
37
|
const attachToEditor = editor || currentEditor
|
|
54
38
|
|
|
55
39
|
if (!attachToEditor) {
|
|
56
|
-
console.warn(
|
|
57
|
-
'BubbleMenu component is not rendered inside of an editor component or does not have editor prop.',
|
|
58
|
-
)
|
|
40
|
+
console.warn('BubbleMenu component is not rendered inside of an editor component or does not have editor prop.')
|
|
59
41
|
return
|
|
60
42
|
}
|
|
61
43
|
|
|
@@ -82,13 +64,6 @@ export const BubbleMenu = React.forwardRef<HTMLDivElement, BubbleMenuProps>(
|
|
|
82
64
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
83
65
|
}, [editor, currentEditor])
|
|
84
66
|
|
|
85
|
-
return createPortal(
|
|
86
|
-
<div
|
|
87
|
-
{...restProps}
|
|
88
|
-
>
|
|
89
|
-
{children}
|
|
90
|
-
</div>,
|
|
91
|
-
menuEl.current,
|
|
92
|
-
)
|
|
67
|
+
return createPortal(<div {...restProps}>{children}</div>, menuEl.current)
|
|
93
68
|
},
|
|
94
69
|
)
|
package/src/Context.tsx
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import { Editor } from '@tiptap/core'
|
|
2
|
-
import React, {
|
|
3
|
-
createContext, HTMLAttributes, ReactNode, useContext, useMemo,
|
|
4
|
-
} from 'react'
|
|
2
|
+
import React, { createContext, HTMLAttributes, ReactNode, useContext, useMemo } from 'react'
|
|
5
3
|
|
|
6
4
|
import { EditorContent } from './EditorContent.js'
|
|
7
5
|
import { useEditor, UseEditorOptions } from './useEditor.js'
|
|
8
6
|
|
|
9
7
|
export type EditorContextValue = {
|
|
10
|
-
editor: Editor | null
|
|
11
|
-
}
|
|
8
|
+
editor: Editor | null
|
|
9
|
+
}
|
|
12
10
|
|
|
13
11
|
export const EditorContext = createContext<EditorContextValue>({
|
|
14
12
|
editor: null,
|
|
@@ -22,11 +20,11 @@ export const EditorConsumer = EditorContext.Consumer
|
|
|
22
20
|
export const useCurrentEditor = () => useContext(EditorContext)
|
|
23
21
|
|
|
24
22
|
export type EditorProviderProps = {
|
|
25
|
-
children?: ReactNode
|
|
26
|
-
slotBefore?: ReactNode
|
|
27
|
-
slotAfter?: ReactNode
|
|
28
|
-
editorContainerProps?: HTMLAttributes<HTMLDivElement
|
|
29
|
-
} & UseEditorOptions
|
|
23
|
+
children?: ReactNode
|
|
24
|
+
slotBefore?: ReactNode
|
|
25
|
+
slotAfter?: ReactNode
|
|
26
|
+
editorContainerProps?: HTMLAttributes<HTMLDivElement>
|
|
27
|
+
} & UseEditorOptions
|
|
30
28
|
|
|
31
29
|
/**
|
|
32
30
|
* This is the provider component for the editor.
|
|
@@ -51,9 +49,7 @@ export function EditorProvider({
|
|
|
51
49
|
<EditorContext.Provider value={contextValue}>
|
|
52
50
|
{slotBefore}
|
|
53
51
|
<EditorConsumer>
|
|
54
|
-
{({ editor: currentEditor }) =>
|
|
55
|
-
<EditorContent editor={currentEditor} {...editorContainerProps} />
|
|
56
|
-
)}
|
|
52
|
+
{({ editor: currentEditor }) => <EditorContent editor={currentEditor} {...editorContainerProps} />}
|
|
57
53
|
</EditorConsumer>
|
|
58
54
|
{children}
|
|
59
55
|
{slotAfter}
|
package/src/Editor.ts
CHANGED
|
@@ -5,9 +5,9 @@ import { ReactRenderer } from './ReactRenderer.js'
|
|
|
5
5
|
|
|
6
6
|
export type EditorWithContentComponent = Editor & { contentComponent?: ContentComponent | null }
|
|
7
7
|
export type ContentComponent = {
|
|
8
|
-
setRenderer(id: string, renderer: ReactRenderer): void
|
|
9
|
-
removeRenderer(id: string): void
|
|
10
|
-
subscribe: (callback: () => void) => () => void
|
|
11
|
-
getSnapshot: () => Record<string, ReactPortal
|
|
12
|
-
getServerSnapshot: () => Record<string, ReactPortal
|
|
8
|
+
setRenderer(id: string, renderer: ReactRenderer): void
|
|
9
|
+
removeRenderer(id: string): void
|
|
10
|
+
subscribe: (callback: () => void) => () => void
|
|
11
|
+
getSnapshot: () => Record<string, ReactPortal>
|
|
12
|
+
getServerSnapshot: () => Record<string, ReactPortal>
|
|
13
13
|
}
|
package/src/EditorContent.tsx
CHANGED
|
@@ -1,22 +1,18 @@
|
|
|
1
1
|
import { Editor } from '@tiptap/core'
|
|
2
|
-
import React, {
|
|
3
|
-
ForwardedRef, forwardRef, HTMLProps, LegacyRef, MutableRefObject,
|
|
4
|
-
} from 'react'
|
|
2
|
+
import React, { ForwardedRef, forwardRef, HTMLProps, LegacyRef, MutableRefObject } from 'react'
|
|
5
3
|
import ReactDOM from 'react-dom'
|
|
6
4
|
import { useSyncExternalStore } from 'use-sync-external-store/shim'
|
|
7
5
|
|
|
8
6
|
import { ContentComponent, EditorWithContentComponent } from './Editor.js'
|
|
9
7
|
import { ReactRenderer } from './ReactRenderer.js'
|
|
10
8
|
|
|
11
|
-
const mergeRefs = <T extends HTMLDivElement>(
|
|
12
|
-
...refs: Array<MutableRefObject<T> | LegacyRef<T> | undefined>
|
|
13
|
-
) => {
|
|
9
|
+
const mergeRefs = <T extends HTMLDivElement>(...refs: Array<MutableRefObject<T> | LegacyRef<T> | undefined>) => {
|
|
14
10
|
return (node: T) => {
|
|
15
11
|
refs.forEach(ref => {
|
|
16
12
|
if (typeof ref === 'function') {
|
|
17
13
|
ref(node)
|
|
18
14
|
} else if (ref) {
|
|
19
|
-
(ref as MutableRefObject<T | null>).current = node
|
|
15
|
+
;(ref as MutableRefObject<T | null>).current = node
|
|
20
16
|
}
|
|
21
17
|
})
|
|
22
18
|
}
|
|
@@ -25,9 +21,7 @@ const mergeRefs = <T extends HTMLDivElement>(
|
|
|
25
21
|
/**
|
|
26
22
|
* This component renders all of the editor's node views.
|
|
27
23
|
*/
|
|
28
|
-
const Portals: React.FC<{ contentComponent: ContentComponent }> = ({
|
|
29
|
-
contentComponent,
|
|
30
|
-
}) => {
|
|
24
|
+
const Portals: React.FC<{ contentComponent: ContentComponent }> = ({ contentComponent }) => {
|
|
31
25
|
// For performance reasons, we render the node view portals on state changes only
|
|
32
26
|
const renderers = useSyncExternalStore(
|
|
33
27
|
contentComponent.subscribe,
|
|
@@ -36,16 +30,12 @@ const Portals: React.FC<{ contentComponent: ContentComponent }> = ({
|
|
|
36
30
|
)
|
|
37
31
|
|
|
38
32
|
// This allows us to directly render the portals without any additional wrapper
|
|
39
|
-
return (
|
|
40
|
-
<>
|
|
41
|
-
{Object.values(renderers)}
|
|
42
|
-
</>
|
|
43
|
-
)
|
|
33
|
+
return <>{Object.values(renderers)}</>
|
|
44
34
|
}
|
|
45
35
|
|
|
46
36
|
export interface EditorContentProps extends HTMLProps<HTMLDivElement> {
|
|
47
|
-
editor: Editor | null
|
|
48
|
-
innerRef?: ForwardedRef<HTMLDivElement | null
|
|
37
|
+
editor: Editor | null
|
|
38
|
+
innerRef?: ForwardedRef<HTMLDivElement | null>
|
|
49
39
|
}
|
|
50
40
|
|
|
51
41
|
function getInstance(): ContentComponent {
|
|
@@ -216,7 +206,7 @@ const EditorContentWithKey = forwardRef<HTMLDivElement, EditorContentProps>(
|
|
|
216
206
|
(props: Omit<EditorContentProps, 'innerRef'>, ref) => {
|
|
217
207
|
const key = React.useMemo(() => {
|
|
218
208
|
return Math.floor(Math.random() * 0xffffffff).toString()
|
|
219
|
-
|
|
209
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
220
210
|
}, [props.editor])
|
|
221
211
|
|
|
222
212
|
// Can't use JSX here because it conflicts with the type definition of Vue's JSX, so use createElement
|
package/src/FloatingMenu.tsx
CHANGED
|
@@ -4,80 +4,65 @@ import { createPortal } from 'react-dom'
|
|
|
4
4
|
|
|
5
5
|
import { useCurrentEditor } from './Context.js'
|
|
6
6
|
|
|
7
|
-
type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K
|
|
7
|
+
type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>
|
|
8
8
|
|
|
9
|
-
export type FloatingMenuProps = Omit<
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
editor: FloatingMenuPluginProps['editor'] | null;
|
|
14
|
-
options?: FloatingMenuPluginProps['options'];
|
|
15
|
-
} & React.HTMLAttributes<HTMLDivElement>;
|
|
9
|
+
export type FloatingMenuProps = Omit<Optional<FloatingMenuPluginProps, 'pluginKey'>, 'element' | 'editor'> & {
|
|
10
|
+
editor: FloatingMenuPluginProps['editor'] | null
|
|
11
|
+
options?: FloatingMenuPluginProps['options']
|
|
12
|
+
} & React.HTMLAttributes<HTMLDivElement>
|
|
16
13
|
|
|
17
|
-
export const FloatingMenu = React.forwardRef<HTMLDivElement, FloatingMenuProps>(
|
|
18
|
-
pluginKey = 'floatingMenu',
|
|
19
|
-
|
|
20
|
-
shouldShow = null,
|
|
21
|
-
options,
|
|
22
|
-
children,
|
|
23
|
-
...restProps
|
|
24
|
-
}, ref) => {
|
|
25
|
-
const menuEl = useRef(document.createElement('div'))
|
|
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'))
|
|
26
17
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
18
|
+
if (typeof ref === 'function') {
|
|
19
|
+
ref(menuEl.current)
|
|
20
|
+
} else if (ref) {
|
|
21
|
+
ref.current = menuEl.current
|
|
22
|
+
}
|
|
32
23
|
|
|
33
|
-
|
|
24
|
+
const { editor: currentEditor } = useCurrentEditor()
|
|
34
25
|
|
|
35
|
-
|
|
36
|
-
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
const floatingMenuElement = menuEl.current
|
|
37
28
|
|
|
38
|
-
|
|
39
|
-
|
|
29
|
+
floatingMenuElement.style.visibility = 'hidden'
|
|
30
|
+
floatingMenuElement.style.position = 'absolute'
|
|
40
31
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
32
|
+
if (editor?.isDestroyed || currentEditor?.isDestroyed) {
|
|
33
|
+
return
|
|
34
|
+
}
|
|
44
35
|
|
|
45
|
-
|
|
36
|
+
const attachToEditor = editor || currentEditor
|
|
46
37
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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
|
+
}
|
|
53
44
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
45
|
+
const plugin = FloatingMenuPlugin({
|
|
46
|
+
editor: attachToEditor,
|
|
47
|
+
element: floatingMenuElement,
|
|
48
|
+
pluginKey,
|
|
49
|
+
shouldShow,
|
|
50
|
+
options,
|
|
51
|
+
})
|
|
61
52
|
|
|
62
|
-
|
|
53
|
+
attachToEditor.registerPlugin(plugin)
|
|
63
54
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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])
|
|
74
65
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
>
|
|
79
|
-
{children}
|
|
80
|
-
</div>,
|
|
81
|
-
menuEl.current,
|
|
82
|
-
)
|
|
83
|
-
})
|
|
66
|
+
return createPortal(<div {...restProps}>{children}</div>, menuEl.current)
|
|
67
|
+
},
|
|
68
|
+
)
|
package/src/NodeViewContent.tsx
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
import React from 'react'
|
|
1
|
+
import React, { ComponentProps } from 'react'
|
|
2
2
|
|
|
3
3
|
import { useReactNodeView } from './useReactNodeView.js'
|
|
4
4
|
|
|
5
|
-
export
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
}
|
|
5
|
+
export type NodeViewContentProps<T extends keyof React.JSX.IntrinsicElements = 'div'> = {
|
|
6
|
+
as?: NoInfer<T>
|
|
7
|
+
} & ComponentProps<T>
|
|
9
8
|
|
|
10
|
-
export
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
export function NodeViewContent<T extends keyof React.JSX.IntrinsicElements = 'div'>({
|
|
10
|
+
as: Tag = 'div' as T,
|
|
11
|
+
...props
|
|
12
|
+
}: NodeViewContentProps<T>) {
|
|
13
|
+
const { nodeViewContentRef, nodeViewContentChildren } = useReactNodeView()
|
|
13
14
|
|
|
14
15
|
return (
|
|
15
16
|
// @ts-ignore
|
|
@@ -21,6 +22,8 @@ export const NodeViewContent: React.FC<NodeViewContentProps> = props => {
|
|
|
21
22
|
whiteSpace: 'pre-wrap',
|
|
22
23
|
...props.style,
|
|
23
24
|
}}
|
|
24
|
-
|
|
25
|
+
>
|
|
26
|
+
{nodeViewContentChildren}
|
|
27
|
+
</Tag>
|
|
25
28
|
)
|
|
26
29
|
}
|
package/src/NodeViewWrapper.tsx
CHANGED
|
@@ -3,8 +3,8 @@ import React from 'react'
|
|
|
3
3
|
import { useReactNodeView } from './useReactNodeView.js'
|
|
4
4
|
|
|
5
5
|
export interface NodeViewWrapperProps {
|
|
6
|
-
[key: string]: any
|
|
7
|
-
as?: React.ElementType
|
|
6
|
+
[key: string]: any
|
|
7
|
+
as?: React.ElementType
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
export const NodeViewWrapper: React.FC<NodeViewWrapperProps> = React.forwardRef((props, ref) => {
|
|
@@ -22,23 +22,23 @@ export interface ReactNodeViewRendererOptions extends NodeViewRendererOptions {
|
|
|
22
22
|
*/
|
|
23
23
|
update:
|
|
24
24
|
| ((props: {
|
|
25
|
-
oldNode: ProseMirrorNode
|
|
26
|
-
oldDecorations: readonly Decoration[]
|
|
27
|
-
oldInnerDecorations: DecorationSource
|
|
28
|
-
newNode: ProseMirrorNode
|
|
29
|
-
newDecorations: readonly Decoration[]
|
|
30
|
-
innerDecorations: DecorationSource
|
|
31
|
-
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
|
|
32
32
|
}) => boolean)
|
|
33
|
-
| null
|
|
33
|
+
| null
|
|
34
34
|
/**
|
|
35
35
|
* The tag name of the element wrapping the React component.
|
|
36
36
|
*/
|
|
37
|
-
as?: string
|
|
37
|
+
as?: string
|
|
38
38
|
/**
|
|
39
39
|
* The class name of the element wrapping the React component.
|
|
40
40
|
*/
|
|
41
|
-
className?: string
|
|
41
|
+
className?: string
|
|
42
42
|
/**
|
|
43
43
|
* Attributes that should be applied to the element wrapping the React component.
|
|
44
44
|
* If this is a function, it will be called each time the node view is updated.
|
|
@@ -46,10 +46,7 @@ export interface ReactNodeViewRendererOptions extends NodeViewRendererOptions {
|
|
|
46
46
|
*/
|
|
47
47
|
attrs?:
|
|
48
48
|
| Record<string, string>
|
|
49
|
-
| ((props: {
|
|
50
|
-
node: ProseMirrorNode;
|
|
51
|
-
HTMLAttributes: Record<string, any>;
|
|
52
|
-
}) => Record<string, string>);
|
|
49
|
+
| ((props: { node: ProseMirrorNode; HTMLAttributes: Record<string, any> }) => Record<string, string>)
|
|
53
50
|
}
|
|
54
51
|
|
|
55
52
|
export class ReactNodeView<
|
|
@@ -104,15 +101,13 @@ export class ReactNodeView<
|
|
|
104
101
|
const Component = this.component
|
|
105
102
|
// For performance reasons, we memoize the provider component
|
|
106
103
|
// 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
|
-
)
|
|
104
|
+
const ReactNodeViewProvider: React.FunctionComponent<NodeViewProps> = React.memo(componentProps => {
|
|
105
|
+
return (
|
|
106
|
+
<ReactNodeViewContext.Provider value={context}>
|
|
107
|
+
{React.createElement(Component, componentProps)}
|
|
108
|
+
</ReactNodeViewContext.Provider>
|
|
109
|
+
)
|
|
110
|
+
})
|
|
116
111
|
|
|
117
112
|
ReactNodeViewProvider.displayName = 'ReactNodeView'
|
|
118
113
|
|
|
@@ -159,8 +154,8 @@ export class ReactNodeView<
|
|
|
159
154
|
*/
|
|
160
155
|
get dom() {
|
|
161
156
|
if (
|
|
162
|
-
this.renderer.element.firstElementChild
|
|
163
|
-
|
|
157
|
+
this.renderer.element.firstElementChild &&
|
|
158
|
+
!this.renderer.element.firstElementChild?.hasAttribute('data-node-view-wrapper')
|
|
164
159
|
) {
|
|
165
160
|
throw Error('Please use the NodeViewWrapper component for your node view.')
|
|
166
161
|
}
|
|
@@ -211,11 +206,7 @@ export class ReactNodeView<
|
|
|
211
206
|
* On update, update the React component.
|
|
212
207
|
* To prevent unnecessary updates, the `update` option can be used.
|
|
213
208
|
*/
|
|
214
|
-
update(
|
|
215
|
-
node: Node,
|
|
216
|
-
decorations: readonly Decoration[],
|
|
217
|
-
innerDecorations: DecorationSource,
|
|
218
|
-
): boolean {
|
|
209
|
+
update(node: Node, decorations: readonly Decoration[], innerDecorations: DecorationSource): boolean {
|
|
219
210
|
const rerenderComponent = (props?: Record<string, any>) => {
|
|
220
211
|
this.renderer.updateProps(props)
|
|
221
212
|
if (typeof this.options.attrs === 'function') {
|
|
@@ -247,11 +238,7 @@ export class ReactNodeView<
|
|
|
247
238
|
})
|
|
248
239
|
}
|
|
249
240
|
|
|
250
|
-
if (
|
|
251
|
-
node === this.node
|
|
252
|
-
&& this.decorations === decorations
|
|
253
|
-
&& this.innerDecorations === innerDecorations
|
|
254
|
-
) {
|
|
241
|
+
if (node === this.node && this.decorations === decorations && this.innerDecorations === innerDecorations) {
|
|
255
242
|
return true
|
|
256
243
|
}
|
|
257
244
|
|
package/src/ReactRenderer.tsx
CHANGED
|
@@ -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,8 +67,8 @@ type ComponentType<R, P> =
|
|
|
74
67
|
* },
|
|
75
68
|
* as: 'span',
|
|
76
69
|
* })
|
|
77
|
-
*/
|
|
78
|
-
export class ReactRenderer<R = unknown, P extends Record<string, any> =
|
|
70
|
+
*/
|
|
71
|
+
export class ReactRenderer<R = unknown, P extends Record<string, any> = object> {
|
|
79
72
|
id: string
|
|
80
73
|
|
|
81
74
|
editor: Editor
|
|
@@ -93,13 +86,11 @@ export class ReactRenderer<R = unknown, P extends Record<string, any> = {}> {
|
|
|
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
|