diff --git a/src/components/editor/Editor.tsx b/src/components/editor/Editor.tsx index 2bbe474..7a62e62 100644 --- a/src/components/editor/Editor.tsx +++ b/src/components/editor/Editor.tsx @@ -97,7 +97,7 @@ const Editor = ({ content }: { content: JSONContent[] | null }) => { Underline, Highlight.configure({ multicolor: true }), TextAlign.configure({ - types: ['paragraph', 'image', 'blockquote', 'horizontal_rule'], + types: ['paragraph', 'image', 'blockquote', 'horizontal_rule', 'file'], }), Blockquote.configure({ HTMLAttributes: { diff --git a/src/components/editor/common/File.ts b/src/components/editor/common/File.ts new file mode 100644 index 0000000..8b002f0 --- /dev/null +++ b/src/components/editor/common/File.ts @@ -0,0 +1,52 @@ +export interface FileAttributes { + src: string; + title: string; +} + +export const createFileNodeHTML = (attrs: FileAttributes): HTMLElement => { + const fileWrapper = document.createElement('div'); + fileWrapper.className = + 'flex items-center justify-between p-3 border border-gray-300 rounded-lg shadow-sm bg-white max-w-md'; + + // 왼쪽 아이콘과 파일 제목 + const leftDiv = document.createElement('div'); + leftDiv.className = 'flex items-center space-x-2'; + + const iconSvg = ` + + + + `; + leftDiv.innerHTML = iconSvg; + + const fileTitle = document.createElement('span'); + fileTitle.className = 'text-sm font-medium text-gray-800 truncate'; + fileTitle.textContent = attrs.title || 'No title'; + + leftDiv.appendChild(fileTitle); + + // 오른쪽 다운로드 아이콘 + const rightSpan = document.createElement('span'); + rightSpan.className = + 'flex items-center justify-center w-8 h-8 text-blue-600 rounded-full transition'; + + const downloadLink = document.createElement('a'); + downloadLink.href = `http://43.200.90.72/file/download/${attrs.src}`; + downloadLink.target = '_blank'; + downloadLink.rel = 'noopener noreferrer'; + + const downloadSvg = ` + + + + `; + downloadLink.innerHTML = downloadSvg; + + rightSpan.appendChild(downloadLink); + + // 전체 구조 결합 + fileWrapper.appendChild(leftDiv); + fileWrapper.appendChild(rightSpan); + + return fileWrapper; +}; diff --git a/src/components/editor/common/extractPaywallData.ts b/src/components/editor/common/extractPaywallData.ts index d6b400d..775faf1 100644 --- a/src/components/editor/common/extractPaywallData.ts +++ b/src/components/editor/common/extractPaywallData.ts @@ -1,6 +1,7 @@ import { Editor } from '@tiptap/react'; import { JSONContent } from '@tiptap/core'; import { DOMSerializer } from '@tiptap/pm/model'; +import { createFileNodeHTML, FileAttributes } from './File'; interface PaywallData { isPremium: boolean; @@ -25,9 +26,18 @@ const extractPaywallData = (editor: Editor): PaywallData => { const tempDiv = document.createElement('div'); nodes.forEach((node) => { try { - const pmNode = schema.nodeFromJSON(node); - const serializedNode = domSerializer.serializeNode(pmNode); - tempDiv.appendChild(serializedNode); + if (node.type === 'file' && node.attrs?.src) { + const fileWrapper = createFileNodeHTML({ + src: node.attrs.src, + title: node.attrs.title, + } as FileAttributes); + + tempDiv.appendChild(fileWrapper); + } else { + const pmNode = schema.nodeFromJSON(node); + const serializedNode = domSerializer.serializeNode(pmNode); + tempDiv.appendChild(serializedNode); + } } catch (error) { console.error('Error converting node to HTML:', error, node); } diff --git a/src/components/editor/customComponent/CustomFile.ts b/src/components/editor/customComponent/CustomFile.ts index 97face7..22438bb 100644 --- a/src/components/editor/customComponent/CustomFile.ts +++ b/src/components/editor/customComponent/CustomFile.ts @@ -7,7 +7,7 @@ const CustomFile = Node.create({ group: 'block', atom: true, - + addAttributes() { return { src: { @@ -18,7 +18,7 @@ const CustomFile = Node.create({ }, }; }, - + parseHTML() { return [ { @@ -26,11 +26,23 @@ const CustomFile = Node.create({ }, ]; }, - + renderHTML({ HTMLAttributes }) { - return ['div', mergeAttributes(HTMLAttributes, { 'data-type': 'file' }), '']; + return [ + 'div', + mergeAttributes(HTMLAttributes, { 'data-type': 'file' }), + [ + 'a', + { + href: HTMLAttributes.src, + target: '_blank', + rel: 'noopener noreferrer', + }, + 'Download File', + ], + ]; }, - + addNodeView() { return ReactNodeViewRenderer(FileComponent); // React 컴포넌트와 연결 },