Просмотр исходного кода

Merge pull request #8219 from weseek/imprv/134164-make-button-add-prefix

imprv: Able to add prefix with toolbar buttons
Yuki Takei 2 лет назад
Родитель
Сommit
25b3ec5a23

+ 9 - 11
packages/editor/src/components/CodeMirrorEditor/Toolbar/TextFormatTools.tsx

@@ -3,7 +3,6 @@ import { useCallback, useState } from 'react';
 import { Collapse } from 'reactstrap';
 
 import type { GlobalCodeMirrorEditorKey } from '../../../consts';
-import { useInsertHeader } from '../../../services/codemirror-editor/use-codemirror-editor/utils/insert-header';
 import { useCodeMirrorEditorIsolated } from '../../../stores';
 
 import styles from './TextFormatTools.module.scss';
@@ -41,7 +40,6 @@ export const TextFormatTools = (props: TextFormatToolsType): JSX.Element => {
   const { editorKey } = props;
   const [isOpen, setOpen] = useState(false);
   const { data: codeMirrorEditor } = useCodeMirrorEditorIsolated(editorKey);
-  const insertHeader = useInsertHeader();
 
   const toggle = useCallback(() => {
     setOpen(bool => !bool);
@@ -51,8 +49,8 @@ export const TextFormatTools = (props: TextFormatToolsType): JSX.Element => {
     codeMirrorEditor?.insertMarkdownElements(prefix, suffix);
   };
 
-  const onClickAddHeaderToSelection = () => {
-    insertHeader(codeMirrorEditor?.view);
+  const onClickInsertPrefix = (prefix: string, noSpaceIfPrefixExists?: boolean) => {
+    codeMirrorEditor?.insertPrefix(prefix, noSpaceIfPrefixExists);
   };
 
   return (
@@ -64,28 +62,28 @@ export const TextFormatTools = (props: TextFormatToolsType): JSX.Element => {
           <button type="button" className="btn btn-toolbar-button" onClick={() => onClickInsertMarkdownElements('**', '**')}>
             <span className="material-symbols-outlined fs-5">format_bold</span>
           </button>
-          <button type="button" className="btn btn-toolbar-button">
-            <span className="material-symbols-outlined fs-5" onClick={() => onClickInsertMarkdownElements('*', '*')}>format_italic</span>
+          <button type="button" className="btn btn-toolbar-button" onClick={() => onClickInsertMarkdownElements('*', '*')}>
+            <span className="material-symbols-outlined fs-5">format_italic</span>
           </button>
           <button type="button" className="btn btn-toolbar-button" onClick={() => onClickInsertMarkdownElements('~', '~')}>
             <span className="material-symbols-outlined fs-5">format_strikethrough</span>
           </button>
-          <button type="button" className="btn btn-toolbar-button" onClick={onClickAddHeaderToSelection}>
+          <button type="button" className="btn btn-toolbar-button" onClick={() => onClickInsertPrefix('#', true)}>
             <span className="material-symbols-outlined fs-5">block</span>
           </button>
           <button type="button" className="btn btn-toolbar-button" onClick={() => onClickInsertMarkdownElements('`', '`')}>
             <span className="material-symbols-outlined fs-5">code</span>
           </button>
-          <button type="button" className="btn btn-toolbar-button">
+          <button type="button" className="btn btn-toolbar-button" onClick={() => onClickInsertPrefix('-')}>
             <span className="material-symbols-outlined fs-5">format_list_bulleted</span>
           </button>
-          <button type="button" className="btn btn-toolbar-button">
+          <button type="button" className="btn btn-toolbar-button" onClick={() => onClickInsertPrefix('1.')}>
             <span className="material-symbols-outlined fs-5">format_list_numbered</span>
           </button>
-          <button type="button" className="btn btn-toolbar-button">
+          <button type="button" className="btn btn-toolbar-button" onClick={() => onClickInsertPrefix('>')}>
             <span className="material-symbols-outlined fs-5">block</span>
           </button>
-          <button type="button" className="btn btn-toolbar-button">
+          <button type="button" className="btn btn-toolbar-button" onClick={() => onClickInsertPrefix('- [ ]')}>
             <span className="material-symbols-outlined fs-5">checklist</span>
           </button>
         </div>

+ 4 - 0
packages/editor/src/services/codemirror-editor/use-codemirror-editor/use-codemirror-editor.ts

@@ -17,6 +17,7 @@ import { useFocus, type Focus } from './utils/focus';
 import { useGetDoc, type GetDoc } from './utils/get-doc';
 import { useInitDoc, type InitDoc } from './utils/init-doc';
 import { useInsertMarkdownElements, type InsertMarkdowElements } from './utils/insert-markdown-elements';
+import { useInsertPrefix, type InsertPrefix } from './utils/insert-prefix';
 import { useInsertText, type InsertText } from './utils/insert-text';
 import { useReplaceText, type ReplaceText } from './utils/replace-text';
 import { useSetCaretLine, type SetCaretLine } from './utils/set-caret-line';
@@ -39,6 +40,7 @@ type UseCodeMirrorEditorUtils = {
   insertText: InsertText,
   replaceText: ReplaceText,
   insertMarkdownElements: InsertMarkdowElements,
+  insertPrefix: InsertPrefix,
 }
 export type UseCodeMirrorEditor = {
   state: EditorState | undefined;
@@ -92,6 +94,7 @@ export const useCodeMirrorEditor = (props?: UseCodeMirror): UseCodeMirrorEditor
   const insertText = useInsertText(view);
   const replaceText = useReplaceText(view);
   const insertMarkdownElements = useInsertMarkdownElements(view);
+  const insertPrefix = useInsertPrefix(view);
 
   return {
     state,
@@ -104,5 +107,6 @@ export const useCodeMirrorEditor = (props?: UseCodeMirror): UseCodeMirrorEditor
     insertText,
     replaceText,
     insertMarkdownElements,
+    insertPrefix,
   };
 };

+ 0 - 31
packages/editor/src/services/codemirror-editor/use-codemirror-editor/utils/insert-header.ts

@@ -1,31 +0,0 @@
-import { useCallback } from 'react';
-
-import { EditorView } from '@codemirror/view';
-
-type InsertHeader = (view?: EditorView) => void;
-
-export const useInsertHeader = (): InsertHeader => {
-  return useCallback((view?: EditorView) => {
-    if (view == null) {
-      return;
-    }
-    let prefix = '#';
-    const cursorPos = view.state.selection.main.head;
-    const line = view.state.doc.lineAt(cursorPos);
-    const insertPos = line.text.startsWith(prefix) ? cursorPos - 1 : cursorPos;
-
-    if (!line.text.startsWith(prefix)) {
-      prefix += ' ';
-    }
-
-    view.dispatch({
-      changes: {
-        from: insertPos,
-        to: insertPos,
-        insert: prefix,
-      },
-      selection: { anchor: cursorPos + prefix.length },
-    });
-    view.focus();
-  }, []);
-};

+ 35 - 0
packages/editor/src/services/codemirror-editor/use-codemirror-editor/utils/insert-prefix.ts

@@ -0,0 +1,35 @@
+import { useCallback } from 'react';
+
+import { EditorView } from '@codemirror/view';
+
+export type InsertPrefix = (prefix: string, noSpaceIfPrefixExists?: boolean) => void;
+
+export const useInsertPrefix = (view?: EditorView): InsertPrefix => {
+  return useCallback((prefix: string, noSpaceIfPrefixExists = false) => {
+    if (view == null) {
+      return;
+    }
+
+    // get the line numbers of the selected range
+    const { from, to } = view.state.selection.main;
+    const startLine = view.state.doc.lineAt(from);
+    const endLine = view.state.doc.lineAt(to);
+
+    // Insert prefix for each line
+    const lines = [];
+    let insertTextLength = 0;
+    for (let i = startLine.number; i <= endLine.number; i++) {
+      const line = view.state.doc.line(i);
+      const insertText = noSpaceIfPrefixExists && line.text.startsWith(prefix)
+        ? prefix
+        : `${prefix} `;
+      insertTextLength += insertText.length;
+      lines.push({ from: line.from, insert: insertText });
+    }
+    view.dispatch({ changes: lines });
+
+    // move the cursor to the end of the selected line
+    view.dispatch({ selection: { anchor: endLine.to + insertTextLength } });
+    view.focus();
+  }, [view]);
+};