mirror of
https://github.com/mickael-kerjean/filestash.git
synced 2025-11-03 04:50:14 +08:00
improvement (editor): #20 + bugfix
This commit is contained in:
@ -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);
|
||||||
|
|||||||
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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}
|
||||||
|
|||||||
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
7
client/pages/viewerpage/ide.scss
Normal file
7
client/pages/viewerpage/ide.scss
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
.fab-appear{
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
.fab-appear.fab-appear-active{
|
||||||
|
transition: all 0.2s ease-out;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
@ -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';
|
|
||||||
|
|||||||
@ -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} />
|
||||||
|
|||||||
Reference in New Issue
Block a user