Open image in lightbox (#767)
authorAjay Bura <32841439+ajbura@users.noreply.github.com>
Fri, 19 Aug 2022 06:45:22 +0000 (12:15 +0530)
committerGitHub <noreply@github.com>
Fri, 19 Aug 2022 06:45:22 +0000 (12:15 +0530)
* Add lightbox

* Fix vertical media height (#467)

* Update dialog animation

* Fix overlay opacity

* Fix dialog animation

* Update open in new tab icon

public/res/ic/outlined/external.svg
src/app/atoms/modal/RawModal.jsx
src/app/atoms/modal/RawModal.scss
src/app/molecules/image-lightbox/ImageLightbox.jsx [new file with mode: 0644]
src/app/molecules/image-lightbox/ImageLightbox.scss [new file with mode: 0644]
src/app/molecules/media/Media.jsx
src/app/molecules/media/Media.scss
src/index.scss

index 92b007cc9f7fe1d0fff8e4ebe55fe77710501f79..adade1bd9a793c1f7f966a77f4fd329f9cc49bde 100644 (file)
@@ -1,12 +1,4 @@
-<?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>
index 306d7d1ba42689abd124e6771a6b80f656941c80..450be0e00817f9098d2d181889705f3a73e06e8e 100644 (file)
@@ -42,7 +42,6 @@ function RawModal({
       shouldCloseOnEsc={closeFromOutside}
       shouldCloseOnOverlayClick={closeFromOutside}
       shouldReturnFocusAfterClose={false}
-      closeTimeoutMS={300}
     >
       {children}
     </Modal>
index 72a64d76da803fb270b4ca7a0d1a597d275009d3..80458752f0453ad110f95efeff6432d101d63ca4 100644 (file)
@@ -1,27 +1,3 @@
-.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
diff --git a/src/app/molecules/image-lightbox/ImageLightbox.jsx b/src/app/molecules/image-lightbox/ImageLightbox.jsx
new file mode 100644 (file)
index 0000000..c1c45db
--- /dev/null
@@ -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;
diff --git a/src/app/molecules/image-lightbox/ImageLightbox.scss b/src/app/molecules/image-lightbox/ImageLightbox.scss
new file mode 100644 (file)
index 0000000..5a900b9
--- /dev/null
@@ -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);
+  }
+}
\ No newline at end of file
index d7ba3854a8fa0c7aa376c5037bf94ed1cda7f4cd..e2b61775cbc0e32cbb545c7ccebefb0dff3fbbf4 100644 (file)
@@ -8,6 +8,7 @@ import { BlurhashCanvas } from 'react-blurhash';
 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';
@@ -124,6 +125,7 @@ function Image({
 }) {
   const [url, setUrl] = useState(null);
   const [blur, setBlur] = useState(true);
+  const [lightbox, setLightbox] = useState(false);
 
   useEffect(() => {
     let unmounted = false;
@@ -138,14 +140,42 @@ function Image({
     };
   }, []);
 
+  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 = {
index 7c73305ab91e38f7995846065e6d98669f06ce00..8d98c428e8917547e2aefa3aef9510d94fbc820c 100644 (file)
     margin: 0 !important;
   }
 }
+.image-container {
+  max-height: 460px;
+  img {
+    cursor: pointer;
+    object-fit: cover;
+  }
+}
 
 .video-container {
   position: relative;
index 3a4707ca94717fc52ead1c6b7ff231df79485de6..39d0612b0d023d5b950eba8eb22aced578bace56 100644 (file)
 
   /* 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%);