이와 같이 bottom에서 모달이 떠 bottom sheet 컴포넌트로 content list를 확인할 수 있도록 하는 컴포넌트를 구현할 것이다.
여기 bottom sheet이 떠 위로 올린 후 스크롤을 내리면서 content list 확인이 가능하게 구현할 것이다.
import { Box, Toolbar, useMediaQuery } from '@mui/material';
// 모바일 영역
const query = '(min-width:0px) and (max-width:600px)';
/**
* Media Query Hook (SSR에서 동작)
* 너비가 [0-360] 픽셀이면 true를 반환
*/
const isMobile = useMediaQuery(query, { noSsr: false });
<Box component="main" sx={{ flexGrow: 1, backgroundColor: 'gray' }}>
{/* Mobile 용 AppBar 영역만큼 차지해줌 */}
{isMobile && <Toolbar />}
{/* 현재 라우팅된 페이지 */}
{children}
{/* Mobile 전용 BottomSheet 영역 */}
{isMobile && <BottomSheet />}
</Box>
// Bottom Sheet FUll 높이 값 :
// DRAWER Puller 높이 + Toolbar 높이 + 빈 Toolbar 높이 + 여유 높이
const BOTTOM_SHEET_FULL_HEIGHT =
DRAWER_BLEEDING + TOOL_BAR_HEIGHT + EMPTY_TOOL_BAR_HEIGHT + 20;
하단 드로어의 전체 높이를 계산하는데 사용되는 변수 BOTTOM_SHEET_FULL_HEIGHT를 정의한다.
const [open, setOpen] = useState(false);
// Bottom Modal Open 함수
const handleOpen = useCallback(() => {
setOpen(true);
}, []);
// Bottom Modal Close 함수
const handleClose = useCallback(() => {
setOpen(false);
}, []);
import { Global } from '@emotion/react';
<Global
styles={{
'.MuiDrawer-root > .MuiPaper-root': {
height: `calc(100% - ${BOTTOM_SHEET_FULL_HEIGHT}px)`,
overflow: 'visible',
},
}}
/>
Global 컴포넌트를 사용하여 글로벌 CSS 스타일을 적용한다. 여기서는 드로어의 스타일을 정의해준다.
높이는 앞에서 정의한 높이 값을 뺀 값으로 높이 값을 가질 수 있도록 한다.
'visible' 값은 내용이 넘칠 경우 스크롤바가 표시되지 않고 내용이 넘칠 수 있도록 한다.
const container =
window !== undefined ? () => window.document.body : undefined;
<SwipeableDrawer
container={container}
anchor="bottom"
open={open}
onOpen={handleOpen}
onClose={handleClose}
swipeAreaWidth={DRAWER_BLEEDING}
disableSwipeToOpen={false}
ModalProps={{ keepMounted: true }}
>
container: 하단 드로어를 포함하는 부모 요소를 가리키는 함수
anchor: 드로어 위치 설정(아래)
open: 드로어 열고 닫기 상태값 넣어주기
onOpen, onClose: 상태 값 true, false로 set하는 함수 넣어주기
swipeAreaWidth: 스와이프 동작을 감지할 영역의 너비를 설정
disableSwipeToOpen={false}: 스와이프 동작으로 드로어를 열 수 있도록 설정
ModalProps: keepMounted: true는 모달이 열려 있을 때도 DOM에 유지
<StyledBox>
<Puller />
<ContentBox>
<ContentList />
</ContentBox>
</StyledBox>
import { Box, styled } from '@mui/material';
export const DRAWER_BLEEDING = 106;
export const StyledBox = styled(Box)(({ theme }) => ({
backgroundColor:
theme.palette.mode === 'light' ? '#fff' : theme.palette.grey[800],
position: 'absolute',
top: -DRAWER_BLEEDING,
borderTopLeftRadius: 8,
borderTopRightRadius: 8,
right: 0,
left: 0,
boxShadow: '0px 2px 9px 5px gray',
visibility: 'visible',
height: `calc(100% + ${DRAWER_BLEEDING}px)`,
}));
export const Puller = styled(Box)(() => ({
width: 40,
height: 3,
backgroundColor: '#E4E4E4',
borderRadius: 3,
position: 'absolute',
top: 20,
left: 'calc(50% - 15px)',
}));
export const ContentBox = styled(Box)`
margin-top: 50px;
height: 90%;
overflow: auto;
scrollbar-width: none;
&::-webkit-scrollbar {
display: none;
}
`;