@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.
Files changed (34) hide show
  1. package/README.md +2 -2
  2. package/dist/packages/react/src/BubbleMenu.d.ts +7 -3
  3. package/dist/packages/react/src/Editor.d.ts +1 -1
  4. package/dist/packages/react/src/EditorContent.d.ts +2 -2
  5. package/dist/packages/react/src/FloatingMenu.d.ts +6 -3
  6. package/dist/packages/react/src/NodeViewContent.d.ts +1 -1
  7. package/dist/packages/react/src/NodeViewWrapper.d.ts +1 -1
  8. package/dist/packages/react/src/ReactNodeViewRenderer.d.ts +12 -6
  9. package/dist/packages/react/src/ReactRenderer.d.ts +12 -9
  10. package/dist/packages/react/src/index.d.ts +6 -6
  11. package/dist/packages/react/src/useEditor.d.ts +2 -1
  12. package/dist/packages/react/src/useReactNodeView.d.ts +1 -0
  13. package/dist/tiptap-react.cjs.js +224 -151
  14. package/dist/tiptap-react.cjs.js.map +1 -1
  15. package/dist/tiptap-react.esm.js +224 -149
  16. package/dist/tiptap-react.esm.js.map +1 -1
  17. package/dist/tiptap-react.umd.js +226 -153
  18. package/dist/tiptap-react.umd.js.map +1 -1
  19. package/package.json +18 -12
  20. package/src/BubbleMenu.tsx +33 -17
  21. package/src/Editor.ts +2 -1
  22. package/src/EditorContent.tsx +9 -7
  23. package/src/FloatingMenu.tsx +37 -14
  24. package/src/NodeViewContent.tsx +10 -3
  25. package/src/NodeViewWrapper.tsx +11 -8
  26. package/src/ReactNodeViewRenderer.tsx +75 -31
  27. package/src/ReactRenderer.tsx +58 -25
  28. package/src/index.ts +6 -6
  29. package/src/useEditor.ts +16 -4
  30. package/src/useReactNodeView.ts +1 -0
  31. package/CHANGELOG.md +0 -178
  32. package/LICENSE.md +0 -21
  33. package/dist/tiptap-react.bundle.umd.min.js +0 -54
  34. 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
