Skip to content

Commit f8d6cb4

Browse files
committed
Merge branch 'master' into feature/public-api
2 parents f54f41e + b7df80a commit f8d6cb4

27 files changed

+3619
-3419
lines changed

.babelrc

Lines changed: 76 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,83 @@
11
{
2-
"presets": ["react", "env", "stage-0"],
2+
"presets": [
3+
"@babel/preset-react",
4+
"@babel/preset-env"
5+
],
36
"env": {
47
"production": {
58
"plugins": [
69
"transform-react-remove-prop-types",
7-
"transform-react-constant-elements",
8-
"transform-react-inline-elements"
10+
"@babel/plugin-transform-react-constant-elements",
11+
"@babel/plugin-transform-react-inline-elements",
12+
"@babel/plugin-syntax-dynamic-import",
13+
"@babel/plugin-syntax-import-meta",
14+
[
15+
"@babel/plugin-proposal-decorators",
16+
{
17+
"legacy": true
18+
}
19+
],
20+
[
21+
"@babel/plugin-proposal-class-properties",
22+
{
23+
"loose": true
24+
}
25+
],
26+
"@babel/plugin-proposal-json-strings",
27+
"@babel/plugin-proposal-function-sent",
28+
"@babel/plugin-proposal-export-namespace-from",
29+
"@babel/plugin-proposal-numeric-separator",
30+
"@babel/plugin-proposal-throw-expressions",
31+
"@babel/plugin-proposal-export-default-from",
32+
"@babel/plugin-proposal-logical-assignment-operators",
33+
"@babel/plugin-proposal-optional-chaining",
34+
[
35+
"@babel/plugin-proposal-pipeline-operator",
36+
{
37+
"proposal": "minimal"
38+
}
39+
],
40+
"@babel/plugin-proposal-nullish-coalescing-operator",
41+
"@babel/plugin-proposal-do-expressions",
42+
"@babel/plugin-proposal-function-bind"
943
],
10-
"presets": ["env", "react", "react-optimize", "stage-0"]
44+
"presets": [
45+
"@babel/preset-env",
46+
"@babel/preset-react"
47+
]
1148
}
12-
}
13-
}
49+
},
50+
"plugins": [
51+
"@babel/plugin-syntax-dynamic-import",
52+
"@babel/plugin-syntax-import-meta",
53+
[
54+
"@babel/plugin-proposal-decorators",
55+
{
56+
"legacy": true
57+
}
58+
],
59+
[
60+
"@babel/plugin-proposal-class-properties",
61+
{
62+
"loose": true
63+
}
64+
],
65+
"@babel/plugin-proposal-json-strings",
66+
"@babel/plugin-proposal-function-sent",
67+
"@babel/plugin-proposal-export-namespace-from",
68+
"@babel/plugin-proposal-numeric-separator",
69+
"@babel/plugin-proposal-throw-expressions",
70+
"@babel/plugin-proposal-export-default-from",
71+
"@babel/plugin-proposal-logical-assignment-operators",
72+
"@babel/plugin-proposal-optional-chaining",
73+
[
74+
"@babel/plugin-proposal-pipeline-operator",
75+
{
76+
"proposal": "minimal"
77+
}
78+
],
79+
"@babel/plugin-proposal-nullish-coalescing-operator",
80+
"@babel/plugin-proposal-do-expressions",
81+
"@babel/plugin-proposal-function-bind"
82+
]
83+
}

client/constants.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,9 @@ export const HIDE_RUNTIME_ERROR_WARNING = 'HIDE_RUNTIME_ERROR_WARNING';
121121
export const SHOW_RUNTIME_ERROR_WARNING = 'SHOW_RUNTIME_ERROR_WARNING';
122122
export const SET_ASSETS = 'SET_ASSETS';
123123

124+
export const TOGGLE_DIRECTION = 'TOGGLE_DIRECTION';
125+
export const SET_SORTING = 'SET_SORTING';
126+
124127
export const START_LOADING = 'START_LOADING';
125128
export const STOP_LOADING = 'STOP_LOADING';
126129

client/images/sort-arrow-down.svg

Lines changed: 9 additions & 0 deletions
Loading

client/images/sort-arrow-up.svg

Lines changed: 9 additions & 0 deletions
Loading

client/index.jsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ require('./images/p5js-square-logo.png');
1313

1414
const history = browserHistory;
1515
const initialState = window.__INITIAL_STATE__;
16+
1617
const store = configureStore(initialState);
1718

