@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.
Files changed (78) hide show
  1. package/README.md +2 -1
  2. package/esm/Context.d.ts +2 -0
  3. package/esm/Context.js.map +2 -2
  4. package/esm/Editor.d.ts +4 -0
  5. package/esm/Editor.js +3 -1
  6. package/esm/Editor.js.map +3 -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 -1
  22. package/esm/components/TextArea/Textarea.js.map +3 -2
  23. package/esm/components/TextArea/handleKeyDown.d.ts +1 -1
  24. package/esm/components/TextArea/handleKeyDown.js +6 -2
  25. package/esm/components/TextArea/handleKeyDown.js.map +3 -2
  26. package/esm/components/Toolbar/index.js +1 -1
  27. package/esm/components/Toolbar/index.js.map +2 -2
  28. package/esm/index.css +1 -1
  29. package/esm/index.less +1 -1
  30. package/esm/utils/InsertTextAtPosition.d.ts +7 -0
  31. package/esm/utils/InsertTextAtPosition.js +27 -1
  32. package/esm/utils/InsertTextAtPosition.js.map +14 -6
  33. package/lib/Context.d.ts +2 -0
  34. package/lib/Context.js.map +2 -2
  35. package/lib/Editor.d.ts +4 -0
  36. package/lib/Editor.js +4 -1
  37. package/lib/Editor.js.map +3 -2
  38. package/lib/commands/title1.js +6 -6
  39. package/lib/commands/title1.js.map +8 -5
  40. package/lib/commands/title2.js +6 -6
  41. package/lib/commands/title2.js.map +8 -5
  42. package/lib/commands/title3.js +6 -6
  43. package/lib/commands/title3.js.map +8 -5
  44. package/lib/commands/title4.js +6 -6
  45. package/lib/commands/title4.js.map +8 -5
  46. package/lib/commands/title5.js +6 -6
  47. package/lib/commands/title5.js.map +8 -5
  48. package/lib/commands/title6.js +6 -6
  49. package/lib/commands/title6.js.map +8 -5
  50. package/lib/components/TextArea/Markdown.js +1 -1
  51. package/lib/components/TextArea/Markdown.js.map +2 -2
  52. package/lib/components/TextArea/Textarea.js +2 -1
  53. package/lib/components/TextArea/Textarea.js.map +3 -2
  54. package/lib/components/TextArea/handleKeyDown.d.ts +1 -1
  55. package/lib/components/TextArea/handleKeyDown.js +2 -1
  56. package/lib/components/TextArea/handleKeyDown.js.map +3 -2
  57. package/lib/components/Toolbar/index.js +1 -1
  58. package/lib/components/Toolbar/index.js.map +2 -2
  59. package/lib/index.less +1 -1
  60. package/lib/utils/InsertTextAtPosition.d.ts +7 -0
  61. package/lib/utils/InsertTextAtPosition.js +30 -0
  62. package/lib/utils/InsertTextAtPosition.js.map +14 -6
  63. package/markdown-editor.css +1 -1
  64. package/package.json +5 -4
  65. package/src/Context.tsx +1 -0
  66. package/src/Editor.tsx +6 -1
  67. package/src/commands/title1.tsx +5 -4
  68. package/src/commands/title2.tsx +5 -4
  69. package/src/commands/title3.tsx +5 -4
  70. package/src/commands/title4.tsx +5 -4
  71. package/src/commands/title5.tsx +5 -4
  72. package/src/commands/title6.tsx +5 -4
  73. package/src/components/TextArea/Markdown.tsx +1 -1
  74. package/src/components/TextArea/Textarea.tsx +12 -3
  75. package/src/components/TextArea/handleKeyDown.tsx +3 -1
  76. package/src/components/Toolbar/index.tsx +1 -1
  77. package/src/index.less +1 -1
  78. 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": "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",
