فهرست منبع

add HackMD tab

Yuki Takei 7 سال پیش
والد
کامیت
68dbf4ba7d

+ 1 - 4
lib/views/_form.html

@@ -16,13 +16,10 @@
 
 <form action="/_/edit" id="page-form" method="post" class="{% if isUploadable() %}uploadable{% endif %} page-form">
 
-  <div id="page-editor">{% if pageForm.body %}{{ pageForm.body }}{% endif %}</div>
-
   <input type="hidden" id="form-body" name="pageForm[body]" value="{% if pageForm.body %}{{ pageForm.body }}{% endif %}">
   <input type="hidden" name="pageForm[path]" value="{{ path }}">
   <input type="hidden" name="pageForm[currentRevision]" value="{{ pageForm.currentRevision|default(page.revision._id.toString()) }}">
-  <div class="page-editor-footer form-submit-group form-group form-inline
-      d-flex align-items-center justify-content-between">
+  <div class="page-editor-footer d-flex flex-row align-items-center justify-content-between">
     <div>
       <div id="page-editor-options-selector"></div>
     </div>

+ 7 - 3
lib/views/widget/page_content.html

@@ -27,11 +27,15 @@
       </div>
     {% endif %}
 
-    {# edit form #}
     {% if not page.isDeleted() %}
-    <div class="edit-form tab-pane {% if req.body.pageForm %}active{% endif %}" id="edit-form">
+      {# edit form #}
+      <div class="tab-pane edit-form {% if req.body.pageForm %}active{% endif %}" id="edit-form">
+        <div id="page-editor">{% if pageForm.body %}{{ pageForm.body }}{% endif %}</div>
+      </div>
+      <div class="tab-pane edit-form" id="hackmd">
+        <div id="hackmd-editor">hackmd-editor</div>
+      </div>
       {% include '../_form.html' %}
-    </div>
     {% endif %}
 
     {# raw revision history #}

+ 6 - 1
lib/views/widget/page_tabs.html

@@ -11,11 +11,16 @@
   </li>
 
   {% if !isTrashPage() %}
-  <li class="nav-main-left-tab {% if req.body.pageForm %}active{% endif %}">
+  <li class="nav-main-left-tab nav-tab-edit {% if req.body.pageForm %}active{% endif %}">
     <a {% if user %}href="#edit-form" data-toggle="tab"{% endif %} class="edit-button {% if not user %}edit-button-disabled{% endif %}">
       <i class="icon-note"></i> {{ t('Edit') }}
     </a>
   </li>
+  <li class="nav-main-left-tab nav-tab-hackmd">
+    <a {% if user %}href="#hackmd" data-toggle="tab"{% endif %} class="{% if not user %}edit-button-disabled{% endif %}">
+      <i class="icon-note"></i> {{ t('HackMD') }}
+    </a>
+  </li>
   {% endif %}
 
   {#

+ 5 - 5
resource/js/components/PageEditor/OptionsSelector.js

@@ -121,7 +121,7 @@ export default class OptionsSelector extends React.Component {
     const bsClassName = 'form-control-dummy'; // set form-control* to shrink width
 
     return (
-      <FormGroup controlId="formControlsSelect">
+      <FormGroup controlId="formControlsSelect" className="my-0">
         <ControlLabel>Theme:</ControlLabel>
         <FormControl componentClass="select" placeholder="select" bsClass={bsClassName} className="btn-group-sm selectpicker"
             onChange={this.onChangeTheme}
@@ -149,7 +149,7 @@ export default class OptionsSelector extends React.Component {
     const bsClassName = 'form-control-dummy'; // set form-control* to shrink width
 
     return (
-      <FormGroup controlId="formControlsSelect">
+      <FormGroup controlId="formControlsSelect" className="my-0">
         <ControlLabel>Keymap:</ControlLabel>
         <FormControl componentClass="select" placeholder="select" bsClass={bsClassName} className="btn-group-sm selectpicker"
             onChange={this.onChangeKeymapMode}
@@ -164,7 +164,7 @@ export default class OptionsSelector extends React.Component {
 
   renderConfigurationDropdown() {
     return (
-      <FormGroup controlId="formControlsSelect">
+      <FormGroup controlId="formControlsSelect" className="my-0">
 
         <Dropdown dropup id="configurationDropdown" className="configuration-dropdown"
             open={this.state.isCddMenuOpened} onToggle={this.onToggleConfigurationDropdown}>
@@ -227,11 +227,11 @@ export default class OptionsSelector extends React.Component {
   }
 
   render() {
-    return <span>
+    return <div className="d-flex flex-row">
       <span className="m-l-5">{this.renderThemeSelector()}</span>
       <span className="m-l-5">{this.renderKeymapModeSelector()}</span>
       <span className="m-l-5">{this.renderConfigurationDropdown()}</span>
-    </span>;
+    </div>;
   }
 }
 

+ 26 - 10
resource/js/legacy/crowi-form.js

@@ -14,6 +14,18 @@ function FetchPagesUpdatePostAndInsert(path) {
   });
 }
 
+function initSlack() {
+  if (slackConfigured) {
+    var $slackChannels = $('#page-form-slack-channel');
+    var slackChannels = $slackChannels.val();
+    // if slackChannels is empty, then fetch default (admin setting)
+    // if not empty, it means someone specified this setting for the page.
+    if (slackChannels === '') {
+      FetchPagesUpdatePostAndInsert(pagePath);
+    }
+  }
+}
+
 var slackConfigured = $('#page-form-setting').data('slack-configured');
 
 // for new page
@@ -29,20 +41,24 @@ if (!pageId) {
 
 $('a[data-toggle="tab"][href="#edit-form"]').on('show.bs.tab', function() {
   $('body').addClass('on-edit');
-
-  if (slackConfigured) {
-    var $slackChannels = $('#page-form-slack-channel');
-    var slackChannels = $slackChannels.val();
-    // if slackChannels is empty, then fetch default (admin setting)
-    // if not empty, it means someone specified this setting for the page.
-    if (slackChannels === '') {
-      FetchPagesUpdatePostAndInsert(pagePath);
-    }
-  }
+  $('body').addClass('builtin-editor');
+  initSlack();
 });
 
 $('a[data-toggle="tab"][href="#edit-form"]').on('hide.bs.tab', function() {
   $('body').removeClass('on-edit');
+  $('body').removeClass('builtin-editor');
+});
+
+$('a[data-toggle="tab"][href="#hackmd"]').on('show.bs.tab', function() {
+  $('body').addClass('on-edit');
+  $('body').addClass('hackmd');
+  initSlack();
+});
+
+$('a[data-toggle="tab"][href="#hackmd"]').on('hide.bs.tab', function() {
+  $('body').removeClass('on-edit');
+  $('body').removeClass('hackmd');
 });
 
 /**

+ 15 - 2
resource/js/legacy/crowi.js

@@ -828,6 +828,10 @@ $(function() {
       window.location.hash = '#edit-form';
       window.history.replaceState('', 'Edit', '#edit-form');
     });
+    $('a[data-toggle="tab"][href="#hackmd"]').on('show.bs.tab', function() {
+      window.location.hash = '#hackmd';
+      window.history.replaceState('', 'HackMD', '#hackmd');
+    });
     $('a[data-toggle="tab"][href="#revision-body"]').on('show.bs.tab', function() {
       // couln't solve https://github.com/weseek/crowi-plus/issues/119 completely -- 2017.07.03 Yuki Takei
       window.location.hash = '#';
@@ -841,6 +845,9 @@ $(function() {
     $('a[data-toggle="tab"][href="#edit-form"]').on('show.bs.tab', function() {
       window.history.replaceState('', 'Edit', '#edit-form');
     });
+    $('a[data-toggle="tab"][href="#hackmd"]').on('show.bs.tab', function() {
+      window.history.replaceState('', 'HackMD', '#hackmd');
+    });
     $('a[data-toggle="tab"][href="#revision-body"]').on('show.bs.tab', function() {
       window.history.replaceState('', '',  location.href.replace(location.hash, ''));
     });
@@ -916,7 +923,10 @@ window.addEventListener('load', function(e) {
       // focus
       Crowi.setCaretLineAndFocusToEditor();
     }
-    if (location.hash == '#revision-history') {
+    else if (location.hash == '#hackmd') {
+      $('a[data-toggle="tab"][href="#hackmd"]').tab('show');
+    }
+    else if (location.hash == '#revision-history') {
       $('a[data-toggle="tab"][href="#revision-history"]').tab('show');
     }
   }
@@ -973,7 +983,10 @@ window.addEventListener('hashchange', function(e) {
     if (location.hash == '#edit-form') {
       $('a[data-toggle="tab"][href="#edit-form"]').tab('show');
     }
-    if (location.hash == '#revision-history') {
+    else if (location.hash == '#hackmd') {
+      $('a[data-toggle="tab"][href="#hackmd"]').tab('show');
+    }
+    else if (location.hash == '#revision-history') {
       $('a[data-toggle="tab"][href="#revision-history"]').tab('show');
     }
   }

+ 232 - 208
resource/styles/scss/_on-edit.scss

@@ -1,5 +1,26 @@
+body:not(.on-edit) {
+  // hide #page-form
+  #page-form {
+    display: none;
+  }
+}
+
 body.on-edit {
 
+  %expand-by-flex {
+    display: flex;
+    flex-direction: column;
+    flex: 1;
+  }
+
+  // calculate margin
+  $header-plus-footer: 2px                      // .main padding-top
+                      + 42px                    // .nav height
+                      + 1px                     // .page-editor-footer border-top
+                      + 40px;                   // .page-editor-footer min-height
+  $editor-margin: $header-plus-footer + 22px;   // .btn-open-dropzone height
+  $editor-margin-sm: $header-plus-footer;
+
   // hide unnecessary elements
   .navbar.navbar-static-top,
   .row.row-alerts,
@@ -29,15 +50,17 @@ body.on-edit {
     display: none;
   }
 
+  // show only either Edit button or HackMD button
+  &.hackmd .nav-tab-edit {
+    display: none;
+  }
+  &:not(.hackmd) .nav-tab-hackmd {
+    display: none;
+  }
 
   /*****************
    * Expand Editor
    *****************/
-  .expand-by-flex {
-    display: flex;
-    flex-direction: column;
-    flex: 1;
-  }
   .container-fluid {
     padding-bottom: 0;
   }
@@ -60,71 +83,8 @@ body.on-edit {
 
     &,
     .content-main,
-    .tab-content,
-    .edit-form,
-    .page-form {
-      @extend .expand-by-flex;
-    }
-
-    .page-form {
-
-      // calculate margin
-      $header-plus-footer: 2px                      // .main padding-top
-                         + 42px                     // .nav height
-                         + 1px                      // .page-editor-footer border-top
-                         + 40px;                    // .page-editor-footer min-height
-      $editor-margin: $header-plus-footer + 22px;   // .btn-open-dropzone height
-      $editor-margin-sm: $header-plus-footer;
-
-      #page-editor {
-        // right(preview)
-        &,
-        .row,
-        .page-editor-preview-container,
-        .page-editor-preview-body {
-          min-height: calc(100vh - #{$header-plus-footer});   // for IE11
-          height: calc(100vh - #{$header-plus-footer});
-        }
-        // left(editor)
-        .page-editor-editor-container {
-          min-height: calc(100vh - #{$header-plus-footer});   // for IE11
-          height: calc(100vh - #{$header-plus-footer});
-
-          .react-codemirror2, .CodeMirror, .CodeMirror-scroll,
-          .textarea-editor {
-            height: calc(100vh - #{$editor-margin});
-            // less than smartphone
-            @media (max-width: $screen-xs) {
-              height: calc(100vh - #{$editor-margin-sm});
-            }
-          }
-        }
-      }
-
-
-      .page-editor-footer {
-        width: 100%;
-        margin: 0;
-        padding: 3px;
-        min-height: 40px;
-        border-top: solid 1px transparent;
-
-        .btn-submit {
-          width: 100px;
-        }
-      }
-
-      // slack
-      .input-group-slack {
-        .input-group-addon {
-          padding: 2px 8px;
-          line-height: 1em;
-          img, input {
-            vertical-align: middle;
-          }
-        }
-      }
-
+    .tab-content {
+      @extend %expand-by-flex;
     }
   }
 
@@ -167,198 +127,262 @@ body.on-edit {
     }
   }
 
-  /*****************
-   * Editor styles
-   *****************/
-  .page-editor-editor-container {
-    border-right: 1px solid transparent;
-    padding-right: 0;
-    // override CodeMirror styles
-    .CodeMirror {
-      .cm-matchhighlight {
-        background-color: cyan;
-      }
-      .CodeMirror-selection-highlight-scrollbar {
-        background-color: darkcyan;
+  .page-editor-footer {
+    width: 100%;
+    margin: 0;
+    padding: 3px;
+    min-height: 40px;
+    border-top: solid 1px transparent;
+
+    // slack
+    .input-group-slack {
+      .input-group-addon {
+        padding: 2px 8px;
+        line-height: 1em;
+        img, input {
+          vertical-align: middle;
+        }
       }
     }
 
-    .overlay {
-      // layout
-      display: flex;
-      justify-content: center;
-      align-items: center;
-      // style
-      margin: 0 15px;
-    }
-    .overlay-content {
-      font-size: 2.5em;
-      padding: 0.5em;
+    .btn-submit {
+      width: 100px;
     }
+  }
 
-    @mixin overlay-processing-style() {
-      .overlay {
-        background: rgba(255,255,255,0.5);
-      }
-      .overlay-content {
-        padding: 0.3em;
-        background: rgba(200,200,200,0.5);
-        color: #444;
+
+  &.builtin-editor #edit-form {
+    @extend %expand-by-flex;
+
+    #page-editor {
+      // right(preview)
+      &,
+      .row,
+      .page-editor-preview-container,
+      .page-editor-preview-body {
+        min-height: calc(100vh - #{$header-plus-footer});   // for IE11
+        height: calc(100vh - #{$header-plus-footer});
       }
-    }
-    // add icon on cursor
-    .autoformat-markdown-table-activated .CodeMirror-cursor {
-      &:after {
-        font-family: 'FontAwesome';
-        content: '\f0ce';
+      // left(editor)
+      .page-editor-editor-container {
+        min-height: calc(100vh - #{$header-plus-footer});   // for IE11
+        height: calc(100vh - #{$header-plus-footer});
+
+        .react-codemirror2, .CodeMirror, .CodeMirror-scroll,
+        .textarea-editor {
+          height: calc(100vh - #{$editor-margin});
+          // less than smartphone
+          @media (max-width: $screen-xs) {
+            height: calc(100vh - #{$editor-margin-sm});
+          }
+        }
       }
     }
 
-    // for Dropzone
-    .dropzone {
-      @mixin insertSimpleLineIcons($code) {
-        &:before {
-          margin-right: 0.2em;
-          font-family: 'simple-line-icons';
-          content: $code;
+    /*****************
+    * Editor styles
+    *****************/
+    .page-editor-editor-container {
+      border-right: 1px solid transparent;
+      padding-right: 0;
+      // override CodeMirror styles
+      .CodeMirror {
+        .cm-matchhighlight {
+          background-color: cyan;
+        }
+        .CodeMirror-selection-highlight-scrollbar {
+          background-color: darkcyan;
         }
       }
 
-      // unuploadable or rejected
-      &.dropzone-unuploadable, &.dropzone-rejected {
+      .overlay {
+        // layout
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        // style
+        margin: 0 15px;
+      }
+      .overlay-content {
+        font-size: 2.5em;
+        padding: 0.5em;
+      }
+
+      @mixin overlay-processing-style() {
         .overlay {
-          background: rgba(200,200,200,0.8);
+          background: rgba(255,255,255,0.5);
         }
         .overlay-content {
+          padding: 0.3em;
+          background: rgba(200,200,200,0.5);
           color: #444;
         }
       }
-      // uploading
-      &.dropzone-uploading {
-        @include overlay-processing-style();
+      // add icon on cursor
+      .autoformat-markdown-table-activated .CodeMirror-cursor {
+        &:after {
+          font-family: 'FontAwesome';
+          content: '\f0ce';
+        }
       }
 
-      // unuploadable
-      &.dropzone-unuploadable {
-        .overlay-content {
-          // insert content
-          @include insertSimpleLineIcons("\e617");  // icon-exclamation
-          &:after {
-            content: "File uploading is disabled";
+      // for Dropzone
+      .dropzone {
+        @mixin insertSimpleLineIcons($code) {
+          &:before {
+            margin-right: 0.2em;
+            font-family: 'simple-line-icons';
+            content: $code;
           }
         }
-      }
-      // uploadable
-      &.dropzone-uploadable {
-        // accepted
-        &.dropzone-accepted:not(.dropzone-rejected) {
+
+        // unuploadable or rejected
+        &.dropzone-unuploadable, &.dropzone-rejected {
           .overlay {
-            border: 4px dashed #ccc;
+            background: rgba(200,200,200,0.8);
           }
+          .overlay-content {
+            color: #444;
+          }
+        }
+        // uploading
+        &.dropzone-uploading {
+          @include overlay-processing-style();
+        }
+
+        // unuploadable
+        &.dropzone-unuploadable {
           .overlay-content {
             // insert content
-            @include insertSimpleLineIcons("\e084");  // icon-cloud-upload
+            @include insertSimpleLineIcons("\e617");  // icon-exclamation
             &:after {
-              content: "Drop here to upload";
+              content: "File uploading is disabled";
             }
-            // style
-            color: #666;
-            background: rgba(200,200,200,0.8);
           }
         }
-        // file type mismatch
-        &.dropzone-rejected:not(.dropzone-uploadablefile) .overlay-content {
-          // insert content
-          @include insertSimpleLineIcons("\e032");  // icon-picture
-          &:after {
-            content: "Only an image file is allowed";
+        // uploadable
+        &.dropzone-uploadable {
+          // accepted
+          &.dropzone-accepted:not(.dropzone-rejected) {
+            .overlay {
+              border: 4px dashed #ccc;
+            }
+            .overlay-content {
+              // insert content
+              @include insertSimpleLineIcons("\e084");  // icon-cloud-upload
+              &:after {
+                content: "Drop here to upload";
+              }
+              // style
+              color: #666;
+              background: rgba(200,200,200,0.8);
+            }
           }
-        }
-        // multiple files
-        &.dropzone-accepted.dropzone-rejected .overlay-content {
-          // insert content
-          @include insertSimpleLineIcons("\e617");  // icon-exclamation
-          &:after {
-            content: "Only 1 file is allowed";
+          // file type mismatch
+          &.dropzone-rejected:not(.dropzone-uploadablefile) .overlay-content {
+            // insert content
+            @include insertSimpleLineIcons("\e032");  // icon-picture
+            &:after {
+              content: "Only an image file is allowed";
+            }
+          }
+          // multiple files
+          &.dropzone-accepted.dropzone-rejected .overlay-content {
+            // insert content
+            @include insertSimpleLineIcons("\e617");  // icon-exclamation
+            &:after {
+              content: "Only 1 file is allowed";
+            }
           }
         }
-      }
-    } // end of.dropzone
+      } // end of.dropzone
 
-    .textarea-editor {
-      border: none;
-      font-family: monospace;
-    }
-
-    .loading-keymap {
-      @include overlay-processing-style();
-    }
+      .textarea-editor {
+        border: none;
+        font-family: monospace;
+      }
 
-    .btn-open-dropzone {
-      z-index: 2;
-      font-size: small;
-      text-align: right;
-      padding-top: 3px;
-      padding-bottom: 0;
-      border: none;
-      border-radius: 0;
-      border-top: 1px dotted #ccc;
-
-      &:active {
-        box-shadow: none;
+      .loading-keymap {
+        @include overlay-processing-style();
       }
 
-      // hide if screen size is less than smartphone
-      @media (max-width: $screen-xs) {
-        display: none;
+      .btn-open-dropzone {
+        z-index: 2;
+        font-size: small;
+        text-align: right;
+        padding-top: 3px;
+        padding-bottom: 0;
+        border: none;
+        border-radius: 0;
+        border-top: 1px dotted #ccc;
+
+        &:active {
+          box-shadow: none;
+        }
+
+        // hide if screen size is less than smartphone
+        @media (max-width: $screen-xs) {
+          display: none;
+        }
       }
     }
-  }
-  .page-editor-preview-container {
-  }
-
-  .page-editor-preview-body {
-    padding-top: 18px;
-    padding-right: 15px;
-    overflow-y: scroll;
-  }
+    .page-editor-preview-container {
+    }
 
-  #page-editor-options-selector {
-    label {
-      margin-right: 0.5em;
+    .page-editor-preview-body {
+      padding-top: 18px;
+      padding-right: 15px;
+      overflow-y: scroll;
     }
 
-    // configuration dropdown
-    .configuration-dropdown {
-      .icon-container {
-        display: inline-block;
-        width: 20px;
+    #page-editor-options-selector {
+      label {
+        margin-right: 0.5em;
       }
-      .dropdown-menu > li > a {
-        display: flex;
-        justify-content: space-between;
-        align-items: center;
 
-        .menuitem-label {
-          flex: 1;
-          margin-right: 10px;
+      // configuration dropdown
+      .configuration-dropdown {
+        .icon-container {
+          display: inline-block;
+          width: 20px;
+        }
+        .dropdown-menu > li > a {
+          display: flex;
+          justify-content: space-between;
+          align-items: center;
+
+          .menuitem-label {
+            flex: 1;
+            margin-right: 10px;
+          }
         }
       }
+
+      @media (max-width: $screen-xs-max) { // {{{ less than smartphone size
+        display: none;
+      }
     }
 
-    @media (max-width: $screen-xs-max) { // {{{ less than smartphone size
-      display: none;
+    #page-grant-selector {
+      .btn-group {
+        min-width: 150px;
+      }
     }
-  }
 
-  #page-grant-selector {
-    .btn-group {
-      min-width: 150px;
+  } // .builtin-editor #edit-form
+
+
+  &.hackmd #hackmd {
+    @extend %expand-by-flex;
+
+    #hackmd-editor {
+      min-height: calc(100vh - #{$header-plus-footer});   // for IE11
+      height: calc(100vh - #{$header-plus-footer});
     }
   }
 
+}
 
-} // }}}
 
 /*
  * for creating portal