-
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 4 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(); | ||||||||
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 AuthorAndCreatedDate = () => { | ||||||||
const { recipe } = useRecipeItemValueContext(); | ||||||||
const { author, createdAt } = recipe; | ||||||||
|
||||||||
return ( | ||||||||
<Text size="caption3" color="sub"> | ||||||||
{`${author.nickname} 님 · ${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.AuthorAndCreatedDate = AuthorAndCreatedDate; | ||||||||
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.