Przeglądaj źródła

Merge pull request #8267 from weseek/imprv/135396-change-markdowntable-util-to-ts

imprv: Change markdownTableUtil to TS
soma 2 lat temu
rodzic
commit
425bf3993e

+ 15 - 13
apps/app/src/components/PageEditor/MarkdownTableInterceptor.js → apps/app/_obsolete/src/components/PageEditor/MarkdownTableInterceptor.js

@@ -2,7 +2,10 @@ import { BasicInterceptor } from '@growi/core/dist/utils';
 
 import MarkdownTable from '~/client/models/MarkdownTable';
 
-import mtu from './MarkdownTableUtil';
+import {
+  getStrFromBot, addRowToMarkdownTable, getStrToEot, isEndOfLine, mergeMarkdownTable, replaceFocusedMarkdownTableWithEditor,
+  isInTable, emptyLineOfTableRE,
+} from '../../../../src/components/PageEditor/markdown-table-util-for-editor';
 
 /**
  * Interceptor for markdown table
@@ -27,24 +30,24 @@ export default class MarkdownTableInterceptor extends BasicInterceptor {
 
   addRow(cm) {
     // get lines all of table from current position to beginning of table
-    const strFromBot = mtu.getStrFromBot(cm);
+    const strFromBot = getStrFromBot(cm);
     let table = MarkdownTable.fromMarkdownString(strFromBot);
 
-    mtu.addRowToMarkdownTable(table);
+    addRowToMarkdownTable(table);
 
-    const strToEot = mtu.getStrToEot(cm);
+    const strToEot = getStrToEot(cm);
     const tableBottom = MarkdownTable.fromMarkdownString(strToEot);
     if (tableBottom.table.length > 0) {
-      table = mtu.mergeMarkdownTable([table, tableBottom]);
+      table = mergeMarkdownTable([table, tableBottom]);
     }
 
-    mtu.replaceMarkdownTableWithReformed(cm, table);
+    replaceFocusedMarkdownTableWithEditor(cm, table);
   }
 
   reformTable(cm) {
-    const tableStr = mtu.getStrFromBot(cm) + mtu.getStrToEot(cm);
+    const tableStr = getStrFromBot(cm) + getStrToEot(cm);
     const table = MarkdownTable.fromMarkdownString(tableStr);
-    mtu.replaceMarkdownTableWithReformed(cm, table);
+    replaceFocusedMarkdownTableWithEditor(cm, table);
   }
 
   removeRow(editor) {
@@ -67,16 +70,15 @@ export default class MarkdownTableInterceptor extends BasicInterceptor {
 
     const cm = editor.getCodeMirror();
 
-    const isInTable = mtu.isInTable(cm);
-    const isLastRow = mtu.getStrToEot(cm) === editor.getStrToEol();
+    const isLastRow = getStrToEot(cm) === editor.getStrToEol();
 
-    if (isInTable) {
+    if (isInTable(cm)) {
       // at EOL in the table
-      if (mtu.isEndOfLine(cm)) {
+      if (isEndOfLine(cm)) {
         this.addRow(cm);
       }
       // last empty row
-      else if (isLastRow && mtu.emptyLineOfTableRE.test(editor.getStrFromBol() + editor.getStrToEol())) {
+      else if (isLastRow && emptyLineOfTableRE.test(editor.getStrFromBol() + editor.getStrToEol())) {
         this.removeRow(editor);
       }
       else {

+ 3 - 3
apps/app/src/client/services/side-effects/handsontable-modal-launcher-for-view.ts

@@ -4,7 +4,7 @@ import EventEmitter from 'events';
 
 import MarkdownTable from '~/client/models/MarkdownTable';
 import { useSaveOrUpdate } from '~/client/services/page-operation';
-import mtu from '~/components/PageEditor/MarkdownTableUtil';
+import { getMarkdownTableFromLine, replaceMarkdownTableInMarkdown } from '~/components/PageEditor/markdown-table-util-for-view';
 import type { OptionsToSave } from '~/interfaces/page-operation';
 import { useShareLinkId } from '~/stores/context';
 import { useHandsontableModal } from '~/stores/modal';
@@ -40,7 +40,7 @@ export const useHandsontableModalLauncherForView = (opts?: {
     }
 
     const currentMarkdown = currentPage.revision.body;
-    const newMarkdown = mtu.replaceMarkdownTableInMarkdown(table, currentMarkdown, bol, eol);
+    const newMarkdown = replaceMarkdownTableInMarkdown(table, currentMarkdown, bol, eol);
 
     const grantUserGroupIds = currentPage.grantedGroups.map((g) => {
       return {
@@ -82,7 +82,7 @@ export const useHandsontableModalLauncherForView = (opts?: {
 
     const handler = (bol: number, eol: number) => {
       const markdown = currentPage.revision.body;
-      const currentMarkdownTable = mtu.getMarkdownTableFromLine(markdown, bol, eol);
+      const currentMarkdownTable = getMarkdownTableFromLine(markdown, bol, eol);
       openHandsontableModal(currentMarkdownTable, false, table => saveByHandsontableModal(table, bol, eol));
     };
     globalEmitter.on('launchHandsonTableModal', handler);

+ 5 - 2
apps/app/src/components/PageEditor/HandsontableModal.tsx

@@ -11,7 +11,7 @@ import {
 import { debounce } from 'throttle-debounce';
 
 import MarkdownTable from '~/client/models/MarkdownTable';
-import mtu from '~/components/PageEditor/MarkdownTableUtil';
+import { replaceFocusedMarkdownTableWithEditor } from '~/components/PageEditor/markdown-table-util-for-editor';
 import { useHandsontableModal } from '~/stores/modal';
 
 import ExpandOrContractButton from '../ExpandOrContractButton';
@@ -166,7 +166,10 @@ export const HandsontableModal = (): JSX.Element => {
       return;
     }
 
-    mtu.replaceFocusedMarkdownTableWithEditor(editor, newMarkdownTable);
+    if (editor == null) {
+      return;
+    }
+    replaceFocusedMarkdownTableWithEditor(editor, newMarkdownTable);
     cancel();
   };
 

+ 0 - 203
apps/app/src/components/PageEditor/MarkdownTableUtil.js

@@ -1,203 +0,0 @@
-import MarkdownTable from '~/client/models/MarkdownTable';
-
-/**
- * Utility for markdown table
- */
-class MarkdownTableUtil {
-
-  constructor() {
-    // https://github.com/markdown-it/markdown-it/blob/d29f421927e93e88daf75f22089a3e732e195bd2/lib/rules_block/table.js#L83
-    this.tableAlignmentLineRE = /^[-:|][-:|\s]*$/;
-    this.tableAlignmentLineNegRE = /^[^-:]*$/; // it is need to check to ignore empty row which is matched above RE
-    // https://regex101.com/r/7BN2fR/10
-    this.linePartOfTableRE = /^([^\r\n|]*)\|(([^\r\n|]*\|)+)$/;
-    // https://regex101.com/r/1UuWBJ/3
-    this.emptyLineOfTableRE = /^([^\r\n|]*)\|((\s*\|)+)$/;
-
-    this.curPos = this.curPos.bind(this);
-    this.getBot = this.getBot.bind(this);
-    this.getEot = this.getEot.bind(this);
-    this.getStrFromBot = this.getStrFromBot.bind(this);
-    this.getStrToEot = this.getStrToEot.bind(this);
-    this.isInTable = this.isInTable.bind(this);
-    this.replaceFocusedMarkdownTableWithEditor = this.replaceFocusedMarkdownTableWithEditor.bind(this);
-    this.replaceMarkdownTableWithReformed = this.replaceFocusedMarkdownTableWithEditor; // alias
-  }
-
-  curPos(editor) {
-    return editor.state.selection.main.head;
-  }
-
-  /**
-   * return the postion of the BOT(beginning of table)
-   * (If the cursor is not in a table, return its position)
-   */
-  getBot(editor) {
-    if (!this.isInTable(editor)) {
-      return this.curPos(editor);
-    }
-
-    const doc = editor.state.doc;
-    const firstLine = 1;
-    let line = doc.lineAt(this.curPos(editor)).number - 1;
-    for (; line >= firstLine; line--) {
-      const strLine = doc.line(line).text;
-      if (!this.linePartOfTableRE.test(strLine)) {
-        break;
-      }
-    }
-    const botLine = Math.max(firstLine, line + 1);
-    return doc.line(botLine).from;
-  }
-
-  /**
-   * return the postion of the EOT(end of table)
-   * (If the cursor is not in a table, return its position)
-   */
-  getEot(editor) {
-    if (!this.isInTable(editor)) {
-      return this.curPos(editor);
-    }
-
-    const doc = editor.state.doc;
-    const lastLine = doc.lines;
-    let line = doc.lineAt(this.curPos(editor)).number + 1;
-    for (; line <= lastLine; line++) {
-      const strLine = doc.line(line).text;
-      if (!this.linePartOfTableRE.test(strLine)) {
-        break;
-      }
-    }
-    const eotLine = Math.min(line - 1, lastLine);
-    return doc.line(eotLine).to;
-  }
-
-  /**
-   * return strings from BOT(beginning of table) to the cursor position
-   */
-  getStrFromBot(editor) {
-    return editor.state.sliceDoc(this.getBot(editor), this.curPos(editor));
-  }
-
-  /**
-   * return strings from the cursor position to EOT(end of table)
-   */
-  getStrToEot(editor) {
-    return editor.state.sliceDoc(this.curPos(editor), this.getEot(editor));
-  }
-
-  /**
-   * return MarkdownTable instance of the table where the cursor is
-   * (If the cursor is not in a table, return null)
-   */
-  getMarkdownTable(editor) {
-    if (!this.isInTable(editor)) {
-      return null;
-    }
-
-    const strFromBotToEot = editor.state.sliceDoc(this.getBot(editor), this.getEot(editor));
-    return MarkdownTable.fromMarkdownString(strFromBotToEot);
-  }
-
-  getMarkdownTableFromLine(markdown, bol, eol) {
-    const tableLines = markdown.split(/\r\n|\r|\n/).slice(bol - 1, eol).join('\n');
-    return MarkdownTable.fromMarkdownString(tableLines);
-  }
-
-  /**
-   * return boolean value whether the cursor position is end of line
-   */
-  isEndOfLine(editor) {
-    return this.curPos(editor) === editor.state.doc.lineAt(this.curPos(editor)).to;
-  }
-
-  /**
-   * return boolean value whether the cursor position is in a table
-   */
-  isInTable(editor) {
-    const lineText = editor.state.doc.lineAt(this.curPos(editor)).text;
-    return this.linePartOfTableRE.test(lineText);
-  }
-
-  /**
-   * add a row at the end
-   * (This function overwrite directory markdown table specified as argument.)
-   * @param {MarkdownTable} markdown table
-   */
-  addRowToMarkdownTable(mdtable) {
-    const numCol = mdtable.table.length > 0 ? mdtable.table[0].length : 1;
-    const newRow = [];
-    (new Array(numCol)).forEach(() => { return newRow.push('') }); // create cols
-    mdtable.table.push(newRow);
-  }
-
-  /**
-   * return markdown table that is merged all of markdown table in array
-   * (The merged markdown table options are used for the first markdown table.)
-   * @param {Array} array of markdown table
-   */
-  mergeMarkdownTable(mdtableList) {
-    if (mdtableList == null || !(mdtableList instanceof Array)) {
-      return undefined;
-    }
-
-    let newTable = [];
-    const options = mdtableList[0].options; // use option of first markdown-table
-    mdtableList.forEach((mdtable) => {
-      newTable = newTable.concat(mdtable.table);
-    });
-    return new MarkdownTable(newTable, options);
-  }
-
-  /**
-   * replace focused markdown table with editor
-   * (A replaced table is reformed by markdown-table.)
-   * @param {MarkdownTable} table
-   */
-  replaceFocusedMarkdownTableWithEditor(editor, table) {
-    const botPos = this.getBot(editor);
-    const eotPos = this.getEot(editor);
-
-    editor.dispatch({
-      changes: {
-        from: botPos,
-        to: eotPos,
-        insert: table.toString(),
-      },
-    });
-    editor.dispatch({
-      selection: { anchor: editor.state.doc.lineAt(eotPos).to },
-    });
-    editor.focus();
-  }
-
-  /**
-   * return markdown where the markdown table specified by line number params is replaced to the markdown table specified by table param
-   * @param {string} markdown
-   * @param {MarkdownTable} table
-   * @param beginLineNumber
-   * @param endLineNumber
-   */
-  replaceMarkdownTableInMarkdown(table, markdown, beginLineNumber, endLineNumber) {
-    const splitMarkdown = markdown.split(/\r\n|\r|\n/);
-    const markdownBeforeTable = splitMarkdown.slice(0, beginLineNumber - 1);
-    const markdownAfterTable = splitMarkdown.slice(endLineNumber);
-
-    let newMarkdown = '';
-    if (markdownBeforeTable.length > 0) {
-      newMarkdown += `${markdownBeforeTable.join('\n')}\n`;
-    }
-    newMarkdown += table;
-    if (markdownAfterTable.length > 0) {
-      newMarkdown += `\n${markdownAfterTable.join('\n')}`;
-    }
-
-    return newMarkdown;
-  }
-
-}
-
-// singleton pattern
-const instance = new MarkdownTableUtil();
-Object.freeze(instance);
-export default instance;

