@tiptap/react 2.1.0-rc.9 → 2.1.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/dist/index.cjs +181 -121
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +179 -123
- package/dist/index.js.map +1 -1
- package/dist/index.umd.js +184 -124
- package/dist/index.umd.js.map +1 -1
- package/dist/packages/react/src/BubbleMenu.d.ts +2 -2
- package/dist/packages/react/src/Context.d.ts +15 -0
- package/dist/packages/react/src/Editor.d.ts +2 -2
- package/dist/packages/react/src/EditorContent.d.ts +4 -4
- package/dist/packages/react/src/FloatingMenu.d.ts +2 -2
- package/dist/packages/react/src/ReactRenderer.d.ts +1 -1
- package/dist/packages/react/src/index.d.ts +10 -9
- package/dist/packages/react/src/useEditor.d.ts +1 -1
- package/package.json +7 -7
- package/src/BubbleMenu.tsx +16 -6
- package/src/Context.tsx +47 -0
- package/src/Editor.ts +2 -2
- package/src/EditorContent.tsx +13 -3
- package/src/FloatingMenu.tsx +16 -5
- package/src/NodeViewContent.tsx +1 -1
- package/src/NodeViewWrapper.tsx +1 -1
- package/src/ReactNodeViewRenderer.tsx +17 -3
- package/src/ReactRenderer.tsx +1 -1
- package/src/index.ts +10 -9
- package/src/useEditor.ts +7 -2
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
export * from './BubbleMenu';
|
|
2
|
-
export
|
|
3
|
-
export
|
|
4
|
-
export * from './
|
|
5
|
-
export * from './
|
|
6
|
-
export * from './
|
|
7
|
-
export * from './
|
|
8
|
-
export * from './
|
|
9
|
-
export * from './
|
|
1
|
+
export * from './BubbleMenu.js';
|
|
2
|
+
export * from './Context.js';
|
|
3
|
+
export { Editor } from './Editor.js';
|
|
4
|
+
export * from './EditorContent.js';
|
|
5
|
+
export * from './FloatingMenu.js';
|
|
6
|
+
export * from './NodeViewContent.js';
|
|
7
|
+
export * from './NodeViewWrapper.js';
|
|
8
|
+
export * from './ReactNodeViewRenderer.js';
|
|
9
|
+
export * from './ReactRenderer.js';
|
|
10
|
+
export * from './useEditor.js';
|
|
10
11
|
export * from '@tiptap/core';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { EditorOptions } from '@tiptap/core';
|
|
2
2
|
import { DependencyList } from 'react';
|
|
3
|
-
import { Editor } from './Editor';
|
|
3
|
+
import { Editor } from './Editor.js';
|
|
4
4
|
export declare const useEditor: (options?: Partial<EditorOptions>, deps?: DependencyList) => Editor | null;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tiptap/react",
|
|
3
3
|
"description": "React components for tiptap",
|
|
4
|
-
"version": "2.1.
|
|
4
|
+
"version": "2.1.1",
|
|
5
5
|
"homepage": "https://tiptap.dev",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"tiptap",
|
|
@@ -29,14 +29,14 @@
|
|
|
29
29
|
"dist"
|
|
30
30
|
],
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@tiptap/extension-bubble-menu": "^2.1.
|
|
33
|
-
"@tiptap/extension-floating-menu": "^2.1.
|
|
32
|
+
"@tiptap/extension-bubble-menu": "^2.1.1",
|
|
33
|
+
"@tiptap/extension-floating-menu": "^2.1.1"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
|
-
"@tiptap/core": "^2.1.
|
|
37
|
-
"@tiptap/pm": "^2.1.
|
|
38
|
-
"@types/react": "^18.
|
|
39
|
-
"@types/react-dom": "^18.
|
|
36
|
+
"@tiptap/core": "^2.1.1",
|
|
37
|
+
"@tiptap/pm": "^2.1.1",
|
|
38
|
+
"@types/react": "^18.2.14",
|
|
39
|
+
"@types/react-dom": "^18.2.6",
|
|
40
40
|
"react": "^18.0.0",
|
|
41
41
|
"react-dom": "^18.0.0"
|
|
42
42
|
},
|
package/src/BubbleMenu.tsx
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { BubbleMenuPlugin, BubbleMenuPluginProps } from '@tiptap/extension-bubble-menu'
|
|
2
2
|
import React, { useEffect, useState } from 'react'
|
|
3
3
|
|
|
4
|
+
import { useCurrentEditor } from './Context.js'
|
|
5
|
+
|
|
4
6
|
type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>;
|
|
5
7
|
|
|
6
|
-
export type BubbleMenuProps = Omit<Optional<BubbleMenuPluginProps, 'pluginKey'>, 'element'> & {
|
|
8
|
+
export type BubbleMenuProps = Omit<Optional<BubbleMenuPluginProps, 'pluginKey' | 'editor'>, 'element'> & {
|
|
7
9
|
className?: string;
|
|
8
10
|
children: React.ReactNode;
|
|
9
11
|
updateDelay?: number;
|
|
@@ -11,13 +13,14 @@ export type BubbleMenuProps = Omit<Optional<BubbleMenuPluginProps, 'pluginKey'>,
|
|
|
11
13
|
|
|
12
14
|
export const BubbleMenu = (props: BubbleMenuProps) => {
|
|
13
15
|
const [element, setElement] = useState<HTMLDivElement | null>(null)
|
|
16
|
+
const { editor: currentEditor } = useCurrentEditor()
|
|
14
17
|
|
|
15
18
|
useEffect(() => {
|
|
16
19
|
if (!element) {
|
|
17
20
|
return
|
|
18
21
|
}
|
|
19
22
|
|
|
20
|
-
if (props.editor
|
|
23
|
+
if (props.editor?.isDestroyed || currentEditor?.isDestroyed) {
|
|
21
24
|
return
|
|
22
25
|
}
|
|
23
26
|
|
|
@@ -25,18 +28,25 @@ export const BubbleMenu = (props: BubbleMenuProps) => {
|
|
|
25
28
|
pluginKey = 'bubbleMenu', editor, tippyOptions = {}, updateDelay, shouldShow = null,
|
|
26
29
|
} = props
|
|
27
30
|
|
|
31
|
+
const menuEditor = editor || currentEditor
|
|
32
|
+
|
|
33
|
+
if (!menuEditor) {
|
|
34
|
+
console.warn('BubbleMenu component is not rendered inside of an editor component or does not have editor prop.')
|
|
35
|
+
return
|
|
36
|
+
}
|
|
37
|
+
|
|
28
38
|
const plugin = BubbleMenuPlugin({
|
|
29
39
|
updateDelay,
|
|
30
|
-
editor,
|
|
40
|
+
editor: menuEditor,
|
|
31
41
|
element,
|
|
32
42
|
pluginKey,
|
|
33
43
|
shouldShow,
|
|
34
44
|
tippyOptions,
|
|
35
45
|
})
|
|
36
46
|
|
|
37
|
-
|
|
38
|
-
return () =>
|
|
39
|
-
}, [props.editor, element])
|
|
47
|
+
menuEditor.registerPlugin(plugin)
|
|
48
|
+
return () => menuEditor.unregisterPlugin(pluginKey)
|
|
49
|
+
}, [props.editor, currentEditor, element])
|
|
40
50
|
|
|
41
51
|
return (
|
|
42
52
|
<div ref={setElement} className={props.className} style={{ visibility: 'hidden' }}>
|
package/src/Context.tsx
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { EditorOptions } from '@tiptap/core'
|
|
2
|
+
import React, { createContext, ReactNode, useContext } from 'react'
|
|
3
|
+
|
|
4
|
+
import { Editor } from './Editor.js'
|
|
5
|
+
import { EditorContent } from './EditorContent.js'
|
|
6
|
+
import { useEditor } from './useEditor.js'
|
|
7
|
+
|
|
8
|
+
export type EditorContextValue = {
|
|
9
|
+
editor: Editor | null;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const EditorContext = createContext<EditorContextValue>({
|
|
13
|
+
editor: null,
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
export const EditorConsumer = EditorContext.Consumer
|
|
17
|
+
|
|
18
|
+
export const useCurrentEditor = () => useContext(EditorContext)
|
|
19
|
+
|
|
20
|
+
export type EditorProviderProps = {
|
|
21
|
+
children: ReactNode;
|
|
22
|
+
slotBefore?: ReactNode;
|
|
23
|
+
slotAfter?: ReactNode;
|
|
24
|
+
} & Partial<EditorOptions>
|
|
25
|
+
|
|
26
|
+
export const EditorProvider = ({
|
|
27
|
+
children, slotAfter, slotBefore, ...editorOptions
|
|
28
|
+
}: EditorProviderProps) => {
|
|
29
|
+
const editor = useEditor(editorOptions)
|
|
30
|
+
|
|
31
|
+
if (!editor) {
|
|
32
|
+
return null
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<EditorContext.Provider value={{ editor }}>
|
|
37
|
+
{slotBefore}
|
|
38
|
+
<EditorConsumer>
|
|
39
|
+
{({ editor: currentEditor }) => (
|
|
40
|
+
<EditorContent editor={currentEditor} />
|
|
41
|
+
)}
|
|
42
|
+
</EditorConsumer>
|
|
43
|
+
{children}
|
|
44
|
+
{slotAfter}
|
|
45
|
+
</EditorContext.Provider>
|
|
46
|
+
)
|
|
47
|
+
}
|
package/src/Editor.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Editor as CoreEditor } from '@tiptap/core'
|
|
2
2
|
import React from 'react'
|
|
3
3
|
|
|
4
|
-
import { EditorContentProps, EditorContentState } from './EditorContent'
|
|
5
|
-
import { ReactRenderer } from './ReactRenderer'
|
|
4
|
+
import { EditorContentProps, EditorContentState } from './EditorContent.js'
|
|
5
|
+
import { ReactRenderer } from './ReactRenderer.js'
|
|
6
6
|
|
|
7
7
|
type ContentComponent = React.Component<EditorContentProps, EditorContentState> & {
|
|
8
8
|
setRenderer(id: string, renderer: ReactRenderer): void;
|
package/src/EditorContent.tsx
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import React, { HTMLProps } from 'react'
|
|
2
2
|
import ReactDOM, { flushSync } from 'react-dom'
|
|
3
3
|
|
|
4
|
-
import { Editor } from './Editor'
|
|
5
|
-
import { ReactRenderer } from './ReactRenderer'
|
|
4
|
+
import { Editor } from './Editor.js'
|
|
5
|
+
import { ReactRenderer } from './ReactRenderer.js'
|
|
6
6
|
|
|
7
7
|
const Portals: React.FC<{ renderers: Record<string, ReactRenderer> }> = ({ renderers }) => {
|
|
8
8
|
return (
|
|
@@ -147,4 +147,14 @@ export class PureEditorContent extends React.Component<EditorContentProps, Edito
|
|
|
147
147
|
}
|
|
148
148
|
}
|
|
149
149
|
|
|
150
|
-
|
|
150
|
+
// EditorContent should be re-created whenever the Editor instance changes
|
|
151
|
+
const EditorContentWithKey = (props: EditorContentProps) => {
|
|
152
|
+
const key = React.useMemo(() => {
|
|
153
|
+
return Math.floor(Math.random() * 0xFFFFFFFF).toString()
|
|
154
|
+
}, [props.editor])
|
|
155
|
+
|
|
156
|
+
// Can't use JSX here because it conflicts with the type definition of Vue's JSX, so use createElement
|
|
157
|
+
return React.createElement(PureEditorContent, { key, ...props })
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export const EditorContent = React.memo(EditorContentWithKey)
|
package/src/FloatingMenu.tsx
CHANGED
|
@@ -3,22 +3,25 @@ import React, {
|
|
|
3
3
|
useEffect, useState,
|
|
4
4
|
} from 'react'
|
|
5
5
|
|
|
6
|
+
import { useCurrentEditor } from './Context.js'
|
|
7
|
+
|
|
6
8
|
type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>
|
|
7
9
|
|
|
8
|
-
export type FloatingMenuProps = Omit<Optional<FloatingMenuPluginProps, 'pluginKey'>, 'element'> & {
|
|
10
|
+
export type FloatingMenuProps = Omit<Optional<FloatingMenuPluginProps, 'pluginKey' | 'editor'>, 'element'> & {
|
|
9
11
|
className?: string,
|
|
10
12
|
children: React.ReactNode
|
|
11
13
|
}
|
|
12
14
|
|
|
13
15
|
export const FloatingMenu = (props: FloatingMenuProps) => {
|
|
14
16
|
const [element, setElement] = useState<HTMLDivElement | null>(null)
|
|
17
|
+
const { editor: currentEditor } = useCurrentEditor()
|
|
15
18
|
|
|
16
19
|
useEffect(() => {
|
|
17
20
|
if (!element) {
|
|
18
21
|
return
|
|
19
22
|
}
|
|
20
23
|
|
|
21
|
-
if (props.editor
|
|
24
|
+
if (props.editor?.isDestroyed || currentEditor?.isDestroyed) {
|
|
22
25
|
return
|
|
23
26
|
}
|
|
24
27
|
|
|
@@ -29,18 +32,26 @@ export const FloatingMenu = (props: FloatingMenuProps) => {
|
|
|
29
32
|
shouldShow = null,
|
|
30
33
|
} = props
|
|
31
34
|
|
|
35
|
+
const menuEditor = editor || currentEditor
|
|
36
|
+
|
|
37
|
+
if (!menuEditor) {
|
|
38
|
+
console.warn('FloatingMenu component is not rendered inside of an editor component or does not have editor prop.')
|
|
39
|
+
return
|
|
40
|
+
}
|
|
41
|
+
|
|
32
42
|
const plugin = FloatingMenuPlugin({
|
|
33
43
|
pluginKey,
|
|
34
|
-
editor,
|
|
44
|
+
editor: menuEditor,
|
|
35
45
|
element,
|
|
36
46
|
tippyOptions,
|
|
37
47
|
shouldShow,
|
|
38
48
|
})
|
|
39
49
|
|
|
40
|
-
|
|
41
|
-
return () =>
|
|
50
|
+
menuEditor.registerPlugin(plugin)
|
|
51
|
+
return () => menuEditor.unregisterPlugin(pluginKey)
|
|
42
52
|
}, [
|
|
43
53
|
props.editor,
|
|
54
|
+
currentEditor,
|
|
44
55
|
element,
|
|
45
56
|
])
|
|
46
57
|
|
package/src/NodeViewContent.tsx
CHANGED
package/src/NodeViewWrapper.tsx
CHANGED
|
@@ -10,9 +10,9 @@ import { Node as ProseMirrorNode } from '@tiptap/pm/model'
|
|
|
10
10
|
import { Decoration, NodeView as ProseMirrorNodeView } from '@tiptap/pm/view'
|
|
11
11
|
import React from 'react'
|
|
12
12
|
|
|
13
|
-
import { Editor } from './Editor'
|
|
14
|
-
import { ReactRenderer } from './ReactRenderer'
|
|
15
|
-
import { ReactNodeViewContext, ReactNodeViewContextProps } from './useReactNodeView'
|
|
13
|
+
import { Editor } from './Editor.js'
|
|
14
|
+
import { ReactRenderer } from './ReactRenderer.js'
|
|
15
|
+
import { ReactNodeViewContext, ReactNodeViewContextProps } from './useReactNodeView.js'
|
|
16
16
|
|
|
17
17
|
export interface ReactNodeViewRendererOptions extends NodeViewRendererOptions {
|
|
18
18
|
update:
|
|
@@ -99,6 +99,9 @@ class ReactNodeView extends NodeView<
|
|
|
99
99
|
|
|
100
100
|
const { className = '' } = this.options
|
|
101
101
|
|
|
102
|
+
this.handleSelectionUpdate = this.handleSelectionUpdate.bind(this)
|
|
103
|
+
this.editor.on('selectionUpdate', this.handleSelectionUpdate)
|
|
104
|
+
|
|
102
105
|
this.renderer = new ReactRenderer(ReactNodeViewProvider, {
|
|
103
106
|
editor: this.editor,
|
|
104
107
|
props,
|
|
@@ -127,6 +130,16 @@ class ReactNodeView extends NodeView<
|
|
|
127
130
|
return this.contentDOMElement
|
|
128
131
|
}
|
|
129
132
|
|
|
133
|
+
handleSelectionUpdate() {
|
|
134
|
+
const { from, to } = this.editor.state.selection
|
|
135
|
+
|
|
136
|
+
if (from <= this.getPos() && to >= this.getPos() + this.node.nodeSize) {
|
|
137
|
+
this.selectNode()
|
|
138
|
+
} else {
|
|
139
|
+
this.deselectNode()
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
130
143
|
update(node: ProseMirrorNode, decorations: DecorationWithType[]) {
|
|
131
144
|
const updateProps = (props?: Record<string, any>) => {
|
|
132
145
|
this.renderer.updateProps(props)
|
|
@@ -178,6 +191,7 @@ class ReactNodeView extends NodeView<
|
|
|
178
191
|
|
|
179
192
|
destroy() {
|
|
180
193
|
this.renderer.destroy()
|
|
194
|
+
this.editor.off('selectionUpdate', this.handleSelectionUpdate)
|
|
181
195
|
this.contentDOMElement = null
|
|
182
196
|
}
|
|
183
197
|
}
|
package/src/ReactRenderer.tsx
CHANGED
package/src/index.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
export * from './BubbleMenu'
|
|
2
|
-
export
|
|
3
|
-
export
|
|
4
|
-
export * from './
|
|
5
|
-
export * from './
|
|
6
|
-
export * from './
|
|
7
|
-
export * from './
|
|
8
|
-
export * from './
|
|
9
|
-
export * from './
|
|
1
|
+
export * from './BubbleMenu.js'
|
|
2
|
+
export * from './Context.js'
|
|
3
|
+
export { Editor } from './Editor.js'
|
|
4
|
+
export * from './EditorContent.js'
|
|
5
|
+
export * from './FloatingMenu.js'
|
|
6
|
+
export * from './NodeViewContent.js'
|
|
7
|
+
export * from './NodeViewWrapper.js'
|
|
8
|
+
export * from './ReactNodeViewRenderer.js'
|
|
9
|
+
export * from './ReactRenderer.js'
|
|
10
|
+
export * from './useEditor.js'
|
|
10
11
|
export * from '@tiptap/core'
|
package/src/useEditor.ts
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
useState,
|
|
7
7
|
} from 'react'
|
|
8
8
|
|
|
9
|
-
import { Editor } from './Editor'
|
|
9
|
+
import { Editor } from './Editor.js'
|
|
10
10
|
|
|
11
11
|
function useForceUpdate() {
|
|
12
12
|
const [, setValue] = useState(0)
|
|
@@ -113,10 +113,15 @@ export const useEditor = (options: Partial<EditorOptions> = {}, deps: Dependency
|
|
|
113
113
|
})
|
|
114
114
|
|
|
115
115
|
return () => {
|
|
116
|
-
instance.destroy()
|
|
117
116
|
isMounted = false
|
|
118
117
|
}
|
|
119
118
|
}, deps)
|
|
120
119
|
|
|
120
|
+
useEffect(() => {
|
|
121
|
+
return () => {
|
|
122
|
+
editor?.destroy()
|
|
123
|
+
}
|
|
124
|
+
}, [editor])
|
|
125
|
+
|
|
121
126
|
return editor
|
|
122
127
|
}
|