diff --git a/package-lock.json b/package-lock.json index 498e502b..e4468158 100644 --- a/package-lock.json +++ b/package-lock.json @@ -56,8 +56,10 @@ "react-syntax-highlighter": "^15.5.0", "recharts": "^2.12.7", "refractor": "^4.8.1", + "remark": "^15.0.1", "remark-gfm": "^4.0.0", "remirror": "^2.0.39", + "strip-markdown": "^6.0.0", "tailwind-merge": "^2.2.2", "tailwind-scrollbar-hide": "^1.1.7", "tailwindcss-animate": "^1.0.7", @@ -20273,6 +20275,21 @@ "node": ">= 0.10" } }, + "node_modules/remark": { + "version": "15.0.1", + "resolved": "https://registry.npmjs.org/remark/-/remark-15.0.1.tgz", + "integrity": "sha512-Eht5w30ruCXgFmxVUSlNWQ9iiimq07URKeFS3hNc8cUWy1llX4KDWfyEDZRycMc+znsN9Ux5/tJ/BFdgdOwA3A==", + "dependencies": { + "@types/mdast": "^4.0.0", + "remark-parse": "^11.0.0", + "remark-stringify": "^11.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/remark-gfm": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.0.tgz", @@ -21765,6 +21782,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strip-markdown": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-markdown/-/strip-markdown-6.0.0.tgz", + "integrity": "sha512-mSa8FtUoX3ExJYDkjPUTC14xaBAn4Ik5GPQD45G5E2egAmeV3kHgVSTfIoSDggbF6Pk9stahVgqsLCNExv6jHw==", + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/style-loader": { "version": "3.3.4", "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.4.tgz", diff --git a/package.json b/package.json index b6c05d0e..b9c5f006 100644 --- a/package.json +++ b/package.json @@ -66,8 +66,10 @@ "react-syntax-highlighter": "^15.5.0", "recharts": "^2.12.7", "refractor": "^4.8.1", + "remark": "^15.0.1", "remark-gfm": "^4.0.0", "remirror": "^2.0.39", + "strip-markdown": "^6.0.0", "tailwind-merge": "^2.2.2", "tailwind-scrollbar-hide": "^1.1.7", "tailwindcss-animate": "^1.0.7", diff --git a/src/app/(routes)/document/[id]/(main)/@header/default.tsx b/src/app/(routes)/document/[id]/(main)/@header/default.tsx index 5d4fb704..323ee536 100644 --- a/src/app/(routes)/document/[id]/(main)/@header/default.tsx +++ b/src/app/(routes)/document/[id]/(main)/@header/default.tsx @@ -24,7 +24,7 @@ const Header = () => { const router = useRouter() const { id } = useParams() const { getPreviousPath } = usePreviousPath({ getCustomPath: true }) - const { data } = useQuery(queries.document.item(Number(id[0]))) + const { data } = useQuery(queries.document.item(Number(id))) const handleClickCancel = () => { const previousPath = getPreviousPath() @@ -58,7 +58,7 @@ const Header = () => { 130 - + {/* 노션일 경우 아래 아이콘 렌더링 */} @@ -149,7 +149,7 @@ const Header = () => { {/* data: 노트 제목, 문제 수, 글자 수, 마지막 수정 날짜 */}
-

{data?.name}

+

{data?.documentName}

{data?.totalQuizCount}문제 diff --git a/src/app/(routes)/document/[id]/modify/@header/default.tsx b/src/app/(routes)/document/[id]/modify/@header/default.tsx index f010bcc5..212e0ab4 100644 --- a/src/app/(routes)/document/[id]/modify/@header/default.tsx +++ b/src/app/(routes)/document/[id]/modify/@header/default.tsx @@ -22,11 +22,11 @@ const Header = () => { const file = new File([blob], `${title}.md`, { type: 'text/markdown' }) updateDocumentMutate( - { documentId: Number(id[0]), request: { name: title, file } }, + { documentId: Number(id), request: { name: title, file } }, { onSuccess: () => { toast({ description: '노트가 수정되었어요' }) - router.push(`/document/${id[0]}`) + router.push('/document/' + String(id)) }, } ) diff --git a/src/features/document/components/document-detail-controller.tsx b/src/features/document/components/document-detail-controller.tsx index 6cc2f3d8..142168f7 100644 --- a/src/features/document/components/document-detail-controller.tsx +++ b/src/features/document/components/document-detail-controller.tsx @@ -22,7 +22,7 @@ const DocumentDetailController = () => { {tabs.map((tab) => ( { + const result = unified() + .use(remarkParse) // 마크다운 파싱 + .use(stripMarkdown) // 문법 제거 + .use(remarkStringify) + .processSync(markdown) // 동기 처리 + + return String(result) // 결과 반환 + } + return (
{ @@ -122,8 +136,9 @@ const SwipeableDocumentCard = ({ typography="text1-regular" className="w-[calc(100%-55px)] truncate text-nowrap break-all text-text-sub" > - {content} + {getTextRemoveMarkdownSyntax(content)} + {quizCount}문제 diff --git a/src/features/document/screens/document-content.tsx b/src/features/document/screens/document-content.tsx index 5f80467a..1b027562 100644 --- a/src/features/document/screens/document-content.tsx +++ b/src/features/document/screens/document-content.tsx @@ -5,16 +5,63 @@ import Text from '@/shared/components/ui/text' import { queries } from '@/shared/lib/tanstack-query/query-keys' import { useQuery } from '@tanstack/react-query' import { useParams } from 'next/navigation' +import { ClassAttributes, HTMLAttributes } from 'react' +import Markdown, { ExtraProps } from 'react-markdown' +import SyntaxHighlighter from 'react-syntax-highlighter' +import { dracula } from 'react-syntax-highlighter/dist/esm/styles/prism' +import remarkGfm from 'remark-gfm' const DocumentContent = () => { const { id } = useParams() - const { data, isPending } = useQuery(queries.document.item(Number(id[0]))) + const { data, isPending } = useQuery(queries.document.item(Number(id))) + + const formattedContent = data?.content.replace(/\n/g, '\n\n') return (
- {isPending ? : data && {data.content}} + {isPending ? ( + + ) : ( + data && ( + + + {formattedContent} + + + ) + )}
) } export default DocumentContent + +const handleMarkDownCodeBlock = ( + props: ClassAttributes & HTMLAttributes & ExtraProps +) => { + // style, node, ref는 사용하지 않음 + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { children, className, style, node, ref, ...rest } = props + const match = /language-(\w+)/.exec(className || '') + return match ? ( + + {String(children).replace(/\n$/, '')} + + ) : ( + + {children} + + ) +} + +const handleParagraph = (props: HTMLAttributes) => { + return ( + <> +

{props.children}

+
+ + ) +} diff --git a/src/features/pages/documents-in-directory.tsx b/src/features/pages/documents-in-directory.tsx index 171d89da..aa424c6a 100644 --- a/src/features/pages/documents-in-directory.tsx +++ b/src/features/pages/documents-in-directory.tsx @@ -58,7 +58,7 @@ const DocumentsInDirectory = () => { id={document.id} createType={document.documentType} title={document.name} - content={document.content.slice(0, 40)} + content={document.previewContent ?? ''} quizCount={document.totalQuizCount} characterCount={document.characterCount} directory={document.directory.name} diff --git a/src/features/pages/modify-document.tsx b/src/features/pages/modify-document.tsx index f40d827c..51cfc1e4 100644 --- a/src/features/pages/modify-document.tsx +++ b/src/features/pages/modify-document.tsx @@ -13,7 +13,7 @@ import { queries } from '@/shared/lib/tanstack-query/query-keys' const ModifyDocument = () => { const { id } = useParams() - const { data, isPending } = useQuery(queries.document.item(Number(id[0]))) + const { data, isPending } = useQuery(queries.document.item(Number(id))) const { editorMarkdownContent: content } = useEditDocumentContext() if (isPending) { diff --git a/src/types/document.d.ts b/src/types/document.d.ts index dfe18e3f..254ba7be 100644 --- a/src/types/document.d.ts +++ b/src/types/document.d.ts @@ -31,7 +31,9 @@ type DocumentItem = { id: number documentType: CreateType name: string + documentName?: string status: DocumentStatus + previewContent?: string content: string characterCount: number totalQuizCount: number diff --git a/yarn.lock b/yarn.lock index adc013a8..78dd9595 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11069,6 +11069,16 @@ remark-stringify@^11.0.0: mdast-util-to-markdown "^2.0.0" unified "^11.0.0" +remark@^15.0.1: + version "15.0.1" + resolved "https://registry.npmjs.org/remark/-/remark-15.0.1.tgz" + integrity sha512-Eht5w30ruCXgFmxVUSlNWQ9iiimq07URKeFS3hNc8cUWy1llX4KDWfyEDZRycMc+znsN9Ux5/tJ/BFdgdOwA3A== + dependencies: + "@types/mdast" "^4.0.0" + remark-parse "^11.0.0" + remark-stringify "^11.0.0" + unified "^11.0.0" + remirror@^2.0.39, remirror@2.0.40: version "2.0.40" resolved "https://registry.npmjs.org/remirror/-/remirror-2.0.40.tgz" @@ -11810,6 +11820,13 @@ strip-json-comments@^3.1.1: resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== +strip-markdown@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/strip-markdown/-/strip-markdown-6.0.0.tgz" + integrity sha512-mSa8FtUoX3ExJYDkjPUTC14xaBAn4Ik5GPQD45G5E2egAmeV3kHgVSTfIoSDggbF6Pk9stahVgqsLCNExv6jHw== + dependencies: + "@types/mdast" "^4.0.0" + style-loader@^3.3.1: version "3.3.4" resolved "https://registry.npmjs.org/style-loader/-/style-loader-3.3.4.tgz"