import { memo, useRef, useCallback, useState, useMemo } from 'react'; import { NativeScrollEvent, NativeSyntheticEvent, ScrollView, StyleSheet, View, ViewStyle, } from 'react-native'; import { getHorizontalMonthOffset, getIndexFromVerticalOffset, getMonthHeight, getVerticalMonthsOffset, } from './Month'; import { beginOffset, estimatedMonthHeight, totalMonths } from './dateUtils'; import AutoSizer from './AutoSizer'; import type { SwiperProps } from './SwiperUtils'; import { montHeaderHeight } from './utils'; function getVisibleArray( i: number, { isHorizontal, height }: { isHorizontal: boolean; height: number }, ) { if (isHorizontal || height < 700) { return [i - 1, i, i + 1]; } return [i - 2, i - 1, i, i + 1, i + 2]; } function Swiper(props: SwiperProps) { return ( {({ width, height }) => } ); } function SwiperInner({ scrollMode, renderItem, renderHeader, renderFooter, initialIndex, width, height, }: SwiperProps & { width: number; height: number }) { const idx = useRef(initialIndex); const isHorizontal = scrollMode === 'horizontal'; const [visibleIndexes, setVisibleIndexes] = useState( getVisibleArray(initialIndex, { isHorizontal, height }), ); const parentRef = useRef(null); const scrollTo = useCallback( (index: number, animated: boolean) => { idx.current = index; setVisibleIndexes(getVisibleArray(index, { isHorizontal, height })); if (!parentRef.current) { return; } const offset = isHorizontal ? getHorizontalMonthOffset(index, width) : getVerticalMonthsOffset(index) - montHeaderHeight; if (isHorizontal) { parentRef.current.scrollTo({ y: 0, x: offset, animated, }); } else { parentRef.current.scrollTo({ y: offset, x: 0, animated, }); } }, [parentRef, isHorizontal, width, height], ); const scrollToInitial = useCallback(() => { scrollTo(idx.current, false); }, [scrollTo]); const onMomentumScrollEnd = useCallback( (e: NativeSyntheticEvent) => { const contentOffset = e.nativeEvent.contentOffset; const viewSize = e.nativeEvent.layoutMeasurement; const newIndex = isHorizontal ? Math.floor(contentOffset.x / viewSize.width) : getIndexFromVerticalOffset(contentOffset.y - beginOffset); if (newIndex === 0) { return; } if (idx.current !== newIndex) { idx.current = newIndex; setVisibleIndexes(getVisibleArray(newIndex, { isHorizontal, height })); } }, [idx, height, isHorizontal], ); const { innerContainerStyle, itemContainerStyle } = useMemo(() => { return { innerContainerStyle: [ styles.inner, { height: isHorizontal ? height : estimatedMonthHeight * totalMonths, width: isHorizontal ? width * totalMonths : width, }, ], itemContainerStyle: (vi: number) => ({ top: isHorizontal ? 0 : getVerticalMonthsOffset(visibleIndexes[vi]), left: isHorizontal ? getHorizontalMonthOffset(visibleIndexes[vi], width) : 0, right: isHorizontal ? undefined : 0, bottom: isHorizontal ? 0 : undefined, position: 'absolute', width: isHorizontal ? width : undefined, height: isHorizontal ? undefined : getMonthHeight(scrollMode, visibleIndexes[vi]), }), }; }, [height, isHorizontal, scrollMode, visibleIndexes, width]); return ( <> {renderHeader && renderHeader()} {visibleIndexes ? new Array(visibleIndexes.length).fill(undefined).map((_, vi) => ( {renderItem(visibleIndexes[vi])} )) : null} {renderFooter && renderFooter()} ); } const styles = StyleSheet.create({ viewPager: { flex: 1, }, inner: { position: 'relative', }, }); export default memo(Swiper);