@tiptap/react 3.0.0 → 3.0.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 +1030 -1163
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +350 -0
- package/dist/index.d.ts +350 -0
- package/dist/index.js +970 -1138
- 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 +34 -21
- package/src/Context.tsx +18 -12
- package/src/Editor.ts +10 -11
- package/src/EditorContent.tsx +104 -64
- package/src/NodeViewContent.tsx +13 -8
- package/src/NodeViewWrapper.tsx +3 -2
- package/src/ReactMarkViewRenderer.tsx +111 -0
- package/src/ReactNodeViewRenderer.tsx +184 -67
- package/src/ReactRenderer.tsx +152 -51
- package/src/index.ts +2 -3
- package/src/menus/BubbleMenu.tsx +68 -0
- package/src/menus/FloatingMenu.tsx +68 -0
- package/src/menus/index.ts +2 -0
- package/src/types.ts +6 -0
- package/src/useEditor.ts +286 -166
- package/src/useEditorState.ts +133 -85
- package/src/useReactNodeView.ts +21 -5
- package/dist/index.umd.js +0 -1219
- package/dist/index.umd.js.map +0 -1
- package/dist/packages/core/src/CommandManager.d.ts +0 -20
- package/dist/packages/core/src/Editor.d.ts +0 -161
- package/dist/packages/core/src/EventEmitter.d.ts +0 -11
- package/dist/packages/core/src/Extension.d.ts +0 -343
- package/dist/packages/core/src/ExtensionManager.d.ts +0 -55
- package/dist/packages/core/src/InputRule.d.ts +0 -42
- package/dist/packages/core/src/Mark.d.ts +0 -451
- package/dist/packages/core/src/Node.d.ts +0 -611
- package/dist/packages/core/src/NodePos.d.ts +0 -44
- package/dist/packages/core/src/NodeView.d.ts +0 -31
- package/dist/packages/core/src/PasteRule.d.ts +0 -50
- package/dist/packages/core/src/Tracker.d.ts +0 -11
- package/dist/packages/core/src/commands/blur.d.ts +0 -13
- package/dist/packages/core/src/commands/clearContent.d.ts +0 -14
- package/dist/packages/core/src/commands/clearNodes.d.ts +0 -13
- package/dist/packages/core/src/commands/command.d.ts +0 -18
- package/dist/packages/core/src/commands/createParagraphNear.d.ts +0 -13
- package/dist/packages/core/src/commands/cut.d.ts +0 -20
- package/dist/packages/core/src/commands/deleteCurrentNode.d.ts +0 -13
- package/dist/packages/core/src/commands/deleteNode.d.ts +0 -15
- package/dist/packages/core/src/commands/deleteRange.d.ts +0 -14
- package/dist/packages/core/src/commands/deleteSelection.d.ts +0 -13
- package/dist/packages/core/src/commands/enter.d.ts +0 -13
- package/dist/packages/core/src/commands/exitCode.d.ts +0 -13
- package/dist/packages/core/src/commands/extendMarkRange.d.ts +0 -25
- package/dist/packages/core/src/commands/first.d.ts +0 -14
- package/dist/packages/core/src/commands/focus.d.ts +0 -27
- package/dist/packages/core/src/commands/forEach.d.ts +0 -14
- package/dist/packages/core/src/commands/index.d.ts +0 -55
- package/dist/packages/core/src/commands/insertContent.d.ts +0 -34
- package/dist/packages/core/src/commands/insertContentAt.d.ts +0 -47
- package/dist/packages/core/src/commands/join.d.ts +0 -41
- package/dist/packages/core/src/commands/joinItemBackward.d.ts +0 -13
- package/dist/packages/core/src/commands/joinItemForward.d.ts +0 -13
- package/dist/packages/core/src/commands/joinTextblockBackward.d.ts +0 -12
- package/dist/packages/core/src/commands/joinTextblockForward.d.ts +0 -12
- package/dist/packages/core/src/commands/keyboardShortcut.d.ts +0 -14
- package/dist/packages/core/src/commands/lift.d.ts +0 -17
- package/dist/packages/core/src/commands/liftEmptyBlock.d.ts +0 -13
- package/dist/packages/core/src/commands/liftListItem.d.ts +0 -15
- package/dist/packages/core/src/commands/newlineInCode.d.ts +0 -13
- package/dist/packages/core/src/commands/resetAttributes.d.ts +0 -16
- package/dist/packages/core/src/commands/scrollIntoView.d.ts +0 -13
- package/dist/packages/core/src/commands/selectAll.d.ts +0 -13
- package/dist/packages/core/src/commands/selectNodeBackward.d.ts +0 -13
- package/dist/packages/core/src/commands/selectNodeForward.d.ts +0 -13
- package/dist/packages/core/src/commands/selectParentNode.d.ts +0 -13
- package/dist/packages/core/src/commands/selectTextblockEnd.d.ts +0 -13
- package/dist/packages/core/src/commands/selectTextblockStart.d.ts +0 -13
- package/dist/packages/core/src/commands/setContent.d.ts +0 -40
- package/dist/packages/core/src/commands/setMark.d.ts +0 -15
- package/dist/packages/core/src/commands/setMeta.d.ts +0 -15
- package/dist/packages/core/src/commands/setNode.d.ts +0 -16
- package/dist/packages/core/src/commands/setNodeSelection.d.ts +0 -14
- package/dist/packages/core/src/commands/setTextSelection.d.ts +0 -14
- package/dist/packages/core/src/commands/sinkListItem.d.ts +0 -15
- package/dist/packages/core/src/commands/splitBlock.d.ts +0 -17
- package/dist/packages/core/src/commands/splitListItem.d.ts +0 -15
- package/dist/packages/core/src/commands/toggleList.d.ts +0 -18
- package/dist/packages/core/src/commands/toggleMark.d.ts +0 -30
- package/dist/packages/core/src/commands/toggleNode.d.ts +0 -17
- package/dist/packages/core/src/commands/toggleWrap.d.ts +0 -16
- package/dist/packages/core/src/commands/undoInputRule.d.ts +0 -13
- package/dist/packages/core/src/commands/unsetAllMarks.d.ts +0 -13
- package/dist/packages/core/src/commands/unsetMark.d.ts +0 -25
- package/dist/packages/core/src/commands/updateAttributes.d.ts +0 -24
- package/dist/packages/core/src/commands/wrapIn.d.ts +0 -16
- package/dist/packages/core/src/commands/wrapInList.d.ts +0 -16
- package/dist/packages/core/src/extensions/clipboardTextSerializer.d.ts +0 -5
- package/dist/packages/core/src/extensions/commands.d.ts +0 -3
- package/dist/packages/core/src/extensions/editable.d.ts +0 -2
- package/dist/packages/core/src/extensions/focusEvents.d.ts +0 -2
- package/dist/packages/core/src/extensions/index.d.ts +0 -6
- package/dist/packages/core/src/extensions/keymap.d.ts +0 -2
- package/dist/packages/core/src/extensions/tabindex.d.ts +0 -2
- package/dist/packages/core/src/helpers/combineTransactionSteps.d.ts +0 -10
- package/dist/packages/core/src/helpers/createChainableState.d.ts +0 -10
- package/dist/packages/core/src/helpers/createDocument.d.ts +0 -12
- package/dist/packages/core/src/helpers/createNodeFromContent.d.ts +0 -15
- package/dist/packages/core/src/helpers/defaultBlockAt.d.ts +0 -7
- package/dist/packages/core/src/helpers/findChildren.d.ts +0 -9
- package/dist/packages/core/src/helpers/findChildrenInRange.d.ts +0 -10
- package/dist/packages/core/src/helpers/findParentNode.d.ts +0 -16
- package/dist/packages/core/src/helpers/findParentNodeClosestToPos.d.ts +0 -17
- package/dist/packages/core/src/helpers/generateHTML.d.ts +0 -8
- package/dist/packages/core/src/helpers/generateJSON.d.ts +0 -8
- package/dist/packages/core/src/helpers/generateText.d.ts +0 -12
- package/dist/packages/core/src/helpers/getAttributes.d.ts +0 -9
- package/dist/packages/core/src/helpers/getAttributesFromExtensions.d.ts +0 -6
- package/dist/packages/core/src/helpers/getChangedRanges.d.ts +0 -11
- package/dist/packages/core/src/helpers/getDebugJSON.d.ts +0 -8
- package/dist/packages/core/src/helpers/getExtensionField.d.ts +0 -9
- package/dist/packages/core/src/helpers/getHTMLFromFragment.d.ts +0 -2
- package/dist/packages/core/src/helpers/getMarkAttributes.d.ts +0 -3
- package/dist/packages/core/src/helpers/getMarkRange.d.ts +0 -3
- package/dist/packages/core/src/helpers/getMarkType.d.ts +0 -2
- package/dist/packages/core/src/helpers/getMarksBetween.d.ts +0 -3
- package/dist/packages/core/src/helpers/getNodeAtPosition.d.ts +0 -11
- package/dist/packages/core/src/helpers/getNodeAttributes.d.ts +0 -3
- package/dist/packages/core/src/helpers/getNodeType.d.ts +0 -2
- package/dist/packages/core/src/helpers/getRenderedAttributes.d.ts +0 -3
- package/dist/packages/core/src/helpers/getSchema.d.ts +0 -4
- package/dist/packages/core/src/helpers/getSchemaByResolvedExtensions.d.ts +0 -10
- package/dist/packages/core/src/helpers/getSchemaTypeByName.d.ts +0 -8
- package/dist/packages/core/src/helpers/getSchemaTypeNameByName.d.ts +0 -8
- package/dist/packages/core/src/helpers/getSplittedAttributes.d.ts +0 -9
- package/dist/packages/core/src/helpers/getText.d.ts +0 -15
- package/dist/packages/core/src/helpers/getTextBetween.d.ts +0 -14
- package/dist/packages/core/src/helpers/getTextContentFromNodes.d.ts +0 -8
- package/dist/packages/core/src/helpers/getTextSerializersFromSchema.d.ts +0 -8
- package/dist/packages/core/src/helpers/index.d.ts +0 -50
- package/dist/packages/core/src/helpers/injectExtensionAttributesToParseRule.d.ts +0 -9
- package/dist/packages/core/src/helpers/isActive.d.ts +0 -2
- package/dist/packages/core/src/helpers/isAtEndOfNode.d.ts +0 -2
- package/dist/packages/core/src/helpers/isAtStartOfNode.d.ts +0 -2
- package/dist/packages/core/src/helpers/isExtensionRulesEnabled.d.ts +0 -2
- package/dist/packages/core/src/helpers/isList.d.ts +0 -2
- package/dist/packages/core/src/helpers/isMarkActive.d.ts +0 -3
- package/dist/packages/core/src/helpers/isNodeActive.d.ts +0 -3
- package/dist/packages/core/src/helpers/isNodeEmpty.d.ts +0 -2
- package/dist/packages/core/src/helpers/isNodeSelection.d.ts +0 -2
- package/dist/packages/core/src/helpers/isTextSelection.d.ts +0 -2
- package/dist/packages/core/src/helpers/posToDOMRect.d.ts +0 -2
- package/dist/packages/core/src/helpers/resolveFocusPosition.d.ts +0 -4
- package/dist/packages/core/src/helpers/selectionToInsertionEnd.d.ts +0 -2
- package/dist/packages/core/src/helpers/splitExtensions.d.ts +0 -9
- package/dist/packages/core/src/index.d.ts +0 -24
- package/dist/packages/core/src/inputRules/index.d.ts +0 -5
- package/dist/packages/core/src/inputRules/markInputRule.d.ts +0 -13
- package/dist/packages/core/src/inputRules/nodeInputRule.d.ts +0 -23
- package/dist/packages/core/src/inputRules/textInputRule.d.ts +0 -10
- package/dist/packages/core/src/inputRules/textblockTypeInputRule.d.ts +0 -15
- package/dist/packages/core/src/inputRules/wrappingInputRule.d.ts +0 -28
- package/dist/packages/core/src/pasteRules/index.d.ts +0 -3
- package/dist/packages/core/src/pasteRules/markPasteRule.d.ts +0 -13
- package/dist/packages/core/src/pasteRules/nodePasteRule.d.ts +0 -13
- package/dist/packages/core/src/pasteRules/textPasteRule.d.ts +0 -10
- package/dist/packages/core/src/style.d.ts +0 -1
- package/dist/packages/core/src/types.d.ts +0 -255
- package/dist/packages/core/src/utilities/callOrReturn.d.ts +0 -9
- package/dist/packages/core/src/utilities/createStyleTag.d.ts +0 -1
- package/dist/packages/core/src/utilities/deleteProps.d.ts +0 -6
- package/dist/packages/core/src/utilities/elementFromString.d.ts +0 -1
- package/dist/packages/core/src/utilities/escapeForRegEx.d.ts +0 -1
- package/dist/packages/core/src/utilities/findDuplicates.d.ts +0 -1
- package/dist/packages/core/src/utilities/fromString.d.ts +0 -1
- package/dist/packages/core/src/utilities/index.d.ts +0 -20
- package/dist/packages/core/src/utilities/isAndroid.d.ts +0 -1
- package/dist/packages/core/src/utilities/isEmptyObject.d.ts +0 -1
- package/dist/packages/core/src/utilities/isFunction.d.ts +0 -1
- package/dist/packages/core/src/utilities/isMacOS.d.ts +0 -1
- package/dist/packages/core/src/utilities/isNumber.d.ts +0 -1
- package/dist/packages/core/src/utilities/isPlainObject.d.ts +0 -1
- package/dist/packages/core/src/utilities/isRegExp.d.ts +0 -1
- package/dist/packages/core/src/utilities/isString.d.ts +0 -1
- package/dist/packages/core/src/utilities/isiOS.d.ts +0 -1
- package/dist/packages/core/src/utilities/mergeAttributes.d.ts +0 -1
- package/dist/packages/core/src/utilities/mergeDeep.d.ts +0 -1
- package/dist/packages/core/src/utilities/minMax.d.ts +0 -1
- package/dist/packages/core/src/utilities/objectIncludes.d.ts +0 -8
- package/dist/packages/core/src/utilities/removeDuplicates.d.ts +0 -8
- package/dist/packages/extension-bubble-menu/src/bubble-menu-plugin.d.ts +0 -76
- package/dist/packages/extension-bubble-menu/src/bubble-menu.d.ts +0 -15
- package/dist/packages/extension-bubble-menu/src/index.d.ts +0 -4
- package/dist/packages/extension-floating-menu/src/floating-menu-plugin.d.ts +0 -66
- package/dist/packages/extension-floating-menu/src/floating-menu.d.ts +0 -15
- package/dist/packages/extension-floating-menu/src/index.d.ts +0 -4
- package/dist/packages/react/src/BubbleMenu.d.ts +0 -11
- package/dist/packages/react/src/Context.d.ts +0 -23
- package/dist/packages/react/src/Editor.d.ts +0 -12
- package/dist/packages/react/src/EditorContent.d.ts +0 -24
- package/dist/packages/react/src/FloatingMenu.d.ts +0 -10
- package/dist/packages/react/src/NodeViewContent.d.ts +0 -6
- package/dist/packages/react/src/NodeViewWrapper.d.ts +0 -6
- package/dist/packages/react/src/ReactNodeViewRenderer.d.ts +0 -16
- package/dist/packages/react/src/ReactRenderer.d.ts +0 -62
- package/dist/packages/react/src/index.d.ts +0 -13
- package/dist/packages/react/src/useEditor.d.ts +0 -39
- package/dist/packages/react/src/useEditorState.d.ts +0 -22
- package/dist/packages/react/src/useReactNodeView.d.ts +0 -6
- package/src/BubbleMenu.tsx +0 -57
- package/src/FloatingMenu.tsx +0 -64
package/src/ReactRenderer.tsx
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
|
-
import { Editor } from '@tiptap/core'
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import type { Editor } from '@tiptap/core'
|
|
2
|
+
import type {
|
|
3
|
+
ComponentClass,
|
|
4
|
+
ForwardRefExoticComponent,
|
|
5
|
+
FunctionComponent,
|
|
6
|
+
PropsWithoutRef,
|
|
7
|
+
ReactNode,
|
|
8
|
+
RefAttributes,
|
|
9
|
+
} from 'react'
|
|
10
|
+
import { version as reactVersion } from 'react'
|
|
11
|
+
import { flushSync } from 'react-dom'
|
|
12
|
+
|
|
13
|
+
import type { EditorWithContentComponent } from './Editor.js'
|
|
5
14
|
|
|
6
15
|
/**
|
|
7
16
|
* Check if a component is a class component.
|
|
@@ -9,11 +18,7 @@ import { Editor as ExtendedEditor } from './Editor.js'
|
|
|
9
18
|
* @returns {boolean}
|
|
10
19
|
*/
|
|
11
20
|
function isClassComponent(Component: any) {
|
|
12
|
-
return !!(
|
|
13
|
-
typeof Component === 'function'
|
|
14
|
-
&& Component.prototype
|
|
15
|
-
&& Component.prototype.isReactComponent
|
|
16
|
-
)
|
|
21
|
+
return !!(typeof Component === 'function' && Component.prototype && Component.prototype.isReactComponent)
|
|
17
22
|
}
|
|
18
23
|
|
|
19
24
|
/**
|
|
@@ -23,31 +28,96 @@ function isClassComponent(Component: any) {
|
|
|
23
28
|
*/
|
|
24
29
|
function isForwardRefComponent(Component: any) {
|
|
25
30
|
return !!(
|
|
26
|
-
typeof Component === 'object'
|
|
27
|
-
|
|
31
|
+
typeof Component === 'object' &&
|
|
32
|
+
Component.$$typeof &&
|
|
33
|
+
(Component.$$typeof.toString() === 'Symbol(react.forward_ref)' ||
|
|
34
|
+
Component.$$typeof.description === 'react.forward_ref')
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Check if a component is a memoized component.
|
|
40
|
+
* @param Component
|
|
41
|
+
* @returns {boolean}
|
|
42
|
+
*/
|
|
43
|
+
function isMemoComponent(Component: any) {
|
|
44
|
+
return !!(
|
|
45
|
+
typeof Component === 'object' &&
|
|
46
|
+
Component.$$typeof &&
|
|
47
|
+
(Component.$$typeof.toString() === 'Symbol(react.memo)' || Component.$$typeof.description === 'react.memo')
|
|
28
48
|
)
|
|
29
49
|
}
|
|
30
50
|
|
|
51
|
+
/**
|
|
52
|
+
* Check if a component can safely receive a ref prop.
|
|
53
|
+
* This includes class components, forwardRef components, and memoized components
|
|
54
|
+
* that wrap forwardRef or class components.
|
|
55
|
+
* @param Component
|
|
56
|
+
* @returns {boolean}
|
|
57
|
+
*/
|
|
58
|
+
function canReceiveRef(Component: any) {
|
|
59
|
+
// Check if it's a class component
|
|
60
|
+
if (isClassComponent(Component)) {
|
|
61
|
+
return true
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Check if it's a forwardRef component
|
|
65
|
+
if (isForwardRefComponent(Component)) {
|
|
66
|
+
return true
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Check if it's a memoized component
|
|
70
|
+
if (isMemoComponent(Component)) {
|
|
71
|
+
// For memoized components, check the wrapped component
|
|
72
|
+
const wrappedComponent = Component.type
|
|
73
|
+
if (wrappedComponent) {
|
|
74
|
+
return isClassComponent(wrappedComponent) || isForwardRefComponent(wrappedComponent)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return false
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Check if we're running React 19+ by detecting if function components support ref props
|
|
83
|
+
* @returns {boolean}
|
|
84
|
+
*/
|
|
85
|
+
function isReact19Plus(): boolean {
|
|
86
|
+
// React 19 is detected by checking React version if available
|
|
87
|
+
// In practice, we'll use a more conservative approach and assume React 18 behavior
|
|
88
|
+
// unless we can definitively detect React 19
|
|
89
|
+
try {
|
|
90
|
+
// @ts-ignore
|
|
91
|
+
if (reactVersion) {
|
|
92
|
+
const majorVersion = parseInt(reactVersion.split('.')[0], 10)
|
|
93
|
+
return majorVersion >= 19
|
|
94
|
+
}
|
|
95
|
+
} catch {
|
|
96
|
+
// Fallback to React 18 behavior if we can't determine version
|
|
97
|
+
}
|
|
98
|
+
return false
|
|
99
|
+
}
|
|
100
|
+
|
|
31
101
|
export interface ReactRendererOptions {
|
|
32
102
|
/**
|
|
33
103
|
* The editor instance.
|
|
34
104
|
* @type {Editor}
|
|
35
105
|
*/
|
|
36
|
-
editor: Editor
|
|
106
|
+
editor: Editor
|
|
37
107
|
|
|
38
108
|
/**
|
|
39
109
|
* The props for the component.
|
|
40
110
|
* @type {Record<string, any>}
|
|
41
111
|
* @default {}
|
|
42
112
|
*/
|
|
43
|
-
props?: Record<string, any
|
|
113
|
+
props?: Record<string, any>
|
|
44
114
|
|
|
45
115
|
/**
|
|
46
116
|
* The tag name of the element.
|
|
47
117
|
* @type {string}
|
|
48
118
|
* @default 'div'
|
|
49
119
|
*/
|
|
50
|
-
as?: string
|
|
120
|
+
as?: string
|
|
51
121
|
|
|
52
122
|
/**
|
|
53
123
|
* The class name of the element.
|
|
@@ -55,21 +125,13 @@ export interface ReactRendererOptions {
|
|
|
55
125
|
* @default ''
|
|
56
126
|
* @example 'foo bar'
|
|
57
127
|
*/
|
|
58
|
-
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>,
|
|
128
|
+
className?: string
|
|
67
129
|
}
|
|
68
130
|
|
|
69
131
|
type ComponentType<R, P> =
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
132
|
+
| ComponentClass<P>
|
|
133
|
+
| FunctionComponent<P>
|
|
134
|
+
| ForwardRefExoticComponent<PropsWithoutRef<P> & RefAttributes<R>>
|
|
73
135
|
|
|
74
136
|
/**
|
|
75
137
|
* The ReactRenderer class. It's responsible for rendering React components inside the editor.
|
|
@@ -81,33 +143,33 @@ type ComponentType<R, P> =
|
|
|
81
143
|
* },
|
|
82
144
|
* as: 'span',
|
|
83
145
|
* })
|
|
84
|
-
*/
|
|
85
|
-
export class ReactRenderer<R = unknown, P =
|
|
146
|
+
*/
|
|
147
|
+
export class ReactRenderer<R = unknown, P extends Record<string, any> = object> {
|
|
86
148
|
id: string
|
|
87
149
|
|
|
88
|
-
editor:
|
|
150
|
+
editor: Editor
|
|
89
151
|
|
|
90
152
|
component: any
|
|
91
153
|
|
|
92
154
|
element: Element
|
|
93
155
|
|
|
94
|
-
props:
|
|
156
|
+
props: P
|
|
95
157
|
|
|
96
|
-
reactElement:
|
|
158
|
+
reactElement: ReactNode
|
|
97
159
|
|
|
98
160
|
ref: R | null = null
|
|
99
161
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
this.id = Math.floor(Math.random() *
|
|
162
|
+
/**
|
|
163
|
+
* Immediately creates element and renders the provided React component.
|
|
164
|
+
*/
|
|
165
|
+
constructor(
|
|
166
|
+
component: ComponentType<R, P>,
|
|
167
|
+
{ editor, props = {}, as = 'div', className = '' }: ReactRendererOptions,
|
|
168
|
+
) {
|
|
169
|
+
this.id = Math.floor(Math.random() * 0xffffffff).toString()
|
|
108
170
|
this.component = component
|
|
109
|
-
this.editor = editor as
|
|
110
|
-
this.props = props
|
|
171
|
+
this.editor = editor as EditorWithContentComponent
|
|
172
|
+
this.props = props as P
|
|
111
173
|
this.element = document.createElement(as)
|
|
112
174
|
this.element.classList.add('react-renderer')
|
|
113
175
|
|
|
@@ -115,30 +177,55 @@ export class ReactRenderer<R = unknown, P = unknown> {
|
|
|
115
177
|
this.element.classList.add(...className.split(' '))
|
|
116
178
|
}
|
|
117
179
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
180
|
+
// If the editor is already initialized, we will need to
|
|
181
|
+
// synchronously render the component to ensure it renders
|
|
182
|
+
// together with Prosemirror's rendering.
|
|
183
|
+
if (this.editor.isInitialized) {
|
|
184
|
+
flushSync(() => {
|
|
185
|
+
this.render()
|
|
186
|
+
})
|
|
187
|
+
} else {
|
|
188
|
+
queueMicrotask(() => {
|
|
189
|
+
this.render()
|
|
121
190
|
})
|
|
122
191
|
}
|
|
123
|
-
|
|
124
|
-
this.render()
|
|
125
192
|
}
|
|
126
193
|
|
|
194
|
+
/**
|
|
195
|
+
* Render the React component.
|
|
196
|
+
*/
|
|
127
197
|
render(): void {
|
|
128
198
|
const Component = this.component
|
|
129
199
|
const props = this.props
|
|
200
|
+
const editor = this.editor as EditorWithContentComponent
|
|
201
|
+
|
|
202
|
+
// Handle ref forwarding with React 18/19 compatibility
|
|
203
|
+
const isReact19 = isReact19Plus()
|
|
204
|
+
const componentCanReceiveRef = canReceiveRef(Component)
|
|
130
205
|
|
|
131
|
-
|
|
132
|
-
|
|
206
|
+
const elementProps = { ...props }
|
|
207
|
+
|
|
208
|
+
// Always remove ref if the component cannot receive it (unless React 19+)
|
|
209
|
+
if (elementProps.ref && !(isReact19 || componentCanReceiveRef)) {
|
|
210
|
+
delete elementProps.ref
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Only assign our own ref if allowed
|
|
214
|
+
if (!elementProps.ref && (isReact19 || componentCanReceiveRef)) {
|
|
215
|
+
// @ts-ignore - Setting ref prop for compatible components
|
|
216
|
+
elementProps.ref = (ref: R) => {
|
|
133
217
|
this.ref = ref
|
|
134
218
|
}
|
|
135
219
|
}
|
|
136
220
|
|
|
137
|
-
this.reactElement = <Component {...
|
|
221
|
+
this.reactElement = <Component {...elementProps} />
|
|
138
222
|
|
|
139
|
-
|
|
223
|
+
editor?.contentComponent?.setRenderer(this.id, this)
|
|
140
224
|
}
|
|
141
225
|
|
|
226
|
+
/**
|
|
227
|
+
* Re-renders the React component with new props.
|
|
228
|
+
*/
|
|
142
229
|
updateProps(props: Record<string, any> = {}): void {
|
|
143
230
|
this.props = {
|
|
144
231
|
...this.props,
|
|
@@ -148,7 +235,21 @@ export class ReactRenderer<R = unknown, P = unknown> {
|
|
|
148
235
|
this.render()
|
|
149
236
|
}
|
|
150
237
|
|
|
238
|
+
/**
|
|
239
|
+
* Destroy the React component.
|
|
240
|
+
*/
|
|
151
241
|
destroy(): void {
|
|
152
|
-
this.editor
|
|
242
|
+
const editor = this.editor as EditorWithContentComponent
|
|
243
|
+
|
|
244
|
+
editor?.contentComponent?.removeRenderer(this.id)
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Update the attributes of the element that holds the React component.
|
|
249
|
+
*/
|
|
250
|
+
updateAttributes(attributes: Record<string, string>): void {
|
|
251
|
+
Object.keys(attributes).forEach(key => {
|
|
252
|
+
this.element.setAttribute(key, attributes[key])
|
|
253
|
+
})
|
|
153
254
|
}
|
|
154
255
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
export * from './BubbleMenu.js'
|
|
2
1
|
export * from './Context.js'
|
|
3
|
-
export { Editor } from './Editor.js'
|
|
4
2
|
export * from './EditorContent.js'
|
|
5
|
-
export * from './FloatingMenu.js'
|
|
6
3
|
export * from './NodeViewContent.js'
|
|
7
4
|
export * from './NodeViewWrapper.js'
|
|
5
|
+
export * from './ReactMarkViewRenderer.js'
|
|
8
6
|
export * from './ReactNodeViewRenderer.js'
|
|
9
7
|
export * from './ReactRenderer.js'
|
|
8
|
+
export * from './types.js'
|
|
10
9
|
export * from './useEditor.js'
|
|
11
10
|
export * from './useEditorState.js'
|
|
12
11
|
export * from './useReactNodeView.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
|
+
)
|