-<?xml version="1.0" encoding="utf-8"?>
-<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
-<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
- viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
-<g>
- <polygon points="14,3 14,5 17.8,5 12.9,9.9 14.3,11.3 19,6.6 19,10.2 21,10.2 21,3 "/>
- <path d="M3,10.2h2V5h5V3H5C3.9,3,3,3.9,3,5V10.2z"/>
- <path d="M5,14.2H3V19c0,1.1,0.9,2,2,2h5v-2H5V14.2z"/>
- <path d="M19,19h-5v2h5c1.1,0,2-0.9,2-2v-4.8h-2V19z"/>
-</g>
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M14 3V5H17.8L12.9 9.9L14.3 11.3L19 6.6V10.2H21V3H14Z" fill="black"/>
+<path d="M5 5H10V3H5C3.9 3 3 3.9 3 5V19C3 20.1 3.9 21 5 21H19C20.1 21 21 20.1 21 19V14.2H19V19H5V5Z" fill="black"/>
</svg>
shouldCloseOnEsc={closeFromOutside}
shouldCloseOnOverlayClick={closeFromOutside}
shouldReturnFocusAfterClose={false}
- closeTimeoutMS={300}
>
{children}
</Modal>
-.ReactModal__Overlay {
- opacity: 0;
- transition: opacity 200ms var(--fluid-slide-up);
-}
-.ReactModal__Overlay--after-open{
- opacity: 1;
-}
-.ReactModal__Overlay--before-close{
- opacity: 0;
-}
-
-.ReactModal__Content {
- transform: translateY(100%);
- transition: transform 200ms var(--fluid-slide-up);
-}
-
-.ReactModal__Content--after-open{
- transform: translateY(0);
-}
-
-.ReactModal__Content--before-close{
- transform: translateY(100%);
-}
-
.raw-modal {
--small-modal-width: 525px;
--medium-modal-width: 712px;
height: 100%;
background-color: var(--bg-overlay);
}
+}
+
+.ReactModal__Overlay {
+ animation: raw-modal--overlay 150ms;
+}
+
+.ReactModal__Content {
+ animation: raw-modal--content 150ms;
+}
+
+@keyframes raw-modal--content {
+ 0% {
+ transform: translateY(100px);
+ opacity: .5;
+ }
+ 100% {
+ transform: translateY(0);
+ opacity: 1;
+ }
+}
+@keyframes raw-modal--overlay {
+ 0% {
+ opacity: 0;
+ }
+ 100% {
+ opacity: 1;
+ }
}
\ No newline at end of file
--- /dev/null
+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;
--- /dev/null
+@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);
+ }
+}
\ No newline at end of file
import Text from '../../atoms/text/Text';
import IconButton from '../../atoms/button/IconButton';
import Spinner from '../../atoms/spinner/Spinner';
+import ImageLightbox from '../image-lightbox/ImageLightbox';
import DownloadSVG from '../../../../public/res/ic/outlined/download.svg';
import ExternalSVG from '../../../../public/res/ic/outlined/external.svg';
}) {
const [url, setUrl] = useState(null);
const [blur, setBlur] = useState(true);
+ const [lightbox, setLightbox] = useState(false);
useEffect(() => {
let unmounted = false;
};
}, []);
+ const toggleLightbox = () => {
+ if (!url) return;
+ setLightbox(!lightbox);
+ };
+
return (
- <div className="file-container">
- <FileHeader name={name} link={url || link} type={type} external />
- <div style={{ height: width !== null ? getNativeHeight(width, height) : 'unset' }} className="image-container">
- { blurhash && blur && <BlurhashCanvas hash={blurhash} punch={1} />}
- { url !== null && <img style={{ display: blur ? 'none' : 'unset' }} onLoad={() => setBlur(false)} src={url || link} alt={name} />}
+ <>
+ <div className="file-container">
+ <div
+ 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} />}
+ { url !== null && (
+ <img
+ style={{ display: blur ? 'none' : 'unset' }}
+ onLoad={() => setBlur(false)}
+ src={url || link}
+ alt={name}
+ />
+ )}
+ </div>
</div>
- </div>
+ {url && (
+ <ImageLightbox
+ url={url}
+ alt={name}
+ isOpen={lightbox}
+ onRequestClose={toggleLightbox}
+ />
+ )}
+ </>
);
}
Image.defaultProps = {
margin: 0 !important;
}
}
+.image-container {
+ max-height: 460px;
+ img {
+ cursor: pointer;
+ object-fit: cover;
+ }
+}
.video-container {
position: relative;
/* shadow and overlay */
--bg-overlay: rgba(0, 0, 0, 20%);
- --bg-overlay-low: rgba(0, 0, 0, 85%);
+ --bg-overlay-low: rgba(0, 0, 0, 50%);
--bs-popup: 0 0 16px rgba(0, 0, 0, 10%);
}
/* shadow and overlay */
- --bg-overlay: rgba(0, 0, 0, 50%);
+ --bg-overlay: rgba(0, 0, 0, 60%);
+ --bg-overlay-low: rgba(0, 0, 0, 80%);
--bs-popup: 0 0 16px rgba(0, 0, 0, 25%);