Skip to content

Commit

Permalink
[Feature/BAR-169] 끄적이는 수정 모드 구현 (#61)
Browse files Browse the repository at this point in the history
  • Loading branch information
wonjin-dev authored Feb 16, 2024
1 parent 6ff3616 commit 5f14b84
Show file tree
Hide file tree
Showing 10 changed files with 302 additions and 7 deletions.
1 change: 1 addition & 0 deletions src/components/Card/style.css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export const header = style([
gap: '8px',
color: COLORS['Grey/400'],
wordBreak: 'keep-all',
position: 'relative',
},
]);

Expand Down
50 changes: 48 additions & 2 deletions src/domain/끄적이는/components/Card/History/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,38 @@ import dayjs from 'dayjs';

import Icon from '@components/Icon';
import useDeleteTemporalMemo from '@domain/끄적이는/mutations/useDeleteTemporalMemo';
import useEditTemporalMemo from '@domain/끄적이는/mutations/useEditTemporalMemo';
import { type TemporalMemo } from '@domain/끄적이는/types';
import { useInput } from '@hooks/useInput';
import useModal from '@hooks/useModal';
import { useToastStore } from '@stores/toast';
import { COLORS } from '@styles/tokens';

import EditInput from '../../EditInput';
import SettingDialog from '../components/SettingDialog';
import * as styles from './style.css';

