1
0
Fork 0
mirror of https://github.com/muerwre/vault-frontend.git synced 2025-04-24 20:36:40 +07:00
vault-frontend/src/components/node/NodeImageSwiperBlock/index.tsx
2025-02-12 18:01:21 +07:00

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 };