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

impl setCaretLine and focusToEditor

Yuki Takei 8 лет назад
Родитель
Сommit
14b53db23b

+ 1 - 0
package.json

@@ -83,6 +83,7 @@
     "express-session": "~1.15.0",
     "express-session": "~1.15.0",
     "express-webpack-assets": "^0.1.0",
     "express-webpack-assets": "^0.1.0",
     "file-loader": "^1.1.0",
     "file-loader": "^1.1.0",
+    "get-line-from-pos": "^1.0.0",
     "googleapis": "^23.0.0",
     "googleapis": "^23.0.0",
     "graceful-fs": "^4.1.11",
     "graceful-fs": "^4.1.11",
     "highlight.js": "^9.10.0",
     "highlight.js": "^9.10.0",

+ 2 - 1
resource/js/app.js

@@ -127,4 +127,5 @@ $('a[data-toggle="tab"][href="#revision-history"]').on('show.bs.tab', function()
   ReactDOM.render(<PageHistory pageId={pageId} crowi={crowi} />, document.getElementById('revision-history'));
   ReactDOM.render(<PageHistory pageId={pageId} crowi={crowi} />, document.getElementById('revision-history'));
 });
 });
 
 
-crowi.componentInstances = componentInstances;
+// set refs for pageEditor
+crowi.setPageEditor(componentInstances['page-editor']);

+ 10 - 6
resource/js/components/PageEditor.js

@@ -15,17 +15,21 @@ export default class PageEditor extends React.Component {
     // initial preview
     // initial preview
     this.renderPreview();
     this.renderPreview();
 
 
-    this.initCaretPosition = this.initCaretPosition.bind(this);
+    this.setCaretLine = this.setCaretLine.bind(this);
+    this.focusToEditor = this.focusToEditor.bind(this);
     this.onMarkdownChanged = this.onMarkdownChanged.bind(this);
     this.onMarkdownChanged = this.onMarkdownChanged.bind(this);
   }
   }
 
 
+  focusToEditor() {
+    this.refs.editor.forceToFocus();
+  }
+
   /**
   /**
-   * initialize caret position of editor
-   * @param {string} value
+   * set caret position of editor
+   * @param {number} line
    */
    */
-  initCaretPosition(position) {
-    console.log(this.refs.editor);
-    this.refs.editor.initCaretPosition(position);
+  setCaretLine(line) {
+    this.refs.editor.setCaretLine(line);
   }
   }
 
 
   /**
   /**

+ 22 - 8
resource/js/components/PageEditor/Editor.js

@@ -26,23 +26,37 @@ export default class Editor extends React.Component {
     };
     };
 
 
     this.getCodeMirror = this.getCodeMirror.bind(this);
     this.getCodeMirror = this.getCodeMirror.bind(this);
-    // this.initCaretPosition = this.initCaretPosition.bind(this);
+    this.setCaretLine = this.setCaretLine.bind(this);
+    this.forceToFocus = this.forceToFocus.bind(this);
+  }
+
+  componentDidMount() {
+    // initialize caret line
+    this.setCaretLine(0);
   }
   }
 
 
   getCodeMirror() {
   getCodeMirror() {
     return this.refs.cm.editor;
     return this.refs.cm.editor;
   }
   }
 
 
+  forceToFocus() {
+    const editor = this.getCodeMirror();
+    // use setInterval with reluctance -- 2018.01.11 Yuki Takei
+    const intervalId = setInterval(() => {
+      this.getCodeMirror().focus();
+      if (editor.hasFocus()) {
+        clearInterval(intervalId);
+      }
+    }, 100);
+  }
+
   /**
   /**
-   * initialize caret position of codemirror
-   * @param {string} value
+   * set caret position of codemirror
+   * @param {string} number
    */
    */
-  initCaretPosition(position) {
+  setCaretLine(line) {
     const editor = this.getCodeMirror();
     const editor = this.getCodeMirror();
-    console.log(editor);
-    // editor.setCursor(0, position);
-    editor.focus();
-    editor.setCursor(3, 3);
+    editor.setCursor({line: line-1});   // leave 'ch' field as null/undefined to indicate the end of line
   }
   }
 
 
   render() {
   render() {

+ 0 - 34
resource/js/legacy/crowi-form.js

@@ -47,40 +47,6 @@
     $('.content-main').removeClass('on-edit');
     $('.content-main').removeClass('on-edit');
   });
   });
 
 
-  // detect mutations for #edit-form
-  const targetSelector = '#edit-form';
-  var mo = new MutationObserver(function(mutations){
-    // initialize caret position when '#edit-form' activated
-    if (mutations[0].target.classList.contains('active')) {
-      initCaretPosition();
-    }
-  });
-  mo.observe(
-    document.querySelector(targetSelector),
-    {
-      attributes: true,
-      attributeFilter: ['class'],
-    }
-  );
-
-  /**
-   * scroll the textarea named '#form-body' according to the attribute 'data-caret-position'
-   *  that is set in Crowi.setScrollPositionToFormBody
-   */
-  function initCaretPosition() {
-    const pageEditorDom = document.querySelector('#page-editor');
-    const position = pageEditorDom.getAttribute('data-caret-position');
-
-    if (position == null) {
-      return;
-    }
-
-    // get the PageEditor component instance
-    const pageEditor = crowi.componentInstances['page-editor']
-    // init position
-    pageEditor.initCaretPosition(position);
-  }
-
 /**
 /**
  * DOM ready
  * DOM ready
  */
  */

+ 31 - 6
resource/js/legacy/crowi.js