+ 147 - 0
apps/app/src/components/PageEditor/markdown-table-util-for-editor.ts

@@ -0,0 +1,147 @@
+import type { EditorView } from '@codemirror/view';
+
+import MarkdownTable from '~/client/models/MarkdownTable';
+
+// https://regex101.com/r/7BN2fR/10
+const linePartOfTableRE = /^([^\r\n|]*)\|(([^\r\n|]*\|)+)$/;
+// https://regex101.com/r/1UuWBJ/3
+export const emptyLineOfTableRE = /^([^\r\n|]*)\|((\s*\|)+)$/;
+
+const curPos = (editor: EditorView): number => {
+  return editor.state.selection.main.head;
+};
+
+/**
+   * return boolean value whether the cursor position is in a table
+   */
+export const isInTable = (editor: EditorView): boolean => {
+  const lineText = editor.state.doc.lineAt(curPos(editor)).text;
+  return linePartOfTableRE.test(lineText);
+};
+
+/**
+   * return the postion of the BOT(beginning of table)
+   * (If the cursor is not in a table, return its position)
+   */
+const getBot = (editor: EditorView): number => {
+  if (!isInTable(editor)) {
+    return curPos(editor);
+  }
+
+  const doc = editor.state.doc;
+  const firstLine = 1;
+  let line = doc.lineAt(curPos(editor)).number - 1;
+  for (; line >= firstLine; line--) {
+    const strLine = doc.line(line).text;
+    if (!linePartOfTableRE.test(strLine)) {
+      break;
+    }
+  }
+  const botLine = Math.max(firstLine, line + 1);
+  return doc.line(botLine).from;
+};
+
+/**
+   * return the postion of the EOT(end of table)
+   * (If the cursor is not in a table, return its position)
+   */
+const getEot = (editor: EditorView): number => {
+  if (!isInTable(editor)) {
+    return curPos(editor);
+  }
+
+  const doc = editor.state.doc;
+  const lastLine = doc.lines;
+  let line = doc.lineAt(curPos(editor)).number + 1;
+  for (; line <= lastLine; line++) {
+    const strLine = doc.line(line).text;
+    if (!linePartOfTableRE.test(strLine)) {
+      break;
+    }
+  }
+  const eotLine = Math.min(line - 1, lastLine);
+  return doc.line(eotLine).to;
+};
+
+/**
+   * return strings from BOT(beginning of table) to the cursor position
+   */
+export const getStrFromBot = (editor: EditorView): string => {
+  return editor.state.sliceDoc(getBot(editor), curPos(editor));
+};
+
+/**
+   * return strings from the cursor position to EOT(end of table)
+   */
+export const getStrToEot = (editor: EditorView): string => {
+  return editor.state.sliceDoc(curPos(editor), getEot(editor));
+};
+
+/**
+   * return MarkdownTable instance of the table where the cursor is
+   * (If the cursor is not in a table, return null)
+   */
+export const getMarkdownTable = (editor: EditorView): MarkdownTable | undefined => {
+  if (!isInTable(editor)) {
+    return;
+  }
+
+  const strFromBotToEot = editor.state.sliceDoc(getBot(editor), getEot(editor));
+  return MarkdownTable.fromMarkdownString(strFromBotToEot);
+};
+
+/**
+   * return boolean value whether the cursor position is end of line
+   */
+export const isEndOfLine = (editor: EditorView): boolean => {
+  return curPos(editor) === editor.state.doc.lineAt(curPos(editor)).to;
+};
+
+/**
+   * add a row at the end
+   * (This function overwrite directory markdown table specified as argument.)
+   */
+export const addRowToMarkdownTable = (mdtable: MarkdownTable): any => {
+  const numCol = mdtable.table.length > 0 ? mdtable.table[0].length : 1;
+  const newRow: string[] = [];
+  (new Array(numCol)).forEach(() => { return newRow.push('') }); // create cols
+  mdtable.table.push(newRow);
+};
+
+/**
+   * return markdown table that is merged all of markdown table in array
+   * (The merged markdown table options are used for the first markdown table.)
+   */
+export const mergeMarkdownTable = (mdtableList: MarkdownTable): MarkdownTable | undefined => {
+  if (mdtableList == null || !(mdtableList instanceof Array)) {
+    return undefined;
+  }
+
+  let newTable = [];
+  const options = mdtableList[0].options; // use option of first markdown-table
+  mdtableList.forEach((mdtable) => {
+    newTable = newTable.concat(mdtable.table);
+  });
+  return (new MarkdownTable(newTable, options));
+};
+
+/**
+   * replace focused markdown table with editor
+   * (A replaced table is reformed by markdown-table.)
+   */
+export const replaceFocusedMarkdownTableWithEditor = (editor: EditorView, table: MarkdownTable): void => {
+  const botPos = getBot(editor);
+  const eotPos = getEot(editor);
+
+  editor.dispatch({
+    changes: {
+      from: botPos,
+      to: eotPos,
+      insert: table.toString(),
+    },
+  });
+  editor.dispatch({
+    selection: { anchor: editor.state.doc.lineAt(curPos(editor)).to },
+  });
+  editor.focus();
+};

