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

Merge pull request #622 from weseek/feat/enable-saving-with-handsonmodal-button-in-view-panel

Feat/enable saving with handsontable button in view panel
Yuki Takei 7 лет назад
Родитель
Сommit
b98d8ff05f

+ 52 - 53
src/client/js/app.js

@@ -110,59 +110,11 @@ if (isEnabledPlugins) {
   crowiPlugin.installAll(crowi, crowiRenderer);
 }
 
-// setup renderer after plugins are installed
-crowiRenderer.setup();
-
-// restore draft when the first time to edit
-const draft = crowi.findDraft(pagePath);
-if (!pageRevisionId && draft != null) {
-  markdown = draft;
-}
-
 /**
- * define components
- *  key: id of element
- *  value: React Element
+ * component store
  */
-const componentMappings = {
-  'search-top': <HeaderSearchBox crowi={crowi} />,
-  'search-sidebar': <HeaderSearchBox crowi={crowi} />,
-  'search-page': <SearchPage crowi={crowi} crowiRenderer={crowiRenderer} />,
-  'page-list-search': <PageListSearch crowi={crowi} />,
-
-  //'revision-history': <PageHistory pageId={pageId} />,
-  'seen-user-list': <SeenUserList pageId={pageId} crowi={crowi} />,
-  'bookmark-button': <BookmarkButton pageId={pageId} crowi={crowi} />,
-  'bookmark-button-lg': <BookmarkButton pageId={pageId} crowi={crowi} size="lg" />,
-
-  'page-name-input': <NewPageNameInput crowi={crowi} parentPageName={pagePath} />,
-
-};
-// additional definitions if data exists
-if (pageId) {
-  componentMappings['page-comments-list'] = <PageComments pageId={pageId} revisionId={pageRevisionId} revisionCreatedAt={pageRevisionCreatedAt} crowi={crowi} crowiOriginRenderer={crowiRenderer} />;
-  componentMappings['page-attachment'] = <PageAttachment pageId={pageId} pageContent={pageContent} crowi={crowi} />;
-}
-if (pagePath) {
-  componentMappings['page'] = <Page crowi={crowi} crowiRenderer={crowiRenderer} markdown={markdown} pagePath={pagePath} showHeadEditButton={true} />;
-  componentMappings['revision-path'] = <RevisionPath pagePath={pagePath} crowi={crowi} />;
-  componentMappings['revision-url'] = <RevisionUrl pageId={pageId} pagePath={pagePath} />;
-}
-
 let componentInstances = {};
 
