@uiw/react-md-editor 3.23.6 → 3.24.0

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 (105) hide show
  1. package/README.md +29 -4
  2. package/dist/mdeditor.css +2 -0
  3. package/dist/mdeditor.js +225 -237
  4. package/dist/mdeditor.min.css +1 -1
  5. package/dist/mdeditor.min.js +1 -1
  6. package/dist/mdeditor.min.js.LICENSE.txt +1 -1
  7. package/esm/commands/bold.js +9 -13
  8. package/esm/commands/code.d.ts +1 -1
  9. package/esm/commands/code.js +75 -68
  10. package/esm/commands/comment.js +20 -18
  11. package/esm/commands/help.d.ts +2 -0
  12. package/esm/commands/help.js +22 -0
  13. package/esm/commands/hr.js +33 -2
  14. package/esm/commands/image.js +39 -16
  15. package/esm/commands/index.d.ts +3 -1
  16. package/esm/commands/index.js +3 -1
  17. package/esm/commands/issue.d.ts +2 -0
  18. package/esm/commands/issue.js +41 -0
  19. package/esm/commands/italic.js +9 -13
  20. package/esm/commands/link.js +40 -14
  21. package/esm/commands/list.d.ts +3 -10
  22. package/esm/commands/list.js +39 -41
  23. package/esm/commands/quote.js +8 -8
  24. package/esm/commands/strikeThrough.js +9 -13
  25. package/esm/commands/table.d.ts +2 -0
  26. package/esm/commands/table.js +57 -0
  27. package/esm/commands/title.d.ts +7 -1
  28. package/esm/commands/title.js +21 -0
  29. package/esm/commands/title1.js +9 -7
  30. package/esm/commands/title2.js +9 -7
  31. package/esm/commands/title3.js +9 -7
  32. package/esm/commands/title4.js +9 -7
  33. package/esm/commands/title5.js +9 -7
  34. package/esm/commands/title6.js +9 -7
  35. package/esm/components/TextArea/handleKeyDown.js +83 -3
  36. package/esm/utils/InsertTextAtPosition.d.ts +0 -7
  37. package/esm/utils/InsertTextAtPosition.js +6 -24
  38. package/esm/utils/markdownUtils.d.ts +23 -1
  39. package/esm/utils/markdownUtils.js +83 -4
  40. package/lib/Context.js +2 -3
  41. package/lib/Editor.js +1 -2
  42. package/lib/commands/bold.js +10 -15
  43. package/lib/commands/code.d.ts +1 -1
  44. package/lib/commands/code.js +75 -70
  45. package/lib/commands/comment.js +21 -20
  46. package/lib/commands/divider.js +2 -3
  47. package/lib/commands/fullscreen.js +2 -3
  48. package/lib/commands/group.js +2 -3
  49. package/lib/commands/help.d.ts +2 -0
  50. package/lib/commands/help.js +29 -0
  51. package/lib/commands/hr.js +35 -5
  52. package/lib/commands/image.js +40 -18
  53. package/lib/commands/index.d.ts +3 -1
  54. package/lib/commands/index.js +8 -10
  55. package/lib/commands/issue.d.ts +2 -0
  56. package/lib/commands/issue.js +48 -0
  57. package/lib/commands/italic.js +10 -15
  58. package/lib/commands/link.js +41 -16
  59. package/lib/commands/list.d.ts +3 -10
  60. package/lib/commands/list.js +43 -52
  61. package/lib/commands/preview.js +4 -7
  62. package/lib/commands/quote.js +9 -10
  63. package/lib/commands/strikeThrough.js +10 -15
  64. package/lib/commands/table.d.ts +2 -0
  65. package/lib/commands/table.js +64 -0
  66. package/lib/commands/title.d.ts +7 -1
  67. package/lib/commands/title.js +23 -3
  68. package/lib/commands/title1.js +11 -10
  69. package/lib/commands/title2.js +11 -10
  70. package/lib/commands/title3.js +11 -10
  71. package/lib/commands/title4.js +11 -10
  72. package/lib/commands/title5.js +11 -10
  73. package/lib/commands/title6.js +11 -10
  74. package/lib/components/DragBar/index.js +1 -2
  75. package/lib/components/TextArea/handleKeyDown.js +84 -4
  76. package/lib/index.js +1 -2
  77. package/lib/utils/InsertTextAtPosition.d.ts +0 -7
  78. package/lib/utils/InsertTextAtPosition.js +6 -26
  79. package/lib/utils/markdownUtils.d.ts +23 -1
  80. package/lib/utils/markdownUtils.js +84 -4
  81. package/package.json +4 -1
  82. package/src/commands/bold.tsx +13 -12
  83. package/src/commands/code.tsx +72 -71
  84. package/src/commands/comment.tsx +20 -15
  85. package/src/commands/help.tsx +19 -0
  86. package/src/commands/hr.tsx +33 -2
  87. package/src/commands/image.tsx +38 -15
  88. package/src/commands/index.ts +8 -1
  89. package/src/commands/issue.tsx +36 -0
  90. package/src/commands/italic.tsx +13 -12
  91. package/src/commands/link.tsx +39 -12
  92. package/src/commands/list.tsx +35 -53
  93. package/src/commands/quote.tsx +14 -13
  94. package/src/commands/strikeThrough.tsx +13 -12
  95. package/src/commands/table.tsx +52 -0
  96. package/src/commands/title.tsx +18 -1
  97. package/src/commands/title1.tsx +6 -9
  98. package/src/commands/title2.tsx +6 -9
  99. package/src/commands/title3.tsx +6 -9
  100. package/src/commands/title4.tsx +6 -9
  101. package/src/commands/title5.tsx +6 -9
  102. package/src/commands/title6.tsx +6 -9
  103. package/src/components/TextArea/handleKeyDown.tsx +50 -3
  104. package/src/utils/InsertTextAtPosition.ts +7 -28
  105. package/src/utils/markdownUtils.ts +94 -4
