improvement (editor): #20 + bugfix

This commit is contained in:
Mickael KERJEAN
2018-03-26 12:57:58 +11:00
parent 050a26e99a
commit 950f56bbdf
10 changed files with 69 additions and 68 deletions

View File

@ -23,6 +23,7 @@ html {
background: var(--bg-color); background: var(--bg-color);
color: var(--color); color: var(--color);
} }
body {overflow: hidden;}
body, html{ body, html{
height: 100%; height: 100%;
margin: 0; margin: 0;
@ -80,19 +81,6 @@ input[type="checkbox"]{position: relative; top: 1px; margin: 0; padding: 0;}
/* I know this looks weird but at least it doesn't rely on javascript to dynamically set element size */
body {overflow: hidden;}
body, body > div, body > div > div, body > div > div > div{ height: 100%;}
/* CONNECTION FORM */ /* CONNECTION FORM */
.login-form button.active{ .login-form button.active{
box-shadow: 0px 1px 5px rgba(0,0,0,0.20); box-shadow: 0px 1px 5px rgba(0,0,0,0.20);

View File

@ -1,10 +1,13 @@
import React from 'react'; import React from 'react';
import './fab.scss'; import './fab.scss';
export const Fab = (props) => { export const Fab = (props) => {
return ( return (
<div className="component_fab" onClick={props.onClick}> <div className="component_fab" onClick={props.onClick}>
{props.children} <div className="content">
{props.children}
</div>
</div> </div>
); );
} }

View File

@ -1,13 +1,15 @@
.component_fab{ .component_fab{
height: 25px;
width: 25px;
padding: 13px;
position: fixed; position: fixed;
bottom: 20px; bottom: 20px;
right: 20px; right: 20px;
border-radius: 50%; .content{
background: var(--color); height: 25px;
box-shadow: rgba(0, 0, 0, 0.14) 0px 4px 5px 0px, rgba(0, 0, 0, 0.12) 0px 1px 10px 0px, rgba(0, 0, 0, 0.2) 0px 2px 4px -1px; width: 25px;
z-index: 1000; padding: 13px;
cursor: pointer; border-radius: 50%;
background: var(--color);
box-shadow: rgba(0, 0, 0, 0.14) 0px 4px 5px 0px, rgba(0, 0, 0, 0.12) 0px 1px 10px 0px, rgba(0, 0, 0, 0.2) 0px 2px 4px -1px;
z-index: 1000;
cursor: pointer;
}
} }

View File

@ -32,6 +32,6 @@
<meta name="description" content="browse your files in the cloud"> <meta name="description" content="browse your files in the cloud">
</head> </head>
<body> <body>
<div id="main"></div> <div id="main" style="height: 100%"></div>
</body> </body>
</html> </html>

View File

@ -28,22 +28,24 @@ export class ViewerPage extends React.Component {
data: '', data: '',
needSaving: false, needSaving: false,
isSaving: false, isSaving: false,
height: null,
loading: true, loading: true,
error: false error: false
}; };
this.resetHeight = debounce(this.resetHeight.bind(this), 100);
this.props.subscribe('file.select', this.onPathUpdate.bind(this)); this.props.subscribe('file.select', this.onPathUpdate.bind(this));
} }
componentWillMount(){ componentWillMount(){
this.setState({loading: true, error: false}); this.setState({loading: null, error: false}, () => {
window.setTimeout(() => {
if(this.state.loading === null) this.setState({loading: true});
}, 500);
});
let app = opener(this.state.path); let app = opener(this.state.path);
if(app === 'editor'){ if(app === 'editor'){
Files.cat(this.state.path).then((content) => { Files.cat(this.state.path).then((content) => {
this.setState({data: content, loading: false, opener: app}); this.setState({data: content, loading: false, opener: app});
}).catch(err => { }).catch(err => {
if(err && err.code === 'CANCELLED'){ return } if(err && err.code === 'CANCELLED'){ return; }
if(err.code === 'BINARY_FILE'){ if(err.code === 'BINARY_FILE'){
Files.url(this.state.path).then((url) => { Files.url(this.state.path).then((url) => {
this.setState({data: url, loading: false, opener: 'download'}); this.setState({data: url, loading: false, opener: 'download'});
@ -58,7 +60,7 @@ export class ViewerPage extends React.Component {
Files.url(this.state.path).then((url) => { Files.url(this.state.path).then((url) => {
this.setState({data: url, loading: false, opener: app}); this.setState({data: url, loading: false, opener: app});
}).catch(err => { }).catch(err => {
if(err && err.code === 'CANCELLED'){ return } if(err && err.code === 'CANCELLED'){ return; }
this.setState({error: err}); this.setState({error: err});
}); });
} }
@ -66,7 +68,6 @@ export class ViewerPage extends React.Component {
componentWillUnmount() { componentWillUnmount() {
this.props.unsubscribe('file.select'); this.props.unsubscribe('file.select');
window.removeEventListener("resize", this.resetHeight);
} }
@ -96,24 +97,13 @@ export class ViewerPage extends React.Component {
this.setState({needSaving: bool}); this.setState({needSaving: bool});
} }
componentDidMount(){
this.resetHeight();
window.addEventListener("resize", this.resetHeight);
}
resetHeight(){
this.setState({
height: document.body.clientHeight - document.querySelector('.breadcrumb').offsetHeight
});
}
render() { render() {
let style = {height: '100%'}; let style = {height: '100%'};
return ( return (
<div> <div style={style}>
<BreadCrumb needSaving={this.state.needSaving} className="breadcrumb" path={this.state.path} /> <BreadCrumb needSaving={this.state.needSaving} className="breadcrumb" path={this.state.path} />
<div style={{height: this.state.height ? this.state.height + 'px' : '100%'}}> <div style={style}>
<NgIf cond={this.state.loading === false} style={{height: '100%'}}> <NgIf cond={this.state.loading === false} style={style}>
<NgIf cond={this.state.opener === 'editor'} style={style}> <NgIf cond={this.state.opener === 'editor'} style={style}>
<IDE needSaving={this.needSaving.bind(this)} <IDE needSaving={this.needSaving.bind(this)}
isSaving={this.state.isSaving} isSaving={this.state.isSaving}

View File

@ -1,6 +1,8 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { NgIf, Loader } from '../../components/';
import CodeMirror from 'codemirror/lib/codemirror'; import CodeMirror from 'codemirror/lib/codemirror';
import 'codemirror/lib/codemirror.css'; import 'codemirror/lib/codemirror.css';
import './editor.scss'; import './editor.scss';
@ -25,6 +27,7 @@ export class Editor extends React.Component {
constructor(props){ constructor(props){
super(props); super(props);
this.state = { this.state = {
loading: null,
editor: null, editor: null,
filename: this.props.filename filename: this.props.filename
}; };
@ -34,13 +37,16 @@ export class Editor extends React.Component {
if(this.props.content !== props.content){ if(this.props.content !== props.content){
this.state.editor.getDoc().setValue(props.content); this.state.editor.getDoc().setValue(props.content);
} }
if(this.props.height !== props.height){
this.updateHeight(props.height);
}
} }
componentDidMount(){ componentDidMount(){
this.setState({loading: null, error: false}, () => {
window.setTimeout(() => {
if(this.state.loading === null) this.setState({loading: true});
}, 200);
});
this.loadMode(this.props.filename) this.loadMode(this.props.filename)
.then((res) => new Promise((done) => this.setState({loading: false}, () => done(res))))
.then(loadCodeMirror.bind(this)); .then(loadCodeMirror.bind(this));
function loadCodeMirror(CodeMirror){ function loadCodeMirror(CodeMirror){
@ -62,7 +68,6 @@ export class Editor extends React.Component {
} }
this.setState({editor: editor}); this.setState({editor: editor});
this.updateHeight(this.props.height);
editor.on('change', (edit) => { editor.on('change', (edit) => {
if(this.props.onChange){ if(this.props.onChange){
@ -83,13 +88,6 @@ export class Editor extends React.Component {
this.state.editor.clearHistory(); this.state.editor.clearHistory();
} }
updateHeight(height){
if(height){
document.querySelector('.CodeMirror').style.height = height+'px';
}
}
loadMode(file){ loadMode(file){
let ext = file.split('.').pop(), let ext = file.split('.').pop(),
mode = null; mode = null;
@ -103,7 +101,7 @@ export class Editor extends React.Component {
else if(ext === 'css'){ mode = 'css'; } else if(ext === 'css'){ mode = 'css'; }
else if(ext === 'less' || ext === 'scss' || ext === 'sass'){ mode = 'sass'; } else if(ext === 'less' || ext === 'scss' || ext === 'sass'){ mode = 'sass'; }
else if(ext === 'js' || ext === 'json'){ mode = 'javascript'; } else if(ext === 'js' || ext === 'json'){ mode = 'javascript'; }
else if(ext === 'jsx'){ mode = 'jsx' } else if(ext === 'jsx'){ mode = 'jsx'; }
else if(ext === 'php' || ext === 'php5' || ext === 'php4'){ mode = 'php'; } else if(ext === 'php' || ext === 'php5' || ext === 'php4'){ mode = 'php'; }
else if(ext === 'elm'){ mode = 'elm'; } else if(ext === 'elm'){ mode = 'elm'; }
else if(ext === 'erl'){ mode = 'erlang'; } else if(ext === 'erl'){ mode = 'erlang'; }
@ -112,7 +110,7 @@ export class Editor extends React.Component {
else if(ext === 'pl' || ext === 'pm'){mode = 'perl'; } else if(ext === 'pl' || ext === 'pm'){mode = 'perl'; }
else if(ext === 'clj'){ mode = 'clojure'; } else if(ext === 'clj'){ mode = 'clojure'; }
else if(ext === 'el' || ext === 'lisp' || ext === 'cl' || ext === 'emacs'){ mode = 'lisp'; } else if(ext === 'el' || ext === 'lisp' || ext === 'cl' || ext === 'emacs'){ mode = 'lisp'; }
else if(ext === 'Dockerfile'){ mode = 'dockerfile'} else if(ext === 'Dockerfile'){ mode = 'dockerfile'; }
else if(ext === 'R'){ mode = 'r'; } else if(ext === 'R'){ mode = 'r'; }
else if(ext === 'Makefile'){ mode = 'cmake'; } else if(ext === 'Makefile'){ mode = 'cmake'; }
else if(ext === 'rb'){ mode = 'ruby'; } else if(ext === 'rb'){ mode = 'ruby'; }
@ -130,12 +128,20 @@ export class Editor extends React.Component {
} }
return import(/* webpackChunkName: "editor" */'./editor/'+mode) return import(/* webpackChunkName: "editor" */'./editor/'+mode)
.catch(() => import("./editor/text"))
.then((module) => Promise.resolve(module.default)); .then((module) => Promise.resolve(module.default));
} }
render() { render() {
return ( return (
<div style={{height: '100%'}}>
<NgIf cond={this.state.loading === true} style={{height: '100%'}}>
<Loader/>
</NgIf>
<NgIf cond={this.state.loading === false} style={{height: '100%'}}>
<div id="editor" style={{height: '100%'}}></div> <div id="editor" style={{height: '100%'}}></div>
</NgIf>
</div>
); );
} }
} }

View File

@ -1,14 +1,16 @@
import React from 'react'; import React from 'react';
import ReactCSSTransitionGroup from 'react-addons-css-transition-group';
import { NgIf, Fab, Icon } from '../../components/'; import { NgIf, Fab, Icon } from '../../components/';
import { MenuBar } from './menubar';
import { Editor } from './editor'; import { Editor } from './editor';
import './ide.scss';
export class IDE extends React.Component { export class IDE extends React.Component {
constructor(props){ constructor(props){
super(props); super(props);
this.state = { this.state = {
contentToSave: null contentToSave: props.content
}; };
} }
@ -32,13 +34,16 @@ export class IDE extends React.Component {
render(){ render(){
return ( return (
<div style={{height: '100%'}}> <div style={{height: '100%'}}>
<Editor onSave={this.save.bind(this)} filename={this.props.filename} content={this.props.content} onChange={this.onContentUpdate.bind(this)} height={this.state.height}/> <Editor onSave={this.save.bind(this)} filename={this.props.filename} content={this.props.content} onChange={this.onContentUpdate.bind(this)} />
<NgIf cond={!this.props.isSaving}>
<Fab onClick={this.save.bind(this)}><Icon name="save" style={{height: '100%', width: '100%'}}/></Fab> <ReactCSSTransitionGroup transitionName="fab" transitionLeave={false} transitionEnter={false} transitionAppear={true} transitionAppearTimeout={25000}>
</NgIf> <NgIf cond={!this.props.isSaving}>
<NgIf cond={this.props.isSaving}> <Fab onClick={this.save.bind(this)}><Icon name="save" style={{height: '100%', width: '100%'}}/></Fab>
<Fab><Icon name="loading_white" style={{height: '100%', width: '100%'}}/></Fab> </NgIf>
</NgIf> <NgIf cond={this.props.isSaving}>
<Fab><Icon name="loading_white" style={{height: '100%', width: '100%'}}/></Fab>
</NgIf>
</ReactCSSTransitionGroup>
</div> </div>
); );
} }

View File

@ -0,0 +1,7 @@
.fab-appear{
opacity: 0;
}
.fab-appear.fab-appear-active{
transition: all 0.2s ease-out;
opacity: 1;
}

View File

@ -2,6 +2,6 @@ export { AudioPlayer } from './audioplayer';
export { FileDownloader } from './filedownloader'; export { FileDownloader } from './filedownloader';
export { ImageViewer } from './imageviewer'; export { ImageViewer } from './imageviewer';
export { PDFViewer } from './pdfviewer'; export { PDFViewer } from './pdfviewer';
export { IDE } from './ide';
// Those are commented because they will be delivered as a separate chunk // Those are commented because they will be delivered as a separate chunk
//export { VideoPlayer } from './videoplayer'; //export { VideoPlayer } from './videoplayer';
//export { IDE } from './ide';

View File

@ -7,9 +7,9 @@ import { Audio, Video } from './components/';
export default class AppRouter extends React.Component { export default class AppRouter extends React.Component {
render() { render() {
return ( return (
<div> <div style={{height: '100%'}}>
<BrowserRouter> <BrowserRouter>
<div> <div style={{height: '100%'}}>
<Switch> <Switch>
<Route exact path="/" component={HomePage} /> <Route exact path="/" component={HomePage} />
<Route path="/login" component={ConnectPage} /> <Route path="/login" component={ConnectPage} />