mirror of
https://github.com/muerwre/vault-frontend.git
synced 2025-04-24 20:36:40 +07:00
130 lines
3.9 KiB
TypeScript
130 lines
3.9 KiB
TypeScript
import { FC, useCallback, useEffect, useMemo, useState } from 'react';
|
|
|
|
import classNames from 'classnames';
|
|
import { observer } from 'mobx-react-lite';
|
|
import { Keyboard, Navigation, Pagination, Zoom } from 'swiper/modules';
|
|
import { Swiper, SwiperSlide } from 'swiper/react';
|
|
import SwiperClass from 'swiper/types/swiper-class';
|
|
|
|
import { ImageLoadingWrapper } from '~/components/common/ImageLoadingWrapper/index';
|
|
import { NodeComponentProps } from '~/constants/node';
|
|
import { imagePresets } from '~/constants/urls';
|
|
import { useWindowSize } from '~/hooks/dom/useWindowSize';
|
|
import { useModal } from '~/hooks/modal/useModal';
|
|
import { useImageModal } from '~/hooks/navigation/useImageModal';
|
|
import { useNodeImages } from '~/hooks/node/useNodeImages';
|
|
import { normalizeBrightColor } from '~/utils/color';
|
|
import { getURL } from '~/utils/dom';
|
|
|
|
import { NodeImageLazy } from '../NodeImageLazy';
|
|
|
|
import { getNodeSwiperImageSizes } from './helpers';
|
|
import { NODE_SWIPER_OPTIONS } from './options';
|
|
import styles from './styles.module.scss';
|
|
|
|
interface Props extends NodeComponentProps {}
|
|
|
|
const NodeImageSwiperBlock: FC<Props> = observer(({ node }) => {
|
|
const [controlledSwiper, setControlledSwiper] = useState<SwiperClass>();
|
|
const showPhotoSwiper = useImageModal();
|
|
const { isOpened: isModalActive } = useModal();
|
|
const { innerWidth, innerHeight } = useWindowSize();
|
|
|
|
const images = useNodeImages(node);
|
|
|
|
const keyboard = useMemo(
|
|
() => ({
|
|
enabled: !isModalActive,
|
|
onlyInViewport: true,
|
|
}),
|
|
[isModalActive],
|
|
);
|
|
|
|
const onOpenPhotoSwipe = useCallback(
|
|
(index: number) => {
|
|
if (
|
|
index !== controlledSwiper?.activeIndex &&
|
|
controlledSwiper?.slideTo
|
|
) {
|
|
controlledSwiper.slideTo(index, 300);
|
|
return;
|
|
}
|
|
|
|
showPhotoSwiper(images, index);
|
|
},
|
|
[images, controlledSwiper, showPhotoSwiper],
|
|
);
|
|
|
|
useEffect(() => {
|
|
controlledSwiper?.slideTo(0, 0);
|
|
return () => {
|
|
controlledSwiper?.slideTo(0, 0);
|
|
};
|
|
}, [controlledSwiper, images, node.id]);
|
|
|
|
useEffect(() => {
|
|
if (isModalActive) {
|
|
controlledSwiper?.keyboard?.disable();
|
|
} else {
|
|
controlledSwiper?.keyboard?.enable();
|
|
}
|
|
}, [controlledSwiper?.keyboard, isModalActive]);
|
|
|
|
if (!images?.length) {
|
|
return null;
|
|
}
|
|
|
|
return (
|
|
<div className={styles.wrapper}>
|
|
<Swiper
|
|
modules={[Navigation, Pagination, Keyboard, Zoom]}
|
|
enabled={images.length > 1}
|
|
initialSlide={0}
|
|
slidesPerView="auto"
|
|
onSwiper={setControlledSwiper}
|
|
breakpoints={NODE_SWIPER_OPTIONS.breakpoints}
|
|
pagination={NODE_SWIPER_OPTIONS.pagination}
|
|
centeredSlides
|
|
observeSlideChildren
|
|
observeParents
|
|
observer
|
|
resizeObserver
|
|
watchOverflow
|
|
keyboard={keyboard}
|
|
grabCursor
|
|
autoHeight
|
|
zoom
|
|
navigation
|
|
watchSlidesProgress
|
|
lazyPreloadPrevNext={1}
|
|
>
|
|
{images.map((file, index) => (
|
|
<SwiperSlide className={styles.slide} key={file.id}>
|
|
<ImageLoadingWrapper
|
|
preview={getURL(file, imagePresets['300'])}
|
|
color={file.metadata?.dominant_color}
|
|
>
|
|
{({ loading, onLoad }) => (
|
|
<NodeImageLazy
|
|
src={getURL(file)}
|
|
width={file.metadata?.width}
|
|
height={file.metadata?.height}
|
|
color={normalizeBrightColor(file?.metadata?.dominant_color)}
|
|
onLoad={onLoad}
|
|
onClick={() => onOpenPhotoSwipe(index)}
|
|
className={classNames(styles.image, 'swiper-lazy', {
|
|
[styles.loading]: loading,
|
|
})}
|
|
sizes={getNodeSwiperImageSizes(file, innerWidth, innerHeight)}
|
|
quality={90}
|
|
/>
|
|
)}
|
|
</ImageLoadingWrapper>
|
|
</SwiperSlide>
|
|
))}
|
|
</Swiper>
|
|
</div>
|
|
);
|
|
});
|
|
|
|
export { NodeImageSwiperBlock };
|