@@ -1,6 +1,6 @@
1
1
  import { insertTextAtPosition } from '../../utils/InsertTextAtPosition';
2
+ import { insertBeforeEachLine, selectLine } from '../../utils/markdownUtils';
2
3
  import { TextAreaTextApi } from '../../commands';
3
- import { insertBeforeEachLine } from '../../commands/list';
4
4
 
5
5
  /**
6
6
  * - `13` - `Enter`
@@ -11,6 +11,38 @@ function stopPropagation(e: KeyboardEvent | React.KeyboardEvent<HTMLTextAreaElem
11
11
  e.preventDefault();
12
12
  }
13
13
 
14
+ function handleLineMove(e: KeyboardEvent | React.KeyboardEvent<HTMLTextAreaElement>, direction: number) {
15
+ stopPropagation(e);
16
+ const target = e.target as HTMLTextAreaElement;
17
+ const textArea = new TextAreaTextApi(target);
18
+ let selection = { start: target.selectionStart, end: target.selectionEnd };
19
+ selection = selectLine({ text: target.value, selection });
20
+ if ((direction < 0 && selection.start <= 0) || (direction > 0 && selection.end >= target.value.length)) {
21
+ return;
22
+ }
23
+
24
+ const blockText = target.value.slice(selection.start, selection.end);
25
+ if (direction < 0) {
26
+ const prevLineSelection = selectLine({
27
+ text: target.value,
28
+ selection: { start: selection.start - 1, end: selection.start - 1 },
29
+ });
30
+ const prevLineText = target.value.slice(prevLineSelection.start, prevLineSelection.end);
31
+ textArea.setSelectionRange({ start: prevLineSelection.start, end: selection.end });
32
+ insertTextAtPosition(target, `${blockText}\n${prevLineText}`);
33
+ textArea.setSelectionRange({ start: prevLineSelection.start, end: prevLineSelection.start + blockText.length });
34
+ } else {
35
+ const nextLineSelection = selectLine({
36
+ text: target.value,
37
+ selection: { start: selection.end + 1, end: selection.end + 1 },
38
+ });
39
+ const nextLineText = target.value.slice(nextLineSelection.start, nextLineSelection.end);
40
+ textArea.setSelectionRange({ start: selection.start, end: nextLineSelection.end });
41
+ insertTextAtPosition(target, `${nextLineText}\n${blockText}`);
42
+ textArea.setSelectionRange({ start: nextLineSelection.end - blockText.length, end: nextLineSelection.end });
43
+ }
44
+ }
45
+
14
46
  export default function handleKeyDown(
15
47
  e: KeyboardEvent | React.KeyboardEvent<HTMLTextAreaElement>,
16
48
  tabSize: number = 2,
@@ -70,7 +102,8 @@ export default function handleKeyDown(
70
102
  } else if (
71
103
  e.keyCode === 13 &&
72
104
  e.code.toLowerCase() === 'enter' &&
73
- (/^(-|\*)\s/.test(currentLineStr) || /^\d+.\s/.test(currentLineStr))
105
+ (/^(-|\*)\s/.test(currentLineStr) || /^\d+.\s/.test(currentLineStr)) &&
106
+ !e.shiftKey
74
107
  ) {
75
108
  /**
76
109
  * `13` - `Enter`
@@ -84,7 +117,7 @@ export default function handleKeyDown(
84
117
 
85
118
  if (currentLineStr.startsWith('- [ ]')) {
86
119
  startStr = '\n- [ ] ';
87
- } else if (currentLineStr.startsWith('- [X]')) {
120
+ } else if (currentLineStr.startsWith('- [X]') || currentLineStr.startsWith('- [x]')) {
88
121
  startStr = '\n- [X] ';
89
122
  }
90
123
 
@@ -92,5 +125,19 @@ export default function handleKeyDown(
92
125
  startStr = `\n${parseInt(currentLineStr) + 1}. `;
93
126
  }
94
127
  return insertTextAtPosition(target, startStr);
128
+ } else if (e.code && e.code.toLowerCase() === 'keyd' && e.ctrlKey) {
129
+ // Duplicate lines
130
+ stopPropagation(e);
131
+ let selection = { start: target.selectionStart, end: target.selectionEnd };
132
+ const savedSelection = selection;
133
+ selection = selectLine({ text: target.value, selection });
134
+ const textToDuplicate = target.value.slice(selection.start, selection.end);
135
+ textArea.setSelectionRange({ start: selection.end, end: selection.end });
136
+ insertTextAtPosition(target, `\n${textToDuplicate}`);
137
+ textArea.setSelectionRange({ start: savedSelection.start, end: savedSelection.end });
138
+ } else if (e.code && e.code.toLowerCase() === 'arrowup' && e.altKey) {
139
+ handleLineMove(e, -1);
140
+ } else if (e.code && e.code.toLowerCase() === 'arrowdown' && e.altKey) {
141
+ handleLineMove(e, 1);
95
142
  }
96
143
  }
@@ -22,33 +22,6 @@ 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
-
52
25
  /**
53
26
  * @param {HTMLTextAreaElement|HTMLInputElement} input
54
27
  * @param {string} text
@@ -71,7 +44,13 @@ export function insertTextAtPosition(input: HTMLTextAreaElement | HTMLInputEleme
71
44
  }
72
45
 
73
46
  // Webkit + Edge
74
- const isSuccess = document.execCommand && document.execCommand('insertText', false, text);
47
+ let isSuccess = false;
48
+ if (text !== '') {
49
+ isSuccess = document.execCommand && document.execCommand('insertText', false, text);
50
+ } else {
51
+ isSuccess = document.execCommand && document.execCommand('delete', false);
52
+ }
53
+
75
54
  if (!isSuccess) {
76
55
  const start = input.selectionStart!;
77
56
  const end = input.selectionEnd!;
@@ -1,16 +1,42 @@
1
1
  import { TextRange } from '../commands';
2
+ import { TextAreaTextApi, ExecuteState } from '../commands';
2
3
 
3
4
  export interface TextSection {
4
5
  text: string;
5
6
  selection: TextRange;
6
7
  }
7
8
 
8
- export function selectWord({ text, selection }: TextSection): TextRange {
9
+ export function selectWord({
10
+ text,
11
+ selection,
12
+ prefix,
13
+ suffix = prefix,
14
+ }: {
15
+ text: string;
16
+ selection: TextRange;
17
+ prefix: string;
18
+ suffix?: string;
19
+ }): TextRange {
20
+ let result = selection;
9
21
  if (text && text.length && selection.start === selection.end) {
10
- // the user is pointing to a word
11
- return getSurroundingWord(text, selection.start);
22
+ result = getSurroundingWord(text, selection.start);
23
+ }
24
+ if (result.start >= prefix.length && result.end <= text.length - suffix.length) {
25
+ const selectedTextContext = text.slice(result.start - prefix.length, result.end + suffix.length);
26
+ if (selectedTextContext.startsWith(prefix) && selectedTextContext.endsWith(suffix)) {
27
+ return { start: result.start - prefix.length, end: result.end + suffix.length };
28
+ }
12
29
  }
13
- return selection;
30
+ return result;
31
+ }
32
+
33
+ export function selectLine({ text, selection }: TextSection): TextRange {
34
+ const start = text.slice(0, selection.start).lastIndexOf('\n') + 1;
35
+ let end = text.slice(selection.end).indexOf('\n') + selection.end;
36
+ if (end === selection.end - 1) {
37
+ end = text.length;
38
+ }
39
+ return { start, end };
14
40
  }
15
41
 
16
42
  /**
@@ -100,3 +126,67 @@ export function getSurroundingWord(text: string, position: number): TextRange {
100
126
 
101
127
  return { start, end };
102
128
  }
129
+
130
+ export function executeCommand({
131
+ api,
132
+ selectedText,
133
+ selection,
134
+ prefix,
135
+ suffix = prefix,
136
+ }: {
137
+ api: TextAreaTextApi;
138
+ selectedText: string;
139
+ selection: TextRange;
140
+ prefix: string;
141
+ suffix?: string;
142
+ }) {
143
+ if (
144
+ selectedText.length >= prefix.length + suffix.length &&
145
+ selectedText.startsWith(prefix) &&
146
+ selectedText.endsWith(suffix)
147
+ ) {
148
+ api.replaceSelection(selectedText.slice(prefix.length, suffix.length ? -suffix.length : undefined));
149
+ api.setSelectionRange({ start: selection.start - prefix.length, end: selection.end - prefix.length });
150
+ } else {
151
+ api.replaceSelection(`${prefix}${selectedText}${suffix}`);
152
+ api.setSelectionRange({ start: selection.start + prefix.length, end: selection.end + prefix.length });
153
+ }
154
+ }
155
+
156
+ export type AlterLineFunction = (line: string, index: number) => string;
157
+
158
+ /**
159
+ * Inserts insertionString before each line
160
+ */
161
+ export function insertBeforeEachLine(
162
+ selectedText: string,
163
+ insertBefore: string | AlterLineFunction,
164
+ ): { modifiedText: string; insertionLength: number } {
165
+ const lines = selectedText.split(/\n/);
166
+
167
+ let insertionLength = 0;
168
+ const modifiedText = lines
169
+ .map((item, index) => {
170
+ if (typeof insertBefore === 'string') {
171
+ if (item.startsWith(insertBefore)) {
172
+ insertionLength -= insertBefore.length;
173
+ return item.slice(insertBefore.length);
174
+ }
175
+ insertionLength += insertBefore.length;
176
+ return insertBefore + item;
177
+ }
178
+ if (typeof insertBefore === 'function') {
179
+ if (item.startsWith(insertBefore(item, index))) {
180
+ insertionLength -= insertBefore(item, index).length;
181
+ return item.slice(insertBefore(item, index).length);
182
+ }
183
+ const insertionResult = insertBefore(item, index);
184
+ insertionLength += insertionResult.length;
185
+ return insertBefore(item, index) + item;
186
+ }
187
+ throw Error('insertion is expected to be either a string or a function');
188
+ })
189
+ .join('\n');
190
+
191
+ return { modifiedText, insertionLength };
192
+ }