add new file popup, not tested with redux

This commit is contained in:
catarak 2016-07-13 16:13:28 -04:00
parent 30992ac2de
commit 70588cd487
15 changed files with 205 additions and 5 deletions

View file

@ -30,6 +30,9 @@ export const SET_PROJECT = 'SET_PROJECT';
export const SET_PROJECTS = 'SET_PROJECTS'; export const SET_PROJECTS = 'SET_PROJECTS';
export const SET_SELECTED_FILE = 'SET_SELECTED_FILE'; 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 // eventually, handle errors more specifically and better
export const ERROR = 'ERROR'; export const ERROR = 'ERROR';

View file

@ -7,3 +7,11 @@ export function updateFileContent(name, content) {
content content
}; };
} }
// TODO make req to server
export function createFile(name) {
return {
type: ActionTypes.CREATE_FILE,
name
};
}

View file

@ -24,3 +24,15 @@ export function setSelectedFile(fileId) {
selectedFile: fileId selectedFile: fileId
}; };
} }
export function newFile() {
return {
type: ActionTypes.SHOW_MODAL
};
}
export function closeNewFileModal() {
return {
type: ActionTypes.HIDE_MODAL
};
}

View file

@ -0,0 +1,28 @@
import React, { PropTypes } from 'react';
function NewFileForm(props) {
const { fields: { name }, handleSubmit } = props;
return (
<form className="new-file-form" onSubmit={handleSubmit(props.createFile.bind(this))}>
<label className="new-file-form__name-label" htmlFor="name">Name:</label>
<input
className="new-file-form__name-input"
id="name"
type="text"
placeholder="Name"
{...name}
/>
<input type="submit" value="Add File" />
</form>
);
}
NewFileForm.propTypes = {
fields: PropTypes.shape({
name: PropTypes.string.isRequired
}).isRequired,
handleSubmit: PropTypes.func.isRequired,
createFile: PropTypes.func.isRequired
};
export default NewFileForm;

View file

@ -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 (
<section className={modalClass}>
<div className="modal-content">
<div className="modal__header">
<h2 className="modal__title">Add File</h2>
<button className="modal__exit-button" onClick={props.closeModal}>
<InlineSVG src={exitUrl} alt="Close New File Modal" />
</button>
</div>
<NewFileForm {...props} />
</div>
</section>
);
}
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);

View file

