modul-s / src /components /RichTextEditor.tsx
sitcod3's picture
Upload src/components/RichTextEditor.tsx with huggingface_hub
f62c0b5 verified
Raw History Blame Contribute Delete
5.71 kB
import { useEditor, EditorContent } from "@tiptap/react";
import StarterKit from "@tiptap/starter-kit";
import { Bold, Italic, List, ListOrdered, Code, Undo, Redo, Sparkles } from "lucide-react";
import { useEffect } from "react";
import { cn } from "../utils/cn";
interface RichTextEditorProps {
content: string;
onChange: (html: string) => void;
placeholder?: string;
variables?: string[];
}
export default function RichTextEditor({
content,
onChange,
variables = ["first_name", "last_name", "company_name", "website", "location"],
}: RichTextEditorProps) {
const editor = useEditor({
extensions: [StarterKit],
content,
editorProps: {
attributes: {
class:
"prose prose-invert max-w-none focus:outline-none min-h-[160px] p-3 text-sm text-zinc-200 leading-relaxed",
},
},
onUpdate: ({ editor }) => {
onChange(editor.getHTML());
},
});
useEffect(() => {
if (editor && content !== editor.getHTML()) {
editor.commands.setContent(content);
}
}, [content, editor]);
if (!editor) return null;
const insertVariable = (variable: string) => {
editor.commands.insertContent(`{{${variable}}}`);
};
return (
<div className="border border-zinc-800 rounded-xl overflow-hidden bg-zinc-950/80 focus-within:border-indigo-500/80 transition-colors">
{/* Toolbar */}
<div className="flex flex-wrap items-center justify-between gap-1 p-2 border-b border-zinc-800/80 bg-zinc-900/60">
<div className="flex items-center gap-1">
<button
type="button"
onClick={() => editor.chain().focus().toggleBold().run()}
className={cn(
"p-1.5 rounded-md hover:bg-zinc-800 text-zinc-400 hover:text-zinc-200 transition-colors",
editor.isActive("bold") ? "bg-zinc-800 text-indigo-400 font-bold" : ""
)}
title="Bold"
>
<Bold className="w-4 h-4" />
</button>
<button
type="button"
onClick={() => editor.chain().focus().toggleItalic().run()}
className={cn(
"p-1.5 rounded-md hover:bg-zinc-800 text-zinc-400 hover:text-zinc-200 transition-colors",
editor.isActive("italic") ? "bg-zinc-800 text-indigo-400" : ""
)}
title="Italic"
>
<Italic className="w-4 h-4" />
</button>
<button
type="button"
onClick={() => editor.chain().focus().toggleBulletList().run()}
className={cn(
"p-1.5 rounded-md hover:bg-zinc-800 text-zinc-400 hover:text-zinc-200 transition-colors",
editor.isActive("bulletList") ? "bg-zinc-800 text-indigo-400" : ""
)}
title="Bullet List"
>
<List className="w-4 h-4" />
</button>
<button
type="button"
onClick={() => editor.chain().focus().toggleOrderedList().run()}
className={cn(
"p-1.5 rounded-md hover:bg-zinc-800 text-zinc-400 hover:text-zinc-200 transition-colors",
editor.isActive("orderedList") ? "bg-zinc-800 text-indigo-400" : ""
)}
title="Numbered List"
>
<ListOrdered className="w-4 h-4" />
</button>
<button
type="button"
onClick={() => editor.chain().focus().toggleCodeBlock().run()}
className={cn(
"p-1.5 rounded-md hover:bg-zinc-800 text-zinc-400 hover:text-zinc-200 transition-colors",
editor.isActive("codeBlock") ? "bg-zinc-800 text-indigo-400" : ""
)}
title="Code Block"
>
<Code className="w-4 h-4" />
</button>
</div>
<div className="flex items-center gap-1">
<button
type="button"
onClick={() => editor.chain().focus().undo().run()}
disabled={!editor.can().undo()}
className="p-1.5 rounded-md hover:bg-zinc-800 text-zinc-400 hover:text-zinc-200 disabled:opacity-30"
>
<Undo className="w-4 h-4" />
</button>
<button
type="button"
onClick={() => editor.chain().focus().redo().run()}
disabled={!editor.can().redo()}
className="p-1.5 rounded-md hover:bg-zinc-800 text-zinc-400 hover:text-zinc-200 disabled:opacity-30"
>
<Redo className="w-4 h-4" />
</button>
</div>
</div>
{/* Variables Bar */}
{variables.length > 0 && (
<div className="px-3 py-1.5 bg-zinc-900/40 border-b border-zinc-800/60 flex items-center gap-2 overflow-x-auto text-xs">
<span className="text-zinc-500 flex items-center gap-1 shrink-0">
<Sparkles className="w-3 h-3 text-indigo-400" /> Insert tag:
</span>
<div className="flex items-center gap-1.5">
{variables.map((v) => (
<button
key={v}
type="button"
onClick={() => insertVariable(v)}
className="px-2 py-0.5 bg-zinc-800/80 hover:bg-indigo-500/20 hover:text-indigo-300 text-zinc-400 rounded text-[11px] font-mono transition-colors border border-zinc-700/60 shrink-0"
>
&#123;&#123;{v}&#125;&#125;
</button>
))}
</div>
</div>
)}
{/* Editor Surface */}
<EditorContent editor={editor} />
</div>
);
}