Merge branch 'master' of https://github.com/processing/p5.js-web-editor into add-readme-ko-translation

This commit is contained in:
Minjun Kim 2020-04-09 14:26:51 +02:00
commit 31e67eed7a
22 changed files with 241 additions and 179 deletions

1
.github/FUNDING.yml vendored Normal file
View file

@ -0,0 +1 @@
custom: https://processingfoundation.org/support

View file

@ -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) {

View file

@ -1,9 +1,11 @@
import React from 'react';
const Loader = () => (
<div className="loader">
<div className="loader__circle1" />
<div className="loader__circle2" />
<div className="loader-container">
<div className="loader">
<div className="loader__circle1" />
<div className="loader__circle2" />
</div>
</div>
);
export default Loader;

View file

@ -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}
/>
}
</React.Fragment>

View file

@ -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 (
<li>
<button
aria-label="add file"
onClick={() => {
this.props.newFile(this.props.id);
setTimeout(() => this.hideFileOptions(), 0);
}}
onBlur={this.onBlurComponent}
onFocus={this.onFocusComponent}
className="sidebar__file-item-option"
>
Add File
</button>
</li>
);
}
})()}
{(() => { // eslint-disable-line
if (this.props.fileType === 'folder') {
return (
<li>
<button
aria-label="add folder"
onClick={() => {
this.props.newFolder(this.props.id);
setTimeout(() => this.hideFileOptions(), 0);
}}
onBlur={this.onBlurComponent}
onFocus={this.onFocusComponent}
className="sidebar__file-item-option"
>
Add Folder
</button>
</li>
<React.Fragment>
<li>
<button
aria-label="add folder"
onClick={() => {
this.props.newFolder(this.props.id);
setTimeout(this.hideFileOptions, 0);
}}
onBlur={this.onBlurComponent}
onFocus={this.onFocusComponent}
className="sidebar__file-item-option"
>
Create folder
</button>
</li>
<li>
<button
aria-label="add file"
onClick={() => {
this.props.newFile(this.props.id);
setTimeout(this.hideFileOptions, 0);
}}
onBlur={this.onBlurComponent}
onFocus={this.onFocusComponent}
className="sidebar__file-item-option"
>
Create file
</button>
</li>
<li>
<button
aria-label="upload file"
onClick={() => {
this.props.openUploadFileModal(this.props.id);
setTimeout(this.hideFileOptions, 0);
}}
onBlur={this.onBlurComponent}
onFocus={this.onFocusComponent}
>
Upload file
</button>
</li>
</React.Fragment>
);
}
})()}
@ -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 = {

View file

@ -29,6 +29,7 @@ class NewFileForm extends React.Component {
id="name"
type="text"
placeholder="Name"
maxLength="128"
{...domOnlyProps(name)}
ref={(element) => { this.fileName = element; }}
/>

View file

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

View file

@ -44,13 +44,9 @@ class Searchbar extends React.Component {
const { searchValue } = this.state;
return (
<div className={`searchbar ${searchValue === '' ? 'searchbar--is-empty' : ''}`}>
<button
type="submit"
className="searchbar__button"
onClick={this.handleSearchEnter}
>
<div className="searchbar__button">
<InlineSVG className="searchbar__icon" src={searchIcon} />
</button>
</div>
<input
className="searchbar__input"
type="text"

View file

@ -32,6 +32,7 @@ class SketchListRowBase extends React.Component {
renameValue: props.sketch.name,
isFocused: false,
};
this.renameInput = React.createRef();
}
onFocusComponent = () => {
@ -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}
/>
}
</React.Fragment>
@ -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) {

View file

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

View file

@ -327,44 +327,46 @@ class Collection extends React.Component {
</Helmet>
{this._renderLoader()}
{this.hasCollection() && this._renderCollectionMetadata()}
<div className="collection-table-wrapper">
{this._renderEmptyTable()}
{this.hasCollectionItems() &&
<table className="sketches-table" summary="table containing all collections">
<thead>
<tr>
{this._renderFieldHeader('name', 'Name')}
{this._renderFieldHeader('createdAt', 'Date Added')}
{this._renderFieldHeader('user', 'Owner')}
<th scope="col"></th>
</tr>
</thead>
<tbody>
{this.props.collection.items.map(item =>
(<CollectionItemRow
key={item.id}
item={item}
user={this.props.user}
username={this.getUsername()}
collection={this.props.collection}
/>))}
</tbody>
</table>
}
{
this.state.isAddingSketches && (
<Overlay
title="Add sketch"
actions={<SketchSearchbar />}
closeOverlay={this.hideAddSketches}
isFixedHeight
>
<div className="collection-add-sketch">
<AddToCollectionSketchList username={this.props.username} collection={this.props.collection} />
</div>
</Overlay>
)
}
<div className="collection-content">
<div className="collection-table-wrapper">
{this._renderEmptyTable()}
{this.hasCollectionItems() &&
<table className="sketches-table" summary="table containing all collections">
<thead>
<tr>
{this._renderFieldHeader('name', 'Name')}
{this._renderFieldHeader('createdAt', 'Date Added')}
{this._renderFieldHeader('user', 'Owner')}
<th scope="col"></th>
</tr>
</thead>
<tbody>
{this.props.collection.items.map(item =>
(<CollectionItemRow
key={item.id}
item={item}
user={this.props.user}
username={this.getUsername()}
collection={this.props.collection}
/>))}
</tbody>
</table>
}
{
this.state.isAddingSketches && (
<Overlay
title="Add sketch"
actions={<SketchSearchbar />}
closeOverlay={this.hideAddSketches}
isFixedHeight
>
<div className="collection-add-sketch">
<AddToCollectionSketchList username={this.props.username} collection={this.props.collection} />
</div>
</Overlay>
)
}
</div>
</div>
</section>
);

View file

@ -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,

View file

@ -79,6 +79,7 @@ h4 {
}
h6 {
font-weight: normal;
font-size: #{12 / $base-font-size}rem;
}
thead {
text-align: left;

View file

@ -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 {

View file

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

View file

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

View file

@ -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,

View file

@ -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 {

View file

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

View file

@ -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 {

View file

@ -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.

View file

@ -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.