Skip to content

Commit

Permalink
[feat] added FeedReplyWriteInput Component
Browse files Browse the repository at this point in the history
- update useAutoResizeTextArea hook (not use row count, use scrollHeight)
  • Loading branch information
seongjin2427 committed Nov 2, 2024
1 parent 07a86f1 commit 49218d4
Show file tree
Hide file tree
Showing 10 changed files with 183 additions and 89 deletions.
28 changes: 21 additions & 7 deletions src/app/(MainLayout)/components/FeedComment/FeedComment.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import Image from 'next/image';

import { compactTimeFormatter, digitNumberFormatter } from '@/utils/formatter';
import ToggleWrapper from '@/components/common/ToggleWrapper';
import LikeButton from '../LikeButton';
import FeedReplyWriteInput from '../FeedReplyWriteInput';

import styles from './FeedComment.module.scss';
import LikeButton from '../LikeButton';

export type FeedCommentType = {
commentId: string;
Expand All @@ -22,7 +24,7 @@ interface FeedCommentProps {

const FeedComment = ({ commentData }: FeedCommentProps) => {
const {
// commentId,
commentId,
nickname,
profileUrl,
feedContent,
Expand All @@ -47,11 +49,23 @@ const FeedComment = ({ commentData }: FeedCommentProps) => {
</div>
<p className={styles.feed_content}>{feedContent}</p>

<div className={styles.feed_coment_info}>
<span>{compactTimeFormatter(updatedAt ?? createdAt)}</span>
<span>좋아요 {digitNumberFormatter(likeCount)}</span>
<span className={styles.replay_write_button}>답글 달기</span>
</div>
<ToggleWrapper>
{({ isToggle, toggleHandler }) => (
<>
<div className={styles.feed_coment_info}>
<span>{compactTimeFormatter(updatedAt ?? createdAt)}</span>
<span>좋아요 {digitNumberFormatter(likeCount)}</span>
<button
className={styles.replay_write_button}
onClick={toggleHandler}
>
답글 달기
</button>
</div>
{isToggle && <FeedReplyWriteInput commentId={commentId} />}
</>
)}
</ToggleWrapper>
</div>
);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
@use '../../../../styles/helpers/index' as *;

.container {
height: 100%;
flex: 1;
display: flex;
flex-direction: column;
}

.comment_container {
padding: 1.25rem;
overflow: scroll;
}

.container_non_padding {
.reply_container {
padding-left: 2rem;
overflow: scroll;
}

.reply_comment {
Expand Down
130 changes: 71 additions & 59 deletions src/app/(MainLayout)/components/FeedCommentList/FeedCommentList.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,33 @@
'use client';

import { useEffect, useState } from 'react';
import clsx from 'clsx';
import { Fragment, useEffect, useState } from 'react';

import If from '@/components/common/If';
import ToggleWrapper from '@/components/common/ToggleWrapper';
import FeedComment, { FeedCommentType } from '../FeedComment/FeedComment';

import styles from './FeedCommentList.module.scss';

const asyncMockDataResponse = async () => {
let uniqueId = 1;
const makeMockData = (id: number) => ({
// eslint-disable-next-line no-plusplus
commentId: Math.ceil(Math.random() * 6),
nickname: `홍길동${id + 1}`,
profileUrl:
'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcT4vkwPhD-NHO6sV_3ailgWXjiP_WPM24J3IhkB3xZ-bQ&s',
feedContent:
'내용 입력 내용 입력 내용 입력 내용 입력 내용 입력 내용 입력 내용 입력 \n내용 입력 내용 입력 내용 입력 내용 입력 내용 입력 내용 입력 내용 입력 내용 입력 내용 입력 내용 입력 내용 입력 내용 입력 내용 입력 내용 입력 내용 입력 내용 입력 내용 입력 내용 입력 내용 입력',
createdAt: '2024-10-25 20:08:22',
updatedAt: '2024-10-28 21:29:22',
likeCount: 1357345 + id,
childFeedComments: +id < 4 ? [4, 5, 6] : [],
});

const asyncMockDataResponse = () => {
return async (id: string) => {
if (uniqueId < 4) {
if (+id < 7) {
return new Promise((res) => {
const data = {
// eslint-disable-next-line no-plusplus
commentId: (uniqueId++).toString(),
nickname: `홍길동${id + 1}`,
profileUrl:
'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcT4vkwPhD-NHO6sV_3ailgWXjiP_WPM24J3IhkB3xZ-bQ&s',
feedContent:
'내용 입력 내용 입력 내용 입력 내용 입력 내용 입력 내용 입력 내용 입력 \n내용 입력 내용 입력 내용 입력 내용 입력 내용 입력 내용 입력 내용 입력 내용 입력 내용 입력 내용 입력 내용 입력 내용 입력 내용 입력 내용 입력 내용 입력 내용 입력 내용 입력 내용 입력 내용 입력',
createdAt: '2024-10-25 20:08:22',
updatedAt: '2024-10-28 21:29:22',
likeCount: 1357345 + id,
childFeedComments: +id < 4 ? [4, 5, 6] : [],
};

const data = makeMockData(+id);
res(data);
});
}
Expand All @@ -37,61 +38,72 @@ const asyncMockDataResponse = async () => {

interface FeedCommentListProps {
feedId?: string;
commentId?: string;
feedCommentIds: string[];
}

const FeedCommentList = ({ feedId, feedCommentIds }: FeedCommentListProps) => {
const FeedCommentList = ({
feedId,
commentId,
feedCommentIds,
}: FeedCommentListProps) => {
const [commentData, setCommentData] = useState<FeedCommentType[]>([]);

useEffect(() => {
(async function () {
const result = (await Promise.all(
feedCommentIds.map(await asyncMockDataResponse()),
)) as FeedCommentType[];
const requests = await feedCommentIds.map(await asyncMockDataResponse());
const result = (await Promise.all(requests)) as FeedCommentType[];
setCommentData(result);
})();
}, []);

return (
<div className={feedId ? styles.container : styles.container_non_padding}>
<If condition={Boolean(feedId)}>
{commentData.map((feedComment) => (
<>
<FeedComment
key={feedComment.commentId}
commentData={feedComment}
/>
<div
className={clsx(
styles.container,
feedId && styles.comment_container,
commentId && styles.reply_container,
)}
>
<If condition={!!feedId || !!commentId}>
<If.True>
{commentData.map((comment) => (
<Fragment key={comment.commentId}>
<FeedComment key={comment?.commentId} commentData={comment} />

<If condition={Boolean(feedComment.childFeedComments.length)}>
<If.True>
<ToggleWrapper>
{({ isToggle, toggleHandler }) => (
<>
{!isToggle && (
<div className={styles.reply_comment}>
<div className={styles.horizon_line} />
<button
className={styles.reply_seeing_button}
onClick={toggleHandler}
>
답글 보기 ({feedComment.childFeedComments.length}개)
</button>
</div>
)}
<If condition={Boolean(comment?.childFeedComments.length)}>
<If.True>
<ToggleWrapper>
{({ isToggle, toggleHandler }) => (
<>
{!isToggle && (
<div className={styles.reply_comment}>
<div className={styles.horizon_line} />
<button
className={styles.reply_seeing_button}
onClick={toggleHandler}
>
답글 보기 ({comment.childFeedComments.length}
개)
</button>
</div>
)}

{isToggle && (
<FeedCommentList
key={feedComment.commentId}
feedCommentIds={feedComment.childFeedComments}
/>
)}
</>
)}
</ToggleWrapper>
</If.True>
</If>
</>
))}
{isToggle && (
<FeedCommentList
key={comment.commentId}
commentId={comment.commentId}
feedCommentIds={comment.childFeedComments}
/>
)}
</>
)}
</ToggleWrapper>
</If.True>
</If>
</Fragment>
))}
</If.True>
</If>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,20 @@ const FeedCommentWriteInput = ({
parentId,
placeholder = '댓글 달기...',
}: FeedCommentWriteInputProps) => {
const { commentContent, setCommentContent, textareaRef } =
const { textareaContent, setTextareaContent, textareaRef } =
useAutoResizeTextArea({ maxLine: 3, lineHeight: 20 });

const changeHandler = (e: ChangeEvent<HTMLTextAreaElement>) => {
const { value } = e.target;
if (value.length <= MAX_LENGTH) {
setCommentContent(value);
setTextareaContent(value);
}
};

const submitHandler = () => {
console.log(postId, parentId);
if (commentContent) {
console.log('게시');
if (textareaContent) {
console.log(textareaContent);
}
};

Expand All @@ -42,7 +42,7 @@ const FeedCommentWriteInput = ({
className={styles.comment_textarea}
placeholder={placeholder}
onChange={changeHandler}
value={commentContent}
value={textareaContent}
ref={textareaRef}
/>
<button className={styles.submit_button} onClick={submitHandler}>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
@use '../../../../styles/helpers/index' as *;

.container {
display: flex;
align-items: center;
height: auto;
margin: 10px 0;
border: 1px solid $COLOR_GRAY_2;
border-radius: 5px;

textarea {
flex: 1;
height: 20px;
margin: 10px;
margin-right: 0;
white-space: pre-wrap;
word-break: break-all;
line-height: 20px;
}

button {
height: 100%;
padding: 0.5rem 1rem;
border-radius: 2px;
text-align: center;
color: $COLOR_PRIMARY_1;
cursor: pointer;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
'use client';

import { ChangeEvent } from 'react';

import useAutoResizeTextArea from '@/hooks/useAutoResizeTextArea';

import styles from './FeedReplyWriteInput.module.scss';

interface FeedReplyWriteInputProps {
commentId: string;
}

const FeedReplyWriteInput = ({ commentId }: FeedReplyWriteInputProps) => {
const { textareaRef, textareaContent, setTextareaContent } =
useAutoResizeTextArea({ maxLine: 3, lineHeight: 20 });

const textareaChangeHandler = (e: ChangeEvent<HTMLTextAreaElement>) => {
const { value } = e.target;
setTextareaContent(value);
};

const replySubmitHandler = () => {
console.log(commentId, textareaContent);
};

return (
<div className={styles.container}>
<textarea
ref={textareaRef}
value={textareaContent}
onChange={textareaChangeHandler}
/>
<button onClick={replySubmitHandler}>게시</button>
</div>
);
};

export default FeedReplyWriteInput;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './FeedReplyWriteInput';
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ const ReadOnlyCommonFeed = () => {
<FeedTextContent feedData={MOCK_FEED_DATA} />
</div>
<div className={styles.right_down}>
<FeedCommentList feedId="1" feedCommentIds={['1', '2', '3']} />
<FeedCommentList feedId="1" feedCommentIds={['0', '2', '3']} />
</div>
<div>
<FeedLikeBox postId={1} likeCount={14264} commentCount={30} />
Expand Down
4 changes: 1 addition & 3 deletions src/app/(MainLayout)/components/Slide/Slide.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,7 @@ const Slide = ({ children, leftIcon, rightIcon }: SlideProps) => {
useEffect(() => {
window.addEventListener('resize', () => updateParentWidth);

return () => {
window.removeEventListener('resize', () => updateParentWidth);
};
return () => window.removeEventListener('resize', () => updateParentWidth);
}, [updateParentWidth]);

const slideItems =
Expand Down
19 changes: 9 additions & 10 deletions src/hooks/useAutoResizeTextArea.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,22 @@ const useAutoResizeTextArea = ({
maxLine = 3,
lineHeight = 20,
}: useAutoResizeTextAreaParams) => {
const [commentContent, setCommentContent] = useState<string>('');
const [textareaContent, setTextareaContent] = useState<string>('');
const textareaRef = useRef<HTMLTextAreaElement>(null);

useEffect(() => {
if (textareaRef.current) {
const rowCount = textareaRef.current.value.split(/\r\n|\r|\n/).length;
const target = textareaRef.current;
const element = textareaRef.current;
const maxLimitHeight = lineHeight * maxLine;

if (rowCount < maxLine) {
target.style.height = `${(rowCount * lineHeight).toString()}px`;
} else {
target.style.height = `${maxLine * lineHeight}px`;
}
element.style.height = `${lineHeight}px`;

const currentHeight = Math.min(element.scrollHeight, maxLimitHeight);
element.style.height = `${currentHeight}px`;
}
}, [maxLine, lineHeight, commentContent]);
}, [maxLine, lineHeight, textareaContent]);

return { commentContent, setCommentContent, textareaRef };
return { textareaContent, setTextareaContent, textareaRef };
};

export default useAutoResizeTextArea;

0 comments on commit 49218d4

Please sign in to comment.