-Object.keys(componentMappings).forEach((key) => {
-  const elem = document.getElementById(key);
-  if (elem) {
-    componentInstances[key] = ReactDOM.render(componentMappings[key], elem);
-  }
-});
-
-// set page if exists
-if (componentInstances['page'] != null) {
-  crowi.setPage(componentInstances['page']);
-}
-
 /**
  * save success handler when reloading is not needed
  * @param {object} page Page instance
@@ -231,10 +183,6 @@ const errorHandler = function(error) {
 
 const saveWithShortcut = function(markdown) {
   const editorMode = crowi.getCrowiForJquery().getCurrentEditorMode();
-  if (editorMode == null) {
-    // do nothing
-    return;
-  }
 
   let revisionId = pageRevisionId;
   // get options
@@ -308,6 +256,57 @@ const saveWithSubmitButton = function() {
     .catch(errorHandler);
 };
 
+// setup renderer after plugins are installed
+crowiRenderer.setup();
+
+// restore draft when the first time to edit
+const draft = crowi.findDraft(pagePath);
+if (!pageRevisionId && draft != null) {
+  markdown = draft;
+}
+
+/**
+ * define components
+ *  key: id of element
+ *  value: React Element
+ */
+const componentMappings = {
+  'search-top': <HeaderSearchBox crowi={crowi} />,
+  'search-sidebar': <HeaderSearchBox crowi={crowi} />,
+  'search-page': <SearchPage crowi={crowi} crowiRenderer={crowiRenderer} />,
+  'page-list-search': <PageListSearch crowi={crowi} />,
+
+  //'revision-history': <PageHistory pageId={pageId} />,
+  'seen-user-list': <SeenUserList pageId={pageId} crowi={crowi} />,
+  'bookmark-button': <BookmarkButton pageId={pageId} crowi={crowi} />,
+  'bookmark-button-lg': <BookmarkButton pageId={pageId} crowi={crowi} size="lg" />,
+
+  'page-name-input': <NewPageNameInput crowi={crowi} parentPageName={pagePath} />,
+
+};
+// additional definitions if data exists
+if (pageId) {
+  componentMappings['page-comments-list'] = <PageComments pageId={pageId} revisionId={pageRevisionId} revisionCreatedAt={pageRevisionCreatedAt} crowi={crowi} crowiOriginRenderer={crowiRenderer} />;
+  componentMappings['page-attachment'] = <PageAttachment pageId={pageId} pageContent={pageContent} crowi={crowi} />;
+}
+if (pagePath) {
+  componentMappings['page'] = <Page crowi={crowi} crowiRenderer={crowiRenderer} markdown={markdown} pagePath={pagePath} showHeadEditButton={true} onSaveWithShortcut={saveWithShortcut} />;
+  componentMappings['revision-path'] = <RevisionPath pagePath={pagePath} crowi={crowi} />;
+  componentMappings['revision-url'] = <RevisionUrl pageId={pageId} pagePath={pagePath} />;
+}
+
+Object.keys(componentMappings).forEach((key) => {
+  const elem = document.getElementById(key);
+  if (elem) {
+    componentInstances[key] = ReactDOM.render(componentMappings[key], elem);
+  }
+});
+
+// set page if exists
+if (componentInstances['page'] != null) {
+  crowi.setPage(componentInstances['page']);
+}
+
 // render SavePageControls
 let savePageControls = null;
 const savePageControlsElem = document.getElementById('save-page-controls');

+ 16 - 8
src/client/js/components/Page.js

@@ -4,6 +4,7 @@ import PropTypes from 'prop-types';
 import RevisionBody from './Page/RevisionBody';
 import HandsontableModal from './PageEditor/HandsontableModal';
 import MarkdownTable from '../models/MarkdownTable';