63
+ "mappings": ";;;;;;;;AAAA;AACA;AACA;AACA;AACA;AAEA,IAAIA,gCAAJ;AAEA;AACA;AACA;AACA;;AACA,SAASC,yBAAT,CAAmCC,KAAnC,EAA2F;AACzF,MAAIA,KAAK,CAACC,QAAN,KAAmB,UAAvB,EAAmC;AACjC,WAAO,KAAP;AACD;;AACD,MAAI,OAAOH,gCAAP,KAA4C,WAAhD,EAA6D;AAC3D,QAAMI,QAA6B,GAAGC,QAAQ,CAACC,aAAT,CAAuB,UAAvB,CAAtC;AACAF,IAAAA,QAAQ,CAACG,KAAT,GAAiB,GAAjB;AACAP,IAAAA,gCAAgC,GAAG,CAAC,CAACI,QAAQ,CAACI,UAA9C;AACD;;AACD,SAAOR,gCAAP;AACD;AAED;AACA;AACA;AACA;AACA;AACA;;;AACO,IAAMS,iBAAiB,GAAG,SAApBA,iBAAoB,CAC/BC,GAD+B,EAE/BC,SAF+B,EAG/BT,KAH+B,EAItB;AACT,MAAMU,OAAO,GAAGV,KAAK,CAACK,KAAtB;AACA,MAAIM,QAAQ,GAAG,CAAf;;AAEA,SAAOF,SAAS,EAAhB,EAAoB;AAClB,QAAIG,IAAI,GAAGF,OAAO,CAACD,SAAD,CAAlB;;AACA,QAAIG,IAAI,KAAK,IAAb,EAAmB;AACjBD,MAAAA,QAAQ,GAAGF,SAAS,GAAG,CAAvB;AACA;AACD;AACF;;AAEDT,EAAAA,KAAK,CAACa,KAAN;AACAb,EAAAA,KAAK,CAACc,YAAN,CAAmBN,GAAnB,EAAwBG,QAAxB,EAAkCA,QAAlC;AACAX,EAAAA,KAAK,CAACe,aAAN,CAAoB,IAAIC,KAAJ,CAAU,OAAV,EAAmB;AAAEC,IAAAA,OAAO,EAAE;AAAX,GAAnB,CAApB;AACD,CAnBM;AAqBP;AACA;AACA;AACA;AACA;;;;;AACO,SAASC,oBAAT,CAA8BlB,KAA9B,EAA6EmB,IAA7E,EAAiG;AACtG;AACAnB,EAAAA,KAAK,CAACa,KAAN,GAFsG,CAItG;;AACA,MAAKV,QAAD,CAAkBiB,SAAtB,EAAiC;AAC/B,QAAMC,OAAO,GAAIlB,QAAD,CAAkBiB,SAAlB,CAA4BE,WAA5B,EAAhB;AACAD,IAAAA,OAAO,CAACF,IAAR,GAAeA,IAAf,CAF+B,CAI/B;;AACAE,IAAAA,OAAO,CAACE,QAAR,CAAiB;AAAM;AAAvB;AACAF,IAAAA,OAAO,CAACG,MAAR;AAEA;AACD,GAdqG,CAgBtG;;;AACA,MAAMC,SAAS,GAAGtB,QAAQ,CAACuB,WAAT,IAAwBvB,QAAQ,CAACuB,WAAT,CAAqB,YAArB,EAAmC,KAAnC,EAA0CP,IAA1C,CAA1C;;AACA,MAAI,CAACM,SAAL,EAAgB;AACd,QAAME,KAAK,GAAG3B,KAAK,CAAC4B,cAApB;AACA,QAAMC,GAAG,GAAG7B,KAAK,CAAC8B,YAAlB,CAFc,CAGd;;AACA,QAAI,OAAO9B,KAAK,CAACc,YAAb,KAA8B,UAAlC,EAA8C;AAC5Cd,MAAAA,KAAK,CAACc,YAAN,CAAmBK,IAAnB;AACD,KAFD,MAEO;AACL;AACA,UAAMY,KAAK,GAAG5B,QAAQ,CAACmB,WAAT,EAAd;AACA,UAAMU,QAAQ,GAAG7B,QAAQ,CAAC8B,cAAT,CAAwBd,IAAxB,CAAjB;;AAEA,UAAIpB,yBAAyB,CAACC,KAAD,CAA7B,EAAsC;AACpC,YAAIkC,IAAI,GAAGlC,KAAK,CAACM,UAAjB,CADoC,CAGpC;;AACA,YAAI,CAAC4B,IAAL,EAAW;AACTlC,UAAAA,KAAK,CAACmC,WAAN,CAAkBH,QAAlB;AACD,SAFD,MAEO;AACL;AACA,cAAII,MAAM,GAAG,CAAb;AACA,cAAIC,SAAS,GAAG,IAAhB;AACA,cAAIC,OAAO,GAAG,IAAd;;AAEA,iBAAOJ,IAAI,KAAKG,SAAS,KAAK,IAAd,IAAsBC,OAAO,KAAK,IAAvC,CAAX,EAAyD;AACvD,gBAAMC,UAAU,GAAGL,IAAI,CAACM,SAAL,CAAgBC,MAAnC,CADuD,CAGvD;;AACA,gBAAId,KAAK,IAAIS,MAAT,IAAmBT,KAAK,IAAIS,MAAM,GAAGG,UAAzC,EAAqD;AACnDR,cAAAA,KAAK,CAACW,QAAN,CAAgBL,SAAS,GAAGH,IAA5B,EAAmCP,KAAK,GAAGS,MAA3C;AACD,aANsD,CAQvD;;;AACA,gBAAIP,GAAG,IAAIO,MAAP,IAAiBP,GAAG,IAAIO,MAAM,GAAGG,UAArC,EAAiD;AAC/CR,cAAAA,KAAK,CAACY,MAAN,CAAcL,OAAO,GAAGJ,IAAxB,EAA+BL,GAAG,GAAGO,MAArC;AACD;;AAEDA,YAAAA,MAAM,IAAIG,UAAV;AACAL,YAAAA,IAAI,GAAGA,IAAI,CAACU,WAAZ;AACD,WArBI,CAuBL;;;AACA,cAAIjB,KAAK,KAAKE,GAAd,EAAmB;AACjBE,YAAAA,KAAK,CAACc,cAAN;AACD;AACF;AACF,OAvCI,CAyCL;AACA;AACA;AACA;AACA;;;AACA,UAAI9C,yBAAyB,CAACC,KAAD,CAAzB,IAAoC+B,KAAK,CAACe,uBAAN,CAA8B7C,QAA9B,KAA2C,OAAnF,EAA4F;AAC1F;AACA8B,QAAAA,KAAK,CAACgB,UAAN,CAAiBf,QAAjB;AACD,OAHD,MAGO;AACL;AACA,YAAM3B,KAAK,GAAGL,KAAK,CAACK,KAApB;AACAL,QAAAA,KAAK,CAACK,KAAN,GAAcA,KAAK,CAAC2C,KAAN,CAAY,CAAZ,EAAerB,KAAf,IAAwBR,IAAxB,GAA+Bd,KAAK,CAAC2C,KAAN,CAAYnB,GAAZ,CAA7C;AACD;AACF,KA5Da,CA8Dd;;;AACA7B,IAAAA,KAAK,CAACiD,iBAAN,CAAwBtB,KAAK,GAAGR,IAAI,CAACsB,MAArC,EAA6Cd,KAAK,GAAGR,IAAI,CAACsB,MAA1D,EA/Dc,CAiEd;;AACA,QAAMS,CAAC,GAAG/C,QAAQ,CAACgD,WAAT,CAAqB,SAArB,CAAV;AACAD,IAAAA,CAAC,CAACE,SAAF,CAAY,OAAZ,EAAqB,IAArB,EAA2B,KAA3B;AACApD,IAAAA,KAAK,CAACe,aAAN,CAAoBmC,CAApB;AACD;AACF",
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
  }
