@tiptap/react 2.5.9 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.umd.js CHANGED
@@ -4,127 +4,6 @@
4
4
  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["@tiptap/react"] = {}, global.extensionBubbleMenu, global.React, global.ReactDOM, global.core, global.extensionFloatingMenu));
5
5
  })(this, (function (exports, extensionBubbleMenu, React, ReactDOM, core, extensionFloatingMenu) { 'use strict';
6
6
 
7
- const mergeRefs = (...refs) => {
8
- return (node) => {
9
- refs.forEach(ref => {
10
- if (typeof ref === 'function') {
11
- ref(node);
12
- }
13
- else if (ref) {
14
- ref.current = node;
15
- }
16
- });
17
- };
18
- };
19
- const Portals = ({ renderers }) => {
20
- return (React.createElement(React.Fragment, null, Object.entries(renderers).map(([key, renderer]) => {
21
- return ReactDOM.createPortal(renderer.reactElement, renderer.element, key);
22
- })));
23
- };
24
- class PureEditorContent extends React.Component {
25
- constructor(props) {
26
- super(props);
27
- this.editorContentRef = React.createRef();
28
- this.initialized = false;
29
- this.state = {
30
- renderers: {},
31
- };
32
- }
33
- componentDidMount() {
34
- this.init();
35
- }
36
- componentDidUpdate() {
37
- this.init();
38
- }
39
- init() {
40
- const { editor } = this.props;
41
- if (editor && !editor.isDestroyed && editor.options.element) {
42
- if (editor.contentComponent) {
43
- return;
44
- }
45
- const element = this.editorContentRef.current;
46
- element.append(...editor.options.element.childNodes);
47
- editor.setOptions({
48
- element,
49
- });
50
- editor.contentComponent = this;
51
- editor.createNodeViews();
52
- this.initialized = true;
53
- }
54
- }
55
- maybeFlushSync(fn) {
56
- // Avoid calling flushSync until the editor is initialized.
57
- // Initialization happens during the componentDidMount or componentDidUpdate
58
- // lifecycle methods, and React doesn't allow calling flushSync from inside
59
- // a lifecycle method.
60
- if (this.initialized) {
61
- ReactDOM.flushSync(fn);
62
- }
63
- else {
64
- fn();
65
- }
66
- }
67
- setRenderer(id, renderer) {
68
- this.maybeFlushSync(() => {
69
- this.setState(({ renderers }) => ({
70
- renderers: {
71
- ...renderers,
72
- [id]: renderer,
73
- },
74
- }));
75
- });
76
- }
77
- removeRenderer(id) {
78
- this.maybeFlushSync(() => {
79
- this.setState(({ renderers }) => {
80
- const nextRenderers = { ...renderers };
81
- delete nextRenderers[id];
82
- return { renderers: nextRenderers };
83
- });
84
- });
85
- }
86
- componentWillUnmount() {
87
- const { editor } = this.props;
88
- if (!editor) {
89
- return;
90
- }
91
- this.initialized = false;
92
- if (!editor.isDestroyed) {
93
- editor.view.setProps({
94
- nodeViews: {},
95
- });
96
- }
97
- editor.contentComponent = null;
98
- if (!editor.options.element.firstChild) {
99
- return;
100
- }
101
- const newElement = document.createElement('div');
102
- newElement.append(...editor.options.element.childNodes);
103
- editor.setOptions({
104
- element: newElement,
105
- });
106
- }
107
- render() {
108
- const { editor, innerRef, ...rest } = this.props;
109
- return (React.createElement(React.Fragment, null,
110
- React.createElement("div", { ref: mergeRefs(innerRef, this.editorContentRef), ...rest }),
111
- React.createElement(Portals, { renderers: this.state.renderers })));
112
- }
113
- }
114
- // EditorContent should be re-created whenever the Editor instance changes
115
- const EditorContentWithKey = React.forwardRef((props, ref) => {
116
- const key = React.useMemo(() => {
117
- return Math.floor(Math.random() * 0xFFFFFFFF).toString();
118
- }, [props.editor]);
119
- // Can't use JSX here because it conflicts with the type definition of Vue's JSX, so use createElement
120
- return React.createElement(PureEditorContent, {
121
- key,
122
- innerRef: ref,
123
- ...props,
124
- });
125
- });
126
- const EditorContent = React.memo(EditorContentWithKey);
127
-
128
7
  var shim = {exports: {}};
129
8
 
130
9
  var useSyncExternalStoreShim_production_min = {};
@@ -403,12 +282,161 @@
403
282
 
404
283
  var shimExports = shim.exports;
405
284
 
406
- class Editor extends core.Editor {
407
- constructor() {
408
- super(...arguments);
409
- this.contentComponent = null;
285
+ const mergeRefs = (...refs) => {
286
+ return (node) => {
287
+ refs.forEach(ref => {
288
+ if (typeof ref === 'function') {
289
+ ref(node);
290
+ }
291
+ else if (ref) {
292
+ ref.current = node;
293
+ }
294
+ });
295
+ };
296
+ };
297
+ /**
298
+ * This component renders all of the editor's node views.
299
+ */
300
+ const Portals = ({ contentComponent, }) => {
301
+ // For performance reasons, we render the node view portals on state changes only
302
+ const renderers = shimExports.useSyncExternalStore(contentComponent.subscribe, contentComponent.getSnapshot, contentComponent.getServerSnapshot);
303
+ // This allows us to directly render the portals without any additional wrapper
304
+ return (React.createElement(React.Fragment, null, Object.values(renderers)));
305
+ };
306
+ function getInstance() {
307
+ const subscribers = new Set();
308
+ let renderers = {};
309
+ return {
310
+ /**
311
+ * Subscribe to the editor instance's changes.
312
+ */
313
+ subscribe(callback) {
314
+ subscribers.add(callback);
315
+ return () => {
316
+ subscribers.delete(callback);
317
+ };
318
+ },
319
+ getSnapshot() {
320
+ return renderers;
321
+ },
322
+ getServerSnapshot() {
323
+ return renderers;
324
+ },
325
+ /**
326
+ * Adds a new NodeView Renderer to the editor.
327
+ */
328
+ setRenderer(id, renderer) {
329
+ renderers = {
330
+ ...renderers,
331
+ [id]: ReactDOM.createPortal(renderer.reactElement, renderer.element, id),
332
+ };
333
+ subscribers.forEach(subscriber => subscriber());
334
+ },
335
+ /**
336
+ * Removes a NodeView Renderer from the editor.
337
+ */
338
+ removeRenderer(id) {
339
+ const nextRenderers = { ...renderers };
340
+ delete nextRenderers[id];
341
+ renderers = nextRenderers;
342
+ subscribers.forEach(subscriber => subscriber());
343
+ },
344
+ };
345
+ }
346
+ class PureEditorContent extends React.Component {
347
+ constructor(props) {
348
+ var _a;
349
+ super(props);
350
+ this.editorContentRef = React.createRef();
351
+ this.initialized = false;
352
+ this.state = {
353
+ hasContentComponentInitialized: Boolean((_a = props.editor) === null || _a === void 0 ? void 0 : _a.contentComponent),
354
+ };
355
+ }
356
+ componentDidMount() {
357
+ this.init();
358
+ }
359
+ componentDidUpdate() {
360
+ this.init();
361
+ }
362
+ init() {
363
+ const { editor } = this.props;
364
+ if (editor && !editor.isDestroyed && editor.options.element) {
365
+ if (editor.contentComponent) {
366
+ return;
367
+ }
368
+ const element = this.editorContentRef.current;
369
+ element.append(...editor.options.element.childNodes);
370
+ editor.setOptions({
371
+ element,
372
+ });
373
+ editor.contentComponent = getInstance();
374
+ // Has the content component been initialized?
375
+ if (!this.state.hasContentComponentInitialized) {
376
+ // Subscribe to the content component
377
+ this.unsubscribeToContentComponent = editor.contentComponent.subscribe(() => {
378
+ this.setState(prevState => {
379
+ if (!prevState.hasContentComponentInitialized) {
380
+ return {
381
+ hasContentComponentInitialized: true,
382
+ };
383
+ }
384
+ return prevState;
385
+ });
386
+ // Unsubscribe to previous content component
387
+ if (this.unsubscribeToContentComponent) {
388
+ this.unsubscribeToContentComponent();
389
+ }
390
+ });
391
+ }
392
+ editor.createNodeViews();
393
+ this.initialized = true;
394
+ }
395
+ }
396
+ componentWillUnmount() {
397
+ const { editor } = this.props;
398
+ if (!editor) {
399
+ return;
400
+ }
401
+ this.initialized = false;
402
+ if (!editor.isDestroyed) {
403
+ editor.view.setProps({
404
+ nodeViews: {},
405
+ });
406
+ }
407
+ if (this.unsubscribeToContentComponent) {
408
+ this.unsubscribeToContentComponent();
409
+ }
410
+ editor.contentComponent = null;
411
+ if (!editor.options.element.firstChild) {
412
+ return;
413
+ }
414
+ const newElement = document.createElement('div');
415
+ newElement.append(...editor.options.element.childNodes);
416
+ editor.setOptions({
417
+ element: newElement,
418
+ });
419
+ }
420
+ render() {
421
+ const { editor, innerRef, ...rest } = this.props;
422
+ return (React.createElement(React.Fragment, null,
423
+ React.createElement("div", { ref: mergeRefs(innerRef, this.editorContentRef), ...rest }),
424
+ (editor === null || editor === void 0 ? void 0 : editor.contentComponent) && React.createElement(Portals, { contentComponent: editor.contentComponent })));
410
425
  }
411
426
  }
427
+ // EditorContent should be re-created whenever the Editor instance changes
428
+ const EditorContentWithKey = React.forwardRef((props, ref) => {
429
+ const key = React.useMemo(() => {
430
+ return Math.floor(Math.random() * 0xffffffff).toString();
431
+ }, [props.editor]);
432
+ // Can't use JSX here because it conflicts with the type definition of Vue's JSX, so use createElement
433
+ return React.createElement(PureEditorContent, {
434
+ key,
435
+ innerRef: ref,
436
+ ...props,
437
+ });
438
+ });
439
+ const EditorContent = React.memo(EditorContentWithKey);
412
440
 
413
441
  var withSelector = {exports: {}};
414
442
 
@@ -768,17 +796,20 @@
768
796
  * Create a new editor instance. And attach event listeners.
769
797
  */
770
798
  createEditor() {
771
- const editor = new Editor(this.options.current);
772
- // Always call the most recent version of the callback function by default
773
- editor.on('beforeCreate', (...args) => { var _a, _b; return (_b = (_a = this.options.current).onBeforeCreate) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); });
774
- editor.on('blur', (...args) => { var _a, _b; return (_b = (_a = this.options.current).onBlur) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); });
775
- editor.on('create', (...args) => { var _a, _b; return (_b = (_a = this.options.current).onCreate) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); });
776
- editor.on('destroy', (...args) => { var _a, _b; return (_b = (_a = this.options.current).onDestroy) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); });
777
- editor.on('focus', (...args) => { var _a, _b; return (_b = (_a = this.options.current).onFocus) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); });
778
- editor.on('selectionUpdate', (...args) => { var _a, _b; return (_b = (_a = this.options.current).onSelectionUpdate) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); });
779
- editor.on('transaction', (...args) => { var _a, _b; return (_b = (_a = this.options.current).onTransaction) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); });
780
- editor.on('update', (...args) => { var _a, _b; return (_b = (_a = this.options.current).onUpdate) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); });
781
- editor.on('contentError', (...args) => { var _a, _b; return (_b = (_a = this.options.current).onContentError) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); });
799
+ const optionsToApply = {
800
+ ...this.options.current,
801
+ // Always call the most recent version of the callback function by default
802
+ onBeforeCreate: (...args) => { var _a, _b; return (_b = (_a = this.options.current).onBeforeCreate) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); },
803
+ onBlur: (...args) => { var _a, _b; return (_b = (_a = this.options.current).onBlur) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); },
804
+ onCreate: (...args) => { var _a, _b; return (_b = (_a = this.options.current).onCreate) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); },
805
+ onDestroy: (...args) => { var _a, _b; return (_b = (_a = this.options.current).onDestroy) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); },
806
+ onFocus: (...args) => { var _a, _b; return (_b = (_a = this.options.current).onFocus) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); },
807
+ onSelectionUpdate: (...args) => { var _a, _b; return (_b = (_a = this.options.current).onSelectionUpdate) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); },
808
+ onTransaction: (...args) => { var _a, _b; return (_b = (_a = this.options.current).onTransaction) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); },
809
+ onUpdate: (...args) => { var _a, _b; return (_b = (_a = this.options.current).onUpdate) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); },
810
+ onContentError: (...args) => { var _a, _b; return (_b = (_a = this.options.current).onContentError) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); },
811
+ };
812
+ const editor = new core.Editor(optionsToApply);
782
813
  // no need to keep track of the event listeners, they will be removed when the editor is destroyed
