decap-cms-widget-markdown 3.9.0 → 3.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/dist/decap-cms-widget-markdown.js +1 -1
- package/dist/decap-cms-widget-markdown.js.map +1 -1
- package/package.json +3 -11
- package/src/MarkdownControl/plugins/BreakToDefaultBlock.js +0 -23
- package/src/MarkdownControl/plugins/CloseBlock.js +0 -25
- package/src/MarkdownControl/plugins/CommandsAndQueries.js +0 -158
- package/src/MarkdownControl/plugins/ForceInsert.js +0 -38
- package/src/MarkdownControl/plugins/Hotkey.js +0 -29
- package/src/MarkdownControl/plugins/LineBreak.js +0 -15
- package/src/MarkdownControl/plugins/Link.js +0 -40
- package/src/MarkdownControl/plugins/lists/locations/isCursorInItemContainingNestedList.js +0 -9
- package/src/MarkdownControl/plugins/util.js +0 -12
- package/src/serializers/remarkImagesToText.js +0 -26
- package/src/types.js +0 -3
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "decap-cms-widget-markdown",
|
|
3
3
|
"description": "Widget for editing markdown in Decap CMS.",
|
|
4
|
-
"version": "3.
|
|
4
|
+
"version": "3.10.1",
|
|
5
5
|
"homepage": "https://www.decapcms.org/docs/widgets/#markdown",
|
|
6
6
|
"repository": "https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-markdown",
|
|
7
7
|
"bugs": "https://github.com/decaporg/decap-cms/issues",
|
|
@@ -21,10 +21,8 @@
|
|
|
21
21
|
"build:esm": "cross-env NODE_ENV=esm babel src --out-dir dist/esm --ignore \"**/__tests__\" --root-mode upward"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"detab": "^2.0.4",
|
|
25
24
|
"dompurify": "^3.4.0",
|
|
26
25
|
"is-hotkey": "^0.2.0",
|
|
27
|
-
"is-url": "^1.2.4",
|
|
28
26
|
"mdast-util-definitions": "^1.2.3",
|
|
29
27
|
"mdast-util-to-string": "^1.0.5",
|
|
30
28
|
"rehype-parse": "^6.0.0",
|
|
@@ -33,21 +31,15 @@
|
|
|
33
31
|
"rehype-stringify": "^7.0.0",
|
|
34
32
|
"remark-parse": "^6.0.3",
|
|
35
33
|
"remark-rehype": "^4.0.0",
|
|
36
|
-
"remark-slate": "^1.8.6",
|
|
37
|
-
"remark-slate-transformer": "^0.7.4",
|
|
38
34
|
"remark-stringify": "^6.0.4",
|
|
39
35
|
"slate": "^0.118.1",
|
|
40
|
-
"slate-base64-serializer": "^0.2.107",
|
|
41
36
|
"slate-dom": "^0.118.1",
|
|
42
37
|
"slate-history": "^0.113.1",
|
|
43
38
|
"slate-hyperscript": "^0.100.0",
|
|
44
|
-
"slate-plain-serializer": "^0.7.3",
|
|
45
39
|
"slate-react": "^0.117.4",
|
|
46
|
-
"slate-soft-break": "^0.9.0",
|
|
47
40
|
"unified": "^9.2.0",
|
|
48
41
|
"unist-builder": "^1.0.3",
|
|
49
|
-
"unist-util-visit-parents": "^2.0.1"
|
|
50
|
-
"vfile-location": "^2.0.6"
|
|
42
|
+
"unist-util-visit-parents": "^2.0.1"
|
|
51
43
|
},
|
|
52
44
|
"peerDependencies": {
|
|
53
45
|
"@emotion/react": "^11.11.1",
|
|
@@ -64,5 +56,5 @@
|
|
|
64
56
|
"commonmark": "^0.30.0",
|
|
65
57
|
"commonmark-spec": "^0.30.0"
|
|
66
58
|
},
|
|
67
|
-
"gitHead": "
|
|
59
|
+
"gitHead": "505b6a428bfc5ab6f114aa939654a81cb1525ea6"
|
|
68
60
|
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import isHotkey from 'is-hotkey';
|
|
2
|
-
|
|
3
|
-
function BreakToDefaultBlock({ defaultType }) {
|
|
4
|
-
return {
|
|
5
|
-
onKeyDown(event, editor, next) {
|
|
6
|
-
const { selection, startBlock } = editor.value;
|
|
7
|
-
const isEnter = isHotkey('enter', event);
|
|
8
|
-
if (!isEnter) {
|
|
9
|
-
return next();
|
|
10
|
-
}
|
|
11
|
-
if (selection.isExpanded) {
|
|
12
|
-
editor.delete();
|
|
13
|
-
return next();
|
|
14
|
-
}
|
|
15
|
-
if (selection.start.isAtEndOfNode(startBlock) && startBlock.type !== defaultType) {
|
|
16
|
-
return editor.insertBlock(defaultType);
|
|
17
|
-
}
|
|
18
|
-
return next();
|
|
19
|
-
},
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export default BreakToDefaultBlock;
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import isHotkey from 'is-hotkey';
|
|
2
|
-
|
|
3
|
-
function CloseBlock({ defaultType }) {
|
|
4
|
-
return {
|
|
5
|
-
onKeyDown(event, editor, next) {
|
|
6
|
-
const { selection, startBlock } = editor.value;
|
|
7
|
-
const isBackspace = isHotkey('backspace', event);
|
|
8
|
-
if (!isBackspace) {
|
|
9
|
-
return next();
|
|
10
|
-
}
|
|
11
|
-
if (selection.isExpanded) {
|
|
12
|
-
return editor.delete();
|
|
13
|
-
}
|
|
14
|
-
if (!selection.start.isAtStartOfNode(startBlock) || startBlock.text.length > 0) {
|
|
15
|
-
return next();
|
|
16
|
-
}
|
|
17
|
-
if (startBlock.type !== defaultType) {
|
|
18
|
-
editor.setBlocks(defaultType);
|
|
19
|
-
}
|
|
20
|
-
return next();
|
|
21
|
-
},
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export default CloseBlock;
|
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
import isArray from 'lodash/isArray';
|
|
2
|
-
import tail from 'lodash/tail';
|
|
3
|
-
import castArray from 'lodash/castArray';
|
|
4
|
-
|
|
5
|
-
function CommandsAndQueries({ defaultType }) {
|
|
6
|
-
return {
|
|
7
|
-
queries: {
|
|
8
|
-
atStartOf(editor, node) {
|
|
9
|
-
const { selection } = editor.value;
|
|
10
|
-
return selection.isCollapsed && selection.start.isAtStartOfNode(node);
|
|
11
|
-
},
|
|
12
|
-
getAncestor(editor, firstKey, lastKey) {
|
|
13
|
-
if (firstKey === lastKey) {
|
|
14
|
-
return editor.value.document.getParent(firstKey);
|
|
15
|
-
}
|
|
16
|
-
return editor.value.document.getCommonAncestor(firstKey, lastKey);
|
|
17
|
-
},
|
|
18
|
-
getOffset(editor, node) {
|
|
19
|
-
const parent = editor.value.document.getParent(node.key);
|
|
20
|
-
return parent.nodes.indexOf(node);
|
|
21
|
-
},
|
|
22
|
-
getSelectedChildren(editor, node) {
|
|
23
|
-
return node.nodes.filter(child => editor.isSelected(child));
|
|
24
|
-
},
|
|
25
|
-
getCommonAncestor(editor) {
|
|
26
|
-
const { startBlock, endBlock, document: doc } = editor.value;
|
|
27
|
-
return doc.getCommonAncestor(startBlock.key, endBlock.key);
|
|
28
|
-
},
|
|
29
|
-
getClosestType(editor, node, type) {
|
|
30
|
-
const types = castArray(type);
|
|
31
|
-
return editor.value.document.getClosest(node.key, n => types.includes(n.type));
|
|
32
|
-
},
|
|
33
|
-
getBlockContainer(editor, node) {
|
|
34
|
-
const targetTypes = ['bulleted-list', 'numbered-list', 'list-item', 'quote', 'table-cell'];
|
|
35
|
-
const { startBlock, selection } = editor.value;
|
|
36
|
-
const target = node
|
|
37
|
-
? editor.value.document.getParent(node.key)
|
|
38
|
-
: (selection.isCollapsed && startBlock) || editor.getCommonAncestor();
|
|
39
|
-
if (!target) {
|
|
40
|
-
return editor.value.document;
|
|
41
|
-
}
|
|
42
|
-
if (targetTypes.includes(target.type)) {
|
|
43
|
-
return target;
|
|
44
|
-
}
|
|
45
|
-
return editor.getBlockContainer(target);
|
|
46
|
-
},
|
|
47
|
-
isSelected(editor, nodes) {
|
|
48
|
-
return castArray(nodes).every(node => {
|
|
49
|
-
return editor.value.document.isInRange(node.key, editor.value.selection);
|
|
50
|
-
});
|
|
51
|
-
},
|
|
52
|
-
isFirstChild(editor, node) {
|
|
53
|
-
return editor.value.document.getParent(node.key).nodes.first().key === node.key;
|
|
54
|
-
},
|
|
55
|
-
areSiblings(editor, nodes) {
|
|
56
|
-
if (!isArray(nodes) || nodes.length < 2) {
|
|
57
|
-
return true;
|
|
58
|
-
}
|
|
59
|
-
const parent = editor.value.document.getParent(nodes[0].key);
|
|
60
|
-
return tail(nodes).every(node => {
|
|
61
|
-
return editor.value.document.getParent(node.key).key === parent.key;
|
|
62
|
-
});
|
|
63
|
-
},
|
|
64
|
-
everyBlock(editor, type) {
|
|
65
|
-
return editor.value.blocks.every(block => block.type === type);
|
|
66
|
-
},
|
|
67
|
-
hasMark(editor, type) {
|
|
68
|
-
return editor.value.activeMarks.some(mark => mark.type === type);
|
|
69
|
-
},
|
|
70
|
-
hasBlock(editor, type) {
|
|
71
|
-
return editor.value.blocks.some(node => node.type === type);
|
|
72
|
-
},
|
|
73
|
-
hasInline(editor, type) {
|
|
74
|
-
return editor.value.inlines.some(node => node.type === type);
|
|
75
|
-
},
|
|
76
|
-
hasQuote(editor, quoteLabel) {
|
|
77
|
-
const { value } = editor;
|
|
78
|
-
const { document, blocks } = value;
|
|
79
|
-
return blocks.some(node => {
|
|
80
|
-
const { key: descendantNodeKey } = node;
|
|
81
|
-
/* When focusing a quote block, the actual block that gets the focus is the paragraph block whose parent is a `quote` block.
|
|
82
|
-
Hence, we need to get its parent and check if its type is `quote`. This parent will always be defined because every block in the editor
|
|
83
|
-
has a Document object as parent by default.
|
|
84
|
-
*/
|
|
85
|
-
const parent = document.getParent(descendantNodeKey);
|
|
86
|
-
return parent.type === quoteLabel;
|
|
87
|
-
});
|
|
88
|
-
},
|
|
89
|
-
hasListItems(editor, listType) {
|
|
90
|
-
const { value } = editor;
|
|
91
|
-
const { document, blocks } = value;
|
|
92
|
-
return blocks.some(node => {
|
|
93
|
-
const { key: lowestNodeKey } = node;
|
|
94
|
-
/* A list block has the following structure:
|
|
95
|
-
<ol>
|
|
96
|
-
<li>
|
|
97
|
-
<p>Coffee</p>
|
|
98
|
-
</li>
|
|
99
|
-
<li>
|
|
100
|
-
<p>Tea</p>
|
|
101
|
-
</li>
|
|
102
|
-
</ol>
|
|
103
|
-
*/
|
|
104
|
-
const parent = document.getParent(lowestNodeKey);
|
|
105
|
-
const grandparent = document.getParent(parent.key);
|
|
106
|
-
return parent.type === 'list-item' && grandparent?.type === listType;
|
|
107
|
-
});
|
|
108
|
-
},
|
|
109
|
-
},
|
|
110
|
-
commands: {
|
|
111
|
-
toggleBlock(editor, type) {
|
|
112
|
-
switch (type) {
|
|
113
|
-
case 'heading-one':
|
|
114
|
-
case 'heading-two':
|
|
115
|
-
case 'heading-three':
|
|
116
|
-
case 'heading-four':
|
|
117
|
-
case 'heading-five':
|
|
118
|
-
case 'heading-six':
|
|
119
|
-
return editor.setBlocks(editor.everyBlock(type) ? defaultType : type);
|
|
120
|
-
case 'quote':
|
|
121
|
-
return editor.toggleQuoteBlock();
|
|
122
|
-
case 'numbered-list':
|
|
123
|
-
case 'bulleted-list': {
|
|
124
|
-
return editor.toggleList(type);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
},
|
|
128
|
-
unwrapBlockChildren(editor, block) {
|
|
129
|
-
if (!block || block.object !== 'block') {
|
|
130
|
-
throw Error(`Expected block but received ${block}.`);
|
|
131
|
-
}
|
|
132
|
-
const index = editor.value.document.getPath(block.key).last();
|
|
133
|
-
const parent = editor.value.document.getParent(block.key);
|
|
134
|
-
editor.withoutNormalizing(() => {
|
|
135
|
-
block.nodes.forEach((node, idx) => {
|
|
136
|
-
editor.moveNodeByKey(node.key, parent.key, index + idx);
|
|
137
|
-
});
|
|
138
|
-
editor.removeNodeByKey(block.key);
|
|
139
|
-
});
|
|
140
|
-
},
|
|
141
|
-
unwrapNodeToDepth(editor, node, depth) {
|
|
142
|
-
let currentDepth = 0;
|
|
143
|
-
editor.withoutNormalizing(() => {
|
|
144
|
-
while (currentDepth < depth) {
|
|
145
|
-
editor.unwrapNodeByKey(node.key);
|
|
146
|
-
currentDepth += 1;
|
|
147
|
-
}
|
|
148
|
-
});
|
|
149
|
-
},
|
|
150
|
-
unwrapNodeFromAncestor(editor, node, ancestor) {
|
|
151
|
-
const depth = ancestor.getDepth(node.key);
|
|
152
|
-
editor.unwrapNodeToDepth(node, depth);
|
|
153
|
-
},
|
|
154
|
-
},
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
export default CommandsAndQueries;
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
function ForceInsert({ defaultType }) {
|
|
2
|
-
return {
|
|
3
|
-
queries: {
|
|
4
|
-
canInsertBeforeNode(editor, node) {
|
|
5
|
-
if (!editor.isVoid(node)) {
|
|
6
|
-
return true;
|
|
7
|
-
}
|
|
8
|
-
return !!editor.value.document.getPreviousSibling(node.key);
|
|
9
|
-
},
|
|
10
|
-
canInsertAfterNode(editor, node) {
|
|
11
|
-
if (!editor.isVoid(node)) {
|
|
12
|
-
return true;
|
|
13
|
-
}
|
|
14
|
-
const nextSibling = editor.value.document.getNextSibling(node.key);
|
|
15
|
-
return nextSibling && !editor.isVoid(nextSibling);
|
|
16
|
-
},
|
|
17
|
-
},
|
|
18
|
-
commands: {
|
|
19
|
-
forceInsertBeforeNode(editor, node) {
|
|
20
|
-
const block = { type: defaultType, object: 'block' };
|
|
21
|
-
const parent = editor.value.document.getParent(node.key);
|
|
22
|
-
return editor.insertNodeByKey(parent.key, 0, block).moveToStartOfNode(parent).focus();
|
|
23
|
-
},
|
|
24
|
-
forceInsertAfterNode(editor, node) {
|
|
25
|
-
return editor.moveToEndOfNode(node).insertBlock(defaultType).focus();
|
|
26
|
-
},
|
|
27
|
-
moveToEndOfDocument(editor) {
|
|
28
|
-
const lastBlock = editor.value.document.nodes.last();
|
|
29
|
-
if (editor.isVoid(lastBlock)) {
|
|
30
|
-
editor.insertBlock(defaultType);
|
|
31
|
-
}
|
|
32
|
-
return editor.moveToEndOfNode(lastBlock).focus();
|
|
33
|
-
},
|
|
34
|
-
},
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export default ForceInsert;
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import isHotkey from 'is-hotkey';
|
|
2
|
-
|
|
3
|
-
export const HOT_KEY_MAP = {
|
|
4
|
-
bold: 'mod+b',
|
|
5
|
-
code: 'mod+shift+c',
|
|
6
|
-
italic: 'mod+i',
|
|
7
|
-
strikethrough: 'mod+shift+s',
|
|
8
|
-
'heading-one': 'mod+1',
|
|
9
|
-
'heading-two': 'mod+2',
|
|
10
|
-
'heading-three': 'mod+3',
|
|
11
|
-
'heading-four': 'mod+4',
|
|
12
|
-
'heading-five': 'mod+5',
|
|
13
|
-
'heading-six': 'mod+6',
|
|
14
|
-
link: 'mod+k',
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
function Hotkey(key, fn) {
|
|
18
|
-
return {
|
|
19
|
-
onKeyDown(event, editor, next) {
|
|
20
|
-
if (!isHotkey(key, event)) {
|
|
21
|
-
return next();
|
|
22
|
-
}
|
|
23
|
-
event.preventDefault();
|
|
24
|
-
editor.command(fn);
|
|
25
|
-
},
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export default Hotkey;
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import isHotkey from 'is-hotkey';
|
|
2
|
-
|
|
3
|
-
function LineBreak() {
|
|
4
|
-
return {
|
|
5
|
-
onKeyDown(event, editor, next) {
|
|
6
|
-
const isShiftEnter = isHotkey('shift+enter', event);
|
|
7
|
-
if (!isShiftEnter) {
|
|
8
|
-
return next();
|
|
9
|
-
}
|
|
10
|
-
return editor.insertInline('break').insertText('').moveToStartOfNextText();
|
|
11
|
-
},
|
|
12
|
-
};
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export default LineBreak;
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
function Link({ type }) {
|
|
2
|
-
return {
|
|
3
|
-
commands: {
|
|
4
|
-
toggleLink(editor, getUrl) {
|
|
5
|
-
const selection = editor.value.selection;
|
|
6
|
-
const isCollapsed = selection && selection.isCollapsed;
|
|
7
|
-
|
|
8
|
-
if (editor.hasInline(type)) {
|
|
9
|
-
const inlines = editor.value.inlines.toJSON();
|
|
10
|
-
const link = inlines.find(item => item.type === type);
|
|
11
|
-
|
|
12
|
-
const url = getUrl(link.data.url);
|
|
13
|
-
|
|
14
|
-
if (url) {
|
|
15
|
-
// replace the old link
|
|
16
|
-
return editor.setInlines({ data: { url } });
|
|
17
|
-
} else {
|
|
18
|
-
// remove url if it was removed by the user
|
|
19
|
-
return editor.unwrapInline(type);
|
|
20
|
-
}
|
|
21
|
-
} else {
|
|
22
|
-
const url = getUrl();
|
|
23
|
-
if (!url) {
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
return isCollapsed
|
|
28
|
-
? editor.insertInline({
|
|
29
|
-
type,
|
|
30
|
-
data: { url },
|
|
31
|
-
nodes: [{ object: 'text', text: url }],
|
|
32
|
-
})
|
|
33
|
-
: editor.wrapInline({ type, data: { url } }).moveToEnd();
|
|
34
|
-
}
|
|
35
|
-
},
|
|
36
|
-
},
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export default Link;
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import getListContainedInListItem from '../selectors/getListContainedInListItem';
|
|
2
|
-
|
|
3
|
-
function isCursorInItemContainingNestedList(editor) {
|
|
4
|
-
const nestedList = getListContainedInListItem(editor);
|
|
5
|
-
|
|
6
|
-
return !!nestedList && `${nestedList[0].type}`.endsWith('-list');
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export default isCursorInItemContainingNestedList;
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import castArray from 'lodash/castArray';
|
|
2
|
-
import isArray from 'lodash/isArray';
|
|
3
|
-
|
|
4
|
-
export function assertType(nodes, type) {
|
|
5
|
-
const nodesArray = castArray(nodes);
|
|
6
|
-
const validate = isArray(type) ? node => type.includes(node.type) : node => type === node.type;
|
|
7
|
-
const invalidNode = nodesArray.find(node => !validate(node));
|
|
8
|
-
if (invalidNode) {
|
|
9
|
-
throw Error(`Expected node of type "${type}", received "${invalidNode.type}".`);
|
|
10
|
-
}
|
|
11
|
-
return true;
|
|
12
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Images must be parsed as shortcodes for asset proxying. This plugin converts
|
|
3
|
-
* MDAST image nodes back to text to allow shortcode pattern matching. Note that
|
|
4
|
-
* this transformation only occurs for images that are the sole child of a top
|
|
5
|
-
* level paragraph - any other image is left alone and treated as an inline
|
|
6
|
-
* image.
|
|
7
|
-
*/
|
|
8
|
-
export default function remarkImagesToText() {
|
|
9
|
-
return transform;
|
|
10
|
-
|
|
11
|
-
function transform(node) {
|
|
12
|
-
const children = node.children.map(child => {
|
|
13
|
-
if (
|
|
14
|
-
child.type === 'paragraph' &&
|
|
15
|
-
child.children.length === 1 &&
|
|
16
|
-
child.children[0].type === 'image'
|
|
17
|
-
) {
|
|
18
|
-
const { alt, url, title } = child.children[0];
|
|
19
|
-
const value = ``;
|
|
20
|
-
child.children = [{ type: 'text', value }];
|
|
21
|
-
}
|
|
22
|
-
return child;
|
|
23
|
-
});
|
|
24
|
-
return { ...node, children };
|
|
25
|
-
}
|
|
26
|
-
}
|
package/src/types.js
DELETED