mirror of
https://github.com/muerwre/vault-frontend.git
synced 2025-04-24 20:36:40 +07:00
152 lines
3.9 KiB
TypeScript
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>
|
|
);
|
|
};
|