From 32db0ff6477ed28a2b21e6c322351e65829142d4 Mon Sep 17 00:00:00 2001 From: Chris Beer <cabeer@stanford.edu> Date: Wed, 13 Feb 2019 09:25:36 -0800 Subject: [PATCH] Implement card layout for adding windows from manifests; part of #1836 --- src/components/ManifestListItem.js | 82 ++++++++++++++++++++++++------ src/components/WorkspaceAdd.js | 18 ++++--- src/containers/ManifestListItem.js | 10 +++- src/state/selectors/index.js | 11 ++++ 4 files changed, 99 insertions(+), 22 deletions(-) diff --git a/src/components/ManifestListItem.js b/src/components/ManifestListItem.js index 74faccb42..0791069ba 100644 --- a/src/components/ManifestListItem.js +++ b/src/components/ManifestListItem.js @@ -1,5 +1,13 @@ import React from 'react'; import PropTypes from 'prop-types'; +import classNames from 'classnames'; +import { withStyles } from '@material-ui/core/styles'; +import Button from '@material-ui/core/Button'; +import Card from '@material-ui/core/Card'; +import CardHeader from '@material-ui/core/CardHeader'; +import CardMedia from '@material-ui/core/CardMedia'; +import CardContent from '@material-ui/core/CardContent'; +import WindowIcon from './WindowIcon'; import ns from '../config/css-ns'; @@ -15,28 +23,72 @@ const handleOpenButtonClick = (event, manifest, addWindow) => { * @param {object} [props.manifest = string] */ -/** - * Determines which classes should be used for display, based on the state of - * the manifest - * @memberof ManifestListItem - * @private - */ -const ManifestListItem = ({ manifest, addWindow, handleClose }) => ( - <li className={ns('manifest-list-item')}> - <button type="button" onClick={(event) => { handleOpenButtonClick(event, manifest, addWindow); handleClose(); }}> - {manifest} - </button> - </li> -); +/** */ +class ManifestListItem extends React.Component { + /** */ + render() { + const { + manifestId, title, thumbnail, logo, addWindow, handleClose, classes, + } = this.props; + + return ( + <Card className={classNames(classes.card, ns('manifest-list-item'))}> + <CardHeader + avatar={ + <WindowIcon manifestLogo={logo} /> + } + title={title || manifestId} + /> + + { + thumbnail && ( + <CardMedia + className={classes.media} + image={thumbnail} + /> + ) + } + <CardContent> + <Button + color="primary" + onClick={ + (event) => { handleOpenButtonClick(event, manifestId, addWindow); handleClose(); } + } + > + {title || manifestId} + </Button> + </CardContent> + </Card> + ); + } +} ManifestListItem.propTypes = { - manifest: PropTypes.string.isRequired, // eslint-disable-line react/forbid-prop-types + manifestId: PropTypes.string.isRequired, addWindow: PropTypes.func.isRequired, handleClose: PropTypes.func, + title: PropTypes.string, + thumbnail: PropTypes.string, + logo: PropTypes.string, + classes: PropTypes.object, // eslint-disable-line react/forbid-prop-types }; ManifestListItem.defaultProps = { handleClose: () => {}, + logo: null, + classes: {}, + thumbnail: null, + title: null, }; -export default ManifestListItem; +/** */ +const styles = theme => ({ + card: { + maxWidth: 400, + }, + media: { + height: 192, + }, +}); + +export default withStyles(styles)(ManifestListItem); diff --git a/src/components/WorkspaceAdd.js b/src/components/WorkspaceAdd.js index 25461d64e..b1858624f 100644 --- a/src/components/WorkspaceAdd.js +++ b/src/components/WorkspaceAdd.js @@ -1,5 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; +import GridList from '@material-ui/core/GridList'; +import GridListTile from '@material-ui/core/GridListTile'; import ns from '../config/css-ns'; import ManifestForm from '../containers/ManifestForm'; import ManifestListItem from '../containers/ManifestListItem'; @@ -17,11 +19,13 @@ class WorkspaceAdd extends React.Component { const { manifests, setWorkspaceAddVisibility } = this.props; const manifestList = Object.keys(manifests).map(manifest => ( - <ManifestListItem - key={manifest} - manifest={manifest} - handleClose={() => setWorkspaceAddVisibility(false)} - /> + <GridListTile key={`tile_${manifest}`}> + <ManifestListItem + key={manifest} + manifestId={manifest} + handleClose={() => setWorkspaceAddVisibility(false)} + /> + </GridListTile> )); return ( @@ -29,7 +33,9 @@ class WorkspaceAdd extends React.Component { <ManifestForm id="add-form" /> - <ul>{manifestList}</ul> + <GridList cellHeight={350} cols={3}> + {manifestList} + </GridList> </div> ); } diff --git a/src/containers/ManifestListItem.js b/src/containers/ManifestListItem.js index a4614eb24..09f13486f 100644 --- a/src/containers/ManifestListItem.js +++ b/src/containers/ManifestListItem.js @@ -1,7 +1,15 @@ import { connect } from 'react-redux'; +import { getManifestTitle, getManifestLogo, getManifestThumbnail } from '../state/selectors'; import * as actions from '../state/actions'; import ManifestListItem from '../components/ManifestListItem'; +/** */ +const mapStateToProps = (state, { manifestId }) => ({ + title: getManifestTitle(state.manifests[manifestId]), + logo: getManifestLogo(state.manifests[manifestId]), + thumbnail: getManifestThumbnail(state.manifests[manifestId]), +}); + /** * mapDispatchToProps - used to hook up connect to action creators * @memberof ManifestListItem @@ -9,4 +17,4 @@ import ManifestListItem from '../components/ManifestListItem'; */ const mapDispatchToProps = { addWindow: actions.addWindow }; -export default connect(null, mapDispatchToProps)(ManifestListItem); +export default connect(mapStateToProps, mapDispatchToProps)(ManifestListItem); diff --git a/src/state/selectors/index.js b/src/state/selectors/index.js index b7ed6c2d3..bdf5010ef 100644 --- a/src/state/selectors/index.js +++ b/src/state/selectors/index.js @@ -21,6 +21,17 @@ export function getManifestLogo(manifest) { && manifest.manifestation.getLogo(); } +/** +* Return the logo of a manifest or null +* @param {object} manifest +* @return {String|null} +*/ +export function getManifestThumbnail(manifest) { + return manifest.manifestation + && manifest.manifestation.getThumbnail() + && manifest.manifestation.getThumbnail().id; +} + /** * Return the logo of a manifest or null * @param {object} manifest -- GitLab