Skip to content

Instantly share code, notes, and snippets.

@roni-castro
Created June 2, 2022 16:41
Show Gist options
  • Save roni-castro/0016d70f81de88320006a4440201f6f8 to your computer and use it in GitHub Desktop.
Save roni-castro/0016d70f81de88320006a4440201f6f8 to your computer and use it in GitHub Desktop.
BottomSheet bottom
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;
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;
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',
}));
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;
};
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