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