Skip to content

Commit

Permalink
Feat: #63 마크다운 기능 추가 CommonMarkdown, GFM, Rehype-raw for HTML)
Browse files Browse the repository at this point in the history
  • Loading branch information
Seok93 committed Aug 23, 2024
1 parent 3e2a2a1 commit 35354ca
Show file tree
Hide file tree
Showing 4 changed files with 167 additions and 5 deletions.
134 changes: 134 additions & 0 deletions src/components/common/CustomMarkdown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import React from 'react';
import Markdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
import rehypeRaw from 'rehype-raw';
import type { Components } from 'react-markdown';

type CustomMarkdownProps = {
markdown: string;
};

const component: Partial<Components> = {
h1(props) {
const { children } = props;
return (
<>
<h1 className="text-3xl">{children}</h1>
<hr className="my-3" />
</>
);
},
h2(props) {
const { children } = props;
return <h2 className="mb-3 text-2xl">{children}</h2>;
},
h3(props) {
const { children } = props;
return <h3 className="mb-3 text-xl">{children}</h3>;
},
h4(props) {
const { children } = props;
return <h3 className="mb-3 text-lg">{children}</h3>;
},
h5(props) {
const { children } = props;
return <h3 className="mb-3 text-base">{children}</h3>;
},
h6(props) {
const { children } = props;
return <h3 className="mb-3 text-sm">{children}</h3>;
},
hr() {
return <hr className="my-5" />;
},
a(props) {
const { href, children } = props;
return (
<a href={href} className="text-cyan-700">
{children}
</a>
);
},
img(props) {
const { src, alt } = props;
return <img src={src} alt={alt} className="m-auto" />;
},
blockquote(props) {
const { children } = props;
return <blockquote className="border-l-[3px] border-[#20C997] bg-[#F8F9FA] p-4">{children}</blockquote>;
},
table(props) {
const { children } = props;
return <table className="border-collapse overflow-hidden rounded-md shadow-md">{children}</table>;
},
th(props) {
const { children, style } = props;
return (
<th style={style} className="bg-[#4CAF50] p-5 font-bold uppercase text-white">
{children}
</th>
);
},
td(props) {
const { children, style } = props;
return (
<td style={style} className="last:border-b-none border-b border-[#dddddd] p-5">
{children}
</td>
);
},
tr(props) {
const { children, style } = props;
return (
<tr style={style} className="border-b border-[#dddddd] transition duration-300 even:bg-[#f4f4f4]">
{children}
</tr>
);
},
ol(props) {
const { children } = props;
const timeKey = Date.now();
return (
<ol key={timeKey} className="ml-10 list-decimal">
{children}
</ol>
);
},
ul(props) {
const { children } = props;
const timeKey = Date.now();
return (
<ul key={timeKey} className="ml-10 list-disc">
{children}
</ul>
);
},
section(props) {
const { children, className } = props;

if (className === 'footnotes') {
const newChildren = React.Children.toArray(children).slice(2) as React.ReactElement[];
return (
<section className={`${className} mt-10 border-t border-[#ddd] bg-[#f9f9f9] p-5`}>
<h3 className="mb-2 font-bold">각주 모음</h3>
{newChildren}
</section>
);
}
return <section className={className}>{children}</section>;
},
code(props) {
const { children, className } = props;
return <code className={`${className} rounded-sm border-none bg-[#E9ECEF] px-2`}>{children}</code>;
},
};

