wix-style-react
Version:
249 lines (212 loc) • 7.03 kB
JavaScript
import React from 'react';
import PropTypes from 'prop-types';
import { st, classes } from './Thumbnail.st.css';
import CheckboxChecked from 'wix-ui-icons-common/system/CheckboxChecked';
import Text from '../Text';
import { withFocusable } from 'wix-ui-core/dist/src/hocs/Focusable/FocusableHOC';
import { dataHooks } from './constants';
import Box from '../Box';
import { FontUpgradeContext } from '../FontUpgrade/context';
const isString = a => typeof a === 'string';
/**
* # Thumbnail
* Component for showing thumbnails
*
* It takes full space of parent component, works well together with `<Proportion/>`
* */
class Thumbnail extends React.PureComponent {
static displayName = 'Thumbnail';
static propTypes = {
/** Applies a data-hook HTML attribute that can be used in the tests. */
dataHook: PropTypes.string,
/** Specifies a CSS class name to be appended to the component’s root element. */
className: PropTypes.string,
/** Renders specified children items inside of the thumbnail. Accepts any kind of content. When passed, title will be rendered below a thumbnail. */
children: PropTypes.node,
/** Sets the title of a thumbnail. */
title: PropTypes.node,
/** Sets thumbnail description. */
description: PropTypes.node,
/** Specifies an image to be displayed inside of a thumbnail.
* If given as string, it will be used within `<img/>`.
* Otherwise can be given as React.Node
*/
image: PropTypes.node,
/** Controls the size of a thumbnail. */
size: PropTypes.oneOf(['tiny', 'small', 'medium']),
/** Specifies whether a thumbnail is selected. */
selected: PropTypes.bool,
/** Specifies whether thumbnail interactions are disabled. */
disabled: PropTypes.bool,
/** Hides a checkmark icon that indicates selected thumbnail. Selected option will be highlighted with border only. */
hideSelectedIcon: PropTypes.bool,
/** Render image as a background of a thumbnail. As a result, title is rendered below the image thumbnail. */
backgroundImage: PropTypes.node,
/** Defines a callback function which is called every time a thumbnail is clicked. */
onClick: PropTypes.func,
/** Controls the width of a thumbnail. */
width: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
/** Controls the height of a thumbnail. */
height: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
/** Controls vertical alignment of content. */
contentAlignment: PropTypes.oneOf(['top', 'center']),
};
static defaultProps = {
size: 'medium',
selected: false,
disabled: false,
contentAlignment: 'center',
};
_renderBottomTitle = () => {
const { size, title, selected, disabled } = this.props;
return (
<Box align="center">
<Text
className={st(classes.bottomTitle, { selected, disabled })}
dataHook={dataHooks.thumbnailBottomTitle}
size={size}
tagName="div"
weight="thin"
ellipsis
children={title}
/>
</Box>
);
};
_renderBackgroundLayout = () => {
const { disabled } = this.props;
return isString(this.props.backgroundImage) ? (
<div
className={st(classes.backgroundImage, { disabled })}
data-hook={dataHooks.thumbnailBackgroundImage}
style={{ backgroundImage: `url(${this.props.backgroundImage})` }}
/>
) : (
this.props.backgroundImage
);
};
_renderNoBackgroundLayout = () => {
const { title, description, image, size, disabled } = this.props;
return (
<div>
{image && (
<div
className={classes.image}
data-hook={dataHooks.thumbnailImage}
children={isString(image) ? <img src={image} /> : image}
/>
)}
{title && (
<FontUpgradeContext.Consumer>
{({ active }) => (
<Text
className={classes.title}
dataHook={dataHooks.thumbnailTitle}
size={size}
tagName="div"
weight={active && size === 'tiny' ? 'bold' : 'normal'}
children={title}
skin={disabled ? 'disabled' : undefined}
/>
)}
</FontUpgradeContext.Consumer>
)}
{description && (
<Text
className={classes.description}
dataHook={dataHooks.thumbnailDescription}
size={size}
weight="thin"
tagName="div"
secondary
children={description}
skin={disabled ? 'disabled' : undefined}
/>
)}
</div>
);
};
_renderThumbnailContent = () => {
const { backgroundImage, children, disabled } = this.props;
const hasBackground = !!backgroundImage;
const hasChildren = !!children;
if (hasChildren) {
return (
<div className={st(classes.customChild, { disabled })}>{children}</div>
);
}
if (hasBackground) {
return this._renderBackgroundLayout();
}
return this._renderNoBackgroundLayout();
};
_renderSelectedIcon = () => (
<div
className={classes.selectedIcon}
data-hook={dataHooks.thumbnailSelectedIcon}
>
<CheckboxChecked height="7.8" width="10" />
</div>
);
_onKeyDown = event =>
[13 /* enter */, 32 /* space */].some(key => event.keyCode === key) &&
this.props.onClick(event);
render() {
const {
children,
dataHook,
size,
selected,
disabled,
title,
backgroundImage,
onClick,
hideSelectedIcon,
width,
height,
contentAlignment,
focusableOnFocus,
focusableOnBlur,
className,
} = this.props;
const hasChildren = !!children;
const hasBackground = !!backgroundImage;
const showBottomTitle = (hasChildren || hasBackground) && title;
return (
<div
style={{ width }}
className={st(classes.root, { disabled })}
onClick={disabled ? null : onClick}
onKeyDown={disabled ? null : this._onKeyDown}
data-hook={dataHook}
>
<div
style={{ height }}
className={st(
classes.thumbnail,
{
selected,
disabled,
size,
hasBackground,
hasChildren,
contentAlignment,
},
className,
)}
data-selected={selected}
data-disabled={disabled}
onFocus={focusableOnFocus}
onBlur={focusableOnBlur}
tabIndex={disabled ? null : 0}
data-hook={dataHooks.thumbnailWrapper}
>
{!hideSelectedIcon && selected && this._renderSelectedIcon()}
{this._renderThumbnailContent()}
</div>
{showBottomTitle ? this._renderBottomTitle() : null}
</div>
);
}
}
export default withFocusable(Thumbnail);