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/flow/FlowSwiperHero/index.tsx
2022-12-06 21:34:36 +06:00

152 lines
3.9 KiB
TypeScript

import React, { FC, useCallback, useMemo, useState } from 'react';
import classNames from 'classnames';
import SwiperCore, { Autoplay, EffectFade, Lazy, Navigation } from 'swiper';
import { Swiper, SwiperSlide } from 'swiper/react';
import SwiperClass from 'swiper/types/swiper-class';
import { Icon } from '~/components/input/Icon';
import { LoaderCircle } from '~/components/input/LoaderCircle';
import { imagePresets, URLS } from '~/constants/urls';
import { useWindowSize } from '~/hooks/dom/useWindowSize';
import { useNavigation } from '~/hooks/navigation/useNavigation';
import { IFlowNode } from '~/types';
import { getURLFromString } from '~/utils/dom';
import styles from './styles.module.scss';
SwiperCore.use([EffectFade, Lazy, Autoplay, Navigation]);
interface Props {
heroes: IFlowNode[];
}
const autoplay = {
delay: 3000,
pauseOnMouseEnter: false,
stopOnLastSlide: false,
disableOnInteraction: false,
};
const lazy = {
enabled: true,
loadPrevNextAmount: 2,
loadOnTransitionStart: true,
loadPrevNext: true,
checkInView: true,
};
export const FlowSwiperHero: FC<Props> = ({ heroes }) => {
const { isTablet } = useWindowSize();
const { push } = useNavigation();
const [controlledSwiper, setControlledSwiper] = useState<
SwiperClass | undefined
>(undefined);
const [currentIndex, setCurrentIndex] = useState(heroes.length);
const preset = useMemo(
() => (isTablet ? imagePresets.cover : imagePresets.small_hero),
[isTablet],
);
const onNext = useCallback(() => {
controlledSwiper?.slideNext(1);
}, [controlledSwiper]);
const onPrev = useCallback(() => {
controlledSwiper?.slidePrev(1);
}, [controlledSwiper]);
const onIndexChange = useCallback((swiper: SwiperClass) => {
let activeIndex = swiper.activeIndex;
let slidesLen = swiper.slides.length;
if (slidesLen === 0) {
return 0;
}
if (swiper.params.loop) {
switch (swiper.activeIndex) {
case 0:
activeIndex = slidesLen - 3;
break;
case slidesLen - 1:
activeIndex = 0;
break;
default:
--activeIndex;
}
}
setCurrentIndex(activeIndex);
}, []);
const onClick = useCallback(
(sw: SwiperClass) => {
push(URLS.NODE_URL(heroes[sw.realIndex]?.id));
},
[push, heroes],
);
if (!heroes.length) {
return (
<div className={styles.loader}>
<LoaderCircle size={200} fill="currentColor" />
</div>
);
}
const title = heroes[currentIndex]?.title;
return (
<div className={styles.wrap}>
<div className={styles.slide}>
<div className={styles.info}>
<div className={styles.title}>{title}</div>
<div className={styles.buttons}>
<button className={styles.button} onClick={onPrev} type="button">
<Icon icon="left" />
</button>
<button className={styles.button} onClick={onNext} type="button">
<Icon icon="right" />
</button>
</div>
</div>
</div>
<Swiper
spaceBetween={300}
effect="fade"
speed={3000}
className={styles.swiper}
lazy={lazy}
loop
slidesPerView={1}
autoplay={autoplay}
runCallbacksOnInit
onSwiper={setControlledSwiper}
onSlidesLengthChange={onIndexChange}
onRealIndexChange={onIndexChange}
onAfterInit={onIndexChange}
onClick={onClick}
followFinger
shortSwipes={false}
watchSlidesProgress
>
{heroes
.filter((node) => node.thumbnail)
.map((node) => (
<SwiperSlide key={node.id}>
<img
data-src={getURLFromString(node.thumbnail!, preset)}
alt=""
className={classNames(styles.preview, 'swiper-lazy')}
/>
</SwiperSlide>
))}
</Swiper>
</div>
);
};