Przeglądaj źródła

Imprv: Enabled to toggle the style for active line

Yuki Takei 8 lat temu
rodzic
commit
f8f34651e7

+ 1 - 1
lib/views/_form.html

@@ -28,7 +28,7 @@
     </button>#}
     </button>#}
 
 
     <div class="pull-left">
     <div class="pull-left">
-      <div id="page-editor-theme-selector"></div>
+      <div id="page-editor-options-selector"></div>
     </div>
     </div>
 
 
     <div class="pull-right form-inline page-form-setting" id="page-form-setting" data-slack-configured="{{ slackConfigured() }}">
     <div class="pull-right form-inline page-form-setting" id="page-form-setting" data-slack-configured="{{ slackConfigured() }}">

+ 13 - 1
resource/css/_form.scss

@@ -209,10 +209,22 @@
     margin-bottom: 0;
     margin-bottom: 0;
   }
   }
 
 
-  #page-editor-theme-selector .theme-selector {
+  #page-editor-options-selector {
     label {
     label {
       margin-right: 0.5em;
       margin-right: 0.5em;
     }
     }
+
+    .btn.btn-style-active-line {
+      &:hover:not(.active), &:focus:not(.active) {
+        background-color: inherit;
+      }
+    }
+  }
+
+  @media (max-width: $screen-xs-max) { // {{{ less than smartphone size
+    #page-editor-options-selector {
+      display: none;
+    }
   }
   }
 } // }}}
 } // }}}
 
 

+ 10 - 12
resource/js/app.js

@@ -7,7 +7,7 @@ import CrowiRenderer from './util/CrowiRenderer';
 import HeaderSearchBox  from './components/HeaderSearchBox';
 import HeaderSearchBox  from './components/HeaderSearchBox';
 import SearchPage       from './components/SearchPage';
 import SearchPage       from './components/SearchPage';
 import PageEditor       from './components/PageEditor';
 import PageEditor       from './components/PageEditor';
-import ThemeSelector    from './components/PageEditor/ThemeSelector';
+import EditorOptionsSelector from './components/PageEditor/EditorOptionsSelector';
 import PageListSearch   from './components/PageListSearch';
 import PageListSearch   from './components/PageListSearch';
 import PageHistory      from './components/PageHistory';
 import PageHistory      from './components/PageHistory';
 import PageComments     from './components/PageComments';
 import PageComments     from './components/PageComments';
@@ -117,30 +117,28 @@ if (elem) {
  * PageEditor
  * PageEditor
  */
  */
 let pageEditor = null;
 let pageEditor = null;
-// load editorTheme
-const editorTheme = crowi.loadEditorTheme();
 // render PageEditor
 // render PageEditor
 const pageEditorElem = document.getElementById('page-editor');
 const pageEditorElem = document.getElementById('page-editor');
 if (pageEditorElem) {
 if (pageEditorElem) {
   pageEditor = ReactDOM.render(
   pageEditor = ReactDOM.render(
     <PageEditor crowi={crowi} pageId={pageId} revisionId={pageRevisionId} pagePath={pagePath}
     <PageEditor crowi={crowi} pageId={pageId} revisionId={pageRevisionId} pagePath={pagePath}
-        markdown={entities.decodeHTML(pageContent)} editorTheme={editorTheme}
+        markdown={entities.decodeHTML(pageContent)} editorOptions={crowi.editorOptions}
         onSaveSuccess={onSaveSuccess} />,
         onSaveSuccess={onSaveSuccess} />,
     pageEditorElem
     pageEditorElem
   );
   );
   // set refs for pageEditor
   // set refs for pageEditor
   crowi.setPageEditor(pageEditor);
   crowi.setPageEditor(pageEditor);
 }
 }
-// render ThemeSelector
-const themeSelectorElem = document.getElementById('page-editor-theme-selector');
-if (themeSelectorElem) {
+// render EditorOptionsSelector
+const editorOptionSelectorElem = document.getElementById('page-editor-options-selector');
+if (editorOptionSelectorElem) {
   ReactDOM.render(
   ReactDOM.render(
-    <ThemeSelector value={editorTheme}
-        onChange={(value) => { // set onChange event handler
-          pageEditor.setEditorTheme(value);
-          crowi.saveEditorTheme(value);
+    <EditorOptionsSelector options={crowi.editorOptions}
+        onChange={(opts) => { // set onChange event handler
+          pageEditor.setEditorOptions(opts);
+          crowi.saveEditorOptions(opts);
         }} />,
         }} />,
-    themeSelectorElem
+    editorOptionSelectorElem
   );
   );
 }
 }
 
 

+ 7 - 7
resource/js/components/PageEditor.js

@@ -21,7 +21,7 @@ export default class PageEditor extends React.Component {
       markdown: this.props.markdown,
       markdown: this.props.markdown,
       isUploadable,
       isUploadable,
       isUploadableFile,
       isUploadableFile,
-      editorTheme: this.props.editorTheme,
+      editorOptions: this.props.editorOptions,
     };
     };
 
 
     this.setCaretLine = this.setCaretLine.bind(this);
     this.setCaretLine = this.setCaretLine.bind(this);
