@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.cjs CHANGED
@@ -11,127 +11,6 @@ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'defau
11
11
  var React__default = /*#__PURE__*/_interopDefaultCompat(React);
12
12
  var ReactDOM__default = /*#__PURE__*/_interopDefaultCompat(ReactDOM);
13
13
 
14
- const mergeRefs = (...refs) => {
15
- return (node) => {
16
- refs.forEach(ref => {
17
- if (typeof ref === 'function') {
18
- ref(node);
19
- }
20
- else if (ref) {
21
- ref.current = node;
22
- }
23
- });
24
- };
25
- };
26
- const Portals = ({ renderers }) => {
27
- return (React__default.default.createElement(React__default.default.Fragment, null, Object.entries(renderers).map(([key, renderer]) => {
28
- return ReactDOM__default.default.createPortal(renderer.reactElement, renderer.element, key);
29
- })));
30
- };
31
- class PureEditorContent extends React__default.default.Component {
32
- constructor(props) {
33
- super(props);
34
- this.editorContentRef = React__default.default.createRef();
35
- this.initialized = false;
36
- this.state = {
37
- renderers: {},
38
- };
39
- }
40
- componentDidMount() {
41
- this.init();
42
- }
43
- componentDidUpdate() {
44
- this.init();
45
- }
46
- init() {
47
- const { editor } = this.props;
48
- if (editor && !editor.isDestroyed && editor.options.element) {
49
- if (editor.contentComponent) {
50
- return;
51
- }
52
- const element = this.editorContentRef.current;
53
- element.append(...editor.options.element.childNodes);
54
- editor.setOptions({
55
- element,
56
- });
57
- editor.contentComponent = this;
58
- editor.createNodeViews();
59
- this.initialized = true;
60
- }
61
- }
62
- maybeFlushSync(fn) {
63
- // Avoid calling flushSync until the editor is initialized.
64
- // Initialization happens during the componentDidMount or componentDidUpdate
65
- // lifecycle methods, and React doesn't allow calling flushSync from inside
66
- // a lifecycle method.
67
- if (this.initialized) {
68
- ReactDOM.flushSync(fn);
69
- }
70
- else {
71
- fn();
72
- }
73
- }
74
- setRenderer(id, renderer) {
75
- this.maybeFlushSync(() => {
76
- this.setState(({ renderers }) => ({
77
- renderers: {
78
- ...renderers,
79
- [id]: renderer,
80
- },
81
- }));
82
- });
83
- }
84
- removeRenderer(id) {
85
- this.maybeFlushSync(() => {
86
- this.setState(({ renderers }) => {
87
- const nextRenderers = { ...renderers };
88
- delete nextRenderers[id];
89
- return { renderers: nextRenderers };
90
- });
91
- });
92
- }
93
- componentWillUnmount() {
94
- const { editor } = this.props;
95
- if (!editor) {
96
- return;
97
- }
98
- this.initialized = false;
99
- if (!editor.isDestroyed) {
100
- editor.view.setProps({
101
- nodeViews: {},
102
- });
103
- }
104
- editor.contentComponent = null;
105
- if (!editor.options.element.firstChild) {
106
- return;
107
- }
108
- const newElement = document.createElement('div');
109
- newElement.append(...editor.options.element.childNodes);
110
- editor.setOptions({
111
- element: newElement,
112
- });
113
- }
114
- render() {
115
- const { editor, innerRef, ...rest } = this.props;
116
- return (React__default.default.createElement(React__default.default.Fragment, null,
117
- React__default.default.createElement("div", { ref: mergeRefs(innerRef, this.editorContentRef), ...rest }),
118
- React__default.default.createElement(Portals, { renderers: this.state.renderers })));
119
- }
120
- }
121
- // EditorContent should be re-created whenever the Editor instance changes
122
- const EditorContentWithKey = React.forwardRef((props, ref) => {
123
- const key = React__default.default.useMemo(() => {
124
- return Math.floor(Math.random() * 0xFFFFFFFF).toString();
125
- }, [props.editor]);
126
- // Can't use JSX here because it conflicts with the type definition of Vue's JSX, so use createElement
127
- return React__default.default.createElement(PureEditorContent, {
128
- key,
129
- innerRef: ref,
130
- ...props,
131
- });
132
- });
133
- const EditorContent = React__default.default.memo(EditorContentWithKey);
134
-
135
14
  var shim = {exports: {}};
136
15
 
137
16
  var useSyncExternalStoreShim_production_min = {};
