Merge branch 'master' into master

This commit is contained in:
Cassie Tarakajian 2019-04-03 11:38:39 -04:00 committed by GitHub
commit 6d998d5f73
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 324 additions and 300 deletions

View file

@ -55,14 +55,16 @@ class Nav extends React.PureComponent {
this.handleFocusForHelp = this.handleFocus.bind(this, 'help'); this.handleFocusForHelp = this.handleFocus.bind(this, 'help');
this.toggleDropdownForAccount = this.toggleDropdown.bind(this, 'account'); this.toggleDropdownForAccount = this.toggleDropdown.bind(this, 'account');
this.handleFocusForAccount = this.handleFocus.bind(this, 'account'); this.handleFocusForAccount = this.handleFocus.bind(this, 'account');
this.closeDropDown = this.closeDropDown.bind(this);
} }
componentWillMount() { componentDidMount() {
document.addEventListener('mousedown', this.handleClick, false); document.addEventListener('mousedown', this.handleClick, false);
document.addEventListener('keydown', this.closeDropDown, false);
} }
componentWillUnmount() { componentWillUnmount() {
document.removeEventListener('mousedown', this.handleClick, false); document.removeEventListener('mousedown', this.handleClick, false);
document.removeEventListener('keydown', this.closeDropDown, false);
} }
setDropdown(dropdown) { setDropdown(dropdown) {
@ -71,8 +73,17 @@ class Nav extends React.PureComponent {
}); });
} }
closeDropDown(e) {
if (e.keyCode === 27) {
this.setDropdown('none');
}
}
handleClick(e) { handleClick(e) {
if (this.node.contains(e.target)) { if (!this.node) {
return;
}
if (this.node && this.node.contains(e.target)) {
return; return;
} }
@ -233,25 +244,23 @@ class Nav extends React.PureComponent {
<nav className="nav" title="main-navigation" ref={(node) => { this.node = node; }}> <nav className="nav" title="main-navigation" ref={(node) => { this.node = node; }}>
<ul className="nav__items-left" title="project-menu"> <ul className="nav__items-left" title="project-menu">
<li className="nav__item-logo"> <li className="nav__item-logo">
<InlineSVG src={logoUrl} alt="p5.js logo" /> <InlineSVG src={logoUrl} alt="p5.js logo" className="svg__logo" />
</li> </li>
<li className={navDropdownState.file}> <li className={navDropdownState.file}>
<button <button
onClick={this.toggleDropdownForFile} onClick={this.toggleDropdownForFile}
onBlur={this.handleBlur} onBlur={this.handleBlur}
onFocus={this.clearHideTimeout} onFocus={this.clearHideTimeout}
onMouseOver={() => {
if (this.state.dropdownOpen !== 'none') {
this.setDropdown('file');
}
}}
> >
<span className="nav__item-header">File</span> <span className="nav__item-header">File</span>
<InlineSVG className="nav__item-header-triangle" src={triangleUrl} /> <InlineSVG className="nav__item-header-triangle" src={triangleUrl} />
</button> </button>
<ul className="nav__dropdown"> <ul className="nav__dropdown">
<button
onClick={this.toggleDropdownForFile}
className="nav__dropdown-heading"
>
<span>File</span>
<InlineSVG src={triangleUrl} />
</button>
<li className="nav__dropdown-item"> <li className="nav__dropdown-item">
<button <button
onClick={this.handleNew} onClick={this.handleNew}
@ -331,18 +340,16 @@ class Nav extends React.PureComponent {
onClick={this.toggleDropdownForEdit} onClick={this.toggleDropdownForEdit}
onBlur={this.handleBlur} onBlur={this.handleBlur}
onFocus={this.clearHideTimeout} onFocus={this.clearHideTimeout}
onMouseOver={() => {
if (this.state.dropdownOpen !== 'none') {
this.setDropdown('edit');
}
}}
> >
<span className="nav__item-header">Edit</span> <span className="nav__item-header">Edit</span>
<InlineSVG className="nav__item-header-triangle" src={triangleUrl} /> <InlineSVG className="nav__item-header-triangle" src={triangleUrl} />
</button> </button>
<ul className="nav__dropdown"> <ul className="nav__dropdown" >
<button
onClick={this.toggleDropdownForEdit}
className="nav__dropdown-heading"
>
<span>Edit</span>
<InlineSVG src={triangleUrl} />
</button>
<li className="nav__dropdown-item"> <li className="nav__dropdown-item">
<button <button
onClick={() => { onClick={() => {
@ -393,18 +400,16 @@ class Nav extends React.PureComponent {
onClick={this.toggleDropdownForSketch} onClick={this.toggleDropdownForSketch}
onBlur={this.handleBlur} onBlur={this.handleBlur}
onFocus={this.clearHideTimeout} onFocus={this.clearHideTimeout}
onMouseOver={() => {
if (this.state.dropdownOpen !== 'none') {
this.setDropdown('sketch');
}
}}
> >
<span className="nav__item-header">Sketch</span> <span className="nav__item-header">Sketch</span>
<InlineSVG className="nav__item-header-triangle" src={triangleUrl} /> <InlineSVG className="nav__item-header-triangle" src={triangleUrl} />
</button> </button>
<ul className="nav__dropdown"> <ul className="nav__dropdown">
<button
onClick={this.toggleDropdownForSketch}
className="nav__dropdown-heading"
>
<span>Sketch</span>
<InlineSVG src={triangleUrl} />
</button>
<li className="nav__dropdown-item"> <li className="nav__dropdown-item">
<button <button
onClick={this.handleAddFile} onClick={this.handleAddFile}
@ -470,18 +475,16 @@ class Nav extends React.PureComponent {
onClick={this.toggleDropdownForHelp} onClick={this.toggleDropdownForHelp}
onBlur={this.handleBlur} onBlur={this.handleBlur}
onFocus={this.clearHideTimeout} onFocus={this.clearHideTimeout}
onMouseOver={() => {
if (this.state.dropdownOpen !== 'none') {
this.setDropdown('help');
}
}}
> >
<span className="nav__item-header">Help & Feedback</span> <span className="nav__item-header">Help & Feedback</span>
<InlineSVG className="nav__item-header-triangle" src={triangleUrl} /> <InlineSVG className="nav__item-header-triangle" src={triangleUrl} />
</button> </button>
<ul className="nav__dropdown"> <ul className="nav__dropdown">
<button
onClick={this.toggleDropdownForHelp}
className="nav__dropdown-heading"
>
<span>Help & Feedback</span>
<InlineSVG src={triangleUrl} />
</button>
<li className="nav__dropdown-item"> <li className="nav__dropdown-item">
<button <button
onFocus={this.handleFocusForHelp} onFocus={this.handleFocusForHelp}
@ -547,18 +550,16 @@ class Nav extends React.PureComponent {
onClick={this.toggleDropdownForAccount} onClick={this.toggleDropdownForAccount}
onBlur={this.handleBlur} onBlur={this.handleBlur}
onFocus={this.clearHideTimeout} onFocus={this.clearHideTimeout}
onMouseOver={() => {
if (this.state.dropdownOpen !== 'none') {
this.setDropdown('account');
}
}}
> >
My Account My Account
</button>
<InlineSVG className="nav__item-header-triangle" src={triangleUrl} /> <InlineSVG className="nav__item-header-triangle" src={triangleUrl} />
<ul className="nav__dropdown">
<button
onClick={this.toggleDropdown.bind(this, 'account')}
className="nav__dropdown-heading"
>
<span>My Account</span>
<InlineSVG src={triangleUrl} />
</button> </button>
<ul className="nav__dropdown">
<li className="nav__dropdown-item"> <li className="nav__dropdown-item">
<Link <Link
to={`/${this.props.user.username}/sketches`} to={`/${this.props.user.username}/sketches`}

View file

@ -1,4 +1,4 @@
// TODO Organize this file by reducer type, ot break this apart into // TODO Organize this file by reducer type, to break this apart into
// multiple files // multiple files
export const UPDATE_FILE_CONTENT = 'UPDATE_FILE_CONTENT'; export const UPDATE_FILE_CONTENT = 'UPDATE_FILE_CONTENT';
export const TOGGLE_SKETCH = 'TOGGLE_SKETCH'; export const TOGGLE_SKETCH = 'TOGGLE_SKETCH';
@ -13,14 +13,6 @@ export const OPEN_PREFERENCES = 'OPEN_PREFERENCES';
export const CLOSE_PREFERENCES = 'CLOSE_PREFERENCES'; export const CLOSE_PREFERENCES = 'CLOSE_PREFERENCES';
export const SET_FONT_SIZE = 'SET_FONT_SIZE'; export const SET_FONT_SIZE = 'SET_FONT_SIZE';
export const INCREASE_INDENTATION = 'INCREASE_INDENTATION';
export const DECREASE_INDENTATION = 'DECREASE_INDENTATION';
export const UPDATE_INDENTATION = 'UPDATE_INDENTATION';
export const SET_INDENTATION = 'SET_INDENTATION';
export const INDENT_WITH_SPACE = 'INDENT_WITH_SPACE';
export const INDENT_WITH_TAB = 'INDENT_WITH_TAB';
export const AUTH_USER = 'AUTH_USER'; export const AUTH_USER = 'AUTH_USER';
export const UNAUTH_USER = 'UNAUTH_USER'; export const UNAUTH_USER = 'UNAUTH_USER';
export const AUTH_ERROR = 'AUTH_ERROR'; export const AUTH_ERROR = 'AUTH_ERROR';
@ -62,6 +54,7 @@ export const UPDATE_FILE_NAME = 'UPDATE_FILE_NAME';
export const DELETE_FILE = 'DELETE_FILE'; export const DELETE_FILE = 'DELETE_FILE';
export const SET_AUTOSAVE = 'SET_AUTOSAVE'; export const SET_AUTOSAVE = 'SET_AUTOSAVE';
export const SET_LINEWRAP = 'SET_LINEWRAP';
export const SET_LINT_WARNING = 'SET_LINT_WARNING'; export const SET_LINT_WARNING = 'SET_LINT_WARNING';
export const SET_PREFERENCES = 'SET_PREFERENCES'; export const SET_PREFERENCES = 'SET_PREFERENCES';
export const SET_TEXT_OUTPUT = 'SET_TEXT_OUTPUT'; export const SET_TEXT_OUTPUT = 'SET_TEXT_OUTPUT';

View file

@ -14,6 +14,7 @@ class App extends React.Component {
componentDidMount() { componentDidMount() {
this.setState({ isMounted: true }); // eslint-disable-line react/no-did-mount-set-state this.setState({ isMounted: true }); // eslint-disable-line react/no-did-mount-set-state
document.body.className = 'light';
} }
componentWillReceiveProps(nextProps) { componentWillReceiveProps(nextProps) {

View file

@ -32,58 +32,6 @@ export function setFontSize(value) {
}; };
} }
export function setIndentation(value) {
return (dispatch, getState) => {
dispatch({
type: ActionTypes.SET_INDENTATION,
value
});
const state = getState();
if (state.user.authenticated) {
const formParams = {
preferences: {
indentationAmount: value
}
};
updatePreferences(formParams, dispatch);
}
};
}
export function indentWithTab() {
return (dispatch, getState) => {
dispatch({
type: ActionTypes.INDENT_WITH_TAB
});
const state = getState();
if (state.user.authenticated) {
const formParams = {
preferences: {
isTabIndent: true
}
};
updatePreferences(formParams, dispatch);
}
};
}
export function indentWithSpace() {
return (dispatch, getState) => {
dispatch({
type: ActionTypes.INDENT_WITH_SPACE
});
const state = getState();
if (state.user.authenticated) {
const formParams = {
preferences: {
isTabIndent: false
}
};
updatePreferences(formParams, dispatch);
}
};
}
export function setAutosave(value) { export function setAutosave(value) {
return (dispatch, getState) => { return (dispatch, getState) => {
dispatch({ dispatch({
@ -102,6 +50,24 @@ export function setAutosave(value) {
}; };
} }
export function setLinewrap(value) {
return (dispatch, getState) => {
dispatch({
type: ActionTypes.SET_LINEWRAP,
value
});
const state = getState();
if (state.user.authenticated) {
const formParams = {
preferences: {
linewrap: value
}
};
updatePreferences(formParams, dispatch);
}
};
}
export function setLintWarning(value) { export function setLintWarning(value) {
return (dispatch, getState) => { return (dispatch, getState) => {
dispatch({ dispatch({

View file

@ -50,6 +50,9 @@ const unsavedChangesDotUrl = require('../../../images/unsaved-changes-dot.svg');
const rightArrowUrl = require('../../../images/right-arrow.svg'); const rightArrowUrl = require('../../../images/right-arrow.svg');
const leftArrowUrl = require('../../../images/left-arrow.svg'); const leftArrowUrl = require('../../../images/left-arrow.svg');
const IS_TAB_INDENT = false;
const INDENTATION_AMOUNT = 2;
class Editor extends React.Component { class Editor extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
@ -80,7 +83,7 @@ class Editor extends React.Component {
lineNumbers: true, lineNumbers: true,
styleActiveLine: true, styleActiveLine: true,
inputStyle: 'contenteditable', inputStyle: 'contenteditable',
lineWrapping: false, lineWrapping: this.props.linewrap,
fixedGutter: false, fixedGutter: false,
foldGutter: true, foldGutter: true,
foldOptions: { widget: '\u2026' }, foldOptions: { widget: '\u2026' },
@ -105,6 +108,7 @@ class Editor extends React.Component {
delete this._cm.options.lint.options.errors; delete this._cm.options.lint.options.errors;
this._cm.setOption('extraKeys', { this._cm.setOption('extraKeys', {
Tab: cm => cm.replaceSelection(' '.repeat(INDENTATION_AMOUNT)),
[`${metaKey}-Enter`]: () => null, [`${metaKey}-Enter`]: () => null,
[`Shift-${metaKey}-Enter`]: () => null, [`Shift-${metaKey}-Enter`]: () => null,
[`${metaKey}-F`]: 'findPersistent', [`${metaKey}-F`]: 'findPersistent',
@ -137,9 +141,6 @@ class Editor extends React.Component {
}); });
this._cm.getWrapperElement().style['font-size'] = `${this.props.fontSize}px`; this._cm.getWrapperElement().style['font-size'] = `${this.props.fontSize}px`;
this._cm.setOption('indentWithTabs', this.props.isTabIndent);
this._cm.setOption('tabSize', this.props.indentationAmount);
this._cm.setOption('indentUnit', this.props.indentationAmount);
this.props.provideController({ this.props.provideController({
tidyCode: this.tidyCode, tidyCode: this.tidyCode,
@ -174,12 +175,8 @@ class Editor extends React.Component {
if (this.props.fontSize !== prevProps.fontSize) { if (this.props.fontSize !== prevProps.fontSize) {
this._cm.getWrapperElement().style['font-size'] = `${this.props.fontSize}px`; this._cm.getWrapperElement().style['font-size'] = `${this.props.fontSize}px`;
} }
if (this.props.indentationAmount !== prevProps.indentationAmount) { if (this.props.linewrap !== prevProps.linewrap) {
this._cm.setOption('tabSize', this.props.indentationAmount); this._cm.setOption('lineWrapping', this.props.linewrap);
this._cm.setOption('indentUnit', this.props.indentationAmount);
}
if (this.props.isTabIndent !== prevProps.isTabIndent) {
this._cm.setOption('indentWithTabs', this.props.isTabIndent);
} }
if (this.props.theme !== prevProps.theme) { if (this.props.theme !== prevProps.theme) {
this._cm.setOption('theme', `p5-${this.props.theme}`); this._cm.setOption('theme', `p5-${this.props.theme}`);
@ -254,8 +251,8 @@ class Editor extends React.Component {
tidyCode() { tidyCode() {
const beautifyOptions = { const beautifyOptions = {
indent_size: this.props.indentationAmount, indent_size: INDENTATION_AMOUNT,
indent_with_tabs: this.props.isTabIndent indent_with_tabs: IS_TAB_INDENT
}; };
const mode = this._cm.getOption('mode'); const mode = this._cm.getOption('mode');
@ -339,6 +336,7 @@ class Editor extends React.Component {
Editor.propTypes = { Editor.propTypes = {
lintWarning: PropTypes.bool.isRequired, lintWarning: PropTypes.bool.isRequired,
linewrap: PropTypes.bool.isRequired,
lintMessages: PropTypes.arrayOf(PropTypes.shape({ lintMessages: PropTypes.arrayOf(PropTypes.shape({
severity: PropTypes.string.isRequired, severity: PropTypes.string.isRequired,
line: PropTypes.number.isRequired, line: PropTypes.number.isRequired,
@ -351,8 +349,6 @@ Editor.propTypes = {
})), })),
updateLintMessage: PropTypes.func.isRequired, updateLintMessage: PropTypes.func.isRequired,
clearLintMessage: PropTypes.func.isRequired, clearLintMessage: PropTypes.func.isRequired,
indentationAmount: PropTypes.number.isRequired,
isTabIndent: PropTypes.bool.isRequired,
updateFileContent: PropTypes.func.isRequired, updateFileContent: PropTypes.func.isRequired,
fontSize: PropTypes.number.isRequired, fontSize: PropTypes.number.isRequired,
file: PropTypes.shape({ file: PropTypes.shape({

View file

@ -24,13 +24,29 @@ export class FileNode extends React.Component {
this.hideFileOptions = this.hideFileOptions.bind(this); this.hideFileOptions = this.hideFileOptions.bind(this);
this.showEditFileName = this.showEditFileName.bind(this); this.showEditFileName = this.showEditFileName.bind(this);
this.hideEditFileName = this.hideEditFileName.bind(this); this.hideEditFileName = this.hideEditFileName.bind(this);
this.onBlurComponent = this.onBlurComponent.bind(this);
this.onFocusComponent = this.onFocusComponent.bind(this);
this.state = { this.state = {
isOptionsOpen: false, isOptionsOpen: false,
isEditingName: false, isEditingName: false,
isFocused: false,
}; };
} }
onFocusComponent() {
this.setState({ isFocused: true });
}
onBlurComponent() {
this.setState({ isFocused: false });
setTimeout(() => {
if (!this.state.isFocused) {
this.hideFileOptions();
}
}, 200);
}
handleFileClick(e) { handleFileClick(e) {
e.stopPropagation(); e.stopPropagation();
if (this.props.name !== 'root' && !this.isDeleting) { if (this.props.name !== 'root' && !this.isDeleting) {
@ -105,6 +121,7 @@ export class FileNode extends React.Component {
'sidebar__file-item--editing': this.state.isEditingName, 'sidebar__file-item--editing': this.state.isEditingName,
'sidebar__file-item--closed': this.props.isFolderClosed 'sidebar__file-item--closed': this.props.isFolderClosed
}); });
return ( return (
<div className={itemClass}> <div className={itemClass}>
{(() => { // eslint-disable-line {(() => { // eslint-disable-line
@ -156,7 +173,8 @@ export class FileNode extends React.Component {
ref={(element) => { this[`fileOptions-${this.props.id}`] = element; }} ref={(element) => { this[`fileOptions-${this.props.id}`] = element; }}
tabIndex="0" tabIndex="0"
onClick={this.toggleFileOptions} onClick={this.toggleFileOptions}
onBlur={() => setTimeout(this.hideFileOptions, 200)} onBlur={this.onBlurComponent}
onFocus={this.onFocusComponent}
> >
<InlineSVG src={downArrowUrl} /> <InlineSVG src={downArrowUrl} />
</button> </button>
@ -168,7 +186,12 @@ export class FileNode extends React.Component {
<li> <li>
<button <button
aria-label="add file" aria-label="add file"
onClick={this.props.newFile} onClick={() => {
this.props.newFile();
setTimeout(() => this.hideFileOptions(), 0);
}}
onBlur={this.onBlurComponent}
onFocus={this.onFocusComponent}
className="sidebar__file-item-option" className="sidebar__file-item-option"
> >
Add File Add File
@ -183,7 +206,12 @@ export class FileNode extends React.Component {
<li> <li>
<button <button
aria-label="add folder" aria-label="add folder"
onClick={this.props.newFolder} onClick={() => {
this.props.newFolder();
setTimeout(() => this.hideFileOptions(), 0);
}}
onBlur={this.onBlurComponent}
onFocus={this.onFocusComponent}
className="sidebar__file-item-option" className="sidebar__file-item-option"
> >
Add Folder Add Folder
@ -198,7 +226,10 @@ export class FileNode extends React.Component {
this.originalFileName = this.props.name; this.originalFileName = this.props.name;
this.showEditFileName(); this.showEditFileName();
setTimeout(() => this.fileNameInput.focus(), 0); setTimeout(() => this.fileNameInput.focus(), 0);
setTimeout(() => this.hideFileOptions(), 0);
}} }}
onBlur={this.onBlurComponent}
onFocus={this.onFocusComponent}
className="sidebar__file-item-option" className="sidebar__file-item-option"
> >
Rename Rename
@ -213,6 +244,8 @@ export class FileNode extends React.Component {
setTimeout(() => this.props.deleteFile(this.props.id, this.props.parentId), 100); setTimeout(() => this.props.deleteFile(this.props.id, this.props.parentId), 100);
} }
}} }}
onBlur={this.onBlurComponent}
onFocus={this.onFocusComponent}
className="sidebar__file-item-option" className="sidebar__file-item-option"
> >
Delete Delete

View file

@ -15,8 +15,8 @@ class Preferences extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.handleUpdateAutosave = this.handleUpdateAutosave.bind(this); this.handleUpdateAutosave = this.handleUpdateAutosave.bind(this);
this.handleUpdateLinewrap = this.handleUpdateLinewrap.bind(this);
this.handleUpdateFont = this.handleUpdateFont.bind(this); this.handleUpdateFont = this.handleUpdateFont.bind(this);
this.handleUpdateIndentation = this.handleUpdateIndentation.bind(this);
this.handleLintWarning = this.handleLintWarning.bind(this); this.handleLintWarning = this.handleLintWarning.bind(this);
} }
@ -34,25 +34,16 @@ class Preferences extends React.Component {
this.props.setFontSize(value); this.props.setFontSize(value);
} }
handleUpdateIndentation(event) {
let value = parseInt(event.target.value, 10);
if (Number.isNaN(value)) {
value = 2;
}
if (value > 6) {
value = 6;
}
if (value < 0) {
value = 0;
}
this.props.setIndentation(value);
}
handleUpdateAutosave(event) { handleUpdateAutosave(event) {
const value = event.target.value === 'true'; const value = event.target.value === 'true';
this.props.setAutosave(value); this.props.setAutosave(value);
} }
handleUpdateLinewrap(event) {
const value = event.target.value === 'true';
this.props.setLinewrap(value);
}
handleLintWarning(event) { handleLintWarning(event) {
const value = event.target.value === 'true'; const value = event.target.value === 'true';
this.props.setLintWarning(value); this.props.setLintWarning(value);
@ -144,65 +135,6 @@ class Preferences extends React.Component {
<h6 className="preference__label">Increase</h6> <h6 className="preference__label">Increase</h6>
</button> </button>
</div> </div>
<div className="preference">
<h4 className="preference__title">Indentation amount</h4>
<button
className="preference__minus-button"
onClick={() => this.props.setIndentation(this.props.indentationAmount - 2)}
aria-label="decrease indentation amount"
disabled={this.props.indentationAmount <= 0}
>
<InlineSVG src={minusUrl} alt="DecreaseIndentation Amount" />
<h6 className="preference__label">Decrease</h6>
</button>
<input
className="preference__value"
aria-live="polite"
aria-atomic="true"
value={this.props.indentationAmount}
onChange={this.handleUpdateIndentation}
ref={(element) => { this.indentationInput = element; }}
onClick={() => {
this.indentationInput.select();
}}
/>
<button
className="preference__plus-button"
onClick={() => this.props.setIndentation(this.props.indentationAmount + 2)}
aria-label="increase indentation amount"
disabled={this.props.indentationAmount >= 6}
>
<InlineSVG src={plusUrl} alt="IncreaseIndentation Amount" />
<h6 className="preference__label">Increase</h6>
</button>
<input
type="radio"
onChange={this.props.indentWithSpace}
aria-label="indentation with space"
name="indentation"
id="indentation-space"
className="preference__radio-button"
value="Spaces"
checked={!this.props.isTabIndent}
/>
<label
htmlFor="indentation-space"
className="preference__option preference__whitespace-button"
>
Spaces
</label>
<input
type="radio"
onChange={this.props.indentWithTab}
aria-label="indentation with tab"
name="indentation"
id="indentation-tab"
className="preference__radio-button"
value="Tabs"
checked={this.props.isTabIndent}
/>
<label htmlFor="indentation-tab" className="preference__option preference__whitespace-button">Tabs</label>
</div>
<div className="preference"> <div className="preference">
<h4 className="preference__title">Autosave</h4> <h4 className="preference__title">Autosave</h4>
<div className="preference__options"> <div className="preference__options">
@ -230,6 +162,33 @@ class Preferences extends React.Component {
<label htmlFor="autosave-off" className="preference__option">Off</label> <label htmlFor="autosave-off" className="preference__option">Off</label>
</div> </div>
</div> </div>
<div className="preference">
<h4 className="preference__title">Word Wrap</h4>
<div className="preference__options">
<input
type="radio"
onChange={() => this.props.setLinewrap(true)}
aria-label="linewrap on"
name="linewrap"
id="linewrap-on"
className="preference__radio-button"
value="On"
checked={this.props.linewrap}
/>
<label htmlFor="linewrap-on" className="preference__option">On</label>
<input
type="radio"
onChange={() => this.props.setLinewrap(false)}
aria-label="linewrap off"
name="linewrap"
id="linewrap-off"
className="preference__radio-button"
value="Off"
checked={!this.props.linewrap}
/>
<label htmlFor="linewrap-off" className="preference__option">Off</label>
</div>
</div>
</TabPanel> </TabPanel>
<TabPanel> <TabPanel>
<div className="preference"> <div className="preference">
@ -318,14 +277,11 @@ class Preferences extends React.Component {
Preferences.propTypes = { Preferences.propTypes = {
fontSize: PropTypes.number.isRequired, fontSize: PropTypes.number.isRequired,
indentationAmount: PropTypes.number.isRequired,
setIndentation: PropTypes.func.isRequired,
indentWithSpace: PropTypes.func.isRequired,
indentWithTab: PropTypes.func.isRequired,
isTabIndent: PropTypes.bool.isRequired,
setFontSize: PropTypes.func.isRequired, setFontSize: PropTypes.func.isRequired,
autosave: PropTypes.bool.isRequired, autosave: PropTypes.bool.isRequired,
linewrap: PropTypes.bool.isRequired,
setAutosave: PropTypes.func.isRequired, setAutosave: PropTypes.func.isRequired,
setLinewrap: PropTypes.func.isRequired,
textOutput: PropTypes.bool.isRequired, textOutput: PropTypes.bool.isRequired,
gridOutput: PropTypes.bool.isRequired, gridOutput: PropTypes.bool.isRequired,
soundOutput: PropTypes.bool.isRequired, soundOutput: PropTypes.bool.isRequired,

View file

@ -12,6 +12,25 @@ class Sidebar extends React.Component {
super(props); super(props);
this.resetSelectedFile = this.resetSelectedFile.bind(this); this.resetSelectedFile = this.resetSelectedFile.bind(this);
this.toggleProjectOptions = this.toggleProjectOptions.bind(this); this.toggleProjectOptions = this.toggleProjectOptions.bind(this);
this.onBlurComponent = this.onBlurComponent.bind(this);
this.onFocusComponent = this.onFocusComponent.bind(this);
this.state = {
isFocused: false,
};
}
onBlurComponent() {
this.setState({ isFocused: false });
setTimeout(() => {
if (!this.state.isFocused) {
this.props.closeProjectOptions();
}
}, 200);
}
onFocusComponent() {
this.setState({ isFocused: true });
} }
resetSelectedFile() { resetSelectedFile() {
@ -65,18 +84,35 @@ class Sidebar extends React.Component {
tabIndex="0" tabIndex="0"
ref={(element) => { this.sidebarOptions = element; }} ref={(element) => { this.sidebarOptions = element; }}
onClick={this.toggleProjectOptions} onClick={this.toggleProjectOptions}
onBlur={() => setTimeout(this.props.closeProjectOptions, 200)} onBlur={this.onBlurComponent}
onFocus={this.onFocusComponent}
> >
<InlineSVG src={downArrowUrl} /> <InlineSVG src={downArrowUrl} />
</button> </button>
<ul className="sidebar__project-options"> <ul className="sidebar__project-options">
<li> <li>
<button aria-label="add folder" onClick={this.props.newFolder} > <button
aria-label="add folder"
onClick={() => {
this.props.newFolder();
setTimeout(this.props.closeProjectOptions, 0);
}}
onBlur={this.onBlurComponent}
onFocus={this.onFocusComponent}
>
Add folder Add folder
</button> </button>
</li> </li>
<li> <li>
<button aria-label="add file" onClick={this.props.newFile} > <button
aria-label="add file"
onClick={() => {
this.props.newFile();
setTimeout(this.props.closeProjectOptions, 0);
}}
onBlur={this.onBlurComponent}
onFocus={this.onFocusComponent}
>
Add file Add file
</button> </button>
</li> </li>

View file

@ -124,6 +124,7 @@ class Toolbar extends React.Component {
</a> </a>
<input <input
type="text" type="text"
maxLength="256"
className="toolbar__project-name-input" className="toolbar__project-name-input"
value={this.props.project.name} value={this.props.project.name}
onChange={this.handleProjectNameChange} onChange={this.handleProjectNameChange}

View file

@ -202,14 +202,11 @@ class IDEView extends React.Component {
> >
<Preferences <Preferences
fontSize={this.props.preferences.fontSize} fontSize={this.props.preferences.fontSize}
indentationAmount={this.props.preferences.indentationAmount}
setIndentation={this.props.setIndentation}
indentWithSpace={this.props.indentWithSpace}
indentWithTab={this.props.indentWithTab}
isTabIndent={this.props.preferences.isTabIndent}
setFontSize={this.props.setFontSize} setFontSize={this.props.setFontSize}
autosave={this.props.preferences.autosave} autosave={this.props.preferences.autosave}
linewrap={this.props.preferences.linewrap}
setAutosave={this.props.setAutosave} setAutosave={this.props.setAutosave}
setLinewrap={this.props.setLinewrap}
lintWarning={this.props.preferences.lintWarning} lintWarning={this.props.preferences.lintWarning}
setLintWarning={this.props.setLintWarning} setLintWarning={this.props.setLintWarning}
textOutput={this.props.preferences.textOutput} textOutput={this.props.preferences.textOutput}
@ -251,7 +248,9 @@ class IDEView extends React.Component {
defaultSize="50%" defaultSize="50%"
onChange={() => { this.overlay.style.display = 'block'; }} onChange={() => { this.overlay.style.display = 'block'; }}
onDragFinished={() => { this.overlay.style.display = 'none'; }} onDragFinished={() => { this.overlay.style.display = 'none'; }}
resizerStyle={{ marginRight: '0', marginLeft: '-10px' }} resizerStyle={{
borderLeftWidth: '2px', borderRightWidth: '2px', width: '2px', margin: '0px 0px'
}}
> >
<SplitPane <SplitPane
split="horizontal" split="horizontal"
@ -264,14 +263,13 @@ class IDEView extends React.Component {
> >
<Editor <Editor
lintWarning={this.props.preferences.lintWarning} lintWarning={this.props.preferences.lintWarning}
linewrap={this.props.preferences.linewrap}
lintMessages={this.props.editorAccessibility.lintMessages} lintMessages={this.props.editorAccessibility.lintMessages}
updateLintMessage={this.props.updateLintMessage} updateLintMessage={this.props.updateLintMessage}
clearLintMessage={this.props.clearLintMessage} clearLintMessage={this.props.clearLintMessage}
file={this.props.selectedFile} file={this.props.selectedFile}
updateFileContent={this.props.updateFileContent} updateFileContent={this.props.updateFileContent}
fontSize={this.props.preferences.fontSize} fontSize={this.props.preferences.fontSize}
indentationAmount={this.props.preferences.indentationAmount}
isTabIndent={this.props.preferences.isTabIndent}
files={this.props.files} files={this.props.files}
editorOptionsVisible={this.props.ide.editorOptionsVisible} editorOptionsVisible={this.props.ide.editorOptionsVisible}
showEditorOptions={this.props.showEditorOptions} showEditorOptions={this.props.showEditorOptions}
@ -511,9 +509,8 @@ IDEView.propTypes = {
clearLintMessage: PropTypes.func.isRequired, clearLintMessage: PropTypes.func.isRequired,
preferences: PropTypes.shape({ preferences: PropTypes.shape({
fontSize: PropTypes.number.isRequired, fontSize: PropTypes.number.isRequired,
indentationAmount: PropTypes.number.isRequired,
isTabIndent: PropTypes.bool.isRequired,
autosave: PropTypes.bool.isRequired, autosave: PropTypes.bool.isRequired,
linewrap: PropTypes.bool.isRequired,
lintWarning: PropTypes.bool.isRequired, lintWarning: PropTypes.bool.isRequired,
textOutput: PropTypes.bool.isRequired, textOutput: PropTypes.bool.isRequired,
gridOutput: PropTypes.bool.isRequired, gridOutput: PropTypes.bool.isRequired,
@ -523,10 +520,8 @@ IDEView.propTypes = {
}).isRequired, }).isRequired,
closePreferences: PropTypes.func.isRequired, closePreferences: PropTypes.func.isRequired,
setFontSize: PropTypes.func.isRequired, setFontSize: PropTypes.func.isRequired,
setIndentation: PropTypes.func.isRequired,
indentWithTab: PropTypes.func.isRequired,
indentWithSpace: PropTypes.func.isRequired,
setAutosave: PropTypes.func.isRequired, setAutosave: PropTypes.func.isRequired,
setLinewrap: PropTypes.func.isRequired,
setLintWarning: PropTypes.func.isRequired, setLintWarning: PropTypes.func.isRequired,
setTextOutput: PropTypes.func.isRequired, setTextOutput: PropTypes.func.isRequired,
setGridOutput: PropTypes.func.isRequired, setGridOutput: PropTypes.func.isRequired,

View file

@ -2,9 +2,8 @@ import * as ActionTypes from '../../../constants';
const initialState = { const initialState = {
fontSize: 18, fontSize: 18,
indentationAmount: 2,
isTabIndent: true,
autosave: true, autosave: true,
linewrap: true,
lintWarning: false, lintWarning: false,
textOutput: false, textOutput: false,
gridOutput: false, gridOutput: false,
@ -17,18 +16,10 @@ const preferences = (state = initialState, action) => {
switch (action.type) { switch (action.type) {
case ActionTypes.SET_FONT_SIZE: case ActionTypes.SET_FONT_SIZE:
return Object.assign({}, state, { fontSize: action.value }); return Object.assign({}, state, { fontSize: action.value });
case ActionTypes.SET_INDENTATION:
return Object.assign({}, state, { indentationAmount: action.value });
case ActionTypes.INDENT_WITH_TAB:
return Object.assign({}, state, {
isTabIndent: true
});
case ActionTypes.INDENT_WITH_SPACE:
return Object.assign({}, state, {
isTabIndent: false
});
case ActionTypes.SET_AUTOSAVE: case ActionTypes.SET_AUTOSAVE:
return Object.assign({}, state, { autosave: action.value }); return Object.assign({}, state, { autosave: action.value });
case ActionTypes.SET_LINEWRAP:
return Object.assign({}, state, { linewrap: action.value });
case ActionTypes.SET_LINT_WARNING: case ActionTypes.SET_LINT_WARNING:
return Object.assign({}, state, { lintWarning: action.value }); return Object.assign({}, state, { lintWarning: action.value });
case ActionTypes.SET_TEXT_OUTPUT: case ActionTypes.SET_TEXT_OUTPUT:

View file

@ -17,6 +17,7 @@ $themes: (
light: ( light: (
logo-color: $p5js-pink, logo-color: $p5js-pink,
primary-text-color: #333, primary-text-color: #333,
dropzone-text-color: #333,
modal-button-color: #333, modal-button-color: #333,
heading-text-color: #333, heading-text-color: #333,
secondary-text-color: #a8a8a8, secondary-text-color: #a8a8a8,
@ -36,7 +37,7 @@ $themes: (
modal-background-color: #f4f4f4, modal-background-color: #f4f4f4,
modal-button-background-color: #e6e6e6, modal-button-background-color: #e6e6e6,
modal-border-color: rgba(17, 17, 17, 0.3), modal-border-color: rgba(17, 17, 17, 0.3),
modal-boder-selected-color: #B9D0E1, modal-border-selected-color: #B9D0E1,
icon-color: $icon-color, icon-color: $icon-color,
icon-hover-color: $icon-hover-color, icon-hover-color: $icon-hover-color,
icon-toast-hover-color: $white, icon-toast-hover-color: $white,
@ -65,6 +66,7 @@ $themes: (
dark: ( dark: (
logo-color: $p5js-pink, logo-color: $p5js-pink,
primary-text-color: $white, primary-text-color: $white,
dropzone-text-color: $black,
modal-button-color: $white, modal-button-color: $white,
heading-text-color: $white, heading-text-color: $white,
secondary-text-color: #DADADA, secondary-text-color: #DADADA,
@ -112,6 +114,7 @@ $themes: (
contrast: ( contrast: (
logo-color: $yellow, logo-color: $yellow,
primary-text-color: $yellow, primary-text-color: $yellow,
dropzone-text-color: $black,
modal-button-color: #333, modal-button-color: #333,
heading-text-color: #e1e1e1, heading-text-color: #e1e1e1,
secondary-text-color: #e1e1e1, secondary-text-color: #e1e1e1,

View file

@ -51,7 +51,7 @@
} }
.tooltipped-n::before, .tooltipped-n::before,
.tooltipped::before, .tooltipped::before
{ {
@include themify() { @include themify() {
color: getThemifyVariable('button-background-hover-color'); color: getThemifyVariable('button-background-hover-color');

View file

@ -3,15 +3,18 @@
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: space-between; justify-content: space-between;
@include themify() { @include themify() {
border-bottom: 1px dashed map-get($theme-map, 'inactive-text-color'); border-bottom: 1px dashed map-get($theme-map, 'inactive-text-color');
} }
& button { & button {
padding: 0; padding: 0;
} }
} }
.nav__items-left, .nav__items-right { .nav__items-left,
.nav__items-right {
list-style: none; list-style: none;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
@ -30,11 +33,14 @@
.nav__item { .nav__item {
position: relative; position: relative;
padding: 0 #{10 / $base-font-size}rem;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
height: 100%; height: 100%;
& button {
padding: #{12 / $base-font-size}rem #{10 / $base-font-size}rem;
}
} }
.nav__item:first-child { .nav__item:first-child {
@ -55,6 +61,7 @@
color: getThemifyVariable('nav-hover-color'); color: getThemifyVariable('nav-hover-color');
} }
} }
.nav__item-header-triangle polygon { .nav__item-header-triangle polygon {
@include themify() { @include themify() {
fill: getThemifyVariable('nav-hover-color'); fill: getThemifyVariable('nav-hover-color');
@ -69,23 +76,28 @@
box-shadow: 0 0 18px 0 getThemifyVariable('shadow-color'); box-shadow: 0 0 18px 0 getThemifyVariable('shadow-color');
color: getThemifyVariable('dropdown-color'); color: getThemifyVariable('dropdown-color');
} }
display: none; display: none;
text-align: left; text-align: left;
width: #{180 / $base-font-size}rem; width: #{180 / $base-font-size}rem;
.nav__item--open & { .nav__item--open & {
display: flex; display: flex;
position: absolute; position: absolute;
flex-direction: column; flex-direction: column;
top: 4px; top: #{40 / $base-font-size}rem;
left: 0; left: 0;
height: auto; height: auto;
} }
z-index: 9999; z-index: 9999;
border-radius: #{6 / $base-font-size}rem; border-radius: #{6 / $base-font-size}rem;
border-top-left-radius: 0px;
} }
.nav__items-right { .nav__items-right {
padding-right: #{20 / $base-font-size}rem; padding-right: #{20 / $base-font-size}rem;
& .nav__dropdown { & .nav__dropdown {
width: #{121 / $base-font-size}rem; width: #{121 / $base-font-size}rem;
} }
@ -98,7 +110,8 @@
} }
} }
.nav__dropdown a, button { .nav__dropdown a,
button {
@include themify() { @include themify() {
color: getThemifyVariable('secondary-text-color'); color: getThemifyVariable('secondary-text-color');
} }
@ -118,18 +131,21 @@
@include themify() { @include themify() {
border-bottom: 1px dashed map-get($theme-map, 'inactive-text-color'); border-bottom: 1px dashed map-get($theme-map, 'inactive-text-color');
} }
height: #{(42 - 5) / $base-font-size}rem; height: #{(42 - 5) / $base-font-size}rem;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
margin: 0 #{16 / $base-font-size}rem; margin: 0 #{16 / $base-font-size}rem;
cursor: pointer; cursor: pointer;
&:hover { &:hover {
span { span {
@include themify() { @include themify() {
color: getThemifyVariable('nav-hover-color'); color: getThemifyVariable('nav-hover-color');
} }
} }
polygon { polygon {
@include themify() { @include themify() {
fill: getThemifyVariable('nav-hover-color'); fill: getThemifyVariable('nav-hover-color');
@ -138,10 +154,12 @@
} }
} }
.nav__dropdown-heading a, .nav__dropdown-heading a:hover { .nav__dropdown-heading a,
.nav__dropdown-heading a:hover {
@include themify() { @include themify() {
color: getThemifyVariable('inactive-text-color'); color: getThemifyVariable('inactive-text-color');
} }
cursor: default; cursor: default;
width: 100%; width: 100%;
} }
@ -158,24 +176,30 @@
align-items: center; align-items: center;
padding: 0 #{16 / $base-font-size}rem; padding: 0 #{16 / $base-font-size}rem;
cursor: pointer; cursor: pointer;
& button, & a {
& button,
& a {
@include themify() { @include themify() {
color: getThemifyVariable('dropdown-color'); color: getThemifyVariable('dropdown-color');
} }
} }
&:hover { &:hover {
@include themify() { @include themify() {
background-color: getThemifyVariable('button-background-hover-color'); background-color: getThemifyVariable('button-background-hover-color');
color: getThemifyVariable('button-hover-color') color: getThemifyVariable('button-hover-color')
} }
& button, & a {
& button,
& a {
@include themify() { @include themify() {
color: getThemifyVariable('button-hover-color'); color: getThemifyVariable('button-hover-color');
} }
} }
} }
& button, & a { & button,
& a {
width: 100%; width: 100%;
height: 100%; height: 100%;
display: flex; display: flex;
@ -185,7 +209,10 @@
} }
.nav__dropdown-item:last-child { .nav__dropdown-item:last-child {
border-radius: 0 0 #{6 / $base-font-size}rem #{6 / $base-font-size}rem; border-radius: 0 0 #{5 / $base-font-size}rem #{5 / $base-font-size}rem;
}
.nav__dropdown-item:first-child {
border-radius: 0 #{5 / $base-font-size}rem 0 0;
} }
.nav__announce { .nav__announce {
@ -207,17 +234,28 @@
position: relative; position: relative;
height: #{42 / $base-font-size}rem; height: #{42 / $base-font-size}rem;
width: #{56 / $base-font-size}rem; width: #{56 / $base-font-size}rem;
& span { & span {
position: absolute; position: absolute;
} }
} }
.svg__logo g > path {
@include themify() {
fill: getThemifyVariable('logo-color');
}
}
.svg__logo g g:first-of-type path {
fill: none;
}
.nav__keyboard-shortcut { .nav__keyboard-shortcut {
font-size: #{12 / $base-font-size}rem; font-size: #{12 / $base-font-size}rem;
font-family: Inconsololata, monospace; font-family: Inconsololata, monospace;
@include themify() { @include themify() {
color: getThemifyVariable('keyboard-shortcut-color'); color: getThemifyVariable('keyboard-shortcut-color');
} }
.nav__dropdown-item:hover & { .nav__dropdown-item:hover & {
@include themify() { @include themify() {
color: getThemifyVariable('button-hover-color'); color: getThemifyVariable('button-hover-color');

View file

@ -1,5 +1,7 @@
.dropzone { .dropzone {
color: $primary-text-color; @include themify() {
color: getThemifyVariable('dropzone-text-color');
}
} }
.uploader { .uploader {

View file

@ -41,6 +41,7 @@
@import 'components/keyboard-shortcuts'; @import 'components/keyboard-shortcuts';
@import 'components/copyable-input'; @import 'components/copyable-input';
@import 'components/feedback'; @import 'components/feedback';
@import 'components/uploader';
@import 'layout/ide'; @import 'layout/ide';
@import 'layout/fullscreen'; @import 'layout/fullscreen';

View file

@ -73,8 +73,8 @@ export function validateSignup(formProps) {
errors.confirmPassword = 'Please enter a password confirmation'; errors.confirmPassword = 'Please enter a password confirmation';
} }
if (formProps.password !== formProps.confirmPassword) { if (formProps.password !== formProps.confirmPassword && formProps.confirmPassword) {
errors.password = 'Passwords must match'; errors.confirmPassword = 'Passwords must match';
} }
return errors; return errors;

View file

@ -29,6 +29,8 @@ These are the steps that happen when you deploy the application.
You'll only need to do this if you're testing the production environment locally. You'll only need to do this if you're testing the production environment locally.
_Note_: The installation steps assume you are using a Unix-like shell. If you are using Windows, you will need to use `copy` instead of `cp`.
1. Clone this repository and `cd` into it 1. Clone this repository and `cd` into it
2. `$ npm install` 2. `$ npm install`
3. Install MongoDB and make sure it is running 3. Install MongoDB and make sure it is running

View file

@ -4,6 +4,8 @@ Follow these instructions to set up your development environment, which you need
## Manual Installation ## Manual Installation
_Note_: The installation steps assume you are using a Unix-like shell. If you are using Windows, you will need to use `copy` instead of `cp`.
1. Install [node.js](http://nodejs.org/), which also automatically installs the [npm](https://www.npmjs.org) package manager. 1. Install [node.js](http://nodejs.org/), which also automatically installs the [npm](https://www.npmjs.org) package manager.
2. [Fork](https://help.github.com/articles/fork-a-repo) the [p5.js Web Editor repository](https://github.com/processing/p5.js-web-editor) into your own GitHub account. 2. [Fork](https://help.github.com/articles/fork-a-repo) the [p5.js Web Editor repository](https://github.com/processing/p5.js-web-editor) into your own GitHub account.
3. [Clone](https://help.github.com/articles/cloning-a-repository/) your new fork of the repository from GitHub onto your local computer. 3. [Clone](https://help.github.com/articles/cloning-a-repository/) your new fork of the repository from GitHub onto your local computer.
@ -31,6 +33,8 @@ Follow these instructions to set up your development environment, which you need
## Docker Installation ## Docker Installation
_Note_: The installation steps assume you are using a Unix-like shell. If you are using Windows, you will need to use `copy` instead of `cp`.
Using Docker, you can have a complete, consistent development environment without having to manually install dependencies such as Node, Mongo, etc. It also helps isolate these dependencies and their data from other projects that you may have on the same computer that use different/conflicting versions, etc. Using Docker, you can have a complete, consistent development environment without having to manually install dependencies such as Node, Mongo, etc. It also helps isolate these dependencies and their data from other projects that you may have on the same computer that use different/conflicting versions, etc.
Note that this takes up a significant amount of space on your machine. Make sure you have at least 5GB free. Note that this takes up a significant amount of space on your machine. Make sure you have at least 5GB free.
@ -59,7 +63,7 @@ If you don't have the full server environment running, you can launch a one-off
## S3 Bucket Configuration ## S3 Bucket Configuration
Note that this is optional, unless you are working on the part of the application that allows a user to upload images, videos, etc. Please refer to the folllowing [gist](https://gist.github.com/catarak/70c9301f0fd1ac2d6b58de03f61997e3) to set up an S3 bucket to be used with this project. Note that this is optional, unless you are working on the part of the application that allows a user to upload images, videos, etc. Please refer to the following [gist](https://gist.github.com/catarak/70c9301f0fd1ac2d6b58de03f61997e3) to set up an S3 bucket to be used with this project.
If your S3 bucket is in the US East (N Virginia) region (us-east-1), you'll If your S3 bucket is in the US East (N Virginia) region (us-east-1), you'll
need to set a custom URL base for it, because it does not follow the standard need to set a custom URL base for it, because it does not follow the standard

View file

@ -1,6 +1,6 @@
# Preparing a pull request # Preparing a pull request
Copied from the [p5.js repository](https://github.com/processing/p5.js). Copied and updated from the [p5.js repository](https://github.com/processing/p5.js).
Pull-requests are easier when your code is up to date! You can use git rebase to update your code to incorporate changes from other contributors. Here's how. Pull-requests are easier when your code is up to date! You can use git rebase to update your code to incorporate changes from other contributors. Here's how.
@ -31,17 +31,19 @@ Then ask git about the latest changes.
### Apply changes from master branch, adds your changes *after* ### Apply changes from master branch, adds your changes *after*
git rebase upstream/master git rebase upstream/master
### Switches back to master branch
git checkout master
### Helps other contributors fully understand the changes that you made
git commit -m "Fixed documentation typos"
### Verifies what git will be committing
git status
## CONFLICTS ## CONFLICTS
You will probably have some conflicts! You may have some conflicts! It's okay. Feel free to ask for help. If merging with the latest upstream master causes conflicts, you can always make a pull request with the upstream repository, which makes the merge conflicts public.
If its just lib/p5.js and lib/p5.min.js, its easy to fix. just build the project again with grunt.
grunt
git add -u
git rebase --continue
If you have conflicts in other files & you're not sure how to resolve them... ask for help! Lauren, David, Kevin, and Kate are familiar with recent changes and can help you figure out what's new.
## And finally, for great glory ## And finally, for great glory
git push origin git push --set-upstream origin your-branch-name-backup
Here's a good reference on rebasing, in case you're intensely curious about the technical details. https://www.atlassian.com/git/tutorials/merging-vs-rebasing Here's a good reference on rebasing, in case you're intensely curious about the technical details. https://www.atlassian.com/git/tutorials/merging-vs-rebasing

View file

@ -110,7 +110,9 @@ export function getProject(req, res) {
function deleteFilesFromS3(files) { function deleteFilesFromS3(files) {
deleteObjectsFromS3(files.filter((file) => { deleteObjectsFromS3(files.filter((file) => {
if (file.url) { if (file.url) {
if (!process.env.S3_DATE || (process.env.S3_DATE && isBefore(new Date(process.env.S3_DATE), new Date(file.createdAt)))) { if (!process.env.S3_DATE || (
process.env.S3_DATE &&
isBefore(new Date(process.env.S3_DATE), new Date(file.createdAt)))) {
return true; return true;
} }
} }

View file

@ -178,7 +178,7 @@ objectsResponse.on('end', () => {
// // console.log('is selected remains'); // // console.log('is selected remains');
// // } // // }
// // if (file.isSelctedFile) { // // if (file.isSelectedFile) {
// // console.log('changed to isSelected file'); // // console.log('changed to isSelected file');
// // } // // }
// project.save((err, savedProject) => { // project.save((err, savedProject) => {

View file

@ -27,6 +27,7 @@ const userSchema = new Schema({
indentationAmount: { type: Number, default: 2 }, indentationAmount: { type: Number, default: 2 },
isTabIndent: { type: Boolean, default: false }, isTabIndent: { type: Boolean, default: false },
autosave: { type: Boolean, default: true }, autosave: { type: Boolean, default: true },
linewrap: { type: Boolean, default: true },
lintWarning: { type: Boolean, default: false }, lintWarning: { type: Boolean, default: false },
textOutput: { type: Boolean, default: false }, textOutput: { type: Boolean, default: false },
gridOutput: { type: Boolean, default: false }, gridOutput: { type: Boolean, default: false },