terra-image
Version:
The terra-image component provides styling for visual imagery.
184 lines (158 loc) • 4.7 kB
JSX
import React from 'react';
import PropTypes from 'prop-types';
import objectFitImages from 'object-fit-images'; // Added polyfill for IE.
import classNames from 'classnames';
import classNamesBind from 'classnames/bind';
import ThemeContext from 'terra-theme-context';
import styles from './Image.module.scss';
import {
getDecorativeImage, getInformativeImage, ImageVariant, FitTypes,
} from './_imageHelper';
const cx = classNamesBind.bind(styles);
const propTypes = {
/**
* The source string for the image which will be loaded and displayed.
*/
src: PropTypes.string.isRequired,
/**
* Sets the style of the image from the following values; `default`, `rounded`, `circle`, `thumbnail`.
*/
variant: PropTypes.oneOf(['default', 'rounded', 'circle', 'thumbnail']),
/**
* Sets the fluid behavior of the image, which is `nonfluid` by default.
*/
isFluid: PropTypes.bool,
/**
* 
* The text content that specifies an alternative text for an image.
* `alt` prop helps to provide meaningfull context for images and should be used for creating informative images. For decorative images `alt` prop can be ignored.
* whenever `alt` prop is empty OR not defined image will be marked as decorative by default and ignored by Assistive Tools.
*/
alt: PropTypes.string,
/**
* A React element which will be displayed during loading and in case of src load failure.
*/
placeholder: PropTypes.element,
/**
* Sets the height of the image.
*/
height: PropTypes.string,
/**
* Sets the width of the image.
*/
width: PropTypes.string,
/**
* Function to be executed when the image load is successful.
*/
onLoad: PropTypes.func,
/**
* Function to be executed when the image load errors.
*/
onError: PropTypes.func,
/**
* Sets the `object-fit` style of the image from the following values; `cover`, `contain`, `fill`, `scale-down`, `none`.
*/
fit: PropTypes.oneOf(['cover', 'scale-down', 'fill', 'contain', 'none']),
};
const defaultProps = {
variant: 'default',
isFluid: false,
fit: 'fill',
};
class Image extends React.Component {
constructor(props) {
super(props);
this.ImageRef = React.createRef();
this.state = {
isLoading: true,
isError: false,
prevPropsSrc: props.src,
};
this.handleOnLoad = this.handleOnLoad.bind(this);
this.handleOnError = this.handleOnError.bind(this);
}
componentDidUpdate() {
objectFitImages(this.ImageRef.current);
}
static getDerivedStateFromProps(nextProps, prevState) {
if (nextProps.src !== prevState.prevPropsSrc) {
return {
isLoading: true,
isError: false,
prevPropsSrc: nextProps.src,
};
}
return null;
}
handleOnLoad() {
this.setState({ isLoading: false });
const { onLoad } = this.props;
if (onLoad !== undefined) {
onLoad();
}
}
handleOnError() {
this.setState({ isLoading: false, isError: true });
const { onError } = this.props;
if (onError !== undefined) {
onError();
}
}
createImage(customProps, imageClasses) {
const {
src, alt, height, width,
} = customProps;
const imageProps = (alt !== undefined && alt?.trim() !== '') ? getInformativeImage(customProps) : getDecorativeImage(customProps);
return (
<img
{...imageProps}
src={src}
alt={alt}
height={height}
width={width}
onLoad={this.handleOnLoad}
onError={this.handleOnError}
className={imageClasses}
ref={this.ImageRef}
/>
);
}
render() {
const {
variant, isFluid, placeholder, fit, ...customProps
} = this.props;
const theme = this.context;
const imageClasses = classNames(
cx(
'image',
fit,
variant,
theme.className,
{ fluid: isFluid },
{ hidden: placeholder && this.state.isLoading },
),
customProps.className,
);
delete customProps.className;
if (!this.state.isLoading) {
objectFitImages(this.ImageRef.current);
}
if (placeholder) {
if (this.state.isLoading) {
return (
<>
{this.createImage(customProps, imageClasses)}
{placeholder}
</>
);
}
return this.state.isError ? placeholder : this.createImage(customProps, imageClasses);
}
return this.createImage(customProps, imageClasses);
}
}
Image.propTypes = propTypes;
Image.defaultProps = defaultProps;
Image.contextType = ThemeContext;
export default Image;
export { ImageVariant, FitTypes };