From c7770b4fc22550e3e0e852876707ca4a77057b89 Mon Sep 17 00:00:00 2001 From: Adam Hari Date: Wed, 30 Aug 2023 17:37:06 -0600 Subject: [PATCH 1/4] fix: apply panGestureHandlerProps to gesture --- src/ScrollViewGesture.tsx | 348 ++++++++++++++++++++------------ src/types.ts | 403 +++++++++++++++++++------------------- 2 files changed, 418 insertions(+), 333 deletions(-) diff --git a/src/ScrollViewGesture.tsx b/src/ScrollViewGesture.tsx index e2f4dace..04b2bfef 100644 --- a/src/ScrollViewGesture.tsx +++ b/src/ScrollViewGesture.tsx @@ -1,11 +1,11 @@ -import type { PropsWithChildren } from "react"; -import React, { useCallback, useMemo } from "react"; -import type { StyleProp, ViewStyle } from "react-native"; -import type { GestureStateChangeEvent, PanGestureHandlerEventPayload } from "react-native-gesture-handler"; -import { - Gesture, - GestureDetector, -} from "react-native-gesture-handler"; +import type { PropsWithChildren } from 'react'; +import React, { useCallback, useMemo } from 'react'; +import type { StyleProp, ViewStyle } from 'react-native'; +import type { + GestureStateChangeEvent, + PanGestureHandlerEventPayload, +} from 'react-native-gesture-handler'; +import { Gesture, GestureDetector, PanGesture } from 'react-native-gesture-handler'; import Animated, { cancelAnimation, measure, @@ -15,28 +15,29 @@ import Animated, { useDerivedValue, useSharedValue, withDecay, -} from "react-native-reanimated"; +} from 'react-native-reanimated'; -import { Easing } from "./constants"; -import { CTX } from "./store"; -import type { WithTimingAnimation } from "./types"; -import { dealWithAnimation } from "./utils/dealWithAnimation"; +import { Easing } from './constants'; +import { CTX } from './store'; +import type { WithTimingAnimation } from './types'; +import { dealWithAnimation } from './utils/dealWithAnimation'; interface Props { - size: number - infinite?: boolean - testID?: string - style?: StyleProp - onScrollBegin?: () => void - onScrollEnd?: () => void - onTouchBegin?: () => void - onTouchEnd?: () => void - translation: Animated.SharedValue + size: number; + infinite?: boolean; + testID?: string; + style?: StyleProp; + onScrollBegin?: () => void; + onScrollEnd?: () => void; + onTouchBegin?: () => void; + onTouchEnd?: () => void; + translation: Animated.SharedValue; } -const IScrollViewGesture: React.FC> = (props) => { +const IScrollViewGesture: React.FC> = props => { const { props: { + panGestureHandlerProps, vertical, pagingEnabled, snapEnabled, @@ -70,18 +71,17 @@ const IScrollViewGesture: React.FC> = (props) => { const scrollEndTranslation = useSharedValue(0); const scrollEndVelocity = useSharedValue(0); const containerRef = useAnimatedRef(); - const maxScrollDistancePerSwipeIsSet = typeof maxScrollDistancePerSwipe === "number"; + const maxScrollDistancePerSwipeIsSet = typeof maxScrollDistancePerSwipe === 'number'; // Get the limit of the scroll. const getLimit = React.useCallback(() => { - "worklet"; + 'worklet'; if (!infinite && !overscrollEnabled) { const { width: containerWidth = 0 } = measure(containerRef); // If the item's total width is less than the container's width, then there is no need to scroll. - if (dataLength * size < containerWidth) - return 0; + if (dataLength * size < containerWidth) return 0; // Disable the "overscroll" effect return dataLength * size - containerWidth; @@ -92,9 +92,9 @@ const IScrollViewGesture: React.FC> = (props) => { const withSpring = React.useCallback( (toValue: number, onFinished?: () => void) => { - "worklet"; + 'worklet'; const defaultWithAnimation: WithTimingAnimation = { - type: "timing", + type: 'timing', config: { duration: scrollAnimationDuration + 100, easing: Easing.easeOutQuart, @@ -104,9 +104,8 @@ const IScrollViewGesture: React.FC> = (props) => { return dealWithAnimation(withAnimation ?? defaultWithAnimation)( toValue, (isFinished: boolean) => { - "worklet"; - if (isFinished) - onFinished && runOnJS(onFinished)(); + 'worklet'; + if (isFinished) onFinished && runOnJS(onFinished)(); }, ); }, @@ -115,24 +114,26 @@ const IScrollViewGesture: React.FC> = (props) => { const endWithSpring = React.useCallback( (onFinished?: () => void) => { - "worklet"; + 'worklet'; const origin = translation.value; const velocity = scrollEndVelocity.value; // Default to scroll in the direction of the slide (with deceleration) let finalTranslation: number = withDecay({ velocity, deceleration: 0.999 }); // If the distance of the swipe exceeds the max scroll distance, keep the view at the current position - if (maxScrollDistancePerSwipeIsSet && Math.abs(scrollEndTranslation.value) > maxScrollDistancePerSwipe) { + if ( + maxScrollDistancePerSwipeIsSet && + Math.abs(scrollEndTranslation.value) > maxScrollDistancePerSwipe + ) { finalTranslation = origin; - } - else { - /** - * The page size is the same as the item size. - * If direction is vertical, the page size is the height of the item. - * If direction is horizontal, the page size is the width of the item. - * - * `page size` equals to `size` variable. - * */ + } else { + /** + * The page size is the same as the item size. + * If direction is vertical, the page size is the height of the item. + * If direction is horizontal, the page size is the width of the item. + * + * `page size` equals to `size` variable. + * */ if (pagingEnabled) { // distance with direction const offset = -(scrollEndTranslation.value >= 0 ? 1 : -1); // 1 or -1 @@ -142,8 +143,7 @@ const IScrollViewGesture: React.FC> = (props) => { if (infinite) { const finalPage = page + offset; finalTranslation = withSpring(withProcessTranslation(-finalPage * size), onFinished); - } - else { + } else { const finalPage = Math.min(maxPage - 1, Math.max(0, page + offset)); finalTranslation = withSpring(withProcessTranslation(-finalPage * size), onFinished); } @@ -185,7 +185,7 @@ const IScrollViewGesture: React.FC> = (props) => { const onFinish = React.useCallback( (isFinished: boolean) => { - "worklet"; + 'worklet'; if (isFinished) { touching.value = false; onScrollEnd && runOnJS(onScrollEnd)(); @@ -195,18 +195,16 @@ const IScrollViewGesture: React.FC> = (props) => { ); const activeDecay = React.useCallback(() => { - "worklet"; + 'worklet'; touching.value = true; - translation.value = withDecay( - { velocity: scrollEndVelocity.value }, - isFinished => onFinish(isFinished as boolean), + translation.value = withDecay({ velocity: scrollEndVelocity.value }, isFinished => + onFinish(isFinished as boolean), ); }, [onFinish, scrollEndVelocity.value, touching, translation]); const resetBoundary = React.useCallback(() => { - "worklet"; - if (touching.value) - return; + 'worklet'; + if (touching.value) return; if (translation.value > 0) { if (scrollEndTranslation.value < 0) { @@ -224,8 +222,7 @@ const IScrollViewGesture: React.FC> = (props) => { activeDecay(); return; } - if (!infinite) - translation.value = withSpring(-((maxPage - 1) * size)); + if (!infinite) translation.value = withSpring(-((maxPage - 1) * size)); } }, [ touching.value, @@ -241,14 +238,13 @@ const IScrollViewGesture: React.FC> = (props) => { useAnimatedReaction( () => translation.value, () => { - if (!pagingEnabled) - resetBoundary(); + if (!pagingEnabled) resetBoundary(); }, [pagingEnabled, resetBoundary], ); function withProcessTranslation(translation: number) { - "worklet"; + 'worklet'; if (!infinite && !overscrollEnabled) { const limit = getLimit(); @@ -260,15 +256,14 @@ const IScrollViewGesture: React.FC> = (props) => { } const onGestureBegin = useCallback(() => { - "worklet"; + 'worklet'; touching.value = true; validStart.value = true; onScrollBegin && runOnJS(onScrollBegin)(); max.value = (maxPage - 1) * size; - if (!infinite && !overscrollEnabled) - max.value = getLimit(); + if (!infinite && !overscrollEnabled) max.value = getLimit(); panOffset.value = translation.value; }, [ @@ -285,89 +280,184 @@ const IScrollViewGesture: React.FC> = (props) => { onScrollBegin, ]); - const onGestureUpdate = useCallback((e: PanGestureHandlerEventPayload) => { - "worklet"; + const onGestureUpdate = useCallback( + (e: PanGestureHandlerEventPayload) => { + 'worklet'; - if (validStart.value) { - validStart.value = false; - cancelAnimation(translation); - } - touching.value = true; - const { translationX, translationY } = e; - const panTranslation = isHorizontal.value - ? translationX - : translationY; - if (!infinite) { - if ((translation.value > 0 || translation.value < -max.value)) { - const boundary = translation.value > 0 ? 0 : -max.value; - const fixed = boundary - panOffset.value; - const dynamic = panTranslation - fixed; - translation.value = boundary + dynamic * 0.5; - return; + if (validStart.value) { + validStart.value = false; + cancelAnimation(translation); + } + touching.value = true; + const { translationX, translationY } = e; + const panTranslation = isHorizontal.value ? translationX : translationY; + if (!infinite) { + if (translation.value > 0 || translation.value < -max.value) { + const boundary = translation.value > 0 ? 0 : -max.value; + const fixed = boundary - panOffset.value; + const dynamic = panTranslation - fixed; + translation.value = boundary + dynamic * 0.5; + return; + } + } + + const translationValue = panOffset.value + panTranslation; + translation.value = translationValue; + }, + [isHorizontal, max, panOffset, infinite, overscrollEnabled, translation, validStart, touching], + ); + + const onGestureFinish = useCallback( + (e: GestureStateChangeEvent) => { + 'worklet'; + + const { velocityX, velocityY, translationX, translationY } = e; + scrollEndVelocity.value = isHorizontal.value ? velocityX : velocityY; + scrollEndTranslation.value = isHorizontal.value ? translationX : translationY; + + const totalTranslation = scrollEndVelocity.value + scrollEndTranslation.value; + + if ( + maxScrollDistancePerSwipeIsSet && + Math.abs(totalTranslation) > maxScrollDistancePerSwipe + ) { + const nextPage = + Math.round( + (panOffset.value + maxScrollDistancePerSwipe * Math.sign(totalTranslation)) / size, + ) * size; + translation.value = withSpring(withProcessTranslation(nextPage), onScrollEnd); + } else { + endWithSpring(onScrollEnd); } + + if (!infinite) touching.value = false; + }, + [ + size, + infinite, + touching, + panOffset, + translation, + isHorizontal, + scrollEndVelocity, + scrollEndTranslation, + maxScrollDistancePerSwipeIsSet, + maxScrollDistancePerSwipe, + endWithSpring, + withSpring, + onScrollEnd, + ], + ); + + const gesture = useMemo(() => { + const panGesture = Gesture.Pan() + .onBegin(onGestureBegin) + .onUpdate(onGestureUpdate) + .onEnd(onGestureFinish); + + const { + activateAfterLongPress, + activeOffsetXEnd, + activeOffsetXStart, + activeOffsetYEnd, + activeOffsetYStart, + avgTouches, + cancelsTouchesInView, + enabled, + enableTrackpadTwoFingerGesture, + failOffsetXEnd, + failOffsetXStart, + failOffsetYEnd, + failOffsetYStart, + hitSlop, + manualActivation, + maxPointers, + minDist, + minPointers, + minVelocity, + minVelocityX, + minVelocityY, + shouldCancelWhenOutside, + } = panGestureHandlerProps ?? {}; + + if (typeof activateAfterLongPress === 'number') { + panGesture.activateAfterLongPress(activateAfterLongPress); } - const translationValue = panOffset.value + panTranslation; - translation.value = translationValue; - }, [ - isHorizontal, - max, - panOffset, - infinite, - overscrollEnabled, - translation, - validStart, - touching, - ]); + if (typeof activeOffsetXStart === 'number' || typeof activeOffsetXEnd === 'number') { + panGesture.activeOffsetX([activeOffsetXStart ?? 0, activeOffsetXEnd ?? 0]); + } - const onGestureFinish = useCallback((e: GestureStateChangeEvent) => { - "worklet"; + if (typeof activeOffsetYStart === 'number' || typeof activeOffsetYEnd === 'number') { + panGesture.activeOffsetY([activeOffsetYStart ?? 0, activeOffsetYEnd ?? 0]); + } - const { velocityX, velocityY, translationX, translationY } = e; - scrollEndVelocity.value = isHorizontal.value - ? velocityX - : velocityY; - scrollEndTranslation.value = isHorizontal.value - ? translationX - : translationY; + if (typeof avgTouches === 'boolean') { + panGesture.averageTouches(avgTouches); + } - const totalTranslation = scrollEndVelocity.value + scrollEndTranslation.value; + if (typeof cancelsTouchesInView === 'boolean') { + panGesture.cancelsTouchesInView(cancelsTouchesInView); + } - if (maxScrollDistancePerSwipeIsSet && Math.abs(totalTranslation) > maxScrollDistancePerSwipe) { - const nextPage = Math.round((panOffset.value + maxScrollDistancePerSwipe * Math.sign(totalTranslation)) / size) * size; - translation.value = withSpring(withProcessTranslation(nextPage), onScrollEnd); + if (typeof enabled === 'boolean') { + panGesture.enabled(enabled); } - else { - endWithSpring(onScrollEnd); + + if (typeof enableTrackpadTwoFingerGesture === 'boolean') { + panGesture.enableTrackpadTwoFingerGesture(enableTrackpadTwoFingerGesture); } - if (!infinite) - touching.value = false; - }, [ - size, - infinite, - touching, - panOffset, - translation, - isHorizontal, - scrollEndVelocity, - scrollEndTranslation, - maxScrollDistancePerSwipeIsSet, - maxScrollDistancePerSwipe, - endWithSpring, - withSpring, - onScrollEnd, - ]); + if (typeof failOffsetXStart === 'number' || typeof failOffsetXEnd === 'number') { + panGesture.failOffsetX([failOffsetXStart ?? -1, failOffsetXEnd ?? 1]); + } - const gesture = useMemo(() => Gesture.Pan().onBegin(onGestureBegin).onUpdate(onGestureUpdate).onEnd(onGestureFinish), [ - onGestureBegin, - onGestureUpdate, - onGestureFinish, - ]); + if (typeof failOffsetYStart === 'number' || typeof failOffsetYEnd === 'number') { + panGesture.failOffsetY([failOffsetYStart ?? -1, failOffsetYEnd ?? 1]); + } + + if (typeof hitSlop === 'number') { + panGesture.hitSlop(hitSlop); + } + + if (typeof manualActivation === 'boolean') { + panGesture.manualActivation(manualActivation); + } + + if (typeof maxPointers === 'number') { + panGesture.maxPointers(maxPointers); + } + + if (typeof minDist === 'number') { + panGesture.minDistance(minDist); + } + + if (typeof minPointers === 'number') { + panGesture.minPointers(minPointers); + } + + if (typeof shouldCancelWhenOutside === 'boolean') { + panGesture.shouldCancelWhenOutside(shouldCancelWhenOutside); + } + + if (typeof minVelocity === 'number') { + panGesture.minVelocity(minVelocity); + } + + if (typeof minVelocityX === 'number') { + panGesture.minVelocityX(minVelocityX); + } + + if (typeof minVelocityY === 'number') { + panGesture.minVelocityY(minVelocityY); + } + + return panGesture; + }, [onGestureBegin, onGestureUpdate, onGestureFinish, panGestureHandlerProps]); const GestureContainer = enabled ? GestureDetector : React.Fragment; return ( - + = - | (T & - VP & { - /** - * Layout items vertically instead of horizontally - */ - vertical: true - /** - * Layout items vertically instead of horizontally - */ - /** - * Specified carousel container width. - */ - width?: number - height: number - }) - | (T & - HP & { - /** - * Layout items vertically instead of horizontally - */ - vertical?: false - /** - * Layout items vertically instead of horizontally - */ - /** - * Specified carousel container width. - */ - width: number - height?: number - }); + | (T & + VP & { + /** + * Layout items vertically instead of horizontally + */ + vertical: true; + /** + * Layout items vertically instead of horizontally + */ + /** + * Specified carousel container width. + */ + width?: number; + height: number; + }) + | (T & + HP & { + /** + * Layout items vertically instead of horizontally + */ + vertical?: false; + /** + * Layout items vertically instead of horizontally + */ + /** + * Specified carousel container width. + */ + width: number; + height?: number; + }); export interface CustomConfig { - type?: "negative" | "positive" - viewCount?: number + type?: 'negative' | 'positive'; + viewCount?: number; } export interface WithSpringAnimation { - type: "spring" - config: WithSpringConfig + type: 'spring'; + config: WithSpringConfig; } export interface WithTimingAnimation { - type: "timing" - config: WithTimingConfig + type: 'timing'; + config: WithTimingConfig; } export type WithAnimation = WithSpringAnimation | WithTimingAnimation; export type TCarouselProps = { - ref?: React.Ref - /** - * The default animated value of the carousel. - */ - defaultScrollOffsetValue?: SharedValue - /** - * Carousel loop playback. - * @default true - */ - loop?: boolean - /** - * Carousel items data set. - */ - data: T[] - /** - * Auto fill data array to allow loop playback when the loop props is true. - * @default true - * @example - * [1] => [1, 1, 1] - * [1, 2] => [1, 2, 1, 2] - */ - autoFillData?: boolean - /** - * Default index - * @default 0 - */ - defaultIndex?: number - /** - * Auto play - */ - autoPlay?: boolean - /** - * Auto play - * @description reverse playback - */ - autoPlayReverse?: boolean - /** - * Auto play - * @description playback interval - */ - autoPlayInterval?: number - /** - * Time a scroll animation takes to finish - * @default 500 (ms) - */ - scrollAnimationDuration?: number - /** - * Carousel container style - */ - style?: StyleProp - /** - * PanGestureHandler props - */ - panGestureHandlerProps?: Partial< - Omit - > - /** - * Determines the maximum number of items will respond to pan gesture events, - * windowSize={11} will active visible item plus up to 5 items above and 5 below the viewpor, - * Reducing this number will reduce the calculation of the animation value and may improve performance. - * @default 0 all items will respond to pan gesture events. - */ - windowSize?: number - /** - * When true, the scroll view stops on multiples of the scroll view's size when scrolling. - * @default true - */ - pagingEnabled?: boolean - /** - * If enabled, releasing the touch will scroll to the nearest item. - * valid when pagingEnabled=false - * @default true - */ - snapEnabled?: boolean - /** - * If enabled, items will scroll to the first placement when scrolling past the edge rather than closing to the last. (previous conditions: loop=false) - * @default true - */ - overscrollEnabled?: boolean - /** - * If false, Carousel will not respond to any gestures. - * @default true - */ - enabled?: boolean - /** - * Specifies the scrolling animation effect. - */ - withAnimation?: WithAnimation - /** - * Used to locate this view in end-to-end tests. - */ - testID?: string - /** - * Maximum offset value for once scroll. - * props.vertical = true => maxScrollDistancePerSwipeY - * props.vertical = false => maxScrollDistancePerSwipeX - * */ - maxScrollDistancePerSwipe?: number - /** - * Custom carousel config. - */ - customConfig?: () => CustomConfig - /** - * Custom animations. - * Must use `worklet`, Details: https://docs.swmansion.com/react-native-reanimated/docs/2.2.0/worklets/ - */ - customAnimation?: (value: number) => AnimatedStyleProp - /** - * Render carousel item. - */ - renderItem: CarouselRenderItem - /** - * Callback fired when navigating to an item. - */ - onSnapToItem?: (index: number) => void - /** - * On scroll begin - */ - onScrollBegin?: () => void - /** - * On scroll end - */ - onScrollEnd?: (index: number) => void - /** - * On progress change - * @param offsetProgress Total of offset distance (0 390 780 ...) - * @param absoluteProgress Convert to index (0 1 2 ...) - */ - onProgressChange?: ( - offsetProgress: number, - absoluteProgress: number - ) => void + ref?: React.Ref; + /** + * The default animated value of the carousel. + */ + defaultScrollOffsetValue?: SharedValue; + /** + * Carousel loop playback. + * @default true + */ + loop?: boolean; + /** + * Carousel items data set. + */ + data: T[]; + /** + * Auto fill data array to allow loop playback when the loop props is true. + * @default true + * @example + * [1] => [1, 1, 1] + * [1, 2] => [1, 2, 1, 2] + */ + autoFillData?: boolean; + /** + * Default index + * @default 0 + */ + defaultIndex?: number; + /** + * Auto play + */ + autoPlay?: boolean; + /** + * Auto play + * @description reverse playback + */ + autoPlayReverse?: boolean; + /** + * Auto play + * @description playback interval + */ + autoPlayInterval?: number; + /** + * Time a scroll animation takes to finish + * @default 500 (ms) + */ + scrollAnimationDuration?: number; + /** + * Carousel container style + */ + style?: StyleProp; + /** + * PanGestureHandler props + */ + panGestureHandlerProps?: BaseGestureConfig & PanGestureConfig; + /** + * Determines the maximum number of items will respond to pan gesture events, + * windowSize={11} will active visible item plus up to 5 items above and 5 below the viewpor, + * Reducing this number will reduce the calculation of the animation value and may improve performance. + * @default 0 all items will respond to pan gesture events. + */ + windowSize?: number; + /** + * When true, the scroll view stops on multiples of the scroll view's size when scrolling. + * @default true + */ + pagingEnabled?: boolean; + /** + * If enabled, releasing the touch will scroll to the nearest item. + * valid when pagingEnabled=false + * @default true + */ + snapEnabled?: boolean; + /** + * If enabled, items will scroll to the first placement when scrolling past the edge rather than closing to the last. (previous conditions: loop=false) + * @default true + */ + overscrollEnabled?: boolean; + /** + * If false, Carousel will not respond to any gestures. + * @default true + */ + enabled?: boolean; + /** + * Specifies the scrolling animation effect. + */ + withAnimation?: WithAnimation; + /** + * Used to locate this view in end-to-end tests. + */ + testID?: string; + /** + * Maximum offset value for once scroll. + * props.vertical = true => maxScrollDistancePerSwipeY + * props.vertical = false => maxScrollDistancePerSwipeX + * */ + maxScrollDistancePerSwipe?: number; + /** + * Custom carousel config. + */ + customConfig?: () => CustomConfig; + /** + * Custom animations. + * Must use `worklet`, Details: https://docs.swmansion.com/react-native-reanimated/docs/2.2.0/worklets/ + */ + customAnimation?: (value: number) => AnimatedStyleProp; + /** + * Render carousel item. + */ + renderItem: CarouselRenderItem; + /** + * Callback fired when navigating to an item. + */ + onSnapToItem?: (index: number) => void; + /** + * On scroll begin + */ + onScrollBegin?: () => void; + /** + * On scroll end + */ + onScrollEnd?: (index: number) => void; + /** + * On progress change + * @param offsetProgress Total of offset distance (0 390 780 ...) + * @param absoluteProgress Convert to index (0 1 2 ...) + */ + onProgressChange?: (offsetProgress: number, absoluteProgress: number) => void; // ============================== deprecated props ============================== /** - * If enabled, releasing the touch will scroll to the nearest item. - * valid when pagingEnabled=false - * @deprecated please use snapEnabled instead - */ - enableSnap?: boolean + * If enabled, releasing the touch will scroll to the nearest item. + * valid when pagingEnabled=false + * @deprecated please use snapEnabled instead + */ + enableSnap?: boolean; } & (TParallaxModeProps | TStackModeProps); export interface ICarouselInstance { /** - * Scroll to previous item, it takes one optional argument (count), - * which allows you to specify how many items to cross - */ - prev: (opts?: Omit) => void + * Scroll to previous item, it takes one optional argument (count), + * which allows you to specify how many items to cross + */ + prev: (opts?: Omit) => void; /** - * Scroll to next item, it takes one optional argument (count), - * which allows you to specify how many items to cross - */ - next: (opts?: Omit) => void + * Scroll to next item, it takes one optional argument (count), + * which allows you to specify how many items to cross + */ + next: (opts?: Omit) => void; /** - * Get current item index - */ - getCurrentIndex: () => number + * Get current item index + */ + getCurrentIndex: () => number; /** - * Use value to scroll to a position where relative to the current position, - * scrollTo(-2) is equivalent to prev(2), scrollTo(2) is equivalent to next(2) - */ - scrollTo: (opts?: TCarouselActionOptions) => void + * Use value to scroll to a position where relative to the current position, + * scrollTo(-2) is equivalent to prev(2), scrollTo(2) is equivalent to next(2) + */ + scrollTo: (opts?: TCarouselActionOptions) => void; } export interface CarouselRenderItemInfo { - item: ItemT - index: number - animationValue: Animated.SharedValue + item: ItemT; + index: number; + animationValue: Animated.SharedValue; } -export type CarouselRenderItem = ( - info: CarouselRenderItemInfo -) => React.ReactElement; +export type CarouselRenderItem = (info: CarouselRenderItemInfo) => React.ReactElement; export interface TCarouselActionOptions { - index?: number - count?: number - animated?: boolean - onFinished?: () => void + index?: number; + count?: number; + animated?: boolean; + onFinished?: () => void; } From 60b9006211fd3970cdcfaec53f322e9b87794457 Mon Sep 17 00:00:00 2001 From: Adam Hari Date: Wed, 30 Aug 2023 17:41:42 -0600 Subject: [PATCH 2/4] Create stupid-trains-impress.md --- .changeset/stupid-trains-impress.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/stupid-trains-impress.md diff --git a/.changeset/stupid-trains-impress.md b/.changeset/stupid-trains-impress.md new file mode 100644 index 00000000..3e1c7c7b --- /dev/null +++ b/.changeset/stupid-trains-impress.md @@ -0,0 +1,5 @@ +--- +"react-native-reanimated-carousel": patch +--- + +fix: apply panGestureHandlerProps to gesture From 283febd4eb903d38fc57a5966a708f72481ac3d7 Mon Sep 17 00:00:00 2001 From: Adam Hari Date: Wed, 30 Aug 2023 17:48:10 -0600 Subject: [PATCH 3/4] fix: remove inapplicable BaseGestureConfig properties from panGestureHandlerProps --- src/types.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/types.ts b/src/types.ts index e2907a8d..9671b031 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,5 +1,4 @@ import type { StyleProp, ViewStyle } from 'react-native'; -import type { PanGestureHandlerProps } from 'react-native-gesture-handler'; import type { AnimatedStyleProp, SharedValue, @@ -10,7 +9,6 @@ import type Animated from 'react-native-reanimated'; import type { TParallaxModeProps } from './layouts/parallax'; import type { TStackModeProps } from './layouts/stack'; -import { BaseGestureConfig } from 'react-native-gesture-handler/src/handlers/gestures/gesture'; import { PanGestureConfig } from 'react-native-gesture-handler/src/handlers/PanGestureHandler'; export type IComputedDirectionTypes = @@ -116,7 +114,7 @@ export type TCarouselProps = { /** * PanGestureHandler props */ - panGestureHandlerProps?: BaseGestureConfig & PanGestureConfig; + panGestureHandlerProps?: PanGestureConfig; /** * Determines the maximum number of items will respond to pan gesture events, * windowSize={11} will active visible item plus up to 5 items above and 5 below the viewpor, From 77caa062fe3b899bdb028b08a1969c96f6514986 Mon Sep 17 00:00:00 2001 From: Adam Hari Date: Mon, 20 Nov 2023 08:44:37 -0700 Subject: [PATCH 4/4] fix: fix panGestureHandlerProps type issue replaces PanGestureConfig with PanGestureHandlerProps --- src/ScrollViewGesture.tsx | 61 ++++++++++++++------------------------- src/types.ts | 7 ++--- 2 files changed, 25 insertions(+), 43 deletions(-) diff --git a/src/ScrollViewGesture.tsx b/src/ScrollViewGesture.tsx index 04b2bfef..2d81cfcc 100644 --- a/src/ScrollViewGesture.tsx +++ b/src/ScrollViewGesture.tsx @@ -5,7 +5,7 @@ import type { GestureStateChangeEvent, PanGestureHandlerEventPayload, } from 'react-native-gesture-handler'; -import { Gesture, GestureDetector, PanGesture } from 'react-native-gesture-handler'; +import { Gesture, GestureDetector } from 'react-native-gesture-handler'; import Animated, { cancelAnimation, measure, @@ -357,49 +357,40 @@ const IScrollViewGesture: React.FC> = props => { const { activateAfterLongPress, - activeOffsetXEnd, - activeOffsetXStart, - activeOffsetYEnd, - activeOffsetYStart, + activeOffsetX, + activeOffsetY, avgTouches, - cancelsTouchesInView, - enabled, enableTrackpadTwoFingerGesture, - failOffsetXEnd, - failOffsetXStart, - failOffsetYEnd, - failOffsetYStart, - hitSlop, - manualActivation, + failOffsetY, + failOffsetX, maxPointers, minDist, minPointers, minVelocity, minVelocityX, minVelocityY, - shouldCancelWhenOutside, } = panGestureHandlerProps ?? {}; if (typeof activateAfterLongPress === 'number') { panGesture.activateAfterLongPress(activateAfterLongPress); } - if (typeof activeOffsetXStart === 'number' || typeof activeOffsetXEnd === 'number') { - panGesture.activeOffsetX([activeOffsetXStart ?? 0, activeOffsetXEnd ?? 0]); + if (typeof activeOffsetX === 'number') { + panGesture.activeOffsetX([-activeOffsetX, activeOffsetX]); + } else if (Array.isArray(activeOffsetX)) { + panGesture.activeOffsetX(activeOffsetX); } - if (typeof activeOffsetYStart === 'number' || typeof activeOffsetYEnd === 'number') { - panGesture.activeOffsetY([activeOffsetYStart ?? 0, activeOffsetYEnd ?? 0]); + if (typeof activeOffsetY === 'number') { + panGesture.activeOffsetY([-activeOffsetY, activeOffsetY]); + } else if (Array.isArray(activeOffsetY)) { + panGesture.activeOffsetX(activeOffsetY); } if (typeof avgTouches === 'boolean') { panGesture.averageTouches(avgTouches); } - if (typeof cancelsTouchesInView === 'boolean') { - panGesture.cancelsTouchesInView(cancelsTouchesInView); - } - if (typeof enabled === 'boolean') { panGesture.enabled(enabled); } @@ -408,20 +399,16 @@ const IScrollViewGesture: React.FC> = props => { panGesture.enableTrackpadTwoFingerGesture(enableTrackpadTwoFingerGesture); } - if (typeof failOffsetXStart === 'number' || typeof failOffsetXEnd === 'number') { - panGesture.failOffsetX([failOffsetXStart ?? -1, failOffsetXEnd ?? 1]); - } - - if (typeof failOffsetYStart === 'number' || typeof failOffsetYEnd === 'number') { - panGesture.failOffsetY([failOffsetYStart ?? -1, failOffsetYEnd ?? 1]); + if (typeof failOffsetX === 'number') { + panGesture.failOffsetX([-failOffsetX, failOffsetX]); + } else if (Array.isArray(failOffsetX)) { + panGesture.activeOffsetX(failOffsetX); } - if (typeof hitSlop === 'number') { - panGesture.hitSlop(hitSlop); - } - - if (typeof manualActivation === 'boolean') { - panGesture.manualActivation(manualActivation); + if (typeof failOffsetY === 'number') { + panGesture.failOffsetY([-failOffsetY, failOffsetY]); + } else if (Array.isArray(failOffsetY)) { + panGesture.activeOffsetX(failOffsetY); } if (typeof maxPointers === 'number') { @@ -436,10 +423,6 @@ const IScrollViewGesture: React.FC> = props => { panGesture.minPointers(minPointers); } - if (typeof shouldCancelWhenOutside === 'boolean') { - panGesture.shouldCancelWhenOutside(shouldCancelWhenOutside); - } - if (typeof minVelocity === 'number') { panGesture.minVelocity(minVelocity); } @@ -457,7 +440,7 @@ const IScrollViewGesture: React.FC> = props => { const GestureContainer = enabled ? GestureDetector : React.Fragment; return ( - + = | (T & @@ -114,7 +113,7 @@ export type TCarouselProps = { /** * PanGestureHandler props */ - panGestureHandlerProps?: PanGestureConfig; + panGestureHandlerProps?: PanGestureHandlerProps; /** * Determines the maximum number of items will respond to pan gesture events, * windowSize={11} will active visible item plus up to 5 items above and 5 below the viewpor,