@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.cjs +201 -22
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +198 -20
- package/dist/index.js.map +1 -1
- package/dist/index.umd.js +1501 -1322
- package/dist/index.umd.js.map +1 -1
- package/dist/packages/core/src/NodeView.d.ts +20 -8
- package/dist/packages/core/src/index.d.ts +2 -0
- package/dist/packages/core/src/plugins/DropPlugin.d.ts +3 -0
- package/dist/packages/core/src/plugins/PastePlugin.d.ts +3 -0
- package/dist/packages/core/src/types.d.ts +35 -15
- package/dist/packages/react/src/ReactNodeViewRenderer.d.ts +86 -7
- package/dist/packages/react/src/ReactRenderer.d.ts +19 -10
- package/dist/packages/react/src/useEditorState.d.ts +23 -1
- package/package.json +8 -7
- package/src/ReactNodeViewRenderer.tsx +152 -41
- package/src/ReactRenderer.tsx +25 -18
- package/src/useEditor.ts +2 -0
- package/src/useEditorState.ts +43 -8
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
|
-
|
|
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(
|
|
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
|
|
720
|
-
}, [options.editor,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1248
|
-
|
|
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
|
-
|
|
1405
|
+
oldInnerDecorations,
|
|
1406
|
+
innerDecorations,
|
|
1407
|
+
updateProps: () => rerenderComponent({ node, decorations, innerDecorations }),
|
|
1265
1408
|
});
|
|
1266
1409
|
}
|
|
1267
|
-
if (node === this.node
|
|
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
|
-
|
|
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
|
|
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
|