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

Merge pull request #652 from weseek/imprv/handsontable-ux

Imprv/handsontable ux
Yuki Takei 7 лет назад
Родитель
Сommit
a8c1e5f93a

+ 2 - 2
package.json

@@ -123,7 +123,7 @@
   },
   "devDependencies": {
     "@alienfast/i18next-loader": "^1.0.16",
-    "@handsontable/react": "^1.1.0",
+    "@handsontable/react": "^2.0.0",
     "autoprefixer": "^9.0.0",
     "babel-core": "^6.25.0",
     "babel-loader": "^7.1.1",
@@ -152,7 +152,7 @@
     "eslint-plugin-react": "^7.7.0",
     "extract-text-webpack-plugin": "^4.0.0-beta.0",
     "file-loader": "^2.0.0",
-    "handsontable": "^5.0.1",
+    "handsontable": "^6.0.1",
     "i18next-browser-languagedetector": "^2.2.0",
     "imports-loader": "^0.8.0",
     "jquery-slimscroll": "^1.3.8",

+ 69 - 15
src/client/js/components/PageEditor/HandsontableModal.jsx

@@ -6,6 +6,7 @@ import Button from 'react-bootstrap/es/Button';
 import Navbar from 'react-bootstrap/es/Navbar';
 import ButtonGroup from 'react-bootstrap/es/ButtonGroup';
 
+import { debounce } from 'throttle-debounce';
 
 import Handsontable from 'handsontable';
 import { HotTable } from '@handsontable/react';
@@ -13,14 +14,20 @@ import { HotTable } from '@handsontable/react';
 import MarkdownTable from '../../models/MarkdownTable';
 import HandsontableUtil from './HandsontableUtil';
 
+const DEFAULT_HOT_HEIGHT = 300;
+
 export default class HandsontableModal extends React.Component {
+
+
   constructor(props) {
     super(props);
 
     this.state = {
       show: false,
+      isWindowExpanded: false,
       markdownTableOnInit: HandsontableModal.getDefaultMarkdownTable(),
       markdownTable: HandsontableModal.getDefaultMarkdownTable(),
+      handsontableHeight: DEFAULT_HOT_HEIGHT,
       handsontableSetting: HandsontableModal.getDefaultHandsontableSetting()
     };
 
@@ -28,6 +35,11 @@ export default class HandsontableModal extends React.Component {
     this.reset = this.reset.bind(this);
     this.cancel = this.cancel.bind(this);
     this.save = this.save.bind(this);
+    this.expandWindow = this.expandWindow.bind(this);
+    this.contractWindow = this.contractWindow.bind(this);
+
+    // create debounced method for expanding HotTable
+    this.expandHotTableHeightWithDebounce = debounce(100, this.expandHotTableHeight);
   }
 
   init(markdownTable) {
@@ -93,13 +105,54 @@ export default class HandsontableModal extends React.Component {
     HandsontableUtil.setClassNameToColumns(this.refs.hotTable.hotInstance, startCol, endCol, className);
   }
 
+  expandWindow() {
+    this.setState({ isWindowExpanded: true });
+
+    // invoke updateHotTableHeight method with delay
+    // cz. Resizing this.refs.hotTableContainer is completeted after a little delay after 'isWindowExpanded' set with 'true'
+    this.expandHotTableHeightWithDebounce();
+  }
+
+  contractWindow() {
+    this.setState({ isWindowExpanded: false, handsontableHeight: DEFAULT_HOT_HEIGHT });
+  }
+
+  /**
+   * Expand the height of the Handsontable
+   *  by updating 'handsontableHeight' state
+   *  according to the height of this.refs.hotTableContainer
+   */
+  expandHotTableHeight() {
+    if (this.state.isWindowExpanded && this.refs.hotTableContainer != null) {
+      const height = this.refs.hotTableContainer.getBoundingClientRect().height;
+      this.setState({ handsontableHeight: height });
+    }
+  }
+
+  renderExpandOrContractButton() {
+    const iconClassName = this.state.isWindowExpanded ? 'icon-size-actual' : 'icon-size-fullscreen';
+    return (
+      <button className="close mr-3" onClick={this.state.isWindowExpanded ? this.contractWindow : this.expandWindow}>
+        <i className={iconClassName} style={{ fontSize: '0.8em' }} aria-hidden="true"></i>
+      </button>
+    );
+  }
+
   render() {
+    const dialogClassNames = ['handsontable-modal'];
+    if (this.state.isWindowExpanded) {
+      dialogClassNames.push('handsontable-modal-expanded');
+    }
+
+    const dialogClassName = dialogClassNames.join(' ');
+
     return (
-      <Modal show={this.state.show} onHide={this.cancel} bsSize="large" dialogClassName="handsontable-modal">
+      <Modal show={this.state.show} onHide={this.cancel} bsSize="large" dialogClassName={dialogClassName}>
         <Modal.Header closeButton>
+          { this.renderExpandOrContractButton() }
           <Modal.Title>Edit Table</Modal.Title>
         </Modal.Header>
-        <Modal.Body className="p-0">
+        <Modal.Body className="p-0 d-flex flex-column">
           <Navbar>
             <Navbar.Form>
               <ButtonGroup>
@@ -109,8 +162,8 @@ export default class HandsontableModal extends React.Component {
               </ButtonGroup>
             </Navbar.Form>
           </Navbar>
-          <div className="p-4">
-            <HotTable ref='hotTable' data={this.state.markdownTable.table} settings={this.state.handsontableSetting} />
+          <div ref="hotTableContainer" className="m-4 hot-table-container">
+            <HotTable ref='hotTable' data={this.state.markdownTable.table} settings={this.state.handsontableSetting} height={this.state.handsontableHeight} />
           </div>
         </Modal.Body>
         <Modal.Footer>
@@ -141,9 +194,19 @@ export default class HandsontableModal extends React.Component {
 
   static getDefaultHandsontableSetting() {
     return {
-      height: 300,
       rowHeaders: true,
       colHeaders: true,
+      manualRowMove: true,
+      manualRowResize: true,
+      manualColumnMove: true,
+      manualColumnResize: true,
+      selectionMode: 'multiple',
+      outsideClickDeselects: false,
+
+      modifyColWidth: function(width) {
+        return Math.max(80, Math.min(400, width));
+      },
+
       contextMenu: {
         items: {
           'row_above': {}, 'row_below': {}, 'col_left': {}, 'col_right': {},
@@ -174,17 +237,8 @@ export default class HandsontableModal extends React.Component {
             }
           }
         }
-      },
-      selectionMode: 'multiple',
-      outsideClickDeselects: false,
-      modifyColWidth: function(width) {
-        if (width < 100) {
-          return 100;
-        }
-        if (width > 300) {
-          return 300;
-        }
       }
+
     };
   }
 }

+ 27 - 2
src/client/styles/scss/_handsontable.scss

@@ -8,6 +8,31 @@
   }
 }
 
-.handsontable-modal.modal-lg {
-  width: 90%;
+// expanded window layout
+.handsontable-modal.handsontable-modal-expanded {
+  // full-screen modal
+  width: 97%;
+  height: 95%;
+  .modal-content {
+    height: 95%;
+  }
+
+  // expand .modal-body (with calculating height)
+  .modal-body {
+    $modal-header: 54px;
+    $modal-footer: 46px;
+    $margin: $modal-header + $modal-footer;
+    height: calc(100% - #{$margin});
+
+    // expand .hot-table-container (with flexbox)
+    .hot-table-container {
+      flex: 1;
+    }
+  }
+}
+
+// Prevent handsontable/handsontable #2937 (Manual column resize does not work when handsontable is loaded inside Bootstrap 3.0 Modal)
+// see https://github.com/handsontable/handsontable/issues/2937#issuecomment-287390111
+.modal.in .modal-dialog.handsontable-modal {
+  transform: none;
 }

+ 6 - 6
yarn.lock

@@ -27,9 +27,9 @@
   version "1.3.0"
   resolved "https://registry.yarnpkg.com/@browser-bunyan/levels/-/levels-1.3.0.tgz#a052303ae5d1a1f9b63eeb3a94495a2f429f4831"
 
-"@handsontable/react@^1.1.0":
-  version "1.1.0"
-  resolved "https://registry.yarnpkg.com/@handsontable/react/-/react-1.1.0.tgz#7f7cc822bc4cfab26f843792982ef81838e82d07"
+"@handsontable/react@^2.0.0":
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/@handsontable/react/-/react-2.0.0.tgz#30d9c2bd05421588a6ed1b3050b1f7dc476b35d3"
 
 "@sinonjs/commons@^1.0.2":
   version "1.0.2"
@@ -4042,9 +4042,9 @@ gzip-size@^5.0.0:
     duplexer "^0.1.1"
     pify "^3.0.0"
 
-handsontable@^5.0.1:
-  version "5.0.1"
-  resolved "https://registry.yarnpkg.com/handsontable/-/handsontable-5.0.1.tgz#4aadbaf1a468d8c7b3cdbf8a5f49c4110879c373"
+handsontable@^6.0.1:
+  version "6.0.1"
+  resolved "https://registry.yarnpkg.com/handsontable/-/handsontable-6.0.1.tgz#93f07d895b42335e2882044a79bca96003a2cab2"
   dependencies:
     moment "2.20.1"
     numbro "^2.0.6"