summaryrefslogtreecommitdiff
path: root/components/qna/tiptap-editor.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'components/qna/tiptap-editor.tsx')
-rw-r--r--components/qna/tiptap-editor.tsx98
1 files changed, 69 insertions, 29 deletions
diff --git a/components/qna/tiptap-editor.tsx b/components/qna/tiptap-editor.tsx
index 5d0a84e9..b1cebf5a 100644
--- a/components/qna/tiptap-editor.tsx
+++ b/components/qna/tiptap-editor.tsx
@@ -1,4 +1,5 @@
import { useEditor, EditorContent } from '@tiptap/react'
+import { useEffect } from 'react' // useEffect 추가
import StarterKit from '@tiptap/starter-kit'
import Underline from '@tiptap/extension-underline'
import { Image as TiptapImage } from '@tiptap/extension-image'
@@ -26,6 +27,18 @@ interface TiptapEditorProps {
height?: string; // 높이 prop 추가
}
+// 이미지 크기 가져오기 헬퍼 함수
+function getImageDimensions(url: string): Promise<{ width: number; height: number }> {
+ return new Promise((resolve, reject) => {
+ const img = new Image()
+ img.onload = () => {
+ resolve({ width: img.width, height: img.height })
+ }
+ img.onerror = reject
+ img.src = url
+ })
+}
+
export default function TiptapEditor({ content, setContent, disabled, height = "300px" }: TiptapEditorProps) {
const editor = useEditor({
extensions: [
@@ -135,6 +148,21 @@ export default function TiptapEditor({ content, setContent, disabled, height = "
},
})
+ // ✅ 핵심 추가: content prop이 변경될 때 에디터 내용 동기화
+ useEffect(() => {
+ if (editor && content !== editor.getHTML()) {
+ console.log('Updating editor content:', content) // 디버깅용
+ editor.commands.setContent(content, false) // false: focus 유지하지 않음
+ }
+ }, [content, editor])
+
+ // ✅ editable 상태 변경 처리
+ useEffect(() => {
+ if (editor) {
+ editor.setEditable(!disabled)
+ }
+ }, [disabled, editor])
+
async function uploadImageToServer(file: File): Promise<string> {
const formData = new FormData();
formData.append('file', file);
@@ -153,39 +181,51 @@ export default function TiptapEditor({ content, setContent, disabled, height = "
return url;
}
-// Base64 → 서버 업로드 방식으로 교체
-const handleImageUpload = async (file: File) => {
- try {
- if (file.size > 3 * 1024 * 1024) {
- alert('이미지 크기는 3 MB 이하만 지원됩니다.');
- return;
- }
- if (!file.type.startsWith('image/')) {
- alert('이미지 파일만 업로드 가능합니다.');
- return;
- }
+ // Base64 → 서버 업로드 방식으로 교체
+ const handleImageUpload = async (file: File) => {
+ try {
+ if (file.size > 3 * 1024 * 1024) {
+ alert('이미지 크기는 3 MB 이하만 지원됩니다.');
+ return;
+ }
+ if (!file.type.startsWith('image/')) {
+ alert('이미지 파일만 업로드 가능합니다.');
+ return;
+ }
- const url = await uploadImageToServer(file); // ← 업로드 & URL 획득
+ const url = await uploadImageToServer(file); // ← 업로드 & URL 획득
- // 이미지 크기(너비)에 따라 style 조정
- const { width, height } = await getImageDimensions(url);
- const maxWidth = 600;
- const newW = width > maxWidth ? maxWidth : width;
- const newH = width > maxWidth ? (height * maxWidth) / width : height;
+ // 이미지 크기(너비)에 따라 style 조정
+ const { width, height } = await getImageDimensions(url);
+ const maxWidth = 600;
+ const newW = width > maxWidth ? maxWidth : width;
+ const newH = width > maxWidth ? (height * maxWidth) / width : height;
+
+ editor
+ ?.chain()
+ .focus()
+ .setImage({
+ src: url,
+ style: `width:${newW}px;height:${newH}px;max-width:100%;`,
+ })
+ .run();
+ } catch (e) {
+ console.error(e);
+ alert('이미지 업로드에 실패했습니다.');
+ }
+ };
- editor
- ?.chain()
- .focus()
- .setImage({
- src: url,
- style: `width:${newW}px;height:${newH}px;max-width:100%;`,
- })
- .run();
- } catch (e) {
- console.error(e);
- alert('이미지 업로드에 실패했습니다.');
+ // ✅ 에디터가 준비되지 않았을 때 로딩 표시
+ if (!editor) {
+ return (
+ <div
+ className="border rounded-md bg-background flex items-center justify-center"
+ style={{ height }}
+ >
+ <div className="text-muted-foreground text-sm">에디터를 로딩 중...</div>
+ </div>
+ )
}
-};
// 높이 계산 (100%인 경우 flex 사용, 아니면 구체적 높이)
const containerStyle = height === "100%"