@uiw/react-md-editor 3.8.1 → 3.8.5
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 +1 -1
- package/esm/Context.d.ts +0 -2
- package/esm/Context.js.map +2 -2
- package/esm/Editor.d.ts +1 -1
- package/esm/Editor.js +11 -4
- package/esm/Editor.js.map +4 -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 -4
- package/esm/components/TextArea/Textarea.js.map +2 -3
- package/esm/components/TextArea/index.d.ts +3 -2
- package/esm/components/TextArea/index.js +1 -2
- package/esm/components/TextArea/index.js.map +3 -3
- 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 +0 -2
- package/lib/Context.js.map +2 -2
- package/lib/Editor.d.ts +1 -1
- package/lib/Editor.js +13 -4
- package/lib/Editor.js.map +4 -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 -4
- package/lib/components/TextArea/Textarea.js.map +2 -3
- package/lib/components/TextArea/index.d.ts +3 -2
- package/lib/components/TextArea/index.js +1 -2
- package/lib/components/TextArea/index.js.map +3 -3
- package/lib/utils/InsertTextAtPosition.d.ts +7 -0
- package/lib/utils/InsertTextAtPosition.js +30 -0
- package/lib/utils/InsertTextAtPosition.js.map +14 -6
- package/package.json +8 -7
- package/src/Context.tsx +0 -1
- package/src/Editor.tsx +14 -4
- 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 +3 -4
- package/src/components/TextArea/index.tsx +5 -5
- package/src/utils/InsertTextAtPosition.ts +29 -2
package/src/Editor.tsx
CHANGED
|
@@ -84,7 +84,7 @@ export interface MDEditorProps extends Omit<React.HTMLAttributes<HTMLDivElement>
|
|
|
84
84
|
* Filter or modify your commands.
|
|
85
85
|
* https://github.com/uiwjs/react-md-editor/issues/296
|
|
86
86
|
*/
|
|
87
|
-
commandsFilter?: (command: ICommand) => false | ICommand;
|
|
87
|
+
commandsFilter?: (command: ICommand, isExtra: boolean) => false | ICommand;
|
|
88
88
|
/**
|
|
89
89
|
* You can create your own commands or reuse existing commands.
|
|
90
90
|
*/
|
|
@@ -134,7 +134,12 @@ const InternalMDEditor = (
|
|
|
134
134
|
...other
|
|
135
135
|
} = props || {};
|
|
136
136
|
|
|
137
|
-
const cmds = commands
|
|
137
|
+
const cmds = commands
|
|
138
|
+
.map((item) => (commandsFilter ? commandsFilter(item, false) : item))
|
|
139
|
+
.filter(Boolean) as ICommand[];
|
|
140
|
+
const extraCmds = extraCommands
|
|
141
|
+
.map((item) => (commandsFilter ? commandsFilter(item, true) : item))
|
|
142
|
+
.filter(Boolean) as ICommand[];
|
|
138
143
|
let [state, dispatch] = useReducer(reducer, {
|
|
139
144
|
markdown: propsValue,
|
|
140
145
|
preview: previewType,
|
|
@@ -144,9 +149,8 @@ const InternalMDEditor = (
|
|
|
144
149
|
scrollTop: 0,
|
|
145
150
|
scrollTopPreview: 0,
|
|
146
151
|
commands: cmds,
|
|
147
|
-
extraCommands,
|
|
152
|
+
extraCommands: extraCmds,
|
|
148
153
|
fullscreen,
|
|
149
|
-
onChange,
|
|
150
154
|
barPopup: {},
|
|
151
155
|
});
|
|
152
156
|
const container = useRef<HTMLDivElement>(null);
|
|
@@ -271,6 +275,12 @@ const InternalMDEditor = (
|
|
|
271
275
|
prefixCls={prefixCls}
|
|
272
276
|
autoFocus={autoFocus}
|
|
273
277
|
{...textareaProps}
|
|
278
|
+
onChange={(evn) => {
|
|
279
|
+
onChange && onChange(evn.target.value);
|
|
280
|
+
if (textareaProps && textareaProps.onChange) {
|
|
281
|
+
textareaProps.onChange(evn);
|
|
282
|
+
}
|
|
283
|
+
}}
|
|
274
284
|
renderTextarea={renderTextarea}
|
|
275
285
|
onScroll={(e) => handleScroll(e, 'text')}
|
|
276
286
|
/>
|
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',
|
|
@@ -9,8 +9,8 @@ import './index.less';
|
|
|
9
9
|
export interface TextAreaProps extends Omit<React.TextareaHTMLAttributes<HTMLTextAreaElement>, 'value'>, IProps {}
|
|
10
10
|
|
|
11
11
|
export default function Textarea(props: TextAreaProps) {
|
|
12
|
-
const { prefixCls, onChange
|
|
13
|
-
const { markdown, commands, fullscreen, preview, highlightEnable, extraCommands, tabSize,
|
|
12
|
+
const { prefixCls, onChange, ...other } = props;
|
|
13
|
+
const { markdown, commands, fullscreen, preview, highlightEnable, extraCommands, tabSize, dispatch } =
|
|
14
14
|
useContext(EditorContext);
|
|
15
15
|
const textRef = React.useRef<HTMLTextAreaElement>(null);
|
|
16
16
|
const executeRef = React.useRef<TextAreaCommandOrchestrator>();
|
|
@@ -58,8 +58,7 @@ export default function Textarea(props: TextAreaProps) {
|
|
|
58
58
|
value={markdown}
|
|
59
59
|
onChange={(e) => {
|
|
60
60
|
dispatch && dispatch({ markdown: e.target.value });
|
|
61
|
-
onChange && onChange(e
|
|
62
|
-
onChangeFromProps && onChangeFromProps(e);
|
|
61
|
+
onChange && onChange(e);
|
|
63
62
|
}}
|
|
64
63
|
/>
|
|
65
64
|
);
|
|
@@ -2,14 +2,14 @@ import React, { useEffect, Fragment, useContext } from 'react';
|
|
|
2
2
|
import { EditorContext, ContextStore, ExecuteCommandState } from '../../Context';
|
|
3
3
|
import shortcuts from './shortcuts';
|
|
4
4
|
import Markdown from './Markdown';
|
|
5
|
-
import Textarea from './Textarea';
|
|
6
|
-
import {
|
|
5
|
+
import Textarea, { TextAreaProps } from './Textarea';
|
|
6
|
+
import { IProps } from '../../Editor';
|
|
7
7
|
import { TextAreaCommandOrchestrator, ICommand } from '../../commands';
|
|
8
8
|
import './index.less';
|
|
9
9
|
|
|
10
10
|
type RenderTextareaHandle = {
|
|
11
11
|
dispatch: ContextStore['dispatch'];
|
|
12
|
-
onChange?:
|
|
12
|
+
onChange?: TextAreaProps['onChange'];
|
|
13
13
|
useContext?: {
|
|
14
14
|
commands: ContextStore['commands'];
|
|
15
15
|
extraCommands: ContextStore['extraCommands'];
|
|
@@ -42,7 +42,7 @@ export type TextAreaRef = {
|
|
|
42
42
|
|
|
43
43
|
export default function TextArea(props: ITextAreaProps) {
|
|
44
44
|
const { prefixCls, className, onScroll, renderTextarea, ...otherProps } = props || {};
|
|
45
|
-
const { markdown, scrollTop, commands, extraCommands,
|
|
45
|
+
const { markdown, scrollTop, commands, extraCommands, dispatch } = useContext(EditorContext);
|
|
46
46
|
const textRef = React.useRef<HTMLTextAreaElement>(null);
|
|
47
47
|
const executeRef = React.useRef<TextAreaCommandOrchestrator>();
|
|
48
48
|
const warp = React.createRef<HTMLDivElement>();
|
|
@@ -88,7 +88,7 @@ export default function TextArea(props: ITextAreaProps) {
|
|
|
88
88
|
},
|
|
89
89
|
{
|
|
90
90
|
dispatch,
|
|
91
|
-
onChange,
|
|
91
|
+
onChange: otherProps.onChange,
|
|
92
92
|
shortcuts,
|
|
93
93
|
useContext: { commands, extraCommands, commandOrchestrator: executeRef.current },
|
|
94
94
|
},
|
|
@@ -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
|
|