@leonsilicon/react-native-reanimated-carousel 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +652 -0
- package/lib/commonjs/components/Carousel.js +2 -0
- package/lib/commonjs/components/Carousel.js.map +1 -0
- package/lib/commonjs/components/CarouselLayout.js +2 -0
- package/lib/commonjs/components/CarouselLayout.js.map +1 -0
- package/lib/commonjs/components/ItemLayout.js +2 -0
- package/lib/commonjs/components/ItemLayout.js.map +1 -0
- package/lib/commonjs/components/ItemRenderer.js +2 -0
- package/lib/commonjs/components/ItemRenderer.js.map +1 -0
- package/lib/commonjs/components/LazyView.js +2 -0
- package/lib/commonjs/components/LazyView.js.map +1 -0
- package/lib/commonjs/components/Pagination/Basic/PaginationItem.js +2 -0
- package/lib/commonjs/components/Pagination/Basic/PaginationItem.js.map +1 -0
- package/lib/commonjs/components/Pagination/Basic/index.js +2 -0
- package/lib/commonjs/components/Pagination/Basic/index.js.map +1 -0
- package/lib/commonjs/components/Pagination/Custom/PaginationItem.js +2 -0
- package/lib/commonjs/components/Pagination/Custom/PaginationItem.js.map +1 -0
- package/lib/commonjs/components/Pagination/Custom/index.js +2 -0
- package/lib/commonjs/components/Pagination/Custom/index.js.map +1 -0
- package/lib/commonjs/components/Pagination/index.js +2 -0
- package/lib/commonjs/components/Pagination/index.js.map +1 -0
- package/lib/commonjs/components/ScrollViewGesture.js +2 -0
- package/lib/commonjs/components/ScrollViewGesture.js.map +1 -0
- package/lib/commonjs/constants/index.js +2 -0
- package/lib/commonjs/constants/index.js.map +1 -0
- package/lib/commonjs/hooks/useAutoPlay.js +2 -0
- package/lib/commonjs/hooks/useAutoPlay.js.map +1 -0
- package/lib/commonjs/hooks/useCarouselController.js +2 -0
- package/lib/commonjs/hooks/useCarouselController.js.map +1 -0
- package/lib/commonjs/hooks/useCheckMounted.js +2 -0
- package/lib/commonjs/hooks/useCheckMounted.js.map +1 -0
- package/lib/commonjs/hooks/useCommonVariables.js +2 -0
- package/lib/commonjs/hooks/useCommonVariables.js.map +1 -0
- package/lib/commonjs/hooks/useInitProps.js +2 -0
- package/lib/commonjs/hooks/useInitProps.js.map +1 -0
- package/lib/commonjs/hooks/useLayoutConfig.js +2 -0
- package/lib/commonjs/hooks/useLayoutConfig.js.map +1 -0
- package/lib/commonjs/hooks/useOffsetX.js +2 -0
- package/lib/commonjs/hooks/useOffsetX.js.map +1 -0
- package/lib/commonjs/hooks/useOnProgressChange.js +2 -0
- package/lib/commonjs/hooks/useOnProgressChange.js.map +1 -0
- package/lib/commonjs/hooks/usePanGestureProxy.js +2 -0
- package/lib/commonjs/hooks/usePanGestureProxy.js.map +1 -0
- package/lib/commonjs/hooks/usePropsErrorBoundary.js +2 -0
- package/lib/commonjs/hooks/usePropsErrorBoundary.js.map +1 -0
- package/lib/commonjs/hooks/useSizeResolver.js +2 -0
- package/lib/commonjs/hooks/useSizeResolver.js.map +1 -0
- package/lib/commonjs/hooks/useUpdateGestureConfig.js +2 -0
- package/lib/commonjs/hooks/useUpdateGestureConfig.js.map +1 -0
- package/lib/commonjs/hooks/useVisibleRanges.js +2 -0
- package/lib/commonjs/hooks/useVisibleRanges.js.map +1 -0
- package/lib/commonjs/index.js +2 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/layouts/index.js +2 -0
- package/lib/commonjs/layouts/index.js.map +1 -0
- package/lib/commonjs/layouts/normal.js +2 -0
- package/lib/commonjs/layouts/normal.js.map +1 -0
- package/lib/commonjs/layouts/parallax.js +2 -0
- package/lib/commonjs/layouts/parallax.js.map +1 -0
- package/lib/commonjs/layouts/stack.js +2 -0
- package/lib/commonjs/layouts/stack.js.map +1 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/commonjs/store/index.js +2 -0
- package/lib/commonjs/store/index.js.map +1 -0
- package/lib/commonjs/types.js +2 -0
- package/lib/commonjs/types.js.map +1 -0
- package/lib/commonjs/utils/compute-gesture-translation.js +2 -0
- package/lib/commonjs/utils/compute-gesture-translation.js.map +1 -0
- package/lib/commonjs/utils/compute-offset-if-data-changed.js +2 -0
- package/lib/commonjs/utils/compute-offset-if-data-changed.js.map +1 -0
- package/lib/commonjs/utils/compute-offset-if-size-changed.js +2 -0
- package/lib/commonjs/utils/compute-offset-if-size-changed.js.map +1 -0
- package/lib/commonjs/utils/compute-offset-if-sizes-changed.js +2 -0
- package/lib/commonjs/utils/compute-offset-if-sizes-changed.js.map +1 -0
- package/lib/commonjs/utils/computed-with-auto-fill-data.js +2 -0
- package/lib/commonjs/utils/computed-with-auto-fill-data.js.map +1 -0
- package/lib/commonjs/utils/deal-with-animation.js +2 -0
- package/lib/commonjs/utils/deal-with-animation.js.map +1 -0
- package/lib/commonjs/utils/handleroffset-direction.js +2 -0
- package/lib/commonjs/utils/handleroffset-direction.js.map +1 -0
- package/lib/commonjs/utils/log.js +2 -0
- package/lib/commonjs/utils/log.js.map +1 -0
- package/lib/commonjs/utils/sanitize-animation-style.js +2 -0
- package/lib/commonjs/utils/sanitize-animation-style.js.map +1 -0
- package/lib/commonjs/utils/size-resolver.js +2 -0
- package/lib/commonjs/utils/size-resolver.js.map +1 -0
- package/lib/module/components/Carousel.js +2 -0
- package/lib/module/components/Carousel.js.map +1 -0
- package/lib/module/components/CarouselLayout.js +2 -0
- package/lib/module/components/CarouselLayout.js.map +1 -0
- package/lib/module/components/ItemLayout.js +2 -0
- package/lib/module/components/ItemLayout.js.map +1 -0
- package/lib/module/components/ItemRenderer.js +2 -0
- package/lib/module/components/ItemRenderer.js.map +1 -0
- package/lib/module/components/LazyView.js +2 -0
- package/lib/module/components/LazyView.js.map +1 -0
- package/lib/module/components/Pagination/Basic/PaginationItem.js +2 -0
- package/lib/module/components/Pagination/Basic/PaginationItem.js.map +1 -0
- package/lib/module/components/Pagination/Basic/index.js +2 -0
- package/lib/module/components/Pagination/Basic/index.js.map +1 -0
- package/lib/module/components/Pagination/Custom/PaginationItem.js +2 -0
- package/lib/module/components/Pagination/Custom/PaginationItem.js.map +1 -0
- package/lib/module/components/Pagination/Custom/index.js +2 -0
- package/lib/module/components/Pagination/Custom/index.js.map +1 -0
- package/lib/module/components/Pagination/index.js +2 -0
- package/lib/module/components/Pagination/index.js.map +1 -0
- package/lib/module/components/ScrollViewGesture.js +2 -0
- package/lib/module/components/ScrollViewGesture.js.map +1 -0
- package/lib/module/constants/index.js +2 -0
- package/lib/module/constants/index.js.map +1 -0
- package/lib/module/hooks/useAutoPlay.js +2 -0
- package/lib/module/hooks/useAutoPlay.js.map +1 -0
- package/lib/module/hooks/useCarouselController.js +2 -0
- package/lib/module/hooks/useCarouselController.js.map +1 -0
- package/lib/module/hooks/useCheckMounted.js +2 -0
- package/lib/module/hooks/useCheckMounted.js.map +1 -0
- package/lib/module/hooks/useCommonVariables.js +2 -0
- package/lib/module/hooks/useCommonVariables.js.map +1 -0
- package/lib/module/hooks/useInitProps.js +2 -0
- package/lib/module/hooks/useInitProps.js.map +1 -0
- package/lib/module/hooks/useLayoutConfig.js +2 -0
- package/lib/module/hooks/useLayoutConfig.js.map +1 -0
- package/lib/module/hooks/useOffsetX.js +2 -0
- package/lib/module/hooks/useOffsetX.js.map +1 -0
- package/lib/module/hooks/useOnProgressChange.js +2 -0
- package/lib/module/hooks/useOnProgressChange.js.map +1 -0
- package/lib/module/hooks/usePanGestureProxy.js +2 -0
- package/lib/module/hooks/usePanGestureProxy.js.map +1 -0
- package/lib/module/hooks/usePropsErrorBoundary.js +2 -0
- package/lib/module/hooks/usePropsErrorBoundary.js.map +1 -0
- package/lib/module/hooks/useSizeResolver.js +2 -0
- package/lib/module/hooks/useSizeResolver.js.map +1 -0
- package/lib/module/hooks/useUpdateGestureConfig.js +2 -0
- package/lib/module/hooks/useUpdateGestureConfig.js.map +1 -0
- package/lib/module/hooks/useVisibleRanges.js +2 -0
- package/lib/module/hooks/useVisibleRanges.js.map +1 -0
- package/lib/module/index.js +2 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/layouts/index.js +2 -0
- package/lib/module/layouts/index.js.map +1 -0
- package/lib/module/layouts/normal.js +2 -0
- package/lib/module/layouts/normal.js.map +1 -0
- package/lib/module/layouts/parallax.js +2 -0
- package/lib/module/layouts/parallax.js.map +1 -0
- package/lib/module/layouts/stack.js +2 -0
- package/lib/module/layouts/stack.js.map +1 -0
- package/lib/module/store/index.js +2 -0
- package/lib/module/store/index.js.map +1 -0
- package/lib/module/types.js +2 -0
- package/lib/module/types.js.map +1 -0
- package/lib/module/utils/compute-gesture-translation.js +2 -0
- package/lib/module/utils/compute-gesture-translation.js.map +1 -0
- package/lib/module/utils/compute-offset-if-data-changed.js +2 -0
- package/lib/module/utils/compute-offset-if-data-changed.js.map +1 -0
- package/lib/module/utils/compute-offset-if-size-changed.js +2 -0
- package/lib/module/utils/compute-offset-if-size-changed.js.map +1 -0
- package/lib/module/utils/compute-offset-if-sizes-changed.js +2 -0
- package/lib/module/utils/compute-offset-if-sizes-changed.js.map +1 -0
- package/lib/module/utils/computed-with-auto-fill-data.js +2 -0
- package/lib/module/utils/computed-with-auto-fill-data.js.map +1 -0
- package/lib/module/utils/deal-with-animation.js +2 -0
- package/lib/module/utils/deal-with-animation.js.map +1 -0
- package/lib/module/utils/handleroffset-direction.js +2 -0
- package/lib/module/utils/handleroffset-direction.js.map +1 -0
- package/lib/module/utils/log.js +2 -0
- package/lib/module/utils/log.js.map +1 -0
- package/lib/module/utils/sanitize-animation-style.js +2 -0
- package/lib/module/utils/sanitize-animation-style.js.map +1 -0
- package/lib/module/utils/size-resolver.js +2 -0
- package/lib/module/utils/size-resolver.js.map +1 -0
- package/lib/typescript/components/Carousel.d.ts +8 -0
- package/lib/typescript/components/Carousel.d.ts.map +1 -0
- package/lib/typescript/components/CarouselLayout.d.ts +6 -0
- package/lib/typescript/components/CarouselLayout.d.ts.map +1 -0
- package/lib/typescript/components/ItemLayout.d.ts +15 -0
- package/lib/typescript/components/ItemLayout.d.ts.map +1 -0
- package/lib/typescript/components/ItemRenderer.d.ts +24 -0
- package/lib/typescript/components/ItemRenderer.d.ts.map +1 -0
- package/lib/typescript/components/LazyView.d.ts +8 -0
- package/lib/typescript/components/LazyView.d.ts.map +1 -0
- package/lib/typescript/components/Pagination/Basic/PaginationItem.d.ts +29 -0
- package/lib/typescript/components/Pagination/Basic/PaginationItem.d.ts.map +1 -0
- package/lib/typescript/components/Pagination/Basic/index.d.ts +23 -0
- package/lib/typescript/components/Pagination/Basic/index.d.ts.map +1 -0
- package/lib/typescript/components/Pagination/Custom/PaginationItem.d.ts +35 -0
- package/lib/typescript/components/Pagination/Custom/PaginationItem.d.ts.map +1 -0
- package/lib/typescript/components/Pagination/Custom/index.d.ts +26 -0
- package/lib/typescript/components/Pagination/Custom/index.d.ts.map +1 -0
- package/lib/typescript/components/Pagination/index.d.ts +5 -0
- package/lib/typescript/components/Pagination/index.d.ts.map +1 -0
- package/lib/typescript/components/ScrollViewGesture.d.ts +19 -0
- package/lib/typescript/components/ScrollViewGesture.d.ts.map +1 -0
- package/lib/typescript/constants/index.d.ts +9 -0
- package/lib/typescript/constants/index.d.ts.map +1 -0
- package/lib/typescript/hooks/useAutoPlay.d.ts +12 -0
- package/lib/typescript/hooks/useAutoPlay.d.ts.map +1 -0
- package/lib/typescript/hooks/useCarouselController.d.ts +28 -0
- package/lib/typescript/hooks/useCarouselController.d.ts.map +1 -0
- package/lib/typescript/hooks/useCheckMounted.d.ts +3 -0
- package/lib/typescript/hooks/useCheckMounted.d.ts.map +1 -0
- package/lib/typescript/hooks/useCommonVariables.d.ts +13 -0
- package/lib/typescript/hooks/useCommonVariables.d.ts.map +1 -0
- package/lib/typescript/hooks/useInitProps.d.ts +17 -0
- package/lib/typescript/hooks/useInitProps.d.ts.map +1 -0
- package/lib/typescript/hooks/useLayoutConfig.d.ts +10 -0
- package/lib/typescript/hooks/useLayoutConfig.d.ts.map +1 -0
- package/lib/typescript/hooks/useOffsetX.d.ts +24 -0
- package/lib/typescript/hooks/useOffsetX.d.ts.map +1 -0
- package/lib/typescript/hooks/useOnProgressChange.d.ts +14 -0
- package/lib/typescript/hooks/useOnProgressChange.d.ts.map +1 -0
- package/lib/typescript/hooks/usePanGestureProxy.d.ts +10 -0
- package/lib/typescript/hooks/usePanGestureProxy.d.ts.map +1 -0
- package/lib/typescript/hooks/usePropsErrorBoundary.d.ts +5 -0
- package/lib/typescript/hooks/usePropsErrorBoundary.d.ts.map +1 -0
- package/lib/typescript/hooks/useSizeResolver.d.ts +27 -0
- package/lib/typescript/hooks/useSizeResolver.d.ts.map +1 -0
- package/lib/typescript/hooks/useUpdateGestureConfig.d.ts +6 -0
- package/lib/typescript/hooks/useUpdateGestureConfig.d.ts.map +1 -0
- package/lib/typescript/hooks/useVisibleRanges.d.ts +23 -0
- package/lib/typescript/hooks/useVisibleRanges.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +7 -0
- package/lib/typescript/index.d.ts.map +1 -0
- package/lib/typescript/layouts/index.d.ts +11 -0
- package/lib/typescript/layouts/index.d.ts.map +1 -0
- package/lib/typescript/layouts/normal.d.ts +16 -0
- package/lib/typescript/layouts/normal.d.ts.map +1 -0
- package/lib/typescript/layouts/parallax.d.ts +50 -0
- package/lib/typescript/layouts/parallax.d.ts.map +1 -0
- package/lib/typescript/layouts/stack.d.ts +38 -0
- package/lib/typescript/layouts/stack.d.ts.map +1 -0
- package/lib/typescript/store/index.d.ts +38 -0
- package/lib/typescript/store/index.d.ts.map +1 -0
- package/lib/typescript/types.d.ts +326 -0
- package/lib/typescript/types.d.ts.map +1 -0
- package/lib/typescript/utils/compute-gesture-translation.d.ts +9 -0
- package/lib/typescript/utils/compute-gesture-translation.d.ts.map +1 -0
- package/lib/typescript/utils/compute-offset-if-data-changed.d.ts +9 -0
- package/lib/typescript/utils/compute-offset-if-data-changed.d.ts.map +1 -0
- package/lib/typescript/utils/compute-offset-if-size-changed.d.ts +6 -0
- package/lib/typescript/utils/compute-offset-if-size-changed.d.ts.map +1 -0
- package/lib/typescript/utils/compute-offset-if-sizes-changed.d.ts +14 -0
- package/lib/typescript/utils/compute-offset-if-sizes-changed.d.ts.map +1 -0
- package/lib/typescript/utils/computed-with-auto-fill-data.d.ts +23 -0
- package/lib/typescript/utils/computed-with-auto-fill-data.d.ts.map +1 -0
- package/lib/typescript/utils/deal-with-animation.d.ts +3 -0
- package/lib/typescript/utils/deal-with-animation.d.ts.map +1 -0
- package/lib/typescript/utils/handleroffset-direction.d.ts +4 -0
- package/lib/typescript/utils/handleroffset-direction.d.ts.map +1 -0
- package/lib/typescript/utils/log.d.ts +7 -0
- package/lib/typescript/utils/log.d.ts.map +1 -0
- package/lib/typescript/utils/sanitize-animation-style.d.ts +3 -0
- package/lib/typescript/utils/sanitize-animation-style.d.ts.map +1 -0
- package/lib/typescript/utils/size-resolver.d.ts +87 -0
- package/lib/typescript/utils/size-resolver.d.ts.map +1 -0
- package/package.json +151 -0
- package/src/components/Carousel.test.tsx +1153 -0
- package/src/components/Carousel.tsx +35 -0
- package/src/components/CarouselLayout.tsx +231 -0
- package/src/components/ItemLayout.tsx +217 -0
- package/src/components/ItemRenderer.tsx +114 -0
- package/src/components/LazyView.test.tsx +61 -0
- package/src/components/LazyView.tsx +14 -0
- package/src/components/Pagination/Basic/PaginationItem.tsx +149 -0
- package/src/components/Pagination/Basic/index.tsx +98 -0
- package/src/components/Pagination/Custom/PaginationItem.tsx +166 -0
- package/src/components/Pagination/Custom/index.tsx +111 -0
- package/src/components/Pagination/Pagination.test.tsx +178 -0
- package/src/components/Pagination/index.tsx +7 -0
- package/src/components/ScrollViewGesture.tsx +577 -0
- package/src/components/rnr-demo.test.tsx +53 -0
- package/src/constants/index.ts +11 -0
- package/src/hooks/useAutoPlay.test.ts +194 -0
- package/src/hooks/useAutoPlay.ts +58 -0
- package/src/hooks/useCarouselController.test.tsx +1158 -0
- package/src/hooks/useCarouselController.tsx +525 -0
- package/src/hooks/useCheckMounted.test.ts +47 -0
- package/src/hooks/useCheckMounted.ts +14 -0
- package/src/hooks/useCommonVariables.test.tsx +384 -0
- package/src/hooks/useCommonVariables.ts +202 -0
- package/src/hooks/useInitProps.test.tsx +134 -0
- package/src/hooks/useInitProps.ts +111 -0
- package/src/hooks/useLayoutConfig.test.tsx +247 -0
- package/src/hooks/useLayoutConfig.ts +30 -0
- package/src/hooks/useOffsetX.test.ts +110 -0
- package/src/hooks/useOffsetX.ts +109 -0
- package/src/hooks/useOnProgressChange.test.tsx +207 -0
- package/src/hooks/useOnProgressChange.ts +105 -0
- package/src/hooks/usePanGestureProxy.test.tsx +368 -0
- package/src/hooks/usePanGestureProxy.ts +144 -0
- package/src/hooks/usePropsErrorBoundary.ts +138 -0
- package/src/hooks/useSizeResolver.test.tsx +112 -0
- package/src/hooks/useSizeResolver.ts +106 -0
- package/src/hooks/useUpdateGestureConfig.test.ts +89 -0
- package/src/hooks/useUpdateGestureConfig.ts +14 -0
- package/src/hooks/useVisibleRanges.test.tsx +366 -0
- package/src/hooks/useVisibleRanges.tsx +123 -0
- package/src/index.tsx +13 -0
- package/src/layouts/index.tsx +12 -0
- package/src/layouts/normal.ts +32 -0
- package/src/layouts/parallax.test.ts +239 -0
- package/src/layouts/parallax.ts +83 -0
- package/src/layouts/stack.test.ts +252 -0
- package/src/layouts/stack.ts +306 -0
- package/src/store/index.test.tsx +314 -0
- package/src/store/index.tsx +66 -0
- package/src/types.ts +348 -0
- package/src/utils/compute-gesture-translation.test.ts +70 -0
- package/src/utils/compute-gesture-translation.ts +29 -0
- package/src/utils/compute-offset-if-data-changed.test.ts +133 -0
- package/src/utils/compute-offset-if-data-changed.ts +44 -0
- package/src/utils/compute-offset-if-size-changed.test.ts +78 -0
- package/src/utils/compute-offset-if-size-changed.ts +14 -0
- package/src/utils/compute-offset-if-sizes-changed.test.ts +74 -0
- package/src/utils/compute-offset-if-sizes-changed.ts +44 -0
- package/src/utils/computed-with-auto-fill-data.test.ts +298 -0
- package/src/utils/computed-with-auto-fill-data.ts +92 -0
- package/src/utils/deal-with-animation.test.ts +181 -0
- package/src/utils/deal-with-animation.ts +17 -0
- package/src/utils/handleroffset-direction.test.ts +124 -0
- package/src/utils/handleroffset-direction.ts +18 -0
- package/src/utils/index.test.ts +90 -0
- package/src/utils/log.test.ts +134 -0
- package/src/utils/log.ts +12 -0
- package/src/utils/sanitize-animation-style.test.ts +40 -0
- package/src/utils/sanitize-animation-style.ts +20 -0
- package/src/utils/size-resolver.test.ts +193 -0
- package/src/utils/size-resolver.ts +216 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { useCommonVariables } from "../hooks/useCommonVariables";
|
|
3
|
+
import { useInitProps } from "../hooks/useInitProps";
|
|
4
|
+
import { usePropsErrorBoundary } from "../hooks/usePropsErrorBoundary";
|
|
5
|
+
import { useSizeResolver } from "../hooks/useSizeResolver";
|
|
6
|
+
import { GlobalStateProvider } from "../store";
|
|
7
|
+
import type { ICarouselInstance, TCarouselProps } from "../types";
|
|
8
|
+
import { CarouselLayout } from "./CarouselLayout";
|
|
9
|
+
|
|
10
|
+
const Carousel = React.forwardRef<ICarouselInstance, TCarouselProps<any>>((_props, ref) => {
|
|
11
|
+
const props = useInitProps(_props);
|
|
12
|
+
const { dataLength, getItemSize } = props;
|
|
13
|
+
const commonVariables = useCommonVariables(props);
|
|
14
|
+
usePropsErrorBoundary({ ...props, dataLength });
|
|
15
|
+
|
|
16
|
+
const sizeResolver = useSizeResolver({
|
|
17
|
+
dataLength,
|
|
18
|
+
getItemSize,
|
|
19
|
+
uniformSize: commonVariables.resolvedSize,
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<GlobalStateProvider value={{ props, common: commonVariables, sizeResolver }}>
|
|
24
|
+
<CarouselLayout ref={ref} />
|
|
25
|
+
</GlobalStateProvider>
|
|
26
|
+
);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
type CarouselComponent = <T>(
|
|
30
|
+
props: React.PropsWithChildren<TCarouselProps<T>> & {
|
|
31
|
+
ref?: React.Ref<ICarouselInstance>;
|
|
32
|
+
}
|
|
33
|
+
) => React.ReactElement;
|
|
34
|
+
|
|
35
|
+
export default Carousel as CarouselComponent;
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { StyleSheet, type ViewStyle } from "react-native";
|
|
3
|
+
import { GestureHandlerRootView } from "react-native-gesture-handler";
|
|
4
|
+
import { useAnimatedReaction, useAnimatedStyle, useDerivedValue } from "react-native-reanimated";
|
|
5
|
+
import { scheduleOnRN } from "react-native-worklets";
|
|
6
|
+
import { useAutoPlay } from "../hooks/useAutoPlay";
|
|
7
|
+
import { useCarouselController } from "../hooks/useCarouselController";
|
|
8
|
+
import { useLayoutConfig } from "../hooks/useLayoutConfig";
|
|
9
|
+
import { useOnProgressChange } from "../hooks/useOnProgressChange";
|
|
10
|
+
import { useGlobalState } from "../store";
|
|
11
|
+
import { ICarouselInstance } from "../types";
|
|
12
|
+
import { computedRealIndexWithAutoFillData } from "../utils/computed-with-auto-fill-data";
|
|
13
|
+
import { ItemRenderer } from "./ItemRenderer";
|
|
14
|
+
import { ScrollViewGesture } from "./ScrollViewGesture";
|
|
15
|
+
|
|
16
|
+
export type TAnimationStyle = (value: number) => ViewStyle;
|
|
17
|
+
|
|
18
|
+
export const CarouselLayout = React.forwardRef<ICarouselInstance>((_props, ref) => {
|
|
19
|
+
const { props, common, sizeResolver } = useGlobalState();
|
|
20
|
+
|
|
21
|
+
const {
|
|
22
|
+
testID,
|
|
23
|
+
loop,
|
|
24
|
+
autoFillData,
|
|
25
|
+
// Fill data with autoFillData
|
|
26
|
+
data,
|
|
27
|
+
// Length of fill data
|
|
28
|
+
dataLength,
|
|
29
|
+
// Length of raw data
|
|
30
|
+
rawDataLength,
|
|
31
|
+
mode,
|
|
32
|
+
style,
|
|
33
|
+
contentContainerStyle,
|
|
34
|
+
vertical,
|
|
35
|
+
autoPlay,
|
|
36
|
+
windowSize,
|
|
37
|
+
autoPlayReverse,
|
|
38
|
+
autoPlayInterval,
|
|
39
|
+
scrollAnimationDuration,
|
|
40
|
+
withAnimation,
|
|
41
|
+
fixedDirection,
|
|
42
|
+
renderItem,
|
|
43
|
+
onScrollEnd,
|
|
44
|
+
onSnapToItem,
|
|
45
|
+
onScrollStart,
|
|
46
|
+
onProgressChange,
|
|
47
|
+
customAnimation,
|
|
48
|
+
defaultIndex,
|
|
49
|
+
variableSize,
|
|
50
|
+
} = props;
|
|
51
|
+
|
|
52
|
+
const { size, handlerOffset, resolvedSize, sizePhase } = common;
|
|
53
|
+
const layoutConfig = useLayoutConfig({ ...props, size, sizeResolver });
|
|
54
|
+
|
|
55
|
+
const isSizeReady = useDerivedValue(() => {
|
|
56
|
+
if (variableSize) return sizeResolver.total.value > 0;
|
|
57
|
+
const currentSize = resolvedSize.value ?? 0;
|
|
58
|
+
return sizePhase.value === "ready" && currentSize > 0;
|
|
59
|
+
}, [resolvedSize, sizePhase, variableSize, sizeResolver]);
|
|
60
|
+
|
|
61
|
+
const offsetX = useDerivedValue(() => {
|
|
62
|
+
const totalSize = variableSize
|
|
63
|
+
? sizeResolver.total.value
|
|
64
|
+
: (resolvedSize.value ?? 0) * dataLength;
|
|
65
|
+
if (totalSize <= 0) return 0;
|
|
66
|
+
|
|
67
|
+
const value = handlerOffset.value;
|
|
68
|
+
if (!loop || totalSize === 0) {
|
|
69
|
+
return value;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const x = value % totalSize;
|
|
73
|
+
return Number.isNaN(x) ? 0 : x;
|
|
74
|
+
}, [loop, dataLength, handlerOffset, resolvedSize, variableSize, sizeResolver]);
|
|
75
|
+
|
|
76
|
+
useOnProgressChange({
|
|
77
|
+
autoFillData,
|
|
78
|
+
loop,
|
|
79
|
+
size,
|
|
80
|
+
sizeReady: isSizeReady,
|
|
81
|
+
offsetX,
|
|
82
|
+
rawDataLength,
|
|
83
|
+
onProgressChange,
|
|
84
|
+
sizeResolver,
|
|
85
|
+
variableSize,
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
const carouselController = useCarouselController({
|
|
89
|
+
ref,
|
|
90
|
+
loop,
|
|
91
|
+
size,
|
|
92
|
+
dataLength,
|
|
93
|
+
autoFillData,
|
|
94
|
+
handlerOffset,
|
|
95
|
+
withAnimation,
|
|
96
|
+
defaultIndex,
|
|
97
|
+
fixedDirection,
|
|
98
|
+
duration: scrollAnimationDuration,
|
|
99
|
+
onScrollEnd: () => scheduleOnRN(_onScrollEnd),
|
|
100
|
+
onScrollStart: () => !!onScrollStart && scheduleOnRN(onScrollStart),
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
const {
|
|
104
|
+
getSharedIndex,
|
|
105
|
+
// index, // Animated index. Could be used for dynamic dimension
|
|
106
|
+
} = carouselController;
|
|
107
|
+
|
|
108
|
+
const _onScrollEnd = React.useCallback(() => {
|
|
109
|
+
const _sharedIndex = Math.round(getSharedIndex());
|
|
110
|
+
|
|
111
|
+
const realIndex = computedRealIndexWithAutoFillData({
|
|
112
|
+
index: _sharedIndex,
|
|
113
|
+
dataLength: rawDataLength,
|
|
114
|
+
loop,
|
|
115
|
+
autoFillData,
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
if (onSnapToItem) onSnapToItem(realIndex);
|
|
119
|
+
|
|
120
|
+
if (onScrollEnd) onScrollEnd(realIndex);
|
|
121
|
+
}, [loop, autoFillData, rawDataLength, getSharedIndex, onSnapToItem, onScrollEnd]);
|
|
122
|
+
|
|
123
|
+
const {
|
|
124
|
+
start: startAutoPlay,
|
|
125
|
+
pause: pauseAutoPlay,
|
|
126
|
+
trigger: triggerAutoPlay,
|
|
127
|
+
} = useAutoPlay({
|
|
128
|
+
autoPlay,
|
|
129
|
+
autoPlayInterval,
|
|
130
|
+
autoPlayReverse,
|
|
131
|
+
carouselController,
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
useAnimatedReaction(
|
|
135
|
+
() => ({ ready: isSizeReady.value }),
|
|
136
|
+
(state, previous) => {
|
|
137
|
+
if (!autoPlay) return;
|
|
138
|
+
if (state.ready === previous?.ready) return;
|
|
139
|
+
|
|
140
|
+
if (state.ready) scheduleOnRN(triggerAutoPlay);
|
|
141
|
+
else scheduleOnRN(pauseAutoPlay);
|
|
142
|
+
},
|
|
143
|
+
[autoPlay]
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
const scrollViewGestureOnScrollStart = React.useCallback(() => {
|
|
147
|
+
pauseAutoPlay();
|
|
148
|
+
onScrollStart?.();
|
|
149
|
+
}, [onScrollStart, pauseAutoPlay]);
|
|
150
|
+
|
|
151
|
+
const scrollViewGestureOnScrollEnd = React.useCallback(() => {
|
|
152
|
+
startAutoPlay();
|
|
153
|
+
_onScrollEnd();
|
|
154
|
+
}, [_onScrollEnd, startAutoPlay]);
|
|
155
|
+
|
|
156
|
+
const scrollViewGestureOnTouchBegin = React.useCallback(pauseAutoPlay, [pauseAutoPlay]);
|
|
157
|
+
|
|
158
|
+
const scrollViewGestureOnTouchEnd = React.useCallback(startAutoPlay, [startAutoPlay]);
|
|
159
|
+
|
|
160
|
+
const { opacity, transform, ...restContentContainerStyle } =
|
|
161
|
+
StyleSheet.flatten(contentContainerStyle) || {};
|
|
162
|
+
const flattenedStyle = StyleSheet.flatten(style) || {};
|
|
163
|
+
|
|
164
|
+
const layoutStyle = useAnimatedStyle(() => {
|
|
165
|
+
const { width, height } = flattenedStyle;
|
|
166
|
+
const measuredSize = resolvedSize.value ?? 0;
|
|
167
|
+
|
|
168
|
+
const computedWidth = width ?? (vertical ? "100%" : measuredSize || "100%");
|
|
169
|
+
const computedHeight = height ?? (vertical ? measuredSize || "100%" : "100%");
|
|
170
|
+
|
|
171
|
+
return {
|
|
172
|
+
width: computedWidth,
|
|
173
|
+
height: computedHeight,
|
|
174
|
+
opacity: isSizeReady.value ? 1 : 0,
|
|
175
|
+
};
|
|
176
|
+
}, [flattenedStyle, isSizeReady, vertical, resolvedSize, sizePhase]);
|
|
177
|
+
|
|
178
|
+
return (
|
|
179
|
+
<GestureHandlerRootView testID={testID} style={[styles.layoutContainer, style]}>
|
|
180
|
+
<ScrollViewGesture
|
|
181
|
+
size={size}
|
|
182
|
+
key={mode}
|
|
183
|
+
translation={handlerOffset}
|
|
184
|
+
style={[
|
|
185
|
+
styles.contentContainer,
|
|
186
|
+
layoutStyle,
|
|
187
|
+
restContentContainerStyle,
|
|
188
|
+
vertical ? styles.itemsVertical : styles.itemsHorizontal,
|
|
189
|
+
]}
|
|
190
|
+
testID="carousel-content-container"
|
|
191
|
+
onLayout={props.onLayout}
|
|
192
|
+
onScrollStart={scrollViewGestureOnScrollStart}
|
|
193
|
+
onScrollEnd={scrollViewGestureOnScrollEnd}
|
|
194
|
+
onTouchBegin={scrollViewGestureOnTouchBegin}
|
|
195
|
+
onTouchEnd={scrollViewGestureOnTouchEnd}
|
|
196
|
+
>
|
|
197
|
+
<ItemRenderer
|
|
198
|
+
data={data}
|
|
199
|
+
dataLength={dataLength}
|
|
200
|
+
rawDataLength={rawDataLength}
|
|
201
|
+
loop={loop}
|
|
202
|
+
size={size}
|
|
203
|
+
windowSize={windowSize}
|
|
204
|
+
autoFillData={autoFillData}
|
|
205
|
+
offsetX={offsetX}
|
|
206
|
+
handlerOffset={handlerOffset}
|
|
207
|
+
layoutConfig={layoutConfig}
|
|
208
|
+
renderItem={renderItem}
|
|
209
|
+
customAnimation={customAnimation}
|
|
210
|
+
sizeResolver={sizeResolver}
|
|
211
|
+
/>
|
|
212
|
+
</ScrollViewGesture>
|
|
213
|
+
</GestureHandlerRootView>
|
|
214
|
+
);
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
const styles = StyleSheet.create({
|
|
218
|
+
layoutContainer: {
|
|
219
|
+
display: "flex",
|
|
220
|
+
overflow: "hidden",
|
|
221
|
+
},
|
|
222
|
+
contentContainer: {
|
|
223
|
+
overflow: "hidden",
|
|
224
|
+
},
|
|
225
|
+
itemsHorizontal: {
|
|
226
|
+
flexDirection: "row",
|
|
227
|
+
},
|
|
228
|
+
itemsVertical: {
|
|
229
|
+
flexDirection: "column",
|
|
230
|
+
},
|
|
231
|
+
});
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { LayoutChangeEvent, ViewStyle } from "react-native";
|
|
3
|
+
import { StyleSheet } from "react-native";
|
|
4
|
+
import type { SharedValue } from "react-native-reanimated";
|
|
5
|
+
import Animated, {
|
|
6
|
+
useAnimatedStyle,
|
|
7
|
+
useDerivedValue,
|
|
8
|
+
useSharedValue,
|
|
9
|
+
} from "react-native-reanimated";
|
|
10
|
+
import { scheduleOnUI } from "react-native-worklets";
|
|
11
|
+
|
|
12
|
+
import type { IOpts } from "../hooks/useOffsetX";
|
|
13
|
+
import { useOffsetX } from "../hooks/useOffsetX";
|
|
14
|
+
import type { IVisibleRanges } from "../hooks/useVisibleRanges";
|
|
15
|
+
import type { ILayoutConfig } from "../layouts/stack";
|
|
16
|
+
import { useGlobalState } from "../store";
|
|
17
|
+
import type { TCarouselProps } from "../types";
|
|
18
|
+
import { sanitizeAnimationStyle } from "../utils/sanitize-animation-style";
|
|
19
|
+
|
|
20
|
+
export type TAnimationStyle = NonNullable<TCarouselProps["customAnimation"]>;
|
|
21
|
+
|
|
22
|
+
export const ItemLayout: React.FC<{
|
|
23
|
+
index: number;
|
|
24
|
+
handlerOffset: SharedValue<number>;
|
|
25
|
+
visibleRanges: IVisibleRanges;
|
|
26
|
+
animationStyle: TAnimationStyle;
|
|
27
|
+
children: (ctx: {
|
|
28
|
+
animationValue: SharedValue<number>;
|
|
29
|
+
}) => React.ReactElement;
|
|
30
|
+
}> = (props) => {
|
|
31
|
+
const { handlerOffset, index, children, visibleRanges, animationStyle } = props;
|
|
32
|
+
|
|
33
|
+
const {
|
|
34
|
+
props: {
|
|
35
|
+
loop,
|
|
36
|
+
dataLength,
|
|
37
|
+
width,
|
|
38
|
+
height,
|
|
39
|
+
vertical,
|
|
40
|
+
customConfig,
|
|
41
|
+
mode,
|
|
42
|
+
modeConfig,
|
|
43
|
+
style,
|
|
44
|
+
itemWidth,
|
|
45
|
+
itemHeight,
|
|
46
|
+
variableSize,
|
|
47
|
+
},
|
|
48
|
+
common,
|
|
49
|
+
sizeResolver,
|
|
50
|
+
layout: { updateItemDimensions },
|
|
51
|
+
} = useGlobalState();
|
|
52
|
+
|
|
53
|
+
const measuredSize = useSharedValue<{ width: number | null; height: number | null }>({
|
|
54
|
+
width: null,
|
|
55
|
+
height: null,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const fallbackSize = common.size;
|
|
59
|
+
// Prefer size from `style` (v5), then fallback to deprecated `width`/`height` for v4 compatibility.
|
|
60
|
+
const { width: styleWidth, height: styleHeight } = StyleSheet.flatten(style) || {};
|
|
61
|
+
const styleWidthNumber = typeof styleWidth === "number" ? styleWidth : undefined;
|
|
62
|
+
const styleHeightNumber = typeof styleHeight === "number" ? styleHeight : undefined;
|
|
63
|
+
|
|
64
|
+
// Variable-size mode reads each item's size from the resolver up-front; the
|
|
65
|
+
// measurement plumbing below is bypassed so onLayout never overrides the
|
|
66
|
+
// user's declaration.
|
|
67
|
+
const variableItemSize = variableSize ? sizeResolver.itemSize(index) : undefined;
|
|
68
|
+
|
|
69
|
+
// When itemWidth/itemHeight is provided, use it for item dimensions (not container style)
|
|
70
|
+
const explicitItemSize = vertical ? itemHeight : itemWidth;
|
|
71
|
+
const explicitAxisSize = vertical ? (styleHeightNumber ?? height) : (styleWidthNumber ?? width);
|
|
72
|
+
// Variable mode → per-item size; uniform fallback to itemWidth/Height or container.
|
|
73
|
+
const size = (variableItemSize ?? explicitItemSize ?? explicitAxisSize ?? fallbackSize) || 0;
|
|
74
|
+
const effectivePageSize = size > 0 ? size : undefined;
|
|
75
|
+
|
|
76
|
+
const dimensionsStyle = useAnimatedStyle<ViewStyle>(() => {
|
|
77
|
+
if (variableSize) {
|
|
78
|
+
const itemPx = sizeResolver.itemSize(index);
|
|
79
|
+
const w = vertical ? (typeof width === "number" ? width : "100%") : itemPx;
|
|
80
|
+
const h = vertical ? itemPx : typeof height === "number" ? height : "100%";
|
|
81
|
+
return { width: w, height: h };
|
|
82
|
+
}
|
|
83
|
+
// When itemWidth/itemHeight is provided, use it for item width/height
|
|
84
|
+
const widthCandidate = vertical ? width : (explicitItemSize ?? explicitAxisSize);
|
|
85
|
+
const heightCandidate = vertical ? (explicitItemSize ?? explicitAxisSize) : height;
|
|
86
|
+
|
|
87
|
+
const computedWidth =
|
|
88
|
+
typeof widthCandidate === "number"
|
|
89
|
+
? widthCandidate
|
|
90
|
+
: (measuredSize.value.width ?? (vertical ? "100%" : (effectivePageSize ?? "100%")));
|
|
91
|
+
|
|
92
|
+
const computedHeight =
|
|
93
|
+
typeof heightCandidate === "number"
|
|
94
|
+
? heightCandidate
|
|
95
|
+
: (measuredSize.value.height ?? (vertical ? (effectivePageSize ?? "100%") : "100%"));
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
width: computedWidth,
|
|
99
|
+
height: computedHeight,
|
|
100
|
+
};
|
|
101
|
+
}, [
|
|
102
|
+
vertical,
|
|
103
|
+
width,
|
|
104
|
+
height,
|
|
105
|
+
explicitAxisSize,
|
|
106
|
+
explicitItemSize,
|
|
107
|
+
effectivePageSize,
|
|
108
|
+
variableSize,
|
|
109
|
+
sizeResolver,
|
|
110
|
+
index,
|
|
111
|
+
]);
|
|
112
|
+
|
|
113
|
+
let offsetXConfig: IOpts = {
|
|
114
|
+
handlerOffset,
|
|
115
|
+
index,
|
|
116
|
+
size,
|
|
117
|
+
dataLength,
|
|
118
|
+
loop,
|
|
119
|
+
resolver: variableSize ? sizeResolver : undefined,
|
|
120
|
+
...(typeof customConfig === "function" ? customConfig() : {}),
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
if (mode === "horizontal-stack") {
|
|
124
|
+
const { snapDirection, showLength } = modeConfig as ILayoutConfig;
|
|
125
|
+
|
|
126
|
+
offsetXConfig = {
|
|
127
|
+
handlerOffset,
|
|
128
|
+
index,
|
|
129
|
+
size,
|
|
130
|
+
dataLength,
|
|
131
|
+
loop,
|
|
132
|
+
type: snapDirection === "right" ? "negative" : "positive",
|
|
133
|
+
viewCount: showLength,
|
|
134
|
+
resolver: variableSize ? sizeResolver : undefined,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const x = useOffsetX(offsetXConfig, visibleRanges);
|
|
139
|
+
const animationValue = useDerivedValue(() => {
|
|
140
|
+
// In variable mode each item normalises `x` by its own size, so the
|
|
141
|
+
// animation `value` keeps the same [-1, 0, 1] meaning every item has in
|
|
142
|
+
// uniform mode (-1 = one item-width left of center).
|
|
143
|
+
const itemSize = variableSize ? sizeResolver.itemSize(index) : size;
|
|
144
|
+
if (!itemSize) return 0;
|
|
145
|
+
return x.value / itemSize;
|
|
146
|
+
}, [x, size, variableSize, sizeResolver, index]);
|
|
147
|
+
const animatedStyle = useAnimatedStyle<ViewStyle>(() => {
|
|
148
|
+
const itemSize = variableSize ? sizeResolver.itemSize(index) : size;
|
|
149
|
+
const safeSize = itemSize || 1;
|
|
150
|
+
return sanitizeAnimationStyle(animationStyle(x.value / safeSize, index));
|
|
151
|
+
}, [animationStyle, index, x, size, variableSize, sizeResolver]);
|
|
152
|
+
|
|
153
|
+
// TODO: For dynamic dimension in the future
|
|
154
|
+
// function handleLayout(e: LayoutChangeEvent) {
|
|
155
|
+
// const { width, height } = e.nativeEvent.layout;
|
|
156
|
+
// updateItemDimensions(index, { width, height });
|
|
157
|
+
// }
|
|
158
|
+
|
|
159
|
+
const child = children({ animationValue });
|
|
160
|
+
|
|
161
|
+
type LayoutableProps = {
|
|
162
|
+
collapsable?: boolean;
|
|
163
|
+
onLayout?: (event: LayoutChangeEvent) => void;
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
const enhancedChild = React.isValidElement<LayoutableProps>(child)
|
|
167
|
+
? variableSize
|
|
168
|
+
? // Variable-size mode: per-item dimensions are authoritative (declared
|
|
169
|
+
// by getItemWidth/getItemHeight), so onLayout measurements must not
|
|
170
|
+
// overwrite them. Forward the user's original onLayout if any.
|
|
171
|
+
React.cloneElement(child, { collapsable: false })
|
|
172
|
+
: React.cloneElement(child, {
|
|
173
|
+
collapsable: false,
|
|
174
|
+
onLayout: (event: LayoutChangeEvent) => {
|
|
175
|
+
const { width: layoutWidth, height: layoutHeight } = event.nativeEvent.layout;
|
|
176
|
+
if (layoutWidth > 0 && layoutHeight > 0) {
|
|
177
|
+
scheduleOnUI(() => {
|
|
178
|
+
const { width: prevWidth, height: prevHeight } = measuredSize.value;
|
|
179
|
+
if (prevWidth === layoutWidth && prevHeight === layoutHeight) return;
|
|
180
|
+
|
|
181
|
+
measuredSize.value = {
|
|
182
|
+
width: layoutWidth,
|
|
183
|
+
height: layoutHeight,
|
|
184
|
+
};
|
|
185
|
+
updateItemDimensions(index, {
|
|
186
|
+
width: layoutWidth,
|
|
187
|
+
height: layoutHeight,
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
child.props?.onLayout?.(event);
|
|
193
|
+
},
|
|
194
|
+
})
|
|
195
|
+
: child;
|
|
196
|
+
|
|
197
|
+
return (
|
|
198
|
+
<Animated.View
|
|
199
|
+
style={[
|
|
200
|
+
{
|
|
201
|
+
position: "absolute",
|
|
202
|
+
pointerEvents: "box-none",
|
|
203
|
+
},
|
|
204
|
+
dimensionsStyle,
|
|
205
|
+
animatedStyle,
|
|
206
|
+
]}
|
|
207
|
+
/**
|
|
208
|
+
* We use this testID to know when the carousel item is ready to be tested in test.
|
|
209
|
+
* e.g.
|
|
210
|
+
* The testID of first item will be changed to __CAROUSEL_ITEM_0_READY__ from __CAROUSEL_ITEM_0_NOT_READY__ when the item is ready.
|
|
211
|
+
* */
|
|
212
|
+
testID={`__CAROUSEL_ITEM_${index}__`}
|
|
213
|
+
>
|
|
214
|
+
{enhancedChild}
|
|
215
|
+
</Animated.View>
|
|
216
|
+
);
|
|
217
|
+
};
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { FC } from "react";
|
|
3
|
+
import type { ViewStyle } from "react-native";
|
|
4
|
+
import type { SharedValue } from "react-native-reanimated";
|
|
5
|
+
import { useAnimatedReaction } from "react-native-reanimated";
|
|
6
|
+
import { scheduleOnRN } from "react-native-worklets";
|
|
7
|
+
|
|
8
|
+
import type { TAnimationStyle } from "./ItemLayout";
|
|
9
|
+
import { ItemLayout } from "./ItemLayout";
|
|
10
|
+
|
|
11
|
+
import type { VisibleRanges } from "../hooks/useVisibleRanges";
|
|
12
|
+
import { useVisibleRanges } from "../hooks/useVisibleRanges";
|
|
13
|
+
import type { CarouselRenderItem } from "../types";
|
|
14
|
+
import { computedRealIndexWithAutoFillData } from "../utils/computed-with-auto-fill-data";
|
|
15
|
+
import type { SizeResolver } from "../utils/size-resolver";
|
|
16
|
+
|
|
17
|
+
interface Props {
|
|
18
|
+
data: any[];
|
|
19
|
+
dataLength: number;
|
|
20
|
+
rawDataLength: number;
|
|
21
|
+
loop: boolean;
|
|
22
|
+
size: number;
|
|
23
|
+
windowSize?: number;
|
|
24
|
+
autoFillData: boolean;
|
|
25
|
+
offsetX: SharedValue<number>;
|
|
26
|
+
handlerOffset: SharedValue<number>;
|
|
27
|
+
layoutConfig: TAnimationStyle;
|
|
28
|
+
renderItem: CarouselRenderItem<any>;
|
|
29
|
+
customAnimation?: (value: number, index: number) => ViewStyle;
|
|
30
|
+
sizeResolver: SizeResolver;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const ItemRenderer: FC<Props> = (props) => {
|
|
34
|
+
const {
|
|
35
|
+
data,
|
|
36
|
+
size,
|
|
37
|
+
windowSize,
|
|
38
|
+
handlerOffset,
|
|
39
|
+
offsetX,
|
|
40
|
+
dataLength,
|
|
41
|
+
rawDataLength,
|
|
42
|
+
loop,
|
|
43
|
+
autoFillData,
|
|
44
|
+
layoutConfig,
|
|
45
|
+
renderItem,
|
|
46
|
+
customAnimation,
|
|
47
|
+
sizeResolver,
|
|
48
|
+
} = props;
|
|
49
|
+
|
|
50
|
+
const visibleRanges = useVisibleRanges({
|
|
51
|
+
total: dataLength,
|
|
52
|
+
viewSize: size,
|
|
53
|
+
translation: handlerOffset,
|
|
54
|
+
windowSize,
|
|
55
|
+
loop,
|
|
56
|
+
resolver: sizeResolver,
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Initialize with a sensible default to avoid blank render on first frame
|
|
60
|
+
const initialRanges: VisibleRanges = React.useMemo(
|
|
61
|
+
() => ({
|
|
62
|
+
negativeRange: [0, 0],
|
|
63
|
+
positiveRange: [0, Math.min(dataLength - 1, (windowSize ?? dataLength) - 1)],
|
|
64
|
+
}),
|
|
65
|
+
[dataLength, windowSize]
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
const [displayedItems, setDisplayedItems] = React.useState<VisibleRanges>(initialRanges);
|
|
69
|
+
|
|
70
|
+
useAnimatedReaction(
|
|
71
|
+
() => visibleRanges.value,
|
|
72
|
+
(ranges) => scheduleOnRN(setDisplayedItems, ranges),
|
|
73
|
+
[visibleRanges]
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
return (
|
|
77
|
+
<>
|
|
78
|
+
{data.map((item, index) => {
|
|
79
|
+
const realIndex = computedRealIndexWithAutoFillData({
|
|
80
|
+
index,
|
|
81
|
+
dataLength: rawDataLength,
|
|
82
|
+
loop,
|
|
83
|
+
autoFillData,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const { negativeRange, positiveRange } = displayedItems;
|
|
87
|
+
|
|
88
|
+
const shouldRender =
|
|
89
|
+
(index >= negativeRange[0] && index <= negativeRange[1]) ||
|
|
90
|
+
(index >= positiveRange[0] && index <= positiveRange[1]);
|
|
91
|
+
|
|
92
|
+
if (!shouldRender) return null;
|
|
93
|
+
|
|
94
|
+
return (
|
|
95
|
+
<ItemLayout
|
|
96
|
+
key={index}
|
|
97
|
+
index={index}
|
|
98
|
+
handlerOffset={offsetX}
|
|
99
|
+
visibleRanges={visibleRanges}
|
|
100
|
+
animationStyle={customAnimation || layoutConfig}
|
|
101
|
+
>
|
|
102
|
+
{({ animationValue }) =>
|
|
103
|
+
renderItem({
|
|
104
|
+
item,
|
|
105
|
+
index: realIndex,
|
|
106
|
+
animationValue,
|
|
107
|
+
})
|
|
108
|
+
}
|
|
109
|
+
</ItemLayout>
|
|
110
|
+
);
|
|
111
|
+
})}
|
|
112
|
+
</>
|
|
113
|
+
);
|
|
114
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { render } from "@testing-library/react-native";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { Text } from "react-native";
|
|
4
|
+
|
|
5
|
+
import { LazyView } from "./LazyView";
|
|
6
|
+
|
|
7
|
+
describe("LazyView", () => {
|
|
8
|
+
it("should render children when shouldUpdate is true", () => {
|
|
9
|
+
const { getByText } = render(
|
|
10
|
+
<LazyView shouldUpdate={true}>
|
|
11
|
+
<Text>Test Child</Text>
|
|
12
|
+
</LazyView>
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
expect(getByText("Test Child")).toBeTruthy();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it("should not render children when shouldUpdate is false", () => {
|
|
19
|
+
const { queryByText } = render(
|
|
20
|
+
<LazyView shouldUpdate={false}>
|
|
21
|
+
<Text>Test Child</Text>
|
|
22
|
+
</LazyView>
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
expect(queryByText("Test Child")).toBeNull();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it("should render empty fragment when shouldUpdate is false", () => {
|
|
29
|
+
const { toJSON } = render(
|
|
30
|
+
<LazyView shouldUpdate={false}>
|
|
31
|
+
<Text>Test Child</Text>
|
|
32
|
+
</LazyView>
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
expect(toJSON()).toBeNull();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("should handle multiple children when shouldUpdate is true", () => {
|
|
39
|
+
const { getByText } = render(
|
|
40
|
+
<LazyView shouldUpdate={true}>
|
|
41
|
+
<Text>First Child</Text>
|
|
42
|
+
<Text>Second Child</Text>
|
|
43
|
+
</LazyView>
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
expect(getByText("First Child")).toBeTruthy();
|
|
47
|
+
expect(getByText("Second Child")).toBeTruthy();
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("should not render multiple children when shouldUpdate is false", () => {
|
|
51
|
+
const { queryByText } = render(
|
|
52
|
+
<LazyView shouldUpdate={false}>
|
|
53
|
+
<Text>First Child</Text>
|
|
54
|
+
<Text>Second Child</Text>
|
|
55
|
+
</LazyView>
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
expect(queryByText("First Child")).toBeNull();
|
|
59
|
+
expect(queryByText("Second Child")).toBeNull();
|
|
60
|
+
});
|
|
61
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { PropsWithChildren } from "react";
|
|
2
|
+
import React from "react";
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
shouldUpdate: boolean;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const LazyView: React.FC<PropsWithChildren<Props>> = (props) => {
|
|
9
|
+
const { shouldUpdate, children } = props;
|
|
10
|
+
|
|
11
|
+
if (!shouldUpdate) return <></>;
|
|
12
|
+
|
|
13
|
+
return <>{children}</>;
|
|
14
|
+
};
|