-
Notifications
You must be signed in to change notification settings - Fork 6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
공통 컴포넌트: Modal #42
Merged
Merged
공통 컴포넌트: Modal #42
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
104df9b
feat: Dimmed 컴포넌트 제작 #41
bottlewook 0484171
design: button 색상 추가 및 flex props 추가
bottlewook a8f69de
feat: useOutsideClick 커스텀훅 제작
bottlewook f2e3ca4
chore: store 설정 변경 및 경로 추가
bottlewook 7336f0a
feat: Modal 컴포넌트 제작
bottlewook 0aa0fb3
test: Modal storybook 제작 #41
bottlewook 2b91b53
chore: resolve build error
bottlewook 4c87ca7
Merge branch 'develop' into common-component-modal
bottlewook 6c891fa
rename: dimmed 폴더명 변경
bottlewook db32e00
Merge branch 'common-component-modal' of https://github.com/Kernel360…
bottlewook 0d3c9dd
chore: Flex classname prop 삭제
bottlewook File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains 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
This file contains 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
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
.container { | ||
display: flex; | ||
position: fixed; | ||
z-index: var(--modal-zindex); | ||
top: 50%; | ||
left: 50%; | ||
box-sizing: border-box; | ||
flex-direction: column; | ||
align-items: center; | ||
width: 280px; | ||
height: 220px; | ||
padding: 24px; | ||
overflow: hidden; | ||
transform: translate(-50%, -50%); | ||
border-radius: 10px; | ||
background-color: var(--white); | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import type { Meta, StoryObj } from '@storybook/react'; | ||
|
||
// eslint-disable-next-line import/no-extraneous-dependencies | ||
import { withReactContext } from 'storybook-react-context'; | ||
|
||
import { ModalContext } from '@contexts/ModalContext'; | ||
|
||
import Modal from './Modal'; | ||
|
||
const meta = { | ||
title: 'Shared/Modal', | ||
decorators: [ | ||
withReactContext({ | ||
Context: ModalContext, | ||
initialState: { | ||
open: true, | ||
title: '탈퇴하시겠습니까?', | ||
description: '회원을 탈퇴하면 차량용품 추천 서비스를 제공받을 수 없습니다. 정말로 탈퇴하시겠습니까?', | ||
topButtonLabel: '예', | ||
bottomButtonLabel: '아니오', | ||
onTopButtonClick: () => { }, | ||
onBottomButtonClick: () => { }, | ||
}, | ||
}), | ||
], | ||
component: Modal, | ||
parameters: { | ||
}, | ||
tags: ['autodocs'], | ||
argTypes: { | ||
}, | ||
} satisfies Meta<typeof Modal>; | ||
|
||
export default meta; | ||
type Story = StoryObj<typeof meta>; | ||
|
||
export const normal: Story = { | ||
args: { | ||
open: true, | ||
title: '탈퇴하시겠습니까?', | ||
description: '회원을 탈퇴하면 차량용품 추천 서비스를 제공받을 수 없습니다. 정말로 탈퇴하시겠습니까?', | ||
topButtonLabel: '예', | ||
bottomButtonLabel: '아니오', | ||
onTopButtonClick: () => { }, | ||
onBottomButtonClick: () => { }, | ||
}, | ||
}; |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
'use client'; | ||
|
||
import classNames from 'classnames/bind'; | ||
|
||
// eslint-disable-next-line import/no-cycle | ||
import useModal from '@contexts/ModalContext'; | ||
import useOutsideClick from '@hooks/useOutsideClick'; | ||
import Button from '@shared/button/Button'; | ||
import Dimmed from '@shared/dimmed/Dimmed'; | ||
import Spacing from '@shared/spacing/Spacing'; | ||
import Text from '@shared/text/Text'; | ||
|
||
import styles from './Modal.module.scss'; | ||
|
||
const cx = classNames.bind(styles); | ||
|
||
interface ModalProps { | ||
open: boolean | ||
title: React.ReactNode | ||
description: React.ReactNode | ||
topButtonLabel: React.ReactNode | ||
bottomButtonLabel: React.ReactNode | ||
onTopButtonClick: () => void | ||
onBottomButtonClick: () => void | ||
} | ||
|
||
function Modal({ | ||
// eslint-disable-next-line max-len | ||
open, title, description, topButtonLabel, bottomButtonLabel, onTopButtonClick, onBottomButtonClick, | ||
}: ModalProps) { | ||
const { close } = useModal(); | ||
const modalRef = useOutsideClick(close); | ||
|
||
if (open === false) { | ||
return null; | ||
} | ||
|
||
return ( | ||
<Dimmed> | ||
<div className={cx('container')} ref={modalRef}> | ||
<Text bold>{title}</Text> | ||
<Spacing size={8} /> | ||
<Text typography="t7" color="gray200">{description}</Text> | ||
<Spacing size={24} /> | ||
<Button onClick={onTopButtonClick} full color="secondary">{topButtonLabel}</Button> | ||
<Spacing size={12} /> | ||
<Button onClick={onBottomButtonClick} full>{bottomButtonLabel}</Button> | ||
</div> | ||
</Dimmed> | ||
); | ||
} | ||
|
||
export default Modal; |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
.container { | ||
position: fixed; | ||
z-index: var(--dimmed-zindex); | ||
background-color: rgb(0 0 0 / 70%); | ||
inset: 0; | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import classNames from 'classnames/bind'; | ||
|
||
import styles from './Dimmed.module.scss'; | ||
|
||
const cx = classNames.bind(styles); | ||
|
||
function Dimmed({ children }: { children: React.ReactNode }) { | ||
return ( | ||
<div className={cx('container')}> | ||
{children} | ||
</div> | ||
); | ||
} | ||
|
||
export default Dimmed; |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
'use client'; | ||
|
||
import { | ||
ComponentProps, createContext, useCallback, useMemo, useState, useContext, | ||
} from 'react'; | ||
import { createPortal } from 'react-dom'; | ||
|
||
// eslint-disable-next-line import/no-cycle | ||
import Modal from '@shared/Modal/Modal'; | ||
|
||
type ModalProps = ComponentProps<typeof Modal>; | ||
type ModalOptions = Omit<ModalProps, 'open'>; | ||
|
||
interface ModalContextValue { | ||
open: (options: ModalOptions) => void | ||
close: () => void | ||
} | ||
|
||
const defaultValues: ModalProps = { | ||
open: false, | ||
title: null, | ||
description: null, | ||
topButtonLabel: null, | ||
bottomButtonLabel: null, | ||
onTopButtonClick: () => { }, | ||
onBottomButtonClick: () => { }, | ||
}; | ||
|
||
export const ModalContext = createContext<ModalContextValue | undefined>(undefined); | ||
|
||
export function ModalContextProvider({ children }: { children: React.ReactNode }) { | ||
const [modalState, setModalState] = useState(defaultValues); | ||
|
||
const PORTAL_ROOT = typeof window !== 'undefined' ? document.getElementById('portal-root') : null; | ||
|
||
const close = useCallback(() => { | ||
setModalState(defaultValues); | ||
}, []); | ||
|
||
// eslint-disable-next-line max-len | ||
const open = useCallback(({ onTopButtonClick, onBottomButtonClick, ...options }: ModalOptions) => { | ||
setModalState({ | ||
...options, | ||
onTopButtonClick: () => { | ||
close(); | ||
onTopButtonClick(); | ||
}, | ||
onBottomButtonClick: () => { | ||
close(); | ||
onBottomButtonClick(); | ||
}, | ||
open: true, | ||
}); | ||
}, [close]); | ||
|
||
const values = useMemo(() => { return { open, close }; }, [open, close]); | ||
|
||
return ( | ||
<ModalContext.Provider value={values}> | ||
{children} | ||
{PORTAL_ROOT != null ? createPortal(<Modal {...modalState} />, PORTAL_ROOT) : null} | ||
</ModalContext.Provider> | ||
); | ||
} | ||
|
||
function useModal() { | ||
const values = useContext(ModalContext); | ||
|
||
if (values == null) { | ||
throw new Error('ModalContext 내부에서 사용해주세요'); | ||
} | ||
|
||
return values; | ||
} | ||
|
||
export default useModal; |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import { useEffect, useRef } from 'react'; | ||
|
||
const useOutsideClick = (callback: () => void) => { | ||
const ref = useRef<HTMLDivElement>(null); | ||
|
||
useEffect(() => { | ||
const handleClickOutside = (event: MouseEvent) => { | ||
if (ref.current && !ref.current.contains(event.target as Node)) { | ||
callback(); | ||
} | ||
}; | ||
|
||
document.addEventListener('mousedown', handleClickOutside); | ||
|
||
return () => { | ||
document.removeEventListener('mousedown', handleClickOutside); | ||
}; | ||
}, [callback]); | ||
|
||
return ref; | ||
}; | ||
|
||
export default useOutsideClick; |
This file contains 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
This file contains 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
This file contains 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
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
시맨틱 태그를 위해서 모달 컨텐츠는 article태그나 dialog태그는 어떤가요??
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
레퍼런스 많이 찾아봤는데 div 태그를 많이 사용하시길래 저도 div로 작성했어요
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
아하 알겠습니다