Add sketch to collection from collection view
This commit is contained in:
parent
7f78fda073
commit
27804acd6a
5 changed files with 171 additions and 96 deletions
24
client/components/AddRemoveButton.jsx
Normal file
24
client/components/AddRemoveButton.jsx
Normal file
|
@ -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 (
|
||||||
|
<button className="overlay__close-button" onClick={onClick}>
|
||||||
|
<InlineSVG src={icon} alt={alt} />
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
AddRemoveButton.propTypes = {
|
||||||
|
type: PropTypes.oneOf(['add', 'remove']).isRequired,
|
||||||
|
onClick: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AddRemoveButton;
|
|
@ -9,26 +9,9 @@ import * as ProjectActions from '../../actions/project';
|
||||||
import * as CollectionsActions from '../../actions/collections';
|
import * as CollectionsActions from '../../actions/collections';
|
||||||
import * as IdeActions from '../../actions/ide';
|
import * as IdeActions from '../../actions/ide';
|
||||||
import * as ToastActions from '../../actions/toast';
|
import * as ToastActions from '../../actions/toast';
|
||||||
|
import AddRemoveButton from '../../../../components/AddRemoveButton';
|
||||||
|
|
||||||
const downFilledTriangle = require('../../../../images/down-filled-triangle.svg');
|
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 (
|
|
||||||
<button className="overlay__close-button" onClick={onClick}>
|
|
||||||
<InlineSVG src={icon} alt={alt} />
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
AddRemoveButton.propTypes = {
|
|
||||||
type: PropTypes.oneOf(['add', 'remove']).isRequired,
|
|
||||||
onClick: PropTypes.func.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
class CollectionListRowBase extends React.Component {
|
class CollectionListRowBase extends React.Component {
|
||||||
static projectInCollection(project, collection) {
|
static projectInCollection(project, collection) {
|
||||||
|
|
|
@ -10,11 +10,13 @@ import classNames from 'classnames';
|
||||||
import slugify from 'slugify';
|
import slugify from 'slugify';
|
||||||
import * as ProjectActions from '../actions/project';
|
import * as ProjectActions from '../actions/project';
|
||||||
import * as ProjectsActions from '../actions/projects';
|
import * as ProjectsActions from '../actions/projects';
|
||||||
|
import * as CollectionsActions from '../actions/collections';
|
||||||
import * as ToastActions from '../actions/toast';
|
import * as ToastActions from '../actions/toast';
|
||||||
import * as SortingActions from '../actions/sorting';
|
import * as SortingActions from '../actions/sorting';
|
||||||
import * as IdeActions from '../actions/ide';
|
import * as IdeActions from '../actions/ide';
|
||||||
import getSortedSketches from '../selectors/projects';
|
import getSortedSketches from '../selectors/projects';
|
||||||
import Loader from '../../App/components/loader';
|
import Loader from '../../App/components/loader';
|
||||||
|
import AddRemoveButton from '../../../components/AddRemoveButton';
|
||||||
|
|
||||||
const arrowUp = require('../../../images/sort-arrow-up.svg');
|
const arrowUp = require('../../../images/sort-arrow-up.svg');
|
||||||
const arrowDown = require('../../../images/sort-arrow-down.svg');
|
const arrowDown = require('../../../images/sort-arrow-down.svg');
|
||||||
|
@ -134,36 +136,11 @@ class SketchListRowBase extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
renderDropdown = () => {
|
||||||
const { sketch, username } = this.props;
|
const { optionsOpen } = this.state;
|
||||||
const { renameOpen, optionsOpen, renameValue } = this.state;
|
|
||||||
const userIsOwner = this.props.user.username === this.props.username;
|
const userIsOwner = this.props.user.username === this.props.username;
|
||||||
let url = `/${username}/sketches/${sketch.id}`;
|
|
||||||
if (username === 'p5') {
|
|
||||||
url = `/${username}/sketches/${slugify(sketch.name, '_')}`;
|
|
||||||
}
|
|
||||||
return (
|
return (
|
||||||
<tr
|
|
||||||
className="sketches-table__row"
|
|
||||||
key={sketch.id}
|
|
||||||
>
|
|
||||||
<th scope="row">
|
|
||||||
<Link to={url}>
|
|
||||||
{renameOpen ? '' : sketch.name}
|
|
||||||
</Link>
|
|
||||||
{renameOpen
|
|
||||||
&&
|
|
||||||
<input
|
|
||||||
value={renameValue}
|
|
||||||
onChange={this.handleRenameChange}
|
|
||||||
onKeyUp={this.handleRenameEnter}
|
|
||||||
onBlur={this.resetSketchName}
|
|
||||||
onClick={e => e.stopPropagation()}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
</th>
|
|
||||||
<td>{format(new Date(sketch.createdAt), 'MMM D, YYYY h:mm A')}</td>
|
|
||||||
<td>{format(new Date(sketch.updatedAt), 'MMM D, YYYY h:mm A')}</td>
|
|
||||||
<td className="sketch-list__dropdown-column">
|
<td className="sketch-list__dropdown-column">
|
||||||
<button
|
<button
|
||||||
className="sketch-list__dropdown-button"
|
className="sketch-list__dropdown-button"
|
||||||
|
@ -232,6 +209,54 @@ class SketchListRowBase extends React.Component {
|
||||||
</li>}
|
</li>}
|
||||||
</ul>}
|
</ul>}
|
||||||
</td>
|
</td>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderAddRemoveButtons = () => {
|
||||||
|
return (
|
||||||
|
<td className="sketch-list__dropdown-column">{
|
||||||
|
this.props.inCollection ?
|
||||||
|
<AddRemoveButton type="remove" onClick={this.props.onCollectionRemove} /> :
|
||||||
|
<AddRemoveButton type="add" onClick={this.props.onCollectionAdd} />
|
||||||
|
}
|
||||||
|
</td>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderActions = () => {
|
||||||
|
return this.props.addMode === true ? this.renderAddRemoveButtons() : this.renderDropdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { sketch, username } = this.props;
|
||||||
|
const { renameOpen, renameValue } = this.state;
|
||||||
|
let url = `/${username}/sketches/${sketch.id}`;
|
||||||
|
if (username === 'p5') {
|
||||||
|
url = `/${username}/sketches/${slugify(sketch.name, '_')}`;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<tr
|
||||||
|
className="sketches-table__row"
|
||||||
|
key={sketch.id}
|
||||||
|
>
|
||||||
|
<th scope="row">
|
||||||
|
<Link to={url}>
|
||||||
|
{renameOpen ? '' : sketch.name}
|
||||||
|
</Link>
|
||||||
|
{renameOpen
|
||||||
|
&&
|
||||||
|
<input
|
||||||
|
value={renameValue}
|
||||||
|
onChange={this.handleRenameChange}
|
||||||
|
onKeyUp={this.handleRenameEnter}
|
||||||
|
onBlur={this.resetSketchName}
|
||||||
|
onClick={e => e.stopPropagation()}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
</th>
|
||||||
|
<td>{format(new Date(sketch.createdAt), 'MMM D, YYYY h:mm A')}</td>
|
||||||
|
<td>{format(new Date(sketch.updatedAt), 'MMM D, YYYY h:mm A')}</td>
|
||||||
|
{this.renderActions()}
|
||||||
</tr>);
|
</tr>);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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() {
|
render() {
|
||||||
const username = this.props.username !== undefined ? this.props.username : this.props.user.username;
|
const username = this.props.username !== undefined ? this.props.username : this.props.user.username;
|
||||||
return (
|
return (
|
||||||
|
@ -337,6 +370,10 @@ class SketchList extends React.Component {
|
||||||
sketch={sketch}
|
sketch={sketch}
|
||||||
user={this.props.user}
|
user={this.props.user}
|
||||||
username={username}
|
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}
|
||||||
/>))}
|
/>))}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>}
|
</table>}
|
||||||
|
@ -365,12 +402,7 @@ SketchList.propTypes = {
|
||||||
field: PropTypes.string.isRequired,
|
field: PropTypes.string.isRequired,
|
||||||
direction: PropTypes.string.isRequired
|
direction: PropTypes.string.isRequired
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
project: PropTypes.shape({
|
addMode: PropTypes.bool,
|
||||||
id: PropTypes.string,
|
|
||||||
owner: PropTypes.shape({
|
|
||||||
id: PropTypes.string
|
|
||||||
})
|
|
||||||
})
|
|
||||||
};
|
};
|
||||||
|
|
||||||
SketchList.defaultProps = {
|
SketchList.defaultProps = {
|
||||||
|
@ -392,7 +424,7 @@ function mapStateToProps(state) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function mapDispatchToProps(dispatch) {
|
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);
|
export default connect(mapStateToProps, mapDispatchToProps)(SketchList);
|
||||||
|
|
|
@ -16,6 +16,8 @@ import * as IdeActions from '../../IDE/actions/ide';
|
||||||
import { getCollection } from '../../IDE/selectors/collections';
|
import { getCollection } from '../../IDE/selectors/collections';
|
||||||
import Loader from '../../App/components/loader';
|
import Loader from '../../App/components/loader';
|
||||||
import EditableInput from '../../IDE/components/EditableInput';
|
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 arrowUp = require('../../../images/sort-arrow-up.svg');
|
||||||
const arrowDown = require('../../../images/sort-arrow-down.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.getCollections(this.props.username);
|
||||||
this.props.resetSorting();
|
this.props.resetSorting();
|
||||||
this._renderFieldHeader = this._renderFieldHeader.bind(this);
|
this._renderFieldHeader = this._renderFieldHeader.bind(this);
|
||||||
|
this.showAddSketches = this.showAddSketches.bind(this);
|
||||||
|
this.hideAddSketches = this.hideAddSketches.bind(this);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
isAddingSketches: false,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
getTitle() {
|
getTitle() {
|
||||||
|
@ -318,9 +326,21 @@ class Collection extends React.Component {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
showAddSketches() {
|
||||||
|
this.setState({
|
||||||
|
isAddingSketches: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
hideAddSketches() {
|
||||||
|
this.setState({
|
||||||
|
isAddingSketches: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
_renderEmptyTable() {
|
_renderEmptyTable() {
|
||||||
if (!this.hasCollectionItems()) {
|
if (!this.hasCollectionItems()) {
|
||||||
return (<p className="sketches-table__empty">No sketches in collection.</p>);
|
return (<p className="sketches-table__empty">No sketches in collection. <button onClick={this.showAddSketches}>Add some sketches</button></p>);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -379,8 +399,19 @@ class Collection extends React.Component {
|
||||||
/>))}
|
/>))}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
<p><button onClick={this.showAddSketches}>Add more sketches</button></p>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
this.state.isAddingSketches && (
|
||||||
|
<Overlay title="Add sketches" closeOverlay={this.hideAddSketches}>
|
||||||
|
<div className="collection-add-sketch">
|
||||||
|
<SketchList username={this.props.username} addMode collection={this.props.collection} />
|
||||||
|
</div>
|
||||||
|
</Overlay>
|
||||||
|
)
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
|
|
|
@ -43,3 +43,8 @@
|
||||||
.collection-table-wrapper {
|
.collection-table-wrapper {
|
||||||
margin: #{28 / $base-font-size}rem #{56 / $base-font-size}rem;
|
margin: #{28 / $base-font-size}rem #{56 / $base-font-size}rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.collection-add-sketch {
|
||||||
|
padding: #{24 / $base-font-size}rem;
|
||||||
|
overflow: scroll;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue