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