From 6a044408f16d15b5bdbe7f099046558be6cee8b1 Mon Sep 17 00:00:00 2001 From: Nermeen Moustafa Date: Mon, 29 Aug 2022 16:19:10 +0200 Subject: [PATCH 01/45] add possibility to view image in full screen - T7582 --- config/wp/webpack-demo.config.js | 10 ++- examples/low-preview/src/index.html | 4 + src/common/ci.utils.js | 126 ++++++++++++++++++++++++++++ src/low-preview/ci.service.js | 87 ++++++++++++++++++- src/low-preview/ci.styles.css | 59 +++++++++++++ 5 files changed, 281 insertions(+), 5 deletions(-) diff --git a/config/wp/webpack-demo.config.js b/config/wp/webpack-demo.config.js index 55ba780..e036aeb 100644 --- a/config/wp/webpack-demo.config.js +++ b/config/wp/webpack-demo.config.js @@ -37,7 +37,15 @@ module.exports = { }, "css-loader" ] - } + }, + { + test: /\.(png|jpe?g|gif|svg)$/i, + use: [ + { + loader: 'file-loader', + }, + ], + }, ] }, plugins: [htmlWebpackPlugin, miniCssExtractPlugin], diff --git a/examples/low-preview/src/index.html b/examples/low-preview/src/index.html index ca5726b..44156d8 100644 --- a/examples/low-preview/src/index.html +++ b/examples/low-preview/src/index.html @@ -209,6 +209,7 @@

Responsive images, in real-time!

id="left-column-image" ci-params="ci_info=2" ci-ratio="0.942" + ci-gallery="gallery1" />
@@ -227,6 +228,7 @@

Responsive images, in real-time!

height="1260" ci-params="ci_info=2" ci-ratio="1" + ci-gallery="gallery2" />

@@ -242,6 +244,7 @@

Responsive images, in real-time!

ci-params="w=265&h=265&gravity=north" id="right-column-second-image" style="border-radius: 50%;" + ci-gallery="gallery2" />
@@ -277,6 +280,7 @@

Responsive images, in real-time!

ci-ratio="2.885" id="second-horizontal-image" ci-params="ci_info=2" + ci-gallery="gallery1" />

diff --git a/src/common/ci.utils.js b/src/common/ci.utils.js index 1b293ea..6715d07 100644 --- a/src/common/ci.utils.js +++ b/src/common/ci.utils.js @@ -71,6 +71,8 @@ const getCommonImageProps = (image) => ({ imgNodeHeight: attr(image, 'height'), doNotReplaceImageUrl: isTrue(image, 'ci-do-not-replace-url'), alt: attr(image, 'alt'), + zoom: attr(image, 'ci-zoom') || undefined, + gallery: attr(image, 'ci-gallery') || undefined, }); const filterImages = (images, type) => { @@ -222,6 +224,123 @@ const setOptions = (node, options) => { return node; }; +const createIcon = (iconSrc, className) => { + const iconWrapper = document.createElement('div'); + const icon = new Image(); + + icon.src = iconSrc; + + if(className){ + iconWrapper.classList.add(className); + } + + iconWrapper.appendChild(icon); + + return iconWrapper; +} + +const destroyGallery = (galleryModal) => { + galleryModal.parentNode.removeChild(galleryModal); +} + +const createGalleryPreviewModule = (imgSelector, imgProps, galleryModal) => { + const {imgNodeSRC } = imgProps; + + const previewModule = galleryModal.querySelector('.ci-gallery-preview-module'); + + const image = new Image(); + + image.setAttribute(imgSelector, imgNodeSRC); + + previewModule.appendChild(image); + + return previewModule; +} + +const galleryPreviewImage = (imgSelector, imgNodeSRC) => { + const image = new Image(); + + image.setAttribute(imgSelector, imgNodeSRC); + + return image; +} + +const createThmbnailsModule = (images, imgSelector, galleryName, galleryModal) => { + const galleryThmbnails = images.filter((image) => { + const { gallery } = getCommonImageProps(image); + + if (gallery === galleryName){ + return image; + } + }); + + const thumbnailsModule = galleryModal.querySelector('.ci-gallery-thumbnail-module'); + + galleryThmbnails.forEach((img) => { + const thmbnailContainer = document.createElement('div'); + thmbnailContainer.classList.add('ci-gallery-thmbnail-container'); + + const image = img.cloneNode(); + image.classList.remove('ci-image'); + image.style.width = '100%'; + image.style.height = '100%'; + + thmbnailContainer.append(image); + + thumbnailsModule.append(thmbnailContainer); + }) + + return thumbnailsModule; +} + +const createGalleryModal = () => { + const galleryModal = document.createElement('div'); + const previewModule = document.createElement('div'); + const thumbnailsModule = document.createElement('div'); + const closeIcon = createIcon('../public/close-icon.svg', 'ci-gallery-close-button'); + + galleryModal.classList.add('ci-gallery-modal'); + previewModule.classList.add('ci-gallery-preview-module'); + thumbnailsModule.classList.add('ci-gallery-thumbnail-module'); + + galleryModal.append(previewModule); + galleryModal.append(thumbnailsModule); + galleryModal.append(closeIcon); + + closeIcon.onclick = destroyGallery.bind(this, galleryModal); + + return galleryModal; +} + +const markCurrentImage = (galleryThmbnails, currentIndex) => { + galleryThmbnails.forEach((imgWrapper, index) => { + imgWrapper.querySelector('img').style.border= '1px solid grey'; + + if(index === currentIndex) { + imgWrapper.querySelector('img').style.border = '1px solid white'; + } + }); +} + +const getCurrentImage = (mainImageWrapper, galleryModal) => { + const galleryThmbnailsModule = galleryModal.querySelector('.ci-gallery-thumbnail-module'); + const galleryThmbnails = [...galleryThmbnailsModule.children]; + + let currentIndex = null; + + galleryThmbnails.forEach((imgWrapper, index) => { + const mainImg = mainImageWrapper.querySelector('[ci-src]').getAttribute('ci-src'); + const galleryImg = imgWrapper.querySelector('[ci-src]').getAttribute('ci-src'); + + if(mainImg === galleryImg){ + currentIndex = index; + markCurrentImage(galleryThmbnails, index); + } + }); + + return currentIndex; +} + export { getParams, filterImages, @@ -238,4 +357,11 @@ export { setAlt, removeClassNames, setOptions, + createIcon, + createGalleryPreviewModule, + galleryPreviewImage, + createThmbnailsModule, + createGalleryModal, + markCurrentImage, + getCurrentImage }; diff --git a/src/low-preview/ci.service.js b/src/low-preview/ci.service.js index bbf89cf..6275df4 100644 --- a/src/low-preview/ci.service.js +++ b/src/low-preview/ci.service.js @@ -19,6 +19,13 @@ import { setOptions, setSrc, setSrcset, + getCurrentImage, + markCurrentImage, + createGalleryModal, + createThmbnailsModule, + galleryPreviewImage, + createGalleryPreviewModule, + createIcon, } from '../common/ci.utils'; import { getInitialConfigLowPreview } from './ci.config'; import { @@ -67,7 +74,7 @@ export default class CIResponsive { if (images.length > -1) { images.forEach((imgNode) => { - this.getBasicInfo(imgNode, isUpdate, windowScreenBecomesBigger, 'image'); + this.getBasicInfo(imgNode, isUpdate, windowScreenBecomesBigger, 'image', images); }); } @@ -78,7 +85,7 @@ export default class CIResponsive { } } - getBasicInfo = (imgNode, isUpdate, windowScreenBecomesBigger, type) => { + getBasicInfo = (imgNode, isUpdate, windowScreenBecomesBigger, type, images) => { const isImage = type === 'image'; const { config } = this; const { @@ -132,16 +139,83 @@ export default class CIResponsive { const cloudimageUrl = generateURLbyDPR(); const cloudimageSrcset = devicePixelRatioList.map((dpr) => ({ dpr: dpr.toString(), url: generateURLbyDPR(dpr) })); const props = { - imgNode, isUpdate, imgProps, lazy, isPreview, containerProps, isSVG, cloudimageUrl, src, preserveSize, isAdaptive, alt: alt || generateAlt(src), + imgNode, isUpdate, imgProps, lazy, isPreview, containerProps, isSVG, cloudimageUrl, src, preserveSize, isAdaptive, imgSelector, alt: alt || generateAlt(src), }; if (isImage) { - this.processImage({ ...props, cloudimageUrl: generateURLbyDPR(1), cloudimageSrcset }); + this.processImage({ ...props, cloudimageUrl: generateURLbyDPR(1), cloudimageSrcset, images}); } else { this.processBackgroundImage(props); } }; + processNextImage = (nextIndex, galleryThmbnailsModule, imgSelector, galleryModal) => { + const nextImageSrc = galleryThmbnailsModule[nextIndex].querySelector('[ci-src]').getAttribute('ci-src'); + const nextImage = galleryPreviewImage(imgSelector, nextImageSrc); + + const previewModule = galleryModal.querySelector('.ci-gallery-preview-module'); + + previewModule.removeChild(previewModule.firstElementChild); + previewModule.append(nextImage); + + markCurrentImage(galleryThmbnailsModule, nextIndex); + + this.process(false, previewModule); + } + + arrowNavigation = (direction, galleryModal, imgSelector) => { + const mainImageWrapper = galleryModal.querySelector('.ci-gallery-preview-module'); + const galleryThmbnailsModule = galleryModal.querySelector('.ci-gallery-thumbnail-module'); + const galleryThmbnails = [...galleryThmbnailsModule.children]; + + + if(galleryThmbnails.length > 1){ + let nextIndex = null; + const currentIndex = getCurrentImage(mainImageWrapper, galleryModal); + + if(direction === 'right'){ + if(currentIndex < galleryThmbnails.length - 1){ + nextIndex = currentIndex + 1; + } else { + nextIndex = 0; + } + } + else{ + if(currentIndex > 0){ + nextIndex = currentIndex - 1; + } else { + nextIndex = galleryThmbnails.length - 1; + } + } + + this.processNextImage(nextIndex, galleryThmbnails, imgSelector, galleryModal); + } + } + + handleClickWrapper(imgSelector, imgProps, images){ + const { gallery } = imgProps; + + if(gallery) { + const galleryModal = createGalleryModal(); + const previewModule = createGalleryPreviewModule(imgSelector, imgProps, galleryModal); + const thumbnailsModule = createThmbnailsModule(images, imgSelector, gallery, galleryModal); + const rightArrow = createIcon('../public/right-arrow-icon.svg', 'ci-gallery-right-arrow-button'); + const leftArrow = createIcon('../public/left-arrow-icon.svg', 'ci-gallery-left-arrow-button'); + + galleryModal.appendChild(previewModule); + galleryModal.appendChild(thumbnailsModule); + galleryModal.append(rightArrow); + galleryModal.append(leftArrow); + + document.body.appendChild(galleryModal); + + this.process(false, previewModule); + + rightArrow.onclick = this.arrowNavigation.bind(this, "right", galleryModal, imgSelector); + leftArrow.onclick = this.arrowNavigation.bind(this, "left", galleryModal, imgSelector); + } + } + processImage(props) { const { imgNode, @@ -156,6 +230,8 @@ export default class CIResponsive { preserveSize, cloudimageSrcset, isAdaptive, + imgSelector, + images, alt, } = props; const { params } = imgProps; @@ -194,6 +270,9 @@ export default class CIResponsive { if (config.onImageLoad && typeof config.onImageLoad === 'function') { config.onImageLoad(imgNode); } + + wrapper.onclick = this.handleClickWrapper.bind(this, imgSelector, imgProps, images); + onImageLoad(wrapper, previewImgNode, imgNode, ratio, preserveSize, isAdaptive); }; diff --git a/src/low-preview/ci.styles.css b/src/low-preview/ci.styles.css index afbeb00..0ce52f5 100644 --- a/src/low-preview/ci.styles.css +++ b/src/low-preview/ci.styles.css @@ -50,3 +50,62 @@ img.ci-image-ratio.ci-image-preview { .ci-bg.ci-bg-animation:before { filter: blur(10px); } + +.ci-gallery-modal { + width: 100%; + height: 100%; + background-color: black; + position: fixed; + top: 0; + left: 0; + z-index: 110; +} + +.ci-gallery-preview-module { + width: 100%; + height: 100%; + display: flex; + justify-content: center; + align-items: center; +} + +.ci-gallery-thumbnail-module { + justify-content: center; + display: flex; + width: 100%; + height: 40px; + position: absolute; + bottom: 10px; + padding-left: 20px; + padding-right: 20px; +} + +.ci-gallery-close-button { + position: absolute; + top: 15px; + right: 15px; + z-index: 111; +} + +.ci-gallery-thmbnail-container { + width: 40px; + height: 40px; + margin : 2px; + display: flex; + justify-content: center; + align-items: center; +} + +.ci-gallery-right-arrow-button { + position: absolute; + top: 50%; + right: 15px; + z-index: 111; +} + +.ci-gallery-left-arrow-button { + position: absolute; + top: 50%; + left: 15px; + z-index: 111; +} From d1eaa3486d79569dbcf8fd25a6446ad86f49ee89 Mon Sep 17 00:00:00 2001 From: Nermeen Moustafa Date: Mon, 29 Aug 2022 17:02:59 +0200 Subject: [PATCH 02/45] add possibility to see images in full screen - T7582 --- examples/low-preview/src/index.html | 1 + src/common/ci.utils.js | 21 ++++++++++++++++++++- src/low-preview/ci.service.js | 17 ++++++++++++++++- src/low-preview/ci.styles.css | 12 ++++++++++++ 4 files changed, 49 insertions(+), 2 deletions(-) diff --git a/examples/low-preview/src/index.html b/examples/low-preview/src/index.html index 44156d8..2ab4819 100644 --- a/examples/low-preview/src/index.html +++ b/examples/low-preview/src/index.html @@ -308,6 +308,7 @@

Original Image

height="400" ci-ratio="1" alt="car-image" + ci-zoom="true" />
 <img
diff --git a/src/common/ci.utils.js b/src/common/ci.utils.js
index 6715d07..ec68240 100644
--- a/src/common/ci.utils.js
+++ b/src/common/ci.utils.js
@@ -341,6 +341,23 @@ const getCurrentImage = (mainImageWrapper, galleryModal) => {
   return currentIndex;
 }
 
+const displayZoomIcon = (wrapper, imgProps) => {
+  const { zoom, gallery } = imgProps;
+
+  if(zoom && !gallery){
+    const zoomIcon = createIcon('../public/right-arrow-icon.svg', 'ci-gallery-zoom-button');
+    wrapper.append(zoomIcon);
+  }
+}
+
+const destroyZoomIcon = (wrapper) => {
+  const zoomIcon = wrapper.querySelector('.ci-gallery-zoom-button');
+
+  if (zoomIcon){
+    zoomIcon.remove();
+  }
+}
+
 export {
   getParams,
   filterImages,
@@ -363,5 +380,7 @@ export {
   createThmbnailsModule,
   createGalleryModal,
   markCurrentImage,
-  getCurrentImage
+  getCurrentImage,
+  displayZoomIcon,
+  destroyZoomIcon,
 };
diff --git a/src/low-preview/ci.service.js b/src/low-preview/ci.service.js
index 6275df4..502fd00 100644
--- a/src/low-preview/ci.service.js
+++ b/src/low-preview/ci.service.js
@@ -26,6 +26,8 @@ import {
   galleryPreviewImage,
   createGalleryPreviewModule,
   createIcon,
+  displayZoomIcon,
+  destroyZoomIcon,
 } from '../common/ci.utils';
 import { getInitialConfigLowPreview } from './ci.config';
 import {
@@ -193,7 +195,18 @@ export default class CIResponsive {
   }
 
   handleClickWrapper(imgSelector, imgProps, images){
-    const { gallery } = imgProps;
+    const { gallery, zoom } = imgProps;
+
+    if(zoom && !gallery) {
+      const galleryModal = createGalleryModal();
+      const previewModule = createGalleryPreviewModule(imgSelector, imgProps, galleryModal);
+
+      galleryModal.appendChild(previewModule);
+
+      document.body.appendChild(galleryModal);
+
+      this.process(false, previewModule);
+    }
 
     if(gallery) {
       const galleryModal = createGalleryModal();
@@ -272,6 +285,8 @@ export default class CIResponsive {
       }
 
       wrapper.onclick = this.handleClickWrapper.bind(this, imgSelector, imgProps, images);
+      wrapper.onmouseenter = () => displayZoomIcon(wrapper, imgProps);
+      wrapper.onmouseout = () => destroyZoomIcon(wrapper);
 
       onImageLoad(wrapper, previewImgNode, imgNode, ratio, preserveSize, isAdaptive);
     };
diff --git a/src/low-preview/ci.styles.css b/src/low-preview/ci.styles.css
index 0ce52f5..b7cfda5 100644
--- a/src/low-preview/ci.styles.css
+++ b/src/low-preview/ci.styles.css
@@ -109,3 +109,15 @@ img.ci-image-ratio.ci-image-preview {
   left: 15px;
   z-index: 111;
 }
+
+.ci-gallery-zoom-button {
+  width: 100%;
+  height: 100%;
+  position: absolute;
+  top: 0;
+  left: 0;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  background-color: rgba(255, 255, 255, 0.5);
+}

From 88517008c398c0226e9acb84f0f596894cb54415 Mon Sep 17 00:00:00 2001
From: Amr Wagdy 
Date: Tue, 30 Aug 2022 15:09:17 +0200
Subject: [PATCH 03/45] Refactor: Code Review

---
 src/common/ci.utils.js        | 86 +++++++++++++++++++++++++----------
 src/low-preview/ci.service.js | 60 +++++++++++-------------
 2 files changed, 89 insertions(+), 57 deletions(-)

diff --git a/src/common/ci.utils.js b/src/common/ci.utils.js
index ec68240..45fd10c 100644
--- a/src/common/ci.utils.js
+++ b/src/common/ci.utils.js
@@ -224,6 +224,12 @@ const setOptions = (node, options) => {
   return node;
 };
 
+const getGalleryImages = (images, galleryName) => images.filter((image) => {
+  const { gallery } = getCommonImageProps(image);
+
+  return gallery === galleryName;
+});
+
 const createIcon = (iconSrc, className) => {
   const iconWrapper = document.createElement('div');
   const icon = new Image();
@@ -237,19 +243,18 @@ const createIcon = (iconSrc, className) => {
   iconWrapper.appendChild(icon);
 
   return iconWrapper;
-}
+};
 
 const destroyGallery = (galleryModal) => {
   galleryModal.parentNode.removeChild(galleryModal);
-}
+};
 
 const createGalleryPreviewModule = (imgSelector, imgProps, galleryModal) => {
   const {imgNodeSRC } = imgProps;
+  const image = new Image();
 
   const previewModule = galleryModal.querySelector('.ci-gallery-preview-module');
 
-  const image = new Image();
-
   image.setAttribute(imgSelector, imgNodeSRC);
 
   previewModule.appendChild(image);
@@ -265,35 +270,63 @@ const galleryPreviewImage = (imgSelector, imgNodeSRC) => {
   return image;
 }
 
-const createThmbnailsModule = (images, imgSelector, galleryName, galleryModal) => {
-  const galleryThmbnails = images.filter((image) => {
-    const { gallery } = getCommonImageProps(image);
+const adaptGalleryThumbnails = (thumbnails = []) => thumbnails.map((thumbnail, index) => {
+  const thmbnailContainer = document.createElement('div');
+  const image = thumbnail.cloneNode();
 
-    if (gallery === galleryName){
-      return image;
-    }
-  });
+  image.classList.remove('ci-image');
+  image.style.width = '100%';
+  image.style.height = '100%';
 
-  const thumbnailsModule = galleryModal.querySelector('.ci-gallery-thumbnail-module');
+  thmbnailContainer.classList.add('ci-gallery-thmbnail-container');
+  thmbnailContainer.setAttribute('data-gallery-index', index);
+  thmbnailContainer.append(image);
 
-  galleryThmbnails.forEach((img) => {
-    const thmbnailContainer = document.createElement('div');
-    thmbnailContainer.classList.add('ci-gallery-thmbnail-container');
+  return thmbnailContainer;
+});
 
-    const image = img.cloneNode();
-    image.classList.remove('ci-image');
-    image.style.width = '100%';
-    image.style.height = '100%';
+const appendGalleryThumbnails = (thumbnails = [], thumbnailsContainer) => {
+  thumbnails.forEach((thumbnail) => {
+    thumbnailsContainer.append(thumbnail)
+  })
+};
 
-    thmbnailContainer.append(image);
+const createThmbnailsModule = (images, galleryModal) => {
+  const thumbnailsModule = galleryModal.querySelector('.ci-gallery-thumbnail-module');
+  const adaptedGalleryThumbnails = adaptGalleryThumbnails(images);
 
-    thumbnailsModule.append(thmbnailContainer);
-  })
+  appendGalleryThumbnails(adaptedGalleryThumbnails, thumbnailsModule)
 
   return thumbnailsModule;
 }
 
-const createGalleryModal = () => {
+const createGalleryArrows = (onClick) => {
+  const leftArrow = createIcon('../public/left-arrow-icon.svg', 'ci-gallery-left-arrow-button');
+  const rightArrow = createIcon('../public/right-arrow-icon.svg', 'ci-gallery-right-arrow-button');
+
+  if (onClick) {
+    leftArrow.onclick = onClick.bind(this, 'left');
+    rightArrow.onclick = onClick.bind(this, 'right')
+  }
+
+  return [leftArrow, rightArrow];
+}
+
+const getGalleryLengthAndIndex = () => {
+  const galleryModal = document.body.querySelector('[data-ci-gallery]');
+  const galleryLength = galleryModal.getAttribute('data-ci-gallery-length');
+  const galleryIndex = galleryModal.getAttribute('data-ci-gallery-index');
+
+  return [galleryLength, galleryIndex]
+}
+
+const setGalleryIndex = (index) => {
+  const galleryModal = document.body.querySelector('[data-ci-gallery]');
+
+  galleryModal.setAttribute('data-ci-gallery-index', index)
+}
+
+const createGalleryModal = (galleryLength) => {
   const galleryModal = document.createElement('div');
   const previewModule = document.createElement('div');
   const thumbnailsModule = document.createElement('div');
@@ -303,6 +336,9 @@ const createGalleryModal = () => {
   previewModule.classList.add('ci-gallery-preview-module');
   thumbnailsModule.classList.add('ci-gallery-thumbnail-module');
 
+  galleryModal.setAttribute('data-ci-gallery', true);
+  galleryModal.setAttribute('data-ci-gallery-length', galleryLength);
+  galleryModal.setAttribute('data-ci-gallery-index', 0);
   galleryModal.append(previewModule);
   galleryModal.append(thumbnailsModule);
   galleryModal.append(closeIcon);
@@ -383,4 +419,8 @@ export {
   getCurrentImage,
   displayZoomIcon,
   destroyZoomIcon,
+  getGalleryImages,
+  createGalleryArrows,
+  getGalleryLengthAndIndex,
+  setGalleryIndex
 };
diff --git a/src/low-preview/ci.service.js b/src/low-preview/ci.service.js
index 502fd00..1f9cffc 100644
--- a/src/low-preview/ci.service.js
+++ b/src/low-preview/ci.service.js
@@ -28,6 +28,10 @@ import {
   createIcon,
   displayZoomIcon,
   destroyZoomIcon,
+  getGalleryImages,
+  createGalleryArrows,
+  getGalleryLengthAndIndex,
+  setGalleryIndex,
 } from '../common/ci.utils';
 import { getInitialConfigLowPreview } from './ci.config';
 import {
@@ -165,33 +169,27 @@ export default class CIResponsive {
     this.process(false, previewModule);
   }
 
-  arrowNavigation = (direction, galleryModal, imgSelector) => {
-    const mainImageWrapper = galleryModal.querySelector('.ci-gallery-preview-module');
-    const galleryThmbnailsModule = galleryModal.querySelector('.ci-gallery-thumbnail-module');
-    const galleryThmbnails = [...galleryThmbnailsModule.children];
-    
+  handleArrowClick(direction) {
+    let nextIndex = 0;
+    const [length, index] = getGalleryLengthAndIndex();
 
-    if(galleryThmbnails.length > 1){
-      let nextIndex = null;
-      const currentIndex = getCurrentImage(mainImageWrapper, galleryModal);
+    nextIndex = +index;
 
-      if(direction === 'right'){
-        if(currentIndex < galleryThmbnails.length - 1){
-          nextIndex = currentIndex + 1;
-        } else {
-          nextIndex = 0;
-        }
-      }
-      else{
-        if(currentIndex > 0){
-          nextIndex = currentIndex - 1;
-        } else {
-          nextIndex = galleryThmbnails.length - 1;
-        }
+    if (direction === 'left') {
+      nextIndex -= 1;
+
+      if ((length -1 + nextIndex) <= 0) {
+        nextIndex = length - 1;
       }
+    } else {
+      nextIndex += 1;
 
-      this.processNextImage(nextIndex, galleryThmbnails, imgSelector, galleryModal);
+      if ((length -1 + nextIndex) > length) {
+        nextIndex = 0;
+      }
     }
+
+    setGalleryIndex(nextIndex);
   }
 
   handleClickWrapper(imgSelector, imgProps, images){
@@ -208,24 +206,18 @@ export default class CIResponsive {
       this.process(false, previewModule);
     }
 
-    if(gallery) {
-      const galleryModal = createGalleryModal();
+    if (gallery) {
+      const galleryImages = getGalleryImages(images, gallery);
+      const galleryModal = createGalleryModal(galleryImages.length);
       const previewModule = createGalleryPreviewModule(imgSelector, imgProps, galleryModal);
-      const thumbnailsModule = createThmbnailsModule(images, imgSelector, gallery, galleryModal);
-      const rightArrow = createIcon('../public/right-arrow-icon.svg', 'ci-gallery-right-arrow-button');
-      const leftArrow = createIcon('../public/left-arrow-icon.svg', 'ci-gallery-left-arrow-button');
+      const thumbnailsModule = createThmbnailsModule(galleryImages, galleryModal);
+      const galleryArrows = createGalleryArrows(this.handleArrowClick);
 
       galleryModal.appendChild(previewModule);
       galleryModal.appendChild(thumbnailsModule);
-      galleryModal.append(rightArrow);
-      galleryModal.append(leftArrow);
+      galleryModal.append(...galleryArrows);
 
       document.body.appendChild(galleryModal);
-
-      this.process(false, previewModule);
-
-      rightArrow.onclick = this.arrowNavigation.bind(this, "right", galleryModal, imgSelector);
-      leftArrow.onclick = this.arrowNavigation.bind(this, "left", galleryModal, imgSelector);
     }
   }
 

From 691bc022a5a822ef736323aa08e605cb1e48f94d Mon Sep 17 00:00:00 2001
From: Amr Wagdy 
Date: Tue, 30 Aug 2022 19:43:00 +0200
Subject: [PATCH 04/45] Refactor: Code review

---
 src/common/ci.utils.js        | 57 ++++++++++++------------
 src/low-preview/ci.service.js | 81 ++++++++++++++++++++++++-----------
 src/low-preview/ci.styles.css |  7 +--
 3 files changed, 85 insertions(+), 60 deletions(-)

diff --git a/src/common/ci.utils.js b/src/common/ci.utils.js
index 45fd10c..71e7efa 100644
--- a/src/common/ci.utils.js
+++ b/src/common/ci.utils.js
@@ -27,14 +27,14 @@ const getSize = (sizes) => {
     // change single quotes to double quotes
     temp = temp.replace(/'/g, '"').replace(/-"width":/g, '-width:');
     resultSizes = JSON.parse(temp);
-  } catch (e) {}
+  } catch (e) { }
 
   if (resultSizes) {
     Object.keys(resultSizes).forEach((key) => {
       if (typeof resultSizes[key] === 'string') {
         try {
           resultSizes[key] = JSON.parse(`{"${decodeURI(resultSizes[key].replace(/&/g, '","').replace(/=/g, '":"'))}"}`);
-        } catch (e) {}
+        } catch (e) { }
       }
     });
   }
@@ -49,12 +49,12 @@ const getParams = (params) => {
     const temp = params.replace(/(\w+:)|(\w+ :)/g, (matchedStr) => `"${matchedStr.substring(0, matchedStr.length - 1)}":`);
 
     resultParams = JSON.parse(temp);
-  } catch (e) {}
+  } catch (e) { }
 
   if (!resultParams) {
     try {
       resultParams = JSON.parse(`{"${decodeURI(params.replace(/&/g, '","').replace(/=/g, '":"'))}"}`);
-    } catch (e) {}
+    } catch (e) { }
   }
 
   return resultParams;
@@ -94,6 +94,7 @@ const getImageProps = (image, imgSelector) => {
   const props = {
     ...getCommonImageProps(image),
     imgNodeSRC: attr(image, imgSelector) || undefined,
+    isProcessedByGallery: isTrue(image, 'data-ci-processed-gallery')
   };
 
   const params = {
@@ -236,7 +237,7 @@ const createIcon = (iconSrc, className) => {
 
   icon.src = iconSrc;
 
-  if(className){
+  if (className) {
     iconWrapper.classList.add(className);
   }
 
@@ -249,19 +250,6 @@ const destroyGallery = (galleryModal) => {
   galleryModal.parentNode.removeChild(galleryModal);
 };
 
-const createGalleryPreviewModule = (imgSelector, imgProps, galleryModal) => {
-  const {imgNodeSRC } = imgProps;
-  const image = new Image();
-
-  const previewModule = galleryModal.querySelector('.ci-gallery-preview-module');
-
-  image.setAttribute(imgSelector, imgNodeSRC);
-
-  previewModule.appendChild(image);
-
-  return previewModule;
-}
-
 const galleryPreviewImage = (imgSelector, imgNodeSRC) => {
   const image = new Image();
 
@@ -270,18 +258,23 @@ const galleryPreviewImage = (imgSelector, imgNodeSRC) => {
   return image;
 }
 
-const adaptGalleryThumbnails = (thumbnails = []) => thumbnails.map((thumbnail, index) => {
+const adaptGalleryThumbnails = (thumbnails = [], onClick) => thumbnails.map((thumbnail, index) => {
   const thmbnailContainer = document.createElement('div');
   const image = thumbnail.cloneNode();
 
   image.classList.remove('ci-image');
+  image.style = {};
   image.style.width = '100%';
   image.style.height = '100%';
 
   thmbnailContainer.classList.add('ci-gallery-thmbnail-container');
-  thmbnailContainer.setAttribute('data-gallery-index', index);
+  thmbnailContainer.setAttribute('data-ci-gallery-index', index);
   thmbnailContainer.append(image);
 
+  if (onClick) {
+    thmbnailContainer.onclick = onClick;
+  }
+
   return thmbnailContainer;
 });
 
@@ -291,9 +284,9 @@ const appendGalleryThumbnails = (thumbnails = [], thumbnailsContainer) => {
   })
 };
 
-const createThmbnailsModule = (images, galleryModal) => {
+const createThmbnailsModule = (images, galleryModal, onClick) => {
   const thumbnailsModule = galleryModal.querySelector('.ci-gallery-thumbnail-module');
-  const adaptedGalleryThumbnails = adaptGalleryThumbnails(images);
+  const adaptedGalleryThumbnails = adaptGalleryThumbnails(images, onClick);
 
   appendGalleryThumbnails(adaptedGalleryThumbnails, thumbnailsModule)
 
@@ -320,6 +313,12 @@ const getGalleryLengthAndIndex = () => {
   return [galleryLength, galleryIndex]
 }
 
+const getGalleryPreviewModule = () => {
+  const galleryModal = document.body.querySelector('[data-ci-gallery]');
+
+  return galleryModal.querySelector('.ci-gallery-preview-module')
+}
+
 const setGalleryIndex = (index) => {
   const galleryModal = document.body.querySelector('[data-ci-gallery]');
 
@@ -350,9 +349,9 @@ const createGalleryModal = (galleryLength) => {
 
 const markCurrentImage = (galleryThmbnails, currentIndex) => {
   galleryThmbnails.forEach((imgWrapper, index) => {
-    imgWrapper.querySelector('img').style.border= '1px solid grey';
+    imgWrapper.querySelector('img').style.border = '1px solid grey';
 
-    if(index === currentIndex) {
+    if (index === currentIndex) {
       imgWrapper.querySelector('img').style.border = '1px solid white';
     }
   });
@@ -368,7 +367,7 @@ const getCurrentImage = (mainImageWrapper, galleryModal) => {
     const mainImg = mainImageWrapper.querySelector('[ci-src]').getAttribute('ci-src');
     const galleryImg = imgWrapper.querySelector('[ci-src]').getAttribute('ci-src');
 
-    if(mainImg === galleryImg){
+    if (mainImg === galleryImg) {
       currentIndex = index;
       markCurrentImage(galleryThmbnails, index);
     }
@@ -380,7 +379,7 @@ const getCurrentImage = (mainImageWrapper, galleryModal) => {
 const displayZoomIcon = (wrapper, imgProps) => {
   const { zoom, gallery } = imgProps;
 
-  if(zoom && !gallery){
+  if (zoom && !gallery) {
     const zoomIcon = createIcon('../public/right-arrow-icon.svg', 'ci-gallery-zoom-button');
     wrapper.append(zoomIcon);
   }
@@ -389,7 +388,7 @@ const displayZoomIcon = (wrapper, imgProps) => {
 const destroyZoomIcon = (wrapper) => {
   const zoomIcon = wrapper.querySelector('.ci-gallery-zoom-button');
 
-  if (zoomIcon){
+  if (zoomIcon) {
     zoomIcon.remove();
   }
 }
@@ -411,7 +410,6 @@ export {
   removeClassNames,
   setOptions,
   createIcon,
-  createGalleryPreviewModule,
   galleryPreviewImage,
   createThmbnailsModule,
   createGalleryModal,
@@ -422,5 +420,6 @@ export {
   getGalleryImages,
   createGalleryArrows,
   getGalleryLengthAndIndex,
-  setGalleryIndex
+  setGalleryIndex,
+  getGalleryPreviewModule
 };
diff --git a/src/low-preview/ci.service.js b/src/low-preview/ci.service.js
index 1f9cffc..2015b02 100644
--- a/src/low-preview/ci.service.js
+++ b/src/low-preview/ci.service.js
@@ -19,19 +19,15 @@ import {
   setOptions,
   setSrc,
   setSrcset,
-  getCurrentImage,
-  markCurrentImage,
   createGalleryModal,
   createThmbnailsModule,
-  galleryPreviewImage,
-  createGalleryPreviewModule,
-  createIcon,
   displayZoomIcon,
   destroyZoomIcon,
   getGalleryImages,
   createGalleryArrows,
   getGalleryLengthAndIndex,
   setGalleryIndex,
+  getGalleryPreviewModule,
 } from '../common/ci.utils';
 import { getInitialConfigLowPreview } from './ci.config';
 import {
@@ -149,27 +145,27 @@ export default class CIResponsive {
     };
 
     if (isImage) {
-      this.processImage({ ...props, cloudimageUrl: generateURLbyDPR(1), cloudimageSrcset, images});
+      this.processImage({ ...props, cloudimageUrl: generateURLbyDPR(1), cloudimageSrcset, images });
     } else {
       this.processBackgroundImage(props);
     }
   };
 
-  processNextImage = (nextIndex, galleryThmbnailsModule, imgSelector, galleryModal) => {
-    const nextImageSrc = galleryThmbnailsModule[nextIndex].querySelector('[ci-src]').getAttribute('ci-src');
-    const nextImage = galleryPreviewImage(imgSelector, nextImageSrc);
+  // processNextImage = (nextIndex, galleryThmbnailsModule, imgSelector, galleryModal) => {
+  //   const nextImageSrc = galleryThmbnailsModule[nextIndex].querySelector('[ci-src]').getAttribute('ci-src');
+  //   const nextImage = galleryPreviewImage(imgSelector, nextImageSrc);
 
-    const previewModule = galleryModal.querySelector('.ci-gallery-preview-module');
+  //   const previewModule = galleryModal.querySelector('.ci-gallery-preview-module');
 
-    previewModule.removeChild(previewModule.firstElementChild);
-    previewModule.append(nextImage);
+  //   previewModule.removeChild(previewModule.firstElementChild);
+  //   previewModule.append(nextImage);
 
-    markCurrentImage(galleryThmbnailsModule, nextIndex);
+  //   markCurrentImage(galleryThmbnailsModule, nextIndex);
 
-    this.process(false, previewModule);
-  }
+  //   this.process(false, previewModule);
+  // }
 
-  handleArrowClick(direction) {
+  handleArrowClick(galleryImages, direction) {
     let nextIndex = 0;
     const [length, index] = getGalleryLengthAndIndex();
 
@@ -178,26 +174,53 @@ export default class CIResponsive {
     if (direction === 'left') {
       nextIndex -= 1;
 
-      if ((length -1 + nextIndex) <= 0) {
+      if ((length - 1 + nextIndex) <= 0) { // left button
         nextIndex = length - 1;
       }
     } else {
       nextIndex += 1;
 
-      if ((length -1 + nextIndex) > length) {
+      if ((length - 1 + nextIndex) > length) { // right button
         nextIndex = 0;
       }
     }
 
+    this.processGalleryPreviewImage(galleryImages[nextIndex])
     setGalleryIndex(nextIndex);
   }
 
-  handleClickWrapper(imgSelector, imgProps, images){
-    const { gallery, zoom } = imgProps;
+  processGalleryPreviewImage(imgNode) {
+    const _imgNode = imgNode.cloneNode();
+    const adaptedImageNode = removeClassNames(_imgNode, loadedImageClassNames);
+    const previewModule = getGalleryPreviewModule();
+
+    adaptedImageNode.style = {};
+    adaptedImageNode.setAttribute('data-ci-processed-gallery', true);
+    previewModule.innerHTML = '';
+    previewModule.appendChild(adaptedImageNode);
+
+    this.process(false, previewModule);
+  }
+
+  handleClickThumbnail(galleryImages, event) {
+    const thumbnail = event.currentTarget;
+    const thumbnailIndex = thumbnail.getAttribute('data-ci-gallery-index');
+    const [, index] = getGalleryLengthAndIndex();
 
-    if(zoom && !gallery) {
+    if (thumbnailIndex !== index) {
+      setGalleryIndex(thumbnailIndex);
+      this.processGalleryPreviewImage(galleryImages[thumbnailIndex])
+    }
+  }
+
+  handleClickWrapper(imgProps, images) {
+    const { gallery, zoom, isProcessedByGallery } = imgProps;
+
+    if (isProcessedByGallery) return;
+
+    if (zoom && !gallery) {
       const galleryModal = createGalleryModal();
-      const previewModule = createGalleryPreviewModule(imgSelector, imgProps, galleryModal);
+      const previewModule = galleryModal.querySelector('.ci-gallery-preview-module');
 
       galleryModal.appendChild(previewModule);
 
@@ -209,15 +232,21 @@ export default class CIResponsive {
     if (gallery) {
       const galleryImages = getGalleryImages(images, gallery);
       const galleryModal = createGalleryModal(galleryImages.length);
-      const previewModule = createGalleryPreviewModule(imgSelector, imgProps, galleryModal);
-      const thumbnailsModule = createThmbnailsModule(galleryImages, galleryModal);
-      const galleryArrows = createGalleryArrows(this.handleArrowClick);
+      const previewModule = galleryModal.querySelector('.ci-gallery-preview-module');
+      const thumbnailsModule = createThmbnailsModule(
+        galleryImages,
+        galleryModal,
+        this.handleClickThumbnail.bind(this, galleryImages)
+      );
+
+      const galleryArrows = createGalleryArrows(this.handleArrowClick.bind(this, galleryImages));
 
       galleryModal.appendChild(previewModule);
       galleryModal.appendChild(thumbnailsModule);
       galleryModal.append(...galleryArrows);
 
       document.body.appendChild(galleryModal);
+      this.processGalleryPreviewImage(galleryImages[0]);
     }
   }
 
@@ -276,7 +305,7 @@ export default class CIResponsive {
         config.onImageLoad(imgNode);
       }
 
-      wrapper.onclick = this.handleClickWrapper.bind(this, imgSelector, imgProps, images);
+      wrapper.onclick = this.handleClickWrapper.bind(this, imgProps, images);
       wrapper.onmouseenter = () => displayZoomIcon(wrapper, imgProps);
       wrapper.onmouseout = () => destroyZoomIcon(wrapper);
 
diff --git a/src/low-preview/ci.styles.css b/src/low-preview/ci.styles.css
index b7cfda5..56f22f1 100644
--- a/src/low-preview/ci.styles.css
+++ b/src/low-preview/ci.styles.css
@@ -23,10 +23,6 @@ img.ci-image-loaded {
   opacity: 1;
 }
 
-img.ci-image-ratio.ci-image-preview {
-  height: 100%;
-}
-
 .ci-bg {
   position: relative;
 }
@@ -58,7 +54,7 @@ img.ci-image-ratio.ci-image-preview {
   position: fixed;
   top: 0;
   left: 0;
-  z-index: 110; 
+  z-index: 110;
 }
 
 .ci-gallery-preview-module {
@@ -67,6 +63,7 @@ img.ci-image-ratio.ci-image-preview {
   display: flex;
   justify-content: center;
   align-items: center;
+  position: relative;
 }
 
 .ci-gallery-thumbnail-module {

From a70f2cfc4e138542e20a0dd35a20be62ec95ba9d Mon Sep 17 00:00:00 2001
From: Amr Wagdy 
Date: Thu, 1 Sep 2022 11:13:08 +0200
Subject: [PATCH 05/45] Refactor: Code review

---
 .eslintrc.json                |  1 +
 src/common/ci.utils.js        | 40 +++++++++++++++++------------------
 src/low-preview/ci.service.js | 21 +++++++++++-------
 3 files changed, 34 insertions(+), 28 deletions(-)

diff --git a/.eslintrc.json b/.eslintrc.json
index b5129e1..c5e2d6c 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -18,6 +18,7 @@
       "error",
       "prefer-double"
     ],
+    "default-param-last": "off",
     "max-lines": [
       "warn",
       {
diff --git a/src/common/ci.utils.js b/src/common/ci.utils.js
index 71e7efa..03955bb 100644
--- a/src/common/ci.utils.js
+++ b/src/common/ci.utils.js
@@ -94,7 +94,7 @@ const getImageProps = (image, imgSelector) => {
   const props = {
     ...getCommonImageProps(image),
     imgNodeSRC: attr(image, imgSelector) || undefined,
-    isProcessedByGallery: isTrue(image, 'data-ci-processed-gallery')
+    isProcessedByGallery: isTrue(image, 'data-ci-processed-gallery'),
   };
 
   const params = {
@@ -256,7 +256,7 @@ const galleryPreviewImage = (imgSelector, imgNodeSRC) => {
   image.setAttribute(imgSelector, imgNodeSRC);
 
   return image;
-}
+};
 
 const adaptGalleryThumbnails = (thumbnails = [], onClick) => thumbnails.map((thumbnail, index) => {
   const thmbnailContainer = document.createElement('div');
@@ -280,18 +280,18 @@ const adaptGalleryThumbnails = (thumbnails = [], onClick) => thumbnails.map((thu
 
 const appendGalleryThumbnails = (thumbnails = [], thumbnailsContainer) => {
   thumbnails.forEach((thumbnail) => {
-    thumbnailsContainer.append(thumbnail)
-  })
+    thumbnailsContainer.append(thumbnail);
+  });
 };
 
 const createThmbnailsModule = (images, galleryModal, onClick) => {
   const thumbnailsModule = galleryModal.querySelector('.ci-gallery-thumbnail-module');
   const adaptedGalleryThumbnails = adaptGalleryThumbnails(images, onClick);
 
-  appendGalleryThumbnails(adaptedGalleryThumbnails, thumbnailsModule)
+  appendGalleryThumbnails(adaptedGalleryThumbnails, thumbnailsModule);
 
   return thumbnailsModule;
-}
+};
 
 const createGalleryArrows = (onClick) => {
   const leftArrow = createIcon('../public/left-arrow-icon.svg', 'ci-gallery-left-arrow-button');
@@ -299,31 +299,31 @@ const createGalleryArrows = (onClick) => {
 
   if (onClick) {
     leftArrow.onclick = onClick.bind(this, 'left');
-    rightArrow.onclick = onClick.bind(this, 'right')
+    rightArrow.onclick = onClick.bind(this, 'right');
   }
 
   return [leftArrow, rightArrow];
-}
+};
 
 const getGalleryLengthAndIndex = () => {
   const galleryModal = document.body.querySelector('[data-ci-gallery]');
   const galleryLength = galleryModal.getAttribute('data-ci-gallery-length');
   const galleryIndex = galleryModal.getAttribute('data-ci-gallery-index');
 
-  return [galleryLength, galleryIndex]
-}
+  return [galleryLength, galleryIndex];
+};
 
 const getGalleryPreviewModule = () => {
   const galleryModal = document.body.querySelector('[data-ci-gallery]');
 
-  return galleryModal.querySelector('.ci-gallery-preview-module')
-}
+  return galleryModal.querySelector('.ci-gallery-preview-module');
+};
 
 const setGalleryIndex = (index) => {
   const galleryModal = document.body.querySelector('[data-ci-gallery]');
 
-  galleryModal.setAttribute('data-ci-gallery-index', index)
-}
+  galleryModal.setAttribute('data-ci-gallery-index', index);
+};
 
 const createGalleryModal = (galleryLength) => {
   const galleryModal = document.createElement('div');
@@ -345,7 +345,7 @@ const createGalleryModal = (galleryLength) => {
   closeIcon.onclick = destroyGallery.bind(this, galleryModal);
 
   return galleryModal;
-}
+};
 
 const markCurrentImage = (galleryThmbnails, currentIndex) => {
   galleryThmbnails.forEach((imgWrapper, index) => {
@@ -355,7 +355,7 @@ const markCurrentImage = (galleryThmbnails, currentIndex) => {
       imgWrapper.querySelector('img').style.border = '1px solid white';
     }
   });
-}
+};
 
 const getCurrentImage = (mainImageWrapper, galleryModal) => {
   const galleryThmbnailsModule = galleryModal.querySelector('.ci-gallery-thumbnail-module');
@@ -374,7 +374,7 @@ const getCurrentImage = (mainImageWrapper, galleryModal) => {
   });
 
   return currentIndex;
-}
+};
 
 const displayZoomIcon = (wrapper, imgProps) => {
   const { zoom, gallery } = imgProps;
@@ -383,7 +383,7 @@ const displayZoomIcon = (wrapper, imgProps) => {
     const zoomIcon = createIcon('../public/right-arrow-icon.svg', 'ci-gallery-zoom-button');
     wrapper.append(zoomIcon);
   }
-}
+};
 
 const destroyZoomIcon = (wrapper) => {
   const zoomIcon = wrapper.querySelector('.ci-gallery-zoom-button');
@@ -391,7 +391,7 @@ const destroyZoomIcon = (wrapper) => {
   if (zoomIcon) {
     zoomIcon.remove();
   }
-}
+};
 
 export {
   getParams,
@@ -421,5 +421,5 @@ export {
   createGalleryArrows,
   getGalleryLengthAndIndex,
   setGalleryIndex,
-  getGalleryPreviewModule
+  getGalleryPreviewModule,
 };
diff --git a/src/low-preview/ci.service.js b/src/low-preview/ci.service.js
index 2015b02..c04d878 100644
--- a/src/low-preview/ci.service.js
+++ b/src/low-preview/ci.service.js
@@ -145,7 +145,9 @@ export default class CIResponsive {
     };
 
     if (isImage) {
-      this.processImage({ ...props, cloudimageUrl: generateURLbyDPR(1), cloudimageSrcset, images });
+      this.processImage({
+        ...props, cloudimageUrl: generateURLbyDPR(1), cloudimageSrcset, images,
+      });
     } else {
       this.processBackgroundImage(props);
     }
@@ -185,7 +187,7 @@ export default class CIResponsive {
       }
     }
 
-    this.processGalleryPreviewImage(galleryImages[nextIndex])
+    this.processGalleryPreviewImage(galleryImages[nextIndex]);
     setGalleryIndex(nextIndex);
   }
 
@@ -209,11 +211,11 @@ export default class CIResponsive {
 
     if (thumbnailIndex !== index) {
       setGalleryIndex(thumbnailIndex);
-      this.processGalleryPreviewImage(galleryImages[thumbnailIndex])
+      this.processGalleryPreviewImage(galleryImages[thumbnailIndex]);
     }
   }
 
-  handleClickWrapper(imgProps, images) {
+  handleClickWrapper(imgProps, images, event) {
     const { gallery, zoom, isProcessedByGallery } = imgProps;
 
     if (isProcessedByGallery) return;
@@ -230,13 +232,16 @@ export default class CIResponsive {
     }
 
     if (gallery) {
+      const clickedImage = event.currentTarget.lastChild;
       const galleryImages = getGalleryImages(images, gallery);
+      const clickedImageIndex = galleryImages.indexOf(clickedImage);
+
       const galleryModal = createGalleryModal(galleryImages.length);
       const previewModule = galleryModal.querySelector('.ci-gallery-preview-module');
       const thumbnailsModule = createThmbnailsModule(
         galleryImages,
         galleryModal,
-        this.handleClickThumbnail.bind(this, galleryImages)
+        this.handleClickThumbnail.bind(this, galleryImages),
       );
 
       const galleryArrows = createGalleryArrows(this.handleArrowClick.bind(this, galleryImages));
@@ -244,9 +249,10 @@ export default class CIResponsive {
       galleryModal.appendChild(previewModule);
       galleryModal.appendChild(thumbnailsModule);
       galleryModal.append(...galleryArrows);
-
       document.body.appendChild(galleryModal);
-      this.processGalleryPreviewImage(galleryImages[0]);
+
+      this.processGalleryPreviewImage(galleryImages[clickedImageIndex]);
+      setGalleryIndex(clickedImageIndex);
     }
   }
 
@@ -264,7 +270,6 @@ export default class CIResponsive {
       preserveSize,
       cloudimageSrcset,
       isAdaptive,
-      imgSelector,
       images,
       alt,
     } = props;

From 93d34a39b52775ca918ea74f1bc642372ce2e760 Mon Sep 17 00:00:00 2001
From: Amr Wagdy 
Date: Thu, 1 Sep 2022 11:21:09 +0200
Subject: [PATCH 06/45] Refactor: Code review

---
 src/low-preview/ci.service.js | 28 ++++++++++++++++------------
 1 file changed, 16 insertions(+), 12 deletions(-)

diff --git a/src/low-preview/ci.service.js b/src/low-preview/ci.service.js
index c04d878..b16531b 100644
--- a/src/low-preview/ci.service.js
+++ b/src/low-preview/ci.service.js
@@ -220,17 +220,6 @@ export default class CIResponsive {
 
     if (isProcessedByGallery) return;
 
-    if (zoom && !gallery) {
-      const galleryModal = createGalleryModal();
-      const previewModule = galleryModal.querySelector('.ci-gallery-preview-module');
-
-      galleryModal.appendChild(previewModule);
-
-      document.body.appendChild(galleryModal);
-
-      this.process(false, previewModule);
-    }
-
     if (gallery) {
       const clickedImage = event.currentTarget.lastChild;
       const galleryImages = getGalleryImages(images, gallery);
@@ -254,6 +243,17 @@ export default class CIResponsive {
       this.processGalleryPreviewImage(galleryImages[clickedImageIndex]);
       setGalleryIndex(clickedImageIndex);
     }
+
+    if (zoom && !gallery) {
+      const galleryModal = createGalleryModal();
+      const previewModule = galleryModal.querySelector('.ci-gallery-preview-module');
+
+      galleryModal.appendChild(previewModule);
+
+      document.body.appendChild(galleryModal);
+
+      this.process(false, previewModule);
+    }
   }
 
   processImage(props) {
@@ -273,7 +273,7 @@ export default class CIResponsive {
       images,
       alt,
     } = props;
-    const { params } = imgProps;
+    const { params, gallery, isProcessedByGallery } = imgProps;
     const { width, ratio } = containerProps;
     const { config } = this;
     const { dataSrcAttr, placeholderBackground } = config;
@@ -310,6 +310,10 @@ export default class CIResponsive {
         config.onImageLoad(imgNode);
       }
 
+      if (gallery && !isProcessedByGallery) {
+        wrapper.style.cursor = 'pointer';
+      }
+
       wrapper.onclick = this.handleClickWrapper.bind(this, imgProps, images);
       wrapper.onmouseenter = () => displayZoomIcon(wrapper, imgProps);
       wrapper.onmouseout = () => destroyZoomIcon(wrapper);

From 44a488fb44b62a2a78c5fd95065c40d306128b40 Mon Sep 17 00:00:00 2001
From: Amr Wagdy 
Date: Thu, 1 Sep 2022 11:23:48 +0200
Subject: [PATCH 07/45] Refactor: Code review

---
 src/common/ci.utils.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/common/ci.utils.js b/src/common/ci.utils.js
index 03955bb..53b566c 100644
--- a/src/common/ci.utils.js
+++ b/src/common/ci.utils.js
@@ -225,7 +225,7 @@ const setOptions = (node, options) => {
   return node;
 };
 
-const getGalleryImages = (images, galleryName) => images.filter((image) => {
+const getGalleryImages = (images = [], galleryName) => images.filter((image) => {
   const { gallery } = getCommonImageProps(image);
 
   return gallery === galleryName;

From cc85b4ed70beeb134b5b3ac666e0ddeb2e2770cf Mon Sep 17 00:00:00 2001
From: Amr Wagdy 
Date: Thu, 1 Sep 2022 11:29:22 +0200
Subject: [PATCH 08/45] Refactor: Code review

---
 src/low-preview/ci.service.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/low-preview/ci.service.js b/src/low-preview/ci.service.js
index b16531b..0e0b214 100644
--- a/src/low-preview/ci.service.js
+++ b/src/low-preview/ci.service.js
@@ -201,7 +201,7 @@ export default class CIResponsive {
     previewModule.innerHTML = '';
     previewModule.appendChild(adaptedImageNode);
 
-    this.process(false, previewModule);
+    this.getBasicInfo(adaptedImageNode, false, false, 'image');
   }
 
   handleClickThumbnail(galleryImages, event) {

From 44082269a6ce97a0286ca1462d1919f25d20da82 Mon Sep 17 00:00:00 2001
From: Amr Wagdy 
Date: Thu, 1 Sep 2022 13:00:38 +0200
Subject: [PATCH 09/45] Feat:Fit gallery images - T7582

---
 src/low-preview/ci.service.js | 31 ++++++++++++++++++++++---------
 src/low-preview/ci.styles.css | 15 +++++++++++++--
 src/low-preview/ci.utis.js    |  8 ++++++--
 3 files changed, 41 insertions(+), 13 deletions(-)

diff --git a/src/low-preview/ci.service.js b/src/low-preview/ci.service.js
index 0e0b214..7baf453 100644
--- a/src/low-preview/ci.service.js
+++ b/src/low-preview/ci.service.js
@@ -87,7 +87,7 @@ export default class CIResponsive {
     }
   }
 
-  getBasicInfo = (imgNode, isUpdate, windowScreenBecomesBigger, type, images) => {
+  getBasicInfo = (imgNode, isUpdate, windowScreenBecomesBigger, type, images = [], isGalleryImg) => {
     const isImage = type === 'image';
     const { config } = this;
     const {
@@ -146,7 +146,11 @@ export default class CIResponsive {
 
     if (isImage) {
       this.processImage({
-        ...props, cloudimageUrl: generateURLbyDPR(1), cloudimageSrcset, images,
+        ...props,
+        cloudimageUrl: generateURLbyDPR(1),
+        cloudimageSrcset,
+        images,
+        isGalleryImg,
       });
     } else {
       this.processBackgroundImage(props);
@@ -201,7 +205,7 @@ export default class CIResponsive {
     previewModule.innerHTML = '';
     previewModule.appendChild(adaptedImageNode);
 
-    this.getBasicInfo(adaptedImageNode, false, false, 'image');
+    this.getBasicInfo(adaptedImageNode, false, false, 'image', undefined, true);
   }
 
   handleClickThumbnail(galleryImages, event) {
@@ -220,7 +224,7 @@ export default class CIResponsive {
 
     if (isProcessedByGallery) return;
 
-    if (gallery) {
+    if (gallery && images) {
       const clickedImage = event.currentTarget.lastChild;
       const galleryImages = getGalleryImages(images, gallery);
       const clickedImageIndex = galleryImages.indexOf(clickedImage);
@@ -272,16 +276,25 @@ export default class CIResponsive {
       isAdaptive,
       images,
       alt,
+      isGalleryImg,
     } = props;
+
     const { params, gallery, isProcessedByGallery } = imgProps;
     const { width, ratio } = containerProps;
     const { config } = this;
     const { dataSrcAttr, placeholderBackground } = config;
-    const { wrapper, previewImgNode, previewWrapper } = applyOrUpdateWrapper(
-      {
-        isUpdate, imgNode, ratio, lazy, placeholderBackground, preserveSize, isPreview, ...imgProps, alt,
-      },
-    );
+    const { wrapper, previewImgNode, previewWrapper } = applyOrUpdateWrapper({
+      isUpdate,
+      imgNode,
+      ratio,
+      lazy,
+      placeholderBackground,
+      preserveSize,
+      isPreview,
+      isGalleryImg,
+      ...imgProps,
+      alt,
+    });
 
     if (!isUpdate) {
       initImageClasses({ imgNode, lazy });
diff --git a/src/low-preview/ci.styles.css b/src/low-preview/ci.styles.css
index 56f22f1..913faf8 100644
--- a/src/low-preview/ci.styles.css
+++ b/src/low-preview/ci.styles.css
@@ -58,14 +58,25 @@ img.ci-image-loaded {
 }
 
 .ci-gallery-preview-module {
-  width: 100%;
-  height: 100%;
+  width: auto;
+  height: 800px;
   display: flex;
   justify-content: center;
   align-items: center;
   position: relative;
 }
 
+.ci-gallery-preview-module img.ci-image {
+  display: block;
+  width: 100%;
+  height: 100%;
+}
+
+.ci-gallery-preview-module img.ci-image-preview {
+  width: 100%;
+  height: 100%;
+}
+
 .ci-gallery-thumbnail-module {
   justify-content: center;
   display: flex;
diff --git a/src/low-preview/ci.utis.js b/src/low-preview/ci.utis.js
index 7ae57e3..dced654 100644
--- a/src/low-preview/ci.utis.js
+++ b/src/low-preview/ci.utis.js
@@ -150,7 +150,7 @@ export const onLazyBeforeUnveil = (event) => {
 
 export const wrapImage = (props) => {
   const {
-    imgNode, ratio, imgNodeWidth, imgNodeHeight, preserveSize, placeholderBackground,
+    imgNode, ratio, imgNodeWidth, imgNodeHeight, preserveSize, placeholderBackground, isGalleryImg,
   } = props;
   let { wrapper } = props;
 
@@ -164,7 +164,11 @@ export const wrapImage = (props) => {
   wrapper.style.overflow = 'hidden';
   wrapper.style.position = 'relative';
 
-  if (ratio) {
+  if (isGalleryImg) {
+    wrapper.style.height = '100%';
+  }
+
+  if (ratio && !isGalleryImg) {
     wrapper.style.paddingBottom = preserveSize ? 'none' : `${100 / ratio}%`;
   }
 

From 49d5ca8a363fc9b64e553e84951e72da826c5dee Mon Sep 17 00:00:00 2001
From: Amr Wagdy 
Date: Thu, 1 Sep 2022 19:32:38 +0200
Subject: [PATCH 10/45] Feat:Fit gallery images - T7582

---
 .eslintrc.json                |  1 +
 src/common/ci.utils.js        | 26 +++++++++++++++++++++++++-
 src/low-preview/ci.service.js | 28 +++++++++++++---------------
 src/low-preview/ci.styles.css | 13 ++++++++-----
 src/low-preview/ci.utis.js    |  6 +++---
 5 files changed, 50 insertions(+), 24 deletions(-)

diff --git a/.eslintrc.json b/.eslintrc.json
index c5e2d6c..8776bde 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -19,6 +19,7 @@
       "prefer-double"
     ],
     "default-param-last": "off",
+    "class-methods-use-this": "off",
     "max-lines": [
       "warn",
       {
diff --git a/src/common/ci.utils.js b/src/common/ci.utils.js
index 53b566c..a2fe007 100644
--- a/src/common/ci.utils.js
+++ b/src/common/ci.utils.js
@@ -225,7 +225,7 @@ const setOptions = (node, options) => {
   return node;
 };
 
-const getGalleryImages = (images = [], galleryName) => images.filter((image) => {
+const getGalleryImages = (images, galleryName) => images && images.filter((image) => {
   const { gallery } = getCommonImageProps(image);
 
   return gallery === galleryName;
@@ -393,6 +393,29 @@ const destroyZoomIcon = (wrapper) => {
   }
 };
 
+const getImageFitStyles = (naturalWidth, naturalHeight) => {
+  let shouldFitHorizontally;
+  const imageStyles = {};
+  const previewModule = document.body.querySelector('.ci-gallery-preview-module');
+
+  if (naturalWidth && previewModule) {
+    const imageAspectRatio = naturalWidth / naturalHeight;
+    const renderWidth = previewModule.offsetHeight * imageAspectRatio;
+    shouldFitHorizontally = (imageAspectRatio < 1)
+      || (renderWidth <= previewModule.offsetWidth);
+  }
+
+  if (shouldFitHorizontally) {
+    imageStyles.width = 'auto';
+    imageStyles.height = '100%';
+  } else {
+    imageStyles.width = '100%';
+    imageStyles.height = 'auto';
+  }
+
+  return imageStyles;
+};
+
 export {
   getParams,
   filterImages,
@@ -422,4 +445,5 @@ export {
   getGalleryLengthAndIndex,
   setGalleryIndex,
   getGalleryPreviewModule,
+  getImageFitStyles,
 };
diff --git a/src/low-preview/ci.service.js b/src/low-preview/ci.service.js
index 7baf453..f4ef41a 100644
--- a/src/low-preview/ci.service.js
+++ b/src/low-preview/ci.service.js
@@ -28,6 +28,7 @@ import {
   getGalleryLengthAndIndex,
   setGalleryIndex,
   getGalleryPreviewModule,
+  getImageFitStyles,
 } from '../common/ci.utils';
 import { getInitialConfigLowPreview } from './ci.config';
 import {
@@ -134,7 +135,7 @@ export default class CIResponsive {
       ...imgProps, size, imgNode, config,
     });
     const { width } = containerProps;
-    const isPreview = isLowQualityPreview(isAdaptive, width, isSVG, minLowQualityWidth);
+    const isPreview = !isGalleryImg && isLowQualityPreview(isAdaptive, width, isSVG, minLowQualityWidth);
     const generateURLbyDPR = (devicePixelRatio) => generateURL({
       src, params, config, containerProps, devicePixelRatio,
     });
@@ -157,20 +158,6 @@ export default class CIResponsive {
     }
   };
 
-  // processNextImage = (nextIndex, galleryThmbnailsModule, imgSelector, galleryModal) => {
-  //   const nextImageSrc = galleryThmbnailsModule[nextIndex].querySelector('[ci-src]').getAttribute('ci-src');
-  //   const nextImage = galleryPreviewImage(imgSelector, nextImageSrc);
-
-  //   const previewModule = galleryModal.querySelector('.ci-gallery-preview-module');
-
-  //   previewModule.removeChild(previewModule.firstElementChild);
-  //   previewModule.append(nextImage);
-
-  //   markCurrentImage(galleryThmbnailsModule, nextIndex);
-
-  //   this.process(false, previewModule);
-  // }
-
   handleArrowClick(galleryImages, direction) {
     let nextIndex = 0;
     const [length, index] = getGalleryLengthAndIndex();
@@ -318,6 +305,17 @@ export default class CIResponsive {
       }
     }
 
+    const dimInterval = setInterval(() => {
+      if (imgNode.naturalWidth) {
+        clearInterval(dimInterval);
+        const imageFitStyles = getImageFitStyles(imgNode.naturalWidth, imgNode.naturalHeight);
+
+        imgNode.style.width = imageFitStyles.width;
+        imgNode.style.height = imageFitStyles.height;
+        imgNode.style.opacity = 1;
+      }
+    }, 10);
+
     imgNode.onload = () => {
       if (config.onImageLoad && typeof config.onImageLoad === 'function') {
         config.onImageLoad(imgNode);
diff --git a/src/low-preview/ci.styles.css b/src/low-preview/ci.styles.css
index 913faf8..49ff120 100644
--- a/src/low-preview/ci.styles.css
+++ b/src/low-preview/ci.styles.css
@@ -58,11 +58,7 @@ img.ci-image-loaded {
 }
 
 .ci-gallery-preview-module {
-  width: auto;
-  height: 800px;
-  display: flex;
-  justify-content: center;
-  align-items: center;
+  height: 100%;
   position: relative;
 }
 
@@ -70,6 +66,9 @@ img.ci-image-loaded {
   display: block;
   width: 100%;
   height: 100%;
+  top: 50%;
+  left: 50%;
+  transform: translate3d(-50%, -50%, 0);
 }
 
 .ci-gallery-preview-module img.ci-image-preview {
@@ -77,6 +76,10 @@ img.ci-image-loaded {
   height: 100%;
 }
 
+.ci-gallery-preview-module img[ci-src] {
+  background-color: black;
+}
+
 .ci-gallery-thumbnail-module {
   justify-content: center;
   display: flex;
diff --git a/src/low-preview/ci.utis.js b/src/low-preview/ci.utis.js
index dced654..bae8c8d 100644
--- a/src/low-preview/ci.utis.js
+++ b/src/low-preview/ci.utis.js
@@ -157,6 +157,7 @@ export const wrapImage = (props) => {
   wrapper = wrapper || document.createElement('div');
 
   addClass(wrapper, 'ci-image-wrapper');
+
   wrapper.style.background = placeholderBackground;
   wrapper.style.display = 'block';
   wrapper.style.width = preserveSize ? imgNodeWidth : '100%';
@@ -166,9 +167,8 @@ export const wrapImage = (props) => {
 
   if (isGalleryImg) {
     wrapper.style.height = '100%';
-  }
-
-  if (ratio && !isGalleryImg) {
+    wrapper.style.background = '';
+  } else if (ratio) {
     wrapper.style.paddingBottom = preserveSize ? 'none' : `${100 / ratio}%`;
   }
 

From 003c5420cfabd2c82cc628eadfc532ac4dc329b1 Mon Sep 17 00:00:00 2001
From: Nermeen Moustafa 
Date: Thu, 1 Sep 2022 21:05:18 +0200
Subject: [PATCH 11/45] add possibility to see images in full screen - T7582

---
 config/low-preview/webpack-demo.config.js | 45 ++++++++++++++---------
 package.json                              |  1 +
 src/common/ci.utils.js                    | 45 ++++++++++++++++-------
 src/low-preview/ci.service.js             | 19 ++++++++--
 src/low-preview/ci.styles.css             | 22 +++++++++++
 src/public/close-icon.svg                 |  1 +
 src/public/left-arrow-icon.svg            |  1 +
 src/public/right-arrow-icon.svg           |  1 +
 src/public/zoom-icon.svg                  |  1 +
 yarn.lock                                 |  8 ++++
 10 files changed, 109 insertions(+), 35 deletions(-)
 create mode 100644 src/public/close-icon.svg
 create mode 100644 src/public/left-arrow-icon.svg
 create mode 100644 src/public/right-arrow-icon.svg
 create mode 100644 src/public/zoom-icon.svg

diff --git a/config/low-preview/webpack-demo.config.js b/config/low-preview/webpack-demo.config.js
index 880f3a1..f73fa64 100644
--- a/config/low-preview/webpack-demo.config.js
+++ b/config/low-preview/webpack-demo.config.js
@@ -1,43 +1,52 @@
 const path = require('path');
-const HtmlWebpackPlugin = require("html-webpack-plugin");
-const MiniCssExtractPlugin = require("mini-css-extract-plugin");
+const HtmlWebpackPlugin = require('html-webpack-plugin');
+const MiniCssExtractPlugin = require('mini-css-extract-plugin');
+
 
 const htmlWebpackPlugin = new HtmlWebpackPlugin({
-  template: path.join(__dirname, "../../examples/low-preview/src/index.html"),
-  filename: "./index.html"
+  template: path.join(__dirname, '../../examples/low-preview/src/index.html'),
+  filename: './index.html',
 });
 
 const miniCssExtractPlugin = new MiniCssExtractPlugin({
   // Options similar to the same options in webpackOptions.output
   // both options are optional
-  filename: "[name].css",
-  chunkFilename: "[id].css",
-})
+  filename: '[name].css',
+  chunkFilename: '[id].css',
+});
 module.exports = {
-  entry: path.resolve(__dirname, "../../examples/low-preview/src/index.js"),
+  entry: path.resolve(__dirname, '../../examples/low-preview/src/index.js'),
   output: {
-    path: path.join(__dirname, "../../examples/low-preview/dist"),
-    filename: "bundle[hash].js"
+    path: path.join(__dirname, '../../examples/low-preview/dist'),
+    filename: 'bundle[hash].js',
   },
   module: {
     rules: [
       {
         test: /\.(js|jsx)$/,
-        use: "babel-loader",
-        exclude: /node_modules/
+        use: 'babel-loader',
+        exclude: /node_modules/,
       },
       {
         test: /\.css$/,
-        use: [ MiniCssExtractPlugin.loader, "css-loader" ],
+        use: [MiniCssExtractPlugin.loader, 'css-loader'],
+      },
+      {
+        test: /\.(png|jpe?g|gif|svg)$/i,
+        use: [
+          {
+            loader: 'file-loader',
+          },
+        ],
       },
-    ]
+    ],
   },
   plugins: [htmlWebpackPlugin, miniCssExtractPlugin],
   resolve: {
-    extensions: [".js", ".jsx"]
+    extensions: ['.js', '.jsx'],
   },
   devServer: {
-    port: 3001
+    port: 3001,
   },
-  devtool: 'source-map'
-};
\ No newline at end of file
+  devtool: 'source-map',
+};
diff --git a/package.json b/package.json
index 5320019..53d67e0 100644
--- a/package.json
+++ b/package.json
@@ -81,6 +81,7 @@
     "eslint-config-airbnb-base": "^15.0.0",
     "eslint-plugin-import": "^2.25.2",
     "extract-text-webpack-plugin": "^1.0.1",
+    "file-loader": "^6.2.0",
     "gh-pages": "^2.2.0",
     "highlight.js": "^11.1.0",
     "html-webpack-plugin": "^5.5.0",
diff --git a/src/common/ci.utils.js b/src/common/ci.utils.js
index 53b566c..4e0c552 100644
--- a/src/common/ci.utils.js
+++ b/src/common/ci.utils.js
@@ -225,22 +225,32 @@ const setOptions = (node, options) => {
   return node;
 };
 
+const getZoomImage = (images, imgSrc) => images.filter((image) => imgSrc === image.getAttribute('ci-src'));
+
 const getGalleryImages = (images = [], galleryName) => images.filter((image) => {
   const { gallery } = getCommonImageProps(image);
 
   return gallery === galleryName;
 });
 
-const createIcon = (iconSrc, className) => {
+const createIcon = (iconSrc, className, iconStyles) => {
+  const { color, width = 15, height = 15 } = iconStyles;
+
   const iconWrapper = document.createElement('div');
   const icon = new Image();
 
   icon.src = iconSrc;
+  icon.width = width;
+  icon.height = height;
 
   if (className) {
     iconWrapper.classList.add(className);
   }
 
+  if (color) {
+    iconWrapper.style.backgroundColor = color;
+  }
+
   iconWrapper.appendChild(icon);
 
   return iconWrapper;
@@ -293,9 +303,11 @@ const createThmbnailsModule = (images, galleryModal, onClick) => {
   return thumbnailsModule;
 };
 
-const createGalleryArrows = (onClick) => {
-  const leftArrow = createIcon('../public/left-arrow-icon.svg', 'ci-gallery-left-arrow-button');
-  const rightArrow = createIcon('../public/right-arrow-icon.svg', 'ci-gallery-right-arrow-button');
+const createGalleryArrows = (leftArrowIcon, rightArrowIcon, onClick) => {
+  const iconStyles = { color: 'rgba(255,255,255,0.5)' };
+
+  const leftArrow = createIcon(leftArrowIcon, 'ci-gallery-left-arrow-button', iconStyles);
+  const rightArrow = createIcon(rightArrowIcon, 'ci-gallery-right-arrow-button', iconStyles);
 
   if (onClick) {
     leftArrow.onclick = onClick.bind(this, 'left');
@@ -325,21 +337,26 @@ const setGalleryIndex = (index) => {
   galleryModal.setAttribute('data-ci-gallery-index', index);
 };
 
-const createGalleryModal = (galleryLength) => {
+const createGalleryModal = (galleryLength, closeIconSrc, isGallery) => {
+  const iconStyles = { color: 'rgba(255,255,255,0.5)' };
+
   const galleryModal = document.createElement('div');
   const previewModule = document.createElement('div');
-  const thumbnailsModule = document.createElement('div');
-  const closeIcon = createIcon('../public/close-icon.svg', 'ci-gallery-close-button');
+  const closeIcon = createIcon(closeIconSrc, 'ci-gallery-close-button', iconStyles);
 
   galleryModal.classList.add('ci-gallery-modal');
   previewModule.classList.add('ci-gallery-preview-module');
-  thumbnailsModule.classList.add('ci-gallery-thumbnail-module');
+
+  if (isGallery) {
+    const thumbnailsModule = document.createElement('div');
+    thumbnailsModule.classList.add('ci-gallery-thumbnail-module');
+    galleryModal.setAttribute('data-ci-gallery-length', galleryLength);
+    galleryModal.setAttribute('data-ci-gallery-index', 0);
+    galleryModal.append(thumbnailsModule);
+  }
 
   galleryModal.setAttribute('data-ci-gallery', true);
-  galleryModal.setAttribute('data-ci-gallery-length', galleryLength);
-  galleryModal.setAttribute('data-ci-gallery-index', 0);
   galleryModal.append(previewModule);
-  galleryModal.append(thumbnailsModule);
   galleryModal.append(closeIcon);
 
   closeIcon.onclick = destroyGallery.bind(this, galleryModal);
@@ -376,11 +393,12 @@ const getCurrentImage = (mainImageWrapper, galleryModal) => {
   return currentIndex;
 };
 
-const displayZoomIcon = (wrapper, imgProps) => {
+const displayZoomIcon = (wrapper, imgProps, zoomIconSrc) => {
   const { zoom, gallery } = imgProps;
+  const iconStyles = { width: 35, height: 35 };
 
   if (zoom && !gallery) {
-    const zoomIcon = createIcon('../public/right-arrow-icon.svg', 'ci-gallery-zoom-button');
+    const zoomIcon = createIcon(zoomIconSrc, 'ci-gallery-zoom-button', iconStyles);
     wrapper.append(zoomIcon);
   }
 };
@@ -422,4 +440,5 @@ export {
   getGalleryLengthAndIndex,
   setGalleryIndex,
   getGalleryPreviewModule,
+  getZoomImage,
 };
diff --git a/src/low-preview/ci.service.js b/src/low-preview/ci.service.js
index 7baf453..49fa66c 100644
--- a/src/low-preview/ci.service.js
+++ b/src/low-preview/ci.service.js
@@ -28,6 +28,7 @@ import {
   getGalleryLengthAndIndex,
   setGalleryIndex,
   getGalleryPreviewModule,
+  getZoomImage,
 } from '../common/ci.utils';
 import { getInitialConfigLowPreview } from './ci.config';
 import {
@@ -43,6 +44,10 @@ import {
   updateSizeWithPixelRatio,
 } from './ci.utis';
 import { bgContentAttr, loadedImageClassNames, processedAttr } from '../common/ci.constants';
+import closeIconSvg from '../public/close-icon.svg';
+import rightArrowSvg from '../public/right-arrow-icon.svg';
+import leftArrowSvg from '../public/left-arrow-icon.svg';
+import zoomIconSvg from '../public/zoom-icon.svg';
 
 
 export default class CIResponsive {
@@ -220,7 +225,9 @@ export default class CIResponsive {
   }
 
   handleClickWrapper(imgProps, images, event) {
-    const { gallery, zoom, isProcessedByGallery } = imgProps;
+    const {
+      gallery, zoom, isProcessedByGallery, imgNodeSRC,
+    } = imgProps;
 
     if (isProcessedByGallery) return;
 
@@ -229,7 +236,7 @@ export default class CIResponsive {
       const galleryImages = getGalleryImages(images, gallery);
       const clickedImageIndex = galleryImages.indexOf(clickedImage);
 
-      const galleryModal = createGalleryModal(galleryImages.length);
+      const galleryModal = createGalleryModal(galleryImages.length, closeIconSvg, true);
       const previewModule = galleryModal.querySelector('.ci-gallery-preview-module');
       const thumbnailsModule = createThmbnailsModule(
         galleryImages,
@@ -237,7 +244,11 @@ export default class CIResponsive {
         this.handleClickThumbnail.bind(this, galleryImages),
       );
 
-      const galleryArrows = createGalleryArrows(this.handleArrowClick.bind(this, galleryImages));
+      const galleryArrows = createGalleryArrows(
+        leftArrowSvg,
+        rightArrowSvg,
+        this.handleArrowClick.bind(this, galleryImages),
+      );
 
       galleryModal.appendChild(previewModule);
       galleryModal.appendChild(thumbnailsModule);
@@ -328,7 +339,7 @@ export default class CIResponsive {
       }
 
       wrapper.onclick = this.handleClickWrapper.bind(this, imgProps, images);
-      wrapper.onmouseenter = () => displayZoomIcon(wrapper, imgProps);
+      wrapper.onmouseenter = () => displayZoomIcon(wrapper, imgProps, zoomIconSvg);
       wrapper.onmouseout = () => destroyZoomIcon(wrapper);
 
       onImageLoad(wrapper, previewImgNode, imgNode, ratio, preserveSize, isAdaptive);
diff --git a/src/low-preview/ci.styles.css b/src/low-preview/ci.styles.css
index 913faf8..fb34191 100644
--- a/src/low-preview/ci.styles.css
+++ b/src/low-preview/ci.styles.css
@@ -89,10 +89,17 @@ img.ci-image-loaded {
 }
 
 .ci-gallery-close-button {
+  width: 23px;
+  height: 23px;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  border-radius: 50%;
   position: absolute;
   top: 15px;
   right: 15px;
   z-index: 111;
+  cursor: pointer;
 }
 
 .ci-gallery-thmbnail-container {
@@ -105,17 +112,32 @@ img.ci-image-loaded {
 }
 
 .ci-gallery-right-arrow-button {
+  width: 25px;
+  height: 25px;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  border-radius: 50%;
+  position: absolute;
   position: absolute;
   top: 50%;
   right: 15px;
   z-index: 111;
+  cursor: pointer;
 }
 
 .ci-gallery-left-arrow-button {
+  width: 25px;
+  height: 25px;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  border-radius: 50%;
   position: absolute;
   top: 50%;
   left: 15px;
   z-index: 111;
+  cursor: pointer;
 }
 
 .ci-gallery-zoom-button {
diff --git a/src/public/close-icon.svg b/src/public/close-icon.svg
new file mode 100644
index 0000000..093b80a
--- /dev/null
+++ b/src/public/close-icon.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/public/left-arrow-icon.svg b/src/public/left-arrow-icon.svg
new file mode 100644
index 0000000..b6ba7c7
--- /dev/null
+++ b/src/public/left-arrow-icon.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/public/right-arrow-icon.svg b/src/public/right-arrow-icon.svg
new file mode 100644
index 0000000..4bccdcc
--- /dev/null
+++ b/src/public/right-arrow-icon.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/public/zoom-icon.svg b/src/public/zoom-icon.svg
new file mode 100644
index 0000000..c8f2eab
--- /dev/null
+++ b/src/public/zoom-icon.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/yarn.lock b/yarn.lock
index ad12d26..e2d7468 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3237,6 +3237,14 @@ file-entry-cache@^6.0.1:
   dependencies:
     flat-cache "^3.0.4"
 
+file-loader@^6.2.0:
+  version "6.2.0"
+  resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-6.2.0.tgz#baef7cf8e1840df325e4390b4484879480eebe4d"
+  integrity sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==
+  dependencies:
+    loader-utils "^2.0.0"
+    schema-utils "^3.0.0"
+
 filename-reserved-regex@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/filename-reserved-regex/-/filename-reserved-regex-1.0.0.tgz#e61cf805f0de1c984567d0386dc5df50ee5af7e4"

From c6a660282b1e27a6fdf061ad572c55a15aa21c03 Mon Sep 17 00:00:00 2001
From: Nermeen Moustafa 
Date: Fri, 2 Sep 2022 13:55:54 +0200
Subject: [PATCH 12/45] feat: possibility to view image in full screen - T7582

---
 src/common/ci.constants.js    |  3 ++
 src/common/ci.utils.js        | 62 +++++++++++++++++++++++++++--------
 src/low-preview/ci.service.js | 31 ++++++++++++++----
 src/low-preview/ci.styles.css | 29 ++++++++++++++++
 4 files changed, 104 insertions(+), 21 deletions(-)

diff --git a/src/common/ci.constants.js b/src/common/ci.constants.js
index 0f7cc1e..cde072f 100644
--- a/src/common/ci.constants.js
+++ b/src/common/ci.constants.js
@@ -6,9 +6,12 @@ const canvasAttr = 'data-ci-canvas';
 
 const loadedImageClassNames = ['ci-image-loaded', 'lazyloaded', 'ci-image'];
 
+const previewContainer = 'ci-gallery-preview-module';
+
 export {
   processedAttr,
   bgContentAttr,
   canvasAttr,
   loadedImageClassNames,
+  previewContainer,
 };
diff --git a/src/common/ci.utils.js b/src/common/ci.utils.js
index db18483..09f379e 100644
--- a/src/common/ci.utils.js
+++ b/src/common/ci.utils.js
@@ -1,5 +1,6 @@
 /* eslint-disable no-empty */
 import { getParamsFromURL } from 'cloudimage-responsive-utils/dist/utils/get-params-from-url';
+import { previewContainer } from './ci.constants';
 
 
 const attr = (element, attribute) => element.getAttribute(attribute);
@@ -227,7 +228,7 @@ const setOptions = (node, options) => {
 
 const getZoomImage = (images, imgSrc) => images.filter((image) => imgSrc === image.getAttribute('ci-src'));
 
-const getGalleryImages = (images, galleryName) => images && images.filter((image) => {
+const getGalleryImages = (images, galleryName) => images && [...images].filter((image) => {
   const { gallery } = getCommonImageProps(image);
 
   return gallery === galleryName;
@@ -257,7 +258,11 @@ const createIcon = (iconSrc, className, iconStyles) => {
 };
 
 const destroyGallery = (galleryModal) => {
-  galleryModal.parentNode.removeChild(galleryModal);
+  galleryModal.style.animation = 'fadeOut 0.7s';
+
+  setTimeout(() => {
+    galleryModal.parentNode.removeChild(galleryModal);
+  }, 700);
 };
 
 const galleryPreviewImage = (imgSelector, imgNodeSRC) => {
@@ -393,21 +398,50 @@ const getCurrentImage = (mainImageWrapper, galleryModal) => {
   return currentIndex;
 };
 
-const displayZoomIcon = (wrapper, imgProps, zoomIconSrc) => {
-  const { zoom, gallery } = imgProps;
-  const iconStyles = { width: 35, height: 35 };
+const handleHoveringWrapper = (wrapper, imgProps, imgNode, zoomIconSrc) => {
+  const isPreviewWrapper = wrapper.parentNode.className === 'ci-gallery-preview-module';
+
+  if (!isPreviewWrapper) {
+    const { zoom, gallery } = imgProps;
+
+    if (zoom && !gallery) {
+      const iconStyles = { width: 35, height: 35 };
+      const zoomIcon = createIcon(zoomIconSrc, 'ci-gallery-zoom-button', iconStyles);
+
+      zoomIcon.style.animation = 'fadeIn 0.3s';
+
+      wrapper.append(zoomIcon);
+    }
 
-  if (zoom && !gallery) {
-    const zoomIcon = createIcon(zoomIconSrc, 'ci-gallery-zoom-button', iconStyles);
-    wrapper.append(zoomIcon);
+    if (gallery) {
+      wrapper.style.transform = 'scale(1.05)';
+      wrapper.style.transition = 'transform 0.5s ease';
+      wrapper.style.zIndex = '2';
+    }
   }
 };
 
-const destroyZoomIcon = (wrapper) => {
-  const zoomIcon = wrapper.querySelector('.ci-gallery-zoom-button');
+const handleUnHoveringWrapper = (wrapper, imgProps, imgNode) => {
+  const isPreviewWrapper = wrapper.parentNode.className === previewContainer;
+
+  if (!isPreviewWrapper) {
+    const { zoom, gallery } = imgProps;
+
+    if (zoom && !gallery) {
+      const zoomIcon = wrapper.querySelector('.ci-gallery-zoom-button');
+      zoomIcon.style.animation = 'fadeOut 0.4s';
 
-  if (zoomIcon) {
-    zoomIcon.remove();
+      setTimeout(() => {
+        if (zoomIcon) {
+          zoomIcon.remove();
+        }
+      }, 300);
+    }
+
+    if (gallery) {
+      wrapper.style.transform = 'scale(1)';
+      wrapper.style.zIndex = '1';
+    }
   }
 };
 
@@ -456,8 +490,8 @@ export {
   createGalleryModal,
   markCurrentImage,
   getCurrentImage,
-  displayZoomIcon,
-  destroyZoomIcon,
+  handleHoveringWrapper,
+  handleUnHoveringWrapper,
   getGalleryImages,
   createGalleryArrows,
   getGalleryLengthAndIndex,
diff --git a/src/low-preview/ci.service.js b/src/low-preview/ci.service.js
index d9de09e..f07b1dd 100644
--- a/src/low-preview/ci.service.js
+++ b/src/low-preview/ci.service.js
@@ -21,8 +21,8 @@ import {
   setSrcset,
   createGalleryModal,
   createThmbnailsModule,
-  displayZoomIcon,
-  destroyZoomIcon,
+  handleHoveringWrapper,
+  handleUnHoveringWrapper,
   getGalleryImages,
   createGalleryArrows,
   getGalleryLengthAndIndex,
@@ -44,7 +44,9 @@ import {
   wrapBackgroundContainer,
   updateSizeWithPixelRatio,
 } from './ci.utis';
-import { bgContentAttr, loadedImageClassNames, processedAttr } from '../common/ci.constants';
+import {
+  bgContentAttr, loadedImageClassNames, processedAttr, previewContainer,
+} from '../common/ci.constants';
 import closeIconSvg from '../public/close-icon.svg';
 import rightArrowSvg from '../public/right-arrow-icon.svg';
 import leftArrowSvg from '../public/left-arrow-icon.svg';
@@ -183,15 +185,19 @@ export default class CIResponsive {
       }
     }
 
-    this.processGalleryPreviewImage(galleryImages[nextIndex]);
+    this.processGalleryPreviewImage(galleryImages[nextIndex], direction);
     setGalleryIndex(nextIndex);
   }
 
-  processGalleryPreviewImage(imgNode) {
+  processGalleryPreviewImage(imgNode, direction) {
     const _imgNode = imgNode.cloneNode();
     const adaptedImageNode = removeClassNames(_imgNode, loadedImageClassNames);
     const previewModule = getGalleryPreviewModule();
 
+    if (direction) {
+      adaptedImageNode.setAttribute('ci-direction', direction);
+    }
+
     adaptedImageNode.style = {};
     adaptedImageNode.setAttribute('data-ci-processed-gallery', true);
     previewModule.innerHTML = '';
@@ -242,6 +248,8 @@ export default class CIResponsive {
       galleryModal.append(...galleryArrows);
       document.body.appendChild(galleryModal);
 
+      galleryModal.style.animation = 'fadeIn 0.7s';
+
       this.processGalleryPreviewImage(galleryImages[clickedImageIndex]);
       setGalleryIndex(clickedImageIndex);
     }
@@ -336,9 +344,18 @@ export default class CIResponsive {
         wrapper.style.cursor = 'pointer';
       }
 
+      if (gallery && wrapper.parentNode.className === previewContainer) {
+        if (wrapper.firstChild.getAttribute('ci-direction') === 'right') {
+          wrapper.style.animation = 'rightPop 0.5s';
+        }
+        if (wrapper.firstChild.getAttribute('ci-direction') === 'left') {
+          wrapper.style.animation = 'leftPop 0.5s';
+        }
+      }
+
       wrapper.onclick = this.handleClickWrapper.bind(this, imgProps, images);
-      wrapper.onmouseenter = () => displayZoomIcon(wrapper, imgProps, zoomIconSvg);
-      wrapper.onmouseout = () => destroyZoomIcon(wrapper);
+      wrapper.onmouseenter = () => handleHoveringWrapper(wrapper, imgProps, imgNode, zoomIconSvg);
+      wrapper.onmouseout = () => handleUnHoveringWrapper(wrapper, imgProps, imgNode);
 
       onImageLoad(wrapper, previewImgNode, imgNode, ratio, preserveSize, isAdaptive);
     };
diff --git a/src/low-preview/ci.styles.css b/src/low-preview/ci.styles.css
index 295dc9d..305d412 100644
--- a/src/low-preview/ci.styles.css
+++ b/src/low-preview/ci.styles.css
@@ -146,6 +146,7 @@ img.ci-image-loaded {
 .ci-gallery-zoom-button {
   width: 100%;
   height: 100%;
+  cursor: pointer;
   position: absolute;
   top: 0;
   left: 0;
@@ -154,3 +155,31 @@ img.ci-image-loaded {
   align-items: center;
   background-color: rgba(255, 255, 255, 0.5);
 }
+
+@keyframes fadeIn {
+  0% { opacity: 0; }
+  100% { opacity: 1; }
+}
+
+@keyframes fadeOut {
+  0% { opacity: 1; }
+  100% { opacity: 0; }
+}
+
+@keyframes rightPop {
+  0% {
+    transform: translateX(100%);
+  }
+  100% {
+    transform: translateX(0%);
+  }
+}
+
+@keyframes leftPop {
+  0% {
+    transform: translateX(-100%);
+  }
+  100% {
+    transform: translateX(0%);
+  }
+}

From 3419482bd1312c81a2c639345026f9dcfe809df9 Mon Sep 17 00:00:00 2001
From: Nermeen Moustafa 
Date: Fri, 2 Sep 2022 15:32:31 +0200
Subject: [PATCH 13/45] feat: possibility to view image in full screen - T7582

---
 src/common/ci.utils.js           | 270 +------------------------------
 src/low-preview/ci.service.js    |  21 +--
 src/low-preview/gallery.utils.js | 269 ++++++++++++++++++++++++++++++
 3 files changed, 281 insertions(+), 279 deletions(-)
 create mode 100644 src/low-preview/gallery.utils.js

diff --git a/src/common/ci.utils.js b/src/common/ci.utils.js
index 09f379e..af7b979 100644
--- a/src/common/ci.utils.js
+++ b/src/common/ci.utils.js
@@ -1,6 +1,5 @@
 /* eslint-disable no-empty */
 import { getParamsFromURL } from 'cloudimage-responsive-utils/dist/utils/get-params-from-url';
-import { previewContainer } from './ci.constants';
 
 
 const attr = (element, attribute) => element.getAttribute(attribute);
@@ -208,16 +207,6 @@ const setAlt = (imgNode, alt) => {
   imgNode.setAttribute('alt', alt);
 };
 
-const removeClassNames = (node, classNames) => {
-  classNames.forEach((className) => {
-    if (node.classList.contains(className)) {
-      node.classList.remove(className);
-    }
-  });
-
-  return node;
-};
-
 const setOptions = (node, options) => {
   Object.entries(options).forEach(([key, value]) => {
     node.setAttribute(key, value);
@@ -226,253 +215,12 @@ const setOptions = (node, options) => {
   return node;
 };
 
-const getZoomImage = (images, imgSrc) => images.filter((image) => imgSrc === image.getAttribute('ci-src'));
-
-const getGalleryImages = (images, galleryName) => images && [...images].filter((image) => {
-  const { gallery } = getCommonImageProps(image);
-
-  return gallery === galleryName;
-});
-
-const createIcon = (iconSrc, className, iconStyles) => {
-  const { color, width = 15, height = 15 } = iconStyles;
-
-  const iconWrapper = document.createElement('div');
-  const icon = new Image();
-
-  icon.src = iconSrc;
-  icon.width = width;
-  icon.height = height;
-
-  if (className) {
-    iconWrapper.classList.add(className);
-  }
-
-  if (color) {
-    iconWrapper.style.backgroundColor = color;
-  }
-
-  iconWrapper.appendChild(icon);
-
-  return iconWrapper;
-};
-
-const destroyGallery = (galleryModal) => {
-  galleryModal.style.animation = 'fadeOut 0.7s';
-
-  setTimeout(() => {
-    galleryModal.parentNode.removeChild(galleryModal);
-  }, 700);
-};
-
-const galleryPreviewImage = (imgSelector, imgNodeSRC) => {
-  const image = new Image();
-
-  image.setAttribute(imgSelector, imgNodeSRC);
-
-  return image;
-};
-
-const adaptGalleryThumbnails = (thumbnails = [], onClick) => thumbnails.map((thumbnail, index) => {
-  const thmbnailContainer = document.createElement('div');
-  const image = thumbnail.cloneNode();
-
-  image.classList.remove('ci-image');
-  image.style = {};
-  image.style.width = '100%';
-  image.style.height = '100%';
-
-  thmbnailContainer.classList.add('ci-gallery-thmbnail-container');
-  thmbnailContainer.setAttribute('data-ci-gallery-index', index);
-  thmbnailContainer.append(image);
-
-  if (onClick) {
-    thmbnailContainer.onclick = onClick;
-  }
-
-  return thmbnailContainer;
-});
-
-const appendGalleryThumbnails = (thumbnails = [], thumbnailsContainer) => {
-  thumbnails.forEach((thumbnail) => {
-    thumbnailsContainer.append(thumbnail);
-  });
-};
-
-const createThmbnailsModule = (images, galleryModal, onClick) => {
-  const thumbnailsModule = galleryModal.querySelector('.ci-gallery-thumbnail-module');
-  const adaptedGalleryThumbnails = adaptGalleryThumbnails(images, onClick);
-
-  appendGalleryThumbnails(adaptedGalleryThumbnails, thumbnailsModule);
-
-  return thumbnailsModule;
-};
-
-const createGalleryArrows = (leftArrowIcon, rightArrowIcon, onClick) => {
-  const iconStyles = { color: 'rgba(255,255,255,0.5)' };
-
-  const leftArrow = createIcon(leftArrowIcon, 'ci-gallery-left-arrow-button', iconStyles);
-  const rightArrow = createIcon(rightArrowIcon, 'ci-gallery-right-arrow-button', iconStyles);
-
-  if (onClick) {
-    leftArrow.onclick = onClick.bind(this, 'left');
-    rightArrow.onclick = onClick.bind(this, 'right');
-  }
-
-  return [leftArrow, rightArrow];
-};
-
-const getGalleryLengthAndIndex = () => {
-  const galleryModal = document.body.querySelector('[data-ci-gallery]');
-  const galleryLength = galleryModal.getAttribute('data-ci-gallery-length');
-  const galleryIndex = galleryModal.getAttribute('data-ci-gallery-index');
-
-  return [galleryLength, galleryIndex];
-};
-
-const getGalleryPreviewModule = () => {
-  const galleryModal = document.body.querySelector('[data-ci-gallery]');
-
-  return galleryModal.querySelector('.ci-gallery-preview-module');
-};
-
-const setGalleryIndex = (index) => {
-  const galleryModal = document.body.querySelector('[data-ci-gallery]');
-
-  galleryModal.setAttribute('data-ci-gallery-index', index);
-};
-
-const createGalleryModal = (galleryLength, closeIconSrc, isGallery) => {
-  const iconStyles = { color: 'rgba(255,255,255,0.5)' };
-
-  const galleryModal = document.createElement('div');
-  const previewModule = document.createElement('div');
-  const closeIcon = createIcon(closeIconSrc, 'ci-gallery-close-button', iconStyles);
-
-  galleryModal.classList.add('ci-gallery-modal');
-  previewModule.classList.add('ci-gallery-preview-module');
-
-  if (isGallery) {
-    const thumbnailsModule = document.createElement('div');
-    thumbnailsModule.classList.add('ci-gallery-thumbnail-module');
-    galleryModal.setAttribute('data-ci-gallery-length', galleryLength);
-    galleryModal.setAttribute('data-ci-gallery-index', 0);
-    galleryModal.append(thumbnailsModule);
-  }
-
-  galleryModal.setAttribute('data-ci-gallery', true);
-  galleryModal.append(previewModule);
-  galleryModal.append(closeIcon);
-
-  closeIcon.onclick = destroyGallery.bind(this, galleryModal);
-
-  return galleryModal;
-};
-
-const markCurrentImage = (galleryThmbnails, currentIndex) => {
-  galleryThmbnails.forEach((imgWrapper, index) => {
-    imgWrapper.querySelector('img').style.border = '1px solid grey';
-
-    if (index === currentIndex) {
-      imgWrapper.querySelector('img').style.border = '1px solid white';
-    }
-  });
-};
-
-const getCurrentImage = (mainImageWrapper, galleryModal) => {
-  const galleryThmbnailsModule = galleryModal.querySelector('.ci-gallery-thumbnail-module');
-  const galleryThmbnails = [...galleryThmbnailsModule.children];
-
-  let currentIndex = null;
-
-  galleryThmbnails.forEach((imgWrapper, index) => {
-    const mainImg = mainImageWrapper.querySelector('[ci-src]').getAttribute('ci-src');
-    const galleryImg = imgWrapper.querySelector('[ci-src]').getAttribute('ci-src');
-
-    if (mainImg === galleryImg) {
-      currentIndex = index;
-      markCurrentImage(galleryThmbnails, index);
-    }
-  });
-
-  return currentIndex;
-};
-
-const handleHoveringWrapper = (wrapper, imgProps, imgNode, zoomIconSrc) => {
-  const isPreviewWrapper = wrapper.parentNode.className === 'ci-gallery-preview-module';
-
-  if (!isPreviewWrapper) {
-    const { zoom, gallery } = imgProps;
-
-    if (zoom && !gallery) {
-      const iconStyles = { width: 35, height: 35 };
-      const zoomIcon = createIcon(zoomIconSrc, 'ci-gallery-zoom-button', iconStyles);
-
-      zoomIcon.style.animation = 'fadeIn 0.3s';
-
-      wrapper.append(zoomIcon);
-    }
-
-    if (gallery) {
-      wrapper.style.transform = 'scale(1.05)';
-      wrapper.style.transition = 'transform 0.5s ease';
-      wrapper.style.zIndex = '2';
-    }
-  }
-};
-
-const handleUnHoveringWrapper = (wrapper, imgProps, imgNode) => {
-  const isPreviewWrapper = wrapper.parentNode.className === previewContainer;
-
-  if (!isPreviewWrapper) {
-    const { zoom, gallery } = imgProps;
-
-    if (zoom && !gallery) {
-      const zoomIcon = wrapper.querySelector('.ci-gallery-zoom-button');
-      zoomIcon.style.animation = 'fadeOut 0.4s';
-
-      setTimeout(() => {
-        if (zoomIcon) {
-          zoomIcon.remove();
-        }
-      }, 300);
-    }
-
-    if (gallery) {
-      wrapper.style.transform = 'scale(1)';
-      wrapper.style.zIndex = '1';
-    }
-  }
-};
-
-const getImageFitStyles = (naturalWidth, naturalHeight) => {
-  let shouldFitHorizontally;
-  const imageStyles = {};
-  const previewModule = document.body.querySelector('.ci-gallery-preview-module');
-
-  if (naturalWidth && previewModule) {
-    const imageAspectRatio = naturalWidth / naturalHeight;
-    const renderWidth = previewModule.offsetHeight * imageAspectRatio;
-    shouldFitHorizontally = (imageAspectRatio < 1)
-      || (renderWidth <= previewModule.offsetWidth);
-  }
-
-  if (shouldFitHorizontally) {
-    imageStyles.width = 'auto';
-    imageStyles.height = '100%';
-  } else {
-    imageStyles.width = '100%';
-    imageStyles.height = 'auto';
-  }
-
-  return imageStyles;
-};
-
 export {
   getParams,
   filterImages,
   getImageProps,
   getBackgroundImageProps,
+  getCommonImageProps,
   addClass,
   getWrapper,
   isLazy,
@@ -482,21 +230,5 @@ export {
   getFreshCIElements,
   destroyNodeImgSize,
   setAlt,
-  removeClassNames,
   setOptions,
-  createIcon,
-  galleryPreviewImage,
-  createThmbnailsModule,
-  createGalleryModal,
-  markCurrentImage,
-  getCurrentImage,
-  handleHoveringWrapper,
-  handleUnHoveringWrapper,
-  getGalleryImages,
-  createGalleryArrows,
-  getGalleryLengthAndIndex,
-  setGalleryIndex,
-  getGalleryPreviewModule,
-  getImageFitStyles,
-  getZoomImage,
 };
diff --git a/src/low-preview/ci.service.js b/src/low-preview/ci.service.js
index f07b1dd..9dd76f2 100644
--- a/src/low-preview/ci.service.js
+++ b/src/low-preview/ci.service.js
@@ -13,24 +13,25 @@ import {
   getFreshCIElements,
   getImageProps,
   isLazy,
-  removeClassNames,
   setAlt,
   setBackgroundSrc,
   setOptions,
   setSrc,
   setSrcset,
+} from '../common/ci.utils';
+import {
   createGalleryModal,
-  createThmbnailsModule,
   handleHoveringWrapper,
   handleUnHoveringWrapper,
-  getGalleryImages,
+  getGalleryPreviewModule,
+  setGalleryIndex,
   createGalleryArrows,
   getGalleryLengthAndIndex,
-  setGalleryIndex,
-  getGalleryPreviewModule,
+  removeClassNames,
+  createThmbnailsModule,
+  getGalleryImages,
   getImageFitStyles,
-  getZoomImage,
-} from '../common/ci.utils';
+} from './gallery.utils';
 import { getInitialConfigLowPreview } from './ci.config';
 import {
   applyBackgroundStyles,
@@ -219,7 +220,7 @@ export default class CIResponsive {
 
   handleClickWrapper(imgProps, images, event) {
     const {
-      gallery, zoom, isProcessedByGallery, imgNodeSRC,
+      gallery, zoom, isProcessedByGallery,
     } = imgProps;
 
     if (isProcessedByGallery) return;
@@ -354,8 +355,8 @@ export default class CIResponsive {
       }
 
       wrapper.onclick = this.handleClickWrapper.bind(this, imgProps, images);
-      wrapper.onmouseenter = () => handleHoveringWrapper(wrapper, imgProps, imgNode, zoomIconSvg);
-      wrapper.onmouseout = () => handleUnHoveringWrapper(wrapper, imgProps, imgNode);
+      wrapper.onmouseenter = handleHoveringWrapper.bind(this, wrapper, imgProps, zoomIconSvg);
+      wrapper.onmouseout = handleUnHoveringWrapper.bind(this, wrapper, imgProps);
 
       onImageLoad(wrapper, previewImgNode, imgNode, ratio, preserveSize, isAdaptive);
     };
diff --git a/src/low-preview/gallery.utils.js b/src/low-preview/gallery.utils.js
new file mode 100644
index 0000000..ea16dfd
--- /dev/null
+++ b/src/low-preview/gallery.utils.js
@@ -0,0 +1,269 @@
+import { getCommonImageProps } from '../common/ci.utils';
+import { previewContainer } from '../common/ci.constants';
+
+
+const createIcon = (iconSrc, className, iconStyles) => {
+  const { color, width = 15, height = 15 } = iconStyles;
+
+  const iconWrapper = document.createElement('div');
+  const icon = new Image();
+
+  icon.src = iconSrc;
+  icon.width = width;
+  icon.height = height;
+
+  if (className) {
+    iconWrapper.classList.add(className);
+  }
+
+  if (color) {
+    iconWrapper.style.backgroundColor = color;
+  }
+
+  iconWrapper.appendChild(icon);
+
+  return iconWrapper;
+};
+
+const destroyGallery = (galleryModal) => {
+  galleryModal.style.animation = 'fadeOut 0.7s';
+
+  setTimeout(() => {
+    galleryModal.parentNode.removeChild(galleryModal);
+  }, 700);
+};
+
+const createGalleryModal = (galleryLength, closeIconSrc, isGallery) => {
+  const iconStyles = { color: 'rgba(255,255,255,0.5)' };
+
+  const galleryModal = document.createElement('div');
+  const previewModule = document.createElement('div');
+  const closeIcon = createIcon(closeIconSrc, 'ci-gallery-close-button', iconStyles);
+
+  galleryModal.classList.add('ci-gallery-modal');
+  previewModule.classList.add('ci-gallery-preview-module');
+
+  if (isGallery) {
+    const thumbnailsModule = document.createElement('div');
+    thumbnailsModule.classList.add('ci-gallery-thumbnail-module');
+    galleryModal.setAttribute('data-ci-gallery-length', galleryLength);
+    galleryModal.setAttribute('data-ci-gallery-index', 0);
+    galleryModal.append(thumbnailsModule);
+  }
+
+  galleryModal.setAttribute('data-ci-gallery', true);
+  galleryModal.append(previewModule);
+  galleryModal.append(closeIcon);
+
+  closeIcon.onclick = destroyGallery.bind(this, galleryModal);
+
+  return galleryModal;
+};
+
+const handleHoveringWrapper = (wrapper, imgProps, zoomIconSrc) => {
+  const isPreviewWrapper = wrapper.parentNode.className === previewContainer;
+
+  if (!isPreviewWrapper) {
+    const { zoom, gallery } = imgProps;
+
+    if (zoom && !gallery) {
+      const iconStyles = { width: 35, height: 35 };
+      const zoomIcon = createIcon(zoomIconSrc, 'ci-gallery-zoom-button', iconStyles);
+
+      zoomIcon.style.animation = 'fadeIn 0.3s';
+
+      wrapper.append(zoomIcon);
+    }
+
+    if (gallery) {
+      wrapper.style.transform = 'scale(1.05)';
+      wrapper.style.transition = 'transform 0.5s ease';
+      wrapper.style.zIndex = '2';
+    }
+  }
+};
+
+const handleUnHoveringWrapper = (wrapper, imgProps) => {
+  const isPreviewWrapper = wrapper.parentNode.className === previewContainer;
+
+  if (!isPreviewWrapper) {
+    const { zoom, gallery } = imgProps;
+
+    if (zoom && !gallery) {
+      const zoomIcon = wrapper.querySelector('.ci-gallery-zoom-button');
+      zoomIcon.style.animation = 'fadeOut 0.4s';
+
+      setTimeout(() => {
+        if (zoomIcon) {
+          zoomIcon.parentNode.removeChild(zoomIcon);
+        }
+      }, 300);
+    }
+
+    if (gallery) {
+      wrapper.style.transform = 'scale(1)';
+      wrapper.style.zIndex = '1';
+    }
+  }
+};
+
+const getGalleryPreviewModule = () => {
+  const galleryModal = document.body.querySelector('[data-ci-gallery]');
+
+  return galleryModal.querySelector('.ci-gallery-preview-module');
+};
+
+const setGalleryIndex = (index) => {
+  const galleryModal = document.body.querySelector('[data-ci-gallery]');
+
+  galleryModal.setAttribute('data-ci-gallery-index', index);
+};
+
+const markCurrentImage = (galleryThmbnails, currentIndex) => {
+  galleryThmbnails.forEach((imgWrapper, index) => {
+    imgWrapper.querySelector('img').style.border = '1px solid grey';
+
+    if (index === currentIndex) {
+      imgWrapper.querySelector('img').style.border = '1px solid white';
+    }
+  });
+};
+
+const createGalleryArrows = (leftArrowIcon, rightArrowIcon, onClick) => {
+  const iconStyles = { color: 'rgba(255,255,255,0.5)' };
+
+  const leftArrow = createIcon(leftArrowIcon, 'ci-gallery-left-arrow-button', iconStyles);
+  const rightArrow = createIcon(rightArrowIcon, 'ci-gallery-right-arrow-button', iconStyles);
+
+  if (onClick) {
+    leftArrow.onclick = onClick.bind(this, 'left');
+    rightArrow.onclick = onClick.bind(this, 'right');
+  }
+
+  return [leftArrow, rightArrow];
+};
+
+const getGalleryLengthAndIndex = () => {
+  const galleryModal = document.body.querySelector('[data-ci-gallery]');
+  const galleryLength = galleryModal.getAttribute('data-ci-gallery-length');
+  const galleryIndex = galleryModal.getAttribute('data-ci-gallery-index');
+
+  return [galleryLength, galleryIndex];
+};
+
+const removeClassNames = (node, classNames) => {
+  classNames.forEach((className) => {
+    if (node.classList.contains(className)) {
+      node.classList.remove(className);
+    }
+  });
+
+  return node;
+};
+
+const adaptGalleryThumbnails = (thumbnails = [], onClick) => thumbnails.map((thumbnail, index) => {
+  const thmbnailContainer = document.createElement('div');
+  const image = thumbnail.cloneNode();
+
+  image.classList.remove('ci-image');
+  image.style = {};
+  image.style.width = '100%';
+  image.style.height = '100%';
+
+  thmbnailContainer.classList.add('ci-gallery-thmbnail-container');
+  thmbnailContainer.setAttribute('data-ci-gallery-index', index);
+  thmbnailContainer.append(image);
+
+  if (onClick) {
+    thmbnailContainer.onclick = onClick;
+  }
+
+  return thmbnailContainer;
+});
+
+const appendGalleryThumbnails = (thumbnails = [], thumbnailsContainer) => {
+  thumbnails.forEach((thumbnail) => {
+    thumbnailsContainer.append(thumbnail);
+  });
+};
+
+const createThmbnailsModule = (images, galleryModal, onClick) => {
+  const thumbnailsModule = galleryModal.querySelector('.ci-gallery-thumbnail-module');
+  const adaptedGalleryThumbnails = adaptGalleryThumbnails(images, onClick);
+
+  appendGalleryThumbnails(adaptedGalleryThumbnails, thumbnailsModule);
+
+  return thumbnailsModule;
+};
+
+const getGalleryImages = (images, galleryName) => images && images.filter((image) => {
+  const { gallery } = getCommonImageProps(image);
+
+  return gallery === galleryName;
+});
+
+const getImageFitStyles = (naturalWidth, naturalHeight) => {
+  let shouldFitHorizontally;
+  const imageStyles = {};
+  const previewModule = document.body.querySelector('.ci-gallery-preview-module');
+
+  if (naturalWidth && previewModule) {
+    const imageAspectRatio = naturalWidth / naturalHeight;
+    const renderWidth = previewModule.offsetHeight * imageAspectRatio;
+    shouldFitHorizontally = (imageAspectRatio < 1)
+        || (renderWidth <= previewModule.offsetWidth);
+  }
+
+  if (shouldFitHorizontally) {
+    imageStyles.width = 'auto';
+    imageStyles.height = '100%';
+  } else {
+    imageStyles.width = '100%';
+    imageStyles.height = 'auto';
+  }
+
+  return imageStyles;
+};
+
+const getCurrentImage = (mainImageWrapper, galleryModal) => {
+  const galleryThmbnailsModule = galleryModal.querySelector('.ci-gallery-thumbnail-module');
+  const galleryThmbnails = [...galleryThmbnailsModule.children];
+
+  let currentIndex = null;
+
+  galleryThmbnails.forEach((imgWrapper, index) => {
+    const mainImg = mainImageWrapper.querySelector('[ci-src]').getAttribute('ci-src');
+    const galleryImg = imgWrapper.querySelector('[ci-src]').getAttribute('ci-src');
+
+    if (mainImg === galleryImg) {
+      currentIndex = index;
+      markCurrentImage(galleryThmbnails, index);
+    }
+  });
+
+  return currentIndex;
+};
+
+const galleryPreviewImage = (imgSelector, imgNodeSRC) => {
+  const image = new Image();
+
+  image.setAttribute(imgSelector, imgNodeSRC);
+
+  return image;
+};
+
+export {
+  createGalleryModal,
+  handleHoveringWrapper,
+  handleUnHoveringWrapper,
+  getGalleryPreviewModule,
+  setGalleryIndex,
+  createGalleryArrows,
+  getGalleryLengthAndIndex,
+  removeClassNames,
+  createThmbnailsModule,
+  getGalleryImages,
+  getImageFitStyles,
+  getCurrentImage,
+  galleryPreviewImage,
+};

From bc6beb858d0ec001e312ab9f8a7355b5f86ec4b1 Mon Sep 17 00:00:00 2001
From: Amr Wagdy 
Date: Sat, 3 Sep 2022 20:21:54 +0200
Subject: [PATCH 14/45] Feat:Fit gallery images - T7582

---
 examples/low-preview/src/index.html |   1 +
 src/common/ci.utils.js              |   8 +
 src/low-preview/ci.service.js       |  61 ++++----
 src/low-preview/gallery.utils.js    |  62 +++++---
 yarn.lock                           | 222 ++++++++++++++--------------
 5 files changed, 189 insertions(+), 165 deletions(-)

diff --git a/examples/low-preview/src/index.html b/examples/low-preview/src/index.html
index f2c89bf..8b6f76f 100644
--- a/examples/low-preview/src/index.html
+++ b/examples/low-preview/src/index.html
@@ -255,6 +255,7 @@ 

Responsive images, in real-time!

alt="img" ci-ratio="2.885" ci-params="ci_info=2" + ci-gallery="gallery2" />

diff --git a/src/common/ci.utils.js b/src/common/ci.utils.js index af7b979..f5d579b 100644 --- a/src/common/ci.utils.js +++ b/src/common/ci.utils.js @@ -215,6 +215,13 @@ const setOptions = (node, options) => { return node; }; +const swapArrayPositions = (array = [], a, b) => { + const clonedArray = [...array]; + [clonedArray[a], clonedArray[b]] = [clonedArray[b], clonedArray[a]]; + + return clonedArray; +}; + export { getParams, filterImages, @@ -231,4 +238,5 @@ export { destroyNodeImgSize, setAlt, setOptions, + swapArrayPositions, }; diff --git a/src/low-preview/ci.service.js b/src/low-preview/ci.service.js index 9dd76f2..8b0fed9 100644 --- a/src/low-preview/ci.service.js +++ b/src/low-preview/ci.service.js @@ -30,7 +30,7 @@ import { removeClassNames, createThmbnailsModule, getGalleryImages, - getImageFitStyles, + getDimAndFit, } from './gallery.utils'; import { getInitialConfigLowPreview } from './ci.config'; import { @@ -46,7 +46,7 @@ import { updateSizeWithPixelRatio, } from './ci.utis'; import { - bgContentAttr, loadedImageClassNames, processedAttr, previewContainer, + bgContentAttr, loadedImageClassNames, processedAttr, } from '../common/ci.constants'; import closeIconSvg from '../public/close-icon.svg'; import rightArrowSvg from '../public/right-arrow-icon.svg'; @@ -96,7 +96,14 @@ export default class CIResponsive { } } - getBasicInfo = (imgNode, isUpdate, windowScreenBecomesBigger, type, images = [], isGalleryImg) => { + getBasicInfo = ( + imgNode, + isUpdate, + windowScreenBecomesBigger, + type, + images, + isGalleryImg, + ) => { const isImage = type === 'image'; const { config } = this; const { @@ -137,7 +144,11 @@ export default class CIResponsive { [src, isSVG] = getImgSRC(size.params.src, baseURL); } } - } else if (isUpdate && !windowScreenBecomesBigger) return; + } else if (isUpdate && !windowScreenBecomesBigger) { + getDimAndFit(imgNode); + + return; + } const containerProps = determineContainerProps({ ...imgProps, size, imgNode, config, @@ -233,6 +244,7 @@ export default class CIResponsive { const galleryModal = createGalleryModal(galleryImages.length, closeIconSvg, true); const previewModule = galleryModal.querySelector('.ci-gallery-preview-module'); const thumbnailsModule = createThmbnailsModule( + clickedImage, galleryImages, galleryModal, this.handleClickThumbnail.bind(this, galleryImages), @@ -249,22 +261,20 @@ export default class CIResponsive { galleryModal.append(...galleryArrows); document.body.appendChild(galleryModal); - galleryModal.style.animation = 'fadeIn 0.7s'; - this.processGalleryPreviewImage(galleryImages[clickedImageIndex]); setGalleryIndex(clickedImageIndex); } - if (zoom && !gallery) { - const galleryModal = createGalleryModal(); - const previewModule = galleryModal.querySelector('.ci-gallery-preview-module'); + // if (zoom && !gallery) { + // const galleryModal = createGalleryModal(); + // const previewModule = galleryModal.querySelector('.ci-gallery-preview-module'); - galleryModal.appendChild(previewModule); + // galleryModal.appendChild(previewModule); - document.body.appendChild(galleryModal); + // document.body.appendChild(galleryModal); - this.process(false, previewModule); - } + // this.process(false, previewModule); + // } } processImage(props) { @@ -325,16 +335,9 @@ export default class CIResponsive { } } - const dimInterval = setInterval(() => { - if (imgNode.naturalWidth) { - clearInterval(dimInterval); - const imageFitStyles = getImageFitStyles(imgNode.naturalWidth, imgNode.naturalHeight); + getDimAndFit(imgNode); - imgNode.style.width = imageFitStyles.width; - imgNode.style.height = imageFitStyles.height; - imgNode.style.opacity = 1; - } - }, 10); + wrapper.onclick = this.handleClickWrapper.bind(this, imgProps, images); imgNode.onload = () => { if (config.onImageLoad && typeof config.onImageLoad === 'function') { @@ -345,20 +348,12 @@ export default class CIResponsive { wrapper.style.cursor = 'pointer'; } - if (gallery && wrapper.parentNode.className === previewContainer) { - if (wrapper.firstChild.getAttribute('ci-direction') === 'right') { - wrapper.style.animation = 'rightPop 0.5s'; - } - if (wrapper.firstChild.getAttribute('ci-direction') === 'left') { - wrapper.style.animation = 'leftPop 0.5s'; - } - } - - wrapper.onclick = this.handleClickWrapper.bind(this, imgProps, images); wrapper.onmouseenter = handleHoveringWrapper.bind(this, wrapper, imgProps, zoomIconSvg); wrapper.onmouseout = handleUnHoveringWrapper.bind(this, wrapper, imgProps); - onImageLoad(wrapper, previewImgNode, imgNode, ratio, preserveSize, isAdaptive); + if (!isProcessedByGallery) { + onImageLoad(wrapper, previewImgNode, imgNode, ratio, preserveSize, isAdaptive); + } }; setSrcset(imgNode, cloudimageSrcset, 'data-srcset', lazy, src, isSVG, dataSrcAttr); diff --git a/src/low-preview/gallery.utils.js b/src/low-preview/gallery.utils.js index ea16dfd..686fac4 100644 --- a/src/low-preview/gallery.utils.js +++ b/src/low-preview/gallery.utils.js @@ -1,4 +1,4 @@ -import { getCommonImageProps } from '../common/ci.utils'; +import { getCommonImageProps, swapArrayPositions } from '../common/ci.utils'; import { previewContainer } from '../common/ci.constants'; @@ -161,25 +161,31 @@ const removeClassNames = (node, classNames) => { return node; }; -const adaptGalleryThumbnails = (thumbnails = [], onClick) => thumbnails.map((thumbnail, index) => { - const thmbnailContainer = document.createElement('div'); - const image = thumbnail.cloneNode(); +const adaptGalleryThumbnails = (clickedImage, thumbnails = [], onClick) => { + const indexOfClickedImage = thumbnails.indexOf(clickedImage); - image.classList.remove('ci-image'); - image.style = {}; - image.style.width = '100%'; - image.style.height = '100%'; + const _thumbnails = swapArrayPositions(thumbnails, indexOfClickedImage, 0); - thmbnailContainer.classList.add('ci-gallery-thmbnail-container'); - thmbnailContainer.setAttribute('data-ci-gallery-index', index); - thmbnailContainer.append(image); + return _thumbnails.map((thumbnail, index) => { + const thmbnailContainer = document.createElement('div'); + const image = thumbnail.cloneNode(); - if (onClick) { - thmbnailContainer.onclick = onClick; - } + image.classList.remove('ci-image'); + image.style = {}; + image.style.width = '100%'; + image.style.height = '100%'; - return thmbnailContainer; -}); + thmbnailContainer.classList.add('ci-gallery-thmbnail-container'); + thmbnailContainer.setAttribute('data-ci-gallery-index', index); + thmbnailContainer.append(image); + + if (onClick) { + thmbnailContainer.onclick = onClick; + } + + return thmbnailContainer; + }); +}; const appendGalleryThumbnails = (thumbnails = [], thumbnailsContainer) => { thumbnails.forEach((thumbnail) => { @@ -187,16 +193,16 @@ const appendGalleryThumbnails = (thumbnails = [], thumbnailsContainer) => { }); }; -const createThmbnailsModule = (images, galleryModal, onClick) => { +const createThmbnailsModule = (clickedImage, images, galleryModal, onClick) => { const thumbnailsModule = galleryModal.querySelector('.ci-gallery-thumbnail-module'); - const adaptedGalleryThumbnails = adaptGalleryThumbnails(images, onClick); + const adaptedGalleryThumbnails = adaptGalleryThumbnails(clickedImage, images, onClick); appendGalleryThumbnails(adaptedGalleryThumbnails, thumbnailsModule); return thumbnailsModule; }; -const getGalleryImages = (images, galleryName) => images && images.filter((image) => { +const getGalleryImages = (images, galleryName) => [...images].filter((image) => { const { gallery } = getCommonImageProps(image); return gallery === galleryName; @@ -210,8 +216,8 @@ const getImageFitStyles = (naturalWidth, naturalHeight) => { if (naturalWidth && previewModule) { const imageAspectRatio = naturalWidth / naturalHeight; const renderWidth = previewModule.offsetHeight * imageAspectRatio; - shouldFitHorizontally = (imageAspectRatio < 1) - || (renderWidth <= previewModule.offsetWidth); + shouldFitHorizontally = (imageAspectRatio <= 1) + && (renderWidth < previewModule.offsetWidth); } if (shouldFitHorizontally) { @@ -252,6 +258,19 @@ const galleryPreviewImage = (imgSelector, imgNodeSRC) => { return image; }; +const getDimAndFit = (imgNode) => { + const dimInterval = setInterval(() => { + if (imgNode.naturalWidth) { + clearInterval(dimInterval); + const imageFitStyles = getImageFitStyles(imgNode.naturalWidth, imgNode.naturalHeight); + + imgNode.style.width = imageFitStyles.width; + imgNode.style.height = imageFitStyles.height; + imgNode.style.opacity = 1; + } + }, 10); +}; + export { createGalleryModal, handleHoveringWrapper, @@ -266,4 +285,5 @@ export { getImageFitStyles, getCurrentImage, galleryPreviewImage, + getDimAndFit, }; diff --git a/yarn.lock b/yarn.lock index e2d7468..c66e653 100644 --- a/yarn.lock +++ b/yarn.lock @@ -34,37 +34,37 @@ "@babel/highlight" "^7.18.6" "@babel/compat-data@^7.17.7", "@babel/compat-data@^7.18.8": - version "7.18.8" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.18.8.tgz#2483f565faca607b8535590e84e7de323f27764d" - integrity sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ== + version "7.18.13" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.18.13.tgz#6aff7b350a1e8c3e40b029e46cbe78e24a913483" + integrity sha512-5yUzC5LqyTFp2HLmDoxGQelcdYgSpP9xsnMWBphAscOdFrHSAVbLNzWiy32sVNDqJRDiJK6klfDnAgu6PAGSHw== "@babel/core@^7.0.0": - version "7.18.10" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.18.10.tgz#39ad504991d77f1f3da91be0b8b949a5bc466fb8" - integrity sha512-JQM6k6ENcBFKVtWvLavlvi/mPcpYZ3+R+2EySDEMSMbp7Mn4FexlbbJVrx2R7Ijhr01T8gyqrOaABWIOgxeUyw== + version "7.18.13" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.18.13.tgz#9be8c44512751b05094a4d3ab05fc53a47ce00ac" + integrity sha512-ZisbOvRRusFktksHSG6pjj1CSvkPkcZq/KHD45LAkVP/oiHJkNBZWfpvlLmX8OtHDG8IuzsFlVRWo08w7Qxn0A== dependencies: "@ampproject/remapping" "^2.1.0" "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.18.10" + "@babel/generator" "^7.18.13" "@babel/helper-compilation-targets" "^7.18.9" "@babel/helper-module-transforms" "^7.18.9" "@babel/helpers" "^7.18.9" - "@babel/parser" "^7.18.10" + "@babel/parser" "^7.18.13" "@babel/template" "^7.18.10" - "@babel/traverse" "^7.18.10" - "@babel/types" "^7.18.10" + "@babel/traverse" "^7.18.13" + "@babel/types" "^7.18.13" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" json5 "^2.2.1" semver "^6.3.0" -"@babel/generator@^7.18.10": - version "7.18.12" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.18.12.tgz#fa58daa303757bd6f5e4bbca91b342040463d9f4" - integrity sha512-dfQ8ebCN98SvyL7IxNMCUtZQSq5R7kxgN+r8qYTGDmmSion1hX2C0zq2yo1bsCDhXixokv1SAWTZUMYbO/V5zg== +"@babel/generator@^7.18.13": + version "7.18.13" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.18.13.tgz#59550cbb9ae79b8def15587bdfbaa388c4abf212" + integrity sha512-CkPg8ySSPuHTYPJYo7IRALdqyjM9HCbt/3uOBEFbzyGVP6Mn8bwFPB0jX6982JVNBlYzM1nnPkfjuXSOPtQeEQ== dependencies: - "@babel/types" "^7.18.10" + "@babel/types" "^7.18.13" "@jridgewell/gen-mapping" "^0.3.2" jsesc "^2.5.1" @@ -94,9 +94,9 @@ semver "^6.3.0" "@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.18.9.tgz#d802ee16a64a9e824fcbf0a2ffc92f19d58550ce" - integrity sha512-WvypNAYaVh23QcjpMR24CwZY2Nz6hqdOcFdPbNpV56hL5H6KiFheO7Xm1aPdlLQ7d5emYZX7VZwPp9x3z+2opw== + version "7.18.13" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.18.13.tgz#63e771187bd06d234f95fdf8bd5f8b6429de6298" + integrity sha512-hDvXp+QYxSRL+23mpAlSGxHMDyIGChm0/AwTfTAAK5Ufe40nCsyNdaYCGuK91phn/fVu9kqayImRDkvNAgdrsA== dependencies: "@babel/helper-annotate-as-pure" "^7.18.6" "@babel/helper-environment-visitor" "^7.18.9" @@ -278,10 +278,10 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.18.10", "@babel/parser@^7.18.11": - version "7.18.11" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.11.tgz#68bb07ab3d380affa9a3f96728df07969645d2d9" - integrity sha512-9JKn5vN+hDt0Hdqn1PiJ2guflwP+B6Ga8qbDuoF0PzzVhrzsKIJo8yGqVk6CmMHiMei9w1C1Bp9IMJSIK+HPIQ== +"@babel/parser@^7.18.10", "@babel/parser@^7.18.13": + version "7.18.13" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.13.tgz#5b2dd21cae4a2c5145f1fbd8ca103f9313d3b7e4" + integrity sha512-dgXcIfMuQ0kgzLB2b9tRZs7TTFFaGM2AbtA4fJgUUYukzGH4jwsS7hzQHEGs67jdehpm22vkgKwvbU+aEflgwg== "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6": version "7.18.6" @@ -641,9 +641,9 @@ "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-transform-destructuring@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.18.9.tgz#68906549c021cb231bee1db21d3b5b095f8ee292" - integrity sha512-p5VCYNddPLkZTq4XymQIaIfZNJwT9YsjkPOhkVEqt6QIpQFZVM9IltqqYpOEkJoN1DPznmxUDyZ5CTZs/ZCuHA== + version "7.18.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.18.13.tgz#9e03bc4a94475d62b7f4114938e6c5c33372cbf5" + integrity sha512-TodpQ29XekIsex2A+YJPj5ax2plkGa8YYY6mFjCohk/IG9IY42Rtuj1FuDeemfg2ipxIFLzPeA83SIBnlhSIow== dependencies: "@babel/helper-plugin-utils" "^7.18.9" @@ -949,26 +949,26 @@ "@babel/parser" "^7.18.10" "@babel/types" "^7.18.10" -"@babel/traverse@^7.18.10", "@babel/traverse@^7.18.11", "@babel/traverse@^7.18.9": - version "7.18.11" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.18.11.tgz#3d51f2afbd83ecf9912bcbb5c4d94e3d2ddaa16f" - integrity sha512-TG9PiM2R/cWCAy6BPJKeHzNbu4lPzOSZpeMfeNErskGpTJx6trEvFaVCbDvpcxwy49BKWmEPwiW8mrysNiDvIQ== +"@babel/traverse@^7.18.11", "@babel/traverse@^7.18.13", "@babel/traverse@^7.18.9": + version "7.18.13" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.18.13.tgz#5ab59ef51a997b3f10c4587d648b9696b6cb1a68" + integrity sha512-N6kt9X1jRMLPxxxPYWi7tgvJRH/rtoU+dbKAPDM44RFHiMH8igdsaSBgFeskhSl/kLWLDUvIh1RXCrTmg0/zvA== dependencies: "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.18.10" + "@babel/generator" "^7.18.13" "@babel/helper-environment-visitor" "^7.18.9" "@babel/helper-function-name" "^7.18.9" "@babel/helper-hoist-variables" "^7.18.6" "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/parser" "^7.18.11" - "@babel/types" "^7.18.10" + "@babel/parser" "^7.18.13" + "@babel/types" "^7.18.13" debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.4.4": - version "7.18.10" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.18.10.tgz#4908e81b6b339ca7c6b7a555a5fc29446f26dde6" - integrity sha512-MJvnbEiiNkpjo+LknnmRrqbY1GPUUggjv+wQVjetM/AONoupqRALB7I6jGqNUAZsKcRIEu2J6FRFvsczljjsaQ== +"@babel/types@^7.18.10", "@babel/types@^7.18.13", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.4.4": + version "7.18.13" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.18.13.tgz#30aeb9e514f4100f7c1cb6e5ba472b30e48f519a" + integrity sha512-ePqfTihzW0W6XAU+aMw2ykilisStJfDnsejDCXRchCcMJ4O0+8DhPXf2YUbZ6wjBlsEmZwLK/sPweWtu8hcJYQ== dependencies: "@babel/helper-string-parser" "^7.18.10" "@babel/helper-validator-identifier" "^7.18.6" @@ -979,14 +979,14 @@ resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== -"@eslint/eslintrc@^1.3.0": - version "1.3.0" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.0.tgz#29f92c30bb3e771e4a2048c95fa6855392dfac4f" - integrity sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw== +"@eslint/eslintrc@^1.3.1": + version "1.3.1" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.1.tgz#de0807bfeffc37b964a7d0400e0c348ce5a2543d" + integrity sha512-OhSY22oQQdw3zgPOOwdoj01l/Dzl1Z+xyUP33tkSN+aqyEhymJCcPHyXt+ylW8FSe0TfRC2VG+ROQOapD0aZSQ== dependencies: ajv "^6.12.4" debug "^4.3.2" - espree "^9.3.2" + espree "^9.4.0" globals "^13.15.0" ignore "^5.2.0" import-fresh "^3.2.1" @@ -1013,6 +1013,11 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/gitignore-to-minimatch/-/gitignore-to-minimatch-1.0.2.tgz#316b0a63b91c10e53f242efb4ace5c3b34e8728d" integrity sha512-rSqmMJDdLFUsyxR6FMtD00nfQKKLFb1kv+qBbOVKqErvloEIJLo5bDTJTQNTYgeyp78JsA7u/NPi5jT1GR/MuA== +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== + "@humanwhocodes/object-schema@^1.2.1": version "1.2.1" resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" @@ -1058,10 +1063,10 @@ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== -"@jridgewell/trace-mapping@^0.3.7", "@jridgewell/trace-mapping@^0.3.8", "@jridgewell/trace-mapping@^0.3.9": - version "0.3.14" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz#b231a081d8f66796e475ad588a1ef473112701ed" - integrity sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ== +"@jridgewell/trace-mapping@^0.3.14", "@jridgewell/trace-mapping@^0.3.8", "@jridgewell/trace-mapping@^0.3.9": + version "0.3.15" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz#aba35c48a38d3fd84b37e66c9c0423f9744f9774" + integrity sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g== dependencies: "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" @@ -1152,9 +1157,9 @@ "@types/estree" "*" "@types/eslint@*": - version "8.4.5" - resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.4.5.tgz#acdfb7dd36b91cc5d812d7c093811a8f3d9b31e4" - integrity sha512-dhsC09y1gpJWnK+Ff4SGvCuSnk9DaU0BJZSzOwa6GVSg65XtTugLBITDAAzRU5duGBoXBHpdR/9jHGxJjNflJQ== + version "8.4.6" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.4.6.tgz#7976f054c1bccfcf514bff0564c0c41df5c08207" + integrity sha512-/fqTbjxyFUaYNO7VcW5g+4npmqVACz1bB7RTHYuLj+PRjw9hrCwrUXVQFpChUS0JsyEFvMZ7U/PfmvWgxJhI9g== dependencies: "@types/estree" "*" "@types/json-schema" "*" @@ -1216,9 +1221,9 @@ integrity sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA== "@types/node@*": - version "18.6.4" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.6.4.tgz#fd26723a8a3f8f46729812a7f9b4fc2d1608ed39" - integrity sha512-I4BD3L+6AWiUobfxZ49DlU43gtI+FTHSv9pE2Zekg6KjMpre4ByusaljW3vYSLJrvQ1ck1hUaeVu8HVlY3vzHg== + version "18.7.14" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.7.14.tgz#0fe081752a3333392d00586d815485a17c2cf3c9" + integrity sha512-6bbDaETVi8oyIARulOE9qF1/Qdi/23z6emrUh0fNJRUmjznqrixD4MpGDdgOFk5Xb0m2H6Xu42JGdvAxaJR/wA== "@types/q@^1.5.1": version "1.5.5" @@ -1950,9 +1955,9 @@ body-parser@1.20.0: unpipe "1.0.0" bonjour-service@^1.0.11: - version "1.0.13" - resolved "https://registry.yarnpkg.com/bonjour-service/-/bonjour-service-1.0.13.tgz#4ac003dc1626023252d58adf2946f57e5da450c1" - integrity sha512-LWKRU/7EqDUC9CTAQtuZl5HzBALoCYwtLhffW3et7vZMwv3bWLpJf8bRYlMD5OCcDpTfnPgNCV4yo9ZIaJGMiA== + version "1.0.14" + resolved "https://registry.yarnpkg.com/bonjour-service/-/bonjour-service-1.0.14.tgz#c346f5bc84e87802d08f8d5a60b93f758e514ee7" + integrity sha512-HIMbgLnk1Vqvs6B4Wq5ep7mxvj9sGz5d1JJyDNSGNIdA/w2MCz6GTjWTdjqOJV1bEPj+6IkxDvWNFKEBxNt4kQ== dependencies: array-flatten "^2.1.2" dns-equal "^1.0.0" @@ -2105,9 +2110,9 @@ caniuse-api@^3.0.0: lodash.uniq "^4.5.0" caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001370: - version "1.0.30001374" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001374.tgz#3dab138e3f5485ba2e74bd13eca7fe1037ce6f57" - integrity sha512-mWvzatRx3w+j5wx/mpFN5v5twlPrabG8NqX2c6e45LCpymdoGqNvRkRutFUqpRTXKFQFNQJasvK0YT7suW6/Hw== + version "1.0.30001388" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001388.tgz#88e01f4591cbd81f9f665f3f078c66b509fbe55d" + integrity sha512-znVbq4OUjqgLxMxoNX2ZeeLR0d7lcDiE5uJ4eUiWdml1J1EkxbnQq6opT9jb9SMfJxB0XA16/ziHwni4u1I3GQ== chalk@^2.0.0, chalk@^2.4.1: version "2.4.2" @@ -2377,17 +2382,17 @@ copy-concurrently@^1.0.0: run-queue "^1.0.0" core-js-compat@^3.21.0, core-js-compat@^3.22.1: - version "3.24.1" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.24.1.tgz#d1af84a17e18dfdd401ee39da9996f9a7ba887de" - integrity sha512-XhdNAGeRnTpp8xbD+sR/HFDK9CbeeeqXT6TuofXh3urqEevzkWmLRgrVoykodsw8okqo2pu1BOmuCKrHx63zdw== + version "3.25.0" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.25.0.tgz#489affbfbf9cb3fa56192fe2dd9ebaee985a66c5" + integrity sha512-extKQM0g8/3GjFx9US12FAgx8KJawB7RCQ5y8ipYLbmfzEzmFRWdDjIlxDx82g7ygcNG85qMVUSRyABouELdow== dependencies: browserslist "^4.21.3" semver "7.0.0" core-js@^3.15.2: - version "3.24.1" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.24.1.tgz#cf7724d41724154010a6576b7b57d94c5d66e64f" - integrity sha512-0QTBSYSUZ6Gq21utGzkfITDylE8jWC9Ne1D2MrhvlsZBI1x39OdDIVbzSqtgMndIy6BlHxBXpMGqzZmnztg2rg== + version "3.25.0" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.25.0.tgz#be71d9e0dd648ffd70c44a7ec2319d039357eceb" + integrity sha512-CVU1xvJEfJGhyCpBrzzzU1kjCfgsGUxhEvwUV2e/cOedYWHdmluamx+knDnmhqALddMG16fZvIqvs9aijsHHaA== core-util-is@~1.0.0: version "1.0.3" @@ -2783,9 +2788,9 @@ ee-first@1.1.1: integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== electron-to-chromium@^1.4.202: - version "1.4.211" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.211.tgz#afaa8b58313807501312d598d99b953568d60f91" - integrity sha512-BZSbMpyFQU0KBJ1JG26XGeFI3i4op+qOYGxftmZXFZoHkhLgsSv4DHDJfl8ogII3hIuzGt51PaZ195OVu0yJ9A== + version "1.4.241" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.241.tgz#5aa03ab94db590d8269f4518157c24b1efad34d6" + integrity sha512-e7Wsh4ilaioBZ5bMm6+F4V5c11dh56/5Jwz7Hl5Tu1J7cnB+Pqx5qIF2iC7HPpfyQMqGSvvLP5bBAIDd2gAtGw== email-addresses@^3.0.1: version "3.1.0" @@ -2847,15 +2852,15 @@ error-ex@^1.3.1: is-arrayish "^0.2.1" es-abstract@^1.17.2, es-abstract@^1.19.0, es-abstract@^1.19.1, es-abstract@^1.19.2, es-abstract@^1.19.5, es-abstract@^1.20.1: - version "1.20.1" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.1.tgz#027292cd6ef44bd12b1913b828116f54787d1814" - integrity sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA== + version "1.20.2" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.2.tgz#8495a07bc56d342a3b8ea3ab01bd986700c2ccb3" + integrity sha512-XxXQuVNrySBNlEkTYJoDNFe5+s2yIOpzq80sUHEdPdQr0S5nTLz4ZPPPswNIpKseDDUS5yghX1gfLIHQZ1iNuQ== dependencies: call-bind "^1.0.2" es-to-primitive "^1.2.1" function-bind "^1.1.1" function.prototype.name "^1.1.5" - get-intrinsic "^1.1.1" + get-intrinsic "^1.1.2" get-symbol-description "^1.0.0" has "^1.0.3" has-property-descriptors "^1.0.0" @@ -2867,9 +2872,9 @@ es-abstract@^1.17.2, es-abstract@^1.19.0, es-abstract@^1.19.1, es-abstract@^1.19 is-shared-array-buffer "^1.0.2" is-string "^1.0.7" is-weakref "^1.0.2" - object-inspect "^1.12.0" + object-inspect "^1.12.2" object-keys "^1.1.1" - object.assign "^4.1.2" + object.assign "^4.1.4" regexp.prototype.flags "^1.4.3" string.prototype.trimend "^1.0.5" string.prototype.trimstart "^1.0.5" @@ -2999,13 +3004,14 @@ eslint-visitor-keys@^3.3.0: integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== "eslint@^7.32.0 || ^8.2.0": - version "8.22.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.22.0.tgz#78fcb044196dfa7eef30a9d65944f6f980402c48" - integrity sha512-ci4t0sz6vSRKdmkOGmprBo6fmI4PrphDFMy5JEq/fNS0gQkJM3rLmrqcp8ipMcdobH3KtUP40KniAE9W19S4wA== + version "8.23.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.23.0.tgz#a184918d288820179c6041bb3ddcc99ce6eea040" + integrity sha512-pBG/XOn0MsJcKcTRLr27S5HpzQo4kLr+HjLQIyK4EiCsijDl/TB+h5uEuJU6bQ8Edvwz1XWOjpaP2qgnXGpTcA== dependencies: - "@eslint/eslintrc" "^1.3.0" + "@eslint/eslintrc" "^1.3.1" "@humanwhocodes/config-array" "^0.10.4" "@humanwhocodes/gitignore-to-minimatch" "^1.0.2" + "@humanwhocodes/module-importer" "^1.0.1" ajv "^6.10.0" chalk "^4.0.0" cross-spawn "^7.0.2" @@ -3015,7 +3021,7 @@ eslint-visitor-keys@^3.3.0: eslint-scope "^7.1.1" eslint-utils "^3.0.0" eslint-visitor-keys "^3.3.0" - espree "^9.3.3" + espree "^9.4.0" esquery "^1.4.0" esutils "^2.0.2" fast-deep-equal "^3.1.3" @@ -3041,12 +3047,11 @@ eslint-visitor-keys@^3.3.0: strip-ansi "^6.0.1" strip-json-comments "^3.1.0" text-table "^0.2.0" - v8-compile-cache "^2.0.3" -espree@^9.3.2, espree@^9.3.3: - version "9.3.3" - resolved "https://registry.yarnpkg.com/espree/-/espree-9.3.3.tgz#2dd37c4162bb05f433ad3c1a52ddf8a49dc08e9d" - integrity sha512-ORs1Rt/uQTqUKjDdGCyrtYxbazf5umATSf/K4qxjmZHORR6HJk+2s/2Pqe+Kk49HHINC/xNIrGfgh8sZcll0ng== +espree@^9.4.0: + version "9.4.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.4.0.tgz#cd4bc3d6e9336c433265fc0aa016fc1aaf182f8a" + integrity sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw== dependencies: acorn "^8.8.0" acorn-jsx "^5.3.2" @@ -3448,7 +3453,7 @@ gensync@^1.0.0-beta.2: resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== -get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: +get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1, get-intrinsic@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.2.tgz#336975123e05ad0b7ba41f152ee4aadbea6cf598" integrity sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA== @@ -4584,7 +4589,7 @@ object-assign@^4.0.1, object-assign@^4.1.0: resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== -object-inspect@^1.12.0, object-inspect@^1.9.0: +object-inspect@^1.12.2, object-inspect@^1.9.0: version "1.12.2" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea" integrity sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ== @@ -4594,10 +4599,10 @@ object-keys@^1.1.1: resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== -object.assign@^4.1.0, object.assign@^4.1.2: - version "4.1.3" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.3.tgz#d36b7700ddf0019abb6b1df1bb13f6445f79051f" - integrity sha512-ZFJnX3zltyjcYJL0RoCJuzb+11zWGyaDbjgxZbdV7rFEcHQuYxrZqhow67aA7xpes6LhojyFDaBKAFfogQrikA== +object.assign@^4.1.0, object.assign@^4.1.2, object.assign@^4.1.4: + version "4.1.4" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f" + integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ== dependencies: call-bind "^1.0.2" define-properties "^1.1.4" @@ -6003,20 +6008,20 @@ tar@^6.0.2: yallist "^4.0.0" terser-webpack-plugin@^5.1.3, terser-webpack-plugin@^5.3.3: - version "5.3.3" - resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.3.tgz#8033db876dd5875487213e87c627bca323e5ed90" - integrity sha512-Fx60G5HNYknNTNQnzQ1VePRuu89ZVYWfjRAeT5rITuCY/1b08s49e5kSQwHDirKZWuoKOBRFS98EUUoZ9kLEwQ== + version "5.3.6" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz#5590aec31aa3c6f771ce1b1acca60639eab3195c" + integrity sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ== dependencies: - "@jridgewell/trace-mapping" "^0.3.7" + "@jridgewell/trace-mapping" "^0.3.14" jest-worker "^27.4.5" schema-utils "^3.1.1" serialize-javascript "^6.0.0" - terser "^5.7.2" + terser "^5.14.1" -terser@^5.10.0, terser@^5.7.2: - version "5.14.2" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.14.2.tgz#9ac9f22b06994d736174f4091aa368db896f1c10" - integrity sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA== +terser@^5.10.0, terser@^5.14.1: + version "5.15.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.15.0.tgz#e16967894eeba6e1091509ec83f0c60e179f2425" + integrity sha512-L1BJiXVmheAQQy+as0oF3Pwtlo4s3Wi1X2zNZ2NxOB4wx9bdS9Vk67XQENLFdLYGCK/Z2di53mTj/hBafR+dTA== dependencies: "@jridgewell/source-map" "^0.3.2" acorn "^8.5.0" @@ -6126,9 +6131,9 @@ typedarray@^0.0.6: integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== uglify-js@^3.6.0: - version "3.16.3" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.16.3.tgz#94c7a63337ee31227a18d03b8a3041c210fd1f1d" - integrity sha512-uVbFqx9vvLhQg0iBaau9Z75AxWJ8tqM9AV890dIZCLApF4rTcyHwmAvLeEdYRs+BzYWu8Iw81F79ah0EfTXbaw== + version "3.17.0" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.17.0.tgz#55bd6e9d19ce5eef0d5ad17cd1f587d85b180a85" + integrity sha512-aTeNPVmgIMPpm1cxXr2Q/nEbvkmV8yq66F3om7X3P/cvOXQ0TMQ64Wk63iyT1gPlmdmGzjGpyLh1f3y8MZWXGg== uglifyjs-webpack-plugin@^2.2.0: version "2.2.0" @@ -6218,9 +6223,9 @@ unquote@~1.1.1: integrity sha512-vRCqFv6UhXpWxZPyGDh/F3ZpNv8/qo7w6iufLpQg9aKnQ71qM4B5KiI7Mia9COcjEhrO9LueHpMYjYzsWH3OIg== update-browserslist-db@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz#be06a5eedd62f107b7c19eb5bcefb194411abf38" - integrity sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q== + version "1.0.7" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.7.tgz#16279639cff1d0f800b14792de43d97df2d11b7d" + integrity sha512-iN/XYesmZ2RmmWAiI4Z5rq0YqSiv0brj9Ce9CfhNE4xIW2h+MFxcgkxIzZ+ShkFPUkjU3gQ+3oypadD3RAMtrg== dependencies: escalade "^3.1.1" picocolors "^1.0.0" @@ -6262,11 +6267,6 @@ uuid@^8.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== -v8-compile-cache@^2.0.3: - version "2.3.0" - resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" - integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== - vary@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" @@ -6334,9 +6334,9 @@ webpack-dev-middleware@^5.3.1: schema-utils "^4.0.0" webpack-dev-server@^4.9.3: - version "4.9.3" - resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-4.9.3.tgz#2360a5d6d532acb5410a668417ad549ee3b8a3c9" - integrity sha512-3qp/eoboZG5/6QgiZ3llN8TUzkSpYg1Ko9khWX1h40MIEUNS2mDoIa8aXsPfskER+GbTvs/IJZ1QTBBhhuetSw== + version "4.10.1" + resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-4.10.1.tgz#124ac9ac261e75303d74d95ab6712b4aec3e12ed" + integrity sha512-FIzMq3jbBarz3ld9l7rbM7m6Rj1lOsgq/DyLGMX/fPEB1UBUPtf5iL/4eNfhx8YYJTRlzfv107UfWSWcBK5Odw== dependencies: "@types/bonjour" "^3.5.9" "@types/connect-history-api-fallback" "^1.3.5" From 4ff33da6dea753ece1a690b52f563ab86686210f Mon Sep 17 00:00:00 2001 From: Amr Wagdy Date: Sun, 4 Sep 2022 20:24:58 +0200 Subject: [PATCH 15/45] Feat:Add animation to gallery - T7582 --- src/low-preview/ci.service.js | 66 ++++++++++++++++++++++------- src/low-preview/ci.styles.css | 80 ++++++++++++++++------------------- 2 files changed, 88 insertions(+), 58 deletions(-) diff --git a/src/low-preview/ci.service.js b/src/low-preview/ci.service.js index 8b0fed9..28009d5 100644 --- a/src/low-preview/ci.service.js +++ b/src/low-preview/ci.service.js @@ -177,13 +177,13 @@ export default class CIResponsive { } }; - handleArrowClick(galleryImages, direction) { + handleClickArrows(galleryImages, direction) { let nextIndex = 0; const [length, index] = getGalleryLengthAndIndex(); - + const leftDirection = direction === 'left'; nextIndex = +index; - if (direction === 'left') { + if (leftDirection) { nextIndex -= 1; if ((length - 1 + nextIndex) <= 0) { // left button @@ -197,21 +197,57 @@ export default class CIResponsive { } } - this.processGalleryPreviewImage(galleryImages[nextIndex], direction); + this.processGalleryPreviewImage(galleryImages[nextIndex], nextIndex, direction); setGalleryIndex(nextIndex); } - processGalleryPreviewImage(imgNode, direction) { + animatePreviewModule(previewModule, nextIndex, direction) { + const currentIndex = previewModule.getAttribute('data-ci-active-image-index'); + const leftDirection = direction === 'left'; + let transform = 1000; + let scale = 0.8; + + if (leftDirection || (!direction && nextIndex < currentIndex)) { + transform = -1000; + } + + const scaleAnimation = setInterval(() => { // Scale preview image + scale += 0.01; + + if (scale >= 1) { + clearInterval(scaleAnimation); + } + + previewModule.style.transform = `scale(${scale}) translate(${transform}px)`; + }, 10); + + const transformAnimation = setInterval(() => { // Transform preview image + if (leftDirection || (!direction && nextIndex < currentIndex)) { + transform += 20; + } else { + transform -= 20; + } + + if (transform === 0) { + clearInterval(transformAnimation); + } + + previewModule.style.transform = `scale(${scale}) translate(${transform}px)`; + }, 5); + } + + processGalleryPreviewImage(imgNode, imageIndex, direction, intial) { const _imgNode = imgNode.cloneNode(); const adaptedImageNode = removeClassNames(_imgNode, loadedImageClassNames); const previewModule = getGalleryPreviewModule(); - if (direction) { - adaptedImageNode.setAttribute('ci-direction', direction); + if (!intial) { + this.animatePreviewModule(previewModule, imageIndex, direction); } adaptedImageNode.style = {}; adaptedImageNode.setAttribute('data-ci-processed-gallery', true); + previewModule.setAttribute('data-ci-active-image-index', imageIndex); previewModule.innerHTML = ''; previewModule.appendChild(adaptedImageNode); @@ -225,7 +261,7 @@ export default class CIResponsive { if (thumbnailIndex !== index) { setGalleryIndex(thumbnailIndex); - this.processGalleryPreviewImage(galleryImages[thumbnailIndex]); + this.processGalleryPreviewImage(galleryImages[thumbnailIndex], thumbnailIndex); } } @@ -253,7 +289,7 @@ export default class CIResponsive { const galleryArrows = createGalleryArrows( leftArrowSvg, rightArrowSvg, - this.handleArrowClick.bind(this, galleryImages), + this.handleClickArrows.bind(this, galleryImages), ); galleryModal.appendChild(previewModule); @@ -261,7 +297,7 @@ export default class CIResponsive { galleryModal.append(...galleryArrows); document.body.appendChild(galleryModal); - this.processGalleryPreviewImage(galleryImages[clickedImageIndex]); + this.processGalleryPreviewImage(galleryImages[clickedImageIndex], clickedImageIndex, undefined, true); setGalleryIndex(clickedImageIndex); } @@ -337,17 +373,17 @@ export default class CIResponsive { getDimAndFit(imgNode); - wrapper.onclick = this.handleClickWrapper.bind(this, imgProps, images); + + if (gallery && !isProcessedByGallery) { + wrapper.style.cursor = 'pointer'; + wrapper.onclick = this.handleClickWrapper.bind(this, imgProps, images); + } imgNode.onload = () => { if (config.onImageLoad && typeof config.onImageLoad === 'function') { config.onImageLoad(imgNode); } - if (gallery && !isProcessedByGallery) { - wrapper.style.cursor = 'pointer'; - } - wrapper.onmouseenter = handleHoveringWrapper.bind(this, wrapper, imgProps, zoomIconSvg); wrapper.onmouseout = handleUnHoveringWrapper.bind(this, wrapper, imgProps); diff --git a/src/low-preview/ci.styles.css b/src/low-preview/ci.styles.css index 305d412..60bf8ab 100644 --- a/src/low-preview/ci.styles.css +++ b/src/low-preview/ci.styles.css @@ -48,13 +48,24 @@ img.ci-image-loaded { } .ci-gallery-modal { - width: 100%; - height: 100%; - background-color: black; - position: fixed; + display: block; top: 0; + height: 100%; + width: 100%; left: 0; - z-index: 110; + position: fixed; + visibility: visible; + opacity: 1; + background-color: rgba(1, 1, 1, 1); + text-align: center; + clear: both; + user-select: none; + -moz-user-select: none; + -webkit-user-drag: none; + -webkit-user-select: none; + -ms-user-select: none; + animation: slideVertical 400ms cubic-bezier(0.075, 0.42, 0.165, 1); + z-index: 200; } .ci-gallery-preview-module { @@ -80,17 +91,6 @@ img.ci-image-loaded { background-color: black; } -.ci-gallery-thumbnail-module { - justify-content: center; - display: flex; - width: 100%; - height: 40px; - position: absolute; - bottom: 10px; - padding-left: 20px; - padding-right: 20px; -} - .ci-gallery-close-button { width: 23px; height: 23px; @@ -105,13 +105,24 @@ img.ci-image-loaded { cursor: pointer; } +.ci-gallery-thumbnail-module { + display: flex; + position: absolute; + align-items: center; + justify-content: center; + column-gap: 5px; + width: 100%; + height: 40px; + bottom: 10px; + left: 0; + animation: slideHorizontal 400ms cubic-bezier(0.075, 0.42, 0.165, 1); +} + .ci-gallery-thmbnail-container { width: 40px; height: 40px; - margin : 2px; - display: flex; - justify-content: center; - align-items: center; + border: 1px solid #888; + cursor: pointer; } .ci-gallery-right-arrow-button { @@ -156,30 +167,13 @@ img.ci-image-loaded { background-color: rgba(255, 255, 255, 0.5); } -@keyframes fadeIn { - 0% { opacity: 0; } - 100% { opacity: 1; } -} - -@keyframes fadeOut { - 0% { opacity: 1; } - 100% { opacity: 0; } +@keyframes slideVertical { + 0% { transform: translateY(1000px);} + 100% {transform: translateY(0);} } -@keyframes rightPop { - 0% { - transform: translateX(100%); - } - 100% { - transform: translateX(0%); - } +@keyframes slideHorizontal { + 0% { transform: translateX(-2000px);} + 100% {transform: translateX(0);} } -@keyframes leftPop { - 0% { - transform: translateX(-100%); - } - 100% { - transform: translateX(0%); - } -} From e98367e0b1b834d671058ebf90b569ef740be745 Mon Sep 17 00:00:00 2001 From: Nermeen Moustafa Date: Mon, 5 Sep 2022 11:26:51 +0200 Subject: [PATCH 16/45] improve the zoom code - T7582 --- src/low-preview/ci.service.js | 38 ++++++++++++++++++++++++-------- src/low-preview/ci.styles.css | 10 +++++++++ src/low-preview/gallery.utils.js | 16 +++++++++++--- 3 files changed, 52 insertions(+), 12 deletions(-) diff --git a/src/low-preview/ci.service.js b/src/low-preview/ci.service.js index 28009d5..5fb25f3 100644 --- a/src/low-preview/ci.service.js +++ b/src/low-preview/ci.service.js @@ -30,6 +30,7 @@ import { removeClassNames, createThmbnailsModule, getGalleryImages, + getZoomImages, getDimAndFit, } from './gallery.utils'; import { getInitialConfigLowPreview } from './ci.config'; @@ -236,6 +237,19 @@ export default class CIResponsive { }, 5); } + processZoomPreviewImage(imgNode) { + const _imgNode = imgNode.cloneNode(); + const adaptedImageNode = removeClassNames(_imgNode, loadedImageClassNames); + const previewModule = getGalleryPreviewModule(); + + adaptedImageNode.style = {}; + adaptedImageNode.setAttribute('data-ci-processed-gallery', true); + previewModule.innerHTML = ''; + previewModule.appendChild(adaptedImageNode); + + this.getBasicInfo(adaptedImageNode, false, false, 'image', undefined, true); + } + processGalleryPreviewImage(imgNode, imageIndex, direction, intial) { const _imgNode = imgNode.cloneNode(); const adaptedImageNode = removeClassNames(_imgNode, loadedImageClassNames); @@ -301,16 +315,20 @@ export default class CIResponsive { setGalleryIndex(clickedImageIndex); } - // if (zoom && !gallery) { - // const galleryModal = createGalleryModal(); - // const previewModule = galleryModal.querySelector('.ci-gallery-preview-module'); + if (zoom && !gallery) { + const clickedImage = event.currentTarget.querySelector('img[ci-src]'); + const zoomImages = getZoomImages(images); + const clickedImageIndex = zoomImages.indexOf(clickedImage); - // galleryModal.appendChild(previewModule); + const galleryModal = createGalleryModal(0, closeIconSvg, false); + const previewModule = galleryModal.querySelector('.ci-gallery-preview-module'); - // document.body.appendChild(galleryModal); + galleryModal.appendChild(previewModule); - // this.process(false, previewModule); - // } + document.body.appendChild(galleryModal); + + this.processZoomPreviewImage(zoomImages[clickedImageIndex]); + } } processImage(props) { @@ -332,7 +350,9 @@ export default class CIResponsive { isGalleryImg, } = props; - const { params, gallery, isProcessedByGallery } = imgProps; + const { + params, zoom, gallery, isProcessedByGallery, + } = imgProps; const { width, ratio } = containerProps; const { config } = this; const { dataSrcAttr, placeholderBackground } = config; @@ -374,7 +394,7 @@ export default class CIResponsive { getDimAndFit(imgNode); - if (gallery && !isProcessedByGallery) { + if ((zoom || gallery) && !isProcessedByGallery) { wrapper.style.cursor = 'pointer'; wrapper.onclick = this.handleClickWrapper.bind(this, imgProps, images); } diff --git a/src/low-preview/ci.styles.css b/src/low-preview/ci.styles.css index 60bf8ab..225ec4d 100644 --- a/src/low-preview/ci.styles.css +++ b/src/low-preview/ci.styles.css @@ -177,3 +177,13 @@ img.ci-image-loaded { 100% {transform: translateX(0);} } +@keyframes fadeIn { + 0% { opacity: 0; } + 100% { opacity: 1; } +} + +@keyframes fadeOut { + 0% { opacity: 1; } + 100% { opacity: 0; } +} + diff --git a/src/low-preview/gallery.utils.js b/src/low-preview/gallery.utils.js index 686fac4..1af6f43 100644 --- a/src/low-preview/gallery.utils.js +++ b/src/low-preview/gallery.utils.js @@ -30,7 +30,7 @@ const destroyGallery = (galleryModal) => { setTimeout(() => { galleryModal.parentNode.removeChild(galleryModal); - }, 700); + }, 600); }; const createGalleryModal = (galleryLength, closeIconSrc, isGallery) => { @@ -91,10 +91,13 @@ const handleUnHoveringWrapper = (wrapper, imgProps) => { if (zoom && !gallery) { const zoomIcon = wrapper.querySelector('.ci-gallery-zoom-button'); - zoomIcon.style.animation = 'fadeOut 0.4s'; + + if (zoomIcon) { + zoomIcon.style.animation = 'fadeOut 0.4s'; + } setTimeout(() => { - if (zoomIcon) { + if (zoomIcon && zoomIcon.parentNode) { zoomIcon.parentNode.removeChild(zoomIcon); } }, 300); @@ -208,6 +211,12 @@ const getGalleryImages = (images, galleryName) => [...images].filter((image) => return gallery === galleryName; }); +const getZoomImages = (images) => [...images].filter((image) => { + const { zoom } = getCommonImageProps(image); + + return zoom === 'true'; +}); + const getImageFitStyles = (naturalWidth, naturalHeight) => { let shouldFitHorizontally; const imageStyles = {}; @@ -282,6 +291,7 @@ export { removeClassNames, createThmbnailsModule, getGalleryImages, + getZoomImages, getImageFitStyles, getCurrentImage, galleryPreviewImage, From e11b77bf9bdb165237e9458d3a492567a0c44648 Mon Sep 17 00:00:00 2001 From: Nermeen Moustafa Date: Mon, 5 Sep 2022 13:02:51 +0200 Subject: [PATCH 17/45] handle arrow events - T7582 --- src/low-preview/ci.service.js | 21 +++++++++++++++++++++ src/low-preview/gallery.utils.js | 5 ++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/low-preview/ci.service.js b/src/low-preview/ci.service.js index 5fb25f3..d757c04 100644 --- a/src/low-preview/ci.service.js +++ b/src/low-preview/ci.service.js @@ -20,6 +20,7 @@ import { setSrcset, } from '../common/ci.utils'; import { + destroyGallery, createGalleryModal, handleHoveringWrapper, handleUnHoveringWrapper, @@ -71,6 +72,22 @@ export default class CIResponsive { this.process(); } + handleArrowEvents(galleryImages, isGallery, event) { + const galleryModal = document.querySelector('.ci-gallery-modal'); + + if (event.key === 'ArrowRight' && isGallery) { + this.handleClickArrows(galleryImages, 'right'); + } + if (event.key === 'ArrowLeft' && isGallery) { + this.handleClickArrows(galleryImages, 'left'); + } + if (event.key === 'Escape') { + destroyGallery(galleryModal); + } + + event.preventDefault(); + } + onUpdateDimensions() { this.process(true); @@ -311,6 +328,8 @@ export default class CIResponsive { galleryModal.append(...galleryArrows); document.body.appendChild(galleryModal); + document.onkeydown = this.handleArrowEvents.bind(this, galleryImages, true); + this.processGalleryPreviewImage(galleryImages[clickedImageIndex], clickedImageIndex, undefined, true); setGalleryIndex(clickedImageIndex); } @@ -327,6 +346,8 @@ export default class CIResponsive { document.body.appendChild(galleryModal); + document.onkeydown = this.handleArrowEvents.bind(this, images, false); + this.processZoomPreviewImage(zoomImages[clickedImageIndex]); } } diff --git a/src/low-preview/gallery.utils.js b/src/low-preview/gallery.utils.js index 1af6f43..4b3e8d4 100644 --- a/src/low-preview/gallery.utils.js +++ b/src/low-preview/gallery.utils.js @@ -29,7 +29,9 @@ const destroyGallery = (galleryModal) => { galleryModal.style.animation = 'fadeOut 0.7s'; setTimeout(() => { - galleryModal.parentNode.removeChild(galleryModal); + if (galleryModal && galleryModal.parentNode) { + galleryModal.parentNode.removeChild(galleryModal); + } }, 600); }; @@ -281,6 +283,7 @@ const getDimAndFit = (imgNode) => { }; export { + destroyGallery, createGalleryModal, handleHoveringWrapper, handleUnHoveringWrapper, From a886953eb03da6c172883a54810c65af931e70b9 Mon Sep 17 00:00:00 2001 From: Nermeen Moustafa Date: Mon, 5 Sep 2022 13:39:43 +0200 Subject: [PATCH 18/45] fix arrows navigation - T7582 --- src/low-preview/ci.service.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/low-preview/ci.service.js b/src/low-preview/ci.service.js index d757c04..d20f035 100644 --- a/src/low-preview/ci.service.js +++ b/src/low-preview/ci.service.js @@ -204,13 +204,13 @@ export default class CIResponsive { if (leftDirection) { nextIndex -= 1; - if ((length - 1 + nextIndex) <= 0) { // left button + if (nextIndex < 0) { // left button nextIndex = length - 1; } } else { nextIndex += 1; - if ((length - 1 + nextIndex) > length) { // right button + if (nextIndex === Number(length)) { // right button nextIndex = 0; } } From 8917042c2c4c547fa5c847ee9d8f3cb35c0f6a1e Mon Sep 17 00:00:00 2001 From: Nermeen Moustafa Date: Mon, 5 Sep 2022 15:07:53 +0200 Subject: [PATCH 19/45] add prop for gallery image name - T7582 --- examples/low-preview/src/index.html | 4 ++++ src/common/ci.utils.js | 1 + src/low-preview/ci.service.js | 13 +++++++++++++ src/low-preview/ci.styles.css | 11 +++++++++++ src/low-preview/gallery.utils.js | 18 ++++++++++++++++++ 5 files changed, 47 insertions(+) diff --git a/examples/low-preview/src/index.html b/examples/low-preview/src/index.html index 8b6f76f..c717807 100644 --- a/examples/low-preview/src/index.html +++ b/examples/low-preview/src/index.html @@ -206,6 +206,7 @@

Responsive images, in real-time!

ci-params="ci_info=2" ci-ratio="0.942" ci-gallery="gallery1" + ci-gallery-img-name = 'House+img.jpg' />
@@ -223,6 +224,7 @@

Responsive images, in real-time!

ci-params="ci_info=2" ci-ratio="1" ci-gallery="gallery2" + ci-gallery-img-name = 'Church+square+img.jpg' />

@@ -238,6 +240,7 @@

Responsive images, in real-time!

ci-params="w=265&h=265&gravity=face&radius=max" id="right-column-second-image" ci-gallery="gallery2" + ci-gallery-img-name = 'Girl+img.jpg' />
@@ -256,6 +259,7 @@

Responsive images, in real-time!

ci-ratio="2.885" ci-params="ci_info=2" ci-gallery="gallery2" + ci-gallery-img-name = 'Dresses+img.jpg' />

diff --git a/src/common/ci.utils.js b/src/common/ci.utils.js index f5d579b..71bafb5 100644 --- a/src/common/ci.utils.js +++ b/src/common/ci.utils.js @@ -73,6 +73,7 @@ const getCommonImageProps = (image) => ({ alt: attr(image, 'alt'), zoom: attr(image, 'ci-zoom') || undefined, gallery: attr(image, 'ci-gallery') || undefined, + galleryImgName: attr(image, 'ci-gallery-img-name') || undefined, }); const filterImages = (images, type) => { diff --git a/src/low-preview/ci.service.js b/src/low-preview/ci.service.js index d20f035..673f6f6 100644 --- a/src/low-preview/ci.service.js +++ b/src/low-preview/ci.service.js @@ -22,6 +22,8 @@ import { import { destroyGallery, createGalleryModal, + appendGalleryImageName, + destroyGalleryImageName, handleHoveringWrapper, handleUnHoveringWrapper, getGalleryPreviewModule, @@ -268,6 +270,10 @@ export default class CIResponsive { } processGalleryPreviewImage(imgNode, imageIndex, direction, intial) { + const { imgSelector } = this.config; + const { galleryImgName } = getImageProps(imgNode, imgSelector); + + const galleryModal = document.querySelector('.ci-gallery-modal'); const _imgNode = imgNode.cloneNode(); const adaptedImageNode = removeClassNames(_imgNode, loadedImageClassNames); const previewModule = getGalleryPreviewModule(); @@ -276,6 +282,13 @@ export default class CIResponsive { this.animatePreviewModule(previewModule, imageIndex, direction); } + destroyGalleryImageName(); + + if (galleryImgName) { + const imageName = appendGalleryImageName(galleryImgName); + galleryModal.append(imageName); + } + adaptedImageNode.style = {}; adaptedImageNode.setAttribute('data-ci-processed-gallery', true); previewModule.setAttribute('data-ci-active-image-index', imageIndex); diff --git a/src/low-preview/ci.styles.css b/src/low-preview/ci.styles.css index 225ec4d..9ec1cfc 100644 --- a/src/low-preview/ci.styles.css +++ b/src/low-preview/ci.styles.css @@ -91,6 +91,17 @@ img.ci-image-loaded { background-color: black; } +.ci-gallery-image-name { + color: white; + padding: 10px; + width: fit-content; + height: fit-content; + position: absolute; + top: 0; + left: 0; + z-index: 2; +} + .ci-gallery-close-button { width: 23px; height: 23px; diff --git a/src/low-preview/gallery.utils.js b/src/low-preview/gallery.utils.js index 4b3e8d4..d9e1b99 100644 --- a/src/low-preview/gallery.utils.js +++ b/src/low-preview/gallery.utils.js @@ -62,6 +62,22 @@ const createGalleryModal = (galleryLength, closeIconSrc, isGallery) => { return galleryModal; }; +const appendGalleryImageName = (imageName) => { + const galleryImageName = document.createElement('div'); + galleryImageName.classList.add('ci-gallery-image-name'); + galleryImageName.innerText = imageName; + + return galleryImageName; +}; + +const destroyGalleryImageName = () => { + const galleryImageName = document.querySelector('.ci-gallery-image-name'); + + if (galleryImageName) { + galleryImageName.parentNode.removeChild(galleryImageName); + } +}; + const handleHoveringWrapper = (wrapper, imgProps, zoomIconSrc) => { const isPreviewWrapper = wrapper.parentNode.className === previewContainer; @@ -285,6 +301,8 @@ const getDimAndFit = (imgNode) => { export { destroyGallery, createGalleryModal, + appendGalleryImageName, + destroyGalleryImageName, handleHoveringWrapper, handleUnHoveringWrapper, getGalleryPreviewModule, From cd11652aa1e8aac4c124d92ff98e25b3b4e6aa13 Mon Sep 17 00:00:00 2001 From: Amr Wagdy Date: Tue, 6 Sep 2022 18:10:22 +0200 Subject: [PATCH 20/45] Refactor:: Code review --- examples/low-preview/src/index.html | 8 ++-- src/common/ci.utils.js | 2 +- src/low-preview/ci.service.js | 69 ++++++++++++++--------------- src/low-preview/ci.styles.css | 33 +++++++------- src/low-preview/gallery.utils.js | 60 ++++++++++++------------- 5 files changed, 81 insertions(+), 91 deletions(-) diff --git a/examples/low-preview/src/index.html b/examples/low-preview/src/index.html index c717807..65ef36e 100644 --- a/examples/low-preview/src/index.html +++ b/examples/low-preview/src/index.html @@ -206,7 +206,7 @@

Responsive images, in real-time!

ci-params="ci_info=2" ci-ratio="0.942" ci-gallery="gallery1" - ci-gallery-img-name = 'House+img.jpg' + ci-image-name= 'House+img.jpg' />
@@ -224,7 +224,7 @@

Responsive images, in real-time!

ci-params="ci_info=2" ci-ratio="1" ci-gallery="gallery2" - ci-gallery-img-name = 'Church+square+img.jpg' + ci-image-name= 'Church+square+img.jpg' />

@@ -240,7 +240,7 @@

Responsive images, in real-time!

ci-params="w=265&h=265&gravity=face&radius=max" id="right-column-second-image" ci-gallery="gallery2" - ci-gallery-img-name = 'Girl+img.jpg' + ci-image-name= 'Girl+img.jpg' />
@@ -259,7 +259,7 @@

Responsive images, in real-time!

ci-ratio="2.885" ci-params="ci_info=2" ci-gallery="gallery2" - ci-gallery-img-name = 'Dresses+img.jpg' + ci-image-name = 'Dresses+img.jpg' />

diff --git a/src/common/ci.utils.js b/src/common/ci.utils.js index 71bafb5..e044c99 100644 --- a/src/common/ci.utils.js +++ b/src/common/ci.utils.js @@ -73,7 +73,7 @@ const getCommonImageProps = (image) => ({ alt: attr(image, 'alt'), zoom: attr(image, 'ci-zoom') || undefined, gallery: attr(image, 'ci-gallery') || undefined, - galleryImgName: attr(image, 'ci-gallery-img-name') || undefined, + imageName: attr(image, 'ci-image-name') || undefined, }); const filterImages = (images, type) => { diff --git a/src/low-preview/ci.service.js b/src/low-preview/ci.service.js index 673f6f6..02c86d3 100644 --- a/src/low-preview/ci.service.js +++ b/src/low-preview/ci.service.js @@ -22,8 +22,6 @@ import { import { destroyGallery, createGalleryModal, - appendGalleryImageName, - destroyGalleryImageName, handleHoveringWrapper, handleUnHoveringWrapper, getGalleryPreviewModule, @@ -35,6 +33,7 @@ import { getGalleryImages, getZoomImages, getDimAndFit, + updateOrCreateImageNameWrapper, } from './gallery.utils'; import { getInitialConfigLowPreview } from './ci.config'; import { @@ -74,20 +73,24 @@ export default class CIResponsive { this.process(); } - handleArrowEvents(galleryImages, isGallery, event) { - const galleryModal = document.querySelector('.ci-gallery-modal'); + handleModalKeydown(galleryImages, event) { + const { keyCode } = event; + const leftKeyCodes = [37, 40]; // left and down + const rightKeyCodes = [39, 38]; // right and up - if (event.key === 'ArrowRight' && isGallery) { - this.handleClickArrows(galleryImages, 'right'); - } - if (event.key === 'ArrowLeft' && isGallery) { - this.handleClickArrows(galleryImages, 'left'); - } - if (event.key === 'Escape') { - destroyGallery(galleryModal); + if (galleryImages) { + if (leftKeyCodes.includes(keyCode)) { + this.handleClickArrows(galleryImages, 'left'); + } + + if (rightKeyCodes.includes(keyCode)) { + this.handleClickArrows(galleryImages, 'right'); + } } - event.preventDefault(); + if (keyCode === 27) { // esc + destroyGallery(); + } } onUpdateDimensions() { @@ -206,13 +209,13 @@ export default class CIResponsive { if (leftDirection) { nextIndex -= 1; - if (nextIndex < 0) { // left button + if (nextIndex < 0) { // reached left-end nextIndex = length - 1; } } else { nextIndex += 1; - if (nextIndex === Number(length)) { // right button + if (nextIndex === length) { // reached right-end nextIndex = 0; } } @@ -271,30 +274,24 @@ export default class CIResponsive { processGalleryPreviewImage(imgNode, imageIndex, direction, intial) { const { imgSelector } = this.config; - const { galleryImgName } = getImageProps(imgNode, imgSelector); - + const { imageName, alt } = getImageProps(imgNode, imgSelector); const galleryModal = document.querySelector('.ci-gallery-modal'); const _imgNode = imgNode.cloneNode(); const adaptedImageNode = removeClassNames(_imgNode, loadedImageClassNames); const previewModule = getGalleryPreviewModule(); + const _imageName = imageName || alt || generateAlt(alt); - if (!intial) { - this.animatePreviewModule(previewModule, imageIndex, direction); - } - - destroyGalleryImageName(); - - if (galleryImgName) { - const imageName = appendGalleryImageName(galleryImgName); - galleryModal.append(imageName); - } - + updateOrCreateImageNameWrapper(_imageName, galleryModal); adaptedImageNode.style = {}; adaptedImageNode.setAttribute('data-ci-processed-gallery', true); previewModule.setAttribute('data-ci-active-image-index', imageIndex); previewModule.innerHTML = ''; previewModule.appendChild(adaptedImageNode); + if (!intial) { + this.animatePreviewModule(previewModule, imageIndex, direction); + } + this.getBasicInfo(adaptedImageNode, false, false, 'image', undefined, true); } @@ -314,6 +311,7 @@ export default class CIResponsive { gallery, zoom, isProcessedByGallery, } = imgProps; + if (isProcessedByGallery) return; if (gallery && images) { @@ -321,7 +319,7 @@ export default class CIResponsive { const galleryImages = getGalleryImages(images, gallery); const clickedImageIndex = galleryImages.indexOf(clickedImage); - const galleryModal = createGalleryModal(galleryImages.length, closeIconSvg, true); + const galleryModal = createGalleryModal(closeIconSvg, galleryImages.length, true); const previewModule = galleryModal.querySelector('.ci-gallery-preview-module'); const thumbnailsModule = createThmbnailsModule( clickedImage, @@ -339,10 +337,10 @@ export default class CIResponsive { galleryModal.appendChild(previewModule); galleryModal.appendChild(thumbnailsModule); galleryModal.append(...galleryArrows); - document.body.appendChild(galleryModal); - - document.onkeydown = this.handleArrowEvents.bind(this, galleryImages, true); + galleryModal.onkeydown = debounce(100, this.handleModalKeydown.bind(this, galleryImages)); + document.body.appendChild(galleryModal); + galleryModal.focus(); this.processGalleryPreviewImage(galleryImages[clickedImageIndex], clickedImageIndex, undefined, true); setGalleryIndex(clickedImageIndex); } @@ -352,15 +350,14 @@ export default class CIResponsive { const zoomImages = getZoomImages(images); const clickedImageIndex = zoomImages.indexOf(clickedImage); - const galleryModal = createGalleryModal(0, closeIconSvg, false); + const galleryModal = createGalleryModal(closeIconSvg); const previewModule = galleryModal.querySelector('.ci-gallery-preview-module'); + galleryModal.tabIndex = 0; galleryModal.appendChild(previewModule); + galleryModal.onkeydown = debounce(100, this.handleModalKeydown.bind(this)); document.body.appendChild(galleryModal); - - document.onkeydown = this.handleArrowEvents.bind(this, images, false); - this.processZoomPreviewImage(zoomImages[clickedImageIndex]); } } diff --git a/src/low-preview/ci.styles.css b/src/low-preview/ci.styles.css index 9ec1cfc..911c381 100644 --- a/src/low-preview/ci.styles.css +++ b/src/low-preview/ci.styles.css @@ -68,6 +68,11 @@ img.ci-image-loaded { z-index: 200; } +.ci-gallery-modal:focus, +.ci-gallery-modal:focus-within { + outline: 0; +} + .ci-gallery-preview-module { height: 100%; position: relative; @@ -92,14 +97,17 @@ img.ci-image-loaded { } .ci-gallery-image-name { - color: white; - padding: 10px; - width: fit-content; - height: fit-content; position: absolute; - top: 0; - left: 0; - z-index: 2; + top: 15px; + left: 15px; + color: #FFF; + text-shadow: -1px -1px 0 #444, 1px -1px 0 #444, -1px 1px 0 #444, 1px 1px 0 #444; + padding: 0; + font-size: 1.2em; + max-width: 60vw; + text-overflow: ellipsis; + vertical-align: middle; + overflow: hidden; } .ci-gallery-close-button { @@ -187,14 +195,3 @@ img.ci-image-loaded { 0% { transform: translateX(-2000px);} 100% {transform: translateX(0);} } - -@keyframes fadeIn { - 0% { opacity: 0; } - 100% { opacity: 1; } -} - -@keyframes fadeOut { - 0% { opacity: 1; } - 100% { opacity: 0; } -} - diff --git a/src/low-preview/gallery.utils.js b/src/low-preview/gallery.utils.js index d9e1b99..e065030 100644 --- a/src/low-preview/gallery.utils.js +++ b/src/low-preview/gallery.utils.js @@ -1,4 +1,4 @@ -import { getCommonImageProps, swapArrayPositions } from '../common/ci.utils'; +import { getCommonImageProps, swapArrayPositions, addClass } from '../common/ci.utils'; import { previewContainer } from '../common/ci.constants'; @@ -25,25 +25,27 @@ const createIcon = (iconSrc, className, iconStyles) => { return iconWrapper; }; -const destroyGallery = (galleryModal) => { - galleryModal.style.animation = 'fadeOut 0.7s'; +const destroyGallery = () => { + const galleryModal = document.querySelector('.ci-gallery-modal'); - setTimeout(() => { - if (galleryModal && galleryModal.parentNode) { - galleryModal.parentNode.removeChild(galleryModal); - } - }, 600); + if (galleryModal) { + galleryModal.parentElement.removeChild(galleryModal); + } }; -const createGalleryModal = (galleryLength, closeIconSrc, isGallery) => { +const createGalleryModal = (closeIconSrc, galleryLength, isGallery) => { const iconStyles = { color: 'rgba(255,255,255,0.5)' }; - const galleryModal = document.createElement('div'); const previewModule = document.createElement('div'); const closeIcon = createIcon(closeIconSrc, 'ci-gallery-close-button', iconStyles); + galleryModal.tabIndex = 0; galleryModal.classList.add('ci-gallery-modal'); previewModule.classList.add('ci-gallery-preview-module'); + galleryModal.setAttribute('data-ci-gallery', true); + galleryModal.append(previewModule); + galleryModal.append(closeIcon); + closeIcon.onclick = destroyGallery; if (isGallery) { const thumbnailsModule = document.createElement('div'); @@ -53,28 +55,25 @@ const createGalleryModal = (galleryLength, closeIconSrc, isGallery) => { galleryModal.append(thumbnailsModule); } - galleryModal.setAttribute('data-ci-gallery', true); - galleryModal.append(previewModule); - galleryModal.append(closeIcon); - - closeIcon.onclick = destroyGallery.bind(this, galleryModal); - return galleryModal; }; -const appendGalleryImageName = (imageName) => { - const galleryImageName = document.createElement('div'); - galleryImageName.classList.add('ci-gallery-image-name'); - galleryImageName.innerText = imageName; +const createImageNameWrapper = (imageName, galleryModal) => { + const imageNameContainer = document.createElement('p'); + + imageNameContainer.innerHTML = imageName; + addClass(imageNameContainer, 'ci-gallery-image-name'); - return galleryImageName; + galleryModal.appendChild(imageNameContainer); }; -const destroyGalleryImageName = () => { - const galleryImageName = document.querySelector('.ci-gallery-image-name'); +const updateOrCreateImageNameWrapper = (imageName, galleryModal) => { + const imageNameContainer = galleryModal.querySelector('.ci-gallery-image-name'); - if (galleryImageName) { - galleryImageName.parentNode.removeChild(galleryImageName); + if (imageNameContainer) { + imageNameContainer.innerHTML = imageName; + } else { + createImageNameWrapper(imageName, galleryModal); } }; @@ -88,8 +87,6 @@ const handleHoveringWrapper = (wrapper, imgProps, zoomIconSrc) => { const iconStyles = { width: 35, height: 35 }; const zoomIcon = createIcon(zoomIconSrc, 'ci-gallery-zoom-button', iconStyles); - zoomIcon.style.animation = 'fadeIn 0.3s'; - wrapper.append(zoomIcon); } @@ -169,7 +166,7 @@ const getGalleryLengthAndIndex = () => { const galleryLength = galleryModal.getAttribute('data-ci-gallery-length'); const galleryIndex = galleryModal.getAttribute('data-ci-gallery-index'); - return [galleryLength, galleryIndex]; + return [+galleryLength, galleryIndex]; }; const removeClassNames = (node, classNames) => { @@ -232,7 +229,7 @@ const getGalleryImages = (images, galleryName) => [...images].filter((image) => const getZoomImages = (images) => [...images].filter((image) => { const { zoom } = getCommonImageProps(image); - return zoom === 'true'; + return zoom; }); const getImageFitStyles = (naturalWidth, naturalHeight) => { @@ -299,10 +296,9 @@ const getDimAndFit = (imgNode) => { }; export { - destroyGallery, createGalleryModal, - appendGalleryImageName, - destroyGalleryImageName, + destroyGallery, + updateOrCreateImageNameWrapper, handleHoveringWrapper, handleUnHoveringWrapper, getGalleryPreviewModule, From dea05d015fe536b780f8479f60de1da218be0db3 Mon Sep 17 00:00:00 2001 From: Amr Wagdy Date: Tue, 6 Sep 2022 18:17:48 +0200 Subject: [PATCH 21/45] Refactor: Code review --- src/low-preview/ci.styles.css | 1 + 1 file changed, 1 insertion(+) diff --git a/src/low-preview/ci.styles.css b/src/low-preview/ci.styles.css index 911c381..4cdf314 100644 --- a/src/low-preview/ci.styles.css +++ b/src/low-preview/ci.styles.css @@ -138,6 +138,7 @@ img.ci-image-loaded { } .ci-gallery-thmbnail-container { + display: flex; width: 40px; height: 40px; border: 1px solid #888; From 5d3d43e73b0c0c68bdccb215c427b4039b0493ee Mon Sep 17 00:00:00 2001 From: Amr Wagdy Date: Tue, 6 Sep 2022 18:29:20 +0200 Subject: [PATCH 22/45] Refactor: Code review --- src/low-preview/ci.styles.css | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/low-preview/ci.styles.css b/src/low-preview/ci.styles.css index 4cdf314..1e4f2f5 100644 --- a/src/low-preview/ci.styles.css +++ b/src/low-preview/ci.styles.css @@ -138,13 +138,22 @@ img.ci-image-loaded { } .ci-gallery-thmbnail-container { - display: flex; + position: relative; width: 40px; height: 40px; border: 1px solid #888; cursor: pointer; } +.ci-gallery-thmbnail-container img { + position: absolute; + top: 50%; + left: 50%; + height: auto; + transform: translate(-50%, -50%); +} + + .ci-gallery-right-arrow-button { width: 25px; height: 25px; From fa6a135b8f1dbd0432755be16a7087ee016817b1 Mon Sep 17 00:00:00 2001 From: Nermeen Moustafa Date: Wed, 7 Sep 2022 17:30:44 +0200 Subject: [PATCH 23/45] handle thmbnails and refactor - T7582 --- src/common/ci.constants.js | 3 -- src/low-preview/ci.service.js | 17 ++++--- src/low-preview/ci.styles.css | 33 +++++++++++- src/low-preview/gallery.utils.js | 86 ++------------------------------ 4 files changed, 46 insertions(+), 93 deletions(-) diff --git a/src/common/ci.constants.js b/src/common/ci.constants.js index cde072f..0f7cc1e 100644 --- a/src/common/ci.constants.js +++ b/src/common/ci.constants.js @@ -6,12 +6,9 @@ const canvasAttr = 'data-ci-canvas'; const loadedImageClassNames = ['ci-image-loaded', 'lazyloaded', 'ci-image']; -const previewContainer = 'ci-gallery-preview-module'; - export { processedAttr, bgContentAttr, canvasAttr, loadedImageClassNames, - previewContainer, }; diff --git a/src/low-preview/ci.service.js b/src/low-preview/ci.service.js index 02c86d3..9be3f01 100644 --- a/src/low-preview/ci.service.js +++ b/src/low-preview/ci.service.js @@ -20,10 +20,9 @@ import { setSrcset, } from '../common/ci.utils'; import { + createIcon, destroyGallery, createGalleryModal, - handleHoveringWrapper, - handleUnHoveringWrapper, getGalleryPreviewModule, setGalleryIndex, createGalleryArrows, @@ -424,10 +423,17 @@ export default class CIResponsive { getDimAndFit(imgNode); - - if ((zoom || gallery) && !isProcessedByGallery) { + if ((gallery || zoom) && !isProcessedByGallery) { wrapper.style.cursor = 'pointer'; wrapper.onclick = this.handleClickWrapper.bind(this, imgProps, images); + + if (gallery) { + wrapper.classList.add('ci-gallery-animation', 'ci-gallery-transition'); + } else { + const iconStyles = { width: 35, height: 35 }; + const zoomIcon = createIcon(zoomIconSvg, 'ci-gallery-zoom-button', iconStyles); + wrapper.append(zoomIcon); + } } imgNode.onload = () => { @@ -435,9 +441,6 @@ export default class CIResponsive { config.onImageLoad(imgNode); } - wrapper.onmouseenter = handleHoveringWrapper.bind(this, wrapper, imgProps, zoomIconSvg); - wrapper.onmouseout = handleUnHoveringWrapper.bind(this, wrapper, imgProps); - if (!isProcessedByGallery) { onImageLoad(wrapper, previewImgNode, imgNode, ratio, preserveSize, isAdaptive); } diff --git a/src/low-preview/ci.styles.css b/src/low-preview/ci.styles.css index 1e4f2f5..565a5cd 100644 --- a/src/low-preview/ci.styles.css +++ b/src/low-preview/ci.styles.css @@ -130,6 +130,8 @@ img.ci-image-loaded { align-items: center; justify-content: center; column-gap: 5px; + overflow: auto hidden; + white-space: nowrap; width: 100%; height: 40px; bottom: 10px; @@ -137,14 +139,39 @@ img.ci-image-loaded { animation: slideHorizontal 400ms cubic-bezier(0.075, 0.42, 0.165, 1); } +.ci-gallery-thumbnail-module::-webkit-scrollbar { + height: 5px; +} + +.ci-gallery-thumbnail-module::-webkit-scrollbar-track { + background-color: rgb(129, 126, 126); + border-radius: 100px; +} + +.ci-gallery-thumbnail-module::-webkit-scrollbar-thumb { + box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3); + border-radius: 100px; +} + .ci-gallery-thmbnail-container { position: relative; width: 40px; height: 40px; + min-width: 40px; + min-height: 40px; border: 1px solid #888; cursor: pointer; } +.ci-gallery-animation:hover { + transform: scale(1.05); + z-index: 2; +} + +.ci-gallery-transition { + transition: transform 0.5s ease; +} + .ci-gallery-thmbnail-container img { position: absolute; top: 50%; @@ -153,7 +180,6 @@ img.ci-image-loaded { transform: translate(-50%, -50%); } - .ci-gallery-right-arrow-button { width: 25px; height: 25px; @@ -183,7 +209,12 @@ img.ci-image-loaded { cursor: pointer; } +.ci-image-wrapper:hover > .ci-gallery-zoom-button { + visibility: visible; +} + .ci-gallery-zoom-button { + visibility: hidden; width: 100%; height: 100%; cursor: pointer; diff --git a/src/low-preview/gallery.utils.js b/src/low-preview/gallery.utils.js index e065030..0f6b8ea 100644 --- a/src/low-preview/gallery.utils.js +++ b/src/low-preview/gallery.utils.js @@ -1,5 +1,4 @@ import { getCommonImageProps, swapArrayPositions, addClass } from '../common/ci.utils'; -import { previewContainer } from '../common/ci.constants'; const createIcon = (iconSrc, className, iconStyles) => { @@ -77,54 +76,6 @@ const updateOrCreateImageNameWrapper = (imageName, galleryModal) => { } }; -const handleHoveringWrapper = (wrapper, imgProps, zoomIconSrc) => { - const isPreviewWrapper = wrapper.parentNode.className === previewContainer; - - if (!isPreviewWrapper) { - const { zoom, gallery } = imgProps; - - if (zoom && !gallery) { - const iconStyles = { width: 35, height: 35 }; - const zoomIcon = createIcon(zoomIconSrc, 'ci-gallery-zoom-button', iconStyles); - - wrapper.append(zoomIcon); - } - - if (gallery) { - wrapper.style.transform = 'scale(1.05)'; - wrapper.style.transition = 'transform 0.5s ease'; - wrapper.style.zIndex = '2'; - } - } -}; - -const handleUnHoveringWrapper = (wrapper, imgProps) => { - const isPreviewWrapper = wrapper.parentNode.className === previewContainer; - - if (!isPreviewWrapper) { - const { zoom, gallery } = imgProps; - - if (zoom && !gallery) { - const zoomIcon = wrapper.querySelector('.ci-gallery-zoom-button'); - - if (zoomIcon) { - zoomIcon.style.animation = 'fadeOut 0.4s'; - } - - setTimeout(() => { - if (zoomIcon && zoomIcon.parentNode) { - zoomIcon.parentNode.removeChild(zoomIcon); - } - }, 300); - } - - if (gallery) { - wrapper.style.transform = 'scale(1)'; - wrapper.style.zIndex = '1'; - } - } -}; - const getGalleryPreviewModule = () => { const galleryModal = document.body.querySelector('[data-ci-gallery]'); @@ -137,16 +88,6 @@ const setGalleryIndex = (index) => { galleryModal.setAttribute('data-ci-gallery-index', index); }; -const markCurrentImage = (galleryThmbnails, currentIndex) => { - galleryThmbnails.forEach((imgWrapper, index) => { - imgWrapper.querySelector('img').style.border = '1px solid grey'; - - if (index === currentIndex) { - imgWrapper.querySelector('img').style.border = '1px solid white'; - } - }); -}; - const createGalleryArrows = (leftArrowIcon, rightArrowIcon, onClick) => { const iconStyles = { color: 'rgba(255,255,255,0.5)' }; @@ -184,7 +125,9 @@ const adaptGalleryThumbnails = (clickedImage, thumbnails = [], onClick) => { const _thumbnails = swapArrayPositions(thumbnails, indexOfClickedImage, 0); - return _thumbnails.map((thumbnail, index) => { + const loadedThmbnails = _thumbnails.filter((thmbnail) => thmbnail.naturalWidth !== 0); + + return loadedThmbnails.map((thumbnail, index) => { const thmbnailContainer = document.createElement('div'); const image = thumbnail.cloneNode(); @@ -255,25 +198,6 @@ const getImageFitStyles = (naturalWidth, naturalHeight) => { return imageStyles; }; -const getCurrentImage = (mainImageWrapper, galleryModal) => { - const galleryThmbnailsModule = galleryModal.querySelector('.ci-gallery-thumbnail-module'); - const galleryThmbnails = [...galleryThmbnailsModule.children]; - - let currentIndex = null; - - galleryThmbnails.forEach((imgWrapper, index) => { - const mainImg = mainImageWrapper.querySelector('[ci-src]').getAttribute('ci-src'); - const galleryImg = imgWrapper.querySelector('[ci-src]').getAttribute('ci-src'); - - if (mainImg === galleryImg) { - currentIndex = index; - markCurrentImage(galleryThmbnails, index); - } - }); - - return currentIndex; -}; - const galleryPreviewImage = (imgSelector, imgNodeSRC) => { const image = new Image(); @@ -296,11 +220,10 @@ const getDimAndFit = (imgNode) => { }; export { + createIcon, createGalleryModal, destroyGallery, updateOrCreateImageNameWrapper, - handleHoveringWrapper, - handleUnHoveringWrapper, getGalleryPreviewModule, setGalleryIndex, createGalleryArrows, @@ -310,7 +233,6 @@ export { getGalleryImages, getZoomImages, getImageFitStyles, - getCurrentImage, galleryPreviewImage, getDimAndFit, }; From 4f496269251b804ec416a8338ae427738886209f Mon Sep 17 00:00:00 2001 From: Amr Wagdy Date: Wed, 7 Sep 2022 18:59:46 +0200 Subject: [PATCH 24/45] Refactor: Code review T7582 --- src/low-preview/ci.styles.css | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/low-preview/ci.styles.css b/src/low-preview/ci.styles.css index 565a5cd..4f088b7 100644 --- a/src/low-preview/ci.styles.css +++ b/src/low-preview/ci.styles.css @@ -169,7 +169,7 @@ img.ci-image-loaded { } .ci-gallery-transition { - transition: transform 0.5s ease; + transition: transform 0.5s ease-in-out; } .ci-gallery-thmbnail-container img { @@ -210,11 +210,10 @@ img.ci-image-loaded { } .ci-image-wrapper:hover > .ci-gallery-zoom-button { - visibility: visible; + transform: translateY(0); } .ci-gallery-zoom-button { - visibility: hidden; width: 100%; height: 100%; cursor: pointer; @@ -224,7 +223,9 @@ img.ci-image-loaded { display: flex; justify-content: center; align-items: center; + transform: translateY(1000px); background-color: rgba(255, 255, 255, 0.5); + transition: transform 300ms ease-in-out; } @keyframes slideVertical { From 2eaec6d5e1b9621d596cfe4c3474047faa192378 Mon Sep 17 00:00:00 2001 From: Nermeen Moustafa Date: Thu, 8 Sep 2022 11:14:23 +0200 Subject: [PATCH 25/45] refactor - T7582 --- examples/low-preview/src/index.html | 2 +- src/blur-hash/ci.service.js | 10 +++---- src/blur-hash/ci.utils.js | 4 +-- src/common/ci.constants.js | 36 ++++++++++++++++++++----- src/common/ci.utils.js | 15 ++++++----- src/low-preview/ci.service.js | 26 +++++++++--------- src/low-preview/ci.utis.js | 6 ++--- src/low-preview/gallery.utils.js | 42 +++++++++++++---------------- src/plain/ci.service.js | 6 ++--- 9 files changed, 83 insertions(+), 64 deletions(-) diff --git a/examples/low-preview/src/index.html b/examples/low-preview/src/index.html index 65ef36e..c05ce9e 100644 --- a/examples/low-preview/src/index.html +++ b/examples/low-preview/src/index.html @@ -302,7 +302,7 @@

Original Image

ci-src="shayna-douglas-VibRcV8tMDM-unsplash.jpg" ci-ratio="1" alt="car-image" - ci-zoom="true" + ci-zoom />
 <img
diff --git a/src/blur-hash/ci.service.js b/src/blur-hash/ci.service.js
index d05ffd8..6ef89bf 100644
--- a/src/blur-hash/ci.service.js
+++ b/src/blur-hash/ci.service.js
@@ -6,7 +6,7 @@ import { getBreakpoint } from 'cloudimage-responsive-utils/dist/utils/get-breakp
 import { isSupportedInBrowser } from 'cloudimage-responsive-utils/dist/utils/is-supported-in-browser';
 import { debounce } from 'throttle-debounce';
 import { generateAlt } from 'cloudimage-responsive-utils/dist/utils/generate-alt';
-import { canvasAttr, loadedImageClassNames, processedAttr } from '../common/ci.constants';
+import { ATTRIBUTES, loadedImageClassNames } from '../common/ci.constants';
 import {
   destroyNodeImgSize,
   getBackgroundImageProps,
@@ -160,7 +160,7 @@ export default class CIResponsive {
       initImageStyles(imgNode);
       setAlt(imgNode, alt);
 
-      imgNode.setAttribute(processedAttr, true);
+      imgNode.setAttribute(ATTRIBUTES.PROCESSED, true);
 
       if (config.destroyNodeImgSize) {
         destroyNodeImgSize(imgNode);
@@ -194,7 +194,7 @@ export default class CIResponsive {
 
       const canvas = applyOrUpdateBlurHashCanvas(imgNode, blurHash);
 
-      imgNode.setAttribute(processedAttr, true);
+      imgNode.setAttribute(ATTRIBUTES.PROCESSED, true);
 
       if (!lazy) {
         const tempImage = new Image();
@@ -238,7 +238,7 @@ export default class CIResponsive {
     }
 
     if (isBackground) {
-      const isProcessed = node.getAttribute(processedAttr);
+      const isProcessed = node.getAttribute(ATTRIBUTES.PROCESSED);
 
       if (src) {
         node.setAttribute(bgSelector, src);
@@ -247,7 +247,7 @@ export default class CIResponsive {
       if (isProcessed) {
         removeClassNames(node, loadedImageClassNames);
 
-        const canvas = node.querySelector(`[${canvasAttr}]`);
+        const canvas = node.querySelector(`[${ATTRIBUTES.CANVAS}]`);
 
         if (canvas) {
           canvas.parentNode.removeChild(canvas);
diff --git a/src/blur-hash/ci.utils.js b/src/blur-hash/ci.utils.js
index 7083b4f..0c27f3d 100644
--- a/src/blur-hash/ci.utils.js
+++ b/src/blur-hash/ci.utils.js
@@ -1,4 +1,4 @@
-import { canvasAttr } from '../common/ci.constants';
+import { ATTRIBUTES } from '../common/ci.constants';
 import { addClass, getWrapper } from '../common/ci.utils';
 import { decode } from './blurHash';
 
@@ -123,7 +123,7 @@ export const applyOrUpdateBlurHashCanvas = (wrapper, blurHash) => {
   if (!canvas && blurHash) {
     canvas = document.createElement('canvas');
 
-    canvas.setAttribute(canvasAttr, true);
+    canvas.setAttribute(ATTRIBUTES.CANVAS, true);
     const pixels = decode(blurHash, 32, 32);
     canvas.width = 32;
     canvas.height = 32;
diff --git a/src/common/ci.constants.js b/src/common/ci.constants.js
index 0f7cc1e..3516ca4 100644
--- a/src/common/ci.constants.js
+++ b/src/common/ci.constants.js
@@ -1,14 +1,36 @@
-const processedAttr = 'data-ci-processed';
+const loadedImageClassNames = ['ci-image-loaded', 'lazyloaded', 'ci-image'];
 
-const bgContentAttr = 'data-ci-bg-container';
+const ATTRIBUTES = {
+  CANVAS: 'data-ci-canvas',
+  PROCESSED_GALLERY: 'data-ci-processed-gallery',
+  ACTIVE_IMAGE_INDEX: 'data-ci-active-image-index',
+  PROCESSED: 'data-ci-processed',
+  OPTIMIZED_URL: 'ci-optimized-url',
+  BG_CONTAINER: 'data-ci-bg-container',
+  PREVIEW: 'ci-preview',
+  GALLERY: 'data-ci-gallery',
+  GALLERY_LENGTH: 'data-ci-gallery-length',
+  GALLERY_INDEX: 'data-ci-gallery-index',
+};
 
-const canvasAttr = 'data-ci-canvas';
+const CLASSNAMES = {
+  PREVIEW_LOADED: 'ci-preview-loaded',
+  GALLERY_ANIMATION: 'ci-gallery-animation',
+  GALLERY_TRANSITION: 'ci-gallery-transition',
+  GALLERY_MODAL: 'ci-gallery-modal',
+  PREVIEW_MODULE: 'ci-gallery-preview-module',
+  THUMBNAIL_MODULE: 'ci-gallery-thumbnail-module',
+  THUMBNAIL_CONTAINER: 'ci-gallery-thmbnail-container',
+};
 
-const loadedImageClassNames = ['ci-image-loaded', 'lazyloaded', 'ci-image'];
+const ICONS_STYLES = {
+  ZOOM: { width: 35, height: 35 },
+  COLOR: { color: 'rgba(255,255,255,0.5)' },
+};
 
 export {
-  processedAttr,
-  bgContentAttr,
-  canvasAttr,
   loadedImageClassNames,
+  ATTRIBUTES,
+  CLASSNAMES,
+  ICONS_STYLES,
 };
diff --git a/src/common/ci.utils.js b/src/common/ci.utils.js
index e044c99..4c755df 100644
--- a/src/common/ci.utils.js
+++ b/src/common/ci.utils.js
@@ -71,7 +71,7 @@ const getCommonImageProps = (image) => ({
   imgNodeHeight: attr(image, 'height'),
   doNotReplaceImageUrl: isTrue(image, 'ci-do-not-replace-url'),
   alt: attr(image, 'alt'),
-  zoom: attr(image, 'ci-zoom') || undefined,
+  zoom: isTrue(image, 'ci-zoom'),
   gallery: attr(image, 'ci-gallery') || undefined,
   imageName: attr(image, 'ci-image-name') || undefined,
 });
@@ -216,11 +216,14 @@ const setOptions = (node, options) => {
   return node;
 };
 
-const swapArrayPositions = (array = [], a, b) => {
-  const clonedArray = [...array];
-  [clonedArray[a], clonedArray[b]] = [clonedArray[b], clonedArray[a]];
+const removeClassNames = (node, classNames) => {
+  classNames.forEach((className) => {
+    if (node.classList.contains(className)) {
+      node.classList.remove(className);
+    }
+  });
 
-  return clonedArray;
+  return node;
 };
 
 export {
@@ -239,5 +242,5 @@ export {
   destroyNodeImgSize,
   setAlt,
   setOptions,
-  swapArrayPositions,
+  removeClassNames,
 };
diff --git a/src/low-preview/ci.service.js b/src/low-preview/ci.service.js
index 9be3f01..d27ee73 100644
--- a/src/low-preview/ci.service.js
+++ b/src/low-preview/ci.service.js
@@ -18,6 +18,7 @@ import {
   setOptions,
   setSrc,
   setSrcset,
+  removeClassNames,
 } from '../common/ci.utils';
 import {
   createIcon,
@@ -27,7 +28,6 @@ import {
   setGalleryIndex,
   createGalleryArrows,
   getGalleryLengthAndIndex,
-  removeClassNames,
   createThmbnailsModule,
   getGalleryImages,
   getZoomImages,
@@ -48,7 +48,7 @@ import {
   updateSizeWithPixelRatio,
 } from './ci.utis';
 import {
-  bgContentAttr, loadedImageClassNames, processedAttr,
+  loadedImageClassNames, ATTRIBUTES, CLASSNAMES, ICONS_STYLES,
 } from '../common/ci.constants';
 import closeIconSvg from '../public/close-icon.svg';
 import rightArrowSvg from '../public/right-arrow-icon.svg';
@@ -264,7 +264,7 @@ export default class CIResponsive {
     const previewModule = getGalleryPreviewModule();
 
     adaptedImageNode.style = {};
-    adaptedImageNode.setAttribute('data-ci-processed-gallery', true);
+    adaptedImageNode.setAttribute(ATTRIBUTES.PROCESSED_GALLERY, true);
     previewModule.innerHTML = '';
     previewModule.appendChild(adaptedImageNode);
 
@@ -282,8 +282,8 @@ export default class CIResponsive {
 
     updateOrCreateImageNameWrapper(_imageName, galleryModal);
     adaptedImageNode.style = {};
-    adaptedImageNode.setAttribute('data-ci-processed-gallery', true);
-    previewModule.setAttribute('data-ci-active-image-index', imageIndex);
+    adaptedImageNode.setAttribute(ATTRIBUTES.PROCESSED_GALLERY, true);
+    previewModule.setAttribute(ATTRIBUTES.ACTIVE_IMAGE_INDEX, imageIndex);
     previewModule.innerHTML = '';
     previewModule.appendChild(adaptedImageNode);
 
@@ -416,6 +416,7 @@ export default class CIResponsive {
         setSrc(previewImgNode, previewImgURL, 'data-src', lazy, src, isSVG, dataSrcAttr);
 
         previewImgNode.onload = () => {
+          previewImgNode.classList.add(CLASSNAMES.PREVIEW_LOADED);
           onPreviewImageLoad(wrapper, previewImgNode, ratio, preserveSize);
         };
       }
@@ -428,10 +429,9 @@ export default class CIResponsive {
       wrapper.onclick = this.handleClickWrapper.bind(this, imgProps, images);
 
       if (gallery) {
-        wrapper.classList.add('ci-gallery-animation', 'ci-gallery-transition');
+        wrapper.classList.add(CLASSNAMES.GALLERY_ANIMATION, CLASSNAMES.GALLERY_TRANSITION);
       } else {
-        const iconStyles = { width: 35, height: 35 };
-        const zoomIcon = createIcon(zoomIconSvg, 'ci-gallery-zoom-button', iconStyles);
+        const zoomIcon = createIcon(zoomIconSvg, 'ci-gallery-zoom-button', ICONS_STYLES.ZOOM);
         wrapper.append(zoomIcon);
       }
     }
@@ -460,7 +460,7 @@ export default class CIResponsive {
     const { dataSrcAttr } = config;
 
     if (!isUpdate) {
-      imgNode.setAttribute(processedAttr, true);
+      imgNode.setAttribute(ATTRIBUTES.PROCESSED, true);
 
       if (isPreview) {
         const previewImgURL = getPreviewSRC({
@@ -473,7 +473,7 @@ export default class CIResponsive {
         });
 
         if (lazy) {
-          imgNode.setAttribute('ci-optimized-url', cloudimageUrl);
+          imgNode.setAttribute(ATTRIBUTES.OPTIMIZED_URL, cloudimageUrl);
 
           setBackgroundSrc(previewBox, previewImgURL, lazy, src, isSVG, dataSrcAttr);
         } else {
@@ -516,7 +516,7 @@ export default class CIResponsive {
     }
 
     if (isBackground) {
-      const isProcessed = node.getAttribute(processedAttr);
+      const isProcessed = node.getAttribute(ATTRIBUTES.PROCESSED);
       const oldNode = node;
 
       if (src) {
@@ -524,11 +524,11 @@ export default class CIResponsive {
       }
 
       if (isProcessed) {
-        const contentBox = node.querySelector(`[${bgContentAttr}]`);
+        const contentBox = node.querySelector(`[${ATTRIBUTES.BG_CONTAINER}]`);
 
         if (contentBox) {
           const contentBoxInner = contentBox.firstChild;
-          node.removeAttribute(processedAttr);
+          node.removeAttribute(ATTRIBUTES.PROCESSED);
           node.innerHTML = '';
           node.appendChild(contentBoxInner);
         } else {
diff --git a/src/low-preview/ci.utis.js b/src/low-preview/ci.utis.js
index bae8c8d..4033883 100644
--- a/src/low-preview/ci.utis.js
+++ b/src/low-preview/ci.utis.js
@@ -1,4 +1,4 @@
-import { bgContentAttr } from '../common/ci.constants';
+import { ATTRIBUTES } from '../common/ci.constants';
 import { addClass, getWrapper } from '../common/ci.utils';
 
 
@@ -39,10 +39,10 @@ export const applyBackgroundStyles = ({
   imgNode.style.position = 'relative';
 
   contentBox.style.position = 'relative';
-  contentBox.setAttribute(bgContentAttr, true);
+  contentBox.setAttribute(ATTRIBUTES.BG_CONTAINER, true);
 
   previewBox.className = `${imgNode.className}${lazy ? ' lazyload' : ''}`;
-  previewBox.setAttribute('ci-preview', true);
+  previewBox.setAttribute(ATTRIBUTES.PREVIEW, true);
   previewBox.style.background = 'inherit';
   previewBox.style.position = 'absolute';
   previewBox.style.left = '0';
diff --git a/src/low-preview/gallery.utils.js b/src/low-preview/gallery.utils.js
index 0f6b8ea..3259c7a 100644
--- a/src/low-preview/gallery.utils.js
+++ b/src/low-preview/gallery.utils.js
@@ -1,4 +1,5 @@
-import { getCommonImageProps, swapArrayPositions, addClass } from '../common/ci.utils';
+import { getCommonImageProps, addClass } from '../common/ci.utils';
+import { ATTRIBUTES, CLASSNAMES, ICONS_STYLES } from '../common/ci.constants';
 
 
 const createIcon = (iconSrc, className, iconStyles) => {
@@ -33,24 +34,23 @@ const destroyGallery = () => {
 };
 
 const createGalleryModal = (closeIconSrc, galleryLength, isGallery) => {
-  const iconStyles = { color: 'rgba(255,255,255,0.5)' };
   const galleryModal = document.createElement('div');
   const previewModule = document.createElement('div');
-  const closeIcon = createIcon(closeIconSrc, 'ci-gallery-close-button', iconStyles);
+  const closeIcon = createIcon(closeIconSrc, 'ci-gallery-close-button', ICONS_STYLES.COLOR);
 
   galleryModal.tabIndex = 0;
-  galleryModal.classList.add('ci-gallery-modal');
-  previewModule.classList.add('ci-gallery-preview-module');
-  galleryModal.setAttribute('data-ci-gallery', true);
+  galleryModal.classList.add(CLASSNAMES.GALLERY_MODAL);
+  previewModule.classList.add(CLASSNAMES.PREVIEW_MODULE);
+  galleryModal.setAttribute(ATTRIBUTES.GALLERY, true);
   galleryModal.append(previewModule);
   galleryModal.append(closeIcon);
   closeIcon.onclick = destroyGallery;
 
   if (isGallery) {
     const thumbnailsModule = document.createElement('div');
-    thumbnailsModule.classList.add('ci-gallery-thumbnail-module');
-    galleryModal.setAttribute('data-ci-gallery-length', galleryLength);
-    galleryModal.setAttribute('data-ci-gallery-index', 0);
+    thumbnailsModule.classList.add(CLASSNAMES.THUMBNAIL_MODULE);
+    galleryModal.setAttribute(ATTRIBUTES.GALLERY_LENGTH, galleryLength);
+    galleryModal.setAttribute(ATTRIBUTES.GALLERY_INDEX, 0);
     galleryModal.append(thumbnailsModule);
   }
 
@@ -85,14 +85,12 @@ const getGalleryPreviewModule = () => {
 const setGalleryIndex = (index) => {
   const galleryModal = document.body.querySelector('[data-ci-gallery]');
 
-  galleryModal.setAttribute('data-ci-gallery-index', index);
+  galleryModal.setAttribute(ATTRIBUTES.GALLERY_INDEX, index);
 };
 
 const createGalleryArrows = (leftArrowIcon, rightArrowIcon, onClick) => {
-  const iconStyles = { color: 'rgba(255,255,255,0.5)' };
-
-  const leftArrow = createIcon(leftArrowIcon, 'ci-gallery-left-arrow-button', iconStyles);
-  const rightArrow = createIcon(rightArrowIcon, 'ci-gallery-right-arrow-button', iconStyles);
+  const leftArrow = createIcon(leftArrowIcon, 'ci-gallery-left-arrow-button', ICONS_STYLES.COLOR);
+  const rightArrow = createIcon(rightArrowIcon, 'ci-gallery-right-arrow-button', ICONS_STYLES.COLOR);
 
   if (onClick) {
     leftArrow.onclick = onClick.bind(this, 'left');
@@ -110,14 +108,11 @@ const getGalleryLengthAndIndex = () => {
   return [+galleryLength, galleryIndex];
 };
 
-const removeClassNames = (node, classNames) => {
-  classNames.forEach((className) => {
-    if (node.classList.contains(className)) {
-      node.classList.remove(className);
-    }
-  });
+const swapArrayPositions = (array = [], a, b) => {
+  const clonedArray = [...array];
+  [clonedArray[a], clonedArray[b]] = [clonedArray[b], clonedArray[a]];
 
-  return node;
+  return clonedArray;
 };
 
 const adaptGalleryThumbnails = (clickedImage, thumbnails = [], onClick) => {
@@ -136,8 +131,8 @@ const adaptGalleryThumbnails = (clickedImage, thumbnails = [], onClick) => {
     image.style.width = '100%';
     image.style.height = '100%';
 
-    thmbnailContainer.classList.add('ci-gallery-thmbnail-container');
-    thmbnailContainer.setAttribute('data-ci-gallery-index', index);
+    thmbnailContainer.classList.add(CLASSNAMES.THUMBNAIL_CONTAINER);
+    thmbnailContainer.setAttribute(ATTRIBUTES.GALLERY_INDEX, index);
     thmbnailContainer.append(image);
 
     if (onClick) {
@@ -228,7 +223,6 @@ export {
   setGalleryIndex,
   createGalleryArrows,
   getGalleryLengthAndIndex,
-  removeClassNames,
   createThmbnailsModule,
   getGalleryImages,
   getZoomImages,
diff --git a/src/plain/ci.service.js b/src/plain/ci.service.js
index 5f0ed40..e23defd 100644
--- a/src/plain/ci.service.js
+++ b/src/plain/ci.service.js
@@ -21,7 +21,7 @@ import {
   setSrc,
   setSrcset,
 } from '../common/ci.utils';
-import { loadedImageClassNames, processedAttr } from '../common/ci.constants';
+import { loadedImageClassNames, ATTRIBUTES } from '../common/ci.constants';
 
 
 export default class CIResponsive {
@@ -175,7 +175,7 @@ export default class CIResponsive {
 
     if (!isUpdate) {
       imgNode.className = `${imgNode.className}${lazy ? ' lazyload' : ''}`;
-      imgNode.setAttribute(processedAttr, true);
+      imgNode.setAttribute(ATTRIBUTES.PROCESSED, true);
     }
 
     setBackgroundSrc(imgNode, cloudimageUrl, lazy, src, isSVG, dataSrcAttr);
@@ -208,7 +208,7 @@ export default class CIResponsive {
     }
 
     if (isBackground) {
-      const isProcessed = node.getAttribute(processedAttr);
+      const isProcessed = node.getAttribute(ATTRIBUTES.PROCESSED);
 
       if (src) {
         node.setAttribute(bgSelector, src);

From 60cb873ade809df0914811bf66da3d79f20215b3 Mon Sep 17 00:00:00 2001
From: Nermeen Moustafa 
Date: Thu, 8 Sep 2022 12:35:49 +0200
Subject: [PATCH 26/45] fix thumbnail images - T7582

---
 src/low-preview/ci.service.js    | 1 -
 src/low-preview/gallery.utils.js | 4 +++-
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/src/low-preview/ci.service.js b/src/low-preview/ci.service.js
index d27ee73..62e0dd9 100644
--- a/src/low-preview/ci.service.js
+++ b/src/low-preview/ci.service.js
@@ -310,7 +310,6 @@ export default class CIResponsive {
       gallery, zoom, isProcessedByGallery,
     } = imgProps;
 
-
     if (isProcessedByGallery) return;
 
     if (gallery && images) {
diff --git a/src/low-preview/gallery.utils.js b/src/low-preview/gallery.utils.js
index 3259c7a..3aa3684 100644
--- a/src/low-preview/gallery.utils.js
+++ b/src/low-preview/gallery.utils.js
@@ -120,7 +120,9 @@ const adaptGalleryThumbnails = (clickedImage, thumbnails = [], onClick) => {
 
   const _thumbnails = swapArrayPositions(thumbnails, indexOfClickedImage, 0);
 
-  const loadedThmbnails = _thumbnails.filter((thmbnail) => thmbnail.naturalWidth !== 0);
+  const thumbnailImages = _thumbnails.map((thmbnail) => thmbnail.parentElement.querySelector('img'));
+
+  const loadedThmbnails = thumbnailImages.filter((thmbnail) => thmbnail.classList.contains(CLASSNAMES.PREVIEW_LOADED));
 
   return loadedThmbnails.map((thumbnail, index) => {
     const thmbnailContainer = document.createElement('div');

From fb7c9ae3084bc376528d74f7691d569c6d5b82e0 Mon Sep 17 00:00:00 2001
From: Nermeen Moustafa 
Date: Thu, 8 Sep 2022 13:26:10 +0200
Subject: [PATCH 27/45] handle unloaded thumbnails - T7582

---
 src/low-preview/ci.service.js | 27 ++++++++++++++++++++++-----
 1 file changed, 22 insertions(+), 5 deletions(-)

diff --git a/src/low-preview/ci.service.js b/src/low-preview/ci.service.js
index 62e0dd9..794c05f 100644
--- a/src/low-preview/ci.service.js
+++ b/src/low-preview/ci.service.js
@@ -199,11 +199,14 @@ export default class CIResponsive {
     }
   };
 
-  handleClickArrows(galleryImages, direction) {
-    let nextIndex = 0;
+  getImageIndex(currentIndex, direction, isLoaded = true) {
+    let nextIndex = currentIndex;
     const [length, index] = getGalleryLengthAndIndex();
     const leftDirection = direction === 'left';
-    nextIndex = +index;
+
+    if (isLoaded) {
+      nextIndex = +index;
+    }
 
     if (leftDirection) {
       nextIndex -= 1;
@@ -219,8 +222,22 @@ export default class CIResponsive {
       }
     }
 
-    this.processGalleryPreviewImage(galleryImages[nextIndex], nextIndex, direction);
-    setGalleryIndex(nextIndex);
+    return nextIndex;
+  }
+
+  handleClickArrows(galleryImages, direction) {
+    let index = 0;
+    index = this.getImageIndex(0, direction);
+
+    const previewImage = galleryImages[index].previousSibling.firstChild;
+    const isLoaded = previewImage.classList.contains(CLASSNAMES.PREVIEW_LOADED);
+
+    if (!isLoaded) {
+      index = this.getImageIndex(index, direction, false);
+    }
+
+    this.processGalleryPreviewImage(galleryImages[index], index, direction);
+    setGalleryIndex(index);
   }
 
   animatePreviewModule(previewModule, nextIndex, direction) {

From e60cdedf6df6cf78a5f4fbaa6f3efac285b6933c Mon Sep 17 00:00:00 2001
From: Amr Wagdy 
Date: Thu, 8 Sep 2022 18:57:35 +0200
Subject: [PATCH 28/45] Refactor: Code review - T7582

---
 src/low-preview/gallery.utils.js | 18 ++++++++----------
 1 file changed, 8 insertions(+), 10 deletions(-)

diff --git a/src/low-preview/gallery.utils.js b/src/low-preview/gallery.utils.js
index 3aa3684..30f93ce 100644
--- a/src/low-preview/gallery.utils.js
+++ b/src/low-preview/gallery.utils.js
@@ -115,16 +115,14 @@ const swapArrayPositions = (array = [], a, b) => {
   return clonedArray;
 };
 
-const adaptGalleryThumbnails = (clickedImage, thumbnails = [], onClick) => {
-  const indexOfClickedImage = thumbnails.indexOf(clickedImage);
-
-  const _thumbnails = swapArrayPositions(thumbnails, indexOfClickedImage, 0);
-
-  const thumbnailImages = _thumbnails.map((thmbnail) => thmbnail.parentElement.querySelector('img'));
-
-  const loadedThmbnails = thumbnailImages.filter((thmbnail) => thmbnail.classList.contains(CLASSNAMES.PREVIEW_LOADED));
-
-  return loadedThmbnails.map((thumbnail, index) => {
+const adaptGalleryThumbnails = (clickedImage, images = [], onClick) => {
+  const indexOfClickedImage = images.indexOf(clickedImage);
+  const lowPreviewImages = images
+    .map((image) => image.previousSibling.firstChild)
+    .filter((thmbnail) => thmbnail.classList.contains(CLASSNAMES.PREVIEW_LOADED));
+  const _thumbnails = swapArrayPositions(lowPreviewImages, indexOfClickedImage, 0);
+
+  return _thumbnails.map((thumbnail, index) => {
     const thmbnailContainer = document.createElement('div');
     const image = thumbnail.cloneNode();
 

From 9753100790a9e47da978b77ba52e92170ef317c4 Mon Sep 17 00:00:00 2001
From: Amr Wagdy 
Date: Fri, 9 Sep 2022 15:00:32 +0200
Subject: [PATCH 29/45] Refactor: Code review T7582

---
 src/common/ci.constants.js       |  1 +
 src/low-preview/ci.service.js    | 26 +++++++++++++++-----------
 src/low-preview/gallery.utils.js | 21 ++++++++++-----------
 3 files changed, 26 insertions(+), 22 deletions(-)

diff --git a/src/common/ci.constants.js b/src/common/ci.constants.js
index 3516ca4..a238766 100644
--- a/src/common/ci.constants.js
+++ b/src/common/ci.constants.js
@@ -21,6 +21,7 @@ const CLASSNAMES = {
   PREVIEW_MODULE: 'ci-gallery-preview-module',
   THUMBNAIL_MODULE: 'ci-gallery-thumbnail-module',
   THUMBNAIL_CONTAINER: 'ci-gallery-thmbnail-container',
+  IMAGE_LOADED: 'ci-image-loaded',
 };
 
 const ICONS_STYLES = {
diff --git a/src/low-preview/ci.service.js b/src/low-preview/ci.service.js
index 794c05f..fdf3a4c 100644
--- a/src/low-preview/ci.service.js
+++ b/src/low-preview/ci.service.js
@@ -33,6 +33,7 @@ import {
   getZoomImages,
   getDimAndFit,
   updateOrCreateImageNameWrapper,
+  swapArrayPositions,
 } from './gallery.utils';
 import { getInitialConfigLowPreview } from './ci.config';
 import {
@@ -331,32 +332,31 @@ export default class CIResponsive {
 
     if (gallery && images) {
       const clickedImage = event.currentTarget.lastChild;
-      const galleryImages = getGalleryImages(images, gallery);
-      const clickedImageIndex = galleryImages.indexOf(clickedImage);
-
-      const galleryModal = createGalleryModal(closeIconSvg, galleryImages.length, true);
+      const clickedImageIndex = images.indexOf(clickedImage);
+      const orderedImages = swapArrayPositions(images, clickedImageIndex, 0)
+        .filter((image) => image.classList.contains(CLASSNAMES.IMAGE_LOADED));
+      const galleryModal = createGalleryModal(closeIconSvg, orderedImages.length, true);
       const previewModule = galleryModal.querySelector('.ci-gallery-preview-module');
       const thumbnailsModule = createThmbnailsModule(
-        clickedImage,
-        galleryImages,
+        orderedImages,
         galleryModal,
-        this.handleClickThumbnail.bind(this, galleryImages),
+        this.handleClickThumbnail.bind(this, orderedImages),
       );
 
       const galleryArrows = createGalleryArrows(
         leftArrowSvg,
         rightArrowSvg,
-        this.handleClickArrows.bind(this, galleryImages),
+        this.handleClickArrows.bind(this, orderedImages),
       );
 
       galleryModal.appendChild(previewModule);
       galleryModal.appendChild(thumbnailsModule);
       galleryModal.append(...galleryArrows);
-      galleryModal.onkeydown = debounce(100, this.handleModalKeydown.bind(this, galleryImages));
+      galleryModal.onkeydown = debounce(100, this.handleModalKeydown.bind(this, orderedImages));
 
       document.body.appendChild(galleryModal);
       galleryModal.focus();
-      this.processGalleryPreviewImage(galleryImages[clickedImageIndex], clickedImageIndex, undefined, true);
+      this.processGalleryPreviewImage(images[clickedImageIndex], clickedImageIndex, undefined, true);
       setGalleryIndex(clickedImageIndex);
     }
 
@@ -402,6 +402,7 @@ export default class CIResponsive {
     const { width, ratio } = containerProps;
     const { config } = this;
     const { dataSrcAttr, placeholderBackground } = config;
+
     const { wrapper, previewImgNode, previewWrapper } = applyOrUpdateWrapper({
       isUpdate,
       imgNode,
@@ -441,8 +442,11 @@ export default class CIResponsive {
     getDimAndFit(imgNode);
 
     if ((gallery || zoom) && !isProcessedByGallery) {
+      const galleryImages = getGalleryImages(images, gallery);
+
       wrapper.style.cursor = 'pointer';
-      wrapper.onclick = this.handleClickWrapper.bind(this, imgProps, images);
+
+      wrapper.onclick = this.handleClickWrapper.bind(this, imgProps, galleryImages);
 
       if (gallery) {
         wrapper.classList.add(CLASSNAMES.GALLERY_ANIMATION, CLASSNAMES.GALLERY_TRANSITION);
diff --git a/src/low-preview/gallery.utils.js b/src/low-preview/gallery.utils.js
index 30f93ce..afd29da 100644
--- a/src/low-preview/gallery.utils.js
+++ b/src/low-preview/gallery.utils.js
@@ -108,21 +108,19 @@ const getGalleryLengthAndIndex = () => {
   return [+galleryLength, galleryIndex];
 };
 
-const swapArrayPositions = (array = [], a, b) => {
+const swapArrayPositions = (array = [], a = 0, b = 0) => {
   const clonedArray = [...array];
-  [clonedArray[a], clonedArray[b]] = [clonedArray[b], clonedArray[a]];
+  if (clonedArray[a] && clonedArray[b]) {
+    [clonedArray[a], clonedArray[b]] = [clonedArray[b], clonedArray[a]];
+  }
 
   return clonedArray;
 };
 
-const adaptGalleryThumbnails = (clickedImage, images = [], onClick) => {
-  const indexOfClickedImage = images.indexOf(clickedImage);
-  const lowPreviewImages = images
-    .map((image) => image.previousSibling.firstChild)
-    .filter((thmbnail) => thmbnail.classList.contains(CLASSNAMES.PREVIEW_LOADED));
-  const _thumbnails = swapArrayPositions(lowPreviewImages, indexOfClickedImage, 0);
+const adaptGalleryThumbnails = (images = [], onClick) => {
+  const lowPreviewImages = images.map((image) => image.previousSibling.firstChild);
 
-  return _thumbnails.map((thumbnail, index) => {
+  return lowPreviewImages.map((thumbnail, index) => {
     const thmbnailContainer = document.createElement('div');
     const image = thumbnail.cloneNode();
 
@@ -149,9 +147,9 @@ const appendGalleryThumbnails = (thumbnails = [], thumbnailsContainer) => {
   });
 };
 
-const createThmbnailsModule = (clickedImage, images, galleryModal, onClick) => {
+const createThmbnailsModule = (images, galleryModal, onClick) => {
   const thumbnailsModule = galleryModal.querySelector('.ci-gallery-thumbnail-module');
-  const adaptedGalleryThumbnails = adaptGalleryThumbnails(clickedImage, images, onClick);
+  const adaptedGalleryThumbnails = adaptGalleryThumbnails(images, onClick);
 
   appendGalleryThumbnails(adaptedGalleryThumbnails, thumbnailsModule);
 
@@ -229,4 +227,5 @@ export {
   getImageFitStyles,
   galleryPreviewImage,
   getDimAndFit,
+  swapArrayPositions,
 };

From 4f240e7e62605817f3fcf445d1a31faa444a7fe3 Mon Sep 17 00:00:00 2001
From: Amr Wagdy 
Date: Fri, 9 Sep 2022 15:23:25 +0200
Subject: [PATCH 30/45] Refactor: Code review T7582

---
 src/low-preview/ci.service.js | 31 ++++++++++++++++++-------------
 1 file changed, 18 insertions(+), 13 deletions(-)

diff --git a/src/low-preview/ci.service.js b/src/low-preview/ci.service.js
index fdf3a4c..099905c 100644
--- a/src/low-preview/ci.service.js
+++ b/src/low-preview/ci.service.js
@@ -323,17 +323,18 @@ export default class CIResponsive {
     }
   }
 
-  handleClickWrapper(imgProps, images, event) {
+  createGallery(imgProps, images, event) {
     const {
       gallery, zoom, isProcessedByGallery,
     } = imgProps;
-
     if (isProcessedByGallery) return;
 
-    if (gallery && images) {
+    const galleryImages = getGalleryImages(images, gallery);
+
+    if (gallery && galleryImages) {
       const clickedImage = event.currentTarget.lastChild;
-      const clickedImageIndex = images.indexOf(clickedImage);
-      const orderedImages = swapArrayPositions(images, clickedImageIndex, 0)
+      const clickedImageIndex = galleryImages.indexOf(clickedImage);
+      const orderedImages = swapArrayPositions(galleryImages, clickedImageIndex, 0)
         .filter((image) => image.classList.contains(CLASSNAMES.IMAGE_LOADED));
       const galleryModal = createGalleryModal(closeIconSvg, orderedImages.length, true);
       const previewModule = galleryModal.querySelector('.ci-gallery-preview-module');
@@ -356,7 +357,7 @@ export default class CIResponsive {
 
       document.body.appendChild(galleryModal);
       galleryModal.focus();
-      this.processGalleryPreviewImage(images[clickedImageIndex], clickedImageIndex, undefined, true);
+      this.processGalleryPreviewImage(galleryImages[clickedImageIndex], clickedImageIndex, undefined, true);
       setGalleryIndex(clickedImageIndex);
     }
 
@@ -370,13 +371,22 @@ export default class CIResponsive {
 
       galleryModal.tabIndex = 0;
       galleryModal.appendChild(previewModule);
-      galleryModal.onkeydown = debounce(100, this.handleModalKeydown.bind(this));
+      galleryModal.onkeydown = debounce(100, this.handleModalKeydown.bind(this, undefined));
 
       document.body.appendChild(galleryModal);
+      galleryModal.focus();
       this.processZoomPreviewImage(zoomImages[clickedImageIndex]);
     }
   }
 
+  handleClickWrapper(imgProps, images, event) {
+    const { gallery, zoom, isProcessedByGallery } = imgProps;
+
+    if ((gallery || zoom) && !isProcessedByGallery) {
+      this.createGallery(imgProps, images, event);
+    }
+  }
+
   processImage(props) {
     const {
       imgNode,
@@ -440,14 +450,8 @@ export default class CIResponsive {
     }
 
     getDimAndFit(imgNode);
-
     if ((gallery || zoom) && !isProcessedByGallery) {
-      const galleryImages = getGalleryImages(images, gallery);
-
       wrapper.style.cursor = 'pointer';
-
-      wrapper.onclick = this.handleClickWrapper.bind(this, imgProps, galleryImages);
-
       if (gallery) {
         wrapper.classList.add(CLASSNAMES.GALLERY_ANIMATION, CLASSNAMES.GALLERY_TRANSITION);
       } else {
@@ -456,6 +460,7 @@ export default class CIResponsive {
       }
     }
 
+    wrapper.onclick = this.handleClickWrapper.bind(this, imgProps, images);
     imgNode.onload = () => {
       if (config.onImageLoad && typeof config.onImageLoad === 'function') {
         config.onImageLoad(imgNode);

From 702ab63e3b59cbe824de570f89c580ad102835bf Mon Sep 17 00:00:00 2001
From: Amr Wagdy 
Date: Fri, 9 Sep 2022 16:07:18 +0200
Subject: [PATCH 31/45] Refactor: Code review T7582

---
 src/common/ci.constants.js       |  1 +
 src/low-preview/ci.service.js    |  2 ++
 src/low-preview/ci.styles.css    | 18 ++++++++++++++----
 src/low-preview/gallery.utils.js | 15 +++++++++++++++
 4 files changed, 32 insertions(+), 4 deletions(-)

diff --git a/src/common/ci.constants.js b/src/common/ci.constants.js
index a238766..d7b8c67 100644
--- a/src/common/ci.constants.js
+++ b/src/common/ci.constants.js
@@ -11,6 +11,7 @@ const ATTRIBUTES = {
   GALLERY: 'data-ci-gallery',
   GALLERY_LENGTH: 'data-ci-gallery-length',
   GALLERY_INDEX: 'data-ci-gallery-index',
+  ACTIVE_THUMBNAIL: 'data-active-thumbnail',
 };
 
 const CLASSNAMES = {
diff --git a/src/low-preview/ci.service.js b/src/low-preview/ci.service.js
index 099905c..deeeeca 100644
--- a/src/low-preview/ci.service.js
+++ b/src/low-preview/ci.service.js
@@ -34,6 +34,7 @@ import {
   getDimAndFit,
   updateOrCreateImageNameWrapper,
   swapArrayPositions,
+  toggleActiveThumbnail,
 } from './gallery.utils';
 import { getInitialConfigLowPreview } from './ci.config';
 import {
@@ -299,6 +300,7 @@ export default class CIResponsive {
     const _imageName = imageName || alt || generateAlt(alt);
 
     updateOrCreateImageNameWrapper(_imageName, galleryModal);
+    toggleActiveThumbnail(galleryModal, imageIndex);
     adaptedImageNode.style = {};
     adaptedImageNode.setAttribute(ATTRIBUTES.PROCESSED_GALLERY, true);
     previewModule.setAttribute(ATTRIBUTES.ACTIVE_IMAGE_INDEX, imageIndex);
diff --git a/src/low-preview/ci.styles.css b/src/low-preview/ci.styles.css
index 4f088b7..8a50f5b 100644
--- a/src/low-preview/ci.styles.css
+++ b/src/low-preview/ci.styles.css
@@ -180,6 +180,11 @@ img.ci-image-loaded {
   transform: translate(-50%, -50%);
 }
 
+.ci-gallery-thmbnail-container[data-active-thumbnail] {
+  border: 2px solid #FFF;
+  animation: borderFade 250ms ease-in-out;
+}
+
 .ci-gallery-right-arrow-button {
   width: 25px;
   height: 25px;
@@ -229,11 +234,16 @@ img.ci-image-loaded {
 }
 
 @keyframes slideVertical {
-  0% { transform: translateY(1000px);}
-  100% {transform: translateY(0);}
+  0% { transform: translateY(1000px); }
+  100% { transform: translateY(0); }
 }
 
 @keyframes slideHorizontal {
-  0% { transform: translateX(-2000px);}
-  100% {transform: translateX(0);}
+  0% { transform: translateX(-2000px); }
+  100% { transform: translateX(0); }
+}
+
+@keyframes borderFade {
+  0% { border: 2px solid transparent }
+  0% { transform: 2px solid #FFF }
 }
diff --git a/src/low-preview/gallery.utils.js b/src/low-preview/gallery.utils.js
index afd29da..3542bcc 100644
--- a/src/low-preview/gallery.utils.js
+++ b/src/low-preview/gallery.utils.js
@@ -76,6 +76,19 @@ const updateOrCreateImageNameWrapper = (imageName, galleryModal) => {
   }
 };
 
+const toggleActiveThumbnail = (galleryModal, imageIndex) => {
+  const thumbnailsModule = galleryModal.querySelector('.ci-gallery-thumbnail-module');
+
+  [...thumbnailsModule.children].forEach((thumbnailContainer) => {
+    thumbnailContainer.removeAttribute(ATTRIBUTES.ACTIVE_THUMBNAIL);
+    const thumbnailContainerIndex = thumbnailContainer.getAttribute(ATTRIBUTES.GALLERY_INDEX);
+
+    if (+imageIndex === +thumbnailContainerIndex) {
+      thumbnailContainer.setAttribute(ATTRIBUTES.ACTIVE_THUMBNAIL, true);
+    }
+  });
+};
+
 const getGalleryPreviewModule = () => {
   const galleryModal = document.body.querySelector('[data-ci-gallery]');
 
@@ -130,6 +143,7 @@ const adaptGalleryThumbnails = (images = [], onClick) => {
     image.style.height = '100%';
 
     thmbnailContainer.classList.add(CLASSNAMES.THUMBNAIL_CONTAINER);
+    thmbnailContainer.setAttribute(ATTRIBUTES.GALLERY_THUMBNAIL_INDEX, index);
     thmbnailContainer.setAttribute(ATTRIBUTES.GALLERY_INDEX, index);
     thmbnailContainer.append(image);
 
@@ -228,4 +242,5 @@ export {
   galleryPreviewImage,
   getDimAndFit,
   swapArrayPositions,
+  toggleActiveThumbnail,
 };

From 04cc0b3964443179682f551229d278ad7b9e84b8 Mon Sep 17 00:00:00 2001
From: Amr Wagdy 
Date: Fri, 9 Sep 2022 16:10:37 +0200
Subject: [PATCH 32/45] Refactor: Code review T7582

---
 src/low-preview/ci.service.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/low-preview/ci.service.js b/src/low-preview/ci.service.js
index deeeeca..f11a12b 100644
--- a/src/low-preview/ci.service.js
+++ b/src/low-preview/ci.service.js
@@ -355,7 +355,7 @@ export default class CIResponsive {
       galleryModal.appendChild(previewModule);
       galleryModal.appendChild(thumbnailsModule);
       galleryModal.append(...galleryArrows);
-      galleryModal.onkeydown = debounce(100, this.handleModalKeydown.bind(this, orderedImages));
+      galleryModal.onkeydown = debounce(250, this.handleModalKeydown.bind(this, orderedImages));
 
       document.body.appendChild(galleryModal);
       galleryModal.focus();

From b051055690aa50ecf22c61702a73fbb731863332 Mon Sep 17 00:00:00 2001
From: Nermeen Moustafa 
Date: Fri, 9 Sep 2022 17:03:42 +0200
Subject: [PATCH 33/45] code refactor - T7582

---
 src/common/ci.constants.js       | 13 ++++-
 src/low-preview/ci.service.js    | 16 +++---
 src/low-preview/ci.styles.css    |  9 ++--
 src/low-preview/gallery.utils.js | 85 ++++++++++++++++----------------
 4 files changed, 67 insertions(+), 56 deletions(-)

diff --git a/src/common/ci.constants.js b/src/common/ci.constants.js
index d7b8c67..11531a6 100644
--- a/src/common/ci.constants.js
+++ b/src/common/ci.constants.js
@@ -16,12 +16,21 @@ const ATTRIBUTES = {
 
 const CLASSNAMES = {
   PREVIEW_LOADED: 'ci-preview-loaded',
+  PREVIEW_MODULE: 'ci-gallery-preview-module',
   GALLERY_ANIMATION: 'ci-gallery-animation',
-  GALLERY_TRANSITION: 'ci-gallery-transition',
   GALLERY_MODAL: 'ci-gallery-modal',
-  PREVIEW_MODULE: 'ci-gallery-preview-module',
+  GALLERY_DATA: 'data-ci-gallery',
+  GALLERY_LENGTH: 'data-ci-gallery-length',
+  GALLERY_INDEX: 'data-ci-gallery-index',
+  ACTIVE_IMAGE_INDEX: 'data-ci-active-image-index',
   THUMBNAIL_MODULE: 'ci-gallery-thumbnail-module',
   THUMBNAIL_CONTAINER: 'ci-gallery-thmbnail-container',
+  CLOSE_BTN: 'ci-gallery-close-button',
+  LEFT_ARROW_BTN: 'ci-gallery-left-arrow-button',
+  RIGHT_ARROW_BTN: 'ci-gallery-right-arrow-button',
+  ZOOM_BTN: 'ci-gallery-zoom-button',
+  IMAGE: 'ci-image',
+  IMAGE_NAME: 'ci-gallery-image-name',
   IMAGE_LOADED: 'ci-image-loaded',
 };
 
diff --git a/src/low-preview/ci.service.js b/src/low-preview/ci.service.js
index f11a12b..110970f 100644
--- a/src/low-preview/ci.service.js
+++ b/src/low-preview/ci.service.js
@@ -243,7 +243,7 @@ export default class CIResponsive {
   }
 
   animatePreviewModule(previewModule, nextIndex, direction) {
-    const currentIndex = previewModule.getAttribute('data-ci-active-image-index');
+    const currentIndex = previewModule.getAttribute(CLASSNAMES.ACTIVE_IMAGE_INDEX);
     const leftDirection = direction === 'left';
     let transform = 1000;
     let scale = 0.8;
@@ -293,7 +293,7 @@ export default class CIResponsive {
   processGalleryPreviewImage(imgNode, imageIndex, direction, intial) {
     const { imgSelector } = this.config;
     const { imageName, alt } = getImageProps(imgNode, imgSelector);
-    const galleryModal = document.querySelector('.ci-gallery-modal');
+    const galleryModal = document.querySelector(`.${CLASSNAMES.GALLERY_MODAL}`);
     const _imgNode = imgNode.cloneNode();
     const adaptedImageNode = removeClassNames(_imgNode, loadedImageClassNames);
     const previewModule = getGalleryPreviewModule();
@@ -316,7 +316,7 @@ export default class CIResponsive {
 
   handleClickThumbnail(galleryImages, event) {
     const thumbnail = event.currentTarget;
-    const thumbnailIndex = thumbnail.getAttribute('data-ci-gallery-index');
+    const thumbnailIndex = thumbnail.getAttribute(CLASSNAMES.GALLERY_INDEX);
     const [, index] = getGalleryLengthAndIndex();
 
     if (thumbnailIndex !== index) {
@@ -339,7 +339,7 @@ export default class CIResponsive {
       const orderedImages = swapArrayPositions(galleryImages, clickedImageIndex, 0)
         .filter((image) => image.classList.contains(CLASSNAMES.IMAGE_LOADED));
       const galleryModal = createGalleryModal(closeIconSvg, orderedImages.length, true);
-      const previewModule = galleryModal.querySelector('.ci-gallery-preview-module');
+      const previewModule = galleryModal.querySelector(`.${CLASSNAMES.PREVIEW_MODULE}`);
       const thumbnailsModule = createThmbnailsModule(
         orderedImages,
         galleryModal,
@@ -369,7 +369,7 @@ export default class CIResponsive {
       const clickedImageIndex = zoomImages.indexOf(clickedImage);
 
       const galleryModal = createGalleryModal(closeIconSvg);
-      const previewModule = galleryModal.querySelector('.ci-gallery-preview-module');
+      const previewModule = galleryModal.querySelector(`.${CLASSNAMES.PREVIEW_MODULE}`);
 
       galleryModal.tabIndex = 0;
       galleryModal.appendChild(previewModule);
@@ -455,9 +455,9 @@ export default class CIResponsive {
     if ((gallery || zoom) && !isProcessedByGallery) {
       wrapper.style.cursor = 'pointer';
       if (gallery) {
-        wrapper.classList.add(CLASSNAMES.GALLERY_ANIMATION, CLASSNAMES.GALLERY_TRANSITION);
+        wrapper.classList.add(CLASSNAMES.GALLERY_ANIMATION);
       } else {
-        const zoomIcon = createIcon(zoomIconSvg, 'ci-gallery-zoom-button', ICONS_STYLES.ZOOM);
+        const zoomIcon = createIcon(zoomIconSvg, CLASSNAMES.ZOOM_BTN, ICONS_STYLES.ZOOM);
         wrapper.append(zoomIcon);
       }
     }
@@ -529,7 +529,7 @@ export default class CIResponsive {
     }
 
     if (isImage) {
-      const isProcessed = node.classList.contains('ci-image');
+      const isProcessed = node.classList.contains(CLASSNAMES.IMAGE);
 
       if (src) {
         node.setAttribute(imgSelector, src);
diff --git a/src/low-preview/ci.styles.css b/src/low-preview/ci.styles.css
index 8a50f5b..5f7c7d2 100644
--- a/src/low-preview/ci.styles.css
+++ b/src/low-preview/ci.styles.css
@@ -160,18 +160,19 @@ img.ci-image-loaded {
   min-width: 40px;
   min-height: 40px;
   border: 1px solid #888;
+  background-color: black;
   cursor: pointer;
 }
 
+.ci-gallery-animation {
+  transition: transform 0.5s ease-in-out;
+}
+
 .ci-gallery-animation:hover {
   transform: scale(1.05);
   z-index: 2;
 }
 
-.ci-gallery-transition {
-  transition: transform 0.5s ease-in-out;
-}
-
 .ci-gallery-thmbnail-container img {
   position: absolute;
   top: 50%;
diff --git a/src/low-preview/gallery.utils.js b/src/low-preview/gallery.utils.js
index 3542bcc..89a87f9 100644
--- a/src/low-preview/gallery.utils.js
+++ b/src/low-preview/gallery.utils.js
@@ -9,8 +9,8 @@ const createIcon = (iconSrc, className, iconStyles) => {
   const icon = new Image();
 
   icon.src = iconSrc;
-  icon.width = width;
-  icon.height = height;
+  icon.style.width = `${width}px`;
+  icon.style.height = `${height}px`;
 
   if (className) {
     iconWrapper.classList.add(className);
@@ -26,7 +26,7 @@ const createIcon = (iconSrc, className, iconStyles) => {
 };
 
 const destroyGallery = () => {
-  const galleryModal = document.querySelector('.ci-gallery-modal');
+  const galleryModal = document.querySelector(`.${CLASSNAMES.GALLERY_MODAL}`);
 
   if (galleryModal) {
     galleryModal.parentElement.removeChild(galleryModal);
@@ -36,7 +36,7 @@ const destroyGallery = () => {
 const createGalleryModal = (closeIconSrc, galleryLength, isGallery) => {
   const galleryModal = document.createElement('div');
   const previewModule = document.createElement('div');
-  const closeIcon = createIcon(closeIconSrc, 'ci-gallery-close-button', ICONS_STYLES.COLOR);
+  const closeIcon = createIcon(closeIconSrc, CLASSNAMES.CLOSE_BTN, ICONS_STYLES.COLOR);
 
   galleryModal.tabIndex = 0;
   galleryModal.classList.add(CLASSNAMES.GALLERY_MODAL);
@@ -61,13 +61,13 @@ const createImageNameWrapper = (imageName, galleryModal) => {
   const imageNameContainer = document.createElement('p');
 
   imageNameContainer.innerHTML = imageName;
-  addClass(imageNameContainer, 'ci-gallery-image-name');
+  addClass(imageNameContainer, CLASSNAMES.IMAGE_NAME);
 
   galleryModal.appendChild(imageNameContainer);
 };
 
 const updateOrCreateImageNameWrapper = (imageName, galleryModal) => {
-  const imageNameContainer = galleryModal.querySelector('.ci-gallery-image-name');
+  const imageNameContainer = galleryModal.querySelector(`.${CLASSNAMES.IMAGE_NAME}`);
 
   if (imageNameContainer) {
     imageNameContainer.innerHTML = imageName;
@@ -77,7 +77,7 @@ const updateOrCreateImageNameWrapper = (imageName, galleryModal) => {
 };
 
 const toggleActiveThumbnail = (galleryModal, imageIndex) => {
-  const thumbnailsModule = galleryModal.querySelector('.ci-gallery-thumbnail-module');
+  const thumbnailsModule = galleryModal.querySelector(`.${CLASSNAMES.THUMBNAIL_MODULE}`);
 
   [...thumbnailsModule.children].forEach((thumbnailContainer) => {
     thumbnailContainer.removeAttribute(ATTRIBUTES.ACTIVE_THUMBNAIL);
@@ -90,20 +90,20 @@ const toggleActiveThumbnail = (galleryModal, imageIndex) => {
 };
 
 const getGalleryPreviewModule = () => {
-  const galleryModal = document.body.querySelector('[data-ci-gallery]');
+  const galleryModal = document.body.querySelector(`[${CLASSNAMES.GALLERY_DATA}]`);
 
-  return galleryModal.querySelector('.ci-gallery-preview-module');
+  return galleryModal.querySelector(`.${CLASSNAMES.PREVIEW_MODULE}`);
 };
 
 const setGalleryIndex = (index) => {
-  const galleryModal = document.body.querySelector('[data-ci-gallery]');
+  const galleryModal = document.body.querySelector(`[${CLASSNAMES.GALLERY_DATA}]`);
 
   galleryModal.setAttribute(ATTRIBUTES.GALLERY_INDEX, index);
 };
 
 const createGalleryArrows = (leftArrowIcon, rightArrowIcon, onClick) => {
-  const leftArrow = createIcon(leftArrowIcon, 'ci-gallery-left-arrow-button', ICONS_STYLES.COLOR);
-  const rightArrow = createIcon(rightArrowIcon, 'ci-gallery-right-arrow-button', ICONS_STYLES.COLOR);
+  const leftArrow = createIcon(leftArrowIcon, CLASSNAMES.LEFT_ARROW_BTN, ICONS_STYLES.COLOR);
+  const rightArrow = createIcon(rightArrowIcon, CLASSNAMES.RIGHT_ARROW_BTN, ICONS_STYLES.COLOR);
 
   if (onClick) {
     leftArrow.onclick = onClick.bind(this, 'left');
@@ -114,9 +114,9 @@ const createGalleryArrows = (leftArrowIcon, rightArrowIcon, onClick) => {
 };
 
 const getGalleryLengthAndIndex = () => {
-  const galleryModal = document.body.querySelector('[data-ci-gallery]');
-  const galleryLength = galleryModal.getAttribute('data-ci-gallery-length');
-  const galleryIndex = galleryModal.getAttribute('data-ci-gallery-index');
+  const galleryModal = document.body.querySelector(`[${CLASSNAMES.GALLERY_DATA}]`);
+  const galleryLength = galleryModal.getAttribute(CLASSNAMES.GALLERY_LENGTH);
+  const galleryIndex = galleryModal.getAttribute(CLASSNAMES.GALLERY_INDEX);
 
   return [+galleryLength, galleryIndex];
 };
@@ -130,17 +130,41 @@ const swapArrayPositions = (array = [], a = 0, b = 0) => {
   return clonedArray;
 };
 
+const getImageFitStyles = (naturalWidth, naturalHeight) => {
+  let shouldFitHorizontally;
+  const imageStyles = {};
+  const previewModule = document.body.querySelector(`.${CLASSNAMES.PREVIEW_MODULE}`);
+
+  if (naturalWidth && previewModule) {
+    const imageAspectRatio = naturalWidth / naturalHeight;
+    const renderWidth = previewModule.offsetHeight * imageAspectRatio;
+    shouldFitHorizontally = (imageAspectRatio <= 1)
+        && (renderWidth < previewModule.offsetWidth);
+  }
+
+  if (shouldFitHorizontally) {
+    imageStyles.width = 'auto';
+    imageStyles.height = '100%';
+  } else {
+    imageStyles.width = '100%';
+    imageStyles.height = 'auto';
+  }
+
+  return imageStyles;
+};
+
 const adaptGalleryThumbnails = (images = [], onClick) => {
   const lowPreviewImages = images.map((image) => image.previousSibling.firstChild);
 
   return lowPreviewImages.map((thumbnail, index) => {
     const thmbnailContainer = document.createElement('div');
+    const imageFitStyles = getImageFitStyles(thumbnail.naturalWidth, thumbnail.naturalHeight);
     const image = thumbnail.cloneNode();
 
-    image.classList.remove('ci-image');
+    image.classList.remove(CLASSNAMES.IMAGE);
     image.style = {};
-    image.style.width = '100%';
-    image.style.height = '100%';
+    image.style.width = imageFitStyles.width;
+    image.style.height = imageFitStyles.height;
 
     thmbnailContainer.classList.add(CLASSNAMES.THUMBNAIL_CONTAINER);
     thmbnailContainer.setAttribute(ATTRIBUTES.GALLERY_THUMBNAIL_INDEX, index);
@@ -162,7 +186,7 @@ const appendGalleryThumbnails = (thumbnails = [], thumbnailsContainer) => {
 };
 
 const createThmbnailsModule = (images, galleryModal, onClick) => {
-  const thumbnailsModule = galleryModal.querySelector('.ci-gallery-thumbnail-module');
+  const thumbnailsModule = galleryModal.querySelector(`.${CLASSNAMES.THUMBNAIL_MODULE}`);
   const adaptedGalleryThumbnails = adaptGalleryThumbnails(images, onClick);
 
   appendGalleryThumbnails(adaptedGalleryThumbnails, thumbnailsModule);
@@ -182,29 +206,6 @@ const getZoomImages = (images) => [...images].filter((image) => {
   return zoom;
 });
 
-const getImageFitStyles = (naturalWidth, naturalHeight) => {
-  let shouldFitHorizontally;
-  const imageStyles = {};
-  const previewModule = document.body.querySelector('.ci-gallery-preview-module');
-
-  if (naturalWidth && previewModule) {
-    const imageAspectRatio = naturalWidth / naturalHeight;
-    const renderWidth = previewModule.offsetHeight * imageAspectRatio;
-    shouldFitHorizontally = (imageAspectRatio <= 1)
-        && (renderWidth < previewModule.offsetWidth);
-  }
-
-  if (shouldFitHorizontally) {
-    imageStyles.width = 'auto';
-    imageStyles.height = '100%';
-  } else {
-    imageStyles.width = '100%';
-    imageStyles.height = 'auto';
-  }
-
-  return imageStyles;
-};
-
 const galleryPreviewImage = (imgSelector, imgNodeSRC) => {
   const image = new Image();
 

From e8274951cc96053cf3ba18ed92a837495eb6bab4 Mon Sep 17 00:00:00 2001
From: Nermeen Moustafa 
Date: Fri, 9 Sep 2022 17:22:48 +0200
Subject: [PATCH 34/45] code refactor - T7582

---
 src/common/ci.constants.js       | 16 ++++++----------
 src/low-preview/ci.service.js    |  8 ++++----
 src/low-preview/ci.utis.js       |  4 ++--
 src/low-preview/gallery.utils.js | 16 ++++++++--------
 4 files changed, 20 insertions(+), 24 deletions(-)

diff --git a/src/common/ci.constants.js b/src/common/ci.constants.js
index 11531a6..1b5ddec 100644
--- a/src/common/ci.constants.js
+++ b/src/common/ci.constants.js
@@ -5,9 +5,7 @@ const ATTRIBUTES = {
   PROCESSED_GALLERY: 'data-ci-processed-gallery',
   ACTIVE_IMAGE_INDEX: 'data-ci-active-image-index',
   PROCESSED: 'data-ci-processed',
-  OPTIMIZED_URL: 'ci-optimized-url',
   BG_CONTAINER: 'data-ci-bg-container',
-  PREVIEW: 'ci-preview',
   GALLERY: 'data-ci-gallery',
   GALLERY_LENGTH: 'data-ci-gallery-length',
   GALLERY_INDEX: 'data-ci-gallery-index',
@@ -15,20 +13,18 @@ const ATTRIBUTES = {
 };
 
 const CLASSNAMES = {
+  OPTIMIZED_URL: 'ci-optimized-url',
+  PREVIEW: 'ci-preview',
   PREVIEW_LOADED: 'ci-preview-loaded',
   PREVIEW_MODULE: 'ci-gallery-preview-module',
   GALLERY_ANIMATION: 'ci-gallery-animation',
   GALLERY_MODAL: 'ci-gallery-modal',
-  GALLERY_DATA: 'data-ci-gallery',
-  GALLERY_LENGTH: 'data-ci-gallery-length',
-  GALLERY_INDEX: 'data-ci-gallery-index',
-  ACTIVE_IMAGE_INDEX: 'data-ci-active-image-index',
   THUMBNAIL_MODULE: 'ci-gallery-thumbnail-module',
   THUMBNAIL_CONTAINER: 'ci-gallery-thmbnail-container',
-  CLOSE_BTN: 'ci-gallery-close-button',
-  LEFT_ARROW_BTN: 'ci-gallery-left-arrow-button',
-  RIGHT_ARROW_BTN: 'ci-gallery-right-arrow-button',
-  ZOOM_BTN: 'ci-gallery-zoom-button',
+  CLOSE_BUTTON: 'ci-gallery-close-button',
+  LEFT_ARROW_BUTTON: 'ci-gallery-left-arrow-button',
+  RIGHT_ARROW_BUTTON: 'ci-gallery-right-arrow-button',
+  ZOOM_BUTTON: 'ci-gallery-zoom-button',
   IMAGE: 'ci-image',
   IMAGE_NAME: 'ci-gallery-image-name',
   IMAGE_LOADED: 'ci-image-loaded',
diff --git a/src/low-preview/ci.service.js b/src/low-preview/ci.service.js
index 110970f..cbc8bcf 100644
--- a/src/low-preview/ci.service.js
+++ b/src/low-preview/ci.service.js
@@ -243,7 +243,7 @@ export default class CIResponsive {
   }
 
   animatePreviewModule(previewModule, nextIndex, direction) {
-    const currentIndex = previewModule.getAttribute(CLASSNAMES.ACTIVE_IMAGE_INDEX);
+    const currentIndex = previewModule.getAttribute(ATTRIBUTES.ACTIVE_IMAGE_INDEX);
     const leftDirection = direction === 'left';
     let transform = 1000;
     let scale = 0.8;
@@ -316,7 +316,7 @@ export default class CIResponsive {
 
   handleClickThumbnail(galleryImages, event) {
     const thumbnail = event.currentTarget;
-    const thumbnailIndex = thumbnail.getAttribute(CLASSNAMES.GALLERY_INDEX);
+    const thumbnailIndex = thumbnail.getAttribute(ATTRIBUTES.GALLERY_INDEX);
     const [, index] = getGalleryLengthAndIndex();
 
     if (thumbnailIndex !== index) {
@@ -457,7 +457,7 @@ export default class CIResponsive {
       if (gallery) {
         wrapper.classList.add(CLASSNAMES.GALLERY_ANIMATION);
       } else {
-        const zoomIcon = createIcon(zoomIconSvg, CLASSNAMES.ZOOM_BTN, ICONS_STYLES.ZOOM);
+        const zoomIcon = createIcon(zoomIconSvg, CLASSNAMES.ZOOM_BUTTON, ICONS_STYLES.ZOOM);
         wrapper.append(zoomIcon);
       }
     }
@@ -500,7 +500,7 @@ export default class CIResponsive {
         });
 
         if (lazy) {
-          imgNode.setAttribute(ATTRIBUTES.OPTIMIZED_URL, cloudimageUrl);
+          imgNode.setAttribute(CLASSNAMES.OPTIMIZED_URL, cloudimageUrl);
 
           setBackgroundSrc(previewBox, previewImgURL, lazy, src, isSVG, dataSrcAttr);
         } else {
diff --git a/src/low-preview/ci.utis.js b/src/low-preview/ci.utis.js
index 4033883..401dcbe 100644
--- a/src/low-preview/ci.utis.js
+++ b/src/low-preview/ci.utis.js
@@ -1,4 +1,4 @@
-import { ATTRIBUTES } from '../common/ci.constants';
+import { ATTRIBUTES, CLASSNAMES } from '../common/ci.constants';
 import { addClass, getWrapper } from '../common/ci.utils';
 
 
@@ -42,7 +42,7 @@ export const applyBackgroundStyles = ({
   contentBox.setAttribute(ATTRIBUTES.BG_CONTAINER, true);
 
   previewBox.className = `${imgNode.className}${lazy ? ' lazyload' : ''}`;
-  previewBox.setAttribute(ATTRIBUTES.PREVIEW, true);
+  previewBox.setAttribute(CLASSNAMES.PREVIEW, true);
   previewBox.style.background = 'inherit';
   previewBox.style.position = 'absolute';
   previewBox.style.left = '0';
diff --git a/src/low-preview/gallery.utils.js b/src/low-preview/gallery.utils.js
index 89a87f9..cf4be8d 100644
--- a/src/low-preview/gallery.utils.js
+++ b/src/low-preview/gallery.utils.js
@@ -36,7 +36,7 @@ const destroyGallery = () => {
 const createGalleryModal = (closeIconSrc, galleryLength, isGallery) => {
   const galleryModal = document.createElement('div');
   const previewModule = document.createElement('div');
-  const closeIcon = createIcon(closeIconSrc, CLASSNAMES.CLOSE_BTN, ICONS_STYLES.COLOR);
+  const closeIcon = createIcon(closeIconSrc, CLASSNAMES.CLOSE_BUTTON, ICONS_STYLES.COLOR);
 
   galleryModal.tabIndex = 0;
   galleryModal.classList.add(CLASSNAMES.GALLERY_MODAL);
@@ -90,20 +90,20 @@ const toggleActiveThumbnail = (galleryModal, imageIndex) => {
 };
 
 const getGalleryPreviewModule = () => {
-  const galleryModal = document.body.querySelector(`[${CLASSNAMES.GALLERY_DATA}]`);
+  const galleryModal = document.body.querySelector(`[${CLASSNAMES.GALLERY}]`);
 
   return galleryModal.querySelector(`.${CLASSNAMES.PREVIEW_MODULE}`);
 };
 
 const setGalleryIndex = (index) => {
-  const galleryModal = document.body.querySelector(`[${CLASSNAMES.GALLERY_DATA}]`);
+  const galleryModal = document.body.querySelector(`[${CLASSNAMES.GALLERY}]`);
 
   galleryModal.setAttribute(ATTRIBUTES.GALLERY_INDEX, index);
 };
 
 const createGalleryArrows = (leftArrowIcon, rightArrowIcon, onClick) => {
-  const leftArrow = createIcon(leftArrowIcon, CLASSNAMES.LEFT_ARROW_BTN, ICONS_STYLES.COLOR);
-  const rightArrow = createIcon(rightArrowIcon, CLASSNAMES.RIGHT_ARROW_BTN, ICONS_STYLES.COLOR);
+  const leftArrow = createIcon(leftArrowIcon, CLASSNAMES.LEFT_ARROW_BUTTON, ICONS_STYLES.COLOR);
+  const rightArrow = createIcon(rightArrowIcon, CLASSNAMES.RIGHT_ARROW_BUTTON, ICONS_STYLES.COLOR);
 
   if (onClick) {
     leftArrow.onclick = onClick.bind(this, 'left');
@@ -114,9 +114,9 @@ const createGalleryArrows = (leftArrowIcon, rightArrowIcon, onClick) => {
 };
 
 const getGalleryLengthAndIndex = () => {
-  const galleryModal = document.body.querySelector(`[${CLASSNAMES.GALLERY_DATA}]`);
-  const galleryLength = galleryModal.getAttribute(CLASSNAMES.GALLERY_LENGTH);
-  const galleryIndex = galleryModal.getAttribute(CLASSNAMES.GALLERY_INDEX);
+  const galleryModal = document.body.querySelector(`[${CLASSNAMES.GALLERY}]`);
+  const galleryLength = galleryModal.getAttribute(ATTRIBUTES.GALLERY_LENGTH);
+  const galleryIndex = galleryModal.getAttribute(ATTRIBUTES.GALLERY_INDEX);
 
   return [+galleryLength, galleryIndex];
 };

From 6386f37b0c6c1a385711ae35e886653fa543bc3c Mon Sep 17 00:00:00 2001
From: Nermeen Moustafa 
Date: Fri, 9 Sep 2022 17:58:33 +0200
Subject: [PATCH 35/45] code refactor - T7582

---
 src/common/ci.constants.js       | 5 +++--
 src/low-preview/ci.service.js    | 2 +-
 src/low-preview/ci.utis.js       | 4 ++--
 src/low-preview/gallery.utils.js | 6 +++---
 4 files changed, 9 insertions(+), 8 deletions(-)

diff --git a/src/common/ci.constants.js b/src/common/ci.constants.js
index 1b5ddec..e69300f 100644
--- a/src/common/ci.constants.js
+++ b/src/common/ci.constants.js
@@ -2,6 +2,8 @@ const loadedImageClassNames = ['ci-image-loaded', 'lazyloaded', 'ci-image'];
 
 const ATTRIBUTES = {
   CANVAS: 'data-ci-canvas',
+  OPTIMIZED_URL: 'ci-optimized-url',
+  PREVIEW: 'ci-preview',
   PROCESSED_GALLERY: 'data-ci-processed-gallery',
   ACTIVE_IMAGE_INDEX: 'data-ci-active-image-index',
   PROCESSED: 'data-ci-processed',
@@ -10,11 +12,10 @@ const ATTRIBUTES = {
   GALLERY_LENGTH: 'data-ci-gallery-length',
   GALLERY_INDEX: 'data-ci-gallery-index',
   ACTIVE_THUMBNAIL: 'data-active-thumbnail',
+  GALLERY_THUMBNAIL_INDEX: 'data-gallery-thumbnail-index',
 };
 
 const CLASSNAMES = {
-  OPTIMIZED_URL: 'ci-optimized-url',
-  PREVIEW: 'ci-preview',
   PREVIEW_LOADED: 'ci-preview-loaded',
   PREVIEW_MODULE: 'ci-gallery-preview-module',
   GALLERY_ANIMATION: 'ci-gallery-animation',
diff --git a/src/low-preview/ci.service.js b/src/low-preview/ci.service.js
index cbc8bcf..4d44dbe 100644
--- a/src/low-preview/ci.service.js
+++ b/src/low-preview/ci.service.js
@@ -500,7 +500,7 @@ export default class CIResponsive {
         });
 
         if (lazy) {
-          imgNode.setAttribute(CLASSNAMES.OPTIMIZED_URL, cloudimageUrl);
+          imgNode.setAttribute(ATTRIBUTES.OPTIMIZED_URL, cloudimageUrl);
 
           setBackgroundSrc(previewBox, previewImgURL, lazy, src, isSVG, dataSrcAttr);
         } else {
diff --git a/src/low-preview/ci.utis.js b/src/low-preview/ci.utis.js
index 401dcbe..4033883 100644
--- a/src/low-preview/ci.utis.js
+++ b/src/low-preview/ci.utis.js
@@ -1,4 +1,4 @@
-import { ATTRIBUTES, CLASSNAMES } from '../common/ci.constants';
+import { ATTRIBUTES } from '../common/ci.constants';
 import { addClass, getWrapper } from '../common/ci.utils';
 
 
@@ -42,7 +42,7 @@ export const applyBackgroundStyles = ({
   contentBox.setAttribute(ATTRIBUTES.BG_CONTAINER, true);
 
   previewBox.className = `${imgNode.className}${lazy ? ' lazyload' : ''}`;
-  previewBox.setAttribute(CLASSNAMES.PREVIEW, true);
+  previewBox.setAttribute(ATTRIBUTES.PREVIEW, true);
   previewBox.style.background = 'inherit';
   previewBox.style.position = 'absolute';
   previewBox.style.left = '0';
diff --git a/src/low-preview/gallery.utils.js b/src/low-preview/gallery.utils.js
index cf4be8d..48b3033 100644
--- a/src/low-preview/gallery.utils.js
+++ b/src/low-preview/gallery.utils.js
@@ -90,13 +90,13 @@ const toggleActiveThumbnail = (galleryModal, imageIndex) => {
 };
 
 const getGalleryPreviewModule = () => {
-  const galleryModal = document.body.querySelector(`[${CLASSNAMES.GALLERY}]`);
+  const galleryModal = document.body.querySelector(`[${ATTRIBUTES.GALLERY}]`);
 
   return galleryModal.querySelector(`.${CLASSNAMES.PREVIEW_MODULE}`);
 };
 
 const setGalleryIndex = (index) => {
-  const galleryModal = document.body.querySelector(`[${CLASSNAMES.GALLERY}]`);
+  const galleryModal = document.body.querySelector(`[${ATTRIBUTES.GALLERY}]`);
 
   galleryModal.setAttribute(ATTRIBUTES.GALLERY_INDEX, index);
 };
@@ -114,7 +114,7 @@ const createGalleryArrows = (leftArrowIcon, rightArrowIcon, onClick) => {
 };
 
 const getGalleryLengthAndIndex = () => {
-  const galleryModal = document.body.querySelector(`[${CLASSNAMES.GALLERY}]`);
+  const galleryModal = document.body.querySelector(`[${ATTRIBUTES.GALLERY}]`);
   const galleryLength = galleryModal.getAttribute(ATTRIBUTES.GALLERY_LENGTH);
   const galleryIndex = galleryModal.getAttribute(ATTRIBUTES.GALLERY_INDEX);
 

From f034ff04407f635d233e9591051c8fdb2bbfb47d Mon Sep 17 00:00:00 2001
From: Nermeen Moustafa 
Date: Fri, 9 Sep 2022 19:56:57 +0200
Subject: [PATCH 36/45] code refactor - T7582

---
 src/common/ci.constants.js       | 1 -
 src/low-preview/ci.service.js    | 7 +++++--
 src/low-preview/gallery.utils.js | 1 -
 3 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/src/common/ci.constants.js b/src/common/ci.constants.js
index e69300f..5f1d735 100644
--- a/src/common/ci.constants.js
+++ b/src/common/ci.constants.js
@@ -12,7 +12,6 @@ const ATTRIBUTES = {
   GALLERY_LENGTH: 'data-ci-gallery-length',
   GALLERY_INDEX: 'data-ci-gallery-index',
   ACTIVE_THUMBNAIL: 'data-active-thumbnail',
-  GALLERY_THUMBNAIL_INDEX: 'data-gallery-thumbnail-index',
 };
 
 const CLASSNAMES = {
diff --git a/src/low-preview/ci.service.js b/src/low-preview/ci.service.js
index 4d44dbe..2c5d970 100644
--- a/src/low-preview/ci.service.js
+++ b/src/low-preview/ci.service.js
@@ -358,9 +358,12 @@ export default class CIResponsive {
       galleryModal.onkeydown = debounce(250, this.handleModalKeydown.bind(this, orderedImages));
 
       document.body.appendChild(galleryModal);
+
+      const [, orderedClickedImage] = getGalleryLengthAndIndex();
+
       galleryModal.focus();
-      this.processGalleryPreviewImage(galleryImages[clickedImageIndex], clickedImageIndex, undefined, true);
-      setGalleryIndex(clickedImageIndex);
+      this.processGalleryPreviewImage(orderedImages[orderedClickedImage], orderedClickedImage, undefined, true);
+      setGalleryIndex(orderedClickedImage);
     }
 
     if (zoom && !gallery) {
diff --git a/src/low-preview/gallery.utils.js b/src/low-preview/gallery.utils.js
index 48b3033..996af39 100644
--- a/src/low-preview/gallery.utils.js
+++ b/src/low-preview/gallery.utils.js
@@ -167,7 +167,6 @@ const adaptGalleryThumbnails = (images = [], onClick) => {
     image.style.height = imageFitStyles.height;
 
     thmbnailContainer.classList.add(CLASSNAMES.THUMBNAIL_CONTAINER);
-    thmbnailContainer.setAttribute(ATTRIBUTES.GALLERY_THUMBNAIL_INDEX, index);
     thmbnailContainer.setAttribute(ATTRIBUTES.GALLERY_INDEX, index);
     thmbnailContainer.append(image);
 

From 0983ebc003540243b5297287b88a7e64e94c2f9a Mon Sep 17 00:00:00 2001
From: Nermeen Moustafa 
Date: Wed, 14 Sep 2022 21:41:15 +0200
Subject: [PATCH 37/45] add some improvements - T7582

---
 examples/low-preview/src/index.js |  2 +-
 examples/low-preview/src/init.js  |  4 +-
 src/low-preview/ci.config.js      | 20 ++++++-
 src/low-preview/ci.service.js     | 53 +++++++++++++-----
 src/low-preview/ci.styles.css     | 17 ++++++
 src/low-preview/gallery.utils.js  | 93 ++++++++++++++++++++++++++-----
 6 files changed, 158 insertions(+), 31 deletions(-)

diff --git a/examples/low-preview/src/index.js b/examples/low-preview/src/index.js
index c0ed3df..32eb385 100644
--- a/examples/low-preview/src/index.js
+++ b/examples/low-preview/src/index.js
@@ -1,4 +1,4 @@
 import '../../../src/low-preview';
 import './init';
 import '../../common/demo';
-import './styles/main.css';
\ No newline at end of file
+import './styles/main.css';
diff --git a/examples/low-preview/src/init.js b/examples/low-preview/src/init.js
index 3f6390a..d65d86a 100644
--- a/examples/low-preview/src/init.js
+++ b/examples/low-preview/src/init.js
@@ -4,9 +4,9 @@ window.ciResponsive = new window.CIResponsive({
   lazyLoading: true,
   apiVersion: null,
   lowQualityPreview: {
-    minImgWidth: 180
+    minImgWidth: 180,
   },
-  doNotReplaceURL: true
+  doNotReplaceURL: true,
 });
 
 window.lazySizes.init();
diff --git a/src/low-preview/ci.config.js b/src/low-preview/ci.config.js
index 6a2d494..6b54fe0 100644
--- a/src/low-preview/ci.config.js
+++ b/src/low-preview/ci.config.js
@@ -33,7 +33,7 @@ export const getInitialConfigLowPreview = (config) => {
     lowQualityPreview: {
       minImgWidth = 400,
     } = {},
-
+    gallery,
     // callback
     onImageLoad = null,
   } = config;
@@ -74,6 +74,24 @@ export const getInitialConfigLowPreview = (config) => {
     detectImageNodeCSS,
     processOnlyWidth,
     onImageLoad,
+    galleryConfigs: {
+      arrowPrevIcon: null,
+      arrowNextIcon: null,
+      closeIcon: null,
+      modalClassName: '',
+      previewClassName: '',
+      thumbnailsClassName: '',
+      arrowPrev: true,
+      arrowNext: true,
+      close: true,
+      thumbnails: true,
+      onOpen: null,
+      onClose: null,
+      onPrev: null,
+      onNext: null,
+      onClickThumbnail: null,
+      ...gallery,
+    },
     // isChrome: /Chrome/.test(navigator.userAgent) && /Google Inc/.test(navigator.vendor)
   };
 };
diff --git a/src/low-preview/ci.service.js b/src/low-preview/ci.service.js
index 2c5d970..cc32406 100644
--- a/src/low-preview/ci.service.js
+++ b/src/low-preview/ci.service.js
@@ -334,27 +334,52 @@ export default class CIResponsive {
     const galleryImages = getGalleryImages(images, gallery);
 
     if (gallery && galleryImages) {
+      const { galleryConfigs } = this.config;
+      const {
+        thumbnails, arrowPrev, arrowNext, onOpen, onClickThumbnail,
+      } = galleryConfigs;
+
+      if (typeof onOpen === 'function') {
+        onOpen(event);
+      }
+
       const clickedImage = event.currentTarget.lastChild;
       const clickedImageIndex = galleryImages.indexOf(clickedImage);
       const orderedImages = swapArrayPositions(galleryImages, clickedImageIndex, 0)
         .filter((image) => image.classList.contains(CLASSNAMES.IMAGE_LOADED));
-      const galleryModal = createGalleryModal(closeIconSvg, orderedImages.length, true);
+      const galleryModal = createGalleryModal(closeIconSvg, galleryConfigs, orderedImages.length, true);
       const previewModule = galleryModal.querySelector(`.${CLASSNAMES.PREVIEW_MODULE}`);
-      const thumbnailsModule = createThmbnailsModule(
-        orderedImages,
-        galleryModal,
-        this.handleClickThumbnail.bind(this, orderedImages),
-      );
-
-      const galleryArrows = createGalleryArrows(
-        leftArrowSvg,
-        rightArrowSvg,
-        this.handleClickArrows.bind(this, orderedImages),
-      );
 
       galleryModal.appendChild(previewModule);
-      galleryModal.appendChild(thumbnailsModule);
-      galleryModal.append(...galleryArrows);
+
+      if (thumbnails && orderedImages.length > 1) {
+        const thumbnailsModule = createThmbnailsModule(
+          orderedImages,
+          galleryModal,
+          onClickThumbnail,
+          this.handleClickThumbnail.bind(this, orderedImages),
+        );
+
+        galleryModal.appendChild(thumbnailsModule);
+      }
+
+      if (orderedImages.length > 1) {
+        const galleryArrows = createGalleryArrows(
+          leftArrowSvg,
+          rightArrowSvg,
+          galleryConfigs,
+          this.handleClickArrows.bind(this, orderedImages),
+        );
+
+        if (arrowPrev) {
+          galleryModal.append(galleryArrows[0]);
+        }
+
+        if (arrowNext) {
+          galleryModal.append(galleryArrows[1]);
+        }
+      }
+
       galleryModal.onkeydown = debounce(250, this.handleModalKeydown.bind(this, orderedImages));
 
       document.body.appendChild(galleryModal);
diff --git a/src/low-preview/ci.styles.css b/src/low-preview/ci.styles.css
index 5f7c7d2..01b3733 100644
--- a/src/low-preview/ci.styles.css
+++ b/src/low-preview/ci.styles.css
@@ -234,6 +234,18 @@ img.ci-image-loaded {
   transition: transform 300ms ease-in-out;
 }
 
+.ci-gallery-loader {
+  border: 3px solid #3498db;
+  border-top: 3px solid white;
+  border-radius: 50%;
+  width: 30px;
+  height: 30px;
+  position: absolute;
+  top: 50%;
+  left: 50%;
+  animation: spin 2s linear infinite;
+}
+
 @keyframes slideVertical {
   0% { transform: translateY(1000px); }
   100% { transform: translateY(0); }
@@ -248,3 +260,8 @@ img.ci-image-loaded {
   0% { border: 2px solid transparent }
   0% { transform: 2px solid #FFF }
 }
+
+@keyframes spin {
+  0% { transform: rotate(0deg); }
+  100% { transform: rotate(360deg); }
+}
diff --git a/src/low-preview/gallery.utils.js b/src/low-preview/gallery.utils.js
index 996af39..6e6ebeb 100644
--- a/src/low-preview/gallery.utils.js
+++ b/src/low-preview/gallery.utils.js
@@ -6,9 +6,15 @@ const createIcon = (iconSrc, className, iconStyles) => {
   const { color, width = 15, height = 15 } = iconStyles;
 
   const iconWrapper = document.createElement('div');
-  const icon = new Image();
+  let icon;
+
+  if (iconSrc.tagName === 'IMG') {
+    icon = iconSrc;
+  } else {
+    icon = new Image();
+    icon.src = iconSrc;
+  }
 
-  icon.src = iconSrc;
   icon.style.width = `${width}px`;
   icon.style.height = `${height}px`;
 
@@ -25,7 +31,11 @@ const createIcon = (iconSrc, className, iconStyles) => {
   return iconWrapper;
 };
 
-const destroyGallery = () => {
+const destroyGallery = (onClose, event) => {
+  if (typeof onClose === 'function') {
+    onClose(event);
+  }
+
   const galleryModal = document.querySelector(`.${CLASSNAMES.GALLERY_MODAL}`);
 
   if (galleryModal) {
@@ -33,22 +43,52 @@ const destroyGallery = () => {
   }
 };
 
-const createGalleryModal = (closeIconSrc, galleryLength, isGallery) => {
+const createGalleryModal = (closeIconSrc, galleryConfigs, galleryLength, isGallery) => {
+  const {
+    modalClassName, previewClassName, thumbnailsClassName, closeIcon, close, onClose,
+  } = galleryConfigs;
+
+  let closeIcn = null;
+
   const galleryModal = document.createElement('div');
   const previewModule = document.createElement('div');
-  const closeIcon = createIcon(closeIconSrc, CLASSNAMES.CLOSE_BUTTON, ICONS_STYLES.COLOR);
+  const loader = document.createElement('div');
+
+  if (closeIcon) {
+    closeIcn = createIcon(closeIcon, CLASSNAMES.CLOSE_BUTTON, ICONS_STYLES.COLOR);
+  } else {
+    closeIcn = createIcon(closeIconSrc, CLASSNAMES.CLOSE_BUTTON, ICONS_STYLES.COLOR);
+  }
 
   galleryModal.tabIndex = 0;
   galleryModal.classList.add(CLASSNAMES.GALLERY_MODAL);
   previewModule.classList.add(CLASSNAMES.PREVIEW_MODULE);
   galleryModal.setAttribute(ATTRIBUTES.GALLERY, true);
+  loader.classList.add('ci-gallery-loader');
   galleryModal.append(previewModule);
-  galleryModal.append(closeIcon);
-  closeIcon.onclick = destroyGallery;
+  galleryModal.append(loader);
+
+  if (close) {
+    galleryModal.append(closeIcn);
+  }
+
+  closeIcn.onclick = destroyGallery.bind(this, onClose);
+
+  if (modalClassName) {
+    galleryModal.classList.add(modalClassName);
+  }
+
+  if (previewClassName) {
+    previewModule.classList.add(previewClassName);
+  }
 
   if (isGallery) {
     const thumbnailsModule = document.createElement('div');
     thumbnailsModule.classList.add(CLASSNAMES.THUMBNAIL_MODULE);
+
+    if (thumbnailsClassName) {
+      thumbnailsModule.classList.add(thumbnailsClassName);
+    }
     galleryModal.setAttribute(ATTRIBUTES.GALLERY_LENGTH, galleryLength);
     galleryModal.setAttribute(ATTRIBUTES.GALLERY_INDEX, 0);
     galleryModal.append(thumbnailsModule);
@@ -101,15 +141,38 @@ const setGalleryIndex = (index) => {
   galleryModal.setAttribute(ATTRIBUTES.GALLERY_INDEX, index);
 };
 
-const createGalleryArrows = (leftArrowIcon, rightArrowIcon, onClick) => {
-  const leftArrow = createIcon(leftArrowIcon, CLASSNAMES.LEFT_ARROW_BUTTON, ICONS_STYLES.COLOR);
-  const rightArrow = createIcon(rightArrowIcon, CLASSNAMES.RIGHT_ARROW_BUTTON, ICONS_STYLES.COLOR);
+const createGalleryArrows = (leftArrowIcon, rightArrowIcon, galleryConfigs, onClick) => {
+  const {
+    arrowPrevIcon, arrowNextIcon, onPrev, onNext,
+  } = galleryConfigs;
+  let leftArrow;
+  let rightArrow;
+
+  if (arrowPrevIcon) {
+    leftArrow = createIcon(arrowPrevIcon, CLASSNAMES.LEFT_ARROW_BUTTON, ICONS_STYLES.COLOR);
+  } else {
+    leftArrow = createIcon(leftArrowIcon, CLASSNAMES.LEFT_ARROW_BUTTON, ICONS_STYLES.COLOR);
+  }
+
+  if (arrowNextIcon) {
+    rightArrow = createIcon(arrowNextIcon, CLASSNAMES.RIGHT_ARROW_BUTTON, ICONS_STYLES.COLOR);
+  } else {
+    rightArrow = createIcon(rightArrowIcon, CLASSNAMES.RIGHT_ARROW_BUTTON, ICONS_STYLES.COLOR);
+  }
 
   if (onClick) {
     leftArrow.onclick = onClick.bind(this, 'left');
     rightArrow.onclick = onClick.bind(this, 'right');
   }
 
+  if (typeof onPrev === 'function') {
+    leftArrow.onclick = onPrev.bind(this);
+  }
+
+  if (typeof onNext === 'function') {
+    rightArrow.onclick = onNext.bind(this);
+  }
+
   return [leftArrow, rightArrow];
 };
 
@@ -153,7 +216,7 @@ const getImageFitStyles = (naturalWidth, naturalHeight) => {
   return imageStyles;
 };
 
-const adaptGalleryThumbnails = (images = [], onClick) => {
+const adaptGalleryThumbnails = (images = [], onClickThumbnail, onClick) => {
   const lowPreviewImages = images.map((image) => image.previousSibling.firstChild);
 
   return lowPreviewImages.map((thumbnail, index) => {
@@ -174,6 +237,10 @@ const adaptGalleryThumbnails = (images = [], onClick) => {
       thmbnailContainer.onclick = onClick;
     }
 
+    if (typeof onClickThumbnail === 'function') {
+      thmbnailContainer.onclick = onClickThumbnail.bind(this);
+    }
+
     return thmbnailContainer;
   });
 };
@@ -184,9 +251,9 @@ const appendGalleryThumbnails = (thumbnails = [], thumbnailsContainer) => {
   });
 };
 
-const createThmbnailsModule = (images, galleryModal, onClick) => {
+const createThmbnailsModule = (images, galleryModal, onClickThumbnail, onClick) => {
   const thumbnailsModule = galleryModal.querySelector(`.${CLASSNAMES.THUMBNAIL_MODULE}`);
-  const adaptedGalleryThumbnails = adaptGalleryThumbnails(images, onClick);
+  const adaptedGalleryThumbnails = adaptGalleryThumbnails(images, onClickThumbnail, onClick);
 
   appendGalleryThumbnails(adaptedGalleryThumbnails, thumbnailsModule);
 

From e5e1c9d5944e2526161d85009632dc58fcf14a7f Mon Sep 17 00:00:00 2001
From: Amr Wagdy 
Date: Thu, 22 Sep 2022 17:09:12 +0200
Subject: [PATCH 38/45] Refactor: code review

---
 src/low-preview/ci.config.js | 2 --
 1 file changed, 2 deletions(-)

diff --git a/src/low-preview/ci.config.js b/src/low-preview/ci.config.js
index 6b54fe0..2b00d8f 100644
--- a/src/low-preview/ci.config.js
+++ b/src/low-preview/ci.config.js
@@ -34,7 +34,6 @@ export const getInitialConfigLowPreview = (config) => {
       minImgWidth = 400,
     } = {},
     gallery,
-    // callback
     onImageLoad = null,
   } = config;
 
@@ -92,6 +91,5 @@ export const getInitialConfigLowPreview = (config) => {
       onClickThumbnail: null,
       ...gallery,
     },
-    // isChrome: /Chrome/.test(navigator.userAgent) && /Google Inc/.test(navigator.vendor)
   };
 };

From 02c6afb901911e717005d00f34764450f1d2bc75 Mon Sep 17 00:00:00 2001
From: Nermeen Moustafa 
Date: Fri, 23 Sep 2022 15:42:34 +0200
Subject: [PATCH 39/45] code refactor - T7582

---
 src/common/ci.constants.js       |  1 +
 src/low-preview/ci.service.js    | 31 ++++++++------
 src/low-preview/ci.styles.css    |  1 +
 src/low-preview/gallery.utils.js | 69 ++++++++++++--------------------
 4 files changed, 46 insertions(+), 56 deletions(-)

diff --git a/src/common/ci.constants.js b/src/common/ci.constants.js
index 5f1d735..56b50c1 100644
--- a/src/common/ci.constants.js
+++ b/src/common/ci.constants.js
@@ -19,6 +19,7 @@ const CLASSNAMES = {
   PREVIEW_MODULE: 'ci-gallery-preview-module',
   GALLERY_ANIMATION: 'ci-gallery-animation',
   GALLERY_MODAL: 'ci-gallery-modal',
+  GALLERY_LOADER: 'ci-gallery-loader',
   THUMBNAIL_MODULE: 'ci-gallery-thumbnail-module',
   THUMBNAIL_CONTAINER: 'ci-gallery-thmbnail-container',
   CLOSE_BUTTON: 'ci-gallery-close-button',
diff --git a/src/low-preview/ci.service.js b/src/low-preview/ci.service.js
index cc32406..643f4bd 100644
--- a/src/low-preview/ci.service.js
+++ b/src/low-preview/ci.service.js
@@ -19,6 +19,7 @@ import {
   setSrc,
   setSrcset,
   removeClassNames,
+  addClass,
 } from '../common/ci.utils';
 import {
   createIcon,
@@ -348,9 +349,6 @@ export default class CIResponsive {
       const orderedImages = swapArrayPositions(galleryImages, clickedImageIndex, 0)
         .filter((image) => image.classList.contains(CLASSNAMES.IMAGE_LOADED));
       const galleryModal = createGalleryModal(closeIconSvg, galleryConfigs, orderedImages.length, true);
-      const previewModule = galleryModal.querySelector(`.${CLASSNAMES.PREVIEW_MODULE}`);
-
-      galleryModal.appendChild(previewModule);
 
       if (thumbnails && orderedImages.length > 1) {
         const thumbnailsModule = createThmbnailsModule(
@@ -364,7 +362,7 @@ export default class CIResponsive {
       }
 
       if (orderedImages.length > 1) {
-        const galleryArrows = createGalleryArrows(
+        const [leftArrow, rightArrow] = createGalleryArrows(
           leftArrowSvg,
           rightArrowSvg,
           galleryConfigs,
@@ -372,15 +370,15 @@ export default class CIResponsive {
         );
 
         if (arrowPrev) {
-          galleryModal.append(galleryArrows[0]);
+          galleryModal.append(leftArrow);
         }
 
         if (arrowNext) {
-          galleryModal.append(galleryArrows[1]);
+          galleryModal.append(rightArrow);
         }
-      }
 
-      galleryModal.onkeydown = debounce(250, this.handleModalKeydown.bind(this, orderedImages));
+        galleryModal.onkeydown = debounce(250, this.handleModalKeydown.bind(this, orderedImages));
+      }
 
       document.body.appendChild(galleryModal);
 
@@ -401,7 +399,7 @@ export default class CIResponsive {
 
       galleryModal.tabIndex = 0;
       galleryModal.appendChild(previewModule);
-      galleryModal.onkeydown = debounce(100, this.handleModalKeydown.bind(this, undefined));
+      galleryModal.onkeydown = debounce(50, this.handleModalKeydown.bind(this, undefined));
 
       document.body.appendChild(galleryModal);
       galleryModal.focus();
@@ -473,25 +471,32 @@ export default class CIResponsive {
         setSrc(previewImgNode, previewImgURL, 'data-src', lazy, src, isSVG, dataSrcAttr);
 
         previewImgNode.onload = () => {
-          previewImgNode.classList.add(CLASSNAMES.PREVIEW_LOADED);
+          addClass(previewImgNode, CLASSNAMES.PREVIEW_LOADED);
           onPreviewImageLoad(wrapper, previewImgNode, ratio, preserveSize);
         };
       }
     }
 
     getDimAndFit(imgNode);
+
     if ((gallery || zoom) && !isProcessedByGallery) {
       wrapper.style.cursor = 'pointer';
+
       if (gallery) {
-        wrapper.classList.add(CLASSNAMES.GALLERY_ANIMATION);
+        addClass(wrapper, CLASSNAMES.GALLERY_ANIMATION);
       } else {
         const zoomIcon = createIcon(zoomIconSvg, CLASSNAMES.ZOOM_BUTTON, ICONS_STYLES.ZOOM);
         wrapper.append(zoomIcon);
       }
     }
 
-    wrapper.onclick = this.handleClickWrapper.bind(this, imgProps, images);
     imgNode.onload = () => {
+      const loader = document.querySelector(`.${CLASSNAMES.GALLERY_LOADER}`);
+
+      if (loader) {
+        loader.parentElement.removeChild(loader);
+      }
+
       if (config.onImageLoad && typeof config.onImageLoad === 'function') {
         config.onImageLoad(imgNode);
       }
@@ -499,6 +504,8 @@ export default class CIResponsive {
       if (!isProcessedByGallery) {
         onImageLoad(wrapper, previewImgNode, imgNode, ratio, preserveSize, isAdaptive);
       }
+
+      wrapper.onclick = this.handleClickWrapper.bind(this, imgProps, images);
     };
 
     setSrcset(imgNode, cloudimageSrcset, 'data-srcset', lazy, src, isSVG, dataSrcAttr);
diff --git a/src/low-preview/ci.styles.css b/src/low-preview/ci.styles.css
index 01b3733..13d0b87 100644
--- a/src/low-preview/ci.styles.css
+++ b/src/low-preview/ci.styles.css
@@ -244,6 +244,7 @@ img.ci-image-loaded {
   top: 50%;
   left: 50%;
   animation: spin 2s linear infinite;
+  z-index: -1;
 }
 
 @keyframes slideVertical {
diff --git a/src/low-preview/gallery.utils.js b/src/low-preview/gallery.utils.js
index 6e6ebeb..4b1fc79 100644
--- a/src/low-preview/gallery.utils.js
+++ b/src/low-preview/gallery.utils.js
@@ -6,21 +6,14 @@ const createIcon = (iconSrc, className, iconStyles) => {
   const { color, width = 15, height = 15 } = iconStyles;
 
   const iconWrapper = document.createElement('div');
-  let icon;
 
-  if (iconSrc.tagName === 'IMG') {
-    icon = iconSrc;
-  } else {
-    icon = new Image();
-    icon.src = iconSrc;
-  }
+  const icon = new Image();
+  icon.src = iconSrc;
 
   icon.style.width = `${width}px`;
   icon.style.height = `${height}px`;
 
-  if (className) {
-    iconWrapper.classList.add(className);
-  }
+  addClass(iconWrapper, className);
 
   if (color) {
     iconWrapper.style.backgroundColor = color;
@@ -48,47 +41,38 @@ const createGalleryModal = (closeIconSrc, galleryConfigs, galleryLength, isGalle
     modalClassName, previewClassName, thumbnailsClassName, closeIcon, close, onClose,
   } = galleryConfigs;
 
-  let closeIcn = null;
-
   const galleryModal = document.createElement('div');
   const previewModule = document.createElement('div');
   const loader = document.createElement('div');
 
-  if (closeIcon) {
-    closeIcn = createIcon(closeIcon, CLASSNAMES.CLOSE_BUTTON, ICONS_STYLES.COLOR);
-  } else {
-    closeIcn = createIcon(closeIconSrc, CLASSNAMES.CLOSE_BUTTON, ICONS_STYLES.COLOR);
-  }
-
   galleryModal.tabIndex = 0;
-  galleryModal.classList.add(CLASSNAMES.GALLERY_MODAL);
-  previewModule.classList.add(CLASSNAMES.PREVIEW_MODULE);
+  addClass(galleryModal, CLASSNAMES.GALLERY_MODAL);
+  addClass(previewModule, CLASSNAMES.PREVIEW_MODULE);
   galleryModal.setAttribute(ATTRIBUTES.GALLERY, true);
-  loader.classList.add('ci-gallery-loader');
+  addClass(loader, CLASSNAMES.GALLERY_LOADER);
   galleryModal.append(previewModule);
   galleryModal.append(loader);
 
   if (close) {
-    galleryModal.append(closeIcn);
-  }
+    let _closeIcon = closeIcon || closeIconSrc;
 
-  closeIcn.onclick = destroyGallery.bind(this, onClose);
+    if (typeof _closeIcon === 'string') {
+      _closeIcon = createIcon(_closeIcon, CLASSNAMES.CLOSE_BUTTON, ICONS_STYLES.COLOR);
+    }
 
-  if (modalClassName) {
-    galleryModal.classList.add(modalClassName);
+    _closeIcon.onclick = destroyGallery.bind(this, onClose);
+    galleryModal.append(_closeIcon);
   }
 
-  if (previewClassName) {
-    previewModule.classList.add(previewClassName);
-  }
+  addClass(galleryModal, modalClassName);
+  addClass(previewModule, previewClassName);
 
   if (isGallery) {
     const thumbnailsModule = document.createElement('div');
-    thumbnailsModule.classList.add(CLASSNAMES.THUMBNAIL_MODULE);
 
-    if (thumbnailsClassName) {
-      thumbnailsModule.classList.add(thumbnailsClassName);
-    }
+    addClass(thumbnailsModule, CLASSNAMES.THUMBNAIL_MODULE);
+    addClass(thumbnailsModule, thumbnailsClassName);
+
     galleryModal.setAttribute(ATTRIBUTES.GALLERY_LENGTH, galleryLength);
     galleryModal.setAttribute(ATTRIBUTES.GALLERY_INDEX, 0);
     galleryModal.append(thumbnailsModule);
@@ -145,21 +129,18 @@ const createGalleryArrows = (leftArrowIcon, rightArrowIcon, galleryConfigs, onCl
   const {
     arrowPrevIcon, arrowNextIcon, onPrev, onNext,
   } = galleryConfigs;
-  let leftArrow;
-  let rightArrow;
 
-  if (arrowPrevIcon) {
-    leftArrow = createIcon(arrowPrevIcon, CLASSNAMES.LEFT_ARROW_BUTTON, ICONS_STYLES.COLOR);
-  } else {
-    leftArrow = createIcon(leftArrowIcon, CLASSNAMES.LEFT_ARROW_BUTTON, ICONS_STYLES.COLOR);
+  let leftArrow = arrowPrevIcon || leftArrowIcon;
+  if (typeof leftArrow === 'string') {
+    leftArrow = createIcon(leftArrow, CLASSNAMES.LEFT_ARROW_BUTTON, ICONS_STYLES.COLOR);
   }
 
-  if (arrowNextIcon) {
-    rightArrow = createIcon(arrowNextIcon, CLASSNAMES.RIGHT_ARROW_BUTTON, ICONS_STYLES.COLOR);
-  } else {
-    rightArrow = createIcon(rightArrowIcon, CLASSNAMES.RIGHT_ARROW_BUTTON, ICONS_STYLES.COLOR);
+  let rightArrow = arrowNextIcon || rightArrowIcon;
+  if (typeof rightArrow === 'string') {
+    rightArrow = createIcon(rightArrow, CLASSNAMES.RIGHT_ARROW_BUTTON, ICONS_STYLES.COLOR);
   }
 
+
   if (onClick) {
     leftArrow.onclick = onClick.bind(this, 'left');
     rightArrow.onclick = onClick.bind(this, 'right');
@@ -229,7 +210,7 @@ const adaptGalleryThumbnails = (images = [], onClickThumbnail, onClick) => {
     image.style.width = imageFitStyles.width;
     image.style.height = imageFitStyles.height;
 
-    thmbnailContainer.classList.add(CLASSNAMES.THUMBNAIL_CONTAINER);
+    addClass(thmbnailContainer, CLASSNAMES.THUMBNAIL_CONTAINER);
     thmbnailContainer.setAttribute(ATTRIBUTES.GALLERY_INDEX, index);
     thmbnailContainer.append(image);
 

From 2957499740fbabae5420290b176a29bbc0a66beb Mon Sep 17 00:00:00 2001
From: Nermeen Moustafa 
Date: Fri, 23 Sep 2022 20:57:13 +0200
Subject: [PATCH 40/45] code refactor - T7582

---
 src/common/ci.constants.js       |  2 +-
 src/low-preview/ci.styles.css    | 49 ++++++++++++++---------------
 src/low-preview/gallery.utils.js | 53 +++++++++++++++-----------------
 3 files changed, 49 insertions(+), 55 deletions(-)

diff --git a/src/common/ci.constants.js b/src/common/ci.constants.js
index 56b50c1..09ad6a1 100644
--- a/src/common/ci.constants.js
+++ b/src/common/ci.constants.js
@@ -22,6 +22,7 @@ const CLASSNAMES = {
   GALLERY_LOADER: 'ci-gallery-loader',
   THUMBNAIL_MODULE: 'ci-gallery-thumbnail-module',
   THUMBNAIL_CONTAINER: 'ci-gallery-thmbnail-container',
+  GALLERY_ICON_BUTTON: 'ci-gallery-icon-button',
   CLOSE_BUTTON: 'ci-gallery-close-button',
   LEFT_ARROW_BUTTON: 'ci-gallery-left-arrow-button',
   RIGHT_ARROW_BUTTON: 'ci-gallery-right-arrow-button',
@@ -33,7 +34,6 @@ const CLASSNAMES = {
 
 const ICONS_STYLES = {
   ZOOM: { width: 35, height: 35 },
-  COLOR: { color: 'rgba(255,255,255,0.5)' },
 };
 
 export {
diff --git a/src/low-preview/ci.styles.css b/src/low-preview/ci.styles.css
index 13d0b87..3f1c67f 100644
--- a/src/low-preview/ci.styles.css
+++ b/src/low-preview/ci.styles.css
@@ -110,20 +110,6 @@ img.ci-image-loaded {
   overflow: hidden;
 }
 
-.ci-gallery-close-button {
-  width: 23px;
-  height: 23px;
-  display: flex;
-  justify-content: center;
-  align-items: center;
-  border-radius: 50%;
-  position: absolute;
-  top: 15px;
-  right: 15px;
-  z-index: 111;
-  cursor: pointer;
-}
-
 .ci-gallery-thumbnail-module {
   display: flex;
   position: absolute;
@@ -186,33 +172,44 @@ img.ci-image-loaded {
   animation: borderFade 250ms ease-in-out;
 }
 
+.ci-gallery-close-button {
+  width: 23px;
+  height: 23px;
+  top: 15px;
+  right: 15px;
+  border-radius: 50%;
+}
+
 .ci-gallery-right-arrow-button {
   width: 25px;
   height: 25px;
-  display: flex;
-  justify-content: center;
-  align-items: center;
-  border-radius: 50%;
-  position: absolute;
-  position: absolute;
   top: 50%;
   right: 15px;
-  z-index: 111;
-  cursor: pointer;
+  border-radius: 50%;
 }
 
 .ci-gallery-left-arrow-button {
   width: 25px;
   height: 25px;
+  top: 50%;
+  left: 15px;
+  border-radius: 50%;
+}
+
+.ci-gallery-icon-button {
   display: flex;
   justify-content: center;
   align-items: center;
-  border-radius: 50%;
   position: absolute;
-  top: 50%;
-  left: 15px;
-  z-index: 111;
   cursor: pointer;
+  z-index: 111;
+  background-color: rgba(255,255,255,0.5);
+}
+
+.ci-gallery-close-button:hover,
+.ci-gallery-right-arrow-button:hover,
+.ci-gallery-left-arrow-button:hover {
+  background-color: rgba(255,255,255,0.8);
 }
 
 .ci-image-wrapper:hover > .ci-gallery-zoom-button {
diff --git a/src/low-preview/gallery.utils.js b/src/low-preview/gallery.utils.js
index 4b1fc79..8b0fd49 100644
--- a/src/low-preview/gallery.utils.js
+++ b/src/low-preview/gallery.utils.js
@@ -1,23 +1,17 @@
 import { getCommonImageProps, addClass } from '../common/ci.utils';
-import { ATTRIBUTES, CLASSNAMES, ICONS_STYLES } from '../common/ci.constants';
+import { ATTRIBUTES, CLASSNAMES } from '../common/ci.constants';
 
 
 const createIcon = (iconSrc, className, iconStyles) => {
-  const { color, width = 15, height = 15 } = iconStyles;
-
   const iconWrapper = document.createElement('div');
 
   const icon = new Image();
   icon.src = iconSrc;
 
-  icon.style.width = `${width}px`;
-  icon.style.height = `${height}px`;
-
-  addClass(iconWrapper, className);
+  icon.style.width = `${iconStyles ? iconStyles.width : '15'}px`;
+  icon.style.height = `${iconStyles ? iconStyles.height : '15'}px`;
 
-  if (color) {
-    iconWrapper.style.backgroundColor = color;
-  }
+  iconWrapper.classList.add(className, CLASSNAMES.GALLERY_ICON_BUTTON);
 
   iconWrapper.appendChild(icon);
 
@@ -37,10 +31,6 @@ const destroyGallery = (onClose, event) => {
 };
 
 const createGalleryModal = (closeIconSrc, galleryConfigs, galleryLength, isGallery) => {
-  const {
-    modalClassName, previewClassName, thumbnailsClassName, closeIcon, close, onClose,
-  } = galleryConfigs;
-
   const galleryModal = document.createElement('div');
   const previewModule = document.createElement('div');
   const loader = document.createElement('div');
@@ -53,29 +43,36 @@ const createGalleryModal = (closeIconSrc, galleryConfigs, galleryLength, isGalle
   galleryModal.append(previewModule);
   galleryModal.append(loader);
 
-  if (close) {
-    let _closeIcon = closeIcon || closeIconSrc;
+  if (isGallery) {
+    const {
+      modalClassName, previewClassName, thumbnailsClassName, closeIcon, close, onClose,
+    } = galleryConfigs;
 
-    if (typeof _closeIcon === 'string') {
-      _closeIcon = createIcon(_closeIcon, CLASSNAMES.CLOSE_BUTTON, ICONS_STYLES.COLOR);
-    }
+    const thumbnailsModule = document.createElement('div');
 
-    _closeIcon.onclick = destroyGallery.bind(this, onClose);
-    galleryModal.append(_closeIcon);
-  }
+    if (close) {
+      let _closeIcon = closeIcon || closeIconSrc;
 
-  addClass(galleryModal, modalClassName);
-  addClass(previewModule, previewClassName);
+      if (typeof _closeIcon === 'string') {
+        _closeIcon = createIcon(_closeIcon, CLASSNAMES.CLOSE_BUTTON);
+      }
 
-  if (isGallery) {
-    const thumbnailsModule = document.createElement('div');
+      _closeIcon.onclick = destroyGallery.bind(this, onClose);
+      galleryModal.append(_closeIcon);
+    }
 
+    addClass(galleryModal, modalClassName);
+    addClass(previewModule, previewClassName);
     addClass(thumbnailsModule, CLASSNAMES.THUMBNAIL_MODULE);
     addClass(thumbnailsModule, thumbnailsClassName);
 
     galleryModal.setAttribute(ATTRIBUTES.GALLERY_LENGTH, galleryLength);
     galleryModal.setAttribute(ATTRIBUTES.GALLERY_INDEX, 0);
     galleryModal.append(thumbnailsModule);
+  } else {
+    const closeIcon = createIcon(closeIconSrc, CLASSNAMES.CLOSE_BUTTON);
+    closeIcon.onclick = destroyGallery.bind(this);
+    galleryModal.append(closeIcon);
   }
 
   return galleryModal;
@@ -132,12 +129,12 @@ const createGalleryArrows = (leftArrowIcon, rightArrowIcon, galleryConfigs, onCl
 
   let leftArrow = arrowPrevIcon || leftArrowIcon;
   if (typeof leftArrow === 'string') {
-    leftArrow = createIcon(leftArrow, CLASSNAMES.LEFT_ARROW_BUTTON, ICONS_STYLES.COLOR);
+    leftArrow = createIcon(leftArrow, CLASSNAMES.LEFT_ARROW_BUTTON);
   }
 
   let rightArrow = arrowNextIcon || rightArrowIcon;
   if (typeof rightArrow === 'string') {
-    rightArrow = createIcon(rightArrow, CLASSNAMES.RIGHT_ARROW_BUTTON, ICONS_STYLES.COLOR);
+    rightArrow = createIcon(rightArrow, CLASSNAMES.RIGHT_ARROW_BUTTON);
   }
 
 

From d4d3806b23ce25d65971e28b46f02e21ebac4f97 Mon Sep 17 00:00:00 2001
From: Nermeen Moustafa 
Date: Tue, 27 Sep 2022 13:51:52 +0200
Subject: [PATCH 41/45] add gallery section in demo - T7582

---
 examples/low-preview/src/index.html      | 108 +++++++++++++++++++++--
 examples/low-preview/src/styles/main.css |  29 ++++++
 2 files changed, 128 insertions(+), 9 deletions(-)

diff --git a/examples/low-preview/src/index.html b/examples/low-preview/src/index.html
index c05ce9e..a8162af 100644
--- a/examples/low-preview/src/index.html
+++ b/examples/low-preview/src/index.html
@@ -205,8 +205,6 @@ 

Responsive images, in real-time!

id="left-column-image" ci-params="ci_info=2" ci-ratio="0.942" - ci-gallery="gallery1" - ci-image-name= 'House+img.jpg' />
@@ -223,8 +221,6 @@

Responsive images, in real-time!

id="right-column-first-image" ci-params="ci_info=2" ci-ratio="1" - ci-gallery="gallery2" - ci-image-name= 'Church+square+img.jpg' />

@@ -239,8 +235,6 @@

Responsive images, in real-time!

alt="img" ci-params="w=265&h=265&gravity=face&radius=max" id="right-column-second-image" - ci-gallery="gallery2" - ci-image-name= 'Girl+img.jpg' />
@@ -258,8 +252,6 @@

Responsive images, in real-time!

alt="img" ci-ratio="2.885" ci-params="ci_info=2" - ci-gallery="gallery2" - ci-image-name = 'Dresses+img.jpg' />

@@ -276,7 +268,6 @@

Responsive images, in real-time!

ci-ratio="2.885" id="second-horizontal-image" ci-params="ci_info=2" - ci-gallery="gallery1" />

@@ -352,6 +343,105 @@

Auto Crop

+ +
Date: Tue, 27 Sep 2022 14:40:00 +0200 Subject: [PATCH 42/45] add disable Animation prop - T7582 --- src/common/ci.utils.js | 1 + src/low-preview/ci.service.js | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/common/ci.utils.js b/src/common/ci.utils.js index 4c755df..44c7245 100644 --- a/src/common/ci.utils.js +++ b/src/common/ci.utils.js @@ -74,6 +74,7 @@ const getCommonImageProps = (image) => ({ zoom: isTrue(image, 'ci-zoom'), gallery: attr(image, 'ci-gallery') || undefined, imageName: attr(image, 'ci-image-name') || undefined, + disableAnimation: isTrue(image, 'disableAnimation'), }); const filterImages = (images, type) => { diff --git a/src/low-preview/ci.service.js b/src/low-preview/ci.service.js index 643f4bd..98645a0 100644 --- a/src/low-preview/ci.service.js +++ b/src/low-preview/ci.service.js @@ -435,7 +435,7 @@ export default class CIResponsive { } = props; const { - params, zoom, gallery, isProcessedByGallery, + params, zoom, gallery, disableAnimation, isProcessedByGallery, } = imgProps; const { width, ratio } = containerProps; const { config } = this; @@ -482,9 +482,9 @@ export default class CIResponsive { if ((gallery || zoom) && !isProcessedByGallery) { wrapper.style.cursor = 'pointer'; - if (gallery) { + if (gallery && !disableAnimation) { addClass(wrapper, CLASSNAMES.GALLERY_ANIMATION); - } else { + } else if (zoom && !gallery) { const zoomIcon = createIcon(zoomIconSvg, CLASSNAMES.ZOOM_BUTTON, ICONS_STYLES.ZOOM); wrapper.append(zoomIcon); } From 6834acd2299e50752108b1da70e2f1030b224493 Mon Sep 17 00:00:00 2001 From: Nermeen Moustafa Date: Tue, 27 Sep 2022 18:30:23 +0200 Subject: [PATCH 43/45] code refactor - T7582 --- src/common/ci.constants.js | 5 ----- src/low-preview/ci.service.js | 4 ++-- src/low-preview/ci.styles.css | 12 ++++++++++++ src/low-preview/gallery.utils.js | 5 +---- 4 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/common/ci.constants.js b/src/common/ci.constants.js index 09ad6a1..715c7f0 100644 --- a/src/common/ci.constants.js +++ b/src/common/ci.constants.js @@ -32,13 +32,8 @@ const CLASSNAMES = { IMAGE_LOADED: 'ci-image-loaded', }; -const ICONS_STYLES = { - ZOOM: { width: 35, height: 35 }, -}; - export { loadedImageClassNames, ATTRIBUTES, CLASSNAMES, - ICONS_STYLES, }; diff --git a/src/low-preview/ci.service.js b/src/low-preview/ci.service.js index 98645a0..67a2d25 100644 --- a/src/low-preview/ci.service.js +++ b/src/low-preview/ci.service.js @@ -51,7 +51,7 @@ import { updateSizeWithPixelRatio, } from './ci.utis'; import { - loadedImageClassNames, ATTRIBUTES, CLASSNAMES, ICONS_STYLES, + loadedImageClassNames, ATTRIBUTES, CLASSNAMES, } from '../common/ci.constants'; import closeIconSvg from '../public/close-icon.svg'; import rightArrowSvg from '../public/right-arrow-icon.svg'; @@ -485,7 +485,7 @@ export default class CIResponsive { if (gallery && !disableAnimation) { addClass(wrapper, CLASSNAMES.GALLERY_ANIMATION); } else if (zoom && !gallery) { - const zoomIcon = createIcon(zoomIconSvg, CLASSNAMES.ZOOM_BUTTON, ICONS_STYLES.ZOOM); + const zoomIcon = createIcon(zoomIconSvg, CLASSNAMES.ZOOM_BUTTON); wrapper.append(zoomIcon); } } diff --git a/src/low-preview/ci.styles.css b/src/low-preview/ci.styles.css index 3f1c67f..0510001 100644 --- a/src/low-preview/ci.styles.css +++ b/src/low-preview/ci.styles.css @@ -206,6 +206,13 @@ img.ci-image-loaded { background-color: rgba(255,255,255,0.5); } +.ci-gallery-close-button img, +.ci-gallery-right-arrow-button img, +.ci-gallery-left-arrow-button img { + width: 15px; + height: 15px; +} + .ci-gallery-close-button:hover, .ci-gallery-right-arrow-button:hover, .ci-gallery-left-arrow-button:hover { @@ -231,6 +238,11 @@ img.ci-image-loaded { transition: transform 300ms ease-in-out; } +.ci-gallery-zoom-button img { + width: 35px; + height: 35px; +} + .ci-gallery-loader { border: 3px solid #3498db; border-top: 3px solid white; diff --git a/src/low-preview/gallery.utils.js b/src/low-preview/gallery.utils.js index 8b0fd49..aaf0dfc 100644 --- a/src/low-preview/gallery.utils.js +++ b/src/low-preview/gallery.utils.js @@ -2,15 +2,12 @@ import { getCommonImageProps, addClass } from '../common/ci.utils'; import { ATTRIBUTES, CLASSNAMES } from '../common/ci.constants'; -const createIcon = (iconSrc, className, iconStyles) => { +const createIcon = (iconSrc, className) => { const iconWrapper = document.createElement('div'); const icon = new Image(); icon.src = iconSrc; - icon.style.width = `${iconStyles ? iconStyles.width : '15'}px`; - icon.style.height = `${iconStyles ? iconStyles.height : '15'}px`; - iconWrapper.classList.add(className, CLASSNAMES.GALLERY_ICON_BUTTON); iconWrapper.appendChild(icon); From 3987771f26a0cef421391893f9c5458e65c5b299 Mon Sep 17 00:00:00 2001 From: Nermeen Moustafa Date: Wed, 28 Sep 2022 10:17:02 +0200 Subject: [PATCH 44/45] add zoom section in demo - T7582 --- examples/low-preview/src/index.html | 193 +++++++++++++---------- examples/low-preview/src/styles/main.css | 31 +++- 2 files changed, 136 insertions(+), 88 deletions(-) diff --git a/examples/low-preview/src/index.html b/examples/low-preview/src/index.html index a8162af..d1a100a 100644 --- a/examples/low-preview/src/index.html +++ b/examples/low-preview/src/index.html @@ -293,7 +293,6 @@

Original Image

ci-src="shayna-douglas-VibRcV8tMDM-unsplash.jpg" ci-ratio="1" alt="car-image" - ci-zoom />
 <img
@@ -344,102 +343,126 @@ 

Auto Crop

+ +
+

Zoom in Images

+
+ windmill + landscape + port
- sea - landscape - windmill - fox
Date: Mon, 3 Oct 2022 17:05:58 +0200 Subject: [PATCH 45/45] code review - T7582 --- examples/low-preview/src/index.html | 90 ++++++++++++++---------- examples/low-preview/src/styles/main.css | 64 ++++++++++++++--- src/low-preview/ci.service.js | 5 ++ src/low-preview/ci.styles.css | 22 ++++-- src/low-preview/gallery.utils.js | 9 ++- 5 files changed, 136 insertions(+), 54 deletions(-) diff --git a/examples/low-preview/src/index.html b/examples/low-preview/src/index.html index d1a100a..a5fd297 100644 --- a/examples/low-preview/src/index.html +++ b/examples/low-preview/src/index.html @@ -342,70 +342,102 @@

Auto Crop

+ +
+

Zoom in Images

+
+ windmill + landscape + port +
+
+ - -
-

Zoom in Images

-
- windmill - landscape - port
diff --git a/examples/low-preview/src/styles/main.css b/examples/low-preview/src/styles/main.css index 807a629..5ddd833 100644 --- a/examples/low-preview/src/styles/main.css +++ b/examples/low-preview/src/styles/main.css @@ -490,12 +490,18 @@ body { font-weight: 500; } -.gallery-section h1 { +.gallery-section { + background-color: #f2f7fa; + padding: 50px 50px 80px 50px; + margin-bottom: 80px; +} + +.gallery-section h2 { font-size: 40px; - line-height: 54px; - font-weight: 400px; + font-weight: 600; + color: #203254; text-align: center; - margin: 50px 0px 35px; + margin-bottom: 30px; } .gallery { @@ -503,9 +509,6 @@ body { display: grid; grid-template-columns: repeat(6, 1fr); grid-auto-rows: repeat(4, 1fr); - padding: 60px 25px; - background-color: rgb(0, 0, 0); - margin-bottom: 100px; } .gallery img { @@ -515,6 +518,7 @@ body { .bird { grid-column: 1/3; grid-row: 1/3; + box-sizing: content-box; } .stellers { @@ -527,11 +531,16 @@ body { grid-row: 3/5; } -.zoom-section h1 { - font-size: 40px; +.zoom-section { + padding: 0px 5%; +} + +.zoom-section h2 { + font-size: 37px; line-height: 54px; - font-weight: 400px; + font-weight: 600; text-align: center; + color: #203254; margin: 50px 0px 35px; } @@ -541,7 +550,40 @@ body { grid-template-columns: repeat(3, 1fr); column-gap: 15px; padding: 0px 25px; - margin-bottom: 100px; + margin-bottom: 80px; +} + +@media (max-width: 700px) { + .gallery { + grid-template-columns: repeat(3, 1fr); + grid-auto-rows: repeat(8, 1fr); + } + + .gallery-section { + padding: 20px 20px 40px 20px; + } + + .gallery-section h2 { + font-size: 30px; + } + + .stellers { + grid-column: 1/3; + grid-row: 7/9; + } + + .road { + grid-column: 2/4; + grid-row: 4/6; + } + + .zoom-section h2 { + font-size: 30px; + } + + .zoom { + column-gap: 10px; + } } .background-images-section { diff --git a/src/low-preview/ci.service.js b/src/low-preview/ci.service.js index 67a2d25..7d6bff9 100644 --- a/src/low-preview/ci.service.js +++ b/src/low-preview/ci.service.js @@ -295,6 +295,7 @@ export default class CIResponsive { const { imgSelector } = this.config; const { imageName, alt } = getImageProps(imgNode, imgSelector); const galleryModal = document.querySelector(`.${CLASSNAMES.GALLERY_MODAL}`); + const thumbnailsModule = galleryModal.querySelector(`.${CLASSNAMES.THUMBNAIL_MODULE}`); const _imgNode = imgNode.cloneNode(); const adaptedImageNode = removeClassNames(_imgNode, loadedImageClassNames); const previewModule = getGalleryPreviewModule(); @@ -308,6 +309,10 @@ export default class CIResponsive { previewModule.innerHTML = ''; previewModule.appendChild(adaptedImageNode); + if (thumbnailsModule.scrollWidth > thumbnailsModule.clientWidth) { + thumbnailsModule.style.justifyContent = 'left'; + } + if (!intial) { this.animatePreviewModule(previewModule, imageIndex, direction); } diff --git a/src/low-preview/ci.styles.css b/src/low-preview/ci.styles.css index 0510001..f8d8b7a 100644 --- a/src/low-preview/ci.styles.css +++ b/src/low-preview/ci.styles.css @@ -111,15 +111,15 @@ img.ci-image-loaded { } .ci-gallery-thumbnail-module { + margin: 0% 5%; display: flex; position: absolute; align-items: center; justify-content: center; - column-gap: 5px; - overflow: auto hidden; + overflow: auto; white-space: nowrap; - width: 100%; - height: 40px; + width: 90%; + height: fit-content; bottom: 10px; left: 0; animation: slideHorizontal 400ms cubic-bezier(0.075, 0.42, 0.165, 1); @@ -256,6 +256,20 @@ img.ci-image-loaded { z-index: -1; } +@media (max-width: 400px){ + .ci-gallery-zoom-button img { + width: 15px; + height: 15px; + } +} + +@media (max-width: 600px){ + .ci-gallery-zoom-button img { + width: 25px; + height: 25px; + } +} + @keyframes slideVertical { 0% { transform: translateY(1000px); } 100% { transform: translateY(0); } diff --git a/src/low-preview/gallery.utils.js b/src/low-preview/gallery.utils.js index aaf0dfc..fd219a6 100644 --- a/src/low-preview/gallery.utils.js +++ b/src/low-preview/gallery.utils.js @@ -103,6 +103,9 @@ const toggleActiveThumbnail = (galleryModal, imageIndex) => { if (+imageIndex === +thumbnailContainerIndex) { thumbnailContainer.setAttribute(ATTRIBUTES.ACTIVE_THUMBNAIL, true); + thumbnailContainer.scrollIntoView({ + behavior: 'smooth', + }); } }); }; @@ -176,7 +179,7 @@ const getImageFitStyles = (naturalWidth, naturalHeight) => { if (naturalWidth && previewModule) { const imageAspectRatio = naturalWidth / naturalHeight; const renderWidth = previewModule.offsetHeight * imageAspectRatio; - shouldFitHorizontally = (imageAspectRatio <= 1) + shouldFitHorizontally = (imageAspectRatio >= 1) && (renderWidth < previewModule.offsetWidth); } @@ -192,7 +195,9 @@ const getImageFitStyles = (naturalWidth, naturalHeight) => { }; const adaptGalleryThumbnails = (images = [], onClickThumbnail, onClick) => { - const lowPreviewImages = images.map((image) => image.previousSibling.firstChild); + const lowPreviewImages = images.map((image) => ( + image.previousSibling ? image.previousSibling.firstChild : image + )); return lowPreviewImages.map((thumbnail, index) => { const thmbnailContainer = document.createElement('div');