- ## Offical Documentation
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-next/blob/main/LICENSE.md).
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
- export declare type BubbleMenuProps = Omit<BubbleMenuPluginProps, 'element'> & {
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: React.FC<BubbleMenuProps>;
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
- export declare type FloatingMenuProps = Omit<FloatingMenuPluginProps, 'element'> & {
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: React.FC<FloatingMenuProps>;
8
+ export declare const FloatingMenu: (props: FloatingMenuProps) => JSX.Element;
9
+ export {};
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  export interface NodeViewContentProps {
3
- className?: string;
3
+ [key: string]: any;
4
4
  as?: React.ElementType;
5
5
  }
6
6
  export declare const NodeViewContent: React.FC<NodeViewContentProps>;
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  export interface NodeViewWrapperProps {
3
- className?: string;
3
+ [key: string]: any;
4
4
  as?: React.ElementType;
5
5
  }
6
6
  export declare const NodeViewWrapper: React.FC<NodeViewWrapperProps>;
@@ -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
- interface ReactNodeViewRendererOptions {
5
- stopEvent: ((event: Event) => boolean) | null;
6
- update: ((node: ProseMirrorNode, decorations: Decoration[]) => boolean) | null;
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 { AnyObject } from '@tiptap/core';
3
- import { Editor } from './Editor';
3
+ import { Editor as ExtendedEditor } from './Editor';
4
4
  export interface ReactRendererOptions {
5
5
  editor: Editor;
6
- props?: AnyObject;
6
+ props?: Record<string, any>;
7
7
  as?: string;
8
+ className?: string;
8
9
  }
9
- export declare class ReactRenderer {
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: Editor;
13
+ editor: ExtendedEditor;
12
14
  component: any;
13
15
  element: Element;
14
- props: AnyObject;
16
+ props: Record<string, any>;
15
17
  reactElement: React.ReactNode;
16
- ref: React.Component | null;
17
- constructor(component: React.Component | React.FunctionComponent, { editor, props, as }: ReactRendererOptions);
18
+ ref: R | null;
19
+ constructor(component: ComponentType<R, P>, { editor, props, as, className, }: ReactRendererOptions);
18
20
  render(): void;
19
- updateProps(props?: AnyObject): void;
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 './NodeViewWrapper';
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>) => Editor | null;
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>;
@@ -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 extensionFloatingMenu = require('@tiptap/extension-floating-menu');
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.useRef(null);
16
+ const BubbleMenu = (props) => {
17
+ const [element, setElement] = React.useState(null);
18
18
  React.useEffect(() => {
19
- const { editor, keepInBounds = true } = props;
20
- editor.registerPlugin(extensionBubbleMenu.BubbleMenuPlugin({
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: element.current,
23
- keepInBounds,
24
- }));
25
- return () => {
26
- editor.unregisterPlugin(extensionBubbleMenu.BubbleMenuPluginKey);
27
- };
28
- }, []);
29
- return (React__default['default'].createElement("div", { ref: element, className: props.className, style: { visibility: 'hidden' } }, props.children));
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 FloatingMenu = props => {
40
- const element = React.useRef(null);
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
- const { editor } = props;
43
- editor.registerPlugin(extensionFloatingMenu.FloatingMenuPlugin({
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: element.current,
46
- }));
47
- return () => {
48
- editor.unregisterPlugin(extensionFloatingMenu.FloatingMenuPluginKey);
49
- };
50
- }, []);
51
- return (React__default['default'].createElement("div", { ref: element, className: props.className, style: { visibility: 'hidden' } }, props.children));
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
- function useForceUpdate() {
55
- const [, setValue] = React.useState(0);
56
- return () => setValue(value => value + 1);
57
- }
58
- const useEditor = (options = {}) => {
59
- const [editor, setEditor] = React.useState(null);
60
- const forceUpdate = useForceUpdate();
61
- React.useEffect(() => {
62
- const instance = new Editor(options);
63
- setEditor(instance);
64
- instance.on('transaction', forceUpdate);
65
- return () => {
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['default'].createElement(Component, Object.assign({}, props));
98
- if ((_a = this.editor) === null || _a === void 0 ? void 0 : _a.contentComponent) {
99
- this.editor.contentComponent.setState({
100
- renderers: this.editor.contentComponent.state.renderers.set(this.id, this),
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
- var _a;
113
- if ((_a = this.editor) === null || _a === void 0 ? void 0 : _a.contentComponent) {
114
- const { renderers } = this.editor.contentComponent.state;
115
- renderers.delete(this.id);
116
- this.editor.contentComponent.setState({
117
- renderers,
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
- return (React__default['default'].createElement(ReactNodeViewContext.Provider, { value: { onDragStart } },
149
- React__default['default'].createElement(Component, Object.assign({}, componentProps))));
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: this.node.isInline
159
- ? 'span'
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
- if (typeof this.options.update === 'function') {
185
- return this.options.update(node, decorations);
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
- this.renderer.updateProps({ node, decorations });
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
- const Portals = ({ renderers }) => {
226
- return (React__default['default'].createElement(React__default['default'].Fragment, null, Array.from(renderers).map(([key, renderer]) => {
227
- return ReactDOM__default['default'].createPortal(renderer.reactElement, renderer.element, key);
228
- })));
229
- };
230
- class PureEditorContent extends React__default['default'].Component {
231
- constructor(props) {
232
- super(props);
233
- this.editorContentRef = React__default['default'].createRef();
234
- this.state = {
235
- renderers: new Map(),
236
- };
237
- }
238
- componentDidMount() {
239
- this.init();
240
- }
241
- componentDidUpdate() {
242
- this.init();
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
- render() {
281
- return (React__default['default'].createElement(React__default['default'].Fragment, null,
282
- React__default['default'].createElement("div", { ref: this.editorContentRef }),
283
- React__default['default'].createElement(Portals, { renderers: this.state.renderers })));
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