Skip to content

Instantly share code, notes, and snippets.

@vincicat
Created August 11, 2022 15:13
Show Gist options
  • Save vincicat/d01ae98d947e7fb93e40799ab1d83446 to your computer and use it in GitHub Desktop.
Save vincicat/d01ae98d947e7fb93e40799ab1d83446 to your computer and use it in GitHub Desktop.
Bottom Sheet Example with Map (Standalone)
// @gorhom/bottom-sheet@^4
import React, {
useCallback,
useLayoutEffect,
useMemo,
useRef,
// useState,
} from 'react';
import { View, StyleSheet, Dimensions, Text } from 'react-native';
import MapView from 'react-native-maps';
import {
useSharedValue,
useAnimatedStyle,
// useDerivedValue,
} from 'react-native-reanimated';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import {
BottomSheetModal,
BottomSheetScrollView,
// BottomSheetBackdrop,
BottomSheetModalProvider,
TouchableOpacity,
} from '@gorhom/bottom-sheet';
import { useHeaderHeight } from '@react-navigation/elements';
const MOCK_DATA = [
{
id: 'ams',
name: 'Amsterdam',
address: 'North Holland, Netherlands',
photos: [
'https://www.infocusclinical.com/wp-content/uploads/2020/02/summer-amsterdam-FP.jpg',
'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',
'https://www.kevinandamanda.com/wp-content/uploads/2014/09/amsterdam-2014-03.jpg',
'https://specials-images.forbesimg.com/imageserve/5de4a1db755ebf0006fbea42/960x0.jpg?cropX1=0&cropX2=2121&cropY1=0&cropY2=1414',
],
},
{
id: 'ams',
name: 'Amsterdam',
address: 'North Holland, Netherlands',
photos: [
'https://www.infocusclinical.com/wp-content/uploads/2020/02/summer-amsterdam-FP.jpg',
'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',
'https://www.kevinandamanda.com/wp-content/uploads/2014/09/amsterdam-2014-03.jpg',
'https://specials-images.forbesimg.com/imageserve/5de4a1db755ebf0006fbea42/960x0.jpg?cropX1=0&cropX2=2121&cropY1=0&cropY2=1414',
],
},
{
id: 'ams',
name: 'Amsterdam',
address: 'North Holland, Netherlands',
photos: [
'https://www.infocusclinical.com/wp-content/uploads/2020/02/summer-amsterdam-FP.jpg',
'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',
'https://www.kevinandamanda.com/wp-content/uploads/2014/09/amsterdam-2014-03.jpg',
'https://specials-images.forbesimg.com/imageserve/5de4a1db755ebf0006fbea42/960x0.jpg?cropX1=0&cropX2=2121&cropY1=0&cropY2=1414',
],
},
];
const SEARCH_HANDLE_HEIGHT = 20;
const { height: SCREEN_HEIGHT } = Dimensions.get('window');
const MapExample = () => {
// const [selectedItem, setSelectedItem] = useState();
// refs
const mapRef = useRef(null);
const poiListModalRef = useRef(null);
// const poiDetailsModalRef = useRef(null);
// hooks
const headerHeight = useHeaderHeight();
const { bottom: bottomSafeArea } = useSafeAreaInsets();
//#region variables
const data = MOCK_DATA;
const poiListSnapPoints = useMemo(
() => [
bottomSafeArea + SEARCH_HANDLE_HEIGHT,
// LOCATION_DETAILS_HEIGHT + bottomSafeArea,
'100%',
],
[bottomSafeArea],
);
const mapInitialCamera = useMemo(
() => ({
center: {
latitude: 52.3791,
longitude: 4.9003,
},
heading: 0,
pitch: 0,
zoom: 0,
altitude: 40000,
}),
[],
);
//#endregion
//#region animated variables
const animatedPOIListIndex = useSharedValue(0);
const animatedPOIListPosition = useSharedValue(SCREEN_HEIGHT);
//#endregion
//#region callbacks
const handleTouchStart = useCallback(() => {
poiListModalRef.current?.collapse();
}, []);
const handlePresentLocationDetails = useCallback((item) => {
// setSelectedItem(item);
// poiDetailsModalRef.current?.present();
}, []);
//#endregion
//#region styles
const scrollViewAnimatedStyle = useAnimatedStyle(() => ({
opacity: animatedPOIListIndex.value,
}));
const scrollViewStyle = useMemo(
() => [styles.scrollView, scrollViewAnimatedStyle],
[scrollViewAnimatedStyle],
);
const scrollViewContentContainer = useMemo(
() => [
styles.scrollViewContentContainer,
{ paddingBottom: bottomSafeArea },
],
[bottomSafeArea],
);
//#endregion
//#region effects
useLayoutEffect(() => {
requestAnimationFrame(() => poiListModalRef.current?.present());
}, []);
//#endregion
// renders
const renderItem = useCallback(
(item, index) => (
<TouchableOpacity
key={`${item.name}.${index}`}
onPress={() => handlePresentLocationDetails(item)}>
<View>
<Text>{item.name}</Text>
</View>
</TouchableOpacity>
),
[handlePresentLocationDetails],
);
return (
<BottomSheetModalProvider>
<View style={styles.container}>
<MapView
ref={mapRef}
initialCamera={mapInitialCamera}
zoomEnabled={false}
style={styles.mapContainer}
onTouchStart={handleTouchStart}
/>
<BottomSheetModal
ref={poiListModalRef}
key="PoiListSheet"
name="PoiListSheet"
index={1}
snapPoints={poiListSnapPoints}
handleHeight={SEARCH_HANDLE_HEIGHT}
topInset={headerHeight}
enableDismissOnClose={false}
enablePanDownToClose={false}
keyboardBehavior="extend"
animatedPosition={animatedPOIListPosition}
animatedIndex={animatedPOIListIndex}
// handleComponent={SearchHandle}
// backdropComponent={renderBackdrop}
// backgroundComponent={BlurredBackground}
>
<BottomSheetScrollView
keyboardDismissMode="on-drag"
keyboardShouldPersistTaps="never"
style={scrollViewStyle}
contentContainerStyle={scrollViewContentContainer}>
{data.map(renderItem)}
</BottomSheetScrollView>
</BottomSheetModal>
</View>
</BottomSheetModalProvider>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 24,
},
mapContainer: {
...StyleSheet.absoluteFillObject,
},
scrollView: {
flex: 1,
},
scrollViewContentContainer: {
paddingHorizontal: 16,
},
});
export default MapExample;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment