@tiptap/core 2.0.0-beta.98 → 2.0.0-rc.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/README.md +3 -3
- package/dist/index.cjs +4360 -0
- package/dist/index.cjs.map +1 -0
- package/dist/{tiptap-core.esm.js → index.js} +2349 -1447
- package/dist/index.js.map +1 -0
- package/dist/index.umd.js +4358 -0
- package/dist/index.umd.js.map +1 -0
- package/dist/packages/core/src/CommandManager.d.ts +14 -7
- package/dist/packages/core/src/Editor.d.ts +25 -11
- package/dist/packages/core/src/EventEmitter.d.ts +8 -4
- package/dist/packages/core/src/Extension.d.ts +63 -29
- package/dist/packages/core/src/ExtensionManager.d.ts +3 -4
- package/dist/packages/core/src/InputRule.d.ts +42 -0
- package/dist/packages/core/src/Mark.d.ts +94 -36
- package/dist/packages/core/src/Node.d.ts +104 -45
- package/dist/packages/core/src/NodeView.d.ts +8 -8
- package/dist/packages/core/src/PasteRule.d.ts +42 -0
- package/dist/packages/core/src/Tracker.d.ts +1 -1
- package/dist/packages/core/src/commands/deleteCurrentNode.d.ts +12 -0
- package/dist/packages/core/src/commands/deleteNode.d.ts +1 -1
- package/dist/packages/core/src/commands/deleteRange.d.ts +1 -1
- package/dist/packages/core/src/commands/extendMarkRange.d.ts +1 -1
- package/dist/packages/core/src/commands/focus.d.ts +4 -2
- package/dist/packages/core/src/commands/index.d.ts +50 -0
- package/dist/packages/core/src/commands/insertContent.d.ts +6 -3
- package/dist/packages/core/src/commands/insertContentAt.d.ts +6 -3
- package/dist/packages/core/src/commands/join.d.ts +33 -0
- package/dist/packages/core/src/commands/lift.d.ts +1 -1
- package/dist/packages/core/src/commands/liftListItem.d.ts +1 -1
- package/dist/packages/core/src/commands/resetAttributes.d.ts +1 -1
- package/dist/packages/core/src/commands/selectTextblockEnd.d.ts +12 -0
- package/dist/packages/core/src/commands/selectTextblockStart.d.ts +12 -0
- package/dist/packages/core/src/commands/setContent.d.ts +2 -2
- package/dist/packages/core/src/commands/setMark.d.ts +1 -1
- package/dist/packages/core/src/commands/setNode.d.ts +1 -1
- package/dist/packages/core/src/commands/setTextSelection.d.ts +1 -1
- package/dist/packages/core/src/commands/sinkListItem.d.ts +1 -1
- package/dist/packages/core/src/commands/splitListItem.d.ts +1 -1
- package/dist/packages/core/src/commands/toggleList.d.ts +2 -2
- package/dist/packages/core/src/commands/toggleMark.d.ts +7 -2
- package/dist/packages/core/src/commands/toggleNode.d.ts +1 -1
- package/dist/packages/core/src/commands/toggleWrap.d.ts +1 -1
- package/dist/packages/core/src/commands/unsetMark.d.ts +7 -2
- package/dist/packages/core/src/commands/updateAttributes.d.ts +1 -1
- package/dist/packages/core/src/commands/wrapIn.d.ts +1 -1
- package/dist/packages/core/src/commands/wrapInList.d.ts +1 -1
- package/dist/packages/core/src/extensions/clipboardTextSerializer.d.ts +1 -1
- package/dist/packages/core/src/extensions/commands.d.ts +2 -97
- package/dist/packages/core/src/extensions/editable.d.ts +1 -1
- package/dist/packages/core/src/extensions/focusEvents.d.ts +1 -1
- package/dist/packages/core/src/extensions/index.d.ts +1 -0
- package/dist/packages/core/src/extensions/keymap.d.ts +1 -1
- package/dist/packages/core/src/extensions/tabindex.d.ts +2 -0
- package/dist/packages/core/src/helpers/combineTransactionSteps.d.ts +7 -0
- package/dist/packages/core/src/helpers/createChainableState.d.ts +5 -0
- package/dist/packages/core/src/helpers/createDocument.d.ts +2 -2
- package/dist/packages/core/src/helpers/createNodeFromContent.d.ts +2 -2
- package/dist/packages/core/src/helpers/defaultBlockAt.d.ts +2 -0
- package/dist/packages/core/src/helpers/findChildren.d.ts +3 -3
- package/dist/packages/core/src/helpers/findChildrenInRange.d.ts +3 -3
- package/dist/packages/core/src/helpers/findParentNode.d.ts +3 -4
- package/dist/packages/core/src/helpers/findParentNodeClosestToPos.d.ts +3 -3
- package/dist/packages/core/src/helpers/generateHTML.d.ts +1 -1
- package/dist/packages/core/src/helpers/generateJSON.d.ts +1 -1
- package/dist/packages/core/src/helpers/generateText.d.ts +5 -0
- package/dist/packages/core/src/helpers/getAttributes.d.ts +3 -3
- package/dist/packages/core/src/helpers/getAttributesFromExtensions.d.ts +2 -2
- package/dist/packages/core/src/helpers/getChangedRanges.d.ts +11 -0
- package/dist/packages/core/src/helpers/getDebugJSON.d.ts +2 -5
- package/dist/packages/core/src/helpers/getExtensionField.d.ts +2 -2
- package/dist/packages/core/src/helpers/getHTMLFromFragment.d.ts +2 -2
- package/dist/packages/core/src/helpers/getMarkAttributes.d.ts +3 -3
- package/dist/packages/core/src/helpers/getMarkRange.d.ts +2 -2
- package/dist/packages/core/src/helpers/getMarkType.d.ts +2 -2
- package/dist/packages/core/src/helpers/getMarksBetween.d.ts +2 -2
- package/dist/packages/core/src/helpers/getNodeAttributes.d.ts +3 -3
- package/dist/packages/core/src/helpers/getNodeType.d.ts +2 -2
- package/dist/packages/core/src/helpers/getRenderedAttributes.d.ts +2 -2
- package/dist/packages/core/src/helpers/getSchema.d.ts +2 -2
- package/dist/packages/core/src/helpers/getSchemaByResolvedExtensions.d.ts +2 -2
- package/dist/packages/core/src/helpers/getSchemaTypeByName.d.ts +2 -2
- package/dist/packages/core/src/helpers/getSchemaTypeNameByName.d.ts +2 -2
- package/dist/packages/core/src/helpers/getSplittedAttributes.d.ts +1 -1
- package/dist/packages/core/src/helpers/getText.d.ts +6 -0
- package/dist/packages/core/src/helpers/getTextBetween.d.ts +6 -0
- package/dist/packages/core/src/helpers/getTextContentFromNodes.d.ts +2 -0
- package/dist/packages/core/src/helpers/getTextSerializersFromSchema.d.ts +3 -0
- package/dist/packages/core/src/helpers/index.d.ts +47 -0
- package/dist/packages/core/src/helpers/injectExtensionAttributesToParseRule.d.ts +2 -2
- package/dist/packages/core/src/helpers/isActive.d.ts +2 -2
- package/dist/packages/core/src/helpers/isExtensionRulesEnabled.d.ts +2 -0
- package/dist/packages/core/src/helpers/isList.d.ts +1 -1
- package/dist/packages/core/src/helpers/isMarkActive.d.ts +3 -3
- package/dist/packages/core/src/helpers/isNodeActive.d.ts +3 -3
- package/dist/packages/core/src/helpers/isNodeEmpty.d.ts +2 -2
- package/dist/packages/core/src/helpers/isNodeSelection.d.ts +2 -2
- package/dist/packages/core/src/helpers/isTextSelection.d.ts +2 -2
- package/dist/packages/core/src/helpers/posToDOMRect.d.ts +2 -2
- package/dist/packages/core/src/helpers/resolveFocusPosition.d.ts +4 -0
- package/dist/packages/core/src/helpers/selectionToInsertionEnd.d.ts +2 -2
- package/dist/packages/core/src/helpers/splitExtensions.d.ts +6 -6
- package/dist/packages/core/src/index.d.ts +12 -34
- package/dist/packages/core/src/inputRules/index.d.ts +5 -0
- package/dist/packages/core/src/inputRules/markInputRule.d.ts +12 -3
- package/dist/packages/core/src/inputRules/nodeInputRule.d.ts +12 -3
- package/dist/packages/core/src/inputRules/textInputRule.d.ts +9 -0
- package/dist/packages/core/src/inputRules/textblockTypeInputRule.d.ts +14 -0
- package/dist/packages/core/src/inputRules/wrappingInputRule.d.ts +27 -0
- package/dist/packages/core/src/pasteRules/index.d.ts +3 -0
- package/dist/packages/core/src/pasteRules/markPasteRule.d.ts +12 -3
- package/dist/packages/core/src/pasteRules/nodePasteRule.d.ts +12 -0
- package/dist/packages/core/src/pasteRules/textPasteRule.d.ts +9 -0
- package/dist/packages/core/src/style.d.ts +1 -2
- package/dist/packages/core/src/types.d.ts +70 -40
- package/dist/packages/core/src/utilities/callOrReturn.d.ts +1 -1
- package/dist/packages/core/src/utilities/createStyleTag.d.ts +1 -1
- package/dist/packages/core/src/utilities/deleteProps.d.ts +1 -1
- package/dist/packages/core/src/utilities/elementFromString.d.ts +1 -1
- package/dist/packages/core/src/utilities/escapeForRegEx.d.ts +1 -0
- package/dist/packages/core/src/utilities/findDuplicates.d.ts +1 -0
- package/dist/packages/core/src/utilities/fromString.d.ts +1 -1
- package/dist/packages/core/src/utilities/index.d.ts +20 -0
- package/dist/packages/core/src/utilities/isEmptyObject.d.ts +1 -1
- package/dist/packages/core/src/utilities/isFunction.d.ts +1 -0
- package/dist/packages/core/src/utilities/isMacOS.d.ts +1 -0
- package/dist/packages/core/src/utilities/isNumber.d.ts +1 -0
- package/dist/packages/core/src/utilities/isPlainObject.d.ts +1 -1
- package/dist/packages/core/src/utilities/isRegExp.d.ts +1 -0
- package/dist/packages/core/src/utilities/isString.d.ts +1 -0
- package/dist/packages/core/src/utilities/isiOS.d.ts +1 -1
- package/dist/packages/core/src/utilities/mergeAttributes.d.ts +1 -1
- package/dist/packages/core/src/utilities/mergeDeep.d.ts +1 -1
- package/dist/packages/core/src/utilities/minMax.d.ts +1 -1
- package/dist/packages/core/src/utilities/objectIncludes.d.ts +3 -1
- package/dist/packages/core/src/utilities/removeDuplicates.d.ts +8 -0
- package/package.json +25 -24
- package/src/CommandManager.ts +73 -83
- package/src/Editor.ts +101 -53
- package/src/EventEmitter.ts +14 -4
- package/src/Extension.ts +244 -138
- package/src/ExtensionManager.ts +153 -152
- package/src/InputRule.ts +260 -0
- package/src/Mark.ts +365 -204
- package/src/Node.ts +406 -253
- package/src/NodeView.ts +59 -38
- package/src/PasteRule.ts +240 -0
- package/src/Tracker.ts +4 -8
- package/src/commands/blur.ts +4 -0
- package/src/commands/clearNodes.ts +15 -9
- package/src/commands/createParagraphNear.ts +3 -2
- package/src/commands/deleteCurrentNode.ts +41 -0
- package/src/commands/deleteNode.ts +3 -2
- package/src/commands/deleteRange.ts +1 -1
- package/src/commands/deleteSelection.ts +3 -2
- package/src/commands/exitCode.ts +3 -2
- package/src/commands/extendMarkRange.ts +9 -5
- package/src/commands/focus.ts +31 -42
- package/src/commands/index.ts +50 -0
- package/src/commands/insertContent.ts +15 -4
- package/src/commands/insertContentAt.ts +71 -14
- package/src/commands/join.ts +53 -0
- package/src/commands/keyboardShortcut.ts +3 -3
- package/src/commands/lift.ts +6 -5
- package/src/commands/liftEmptyBlock.ts +2 -1
- package/src/commands/liftListItem.ts +5 -4
- package/src/commands/newlineInCode.ts +3 -2
- package/src/commands/resetAttributes.ts +16 -10
- package/src/commands/selectAll.ts +5 -3
- package/src/commands/selectNodeBackward.ts +3 -2
- package/src/commands/selectNodeForward.ts +3 -2
- package/src/commands/selectParentNode.ts +3 -2
- package/src/commands/selectTextblockEnd.ts +20 -0
- package/src/commands/selectTextblockStart.ts +20 -0
- package/src/commands/setContent.ts +6 -9
- package/src/commands/setMark.ts +66 -13
- package/src/commands/setNode.ts +30 -6
- package/src/commands/setNodeSelection.ts +6 -7
- package/src/commands/setTextSelection.ts +8 -9
- package/src/commands/sinkListItem.ts +5 -4
- package/src/commands/splitBlock.ts +23 -38
- package/src/commands/splitListItem.ts +30 -27
- package/src/commands/toggleList.ts +108 -19
- package/src/commands/toggleMark.ts +17 -6
- package/src/commands/toggleNode.ts +9 -4
- package/src/commands/toggleWrap.ts +8 -8
- package/src/commands/undoInputRule.ts +31 -2
- package/src/commands/unsetAllMarks.ts +4 -8
- package/src/commands/unsetMark.ts +34 -21
- package/src/commands/updateAttributes.ts +18 -12
- package/src/commands/wrapIn.ts +5 -10
- package/src/commands/wrapInList.ts +5 -4
- package/src/extensions/clipboardTextSerializer.ts +15 -36
- package/src/extensions/commands.ts +3 -144
- package/src/extensions/editable.ts +2 -1
- package/src/extensions/focusEvents.ts +4 -6
- package/src/extensions/index.ts +1 -0
- package/src/extensions/keymap.ts +111 -13
- package/src/extensions/tabindex.ts +18 -0
- package/src/helpers/combineTransactionSteps.ts +21 -0
- package/src/helpers/createChainableState.ts +38 -0
- package/src/helpers/createDocument.ts +4 -3
- package/src/helpers/createNodeFromContent.ts +10 -15
- package/src/helpers/defaultBlockAt.ts +13 -0
- package/src/helpers/findChildren.ts +4 -3
- package/src/helpers/findChildrenInRange.ts +8 -3
- package/src/helpers/findParentNode.ts +4 -3
- package/src/helpers/findParentNodeClosestToPos.ts +13 -7
- package/src/helpers/generateHTML.ts +6 -5
- package/src/helpers/generateJSON.ts +6 -7
- package/src/helpers/generateText.ts +27 -0
- package/src/helpers/getAttributes.ts +8 -9
- package/src/helpers/getAttributesFromExtensions.ts +25 -12
- package/src/helpers/getChangedRanges.ts +83 -0
- package/src/helpers/getDebugJSON.ts +42 -38
- package/src/helpers/getExtensionField.ts +3 -3
- package/src/helpers/getHTMLFromFragment.ts +5 -6
- package/src/helpers/getMarkAttributes.ts +18 -10
- package/src/helpers/getMarkRange.ts +13 -8
- package/src/helpers/getMarkType.ts +5 -3
- package/src/helpers/getMarksBetween.ts +34 -10
- package/src/helpers/getNodeAttributes.ts +14 -12
- package/src/helpers/getNodeType.ts +5 -3
- package/src/helpers/getRenderedAttributes.ts +8 -6
- package/src/helpers/getSchema.ts +5 -4
- package/src/helpers/getSchemaByResolvedExtensions.ts +165 -111
- package/src/helpers/getSchemaTypeByName.ts +3 -11
- package/src/helpers/getSchemaTypeNameByName.ts +2 -2
- package/src/helpers/getSplittedAttributes.ts +1 -1
- package/src/helpers/getText.ts +19 -0
- package/src/helpers/getTextBetween.ts +46 -0
- package/src/helpers/getTextContentFromNodes.ts +26 -0
- package/src/helpers/getTextSerializersFromSchema.ts +11 -0
- package/src/helpers/index.ts +47 -0
- package/src/helpers/injectExtensionAttributesToParseRule.ts +22 -23
- package/src/helpers/isActive.ts +10 -5
- package/src/helpers/isExtensionRulesEnabled.ts +15 -0
- package/src/helpers/isList.ts +6 -5
- package/src/helpers/isMarkActive.ts +32 -35
- package/src/helpers/isNodeActive.ts +27 -37
- package/src/helpers/isNodeEmpty.ts +2 -2
- package/src/helpers/isNodeSelection.ts +3 -4
- package/src/helpers/isTextSelection.ts +3 -4
- package/src/helpers/posToDOMRect.ts +10 -4
- package/src/helpers/resolveFocusPosition.ts +42 -0
- package/src/helpers/selectionToInsertionEnd.ts +3 -3
- package/src/helpers/splitExtensions.ts +3 -3
- package/src/index.ts +12 -37
- package/src/inputRules/index.ts +5 -0
- package/src/inputRules/markInputRule.ts +59 -40
- package/src/inputRules/nodeInputRule.ts +45 -12
- package/src/inputRules/textInputRule.ts +35 -0
- package/src/inputRules/textblockTypeInputRule.ts +37 -0
- package/src/inputRules/wrappingInputRule.ts +84 -0
- package/src/pasteRules/index.ts +3 -0
- package/src/pasteRules/markPasteRule.ts +49 -56
- package/src/pasteRules/nodePasteRule.ts +37 -0
- package/src/pasteRules/textPasteRule.ts +35 -0
- package/src/style.ts +12 -3
- package/src/types.ts +140 -106
- package/src/utilities/callOrReturn.ts +3 -2
- package/src/utilities/createStyleTag.ts +8 -4
- package/src/utilities/deleteProps.ts +1 -1
- package/src/utilities/elementFromString.ts +1 -1
- package/src/utilities/escapeForRegEx.ts +4 -0
- package/src/utilities/findDuplicates.ts +5 -0
- package/src/utilities/fromString.ts +2 -2
- package/src/utilities/index.ts +20 -0
- package/src/utilities/isEmptyObject.ts +2 -2
- package/src/utilities/isFunction.ts +3 -0
- package/src/utilities/isMacOS.ts +5 -0
- package/src/utilities/isNumber.ts +3 -0
- package/src/utilities/isPlainObject.ts +8 -5
- package/src/utilities/isRegExp.ts +3 -0
- package/src/utilities/isString.ts +3 -0
- package/src/utilities/isiOS.ts +1 -1
- package/src/utilities/mergeAttributes.ts +2 -1
- package/src/utilities/mergeDeep.ts +2 -2
- package/src/utilities/minMax.ts +1 -1
- package/src/utilities/objectIncludes.ts +18 -4
- package/src/utilities/removeDuplicates.ts +15 -0
- package/CHANGELOG.md +0 -1182
- package/LICENSE.md +0 -21
- package/dist/packages/core/src/commands/joinBackward.d.ts +0 -12
- package/dist/packages/core/src/commands/joinForward.d.ts +0 -12
- package/dist/packages/core/src/utilities/isClass.d.ts +0 -1
- package/dist/packages/core/src/utilities/isObject.d.ts +0 -1
- package/dist/packages/core/src/utilities/removeElement.d.ts +0 -1
- package/dist/tiptap-core.cjs.js +0 -3408
- package/dist/tiptap-core.cjs.js.map +0 -1
- package/dist/tiptap-core.esm.js.map +0 -1
- package/dist/tiptap-core.umd.js +0 -3405
- package/dist/tiptap-core.umd.js.map +0 -1
- package/src/commands/joinBackward.ts +0 -17
- package/src/commands/joinForward.ts +0 -17
- package/src/utilities/isClass.ts +0 -7
- package/src/utilities/isObject.ts +0 -10
- package/src/utilities/removeElement.ts +0 -5
|
@@ -1,318 +1,64 @@
|
|
|
1
|
-
import { Plugin, PluginKey, TextSelection, Selection, NodeSelection, EditorState } from '
|
|
2
|
-
import { EditorView } from '
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
.reverse()
|
|
39
|
-
.find(nodeItem => nodeItem.type.name === type.name);
|
|
40
|
-
if (node) {
|
|
41
|
-
return { ...node.attrs };
|
|
42
|
-
}
|
|
43
|
-
return {};
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function getMarkType(nameOrType, schema) {
|
|
47
|
-
if (typeof nameOrType === 'string') {
|
|
48
|
-
if (!schema.marks[nameOrType]) {
|
|
49
|
-
throw Error(`There is no mark type named '${nameOrType}'. Maybe you forgot to add the extension?`);
|
|
50
|
-
}
|
|
51
|
-
return schema.marks[nameOrType];
|
|
52
|
-
}
|
|
53
|
-
return nameOrType;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function getMarkAttributes(state, typeOrName) {
|
|
57
|
-
const type = getMarkType(typeOrName, state.schema);
|
|
58
|
-
const { from, to, empty } = state.selection;
|
|
59
|
-
let marks = [];
|
|
60
|
-
if (empty) {
|
|
61
|
-
marks = state.selection.$head.marks();
|
|
62
|
-
}
|
|
63
|
-
else {
|
|
64
|
-
state.doc.nodesBetween(from, to, node => {
|
|
65
|
-
marks = [...marks, ...node.marks];
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
const mark = marks.find(markItem => markItem.type.name === type.name);
|
|
69
|
-
if (mark) {
|
|
70
|
-
return { ...mark.attrs };
|
|
71
|
-
}
|
|
72
|
-
return {};
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
function getAttributes(state, typeOrName) {
|
|
76
|
-
const schemaType = getSchemaTypeNameByName(typeof typeOrName === 'string'
|
|
77
|
-
? typeOrName
|
|
78
|
-
: typeOrName.name, state.schema);
|
|
79
|
-
if (schemaType === 'node') {
|
|
80
|
-
return getNodeAttributes(state, typeOrName);
|
|
81
|
-
}
|
|
82
|
-
if (schemaType === 'mark') {
|
|
83
|
-
return getMarkAttributes(state, typeOrName);
|
|
84
|
-
}
|
|
85
|
-
return {};
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Check if object1 includes object2
|
|
90
|
-
* @param object1 Object
|
|
91
|
-
* @param object2 Object
|
|
92
|
-
*/
|
|
93
|
-
function objectIncludes(object1, object2) {
|
|
94
|
-
const keys = Object.keys(object2);
|
|
95
|
-
if (!keys.length) {
|
|
96
|
-
return true;
|
|
97
|
-
}
|
|
98
|
-
return !!keys
|
|
99
|
-
.filter(key => object2[key] === object1[key])
|
|
100
|
-
.length;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
function isNodeActive(state, typeOrName, attributes = {}) {
|
|
104
|
-
const { from, to, empty } = state.selection;
|
|
105
|
-
const type = typeOrName
|
|
106
|
-
? getNodeType(typeOrName, state.schema)
|
|
107
|
-
: null;
|
|
108
|
-
let nodeRanges = [];
|
|
109
|
-
state.doc.nodesBetween(from, to, (node, pos) => {
|
|
110
|
-
if (!node.isText) {
|
|
111
|
-
const relativeFrom = Math.max(from, pos);
|
|
112
|
-
const relativeTo = Math.min(to, pos + node.nodeSize);
|
|
113
|
-
nodeRanges = [...nodeRanges, {
|
|
114
|
-
node,
|
|
115
|
-
from: relativeFrom,
|
|
116
|
-
to: relativeTo,
|
|
117
|
-
}];
|
|
118
|
-
}
|
|
119
|
-
});
|
|
120
|
-
if (empty) {
|
|
121
|
-
return !!nodeRanges
|
|
122
|
-
.filter(nodeRange => {
|
|
123
|
-
if (!type) {
|
|
124
|
-
return true;
|
|
125
|
-
}
|
|
126
|
-
return type.name === nodeRange.node.type.name;
|
|
127
|
-
})
|
|
128
|
-
.find(nodeRange => objectIncludes(nodeRange.node.attrs, attributes));
|
|
129
|
-
}
|
|
130
|
-
const selectionRange = to - from;
|
|
131
|
-
const range = nodeRanges
|
|
132
|
-
.filter(nodeRange => {
|
|
133
|
-
if (!type) {
|
|
134
|
-
return true;
|
|
135
|
-
}
|
|
136
|
-
return type.name === nodeRange.node.type.name;
|
|
137
|
-
})
|
|
138
|
-
.filter(nodeRange => objectIncludes(nodeRange.node.attrs, attributes))
|
|
139
|
-
.reduce((sum, nodeRange) => {
|
|
140
|
-
const size = nodeRange.to - nodeRange.from;
|
|
141
|
-
return sum + size;
|
|
142
|
-
}, 0);
|
|
143
|
-
return range >= selectionRange;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
function isMarkActive(state, typeOrName, attributes = {}) {
|
|
147
|
-
const { from, to, empty } = state.selection;
|
|
148
|
-
const type = typeOrName
|
|
149
|
-
? getMarkType(typeOrName, state.schema)
|
|
150
|
-
: null;
|
|
151
|
-
if (empty) {
|
|
152
|
-
return !!(state.storedMarks || state.selection.$from.marks())
|
|
153
|
-
.filter(mark => {
|
|
154
|
-
if (!type) {
|
|
155
|
-
return true;
|
|
156
|
-
}
|
|
157
|
-
return type.name === mark.type.name;
|
|
158
|
-
})
|
|
159
|
-
.find(mark => objectIncludes(mark.attrs, attributes));
|
|
160
|
-
}
|
|
161
|
-
let selectionRange = 0;
|
|
162
|
-
let markRanges = [];
|
|
163
|
-
state.doc.nodesBetween(from, to, (node, pos) => {
|
|
164
|
-
if (node.isText) {
|
|
165
|
-
const relativeFrom = Math.max(from, pos);
|
|
166
|
-
const relativeTo = Math.min(to, pos + node.nodeSize);
|
|
167
|
-
const range = relativeTo - relativeFrom;
|
|
168
|
-
selectionRange += range;
|
|
169
|
-
markRanges = [...markRanges, ...node.marks.map(mark => ({
|
|
170
|
-
mark,
|
|
171
|
-
from: relativeFrom,
|
|
172
|
-
to: relativeTo,
|
|
173
|
-
}))];
|
|
174
|
-
}
|
|
175
|
-
});
|
|
176
|
-
if (selectionRange === 0) {
|
|
177
|
-
return false;
|
|
178
|
-
}
|
|
179
|
-
// calculate range of matched mark
|
|
180
|
-
const matchedRange = markRanges
|
|
181
|
-
.filter(markRange => {
|
|
182
|
-
if (!type) {
|
|
183
|
-
return true;
|
|
184
|
-
}
|
|
185
|
-
return type.name === markRange.mark.type.name;
|
|
186
|
-
})
|
|
187
|
-
.filter(markRange => objectIncludes(markRange.mark.attrs, attributes))
|
|
188
|
-
.reduce((sum, markRange) => {
|
|
189
|
-
const size = markRange.to - markRange.from;
|
|
190
|
-
return sum + size;
|
|
191
|
-
}, 0);
|
|
192
|
-
// calculate range of marks that excludes the searched mark
|
|
193
|
-
// for example `code` doesn’t allow any other marks
|
|
194
|
-
const excludedRange = markRanges
|
|
195
|
-
.filter(markRange => {
|
|
196
|
-
if (!type) {
|
|
197
|
-
return true;
|
|
198
|
-
}
|
|
199
|
-
return markRange.mark.type !== type
|
|
200
|
-
&& markRange.mark.type.excludes(type);
|
|
201
|
-
})
|
|
202
|
-
.reduce((sum, markRange) => {
|
|
203
|
-
const size = markRange.to - markRange.from;
|
|
204
|
-
return sum + size;
|
|
205
|
-
}, 0);
|
|
206
|
-
// we only include the result of `excludedRange`
|
|
207
|
-
// if there is a match at all
|
|
208
|
-
const range = matchedRange > 0
|
|
209
|
-
? matchedRange + excludedRange
|
|
210
|
-
: matchedRange;
|
|
211
|
-
return range >= selectionRange;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
function isActive(state, name, attributes = {}) {
|
|
215
|
-
if (!name) {
|
|
216
|
-
return isNodeActive(state, null, attributes) || isMarkActive(state, null, attributes);
|
|
217
|
-
}
|
|
218
|
-
const schemaType = getSchemaTypeNameByName(name, state.schema);
|
|
219
|
-
if (schemaType === 'node') {
|
|
220
|
-
return isNodeActive(state, name, attributes);
|
|
221
|
-
}
|
|
222
|
-
if (schemaType === 'mark') {
|
|
223
|
-
return isMarkActive(state, name, attributes);
|
|
224
|
-
}
|
|
225
|
-
return false;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
function removeElement(element) {
|
|
229
|
-
if (element && element.parentNode) {
|
|
230
|
-
element.parentNode.removeChild(element);
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
function elementFromString(value) {
|
|
235
|
-
// add a wrapper to preserve leading and trailing whitespace
|
|
236
|
-
const wrappedValue = `<body>${value}</body>`;
|
|
237
|
-
return new window.DOMParser().parseFromString(wrappedValue, 'text/html').body;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
function createNodeFromContent(content, schema, options) {
|
|
241
|
-
options = {
|
|
242
|
-
slice: true,
|
|
243
|
-
parseOptions: {},
|
|
244
|
-
...options,
|
|
1
|
+
import { Plugin, PluginKey, TextSelection, Selection, NodeSelection, EditorState } from '@tiptap/pm/state';
|
|
2
|
+
import { EditorView } from '@tiptap/pm/view';
|
|
3
|
+
import { keymap } from '@tiptap/pm/keymap';
|
|
4
|
+
import { Schema, Fragment, DOMParser, DOMSerializer, Node as Node$1, Slice } from '@tiptap/pm/model';
|
|
5
|
+
import { liftTarget, ReplaceStep, ReplaceAroundStep, Transform, canSplit, canJoin, findWrapping } from '@tiptap/pm/transform';
|
|
6
|
+
import { createParagraphNear as createParagraphNear$1, deleteSelection as deleteSelection$1, exitCode as exitCode$1, joinUp as joinUp$1, joinDown as joinDown$1, joinBackward as joinBackward$1, joinForward as joinForward$1, lift as lift$1, liftEmptyBlock as liftEmptyBlock$1, newlineInCode as newlineInCode$1, selectNodeBackward as selectNodeBackward$1, selectNodeForward as selectNodeForward$1, selectParentNode as selectParentNode$1, selectTextblockEnd as selectTextblockEnd$1, selectTextblockStart as selectTextblockStart$1, setBlockType, wrapIn as wrapIn$1 } from '@tiptap/pm/commands';
|
|
7
|
+
import { liftListItem as liftListItem$1, sinkListItem as sinkListItem$1, wrapInList as wrapInList$1 } from '@tiptap/pm/schema-list';
|
|
8
|
+
|
|
9
|
+
function createChainableState(config) {
|
|
10
|
+
const { state, transaction } = config;
|
|
11
|
+
let { selection } = transaction;
|
|
12
|
+
let { doc } = transaction;
|
|
13
|
+
let { storedMarks } = transaction;
|
|
14
|
+
return {
|
|
15
|
+
...state,
|
|
16
|
+
apply: state.apply.bind(state),
|
|
17
|
+
applyTransaction: state.applyTransaction.bind(state),
|
|
18
|
+
filterTransaction: state.filterTransaction,
|
|
19
|
+
plugins: state.plugins,
|
|
20
|
+
schema: state.schema,
|
|
21
|
+
reconfigure: state.reconfigure.bind(state),
|
|
22
|
+
toJSON: state.toJSON.bind(state),
|
|
23
|
+
get storedMarks() {
|
|
24
|
+
return storedMarks;
|
|
25
|
+
},
|
|
26
|
+
get selection() {
|
|
27
|
+
return selection;
|
|
28
|
+
},
|
|
29
|
+
get doc() {
|
|
30
|
+
return doc;
|
|
31
|
+
},
|
|
32
|
+
get tr() {
|
|
33
|
+
selection = transaction.selection;
|
|
34
|
+
doc = transaction.doc;
|
|
35
|
+
storedMarks = transaction.storedMarks;
|
|
36
|
+
return transaction;
|
|
37
|
+
},
|
|
245
38
|
};
|
|
246
|
-
if (typeof content === 'object' && content !== null) {
|
|
247
|
-
try {
|
|
248
|
-
if (Array.isArray(content)) {
|
|
249
|
-
return Fragment.fromArray(content.map(item => schema.nodeFromJSON(item)));
|
|
250
|
-
}
|
|
251
|
-
return schema.nodeFromJSON(content);
|
|
252
|
-
}
|
|
253
|
-
catch (error) {
|
|
254
|
-
console.warn('[tiptap warn]: Invalid content.', 'Passed value:', content, 'Error:', error);
|
|
255
|
-
return createNodeFromContent('', schema, options);
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
if (typeof content === 'string') {
|
|
259
|
-
const parser = DOMParser.fromSchema(schema);
|
|
260
|
-
return options.slice
|
|
261
|
-
? parser.parseSlice(elementFromString(content), options.parseOptions).content
|
|
262
|
-
: parser.parse(elementFromString(content), options.parseOptions);
|
|
263
|
-
}
|
|
264
|
-
return createNodeFromContent('', schema, options);
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
function createDocument(content, schema, parseOptions = {}) {
|
|
268
|
-
return createNodeFromContent(content, schema, { slice: false, parseOptions });
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
function getHTMLFromFragment(doc, schema) {
|
|
272
|
-
const fragment = DOMSerializer
|
|
273
|
-
.fromSchema(schema)
|
|
274
|
-
.serializeFragment(doc.content);
|
|
275
|
-
const temporaryDocument = document.implementation.createHTMLDocument();
|
|
276
|
-
const container = temporaryDocument.createElement('div');
|
|
277
|
-
container.appendChild(fragment);
|
|
278
|
-
return container.innerHTML;
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
function isNodeEmpty(node) {
|
|
282
|
-
var _a;
|
|
283
|
-
const defaultContent = (_a = node.type.createAndFill()) === null || _a === void 0 ? void 0 : _a.toJSON();
|
|
284
|
-
const content = node.toJSON();
|
|
285
|
-
return JSON.stringify(defaultContent) === JSON.stringify(content);
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
function createStyleTag(style) {
|
|
289
|
-
const tipTapStyleTag = document.querySelector('style[data-tiptap-style]');
|
|
290
|
-
if (tipTapStyleTag !== null) {
|
|
291
|
-
return tipTapStyleTag;
|
|
292
|
-
}
|
|
293
|
-
const styleNode = document.createElement('style');
|
|
294
|
-
styleNode.setAttribute('data-tiptap-style', '');
|
|
295
|
-
styleNode.innerHTML = style;
|
|
296
|
-
document.getElementsByTagName('head')[0].appendChild(styleNode);
|
|
297
|
-
return styleNode;
|
|
298
39
|
}
|
|
299
40
|
|
|
300
41
|
class CommandManager {
|
|
301
|
-
constructor(
|
|
302
|
-
this.editor = editor;
|
|
303
|
-
this.
|
|
42
|
+
constructor(props) {
|
|
43
|
+
this.editor = props.editor;
|
|
44
|
+
this.rawCommands = this.editor.extensionManager.commands;
|
|
45
|
+
this.customState = props.state;
|
|
304
46
|
}
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
47
|
+
get hasCustomState() {
|
|
48
|
+
return !!this.customState;
|
|
49
|
+
}
|
|
50
|
+
get state() {
|
|
51
|
+
return this.customState || this.editor.state;
|
|
52
|
+
}
|
|
53
|
+
get commands() {
|
|
54
|
+
const { rawCommands, editor, state } = this;
|
|
55
|
+
const { view } = editor;
|
|
308
56
|
const { tr } = state;
|
|
309
57
|
const props = this.buildProps(tr);
|
|
310
|
-
return Object.fromEntries(Object
|
|
311
|
-
.entries(commands)
|
|
312
|
-
.map(([name, command]) => {
|
|
58
|
+
return Object.fromEntries(Object.entries(rawCommands).map(([name, command]) => {
|
|
313
59
|
const method = (...args) => {
|
|
314
60
|
const callback = command(...args)(props);
|
|
315
|
-
if (!tr.getMeta('preventDispatch')) {
|
|
61
|
+
if (!tr.getMeta('preventDispatch') && !this.hasCustomState) {
|
|
316
62
|
view.dispatch(tr);
|
|
317
63
|
}
|
|
318
64
|
return callback;
|
|
@@ -320,20 +66,29 @@ class CommandManager {
|
|
|
320
66
|
return [name, method];
|
|
321
67
|
}));
|
|
322
68
|
}
|
|
69
|
+
get chain() {
|
|
70
|
+
return () => this.createChain();
|
|
71
|
+
}
|
|
72
|
+
get can() {
|
|
73
|
+
return () => this.createCan();
|
|
74
|
+
}
|
|
323
75
|
createChain(startTr, shouldDispatch = true) {
|
|
324
|
-
const {
|
|
325
|
-
const {
|
|
76
|
+
const { rawCommands, editor, state } = this;
|
|
77
|
+
const { view } = editor;
|
|
326
78
|
const callbacks = [];
|
|
327
79
|
const hasStartTransaction = !!startTr;
|
|
328
80
|
const tr = startTr || state.tr;
|
|
329
81
|
const run = () => {
|
|
330
|
-
if (!hasStartTransaction
|
|
82
|
+
if (!hasStartTransaction
|
|
83
|
+
&& shouldDispatch
|
|
84
|
+
&& !tr.getMeta('preventDispatch')
|
|
85
|
+
&& !this.hasCustomState) {
|
|
331
86
|
view.dispatch(tr);
|
|
332
87
|
}
|
|
333
88
|
return callbacks.every(callback => callback === true);
|
|
334
89
|
};
|
|
335
90
|
const chain = {
|
|
336
|
-
...Object.fromEntries(Object.entries(
|
|
91
|
+
...Object.fromEntries(Object.entries(rawCommands).map(([name, command]) => {
|
|
337
92
|
const chainedCommand = (...args) => {
|
|
338
93
|
const props = this.buildProps(tr, shouldDispatch);
|
|
339
94
|
const callback = command(...args)(props);
|
|
@@ -347,15 +102,12 @@ class CommandManager {
|
|
|
347
102
|
return chain;
|
|
348
103
|
}
|
|
349
104
|
createCan(startTr) {
|
|
350
|
-
const {
|
|
351
|
-
const
|
|
352
|
-
const dispatch = undefined;
|
|
105
|
+
const { rawCommands, state } = this;
|
|
106
|
+
const dispatch = false;
|
|
353
107
|
const tr = startTr || state.tr;
|
|
354
108
|
const props = this.buildProps(tr, dispatch);
|
|
355
|
-
const formattedCommands = Object.fromEntries(Object
|
|
356
|
-
|
|
357
|
-
.map(([name, command]) => {
|
|
358
|
-
return [name, (...args) => command(...args)({ ...props, dispatch })];
|
|
109
|
+
const formattedCommands = Object.fromEntries(Object.entries(rawCommands).map(([name, command]) => {
|
|
110
|
+
return [name, (...args) => command(...args)({ ...props, dispatch: undefined })];
|
|
359
111
|
}));
|
|
360
112
|
return {
|
|
361
113
|
...formattedCommands,
|
|
@@ -363,8 +115,8 @@ class CommandManager {
|
|
|
363
115
|
};
|
|
364
116
|
}
|
|
365
117
|
buildProps(tr, shouldDispatch = true) {
|
|
366
|
-
const { editor,
|
|
367
|
-
const {
|
|
118
|
+
const { rawCommands, editor, state } = this;
|
|
119
|
+
const { view } = editor;
|
|
368
120
|
if (state.storedMarks) {
|
|
369
121
|
tr.setStoredMarks(state.storedMarks);
|
|
370
122
|
}
|
|
@@ -372,68 +124,73 @@ class CommandManager {
|
|
|
372
124
|
tr,
|
|
373
125
|
editor,
|
|
374
126
|
view,
|
|
375
|
-
state:
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
127
|
+
state: createChainableState({
|
|
128
|
+
state,
|
|
129
|
+
transaction: tr,
|
|
130
|
+
}),
|
|
131
|
+
dispatch: shouldDispatch ? () => undefined : undefined,
|
|
379
132
|
chain: () => this.createChain(tr),
|
|
380
133
|
can: () => this.createCan(tr),
|
|
381
134
|
get commands() {
|
|
382
|
-
return Object.fromEntries(Object
|
|
383
|
-
.entries(commands)
|
|
384
|
-
.map(([name, command]) => {
|
|
135
|
+
return Object.fromEntries(Object.entries(rawCommands).map(([name, command]) => {
|
|
385
136
|
return [name, (...args) => command(...args)(props)];
|
|
386
137
|
}));
|
|
387
138
|
},
|
|
388
139
|
};
|
|
389
140
|
return props;
|
|
390
141
|
}
|
|
391
|
-
chainableState(tr, state) {
|
|
392
|
-
let { selection } = tr;
|
|
393
|
-
let { doc } = tr;
|
|
394
|
-
let { storedMarks } = tr;
|
|
395
|
-
return {
|
|
396
|
-
...state,
|
|
397
|
-
schema: state.schema,
|
|
398
|
-
plugins: state.plugins,
|
|
399
|
-
apply: state.apply.bind(state),
|
|
400
|
-
applyTransaction: state.applyTransaction.bind(state),
|
|
401
|
-
reconfigure: state.reconfigure.bind(state),
|
|
402
|
-
toJSON: state.toJSON.bind(state),
|
|
403
|
-
get storedMarks() {
|
|
404
|
-
return storedMarks;
|
|
405
|
-
},
|
|
406
|
-
get selection() {
|
|
407
|
-
return selection;
|
|
408
|
-
},
|
|
409
|
-
get doc() {
|
|
410
|
-
return doc;
|
|
411
|
-
},
|
|
412
|
-
get tr() {
|
|
413
|
-
selection = tr.selection;
|
|
414
|
-
doc = tr.doc;
|
|
415
|
-
storedMarks = tr.storedMarks;
|
|
416
|
-
return tr;
|
|
417
|
-
},
|
|
418
|
-
};
|
|
419
|
-
}
|
|
420
142
|
}
|
|
421
143
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
144
|
+
class EventEmitter {
|
|
145
|
+
constructor() {
|
|
146
|
+
this.callbacks = {};
|
|
425
147
|
}
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
});
|
|
433
|
-
return value;
|
|
148
|
+
on(event, fn) {
|
|
149
|
+
if (!this.callbacks[event]) {
|
|
150
|
+
this.callbacks[event] = [];
|
|
151
|
+
}
|
|
152
|
+
this.callbacks[event].push(fn);
|
|
153
|
+
return this;
|
|
434
154
|
}
|
|
435
|
-
|
|
436
|
-
|
|
155
|
+
emit(event, ...args) {
|
|
156
|
+
const callbacks = this.callbacks[event];
|
|
157
|
+
if (callbacks) {
|
|
158
|
+
callbacks.forEach(callback => callback.apply(this, args));
|
|
159
|
+
}
|
|
160
|
+
return this;
|
|
161
|
+
}
|
|
162
|
+
off(event, fn) {
|
|
163
|
+
const callbacks = this.callbacks[event];
|
|
164
|
+
if (callbacks) {
|
|
165
|
+
if (fn) {
|
|
166
|
+
this.callbacks[event] = callbacks.filter(callback => callback !== fn);
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
delete this.callbacks[event];
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return this;
|
|
173
|
+
}
|
|
174
|
+
removeAllListeners() {
|
|
175
|
+
this.callbacks = {};
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function getExtensionField(extension, field, context) {
|
|
180
|
+
if (extension.config[field] === undefined && extension.parent) {
|
|
181
|
+
return getExtensionField(extension.parent, field, context);
|
|
182
|
+
}
|
|
183
|
+
if (typeof extension.config[field] === 'function') {
|
|
184
|
+
const value = extension.config[field].bind({
|
|
185
|
+
...context,
|
|
186
|
+
parent: extension.parent
|
|
187
|
+
? getExtensionField(extension.parent, field, context)
|
|
188
|
+
: null,
|
|
189
|
+
});
|
|
190
|
+
return value;
|
|
191
|
+
}
|
|
192
|
+
return extension.config[field];
|
|
193
|
+
}
|
|
437
194
|
|
|
438
195
|
function splitExtensions(extensions) {
|
|
439
196
|
const baseExtensions = extensions.filter(extension => extension.type === 'extension');
|
|
@@ -460,11 +217,13 @@ function getAttributesFromExtensions(extensions) {
|
|
|
460
217
|
renderHTML: null,
|
|
461
218
|
parseHTML: null,
|
|
462
219
|
keepOnSplit: true,
|
|
220
|
+
isRequired: false,
|
|
463
221
|
};
|
|
464
222
|
extensions.forEach(extension => {
|
|
465
223
|
const context = {
|
|
466
224
|
name: extension.name,
|
|
467
225
|
options: extension.options,
|
|
226
|
+
storage: extension.storage,
|
|
468
227
|
};
|
|
469
228
|
const addGlobalAttributes = getExtensionField(extension, 'addGlobalAttributes', context);
|
|
470
229
|
if (!addGlobalAttributes) {
|
|
@@ -493,6 +252,7 @@ function getAttributesFromExtensions(extensions) {
|
|
|
493
252
|
const context = {
|
|
494
253
|
name: extension.name,
|
|
495
254
|
options: extension.options,
|
|
255
|
+
storage: extension.storage,
|
|
496
256
|
};
|
|
497
257
|
const addAttributes = getExtensionField(extension, 'addAttributes', context);
|
|
498
258
|
if (!addAttributes) {
|
|
@@ -503,19 +263,36 @@ function getAttributesFromExtensions(extensions) {
|
|
|
503
263
|
Object
|
|
504
264
|
.entries(attributes)
|
|
505
265
|
.forEach(([name, attribute]) => {
|
|
266
|
+
const mergedAttr = {
|
|
267
|
+
...defaultAttribute,
|
|
268
|
+
...attribute,
|
|
269
|
+
};
|
|
270
|
+
if (typeof (mergedAttr === null || mergedAttr === void 0 ? void 0 : mergedAttr.default) === 'function') {
|
|
271
|
+
mergedAttr.default = mergedAttr.default();
|
|
272
|
+
}
|
|
273
|
+
if ((mergedAttr === null || mergedAttr === void 0 ? void 0 : mergedAttr.isRequired) && (mergedAttr === null || mergedAttr === void 0 ? void 0 : mergedAttr.default) === undefined) {
|
|
274
|
+
delete mergedAttr.default;
|
|
275
|
+
}
|
|
506
276
|
extensionAttributes.push({
|
|
507
277
|
type: extension.name,
|
|
508
278
|
name,
|
|
509
|
-
attribute:
|
|
510
|
-
...defaultAttribute,
|
|
511
|
-
...attribute,
|
|
512
|
-
},
|
|
279
|
+
attribute: mergedAttr,
|
|
513
280
|
});
|
|
514
281
|
});
|
|
515
282
|
});
|
|
516
283
|
return extensionAttributes;
|
|
517
284
|
}
|
|
518
285
|
|
|
286
|
+
function getNodeType(nameOrType, schema) {
|
|
287
|
+
if (typeof nameOrType === 'string') {
|
|
288
|
+
if (!schema.nodes[nameOrType]) {
|
|
289
|
+
throw Error(`There is no node type named '${nameOrType}'. Maybe you forgot to add the extension?`);
|
|
290
|
+
}
|
|
291
|
+
return schema.nodes[nameOrType];
|
|
292
|
+
}
|
|
293
|
+
return nameOrType;
|
|
294
|
+
}
|
|
295
|
+
|
|
519
296
|
function mergeAttributes(...objects) {
|
|
520
297
|
return objects
|
|
521
298
|
.filter(item => !!item)
|
|
@@ -552,20 +329,39 @@ function getRenderedAttributes(nodeOrMark, extensionAttributes) {
|
|
|
552
329
|
}
|
|
553
330
|
return item.attribute.renderHTML(nodeOrMark.attrs) || {};
|
|
554
331
|
})
|
|
555
|
-
.reduce((attributes, attribute) => {
|
|
556
|
-
|
|
557
|
-
|
|
332
|
+
.reduce((attributes, attribute) => mergeAttributes(attributes, attribute), {});
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function isFunction(value) {
|
|
336
|
+
return typeof value === 'function';
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Optionally calls `value` as a function.
|
|
341
|
+
* Otherwise it is returned directly.
|
|
342
|
+
* @param value Function or any value.
|
|
343
|
+
* @param context Optional context to bind to function.
|
|
344
|
+
* @param props Optional props to pass to function.
|
|
345
|
+
*/
|
|
346
|
+
function callOrReturn(value, context = undefined, ...props) {
|
|
347
|
+
if (isFunction(value)) {
|
|
348
|
+
if (context) {
|
|
349
|
+
return value.bind(context)(...props);
|
|
350
|
+
}
|
|
351
|
+
return value(...props);
|
|
352
|
+
}
|
|
353
|
+
return value;
|
|
558
354
|
}
|
|
559
355
|
|
|
560
|
-
function isEmptyObject(
|
|
561
|
-
return Object.keys(
|
|
356
|
+
function isEmptyObject(value = {}) {
|
|
357
|
+
return Object.keys(value).length === 0 && value.constructor === Object;
|
|
562
358
|
}
|
|
563
359
|
|
|
564
360
|
function fromString(value) {
|
|
565
361
|
if (typeof value !== 'string') {
|
|
566
362
|
return value;
|
|
567
363
|
}
|
|
568
|
-
if (value.match(
|
|
364
|
+
if (value.match(/^[+-]?(?:\d*\.)?\d+$/)) {
|
|
569
365
|
return Number(value);
|
|
570
366
|
}
|
|
571
367
|
if (value === 'true') {
|
|
@@ -590,25 +386,20 @@ function injectExtensionAttributesToParseRule(parseRule, extensionAttributes) {
|
|
|
590
386
|
return {
|
|
591
387
|
...parseRule,
|
|
592
388
|
getAttrs: node => {
|
|
593
|
-
const oldAttributes = parseRule.getAttrs
|
|
594
|
-
? parseRule.getAttrs(node)
|
|
595
|
-
: parseRule.attrs;
|
|
389
|
+
const oldAttributes = parseRule.getAttrs ? parseRule.getAttrs(node) : parseRule.attrs;
|
|
596
390
|
if (oldAttributes === false) {
|
|
597
391
|
return false;
|
|
598
392
|
}
|
|
599
|
-
const newAttributes = extensionAttributes
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
};
|
|
607
|
-
const filteredAttributes = Object.fromEntries(Object.entries(attributes)
|
|
608
|
-
.filter(([, value]) => value !== undefined && value !== null));
|
|
393
|
+
const newAttributes = extensionAttributes.reduce((items, item) => {
|
|
394
|
+
const value = item.attribute.parseHTML
|
|
395
|
+
? item.attribute.parseHTML(node)
|
|
396
|
+
: fromString(node.getAttribute(item.name));
|
|
397
|
+
if (value === null || value === undefined) {
|
|
398
|
+
return items;
|
|
399
|
+
}
|
|
609
400
|
return {
|
|
610
401
|
...items,
|
|
611
|
-
|
|
402
|
+
[item.name]: value,
|
|
612
403
|
};
|
|
613
404
|
}, {});
|
|
614
405
|
return { ...oldAttributes, ...newAttributes };
|
|
@@ -616,23 +407,6 @@ function injectExtensionAttributesToParseRule(parseRule, extensionAttributes) {
|
|
|
616
407
|
};
|
|
617
408
|
}
|
|
618
409
|
|
|
619
|
-
/**
|
|
620
|
-
* Optionally calls `value` as a function.
|
|
621
|
-
* Otherwise it is returned directly.
|
|
622
|
-
* @param value Function or any value.
|
|
623
|
-
* @param context Optional context to bind to function.
|
|
624
|
-
* @param props Optional props to pass to function.
|
|
625
|
-
*/
|
|
626
|
-
function callOrReturn(value, context = undefined, ...props) {
|
|
627
|
-
if (typeof value === 'function') {
|
|
628
|
-
if (context) {
|
|
629
|
-
return value.bind(context)(...props);
|
|
630
|
-
}
|
|
631
|
-
return value(...props);
|
|
632
|
-
}
|
|
633
|
-
return value;
|
|
634
|
-
}
|
|
635
|
-
|
|
636
410
|
function cleanUpSchemaItem(data) {
|
|
637
411
|
return Object.fromEntries(Object.entries(data).filter(([key, value]) => {
|
|
638
412
|
if (key === 'attrs' && isEmptyObject(value)) {
|
|
@@ -651,6 +425,7 @@ function getSchemaByResolvedExtensions(extensions) {
|
|
|
651
425
|
const context = {
|
|
652
426
|
name: extension.name,
|
|
653
427
|
options: extension.options,
|
|
428
|
+
storage: extension.storage,
|
|
654
429
|
};
|
|
655
430
|
const extraNodeFields = extensions.reduce((fields, e) => {
|
|
656
431
|
const extendNodeSchema = getExtensionField(e, 'extendNodeSchema', context);
|
|
@@ -678,8 +453,7 @@ function getSchemaByResolvedExtensions(extensions) {
|
|
|
678
453
|
});
|
|
679
454
|
const parseHTML = callOrReturn(getExtensionField(extension, 'parseHTML', context));
|
|
680
455
|
if (parseHTML) {
|
|
681
|
-
schema.parseDOM = parseHTML
|
|
682
|
-
.map(parseRule => injectExtensionAttributesToParseRule(parseRule, extensionAttributes));
|
|
456
|
+
schema.parseDOM = parseHTML.map(parseRule => injectExtensionAttributesToParseRule(parseRule, extensionAttributes));
|
|
683
457
|
}
|
|
684
458
|
const renderHTML = getExtensionField(extension, 'renderHTML', context);
|
|
685
459
|
if (renderHTML) {
|
|
@@ -688,6 +462,10 @@ function getSchemaByResolvedExtensions(extensions) {
|
|
|
688
462
|
HTMLAttributes: getRenderedAttributes(node, extensionAttributes),
|
|
689
463
|
});
|
|
690
464
|
}
|
|
465
|
+
const renderText = getExtensionField(extension, 'renderText', context);
|
|
466
|
+
if (renderText) {
|
|
467
|
+
schema.toText = renderText;
|
|
468
|
+
}
|
|
691
469
|
return [extension.name, schema];
|
|
692
470
|
}));
|
|
693
471
|
const marks = Object.fromEntries(markExtensions.map(extension => {
|
|
@@ -695,6 +473,7 @@ function getSchemaByResolvedExtensions(extensions) {
|
|
|
695
473
|
const context = {
|
|
696
474
|
name: extension.name,
|
|
697
475
|
options: extension.options,
|
|
476
|
+
storage: extension.storage,
|
|
698
477
|
};
|
|
699
478
|
const extraMarkFields = extensions.reduce((fields, e) => {
|
|
700
479
|
const extendMarkSchema = getExtensionField(e, 'extendMarkSchema', context);
|
|
@@ -709,6 +488,7 @@ function getSchemaByResolvedExtensions(extensions) {
|
|
|
709
488
|
excludes: callOrReturn(getExtensionField(extension, 'excludes', context)),
|
|
710
489
|
group: callOrReturn(getExtensionField(extension, 'group', context)),
|
|
711
490
|
spanning: callOrReturn(getExtensionField(extension, 'spanning', context)),
|
|
491
|
+
code: callOrReturn(getExtensionField(extension, 'code', context)),
|
|
712
492
|
attrs: Object.fromEntries(extensionAttributes.map(extensionAttribute => {
|
|
713
493
|
var _a;
|
|
714
494
|
return [extensionAttribute.name, { default: (_a = extensionAttribute === null || extensionAttribute === void 0 ? void 0 : extensionAttribute.attribute) === null || _a === void 0 ? void 0 : _a.default }];
|
|
@@ -716,8 +496,7 @@ function getSchemaByResolvedExtensions(extensions) {
|
|
|
716
496
|
});
|
|
717
497
|
const parseHTML = callOrReturn(getExtensionField(extension, 'parseHTML', context));
|
|
718
498
|
if (parseHTML) {
|
|
719
|
-
schema.parseDOM = parseHTML
|
|
720
|
-
.map(parseRule => injectExtensionAttributesToParseRule(parseRule, extensionAttributes));
|
|
499
|
+
schema.parseDOM = parseHTML.map(parseRule => injectExtensionAttributesToParseRule(parseRule, extensionAttributes));
|
|
721
500
|
}
|
|
722
501
|
const renderHTML = getExtensionField(extension, 'renderHTML', context);
|
|
723
502
|
if (renderHTML) {
|
|
@@ -736,13 +515,360 @@ function getSchemaByResolvedExtensions(extensions) {
|
|
|
736
515
|
}
|
|
737
516
|
|
|
738
517
|
function getSchemaTypeByName(name, schema) {
|
|
739
|
-
|
|
740
|
-
|
|
518
|
+
return schema.nodes[name] || schema.marks[name] || null;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
function isExtensionRulesEnabled(extension, enabled) {
|
|
522
|
+
if (Array.isArray(enabled)) {
|
|
523
|
+
return enabled.some(enabledExtension => {
|
|
524
|
+
const name = typeof enabledExtension === 'string'
|
|
525
|
+
? enabledExtension
|
|
526
|
+
: enabledExtension.name;
|
|
527
|
+
return name === extension.name;
|
|
528
|
+
});
|
|
741
529
|
}
|
|
742
|
-
|
|
743
|
-
|
|
530
|
+
return enabled;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
const getTextContentFromNodes = ($from, maxMatch = 500) => {
|
|
534
|
+
let textBefore = '';
|
|
535
|
+
const sliceEndPos = $from.parentOffset;
|
|
536
|
+
$from.parent.nodesBetween(Math.max(0, sliceEndPos - maxMatch), sliceEndPos, (node, pos, parent, index) => {
|
|
537
|
+
var _a, _b;
|
|
538
|
+
const chunk = ((_b = (_a = node.type.spec).toText) === null || _b === void 0 ? void 0 : _b.call(_a, {
|
|
539
|
+
node,
|
|
540
|
+
pos,
|
|
541
|
+
parent,
|
|
542
|
+
index,
|
|
543
|
+
}))
|
|
544
|
+
|| node.textContent
|
|
545
|
+
|| '%leaf%';
|
|
546
|
+
textBefore += chunk.slice(0, Math.max(0, sliceEndPos - pos));
|
|
547
|
+
});
|
|
548
|
+
return textBefore;
|
|
549
|
+
};
|
|
550
|
+
|
|
551
|
+
function isRegExp(value) {
|
|
552
|
+
return Object.prototype.toString.call(value) === '[object RegExp]';
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
class InputRule {
|
|
556
|
+
constructor(config) {
|
|
557
|
+
this.find = config.find;
|
|
558
|
+
this.handler = config.handler;
|
|
744
559
|
}
|
|
745
|
-
|
|
560
|
+
}
|
|
561
|
+
const inputRuleMatcherHandler = (text, find) => {
|
|
562
|
+
if (isRegExp(find)) {
|
|
563
|
+
return find.exec(text);
|
|
564
|
+
}
|
|
565
|
+
const inputRuleMatch = find(text);
|
|
566
|
+
if (!inputRuleMatch) {
|
|
567
|
+
return null;
|
|
568
|
+
}
|
|
569
|
+
const result = [inputRuleMatch.text];
|
|
570
|
+
result.index = inputRuleMatch.index;
|
|
571
|
+
result.input = text;
|
|
572
|
+
result.data = inputRuleMatch.data;
|
|
573
|
+
if (inputRuleMatch.replaceWith) {
|
|
574
|
+
if (!inputRuleMatch.text.includes(inputRuleMatch.replaceWith)) {
|
|
575
|
+
console.warn('[tiptap warn]: "inputRuleMatch.replaceWith" must be part of "inputRuleMatch.text".');
|
|
576
|
+
}
|
|
577
|
+
result.push(inputRuleMatch.replaceWith);
|
|
578
|
+
}
|
|
579
|
+
return result;
|
|
580
|
+
};
|
|
581
|
+
function run$1(config) {
|
|
582
|
+
var _a;
|
|
583
|
+
const { editor, from, to, text, rules, plugin, } = config;
|
|
584
|
+
const { view } = editor;
|
|
585
|
+
if (view.composing) {
|
|
586
|
+
return false;
|
|
587
|
+
}
|
|
588
|
+
const $from = view.state.doc.resolve(from);
|
|
589
|
+
if (
|
|
590
|
+
// check for code node
|
|
591
|
+
$from.parent.type.spec.code
|
|
592
|
+
// check for code mark
|
|
593
|
+
|| !!((_a = ($from.nodeBefore || $from.nodeAfter)) === null || _a === void 0 ? void 0 : _a.marks.find(mark => mark.type.spec.code))) {
|
|
594
|
+
return false;
|
|
595
|
+
}
|
|
596
|
+
let matched = false;
|
|
597
|
+
const textBefore = getTextContentFromNodes($from) + text;
|
|
598
|
+
rules.forEach(rule => {
|
|
599
|
+
if (matched) {
|
|
600
|
+
return;
|
|
601
|
+
}
|
|
602
|
+
const match = inputRuleMatcherHandler(textBefore, rule.find);
|
|
603
|
+
if (!match) {
|
|
604
|
+
return;
|
|
605
|
+
}
|
|
606
|
+
const tr = view.state.tr;
|
|
607
|
+
const state = createChainableState({
|
|
608
|
+
state: view.state,
|
|
609
|
+
transaction: tr,
|
|
610
|
+
});
|
|
611
|
+
const range = {
|
|
612
|
+
from: from - (match[0].length - text.length),
|
|
613
|
+
to,
|
|
614
|
+
};
|
|
615
|
+
const { commands, chain, can } = new CommandManager({
|
|
616
|
+
editor,
|
|
617
|
+
state,
|
|
618
|
+
});
|
|
619
|
+
const handler = rule.handler({
|
|
620
|
+
state,
|
|
621
|
+
range,
|
|
622
|
+
match,
|
|
623
|
+
commands,
|
|
624
|
+
chain,
|
|
625
|
+
can,
|
|
626
|
+
});
|
|
627
|
+
// stop if there are no changes
|
|
628
|
+
if (handler === null || !tr.steps.length) {
|
|
629
|
+
return;
|
|
630
|
+
}
|
|
631
|
+
// store transform as meta data
|
|
632
|
+
// so we can undo input rules within the `undoInputRules` command
|
|
633
|
+
tr.setMeta(plugin, {
|
|
634
|
+
transform: tr,
|
|
635
|
+
from,
|
|
636
|
+
to,
|
|
637
|
+
text,
|
|
638
|
+
});
|
|
639
|
+
view.dispatch(tr);
|
|
640
|
+
matched = true;
|
|
641
|
+
});
|
|
642
|
+
return matched;
|
|
643
|
+
}
|
|
644
|
+
/**
|
|
645
|
+
* Create an input rules plugin. When enabled, it will cause text
|
|
646
|
+
* input that matches any of the given rules to trigger the rule’s
|
|
647
|
+
* action.
|
|
648
|
+
*/
|
|
649
|
+
function inputRulesPlugin(props) {
|
|
650
|
+
const { editor, rules } = props;
|
|
651
|
+
const plugin = new Plugin({
|
|
652
|
+
state: {
|
|
653
|
+
init() {
|
|
654
|
+
return null;
|
|
655
|
+
},
|
|
656
|
+
apply(tr, prev) {
|
|
657
|
+
const stored = tr.getMeta(plugin);
|
|
658
|
+
if (stored) {
|
|
659
|
+
return stored;
|
|
660
|
+
}
|
|
661
|
+
return tr.selectionSet || tr.docChanged ? null : prev;
|
|
662
|
+
},
|
|
663
|
+
},
|
|
664
|
+
props: {
|
|
665
|
+
handleTextInput(view, from, to, text) {
|
|
666
|
+
return run$1({
|
|
667
|
+
editor,
|
|
668
|
+
from,
|
|
669
|
+
to,
|
|
670
|
+
text,
|
|
671
|
+
rules,
|
|
672
|
+
plugin,
|
|
673
|
+
});
|
|
674
|
+
},
|
|
675
|
+
handleDOMEvents: {
|
|
676
|
+
compositionend: view => {
|
|
677
|
+
setTimeout(() => {
|
|
678
|
+
const { $cursor } = view.state.selection;
|
|
679
|
+
if ($cursor) {
|
|
680
|
+
run$1({
|
|
681
|
+
editor,
|
|
682
|
+
from: $cursor.pos,
|
|
683
|
+
to: $cursor.pos,
|
|
684
|
+
text: '',
|
|
685
|
+
rules,
|
|
686
|
+
plugin,
|
|
687
|
+
});
|
|
688
|
+
}
|
|
689
|
+
});
|
|
690
|
+
return false;
|
|
691
|
+
},
|
|
692
|
+
},
|
|
693
|
+
// add support for input rules to trigger on enter
|
|
694
|
+
// this is useful for example for code blocks
|
|
695
|
+
handleKeyDown(view, event) {
|
|
696
|
+
if (event.key !== 'Enter') {
|
|
697
|
+
return false;
|
|
698
|
+
}
|
|
699
|
+
const { $cursor } = view.state.selection;
|
|
700
|
+
if ($cursor) {
|
|
701
|
+
return run$1({
|
|
702
|
+
editor,
|
|
703
|
+
from: $cursor.pos,
|
|
704
|
+
to: $cursor.pos,
|
|
705
|
+
text: '\n',
|
|
706
|
+
rules,
|
|
707
|
+
plugin,
|
|
708
|
+
});
|
|
709
|
+
}
|
|
710
|
+
return false;
|
|
711
|
+
},
|
|
712
|
+
},
|
|
713
|
+
// @ts-ignore
|
|
714
|
+
isInputRules: true,
|
|
715
|
+
});
|
|
716
|
+
return plugin;
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
function isNumber(value) {
|
|
720
|
+
return typeof value === 'number';
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
class PasteRule {
|
|
724
|
+
constructor(config) {
|
|
725
|
+
this.find = config.find;
|
|
726
|
+
this.handler = config.handler;
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
const pasteRuleMatcherHandler = (text, find) => {
|
|
730
|
+
if (isRegExp(find)) {
|
|
731
|
+
return [...text.matchAll(find)];
|
|
732
|
+
}
|
|
733
|
+
const matches = find(text);
|
|
734
|
+
if (!matches) {
|
|
735
|
+
return [];
|
|
736
|
+
}
|
|
737
|
+
return matches.map(pasteRuleMatch => {
|
|
738
|
+
const result = [pasteRuleMatch.text];
|
|
739
|
+
result.index = pasteRuleMatch.index;
|
|
740
|
+
result.input = text;
|
|
741
|
+
result.data = pasteRuleMatch.data;
|
|
742
|
+
if (pasteRuleMatch.replaceWith) {
|
|
743
|
+
if (!pasteRuleMatch.text.includes(pasteRuleMatch.replaceWith)) {
|
|
744
|
+
console.warn('[tiptap warn]: "pasteRuleMatch.replaceWith" must be part of "pasteRuleMatch.text".');
|
|
745
|
+
}
|
|
746
|
+
result.push(pasteRuleMatch.replaceWith);
|
|
747
|
+
}
|
|
748
|
+
return result;
|
|
749
|
+
});
|
|
750
|
+
};
|
|
751
|
+
function run(config) {
|
|
752
|
+
const { editor, state, from, to, rule, } = config;
|
|
753
|
+
const { commands, chain, can } = new CommandManager({
|
|
754
|
+
editor,
|
|
755
|
+
state,
|
|
756
|
+
});
|
|
757
|
+
const handlers = [];
|
|
758
|
+
state.doc.nodesBetween(from, to, (node, pos) => {
|
|
759
|
+
if (!node.isTextblock || node.type.spec.code) {
|
|
760
|
+
return;
|
|
761
|
+
}
|
|
762
|
+
const resolvedFrom = Math.max(from, pos);
|
|
763
|
+
const resolvedTo = Math.min(to, pos + node.content.size);
|
|
764
|
+
const textToMatch = node.textBetween(resolvedFrom - pos, resolvedTo - pos, undefined, '\ufffc');
|
|
765
|
+
const matches = pasteRuleMatcherHandler(textToMatch, rule.find);
|
|
766
|
+
matches.forEach(match => {
|
|
767
|
+
if (match.index === undefined) {
|
|
768
|
+
return;
|
|
769
|
+
}
|
|
770
|
+
const start = resolvedFrom + match.index + 1;
|
|
771
|
+
const end = start + match[0].length;
|
|
772
|
+
const range = {
|
|
773
|
+
from: state.tr.mapping.map(start),
|
|
774
|
+
to: state.tr.mapping.map(end),
|
|
775
|
+
};
|
|
776
|
+
const handler = rule.handler({
|
|
777
|
+
state,
|
|
778
|
+
range,
|
|
779
|
+
match,
|
|
780
|
+
commands,
|
|
781
|
+
chain,
|
|
782
|
+
can,
|
|
783
|
+
});
|
|
784
|
+
handlers.push(handler);
|
|
785
|
+
});
|
|
786
|
+
});
|
|
787
|
+
const success = handlers.every(handler => handler !== null);
|
|
788
|
+
return success;
|
|
789
|
+
}
|
|
790
|
+
/**
|
|
791
|
+
* Create an paste rules plugin. When enabled, it will cause pasted
|
|
792
|
+
* text that matches any of the given rules to trigger the rule’s
|
|
793
|
+
* action.
|
|
794
|
+
*/
|
|
795
|
+
function pasteRulesPlugin(props) {
|
|
796
|
+
const { editor, rules } = props;
|
|
797
|
+
let dragSourceElement = null;
|
|
798
|
+
let isPastedFromProseMirror = false;
|
|
799
|
+
let isDroppedFromProseMirror = false;
|
|
800
|
+
const plugins = rules.map(rule => {
|
|
801
|
+
return new Plugin({
|
|
802
|
+
// we register a global drag handler to track the current drag source element
|
|
803
|
+
view(view) {
|
|
804
|
+
const handleDragstart = (event) => {
|
|
805
|
+
var _a;
|
|
806
|
+
dragSourceElement = ((_a = view.dom.parentElement) === null || _a === void 0 ? void 0 : _a.contains(event.target))
|
|
807
|
+
? view.dom.parentElement
|
|
808
|
+
: null;
|
|
809
|
+
};
|
|
810
|
+
window.addEventListener('dragstart', handleDragstart);
|
|
811
|
+
return {
|
|
812
|
+
destroy() {
|
|
813
|
+
window.removeEventListener('dragstart', handleDragstart);
|
|
814
|
+
},
|
|
815
|
+
};
|
|
816
|
+
},
|
|
817
|
+
props: {
|
|
818
|
+
handleDOMEvents: {
|
|
819
|
+
drop: view => {
|
|
820
|
+
isDroppedFromProseMirror = dragSourceElement === view.dom.parentElement;
|
|
821
|
+
return false;
|
|
822
|
+
},
|
|
823
|
+
paste: (view, event) => {
|
|
824
|
+
var _a;
|
|
825
|
+
const html = (_a = event.clipboardData) === null || _a === void 0 ? void 0 : _a.getData('text/html');
|
|
826
|
+
isPastedFromProseMirror = !!(html === null || html === void 0 ? void 0 : html.includes('data-pm-slice'));
|
|
827
|
+
return false;
|
|
828
|
+
},
|
|
829
|
+
},
|
|
830
|
+
},
|
|
831
|
+
appendTransaction: (transactions, oldState, state) => {
|
|
832
|
+
const transaction = transactions[0];
|
|
833
|
+
const isPaste = transaction.getMeta('uiEvent') === 'paste' && !isPastedFromProseMirror;
|
|
834
|
+
const isDrop = transaction.getMeta('uiEvent') === 'drop' && !isDroppedFromProseMirror;
|
|
835
|
+
if (!isPaste && !isDrop) {
|
|
836
|
+
return;
|
|
837
|
+
}
|
|
838
|
+
// stop if there is no changed range
|
|
839
|
+
const from = oldState.doc.content.findDiffStart(state.doc.content);
|
|
840
|
+
const to = oldState.doc.content.findDiffEnd(state.doc.content);
|
|
841
|
+
if (!isNumber(from) || !to || from === to.b) {
|
|
842
|
+
return;
|
|
843
|
+
}
|
|
844
|
+
// build a chainable state
|
|
845
|
+
// so we can use a single transaction for all paste rules
|
|
846
|
+
const tr = state.tr;
|
|
847
|
+
const chainableState = createChainableState({
|
|
848
|
+
state,
|
|
849
|
+
transaction: tr,
|
|
850
|
+
});
|
|
851
|
+
const handler = run({
|
|
852
|
+
editor,
|
|
853
|
+
state: chainableState,
|
|
854
|
+
from: Math.max(from - 1, 0),
|
|
855
|
+
to: to.b - 1,
|
|
856
|
+
rule,
|
|
857
|
+
});
|
|
858
|
+
// stop if there are no changes
|
|
859
|
+
if (!handler || !tr.steps.length) {
|
|
860
|
+
return;
|
|
861
|
+
}
|
|
862
|
+
return tr;
|
|
863
|
+
},
|
|
864
|
+
});
|
|
865
|
+
});
|
|
866
|
+
return plugins;
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
function findDuplicates(items) {
|
|
870
|
+
const filtered = items.filter((el, index) => items.indexOf(el) !== index);
|
|
871
|
+
return [...new Set(filtered)];
|
|
746
872
|
}
|
|
747
873
|
|
|
748
874
|
class ExtensionManager {
|
|
@@ -753,9 +879,12 @@ class ExtensionManager {
|
|
|
753
879
|
this.schema = getSchemaByResolvedExtensions(this.extensions);
|
|
754
880
|
this.extensions.forEach(extension => {
|
|
755
881
|
var _a;
|
|
882
|
+
// store extension storage in editor
|
|
883
|
+
this.editor.extensionStorage[extension.name] = extension.storage;
|
|
756
884
|
const context = {
|
|
757
885
|
name: extension.name,
|
|
758
886
|
options: extension.options,
|
|
887
|
+
storage: extension.storage,
|
|
759
888
|
editor: this.editor,
|
|
760
889
|
type: getSchemaTypeByName(extension.name, this.schema),
|
|
761
890
|
};
|
|
@@ -800,26 +929,31 @@ class ExtensionManager {
|
|
|
800
929
|
});
|
|
801
930
|
}
|
|
802
931
|
static resolve(extensions) {
|
|
803
|
-
|
|
932
|
+
const resolvedExtensions = ExtensionManager.sort(ExtensionManager.flatten(extensions));
|
|
933
|
+
const duplicatedNames = findDuplicates(resolvedExtensions.map(extension => extension.name));
|
|
934
|
+
if (duplicatedNames.length) {
|
|
935
|
+
console.warn(`[tiptap warn]: Duplicate extension names found: [${duplicatedNames
|
|
936
|
+
.map(item => `'${item}'`)
|
|
937
|
+
.join(', ')}]. This can lead to issues.`);
|
|
938
|
+
}
|
|
939
|
+
return resolvedExtensions;
|
|
804
940
|
}
|
|
805
941
|
static flatten(extensions) {
|
|
806
|
-
return extensions
|
|
942
|
+
return (extensions
|
|
807
943
|
.map(extension => {
|
|
808
944
|
const context = {
|
|
809
945
|
name: extension.name,
|
|
810
946
|
options: extension.options,
|
|
947
|
+
storage: extension.storage,
|
|
811
948
|
};
|
|
812
949
|
const addExtensions = getExtensionField(extension, 'addExtensions', context);
|
|
813
950
|
if (addExtensions) {
|
|
814
|
-
return [
|
|
815
|
-
extension,
|
|
816
|
-
...this.flatten(addExtensions()),
|
|
817
|
-
];
|
|
951
|
+
return [extension, ...this.flatten(addExtensions())];
|
|
818
952
|
}
|
|
819
953
|
return extension;
|
|
820
954
|
})
|
|
821
955
|
// `Infinity` will break TypeScript so we set a number that is probably high enough
|
|
822
|
-
.flat(10);
|
|
956
|
+
.flat(10));
|
|
823
957
|
}
|
|
824
958
|
static sort(extensions) {
|
|
825
959
|
const defaultPriority = 100;
|
|
@@ -840,6 +974,7 @@ class ExtensionManager {
|
|
|
840
974
|
const context = {
|
|
841
975
|
name: extension.name,
|
|
842
976
|
options: extension.options,
|
|
977
|
+
storage: extension.storage,
|
|
843
978
|
editor: this.editor,
|
|
844
979
|
type: getSchemaTypeByName(extension.name, this.schema),
|
|
845
980
|
};
|
|
@@ -854,38 +989,46 @@ class ExtensionManager {
|
|
|
854
989
|
}, {});
|
|
855
990
|
}
|
|
856
991
|
get plugins() {
|
|
857
|
-
|
|
858
|
-
|
|
992
|
+
const { editor } = this;
|
|
993
|
+
// With ProseMirror, first plugins within an array are executed first.
|
|
994
|
+
// In tiptap, we provide the ability to override plugins,
|
|
995
|
+
// so it feels more natural to run plugins at the end of an array first.
|
|
996
|
+
// That’s why we have to reverse the `extensions` array and sort again
|
|
997
|
+
// based on the `priority` option.
|
|
998
|
+
const extensions = ExtensionManager.sort([...this.extensions].reverse());
|
|
999
|
+
const inputRules = [];
|
|
1000
|
+
const pasteRules = [];
|
|
1001
|
+
const allPlugins = extensions
|
|
859
1002
|
.map(extension => {
|
|
860
1003
|
const context = {
|
|
861
1004
|
name: extension.name,
|
|
862
1005
|
options: extension.options,
|
|
863
|
-
|
|
1006
|
+
storage: extension.storage,
|
|
1007
|
+
editor,
|
|
864
1008
|
type: getSchemaTypeByName(extension.name, this.schema),
|
|
865
1009
|
};
|
|
866
1010
|
const plugins = [];
|
|
867
1011
|
const addKeyboardShortcuts = getExtensionField(extension, 'addKeyboardShortcuts', context);
|
|
1012
|
+
let defaultBindings = {};
|
|
1013
|
+
// bind exit handling
|
|
1014
|
+
if (extension.type === 'mark' && extension.config.exitable) {
|
|
1015
|
+
defaultBindings.ArrowRight = () => Mark.handleExit({ editor, mark: extension });
|
|
1016
|
+
}
|
|
868
1017
|
if (addKeyboardShortcuts) {
|
|
869
|
-
const bindings = Object.fromEntries(Object
|
|
870
|
-
|
|
871
|
-
.map(([shortcut, method]) => {
|
|
872
|
-
return [shortcut, () => method({ editor: this.editor })];
|
|
1018
|
+
const bindings = Object.fromEntries(Object.entries(addKeyboardShortcuts()).map(([shortcut, method]) => {
|
|
1019
|
+
return [shortcut, () => method({ editor })];
|
|
873
1020
|
}));
|
|
874
|
-
|
|
875
|
-
plugins.push(keyMapPlugin);
|
|
1021
|
+
defaultBindings = { ...defaultBindings, ...bindings };
|
|
876
1022
|
}
|
|
1023
|
+
const keyMapPlugin = keymap(defaultBindings);
|
|
1024
|
+
plugins.push(keyMapPlugin);
|
|
877
1025
|
const addInputRules = getExtensionField(extension, 'addInputRules', context);
|
|
878
|
-
if (
|
|
879
|
-
|
|
880
|
-
const inputRulePlugins = inputRules$1.length
|
|
881
|
-
? [inputRules({ rules: inputRules$1 })]
|
|
882
|
-
: [];
|
|
883
|
-
plugins.push(...inputRulePlugins);
|
|
1026
|
+
if (isExtensionRulesEnabled(extension, editor.options.enableInputRules) && addInputRules) {
|
|
1027
|
+
inputRules.push(...addInputRules());
|
|
884
1028
|
}
|
|
885
1029
|
const addPasteRules = getExtensionField(extension, 'addPasteRules', context);
|
|
886
|
-
if (
|
|
887
|
-
|
|
888
|
-
plugins.push(...pasteRulePlugins);
|
|
1030
|
+
if (isExtensionRulesEnabled(extension, editor.options.enablePasteRules) && addPasteRules) {
|
|
1031
|
+
pasteRules.push(...addPasteRules());
|
|
889
1032
|
}
|
|
890
1033
|
const addProseMirrorPlugins = getExtensionField(extension, 'addProseMirrorPlugins', context);
|
|
891
1034
|
if (addProseMirrorPlugins) {
|
|
@@ -895,6 +1038,17 @@ class ExtensionManager {
|
|
|
895
1038
|
return plugins;
|
|
896
1039
|
})
|
|
897
1040
|
.flat();
|
|
1041
|
+
return [
|
|
1042
|
+
inputRulesPlugin({
|
|
1043
|
+
editor,
|
|
1044
|
+
rules: inputRules,
|
|
1045
|
+
}),
|
|
1046
|
+
...pasteRulesPlugin({
|
|
1047
|
+
editor,
|
|
1048
|
+
rules: pasteRules,
|
|
1049
|
+
}),
|
|
1050
|
+
...allPlugins,
|
|
1051
|
+
];
|
|
898
1052
|
}
|
|
899
1053
|
get attributes() {
|
|
900
1054
|
return getAttributesFromExtensions(this.extensions);
|
|
@@ -909,6 +1063,7 @@ class ExtensionManager {
|
|
|
909
1063
|
const context = {
|
|
910
1064
|
name: extension.name,
|
|
911
1065
|
options: extension.options,
|
|
1066
|
+
storage: extension.storage,
|
|
912
1067
|
editor,
|
|
913
1068
|
type: getNodeType(extension.name, this.schema),
|
|
914
1069
|
};
|
|
@@ -930,89 +1085,35 @@ class ExtensionManager {
|
|
|
930
1085
|
return [extension.name, nodeview];
|
|
931
1086
|
}));
|
|
932
1087
|
}
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
editor,
|
|
943
|
-
type: getNodeType(extension.name, this.schema),
|
|
944
|
-
};
|
|
945
|
-
const renderText = getExtensionField(extension, 'renderText', context);
|
|
946
|
-
if (!renderText) {
|
|
947
|
-
return [];
|
|
948
|
-
}
|
|
949
|
-
const textSerializer = (props) => renderText(props);
|
|
950
|
-
return [extension.name, textSerializer];
|
|
951
|
-
}));
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
// see: https://github.com/mesqueeb/is-what/blob/88d6e4ca92fb2baab6003c54e02eedf4e729e5ab/src/index.ts
|
|
1091
|
+
function getType(value) {
|
|
1092
|
+
return Object.prototype.toString.call(value).slice(8, -1);
|
|
1093
|
+
}
|
|
1094
|
+
function isPlainObject(value) {
|
|
1095
|
+
if (getType(value) !== 'Object') {
|
|
1096
|
+
return false;
|
|
952
1097
|
}
|
|
1098
|
+
return value.constructor === Object && Object.getPrototypeOf(value) === Object.prototype;
|
|
953
1099
|
}
|
|
954
1100
|
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
}
|
|
971
|
-
return this;
|
|
972
|
-
}
|
|
973
|
-
off(event, fn) {
|
|
974
|
-
const callbacks = this.callbacks[event];
|
|
975
|
-
if (callbacks) {
|
|
976
|
-
if (fn) {
|
|
977
|
-
this.callbacks[event] = callbacks.filter(callback => callback !== fn);
|
|
978
|
-
}
|
|
979
|
-
else {
|
|
980
|
-
delete this.callbacks[event];
|
|
981
|
-
}
|
|
982
|
-
}
|
|
983
|
-
return this;
|
|
984
|
-
}
|
|
985
|
-
removeAllListeners() {
|
|
986
|
-
this.callbacks = {};
|
|
987
|
-
}
|
|
988
|
-
}
|
|
989
|
-
|
|
990
|
-
// see: https://github.com/mesqueeb/is-what/blob/88d6e4ca92fb2baab6003c54e02eedf4e729e5ab/src/index.ts
|
|
991
|
-
function getType(payload) {
|
|
992
|
-
return Object.prototype.toString.call(payload).slice(8, -1);
|
|
993
|
-
}
|
|
994
|
-
function isPlainObject(payload) {
|
|
995
|
-
if (getType(payload) !== 'Object')
|
|
996
|
-
return false;
|
|
997
|
-
return payload.constructor === Object && Object.getPrototypeOf(payload) === Object.prototype;
|
|
998
|
-
}
|
|
999
|
-
|
|
1000
|
-
function mergeDeep(target, source) {
|
|
1001
|
-
const output = { ...target };
|
|
1002
|
-
if (isPlainObject(target) && isPlainObject(source)) {
|
|
1003
|
-
Object.keys(source).forEach(key => {
|
|
1004
|
-
if (isPlainObject(source[key])) {
|
|
1005
|
-
if (!(key in target)) {
|
|
1006
|
-
Object.assign(output, { [key]: source[key] });
|
|
1007
|
-
}
|
|
1008
|
-
else {
|
|
1009
|
-
output[key] = mergeDeep(target[key], source[key]);
|
|
1010
|
-
}
|
|
1011
|
-
}
|
|
1012
|
-
else {
|
|
1013
|
-
Object.assign(output, { [key]: source[key] });
|
|
1014
|
-
}
|
|
1015
|
-
});
|
|
1101
|
+
function mergeDeep(target, source) {
|
|
1102
|
+
const output = { ...target };
|
|
1103
|
+
if (isPlainObject(target) && isPlainObject(source)) {
|
|
1104
|
+
Object.keys(source).forEach(key => {
|
|
1105
|
+
if (isPlainObject(source[key])) {
|
|
1106
|
+
if (!(key in target)) {
|
|
1107
|
+
Object.assign(output, { [key]: source[key] });
|
|
1108
|
+
}
|
|
1109
|
+
else {
|
|
1110
|
+
output[key] = mergeDeep(target[key], source[key]);
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
else {
|
|
1114
|
+
Object.assign(output, { [key]: source[key] });
|
|
1115
|
+
}
|
|
1116
|
+
});
|
|
1016
1117
|
}
|
|
1017
1118
|
return output;
|
|
1018
1119
|
}
|
|
@@ -1032,7 +1133,20 @@ class Extension {
|
|
|
1032
1133
|
...config,
|
|
1033
1134
|
};
|
|
1034
1135
|
this.name = this.config.name;
|
|
1136
|
+
if (config.defaultOptions) {
|
|
1137
|
+
console.warn(`[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${this.name}".`);
|
|
1138
|
+
}
|
|
1139
|
+
// TODO: remove `addOptions` fallback
|
|
1035
1140
|
this.options = this.config.defaultOptions;
|
|
1141
|
+
if (this.config.addOptions) {
|
|
1142
|
+
this.options = callOrReturn(getExtensionField(this, 'addOptions', {
|
|
1143
|
+
name: this.name,
|
|
1144
|
+
}));
|
|
1145
|
+
}
|
|
1146
|
+
this.storage = callOrReturn(getExtensionField(this, 'addStorage', {
|
|
1147
|
+
name: this.name,
|
|
1148
|
+
options: this.options,
|
|
1149
|
+
})) || {};
|
|
1036
1150
|
}
|
|
1037
1151
|
static create(config = {}) {
|
|
1038
1152
|
return new Extension(config);
|
|
@@ -1042,49 +1156,74 @@ class Extension {
|
|
|
1042
1156
|
// with different calls of `configure`
|
|
1043
1157
|
const extension = this.extend();
|
|
1044
1158
|
extension.options = mergeDeep(this.options, options);
|
|
1159
|
+
extension.storage = callOrReturn(getExtensionField(extension, 'addStorage', {
|
|
1160
|
+
name: extension.name,
|
|
1161
|
+
options: extension.options,
|
|
1162
|
+
}));
|
|
1045
1163
|
return extension;
|
|
1046
1164
|
}
|
|
1047
1165
|
extend(extendedConfig = {}) {
|
|
1048
1166
|
const extension = new Extension(extendedConfig);
|
|
1049
1167
|
extension.parent = this;
|
|
1050
1168
|
this.child = extension;
|
|
1051
|
-
extension.name = extendedConfig.name
|
|
1052
|
-
|
|
1053
|
-
: extension.
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
: extension.
|
|
1169
|
+
extension.name = extendedConfig.name ? extendedConfig.name : extension.parent.name;
|
|
1170
|
+
if (extendedConfig.defaultOptions) {
|
|
1171
|
+
console.warn(`[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${extension.name}".`);
|
|
1172
|
+
}
|
|
1173
|
+
extension.options = callOrReturn(getExtensionField(extension, 'addOptions', {
|
|
1174
|
+
name: extension.name,
|
|
1175
|
+
}));
|
|
1176
|
+
extension.storage = callOrReturn(getExtensionField(extension, 'addStorage', {
|
|
1177
|
+
name: extension.name,
|
|
1178
|
+
options: extension.options,
|
|
1179
|
+
}));
|
|
1057
1180
|
return extension;
|
|
1058
1181
|
}
|
|
1059
1182
|
}
|
|
1060
1183
|
|
|
1061
|
-
|
|
1184
|
+
function getTextBetween(startNode, range, options) {
|
|
1185
|
+
const { from, to } = range;
|
|
1186
|
+
const { blockSeparator = '\n\n', textSerializers = {} } = options || {};
|
|
1062
1187
|
let text = '';
|
|
1063
1188
|
let separated = true;
|
|
1064
|
-
|
|
1189
|
+
startNode.nodesBetween(from, to, (node, pos, parent, index) => {
|
|
1065
1190
|
var _a;
|
|
1066
|
-
const textSerializer =
|
|
1191
|
+
const textSerializer = textSerializers === null || textSerializers === void 0 ? void 0 : textSerializers[node.type.name];
|
|
1067
1192
|
if (textSerializer) {
|
|
1068
|
-
|
|
1069
|
-
|
|
1193
|
+
if (node.isBlock && !separated) {
|
|
1194
|
+
text += blockSeparator;
|
|
1195
|
+
separated = true;
|
|
1196
|
+
}
|
|
1197
|
+
if (parent) {
|
|
1198
|
+
text += textSerializer({
|
|
1199
|
+
node,
|
|
1200
|
+
pos,
|
|
1201
|
+
parent,
|
|
1202
|
+
index,
|
|
1203
|
+
range,
|
|
1204
|
+
});
|
|
1205
|
+
}
|
|
1070
1206
|
}
|
|
1071
1207
|
else if (node.isText) {
|
|
1072
|
-
text += (_a = node === null || node === void 0 ? void 0 : node.text) === null || _a === void 0 ? void 0 : _a.slice(Math.max(from, pos) - pos, to - pos);
|
|
1073
|
-
separated =
|
|
1208
|
+
text += (_a = node === null || node === void 0 ? void 0 : node.text) === null || _a === void 0 ? void 0 : _a.slice(Math.max(from, pos) - pos, to - pos); // eslint-disable-line
|
|
1209
|
+
separated = false;
|
|
1074
1210
|
}
|
|
1075
|
-
else if (node.
|
|
1076
|
-
text += leafText;
|
|
1077
|
-
separated = !blockSeparator;
|
|
1078
|
-
}
|
|
1079
|
-
else if (!separated && node.isBlock) {
|
|
1211
|
+
else if (node.isBlock && !separated) {
|
|
1080
1212
|
text += blockSeparator;
|
|
1081
1213
|
separated = true;
|
|
1082
1214
|
}
|
|
1083
|
-
}
|
|
1215
|
+
});
|
|
1084
1216
|
return text;
|
|
1085
|
-
}
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
function getTextSerializersFromSchema(schema) {
|
|
1220
|
+
return Object.fromEntries(Object.entries(schema.nodes)
|
|
1221
|
+
.filter(([, node]) => node.spec.toText)
|
|
1222
|
+
.map(([name, node]) => [name, node.spec.toText]));
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1086
1225
|
const ClipboardTextSerializer = Extension.create({
|
|
1087
|
-
name: '
|
|
1226
|
+
name: 'clipboardTextSerializer',
|
|
1088
1227
|
addProseMirrorPlugins() {
|
|
1089
1228
|
return [
|
|
1090
1229
|
new Plugin({
|
|
@@ -1092,8 +1231,16 @@ const ClipboardTextSerializer = Extension.create({
|
|
|
1092
1231
|
props: {
|
|
1093
1232
|
clipboardTextSerializer: () => {
|
|
1094
1233
|
const { editor } = this;
|
|
1095
|
-
const {
|
|
1096
|
-
|
|
1234
|
+
const { state, schema } = editor;
|
|
1235
|
+
const { doc, selection } = state;
|
|
1236
|
+
const { ranges } = selection;
|
|
1237
|
+
const from = Math.min(...ranges.map(range => range.$from.pos));
|
|
1238
|
+
const to = Math.max(...ranges.map(range => range.$to.pos));
|
|
1239
|
+
const textSerializers = getTextSerializersFromSchema(schema);
|
|
1240
|
+
const range = { from, to };
|
|
1241
|
+
return getTextBetween(doc, range, {
|
|
1242
|
+
textSerializers,
|
|
1243
|
+
});
|
|
1097
1244
|
},
|
|
1098
1245
|
},
|
|
1099
1246
|
}),
|
|
@@ -1103,47 +1250,45 @@ const ClipboardTextSerializer = Extension.create({
|
|
|
1103
1250
|
|
|
1104
1251
|
const blur = () => ({ editor, view }) => {
|
|
1105
1252
|
requestAnimationFrame(() => {
|
|
1253
|
+
var _a;
|
|
1106
1254
|
if (!editor.isDestroyed) {
|
|
1107
1255
|
view.dom.blur();
|
|
1256
|
+
// Browsers should remove the caret on blur but safari does not.
|
|
1257
|
+
// See: https://github.com/ueberdosis/tiptap/issues/2405
|
|
1258
|
+
(_a = window === null || window === void 0 ? void 0 : window.getSelection()) === null || _a === void 0 ? void 0 : _a.removeAllRanges();
|
|
1108
1259
|
}
|
|
1109
1260
|
});
|
|
1110
1261
|
return true;
|
|
1111
1262
|
};
|
|
1112
1263
|
|
|
1113
|
-
var blur$1 = /*#__PURE__*/Object.freeze({
|
|
1114
|
-
__proto__: null,
|
|
1115
|
-
blur: blur
|
|
1116
|
-
});
|
|
1117
|
-
|
|
1118
1264
|
const clearContent = (emitUpdate = false) => ({ commands }) => {
|
|
1119
1265
|
return commands.setContent('', emitUpdate);
|
|
1120
1266
|
};
|
|
1121
1267
|
|
|
1122
|
-
var clearContent$1 = /*#__PURE__*/Object.freeze({
|
|
1123
|
-
__proto__: null,
|
|
1124
|
-
clearContent: clearContent
|
|
1125
|
-
});
|
|
1126
|
-
|
|
1127
1268
|
const clearNodes = () => ({ state, tr, dispatch }) => {
|
|
1128
1269
|
const { selection } = tr;
|
|
1129
1270
|
const { ranges } = selection;
|
|
1130
|
-
|
|
1131
|
-
|
|
1271
|
+
if (!dispatch) {
|
|
1272
|
+
return true;
|
|
1273
|
+
}
|
|
1274
|
+
ranges.forEach(({ $from, $to }) => {
|
|
1275
|
+
state.doc.nodesBetween($from.pos, $to.pos, (node, pos) => {
|
|
1132
1276
|
if (node.type.isText) {
|
|
1133
1277
|
return;
|
|
1134
1278
|
}
|
|
1135
|
-
const
|
|
1136
|
-
const $
|
|
1137
|
-
const
|
|
1279
|
+
const { doc, mapping } = tr;
|
|
1280
|
+
const $mappedFrom = doc.resolve(mapping.map(pos));
|
|
1281
|
+
const $mappedTo = doc.resolve(mapping.map(pos + node.nodeSize));
|
|
1282
|
+
const nodeRange = $mappedFrom.blockRange($mappedTo);
|
|
1138
1283
|
if (!nodeRange) {
|
|
1139
1284
|
return;
|
|
1140
1285
|
}
|
|
1141
1286
|
const targetLiftDepth = liftTarget(nodeRange);
|
|
1142
|
-
if (node.type.isTextblock
|
|
1143
|
-
const { defaultType } = $
|
|
1287
|
+
if (node.type.isTextblock) {
|
|
1288
|
+
const { defaultType } = $mappedFrom.parent.contentMatchAt($mappedFrom.index());
|
|
1144
1289
|
tr.setNodeMarkup(nodeRange.start, defaultType);
|
|
1145
1290
|
}
|
|
1146
|
-
if (
|
|
1291
|
+
if (targetLiftDepth || targetLiftDepth === 0) {
|
|
1147
1292
|
tr.lift(nodeRange, targetLiftDepth);
|
|
1148
1293
|
}
|
|
1149
1294
|
});
|
|
@@ -1151,28 +1296,35 @@ const clearNodes = () => ({ state, tr, dispatch }) => {
|
|
|
1151
1296
|
return true;
|
|
1152
1297
|
};
|
|
1153
1298
|
|
|
1154
|
-
var clearNodes$1 = /*#__PURE__*/Object.freeze({
|
|
1155
|
-
__proto__: null,
|
|
1156
|
-
clearNodes: clearNodes
|
|
1157
|
-
});
|
|
1158
|
-
|
|
1159
1299
|
const command = fn => props => {
|
|
1160
1300
|
return fn(props);
|
|
1161
1301
|
};
|
|
1162
1302
|
|
|
1163
|
-
var command$1 = /*#__PURE__*/Object.freeze({
|
|
1164
|
-
__proto__: null,
|
|
1165
|
-
command: command
|
|
1166
|
-
});
|
|
1167
|
-
|
|
1168
1303
|
const createParagraphNear = () => ({ state, dispatch }) => {
|
|
1169
|
-
return createParagraphNear$
|
|
1304
|
+
return createParagraphNear$1(state, dispatch);
|
|
1170
1305
|
};
|
|
1171
1306
|
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1307
|
+
const deleteCurrentNode = () => ({ tr, dispatch }) => {
|
|
1308
|
+
const { selection } = tr;
|
|
1309
|
+
const currentNode = selection.$anchor.node();
|
|
1310
|
+
// if there is content inside the current node, break out of this command
|
|
1311
|
+
if (currentNode.content.size > 0) {
|
|
1312
|
+
return false;
|
|
1313
|
+
}
|
|
1314
|
+
const $pos = tr.selection.$anchor;
|
|
1315
|
+
for (let depth = $pos.depth; depth > 0; depth -= 1) {
|
|
1316
|
+
const node = $pos.node(depth);
|
|
1317
|
+
if (node.type === currentNode.type) {
|
|
1318
|
+
if (dispatch) {
|
|
1319
|
+
const from = $pos.before(depth);
|
|
1320
|
+
const to = $pos.after(depth);
|
|
1321
|
+
tr.delete(from, to).scrollIntoView();
|
|
1322
|
+
}
|
|
1323
|
+
return true;
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
return false;
|
|
1327
|
+
};
|
|
1176
1328
|
|
|
1177
1329
|
const deleteNode = typeOrName => ({ tr, state, dispatch }) => {
|
|
1178
1330
|
const type = getNodeType(typeOrName, state.schema);
|
|
@@ -1191,11 +1343,6 @@ const deleteNode = typeOrName => ({ tr, state, dispatch }) => {
|
|
|
1191
1343
|
return false;
|
|
1192
1344
|
};
|
|
1193
1345
|
|
|
1194
|
-
var deleteNode$1 = /*#__PURE__*/Object.freeze({
|
|
1195
|
-
__proto__: null,
|
|
1196
|
-
deleteNode: deleteNode
|
|
1197
|
-
});
|
|
1198
|
-
|
|
1199
1346
|
const deleteRange = range => ({ tr, dispatch }) => {
|
|
1200
1347
|
const { from, to } = range;
|
|
1201
1348
|
if (dispatch) {
|
|
@@ -1204,37 +1351,38 @@ const deleteRange = range => ({ tr, dispatch }) => {
|
|
|
1204
1351
|
return true;
|
|
1205
1352
|
};
|
|
1206
1353
|
|
|
1207
|
-
var deleteRange$1 = /*#__PURE__*/Object.freeze({
|
|
1208
|
-
__proto__: null,
|
|
1209
|
-
deleteRange: deleteRange
|
|
1210
|
-
});
|
|
1211
|
-
|
|
1212
1354
|
const deleteSelection = () => ({ state, dispatch }) => {
|
|
1213
|
-
return deleteSelection$
|
|
1355
|
+
return deleteSelection$1(state, dispatch);
|
|
1214
1356
|
};
|
|
1215
1357
|
|
|
1216
|
-
var deleteSelection$1 = /*#__PURE__*/Object.freeze({
|
|
1217
|
-
__proto__: null,
|
|
1218
|
-
deleteSelection: deleteSelection
|
|
1219
|
-
});
|
|
1220
|
-
|
|
1221
1358
|
const enter = () => ({ commands }) => {
|
|
1222
1359
|
return commands.keyboardShortcut('Enter');
|
|
1223
1360
|
};
|
|
1224
1361
|
|
|
1225
|
-
var enter$1 = /*#__PURE__*/Object.freeze({
|
|
1226
|
-
__proto__: null,
|
|
1227
|
-
enter: enter
|
|
1228
|
-
});
|
|
1229
|
-
|
|
1230
1362
|
const exitCode = () => ({ state, dispatch }) => {
|
|
1231
|
-
return exitCode$
|
|
1363
|
+
return exitCode$1(state, dispatch);
|
|
1232
1364
|
};
|
|
1233
1365
|
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1366
|
+
/**
|
|
1367
|
+
* Check if object1 includes object2
|
|
1368
|
+
* @param object1 Object
|
|
1369
|
+
* @param object2 Object
|
|
1370
|
+
*/
|
|
1371
|
+
function objectIncludes(object1, object2, options = { strict: true }) {
|
|
1372
|
+
const keys = Object.keys(object2);
|
|
1373
|
+
if (!keys.length) {
|
|
1374
|
+
return true;
|
|
1375
|
+
}
|
|
1376
|
+
return keys.every(key => {
|
|
1377
|
+
if (options.strict) {
|
|
1378
|
+
return object2[key] === object1[key];
|
|
1379
|
+
}
|
|
1380
|
+
if (isRegExp(object2[key])) {
|
|
1381
|
+
return object2[key].test(object1[key]);
|
|
1382
|
+
}
|
|
1383
|
+
return object2[key] === object1[key];
|
|
1384
|
+
});
|
|
1385
|
+
}
|
|
1238
1386
|
|
|
1239
1387
|
function findMarkInSet(marks, type, attributes = {}) {
|
|
1240
1388
|
return marks.find(item => {
|
|
@@ -1248,25 +1396,28 @@ function getMarkRange($pos, type, attributes = {}) {
|
|
|
1248
1396
|
if (!$pos || !type) {
|
|
1249
1397
|
return;
|
|
1250
1398
|
}
|
|
1251
|
-
|
|
1399
|
+
let start = $pos.parent.childAfter($pos.parentOffset);
|
|
1400
|
+
if ($pos.parentOffset === start.offset && start.offset !== 0) {
|
|
1401
|
+
start = $pos.parent.childBefore($pos.parentOffset);
|
|
1402
|
+
}
|
|
1252
1403
|
if (!start.node) {
|
|
1253
1404
|
return;
|
|
1254
1405
|
}
|
|
1255
|
-
const mark = findMarkInSet(start.node.marks, type, attributes);
|
|
1406
|
+
const mark = findMarkInSet([...start.node.marks], type, attributes);
|
|
1256
1407
|
if (!mark) {
|
|
1257
1408
|
return;
|
|
1258
1409
|
}
|
|
1259
|
-
let startIndex =
|
|
1410
|
+
let startIndex = start.index;
|
|
1260
1411
|
let startPos = $pos.start() + start.offset;
|
|
1261
1412
|
let endIndex = startIndex + 1;
|
|
1262
1413
|
let endPos = startPos + start.node.nodeSize;
|
|
1263
|
-
findMarkInSet(start.node.marks, type, attributes);
|
|
1414
|
+
findMarkInSet([...start.node.marks], type, attributes);
|
|
1264
1415
|
while (startIndex > 0 && mark.isInSet($pos.parent.child(startIndex - 1).marks)) {
|
|
1265
1416
|
startIndex -= 1;
|
|
1266
1417
|
startPos -= $pos.parent.child(startIndex).nodeSize;
|
|
1267
1418
|
}
|
|
1268
1419
|
while (endIndex < $pos.parent.childCount
|
|
1269
|
-
&& isMarkInSet(
|
|
1420
|
+
&& isMarkInSet([...$pos.parent.child(endIndex).marks], type, attributes)) {
|
|
1270
1421
|
endPos += $pos.parent.child(endIndex).nodeSize;
|
|
1271
1422
|
endIndex += 1;
|
|
1272
1423
|
}
|
|
@@ -1276,6 +1427,16 @@ function getMarkRange($pos, type, attributes = {}) {
|
|
|
1276
1427
|
};
|
|
1277
1428
|
}
|
|
1278
1429
|
|
|
1430
|
+
function getMarkType(nameOrType, schema) {
|
|
1431
|
+
if (typeof nameOrType === 'string') {
|
|
1432
|
+
if (!schema.marks[nameOrType]) {
|
|
1433
|
+
throw Error(`There is no mark type named '${nameOrType}'. Maybe you forgot to add the extension?`);
|
|
1434
|
+
}
|
|
1435
|
+
return schema.marks[nameOrType];
|
|
1436
|
+
}
|
|
1437
|
+
return nameOrType;
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1279
1440
|
const extendMarkRange = (typeOrName, attributes = {}) => ({ tr, state, dispatch }) => {
|
|
1280
1441
|
const type = getMarkType(typeOrName, state.schema);
|
|
1281
1442
|
const { doc, selection } = tr;
|
|
@@ -1290,11 +1451,6 @@ const extendMarkRange = (typeOrName, attributes = {}) => ({ tr, state, dispatch
|
|
|
1290
1451
|
return true;
|
|
1291
1452
|
};
|
|
1292
1453
|
|
|
1293
|
-
var extendMarkRange$1 = /*#__PURE__*/Object.freeze({
|
|
1294
|
-
__proto__: null,
|
|
1295
|
-
extendMarkRange: extendMarkRange
|
|
1296
|
-
});
|
|
1297
|
-
|
|
1298
1454
|
const first = commands => props => {
|
|
1299
1455
|
const items = typeof commands === 'function'
|
|
1300
1456
|
? commands(props)
|
|
@@ -1307,63 +1463,66 @@ const first = commands => props => {
|
|
|
1307
1463
|
return false;
|
|
1308
1464
|
};
|
|
1309
1465
|
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
});
|
|
1466
|
+
function isTextSelection(value) {
|
|
1467
|
+
return value instanceof TextSelection;
|
|
1468
|
+
}
|
|
1314
1469
|
|
|
1315
1470
|
function minMax(value = 0, min = 0, max = 0) {
|
|
1316
1471
|
return Math.min(Math.max(value, min), max);
|
|
1317
1472
|
}
|
|
1318
1473
|
|
|
1319
|
-
function
|
|
1320
|
-
var _a;
|
|
1321
|
-
if (((_a = item.constructor) === null || _a === void 0 ? void 0 : _a.toString().substring(0, 5)) !== 'class') {
|
|
1322
|
-
return false;
|
|
1323
|
-
}
|
|
1324
|
-
return true;
|
|
1325
|
-
}
|
|
1326
|
-
|
|
1327
|
-
function isObject(item) {
|
|
1328
|
-
return (item
|
|
1329
|
-
&& typeof item === 'object'
|
|
1330
|
-
&& !Array.isArray(item)
|
|
1331
|
-
&& !isClass(item));
|
|
1332
|
-
}
|
|
1333
|
-
|
|
1334
|
-
function isTextSelection(value) {
|
|
1335
|
-
return isObject(value) && value instanceof TextSelection;
|
|
1336
|
-
}
|
|
1337
|
-
|
|
1338
|
-
function resolveSelection(state, position = null) {
|
|
1474
|
+
function resolveFocusPosition(doc, position = null) {
|
|
1339
1475
|
if (!position) {
|
|
1340
1476
|
return null;
|
|
1341
1477
|
}
|
|
1478
|
+
const selectionAtStart = Selection.atStart(doc);
|
|
1479
|
+
const selectionAtEnd = Selection.atEnd(doc);
|
|
1342
1480
|
if (position === 'start' || position === true) {
|
|
1343
|
-
return
|
|
1344
|
-
from: 0,
|
|
1345
|
-
to: 0,
|
|
1346
|
-
};
|
|
1481
|
+
return selectionAtStart;
|
|
1347
1482
|
}
|
|
1348
1483
|
if (position === 'end') {
|
|
1349
|
-
|
|
1350
|
-
return {
|
|
1351
|
-
from: size,
|
|
1352
|
-
to: size,
|
|
1353
|
-
};
|
|
1484
|
+
return selectionAtEnd;
|
|
1354
1485
|
}
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1486
|
+
const minPos = selectionAtStart.from;
|
|
1487
|
+
const maxPos = selectionAtEnd.to;
|
|
1488
|
+
if (position === 'all') {
|
|
1489
|
+
return TextSelection.create(doc, minMax(0, minPos, maxPos), minMax(doc.content.size, minPos, maxPos));
|
|
1490
|
+
}
|
|
1491
|
+
return TextSelection.create(doc, minMax(position, minPos, maxPos), minMax(position, minPos, maxPos));
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1494
|
+
function isiOS() {
|
|
1495
|
+
return [
|
|
1496
|
+
'iPad Simulator',
|
|
1497
|
+
'iPhone Simulator',
|
|
1498
|
+
'iPod Simulator',
|
|
1499
|
+
'iPad',
|
|
1500
|
+
'iPhone',
|
|
1501
|
+
'iPod',
|
|
1502
|
+
].includes(navigator.platform)
|
|
1503
|
+
// iPad on iOS 13 detection
|
|
1504
|
+
|| (navigator.userAgent.includes('Mac') && 'ontouchend' in document);
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1507
|
+
const focus = (position = null, options = {}) => ({ editor, view, tr, dispatch, }) => {
|
|
1508
|
+
options = {
|
|
1509
|
+
scrollIntoView: true,
|
|
1510
|
+
...options,
|
|
1358
1511
|
};
|
|
1359
|
-
}
|
|
1360
|
-
const focus = (position = null) => ({ editor, view, tr, dispatch, }) => {
|
|
1361
1512
|
const delayedFocus = () => {
|
|
1513
|
+
// focus within `requestAnimationFrame` breaks focus on iOS
|
|
1514
|
+
// so we have to call this
|
|
1515
|
+
if (isiOS()) {
|
|
1516
|
+
view.dom.focus();
|
|
1517
|
+
}
|
|
1362
1518
|
// For React we have to focus asynchronously. Otherwise wild things happen.
|
|
1363
1519
|
// see: https://github.com/ueberdosis/tiptap/issues/1520
|
|
1364
1520
|
requestAnimationFrame(() => {
|
|
1365
1521
|
if (!editor.isDestroyed) {
|
|
1366
1522
|
view.focus();
|
|
1523
|
+
if (options === null || options === void 0 ? void 0 : options.scrollIntoView) {
|
|
1524
|
+
editor.commands.scrollIntoView();
|
|
1525
|
+
}
|
|
1367
1526
|
}
|
|
1368
1527
|
});
|
|
1369
1528
|
};
|
|
@@ -1375,13 +1534,9 @@ const focus = (position = null) => ({ editor, view, tr, dispatch, }) => {
|
|
|
1375
1534
|
delayedFocus();
|
|
1376
1535
|
return true;
|
|
1377
1536
|
}
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
const
|
|
1381
|
-
const maxPos = Selection.atEnd(doc).to;
|
|
1382
|
-
const resolvedFrom = minMax(from, minPos, maxPos);
|
|
1383
|
-
const resolvedEnd = minMax(to, minPos, maxPos);
|
|
1384
|
-
const selection = TextSelection.create(doc, resolvedFrom, resolvedEnd);
|
|
1537
|
+
// pass through tr.doc instead of editor.state.doc
|
|
1538
|
+
// since transactions could change the editors state before this command has been run
|
|
1539
|
+
const selection = resolveFocusPosition(tr.doc, position) || editor.state.selection;
|
|
1385
1540
|
const isSameSelection = editor.state.selection.eq(selection);
|
|
1386
1541
|
if (dispatch) {
|
|
1387
1542
|
if (!isSameSelection) {
|
|
@@ -1389,36 +1544,54 @@ const focus = (position = null) => ({ editor, view, tr, dispatch, }) => {
|
|
|
1389
1544
|
}
|
|
1390
1545
|
// `tr.setSelection` resets the stored marks
|
|
1391
1546
|
// so we’ll restore them if the selection is the same as before
|
|
1392
|
-
if (isSameSelection && storedMarks) {
|
|
1393
|
-
tr.setStoredMarks(storedMarks);
|
|
1547
|
+
if (isSameSelection && tr.storedMarks) {
|
|
1548
|
+
tr.setStoredMarks(tr.storedMarks);
|
|
1394
1549
|
}
|
|
1395
1550
|
delayedFocus();
|
|
1396
1551
|
}
|
|
1397
1552
|
return true;
|
|
1398
1553
|
};
|
|
1399
1554
|
|
|
1400
|
-
var focus$1 = /*#__PURE__*/Object.freeze({
|
|
1401
|
-
__proto__: null,
|
|
1402
|
-
focus: focus
|
|
1403
|
-
});
|
|
1404
|
-
|
|
1405
1555
|
const forEach = (items, fn) => props => {
|
|
1406
1556
|
return items.every((item, index) => fn(item, { ...props, index }));
|
|
1407
1557
|
};
|
|
1408
1558
|
|
|
1409
|
-
var forEach$1 = /*#__PURE__*/Object.freeze({
|
|
1410
|
-
__proto__: null,
|
|
1411
|
-
forEach: forEach
|
|
1412
|
-
});
|
|
1413
|
-
|
|
1414
1559
|
const insertContent = (value, options) => ({ tr, commands }) => {
|
|
1415
1560
|
return commands.insertContentAt({ from: tr.selection.from, to: tr.selection.to }, value, options);
|
|
1416
1561
|
};
|
|
1417
1562
|
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1563
|
+
function elementFromString(value) {
|
|
1564
|
+
// add a wrapper to preserve leading and trailing whitespace
|
|
1565
|
+
const wrappedValue = `<body>${value}</body>`;
|
|
1566
|
+
return new window.DOMParser().parseFromString(wrappedValue, 'text/html').body;
|
|
1567
|
+
}
|
|
1568
|
+
|
|
1569
|
+
function createNodeFromContent(content, schema, options) {
|
|
1570
|
+
options = {
|
|
1571
|
+
slice: true,
|
|
1572
|
+
parseOptions: {},
|
|
1573
|
+
...options,
|
|
1574
|
+
};
|
|
1575
|
+
if (typeof content === 'object' && content !== null) {
|
|
1576
|
+
try {
|
|
1577
|
+
if (Array.isArray(content) && content.length > 0) {
|
|
1578
|
+
return Fragment.fromArray(content.map(item => schema.nodeFromJSON(item)));
|
|
1579
|
+
}
|
|
1580
|
+
return schema.nodeFromJSON(content);
|
|
1581
|
+
}
|
|
1582
|
+
catch (error) {
|
|
1583
|
+
console.warn('[tiptap warn]: Invalid content.', 'Passed value:', content, 'Error:', error);
|
|
1584
|
+
return createNodeFromContent('', schema, options);
|
|
1585
|
+
}
|
|
1586
|
+
}
|
|
1587
|
+
if (typeof content === 'string') {
|
|
1588
|
+
const parser = DOMParser.fromSchema(schema);
|
|
1589
|
+
return options.slice
|
|
1590
|
+
? parser.parseSlice(elementFromString(content), options.parseOptions).content
|
|
1591
|
+
: parser.parse(elementFromString(content), options.parseOptions);
|
|
1592
|
+
}
|
|
1593
|
+
return createNodeFromContent('', schema, options);
|
|
1594
|
+
}
|
|
1422
1595
|
|
|
1423
1596
|
// source: https://github.com/ProseMirror/prosemirror-state/blob/master/src/selection.js#L466
|
|
1424
1597
|
function selectionToInsertionEnd(tr, startLen, bias) {
|
|
@@ -1440,52 +1613,94 @@ function selectionToInsertionEnd(tr, startLen, bias) {
|
|
|
1440
1613
|
tr.setSelection(Selection.near(tr.doc.resolve(end), bias));
|
|
1441
1614
|
}
|
|
1442
1615
|
|
|
1616
|
+
const isFragment = (nodeOrFragment) => {
|
|
1617
|
+
return nodeOrFragment.toString().startsWith('<');
|
|
1618
|
+
};
|
|
1443
1619
|
const insertContentAt = (position, value, options) => ({ tr, dispatch, editor }) => {
|
|
1444
1620
|
if (dispatch) {
|
|
1621
|
+
options = {
|
|
1622
|
+
parseOptions: {},
|
|
1623
|
+
updateSelection: true,
|
|
1624
|
+
...options,
|
|
1625
|
+
};
|
|
1445
1626
|
const content = createNodeFromContent(value, editor.schema, {
|
|
1446
1627
|
parseOptions: {
|
|
1447
1628
|
preserveWhitespace: 'full',
|
|
1629
|
+
...options.parseOptions,
|
|
1448
1630
|
},
|
|
1449
|
-
...(options || {}),
|
|
1450
1631
|
});
|
|
1451
1632
|
// don’t dispatch an empty fragment because this can lead to strange errors
|
|
1452
1633
|
if (content.toString() === '<>') {
|
|
1453
1634
|
return true;
|
|
1454
1635
|
}
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1636
|
+
let { from, to } = typeof position === 'number' ? { from: position, to: position } : position;
|
|
1637
|
+
let isOnlyTextContent = true;
|
|
1638
|
+
let isOnlyBlockContent = true;
|
|
1639
|
+
const nodes = isFragment(content) ? content : [content];
|
|
1640
|
+
nodes.forEach(node => {
|
|
1641
|
+
// check if added node is valid
|
|
1642
|
+
node.check();
|
|
1643
|
+
isOnlyTextContent = isOnlyTextContent ? node.isText && node.marks.length === 0 : false;
|
|
1644
|
+
isOnlyBlockContent = isOnlyBlockContent ? node.isBlock : false;
|
|
1645
|
+
});
|
|
1646
|
+
// check if we can replace the wrapping node by
|
|
1647
|
+
// the newly inserted content
|
|
1648
|
+
// example:
|
|
1649
|
+
// replace an empty paragraph by an inserted image
|
|
1650
|
+
// instead of inserting the image below the paragraph
|
|
1651
|
+
if (from === to && isOnlyBlockContent) {
|
|
1652
|
+
const { parent } = tr.doc.resolve(from);
|
|
1653
|
+
const isEmptyTextBlock = parent.isTextblock && !parent.type.spec.code && !parent.childCount;
|
|
1654
|
+
if (isEmptyTextBlock) {
|
|
1655
|
+
from -= 1;
|
|
1656
|
+
to += 1;
|
|
1657
|
+
}
|
|
1658
|
+
}
|
|
1659
|
+
// if there is only plain text we have to use `insertText`
|
|
1660
|
+
// because this will keep the current marks
|
|
1661
|
+
if (isOnlyTextContent) {
|
|
1662
|
+
// if value is string, we can use it directly
|
|
1663
|
+
// otherwise if it is an array, we have to join it
|
|
1664
|
+
if (Array.isArray(value)) {
|
|
1665
|
+
tr.insertText(value.map(v => v.text || '').join(''), from, to);
|
|
1666
|
+
}
|
|
1667
|
+
else if (typeof value === 'object' && !!value && !!value.text) {
|
|
1668
|
+
tr.insertText(value.text, from, to);
|
|
1669
|
+
}
|
|
1670
|
+
else {
|
|
1671
|
+
tr.insertText(value, from, to);
|
|
1672
|
+
}
|
|
1673
|
+
}
|
|
1674
|
+
else {
|
|
1675
|
+
tr.replaceWith(from, to, content);
|
|
1676
|
+
}
|
|
1459
1677
|
// set cursor at end of inserted content
|
|
1460
|
-
|
|
1678
|
+
if (options.updateSelection) {
|
|
1679
|
+
selectionToInsertionEnd(tr, tr.steps.length - 1, -1);
|
|
1680
|
+
}
|
|
1461
1681
|
}
|
|
1462
1682
|
return true;
|
|
1463
1683
|
};
|
|
1464
1684
|
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
})
|
|
1469
|
-
|
|
1685
|
+
const joinUp = () => ({ state, dispatch }) => {
|
|
1686
|
+
return joinUp$1(state, dispatch);
|
|
1687
|
+
};
|
|
1688
|
+
const joinDown = () => ({ state, dispatch }) => {
|
|
1689
|
+
return joinDown$1(state, dispatch);
|
|
1690
|
+
};
|
|
1470
1691
|
const joinBackward = () => ({ state, dispatch }) => {
|
|
1471
|
-
return joinBackward$
|
|
1692
|
+
return joinBackward$1(state, dispatch);
|
|
1693
|
+
};
|
|
1694
|
+
const joinForward = () => ({ state, dispatch }) => {
|
|
1695
|
+
return joinForward$1(state, dispatch);
|
|
1472
1696
|
};
|
|
1473
1697
|
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1698
|
+
function isMacOS() {
|
|
1699
|
+
return typeof navigator !== 'undefined'
|
|
1700
|
+
? /Mac/.test(navigator.platform)
|
|
1701
|
+
: false;
|
|
1702
|
+
}
|
|
1478
1703
|
|
|
1479
|
-
const joinForward = () => ({ state, dispatch }) => {
|
|
1480
|
-
return joinForward$2(state, dispatch);
|
|
1481
|
-
};
|
|
1482
|
-
|
|
1483
|
-
var joinForward$1 = /*#__PURE__*/Object.freeze({
|
|
1484
|
-
__proto__: null,
|
|
1485
|
-
joinForward: joinForward
|
|
1486
|
-
});
|
|
1487
|
-
|
|
1488
|
-
const mac = typeof navigator !== 'undefined' ? /Mac/.test(navigator.platform) : false;
|
|
1489
1704
|
function normalizeKeyName(name) {
|
|
1490
1705
|
const parts = name.split(/-(?!$)/);
|
|
1491
1706
|
let result = parts[parts.length - 1];
|
|
@@ -1511,7 +1726,7 @@ function normalizeKeyName(name) {
|
|
|
1511
1726
|
shift = true;
|
|
1512
1727
|
}
|
|
1513
1728
|
else if (/^mod$/i.test(mod)) {
|
|
1514
|
-
if (
|
|
1729
|
+
if (isiOS() || isMacOS()) {
|
|
1515
1730
|
meta = true;
|
|
1516
1731
|
}
|
|
1517
1732
|
else {
|
|
@@ -1562,10 +1777,37 @@ const keyboardShortcut = name => ({ editor, view, tr, dispatch, }) => {
|
|
|
1562
1777
|
return true;
|
|
1563
1778
|
};
|
|
1564
1779
|
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1780
|
+
function isNodeActive(state, typeOrName, attributes = {}) {
|
|
1781
|
+
const { from, to, empty } = state.selection;
|
|
1782
|
+
const type = typeOrName ? getNodeType(typeOrName, state.schema) : null;
|
|
1783
|
+
const nodeRanges = [];
|
|
1784
|
+
state.doc.nodesBetween(from, to, (node, pos) => {
|
|
1785
|
+
if (node.isText) {
|
|
1786
|
+
return;
|
|
1787
|
+
}
|
|
1788
|
+
const relativeFrom = Math.max(from, pos);
|
|
1789
|
+
const relativeTo = Math.min(to, pos + node.nodeSize);
|
|
1790
|
+
nodeRanges.push({
|
|
1791
|
+
node,
|
|
1792
|
+
from: relativeFrom,
|
|
1793
|
+
to: relativeTo,
|
|
1794
|
+
});
|
|
1795
|
+
});
|
|
1796
|
+
const selectionRange = to - from;
|
|
1797
|
+
const matchedNodeRanges = nodeRanges
|
|
1798
|
+
.filter(nodeRange => {
|
|
1799
|
+
if (!type) {
|
|
1800
|
+
return true;
|
|
1801
|
+
}
|
|
1802
|
+
return type.name === nodeRange.node.type.name;
|
|
1803
|
+
})
|
|
1804
|
+
.filter(nodeRange => objectIncludes(nodeRange.node.attrs, attributes, { strict: false }));
|
|
1805
|
+
if (empty) {
|
|
1806
|
+
return !!matchedNodeRanges.length;
|
|
1807
|
+
}
|
|
1808
|
+
const range = matchedNodeRanges.reduce((sum, nodeRange) => sum + nodeRange.to - nodeRange.from, 0);
|
|
1809
|
+
return range >= selectionRange;
|
|
1810
|
+
}
|
|
1569
1811
|
|
|
1570
1812
|
const lift = (typeOrName, attributes = {}) => ({ state, dispatch }) => {
|
|
1571
1813
|
const type = getNodeType(typeOrName, state.schema);
|
|
@@ -1573,165 +1815,634 @@ const lift = (typeOrName, attributes = {}) => ({ state, dispatch }) => {
|
|
|
1573
1815
|
if (!isActive) {
|
|
1574
1816
|
return false;
|
|
1575
1817
|
}
|
|
1576
|
-
return lift$
|
|
1818
|
+
return lift$1(state, dispatch);
|
|
1577
1819
|
};
|
|
1578
1820
|
|
|
1579
|
-
var lift$1 = /*#__PURE__*/Object.freeze({
|
|
1580
|
-
__proto__: null,
|
|
1581
|
-
lift: lift
|
|
1582
|
-
});
|
|
1583
|
-
|
|
1584
1821
|
const liftEmptyBlock = () => ({ state, dispatch }) => {
|
|
1585
|
-
return liftEmptyBlock$
|
|
1822
|
+
return liftEmptyBlock$1(state, dispatch);
|
|
1586
1823
|
};
|
|
1587
1824
|
|
|
1588
|
-
var liftEmptyBlock$1 = /*#__PURE__*/Object.freeze({
|
|
1589
|
-
__proto__: null,
|
|
1590
|
-
liftEmptyBlock: liftEmptyBlock
|
|
1591
|
-
});
|
|
1592
|
-
|
|
1593
1825
|
const liftListItem = typeOrName => ({ state, dispatch }) => {
|
|
1594
1826
|
const type = getNodeType(typeOrName, state.schema);
|
|
1595
|
-
return liftListItem$
|
|
1827
|
+
return liftListItem$1(type)(state, dispatch);
|
|
1596
1828
|
};
|
|
1597
1829
|
|
|
1598
|
-
var liftListItem$1 = /*#__PURE__*/Object.freeze({
|
|
1599
|
-
__proto__: null,
|
|
1600
|
-
liftListItem: liftListItem
|
|
1601
|
-
});
|
|
1602
|
-
|
|
1603
1830
|
const newlineInCode = () => ({ state, dispatch }) => {
|
|
1604
|
-
return newlineInCode$
|
|
1831
|
+
return newlineInCode$1(state, dispatch);
|
|
1605
1832
|
};
|
|
1606
1833
|
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
}
|
|
1834
|
+
function getSchemaTypeNameByName(name, schema) {
|
|
1835
|
+
if (schema.nodes[name]) {
|
|
1836
|
+
return 'node';
|
|
1837
|
+
}
|
|
1838
|
+
if (schema.marks[name]) {
|
|
1839
|
+
return 'mark';
|
|
1840
|
+
}
|
|
1841
|
+
return null;
|
|
1842
|
+
}
|
|
1843
|
+
|
|
1844
|
+
/**
|
|
1845
|
+
* Remove a property or an array of properties from an object
|
|
1846
|
+
* @param obj Object
|
|
1847
|
+
* @param key Key to remove
|
|
1848
|
+
*/
|
|
1849
|
+
function deleteProps(obj, propOrProps) {
|
|
1850
|
+
const props = typeof propOrProps === 'string'
|
|
1851
|
+
? [propOrProps]
|
|
1852
|
+
: propOrProps;
|
|
1853
|
+
return Object
|
|
1854
|
+
.keys(obj)
|
|
1855
|
+
.reduce((newObj, prop) => {
|
|
1856
|
+
if (!props.includes(prop)) {
|
|
1857
|
+
newObj[prop] = obj[prop];
|
|
1858
|
+
}
|
|
1859
|
+
return newObj;
|
|
1860
|
+
}, {});
|
|
1861
|
+
}
|
|
1862
|
+
|
|
1863
|
+
const resetAttributes = (typeOrName, attributes) => ({ tr, state, dispatch }) => {
|
|
1864
|
+
let nodeType = null;
|
|
1865
|
+
let markType = null;
|
|
1866
|
+
const schemaType = getSchemaTypeNameByName(typeof typeOrName === 'string' ? typeOrName : typeOrName.name, state.schema);
|
|
1867
|
+
if (!schemaType) {
|
|
1868
|
+
return false;
|
|
1869
|
+
}
|
|
1870
|
+
if (schemaType === 'node') {
|
|
1871
|
+
nodeType = getNodeType(typeOrName, state.schema);
|
|
1872
|
+
}
|
|
1873
|
+
if (schemaType === 'mark') {
|
|
1874
|
+
markType = getMarkType(typeOrName, state.schema);
|
|
1875
|
+
}
|
|
1876
|
+
if (dispatch) {
|
|
1877
|
+
tr.selection.ranges.forEach(range => {
|
|
1878
|
+
state.doc.nodesBetween(range.$from.pos, range.$to.pos, (node, pos) => {
|
|
1879
|
+
if (nodeType && nodeType === node.type) {
|
|
1880
|
+
tr.setNodeMarkup(pos, undefined, deleteProps(node.attrs, attributes));
|
|
1881
|
+
}
|
|
1882
|
+
if (markType && node.marks.length) {
|
|
1883
|
+
node.marks.forEach(mark => {
|
|
1884
|
+
if (markType === mark.type) {
|
|
1885
|
+
tr.addMark(pos, pos + node.nodeSize, markType.create(deleteProps(mark.attrs, attributes)));
|
|
1886
|
+
}
|
|
1887
|
+
});
|
|
1888
|
+
}
|
|
1889
|
+
});
|
|
1890
|
+
});
|
|
1891
|
+
}
|
|
1892
|
+
return true;
|
|
1893
|
+
};
|
|
1894
|
+
|
|
1895
|
+
const scrollIntoView = () => ({ tr, dispatch }) => {
|
|
1896
|
+
if (dispatch) {
|
|
1897
|
+
tr.scrollIntoView();
|
|
1898
|
+
}
|
|
1899
|
+
return true;
|
|
1900
|
+
};
|
|
1901
|
+
|
|
1902
|
+
const selectAll = () => ({ tr, commands }) => {
|
|
1903
|
+
return commands.setTextSelection({
|
|
1904
|
+
from: 0,
|
|
1905
|
+
to: tr.doc.content.size,
|
|
1906
|
+
});
|
|
1907
|
+
};
|
|
1908
|
+
|
|
1909
|
+
const selectNodeBackward = () => ({ state, dispatch }) => {
|
|
1910
|
+
return selectNodeBackward$1(state, dispatch);
|
|
1911
|
+
};
|
|
1912
|
+
|
|
1913
|
+
const selectNodeForward = () => ({ state, dispatch }) => {
|
|
1914
|
+
return selectNodeForward$1(state, dispatch);
|
|
1915
|
+
};
|
|
1916
|
+
|
|
1917
|
+
const selectParentNode = () => ({ state, dispatch }) => {
|
|
1918
|
+
return selectParentNode$1(state, dispatch);
|
|
1919
|
+
};
|
|
1920
|
+
|
|
1921
|
+
// @ts-ignore
|
|
1922
|
+
const selectTextblockEnd = () => ({ state, dispatch }) => {
|
|
1923
|
+
return selectTextblockEnd$1(state, dispatch);
|
|
1924
|
+
};
|
|
1925
|
+
|
|
1926
|
+
// @ts-ignore
|
|
1927
|
+
const selectTextblockStart = () => ({ state, dispatch }) => {
|
|
1928
|
+
return selectTextblockStart$1(state, dispatch);
|
|
1929
|
+
};
|
|
1930
|
+
|
|
1931
|
+
function createDocument(content, schema, parseOptions = {}) {
|
|
1932
|
+
return createNodeFromContent(content, schema, { slice: false, parseOptions });
|
|
1933
|
+
}
|
|
1934
|
+
|
|
1935
|
+
const setContent = (content, emitUpdate = false, parseOptions = {}) => ({ tr, editor, dispatch }) => {
|
|
1936
|
+
const { doc } = tr;
|
|
1937
|
+
const document = createDocument(content, editor.schema, parseOptions);
|
|
1938
|
+
if (dispatch) {
|
|
1939
|
+
tr.replaceWith(0, doc.content.size, document).setMeta('preventUpdate', !emitUpdate);
|
|
1940
|
+
}
|
|
1941
|
+
return true;
|
|
1942
|
+
};
|
|
1943
|
+
|
|
1944
|
+
/**
|
|
1945
|
+
* Returns a new `Transform` based on all steps of the passed transactions.
|
|
1946
|
+
*/
|
|
1947
|
+
function combineTransactionSteps(oldDoc, transactions) {
|
|
1948
|
+
const transform = new Transform(oldDoc);
|
|
1949
|
+
transactions.forEach(transaction => {
|
|
1950
|
+
transaction.steps.forEach(step => {
|
|
1951
|
+
transform.step(step);
|
|
1952
|
+
});
|
|
1953
|
+
});
|
|
1954
|
+
return transform;
|
|
1955
|
+
}
|
|
1956
|
+
|
|
1957
|
+
function defaultBlockAt(match) {
|
|
1958
|
+
for (let i = 0; i < match.edgeCount; i += 1) {
|
|
1959
|
+
const { type } = match.edge(i);
|
|
1960
|
+
if (type.isTextblock && !type.hasRequiredAttrs()) {
|
|
1961
|
+
return type;
|
|
1962
|
+
}
|
|
1963
|
+
}
|
|
1964
|
+
return null;
|
|
1965
|
+
}
|
|
1966
|
+
|
|
1967
|
+
function findChildren(node, predicate) {
|
|
1968
|
+
const nodesWithPos = [];
|
|
1969
|
+
node.descendants((child, pos) => {
|
|
1970
|
+
if (predicate(child)) {
|
|
1971
|
+
nodesWithPos.push({
|
|
1972
|
+
node: child,
|
|
1973
|
+
pos,
|
|
1974
|
+
});
|
|
1975
|
+
}
|
|
1976
|
+
});
|
|
1977
|
+
return nodesWithPos;
|
|
1978
|
+
}
|
|
1979
|
+
|
|
1980
|
+
/**
|
|
1981
|
+
* Same as `findChildren` but searches only within a `range`.
|
|
1982
|
+
*/
|
|
1983
|
+
function findChildrenInRange(node, range, predicate) {
|
|
1984
|
+
const nodesWithPos = [];
|
|
1985
|
+
// if (range.from === range.to) {
|
|
1986
|
+
// const nodeAt = node.nodeAt(range.from)
|
|
1987
|
+
// if (nodeAt) {
|
|
1988
|
+
// nodesWithPos.push({
|
|
1989
|
+
// node: nodeAt,
|
|
1990
|
+
// pos: range.from,
|
|
1991
|
+
// })
|
|
1992
|
+
// }
|
|
1993
|
+
// }
|
|
1994
|
+
node.nodesBetween(range.from, range.to, (child, pos) => {
|
|
1995
|
+
if (predicate(child)) {
|
|
1996
|
+
nodesWithPos.push({
|
|
1997
|
+
node: child,
|
|
1998
|
+
pos,
|
|
1999
|
+
});
|
|
2000
|
+
}
|
|
2001
|
+
});
|
|
2002
|
+
return nodesWithPos;
|
|
2003
|
+
}
|
|
2004
|
+
|
|
2005
|
+
function findParentNodeClosestToPos($pos, predicate) {
|
|
2006
|
+
for (let i = $pos.depth; i > 0; i -= 1) {
|
|
2007
|
+
const node = $pos.node(i);
|
|
2008
|
+
if (predicate(node)) {
|
|
2009
|
+
return {
|
|
2010
|
+
pos: i > 0 ? $pos.before(i) : 0,
|
|
2011
|
+
start: $pos.start(i),
|
|
2012
|
+
depth: i,
|
|
2013
|
+
node,
|
|
2014
|
+
};
|
|
2015
|
+
}
|
|
2016
|
+
}
|
|
2017
|
+
}
|
|
2018
|
+
|
|
2019
|
+
function findParentNode(predicate) {
|
|
2020
|
+
return (selection) => findParentNodeClosestToPos(selection.$from, predicate);
|
|
2021
|
+
}
|
|
2022
|
+
|
|
2023
|
+
function getHTMLFromFragment(fragment, schema) {
|
|
2024
|
+
const documentFragment = DOMSerializer.fromSchema(schema).serializeFragment(fragment);
|
|
2025
|
+
const temporaryDocument = document.implementation.createHTMLDocument();
|
|
2026
|
+
const container = temporaryDocument.createElement('div');
|
|
2027
|
+
container.appendChild(documentFragment);
|
|
2028
|
+
return container.innerHTML;
|
|
2029
|
+
}
|
|
2030
|
+
|
|
2031
|
+
function getSchema(extensions) {
|
|
2032
|
+
const resolvedExtensions = ExtensionManager.resolve(extensions);
|
|
2033
|
+
return getSchemaByResolvedExtensions(resolvedExtensions);
|
|
2034
|
+
}
|
|
2035
|
+
|
|
2036
|
+
function generateHTML(doc, extensions) {
|
|
2037
|
+
const schema = getSchema(extensions);
|
|
2038
|
+
const contentNode = Node$1.fromJSON(schema, doc);
|
|
2039
|
+
return getHTMLFromFragment(contentNode.content, schema);
|
|
2040
|
+
}
|
|
2041
|
+
|
|
2042
|
+
function generateJSON(html, extensions) {
|
|
2043
|
+
const schema = getSchema(extensions);
|
|
2044
|
+
const dom = elementFromString(html);
|
|
2045
|
+
return DOMParser.fromSchema(schema).parse(dom).toJSON();
|
|
2046
|
+
}
|
|
2047
|
+
|
|
2048
|
+
function getText(node, options) {
|
|
2049
|
+
const range = {
|
|
2050
|
+
from: 0,
|
|
2051
|
+
to: node.content.size,
|
|
2052
|
+
};
|
|
2053
|
+
return getTextBetween(node, range, options);
|
|
2054
|
+
}
|
|
2055
|
+
|
|
2056
|
+
function generateText(doc, extensions, options) {
|
|
2057
|
+
const { blockSeparator = '\n\n', textSerializers = {} } = options || {};
|
|
2058
|
+
const schema = getSchema(extensions);
|
|
2059
|
+
const contentNode = Node$1.fromJSON(schema, doc);
|
|
2060
|
+
return getText(contentNode, {
|
|
2061
|
+
blockSeparator,
|
|
2062
|
+
textSerializers: {
|
|
2063
|
+
...getTextSerializersFromSchema(schema),
|
|
2064
|
+
...textSerializers,
|
|
2065
|
+
},
|
|
2066
|
+
});
|
|
2067
|
+
}
|
|
2068
|
+
|
|
2069
|
+
function getMarkAttributes(state, typeOrName) {
|
|
2070
|
+
const type = getMarkType(typeOrName, state.schema);
|
|
2071
|
+
const { from, to, empty } = state.selection;
|
|
2072
|
+
const marks = [];
|
|
2073
|
+
if (empty) {
|
|
2074
|
+
if (state.storedMarks) {
|
|
2075
|
+
marks.push(...state.storedMarks);
|
|
2076
|
+
}
|
|
2077
|
+
marks.push(...state.selection.$head.marks());
|
|
2078
|
+
}
|
|
2079
|
+
else {
|
|
2080
|
+
state.doc.nodesBetween(from, to, node => {
|
|
2081
|
+
marks.push(...node.marks);
|
|
2082
|
+
});
|
|
2083
|
+
}
|
|
2084
|
+
const mark = marks.find(markItem => markItem.type.name === type.name);
|
|
2085
|
+
if (!mark) {
|
|
2086
|
+
return {};
|
|
2087
|
+
}
|
|
2088
|
+
return { ...mark.attrs };
|
|
2089
|
+
}
|
|
2090
|
+
|
|
2091
|
+
function getNodeAttributes(state, typeOrName) {
|
|
2092
|
+
const type = getNodeType(typeOrName, state.schema);
|
|
2093
|
+
const { from, to } = state.selection;
|
|
2094
|
+
const nodes = [];
|
|
2095
|
+
state.doc.nodesBetween(from, to, node => {
|
|
2096
|
+
nodes.push(node);
|
|
2097
|
+
});
|
|
2098
|
+
const node = nodes.reverse().find(nodeItem => nodeItem.type.name === type.name);
|
|
2099
|
+
if (!node) {
|
|
2100
|
+
return {};
|
|
2101
|
+
}
|
|
2102
|
+
return { ...node.attrs };
|
|
2103
|
+
}
|
|
2104
|
+
|
|
2105
|
+
function getAttributes(state, typeOrName) {
|
|
2106
|
+
const schemaType = getSchemaTypeNameByName(typeof typeOrName === 'string' ? typeOrName : typeOrName.name, state.schema);
|
|
2107
|
+
if (schemaType === 'node') {
|
|
2108
|
+
return getNodeAttributes(state, typeOrName);
|
|
2109
|
+
}
|
|
2110
|
+
if (schemaType === 'mark') {
|
|
2111
|
+
return getMarkAttributes(state, typeOrName);
|
|
2112
|
+
}
|
|
2113
|
+
return {};
|
|
2114
|
+
}
|
|
2115
|
+
|
|
2116
|
+
/**
|
|
2117
|
+
* Removes duplicated values within an array.
|
|
2118
|
+
* Supports numbers, strings and objects.
|
|
2119
|
+
*/
|
|
2120
|
+
function removeDuplicates(array, by = JSON.stringify) {
|
|
2121
|
+
const seen = {};
|
|
2122
|
+
return array.filter(item => {
|
|
2123
|
+
const key = by(item);
|
|
2124
|
+
return Object.prototype.hasOwnProperty.call(seen, key)
|
|
2125
|
+
? false
|
|
2126
|
+
: (seen[key] = true);
|
|
2127
|
+
});
|
|
2128
|
+
}
|
|
1611
2129
|
|
|
1612
2130
|
/**
|
|
1613
|
-
*
|
|
1614
|
-
*
|
|
1615
|
-
* @param key Key to remove
|
|
2131
|
+
* Removes duplicated ranges and ranges that are
|
|
2132
|
+
* fully captured by other ranges.
|
|
1616
2133
|
*/
|
|
1617
|
-
function
|
|
1618
|
-
const
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
2134
|
+
function simplifyChangedRanges(changes) {
|
|
2135
|
+
const uniqueChanges = removeDuplicates(changes);
|
|
2136
|
+
return uniqueChanges.length === 1
|
|
2137
|
+
? uniqueChanges
|
|
2138
|
+
: uniqueChanges.filter((change, index) => {
|
|
2139
|
+
const rest = uniqueChanges.filter((_, i) => i !== index);
|
|
2140
|
+
return !rest.some(otherChange => {
|
|
2141
|
+
return change.oldRange.from >= otherChange.oldRange.from
|
|
2142
|
+
&& change.oldRange.to <= otherChange.oldRange.to
|
|
2143
|
+
&& change.newRange.from >= otherChange.newRange.from
|
|
2144
|
+
&& change.newRange.to <= otherChange.newRange.to;
|
|
2145
|
+
});
|
|
2146
|
+
});
|
|
2147
|
+
}
|
|
2148
|
+
/**
|
|
2149
|
+
* Returns a list of changed ranges
|
|
2150
|
+
* based on the first and last state of all steps.
|
|
2151
|
+
*/
|
|
2152
|
+
function getChangedRanges(transform) {
|
|
2153
|
+
const { mapping, steps } = transform;
|
|
2154
|
+
const changes = [];
|
|
2155
|
+
mapping.maps.forEach((stepMap, index) => {
|
|
2156
|
+
const ranges = [];
|
|
2157
|
+
// This accounts for step changes where no range was actually altered
|
|
2158
|
+
// e.g. when setting a mark, node attribute, etc.
|
|
2159
|
+
// @ts-ignore
|
|
2160
|
+
if (!stepMap.ranges.length) {
|
|
2161
|
+
const { from, to } = steps[index];
|
|
2162
|
+
if (from === undefined || to === undefined) {
|
|
2163
|
+
return;
|
|
2164
|
+
}
|
|
2165
|
+
ranges.push({ from, to });
|
|
1626
2166
|
}
|
|
1627
|
-
|
|
1628
|
-
|
|
2167
|
+
else {
|
|
2168
|
+
stepMap.forEach((from, to) => {
|
|
2169
|
+
ranges.push({ from, to });
|
|
2170
|
+
});
|
|
2171
|
+
}
|
|
2172
|
+
ranges.forEach(({ from, to }) => {
|
|
2173
|
+
const newStart = mapping.slice(index).map(from, -1);
|
|
2174
|
+
const newEnd = mapping.slice(index).map(to);
|
|
2175
|
+
const oldStart = mapping.invert().map(newStart, -1);
|
|
2176
|
+
const oldEnd = mapping.invert().map(newEnd);
|
|
2177
|
+
changes.push({
|
|
2178
|
+
oldRange: {
|
|
2179
|
+
from: oldStart,
|
|
2180
|
+
to: oldEnd,
|
|
2181
|
+
},
|
|
2182
|
+
newRange: {
|
|
2183
|
+
from: newStart,
|
|
2184
|
+
to: newEnd,
|
|
2185
|
+
},
|
|
2186
|
+
});
|
|
2187
|
+
});
|
|
2188
|
+
});
|
|
2189
|
+
return simplifyChangedRanges(changes);
|
|
1629
2190
|
}
|
|
1630
2191
|
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
const
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
2192
|
+
function getDebugJSON(node, startOffset = 0) {
|
|
2193
|
+
const isTopNode = node.type === node.type.schema.topNodeType;
|
|
2194
|
+
const increment = isTopNode ? 0 : 1;
|
|
2195
|
+
const from = startOffset;
|
|
2196
|
+
const to = from + node.nodeSize;
|
|
2197
|
+
const marks = node.marks.map(mark => {
|
|
2198
|
+
const output = {
|
|
2199
|
+
type: mark.type.name,
|
|
2200
|
+
};
|
|
2201
|
+
if (Object.keys(mark.attrs).length) {
|
|
2202
|
+
output.attrs = { ...mark.attrs };
|
|
2203
|
+
}
|
|
2204
|
+
return output;
|
|
2205
|
+
});
|
|
2206
|
+
const attrs = { ...node.attrs };
|
|
2207
|
+
const output = {
|
|
2208
|
+
type: node.type.name,
|
|
2209
|
+
from,
|
|
2210
|
+
to,
|
|
2211
|
+
};
|
|
2212
|
+
if (Object.keys(attrs).length) {
|
|
2213
|
+
output.attrs = attrs;
|
|
1639
2214
|
}
|
|
1640
|
-
if (
|
|
1641
|
-
|
|
2215
|
+
if (marks.length) {
|
|
2216
|
+
output.marks = marks;
|
|
1642
2217
|
}
|
|
1643
|
-
if (
|
|
1644
|
-
|
|
2218
|
+
if (node.content.childCount) {
|
|
2219
|
+
output.content = [];
|
|
2220
|
+
node.forEach((child, offset) => {
|
|
2221
|
+
var _a;
|
|
2222
|
+
(_a = output.content) === null || _a === void 0 ? void 0 : _a.push(getDebugJSON(child, startOffset + offset + increment));
|
|
2223
|
+
});
|
|
1645
2224
|
}
|
|
1646
|
-
if (
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
2225
|
+
if (node.text) {
|
|
2226
|
+
output.text = node.text;
|
|
2227
|
+
}
|
|
2228
|
+
return output;
|
|
2229
|
+
}
|
|
2230
|
+
|
|
2231
|
+
function getMarksBetween(from, to, doc) {
|
|
2232
|
+
const marks = [];
|
|
2233
|
+
// get all inclusive marks on empty selection
|
|
2234
|
+
if (from === to) {
|
|
2235
|
+
doc
|
|
2236
|
+
.resolve(from)
|
|
2237
|
+
.marks()
|
|
2238
|
+
.forEach(mark => {
|
|
2239
|
+
const $pos = doc.resolve(from - 1);
|
|
2240
|
+
const range = getMarkRange($pos, mark.type);
|
|
2241
|
+
if (!range) {
|
|
2242
|
+
return;
|
|
2243
|
+
}
|
|
2244
|
+
marks.push({
|
|
2245
|
+
mark,
|
|
2246
|
+
...range,
|
|
1659
2247
|
});
|
|
1660
2248
|
});
|
|
1661
2249
|
}
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
});
|
|
1669
|
-
|
|
1670
|
-
const scrollIntoView = () => ({ tr, dispatch }) => {
|
|
1671
|
-
if (dispatch) {
|
|
1672
|
-
tr.scrollIntoView();
|
|
2250
|
+
else {
|
|
2251
|
+
doc.nodesBetween(from, to, (node, pos) => {
|
|
2252
|
+
marks.push(...node.marks.map(mark => ({
|
|
2253
|
+
from: pos,
|
|
2254
|
+
to: pos + node.nodeSize,
|
|
2255
|
+
mark,
|
|
2256
|
+
})));
|
|
2257
|
+
});
|
|
1673
2258
|
}
|
|
1674
|
-
return
|
|
1675
|
-
}
|
|
1676
|
-
|
|
1677
|
-
var scrollIntoView$1 = /*#__PURE__*/Object.freeze({
|
|
1678
|
-
__proto__: null,
|
|
1679
|
-
scrollIntoView: scrollIntoView
|
|
1680
|
-
});
|
|
1681
|
-
|
|
1682
|
-
const selectAll = () => ({ state, dispatch }) => {
|
|
1683
|
-
return selectAll$2(state, dispatch);
|
|
1684
|
-
};
|
|
2259
|
+
return marks;
|
|
2260
|
+
}
|
|
1685
2261
|
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
2262
|
+
function getSplittedAttributes(extensionAttributes, typeName, attributes) {
|
|
2263
|
+
return Object.fromEntries(Object
|
|
2264
|
+
.entries(attributes)
|
|
2265
|
+
.filter(([name]) => {
|
|
2266
|
+
const extensionAttribute = extensionAttributes.find(item => {
|
|
2267
|
+
return item.type === typeName && item.name === name;
|
|
2268
|
+
});
|
|
2269
|
+
if (!extensionAttribute) {
|
|
2270
|
+
return false;
|
|
2271
|
+
}
|
|
2272
|
+
return extensionAttribute.attribute.keepOnSplit;
|
|
2273
|
+
}));
|
|
2274
|
+
}
|
|
1690
2275
|
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
2276
|
+
function isMarkActive(state, typeOrName, attributes = {}) {
|
|
2277
|
+
const { empty, ranges } = state.selection;
|
|
2278
|
+
const type = typeOrName ? getMarkType(typeOrName, state.schema) : null;
|
|
2279
|
+
if (empty) {
|
|
2280
|
+
return !!(state.storedMarks || state.selection.$from.marks())
|
|
2281
|
+
.filter(mark => {
|
|
2282
|
+
if (!type) {
|
|
2283
|
+
return true;
|
|
2284
|
+
}
|
|
2285
|
+
return type.name === mark.type.name;
|
|
2286
|
+
})
|
|
2287
|
+
.find(mark => objectIncludes(mark.attrs, attributes, { strict: false }));
|
|
2288
|
+
}
|
|
2289
|
+
let selectionRange = 0;
|
|
2290
|
+
const markRanges = [];
|
|
2291
|
+
ranges.forEach(({ $from, $to }) => {
|
|
2292
|
+
const from = $from.pos;
|
|
2293
|
+
const to = $to.pos;
|
|
2294
|
+
state.doc.nodesBetween(from, to, (node, pos) => {
|
|
2295
|
+
if (!node.isText && !node.marks.length) {
|
|
2296
|
+
return;
|
|
2297
|
+
}
|
|
2298
|
+
const relativeFrom = Math.max(from, pos);
|
|
2299
|
+
const relativeTo = Math.min(to, pos + node.nodeSize);
|
|
2300
|
+
const range = relativeTo - relativeFrom;
|
|
2301
|
+
selectionRange += range;
|
|
2302
|
+
markRanges.push(...node.marks.map(mark => ({
|
|
2303
|
+
mark,
|
|
2304
|
+
from: relativeFrom,
|
|
2305
|
+
to: relativeTo,
|
|
2306
|
+
})));
|
|
2307
|
+
});
|
|
2308
|
+
});
|
|
2309
|
+
if (selectionRange === 0) {
|
|
2310
|
+
return false;
|
|
2311
|
+
}
|
|
2312
|
+
// calculate range of matched mark
|
|
2313
|
+
const matchedRange = markRanges
|
|
2314
|
+
.filter(markRange => {
|
|
2315
|
+
if (!type) {
|
|
2316
|
+
return true;
|
|
2317
|
+
}
|
|
2318
|
+
return type.name === markRange.mark.type.name;
|
|
2319
|
+
})
|
|
2320
|
+
.filter(markRange => objectIncludes(markRange.mark.attrs, attributes, { strict: false }))
|
|
2321
|
+
.reduce((sum, markRange) => sum + markRange.to - markRange.from, 0);
|
|
2322
|
+
// calculate range of marks that excludes the searched mark
|
|
2323
|
+
// for example `code` doesn’t allow any other marks
|
|
2324
|
+
const excludedRange = markRanges
|
|
2325
|
+
.filter(markRange => {
|
|
2326
|
+
if (!type) {
|
|
2327
|
+
return true;
|
|
2328
|
+
}
|
|
2329
|
+
return markRange.mark.type !== type && markRange.mark.type.excludes(type);
|
|
2330
|
+
})
|
|
2331
|
+
.reduce((sum, markRange) => sum + markRange.to - markRange.from, 0);
|
|
2332
|
+
// we only include the result of `excludedRange`
|
|
2333
|
+
// if there is a match at all
|
|
2334
|
+
const range = matchedRange > 0 ? matchedRange + excludedRange : matchedRange;
|
|
2335
|
+
return range >= selectionRange;
|
|
2336
|
+
}
|
|
1694
2337
|
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
}
|
|
2338
|
+
function isActive(state, name, attributes = {}) {
|
|
2339
|
+
if (!name) {
|
|
2340
|
+
return isNodeActive(state, null, attributes) || isMarkActive(state, null, attributes);
|
|
2341
|
+
}
|
|
2342
|
+
const schemaType = getSchemaTypeNameByName(name, state.schema);
|
|
2343
|
+
if (schemaType === 'node') {
|
|
2344
|
+
return isNodeActive(state, name, attributes);
|
|
2345
|
+
}
|
|
2346
|
+
if (schemaType === 'mark') {
|
|
2347
|
+
return isMarkActive(state, name, attributes);
|
|
2348
|
+
}
|
|
2349
|
+
return false;
|
|
2350
|
+
}
|
|
1699
2351
|
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
2352
|
+
function isList(name, extensions) {
|
|
2353
|
+
const { nodeExtensions } = splitExtensions(extensions);
|
|
2354
|
+
const extension = nodeExtensions.find(item => item.name === name);
|
|
2355
|
+
if (!extension) {
|
|
2356
|
+
return false;
|
|
2357
|
+
}
|
|
2358
|
+
const context = {
|
|
2359
|
+
name: extension.name,
|
|
2360
|
+
options: extension.options,
|
|
2361
|
+
storage: extension.storage,
|
|
2362
|
+
};
|
|
2363
|
+
const group = callOrReturn(getExtensionField(extension, 'group', context));
|
|
2364
|
+
if (typeof group !== 'string') {
|
|
2365
|
+
return false;
|
|
2366
|
+
}
|
|
2367
|
+
return group.split(' ').includes('list');
|
|
2368
|
+
}
|
|
1703
2369
|
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
2370
|
+
function isNodeEmpty(node) {
|
|
2371
|
+
var _a;
|
|
2372
|
+
const defaultContent = (_a = node.type.createAndFill()) === null || _a === void 0 ? void 0 : _a.toJSON();
|
|
2373
|
+
const content = node.toJSON();
|
|
2374
|
+
return JSON.stringify(defaultContent) === JSON.stringify(content);
|
|
2375
|
+
}
|
|
1708
2376
|
|
|
1709
|
-
|
|
1710
|
-
return
|
|
1711
|
-
}
|
|
2377
|
+
function isNodeSelection(value) {
|
|
2378
|
+
return value instanceof NodeSelection;
|
|
2379
|
+
}
|
|
1712
2380
|
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
2381
|
+
function posToDOMRect(view, from, to) {
|
|
2382
|
+
const minPos = 0;
|
|
2383
|
+
const maxPos = view.state.doc.content.size;
|
|
2384
|
+
const resolvedFrom = minMax(from, minPos, maxPos);
|
|
2385
|
+
const resolvedEnd = minMax(to, minPos, maxPos);
|
|
2386
|
+
const start = view.coordsAtPos(resolvedFrom);
|
|
2387
|
+
const end = view.coordsAtPos(resolvedEnd, -1);
|
|
2388
|
+
const top = Math.min(start.top, end.top);
|
|
2389
|
+
const bottom = Math.max(start.bottom, end.bottom);
|
|
2390
|
+
const left = Math.min(start.left, end.left);
|
|
2391
|
+
const right = Math.max(start.right, end.right);
|
|
2392
|
+
const width = right - left;
|
|
2393
|
+
const height = bottom - top;
|
|
2394
|
+
const x = left;
|
|
2395
|
+
const y = top;
|
|
2396
|
+
const data = {
|
|
2397
|
+
top,
|
|
2398
|
+
bottom,
|
|
2399
|
+
left,
|
|
2400
|
+
right,
|
|
2401
|
+
width,
|
|
2402
|
+
height,
|
|
2403
|
+
x,
|
|
2404
|
+
y,
|
|
2405
|
+
};
|
|
2406
|
+
return {
|
|
2407
|
+
...data,
|
|
2408
|
+
toJSON: () => data,
|
|
2409
|
+
};
|
|
2410
|
+
}
|
|
1717
2411
|
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
const
|
|
1721
|
-
|
|
1722
|
-
if (
|
|
1723
|
-
|
|
1724
|
-
.replaceSelectionWith(document, false)
|
|
1725
|
-
.setMeta('preventUpdate', !emitUpdate);
|
|
2412
|
+
function canSetMark(state, tr, newMarkType) {
|
|
2413
|
+
var _a;
|
|
2414
|
+
const { selection } = tr;
|
|
2415
|
+
let cursor = null;
|
|
2416
|
+
if (isTextSelection(selection)) {
|
|
2417
|
+
cursor = selection.$cursor;
|
|
1726
2418
|
}
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
}
|
|
1734
|
-
|
|
2419
|
+
if (cursor) {
|
|
2420
|
+
const currentMarks = (_a = state.storedMarks) !== null && _a !== void 0 ? _a : cursor.marks();
|
|
2421
|
+
// There can be no current marks that exclude the new mark
|
|
2422
|
+
return (!!newMarkType.isInSet(currentMarks)
|
|
2423
|
+
|| !currentMarks.some(mark => mark.type.excludes(newMarkType)));
|
|
2424
|
+
}
|
|
2425
|
+
const { ranges } = selection;
|
|
2426
|
+
return ranges.some(({ $from, $to }) => {
|
|
2427
|
+
let someNodeSupportsMark = $from.depth === 0
|
|
2428
|
+
? state.doc.inlineContent && state.doc.type.allowsMarkType(newMarkType)
|
|
2429
|
+
: false;
|
|
2430
|
+
state.doc.nodesBetween($from.pos, $to.pos, (node, _pos, parent) => {
|
|
2431
|
+
// If we already found a mark that we can enable, return false to bypass the remaining search
|
|
2432
|
+
if (someNodeSupportsMark) {
|
|
2433
|
+
return false;
|
|
2434
|
+
}
|
|
2435
|
+
if (node.isInline) {
|
|
2436
|
+
const parentAllowsMarkType = !parent || parent.type.allowsMarkType(newMarkType);
|
|
2437
|
+
const currentMarksAllowMarkType = !!newMarkType.isInSet(node.marks)
|
|
2438
|
+
|| !node.marks.some(otherMark => otherMark.type.excludes(newMarkType));
|
|
2439
|
+
someNodeSupportsMark = parentAllowsMarkType && currentMarksAllowMarkType;
|
|
2440
|
+
}
|
|
2441
|
+
return !someNodeSupportsMark;
|
|
2442
|
+
});
|
|
2443
|
+
return someNodeSupportsMark;
|
|
2444
|
+
});
|
|
2445
|
+
}
|
|
1735
2446
|
const setMark = (typeOrName, attributes = {}) => ({ tr, state, dispatch }) => {
|
|
1736
2447
|
const { selection } = tr;
|
|
1737
2448
|
const { empty, ranges } = selection;
|
|
@@ -1772,59 +2483,52 @@ const setMark = (typeOrName, attributes = {}) => ({ tr, state, dispatch }) => {
|
|
|
1772
2483
|
});
|
|
1773
2484
|
}
|
|
1774
2485
|
}
|
|
1775
|
-
return
|
|
2486
|
+
return canSetMark(state, tr, type);
|
|
1776
2487
|
};
|
|
1777
2488
|
|
|
1778
|
-
var setMark$1 = /*#__PURE__*/Object.freeze({
|
|
1779
|
-
__proto__: null,
|
|
1780
|
-
setMark: setMark
|
|
1781
|
-
});
|
|
1782
|
-
|
|
1783
2489
|
const setMeta = (key, value) => ({ tr }) => {
|
|
1784
2490
|
tr.setMeta(key, value);
|
|
1785
2491
|
return true;
|
|
1786
2492
|
};
|
|
1787
2493
|
|
|
1788
|
-
|
|
1789
|
-
__proto__: null,
|
|
1790
|
-
setMeta: setMeta
|
|
1791
|
-
});
|
|
1792
|
-
|
|
1793
|
-
const setNode = (typeOrName, attributes = {}) => ({ state, dispatch }) => {
|
|
2494
|
+
const setNode = (typeOrName, attributes = {}) => ({ state, dispatch, chain }) => {
|
|
1794
2495
|
const type = getNodeType(typeOrName, state.schema);
|
|
1795
|
-
|
|
2496
|
+
// TODO: use a fallback like insertContent?
|
|
2497
|
+
if (!type.isTextblock) {
|
|
2498
|
+
console.warn('[tiptap warn]: Currently "setNode()" only supports text block nodes.');
|
|
2499
|
+
return false;
|
|
2500
|
+
}
|
|
2501
|
+
return (chain()
|
|
2502
|
+
// try to convert node to default node if needed
|
|
2503
|
+
.command(({ commands }) => {
|
|
2504
|
+
const canSetBlock = setBlockType(type, attributes)(state);
|
|
2505
|
+
if (canSetBlock) {
|
|
2506
|
+
return true;
|
|
2507
|
+
}
|
|
2508
|
+
return commands.clearNodes();
|
|
2509
|
+
})
|
|
2510
|
+
.command(({ state: updatedState }) => {
|
|
2511
|
+
return setBlockType(type, attributes)(updatedState, dispatch);
|
|
2512
|
+
})
|
|
2513
|
+
.run());
|
|
1796
2514
|
};
|
|
1797
2515
|
|
|
1798
|
-
var setNode$1 = /*#__PURE__*/Object.freeze({
|
|
1799
|
-
__proto__: null,
|
|
1800
|
-
setNode: setNode
|
|
1801
|
-
});
|
|
1802
|
-
|
|
1803
2516
|
const setNodeSelection = position => ({ tr, dispatch }) => {
|
|
1804
2517
|
if (dispatch) {
|
|
1805
2518
|
const { doc } = tr;
|
|
1806
|
-
const
|
|
1807
|
-
const
|
|
1808
|
-
const resolvedPos = minMax(position, minPos, maxPos);
|
|
1809
|
-
const selection = NodeSelection.create(doc, resolvedPos);
|
|
2519
|
+
const from = minMax(position, 0, doc.content.size);
|
|
2520
|
+
const selection = NodeSelection.create(doc, from);
|
|
1810
2521
|
tr.setSelection(selection);
|
|
1811
2522
|
}
|
|
1812
2523
|
return true;
|
|
1813
2524
|
};
|
|
1814
2525
|
|
|
1815
|
-
var setNodeSelection$1 = /*#__PURE__*/Object.freeze({
|
|
1816
|
-
__proto__: null,
|
|
1817
|
-
setNodeSelection: setNodeSelection
|
|
1818
|
-
});
|
|
1819
|
-
|
|
1820
2526
|
const setTextSelection = position => ({ tr, dispatch }) => {
|
|
1821
2527
|
if (dispatch) {
|
|
1822
2528
|
const { doc } = tr;
|
|
1823
|
-
const { from, to } = typeof position === 'number'
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
const minPos = Selection.atStart(doc).from;
|
|
1827
|
-
const maxPos = Selection.atEnd(doc).to;
|
|
2529
|
+
const { from, to } = typeof position === 'number' ? { from: position, to: position } : position;
|
|
2530
|
+
const minPos = TextSelection.atStart(doc).from;
|
|
2531
|
+
const maxPos = TextSelection.atEnd(doc).to;
|
|
1828
2532
|
const resolvedFrom = minMax(from, minPos, maxPos);
|
|
1829
2533
|
const resolvedEnd = minMax(to, minPos, maxPos);
|
|
1830
2534
|
const selection = TextSelection.create(doc, resolvedFrom, resolvedEnd);
|
|
@@ -1833,47 +2537,13 @@ const setTextSelection = position => ({ tr, dispatch }) => {
|
|
|
1833
2537
|
return true;
|
|
1834
2538
|
};
|
|
1835
2539
|
|
|
1836
|
-
var setTextSelection$1 = /*#__PURE__*/Object.freeze({
|
|
1837
|
-
__proto__: null,
|
|
1838
|
-
setTextSelection: setTextSelection
|
|
1839
|
-
});
|
|
1840
|
-
|
|
1841
2540
|
const sinkListItem = typeOrName => ({ state, dispatch }) => {
|
|
1842
2541
|
const type = getNodeType(typeOrName, state.schema);
|
|
1843
|
-
return sinkListItem$
|
|
2542
|
+
return sinkListItem$1(type)(state, dispatch);
|
|
1844
2543
|
};
|
|
1845
2544
|
|
|
1846
|
-
var sinkListItem$1 = /*#__PURE__*/Object.freeze({
|
|
1847
|
-
__proto__: null,
|
|
1848
|
-
sinkListItem: sinkListItem
|
|
1849
|
-
});
|
|
1850
|
-
|
|
1851
|
-
function getSplittedAttributes(extensionAttributes, typeName, attributes) {
|
|
1852
|
-
return Object.fromEntries(Object
|
|
1853
|
-
.entries(attributes)
|
|
1854
|
-
.filter(([name]) => {
|
|
1855
|
-
const extensionAttribute = extensionAttributes.find(item => {
|
|
1856
|
-
return item.type === typeName && item.name === name;
|
|
1857
|
-
});
|
|
1858
|
-
if (!extensionAttribute) {
|
|
1859
|
-
return false;
|
|
1860
|
-
}
|
|
1861
|
-
return extensionAttribute.attribute.keepOnSplit;
|
|
1862
|
-
}));
|
|
1863
|
-
}
|
|
1864
|
-
|
|
1865
|
-
function defaultBlockAt(match) {
|
|
1866
|
-
for (let i = 0; i < match.edgeCount; i += 1) {
|
|
1867
|
-
const { type } = match.edge(i);
|
|
1868
|
-
if (type.isTextblock && !type.hasRequiredAttrs()) {
|
|
1869
|
-
return type;
|
|
1870
|
-
}
|
|
1871
|
-
}
|
|
1872
|
-
return null;
|
|
1873
|
-
}
|
|
1874
2545
|
function ensureMarks(state, splittableMarks) {
|
|
1875
|
-
const marks = state.storedMarks
|
|
1876
|
-
|| (state.selection.$to.parentOffset && state.selection.$from.marks());
|
|
2546
|
+
const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks());
|
|
1877
2547
|
if (marks) {
|
|
1878
2548
|
const filteredMarks = marks.filter(mark => splittableMarks === null || splittableMarks === void 0 ? void 0 : splittableMarks.includes(mark.type.name));
|
|
1879
2549
|
state.tr.ensureMarks(filteredMarks);
|
|
@@ -1908,10 +2578,12 @@ const splitBlock = ({ keepMarks = true } = {}) => ({ tr, state, dispatch, editor
|
|
|
1908
2578
|
? undefined
|
|
1909
2579
|
: defaultBlockAt($from.node(-1).contentMatchAt($from.indexAfter(-1)));
|
|
1910
2580
|
let types = atEnd && deflt
|
|
1911
|
-
? [
|
|
2581
|
+
? [
|
|
2582
|
+
{
|
|
1912
2583
|
type: deflt,
|
|
1913
2584
|
attrs: newAttributes,
|
|
1914
|
-
}
|
|
2585
|
+
},
|
|
2586
|
+
]
|
|
1915
2587
|
: undefined;
|
|
1916
2588
|
let can = canSplit(tr.doc, tr.mapping.map($from.pos), 1, types);
|
|
1917
2589
|
if (!types
|
|
@@ -1919,18 +2591,17 @@ const splitBlock = ({ keepMarks = true } = {}) => ({ tr, state, dispatch, editor
|
|
|
1919
2591
|
&& canSplit(tr.doc, tr.mapping.map($from.pos), 1, deflt ? [{ type: deflt }] : undefined)) {
|
|
1920
2592
|
can = true;
|
|
1921
2593
|
types = deflt
|
|
1922
|
-
? [
|
|
2594
|
+
? [
|
|
2595
|
+
{
|
|
1923
2596
|
type: deflt,
|
|
1924
2597
|
attrs: newAttributes,
|
|
1925
|
-
}
|
|
2598
|
+
},
|
|
2599
|
+
]
|
|
1926
2600
|
: undefined;
|
|
1927
2601
|
}
|
|
1928
2602
|
if (can) {
|
|
1929
2603
|
tr.split(tr.mapping.map($from.pos), 1, types);
|
|
1930
|
-
if (deflt
|
|
1931
|
-
&& !atEnd
|
|
1932
|
-
&& !$from.parentOffset
|
|
1933
|
-
&& $from.parent.type !== deflt) {
|
|
2604
|
+
if (deflt && !atEnd && !$from.parentOffset && $from.parent.type !== deflt) {
|
|
1934
2605
|
const first = tr.mapping.map($from.before());
|
|
1935
2606
|
const $first = tr.doc.resolve(first);
|
|
1936
2607
|
if ($from.node(-1).canReplaceWith($first.index(), $first.index() + 1, deflt)) {
|
|
@@ -1946,11 +2617,6 @@ const splitBlock = ({ keepMarks = true } = {}) => ({ tr, state, dispatch, editor
|
|
|
1946
2617
|
return true;
|
|
1947
2618
|
};
|
|
1948
2619
|
|
|
1949
|
-
var splitBlock$1 = /*#__PURE__*/Object.freeze({
|
|
1950
|
-
__proto__: null,
|
|
1951
|
-
splitBlock: splitBlock
|
|
1952
|
-
});
|
|
1953
|
-
|
|
1954
2620
|
const splitListItem = typeOrName => ({ tr, state, dispatch, editor, }) => {
|
|
1955
2621
|
var _a;
|
|
1956
2622
|
const type = getNodeType(typeOrName, state.schema);
|
|
@@ -1978,22 +2644,14 @@ const splitListItem = typeOrName => ({ tr, state, dispatch, editor, }) => {
|
|
|
1978
2644
|
if (dispatch) {
|
|
1979
2645
|
let wrap = Fragment.empty;
|
|
1980
2646
|
// eslint-disable-next-line
|
|
1981
|
-
const depthBefore = $from.index(-1)
|
|
1982
|
-
? 1
|
|
1983
|
-
: $from.index(-2)
|
|
1984
|
-
? 2
|
|
1985
|
-
: 3;
|
|
2647
|
+
const depthBefore = $from.index(-1) ? 1 : $from.index(-2) ? 2 : 3;
|
|
1986
2648
|
// Build a fragment containing empty versions of the structure
|
|
1987
2649
|
// from the outer list item to the parent node of the cursor
|
|
1988
2650
|
for (let d = $from.depth - depthBefore; d >= $from.depth - 3; d -= 1) {
|
|
1989
2651
|
wrap = Fragment.from($from.node(d).copy(wrap));
|
|
1990
2652
|
}
|
|
1991
2653
|
// eslint-disable-next-line
|
|
1992
|
-
const depthAfter = $from.indexAfter(-1) < $from.node(-2).childCount
|
|
1993
|
-
? 1
|
|
1994
|
-
: $from.indexAfter(-2) < $from.node(-3).childCount
|
|
1995
|
-
? 2
|
|
1996
|
-
: 3;
|
|
2654
|
+
const depthAfter = $from.indexAfter(-1) < $from.node(-2).childCount ? 1 : $from.indexAfter(-2) < $from.node(-3).childCount ? 2 : 3;
|
|
1997
2655
|
// Add a second list item with an empty default start node
|
|
1998
2656
|
const newNextTypeAttributes = getSplittedAttributes(extensionAttributes, $from.node().type.name, $from.node().attrs);
|
|
1999
2657
|
const nextType = ((_a = type.contentMatch.defaultType) === null || _a === void 0 ? void 0 : _a.createAndFill(newNextTypeAttributes)) || undefined;
|
|
@@ -2016,71 +2674,75 @@ const splitListItem = typeOrName => ({ tr, state, dispatch, editor, }) => {
|
|
|
2016
2674
|
}
|
|
2017
2675
|
return true;
|
|
2018
2676
|
}
|
|
2019
|
-
const nextType = $to.pos === $from.end()
|
|
2020
|
-
? grandParent.contentMatchAt(0).defaultType
|
|
2021
|
-
: null;
|
|
2677
|
+
const nextType = $to.pos === $from.end() ? grandParent.contentMatchAt(0).defaultType : null;
|
|
2022
2678
|
const newTypeAttributes = getSplittedAttributes(extensionAttributes, grandParent.type.name, grandParent.attrs);
|
|
2023
2679
|
const newNextTypeAttributes = getSplittedAttributes(extensionAttributes, $from.node().type.name, $from.node().attrs);
|
|
2024
2680
|
tr.delete($from.pos, $to.pos);
|
|
2025
2681
|
const types = nextType
|
|
2026
|
-
? [
|
|
2682
|
+
? [
|
|
2683
|
+
{ type, attrs: newTypeAttributes },
|
|
2684
|
+
{ type: nextType, attrs: newNextTypeAttributes },
|
|
2685
|
+
]
|
|
2027
2686
|
: [{ type, attrs: newTypeAttributes }];
|
|
2028
2687
|
if (!canSplit(tr.doc, $from.pos, 2)) {
|
|
2029
2688
|
return false;
|
|
2030
2689
|
}
|
|
2031
2690
|
if (dispatch) {
|
|
2691
|
+
const { selection, storedMarks } = state;
|
|
2692
|
+
const { splittableMarks } = editor.extensionManager;
|
|
2693
|
+
const marks = storedMarks || (selection.$to.parentOffset && selection.$from.marks());
|
|
2032
2694
|
tr.split($from.pos, 2, types).scrollIntoView();
|
|
2695
|
+
if (!marks || !dispatch) {
|
|
2696
|
+
return true;
|
|
2697
|
+
}
|
|
2698
|
+
const filteredMarks = marks.filter(mark => splittableMarks.includes(mark.type.name));
|
|
2699
|
+
tr.ensureMarks(filteredMarks);
|
|
2033
2700
|
}
|
|
2034
2701
|
return true;
|
|
2035
2702
|
};
|
|
2036
2703
|
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
function findParentNodeClosestToPos($pos, predicate) {
|
|
2043
|
-
for (let i = $pos.depth; i > 0; i -= 1) {
|
|
2044
|
-
const node = $pos.node(i);
|
|
2045
|
-
if (predicate(node)) {
|
|
2046
|
-
return {
|
|
2047
|
-
pos: i > 0 ? $pos.before(i) : 0,
|
|
2048
|
-
start: $pos.start(i),
|
|
2049
|
-
depth: i,
|
|
2050
|
-
node,
|
|
2051
|
-
};
|
|
2052
|
-
}
|
|
2704
|
+
const joinListBackwards = (tr, listType) => {
|
|
2705
|
+
const list = findParentNode(node => node.type === listType)(tr.selection);
|
|
2706
|
+
if (!list) {
|
|
2707
|
+
return true;
|
|
2053
2708
|
}
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
return (selection) => findParentNodeClosestToPos(selection.$from, predicate);
|
|
2058
|
-
}
|
|
2059
|
-
|
|
2060
|
-
function isList(name, extensions) {
|
|
2061
|
-
const { nodeExtensions } = splitExtensions(extensions);
|
|
2062
|
-
const extension = nodeExtensions.find(item => item.name === name);
|
|
2063
|
-
if (!extension) {
|
|
2064
|
-
return false;
|
|
2709
|
+
const before = tr.doc.resolve(Math.max(0, list.pos - 1)).before(list.depth);
|
|
2710
|
+
if (before === undefined) {
|
|
2711
|
+
return true;
|
|
2065
2712
|
}
|
|
2066
|
-
const
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
const group = callOrReturn(getExtensionField(extension, 'group', context));
|
|
2071
|
-
if (typeof group !== 'string') {
|
|
2072
|
-
return false;
|
|
2713
|
+
const nodeBefore = tr.doc.nodeAt(before);
|
|
2714
|
+
const canJoinBackwards = list.node.type === (nodeBefore === null || nodeBefore === void 0 ? void 0 : nodeBefore.type) && canJoin(tr.doc, list.pos);
|
|
2715
|
+
if (!canJoinBackwards) {
|
|
2716
|
+
return true;
|
|
2073
2717
|
}
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
const
|
|
2078
|
-
const
|
|
2718
|
+
tr.join(list.pos);
|
|
2719
|
+
return true;
|
|
2720
|
+
};
|
|
2721
|
+
const joinListForwards = (tr, listType) => {
|
|
2722
|
+
const list = findParentNode(node => node.type === listType)(tr.selection);
|
|
2723
|
+
if (!list) {
|
|
2724
|
+
return true;
|
|
2725
|
+
}
|
|
2726
|
+
const after = tr.doc.resolve(list.start).after(list.depth);
|
|
2727
|
+
if (after === undefined) {
|
|
2728
|
+
return true;
|
|
2729
|
+
}
|
|
2730
|
+
const nodeAfter = tr.doc.nodeAt(after);
|
|
2731
|
+
const canJoinForwards = list.node.type === (nodeAfter === null || nodeAfter === void 0 ? void 0 : nodeAfter.type) && canJoin(tr.doc, after);
|
|
2732
|
+
if (!canJoinForwards) {
|
|
2733
|
+
return true;
|
|
2734
|
+
}
|
|
2735
|
+
tr.join(after);
|
|
2736
|
+
return true;
|
|
2737
|
+
};
|
|
2738
|
+
const toggleList = (listTypeOrName, itemTypeOrName, keepMarks, attributes = {}) => ({ editor, tr, state, dispatch, chain, commands, can, }) => {
|
|
2739
|
+
const { extensions, splittableMarks } = editor.extensionManager;
|
|
2079
2740
|
const listType = getNodeType(listTypeOrName, state.schema);
|
|
2080
2741
|
const itemType = getNodeType(itemTypeOrName, state.schema);
|
|
2081
|
-
const { selection } = state;
|
|
2742
|
+
const { selection, storedMarks } = state;
|
|
2082
2743
|
const { $from, $to } = selection;
|
|
2083
2744
|
const range = $from.blockRange($to);
|
|
2745
|
+
const marks = storedMarks || (selection.$to.parentOffset && selection.$from.marks());
|
|
2084
2746
|
if (!range) {
|
|
2085
2747
|
return false;
|
|
2086
2748
|
}
|
|
@@ -2094,40 +2756,58 @@ const toggleList = (listTypeOrName, itemTypeOrName) => ({ editor, tr, state, dis
|
|
|
2094
2756
|
if (isList(parentList.node.type.name, extensions)
|
|
2095
2757
|
&& listType.validContent(parentList.node.content)
|
|
2096
2758
|
&& dispatch) {
|
|
2097
|
-
|
|
2098
|
-
|
|
2759
|
+
return chain()
|
|
2760
|
+
.command(() => {
|
|
2761
|
+
tr.setNodeMarkup(parentList.pos, listType);
|
|
2762
|
+
return true;
|
|
2763
|
+
})
|
|
2764
|
+
.command(() => joinListBackwards(tr, listType))
|
|
2765
|
+
.command(() => joinListForwards(tr, listType))
|
|
2766
|
+
.run();
|
|
2099
2767
|
}
|
|
2100
2768
|
}
|
|
2101
|
-
|
|
2102
|
-
// try to convert node to paragraph if needed
|
|
2103
|
-
if (!canWrapInList) {
|
|
2769
|
+
if (!keepMarks || !marks || !dispatch) {
|
|
2104
2770
|
return chain()
|
|
2105
|
-
|
|
2106
|
-
.
|
|
2771
|
+
// try to convert node to default node if needed
|
|
2772
|
+
.command(() => {
|
|
2773
|
+
const canWrapInList = can().wrapInList(listType, attributes);
|
|
2774
|
+
if (canWrapInList) {
|
|
2775
|
+
return true;
|
|
2776
|
+
}
|
|
2777
|
+
return commands.clearNodes();
|
|
2778
|
+
})
|
|
2779
|
+
.wrapInList(listType, attributes)
|
|
2780
|
+
.command(() => joinListBackwards(tr, listType))
|
|
2781
|
+
.command(() => joinListForwards(tr, listType))
|
|
2107
2782
|
.run();
|
|
2108
2783
|
}
|
|
2109
|
-
return
|
|
2784
|
+
return (chain()
|
|
2785
|
+
// try to convert node to default node if needed
|
|
2786
|
+
.command(() => {
|
|
2787
|
+
const canWrapInList = can().wrapInList(listType, attributes);
|
|
2788
|
+
const filteredMarks = marks.filter(mark => splittableMarks.includes(mark.type.name));
|
|
2789
|
+
tr.ensureMarks(filteredMarks);
|
|
2790
|
+
if (canWrapInList) {
|
|
2791
|
+
return true;
|
|
2792
|
+
}
|
|
2793
|
+
return commands.clearNodes();
|
|
2794
|
+
})
|
|
2795
|
+
.wrapInList(listType, attributes)
|
|
2796
|
+
.command(() => joinListBackwards(tr, listType))
|
|
2797
|
+
.command(() => joinListForwards(tr, listType))
|
|
2798
|
+
.run());
|
|
2110
2799
|
};
|
|
2111
2800
|
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
toggleList: toggleList
|
|
2115
|
-
});
|
|
2116
|
-
|
|
2117
|
-
const toggleMark = (typeOrName, attributes = {}) => ({ state, commands }) => {
|
|
2801
|
+
const toggleMark = (typeOrName, attributes = {}, options = {}) => ({ state, commands }) => {
|
|
2802
|
+
const { extendEmptyMarkRange = false } = options;
|
|
2118
2803
|
const type = getMarkType(typeOrName, state.schema);
|
|
2119
2804
|
const isActive = isMarkActive(state, type, attributes);
|
|
2120
2805
|
if (isActive) {
|
|
2121
|
-
return commands.unsetMark(type);
|
|
2806
|
+
return commands.unsetMark(type, { extendEmptyMarkRange });
|
|
2122
2807
|
}
|
|
2123
2808
|
return commands.setMark(type, attributes);
|
|
2124
2809
|
};
|
|
2125
2810
|
|
|
2126
|
-
var toggleMark$1 = /*#__PURE__*/Object.freeze({
|
|
2127
|
-
__proto__: null,
|
|
2128
|
-
toggleMark: toggleMark
|
|
2129
|
-
});
|
|
2130
|
-
|
|
2131
2811
|
const toggleNode = (typeOrName, toggleTypeOrName, attributes = {}) => ({ state, commands }) => {
|
|
2132
2812
|
const type = getNodeType(typeOrName, state.schema);
|
|
2133
2813
|
const toggleType = getNodeType(toggleTypeOrName, state.schema);
|
|
@@ -2138,92 +2818,89 @@ const toggleNode = (typeOrName, toggleTypeOrName, attributes = {}) => ({ state,
|
|
|
2138
2818
|
return commands.setNode(type, attributes);
|
|
2139
2819
|
};
|
|
2140
2820
|
|
|
2141
|
-
|
|
2142
|
-
__proto__: null,
|
|
2143
|
-
toggleNode: toggleNode
|
|
2144
|
-
});
|
|
2145
|
-
|
|
2146
|
-
const toggleWrap = (typeOrName, attributes = {}) => ({ state, dispatch }) => {
|
|
2821
|
+
const toggleWrap = (typeOrName, attributes = {}) => ({ state, commands }) => {
|
|
2147
2822
|
const type = getNodeType(typeOrName, state.schema);
|
|
2148
2823
|
const isActive = isNodeActive(state, type, attributes);
|
|
2149
2824
|
if (isActive) {
|
|
2150
|
-
return lift
|
|
2825
|
+
return commands.lift(type);
|
|
2151
2826
|
}
|
|
2152
|
-
return wrapIn
|
|
2827
|
+
return commands.wrapIn(type, attributes);
|
|
2153
2828
|
};
|
|
2154
2829
|
|
|
2155
|
-
var toggleWrap$1 = /*#__PURE__*/Object.freeze({
|
|
2156
|
-
__proto__: null,
|
|
2157
|
-
toggleWrap: toggleWrap
|
|
2158
|
-
});
|
|
2159
|
-
|
|
2160
2830
|
const undoInputRule = () => ({ state, dispatch }) => {
|
|
2161
|
-
|
|
2831
|
+
const plugins = state.plugins;
|
|
2832
|
+
for (let i = 0; i < plugins.length; i += 1) {
|
|
2833
|
+
const plugin = plugins[i];
|
|
2834
|
+
let undoable;
|
|
2835
|
+
// @ts-ignore
|
|
2836
|
+
// eslint-disable-next-line
|
|
2837
|
+
if (plugin.spec.isInputRules && (undoable = plugin.getState(state))) {
|
|
2838
|
+
if (dispatch) {
|
|
2839
|
+
const tr = state.tr;
|
|
2840
|
+
const toUndo = undoable.transform;
|
|
2841
|
+
for (let j = toUndo.steps.length - 1; j >= 0; j -= 1) {
|
|
2842
|
+
tr.step(toUndo.steps[j].invert(toUndo.docs[j]));
|
|
2843
|
+
}
|
|
2844
|
+
if (undoable.text) {
|
|
2845
|
+
const marks = tr.doc.resolve(undoable.from).marks();
|
|
2846
|
+
tr.replaceWith(undoable.from, undoable.to, state.schema.text(undoable.text, marks));
|
|
2847
|
+
}
|
|
2848
|
+
else {
|
|
2849
|
+
tr.delete(undoable.from, undoable.to);
|
|
2850
|
+
}
|
|
2851
|
+
}
|
|
2852
|
+
return true;
|
|
2853
|
+
}
|
|
2854
|
+
}
|
|
2855
|
+
return false;
|
|
2162
2856
|
};
|
|
2163
2857
|
|
|
2164
|
-
|
|
2165
|
-
__proto__: null,
|
|
2166
|
-
undoInputRule: undoInputRule
|
|
2167
|
-
});
|
|
2168
|
-
|
|
2169
|
-
const unsetAllMarks = () => ({ tr, state, dispatch }) => {
|
|
2858
|
+
const unsetAllMarks = () => ({ tr, dispatch }) => {
|
|
2170
2859
|
const { selection } = tr;
|
|
2171
2860
|
const { empty, ranges } = selection;
|
|
2172
2861
|
if (empty) {
|
|
2173
2862
|
return true;
|
|
2174
2863
|
}
|
|
2175
2864
|
if (dispatch) {
|
|
2176
|
-
|
|
2177
|
-
.
|
|
2178
|
-
.forEach(([, mark]) => {
|
|
2179
|
-
ranges.forEach(range => {
|
|
2180
|
-
tr.removeMark(range.$from.pos, range.$to.pos, mark);
|
|
2181
|
-
});
|
|
2865
|
+
ranges.forEach(range => {
|
|
2866
|
+
tr.removeMark(range.$from.pos, range.$to.pos);
|
|
2182
2867
|
});
|
|
2183
2868
|
}
|
|
2184
2869
|
return true;
|
|
2185
2870
|
};
|
|
2186
2871
|
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
});
|
|
2191
|
-
|
|
2192
|
-
const unsetMark = typeOrName => ({ tr, state, dispatch }) => {
|
|
2872
|
+
const unsetMark = (typeOrName, options = {}) => ({ tr, state, dispatch }) => {
|
|
2873
|
+
var _a;
|
|
2874
|
+
const { extendEmptyMarkRange = false } = options;
|
|
2193
2875
|
const { selection } = tr;
|
|
2194
2876
|
const type = getMarkType(typeOrName, state.schema);
|
|
2195
2877
|
const { $from, empty, ranges } = selection;
|
|
2196
|
-
if (dispatch) {
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
else {
|
|
2207
|
-
ranges.forEach(range => {
|
|
2208
|
-
tr.removeMark(range.$from.pos, range.$to.pos, type);
|
|
2209
|
-
});
|
|
2878
|
+
if (!dispatch) {
|
|
2879
|
+
return true;
|
|
2880
|
+
}
|
|
2881
|
+
if (empty && extendEmptyMarkRange) {
|
|
2882
|
+
let { from, to } = selection;
|
|
2883
|
+
const attrs = (_a = $from.marks().find(mark => mark.type === type)) === null || _a === void 0 ? void 0 : _a.attrs;
|
|
2884
|
+
const range = getMarkRange($from, type, attrs);
|
|
2885
|
+
if (range) {
|
|
2886
|
+
from = range.from;
|
|
2887
|
+
to = range.to;
|
|
2210
2888
|
}
|
|
2211
|
-
tr.
|
|
2889
|
+
tr.removeMark(from, to, type);
|
|
2890
|
+
}
|
|
2891
|
+
else {
|
|
2892
|
+
ranges.forEach(range => {
|
|
2893
|
+
tr.removeMark(range.$from.pos, range.$to.pos, type);
|
|
2894
|
+
});
|
|
2212
2895
|
}
|
|
2896
|
+
tr.removeStoredMark(type);
|
|
2213
2897
|
return true;
|
|
2214
2898
|
};
|
|
2215
2899
|
|
|
2216
|
-
var unsetMark$1 = /*#__PURE__*/Object.freeze({
|
|
2217
|
-
__proto__: null,
|
|
2218
|
-
unsetMark: unsetMark
|
|
2219
|
-
});
|
|
2220
|
-
|
|
2221
2900
|
const updateAttributes = (typeOrName, attributes = {}) => ({ tr, state, dispatch }) => {
|
|
2222
2901
|
let nodeType = null;
|
|
2223
2902
|
let markType = null;
|
|
2224
|
-
const schemaType = getSchemaTypeNameByName(typeof typeOrName === 'string'
|
|
2225
|
-
? typeOrName
|
|
2226
|
-
: typeOrName.name, state.schema);
|
|
2903
|
+
const schemaType = getSchemaTypeNameByName(typeof typeOrName === 'string' ? typeOrName : typeOrName.name, state.schema);
|
|
2227
2904
|
if (!schemaType) {
|
|
2228
2905
|
return false;
|
|
2229
2906
|
}
|
|
@@ -2262,32 +2939,70 @@ const updateAttributes = (typeOrName, attributes = {}) => ({ tr, state, dispatch
|
|
|
2262
2939
|
return true;
|
|
2263
2940
|
};
|
|
2264
2941
|
|
|
2265
|
-
var updateAttributes$1 = /*#__PURE__*/Object.freeze({
|
|
2266
|
-
__proto__: null,
|
|
2267
|
-
updateAttributes: updateAttributes
|
|
2268
|
-
});
|
|
2269
|
-
|
|
2270
2942
|
const wrapIn = (typeOrName, attributes = {}) => ({ state, dispatch }) => {
|
|
2271
2943
|
const type = getNodeType(typeOrName, state.schema);
|
|
2272
|
-
|
|
2273
|
-
if (isActive) {
|
|
2274
|
-
return false;
|
|
2275
|
-
}
|
|
2276
|
-
return wrapIn$2(type, attributes)(state, dispatch);
|
|
2944
|
+
return wrapIn$1(type, attributes)(state, dispatch);
|
|
2277
2945
|
};
|
|
2278
2946
|
|
|
2279
|
-
var wrapIn$1 = /*#__PURE__*/Object.freeze({
|
|
2280
|
-
__proto__: null,
|
|
2281
|
-
wrapIn: wrapIn
|
|
2282
|
-
});
|
|
2283
|
-
|
|
2284
2947
|
const wrapInList = (typeOrName, attributes = {}) => ({ state, dispatch }) => {
|
|
2285
2948
|
const type = getNodeType(typeOrName, state.schema);
|
|
2286
|
-
return wrapInList$
|
|
2949
|
+
return wrapInList$1(type, attributes)(state, dispatch);
|
|
2287
2950
|
};
|
|
2288
2951
|
|
|
2289
|
-
var
|
|
2952
|
+
var commands = /*#__PURE__*/Object.freeze({
|
|
2290
2953
|
__proto__: null,
|
|
2954
|
+
blur: blur,
|
|
2955
|
+
clearContent: clearContent,
|
|
2956
|
+
clearNodes: clearNodes,
|
|
2957
|
+
command: command,
|
|
2958
|
+
createParagraphNear: createParagraphNear,
|
|
2959
|
+
deleteCurrentNode: deleteCurrentNode,
|
|
2960
|
+
deleteNode: deleteNode,
|
|
2961
|
+
deleteRange: deleteRange,
|
|
2962
|
+
deleteSelection: deleteSelection,
|
|
2963
|
+
enter: enter,
|
|
2964
|
+
exitCode: exitCode,
|
|
2965
|
+
extendMarkRange: extendMarkRange,
|
|
2966
|
+
first: first,
|
|
2967
|
+
focus: focus,
|
|
2968
|
+
forEach: forEach,
|
|
2969
|
+
insertContent: insertContent,
|
|
2970
|
+
insertContentAt: insertContentAt,
|
|
2971
|
+
joinUp: joinUp,
|
|
2972
|
+
joinDown: joinDown,
|
|
2973
|
+
joinBackward: joinBackward,
|
|
2974
|
+
joinForward: joinForward,
|
|
2975
|
+
keyboardShortcut: keyboardShortcut,
|
|
2976
|
+
lift: lift,
|
|
2977
|
+
liftEmptyBlock: liftEmptyBlock,
|
|
2978
|
+
liftListItem: liftListItem,
|
|
2979
|
+
newlineInCode: newlineInCode,
|
|
2980
|
+
resetAttributes: resetAttributes,
|
|
2981
|
+
scrollIntoView: scrollIntoView,
|
|
2982
|
+
selectAll: selectAll,
|
|
2983
|
+
selectNodeBackward: selectNodeBackward,
|
|
2984
|
+
selectNodeForward: selectNodeForward,
|
|
2985
|
+
selectParentNode: selectParentNode,
|
|
2986
|
+
selectTextblockEnd: selectTextblockEnd,
|
|
2987
|
+
selectTextblockStart: selectTextblockStart,
|
|
2988
|
+
setContent: setContent,
|
|
2989
|
+
setMark: setMark,
|
|
2990
|
+
setMeta: setMeta,
|
|
2991
|
+
setNode: setNode,
|
|
2992
|
+
setNodeSelection: setNodeSelection,
|
|
2993
|
+
setTextSelection: setTextSelection,
|
|
2994
|
+
sinkListItem: sinkListItem,
|
|
2995
|
+
splitBlock: splitBlock,
|
|
2996
|
+
splitListItem: splitListItem,
|
|
2997
|
+
toggleList: toggleList,
|
|
2998
|
+
toggleMark: toggleMark,
|
|
2999
|
+
toggleNode: toggleNode,
|
|
3000
|
+
toggleWrap: toggleWrap,
|
|
3001
|
+
undoInputRule: undoInputRule,
|
|
3002
|
+
unsetAllMarks: unsetAllMarks,
|
|
3003
|
+
unsetMark: unsetMark,
|
|
3004
|
+
updateAttributes: updateAttributes,
|
|
3005
|
+
wrapIn: wrapIn,
|
|
2291
3006
|
wrapInList: wrapInList
|
|
2292
3007
|
});
|
|
2293
3008
|
|
|
@@ -2295,54 +3010,7 @@ const Commands = Extension.create({
|
|
|
2295
3010
|
name: 'commands',
|
|
2296
3011
|
addCommands() {
|
|
2297
3012
|
return {
|
|
2298
|
-
...
|
|
2299
|
-
...clearContent$1,
|
|
2300
|
-
...clearNodes$1,
|
|
2301
|
-
...command$1,
|
|
2302
|
-
...createParagraphNear$1,
|
|
2303
|
-
...deleteNode$1,
|
|
2304
|
-
...deleteRange$1,
|
|
2305
|
-
...deleteSelection$1,
|
|
2306
|
-
...enter$1,
|
|
2307
|
-
...exitCode$1,
|
|
2308
|
-
...extendMarkRange$1,
|
|
2309
|
-
...first$1,
|
|
2310
|
-
...focus$1,
|
|
2311
|
-
...forEach$1,
|
|
2312
|
-
...insertContent$1,
|
|
2313
|
-
...insertContentAt$1,
|
|
2314
|
-
...joinBackward$1,
|
|
2315
|
-
...joinForward$1,
|
|
2316
|
-
...keyboardShortcut$1,
|
|
2317
|
-
...lift$1,
|
|
2318
|
-
...liftEmptyBlock$1,
|
|
2319
|
-
...liftListItem$1,
|
|
2320
|
-
...newlineInCode$1,
|
|
2321
|
-
...resetAttributes$1,
|
|
2322
|
-
...scrollIntoView$1,
|
|
2323
|
-
...selectAll$1,
|
|
2324
|
-
...selectNodeBackward$1,
|
|
2325
|
-
...selectNodeForward$1,
|
|
2326
|
-
...selectParentNode$1,
|
|
2327
|
-
...setContent$1,
|
|
2328
|
-
...setMark$1,
|
|
2329
|
-
...setMeta$1,
|
|
2330
|
-
...setNode$1,
|
|
2331
|
-
...setNodeSelection$1,
|
|
2332
|
-
...setTextSelection$1,
|
|
2333
|
-
...sinkListItem$1,
|
|
2334
|
-
...splitBlock$1,
|
|
2335
|
-
...splitListItem$1,
|
|
2336
|
-
...toggleList$1,
|
|
2337
|
-
...toggleMark$1,
|
|
2338
|
-
...toggleNode$1,
|
|
2339
|
-
...toggleWrap$1,
|
|
2340
|
-
...undoInputRule$1,
|
|
2341
|
-
...unsetAllMarks$1,
|
|
2342
|
-
...unsetMark$1,
|
|
2343
|
-
...updateAttributes$1,
|
|
2344
|
-
...wrapIn$1,
|
|
2345
|
-
...wrapInList$1,
|
|
3013
|
+
...commands,
|
|
2346
3014
|
};
|
|
2347
3015
|
},
|
|
2348
3016
|
});
|
|
@@ -2369,9 +3037,6 @@ const FocusEvents = Extension.create({
|
|
|
2369
3037
|
new Plugin({
|
|
2370
3038
|
key: new PluginKey('focusEvents'),
|
|
2371
3039
|
props: {
|
|
2372
|
-
attributes: {
|
|
2373
|
-
tabindex: '0',
|
|
2374
|
-
},
|
|
2375
3040
|
handleDOMEvents: {
|
|
2376
3041
|
focus: (view, event) => {
|
|
2377
3042
|
editor.isFocused = true;
|
|
@@ -2401,30 +3066,119 @@ const Keymap = Extension.create({
|
|
|
2401
3066
|
addKeyboardShortcuts() {
|
|
2402
3067
|
const handleBackspace = () => this.editor.commands.first(({ commands }) => [
|
|
2403
3068
|
() => commands.undoInputRule(),
|
|
3069
|
+
// maybe convert first text block node to default node
|
|
3070
|
+
() => commands.command(({ tr }) => {
|
|
3071
|
+
const { selection, doc } = tr;
|
|
3072
|
+
const { empty, $anchor } = selection;
|
|
3073
|
+
const { pos, parent } = $anchor;
|
|
3074
|
+
const isAtStart = Selection.atStart(doc).from === pos;
|
|
3075
|
+
if (!empty || !isAtStart || !parent.type.isTextblock || parent.textContent.length) {
|
|
3076
|
+
return false;
|
|
3077
|
+
}
|
|
3078
|
+
return commands.clearNodes();
|
|
3079
|
+
}),
|
|
2404
3080
|
() => commands.deleteSelection(),
|
|
2405
3081
|
() => commands.joinBackward(),
|
|
2406
3082
|
() => commands.selectNodeBackward(),
|
|
2407
3083
|
]);
|
|
2408
3084
|
const handleDelete = () => this.editor.commands.first(({ commands }) => [
|
|
2409
3085
|
() => commands.deleteSelection(),
|
|
3086
|
+
() => commands.deleteCurrentNode(),
|
|
2410
3087
|
() => commands.joinForward(),
|
|
2411
3088
|
() => commands.selectNodeForward(),
|
|
2412
3089
|
]);
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
3090
|
+
const handleEnter = () => this.editor.commands.first(({ commands }) => [
|
|
3091
|
+
() => commands.newlineInCode(),
|
|
3092
|
+
() => commands.createParagraphNear(),
|
|
3093
|
+
() => commands.liftEmptyBlock(),
|
|
3094
|
+
() => commands.splitBlock(),
|
|
3095
|
+
]);
|
|
3096
|
+
const baseKeymap = {
|
|
3097
|
+
Enter: handleEnter,
|
|
2420
3098
|
'Mod-Enter': () => this.editor.commands.exitCode(),
|
|
2421
|
-
Backspace:
|
|
2422
|
-
'Mod-Backspace':
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
3099
|
+
Backspace: handleBackspace,
|
|
3100
|
+
'Mod-Backspace': handleBackspace,
|
|
3101
|
+
'Shift-Backspace': handleBackspace,
|
|
3102
|
+
Delete: handleDelete,
|
|
3103
|
+
'Mod-Delete': handleDelete,
|
|
3104
|
+
'Mod-a': () => this.editor.commands.selectAll(),
|
|
2427
3105
|
};
|
|
3106
|
+
const pcKeymap = {
|
|
3107
|
+
...baseKeymap,
|
|
3108
|
+
};
|
|
3109
|
+
const macKeymap = {
|
|
3110
|
+
...baseKeymap,
|
|
3111
|
+
'Ctrl-h': handleBackspace,
|
|
3112
|
+
'Alt-Backspace': handleBackspace,
|
|
3113
|
+
'Ctrl-d': handleDelete,
|
|
3114
|
+
'Ctrl-Alt-Backspace': handleDelete,
|
|
3115
|
+
'Alt-Delete': handleDelete,
|
|
3116
|
+
'Alt-d': handleDelete,
|
|
3117
|
+
'Ctrl-a': () => this.editor.commands.selectTextblockStart(),
|
|
3118
|
+
'Ctrl-e': () => this.editor.commands.selectTextblockEnd(),
|
|
3119
|
+
};
|
|
3120
|
+
if (isiOS() || isMacOS()) {
|
|
3121
|
+
return macKeymap;
|
|
3122
|
+
}
|
|
3123
|
+
return pcKeymap;
|
|
3124
|
+
},
|
|
3125
|
+
addProseMirrorPlugins() {
|
|
3126
|
+
return [
|
|
3127
|
+
// With this plugin we check if the whole document was selected and deleted.
|
|
3128
|
+
// In this case we will additionally call `clearNodes()` to convert e.g. a heading
|
|
3129
|
+
// to a paragraph if necessary.
|
|
3130
|
+
// This is an alternative to ProseMirror's `AllSelection`, which doesn’t work well
|
|
3131
|
+
// with many other commands.
|
|
3132
|
+
new Plugin({
|
|
3133
|
+
key: new PluginKey('clearDocument'),
|
|
3134
|
+
appendTransaction: (transactions, oldState, newState) => {
|
|
3135
|
+
const docChanges = transactions.some(transaction => transaction.docChanged)
|
|
3136
|
+
&& !oldState.doc.eq(newState.doc);
|
|
3137
|
+
if (!docChanges) {
|
|
3138
|
+
return;
|
|
3139
|
+
}
|
|
3140
|
+
const { empty, from, to } = oldState.selection;
|
|
3141
|
+
const allFrom = Selection.atStart(oldState.doc).from;
|
|
3142
|
+
const allEnd = Selection.atEnd(oldState.doc).to;
|
|
3143
|
+
const allWasSelected = from === allFrom && to === allEnd;
|
|
3144
|
+
if (empty || !allWasSelected) {
|
|
3145
|
+
return;
|
|
3146
|
+
}
|
|
3147
|
+
const isEmpty = newState.doc.textBetween(0, newState.doc.content.size, ' ', ' ').length === 0;
|
|
3148
|
+
if (!isEmpty) {
|
|
3149
|
+
return;
|
|
3150
|
+
}
|
|
3151
|
+
const tr = newState.tr;
|
|
3152
|
+
const state = createChainableState({
|
|
3153
|
+
state: newState,
|
|
3154
|
+
transaction: tr,
|
|
3155
|
+
});
|
|
3156
|
+
const { commands } = new CommandManager({
|
|
3157
|
+
editor: this.editor,
|
|
3158
|
+
state,
|
|
3159
|
+
});
|
|
3160
|
+
commands.clearNodes();
|
|
3161
|
+
if (!tr.steps.length) {
|
|
3162
|
+
return;
|
|
3163
|
+
}
|
|
3164
|
+
return tr;
|
|
3165
|
+
},
|
|
3166
|
+
}),
|
|
3167
|
+
];
|
|
3168
|
+
},
|
|
3169
|
+
});
|
|
3170
|
+
|
|
3171
|
+
const Tabindex = Extension.create({
|
|
3172
|
+
name: 'tabindex',
|
|
3173
|
+
addProseMirrorPlugins() {
|
|
3174
|
+
return [
|
|
3175
|
+
new Plugin({
|
|
3176
|
+
key: new PluginKey('tabindex'),
|
|
3177
|
+
props: {
|
|
3178
|
+
attributes: this.editor.isEditable ? { tabindex: '0' } : {},
|
|
3179
|
+
},
|
|
3180
|
+
}),
|
|
3181
|
+
];
|
|
2428
3182
|
},
|
|
2429
3183
|
});
|
|
2430
3184
|
|
|
@@ -2434,7 +3188,8 @@ var extensions = /*#__PURE__*/Object.freeze({
|
|
|
2434
3188
|
Commands: Commands,
|
|
2435
3189
|
Editable: Editable,
|
|
2436
3190
|
FocusEvents: FocusEvents,
|
|
2437
|
-
Keymap: Keymap
|
|
3191
|
+
Keymap: Keymap,
|
|
3192
|
+
Tabindex: Tabindex
|
|
2438
3193
|
});
|
|
2439
3194
|
|
|
2440
3195
|
const style = `.ProseMirror {
|
|
@@ -2444,8 +3199,10 @@ const style = `.ProseMirror {
|
|
|
2444
3199
|
.ProseMirror {
|
|
2445
3200
|
word-wrap: break-word;
|
|
2446
3201
|
white-space: pre-wrap;
|
|
3202
|
+
white-space: break-spaces;
|
|
2447
3203
|
-webkit-font-variant-ligatures: none;
|
|
2448
3204
|
font-variant-ligatures: none;
|
|
3205
|
+
font-feature-settings: "liga" 0; /* the above doesn't seem to work in Edge */
|
|
2449
3206
|
}
|
|
2450
3207
|
|
|
2451
3208
|
.ProseMirror [contenteditable="false"] {
|
|
@@ -2460,10 +3217,19 @@ const style = `.ProseMirror {
|
|
|
2460
3217
|
white-space: pre-wrap;
|
|
2461
3218
|
}
|
|
2462
3219
|
|
|
3220
|
+
img.ProseMirror-separator {
|
|
3221
|
+
display: inline !important;
|
|
3222
|
+
border: none !important;
|
|
3223
|
+
margin: 0 !important;
|
|
3224
|
+
width: 1px !important;
|
|
3225
|
+
height: 1px !important;
|
|
3226
|
+
}
|
|
3227
|
+
|
|
2463
3228
|
.ProseMirror-gapcursor {
|
|
2464
3229
|
display: none;
|
|
2465
3230
|
pointer-events: none;
|
|
2466
3231
|
position: absolute;
|
|
3232
|
+
margin: 0;
|
|
2467
3233
|
}
|
|
2468
3234
|
|
|
2469
3235
|
.ProseMirror-gapcursor:after {
|
|
@@ -2502,14 +3268,31 @@ const style = `.ProseMirror {
|
|
|
2502
3268
|
opacity: 0
|
|
2503
3269
|
}`;
|
|
2504
3270
|
|
|
3271
|
+
function createStyleTag(style, nonce) {
|
|
3272
|
+
const tiptapStyleTag = document.querySelector('style[data-tiptap-style]');
|
|
3273
|
+
if (tiptapStyleTag !== null) {
|
|
3274
|
+
return tiptapStyleTag;
|
|
3275
|
+
}
|
|
3276
|
+
const styleNode = document.createElement('style');
|
|
3277
|
+
if (nonce) {
|
|
3278
|
+
styleNode.setAttribute('nonce', nonce);
|
|
3279
|
+
}
|
|
3280
|
+
styleNode.setAttribute('data-tiptap-style', '');
|
|
3281
|
+
styleNode.innerHTML = style;
|
|
3282
|
+
document.getElementsByTagName('head')[0].appendChild(styleNode);
|
|
3283
|
+
return styleNode;
|
|
3284
|
+
}
|
|
3285
|
+
|
|
2505
3286
|
class Editor extends EventEmitter {
|
|
2506
3287
|
constructor(options = {}) {
|
|
2507
3288
|
super();
|
|
2508
3289
|
this.isFocused = false;
|
|
3290
|
+
this.extensionStorage = {};
|
|
2509
3291
|
this.options = {
|
|
2510
3292
|
element: document.createElement('div'),
|
|
2511
3293
|
content: '',
|
|
2512
3294
|
injectCSS: true,
|
|
3295
|
+
injectNonce: undefined,
|
|
2513
3296
|
extensions: [],
|
|
2514
3297
|
autofocus: false,
|
|
2515
3298
|
editable: true,
|
|
@@ -2517,6 +3300,7 @@ class Editor extends EventEmitter {
|
|
|
2517
3300
|
parseOptions: {},
|
|
2518
3301
|
enableInputRules: true,
|
|
2519
3302
|
enablePasteRules: true,
|
|
3303
|
+
enableCoreExtensions: true,
|
|
2520
3304
|
onBeforeCreate: () => null,
|
|
2521
3305
|
onCreate: () => null,
|
|
2522
3306
|
onUpdate: () => null,
|
|
@@ -2551,30 +3335,36 @@ class Editor extends EventEmitter {
|
|
|
2551
3335
|
this.emit('create', { editor: this });
|
|
2552
3336
|
}, 0);
|
|
2553
3337
|
}
|
|
3338
|
+
/**
|
|
3339
|
+
* Returns the editor storage.
|
|
3340
|
+
*/
|
|
3341
|
+
get storage() {
|
|
3342
|
+
return this.extensionStorage;
|
|
3343
|
+
}
|
|
2554
3344
|
/**
|
|
2555
3345
|
* An object of all registered commands.
|
|
2556
3346
|
*/
|
|
2557
3347
|
get commands() {
|
|
2558
|
-
return this.commandManager.
|
|
3348
|
+
return this.commandManager.commands;
|
|
2559
3349
|
}
|
|
2560
3350
|
/**
|
|
2561
3351
|
* Create a command chain to call multiple commands at once.
|
|
2562
3352
|
*/
|
|
2563
3353
|
chain() {
|
|
2564
|
-
return this.commandManager.
|
|
3354
|
+
return this.commandManager.chain();
|
|
2565
3355
|
}
|
|
2566
3356
|
/**
|
|
2567
3357
|
* Check if a command or a command chain can be executed. Without executing it.
|
|
2568
3358
|
*/
|
|
2569
3359
|
can() {
|
|
2570
|
-
return this.commandManager.
|
|
3360
|
+
return this.commandManager.can();
|
|
2571
3361
|
}
|
|
2572
3362
|
/**
|
|
2573
3363
|
* Inject CSS styles.
|
|
2574
3364
|
*/
|
|
2575
3365
|
injectCSS() {
|
|
2576
3366
|
if (this.options.injectCSS && document) {
|
|
2577
|
-
this.css = createStyleTag(style);
|
|
3367
|
+
this.css = createStyleTag(style, this.options.injectNonce);
|
|
2578
3368
|
}
|
|
2579
3369
|
}
|
|
2580
3370
|
/**
|
|
@@ -2598,8 +3388,11 @@ class Editor extends EventEmitter {
|
|
|
2598
3388
|
/**
|
|
2599
3389
|
* Update editable state of the editor.
|
|
2600
3390
|
*/
|
|
2601
|
-
setEditable(editable) {
|
|
3391
|
+
setEditable(editable, emitUpdate = true) {
|
|
2602
3392
|
this.setOptions({ editable });
|
|
3393
|
+
if (emitUpdate) {
|
|
3394
|
+
this.emit('update', { editor: this, transaction: this.state.tr });
|
|
3395
|
+
}
|
|
2603
3396
|
}
|
|
2604
3397
|
/**
|
|
2605
3398
|
* Returns whether the editor is editable.
|
|
@@ -2608,9 +3401,7 @@ class Editor extends EventEmitter {
|
|
|
2608
3401
|
// since plugins are applied after creating the view
|
|
2609
3402
|
// `editable` is always `true` for one tick.
|
|
2610
3403
|
// that’s why we also have to check for `options.editable`
|
|
2611
|
-
return this.options.editable
|
|
2612
|
-
&& this.view
|
|
2613
|
-
&& this.view.editable;
|
|
3404
|
+
return this.options.editable && this.view && this.view.editable;
|
|
2614
3405
|
}
|
|
2615
3406
|
/**
|
|
2616
3407
|
* Returns the editor state.
|
|
@@ -2625,8 +3416,8 @@ class Editor extends EventEmitter {
|
|
|
2625
3416
|
* @param handlePlugins Control how to merge the plugin into the existing plugins.
|
|
2626
3417
|
*/
|
|
2627
3418
|
registerPlugin(plugin, handlePlugins) {
|
|
2628
|
-
const plugins =
|
|
2629
|
-
? handlePlugins(plugin, this.state.plugins)
|
|
3419
|
+
const plugins = isFunction(handlePlugins)
|
|
3420
|
+
? handlePlugins(plugin, [...this.state.plugins])
|
|
2630
3421
|
: [...this.state.plugins, plugin];
|
|
2631
3422
|
const state = this.state.reconfigure({ plugins });
|
|
2632
3423
|
this.view.updateState(state);
|
|
@@ -2634,16 +3425,14 @@ class Editor extends EventEmitter {
|
|
|
2634
3425
|
/**
|
|
2635
3426
|
* Unregister a ProseMirror plugin.
|
|
2636
3427
|
*
|
|
2637
|
-
* @param
|
|
3428
|
+
* @param nameOrPluginKey The plugins name
|
|
2638
3429
|
*/
|
|
2639
3430
|
unregisterPlugin(nameOrPluginKey) {
|
|
2640
3431
|
if (this.isDestroyed) {
|
|
2641
3432
|
return;
|
|
2642
3433
|
}
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
// @ts-ignore
|
|
2646
|
-
: nameOrPluginKey.key;
|
|
3434
|
+
// @ts-ignore
|
|
3435
|
+
const name = typeof nameOrPluginKey === 'string' ? `${nameOrPluginKey}$` : nameOrPluginKey.key;
|
|
2647
3436
|
const state = this.state.reconfigure({
|
|
2648
3437
|
// @ts-ignore
|
|
2649
3438
|
plugins: this.state.plugins.filter(plugin => !plugin.key.startsWith(name)),
|
|
@@ -2654,7 +3443,7 @@ class Editor extends EventEmitter {
|
|
|
2654
3443
|
* Creates an extension manager.
|
|
2655
3444
|
*/
|
|
2656
3445
|
createExtensionManager() {
|
|
2657
|
-
const coreExtensions = Object.
|
|
3446
|
+
const coreExtensions = this.options.enableCoreExtensions ? Object.values(extensions) : [];
|
|
2658
3447
|
const allExtensions = [...coreExtensions, ...this.options.extensions].filter(extension => {
|
|
2659
3448
|
return ['extension', 'node', 'mark'].includes(extension === null || extension === void 0 ? void 0 : extension.type);
|
|
2660
3449
|
});
|
|
@@ -2664,7 +3453,9 @@ class Editor extends EventEmitter {
|
|
|
2664
3453
|
* Creates an command manager.
|
|
2665
3454
|
*/
|
|
2666
3455
|
createCommandManager() {
|
|
2667
|
-
this.commandManager = new CommandManager(
|
|
3456
|
+
this.commandManager = new CommandManager({
|
|
3457
|
+
editor: this,
|
|
3458
|
+
});
|
|
2668
3459
|
}
|
|
2669
3460
|
/**
|
|
2670
3461
|
* Creates a ProseMirror schema.
|
|
@@ -2676,11 +3467,14 @@ class Editor extends EventEmitter {
|
|
|
2676
3467
|
* Creates a ProseMirror view.
|
|
2677
3468
|
*/
|
|
2678
3469
|
createView() {
|
|
3470
|
+
const doc = createDocument(this.options.content, this.schema, this.options.parseOptions);
|
|
3471
|
+
const selection = resolveFocusPosition(doc, this.options.autofocus);
|
|
2679
3472
|
this.view = new EditorView(this.options.element, {
|
|
2680
3473
|
...this.options.editorProps,
|
|
2681
3474
|
dispatchTransaction: this.dispatchTransaction.bind(this),
|
|
2682
3475
|
state: EditorState.create({
|
|
2683
|
-
doc
|
|
3476
|
+
doc,
|
|
3477
|
+
selection: selection || undefined,
|
|
2684
3478
|
}),
|
|
2685
3479
|
});
|
|
2686
3480
|
// `editor.view` is not yet available at this time.
|
|
@@ -2717,6 +3511,11 @@ class Editor extends EventEmitter {
|
|
|
2717
3511
|
* @param transaction An editor state transaction
|
|
2718
3512
|
*/
|
|
2719
3513
|
dispatchTransaction(transaction) {
|
|
3514
|
+
// if the editor / the view of the editor was destroyed
|
|
3515
|
+
// the transaction should not be dispatched as there is no view anymore.
|
|
3516
|
+
if (this.view.isDestroyed) {
|
|
3517
|
+
return;
|
|
3518
|
+
}
|
|
2720
3519
|
if (this.isCapturingTransaction) {
|
|
2721
3520
|
if (!this.capturedTransaction) {
|
|
2722
3521
|
this.capturedTransaction = transaction;
|
|
@@ -2769,12 +3568,8 @@ class Editor extends EventEmitter {
|
|
|
2769
3568
|
return getAttributes(this.state, nameOrType);
|
|
2770
3569
|
}
|
|
2771
3570
|
isActive(nameOrAttributes, attributesOrUndefined) {
|
|
2772
|
-
const name = typeof nameOrAttributes === 'string'
|
|
2773
|
-
|
|
2774
|
-
: null;
|
|
2775
|
-
const attributes = typeof nameOrAttributes === 'string'
|
|
2776
|
-
? attributesOrUndefined
|
|
2777
|
-
: nameOrAttributes;
|
|
3571
|
+
const name = typeof nameOrAttributes === 'string' ? nameOrAttributes : null;
|
|
3572
|
+
const attributes = typeof nameOrAttributes === 'string' ? attributesOrUndefined : nameOrAttributes;
|
|
2778
3573
|
return isActive(this.state, name, attributes);
|
|
2779
3574
|
}
|
|
2780
3575
|
/**
|
|
@@ -2787,7 +3582,20 @@ class Editor extends EventEmitter {
|
|
|
2787
3582
|
* Get the document as HTML.
|
|
2788
3583
|
*/
|
|
2789
3584
|
getHTML() {
|
|
2790
|
-
return getHTMLFromFragment(this.state.doc, this.schema);
|
|
3585
|
+
return getHTMLFromFragment(this.state.doc.content, this.schema);
|
|
3586
|
+
}
|
|
3587
|
+
/**
|
|
3588
|
+
* Get the document as text.
|
|
3589
|
+
*/
|
|
3590
|
+
getText(options) {
|
|
3591
|
+
const { blockSeparator = '\n\n', textSerializers = {} } = options || {};
|
|
3592
|
+
return getText(this.state.doc, {
|
|
3593
|
+
blockSeparator,
|
|
3594
|
+
textSerializers: {
|
|
3595
|
+
...getTextSerializersFromSchema(this.schema),
|
|
3596
|
+
...textSerializers,
|
|
3597
|
+
},
|
|
3598
|
+
});
|
|
2791
3599
|
}
|
|
2792
3600
|
/**
|
|
2793
3601
|
* Check if there is no content.
|
|
@@ -2797,8 +3605,11 @@ class Editor extends EventEmitter {
|
|
|
2797
3605
|
}
|
|
2798
3606
|
/**
|
|
2799
3607
|
* Get the number of characters for the current document.
|
|
3608
|
+
*
|
|
3609
|
+
* @deprecated
|
|
2800
3610
|
*/
|
|
2801
3611
|
getCharacterCount() {
|
|
3612
|
+
console.warn('[tiptap warn]: "editor.getCharacterCount()" is deprecated. Please use "editor.storage.characterCount.characters()" instead.');
|
|
2802
3613
|
return this.state.doc.content.size - 2;
|
|
2803
3614
|
}
|
|
2804
3615
|
/**
|
|
@@ -2810,7 +3621,6 @@ class Editor extends EventEmitter {
|
|
|
2810
3621
|
this.view.destroy();
|
|
2811
3622
|
}
|
|
2812
3623
|
this.removeAllListeners();
|
|
2813
|
-
removeElement(this.css);
|
|
2814
3624
|
}
|
|
2815
3625
|
/**
|
|
2816
3626
|
* Check if the editor is already destroyed.
|
|
@@ -2822,10 +3632,188 @@ class Editor extends EventEmitter {
|
|
|
2822
3632
|
}
|
|
2823
3633
|
}
|
|
2824
3634
|
|
|
2825
|
-
|
|
3635
|
+
/**
|
|
3636
|
+
* Build an input rule that adds a mark when the
|
|
3637
|
+
* matched text is typed into it.
|
|
3638
|
+
*/
|
|
3639
|
+
function markInputRule(config) {
|
|
3640
|
+
return new InputRule({
|
|
3641
|
+
find: config.find,
|
|
3642
|
+
handler: ({ state, range, match }) => {
|
|
3643
|
+
const attributes = callOrReturn(config.getAttributes, undefined, match);
|
|
3644
|
+
if (attributes === false || attributes === null) {
|
|
3645
|
+
return null;
|
|
3646
|
+
}
|
|
3647
|
+
const { tr } = state;
|
|
3648
|
+
const captureGroup = match[match.length - 1];
|
|
3649
|
+
const fullMatch = match[0];
|
|
3650
|
+
let markEnd = range.to;
|
|
3651
|
+
if (captureGroup) {
|
|
3652
|
+
const startSpaces = fullMatch.search(/\S/);
|
|
3653
|
+
const textStart = range.from + fullMatch.indexOf(captureGroup);
|
|
3654
|
+
const textEnd = textStart + captureGroup.length;
|
|
3655
|
+
const excludedMarks = getMarksBetween(range.from, range.to, state.doc)
|
|
3656
|
+
.filter(item => {
|
|
3657
|
+
// @ts-ignore
|
|
3658
|
+
const excluded = item.mark.type.excluded;
|
|
3659
|
+
return excluded.find(type => type === config.type && type !== item.mark.type);
|
|
3660
|
+
})
|
|
3661
|
+
.filter(item => item.to > textStart);
|
|
3662
|
+
if (excludedMarks.length) {
|
|
3663
|
+
return null;
|
|
3664
|
+
}
|
|
3665
|
+
if (textEnd < range.to) {
|
|
3666
|
+
tr.delete(textEnd, range.to);
|
|
3667
|
+
}
|
|
3668
|
+
if (textStart > range.from) {
|
|
3669
|
+
tr.delete(range.from + startSpaces, textStart);
|
|
3670
|
+
}
|
|
3671
|
+
markEnd = range.from + startSpaces + captureGroup.length;
|
|
3672
|
+
tr.addMark(range.from + startSpaces, markEnd, config.type.create(attributes || {}));
|
|
3673
|
+
tr.removeStoredMark(config.type);
|
|
3674
|
+
}
|
|
3675
|
+
},
|
|
3676
|
+
});
|
|
3677
|
+
}
|
|
3678
|
+
|
|
3679
|
+
/**
|
|
3680
|
+
* Build an input rule that adds a node when the
|
|
3681
|
+
* matched text is typed into it.
|
|
3682
|
+
*/
|
|
3683
|
+
function nodeInputRule(config) {
|
|
3684
|
+
return new InputRule({
|
|
3685
|
+
find: config.find,
|
|
3686
|
+
handler: ({ state, range, match }) => {
|
|
3687
|
+
const attributes = callOrReturn(config.getAttributes, undefined, match) || {};
|
|
3688
|
+
const { tr } = state;
|
|
3689
|
+
const start = range.from;
|
|
3690
|
+
let end = range.to;
|
|
3691
|
+
if (match[1]) {
|
|
3692
|
+
const offset = match[0].lastIndexOf(match[1]);
|
|
3693
|
+
let matchStart = start + offset;
|
|
3694
|
+
if (matchStart > end) {
|
|
3695
|
+
matchStart = end;
|
|
3696
|
+
}
|
|
3697
|
+
else {
|
|
3698
|
+
end = matchStart + match[1].length;
|
|
3699
|
+
}
|
|
3700
|
+
// insert last typed character
|
|
3701
|
+
const lastChar = match[0][match[0].length - 1];
|
|
3702
|
+
tr.insertText(lastChar, start + match[0].length - 1);
|
|
3703
|
+
// insert node from input rule
|
|
3704
|
+
tr.replaceWith(matchStart, end, config.type.create(attributes));
|
|
3705
|
+
}
|
|
3706
|
+
else if (match[0]) {
|
|
3707
|
+
tr.replaceWith(start, end, config.type.create(attributes));
|
|
3708
|
+
}
|
|
3709
|
+
},
|
|
3710
|
+
});
|
|
3711
|
+
}
|
|
3712
|
+
|
|
3713
|
+
/**
|
|
3714
|
+
* Build an input rule that changes the type of a textblock when the
|
|
3715
|
+
* matched text is typed into it. When using a regular expresion you’ll
|
|
3716
|
+
* probably want the regexp to start with `^`, so that the pattern can
|
|
3717
|
+
* only occur at the start of a textblock.
|
|
3718
|
+
*/
|
|
3719
|
+
function textblockTypeInputRule(config) {
|
|
3720
|
+
return new InputRule({
|
|
3721
|
+
find: config.find,
|
|
3722
|
+
handler: ({ state, range, match }) => {
|
|
3723
|
+
const $start = state.doc.resolve(range.from);
|
|
3724
|
+
const attributes = callOrReturn(config.getAttributes, undefined, match) || {};
|
|
3725
|
+
if (!$start.node(-1).canReplaceWith($start.index(-1), $start.indexAfter(-1), config.type)) {
|
|
3726
|
+
return null;
|
|
3727
|
+
}
|
|
3728
|
+
state.tr
|
|
3729
|
+
.delete(range.from, range.to)
|
|
3730
|
+
.setBlockType(range.from, range.from, config.type, attributes);
|
|
3731
|
+
},
|
|
3732
|
+
});
|
|
3733
|
+
}
|
|
3734
|
+
|
|
3735
|
+
/**
|
|
3736
|
+
* Build an input rule that replaces text when the
|
|
3737
|
+
* matched text is typed into it.
|
|
3738
|
+
*/
|
|
3739
|
+
function textInputRule(config) {
|
|
3740
|
+
return new InputRule({
|
|
3741
|
+
find: config.find,
|
|
3742
|
+
handler: ({ state, range, match }) => {
|
|
3743
|
+
let insert = config.replace;
|
|
3744
|
+
let start = range.from;
|
|
3745
|
+
const end = range.to;
|
|
3746
|
+
if (match[1]) {
|
|
3747
|
+
const offset = match[0].lastIndexOf(match[1]);
|
|
3748
|
+
insert += match[0].slice(offset + match[1].length);
|
|
3749
|
+
start += offset;
|
|
3750
|
+
const cutOff = start - end;
|
|
3751
|
+
if (cutOff > 0) {
|
|
3752
|
+
insert = match[0].slice(offset - cutOff, offset) + insert;
|
|
3753
|
+
start = end;
|
|
3754
|
+
}
|
|
3755
|
+
}
|
|
3756
|
+
state.tr.insertText(insert, start, end);
|
|
3757
|
+
},
|
|
3758
|
+
});
|
|
3759
|
+
}
|
|
3760
|
+
|
|
3761
|
+
/**
|
|
3762
|
+
* Build an input rule for automatically wrapping a textblock when a
|
|
3763
|
+
* given string is typed. When using a regular expresion you’ll
|
|
3764
|
+
* probably want the regexp to start with `^`, so that the pattern can
|
|
3765
|
+
* only occur at the start of a textblock.
|
|
3766
|
+
*
|
|
3767
|
+
* `type` is the type of node to wrap in.
|
|
3768
|
+
*
|
|
3769
|
+
* By default, if there’s a node with the same type above the newly
|
|
3770
|
+
* wrapped node, the rule will try to join those
|
|
3771
|
+
* two nodes. You can pass a join predicate, which takes a regular
|
|
3772
|
+
* expression match and the node before the wrapped node, and can
|
|
3773
|
+
* return a boolean to indicate whether a join should happen.
|
|
3774
|
+
*/
|
|
3775
|
+
function wrappingInputRule(config) {
|
|
3776
|
+
return new InputRule({
|
|
3777
|
+
find: config.find,
|
|
3778
|
+
handler: ({ state, range, match, chain, }) => {
|
|
3779
|
+
const attributes = callOrReturn(config.getAttributes, undefined, match) || {};
|
|
3780
|
+
const tr = state.tr.delete(range.from, range.to);
|
|
3781
|
+
const $start = tr.doc.resolve(range.from);
|
|
3782
|
+
const blockRange = $start.blockRange();
|
|
3783
|
+
const wrapping = blockRange && findWrapping(blockRange, config.type, attributes);
|
|
3784
|
+
if (!wrapping) {
|
|
3785
|
+
return null;
|
|
3786
|
+
}
|
|
3787
|
+
tr.wrap(blockRange, wrapping);
|
|
3788
|
+
if (config.keepMarks && config.editor) {
|
|
3789
|
+
const { selection, storedMarks } = state;
|
|
3790
|
+
const { splittableMarks } = config.editor.extensionManager;
|
|
3791
|
+
const marks = storedMarks || (selection.$to.parentOffset && selection.$from.marks());
|
|
3792
|
+
if (marks) {
|
|
3793
|
+
const filteredMarks = marks.filter(mark => splittableMarks.includes(mark.type.name));
|
|
3794
|
+
tr.ensureMarks(filteredMarks);
|
|
3795
|
+
}
|
|
3796
|
+
}
|
|
3797
|
+
if (config.keepAttributes) {
|
|
3798
|
+
/** If the nodeType is `bulletList` or `orderedList` set the `nodeType` as `listItem` */
|
|
3799
|
+
const nodeType = config.type.name === 'bulletList' || config.type.name === 'orderedList' ? 'listItem' : 'taskList';
|
|
3800
|
+
chain().updateAttributes(nodeType, attributes).run();
|
|
3801
|
+
}
|
|
3802
|
+
const before = tr.doc.resolve(range.from - 1).nodeBefore;
|
|
3803
|
+
if (before
|
|
3804
|
+
&& before.type === config.type
|
|
3805
|
+
&& canJoin(tr.doc, range.from - 1)
|
|
3806
|
+
&& (!config.joinPredicate || config.joinPredicate(match, before))) {
|
|
3807
|
+
tr.join(range.from - 1);
|
|
3808
|
+
}
|
|
3809
|
+
},
|
|
3810
|
+
});
|
|
3811
|
+
}
|
|
3812
|
+
|
|
3813
|
+
class Mark {
|
|
2826
3814
|
constructor(config = {}) {
|
|
2827
|
-
this.type = '
|
|
2828
|
-
this.name = '
|
|
3815
|
+
this.type = 'mark';
|
|
3816
|
+
this.name = 'mark';
|
|
2829
3817
|
this.parent = null;
|
|
2830
3818
|
this.child = null;
|
|
2831
3819
|
this.config = {
|
|
@@ -2837,36 +3825,78 @@ class Node {
|
|
|
2837
3825
|
...config,
|
|
2838
3826
|
};
|
|
2839
3827
|
this.name = this.config.name;
|
|
3828
|
+
if (config.defaultOptions) {
|
|
3829
|
+
console.warn(`[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${this.name}".`);
|
|
3830
|
+
}
|
|
3831
|
+
// TODO: remove `addOptions` fallback
|
|
2840
3832
|
this.options = this.config.defaultOptions;
|
|
3833
|
+
if (this.config.addOptions) {
|
|
3834
|
+
this.options = callOrReturn(getExtensionField(this, 'addOptions', {
|
|
3835
|
+
name: this.name,
|
|
3836
|
+
}));
|
|
3837
|
+
}
|
|
3838
|
+
this.storage = callOrReturn(getExtensionField(this, 'addStorage', {
|
|
3839
|
+
name: this.name,
|
|
3840
|
+
options: this.options,
|
|
3841
|
+
})) || {};
|
|
2841
3842
|
}
|
|
2842
3843
|
static create(config = {}) {
|
|
2843
|
-
return new
|
|
3844
|
+
return new Mark(config);
|
|
2844
3845
|
}
|
|
2845
3846
|
configure(options = {}) {
|
|
2846
3847
|
// return a new instance so we can use the same extension
|
|
2847
3848
|
// with different calls of `configure`
|
|
2848
3849
|
const extension = this.extend();
|
|
2849
3850
|
extension.options = mergeDeep(this.options, options);
|
|
3851
|
+
extension.storage = callOrReturn(getExtensionField(extension, 'addStorage', {
|
|
3852
|
+
name: extension.name,
|
|
3853
|
+
options: extension.options,
|
|
3854
|
+
}));
|
|
2850
3855
|
return extension;
|
|
2851
3856
|
}
|
|
2852
3857
|
extend(extendedConfig = {}) {
|
|
2853
|
-
const extension = new
|
|
3858
|
+
const extension = new Mark(extendedConfig);
|
|
2854
3859
|
extension.parent = this;
|
|
2855
3860
|
this.child = extension;
|
|
2856
|
-
extension.name = extendedConfig.name
|
|
2857
|
-
|
|
2858
|
-
: extension.
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
: extension.
|
|
3861
|
+
extension.name = extendedConfig.name ? extendedConfig.name : extension.parent.name;
|
|
3862
|
+
if (extendedConfig.defaultOptions) {
|
|
3863
|
+
console.warn(`[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${extension.name}".`);
|
|
3864
|
+
}
|
|
3865
|
+
extension.options = callOrReturn(getExtensionField(extension, 'addOptions', {
|
|
3866
|
+
name: extension.name,
|
|
3867
|
+
}));
|
|
3868
|
+
extension.storage = callOrReturn(getExtensionField(extension, 'addStorage', {
|
|
3869
|
+
name: extension.name,
|
|
3870
|
+
options: extension.options,
|
|
3871
|
+
}));
|
|
2862
3872
|
return extension;
|
|
2863
3873
|
}
|
|
3874
|
+
static handleExit({ editor, mark }) {
|
|
3875
|
+
const { tr } = editor.state;
|
|
3876
|
+
const currentPos = editor.state.selection.$from;
|
|
3877
|
+
const isAtEnd = currentPos.pos === currentPos.end();
|
|
3878
|
+
if (isAtEnd) {
|
|
3879
|
+
const currentMarks = currentPos.marks();
|
|
3880
|
+
const isInMark = !!currentMarks.find(m => (m === null || m === void 0 ? void 0 : m.type.name) === mark.name);
|
|
3881
|
+
if (!isInMark) {
|
|
3882
|
+
return false;
|
|
3883
|
+
}
|
|
3884
|
+
const removeMark = currentMarks.find(m => (m === null || m === void 0 ? void 0 : m.type.name) === mark.name);
|
|
3885
|
+
if (removeMark) {
|
|
3886
|
+
tr.removeStoredMark(removeMark);
|
|
3887
|
+
}
|
|
3888
|
+
tr.insertText(' ', currentPos.pos);
|
|
3889
|
+
editor.view.dispatch(tr);
|
|
3890
|
+
return true;
|
|
3891
|
+
}
|
|
3892
|
+
return false;
|
|
3893
|
+
}
|
|
2864
3894
|
}
|
|
2865
3895
|
|
|
2866
|
-
class
|
|
3896
|
+
class Node {
|
|
2867
3897
|
constructor(config = {}) {
|
|
2868
|
-
this.type = '
|
|
2869
|
-
this.name = '
|
|
3898
|
+
this.type = 'node';
|
|
3899
|
+
this.name = 'node';
|
|
2870
3900
|
this.parent = null;
|
|
2871
3901
|
this.child = null;
|
|
2872
3902
|
this.config = {
|
|
@@ -2878,45 +3908,54 @@ class Mark {
|
|
|
2878
3908
|
...config,
|
|
2879
3909
|
};
|
|
2880
3910
|
this.name = this.config.name;
|
|
3911
|
+
if (config.defaultOptions) {
|
|
3912
|
+
console.warn(`[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${this.name}".`);
|
|
3913
|
+
}
|
|
3914
|
+
// TODO: remove `addOptions` fallback
|
|
2881
3915
|
this.options = this.config.defaultOptions;
|
|
3916
|
+
if (this.config.addOptions) {
|
|
3917
|
+
this.options = callOrReturn(getExtensionField(this, 'addOptions', {
|
|
3918
|
+
name: this.name,
|
|
3919
|
+
}));
|
|
3920
|
+
}
|
|
3921
|
+
this.storage = callOrReturn(getExtensionField(this, 'addStorage', {
|
|
3922
|
+
name: this.name,
|
|
3923
|
+
options: this.options,
|
|
3924
|
+
})) || {};
|
|
2882
3925
|
}
|
|
2883
3926
|
static create(config = {}) {
|
|
2884
|
-
return new
|
|
3927
|
+
return new Node(config);
|
|
2885
3928
|
}
|
|
2886
3929
|
configure(options = {}) {
|
|
2887
3930
|
// return a new instance so we can use the same extension
|
|
2888
3931
|
// with different calls of `configure`
|
|
2889
3932
|
const extension = this.extend();
|
|
2890
3933
|
extension.options = mergeDeep(this.options, options);
|
|
3934
|
+
extension.storage = callOrReturn(getExtensionField(extension, 'addStorage', {
|
|
3935
|
+
name: extension.name,
|
|
3936
|
+
options: extension.options,
|
|
3937
|
+
}));
|
|
2891
3938
|
return extension;
|
|
2892
3939
|
}
|
|
2893
3940
|
extend(extendedConfig = {}) {
|
|
2894
|
-
const extension = new
|
|
3941
|
+
const extension = new Node(extendedConfig);
|
|
2895
3942
|
extension.parent = this;
|
|
2896
3943
|
this.child = extension;
|
|
2897
|
-
extension.name = extendedConfig.name
|
|
2898
|
-
|
|
2899
|
-
: extension.
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
: extension.
|
|
3944
|
+
extension.name = extendedConfig.name ? extendedConfig.name : extension.parent.name;
|
|
3945
|
+
if (extendedConfig.defaultOptions) {
|
|
3946
|
+
console.warn(`[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${extension.name}".`);
|
|
3947
|
+
}
|
|
3948
|
+
extension.options = callOrReturn(getExtensionField(extension, 'addOptions', {
|
|
3949
|
+
name: extension.name,
|
|
3950
|
+
}));
|
|
3951
|
+
extension.storage = callOrReturn(getExtensionField(extension, 'addStorage', {
|
|
3952
|
+
name: extension.name,
|
|
3953
|
+
options: extension.options,
|
|
3954
|
+
}));
|
|
2903
3955
|
return extension;
|
|
2904
3956
|
}
|
|
2905
3957
|
}
|
|
2906
3958
|
|
|
2907
|
-
function isiOS() {
|
|
2908
|
-
return [
|
|
2909
|
-
'iPad Simulator',
|
|
2910
|
-
'iPhone Simulator',
|
|
2911
|
-
'iPod Simulator',
|
|
2912
|
-
'iPad',
|
|
2913
|
-
'iPhone',
|
|
2914
|
-
'iPod',
|
|
2915
|
-
].includes(navigator.platform)
|
|
2916
|
-
// iPad on iOS 13 detection
|
|
2917
|
-
|| (navigator.userAgent.includes('Mac') && 'ontouchend' in document);
|
|
2918
|
-
}
|
|
2919
|
-
|
|
2920
3959
|
class NodeView {
|
|
2921
3960
|
constructor(component, props, options) {
|
|
2922
3961
|
this.isDragging = false;
|
|
@@ -2938,13 +3977,13 @@ class NodeView {
|
|
|
2938
3977
|
return;
|
|
2939
3978
|
}
|
|
2940
3979
|
get dom() {
|
|
2941
|
-
return
|
|
3980
|
+
return this.editor.view.dom;
|
|
2942
3981
|
}
|
|
2943
3982
|
get contentDOM() {
|
|
2944
3983
|
return null;
|
|
2945
3984
|
}
|
|
2946
3985
|
onDragStart(event) {
|
|
2947
|
-
var _a, _b, _c;
|
|
3986
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
2948
3987
|
const { view } = this.editor;
|
|
2949
3988
|
const target = event.target;
|
|
2950
3989
|
// get the drag handle element
|
|
@@ -2952,9 +3991,7 @@ class NodeView {
|
|
|
2952
3991
|
const dragHandle = target.nodeType === 3
|
|
2953
3992
|
? (_a = target.parentElement) === null || _a === void 0 ? void 0 : _a.closest('[data-drag-handle]')
|
|
2954
3993
|
: target.closest('[data-drag-handle]');
|
|
2955
|
-
if (!this.dom
|
|
2956
|
-
|| ((_b = this.contentDOM) === null || _b === void 0 ? void 0 : _b.contains(target))
|
|
2957
|
-
|| !dragHandle) {
|
|
3994
|
+
if (!this.dom || ((_b = this.contentDOM) === null || _b === void 0 ? void 0 : _b.contains(target)) || !dragHandle) {
|
|
2958
3995
|
return;
|
|
2959
3996
|
}
|
|
2960
3997
|
let x = 0;
|
|
@@ -2963,10 +4000,13 @@ class NodeView {
|
|
|
2963
4000
|
if (this.dom !== dragHandle) {
|
|
2964
4001
|
const domBox = this.dom.getBoundingClientRect();
|
|
2965
4002
|
const handleBox = dragHandle.getBoundingClientRect();
|
|
2966
|
-
|
|
2967
|
-
|
|
4003
|
+
// In React, we have to go through nativeEvent to reach offsetX/offsetY.
|
|
4004
|
+
const offsetX = (_c = event.offsetX) !== null && _c !== void 0 ? _c : (_d = event.nativeEvent) === null || _d === void 0 ? void 0 : _d.offsetX;
|
|
4005
|
+
const offsetY = (_e = event.offsetY) !== null && _e !== void 0 ? _e : (_f = event.nativeEvent) === null || _f === void 0 ? void 0 : _f.offsetY;
|
|
4006
|
+
x = handleBox.x - domBox.x + offsetX;
|
|
4007
|
+
y = handleBox.y - domBox.y + offsetY;
|
|
2968
4008
|
}
|
|
2969
|
-
(
|
|
4009
|
+
(_g = event.dataTransfer) === null || _g === void 0 ? void 0 : _g.setDragImage(this.dom, x, y);
|
|
2970
4010
|
// we need to tell ProseMirror that we want to move the whole node
|
|
2971
4011
|
// so we create a NodeSelection
|
|
2972
4012
|
const selection = NodeSelection.create(view.state.doc, this.getPos());
|
|
@@ -2987,10 +4027,11 @@ class NodeView {
|
|
|
2987
4027
|
if (!isInElement) {
|
|
2988
4028
|
return false;
|
|
2989
4029
|
}
|
|
2990
|
-
const
|
|
2991
|
-
|
|
4030
|
+
const isDragEvent = event.type.startsWith('drag');
|
|
4031
|
+
const isDropEvent = event.type === 'drop';
|
|
4032
|
+
const isInput = ['INPUT', 'BUTTON', 'SELECT', 'TEXTAREA'].includes(target.tagName) || target.isContentEditable;
|
|
2992
4033
|
// any input event within node views should be ignored by ProseMirror
|
|
2993
|
-
if (isInput) {
|
|
4034
|
+
if (isInput && !isDropEvent && !isDragEvent) {
|
|
2994
4035
|
return true;
|
|
2995
4036
|
}
|
|
2996
4037
|
const { isEditable } = this.editor;
|
|
@@ -3001,7 +4042,6 @@ class NodeView {
|
|
|
3001
4042
|
const isPasteEvent = event.type === 'paste';
|
|
3002
4043
|
const isCutEvent = event.type === 'cut';
|
|
3003
4044
|
const isClickEvent = event.type === 'mousedown';
|
|
3004
|
-
const isDragEvent = event.type.startsWith('drag') || event.type === 'drop';
|
|
3005
4045
|
// ProseMirror tries to drag selectable nodes
|
|
3006
4046
|
// even if `draggable` is set to `false`
|
|
3007
4047
|
// this fix prevents that
|
|
@@ -3015,13 +4055,15 @@ class NodeView {
|
|
|
3015
4055
|
// we have to store that dragging started
|
|
3016
4056
|
if (isDraggable && isEditable && !isDragging && isClickEvent) {
|
|
3017
4057
|
const dragHandle = target.closest('[data-drag-handle]');
|
|
3018
|
-
const isValidDragHandle = dragHandle
|
|
3019
|
-
&& (this.dom === dragHandle || (this.dom.contains(dragHandle)));
|
|
4058
|
+
const isValidDragHandle = dragHandle && (this.dom === dragHandle || this.dom.contains(dragHandle));
|
|
3020
4059
|
if (isValidDragHandle) {
|
|
3021
4060
|
this.isDragging = true;
|
|
3022
4061
|
document.addEventListener('dragend', () => {
|
|
3023
4062
|
this.isDragging = false;
|
|
3024
4063
|
}, { once: true });
|
|
4064
|
+
document.addEventListener('drop', () => {
|
|
4065
|
+
this.isDragging = false;
|
|
4066
|
+
}, { once: true });
|
|
3025
4067
|
document.addEventListener('mouseup', () => {
|
|
3026
4068
|
this.isDragging = false;
|
|
3027
4069
|
}, { once: true });
|
|
@@ -3029,6 +4071,7 @@ class NodeView {
|
|
|
3029
4071
|
}
|
|
3030
4072
|
// these events are handled by prosemirror
|
|
3031
4073
|
if (isDragging
|
|
4074
|
+
|| isDropEvent
|
|
3032
4075
|
|| isCopyEvent
|
|
3033
4076
|
|| isPasteEvent
|
|
3034
4077
|
|| isCutEvent
|
|
@@ -3057,7 +4100,10 @@ class NodeView {
|
|
|
3057
4100
|
// this is because ProseMirror can’t preventDispatch on enter
|
|
3058
4101
|
// this will lead to a re-render of the node view on enter
|
|
3059
4102
|
// see: https://github.com/ueberdosis/tiptap/issues/1214
|
|
3060
|
-
if (this.dom.contains(mutation.target)
|
|
4103
|
+
if (this.dom.contains(mutation.target)
|
|
4104
|
+
&& mutation.type === 'childList'
|
|
4105
|
+
&& isiOS()
|
|
4106
|
+
&& this.editor.isFocused) {
|
|
3061
4107
|
const changedNodes = [
|
|
3062
4108
|
...Array.from(mutation.addedNodes),
|
|
3063
4109
|
...Array.from(mutation.removedNodes),
|
|
@@ -3096,273 +4142,129 @@ class NodeView {
|
|
|
3096
4142
|
}
|
|
3097
4143
|
}
|
|
3098
4144
|
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
const mapResult = step
|
|
3110
|
-
.getMap()
|
|
3111
|
-
.mapResult(newPosition);
|
|
3112
|
-
if (mapResult.deleted) {
|
|
3113
|
-
deleted = true;
|
|
3114
|
-
}
|
|
3115
|
-
return mapResult.pos;
|
|
3116
|
-
}, position);
|
|
3117
|
-
return {
|
|
3118
|
-
position: mappedPosition,
|
|
3119
|
-
deleted,
|
|
3120
|
-
};
|
|
3121
|
-
}
|
|
3122
|
-
}
|
|
3123
|
-
|
|
3124
|
-
function nodeInputRule (regexp, type, getAttributes) {
|
|
3125
|
-
return new InputRule(regexp, (state, match, start, end) => {
|
|
3126
|
-
const attributes = getAttributes instanceof Function
|
|
3127
|
-
? getAttributes(match)
|
|
3128
|
-
: getAttributes;
|
|
3129
|
-
const { tr } = state;
|
|
3130
|
-
if (match[0]) {
|
|
3131
|
-
tr.replaceWith(start - 1, end, type.create(attributes));
|
|
3132
|
-
}
|
|
3133
|
-
return tr;
|
|
3134
|
-
});
|
|
3135
|
-
}
|
|
3136
|
-
|
|
3137
|
-
function getMarksBetween(from, to, state) {
|
|
3138
|
-
let marks = [];
|
|
3139
|
-
state.doc.nodesBetween(from, to, (node, pos) => {
|
|
3140
|
-
marks = [...marks, ...node.marks.map(mark => ({
|
|
3141
|
-
from: pos,
|
|
3142
|
-
to: pos + node.nodeSize,
|
|
3143
|
-
mark,
|
|
3144
|
-
}))];
|
|
3145
|
-
});
|
|
3146
|
-
return marks;
|
|
3147
|
-
}
|
|
3148
|
-
|
|
3149
|
-
function markInputRule (regexp, markType, getAttributes) {
|
|
3150
|
-
return new InputRule(regexp, (state, match, start, end) => {
|
|
3151
|
-
const attributes = getAttributes instanceof Function
|
|
3152
|
-
? getAttributes(match)
|
|
3153
|
-
: getAttributes;
|
|
3154
|
-
const { tr } = state;
|
|
3155
|
-
const captureGroup = match[match.length - 1];
|
|
3156
|
-
const fullMatch = match[0];
|
|
3157
|
-
let markEnd = end;
|
|
3158
|
-
if (captureGroup) {
|
|
3159
|
-
const startSpaces = fullMatch.search(/\S/);
|
|
3160
|
-
const textStart = start + fullMatch.indexOf(captureGroup);
|
|
3161
|
-
const textEnd = textStart + captureGroup.length;
|
|
3162
|
-
const excludedMarks = getMarksBetween(start, end, state)
|
|
3163
|
-
.filter(item => {
|
|
3164
|
-
// TODO: PR to add excluded to MarkType
|
|
3165
|
-
// @ts-ignore
|
|
3166
|
-
const { excluded } = item.mark.type;
|
|
3167
|
-
return excluded.find((type) => type.name === markType.name);
|
|
3168
|
-
})
|
|
3169
|
-
.filter(item => item.to > textStart);
|
|
3170
|
-
if (excludedMarks.length) {
|
|
4145
|
+
/**
|
|
4146
|
+
* Build an paste rule that adds a mark when the
|
|
4147
|
+
* matched text is pasted into it.
|
|
4148
|
+
*/
|
|
4149
|
+
function markPasteRule(config) {
|
|
4150
|
+
return new PasteRule({
|
|
4151
|
+
find: config.find,
|
|
4152
|
+
handler: ({ state, range, match }) => {
|
|
4153
|
+
const attributes = callOrReturn(config.getAttributes, undefined, match);
|
|
4154
|
+
if (attributes === false || attributes === null) {
|
|
3171
4155
|
return null;
|
|
3172
4156
|
}
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
fragment.forEach(child => {
|
|
3191
|
-
if (child.isText && child.text) {
|
|
3192
|
-
const { text } = child;
|
|
3193
|
-
let pos = 0;
|
|
3194
|
-
let match;
|
|
3195
|
-
// eslint-disable-next-line
|
|
3196
|
-
while ((match = regexp.exec(text)) !== null) {
|
|
3197
|
-
const outerMatch = Math.max(match.length - 2, 0);
|
|
3198
|
-
const innerMatch = Math.max(match.length - 1, 0);
|
|
3199
|
-
if (parent === null || parent === void 0 ? void 0 : parent.type.allowsMarkType(type)) {
|
|
3200
|
-
const start = match.index;
|
|
3201
|
-
const matchStart = start + match[0].indexOf(match[outerMatch]);
|
|
3202
|
-
const matchEnd = matchStart + match[outerMatch].length;
|
|
3203
|
-
const textStart = matchStart + match[outerMatch].lastIndexOf(match[innerMatch]);
|
|
3204
|
-
const textEnd = textStart + match[innerMatch].length;
|
|
3205
|
-
const attrs = getAttributes instanceof Function
|
|
3206
|
-
? getAttributes(match)
|
|
3207
|
-
: getAttributes;
|
|
3208
|
-
if (!attrs && attrs !== undefined) {
|
|
3209
|
-
continue;
|
|
3210
|
-
}
|
|
3211
|
-
// adding text before markdown to nodes
|
|
3212
|
-
if (matchStart > 0) {
|
|
3213
|
-
nodes.push(child.cut(pos, matchStart));
|
|
3214
|
-
}
|
|
3215
|
-
// adding the markdown part to nodes
|
|
3216
|
-
nodes.push(child
|
|
3217
|
-
.cut(textStart, textEnd)
|
|
3218
|
-
.mark(type.create(attrs).addToSet(child.marks)));
|
|
3219
|
-
pos = matchEnd;
|
|
3220
|
-
}
|
|
4157
|
+
const { tr } = state;
|
|
4158
|
+
const captureGroup = match[match.length - 1];
|
|
4159
|
+
const fullMatch = match[0];
|
|
4160
|
+
let markEnd = range.to;
|
|
4161
|
+
if (captureGroup) {
|
|
4162
|
+
const startSpaces = fullMatch.search(/\S/);
|
|
4163
|
+
const textStart = range.from + fullMatch.indexOf(captureGroup);
|
|
4164
|
+
const textEnd = textStart + captureGroup.length;
|
|
4165
|
+
const excludedMarks = getMarksBetween(range.from, range.to, state.doc)
|
|
4166
|
+
.filter(item => {
|
|
4167
|
+
// @ts-ignore
|
|
4168
|
+
const excluded = item.mark.type.excluded;
|
|
4169
|
+
return excluded.find(type => type === config.type && type !== item.mark.type);
|
|
4170
|
+
})
|
|
4171
|
+
.filter(item => item.to > textStart);
|
|
4172
|
+
if (excludedMarks.length) {
|
|
4173
|
+
return null;
|
|
3221
4174
|
}
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
nodes.push(child.cut(pos));
|
|
4175
|
+
if (textEnd < range.to) {
|
|
4176
|
+
tr.delete(textEnd, range.to);
|
|
3225
4177
|
}
|
|
4178
|
+
if (textStart > range.from) {
|
|
4179
|
+
tr.delete(range.from + startSpaces, textStart);
|
|
4180
|
+
}
|
|
4181
|
+
markEnd = range.from + startSpaces + captureGroup.length;
|
|
4182
|
+
tr.addMark(range.from + startSpaces, markEnd, config.type.create(attributes || {}));
|
|
4183
|
+
tr.removeStoredMark(config.type);
|
|
3226
4184
|
}
|
|
3227
|
-
else {
|
|
3228
|
-
nodes.push(child.copy(handler(child.content, child)));
|
|
3229
|
-
}
|
|
3230
|
-
});
|
|
3231
|
-
return Fragment.fromArray(nodes);
|
|
3232
|
-
};
|
|
3233
|
-
return new Plugin({
|
|
3234
|
-
key: new PluginKey('markPasteRule'),
|
|
3235
|
-
props: {
|
|
3236
|
-
transformPasted: slice => {
|
|
3237
|
-
return new Slice(handler(slice.content), slice.openStart, slice.openEnd);
|
|
3238
|
-
},
|
|
3239
4185
|
},
|
|
3240
4186
|
});
|
|
3241
4187
|
}
|
|
3242
4188
|
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
});
|
|
3251
|
-
}
|
|
3252
|
-
});
|
|
3253
|
-
return nodesWithPos;
|
|
4189
|
+
// source: https://stackoverflow.com/a/6969486
|
|
4190
|
+
function escapeForRegEx(string) {
|
|
4191
|
+
return string.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
|
|
4192
|
+
}
|
|
4193
|
+
|
|
4194
|
+
function isString(value) {
|
|
4195
|
+
return typeof value === 'string';
|
|
3254
4196
|
}
|
|
3255
4197
|
|
|
3256
4198
|
/**
|
|
3257
|
-
*
|
|
4199
|
+
* Build an paste rule that adds a node when the
|
|
4200
|
+
* matched text is pasted into it.
|
|
3258
4201
|
*/
|
|
3259
|
-
function
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
pos,
|
|
3275
|
-
});
|
|
3276
|
-
}
|
|
4202
|
+
function nodePasteRule(config) {
|
|
4203
|
+
return new PasteRule({
|
|
4204
|
+
find: config.find,
|
|
4205
|
+
handler({ match, chain, range }) {
|
|
4206
|
+
const attributes = callOrReturn(config.getAttributes, undefined, match);
|
|
4207
|
+
if (attributes === false || attributes === null) {
|
|
4208
|
+
return null;
|
|
4209
|
+
}
|
|
4210
|
+
if (match.input) {
|
|
4211
|
+
chain().deleteRange(range).insertContentAt(range.from, {
|
|
4212
|
+
type: config.type.name,
|
|
4213
|
+
attrs: attributes,
|
|
4214
|
+
});
|
|
4215
|
+
}
|
|
4216
|
+
},
|
|
3277
4217
|
});
|
|
3278
|
-
return nodesWithPos;
|
|
3279
|
-
}
|
|
3280
|
-
|
|
3281
|
-
function getSchema(extensions) {
|
|
3282
|
-
const resolvedExtensions = ExtensionManager.resolve(extensions);
|
|
3283
|
-
return getSchemaByResolvedExtensions(resolvedExtensions);
|
|
3284
|
-
}
|
|
3285
|
-
|
|
3286
|
-
function generateHTML(doc, extensions) {
|
|
3287
|
-
const schema = getSchema(extensions);
|
|
3288
|
-
const contentNode = Node$1.fromJSON(schema, doc);
|
|
3289
|
-
return getHTMLFromFragment(contentNode, schema);
|
|
3290
|
-
}
|
|
3291
|
-
|
|
3292
|
-
function generateJSON(html, extensions) {
|
|
3293
|
-
const schema = getSchema(extensions);
|
|
3294
|
-
const dom = elementFromString(html);
|
|
3295
|
-
return DOMParser.fromSchema(schema)
|
|
3296
|
-
.parse(dom)
|
|
3297
|
-
.toJSON();
|
|
3298
4218
|
}
|
|
3299
4219
|
|
|
3300
4220
|
/**
|
|
3301
|
-
*
|
|
4221
|
+
* Build an paste rule that replaces text when the
|
|
4222
|
+
* matched text is pasted into it.
|
|
3302
4223
|
*/
|
|
3303
|
-
function
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
}
|
|
3322
|
-
if (marks.length) {
|
|
3323
|
-
output.marks = marks;
|
|
3324
|
-
}
|
|
3325
|
-
if (content.length) {
|
|
3326
|
-
output.content = content;
|
|
3327
|
-
}
|
|
3328
|
-
if (n.text) {
|
|
3329
|
-
output.text = n.text;
|
|
3330
|
-
}
|
|
3331
|
-
nodes.push(output);
|
|
4224
|
+
function textPasteRule(config) {
|
|
4225
|
+
return new PasteRule({
|
|
4226
|
+
find: config.find,
|
|
4227
|
+
handler: ({ state, range, match }) => {
|
|
4228
|
+
let insert = config.replace;
|
|
4229
|
+
let start = range.from;
|
|
4230
|
+
const end = range.to;
|
|
4231
|
+
if (match[1]) {
|
|
4232
|
+
const offset = match[0].lastIndexOf(match[1]);
|
|
4233
|
+
insert += match[0].slice(offset + match[1].length);
|
|
4234
|
+
start += offset;
|
|
4235
|
+
const cutOff = start - end;
|
|
4236
|
+
if (cutOff > 0) {
|
|
4237
|
+
insert = match[0].slice(offset - cutOff, offset) + insert;
|
|
4238
|
+
start = end;
|
|
4239
|
+
}
|
|
4240
|
+
}
|
|
4241
|
+
state.tr.insertText(insert, start, end);
|
|
4242
|
+
},
|
|
3332
4243
|
});
|
|
3333
|
-
return nodes;
|
|
3334
|
-
}
|
|
3335
|
-
|
|
3336
|
-
function isNodeSelection(value) {
|
|
3337
|
-
return isObject(value) && value instanceof NodeSelection;
|
|
3338
4244
|
}
|
|
3339
4245
|
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
}
|
|
3361
|
-
return {
|
|
3362
|
-
...data,
|
|
3363
|
-
toJSON: () => data,
|
|
3364
|
-
};
|
|
4246
|
+
class Tracker {
|
|
4247
|
+
constructor(transaction) {
|
|
4248
|
+
this.transaction = transaction;
|
|
4249
|
+
this.currentStep = this.transaction.steps.length;
|
|
4250
|
+
}
|
|
4251
|
+
map(position) {
|
|
4252
|
+
let deleted = false;
|
|
4253
|
+
const mappedPosition = this.transaction.steps
|
|
4254
|
+
.slice(this.currentStep)
|
|
4255
|
+
.reduce((newPosition, step) => {
|
|
4256
|
+
const mapResult = step.getMap().mapResult(newPosition);
|
|
4257
|
+
if (mapResult.deleted) {
|
|
4258
|
+
deleted = true;
|
|
4259
|
+
}
|
|
4260
|
+
return mapResult.pos;
|
|
4261
|
+
}, position);
|
|
4262
|
+
return {
|
|
4263
|
+
position: mappedPosition,
|
|
4264
|
+
deleted,
|
|
4265
|
+
};
|
|
4266
|
+
}
|
|
3365
4267
|
}
|
|
3366
4268
|
|
|
3367
|
-
export { Editor, Extension, Mark, Node, NodeView, Tracker, callOrReturn, extensions, findChildren, findChildrenInRange, findParentNode, findParentNodeClosestToPos, generateHTML, generateJSON, getAttributes, getDebugJSON, getExtensionField, getHTMLFromFragment, getMarkAttributes, getMarkRange, getMarkType, getMarksBetween, getNodeAttributes, getNodeType, getSchema, isActive, isList, isMarkActive, isNodeActive, isNodeEmpty, isNodeSelection, isTextSelection, markInputRule, markPasteRule, mergeAttributes, nodeInputRule, posToDOMRect };
|
|
3368
|
-
//# sourceMappingURL=
|
|
4269
|
+
export { CommandManager, Editor, Extension, InputRule, Mark, Node, NodeView, PasteRule, Tracker, callOrReturn, combineTransactionSteps, createChainableState, createDocument, createNodeFromContent, createStyleTag, defaultBlockAt, deleteProps, elementFromString, escapeForRegEx, extensions, findChildren, findChildrenInRange, findDuplicates, findParentNode, findParentNodeClosestToPos, fromString, generateHTML, generateJSON, generateText, getAttributes, getAttributesFromExtensions, getChangedRanges, getDebugJSON, getExtensionField, getHTMLFromFragment, getMarkAttributes, getMarkRange, getMarkType, getMarksBetween, getNodeAttributes, getNodeType, getRenderedAttributes, getSchema, getSchemaByResolvedExtensions, getSchemaTypeByName, getSchemaTypeNameByName, getSplittedAttributes, getText, getTextBetween, getTextContentFromNodes, getTextSerializersFromSchema, injectExtensionAttributesToParseRule, inputRulesPlugin, isActive, isEmptyObject, isExtensionRulesEnabled, isFunction, isList, isMacOS, isMarkActive, isNodeActive, isNodeEmpty, isNodeSelection, isNumber, isPlainObject, isRegExp, isString, isTextSelection, isiOS, markInputRule, markPasteRule, mergeAttributes, mergeDeep, minMax, nodeInputRule, nodePasteRule, objectIncludes, pasteRulesPlugin, posToDOMRect, removeDuplicates, resolveFocusPosition, selectionToInsertionEnd, splitExtensions, textInputRule, textPasteRule, textblockTypeInputRule, wrappingInputRule };
|
|
4270
|
+
//# sourceMappingURL=index.js.map
|