const WriteHistoryCard = ({ id, createdAt, content }: TemporalMemo) => {
interface WriteHistoryCardProps extends TemporalMemo {
isEditMode: boolean;
onEditClick: VoidFunction;
onEditCompleteClick: VoidFunction;
}

const WriteHistoryCard = ({
id,
createdAt,
content,
isEditMode,
onEditClick,
onEditCompleteClick,
}: WriteHistoryCardProps) => {
const { showToast } = useToastStore();
const { mutate: updateTemporalMemo } = useEditTemporalMemo();
const { mutate: deleteTemporalMemo } = useDeleteTemporalMemo();
const editedInputProps = useInput({
id: 'edit-input',
defaultValue: content,
});

const {
isOpen: settingModalOpen,
Expand All @@ -27,6 +48,31 @@ const WriteHistoryCard = ({ id, createdAt, content }: TemporalMemo) => {
});
};

// TODO 밸리데이션 추가
const handleEditCompleteClick = () => {
updateTemporalMemo({ id: id, content: editedInputProps.value });
setTimeout(() => onEditCompleteClick(), 0);
};

if (isEditMode) {
return (
<li className={styles.container}>
<div className={styles.header}>
<p className={styles.date}>
{dayjs(createdAt).locale('ko').format('a h:mm')}
</p>
<button
onClick={handleEditCompleteClick}
className={styles.editCompleteBtn}
>
완료
</button>
</div>
<EditInput inputProps={editedInputProps} />
</li>
);
}

return (
<li className={styles.container}>
<div className={styles.header}>
Expand Down Expand Up @@ -66,7 +112,7 @@ const WriteHistoryCard = ({ id, createdAt, content }: TemporalMemo) => {

{settingModalOpen && (
<SettingDialog
onEditClick={() => {}}
onEditClick={onEditClick}
onDeleteClick={() => deleteTemporalMemo(id)}
/>
)}
Expand Down
46 changes: 46 additions & 0 deletions src/domain/끄적이는/components/Card/History/style.css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,52 @@ export const container = style({
position: 'relative',
});

export const editContainer = style({
backgroundColor: 'white',
width: '100%',
padding: '12px 16px',
borderRadius: '8px',
});

export const editInput = style([
sprinkles({
typography: '15/Body/Regular',
}),
{
width: '100%',
marginBottom: '8px',
resize: 'none',
overflowWrap: 'break-word',
maxHeight: '217px',
},
]);

export const editInputTextCount = style({
height: '17px',
textAlign: 'end',
color: COLORS['Grey/400'],
fontSize: '14px',
fontWeight: '400',
letterSpacing: '0.2px',
});

export const editTextCurrentCount = style({
color: COLORS['Blue/Default'],
fontWeight: '700',
});

export const editCompleteBtn = style([
sprinkles({
typography: '15/Body/Medium',
}),
{
position: 'absolute',
top: '20px',
right: '32px',
color: COLORS['Grey/700'],
},
]);

export const header = style({
display: 'flex',
justifyContent: 'space-between',
Expand Down
45 changes: 43 additions & 2 deletions src/domain/끄적이는/components/Card/Today/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@ import Card from '@components/Card';
import Icon from '@components/Icon';
import SkeletonContent from '@components/Loading/Skeleton/SkeletonContent';
import useDeleteTemporalMemo from '@domain/끄적이는/mutations/useDeleteTemporalMemo';
import useEditTemporalMemo from '@domain/끄적이는/mutations/useEditTemporalMemo';
import { useInput } from '@hooks/useInput';
import useModal from '@hooks/useModal';
import usePostSpellCheck from '@queries/usePostSpellCheck';
import { useToastStore } from '@stores/toast';

import EditInput from '../../EditInput';
import SpellCheckCard from '../../Today/components/SpellCheckCard';
import SettingDialog from '../components/SettingDialog';
import * as styles from './style.css';
Expand All @@ -19,17 +22,33 @@ interface WriteTodayCardProps {
id: number;
createAt: string;
content: string;
isEditMode: boolean;
onEditClick: (id: number) => void;
onEditCompleteClick: VoidFunction;
}

const WriteTodayCard = ({ id, createAt, content }: WriteTodayCardProps) => {
const WriteTodayCard = ({
id,
createAt,
content,
isEditMode,
onEditClick,
onEditCompleteClick,
}: WriteTodayCardProps) => {
const { showToast } = useToastStore();
const { mutate: updateTemporalMemo } = useEditTemporalMemo();
const { mutate: deleteTemporalMemo } = useDeleteTemporalMemo();
const {
isOpen: settingModalOpen,
handleOpen: showSettingModal,
handleClose: hideSettingModal,
} = useModal();

const editInputProps = useInput({
id: 'edit-today-input',
defaultValue: content,
});

const [spellCheckResult, setSpellCheckResult] =
useState<SpellCheckResponse>();
const {
Expand All @@ -53,6 +72,28 @@ const WriteTodayCard = ({ id, createAt, content }: WriteTodayCardProps) => {
});
};

//TODO: 밸리데이션 추가
const handleUpdate = () => {
updateTemporalMemo({ id: id, content: editInputProps.value });
setTimeout(() => onEditCompleteClick(), 0);
};

if (isEditMode) {
return (
<Card color="blue" onBlur={hideSettingModal}>
<Card.Header>
{dayjs(createAt).locale('ko').format('a h:mm')}
<button className={styles.editCompleteBtn} onClick={handleUpdate}>
완료
</button>
</Card.Header>
<Card.Body>
<EditInput inputProps={editInputProps} />
</Card.Body>
</Card>
);
}

return (
<Card color="blue" onBlur={hideSettingModal}>
{!isSuccessSpellCheck && (
Expand Down Expand Up @@ -91,7 +132,7 @@ const WriteTodayCard = ({ id, createAt, content }: WriteTodayCardProps) => {

{settingModalOpen && (
<SettingDialog
onEditClick={() => {}}
onEditClick={() => onEditClick(id)}
onDeleteClick={() => deleteTemporalMemo(id)}
/>
)}
Expand Down
11 changes: 11 additions & 0 deletions src/domain/끄적이는/components/Card/Today/style.css.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { style } from '@vanilla-extract/css';

import { sprinkles } from '@styles/sprinkles.css';
import { COLORS } from '@styles/tokens';

export const icon = style({
Expand All @@ -21,3 +22,13 @@ export const skeletonSuggestion = style({
borderRadius: '12px',
backgroundColor: COLORS['Grey/White'],
});

export const editCompleteBtn = style([
sprinkles({ typography: '16/Body/Medium' }),
{
position: 'absolute',
top: '-2px',
right: '0',
color: COLORS['Grey/700'],
},
]);
78 changes: 78 additions & 0 deletions src/domain/끄적이는/components/EditInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { type ChangeEvent, type KeyboardEvent, useRef, useState } from 'react';

import { type UseInputReturn } from '@hooks/useInput';

import * as styles from './editInput.css';

interface EditInputProps {
inputProps: UseInputReturn;
}

const EditInput = ({ inputProps }: EditInputProps) => {
const inputRef = useRef<HTMLTextAreaElement | null>(null);
const [textareaHeight, setTextareaHeight] = useState<{
row: number;
lineBreak: Record<number, boolean>;
}>({
row: 1,
lineBreak: {},
});

const handleResize = (e: ChangeEvent<HTMLTextAreaElement>) => {
const { scrollHeight, clientHeight, value } = e.target;

if (value.length === 0) {
setTextareaHeight((prev) => ({
row: 1,
lineBreak: { ...prev.lineBreak, [e.target.value.length]: false },
}));
}

if (scrollHeight > clientHeight) {
setTextareaHeight((prev) => ({
row: prev.row + 1,
lineBreak: { ...prev.lineBreak, [value.length - 1]: true },
}));
}

if (textareaHeight.lineBreak[value.length]) {
setTextareaHeight((prev) => ({
row: prev.row - 1,
lineBreak: { ...prev.lineBreak, [value.length]: false },
}));
}
};

const handleKeydownEnter = (e: KeyboardEvent<HTMLTextAreaElement>) => {
if (e.code === 'Enter') {
setTextareaHeight((prev) => ({
row: prev.row + 1,
lineBreak: { ...prev.lineBreak, [inputProps.value.length]: true },
}));
return;
}
};

return (
<div className={styles.editContainer}>
<textarea
{...inputProps}
ref={inputRef}
className={styles.editInput}
autoComplete="off"
rows={textareaHeight.row}
maxLength={500}
onInput={handleResize}
onKeyDown={handleKeydownEnter}
/>
<p className={styles.editInputTextCount}>
<span className={styles.editTextCurrentCount}>
{inputProps.value.length}
</span>
/ 500
</p>
</div>
);
};

export default EditInput;
18 changes: 16 additions & 2 deletions src/domain/끄적이는/components/History/Column/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,31 @@ import * as styles from './style.css';

interface ColumnProps {
list: TemporalMemo[] | undefined;
editModeCardId: number | null;
resetEditModeCardId: VoidFunction;
onEditClick: (id: number) => void;
}

const Column = ({ list }: ColumnProps) => {
const Column = ({
list,
editModeCardId,
resetEditModeCardId,
onEditClick,
}: ColumnProps) => {
if (!list) {
return null;
}

return (
<ol className={styles.container}>
{list.map((el) => (
<WriteHistoryCard key={el.id} {...el} />
<WriteHistoryCard
key={el.id}
{...el}
isEditMode={editModeCardId === el.id}
onEditClick={() => onEditClick(el.id)}
onEditCompleteClick={resetEditModeCardId}
/>
))}
</ol>
);
Expand Down
15 changes: 14 additions & 1 deletion src/domain/끄적이는/components/History/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { useState } from 'react';
import dayjs from 'dayjs';

import 'dayjs/locale/ko';
Expand Down Expand Up @@ -32,6 +33,8 @@ const parser = (arr: TemporalMemo[]): TemporalMemo[][] => {
};

const TemporalMemoHistoryTable = ({ data }: TemporalMemoHistoryTableProps) => {
const [editModeCardId, setEditModeCardId] = useState<number | null>(null);

return (
<article className={styles.container}>
{data.map((hisotry, i) => {
Expand All @@ -50,7 +53,17 @@ const TemporalMemoHistoryTable = ({ data }: TemporalMemoHistoryTableProps) => {

<section className={styles.contentWrapper}>
{parser(temporalMemos).map((list, i) => {
return <Column key={i} list={list} />;
return (
<Column
key={i}
list={list}
editModeCardId={editModeCardId}
onEditClick={(id: number) => {
setEditModeCardId(id);
}}
resetEditModeCardId={() => setEditModeCardId(null)}
/>
);
})}
</section>
</section>
Expand Down
Loading

0 comments on commit 5f14b84

Please sign in to comment.