diff --git a/client/components/AddRemoveButton.jsx b/client/components/AddRemoveButton.jsx new file mode 100644 index 00000000..f0ec1272 --- /dev/null +++ b/client/components/AddRemoveButton.jsx @@ -0,0 +1,24 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import InlineSVG from 'react-inlinesvg'; + +const addIcon = require('../images/plus.svg'); +const removeIcon = require('../images/minus.svg'); + +const AddRemoveButton = ({ type, onClick }) => { + const alt = type === 'add' ? 'add to collection' : 'remove from collection'; + const icon = type === 'add' ? addIcon : removeIcon; + + return ( + + ); +}; + +AddRemoveButton.propTypes = { + type: PropTypes.oneOf(['add', 'remove']).isRequired, + onClick: PropTypes.func.isRequired, +}; + +export default AddRemoveButton; diff --git a/client/modules/IDE/components/CollectionList/CollectionListRow.jsx b/client/modules/IDE/components/CollectionList/CollectionListRow.jsx index 05ef9dce..69a04219 100644 --- a/client/modules/IDE/components/CollectionList/CollectionListRow.jsx +++ b/client/modules/IDE/components/CollectionList/CollectionListRow.jsx @@ -9,26 +9,9 @@ import * as ProjectActions from '../../actions/project'; import * as CollectionsActions from '../../actions/collections'; import * as IdeActions from '../../actions/ide'; import * as ToastActions from '../../actions/toast'; +import AddRemoveButton from '../../../../components/AddRemoveButton'; const downFilledTriangle = require('../../../../images/down-filled-triangle.svg'); -const addIcon = require('../../../../images/plus.svg'); -const removeIcon = require('../../../../images/minus.svg'); - -const AddRemoveButton = ({ type, onClick }) => { - const alt = type === 'add' ? 'add to collection' : 'remove from collection'; - const icon = type === 'add' ? addIcon : removeIcon; - - return ( - - ); -}; - -AddRemoveButton.propTypes = { - type: PropTypes.oneOf(['add', 'remove']).isRequired, - onClick: PropTypes.func.isRequired, -}; class CollectionListRowBase extends React.Component { static projectInCollection(project, collection) { diff --git a/client/modules/IDE/components/SketchList.jsx b/client/modules/IDE/components/SketchList.jsx index b430df14..09d19168 100644 --- a/client/modules/IDE/components/SketchList.jsx +++ b/client/modules/IDE/components/SketchList.jsx @@ -10,11 +10,13 @@ import classNames from 'classnames'; import slugify from 'slugify'; 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 getSortedSketches from '../selectors/projects'; import Loader from '../../App/components/loader'; +import AddRemoveButton from '../../../components/AddRemoveButton'; const arrowUp = require('../../../images/sort-arrow-up.svg'); const arrowDown = require('../../../images/sort-arrow-down.svg'); @@ -134,10 +136,100 @@ class SketchListRowBase extends React.Component { } } + renderDropdown = () => { + const { optionsOpen } = this.state; + const userIsOwner = this.props.user.username === this.props.username; + + return ( + + + {optionsOpen && + } + + ); + } + + renderAddRemoveButtons = () => { + return ( + { + this.props.inCollection ? + : + + } + + ); + } + + renderActions = () => { + return this.props.addMode === true ? this.renderAddRemoveButtons() : this.renderDropdown(); + } + render() { const { sketch, username } = this.props; - const { renameOpen, optionsOpen, renameValue } = this.state; - const userIsOwner = this.props.user.username === this.props.username; + const { renameOpen, renameValue } = this.state; let url = `/${username}/sketches/${sketch.id}`; if (username === 'p5') { url = `/${username}/sketches/${slugify(sketch.name, '_')}`; @@ -164,74 +256,7 @@ class SketchListRowBase extends React.Component { {format(new Date(sketch.createdAt), 'MMM D, YYYY h:mm A')} {format(new Date(sketch.updatedAt), 'MMM D, YYYY h:mm A')} - - - {optionsOpen && - } - + {this.renderActions()} ); } } @@ -311,6 +336,14 @@ class SketchList extends React.Component { ); } + handleCollectionAdd = (sketchId) => { + this.props.addToCollection(this.props.collection.id, sketchId); + } + + handleCollectionRemove = (sketchId) => { + this.props.removeFromCollection(this.props.collection.id, sketchId); + } + render() { const username = this.props.username !== undefined ? this.props.username : this.props.user.username; return ( @@ -337,6 +370,10 @@ class SketchList extends React.Component { sketch={sketch} user={this.props.user} username={username} + addMode={this.props.addMode} + onCollectionAdd={() => this.handleCollectionAdd(sketch.id)} + onCollectionRemove={() => this.handleCollectionRemove(sketch.id)} + inCollection={this.props.collection && this.props.collection.items.find(item => item.project.id === sketch.id) != null} />))} } @@ -365,12 +402,7 @@ SketchList.propTypes = { field: PropTypes.string.isRequired, direction: PropTypes.string.isRequired }).isRequired, - project: PropTypes.shape({ - id: PropTypes.string, - owner: PropTypes.shape({ - id: PropTypes.string - }) - }) + addMode: PropTypes.bool, }; SketchList.defaultProps = { @@ -392,7 +424,7 @@ function mapStateToProps(state) { } function mapDispatchToProps(dispatch) { - return bindActionCreators(Object.assign({}, ProjectsActions, ToastActions, SortingActions), dispatch); + return bindActionCreators(Object.assign({}, ProjectsActions, CollectionsActions, ToastActions, SortingActions), dispatch); } export default connect(mapStateToProps, mapDispatchToProps)(SketchList); diff --git a/client/modules/User/components/Collection.jsx b/client/modules/User/components/Collection.jsx index 480001e8..73ca442f 100644 --- a/client/modules/User/components/Collection.jsx +++ b/client/modules/User/components/Collection.jsx @@ -16,6 +16,8 @@ import * as IdeActions from '../../IDE/actions/ide'; import { getCollection } from '../../IDE/selectors/collections'; import Loader from '../../App/components/loader'; import EditableInput from '../../IDE/components/EditableInput'; +import Overlay from '../../App/components/Overlay'; +import SketchList from '../../IDE/components/SketchList'; const arrowUp = require('../../../images/sort-arrow-up.svg'); const arrowDown = require('../../../images/sort-arrow-down.svg'); @@ -232,6 +234,12 @@ class Collection extends React.Component { this.props.getCollections(this.props.username); this.props.resetSorting(); this._renderFieldHeader = this._renderFieldHeader.bind(this); + this.showAddSketches = this.showAddSketches.bind(this); + this.hideAddSketches = this.hideAddSketches.bind(this); + + this.state = { + isAddingSketches: false, + }; } getTitle() { @@ -318,9 +326,21 @@ class Collection extends React.Component { ); } + showAddSketches() { + this.setState({ + isAddingSketches: true, + }); + } + + hideAddSketches() { + this.setState({ + isAddingSketches: false, + }); + } + _renderEmptyTable() { if (!this.hasCollectionItems()) { - return (

No sketches in collection.

); + return (

No sketches in collection.

); } return null; } @@ -379,8 +399,19 @@ class Collection extends React.Component { />))} + +

} + { + this.state.isAddingSketches && ( + +
+ +
+
+ ) + } ); diff --git a/client/styles/components/_collection.scss b/client/styles/components/_collection.scss index 92768d38..2927ec62 100644 --- a/client/styles/components/_collection.scss +++ b/client/styles/components/_collection.scss @@ -43,3 +43,8 @@ .collection-table-wrapper { margin: #{28 / $base-font-size}rem #{56 / $base-font-size}rem; } + +.collection-add-sketch { + padding: #{24 / $base-font-size}rem; + overflow: scroll; +}