+ 26 - 0
apps/app/src/components/PageEditor/markdown-table-util-for-view.ts

@@ -0,0 +1,26 @@
+import MarkdownTable from '~/client/models/MarkdownTable';
+
+export const getMarkdownTableFromLine = (markdown: string, bol: number, eol: number): MarkdownTable => {
+  const tableLines = markdown.split(/\r\n|\r|\n/).slice(bol - 1, eol).join('\n');
+  return MarkdownTable.fromMarkdownString(tableLines);
+};
+
+/**
+   * return markdown where the markdown table specified by line number params is replaced to the markdown table specified by table param
+   */
+export const replaceMarkdownTableInMarkdown = (table: MarkdownTable, markdown: string, beginLineNumber: number, endLineNumber: number): string => {
+  const splitMarkdown = markdown.split(/\r\n|\r|\n/);
+  const markdownBeforeTable = splitMarkdown.slice(0, beginLineNumber - 1);
+  const markdownAfterTable = splitMarkdown.slice(endLineNumber);
+
+  let newMarkdown = '';
+  if (markdownBeforeTable.length > 0) {
+    newMarkdown += `${markdownBeforeTable.join('\n')}\n`;
+  }
+  newMarkdown += table;
+  if (markdownAfterTable.length > 0) {
+    newMarkdown += `\n${markdownAfterTable.join('\n')}`;
+  }
+
+  return newMarkdown;
+};

+ 1 - 1
packages/editor/src/components/CodeMirrorEditor/Toolbar/TableButton.tsx

@@ -14,7 +14,7 @@ export const TableButton = (props: Props): JSX.Element => {
   const editor = codeMirrorEditor?.view;
   const openTableModalHandler = useCallback(() => {
     openTableModal(editor);
-  }, [editor]);
+  }, [editor, openTableModal]);
 
   return (
     <button type="button" className="btn btn-toolbar-button" onClick={openTableModalHandler}>