@tiptap/react 2.6.5 → 2.7.0-pre.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.js CHANGED
@@ -1,10 +1,14 @@
1
1
  import { BubbleMenuPlugin } from '@tiptap/extension-bubble-menu';
2
2
  import React, { forwardRef, useState, useEffect, useDebugValue, useRef, createContext, useContext } from 'react';
3
3
  import ReactDOM, { flushSync } from 'react-dom';
4
- import { Editor, NodeView } from '@tiptap/core';
4
+ import { Editor, NodeView, getRenderedAttributes } from '@tiptap/core';
5
5
  export * from '@tiptap/core';
6
6
  import { FloatingMenuPlugin } from '@tiptap/extension-floating-menu';
7
7
 
8
+ function getDefaultExportFromCjs (x) {
9
+ return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
10
+ }
11
+
8
12
  var shim = {exports: {}};
9
13
 
10
14
  var useSyncExternalStoreShim_production_min = {};
@@ -440,6 +444,80 @@ const EditorContentWithKey = forwardRef((props, ref) => {
440
444
  });
441
445
  const EditorContent = React.memo(EditorContentWithKey);
442
446
 
447
+ var react = function equal(a, b) {
448
+ if (a === b) return true;
449
+
450
+ if (a && b && typeof a == 'object' && typeof b == 'object') {
451
+ if (a.constructor !== b.constructor) return false;
452
+
453
+ var length, i, keys;
454
+ if (Array.isArray(a)) {
455
+ length = a.length;
456
+ if (length != b.length) return false;
457
+ for (i = length; i-- !== 0;)
458
+ if (!equal(a[i], b[i])) return false;
459
+ return true;
460
+ }
461
+
462
+
463
+ if ((a instanceof Map) && (b instanceof Map)) {
464
+ if (a.size !== b.size) return false;
465
+ for (i of a.entries())
466
+ if (!b.has(i[0])) return false;
467
+ for (i of a.entries())
468
+ if (!equal(i[1], b.get(i[0]))) return false;
469
+ return true;
470
+ }
471
+
472
+ if ((a instanceof Set) && (b instanceof Set)) {
473
+ if (a.size !== b.size) return false;
474
+ for (i of a.entries())
475
+ if (!b.has(i[0])) return false;
476
+ return true;
477
+ }
478
+
479
+ if (ArrayBuffer.isView(a) && ArrayBuffer.isView(b)) {
480
+ length = a.length;
481
+ if (length != b.length) return false;
482
+ for (i = length; i-- !== 0;)
483
+ if (a[i] !== b[i]) return false;
484
+ return true;
485
+ }
486
+
487
+
488
+ if (a.constructor === RegExp) return a.source === b.source && a.flags === b.flags;
489
+ if (a.valueOf !== Object.prototype.valueOf) return a.valueOf() === b.valueOf();
490
+ if (a.toString !== Object.prototype.toString) return a.toString() === b.toString();
491
+
492
+ keys = Object.keys(a);
493
+ length = keys.length;
494
+ if (length !== Object.keys(b).length) return false;
495
+
496
+ for (i = length; i-- !== 0;)
497
+ if (!Object.prototype.hasOwnProperty.call(b, keys[i])) return false;
498
+
499
+ for (i = length; i-- !== 0;) {
500
+ var key = keys[i];
501
+
502
+ if (key === '_owner' && a.$$typeof) {
503
+ // React-specific: avoid traversing React elements' _owner.
504
+ // _owner contains circular references
505
+ // and is not needed when comparing the actual elements (and not their owners)
506
+ continue;
507
+ }
508
+
509
+ if (!equal(a[key], b[key])) return false;
510
+ }
511
+
512
+ return true;
513
+ }
514
+
515
+ // true if both NaN, false otherwise
516
+ return a!==a && b!==b;
517
+ };
518
+
519
+ var deepEqual = /*@__PURE__*/getDefaultExportFromCjs(react);
520
+
443
521
  var withSelector = {exports: {}};
444
522
 
445
523
  var withSelector_production_min = {};
@@ -711,13 +789,25 @@ class EditorStateManager {
711
789
  return undefined;
712
790
  }
713
791
  }