@@ -4,6 +4,7 @@
 
 
 var io = require('socket.io-client');
 var io = require('socket.io-client');
 var entities = require("entities");
 var entities = require("entities");
+var getLineFromPos = require('get-line-from-pos');
 require('bootstrap-sass');
 require('bootstrap-sass');
 require('jquery.cookie');
 require('jquery.cookie');
 
 
@@ -46,11 +47,12 @@ Crowi.appendEditSectionButtons = function(contentId, markdown) {
     if (position < 0) { // if not found, search with header text only
     if (position < 0) { // if not found, search with header text only
       position = markdown.search(text);
       position = markdown.search(text);
     }
     }
+    const line = getLineFromPos(markdown, position);
 
 
     // add button
     // add button
     $(this).append(`
     $(this).append(`
       <span class="revision-head-edit-button">
       <span class="revision-head-edit-button">
-        <a href="#edit-form" onClick="Crowi.setCaretPositionData(${position})">
+        <a href="#edit-form" onClick="Crowi.setCaretLineData(${line})">
           <i class="fa fa-edit"></i>
           <i class="fa fa-edit"></i>
         </a>
         </a>
       </span>
       </span>
@@ -60,14 +62,31 @@ Crowi.appendEditSectionButtons = function(contentId, markdown) {
 };
 };
 
 
 /**
 /**
- * set 'data-caret-position' attribute that will be processed in crowi-form.js
- * @param {number} position
+ * set 'data-caret-line' attribute that will be processed when 'shown.bs.tab' event fired
+ * @param {number} line
  */
  */
-Crowi.setCaretPositionData = function(position) {
-  const formBody = document.querySelector('#page-editor');
-  formBody.setAttribute('data-caret-position', position);
+Crowi.setCaretLineData = function(line) {
+  const pageEditorDom = document.querySelector('#page-editor');
+  pageEditorDom.setAttribute('data-caret-line', line);
 }
 }
 
 
+/**
+ * invoked when 'shown.bs.tab' event fired
+ */
+Crowi.setCaretLineAndFocusToEditor = function() {
+  // get 'data-caret-line' attributes
+  const pageEditorDom = document.querySelector('#page-editor');
+  const line = pageEditorDom.getAttribute('data-caret-line');
+
+  if (line != null) {
+    crowi.setCaretLine(line);
+    // reset data-caret-line attribute
+    pageEditorDom.removeAttribute('data-caret-line');
+  }
+
+  // focus
+  crowi.focusToEditor();
+}
 
 
 Crowi.revisionToc = function(contentId, tocId) {
 Crowi.revisionToc = function(contentId, tocId) {
   var $content = $(contentId || '#revision-body-content');
   var $content = $(contentId || '#revision-body-content');
@@ -802,6 +821,11 @@ $(function() {
       window.location.replace('#edit-form');
       window.location.replace('#edit-form');
     });
     });
   }
   }
+
+  // focus to editor when 'shown.bs.tab' event fired
+  $('a[href="#edit-form"]').on('shown.bs.tab', function(e) {
+    Crowi.setCaretLineAndFocusToEditor();
+  });
 });
 });
 
 
 Crowi.getRevisionBodyContent = function() {
 Crowi.getRevisionBodyContent = function() {
@@ -885,6 +909,7 @@ window.addEventListener('load', function(e) {
 
 
   Crowi.highlightSelectedSection(location.hash);
   Crowi.highlightSelectedSection(location.hash);
   Crowi.modifyScrollTop();
   Crowi.modifyScrollTop();
+  Crowi.setCaretLineAndFocusToEditor();
 });
 });
 
 
 window.addEventListener('hashchange', function(e) {
 window.addEventListener('hashchange', function(e) {

+ 13 - 1
resource/js/util/Crowi.js

@@ -19,7 +19,7 @@ export default class Crowi {
     this.location = window.location || {};
     this.location = window.location || {};
     this.document = window.document || {};
     this.document = window.document || {};
     this.localStorage = window.localStorage || {};
     this.localStorage = window.localStorage || {};
-    this.componentInstances = undefined;
+    this.pageEditor = undefined;
 
 
     this.fetchUsers = this.fetchUsers.bind(this);
     this.fetchUsers = this.fetchUsers.bind(this);
     this.apiGet = this.apiGet.bind(this);
     this.apiGet = this.apiGet.bind(this);
@@ -62,6 +62,10 @@ export default class Crowi {
     return this.config;
     return this.config;
   }
   }
 
 
+  setPageEditor(pageEditor) {
+    this.pageEditor = pageEditor;
+  }
+
   recoverData() {
   recoverData() {
     const keys = [
     const keys = [
       'userByName',
       'userByName',
@@ -113,6 +117,14 @@ export default class Crowi {
     });
     });
   }
   }
 
 
+  setCaretLine(line) {
+    this.pageEditor.setCaretLine(line);
+  }
+
+  focusToEditor() {
+    this.pageEditor.focusToEditor();
+  }
+
   clearDraft(path) {
   clearDraft(path) {
     delete this.draft[path];
     delete this.draft[path];
     this.localStorage.draft = JSON.stringify(this.draft);
     this.localStorage.draft = JSON.stringify(this.draft);

+ 4 - 0
yarn.lock

@@ -2583,6 +2583,10 @@ get-func-name@^2.0.0:
   version "2.0.0"
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41"
   resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41"
 
 
+get-line-from-pos@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/get-line-from-pos/-/get-line-from-pos-1.0.0.tgz#e3ca483035eef374ad40fff4d43df3fa2da328b3"
+
 get-stdin@^4.0.1:
 get-stdin@^4.0.1:
   version "4.0.1"
   version "4.0.1"
   resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe"
   resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe"