Skip to content

Commit

Permalink
feat: add CRUD to file explorer
Browse files Browse the repository at this point in the history
  • Loading branch information
alaibe committed Nov 13, 2018
1 parent c8d6f18 commit f82d3de
Show file tree
Hide file tree
Showing 14 changed files with 390 additions and 78 deletions.
9 changes: 8 additions & 1 deletion embark-ui/src/actions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -293,10 +293,17 @@ export const saveFile = {
failure: (error) => action(SAVE_FILE[FAILURE], {error})
};

export const SAVE_FOLDER = createRequestTypes('SAVE_FOLDER');
export const saveFolder = {
request: ({path}) => action(SAVE_FOLDER[REQUEST], {path}),
success: () => action(SAVE_FOLDER[SUCCESS]),
failure: (error) => action(SAVE_FOLDER[FAILURE], {error})
};

export const REMOVE_FILE = createRequestTypes('REMOVE_FILE');
export const removeFile = {
request: ({name, path, content}) => action(REMOVE_FILE[REQUEST], {name, path, content}),
success: () => action(REMOVE_FILE[SUCCESS]),
success: (_, file) => action(REMOVE_FILE[SUCCESS], {file}),
failure: (error) => action(REMOVE_FILE[FAILURE], {error})
};

Expand Down
51 changes: 51 additions & 0 deletions embark-ui/src/components/AddFileModal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React from 'react';
import {Button, Modal, ModalHeader, ModalBody, ModalFooter, Input} from 'reactstrap';
import PropTypes from 'prop-types';
import classNames from 'classnames';

import {isDarkTheme} from '../utils/utils';

class AddFileModal extends React.Component {
constructor(props) {
super(props)
this.state = {modal: false, filename: ''};
}

toggle() {
this.setState({modal: !this.state.modal});
}

handleChange(event) {
this.setState({filename: event.target.value});
}

addFile() {
this.props.saveFile({path: `${this.props.node.path}/${this.state.filename}`, content: ''});
this.toggle();
}

render() {
return (
<Modal contentClassName={classNames({'dark-theme': isDarkTheme(this.props.theme)})}
isOpen={this.state.modal}
toggle={() => this.toggle()}>
<ModalHeader toggle={() => this.toggle()}>Please give the file a name</ModalHeader>
<ModalBody>
<Input autofocus="true" value={this.state.filename} onChange={e => this.handleChange(e)} />
</ModalBody>
<ModalFooter>
<Button color="primary" onClick={() => this.addFile()}>Add File</Button>{' '}
<Button color="secondary" onClick={() => this.toggle()}>Cancel</Button>
</ModalFooter>
</Modal>
)
}
}

AddFileModal.propTypes = {
saveFile: PropTypes.func,
node: PropTypes.object,
theme: PropTypes.string
};

export default AddFileModal;
51 changes: 51 additions & 0 deletions embark-ui/src/components/AddFolderModal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React from 'react';
import {Button, Modal, ModalHeader, ModalBody, ModalFooter, Input} from 'reactstrap';
import PropTypes from 'prop-types';
import classNames from 'classnames';

import {isDarkTheme} from '../utils/utils';

class AddFolderModal extends React.Component {
constructor(props) {
super(props)
this.state = {modal: false, folder: ''};
}

toggle() {
this.setState({modal: !this.state.modal});
}

handleChange(event) {
this.setState({folder: event.target.value});
}

addFolder() {
this.props.saveFolder({path: `${this.props.node.path}/${this.state.folder}`});
this.toggle();
}

render() {
return (
<Modal contentClassName={classNames({'dark-theme': isDarkTheme(this.props.theme)})}
isOpen={this.state.modal}
toggle={() => this.toggle()}>
<ModalHeader toggle={() => this.toggle()}>Please give the folder a name</ModalHeader>
<ModalBody>
<Input autofocus="true" value={this.state.filename} onChange={e => this.handleChange(e)} />
</ModalBody>
<ModalFooter>
<Button color="primary" onClick={() => this.addFolder()}>Add Folder</Button>{' '}
<Button color="secondary" onClick={() => this.toggle()}>Cancel</Button>
</ModalFooter>
</Modal>
)
}
}

