diff --git a/frontend/package.json b/frontend/package.json index 358f1fe4b..335158204 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -13,7 +13,7 @@ "test:coverage": "jest --watchAll --coverage" }, "dependencies": { - "@fun-eat/design-system": "^0.3.18", + "@fun-eat/design-system": "^0.4.1", "@tanstack/react-query": "^4.32.6", "@tanstack/react-query-devtools": "^4.32.6", "browser-image-compression": "^2.0.2", diff --git a/frontend/src/components/Common/Carousel/Carousel.stories.tsx b/frontend/src/components/Common/Carousel/Carousel.stories.tsx deleted file mode 100644 index 8d7eccf6f..000000000 --- a/frontend/src/components/Common/Carousel/Carousel.stories.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/react'; - -import Carousel from './Carousel'; - -import { RecipeItem } from '@/components/Recipe'; -import mockRecipe from '@/mocks/data/recipes.json'; - -const meta: Meta = { - title: 'common/Carousel', - component: Carousel, -}; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - args: { - carouselList: [ - { - id: 0, - children:
1
, - }, - { - id: 1, - children:
2
, - }, - { - id: 2, - children:
3
, - }, - ], - }, -}; - -export const RecipeRanking: Story = { - args: { - carouselList: [ - { - id: 0, - children: , - }, - { - id: 1, - children: , - }, - { - id: 2, - children: , - }, - ], - }, -}; diff --git a/frontend/src/components/Common/Carousel/Carousel.tsx b/frontend/src/components/Common/Carousel/Carousel.tsx deleted file mode 100644 index 038b5cefa..000000000 --- a/frontend/src/components/Common/Carousel/Carousel.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import { useEffect, useState } from 'react'; -import styled from 'styled-components'; - -import type { CarouselChildren } from '@/types/common'; - -interface CarouselProps { - carouselList: CarouselChildren[]; -} - -const Carousel = ({ carouselList }: CarouselProps) => { - const extendedCarouselList = [...carouselList, carouselList[0]]; - const [currentIndex, setCurrentIndex] = useState(0); - - const CAROUSEL_WIDTH = window.innerWidth; - - const showNextSlide = () => { - setCurrentIndex((prev) => (prev === carouselList.length ? 0 : prev + 1)); - }; - - useEffect(() => { - const timer = setInterval(showNextSlide, 2000); - - return () => clearInterval(timer); - }, [currentIndex]); - - return ( - - - {extendedCarouselList.map(({ id, children }, index) => ( - - {children} - - ))} - - - ); -}; - -export default Carousel; - -const CarouselContainer = styled.div` - display: flex; - width: 100%; - border: 1px solid ${({ theme }) => theme.colors.gray2}; - border-radius: 10px; - overflow: hidden; -`; - -const CarouselWrapper = styled.ul` - display: flex; -`; - -const CarouselItem = styled.li` - height: fit-content; -`; diff --git a/frontend/src/components/Common/ImageUploader/ImageUploader.tsx b/frontend/src/components/Common/ImageUploader/ImageUploader.tsx index 9c915081e..b139a1b4c 100644 --- a/frontend/src/components/Common/ImageUploader/ImageUploader.tsx +++ b/frontend/src/components/Common/ImageUploader/ImageUploader.tsx @@ -1,10 +1,9 @@ -import { Button } from '@fun-eat/design-system'; +import { Button, useToastActionContext } from '@fun-eat/design-system'; import type { ChangeEventHandler } from 'react'; import styled from 'styled-components'; import { IMAGE_MAX_SIZE } from '@/constants'; import { useEnterKeyDown } from '@/hooks/common'; -import { useToastActionContext } from '@/hooks/context'; interface ReviewImageUploaderProps { previewImage: string; diff --git a/frontend/src/components/Common/Input/Input.stories.tsx b/frontend/src/components/Common/Input/Input.stories.tsx deleted file mode 100644 index 48e8856a3..000000000 --- a/frontend/src/components/Common/Input/Input.stories.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/react'; - -import Input from './Input'; -import SvgIcon from '../Svg/SvgIcon'; - -const meta: Meta = { - title: 'common/Input', - component: Input, - argTypes: { - rightIcon: { - control: { type: 'boolean' }, - mapping: { false: '', true: }, - }, - }, - args: { - customWidth: '300px', - isError: false, - rightIcon: false, - errorMessage: '', - }, -}; - -export default meta; -type Story = StoryObj; - -export const Default: Story = {}; - -export const WithPlaceholder: Story = { - args: { - placeholder: '상품 이름을 검색하세요.', - }, -}; - -export const WithIcon: Story = { - args: { - placeholder: '상품 이름을 검색하세요.', - rightIcon: true, - }, -}; - -export const Error: Story = { - args: { - isError: true, - errorMessage: '10글자 이내로 입력해주세요.', - }, -}; - -export const Disabled: Story = { - render: () => , -}; diff --git a/frontend/src/components/Common/Input/Input.tsx b/frontend/src/components/Common/Input/Input.tsx deleted file mode 100644 index c3b3b40f1..000000000 --- a/frontend/src/components/Common/Input/Input.tsx +++ /dev/null @@ -1,97 +0,0 @@ -import { Text, theme } from '@fun-eat/design-system'; -import type { ComponentPropsWithRef, ForwardedRef, ReactNode } from 'react'; -import { forwardRef } from 'react'; -import styled from 'styled-components'; - -interface InputProps extends ComponentPropsWithRef<'input'> { - /** - * Input 컴포넌트의 너비값입니다. - */ - customWidth?: string; - /** - * Input 컴포넌트의 최소 너비값입니다. - */ - minWidth?: string; - /** - * Input value에 에러가 있는지 여부입니다. - */ - isError?: boolean; - /** - * Input 컴포넌트 오른쪽에 위치할 아이콘입니다. - */ - rightIcon?: ReactNode; - /** - * isError가 true일 때 보여줄 에러 메시지입니다. - */ - errorMessage?: string; -} - -const Input = forwardRef( - ( - { customWidth = '300px', minWidth, isError = false, rightIcon, errorMessage, ...props }: InputProps, - ref: ForwardedRef - ) => { - return ( - <> - - - {rightIcon && {rightIcon}} - - {isError && {errorMessage}} - - ); - } -); - -Input.displayName = 'Input'; - -export default Input; - -type InputContainerStyleProps = Pick; -type CustomInputStyleProps = Pick; - -const InputContainer = styled.div` - position: relative; - min-width: ${({ minWidth }) => minWidth ?? 0}; - max-width: ${({ customWidth }) => customWidth}; - text-align: center; -`; - -const CustomInput = styled.input` - width: 100%; - height: 40px; - padding: 10px 0 10px 12px; - color: ${({ isError }) => (isError ? theme.colors.error : theme.textColors.default)}; - border: 1px solid ${({ isError }) => (isError ? theme.colors.error : theme.borderColors.default)}; - border-radius: 5px; - - &:focus { - border: 2px solid ${({ isError }) => (isError ? theme.colors.error : theme.borderColors.strong)}; - outline: none; - } - - &:disabled { - border: 1px solid ${({ theme }) => theme.borderColors.disabled}; - background: ${({ theme }) => theme.colors.gray1}; - } - - &::placeholder { - color: ${theme.textColors.disabled}; - font-size: ${theme.fontSizes.sm}; - } -`; - -const IconWrapper = styled.div` - position: absolute; - top: 0; - right: 0; - display: flex; - align-items: center; - height: 100%; - margin-right: 8px; -`; - -const ErrorMessage = styled(Text)` - color: ${theme.colors.error}; - font-size: ${theme.fontSizes.xs}; -`; diff --git a/frontend/src/components/Common/Skeleton/Skeleton.stories.tsx b/frontend/src/components/Common/Skeleton/Skeleton.stories.tsx deleted file mode 100644 index e8952c316..000000000 --- a/frontend/src/components/Common/Skeleton/Skeleton.stories.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/react'; - -import Skeleton from './Skeleton'; - -const meta: Meta = { - title: 'common/Skeleton', - component: Skeleton, -}; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - args: { - width: 100, - height: 100, - }, -}; diff --git a/frontend/src/components/Common/Skeleton/Skeleton.tsx b/frontend/src/components/Common/Skeleton/Skeleton.tsx deleted file mode 100644 index 857d03079..000000000 --- a/frontend/src/components/Common/Skeleton/Skeleton.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import type { ComponentPropsWithoutRef } from 'react'; -import styled from 'styled-components'; - -interface SkeletonProps extends ComponentPropsWithoutRef<'div'> { - width?: string | number; - height?: string | number; -} - -const Skeleton = ({ width, height }: SkeletonProps) => { - return ; -}; - -export default Skeleton; - -export const SkeletonContainer = styled.div` - position: absolute; - width: ${({ width }) => (typeof width === 'number' ? width + 'px' : width)}; - height: ${({ height }) => (typeof height === 'number' ? height + 'px' : height)}; - border-radius: 8px; - background: linear-gradient(-90deg, #dddddd, #f7f7f7, #dddddd, #f7f7f7); - background-size: 400%; - overflow: hidden; - animation: skeleton-gradient 5s infinite ease-out; - - @keyframes skeleton-gradient { - 0% { - background-position: 0% 50%; - } - 50% { - background-position: 100% 50%; - } - 100% { - background-position: 0% 50%; - } - } -`; diff --git a/frontend/src/components/Common/Toast/Toast.stories.tsx b/frontend/src/components/Common/Toast/Toast.stories.tsx deleted file mode 100644 index 383c43751..000000000 --- a/frontend/src/components/Common/Toast/Toast.stories.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/react'; - -import Toast from './Toast'; - -import ToastProvider from '@/contexts/ToastContext'; -import { useToastActionContext } from '@/hooks/context'; - -const meta: Meta = { - title: 'common/Toast', - component: Toast, - decorators: [ - (Story) => ( - - - - ), - ], -}; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - render: () => { - const { toast } = useToastActionContext(); - const handleClick = () => { - toast.success('성공'); - }; - return ( -
- -
- ); - }, -}; - -export const Error: Story = { - render: () => { - const { toast } = useToastActionContext(); - const handleClick = () => { - toast.error('실패'); - }; - return ( -
- -
- ); - }, -}; diff --git a/frontend/src/components/Common/Toast/Toast.tsx b/frontend/src/components/Common/Toast/Toast.tsx deleted file mode 100644 index c9d9dc46f..000000000 --- a/frontend/src/components/Common/Toast/Toast.tsx +++ /dev/null @@ -1,41 +0,0 @@ -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 ( - - {message} - - ); -}; - -export default Toast; - -type ToastStyleProps = Pick & { isAnimating?: boolean }; - -const ToastWrapper = styled.div` - position: relative; - width: calc(100% - 20px); - 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; -`; diff --git a/frontend/src/components/Common/index.ts b/frontend/src/components/Common/index.ts index 070263f54..f5b218215 100644 --- a/frontend/src/components/Common/index.ts +++ b/frontend/src/components/Common/index.ts @@ -10,18 +10,14 @@ export { default as TabMenu } from './TabMenu/TabMenu'; export { default as TagList } from './TagList/TagList'; export { default as SectionTitle } from './SectionTitle/SectionTitle'; export { default as ScrollButton } from './ScrollButton/ScrollButton'; -export { default as Input } from './Input/Input'; export { default as ImageUploader } from './ImageUploader/ImageUploader'; export { default as ErrorBoundary } from './ErrorBoundary/ErrorBoundary'; export { default as ErrorComponent } from './ErrorComponent/ErrorComponent'; export { default as Loading } from './Loading/Loading'; export { default as MarkedText } from './MarkedText/MarkedText'; export { default as NavigableSectionTitle } from './NavigableSectionTitle/NavigableSectionTitle'; -export { default as Carousel } from './Carousel/Carousel'; export { default as RegisterButton } from './RegisterButton/RegisterButton'; -export { default as Toast } from './Toast/Toast'; export { default as CategoryItem } from './CategoryItem/CategoryItem'; export { default as CategoryFoodList } from './CategoryFoodList/CategoryFoodList'; export { default as CategoryStoreList } from './CategoryStoreList/CategoryStoreList'; -export { default as Skeleton } from './Skeleton/Skeleton'; export { default as Banner } from './Banner/Banner'; diff --git a/frontend/src/components/Members/MemberModifyInput/MemberModifyInput.tsx b/frontend/src/components/Members/MemberModifyInput/MemberModifyInput.tsx index 39a78e6c5..6a03e4507 100644 --- a/frontend/src/components/Members/MemberModifyInput/MemberModifyInput.tsx +++ b/frontend/src/components/Members/MemberModifyInput/MemberModifyInput.tsx @@ -1,9 +1,7 @@ -import { Heading, Spacing, Text, useTheme } from '@fun-eat/design-system'; +import { Heading, Spacing, Text, Input, useTheme } from '@fun-eat/design-system'; import type { ChangeEventHandler } from 'react'; import styled from 'styled-components'; -import { Input } from '@/components/Common'; - const MIN_LENGTH = 1; const MAX_LENGTH = 10; diff --git a/frontend/src/components/Members/MemberReviewItem/MemberReviewItem.stories.tsx b/frontend/src/components/Members/MemberReviewItem/MemberReviewItem.stories.tsx index a631341d4..3856648f5 100644 --- a/frontend/src/components/Members/MemberReviewItem/MemberReviewItem.stories.tsx +++ b/frontend/src/components/Members/MemberReviewItem/MemberReviewItem.stories.tsx @@ -2,18 +2,9 @@ import type { Meta, StoryObj } from '@storybook/react'; import MemberReviewItem from './MemberReviewItem'; -import ToastProvider from '@/contexts/ToastContext'; - const meta: Meta = { title: 'members/MemberReviewItem', component: MemberReviewItem, - decorators: [ - (Story) => ( - - - - ), - ], args: { review: { reviewId: 1, diff --git a/frontend/src/components/Members/MemberReviewItem/MemberReviewItem.tsx b/frontend/src/components/Members/MemberReviewItem/MemberReviewItem.tsx index 1d4503853..3024b982a 100644 --- a/frontend/src/components/Members/MemberReviewItem/MemberReviewItem.tsx +++ b/frontend/src/components/Members/MemberReviewItem/MemberReviewItem.tsx @@ -1,9 +1,8 @@ -import { useTheme, Spacing, Text, Button } from '@fun-eat/design-system'; +import { useTheme, Spacing, Text, Button, useToastActionContext } from '@fun-eat/design-system'; import type { MouseEventHandler } from 'react'; import styled from 'styled-components'; import { SvgIcon } from '@/components/Common'; -import { useToastActionContext } from '@/hooks/context'; import { useDeleteReview } from '@/hooks/queries/members'; import type { MemberReview } from '@/types/review'; diff --git a/frontend/src/components/Product/ProductItem/ProductItem.tsx b/frontend/src/components/Product/ProductItem/ProductItem.tsx index 43dc773e9..d961894d3 100644 --- a/frontend/src/components/Product/ProductItem/ProductItem.tsx +++ b/frontend/src/components/Product/ProductItem/ProductItem.tsx @@ -1,11 +1,11 @@ -import { Text, useTheme } from '@fun-eat/design-system'; +import { Text, Skeleton, useTheme } from '@fun-eat/design-system'; import { memo, useState } from 'react'; import { useParams } from 'react-router-dom'; import styled from 'styled-components'; import PreviewImage from '@/assets/characters.svg'; import PBPreviewImage from '@/assets/samgakgimbab.svg'; -import { Skeleton, SvgIcon } from '@/components/Common'; +import { SvgIcon } from '@/components/Common'; import { CATEGORY_TYPE } from '@/constants'; import type { Product } from '@/types/product'; diff --git a/frontend/src/components/Rank/RecipeRankingItem/RecipeRankingItem.tsx b/frontend/src/components/Rank/RecipeRankingItem/RecipeRankingItem.tsx index b887ad10d..308f2d3fd 100644 --- a/frontend/src/components/Rank/RecipeRankingItem/RecipeRankingItem.tsx +++ b/frontend/src/components/Rank/RecipeRankingItem/RecipeRankingItem.tsx @@ -1,9 +1,9 @@ -import { Spacing, Text, useTheme } from '@fun-eat/design-system'; +import { Spacing, Text, Skeleton, useTheme } from '@fun-eat/design-system'; import { useState } from 'react'; import styled from 'styled-components'; import RecipePreviewImage from '@/assets/plate.svg'; -import { Skeleton, SvgIcon } from '@/components/Common'; +import { SvgIcon } from '@/components/Common'; import type { RecipeRanking } from '@/types/ranking'; import { getRelativeDate } from '@/utils/date'; diff --git a/frontend/src/components/Rank/RecipeRankingList/RecipeRankingList.tsx b/frontend/src/components/Rank/RecipeRankingList/RecipeRankingList.tsx index 76397964d..368bff096 100644 --- a/frontend/src/components/Rank/RecipeRankingList/RecipeRankingList.tsx +++ b/frontend/src/components/Rank/RecipeRankingList/RecipeRankingList.tsx @@ -1,9 +1,8 @@ -import { Link, Text } from '@fun-eat/design-system'; +import { Carousel, Link, Text } from '@fun-eat/design-system'; import { Link as RouterLink } from 'react-router-dom'; import RecipeRankingItem from '../RecipeRankingItem/RecipeRankingItem'; -import { Carousel } from '@/components/Common'; import { PATH } from '@/constants/path'; import { useGA } from '@/hooks/common'; import { useRecipeRankingQuery } from '@/hooks/queries/rank'; diff --git a/frontend/src/components/Recipe/CommentForm/CommentForm.tsx b/frontend/src/components/Recipe/CommentForm/CommentForm.tsx index 104657552..5eb0ac1c7 100644 --- a/frontend/src/components/Recipe/CommentForm/CommentForm.tsx +++ b/frontend/src/components/Recipe/CommentForm/CommentForm.tsx @@ -1,11 +1,10 @@ -import { Button, Spacing, Text, Textarea, useTheme } from '@fun-eat/design-system'; +import { Button, Spacing, Text, Textarea, useTheme, useToastActionContext } from '@fun-eat/design-system'; import type { ChangeEventHandler, FormEventHandler, RefObject } from 'react'; import { useState } from 'react'; import styled from 'styled-components'; import { SvgIcon } from '@/components/Common'; import { useScroll } from '@/hooks/common'; -import { useToastActionContext } from '@/hooks/context'; import { useRecipeCommentMutation } from '@/hooks/queries/recipe'; interface CommentFormProps { diff --git a/frontend/src/components/Recipe/RecipeItem/RecipeItem.tsx b/frontend/src/components/Recipe/RecipeItem/RecipeItem.tsx index 5846bd872..f7e7f229f 100644 --- a/frontend/src/components/Recipe/RecipeItem/RecipeItem.tsx +++ b/frontend/src/components/Recipe/RecipeItem/RecipeItem.tsx @@ -1,9 +1,9 @@ -import { Heading, Text, useTheme } from '@fun-eat/design-system'; +import { Heading, Text, Skeleton, useTheme } from '@fun-eat/design-system'; import { Fragment, memo, useState } from 'react'; import styled from 'styled-components'; import PreviewImage from '@/assets/plate.svg'; -import { Skeleton, SvgIcon } from '@/components/Common'; +import { SvgIcon } from '@/components/Common'; import type { MemberRecipe, Recipe } from '@/types/recipe'; import { getFormattedDate } from '@/utils/date'; diff --git a/frontend/src/components/Recipe/RecipeNameInput/RecipeNameInput.tsx b/frontend/src/components/Recipe/RecipeNameInput/RecipeNameInput.tsx index 3ab0d8bc8..093f31143 100644 --- a/frontend/src/components/Recipe/RecipeNameInput/RecipeNameInput.tsx +++ b/frontend/src/components/Recipe/RecipeNameInput/RecipeNameInput.tsx @@ -1,8 +1,7 @@ -import { Heading, Spacing, Text, useTheme } from '@fun-eat/design-system'; +import { Heading, Input, Spacing, Text, useTheme } from '@fun-eat/design-system'; import type { ChangeEventHandler } from 'react'; import styled from 'styled-components'; -import { Input } from '@/components/Common'; import { useRecipeFormActionContext } from '@/hooks/context'; const MIN_LENGTH = 1; diff --git a/frontend/src/components/Recipe/RecipeRegisterForm/RecipeRegisterForm.tsx b/frontend/src/components/Recipe/RecipeRegisterForm/RecipeRegisterForm.tsx index e92d894c5..dbc7f5254 100644 --- a/frontend/src/components/Recipe/RecipeRegisterForm/RecipeRegisterForm.tsx +++ b/frontend/src/components/Recipe/RecipeRegisterForm/RecipeRegisterForm.tsx @@ -1,4 +1,4 @@ -import { Button, Divider, Heading, Spacing, Text, useTheme } from '@fun-eat/design-system'; +import { Button, Divider, Heading, Spacing, Text, useTheme, useToastActionContext } from '@fun-eat/design-system'; import type { FormEventHandler } from 'react'; import styled from 'styled-components'; @@ -8,7 +8,7 @@ import RecipeUsedProducts from '../RecipeUsedProducts/RecipeUsedProducts'; import { ImageUploader, SvgIcon } from '@/components/Common'; import { useImageUploader, useFormData } from '@/hooks/common'; -import { useRecipeFormValueContext, useRecipeFormActionContext, useToastActionContext } from '@/hooks/context'; +import { useRecipeFormValueContext, useRecipeFormActionContext } from '@/hooks/context'; import { useRecipeRegisterFormMutation } from '@/hooks/queries/recipe'; import type { RecipeRequest } from '@/types/recipe'; diff --git a/frontend/src/components/Recipe/RecipeUsedProducts/RecipeUsedProducts.tsx b/frontend/src/components/Recipe/RecipeUsedProducts/RecipeUsedProducts.tsx index 8ee6faec4..00739f3fe 100644 --- a/frontend/src/components/Recipe/RecipeUsedProducts/RecipeUsedProducts.tsx +++ b/frontend/src/components/Recipe/RecipeUsedProducts/RecipeUsedProducts.tsx @@ -1,11 +1,11 @@ -import { Badge, Button, Heading, Text, useTheme } from '@fun-eat/design-system'; +import { Badge, Button, Heading, Text, Input, useTheme } from '@fun-eat/design-system'; import { useQueryErrorResetBoundary } from '@tanstack/react-query'; import { Suspense, useState } from 'react'; import styled from 'styled-components'; import SearchedProductList from './SearchedProductList'; -import { ErrorBoundary, ErrorComponent, Input, Loading, SvgIcon } from '@/components/Common'; +import { ErrorBoundary, ErrorComponent, Loading, SvgIcon } from '@/components/Common'; import { useDebounce } from '@/hooks/common'; import { useRecipeFormActionContext } from '@/hooks/context'; import { useSearch } from '@/hooks/search'; diff --git a/frontend/src/components/Review/ReviewRegisterForm/ReviewRegisterForm.tsx b/frontend/src/components/Review/ReviewRegisterForm/ReviewRegisterForm.tsx index 6c422c68d..7c3ac6ec1 100644 --- a/frontend/src/components/Review/ReviewRegisterForm/ReviewRegisterForm.tsx +++ b/frontend/src/components/Review/ReviewRegisterForm/ReviewRegisterForm.tsx @@ -1,4 +1,4 @@ -import { Button, Divider, Heading, Spacing, Text, theme } from '@fun-eat/design-system'; +import { Button, Divider, Heading, Spacing, Text, theme, useToastActionContext } from '@fun-eat/design-system'; import type { FormEventHandler, RefObject } from 'react'; import styled from 'styled-components'; @@ -11,7 +11,7 @@ import { ImageUploader, SvgIcon } from '@/components/Common'; import { ProductOverviewItem } from '@/components/Product'; import { MIN_DISPLAYED_TAGS_LENGTH } from '@/constants'; import { useFormData, useImageUploader, useScroll } from '@/hooks/common'; -import { useReviewFormActionContext, useReviewFormValueContext, useToastActionContext } from '@/hooks/context'; +import { useReviewFormActionContext, useReviewFormValueContext } from '@/hooks/context'; import { useProductDetailQuery } from '@/hooks/queries/product'; import { useReviewRegisterFormMutation } from '@/hooks/queries/review'; import type { ReviewRequest } from '@/types/review'; diff --git a/frontend/src/contexts/ToastContext.tsx b/frontend/src/contexts/ToastContext.tsx deleted file mode 100644 index f14148646..000000000 --- a/frontend/src/contexts/ToastContext.tsx +++ /dev/null @@ -1,80 +0,0 @@ -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(null); -export const ToastActionContext = createContext(null); - -const ToastProvider = ({ children }: PropsWithChildren) => { - const [toasts, setToasts] = useState([]); - - 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 ( - - - {children} - {createPortal( - - {toasts.map(({ id, message, isError }) => ( - - ))} - , - document.getElementById('toast-container') as HTMLElement - )} - - - ); -}; - -export default ToastProvider; - -const ToastContainer = styled.div` - position: fixed; - z-index: 1000; - display: flex; - flex-direction: column; - align-items: center; - width: 100%; - transform: translate(0, -10px); -`; diff --git a/frontend/src/hooks/common/index.ts b/frontend/src/hooks/common/index.ts index 199ae608a..2cbf28c1f 100644 --- a/frontend/src/hooks/common/index.ts +++ b/frontend/src/hooks/common/index.ts @@ -10,5 +10,4 @@ export { default as useTimeout } from './useTimeout'; export { default as useRouteChangeTracker } from './useRouteChangeTracker'; export { default as useTabMenu } from './useTabMenu'; export { default as useScrollRestoration } from './useScrollRestoration'; -export { default as useToast } from './useToast'; export { default as useGA } from './useGA'; diff --git a/frontend/src/hooks/common/useImageUploader.ts b/frontend/src/hooks/common/useImageUploader.ts index cc093517d..1f56a9923 100644 --- a/frontend/src/hooks/common/useImageUploader.ts +++ b/frontend/src/hooks/common/useImageUploader.ts @@ -1,8 +1,7 @@ +import { useToastActionContext } from '@fun-eat/design-system'; import imageCompression from 'browser-image-compression'; import { useState } from 'react'; -import { useToastActionContext } from '../context'; - const isImageFile = (file: File) => file.type !== 'image/png' && file.type !== 'image/jpeg'; const options = { diff --git a/frontend/src/hooks/common/useToast.ts b/frontend/src/hooks/common/useToast.ts deleted file mode 100644 index f95f33ef9..000000000 --- a/frontend/src/hooks/common/useToast.ts +++ /dev/null @@ -1,37 +0,0 @@ -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(null); - const deleteTimeoutRef = useRef(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; diff --git a/frontend/src/hooks/context/index.ts b/frontend/src/hooks/context/index.ts index dd03253c9..56470cfbb 100644 --- a/frontend/src/hooks/context/index.ts +++ b/frontend/src/hooks/context/index.ts @@ -4,5 +4,3 @@ export { default as useReviewFormActionContext } from './useReviewFormActionCont export { default as useReviewFormValueContext } from './useReviewFormValueContext'; export { default as useRecipeFormActionContext } from './useRecipeFormActionContext'; export { default as useRecipeFormValueContext } from './useRecipeFormValueContext'; -export { default as useToastActionContext } from './useToastActionContext'; -export { default as useToastValueContext } from './useToastValueContext'; diff --git a/frontend/src/hooks/context/useToastActionContext.ts b/frontend/src/hooks/context/useToastActionContext.ts deleted file mode 100644 index e0d7e31a2..000000000 --- a/frontend/src/hooks/context/useToastActionContext.ts +++ /dev/null @@ -1,14 +0,0 @@ -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; diff --git a/frontend/src/hooks/context/useToastValueContext.ts b/frontend/src/hooks/context/useToastValueContext.ts deleted file mode 100644 index ca4b65ca3..000000000 --- a/frontend/src/hooks/context/useToastValueContext.ts +++ /dev/null @@ -1,14 +0,0 @@ -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; diff --git a/frontend/src/hooks/queries/members/useLogoutMutation.ts b/frontend/src/hooks/queries/members/useLogoutMutation.ts index afc27e2bc..8aed88849 100644 --- a/frontend/src/hooks/queries/members/useLogoutMutation.ts +++ b/frontend/src/hooks/queries/members/useLogoutMutation.ts @@ -1,9 +1,9 @@ +import { useToastActionContext } from '@fun-eat/design-system'; import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useNavigate } from 'react-router-dom'; import { logoutApi } from '@/apis'; import { PATH } from '@/constants/path'; -import { useToastActionContext } from '@/hooks/context'; const useLogoutMutation = () => { const navigate = useNavigate(); diff --git a/frontend/src/hooks/search/useSearch.ts b/frontend/src/hooks/search/useSearch.ts index ba9853940..bf0452206 100644 --- a/frontend/src/hooks/search/useSearch.ts +++ b/frontend/src/hooks/search/useSearch.ts @@ -1,9 +1,9 @@ +import { useToastActionContext } from '@fun-eat/design-system'; import type { ChangeEventHandler, FormEventHandler, MouseEventHandler } from 'react'; import { useRef, useState } from 'react'; import { useSearchParams } from 'react-router-dom'; import { useGA } from '../common'; -import { useToastActionContext } from '../context'; const useSearch = () => { const inputRef = useRef(null); diff --git a/frontend/src/index.tsx b/frontend/src/index.tsx index 7cb7bc771..0299216de 100644 --- a/frontend/src/index.tsx +++ b/frontend/src/index.tsx @@ -8,7 +8,6 @@ import { RouterProvider } from 'react-router-dom'; import { SvgSprite } from './components/Common'; import { ENVIRONMENT } from './constants'; -import ToastProvider from './contexts/ToastContext'; import router from './router'; import GlobalStyle from './styles/globalStyle'; @@ -43,9 +42,7 @@ root.render( - - ...loading

} /> -
+ ...loading

} />
diff --git a/frontend/src/pages/IntegratedSearchPage.tsx b/frontend/src/pages/IntegratedSearchPage.tsx index 77f781f32..a74f7a776 100644 --- a/frontend/src/pages/IntegratedSearchPage.tsx +++ b/frontend/src/pages/IntegratedSearchPage.tsx @@ -1,9 +1,9 @@ -import { Button, Heading, Spacing, Text } from '@fun-eat/design-system'; +import { Button, Heading, Spacing, Text, Input } from '@fun-eat/design-system'; import { useQueryErrorResetBoundary } from '@tanstack/react-query'; import { Suspense, useEffect, useState } from 'react'; import styled from 'styled-components'; -import { ErrorBoundary, ErrorComponent, Input, Loading, SvgIcon, TabMenu } from '@/components/Common'; +import { ErrorBoundary, ErrorComponent, Loading, SvgIcon, TabMenu } from '@/components/Common'; import { RecommendList, ProductSearchResultList, RecipeSearchResultList } from '@/components/Search'; import { SEARCH_TAB_VARIANTS } from '@/constants'; import { useDebounce, useTabMenu } from '@/hooks/common'; diff --git a/frontend/src/pages/MemberModifyPage.tsx b/frontend/src/pages/MemberModifyPage.tsx index 6b3031a9e..4e6624e59 100644 --- a/frontend/src/pages/MemberModifyPage.tsx +++ b/frontend/src/pages/MemberModifyPage.tsx @@ -1,4 +1,4 @@ -import { Button, Spacing } from '@fun-eat/design-system'; +import { Button, Spacing, useToastActionContext } from '@fun-eat/design-system'; import type { ChangeEventHandler, FormEventHandler } from 'react'; import { useState } from 'react'; import { useNavigate } from 'react-router-dom'; @@ -8,7 +8,6 @@ import { SectionTitle, SvgIcon } from '@/components/Common'; import { MemberModifyInput } from '@/components/Members'; import { IMAGE_MAX_SIZE } from '@/constants'; import { useFormData, useImageUploader } from '@/hooks/common'; -import { useToastActionContext } from '@/hooks/context'; import { useMemberModifyMutation, useMemberQuery } from '@/hooks/queries/members'; import type { MemberRequest } from '@/types/member'; diff --git a/frontend/src/pages/SearchPage.tsx b/frontend/src/pages/SearchPage.tsx index b14c4d6a4..be5cf7971 100644 --- a/frontend/src/pages/SearchPage.tsx +++ b/frontend/src/pages/SearchPage.tsx @@ -1,10 +1,10 @@ -import { Button, Heading, Spacing, Text, useTheme } from '@fun-eat/design-system'; +import { Button, Heading, Spacing, Text, Input, useTheme } from '@fun-eat/design-system'; import { useQueryErrorResetBoundary } from '@tanstack/react-query'; import { Suspense, useEffect, useState } from 'react'; import { useParams } from 'react-router-dom'; import styled from 'styled-components'; -import { ErrorBoundary, ErrorComponent, Input, Loading, SvgIcon } from '@/components/Common'; +import { ErrorBoundary, ErrorComponent, Loading, SvgIcon } from '@/components/Common'; import { RecommendList, ProductSearchResultList, RecipeSearchResultList } from '@/components/Search'; import { SEARCH_PAGE_VARIANTS } from '@/constants'; import { useDebounce, useRoutePage } from '@/hooks/common'; diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 0a59dfb08..b0955c2e1 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -1375,10 +1375,10 @@ resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.1.6.tgz#22958c042e10b67463997bd6ea7115fe28cbcaf9" integrity sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A== -"@fun-eat/design-system@^0.3.18": - version "0.3.18" - resolved "https://registry.yarnpkg.com/@fun-eat/design-system/-/design-system-0.3.18.tgz#0c930437cd47923a9daffbaec748ef5db3b4d0c1" - integrity sha512-d1yfTLJLKPakFzf/wiDcLkRi5cit16hDJClH4+Mj6nMtChxMeUu3VU+i4oCJNqaNjZHDw9wOa+7L4kmIcKQnRg== +"@fun-eat/design-system@^0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@fun-eat/design-system/-/design-system-0.4.1.tgz#a486b58e9cc4db2535e5ec5370b711cfc5ca78b3" + integrity sha512-nmAu+H0qTmR161WrOVUD5pE66W7MtZl26+fASMBLZw7al9khTUwXkiI6vMr3sGaglih0TsB0bRSiMxWKtV4DlA== "@humanwhocodes/config-array@^0.11.11": version "0.11.11"