Skip to content

Commit 633ee24

Browse files
committed
move project (directory) operations to the sidebar and cleanup overlay logic, also add delete confirmation - closes #27
1 parent 25eb6c8 commit 633ee24

File tree

7 files changed

+326
-2
lines changed

7 files changed

+326
-2
lines changed

plugins/sidebar/icon-button.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
'use strict';
2+
3+
const React = require('react');
4+
const Icon = require('react-material/components/Icon');
5+
6+
const styles = require('./styles');
7+
8+
const IconButton = React.createClass({
9+
onClick: function(){
10+
if(this.props.onClick){
11+
this.props.onClick.apply(this, arguments);
12+
}
13+
},
14+
render: function(){
15+
return (
16+
<button style={styles.deleteButton} onClick={this.onClick}>
17+
<Icon icon={this.props.icon} styles={styles.buttonIcon} />
18+
</button>
19+
);
20+
}
21+
});
22+
23+
module.exports = IconButton;

plugins/sidebar/index.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@ const Sidebar = require('./sidebar');
77
const FileList = require('./file-list');
88
const File = require('./file');
99
const FileOperations = require('./file-operations');
10+
const ProjectOperations = require('./project-operations');
1011

1112
function sidebar(app, opts, done){
1213

1314
const space = app.workspace;
1415
const overlay = app.overlay;
16+
const userConfig = app.userConfig;
1517
const programmer = app.bs2serial;
1618

1719
app.view('sidebar', function(el, cb){
@@ -20,6 +22,7 @@ function sidebar(app, opts, done){
2022

2123
const Component = (
2224
<Sidebar>
25+
<ProjectOperations workspace={space} overlay={overlay} config={userConfig} />
2326
<FileList>
2427
<ListItem icon="folder" disableRipple>{space.cwd.deref()}</ListItem>
2528
{directory.map((filename) => <File key={filename} workspace={space} filename={filename} />)}
@@ -31,7 +34,7 @@ function sidebar(app, opts, done){
3134
React.render(Component, el, cb);
3235
});
3336

34-
const cwd = app.userConfig.get('cwd') || opts.defaultProject;
37+
const cwd = userConfig.get('cwd') || opts.defaultProject;
3538

3639
space.changeDir(cwd, done);
3740
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
'use strict';
2+
3+
const React = require('react');
4+
const Card = require('react-material/components/Card');
5+
const Button = require('react-material/components/Button');
6+
const TextField = require('react-material/components/TextField');
7+
8+
const Project = require('../project');
9+
const ProjectList = require('../project-list');
10+
11+
const styles = require('../styles');
12+
13+
class ProjectOverlay extends React.Component {
14+
constructor(){
15+
this.state = {
16+
value: ''
17+
};
18+
19+
this.updateName = this.updateName.bind(this);
20+
this.onAccept = this.onAccept.bind(this);
21+
this.onDelete = this.onDelete.bind(this);
22+
this.onCancel = this.onCancel.bind(this);
23+
this.newProject = this.newProject.bind(this);
24+
}
25+
26+
updateName(evt){
27+
this.setState({
28+
value: evt.target.value
29+
});
30+
}
31+
32+
clearName(){
33+
this.setState({
34+
value: ''
35+
});
36+
}
37+
38+
newProject(){
39+
this.onAccept(this.state.value);
40+
}
41+
42+
onAccept(name, evt){
43+
this.clearName();
44+
if(typeof this.props.onAccept === 'function'){
45+
this.props.onAccept(name, evt);
46+
}
47+
}
48+
49+
onCancel(evt){
50+
this.clearName();
51+
if(typeof this.props.onCancel === 'function'){
52+
this.props.onCancel(evt);
53+
}
54+
}
55+
56+
onDelete(name, evt){
57+
evt.stopPropagation();
58+
evt.preventDefault();
59+
60+
if(typeof this.props.onDelete === 'function'){
61+
this.props.onDelete(name, evt);
62+
}
63+
}
64+
65+
render(){
66+
const space = this.props.workspace;
67+
const projects = space.projects;
68+
const cwd = space.cwd.deref().substr(1);
69+
70+
return (
71+
<Card styles={[styles.overlay, styles.overlayLarge]}>
72+
<h3 style={styles.overlayTitle}>Open an existing project.</h3>
73+
<ProjectList>
74+
{projects.map((name) => <Project
75+
key={name}
76+
onSelect={this.onAccept}
77+
onDelete={this.onDelete}
78+
current={name === cwd}
79+
name={name} />)}
80+
</ProjectList>
81+
<h3 style={styles.overlayTitle}>Or start a brand new one.</h3>
82+
<TextField
83+
value={this.state.value}
84+
placeHolder="project name"
85+
styles={styles.textField}
86+
floatingLabel
87+
onChange={this.updateName} />
88+
<div style={styles.overlayButtonContainer}>
89+
<Button onClick={this.newProject}>Create</Button>
90+
<Button onClick={this.onCancel}>Cancel</Button>
91+
</div>
92+
</Card>
93+
);
94+
}
95+
}
96+
97+
module.exports = ProjectOverlay;

plugins/sidebar/project-list.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
'use strict';
2+
3+
const React = require('react');
4+
const List = require('react-material/components/List');
5+
6+
const styles = require('./styles');
7+
8+
class ProjectsList extends React.Component {
9+
10+
render(){
11+
return (
12+
<List styles={styles.projectList}>
13+
{this.props.children}
14+
</List>
15+
);
16+
}
17+
}
18+
19+
module.exports = ProjectsList;
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
'use strict';
2+
3+
const React = require('react');
4+
const { MainButton } = require('react-mfb-iceddev');
5+
6+
const ProjectOverlay = require('./overlays/project');
7+
const DeleteConfirmOverlay = require('./overlays/delete-confirm');
8+
9+
const styles = require('./styles');
10+
11+
class ProjectOperations extends React.Component {
12+
13+
constructor(){
14+
this.renderOverlay = this.renderOverlay.bind(this);
15+
this.hideOverlay = this.hideOverlay.bind(this);
16+
this.changeProject = this.changeProject.bind(this);
17+
this.deleteProject = this.deleteProject.bind(this);
18+
this.showProjectOverlay = this.showProjectOverlay.bind(this);
19+
this.showDeleteOverlay = this.showDeleteOverlay.bind(this);
20+
}
21+
22+
changeProject(projectName){
23+
const space = this.props.workspace;
24+
const config = this.props.config;
25+
26+
if(!projectName){
27+
return;
28+
}
29+
30+
space.changeDir(projectName, () => {
31+
config.set('cwd', projectName);
32+
// TODO: handle error
33+
this.hideOverlay();
34+
});
35+
}
36+
37+
deleteProject(projectName){
38+
const space = this.props.workspace;
39+
40+
if(!projectName){
41+
return;
42+
}
43+
44+
space.deleteDir(projectName, () => {
45+
// TODO: handle error
46+
this.showProjectOverlay();
47+
});
48+
}
49+
50+
renderOverlay(component){
51+
const overlay = this.props.overlay;
52+
53+
function renderer(el){
54+
React.render(component, el);
55+
}
56+
57+
overlay.render(renderer, { backdrop: true });
58+
}
59+
60+
hideOverlay(){
61+
const overlay = this.props.overlay;
62+
overlay.hide();
63+
}
64+
65+
showProjectOverlay(evt){
66+
if(evt && typeof evt.preventDefault === 'function'){
67+
evt.preventDefault();
68+
}
69+
70+
const space = this.props.workspace;
71+
72+
const component = (
73+
<ProjectOverlay
74+
workspace={space}
75+
onAccept={this.changeProject}
76+
onDelete={this.showDeleteOverlay}
77+
onCancel={this.hideOverlay} />
78+
);
79+
80+
this.renderOverlay(component);
81+
}
82+
83+
showDeleteOverlay(projectName){
84+
const component = (
85+
<DeleteConfirmOverlay
86+
name={projectName}
87+
onAccept={this.deleteProject}
88+
onCancel={this.hideOverlay} />
89+
);
90+
91+
this.renderOverlay(component);
92+
}
93+
94+
render(){
95+
return (
96+
<div className="mfb-component--tl" data-mfb-toggle="hover" style={styles.changeFolderButton}>
97+
<MainButton
98+
iconResting="ion-folder"
99+
iconActive="ion-folder"
100+
label="Change Project"
101+
onToggleMenu={this.showProjectOverlay} />
102+
</div>
103+
);
104+
}
105+
}
106+
107+
module.exports = ProjectOperations;

plugins/sidebar/project.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
'use strict';
2+
3+
const React = require('react');
4+
const ListItem = require('react-material/components/ListItem');
5+
6+
const IconButton = require('./icon-button');
7+
8+
class Project extends React.Component {
9+
10+
constructor(){
11+
this.onClick = this.onClick.bind(this);
12+
this.onDelete = this.onDelete.bind(this);
13+
}
14+
15+
onClick(evt){
16+
if(typeof this.props.onSelect === 'function'){
17+
this.props.onSelect(this.props.name, evt);
18+
}
19+
}
20+
21+
onDelete(evt){
22+
if(typeof this.props.onDelete === 'function'){
23+
this.props.onDelete(this.props.name, evt);
24+
}
25+
}
26+
27+
render(){
28+
const { name, current } = this.props;
29+
30+
let icon;
31+
if(!current){
32+
icon = (
33+
<IconButton icon="delete" onClick={this.onDelete} />
34+
);
35+
}
36+
37+
return (
38+
<ListItem icon="folder" key={name} onClick={this.onClick}>
39+
{name} {icon}
40+
</ListItem>
41+
);
42+
}
43+
}
44+
45+
module.exports = Project;

plugins/sidebar/styles.js

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ const styles = {
2222
display: 'flex',
2323
flexDirection: 'column'
2424
}),
25+
overlayLarge: StyleSheet.create({
26+
height: 400
27+
}),
2528
overlayTitle: {
2629
margin: 0
2730
},
@@ -31,7 +34,34 @@ const styles = {
3134
overlaySelectContainer: {
3235
display: 'flex',
3336
marginTop: 20
34-
}
37+
},
38+
changeFolderButton: {
39+
position: 'absolute',
40+
top: -28,
41+
margin: 0,
42+
right: 19,
43+
left: 'auto',
44+
transform: 'none'
45+
},
46+
deleteButton: {
47+
border: 0,
48+
background: 'none',
49+
padding: 0,
50+
float: 'right',
51+
position: 'relative',
52+
zIndex: 7
53+
},
54+
buttonIcon: StyleSheet.create({
55+
display: 'inline-block',
56+
padding: 0,
57+
width: '30px',
58+
verticalAlign: 'middle',
59+
pointerEvents: 'none'
60+
}),
61+
projectList: StyleSheet.create({
62+
flex: 1,
63+
marginTop: 10
64+
})
3565
};
3666

3767
module.exports = styles;

0 commit comments

Comments
 (0)