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

Merge branch 'master' into fix/fix-header-edit-button-duplication

utsushiiro 7 лет назад
Родитель
Сommit
7bfb04f72c

+ 71 - 19
src/client/js/components/PageEditor/HandsontableModal.jsx

@@ -4,9 +4,11 @@ import PropTypes from 'prop-types';
 import Modal from 'react-bootstrap/es/Modal';
 import Button from 'react-bootstrap/es/Button';
 
+import Handsontable from 'handsontable';
 import { HotTable } from '@handsontable/react';
 
 import MarkdownTable from '../../models/MarkdownTable';
+import HandsontableUtil from './HandsontableUtil';
 
 export default class HandsontableModal extends React.Component {
   constructor(props) {
@@ -15,16 +17,8 @@ export default class HandsontableModal extends React.Component {
     this.state = {
       show: false,
       markdownTableOnInit: HandsontableModal.getDefaultMarkdownTable(),
-      markdownTable: HandsontableModal.getDefaultMarkdownTable()
-    };
-
-    this.settings = {
-      height: 300,
-      rowHeaders: true,
-      colHeaders: true,
-      contextMenu: ['row_above', 'row_below', 'col_left', 'col_right', '---------', 'remove_row', 'remove_col'],
-      stretchH: 'all',
-      selectionMode: 'multiple',
+      markdownTable: HandsontableModal.getDefaultMarkdownTable(),
+      handsontableSetting: HandsontableModal.getDefaultHandsotableSetting()
     };
 
     this.init = this.init.bind(this);
@@ -35,8 +29,16 @@ export default class HandsontableModal extends React.Component {
 
   init(markdownTable) {
     const initMarkdownTable = markdownTable || HandsontableModal.getDefaultMarkdownTable();
-    this.setState({ markdownTableOnInit: initMarkdownTable });
-    this.setState({ markdownTable: initMarkdownTable.clone() });
+    this.setState(
+      {
+        markdownTableOnInit: initMarkdownTable,
+        markdownTable: initMarkdownTable.clone(),
+        handsontableSetting: Object.assign({}, this.state.handsontableSetting, {
+          afterUpdateSettings: HandsontableUtil.createHandlerToSynchronizeHandontableAlignWith(initMarkdownTable.options.align),
+          loadData: HandsontableUtil.createHandlerToSynchronizeHandontableAlignWith(initMarkdownTable.options.align)
+        })
+      }
+    );
   }
 
   show(markdownTable) {
@@ -53,9 +55,13 @@ export default class HandsontableModal extends React.Component {
   }
 
   save() {
+    let newMarkdownTable = this.state.markdownTable.clone();
+    newMarkdownTable.options.align = HandsontableUtil.getMarkdownTableAlignmentFrom(this.refs.hotTable.hotInstance);
+
     if (this.props.onSave != null) {
-      this.props.onSave(this.state.markdownTable);
+      this.props.onSave(newMarkdownTable);
     }
+
     this.setState({ show: false });
   }
 
@@ -67,7 +73,7 @@ export default class HandsontableModal extends React.Component {
         </Modal.Header>
         <Modal.Body className="p-0">
           <div className="p-4">
-            <HotTable data={this.state.markdownTable.table} settings={this.settings} />
+            <HotTable ref='hotTable' data={this.state.markdownTable.table} settings={this.state.handsontableSetting} />
           </div>
         </Modal.Body>
         <Modal.Footer>
@@ -84,11 +90,57 @@ export default class HandsontableModal extends React.Component {
   }
 
   static getDefaultMarkdownTable() {
-    return new MarkdownTable([
-      ['col1', 'col2', 'col3'],
-      ['', '', ''],
-      ['', '', ''],
-    ]);
+    return new MarkdownTable(
+      [
+        ['col1', 'col2', 'col3'],
+        ['', '', ''],
+        ['', '', ''],
+      ],
+      {
+        align: ['', '', '']
+      }
+    );
+  }
+
+  static getDefaultHandsotableSetting() {
+    return {
+      height: 300,
+      rowHeaders: true,
+      colHeaders: true,
+      contextMenu: {
+        items: {
+          'row_above': {}, 'row_below': {}, 'col_left': {}, 'col_right': {},
+          'separator1': Handsontable.plugins.ContextMenu.SEPARATOR,
+          'remove_row': {}, 'remove_col': {},
+          'separator2': Handsontable.plugins.ContextMenu.SEPARATOR,
+          'custom_alignment': {
+            name: 'Align columns',
+            key: 'align_columns',
+            submenu: {
+              items: [{
+                name: 'Left',
+                key: 'align_columns:1',
+                callback: function(key, selection) {
+                  HandsontableUtil.setClassNameToColumns(this, selection[0].start.col, selection[0].end.col, 'htLeft');
+                }}, {
+                name: 'Center',
+                key: 'align_columns:2',
+                callback: function(key, selection) {
+                  HandsontableUtil.setClassNameToColumns(this, selection[0].start.col, selection[0].end.col, 'htCenter');
+                }}, {
+                name: 'Right',
+                key: 'align_columns:3',
+                callback: function(key, selection) {
+                  HandsontableUtil.setClassNameToColumns(this, selection[0].start.col, selection[0].end.col, 'htRight');
+                }}
+              ]
+            }
+          }
+        }
+      },
+      stretchH: 'all',
+      selectionMode: 'multiple'
+    };
   }
 }
 

+ 54 - 0
src/client/js/components/PageEditor/HandsontableUtil.js

@@ -0,0 +1,54 @@
+/**
+ * Utility for Handsontable (and cooperation with MarkdownTable)
+ */
+export default class HandsontableUtil {
+
+  static setClassNameToColumns(core, startCol, endCol, className) {
+    for (let i = startCol; i <= endCol; i++) {
+      for (let j = 0; j < core.countRows(); j++) {
+        core.setCellMeta(j, i, 'className', className);
+      }
+    }
+    core.render();
+  }
+
+  /**
+   * return a function(handsontable event handler) to adjust the handsontable alignment to the markdown table
+   */
+  static createHandlerToSynchronizeHandontableAlignWith(markdownTableAlign) {
+    const mapping = {
+      'r': 'htRight',
+      'c': 'htCenter',
+      'l': 'htLeft',
+      '': ''
+    };
+
+    return function() {
+      const align = markdownTableAlign;
+      for (let i = 0; i < align.length; i++) {
+        HandsontableUtil.setClassNameToColumns(this, i, i, mapping[align[i]]);
+      }
+    };
+  }
+
+  /**
+   * return MarkdownTable alignment retrieved from Handsontable instance
+   */
+  static getMarkdownTableAlignmentFrom(handsontable) {
+    const cellMetasAtFirstRow = handsontable.getCellMetaAtRow(0);
+    const mapping = {
+      'htRight': 'r',
+      'htCenter': 'c',
+      'htLeft': 'l',
+      '': ''
+    };
+
+    let align = [];
+    for (let i = 0; i < cellMetasAtFirstRow.length; i++) {
+      align.push(mapping[cellMetasAtFirstRow[i].className]);
+    }
+
+    return align;
+  }
+}
+

+ 1 - 1
src/server/models/page.js

@@ -540,7 +540,7 @@ module.exports = function(crowi) {
     const Page = this;
     const templatePath = cutOffLastSlash(path);
     const pathList = generatePathsOnTree(templatePath, []);
-    const regexpList = pathList.map(path => new RegExp(`^${path}/_{1,2}template$`));
+    const regexpList = pathList.map(path => new RegExp(`^${escapeStringRegexp(path)}/_{1,2}template$`));
 
     return Page
       .find({path: {$in: regexpList}})