Skip to content

Commit dd375cc

Browse files
authored
Merge pull request #143 from Boggle-Boggle/feat/136 : 토스트 컴포넌트 구현
[Feat/136] 토스트 컴포넌트 구현
2 parents 26b7bb2 + e09a294 commit dd375cc

15 files changed

Lines changed: 289 additions & 90 deletions

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
1010
"preview": "vite preview",
1111
"storybook": "storybook dev -p 6006",
12-
"build-storybook": "storybook build",
13-
"build:icons": "npx @svgr/cli ./src/assets/refactor/icons --out-dir ./src/components/icons --icon --replace-attr-values \"#303030=currentColor\" --no-prettier --ext tsx --typescript"
12+
"icons": "npx @svgr/cli ./src/assets/refactor/icons --out-dir ./src/components/icons --replace-attr-values \"#303030=currentColor\"--ignore-existing --typescript --index-template svgr-template.js",
13+
"build-storybook": "storybook build"
1414
},
1515
"dependencies": {
1616
"@react-three/drei": "^9.117.3",

src/App.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { Outlet } from 'react-router-dom';
2+
3+
import ToastContainer from 'components/refactor/Toast/ToastContainer';
4+
5+
const App = () => {
6+
return (
7+
<>
8+
<Outlet />
9+
<ToastContainer />
10+
</>
11+
);
12+
};
13+
14+
export default App;

src/components/icons/index.tsx

Lines changed: 56 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,56 @@
1-
export { default as ArrowBack } from './ArrowBack'
2-
export { default as ArrowDown } from './ArrowDown'
3-
export { default as ArrowDownFilled } from './ArrowDownFilled'
4-
export { default as ArrowLeft } from './ArrowLeft'
5-
export { default as ArrowLeftFilled } from './ArrowLeftFilled'
6-
export { default as ArrowNext } from './ArrowNext'
7-
export { default as ArrowRight } from './ArrowRight'
8-
export { default as ArrowRightFilled } from './ArrowRightFilled'
9-
export { default as ArrowUp } from './ArrowUp'
10-
export { default as ArrowUpFilled } from './ArrowUpFilled'
11-
export { default as Bbaegok } from './Bbaegok'
12-
export { default as Book } from './Book'
13-
export { default as BookPlus } from './BookPlus'
14-
export { default as BookSearch } from './BookSearch'
15-
export { default as BookSearchFilled } from './BookSearchFilled'
16-
export { default as Bookmark } from './Bookmark'
17-
export { default as Calendar } from './Calendar'
18-
export { default as Cancel } from './Cancel'
19-
export { default as Capture } from './Capture'
20-
export { default as CircleCancel } from './CircleCancel'
21-
export { default as CircleCancelFilled } from './CircleCancelFilled'
22-
export { default as CircleCheck } from './CircleCheck'
23-
export { default as CircleDelete } from './CircleDelete'
24-
export { default as CircleInfo } from './CircleInfo'
25-
export { default as CirclePlus } from './CirclePlus'
26-
export { default as Comment } from './Comment'
27-
export { default as Edit } from './Edit'
28-
export { default as EllipsisHorizontal } from './EllipsisHorizontal'
29-
export { default as EllipsisVertical } from './EllipsisVertical'
30-
export { default as File } from './File'
31-
export { default as FileCopy } from './FileCopy'
32-
export { default as Graduation } from './Graduation'
33-
export { default as Headphone } from './Headphone'
34-
export { default as Heart } from './Heart'
35-
export { default as HeartFilled } from './HeartFilled'
36-
export { default as Highlight } from './Highlight'
37-
export { default as Home } from './Home'
38-
export { default as HomeFilled } from './HomeFilled'
39-
export { default as LayoutGrid } from './LayoutGrid'
40-
export { default as LayoutList } from './LayoutList'
41-
export { default as Library } from './Library'
42-
export { default as LibraryFilled } from './LibraryFilled'
43-
export { default as Menu } from './Menu'
44-
export { default as Note } from './Note'
45-
export { default as Pen } from './Pen'
46-
export { default as Pin } from './Pin'
47-
export { default as PinFilled } from './PinFilled'
48-
export { default as Plus } from './Plus'
49-
export { default as Rating } from './Rating'
50-
export { default as RatingFilled } from './RatingFilled'
51-
export { default as Scan } from './Scan'
52-
export { default as Search } from './Search'
53-
export { default as Tag } from './Tag'
54-
export { default as Trash } from './Trash'
55-
export { default as User } from './User'
56-
export { default as UserFilled } from './UserFilled'
1+
export { default as IconArrowBack } from './ArrowBack'
2+
export { default as IconArrowDown } from './ArrowDown'
3+
export { default as IconArrowDownFilled } from './ArrowDownFilled'
4+
export { default as IconArrowLeft } from './ArrowLeft'
5+
export { default as IconArrowLeftFilled } from './ArrowLeftFilled'
6+
export { default as IconArrowNext } from './ArrowNext'
7+
export { default as IconArrowRight } from './ArrowRight'
8+
export { default as IconArrowRightFilled } from './ArrowRightFilled'
9+
export { default as IconArrowUp } from './ArrowUp'
10+
export { default as IconArrowUpFilled } from './ArrowUpFilled'
11+
export { default as IconBbaegok } from './Bbaegok'
12+
export { default as IconBook } from './Book'
13+
export { default as IconBookPlus } from './BookPlus'
14+
export { default as IconBookSearch } from './BookSearch'
15+
export { default as IconBookSearchFilled } from './BookSearchFilled'
16+
export { default as IconBookmark } from './Bookmark'
17+
export { default as IconCalendar } from './Calendar'
18+
export { default as IconCancel } from './Cancel'
19+
export { default as IconCapture } from './Capture'
20+
export { default as IconCircleCancel } from './CircleCancel'
21+
export { default as IconCircleCancelFilled } from './CircleCancelFilled'
22+
export { default as IconCircleCheck } from './CircleCheck'
23+
export { default as IconCircleDelete } from './CircleDelete'
24+
export { default as IconCircleInfo } from './CircleInfo'
25+
export { default as IconCirclePlus } from './CirclePlus'
26+
export { default as IconComment } from './Comment'
27+
export { default as IconEdit } from './Edit'
28+
export { default as IconEllipsisHorizontal } from './EllipsisHorizontal'
29+
export { default as IconEllipsisVertical } from './EllipsisVertical'
30+
export { default as IconFile } from './File'
31+
export { default as IconFileCopy } from './FileCopy'
32+
export { default as IconGraduation } from './Graduation'
33+
export { default as IconHeadphone } from './Headphone'
34+
export { default as IconHeart } from './Heart'
35+
export { default as IconHeartFilled } from './HeartFilled'
36+
export { default as IconHighlight } from './Highlight'
37+
export { default as IconHome } from './Home'
38+
export { default as IconHomeFilled } from './HomeFilled'
39+
export { default as IconLayoutGrid } from './LayoutGrid'
40+
export { default as IconLayoutList } from './LayoutList'
41+
export { default as IconLibrary } from './Library'
42+
export { default as IconLibraryFilled } from './LibraryFilled'
43+
export { default as IconMenu } from './Menu'
44+
export { default as IconNote } from './Note'
45+
export { default as IconPen } from './Pen'
46+
export { default as IconPin } from './Pin'
47+
export { default as IconPinFilled } from './PinFilled'
48+
export { default as IconPlus } from './Plus'
49+
export { default as IconRating } from './Rating'
50+
export { default as IconRatingFilled } from './RatingFilled'
51+
export { default as IconScan } from './Scan'
52+
export { default as IconSearch } from './Search'
53+
export { default as IconTag } from './Tag'
54+
export { default as IconTrash } from './Trash'
55+
export { default as IconUser } from './User'
56+
export { default as IconUserFilled } from './UserFilled'

src/components/refactor/CheckBox.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ const CheckBox = ({ size = 'medium', variant = 'primary', checked = false, onCha
1010
variant === 'primary'
1111
? 'border-neutral-40 peer-checked:bg-primary '
1212
: 'border-neutral-60 peer-checked:bg-neutral-80 ';
13-
const sizeClass = size === 'small' ? 'h-3 w-3 text-body2' : 'h-6 w-6 text-title2';
13+
const sizeClass = size === 'small' ? 'size-3 text-body2' : 'size-6 text-title2';
1414

1515
return (
1616
<label htmlFor="checkbox">

src/components/refactor/Radio.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ const Radio = ({ id, name, checked, onChange, size = 'medium', variant = 'primar
1515
: 'border-neutral-80 peer-checked:border-neutral-80';
1616

1717
const sizeInnerClass = size === 'small' ? 'h-[10.7px] w-[10.7px]' : 'h-[15px] w-[15px]';
18-
const sizeOuterClass = size === 'small' ? 'h-4 w-4' : 'h-6 w-6';
18+
const sizeOuterClass = size === 'small' ? 'size-4' : 'size-6';
1919

2020
return (
2121
<label htmlFor={id}>
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/* eslint-disable react/jsx-props-no-spreading */
2+
import { useToastStore } from 'stores/useToastStore';
3+
4+
import Toast from '.';
5+
6+
const ToastContainer = () => {
7+
const { toasts } = useToastStore();
8+
9+
return (
10+
<section className="fixed bottom-6 z-toast flex w-full max-w-mobile flex-col px-mobile">
11+
{toasts.map((toast) => {
12+
return (
13+
<div key={toast.id} className="mt-1">
14+
<Toast {...toast} />
15+
</div>
16+
);
17+
})}
18+
</section>
19+
);
20+
};
21+
22+
export default ToastContainer;
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { useEffect, useState } from 'react';
2+
3+
import { IconCircleCancel, IconCircleCheck, IconCircleInfo } from 'components/icons';
4+
5+
export type ToastProps = {
6+
type: 'info' | 'error' | 'success';
7+
description: string;
8+
title?: string;
9+
// dismissible?: boolean;
10+
};
11+
12+
const Toast = ({ type, description, title }: ToastProps) => {
13+
const [isLeaving, setIsLeaving] = useState(false);
14+
15+
useEffect(() => {
16+
const timer = setTimeout(() => {
17+
setIsLeaving(true);
18+
}, 2000);
19+
20+
return () => clearTimeout(timer);
21+
}, []);
22+
23+
const borderClass = type === 'info' ? 'border-information' : type === 'error' ? 'border-danger' : 'border-primary';
24+
const icon =
25+
type === 'info' ? (
26+
<IconCircleInfo className="mr-1 text-information" />
27+
) : type === 'error' ? (
28+
<IconCircleCancel className="mr-1 text-danger" />
29+
) : (
30+
<IconCircleCheck className="mr-1 text-primary" />
31+
);
32+
33+
return (
34+
<div className={`${borderClass} ${isLeaving ? 'animate-fadeOut' : 'animate-fadeIn'} rounded-xl border px-4 py-2`}>
35+
{title && (
36+
<div className="mb-1 flex items-center text-body2 text-neutral-80">
37+
{icon}
38+
{title}
39+
</div>
40+
)}
41+
<div className={`flex items-center text-caption1 ${title ? 'text-neutral-60' : 'text-neutral-80'}`}>
42+
{!title && icon}
43+
{description}
44+
</div>
45+
</div>
46+
);
47+
};
48+
49+
export default Toast;

src/main.tsx

Lines changed: 32 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -24,44 +24,50 @@ import Record from 'pages/Record';
2424
import Search from 'pages/Search';
2525
import SignUp from 'pages/SignUp';
2626

27-
import WithBottomNavLayout from './WithBottomNavLayout';
28-
import WithoutBottomNavLayout from './WithoutBottomNavLayout';
27+
import WithBottomNavLayout from 'pages/Layout/WithBottomNavLayout';
28+
import WithoutBottomNavLayout from 'pages/Layout/WithoutBottomNavLayout';
2929

3030
import './main.css';
31-
31+
import App from './App';
3232
const router = createBrowserRouter([
3333
{
3434
path: '/',
35-
element: <PrivateRoute />,
35+
element: <App />,
3636
children: [
3737
{
38-
element: <WithBottomNavLayout />,
39-
children: [
40-
{ path: '/', element: <Home /> },
41-
{ path: 'library', element: <Library /> },
42-
{ path: 'myPage', element: <MyPage /> },
43-
{ path: 'search', element: <Search /> },
44-
{ path: 'detail/:detailId', element: <BookDetail /> },
45-
{ path: 'record/:recordId', element: <Record /> },
46-
{ path: 'edit/:recordId', element: <Edit /> },
47-
],
48-
},
49-
{
50-
element: <WithoutBottomNavLayout />,
38+
path: '/',
39+
element: <PrivateRoute />,
5140
children: [
52-
{ path: 'note/write', element: <Note /> },
53-
{ path: 'myPage/nickname', element: <EditNickname /> },
54-
{ path: 'myPage/terms', element: <Term /> },
55-
{ path: 'myPage/VersionInfo', element: <VersionInfo /> },
56-
{ path: 'myPage/deleteAccount', element: <DeleteAccount /> },
57-
{ path: 'myPage/QnA', element: <QnA /> },
41+
{
42+
element: <WithBottomNavLayout />,
43+
children: [
44+
{ path: '/', element: <Home /> },
45+
{ path: 'library', element: <Library /> },
46+
{ path: 'myPage', element: <MyPage /> },
47+
{ path: 'search', element: <Search /> },
48+
{ path: 'detail/:detailId', element: <BookDetail /> },
49+
{ path: 'record/:recordId', element: <Record /> },
50+
{ path: 'edit/:recordId', element: <Edit /> },
51+
],
52+
},
53+
{
54+
element: <WithoutBottomNavLayout />,
55+
children: [
56+
{ path: 'note/write', element: <Note /> },
57+
{ path: 'myPage/nickname', element: <EditNickname /> },
58+
{ path: 'myPage/terms', element: <Term /> },
59+
{ path: 'myPage/VersionInfo', element: <VersionInfo /> },
60+
{ path: 'myPage/deleteAccount', element: <DeleteAccount /> },
61+
{ path: 'myPage/QnA', element: <QnA /> },
62+
],
63+
},
5864
],
5965
},
66+
{ path: 'login', element: <Login /> },
67+
{ path: 'signUp', element: <SignUp /> },
68+
{ path: 'oauth/redirect', element: <Auth /> },
6069
],
6170
},
62-
{ path: 'login', element: <Login /> },
63-
{ path: 'signUp', element: <SignUp /> },
64-
{ path: 'oauth/redirect', element: <Auth /> },
6571
]);
6672

6773
const queryClient = new QueryClient({
File renamed without changes.

0 commit comments

Comments
 (0)