diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..82c461c7 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +custom: https://processingfoundation.org/support diff --git a/client/components/Nav.jsx b/client/components/Nav.jsx index 3886ccc8..4a1212c9 100644 --- a/client/components/Nav.jsx +++ b/client/components/Nav.jsx @@ -2,7 +2,7 @@ import PropTypes from 'prop-types'; import React from 'react'; import { connect } from 'react-redux'; import { withRouter } from 'react-router'; -import { Link, browserHistory } from 'react-router'; +import { Link } from 'react-router'; import InlineSVG from 'react-inlinesvg'; import classNames from 'classnames'; import * as IDEActions from '../modules/IDE/actions/ide'; @@ -167,8 +167,6 @@ class Nav extends React.PureComponent { handleLogout() { this.props.logoutUser(); - // if you're on the settings page, probably. - browserHistory.push('/'); this.setDropdown('none'); } @@ -184,7 +182,8 @@ class Nav extends React.PureComponent { } handleShare() { - this.props.showShareModal(); + const { username } = this.props.params; + this.props.showShareModal(this.props.project.id, this.props.project.name, username); this.setDropdown('none'); } @@ -717,6 +716,7 @@ Nav.propTypes = { }).isRequired, project: PropTypes.shape({ id: PropTypes.string, + name: PropTypes.string, owner: PropTypes.shape({ id: PropTypes.string }) @@ -742,7 +742,10 @@ Nav.propTypes = { layout: PropTypes.oneOf(['dashboard', 'project']), rootFile: PropTypes.shape({ id: PropTypes.string.isRequired - }).isRequired + }).isRequired, + params: PropTypes.shape({ + username: PropTypes.string + }) }; Nav.defaultProps = { @@ -752,7 +755,10 @@ Nav.defaultProps = { }, cmController: {}, layout: 'project', - warnIfUnsavedChanges: undefined + warnIfUnsavedChanges: undefined, + params: { + username: undefined + } }; function mapStateToProps(state) { diff --git a/client/modules/App/components/loader.jsx b/client/modules/App/components/loader.jsx index 1561387e..37306427 100644 --- a/client/modules/App/components/loader.jsx +++ b/client/modules/App/components/loader.jsx @@ -1,9 +1,11 @@ import React from 'react'; const Loader = () => ( -
-
-
+
+
+
+
+
); export default Loader; diff --git a/client/modules/IDE/components/CollectionList/CollectionListRow.jsx b/client/modules/IDE/components/CollectionList/CollectionListRow.jsx index 93513511..0f7f9a53 100644 --- a/client/modules/IDE/components/CollectionList/CollectionListRow.jsx +++ b/client/modules/IDE/components/CollectionList/CollectionListRow.jsx @@ -25,6 +25,7 @@ class CollectionListRowBase extends React.Component { renameOpen: false, renameValue: '', }; + this.renameInput = React.createRef(); } onFocusComponent = () => { @@ -89,7 +90,7 @@ class CollectionListRowBase extends React.Component { this.setState({ renameOpen: true, renameValue: this.props.collection.name, - }); + }, () => this.renameInput.current.focus()); } handleRenameChange = (e) => { @@ -99,23 +100,23 @@ class CollectionListRowBase extends React.Component { } handleRenameEnter = (e) => { - const isValid = this.state.renameValue !== ''; - if (e.key === 'Enter') { - if (isValid) { - this.props.editCollection(this.props.collection.id, { name: this.state.renameValue }); - } - - // this.resetName(); + this.updateName(); this.closeAll(); } } - // resetName = () => { - // this.setState({ - // renameValue: this.props.collection.name - // }); - // } + handleRenameBlur = () => { + this.updateName(); + this.closeAll(); + } + + updateName = () => { + const isValid = this.state.renameValue.trim().length !== 0; + if (isValid) { + this.props.editCollection(this.props.collection.id, { name: this.state.renameValue.trim() }); + } + } renderActions = () => { const { optionsOpen } = this.state; @@ -188,8 +189,9 @@ class CollectionListRowBase extends React.Component { value={renameValue} onChange={this.handleRenameChange} onKeyUp={this.handleRenameEnter} - // onBlur={this.resetName} + onBlur={this.handleRenameBlur} onClick={e => e.stopPropagation()} + ref={this.renameInput} /> } diff --git a/client/modules/IDE/components/FileNode.jsx b/client/modules/IDE/components/FileNode.jsx index 12496b38..079e1826 100644 --- a/client/modules/IDE/components/FileNode.jsx +++ b/client/modules/IDE/components/FileNode.jsx @@ -160,6 +160,7 @@ export class FileNode extends React.Component { type="text" className="sidebar__file-item-input" value={this.props.name} + maxLength="128" onChange={this.handleFileNameChange} ref={(element) => { this.fileNameInput = element; }} onBlur={() => { @@ -184,40 +185,50 @@ export class FileNode extends React.Component { {(() => { // eslint-disable-line if (this.props.fileType === 'folder') { return ( -
  • - -
  • - ); - } - })()} - {(() => { // eslint-disable-line - if (this.props.fileType === 'folder') { - return ( -
  • - -
  • + +
  • + +
  • +
  • + +
  • +
  • + +
  • + +
    ); } })()} @@ -288,7 +299,8 @@ FileNode.propTypes = { newFolder: PropTypes.func.isRequired, showFolderChildren: PropTypes.func.isRequired, hideFolderChildren: PropTypes.func.isRequired, - canEdit: PropTypes.bool.isRequired + canEdit: PropTypes.bool.isRequired, + openUploadFileModal: PropTypes.func.isRequired }; FileNode.defaultProps = { diff --git a/client/modules/IDE/components/NewFileForm.jsx b/client/modules/IDE/components/NewFileForm.jsx index 926a9925..1ff8bff4 100644 --- a/client/modules/IDE/components/NewFileForm.jsx +++ b/client/modules/IDE/components/NewFileForm.jsx @@ -29,6 +29,7 @@ class NewFileForm extends React.Component { id="name" type="text" placeholder="Name" + maxLength="128" {...domOnlyProps(name)} ref={(element) => { this.fileName = element; }} /> diff --git a/client/modules/IDE/components/NewFolderForm.jsx b/client/modules/IDE/components/NewFolderForm.jsx index 5fa48acd..70750394 100644 --- a/client/modules/IDE/components/NewFolderForm.jsx +++ b/client/modules/IDE/components/NewFolderForm.jsx @@ -29,6 +29,7 @@ class NewFolderForm extends React.Component { className="new-folder-form__name-input" id="name" type="text" + maxLength="128" placeholder="Name" ref={(element) => { this.fileName = element; }} {...domOnlyProps(name)} diff --git a/client/modules/IDE/components/Searchbar/Searchbar.jsx b/client/modules/IDE/components/Searchbar/Searchbar.jsx index 2a926c4e..1577ec5e 100644 --- a/client/modules/IDE/components/Searchbar/Searchbar.jsx +++ b/client/modules/IDE/components/Searchbar/Searchbar.jsx @@ -44,13 +44,9 @@ class Searchbar extends React.Component { const { searchValue } = this.state; return (
    - +
    { @@ -69,8 +70,9 @@ class SketchListRowBase extends React.Component { openRename = () => { this.setState({ - renameOpen: true - }); + renameOpen: true, + renameValue: this.props.sketch.name + }, () => this.renameInput.current.focus()); } closeRename = () => { @@ -94,15 +96,27 @@ class SketchListRowBase extends React.Component { handleRenameEnter = (e) => { if (e.key === 'Enter') { - // TODO pass this func - this.props.changeProjectName(this.props.sketch.id, this.state.renameValue); + this.updateName(); this.closeAll(); } } + handleRenameBlur = () => { + this.updateName(); + this.closeAll(); + } + + updateName = () => { + const isValid = this.state.renameValue.trim().length !== 0; + if (isValid) { + this.props.changeProjectName(this.props.sketch.id, this.state.renameValue.trim()); + } + } + resetSketchName = () => { this.setState({ - renameValue: this.props.sketch.name + renameValue: this.props.sketch.name, + renameOpen: false }); } @@ -255,8 +269,9 @@ class SketchListRowBase extends React.Component { value={renameValue} onChange={this.handleRenameChange} onKeyUp={this.handleRenameEnter} - onBlur={this.resetSketchName} + onBlur={this.handleRenameBlur} onClick={e => e.stopPropagation()} + ref={this.renameInput} /> } @@ -297,7 +312,7 @@ SketchListRowBase.propTypes = { cloneProject: PropTypes.func.isRequired, exportProjectAsZip: PropTypes.func.isRequired, changeProjectName: PropTypes.func.isRequired, - onAddToCollection: PropTypes.func.isRequired, + onAddToCollection: PropTypes.func.isRequired }; function mapDispatchToPropsSketchListRow(dispatch) { diff --git a/client/modules/IDE/components/Toolbar.jsx b/client/modules/IDE/components/Toolbar.jsx index b331e10e..35c29f7d 100644 --- a/client/modules/IDE/components/Toolbar.jsx +++ b/client/modules/IDE/components/Toolbar.jsx @@ -32,7 +32,7 @@ class Toolbar extends React.Component { } validateProjectName() { - if (this.props.project.name === '') { + if ((this.props.project.name.trim()).length === 0) { this.props.setProjectName(this.originalProjectName); } } diff --git a/client/modules/User/components/Collection.jsx b/client/modules/User/components/Collection.jsx index 6dec5e8c..477f1319 100644 --- a/client/modules/User/components/Collection.jsx +++ b/client/modules/User/components/Collection.jsx @@ -327,44 +327,46 @@ class Collection extends React.Component { {this._renderLoader()} {this.hasCollection() && this._renderCollectionMetadata()} -
    - {this._renderEmptyTable()} - {this.hasCollectionItems() && - - - - {this._renderFieldHeader('name', 'Name')} - {this._renderFieldHeader('createdAt', 'Date Added')} - {this._renderFieldHeader('user', 'Owner')} - - - - - {this.props.collection.items.map(item => - ())} - -
    - } - { - this.state.isAddingSketches && ( - } - closeOverlay={this.hideAddSketches} - isFixedHeight - > -
    - -
    -
    - ) - } +
    +
    + {this._renderEmptyTable()} + {this.hasCollectionItems() && + + + + {this._renderFieldHeader('name', 'Name')} + {this._renderFieldHeader('createdAt', 'Date Added')} + {this._renderFieldHeader('user', 'Owner')} + + + + + {this.props.collection.items.map(item => + ())} + +
    + } + { + this.state.isAddingSketches && ( + } + closeOverlay={this.hideAddSketches} + isFixedHeight + > +
    + +
    +
    + ) + } +
    ); diff --git a/client/styles/abstracts/_variables.scss b/client/styles/abstracts/_variables.scss index 26407719..b0fd5a69 100644 --- a/client/styles/abstracts/_variables.scss +++ b/client/styles/abstracts/_variables.scss @@ -194,7 +194,7 @@ $themes: ( toolbar-button-color: #333333, toolbar-button-background-color: #C1C1C1, button-background-hover-color: $yellow, - button-background-active-color: #f10046, + button-background-active-color: $yellow, button-nav-inactive-color: #a0a0a0, button-hover-color: #333333, button-active-color: #333333, diff --git a/client/styles/base/_base.scss b/client/styles/base/_base.scss index de49c804..c0d8a05c 100644 --- a/client/styles/base/_base.scss +++ b/client/styles/base/_base.scss @@ -79,6 +79,7 @@ h4 { } h6 { font-weight: normal; + font-size: #{12 / $base-font-size}rem; } thead { text-align: left; diff --git a/client/styles/components/_asset-list.scss b/client/styles/components/_asset-list.scss index 0a34f343..335fc7bf 100644 --- a/client/styles/components/_asset-list.scss +++ b/client/styles/components/_asset-list.scss @@ -1,7 +1,7 @@ .asset-table-container { overflow-y: auto; max-width: 100%; - min-height: #{400 / $base-font-size}rem; + min-height: 100%; } .asset-table { diff --git a/client/styles/components/_collection.scss b/client/styles/components/_collection.scss index 3a98f78d..ff488192 100644 --- a/client/styles/components/_collection.scss +++ b/client/styles/components/_collection.scss @@ -1,12 +1,15 @@ .collection-container { padding: #{24 / $base-font-size}rem #{66 / $base-font-size}rem; + position: relative; flex: 1; + overflow: hidden; display: flex; - flex-direction: column; + flex-direction:column; } .collection-metadata { - width: #{1012 / $base-font-size}rem; + max-width: #{1012 / $base-font-size}rem; + width: 100%; margin: 0 auto; margin-bottom: #{24 / $base-font-size}rem; } @@ -114,15 +117,28 @@ flex-grow: 0; } -.collection-table-wrapper { - width: #{1012 / $base-font-size}rem; - margin: 0 auto; +.collection-content { + display: flex; + flex-direction: column; flex: 1; + overflow: hidden; + max-width: #{1012 / $base-font-size}rem; + margin: 0 auto; + width: 100%; + @include themify() { border: 1px solid getThemifyVariable('modal-border-color'); } } +.collection-table-wrapper { + overflow-y: auto; + max-width: 100%; + min-height: 100%; +} + + +// maybe don't need this? [data-has-items=false] .collection-table-wrapper { display: flex; justify-content: center; diff --git a/client/styles/components/_editor.scss b/client/styles/components/_editor.scss index e6f9f9f4..6096afa1 100644 --- a/client/styles/components/_editor.scss +++ b/client/styles/components/_editor.scss @@ -1,7 +1,4 @@ .CodeMirror { - @include themify() { - border: 1px solid getThemifyVariable('ide-border-color'); - } font-family: Inconsolata, monospace; height: 100%; } @@ -328,7 +325,10 @@ pre.CodeMirror-line { height: calc(100% - #{29 / $base-font-size}rem); width: 100%; position: absolute; - &.editor-holder--hidden { + @include themify() { + border: 1px solid getThemifyVariable('ide-border-color'); + } + &.editor-holder--hidden .CodeMirror { display: none; } } diff --git a/client/styles/components/_loader.scss b/client/styles/components/_loader.scss index ec52410a..b9f2a05c 100644 --- a/client/styles/components/_loader.scss +++ b/client/styles/components/_loader.scss @@ -1,9 +1,17 @@ +.loader-container { + width: 100%; + height: 100%; + display: flex; + justify-content: center; + align-items: center; +} + + .loader { width: #{80 / $base-font-size }rem; height: #{80 / $base-font-size}rem; position: relative; - margin: #{100 / $base-font-size}rem auto; } .loader__circle1, diff --git a/client/styles/components/_p5-dark-codemirror-theme.scss b/client/styles/components/_p5-dark-codemirror-theme.scss index f10cbad9..bd073941 100644 --- a/client/styles/components/_p5-dark-codemirror-theme.scss +++ b/client/styles/components/_p5-dark-codemirror-theme.scss @@ -14,75 +14,77 @@ $p5-dark-lightbrown: #A67F59; $p5-light-green: #42F48F; $p5-dark-black: #333; $p5-dark-pink: #D9328F; -$p5-dark-gray: #A0A0A0; -$p5-dark-lightblue: #00A1D3; -$p5-dark-darkblue: #2D7BB6; +$p5-dark-gray: #999999; +$p5-dark-lightblue: #0F9DD7; +$p5-dark-darkblue: #318094; $p5-dark-white: #FDFDFD; $p5-dark-orange: #EE9900; $p5-dark-lightgray: #E0D7D1; $p5-dark-darkgray: #666666; $p5-dark-green: #58a10b; -$p5-dark-goldbrown: #b58317; +$p5-dark-goldbrown: #b58318; $p5-dark-gutter: #f4f4f4; $p5-dark-number: #b5b5b5; $p5-dark-selected: rgba(45, 123, 182, 25); $p5-dark-activeline: rgb(207, 207, 207); +$p5-dark-error: #df3a3d; + .cm-s-p5-dark { background-color: $p5-dark-black; color: $p5-dark-white; } -.cm-s-p5-dark .cm-comment { +.cm-s-p5-dark span.cm-comment { color: $p5-dark-gray; } -.cm-s-p5-dark .cm-def { - color: $p5-dark-darkblue; -} - -.cm-s-p5-dark .cm-string { - color: $p5-dark-green; -} - -.cm-s-p5-dark .cm-string-2 { - color: $p5-dark-orange; -} - -.cm-s-p5-dark .cm-number { - color: $p5-dark-white; -} - -.cm-s-p5-dark .cm-keyword { - color: $p5-dark-goldbrown; -} - -.cm-s-p5-dark .cm-variable { +.cm-s-p5-dark span.cm-def { color: $p5-dark-lightblue; } -.cm-s-p5-dark .cm-variable-2 { +.cm-s-p5-dark span.cm-string { + color: $p5-dark-green; +} + +.cm-s-p5-dark span.cm-string-2 { + color: $p5-dark-orange; +} + +.cm-s-p5-dark span.cm-number { color: $p5-dark-white; } -.cm-s-p5-dark .cm-property { +.cm-s-p5-dark span.cm-keyword { + color: $p5-dark-goldbrown; +} + +.cm-s-p5-dark span.cm-variable { + color: $p5-dark-lightblue; +} + +.cm-s-p5-dark span.cm-variable-2 { color: $p5-dark-white; } -.cm-s-p5-dark .cm-atom { +.cm-s-p5-dark span.cm-property { + color: $p5-dark-white; +} + +.cm-s-p5-dark span.cm-atom { color: $p5-dark-pink; } -.cm-s-p5-dark .cm-operator { - color: $p5-dark-lightbrown; +.cm-s-p5-dark span.cm-operator { + color: $p5-dark-white; } .cm-s-p5-dark .cm-linenumber { color: $p5-dark-number; } -.cm-s-p5-dark .CodeMirror-selected { +.cm-s-p5-dark div.CodeMirror-selected { background-color: $p5-dark-selected; } @@ -95,34 +97,36 @@ $p5-dark-activeline: rgb(207, 207, 207); border-right: 1px solid #949494; } -.cm-s-p5-dark .cm-error { - color: #f00; -} - -.cm-s-p5-dark .CodeMirror-matchingbracket { +.cm-s-p5-dark span.CodeMirror-matchingbracket { outline: 1px solid $p5-dark-gray; outline-offset: 1px; color: $p5-dark-white !important; } -.cm-s-p5-dark .cm-qualifier { +.cm-s-p5-dark span.cm-qualifier { color: $p5-dark-lightblue; } -.cm-s-p5-dark .cm-tag { +.cm-s-p5-dark span.cm-tag { color: $p5-dark-pink; } -.cm-s-p5-dark .cm-builtin { - color: $p5-dark-lightblue; +.cm-s-p5-dark span.cm-error { + color: $p5-dark-error; } -.cm-s-p5-dark .cm-attribute { +.cm-s-p5-dark span.cm-builtin { + color: $p5-dark-lightblue; + font-weight: bold; +} + +.cm-s-p5-dark span.cm-attribute { color: $p5-dark-lightblue; } .cm-s-p5-dark .cm-p5-function { - color: $p5-dark-darkblue; + color: $p5-dark-lightblue; + font-weight: bold !important; } .cm-s-p5-dark .cm-p5-variable { diff --git a/client/styles/components/_searchbar.scss b/client/styles/components/_searchbar.scss index 78434d03..7076f75c 100644 --- a/client/styles/components/_searchbar.scss +++ b/client/styles/components/_searchbar.scss @@ -15,26 +15,20 @@ } } -button[type="submit"].searchbar__button { +div.searchbar__button { background-color: unset; width: #{31 / $base-font-size}rem; height: #{36 / $base-font-size}rem; position: absolute; + display: flex; + justify-content: center; + align-items: center; padding: 0; border: 0; border-right: solid 1px; @include themify() { border-right-color: getThemifyVariable('input-border-color'); } - &:enabled:hover { - background-color: unset; - @include themify() { - border-right-color: getThemifyVariable('input-border-color'); - } - & g { - fill: unset; - } - } } .searchbar__icon { @@ -43,6 +37,7 @@ button[type="submit"].searchbar__button { width: #{22 / $base-font-size}rem; height: #{27 / $base-font-size}rem; transform: scaleX(-1); + padding-top: #{3 / $base-font-size}rem; @include themify() { fill: getThemifyVariable('input-text-color'); } diff --git a/client/styles/components/_sketch-list.scss b/client/styles/components/_sketch-list.scss index 76d456ab..87162305 100644 --- a/client/styles/components/_sketch-list.scss +++ b/client/styles/components/_sketch-list.scss @@ -1,7 +1,7 @@ .sketches-table-container { overflow-y: auto; max-width: 100%; - min-height: #{400 / $base-font-size}rem; + min-height: 100%; } .sketches-table { diff --git a/developer_docs/README.md b/developer_docs/README.md index d4245b04..2ac1dbf1 100644 --- a/developer_docs/README.md +++ b/developer_docs/README.md @@ -9,6 +9,6 @@ This folder contains documents intended for developers of the p5.js Web Editor. * [Deployment](deployment.md) - A guide to production deployment, and all platforms that are being used. ## Documents to Create -* Design Principles - reference [p5.js design principles](https://github.com/processing/p5.js/edit/master/developer_docs/design_principles.md) -* Issue Labels - reference [p5.js issue labels](https://github.com/processing/p5.js/blob/master/developer_docs/issue_labels.md) +* Design Principles - reference [p5.js design principles](https://github.com/processing/p5.js/edit/master/contributor_docs/design_principles.md) +* Issue Labels - reference [p5.js issue labels](https://github.com/processing/p5.js/blob/master/contributor_docs/issue_labels.md) * File Structure - An explanation of the file structure of this application. diff --git a/developer_docs/preparing_a_pull_request.md b/developer_docs/preparing_a_pull_request.md index c420e437..050deebd 100644 --- a/developer_docs/preparing_a_pull_request.md +++ b/developer_docs/preparing_a_pull_request.md @@ -19,7 +19,7 @@ Make sure you're tracking upstream p5.js repository. If you see an error, you'll need to start tracking the main p5.js repo as an "upstream" remote repository. You'll only need to do this once! But, no harm is done if you run it a second time. - git remote add upstream https://github.com/processing/p5.js + git remote add upstream https://github.com/processing/p5.js-web-editor Then ask git about the latest changes.