Skip to content

Instantly share code, notes, and snippets.

@qrobin
Created February 10, 2017 10:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save qrobin/0f598d17341a91da7f57b9817be9b830 to your computer and use it in GitHub Desktop.
Save qrobin/0f598d17341a91da7f57b9817be9b830 to your computer and use it in GitHub Desktop.
// (C) Copyright 2014-2016 Hewlett Packard Enterprise Development LP
import React, { Component, PropTypes } from 'react';
import classnames from 'classnames';
import CSSClassnames from '../utils/CSSClassnames';
import Props from '../utils/Props';
import Box from './Box';
import Label from './Label';
import Heading from './Heading';
import Headline from './Headline';
import Markdown from './Markdown';
import Anchor from './Anchor';
import Layer from './Layer';
import Video from './Video';
import CirclePlayIcon from './icons/base/CirclePlay';
import Responsive from '../utils/Responsive';
const CLASS_ROOT = CSSClassnames.CARD;
const LABEL_SIZES = {
xlarge: 'medium',
large: 'medium',
medium: 'medium',
small: 'medium',
xsmall: 'small'
};
const HEADLINE_SIZES = {
xlarge: 'medium',
large: 'medium'
};
const HEADING_TAGS = {
medium: 'h1',
small: 'h2',
xsmall: 'h3'
};
const PARAGRAPH_SIZES = {
xlarge: 'xlarge',
large: 'xlarge',
medium: 'large',
small: 'large',
xsmall: 'medium'
};
const PARAGRAPH_MARGINS = {
xlarge: 'large',
large: 'large',
medium: 'medium',
small: 'medium',
xsmall: 'small'
};
export default class Card extends Component {
constructor(props) {
super(props);
this._onClick = this._onClick.bind(this);
this._onResponsive = this._onResponsive.bind(this);
this.state = {
activeVideo: false,
small: false
};
}
componentDidMount() {
this._responsive = Responsive.start(this._onResponsive);
}
componentWillUnmount() {
if (this._responsive) {
this._responsive.stop();
}
}
_onResponsive(small) {
this.setState({
small: !!small
});
}
_onClick(event) {
const {video} = this.props;
if (video) {
event.preventDefault();
this.setState({
activeVideo: !this.state.activeVideo
});
}
}
_renderLabel() {
const {label, textSize} = this.props;
let result = label;
if (typeof label === 'string') {
result = (
<Label size={ LABEL_SIZES[textSize] } margin="none" uppercase={ true }>
{ label }
</Label>
);
}
return result;
}
_renderHeading() {
const {heading, headingStrong, textSize} = this.props;
let result = heading;
if (typeof heading === 'string') {
if (HEADLINE_SIZES[textSize]) {
result = (
<Headline size={ HEADLINE_SIZES[textSize] } strong={ headingStrong }>
{ heading }
</Headline>
);
} else {
result = (
<Heading tag={ HEADING_TAGS[textSize] } strong={ headingStrong }>
{ heading }
</Heading>
);
}
}
return result;
}
_renderLink() {
const {link} = this.props;
return link;
}
_renderThumbnail() {
const {direction, thumbnail, video} = this.props;
const {small} = this.state;
let result = thumbnail;
if (typeof thumbnail === 'string') {
const size = small ? 'large' : 'xlarge';
const videoIcon = (video) ?
(
<Anchor icon={ <CirclePlayIcon responsive={ false } colorIndex="brand" size={ size } /> } />
) :
undefined;
const flex = 'row' === direction ? 'grow' : undefined;
result = (
<Box className={ `${CLASS_ROOT}__thumbnail` } flex={ flex } backgroundImage={ `url(${thumbnail})` } basis="small" justify="center" align="center">
{ videoIcon }
</Box>
);
}
return result;
}
_renderVideoLayer() {
const {video} = this.props;
const {activeVideo} = this.state;
let result;
if (video && activeVideo) {
let layerContent;
if (video.source) {
layerContent = (
<Video>
<source src={ video.source } type={ `video/${video.type}` } />
</Video>
);
} else {
layerContent = video;
}
result = (
<Layer onClose={ this._onClick } closer={ true } flush={ true }>
{ layerContent }
</Layer>
);
}
return result;
}
_renderDescription() {
const {description, textSize} = this.props;
let result = description;
if (typeof description === 'string') {
const components = {
p: {
props: {
margin: PARAGRAPH_MARGINS[textSize],
size: PARAGRAPH_SIZES[textSize]
}
}
};
result = <Markdown components={ components } content={ description } />;
}
return result;
}
render() {
const {a11yTitle, children, className, contentPad, onClick, reverse, truncate} = this.props;
const boxProps = Props.pick(this.props, Object.keys(Box.propTypes));
const restProps = Props.omit(this.props, Object.keys(Card.propTypes));
const classes = classnames(
CLASS_ROOT,
{
[`${CLASS_ROOT}--selectable`]: (onClick)
},
className
);
let thumbnail = this._renderThumbnail();
let label = this._renderLabel();
let heading = this._renderHeading();
let description = this._renderDescription();
let link = this._renderLink();
let videoLayer = this._renderVideoLayer();
const contentClasses = classnames(
{
[`${CLASS_ROOT}__content`]: true,
[`${CLASS_ROOT}__content--truncate`]: truncate
}
);
const basis = 'row' === this.props.direction ? '2/3' : undefined;
const text = (
<Box className={ contentClasses } pad={ contentPad } basis={ basis }>
{ label }
{ heading }
{ description }
{ children }
{ link }
</Box>
);
let cardJustify;
if (reverse) {
// align thumbnail to bottom/right of card for bottom cardPlacement
cardJustify = 'between';
}
if (!this.props.size) {
if (this.props.direction === 'row') {
boxProps.size = {
width: 'xlarge'
};
} else {
boxProps.size = {
width: 'medium'
};
}
}
console.log(Card);
return (
<Box {...boxProps} {...restProps} className={ classes } wrap={ true } justify={ cardJustify } onClick={ onClick } a11yTitle={ a11yTitle }>
{ thumbnail }
{ text }
{ videoLayer }
</Box>
);
}
}
;
Card.propTypes = {
contentPad: Box.propTypes.pad,
description: PropTypes.oneOfType([
PropTypes.string,
PropTypes.element
]),
heading: PropTypes.oneOfType([
PropTypes.string,
PropTypes.element
]),
headingStrong: PropTypes.bool,
label: PropTypes.oneOfType([
PropTypes.string,
PropTypes.element
]),
link: PropTypes.element,
textSize: PropTypes.oneOf(['xsmall', 'small', 'medium', 'large', 'xlarge']),
thumbnail: PropTypes.oneOfType([
PropTypes.string,
PropTypes.element
]),
truncate: PropTypes.bool,
video: PropTypes.oneOfType([
PropTypes.shape({
source: PropTypes.string.isRequired,
type: PropTypes.string
}),
PropTypes.element
]),
...Box.propTypes
};
Card.defaultProps = {
a11yTitle: 'Card',
contentPad: 'medium',
headingStrong: true,
textSize: 'small'
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment