"use client"; import { BlockNoteView } from "@blocknote/mantine"; import { de } from "@blocknote/core/locales"; import { useCreateBlockNote } from "@blocknote/react"; import { useEffect } from "react"; const API_URL = process.env.NEXT_PUBLIC_API_URL ?? "http://localhost:3002"; type NewsBlockNoteEditorProps = { label?: string; value: string; placeholder?: string; minHeightClassName?: string; ensureLeadingHeading?: boolean; onChange: (next: string) => void; }; function normalizeHtml(html: string) { const trimmed = html.trim(); if (!trimmed) return ""; if (trimmed === "

" || trimmed === "


") return ""; return trimmed; } function plainTextFromHtml(html: string) { return html.replace(/<[^>]*>/g, " ").replace(/\s+/g, " ").trim(); } async function uploadEditorImage(file: File) { const formData = new FormData(); formData.append("file", file); const response = await fetch(`${API_URL}/api/uploads/image`, { method: "POST", credentials: "include", body: formData, }); const payload = (await response.json().catch(() => ({}))) as { url?: string; error?: string }; if (!response.ok || !payload.url) { throw new Error(payload.error ?? "Bild-Upload fehlgeschlagen"); } return payload.url; } export function NewsBlockNoteEditor({ label, value, placeholder = "Hier den Beitrag schreiben...", minHeightClassName = "min-h-[480px]", ensureLeadingHeading = false, onChange, }: NewsBlockNoteEditorProps) { const editor = useCreateBlockNote({ dictionary: { ...de, placeholders: { ...(de as { placeholders?: Record }).placeholders, default: placeholder, emptyDocument: placeholder, }, }, uploadFile: uploadEditorImage, }); useEffect(() => { const currentHtml = normalizeHtml(editor.blocksToHTMLLossy(editor.document)); const targetHtml = normalizeHtml(value || ""); if (currentHtml === targetHtml) return; const starterBlocks = [ { type: "heading", props: { level: 1 }, }, { type: "paragraph", }, ] as never; try { const blocks = targetHtml ? editor.tryParseHTMLToBlocks(targetHtml) : []; if (blocks.length > 0) { editor.replaceBlocks(editor.document, blocks); return; } const fallbackText = plainTextFromHtml(targetHtml); if (!fallbackText) { editor.replaceBlocks(editor.document, ensureLeadingHeading ? starterBlocks : []); return; } editor.replaceBlocks(editor.document, [ { type: "paragraph", content: fallbackText, } as never, ]); } catch { const fallbackText = plainTextFromHtml(targetHtml); if (!fallbackText) { if (ensureLeadingHeading) { editor.replaceBlocks(editor.document, starterBlocks); } return; } editor.replaceBlocks(editor.document, [ { type: "paragraph", content: fallbackText, } as never, ]); } }, [editor, ensureLeadingHeading, value]); return (
{label ? : null}
{ const html = normalizeHtml(editor.blocksToHTMLLossy(editor.document)); onChange(html); }} slashMenu formattingToolbar sideMenu tableHandles className="bg-white" />
); }