├── .prettierignore ├── .gitattributes ├── .eslintignore ├── example ├── bare │ ├── src │ │ ├── screens │ │ │ ├── index.ts │ │ │ ├── DummyScreen.tsx │ │ │ ├── screens.ts │ │ │ └── integrations │ │ │ │ ├── NativeScreensExample.tsx │ │ │ │ └── ViewPagerExample.tsx │ │ ├── components │ │ │ ├── weather │ │ │ │ ├── index.ts │ │ │ │ └── Weather.tsx │ │ │ ├── locationItem │ │ │ │ ├── index.ts │ │ │ │ └── LocationItem.tsx │ │ │ ├── blurredBackground │ │ │ │ ├── index.ts │ │ │ │ └── BlurredBackground.tsx │ │ │ ├── locationDetailsHandle │ │ │ │ ├── index.ts │ │ │ │ └── LocationDetailsHandle.tsx │ │ │ └── locationDetails │ │ │ │ └── index.ts │ │ ├── App.tsx │ │ ├── utilities │ │ │ ├── index.ts │ │ │ ├── transformOrigin.ts │ │ │ └── createMockData.ts │ │ └── types.d.ts │ ├── app.json │ ├── android │ │ ├── .settings │ │ │ └── org.eclipse.buildship.core.prefs │ │ ├── app │ │ │ ├── debug.keystore │ │ │ ├── src │ │ │ │ ├── main │ │ │ │ │ ├── res │ │ │ │ │ │ ├── values │ │ │ │ │ │ │ ├── strings.xml │ │ │ │ │ │ │ ├── ic_launcher_background.xml │ │ │ │ │ │ │ └── styles.xml │ │ │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ │ ├── ic_launcher_round.png │ │ │ │ │ │ │ └── ic_launcher_foreground.png │ │ │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ │ ├── ic_launcher_round.png │ │ │ │ │ │ │ └── ic_launcher_foreground.png │ │ │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ │ ├── ic_launcher_round.png │ │ │ │ │ │ │ └── ic_launcher_foreground.png │ │ │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ │ ├── ic_launcher_round.png │ │ │ │ │ │ │ └── ic_launcher_foreground.png │ │ │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ │ ├── ic_launcher_round.png │ │ │ │ │ │ │ └── ic_launcher_foreground.png │ │ │ │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ │ │ │ ├── ic_launcher.xml │ │ │ │ │ │ │ └── ic_launcher_round.xml │ │ │ │ │ │ └── drawable │ │ │ │ │ │ │ └── rn_edit_text_material.xml │ │ │ │ │ ├── ic_launcher-playstore.png │ │ │ │ │ ├── jni │ │ │ │ │ │ ├── MainApplicationModuleProvider.h │ │ │ │ │ │ ├── OnLoad.cpp │ │ │ │ │ │ ├── MainApplicationModuleProvider.cpp │ │ │ │ │ │ ├── MainComponentsRegistry.h │ │ │ │ │ │ ├── MainApplicationTurboModuleManagerDelegate.h │ │ │ │ │ │ ├── MainApplicationTurboModuleManagerDelegate.cpp │ │ │ │ │ │ ├── Android.mk │ │ │ │ │ │ └── MainComponentsRegistry.cpp │ │ │ │ │ ├── AndroidManifest.xml │ │ │ │ │ └── java │ │ │ │ │ │ └── dev │ │ │ │ │ │ └── gorhom │ │ │ │ │ │ └── bottomsheet │ │ │ │ │ │ ├── components │ │ │ │ │ │ └── MainComponentsRegistry.java │ │ │ │ │ │ ├── MainActivity.java │ │ │ │ │ │ └── modules │ │ │ │ │ │ └── MainApplicationTurboModuleManagerDelegate.java │ │ │ │ └── debug │ │ │ │ │ └── AndroidManifest.xml │ │ │ └── proguard-rules.pro │ │ ├── gradle │ │ │ └── wrapper │ │ │ │ ├── gradle-wrapper.jar │ │ │ │ └── gradle-wrapper.properties │ │ ├── .project │ │ ├── settings.gradle │ │ ├── gradle.properties │ │ └── build.gradle │ ├── ios │ │ ├── BottomSheetExample │ │ │ ├── Images.xcassets │ │ │ │ ├── Contents.json │ │ │ │ └── AppIcon.appiconset │ │ │ │ │ ├── 20.png │ │ │ │ │ ├── 29.png │ │ │ │ │ ├── 40.png │ │ │ │ │ ├── 50.png │ │ │ │ │ ├── 57.png │ │ │ │ │ ├── 58.png │ │ │ │ │ ├── 60.png │ │ │ │ │ ├── 72.png │ │ │ │ │ ├── 76.png │ │ │ │ │ ├── 80.png │ │ │ │ │ ├── 87.png │ │ │ │ │ ├── 100.png │ │ │ │ │ ├── 1024.png │ │ │ │ │ ├── 114.png │ │ │ │ │ ├── 120.png │ │ │ │ │ ├── 144.png │ │ │ │ │ ├── 152.png │ │ │ │ │ ├── 167.png │ │ │ │ │ └── 180.png │ │ │ ├── AppDelegate.h │ │ │ ├── main.m │ │ │ └── Info.plist │ │ ├── BottomSheetExample.xcworkspace │ │ │ ├── xcshareddata │ │ │ │ └── IDEWorkspaceChecks.plist │ │ │ └── contents.xcworkspacedata │ │ ├── .xcode.env │ │ └── Podfile │ ├── babel.config.js │ ├── index.ts │ ├── patches │ │ ├── @react-native-community+blur+3.6.0.patch │ │ └── react-native-gesture-handler+1.10.3._patch │ ├── tsconfig.json │ ├── metro.config.js │ └── package.json ├── app │ ├── src │ │ ├── components │ │ │ ├── button │ │ │ │ ├── index.ts │ │ │ │ └── Button.tsx │ │ │ ├── contactItem │ │ │ │ ├── index.ts │ │ │ │ └── ContactItem.tsx │ │ │ ├── customFooter │ │ │ │ └── index.ts │ │ │ ├── customHandle │ │ │ │ └── index.ts │ │ │ ├── headerHandle │ │ │ │ ├── index.ts │ │ │ │ └── HeaderHandle.tsx │ │ │ ├── customBackground │ │ │ │ ├── index.ts │ │ │ │ └── CustomBackground.tsx │ │ │ ├── searchHandle │ │ │ │ ├── index.ts │ │ │ │ └── SearchHandle.tsx │ │ │ └── contactList │ │ │ │ ├── index.ts │ │ │ │ ├── styles.web.ts │ │ │ │ └── styles.ts │ │ ├── types.d.ts │ │ ├── utilities │ │ │ ├── transformOrigin.ts │ │ │ └── createMockData.ts │ │ ├── screens │ │ │ ├── modal │ │ │ │ └── withModalProvider.tsx │ │ │ └── advanced │ │ │ │ ├── customGestureHandling │ │ │ │ └── GestureTranslationContext.tsx │ │ │ │ ├── PullToRefreshExample.tsx │ │ │ │ ├── CustomHandleExample.tsx │ │ │ │ ├── CustomBackgroundExample.tsx │ │ │ │ ├── FooterExample.tsx │ │ │ │ └── ShadowExample.tsx │ │ ├── index.ts │ │ └── App.tsx │ ├── tsconfig.json │ └── package.json └── expo │ ├── assets │ ├── icon.png │ ├── favicon.png │ ├── splash.png │ └── adaptive-icon.png │ ├── src │ └── App.tsx │ ├── .gitignore │ ├── tsconfig.json │ ├── index.ts │ ├── app.json │ ├── babel.config.js │ ├── webpack.config.js │ ├── metro.config.js │ └── package.json ├── src ├── components │ ├── bottomSheetView │ │ ├── index.ts │ │ ├── styles.ts │ │ └── types.d.ts │ ├── bottomSheetDebugView │ │ ├── index.ts │ │ ├── styles.ts │ │ ├── BottomSheetDebugView.tsx │ │ └── ReText.tsx │ ├── bottomSheetRefreshControl │ │ ├── BottomSheetRefreshControl.tsx │ │ ├── index.ts │ │ └── BottomSheetRefreshControl.android.tsx │ ├── bottomSheetDraggableView │ │ ├── index.ts │ │ └── types.d.ts │ ├── bottomSheetModalProvider │ │ ├── index.ts │ │ └── types.d.ts │ ├── bottomSheetHandleContainer │ │ ├── index.ts │ │ └── types.d.ts │ ├── bottomSheetBackdropContainer │ │ ├── index.ts │ │ ├── styles.ts │ │ ├── types.d.ts │ │ └── BottomSheetBackdropContainer.tsx │ ├── bottomSheetBackgroundContainer │ │ ├── index.ts │ │ ├── styles.ts │ │ ├── types.d.ts │ │ └── BottomSheetBackgroundContainer.tsx │ ├── bottomSheetGestureHandlersProvider │ │ ├── index.ts │ │ ├── types.d.ts │ │ └── BottomSheetGestureHandlersProvider.tsx │ ├── bottomSheet │ │ ├── index.ts │ │ ├── styles.ts │ │ └── constants.ts │ ├── bottomSheetFooter │ │ ├── index.ts │ │ ├── styles.ts │ │ ├── types.d.ts │ │ └── BottomSheetFooter.tsx │ ├── bottomSheetHandle │ │ ├── index.ts │ │ ├── styles.ts │ │ ├── constants.ts │ │ ├── types.d.ts │ │ └── BottomSheetHandle.tsx │ ├── bottomSheetBackdrop │ │ ├── index.ts │ │ ├── styles.ts │ │ ├── constants.ts │ │ └── types.d.ts │ ├── bottomSheetBackground │ │ ├── index.ts │ │ ├── styles.ts │ │ ├── types.d.ts │ │ └── BottomSheetBackground.tsx │ ├── bottomSheetContainer │ │ ├── index.ts │ │ ├── styles.ts │ │ ├── styles.web.ts │ │ ├── types.d.ts │ │ └── BottomSheetContainer.tsx │ ├── bottomSheetTextInput │ │ ├── index.ts │ │ ├── types.ts │ │ └── BottomSheetTextInput.tsx │ ├── touchables │ │ ├── Touchables.ios.tsx │ │ ├── Touchables.tsx │ │ └── index.ts │ ├── bottomSheetModal │ │ ├── constants.ts │ │ ├── index.ts │ │ └── types.d.ts │ ├── bottomSheetScrollable │ │ ├── styles.ts │ │ ├── index.ts │ │ ├── BottomSheetFlatList.tsx │ │ ├── BottomSheetScrollView.tsx │ │ ├── BottomSheetSectionList.tsx │ │ └── BottomSheetVirtualizedList.tsx │ └── bottomSheetFooterContainer │ │ ├── types.d.ts │ │ └── BottomSheetFooterContainer.tsx ├── utilities │ ├── noop.ts │ ├── id.ts │ ├── clamp.ts │ ├── index.ts │ ├── easingExp.ts │ ├── snapPoint.ts │ ├── normalizeSnapPoint.ts │ ├── validateSnapPoint.ts │ ├── getKeyboardAnimationConfigs.ts │ ├── logger.ts │ ├── animate.ts │ └── getRefNativeTag.ts ├── contexts │ ├── external.ts │ ├── modal │ │ ├── external.ts │ │ └── internal.ts │ ├── index.ts │ └── gesture.ts └── hooks │ ├── useBottomSheetModal.ts │ ├── useBottomSheet.ts │ ├── useBottomSheetSpringConfigs.ts │ ├── useBottomSheetGestureHandlers.ts │ ├── useStableCallback.ts │ ├── useBottomSheetTimingConfigs.ts │ ├── useBottomSheetInternal.ts │ ├── useBottomSheetModalInternal.ts │ ├── index.ts │ ├── useReactiveSharedValue.ts │ ├── useScrollableSetter.ts │ ├── useScrollHandler.ts │ ├── useScrollable.ts │ ├── usePropsValidator.ts │ └── useNormalizedSnapPoints.ts ├── .github ├── FUNDING.yml ├── PULL_REQUEST_TEMPLATE.md ├── workflows │ ├── label-sponsors.yml │ ├── main.yml │ ├── stale.yml │ └── documentation.yml └── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── preview.gif ├── commitlint.config.js ├── mogorhom-dark.png ├── mogorhom-light.png ├── .huskyrc.json ├── lint-staged.config.js ├── babel.config.js ├── .prettierrc.json ├── .eslintrc.js ├── .editorconfig ├── .release-it.json ├── .auto-changelog ├── tsconfig.json ├── .gitignore ├── .all-contributorsrc ├── LICENSE ├── scripts └── auto-changelog.js └── templates └── release-template.hbs /.prettierignore: -------------------------------------------------------------------------------- 1 | .github 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.pbxproj -text 2 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | 3 | # generated by bob 4 | lib/ 5 | -------------------------------------------------------------------------------- /example/bare/src/screens/index.ts: -------------------------------------------------------------------------------- 1 | export { screens } from './screens'; 2 | -------------------------------------------------------------------------------- /example/app/src/components/button/index.ts: -------------------------------------------------------------------------------- 1 | export { Button } from './Button'; 2 | -------------------------------------------------------------------------------- /example/bare/src/components/weather/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './Weather'; 2 | -------------------------------------------------------------------------------- /src/components/bottomSheetView/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './BottomSheetView'; 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: gorhom 4 | -------------------------------------------------------------------------------- /preview.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/discord/react-native-bottom-sheet/HEAD/preview.gif -------------------------------------------------------------------------------- /example/app/src/components/contactItem/index.ts: -------------------------------------------------------------------------------- 1 | export { ContactItem } from './ContactItem'; 2 | -------------------------------------------------------------------------------- /src/components/bottomSheetDebugView/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './BottomSheetDebugView'; 2 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['@commitlint/config-conventional'], 3 | }; 4 | -------------------------------------------------------------------------------- /example/app/src/components/customFooter/index.ts: -------------------------------------------------------------------------------- 1 | export { CustomFooter } from './CustomFooter'; 2 | -------------------------------------------------------------------------------- /example/app/src/components/customHandle/index.ts: -------------------------------------------------------------------------------- 1 | export { CustomHandle } from './CustomHandle'; 2 | -------------------------------------------------------------------------------- /example/app/src/components/headerHandle/index.ts: -------------------------------------------------------------------------------- 1 | export { HeaderHandle } from './HeaderHandle'; 2 | -------------------------------------------------------------------------------- /example/bare/src/components/locationItem/index.ts: -------------------------------------------------------------------------------- 1 | export { LocationItem } from './LocationItem'; 2 | -------------------------------------------------------------------------------- /mogorhom-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/discord/react-native-bottom-sheet/HEAD/mogorhom-dark.png -------------------------------------------------------------------------------- /src/components/bottomSheetRefreshControl/BottomSheetRefreshControl.tsx: -------------------------------------------------------------------------------- 1 | export default () => null; 2 | -------------------------------------------------------------------------------- /example/bare/src/components/blurredBackground/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './BlurredBackground'; 2 | -------------------------------------------------------------------------------- /mogorhom-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/discord/react-native-bottom-sheet/HEAD/mogorhom-light.png -------------------------------------------------------------------------------- /src/components/bottomSheetDraggableView/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './BottomSheetDraggableView'; 2 | -------------------------------------------------------------------------------- /src/components/bottomSheetModalProvider/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './BottomSheetModalProvider'; 2 | -------------------------------------------------------------------------------- /example/app/src/components/customBackground/index.ts: -------------------------------------------------------------------------------- 1 | export { CustomBackground } from './CustomBackground'; 2 | -------------------------------------------------------------------------------- /example/bare/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "BottomSheetExample", 3 | "displayName": "BottomSheet Example" 4 | } 5 | -------------------------------------------------------------------------------- /example/bare/src/components/locationDetailsHandle/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './LocationDetailsHandle'; 2 | -------------------------------------------------------------------------------- /src/components/bottomSheetHandleContainer/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './BottomSheetHandleContainer'; 2 | -------------------------------------------------------------------------------- /src/components/bottomSheetBackdropContainer/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './BottomSheetBackdropContainer'; 2 | -------------------------------------------------------------------------------- /src/components/bottomSheetBackgroundContainer/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './BottomSheetBackgroundContainer'; 2 | -------------------------------------------------------------------------------- /example/app/src/components/searchHandle/index.ts: -------------------------------------------------------------------------------- 1 | export { SearchHandle, SEARCH_HANDLE_HEIGHT } from './SearchHandle'; 2 | -------------------------------------------------------------------------------- /example/bare/android/.settings/org.eclipse.buildship.core.prefs: -------------------------------------------------------------------------------- 1 | connection.project.dir= 2 | eclipse.preferences.version=1 3 | -------------------------------------------------------------------------------- /example/expo/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/discord/react-native-bottom-sheet/HEAD/example/expo/assets/icon.png -------------------------------------------------------------------------------- /src/components/bottomSheetGestureHandlersProvider/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './BottomSheetGestureHandlersProvider'; 2 | -------------------------------------------------------------------------------- /example/expo/assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/discord/react-native-bottom-sheet/HEAD/example/expo/assets/favicon.png -------------------------------------------------------------------------------- /example/expo/assets/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/discord/react-native-bottom-sheet/HEAD/example/expo/assets/splash.png -------------------------------------------------------------------------------- /src/components/bottomSheet/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './BottomSheet'; 2 | export type { BottomSheetProps } from './types'; 3 | -------------------------------------------------------------------------------- /example/bare/src/components/locationDetails/index.ts: -------------------------------------------------------------------------------- 1 | export { LocationDetails, LOCATION_DETAILS_HEIGHT } from './LocationDetails'; 2 | -------------------------------------------------------------------------------- /.huskyrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "hooks": { 3 | "commit-msg": "commitlint -E HUSKY_GIT_PARAMS", 4 | "pre-commit": "lint-staged" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /example/expo/assets/adaptive-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/discord/react-native-bottom-sheet/HEAD/example/expo/assets/adaptive-icon.png -------------------------------------------------------------------------------- /example/bare/android/app/debug.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/discord/react-native-bottom-sheet/HEAD/example/bare/android/app/debug.keystore -------------------------------------------------------------------------------- /src/utilities/noop.ts: -------------------------------------------------------------------------------- 1 | const workletNoop = () => { 2 | 'worklet'; 3 | }; 4 | 5 | const noop = () => {}; 6 | 7 | export { noop, workletNoop }; 8 | -------------------------------------------------------------------------------- /example/bare/android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | BottomSheet Example 3 | 4 | -------------------------------------------------------------------------------- /example/bare/ios/BottomSheetExample/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /lint-staged.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | '**/*.js': ['eslint'], 3 | '**/*.{ts,tsx}': [() => 'tsc --skipLibCheck --noEmit', 'eslint'], 4 | }; 5 | -------------------------------------------------------------------------------- /src/components/bottomSheetFooter/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './BottomSheetFooter'; 2 | export type { BottomSheetFooterProps } from './types'; 3 | -------------------------------------------------------------------------------- /src/components/bottomSheetHandle/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './BottomSheetHandle'; 2 | export type { BottomSheetHandleProps } from './types'; 3 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['module:metro-react-native-babel-preset'], 3 | plugins: ['react-native-reanimated/plugin'], 4 | }; 5 | -------------------------------------------------------------------------------- /example/app/src/components/contactList/index.ts: -------------------------------------------------------------------------------- 1 | export { ContactList } from './ContactList'; 2 | export type { ContactListProps } from './ContactList'; 3 | -------------------------------------------------------------------------------- /example/expo/src/App.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { App } from '@gorhom/bottom-sheet-example-app'; 3 | 4 | export default () => ; 5 | -------------------------------------------------------------------------------- /src/components/bottomSheetBackdrop/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './BottomSheetBackdrop'; 2 | export type { BottomSheetBackdropProps } from './types'; 3 | -------------------------------------------------------------------------------- /src/components/bottomSheetBackground/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './BottomSheetBackground'; 2 | export type { BottomSheetBackgroundProps } from './types'; 3 | -------------------------------------------------------------------------------- /src/components/bottomSheetContainer/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './BottomSheetContainer'; 2 | export type { BottomSheetContainerProps } from './types'; 3 | -------------------------------------------------------------------------------- /src/components/bottomSheetTextInput/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './BottomSheetTextInput'; 2 | export type { BottomSheetTextInputProps } from './types'; 3 | -------------------------------------------------------------------------------- /src/utilities/id.ts: -------------------------------------------------------------------------------- 1 | let current = 0; 2 | 3 | export const id = () => { 4 | current = (current + 1) % Number.MAX_SAFE_INTEGER; 5 | return current; 6 | }; 7 | -------------------------------------------------------------------------------- /example/bare/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['module:metro-react-native-babel-preset'], 3 | plugins: ['react-native-reanimated/plugin'], 4 | }; 5 | -------------------------------------------------------------------------------- /src/components/touchables/Touchables.ios.tsx: -------------------------------------------------------------------------------- 1 | export { 2 | TouchableOpacity, 3 | TouchableHighlight, 4 | TouchableWithoutFeedback, 5 | } from 'react-native'; 6 | -------------------------------------------------------------------------------- /example/bare/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/discord/react-native-bottom-sheet/HEAD/example/bare/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /src/components/bottomSheetTextInput/types.ts: -------------------------------------------------------------------------------- 1 | import type { TextInputProps } from 'react-native'; 2 | 3 | export interface BottomSheetTextInputProps extends TextInputProps {} 4 | -------------------------------------------------------------------------------- /src/components/touchables/Touchables.tsx: -------------------------------------------------------------------------------- 1 | export { 2 | TouchableOpacity, 3 | TouchableHighlight, 4 | TouchableWithoutFeedback, 5 | } from 'react-native-gesture-handler'; 6 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 80, 3 | "arrowParens": "avoid", 4 | "singleQuote": true, 5 | "tabWidth": 2, 6 | "trailingComma": "es5", 7 | "useTabs": false 8 | } 9 | -------------------------------------------------------------------------------- /example/bare/android/app/src/main/ic_launcher-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/discord/react-native-bottom-sheet/HEAD/example/bare/android/app/src/main/ic_launcher-playstore.png -------------------------------------------------------------------------------- /src/components/bottomSheetView/styles.ts: -------------------------------------------------------------------------------- 1 | import { StyleSheet } from 'react-native'; 2 | 3 | export const styles = StyleSheet.create({ 4 | container: { 5 | // flex: 1, 6 | }, 7 | }); 8 | -------------------------------------------------------------------------------- /example/bare/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/discord/react-native-bottom-sheet/HEAD/example/bare/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/bare/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/discord/react-native-bottom-sheet/HEAD/example/bare/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/bare/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/discord/react-native-bottom-sheet/HEAD/example/bare/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/bare/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/discord/react-native-bottom-sheet/HEAD/example/bare/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/bare/android/app/src/main/res/values/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #000000 4 | -------------------------------------------------------------------------------- /example/bare/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/discord/react-native-bottom-sheet/HEAD/example/bare/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/bare/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/discord/react-native-bottom-sheet/HEAD/example/bare/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/bare/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/discord/react-native-bottom-sheet/HEAD/example/bare/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/bare/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/discord/react-native-bottom-sheet/HEAD/example/bare/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/bare/src/App.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { screens } from './screens'; 3 | import { App } from '@gorhom/bottom-sheet-example-app'; 4 | 5 | export default () => ; 6 | -------------------------------------------------------------------------------- /example/expo/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .expo/ 3 | dist/ 4 | npm-debug.* 5 | *.jks 6 | *.p8 7 | *.p12 8 | *.key 9 | *.mobileprovision 10 | *.orig.* 11 | web-build/ 12 | 13 | # macOS 14 | .DS_Store 15 | -------------------------------------------------------------------------------- /src/components/bottomSheetBackdrop/styles.ts: -------------------------------------------------------------------------------- 1 | import { StyleSheet } from 'react-native'; 2 | 3 | export const styles = StyleSheet.create({ 4 | container: { 5 | backgroundColor: 'black', 6 | }, 7 | }); 8 | -------------------------------------------------------------------------------- /example/bare/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/discord/react-native-bottom-sheet/HEAD/example/bare/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/bare/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/discord/react-native-bottom-sheet/HEAD/example/bare/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /src/components/bottomSheetModal/constants.ts: -------------------------------------------------------------------------------- 1 | const DEFAULT_STACK_BEHAVIOR = 'replace'; 2 | const DEFAULT_ENABLE_DISMISS_ON_CLOSE = true; 3 | 4 | export { DEFAULT_STACK_BEHAVIOR, DEFAULT_ENABLE_DISMISS_ON_CLOSE }; 5 | -------------------------------------------------------------------------------- /example/bare/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/discord/react-native-bottom-sheet/HEAD/example/bare/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /example/bare/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/discord/react-native-bottom-sheet/HEAD/example/bare/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /example/bare/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/discord/react-native-bottom-sheet/HEAD/example/bare/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /example/bare/ios/BottomSheetExample/Images.xcassets/AppIcon.appiconset/20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/discord/react-native-bottom-sheet/HEAD/example/bare/ios/BottomSheetExample/Images.xcassets/AppIcon.appiconset/20.png -------------------------------------------------------------------------------- /example/bare/ios/BottomSheetExample/Images.xcassets/AppIcon.appiconset/29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/discord/react-native-bottom-sheet/HEAD/example/bare/ios/BottomSheetExample/Images.xcassets/AppIcon.appiconset/29.png -------------------------------------------------------------------------------- /example/bare/ios/BottomSheetExample/Images.xcassets/AppIcon.appiconset/40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/discord/react-native-bottom-sheet/HEAD/example/bare/ios/BottomSheetExample/Images.xcassets/AppIcon.appiconset/40.png -------------------------------------------------------------------------------- /example/bare/ios/BottomSheetExample/Images.xcassets/AppIcon.appiconset/50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/discord/react-native-bottom-sheet/HEAD/example/bare/ios/BottomSheetExample/Images.xcassets/AppIcon.appiconset/50.png -------------------------------------------------------------------------------- /example/bare/ios/BottomSheetExample/Images.xcassets/AppIcon.appiconset/57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/discord/react-native-bottom-sheet/HEAD/example/bare/ios/BottomSheetExample/Images.xcassets/AppIcon.appiconset/57.png -------------------------------------------------------------------------------- /example/bare/ios/BottomSheetExample/Images.xcassets/AppIcon.appiconset/58.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/discord/react-native-bottom-sheet/HEAD/example/bare/ios/BottomSheetExample/Images.xcassets/AppIcon.appiconset/58.png -------------------------------------------------------------------------------- /example/bare/ios/BottomSheetExample/Images.xcassets/AppIcon.appiconset/60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/discord/react-native-bottom-sheet/HEAD/example/bare/ios/BottomSheetExample/Images.xcassets/AppIcon.appiconset/60.png -------------------------------------------------------------------------------- /example/bare/ios/BottomSheetExample/Images.xcassets/AppIcon.appiconset/72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/discord/react-native-bottom-sheet/HEAD/example/bare/ios/BottomSheetExample/Images.xcassets/AppIcon.appiconset/72.png -------------------------------------------------------------------------------- /example/bare/ios/BottomSheetExample/Images.xcassets/AppIcon.appiconset/76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/discord/react-native-bottom-sheet/HEAD/example/bare/ios/BottomSheetExample/Images.xcassets/AppIcon.appiconset/76.png -------------------------------------------------------------------------------- /example/bare/ios/BottomSheetExample/Images.xcassets/AppIcon.appiconset/80.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/discord/react-native-bottom-sheet/HEAD/example/bare/ios/BottomSheetExample/Images.xcassets/AppIcon.appiconset/80.png -------------------------------------------------------------------------------- /example/bare/ios/BottomSheetExample/Images.xcassets/AppIcon.appiconset/87.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/discord/react-native-bottom-sheet/HEAD/example/bare/ios/BottomSheetExample/Images.xcassets/AppIcon.appiconset/87.png -------------------------------------------------------------------------------- /src/components/bottomSheetContainer/styles.ts: -------------------------------------------------------------------------------- 1 | import { StyleSheet } from 'react-native'; 2 | 3 | export const styles = StyleSheet.create({ 4 | container: { 5 | ...StyleSheet.absoluteFillObject, 6 | }, 7 | }); 8 | -------------------------------------------------------------------------------- /src/utilities/clamp.ts: -------------------------------------------------------------------------------- 1 | export const clamp = ( 2 | value: number, 3 | lowerBound: number, 4 | upperBound: number 5 | ) => { 6 | 'worklet'; 7 | return Math.min(Math.max(lowerBound, value), upperBound); 8 | }; 9 | -------------------------------------------------------------------------------- /example/bare/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/discord/react-native-bottom-sheet/HEAD/example/bare/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /example/bare/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/discord/react-native-bottom-sheet/HEAD/example/bare/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /example/bare/ios/BottomSheetExample/Images.xcassets/AppIcon.appiconset/100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/discord/react-native-bottom-sheet/HEAD/example/bare/ios/BottomSheetExample/Images.xcassets/AppIcon.appiconset/100.png -------------------------------------------------------------------------------- /example/bare/ios/BottomSheetExample/Images.xcassets/AppIcon.appiconset/1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/discord/react-native-bottom-sheet/HEAD/example/bare/ios/BottomSheetExample/Images.xcassets/AppIcon.appiconset/1024.png -------------------------------------------------------------------------------- /example/bare/ios/BottomSheetExample/Images.xcassets/AppIcon.appiconset/114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/discord/react-native-bottom-sheet/HEAD/example/bare/ios/BottomSheetExample/Images.xcassets/AppIcon.appiconset/114.png -------------------------------------------------------------------------------- /example/bare/ios/BottomSheetExample/Images.xcassets/AppIcon.appiconset/120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/discord/react-native-bottom-sheet/HEAD/example/bare/ios/BottomSheetExample/Images.xcassets/AppIcon.appiconset/120.png -------------------------------------------------------------------------------- /example/bare/ios/BottomSheetExample/Images.xcassets/AppIcon.appiconset/144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/discord/react-native-bottom-sheet/HEAD/example/bare/ios/BottomSheetExample/Images.xcassets/AppIcon.appiconset/144.png -------------------------------------------------------------------------------- /example/bare/ios/BottomSheetExample/Images.xcassets/AppIcon.appiconset/152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/discord/react-native-bottom-sheet/HEAD/example/bare/ios/BottomSheetExample/Images.xcassets/AppIcon.appiconset/152.png -------------------------------------------------------------------------------- /example/bare/ios/BottomSheetExample/Images.xcassets/AppIcon.appiconset/167.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/discord/react-native-bottom-sheet/HEAD/example/bare/ios/BottomSheetExample/Images.xcassets/AppIcon.appiconset/167.png -------------------------------------------------------------------------------- /example/bare/ios/BottomSheetExample/Images.xcassets/AppIcon.appiconset/180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/discord/react-native-bottom-sheet/HEAD/example/bare/ios/BottomSheetExample/Images.xcassets/AppIcon.appiconset/180.png -------------------------------------------------------------------------------- /src/components/bottomSheetScrollable/styles.ts: -------------------------------------------------------------------------------- 1 | import { StyleSheet } from 'react-native'; 2 | 3 | export const styles = StyleSheet.create({ 4 | container: { 5 | flex: 1, 6 | overflow: 'visible', 7 | }, 8 | }); 9 | -------------------------------------------------------------------------------- /src/components/bottomSheetModal/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './BottomSheetModal'; 2 | export type { 3 | BottomSheetModalProps, 4 | BottomSheetModalPrivateMethods, 5 | BottomSheetModalStackBehavior, 6 | } from './types'; 7 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: ['@react-native-community', 'prettier'], 4 | rules: { 5 | 'no-console': ['error', { allow: ['warn', 'error'] }], 6 | 'prettier/prettier': 'error', 7 | }, 8 | }; 9 | -------------------------------------------------------------------------------- /example/bare/src/utilities/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | createContactListMockData, 3 | createContactSectionsMockData, 4 | createLocationListMockData, 5 | } from './createMockData'; 6 | export { transformOrigin } from './transformOrigin'; 7 | -------------------------------------------------------------------------------- /src/components/bottomSheetBackground/styles.ts: -------------------------------------------------------------------------------- 1 | import { StyleSheet } from 'react-native'; 2 | 3 | export const styles = StyleSheet.create({ 4 | container: { 5 | backgroundColor: 'white', 6 | borderRadius: 15, 7 | }, 8 | }); 9 | -------------------------------------------------------------------------------- /src/components/bottomSheetFooterContainer/types.d.ts: -------------------------------------------------------------------------------- 1 | import type { BottomSheetProps } from '../bottomSheet'; 2 | 3 | export interface BottomSheetFooterContainerProps 4 | extends Required> {} 5 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Please provide enough information so that others can review your pull request: 2 | 3 | ## Motivation 4 | 5 | Explain the **motivation** for making this change. What existing problem does the pull request solve? 6 | 7 | -------------------------------------------------------------------------------- /example/app/src/types.d.ts: -------------------------------------------------------------------------------- 1 | export type Contact = { 2 | name: string; 3 | jobTitle: string; 4 | address: string; 5 | }; 6 | 7 | export type Location = { 8 | id: string; 9 | name: string; 10 | address: string; 11 | photos: string[]; 12 | }; 13 | -------------------------------------------------------------------------------- /example/bare/src/types.d.ts: -------------------------------------------------------------------------------- 1 | export type Contact = { 2 | name: string; 3 | jobTitle: string; 4 | address: string; 5 | }; 6 | 7 | export type Location = { 8 | id: string; 9 | name: string; 10 | address: string; 11 | photos: string[]; 12 | }; 13 | -------------------------------------------------------------------------------- /example/bare/ios/BottomSheetExample/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface AppDelegate : UIResponder 5 | 6 | @property (nonatomic, strong) UIWindow *window; 7 | 8 | @end 9 | -------------------------------------------------------------------------------- /example/bare/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /example/bare/ios/BottomSheetExample/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "AppDelegate.h" 4 | 5 | int main(int argc, char * argv[]) { 6 | @autoreleasepool { 7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/components/bottomSheetFooter/styles.ts: -------------------------------------------------------------------------------- 1 | import { StyleSheet } from 'react-native'; 2 | 3 | export const styles = StyleSheet.create({ 4 | container: { 5 | position: 'absolute', 6 | top: 0, 7 | left: 0, 8 | right: 0, 9 | zIndex: 9999, 10 | }, 11 | }); 12 | -------------------------------------------------------------------------------- /src/utilities/index.ts: -------------------------------------------------------------------------------- 1 | export { normalizeSnapPoint } from './normalizeSnapPoint'; 2 | export { animate } from './animate'; 3 | export { getKeyboardAnimationConfigs } from './getKeyboardAnimationConfigs'; 4 | export { print } from './logger'; 5 | export { noop, workletNoop } from './noop'; 6 | -------------------------------------------------------------------------------- /example/expo/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "expo/tsconfig.base", 3 | "compilerOptions": { 4 | "paths": { 5 | "@gorhom/bottom-sheet": ["../../src/index"], 6 | "@gorhom/bottom-sheet-example-app": ["../app/src/index"] 7 | }, 8 | "strict": true 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/components/bottomSheetBackdropContainer/styles.ts: -------------------------------------------------------------------------------- 1 | import { StyleSheet } from 'react-native'; 2 | 3 | export const styles = StyleSheet.create({ 4 | container: { 5 | position: 'absolute', 6 | top: 0, 7 | left: 0, 8 | right: 0, 9 | bottom: 0, 10 | }, 11 | }); 12 | -------------------------------------------------------------------------------- /src/components/bottomSheetBackground/types.d.ts: -------------------------------------------------------------------------------- 1 | import type { ViewProps } from 'react-native'; 2 | import type { BottomSheetVariables } from '../../types'; 3 | 4 | export interface BottomSheetBackgroundProps 5 | extends Pick, 6 | BottomSheetVariables {} 7 | -------------------------------------------------------------------------------- /src/components/bottomSheetBackgroundContainer/styles.ts: -------------------------------------------------------------------------------- 1 | import { StyleSheet } from 'react-native'; 2 | 3 | export const styles = StyleSheet.create({ 4 | container: { 5 | position: 'absolute', 6 | top: 0, 7 | left: 0, 8 | right: 0, 9 | bottom: 0, 10 | }, 11 | }); 12 | -------------------------------------------------------------------------------- /src/components/bottomSheetContainer/styles.web.ts: -------------------------------------------------------------------------------- 1 | import { StyleSheet } from 'react-native'; 2 | 3 | export const styles = StyleSheet.create({ 4 | container: { 5 | // @ts-ignore 6 | position: 'fixed', 7 | left: 0, 8 | right: 0, 9 | bottom: 0, 10 | top: 0, 11 | }, 12 | }); 13 | -------------------------------------------------------------------------------- /example/app/src/utilities/transformOrigin.ts: -------------------------------------------------------------------------------- 1 | // @ts-ignore 2 | export const transformOrigin = ({ x, y }, ...transformations) => { 3 | 'worklet'; 4 | return [ 5 | { translateX: x }, 6 | { translateY: y }, 7 | ...transformations, 8 | { translateX: x * -1 }, 9 | { translateY: y * -1 }, 10 | ]; 11 | }; 12 | -------------------------------------------------------------------------------- /example/bare/src/utilities/transformOrigin.ts: -------------------------------------------------------------------------------- 1 | // @ts-ignore 2 | export const transformOrigin = ({ x, y }, ...transformations) => { 3 | 'worklet'; 4 | return [ 5 | { translateX: x }, 6 | { translateY: y }, 7 | ...transformations, 8 | { translateX: x * -1 }, 9 | { translateY: y * -1 }, 10 | ]; 11 | }; 12 | -------------------------------------------------------------------------------- /example/app/src/screens/modal/withModalProvider.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react'; 2 | import { BottomSheetModalProvider } from '@gorhom/bottom-sheet'; 3 | 4 | export const withModalProvider = (Component: FC) => () => 5 | ( 6 | 7 | 8 | 9 | ); 10 | -------------------------------------------------------------------------------- /src/components/bottomSheetBackdropContainer/types.d.ts: -------------------------------------------------------------------------------- 1 | import type { BottomSheetProps } from '../bottomSheet'; 2 | import type { BottomSheetBackdropProps } from '../bottomSheetBackdrop'; 3 | 4 | export interface BottomSheetBackdropContainerProps 5 | extends Pick, 6 | BottomSheetBackdropProps {} 7 | -------------------------------------------------------------------------------- /src/contexts/external.ts: -------------------------------------------------------------------------------- 1 | import { createContext } from 'react'; 2 | import type { BottomSheetMethods, BottomSheetVariables } from '../types'; 3 | 4 | export const BottomSheetContext = createContext< 5 | (BottomSheetMethods & BottomSheetVariables) | null 6 | >(null); 7 | 8 | export const BottomSheetProvider = BottomSheetContext.Provider; 9 | -------------------------------------------------------------------------------- /example/bare/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /example/expo/index.ts: -------------------------------------------------------------------------------- 1 | import { registerRootComponent } from 'expo'; 2 | 3 | import { enableScreens } from 'react-native-screens'; 4 | enableScreens(true); 5 | 6 | // @ts-ignore 7 | import { enableLogging } from '@gorhom/bottom-sheet'; 8 | enableLogging(); 9 | 10 | import App from './src/App'; 11 | 12 | registerRootComponent(App); 13 | -------------------------------------------------------------------------------- /example/bare/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /example/bare/ios/BottomSheetExample.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/components/bottomSheetGestureHandlersProvider/types.d.ts: -------------------------------------------------------------------------------- 1 | import type { ReactChild } from 'react'; 2 | import type { BottomSheetProps } from '../bottomSheet/types'; 3 | 4 | export interface BottomSheetGestureHandlersProviderProps 5 | extends Pick { 6 | children: ReactChild | ReactChild[]; 7 | } 8 | -------------------------------------------------------------------------------- /example/bare/ios/BottomSheetExample.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | 9 | indent_style = space 10 | indent_size = 2 11 | 12 | end_of_line = lf 13 | charset = utf-8 14 | trim_trailing_whitespace = true 15 | insert_final_newline = true 16 | -------------------------------------------------------------------------------- /src/components/bottomSheetBackgroundContainer/types.d.ts: -------------------------------------------------------------------------------- 1 | import type { BottomSheetProps } from '../bottomSheet'; 2 | import type { BottomSheetBackgroundProps } from '../bottomSheetBackground'; 3 | 4 | export interface BottomSheetBackgroundContainerProps 5 | extends Pick, 6 | BottomSheetBackgroundProps {} 7 | -------------------------------------------------------------------------------- /example/bare/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/hooks/useBottomSheetModal.ts: -------------------------------------------------------------------------------- 1 | import { useContext } from 'react'; 2 | import { BottomSheetModalContext } from '../contexts'; 3 | 4 | export const useBottomSheetModal = () => { 5 | const context = useContext(BottomSheetModalContext); 6 | 7 | if (context === null) { 8 | throw "'BottomSheetModalContext' cannot be null!"; 9 | } 10 | 11 | return context; 12 | }; 13 | -------------------------------------------------------------------------------- /src/hooks/useBottomSheet.ts: -------------------------------------------------------------------------------- 1 | import { useContext } from 'react'; 2 | import { BottomSheetContext } from '../contexts/external'; 3 | 4 | export const useBottomSheet = () => { 5 | const context = useContext(BottomSheetContext); 6 | 7 | if (context === null) { 8 | throw "'useBottomSheet' cannot be used out of the BottomSheet!"; 9 | } 10 | 11 | return context; 12 | }; 13 | -------------------------------------------------------------------------------- /src/components/bottomSheet/styles.ts: -------------------------------------------------------------------------------- 1 | import { StyleSheet } from 'react-native'; 2 | 3 | export const styles = StyleSheet.create({ 4 | container: { 5 | flexDirection: 'column-reverse', 6 | position: 'absolute', 7 | top: 0, 8 | left: 0, 9 | right: 0, 10 | }, 11 | contentContainer: {}, 12 | contentMaskContainer: { 13 | overflow: 'hidden', 14 | }, 15 | }); 16 | -------------------------------------------------------------------------------- /src/utilities/easingExp.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * A modified version of the default AnimatedEasing.exp, 3 | * to insure its value never goes below `0`. 4 | * @see https://github.com/software-mansion/react-native-reanimated/issues/1610 5 | * @param t number 6 | */ 7 | export const exp = (t: number) => { 8 | 'worklet'; 9 | return Math.min(Math.max(0, Math.pow(2, 10 * (t - 1))), 1); 10 | }; 11 | -------------------------------------------------------------------------------- /src/contexts/modal/external.ts: -------------------------------------------------------------------------------- 1 | import { createContext } from 'react'; 2 | 3 | export interface BottomSheetModalContextType { 4 | dismiss: (key?: string) => boolean; 5 | dismissAll: () => void; 6 | } 7 | 8 | export const BottomSheetModalContext = 9 | createContext(null); 10 | 11 | export const BottomSheetModalProvider = BottomSheetModalContext.Provider; 12 | -------------------------------------------------------------------------------- /src/utilities/snapPoint.ts: -------------------------------------------------------------------------------- 1 | export const snapPoint = ( 2 | value: number, 3 | velocity: number, 4 | points: ReadonlyArray 5 | ): number => { 6 | 'worklet'; 7 | const point = value + 0.2 * velocity; 8 | const deltas = points.map(p => Math.abs(point - p)); 9 | const minDelta = Math.min.apply(null, deltas); 10 | return points.filter(p => Math.abs(point - p) === minDelta)[0]; 11 | }; 12 | -------------------------------------------------------------------------------- /.release-it.json: -------------------------------------------------------------------------------- 1 | { 2 | "git": { 3 | "push": true, 4 | "tagName": "v${version}", 5 | "commitMessage": "chore: release v${version}" 6 | }, 7 | "github": { 8 | "release": true 9 | }, 10 | "npm": { 11 | "publish": false 12 | }, 13 | "plugins": { 14 | "@release-it/conventional-changelog": { 15 | "preset": "angular", 16 | "infile": "CHANGELOG.md" 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/hooks/useBottomSheetSpringConfigs.ts: -------------------------------------------------------------------------------- 1 | import { useMemo } from 'react'; 2 | import type { WithSpringConfig } from 'react-native-reanimated'; 3 | 4 | /** 5 | * Generate spring animation configs. 6 | * @param configs overridable configs. 7 | */ 8 | export const useBottomSheetSpringConfigs = ( 9 | configs: Omit 10 | ) => { 11 | return useMemo(() => configs, [configs]); 12 | }; 13 | -------------------------------------------------------------------------------- /.github/workflows/label-sponsors.yml: -------------------------------------------------------------------------------- 1 | name: Label sponsors 2 | on: 3 | pull_request: 4 | types: [opened] 5 | issues: 6 | types: [opened] 7 | jobs: 8 | build: 9 | name: is-sponsor-label 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: JasonEtco/is-sponsor-label-action@v1.2.0 13 | with: 14 | label: sponsor 15 | env: 16 | GITHUB_TOKEN: ${{ secrets.ACTIONS_TOKEN }} 17 | -------------------------------------------------------------------------------- /src/components/bottomSheetModalProvider/types.d.ts: -------------------------------------------------------------------------------- 1 | import type { ReactNode } from 'react'; 2 | import type { BottomSheetModalPrivateMethods } from '../bottomSheetModal'; 3 | 4 | export interface BottomSheetModalRef { 5 | key: string; 6 | ref: { 7 | current: BottomSheetModalPrivateMethods; 8 | }; 9 | willUnmount: boolean; 10 | } 11 | 12 | export interface BottomSheetModalProviderProps { 13 | children?: ReactNode; 14 | } 15 | -------------------------------------------------------------------------------- /src/components/bottomSheetHandle/styles.ts: -------------------------------------------------------------------------------- 1 | import { StyleSheet } from 'react-native'; 2 | import { WINDOW_WIDTH } from '../../constants'; 3 | 4 | export const styles = StyleSheet.create({ 5 | container: { 6 | padding: 10, 7 | }, 8 | 9 | indicator: { 10 | alignSelf: 'center', 11 | width: (7.5 * WINDOW_WIDTH) / 100, 12 | height: 4, 13 | borderRadius: 4, 14 | backgroundColor: 'rgba(0, 0, 0, 0.75)', 15 | }, 16 | }); 17 | -------------------------------------------------------------------------------- /example/bare/android/app/src/main/jni/MainApplicationModuleProvider.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | namespace facebook { 9 | namespace react { 10 | 11 | std::shared_ptr MainApplicationModuleProvider( 12 | const std::string moduleName, 13 | const JavaTurboModule::InitParams ¶ms); 14 | 15 | } // namespace react 16 | } // namespace facebook -------------------------------------------------------------------------------- /src/components/bottomSheetHandle/constants.ts: -------------------------------------------------------------------------------- 1 | const DEFAULT_ACCESSIBLE = true; 2 | const DEFAULT_ACCESSIBILITY_ROLE = 'adjustable'; 3 | const DEFAULT_ACCESSIBILITY_LABEL = 'Bottom sheet handle'; 4 | const DEFAULT_ACCESSIBILITY_HINT = 5 | 'Drag up or down to extend or minimize the bottom sheet'; 6 | 7 | export { 8 | DEFAULT_ACCESSIBLE, 9 | DEFAULT_ACCESSIBILITY_ROLE, 10 | DEFAULT_ACCESSIBILITY_LABEL, 11 | DEFAULT_ACCESSIBILITY_HINT, 12 | }; 13 | -------------------------------------------------------------------------------- /src/hooks/useBottomSheetGestureHandlers.ts: -------------------------------------------------------------------------------- 1 | import { useContext } from 'react'; 2 | import { BottomSheetGestureHandlersContext } from '../contexts/gesture'; 3 | 4 | export const useBottomSheetGestureHandlers = () => { 5 | const context = useContext(BottomSheetGestureHandlersContext); 6 | 7 | if (context === null) { 8 | throw "'useBottomSheetGestureHandlers' cannot be used out of the BottomSheet!"; 9 | } 10 | 11 | return context; 12 | }; 13 | -------------------------------------------------------------------------------- /example/bare/android/app/src/main/jni/OnLoad.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "MainApplicationTurboModuleManagerDelegate.h" 3 | #include "MainComponentsRegistry.h" 4 | 5 | JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) { 6 | return facebook::jni::initialize(vm, [] { 7 | facebook::react::MainApplicationTurboModuleManagerDelegate:: 8 | registerNatives(); 9 | facebook::react::MainComponentsRegistry::registerNatives(); 10 | }); 11 | } -------------------------------------------------------------------------------- /src/components/bottomSheetDebugView/styles.ts: -------------------------------------------------------------------------------- 1 | import { StyleSheet } from 'react-native'; 2 | 3 | export const styles = StyleSheet.create({ 4 | container: { 5 | position: 'absolute', 6 | right: 4, 7 | top: 0, 8 | padding: 2, 9 | backgroundColor: 'rgba(0, 0,0,0.5)', 10 | }, 11 | text: { 12 | fontSize: 14, 13 | lineHeight: 16, 14 | textAlignVertical: 'center', 15 | height: 20, 16 | padding: 0, 17 | color: 'white', 18 | }, 19 | }); 20 | -------------------------------------------------------------------------------- /example/app/src/screens/advanced/customGestureHandling/GestureTranslationContext.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext } from 'react'; 2 | import Animated from 'react-native-reanimated'; 3 | 4 | const gestureTranslationContext = React.createContext< 5 | Animated.SharedValue 6 | >({ value: 0 }); 7 | 8 | export const GestureTranslationProvider = gestureTranslationContext.Provider; 9 | 10 | export const useGestureTranslationY = () => { 11 | return useContext(gestureTranslationContext); 12 | }; 13 | -------------------------------------------------------------------------------- /example/app/src/index.ts: -------------------------------------------------------------------------------- 1 | export { App } from './App'; 2 | export { default as ModalBackdropExample } from './screens/modal/BackdropExample'; 3 | export { withModalProvider } from './screens/modal/withModalProvider'; 4 | export { Button } from './components/button'; 5 | export { ContactList } from './components/contactList'; 6 | export { ContactItem } from './components/contactItem'; 7 | export { SearchHandle, SEARCH_HANDLE_HEIGHT } from './components/searchHandle'; 8 | export * from './utilities/createMockData'; 9 | -------------------------------------------------------------------------------- /example/bare/index.ts: -------------------------------------------------------------------------------- 1 | import 'react-native-gesture-handler'; 2 | 3 | import { enableScreens } from 'react-native-screens'; 4 | enableScreens(true); 5 | 6 | // @ts-ignore 7 | import { enableLogging } from '@gorhom/bottom-sheet'; 8 | enableLogging(); 9 | 10 | import { AppRegistry, LogBox } from 'react-native'; 11 | import App from './src/App'; 12 | import { name as appName } from './app.json'; 13 | 14 | LogBox.ignoreLogs(['react-native-maps']); 15 | 16 | AppRegistry.registerComponent(appName, () => App); 17 | -------------------------------------------------------------------------------- /example/app/src/components/contactList/styles.web.ts: -------------------------------------------------------------------------------- 1 | import { StyleSheet } from 'react-native'; 2 | 3 | export const styles = StyleSheet.create({ 4 | sectionHeaderContainer: { 5 | paddingTop: 24, 6 | paddingBottom: 6, 7 | backgroundColor: 'white', 8 | }, 9 | sectionHeaderTitle: { 10 | fontSize: 16, 11 | textTransform: 'uppercase', 12 | }, 13 | container: { 14 | flex: 1, 15 | paddingHorizontal: 16, 16 | }, 17 | contentContainer: { 18 | paddingHorizontal: 16, 19 | }, 20 | }); 21 | -------------------------------------------------------------------------------- /example/bare/android/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | android 4 | Project android created by Buildship. 5 | 6 | 7 | 8 | 9 | org.eclipse.buildship.core.gradleprojectbuilder 10 | 11 | 12 | 13 | 14 | 15 | org.eclipse.buildship.core.gradleprojectnature 16 | 17 | 18 | -------------------------------------------------------------------------------- /example/bare/ios/.xcode.env: -------------------------------------------------------------------------------- 1 | 2 | # This `.xcode.env` file is versioned and is used to source the environment 3 | # used when running script phases inside Xcode. 4 | # To customize your local environment, you can create an `.xcode.env.local` 5 | # file that is not versioned. 6 | # NODE_BINARY variable contains the PATH to the node executable. 7 | # 8 | # Customize the NODE_BINARY variable here. 9 | # For example, to use nvm with brew, add the following line 10 | # . "$(brew --prefix nvm)/nvm.sh" --no-use 11 | export NODE_BINARY=$(command -v node) -------------------------------------------------------------------------------- /src/utilities/normalizeSnapPoint.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Converts a snap point to fixed numbers. 3 | */ 4 | export const normalizeSnapPoint = ( 5 | snapPoint: number | string, 6 | containerHeight: number 7 | ) => { 8 | 'worklet'; 9 | let normalizedSnapPoint = snapPoint; 10 | 11 | // percentage snap point 12 | if (typeof normalizedSnapPoint === 'string') { 13 | normalizedSnapPoint = 14 | (Number(normalizedSnapPoint.split('%')[0]) * containerHeight) / 100; 15 | } 16 | return Math.max(0, containerHeight - normalizedSnapPoint); 17 | }; 18 | -------------------------------------------------------------------------------- /example/app/src/components/contactList/styles.ts: -------------------------------------------------------------------------------- 1 | import { StyleSheet } from 'react-native'; 2 | 3 | export const styles = StyleSheet.create({ 4 | sectionHeaderContainer: { 5 | paddingTop: 24, 6 | paddingBottom: 6, 7 | backgroundColor: 'white', 8 | }, 9 | sectionHeaderTitle: { 10 | fontSize: 16, 11 | textTransform: 'uppercase', 12 | color: 'black', 13 | }, 14 | container: { 15 | overflow: 'visible', 16 | flex: 1, 17 | }, 18 | contentContainer: { 19 | paddingHorizontal: 16, 20 | overflow: 'visible', 21 | }, 22 | }); 23 | -------------------------------------------------------------------------------- /example/bare/android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | -keep class com.facebook.react.turbomodule.** { *; } 13 | -------------------------------------------------------------------------------- /src/contexts/index.ts: -------------------------------------------------------------------------------- 1 | export { BottomSheetContext, BottomSheetProvider } from './external'; 2 | export { 3 | BottomSheetInternalContext, 4 | BottomSheetInternalProvider, 5 | } from './internal'; 6 | export { BottomSheetGestureHandlersContext } from './gesture'; 7 | export { 8 | BottomSheetModalContext, 9 | BottomSheetModalProvider, 10 | } from './modal/external'; 11 | export { 12 | BottomSheetModalInternalContext, 13 | BottomSheetModalInternalProvider, 14 | } from './modal/internal'; 15 | export type { BottomSheetModalInternalContextType } from './modal/internal'; 16 | -------------------------------------------------------------------------------- /src/contexts/gesture.ts: -------------------------------------------------------------------------------- 1 | import { createContext } from 'react'; 2 | import type { PanGestureHandlerGestureEvent } from 'react-native-gesture-handler'; 3 | 4 | export interface BottomSheetGestureHandlersContextType { 5 | contentPanGestureHandler: (event: PanGestureHandlerGestureEvent) => void; 6 | handlePanGestureHandler: (event: PanGestureHandlerGestureEvent) => void; 7 | scrollablePanGestureHandler: (event: PanGestureHandlerGestureEvent) => void; 8 | } 9 | 10 | export const BottomSheetGestureHandlersContext = 11 | createContext(null); 12 | -------------------------------------------------------------------------------- /example/bare/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 11 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/components/bottomSheetContainer/types.d.ts: -------------------------------------------------------------------------------- 1 | import type { ReactNode } from 'react'; 2 | import type { Insets, StyleProp, ViewStyle } from 'react-native'; 3 | import type Animated from 'react-native-reanimated'; 4 | import type { BottomSheetProps } from '../bottomSheet/types'; 5 | 6 | export interface BottomSheetContainerProps 7 | extends Partial< 8 | Pick 9 | > { 10 | containerHeight: Animated.SharedValue; 11 | containerOffset: Animated.SharedValue; 12 | shouldCalculateHeight?: boolean; 13 | style?: StyleProp; 14 | children: ReactNode; 15 | } 16 | -------------------------------------------------------------------------------- /example/bare/patches/@react-native-community+blur+3.6.0.patch: -------------------------------------------------------------------------------- 1 | diff --git a/node_modules/@react-native-community/blur/android/build.gradle b/node_modules/@react-native-community/blur/android/build.gradle 2 | index 8177235..b401cb8 100644 3 | --- a/node_modules/@react-native-community/blur/android/build.gradle 4 | +++ b/node_modules/@react-native-community/blur/android/build.gradle 5 | @@ -43,5 +43,5 @@ repositories { 6 | dependencies { 7 | //noinspection GradleDynamicVersion 8 | implementation 'com.facebook.react:react-native:+' 9 | - implementation 'com.eightbitlab:blurview:1.6.3' 10 | + implementation 'com.github.Dimezis:BlurView:version-1.6.6' 11 | } 12 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | on: 2 | issues: 3 | types: [opened, edited] 4 | 5 | jobs: 6 | auto_close_issues: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - name: Checkout 10 | uses: actions/checkout@v1 11 | - name: Automatically close issues that don't follow the issue template 12 | uses: lucasbento/auto-close-issues@v1.0.2 13 | with: 14 | github-token: ${{ secrets.GITHUB_TOKEN }} 15 | issue-close-message: "@${issue.user.login}: hello! :wave:\n\nThis issue is being automatically closed because it does not follow the issue template." # optional property 16 | closed-issues-label: "not-following-issue-template" -------------------------------------------------------------------------------- /src/components/bottomSheetScrollable/index.ts: -------------------------------------------------------------------------------- 1 | export { createBottomSheetScrollableComponent } from './createBottomSheetScrollableComponent'; 2 | export { default as BottomSheetSectionList } from './BottomSheetSectionList'; 3 | export { default as BottomSheetFlatList } from './BottomSheetFlatList'; 4 | export { default as BottomSheetScrollView } from './BottomSheetScrollView'; 5 | export { default as BottomSheetVirtualizedList } from './BottomSheetVirtualizedList'; 6 | 7 | export type { 8 | BottomSheetFlatListMethods, 9 | BottomSheetScrollViewMethods, 10 | BottomSheetSectionListMethods, 11 | BottomSheetVirtualizedListMethods, 12 | BottomSheetScrollableProps, 13 | } from './types'; 14 | -------------------------------------------------------------------------------- /src/components/touchables/index.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | TouchableOpacity as RNTouchableOpacity, 3 | TouchableHighlight as RNTouchableHighlight, 4 | TouchableWithoutFeedback as RNTouchableWithoutFeedback, 5 | } from 'react-native'; 6 | 7 | import { 8 | TouchableOpacity, 9 | TouchableHighlight, 10 | TouchableWithoutFeedback, 11 | // @ts-ignore 12 | } from './Touchables'; 13 | 14 | export default { 15 | TouchableOpacity: TouchableOpacity as any as typeof RNTouchableOpacity, 16 | TouchableHighlight: TouchableHighlight as any as typeof RNTouchableHighlight, 17 | TouchableWithoutFeedback: 18 | TouchableWithoutFeedback as any as typeof RNTouchableWithoutFeedback, 19 | }; 20 | -------------------------------------------------------------------------------- /src/hooks/useStableCallback.ts: -------------------------------------------------------------------------------- 1 | import { useRef, useCallback, useEffect } from 'react'; 2 | 3 | type Callback = (...args: any[]) => any; 4 | /** 5 | * Provide a stable version of useCallback 6 | * https://gist.github.com/JakeCoxon/c7ebf6e6496f8468226fd36b596e1985 7 | */ 8 | export const useStableCallback = (callback: Callback) => { 9 | const callbackRef = useRef(); 10 | const memoCallback = useCallback( 11 | (...args: any) => callbackRef.current && callbackRef.current(...args), 12 | [] 13 | ); 14 | useEffect(() => { 15 | callbackRef.current = callback; 16 | return () => (callbackRef.current = undefined); 17 | }); 18 | return memoCallback; 19 | }; 20 | -------------------------------------------------------------------------------- /src/components/bottomSheetRefreshControl/index.ts: -------------------------------------------------------------------------------- 1 | import type React from 'react'; 2 | import type { RefreshControlProps } from 'react-native'; 3 | import type { NativeViewGestureHandlerProps } from 'react-native-gesture-handler'; 4 | import BottomSheetRefreshControl from './BottomSheetRefreshControl'; 5 | 6 | export default BottomSheetRefreshControl as any as React.MemoExoticComponent< 7 | React.ForwardRefExoticComponent< 8 | RefreshControlProps & { 9 | children: React.ReactNode | React.ReactNode[]; 10 | } & React.RefAttributes< 11 | React.ComponentType< 12 | NativeViewGestureHandlerProps & React.RefAttributes 13 | > 14 | > 15 | > 16 | >; 17 | -------------------------------------------------------------------------------- /example/app/src/components/button/Button.tsx: -------------------------------------------------------------------------------- 1 | import React, { memo } from 'react'; 2 | import { ViewStyle, TextStyle } from 'react-native'; 3 | import { ShowcaseButton, ShowcaseLabel } from '@gorhom/showcase-template'; 4 | 5 | interface ButtonProps { 6 | label: string; 7 | labelStyle?: TextStyle; 8 | style?: ViewStyle; 9 | onPress: () => void; 10 | } 11 | 12 | const ButtonComponent = ({ 13 | label, 14 | labelStyle, 15 | style, 16 | onPress, 17 | }: ButtonProps) => ( 18 | 19 | {label} 20 | 21 | ); 22 | 23 | export const Button = memo(ButtonComponent); 24 | -------------------------------------------------------------------------------- /example/bare/android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'BottomSheetExample' 2 | apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings) 3 | include ':app' 4 | 5 | includeBuild('../node_modules/react-native-gradle-plugin') 6 | if (settings.hasProperty("newArchEnabled") && settings.newArchEnabled == "true") { 7 | include(":ReactAndroid") 8 | project(":ReactAndroid").projectDir = file('../node_modules/react-native/ReactAndroid') 9 | 10 | include(":ReactAndroid:hermes-engine") 11 | project(":ReactAndroid:hermes-engine").projectDir = file('../node_modules/react-native/ReactAndroid/hermes-engine') 12 | } 13 | -------------------------------------------------------------------------------- /src/components/bottomSheetDraggableView/types.d.ts: -------------------------------------------------------------------------------- 1 | import type { ReactNode, Ref } from 'react'; 2 | import type { ViewProps as RNViewProps } from 'react-native'; 3 | import type { NativeViewGestureHandler } from 'react-native-gesture-handler'; 4 | import type { GESTURE_SOURCE } from '../../constants'; 5 | 6 | export type BottomSheetDraggableViewProps = RNViewProps & { 7 | /** 8 | * Defines the gesture type of the draggable view. 9 | * 10 | * @default GESTURE_SOURCE.CONTENT 11 | * @type GESTURE_SOURCE 12 | */ 13 | gestureType?: GESTURE_SOURCE; 14 | nativeGestureRef?: Ref | null; 15 | refreshControlGestureRef?: Ref | null; 16 | children: ReactNode[] | ReactNode; 17 | }; 18 | -------------------------------------------------------------------------------- /src/hooks/useBottomSheetTimingConfigs.ts: -------------------------------------------------------------------------------- 1 | import { useMemo } from 'react'; 2 | import type { WithTimingConfig } from 'react-native-reanimated'; 3 | import { ANIMATION_DURATION, ANIMATION_EASING } from '../constants'; 4 | 5 | /** 6 | * Generate timing animation configs. 7 | * @default 8 | * - easing: Easing.out(Easing.exp) 9 | * - duration 250 10 | * @param configs overridable configs. 11 | */ 12 | export const useBottomSheetTimingConfigs = (configs: WithTimingConfig) => { 13 | return useMemo(() => { 14 | const _configs: WithTimingConfig = { 15 | easing: configs.easing || ANIMATION_EASING, 16 | duration: configs.duration || ANIMATION_DURATION, 17 | }; 18 | 19 | return _configs; 20 | }, [configs.duration, configs.easing]); 21 | }; 22 | -------------------------------------------------------------------------------- /src/components/bottomSheetBackground/BottomSheetBackground.tsx: -------------------------------------------------------------------------------- 1 | import React, { memo } from 'react'; 2 | import { View } from 'react-native'; 3 | import type { BottomSheetBackgroundProps } from './types'; 4 | import { styles } from './styles'; 5 | 6 | const BottomSheetBackgroundComponent = ({ 7 | pointerEvents, 8 | style, 9 | }: BottomSheetBackgroundProps) => ( 10 | 17 | ); 18 | 19 | const BottomSheetBackground = memo(BottomSheetBackgroundComponent); 20 | BottomSheetBackground.displayName = 'BottomSheetBackground'; 21 | 22 | export default BottomSheetBackground; 23 | -------------------------------------------------------------------------------- /src/hooks/useBottomSheetInternal.ts: -------------------------------------------------------------------------------- 1 | import { useContext } from 'react'; 2 | import { 3 | BottomSheetInternalContext, 4 | BottomSheetInternalContextType, 5 | } from '../contexts/internal'; 6 | 7 | export function useBottomSheetInternal( 8 | unsafe?: false 9 | ): BottomSheetInternalContextType; 10 | 11 | export function useBottomSheetInternal( 12 | unsafe: true 13 | ): BottomSheetInternalContextType | null; 14 | 15 | export function useBottomSheetInternal( 16 | unsafe?: boolean 17 | ): BottomSheetInternalContextType | null { 18 | const context = useContext(BottomSheetInternalContext); 19 | 20 | if (unsafe !== true && context === null) { 21 | throw "'useBottomSheetInternal' cannot be used out of the BottomSheet!"; 22 | } 23 | 24 | return context; 25 | } 26 | -------------------------------------------------------------------------------- /.auto-changelog: -------------------------------------------------------------------------------- 1 | { 2 | "handlebarsSetup": "./scripts/auto-changelog.js", 3 | "ignoreCommitPattern": "release v", 4 | "startingVersion": "v4.0.0-alpha.0", 5 | "unreleased": false, 6 | "commitLimit": false, 7 | "replaceText": { 8 | "([bB]reaking: )": "", 9 | "([bB]reaking change: )": "", 10 | "(^[fF]eat: )": "", 11 | "(^[fF]eat\\()": "(", 12 | "(^[fF]ix: )": "", 13 | "(^[fF]ix\\()": "(", 14 | "(^[cC]hore: )": "", 15 | "(^[cC]hore\\()": "(", 16 | "(^[dD]ocs: )": "", 17 | "(^[dD]ocs\\()": "(", 18 | "(^[rR]efactor: )": "", 19 | "(^[rR]efactor\\()": "(", 20 | "(^[tT]est: )": "", 21 | "(^[tT]est\\()": "(", 22 | "(^[sS]tyle: )": "", 23 | "(^[sS]tyle\\()": "(", 24 | "(^[pP]erf: )": "", 25 | "(^[pP]erf\\()": "(" 26 | } 27 | } -------------------------------------------------------------------------------- /src/components/bottomSheetBackdrop/constants.ts: -------------------------------------------------------------------------------- 1 | const DEFAULT_OPACITY = 0.5; 2 | const DEFAULT_APPEARS_ON_INDEX = 1; 3 | const DEFAULT_DISAPPEARS_ON_INDEX = 0; 4 | const DEFAULT_ENABLE_TOUCH_THROUGH = false; 5 | const DEFAULT_PRESS_BEHAVIOR = 'close' as const; 6 | 7 | const DEFAULT_ACCESSIBLE = true; 8 | const DEFAULT_ACCESSIBILITY_ROLE = 'button'; 9 | const DEFAULT_ACCESSIBILITY_LABEL = 'Bottom sheet backdrop'; 10 | const DEFAULT_ACCESSIBILITY_HINT = 'Tap to close the bottom sheet'; 11 | 12 | export { 13 | DEFAULT_OPACITY, 14 | DEFAULT_APPEARS_ON_INDEX, 15 | DEFAULT_DISAPPEARS_ON_INDEX, 16 | DEFAULT_ENABLE_TOUCH_THROUGH, 17 | DEFAULT_PRESS_BEHAVIOR, 18 | DEFAULT_ACCESSIBLE, 19 | DEFAULT_ACCESSIBILITY_ROLE, 20 | DEFAULT_ACCESSIBILITY_LABEL, 21 | DEFAULT_ACCESSIBILITY_HINT, 22 | }; 23 | -------------------------------------------------------------------------------- /src/hooks/useBottomSheetModalInternal.ts: -------------------------------------------------------------------------------- 1 | import { useContext } from 'react'; 2 | import { 3 | BottomSheetModalInternalContext, 4 | BottomSheetModalInternalContextType, 5 | } from '../contexts'; 6 | 7 | export function useBottomSheetModalInternal( 8 | unsafe?: false 9 | ): BottomSheetModalInternalContextType; 10 | 11 | export function useBottomSheetModalInternal( 12 | unsafe: true 13 | ): BottomSheetModalInternalContextType | null; 14 | 15 | export function useBottomSheetModalInternal( 16 | unsafe?: boolean 17 | ): BottomSheetModalInternalContextType | null { 18 | const context = useContext(BottomSheetModalInternalContext); 19 | 20 | if (unsafe !== true && context === null) { 21 | throw "'BottomSheetModalInternalContext' cannot be null!"; 22 | } 23 | 24 | return context; 25 | } 26 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | # Feature Request 11 | 12 | 15 | 16 | ## Why it is needed 17 | 18 | 21 | 22 | ## Possible implementation 23 | 24 | 27 | 28 | ### Code sample 29 | 30 | 33 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "paths": { 5 | "@gorhom/bottom-sheet": ["./src/index"] 6 | }, 7 | "allowUnreachableCode": false, 8 | "allowUnusedLabels": false, 9 | "esModuleInterop": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "jsx": "react", 12 | "lib": ["esnext"], 13 | "module": "esnext", 14 | "moduleResolution": "node", 15 | "noFallthroughCasesInSwitch": true, 16 | "noImplicitReturns": true, 17 | "noImplicitUseStrict": false, 18 | "noStrictGenericChecks": false, 19 | "noUnusedLocals": true, 20 | "noUnusedParameters": true, 21 | "resolveJsonModule": true, 22 | "skipLibCheck": true, 23 | "strict": true, 24 | "target": "esnext" 25 | }, 26 | "exclude": ["example"], 27 | "include": ["src"] 28 | } 29 | -------------------------------------------------------------------------------- /src/components/bottomSheetDebugView/BottomSheetDebugView.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { View } from 'react-native'; 3 | import Animated from 'react-native-reanimated'; 4 | import ReText from './ReText'; 5 | import { styles } from './styles'; 6 | 7 | interface BottomSheetDebugViewProps { 8 | values: Record | number>; 9 | } 10 | 11 | const BottomSheetDebugView = ({ values }: BottomSheetDebugViewProps) => { 12 | return ( 13 | 14 | {Object.keys(values).map(key => ( 15 | 21 | ))} 22 | 23 | ); 24 | }; 25 | 26 | export default BottomSheetDebugView; 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # XDE 6 | .expo/ 7 | 8 | # VSCode 9 | .vscode/ 10 | jsconfig.json 11 | 12 | # Xcode 13 | # 14 | build/ 15 | *.pbxuser 16 | !default.pbxuser 17 | *.mode1v3 18 | !default.mode1v3 19 | *.mode2v3 20 | !default.mode2v3 21 | *.perspectivev3 22 | !default.perspectivev3 23 | xcuserdata 24 | *.xccheckout 25 | *.moved-aside 26 | DerivedData 27 | *.hmap 28 | *.ipa 29 | *.xcuserstate 30 | project.xcworkspace 31 | 32 | # Android/IJ 33 | # 34 | .idea 35 | .gradle 36 | local.properties 37 | android.iml 38 | 39 | # Cocoapods 40 | # 41 | Pods/ 42 | 43 | # node.js 44 | # 45 | node_modules/ 46 | npm-debug.log 47 | yarn-debug.log 48 | yarn-error.log 49 | yarn.lock 50 | example/yarn.lock 51 | 52 | # BUCK 53 | buck-out/ 54 | \.buckd/ 55 | android/app/libs 56 | android/keystores/debug.keystore 57 | 58 | # generated by bob 59 | lib/ 60 | -------------------------------------------------------------------------------- /src/components/bottomSheetBackdropContainer/BottomSheetBackdropContainer.tsx: -------------------------------------------------------------------------------- 1 | import React, { memo } from 'react'; 2 | import type { BottomSheetBackdropContainerProps } from './types'; 3 | import { styles } from './styles'; 4 | 5 | const BottomSheetBackdropContainerComponent = ({ 6 | animatedIndex, 7 | animatedPosition, 8 | backdropComponent: BackdropComponent, 9 | }: BottomSheetBackdropContainerProps) => { 10 | return BackdropComponent ? ( 11 | 16 | ) : null; 17 | }; 18 | 19 | const BottomSheetBackdropContainer = memo( 20 | BottomSheetBackdropContainerComponent 21 | ); 22 | BottomSheetBackdropContainer.displayName = 'BottomSheetBackdropContainer'; 23 | 24 | export default BottomSheetBackdropContainer; 25 | -------------------------------------------------------------------------------- /src/utilities/validateSnapPoint.ts: -------------------------------------------------------------------------------- 1 | import invariant from 'invariant'; 2 | 3 | export const validateSnapPoint = (snapPoint: any) => { 4 | invariant( 5 | typeof snapPoint === 'number' || typeof snapPoint === 'string', 6 | `'${snapPoint}' is not a valid snap point! expected types are string or number.` 7 | ); 8 | 9 | invariant( 10 | typeof snapPoint === 'number' || 11 | (typeof snapPoint === 'string' && snapPoint.includes('%')), 12 | `'${snapPoint}' is not a valid percentage snap point! expected percentage snap point must include '%'. e.g. '50%'` 13 | ); 14 | 15 | invariant( 16 | typeof snapPoint === 'number' || 17 | (typeof snapPoint === 'string' && Number(snapPoint.split('%')[0])), 18 | `'${snapPoint}' is not a valid percentage snap point! expected percentage snap point must be only numbers and '%'. e.g. '50%'` 19 | ); 20 | }; 21 | -------------------------------------------------------------------------------- /example/expo/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "name": "BottomSheetExample", 4 | "slug": "BottomSheetExample", 5 | "version": "1.0.0", 6 | "orientation": "portrait", 7 | "icon": "./assets/icon.png", 8 | "entryPoint": "./index.ts", 9 | "userInterfaceStyle": "light", 10 | "splash": { 11 | "image": "./assets/splash.png", 12 | "resizeMode": "contain", 13 | "backgroundColor": "#ffffff" 14 | }, 15 | "updates": { 16 | "fallbackToCacheTimeout": 0 17 | }, 18 | "assetBundlePatterns": ["**/*"], 19 | "ios": { 20 | "supportsTablet": true 21 | }, 22 | "android": { 23 | "adaptiveIcon": { 24 | "foregroundImage": "./assets/adaptive-icon.png", 25 | "backgroundColor": "#FFFFFF" 26 | } 27 | }, 28 | "web": { 29 | "favicon": "./assets/favicon.png" 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /example/bare/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "paths": { 5 | "@gorhom/bottom-sheet": ["../../src/index"], 6 | "@gorhom/bottom-sheet-example-app": ["../app/src/index"] 7 | }, 8 | "allowUnreachableCode": false, 9 | "allowUnusedLabels": false, 10 | "esModuleInterop": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "jsx": "react", 13 | "lib": ["esnext"], 14 | "module": "esnext", 15 | "moduleResolution": "node", 16 | "noFallthroughCasesInSwitch": true, 17 | "noImplicitReturns": true, 18 | "noImplicitUseStrict": false, 19 | "noStrictGenericChecks": false, 20 | "noUnusedLocals": true, 21 | "noUnusedParameters": true, 22 | "resolveJsonModule": true, 23 | "skipLibCheck": true, 24 | "strict": true, 25 | "target": "esnext" 26 | }, 27 | "include": ["src"] 28 | } 29 | -------------------------------------------------------------------------------- /example/bare/android/app/src/main/jni/MainApplicationModuleProvider.cpp: -------------------------------------------------------------------------------- 1 | #include "MainApplicationModuleProvider.h" 2 | 3 | #include 4 | 5 | namespace facebook { 6 | namespace react { 7 | 8 | std::shared_ptr MainApplicationModuleProvider( 9 | const std::string moduleName, 10 | const JavaTurboModule::InitParams ¶ms) { 11 | // Here you can provide your own module provider for TurboModules coming from 12 | // either your application or from external libraries. The approach to follow 13 | // is similar to the following (for a library called `samplelibrary`: 14 | // 15 | // auto module = samplelibrary_ModuleProvider(moduleName, params); 16 | // if (module != nullptr) { 17 | // return module; 18 | // } 19 | // return rncore_ModuleProvider(moduleName, params); 20 | return rncore_ModuleProvider(moduleName, params); 21 | } 22 | 23 | } // namespace react 24 | } // namespace facebook -------------------------------------------------------------------------------- /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | "README.md" 4 | ], 5 | "imageSize": 76, 6 | "commit": false, 7 | "contributors": [ 8 | { 9 | "login": "vonovak", 10 | "name": "Vojtech Novak", 11 | "avatar_url": "https://avatars.githubusercontent.com/u/1566403?v=4", 12 | "profile": "https://react-native-training.eu", 13 | "contributions": [ 14 | "code" 15 | ] 16 | }, 17 | { 18 | "login": "kickbk", 19 | "name": "kickbk", 20 | "avatar_url": "https://avatars.githubusercontent.com/u/31323376?v=4", 21 | "profile": "https://github.com/kickbk", 22 | "contributions": [ 23 | "bug", 24 | "test" 25 | ] 26 | } 27 | ], 28 | "contributorsPerLine": 7, 29 | "projectName": "react-native-bottom-sheet", 30 | "projectOwner": "gorhom", 31 | "repoType": "github", 32 | "repoHost": "https://github.com", 33 | "skipCi": true 34 | } 35 | -------------------------------------------------------------------------------- /src/components/bottomSheetView/types.d.ts: -------------------------------------------------------------------------------- 1 | import type { EffectCallback, DependencyList, ReactNode } from 'react'; 2 | import type { ViewProps as RNViewProps } from 'react-native'; 3 | 4 | export interface BottomSheetViewProps extends RNViewProps { 5 | /** 6 | * Adjust the scrollable bottom margin to avoid the animated footer. 7 | * 8 | * @type boolean 9 | * @default false 10 | */ 11 | enableFooterMarginAdjustment?: boolean; 12 | 13 | /** 14 | * This needed when bottom sheet used with multiple scrollables to allow bottom sheet 15 | * detect the current scrollable ref, especially when used with `React Navigation`. 16 | * You will need to provide `useFocusEffect` from `@react-navigation/native`. 17 | * 18 | * @type (effect: EffectCallback, deps?: DependencyList) => void 19 | * @default useEffect 20 | */ 21 | focusHook?: (effect: EffectCallback, deps?: DependencyList) => void; 22 | 23 | children: ReactNode[] | ReactNode; 24 | } 25 | -------------------------------------------------------------------------------- /src/contexts/modal/internal.ts: -------------------------------------------------------------------------------- 1 | import { createContext, Ref } from 'react'; 2 | import type { Insets } from 'react-native'; 3 | import type Animated from 'react-native-reanimated'; 4 | import type BottomSheet from '../../components/bottomSheet'; 5 | import type { BottomSheetModalStackBehavior } from '../../components/bottomSheetModal'; 6 | 7 | export interface BottomSheetModalInternalContextType { 8 | containerHeight: Animated.SharedValue; 9 | containerOffset: Animated.SharedValue>; 10 | mountSheet: ( 11 | key: string, 12 | ref: Ref, 13 | stackBehavior: BottomSheetModalStackBehavior 14 | ) => void; 15 | unmountSheet: (key: string) => void; 16 | willUnmountSheet: (key: string) => void; 17 | } 18 | 19 | export const BottomSheetModalInternalContext = 20 | createContext(null); 21 | 22 | export const BottomSheetModalInternalProvider = 23 | BottomSheetModalInternalContext.Provider; 24 | -------------------------------------------------------------------------------- /example/expo/babel.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const fs = require('fs'); 3 | 4 | const root = path.resolve(__dirname, '../..'); 5 | const rootPak = JSON.parse( 6 | fs.readFileSync(path.join(root, 'package.json'), 'utf8') 7 | ); 8 | 9 | const app = path.join(__dirname, '../app'); 10 | const appPak = JSON.parse( 11 | fs.readFileSync(path.join(app, 'package.json'), 'utf8') 12 | ); 13 | 14 | module.exports = function (api) { 15 | api.cache(true); 16 | return { 17 | presets: ['babel-preset-expo'], 18 | plugins: [ 19 | 'react-native-reanimated/plugin', 20 | [ 21 | 'module-resolver', 22 | { 23 | extensions: ['.tsx', '.ts', '.js', '.json'], 24 | alias: { 25 | // For development, we want to alias the library to the source 26 | [rootPak.name]: path.join(root, rootPak['react-native']), 27 | [appPak.name]: path.join(app, appPak['react-native']), 28 | }, 29 | }, 30 | ], 31 | ], 32 | }; 33 | }; 34 | -------------------------------------------------------------------------------- /src/components/bottomSheetHandleContainer/types.d.ts: -------------------------------------------------------------------------------- 1 | import type { PanGestureHandlerProperties } from 'react-native-gesture-handler'; 2 | import type Animated from 'react-native-reanimated'; 3 | import type { BottomSheetProps } from '../bottomSheet'; 4 | import type { BottomSheetHandleProps } from '../bottomSheetHandle'; 5 | import type { useInteractivePanGestureHandlerConfigs } from '../../hooks/useGestureHandler'; 6 | 7 | export interface BottomSheetHandleContainerProps 8 | extends Pick, 9 | Pick< 10 | BottomSheetProps, 11 | | 'handleComponent' 12 | | 'enableHandlePanningGesture' 13 | | 'handleIndicatorStyle' 14 | | 'handleStyle' 15 | >, 16 | Pick< 17 | useInteractivePanGestureHandlerConfigs, 18 | | 'enableOverDrag' 19 | | 'enablePanDownToClose' 20 | | 'overDragResistanceFactor' 21 | | 'keyboardBehavior' 22 | >, 23 | BottomSheetHandleProps { 24 | handleHeight: Animated.SharedValue; 25 | } 26 | -------------------------------------------------------------------------------- /example/bare/src/screens/DummyScreen.tsx: -------------------------------------------------------------------------------- 1 | import React, { useCallback, memo } from 'react'; 2 | import { useNavigation } from '@react-navigation/native'; 3 | import { ContactList } from '@gorhom/bottom-sheet-example-app'; 4 | 5 | interface DummyScreenProps { 6 | title: string; 7 | nextScreen: string; 8 | type: 'FlatList' | 'SectionList' | 'ScrollView' | 'View'; 9 | count?: number; 10 | } 11 | 12 | const createDummyScreen = ({ 13 | nextScreen, 14 | type, 15 | count = 50, 16 | }: DummyScreenProps) => 17 | memo(() => { 18 | const { navigate } = useNavigation(); 19 | 20 | const handleNavigatePress = useCallback(() => { 21 | requestAnimationFrame(() => navigate(nextScreen as any)); 22 | // eslint-disable-next-line react-hooks/exhaustive-deps 23 | }, []); 24 | 25 | return ( 26 | 32 | ); 33 | }); 34 | 35 | export default createDummyScreen; 36 | -------------------------------------------------------------------------------- /example/bare/android/app/src/main/jni/MainComponentsRegistry.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace facebook { 9 | namespace react { 10 | 11 | class MainComponentsRegistry 12 | : public facebook::jni::HybridClass { 13 | public: 14 | // Adapt it to the package you used for your Java class. 15 | constexpr static auto kJavaDescriptor = 16 | "Ldev/gorhom/bottomsheet/components/MainComponentsRegistry;"; 17 | 18 | static void registerNatives(); 19 | 20 | MainComponentsRegistry(ComponentFactory *delegate); 21 | 22 | private: 23 | static std::shared_ptr 24 | sharedProviderRegistry(); 25 | 26 | static jni::local_ref initHybrid( 27 | jni::alias_ref, 28 | ComponentFactory *delegate); 29 | }; 30 | 31 | } // namespace react 32 | } // namespace facebook -------------------------------------------------------------------------------- /src/components/bottomSheetFooter/types.d.ts: -------------------------------------------------------------------------------- 1 | import type { ReactElement, ReactNode } from 'react'; 2 | import { ViewStyle } from 'react-native'; 3 | import type Animated from 'react-native-reanimated'; 4 | 5 | export interface BottomSheetFooterProps { 6 | /** 7 | * Calculated footer animated position. 8 | * 9 | * @type Animated.SharedValue 10 | */ 11 | animatedFooterPosition: Animated.SharedValue; 12 | } 13 | 14 | export interface BottomSheetDefaultFooterProps extends BottomSheetFooterProps { 15 | /** 16 | * Bottom inset to be added below the footer, usually comes 17 | * from `react-native-safe-area-context` hook `useSafeArea`. 18 | * 19 | * @type number 20 | * @default 0 21 | */ 22 | bottomInset?: number; 23 | 24 | /** 25 | * Container style. 26 | * 27 | * @type ViewStyle 28 | */ 29 | style?: ViewStyle; 30 | 31 | /** 32 | * Component to be placed in the footer. 33 | * 34 | * @type {ReactNode | ReactNode[] | (() => ReactElement)} 35 | */ 36 | children?: ReactNode | ReactNode[] | (() => ReactElement); 37 | } 38 | -------------------------------------------------------------------------------- /src/components/bottomSheetScrollable/BottomSheetFlatList.tsx: -------------------------------------------------------------------------------- 1 | import { memo } from 'react'; 2 | import { 3 | FlatList as RNFlatList, 4 | FlatListProps as RNFlatListProps, 5 | } from 'react-native'; 6 | import Animated from 'react-native-reanimated'; 7 | import { SCROLLABLE_TYPE } from '../../constants'; 8 | import { createBottomSheetScrollableComponent } from './createBottomSheetScrollableComponent'; 9 | import type { 10 | BottomSheetFlatListMethods, 11 | BottomSheetFlatListProps, 12 | } from './types'; 13 | 14 | const AnimatedFlatList = 15 | Animated.createAnimatedComponent>(RNFlatList); 16 | 17 | const BottomSheetFlatListComponent = createBottomSheetScrollableComponent< 18 | BottomSheetFlatListMethods, 19 | BottomSheetFlatListProps 20 | >(SCROLLABLE_TYPE.FLATLIST, AnimatedFlatList); 21 | 22 | const BottomSheetFlatList = memo(BottomSheetFlatListComponent); 23 | BottomSheetFlatList.displayName = 'BottomSheetFlatList'; 24 | 25 | export default BottomSheetFlatList as ( 26 | props: BottomSheetFlatListProps 27 | ) => ReturnType; 28 | -------------------------------------------------------------------------------- /src/utilities/getKeyboardAnimationConfigs.ts: -------------------------------------------------------------------------------- 1 | import { Easing } from 'react-native-reanimated'; 2 | import type { KeyboardEventEasing } from 'react-native'; 3 | 4 | export const getKeyboardAnimationConfigs = ( 5 | easing: KeyboardEventEasing, 6 | duration: number 7 | ) => { 8 | 'worklet'; 9 | switch (easing) { 10 | case 'easeIn': 11 | return { 12 | easing: Easing.in(Easing.ease), 13 | duration, 14 | }; 15 | 16 | case 'easeOut': 17 | return { 18 | easing: Easing.out(Easing.ease), 19 | duration, 20 | }; 21 | 22 | case 'easeInEaseOut': 23 | return { 24 | easing: Easing.inOut(Easing.ease), 25 | duration, 26 | }; 27 | 28 | case 'linear': 29 | return { 30 | easing: Easing.linear, 31 | duration, 32 | }; 33 | 34 | case 'keyboard': 35 | return { 36 | damping: 500, 37 | stiffness: 1000, 38 | mass: 3, 39 | overshootClamping: true, 40 | restDisplacementThreshold: 10, 41 | restSpeedThreshold: 10, 42 | }; 43 | } 44 | }; 45 | -------------------------------------------------------------------------------- /src/utilities/logger.ts: -------------------------------------------------------------------------------- 1 | interface PrintOptions { 2 | component?: string; 3 | method?: string; 4 | params?: Record | string | number | boolean; 5 | } 6 | 7 | type Print = (options: PrintOptions) => void; 8 | 9 | let isLoggingEnabled = false; 10 | 11 | const enableLogging = () => { 12 | if (!__DEV__) { 13 | console.warn('[BottomSheet] could not enable logging on production!'); 14 | return; 15 | } 16 | isLoggingEnabled = true; 17 | }; 18 | 19 | let print: Print = () => {}; 20 | 21 | if (__DEV__) { 22 | print = ({ component, method, params }) => { 23 | if (!isLoggingEnabled) { 24 | return; 25 | } 26 | let message = ''; 27 | 28 | if (typeof params === 'object') { 29 | message = Object.keys(params) 30 | .map(key => `${key}:${params[key]}`) 31 | .join(' '); 32 | } else { 33 | message = `${params ?? ''}`; 34 | } 35 | // eslint-disable-next-line no-console 36 | console.log(`[${[component, method].filter(Boolean).join('::')}]`, message); 37 | }; 38 | } 39 | 40 | Object.freeze(print); 41 | 42 | export { print, enableLogging }; 43 | -------------------------------------------------------------------------------- /example/bare/ios/Podfile: -------------------------------------------------------------------------------- 1 | require_relative '../node_modules/react-native/scripts/react_native_pods' 2 | require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' 3 | 4 | platform :ios, '12.4' 5 | install! 'cocoapods', :deterministic_uuids => false 6 | 7 | production = ENV["PRODUCTION"] == "1" 8 | 9 | target 'BottomSheetExample' do 10 | config = use_native_modules! 11 | 12 | # Flags change depending on the env values. 13 | flags = get_default_flags() 14 | 15 | use_react_native!( 16 | :path => config[:reactNativePath], 17 | # to enable hermes on iOS, change `false` to `true` and then install pods 18 | :production => production, 19 | :hermes_enabled => true, 20 | :fabric_enabled => flags[:fabric_enabled], 21 | :flipper_configuration => FlipperConfiguration.enabled, 22 | # An absolute path to your application root. 23 | :app_path => "#{Pod::Config.instance.installation_root}/.." 24 | ) 25 | 26 | post_install do |installer| 27 | react_native_post_install(installer) 28 | __apply_Xcode_12_5_M1_post_install_workaround(installer) 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /src/components/bottomSheetScrollable/BottomSheetScrollView.tsx: -------------------------------------------------------------------------------- 1 | import { memo } from 'react'; 2 | import { 3 | ScrollView as RNScrollView, 4 | ScrollViewProps as RNScrollViewProps, 5 | } from 'react-native'; 6 | import Animated from 'react-native-reanimated'; 7 | import { SCROLLABLE_TYPE } from '../../constants'; 8 | import { createBottomSheetScrollableComponent } from './createBottomSheetScrollableComponent'; 9 | import type { 10 | BottomSheetScrollViewMethods, 11 | BottomSheetScrollViewProps, 12 | } from './types'; 13 | 14 | const AnimatedScrollView = 15 | Animated.createAnimatedComponent(RNScrollView); 16 | 17 | const BottomSheetScrollViewComponent = createBottomSheetScrollableComponent< 18 | BottomSheetScrollViewMethods, 19 | BottomSheetScrollViewProps 20 | >(SCROLLABLE_TYPE.SCROLLVIEW, AnimatedScrollView); 21 | 22 | const BottomSheetScrollView = memo(BottomSheetScrollViewComponent); 23 | BottomSheetScrollView.displayName = 'BottomSheetScrollView'; 24 | 25 | export default BottomSheetScrollView as ( 26 | props: BottomSheetScrollViewProps 27 | ) => ReturnType; 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Mo Gorhom 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /example/expo/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const createExpoWebpackConfigAsync = require('@expo/webpack-config'); 3 | const { resolver } = require('./metro.config'); 4 | 5 | const root = path.resolve(__dirname, '../..'); 6 | const app = path.resolve(__dirname, '../app'); 7 | const node_modules = path.join(__dirname, 'node_modules'); 8 | 9 | module.exports = async function (env, argv) { 10 | const config = await createExpoWebpackConfigAsync(env, argv); 11 | 12 | config.module.rules.push({ 13 | test: /\.(js|jsx|ts|tsx)$/, 14 | include: path.resolve(root, 'src'), 15 | use: 'babel-loader', 16 | }); 17 | 18 | config.module.rules.push({ 19 | test: /\.(js|jsx|ts|tsx)$/, 20 | include: path.resolve(app, 'src'), 21 | use: 'babel-loader', 22 | }); 23 | 24 | // We need to make sure that only one version is loaded for peerDependencies 25 | // So we alias them to the versions in example's node_modules 26 | Object.assign(config.resolve.alias, { 27 | ...resolver.extraNodeModules, 28 | 'react-native-web': path.join(node_modules, 'react-native-web'), 29 | }); 30 | 31 | return config; 32 | }; 33 | -------------------------------------------------------------------------------- /example/bare/src/screens/screens.ts: -------------------------------------------------------------------------------- 1 | import type { ShowcaseExampleScreenType } from '@gorhom/showcase-template'; 2 | 3 | export const screens = [ 4 | { 5 | title: 'Third Party Integration', 6 | data: [ 7 | { 8 | name: 'React Navigation', 9 | slug: 'Integrations/NavigatorExample', 10 | getScreen: () => require('./integrations/NavigatorExample').default, 11 | }, 12 | { 13 | name: 'React Native Screens', 14 | slug: 'Integrations/NativeScreensExample', 15 | getScreen: () => require('./integrations/NativeScreensExample').default, 16 | }, 17 | { 18 | name: 'View Pager', 19 | slug: 'Integrations/ViewPagerExample', 20 | getScreen: () => require('./integrations/ViewPagerExample').default, 21 | }, 22 | { 23 | name: 'Map', 24 | slug: 'Integrations/MapExample', 25 | getScreen: () => require('./integrations/MapExample').default, 26 | screenOptions: { 27 | headerTintColor: 'black', 28 | headerTransparent: true, 29 | }, 30 | }, 31 | ] as ShowcaseExampleScreenType[], 32 | }, 33 | ]; 34 | -------------------------------------------------------------------------------- /src/utilities/animate.ts: -------------------------------------------------------------------------------- 1 | import { 2 | WithSpringConfig, 3 | WithTimingConfig, 4 | withTiming, 5 | withSpring, 6 | AnimationCallback, 7 | } from 'react-native-reanimated'; 8 | import { ANIMATION_CONFIGS, ANIMATION_METHOD } from '../constants'; 9 | 10 | interface AnimateParams { 11 | point: number; 12 | velocity?: number; 13 | configs?: WithSpringConfig | WithTimingConfig; 14 | onComplete?: AnimationCallback; 15 | } 16 | 17 | export const animate = ({ 18 | point, 19 | configs = undefined, 20 | velocity = 0, 21 | onComplete, 22 | }: AnimateParams) => { 23 | 'worklet'; 24 | 25 | if (!configs) { 26 | configs = ANIMATION_CONFIGS; 27 | } 28 | 29 | // detect animation type 30 | const type = 31 | 'duration' in configs || 'easing' in configs 32 | ? ANIMATION_METHOD.TIMING 33 | : ANIMATION_METHOD.SPRING; 34 | 35 | if (type === ANIMATION_METHOD.TIMING) { 36 | return withTiming(point, configs as WithTimingConfig, onComplete); 37 | } else { 38 | return withSpring( 39 | point, 40 | Object.assign({ velocity }, configs) as WithSpringConfig, 41 | onComplete 42 | ); 43 | } 44 | }; 45 | -------------------------------------------------------------------------------- /src/components/bottomSheetHandle/types.d.ts: -------------------------------------------------------------------------------- 1 | import type React from 'react'; 2 | import type { ViewProps } from 'react-native'; 3 | import type { AnimateProps } from 'react-native-reanimated'; 4 | import type { 5 | BottomSheetVariables, 6 | NullableAccessibilityProps, 7 | } from '../../types'; 8 | 9 | export interface BottomSheetHandleProps extends BottomSheetVariables {} 10 | 11 | export interface BottomSheetDefaultHandleProps 12 | extends BottomSheetHandleProps, 13 | NullableAccessibilityProps { 14 | /** 15 | * View style to be applied to the handle container. 16 | * @type Animated.AnimateStyle | ViewStyle 17 | * @default undefined 18 | */ 19 | style?: AnimateProps['style']; 20 | /** 21 | * View style to be applied to the handle indicator. 22 | * @type Animated.AnimateStyle | ViewStyle 23 | * @default undefined 24 | */ 25 | indicatorStyle?: AnimateProps['style']; 26 | /** 27 | * Content to be added below the indicator. 28 | * @type React.ReactNode | React.ReactNode[]; 29 | * @default undefined 30 | */ 31 | children?: React.ReactNode | React.ReactNode[]; 32 | } 33 | -------------------------------------------------------------------------------- /example/app/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "paths": { 5 | "@gorhom/bottom-sheet": ["../../src/index"], 6 | "@gorhom/showcase-template": [ 7 | "../bare/node_modules/@gorhom/showcase-template" 8 | ], 9 | "react-native-safe-area-context": [ 10 | "../bare/node_modules/react-native-safe-area-context" 11 | ], 12 | "@react-navigation/native": [ 13 | "../bare/node_modules/@react-navigation/native" 14 | ] 15 | }, 16 | "allowUnreachableCode": false, 17 | "allowUnusedLabels": false, 18 | "esModuleInterop": true, 19 | "forceConsistentCasingInFileNames": true, 20 | "jsx": "react", 21 | "lib": ["esnext"], 22 | "module": "esnext", 23 | "moduleResolution": "node", 24 | "noFallthroughCasesInSwitch": true, 25 | "noImplicitReturns": true, 26 | "noImplicitUseStrict": false, 27 | "noStrictGenericChecks": false, 28 | "noUnusedLocals": true, 29 | "noUnusedParameters": true, 30 | "resolveJsonModule": true, 31 | "skipLibCheck": true, 32 | "strict": true, 33 | "target": "esnext" 34 | }, 35 | "include": ["src"] 36 | } 37 | -------------------------------------------------------------------------------- /example/bare/src/components/locationDetailsHandle/LocationDetailsHandle.tsx: -------------------------------------------------------------------------------- 1 | import React, { useMemo } from 'react'; 2 | import { View, StyleSheet, Dimensions } from 'react-native'; 3 | import { useShowcaseTheme } from '@gorhom/showcase-template'; 4 | 5 | const { width: SCREEN_WIDTH } = Dimensions.get('screen'); 6 | 7 | const LocationDetailsHandle = () => { 8 | // hooks 9 | const { colors } = useShowcaseTheme(); 10 | 11 | // styles 12 | const indicatorStyle = useMemo( 13 | () => [ 14 | styles.indicator, 15 | { 16 | backgroundColor: colors.border, 17 | }, 18 | ], 19 | [colors.border] 20 | ); 21 | 22 | // render 23 | return ( 24 | 25 | 26 | 27 | ); 28 | }; 29 | 30 | export const styles = StyleSheet.create({ 31 | container: { 32 | paddingHorizontal: 16, 33 | paddingVertical: 5, 34 | }, 35 | indicator: { 36 | alignSelf: 'center', 37 | width: (8 * SCREEN_WIDTH) / 100, 38 | height: 5, 39 | borderRadius: 4, 40 | backgroundColor: 'rgba(0, 0, 0, 0.5)', 41 | }, 42 | }); 43 | 44 | export default LocationDetailsHandle; 45 | -------------------------------------------------------------------------------- /src/hooks/index.ts: -------------------------------------------------------------------------------- 1 | export { useBottomSheet } from './useBottomSheet'; 2 | export { useBottomSheetInternal } from './useBottomSheetInternal'; 3 | 4 | // modal 5 | export { useBottomSheetModal } from './useBottomSheetModal'; 6 | export { useBottomSheetModalInternal } from './useBottomSheetModalInternal'; 7 | 8 | // scrollable 9 | export { useScrollable } from './useScrollable'; 10 | export { useScrollableSetter } from './useScrollableSetter'; 11 | export { useScrollHandler } from './useScrollHandler'; 12 | 13 | // gestures 14 | export { useGestureHandler } from './useGestureHandler'; 15 | export { useGestureEventsHandlersDefault } from './useGestureEventsHandlersDefault'; 16 | 17 | // utilities 18 | export { useKeyboard } from './useKeyboard'; 19 | export { useStableCallback } from './useStableCallback'; 20 | export { usePropsValidator } from './usePropsValidator'; 21 | export { useNormalizedSnapPoints } from './useNormalizedSnapPoints'; 22 | export { useReactiveSharedValue } from './useReactiveSharedValue'; 23 | export { useBottomSheetDynamicSnapPoints } from './useBottomSheetDynamicSnapPoints'; 24 | export { useBottomSheetGestureHandlers } from './useBottomSheetGestureHandlers'; 25 | -------------------------------------------------------------------------------- /example/app/src/App.tsx: -------------------------------------------------------------------------------- 1 | import React, { useMemo } from 'react'; 2 | import { StyleSheet } from 'react-native'; 3 | import { ShowcaseApp } from '@gorhom/showcase-template'; 4 | import { screens as defaultScreens } from './screens'; 5 | import { version, description } from '../../../package.json'; 6 | import { GestureHandlerRootView } from 'react-native-gesture-handler'; 7 | 8 | const author = { 9 | username: 'Mo Gorhom', 10 | url: 'https://gorhom.dev', 11 | }; 12 | 13 | interface AppProps { 14 | screens?: any[]; 15 | } 16 | 17 | export const App = ({ screens: providedScreens }: AppProps) => { 18 | const screens = useMemo( 19 | () => [...defaultScreens, ...(providedScreens ? providedScreens : [])], 20 | [providedScreens] 21 | ); 22 | return ( 23 | 24 | 31 | 32 | ); 33 | }; 34 | 35 | const styles = StyleSheet.create({ 36 | container: { 37 | flex: 1, 38 | flexGrow: 1, 39 | }, 40 | }); 41 | -------------------------------------------------------------------------------- /example/bare/src/components/blurredBackground/BlurredBackground.tsx: -------------------------------------------------------------------------------- 1 | import React, { useMemo } from 'react'; 2 | import { Platform, StyleSheet, View } from 'react-native'; 3 | import { BlurView } from '@react-native-community/blur'; 4 | import { useShowcaseTheme } from '@gorhom/showcase-template'; 5 | 6 | const BlurredBackground = () => { 7 | const { colors } = useShowcaseTheme(); 8 | const containerStyle = useMemo( 9 | () => [ 10 | styles.container, 11 | { 12 | backgroundColor: colors.background, 13 | opacity: 0.95, 14 | }, 15 | ], 16 | [colors.background] 17 | ); 18 | return Platform.OS === 'ios' ? ( 19 | 20 | 21 | 22 | ) : ( 23 | 24 | ); 25 | }; 26 | 27 | const styles = StyleSheet.create({ 28 | blurView: { 29 | ...StyleSheet.absoluteFillObject, 30 | }, 31 | container: { 32 | ...StyleSheet.absoluteFillObject, 33 | borderTopLeftRadius: 10, 34 | borderTopRightRadius: 10, 35 | overflow: 'hidden', 36 | }, 37 | }); 38 | 39 | export default BlurredBackground; 40 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | name: Mark stale issues and pull requests 2 | 3 | on: 4 | schedule: 5 | - cron: '39 9 * * *' 6 | 7 | jobs: 8 | stale: 9 | 10 | runs-on: ubuntu-latest 11 | permissions: 12 | issues: write 13 | pull-requests: write 14 | 15 | steps: 16 | - uses: actions/stale@v5.0.0 17 | with: 18 | repo-token: ${{ secrets.GITHUB_TOKEN }} 19 | stale-issue-message: 'This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.' 20 | stale-pr-message: 'This PR is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 10 days.' 21 | close-issue-message: 'This issue was closed because it has been stalled for 5 days with no activity.' 22 | stale-issue-label: 'no-issue-activity' 23 | stale-pr-label: 'no-pr-activity' 24 | days-before-stale: 30 25 | days-before-pr-stale: 30 26 | days-before-close: 5 27 | days-before-pr-close: 10 28 | exempt-assignees: 'gorhom' 29 | exempt-issue-labels: 'sponsor' 30 | exempt-pr-labels: 'sponsor' 31 | -------------------------------------------------------------------------------- /src/components/bottomSheetScrollable/BottomSheetSectionList.tsx: -------------------------------------------------------------------------------- 1 | import { memo } from 'react'; 2 | import { 3 | DefaultSectionT, 4 | SectionList as RNSectionList, 5 | SectionListProps as RNSectionListProps, 6 | } from 'react-native'; 7 | import Animated from 'react-native-reanimated'; 8 | import { SCROLLABLE_TYPE } from '../../constants'; 9 | import { createBottomSheetScrollableComponent } from './createBottomSheetScrollableComponent'; 10 | import type { 11 | BottomSheetSectionListMethods, 12 | BottomSheetSectionListProps, 13 | } from './types'; 14 | 15 | const AnimatedSectionList = 16 | Animated.createAnimatedComponent>(RNSectionList); 17 | 18 | const BottomSheetSectionListComponent = createBottomSheetScrollableComponent< 19 | BottomSheetSectionListMethods, 20 | BottomSheetSectionListProps 21 | >(SCROLLABLE_TYPE.SECTIONLIST, AnimatedSectionList); 22 | 23 | const BottomSheetSectionList = memo(BottomSheetSectionListComponent); 24 | BottomSheetSectionList.displayName = 'BottomSheetSectionList'; 25 | 26 | export default BottomSheetSectionList as ( 27 | props: BottomSheetSectionListProps 28 | ) => ReturnType; 29 | -------------------------------------------------------------------------------- /example/bare/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 13 | 20 | 21 | 22 | 23 | 24 | 25 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/components/bottomSheetScrollable/BottomSheetVirtualizedList.tsx: -------------------------------------------------------------------------------- 1 | import { memo } from 'react'; 2 | import { 3 | VirtualizedList as RNVirtualizedList, 4 | VirtualizedListProps as RNVirtualizedListProps, 5 | } from 'react-native'; 6 | import Animated from 'react-native-reanimated'; 7 | import { SCROLLABLE_TYPE } from '../../constants'; 8 | import { createBottomSheetScrollableComponent } from './createBottomSheetScrollableComponent'; 9 | import type { 10 | BottomSheetVirtualizedListMethods, 11 | BottomSheetVirtualizedListProps, 12 | } from './types'; 13 | 14 | const AnimatedVirtualizedList = 15 | Animated.createAnimatedComponent>( 16 | RNVirtualizedList 17 | ); 18 | 19 | const BottomSheetVirtualizedListComponent = 20 | createBottomSheetScrollableComponent< 21 | BottomSheetVirtualizedListMethods, 22 | BottomSheetVirtualizedListProps 23 | >(SCROLLABLE_TYPE.VIRTUALIZEDLIST, AnimatedVirtualizedList); 24 | 25 | const BottomSheetVirtualizedList = memo(BottomSheetVirtualizedListComponent); 26 | BottomSheetVirtualizedList.displayName = 'BottomSheetVirtualizedList'; 27 | 28 | export default BottomSheetVirtualizedList as ( 29 | props: BottomSheetVirtualizedListProps 30 | ) => ReturnType; 31 | -------------------------------------------------------------------------------- /example/app/src/components/customBackground/CustomBackground.tsx: -------------------------------------------------------------------------------- 1 | import React, { memo, useMemo } from 'react'; 2 | import { StyleSheet } from 'react-native'; 3 | import { BottomSheetBackgroundProps } from '@gorhom/bottom-sheet'; 4 | import Animated, { 5 | useAnimatedStyle, 6 | interpolateColor, 7 | } from 'react-native-reanimated'; 8 | 9 | interface CustomBackgroundProps extends BottomSheetBackgroundProps {} 10 | 11 | const CustomBackgroundComponent: React.FC = ({ 12 | style, 13 | animatedIndex, 14 | }) => { 15 | //#region styles 16 | const containerAnimatedStyle = useAnimatedStyle(() => ({ 17 | // @ts-ignore 18 | backgroundColor: interpolateColor( 19 | animatedIndex.value, 20 | [0, 1], 21 | ['#ffffff', '#a8b5eb'] 22 | ), 23 | })); 24 | const containerStyle = useMemo( 25 | () => [styles.container, style, containerAnimatedStyle], 26 | [style, containerAnimatedStyle] 27 | ); 28 | //#endregion 29 | 30 | // render 31 | return ; 32 | }; 33 | 34 | export const CustomBackground = memo(CustomBackgroundComponent); 35 | 36 | const styles = StyleSheet.create({ 37 | container: { 38 | borderTopLeftRadius: 20, 39 | borderTopRightRadius: 20, 40 | backgroundColor: '#fff', 41 | }, 42 | }); 43 | -------------------------------------------------------------------------------- /example/bare/android/app/src/main/jni/MainApplicationTurboModuleManagerDelegate.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | namespace facebook { 8 | namespace react { 9 | 10 | class MainApplicationTurboModuleManagerDelegate 11 | : public jni::HybridClass< 12 | MainApplicationTurboModuleManagerDelegate, 13 | TurboModuleManagerDelegate> { 14 | public: 15 | // Adapt it to the package you used for your Java class. 16 | static constexpr auto kJavaDescriptor = 17 | "Ldev/gorhom/bottomsheet/modules/MainApplicationTurboModuleManagerDelegate;"; 18 | 19 | static jni::local_ref initHybrid(jni::alias_ref); 20 | 21 | static void registerNatives(); 22 | 23 | std::shared_ptr getTurboModule( 24 | const std::string name, 25 | const std::shared_ptr jsInvoker) override; 26 | std::shared_ptr getTurboModule( 27 | const std::string name, 28 | const JavaTurboModule::InitParams ¶ms) override; 29 | 30 | /** 31 | * Test-only method. Allows user to verify whether a TurboModule can be 32 | * created by instances of this class. 33 | */ 34 | bool canCreateTurboModule(std::string name); 35 | }; 36 | 37 | } // namespace react 38 | } // namespace facebook -------------------------------------------------------------------------------- /src/components/bottomSheetDebugView/ReText.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { TextProps as RNTextProps, TextInput } from 'react-native'; 3 | import Animated, { 4 | useAnimatedProps, 5 | useDerivedValue, 6 | } from 'react-native-reanimated'; 7 | 8 | interface TextProps { 9 | text: string; 10 | value: Animated.SharedValue | number; 11 | style?: Animated.AnimateProps['style']; 12 | } 13 | 14 | const AnimatedTextInput = Animated.createAnimatedComponent(TextInput); 15 | 16 | const ReText = (props: TextProps) => { 17 | const { text, value: _providedValue, style } = { style: {}, ...props }; 18 | const providedValue = useDerivedValue(() => 19 | typeof _providedValue === 'number' 20 | ? _providedValue 21 | : typeof _providedValue.value === 'number' 22 | ? _providedValue.value.toFixed(2) 23 | : _providedValue.value 24 | ); 25 | const animatedProps = useAnimatedProps(() => { 26 | return { 27 | text: `${text}: ${providedValue.value}`, 28 | }; 29 | }, [providedValue]); 30 | return ( 31 | 39 | ); 40 | }; 41 | 42 | export default ReText; 43 | -------------------------------------------------------------------------------- /example/app/src/components/headerHandle/HeaderHandle.tsx: -------------------------------------------------------------------------------- 1 | import React, { memo } from 'react'; 2 | import { StyleSheet, Text } from 'react-native'; 3 | import { 4 | BottomSheetHandle, 5 | BottomSheetHandleProps, 6 | } from '@gorhom/bottom-sheet'; 7 | 8 | interface HeaderHandleProps extends BottomSheetHandleProps { 9 | children?: string | React.ReactNode | React.ReactNode[]; 10 | } 11 | 12 | const HeaderHandleComponent = ({ children, ...rest }: HeaderHandleProps) => { 13 | return ( 14 | 19 | {typeof children === 'string' ? ( 20 | {children} 21 | ) : ( 22 | children 23 | )} 24 | 25 | ); 26 | }; 27 | 28 | const styles = StyleSheet.create({ 29 | container: { 30 | paddingBottom: 12, 31 | paddingHorizontal: 16, 32 | borderBottomWidth: 1, 33 | borderBottomColor: 'rgba(0,0,0,0.075)', 34 | zIndex: 99999, 35 | }, 36 | title: { 37 | marginTop: 16, 38 | fontSize: 20, 39 | lineHeight: 20, 40 | textAlign: 'center', 41 | fontWeight: 'bold', 42 | color: 'black', 43 | }, 44 | indicator: { 45 | height: 4, 46 | opacity: 0.5, 47 | }, 48 | }); 49 | 50 | export const HeaderHandle = memo(HeaderHandleComponent); 51 | -------------------------------------------------------------------------------- /example/bare/android/app/src/main/java/dev/gorhom/bottomsheet/components/MainComponentsRegistry.java: -------------------------------------------------------------------------------- 1 | package dev.gorhom.bottomsheet.components; 2 | 3 | import com.facebook.jni.HybridData; 4 | import com.facebook.proguard.annotations.DoNotStrip; 5 | import com.facebook.react.fabric.ComponentFactory; 6 | import com.facebook.soloader.SoLoader; 7 | 8 | /** 9 | * Class responsible to load the custom Fabric Components. This class has native methods and needs a 10 | * corresponding C++ implementation/header file to work correctly (already placed inside the jni/ 11 | * folder for you). 12 | * 13 | *

