From a58a29fc0f9a8f92ce72dc59fefe83a86c6c8091 Mon Sep 17 00:00:00 2001 From: Mo Gorhom Date: Tue, 11 Aug 2020 11:00:09 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20rewrite=20bottom=20sheet=20from=20scrat?= =?UTF-8?q?ch=20=F0=9F=8E=89=20(#2)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: added utilities functionalities * chore: added default handle * chore: rewrite the whole bottom sheet into a functional component * chore: added basic example WIP * chore: extract content pan gesture * chore: updated flatlist implementation * chore: updated scrollview implementation * chore: updated sectionview implementation * chore: improve snapping by clamping velocity * chore: extract scrollable functioanlites and introduce removeScrollableRef * chore: added snapTo and close public methods * chore: updated basic examples * chore: added draggable view * chore: revert clampedVelocity * chore: fixed issue with manual animation * fix: fixed android issue with scrollables * chore: updated dependencies * chore: extract handle pan gesture from content * chore: updated examples * chore: persist scrollable content offset per view * chore: rename scrollable components * chore: revert initial screen * chore: added touchables * chore: updated examples * chore: added animatedPosition & animatedPositionIndex callback nodes * chore: added custom handle & shadow overlay examples * chore: support initial closed state * chore: allow user to modify animation configs * chore: updated navigator example * chore: added loadash.isequal --- example/babel.config.js | 16 - .../project.pbxproj | 9 +- example/ios/Podfile.lock | 12 +- example/metro.config.js | 25 +- example/package.json | 15 +- example/src/App.tsx | 30 +- example/src/components/Handle.tsx | 43 - example/src/components/Transaction.tsx | 28 - example/src/components/button/Button.tsx | 4 +- .../components/contactList/ContactList.tsx | 53 +- example/src/components/handle/Handle.tsx | 123 ++ example/src/components/handle/index.ts | 1 + example/src/screens/BasicExample.tsx | 194 +++ example/src/screens/BasicExamples.tsx | 107 ++ example/src/screens/CustomHandleExample.tsx | 104 ++ example/src/screens/DummyScreen.tsx | 56 +- example/src/screens/FlatListExample.tsx | 65 - example/src/screens/NavigatorExample.tsx | 76 +- example/src/screens/Root.tsx | 42 +- example/src/screens/ScrollViewExample.tsx | 64 - example/src/screens/SectionListExample.tsx | 67 -- example/src/screens/ShadowOverlayExample.tsx | 126 ++ example/src/types.ts | 3 + example/src/utils/index.ts | 10 +- example/yarn.lock | 220 ++-- package.json | 14 +- src/components/bottomSheet/BottomSheet.tsx | 1058 ++++++----------- src/components/bottomSheet/styles.ts | 24 + src/components/bottomSheet/types.d.ts | 29 + src/components/bottomSheet/useTransition.ts | 203 ++++ .../contentWrapper/ContentWrapper.android.tsx | 31 + .../contentWrapper/ContentWrapper.ios.tsx | 34 + src/components/contentWrapper/index.ts | 4 + src/components/contentWrapper/types.d.ts | 15 + .../draggableView/DraggableView.tsx | 80 ++ src/components/draggableView/index.ts | 1 + src/components/draggableView/styles.ts | 7 + src/components/draggableView/types.d.ts | 7 + src/components/flatList/FlatList.tsx | 144 +-- src/components/flatList/types.d.ts | 2 +- src/components/handle/Handle.tsx | 19 + src/components/handle/index.ts | 2 + src/components/handle/styles.ts | 35 + src/components/handle/types.d.ts | 5 + src/components/scrollView/ScrollView.tsx | 145 +-- src/components/scrollView/types.d.ts | 2 +- src/components/sectionList/SectionList.tsx | 142 +-- src/components/sectionList/types.d.ts | 2 +- .../touchables/Touchables.android.tsx | 5 + src/components/touchables/Touchables.ios.tsx | 5 + src/components/touchables/index.ts | 18 + src/components/view/View.tsx | 41 + src/components/view/index.ts | 1 + src/components/view/styles.ts | 7 + src/components/view/types.d.ts | 7 + src/constants.ts | 4 + src/context.ts | 34 +- src/index.ts | 15 + src/index.tsx | 4 - src/types.ts | 4 +- src/utilities/index.ts | 3 + src/utilities/normalizeSnapPoints.ts | 16 + src/utilities/useScrollable.ts | 187 +++ src/utilities/useStableCallback.ts | 19 + yarn.lock | 705 ++++------- 65 files changed, 2558 insertions(+), 2015 deletions(-) delete mode 100644 example/babel.config.js delete mode 100644 example/src/components/Handle.tsx delete mode 100644 example/src/components/Transaction.tsx create mode 100644 example/src/components/handle/Handle.tsx create mode 100644 example/src/components/handle/index.ts create mode 100644 example/src/screens/BasicExample.tsx create mode 100644 example/src/screens/BasicExamples.tsx create mode 100644 example/src/screens/CustomHandleExample.tsx delete mode 100644 example/src/screens/FlatListExample.tsx delete mode 100644 example/src/screens/ScrollViewExample.tsx delete mode 100644 example/src/screens/SectionListExample.tsx create mode 100644 example/src/screens/ShadowOverlayExample.tsx create mode 100644 src/components/bottomSheet/styles.ts create mode 100644 src/components/bottomSheet/types.d.ts create mode 100644 src/components/bottomSheet/useTransition.ts create mode 100644 src/components/contentWrapper/ContentWrapper.android.tsx create mode 100644 src/components/contentWrapper/ContentWrapper.ios.tsx create mode 100644 src/components/contentWrapper/index.ts create mode 100644 src/components/contentWrapper/types.d.ts create mode 100644 src/components/draggableView/DraggableView.tsx create mode 100644 src/components/draggableView/index.ts create mode 100644 src/components/draggableView/styles.ts create mode 100644 src/components/draggableView/types.d.ts create mode 100644 src/components/handle/Handle.tsx create mode 100644 src/components/handle/index.ts create mode 100644 src/components/handle/styles.ts create mode 100644 src/components/handle/types.d.ts create mode 100644 src/components/touchables/Touchables.android.tsx create mode 100644 src/components/touchables/Touchables.ios.tsx create mode 100644 src/components/touchables/index.ts create mode 100644 src/components/view/View.tsx create mode 100644 src/components/view/index.ts create mode 100644 src/components/view/styles.ts create mode 100644 src/components/view/types.d.ts create mode 100644 src/constants.ts create mode 100644 src/index.ts delete mode 100644 src/index.tsx create mode 100644 src/utilities/index.ts create mode 100644 src/utilities/normalizeSnapPoints.ts create mode 100644 src/utilities/useScrollable.ts create mode 100644 src/utilities/useStableCallback.ts diff --git a/example/babel.config.js b/example/babel.config.js deleted file mode 100644 index db64a007b..000000000 --- a/example/babel.config.js +++ /dev/null @@ -1,16 +0,0 @@ -const path = require('path'); -const pak = require('../package.json'); - -module.exports = { - presets: ['module:metro-react-native-babel-preset'], - plugins: [ - [ - 'module-resolver', - { - alias: { - [pak.name]: path.join(__dirname, '..', pak.source), - }, - }, - ], - ], -}; diff --git a/example/ios/BottomSheetExample.xcodeproj/project.pbxproj b/example/ios/BottomSheetExample.xcodeproj/project.pbxproj index fad44c45e..3bc5e215c 100644 --- a/example/ios/BottomSheetExample.xcodeproj/project.pbxproj +++ b/example/ios/BottomSheetExample.xcodeproj/project.pbxproj @@ -142,6 +142,7 @@ ORGANIZATIONNAME = Facebook; TargetAttributes = { 13B07F861A680F5B00A75B9A = { + DevelopmentTeam = 4P8A237MEX; LastSwiftMigration = 1110; }; }; @@ -269,6 +270,7 @@ CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = 1; DEAD_CODE_STRIPPING = NO; + DEVELOPMENT_TEAM = 4P8A237MEX; INFOPLIST_FILE = BottomSheetExample/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; OTHER_CFLAGS = ( @@ -280,7 +282,7 @@ "-ObjC", "-lc++", ); - PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.BottomSheetExample.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_BUNDLE_IDENTIFIER = "dev.gorhom.bottom-sheet"; PRODUCT_NAME = BottomSheetExample; SWIFT_OBJC_BRIDGING_HEADER = "BottomSheetExample-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -296,6 +298,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 4P8A237MEX; INFOPLIST_FILE = BottomSheetExample/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; OTHER_CFLAGS = ( @@ -307,7 +310,7 @@ "-ObjC", "-lc++", ); - PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.BottomSheetExample.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_BUNDLE_IDENTIFIER = "dev.gorhom.bottom-sheet"; PRODUCT_NAME = BottomSheetExample; SWIFT_OBJC_BRIDGING_HEADER = "BottomSheetExample-Bridging-Header.h"; SWIFT_VERSION = 5.0; @@ -344,6 +347,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; + ENABLE_BITCODE = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; @@ -397,6 +401,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = YES; + ENABLE_BITCODE = NO; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 117b1d386..68ac77c74 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -296,13 +296,13 @@ PODS: - React-cxxreact (= 0.62.2) - React-jsi (= 0.62.2) - ReactCommon/callinvoker (= 0.62.2) - - RNCMaskedView (0.1.6): + - RNCMaskedView (0.1.10): - React - RNGestureHandler (1.7.0): - React - - RNReanimated (1.10.1): + - RNReanimated (1.11.0): - React - - RNScreens (2.9.0): + - RNScreens (2.10.1): - React - Yoga (1.14.0) - YogaKit (1.18.1): @@ -466,10 +466,10 @@ SPEC CHECKSUMS: React-RCTText: fae545b10cfdb3d247c36c56f61a94cfd6dba41d React-RCTVibration: 4356114dbcba4ce66991096e51a66e61eda51256 ReactCommon: ed4e11d27609d571e7eee8b65548efc191116eb3 - RNCMaskedView: a88953beefbd347a29072d9eba90e42945fe291e + RNCMaskedView: 5a8ec07677aa885546a0d98da336457e2bea557f RNGestureHandler: b6b359bb800ae399a9c8b27032bdbf7c18f08a08 - RNReanimated: c2bb7438b57a3d987bb2e4e6e4bca94787e30b02 - RNScreens: c526239bbe0e957b988dacc8d75ac94ec9cb19da + RNReanimated: f05ea1c5ee07891aaefee662f073f4944c87ac4d + RNScreens: b748efec66e095134c7166ca333b628cd7e6f3e2 Yoga: 3ebccbdd559724312790e7742142d062476b698e YogaKit: f782866e155069a2cca2517aafea43200b01fd5a diff --git a/example/metro.config.js b/example/metro.config.js index 9e8c9e97a..0cb4475e3 100644 --- a/example/metro.config.js +++ b/example/metro.config.js @@ -1,26 +1,29 @@ const path = require('path'); +const fs = require('fs'); const blacklist = require('metro-config/src/defaults/blacklist'); const escape = require('escape-string-regexp'); -const pak = require('../package.json'); const root = path.resolve(__dirname, '..'); +const pak = JSON.parse( + fs.readFileSync(path.join(root, 'package.json'), 'utf8') +); -const modules = Object.keys({ - ...pak.peerDependencies, -}); +const modules = [ + '@babel/runtime', + ...Object.keys({ + ...pak.dependencies, + ...pak.peerDependencies, + }), +]; module.exports = { projectRoot: __dirname, watchFolders: [root], - // We need to make sure that only one version is loaded for peerDependencies - // So we blacklist them at the root, and alias them to the versions in example's node_modules resolver: { - blacklistRE: blacklist( - modules.map( - m => new RegExp(`^${escape(path.join(root, 'node_modules', m))}\\/.*$`) - ) - ), + blacklistRE: blacklist([ + new RegExp(`^${escape(path.join(root, 'node_modules'))}\\/.*$`), + ]), extraNodeModules: modules.reduce((acc, name) => { acc[name] = path.join(__dirname, 'node_modules', name); diff --git a/example/package.json b/example/package.json index 3cd5e5fcd..e98ec2d39 100644 --- a/example/package.json +++ b/example/package.json @@ -10,16 +10,18 @@ }, "dependencies": { "@gorhom/showcase-template": "^0.3.1", - "@react-native-community/masked-view": "0.1.6", + "@react-native-community/masked-view": "0.1.10", "@react-navigation/native": "^5.3.2", "@react-navigation/stack": "^5.3.5", "date-fns": "^2.15.0", "faker": "^4.1.0", + "lodash.isequal": "^4.5.0", "react": "16.11.0", "react-native": "0.62.2", - "react-native-gesture-handler": "^1.6.1", + "react-native-gesture-handler": "^1.7.0", "react-native-maps": "^0.27.1", - "react-native-reanimated": "^1.7.0", + "react-native-reanimated": "^1.11.0", + "react-native-redash": "^14.2.3", "react-native-safe-area-context": "0.7.3", "react-native-screens": "^2.9.0" }, @@ -27,10 +29,9 @@ "@babel/core": "^7.9.6", "@babel/runtime": "^7.9.6", "@types/faker": "^4.1.12", - "@types/react": "~16.9.23", - "@types/react-native": "~0.61.17", - "babel-plugin-module-resolver": "^4.0.0", + "@types/react": "^16.9.23", + "@types/react-native": "^0.61.17", "metro-react-native-babel-preset": "^0.59.0", - "typescript": "^3.8.3" + "typescript": "^3.9.7" } } diff --git a/example/src/App.tsx b/example/src/App.tsx index 76e64e8f1..f469827d0 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -3,9 +3,14 @@ import { NavigationContainer } from '@react-navigation/native'; import { createStackNavigator } from '@react-navigation/stack'; import RootScreen from './screens/Root'; import NavigatorExampleScreen from './screens/NavigatorExample'; -import FlatListExampleScreen from './screens/FlatListExample'; -import SectionListExampleScreen from './screens/SectionListExample'; -import ScrollViewExampleScreen from './screens/ScrollViewExample'; +import { + FlatListExampleScreen, + SectionListExampleScreen, + ScrollViewExampleScreen, + ViewExampleScreen, +} from './screens/BasicExamples'; +import CustomHandleExampleScreen from './screens/CustomHandleExample'; +import ShadowOverlayExampleScreen from './screens/ShadowOverlayExample'; import { AppStackParamsList } from './types'; const Stack = createStackNavigator(); @@ -18,10 +23,7 @@ function App() { component={RootScreen} options={{ headerShown: false }} /> - + {/* basic examples */} + + {/* advanced examples */} + + + ); diff --git a/example/src/components/Handle.tsx b/example/src/components/Handle.tsx deleted file mode 100644 index 99f1e5d47..000000000 --- a/example/src/components/Handle.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import React, { useMemo } from 'react'; -import { StyleProp, StyleSheet, View, ViewStyle } from 'react-native'; - -interface Props { - style?: StyleProp; -} - -const Handle: React.FC = ({ children, style }) => { - const containerStyle = useMemo(() => [styles.header, style], [style]); - return ( - - {children || } - - ); -}; - -export default Handle; - -const styles = StyleSheet.create({ - header: { - alignItems: 'center', - backgroundColor: 'white', - paddingVertical: 14, - borderTopLeftRadius: 20, - borderTopRightRadius: 20, - shadowColor: 'black', - shadowOffset: { - width: 0, - height: -20, - }, - shadowOpacity: 0.1, - shadowRadius: 10, - elevation: 16, - borderBottomWidth: 1, - borderBottomColor: '#F2F2F2', - }, - panelHandle: { - width: 40, - height: 4, - backgroundColor: 'rgba(0,0,0,0.3)', - borderRadius: 4, - }, -}); diff --git a/example/src/components/Transaction.tsx b/example/src/components/Transaction.tsx deleted file mode 100644 index 5451ccba8..000000000 --- a/example/src/components/Transaction.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import React from 'react'; -import { View, Text, StyleSheet } from 'react-native'; -import { ListItemData } from '../utils'; - -const Transaction = ({ title, subtitle }: ListItemData) => { - return ( - - {title} - {subtitle} - - ); -}; - -export default Transaction; - -const styles = StyleSheet.create({ - container: { - padding: 6, - }, - title: { - fontSize: 16, - fontWeight: '500', - }, - subtitle: { - fontSize: 14, - fontWeight: '400', - }, -}); diff --git a/example/src/components/button/Button.tsx b/example/src/components/button/Button.tsx index 6a7557983..7fb9da4d0 100644 --- a/example/src/components/button/Button.tsx +++ b/example/src/components/button/Button.tsx @@ -1,5 +1,6 @@ import React, { useMemo } from 'react'; -import { Text, TouchableOpacity, StyleSheet, ViewStyle } from 'react-native'; +import { Text, StyleSheet, ViewStyle } from 'react-native'; +import { TouchableOpacity } from '@gorhom/bottom-sheet'; interface ButtonProps { label: string; @@ -27,7 +28,6 @@ const styles = StyleSheet.create({ }, label: { color: 'white', - textTransform: 'capitalize', }, }); diff --git a/example/src/components/contactList/ContactList.tsx b/example/src/components/contactList/ContactList.tsx index ce4b6e0e1..b8abb9aac 100644 --- a/example/src/components/contactList/ContactList.tsx +++ b/example/src/components/contactList/ContactList.tsx @@ -1,8 +1,13 @@ import React, { useMemo, useCallback } from 'react'; -import { StyleSheet, View, Text, Platform } from 'react-native'; +import { StyleSheet, Text, Platform, View } from 'react-native'; import { useSafeArea } from 'react-native-safe-area-context'; import { useFocusEffect } from '@react-navigation/native'; -import { FlatList, ScrollView, SectionList } from '@gorhom/bottom-sheet'; +import { + BottomSheetFlatList, + BottomSheetScrollView, + BottomSheetSectionList, + BottomSheetView, +} from '@gorhom/bottom-sheet'; import { createContactListMockData, createContactSectionsMockData, @@ -10,17 +15,18 @@ import { import ContactItem from '../contactItem'; interface ContactListProps { - type: 'FlatList' | 'SectionList' | 'ScrollView'; - header?: React.ReactElement; + type: 'FlatList' | 'SectionList' | 'ScrollView' | 'View'; + count?: number; + header?: (() => JSX.Element) | null; } -const ContactList = ({ type, header = null }: ContactListProps) => { +const ContactList = ({ type, count = 50, header = null }: ContactListProps) => { // hooks const { bottom: bottomSafeArea } = useSafeArea(); // variables - const sections = useMemo(() => createContactSectionsMockData(), []); - const data = useMemo(() => createContactListMockData(), []); + const sections = useMemo(() => createContactSectionsMockData(count), [count]); + const data = useMemo(() => createContactListMockData(count), [count]); // styles const contentContainerStyle = useMemo( @@ -47,7 +53,7 @@ const ContactList = ({ type, header = null }: ContactListProps) => { ), [type] @@ -73,9 +79,12 @@ const ContactList = ({ type, header = null }: ContactListProps) => { if (type === 'FlatList') { return ( - i.name} + initialNumToRender={10} + windowSize={20} + maxToRenderPerBatch={5} renderItem={renderFlatListItem} {...(header && { stickyHeaderIndices: [0], @@ -87,28 +96,41 @@ const ContactList = ({ type, header = null }: ContactListProps) => { ); } else if (type === 'ScrollView') { return ( - - {header} + {header && header()} {data.map(renderScrollViewItem)} - + ); } else if (type === 'SectionList') { return ( - i.name} renderSectionHeader={renderSectionHeader} renderItem={renderSectionItem} - stickyHeaderIndices={[0]} - ListHeaderComponent={header} + {...(header && { + stickyHeaderIndices: [0], + ListHeaderComponent: header, + })} + focusHook={useFocusEffect} removeClippedSubviews={Platform.OS === 'android' && sections.length > 0} /> ); + } else if (type === 'View') { + return ( + + {header && header()} + {data.map(renderScrollViewItem)} + + ); } return null; @@ -125,7 +147,6 @@ const styles = StyleSheet.create({ textTransform: 'uppercase', }, contentContainer: { - paddingTop: 12, paddingHorizontal: 24, backgroundColor: 'white', }, diff --git a/example/src/components/handle/Handle.tsx b/example/src/components/handle/Handle.tsx new file mode 100644 index 000000000..623f486c2 --- /dev/null +++ b/example/src/components/handle/Handle.tsx @@ -0,0 +1,123 @@ +import React, { useMemo } from 'react'; +import { StyleProp, StyleSheet, ViewStyle } from 'react-native'; +import { BottomSheetHandleProps } from '@gorhom/bottom-sheet'; +import Animated, { interpolate, Extrapolate } from 'react-native-reanimated'; +import { transformOrigin, toRad } from 'react-native-redash'; + +interface HandleProps extends BottomSheetHandleProps { + style?: StyleProp; +} + +const Handle: React.FC = ({ style, animatedPositionIndex }) => { + //#region animations + const borderTopRadius = interpolate(animatedPositionIndex, { + inputRange: [1, 2], + outputRange: [20, 0], + extrapolate: Extrapolate.CLAMP, + }); + const indicatorTransformOriginY = interpolate(animatedPositionIndex, { + inputRange: [0, 1, 2], + outputRange: [-1, 0, 1], + extrapolate: Extrapolate.CLAMP, + }); + const leftIndicatorRotate = interpolate(animatedPositionIndex, { + inputRange: [0, 1, 2], + outputRange: [toRad(-30), 0, toRad(30)], + extrapolate: Extrapolate.CLAMP, + }); + const rightIndicatorRotate = interpolate(animatedPositionIndex, { + inputRange: [0, 1, 2], + outputRange: [toRad(30), 0, toRad(-30)], + extrapolate: Extrapolate.CLAMP, + }); + //#endregion + + //#region styles + const containerStyle = useMemo( + () => [ + styles.header, + style, + { + borderTopLeftRadius: borderTopRadius, + borderTopRightRadius: borderTopRadius, + }, + ], + // eslint-disable-next-line react-hooks/exhaustive-deps + [style] + ); + const leftIndicatorStyle = useMemo( + () => ({ + ...styles.indicator, + ...styles.leftIndicator, + transform: transformOrigin( + { x: 0, y: indicatorTransformOriginY }, + { + rotate: leftIndicatorRotate, + translateX: -5, + } + ), + }), + // eslint-disable-next-line react-hooks/exhaustive-deps + [] + ); + const rightIndicatorStyle = useMemo( + () => ({ + ...styles.indicator, + ...styles.rightIndicator, + transform: transformOrigin( + { x: 0, y: indicatorTransformOriginY }, + { + rotate: rightIndicatorRotate, + translateX: 5, + } + ), + }), + // eslint-disable-next-line react-hooks/exhaustive-deps + [] + ); + //#endregion + + // render + return ( + + + + + ); +}; + +export default Handle; + +const styles = StyleSheet.create({ + header: { + alignContent: 'center', + alignItems: 'center', + justifyContent: 'center', + backgroundColor: 'white', + paddingVertical: 14, + shadowColor: 'black', + shadowOffset: { + width: 0, + height: -20, + }, + shadowOpacity: 0.1, + shadowRadius: 10, + elevation: 16, + borderBottomWidth: 1, + borderBottomColor: '#fff', + }, + indicator: { + position: 'absolute', + width: 10, + height: 4, + backgroundColor: '#999', + }, + leftIndicator: { + borderTopStartRadius: 2, + borderBottomStartRadius: 2, + }, + rightIndicator: { + borderTopEndRadius: 2, + borderBottomEndRadius: 2, + }, +}); diff --git a/example/src/components/handle/index.ts b/example/src/components/handle/index.ts new file mode 100644 index 000000000..1b3992686 --- /dev/null +++ b/example/src/components/handle/index.ts @@ -0,0 +1 @@ +export { default } from './Handle'; diff --git a/example/src/screens/BasicExample.tsx b/example/src/screens/BasicExample.tsx new file mode 100644 index 000000000..9fb4032e6 --- /dev/null +++ b/example/src/screens/BasicExample.tsx @@ -0,0 +1,194 @@ +import React, { useCallback, useMemo, useRef } from 'react'; +import { View, StyleSheet, Text } from 'react-native'; +import { useHeaderHeight } from '@react-navigation/stack'; +import Animated, { + useValue, + interpolate, + concat, + Extrapolate, +} from 'react-native-reanimated'; +import BottomSheet from '@gorhom/bottom-sheet'; +import Handle from '../components/handle'; +import Button from '../components/button'; +import ContactList from '../components/contactList'; +import { ReText } from 'react-native-redash'; + +const BasicExample = () => { + // hooks + const bottomSheetRef = useRef(null); + const headerHeight = useHeaderHeight(); + + // variables + const snapPoints = useMemo(() => [150, 300, 450], []); + const position = useValue(0); + + // styles + const shadowOverlayStyle = useMemo( + () => ({ + ...styles.shadowOverlay, + opacity: interpolate(position, { + inputRange: [300, 450], + outputRange: [0, 1], + extrapolate: Extrapolate.CLAMP, + }), + }), + // eslint-disable-next-line react-hooks/exhaustive-deps + [] + ); + + // callbacks + const handleSheetChanges = useCallback((index: number) => { + console.log('handleSheetChanges', index); + }, []); + const handleSnapPress = useCallback(index => { + bottomSheetRef.current?.snapTo(index); + }, []); + + const handleClosePress = useCallback(() => { + bottomSheetRef.current?.close(); + }, []); + + // renders + const renderHeader = useCallback(() => { + return ( + + Basic Screen + + ); + }, []); + + return ( + +