@tiptap/react 2.0.0-beta.20 → 2.0.0-beta.201
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/README.md +2 -2
- package/dist/packages/react/src/BubbleMenu.d.ts +7 -3
- package/dist/packages/react/src/Editor.d.ts +1 -1
- package/dist/packages/react/src/EditorContent.d.ts +2 -2
- package/dist/packages/react/src/FloatingMenu.d.ts +6 -3
- package/dist/packages/react/src/NodeViewContent.d.ts +1 -1
- package/dist/packages/react/src/NodeViewWrapper.d.ts +1 -1
- package/dist/packages/react/src/ReactNodeViewRenderer.d.ts +12 -6
- package/dist/packages/react/src/ReactRenderer.d.ts +12 -9
- package/dist/packages/react/src/index.d.ts +6 -6
- package/dist/packages/react/src/useEditor.d.ts +2 -1
- package/dist/packages/react/src/useReactNodeView.d.ts +1 -0
- package/dist/tiptap-react.cjs.js +224 -151
- package/dist/tiptap-react.cjs.js.map +1 -1
- package/dist/tiptap-react.esm.js +224 -149
- package/dist/tiptap-react.esm.js.map +1 -1
- package/dist/tiptap-react.umd.js +226 -153
- package/dist/tiptap-react.umd.js.map +1 -1
- package/package.json +18 -12
- package/src/BubbleMenu.tsx +33 -17
- package/src/Editor.ts +2 -1
- package/src/EditorContent.tsx +9 -7
- package/src/FloatingMenu.tsx +37 -14
- package/src/NodeViewContent.tsx +10 -3
- package/src/NodeViewWrapper.tsx +11 -8
- package/src/ReactNodeViewRenderer.tsx +75 -31
- package/src/ReactRenderer.tsx +58 -25
- package/src/index.ts +6 -6
- package/src/useEditor.ts +16 -4
- package/src/useReactNodeView.ts +1 -0
- package/CHANGELOG.md +0 -178
- package/LICENSE.md +0 -21
- package/dist/tiptap-react.bundle.umd.min.js +0 -54
- package/dist/tiptap-react.bundle.umd.min.js.map +0 -1
package/README.md
CHANGED
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
## Introduction
|
|
8
8
|
tiptap is a headless wrapper around [ProseMirror](https://ProseMirror.net) – a toolkit for building rich text WYSIWYG editors, which is already in use at many well-known companies such as *New York Times*, *The Guardian* or *Atlassian*.
|
|
9
9
|
|
|
10
|
-
##
|
|
10
|
+
## Official Documentation
|
|
11
11
|
Documentation can be found on the [tiptap website](https://tiptap.dev).
|
|
12
12
|
|
|
13
13
|
## License
|
|
14
|
-
tiptap is open sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap
|
|
14
|
+
tiptap is open sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap/blob/main/LICENSE.md).
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
1
|
import { BubbleMenuPluginProps } from '@tiptap/extension-bubble-menu';
|
|
3
|
-
|
|
2
|
+
import React from 'react';
|
|
3
|
+
declare type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>;
|
|
4
|
+
export declare type BubbleMenuProps = Omit<Optional<BubbleMenuPluginProps, 'pluginKey'>, 'element'> & {
|
|
4
5
|
className?: string;
|
|
6
|
+
children: React.ReactNode;
|
|
7
|
+
delay?: number;
|
|
5
8
|
};
|
|
6
|
-
export declare const BubbleMenu:
|
|
9
|
+
export declare const BubbleMenu: (props: BubbleMenuProps) => JSX.Element;
|
|
10
|
+
export {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
1
|
import { Editor as CoreEditor } from '@tiptap/core';
|
|
2
|
+
import React from 'react';
|
|
3
3
|
import { EditorContentProps, EditorContentState } from './EditorContent';
|
|
4
4
|
export declare class Editor extends CoreEditor {
|
|
5
5
|
contentComponent: React.Component<EditorContentProps, EditorContentState> | null;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { HTMLProps } from 'react';
|
|
2
2
|
import { Editor } from './Editor';
|
|
3
3
|
import { ReactRenderer } from './ReactRenderer';
|
|
4
|
-
export interface EditorContentProps {
|
|
4
|
+
export interface EditorContentProps extends HTMLProps<HTMLDivElement> {
|
|
5
5
|
editor: Editor | null;
|
|
6
6
|
}
|
|
7
7
|
export interface EditorContentState {
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
1
|
import { FloatingMenuPluginProps } from '@tiptap/extension-floating-menu';
|
|
3
|
-
|
|
2
|
+
import React from 'react';
|
|
3
|
+
declare type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>;
|
|
4
|
+
export declare type FloatingMenuProps = Omit<Optional<FloatingMenuPluginProps, 'pluginKey'>, 'element'> & {
|
|
4
5
|
className?: string;
|
|
6
|
+
children: React.ReactNode;
|
|
5
7
|
};
|
|
6
|
-
export declare const FloatingMenu:
|
|
8
|
+
export declare const FloatingMenu: (props: FloatingMenuProps) => JSX.Element;
|
|
9
|
+
export {};
|
|
@@ -1,9 +1,15 @@
|
|
|
1
|
-
import { NodeViewRenderer } from '@tiptap/core';
|
|
2
|
-
import { Decoration } from 'prosemirror-view';
|
|
1
|
+
import { NodeViewRenderer, NodeViewRendererOptions } from '@tiptap/core';
|
|
3
2
|
import { Node as ProseMirrorNode } from 'prosemirror-model';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
update: ((
|
|
3
|
+
import { Decoration } from 'prosemirror-view';
|
|
4
|
+
export interface ReactNodeViewRendererOptions extends NodeViewRendererOptions {
|
|
5
|
+
update: ((props: {
|
|
6
|
+
oldNode: ProseMirrorNode;
|
|
7
|
+
oldDecorations: Decoration[];
|
|
8
|
+
newNode: ProseMirrorNode;
|
|
9
|
+
newDecorations: Decoration[];
|
|
10
|
+
updateProps: () => void;
|
|
11
|
+
}) => boolean) | null;
|
|
12
|
+
as?: string;
|
|
13
|
+
className?: string;
|
|
7
14
|
}
|
|
8
15
|
export declare function ReactNodeViewRenderer(component: any, options?: Partial<ReactNodeViewRendererOptions>): NodeViewRenderer;
|
|
9
|
-
export {};
|
|
@@ -1,21 +1,24 @@
|
|
|
1
|
+
import { Editor } from '@tiptap/core';
|
|
1
2
|
import React from 'react';
|
|
2
|
-
import {
|
|
3
|
-
import { Editor } from './Editor';
|
|
3
|
+
import { Editor as ExtendedEditor } from './Editor';
|
|
4
4
|
export interface ReactRendererOptions {
|
|
5
5
|
editor: Editor;
|
|
6
|
-
props?:
|
|
6
|
+
props?: Record<string, any>;
|
|
7
7
|
as?: string;
|
|
8
|
+
className?: string;
|
|
8
9
|
}
|
|
9
|
-
|
|
10
|
+
declare type ComponentType<R, P> = React.ComponentClass<P> | React.FunctionComponent<P> | React.ForwardRefExoticComponent<React.PropsWithoutRef<P> & React.RefAttributes<R>>;
|
|
11
|
+
export declare class ReactRenderer<R = unknown, P = unknown> {
|
|
10
12
|
id: string;
|
|
11
|
-
editor:
|
|
13
|
+
editor: ExtendedEditor;
|
|
12
14
|
component: any;
|
|
13
15
|
element: Element;
|
|
14
|
-
props:
|
|
16
|
+
props: Record<string, any>;
|
|
15
17
|
reactElement: React.ReactNode;
|
|
16
|
-
ref:
|
|
17
|
-
constructor(component:
|
|
18
|
+
ref: R | null;
|
|
19
|
+
constructor(component: ComponentType<R, P>, { editor, props, as, className, }: ReactRendererOptions);
|
|
18
20
|
render(): void;
|
|
19
|
-
updateProps(props?:
|
|
21
|
+
updateProps(props?: Record<string, any>): void;
|
|
20
22
|
destroy(): void;
|
|
21
23
|
}
|
|
24
|
+
export {};
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
export * from '@tiptap/core';
|
|
2
1
|
export * from './BubbleMenu';
|
|
3
2
|
export { Editor } from './Editor';
|
|
4
|
-
export * from './FloatingMenu';
|
|
5
|
-
export * from './useEditor';
|
|
6
|
-
export * from './ReactRenderer';
|
|
7
|
-
export * from './ReactNodeViewRenderer';
|
|
8
3
|
export * from './EditorContent';
|
|
9
|
-
export * from './
|
|
4
|
+
export * from './FloatingMenu';
|
|
10
5
|
export * from './NodeViewContent';
|
|
6
|
+
export * from './NodeViewWrapper';
|
|
7
|
+
export * from './ReactNodeViewRenderer';
|
|
8
|
+
export * from './ReactRenderer';
|
|
9
|
+
export * from './useEditor';
|
|
10
|
+
export * from '@tiptap/core';
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import { EditorOptions } from '@tiptap/core';
|
|
2
|
+
import { DependencyList } from 'react';
|
|
2
3
|
import { Editor } from './Editor';
|
|
3
|
-
export declare const useEditor: (options?: Partial<EditorOptions
|
|
4
|
+
export declare const useEditor: (options?: Partial<EditorOptions>, deps?: DependencyList) => Editor | null;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
2
|
export interface ReactNodeViewContextProps {
|
|
3
3
|
onDragStart: (event: DragEvent) => void;
|
|
4
|
+
nodeViewContentRef: (element: HTMLElement | null) => void;
|
|
4
5
|
}
|
|
5
6
|
export declare const ReactNodeViewContext: import("react").Context<Partial<ReactNodeViewContextProps>>;
|
|
6
7
|
export declare const useReactNodeView: () => Partial<ReactNodeViewContextProps>;
|
package/dist/tiptap-react.cjs.js
CHANGED
|
@@ -2,31 +2,39 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
-
var core = require('@tiptap/core');
|
|
6
|
-
var React = require('react');
|
|
7
5
|
var extensionBubbleMenu = require('@tiptap/extension-bubble-menu');
|
|
8
|
-
var
|
|
6
|
+
var React = require('react');
|
|
7
|
+
var core = require('@tiptap/core');
|
|
9
8
|
var ReactDOM = require('react-dom');
|
|
9
|
+
var extensionFloatingMenu = require('@tiptap/extension-floating-menu');
|
|
10
10
|
|
|
11
11
|
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
12
12
|
|
|
13
13
|
var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
|
|
14
14
|
var ReactDOM__default = /*#__PURE__*/_interopDefaultLegacy(ReactDOM);
|
|
15
15
|
|
|
16
|
-
const BubbleMenu = props => {
|
|
17
|
-
const element = React.
|
|
16
|
+
const BubbleMenu = (props) => {
|
|
17
|
+
const [element, setElement] = React.useState(null);
|
|
18
18
|
React.useEffect(() => {
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
if (!element) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
if (props.editor.isDestroyed) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const { pluginKey = 'bubbleMenu', editor, tippyOptions = {}, delay, shouldShow = null, } = props;
|
|
26
|
+
const plugin = extensionBubbleMenu.BubbleMenuPlugin({
|
|
27
|
+
delay,
|
|
21
28
|
editor,
|
|
22
|
-
element
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
element,
|
|
30
|
+
pluginKey,
|
|
31
|
+
shouldShow,
|
|
32
|
+
tippyOptions,
|
|
33
|
+
});
|
|
34
|
+
editor.registerPlugin(plugin);
|
|
35
|
+
return () => editor.unregisterPlugin(pluginKey);
|
|
36
|
+
}, [props.editor, element]);
|
|
37
|
+
return (React__default["default"].createElement("div", { ref: setElement, className: props.className, style: { visibility: 'hidden' } }, props.children));
|
|
30
38
|
};
|
|
31
39
|
|
|
32
40
|
class Editor extends core.Editor {
|
|
@@ -36,46 +44,130 @@ class Editor extends core.Editor {
|
|
|
36
44
|
}
|
|
37
45
|
}
|
|
38
46
|
|
|
39
|
-
const
|
|
40
|
-
|
|
47
|
+
const Portals = ({ renderers }) => {
|
|
48
|
+
return (React__default["default"].createElement(React__default["default"].Fragment, null, Array.from(renderers).map(([key, renderer]) => {
|
|
49
|
+
return ReactDOM__default["default"].createPortal(renderer.reactElement, renderer.element, key);
|
|
50
|
+
})));
|
|
51
|
+
};
|
|
52
|
+
class PureEditorContent extends React__default["default"].Component {
|
|
53
|
+
constructor(props) {
|
|
54
|
+
super(props);
|
|
55
|
+
this.editorContentRef = React__default["default"].createRef();
|
|
56
|
+
this.state = {
|
|
57
|
+
renderers: new Map(),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
componentDidMount() {
|
|
61
|
+
this.init();
|
|
62
|
+
}
|
|
63
|
+
componentDidUpdate() {
|
|
64
|
+
this.init();
|
|
65
|
+
}
|
|
66
|
+
init() {
|
|
67
|
+
const { editor } = this.props;
|
|
68
|
+
if (editor && editor.options.element) {
|
|
69
|
+
if (editor.contentComponent) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const element = this.editorContentRef.current;
|
|
73
|
+
element.append(...editor.options.element.childNodes);
|
|
74
|
+
editor.setOptions({
|
|
75
|
+
element,
|
|
76
|
+
});
|
|
77
|
+
editor.contentComponent = this;
|
|
78
|
+
editor.createNodeViews();
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
componentWillUnmount() {
|
|
82
|
+
const { editor } = this.props;
|
|
83
|
+
if (!editor) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (!editor.isDestroyed) {
|
|
87
|
+
editor.view.setProps({
|
|
88
|
+
nodeViews: {},
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
editor.contentComponent = null;
|
|
92
|
+
if (!editor.options.element.firstChild) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const newElement = document.createElement('div');
|
|
96
|
+
newElement.append(...editor.options.element.childNodes);
|
|
97
|
+
editor.setOptions({
|
|
98
|
+
element: newElement,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
render() {
|
|
102
|
+
const { editor, ...rest } = this.props;
|
|
103
|
+
return (React__default["default"].createElement(React__default["default"].Fragment, null,
|
|
104
|
+
React__default["default"].createElement("div", { ref: this.editorContentRef, ...rest }),
|
|
105
|
+
React__default["default"].createElement(Portals, { renderers: this.state.renderers })));
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
const EditorContent = React__default["default"].memo(PureEditorContent);
|
|
109
|
+
|
|
110
|
+
const FloatingMenu = (props) => {
|
|
111
|
+
const [element, setElement] = React.useState(null);
|
|
41
112
|
React.useEffect(() => {
|
|
42
|
-
|
|
43
|
-
|
|
113
|
+
if (!element) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
if (props.editor.isDestroyed) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
const { pluginKey = 'floatingMenu', editor, tippyOptions = {}, shouldShow = null, } = props;
|
|
120
|
+
const plugin = extensionFloatingMenu.FloatingMenuPlugin({
|
|
121
|
+
pluginKey,
|
|
44
122
|
editor,
|
|
45
|
-
element
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
123
|
+
element,
|
|
124
|
+
tippyOptions,
|
|
125
|
+
shouldShow,
|
|
126
|
+
});
|
|
127
|
+
editor.registerPlugin(plugin);
|
|
128
|
+
return () => editor.unregisterPlugin(pluginKey);
|
|
129
|
+
}, [
|
|
130
|
+
props.editor,
|
|
131
|
+
element,
|
|
132
|
+
]);
|
|
133
|
+
return (React__default["default"].createElement("div", { ref: setElement, className: props.className, style: { visibility: 'hidden' } }, props.children));
|
|
52
134
|
};
|
|
53
135
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
instance.destroy();
|
|
67
|
-
};
|
|
68
|
-
}, []);
|
|
69
|
-
return editor;
|
|
136
|
+
const ReactNodeViewContext = React.createContext({
|
|
137
|
+
onDragStart: undefined,
|
|
138
|
+
});
|
|
139
|
+
const useReactNodeView = () => React.useContext(ReactNodeViewContext);
|
|
140
|
+
|
|
141
|
+
const NodeViewContent = props => {
|
|
142
|
+
const Tag = props.as || 'div';
|
|
143
|
+
const { nodeViewContentRef } = useReactNodeView();
|
|
144
|
+
return (React__default["default"].createElement(Tag, { ...props, ref: nodeViewContentRef, "data-node-view-content": "", style: {
|
|
145
|
+
whiteSpace: 'pre-wrap',
|
|
146
|
+
...props.style,
|
|
147
|
+
} }));
|
|
70
148
|
};
|
|
71
149
|
|
|
150
|
+
const NodeViewWrapper = React__default["default"].forwardRef((props, ref) => {
|
|
151
|
+
const { onDragStart } = useReactNodeView();
|
|
152
|
+
const Tag = props.as || 'div';
|
|
153
|
+
return (React__default["default"].createElement(Tag, { ...props, ref: ref, "data-node-view-wrapper": "", onDragStart: onDragStart, style: {
|
|
154
|
+
whiteSpace: 'normal',
|
|
155
|
+
...props.style,
|
|
156
|
+
} }));
|
|
157
|
+
});
|
|
158
|
+
|
|
72
159
|
function isClassComponent(Component) {
|
|
73
160
|
return !!(typeof Component === 'function'
|
|
74
161
|
&& Component.prototype
|
|
75
162
|
&& Component.prototype.isReactComponent);
|
|
76
163
|
}
|
|
164
|
+
function isForwardRefComponent(Component) {
|
|
165
|
+
var _a;
|
|
166
|
+
return !!(typeof Component === 'object'
|
|
167
|
+
&& ((_a = Component.$$typeof) === null || _a === void 0 ? void 0 : _a.toString()) === 'Symbol(react.forward_ref)');
|
|
168
|
+
}
|
|
77
169
|
class ReactRenderer {
|
|
78
|
-
constructor(component, { editor, props = {}, as = 'div' }) {
|
|
170
|
+
constructor(component, { editor, props = {}, as = 'div', className = '', }) {
|
|
79
171
|
this.ref = null;
|
|
80
172
|
this.id = Math.floor(Math.random() * 0xFFFFFFFF).toString();
|
|
81
173
|
this.component = component;
|
|
@@ -83,23 +175,30 @@ class ReactRenderer {
|
|
|
83
175
|
this.props = props;
|
|
84
176
|
this.element = document.createElement(as);
|
|
85
177
|
this.element.classList.add('react-renderer');
|
|
178
|
+
if (className) {
|
|
179
|
+
this.element.classList.add(...className.split(' '));
|
|
180
|
+
}
|
|
86
181
|
this.render();
|
|
87
182
|
}
|
|
88
183
|
render() {
|
|
89
|
-
var _a;
|
|
90
184
|
const Component = this.component;
|
|
91
185
|
const props = this.props;
|
|
92
|
-
if (isClassComponent(Component)) {
|
|
186
|
+
if (isClassComponent(Component) || isForwardRefComponent(Component)) {
|
|
93
187
|
props.ref = (ref) => {
|
|
94
188
|
this.ref = ref;
|
|
95
189
|
};
|
|
96
190
|
}
|
|
97
|
-
this.reactElement = React__default[
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
191
|
+
this.reactElement = React__default["default"].createElement(Component, { ...props });
|
|
192
|
+
queueMicrotask(() => {
|
|
193
|
+
ReactDOM.flushSync(() => {
|
|
194
|
+
var _a;
|
|
195
|
+
if ((_a = this.editor) === null || _a === void 0 ? void 0 : _a.contentComponent) {
|
|
196
|
+
this.editor.contentComponent.setState({
|
|
197
|
+
renderers: this.editor.contentComponent.state.renderers.set(this.id, this),
|
|
198
|
+
});
|
|
199
|
+
}
|
|
101
200
|
});
|
|
102
|
-
}
|
|
201
|
+
});
|
|
103
202
|
}
|
|
104
203
|
updateProps(props = {}) {
|
|
105
204
|
this.props = {
|
|
@@ -109,22 +208,21 @@ class ReactRenderer {
|
|
|
109
208
|
this.render();
|
|
110
209
|
}
|
|
111
210
|
destroy() {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
211
|
+
queueMicrotask(() => {
|
|
212
|
+
ReactDOM.flushSync(() => {
|
|
213
|
+
var _a;
|
|
214
|
+
if ((_a = this.editor) === null || _a === void 0 ? void 0 : _a.contentComponent) {
|
|
215
|
+
const { renderers } = this.editor.contentComponent.state;
|
|
216
|
+
renderers.delete(this.id);
|
|
217
|
+
this.editor.contentComponent.setState({
|
|
218
|
+
renderers,
|
|
219
|
+
});
|
|
220
|
+
}
|
|
118
221
|
});
|
|
119
|
-
}
|
|
222
|
+
});
|
|
120
223
|
}
|
|
121
224
|
}
|
|
122
225
|
|
|
123
|
-
const ReactNodeViewContext = React.createContext({
|
|
124
|
-
onDragStart: undefined,
|
|
125
|
-
});
|
|
126
|
-
const useReactNodeView = () => React.useContext(ReactNodeViewContext);
|
|
127
|
-
|
|
128
226
|
class ReactNodeView extends core.NodeView {
|
|
129
227
|
mount() {
|
|
130
228
|
const props = {
|
|
@@ -135,6 +233,7 @@ class ReactNodeView extends core.NodeView {
|
|
|
135
233
|
extension: this.extension,
|
|
136
234
|
getPos: () => this.getPos(),
|
|
137
235
|
updateAttributes: (attributes = {}) => this.updateAttributes(attributes),
|
|
236
|
+
deleteNode: () => this.deleteNode(),
|
|
138
237
|
};
|
|
139
238
|
if (!this.component.displayName) {
|
|
140
239
|
const capitalizeFirstChar = (string) => {
|
|
@@ -143,21 +242,36 @@ class ReactNodeView extends core.NodeView {
|
|
|
143
242
|
this.component.displayName = capitalizeFirstChar(this.extension.name);
|
|
144
243
|
}
|
|
145
244
|
const ReactNodeViewProvider = componentProps => {
|
|
146
|
-
const onDragStart = this.onDragStart.bind(this);
|
|
147
245
|
const Component = this.component;
|
|
148
|
-
|
|
149
|
-
|
|
246
|
+
const onDragStart = this.onDragStart.bind(this);
|
|
247
|
+
const nodeViewContentRef = element => {
|
|
248
|
+
if (element && this.contentDOMElement && element.firstChild !== this.contentDOMElement) {
|
|
249
|
+
element.appendChild(this.contentDOMElement);
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
return (React__default["default"].createElement(ReactNodeViewContext.Provider, { value: { onDragStart, nodeViewContentRef } },
|
|
253
|
+
React__default["default"].createElement(Component, { ...componentProps })));
|
|
150
254
|
};
|
|
151
255
|
ReactNodeViewProvider.displayName = 'ReactNodeView';
|
|
152
256
|
this.contentDOMElement = this.node.isLeaf
|
|
153
257
|
? null
|
|
154
258
|
: document.createElement(this.node.isInline ? 'span' : 'div');
|
|
259
|
+
if (this.contentDOMElement) {
|
|
260
|
+
// For some reason the whiteSpace prop is not inherited properly in Chrome and Safari
|
|
261
|
+
// With this fix it seems to work fine
|
|
262
|
+
// See: https://github.com/ueberdosis/tiptap/issues/1197
|
|
263
|
+
this.contentDOMElement.style.whiteSpace = 'inherit';
|
|
264
|
+
}
|
|
265
|
+
let as = this.node.isInline ? 'span' : 'div';
|
|
266
|
+
if (this.options.as) {
|
|
267
|
+
as = this.options.as;
|
|
268
|
+
}
|
|
269
|
+
const { className = '' } = this.options;
|
|
155
270
|
this.renderer = new ReactRenderer(ReactNodeViewProvider, {
|
|
156
271
|
editor: this.editor,
|
|
157
272
|
props,
|
|
158
|
-
as
|
|
159
|
-
|
|
160
|
-
: 'div',
|
|
273
|
+
as,
|
|
274
|
+
className: `node-${this.node.type.name} ${className}`.trim(),
|
|
161
275
|
});
|
|
162
276
|
}
|
|
163
277
|
get dom() {
|
|
@@ -172,27 +286,34 @@ class ReactNodeView extends core.NodeView {
|
|
|
172
286
|
if (this.node.isLeaf) {
|
|
173
287
|
return null;
|
|
174
288
|
}
|
|
175
|
-
const contentElement = this.dom.querySelector('[data-node-view-content]');
|
|
176
|
-
if (this.contentDOMElement
|
|
177
|
-
&& contentElement
|
|
178
|
-
&& !contentElement.contains(this.contentDOMElement)) {
|
|
179
|
-
contentElement.appendChild(this.contentDOMElement);
|
|
180
|
-
}
|
|
181
289
|
return this.contentDOMElement;
|
|
182
290
|
}
|
|
183
291
|
update(node, decorations) {
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
}
|
|
292
|
+
const updateProps = (props) => {
|
|
293
|
+
this.renderer.updateProps(props);
|
|
294
|
+
};
|
|
187
295
|
if (node.type !== this.node.type) {
|
|
188
296
|
return false;
|
|
189
297
|
}
|
|
298
|
+
if (typeof this.options.update === 'function') {
|
|
299
|
+
const oldNode = this.node;
|
|
300
|
+
const oldDecorations = this.decorations;
|
|
301
|
+
this.node = node;
|
|
302
|
+
this.decorations = decorations;
|
|
303
|
+
return this.options.update({
|
|
304
|
+
oldNode,
|
|
305
|
+
oldDecorations,
|
|
306
|
+
newNode: node,
|
|
307
|
+
newDecorations: decorations,
|
|
308
|
+
updateProps: () => updateProps({ node, decorations }),
|
|
309
|
+
});
|
|
310
|
+
}
|
|
190
311
|
if (node === this.node && this.decorations === decorations) {
|
|
191
312
|
return true;
|
|
192
313
|
}
|
|
193
314
|
this.node = node;
|
|
194
315
|
this.decorations = decorations;
|
|
195
|
-
|
|
316
|
+
updateProps({ node, decorations });
|
|
196
317
|
return true;
|
|
197
318
|
}
|
|
198
319
|
selectNode() {
|
|
@@ -222,78 +343,32 @@ function ReactNodeViewRenderer(component, options) {
|
|
|
222
343
|
};
|
|
223
344
|
}
|
|
224
345
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
}
|
|
244
|
-
init() {
|
|
245
|
-
const { editor } = this.props;
|
|
246
|
-
if (editor && editor.options.element) {
|
|
247
|
-
if (editor.contentComponent) {
|
|
248
|
-
return;
|
|
249
|
-
}
|
|
250
|
-
const element = this.editorContentRef.current;
|
|
251
|
-
element.appendChild(editor.options.element.firstChild);
|
|
252
|
-
editor.setOptions({
|
|
253
|
-
element,
|
|
254
|
-
});
|
|
255
|
-
editor.contentComponent = this;
|
|
256
|
-
// TODO: alternative to setTimeout?
|
|
257
|
-
setTimeout(() => editor.createNodeViews(), 0);
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
componentWillUnmount() {
|
|
261
|
-
const { editor } = this.props;
|
|
262
|
-
if (!editor) {
|
|
263
|
-
return;
|
|
264
|
-
}
|
|
265
|
-
if (!editor.isDestroyed) {
|
|
266
|
-
editor.view.setProps({
|
|
267
|
-
nodeViews: {},
|
|
346
|
+
function useForceUpdate() {
|
|
347
|
+
const [, setValue] = React.useState(0);
|
|
348
|
+
return () => setValue(value => value + 1);
|
|
349
|
+
}
|
|
350
|
+
const useEditor = (options = {}, deps = []) => {
|
|
351
|
+
const [editor, setEditor] = React.useState(null);
|
|
352
|
+
const forceUpdate = useForceUpdate();
|
|
353
|
+
React.useEffect(() => {
|
|
354
|
+
let isMounted = true;
|
|
355
|
+
const instance = new Editor(options);
|
|
356
|
+
setEditor(instance);
|
|
357
|
+
instance.on('transaction', () => {
|
|
358
|
+
requestAnimationFrame(() => {
|
|
359
|
+
requestAnimationFrame(() => {
|
|
360
|
+
if (isMounted) {
|
|
361
|
+
forceUpdate();
|
|
362
|
+
}
|
|
363
|
+
});
|
|
268
364
|
});
|
|
269
|
-
}
|
|
270
|
-
editor.contentComponent = null;
|
|
271
|
-
if (!editor.options.element.firstChild) {
|
|
272
|
-
return;
|
|
273
|
-
}
|
|
274
|
-
const newElement = document.createElement('div');
|
|
275
|
-
newElement.appendChild(editor.options.element.firstChild);
|
|
276
|
-
editor.setOptions({
|
|
277
|
-
element: newElement,
|
|
278
365
|
});
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
}
|
|
286
|
-
const EditorContent = React__default['default'].memo(PureEditorContent);
|
|
287
|
-
|
|
288
|
-
const NodeViewWrapper = props => {
|
|
289
|
-
const { onDragStart } = useReactNodeView();
|
|
290
|
-
const Tag = props.as || 'div';
|
|
291
|
-
return (React__default['default'].createElement(Tag, { className: props.className, "data-node-view-wrapper": "", onDragStart: onDragStart, style: { whiteSpace: 'normal' } }, props.children));
|
|
292
|
-
};
|
|
293
|
-
|
|
294
|
-
const NodeViewContent = props => {
|
|
295
|
-
const Tag = props.as || 'div';
|
|
296
|
-
return (React__default['default'].createElement(Tag, { className: props.className, "data-node-view-content": "", style: { whiteSpace: 'pre-wrap' } }));
|
|
366
|
+
return () => {
|
|
367
|
+
instance.destroy();
|
|
368
|
+
isMounted = false;
|
|
369
|
+
};
|
|
370
|
+
}, deps);
|
|
371
|
+
return editor;
|
|
297
372
|
};
|
|
298
373
|
|
|
299
374
|
exports.BubbleMenu = BubbleMenu;
|
|
@@ -309,9 +384,7 @@ exports.useEditor = useEditor;
|
|
|
309
384
|
Object.keys(core).forEach(function (k) {
|
|
310
385
|
if (k !== 'default' && !exports.hasOwnProperty(k)) Object.defineProperty(exports, k, {
|
|
311
386
|
enumerable: true,
|
|
312
|
-
get: function () {
|
|
313
|
-
return core[k];
|
|
314
|
-
}
|
|
387
|
+
get: function () { return core[k]; }
|
|
315
388
|
});
|
|
316
389
|
});
|
|
317
390
|
//# sourceMappingURL=tiptap-react.cjs.js.map
|