@ -4,6 +4,15 @@ import classNames from 'classnames';
function Sidebar(props) { function Sidebar(props) {
return ( return (
<section className="sidebar"> <section className="sidebar">
<div className="sidebar__header">
<h3 className="sidebar__title">Sketch Files</h3>
<a
className="sidebar__add"
onClick={props.newFile}
>
+
</a>
</div>
<ul className="sidebar__file-list"> <ul className="sidebar__file-list">
{props.files.map(file => { {props.files.map(file => {
let itemClass = classNames({ let itemClass = classNames({

View file

@ -4,6 +4,7 @@ import Sidebar from '../components/Sidebar';
import PreviewFrame from '../components/PreviewFrame'; import PreviewFrame from '../components/PreviewFrame';
import Toolbar from '../components/Toolbar'; import Toolbar from '../components/Toolbar';
import Preferences from '../components/Preferences'; import Preferences from '../components/Preferences';
import NewFileModal from '../components/NewFileModal';
import Nav from '../../../components/Nav'; import Nav from '../../../components/Nav';
import { bindActionCreators } from 'redux'; import { bindActionCreators } from 'redux';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
@ -58,6 +59,7 @@ class IDEView extends React.Component {
files={this.props.files} files={this.props.files}
selectedFile={this.props.selectedFile} selectedFile={this.props.selectedFile}
setSelectedFile={this.props.setSelectedFile} setSelectedFile={this.props.setSelectedFile}
newFile={this.props.newFile}
/> />
<Editor <Editor
file={this.props.selectedFile} file={this.props.selectedFile}
@ -78,6 +80,10 @@ class IDEView extends React.Component {
} }
isPlaying={this.props.ide.isPlaying} isPlaying={this.props.ide.isPlaying}
/> />
<NewFileModal
isVisible={this.props.ide.modalIsVisible}
closeModal={this.props.closeNewFileModal}
/>
</div> </div>
); );
} }
@ -92,7 +98,8 @@ IDEView.propTypes = {
createProject: PropTypes.func.isRequired, createProject: PropTypes.func.isRequired,
saveProject: PropTypes.func.isRequired, saveProject: PropTypes.func.isRequired,
ide: PropTypes.shape({ ide: PropTypes.shape({
isPlaying: PropTypes.bool.isRequired isPlaying: PropTypes.bool.isRequired,
modalIsVisible: PropTypes.bool.isRequired
}).isRequired, }).isRequired,
startSketch: PropTypes.func.isRequired, startSketch: PropTypes.func.isRequired,
stopSketch: PropTypes.func.isRequired, stopSketch: PropTypes.func.isRequired,
@ -125,7 +132,9 @@ IDEView.propTypes = {
setSelectedFile: PropTypes.func.isRequired, setSelectedFile: PropTypes.func.isRequired,
htmlFile: PropTypes.object.isRequired, htmlFile: PropTypes.object.isRequired,
jsFiles: PropTypes.array.isRequired, jsFiles: PropTypes.array.isRequired,
cssFiles: PropTypes.array.isRequired cssFiles: PropTypes.array.isRequired,
newFile: PropTypes.func.isRequired,
closeNewFileModal: PropTypes.func.isRequired
}; };
function mapStateToProps(state) { function mapStateToProps(state) {

View file

@ -64,6 +64,8 @@ const files = (state = initialState, action) => {
return [...action.files]; return [...action.files];
case ActionTypes.SET_PROJECT: case ActionTypes.SET_PROJECT:
return [...action.files]; return [...action.files];
case ActionTypes.NEW_FILE:
return [{ name: action.name, content: '' }, ...state.files];
default: default:
return state; return state;
} }

View file

@ -2,7 +2,8 @@ import * as ActionTypes from '../../../constants';
const initialState = { const initialState = {
isPlaying: false, isPlaying: false,
selectedFile: '1' selectedFile: '1',
modalIsVisible: false
}; };
const ide = (state = initialState, action) => { const ide = (state = initialState, action) => {
@ -17,6 +18,10 @@ const ide = (state = initialState, action) => {
case ActionTypes.SET_PROJECT: case ActionTypes.SET_PROJECT:
case ActionTypes.NEW_PROJECT: case ActionTypes.NEW_PROJECT:
return Object.assign({}, state, { selectedFile: action.selectedFile }); 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: default:
return state; return state;
} }

View file

@ -19,6 +19,7 @@ $light-button-background-active-color: #f10046;
$light-button-hover-color: $white; $light-button-hover-color: $white;
$light-button-active-color: $white; $light-button-active-color: $white;
$light-modal-button-background-color: #e6e6e6; $light-modal-button-background-color: #e6e6e6;
$light-modal-border-color: #B9D0E1;
$light-icon-color: #8b8b8b; $light-icon-color: #8b8b8b;
$light-icon-hover-color: $light-primary-text-color; $light-icon-hover-color: $light-primary-text-color;

View file

@ -31,8 +31,9 @@ input, button {
input { input {
padding: #{5 / $base-font-size}rem; padding: #{5 / $base-font-size}rem;
border-radius: 2px; // border-radius: 2px;
border: 1px solid $input-border-color; border: 1px solid $input-border-color;
padding: #{10 / $base-font-size}rem;
} }
input[type="submit"] { input[type="submit"] {

View file

@ -56,7 +56,7 @@
.CodeMirror-lint-tooltip { .CodeMirror-lint-tooltip {
font-family: Montserrat, sans-serif; font-family: Montserrat, sans-serif;
border-radius: 2px; border-radius: 2px;
border: 1px solid #B9D0E1; border: 1px solid $light-modal-border-color;
background-color: $light-button-background-color; background-color: $light-button-background-color;
} }

View file

@ -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;
}

View file

@ -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 { .sidebar__file-list {
border-top: 1px solid $ide-border-color; border-top: 1px solid $ide-border-color;
} }
.sidebar__file-item { .sidebar__file-item {
font-size: #{12 / $base-font-size}rem;
padding: #{8 / $base-font-size}rem #{20 / $base-font-size}rem; padding: #{8 / $base-font-size}rem #{20 / $base-font-size}rem;
color: $light-inactive-text-color; color: $light-inactive-text-color;
cursor: pointer; cursor: pointer;

View file

@ -16,6 +16,7 @@
@import 'components/login'; @import 'components/login';
@import 'components/sketch-list'; @import 'components/sketch-list';
@import 'components/sidebar'; @import 'components/sidebar';
@import 'components/modal';
@import 'layout/ide'; @import 'layout/ide';
@import 'layout/sketch-list'; @import 'layout/sketch-list';