diff --git a/client/constants.js b/client/constants.js
index 967df39b..6f5833fd 100644
--- a/client/constants.js
+++ b/client/constants.js
@@ -30,6 +30,9 @@ export const SET_PROJECT = 'SET_PROJECT';
export const SET_PROJECTS = 'SET_PROJECTS';
export const SET_SELECTED_FILE = 'SET_SELECTED_FILE';
+export const SHOW_MODAL = 'SHOW_MODAL';
+export const HIDE_MODAL = 'HIDE_MODAL';
+export const CREATE_FILE = 'CREATE_FILE';
// eventually, handle errors more specifically and better
export const ERROR = 'ERROR';
diff --git a/client/modules/IDE/actions/files.js b/client/modules/IDE/actions/files.js
index f7ebd8f3..8bc0c6c3 100644
--- a/client/modules/IDE/actions/files.js
+++ b/client/modules/IDE/actions/files.js
@@ -7,3 +7,11 @@ export function updateFileContent(name, content) {
content
};
}
+
+// TODO make req to server
+export function createFile(name) {
+ return {
+ type: ActionTypes.CREATE_FILE,
+ name
+ };
+}
diff --git a/client/modules/IDE/actions/ide.js b/client/modules/IDE/actions/ide.js
index 0bcbc414..70363669 100644
--- a/client/modules/IDE/actions/ide.js
+++ b/client/modules/IDE/actions/ide.js
@@ -24,3 +24,15 @@ export function setSelectedFile(fileId) {
selectedFile: fileId
};
}
+
+export function newFile() {
+ return {
+ type: ActionTypes.SHOW_MODAL
+ };
+}
+
+export function closeNewFileModal() {
+ return {
+ type: ActionTypes.HIDE_MODAL
+ };
+}
diff --git a/client/modules/IDE/components/NewFileForm.js b/client/modules/IDE/components/NewFileForm.js
new file mode 100644
index 00000000..c46ca9e5
--- /dev/null
+++ b/client/modules/IDE/components/NewFileForm.js
@@ -0,0 +1,28 @@
+import React, { PropTypes } from 'react';
+
+function NewFileForm(props) {
+ const { fields: { name }, handleSubmit } = props;
+ return (
+
+ );
+}
+
+NewFileForm.propTypes = {
+ fields: PropTypes.shape({
+ name: PropTypes.string.isRequired
+ }).isRequired,
+ handleSubmit: PropTypes.func.isRequired,
+ createFile: PropTypes.func.isRequired
+};
+
+export default NewFileForm;
diff --git a/client/modules/IDE/components/NewFileModal.js b/client/modules/IDE/components/NewFileModal.js
new file mode 100644
index 00000000..1072348b
--- /dev/null
+++ b/client/modules/IDE/components/NewFileModal.js
@@ -0,0 +1,68 @@
+import React, { PropTypes } from 'react';
+import { bindActionCreators } from 'redux';
+import { reduxForm } from 'redux-form';
+import NewFileForm from './NewFileForm';
+import * as FileActions from '../actions/files';
+import classNames from 'classnames';
+import InlineSVG from 'react-inlinesvg';
+const exitUrl = require('../../../images/exit.svg');
+
+// At some point this will probably be generalized to a generic modal
+// in which you can insert different content
+// but for now, let's just make this work
+function NewFileModal(props) {
+ const modalClass = classNames({
+ modal: true,
+ 'modal--hidden': !props.isVisible
+ });
+
+ return (
+
+ );
+}
+
+NewFileModal.propTypes = {
+ isVisible: PropTypes.bool.isRequired,
+ closeModal: PropTypes.func.isRequired
+};
+
+function mapStateToProps(state) {
+ return {
+ file: state.files
+ };
+}
+
+function mapDispatchToProps(dispatch) {
+ return bindActionCreators(FileActions, dispatch);
+}
+
+function validate(formProps) {
+ const errors = {};
+
+ if (!formProps.name) {
+ errors.name = 'Please enter a name';
+ }
+
+ // if (formProps.name.match(/(.+\.js$|.+\.css$)/)) {
+ // errors.name = 'File must be of type JavaScript or CSS.';
+ // }
+
+ return errors;
+}
+
+
+export default reduxForm({
+ form: 'new-file',
+ fields: ['name'],
+ validate
+}, mapStateToProps, mapDispatchToProps)(NewFileModal);
diff --git a/client/modules/IDE/components/Sidebar.js b/client/modules/IDE/components/Sidebar.js
index cb8c276f..ffeea432 100644
--- a/client/modules/IDE/components/Sidebar.js
+++ b/client/modules/IDE/components/Sidebar.js
@@ -4,6 +4,15 @@ import classNames from 'classnames';
function Sidebar(props) {
return (
+
{props.files.map(file => {
let itemClass = classNames({
diff --git a/client/modules/IDE/pages/IDEView.js b/client/modules/IDE/pages/IDEView.js
index c7308fa9..edc9043b 100644
--- a/client/modules/IDE/pages/IDEView.js
+++ b/client/modules/IDE/pages/IDEView.js
@@ -4,6 +4,7 @@ import Sidebar from '../components/Sidebar';
import PreviewFrame from '../components/PreviewFrame';
import Toolbar from '../components/Toolbar';
import Preferences from '../components/Preferences';
+import NewFileModal from '../components/NewFileModal';
import Nav from '../../../components/Nav';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
@@ -58,6 +59,7 @@ class IDEView extends React.Component {
files={this.props.files}
selectedFile={this.props.selectedFile}
setSelectedFile={this.props.setSelectedFile}
+ newFile={this.props.newFile}
/>
+
);
}
@@ -92,7 +98,8 @@ IDEView.propTypes = {
createProject: PropTypes.func.isRequired,
saveProject: PropTypes.func.isRequired,
ide: PropTypes.shape({
- isPlaying: PropTypes.bool.isRequired
+ isPlaying: PropTypes.bool.isRequired,
+ modalIsVisible: PropTypes.bool.isRequired
}).isRequired,
startSketch: PropTypes.func.isRequired,
stopSketch: PropTypes.func.isRequired,
@@ -125,7 +132,9 @@ IDEView.propTypes = {
setSelectedFile: PropTypes.func.isRequired,
htmlFile: PropTypes.object.isRequired,
jsFiles: PropTypes.array.isRequired,
- cssFiles: PropTypes.array.isRequired
+ cssFiles: PropTypes.array.isRequired,
+ newFile: PropTypes.func.isRequired,
+ closeNewFileModal: PropTypes.func.isRequired
};
function mapStateToProps(state) {
diff --git a/client/modules/IDE/reducers/files.js b/client/modules/IDE/reducers/files.js
index db53dbc3..6bc35e1b 100644
--- a/client/modules/IDE/reducers/files.js
+++ b/client/modules/IDE/reducers/files.js
@@ -64,6 +64,8 @@ const files = (state = initialState, action) => {
return [...action.files];
case ActionTypes.SET_PROJECT:
return [...action.files];
+ case ActionTypes.NEW_FILE:
+ return [{ name: action.name, content: '' }, ...state.files];
default:
return state;
}
diff --git a/client/modules/IDE/reducers/ide.js b/client/modules/IDE/reducers/ide.js
index 9c4c5b88..8b283ca0 100644
--- a/client/modules/IDE/reducers/ide.js
+++ b/client/modules/IDE/reducers/ide.js
@@ -2,7 +2,8 @@ import * as ActionTypes from '../../../constants';
const initialState = {
isPlaying: false,
- selectedFile: '1'
+ selectedFile: '1',
+ modalIsVisible: false
};
const ide = (state = initialState, action) => {
@@ -17,6 +18,10 @@ const ide = (state = initialState, action) => {
case ActionTypes.SET_PROJECT:
case ActionTypes.NEW_PROJECT:
return Object.assign({}, state, { selectedFile: action.selectedFile });
+ case ActionTypes.SHOW_MODAL:
+ return Object.assign({}, state, { modalIsVisible: true });
+ case ActionTypes.HIDE_MODAL:
+ return Object.assign({}, state, { modalIsVisible: false });
default:
return state;
}
diff --git a/client/styles/abstracts/_variables.scss b/client/styles/abstracts/_variables.scss
index a41a2097..aea3f2fb 100644
--- a/client/styles/abstracts/_variables.scss
+++ b/client/styles/abstracts/_variables.scss
@@ -19,6 +19,7 @@ $light-button-background-active-color: #f10046;
$light-button-hover-color: $white;
$light-button-active-color: $white;
$light-modal-button-background-color: #e6e6e6;
+$light-modal-border-color: #B9D0E1;
$light-icon-color: #8b8b8b;
$light-icon-hover-color: $light-primary-text-color;
diff --git a/client/styles/base/_base.scss b/client/styles/base/_base.scss
index fda0b662..4f5358bb 100644
--- a/client/styles/base/_base.scss
+++ b/client/styles/base/_base.scss
@@ -31,8 +31,9 @@ input, button {
input {
padding: #{5 / $base-font-size}rem;
- border-radius: 2px;
+ // border-radius: 2px;
border: 1px solid $input-border-color;
+ padding: #{10 / $base-font-size}rem;
}
input[type="submit"] {
diff --git a/client/styles/components/_editor.scss b/client/styles/components/_editor.scss
index 0964019c..c1efcc43 100644
--- a/client/styles/components/_editor.scss
+++ b/client/styles/components/_editor.scss
@@ -56,7 +56,7 @@
.CodeMirror-lint-tooltip {
font-family: Montserrat, sans-serif;
border-radius: 2px;
- border: 1px solid #B9D0E1;
+ border: 1px solid $light-modal-border-color;
background-color: $light-button-background-color;
}
diff --git a/client/styles/components/_modal.scss b/client/styles/components/_modal.scss
new file mode 100644
index 00000000..22745606
--- /dev/null
+++ b/client/styles/components/_modal.scss
@@ -0,0 +1,36 @@
+.modal {
+ position: absolute;
+ top: #{66 / $base-font-size}rem;
+ right: #{400 / $base-font-size}rem;;
+ z-index: 100;
+}
+
+.modal--hidden {
+ display: none;
+}
+
+.modal-content {
+ border: 1px solid $light-modal-border-color;
+ background-color: $light-button-background-color;
+ height: #{150 / $base-font-size}rem;
+ width: #{400 / $base-font-size}rem;
+ padding: #{20 / $base-font-size}rem;
+}
+
+.modal__exit-button {
+ @extend %icon;
+}
+
+.modal__header {
+ display: flex;
+ justify-content: space-between;
+ margin-bottom: #{20 / $base-font-size}rem;
+}
+
+.new-file-form__name-label {
+ display: none;
+}
+
+.new-file-form__name-input {
+ margin-right: #{10 / $base-font-size}rem;
+}
\ No newline at end of file
diff --git a/client/styles/components/_sidebar.scss b/client/styles/components/_sidebar.scss
index 38883386..b5f3d8e2 100644
--- a/client/styles/components/_sidebar.scss
+++ b/client/styles/components/_sidebar.scss
@@ -1,8 +1,25 @@
+.sidebar__header {
+ padding: #{10 / $base-font-size}rem #{6 / $base-font-size}rem;
+ display: flex;
+ justify-content: space-between;
+ border-top: 1px solid $ide-border-color;
+}
+
+.sidebar__title {
+ font-size: #{12 / $base-font-size}rem;
+ display: inline-block;
+}
+
+.sidebar__add {
+ cursor: pointer;
+}
+
.sidebar__file-list {
border-top: 1px solid $ide-border-color;
}
.sidebar__file-item {
+ font-size: #{12 / $base-font-size}rem;
padding: #{8 / $base-font-size}rem #{20 / $base-font-size}rem;
color: $light-inactive-text-color;
cursor: pointer;
diff --git a/client/styles/main.scss b/client/styles/main.scss
index 4a20ba94..64dc92ab 100644
--- a/client/styles/main.scss
+++ b/client/styles/main.scss
@@ -16,6 +16,7 @@
@import 'components/login';
@import 'components/sketch-list';
@import 'components/sidebar';
+@import 'components/modal';
@import 'layout/ide';
@import 'layout/sketch-list';