@tiptap/react 2.0.0-beta.20 → 2.0.0-beta.200
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 +6 -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 +226 -151
- package/dist/tiptap-react.cjs.js.map +1 -1
- package/dist/tiptap-react.esm.js +226 -149
- package/dist/tiptap-react.esm.js.map +1 -1
- package/dist/tiptap-react.umd.js +228 -153
- package/dist/tiptap-react.umd.js.map +1 -1
- package/package.json +18 -12
- package/src/BubbleMenu.tsx +37 -15
- 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,9 @@
|
|
|
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;
|
|
5
7
|
};
|
|
6
|
-
export declare const BubbleMenu:
|
|
8
|
+
export declare const BubbleMenu: (props: BubbleMenuProps) => JSX.Element;
|
|
9
|
+
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,41 @@
|
|
|
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 = {}, shouldShow = null, } = props;
|
|
26
|
+
const plugin = extensionBubbleMenu.BubbleMenuPlugin({
|
|
27
|
+
pluginKey,
|
|
21
28
|
editor,
|
|
22
|
-
element
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}, [
|
|
29
|
-
|
|
29
|
+
element,
|
|
30
|
+
tippyOptions,
|
|
31
|
+
shouldShow,
|
|
32
|
+
});
|
|
33
|
+
editor.registerPlugin(plugin);
|
|
34
|
+
return () => editor.unregisterPlugin(pluginKey);
|
|
35
|
+
}, [
|
|
36
|
+
props.editor,
|
|
37
|
+
element,
|
|
38
|
+
]);
|
|
39
|
+
return (React__default["default"].createElement("div", { ref: setElement, className: props.className, style: { visibility: 'hidden' } }, props.children));
|
|
30
40
|
};
|
|
31
41
|
|
|
32
42
|
class Editor extends core.Editor {
|
|
@@ -36,46 +46,130 @@ class Editor extends core.Editor {
|
|
|
36
46
|
}
|
|
37
47
|
}
|
|
38
48
|
|
|
39
|
-
const
|
|
40
|
-
|
|
49
|
+
const Portals = ({ renderers }) => {
|
|
50
|
+
return (React__default["default"].createElement(React__default["default"].Fragment, null, Array.from(renderers).map(([key, renderer]) => {
|
|
51
|
+
return ReactDOM__default["default"].createPortal(renderer.reactElement, renderer.element, key);
|
|
52
|
+
})));
|
|
53
|
+
};
|
|
54
|
+
class PureEditorContent extends React__default["default"].Component {
|
|
55
|
+
constructor(props) {
|
|
56
|
+
super(props);
|
|
57
|
+
this.editorContentRef = React__default["default"].createRef();
|
|
58
|
+
this.state = {
|
|
59
|
+
renderers: new Map(),
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
componentDidMount() {
|
|
63
|
+
this.init();
|
|
64
|
+
}
|
|
65
|
+
componentDidUpdate() {
|
|
66
|
+
this.init();
|
|
67
|
+
}
|
|
68
|
+
init() {
|
|
69
|
+
const { editor } = this.props;
|
|
70
|
+
if (editor && editor.options.element) {
|
|
71
|
+
if (editor.contentComponent) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const element = this.editorContentRef.current;
|
|
75
|
+
element.append(...editor.options.element.childNodes);
|
|
76
|
+
editor.setOptions({
|
|
77
|
+
element,
|
|
78
|
+
});
|
|
79
|
+
editor.contentComponent = this;
|
|
80
|
+
editor.createNodeViews();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
componentWillUnmount() {
|
|
84
|
+
const { editor } = this.props;
|
|
85
|
+
if (!editor) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (!editor.isDestroyed) {
|
|
89
|
+
editor.view.setProps({
|
|
90
|
+
nodeViews: {},
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
editor.contentComponent = null;
|
|
94
|
+
if (!editor.options.element.firstChild) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
const newElement = document.createElement('div');
|
|
98
|
+
newElement.append(...editor.options.element.childNodes);
|
|
99
|
+
editor.setOptions({
|
|
100
|
+
element: newElement,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
render() {
|
|
104
|
+
const { editor, ...rest } = this.props;
|
|
105
|
+
return (React__default["default"].createElement(React__default["default"].Fragment, null,
|
|
106
|
+
React__default["default"].createElement("div", { ref: this.editorContentRef, ...rest }),
|
|
107
|
+
React__default["default"].createElement(Portals, { renderers: this.state.renderers })));
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
const EditorContent = React__default["default"].memo(PureEditorContent);
|
|
111
|
+
|
|
112
|
+
const FloatingMenu = (props) => {
|
|
113
|
+
const [element, setElement] = React.useState(null);
|
|
41
114
|
React.useEffect(() => {
|
|
42
|
-
|
|
43
|
-
|
|
115
|
+
if (!element) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
if (props.editor.isDestroyed) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
const { pluginKey = 'floatingMenu', editor, tippyOptions = {}, shouldShow = null, } = props;
|
|
122
|
+
const plugin = extensionFloatingMenu.FloatingMenuPlugin({
|
|
123
|
+
pluginKey,
|
|
44
124
|
editor,
|
|
45
|
-
element
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
125
|
+
element,
|
|
126
|
+
tippyOptions,
|
|
127
|
+
shouldShow,
|
|
128
|
+
});
|
|
129
|
+
editor.registerPlugin(plugin);
|
|
130
|
+
return () => editor.unregisterPlugin(pluginKey);
|
|
131
|
+
}, [
|
|
132
|
+
props.editor,
|
|
133
|
+
element,
|
|
134
|
+
]);
|
|
135
|
+
return (React__default["default"].createElement("div", { ref: setElement, className: props.className, style: { visibility: 'hidden' } }, props.children));
|
|
52
136
|
};
|
|
53
137
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
instance.destroy();
|
|
67
|
-
};
|
|
68
|
-
}, []);
|
|
69
|
-
return editor;
|
|
138
|
+
const ReactNodeViewContext = React.createContext({
|
|
139
|
+
onDragStart: undefined,
|
|
140
|
+
});
|
|
141
|
+
const useReactNodeView = () => React.useContext(ReactNodeViewContext);
|
|
142
|
+
|
|
143
|
+
const NodeViewContent = props => {
|
|
144
|
+
const Tag = props.as || 'div';
|
|
145
|
+
const { nodeViewContentRef } = useReactNodeView();
|
|
146
|
+
return (React__default["default"].createElement(Tag, { ...props, ref: nodeViewContentRef, "data-node-view-content": "", style: {
|
|
147
|
+
whiteSpace: 'pre-wrap',
|
|
148
|
+
...props.style,
|
|
149
|
+
} }));
|
|
70
150
|
};
|
|
71
151
|
|
|
152
|
+
const NodeViewWrapper = React__default["default"].forwardRef((props, ref) => {
|
|
153
|
+
const { onDragStart } = useReactNodeView();
|
|
154
|
+
const Tag = props.as || 'div';
|
|
155
|
+
return (React__default["default"].createElement(Tag, { ...props, ref: ref, "data-node-view-wrapper": "", onDragStart: onDragStart, style: {
|
|
156
|
+
whiteSpace: 'normal',
|
|
157
|
+
...props.style,
|
|
158
|
+
} }));
|
|
159
|
+
});
|
|
160
|
+
|
|
72
161
|
function isClassComponent(Component) {
|
|
73
162
|
return !!(typeof Component === 'function'
|
|
74
163
|
&& Component.prototype
|
|
75
164
|
&& Component.prototype.isReactComponent);
|
|
76
165
|
}
|
|
166
|
+
function isForwardRefComponent(Component) {
|
|
167
|
+
var _a;
|
|
168
|
+
return !!(typeof Component === 'object'
|
|
169
|
+
&& ((_a = Component.$$typeof) === null || _a === void 0 ? void 0 : _a.toString()) === 'Symbol(react.forward_ref)');
|
|
170
|
+
}
|
|
77
171
|
class ReactRenderer {
|
|
78
|
-
constructor(component, { editor, props = {}, as = 'div' }) {
|
|
172
|
+
constructor(component, { editor, props = {}, as = 'div', className = '', }) {
|
|
79
173
|
this.ref = null;
|
|
80
174
|
this.id = Math.floor(Math.random() * 0xFFFFFFFF).toString();
|
|
81
175
|
this.component = component;
|
|
@@ -83,23 +177,30 @@ class ReactRenderer {
|
|
|
83
177
|
this.props = props;
|
|
84
178
|
this.element = document.createElement(as);
|
|
85
179
|
this.element.classList.add('react-renderer');
|
|
180
|
+
if (className) {
|
|
181
|
+
this.element.classList.add(...className.split(' '));
|
|
182
|
+
}
|
|
86
183
|
this.render();
|
|
87
184
|
}
|
|
88
185
|
render() {
|
|
89
|
-
var _a;
|
|
90
186
|
const Component = this.component;
|
|
91
187
|
const props = this.props;
|
|
92
|
-
if (isClassComponent(Component)) {
|
|
188
|
+
if (isClassComponent(Component) || isForwardRefComponent(Component)) {
|
|
93
189
|
props.ref = (ref) => {
|
|
94
190
|
this.ref = ref;
|
|
95
191
|
};
|
|
96
192
|
}
|
|
97
|
-
this.reactElement = React__default[
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
193
|
+
this.reactElement = React__default["default"].createElement(Component, { ...props });
|
|
194
|
+
queueMicrotask(() => {
|
|
195
|
+
ReactDOM.flushSync(() => {
|
|
196
|
+
var _a;
|
|
197
|
+
if ((_a = this.editor) === null || _a === void 0 ? void 0 : _a.contentComponent) {
|
|
198
|
+
this.editor.contentComponent.setState({
|
|
199
|
+
renderers: this.editor.contentComponent.state.renderers.set(this.id, this),
|
|
200
|
+
});
|
|
201
|
+
}
|
|
101
202
|
});
|
|
102
|
-
}
|
|
203
|
+
});
|
|
103
204
|
}
|
|
104
205
|
updateProps(props = {}) {
|
|
105
206
|
this.props = {
|
|
@@ -109,22 +210,21 @@ class ReactRenderer {
|
|
|
109
210
|
this.render();
|
|
110
211
|
}
|
|
111
212
|
destroy() {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
213
|
+
queueMicrotask(() => {
|
|
214
|
+
ReactDOM.flushSync(() => {
|
|
215
|
+
var _a;
|
|
216
|
+
if ((_a = this.editor) === null || _a === void 0 ? void 0 : _a.contentComponent) {
|
|
217
|
+
const { renderers } = this.editor.contentComponent.state;
|
|
218
|
+
renderers.delete(this.id);
|
|
219
|
+
this.editor.contentComponent.setState({
|
|
220
|
+
renderers,
|
|
221
|
+
});
|
|
222
|
+
}
|
|
118
223
|
});
|
|
119
|
-
}
|
|
224
|
+
});
|
|
120
225
|
}
|
|
121
226
|
}
|
|
122
227
|
|
|
123
|
-
const ReactNodeViewContext = React.createContext({
|
|
124
|
-
onDragStart: undefined,
|
|
125
|
-
});
|
|
126
|
-
const useReactNodeView = () => React.useContext(ReactNodeViewContext);
|
|
127
|
-
|
|
128
228
|
class ReactNodeView extends core.NodeView {
|
|
129
229
|
mount() {
|
|
130
230
|
const props = {
|
|
@@ -135,6 +235,7 @@ class ReactNodeView extends core.NodeView {
|
|
|
135
235
|
extension: this.extension,
|
|
136
236
|
getPos: () => this.getPos(),
|
|
137
237
|
updateAttributes: (attributes = {}) => this.updateAttributes(attributes),
|
|
238
|
+
deleteNode: () => this.deleteNode(),
|
|
138
239
|
};
|
|
139
240
|
if (!this.component.displayName) {
|
|
140
241
|
const capitalizeFirstChar = (string) => {
|
|
@@ -143,21 +244,36 @@ class ReactNodeView extends core.NodeView {
|
|
|
143
244
|
this.component.displayName = capitalizeFirstChar(this.extension.name);
|
|
144
245
|
}
|
|
145
246
|
const ReactNodeViewProvider = componentProps => {
|
|
146
|
-
const onDragStart = this.onDragStart.bind(this);
|
|
147
247
|
const Component = this.component;
|
|
148
|
-
|
|
149
|
-
|
|
248
|
+
const onDragStart = this.onDragStart.bind(this);
|
|
249
|
+
const nodeViewContentRef = element => {
|
|
250
|
+
if (element && this.contentDOMElement && element.firstChild !== this.contentDOMElement) {
|
|
251
|
+
element.appendChild(this.contentDOMElement);
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
return (React__default["default"].createElement(ReactNodeViewContext.Provider, { value: { onDragStart, nodeViewContentRef } },
|
|
255
|
+
React__default["default"].createElement(Component, { ...componentProps })));
|
|
150
256
|
};
|
|
151
257
|
ReactNodeViewProvider.displayName = 'ReactNodeView';
|
|
152
258
|
this.contentDOMElement = this.node.isLeaf
|
|
153
259
|
? null
|
|
154
260
|
: document.createElement(this.node.isInline ? 'span' : 'div');
|
|
261
|
+
if (this.contentDOMElement) {
|
|
262
|
+
// For some reason the whiteSpace prop is not inherited properly in Chrome and Safari
|
|
263
|
+
// With this fix it seems to work fine
|
|
264
|
+
// See: https://github.com/ueberdosis/tiptap/issues/1197
|
|
265
|
+
this.contentDOMElement.style.whiteSpace = 'inherit';
|
|
266
|
+
}
|
|
267
|
+
let as = this.node.isInline ? 'span' : 'div';
|
|
268
|
+
if (this.options.as) {
|
|
269
|
+
as = this.options.as;
|
|
270
|
+
}
|
|
271
|
+
const { className = '' } = this.options;
|
|
155
272
|
this.renderer = new ReactRenderer(ReactNodeViewProvider, {
|
|
156
273
|
editor: this.editor,
|
|
157
274
|
props,
|
|
158
|
-
as
|
|
159
|
-
|
|
160
|
-
: 'div',
|
|
275
|
+
as,
|
|
276
|
+
className: `node-${this.node.type.name} ${className}`.trim(),
|
|
161
277
|
});
|
|
162
278
|
}
|
|
163
279
|
get dom() {
|
|
@@ -172,27 +288,34 @@ class ReactNodeView extends core.NodeView {
|
|
|
172
288
|
if (this.node.isLeaf) {
|
|
173
289
|
return null;
|
|
174
290
|
}
|
|
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
291
|
return this.contentDOMElement;
|
|
182
292
|
}
|
|
183
293
|
update(node, decorations) {
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
}
|
|
294
|
+
const updateProps = (props) => {
|
|
295
|
+
this.renderer.updateProps(props);
|
|
296
|
+
};
|
|
187
297
|
if (node.type !== this.node.type) {
|
|
188
298
|
return false;
|
|
189
299
|
}
|
|
300
|
+
if (typeof this.options.update === 'function') {
|
|
301
|
+
const oldNode = this.node;
|
|
302
|
+
const oldDecorations = this.decorations;
|
|
303
|
+
this.node = node;
|
|
304
|
+
this.decorations = decorations;
|
|
305
|
+
return this.options.update({
|
|
306
|
+
oldNode,
|
|
307
|
+
oldDecorations,
|
|
308
|
+
newNode: node,
|
|
309
|
+
newDecorations: decorations,
|
|
310
|
+
updateProps: () => updateProps({ node, decorations }),
|
|
311
|
+
});
|
|
312
|
+
}
|
|
190
313
|
if (node === this.node && this.decorations === decorations) {
|
|
191
314
|
return true;
|
|
192
315
|
}
|
|
193
316
|
this.node = node;
|
|
194
317
|
this.decorations = decorations;
|
|
195
|
-
|
|
318
|
+
updateProps({ node, decorations });
|
|
196
319
|
return true;
|
|
197
320
|
}
|
|
198
321
|
selectNode() {
|
|
@@ -222,78 +345,32 @@ function ReactNodeViewRenderer(component, options) {
|
|
|
222
345
|
};
|
|
223
346
|
}
|
|
224
347
|
|
|
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: {},
|
|
348
|
+
function useForceUpdate() {
|
|
349
|
+
const [, setValue] = React.useState(0);
|
|
350
|
+
return () => setValue(value => value + 1);
|
|
351
|
+
}
|
|
352
|
+
const useEditor = (options = {}, deps = []) => {
|
|
353
|
+
const [editor, setEditor] = React.useState(null);
|
|
354
|
+
const forceUpdate = useForceUpdate();
|
|
355
|
+
React.useEffect(() => {
|
|
356
|
+
let isMounted = true;
|
|
357
|
+
const instance = new Editor(options);
|
|
358
|
+
setEditor(instance);
|
|
359
|
+
instance.on('transaction', () => {
|
|
360
|
+
requestAnimationFrame(() => {
|
|
361
|
+
requestAnimationFrame(() => {
|
|
362
|
+
if (isMounted) {
|
|
363
|
+
forceUpdate();
|
|
364
|
+
}
|
|
365
|
+
});
|
|
268
366
|
});
|
|
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
367
|
});
|
|
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' } }));
|
|
368
|
+
return () => {
|
|
369
|
+
instance.destroy();
|
|
370
|
+
isMounted = false;
|
|
371
|
+
};
|
|
372
|
+
}, deps);
|
|
373
|
+
return editor;
|
|
297
374
|
};
|
|
298
375
|
|
|
299
376
|
exports.BubbleMenu = BubbleMenu;
|
|
@@ -309,9 +386,7 @@ exports.useEditor = useEditor;
|
|
|
309
386
|
Object.keys(core).forEach(function (k) {
|
|
310
387
|
if (k !== 'default' && !exports.hasOwnProperty(k)) Object.defineProperty(exports, k, {
|
|
311
388
|
enumerable: true,
|
|
312
|
-
get: function () {
|
|
313
|
-
return core[k];
|
|
314
|
-
}
|
|
389
|
+
get: function () { return core[k]; }
|
|
315
390
|
});
|
|
316
391
|
});
|
|
317
392
|
//# sourceMappingURL=tiptap-react.cjs.js.map
|