792
+ /**
793
+ * This hook allows you to watch for changes on the editor instance.
794
+ * It will allow you to select a part of the editor state and re-render the component when it changes.
795
+ * @example
796
+ * ```tsx
797
+ * const editor = useEditor({...options})
798
+ * const { currentSelection } = useEditorState({
799
+ * editor,
800
+ * selector: snapshot => ({ currentSelection: snapshot.editor.state.selection }),
801
+ * })
802
+ */
714
803
  function useEditorState(options) {
715
- const [editorInstance] = useState(() => new EditorStateManager(options.editor));
804
+ var _a;
805
+ const [editorStateManager] = useState(() => new EditorStateManager(options.editor));
716
806
  // Using the `useSyncExternalStore` hook to sync the editor instance with the component state
717
- const selectedState = withSelectorExports.useSyncExternalStoreWithSelector(editorInstance.subscribe, editorInstance.getSnapshot, editorInstance.getServerSnapshot, options.selector, options.equalityFn);
807
+ const selectedState = withSelectorExports.useSyncExternalStoreWithSelector(editorStateManager.subscribe, editorStateManager.getSnapshot, editorStateManager.getServerSnapshot, options.selector, (_a = options.equalityFn) !== null && _a !== void 0 ? _a : deepEqual);
718
808
  useEffect(() => {
719
- return editorInstance.watch(options.editor);
720
- }, [options.editor, editorInstance]);
809
+ return editorStateManager.watch(options.editor);
810
+ }, [options.editor, editorStateManager]);
721
811
  useDebugValue(selectedState);
722
812
  return selectedState;
723
813
  }
@@ -811,6 +901,8 @@ class EditorInstanceManager {
811
901
  onTransaction: (...args) => { var _a, _b; return (_b = (_a = this.options.current).onTransaction) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); },
812
902
  onUpdate: (...args) => { var _a, _b; return (_b = (_a = this.options.current).onUpdate) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); },
813
903
  onContentError: (...args) => { var _a, _b; return (_b = (_a = this.options.current).onContentError) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); },
904
+ onDrop: (...args) => { var _a, _b; return (_b = (_a = this.options.current).onDrop) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); },
905
+ onPaste: (...args) => { var _a, _b; return (_b = (_a = this.options.current).onPaste) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); },
814
906
  };
815
907
  const editor = new Editor(optionsToApply);
816
908
  // no need to keep track of the event listeners, they will be removed when the editor is destroyed
@@ -1099,7 +1191,10 @@ function isForwardRefComponent(Component) {
1099
1191
  * })
1100
1192
  */