783
814
  return editor;
784
815
  }
@@ -970,6 +1001,13 @@
970
1001
  return (React.createElement("div", { ref: setElement, className: props.className, style: { visibility: 'hidden' } }, props.children));
971
1002
  };
972
1003
 
1004
+ class Editor extends core.Editor {
1005
+ constructor() {
1006
+ super(...arguments);
1007
+ this.contentComponent = null;
1008
+ }
1009
+ }
1010
+
973
1011
  const FloatingMenu = (props) => {
974
1012
  const [element, setElement] = React.useState(null);
975
1013
  const { editor: currentEditor } = useCurrentEditor();
@@ -1021,7 +1059,9 @@
1021
1059
  const NodeViewWrapper = React.forwardRef((props, ref) => {
1022
1060
  const { onDragStart } = useReactNodeView();
1023
1061
  const Tag = props.as || 'div';
1024
- return (React.createElement(Tag, { ...props, ref: ref, "data-node-view-wrapper": "", onDragStart: onDragStart, style: {
1062
+ return (
1063
+ // @ts-ignore
1064
+ React.createElement(Tag, { ...props, ref: ref, "data-node-view-wrapper": "", onDragStart: onDragStart, style: {
1025
1065
  whiteSpace: 'normal',
1026
1066
  ...props.style,
1027
1067
  } }));
@@ -1075,7 +1115,16 @@
1075
1115
  this.element.setAttribute(key, attrs[key]);
1076
1116
  });
1077
1117
  }
1078
- this.render();
1118
+ if (this.editor.isInitialized) {
1119
+ // On first render, we need to flush the render synchronously
1120
+ // Renders afterwards can be async, but this fixes a cursor positioning issue
1121
+ ReactDOM.flushSync(() => {
1122
+ this.render();
1123
+ });
1124
+ }
1125
+ else {
1126
+ this.render();
1127
+ }
1079
1128
  }
1080
1129
  render() {
1081
1130
  var _a, _b;
@@ -1086,7 +1135,7 @@
1086
1135
  this.ref = ref;
1087
1136
  };
1088
1137
  }
1089
- this.reactElement = React.createElement(Component, { ...props });
1138
+ this.reactElement = React.createElement(Component, props);
1090
1139
  (_b = (_a = this.editor) === null || _a === void 0 ? void 0 : _a.contentComponent) === null || _b === void 0 ? void 0 : _b.setRenderer(this.id, this);
1091
1140
  }
1092
1141
  updateProps(props = {}) {
@@ -1120,18 +1169,19 @@
1120
1169
  };
1121
1170
  this.component.displayName = capitalizeFirstChar(this.extension.name);
1122
1171
  }
