Skip to content

Commit

Permalink
feat: add single type image to gallery component
Browse files Browse the repository at this point in the history
  • Loading branch information
ViralLka committed Nov 15, 2024
1 parent aae14d9 commit c0fd8cf
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 16 deletions.
3 changes: 2 additions & 1 deletion packages/gallery/src/Component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import SwiperCore from 'swiper';
import { BaseModal } from '@alfalab/core-components-base-modal';
import { useMedia } from '@alfalab/hooks';

import { SingleImageViewer } from './components/image-viewer/single-image-viewer';
import { Header, HeaderMobile, ImageViewer, InfoBar, NavigationBar } from './components';
import { GalleryContext } from './context';
import { GalleryImage, ImageMeta } from './types';
Expand Down Expand Up @@ -242,7 +243,7 @@ export const Gallery: FC<GalleryProps> = ({
>
<div className={styles.container}>
{view === 'desktop' ? <Header /> : <HeaderMobile />}
<ImageViewer />
{images.length === 1 ? <SingleImageViewer /> : <ImageViewer />}
<nav
className={cn({
[styles.navigationVideo]: isCurrentVideo && isMobile,
Expand Down
11 changes: 4 additions & 7 deletions packages/gallery/src/components/image-viewer/component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ SwiperCore.use([EffectFade, A11y, Controller]);

export const ImageViewer: FC = () => {
const {
singleSlide,
images,
imagesMeta,
fullScreen,
Expand Down Expand Up @@ -126,7 +125,7 @@ export const ImageViewer: FC = () => {
},
className: cn(styles.swiper, {
[styles.hidden]: fullScreen && !isVideo(currentImage?.src),
[styles.fullScreenVideo]: fullScreen && !singleSlide && isVideo(currentImage?.src),
[styles.fullScreenVideo]: fullScreen && isVideo(currentImage?.src),
[styles.mobile]: isMobile,
[styles.mobileVideo]: isMobile && isVideo(currentImage?.src),
}),
Expand All @@ -143,7 +142,6 @@ export const ImageViewer: FC = () => {
[
fullScreen,
currentImage?.src,
singleSlide,
isMobile,
swiper,
initialSlide,
Expand All @@ -152,7 +150,7 @@ export const ImageViewer: FC = () => {
],
);

const showControls = !singleSlide && !fullScreen && !isMobile && !!images.length;
const showControls = !fullScreen && !isMobile && !!images.length;

const swiperWidth = swiper?.width || 1;
const swiperHeight = swiper?.height || swiper?.width || 1;
Expand All @@ -163,7 +161,6 @@ export const ImageViewer: FC = () => {
/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */
<div
className={cn(styles.component, {
[styles.singleSlide]: singleSlide,
[styles.mobile]: isMobile,
[styles.mobileVideo]: isMobile && isVideo(currentImage?.src),
})}
Expand Down Expand Up @@ -216,9 +213,9 @@ export const ImageViewer: FC = () => {
{({ isActive }) => (
<Slide
isActive={isActive}
swiperAspectRatio={swiperAspectRatio}
containerAspectRatio={swiperAspectRatio}
image={image}
swiperHeight={swiperHeight}
containerHeight={swiperHeight}
meta={meta}
index={index}
imageAspectRatio={imageAspectRatio}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,14 @@
}
}

.singleSlide .swiper {
.singleSlideContainer {
display: flex;
width: 100%;
height: 100%;

max-height: calc(100vh - 80px);
padding: var(--gap-32);
box-sizing: border-box;

&.mobile {
max-height: calc(100vh - 174px);
Expand Down
119 changes: 119 additions & 0 deletions packages/gallery/src/components/image-viewer/single-image-viewer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import React, {
FC,
MouseEventHandler,
SyntheticEvent,
useCallback,
useContext,
useEffect,
useRef,
} from 'react';
import cn from 'classnames';
import elementClosest from 'element-closest';

import { GalleryContext } from '../../context';
import { getImageAlt, isVideo } from '../../utils';

import { Slide } from './slide';

import styles from './index.module.css';

export const SingleImageViewer: FC = () => {
const {
fullScreen,
currentSlideIndex,
onClose,
setImageMeta,
getCurrentImage,
getCurrentImageMeta,
view,
} = useContext(GalleryContext);

const isMobile = view === 'mobile';

const leftArrowRef = useRef<HTMLDivElement>(null);
const rightArrowRef = useRef<HTMLDivElement>(null);
const wrapperRef = useRef<HTMLDivElement>(null);

useEffect(() => {
elementClosest(window);
}, []);

const handleLoad = (event: SyntheticEvent<HTMLImageElement>, index: number) => {
const target = event.currentTarget;

const { naturalWidth, naturalHeight } = target;

setImageMeta({ width: naturalWidth, height: naturalHeight }, index);
};

const handleLoadError = (index: number) => {
setImageMeta({ width: 0, height: 0, broken: true }, index);
};

const handleWrapperClick = useCallback<MouseEventHandler>(
(event) => {
const eventTarget = event.target as HTMLElement;

const isArrow =
leftArrowRef.current?.contains(eventTarget) ||
rightArrowRef.current?.contains(eventTarget);

const isPlaceholder = Boolean(eventTarget.closest(`.${styles.placeholder}`));

const isImg = eventTarget.tagName === 'IMG';

if (!isImg && !isPlaceholder && !isArrow && !isMobile) {
onClose();
}
},
[isMobile, onClose],
);

const currentImage = getCurrentImage();
const currentImageMeta = getCurrentImageMeta();

if (!currentImage) return null;

const imageWidth = currentImageMeta?.width || 1;
const imageHeight = currentImageMeta?.height || 1;

const imageAspectRatio = imageWidth / imageHeight;

const wrapperRect = wrapperRef.current?.getBoundingClientRect();

const wrapperAspectRatio = (wrapperRect?.width || 1) / (wrapperRect?.height || 1);

return (
/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */
<div className={styles.component} onClick={handleWrapperClick}>
{fullScreen && !isVideo(currentImage?.src) ? (
<img
src={currentImage?.src}
alt={currentImage ? getImageAlt(currentImage, currentSlideIndex) : ''}
className={styles.fullScreenImage}
/>
) : (
<div
className={cn(styles.singleSlideContainer, {
[styles.mobile]: isMobile,
[styles.mobileVideo]: isMobile && isVideo(currentImage?.src),
})}
ref={wrapperRef}
>
<Slide
isActive={true}
containerAspectRatio={wrapperAspectRatio}
image={currentImage}
containerHeight={wrapperRect?.height || 0}
meta={currentImageMeta}
index={0}
imageAspectRatio={imageAspectRatio}
slideVisible={true}
handleLoad={handleLoad}
handleLoadError={handleLoadError}
/>
</div>
)}
</div>
);
};
14 changes: 7 additions & 7 deletions packages/gallery/src/components/image-viewer/slide.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,10 @@ type SlideProps = {
isActive: boolean;
image: GalleryImage;
meta?: ImageMeta;
swiperAspectRatio: number;
containerAspectRatio: number;
imageAspectRatio: number;
index: number;
swiperHeight: number;
containerHeight: number;
slideVisible: boolean;
handleLoad: (event: SyntheticEvent<HTMLImageElement>, index: number) => void;
handleLoadError: (index: number) => void;
Expand All @@ -80,11 +80,11 @@ type SlideProps = {
export const Slide: FC<SlideProps> = ({
isActive,
meta,
swiperAspectRatio,
containerAspectRatio,
imageAspectRatio,
image,
index,
swiperHeight,
containerHeight,
slideVisible,
handleLoad,
handleLoadError,
Expand All @@ -93,8 +93,8 @@ export const Slide: FC<SlideProps> = ({

const broken = Boolean(meta?.broken);
const small = isSmallImage(meta);
const verticalImageFit = !small && swiperAspectRatio > imageAspectRatio;
const horizontalImageFit = !small && swiperAspectRatio <= imageAspectRatio;
const verticalImageFit = !small && containerAspectRatio > imageAspectRatio;
const horizontalImageFit = !small && containerAspectRatio <= imageAspectRatio;

if (isVideo(image.src)) {
return (
Expand Down Expand Up @@ -130,7 +130,7 @@ export const Slide: FC<SlideProps> = ({
onLoad={(event) => handleLoad(event, index)}
onError={() => handleLoadError(index)}
style={{
maxHeight: `${swiperHeight}px`,
maxHeight: `${containerHeight}px`,
}}
data-test-id={slideVisible ? TestIds.ACTIVE_IMAGE : undefined}
/>
Expand Down

0 comments on commit c0fd8cf

Please sign in to comment.