@@ -410,12 +289,161 @@ if (process.env.NODE_ENV === 'production') {
410
289
 
411
290
  var shimExports = shim.exports;
412
291
 
413
- class Editor extends core.Editor {
414
- constructor() {
415
- super(...arguments);
416
- this.contentComponent = null;
292
+ const mergeRefs = (...refs) => {
293
+ return (node) => {
294
+ refs.forEach(ref => {
295
+ if (typeof ref === 'function') {
296
+ ref(node);
297
+ }
298
+ else if (ref) {
299
+ ref.current = node;
300
+ }
301
+ });
302
+ };
303
+ };
304
+ /**
305
+ * This component renders all of the editor's node views.
306
+ */
307
+ const Portals = ({ contentComponent, }) => {
308
+ // For performance reasons, we render the node view portals on state changes only
309
+ const renderers = shimExports.useSyncExternalStore(contentComponent.subscribe, contentComponent.getSnapshot, contentComponent.getServerSnapshot);
310
+ // This allows us to directly render the portals without any additional wrapper
311
+ return (React__default.default.createElement(React__default.default.Fragment, null, Object.values(renderers)));
312
+ };
313
+ function getInstance() {
314
+ const subscribers = new Set();
315
+ let renderers = {};
316
+ return {
317
+ /**
318
+ * Subscribe to the editor instance's changes.
319
+ */
320
+ subscribe(callback) {
321
+ subscribers.add(callback);
322
+ return () => {
323
+ subscribers.delete(callback);
324
+ };
325
+ },
326
+ getSnapshot() {
327
+ return renderers;
328
+ },
329
+ getServerSnapshot() {
330
+ return renderers;
331
+ },
332
+ /**
333
+ * Adds a new NodeView Renderer to the editor.
334
+ */
335
+ setRenderer(id, renderer) {
336
+ renderers = {
337
+ ...renderers,
338
+ [id]: ReactDOM__default.default.createPortal(renderer.reactElement, renderer.element, id),
339
+ };
340
+ subscribers.forEach(subscriber => subscriber());
341
+ },
342
+ /**
343
+ * Removes a NodeView Renderer from the editor.
344
+ */
345
+ removeRenderer(id) {
346
+ const nextRenderers = { ...renderers };
347
+ delete nextRenderers[id];
348
+ renderers = nextRenderers;
349
+ subscribers.forEach(subscriber => subscriber());
350
+ },
351
+ };
352
+ }
353
+ class PureEditorContent extends React__default.default.Component {
354
+ constructor(props) {
355
+ var _a;
356
+ super(props);
357
+ this.editorContentRef = React__default.default.createRef();
358
+ this.initialized = false;
359
+ this.state = {
360
+ hasContentComponentInitialized: Boolean((_a = props.editor) === null || _a === void 0 ? void 0 : _a.contentComponent),
361
+ };
362
+ }
363
+ componentDidMount() {
364
+ this.init();
365
+ }
366
+ componentDidUpdate() {
367
+ this.init();
368
+ }
369
+ init() {
370
+ const { editor } = this.props;
371
+ if (editor && !editor.isDestroyed && editor.options.element) {
372
+ if (editor.contentComponent) {
373
+ return;
374
+ }
375
+ const element = this.editorContentRef.current;
376
+ element.append(...editor.options.element.childNodes);
377
+ editor.setOptions({
378
+ element,
379
+ });
380
+ editor.contentComponent = getInstance();
381
+ // Has the content component been initialized?
382
+ if (!this.state.hasContentComponentInitialized) {
383
+ // Subscribe to the content component
384
+ this.unsubscribeToContentComponent = editor.contentComponent.subscribe(() => {
385
+ this.setState(prevState => {
386
+ if (!prevState.hasContentComponentInitialized) {
387
+ return {
388
+ hasContentComponentInitialized: true,
389
+ };
390
+ }
391
+ return prevState;
392
+ });
393
+ // Unsubscribe to previous content component
394
+ if (this.unsubscribeToContentComponent) {
395
+ this.unsubscribeToContentComponent();
396
+ }
397
+ });
398
+ }
399
+ editor.createNodeViews();
400
+ this.initialized = true;
401
+ }
402
+ }
403
+ componentWillUnmount() {
404
+ const { editor } = this.props;
405
+ if (!editor) {
406
+ return;
407
+ }
408
+ this.initialized = false;
409
+ if (!editor.isDestroyed) {
410
+ editor.view.setProps({
411
+ nodeViews: {},
412
+ });
413
+ }
414
+ if (this.unsubscribeToContentComponent) {
415
+ this.unsubscribeToContentComponent();
416
+ }
417
+ editor.contentComponent = null;
418
+ if (!editor.options.element.firstChild) {
419
+ return;
420
+ }
421
+ const newElement = document.createElement('div');
422
+ newElement.append(...editor.options.element.childNodes);
423
+ editor.setOptions({
424
+ element: newElement,
425
+ });
426
+ }
427
+ render() {
428
+ const { editor, innerRef, ...rest } = this.props;
429
+ return (React__default.default.createElement(React__default.default.Fragment, null,
430
+ React__default.default.createElement("div", { ref: mergeRefs(innerRef, this.editorContentRef), ...rest }),
431
+ (editor === null || editor === void 0 ? void 0 : editor.contentComponent) && React__default.default.createElement(Portals, { contentComponent: editor.contentComponent })));
417
432
  }
418
433
  }
434
+ // EditorContent should be re-created whenever the Editor instance changes
435
+ const EditorContentWithKey = React.forwardRef((props, ref) => {
436
+ const key = React__default.default.useMemo(() => {
437
+ return Math.floor(Math.random() * 0xffffffff).toString();
438
+ }, [props.editor]);
439
+ // Can't use JSX here because it conflicts with the type definition of Vue's JSX, so use createElement
440
+ return React__default.default.createElement(PureEditorContent, {
441
+ key,
442
+ innerRef: ref,
443
+ ...props,
444
+ });
445
+ });
446
+ const EditorContent = React__default.default.memo(EditorContentWithKey);
419
447
 
420
448
  var withSelector = {exports: {}};
421
449
 
@@ -775,17 +803,20 @@ class EditorInstanceManager {
775
803
  * Create a new editor instance. And attach event listeners.
776
804
  */
777
805
  createEditor() {
778
- const editor = new Editor(this.options.current);
779
- // Always call the most recent version of the callback function by default
780
- editor.on('beforeCreate', (...args) => { var _a, _b; return (_b = (_a = this.options.current).onBeforeCreate) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); });
781
- editor.on('blur', (...args) => { var _a, _b; return (_b = (_a = this.options.current).onBlur) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); });
782
- editor.on('create', (...args) => { var _a, _b; return (_b = (_a = this.options.current).onCreate) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); });
783
- editor.on('destroy', (...args) => { var _a, _b; return (_b = (_a = this.options.current).onDestroy) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); });
784
- editor.on('focus', (...args) => { var _a, _b; return (_b = (_a = this.options.current).onFocus) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); });
785
- editor.on('selectionUpdate', (...args) => { var _a, _b; return (_b = (_a = this.options.current).onSelectionUpdate) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); });
786
- editor.on('transaction', (...args) => { var _a, _b; return (_b = (_a = this.options.current).onTransaction) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); });
787
- editor.on('update', (...args) => { var _a, _b; return (_b = (_a = this.options.current).onUpdate) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); });
788
- editor.on('contentError', (...args) => { var _a, _b; return (_b = (_a = this.options.current).onContentError) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); });
806
+ const optionsToApply = {
807
+ ...this.options.current,
808
+ // Always call the most recent version of the callback function by default
809
+ onBeforeCreate: (...args) => { var _a, _b; return (_b = (_a = this.options.current).onBeforeCreate) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); },
810
+ onBlur: (...args) => { var _a, _b; return (_b = (_a = this.options.current).onBlur) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); },
811
+ onCreate: (...args) => { var _a, _b; return (_b = (_a = this.options.current).onCreate) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); },
812
+ onDestroy: (...args) => { var _a, _b; return (_b = (_a = this.options.current).onDestroy) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); },
813
+ onFocus: (...args) => { var _a, _b; return (_b = (_a = this.options.current).onFocus) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); },
814
+ onSelectionUpdate: (...args) => { var _a, _b; return (_b = (_a = this.options.current).onSelectionUpdate) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); },
815
+ onTransaction: (...args) => { var _a, _b; return (_b = (_a = this.options.current).onTransaction) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); },
816
+ onUpdate: (...args) => { var _a, _b; return (_b = (_a = this.options.current).onUpdate) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); },
817
+ onContentError: (...args) => { var _a, _b; return (_b = (_a = this.options.current).onContentError) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); },
818
+ };
819
+ const editor = new core.Editor(optionsToApply);
789
820
  // no need to keep track of the event listeners, they will be removed when the editor is destroyed
