diff --git a/client/modules/IDE/components/CollectionList.jsx b/client/modules/IDE/components/CollectionList.jsx
deleted file mode 100644
index c967be63..00000000
--- a/client/modules/IDE/components/CollectionList.jsx
+++ /dev/null
@@ -1,398 +0,0 @@
-import format from 'date-fns/format';
-import PropTypes from 'prop-types';
-import React from 'react';
-import { Helmet } from 'react-helmet';
-import InlineSVG from 'react-inlinesvg';
-import { connect } from 'react-redux';
-import { Link } from 'react-router';
-import { bindActionCreators } from 'redux';
-import classNames from 'classnames';
-import * as ProjectActions from '../actions/project';
-import * as ProjectsActions from '../actions/projects';
-import * as CollectionsActions from '../actions/collections';
-import * as ToastActions from '../actions/toast';
-import * as SortingActions from '../actions/sorting';
-import * as IdeActions from '../actions/ide';
-import getSortedCollections from '../selectors/collections';
-import Loader from '../../App/components/loader';
-
-const arrowUp = require('../../../images/sort-arrow-up.svg');
-const arrowDown = require('../../../images/sort-arrow-down.svg');
-const downFilledTriangle = require('../../../images/down-filled-triangle.svg');
-
-class CollectionListRowBase extends React.Component {
- static projectInCollection(project, collection) {
- return collection.items.find(item => item.project.id === project.id) != null;
- }
-
- constructor(props) {
- super(props);
- this.state = {
- optionsOpen: false,
- renameOpen: false,
- renameValue: props.collection.name,
- isFocused: false
- };
- }
-
- onFocusComponent = () => {
- this.setState({ isFocused: true });
- }
-
- onBlurComponent = () => {
- this.setState({ isFocused: false });
- setTimeout(() => {
- if (!this.state.isFocused) {
- this.closeAll();
- }
- }, 200);
- }
-
- openOptions = () => {
- this.setState({
- optionsOpen: true
- });
- }
-
- closeOptions = () => {
- this.setState({
- optionsOpen: false
- });
- }
-
- toggleOptions = () => {
- if (this.state.optionsOpen) {
- this.closeOptions();
- } else {
- this.openOptions();
- }
- }
-
- openRename = () => {
- this.setState({
- renameOpen: true
- });
- }
-
- closeRename = () => {
- this.setState({
- renameOpen: false
- });
- }
-
- closeAll = () => {
- this.setState({
- renameOpen: false,
- optionsOpen: false
- });
- }
-
- handleRenameChange = (e) => {
- this.setState({
- renameValue: e.target.value
- });
- }
-
- handleRenameEnter = (e) => {
- if (e.key === 'Enter') {
- // TODO pass this func
- this.props.changeProjectName(this.props.collection.id, this.state.renameValue);
- this.closeAll();
- }
- }
-
- resetSketchName = () => {
- this.setState({
- renameValue: this.props.collection.name
- });
- }
-
- handleDropdownOpen = () => {
- this.closeAll();
- this.openOptions();
- }
-
- handleRenameOpen = () => {
- this.closeAll();
- this.openRename();
- }
-
- handleSketchDownload = () => {
- this.props.exportProjectAsZip(this.props.collection.id);
- }
-
- handleSketchDuplicate = () => {
- this.closeAll();
- this.props.cloneProject(this.props.collection.id);
- }
-
- handleSketchShare = () => {
- this.closeAll();
- this.props.showShareModal(this.props.collection.id, this.props.collection.name, this.props.username);
- }
-
- handleSketchDelete = () => {
- this.closeAll();
- if (window.confirm(`Are you sure you want to delete "${this.props.collection.name}"?`)) {
- this.props.deleteProject(this.props.collection.id);
- }
- }
-
- handleCollectionAdd = () => {
- this.props.addToCollection(this.props.collection.id, this.props.project.id);
- }
-
- handleCollectionRemove = () => {
- this.props.removeFromCollection(this.props.collection.id, this.props.project.id);
- }
-
- render() {
- const { collection, username } = this.props;
- const { renameOpen, optionsOpen, renameValue } = this.state;
- const userIsOwner = this.props.user.username === this.props.username;
-
- const dropdown = (
-
-
- {optionsOpen &&
-
- {userIsOwner &&
- -
-
-
}
-
- }
- |
- );
-
- return (
-
-
-
- {renameOpen ? '' : collection.name}
-
- {renameOpen
- &&
- e.stopPropagation()}
- />
- }
- |
- {format(new Date(collection.createdAt), 'MMM D, YYYY h:mm A')} |
- {format(new Date(collection.updatedAt), 'MMM D, YYYY h:mm A')} |
- {(collection.items || []).length} |
- {dropdown} |
-
);
- }
-}
-
-CollectionListRowBase.propTypes = {
- addToCollection: PropTypes.func.isRequired,
- removeFromCollection: PropTypes.func.isRequired,
- collection: PropTypes.shape({
- id: PropTypes.string.isRequired,
- name: PropTypes.string.isRequired
- }).isRequired,
- username: PropTypes.string.isRequired,
- user: PropTypes.shape({
- username: PropTypes.string,
- authenticated: PropTypes.bool.isRequired
- }).isRequired,
- deleteProject: PropTypes.func.isRequired,
- showShareModal: PropTypes.func.isRequired,
- cloneProject: PropTypes.func.isRequired,
- exportProjectAsZip: PropTypes.func.isRequired,
- changeProjectName: PropTypes.func.isRequired
-};
-
-function mapDispatchToPropsSketchListRow(dispatch) {
- return bindActionCreators(Object.assign({}, CollectionsActions, ProjectActions, IdeActions), dispatch);
-}
-
-const CollectionListRow = connect(null, mapDispatchToPropsSketchListRow)(CollectionListRowBase);
-
-class CollectionList extends React.Component {
- constructor(props) {
- super(props);
- this.props.getCollections(this.props.username);
- this.props.resetSorting();
- this._renderFieldHeader = this._renderFieldHeader.bind(this);
- }
-
- getTitle() {
- if (this.props.username === this.props.user.username) {
- return 'p5.js Web Editor | My collections';
- }
- return `p5.js Web Editor | ${this.props.username}'s collections`;
- }
-
- hasCollections() {
- return !this.props.loading && this.props.collections.length > 0;
- }
-
- _renderLoader() {
- if (this.props.loading) return ;
- return null;
- }
-
- _renderEmptyTable() {
- if (!this.props.loading && this.props.collections.length === 0) {
- return (No collections.
);
- }
- return null;
- }
-
- _renderFieldHeader(fieldName, displayName) {
- const { field, direction } = this.props.sorting;
- const headerClass = classNames({
- 'sketches-table__header': true,
- 'sketches-table__header--selected': field === fieldName
- });
- return (
-
-
- |
- );
- }
-
- render() {
- const username = this.props.username !== undefined ? this.props.username : this.props.user.username;
- return (
-
-
- {this.getTitle()}
-
-
-
New collection
-
- {this._renderLoader()}
- {this._renderEmptyTable()}
- {this.hasCollections() &&
-
-
-
- {this._renderFieldHeader('name', 'Name')}
- {this._renderFieldHeader('createdAt', 'Date Created')}
- {this._renderFieldHeader('updatedAt', 'Date Updated')}
- {this._renderFieldHeader('numItems', '# sketches')}
- |
-
-
-
- {this.props.collections.map(collection =>
- ())}
-
-
}
-
- );
- }
-}
-
-const ProjectShape = PropTypes.shape({
- id: PropTypes.string.isRequired,
- name: PropTypes.string.isRequired,
- createdAt: PropTypes.string.isRequired,
- updatedAt: PropTypes.string.isRequired,
- user: PropTypes.shape({
- username: PropTypes.string.isRequired
- }).isRequired,
-});
-
-const ItemsShape = PropTypes.shape({
- createdAt: PropTypes.string.isRequired,
- updatedAt: PropTypes.string.isRequired,
- project: ProjectShape
-});
-
-CollectionList.propTypes = {
- user: PropTypes.shape({
- username: PropTypes.string,
- authenticated: PropTypes.bool.isRequired
- }).isRequired,
- getCollections: PropTypes.func.isRequired,
- collections: PropTypes.arrayOf(PropTypes.shape({
- id: PropTypes.string.isRequired,
- name: PropTypes.string.isRequired,
- description: PropTypes.string,
- createdAt: PropTypes.string.isRequired,
- updatedAt: PropTypes.string.isRequired,
- items: PropTypes.arrayOf(ItemsShape),
- })).isRequired,
- username: PropTypes.string,
- loading: PropTypes.bool.isRequired,
- toggleDirectionForField: PropTypes.func.isRequired,
- resetSorting: PropTypes.func.isRequired,
- sorting: PropTypes.shape({
- field: PropTypes.string.isRequired,
- direction: PropTypes.string.isRequired
- }).isRequired,
- project: PropTypes.shape({
- id: PropTypes.string,
- owner: PropTypes.shape({
- id: PropTypes.string
- })
- })
-};
-
-CollectionList.defaultProps = {
- project: {
- id: undefined,
- owner: undefined
- },
- username: undefined
-};
-
-function mapStateToProps(state) {
- return {
- user: state.user,
- collections: getSortedCollections(state),
- sorting: state.sorting,
- loading: state.loading,
- project: state.project
- };
-}
-
-function mapDispatchToProps(dispatch) {
- return bindActionCreators(
- Object.assign({}, CollectionsActions, ProjectsActions, ToastActions, SortingActions),
- dispatch
- );
-}
-
-export default connect(mapStateToProps, mapDispatchToProps)(CollectionList);
diff --git a/client/modules/IDE/components/CollectionList/CollectionList.jsx b/client/modules/IDE/components/CollectionList/CollectionList.jsx
new file mode 100644
index 00000000..cbf0613f
--- /dev/null
+++ b/client/modules/IDE/components/CollectionList/CollectionList.jsx
@@ -0,0 +1,188 @@
+import PropTypes from 'prop-types';
+import React from 'react';
+import { Helmet } from 'react-helmet';
+import InlineSVG from 'react-inlinesvg';
+import { connect } from 'react-redux';
+import { bindActionCreators } from 'redux';
+import classNames from 'classnames';
+import * as ProjectActions from '../../actions/project';
+import * as ProjectsActions from '../../actions/projects';
+import * as CollectionsActions from '../../actions/collections';
+import * as ToastActions from '../../actions/toast';
+import * as SortingActions from '../../actions/sorting';
+import getSortedCollections from '../../selectors/collections';
+import Loader from '../../../App/components/loader';
+import CollectionListRow from './CollectionListRow';
+
+const arrowUp = require('../../../../images/sort-arrow-up.svg');
+const arrowDown = require('../../../../images/sort-arrow-down.svg');
+
+class CollectionList extends React.Component {
+ constructor(props) {
+ super(props);
+
+ if (props.projectId) {
+ props.getProject(props.projectId);
+ }
+
+ this.props.getCollections(this.props.username);
+ this.props.resetSorting();
+ this._renderFieldHeader = this._renderFieldHeader.bind(this);
+ }
+
+ getTitle() {
+ if (this.props.username === this.props.user.username) {
+ return 'p5.js Web Editor | My collections';
+ }
+ return `p5.js Web Editor | ${this.props.username}'s collections`;
+ }
+
+ hasCollections() {
+ return !this.props.loading && this.props.collections.length > 0;
+ }
+
+ _renderLoader() {
+ if (this.props.loading) return ;
+ return null;
+ }
+
+ _renderEmptyTable() {
+ if (!this.props.loading && this.props.collections.length === 0) {
+ return (No collections.
);
+ }
+ return null;
+ }
+
+ _renderFieldHeader(fieldName, displayName) {
+ const { field, direction } = this.props.sorting;
+ const headerClass = classNames({
+ 'sketches-table__header': true,
+ 'sketches-table__header--selected': field === fieldName
+ });
+ return (
+
+
+ |
+ );
+ }
+
+ render() {
+ const username = this.props.username !== undefined ? this.props.username : this.props.user.username;
+
+ return (
+
+
+ {this.getTitle()}
+
+
+ {this._renderLoader()}
+ {this._renderEmptyTable()}
+ {this.hasCollections() &&
+
+
+
+ {this._renderFieldHeader('name', 'Name')}
+ {this._renderFieldHeader('createdAt', 'Date Created')}
+ {this._renderFieldHeader('updatedAt', 'Date Updated')}
+ {this._renderFieldHeader('numItems', '# sketches')}
+ |
+
+
+
+ {this.props.collections.map(collection =>
+ ())}
+
+
}
+
+ );
+ }
+}
+
+const ProjectShape = PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ name: PropTypes.string.isRequired,
+ createdAt: PropTypes.string.isRequired,
+ updatedAt: PropTypes.string.isRequired,
+ user: PropTypes.shape({
+ username: PropTypes.string.isRequired
+ }).isRequired,
+});
+
+const ItemsShape = PropTypes.shape({
+ createdAt: PropTypes.string.isRequired,
+ updatedAt: PropTypes.string.isRequired,
+ project: ProjectShape
+});
+
+CollectionList.propTypes = {
+ user: PropTypes.shape({
+ username: PropTypes.string,
+ authenticated: PropTypes.bool.isRequired
+ }).isRequired,
+ getCollections: PropTypes.func.isRequired,
+ collections: PropTypes.arrayOf(PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ name: PropTypes.string.isRequired,
+ description: PropTypes.string,
+ createdAt: PropTypes.string.isRequired,
+ updatedAt: PropTypes.string.isRequired,
+ items: PropTypes.arrayOf(ItemsShape),
+ })).isRequired,
+ username: PropTypes.string,
+ loading: PropTypes.bool.isRequired,
+ toggleDirectionForField: PropTypes.func.isRequired,
+ resetSorting: PropTypes.func.isRequired,
+ sorting: PropTypes.shape({
+ field: PropTypes.string.isRequired,
+ direction: PropTypes.string.isRequired
+ }).isRequired,
+ project: PropTypes.shape({
+ id: PropTypes.string,
+ owner: PropTypes.shape({
+ id: PropTypes.string
+ })
+ })
+};
+
+CollectionList.defaultProps = {
+ project: {
+ id: undefined,
+ owner: undefined
+ },
+ username: undefined
+};
+
+function mapStateToProps(state, ownProps) {
+ return {
+ user: state.user,
+ collections: getSortedCollections(state),
+ sorting: state.sorting,
+ loading: state.loading,
+ project: state.project,
+ projectId: ownProps && ownProps.params ? ownProps.prams.project_id : null,
+ };
+}
+
+function mapDispatchToProps(dispatch) {
+ return bindActionCreators(
+ Object.assign({}, CollectionsActions, ProjectsActions, ProjectActions, ToastActions, SortingActions),
+ dispatch
+ );
+}
+
+export default connect(mapStateToProps, mapDispatchToProps)(CollectionList);
diff --git a/client/modules/IDE/components/CollectionList/CollectionListRow.jsx b/client/modules/IDE/components/CollectionList/CollectionListRow.jsx
new file mode 100644
index 00000000..f720f628
--- /dev/null
+++ b/client/modules/IDE/components/CollectionList/CollectionListRow.jsx
@@ -0,0 +1,253 @@
+import format from 'date-fns/format';
+import PropTypes from 'prop-types';
+import React from 'react';
+import InlineSVG from 'react-inlinesvg';
+import { connect } from 'react-redux';
+import { Link } from 'react-router';
+import { bindActionCreators } from 'redux';
+import * as ProjectActions from '../../actions/project';
+import * as CollectionsActions from '../../actions/collections';
+import * as IdeActions from '../../actions/ide';
+import * as ToastActions from '../../actions/toast';
+
+const downFilledTriangle = require('../../../../images/down-filled-triangle.svg');
+
+class CollectionListRowBase extends React.Component {
+ static projectInCollection(project, collection) {
+ console.log('project in collection', project, collection);
+ return collection.items.find(item => item.project.id === project.id) != null;
+ }
+
+ constructor(props) {
+ super(props);
+ this.state = {
+ optionsOpen: false,
+ renameOpen: false,
+ renameValue: props.collection.name,
+ isFocused: false
+ };
+ }
+
+ onFocusComponent = () => {
+ this.setState({ isFocused: true });
+ }
+
+ onBlurComponent = () => {
+ this.setState({ isFocused: false });
+ setTimeout(() => {
+ if (!this.state.isFocused) {
+ this.closeAll();
+ }
+ }, 200);
+ }
+
+ openOptions = () => {
+ this.setState({
+ optionsOpen: true
+ });
+ }
+
+ closeOptions = () => {
+ this.setState({
+ optionsOpen: false
+ });
+ }
+
+ toggleOptions = () => {
+ if (this.state.optionsOpen) {
+ this.closeOptions();
+ } else {
+ this.openOptions();
+ }
+ }
+
+ openRename = () => {
+ this.setState({
+ renameOpen: true
+ });
+ }
+
+ closeRename = () => {
+ this.setState({
+ renameOpen: false
+ });
+ }
+
+ closeAll = () => {
+ this.setState({
+ renameOpen: false,
+ optionsOpen: false
+ });
+ }
+
+ handleRenameChange = (e) => {
+ this.setState({
+ renameValue: e.target.value
+ });
+ }
+
+ handleRenameEnter = (e) => {
+ if (e.key === 'Enter') {
+ // TODO pass this func
+ this.props.changeProjectName(this.props.collection.id, this.state.renameValue);
+ this.closeAll();
+ }
+ }
+
+ resetSketchName = () => {
+ this.setState({
+ renameValue: this.props.collection.name
+ });
+ }
+
+ handleDropdownOpen = () => {
+ this.closeAll();
+ this.openOptions();
+ }
+
+ handleRenameOpen = () => {
+ this.closeAll();
+ this.openRename();
+ }
+
+ handleSketchDownload = () => {
+ this.props.exportProjectAsZip(this.props.collection.id);
+ }
+
+ handleSketchDuplicate = () => {
+ this.closeAll();
+ this.props.cloneProject(this.props.collection.id);
+ }
+
+ handleSketchShare = () => {
+ this.closeAll();
+ this.props.showShareModal(this.props.collection.id, this.props.collection.name, this.props.username);
+ }
+
+ handleSketchDelete = () => {
+ this.closeAll();
+ if (window.confirm(`Are you sure you want to delete "${this.props.collection.name}"?`)) {
+ this.props.deleteProject(this.props.collection.id);
+ }
+ }
+
+ handleCollectionAdd = () => {
+ this.props.addToCollection(this.props.collection.id, this.props.project.id);
+ }
+
+ handleCollectionRemove = () => {
+ this.props.removeFromCollection(this.props.collection.id, this.props.project.id);
+ }
+
+ renderActions = () => {
+ const { optionsOpen } = this.state;
+ const userIsOwner = this.props.user.username === this.props.username;
+
+ if (this.props.addMode === true && this.props.project) {
+ return CollectionListRowBase.projectInCollection(this.props.project, this.props.collection) ? : ;
+ }
+
+ return (
+
+
+ {optionsOpen &&
+
+ {userIsOwner &&
+ -
+
+
}
+
+ }
+
+ );
+ }
+
+ renderCollectionName = () => {
+ const { addMode, collection, username } = this.props;
+ const { renameOpen, renameValue } = this.state;
+
+ if (addMode) {
+ return collection.name;
+ }
+
+ return (
+
+
+ {renameOpen ? '' : collection.name}
+
+ {renameOpen
+ &&
+ e.stopPropagation()}
+ />
+ }
+
+ )
+ }
+
+ render() {
+ const { collection } = this.props;
+
+ return (
+
+
+ {this.renderCollectionName()}
+ |
+ {format(new Date(collection.createdAt), 'MMM D, YYYY h:mm A')} |
+ {format(new Date(collection.updatedAt), 'MMM D, YYYY h:mm A')} |
+ {(collection.items || []).length} |
+
+ {this.renderActions()}
+ |
+
+ );
+ }
+}
+
+CollectionListRowBase.propTypes = {
+ addToCollection: PropTypes.func.isRequired,
+ removeFromCollection: PropTypes.func.isRequired,
+ collection: PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ name: PropTypes.string.isRequired
+ }).isRequired,
+ username: PropTypes.string.isRequired,
+ user: PropTypes.shape({
+ username: PropTypes.string,
+ authenticated: PropTypes.bool.isRequired
+ }).isRequired,
+ deleteProject: PropTypes.func.isRequired,
+ showShareModal: PropTypes.func.isRequired,
+ cloneProject: PropTypes.func.isRequired,
+ exportProjectAsZip: PropTypes.func.isRequired,
+ changeProjectName: PropTypes.func.isRequired
+};
+
+function mapDispatchToPropsSketchListRow(dispatch) {
+ return bindActionCreators(Object.assign({}, CollectionsActions, ProjectActions, IdeActions, ToastActions), dispatch);
+}
+
+export default connect(null, mapDispatchToPropsSketchListRow)(CollectionListRowBase);
diff --git a/client/modules/IDE/components/CollectionList/index.js b/client/modules/IDE/components/CollectionList/index.js
new file mode 100644
index 00000000..51d43417
--- /dev/null
+++ b/client/modules/IDE/components/CollectionList/index.js
@@ -0,0 +1 @@
+export { default } from './CollectionList';