import { type Component, createSignal, createEffect, For } from "solid-js"; import { Bold, Italic, Underline, Highlighter, AlignLeft, AlignCenter, AlignRight, Heading1, Heading2, Heading3, Eraser, ListTodo, Grid3X3 } from "lucide-solid"; import { cn } from "@/lib/utils"; import type { Editor } from "@tiptap/core"; interface GenericAction { label: string; icon: Component; isActive?: () => boolean; command: () => void; iconClass?: string; } interface FormatGroup { name: string; actions: GenericAction[]; } export const TextBubbleMenu: Component<{ editor: Editor }> = (props) => { // Track state of active styles so we can highlight the buttons const [activeFormats, setActiveFormats] = createSignal>({}); createEffect(() => { const updateState = () => { const editor = props.editor; if (!editor) return; setActiveFormats({ bold: editor.isActive('bold'), italic: editor.isActive('italic'), underline: editor.isActive('underline'), highlight: editor.isActive('highlight'), alignLeft: editor.isActive({ textAlign: 'left' }), alignCenter: editor.isActive({ textAlign: 'center' }), alignRight: editor.isActive({ textAlign: 'right' }), h1: editor.isActive('heading', { level: 1 }), h2: editor.isActive('heading', { level: 2 }), h3: editor.isActive('heading', { level: 3 }), checklist: editor.isActive('taskList'), table: editor.isActive('table'), }); }; props.editor.on("selectionUpdate", updateState); props.editor.on("transaction", updateState); return () => { props.editor.off("selectionUpdate", updateState); props.editor.off("transaction", updateState); }; }); const groups: FormatGroup[] = [ { name: "Style", actions: [ { label: "Bold", icon: () => , isActive: () => activeFormats().bold, command: () => props.editor.chain().focus().toggleBold().run() }, { label: "Italic", icon: () => , isActive: () => activeFormats().italic, command: () => props.editor.chain().focus().toggleItalic().run() }, { label: "Underline", icon: () => , isActive: () => activeFormats().underline, command: () => props.editor.chain().focus().toggleUnderline().run() }, { label: "Highlight", icon: () => , isActive: () => activeFormats().highlight, command: () => props.editor.chain().focus().toggleHighlight().run() }, ] }, { name: "Headings", actions: [ { label: "Heading 1", icon: () => , isActive: () => activeFormats().h1, command: () => props.editor.chain().focus().toggleHeading({ level: 1 }).run() }, { label: "Heading 2", icon: () => , isActive: () => activeFormats().h2, command: () => props.editor.chain().focus().toggleHeading({ level: 2 }).run() }, { label: "Heading 3", icon: () => , isActive: () => activeFormats().h3, command: () => props.editor.chain().focus().toggleHeading({ level: 3 }).run() }, ] }, { name: "Alignment", actions: [ { label: "Align Left", icon: () => , isActive: () => activeFormats().alignLeft, command: () => props.editor.chain().focus().setTextAlign('left').run() }, { label: "Align Center", icon: () => , isActive: () => activeFormats().alignCenter, command: () => props.editor.chain().focus().setTextAlign('center').run() }, { label: "Align Right", icon: () => , isActive: () => activeFormats().alignRight, command: () => props.editor.chain().focus().setTextAlign('right').run() }, ] }, { name: "Blocks", actions: [ { label: "Checklist", icon: () => , isActive: () => activeFormats().checklist, command: () => props.editor.chain().focus().toggleTaskList().run() }, { label: "Table", icon: () => , isActive: () => activeFormats().table, command: () => props.editor.chain().focus().insertTable({ rows: 3, cols: 3, withHeaderRow: true }).run() }, ] }, { name: "Clear", actions: [ { label: "Clear Formatting", icon: () => , command: () => props.editor.chain().focus().clearNodes().unsetAllMarks().run() }, ] } ]; return (
{ e.preventDefault(); e.stopPropagation(); }} >
{(group, groupIdx) => ( <>
{(action) => ( )}
{groupIdx() < groups.length - 1 && (
)} )}
); };