Merge pull request #1032 from embark-framework/features/resizable-editor-main

Make the editor resizable
This commit is contained in:
Iuri Matias 2018-11-08 16:13:51 -05:00 committed by GitHub
commit 1d31e20476
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 166 additions and 29 deletions

View File

@ -10121,6 +10121,11 @@
} }
} }
}, },
"re-resizable": {
"version": "4.9.3",
"resolved": "https://registry.npmjs.org/re-resizable/-/re-resizable-4.9.3.tgz",
"integrity": "sha512-JKzmZdUAYWs85YErkmZNB7hjGR9qUOHFUZUtEplZlEZBFHRguiWck5J+HFTy/NjlMJtqQsYPQq57nQAO2BuRRg=="
},
"react": { "react": {
"version": "16.4.2", "version": "16.4.2",
"resolved": "https://registry.npmjs.org/react/-/react-16.4.2.tgz", "resolved": "https://registry.npmjs.org/react/-/react-16.4.2.tgz",

View File

@ -50,6 +50,7 @@
"prop-types": "^15.6.2", "prop-types": "^15.6.2",
"qs": "^6.5.2", "qs": "^6.5.2",
"raf": "3.4.0", "raf": "3.4.0",
"re-resizable": "^4.9.3",
"react": "^16.4.2", "react": "^16.4.2",
"react-blockies": "^1.4.0", "react-blockies": "^1.4.0",
"react-bootstrap-typeahead": "^3.2.4", "react-bootstrap-typeahead": "^3.2.4",

View File

@ -21,7 +21,8 @@ const style = (theme) => ({
top: 0, top: 0,
bottom: '40px', bottom: '40px',
left: 0, left: 0,
right: 0 right: 0,
minWidth: '175px'
}, },
node: { node: {
base: { base: {
@ -214,7 +215,7 @@ class FileExplorer extends React.Component {
style={style(this.props.theme)} style={style(this.props.theme)}
/> />
<Label className="hidden-toogle mb-0 pt-2 pr-2 pb-1 border-top text-right"> <Label className="hidden-toggle mb-0 pt-2 pr-2 pb-1 border-top text-right">
<span className="mr-2 align-top" style={{"fontSize": "12px"}}>Show hidden files</span> <span className="mr-2 align-top" style={{"fontSize": "12px"}}>Show hidden files</span>
<AppSwitch color="success" variant="pill" size="sm" onChange={this.props.toggleShowHiddenFiles}/> <AppSwitch color="success" variant="pill" size="sm" onChange={this.props.toggleShowHiddenFiles}/>
</Label> </Label>

View File

@ -46,6 +46,10 @@ class TextEditor extends React.Component {
}); });
} }
componentWillUnmount() {
window.removeEventListener("resize", this.handleResize);
}
handleResize = () => editor.layout(); handleResize = () => editor.layout();
@ -134,6 +138,10 @@ class TextEditor extends React.Component {
this.handleResize(); this.handleResize();
} }
addEditorTabs(e, file) {
e.preventDefault(); this.props.addEditorTabs(file);
}
renderTabs() { renderTabs() {
return ( return (
<ul className="list-inline m-0 p-0"> <ul className="list-inline m-0 p-0">
@ -144,7 +152,7 @@ class TextEditor extends React.Component {
})}> })}>
<a <a
href="#switch-tab" href="#switch-tab"
onClick={() => this.props.addEditorTabs(file)} onClick={(e) => this.addEditorTabs(e, file)}
className="p-2 text-muted" className="p-2 text-muted"
> >
{file.name} {file.name}

View File

@ -8,3 +8,7 @@ export const DEPLOYMENT_PIPELINES = {
embark: 'embark' embark: 'embark'
}; };
export const DEFAULT_HOST = process.env.NODE_ENV === 'development' ? 'localhost:8000' : window.location.host; export const DEFAULT_HOST = process.env.NODE_ENV === 'development' ? 'localhost:8000' : window.location.host;
export const OPERATIONS = {
MORE: 1,
LESS: -1
};

View File

@ -4,11 +4,12 @@
margin-top: -1.5rem !important; margin-top: -1.5rem !important;
} }
.hidden-toogle { .hidden-toggle {
position: absolute; position: absolute;
bottom: 0; bottom: 0;
right: 0; right: 0;
left: 0; left: 0;
height: 40px;
} }
.text-editor__debuggerLine { .text-editor__debuggerLine {
@ -34,8 +35,12 @@
.editor-aside { .editor-aside {
position: relative; position: relative;
} }
}
.aside-opened .text-editor-container {
max-height: 500px; .resizer-handle {
} width: 15px !important;
}
.resize-handle-horizontal {
height: 15px !important;
} }

View File

