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

Merge pull request #247 from weseek/master

v2.3.6
Yuki Takei 8 лет назад
Родитель
Сommit
e3a53da8d8

+ 9 - 0
CHANGES.md

@@ -1,11 +1,20 @@
 CHANGES
 ========
 
+## 2.3.6
+
+* Feature: Theme Selector for Editor
+* Improvement: Remove unportalize button from crowi-plus layout
+* Fix: CSS for admin pages
+* Support: Shrink the size of libraries to include
+
 ## 2.3.5
 
 * Feature: Enhanced Editor by CodeMirror
 * Feature: Emoji AutoComplete
 * Feature: Add keyboard shortcuts
+* Improvement: Attaching file with Dropzone.js
+* Improvement: Show shortcuts help with `Ctrl-/`
 * Fix: DOMs that has `.alert-info` class don't be displayed
 * Support: Switch and upgrade libs
     * 8fold-marked -> marked

+ 1 - 0
lib/locales/en-US/translation.json

@@ -200,6 +200,7 @@
           "title": "Editor shortcuts",
           "Indent": "Indent",
           "Outdent": "Outdent",
+          "Save Page": "Save Page",
           "Delete Line": "Delete Line"
       }
   }

+ 1 - 0
lib/locales/ja/translation.json

@@ -197,6 +197,7 @@
     "editor": {
         "Indent": "インデント",
         "Outdent": "左インデント",
+        "Save Page": "保存",
         "Delete Line": "行削除"
     }
 }

+ 4 - 0
lib/views/_form.html

@@ -27,6 +27,10 @@
       ファイルを追加 ...
     </button>#}
 
+    <div class="pull-left">
+      <div id="page-editor-theme-selector"></div>
+    </div>
+
     <div class="pull-right form-inline page-form-setting" id="page-form-setting" data-slack-configured="{{ slackConfigured() }}">
       {% if slackConfigured() %}
       <span class="input-group extended-setting">

+ 1 - 1
lib/views/admin/security.html

@@ -11,7 +11,7 @@
 {% endblock %}
 
 {% block content_main %}
-<div class="content-main">
+<div class="content-main admin-security">
   <div class="row">
     <div class="col-md-3">
       {% include './widget/menu.html' with {current: 'security'} %}

+ 3 - 0
lib/views/crowi-plus/page_list.html

