first commit
This commit is contained in:
73
app/components/editor.tsx
Normal file
73
app/components/editor.tsx
Normal file
@@ -0,0 +1,73 @@
|
||||
"use client";
|
||||
import type { FC } from "react";
|
||||
import { Crepe as CrepeClass } from "@milkdown/crepe";
|
||||
import { Milkdown, useEditor } from "@milkdown/react";
|
||||
import { Decoration } from "@milkdown/prose/view";
|
||||
import { upload, uploadConfig, type Uploader } from "@milkdown/plugin-upload";
|
||||
import type { Node as PMNode } from "@milkdown/prose/model";
|
||||
|
||||
// (선택) UI 스타일
|
||||
import "@milkdown/crepe/theme/common/style.css";
|
||||
import "@milkdown/crepe/theme/frame.css";
|
||||
|
||||
async function uploadToServer(file: File): Promise<string> {
|
||||
const fd = new FormData();
|
||||
fd.append("file", file);
|
||||
const res = await fetch("/api/upload", { method: "POST", body: fd });
|
||||
const data = await res.json();
|
||||
if (!res.ok || !data.ok) throw new Error(data?.error ?? "UPLOAD_FAILED");
|
||||
return data.url as string; // 예: /uploads/xxx.png
|
||||
}
|
||||
|
||||
export const MilkdownEditor: FC<{
|
||||
editorRef?: React.RefObject<CrepeClass>;
|
||||
}> = ({ editorRef }) => {
|
||||
useEditor((root) => {
|
||||
const crepe = new CrepeClass({
|
||||
root,
|
||||
defaultValue: "여기에 내용을 입력",
|
||||
featureConfigs: {
|
||||
[CrepeClass.Feature.ImageBlock]: {
|
||||
// ✅ 이 버전에선 string(URL)만 반환
|
||||
onUpload: async (file: File) => {
|
||||
const url = await uploadToServer(file);
|
||||
return url;
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (editorRef) editorRef.current = crepe;
|
||||
|
||||
const uploader: Uploader = async (files, schema) => {
|
||||
const nodes: PMNode[] = [];
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
const f = files.item(i);
|
||||
if (!f || !f.type.startsWith("image/")) continue;
|
||||
const url = await uploadToServer(f);
|
||||
const node = schema.nodes.image.createAndFill({ src: url, alt: f.name, title: "" });
|
||||
if (node) nodes.push(node);
|
||||
}
|
||||
return nodes;
|
||||
};
|
||||
|
||||
crepe.editor
|
||||
.config((ctx) => {
|
||||
ctx.set(uploadConfig.key, {
|
||||
enableHtmlFileUploader: true,
|
||||
uploadWidgetFactory: (pos) =>
|
||||
Decoration.widget(pos, () => {
|
||||
const el = document.createElement("span");
|
||||
el.className = "md-upload-placeholder";
|
||||
return el;
|
||||
}),
|
||||
uploader, // 붙여넣기/드래그 업로드
|
||||
});
|
||||
})
|
||||
.use(upload);
|
||||
|
||||
return crepe.editor;
|
||||
}, []);
|
||||
|
||||
return <Milkdown />;
|
||||
};
|
||||
Reference in New Issue
Block a user