2022-11-18 11:54:06 +08:00
|
|
|
import React from 'react';
|
|
|
|
import classNames from 'classnames';
|
|
|
|
import { Modal, Carousel } from 'antd';
|
|
|
|
|
2022-12-07 18:26:29 +08:00
|
|
|
function isGood(className: string): boolean {
|
2022-11-18 11:54:06 +08:00
|
|
|
return /\bgood\b/i.test(className);
|
|
|
|
}
|
|
|
|
|
2022-12-07 18:26:29 +08:00
|
|
|
function isBad(className: string): boolean {
|
2022-11-18 11:54:06 +08:00
|
|
|
return /\bbad\b/i.test(className);
|
|
|
|
}
|
|
|
|
|
2022-12-07 18:26:29 +08:00
|
|
|
function isInline(className: string): boolean {
|
2022-11-18 11:54:06 +08:00
|
|
|
return /\binline\b/i.test(className);
|
|
|
|
}
|
|
|
|
|
2022-12-07 18:26:29 +08:00
|
|
|
function isGoodBadImg(imgMeta: any): boolean {
|
|
|
|
return imgMeta.isGood || imgMeta.isBad;
|
|
|
|
}
|
|
|
|
|
|
|
|
function isCompareImg(imgMeta: any): boolean {
|
|
|
|
return isGoodBadImg(imgMeta) || imgMeta.inline;
|
|
|
|
}
|
|
|
|
|
|
|
|
interface PreviewImageBoxProps {
|
|
|
|
coverMeta: any;
|
|
|
|
cover: React.ReactNode;
|
|
|
|
imgs: React.ReactNode[];
|
|
|
|
style: React.CSSProperties;
|
|
|
|
previewVisible: boolean;
|
|
|
|
comparable: boolean;
|
|
|
|
onClick: () => void;
|
|
|
|
onCancel: () => void;
|
|
|
|
}
|
|
|
|
|
|
|
|
const PreviewImageBox: React.FC<PreviewImageBoxProps> = (props) => {
|
|
|
|
const { cover, coverMeta, imgs, style, previewVisible, comparable, onClick, onCancel } = props;
|
2022-11-18 11:54:06 +08:00
|
|
|
const onlyOneImg = comparable || imgs.length === 1;
|
|
|
|
const imageWrapperClassName = classNames('preview-image-wrapper', {
|
|
|
|
good: coverMeta.isGood,
|
|
|
|
bad: coverMeta.isBad,
|
|
|
|
});
|
|
|
|
return (
|
|
|
|
<div className="preview-image-box" style={style}>
|
|
|
|
<div onClick={onClick} className={imageWrapperClassName}>
|
|
|
|
<img className={coverMeta.className} src={coverMeta.src} alt={coverMeta.alt} />
|
|
|
|
</div>
|
|
|
|
<div className="preview-image-title">{coverMeta.alt}</div>
|
|
|
|
<div
|
|
|
|
className="preview-image-description"
|
|
|
|
dangerouslySetInnerHTML={{ __html: coverMeta.description }}
|
|
|
|
/>
|
|
|
|
<Modal
|
|
|
|
className="image-modal"
|
|
|
|
width={960}
|
|
|
|
visible={previewVisible}
|
|
|
|
title={null}
|
|
|
|
footer={null}
|
|
|
|
onCancel={onCancel}
|
|
|
|
>
|
|
|
|
<Carousel
|
|
|
|
className={`${onlyOneImg ? 'image-modal-single' : ''}`}
|
|
|
|
draggable={!onlyOneImg}
|
|
|
|
adaptiveHeight
|
|
|
|
>
|
|
|
|
{comparable ? cover : imgs}
|
|
|
|
</Carousel>
|
|
|
|
<div className="preview-image-title">{coverMeta.alt}</div>
|
|
|
|
</Modal>
|
|
|
|
</div>
|
|
|
|
);
|
2022-12-07 18:26:29 +08:00
|
|
|
};
|
2022-11-18 11:54:06 +08:00
|
|
|
|
2022-12-07 18:26:29 +08:00
|
|
|
interface ImagePreviewProps {
|
|
|
|
imgs: any[];
|
2022-11-18 11:54:06 +08:00
|
|
|
}
|
|
|
|
|
2022-12-07 18:26:29 +08:00
|
|
|
interface ImagePreviewStates {
|
|
|
|
previewVisible?: Record<PropertyKey, boolean>;
|
2022-11-18 11:54:06 +08:00
|
|
|
}
|
|
|
|
|
2022-12-07 18:26:29 +08:00
|
|
|
class ImagePreview extends React.Component<ImagePreviewProps, ImagePreviewStates> {
|
|
|
|
constructor(props: ImagePreviewProps) {
|
2022-11-18 11:54:06 +08:00
|
|
|
super(props);
|
|
|
|
this.state = {
|
|
|
|
previewVisible: {},
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2022-12-07 18:26:29 +08:00
|
|
|
handleClick = (index: number) => {
|
2022-11-18 11:54:06 +08:00
|
|
|
this.setState({
|
2022-12-07 18:26:29 +08:00
|
|
|
previewVisible: { [index]: true },
|
2022-11-18 11:54:06 +08:00
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
handleCancel = () => {
|
|
|
|
this.setState({
|
|
|
|
previewVisible: {},
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
render() {
|
|
|
|
const { imgs } = this.props;
|
2022-11-19 13:47:33 +08:00
|
|
|
const imgsMeta = imgs.map((img) => {
|
2022-11-18 11:54:06 +08:00
|
|
|
const { alt, description, src } = img;
|
|
|
|
const imgClassName = img.class;
|
|
|
|
return {
|
|
|
|
className: imgClassName,
|
|
|
|
alt,
|
|
|
|
description,
|
|
|
|
src,
|
|
|
|
isGood: isGood(imgClassName),
|
|
|
|
isBad: isBad(imgClassName),
|
|
|
|
inline: isInline(imgClassName),
|
|
|
|
};
|
|
|
|
});
|
|
|
|
|
2022-12-07 18:26:29 +08:00
|
|
|
const imagesList = imgsMeta.map<React.ReactNode>((meta, index) => {
|
2022-11-18 11:54:06 +08:00
|
|
|
const metaCopy = { ...meta };
|
|
|
|
delete metaCopy.description;
|
|
|
|
delete metaCopy.isGood;
|
|
|
|
delete metaCopy.isBad;
|
|
|
|
return (
|
|
|
|
<div key={index}>
|
|
|
|
<div className="image-modal-container">
|
|
|
|
<img {...metaCopy} alt={meta.alt} />
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
const comparable =
|
|
|
|
(imgs.length === 2 && imgsMeta.every(isCompareImg)) ||
|
|
|
|
(imgs.length >= 2 && imgsMeta.every(isGoodBadImg));
|
|
|
|
|
2022-12-07 18:26:29 +08:00
|
|
|
const style: React.CSSProperties = comparable
|
|
|
|
? { width: `${(100 / imgs.length).toFixed(3)}%` }
|
|
|
|
: {};
|
2022-11-18 11:54:06 +08:00
|
|
|
|
|
|
|
const hasCarousel = imgs.length > 1 && !comparable;
|
|
|
|
const previewClassName = classNames({
|
|
|
|
'preview-image-boxes': true,
|
|
|
|
clearfix: true,
|
|
|
|
'preview-image-boxes-compare': comparable,
|
|
|
|
'preview-image-boxes-with-carousel': hasCarousel,
|
|
|
|
});
|
|
|
|
return (
|
|
|
|
<div className={previewClassName}>
|
|
|
|
{imagesList.map((_, index) => {
|
|
|
|
if (!comparable && index !== 0) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return (
|
|
|
|
<PreviewImageBox
|
|
|
|
key={index}
|
|
|
|
style={style}
|
|
|
|
comparable={comparable}
|
2022-12-07 18:26:29 +08:00
|
|
|
previewVisible={!!this.state.previewVisible?.[index]}
|
2022-11-18 11:54:06 +08:00
|
|
|
cover={imagesList[index]}
|
|
|
|
coverMeta={imgsMeta[index]}
|
|
|
|
imgs={imagesList}
|
2022-12-07 18:26:29 +08:00
|
|
|
onCancel={this.handleCancel}
|
2022-11-18 11:54:06 +08:00
|
|
|
onClick={() => {
|
|
|
|
this.handleClick(index);
|
|
|
|
}}
|
|
|
|
/>
|
|
|
|
);
|
|
|
|
})}
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2022-12-07 18:26:29 +08:00
|
|
|
|
|
|
|
export default ImagePreview;
|