AddFolderModal.propTypes = {
saveFolder: PropTypes.func,
node: PropTypes.object,
theme: PropTypes.string
};

export default AddFolderModal;
125 changes: 66 additions & 59 deletions embark-ui/src/components/FileExplorer.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import React from 'react';
import {AppSwitch} from '@coreui/react';
import {Label} from 'reactstrap';
import React from 'react';
import PropTypes from 'prop-types';
import {Treebeard, decorators} from 'react-treebeard';
import classNames from 'classnames';
import {DARK_THEME} from '../constants';

const isDarkTheme= (theme) => theme === DARK_THEME;
import FileExplorerRowContainer from '../containers/FileExplorerRowContainer';
import {isDarkTheme} from '../utils/utils';

const style = (theme) => ({
tree: {
Expand Down Expand Up @@ -77,67 +77,73 @@ const style = (theme) => ({
}
}
});


const Header = ({style, node}) => {
let icon;

if (!node.children) {
const extension = node.path.split('.').pop();
switch(extension) {
case 'html':
icon = 'text-danger fa fa-html5';
break;
case 'css':
icon = 'text-warning fa fa-css3';
break;
case 'js':
case 'jsx':
icon = 'text-primary icon js-icon';
break;
case 'json':
icon = 'text-success icon hjson-icon';
break;
case 'sol':
icon = 'text-warning icon solidity-icon';
break;
default:
icon = 'fa fa-file-o';
}
} else {
switch(node.name) {
case 'dist':
icon = 'text-danger icon easybuild-icon';
break;
case 'config':
icon = 'text-warning fa fa-cogs';
break;
case 'contracts':
icon = 'text-success fa fa-file-text';
break;
case 'app':
icon = 'text-primary fa fa-code';
break;
case 'test':
icon = 'icon test-dir-icon';
break;
case 'node_modules':
icon = 'fa fa-folder-o';
break;
default:
icon = 'fa fa-folder';
class Header extends React.Component {
resolveIcon() {
let icon;
let {node} = this.props;

if (!node.children) {
const extension = node.path.split('.').pop();
switch(extension) {
case 'html':
icon = 'text-danger fa fa-html5';
break;
case 'css':
icon = 'text-warning fa fa-css3';
break;
case 'js':
case 'jsx':
icon = 'text-primary icon js-icon';
break;
case 'json':
icon = 'text-success icon hjson-icon';
break;
case 'sol':
icon = 'text-warning icon solidity-icon';
break;
default:
icon = 'fa fa-file-o';
}
} else {
switch(node.name) {
case 'dist':
icon = 'text-danger icon easybuild-icon';
break;
case 'config':
icon = 'text-warning fa fa-cogs';
break;
case 'contracts':
icon = 'text-success fa fa-file-text';
break;
case 'app':
icon = 'text-primary fa fa-code';
break;
case 'test':
icon = 'icon test-dir-icon';
break;
case 'node_modules':
icon = 'fa fa-folder-o';
break;
default:
icon = 'fa fa-folder';
}
}

return icon;
}

return (
<div className="mb-1" style={style.base}>
<div style={style.title}>
<i className={classNames('mr-1', icon)} />
{node.name}
render() {
let {node, style} = this.props;
return (
<div className="mb-1 d-inline-block"
style={style.base}>
<div style={style.title}>
<i className={classNames('mr-1', this.resolveIcon())} />
{node.name}
</div>
</div>
</div>
);
);
}
};

Header.propTypes = {
Expand All @@ -146,6 +152,7 @@ Header.propTypes = {
};

decorators.Header = Header;
decorators.Container = FileExplorerRowContainer;

class FileExplorer extends React.Component {
constructor(props) {
Expand Down
22 changes: 22 additions & 0 deletions embark-ui/src/components/TextEditorToolbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import {Button, Nav, NavLink} from 'reactstrap';
import classnames from 'classnames';
import FontAwesomeIcon from 'react-fontawesome';

import AddFileModal from '../components/AddFileModal';
import AddFolderModal from '../components/AddFolderModal';

export const TextEditorToolbarTabs = {
Interact: { label: 'Interact', icon: 'bolt' },
Details: { label: 'Details', icon: 'info-circle' },
Expand All @@ -13,6 +16,11 @@ export const TextEditorToolbarTabs = {
};

class TextEditorToolbar extends Component {
constructor(props) {
super(props);
this.addFileModal = React.createRef();
this.addFolderModal = React.createRef();
}

isActiveTab(tab) {
return this.props.activeTab === tab;
Expand All @@ -38,6 +46,16 @@ class TextEditorToolbar extends Component {
return (
<ol className="breadcrumb mb-0">
<li className="breadcrumb-item">
<Button color="success" size="sm" className="mr-1" onClick={() => this.addFileModal.current.toggle()}>
<FontAwesomeIcon className="mr-2" name="plus"/>
Add File
</Button>
<AddFileModal theme={this.props.theme} node={{path: this.props.rootDirname}} saveFile={this.props.saveFile} ref={this.addFileModal} />
<Button color="success" size="sm" className="mr-1" onClick={() => this.addFolderModal.current.toggle()}>
<FontAwesomeIcon className="mr-2" name="folder-open"/>
Add Folder
</Button>
<AddFolderModal theme={this.props.theme} node={{path: this.props.rootDirname}} saveFolder={this.props.saveFolder} ref={this.addFolderModal} />
<Button color="success" size="sm" className="mr-1" onClick={this.props.save}>
<FontAwesomeIcon className="mr-2" name="save"/>
Save
Expand All @@ -61,7 +79,11 @@ class TextEditorToolbar extends Component {

TextEditorToolbar.propTypes = {
isContract: PropTypes.bool,
theme: PropTypes.string,
save: PropTypes.func,
saveFile: PropTypes.func,
rootDirname: PropTypes.string,
saveFolder: PropTypes.func,
remove: PropTypes.func,
toggleShowHiddenFiles: PropTypes.func,
openAsideTab: PropTypes.func,
Expand Down
15 changes: 7 additions & 8 deletions embark-ui/src/containers/FileExplorerContainer.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import React, {Component} from 'react';
import {connect} from 'react-redux';
import PropTypes from 'prop-types';
import {files as filesAction, file as fileAction} from "../actions";

import FileExplorer from '../components/FileExplorer';
import DataWrapper from "../components/DataWrapper";
import {getFiles, getTheme} from "../reducers/selectors";
Expand All @@ -14,13 +13,13 @@ class FileExplorerContainer extends Component {

render() {
return (
<DataWrapper shouldRender={this.props.files.length > 0} {...this.props} render={({files, fetchFile, showHiddenFiles, toggleShowHiddenFiles, theme}) => (
<FileExplorer files={files}
fetchFile={fetchFile}
showHiddenFiles={showHiddenFiles}
toggleShowHiddenFiles={toggleShowHiddenFiles}
theme={theme} />
)} />
<DataWrapper shouldRender={this.props.files.length > 0} {...this.props} render={({files, fetchFile, showHiddenFiles, toggleShowHiddenFiles, theme}) => (
<FileExplorer files={files}
fetchFile={fetchFile}
showHiddenFiles={showHiddenFiles}
toggleShowHiddenFiles={toggleShowHiddenFiles}
theme={theme} />
)} />
);
}
}
Expand Down
Loading

0 comments on commit f82d3de

Please sign in to comment.