feat(blog): add codemirror markdown editor

This commit is contained in:
2026-06-12 23:43:55 +03:30
parent cb8eeadba9
commit 25cbf53179
4 changed files with 954 additions and 18 deletions

View File

@@ -4,6 +4,7 @@ import { useEffect, useMemo, useRef, useState } from "react";
import { ArrowLeft, ArrowRight, FolderUp, ImageUp, Loader2, Save, Send, Trash2 } from "lucide-react";
import { useRouter } from "next/navigation";
import Markdown from "@/components/Markdown";
import MarkdownEditor, { type MarkdownDirectionMode } from "@/components/MarkdownEditor";
import { useAuth } from "@/contexts/AuthContext";
import { Link } from "@/lib/router";
import { Badge } from "@/components/ui/badge";
@@ -55,6 +56,7 @@ export default function AdminBlogEditor({ postId }: Props) {
const [loading, setLoading] = useState(Boolean(postId));
const [saving, setSaving] = useState(false);
const [uploadingFeatured, setUploadingFeatured] = useState(false);
const [editorDirection, setEditorDirection] = useState<MarkdownDirectionMode>("auto");
const isNew = postId == null;
const featuredImage = post?.absolute_featured_image_preview_url || post?.absolute_featured_image_url || post?.featured_image;
@@ -399,25 +401,28 @@ export default function AdminBlogEditor({ postId }: Props) {
<Card>
<CardHeader className="text-right">
<CardTitle>متن نوشته</CardTitle>
<CardDescription>ویرایشگر مارکداون در کنار پیشنمایش زنده، مشابه جریان نوشتن Quera.</CardDescription>
<CardDescription>ویرایشگر مارکداون در کنار پیشنمایش زنده</CardDescription>
</CardHeader>
<CardContent>
<div className="hidden grid-cols-2 gap-0 overflow-hidden rounded-3xl border bg-muted/20 md:grid">
<div className="border-l bg-background">
<div className="border-b px-4 py-3 text-right text-sm font-medium">پیشنمایش</div>
<div className="hidden grid-cols-2 gap-0 overflow-hidden bg-muted/20 md:grid">
<div className="bg-background ">
{/* <div className="border-b px-4 py-3 text-right text-sm font-medium">متن مارک‌داون</div> */}
<MarkdownEditor
value={form.content}
onChange={(value) => updateForm("content", value)}
minHeight="620px"
directionMode={editorDirection}
onDirectionModeChange={setEditorDirection}
onSave={savePost}
className="rounded-none border-0"
/>
</div>
<div className="bg-background">
{/* <div className="border-b px-4 py-3 text-right text-sm font-medium">پیش‌نمایش</div> */}
<div className="min-h-[560px] p-5">
<Markdown content={form.content || "هنوز محتوایی نوشته نشده است."} justify={false} size="base" />
</div>
</div>
<div className="bg-background">
<div className="border-b px-4 py-3 text-right text-sm font-medium">متن مارکداون</div>
<Textarea
value={form.content}
onChange={(event) => updateForm("content", event.target.value)}
className="min-h-[620px] resize-y rounded-none border-0 font-mono text-left shadow-none focus-visible:ring-0"
dir="ltr"
/>
</div>
</div>
<Tabs defaultValue="editor" className="md:hidden" dir="rtl">
@@ -426,11 +431,13 @@ export default function AdminBlogEditor({ postId }: Props) {
<TabsTrigger value="preview">پیشنمایش</TabsTrigger>
</TabsList>
<TabsContent value="editor">
<Textarea
<MarkdownEditor
value={form.content}
onChange={(event) => updateForm("content", event.target.value)}
className="min-h-[520px] font-mono text-left"
dir="ltr"
onChange={(value) => updateForm("content", value)}
minHeight="520px"
directionMode={editorDirection}
onDirectionModeChange={setEditorDirection}
onSave={savePost}
/>
</TabsContent>
<TabsContent value="preview">
@@ -445,7 +452,7 @@ export default function AdminBlogEditor({ postId }: Props) {
<Card>
<CardHeader className="text-right">
<CardTitle>تنظیمات سئو</CardTitle>
<CardDescription>این بخش جدا از متن اصلی است تا جریان نوشتن ساده و متمرکز بماند.</CardDescription>
{/* <CardDescription>این بخش جدا از متن اصلی است تا جریان نوشتن ساده و متمرکز بماند.</CardDescription> */}
</CardHeader>
<CardContent className="space-y-5">
<div className="grid gap-4 md:grid-cols-2">