-
Notifications
You must be signed in to change notification settings - Fork 0
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
refactor: 기초 recipe item 컴포넌트 구현 #83
Changes from 2 commits
5dff033
6907a47
266e366
c06af98
96403bb
af65092
eeccf3a
f1d84bc
bba1706
4ad3aa3
3dd571c
08259c4
a6a656e
be48d52
d49e5c5
33ccebc
f3281b0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
@@ -1,69 +1,81 @@ | ||||||||
import { BottomSheet, Skeleton, useBottomSheet } from '@fun-eat/design-system'; | ||||||||
import type { MouseEventHandler } from 'react'; | ||||||||
import { memo, useState } from 'react'; | ||||||||
import type { MouseEventHandler, PropsWithChildren } from 'react'; | ||||||||
import { useState } from 'react'; | ||||||||
import { Link } from 'react-router-dom'; | ||||||||
|
||||||||
import { | ||||||||
ellipsis, | ||||||||
favoriteButtonWrapper, | ||||||||
imageWrapper, | ||||||||
productButtonWrapper, | ||||||||
recipeAuthor, | ||||||||
recipeContent, | ||||||||
productCircleListWrapper, | ||||||||
productCircleWrapper, | ||||||||
productImage, | ||||||||
recipeImage, | ||||||||
recipeProductWrapper, | ||||||||
recipeTitle, | ||||||||
recipeProductsCount, | ||||||||
thirdProductImage, | ||||||||
} from './recipeItem.css'; | ||||||||
import RecipeFavoriteButton from '../RecipeFavoriteButton/RecipeFavoriteButton'; | ||||||||
import RecipeProductButton from '../RecipeProductButton/RecipeProductButton'; | ||||||||
|
||||||||
import { Text } from '@/components/Common'; | ||||||||
import { ProductOverviewList } from '@/components/Product'; | ||||||||
import { RECIPE_CARD_DEFAULT_IMAGE_URL } from '@/constants/image'; | ||||||||
import type { MemberRecipe, Recipe } from '@/types/recipe'; | ||||||||
import RecipeItemProvider from '@/contexts/RecipeItemContext'; | ||||||||
import { useRecipeItemValueContext } from '@/hooks/context'; | ||||||||
import { getRelativeDate } from '@/utils/date'; | ||||||||
import displaySlice from '@/utils/displaySlice'; | ||||||||
|
||||||||
interface RecipeItemProps { | ||||||||
recipe: Recipe | MemberRecipe; | ||||||||
isMemberPage?: boolean; | ||||||||
} | ||||||||
const RecipeItem = ({ children }: PropsWithChildren) => { | ||||||||
const { recipe } = useRecipeItemValueContext(); | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
const { id } = recipe; | ||||||||
|
||||||||
const RecipeItem = ({ recipe, isMemberPage = false }: RecipeItemProps) => { | ||||||||
const { id, image, title, content, favorite, products } = recipe; | ||||||||
const { isOpen, isClosing, handleOpenBottomSheet, handleCloseBottomSheet } = useBottomSheet(); | ||||||||
|
||||||||
const author = 'author' in recipe ? recipe.author : null; | ||||||||
return ( | ||||||||
<RecipeItemProvider recipe={recipe}> | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 여기서 아래로 내려주니까 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 맞네...여기서 감싸주고 있었구나... |
||||||||
<Link to={`${id}`}>{children}</Link> | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
그냥 이렇게 적어도 될듯!! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. to가 string만 받을 수 있나봐요..! |
||||||||
</RecipeItemProvider> | ||||||||
); | ||||||||
}; | ||||||||
|
||||||||
const ImageAndFavoriteButton = ({ children }: PropsWithChildren) => { | ||||||||
const { recipe } = useRecipeItemValueContext(); | ||||||||
const { id, image, title, favorite } = recipe; | ||||||||
const [isImageLoading, setIsImageLoading] = useState(true); | ||||||||
|
||||||||
return ( | ||||||||
<div className={imageWrapper}> | ||||||||
<img | ||||||||
className={recipeImage} | ||||||||
src={image ?? RECIPE_CARD_DEFAULT_IMAGE_URL} | ||||||||
alt={`조리된 ${title}`} | ||||||||
loading="lazy" | ||||||||
onLoad={() => image && setIsImageLoading(false)} | ||||||||
/> | ||||||||
{isImageLoading && image && <Skeleton width={163} height={200} />} | ||||||||
<div className={favoriteButtonWrapper} onClick={(e) => e.preventDefault()}> | ||||||||
<RecipeFavoriteButton recipeId={id} favorite={favorite} /> | ||||||||
</div> | ||||||||
{children} | ||||||||
</div> | ||||||||
); | ||||||||
}; | ||||||||
|
||||||||
const ProductButton = () => { | ||||||||
const { recipe } = useRecipeItemValueContext(); | ||||||||
const { products } = recipe; | ||||||||
const { isOpen, isClosing, handleOpenBottomSheet, handleCloseBottomSheet } = useBottomSheet(); | ||||||||
|
||||||||
const handleOpenProductSheet: MouseEventHandler<HTMLDivElement> = (event) => { | ||||||||
event.preventDefault(); | ||||||||
handleOpenBottomSheet(); | ||||||||
}; | ||||||||
|
||||||||
return ( | ||||||||
<> | ||||||||
<Link to={`${id}`}> | ||||||||
{!isMemberPage && ( | ||||||||
<div className={imageWrapper}> | ||||||||
<img | ||||||||
className={recipeImage} | ||||||||
src={image ?? RECIPE_CARD_DEFAULT_IMAGE_URL} | ||||||||
alt={`조리된 ${title}`} | ||||||||
loading="lazy" | ||||||||
onLoad={() => image && setIsImageLoading(false)} | ||||||||
/> | ||||||||
{isImageLoading && image && <Skeleton width={163} height={200} />} | ||||||||
<div className={favoriteButtonWrapper} onClick={(e) => e.preventDefault()}> | ||||||||
<RecipeFavoriteButton recipeId={id} favorite={favorite} /> | ||||||||
</div> | ||||||||
<div className={productButtonWrapper} onClick={(e) => handleOpenProductSheet(e)}> | ||||||||
<RecipeProductButton isTranslucent /> | ||||||||
</div> | ||||||||
</div> | ||||||||
)} | ||||||||
<div style={{ height: '8px' }} /> | ||||||||
<p className={recipeTitle}>{title}</p> | ||||||||
<p className={recipeAuthor}>{author && `${author.nickname} 님`}</p> | ||||||||
<p className={recipeContent}>{content}</p> | ||||||||
</Link> | ||||||||
<div className={productButtonWrapper} onClick={(e) => handleOpenProductSheet(e)}> | ||||||||
<RecipeProductButton isTranslucent /> | ||||||||
</div> | ||||||||
|
||||||||
<BottomSheet isOpen={isOpen} isClosing={isClosing} maxWidth="400px" close={handleCloseBottomSheet}> | ||||||||
<div className={recipeProductWrapper}> | ||||||||
|
@@ -74,4 +86,76 @@ const RecipeItem = ({ recipe, isMemberPage = false }: RecipeItemProps) => { | |||||||
); | ||||||||
}; | ||||||||
|
||||||||
export default memo(RecipeItem); | ||||||||
const ProductCircleButton = () => { | ||||||||
const { recipe } = useRecipeItemValueContext(); | ||||||||
const { products } = recipe; | ||||||||
|
||||||||
return ( | ||||||||
<ul className={productCircleWrapper}> | ||||||||
{displaySlice(true, products, 3).map(({ id, image }, idx) => ( | ||||||||
<li key={id} className={productCircleListWrapper}> | ||||||||
<img | ||||||||
src={image} | ||||||||
alt="사용한 상품" | ||||||||
className={products.length > 3 && idx === 2 ? thirdProductImage : productImage} | ||||||||
/> | ||||||||
{idx === 2 && ( | ||||||||
<Text size="caption3" weight="medium" className={recipeProductsCount}> | ||||||||
+{products.length - 3} | ||||||||
</Text> | ||||||||
)} | ||||||||
</li> | ||||||||
))} | ||||||||
</ul> | ||||||||
); | ||||||||
}; | ||||||||
|
||||||||
const Title = () => { | ||||||||
const { recipe } = useRecipeItemValueContext(); | ||||||||
const { title } = recipe; | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
너무 반복된다면 이렇게 할 수 있었던거 같은데 잘 기억이 안 나네요 ㅎㅎ |
||||||||
|
||||||||
return ( | ||||||||
<Text className={ellipsis} size="caption1" weight="semiBold" color="default"> | ||||||||
{title} | ||||||||
</Text> | ||||||||
); | ||||||||
}; | ||||||||
|
||||||||
const Author = () => { | ||||||||
const { recipe } = useRecipeItemValueContext(); | ||||||||
const { author } = recipe; | ||||||||
|
||||||||
return <Text size="caption3" color="sub">{`${author.nickname} 님`}</Text>; | ||||||||
}; | ||||||||
|
||||||||
const CreatedDate = () => { | ||||||||
const { recipe } = useRecipeItemValueContext(); | ||||||||
const { createdAt } = recipe; | ||||||||
|
||||||||
return ( | ||||||||
<Text size="caption3" color="sub"> | ||||||||
· {getRelativeDate(createdAt)} | ||||||||
</Text> | ||||||||
); | ||||||||
}; | ||||||||
|
||||||||
const Content = () => { | ||||||||
const { recipe } = useRecipeItemValueContext(); | ||||||||
const { content } = recipe; | ||||||||
|
||||||||
return ( | ||||||||
<Text className={ellipsis} size="caption4" color="disabled"> | ||||||||
{content} | ||||||||
</Text> | ||||||||
); | ||||||||
}; | ||||||||
|
||||||||
RecipeItem.ImageAndFavoriteButton = ImageAndFavoriteButton; | ||||||||
RecipeItem.ProductButton = ProductButton; | ||||||||
RecipeItem.ProductCircleButton = ProductCircleButton; | ||||||||
RecipeItem.Title = Title; | ||||||||
RecipeItem.Author = Author; | ||||||||
RecipeItem.CreatedDate = CreatedDate; | ||||||||
RecipeItem.Content = Content; | ||||||||
|
||||||||
export default RecipeItem; | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 얘는 외부에서 사용하지 않고 아래 친구들만 사용하는거죠? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 얍얍 그렇슴다! |
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.
인라인 이런 말하는건가요?! 저는 얘네는 큰 컴포넌트로 묶어도 될 것 같아요!
그리고 Flex, Grid 컴포넌트 만들면 더 깔끔해지겠네요
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.
좋슴니다 따로 묶어서 구현했습니다!
나중에 화면 작업 끝나면 바로 Flex랑 Grid랑 Heading 같은 애들 만듭시다~~
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.
Flex, Grid, Heading 너무 필요..