add new file popup, not tested with redux
This commit is contained in:
parent
30992ac2de
commit
70588cd487
15 changed files with 205 additions and 5 deletions
|
@ -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';
|
||||||
|
|
|
@ -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
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
28
client/modules/IDE/components/NewFileForm.js
Normal file
28
client/modules/IDE/components/NewFileForm.js
Normal 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;
|
68
client/modules/IDE/components/NewFileModal.js
Normal file
68
client/modules/IDE/components/NewFileModal.js
Normal 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);
|
|
@ -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({
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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"] {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
36
client/styles/components/_modal.scss
Normal file
36
client/styles/components/_modal.scss
Normal 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;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
|
|
@ -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';
|
||||||
|
|
Loading…
Reference in a new issue