@@ -294,7 +294,7 @@
294
294
  .w-md-editor-fullscreen {
295
295
  overflow: hidden;
296
296
  position: fixed;
297
- z-index: 9999;
297
+ z-index: 99999;
298
298
  top: 0;
299
299
  left: 0;
300
300
  right: 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uiw/react-md-editor",
3
- "version": "3.8.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.3",
56
+ "jest-environment-jsdom": "27.4.4",
57
57
  "kkt": "6.11.0",
58
58
  "lint-staged": "12.1.2",
59
- "prettier": "2.5.0",
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.1",
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
@@ -22,6 +22,7 @@ export type ContextStore = {
22
22
  scrollTop?: number;
23
23
  scrollTopPreview?: number;
24
24
  tabSize?: number;
25
+ defaultTabEnable?: boolean;
25
26
  };
26
27
 
27
28
  export type ExecuteCommandState = Pick<ContextStore, 'fullscreen' | 'preview' | 'highlightEnable'>;
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,
@@ -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',
@@ -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 { markdown, commands, fullscreen, preview, highlightEnable, extraCommands, tabSize, dispatch } =
14
- useContext(EditorContext);
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) {
@@ -46,7 +46,7 @@ export function ToolbarItems(props: IToolbarProps) {
46
46
 
47
47
  useEffect(() => {
48
48
  if (document) {
49
- document.body.style.overflow = !fullscreen ? 'initial' : 'hidden';
49
+ document.body.style.overflow = !fullscreen ? '' : 'hidden';
50
50
  }
51
51
  }, [fullscreen]);
52
52
 
package/src/index.less CHANGED
@@ -54,7 +54,7 @@
54
54
  &-fullscreen {
55
55
  overflow: hidden;
56
56
  position: fixed;
57
- z-index: 9999;
57
+ z-index: 99999;
58
58
  top: 0;
59
59
  left: 0;
60
60
  right: 0;
@@ -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