|
@@ -3,6 +3,10 @@ import PropTypes from 'prop-types';
|
|
|
|
|
|
|
|
import Modal from 'react-bootstrap/es/Modal';
|
|
import Modal from 'react-bootstrap/es/Modal';
|
|
|
import Button from 'react-bootstrap/es/Button';
|
|
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 Handsontable from 'handsontable';
|
|
|
import { HotTable } from '@handsontable/react';
|
|
import { HotTable } from '@handsontable/react';
|
|
@@ -10,21 +14,32 @@ import { HotTable } from '@handsontable/react';
|
|
|
import MarkdownTable from '../../models/MarkdownTable';
|
|
import MarkdownTable from '../../models/MarkdownTable';
|
|
|
import HandsontableUtil from './HandsontableUtil';
|
|
import HandsontableUtil from './HandsontableUtil';
|
|
|
|
|
|
|
|
|
|
+const DEFAULT_HOT_HEIGHT = 300;
|
|
|
|
|
+
|
|
|
export default class HandsontableModal extends React.Component {
|
|
export default class HandsontableModal extends React.Component {
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
constructor(props) {
|
|
constructor(props) {
|
|
|
super(props);
|
|
super(props);
|
|
|
|
|
|
|
|
this.state = {
|
|
this.state = {
|
|
|
show: false,
|
|
show: false,
|
|
|
|
|
+ isWindowExpanded: false,
|
|
|
markdownTableOnInit: HandsontableModal.getDefaultMarkdownTable(),
|
|
markdownTableOnInit: HandsontableModal.getDefaultMarkdownTable(),
|
|
|
markdownTable: HandsontableModal.getDefaultMarkdownTable(),
|
|
markdownTable: HandsontableModal.getDefaultMarkdownTable(),
|
|
|
- handsontableSetting: HandsontableModal.getDefaultHandsotableSetting()
|
|
|
|
|
|
|
+ handsontableHeight: DEFAULT_HOT_HEIGHT,
|
|
|
|
|
+ handsontableSetting: HandsontableModal.getDefaultHandsontableSetting()
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
this.init = this.init.bind(this);
|
|
this.init = this.init.bind(this);
|
|
|
this.reset = this.reset.bind(this);
|
|
this.reset = this.reset.bind(this);
|
|
|
this.cancel = this.cancel.bind(this);
|
|
this.cancel = this.cancel.bind(this);
|
|
|
this.save = this.save.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) {
|
|
init(markdownTable) {
|
|
@@ -34,8 +49,15 @@ export default class HandsontableModal extends React.Component {
|
|
|
markdownTableOnInit: initMarkdownTable,
|
|
markdownTableOnInit: initMarkdownTable,
|
|
|
markdownTable: initMarkdownTable.clone(),
|
|
markdownTable: initMarkdownTable.clone(),
|
|
|
handsontableSetting: Object.assign({}, this.state.handsontableSetting, {
|
|
handsontableSetting: Object.assign({}, this.state.handsontableSetting, {
|
|
|
- afterUpdateSettings: HandsontableUtil.createHandlerToSynchronizeHandontableAlignWith(initMarkdownTable.options.align),
|
|
|
|
|
- loadData: HandsontableUtil.createHandlerToSynchronizeHandontableAlignWith(initMarkdownTable.options.align)
|
|
|
|
|
|
|
+ /*
|
|
|
|
|
+ * The afterUpdateSettings hook is called when this component state changes.
|
|
|
|
|
+ *
|
|
|
|
|
+ * In detail, when this component state changes, React will re-render HotTable because it is passed some state values of this component.
|
|
|
|
|
+ * HotTable#shouldComponentUpdate is called in this process and it call the updateSettings method for the Handsontable instance.
|
|
|
|
|
+ * After updateSetting is executed, Handsontable calls a AfterUpdateSetting hook.
|
|
|
|
|
+ */
|
|
|
|
|
+ //// commented out and will be fixed by GC-1203 -- 2018.10.19 Yuki Takei
|
|
|
|
|
+ // afterUpdateSettings: HandsontableUtil.createHandlerToSynchronizeHandontableAlignWith(initMarkdownTable.options.align)
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
);
|
|
);
|
|
@@ -56,7 +78,8 @@ export default class HandsontableModal extends React.Component {
|
|
|
|
|
|
|
|
save() {
|
|
save() {
|
|
|
let newMarkdownTable = this.state.markdownTable.clone();
|
|
let newMarkdownTable = this.state.markdownTable.clone();
|
|
|
- newMarkdownTable.options.align = HandsontableUtil.getMarkdownTableAlignmentFrom(this.refs.hotTable.hotInstance);
|
|
|
|
|
|
|
+ //// commented out and will be fixed by GC-1203 -- 2018.10.19 Yuki Takei
|
|
|
|
|
+ // newMarkdownTable.options.align = HandsontableUtil.getMarkdownTableAlignmentFrom(this.refs.hotTable.hotInstance);
|
|
|
|
|
|
|
|
if (this.props.onSave != null) {
|
|
if (this.props.onSave != null) {
|
|
|
this.props.onSave(newMarkdownTable);
|
|
this.props.onSave(newMarkdownTable);
|
|
@@ -65,15 +88,86 @@ export default class HandsontableModal extends React.Component {
|
|
|
this.setState({ show: false });
|
|
this.setState({ show: false });
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ setClassNameToColumns(className) {
|
|
|
|
|
+ const selectedRange = this.refs.hotTable.hotInstance.getSelectedRange();
|
|
|
|
|
+ if (selectedRange == null) return;
|
|
|
|
|
+
|
|
|
|
|
+ let startCol;
|
|
|
|
|
+ let endCol;
|
|
|
|
|
+
|
|
|
|
|
+ if (selectedRange[0].from.col < selectedRange[0].to.col) {
|
|
|
|
|
+ startCol = selectedRange[0].from.col;
|
|
|
|
|
+ endCol = selectedRange[0].to.col;
|
|
|
|
|
+ }
|
|
|
|
|
+ else {
|
|
|
|
|
+ startCol = selectedRange[0].to.col;
|
|
|
|
|
+ endCol = selectedRange[0].from.col;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ 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() {
|
|
render() {
|
|
|
|
|
+ const dialogClassNames = ['handsontable-modal'];
|
|
|
|
|
+ if (this.state.isWindowExpanded) {
|
|
|
|
|
+ dialogClassNames.push('handsontable-modal-expanded');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const dialogClassName = dialogClassNames.join(' ');
|
|
|
|
|
+
|
|
|
return (
|
|
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>
|
|
<Modal.Header closeButton>
|
|
|
|
|
+ { this.renderExpandOrContractButton() }
|
|
|
<Modal.Title>Edit Table</Modal.Title>
|
|
<Modal.Title>Edit Table</Modal.Title>
|
|
|
</Modal.Header>
|
|
</Modal.Header>
|
|
|
- <Modal.Body className="p-0">
|
|
|
|
|
- <div className="p-4">
|
|
|
|
|
- <HotTable ref='hotTable' data={this.state.markdownTable.table} settings={this.state.handsontableSetting} />
|
|
|
|
|
|
|
+ <Modal.Body className="p-0 d-flex flex-column">
|
|
|
|
|
+ <Navbar className="mb-0">
|
|
|
|
|
+ <Navbar.Form>
|
|
|
|
|
+ {/* commented out and will be fixed by GC-1203 -- 2018.10.19 Yuki Takei
|
|
|
|
|
+ <ButtonGroup>
|
|
|
|
|
+ <Button onClick={() => { this.setClassNameToColumns('htLeft') }}><i className="ti-align-left"></i></Button>
|
|
|
|
|
+ <Button onClick={() => { this.setClassNameToColumns('htCenter') }}><i className="ti-align-center"></i></Button>
|
|
|
|
|
+ <Button onClick={() => { this.setClassNameToColumns('htRight') }}><i className="ti-align-right"></i></Button>
|
|
|
|
|
+ </ButtonGroup>
|
|
|
|
|
+ */}
|
|
|
|
|
+ </Navbar.Form>
|
|
|
|
|
+ </Navbar>
|
|
|
|
|
+ <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>
|
|
</div>
|
|
|
</Modal.Body>
|
|
</Modal.Body>
|
|
|
<Modal.Footer>
|
|
<Modal.Footer>
|
|
@@ -102,51 +196,54 @@ export default class HandsontableModal extends React.Component {
|
|
|
);
|
|
);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- static getDefaultHandsotableSetting() {
|
|
|
|
|
|
|
+ static getDefaultHandsontableSetting() {
|
|
|
return {
|
|
return {
|
|
|
- height: 300,
|
|
|
|
|
rowHeaders: true,
|
|
rowHeaders: true,
|
|
|
colHeaders: 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: {
|
|
contextMenu: {
|
|
|
items: {
|
|
items: {
|
|
|
'row_above': {}, 'row_below': {}, 'col_left': {}, 'col_right': {},
|
|
'row_above': {}, 'row_below': {}, 'col_left': {}, 'col_right': {},
|
|
|
'separator1': Handsontable.plugins.ContextMenu.SEPARATOR,
|
|
'separator1': Handsontable.plugins.ContextMenu.SEPARATOR,
|
|
|
'remove_row': {}, 'remove_col': {},
|
|
'remove_row': {}, 'remove_col': {},
|
|
|
'separator2': Handsontable.plugins.ContextMenu.SEPARATOR,
|
|
'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');
|
|
|
|
|
- }}
|
|
|
|
|
- ]
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
- selectionMode: 'multiple',
|
|
|
|
|
- modifyColWidth: function(width) {
|
|
|
|
|
- if (width < 100) {
|
|
|
|
|
- return 100;
|
|
|
|
|
- }
|
|
|
|
|
- if (width > 300) {
|
|
|
|
|
- return 300;
|
|
|
|
|
|
|
+ //// commented out and will be fixed by GC-1203 -- 2018.10.19 Yuki Takei
|
|
|
|
|
+ // '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');
|
|
|
|
|
+ // }}
|
|
|
|
|
+ // ]
|
|
|
|
|
+ // }
|
|
|
|
|
+ // }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
};
|
|
};
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|