@tiptap/core 2.4.0 → 2.5.0-beta.1
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 +96 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +96 -16
- package/dist/index.js.map +1 -1
- package/dist/index.umd.js +96 -16
- package/dist/index.umd.js.map +1 -1
- package/dist/packages/core/src/Editor.d.ts +4 -2
- package/dist/packages/core/src/EventEmitter.d.ts +2 -2
- package/dist/packages/core/src/commands/insertContentAt.d.ts +10 -0
- package/dist/packages/core/src/commands/setContent.d.ts +10 -1
- package/dist/packages/core/src/helpers/createDocument.d.ts +3 -1
- package/dist/packages/core/src/helpers/createNodeFromContent.d.ts +1 -0
- package/dist/packages/core/src/types.d.ts +21 -0
- package/package.json +2 -2
- package/src/Editor.ts +45 -4
- package/src/EventEmitter.ts +2 -2
- package/src/commands/insertContentAt.ts +26 -6
- package/src/commands/joinItemBackward.ts +4 -2
- package/src/commands/setContent.ts +21 -3
- package/src/helpers/createDocument.ts +6 -1
- package/src/helpers/createNodeFromContent.ts +42 -2
- package/src/inputRules/nodeInputRule.ts +3 -1
- package/src/types.ts +21 -0
package/dist/index.cjs
CHANGED
|
@@ -1774,15 +1774,49 @@ function createNodeFromContent(content, schema, options) {
|
|
|
1774
1774
|
return schema.nodeFromJSON(content);
|
|
1775
1775
|
}
|
|
1776
1776
|
catch (error) {
|
|
1777
|
+
if (options.errorOnInvalidContent) {
|
|
1778
|
+
throw new Error('[tiptap error]: Invalid JSON content', { cause: error });
|
|
1779
|
+
}
|
|
1777
1780
|
console.warn('[tiptap warn]: Invalid content.', 'Passed value:', content, 'Error:', error);
|
|
1778
1781
|
return createNodeFromContent('', schema, options);
|
|
1779
1782
|
}
|
|
1780
1783
|
}
|
|
1781
1784
|
if (isTextContent) {
|
|
1782
|
-
|
|
1783
|
-
|
|
1785
|
+
let schemaToUse = schema;
|
|
1786
|
+
let hasInvalidContent = false;
|
|
1787
|
+
// Only ever check for invalid content if we're supposed to throw an error
|
|
1788
|
+
if (options.errorOnInvalidContent) {
|
|
1789
|
+
schemaToUse = new model.Schema({
|
|
1790
|
+
topNode: schema.spec.topNode,
|
|
1791
|
+
marks: schema.spec.marks,
|
|
1792
|
+
// Prosemirror's schemas are executed such that: the last to execute, matches last
|
|
1793
|
+
// This means that we can add a catch-all node at the end of the schema to catch any content that we don't know how to handle
|
|
1794
|
+
nodes: schema.spec.nodes.append({
|
|
1795
|
+
__tiptap__private__unknown__catch__all__node: {
|
|
1796
|
+
content: 'inline*',
|
|
1797
|
+
group: 'block',
|
|
1798
|
+
parseDOM: [
|
|
1799
|
+
{
|
|
1800
|
+
tag: '*',
|
|
1801
|
+
getAttrs: () => {
|
|
1802
|
+
// If this is ever called, we know that the content has something that we don't know how to handle in the schema
|
|
1803
|
+
hasInvalidContent = true;
|
|
1804
|
+
return null;
|
|
1805
|
+
},
|
|
1806
|
+
},
|
|
1807
|
+
],
|
|
1808
|
+
},
|
|
1809
|
+
}),
|
|
1810
|
+
});
|
|
1811
|
+
}
|
|
1812
|
+
const parser = model.DOMParser.fromSchema(schemaToUse);
|
|
1813
|
+
const response = options.slice
|
|
1784
1814
|
? parser.parseSlice(elementFromString(content), options.parseOptions).content
|
|
1785
1815
|
: parser.parse(elementFromString(content), options.parseOptions);
|
|
1816
|
+
if (options.errorOnInvalidContent && hasInvalidContent) {
|
|
1817
|
+
throw new Error('[tiptap error]: Invalid HTML content');
|
|
1818
|
+
}
|
|
1819
|
+
return response;
|
|
1786
1820
|
}
|
|
1787
1821
|
return createNodeFromContent('', schema, options);
|
|
1788
1822
|
}
|
|
@@ -1811,6 +1845,7 @@ const isFragment = (nodeOrFragment) => {
|
|
|
1811
1845
|
return nodeOrFragment.toString().startsWith('<');
|
|
1812
1846
|
};
|
|
1813
1847
|
const insertContentAt = (position, value, options) => ({ tr, dispatch, editor }) => {
|
|
1848
|
+
var _a;
|
|
1814
1849
|
if (dispatch) {
|
|
1815
1850
|
options = {
|
|
1816
1851
|
parseOptions: {},
|
|
@@ -1819,12 +1854,19 @@ const insertContentAt = (position, value, options) => ({ tr, dispatch, editor })
|
|
|
1819
1854
|
applyPasteRules: false,
|
|
1820
1855
|
...options,
|
|
1821
1856
|
};
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1857
|
+
let content;
|
|
1858
|
+
try {
|
|
1859
|
+
content = createNodeFromContent(value, editor.schema, {
|
|
1860
|
+
parseOptions: {
|
|
1861
|
+
preserveWhitespace: 'full',
|
|
1862
|
+
...options.parseOptions,
|
|
1863
|
+
},
|
|
1864
|
+
errorOnInvalidContent: (_a = options.errorOnInvalidContent) !== null && _a !== void 0 ? _a : editor.options.enableContentCheck,
|
|
1865
|
+
});
|
|
1866
|
+
}
|
|
1867
|
+
catch (e) {
|
|
1868
|
+
return false;
|
|
1869
|
+
}
|
|
1828
1870
|
// don’t dispatch an empty fragment because this can lead to strange errors
|
|
1829
1871
|
if (content.toString() === '<>') {
|
|
1830
1872
|
return true;
|
|
@@ -1900,7 +1942,7 @@ const joinForward = () => ({ state, dispatch }) => {
|
|
|
1900
1942
|
return commands$1.joinForward(state, dispatch);
|
|
1901
1943
|
};
|
|
1902
1944
|
|
|
1903
|
-
const joinItemBackward = () => ({
|
|
1945
|
+
const joinItemBackward = () => ({ state, dispatch, tr, }) => {
|
|
1904
1946
|
try {
|
|
1905
1947
|
const point = transform.joinPoint(state.doc, state.selection.$from.pos, -1);
|
|
1906
1948
|
if (point === null || point === undefined) {
|
|
@@ -1912,7 +1954,7 @@ const joinItemBackward = () => ({ tr, state, dispatch, }) => {
|
|
|
1912
1954
|
}
|
|
1913
1955
|
return true;
|
|
1914
1956
|
}
|
|
1915
|
-
catch {
|
|
1957
|
+
catch (e) {
|
|
1916
1958
|
return false;
|
|
1917
1959
|
}
|
|
1918
1960
|
};
|
|
@@ -2188,13 +2230,26 @@ const selectTextblockStart = () => ({ state, dispatch }) => {
|
|
|
2188
2230
|
* @param parseOptions Options for the parser
|
|
2189
2231
|
* @returns The created Prosemirror document node
|
|
2190
2232
|
*/
|
|
2191
|
-
function createDocument(content, schema, parseOptions = {}) {
|
|
2192
|
-
return createNodeFromContent(content, schema, {
|
|
2233
|
+
function createDocument(content, schema, parseOptions = {}, options = {}) {
|
|
2234
|
+
return createNodeFromContent(content, schema, {
|
|
2235
|
+
slice: false,
|
|
2236
|
+
parseOptions,
|
|
2237
|
+
errorOnInvalidContent: options.errorOnInvalidContent,
|
|
2238
|
+
});
|
|
2193
2239
|
}
|
|
2194
2240
|
|
|
2195
|
-
const setContent = (content, emitUpdate = false, parseOptions = {}) => ({ tr, editor, dispatch }) => {
|
|
2241
|
+
const setContent = (content, emitUpdate = false, parseOptions = {}, options = {}) => ({ tr, editor, dispatch }) => {
|
|
2242
|
+
var _a;
|
|
2196
2243
|
const { doc } = tr;
|
|
2197
|
-
|
|
2244
|
+
let document;
|
|
2245
|
+
try {
|
|
2246
|
+
document = createDocument(content, editor.schema, parseOptions, {
|
|
2247
|
+
errorOnInvalidContent: (_a = options.errorOnInvalidContent) !== null && _a !== void 0 ? _a : editor.options.enableContentCheck,
|
|
2248
|
+
});
|
|
2249
|
+
}
|
|
2250
|
+
catch (e) {
|
|
2251
|
+
return false;
|
|
2252
|
+
}
|
|
2198
2253
|
if (dispatch) {
|
|
2199
2254
|
tr.replaceWith(0, doc.content.size, document).setMeta('preventUpdate', !emitUpdate);
|
|
2200
2255
|
}
|
|
@@ -3883,6 +3938,7 @@ class Editor extends EventEmitter {
|
|
|
3883
3938
|
enableInputRules: true,
|
|
3884
3939
|
enablePasteRules: true,
|
|
3885
3940
|
enableCoreExtensions: true,
|
|
3941
|
+
enableContentCheck: false,
|
|
3886
3942
|
onBeforeCreate: () => null,
|
|
3887
3943
|
onCreate: () => null,
|
|
3888
3944
|
onUpdate: () => null,
|
|
@@ -3891,6 +3947,7 @@ class Editor extends EventEmitter {
|
|
|
3891
3947
|
onFocus: () => null,
|
|
3892
3948
|
onBlur: () => null,
|
|
3893
3949
|
onDestroy: () => null,
|
|
3950
|
+
onContentError: ({ error }) => { throw error; },
|
|
3894
3951
|
};
|
|
3895
3952
|
this.isCapturingTransaction = false;
|
|
3896
3953
|
this.capturedTransaction = null;
|
|
@@ -3900,6 +3957,7 @@ class Editor extends EventEmitter {
|
|
|
3900
3957
|
this.createSchema();
|
|
3901
3958
|
this.on('beforeCreate', this.options.onBeforeCreate);
|
|
3902
3959
|
this.emit('beforeCreate', { editor: this });
|
|
3960
|
+
this.on('contentError', this.options.onContentError);
|
|
3903
3961
|
this.createView();
|
|
3904
3962
|
this.injectCSS();
|
|
3905
3963
|
this.on('create', this.options.onCreate);
|
|
@@ -4059,7 +4117,28 @@ class Editor extends EventEmitter {
|
|
|
4059
4117
|
* Creates a ProseMirror view.
|
|
4060
4118
|
*/
|
|
4061
4119
|
createView() {
|
|
4062
|
-
|
|
4120
|
+
let doc;
|
|
4121
|
+
try {
|
|
4122
|
+
doc = createDocument(this.options.content, this.schema, this.options.parseOptions, { errorOnInvalidContent: this.options.enableContentCheck });
|
|
4123
|
+
}
|
|
4124
|
+
catch (e) {
|
|
4125
|
+
if (!(e instanceof Error) || !['[tiptap error]: Invalid JSON content', '[tiptap error]: Invalid HTML content'].includes(e.message)) {
|
|
4126
|
+
// Not the content error we were expecting
|
|
4127
|
+
throw e;
|
|
4128
|
+
}
|
|
4129
|
+
this.emit('contentError', {
|
|
4130
|
+
editor: this,
|
|
4131
|
+
error: e,
|
|
4132
|
+
disableCollaboration: () => {
|
|
4133
|
+
// To avoid syncing back invalid content, reinitialize the extensions without the collaboration extension
|
|
4134
|
+
this.options.extensions = this.options.extensions.filter(extension => extension.name !== 'collaboration');
|
|
4135
|
+
// Restart the initialization process by recreating the extension manager with the new set of extensions
|
|
4136
|
+
this.createExtensionManager();
|
|
4137
|
+
},
|
|
4138
|
+
});
|
|
4139
|
+
// Content is invalid, but attempt to create it anyway, stripping out the invalid parts
|
|
4140
|
+
doc = createDocument(this.options.content, this.schema, this.options.parseOptions, { errorOnInvalidContent: false });
|
|
4141
|
+
}
|
|
4063
4142
|
const selection = resolveFocusPosition(doc, this.options.autofocus);
|
|
4064
4143
|
this.view = new view.EditorView(this.options.element, {
|
|
4065
4144
|
...this.options.editorProps,
|
|
@@ -4320,7 +4399,8 @@ function nodeInputRule(config) {
|
|
|
4320
4399
|
tr.replaceWith(matchStart, end, newNode);
|
|
4321
4400
|
}
|
|
4322
4401
|
else if (match[0]) {
|
|
4323
|
-
|
|
4402
|
+
const insertionStart = config.type.isInline ? start : start - 1;
|
|
4403
|
+
tr.insert(insertionStart, config.type.create(attributes)).delete(tr.mapping.map(start), tr.mapping.map(end));
|
|
4324
4404
|
}
|
|
4325
4405
|
tr.scrollIntoView();
|
|
4326
4406
|
},
|