@tiptap/core 2.5.6 → 2.5.8
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 +53 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +53 -17
- package/dist/index.js.map +1 -1
- package/dist/index.umd.js +53 -17
- package/dist/index.umd.js.map +1 -1
- package/dist/packages/core/src/helpers/isNodeEmpty.d.ts +7 -1
- package/package.json +3 -3
- package/src/Editor.ts +2 -0
- package/src/commands/insertContentAt.ts +7 -0
- package/src/helpers/createNodeFromContent.ts +21 -13
- package/src/helpers/getTextContentFromNodes.ts +1 -1
- package/src/helpers/isNodeEmpty.ts +34 -4
|
@@ -1,2 +1,8 @@
|
|
|
1
1
|
import { Node as ProseMirrorNode } from '@tiptap/pm/model';
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Returns true if the given node is empty.
|
|
4
|
+
* When `checkChildren` is true (default), it will also check if all children are empty.
|
|
5
|
+
*/
|
|
6
|
+
export declare function isNodeEmpty(node: ProseMirrorNode, { checkChildren }?: {
|
|
7
|
+
checkChildren: boolean;
|
|
8
|
+
}): boolean;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tiptap/core",
|
|
3
3
|
"description": "headless rich text editor",
|
|
4
|
-
"version": "2.5.
|
|
4
|
+
"version": "2.5.8",
|
|
5
5
|
"homepage": "https://tiptap.dev",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"tiptap",
|
|
@@ -32,10 +32,10 @@
|
|
|
32
32
|
"dist"
|
|
33
33
|
],
|
|
34
34
|
"devDependencies": {
|
|
35
|
-
"@tiptap/pm": "^2.5.
|
|
35
|
+
"@tiptap/pm": "^2.5.8"
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|
|
38
|
-
"@tiptap/pm": "^2.5.
|
|
38
|
+
"@tiptap/pm": "^2.5.8"
|
|
39
39
|
},
|
|
40
40
|
"repository": {
|
|
41
41
|
"type": "git",
|
package/src/Editor.ts
CHANGED
|
@@ -39,6 +39,7 @@ import { isFunction } from './utilities/isFunction.js'
|
|
|
39
39
|
|
|
40
40
|
export * as extensions from './extensions/index.js'
|
|
41
41
|
|
|
42
|
+
// @ts-ignore
|
|
42
43
|
export interface TiptapEditorHTMLElement extends HTMLElement {
|
|
43
44
|
editor?: Editor
|
|
44
45
|
}
|
|
@@ -340,6 +341,7 @@ export class Editor extends EventEmitter<EditorEvents> {
|
|
|
340
341
|
|
|
341
342
|
// Let’s store the editor instance in the DOM element.
|
|
342
343
|
// So we’ll have access to it for tests.
|
|
344
|
+
// @ts-ignore
|
|
343
345
|
const dom = this.view.dom as TiptapEditorHTMLElement
|
|
344
346
|
|
|
345
347
|
dom.editor = this
|
|
@@ -81,6 +81,13 @@ export const insertContentAt: RawCommands['insertContentAt'] = (position, value,
|
|
|
81
81
|
errorOnInvalidContent: options.errorOnInvalidContent ?? editor.options.enableContentCheck,
|
|
82
82
|
})
|
|
83
83
|
} catch (e) {
|
|
84
|
+
editor.emit('contentError', {
|
|
85
|
+
editor,
|
|
86
|
+
error: e as Error,
|
|
87
|
+
disableCollaboration: () => {
|
|
88
|
+
console.error('[tiptap error]: Unable to disable collaboration at this point in time')
|
|
89
|
+
},
|
|
90
|
+
})
|
|
84
91
|
return false
|
|
85
92
|
}
|
|
86
93
|
|
|
@@ -58,13 +58,14 @@ export function createNodeFromContent(
|
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
if (isTextContent) {
|
|
61
|
-
let schemaToUse = schema
|
|
62
|
-
let hasInvalidContent = false
|
|
63
|
-
let invalidContent = ''
|
|
64
61
|
|
|
65
|
-
//
|
|
62
|
+
// Check for invalid content
|
|
66
63
|
if (options.errorOnInvalidContent) {
|
|
67
|
-
|
|
64
|
+
let hasInvalidContent = false
|
|
65
|
+
let invalidContent = ''
|
|
66
|
+
|
|
67
|
+
// A copy of the current schema with a catch-all node at the end
|
|
68
|
+
const contentCheckSchema = new Schema({
|
|
68
69
|
topNode: schema.spec.topNode,
|
|
69
70
|
marks: schema.spec.marks,
|
|
70
71
|
// Prosemirror's schemas are executed such that: the last to execute, matches last
|
|
@@ -88,19 +89,26 @@ export function createNodeFromContent(
|
|
|
88
89
|
},
|
|
89
90
|
}),
|
|
90
91
|
})
|
|
91
|
-
}
|
|
92
92
|
|
|
93
|
-
|
|
93
|
+
if (options.slice) {
|
|
94
|
+
DOMParser.fromSchema(contentCheckSchema).parseSlice(elementFromString(content), options.parseOptions)
|
|
95
|
+
} else {
|
|
96
|
+
DOMParser.fromSchema(contentCheckSchema).parse(elementFromString(content), options.parseOptions)
|
|
97
|
+
}
|
|
94
98
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
99
|
+
if (options.errorOnInvalidContent && hasInvalidContent) {
|
|
100
|
+
throw new Error('[tiptap error]: Invalid HTML content', { cause: new Error(`Invalid element found: ${invalidContent}`) })
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const parser = DOMParser.fromSchema(schema)
|
|
98
105
|
|
|
99
|
-
if (options.
|
|
100
|
-
|
|
106
|
+
if (options.slice) {
|
|
107
|
+
return parser.parseSlice(elementFromString(content), options.parseOptions).content
|
|
101
108
|
}
|
|
102
109
|
|
|
103
|
-
return
|
|
110
|
+
return parser.parse(elementFromString(content), options.parseOptions)
|
|
111
|
+
|
|
104
112
|
}
|
|
105
113
|
|
|
106
114
|
return createNodeFromContent('', schema, options)
|
|
@@ -24,7 +24,7 @@ export const getTextContentFromNodes = ($from: ResolvedPos, maxMatch = 500) => {
|
|
|
24
24
|
|| node.textContent
|
|
25
25
|
|| '%leaf%'
|
|
26
26
|
|
|
27
|
-
textBefore += node.isAtom ? chunk : chunk.slice(0, Math.max(0, sliceEndPos - pos))
|
|
27
|
+
textBefore += node.isAtom && !node.isText ? chunk : chunk.slice(0, Math.max(0, sliceEndPos - pos))
|
|
28
28
|
},
|
|
29
29
|
)
|
|
30
30
|
|
|
@@ -1,11 +1,41 @@
|
|
|
1
1
|
import { Node as ProseMirrorNode } from '@tiptap/pm/model'
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Returns true if the given node is empty.
|
|
5
|
+
* When `checkChildren` is true (default), it will also check if all children are empty.
|
|
6
|
+
*/
|
|
7
|
+
export function isNodeEmpty(
|
|
8
|
+
node: ProseMirrorNode,
|
|
9
|
+
{ checkChildren }: { checkChildren: boolean } = { checkChildren: true },
|
|
10
|
+
): boolean {
|
|
11
|
+
if (node.isText) {
|
|
12
|
+
return !node.text
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (node.content.childCount === 0) {
|
|
16
|
+
return true
|
|
17
|
+
}
|
|
5
18
|
|
|
6
|
-
if (
|
|
19
|
+
if (node.isLeaf) {
|
|
7
20
|
return false
|
|
8
21
|
}
|
|
9
22
|
|
|
10
|
-
|
|
23
|
+
if (checkChildren) {
|
|
24
|
+
let hasSameContent = true
|
|
25
|
+
|
|
26
|
+
node.content.forEach(childNode => {
|
|
27
|
+
if (hasSameContent === false) {
|
|
28
|
+
// Exit early for perf
|
|
29
|
+
return
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (!isNodeEmpty(childNode)) {
|
|
33
|
+
hasSameContent = false
|
|
34
|
+
}
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
return hasSameContent
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return false
|
|
11
41
|
}
|