1123
- const ReactNodeViewProvider = componentProps => {
1124
- const Component = this.component;
1125
- const onDragStart = this.onDragStart.bind(this);
1126
- const nodeViewContentRef = element => {
1127
- if (element && this.contentDOMElement && element.firstChild !== this.contentDOMElement) {
1128
- element.appendChild(this.contentDOMElement);
1129
- }
1130
- };
1131
- return (React.createElement(React.Fragment, null,
1132
- React.createElement(ReactNodeViewContext.Provider, { value: { onDragStart, nodeViewContentRef } },
1133
- React.createElement(Component, { ...componentProps }))));
1172
+ const onDragStart = this.onDragStart.bind(this);
1173
+ const nodeViewContentRef = element => {
1174
+ if (element && this.contentDOMElement && element.firstChild !== this.contentDOMElement) {
1175
+ element.appendChild(this.contentDOMElement);
1176
+ }
1134
1177
  };
1178
+ const context = { onDragStart, nodeViewContentRef };
1179
+ const Component = this.component;
1180
+ // For performance reasons, we memoize the provider component
1181
+ // And all of the things it requires are declared outside of the component, so it doesn't need to re-render
1182
+ const ReactNodeViewProvider = React.memo(componentProps => {
1183
+ return (React.createElement(ReactNodeViewContext.Provider, { value: context }, React.createElement(Component, componentProps)));
1184
+ });
1135
1185
  ReactNodeViewProvider.displayName = 'ReactNodeView';
1136
1186
  if (this.node.isLeaf) {
1137
1187
  this.contentDOMElement = null;