790
821
  return editor;
791
822
  }
@@ -977,6 +1008,13 @@ const BubbleMenu = (props) => {
977
1008
  return (React__default.default.createElement("div", { ref: setElement, className: props.className, style: { visibility: 'hidden' } }, props.children));
978
1009
  };
979
1010
 
1011
+ class Editor extends core.Editor {
1012
+ constructor() {
1013
+ super(...arguments);
1014
+ this.contentComponent = null;
1015
+ }
1016
+ }
1017
+
980
1018
  const FloatingMenu = (props) => {
981
1019
  const [element, setElement] = React.useState(null);
982
1020
  const { editor: currentEditor } = useCurrentEditor();
@@ -1028,7 +1066,9 @@ const NodeViewContent = props => {
1028
1066
  const NodeViewWrapper = React__default.default.forwardRef((props, ref) => {
1029
1067
  const { onDragStart } = useReactNodeView();
1030
1068
  const Tag = props.as || 'div';
1031
- return (React__default.default.createElement(Tag, { ...props, ref: ref, "data-node-view-wrapper": "", onDragStart: onDragStart, style: {
1069
+ return (
1070
+ // @ts-ignore
1071
+ React__default.default.createElement(Tag, { ...props, ref: ref, "data-node-view-wrapper": "", onDragStart: onDragStart, style: {
1032
1072
  whiteSpace: 'normal',
1033
1073
  ...props.style,
1034
1074
  } }));
@@ -1082,7 +1122,16 @@ class ReactRenderer {
1082
1122
  this.element.setAttribute(key, attrs[key]);
1083
1123
  });
1084
1124
  }
1085
- this.render();
1125
+ if (this.editor.isInitialized) {
1126
+ // On first render, we need to flush the render synchronously
1127
+ // Renders afterwards can be async, but this fixes a cursor positioning issue
1128
+ ReactDOM.flushSync(() => {
1129
+ this.render();
1130
+ });
1131
+ }
1132
+ else {
1133
+ this.render();
1134
+ }
1086
1135
  }
1087
1136
  render() {
1088
1137
  var _a, _b;
@@ -1093,7 +1142,7 @@ class ReactRenderer {
1093
1142
  this.ref = ref;
1094
1143
  };
1095
1144
  }
1096
- this.reactElement = React__default.default.createElement(Component, { ...props });
1145
+ this.reactElement = React__default.default.createElement(Component, props);
1097
1146
  (_b = (_a = this.editor) === null || _a === void 0 ? void 0 : _a.contentComponent) === null || _b === void 0 ? void 0 : _b.setRenderer(this.id, this);
1098
1147
  }
1099
1148
  updateProps(props = {}) {
@@ -1127,18 +1176,19 @@ class ReactNodeView extends core.NodeView {
1127
1176
  };
1128
1177
  this.component.displayName = capitalizeFirstChar(this.extension.name);
1129
1178
  }
1130
- const ReactNodeViewProvider = componentProps => {
1131
- const Component = this.component;
1132
- const onDragStart = this.onDragStart.bind(this);
1133
- const nodeViewContentRef = element => {
1134
- if (element && this.contentDOMElement && element.firstChild !== this.contentDOMElement) {
1135
- element.appendChild(this.contentDOMElement);
1136
- }
1137
- };
1138
- return (React__default.default.createElement(React__default.default.Fragment, null,
1139
- React__default.default.createElement(ReactNodeViewContext.Provider, { value: { onDragStart, nodeViewContentRef } },
1140
- React__default.default.createElement(Component, { ...componentProps }))));
1179
+ const onDragStart = this.onDragStart.bind(this);
1180
+ const nodeViewContentRef = element => {
1181
+ if (element && this.contentDOMElement && element.firstChild !== this.contentDOMElement) {
1182
+ element.appendChild(this.contentDOMElement);
1183
+ }
1141
1184
  };
1185
+ const context = { onDragStart, nodeViewContentRef };
1186
+ const Component = this.component;
1187
+ // For performance reasons, we memoize the provider component
1188
+ // And all of the things it requires are declared outside of the component, so it doesn't need to re-render
1189
+ const ReactNodeViewProvider = React__default.default.memo(componentProps => {
1190
+ return (React__default.default.createElement(ReactNodeViewContext.Provider, { value: context }, React__default.default.createElement(Component, componentProps)));
1191
+ });
1142
1192
  ReactNodeViewProvider.displayName = 'ReactNodeView';
1143
1193
  if (this.node.isLeaf) {
1144
1194
  this.contentDOMElement = null;