Please note that this class is used ONLY if you opt-in for the New Architecture (see the 14 | * `newArchEnabled` property). Is ignored otherwise. 15 | */ 16 | @DoNotStrip 17 | public class MainComponentsRegistry { 18 | static { 19 | SoLoader.loadLibrary("fabricjni"); 20 | } 21 | 22 | @DoNotStrip 23 | private final HybridData mHybridData; 24 | 25 | @DoNotStrip 26 | private native HybridData initHybrid(ComponentFactory componentFactory); 27 | 28 | @DoNotStrip 29 | private MainComponentsRegistry(ComponentFactory componentFactory) { 30 | mHybridData = initHybrid(componentFactory); 31 | } 32 | 33 | @DoNotStrip 34 | public static MainComponentsRegistry register(ComponentFactory componentFactory) { 35 | return new MainComponentsRegistry(componentFactory); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/components/bottomSheetBackgroundContainer/BottomSheetBackgroundContainer.tsx: -------------------------------------------------------------------------------- 1 | import React, { memo, useMemo } from 'react'; 2 | import BottomSheetBackground from '../bottomSheetBackground'; 3 | import type { BottomSheetBackgroundContainerProps } from './types'; 4 | import { styles } from './styles'; 5 | import { StyleSheet } from 'react-native'; 6 | 7 | const BottomSheetBackgroundContainerComponent = ({ 8 | animatedIndex, 9 | animatedPosition, 10 | backgroundComponent: _providedBackgroundComponent, 11 | backgroundStyle: _providedBackgroundStyle, 12 | }: BottomSheetBackgroundContainerProps) => { 13 | const BackgroundComponent = 14 | _providedBackgroundComponent || BottomSheetBackground; 15 | 16 | const backgroundStyle = useMemo( 17 | () => StyleSheet.flatten([styles.container, _providedBackgroundStyle]), 18 | [_providedBackgroundStyle] 19 | ); 20 | 21 | return _providedBackgroundComponent === null ? null : ( 22 | 28 | ); 29 | }; 30 | 31 | const BottomSheetBackgroundContainer = memo( 32 | BottomSheetBackgroundContainerComponent 33 | ); 34 | BottomSheetBackgroundContainer.displayName = 'BottomSheetBackgroundContainer'; 35 | 36 | export default BottomSheetBackgroundContainer; 37 | -------------------------------------------------------------------------------- /example/bare/src/screens/integrations/NativeScreensExample.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { View, StyleSheet, Platform } from 'react-native'; 3 | import { useNavigation } from '@react-navigation/native'; 4 | import { createNativeStackNavigator } from '@react-navigation/native-stack'; 5 | import { 6 | ModalBackdropExample, 7 | Button, 8 | withModalProvider, 9 | } from '@gorhom/bottom-sheet-example-app'; 10 | 11 | const RootScreen = () => { 12 | const { navigate } = useNavigation(); 13 | return ( 14 | 15 |