import React, { useState } from 'react'
import PropTypes from 'prop-types'
import { makeStyles } from '@material-ui/core/styles'
import Skeleton from '@material-ui/lab/Skeleton'
import ErrorOutlineIcon from '@material-ui/icons/ErrorOutline'
import { Typography } from '@material-ui/core'
const useStyles = makeStyles(
theme => ({
root: {
width: '100%',
backgroundColor: theme.palette.common.grey,
position: 'relative',
'& > *': {
maxWidth: '100%',
maxHeight: '100%',
},
},
centered: {
position: 'absolute !important',
top: '50%',
left: '50%',
transform: 'translateX(-50%) translateY(-50%)',
},
static: {
width: '100%',
height: '100%',
},
hidden: {
visibility: 'hidden',
},
error: {
'& > *': {
display: 'inline-block',
verticalAlign: 'middle',
marginLeft: '.5em',
},
},
}),
{ name: 'image' }
)
/**
* Generic image view component.
*
* @component
*/
const Image = ({
src,
ratioX,
ratioY,
alt,
className,
loadingPlaceholder,
missingText,
errorText,
...props
}) => {
const [loaded, setLoaded] = useState(false)
const [error, setError] = useState(false)
const classes = useStyles()
const ratio = (ratioY / ratioX) * 100
const handleImageLoaded = () => {
setLoaded(true)
setError(false)
}
const handleImageError = () => {
setLoaded(true)
setError(true)
}
if (!src) {
return (
<div
className={[classes.root, className].join(' ')}
style={{ paddingBottom: `${ratio}%` }}
{...props}
>
<div className={[classes.centered, classes.error].join(' ')}>
<ErrorOutlineIcon />
<Typography>{missingText}</Typography>
</div>
</div>
)
}
return (
<div
className={[classes.root, className].join(' ')}
style={{ paddingBottom: `${ratio}%` }}
{...props}
>
<img
src={src}
alt={alt}
className={[
classes.centered,
!loaded || error ? classes.hidden : undefined,
].join(' ')}
onLoad={handleImageLoaded}
onError={handleImageError}
/>
{!loaded &&
(loadingPlaceholder || (
<Skeleton
className={classes.centered}
width="100%"
height="100%"
variant="rect"
animation="wave"
/>
))}
{loaded && error && (
<div className={[classes.centered, classes.error].join(' ')}>
<ErrorOutlineIcon />
<Typography>{errorText}</Typography>
</div>
)}
</div>
)
}
Image.defaultProps = {
missingText: 'missing.image',
errorText: 'error.image',
}
Image.propTypes = {
/** Target URL */
src: PropTypes.string,
/** Alternative text shown when image could not be loaded */
alt: PropTypes.string,
/** Ratio number for x direction */
ratioX: PropTypes.number.isRequired,
/** Ratio number for y direction */
ratioY: PropTypes.number.isRequired,
/** Placeholder shown when image is loading */
loadingPlaceholder: PropTypes.node,
/** Text displayed when no image is available. */
missingText: PropTypes.string,
/** Text displayed when the image could not be loaded. */
errorText: PropTypes.string,
/** @ignore */
className: PropTypes.string,
}
export default Image
Source