Created
August 11, 2022 15:13
-
-
Save vincicat/d01ae98d947e7fb93e40799ab1d83446 to your computer and use it in GitHub Desktop.
Bottom Sheet Example with Map (Standalone)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// @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