@ -9,21 +9,51 @@ import TextEditorToolbarContainer from './TextEditorToolbarContainer';
import {fetchEditorTabs as fetchEditorTabsAction} from '../actions'; import {fetchEditorTabs as fetchEditorTabsAction} from '../actions';
import {getCurrentFile} from '../reducers/selectors'; import {getCurrentFile} from '../reducers/selectors';
import classnames from 'classnames'; import classnames from 'classnames';
import Resizable from 're-resizable';
import {OPERATIONS} from '../constants';
import './EditorContainer.css'; import './EditorContainer.css';
class EditorContainer extends React.Component { class EditorContainer extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = {currentAsideTab: {}, showHiddenFiles: false, currentFile: this.props.currentFile}; this.DEFAULT_EDITOR_WIDTH = 85;
this.DEFAULT_EDITOR_WIDTH_SMALL = 75;
this.DEFAULT_HEIGHT = 500;
this.SMALL_SIZE = 768;
this.windowWidth = window.innerWidth;
this.state = {
currentAsideTab: {}, showHiddenFiles: false, currentFile: this.props.currentFile,
editorHeight: this.DEFAULT_HEIGHT,
editorWidth: ((this.windowWidth < this.SMALL_SIZE) ? this.DEFAULT_EDITOR_WIDTH_SMALL : this.DEFAULT_EDITOR_WIDTH) + '%',
asideHeight: '100%', asideWidth: '25%',
isSmallSize: (this.windowWidth < this.SMALL_SIZE)
};
} }
componentDidMount() { componentDidMount() {
this.props.fetchEditorTabs(); this.props.fetchEditorTabs();
window.addEventListener("resize", this.updateDimensions.bind(this));
}
componentWillUnmount() {
window.removeEventListener("resize", this.updateDimensions.bind(this));
}
updateDimensions() {
this.windowWidth = window.innerWidth;
const isSmallSize = (this.windowWidth < this.SMALL_SIZE);
if (this.state.isSmallSize !== isSmallSize) {
this.setState({isSmallSize});
this.changeEditorWidth(isSmallSize ? OPERATIONS.MORE : OPERATIONS.LESS);
this.editor.handleResize();
}
} }
componentDidUpdate(prevProps) { componentDidUpdate(prevProps) {
if(this.props.currentFile.path !== prevProps.currentFile.path) { if (this.props.currentFile.path !== prevProps.currentFile.path) {
this.setState({currentFile: this.props.currentFile}); this.setState({currentFile: this.props.currentFile});
} }
} }
@ -43,38 +73,116 @@ class EditorContainer extends React.Component {
} }
openAsideTab(newTab) { openAsideTab(newTab) {
if (!this.state.isSmallSize) {
this.changeEditorWidth((!this.state.currentAsideTab.label) ? OPERATIONS.LESS : OPERATIONS.MORE);
}
if (newTab.label === this.state.currentAsideTab.label) { if (newTab.label === this.state.currentAsideTab.label) {
this.editor.handleResize();
return this.setState({currentAsideTab: {}}); return this.setState({currentAsideTab: {}});
} }
this.setState({currentAsideTab: newTab}); this.setState({currentAsideTab: newTab});
} }
textEditorMdSize() { changeEditorWidth(operation) {
return this.state.currentAsideTab.label ? 7 : 10; this.setState({
editorWidth: (parseFloat(this.state.editorWidth) + (operation * parseFloat(this.state.asideWidth))) + '%'
});
}
renderTextEditor() {
const height = !!(this.state.isSmallSize && this.state.currentAsideTab.label) ? this.state.editorHeight : 'auto';
return (
<Resizable
size={{width: this.state.editorWidth, height: height}}
minWidth="20%" maxWidth="90%"
handleClasses={{left: 'resizer-handle', right: 'resizer-handle', bottom: 'resize-handle-horizontal'}}
onResizeStop={(e, direction, ref, d) => {
this.setState({
editorWidth: ref.style.width,
editorHeight: this.state.editorHeight + d.height
});
this.editor.handleResize();
}}
className="text-editor-container border-left"
enable={{
top: false,
right: false,
bottom: !!(this.state.isSmallSize && this.state.currentAsideTab.label),
left: true,
topRight: false,
bottomRight: false,
bottomLeft: false,
topLeft: false
}}>
<TextEditorContainer
ref={instance => {
this.editor = instance ? instance.getWrappedInstance().editor : null;
}}
currentFile={this.props.currentFile}
onFileContentChange={(newContent) => this.onFileContentChange(newContent)}/>
</Resizable>
);
}
renderAside() {
const aside = (
<div className="editor-aside">
<TextEditorAsideContainer currentAsideTab={this.state.currentAsideTab}
currentFile={this.props.currentFile}/>
</div>
);
if (this.windowWidth < this.SMALL_SIZE) {
return (<Col sm={12} className="border-left-0 relative">
{aside}
</Col>);
}
return (
<Resizable defaultSize={{width: this.state.asideWidth, height: 'auto'}}
maxWidth='60%' minWidth='17%'
handleClasses={{left: 'resizer-handle', right: 'resizer-handle'}}
className="border-left-0 relative"
enable={{
top: false,
right: false,
bottom: false,
left: true,
topRight: false,
bottomRight: false,
bottomLeft: false,
topLeft: false
}}
onResize={(e, direction, ref, _d) => {
this.setState({
editorWidth: this.DEFAULT_EDITOR_WIDTH - parseFloat(ref.style.width) + '%'
});
}}>
{aside}
</Resizable>
);
} }
render() { render() {
return ( return (
<Row noGutters className={classnames('h-100', 'editor--grid', {'aside-opened': this.state.currentAsideTab.label})}> <Row noGutters
className={classnames('h-100', 'editor--grid', {'aside-opened': this.state.currentAsideTab.label})}>
<Col xs={12}> <Col xs={12}>
<TextEditorToolbarContainer openAsideTab={(newTab) => this.openAsideTab(newTab)} <TextEditorToolbarContainer openAsideTab={(newTab) => this.openAsideTab(newTab)}
isContract={this.isContract()} isContract={this.isContract()}
currentFile={this.props.currentFile} currentFile={this.props.currentFile}
activeTab={this.state.currentAsideTab}/> activeTab={this.state.currentAsideTab}/>
</Col> </Col>
<Col sm={4} md={2} xl={2} lg={2} className="border-right">
<FileExplorerContainer showHiddenFiles={this.state.showHiddenFiles} toggleShowHiddenFiles={() => this.toggleShowHiddenFiles()} /> <Col className="border-right">
<FileExplorerContainer showHiddenFiles={this.state.showHiddenFiles}
toggleShowHiddenFiles={() => this.toggleShowHiddenFiles()}/>
</Col> </Col>
<Col sm={8} md={this.textEditorMdSize()} className="text-editor-container">
<TextEditorContainer currentFile={this.props.currentFile} onFileContentChange={(newContent) => this.onFileContentChange(newContent)} /> {this.renderTextEditor()}
</Col>
{this.state.currentAsideTab.label && {this.state.currentAsideTab.label && this.renderAside()}
<Col sm={12} md={3} className="border-left-0 relative">
<div className="editor-aside">
<TextEditorAsideContainer currentAsideTab={this.state.currentAsideTab}
currentFile={this.props.currentFile}/>
</div>
</Col>}
</Row> </Row>
); );
} }
@ -95,6 +203,6 @@ EditorContainer.propTypes = {
export default connect( export default connect(
mapStateToProps, mapStateToProps,
{fetchEditorTabs: fetchEditorTabsAction.request}, {fetchEditorTabs: fetchEditorTabsAction.request}
)(EditorContainer); )(EditorContainer);

View File

@ -27,7 +27,10 @@ class TextEditorContainer extends React.Component {
addEditorTabs={this.props.addEditorTabs} addEditorTabs={this.props.addEditorTabs}
debuggerLine={this.props.debuggerLine} debuggerLine={this.props.debuggerLine}
onFileContentChange={this.props.onFileContentChange} onFileContentChange={this.props.onFileContentChange}
theme={this.props.theme} /> theme={this.props.theme}
ref={instance => {
if (instance) this.editor = instance;
}}/>
); );
} }
} }
@ -45,7 +48,7 @@ TextEditorContainer.propTypes = {
onFileContentChange: PropTypes.func, onFileContentChange: PropTypes.func,
toggleBreakpoints: PropTypes.func, toggleBreakpoints: PropTypes.func,
breakpoints: PropTypes.array, breakpoints: PropTypes.array,
toggleBreakpoint: PropTypes.object, toggleBreakpoint: PropTypes.func,
fetchEditorTabs: PropTypes.func, fetchEditorTabs: PropTypes.func,
removeEditorTabs: PropTypes.func, removeEditorTabs: PropTypes.func,
addEditorTabs: PropTypes.func, addEditorTabs: PropTypes.func,
@ -57,9 +60,11 @@ TextEditorContainer.propTypes = {
export default connect( export default connect(
mapStateToProps, mapStateToProps,
{ {
toggleBreakpoint, toggleBreakpoint: toggleBreakpoint.request,
fetchEditorTabs: fetchEditorTabsAction.request, fetchEditorTabs: fetchEditorTabsAction.request,
removeEditorTabs: removeEditorTabsAction.request, removeEditorTabs: removeEditorTabsAction.request,
addEditorTabs: addEditorTabsAction.request addEditorTabs: addEditorTabsAction.request
}, },
null,
{ withRef: true }
)(TextEditorContainer); )(TextEditorContainer);