-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: toast 구현 * chore: 스토리북 버튼 수정
- Loading branch information
Showing
11 changed files
with
213 additions
and
0 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
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
Binary file not shown.
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,38 @@ | ||
import { type Meta } from '@storybook/react'; | ||
|
||
import Button from '../button/Button'; | ||
import WarningIcon from '../icons/WarningIcon'; | ||
import Toast from './Toast'; | ||
import useToast from './useToast'; | ||
|
||
const meta: Meta<typeof Toast.Content> = { | ||
title: 'Toast', | ||
component: Toast.Content, | ||
}; | ||
|
||
export default meta; | ||
|
||
export function Default() { | ||
const { fireToast } = useToast(); | ||
|
||
return ( | ||
<div> | ||
<Button onClick={() => fireToast({ content: '토스트 메세지 입니다' })}>토스트 발사</Button> | ||
<Button | ||
onClick={() => | ||
fireToast({ | ||
content: ( | ||
<> | ||
<WarningIcon /> | ||
<Toast.Text>토스트 메시지 with 아이콘</Toast.Text> | ||
</> | ||
), | ||
higherThanCTA: true, | ||
}) | ||
} | ||
> | ||
다른 토스트 발사 | ||
</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,39 @@ | ||
import { css, type Theme } from '@emotion/react'; | ||
|
||
import { type ToastProps } from '~/store/toast'; | ||
import { BODY_1 } from '~/styles/typo'; | ||
|
||
const Content = ({ content }: Pick<ToastProps, 'content'>) => { | ||
return <div css={toastCss}>{typeof content === 'string' ? <Toast.Text>{content}</Toast.Text> : content}</div>; | ||
}; | ||
|
||
const toastCss = css` | ||
display: flex; | ||
gap: 8px; | ||
align-items: center; | ||
justify-content: center; | ||
height: 56px; | ||
padding: 16px 20px; | ||
background-color: #394258cc; | ||
backdrop-filter: blur(4px); | ||
border-radius: 44px; | ||
`; | ||
|
||
const Text = ({ children }: { children: string }) => { | ||
return <span css={toastTextCss}>{children}</span>; | ||
}; | ||
|
||
const toastTextCss = ({ colors }: Theme) => css` | ||
${BODY_1}; | ||
color: ${colors.white}; | ||
`; | ||
|
||
const Toast = { | ||
Content, | ||
Text, | ||
}; | ||
|
||
export default Toast; |
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 { css } from '@emotion/react'; | ||
import { AnimatePresence, m } from 'framer-motion'; | ||
import { useAtomValue } from 'jotai'; | ||
|
||
import { defaultFadeInUpVariants } from '~/constants/motions'; | ||
import { toastAtom } from '~/store/toast'; | ||
|
||
import Toast from './Toast'; | ||
|
||
const CTA_HEIGHT = 92; | ||
const CTA_PADDING = 8; | ||
const BOTTOM_SAFETY_HEIGHT = 24; | ||
|
||
const ToastWrapper = () => { | ||
const toast = useAtomValue(toastAtom); | ||
|
||
return ( | ||
<div> | ||
<AnimatePresence mode="wait"> | ||
{toast && ( | ||
<m.div | ||
key={String(toast.content)} | ||
css={toastContainerCss(toast.higherThanCTA ?? false)} | ||
variants={defaultFadeInUpVariants} | ||
initial="initial" | ||
animate="animate" | ||
exit="exit" | ||
> | ||
<Toast.Content content={toast.content} /> | ||
</m.div> | ||
)} | ||
</AnimatePresence> | ||
</div> | ||
); | ||
}; | ||
|
||
export default ToastWrapper; | ||
|
||
const toastContainerCss = (higherThanCTA: boolean) => css` | ||
position: fixed; | ||
bottom: ${higherThanCTA ? CTA_HEIGHT + CTA_PADDING : BOTTOM_SAFETY_HEIGHT}px; | ||
left: 0; | ||
display: flex; | ||
justify-content: center; | ||
width: 100%; | ||
height: fit-content; | ||
`; |
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,35 @@ | ||
import { type ReactElement, useCallback } from 'react'; | ||
import { useAtom } from 'jotai'; | ||
|
||
import { toastAtom } from '~/store/toast'; | ||
|
||
interface FireToast { | ||
content: string | ReactElement; | ||
duration?: number; | ||
higherThanCTA?: boolean; | ||
} | ||
|
||
const DEFAULT_DURATION = 1500; | ||
|
||
const useToast = () => { | ||
const [toast, setToast] = useAtom(toastAtom); | ||
|
||
const removeToast = useCallback((id: string) => { | ||
setToast((prev) => { | ||
if (!prev) return null; | ||
if (prev.id === id) return null; | ||
|
||
return prev; | ||
}); | ||
}, []); | ||
|
||
const fireToast = useCallback(({ content, duration = DEFAULT_DURATION, higherThanCTA }: FireToast) => { | ||
const id = new Date().getTime().toString(); | ||
setToast({ id, content, higherThanCTA }); | ||
setTimeout(() => removeToast(id), duration); | ||
}, []); | ||
|
||
return { toast, fireToast }; | ||
}; | ||
|
||
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,10 @@ | ||
import { type ReactElement } from 'react'; | ||
import { atom } from 'jotai'; | ||
|
||
export interface ToastProps { | ||
id: string; | ||
content: string | ReactElement; | ||
higherThanCTA?: boolean; | ||
} | ||
|
||
export const toastAtom = atom<ToastProps | null>(null); |
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