1101
1193
  class ReactRenderer {
1102
- constructor(component, { editor, props = {}, as = 'div', className = '', attrs, }) {
1194
+ /**
1195
+ * Immediately creates element and renders the provided React component.
1196
+ */
1197
+ constructor(component, { editor, props = {}, as = 'div', className = '', }) {
1103
1198
  this.ref = null;
1104
1199
  this.id = Math.floor(Math.random() * 0xFFFFFFFF).toString();
1105
1200
  this.component = component;
@@ -1110,11 +1205,6 @@ class ReactRenderer {
1110
1205
  if (className) {
1111
1206
  this.element.classList.add(...className.split(' '));
1112
1207
  }
1113
- if (attrs) {
1114
- Object.keys(attrs).forEach(key => {
1115
- this.element.setAttribute(key, attrs[key]);
1116
- });
1117
- }
1118
1208
  if (this.editor.isInitialized) {
1119
1209
  // On first render, we need to flush the render synchronously
1120
1210
  // Renders afterwards can be async, but this fixes a cursor positioning issue
@@ -1126,12 +1216,16 @@ class ReactRenderer {
1126
1216
  this.render();
1127
1217
  }
1128
1218
  }
1219
+ /**
1220
+ * Render the React component.
1221
+ */
1129
1222
  render() {
1130
1223
  var _a;
1131
1224
  const Component = this.component;
1132
1225
  const props = this.props;
1133
1226
  const editor = this.editor;
1134
1227
  if (isClassComponent(Component) || isForwardRefComponent(Component)) {
1228
+ // @ts-ignore This is a hack to make the ref work
1135
1229
  props.ref = (ref) => {
1136
1230
  this.ref = ref;
1137
1231
  };
@@ -1139,6 +1233,9 @@ class ReactRenderer {
1139
1233
  this.reactElement = React.createElement(Component, props);
1140
1234
  (_a = editor === null || editor === void 0 ? void 0 : editor.contentComponent) === null || _a === void 0 ? void 0 : _a.setRenderer(this.id, this);
1141
1235
  }
1236
+ /**
1237
+ * Re-renders the React component with new props.
1238
+ */
1142
1239
  updateProps(props = {}) {
1143
1240
  this.props = {
1144
1241
  ...this.props,
@@ -1146,21 +1243,39 @@ class ReactRenderer {
1146
1243
  };
1147
1244
  this.render();
1148
1245
  }
1246
+ /**
1247
+ * Destroy the React component.
1248
+ */
1149
1249
  destroy() {
1150
1250
  var _a;
1151
1251
  const editor = this.editor;
1152
1252
  (_a = editor === null || editor === void 0 ? void 0 : editor.contentComponent) === null || _a === void 0 ? void 0 : _a.removeRenderer(this.id);
1153
1253
  }
1254
+ /**
1255
+ * Update the attributes of the element that holds the React component.
1256
+ */
1257
+ updateAttributes(attributes) {
1258
+ Object.keys(attributes).forEach(key => {
1259
+ this.element.setAttribute(key, attributes[key]);
1260
+ });
1261
+ }
1154
1262
  }
1155
1263
 
1156
1264
  class ReactNodeView extends NodeView {
1265
+ /**
1266
+ * Setup the React component.
1267
+ * Called on initialization.
1268
+ */
1157
1269
  mount() {
1158
1270
  const props = {
1159
1271
  editor: this.editor,
1160
1272
  node: this.node,
1161
1273
  decorations: this.decorations,
1274
+ innerDecorations: this.innerDecorations,
1275
+ view: this.view,
1162
1276
  selected: false,
1163
1277
  extension: this.extension,
1278
+ HTMLAttributes: this.HTMLAttributes,
1164
1279
  getPos: () => this.getPos(),
1165
1280
  updateAttributes: (attributes = {}) => this.updateAttributes(attributes),
1166
1281
  deleteNode: () => this.deleteNode(),
@@ -1195,6 +1310,7 @@ class ReactNodeView extends NodeView {
1195
1310
  this.contentDOMElement = document.createElement(this.node.isInline ? 'span' : 'div');
1196
1311
  }
1197
1312
  if (this.contentDOMElement) {
1313
+ this.contentDOMElement.dataset.nodeViewContentReact = '';
1198
1314
  // For some reason the whiteSpace prop is not inherited properly in Chrome and Safari
1199
1315
  // With this fix it seems to work fine
1200
1316
  // See: https://github.com/ueberdosis/tiptap/issues/1197
@@ -1212,9 +1328,13 @@ class ReactNodeView extends NodeView {
1212
1328
  props,
1213
1329
  as,
1214
1330
  className: `node-${this.node.type.name} ${className}`.trim(),
1215
- attrs: this.options.attrs,
1216
1331
  });
1332
+ this.updateElementAttributes();
1217
1333
  }
1334
+ /**
1335
+ * Return the DOM element.
1336
+ * This is the element that will be used to display the node view.
1337
+ */
1218
1338
  get dom() {
1219
1339
  var _a;
1220
1340
  if (this.renderer.element.firstElementChild
@@ -1223,15 +1343,27 @@ class ReactNodeView extends NodeView {
1223
1343
  }
1224
1344
  return this.renderer.element;
1225
1345
  }
1346
+ /**
1347
+ * Return the content DOM element.
1348
+ * This is the element that will be used to display the rich-text content of the node.
1349
+ */
1226
1350
  get contentDOM() {
1227
1351
  if (this.node.isLeaf) {
1228
1352
  return null;
1229
1353
  }
1230
1354
  return this.contentDOMElement;
1231
1355
  }
1356
+ /**
1357
+ * On editor selection update, check if the node is selected.
1358
+ * If it is, call `selectNode`, otherwise call `deselectNode`.
1359
+ */
1232
1360
  handleSelectionUpdate() {
1233
1361
  const { from, to } = this.editor.state.selection;
1234
- if (from <= this.getPos() && to >= this.getPos() + this.node.nodeSize) {
1362
+ const pos = this.getPos();
1363
+ if (typeof pos !== 'number') {
1364
+ return;
1365
+ }
1366
+ if (from <= pos && to >= pos + this.node.nodeSize) {
1235
1367
  if (this.renderer.props.selected) {
1236
1368
  return;
1237
1369
  }
@@ -1244,9 +1376,16 @@ class ReactNodeView extends NodeView {
1244
1376
  this.deselectNode();
1245
1377
  }
1246
1378
  }
1247
- update(node, decorations) {
1248
- const updateProps = (props) => {
1379
+ /**
1380
+ * On update, update the React component.
1381
+ * To prevent unnecessary updates, the `update` option can be used.
1382
+ */
1383
+ update(node, decorations, innerDecorations) {
1384
+ const rerenderComponent = (props) => {
1249
1385
  this.renderer.updateProps(props);
1386
+ if (typeof this.options.attrs === 'function') {
1387
+ this.updateElementAttributes();
1388
+ }
1250
1389
  };
1251
1390
  if (node.type !== this.node.type) {
1252
1391
  return false;
@@ -1254,44 +1393,83 @@ class ReactNodeView extends NodeView {
1254
1393
  if (typeof this.options.update === 'function') {
1255
1394
  const oldNode = this.node;
1256
1395
  const oldDecorations = this.decorations;
1396
+ const oldInnerDecorations = this.innerDecorations;
1257
1397
  this.node = node;
1258
1398
  this.decorations = decorations;
1399
+ this.innerDecorations = innerDecorations;
1259
1400
  return this.options.update({
1260
1401
  oldNode,
1261
1402
  oldDecorations,
1262
1403
  newNode: node,
1263
1404
  newDecorations: decorations,
1264
- updateProps: () => updateProps({ node, decorations }),
1405
+ oldInnerDecorations,
1406
+ innerDecorations,
1407
+ updateProps: () => rerenderComponent({ node, decorations, innerDecorations }),
1265
1408
  });
1266
1409
  }
1267
- if (node === this.node && this.decorations === decorations) {
1410
+ if (node === this.node
1411
+ && this.decorations === decorations
1412
+ && this.innerDecorations === innerDecorations) {
1268
1413
  return true;
1269
1414
  }
1270
1415
  this.node = node;
1271
1416
  this.decorations = decorations;
1272
- updateProps({ node, decorations });
1417
+ this.innerDecorations = innerDecorations;
1418
+ rerenderComponent({ node, decorations, innerDecorations });
1273
1419
  return true;
1274
1420
  }
1421
+ /**
1422
+ * Select the node.
1423
+ * Add the `selected` prop and the `ProseMirror-selectednode` class.
1424
+ */
1275
1425
  selectNode() {
1276
1426
  this.renderer.updateProps({
1277
1427
  selected: true,
1278
1428
  });
1279
1429
  this.renderer.element.classList.add('ProseMirror-selectednode');
1280
1430
  }
1431
+ /**
1432
+ * Deselect the node.
1433
+ * Remove the `selected` prop and the `ProseMirror-selectednode` class.
1434
+ */
1281
1435
  deselectNode() {
1282
1436
  this.renderer.updateProps({
1283
1437
  selected: false,
1284
1438
  });
1285
1439
  this.renderer.element.classList.remove('ProseMirror-selectednode');
1286
1440
  }
1441
+ /**
1442
+ * Destroy the React component instance.
1443
+ */
1287
1444
  destroy() {
1288
1445
  this.renderer.destroy();
1289
1446
  this.editor.off('selectionUpdate', this.handleSelectionUpdate);
1290
1447
  this.contentDOMElement = null;
1291
1448
  }
1449
+ /**
1450
+ * Update the attributes of the top-level element that holds the React component.
1451
+ * Applying the attributes defined in the `attrs` option.
1452
+ */
1453
+ updateElementAttributes() {
1454
+ if (this.options.attrs) {
1455
+ let attrsObj = {};
1456
+ if (typeof this.options.attrs === 'function') {
1457
+ const extensionAttributes = this.editor.extensionManager.attributes;
1458
+ const HTMLAttributes = getRenderedAttributes(this.node, extensionAttributes);
1459
+ attrsObj = this.options.attrs({ node: this.node, HTMLAttributes });
1460
+ }
1461
+ else {
1462
+ attrsObj = this.options.attrs;
1463
+ }
1464
+ this.renderer.updateAttributes(attrsObj);
1465
+ }
1466
+ }
1292
1467
  }
1468
+ /**
1469
+ * Create a React node view renderer.
1470
+ */
1293
1471
  function ReactNodeViewRenderer(component, options) {
1294
- return (props) => {
1472
+ return props => {
1295
1473
  // try to get the parent component
1296
1474
  // this is important for vue devtools to show the component hierarchy correctly
1297
1475
  // maybe it’s `undefined` because <editor-content> isn’t rendered yet
@@ -1302,5 +1480,5 @@ function ReactNodeViewRenderer(component, options) {
1302
1480
  };
1303
1481
  }
1304
1482
 
1305
- export { BubbleMenu, EditorConsumer, EditorContent, EditorContext, EditorProvider, FloatingMenu, NodeViewContent, NodeViewWrapper, PureEditorContent, ReactNodeViewContext, ReactNodeViewRenderer, ReactRenderer, useCurrentEditor, useEditor, useEditorState, useReactNodeView };
1483
+ export { BubbleMenu, EditorConsumer, EditorContent, EditorContext, EditorProvider, FloatingMenu, NodeViewContent, NodeViewWrapper, PureEditorContent, ReactNodeView, ReactNodeViewContext, ReactNodeViewRenderer, ReactRenderer, useCurrentEditor, useEditor, useEditorState, useReactNodeView };
1306
1484
  //# sourceMappingURL=index.js.map