@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.
Files changed (68) hide show
  1. package/README.md +1 -1
  2. package/esm/Context.d.ts +0 -2
  3. package/esm/Context.js.map +2 -2
  4. package/esm/Editor.d.ts +1 -1
  5. package/esm/Editor.js +11 -4
  6. package/esm/Editor.js.map +4 -2
  7. package/esm/commands/title1.js +5 -6
  8. package/esm/commands/title1.js.map +9 -5
  9. package/esm/commands/title2.js +5 -6
  10. package/esm/commands/title2.js.map +9 -5
  11. package/esm/commands/title3.js +5 -6
  12. package/esm/commands/title3.js.map +9 -5
  13. package/esm/commands/title4.js +5 -6
  14. package/esm/commands/title4.js.map +9 -5
  15. package/esm/commands/title5.js +5 -6
  16. package/esm/commands/title5.js.map +9 -5
  17. package/esm/commands/title6.js +5 -6
  18. package/esm/commands/title6.js.map +9 -5
  19. package/esm/components/TextArea/Markdown.js +1 -1
  20. package/esm/components/TextArea/Markdown.js.map +2 -2
  21. package/esm/components/TextArea/Textarea.js +2 -4
  22. package/esm/components/TextArea/Textarea.js.map +2 -3
  23. package/esm/components/TextArea/index.d.ts +3 -2
  24. package/esm/components/TextArea/index.js +1 -2
  25. package/esm/components/TextArea/index.js.map +3 -3
  26. package/esm/utils/InsertTextAtPosition.d.ts +7 -0
  27. package/esm/utils/InsertTextAtPosition.js +27 -1
  28. package/esm/utils/InsertTextAtPosition.js.map +14 -6
  29. package/lib/Context.d.ts +0 -2
  30. package/lib/Context.js.map +2 -2
  31. package/lib/Editor.d.ts +1 -1
  32. package/lib/Editor.js +13 -4
  33. package/lib/Editor.js.map +4 -2
  34. package/lib/commands/title1.js +6 -6
  35. package/lib/commands/title1.js.map +8 -5
  36. package/lib/commands/title2.js +6 -6
  37. package/lib/commands/title2.js.map +8 -5
  38. package/lib/commands/title3.js +6 -6
  39. package/lib/commands/title3.js.map +8 -5
  40. package/lib/commands/title4.js +6 -6
  41. package/lib/commands/title4.js.map +8 -5
  42. package/lib/commands/title5.js +6 -6
  43. package/lib/commands/title5.js.map +8 -5
  44. package/lib/commands/title6.js +6 -6
  45. package/lib/commands/title6.js.map +8 -5
  46. package/lib/components/TextArea/Markdown.js +1 -1
  47. package/lib/components/TextArea/Markdown.js.map +2 -2
  48. package/lib/components/TextArea/Textarea.js +2 -4
  49. package/lib/components/TextArea/Textarea.js.map +2 -3
  50. package/lib/components/TextArea/index.d.ts +3 -2
  51. package/lib/components/TextArea/index.js +1 -2
  52. package/lib/components/TextArea/index.js.map +3 -3
  53. package/lib/utils/InsertTextAtPosition.d.ts +7 -0
  54. package/lib/utils/InsertTextAtPosition.js +30 -0
  55. package/lib/utils/InsertTextAtPosition.js.map +14 -6
  56. package/package.json +8 -7
  57. package/src/Context.tsx +0 -1
  58. package/src/Editor.tsx +14 -4
  59. package/src/commands/title1.tsx +5 -4
  60. package/src/commands/title2.tsx +5 -4
  61. package/src/commands/title3.tsx +5 -4
  62. package/src/commands/title4.tsx +5 -4
  63. package/src/commands/title5.tsx +5 -4
  64. package/src/commands/title6.tsx +5 -4
  65. package/src/components/TextArea/Markdown.tsx +1 -1
  66. package/src/components/TextArea/Textarea.tsx +3 -4
  67. package/src/components/TextArea/index.tsx +5 -5
  68. 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.map((item) => (commandsFilter ? commandsFilter(item) : item)).filter(Boolean) as ICommand[];
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
  />
@@ -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
- let modifyText = `# ${state.selectedText}\n`;
12
- if (!state.selectedText) {
13
- modifyText = `# `;
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
  };
@@ -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
- let modifyText = `## ${state.selectedText}\n`;
12
- if (!state.selectedText) {
13
- modifyText = `## `;
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
  };
@@ -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
- let modifyText = `### ${state.selectedText}\n`;
12
- if (!state.selectedText) {
13
- modifyText = `### `;
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
  };
@@ -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
- let modifyText = `#### ${state.selectedText}\n`;
12
- if (!state.selectedText) {
13
- modifyText = `#### `;
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
  };
@@ -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
- let modifyText = `##### ${state.selectedText}\n`;
12
- if (!state.selectedText) {
13
- modifyText = `##### `;
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
  };
@@ -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
- let modifyText = `###### ${state.selectedText}\n`;
12
- if (!state.selectedText) {
13
- modifyText = `###### `;
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: onChangeFromProps, ...other } = props;
13
- const { markdown, commands, fullscreen, preview, highlightEnable, extraCommands, tabSize, onChange, dispatch } =
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.target.value);
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 { MDEditorProps, IProps } from '../../Editor';
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?: MDEditorProps['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, onChange, dispatch } = useContext(EditorContext);
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