Remove popover and "add to collection" code from SketchList

This commit is contained in:
Andrew Nicolaou 2019-11-10 17:07:53 +01:00
parent ad13684fe3
commit 161725cb28
4 changed files with 52 additions and 300 deletions

View file

@ -1,144 +0,0 @@
import PropTypes from 'prop-types';
import React from 'react';
import InlineSVG from 'react-inlinesvg';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as CollectionsActions from '../../actions/collections';
import getSortedCollections from '../../selectors/collections';
import exitUrl from '../../../../images/exit.svg';
import Loader from '../../../App/components/loader';
import { Searchbar } from '../Searchbar';
import Item from './Item';
const NoCollections = () => (
<div className="collection-popover__empty">
<p>No collections</p>
</div>);
const projectInCollection = (project, collection) => (
collection.items.find(item => item.project.id === project.id) != null
);
const CollectionPopover = ({
loading, onClose, project, collections, addToCollection, removeFromCollection, getCollections, user
}) => {
const [didLoadData, setDidLoadData] = React.useState(null);
const [searchTerm, setSearchTerm] = React.useState('');
const filteredCollections = searchTerm === '' ?
collections :
collections.filter(({ name }) => name.toUpperCase().includes(searchTerm.toUpperCase()));
React.useEffect(() => {
getCollections(user.username);
}, [user]);
React.useEffect(() => {
if (didLoadData === true) {
return;
}
if (loading && didLoadData === null) {
setDidLoadData(false);
} else if (!loading && didLoadData === false) {
setDidLoadData(true);
}
}, [loading]);
const handleAddToCollection = (collectionId) => {
addToCollection(collectionId, project.id);
};
const handleRemoveFromCollection = (collectionId) => {
removeFromCollection(collectionId, project.id);
};
let content = null;
if (didLoadData && collections.length === 0) {
content = <NoCollections />;
} else if (didLoadData) {
content = (
<ul>
{
filteredCollections.map((collection) => {
const inCollection = projectInCollection(project, collection);
const handleSelect = inCollection ? handleRemoveFromCollection : handleAddToCollection;
return (
<Item
inCollection={inCollection}
key={collection.id}
collection={collection}
onSelect={() => handleSelect(collection.id)}
/>
);
})
}
</ul>
);
} else {
content = <Loader />;
}
return (
<div className="collection-popover">
<div className="collection-popover__header">
<h4>Add to collection</h4>
<button className="collection-popover__exit-button" onClick={onClose}>
<InlineSVG src={exitUrl} alt="Close Add to Collection" />
</button>
</div>
<div className="collection-popover__filter">
<Searchbar
searchLabel="Search collections..."
searchTerm={searchTerm}
setSearchTerm={setSearchTerm}
resetSearchTerm={() => setSearchTerm('')}
/>
</div>
<div className="collection-popover__items">
{content}
</div>
</div>
);
};
CollectionPopover.propTypes = {
loading: PropTypes.bool.isRequired,
onClose: PropTypes.func.isRequired,
getCollections: PropTypes.func.isRequired,
addToCollection: PropTypes.func.isRequired,
removeFromCollection: PropTypes.func.isRequired,
user: PropTypes.shape({
username: PropTypes.string.isRequired,
}).isRequired,
collections: PropTypes.arrayOf(PropTypes.shape({
name: PropTypes.string,
})).isRequired,
project: PropTypes.shape({
id: PropTypes.string,
}).isRequired,
};
function mapStateToProps(state, ownProps) {
return {
user: state.user,
collections: getSortedCollections(state),
loading: state.loading,
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(
Object.assign({}, CollectionsActions),
dispatch
);
}
export default connect(mapStateToProps, mapDispatchToProps)(CollectionPopover);

View file

@ -1,37 +0,0 @@
import PropTypes from 'prop-types';
import React from 'react';
import { Link } from 'react-router';
import InlineSVG from 'react-inlinesvg';
import check from '../../../../images/check.svg';
const checkIcon = (
<InlineSVG className="sketch-list__check-icon" src={check} alt="In collection" />
);
const CollectionItem = ({ inCollection, collection, onSelect }) => (
<li className="collection-popover__item">
<div className="collection-popover__item__info">
{inCollection && checkIcon}
<button onClick={onSelect}>
{collection.name}
</button>
</div>
<div className="collection-popover__item__view">
<Link className="collection-popover__item__view-button" to={`/${collection.owner.username}/collections/${collection.id}`}>View</Link>
</div>
</li>
);
CollectionItem.propTypes = {
inCollection: PropTypes.bool.isRequired,
onSelect: PropTypes.func.isRequired,
collection: PropTypes.shape({
name: PropTypes.string,
}).isRequired,
};
export default CollectionItem;

View file

@ -1 +0,0 @@
export { default } from './CollectionPopover.jsx';

View file

@ -16,32 +16,12 @@ 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 CollectionPopover from './CollectionPopover'; import Overlay from '../../App/components/Overlay';
import AddToCollectionList from './AddToCollectionList';
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');
const downFilledTriangle = require('../../../images/down-filled-triangle.svg'); const downFilledTriangle = require('../../../images/down-filled-triangle.svg');
const check = require('../../../images/check_encircled.svg');
const close = require('../../../images/close.svg');
const Icons = ({ inCollection }) => {
const classes = [
'sketch-list__icon',
inCollection ? 'sketch-list__icon--in-collection' : 'sketch-list__icon--not-in-collection'
].join(' ');
return (
<div className={classes}>
<InlineSVG className="sketch-list__remove-icon" src={close} alt="Remove from collection" />
<InlineSVG className="sketch-list__in-icon" src={check} alt="In collection" />
<InlineSVG className="sketch-list__add-icon" src={close} alt="Add to collection" />
</div>
);
};
Icons.propTypes = {
inCollection: PropTypes.bool.isRequired,
};
class SketchListRowBase extends React.Component { class SketchListRowBase extends React.Component {
constructor(props) { constructor(props) {
@ -51,7 +31,6 @@ class SketchListRowBase extends React.Component {
renameOpen: false, renameOpen: false,
renameValue: props.sketch.name, renameValue: props.sketch.name,
isFocused: false, isFocused: false,
showPopover: false,
}; };
} }
@ -141,18 +120,6 @@ class SketchListRowBase extends React.Component {
this.props.exportProjectAsZip(this.props.sketch.id); this.props.exportProjectAsZip(this.props.sketch.id);
} }
handleShowCollectionPopover = () => {
this.setState({
showPopover: true
});
}
handleCloseCollectionPopover = () => {
this.setState({
showPopover: false
});
}
handleSketchDuplicate = () => { handleSketchDuplicate = () => {
this.closeAll(); this.closeAll();
this.props.cloneProject(this.props.sketch.id); this.props.cloneProject(this.props.sketch.id);
@ -170,18 +137,6 @@ class SketchListRowBase extends React.Component {
} }
} }
handleRowClick = (evt) => {
if (!this.props.addMode) {
return;
}
if (this.props.inCollection) {
this.props.onCollectionRemove();
} else {
this.props.onCollectionAdd();
}
}
renderViewButton = sketchURL => ( renderViewButton = sketchURL => (
<td className="sketch-list__dropdown-column"> <td className="sketch-list__dropdown-column">
<Link to={sketchURL}>View</Link> <Link to={sketchURL}>View</Link>
@ -242,7 +197,10 @@ class SketchListRowBase extends React.Component {
<li> <li>
<button <button
className="sketch-list__action-option" className="sketch-list__action-option"
onClick={this.handleShowCollectionPopover} onClick={() => {
this.props.onAddToCollection();
this.closeAll();
}}
onBlur={this.onBlurComponent} onBlur={this.onBlurComponent}
onFocus={this.onFocusComponent} onFocus={this.onFocusComponent}
> >
@ -271,24 +229,14 @@ class SketchListRowBase extends React.Component {
</button> </button>
</li>} </li>}
</ul>} </ul>}
{this.state.showPopover &&
<CollectionPopover
onClose={this.handleCloseCollectionPopover}
project={this.props.sketch}
/>
}
</td> </td>
); );
} }
renderActions = sketchURL => (this.props.addMode === true ? this.renderViewButton(sketchURL) : this.renderDropdown())
render() { render() {
const { const {
sketch, sketch,
username, username,
addMode,
inCollection,
} = this.props; } = this.props;
const { renameOpen, renameValue } = this.state; const { renameOpen, renameValue } = this.state;
let url = `/${username}/sketches/${sketch.id}`; let url = `/${username}/sketches/${sketch.id}`;
@ -296,53 +244,43 @@ class SketchListRowBase extends React.Component {
url = `/${username}/sketches/${slugify(sketch.name, '_')}`; url = `/${username}/sketches/${slugify(sketch.name, '_')}`;
} }
const name = addMode ? const name = (
<span className="sketches-table__name">{sketch.name}</span> <React.Fragment>
: ( <Link to={url}>
<React.Fragment> {renameOpen ? '' : sketch.name}
<Link to={url}> </Link>
{renameOpen ? '' : sketch.name} {renameOpen
</Link> &&
{renameOpen <input
&& value={renameValue}
<input onChange={this.handleRenameChange}
value={renameValue} onKeyUp={this.handleRenameEnter}
onChange={this.handleRenameChange} onBlur={this.resetSketchName}
onKeyUp={this.handleRenameEnter} onClick={e => e.stopPropagation()}
onBlur={this.resetSketchName} />
onClick={e => e.stopPropagation()} }
/> </React.Fragment>
} );
</React.Fragment>
);
return ( return (
<React.Fragment> <React.Fragment>
<tr <tr
className={`sketches-table__row ${addMode ? 'sketches-table__row--is-add-mode' : ''}`} className="sketches-table__row"
key={sketch.id} key={sketch.id}
onClick={this.handleRowClick} onClick={this.handleRowClick}
> >
{
this.props.addMode &&
<td className="sketches-table__icon-cell">
<Icons inCollection={inCollection} />
</td>
}
<th scope="row"> <th scope="row">
{name} {name}
</th> </th>
{!this.props.addMode && <td>{format(new Date(sketch.createdAt), 'MMM D, YYYY h:mm A')}</td>} <td>{format(new Date(sketch.createdAt), 'MMM D, YYYY h:mm A')}</td>
{!this.props.addMode && <td>{format(new Date(sketch.updatedAt), 'MMM D, YYYY h:mm A')}</td>} <td>{format(new Date(sketch.updatedAt), 'MMM D, YYYY h:mm A')}</td>
{this.renderActions(url)} {this.renderDropdown()}
</tr> </tr>
</React.Fragment>); </React.Fragment>);
} }
} }
SketchListRowBase.propTypes = { SketchListRowBase.propTypes = {
addMode: PropTypes.bool.isRequired,
inCollection: PropTypes.bool.isRequired,
sketch: PropTypes.shape({ sketch: PropTypes.shape({
id: PropTypes.string.isRequired, id: PropTypes.string.isRequired,
name: PropTypes.string.isRequired name: PropTypes.string.isRequired
@ -357,8 +295,7 @@ SketchListRowBase.propTypes = {
cloneProject: PropTypes.func.isRequired, cloneProject: PropTypes.func.isRequired,
exportProjectAsZip: PropTypes.func.isRequired, exportProjectAsZip: PropTypes.func.isRequired,
changeProjectName: PropTypes.func.isRequired, changeProjectName: PropTypes.func.isRequired,
onCollectionAdd: PropTypes.func.isRequired, onAddToCollection: PropTypes.func.isRequired,
onCollectionRemove: PropTypes.func.isRequired,
}; };
function mapDispatchToPropsSketchListRow(dispatch) { function mapDispatchToPropsSketchListRow(dispatch) {
@ -435,14 +372,6 @@ 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 (
@ -454,16 +383,14 @@ class SketchList extends React.Component {
{this._renderEmptyTable()} {this._renderEmptyTable()}
{this.hasSketches() && {this.hasSketches() &&
<table className="sketches-table" summary="table containing all saved projects"> <table className="sketches-table" summary="table containing all saved projects">
{!this.props.addMode && <thead>
<thead> <tr>
<tr> {this._renderFieldHeader('name', 'Sketch')}
{this._renderFieldHeader('name', 'Sketch')} {this._renderFieldHeader('createdAt', 'Date Created')}
{this._renderFieldHeader('createdAt', 'Date Created')} {this._renderFieldHeader('updatedAt', 'Date Updated')}
{this._renderFieldHeader('updatedAt', 'Date Updated')} <th scope="col"></th>
<th scope="col"></th> </tr>
</tr> </thead>
</thead>
}
<tbody> <tbody>
{this.props.sketches.map(sketch => {this.props.sketches.map(sketch =>
(<SketchListRow (<SketchListRow
@ -471,14 +398,25 @@ 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} onAddToCollection={() => {
onCollectionAdd={() => this.handleCollectionAdd(sketch.id)} this.setState({ sketchToAddToCollection: sketch });
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>}
{
this.state.sketchToAddToCollection &&
<Overlay
title="Add to collection"
closeOverlay={() => this.setState({ sketchToAddToCollection: null })}
>
<AddToCollectionList
project={this.state.sketchToAddToCollection}
username={this.props.username}
user={this.props.user}
/>
</Overlay>
}
</div> </div>
); );
} }
@ -513,13 +451,9 @@ SketchList.propTypes = {
field: PropTypes.string.isRequired, field: PropTypes.string.isRequired,
direction: PropTypes.string.isRequired direction: PropTypes.string.isRequired
}).isRequired, }).isRequired,
addToCollection: PropTypes.func.isRequired,
removeFromCollection: PropTypes.func.isRequired,
addMode: PropTypes.bool,
}; };
SketchList.defaultProps = { SketchList.defaultProps = {
addMode: false,
username: undefined username: undefined
}; };