diff --git a/example/package.json b/example/package.json index 41f4fd6..0618012 100644 --- a/example/package.json +++ b/example/package.json @@ -6,13 +6,15 @@ "scripts": { "android": "react-native run-android", "ios": "react-native run-ios", - "start": "react-native start" + "start": "react-native start", + "posxtinstall": "npx patch-package" }, "dependencies": { "@gorhom/portal": "^0.1.4", "@gorhom/showcase-template": "^1.0.2", "@react-native-community/blur": "^3.6.0", "@react-native-community/masked-view": "0.1.10", + "@react-navigation/material-top-tabs": "^5.3.10", "@react-navigation/native": "^5.8.10", "@react-navigation/stack": "^5.12.8", "faker": "^4.1.0", @@ -25,7 +27,8 @@ "react-native-reanimated": "^2.0.0-rc.1", "react-native-redash": "^16.0.4", "react-native-safe-area-context": "0.7.3", - "react-native-screens": "^2.16.1" + "react-native-screens": "^2.16.1", + "react-native-tab-view": "^2.15.2" }, "devDependencies": { "@babel/core": "^7.12.10", diff --git a/example/patches/react-native-tab-view+2.15.2.patch b/example/patches/react-native-tab-view+2.15.2.patch new file mode 100644 index 0000000..c3f6aba --- /dev/null +++ b/example/patches/react-native-tab-view+2.15.2.patch @@ -0,0 +1,138 @@ +diff --git a/node_modules/react-native-tab-view/lib/commonjs/Pager.js b/node_modules/react-native-tab-view/lib/commonjs/Pager.js +index 1c491e8..14241b1 100644 +--- a/node_modules/react-native-tab-view/lib/commonjs/Pager.js ++++ b/node_modules/react-native-tab-view/lib/commonjs/Pager.js +@@ -96,7 +96,10 @@ class Pager extends React.Component { + + _defineProperty(this, "mounted", false); + ++ _defineProperty(this, "gestureHandlerRef", /*#__PURE__*/React.createRef()); ++ + _defineProperty(this, "providerVal", { ++ pagerRef: this.gestureHandlerRef, + addGestureHandlerRef: ref => { + if (!this.state.childPanGestureHandlerRefs.includes(ref) && this.mounted) { + this.setState(prevState => ({ +@@ -106,8 +109,6 @@ class Pager extends React.Component { + } + }); + +- _defineProperty(this, "gestureHandlerRef", /*#__PURE__*/React.createRef()); +- + _defineProperty(this, "clock", new Clock()); + + _defineProperty(this, "velocityX", new Value(0)); +diff --git a/node_modules/react-native-tab-view/lib/commonjs/index.js b/node_modules/react-native-tab-view/lib/commonjs/index.js +index e3f00fc..9a39fc8 100644 +--- a/node_modules/react-native-tab-view/lib/commonjs/index.js ++++ b/node_modules/react-native-tab-view/lib/commonjs/index.js +@@ -15,6 +15,12 @@ Object.defineProperty(exports, "TabView", { + return _TabView.default; + } + }); ++Object.defineProperty(exports, "Pager", { ++ enumerable: true, ++ get: function get() { ++ return _Pager.default; ++ } ++}); + Object.defineProperty(exports, "TabBarIndicator", { + enumerable: true, + get: function get() { +@@ -60,5 +66,7 @@ var _SceneMap = _interopRequireDefault(require("./SceneMap")); + + var _ScrollPager = _interopRequireDefault(require("./ScrollPager")); + ++var _Pager = _interopRequireDefault(require("./Pager")); ++ + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + //# sourceMappingURL=index.js.map +\ No newline at end of file +diff --git a/node_modules/react-native-tab-view/lib/module/Pager.js b/node_modules/react-native-tab-view/lib/module/Pager.js +index 406e30a..4482bcb 100644 +--- a/node_modules/react-native-tab-view/lib/module/Pager.js ++++ b/node_modules/react-native-tab-view/lib/module/Pager.js +@@ -78,7 +78,10 @@ export default class Pager extends React.Component { + + _defineProperty(this, "mounted", false); + ++ _defineProperty(this, "gestureHandlerRef", /*#__PURE__*/React.createRef()); ++ + _defineProperty(this, "providerVal", { ++ pagerRef: this.gestureHandlerRef, + addGestureHandlerRef: ref => { + if (!this.state.childPanGestureHandlerRefs.includes(ref) && this.mounted) { + this.setState(prevState => ({ +@@ -88,8 +91,6 @@ export default class Pager extends React.Component { + } + }); + +- _defineProperty(this, "gestureHandlerRef", /*#__PURE__*/React.createRef()); +- + _defineProperty(this, "clock", new Clock()); + + _defineProperty(this, "velocityX", new Value(0)); +diff --git a/node_modules/react-native-tab-view/lib/module/index.js b/node_modules/react-native-tab-view/lib/module/index.js +index c687a54..5466d89 100644 +--- a/node_modules/react-native-tab-view/lib/module/index.js ++++ b/node_modules/react-native-tab-view/lib/module/index.js +@@ -5,4 +5,5 @@ export { default as TabBarItem } from './TabBarItem'; + export { default as TouchableItem } from './TouchableItem'; + export { default as SceneMap } from './SceneMap'; + export { default as ScrollPager } from './ScrollPager'; ++export { default as Pager } from './Pager'; + //# sourceMappingURL=index.js.map +\ No newline at end of file +diff --git a/node_modules/react-native-tab-view/lib/typescript/src/index.d.ts b/node_modules/react-native-tab-view/lib/typescript/src/index.d.ts +index 60cada6..2dc8881 100644 +--- a/node_modules/react-native-tab-view/lib/typescript/src/index.d.ts ++++ b/node_modules/react-native-tab-view/lib/typescript/src/index.d.ts +@@ -9,4 +9,5 @@ export type { Props as TabBarItemProps } from './TabBarItem'; + export { default as TouchableItem } from './TouchableItem'; + export { default as SceneMap } from './SceneMap'; + export { default as ScrollPager } from './ScrollPager'; ++export { default as Pager } from './Pager'; + export type { Route, NavigationState, SceneRendererProps } from './types'; +diff --git a/node_modules/react-native-tab-view/src/Pager.tsx b/node_modules/react-native-tab-view/src/Pager.tsx +index 3441d69..1202c35 100644 +--- a/node_modules/react-native-tab-view/src/Pager.tsx ++++ b/node_modules/react-native-tab-view/src/Pager.tsx +@@ -245,9 +245,15 @@ export default class Pager extends React.Component< + + static contextType = PagerContext; + ++ // PanGestureHandler ref used for coordination with parent handlers ++ private gestureHandlerRef: React.RefObject< ++ PanGestureHandler ++ > = React.createRef(); ++ + // Mechanism to add child PanGestureHandler refs in the case that this + // Pager is a parent to child Pagers. Allows for coordination between handlers + private providerVal = { ++ pagerRef: this.gestureHandlerRef, + addGestureHandlerRef: (ref: React.RefObject) => { + if ( + !this.state.childPanGestureHandlerRefs.includes(ref) && +@@ -263,11 +269,6 @@ export default class Pager extends React.Component< + }, + }; + +- // PanGestureHandler ref used for coordination with parent handlers +- private gestureHandlerRef: React.RefObject< +- PanGestureHandler +- > = React.createRef(); +- + // Clock used for tab transition animations + private clock = new Clock(); + +diff --git a/node_modules/react-native-tab-view/src/index.tsx b/node_modules/react-native-tab-view/src/index.tsx +index ce01571..a7dffb4 100644 +--- a/node_modules/react-native-tab-view/src/index.tsx ++++ b/node_modules/react-native-tab-view/src/index.tsx +@@ -14,5 +14,6 @@ export { default as TouchableItem } from './TouchableItem'; + + export { default as SceneMap } from './SceneMap'; + export { default as ScrollPager } from './ScrollPager'; ++export { default as Pager } from './Pager'; + + export type { Route, NavigationState, SceneRendererProps } from './types'; diff --git a/example/src/App.tsx b/example/src/App.tsx index f1a308e..b4eb9a5 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -133,6 +133,16 @@ function App() { require('./screens/advanced/DynamicSnapPointExample').default } /> + + + require('./screens/advanced/ViewPagerExample').default + } + /> diff --git a/example/src/screens/Root.tsx b/example/src/screens/Root.tsx index f367bda..b2889ff 100644 --- a/example/src/screens/Root.tsx +++ b/example/src/screens/Root.tsx @@ -70,6 +70,10 @@ const data = [ name: 'Dynamic Snap Point', slug: 'Advanced/DynamicSnapPointExample', }, + { + name: 'View Pager', + slug: 'Advanced/ViewPagerExample', + }, ], }, ]; diff --git a/example/src/screens/advanced/ViewPagerExample.tsx b/example/src/screens/advanced/ViewPagerExample.tsx new file mode 100644 index 0000000..09535a9 --- /dev/null +++ b/example/src/screens/advanced/ViewPagerExample.tsx @@ -0,0 +1,61 @@ +import React, { useContext, useMemo } from 'react'; +import { View, Text, StyleSheet } from 'react-native'; +import { createMaterialTopTabNavigator } from '@react-navigation/material-top-tabs'; +import { Pager } from 'react-native-tab-view'; +import BottomSheet from '@gorhom/bottom-sheet'; +import ContactList from '../../components/contactList'; + +const FirstRoute = () => { + // @ts-ignore + const { pagerRef } = useContext(Pager.contextType); + const snapPoints = useMemo(() => ['25%', '50%', '90%'], []); + + return ( + + + + + + ); +}; + +const SecondRoute = () => ( + + 🙈 + +); + +const Tab = createMaterialTopTabNavigator(); + +const ViewPagerScreen = () => { + return ( + + + + + ); +}; + +const styles = StyleSheet.create({ + scene: { + flex: 1, + }, + firstScene: { + backgroundColor: '#ff4081', + }, + secondScene: { + alignContent: 'center', + alignItems: 'center', + justifyContent: 'center', + backgroundColor: '#673ab7', + }, + emoji: { + fontSize: 46, + }, +}); + +export default ViewPagerScreen; diff --git a/example/src/types.d.ts b/example/src/types.d.ts index 02d745d..d19056a 100644 --- a/example/src/types.d.ts +++ b/example/src/types.d.ts @@ -19,6 +19,7 @@ export type AppStackParamsList = { ['Advanced/BackdropExample']: undefined; ['Advanced/MapExample']: undefined; ['Advanced/DynamicSnapPointExample']: undefined; + ['Advanced/ViewPagerExample']: undefined; }; export type Contact = { diff --git a/example/yarn.lock b/example/yarn.lock index 5705acf..2a08d7c 100644 --- a/example/yarn.lock +++ b/example/yarn.lock @@ -1102,6 +1102,13 @@ query-string "^6.13.6" react-is "^16.13.0" +"@react-navigation/material-top-tabs@^5.3.10": + version "5.3.10" + resolved "https://registry.yarnpkg.com/@react-navigation/material-top-tabs/-/material-top-tabs-5.3.10.tgz#158b694e87bff2eb9577e8142415de8ac3547412" + integrity sha512-mmQYEBhcLp1DwvuD8+HiFtYPk5zP43272C/38iX2T8AblcwRdoJejuO/GUzQcEPrmZHjeAnA5GDaMiXQM4EXLQ== + dependencies: + color "^3.1.3" + "@react-navigation/native@^5.8.10": version "5.8.10" resolved "https://registry.yarnpkg.com/@react-navigation/native/-/native-5.8.10.tgz#3fe806abff9efb085bcf595212803dd05a1347ca" @@ -3904,6 +3911,11 @@ react-native-screens@^2.16.1: resolved "https://registry.yarnpkg.com/react-native-screens/-/react-native-screens-2.16.1.tgz#b105a127378d90018a46daf0c2f6518fca60c06f" integrity sha512-WZ7m0sBDVaHbBnlHxwQnUlI6KNfQKHq+Unfw+VBuAlnSXvT+aw6Bb/K2bUlHzBgvrPjwY3Spc7ZERFuTwRLLwg== +react-native-tab-view@^2.15.2: + version "2.15.2" + resolved "https://registry.yarnpkg.com/react-native-tab-view/-/react-native-tab-view-2.15.2.tgz#4bc7832d33a119306614efee667509672a7ee64e" + integrity sha512-2hxLkBnZtEKFDyfvNO5EUywhy3f/EiLOBO8SWqKj4BMBTO0QwnybaPE5MVF00Fhz+VA4+h/iI40Dkrrtq70dGg== + react-native@0.63.4: version "0.63.4" resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.63.4.tgz#2210fdd404c94a5fa6b423c6de86f8e48810ec36" diff --git a/src/components/bottomSheet/BottomSheet.tsx b/src/components/bottomSheet/BottomSheet.tsx index b5fb905..b1f284e 100644 --- a/src/components/bottomSheet/BottomSheet.tsx +++ b/src/components/bottomSheet/BottomSheet.tsx @@ -92,6 +92,11 @@ const BottomSheetComponent = forwardRef( // animated callback shared values animatedPosition: _providedAnimatedPosition, animatedIndex: _providedAnimatedIndex, + + // gestures + simultaneousHandlers: _providedSimultaneousHandlers, + waitFor: _providedWaitFor, + // callbacks onChange: _providedOnChange, onAnimate: _providedOnAnimate, @@ -395,6 +400,8 @@ const BottomSheetComponent = forwardRef( contentPanGestureHandler, scrollableContentOffsetY, scrollableDecelerationRate, + simultaneousHandlers: _providedSimultaneousHandlers, + waitFor: _providedWaitFor, setScrollableRef: handleSettingScrollableRef, removeScrollableRef, }), @@ -409,6 +416,8 @@ const BottomSheetComponent = forwardRef( scrollableContentOffsetY, scrollableDecelerationRate, enableContentPanningGesture, + _providedSimultaneousHandlers, + _providedWaitFor, ] ); const externalContextVariables = useMemo( diff --git a/src/components/bottomSheet/types.d.ts b/src/components/bottomSheet/types.d.ts index 5dd33ac..1d97dda 100644 --- a/src/components/bottomSheet/types.d.ts +++ b/src/components/bottomSheet/types.d.ts @@ -1,10 +1,13 @@ import type React from 'react'; import type Animated from 'react-native-reanimated'; +import type { GestureHandlerProperties } from 'react-native-gesture-handler'; import type { BottomSheetHandleProps } from '../bottomSheetHandle'; import type { BottomSheetBackdropProps } from '../bottomSheetBackdrop'; import type { BottomSheetBackgroundProps } from '../bottomSheetBackground'; -export interface BottomSheetProps extends BottomSheetAnimationConfigs { +export interface BottomSheetProps + extends BottomSheetAnimationConfigs, + Pick { // configuration /** * Initial snap index, you also could provide `-1` to initiate bottom sheet in closed state. diff --git a/src/components/bottomSheetDraggableView/BottomSheetDraggableView.tsx b/src/components/bottomSheetDraggableView/BottomSheetDraggableView.tsx index e99b9e7..7bf2707 100644 --- a/src/components/bottomSheetDraggableView/BottomSheetDraggableView.tsx +++ b/src/components/bottomSheetDraggableView/BottomSheetDraggableView.tsx @@ -20,16 +20,32 @@ const BottomSheetDraggableViewComponent = ({ enableContentPanningGesture, contentWrapperGestureRef, contentPanGestureHandler, + simultaneousHandlers: _providedSimultaneousHandlers, + waitFor, } = useBottomSheetInternal(); // variables - const simultaneousHandlers = useMemo( - () => - nativeGestureRef - ? [contentWrapperGestureRef, nativeGestureRef] - : contentWrapperGestureRef, - [contentWrapperGestureRef, nativeGestureRef] - ); + const simultaneousHandlers = useMemo(() => { + const refs = [contentWrapperGestureRef]; + + if (nativeGestureRef) { + refs.push(nativeGestureRef); + } + + if (_providedSimultaneousHandlers) { + if (Array.isArray(_providedSimultaneousHandlers)) { + refs.push(..._providedSimultaneousHandlers); + } else { + refs.push(_providedSimultaneousHandlers); + } + } + + return refs; + }, [ + _providedSimultaneousHandlers, + contentWrapperGestureRef, + nativeGestureRef, + ]); // styles const containerStyle = useMemo( @@ -43,6 +59,7 @@ const BottomSheetDraggableViewComponent = ({ enabled={enableContentPanningGesture} simultaneousHandlers={simultaneousHandlers} shouldCancelWhenOutside={false} + waitFor={waitFor} onGestureEvent={contentPanGestureHandler} > diff --git a/src/contexts/internal.ts b/src/contexts/internal.ts index adb9b5f..b8f7e3b 100644 --- a/src/contexts/internal.ts +++ b/src/contexts/internal.ts @@ -1,7 +1,10 @@ import { createContext, Ref, RefObject } from 'react'; -import type { TapGestureHandler } from 'react-native-gesture-handler'; +import type { + TapGestureHandler, + GestureHandlerProperties, +} from 'react-native-gesture-handler'; import type Animated from 'react-native-reanimated'; -import { ANIMATION_STATE } from '../constants'; +import type { ANIMATION_STATE } from '../constants'; import type { Scrollable, ScrollableRef } from '../types'; export type BottomSheetInternalContextType = { @@ -16,7 +19,7 @@ export type BottomSheetInternalContextType = { scrollableDecelerationRate: Animated.SharedValue; setScrollableRef: (ref: ScrollableRef) => void; removeScrollableRef: (ref: RefObject) => void; -}; +} & Pick; // @ts-ignore export const BottomSheetInternalContext = createContext();