"use client"; import { Button } from "@mdxui/primitives/button"; import { Card } from "@mdxui/primitives/card"; import { cn } from "@mdxui/primitives/lib/utils"; import { Toggle } from "@mdxui/primitives/toggle"; import Link from "@tiptap/extension-link"; import Placeholder from "@tiptap/extension-placeholder"; import Table from "@tiptap/extension-table"; import TableCell from "@tiptap/extension-table-cell"; import TableHeader from "@tiptap/extension-table-header"; import TableRow from "@tiptap/extension-table-row"; import TaskItem from "@tiptap/extension-task-item"; import TaskList from "@tiptap/extension-task-list"; import Underline from "@tiptap/extension-underline"; import { type Editor, EditorContent, useEditor } from "@tiptap/react"; import StarterKit from "@tiptap/starter-kit"; import { Bold, CheckSquare, Code, Heading1, Heading2, Heading3, Italic, Link as LinkIcon, List, ListOrdered, Minus, Quote, Redo, Strikethrough, Table as TableIcon, Underline as UnderlineIcon, Undo, } from "lucide-react"; import type React from "react"; import { useCallback } from "react"; export interface WYSIWYGEditorProps { /** Initial HTML content */ initialValue?: string; /** Callback when content changes */ onChange?: (html: string, json: any) => void; /** Placeholder text */ placeholder?: string; /** Height of the editor */ height?: string | number; /** Additional CSS class */ className?: string; /** Whether the editor is read-only */ readOnly?: boolean; /** Enable tables */ enableTables?: boolean; /** Enable task lists */ enableTaskLists?: boolean; /** Custom toolbar */ toolbar?: (editor: Editor) => React.ReactNode; } const MenuBar: React.FC<{ editor: Editor }> = ({ editor }) => { const setLink = useCallback(() => { const url = window.prompt("Enter URL"); if (url) { editor.chain().focus().setLink({ href: url }).run(); } }, [editor]); return (
editor.chain().focus().toggleBold().run()} aria-label="Bold" > editor.chain().focus().toggleItalic().run()} aria-label="Italic" > editor.chain().focus().toggleUnderline().run()} aria-label="Underline" > editor.chain().focus().toggleStrike().run()} aria-label="Strikethrough" > editor.chain().focus().toggleCode().run()} aria-label="Code" >
editor.chain().focus().toggleHeading({ level: 1 }).run() } aria-label="Heading 1" > editor.chain().focus().toggleHeading({ level: 2 }).run() } aria-label="Heading 2" > editor.chain().focus().toggleHeading({ level: 3 }).run() } aria-label="Heading 3" >
editor.chain().focus().toggleBulletList().run()} aria-label="Bullet List" > editor.chain().focus().toggleOrderedList().run()} aria-label="Ordered List" > editor.chain().focus().toggleTaskList().run()} aria-label="Task List" > editor.chain().focus().toggleBlockquote().run()} aria-label="Quote" >
); }; export const WYSIWYGEditor: React.FC = ({ initialValue = "", onChange, placeholder = "Start writing...", height = 400, className, readOnly = false, enableTables = true, enableTaskLists = true, toolbar, }) => { const editor = useEditor({ extensions: [ StarterKit, Placeholder.configure({ placeholder, }), Link.configure({ openOnClick: false, }), Underline, ...(enableTables ? [Table, TableRow, TableCell, TableHeader] : []), ...(enableTaskLists ? [ TaskList, TaskItem.configure({ nested: true, }), ] : []), ], content: initialValue, editable: !readOnly, onUpdate: ({ editor }) => { const html = editor.getHTML(); const json = editor.getJSON(); onChange?.(html, json); }, }); if (!editor) { return null; } return ( {toolbar ? toolbar(editor) : } ); };