| import { BubbleMenu } from "@tiptap/react/menus"; |
| import type { Editor } from "@tiptap/core"; |
| import { Tooltip } from "../components/Tooltip"; |
| import { |
| Bold, |
| Italic, |
| Strikethrough, |
| Code, |
| Code2, |
| Quote, |
| Link, |
| Heading2, |
| MessageCircle, |
| Sigma, |
| } from "lucide-react"; |
|
|
| interface BubbleToolbarProps { |
| editor: Editor; |
| onAddComment?: () => void; |
| } |
|
|
| function Btn({ |
| onClick, |
| active, |
| tooltip, |
| children, |
| }: { |
| onClick: () => void; |
| active?: boolean; |
| tooltip: string; |
| children: React.ReactNode; |
| }) { |
| return ( |
| <Tooltip title={tooltip} placement="top"> |
| <button |
| className={`icon-btn ${active ? "icon-btn--active" : ""}`} |
| onMouseDown={(e) => { |
| e.preventDefault(); |
| onClick(); |
| }} |
| aria-label={tooltip} |
| > |
| {children} |
| </button> |
| </Tooltip> |
| ); |
| } |
|
|
| export function BubbleToolbar({ editor, onAddComment }: BubbleToolbarProps) { |
| const setLink = () => { |
| const url = window.prompt("URL:"); |
| if (url) { |
| editor.chain().focus().setLink({ href: url }).run(); |
| } |
| }; |
|
|
| return ( |
| <BubbleMenu |
| editor={editor} |
| options={{ placement: "top", offset: 6 }} |
| > |
| <div className="bubble-toolbar"> |
| <Btn |
| onClick={() => editor.chain().focus().toggleBold().run()} |
| active={editor.isActive("bold")} |
| tooltip="Bold" |
| > |
| <Bold size={18} /> |
| </Btn> |
| <Btn |
| onClick={() => editor.chain().focus().toggleItalic().run()} |
| active={editor.isActive("italic")} |
| tooltip="Italic" |
| > |
| <Italic size={18} /> |
| </Btn> |
| <Btn |
| onClick={() => editor.chain().focus().toggleStrike().run()} |
| active={editor.isActive("strike")} |
| tooltip="Strikethrough" |
| > |
| <Strikethrough size={18} /> |
| </Btn> |
| <Btn |
| onClick={() => editor.chain().focus().toggleCode().run()} |
| active={editor.isActive("code")} |
| tooltip="Code" |
| > |
| <Code size={18} /> |
| </Btn> |
| <Btn |
| onClick={() => editor.chain().focus().insertInlineMath({ latex: "x^2" }).run()} |
| active={editor.isActive("inlineMath")} |
| tooltip="Inline math" |
| > |
| <Sigma size={18} /> |
| </Btn> |
| |
| <span className="divider-v" /> |
| |
| <Btn |
| onClick={() => editor.chain().focus().toggleHeading({ level: 2 }).run()} |
| active={editor.isActive("heading", { level: 2 })} |
| tooltip="Heading" |
| > |
| <Heading2 size={18} /> |
| </Btn> |
| <Btn |
| onClick={() => editor.chain().focus().toggleBlockquote().run()} |
| active={editor.isActive("blockquote")} |
| tooltip="Quote" |
| > |
| <Quote size={18} /> |
| </Btn> |
| <Btn |
| onClick={() => editor.chain().focus().toggleCodeBlock().run()} |
| active={editor.isActive("codeBlock")} |
| tooltip="Code block" |
| > |
| <Code2 size={18} /> |
| </Btn> |
| <Btn |
| onClick={setLink} |
| active={editor.isActive("link")} |
| tooltip="Link" |
| > |
| <Link size={18} /> |
| </Btn> |
| |
| {onAddComment && ( |
| <> |
| <span className="divider-v" /> |
| <Btn onClick={onAddComment} tooltip="Comment"> |
| <MessageCircle size={18} /> |
| </Btn> |
| </> |
| )} |
| </div> |
| </BubbleMenu> |
| ); |
| } |
|
|