@uiw/react-md-editor 3.8.3 → 3.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/esm/Context.d.ts +2 -0
- package/esm/Context.js.map +2 -2
- package/esm/Editor.d.ts +4 -0
- package/esm/Editor.js +3 -1
- package/esm/Editor.js.map +3 -2
- package/esm/commands/title1.js +5 -6
- package/esm/commands/title1.js.map +9 -5
- package/esm/commands/title2.js +5 -6
- package/esm/commands/title2.js.map +9 -5
- package/esm/commands/title3.js +5 -6
- package/esm/commands/title3.js.map +9 -5
- package/esm/commands/title4.js +5 -6
- package/esm/commands/title4.js.map +9 -5
- package/esm/commands/title5.js +5 -6
- package/esm/commands/title5.js.map +9 -5
- package/esm/commands/title6.js +5 -6
- package/esm/commands/title6.js.map +9 -5
- package/esm/components/TextArea/Markdown.js +1 -1
- package/esm/components/TextArea/Markdown.js.map +2 -2
- package/esm/components/TextArea/Textarea.js +2 -1
- package/esm/components/TextArea/Textarea.js.map +3 -2
- package/esm/components/TextArea/handleKeyDown.d.ts +1 -1
- package/esm/components/TextArea/handleKeyDown.js +6 -2
- package/esm/components/TextArea/handleKeyDown.js.map +3 -2
- package/esm/components/Toolbar/index.js +1 -1
- package/esm/components/Toolbar/index.js.map +2 -2
- package/esm/index.css +1 -1
- package/esm/index.less +1 -1
- package/esm/utils/InsertTextAtPosition.d.ts +7 -0
- package/esm/utils/InsertTextAtPosition.js +27 -1
- package/esm/utils/InsertTextAtPosition.js.map +14 -6
- package/lib/Context.d.ts +2 -0
- package/lib/Context.js.map +2 -2
- package/lib/Editor.d.ts +4 -0
- package/lib/Editor.js +4 -1
- package/lib/Editor.js.map +3 -2
- package/lib/commands/title1.js +6 -6
- package/lib/commands/title1.js.map +8 -5
- package/lib/commands/title2.js +6 -6
- package/lib/commands/title2.js.map +8 -5
- package/lib/commands/title3.js +6 -6
- package/lib/commands/title3.js.map +8 -5
- package/lib/commands/title4.js +6 -6
- package/lib/commands/title4.js.map +8 -5
- package/lib/commands/title5.js +6 -6
- package/lib/commands/title5.js.map +8 -5
- package/lib/commands/title6.js +6 -6
- package/lib/commands/title6.js.map +8 -5
- package/lib/components/TextArea/Markdown.js +1 -1
- package/lib/components/TextArea/Markdown.js.map +2 -2
- package/lib/components/TextArea/Textarea.js +2 -1
- package/lib/components/TextArea/Textarea.js.map +3 -2
- package/lib/components/TextArea/handleKeyDown.d.ts +1 -1
- package/lib/components/TextArea/handleKeyDown.js +2 -1
- package/lib/components/TextArea/handleKeyDown.js.map +3 -2
- package/lib/components/Toolbar/index.js +1 -1
- package/lib/components/Toolbar/index.js.map +2 -2
- package/lib/index.less +1 -1
- package/lib/utils/InsertTextAtPosition.d.ts +7 -0
- package/lib/utils/InsertTextAtPosition.js +30 -0
- package/lib/utils/InsertTextAtPosition.js.map +14 -6
- package/markdown-editor.css +1 -1
- package/package.json +5 -4
- package/src/Context.tsx +1 -0
- package/src/Editor.tsx +6 -1
- package/src/commands/title1.tsx +5 -4
- package/src/commands/title2.tsx +5 -4
- package/src/commands/title3.tsx +5 -4
- package/src/commands/title4.tsx +5 -4
- package/src/commands/title5.tsx +5 -4
- package/src/commands/title6.tsx +5 -4
- package/src/components/TextArea/Markdown.tsx +1 -1
- package/src/components/TextArea/Textarea.tsx +12 -3
- package/src/components/TextArea/handleKeyDown.tsx +3 -1
- package/src/components/Toolbar/index.tsx +1 -1
- package/src/index.less +1 -1
- package/src/utils/InsertTextAtPosition.ts +29 -2
|
@@ -13,9 +13,19 @@
|
|
|
13
13
|
"createElement",
|
|
14
14
|
"value",
|
|
15
15
|
"firstChild",
|
|
16
|
+
"insertAtLineStart",
|
|
17
|
+
"val",
|
|
18
|
+
"cursorIdx",
|
|
19
|
+
"content",
|
|
20
|
+
"startIdx",
|
|
21
|
+
"char",
|
|
22
|
+
"focus",
|
|
23
|
+
"setRangeText",
|
|
24
|
+
"dispatchEvent",
|
|
25
|
+
"Event",
|
|
26
|
+
"bubbles",
|
|
16
27
|
"insertTextAtPosition",
|
|
17
28
|
"text",
|
|
18
|
-
"focus",
|
|
19
29
|
"selection",
|
|
20
30
|
"ieRange",
|
|
21
31
|
"createRange",
|
|
@@ -27,7 +37,6 @@
|
|
|
27
37
|
"selectionStart",
|
|
28
38
|
"end",
|
|
29
39
|
"selectionEnd",
|
|
30
|
-
"setRangeText",
|
|
31
40
|
"range",
|
|
32
41
|
"textNode",
|
|
33
42
|
"createTextNode",
|
|
@@ -49,11 +58,10 @@
|
|
|
49
58
|
"setSelectionRange",
|
|
50
59
|
"e",
|
|
51
60
|
"createEvent",
|
|
52
|
-
"initEvent"
|
|
53
|
-
"dispatchEvent"
|
|
61
|
+
"initEvent"
|
|
54
62
|
],
|
|
55
|
-
"mappings": "
|
|
63
|
+
"mappings": "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",
|
|
56
64
|
"sourcesContent": [
|
|
57
|
-
"/**\n * The MIT License\n * Copyright (c) 2018 Dmitriy Kubyshkin\n * Copied from https://github.com/grassator/insert-text-at-cursor\n */\n\nlet browserSupportsTextareaTextNodes: any;\n\n/**\n * @param {HTMLElement} input\n * @return {boolean}\n */\nfunction canManipulateViaTextNodes(input: HTMLTextAreaElement | HTMLInputElement) {\n if (input.nodeName !== 'TEXTAREA') {\n return false;\n }\n if (typeof browserSupportsTextareaTextNodes === 'undefined') {\n const textarea: HTMLTextAreaElement = document.createElement('textarea');\n textarea.value = '1';\n browserSupportsTextareaTextNodes = !!textarea.firstChild;\n }\n return browserSupportsTextareaTextNodes;\n}\n\n/**\n * @param {HTMLTextAreaElement|HTMLInputElement} input\n * @param {string} text\n * @returns {void}\n */\nexport function insertTextAtPosition(input: HTMLTextAreaElement | HTMLInputElement, text: string) {\n // Most of the used APIs only work with the field selected\n input.focus();\n\n // IE 8-10\n if ((document as any).selection) {\n const ieRange = (document as any).selection.createRange();\n ieRange.text = text;\n\n // Move cursor after the inserted text\n ieRange.collapse(false /* to the end */);\n ieRange.select();\n\n return;\n }\n\n // Webkit + Edge\n const isSuccess = document.execCommand && document.execCommand('insertText', false, text);\n if (!isSuccess) {\n const start = input.selectionStart!;\n const end = input.selectionEnd!;\n // Firefox (non-standard method)\n if (typeof input.setRangeText === 'function') {\n input.setRangeText(text);\n } else {\n // To make a change we just need a Range, not a Selection\n const range = document.createRange();\n const textNode = document.createTextNode(text);\n\n if (canManipulateViaTextNodes(input)) {\n let node = input.firstChild;\n\n // If textarea is empty, just insert the text\n if (!node) {\n input.appendChild(textNode);\n } else {\n // Otherwise we need to find a nodes for start and end\n let offset = 0;\n let startNode = null;\n let endNode = null;\n\n while (node && (startNode === null || endNode === null)) {\n const nodeLength = node.nodeValue!.length;\n\n // if start of the selection falls into current node\n if (start >= offset && start <= offset + nodeLength) {\n range.setStart((startNode = node), start - offset);\n }\n\n // if end of the selection falls into current node\n if (end >= offset && end <= offset + nodeLength) {\n range.setEnd((endNode = node), end - offset);\n }\n\n offset += nodeLength;\n node = node.nextSibling;\n }\n\n // If there is some text selected, remove it as we should replace it\n if (start !== end) {\n range.deleteContents();\n }\n }\n }\n\n // If the node is a textarea and the range doesn't span outside the element\n //\n // Get the commonAncestorContainer of the selected range and test its type\n // If the node is of type `#text` it means that we're still working with text nodes within our textarea element\n // otherwise, if it's of type `#document` for example it means our selection spans outside the textarea.\n if (canManipulateViaTextNodes(input) && range.commonAncestorContainer.nodeName === '#text') {\n // Finally insert a new node. The browser will automatically split start and end nodes into two if necessary\n range.insertNode(textNode);\n } else {\n // If the node is not a textarea or the range spans outside a textarea the only way is to replace the whole value\n const value = input.value;\n input.value = value.slice(0, start) + text + value.slice(end);\n }\n }\n\n // Correct the cursor position to be at the end of the insertion\n input.setSelectionRange(start + text.length, start + text.length);\n\n // Notify any possible listeners of the change\n const e = document.createEvent('UIEvent');\n e.initEvent('input', true, false);\n input.dispatchEvent(e);\n }\n}\n"
|
|
65
|
+
"/**\n * The MIT License\n * Copyright (c) 2018 Dmitriy Kubyshkin\n * Copied from https://github.com/grassator/insert-text-at-cursor\n */\n\nlet browserSupportsTextareaTextNodes: any;\n\n/**\n * @param {HTMLElement} input\n * @return {boolean}\n */\nfunction canManipulateViaTextNodes(input: HTMLTextAreaElement | HTMLInputElement): boolean {\n if (input.nodeName !== 'TEXTAREA') {\n return false;\n }\n if (typeof browserSupportsTextareaTextNodes === 'undefined') {\n const textarea: HTMLTextAreaElement = document.createElement('textarea');\n textarea.value = '1';\n browserSupportsTextareaTextNodes = !!textarea.firstChild;\n }\n return browserSupportsTextareaTextNodes;\n}\n\n/**\n * @param {string} val\n * @param {number} cursorIdx\n * @param {HTMLTextAreaElement|HTMLInputElement} input\n * @return {void}\n */\nexport const insertAtLineStart = (\n val: string,\n cursorIdx: number,\n input: HTMLTextAreaElement | HTMLInputElement,\n): void => {\n const content = input.value;\n let startIdx = 0;\n\n while (cursorIdx--) {\n let char = content[cursorIdx];\n if (char === '\\n') {\n startIdx = cursorIdx + 1;\n break;\n }\n }\n\n input.focus();\n input.setRangeText(val, startIdx, startIdx);\n input.dispatchEvent(new Event('input', { bubbles: true }));\n};\n\n/**\n * @param {HTMLTextAreaElement|HTMLInputElement} input\n * @param {string} text\n * @returns {void}\n */\nexport function insertTextAtPosition(input: HTMLTextAreaElement | HTMLInputElement, text: string): void {\n // Most of the used APIs only work with the field selected\n input.focus();\n\n // IE 8-10\n if ((document as any).selection) {\n const ieRange = (document as any).selection.createRange();\n ieRange.text = text;\n\n // Move cursor after the inserted text\n ieRange.collapse(false /* to the end */);\n ieRange.select();\n\n return;\n }\n\n // Webkit + Edge\n const isSuccess = document.execCommand && document.execCommand('insertText', false, text);\n if (!isSuccess) {\n const start = input.selectionStart!;\n const end = input.selectionEnd!;\n // Firefox (non-standard method)\n if (typeof input.setRangeText === 'function') {\n input.setRangeText(text);\n } else {\n // To make a change we just need a Range, not a Selection\n const range = document.createRange();\n const textNode = document.createTextNode(text);\n\n if (canManipulateViaTextNodes(input)) {\n let node = input.firstChild;\n\n // If textarea is empty, just insert the text\n if (!node) {\n input.appendChild(textNode);\n } else {\n // Otherwise we need to find a nodes for start and end\n let offset = 0;\n let startNode = null;\n let endNode = null;\n\n while (node && (startNode === null || endNode === null)) {\n const nodeLength = node.nodeValue!.length;\n\n // if start of the selection falls into current node\n if (start >= offset && start <= offset + nodeLength) {\n range.setStart((startNode = node), start - offset);\n }\n\n // if end of the selection falls into current node\n if (end >= offset && end <= offset + nodeLength) {\n range.setEnd((endNode = node), end - offset);\n }\n\n offset += nodeLength;\n node = node.nextSibling;\n }\n\n // If there is some text selected, remove it as we should replace it\n if (start !== end) {\n range.deleteContents();\n }\n }\n }\n\n // If the node is a textarea and the range doesn't span outside the element\n //\n // Get the commonAncestorContainer of the selected range and test its type\n // If the node is of type `#text` it means that we're still working with text nodes within our textarea element\n // otherwise, if it's of type `#document` for example it means our selection spans outside the textarea.\n if (canManipulateViaTextNodes(input) && range.commonAncestorContainer.nodeName === '#text') {\n // Finally insert a new node. The browser will automatically split start and end nodes into two if necessary\n range.insertNode(textNode);\n } else {\n // If the node is not a textarea or the range spans outside a textarea the only way is to replace the whole value\n const value = input.value;\n input.value = value.slice(0, start) + text + value.slice(end);\n }\n }\n\n // Correct the cursor position to be at the end of the insertion\n input.setSelectionRange(start + text.length, start + text.length);\n\n // Notify any possible listeners of the change\n const e = document.createEvent('UIEvent');\n e.initEvent('input', true, false);\n input.dispatchEvent(e);\n }\n}\n"
|
|
58
66
|
]
|
|
59
67
|
}
|
package/markdown-editor.css
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uiw/react-md-editor",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.9.1",
|
|
4
4
|
"description": "A markdown editor with preview, implemented with React.js and TypeScript.",
|
|
5
5
|
"homepage": "https://uiwjs.github.io/react-md-editor/",
|
|
6
6
|
"author": "kenny wang <wowohoo@qq.com>",
|
|
@@ -53,10 +53,10 @@
|
|
|
53
53
|
"compile-less-cli": "1.8.9",
|
|
54
54
|
"katex": "0.15.1",
|
|
55
55
|
"husky": "7.0.4",
|
|
56
|
-
"jest-environment-jsdom": "27.4.
|
|
56
|
+
"jest-environment-jsdom": "27.4.4",
|
|
57
57
|
"kkt": "6.11.0",
|
|
58
58
|
"lint-staged": "12.1.2",
|
|
59
|
-
"prettier": "2.5.
|
|
59
|
+
"prettier": "2.5.1",
|
|
60
60
|
"react": "17.0.2",
|
|
61
61
|
"react-dom": "17.0.2",
|
|
62
62
|
"react-test-renderer": "17.0.2",
|
|
@@ -65,7 +65,8 @@
|
|
|
65
65
|
},
|
|
66
66
|
"dependencies": {
|
|
67
67
|
"@babel/runtime": "^7.14.6",
|
|
68
|
-
"@uiw/react-markdown-preview": "3.4.
|
|
68
|
+
"@uiw/react-markdown-preview": "3.4.5",
|
|
69
|
+
"rehype-sanitize": "5.0.1",
|
|
69
70
|
"rehype": "12.0.0"
|
|
70
71
|
},
|
|
71
72
|
"keywords": [
|
package/src/Context.tsx
CHANGED
package/src/Editor.tsx
CHANGED
|
@@ -76,6 +76,10 @@ export interface MDEditorProps extends Omit<React.HTMLAttributes<HTMLDivElement>
|
|
|
76
76
|
* Default `2` spaces.
|
|
77
77
|
*/
|
|
78
78
|
tabSize?: number;
|
|
79
|
+
/**
|
|
80
|
+
* If `false`, the `tab` key inserts a tab character into the textarea. If `true`, the `tab` key executes default behavior e.g. focus shifts to next element.
|
|
81
|
+
*/
|
|
82
|
+
defaultTabEnable?: boolean;
|
|
79
83
|
/**
|
|
80
84
|
* You can create your own commands or reuse existing commands.
|
|
81
85
|
*/
|
|
@@ -128,12 +132,12 @@ const InternalMDEditor = (
|
|
|
128
132
|
minHeight = 100,
|
|
129
133
|
autoFocus,
|
|
130
134
|
tabSize = 2,
|
|
135
|
+
defaultTabEnable = false,
|
|
131
136
|
onChange,
|
|
132
137
|
hideToolbar,
|
|
133
138
|
renderTextarea,
|
|
134
139
|
...other
|
|
135
140
|
} = props || {};
|
|
136
|
-
|
|
137
141
|
const cmds = commands
|
|
138
142
|
.map((item) => (commandsFilter ? commandsFilter(item, false) : item))
|
|
139
143
|
.filter(Boolean) as ICommand[];
|
|
@@ -146,6 +150,7 @@ const InternalMDEditor = (
|
|
|
146
150
|
height,
|
|
147
151
|
highlightEnable,
|
|
148
152
|
tabSize,
|
|
153
|
+
defaultTabEnable,
|
|
149
154
|
scrollTop: 0,
|
|
150
155
|
scrollTopPreview: 0,
|
|
151
156
|
commands: cmds,
|
package/src/commands/title1.tsx
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import { insertAtLineStart } from '../utils/InsertTextAtPosition';
|
|
2
3
|
import { ICommand, TextState, TextAreaTextApi } from './';
|
|
3
4
|
|
|
4
5
|
export const title1: ICommand = {
|
|
@@ -8,10 +9,10 @@ export const title1: ICommand = {
|
|
|
8
9
|
buttonProps: { 'aria-label': 'Insert title 1', title: 'Insert title 1' },
|
|
9
10
|
icon: <div style={{ fontSize: 18, textAlign: 'left' }}>Title 1</div>,
|
|
10
11
|
execute: (state: TextState, api: TextAreaTextApi) => {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
if (state.selection.start === 0 || /\n$/.test(state.text)) {
|
|
13
|
+
api.replaceSelection('# ');
|
|
14
|
+
} else {
|
|
15
|
+
insertAtLineStart('# ', state.selection.start, api.textArea);
|
|
14
16
|
}
|
|
15
|
-
api.replaceSelection(modifyText);
|
|
16
17
|
},
|
|
17
18
|
};
|
package/src/commands/title2.tsx
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
+
import { insertAtLineStart } from '../utils/InsertTextAtPosition';
|
|
2
3
|
import { ICommand, TextState, TextAreaTextApi } from './';
|
|
3
4
|
|
|
4
5
|
export const title2: ICommand = {
|
|
@@ -8,10 +9,10 @@ export const title2: ICommand = {
|
|
|
8
9
|
buttonProps: { 'aria-label': 'Insert title2', title: 'Insert title 2' },
|
|
9
10
|
icon: <div style={{ fontSize: 16, textAlign: 'left' }}>Title 2</div>,
|
|
10
11
|
execute: (state: TextState, api: TextAreaTextApi) => {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
if (state.selection.start === 0 || /\n$/.test(state.text)) {
|
|
13
|
+
api.replaceSelection('## ');
|
|
14
|
+
} else {
|
|
15
|
+
insertAtLineStart('## ', state.selection.start, api.textArea);
|
|
14
16
|
}
|
|
15
|
-
api.replaceSelection(modifyText);
|
|
16
17
|
},
|
|
17
18
|
};
|
package/src/commands/title3.tsx
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
+
import { insertAtLineStart } from '../utils/InsertTextAtPosition';
|
|
2
3
|
import { ICommand, TextState, TextAreaTextApi } from './';
|
|
3
4
|
|
|
4
5
|
export const title3: ICommand = {
|
|
@@ -8,10 +9,10 @@ export const title3: ICommand = {
|
|
|
8
9
|
buttonProps: { 'aria-label': 'Insert title3', title: 'Insert title 3' },
|
|
9
10
|
icon: <div style={{ fontSize: 15, textAlign: 'left' }}>Title 3</div>,
|
|
10
11
|
execute: (state: TextState, api: TextAreaTextApi) => {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
if (state.selection.start === 0 || /\n$/.test(state.text)) {
|
|
13
|
+
api.replaceSelection('### ');
|
|
14
|
+
} else {
|
|
15
|
+
insertAtLineStart('### ', state.selection.start, api.textArea);
|
|
14
16
|
}
|
|
15
|
-
api.replaceSelection(modifyText);
|
|
16
17
|
},
|
|
17
18
|
};
|
package/src/commands/title4.tsx
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
+
import { insertAtLineStart } from '../utils/InsertTextAtPosition';
|
|
2
3
|
import { ICommand, TextState, TextAreaTextApi } from './';
|
|
3
4
|
|
|
4
5
|
export const title4: ICommand = {
|
|
@@ -8,10 +9,10 @@ export const title4: ICommand = {
|
|
|
8
9
|
buttonProps: { 'aria-label': 'Insert title4', title: 'Insert title 4' },
|
|
9
10
|
icon: <div style={{ fontSize: 14, textAlign: 'left' }}>Title 4</div>,
|
|
10
11
|
execute: (state: TextState, api: TextAreaTextApi) => {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
if (state.selection.start === 0 || /\n$/.test(state.text)) {
|
|
13
|
+
api.replaceSelection('#### ');
|
|
14
|
+
} else {
|
|
15
|
+
insertAtLineStart('#### ', state.selection.start, api.textArea);
|
|
14
16
|
}
|
|
15
|
-
api.replaceSelection(modifyText);
|
|
16
17
|
},
|
|
17
18
|
};
|
package/src/commands/title5.tsx
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
+
import { insertAtLineStart } from '../utils/InsertTextAtPosition';
|
|
2
3
|
import { ICommand, TextState, TextAreaTextApi } from './';
|
|
3
4
|
|
|
4
5
|
export const title5: ICommand = {
|
|
@@ -8,10 +9,10 @@ export const title5: ICommand = {
|
|
|
8
9
|
buttonProps: { 'aria-label': 'Insert title5', title: 'Insert title 5' },
|
|
9
10
|
icon: <div style={{ fontSize: 12, textAlign: 'left' }}>Title 5</div>,
|
|
10
11
|
execute: (state: TextState, api: TextAreaTextApi) => {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
if (state.selection.start === 0 || /\n$/.test(state.text)) {
|
|
13
|
+
api.replaceSelection('##### ');
|
|
14
|
+
} else {
|
|
15
|
+
insertAtLineStart('##### ', state.selection.start, api.textArea);
|
|
14
16
|
}
|
|
15
|
-
api.replaceSelection(modifyText);
|
|
16
17
|
},
|
|
17
18
|
};
|
package/src/commands/title6.tsx
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
+
import { insertAtLineStart } from '../utils/InsertTextAtPosition';
|
|
2
3
|
import { ICommand, TextState, TextAreaTextApi } from './';
|
|
3
4
|
|
|
4
5
|
export const title6: ICommand = {
|
|
@@ -8,10 +9,10 @@ export const title6: ICommand = {
|
|
|
8
9
|
buttonProps: { 'aria-label': 'Insert title6', title: 'Insert title 6' },
|
|
9
10
|
icon: <div style={{ fontSize: 12, textAlign: 'left' }}>Title 6</div>,
|
|
10
11
|
execute: (state: TextState, api: TextAreaTextApi) => {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
if (state.selection.start === 0 || /\n$/.test(state.text)) {
|
|
13
|
+
api.replaceSelection('###### ');
|
|
14
|
+
} else {
|
|
15
|
+
insertAtLineStart('###### ', state.selection.start, api.textArea);
|
|
14
16
|
}
|
|
15
|
-
api.replaceSelection(modifyText);
|
|
16
17
|
},
|
|
17
18
|
};
|
|
@@ -41,7 +41,7 @@ export default function Markdown(props: MarkdownProps) {
|
|
|
41
41
|
.processSync(
|
|
42
42
|
`<pre class="language-markdown ${prefixCls}-text-pre wmde-markdown-color"><code class="language-markdown">${html2Escape(
|
|
43
43
|
markdown,
|
|
44
|
-
)}</code></pre>`,
|
|
44
|
+
)}\n</code></pre>`,
|
|
45
45
|
);
|
|
46
46
|
return React.createElement('div', {
|
|
47
47
|
className: 'wmde-markdown-color',
|
|
@@ -10,8 +10,17 @@ export interface TextAreaProps extends Omit<React.TextareaHTMLAttributes<HTMLTex
|
|
|
10
10
|
|
|
11
11
|
export default function Textarea(props: TextAreaProps) {
|
|
12
12
|
const { prefixCls, onChange, ...other } = props;
|
|
13
|
-
const {
|
|
14
|
-
|
|
13
|
+
const {
|
|
14
|
+
markdown,
|
|
15
|
+
commands,
|
|
16
|
+
fullscreen,
|
|
17
|
+
preview,
|
|
18
|
+
highlightEnable,
|
|
19
|
+
extraCommands,
|
|
20
|
+
tabSize,
|
|
21
|
+
defaultTabEnable,
|
|
22
|
+
dispatch,
|
|
23
|
+
} = useContext(EditorContext);
|
|
15
24
|
const textRef = React.useRef<HTMLTextAreaElement>(null);
|
|
16
25
|
const executeRef = React.useRef<TextAreaCommandOrchestrator>();
|
|
17
26
|
const statesRef = React.useRef<ExecuteCommandState>({ fullscreen, preview });
|
|
@@ -30,7 +39,7 @@ export default function Textarea(props: TextAreaProps) {
|
|
|
30
39
|
}, []);
|
|
31
40
|
|
|
32
41
|
const onKeyDown = (e: KeyboardEvent | React.KeyboardEvent<HTMLTextAreaElement>) => {
|
|
33
|
-
handleKeyDown(e, tabSize);
|
|
42
|
+
handleKeyDown(e, tabSize, defaultTabEnable);
|
|
34
43
|
shortcuts(e, [...(commands || []), ...(extraCommands || [])], executeRef.current, dispatch, statesRef.current);
|
|
35
44
|
};
|
|
36
45
|
useEffect(() => {
|
|
@@ -14,16 +14,18 @@ function stopPropagation(e: KeyboardEvent | React.KeyboardEvent<HTMLTextAreaElem
|
|
|
14
14
|
export default function handleKeyDown(
|
|
15
15
|
e: KeyboardEvent | React.KeyboardEvent<HTMLTextAreaElement>,
|
|
16
16
|
tabSize: number = 2,
|
|
17
|
+
defaultTabEnable: boolean = false,
|
|
17
18
|
) {
|
|
18
19
|
const target = e.target as HTMLTextAreaElement;
|
|
19
20
|
const starVal = target.value.substr(0, target.selectionStart);
|
|
20
21
|
const valArr = starVal.split('\n');
|
|
21
22
|
const currentLineStr = valArr[valArr.length - 1];
|
|
22
23
|
const textArea = new TextAreaTextApi(target);
|
|
24
|
+
|
|
23
25
|
/**
|
|
24
26
|
* `9` - `Tab`
|
|
25
27
|
*/
|
|
26
|
-
if (e.code && e.code.toLowerCase() === 'tab') {
|
|
28
|
+
if (!defaultTabEnable && e.code && e.code.toLowerCase() === 'tab') {
|
|
27
29
|
stopPropagation(e);
|
|
28
30
|
const space = new Array(tabSize + 1).join(' ');
|
|
29
31
|
if (target.selectionStart !== target.selectionEnd) {
|
package/src/index.less
CHANGED
|
@@ -10,7 +10,7 @@ let browserSupportsTextareaTextNodes: any;
|
|
|
10
10
|
* @param {HTMLElement} input
|
|
11
11
|
* @return {boolean}
|
|
12
12
|
*/
|
|
13
|
-
function canManipulateViaTextNodes(input: HTMLTextAreaElement | HTMLInputElement) {
|
|
13
|
+
function canManipulateViaTextNodes(input: HTMLTextAreaElement | HTMLInputElement): boolean {
|
|
14
14
|
if (input.nodeName !== 'TEXTAREA') {
|
|
15
15
|
return false;
|
|
16
16
|
}
|
|
@@ -22,12 +22,39 @@ function canManipulateViaTextNodes(input: HTMLTextAreaElement | HTMLInputElement
|
|
|
22
22
|
return browserSupportsTextareaTextNodes;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
/**
|
|
26
|
+
* @param {string} val
|
|
27
|
+
* @param {number} cursorIdx
|
|
28
|
+
* @param {HTMLTextAreaElement|HTMLInputElement} input
|
|
29
|
+
* @return {void}
|
|
30
|
+
*/
|
|
31
|
+
export const insertAtLineStart = (
|
|
32
|
+
val: string,
|
|
33
|
+
cursorIdx: number,
|
|
34
|
+
input: HTMLTextAreaElement | HTMLInputElement,
|
|
35
|
+
): void => {
|
|
36
|
+
const content = input.value;
|
|
37
|
+
let startIdx = 0;
|
|
38
|
+
|
|
39
|
+
while (cursorIdx--) {
|
|
40
|
+
let char = content[cursorIdx];
|
|
41
|
+
if (char === '\n') {
|
|
42
|
+
startIdx = cursorIdx + 1;
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
input.focus();
|
|
48
|
+
input.setRangeText(val, startIdx, startIdx);
|
|
49
|
+
input.dispatchEvent(new Event('input', { bubbles: true }));
|
|
50
|
+
};
|
|
51
|
+
|
|
25
52
|
/**
|
|
26
53
|
* @param {HTMLTextAreaElement|HTMLInputElement} input
|
|
27
54
|
* @param {string} text
|
|
28
55
|
* @returns {void}
|
|
29
56
|
*/
|
|
30
|
-
export function insertTextAtPosition(input: HTMLTextAreaElement | HTMLInputElement, text: string) {
|
|
57
|
+
export function insertTextAtPosition(input: HTMLTextAreaElement | HTMLInputElement, text: string): void {
|
|
31
58
|
// Most of the used APIs only work with the field selected
|
|
32
59
|
input.focus();
|
|
33
60
|
|