@tiptap/core 2.2.5 → 2.3.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
@@ -661,6 +661,23 @@ function inputRulesPlugin(props) {
661
661
  if (stored) {
662
662
  return stored;
663
663
  }
664
+ // if InputRule is triggered by insertContent()
665
+ const simulatedInputMeta = tr.getMeta('applyInputRules');
666
+ const isSimulatedInput = !!simulatedInputMeta;
667
+ if (isSimulatedInput) {
668
+ setTimeout(() => {
669
+ const { from, text } = simulatedInputMeta;
670
+ const to = from + text.length;
671
+ run$1({
672
+ editor,
673
+ from,
674
+ to,
675
+ text,
676
+ rules,
677
+ plugin,
678
+ });
679
+ });
680
+ }
664
681
  return tr.selectionSet || tr.docChanged ? null : prev;
665
682
  },
666
683
  },
@@ -792,6 +809,14 @@ function run(config) {
792
809
  const success = handlers.every(handler => handler !== null);
793
810
  return success;
794
811
  }
812
+ const createClipboardPasteEvent = (text) => {
813
+ var _a;
814
+ const event = new ClipboardEvent('paste', {
815
+ clipboardData: new DataTransfer(),
816
+ });
817
+ (_a = event.clipboardData) === null || _a === void 0 ? void 0 : _a.setData('text/html', text);
818
+ return event;
819
+ };
795
820
  /**
796
821
  * Create an paste rules plugin. When enabled, it will cause pasted
797
822
  * text that matches any of the given rules to trigger the rule’s
@@ -804,6 +829,28 @@ function pasteRulesPlugin(props) {
804
829
  let isDroppedFromProseMirror = false;
805
830
  let pasteEvent = typeof ClipboardEvent !== 'undefined' ? new ClipboardEvent('paste') : null;
806
831
  let dropEvent = typeof DragEvent !== 'undefined' ? new DragEvent('drop') : null;
832
+ const processEvent = ({ state, from, to, rule, pasteEvt, }) => {
833
+ const tr = state.tr;
834
+ const chainableState = createChainableState({
835
+ state,
836
+ transaction: tr,
837
+ });
838
+ const handler = run({
839
+ editor,
840
+ state: chainableState,
841
+ from: Math.max(from - 1, 0),
842
+ to: to.b - 1,
843
+ rule,
844
+ pasteEvent: pasteEvt,
845
+ dropEvent,
846
+ });
847
+ if (!handler || !tr.steps.length) {
848
+ return;
849
+ }
850
+ dropEvent = typeof DragEvent !== 'undefined' ? new DragEvent('drop') : null;
851
+ pasteEvent = typeof ClipboardEvent !== 'undefined' ? new ClipboardEvent('paste') : null;
852
+ return tr;
853
+ };
807
854
  const plugins = rules.map(rule => {
808
855
  return new Plugin({
809
856
  // we register a global drag handler to track the current drag source element
@@ -841,38 +888,39 @@ function pasteRulesPlugin(props) {
841
888
  const transaction = transactions[0];
842
889
  const isPaste = transaction.getMeta('uiEvent') === 'paste' && !isPastedFromProseMirror;
843
890
  const isDrop = transaction.getMeta('uiEvent') === 'drop' && !isDroppedFromProseMirror;
844
- if (!isPaste && !isDrop) {
891
+ // if PasteRule is triggered by insertContent()
892
+ const simulatedPasteMeta = transaction.getMeta('applyPasteRules');
893
+ const isSimulatedPaste = !!simulatedPasteMeta;
894
+ if (!isPaste && !isDrop && !isSimulatedPaste) {
845
895
  return;
846
896
  }
847
- // stop if there is no changed range
897
+ // Handle simulated paste
898
+ if (isSimulatedPaste) {
899
+ const { from, text } = simulatedPasteMeta;
900
+ const to = from + text.length;
901
+ const pasteEvt = createClipboardPasteEvent(text);
902
+ return processEvent({
903
+ rule,
904
+ state,
905
+ from,
906
+ to: { b: to },
907
+ pasteEvt,
908
+ });
909
+ }
910
+ // handle actual paste/drop
848
911
  const from = oldState.doc.content.findDiffStart(state.doc.content);
849
912
  const to = oldState.doc.content.findDiffEnd(state.doc.content);
913
+ // stop if there is no changed range
850
914
  if (!isNumber(from) || !to || from === to.b) {
851
915
  return;
852
916
  }
853
- // build a chainable state
854
- // so we can use a single transaction for all paste rules
855
- const tr = state.tr;
856
- const chainableState = createChainableState({
857
- state,
858
- transaction: tr,
859
- });
860
- const handler = run({
861
- editor,
862
- state: chainableState,
863
- from: Math.max(from - 1, 0),
864
- to: to.b - 1,
917
+ return processEvent({
865
918
  rule,
866
- pasteEvent,
867
- dropEvent,
919
+ state,
920
+ from,
921
+ to,
922
+ pasteEvt: pasteEvent,
868
923
  });
869
- // stop if there are no changes
870
- if (!handler || !tr.steps.length) {
871
- return;
872
- }
873
- dropEvent = typeof DragEvent !== 'undefined' ? new DragEvent('drop') : null;
874
- pasteEvent = typeof ClipboardEvent !== 'undefined' ? new ClipboardEvent('paste') : null;
875
- return tr;
876
924
  },
877
925
  });
878
926
  });
@@ -1216,8 +1264,10 @@ function getTextBetween(startNode, range, options) {
1216
1264
  range,
1217
1265
  });
1218
1266
  }
1267
+ // do not descend into child nodes when there exists a serializer
1268
+ return false;
1219
1269
  }
1220
- else if (node.isText) {
1270
+ if (node.isText) {
1221
1271
  text += (_a = node === null || node === void 0 ? void 0 : node.text) === null || _a === void 0 ? void 0 : _a.slice(Math.max(from, pos) - pos, to - pos); // eslint-disable-line
1222
1272
  separated = false;
1223
1273
  }
@@ -1237,6 +1287,11 @@ function getTextSerializersFromSchema(schema) {
1237
1287
 
1238
1288
  const ClipboardTextSerializer = Extension.create({
1239
1289
  name: 'clipboardTextSerializer',
1290
+ addOptions() {
1291
+ return {
1292
+ blockSeparator: undefined,
1293
+ };
1294
+ },
1240
1295
  addProseMirrorPlugins() {
1241
1296
  return [
1242
1297
  new Plugin({
@@ -1252,6 +1307,9 @@ const ClipboardTextSerializer = Extension.create({
1252
1307
  const textSerializers = getTextSerializersFromSchema(schema);
1253
1308
  const range = { from, to };
1254
1309
  return getTextBetween(doc, range, {
1310
+ ...(this.options.blockSeparator !== undefined
1311
+ ? { blockSeparator: this.options.blockSeparator }
1312
+ : {}),
1255
1313
  textSerializers,
1256
1314
  });
1257
1315
  },
@@ -1658,6 +1716,8 @@ const insertContentAt = (position, value, options) => ({ tr, dispatch, editor })
1658
1716
  options = {
1659
1717
  parseOptions: {},
1660
1718
  updateSelection: true,
1719
+ applyInputRules: false,
1720
+ applyPasteRules: false,
1661
1721
  ...options,
1662
1722
  };
1663
1723
  const content = createNodeFromContent(value, editor.schema, {
@@ -1693,28 +1753,37 @@ const insertContentAt = (position, value, options) => ({ tr, dispatch, editor })
1693
1753
  to += 1;
1694
1754
  }
1695
1755
  }
1756
+ let newContent;
1696
1757
  // if there is only plain text we have to use `insertText`
1697
1758
  // because this will keep the current marks
1698
1759
  if (isOnlyTextContent) {
1699
1760
  // if value is string, we can use it directly
1700
1761
  // otherwise if it is an array, we have to join it
1701
1762
  if (Array.isArray(value)) {
1702
- tr.insertText(value.map(v => v.text || '').join(''), from, to);
1763
+ newContent = value.map(v => v.text || '').join('');
1703
1764
  }
1704
1765
  else if (typeof value === 'object' && !!value && !!value.text) {
1705
- tr.insertText(value.text, from, to);
1766
+ newContent = value.text;
1706
1767
  }
1707
1768
  else {
1708
- tr.insertText(value, from, to);
1769
+ newContent = value;
1709
1770
  }
1771
+ tr.insertText(newContent, from, to);
1710
1772
  }
1711
1773
  else {
1712
- tr.replaceWith(from, to, content);
1774
+ newContent = content;
1775
+ tr.replaceWith(from, to, newContent);
1713
1776
  }
1714
1777
  // set cursor at end of inserted content
1715
1778
  if (options.updateSelection) {
1716
1779
  selectionToInsertionEnd(tr, tr.steps.length - 1, -1);
1717
1780
  }
1781
+ if (options.applyInputRules) {
1782
+ tr.setMeta('applyInputRules', { from, text: newContent });
1783
+ }
1784
+ if (options.applyPasteRules) {
1785
+ tr.setMeta('applyPasteRules', { from, text: newContent });
1786
+ }
1718
1787
  }
1719
1788
  return true;
1720
1789
  };
@@ -3209,7 +3278,7 @@ const Keymap = Extension.create({
3209
3278
  const { selection, doc } = tr;
3210
3279
  const { empty, $anchor } = selection;
3211
3280
  const { pos, parent } = $anchor;
3212
- const $parentPos = $anchor.parent.isTextblock ? tr.doc.resolve(pos - 1) : $anchor;
3281
+ const $parentPos = $anchor.parent.isTextblock && pos > 0 ? tr.doc.resolve(pos - 1) : $anchor;
3213
3282
  const parentIsIsolating = $parentPos.parent.type.spec.isolating;
3214
3283
  const parentPos = $anchor.pos - $anchor.parentOffset;
3215
3284
  const isAtStart = (parentIsIsolating && $parentPos.parent.childCount === 1)
@@ -3325,7 +3394,7 @@ const Tabindex = Extension.create({
3325
3394
  },
3326
3395
  });
3327
3396
 
3328
- var extensions = /*#__PURE__*/Object.freeze({
3397
+ var index = /*#__PURE__*/Object.freeze({
3329
3398
  __proto__: null,
3330
3399
  ClipboardTextSerializer: ClipboardTextSerializer,
3331
3400
  Commands: Commands,
@@ -3429,7 +3498,7 @@ class NodePos {
3429
3498
  const children = [];
3430
3499
  this.node.content.forEach((node, offset) => {
3431
3500
  const isBlock = node.isBlock && !node.isTextblock;
3432
- const targetPos = this.pos + offset + (isBlock ? 0 : 1);
3501
+ const targetPos = this.pos + offset + 1;
3433
3502
  const $pos = this.resolvedPos.doc.resolve(targetPos);
3434
3503
  if (!isBlock && $pos.depth <= this.depth) {
3435
3504
  return;
@@ -3478,7 +3547,7 @@ class NodePos {
3478
3547
  querySelectorAll(selector, attributes = {}, firstItemOnly = false) {
3479
3548
  let nodes = [];
3480
3549
  // iterate through children recursively finding all nodes which match the selector with the node name
3481
- if (this.isBlock || !this.children || this.children.length === 0) {
3550
+ if (!this.children || this.children.length === 0) {
3482
3551
  return nodes;
3483
3552
  }
3484
3553
  this.children.forEach(childPos => {
@@ -3615,6 +3684,7 @@ class Editor extends EventEmitter {
3615
3684
  editable: true,
3616
3685
  editorProps: {},
3617
3686
  parseOptions: {},
3687
+ coreExtensionOptions: {},
3618
3688
  enableInputRules: true,
3619
3689
  enablePasteRules: true,
3620
3690
  enableCoreExtensions: true,
@@ -3760,7 +3830,17 @@ class Editor extends EventEmitter {
3760
3830
  * Creates an extension manager.
3761
3831
  */
3762
3832
  createExtensionManager() {
3763
- const coreExtensions = this.options.enableCoreExtensions ? Object.values(extensions) : [];
3833
+ var _a, _b;
3834
+ const coreExtensions = this.options.enableCoreExtensions ? [
3835
+ Editable,
3836
+ ClipboardTextSerializer.configure({
3837
+ blockSeparator: (_b = (_a = this.options.coreExtensionOptions) === null || _a === void 0 ? void 0 : _a.clipboardTextSerializer) === null || _b === void 0 ? void 0 : _b.blockSeparator,
3838
+ }),
3839
+ Commands,
3840
+ FocusEvents,
3841
+ Keymap,
3842
+ Tabindex,
3843
+ ] : [];
3764
3844
  const allExtensions = [...coreExtensions, ...this.options.extensions].filter(extension => {
3765
3845
  return ['extension', 'node', 'mark'].includes(extension === null || extension === void 0 ? void 0 : extension.type);
3766
3846
  });
@@ -4611,5 +4691,5 @@ class Tracker {
4611
4691
  }
4612
4692
  }
4613
4693
 
4614
- export { CommandManager, Editor, Extension, InputRule, Mark, Node, NodePos, NodeView, PasteRule, Tracker, callOrReturn, combineTransactionSteps, createChainableState, createDocument, createNodeFromContent, createStyleTag, defaultBlockAt, deleteProps, elementFromString, escapeForRegEx, extensions, findChildren, findChildrenInRange, findDuplicates, findParentNode, findParentNodeClosestToPos, fromString, generateHTML, generateJSON, generateText, getAttributes, getAttributesFromExtensions, getChangedRanges, getDebugJSON, getExtensionField, getHTMLFromFragment, getMarkAttributes, getMarkRange, getMarkType, getMarksBetween, getNodeAtPosition, getNodeAttributes, getNodeType, getRenderedAttributes, getSchema, getSchemaByResolvedExtensions, getSchemaTypeByName, getSchemaTypeNameByName, getSplittedAttributes, getText, getTextBetween, getTextContentFromNodes, getTextSerializersFromSchema, injectExtensionAttributesToParseRule, inputRulesPlugin, isActive, isAtEndOfNode, isAtStartOfNode, isEmptyObject, isExtensionRulesEnabled, isFunction, isList, isMacOS, isMarkActive, isNodeActive, isNodeEmpty, isNodeSelection, isNumber, isPlainObject, isRegExp, isString, isTextSelection, isiOS, markInputRule, markPasteRule, mergeAttributes, mergeDeep, minMax, nodeInputRule, nodePasteRule, objectIncludes, pasteRulesPlugin, posToDOMRect, removeDuplicates, resolveFocusPosition, selectionToInsertionEnd, splitExtensions, textInputRule, textPasteRule, textblockTypeInputRule, wrappingInputRule };
4694
+ export { CommandManager, Editor, Extension, InputRule, Mark, Node, NodePos, NodeView, PasteRule, Tracker, callOrReturn, combineTransactionSteps, createChainableState, createDocument, createNodeFromContent, createStyleTag, defaultBlockAt, deleteProps, elementFromString, escapeForRegEx, index as extensions, findChildren, findChildrenInRange, findDuplicates, findParentNode, findParentNodeClosestToPos, fromString, generateHTML, generateJSON, generateText, getAttributes, getAttributesFromExtensions, getChangedRanges, getDebugJSON, getExtensionField, getHTMLFromFragment, getMarkAttributes, getMarkRange, getMarkType, getMarksBetween, getNodeAtPosition, getNodeAttributes, getNodeType, getRenderedAttributes, getSchema, getSchemaByResolvedExtensions, getSchemaTypeByName, getSchemaTypeNameByName, getSplittedAttributes, getText, getTextBetween, getTextContentFromNodes, getTextSerializersFromSchema, injectExtensionAttributesToParseRule, inputRulesPlugin, isActive, isAtEndOfNode, isAtStartOfNode, isEmptyObject, isExtensionRulesEnabled, isFunction, isList, isMacOS, isMarkActive, isNodeActive, isNodeEmpty, isNodeSelection, isNumber, isPlainObject, isRegExp, isString, isTextSelection, isiOS, markInputRule, markPasteRule, mergeAttributes, mergeDeep, minMax, nodeInputRule, nodePasteRule, objectIncludes, pasteRulesPlugin, posToDOMRect, removeDuplicates, resolveFocusPosition, selectionToInsertionEnd, splitExtensions, textInputRule, textPasteRule, textblockTypeInputRule, wrappingInputRule };
4615
4695
  //# sourceMappingURL=index.js.map