Files
dashfront/src/components/article/ArticlePhoto.jsx
2026-10-30 04:16:56 +01:00

131 lines
3.5 KiB
JavaScript

import { Component } from 'react';
import Box from '@mui/material/Box';
import { thumb } from '../../api';
import ThumbHashImage from './ThumbHashImage';
import {
SCRIM_SOFT,
} from '../../colors';
class ArticlePhoto extends Component {
constructor(props) {
super(props);
this.state = { thumbLoaded: false, smallThumbError: false };
this.handleThumbLoad = this.handleThumbLoad.bind(this);
this.handleThumbError = this.handleThumbError.bind(this);
}
componentDidUpdate(prevProps) {
if (
prevProps.photo?.fullHash !== this.props.photo?.fullHash ||
prevProps.useSmallThumb !== this.props.useSmallThumb
) {
this.setState({ thumbLoaded: false, smallThumbError: false });
}
}
handleThumbLoad() {
this.setState({ thumbLoaded: true });
}
handleThumbError() {
if (this.props.useSmallThumb && !this.state.smallThumbError) {
this.setState({ smallThumbError: true, thumbLoaded: false });
} else {
this.setState({ thumbLoaded: false });
}
}
render() {
const {
photo,
size = 200,
fill = false,
objectFit = fill ? 'cover' : 'contain',
useSmallThumb = false,
sx,
...rest
} = this.props;
const { thumbLoaded, smallThumbError } = this.state;
const fullHash = photo?.fullHash;
const hasPreview = photo?.thumbHash != null;
const aspectRatio =
photo?.thumbW != null && photo?.thumbH != null
? `${photo.thumbW} / ${photo.thumbH}`
: '1 / 1';
const imgSrc = fullHash
? useSmallThumb && !smallThumbError
? thumb.smallUrl(fullHash)
: thumb.url(fullHash)
: null;
return (
<Box
{...rest}
sx={{
position: 'relative',
...(fill
? { width: '100%', height: '100%' }
: { width: size, height: size, maxWidth: '100%', flexShrink: 0 }),
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
borderRadius: fill ? 0 : 2,
overflow: 'hidden',
bgcolor: SCRIM_SOFT,
...sx,
}}
>
{hasPreview && !thumbLoaded ? (
<Box
sx={
fill || objectFit === 'cover'
? { position: 'absolute', inset: 0 }
: {
position: 'absolute',
maxWidth: '100%',
maxHeight: '100%',
aspectRatio,
width: photo.thumbW >= photo.thumbH ? '100%' : 'auto',
height: photo.thumbH > photo.thumbW ? '100%' : 'auto',
}
}
>
<ThumbHashImage thumbHash={photo.thumbHash} />
</Box>
) : null}
{imgSrc ? (
<Box
component="img"
src={imgSrc}
alt=""
onLoad={this.handleThumbLoad}
onError={this.handleThumbError}
sx={
fill || objectFit === 'cover'
? {
display: 'block',
width: '100%',
height: '100%',
objectFit: 'cover',
opacity: thumbLoaded ? 1 : 0,
}
: {
display: 'block',
maxWidth: '100%',
maxHeight: '100%',
width: 'auto',
height: 'auto',
objectFit: 'contain',
opacity: thumbLoaded ? 1 : 0,
}
}
/>
) : null}
</Box>
);
}
}
export default ArticlePhoto;