Skip to content

Commit

Permalink
Merge pull request #142 from prgrms-web-devcourse-final-project/feat/#38
Browse files Browse the repository at this point in the history


feat: CommentItem 컴포넌트 개발
  • Loading branch information
JW-Ahn0 authored Nov 29, 2024
2 parents f097d87 + 8fa9fc0 commit 027d2e4
Show file tree
Hide file tree
Showing 5 changed files with 233 additions and 1 deletion.
7 changes: 6 additions & 1 deletion src/components/molecules/KebabMenu/styled.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import styled from "@emotion/styled";
import { TextButtonWrapper } from "components/atoms/Button/TextButton/styled";

export const KebabMenuWrapper: ReturnType<typeof styled.div> = styled.div`
display: flex;
flex-direction: column;
justifiy-content: center;
justify-content: center;
align-items: center;
${TextButtonWrapper} {
width: 100%;
}
`;
88 changes: 88 additions & 0 deletions src/components/organisms/CommentItem/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { IconButton, Image, Text } from "components/atoms";
import { CommentItemWrapper } from "./styled";
import { KebabIcon } from "components/atoms/Icon";
import { getRelativeTime } from "utils";
import { useEffect, useRef, useState } from "react";
import { useKebabMenuManager } from "hooks/useKebabMenuManager";
import { KebabMenu } from "components/molecules";

interface ICommentItemProps {
/** 댓글 아이디 */
commentId: number;
/** 프로필 img Url */
imgUrl: string;
/** 작성자 닉네임 */
nickname: string;
/** 작성 날짜*/
createdAt: string;
/** 내용 */
content: string;
/** 해당 댓글이 내가 작성한 댓글인지 여부 */
isMyComment: boolean;
}

export const CommentItem = ({
commentId,
imgUrl,
nickname,
createdAt,
content,
isMyComment,
}: ICommentItemProps) => {
const [isMenuOpen, setIsMenuOpen] = useState(false);
const menuRef = useRef<HTMLDivElement | null>(null);
const time = getRelativeTime(createdAt);
const { getMenus } = useKebabMenuManager();
const menus = getMenus(isMyComment ? "myComment" : "notMyComment");

useEffect(() => {
/**
* 메뉴 영역 외 클릭 감지해서 메뉴 닫는 함수
* @param event : MouseEvent
* @return void
*/
const handleOutsideClick = (event: MouseEvent) => {
if (
isMenuOpen &&
menuRef.current &&
!menuRef.current.contains(event.target as Node)
) {
setIsMenuOpen(false);
}
};

document.addEventListener("mousedown", handleOutsideClick);
return () => {
document.removeEventListener("mousedown", handleOutsideClick);
};
}, []);

/**
* 케밥 메뉴 여는 함수
* @param void
* @return void
*/
const handleMenuClick = () => {
setIsMenuOpen(true);
};

return (
<CommentItemWrapper key={commentId}>
<Image url={imgUrl} type="round"></Image>
<div className="content-con">
<div className="writer-create-con">
<Text content={nickname}></Text>
<span className="separator">|</span>
<Text content={time}></Text>
</div>
<Text content={content}></Text>
</div>
<IconButton
backgroundColor="transparent"
icon={KebabIcon}
onClick={handleMenuClick}
></IconButton>
<div ref={menuRef}>{isMenuOpen && <KebabMenu menus={menus} />}</div>
</CommentItemWrapper>
);
};
47 changes: 47 additions & 0 deletions src/components/organisms/CommentItem/stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import type { Meta, StoryObj } from "@storybook/react";
import { CommentItem } from ".";
import { DEFAULT_IMG_PATH } from "constants/imgPath";

const meta: Meta<typeof CommentItem> = {
title: "Organisms/CommentItem",
component: CommentItem,
tags: ["autodocs"],
};

type Story = StoryObj<typeof meta>;

export const Default: Story = {
args: {
commentId: 1,
imgUrl: DEFAULT_IMG_PATH,
nickname: "작성자",
createdAt: new Date().toString(),
content: "내용",
isMyComment: false,
},
};

export const LongContent: Story = {
args: {
commentId: 1,
imgUrl: DEFAULT_IMG_PATH,
nickname: "작성자",
createdAt: new Date().toString(),
content:
"내용내용내용내용내용내용내용내용내용내용내용내용내용내용내용내용용내용내용내용내용내용내용내용내용내용내용내용내용내용내용내용내용용",
isMyComment: false,
},
};

export const MyComment: Story = {
args: {
commentId: 1,
imgUrl: DEFAULT_IMG_PATH,
nickname: "작성자",
createdAt: new Date().toString(),
content:
"내용내용내용내용내용내용내용내용내용내용내용내용내용내용내용내용용내용내용내용내용내용내용내용내용내용내용내용내용내용내용내용내용용",
isMyComment: true,
},
};
export default meta;
44 changes: 44 additions & 0 deletions src/components/organisms/CommentItem/styled.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import styled from "@emotion/styled";
import { IconButtonWrapper } from "components/atoms/Button/IconButton/styled";

import { ImageWrapper } from "components/atoms/Image/styled";
import { KebabMenuWrapper } from "components/molecules/KebabMenu/styled";

export const CommentItemWrapper: ReturnType<typeof styled.div> = styled.div`
display: flex;
gap: 8px;
position: relative;
.content-con {
display: flex;
flex-direction: column;
}
${ImageWrapper} {
width: 55px;
}
.content-con {
display: flex;
justify-content: center;
flex: 1;
}
.writer-create-con {
display: flex;
flex-direction: row;
white-space: pre-wrap;
}
.separator {
padding: 4px;
}
${KebabMenuWrapper} {
position: absolute;
right: 0;
transform: translate(-20%, 10%);
}
${IconButtonWrapper} {
height: max-content;
}
`;
48 changes: 48 additions & 0 deletions src/hooks/useKebabMenuManager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { IMenu } from "components/molecules/KebabMenu";
import { useCallback } from "react";

export const useKebabMenuManager = () => {
const onReply = useCallback(() => {
console.log("답글 클릭");
}, []);

const onEdit = useCallback(() => {
console.log("수정하기 클릭");
}, []);

const onDelete = useCallback(() => {
console.log("삭제하기 클릭");
}, []);

const onBlock = useCallback(() => {
console.log("차단하기 클릭");
}, []);
const onRepot = useCallback(() => {
console.log("신고하기 클릭");
}, []);
const getMenus = useCallback(
(scenario: string): IMenu[] => {
switch (scenario) {
/** CommentItem에서 내 코멘트인 경우 */
case "myComment":
return [
{ content: "답글", onClick: onReply },
{ content: "수정하기", onClick: onEdit },
{ content: "삭제하기", onClick: onDelete },
];
/** CommentItem에서 내 코멘트 아닌 경우 */
case "notMyComment":
return [
{ content: "답글", onClick: onReply },
{ content: "차단하기", onClick: onBlock },
{ content: "신고하기", onClick: onRepot },
];
default:
return [];
}
},
[onBlock, onDelete, onEdit, onReply, onRepot]
);

return { getMenus };
};

0 comments on commit 027d2e4

Please sign in to comment.