@@ -29,6 +29,9 @@
         {# force remove #revision-toc from #content_main of parent #}
         <script>
           $('#revision-toc').remove();
+
+          // hide unportalize button
+          $('.portal > .nav > .dropdown').remove();
         </script>
 
       </div> {# /.col- #}

+ 17 - 1
lib/views/crowi-plus/widget/system-version.html

@@ -3,6 +3,22 @@
     <a href="https://github.com/weseek/crowi-plus">crowi-plus</a> {{ crowiVersion() }}
   </span>
   <span>
-    <a href="" data-target="#shortcuts-modal" data-toggle="modal"><i class="fa fa-keyboard-o"></i> Ctrl+/</a>
+    <a href="" data-target="#shortcuts-modal" data-toggle="modal"><i class="fa fa-keyboard-o"></i>&nbsp;<span class="cmd-key"></span>-/</a>
   </span>
 </div>
+<script>
+  /*
+  * add classes to cmd-key by OS
+  */
+  var platform = navigator.platform.toLowerCase();
+  var isMac = (platform.indexOf('mac') > -1);
+
+  document.querySelectorAll('.system-version .cmd-key').forEach((element) => {
+    if (isMac) {
+      element.classList.add('mac');
+    }
+    else {
+      element.classList.add('win', 'key-longer');
+    }
+  })
+</script>

+ 1 - 1
lib/views/layout/2column.html

@@ -20,7 +20,7 @@
     <footer class="">
       <p>
         <a href="https://github.com/weseek/crowi-plus">crowi-plus</a> {{ crowiVersion() }}
-        <a href="" class="pull-right" data-target="#shortcuts-modal" data-toggle="modal"><i class="fa fa-keyboard-o"></i> Ctrl+/</a>
+        <a href="" class="pull-right" data-target="#shortcuts-modal" data-toggle="modal"><i class="fa fa-keyboard-o"></i>&nbsp;<span class="cmd-key"></span>-/</a>
       </p>
     </footer>
   </div>

+ 17 - 1
lib/views/layout/single.html

@@ -23,7 +23,23 @@
     <a href="https://github.com/weseek/crowi-plus">crowi-plus</a> {{ crowiVersion() }}
   </span>
   <span>
-    <a href="" data-target="#shortcuts-modal" data-toggle="modal"><i class="fa fa-keyboard-o"></i> Ctrl+/</a>
+    <a href="" data-target="#shortcuts-modal" data-toggle="modal"><i class="fa fa-keyboard-o"></i>&nbsp;<span class="cmd-key"></span>-/</a>
   </span>
 </div>
+<script>
+  /*
+  * add classes to cmd-key by OS
+  */
+  var platform = navigator.platform.toLowerCase();
+  var isMac = (platform.indexOf('mac') > -1);
+
+  document.querySelectorAll('.system-version .cmd-key').forEach((element) => {
+    if (isMac) {
+      element.classList.add('mac');
+    }
+    else {
+      element.classList.add('win', 'key-longer');
+    }
+  })
+</script>
 {% endblock %}

+ 10 - 6
lib/views/modal/shortcuts.html

@@ -41,6 +41,10 @@
                 <th>{{ t('modal_shortcuts.editor.Outdent') }}:</th>
                 <td><span class="key key-long">Shift</span> + <span class="key key-longer">Tab</span></td>
               </tr>
+              <tr>
+                <th>{{ t('modal_shortcuts.editor.Save Page') }}:</th>
+                <td><span class="key cmd-key"></span> + <span class="key">S</span></td>
+              </tr>
               <tr>
                 <th>{{ t('modal_shortcuts.editor.Delete Line') }}:</th>
                 <td><span class="key cmd-key"></span> + <span class="key">D</span></td>
@@ -57,15 +61,15 @@
   </div><!-- /.modal-dialog -->
 
   <script>
+    /*
+     * add classes to cmd-key by OS
+     */
     var platform = navigator.platform.toLowerCase();
     var isMac = (platform.indexOf('mac') > -1);
 
-    document.querySelectorAll('.cmd-key').forEach((element) => {
-      if (isMac) {
-        element.classList.add('mac');
-      }
-      else {
-        element.classList.add('win', 'key-longer');
+    document.querySelectorAll('#shortcuts-modal .cmd-key').forEach((element) => {
+      if (!isMac) {
+        element.classList.add('key-longer');
       }
     })
   </script>

+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "crowi-plus",
-  "version": "2.3.5-RC",
+  "version": "2.3.6-RC",
   "description": "Enhanced Crowi",
   "tags": [
     "wiki",

+ 4 - 1
resource/css/_admin.scss

@@ -10,7 +10,7 @@
     }
   }
 
-  .content-main.admin-customize {
+  .admin-customize {
     .ss-container img {
       padding: .5em;
       background-color: #ddd;
@@ -40,7 +40,9 @@
     .CodeMirror pre {
       font-family: $font-family-monospace;
     }
+  }
 
+  .admin-security {
     .passport-logo {
       padding: 4px;
       height: 32px;
@@ -51,4 +53,5 @@
       min-height: 800px;
     }
   }
+  
 }

+ 6 - 0
resource/css/_form.scss

@@ -208,6 +208,12 @@
     border-top: solid 1px #ccc;
     margin-bottom: 0;
   }
+
+  #page-editor-theme-selector .theme-selector {
+    label {
+      margin-right: 0.5em;
+    }
+  }
 } // }}}
 
 .crowi.main-container .main .page-list.content-main { // {{{ Edit Form of Page List

+ 0 - 11
resource/css/_shortcuts.scss

@@ -42,17 +42,6 @@
     text-align: center;
     color: #666;
 
-    &.cmd-key.mac {
-      &:after {
-        content: '⌘';
-      }
-    }
-    &.cmd-key.win {
-      &:after {
-        content: 'ctrl';
-      }
-    }
-
     &.key-longer {
       width: 64px;
     }

+ 11 - 0
resource/css/crowi.scss

@@ -463,3 +463,14 @@ input.searching {
     border: 0;
   }
 }
+
+.cmd-key.mac {
+  &:after {
+    content: '⌘';
+  }
+}
+.cmd-key.win {
+  &:after {
+    content: 'Ctrl';
+  }
+}

+ 30 - 2
resource/js/app.js

@@ -7,6 +7,7 @@ import CrowiRenderer from './util/CrowiRenderer';
 import HeaderSearchBox  from './components/HeaderSearchBox';
 import SearchPage       from './components/SearchPage';
 import PageEditor       from './components/PageEditor';
+import ThemeSelector    from './components/PageEditor/ThemeSelector';
 import PageListSearch   from './components/PageListSearch';
 import PageHistory      from './components/PageHistory';
 import PageComments     from './components/PageComments';
@@ -76,8 +77,6 @@ const onSaveSuccess = function(page) {
 const componentMappings = {
   'search-top': <HeaderSearchBox crowi={crowi} />,
   'search-page': <SearchPage crowi={crowi} />,
-  'page-editor': <PageEditor crowi={crowi} pageId={pageId} revisionId={pageRevisionId} pagePath={pagePath} markdown={entities.decodeHTML(pageContent)}
-                              onSaveSuccess={onSaveSuccess}/>,
   'page-list-search': <PageListSearch crowi={crowi} />,
   'page-comments-list': <PageComments pageId={pageId} revisionId={pageRevisionId} revisionCreatedAt= {pageRevisionCreatedAt} crowi={crowi} />,
   'page-attachment': <PageAttachment pageId={pageId} pageContent={pageContent} crowi={crowi} />,
@@ -106,6 +105,35 @@ if (elem) {
   ReactDOM.render(<PageCommentFormBehavior crowi={crowi} pageComments={componentInstances['page-comments-list']} />, elem);
 }
 
+/*
+ * PageEditor
+ */
+let pageEditor = null;
+// load editorTheme
+const editorTheme = crowi.loadEditorTheme();
+// render PageEditor
+const pageEditorElem = document.getElementById('page-editor');
+if (pageEditorElem) {
+  pageEditor = ReactDOM.render(
+    <PageEditor crowi={crowi} pageId={pageId} revisionId={pageRevisionId} pagePath={pagePath}
+        markdown={entities.decodeHTML(pageContent)} editorTheme={editorTheme}
+        onSaveSuccess={onSaveSuccess} />,
+    pageEditorElem
+  );
+}
+// render ThemeSelector
+const themeSelectorElem = document.getElementById('page-editor-theme-selector');
+if (themeSelectorElem) {
+  ReactDOM.render(
+    <ThemeSelector value={editorTheme}
+        onChange={(value) => { // set onChange event handler
+          pageEditor.setEditorTheme(value);
+          crowi.saveEditorTheme(value);
+        }} />,
+    themeSelectorElem
+  );
+}
+
 // render for admin
 const customCssEditorElem = document.getElementById('custom-css-editor');
 if (customCssEditorElem != null) {

+ 3 - 1
resource/js/components/HeaderSearchBox/SearchForm.js

@@ -1,5 +1,7 @@
 import React from 'react';
-import { FormGroup, Button, InputGroup } from 'react-bootstrap';
+import FormGroup from 'react-bootstrap/es/FormGroup';
+import Button from 'react-bootstrap/es/Button';
+import InputGroup from 'react-bootstrap/es/InputGroup';
 
 import { AsyncTypeahead } from 'react-bootstrap-typeahead';
 

+ 2 - 1
resource/js/components/PageAttachment/DeleteAttachmentModal.js

@@ -1,5 +1,6 @@
 import React from 'react';
-import { Button, Modal } from 'react-bootstrap';
+import Button from 'react-bootstrap/es/Button';
+import Modal from 'react-bootstrap/es/Modal';
 
 import Icon from '../Common/Icon';
 import User from '../User/User';

+ 3 - 1
resource/js/components/PageComment/DeleteCommentModal.js

@@ -1,7 +1,9 @@
 import React from 'react';
 import PropTypes from 'prop-types';
 
-import { Button, Modal } from 'react-bootstrap';
+import Button from 'react-bootstrap/es/Button';
+import Modal from 'react-bootstrap/es/Modal';
+
 import dateFnsFormat from 'date-fns/format';
 
 import ReactUtils from '../ReactUtils';

+ 14 - 0
resource/js/components/PageEditor.js

@@ -21,6 +21,7 @@ export default class PageEditor extends React.Component {
       markdown: this.props.markdown,
       isUploadable,
       isUploadableFile,
+      editorTheme: this.props.editorTheme,
     };
 
     this.setCaretLine = this.setCaretLine.bind(this);
@@ -60,6 +61,14 @@ export default class PageEditor extends React.Component {
     this.refs.editor.setCaretLine(line);
   }
 
+  /**
+   * set theme (used from the outside)
+   * @param {string} theme theme name
+   */
+  setEditorTheme(theme) {
+    this.setState({ editorTheme: theme });
+  }
+
   /**
    * the change event handler for `markdown` state
    * @param {string} value
@@ -182,6 +191,9 @@ export default class PageEditor extends React.Component {
     return top;
   };
 
+  /*
+   * methods for draft
+   */
   restoreDraft() {
     // restore draft when the first time to edit
     const draft = this.props.crowi.findDraft(this.props.pagePath);
@@ -277,6 +289,7 @@ export default class PageEditor extends React.Component {
           <Editor ref="editor" value={this.state.markdown}
               isUploadable={this.state.isUploadable}
               isUploadableFile={this.state.isUploadableFile}
+              theme={this.state.editorTheme}
               onScroll={this.onEditorScroll}
               onChange={this.onMarkdownChanged}
               onSave={this.onSave}
@@ -298,4 +311,5 @@ PageEditor.propTypes = {
   revisionId: PropTypes.string,
   pagePath: PropTypes.string,
   onSaveSuccess: PropTypes.func,
+  editorTheme: PropTypes.string,
 };

+ 11 - 2
resource/js/components/PageEditor/Editor.js

@@ -21,7 +21,14 @@ require('codemirror/addon/fold/foldgutter.css');
 require('codemirror/addon/fold/markdown-fold');
 require('codemirror/addon/fold/brace-fold');
 require('codemirror/mode/gfm/gfm');
-require('codemirror/theme/eclipse.css');
+
+require('codemirror/theme/elegant.css');
+require('codemirror/theme/neo.css');
+require('codemirror/theme/mdn-like.css');
+require('codemirror/theme/material.css');
+require('codemirror/theme/monokai.css');
+require('codemirror/theme/twilight.css');
+
 
 import Dropzone from 'react-dropzone';
 
@@ -256,6 +263,7 @@ export default class Editor extends React.Component {
       height: 'calc(100% - 20px)'
     }
 
+    const theme = this.props.theme || 'elegant';
     return (
       <div style={flexContainer}>
         <Dropzone
@@ -282,7 +290,7 @@ export default class Editor extends React.Component {
             value={this.state.value}
             options={{
               mode: 'gfm',
-              theme: 'eclipse',
+              theme: theme,
               lineNumbers: true,
               tabSize: 4,
               indentUnit: 4,
@@ -339,6 +347,7 @@ export default class Editor extends React.Component {
 
 Editor.propTypes = {
   value: PropTypes.string,
+  theme: PropTypes.string,
   isUploadable: PropTypes.bool,
   isUploadableFile: PropTypes.bool,
   onChange: PropTypes.func,

+ 57 - 0
resource/js/components/PageEditor/ThemeSelector.js

@@ -0,0 +1,57 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+import FormGroup from 'react-bootstrap/es/FormGroup';
+import FormControl from 'react-bootstrap/es/FormControl';
+import ControlLabel from 'react-bootstrap/es/ControlLabel';
+
+export default class ThemeSelector extends React.Component {
+
+  constructor(props) {
+    super(props);
+
+    this.availableThemes = [
+      'elegant', 'neo', 'mdn-like', 'material', 'monokai', 'twilight'
+    ]
+    
+    this.onChange = this.onChange.bind(this);
+  }
+
+  componentDidMount() {
+    this.init(this.props.value || this.availableThemes[0]);
+  }
+
+  init(value) {
+    this.inputEl.value = value;
+  }
+
+  onChange() {
+    if (this.props.onChange != null) {
+      this.props.onChange(this.inputEl.value);
+    }
+  }
+
+  render() {
+    const options = this.availableThemes.map((theme) => {
+      return <option key={theme} value={theme}>{theme}</option>;
+    });
+
+    return (
+      <FormGroup controlId="formControlsSelect" bsClass="theme-selector">
+        <ControlLabel>Theme:</ControlLabel>
+        <FormControl componentClass="select" placeholder="select"
+            onChange={this.onChange}
+            inputRef={ el => this.inputEl=el }>
+
+          {options}
+
+        </FormControl>
+      </FormGroup>
+    )
+  }
+}
+
+ThemeSelector.propTypes = {
+  value: PropTypes.string,
+  onChange: PropTypes.func,
+};

+ 3 - 1
resource/js/components/SearchPage/DeletePageListModal.js

@@ -1,7 +1,9 @@
 import React from 'react';
 import PropTypes from 'prop-types';
 
-import { Button, Modal, Checkbox } from 'react-bootstrap';
+import { Button } from 'react-bootstrap/es/Button';
+import { Modal } from 'react-bootstrap/es/Modal';
+import { Checkbox } from 'react-bootstrap/es/Checkbox';
 
 import ReactUtils from '../ReactUtils';
 

+ 14 - 2
resource/js/util/Crowi.js

@@ -118,11 +118,15 @@ export default class Crowi {
   }
 
   setCaretLine(line) {
-    this.pageEditor.setCaretLine(line);
+    if (this.pageEditor != null) {
+      this.pageEditor.setCaretLine(line);
+    }
   }
 
   focusToEditor() {
-    this.pageEditor.focusToEditor();
+    if (this.pageEditor != null) {
+      this.pageEditor.focusToEditor();
+    }
   }
 
   clearDraft(path) {
@@ -143,6 +147,14 @@ export default class Crowi {
     return null;
   }
 
+  saveEditorTheme(theme) {
+    this.localStorage.setItem('editorTheme', theme);
+  }
+
+  loadEditorTheme() {
+    return this.localStorage.getItem('editorTheme');
+  }
+
   findUserById(userId) {
     if (this.userById && this.userById[userId]) {
       return this.userById[userId];