Add lightbox
This commit is contained in:
parent
a417980a81
commit
eb600c9acb
4 changed files with 136 additions and 6 deletions
47
src/app/molecules/image-lightbox/ImageLightbox.jsx
Normal file
47
src/app/molecules/image-lightbox/ImageLightbox.jsx
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import './ImageLightbox.scss';
|
||||||
|
import FileSaver from 'file-saver';
|
||||||
|
|
||||||
|
import Text from '../../atoms/text/Text';
|
||||||
|
import RawModal from '../../atoms/modal/RawModal';
|
||||||
|
import IconButton from '../../atoms/button/IconButton';
|
||||||
|
|
||||||
|
import DownloadSVG from '../../../../public/res/ic/outlined/download.svg';
|
||||||
|
import ExternalSVG from '../../../../public/res/ic/outlined/external.svg';
|
||||||
|
|
||||||
|
function ImageLightbox({
|
||||||
|
url, alt, isOpen, onRequestClose,
|
||||||
|
}) {
|
||||||
|
const handleDownload = () => {
|
||||||
|
FileSaver.saveAs(url, alt);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<RawModal
|
||||||
|
className="image-lightbox__modal"
|
||||||
|
overlayClassName="image-lightbox__overlay"
|
||||||
|
isOpen={isOpen}
|
||||||
|
onRequestClose={onRequestClose}
|
||||||
|
size="large"
|
||||||
|
>
|
||||||
|
<div className="image-lightbox__header">
|
||||||
|
<Text variant="b2" weight="medium">{alt}</Text>
|
||||||
|
<IconButton onClick={() => window.open(url)} size="small" src={ExternalSVG} />
|
||||||
|
<IconButton onClick={handleDownload} size="small" src={DownloadSVG} />
|
||||||
|
</div>
|
||||||
|
<div className="image-lightbox__content">
|
||||||
|
<img src={url} alt={alt} />
|
||||||
|
</div>
|
||||||
|
</RawModal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImageLightbox.propTypes = {
|
||||||
|
url: PropTypes.string.isRequired,
|
||||||
|
alt: PropTypes.string.isRequired,
|
||||||
|
isOpen: PropTypes.bool.isRequired,
|
||||||
|
onRequestClose: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ImageLightbox;
|
50
src/app/molecules/image-lightbox/ImageLightbox.scss
Normal file
50
src/app/molecules/image-lightbox/ImageLightbox.scss
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
@use '../../partials/flex';
|
||||||
|
@use '../../partials/text';
|
||||||
|
|
||||||
|
.image-lightbox__modal {
|
||||||
|
box-shadow: none;
|
||||||
|
width: unset;
|
||||||
|
gap: var(--sp-normal);
|
||||||
|
|
||||||
|
border-radius: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
|
||||||
|
& .text {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
& .ic-raw {
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-lightbox__overlay {
|
||||||
|
background-color: var(--bg-overlay-low);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.image-lightbox__header > *,
|
||||||
|
.image-lightbox__content > * {
|
||||||
|
pointer-events: all;
|
||||||
|
}
|
||||||
|
.image-lightbox__header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
& > .text {
|
||||||
|
@extend .cp-fx__item-one;
|
||||||
|
@extend .cp-txt__ellipsis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.image-lightbox__content {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
max-height: 90vh;
|
||||||
|
|
||||||
|
& img {
|
||||||
|
background-color: black;
|
||||||
|
object-fit: contain;
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 100%;
|
||||||
|
border-radius: var(--bo-radius);
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,6 +8,7 @@ import { BlurhashCanvas } from 'react-blurhash';
|
||||||
import Text from '../../atoms/text/Text';
|
import Text from '../../atoms/text/Text';
|
||||||
import IconButton from '../../atoms/button/IconButton';
|
import IconButton from '../../atoms/button/IconButton';
|
||||||
import Spinner from '../../atoms/spinner/Spinner';
|
import Spinner from '../../atoms/spinner/Spinner';
|
||||||
|
import ImageLightbox from '../image-lightbox/ImageLightbox';
|
||||||
|
|
||||||
import DownloadSVG from '../../../../public/res/ic/outlined/download.svg';
|
import DownloadSVG from '../../../../public/res/ic/outlined/download.svg';
|
||||||
import ExternalSVG from '../../../../public/res/ic/outlined/external.svg';
|
import ExternalSVG from '../../../../public/res/ic/outlined/external.svg';
|
||||||
|
@ -124,6 +125,7 @@ function Image({
|
||||||
}) {
|
}) {
|
||||||
const [url, setUrl] = useState(null);
|
const [url, setUrl] = useState(null);
|
||||||
const [blur, setBlur] = useState(true);
|
const [blur, setBlur] = useState(true);
|
||||||
|
const [lightbox, setLightbox] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let unmounted = false;
|
let unmounted = false;
|
||||||
|
@ -138,14 +140,42 @@ function Image({
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const toggleLightbox = () => {
|
||||||
|
if (!url) return;
|
||||||
|
setLightbox(!lightbox);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
<div className="file-container">
|
<div className="file-container">
|
||||||
<FileHeader name={name} link={url || link} type={type} external />
|
<div
|
||||||
<div style={{ height: width !== null ? getNativeHeight(width, height) : 'unset' }} className="image-container">
|
style={{ height: width !== null ? getNativeHeight(width, height) : 'unset' }}
|
||||||
|
className="image-container"
|
||||||
|
role="button"
|
||||||
|
tabIndex="0"
|
||||||
|
onClick={toggleLightbox}
|
||||||
|
onKeyDown={toggleLightbox}
|
||||||
|
>
|
||||||
{ blurhash && blur && <BlurhashCanvas hash={blurhash} punch={1} />}
|
{ blurhash && blur && <BlurhashCanvas hash={blurhash} punch={1} />}
|
||||||
{ url !== null && <img style={{ display: blur ? 'none' : 'unset' }} onLoad={() => setBlur(false)} src={url || link} alt={name} />}
|
{ url !== null && (
|
||||||
|
<img
|
||||||
|
style={{ display: blur ? 'none' : 'unset' }}
|
||||||
|
onLoad={() => setBlur(false)}
|
||||||
|
src={url || link}
|
||||||
|
alt={name}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{url && (
|
||||||
|
<ImageLightbox
|
||||||
|
url={url}
|
||||||
|
alt={name}
|
||||||
|
isOpen={lightbox}
|
||||||
|
onRequestClose={toggleLightbox}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Image.defaultProps = {
|
Image.defaultProps = {
|
||||||
|
|
|
@ -62,6 +62,9 @@
|
||||||
margin: 0 !important;
|
margin: 0 !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.image-container img {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
.video-container {
|
.video-container {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
Loading…
Reference in a new issue