Skip to content

Commit

Permalink
[Fix] - 메인페이지 modal css 깨짐 및 modal, drawer 애니메이션 적용 안되던 이슈 수정 (#567)
Browse files Browse the repository at this point in the history
* style(SingleSelectionTagModalBottomSheet): 바텀 시트 css 깨지던것 수정

* feat(useUnmountAnimation): 컴포넌트 언마운트시 애니메이션 재생되는 훅 구현

* feat(MainPage): 정렬,필터 모달에 useUnmountAnimation 훅 사용

* feat(Drawer): Drawer에 useUnmountAnimation 훅 사용

* refactor(useUnmountAnimation): shouldRender를  isRendered로 수정

* fix: 프로필 사진 수정, 여행기 및 여행계획 수정 bottomSheet에 useUnmountAnimation 훅 사용

* fix(EditRegisterModalBottomSheet): 애니메이션 실종 이슈 수정

* style(FloatingButton): animation 추가 및 useUnmountAnimation 훅 사용

* style(Icon): 세로 가운데 정렬을 위한 wrapper 추가

* feat(animation): animation 토큰 추가

* refactor: Drawer,FloatingButton에서 animation 토큰 사용

* chore: 필요없는 주석 삭제

* fix(route): 앞에 / 추가

* fix(Drawer): overlay 위치 수정

- 페이지 이동할 때 마다 ovelay가 보였다가 안보이는 이슈가 있어서 수정
  • Loading branch information
0jenn0 authored Oct 23, 2024
1 parent 0b3b3ee commit e970e20
Show file tree
Hide file tree
Showing 17 changed files with 264 additions and 103 deletions.
47 changes: 28 additions & 19 deletions frontend/src/components/common/Drawer/Drawer.styled.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,41 @@
import styled from "@emotion/styled";

export const DrawerContainer = styled.div<{ isOpen: boolean }>`
display: flex;
flex-direction: column;
import theme from "@styles/theme";
import { PRIMITIVE_COLORS } from "@styles/tokens";

export const Overlay = styled.div<{ isOpen: boolean }>`
visibility: ${({ isOpen }) => (isOpen ? "visible" : "hidden")};
position: fixed;
top: 0;
right: ${({ isOpen }) => (isOpen ? "0" : "-210px")};
z-index: ${({ theme }) => theme.zIndex.drawer};
width: 210px;
z-index: ${({ theme }) => theme.zIndex.drawerOverlay};
width: 100%;
height: 100%;
inset: 0;
background-color: #fff;
background-color: rgb(0 0 0 / 30%);
opacity: ${({ isOpen }) => (isOpen ? 1 : 0)};
transition: right 0.3s ease-in-out;
animation: ${({ isOpen, theme }) => (isOpen ? theme.animation.fade.in : theme.animation.fade.out)}
${theme.animation.duration.default} ease-in-out;
`;

export const Overlay = styled.div<{ isOpen: boolean }>`
export const DrawerContainer = styled.div<{ isOpen: boolean }>`
display: flex;
flex-direction: column;
visibility: ${({ isOpen }) => (isOpen ? "visible" : "hidden")};
position: fixed;
top: 0;
left: 0;
z-index: ${({ theme }) => theme.zIndex.drawerOverlay};
width: 100%;
right: 0;
z-index: ${({ theme }) => theme.zIndex.drawer};
width: 210px;
height: 100%;
background-color: rgb(0 0 0 / 30%);
opacity: ${({ isOpen }) => (isOpen ? 1 : 0)};
transition:
opacity 0.3s ease-in-out,
visibility 0.3s ease-in-out;
background-color: ${PRIMITIVE_COLORS.white};
animation: ${({ isOpen, theme }) =>
isOpen
? theme.animation.slide.in({ from: "100%", to: 0 })
: theme.animation.slide.out({ from: 0, to: "100%" })}
${theme.animation.duration.default} ease-in-out;
`;

export const DrawerHeader = styled.div`
Expand All @@ -46,9 +53,11 @@ export const DrawerContent = styled.div`
`;

export const TriggerButton = styled.button`
background: none;
border: none;
background-color: none;
font-size: 1.5rem;
cursor: pointer;
`;
20 changes: 13 additions & 7 deletions frontend/src/components/common/Drawer/Drawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,19 @@ import DrawerProvider, { useDrawerContext } from "@contexts/DrawerProvider";
import useModalControl from "@hooks/useModalControl";
import usePressESC from "@hooks/usePressESC";
import useToggle from "@hooks/useToggle";
import useUnmountAnimation from "@hooks/useUnmountAnimation";

import VisuallyHidden from "../VisuallyHidden/VisuallyHidden";
import * as S from "./Drawer.styled";

const Drawer = ({ children }: React.PropsWithChildren) => {
const [isOpen, , , toggle] = useToggle();
usePressESC(isOpen, toggle);

usePressESC(isOpen, toggle);
useModalControl(isOpen, toggle);

const { isRendered } = useUnmountAnimation({ isOpen });

let headerContent: React.ReactNode | null = null;
let drawerContent: React.ReactNode | null = null;
const otherContent: React.ReactNode[] = [];
Expand All @@ -38,13 +41,16 @@ const Drawer = ({ children }: React.PropsWithChildren) => {
{isOpen ? "사용자 메뉴가 열렸습니다." : "사용자 메뉴가 닫혔습니다."}
</VisuallyHidden>
{otherContent}
<S.Overlay isOpen={isOpen} onClick={toggle} />
{isOpen &&

{isRendered &&
ReactDOM.createPortal(
<S.DrawerContainer id="drawer-content" isOpen={isOpen} aria-modal="true" role="dialog">
{headerContent}
{drawerContent}
</S.DrawerContainer>,
<>
<S.Overlay isOpen={isOpen} onClick={toggle} />
<S.DrawerContainer id="drawer-content" isOpen={isOpen} aria-modal="true" role="dialog">
{headerContent}
{drawerContent}
</S.DrawerContainer>
</>,
document.body,
)}
</DrawerProvider>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { css } from "@emotion/react";
import styled from "@emotion/styled";

import theme from "@styles/theme";
import { PRIMITIVE_COLORS } from "@styles/tokens";

export const FloatingButtonContainer = styled.div`
Expand All @@ -13,36 +14,41 @@ export const FloatingButtonContainer = styled.div`
z-index: ${({ theme }) => theme.zIndex.floating};
`;

export const BackdropLayout = styled.div`
export const BackdropLayout = styled.div<{ $isOpen: boolean }>`
visibility: ${({ $isOpen }) => ($isOpen ? "visible" : "hidden")};
position: fixed;
width: 100%;
height: 100%;
inset: 0;
background-color: ${({ theme }) => theme.colors.dimmed};
animation: ${({ $isOpen, theme }) =>
$isOpen ? theme.animation.fade.in : theme.animation.fade.out}
${theme.animation.duration.default} ease-in-out;
inset: 0;
cursor: pointer;
`;

export const SubButtonContainer = styled.div<{ $isOpen: boolean }>`
display: flex;
flex-direction: column;
visibility: ${({ $isOpen }) => ($isOpen ? "visible" : "hidden")};
position: absolute;
bottom: 100%;
gap: ${({ theme }) => theme.spacing.l};
width: 16rem;
padding: ${({ theme }) => theme.spacing.l} ${({ theme }) => theme.spacing.m};
border-radius: ${({ theme }) => theme.spacing.s};
background-color: ${PRIMITIVE_COLORS.gray[700]};
transition: all 0.3s ease-out;
${({ $isOpen }) => css`
opacity: ${$isOpen ? 1 : 0};
visibility: ${$isOpen ? "visible" : "hidden"};
transform: translateY(${$isOpen ? -0.8 : 2}rem);
`}
animation: ${({ $isOpen, theme }) =>
$isOpen
? theme.animation.slide.up({ from: 2, to: -0.8 })
: theme.animation.slide.down({ from: -0.8, to: 2 })}
${theme.animation.duration.default} ease-in-out;
gap: ${({ theme }) => theme.spacing.l};
animation-fill-mode: forwards;
`;

export const SubButton = styled.button`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useNavigate } from "react-router-dom";