export default function CustomMarkdown({ markdown }: CustomMarkdownProps) {
return (
<section className="rounded-md border border-input p-10 text-sm">
<Markdown components={component} remarkPlugins={[remarkGfm]} rehypePlugins={[rehypeRaw]}>
{markdown}
</Markdown>
</section>
);
}
28 changes: 24 additions & 4 deletions src/components/modal/task/ModalTaskForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { IoMdCloseCircle } from 'react-icons/io';
import { TASK_VALIDATION_RULES } from '@constants/formValidationRules';
import RoleIcon from '@components/common/RoleIcon';
import ToggleButton from '@components/common/ToggleButton';
import CustomMarkdown from '@components/common/CustomMarkdown';
import DuplicationCheckInput from '@components/common/DuplicationCheckInput';
import useToast from '@hooks/useToast';
import useAxios from '@hooks/useAxios';
Expand All @@ -30,9 +31,11 @@ export default function ModalTaskForm({ formId, project, taskId, onSubmit }: Mod
const { projectId, startDate, endDate } = project;
const debounceRef = useRef<NodeJS.Timeout | null>(null);
const abortControllerRef = useRef<AbortController | null>(null);

const [hasDeadline, setHasDeadline] = useState(false);
const [keyword, setKeyword] = useState('');
const [workers, setWorkers] = useState<UserWithRole[]>([]);
const [preview, setPreview] = useState(false);

const { statusList } = useStatusQuery(projectId, taskId);
const { taskNameList } = useTaskQuery(projectId);
Expand Down Expand Up @@ -86,6 +89,8 @@ export default function ModalTaskForm({ formId, project, taskId, onSubmit }: Mod
setHasDeadline((prev) => !prev);
};

const handlePreviewToggle = () => setPreview((prev) => !prev);

const handleKeywordChange = (e: React.ChangeEvent<HTMLInputElement>) => setKeyword(e.target.value.trim());

const handleSearchClick = () => searchUsers();
Expand Down Expand Up @@ -169,8 +174,8 @@ export default function ModalTaskForm({ formId, project, taskId, onSubmit }: Mod
</div>
</label>
<label htmlFor="endDate" className="w-1/2">
<h3 className="flex items-center text-large">
<span className="mr-2">종료일</span>
<h3 className="flex items-center space-x-2 text-large">
<span>종료일</span>
<ToggleButton id="deadline" checked={hasDeadline} onChange={handleDeadlineToggle} />
</h3>
<input
Expand Down Expand Up @@ -247,10 +252,25 @@ export default function ModalTaskForm({ formId, project, taskId, onSubmit }: Mod
))}
</section>
</div>

<label htmlFor="content" className="mb-20">
<h3 className="text-large">내용</h3>
<textarea name="content" id="content" className="w-full border" rows={5} />
<h3 className="flex items-center space-x-2">
<span className="text-large">내용</span>
<ToggleButton id="preview" checked={preview} onChange={handlePreviewToggle} />
</h3>
{preview ? (
<CustomMarkdown markdown={watch('content')} />
) : (
<textarea
id="content"
rows={10}
className="w-full border border-input p-10 placeholder:text-xs"
placeholder="마크다운 형식으로 입력해주세요."
{...register('content')}
/>
)}
</label>

<label htmlFor="files">
<h3 className="text-large">첨부파일</h3>
<input type="file" id="files" />
Expand Down
8 changes: 8 additions & 0 deletions src/mocks/mockData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,7 @@ export const TASK_DUMMY: TaskListWithStatus[] = [
name: '할일 추가 모달 구현하기',
order: 1,
userId: 3,
content: '',
files: [],
startDate: '2024-06-26',
endDate: '2024-07-02',
Expand All @@ -520,6 +521,7 @@ export const TASK_DUMMY: TaskListWithStatus[] = [
name: 'ID 찾기 페이지 작성하기',
order: 2,
userId: 3,
content: '',
files: [],
startDate: '2024-07-03',
endDate: '2024-07-05',
Expand All @@ -529,6 +531,7 @@ export const TASK_DUMMY: TaskListWithStatus[] = [
name: 'DnD 구현하기',
order: 3,
userId: 1,
content: '',
files: [],
startDate: '2024-06-30',
endDate: '2024-07-02',
Expand All @@ -546,6 +549,7 @@ export const TASK_DUMMY: TaskListWithStatus[] = [
name: 'DnD 기술 조사하기',
order: 1,
userId: 1,
content: '',
files: [],
startDate: '2024-06-27',
endDate: '2024-06-29',
Expand All @@ -555,6 +559,7 @@ export const TASK_DUMMY: TaskListWithStatus[] = [
name: 'API 명세서 작성하기',
order: 2,
userId: 2,
content: '',
files: [],
startDate: '2024-06-27',
endDate: '2024-06-29',
Expand All @@ -572,6 +577,7 @@ export const TASK_DUMMY: TaskListWithStatus[] = [
name: 'task 상태 추가 모달 작업하기',
order: 1,
userId: 2,
content: '',
files: [],
startDate: '2024-06-22',
endDate: '2024-06-26',
Expand All @@ -581,6 +587,7 @@ export const TASK_DUMMY: TaskListWithStatus[] = [
name: 'project layout 작성하기',
order: 2,
userId: 1,
content: '',
files: [],
startDate: '2024-06-18',
endDate: '2024-06-21',
Expand All @@ -590,6 +597,7 @@ export const TASK_DUMMY: TaskListWithStatus[] = [
name: 'tailwindcss 설정하기',
order: 3,
userId: 3,
content: '',
files: [],
startDate: '2024-06-14',
endDate: '2024-06-18',
Expand Down
2 changes: 1 addition & 1 deletion src/types/TaskType.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export type Task = {
taskId: number;
name: string;
userId: number;
// content: string;
content: string;
startDate: string;
endDate: string;
files: string[];
Expand Down

0 comments on commit 35354ca

Please sign in to comment.