diff --git a/README.md b/README.md index 63d394d..fb7c5b2 100644 --- a/README.md +++ b/README.md @@ -131,7 +131,8 @@ enum EAnimationType { 'right-swipe' = 'right-swipe', // Reveals content when swiping right 'left-right-swipe' = 'left-right-swipe', // Reveals content when swiping either direction 'right-full-swipe' = 'right-full-swipe', // Triggers action on full right swipe - 'left-full-swipe' = 'left-full-swipe' // Triggers action on full left swipe + 'left-full-swipe' = 'left-full-swipe', // Triggers action on full left swipe + 'combo-left-swipe' = 'combo-left-swipe', // Combines full left swipe with left swipe actions (e.g. iOS notifications) } ``` diff --git a/example/src/constants.ts b/example/src/constants.ts index 541ffcc..de9fea9 100644 --- a/example/src/constants.ts +++ b/example/src/constants.ts @@ -22,7 +22,7 @@ export const SONGS = [ title: 'Viva La Vida', singer: 'Coldplay', imageSrc: 'https://i.ytimg.com/vi/dvgZkm1xWPE/maxresdefault.jpg', - type: EAnimationType['left-right-swipe'], + type: EAnimationType['combo-left-swipe'], }, { id: 3, diff --git a/example/src/screens/List.tsx b/example/src/screens/List.tsx index 20b76a5..b876e06 100644 --- a/example/src/screens/List.tsx +++ b/example/src/screens/List.tsx @@ -27,7 +27,8 @@ export const List = () => { animationType={item.type} leftSwipeView={ item.type === EAnimationType['left-swipe'] || - item.type === EAnimationType['left-right-swipe'] ? ( + item.type === EAnimationType['left-right-swipe'] || + item.type === EAnimationType['combo-left-swipe'] ? ( { ) : undefined } leftFullSwipeView={ - item.type === EAnimationType['left-full-swipe'] ? ( + item.type === EAnimationType['left-full-swipe'] || + item.type === EAnimationType['combo-left-swipe'] ? ( { )} keyExtractor={(item) => item.id.toString()} + contentContainerStyle={styles.listContentContainer} /> ); diff --git a/package.json b/package.json index 3b70843..46737d9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-swipe-reveal", - "version": "0.1.6", + "version": "0.2.0", "description": "✨ Buttery-smooth Swipeable Wrapper for React Native - Powered by Reanimated V3 ✨", "source": "./src/index.tsx", "main": "./lib/commonjs/index.js", @@ -56,6 +56,12 @@ "url": "git+https://github.com/varunkukade/react-native-swipe-reveal.git" }, "author": "varunkukade (https://github.com/varunkukade)", + "contributors": [ + { + "name": "savaughn", + "url": "https://github.com/savaughn" + } + ], "license": "MIT", "bugs": { "url": "https://github.com/varunkukade/react-native-swipe-reveal/issues" @@ -65,6 +71,7 @@ "registry": "https://registry.npmjs.org/" }, "devDependencies": { + "@babel/runtime": "^7.27.6", "@commitlint/config-conventional": "^17.0.2", "@evilmartians/lefthook": "^1.5.0", "@react-native-community/cli": "15.0.1", diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..d8d4606 --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,3 @@ +packages: + - 'example' + - '.' \ No newline at end of file diff --git a/src/__tests__/index.test.tsx b/src/__tests__/index.test.tsx index bf84291..b20e78e 100644 --- a/src/__tests__/index.test.tsx +++ b/src/__tests__/index.test.tsx @@ -1 +1 @@ -it.todo('write a test'); +it.todo('write a test'); \ No newline at end of file diff --git a/src/components/GestureDetectorComponent.tsx b/src/components/GestureDetectorComponent.tsx index 040248f..c5f311e 100644 --- a/src/components/GestureDetectorComponent.tsx +++ b/src/components/GestureDetectorComponent.tsx @@ -42,6 +42,13 @@ export const GestureDetectorComponent = ({ return animationType === EAnimationType['right-full-swipe']; }, [animationType]); + const isComboLeftSwipe = useMemo(() => { + return ( + animationType === EAnimationType['combo-left-swipe'] && + leftSwipeViewWidth !== 0 + ); + }, [animationType, leftSwipeViewWidth, rightSwipeViewWidth]); + const { panXAnimatedStyles, panXGesture } = usePanXGesture( leftSwipeViewWidth, rightSwipeViewWidth, @@ -52,10 +59,11 @@ export const GestureDetectorComponent = ({ isRightSwipe, isLeftFullSwipe, isRightFullSwipe, + isComboLeftSwipe, itemWidth ); - return isLeftSwipe || isRightSwipe || isLeftFullSwipe || isRightFullSwipe ? ( + return isLeftSwipe || isRightSwipe || isLeftFullSwipe || isRightFullSwipe || isComboLeftSwipe ? ( {(animationType === EAnimationType['left-swipe'] || - animationType === EAnimationType['left-right-swipe']) && + animationType === EAnimationType['left-right-swipe'] || + animationType === EAnimationType['combo-left-swipe']) && leftSwipeView ? ( { //this is used to make scrollview active with pangesture @@ -145,18 +146,30 @@ export const usePanXGesture = ( } } if (dragDirectionShared.value === EDraggingDirection.left) { - if (!isLeftSwipe && !isLeftFullSwipe) { - return; - } - if (dragX < 0) { - if (isLeftSwipe && getLeftPanX(dragX) > leftSwipeViewWidth) { - return; + if (isComboLeftSwipe) { + if (dragX < 0) { + // Limit to leftSwipeViewWidth for half swipe, allow up to -itemWidth for full swipe + if (getLeftPanX(dragX) > itemWidth) { + return; + } + offsetX.value = dragX; + } else { + resetOffsets(); } - //drag item to left - offsetX.value = dragX; } else { - //while dragging left, if dragged to rightmost end reset values - resetOffsets(); + if (!isLeftSwipe && !isLeftFullSwipe) { + return; + } + if (dragX < 0) { + if (isLeftSwipe && getLeftPanX(dragX) > leftSwipeViewWidth) { + return; + } + //drag item to left + offsetX.value = dragX; + } else { + //while dragging left, if dragged to rightmost end reset values + resetOffsets(); + } } } }) @@ -200,27 +213,9 @@ export const usePanXGesture = ( } } else if (dragDirectionShared.value === EDraggingDirection.left) { //moving to left side - - if (isLeftSwipe) { - if (getLeftPanX(offsetX.value) >= leftSwipeViewWidth / 2) { - //move to left drag boundary - - //we set -leftSwipeViewWidth, as moving from left to right, values should be negative. - offsetX.value = withTiming(-leftSwipeViewWidth, { - duration: ANIMATION_DURATION, - }); - startX.value = -leftSwipeViewWidth; - } else if (getLeftPanX(offsetX.value) < leftSwipeViewWidth / 2) { - //move to rightmost end - resetOffsets(ANIMATION_DURATION); - } - } - - if (isLeftFullSwipe) { + if (isComboLeftSwipe) { if (getLeftPanX(offsetX.value) >= itemWidth / 2) { - //move to leftmost end and remove item - - //we set -itemWidth, as moving from left to right, values should be negative. + // Full swipe: move off screen and call function offsetX.value = withTiming( -itemWidth, { @@ -233,10 +228,55 @@ export const usePanXGesture = ( } ); startX.value = -itemWidth; - } else if (getLeftPanX(offsetX.value) < itemWidth / 2) { - //move to rightmost end + } else if (getLeftPanX(offsetX.value) >= leftSwipeViewWidth / 2) { + // Half swipe: snap to leftSwipeViewWidth + offsetX.value = withTiming(-leftSwipeViewWidth, { + duration: ANIMATION_DURATION, + }); + startX.value = -leftSwipeViewWidth; + } else { + // Not enough swipe: reset resetOffsets(ANIMATION_DURATION); } + } else { + + if (isLeftSwipe) { + if (getLeftPanX(offsetX.value) >= leftSwipeViewWidth / 2) { + //move to left drag boundary + + //we set -leftSwipeViewWidth, as moving from left to right, values should be negative. + offsetX.value = withTiming(-leftSwipeViewWidth, { + duration: ANIMATION_DURATION, + }); + startX.value = -leftSwipeViewWidth; + } else if (getLeftPanX(offsetX.value) < leftSwipeViewWidth / 2) { + //move to rightmost end + resetOffsets(ANIMATION_DURATION); + } + } + + if (isLeftFullSwipe) { + if (getLeftPanX(offsetX.value) >= itemWidth / 2) { + //move to leftmost end and remove item + + //we set -itemWidth, as moving from left to right, values should be negative. + offsetX.value = withTiming( + -itemWidth, + { + duration: ANIMATION_DURATION, + }, + () => { + if (typeof onLeftFullSwipe === 'function') { + runOnJS(onLeftFullSwipe)(id); + } + } + ); + startX.value = -itemWidth; + } else if (getLeftPanX(offsetX.value) < itemWidth / 2) { + //move to rightmost end + resetOffsets(ANIMATION_DURATION); + } + } } } else { //reset all values