yusuketk пре 5 година
родитељ
комит
e42f51fa00

+ 74 - 33
src/client/js/components/PageEditor/LinkEditModal.jsx

@@ -10,6 +10,9 @@ import {
   ModalFooter,
 } from 'reactstrap';
 
+import Preview from './Preview';
+
+import AppContainer from '../../services/AppContainer';
 import PageContainer from '../../services/PageContainer';
 
 import PagePathAutoComplete from '../PagePathAutoComplete';
@@ -28,6 +31,9 @@ class LinkEditModal extends React.PureComponent {
       linkInputValue: '',
       labelInputValue: '',
       linkerType: 'mdLink',
+      markdown: '',
+      permalink: '',
+      isEnablePermanentLink: false,
     };
 
     this.isApplyPukiwikiLikeLinkerPlugin = window.growiRenderer.preProcessors.some(process => process.constructor.name === 'PukiwikiLikeLinker');
@@ -35,14 +41,23 @@ class LinkEditModal extends React.PureComponent {
     this.show = this.show.bind(this);
     this.hide = this.hide.bind(this);
     this.cancel = this.cancel.bind(this);
-    this.inputChangeHandler = this.inputChangeHandler.bind(this);
-    this.submitHandler = this.submitHandler.bind(this);
+    this.handleChangeLinkInput = this.handleChangeLinkInput.bind(this);
     this.handleChangeLabelInput = this.handleChangeLabelInput.bind(this);
     this.handleSelecteLinkerType = this.handleSelecteLinkerType.bind(this);
     this.toggleIsUseRelativePath = this.toggleIsUseRelativePath.bind(this);
     this.toggleIsUsePamanentLink = this.toggleIsUsePamanentLink.bind(this);
     this.save = this.save.bind(this);
     this.generateLink = this.generateLink.bind(this);
+    this.getPageWithLinkInputValue = this.getPageWithLinkInputValue.bind(this);
+    this.renderPreview = this.renderPreview.bind(this);
+  }
+
+  componentDidUpdate(prevState) {
+    const { linkInputValue: prevLinkInputValue } = prevState;
+    const { linkInputValue } = this.state;
+    if (linkInputValue !== prevLinkInputValue) {
+      this.getPageWithLinkInputValue(linkInputValue);
+    }
   }
 
   show(defaultLabelInputValue = '') {
@@ -63,19 +78,33 @@ class LinkEditModal extends React.PureComponent {
     if (this.state.linkerType === 'growiLink') {
       return;
     }
-    this.setState({ isUseRelativePath: !this.state.isUseRelativePath });
+
+    // User can't use both relativePath and permalink at the same time
+    this.setState({ isUseRelativePath: !this.state.isUseRelativePath, isUsePermanentLink: false });
   }
 
   toggleIsUsePamanentLink() {
-    this.setState({ isUsePermanentLink: !this.state.isUsePermanentLink });
+    if (!this.state.isEnablePermanentLink) {
+      return;
+    }
+
+    // User can't use both relativePath and permalink at the same time
+    this.setState({ isUsePermanentLink: !this.state.isUsePermanentLink, isUseRelativePath: false });
   }
 
   renderPreview() {
-    // TODO GW-2658
+    return (
+      <div className="linkedit-preview">
+        <Preview
+          markdown={this.state.markdown}
+          inputRef={() => {}}
+        />
+      </div>
+    );
   }
 
-  insertLinkIntoEditor() {
-    // TODO GW-2659
+  handleChangeLinkInput(inputChangeValue) {
+    this.setState({ linkInputValue: inputChangeValue, isEnablePermanentLink: false, isUsePermanentLink: false });
   }
 
   handleChangeLabelInput(label) {
@@ -99,13 +128,20 @@ class LinkEditModal extends React.PureComponent {
     this.hide();
   }
 
-  inputChangeHandler(inputChangeValue) {
-    this.setState({ linkInputValue: inputChangeValue });
-
-  }
-
-  submitHandler(submitValue) {
-    this.setState({ linkInputValue: submitValue });
+  async getPageWithLinkInputValue(path) {
+    let markdown = '';
+    let permalink = '';
+    let isEnablePermanentLink = false;
+    try {
+      const res = await this.props.appContainer.apiGet('/pages.get', { path });
+      markdown = res.page.revision.body;
+      permalink = `${window.location.origin}/${res.page.id}`;
+      isEnablePermanentLink = true;
+    }
+    catch (err) {
+      markdown = `<div class="alert alert-warning" role="alert"><strong>${err.message}</strong></div>`;
+    }
+    this.setState({ markdown, permalink, isEnablePermanentLink });
   }
 
 
@@ -116,6 +152,7 @@ class LinkEditModal extends React.PureComponent {
       labelInputValue,
       linkerType,
       isUseRelativePath,
+      isUsePermanentLink,
     } = this.state;
 
     let reshapedLink = linkInputValue;
@@ -123,6 +160,9 @@ class LinkEditModal extends React.PureComponent {
     if (isUseRelativePath && linkInputValue.match(/^\//)) {
       reshapedLink = path.relative(pageContainer.state.path, linkInputValue);
     }
+    if (isUsePermanentLink) {
+      reshapedLink = this.state.permalink;
+    }
 
     if (linkerType === 'pukiwikiLink') {
       return `[[${labelInputValue}>${reshapedLink}]]`;
@@ -150,24 +190,11 @@ class LinkEditModal extends React.PureComponent {
                   <label htmlFor="linkInput">Link</label>
                   <div className="input-group">
                     <PagePathAutoComplete
-                      onInputChange={this.inputChangeHandler}
-                      onSubmit={this.submitHandler}
+                      onInputChange={this.handleChangeLinkInput}
+                      onSubmit={this.getPageWithLinkInputValue}
                     />
                   </div>
                 </div>
-                <div className="form-inline">
-                  <div className="custom-control custom-checkbox custom-checkbox-info">
-                    <input
-                      className="custom-control-input"
-                      id="permanentLink"
-                      type="checkbox"
-                      checked={this.state.isUsePamanentLink}
-                    />
-                    <label className="custom-control-label" htmlFor="permanentLink" onClick={this.toggleIsUsePamanentLink}>
-                      Use permanent link
-                    </label>
-                  </div>
-                </div>
               </form>
 
               <div className="card">
@@ -227,14 +254,27 @@ class LinkEditModal extends React.PureComponent {
                         </label>
                       </div>
                     </div>
+                    <div className="form-inline">
+                      <div className="custom-control custom-checkbox custom-checkbox-info">
+                        <input
+                          className="custom-control-input"
+                          id="permanentLink"
+                          type="checkbox"
+                          checked={this.state.isUsePermanentLink}
+                          disabled={!this.state.isEnablePermanentLink}
+                        />
+                        <label className="custom-control-label" htmlFor="permanentLink" onClick={this.toggleIsUsePamanentLink}>
+                          Use permanent link
+                        </label>
+                      </div>
+                    </div>
                   </form>
                 </div>
               </div>
             </div>
             <div className="col-12 col-lg-6">
-              <div className="d-block d-lg-none">
-                {this.renderPreview}
-                render preview
+              <div className="d-block mb-3">
+                {this.renderPreview()}
               </div>
             </div>
           </div>
@@ -254,6 +294,7 @@ class LinkEditModal extends React.PureComponent {
 }
 
 LinkEditModal.propTypes = {
+  appContainer: PropTypes.instanceOf(AppContainer).isRequired,
   pageContainer: PropTypes.instanceOf(PageContainer).isRequired,
   onSave: PropTypes.func,
 };
@@ -261,6 +302,6 @@ LinkEditModal.propTypes = {
 /**
  * Wrapper component for using unstated
  */
-const LinkEditModalWrapper = withUnstatedContainers(LinkEditModal, [PageContainer]);
+const LinkEditModalWrapper = withUnstatedContainers(LinkEditModal, [AppContainer, PageContainer]);
 
 export default LinkEditModalWrapper;

+ 76 - 7
src/client/js/components/PageEditor/MarkdownLinkUtil.js

@@ -4,17 +4,67 @@
 class MarkdownLinkUtil {
 
   constructor() {
-    // https://regex101.com/r/1UuWBJ/8
-    this.linePartOfLink = /(\[+(.*)+\]){1}(\(+(.*)+\)){1}/;
     this.isInLink = this.isInLink.bind(this);
+    this.getBeginningAndEndOfTheClosestLinkToCursor = this.getBeginningAndEndOfTheClosestLinkToCursor.bind(this);
   }
 
+  // return text as markdown link if the cursor on markdown link else return text as default label of new link.
   getMarkdownLink(editor) {
-    const isInLink = true;
-    if (isInLink) {
-      return; // with param
+    console.log(this.isInLink(editor));
+    if (!this.isInLink(editor)) {
+      return editor.getDoc().getSelection();
     }
-    return editor.getDoc().getSelection();
+    const curPos = editor.getCursor();
+
+    const { beginningOfLink, endOfLink } = this.getBeginningAndEndOfTheClosestLinkToCursor(editor);
+    console.log(editor.getDoc().getLine(curPos.line).subString(beginningOfLink, endOfLink));
+    return editor.getDoc().getLine(curPos.line).subString(beginningOfLink, endOfLink);
+  }
+
+
+  /**
+   * return the postion of the BOL(beginning of link)
+   * (If the cursor is not in a link, return its position)
+   */
+  getBot(editor) {
+    const curPos = editor.getCursor();
+    if (!this.isInLink(editor)) {
+      return { line: curPos.line, ch: curPos.ch };
+    }
+
+    const firstLine = editor.getDoc().firstLine();
+    let line = curPos.line - 1;
+    for (; line >= firstLine; line--) {
+      const strLine = editor.getDoc().getLine(line);
+      if (!this.linePartOfTableRE.test(strLine)) {
+        break;
+      }
+    }
+    const botLine = Math.max(firstLine, line + 1);
+    return { line: botLine, ch: 0 };
+  }
+
+  /**
+   * return the postion of the EOT(end of table)
+   * (If the cursor is not in a table, return its position)
+   */
+  getEot(editor) {
+    const curPos = editor.getCursor();
+    if (!this.isInTable(editor)) {
+      return { line: curPos.line, ch: curPos.ch };
+    }
+
+    const lastLine = editor.getDoc().lastLine();
+    let line = curPos.line + 1;
+    for (; line <= lastLine; line++) {
+      const strLine = editor.getDoc().getLine(line);
+      if (!this.linePartOfTableRE.test(strLine)) {
+        break;
+      }
+    }
+    const eotLine = Math.min(line - 1, lastLine);
+    const lineLength = editor.getDoc().getLine(eotLine).length;
+    return { line: eotLine, ch: lineLength };
   }
 
   getSelectedTextInEditor(editor) {
@@ -27,7 +77,26 @@ class MarkdownLinkUtil {
 
   isInLink(editor) {
     const curPos = editor.getCursor();
-    return this.linePartOfLink.test(editor.getDoc().getLine(curPos.line));
+    const { endOfLink } = this.getBeginningAndEndOfTheClosestLinkToCursor(editor);
+    return curPos < endOfLink;
+  }
+
+  // return beginning index and end index of the closest link to cursor
+  // if there is no link, return { beginningOfLink: -1, endOfLink: -1}
+  getBeginningAndEndOfTheClosestLinkToCursor(editor) {
+    const curPos = editor.getCursor();
+    const line = editor.getDoc().getLine(curPos.line);
+    let beginningOfLink = line.lastIndexOf('[', curPos.ch);
+    let endOfLink = line.indexOf(']', beginningOfLink);
+    if (line.charAt(endOfLink + 1) === '(') {
+      endOfLink = line.indexOf(')', endOfLink);
+    }
+    else if (line.charAt(beginningOfLink - 1) === '[' && line.charAt(endOfLink + 1) === ']') { // todo 先頭が[一つの時にえらーにならないか調査する
+      beginningOfLink -= 1;
+      endOfLink += 1;
+    }
+
+    return { beginningOfLink, endOfLink };
   }
 
 }