-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: 토스트 구현 * feat: portal 구현 * chore: 파일 이름 변경 * chore: 변경된 파일 이름에 따라 import명 변경 * refactor: animation 분리 * feat: 에러일 때 스타일 추가 * refactor: 기본, 에러 버전 구현 * refactor: 리뷰 반영 * refactor: toast context로 변경 * refactor: context action과 value로 분리 * refactor: toast context에서 portal 실행되도록 변경 * refactor: portal 위치 변경 * refactor: context를 사용해 스토리북 변경 * chore: 필요없는 fragment 삭제 * refactor: 구조 분해 할당으로 변경 * refactor: style 이름 변경 * refactor: 사용하지 않는 action 삭제 * refactor: useToast로 분리 Co-authored-by: Leejin Yang <[email protected]> * chore: 사용하지 않는 테스트 버튼 삭제 --------- Co-authored-by: Leejin Yang <[email protected]>
- Loading branch information
1 parent
062ca02
commit a1a27dc
Showing
16 changed files
with
1,593 additions
and
1,299 deletions.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -102,3 +102,4 @@ | |
</symbol> | ||
</svg> | ||
</div> | ||
<div id="toast-container"></div> |
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 |
---|---|---|
|
@@ -27,5 +27,6 @@ | |
</head> | ||
<body> | ||
<div id="root"></div> | ||
<div id="toast-container"></div> | ||
</body> | ||
</html> |
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,49 @@ | ||
import type { Meta, StoryObj } from '@storybook/react'; | ||
|
||
import Toast from './Toast'; | ||
|
||
import ToastProvider from '@/contexts/ToastContext'; | ||
import { useToastActionContext } from '@/hooks/context'; | ||
|
||
const meta: Meta<typeof Toast> = { | ||
title: 'common/Toast', | ||
component: Toast, | ||
decorators: [ | ||
(Story) => ( | ||
<ToastProvider> | ||
<Story /> | ||
</ToastProvider> | ||
), | ||
], | ||
}; | ||
|
||
export default meta; | ||
type Story = StoryObj<typeof Toast>; | ||
|
||
export const Default: Story = { | ||
render: () => { | ||
const { toast } = useToastActionContext(); | ||
const handleClick = () => { | ||
toast.success('성공'); | ||
}; | ||
return ( | ||
<div style={{ width: '375px' }}> | ||
<button onClick={handleClick}>토스트 성공</button> | ||
</div> | ||
); | ||
}, | ||
}; | ||
|
||
export const Error: Story = { | ||
render: () => { | ||
const { toast } = useToastActionContext(); | ||
const handleClick = () => { | ||
toast.error('실패'); | ||
}; | ||
return ( | ||
<div style={{ width: '375px' }}> | ||
<button onClick={handleClick}>토스트 에러</button> | ||
</div> | ||
); | ||
}, | ||
}; |
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,41 @@ | ||
import { Text, useTheme } from '@fun-eat/design-system'; | ||
import styled from 'styled-components'; | ||
|
||
import { useToast } from '@/hooks/common'; | ||
import { fadeOut, slideIn } from '@/styles/animations'; | ||
|
||
interface ToastProps { | ||
id: number; | ||
message: string; | ||
isError?: boolean; | ||
} | ||
|
||
const Toast = ({ id, message, isError = false }: ToastProps) => { | ||
const theme = useTheme(); | ||
const isShown = useToast(id); | ||
|
||
return ( | ||
<ToastWrapper isError={isError} isAnimating={isShown}> | ||
<Message color={theme.colors.white}>{message}</Message> | ||
</ToastWrapper> | ||
); | ||
}; | ||
|
||
export default Toast; | ||
|
||
type ToastStyleProps = Pick<ToastProps, 'isError'> & { isAnimating?: boolean }; | ||
|
||
const ToastWrapper = styled.div<ToastStyleProps>` | ||
position: relative; | ||
width: 100%; | ||
height: 55px; | ||
max-width: 560px; | ||
border-radius: 10px; | ||
background: ${({ isError, theme }) => (isError ? theme.colors.error : theme.colors.black)}; | ||
animation: ${({ isAnimating }) => (isAnimating ? slideIn : fadeOut)} 0.3s ease-in-out forwards; | ||
`; | ||
|
||
const Message = styled(Text)` | ||
margin-left: 20px; | ||
line-height: 55px; | ||
`; |
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,80 @@ | ||
import type { PropsWithChildren } from 'react'; | ||
import { createContext, useState } from 'react'; | ||
import { createPortal } from 'react-dom'; | ||
import styled from 'styled-components'; | ||
|
||
import { Toast } from '@/components/Common'; | ||
|
||
interface ToastState { | ||
id: number; | ||
message: string; | ||
isError?: boolean; | ||
} | ||
|
||
interface ToastValue { | ||
toasts: ToastState[]; | ||
} | ||
interface ToastAction { | ||
toast: { | ||
success: (message: string) => void; | ||
error: (message: string) => void; | ||
}; | ||
deleteToast: (id: number) => void; | ||
} | ||
|
||
export const ToastValueContext = createContext<ToastValue | null>(null); | ||
export const ToastActionContext = createContext<ToastAction | null>(null); | ||
|
||
const ToastProvider = ({ children }: PropsWithChildren) => { | ||
const [toasts, setToasts] = useState<ToastState[]>([]); | ||
|
||
const showToast = (id: number, message: string, isError?: boolean) => { | ||
setToasts([...toasts, { id, message, isError }]); | ||
}; | ||
|
||
const deleteToast = (id: number) => { | ||
setToasts((prevToasts) => prevToasts.filter((toast) => toast.id !== id)); | ||
}; | ||
|
||
const toast = { | ||
success: (message: string) => showToast(Number(Date.now()), message), | ||
error: (message: string) => showToast(Number(Date.now()), message, true), | ||
}; | ||
|
||
const toastValue = { | ||
toasts, | ||
}; | ||
|
||
const toastAction = { | ||
toast, | ||
deleteToast, | ||
}; | ||
|
||
return ( | ||
<ToastActionContext.Provider value={toastAction}> | ||
<ToastValueContext.Provider value={toastValue}> | ||
{children} | ||
{createPortal( | ||
<ToastContainer> | ||
{toasts.map(({ id, message, isError }) => ( | ||
<Toast key={id} id={id} message={message} isError={isError} /> | ||
))} | ||
</ToastContainer>, | ||
document.getElementById('toast-container') as HTMLElement | ||
)} | ||
</ToastValueContext.Provider> | ||
</ToastActionContext.Provider> | ||
); | ||
}; | ||
|
||
export default ToastProvider; | ||
|
||
const ToastContainer = styled.div` | ||
position: fixed; | ||
z-index: 1000; | ||
display: flex; | ||
flex-direction: column; | ||
align-items: center; | ||
width: calc(100% - 20px); | ||
transform: translate(0, -10px); | ||
`; |
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,37 @@ | ||
import { useEffect, useRef, useState } from 'react'; | ||
|
||
import { useToastActionContext } from '../context'; | ||
|
||
const useToast = (id: number) => { | ||
const { deleteToast } = useToastActionContext(); | ||
const [isShown, setIsShown] = useState(true); | ||
|
||
const showTimeoutRef = useRef<number | null>(null); | ||
const deleteTimeoutRef = useRef<number | null>(null); | ||
|
||
useEffect(() => { | ||
showTimeoutRef.current = window.setTimeout(() => setIsShown(false), 2000); | ||
|
||
return () => { | ||
if (showTimeoutRef.current) { | ||
clearTimeout(showTimeoutRef.current); | ||
} | ||
}; | ||
}, []); | ||
|
||
useEffect(() => { | ||
if (!isShown) { | ||
deleteTimeoutRef.current = window.setTimeout(() => deleteToast(id), 2000); | ||
} | ||
|
||
return () => { | ||
if (deleteTimeoutRef.current) { | ||
clearTimeout(deleteTimeoutRef.current); | ||
} | ||
}; | ||
}, [isShown]); | ||
|
||
return isShown; | ||
}; | ||
|
||
export default useToast; |
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,14 @@ | ||
import { useContext } from 'react'; | ||
|
||
import { ToastActionContext } from '@/contexts/ToastContext'; | ||
|
||
const useToastActionContext = () => { | ||
const toastAction = useContext(ToastActionContext); | ||
if (toastAction === null || toastAction === undefined) { | ||
throw new Error('useToastActionContext는 Toast Provider 안에서 사용해야 합니다.'); | ||
} | ||
|
||
return toastAction; | ||
}; | ||
|
||
export default useToastActionContext; |
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,14 @@ | ||
import { useContext } from 'react'; | ||
|
||
import { ToastValueContext } from '@/contexts/ToastContext'; | ||
|
||
const useToastValueContext = () => { | ||
const toastValue = useContext(ToastValueContext); | ||
if (toastValue === null || toastValue === undefined) { | ||
throw new Error('useToastValueContext는 Toast Provider 안에서 사용해야 합니다.'); | ||
} | ||
|
||
return toastValue; | ||
}; | ||
|
||
export default useToastValueContext; |
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,23 @@ | ||
import { keyframes } from 'styled-components'; | ||
|
||
export const slideIn = keyframes` | ||
0% { | ||
transform: translateY(-100px); | ||
} | ||
100% { | ||
transform: translateY(70px); | ||
} | ||
`; | ||
|
||
export const fadeOut = keyframes` | ||
0% { | ||
transform: translateY(70px); | ||
opacity: 1; | ||
} | ||
100% { | ||
transform: translateY(70px); | ||
opacity:0; | ||
} | ||
`; |
File renamed without changes.
Oops, something went wrong.