UNPKG

backpack-ui

Version:
439 lines (379 loc) 9.58 kB
import React from "react"; import PropTypes from "prop-types"; import radium from "radium"; import truncate from "truncate"; import moment from "moment"; import { Link } from "react-router"; import colors from "../../styles/colors"; import mq from "../../styles/mq"; import { gutter, percentage, span, } from "../../utils/grid"; import BookmarkButton from "../bookmarkButton"; import MoreLink from "../moreLink"; const containerWidth = span(8, "static"); const imageWidth = span(2, "static"); const contentWidth = span(6, "static"); const styles = { image: { base: { position: "relative", width: percentage("78px", "335px"), [`@media (max-width: ${mq.max[768]})`]: { float: "right", marginTop: ".9rem", }, [`@media (min-width: ${mq.min[768]})`]: { position: "absolute", right: 0, top: 0, width: percentage(imageWidth, containerWidth), }, }, img: { display: "block", float: "right", }, }, content: { base: { position: "relative", }, }, info: { base: { overflow: "hidden", }, }, header: { base: { overflow: "hidden", [`@media (min-width: ${mq.min[768]})`]: { marginRight: percentage(gutter("static"), containerWidth), width: percentage(contentWidth, containerWidth), }, }, }, category: { base: { color: colors.accentGray, fontSize: "1rem", lineHeight: 1, marginBottom: ".5rem", textTransform: "uppercase", [`@media (max-width: ${mq.max[768]})`]: { letterSpacing: ".4px", marginTop: ".1rem", }, [`@media (min-width: ${mq.min[768]})`]: { fontSize: "1.2rem", marginBottom: "1rem", }, }, sponsored: { color: colors.accentYellow, fontWeight: 600, }, topChoice: { color: colors.accentRed, fontWeight: 600, }, location: { [`@media (max-width: ${mq.max[768]})`]: { display: "none", }, }, }, title: { base: { color: colors.textPrimary, float: "left", fontSize: "2rem", fontWeight: 600, letterSpacing: "-1px", lineHeight: (24 / 20), margin: 0, maxWidth: "76%", [`@media (min-width: ${mq.min[768]})`]: { fontSize: "2.8rem", lineHeight: (34 / 28), maxWidth: "100%", textOverflow: "ellipsis", overflow: "hidden", whiteSpace: "nowrap", }, }, }, bookmark: { base: { [`@media (max-width: ${mq.max[768]})`]: { bottom: "-2rem", position: "absolute", right: "-.4rem", }, [`@media (min-width: ${mq.min[768]})`]: { display: "inline-block", marginLeft: ".5rem", marginTop: ".3rem", }, }, }, description: { base: { color: colors.textPrimary, float: "left", fontSize: "1.4rem", lineHeight: (24 / 14), marginTop: ".9rem", width: percentage("242px", "335px"), fontWeight: 300, [`@media (min-width: ${mq.min[768]})`]: { fontSize: "1.8rem", lineHeight: (32 / 18), marginRight: percentage(gutter("static"), containerWidth), marginTop: "1.4rem", width: percentage(contentWidth, containerWidth), }, }, clamp: { display: "-webkit-box", overflow: "hidden", textOverflow: "ellipsis", WebkitBoxOrient: "vertical", WebkitLineClamp: 2, }, }, link: { image: { [`@media (max-width: ${mq.max[1024]})`]: { display: "none", }, [`@media (min-width: ${mq.min[1024]})`]: { marginTop: "2rem", }, }, description: { [`@media (max-width: ${mq.max[768]})`]: { marginTop: "1.2rem", }, [`@media (min-width: ${mq.min[768]}) and (max-width: ${mq.max[1024]})`]: { marginTop: "1.9rem", }, [`@media (min-width: ${mq.min[1024]})`]: { display: "none", }, }, }, }; function ListItem({ title, slug, subtype, place, image, link, description, sponsored, topChoice, bookmark, bookmarkSize, ad, date, id, onClick, }) { const shortDescription = typeof description === "string" && truncate(description.replace(/(<([^>]+)>)/ig, ""), 215); const showLink = link.title && link.url; const ListItemLink = showLink && ( <div className="ListItem-link" style={styles.link.image}> <MoreLink href={link.url} caps> {link.title} </MoreLink> </div> ); return ( <div className="ListItem" id={id}> <div className="ListItem-content" style={styles.content.base}> <header className="ListItem-header" style={styles.header.base}> <div className="ListItem-category" style={styles.category.base}> {date && <time dateTime={moment(date).format("YYYY-MM-DD")}> {moment(date).format("D MMMM YYYY")} </time> } {!date && sponsored && ad && <span style={styles.category.sponsored}> Sponsored {ad} </span> } {!date && topChoice && <span style={styles.category.topChoice}> Top Choice </span> } {!date && subtype} {!date && place.name && <span style={styles.category.location}> in {place.name}</span> } </div> <h2 className="ListItem-title" style={styles.title.base}> <Link to={`${slug}`} onClick={onClick} style={{ color: "currentColor" }}> {title} </Link> </h2> {bookmark && <div className="ListItem-bookmark" style={styles.bookmark.base}> <BookmarkButton onClick={null} size={bookmarkSize} /> </div> } </header> {description && <div className="ListItem-info" style={styles.info.base}> <div className="ListItem-description" style={styles.description.base}> <div style={styles.description.clamp} dangerouslySetInnerHTML={{ __html: shortDescription }} /> {showLink && <div className="ListItem-link" style={styles.link.description}> <MoreLink href={link.url} caps> {link.title} </MoreLink> </div> } </div> {image.path && showLink && <div className="ListItem-image" style={styles.image.base}> <div style={styles.image.img}> <Link to={`${slug}`} onClick={onClick} style={{ display: "block" }}> <img src={image.path} alt="" /> </Link> {ListItemLink} </div> </div> } {image.path && !showLink && <div className="ListItem-image" style={styles.image.base}> <Link to={`${slug}`} onClick={onClick} style={styles.image.img}> <img src={image.path} alt="" /> </Link> </div> } {!image.path && showLink && <div className="ListItem-image" style={[ styles.image.base, { paddingLeft: "20px" }, ]} > {ListItemLink} </div> } </div> } </div> </div> ); } ListItem.propTypes = { /** * The name of the POI */ title: PropTypes.string.isRequired, /** * The URL slug of the POI */ slug: PropTypes.string.isRequired, /** * The subtype of POI; i.e. Museum */ subtype: PropTypes.string.isRequired, /** * The place data for the POI; required keys are name and type */ place: PropTypes.shape({ name: PropTypes.string, type: PropTypes.string, }).isRequired, /** * Image src for the POI; required keys are path and orientation */ image: PropTypes.shape({ path: PropTypes.string, orientation: PropTypes.oneOf([ "", "portrait", "landscape", ]), }), /** * Link to display under image; required keys are title and url */ link: PropTypes.shape({ title: PropTypes.string, url: PropTypes.string, }), /** * Description for POI */ description: PropTypes.string, /** * Add a "sponsored" label */ sponsored: PropTypes.bool, /** * Add a "top choice" label */ topChoice: PropTypes.bool, /** * If list item is able to be bookmarked */ bookmark: PropTypes.bool, /** * Size of bookmark component */ bookmarkSize: PropTypes.oneOf([ "small", "large", ]), /** * Google DFP ad; sponsored must be true */ ad: PropTypes.string, /** * Publish date for news article */ date: PropTypes.string, /** * Unique ID for item */ id: PropTypes.string, onClick: PropTypes.func, }; ListItem.defaultProps = { title: "", slug: "", type: "", subtype: "", place: {}, image: {}, link: {}, description: "", sponsored: false, topChoice: false, bookmark: false, bookmarkSize: "small", ad: "", date: "", id: "", }; ListItem.styles = styles; export default radium(ListItem);