6 | >({ value: 0 });
7 |
8 | export const GestureTranslationProvider = gestureTranslationContext.Provider;
9 |
10 | export const useGestureTranslationY = () => {
11 | return useContext(gestureTranslationContext);
12 | };
13 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/app/src/utilities/createMockData.ts:
--------------------------------------------------------------------------------
1 | import Faker from 'faker';
2 | import { Dimensions } from 'react-native';
3 | import type { Contact, Location } from '../types';
4 |
5 | const { width: SCREEN_WIDTH } = Dimensions.get('screen');
6 |
7 | export const createContactListMockData = (count: number = 20): Contact[] => {
8 | return new Array(count).fill(0).map(() => ({
9 | name: `${Faker.name.firstName()} ${Faker.name.lastName()}`,
10 | address: `${Faker.address.city()}, ${Faker.address.country()}`,
11 | jobTitle: Faker.name.jobTitle(),
12 | }));
13 | };
14 |
15 | export const createContactSectionsMockData = (count: number = 20) => {
16 | return new Array(Math.round(count / 4)).fill(0).map(() => ({
17 | title: Faker.address.country(),
18 | data: new Array(Math.round(count / 4)).fill(0).map(() => ({
19 | name: `${Faker.name.firstName()} ${Faker.name.lastName()}`,
20 | address: `${Faker.address.city()}, ${Faker.address.country()}`,
21 | jobTitle: Faker.name.jobTitle(),
22 | })),
23 | }));
24 | };
25 |
26 | export const createLocationListMockData = (count: number = 50): Location[] => {
27 | return [
28 | {
29 | id: 'ams',
30 | name: 'Amsterdam',
31 | address: 'North Holland, Netherlands',
32 | photos: [
33 | 'https://www.infocusclinical.com/wp-content/uploads/2020/02/summer-amsterdam-FP.jpg',
34 | 'https://images.theconversation.com/files/162459/original/image-20170325-12162-1tfrmbb.jpg?ixlib=rb-1.1.0&q=45&auto=format&w=200&fit=clip',
35 | 'https://www.kevinandamanda.com/wp-content/uploads/2014/09/amsterdam-2014-03.jpg',
36 | 'https://specials-images.forbesimg.com/imageserve/5de4a1db755ebf0006fbea42/960x0.jpg?cropX1=0&cropX2=2121&cropY1=0&cropY2=1414',
37 | ],
38 | },
39 | ...new Array(count).fill(0).map((_, index) => ({
40 | id: Faker.random.alphaNumeric(6),
41 | name: `${Faker.address.city()}`,
42 | address: `${Faker.address.state()}, ${Faker.address.country()}`,
43 | photos: Array(5)
44 | .fill(0)
45 | .map((__, _index) => Faker.image.city(SCREEN_WIDTH + index + _index)),
46 | })),
47 | ];
48 | };
49 |
--------------------------------------------------------------------------------
/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/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/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/android/.settings/org.eclipse.buildship.core.prefs:
--------------------------------------------------------------------------------
1 | connection.project.dir=
2 | eclipse.preferences.version=1
3 |
--------------------------------------------------------------------------------
/example/bare/android/app/debug.keystore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/discord/react-native-bottom-sheet/2b37e067aa027f408b32d50cc2636d4093bcacfc/example/bare/android/app/debug.keystore
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/example/bare/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
11 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/example/bare/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
13 |
20 |
21 |
22 |
23 |
24 |
25 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/example/bare/android/app/src/main/ic_launcher-playstore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/discord/react-native-bottom-sheet/2b37e067aa027f408b32d50cc2636d4093bcacfc/example/bare/android/app/src/main/ic_launcher-playstore.png
--------------------------------------------------------------------------------
/example/bare/android/app/src/main/java/dev/gorhom/bottomsheet/MainActivity.java:
--------------------------------------------------------------------------------
1 | package dev.gorhom.bottomsheet;
2 |
3 | import android.os.Bundle;
4 |
5 | import com.facebook.react.ReactActivity;
6 | import com.facebook.react.ReactActivityDelegate;
7 | import com.facebook.react.ReactRootView;
8 |
9 | public class MainActivity extends ReactActivity {
10 |
11 | @Override
12 | protected void onCreate(Bundle savedInstanceState) {
13 | super.onCreate(null);
14 | }
15 |
16 | /**
17 | * Returns the name of the main component registered from JavaScript. This is used to schedule
18 | * rendering of the component.
19 | */
20 | @Override
21 | protected String getMainComponentName() {
22 | return "BottomSheetExample";
23 | }
24 |
25 | @Override
26 | protected ReactActivityDelegate createReactActivityDelegate() {
27 | return new MainActivityDelegate(this, getMainComponentName());
28 | }
29 |
30 | public static class MainActivityDelegate extends ReactActivityDelegate {
31 | public MainActivityDelegate(ReactActivity activity, String mainComponentName) {
32 | super(activity, mainComponentName);
33 | }
34 |
35 | @Override
36 | protected ReactRootView createRootView() {
37 | ReactRootView reactRootView = new ReactRootView(getContext());
38 | // If you opted-in for the New Architecture, we enable the Fabric Renderer.
39 | reactRootView.setIsFabric(BuildConfig.IS_NEW_ARCHITECTURE_ENABLED);
40 | return reactRootView;
41 | }
42 |
43 | @Override
44 | protected boolean isConcurrentRootEnabled() {
45 | // If you opted-in for the New Architecture, we enable Concurrent Root (i.e. React 18).
46 | // More on this on https://reactjs.org/blog/2022/03/29/react-v18.html
47 | return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
48 | }
49 | }
50 | }
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 |
--------------------------------------------------------------------------------
/example/bare/android/app/src/main/java/dev/gorhom/bottomsheet/modules/MainApplicationTurboModuleManagerDelegate.java:
--------------------------------------------------------------------------------
1 | package dev.gorhom.bottomsheet.modules;
2 |
3 | import com.facebook.jni.HybridData;
4 | import com.facebook.react.ReactPackage;
5 | import com.facebook.react.ReactPackageTurboModuleManagerDelegate;
6 | import com.facebook.react.bridge.ReactApplicationContext;
7 | import com.facebook.soloader.SoLoader;
8 |
9 | import java.util.List;
10 |
11 | /**
12 | * Class responsible to load the TurboModules. This class has native methods and needs a
13 | * corresponding C++ implementation/header file to work correctly (already placed inside the jni/
14 | * folder for you).
15 | *
16 | *
Please note that this class is used ONLY if you opt-in for the New Architecture (see the
17 | * `newArchEnabled` property). Is ignored otherwise.
18 | */
19 | public class MainApplicationTurboModuleManagerDelegate
20 | extends ReactPackageTurboModuleManagerDelegate {
21 |
22 | private static volatile boolean sIsSoLibraryLoaded;
23 |
24 | protected MainApplicationTurboModuleManagerDelegate(
25 | ReactApplicationContext reactApplicationContext, List packages) {
26 | super(reactApplicationContext, packages);
27 | }
28 |
29 | protected native HybridData initHybrid();
30 |
31 | native boolean canCreateTurboModule(String moduleName);
32 |
33 | public static class Builder extends ReactPackageTurboModuleManagerDelegate.Builder {
34 | protected MainApplicationTurboModuleManagerDelegate build(
35 | ReactApplicationContext context, List packages) {
36 | return new MainApplicationTurboModuleManagerDelegate(context, packages);
37 | }
38 | }
39 |
40 | @Override
41 | protected synchronized void maybeLoadOtherSoLibraries() {
42 | if (!sIsSoLibraryLoaded) {
43 | // If you change the name of your application .so file in the Android.mk file,
44 | // make sure you update the name here as well.
45 | SoLoader.loadLibrary("bottomsheet_appmodules");
46 | sIsSoLibraryLoaded = true;
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/example/bare/android/app/src/main/jni/Android.mk:
--------------------------------------------------------------------------------
1 | THIS_DIR := $(call my-dir)
2 |
3 | include $(REACT_ANDROID_DIR)/Android-prebuilt.mk
4 |
5 | # If you wish to add a custom TurboModule or Fabric component in your app you
6 | # will have to include the following autogenerated makefile.
7 | # include $(GENERATED_SRC_DIR)/codegen/jni/Android.mk
8 | include $(CLEAR_VARS)
9 |
10 | LOCAL_PATH := $(THIS_DIR)
11 |
12 | # You can customize the name of your application .so file here.
13 | LOCAL_MODULE := bottomsheet_appmodules
14 |
15 | LOCAL_C_INCLUDES := $(LOCAL_PATH)
16 | LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp)
17 | LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)
18 |
19 | # If you wish to add a custom TurboModule or Fabric component in your app you
20 | # will have to uncomment those lines to include the generated source
21 | # files from the codegen (placed in $(GENERATED_SRC_DIR)/codegen/jni)
22 | #
23 | # LOCAL_C_INCLUDES += $(GENERATED_SRC_DIR)/codegen/jni
24 | # LOCAL_SRC_FILES += $(wildcard $(GENERATED_SRC_DIR)/codegen/jni/*.cpp)
25 | # LOCAL_EXPORT_C_INCLUDES += $(GENERATED_SRC_DIR)/codegen/jni
26 |
27 | # Here you should add any native library you wish to depend on.
28 | LOCAL_SHARED_LIBRARIES := \
29 | libfabricjni \
30 | libfbjni \
31 | libfolly_runtime \
32 | libglog \
33 | libjsi \
34 | libreact_codegen_rncore \
35 | libreact_debug \
36 | libreact_nativemodule_core \
37 | libreact_render_componentregistry \
38 | libreact_render_core \
39 | libreact_render_debug \
40 | libreact_render_graphics \
41 | librrc_view \
42 | libruntimeexecutor \
43 | libturbomodulejsijni \
44 | libyoga
45 |
46 | LOCAL_CFLAGS := -DLOG_TAG=\"ReactNative\" -fexceptions -frtti -std=c++17 -Wall
47 |
48 | include $(BUILD_SHARED_LIBRARY)
--------------------------------------------------------------------------------
/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
--------------------------------------------------------------------------------
/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
--------------------------------------------------------------------------------
/example/bare/android/app/src/main/jni/MainApplicationTurboModuleManagerDelegate.cpp:
--------------------------------------------------------------------------------
1 | #include "MainApplicationTurboModuleManagerDelegate.h"
2 | #include "MainApplicationModuleProvider.h"
3 |
4 | namespace facebook {
5 | namespace react {
6 |
7 | jni::local_ref
8 | MainApplicationTurboModuleManagerDelegate::initHybrid(
9 | jni::alias_ref) {
10 | return makeCxxInstance();
11 | }
12 |
13 | void MainApplicationTurboModuleManagerDelegate::registerNatives() {
14 | registerHybrid({
15 | makeNativeMethod(
16 | "initHybrid", MainApplicationTurboModuleManagerDelegate::initHybrid),
17 | makeNativeMethod(
18 | "canCreateTurboModule",
19 | MainApplicationTurboModuleManagerDelegate::canCreateTurboModule),
20 | });
21 | }
22 |
23 | std::shared_ptr
24 | MainApplicationTurboModuleManagerDelegate::getTurboModule(
25 | const std::string name,
26 | const std::shared_ptr jsInvoker) {
27 | // Not implemented yet: provide pure-C++ NativeModules here.
28 | return nullptr;
29 | }
30 |
31 | std::shared_ptr
32 | MainApplicationTurboModuleManagerDelegate::getTurboModule(
33 | const std::string name,
34 | const JavaTurboModule::InitParams ¶ms) {
35 | return MainApplicationModuleProvider(name, params);
36 | }
37 |
38 | bool MainApplicationTurboModuleManagerDelegate::canCreateTurboModule(
39 | std::string name) {
40 | return getTurboModule(name, nullptr) != nullptr ||
41 | getTurboModule(name, {.moduleName = name}) != nullptr;
42 | }
43 |
44 | } // namespace react
45 | } // namespace facebook
--------------------------------------------------------------------------------
/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
--------------------------------------------------------------------------------
/example/bare/android/app/src/main/jni/MainComponentsRegistry.cpp:
--------------------------------------------------------------------------------
1 | #include "MainComponentsRegistry.h"
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 | namespace facebook {
9 | namespace react {
10 |
11 | MainComponentsRegistry::MainComponentsRegistry(ComponentFactory *delegate) {}
12 |
13 | std::shared_ptr
14 | MainComponentsRegistry::sharedProviderRegistry() {
15 | auto providerRegistry = CoreComponentsRegistry::sharedProviderRegistry();
16 |
17 | // Custom Fabric Components go here. You can register custom
18 | // components coming from your App or from 3rd party libraries here.
19 | //
20 | // providerRegistry->add(concreteComponentDescriptorProvider<
21 | // AocViewerComponentDescriptor>());
22 | return providerRegistry;
23 | }
24 |
25 | jni::local_ref
26 | MainComponentsRegistry::initHybrid(
27 | jni::alias_ref,
28 | ComponentFactory *delegate) {
29 | auto instance = makeCxxInstance(delegate);
30 |
31 | auto buildRegistryFunction =
32 | [](EventDispatcher::Weak const &eventDispatcher,
33 | ContextContainer::Shared const &contextContainer)
34 | -> ComponentDescriptorRegistry::Shared {
35 | auto registry = MainComponentsRegistry::sharedProviderRegistry()
36 | ->createComponentDescriptorRegistry(
37 | {eventDispatcher, contextContainer});
38 |
39 | auto mutableRegistry =
40 | std::const_pointer_cast(registry);
41 |
42 | mutableRegistry->setFallbackComponentDescriptor(
43 | std::make_shared(
44 | ComponentDescriptorParameters{
45 | eventDispatcher, contextContainer, nullptr}));
46 |
47 | return registry;
48 | };
49 |
50 | delegate->buildRegistryFunction = buildRegistryFunction;
51 | return instance;
52 | }
53 |
54 | void MainComponentsRegistry::registerNatives() {
55 | registerHybrid({
56 | makeNativeMethod("initHybrid", MainComponentsRegistry::initHybrid),
57 | });
58 | }
59 |
60 | } // namespace react
61 | } // namespace facebook
--------------------------------------------------------------------------------
/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
--------------------------------------------------------------------------------
/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 | }
--------------------------------------------------------------------------------
/example/bare/android/app/src/main/res/drawable/rn_edit_text_material.xml:
--------------------------------------------------------------------------------
1 |
2 |
16 |
21 |
22 |
23 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/example/bare/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/example/bare/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/example/bare/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/discord/react-native-bottom-sheet/2b37e067aa027f408b32d50cc2636d4093bcacfc/example/bare/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/bare/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/discord/react-native-bottom-sheet/2b37e067aa027f408b32d50cc2636d4093bcacfc/example/bare/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/example/bare/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/discord/react-native-bottom-sheet/2b37e067aa027f408b32d50cc2636d4093bcacfc/example/bare/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/example/bare/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/discord/react-native-bottom-sheet/2b37e067aa027f408b32d50cc2636d4093bcacfc/example/bare/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/bare/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/discord/react-native-bottom-sheet/2b37e067aa027f408b32d50cc2636d4093bcacfc/example/bare/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/example/bare/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/discord/react-native-bottom-sheet/2b37e067aa027f408b32d50cc2636d4093bcacfc/example/bare/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/example/bare/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/discord/react-native-bottom-sheet/2b37e067aa027f408b32d50cc2636d4093bcacfc/example/bare/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/bare/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/discord/react-native-bottom-sheet/2b37e067aa027f408b32d50cc2636d4093bcacfc/example/bare/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/example/bare/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/discord/react-native-bottom-sheet/2b37e067aa027f408b32d50cc2636d4093bcacfc/example/bare/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/example/bare/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/discord/react-native-bottom-sheet/2b37e067aa027f408b32d50cc2636d4093bcacfc/example/bare/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/bare/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/discord/react-native-bottom-sheet/2b37e067aa027f408b32d50cc2636d4093bcacfc/example/bare/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/example/bare/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/discord/react-native-bottom-sheet/2b37e067aa027f408b32d50cc2636d4093bcacfc/example/bare/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/example/bare/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/discord/react-native-bottom-sheet/2b37e067aa027f408b32d50cc2636d4093bcacfc/example/bare/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/bare/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/discord/react-native-bottom-sheet/2b37e067aa027f408b32d50cc2636d4093bcacfc/example/bare/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/example/bare/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/discord/react-native-bottom-sheet/2b37e067aa027f408b32d50cc2636d4093bcacfc/example/bare/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.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/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | BottomSheet Example
3 |
4 |
--------------------------------------------------------------------------------
/example/bare/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/example/bare/android/build.gradle:
--------------------------------------------------------------------------------
1 | import org.apache.tools.ant.taskdefs.condition.Os
2 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
3 |
4 | buildscript {
5 | ext {
6 | buildToolsVersion = "31.0.0"
7 | minSdkVersion = 21
8 | compileSdkVersion = 31
9 | targetSdkVersion = 31
10 |
11 | if (System.properties['os.arch'] == "aarch64") {
12 | // For M1 Users we need to use the NDK 24 which added support for aarch64
13 | ndkVersion = "24.0.8215888"
14 | } else {
15 | // Otherwise we default to the side-by-side NDK version from AGP.
16 | ndkVersion = "21.4.7075529"
17 | }
18 | }
19 | repositories {
20 | google()
21 | mavenCentral()
22 | }
23 | dependencies {
24 | classpath("com.android.tools.build:gradle:7.1.1")
25 | classpath("com.facebook.react:react-native-gradle-plugin")
26 | classpath("de.undercouch:gradle-download-task:5.0.1")
27 |
28 | // NOTE: Do not place your application dependencies here; they belong
29 | // in the individual module build.gradle files
30 | }
31 | }
32 |
33 | allprojects {
34 | repositories {
35 | maven {
36 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
37 | url("$rootDir/../node_modules/react-native/android")
38 | }
39 | maven {
40 | // Android JSC is installed from npm
41 | url("$rootDir/../node_modules/jsc-android/dist")
42 | }
43 | mavenCentral {
44 | // We don't want to fetch react-native from Maven Central as there are
45 | // older versions over there.
46 | content {
47 | excludeGroup "com.facebook.react"
48 | }
49 | }
50 | google()
51 | maven { url 'https://www.jitpack.io' }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/example/bare/android/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx512m -XX:MaxMetaspaceSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 | org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m
15 |
16 | # When configured, Gradle will run in incubating parallel mode.
17 | # This option should only be used with decoupled projects. More details, visit
18 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
19 | # org.gradle.parallel=true
20 |
21 | android.useAndroidX=true
22 | android.enableJetifier=true
23 |
24 | FLIPPER_VERSION=0.125.0
25 |
26 | # Use this property to specify which architecture you want to build.
27 | # You can also override it from the CLI using
28 | # ./gradlew -PreactNativeArchitectures=x86_64
29 | reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64
30 | # Use this property to enable support to the new architecture.
31 | # This will allow you to use TurboModules and the Fabric render in
32 | # your application. You should enable this flag either if you want
33 | # to write custom TurboModules/Fabric components OR use libraries that
34 | # are providing them.
35 | newArchEnabled=false
--------------------------------------------------------------------------------
/example/bare/android/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/discord/react-native-bottom-sheet/2b37e067aa027f408b32d50cc2636d4093bcacfc/example/bare/android/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/example/bare/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "BottomSheetExample",
3 | "displayName": "BottomSheet Example"
4 | }
5 |
--------------------------------------------------------------------------------
/example/bare/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: ['module:metro-react-native-babel-preset'],
3 | plugins: ['react-native-reanimated/plugin'],
4 | };
5 |
--------------------------------------------------------------------------------
/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/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)
--------------------------------------------------------------------------------
/example/bare/ios/BottomSheetExample.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/example/bare/ios/BottomSheetExample.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/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/ios/BottomSheetExample/Images.xcassets/AppIcon.appiconset/100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/discord/react-native-bottom-sheet/2b37e067aa027f408b32d50cc2636d4093bcacfc/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/2b37e067aa027f408b32d50cc2636d4093bcacfc/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/2b37e067aa027f408b32d50cc2636d4093bcacfc/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/2b37e067aa027f408b32d50cc2636d4093bcacfc/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/2b37e067aa027f408b32d50cc2636d4093bcacfc/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/2b37e067aa027f408b32d50cc2636d4093bcacfc/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/2b37e067aa027f408b32d50cc2636d4093bcacfc/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/2b37e067aa027f408b32d50cc2636d4093bcacfc/example/bare/ios/BottomSheetExample/Images.xcassets/AppIcon.appiconset/180.png
--------------------------------------------------------------------------------
/example/bare/ios/BottomSheetExample/Images.xcassets/AppIcon.appiconset/20.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/discord/react-native-bottom-sheet/2b37e067aa027f408b32d50cc2636d4093bcacfc/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/2b37e067aa027f408b32d50cc2636d4093bcacfc/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/2b37e067aa027f408b32d50cc2636d4093bcacfc/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/2b37e067aa027f408b32d50cc2636d4093bcacfc/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/2b37e067aa027f408b32d50cc2636d4093bcacfc/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/2b37e067aa027f408b32d50cc2636d4093bcacfc/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/2b37e067aa027f408b32d50cc2636d4093bcacfc/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/2b37e067aa027f408b32d50cc2636d4093bcacfc/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/2b37e067aa027f408b32d50cc2636d4093bcacfc/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/2b37e067aa027f408b32d50cc2636d4093bcacfc/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/2b37e067aa027f408b32d50cc2636d4093bcacfc/example/bare/ios/BottomSheetExample/Images.xcassets/AppIcon.appiconset/87.png
--------------------------------------------------------------------------------
/example/bare/ios/BottomSheetExample/Images.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/example/bare/ios/BottomSheetExample/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | BottomSheet Example
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | 1
25 | LSRequiresIPhoneOS
26 |
27 | NSAppTransportSecurity
28 |
29 | NSExceptionDomains
30 |
31 | localhost
32 |
33 | NSExceptionAllowsInsecureHTTPLoads
34 |
35 |
36 |
37 |
38 | NSLocationWhenInUseUsageDescription
39 |
40 | UILaunchStoryboardName
41 | LaunchScreen
42 | UIRequiredDeviceCapabilities
43 |
44 | armv7
45 |
46 | UISupportedInterfaceOrientations
47 |
48 | UIInterfaceOrientationPortrait
49 | UIInterfaceOrientationLandscapeRight
50 | UIInterfaceOrientationPortraitUpsideDown
51 | UIInterfaceOrientationLandscapeLeft
52 |
53 | UIViewControllerBasedStatusBarAppearance
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/example/bare/metro.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const fs = require('fs');
3 | const exclusionList = require('metro-config/src/defaults/exclusionList');
4 | const escape = require('escape-string-regexp');
5 |
6 | const root = path.resolve(__dirname, '../..');
7 | const rootPak = JSON.parse(
8 | fs.readFileSync(path.join(root, 'package.json'), 'utf8')
9 | );
10 |
11 | const app = path.join(__dirname, '../app');
12 | const appPak = JSON.parse(
13 | fs.readFileSync(path.join(app, 'package.json'), 'utf8')
14 | );
15 |
16 | const modules = [
17 | '@babel/runtime',
18 | ...Object.keys({
19 | ...rootPak.dependencies,
20 | ...rootPak.peerDependencies,
21 | ...appPak.devDependencies,
22 | ...appPak.peerDependencies,
23 | }),
24 | ];
25 |
26 | module.exports = {
27 | projectRoot: __dirname,
28 | watchFolders: [root],
29 |
30 | resolver: {
31 | blacklistRE: exclusionList([
32 | new RegExp(`^${escape(path.join(root, 'node_modules'))}\\/.*$`),
33 | new RegExp(`^${escape(path.join(app, 'node_modules'))}\\/.*$`),
34 | ]),
35 |
36 | extraNodeModules: modules.reduce((acc, name) => {
37 | acc[name] = path.join(__dirname, 'node_modules', name);
38 | return acc;
39 | }, {}),
40 | },
41 |
42 | transformer: {
43 | getTransformOptions: async () => ({
44 | transform: {
45 | experimentalImportSupport: false,
46 | inlineRequires: true,
47 | },
48 | }),
49 | },
50 | };
51 |
--------------------------------------------------------------------------------
/example/bare/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@gorhom/bottom-sheet-example",
3 | "description": "Example app for @gorhom/bottom-sheet",
4 | "version": "0.0.1",
5 | "private": true,
6 | "scripts": {
7 | "android": "react-native run-android",
8 | "ios": "react-native run-ios",
9 | "start": "react-native start",
10 | "postinstall": "npx patch-package"
11 | },
12 | "dependencies": {
13 | "@gorhom/portal": "^1.0.13",
14 | "@gorhom/showcase-template": "^2.1.0",
15 | "@react-native-community/blur": "^3.6.0",
16 | "@react-native-community/masked-view": "0.1.11",
17 | "@react-navigation/bottom-tabs": "^6.0.9",
18 | "@react-navigation/elements": "^1.2.1",
19 | "@react-navigation/material-top-tabs": "^6.0.6",
20 | "@react-navigation/native": "^6.0.6",
21 | "@react-navigation/native-stack": "^6.2.5",
22 | "@react-navigation/stack": "^6.0.11",
23 | "faker": "^4.1.0",
24 | "nanoid": "^3.3.3",
25 | "react": "18.0.0",
26 | "react-native": "0.69.4",
27 | "react-native-gesture-handler": "^2.5.0",
28 | "react-native-maps": "^0.30.1",
29 | "react-native-pager-view": "^5.4.9",
30 | "react-native-reanimated": "^2.9.1",
31 | "react-native-redash": "^16.0.11",
32 | "react-native-safe-area-context": "4.2.4",
33 | "react-native-screens": "^3.15.0",
34 | "react-native-tab-view": "^3.1.1"
35 | },
36 | "devDependencies": {
37 | "@babel/core": "^7.18.0",
38 | "@babel/runtime": "^7.18.0",
39 | "@types/faker": "^4.1.12",
40 | "@types/react": "17.0.2",
41 | "@types/react-native": "^0.67.7",
42 | "metro-react-native-babel-preset": "^0.70.3",
43 | "typescript": "^4.2.4"
44 | },
45 | "resolutions": {
46 | "@babel/core": "^7.18.0",
47 | "@babel/runtime": "^7.18.0",
48 | "@types/react": "17.0.2",
49 | "@types/react-native": "0.67.7"
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/example/bare/patches/react-native-gesture-handler+1.10.3._patch:
--------------------------------------------------------------------------------
1 | diff --git a/node_modules/react-native-gesture-handler/android/lib/src/main/java/com/swmansion/gesturehandler/GestureHandlerOrchestrator.java b/node_modules/react-native-gesture-handler/android/lib/src/main/java/com/swmansion/gesturehandler/GestureHandlerOrchestrator.java
2 | index f08713b..14f7729 100644
3 | --- a/node_modules/react-native-gesture-handler/android/lib/src/main/java/com/swmansion/gesturehandler/GestureHandlerOrchestrator.java
4 | +++ b/node_modules/react-native-gesture-handler/android/lib/src/main/java/com/swmansion/gesturehandler/GestureHandlerOrchestrator.java
5 | @@ -13,6 +13,8 @@ import java.util.Comparator;
6 |
7 | import androidx.annotation.Nullable;
8 |
9 | +import com.swmansion.gesturehandler.react.RNGestureHandlerRootHelper;
10 | +
11 | public class GestureHandlerOrchestrator {
12 |
13 | // The limit doesn't necessarily need to exists, it was just simpler to implement it that way
14 | @@ -513,6 +515,7 @@ public class GestureHandlerOrchestrator {
15 | }
16 |
17 | private static boolean canRunSimultaneously(GestureHandler a, GestureHandler b) {
18 | +
19 | return a == b || a.shouldRecognizeSimultaneously(b) || b.shouldRecognizeSimultaneously(a);
20 | }
21 |
22 | @@ -533,6 +536,11 @@ public class GestureHandlerOrchestrator {
23 | // state, we delegate the decision to the implementation of GestureHandler#shouldBeCancelledBy
24 | return handler.shouldBeCancelledBy(other);
25 | }
26 | +
27 | + if (other instanceof RNGestureHandlerRootHelper.RootViewGestureHandler && handler instanceof PanGestureHandler) {
28 | + return false;
29 | + }
30 | +
31 | return true;
32 | }
33 |
34 | diff --git a/node_modules/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerRootHelper.java b/node_modules/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerRootHelper.java
35 | index 9bf0c8f..8d4e58c 100644
36 | --- a/node_modules/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerRootHelper.java
37 | +++ b/node_modules/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerRootHelper.java
38 | @@ -81,7 +81,7 @@ public class RNGestureHandlerRootHelper {
39 | return mRootView;
40 | }
41 |
42 | - private class RootViewGestureHandler extends GestureHandler {
43 | + public class RootViewGestureHandler extends GestureHandler {
44 | @Override
45 | protected void onHandle(MotionEvent event) {
46 | int currentState = getState();
47 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/example/bare/src/components/blurredBackground/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from './BlurredBackground';
2 |
--------------------------------------------------------------------------------
/example/bare/src/components/locationDetails/index.ts:
--------------------------------------------------------------------------------
1 | export { LocationDetails, LOCATION_DETAILS_HEIGHT } from './LocationDetails';
2 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/example/bare/src/components/locationDetailsHandle/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from './LocationDetailsHandle';
2 |
--------------------------------------------------------------------------------
/example/bare/src/components/locationItem/LocationItem.tsx:
--------------------------------------------------------------------------------
1 | import React, { useMemo, memo } from 'react';
2 | import { StyleSheet, View } from 'react-native';
3 | import { ShowcaseLabel, useShowcaseTheme } from '@gorhom/showcase-template';
4 |
5 | interface LocationItemProps {
6 | title: string;
7 | subTitle?: string;
8 | }
9 |
10 | const LocationItemComponent = ({ title, subTitle }: LocationItemProps) => {
11 | const { colors } = useShowcaseTheme();
12 | const separatorStyle = useMemo(
13 | () => [
14 | styles.separator,
15 | {
16 | backgroundColor: colors.border,
17 | },
18 | ],
19 | [colors.border]
20 | );
21 | // render
22 | return (
23 | <>
24 |
25 |
26 |
27 | {title}
28 | {subTitle && (
29 | {subTitle}
30 | )}
31 |
32 |
33 |
34 | >
35 | );
36 | };
37 |
38 | const styles = StyleSheet.create({
39 | container: {
40 | flexDirection: 'row',
41 | alignContent: 'center',
42 | alignItems: 'center',
43 | marginVertical: 12,
44 | },
45 | contentContainer: {
46 | flex: 1,
47 | alignSelf: 'center',
48 | marginLeft: 12,
49 | },
50 | thumbnail: {
51 | width: 32,
52 | height: 32,
53 | borderRadius: 32,
54 | backgroundColor: 'rgba(0, 0, 0, 0.25)',
55 | },
56 | title: {
57 | fontSize: 16,
58 | marginBottom: 4,
59 | textTransform: 'capitalize',
60 | },
61 | subtitle: {
62 | color: '#666',
63 | fontSize: 14,
64 | textTransform: 'capitalize',
65 | },
66 | separator: {
67 | flex: 1,
68 | height: 1,
69 | },
70 | });
71 |
72 | export const LocationItem = memo(LocationItemComponent);
73 |
--------------------------------------------------------------------------------
/example/bare/src/components/locationItem/index.ts:
--------------------------------------------------------------------------------
1 | export { LocationItem } from './LocationItem';
2 |
--------------------------------------------------------------------------------
/example/bare/src/components/weather/Weather.tsx:
--------------------------------------------------------------------------------
1 | import React, { useMemo } from 'react';
2 | import { Dimensions, StyleSheet } from 'react-native';
3 | import Animated, {
4 | Extrapolate,
5 | interpolate,
6 | useAnimatedStyle,
7 | } from 'react-native-reanimated';
8 | import { ShowcaseLabel, useShowcaseTheme } from '@gorhom/showcase-template';
9 | import { useSafeAreaInsets } from 'react-native-safe-area-context';
10 | import { SEARCH_HANDLE_HEIGHT } from '@gorhom/bottom-sheet-example-app';
11 | import { LOCATION_DETAILS_HEIGHT } from '../locationDetails';
12 |
13 | interface WeatherProps {
14 | animatedPosition: Animated.SharedValue;
15 | animatedIndex: Animated.SharedValue;
16 | }
17 |
18 | const { height: SCREEN_HEIGHT } = Dimensions.get('window');
19 |
20 | const Weather = ({ animatedIndex, animatedPosition }: WeatherProps) => {
21 | // hooks
22 | const { colors } = useShowcaseTheme();
23 | const { bottom: bottomSafeArea } = useSafeAreaInsets();
24 |
25 | // styles
26 | const lockedYPosition = useMemo(
27 | () =>
28 | SCREEN_HEIGHT -
29 | SEARCH_HANDLE_HEIGHT -
30 | LOCATION_DETAILS_HEIGHT -
31 | bottomSafeArea,
32 | [bottomSafeArea]
33 | );
34 | const containerAnimatedStyle = useAnimatedStyle(
35 | () => ({
36 | transform: [
37 | {
38 | translateY:
39 | animatedPosition.value > lockedYPosition
40 | ? animatedPosition.value - 24
41 | : lockedYPosition - 24,
42 | },
43 | {
44 | scale: interpolate(
45 | animatedIndex.value,
46 | [1, 1.25],
47 | [1, 0],
48 | Extrapolate.CLAMP
49 | ),
50 | },
51 | ],
52 | }),
53 | [lockedYPosition]
54 | );
55 | const containerStyle = useMemo(
56 | () => [
57 | styles.container,
58 | { backgroundColor: colors.secondaryCard },
59 | containerAnimatedStyle,
60 | ],
61 | [colors.secondaryCard, containerAnimatedStyle]
62 | );
63 | return (
64 |
65 | ☁️12°
66 |
67 | );
68 | };
69 |
70 | const styles = StyleSheet.create({
71 | container: {
72 | position: 'absolute',
73 | right: 12,
74 | top: 0,
75 | padding: 2,
76 | marginTop: 0,
77 | borderRadius: 4,
78 | },
79 | label: {
80 | fontSize: 16,
81 | lineHeight: 16,
82 | },
83 | });
84 |
85 | export default Weather;
86 |
--------------------------------------------------------------------------------
/example/bare/src/components/weather/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from './Weather';
2 |
--------------------------------------------------------------------------------
/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/src/screens/index.ts:
--------------------------------------------------------------------------------
1 | export { screens } from './screens';
2 |
--------------------------------------------------------------------------------
/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 |
21 | );
22 | };
23 |
24 | const styles = StyleSheet.create({
25 | container: {
26 | flex: 1,
27 | padding: 24,
28 | },
29 | });
30 |
31 | const NativeStack = createNativeStackNavigator();
32 |
33 | export default withModalProvider(() => (
34 |
35 |
40 |
48 |
49 | ));
50 |
--------------------------------------------------------------------------------
/example/bare/src/screens/integrations/ViewPagerExample.tsx:
--------------------------------------------------------------------------------
1 | import React, { useMemo } from 'react';
2 | import { View, Text, StyleSheet } from 'react-native';
3 | import { createMaterialTopTabNavigator } from '@react-navigation/material-top-tabs';
4 | import BottomSheet from '@gorhom/bottom-sheet';
5 | import { ContactList } from '@gorhom/bottom-sheet-example-app';
6 |
7 | const FirstRoute = () => {
8 | const snapPoints = useMemo(() => ['25%', '50%', '90%'], []);
9 |
10 | return (
11 |
12 |
18 |
19 |
20 |
21 | );
22 | };
23 |
24 | const SecondRoute = () => (
25 |
26 | 🙈
27 |
28 | );
29 |
30 | const Tab = createMaterialTopTabNavigator();
31 |
32 | const ViewPagerScreen = () => {
33 | return (
34 |
35 |
36 |
37 |
38 | );
39 | };
40 |
41 | const styles = StyleSheet.create({
42 | scene: {
43 | flex: 1,
44 | },
45 | firstScene: {
46 | backgroundColor: '#ff4081',
47 | },
48 | secondScene: {
49 | alignContent: 'center',
50 | alignItems: 'center',
51 | justifyContent: 'center',
52 | backgroundColor: '#673ab7',
53 | },
54 | emoji: {
55 | fontSize: 46,
56 | },
57 | });
58 |
59 | export default ViewPagerScreen;
60 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/src/utilities/createMockData.ts:
--------------------------------------------------------------------------------
1 | import Faker from 'faker';
2 | import { Dimensions } from 'react-native';
3 | import type { Contact, Location } from '../types';
4 |
5 | const { width: SCREEN_WIDTH } = Dimensions.get('screen');
6 |
7 | export const createContactListMockData = (count: number = 20): Contact[] => {
8 | return new Array(count).fill(0).map(() => ({
9 | name: `${Faker.name.firstName()} ${Faker.name.lastName()}`,
10 | address: `${Faker.address.city()}, ${Faker.address.country()}`,
11 | jobTitle: Faker.name.jobTitle(),
12 | }));
13 | };
14 |
15 | export const createContactSectionsMockData = (count: number = 20) => {
16 | return new Array(Math.round(count / 4)).fill(0).map(() => ({
17 | title: Faker.address.country(),
18 | data: new Array(Math.round(count / 4)).fill(0).map(() => ({
19 | name: `${Faker.name.firstName()} ${Faker.name.lastName()}`,
20 | address: `${Faker.address.city()}, ${Faker.address.country()}`,
21 | jobTitle: Faker.name.jobTitle(),
22 | })),
23 | }));
24 | };
25 |
26 | export const createLocationListMockData = (count: number = 50): Location[] => {
27 | return [
28 | {
29 | id: 'ams',
30 | name: 'Amsterdam',
31 | address: 'North Holland, Netherlands',
32 | photos: [
33 | 'https://www.infocusclinical.com/wp-content/uploads/2020/02/summer-amsterdam-FP.jpg',
34 | 'https://images.theconversation.com/files/162459/original/image-20170325-12162-1tfrmbb.jpg?ixlib=rb-1.1.0&q=45&auto=format&w=200&fit=clip',
35 | 'https://www.kevinandamanda.com/wp-content/uploads/2014/09/amsterdam-2014-03.jpg',
36 | 'https://specials-images.forbesimg.com/imageserve/5de4a1db755ebf0006fbea42/960x0.jpg?cropX1=0&cropX2=2121&cropY1=0&cropY2=1414',
37 | ],
38 | },
39 | ...new Array(count).fill(0).map((_, index) => ({
40 | id: Faker.random.alphaNumeric(6),
41 | name: `${Faker.address.city()}`,
42 | address: `${Faker.address.state()}, ${Faker.address.country()}`,
43 | photos: Array(5)
44 | .fill(0)
45 | .map((__, _index) => Faker.image.city(SCREEN_WIDTH + index + _index)),
46 | })),
47 | ];
48 | };
49 |
--------------------------------------------------------------------------------
/example/bare/src/utilities/index.ts:
--------------------------------------------------------------------------------
1 | export {
2 | createContactListMockData,
3 | createContactSectionsMockData,
4 | createLocationListMockData,
5 | } from './createMockData';
6 | export { transformOrigin } from './transformOrigin';
7 |
--------------------------------------------------------------------------------
/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/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/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 |
--------------------------------------------------------------------------------
/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/expo/assets/adaptive-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/discord/react-native-bottom-sheet/2b37e067aa027f408b32d50cc2636d4093bcacfc/example/expo/assets/adaptive-icon.png
--------------------------------------------------------------------------------
/example/expo/assets/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/discord/react-native-bottom-sheet/2b37e067aa027f408b32d50cc2636d4093bcacfc/example/expo/assets/favicon.png
--------------------------------------------------------------------------------
/example/expo/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/discord/react-native-bottom-sheet/2b37e067aa027f408b32d50cc2636d4093bcacfc/example/expo/assets/icon.png
--------------------------------------------------------------------------------
/example/expo/assets/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/discord/react-native-bottom-sheet/2b37e067aa027f408b32d50cc2636d4093bcacfc/example/expo/assets/splash.png
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/expo/metro.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const fs = require('fs');
3 | const exclusionList = require('metro-config/src/defaults/exclusionList');
4 | const escape = require('escape-string-regexp');
5 |
6 | const root = path.resolve(__dirname, '../..');
7 | const rootPak = JSON.parse(
8 | fs.readFileSync(path.join(root, 'package.json'), 'utf8')
9 | );
10 |
11 | const app = path.join(__dirname, '../app');
12 | const appPak = JSON.parse(
13 | fs.readFileSync(path.join(app, 'package.json'), 'utf8')
14 | );
15 |
16 | const modules = [
17 | '@babel/runtime',
18 | ...Object.keys({
19 | ...rootPak.dependencies,
20 | ...rootPak.peerDependencies,
21 | ...appPak.devDependencies,
22 | ...appPak.peerDependencies,
23 | }),
24 | ];
25 |
26 | module.exports = {
27 | projectRoot: __dirname,
28 | watchFolders: [root],
29 |
30 | // We need to make sure that only one version is loaded for peerDependencies
31 | // So we blacklist them at the root, and alias them to the versions in example's node_modules
32 | resolver: {
33 | blacklistRE: exclusionList([
34 | new RegExp(`^${escape(path.join(root, 'node_modules'))}\\/.*$`),
35 | new RegExp(`^${escape(path.join(app, 'node_modules'))}\\/.*$`),
36 | ]),
37 |
38 | extraNodeModules: modules.reduce((acc, name) => {
39 | acc[name] = path.join(__dirname, 'node_modules', name);
40 | return acc;
41 | }, {}),
42 | },
43 |
44 | transformer: {
45 | getTransformOptions: async () => ({
46 | transform: {
47 | experimentalImportSupport: false,
48 | inlineRequires: true,
49 | },
50 | }),
51 | },
52 | };
53 |
--------------------------------------------------------------------------------
/example/expo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@gorhom/bottom-sheet-example-expo",
3 | "description": "Example app for @gorhom/bottom-sheet",
4 | "version": "0.0.1",
5 | "private": true,
6 | "main": "./index.ts",
7 | "scripts": {
8 | "start": "expo start",
9 | "android": "expo start --android",
10 | "ios": "expo start --ios",
11 | "web": "expo start --web",
12 | "eject": "expo eject"
13 | },
14 | "dependencies": {
15 | "@gorhom/portal": "^1.0.13",
16 | "@gorhom/showcase-template": "^2.1.0",
17 | "@react-navigation/bottom-tabs": "^6.3.1",
18 | "@react-navigation/elements": "^1.3.3",
19 | "@react-navigation/material-top-tabs": "^6.2.1",
20 | "@react-navigation/native": "^6.0.10",
21 | "@react-navigation/native-stack": "^6.6.2",
22 | "@react-navigation/stack": "^6.2.1",
23 | "expo": "^46.0.0",
24 | "expo-status-bar": "~1.4.0",
25 | "faker": "^4.1.0",
26 | "nanoid": "^3.3.3",
27 | "react": "18.0.0",
28 | "react-dom": "18.0.0",
29 | "react-native": "0.69.4",
30 | "react-native-gesture-handler": "~2.5.0",
31 | "react-native-pager-view": "5.4.24",
32 | "react-native-reanimated": "~2.9.1",
33 | "react-native-redash": "^16.2.4",
34 | "react-native-safe-area-context": "4.3.1",
35 | "react-native-screens": "~3.15.0",
36 | "react-native-tab-view": "^3.1.1",
37 | "react-native-web": "~0.18.7"
38 | },
39 | "devDependencies": {
40 | "@babel/core": "^7.18.6",
41 | "@types/react": "~18.0.0",
42 | "@types/react-native": "~0.69.1",
43 | "babel-plugin-module-resolver": "^4.1.0",
44 | "expo-cli": "^6.0.2",
45 | "typescript": "^4.6.3"
46 | },
47 | "resolutions": {
48 | "@babel/core": "^7.18.0",
49 | "@babel/runtime": "^7.18.0",
50 | "@babel/preset-typescript": "7.17.12",
51 | "@types/react": "17.0.2",
52 | "@types/react-native": "0.67.7"
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/example/expo/src/App.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { App } from '@gorhom/bottom-sheet-example-app';
3 |
4 | export default () => ;
5 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/lint-staged.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | '**/*.js': ['eslint'],
3 | '**/*.{ts,tsx}': [() => 'tsc --skipLibCheck --noEmit', 'eslint'],
4 | };
5 |
--------------------------------------------------------------------------------
/mogorhom-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/discord/react-native-bottom-sheet/2b37e067aa027f408b32d50cc2636d4093bcacfc/mogorhom-dark.png
--------------------------------------------------------------------------------
/mogorhom-light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/discord/react-native-bottom-sheet/2b37e067aa027f408b32d50cc2636d4093bcacfc/mogorhom-light.png
--------------------------------------------------------------------------------
/preview.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/discord/react-native-bottom-sheet/2b37e067aa027f408b32d50cc2636d4093bcacfc/preview.gif
--------------------------------------------------------------------------------
/scripts/auto-changelog.js:
--------------------------------------------------------------------------------
1 | module.exports = function (Handlebars) {
2 | Handlebars.registerHelper(
3 | 'custom',
4 | function (context, merges, fixes, options) {
5 | if (
6 | (!context || context.length === 0) &&
7 | (!merges || merges.length === 0) &&
8 | (!fixes || fixes.length === 0)
9 | ) {
10 | return '';
11 | }
12 |
13 | const list = [...context, ...(merges || []), ...(fixes || [])]
14 | .filter(item => {
15 | const commit = item.commit || item;
16 | if (options.hash.exclude) {
17 | const pattern = new RegExp(options.hash.exclude, 'm');
18 | if (pattern.test(commit.message)) {
19 | return false;
20 | }
21 | }
22 | if (options.hash.message) {
23 | const pattern = new RegExp(options.hash.message, 'm');
24 | return pattern.test(commit.message);
25 | }
26 | if (options.hash.subject) {
27 | const pattern = new RegExp(options.hash.subject);
28 | return pattern.test(commit.subject);
29 | }
30 | return true;
31 | })
32 | .map(item => options.fn(item.commit || item))
33 | .join('');
34 |
35 | if (!list) {
36 | return '';
37 | }
38 |
39 | return options.hash.heading
40 | ? `${options.hash.heading}\n\n${list}`
41 | : `${list}`;
42 | }
43 | );
44 | };
45 |
--------------------------------------------------------------------------------
/src/components/bottomSheet/constants.ts:
--------------------------------------------------------------------------------
1 | import {
2 | KEYBOARD_BEHAVIOR,
3 | KEYBOARD_BLUR_BEHAVIOR,
4 | KEYBOARD_INPUT_MODE,
5 | SCREEN_HEIGHT,
6 | } from '../../constants';
7 |
8 | // default values
9 | const DEFAULT_HANDLE_HEIGHT = 24;
10 | const DEFAULT_OVER_DRAG_RESISTANCE_FACTOR = 2.5;
11 | const DEFAULT_ENABLE_CONTENT_PANNING_GESTURE = true;
12 | const DEFAULT_ENABLE_HANDLE_PANNING_GESTURE = true;
13 | const DEFAULT_ENABLE_OVER_DRAG = true;
14 | const DEFAULT_ENABLE_PAN_DOWN_TO_CLOSE = false;
15 | const DEFAULT_ANIMATE_ON_MOUNT = true;
16 | const DEFAULT_DYNAMIC_SIZING = false;
17 |
18 | // keyboard
19 | const DEFAULT_KEYBOARD_BEHAVIOR = KEYBOARD_BEHAVIOR.interactive;
20 | const DEFAULT_KEYBOARD_BLUR_BEHAVIOR = KEYBOARD_BLUR_BEHAVIOR.none;
21 | const DEFAULT_KEYBOARD_INPUT_MODE = KEYBOARD_INPUT_MODE.adjustPan;
22 |
23 | // initial values
24 | const INITIAL_VALUE = Number.NEGATIVE_INFINITY;
25 | const INITIAL_SNAP_POINT = -999;
26 | const INITIAL_CONTAINER_HEIGHT = -999;
27 | const INITIAL_CONTAINER_OFFSET = {
28 | top: 0,
29 | bottom: 0,
30 | left: 0,
31 | right: 0,
32 | };
33 | const INITIAL_HANDLE_HEIGHT = -999;
34 | const INITIAL_POSITION = SCREEN_HEIGHT;
35 |
36 | // accessibility
37 | const DEFAULT_ACCESSIBLE = true;
38 | const DEFAULT_ACCESSIBILITY_LABEL = 'Bottom Sheet';
39 | const DEFAULT_ACCESSIBILITY_ROLE = 'adjustable';
40 | const DEFAULT_ENABLE_ACCESSIBILITY_CHANGE_ANNOUNCEMENT = true;
41 | const DEFAULT_ACCESSIBILITY_POSITION_CHANGE_ANNOUNCEMENT = (
42 | positionInScreen: string
43 | ) => `Bottom sheet snapped to ${positionInScreen}% of the screen`;
44 |
45 | export {
46 | DEFAULT_HANDLE_HEIGHT,
47 | DEFAULT_OVER_DRAG_RESISTANCE_FACTOR,
48 | DEFAULT_ENABLE_CONTENT_PANNING_GESTURE,
49 | DEFAULT_ENABLE_HANDLE_PANNING_GESTURE,
50 | DEFAULT_ENABLE_OVER_DRAG,
51 | DEFAULT_ENABLE_PAN_DOWN_TO_CLOSE,
52 | DEFAULT_DYNAMIC_SIZING,
53 | DEFAULT_ANIMATE_ON_MOUNT,
54 | // keyboard
55 | DEFAULT_KEYBOARD_BEHAVIOR,
56 | DEFAULT_KEYBOARD_BLUR_BEHAVIOR,
57 | DEFAULT_KEYBOARD_INPUT_MODE,
58 | // layout
59 | INITIAL_POSITION,
60 | INITIAL_CONTAINER_HEIGHT,
61 | INITIAL_CONTAINER_OFFSET,
62 | INITIAL_HANDLE_HEIGHT,
63 | INITIAL_SNAP_POINT,
64 | INITIAL_VALUE,
65 | // accessibility
66 | DEFAULT_ACCESSIBLE,
67 | DEFAULT_ACCESSIBILITY_LABEL,
68 | DEFAULT_ACCESSIBILITY_ROLE,
69 | DEFAULT_ENABLE_ACCESSIBILITY_CHANGE_ANNOUNCEMENT,
70 | DEFAULT_ACCESSIBILITY_POSITION_CHANGE_ANNOUNCEMENT,
71 | };
72 |
--------------------------------------------------------------------------------
/src/components/bottomSheet/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from './BottomSheet';
2 | export type { BottomSheetProps } from './types';
3 |
--------------------------------------------------------------------------------
/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/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/components/bottomSheetBackdrop/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from './BottomSheetBackdrop';
2 | export type { BottomSheetBackdropProps } from './types';
3 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/components/bottomSheetBackdrop/types.d.ts:
--------------------------------------------------------------------------------
1 | import { ReactNode } from 'react';
2 | import type { ViewProps } from 'react-native';
3 | import type {
4 | BottomSheetVariables,
5 | NullableAccessibilityProps,
6 | } from '../../types';
7 |
8 | export interface BottomSheetBackdropProps
9 | extends Pick,
10 | BottomSheetVariables {}
11 |
12 | export type BackdropPressBehavior = 'none' | 'close' | 'collapse' | number;
13 |
14 | export interface BottomSheetDefaultBackdropProps
15 | extends BottomSheetBackdropProps,
16 | NullableAccessibilityProps {
17 | /**
18 | * Backdrop opacity.
19 | * @type number
20 | * @default 0.5
21 | */
22 | opacity?: number;
23 | /**
24 | * Snap point index when backdrop will appears on.
25 | * @type number
26 | * @default 1
27 | */
28 | appearsOnIndex?: number;
29 | /**
30 | * Snap point index when backdrop will disappears on.
31 | * @type number
32 | * @default 0
33 | */
34 | disappearsOnIndex?: number;
35 | /**
36 | * Enable touch through backdrop component.
37 | * @type boolean
38 | * @default false
39 | */
40 | enableTouchThrough?: boolean;
41 | /**
42 | * What should happen when user press backdrop?
43 | * @type BackdropPressBehavior
44 | * @default 'close'
45 | */
46 | pressBehavior?: BackdropPressBehavior;
47 |
48 | /**
49 | * Function which will be executed on pressing backdrop component
50 | * @type {Function}
51 | */
52 | onPress?: () => void;
53 | /**
54 | * Child component that will be rendered on backdrop.
55 | */
56 | children?: ReactNode | ReactNode[];
57 | }
58 |
--------------------------------------------------------------------------------
/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/components/bottomSheetBackdropContainer/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from './BottomSheetBackdropContainer';
2 |
--------------------------------------------------------------------------------
/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/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/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/components/bottomSheetBackground/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from './BottomSheetBackground';
2 | export type { BottomSheetBackgroundProps } from './types';
3 |
--------------------------------------------------------------------------------
/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/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/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 |
--------------------------------------------------------------------------------
/src/components/bottomSheetBackgroundContainer/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from './BottomSheetBackgroundContainer';
2 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/src/components/bottomSheetContainer/BottomSheetContainer.tsx:
--------------------------------------------------------------------------------
1 | import React, { memo, useCallback, useMemo, useRef } from 'react';
2 | import {
3 | LayoutChangeEvent,
4 | StatusBar,
5 | StyleProp,
6 | View,
7 | ViewStyle,
8 | } from 'react-native';
9 | import { WINDOW_HEIGHT } from '../../constants';
10 | import { print } from '../../utilities';
11 | import { styles } from './styles';
12 | import type { BottomSheetContainerProps } from './types';
13 |
14 | function BottomSheetContainerComponent({
15 | containerHeight,
16 | containerOffset,
17 | topInset = 0,
18 | bottomInset = 0,
19 | shouldCalculateHeight = true,
20 | detached,
21 | style,
22 | children,
23 | }: BottomSheetContainerProps) {
24 | const containerRef = useRef(null);
25 | //#region styles
26 | const containerStyle = useMemo>(
27 | () => [
28 | style,
29 | styles.container,
30 | {
31 | top: topInset,
32 | bottom: bottomInset,
33 | overflow: detached ? 'visible' : 'hidden',
34 | },
35 | ],
36 | [style, detached, topInset, bottomInset]
37 | );
38 | //#endregion
39 |
40 | //#region callbacks
41 | const handleContainerLayout = useCallback(
42 | function handleContainerLayout({
43 | nativeEvent: {
44 | layout: { height },
45 | },
46 | }: LayoutChangeEvent) {
47 | containerHeight.value = height;
48 |
49 | containerRef.current?.measure(
50 | (_x, _y, _width, _height, _pageX, pageY) => {
51 | containerOffset.value = {
52 | top: pageY ?? 0,
53 | left: 0,
54 | right: 0,
55 | bottom: Math.max(
56 | 0,
57 | WINDOW_HEIGHT -
58 | ((pageY ?? 0) + height + (StatusBar.currentHeight ?? 0))
59 | ),
60 | };
61 | }
62 | );
63 |
64 | print({
65 | component: BottomSheetContainer.displayName,
66 | method: 'handleContainerLayout',
67 | params: {
68 | height,
69 | },
70 | });
71 | },
72 | [containerHeight, containerOffset, containerRef]
73 | );
74 | //#endregion
75 |
76 | //#region render
77 | return (
78 |
85 | );
86 | //#endregion
87 | }
88 |
89 | const BottomSheetContainer = memo(BottomSheetContainerComponent);
90 | BottomSheetContainer.displayName = 'BottomSheetContainer';
91 |
92 | export default BottomSheetContainer;
93 |
--------------------------------------------------------------------------------
/src/components/bottomSheetContainer/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from './BottomSheetContainer';
2 | export type { BottomSheetContainerProps } from './types';
3 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/components/bottomSheetDebugView/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from './BottomSheetDebugView';
2 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/components/bottomSheetDraggableView/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from './BottomSheetDraggableView';
2 |
--------------------------------------------------------------------------------
/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/components/bottomSheetFooter/BottomSheetFooter.tsx:
--------------------------------------------------------------------------------
1 | import React, { memo, useCallback, useMemo } from 'react';
2 | import { LayoutChangeEvent } from 'react-native';
3 | import Animated, { useAnimatedStyle } from 'react-native-reanimated';
4 | import { KEYBOARD_STATE } from '../../constants';
5 | import { useBottomSheetInternal } from '../../hooks';
6 | import type { BottomSheetDefaultFooterProps } from './types';
7 | import { styles } from './styles';
8 |
9 | function BottomSheetFooterComponent({
10 | animatedFooterPosition,
11 | bottomInset = 0,
12 | style,
13 | children,
14 | }: BottomSheetDefaultFooterProps) {
15 | //#region hooks
16 | const { animatedFooterHeight, animatedKeyboardState } =
17 | useBottomSheetInternal();
18 | //#endregion
19 |
20 | //#region styles
21 | const containerAnimatedStyle = useAnimatedStyle(() => {
22 | let footerTranslateY = animatedFooterPosition.value;
23 |
24 | /**
25 | * Offset the bottom inset only when keyboard is not shown
26 | */
27 | if (animatedKeyboardState.value !== KEYBOARD_STATE.SHOWN) {
28 | footerTranslateY = footerTranslateY - bottomInset;
29 | }
30 |
31 | return {
32 | transform: [
33 | {
34 | translateY: Math.max(0, footerTranslateY),
35 | },
36 | ],
37 | };
38 | }, [bottomInset, animatedKeyboardState, animatedFooterPosition]);
39 | const containerStyle = useMemo(
40 | () => [styles.container, style, containerAnimatedStyle],
41 | [style, containerAnimatedStyle]
42 | );
43 | //#endregion
44 |
45 | //#region callbacks
46 | const handleContainerLayout = useCallback(
47 | ({
48 | nativeEvent: {
49 | layout: { height },
50 | },
51 | }: LayoutChangeEvent) => {
52 | animatedFooterHeight.value = height;
53 | },
54 | [animatedFooterHeight]
55 | );
56 | //#endregion
57 |
58 | return children !== null ? (
59 |
64 | {typeof children === 'function' ? children() : children}
65 |
66 | ) : null;
67 | }
68 |
69 | const BottomSheetFooter = memo(BottomSheetFooterComponent);
70 | BottomSheetFooter.displayName = 'BottomSheetFooter';
71 |
72 | export default BottomSheetFooter;
73 |
--------------------------------------------------------------------------------
/src/components/bottomSheetFooter/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from './BottomSheetFooter';
2 | export type { BottomSheetFooterProps } from './types';
3 |
--------------------------------------------------------------------------------
/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/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/bottomSheetFooterContainer/BottomSheetFooterContainer.tsx:
--------------------------------------------------------------------------------
1 | import React, { memo } from 'react';
2 | import { useDerivedValue } from 'react-native-reanimated';
3 | import { useBottomSheetInternal } from '../../hooks';
4 | import { KEYBOARD_STATE } from '../../constants';
5 | import type { BottomSheetFooterContainerProps } from './types';
6 |
7 | const BottomSheetFooterContainerComponent = ({
8 | footerComponent: FooterComponent,
9 | }: BottomSheetFooterContainerProps) => {
10 | //#region hooks
11 | const {
12 | animatedContainerHeight,
13 | animatedHandleHeight,
14 | animatedFooterHeight,
15 | animatedPosition,
16 | animatedKeyboardState,
17 | animatedKeyboardHeightInContainer,
18 | } = useBottomSheetInternal();
19 | //#endregion
20 |
21 | //#region variables
22 | const animatedFooterPosition = useDerivedValue(() => {
23 | const keyboardHeight = animatedKeyboardHeightInContainer.value;
24 | let footerTranslateY = Math.max(
25 | 0,
26 | animatedContainerHeight.value - animatedPosition.value
27 | );
28 |
29 | if (animatedKeyboardState.value === KEYBOARD_STATE.SHOWN) {
30 | footerTranslateY = footerTranslateY - keyboardHeight;
31 | }
32 |
33 | footerTranslateY =
34 | footerTranslateY -
35 | animatedFooterHeight.value -
36 | animatedHandleHeight.value;
37 |
38 | return footerTranslateY;
39 | }, [
40 | animatedKeyboardHeightInContainer,
41 | animatedContainerHeight,
42 | animatedPosition,
43 | animatedKeyboardState,
44 | animatedFooterHeight,
45 | animatedHandleHeight,
46 | ]);
47 | //#endregion
48 |
49 | return ;
50 | };
51 |
52 | const BottomSheetFooterContainer = memo(BottomSheetFooterContainerComponent);
53 | BottomSheetFooterContainer.displayName = 'BottomSheetFooterContainer';
54 |
55 | export default BottomSheetFooterContainer;
56 |
--------------------------------------------------------------------------------
/src/components/bottomSheetFooterContainer/types.d.ts:
--------------------------------------------------------------------------------
1 | import type { BottomSheetProps } from '../bottomSheet';
2 |
3 | export interface BottomSheetFooterContainerProps
4 | extends Required> {}
5 |
--------------------------------------------------------------------------------
/src/components/bottomSheetGestureHandlersProvider/BottomSheetGestureHandlersProvider.tsx:
--------------------------------------------------------------------------------
1 | import React, { useMemo } from 'react';
2 | import { GESTURE_SOURCE } from '../../constants';
3 | import {
4 | useGestureHandler,
5 | useBottomSheetInternal,
6 | useGestureEventsHandlersDefault,
7 | } from '../../hooks';
8 | import { BottomSheetGestureHandlersContext } from '../../contexts';
9 | import type { BottomSheetGestureHandlersProviderProps } from './types';
10 | import { useSharedValue } from 'react-native-reanimated';
11 |
12 | const BottomSheetGestureHandlersProvider = ({
13 | gestureEventsHandlersHook:
14 | useGestureEventsHandlers = useGestureEventsHandlersDefault,
15 | children,
16 | }: BottomSheetGestureHandlersProviderProps) => {
17 | //#region variables
18 | const animatedGestureSource = useSharedValue(
19 | GESTURE_SOURCE.UNDETERMINED
20 | );
21 | //#endregion
22 |
23 | //#region hooks
24 | const { animatedContentGestureState, animatedHandleGestureState } =
25 | useBottomSheetInternal();
26 | const { handleOnStart, handleOnActive, handleOnEnd } =
27 | useGestureEventsHandlers();
28 | //#endregion
29 |
30 | //#region gestures
31 | const contentPanGestureHandler = useGestureHandler(
32 | GESTURE_SOURCE.CONTENT,
33 | animatedContentGestureState,
34 | animatedGestureSource,
35 | handleOnStart,
36 | handleOnActive,
37 | handleOnEnd
38 | );
39 |
40 | const scrollablePanGestureHandler = useGestureHandler(
41 | GESTURE_SOURCE.SCROLLABLE,
42 | animatedContentGestureState,
43 | animatedGestureSource,
44 | handleOnStart,
45 | handleOnActive,
46 | handleOnEnd
47 | );
48 |
49 | const handlePanGestureHandler = useGestureHandler(
50 | GESTURE_SOURCE.HANDLE,
51 | animatedHandleGestureState,
52 | animatedGestureSource,
53 | handleOnStart,
54 | handleOnActive,
55 | handleOnEnd
56 | );
57 | //#endregion
58 |
59 | //#region context
60 | const contextValue = useMemo(
61 | () => ({
62 | contentPanGestureHandler,
63 | handlePanGestureHandler,
64 | scrollablePanGestureHandler,
65 | animatedGestureSource,
66 | }),
67 | [
68 | contentPanGestureHandler,
69 | handlePanGestureHandler,
70 | scrollablePanGestureHandler,
71 | animatedGestureSource,
72 | ]
73 | );
74 | //#endregion
75 | return (
76 |
77 | {children}
78 |
79 | );
80 | };
81 |
82 | export default BottomSheetGestureHandlersProvider;
83 |
--------------------------------------------------------------------------------
/src/components/bottomSheetGestureHandlersProvider/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from './BottomSheetGestureHandlersProvider';
2 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/components/bottomSheetHandle/BottomSheetHandle.tsx:
--------------------------------------------------------------------------------
1 | import React, { memo, useMemo } from 'react';
2 | import Animated from 'react-native-reanimated';
3 | import { styles } from './styles';
4 | import type { BottomSheetDefaultHandleProps } from './types';
5 | import {
6 | DEFAULT_ACCESSIBLE,
7 | DEFAULT_ACCESSIBILITY_ROLE,
8 | DEFAULT_ACCESSIBILITY_LABEL,
9 | DEFAULT_ACCESSIBILITY_HINT,
10 | } from './constants';
11 |
12 | const BottomSheetHandleComponent = ({
13 | style,
14 | indicatorStyle: _indicatorStyle,
15 | children,
16 | accessible = DEFAULT_ACCESSIBLE,
17 | accessibilityRole = DEFAULT_ACCESSIBILITY_ROLE,
18 | accessibilityLabel = DEFAULT_ACCESSIBILITY_LABEL,
19 | accessibilityHint = DEFAULT_ACCESSIBILITY_HINT,
20 | }: BottomSheetDefaultHandleProps) => {
21 | // styles
22 | const containerStyle = useMemo(
23 | () => [styles.container, ...[Array.isArray(style) ? style : [style]]],
24 | [style]
25 | );
26 | const indicatorStyle = useMemo(
27 | () => [
28 | styles.indicator,
29 | ...[Array.isArray(_indicatorStyle) ? _indicatorStyle : [_indicatorStyle]],
30 | ],
31 | [_indicatorStyle]
32 | );
33 |
34 | // render
35 | return (
36 |
43 |
44 | {children}
45 |
46 | );
47 | };
48 |
49 | const BottomSheetHandle = memo(BottomSheetHandleComponent);
50 | BottomSheetHandle.displayName = 'BottomSheetHandle';
51 |
52 | export default BottomSheetHandle;
53 |
--------------------------------------------------------------------------------
/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/components/bottomSheetHandle/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from './BottomSheetHandle';
2 | export type { BottomSheetHandleProps } from './types';
3 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/components/bottomSheetHandleContainer/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from './BottomSheetHandleContainer';
2 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/components/bottomSheetModal/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from './BottomSheetModal';
2 | export type {
3 | BottomSheetModalProps,
4 | BottomSheetModalPrivateMethods,
5 | BottomSheetModalStackBehavior,
6 | } from './types';
7 |
--------------------------------------------------------------------------------
/src/components/bottomSheetModal/types.d.ts:
--------------------------------------------------------------------------------
1 | import type React from 'react';
2 | import type { BottomSheetProps } from '../bottomSheet';
3 | import type { MODAL_STACK_BEHAVIOR } from '../../constants';
4 |
5 | export interface BottomSheetModalPrivateMethods {
6 | dismiss: (force?: boolean) => void;
7 | minimize: () => void;
8 | restore: () => void;
9 | }
10 |
11 | export type BottomSheetModalStackBehavior = keyof typeof MODAL_STACK_BEHAVIOR;
12 |
13 | export interface BottomSheetModalProps
14 | extends Omit {
15 | /**
16 | * Modal name to help identify the modal for later on.
17 | * @type string
18 | * @default nanoid generated unique key.
19 | */
20 | name?: string;
21 |
22 | /**
23 | * Defines the stack behavior when modal mount.
24 | * - `push` it will mount the modal on top of current modal.
25 | * - `replace` it will minimize the current modal then mount the modal.
26 | * @type `push` | `replace`
27 | * @default replace
28 | */
29 | stackBehavior?: BottomSheetModalStackBehavior;
30 |
31 | /**
32 | * Enable dismiss the modal when it is closed.
33 | * @type boolean
34 | * @default true
35 | */
36 | enableDismissOnClose?: boolean;
37 |
38 | /**
39 | * Add a custom container like FullWindowOverlay
40 | * allow to fix issue like https://github.com/gorhom/react-native-bottom-sheet/issues/832
41 | * @type React.ComponentType
42 | * @default undefined
43 | */
44 | containerComponent?: React.ComponentType>;
45 |
46 | // callbacks
47 | /**
48 | * Callback when the modal dismissed.
49 | * @type () => void;
50 | */
51 | onDismiss?: () => void;
52 |
53 | /**
54 | * A scrollable node or normal view.
55 | * @type React.ReactNode[] | React.ReactNode | (({ data: any }?) => React.ReactElement)
56 | */
57 | children:
58 | | (({ data: any }?) => React.ReactElement)
59 | | React.ReactNode[]
60 | | React.ReactNode;
61 | }
62 |
--------------------------------------------------------------------------------
/src/components/bottomSheetModalProvider/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from './BottomSheetModalProvider';
2 |
--------------------------------------------------------------------------------
/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/bottomSheetRefreshControl/BottomSheetRefreshControl.android.tsx:
--------------------------------------------------------------------------------
1 | import React, { forwardRef, memo } from 'react';
2 | import { RefreshControl, RefreshControlProps } from 'react-native';
3 | import { NativeViewGestureHandler } from 'react-native-gesture-handler';
4 | import Animated, { useAnimatedProps } from 'react-native-reanimated';
5 | import { SCROLLABLE_STATE } from '../../constants';
6 | import { useBottomSheetInternal } from '../../hooks';
7 |
8 | const AnimatedRefreshControl = Animated.createAnimatedComponent(RefreshControl);
9 |
10 | const BottomSheetRefreshControlComponent = forwardRef<
11 | NativeViewGestureHandler,
12 | RefreshControlProps
13 | >(({ onRefresh, ...rest }, ref) => {
14 | // hooks
15 | const { animatedScrollableState } = useBottomSheetInternal();
16 |
17 | // variables
18 | const animatedProps = useAnimatedProps(() => ({
19 | enabled: animatedScrollableState.value === SCROLLABLE_STATE.UNLOCKED,
20 | }));
21 |
22 | // render
23 | return (
24 |
25 |
30 |
31 | );
32 | });
33 |
34 | const BottomSheetRefreshControl = memo(BottomSheetRefreshControlComponent);
35 | BottomSheetRefreshControl.displayName = 'BottomSheetRefreshControl';
36 |
37 | export default BottomSheetRefreshControl;
38 |
--------------------------------------------------------------------------------
/src/components/bottomSheetRefreshControl/BottomSheetRefreshControl.tsx:
--------------------------------------------------------------------------------
1 | export default () => null;
2 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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/bottomSheetTextInput/BottomSheetTextInput.tsx:
--------------------------------------------------------------------------------
1 | import React, { memo, useCallback, forwardRef, useEffect } from 'react';
2 | import { TextInput } from 'react-native-gesture-handler';
3 | import { useBottomSheetInternal } from '../../hooks';
4 | import type { BottomSheetTextInputProps } from './types';
5 |
6 | const BottomSheetTextInputComponent = forwardRef<
7 | TextInput,
8 | BottomSheetTextInputProps
9 | >(({ onFocus, onBlur, ...rest }, ref) => {
10 | //#region hooks
11 | const { shouldHandleKeyboardEvents } = useBottomSheetInternal();
12 |
13 | useEffect(() => {
14 | return () => {
15 | // Reset the flag on unmount
16 | shouldHandleKeyboardEvents.value = false;
17 | };
18 | }, [shouldHandleKeyboardEvents]);
19 | //#endregion
20 |
21 | //#region callbacks
22 | const handleOnFocus = useCallback(
23 | args => {
24 | shouldHandleKeyboardEvents.value = true;
25 | if (onFocus) {
26 | onFocus(args);
27 | }
28 | },
29 | [onFocus, shouldHandleKeyboardEvents]
30 | );
31 | const handleOnBlur = useCallback(
32 | args => {
33 | shouldHandleKeyboardEvents.value = false;
34 | if (onBlur) {
35 | onBlur(args);
36 | }
37 | },
38 | [onBlur, shouldHandleKeyboardEvents]
39 | );
40 | //#endregion
41 |
42 | return (
43 |
49 | );
50 | });
51 |
52 | const BottomSheetTextInput = memo(BottomSheetTextInputComponent);
53 | BottomSheetTextInput.displayName = 'BottomSheetTextInput';
54 |
55 | export default BottomSheetTextInput;
56 |
--------------------------------------------------------------------------------
/src/components/bottomSheetTextInput/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from './BottomSheetTextInput';
2 | export type { BottomSheetTextInputProps } from './types';
3 |
--------------------------------------------------------------------------------
/src/components/bottomSheetTextInput/types.ts:
--------------------------------------------------------------------------------
1 | import type { TextInputProps } from 'react-native';
2 |
3 | export interface BottomSheetTextInputProps extends TextInputProps {}
4 |
--------------------------------------------------------------------------------
/src/components/bottomSheetView/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from './BottomSheetView';
2 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/components/touchables/Touchables.ios.tsx:
--------------------------------------------------------------------------------
1 | export {
2 | TouchableOpacity,
3 | TouchableHighlight,
4 | TouchableWithoutFeedback,
5 | } from 'react-native';
6 |
--------------------------------------------------------------------------------
/src/components/touchables/Touchables.tsx:
--------------------------------------------------------------------------------
1 | export {
2 | TouchableOpacity,
3 | TouchableHighlight,
4 | TouchableWithoutFeedback,
5 | } from 'react-native-gesture-handler';
6 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/hooks/usePropsValidator.ts:
--------------------------------------------------------------------------------
1 | import { useMemo } from 'react';
2 | import invariant from 'invariant';
3 | import { INITIAL_SNAP_POINT } from '../components/bottomSheet/constants';
4 | import type { BottomSheetProps } from '../components/bottomSheet';
5 |
6 | /**
7 | * @todo
8 | * replace this with `prop-types`.
9 | */
10 |
11 | export const usePropsValidator = ({
12 | index,
13 | snapPoints,
14 | enableDynamicSizing,
15 | topInset,
16 | bottomInset,
17 | }: BottomSheetProps) => {
18 | useMemo(() => {
19 | //#region snap points
20 | const _snapPoints = snapPoints
21 | ? 'value' in snapPoints
22 | ? snapPoints.value
23 | : snapPoints
24 | : [];
25 | invariant(
26 | _snapPoints || enableDynamicSizing,
27 | `'snapPoints' was not provided! please provide at least one snap point.`
28 | );
29 |
30 | _snapPoints.map(snapPoint => {
31 | const _snapPoint =
32 | typeof snapPoint === 'number'
33 | ? snapPoint
34 | : parseInt(snapPoint.replace('%', ''), 10);
35 |
36 | invariant(
37 | _snapPoint > 0 || _snapPoint === INITIAL_SNAP_POINT,
38 | `Snap point '${snapPoint}' is invalid. if you want to allow user to close the sheet, Please use 'enablePanDownToClose' prop.`
39 | );
40 | });
41 |
42 | invariant(
43 | 'value' in _snapPoints || _snapPoints.length > 0 || enableDynamicSizing,
44 | `'snapPoints' was provided with no points! please provide at least one snap point.`
45 | );
46 | //#endregion
47 |
48 | //#region index
49 | invariant(
50 | typeof index === 'number' || typeof index === 'undefined',
51 | `'index' was provided but with wrong type ! expected type is a number.`
52 | );
53 |
54 | invariant(
55 | enableDynamicSizing ||
56 | (typeof index === 'number'
57 | ? index >= -1 && index <= _snapPoints.length - 1
58 | : true),
59 | `'index' was provided but out of the provided snap points range! expected value to be between -1, ${
60 | _snapPoints.length - 1
61 | }`
62 | );
63 | //#endregion
64 |
65 | //#region insets
66 | invariant(
67 | typeof topInset === 'number' || typeof topInset === 'undefined',
68 | `'topInset' was provided but with wrong type ! expected type is a number.`
69 | );
70 | invariant(
71 | typeof bottomInset === 'number' || typeof bottomInset === 'undefined',
72 | `'bottomInset' was provided but with wrong type ! expected type is a number.`
73 | );
74 | //#endregion
75 |
76 | // animations
77 | }, [index, snapPoints, topInset, bottomInset, enableDynamicSizing]);
78 | };
79 |
--------------------------------------------------------------------------------
/src/hooks/useReactiveSharedValue.ts:
--------------------------------------------------------------------------------
1 | import { useEffect, useRef } from 'react';
2 | import Animated, {
3 | cancelAnimation,
4 | makeMutable,
5 | } from 'react-native-reanimated';
6 | import type { Primitive } from '../types';
7 |
8 | export const useReactiveSharedValue = (
9 | value: T
10 | ): T extends Primitive ? Animated.SharedValue : T => {
11 | const initialValueRef = useRef(null);
12 | const valueRef = useRef>(null);
13 |
14 | if (value && typeof value === 'object' && 'value' in value) {
15 | /**
16 | * if provided value is a shared value,
17 | * then we do not initialize another one.
18 | */
19 | } else if (valueRef.current === null) {
20 | // @ts-ignore
21 | initialValueRef.current = value;
22 | /**
23 | * if value is an object, then we need to
24 | * pass a clone.
25 | */
26 | if (typeof value === 'object') {
27 | // @ts-ignore
28 | valueRef.current = makeMutable({ ...value });
29 | } else {
30 | // @ts-ignore
31 | valueRef.current = makeMutable(value);
32 | }
33 | } else if (initialValueRef.current !== value) {
34 | valueRef.current.value = value as T;
35 | }
36 |
37 | useEffect(() => {
38 | return () => {
39 | if (valueRef.current) {
40 | cancelAnimation(valueRef.current);
41 | }
42 | };
43 | }, []);
44 |
45 | // @ts-ignore
46 | return valueRef.current ?? value;
47 | };
48 |
--------------------------------------------------------------------------------
/src/hooks/useScrollHandler.ts:
--------------------------------------------------------------------------------
1 | import {
2 | runOnJS,
3 | useAnimatedRef,
4 | useAnimatedScrollHandler,
5 | useSharedValue,
6 | } from 'react-native-reanimated';
7 | import { useScrollEventsHandlersDefault } from './useScrollEventsHandlersDefault';
8 | import { workletNoop as noop } from '../utilities';
9 | import type { Scrollable, ScrollableEvent } from '../types';
10 |
11 | export const useScrollHandler = (
12 | useScrollEventsHandlers = useScrollEventsHandlersDefault,
13 | onScroll?: ScrollableEvent,
14 | onScrollBeginDrag?: ScrollableEvent,
15 | onScrollEndDrag?: ScrollableEvent
16 | ) => {
17 | // refs
18 | const scrollableRef = useAnimatedRef();
19 |
20 | // variables
21 | const scrollableContentOffsetY = useSharedValue(0);
22 |
23 | // hooks
24 | const {
25 | handleOnScroll = noop,
26 | handleOnBeginDrag = noop,
27 | handleOnEndDrag = noop,
28 | handleOnMomentumEnd = noop,
29 | handleOnMomentumBegin = noop,
30 | } = useScrollEventsHandlers(scrollableRef, scrollableContentOffsetY);
31 |
32 | // callbacks
33 | const scrollHandler = useAnimatedScrollHandler(
34 | {
35 | onScroll: (event, context) => {
36 | handleOnScroll(event, context);
37 |
38 | if (onScroll) {
39 | runOnJS(onScroll)({ nativeEvent: event });
40 | }
41 | },
42 | onBeginDrag: (event, context) => {
43 | handleOnBeginDrag(event, context);
44 |
45 | if (onScrollBeginDrag) {
46 | runOnJS(onScrollBeginDrag)({ nativeEvent: event });
47 | }
48 | },
49 | onEndDrag: (event, context) => {
50 | handleOnEndDrag(event, context);
51 |
52 | if (onScrollEndDrag) {
53 | runOnJS(onScrollEndDrag)({ nativeEvent: event });
54 | }
55 | },
56 | onMomentumBegin: handleOnMomentumBegin,
57 | onMomentumEnd: handleOnMomentumEnd,
58 | },
59 | [
60 | handleOnScroll,
61 | handleOnBeginDrag,
62 | handleOnEndDrag,
63 | handleOnMomentumBegin,
64 | handleOnMomentumEnd,
65 | onScroll,
66 | onScrollBeginDrag,
67 | onScrollEndDrag,
68 | ]
69 | );
70 |
71 | return { scrollHandler, scrollableRef, scrollableContentOffsetY };
72 | };
73 |
--------------------------------------------------------------------------------
/src/hooks/useScrollable.ts:
--------------------------------------------------------------------------------
1 | import { useCallback, RefObject, useRef } from 'react';
2 | import { useSharedValue } from 'react-native-reanimated';
3 | import { getRefNativeTag } from '../utilities/getRefNativeTag';
4 | import { SCROLLABLE_STATE, SCROLLABLE_TYPE } from '../constants';
5 | import type { ScrollableRef, Scrollable } from '../types';
6 |
7 | export const useScrollable = () => {
8 | // refs
9 | const scrollableRef = useRef(null);
10 | const previousScrollableRef = useRef(null);
11 |
12 | // variables
13 | const animatedScrollableType = useSharedValue(
14 | SCROLLABLE_TYPE.UNDETERMINED
15 | );
16 | const animatedScrollableContentOffsetY = useSharedValue(0);
17 | const animatedScrollableOverrideState = useSharedValue(
18 | SCROLLABLE_STATE.UNDETERMINED
19 | );
20 | const isScrollableRefreshable = useSharedValue(false);
21 |
22 | // callbacks
23 | const setScrollableRef = useCallback((ref: ScrollableRef) => {
24 | // get current node handle id
25 | let currentRefId = scrollableRef.current?.id ?? null;
26 |
27 | if (currentRefId !== ref.id) {
28 | if (scrollableRef.current) {
29 | // @ts-ignore
30 | previousScrollableRef.current = scrollableRef.current;
31 | }
32 | // @ts-ignore
33 | scrollableRef.current = ref;
34 | }
35 | }, []);
36 |
37 | const removeScrollableRef = useCallback((ref: RefObject) => {
38 | // find node handle id
39 | let id;
40 | try {
41 | id = getRefNativeTag(ref);
42 | } catch {
43 | return;
44 | }
45 |
46 | // get current node handle id
47 | let currentRefId = scrollableRef.current?.id ?? null;
48 |
49 | /**
50 | * @DEV
51 | * when the incoming node is actually the current node, we reset
52 | * the current scrollable ref to the previous one.
53 | */
54 | if (id === currentRefId) {
55 | // @ts-ignore
56 | scrollableRef.current = previousScrollableRef.current;
57 | }
58 | }, []);
59 |
60 | return {
61 | scrollableRef,
62 | animatedScrollableType,
63 | animatedScrollableContentOffsetY,
64 | animatedScrollableOverrideState,
65 | isScrollableRefreshable,
66 | setScrollableRef,
67 | removeScrollableRef,
68 | };
69 | };
70 |
--------------------------------------------------------------------------------
/src/hooks/useScrollableSetter.ts:
--------------------------------------------------------------------------------
1 | import React, { useCallback, useEffect } from 'react';
2 | import Animated from 'react-native-reanimated';
3 | import { useBottomSheetInternal } from './useBottomSheetInternal';
4 | import { getRefNativeTag } from '../utilities/getRefNativeTag';
5 | import { SCROLLABLE_TYPE } from '../constants';
6 | import type { Scrollable } from '../types';
7 |
8 | export const useScrollableSetter = (
9 | ref: React.RefObject,
10 | type: SCROLLABLE_TYPE,
11 | contentOffsetY: Animated.SharedValue,
12 | refreshable: boolean,
13 | useFocusHook = useEffect
14 | ) => {
15 | // hooks
16 | const {
17 | animatedScrollableType,
18 | animatedScrollableContentOffsetY: rootScrollableContentOffsetY,
19 | isContentHeightFixed,
20 | isScrollableRefreshable,
21 | setScrollableRef,
22 | removeScrollableRef,
23 | } = useBottomSheetInternal();
24 |
25 | // callbacks
26 | const handleSettingScrollable = useCallback(() => {
27 | // set current content offset
28 | rootScrollableContentOffsetY.value = contentOffsetY.value;
29 | animatedScrollableType.value = type;
30 | isScrollableRefreshable.value = refreshable;
31 | isContentHeightFixed.value = false;
32 |
33 | // set current scrollable ref
34 | const id = getRefNativeTag(ref);
35 | if (id) {
36 | setScrollableRef({
37 | id: id,
38 | node: ref,
39 | });
40 | } else {
41 | console.warn(`Couldn't find the scrollable node handle id!`);
42 | }
43 |
44 | return () => {
45 | removeScrollableRef(ref);
46 | };
47 | }, [
48 | ref,
49 | type,
50 | refreshable,
51 | animatedScrollableType,
52 | rootScrollableContentOffsetY,
53 | contentOffsetY,
54 | isScrollableRefreshable,
55 | isContentHeightFixed,
56 | setScrollableRef,
57 | removeScrollableRef,
58 | ]);
59 |
60 | // effects
61 | useFocusHook(handleSettingScrollable);
62 | };
63 |
--------------------------------------------------------------------------------
/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/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/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 |
--------------------------------------------------------------------------------
/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/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/getRefNativeTag.ts:
--------------------------------------------------------------------------------
1 | const isFunction = (ref: unknown): ref is Function => typeof ref === 'function';
2 |
3 | const hasNativeTag = (
4 | ref: unknown
5 | ): ref is { current: { _nativeTag: number } } =>
6 | !!ref &&
7 | typeof ref === 'object' &&
8 | 'current' in (ref || {}) &&
9 | '_nativeTag' in ((ref as any)?.current || {});
10 |
11 | /*
12 | * getRefNativeTag is an internal utility used by createBottomSheetScrollableComponent
13 | * to grab the native tag from the native host component. It only works when the ref
14 | * is pointing to a native Host component.
15 | *
16 | * Internally in the bottom-sheet library ref can be a function that returns a native tag
17 | * this seems to happen due to the usage of Reanimated's animated scroll components.
18 | *
19 | * This should be Fabric compatible as long as the ref is a native host component.
20 | * */
21 | export function getRefNativeTag(ref: unknown) {
22 | const refType = typeof ref;
23 | let nativeTag: undefined | number;
24 | if (isFunction(ref)) {
25 | nativeTag = ref();
26 | } else if (hasNativeTag(ref)) {
27 | nativeTag = ref.current._nativeTag;
28 | }
29 |
30 | if (!nativeTag || typeof nativeTag !== 'number') {
31 | throw new Error(
32 | `Unexpected nativeTag: ${refType}; nativeTag=${nativeTag}
33 |
34 | createBottomSheetScrollableComponent's ScrollableComponent needs to return
35 | a reference that contains a nativeTag to a Native HostComponent.
36 |
37 | ref=${ref}
38 | `
39 | );
40 | }
41 |
42 | return nativeTag;
43 | }
44 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/utilities/noop.ts:
--------------------------------------------------------------------------------
1 | const workletNoop = () => {
2 | 'worklet';
3 | };
4 |
5 | const noop = () => {};
6 |
7 | export { noop, workletNoop };
8 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/templates/release-template.hbs:
--------------------------------------------------------------------------------
1 | {{#each releases}}
2 | {{#if @first}}
3 | {{#custom merges fixes commits heading='#### Breaking Changes :warning:' message='[bB]reaking [cC]hange:|[bB]reaking:' }}
4 | - {{subject}} ([`{{shorthash}}`]({{href}}))
5 | {{/custom}}
6 |
7 | {{#custom merges fixes commits heading='#### New Features' message='^[fF]eat:|[fF]eat\(' exclude='[bB]reaking [cC]hange:|[bB]reaking:'}}
8 | - {{subject}} ([`{{shorthash}}`]({{href}}))
9 | {{/custom}}
10 |
11 | {{#custom merges fixes commits heading='#### Fixes' message='^[fF]ix:|^[fF]ix\(' exclude='[bB]reaking [cC]hange:|[bB]reaking:'}}
12 | - {{subject}} ([`{{shorthash}}`]({{href}}))
13 | {{/custom}}
14 |
15 | {{#custom merges fixes commits heading='#### Documentation Changes' message='^[dD]ocs:|^[dD]ocs\(' exclude='[bB]reaking [cC]hange:|[bB]reaking:'}}
16 | - {{subject}} ([`{{shorthash}}`]({{href}}))
17 | {{/custom}}
18 |
19 | {{#custom merges fixes commits heading='#### Refactoring and Updates' message='^[rR]efactor:|^[rR]efactor\(' exclude='[bB]reaking [cC]hange:|[bB]reaking:'}}
20 | - {{subject}} ([`{{shorthash}}`]({{href}}))
21 | {{/custom}}
22 |
23 | {{#custom merges fixes commits heading='#### Changes to Test Assets' message='^[tT]est:|^[tT]est\(' exclude='[bB]reaking [cC]hange:|[bB]reaking:'}}
24 | - {{subject}} ([`{{shorthash}}`]({{href}}))
25 | {{/custom}}
26 |
27 | {{#custom merges fixes commits heading='#### Tidying of Code eg Whitespace' message='^[sS]tyle:|^[sS]tyle\(' exclude='[bB]reaking [cC]hange:|[bB]reaking:'}}
28 | - {{subject}} ([`{{shorthash}}`]({{href}}))
29 | {{/custom}}
30 |
31 | {{#custom merges fixes commits heading='#### Performance Improvements' message='^[pP]erf:|^[pP]erf\(' exclude='[bB]reaking [cC]hange:|[bB]reaking:'}}
32 | - {{subject}} ([`{{shorthash}}`]({{href}}))
33 | {{/custom}}
34 |
35 | {{#custom merges fixes commits heading='#### Chores And Housekeeping' message='^[cC]hore:|^[cC]hore\(' exclude='[bB]reaking [cC]hange:|[bB]reaking:'}}
36 | - {{subject}} ([`{{shorthash}}`]({{href}}))
37 | {{/custom}}
38 |
39 | {{#custom merges fixes commits heading='#### General Changes' exclude='[bB]reaking [cC]hange:|[bB]reaking:|^[fF]eat:|^[fF]eat\(|^[fF]ix:|^[fF]ix\(|^[cC]hore:|^[cC]hore\(|^[dD]ocs:|^[dD]ocs\(|^[rR]efactor:|^[rR]efactor\(|^[tT]est:|^[tT]est\(|^[sS]tyle:|^[sS]tyle\(|^[pP]erf:|^[pP]erf\('}}
40 | - {{subject}} ([`{{shorthash}}`]({{href}}))
41 | {{/custom}}
42 | {{/if}}
43 | {{/each}}
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------