+import mtu from './PageEditor/MarkdownTableUtil';
 
 export default class Page extends React.Component {
 
@@ -11,12 +12,15 @@ export default class Page extends React.Component {
     super(props);
 
     this.state = {
-      html: ''
+      html: '',
+      markdown: '',
+      currentTargetTableArea: null
     };
 
     this.appendEditSectionButtons = this.appendEditSectionButtons.bind(this);
     this.renderHtml = this.renderHtml.bind(this);
     this.getHighlightedBody = this.getHighlightedBody.bind(this);
+    this.saveHandlerForHandsontableModal = this.saveHandlerForHandsontableModal.bind(this);
   }
 
   componentWillMount() {
@@ -27,10 +31,6 @@ export default class Page extends React.Component {
     this.appendEditSectionButtons();
   }
 
-  componentWillReceiveProps(nextProps) {
-    this.renderHtml(nextProps.markdown, nextProps.highlightKeywords);
-  }
-
   setMarkdown(markdown) {
     this.renderHtml(markdown, this.props.highlightKeywords);
   }
@@ -76,10 +76,17 @@ export default class Page extends React.Component {
    * @param endLineNumber
    */
   launchHandsontableModal(beginLineNumber, endLineNumber) {
-    const tableLines = this.props.markdown.split('\n').slice(beginLineNumber - 1, endLineNumber).join('\n');
+    const tableLines = this.state.markdown.split('\n').slice(beginLineNumber - 1, endLineNumber).join('\n');
+    this.setState({currentTargetTableArea: {beginLineNumber, endLineNumber}});
     this.refs.handsontableModal.show(MarkdownTable.fromMarkdownString(tableLines));
   }
 
+  saveHandlerForHandsontableModal(markdownTable) {
+    const newMarkdown = mtu.replaceMarkdownTableInMarkdown(markdownTable, this.state.markdown, this.state.currentTargetTableArea.beginLineNumber, this.state.currentTargetTableArea.endLineNumber);
+    this.props.onSaveWithShortcut(newMarkdown);
+    this.setState({currentTargetTableArea: null});
+  }
+
   renderHtml(markdown, highlightKeywords) {
     let context = {
       markdown,
@@ -110,7 +117,7 @@ export default class Page extends React.Component {
       .then(() => interceptorManager.process('postPostProcess', context))
       .then(() => interceptorManager.process('preRenderHtml', context))
       .then(() => {
-        this.setState({ html: context.parsedHTML });
+        this.setState({ html: context.parsedHTML, markdown });
       })
       // process interceptors for post rendering
       .then(() => interceptorManager.process('postRenderHtml', context));
@@ -129,7 +136,7 @@ export default class Page extends React.Component {
           isMathJaxEnabled={isMathJaxEnabled}
           renderMathJaxOnInit={true}
       />
-      <HandsontableModal ref='handsontableModal' />
+      <HandsontableModal ref='handsontableModal' onSave={this.saveHandlerForHandsontableModal} />
     </div>;
   }
 }
@@ -137,6 +144,7 @@ export default class Page extends React.Component {
 Page.propTypes = {
   crowi: PropTypes.object.isRequired,
   crowiRenderer: PropTypes.object.isRequired,
+  onSaveWithShortcut: PropTypes.func.isRequired,
   markdown: PropTypes.string.isRequired,
   pagePath: PropTypes.string.isRequired,
   showHeadEditButton: PropTypes.bool,

+ 1 - 1
src/client/js/components/PageEditor/CodeMirrorEditor.js

@@ -730,7 +730,7 @@ export default class CodeMirrorEditor extends AbstractEditor {
         { this.state.isCheatsheetModalButtonShown && this.renderCheatsheetModalButton() }
       </div>
 
-      <HandsontableModal ref='handsontableModal' onSave={ table => mtu.replaceMarkdownTable(this.getCodeMirror(), table) }/>
+      <HandsontableModal ref='handsontableModal' onSave={ table => mtu.replaceFocusedMarkdownTableWithEditor(this.getCodeMirror(), table) }/>
     </React.Fragment>;
   }
 

+ 30 - 6
src/client/js/components/PageEditor/MarkdownTableUtil.js

@@ -18,8 +18,8 @@ class MarkdownTableUtil {
     this.getStrFromBot = this.getStrFromBot.bind(this);
     this.getStrToEot = this.getStrToEot.bind(this);
     this.isInTable = this.isInTable.bind(this);
-    this.replaceMarkdownTable = this.replaceMarkdownTable.bind(this);
-    this.replaceMarkdownTableWithReformed = this.replaceMarkdownTable; // alias
+    this.replaceFocusedMarkdownTableWithEditor = this.replaceFocusedMarkdownTableWithEditor.bind(this);
+    this.replaceMarkdownTableWithReformed = this.replaceFocusedMarkdownTableWithEditor; // alias
   }
 
   /**
@@ -133,7 +133,7 @@ class MarkdownTableUtil {
   }
 
   /**
-   * returns markdown table that is merged all of markdown table in array
+   * 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
    */
@@ -152,15 +152,39 @@ class MarkdownTableUtil {
   }
 
   /**
-   * replace markdown table
+   * replace focused markdown table with editor
    * (A replaced table is reformed by markdown-table.)
-   * @param {MarkdownTable} markdown table
+   * @param {MarkdownTable} table
    */
-  replaceMarkdownTable(editor, table) {
+  replaceFocusedMarkdownTableWithEditor(editor, table) {
     const curPos = editor.getCursor();
     editor.getDoc().replaceRange(table.toString(), this.getBot(editor), this.getEot(editor));
     editor.getDoc().setCursor(curPos.line + 1, 2);
   }
+
+  /**
+   * 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('\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