@tiptap/core 2.5.5 → 2.5.7

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
@@ -1779,12 +1779,12 @@ function createNodeFromContent(content, schema, options) {
1779
1779
  }
1780
1780
  }
1781
1781
  if (isTextContent) {
1782
- let schemaToUse = schema;
1783
- let hasInvalidContent = false;
1784
- let invalidContent = '';
1785
- // Only ever check for invalid content if we're supposed to throw an error
1782
+ // Check for invalid content
1786
1783
  if (options.errorOnInvalidContent) {
1787
- schemaToUse = new model.Schema({
1784
+ let hasInvalidContent = false;
1785
+ let invalidContent = '';
1786
+ // A copy of the current schema with a catch-all node at the end
1787
+ const contentCheckSchema = new model.Schema({
1788
1788
  topNode: schema.spec.topNode,
1789
1789
  marks: schema.spec.marks,
1790
1790
  // Prosemirror's schemas are executed such that: the last to execute, matches last
@@ -1808,15 +1808,21 @@ function createNodeFromContent(content, schema, options) {
1808
1808
  },
1809
1809
  }),
1810
1810
  });
1811
+ if (options.slice) {
1812
+ model.DOMParser.fromSchema(contentCheckSchema).parseSlice(elementFromString(content), options.parseOptions);
1813
+ }
1814
+ else {
1815
+ model.DOMParser.fromSchema(contentCheckSchema).parse(elementFromString(content), options.parseOptions);
1816
+ }
1817
+ if (options.errorOnInvalidContent && hasInvalidContent) {
1818
+ throw new Error('[tiptap error]: Invalid HTML content', { cause: new Error(`Invalid element found: ${invalidContent}`) });
1819
+ }
1811
1820
  }
1812
- const parser = model.DOMParser.fromSchema(schemaToUse);
1813
- const response = options.slice
1814
- ? parser.parseSlice(elementFromString(content), options.parseOptions).content
1815
- : parser.parse(elementFromString(content), options.parseOptions);
1816
- if (options.errorOnInvalidContent && hasInvalidContent) {
1817
- throw new Error('[tiptap error]: Invalid HTML content', { cause: new Error(`Invalid element found: ${invalidContent}`) });
1821
+ const parser = model.DOMParser.fromSchema(schema);
1822
+ if (options.slice) {
1823
+ return parser.parseSlice(elementFromString(content), options.parseOptions).content;
1818
1824
  }
1819
- return response;
1825
+ return parser.parse(elementFromString(content), options.parseOptions);
1820
1826
  }
1821
1827
  return createNodeFromContent('', schema, options);
1822
1828
  }
@@ -1865,6 +1871,13 @@ const insertContentAt = (position, value, options) => ({ tr, dispatch, editor })
1865
1871
  });
1866
1872
  }
1867
1873
  catch (e) {
1874
+ editor.emit('contentError', {
1875
+ editor,
1876
+ error: e,
1877
+ disableCollaboration: () => {
1878
+ console.error('[tiptap error]: Unable to disable collaboration at this point in time');
1879
+ },
1880
+ });
1868
1881
  return false;
1869
1882
  }
1870
1883
  let { from, to } = typeof position === 'number' ? { from: position, to: position } : { from: position.from, to: position.to };
@@ -2815,12 +2828,34 @@ function isList(name, extensions) {
2815
2828
  return group.split(' ').includes('list');
2816
2829
  }
2817
2830
 
