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.toggleDropdownForAccount = this.toggleDropdown.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('keydown', this.closeDropDown, false);
}
componentWillUnmount() {
document.removeEventListener('mousedown', this.handleClick, false);
document.removeEventListener('keydown', this.closeDropDown, false);
}
setDropdown(dropdown) {
@ -71,8 +73,17 @@ class Nav extends React.PureComponent {
});
}
closeDropDown(e) {
if (e.keyCode === 27) {
this.setDropdown('none');
}
}
handleClick(e) {
if (this.node.contains(e.target)) {
if (!this.node) {
return;
}
if (this.node && this.node.contains(e.target)) {
return;
}
@ -233,25 +244,23 @@ class Nav extends React.PureComponent {
<nav className="nav" title="main-navigation" ref={(node) => { this.node = node; }}>
<ul className="nav__items-left" title="project-menu">
<li className="nav__item-logo">
<InlineSVG src={logoUrl} alt="p5.js logo" />
<InlineSVG src={logoUrl} alt="p5.js logo" className="svg__logo" />
</li>
<li className={navDropdownState.file}>
<button
onClick={this.toggleDropdownForFile}
onBlur={this.handleBlur}
onFocus={this.clearHideTimeout}
onMouseOver={() => {
if (this.state.dropdownOpen !== 'none') {
this.setDropdown('file');
}
}}
>
<span className="nav__item-header">File</span>
<InlineSVG className="nav__item-header-triangle" src={triangleUrl} />
</button>
<ul className="nav__dropdown">
<button
onClick={this.toggleDropdownForFile}
className="nav__dropdown-heading"
>
<span>File</span>
<InlineSVG src={triangleUrl} />
</button>
<li className="nav__dropdown-item">
<button
onClick={this.handleNew}
@ -331,18 +340,16 @@ class Nav extends React.PureComponent {
onClick={this.toggleDropdownForEdit}
onBlur={this.handleBlur}
onFocus={this.clearHideTimeout}
onMouseOver={() => {
if (this.state.dropdownOpen !== 'none') {
this.setDropdown('edit');
}
}}
>
<span className="nav__item-header">Edit</span>
<InlineSVG className="nav__item-header-triangle" src={triangleUrl} />
</button>
<ul className="nav__dropdown">
<button
onClick={this.toggleDropdownForEdit}
className="nav__dropdown-heading"
>
<span>Edit</span>
<InlineSVG src={triangleUrl} />
</button>
<ul className="nav__dropdown" >
<li className="nav__dropdown-item">
<button
onClick={() => {
@ -393,18 +400,16 @@ class Nav extends React.PureComponent {
onClick={this.toggleDropdownForSketch}
onBlur={this.handleBlur}
onFocus={this.clearHideTimeout}
onMouseOver={() => {
if (this.state.dropdownOpen !== 'none') {
this.setDropdown('sketch');
}
}}
>
<span className="nav__item-header">Sketch</span>
<InlineSVG className="nav__item-header-triangle" src={triangleUrl} />
</button>
<ul className="nav__dropdown">
<button
onClick={this.toggleDropdownForSketch}
className="nav__dropdown-heading"
>
<span>Sketch</span>
<InlineSVG src={triangleUrl} />
</button>
<li className="nav__dropdown-item">
<button
onClick={this.handleAddFile}
@ -470,18 +475,16 @@ class Nav extends React.PureComponent {
onClick={this.toggleDropdownForHelp}
onBlur={this.handleBlur}
onFocus={this.clearHideTimeout}
onMouseOver={() => {
if (this.state.dropdownOpen !== 'none') {
this.setDropdown('help');
}
}}
>
<span className="nav__item-header">Help & Feedback</span>
<InlineSVG className="nav__item-header-triangle" src={triangleUrl} />
</button>
<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">
<button
onFocus={this.handleFocusForHelp}
@ -547,18 +550,16 @@ class Nav extends React.PureComponent {
onClick={this.toggleDropdownForAccount}
onBlur={this.handleBlur}
onFocus={this.clearHideTimeout}
onMouseOver={() => {
if (this.state.dropdownOpen !== 'none') {
this.setDropdown('account');
}
}}
>
My Account
<InlineSVG className="nav__item-header-triangle" src={triangleUrl} />
</button>
<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>
<li className="nav__dropdown-item">
<Link
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
export const UPDATE_FILE_CONTENT = 'UPDATE_FILE_CONTENT';
export const TOGGLE_SKETCH = 'TOGGLE_SKETCH';
@ -13,14 +13,6 @@ export const OPEN_PREFERENCES = 'OPEN_PREFERENCES';
export const CLOSE_PREFERENCES = 'CLOSE_PREFERENCES';
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 UNAUTH_USER = 'UNAUTH_USER';
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 SET_AUTOSAVE = 'SET_AUTOSAVE';
export const SET_LINEWRAP = 'SET_LINEWRAP';
export const SET_LINT_WARNING = 'SET_LINT_WARNING';
export const SET_PREFERENCES = 'SET_PREFERENCES';
export const SET_TEXT_OUTPUT = 'SET_TEXT_OUTPUT';

View file

@ -14,6 +14,7 @@ class App extends React.Component {
componentDidMount() {
this.setState({ isMounted: true }); // eslint-disable-line react/no-did-mount-set-state
document.body.className = 'light';
}
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) {
return (dispatch, getState) => {
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) {
return (dispatch, getState) => {
dispatch({

View file

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

View file

@ -24,13 +24,29 @@ export class FileNode extends React.Component {
this.hideFileOptions = this.hideFileOptions.bind(this);
this.showEditFileName = this.showEditFileName.bind(this);
this.hideEditFileName = this.hideEditFileName.bind(this);
this.onBlurComponent = this.onBlurComponent.bind(this);
this.onFocusComponent = this.onFocusComponent.bind(this);
this.state = {
isOptionsOpen: 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) {
e.stopPropagation();
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--closed': this.props.isFolderClosed
});
return (
<div className={itemClass}>
{(() => { // eslint-disable-line
@ -156,7 +173,8 @@ export class FileNode extends React.Component {
ref={(element) => { this[`fileOptions-${this.props.id}`] = element; }}
tabIndex="0"
onClick={this.toggleFileOptions}
onBlur={() => setTimeout(this.hideFileOptions, 200)}
onBlur={this.onBlurComponent}
onFocus={this.onFocusComponent}
>
<InlineSVG src={downArrowUrl} />
</button>
@ -168,7 +186,12 @@ export class FileNode extends React.Component {
<li>
<button
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"
>
Add File
@ -183,7 +206,12 @@ export class FileNode extends React.Component {
<li>
<button
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"
>
Add Folder
@ -198,7 +226,10 @@ export class FileNode extends React.Component {
this.originalFileName = this.props.name;
this.showEditFileName();
setTimeout(() => this.fileNameInput.focus(), 0);
setTimeout(() => this.hideFileOptions(), 0);
}}
onBlur={this.onBlurComponent}
onFocus={this.onFocusComponent}
className="sidebar__file-item-option"
>
Rename
@ -213,6 +244,8 @@ export class FileNode extends React.Component {
setTimeout(() => this.props.deleteFile(this.props.id, this.props.parentId), 100);
}
}}
onBlur={this.onBlurComponent}
onFocus={this.onFocusComponent}
className="sidebar__file-item-option"
>
Delete

View file

@ -15,8 +15,8 @@ class Preferences extends React.Component {
constructor(props) {
super(props);
this.handleUpdateAutosave = this.handleUpdateAutosave.bind(this);
this.handleUpdateLinewrap = this.handleUpdateLinewrap.bind(this);
this.handleUpdateFont = this.handleUpdateFont.bind(this);
this.handleUpdateIndentation = this.handleUpdateIndentation.bind(this);
this.handleLintWarning = this.handleLintWarning.bind(this);
}
@ -34,25 +34,16 @@ class Preferences extends React.Component {
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) {
const value = event.target.value === 'true';
this.props.setAutosave(value);
}
handleUpdateLinewrap(event) {
const value = event.target.value === 'true';
this.props.setLinewrap(value);
}
handleLintWarning(event) {
const value = event.target.value === 'true';
this.props.setLintWarning(value);
@ -144,65 +135,6 @@ class Preferences extends React.Component {
<h6 className="preference__label">Increase</h6>
</button>
</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">
<h4 className="preference__title">Autosave</h4>
<div className="preference__options">
@ -230,6 +162,33 @@ class Preferences extends React.Component {
<label htmlFor="autosave-off" className="preference__option">Off</label>
</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>
<div className="preference">
@ -318,14 +277,11 @@ class Preferences extends React.Component {
Preferences.propTypes = {
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,
autosave: PropTypes.bool.isRequired,
linewrap: PropTypes.bool.isRequired,
setAutosave: PropTypes.func.isRequired,
setLinewrap: PropTypes.func.isRequired,
textOutput: PropTypes.bool.isRequired,
gridOutput: PropTypes.bool.isRequired,
soundOutput: PropTypes.bool.isRequired,

View file

@ -12,6 +12,25 @@ class Sidebar extends React.Component {
super(props);
this.resetSelectedFile = this.resetSelectedFile.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() {
@ -65,18 +84,35 @@ class Sidebar extends React.Component {
tabIndex="0"
ref={(element) => { this.sidebarOptions = element; }}
onClick={this.toggleProjectOptions}
onBlur={() => setTimeout(this.props.closeProjectOptions, 200)}
onBlur={this.onBlurComponent}
onFocus={this.onFocusComponent}
>
<InlineSVG src={downArrowUrl} />
</button>
<ul className="sidebar__project-options">
<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
</button>
</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
</button>
</li>

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,21 +1,24 @@
.nav {
height: #{42 / $base-font-size}rem;
display: flex;
flex-direction: row;
justify-content: space-between;
@include themify() {
border-bottom: 1px dashed map-get($theme-map, 'inactive-text-color');
}
display: flex;
flex-direction: row;
justify-content: space-between;
@include themify() {
border-bottom: 1px dashed map-get($theme-map, 'inactive-text-color');
}
& button {
padding: 0;
}
}
.nav__items-left, .nav__items-right {
list-style: none;
display: flex;
flex-direction: row;
justify-content: flex-end;
.nav__items-left,
.nav__items-right {
list-style: none;
display: flex;
flex-direction: row;
justify-content: flex-end;
height: 100%;
align-items: center;
}
@ -29,20 +32,23 @@
}
.nav__item {
position: relative;
padding: 0 #{10 / $base-font-size}rem;
position: relative;
display: flex;
align-items: center;
justify-content: center;
height: 100%;
& button {
padding: #{12 / $base-font-size}rem #{10 / $base-font-size}rem;
}
}
.nav__item:first-child {
padding-left: #{15 / $base-font-size}rem;
padding-left: #{15 / $base-font-size}rem;
}
.nav__item:last-child {
padding-right: #{15 / $base-font-size}rem;
padding-right: #{15 / $base-font-size}rem;
}
.nav__item-header {
@ -55,50 +61,57 @@
color: getThemifyVariable('nav-hover-color');
}
}
.nav__item-header-triangle polygon {
@include themify() {
@include themify() {
fill: getThemifyVariable('nav-hover-color');
}
}
}
.nav__dropdown {
@include themify() {
background-color: map-get($theme-map, 'modal-background-color');
border: 1px solid map-get($theme-map, 'modal-border-color');
box-shadow: 0 0 18px 0 getThemifyVariable('shadow-color');
@include themify() {
background-color: map-get($theme-map, 'modal-background-color');
border: 1px solid map-get($theme-map, 'modal-border-color');
box-shadow: 0 0 18px 0 getThemifyVariable('shadow-color');
color: getThemifyVariable('dropdown-color');
}
}
display: none;
text-align: left;
width: #{180 / $base-font-size}rem;
.nav__item--open & {
display: flex;
position: absolute;
flex-direction: column;
top: 4px;
left: 0;
height: auto;
}
text-align: left;
width: #{180 / $base-font-size}rem;
.nav__item--open & {
display: flex;
position: absolute;
flex-direction: column;
top: #{40 / $base-font-size}rem;
left: 0;
height: auto;
}
z-index: 9999;
border-radius: #{6 / $base-font-size}rem;
border-top-left-radius: 0px;
}
.nav__items-right {
padding-right: #{20 / $base-font-size}rem;
& .nav__dropdown {
width: #{121 / $base-font-size}rem;
}
}
.nav__item-spacer {
@include themify() {
color: map-get($theme-map, 'inactive-text-color');
@include themify() {
color: map-get($theme-map, 'inactive-text-color');
margin: 0 #{8 / $base-font-size}rem;
}
}
}
.nav__dropdown a, button {
.nav__dropdown a,
button {
@include themify() {
color: getThemifyVariable('secondary-text-color');
}
@ -116,32 +129,37 @@
.nav__dropdown-heading {
@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;
display: flex;
align-items: center;
justify-content: space-between;
margin: 0 #{16 / $base-font-size}rem;
cursor: pointer;
&:hover {
span {
@include themify() {
color: getThemifyVariable('nav-hover-color');
}
}
polygon {
@include themify() {
@include themify() {
fill: getThemifyVariable('nav-hover-color');
}
}
}
}
.nav__dropdown-heading a, .nav__dropdown-heading a:hover {
.nav__dropdown-heading a,
.nav__dropdown-heading a:hover {
@include themify() {
color: getThemifyVariable('inactive-text-color');
}
cursor: default;
width: 100%;
}
@ -158,24 +176,30 @@
align-items: center;
padding: 0 #{16 / $base-font-size}rem;
cursor: pointer;
& button, & a {
& button,
& a {
@include themify() {
color: getThemifyVariable('dropdown-color');
}
}
&:hover {
@include themify() {
background-color: getThemifyVariable('button-background-hover-color');
color: getThemifyVariable('button-hover-color')
}
& button, & a {
& button,
& a {
@include themify() {
color: getThemifyVariable('button-hover-color');
}
}
}
& button, & a {
& button,
& a {
width: 100%;
height: 100%;
display: flex;
@ -185,7 +209,10 @@
}
.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 {
@ -207,17 +234,28 @@
position: relative;
height: #{42 / $base-font-size}rem;
width: #{56 / $base-font-size}rem;
& span {
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 {
font-size: #{12 / $base-font-size}rem;
font-family: Inconsololata, monospace;
@include themify() {
color: getThemifyVariable('keyboard-shortcut-color');
}
.nav__dropdown-item:hover & {
@include themify() {
color: getThemifyVariable('button-hover-color');

View file

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

View file

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

View file

@ -73,8 +73,8 @@ export function validateSignup(formProps) {
errors.confirmPassword = 'Please enter a password confirmation';
}
if (formProps.password !== formProps.confirmPassword) {
errors.password = 'Passwords must match';
if (formProps.password !== formProps.confirmPassword && formProps.confirmPassword) {
errors.confirmPassword = 'Passwords must match';
}
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.
_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
2. `$ npm install`
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
_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.
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.
@ -31,6 +33,8 @@ Follow these instructions to set up your development environment, which you need
## 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.
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
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
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
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.
@ -31,17 +31,19 @@ Then ask git about the latest changes.
### Apply changes from master branch, adds your changes *after*
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
You will probably have some conflicts!
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.
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.
## 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

View file

@ -110,7 +110,9 @@ export function getProject(req, res) {
function deleteFilesFromS3(files) {
deleteObjectsFromS3(files.filter((file) => {
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;
}
}

View file

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

View file

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