@@ -62,11 +62,11 @@ export default class PageEditor extends React.Component {
   }
   }
 
 
   /**
   /**
-   * set theme (used from the outside)
-   * @param {string} theme theme name
+   * set options (used from the outside)
+   * @param {object} editorOptions
    */
    */
-  setEditorTheme(theme) {
-    this.setState({ editorTheme: theme });
+  setEditorOptions(editorOptions) {
+    this.setState({ editorOptions });
   }
   }
 
 
   /**
   /**
@@ -287,9 +287,9 @@ export default class PageEditor extends React.Component {
       <div className="row">
       <div className="row">
         <div className="col-md-6 col-sm-12 page-editor-editor-container">
         <div className="col-md-6 col-sm-12 page-editor-editor-container">
           <Editor ref="editor" value={this.state.markdown}
           <Editor ref="editor" value={this.state.markdown}
+              editorOptions={this.state.editorOptions}
               isUploadable={this.state.isUploadable}
               isUploadable={this.state.isUploadable}
               isUploadableFile={this.state.isUploadableFile}
               isUploadableFile={this.state.isUploadableFile}
-              theme={this.state.editorTheme}
               onScroll={this.onEditorScroll}
               onScroll={this.onEditorScroll}
               onChange={this.onMarkdownChanged}
               onChange={this.onMarkdownChanged}
               onSave={this.onSave}
               onSave={this.onSave}
@@ -311,5 +311,5 @@ PageEditor.propTypes = {
   revisionId: PropTypes.string,
   revisionId: PropTypes.string,
   pagePath: PropTypes.string,
   pagePath: PropTypes.string,
   onSaveSuccess: PropTypes.func,
   onSaveSuccess: PropTypes.func,
-  editorTheme: PropTypes.string,
+  editorOptions: PropTypes.object,
 };
 };

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

@@ -14,6 +14,7 @@ require('codemirror/addon/hint/show-hint');
 require('codemirror/addon/hint/show-hint.css');
 require('codemirror/addon/hint/show-hint.css');
 require('codemirror/addon/search/searchcursor');
 require('codemirror/addon/search/searchcursor');
 require('codemirror/addon/search/match-highlighter');
 require('codemirror/addon/search/match-highlighter');
+require('codemirror/addon/selection/active-line');
 require('codemirror/addon/scroll/annotatescrollbar');
 require('codemirror/addon/scroll/annotatescrollbar');
 require('codemirror/addon/fold/foldcode');
 require('codemirror/addon/fold/foldcode');
 require('codemirror/addon/fold/foldgutter');
 require('codemirror/addon/fold/foldgutter');
@@ -270,7 +271,8 @@ export default class Editor extends React.Component {
       height: 'calc(100% - 20px)'
       height: 'calc(100% - 20px)'
     }
     }
 
 
-    const theme = this.props.theme || 'elegant';
+    const theme = this.props.editorOptions.theme || 'elegant';
+    const styleActiveLine = this.props.editorOptions.styleActiveLine || undefined;
     return (
     return (
       <div style={flexContainer}>
       <div style={flexContainer}>
         <Dropzone
         <Dropzone
@@ -298,6 +300,7 @@ export default class Editor extends React.Component {
             options={{
             options={{
               mode: 'gfm',
               mode: 'gfm',
               theme: theme,
               theme: theme,
+              styleActiveLine: styleActiveLine,
               lineNumbers: true,
               lineNumbers: true,
               tabSize: 4,
               tabSize: 4,
               indentUnit: 4,
               indentUnit: 4,
@@ -354,7 +357,7 @@ export default class Editor extends React.Component {
 
 
 Editor.propTypes = {
 Editor.propTypes = {
   value: PropTypes.string,
   value: PropTypes.string,
-  theme: PropTypes.string,
+  options: PropTypes.object,
   isUploadable: PropTypes.bool,
   isUploadable: PropTypes.bool,
   isUploadableFile: PropTypes.bool,
   isUploadableFile: PropTypes.bool,
   onChange: PropTypes.func,
   onChange: PropTypes.func,
@@ -362,3 +365,4 @@ Editor.propTypes = {
   onSave: PropTypes.func,
   onSave: PropTypes.func,
   onUpload: PropTypes.func,
   onUpload: PropTypes.func,
 };
 };
+

+ 99 - 0
resource/js/components/PageEditor/EditorOptionsSelector.js

@@ -0,0 +1,99 @@
+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';
+import Button from 'react-bootstrap/es/Button';
+
+export default class EditorOptionsSelector extends React.Component {
+
+  constructor(props) {
+    super(props);
+
+    this.state = {
+      options: this.props.options,
+    }
+
+    this.availableThemes = [
+      'elegant', 'neo', 'mdn-like', 'material', 'monokai', 'twilight'
+    ]
+
+    this.onChangeTheme = this.onChangeTheme.bind(this);
+    this.onClickStyleActiveLine = this.onClickStyleActiveLine.bind(this);
+  }
+
+  componentDidMount() {
+    this.init();
+  }
+
+  init() {
+    this.themeSelectorInputEl.value = this.state.options.theme || this.availableThemes[0];
+  }
+
+  onChangeTheme() {
+    const newValue = this.themeSelectorInputEl.value;
+    const newOpts = Object.assign(this.state.options, {theme: newValue});
+    this.setState({options: newOpts});
+
+    // dispatch event
+    this.dispatchOnChange();
+  }
+
+  onClickStyleActiveLine(event) {
+    const newValue = !this.state.options.styleActiveLine;
+    console.log(newValue);
+    const newOpts = Object.assign(this.state.options, {styleActiveLine: newValue});
+    this.setState({options: newOpts});
+
+    // dispatch event
+    this.dispatchOnChange();
+  }
+
+  dispatchOnChange() {
+    if (this.props.onChange != null) {
+      this.props.onChange(this.state.options);
+    }
+  }
+
+  renderThemeSelector() {
+    const optionElems = this.availableThemes.map((theme) => {
+      return <option key={theme} value={theme}>{theme}</option>;
+    });
+
+    return (
+      <FormGroup controlId="formControlsSelect">
+        <ControlLabel>Theme:</ControlLabel>
+        <FormControl componentClass="select" placeholder="select"
+            onChange={this.onChangeTheme}
+            inputRef={ el => this.themeSelectorInputEl=el }>
+
+          {optionElems}
+
+        </FormControl>
+      </FormGroup>
+    )
+  }
+
+  renderStyleActiveLineSelector() {
+    const bool = this.state.options.styleActiveLine || false;
+    return (
+      <FormGroup controlId="formControlsSelect">
+        <Button active={bool} className="btn-style-active-line"
+            onClick={this.onClickStyleActiveLine}
+            ref="styleActiveLineButton">
+          Active Line
+        </Button>
+      </FormGroup>
+    )
+  }
+
+  render() {
+    return <span>{this.renderThemeSelector()} {this.renderStyleActiveLineSelector()}</span>
+  }
+}
+
+EditorOptionsSelector.propTypes = {
+  options: PropTypes.object,
+  onChange: PropTypes.func,
+};

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

@@ -1,57 +0,0 @@
-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,
-};

+ 4 - 6
resource/js/util/Crowi.js

@@ -39,6 +39,7 @@ export default class Crowi {
     this.userByName = {};
     this.userByName = {};
     this.userById   = {};
     this.userById   = {};
     this.draft = {};
     this.draft = {};
+    this.editorOptions = {};
 
 
     this.recoverData();
     this.recoverData();
   }
   }
@@ -72,6 +73,7 @@ export default class Crowi {
       'userById',
       'userById',
       'users',
       'users',
       'draft',
       'draft',
+      'editorOptions',
     ];
     ];
 
 
     keys.forEach(key => {
     keys.forEach(key => {
@@ -147,12 +149,8 @@ export default class Crowi {
     return null;
     return null;
   }
   }
 
 
-  saveEditorTheme(theme) {
-    this.localStorage.setItem('editorTheme', theme);
-  }
-
-  loadEditorTheme() {
-    return this.localStorage.getItem('editorTheme');
+  saveEditorOptions(options) {
+    this.localStorage.setItem('editorOptions', JSON.stringify(options));
   }
   }
 
 
   findUserById(userId) {
   findUserById(userId) {