2818
- function isNodeEmpty(node) {
2819
- const defaultContent = node.type.createAndFill();
2820
- if (!defaultContent) {
2831
+ /**
2832
+ * Returns true if the given node is empty.
2833
+ * When `checkChildren` is true (default), it will also check if all children are empty.
2834
+ */
2835
+ function isNodeEmpty(node, { checkChildren } = { checkChildren: true }) {
2836
+ if (node.isText) {
2837
+ return !node.text;
2838
+ }
2839
+ if (node.content.childCount === 0) {
2840
+ return true;
2841
+ }
2842
+ if (node.isLeaf) {
2821
2843
  return false;
2822
2844
  }
2823
- return node.eq(defaultContent);
2845
+ if (checkChildren) {
2846
+ let hasSameContent = true;
2847
+ node.content.forEach(childNode => {
2848
+ if (hasSameContent === false) {
2849
+ // Exit early for perf
2850
+ return;
2851
+ }
2852
+ if (!isNodeEmpty(childNode)) {
2853
+ hasSameContent = false;
2854
+ }
2855
+ });
2856
+ return hasSameContent;
2857
+ }
2858
+ return false;
2824
2859
  }
2825
2860
 
2826
2861
  function isNodeSelection(value) {
@@ -3018,15 +3053,24 @@ const splitBlock = ({ keepMarks = true } = {}) => ({ tr, state: state$1, dispatc
3018
3053
  if (!$from.parent.isBlock) {
3019
3054
  return false;
3020
3055
  }
3021
- if (dispatch) {
3022
- const atEnd = $to.parentOffset === $to.parent.content.size;
3023
- if (selection instanceof state.TextSelection) {
3024
- tr.deleteSelection();
3025
- }
3026
- const deflt = $from.depth === 0
3027
- ? undefined
3028
- : defaultBlockAt($from.node(-1).contentMatchAt($from.indexAfter(-1)));
3029
- let types = atEnd && deflt
3056
+ const atEnd = $to.parentOffset === $to.parent.content.size;
3057
+ const deflt = $from.depth === 0
3058
+ ? undefined
3059
+ : defaultBlockAt($from.node(-1).contentMatchAt($from.indexAfter(-1)));
3060
+ let types = atEnd && deflt
3061
+ ? [
3062
+ {
3063
+ type: deflt,
3064
+ attrs: newAttributes,
3065
+ },
3066
+ ]
3067
+ : undefined;
3068
+ let can = transform.canSplit(tr.doc, tr.mapping.map($from.pos), 1, types);
3069
+ if (!types
3070
+ && !can
3071
+ && transform.canSplit(tr.doc, tr.mapping.map($from.pos), 1, deflt ? [{ type: deflt }] : undefined)) {
3072
+ can = true;
3073
+ types = deflt
3030
3074
  ? [
3031
3075
  {
3032
3076
  type: deflt,
@@ -3034,21 +3078,12 @@ const splitBlock = ({ keepMarks = true } = {}) => ({ tr, state: state$1, dispatc
3034
3078
  },
3035
3079
  ]
3036
3080
  : undefined;
3037
- let can = transform.canSplit(tr.doc, tr.mapping.map($from.pos), 1, types);
3038
- if (!types
3039
- && !can
3040
- && transform.canSplit(tr.doc, tr.mapping.map($from.pos), 1, deflt ? [{ type: deflt }] : undefined)) {
3041
- can = true;
3042
- types = deflt
3043
- ? [
3044
- {
3045
- type: deflt,
3046
- attrs: newAttributes,
3047
- },
3048
- ]
3049
- : undefined;
3050
- }
3081
+ }
3082
+ if (dispatch) {
3051
3083
  if (can) {
3084
+ if (selection instanceof state.TextSelection) {
3085
+ tr.deleteSelection();
3086
+ }
3052
3087
  tr.split(tr.mapping.map($from.pos), 1, types);
3053
3088
  if (deflt && !atEnd && !$from.parentOffset && $from.parent.type !== deflt) {
3054
3089
  const first = tr.mapping.map($from.before());
@@ -3063,7 +3098,7 @@ const splitBlock = ({ keepMarks = true } = {}) => ({ tr, state: state$1, dispatc
3063
3098
  }
3064
3099
  tr.scrollIntoView();
3065
3100
  }
3066
- return true;
3101
+ return can;
3067
3102
  };
3068
3103
 
3069
3104
  const splitListItem = typeOrName => ({ tr, state: state$1, dispatch, editor, }) => {
@@ -4162,6 +4197,7 @@ class Editor extends EventEmitter {
4162
4197
  this.prependClass();
4163
4198
  // Let’s store the editor instance in the DOM element.
4164
4199
  // So we’ll have access to it for tests.
4200
+ // @ts-ignore
4165
4201
  const dom = this.view.dom;
4166
4202
  dom.editor = this;
4167
4203
  }
@@ -4169,6 +4205,9 @@ class Editor extends EventEmitter {
4169
4205
  * Creates all node views.
4170
4206
  */
4171
4207
  createNodeViews() {
4208
+ if (this.view.isDestroyed) {
4209
+ return;
4210
+ }
4172
4211
  this.view.setProps({
4173
4212
  nodeViews: this.extensionManager.nodeViews,
4174
4213
  });