Created
June 2, 2022 16:41
-
-
Save roni-castro/0016d70f81de88320006a4440201f6f8 to your computer and use it in GitHub Desktop.
BottomSheet bottom
This file contains hidden or 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
import React from 'react'; | |
import styled from 'styled-components/native'; | |
const ModalHandleWrapper = styled.View` | |
align-items: center; | |
justify-content: center; | |
`; | |
const ModalHandle = styled.View` | |
height: 5px; | |
width: 100px; | |
background-color: #ffffff; | |
border-radius: 2.5px; | |
margin-bottom: 9px; | |
`; | |
const Handle = () => { | |
return ( | |
<ModalHandleWrapper> | |
<ModalHandle /> | |
</ModalHandleWrapper> | |
); | |
}; | |
export default Handle; |
This file contains hidden or 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
import React, { useState } from 'react'; | |
import PropTypes from 'prop-types'; | |
import { ScrollView, useWindowDimensions } from 'react-native'; | |
import { useSafeAreaInsets } from 'react-native-safe-area-context'; | |
import Handle from './handle'; | |
import { ModalBottomBoxWrapper, ModalBottomContent, ModalStyled } from './styles'; | |
import { calculateModalHeight } from './utils'; | |
const BottomSheetComponent = ({ | |
isVisible, | |
onCloseOrDismissModal, | |
maxHeight, | |
children, | |
headerComponent, | |
handleComponent, | |
}) => { | |
const [contentHeight, setContentHeight] = useState(0); | |
const insets = useSafeAreaInsets(); | |
const { height: windowHeight } = useWindowDimensions(); | |
const modalMaxHeight = calculateModalHeight({ | |
maxHeight, | |
windowHeight, | |
topSpacing: insets.top, | |
}); | |
const shouldActivateScroll = contentHeight > modalMaxHeight; | |
return ( | |
<ModalStyled | |
isVisible={isVisible} | |
onSwipeComplete={onCloseOrDismissModal} | |
onBackdropPress={onCloseOrDismissModal} | |
onBackButtonPress={onCloseOrDismissModal} | |
swipeDirection={['down']} | |
useNativeDriver | |
propagateSwipe={shouldActivateScroll} | |
> | |
{handleComponent ? handleComponent : <Handle />} | |
{headerComponent} | |
<ModalBottomBoxWrapper style={{ maxHeight: modalMaxHeight, paddingBottom: insets.bottom }}> | |
<ScrollView scrollEnabled={shouldActivateScroll} contentContainerStyle={{ flexGrow: 1 }}> | |
<ModalBottomContent | |
onLayout={event => { | |
const { height } = event.nativeEvent.layout; | |
setContentHeight(height); | |
}} | |
> | |
{children} | |
</ModalBottomContent> | |
</ScrollView> | |
</ModalBottomBoxWrapper> | |
</ModalStyled> | |
); | |
}; | |
/** | |
* Define the interface of the component. | |
* | |
* @type {Object} | |
*/ | |
BottomSheetComponent.propTypes = { | |
isVisible: PropTypes.bool.isRequired, | |
onCloseOrDismissModal: PropTypes.func.isRequired, | |
children: PropTypes.node.isRequired, | |
maxHeight: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), | |
headerComponent: PropTypes.node, | |
handleComponent: PropTypes.node, | |
Header: PropTypes.func, | |
}; | |
BottomSheetComponent.defaultProps = { | |
maxHeight: '60%', | |
}; | |
export default BottomSheetComponent; |
This file contains hidden or 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
import Modal from 'react-native-modal'; | |
import styled from 'styled-components/native'; | |
export const ModalStyled = styled(Modal)({ | |
margin: 0, | |
justifyContent: 'flex-end', | |
}); | |
export const ModalBottomBoxWrapper = styled.View(({ theme }) => ({ | |
backgroundColor: theme.colors.white, | |
})); | |
export const ModalBottomContent = styled.View(() => ({ | |
justifyContent: 'center', | |
})); |
This file contains hidden or 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
const isPercentageValue = value => /^\d+(\.\d+)?%$/.test(value); | |
const MinTopModalMargin = 50; | |
export const calculateModalHeight = ({ maxHeight, windowHeight, topSpacing }) => { | |
const isPercentValue = isPercentageValue(maxHeight); | |
const maxHeightNumber = parseFloat(maxHeight); | |
if (isNaN(maxHeightNumber) || maxHeightNumber > windowHeight || maxHeightNumber === 0) | |
return windowHeight - Math.max(topSpacing, MinTopModalMargin); | |
if (isPercentValue) return (maxHeightNumber / 100) * windowHeight; | |
else return maxHeightNumber; | |
}; |
This file contains hidden or 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
import { calculateModalHeight } from './'; | |
const DefaultWindowHeight = 600; | |
const DefaultTopSpacing = 90; | |
describe('Unit | modal utils', () => { | |
describe('#calculateModalHeight', () => { | |
[ | |
{ maxHeight: '20', expectedModalHeight: 20 }, | |
{ maxHeight: '80%', expectedModalHeight: 0.8 * DefaultWindowHeight }, | |
{ maxHeight: '100%', expectedModalHeight: DefaultWindowHeight }, | |
{ maxHeight: DefaultWindowHeight - 100, expectedModalHeight: DefaultWindowHeight - 100 }, | |
{ maxHeight: '0', expectedModalHeight: DefaultWindowHeight - DefaultTopSpacing }, | |
{ maxHeight: undefined, expectedModalHeight: DefaultWindowHeight - DefaultTopSpacing }, | |
{ maxHeight: 0, expectedModalHeight: DefaultWindowHeight - DefaultTopSpacing }, | |
{ | |
maxHeight: DefaultWindowHeight + 100, | |
expectedModalHeight: DefaultWindowHeight - DefaultTopSpacing, | |
}, | |
].forEach(({ maxHeight, expectedModalHeight }) => { | |
describe(`when maxHeight is ${maxHeight}`, () => { | |
it(`it is expected the modal height to be ${expectedModalHeight}`, () => { | |
expect( | |
calculateModalHeight({ | |
maxHeight, | |
windowHeight: DefaultWindowHeight, | |
topSpacing: DefaultTopSpacing, | |
}), | |
).toBe(expectedModalHeight); | |
}); | |
}); | |
}); | |
}); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment