@tiptap/core 3.0.0-next.3 → 3.0.0-next.4
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/LICENSE.md +21 -0
- package/README.md +5 -1
- package/dist/index.cjs +2402 -2540
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1355 -1273
- package/dist/index.d.ts +1355 -1273
- package/dist/index.js +2408 -2562
- package/dist/index.js.map +1 -1
- package/package.json +9 -5
- package/src/CommandManager.ts +2 -9
- package/src/Editor.ts +87 -72
- package/src/EventEmitter.ts +7 -10
- package/src/Extension.ts +8 -14
- package/src/ExtensionManager.ts +26 -125
- package/src/InputRule.ts +35 -48
- package/src/Mark.ts +9 -9
- package/src/Node.ts +9 -9
- package/src/NodePos.ts +1 -3
- package/src/NodeView.ts +10 -20
- package/src/PasteRule.ts +43 -55
- package/src/Tracker.ts +7 -9
- package/src/commands/blur.ts +14 -12
- package/src/commands/clearContent.ts +6 -4
- package/src/commands/clearNodes.ts +32 -30
- package/src/commands/command.ts +1 -1
- package/src/commands/createParagraphNear.ts +5 -3
- package/src/commands/cut.ts +12 -10
- package/src/commands/deleteCurrentNode.ts +23 -21
- package/src/commands/deleteNode.ts +18 -16
- package/src/commands/deleteRange.ts +10 -8
- package/src/commands/deleteSelection.ts +5 -3
- package/src/commands/enter.ts +6 -4
- package/src/commands/exitCode.ts +5 -3
- package/src/commands/extendMarkRange.ts +14 -12
- package/src/commands/first.ts +2 -4
- package/src/commands/focus.ts +45 -48
- package/src/commands/forEach.ts +2 -2
- package/src/commands/insertContent.ts +12 -14
- package/src/commands/insertContentAt.ts +101 -98
- package/src/commands/join.ts +20 -12
- package/src/commands/joinItemBackward.ts +16 -18
- package/src/commands/joinItemForward.ts +16 -18
- package/src/commands/joinTextblockBackward.ts +5 -3
- package/src/commands/joinTextblockForward.ts +5 -3
- package/src/commands/keyboardShortcut.ts +29 -34
- package/src/commands/lift.ts +10 -8
- package/src/commands/liftEmptyBlock.ts +6 -4
- package/src/commands/liftListItem.ts +6 -4
- package/src/commands/newlineInCode.ts +5 -3
- package/src/commands/resetAttributes.ts +36 -41
- package/src/commands/scrollIntoView.ts +9 -7
- package/src/commands/selectAll.ts +10 -8
- package/src/commands/selectNodeBackward.ts +5 -3
- package/src/commands/selectNodeForward.ts +5 -3
- package/src/commands/selectParentNode.ts +5 -3
- package/src/commands/selectTextblockEnd.ts +5 -3
- package/src/commands/selectTextblockStart.ts +5 -3
- package/src/commands/setContent.ts +25 -25
- package/src/commands/setMark.ts +55 -57
- package/src/commands/setMeta.ts +7 -5
- package/src/commands/setNode.ts +32 -30
- package/src/commands/setNodeSelection.ts +11 -9
- package/src/commands/setTextSelection.ts +15 -13
- package/src/commands/sinkListItem.ts +6 -4
- package/src/commands/splitBlock.ts +67 -76
- package/src/commands/splitListItem.ts +93 -106
- package/src/commands/toggleList.ts +73 -71
- package/src/commands/toggleMark.ts +11 -9
- package/src/commands/toggleNode.ts +18 -16
- package/src/commands/toggleWrap.ts +10 -8
- package/src/commands/undoInputRule.ts +31 -29
- package/src/commands/unsetAllMarks.ts +16 -14
- package/src/commands/unsetMark.ts +27 -25
- package/src/commands/updateAttributes.ts +92 -100
- package/src/commands/wrapIn.ts +6 -4
- package/src/commands/wrapInList.ts +6 -4
- package/src/extensions/clipboardTextSerializer.ts +2 -4
- package/src/extensions/focusEvents.ts +2 -6
- package/src/extensions/keymap.ts +54 -50
- package/src/extensions/paste.ts +0 -1
- package/src/extensions/tabindex.ts +1 -1
- package/src/helpers/combineTransactionSteps.ts +1 -4
- package/src/helpers/createChainableState.ts +1 -4
- package/src/helpers/createDocument.ts +1 -3
- package/src/helpers/createNodeFromContent.ts +4 -10
- package/src/helpers/findChildrenInRange.ts +1 -5
- package/src/helpers/findParentNode.ts +3 -1
- package/src/helpers/flattenExtensions.ts +30 -0
- package/src/helpers/getAttributes.ts +1 -4
- package/src/helpers/getAttributesFromExtensions.ts +28 -37
- package/src/helpers/getChangedRanges.ts +13 -11
- package/src/helpers/getExtensionField.ts +1 -4
- package/src/helpers/getMarkAttributes.ts +1 -4
- package/src/helpers/getMarkRange.ts +5 -15
- package/src/helpers/getMarkType.ts +1 -3
- package/src/helpers/getNodeAttributes.ts +1 -4
- package/src/helpers/getNodeType.ts +1 -3
- package/src/helpers/getRenderedAttributes.ts +1 -3
- package/src/helpers/getSchema.ts +2 -2
- package/src/helpers/getSchemaByResolvedExtensions.ts +45 -77
- package/src/helpers/getSplittedAttributes.ts +4 -4
- package/src/helpers/getTextContentFromNodes.ts +8 -11
- package/src/helpers/index.ts +4 -0
- package/src/helpers/injectExtensionAttributesToParseRule.ts +1 -1
- package/src/helpers/isActive.ts +1 -5
- package/src/helpers/isExtensionRulesEnabled.ts +1 -3
- package/src/helpers/isNodeEmpty.ts +2 -2
- package/src/helpers/resolveExtensions.ts +25 -0
- package/src/helpers/resolveFocusPosition.ts +3 -14
- package/src/helpers/rewriteUnknownContent.ts +149 -0
- package/src/helpers/sortExtensions.ts +26 -0
- package/src/inputRules/markInputRule.ts +1 -5
- package/src/inputRules/nodeInputRule.ts +2 -9
- package/src/inputRules/textInputRule.ts +1 -4
- package/src/inputRules/textblockTypeInputRule.ts +2 -8
- package/src/inputRules/wrappingInputRule.ts +13 -19
- package/src/pasteRules/markPasteRule.ts +1 -3
- package/src/pasteRules/nodePasteRule.ts +2 -8
- package/src/pasteRules/textPasteRule.ts +1 -4
- package/src/types.ts +212 -172
- package/src/utilities/createStyleTag.ts +3 -1
- package/src/utilities/deleteProps.ts +7 -11
- package/src/utilities/findDuplicates.ts +4 -1
- package/src/utilities/isFunction.ts +1 -0
- package/src/utilities/isMacOS.ts +1 -3
- package/src/utilities/isiOS.ts +5 -10
- package/src/utilities/mergeAttributes.ts +16 -6
- package/src/utilities/removeDuplicates.ts +1 -3
|
@@ -1,11 +1,5 @@
|
|
|
1
1
|
import type { MarkConfig, NodeConfig } from '../index.js'
|
|
2
|
-
import {
|
|
3
|
-
AnyConfig,
|
|
4
|
-
Attribute,
|
|
5
|
-
Attributes,
|
|
6
|
-
ExtensionAttribute,
|
|
7
|
-
Extensions,
|
|
8
|
-
} from '../types.js'
|
|
2
|
+
import { AnyConfig, Attribute, Attributes, ExtensionAttribute, Extensions } from '../types.js'
|
|
9
3
|
import { getExtensionField } from './getExtensionField.js'
|
|
10
4
|
import { splitExtensions } from './splitExtensions.js'
|
|
11
5
|
|
|
@@ -17,8 +11,9 @@ export function getAttributesFromExtensions(extensions: Extensions): ExtensionAt
|
|
|
17
11
|
const extensionAttributes: ExtensionAttribute[] = []
|
|
18
12
|
const { nodeExtensions, markExtensions } = splitExtensions(extensions)
|
|
19
13
|
const nodeAndMarkExtensions = [...nodeExtensions, ...markExtensions]
|
|
20
|
-
const defaultAttribute: Required<Attribute> = {
|
|
14
|
+
const defaultAttribute: Required<Omit<Attribute, 'validate'>> & Pick<Attribute, 'validate'> = {
|
|
21
15
|
default: null,
|
|
16
|
+
validate: undefined,
|
|
22
17
|
rendered: true,
|
|
23
18
|
renderHTML: null,
|
|
24
19
|
parseHTML: null,
|
|
@@ -48,18 +43,16 @@ export function getAttributesFromExtensions(extensions: Extensions): ExtensionAt
|
|
|
48
43
|
|
|
49
44
|
globalAttributes.forEach(globalAttribute => {
|
|
50
45
|
globalAttribute.types.forEach(type => {
|
|
51
|
-
Object
|
|
52
|
-
.
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
attribute
|
|
58
|
-
|
|
59
|
-
...attribute,
|
|
60
|
-
},
|
|
61
|
-
})
|
|
46
|
+
Object.entries(globalAttribute.attributes).forEach(([name, attribute]) => {
|
|
47
|
+
extensionAttributes.push({
|
|
48
|
+
type,
|
|
49
|
+
name,
|
|
50
|
+
attribute: {
|
|
51
|
+
...defaultAttribute,
|
|
52
|
+
...attribute,
|
|
53
|
+
},
|
|
62
54
|
})
|
|
55
|
+
})
|
|
63
56
|
})
|
|
64
57
|
})
|
|
65
58
|
})
|
|
@@ -84,28 +77,26 @@ export function getAttributesFromExtensions(extensions: Extensions): ExtensionAt
|
|
|
84
77
|
// TODO: remove `as Attributes`
|
|
85
78
|
const attributes = addAttributes() as Attributes
|
|
86
79
|
|
|
87
|
-
Object
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
...attribute,
|
|
93
|
-
}
|
|
80
|
+
Object.entries(attributes).forEach(([name, attribute]) => {
|
|
81
|
+
const mergedAttr = {
|
|
82
|
+
...defaultAttribute,
|
|
83
|
+
...attribute,
|
|
84
|
+
}
|
|
94
85
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
86
|
+
if (typeof mergedAttr?.default === 'function') {
|
|
87
|
+
mergedAttr.default = mergedAttr.default()
|
|
88
|
+
}
|
|
98
89
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
90
|
+
if (mergedAttr?.isRequired && mergedAttr?.default === undefined) {
|
|
91
|
+
delete mergedAttr.default
|
|
92
|
+
}
|
|
102
93
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
})
|
|
94
|
+
extensionAttributes.push({
|
|
95
|
+
type: extension.name,
|
|
96
|
+
name,
|
|
97
|
+
attribute: mergedAttr,
|
|
108
98
|
})
|
|
99
|
+
})
|
|
109
100
|
})
|
|
110
101
|
|
|
111
102
|
return extensionAttributes
|
|
@@ -4,8 +4,8 @@ import { Range } from '../types.js'
|
|
|
4
4
|
import { removeDuplicates } from '../utilities/removeDuplicates.js'
|
|
5
5
|
|
|
6
6
|
export type ChangedRange = {
|
|
7
|
-
oldRange: Range
|
|
8
|
-
newRange: Range
|
|
7
|
+
oldRange: Range
|
|
8
|
+
newRange: Range
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
/**
|
|
@@ -18,15 +18,17 @@ function simplifyChangedRanges(changes: ChangedRange[]): ChangedRange[] {
|
|
|
18
18
|
return uniqueChanges.length === 1
|
|
19
19
|
? uniqueChanges
|
|
20
20
|
: uniqueChanges.filter((change, index) => {
|
|
21
|
-
|
|
21
|
+
const rest = uniqueChanges.filter((_, i) => i !== index)
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
return !rest.some(otherChange => {
|
|
24
|
+
return (
|
|
25
|
+
change.oldRange.from >= otherChange.oldRange.from &&
|
|
26
|
+
change.oldRange.to <= otherChange.oldRange.to &&
|
|
27
|
+
change.newRange.from >= otherChange.newRange.from &&
|
|
28
|
+
change.newRange.to <= otherChange.newRange.to
|
|
29
|
+
)
|
|
30
|
+
})
|
|
28
31
|
})
|
|
29
|
-
})
|
|
30
32
|
}
|
|
31
33
|
|
|
32
34
|
/**
|
|
@@ -45,8 +47,8 @@ export function getChangedRanges(transform: Transform): ChangedRange[] {
|
|
|
45
47
|
// @ts-ignore
|
|
46
48
|
if (!stepMap.ranges.length) {
|
|
47
49
|
const { from, to } = steps[index] as Step & {
|
|
48
|
-
from?: number
|
|
49
|
-
to?: number
|
|
50
|
+
from?: number
|
|
51
|
+
to?: number
|
|
50
52
|
}
|
|
51
53
|
|
|
52
54
|
if (from === undefined || to === undefined) {
|
|
@@ -12,7 +12,6 @@ export function getExtensionField<T = any>(
|
|
|
12
12
|
field: string,
|
|
13
13
|
context?: Omit<MaybeThisParameterType<T>, 'parent'>,
|
|
14
14
|
): RemoveThis<T> {
|
|
15
|
-
|
|
16
15
|
if (extension.config[field] === undefined && extension.parent) {
|
|
17
16
|
return getExtensionField(extension.parent, field, context)
|
|
18
17
|
}
|
|
@@ -20,9 +19,7 @@ export function getExtensionField<T = any>(
|
|
|
20
19
|
if (typeof extension.config[field] === 'function') {
|
|
21
20
|
const value = extension.config[field].bind({
|
|
22
21
|
...context,
|
|
23
|
-
parent: extension.parent
|
|
24
|
-
? getExtensionField(extension.parent, field, context)
|
|
25
|
-
: null,
|
|
22
|
+
parent: extension.parent ? getExtensionField(extension.parent, field, context) : null,
|
|
26
23
|
})
|
|
27
24
|
|
|
28
25
|
return value
|
|
@@ -3,10 +3,7 @@ import { EditorState } from '@tiptap/pm/state'
|
|
|
3
3
|
|
|
4
4
|
import { getMarkType } from './getMarkType.js'
|
|
5
5
|
|
|
6
|
-
export function getMarkAttributes(
|
|
7
|
-
state: EditorState,
|
|
8
|
-
typeOrName: string | MarkType,
|
|
9
|
-
): Record<string, any> {
|
|
6
|
+
export function getMarkAttributes(state: EditorState, typeOrName: string | MarkType): Record<string, any> {
|
|
10
7
|
const type = getMarkType(typeOrName, state.schema)
|
|
11
8
|
const { from, to, empty } = state.selection
|
|
12
9
|
const marks: Mark[] = []
|
|
@@ -10,8 +10,8 @@ function findMarkInSet(
|
|
|
10
10
|
): ProseMirrorMark | undefined {
|
|
11
11
|
return marks.find(item => {
|
|
12
12
|
return (
|
|
13
|
-
item.type === type
|
|
14
|
-
|
|
13
|
+
item.type === type &&
|
|
14
|
+
objectIncludes(
|
|
15
15
|
// Only check equality for the attributes that are provided
|
|
16
16
|
Object.fromEntries(Object.keys(attributes).map(k => [k, item.attrs[k]])),
|
|
17
17
|
attributes,
|
|
@@ -20,11 +20,7 @@ function findMarkInSet(
|
|
|
20
20
|
})
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
function isMarkInSet(
|
|
24
|
-
marks: ProseMirrorMark[],
|
|
25
|
-
type: MarkType,
|
|
26
|
-
attributes: Record<string, any> = {},
|
|
27
|
-
): boolean {
|
|
23
|
+
function isMarkInSet(marks: ProseMirrorMark[], type: MarkType, attributes: Record<string, any> = {}): boolean {
|
|
28
24
|
return !!findMarkInSet(marks, type, attributes)
|
|
29
25
|
}
|
|
30
26
|
|
|
@@ -77,18 +73,12 @@ export function getMarkRange(
|
|
|
77
73
|
let endIndex = startIndex + 1
|
|
78
74
|
let endPos = startPos + start.node.nodeSize
|
|
79
75
|
|
|
80
|
-
while (
|
|
81
|
-
startIndex > 0
|
|
82
|
-
&& isMarkInSet([...$pos.parent.child(startIndex - 1).marks], type, attributes)
|
|
83
|
-
) {
|
|
76
|
+
while (startIndex > 0 && isMarkInSet([...$pos.parent.child(startIndex - 1).marks], type, attributes)) {
|
|
84
77
|
startIndex -= 1
|
|
85
78
|
startPos -= $pos.parent.child(startIndex).nodeSize
|
|
86
79
|
}
|
|
87
80
|
|
|
88
|
-
while (
|
|
89
|
-
endIndex < $pos.parent.childCount
|
|
90
|
-
&& isMarkInSet([...$pos.parent.child(endIndex).marks], type, attributes)
|
|
91
|
-
) {
|
|
81
|
+
while (endIndex < $pos.parent.childCount && isMarkInSet([...$pos.parent.child(endIndex).marks], type, attributes)) {
|
|
92
82
|
endPos += $pos.parent.child(endIndex).nodeSize
|
|
93
83
|
endIndex += 1
|
|
94
84
|
}
|
|
@@ -3,9 +3,7 @@ import { MarkType, Schema } from '@tiptap/pm/model'
|
|
|
3
3
|
export function getMarkType(nameOrType: string | MarkType, schema: Schema): MarkType {
|
|
4
4
|
if (typeof nameOrType === 'string') {
|
|
5
5
|
if (!schema.marks[nameOrType]) {
|
|
6
|
-
throw Error(
|
|
7
|
-
`There is no mark type named '${nameOrType}'. Maybe you forgot to add the extension?`,
|
|
8
|
-
)
|
|
6
|
+
throw Error(`There is no mark type named '${nameOrType}'. Maybe you forgot to add the extension?`)
|
|
9
7
|
}
|
|
10
8
|
|
|
11
9
|
return schema.marks[nameOrType]
|
|
@@ -3,10 +3,7 @@ import { EditorState } from '@tiptap/pm/state'
|
|
|
3
3
|
|
|
4
4
|
import { getNodeType } from './getNodeType.js'
|
|
5
5
|
|
|
6
|
-
export function getNodeAttributes(
|
|
7
|
-
state: EditorState,
|
|
8
|
-
typeOrName: string | NodeType,
|
|
9
|
-
): Record<string, any> {
|
|
6
|
+
export function getNodeAttributes(state: EditorState, typeOrName: string | NodeType): Record<string, any> {
|
|
10
7
|
const type = getNodeType(typeOrName, state.schema)
|
|
11
8
|
const { from, to } = state.selection
|
|
12
9
|
const nodes: Node[] = []
|
|
@@ -3,9 +3,7 @@ import { NodeType, Schema } from '@tiptap/pm/model'
|
|
|
3
3
|
export function getNodeType(nameOrType: string | NodeType, schema: Schema): NodeType {
|
|
4
4
|
if (typeof nameOrType === 'string') {
|
|
5
5
|
if (!schema.nodes[nameOrType]) {
|
|
6
|
-
throw Error(
|
|
7
|
-
`There is no node type named '${nameOrType}'. Maybe you forgot to add the extension?`,
|
|
8
|
-
)
|
|
6
|
+
throw Error(`There is no node type named '${nameOrType}'. Maybe you forgot to add the extension?`)
|
|
9
7
|
}
|
|
10
8
|
|
|
11
9
|
return schema.nodes[nameOrType]
|
|
@@ -8,9 +8,7 @@ export function getRenderedAttributes(
|
|
|
8
8
|
extensionAttributes: ExtensionAttribute[],
|
|
9
9
|
): Record<string, any> {
|
|
10
10
|
return extensionAttributes
|
|
11
|
-
.filter(
|
|
12
|
-
attribute => attribute.type === nodeOrMark.type.name,
|
|
13
|
-
)
|
|
11
|
+
.filter(attribute => attribute.type === nodeOrMark.type.name)
|
|
14
12
|
.filter(item => item.attribute.rendered)
|
|
15
13
|
.map(item => {
|
|
16
14
|
if (!item.attribute.renderHTML) {
|
package/src/helpers/getSchema.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { Schema } from '@tiptap/pm/model'
|
|
2
2
|
|
|
3
3
|
import { Editor } from '../Editor.js'
|
|
4
|
-
import { ExtensionManager } from '../ExtensionManager.js'
|
|
5
4
|
import { Extensions } from '../types.js'
|
|
6
5
|
import { getSchemaByResolvedExtensions } from './getSchemaByResolvedExtensions.js'
|
|
6
|
+
import { resolveExtensions } from './resolveExtensions.js'
|
|
7
7
|
|
|
8
8
|
export function getSchema(extensions: Extensions, editor?: Editor): Schema {
|
|
9
|
-
const resolvedExtensions =
|
|
9
|
+
const resolvedExtensions = resolveExtensions(extensions)
|
|
10
10
|
|
|
11
11
|
return getSchemaByResolvedExtensions(resolvedExtensions, editor)
|
|
12
12
|
}
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
MarkSpec, NodeSpec, Schema, TagParseRule,
|
|
3
|
-
} from '@tiptap/pm/model'
|
|
1
|
+
import { MarkSpec, NodeSpec, Schema, TagParseRule } from '@tiptap/pm/model'
|
|
4
2
|
|
|
5
3
|
import type { Editor, MarkConfig, NodeConfig } from '../index.js'
|
|
6
4
|
import { AnyConfig, Extensions } from '../types.js'
|
|
@@ -16,7 +14,7 @@ function cleanUpSchemaItem<T>(data: T) {
|
|
|
16
14
|
return Object.fromEntries(
|
|
17
15
|
// @ts-ignore
|
|
18
16
|
Object.entries(data).filter(([key, value]) => {
|
|
19
|
-
if (key === 'attrs' && isEmptyObject(value as
|
|
17
|
+
if (key === 'attrs' && isEmptyObject(value as object | undefined)) {
|
|
20
18
|
return false
|
|
21
19
|
}
|
|
22
20
|
|
|
@@ -38,9 +36,7 @@ export function getSchemaByResolvedExtensions(extensions: Extensions, editor?: E
|
|
|
38
36
|
|
|
39
37
|
const nodes = Object.fromEntries(
|
|
40
38
|
nodeExtensions.map(extension => {
|
|
41
|
-
const extensionAttributes = allAttributes.filter(
|
|
42
|
-
attribute => attribute.type === extension.name,
|
|
43
|
-
)
|
|
39
|
+
const extensionAttributes = allAttributes.filter(attribute => attribute.type === extension.name)
|
|
44
40
|
const context = {
|
|
45
41
|
name: extension.name,
|
|
46
42
|
options: extension.options,
|
|
@@ -49,11 +45,7 @@ export function getSchemaByResolvedExtensions(extensions: Extensions, editor?: E
|
|
|
49
45
|
}
|
|
50
46
|
|
|
51
47
|
const extraNodeFields = extensions.reduce((fields, e) => {
|
|
52
|
-
const extendNodeSchema = getExtensionField<AnyConfig['extendNodeSchema']>(
|
|
53
|
-
e,
|
|
54
|
-
'extendNodeSchema',
|
|
55
|
-
context,
|
|
56
|
-
)
|
|
48
|
+
const extendNodeSchema = getExtensionField<AnyConfig['extendNodeSchema']>(e, 'extendNodeSchema', context)
|
|
57
49
|
|
|
58
50
|
return {
|
|
59
51
|
...fields,
|
|
@@ -63,61 +55,49 @@ export function getSchemaByResolvedExtensions(extensions: Extensions, editor?: E
|
|
|
63
55
|
|
|
64
56
|
const schema: NodeSpec = cleanUpSchemaItem({
|
|
65
57
|
...extraNodeFields,
|
|
66
|
-
content: callOrReturn(
|
|
67
|
-
getExtensionField<NodeConfig['content']>(extension, 'content', context),
|
|
68
|
-
),
|
|
58
|
+
content: callOrReturn(getExtensionField<NodeConfig['content']>(extension, 'content', context)),
|
|
69
59
|
marks: callOrReturn(getExtensionField<NodeConfig['marks']>(extension, 'marks', context)),
|
|
70
60
|
group: callOrReturn(getExtensionField<NodeConfig['group']>(extension, 'group', context)),
|
|
71
61
|
inline: callOrReturn(getExtensionField<NodeConfig['inline']>(extension, 'inline', context)),
|
|
72
62
|
atom: callOrReturn(getExtensionField<NodeConfig['atom']>(extension, 'atom', context)),
|
|
73
|
-
selectable: callOrReturn(
|
|
74
|
-
|
|
75
|
-
),
|
|
76
|
-
draggable: callOrReturn(
|
|
77
|
-
getExtensionField<NodeConfig['draggable']>(extension, 'draggable', context),
|
|
78
|
-
),
|
|
63
|
+
selectable: callOrReturn(getExtensionField<NodeConfig['selectable']>(extension, 'selectable', context)),
|
|
64
|
+
draggable: callOrReturn(getExtensionField<NodeConfig['draggable']>(extension, 'draggable', context)),
|
|
79
65
|
code: callOrReturn(getExtensionField<NodeConfig['code']>(extension, 'code', context)),
|
|
80
66
|
whitespace: callOrReturn(getExtensionField<NodeConfig['whitespace']>(extension, 'whitespace', context)),
|
|
81
|
-
linebreakReplacement: callOrReturn(
|
|
82
|
-
|
|
83
|
-
getExtensionField<NodeConfig['defining']>(extension, 'defining', context),
|
|
84
|
-
),
|
|
85
|
-
isolating: callOrReturn(
|
|
86
|
-
getExtensionField<NodeConfig['isolating']>(extension, 'isolating', context),
|
|
67
|
+
linebreakReplacement: callOrReturn(
|
|
68
|
+
getExtensionField<NodeConfig['linebreakReplacement']>(extension, 'linebreakReplacement', context),
|
|
87
69
|
),
|
|
70
|
+
defining: callOrReturn(getExtensionField<NodeConfig['defining']>(extension, 'defining', context)),
|
|
71
|
+
isolating: callOrReturn(getExtensionField<NodeConfig['isolating']>(extension, 'isolating', context)),
|
|
88
72
|
attrs: Object.fromEntries(
|
|
89
73
|
extensionAttributes.map(extensionAttribute => {
|
|
90
|
-
return [
|
|
74
|
+
return [
|
|
75
|
+
extensionAttribute.name,
|
|
76
|
+
{ default: extensionAttribute?.attribute?.default, validate: extensionAttribute?.attribute?.validate },
|
|
77
|
+
]
|
|
91
78
|
}),
|
|
92
79
|
),
|
|
93
80
|
})
|
|
94
81
|
|
|
95
|
-
const parseHTML = callOrReturn(
|
|
96
|
-
getExtensionField<NodeConfig['parseHTML']>(extension, 'parseHTML', context),
|
|
97
|
-
)
|
|
82
|
+
const parseHTML = callOrReturn(getExtensionField<NodeConfig['parseHTML']>(extension, 'parseHTML', context))
|
|
98
83
|
|
|
99
84
|
if (parseHTML) {
|
|
100
|
-
schema.parseDOM = parseHTML.map(parseRule =>
|
|
85
|
+
schema.parseDOM = parseHTML.map(parseRule =>
|
|
86
|
+
injectExtensionAttributesToParseRule(parseRule, extensionAttributes),
|
|
87
|
+
) as TagParseRule[]
|
|
101
88
|
}
|
|
102
89
|
|
|
103
|
-
const renderHTML = getExtensionField<NodeConfig['renderHTML']>(
|
|
104
|
-
extension,
|
|
105
|
-
'renderHTML',
|
|
106
|
-
context,
|
|
107
|
-
)
|
|
90
|
+
const renderHTML = getExtensionField<NodeConfig['renderHTML']>(extension, 'renderHTML', context)
|
|
108
91
|
|
|
109
92
|
if (renderHTML) {
|
|
110
|
-
schema.toDOM = node =>
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
93
|
+
schema.toDOM = node =>
|
|
94
|
+
renderHTML({
|
|
95
|
+
node,
|
|
96
|
+
HTMLAttributes: getRenderedAttributes(node, extensionAttributes),
|
|
97
|
+
})
|
|
114
98
|
}
|
|
115
99
|
|
|
116
|
-
const renderText = getExtensionField<NodeConfig['renderText']>(
|
|
117
|
-
extension,
|
|
118
|
-
'renderText',
|
|
119
|
-
context,
|
|
120
|
-
)
|
|
100
|
+
const renderText = getExtensionField<NodeConfig['renderText']>(extension, 'renderText', context)
|
|
121
101
|
|
|
122
102
|
if (renderText) {
|
|
123
103
|
schema.toText = renderText
|
|
@@ -129,9 +109,7 @@ export function getSchemaByResolvedExtensions(extensions: Extensions, editor?: E
|
|
|
129
109
|
|
|
130
110
|
const marks = Object.fromEntries(
|
|
131
111
|
markExtensions.map(extension => {
|
|
132
|
-
const extensionAttributes = allAttributes.filter(
|
|
133
|
-
attribute => attribute.type === extension.name,
|
|
134
|
-
)
|
|
112
|
+
const extensionAttributes = allAttributes.filter(attribute => attribute.type === extension.name)
|
|
135
113
|
const context = {
|
|
136
114
|
name: extension.name,
|
|
137
115
|
options: extension.options,
|
|
@@ -140,11 +118,7 @@ export function getSchemaByResolvedExtensions(extensions: Extensions, editor?: E
|
|
|
140
118
|
}
|
|
141
119
|
|
|
142
120
|
const extraMarkFields = extensions.reduce((fields, e) => {
|
|
143
|
-
const extendMarkSchema = getExtensionField<AnyConfig['extendMarkSchema']>(
|
|
144
|
-
e,
|
|
145
|
-
'extendMarkSchema',
|
|
146
|
-
context,
|
|
147
|
-
)
|
|
121
|
+
const extendMarkSchema = getExtensionField<AnyConfig['extendMarkSchema']>(e, 'extendMarkSchema', context)
|
|
148
122
|
|
|
149
123
|
return {
|
|
150
124
|
...fields,
|
|
@@ -154,43 +128,37 @@ export function getSchemaByResolvedExtensions(extensions: Extensions, editor?: E
|
|
|
154
128
|
|
|
155
129
|
const schema: MarkSpec = cleanUpSchemaItem({
|
|
156
130
|
...extraMarkFields,
|
|
157
|
-
inclusive: callOrReturn(
|
|
158
|
-
|
|
159
|
-
),
|
|
160
|
-
excludes: callOrReturn(
|
|
161
|
-
getExtensionField<MarkConfig['excludes']>(extension, 'excludes', context),
|
|
162
|
-
),
|
|
131
|
+
inclusive: callOrReturn(getExtensionField<MarkConfig['inclusive']>(extension, 'inclusive', context)),
|
|
132
|
+
excludes: callOrReturn(getExtensionField<MarkConfig['excludes']>(extension, 'excludes', context)),
|
|
163
133
|
group: callOrReturn(getExtensionField<MarkConfig['group']>(extension, 'group', context)),
|
|
164
|
-
spanning: callOrReturn(
|
|
165
|
-
getExtensionField<MarkConfig['spanning']>(extension, 'spanning', context),
|
|
166
|
-
),
|
|
134
|
+
spanning: callOrReturn(getExtensionField<MarkConfig['spanning']>(extension, 'spanning', context)),
|
|
167
135
|
code: callOrReturn(getExtensionField<MarkConfig['code']>(extension, 'code', context)),
|
|
168
136
|
attrs: Object.fromEntries(
|
|
169
137
|
extensionAttributes.map(extensionAttribute => {
|
|
170
|
-
return [
|
|
138
|
+
return [
|
|
139
|
+
extensionAttribute.name,
|
|
140
|
+
{ default: extensionAttribute?.attribute?.default, validate: extensionAttribute?.attribute?.validate },
|
|
141
|
+
]
|
|
171
142
|
}),
|
|
172
143
|
),
|
|
173
144
|
})
|
|
174
145
|
|
|
175
|
-
const parseHTML = callOrReturn(
|
|
176
|
-
getExtensionField<MarkConfig['parseHTML']>(extension, 'parseHTML', context),
|
|
177
|
-
)
|
|
146
|
+
const parseHTML = callOrReturn(getExtensionField<MarkConfig['parseHTML']>(extension, 'parseHTML', context))
|
|
178
147
|
|
|
179
148
|
if (parseHTML) {
|
|
180
|
-
schema.parseDOM = parseHTML.map(parseRule =>
|
|
149
|
+
schema.parseDOM = parseHTML.map(parseRule =>
|
|
150
|
+
injectExtensionAttributesToParseRule(parseRule, extensionAttributes),
|
|
151
|
+
)
|
|
181
152
|
}
|
|
182
153
|
|
|
183
|
-
const renderHTML = getExtensionField<MarkConfig['renderHTML']>(
|
|
184
|
-
extension,
|
|
185
|
-
'renderHTML',
|
|
186
|
-
context,
|
|
187
|
-
)
|
|
154
|
+
const renderHTML = getExtensionField<MarkConfig['renderHTML']>(extension, 'renderHTML', context)
|
|
188
155
|
|
|
189
156
|
if (renderHTML) {
|
|
190
|
-
schema.toDOM = mark =>
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
157
|
+
schema.toDOM = mark =>
|
|
158
|
+
renderHTML({
|
|
159
|
+
mark,
|
|
160
|
+
HTMLAttributes: getRenderedAttributes(mark, extensionAttributes),
|
|
161
|
+
})
|
|
194
162
|
}
|
|
195
163
|
|
|
196
164
|
return [extension.name, schema]
|
|
@@ -12,9 +12,8 @@ export function getSplittedAttributes(
|
|
|
12
12
|
typeName: string,
|
|
13
13
|
attributes: Record<string, any>,
|
|
14
14
|
): Record<string, any> {
|
|
15
|
-
return Object.fromEntries(
|
|
16
|
-
.entries(attributes)
|
|
17
|
-
.filter(([name]) => {
|
|
15
|
+
return Object.fromEntries(
|
|
16
|
+
Object.entries(attributes).filter(([name]) => {
|
|
18
17
|
const extensionAttribute = extensionAttributes.find(item => {
|
|
19
18
|
return item.type === typeName && item.name === name
|
|
20
19
|
})
|
|
@@ -24,5 +23,6 @@ export function getSplittedAttributes(
|
|
|
24
23
|
}
|
|
25
24
|
|
|
26
25
|
return extensionAttribute.attribute.keepOnSplit
|
|
27
|
-
})
|
|
26
|
+
}),
|
|
27
|
+
)
|
|
28
28
|
}
|
|
@@ -11,22 +11,19 @@ export const getTextContentFromNodes = ($from: ResolvedPos, maxMatch = 500) => {
|
|
|
11
11
|
|
|
12
12
|
const sliceEndPos = $from.parentOffset
|
|
13
13
|
|
|
14
|
-
$from.parent.nodesBetween(
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
(node, pos, parent, index) => {
|
|
18
|
-
const chunk = node.type.spec.toText?.({
|
|
14
|
+
$from.parent.nodesBetween(Math.max(0, sliceEndPos - maxMatch), sliceEndPos, (node, pos, parent, index) => {
|
|
15
|
+
const chunk =
|
|
16
|
+
node.type.spec.toText?.({
|
|
19
17
|
node,
|
|
20
18
|
pos,
|
|
21
19
|
parent,
|
|
22
20
|
index,
|
|
23
|
-
})
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
}) ||
|
|
22
|
+
node.textContent ||
|
|
23
|
+
'%leaf%'
|
|
26
24
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
)
|
|
25
|
+
textBefore += node.isAtom && !node.isText ? chunk : chunk.slice(0, Math.max(0, sliceEndPos - pos))
|
|
26
|
+
})
|
|
30
27
|
|
|
31
28
|
return textBefore
|
|
32
29
|
}
|
package/src/helpers/index.ts
CHANGED
|
@@ -7,6 +7,7 @@ export * from './findChildren.js'
|
|
|
7
7
|
export * from './findChildrenInRange.js'
|
|
8
8
|
export * from './findParentNode.js'
|
|
9
9
|
export * from './findParentNodeClosestToPos.js'
|
|
10
|
+
export * from './flattenExtensions.js'
|
|
10
11
|
export * from './generateHTML.js'
|
|
11
12
|
export * from './generateJSON.js'
|
|
12
13
|
export * from './generateText.js'
|
|
@@ -45,6 +46,9 @@ export * from './isNodeEmpty.js'
|
|
|
45
46
|
export * from './isNodeSelection.js'
|
|
46
47
|
export * from './isTextSelection.js'
|
|
47
48
|
export * from './posToDOMRect.js'
|
|
49
|
+
export * from './resolveExtensions.js'
|
|
48
50
|
export * from './resolveFocusPosition.js'
|
|
51
|
+
export * from './rewriteUnknownContent.js'
|
|
49
52
|
export * from './selectionToInsertionEnd.js'
|
|
53
|
+
export * from './sortExtensions.js'
|
|
50
54
|
export * from './splitExtensions.js'
|
|
@@ -29,7 +29,7 @@ export function injectExtensionAttributesToParseRule(
|
|
|
29
29
|
const newAttributes = extensionAttributes.reduce((items, item) => {
|
|
30
30
|
const value = item.attribute.parseHTML
|
|
31
31
|
? item.attribute.parseHTML(node)
|
|
32
|
-
: fromString(
|
|
32
|
+
: fromString(node.getAttribute(item.name))
|
|
33
33
|
|
|
34
34
|
if (value === null || value === undefined) {
|
|
35
35
|
return items
|
package/src/helpers/isActive.ts
CHANGED
|
@@ -4,11 +4,7 @@ import { getSchemaTypeNameByName } from './getSchemaTypeNameByName.js'
|
|
|
4
4
|
import { isMarkActive } from './isMarkActive.js'
|
|
5
5
|
import { isNodeActive } from './isNodeActive.js'
|
|
6
6
|
|
|
7
|
-
export function isActive(
|
|
8
|
-
state: EditorState,
|
|
9
|
-
name: string | null,
|
|
10
|
-
attributes: Record<string, any> = {},
|
|
11
|
-
): boolean {
|
|
7
|
+
export function isActive(state: EditorState, name: string | null, attributes: Record<string, any> = {}): boolean {
|
|
12
8
|
if (!name) {
|
|
13
9
|
return isNodeActive(state, null, attributes) || isMarkActive(state, null, attributes)
|
|
14
10
|
}
|
|
@@ -3,9 +3,7 @@ import { AnyExtension, EnableRules } from '../types.js'
|
|
|
3
3
|
export function isExtensionRulesEnabled(extension: AnyExtension, enabled: EnableRules): boolean {
|
|
4
4
|
if (Array.isArray(enabled)) {
|
|
5
5
|
return enabled.some(enabledExtension => {
|
|
6
|
-
const name = typeof enabledExtension === 'string'
|
|
7
|
-
? enabledExtension
|
|
8
|
-
: enabledExtension.name
|
|
6
|
+
const name = typeof enabledExtension === 'string' ? enabledExtension : enabledExtension.name
|
|
9
7
|
|
|
10
8
|
return name === extension.name
|
|
11
9
|
})
|
|
@@ -12,11 +12,11 @@ export function isNodeEmpty(
|
|
|
12
12
|
/**
|
|
13
13
|
* When true (default), it will also check if all children are empty.
|
|
14
14
|
*/
|
|
15
|
-
checkChildren?: boolean
|
|
15
|
+
checkChildren?: boolean
|
|
16
16
|
/**
|
|
17
17
|
* When true, it will ignore whitespace when checking for emptiness.
|
|
18
18
|
*/
|
|
19
|
-
ignoreWhitespace?: boolean
|
|
19
|
+
ignoreWhitespace?: boolean
|
|
20
20
|
} = {},
|
|
21
21
|
): boolean {
|
|
22
22
|
if (ignoreWhitespace) {
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Extensions } from '../types.js'
|
|
2
|
+
import { findDuplicates } from '../utilities/findDuplicates.js'
|
|
3
|
+
import { flattenExtensions } from './flattenExtensions.js'
|
|
4
|
+
import { sortExtensions } from './sortExtensions.js'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Returns a flattened and sorted extension list while
|
|
8
|
+
* also checking for duplicated extensions and warns the user.
|
|
9
|
+
* @param extensions An array of Tiptap extensions
|
|
10
|
+
* @returns An flattened and sorted array of Tiptap extensions
|
|
11
|
+
*/
|
|
12
|
+
export function resolveExtensions(extensions: Extensions): Extensions {
|
|
13
|
+
const resolvedExtensions = sortExtensions(flattenExtensions(extensions))
|
|
14
|
+
const duplicatedNames = findDuplicates(resolvedExtensions.map(extension => extension.name))
|
|
15
|
+
|
|
16
|
+
if (duplicatedNames.length) {
|
|
17
|
+
console.warn(
|
|
18
|
+
`[tiptap warn]: Duplicate extension names found: [${duplicatedNames
|
|
19
|
+
.map(item => `'${item}'`)
|
|
20
|
+
.join(', ')}]. This can lead to issues.`,
|
|
21
|
+
)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return resolvedExtensions
|
|
25
|
+
}
|