1819
const App = () => (

client/modules/IDE/actions/sorting.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import * as ActionTypes from '../../../constants';
2+
3+
export const DIRECTION = {
4+
ASC: 'ASCENDING',
5+
DESC: 'DESCENDING'
6+
};
7+
8+
export function setSorting(field, direction) {
9+
return {
10+
type: ActionTypes.SET_SORTING,
11+
payload: {
12+
field,
13+
direction
14+
}
15+
};
16+
}
17+
18+
export function resetSorting() {
19+
return setSorting('createdAt', DIRECTION.DESC);
20+
}
21+
22+
export function toggleDirectionForField(field) {
23+
return {
24+
type: ActionTypes.TOGGLE_DIRECTION,
25+
field
26+
};
27+
}

client/modules/IDE/components/Editor.jsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -283,8 +283,6 @@ class Editor extends React.Component {
283283
}
284284
}
285285

286-
_cm: CodeMirror.Editor
287-
288286
render() {
289287
const editorSectionClass = classNames({
290288
'editor': true,

client/modules/IDE/components/ErrorModal.jsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ class ErrorModal extends React.Component {
2626
staleProject() {
2727
return (
2828
<p>
29-
The project you have attempted to save has been saved from another window. Please refresh the page to see the latest version.
29+
The project you have attempted to save has been saved from another window.
30+
Please refresh the page to see the latest version.
3031
</p>
3132
);
3233
}

client/modules/IDE/components/SketchList.jsx

Lines changed: 49 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,24 @@ import InlineSVG from 'react-inlinesvg';
66
import { connect } from 'react-redux';
77
import { browserHistory, Link } from 'react-router';
88
import { bindActionCreators } from 'redux';
9+
import classNames from 'classnames';
910
import * as ProjectActions from '../actions/project';
1011
import * as SketchActions from '../actions/projects';
1112
import * as ToastActions from '../actions/toast';
13+
import * as SortingActions from '../actions/sorting';
14+
import getSortedSketches from '../selectors/projects';
1215
import Loader from '../../App/components/loader';
1316

1417
const trashCan = require('../../../images/trash-can.svg');
18+
const arrowUp = require('../../../images/sort-arrow-up.svg');
19+
const arrowDown = require('../../../images/sort-arrow-down.svg');
1520

1621
class SketchList extends React.Component {
1722
constructor(props) {
1823
super(props);
1924
this.props.getProjects(this.props.username);
25+
this.props.resetSorting();
26+
this._renderFieldHeader = this._renderFieldHeader.bind(this);
2027
}
2128

2229
getSketchesTitle() {
@@ -30,33 +37,56 @@ class SketchList extends React.Component {
3037
return !this.props.loading && this.props.sketches.length > 0;
3138
}
3239

33-
renderLoader() {
40+
_renderLoader() {
3441
if (this.props.loading) return <Loader />;
3542
return null;
3643
}
3744

38-
renderEmptyTable() {
39-
if (!this.props.loading && this.props.sketches.length === 0) return (<p className="sketches-table__empty">No sketches.</p>);
45+
_renderEmptyTable() {
46+
if (!this.props.loading && this.props.sketches.length === 0) {
47+
return (<p className="sketches-table__empty">No sketches.</p>);
48+
}
4049
return null;
4150
}
4251

52+
_renderFieldHeader(fieldName, displayName) {
53+
const { field, direction } = this.props.sorting;
54+
const headerClass = classNames({
55+
'sketches-table__header': true,
56+
'sketches-table__header--selected': field === fieldName
57+
});
58+
return (
59+
<th scope="col">
60+
<button className="sketch-list__sort-button" onClick={() => this.props.toggleDirectionForField(fieldName)}>
61+
<span className={headerClass}>{displayName}</span>
62+
{field === fieldName && direction === SortingActions.DIRECTION.ASC &&
63+
<InlineSVG src={arrowUp} />
64+
}
65+
{field === fieldName && direction === SortingActions.DIRECTION.DESC &&
66+
<InlineSVG src={arrowDown} />
67+
}
68+
</button>
69+
</th>
70+
);
71+
}
72+
4373
render() {
4474
const username = this.props.username !== undefined ? this.props.username : this.props.user.username;
4575
return (
4676
<div className="sketches-table-container">
4777
<Helmet>
4878
<title>{this.getSketchesTitle()}</title>
4979
</Helmet>
50-
{this.renderLoader()}
51-
{this.renderEmptyTable()}
80+
{this._renderLoader()}
81+
{this._renderEmptyTable()}
5282
{this.hasSketches() &&
5383
<table className="sketches-table" summary="table containing all saved projects">
5484
<thead>
5585
<tr>
5686
<th className="sketch-list__trash-column" scope="col"></th>
57-
<th scope="col">Sketch</th>
58-
<th scope="col">Date created</th>
59-
<th scope="col">Date updated</th>
87+
{this._renderFieldHeader('name', 'Sketch')}
88+
{this._renderFieldHeader('createdAt', 'Date Created')}
89+
{this._renderFieldHeader('updatedAt', 'Date Updated')}
6090
</tr>
6191
</thead>
6292
<tbody>
@@ -110,7 +140,13 @@ SketchList.propTypes = {
110140
})).isRequired,
111141
username: PropTypes.string,
112142
loading: PropTypes.bool.isRequired,
113-
deleteProject: PropTypes.func.isRequired
143+
deleteProject: PropTypes.func.isRequired,
144+
toggleDirectionForField: PropTypes.func.isRequired,
145+
resetSorting: PropTypes.func.isRequired,
146+
sorting: PropTypes.shape({
147+
field: PropTypes.string.isRequired,
148+
direction: PropTypes.string.isRequired
149+
}).isRequired,
114150
};
115151

116152
SketchList.defaultProps = {
@@ -120,13 +156,14 @@ SketchList.defaultProps = {
120156
function mapStateToProps(state) {
121157
return {
122158
user: state.user,
123-
sketches: state.sketches,
124-
loading: state.loading,
159+
sketches: getSortedSketches(state),
160+
sorting: state.sorting,
161+
loading: state.loading
125162
};
126163
}
127164

128165
function mapDispatchToProps(dispatch) {
129-
return bindActionCreators(Object.assign({}, SketchActions, ProjectActions, ToastActions), dispatch);
166+
return bindActionCreators(Object.assign({}, SketchActions, ProjectActions, ToastActions, SortingActions), dispatch);
130167
}
131168

132169
export default connect(mapStateToProps, mapDispatchToProps)(SketchList);

client/modules/IDE/reducers/project.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ const project = (state, action) => {
5050
return Object.assign({}, state, { updatedAt: action.value });
5151
case ActionTypes.START_SAVING_PROJECT:
5252
return Object.assign({}, state, { isSaving: true });
53-
case ActionTypes.START_STOP_PROJECT:
53+
case ActionTypes.END_SAVING_PROJECT:
5454
return Object.assign({}, state, { isSaving: false });
5555
default:
5656
return state;
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import * as ActionTypes from '../../../constants';
2+
import { DIRECTION } from '../actions/sorting';
3+
4+
const initialState = {
5+
field: 'createdAt',
6+
direction: DIRECTION.DESC
7+
};
8+
9+
const sorting = (state = initialState, action) => {
10+
switch (action.type) {
11+
case ActionTypes.TOGGLE_DIRECTION:
12+
if (action.field && action.field !== state.field) {
13+
if (action.field === 'name') {
14+
return { ...state, field: action.field, direction: DIRECTION.ASC };
15+
}
16+
return { ...state, field: action.field, direction: DIRECTION.DESC };
17+
}
18+
if (state.direction === DIRECTION.ASC) {
19+
return { ...state, direction: DIRECTION.DESC };
20+
}
21+
return { ...state, direction: DIRECTION.ASC };
22+
case ActionTypes.SET_SORTING:
23+
return { ...state, field: action.payload.field, direction: action.payload.direction };
24+
default:
25+
return state;
26+
}
27+
};
28+
29+
export default sorting;
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { createSelector } from 'reselect';
2+
import differenceInMilliseconds from 'date-fns/difference_in_milliseconds';
3+
import orderBy from 'lodash/orderBy';
4+
import { DIRECTION } from '../actions/sorting';
5+
6+
const getSketches = state => state.sketches;
7+
const getField = state => state.sorting.field;
8+
const getDirection = state => state.sorting.direction;
9+
10+
const getSortedSketches = createSelector(
11+
getSketches,
12+
getField,
13+
getDirection,
14+
(sketches, field, direction) => {
15+
if (field === 'name') {
16+
if (direction === DIRECTION.DESC) {
17+
return orderBy(sketches, 'name', 'desc');
18+
}
19+
return orderBy(sketches, 'name', 'asc');
20+
}
21+
const sortedSketches = [...sketches].sort((a, b) => {
22+
const result =
23+
direction === DIRECTION.ASC
24+
? differenceInMilliseconds(new Date(a[field]), new Date(b[field]))
25+
: differenceInMilliseconds(new Date(b[field]), new Date(a[field]));
26+
return result;
27+
});
28+
return sortedSketches;
29+
}
30+
);
31+
32+
export default getSortedSketches;

client/reducers.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import sketches from './modules/IDE/reducers/projects';
1010
import toast from './modules/IDE/reducers/toast';
1111
import console from './modules/IDE/reducers/console';
1212
import assets from './modules/IDE/reducers/assets';
13+
import sorting from './modules/IDE/reducers/sorting';
1314
import loading from './modules/IDE/reducers/loading';
1415

1516
const rootReducer = combineReducers({
@@ -20,6 +21,7 @@ const rootReducer = combineReducers({
2021
user,
2122
project,
2223
sketches,
24+
sorting,
2325
editorAccessibility,
2426
toast,
2527
console,

0 commit comments

Comments
 (0)