gnb랑 모달이 하드코딩 되어있었어서 계속 마음에 걸렸다.
그래서 뿌셨다!
우선 모달 리스트를 따로 만들어 놓았다. 여기저기서 쓰일 데이터다.
이 리스트를 받아서 gnb에서 label을 text로 표현할 것이다. 해당 모달을 오픈하기도 하고, withAuth에 따라 팝업창을 띄우기도 한다.
그리고 모달 아이템에서는 type에 따라 컨트롤할 모달, 툴팁 내용, 아이콘이 달라진다.
interface Modal {
type: string;
component: JSX.Element;
label: string;
modalIcon: typeof FontAwesomeIcon;
iconProp?: IconProp;
isFull: boolean;
withAuth: boolean;
tooltip?: Tooltip;
}
export const modalList: Modal[] = [
{
type: 'LetterModal',
component: <Letter />,
label: '편지',
modalIcon: FontAwesomeIcon,
iconProp: faEnvelope,
isFull: false,
withAuth: false,
tooltip: {
info: '오른쪽 아래 + 버튼을 눌러서 친구에게 편지를 보낼 수 있어요.',
place: 'right',
},
},
{
type: 'TodoModal',
component: <TodoList lookbackRefresher={undefined} />,
label: '오늘할일',
modalIcon: FontAwesomeIcon,
iconProp: faHighlighter,
isFull: false,
withAuth: false,
},
{
type: 'FriendModal',
component: <Friends />,
label: '친구',
modalIcon: FontAwesomeIcon,
iconProp: faUserGroup,
isFull: false,
withAuth: false,
tooltip: {
info: '+ 버튼을 눌러서 친구의 무드카드를 얻어보세요.',
place: 'right',
},
},
{
type: 'ThemeModal',
component: <ThemeStore />,
label: '색상테마',
modalIcon: FontAwesomeIcon,
iconProp: faStore,
isFull: false,
withAuth: false,
},
{
type: 'MonthlyModal',
component: <MonthlyLookback setHiddenCard={undefined} />,
label: '한달기록',
modalIcon: FontAwesomeIcon,
iconProp: faCalendarDays,
isFull: true,
withAuth: true,
},
{
type: 'LookbackModal',
component: (
<LookBack lookbackRefresh={undefined} setHiddenCard={undefined} />
),
label: '일년기록',
modalIcon: FontAwesomeIcon,
iconProp: faFilm,
isFull: true,
withAuth: true,
},
];
gnb을 나누어서 gnb item을 만들고 onClick event를 props로 받도록 했다.
그리고 logout item은 event가 다르기도 하고 auth에 따라 ui에 보이기도 안보이기도 해야해서 따로 분리해서 만들었는데, 지금 생각해보면 굳이 그러지 않았더도 됐을 것 같네...
interface GnbItemProps {
label: string;
icon: IconProp;
onClick: React.MouseEventHandler<HTMLElement>;
}
const GnbItem = (props: GnbItemProps) => {
const { label, icon, onClick } = props;
return (
<>
<NavItem onClick={onClick}>
<DarkIcon>
<FontAwesomeIcon icon={icon} size="lg" />
</DarkIcon>
<Label>{label}</Label>
</NavItem>
</>
);
};
interface ModalType {
modalType: string;
children: React.ReactNode;
}
const ModalItem: React.FC<ModalType> = ({ modalType, children }) => {
const type = modalList.find(modal => {
return modal.type === modalType;
});
const dispatch = useDispatch();
const handleCloseModal = () => {
dispatch(closeModal());
};
return (
<Style.Basic>
<Header
closeModal={handleCloseModal}
title={type?.label}
tooltip={type?.tooltip}
icon={type?.iconProp}
/>
<Style.Content>{children}</Style.Content>
</Style.Basic>
);
};
interface GnbDispatchType {
type: string;
withAuth: boolean;
}
const Gnb = () => {
const accessToken = getCookie('accessToken');
const dispatch = useDispatch();
const { authPopup, handlePopup } = usePopUp();
const handleModal = (modal: GnbDispatchType) => {
modal.withAuth && !accessToken
? handlePopup(modal.withAuth)
: dispatch(
openModal({
modalType: modal.type,
isOpen: true,
})
);
};
//withAuth & !accessToken일때 popup
return (
<>
{authPopup && <Overlay />}
<Bubble>
{modalList.map(modal => {
return (
<GnbItem
key={modal.type}
label={modal.label}
icon={modal.iconProp}
onClick={() => handleModal(modal)}
/>
);
})}
{accessToken ? <LogoutItem /> : null}
</Bubble>
</>
);
};
모달에 따라 무드카드와 나란히 띄우거나 혹은 모달 하나만 화면에 가득차거나 해야한다. 이 UI를 표현하기 위해서 수많은 노력을 했었는데, 이 방법은 isFull이 false일때는 무드카드를 함께 렌더링하고, true일때는 무드카드 대신 null을 렌더링하는 액자 UI를 만들어 끼우는 것이다.
interface FullPageProp {
isFull: boolean;
children: React.ReactNode;
}
const IsFullModal: React.FC<FullPageProp> = ({ isFull, children }) => {
return (
<ContentLayout>
{isFull ? null : (
<div>
<MoodSelector /> //이게 무드카드 컴포넌트다.
</div>
)}
<div>{children}</div>
</ContentLayout>
);
};
위에서 만든 액자를 모달 렌더링할때 끼워팔면 된다.
function GlobalModal() {
const { modalType, isOpen } = useSelector(selectModal);
if (!isOpen) {
return <IsFullModal isFull={false}>{null}</IsFullModal>;
}
const findModal = modalList.find(modal => {
return modal.type === modalType;
});
const renderModal = () => {
return findModal.component;
};
return <IsFullModal isFull={findModal.isFull}>{renderModal()}</IsFullModal>;
}