import useModalControl from "@hooks/useModalControl";
import useToggle from "@hooks/useToggle";
import useUnmountAnimation from "@hooks/useUnmountAnimation";

import { removeEmoji } from "@utils/removeEmojis";

Expand All @@ -23,6 +24,7 @@ const FloatingButton = () => {
};

useModalControl(isOpen, handleToggleButton);
const { isRendered } = useUnmountAnimation({ isOpen });

return (
<S.FloatingButtonContainer>
Expand All @@ -31,9 +33,9 @@ const FloatingButton = () => {
? "여행기 및 여행 계획 작성 메뉴가 열렸습니다. 닫으려면 esc버튼을 눌러주세요."
: "여행기 및 여행 계획 작성 메뉴가 닫혔습니다."}
</VisuallyHidden>
{isOpen && (
{isRendered && (
<>
<S.BackdropLayout onClick={handleToggleButton} />
<S.BackdropLayout $isOpen={isOpen} onClick={handleToggleButton} />
<FocusTrap>
<S.SubButtonContainer $isOpen={isOpen}>
{SUB_BUTTONS.map(({ text, route }) => (
Expand Down
7 changes: 7 additions & 0 deletions frontend/src/components/common/Icon/Icon.styled.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import styled from "@emotion/styled";

export const Wrapper = styled.div<{ size: string }>`
display: flex;
align-items: center;
height: ${({ size }) => size}px;
`;
41 changes: 23 additions & 18 deletions frontend/src/components/common/Icon/Icon.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { StrokeLineCap, StrokeLineJoin } from "@components/common/Icon/Icon.type";

import * as S from "./Icon.styled";
import SVG_ICONS_MAP from "./svg-icons.json";

interface IconProps extends React.ComponentPropsWithoutRef<"svg"> {
Expand All @@ -10,24 +11,28 @@ interface IconProps extends React.ComponentPropsWithoutRef<"svg"> {

const Icon = ({ iconType, color, size, ...attributes }: IconProps) => {
return (
<svg
width={size}
height={size}
viewBox={`0 0 ${SVG_ICONS_MAP[iconType].width} ${SVG_ICONS_MAP[iconType].height}`}
xmlns="http://www.w3.org/2000/svg"
{...attributes}
>
<path
d={SVG_ICONS_MAP[iconType].path}
stroke={color ?? SVG_ICONS_MAP[iconType].stroke}
strokeWidth={SVG_ICONS_MAP[iconType].strokeWidth}
strokeLinecap={SVG_ICONS_MAP[iconType].strokeLinecap as StrokeLineCap}
strokeLinejoin={SVG_ICONS_MAP[iconType].strokeLinejoin as StrokeLineJoin}
fill={
SVG_ICONS_MAP[iconType].strokeLinecap === "round" && iconType !== "kakao" ? "none" : color
}
/>
</svg>
<S.Wrapper size={size}>
<svg
width={size}
height={size}
viewBox={`0 0 ${SVG_ICONS_MAP[iconType].width} ${SVG_ICONS_MAP[iconType].height}`}
xmlns="http://www.w3.org/2000/svg"
{...attributes}
>
<path
d={SVG_ICONS_MAP[iconType].path}
stroke={color ?? SVG_ICONS_MAP[iconType].stroke}
strokeWidth={SVG_ICONS_MAP[iconType].strokeWidth}
strokeLinecap={SVG_ICONS_MAP[iconType].strokeLinecap as StrokeLineCap}
strokeLinejoin={SVG_ICONS_MAP[iconType].strokeLinejoin as StrokeLineJoin}
fill={
SVG_ICONS_MAP[iconType].strokeLinecap === "round" && iconType !== "kakao"
? "none"
: color
}
/>
</svg>
</S.Wrapper>
);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,40 +24,38 @@ const EditRegisterModalBottomSheet = ({
onConfirm,
}: EditRegisterModalBottomSheetProps) => {
return (
isOpen && (
<Modal isOpen={isOpen} onCloseModal={onClose} position="bottom" boxLayoutGap="xxxl">
<Modal.Header headerPosition="center">
<S.HandleBar />
</Modal.Header>
<Modal.Body direction="column" css={S.modalBodyStyle}>
<Tturi />
<S.TextContainer>
<Text textType="bodyBold">{mainText}</Text>
<Text textType="detail" css={S.subTextStyle}>
{subText}
</Text>
</S.TextContainer>
</Modal.Body>
<Modal isOpen={isOpen} onCloseModal={onClose} position="bottom" boxLayoutGap="xxxl">
<Modal.Header headerPosition="center">
<S.HandleBar />
</Modal.Header>
<Modal.Body direction="column" css={S.modalBodyStyle}>
<Tturi />
<S.TextContainer>
<Text textType="bodyBold">{mainText}</Text>
<Text textType="detail" css={S.subTextStyle}>
{subText}
</Text>
</S.TextContainer>
</Modal.Body>

<Modal.Footer>
<Button
variants="secondary"
onClick={onClose}
data-cy={CYPRESS_DATA_MAP.modalBottomSheet.closeButton}
>
취소
</Button>
<Button
variants="primary"
onClick={onConfirm}
disabled={isPending}
data-cy={CYPRESS_DATA_MAP.modalBottomSheet.confirmButton}
>
{isPending ? <Spinner variants="circle" size={20} /> : "확인"}
</Button>
</Modal.Footer>
</Modal>
)
<Modal.Footer>
<Button
variants="secondary"
onClick={onClose}
data-cy={CYPRESS_DATA_MAP.modalBottomSheet.closeButton}
>
취소
</Button>
<Button
variants="primary"
onClick={onConfirm}
disabled={isPending}
data-cy={CYPRESS_DATA_MAP.modalBottomSheet.confirmButton}
>
{isPending ? <Spinner variants="circle" size={20} /> : "확인"}
</Button>
</Modal.Footer>
</Modal>
);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ export const HandleBar = styled.div`
export const modalBodyStyle = css`
align-items: flex-start;
gap: ${theme.spacing.l};
padding-bottom: ${theme.spacing.l};
`;
13 changes: 11 additions & 2 deletions frontend/src/components/pages/main/MainPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import useKeyDown from "@hooks/useKeyDown/useKeyDown";
import useMultiSelectionTag from "@hooks/useMultiSelectionTag";
import useSingleSelectionTag from "@hooks/useSingleSelectionTag";
import useTravelogueCardFocus from "@hooks/useTravelogueCardFocus";
import useUnmountAnimation from "@hooks/useUnmountAnimation";

import { ERROR_MESSAGE_MAP } from "@constants/errorMessage";
import { FORM_VALIDATIONS_MAP } from "@constants/formValidation";
Expand Down Expand Up @@ -114,6 +115,14 @@ const MainPage = () => {

const cardRefs = useTravelogueCardFocus(isFetchingNextPage);

const { isRendered: shouldSortingRender } = useUnmountAnimation({
isOpen: sorting.isModalOpen,
});

const { isRendered: shouldFilterRender } = useUnmountAnimation({
isOpen: travelPeriod.isModalOpen,
});

if (isPaused) {
alert(ERROR_MESSAGE_MAP.network);
}
Expand Down Expand Up @@ -271,7 +280,7 @@ const MainPage = () => {
? "여행기 정렬 메뉴가 열렸습니다."
: "여행기 정렬 메뉴가 닫혔습니다."}
</VisuallyHidden>
{sorting.isModalOpen && (
{shouldSortingRender && (
<SingleSelectionTagModalBottomSheet
isOpen={sorting.isModalOpen}
onClose={sorting.handleCloseModal}
Expand Down Expand Up @@ -302,7 +311,7 @@ const MainPage = () => {
? "여행기 필터 메뉴가 열렸습니다."
: "여행기 필터 메뉴가 닫혔습니다."}
</VisuallyHidden>
{travelPeriod.isModalOpen && (
{shouldFilterRender && (
<SingleSelectionTagModalBottomSheet
isOpen={travelPeriod.isModalOpen}
onClose={travelPeriod.handleCloseModal}
Expand Down
Loading

0 comments on commit e970e20

Please sign in to comment.