"use client"; import type { Editor } from "@tiptap/react"; import { AlignCenter, AlignJustifyIcon, AlignLeft, AlignRight, Bold, Code, Code2, Heading1, Heading2, Heading3, ImageIcon, Italic, LinkIcon, List, ListOrdered, Minus, Pilcrow, Quote, Strikethrough, Underline, X, } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Level } from "@tiptap/extension-heading"; import { Separator } from "@/components/ui/separator"; import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "@/components/ui/tooltip"; import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover"; import { Label } from "./ui/label"; import { Input } from "./ui/input"; import { Switch } from "./ui/switch"; import { useState } from "react"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "./ui/dialog"; interface EditorMenuProps { editor: Editor | null; } export function EditorMenu({ editor }: EditorMenuProps) { if (!editor) { return null; } return ( editor .chain() .focus() .toggleBold() .run() } > Bold editor .chain() .focus() .toggleItalic() .run() } > Italic editor .chain() .focus() .toggleUnderline() .run() } > Underline editor .chain() .focus() .toggleStrike() .run() } > Strikethrough editor .chain() .focus() .toggleCode() .run() } > Code { if (value === "paragraph") { editor.chain().focus().setParagraph().run(); } else { editor .chain() .focus() .toggleHeading({ level: Number.parseInt(value) as Level, }) .run(); } }} > Paragraph Heading 1 Heading 2 Heading 3 editor .chain() .focus() .setTextAlign("left") .run() } > Align left editor .chain() .focus() .setTextAlign("center") .run() } > Align center editor .chain() .focus() .setTextAlign("right") .run() } > Align right editor .chain() .focus() .setTextAlign("justify") .run() } > Align justify editor .chain() .focus() .toggleBulletList() .run() } > Bullet list editor .chain() .focus() .toggleOrderedList() .run() } > Ordered list editor .chain() .focus() .toggleCodeBlock() .run() } > Code block editor .chain() .focus() .toggleBlockquote() .run() } > Blockquote editor .chain() .focus() .setHorizontalRule() .run() } > Horizontal rule editor .chain() .focus() .clearNodes() .unsetAllMarks() .run() } > Clear formatting Add link Add image ); } function getActiveHeading(editor: Editor) { if (editor.isActive("heading", { level: 1 })) return "1"; if (editor.isActive("heading", { level: 2 })) return "2"; if (editor.isActive("heading", { level: 3 })) return "3"; return "paragraph"; } const LinkPopover: React.FC<{ editor: Editor }> = ({ editor }) => { const [linkUrl, setLinkUrl] = useState(""); const [linkNewTab, setLinkNewTab] = useState(false); const [isLinkOpen, setIsLinkOpen] = useState(false); const setLink = () => { if (linkUrl) { editor .chain() .focus() .setLink({ href: linkUrl, target: linkNewTab ? "_blank" : null, }) .run(); } else { editor.chain().focus().unsetLink().run(); } setIsLinkOpen(false); setLinkUrl(""); setLinkNewTab(false); }; return ( Link URL setLinkUrl(e.target.value)} placeholder="https://example.com" /> Open in new tab Save {editor.isActive("link") && ( { editor.chain().focus().unsetLink().run(); setIsLinkOpen(false); }} > Remove )} ); }; const ImageDialog: React.FC<{ editor: Editor }> = ({ editor }) => { const [isImageOpen, setIsImageOpen] = useState(false); const [imageUrl, setImageUrl] = useState(""); const [imageAlt, setImageAlt] = useState(""); const addImage = () => { if (imageUrl) { editor .chain() .focus() .setImage({ src: imageUrl, alt: imageAlt, }) .run(); } setIsImageOpen(false); setImageUrl(""); setImageAlt(""); }; return ( setIsImageOpen(true)} > Add image Insert an image by providing its URL and alt text. Image URL setImageUrl(e.target.value)} placeholder="https://example.com/image.jpg" /> Alt text setImageAlt(e.target.value)} placeholder="Description of the image" /> setIsImageOpen(false)} > Cancel Add image ); };