119 lines
3.7 KiB
TypeScript
119 lines
3.7 KiB
TypeScript
import { Editor, useEditorState } from "@tiptap/react";
|
|
import {
|
|
AlignCenter,
|
|
AlignLeft,
|
|
AlignRight,
|
|
Bold,
|
|
Heading1,
|
|
Heading2,
|
|
Heading3,
|
|
Highlighter,
|
|
Italic,
|
|
List,
|
|
ListOrdered,
|
|
} from "lucide-react";
|
|
import { Toggle } from "../ui/toggle";
|
|
|
|
export default function Menubar({ editor }: { editor: Editor | null }) {
|
|
if (!editor) {
|
|
return null;
|
|
}
|
|
|
|
const editorState = useEditorState({
|
|
editor,
|
|
selector: (ctx) => {
|
|
return {
|
|
// Text formatting
|
|
isBold: ctx.editor.isActive("bold") ?? false,
|
|
isItalic: ctx.editor.isActive("italic") ?? false,
|
|
isStrike: ctx.editor.isActive("strike") ?? false,
|
|
isHighlight: ctx.editor.isActive("highlight") ?? false,
|
|
|
|
// Text alignment
|
|
isAlignLeft: ctx.editor.isActive({ textAlign: "left" }) ?? false,
|
|
isAlignCenter: ctx.editor.isActive({ textAlign: "center" }) ?? false,
|
|
isAlignRight: ctx.editor.isActive({ textAlign: "right" }) ?? false,
|
|
isAlignJustify: ctx.editor.isActive({ textAlign: "justify" }) ?? false,
|
|
|
|
// Block types
|
|
isParagraph: ctx.editor.isActive("paragraph") ?? false,
|
|
isHeading1: ctx.editor.isActive("heading", { level: 1 }) ?? false,
|
|
isHeading2: ctx.editor.isActive("heading", { level: 2 }) ?? false,
|
|
isHeading3: ctx.editor.isActive("heading", { level: 3 }) ?? false,
|
|
};
|
|
},
|
|
});
|
|
|
|
const options: {
|
|
icon: React.ReactNode;
|
|
onClick: () => void;
|
|
pressed: boolean;
|
|
}[] = [
|
|
{
|
|
icon: <Heading1 className="size-4" />,
|
|
onClick: () => editor.chain().focus().toggleHeading({ level: 1 }).run(),
|
|
pressed: editor.isActive("heading", { level: 1 }),
|
|
},
|
|
{
|
|
icon: <Heading2 className="size-4" />,
|
|
onClick: () => editor.chain().focus().toggleHeading({ level: 2 }).run(),
|
|
pressed: editor.isActive("heading", { level: 2 }),
|
|
},
|
|
{
|
|
icon: <Heading3 className="size-4" />,
|
|
onClick: () => editor.chain().focus().toggleHeading({ level: 3 }).run(),
|
|
pressed: editor.isActive("heading", { level: 3 }),
|
|
},
|
|
{
|
|
icon: <Bold className="size-4" />,
|
|
onClick: () => editor.chain().focus().toggleBold().run(),
|
|
pressed: editor.isActive("bold"),
|
|
},
|
|
{
|
|
icon: <Italic className="size-4" />,
|
|
onClick: () => editor.chain().focus().toggleItalic().run(),
|
|
pressed: editor.isActive("italic"),
|
|
},
|
|
{
|
|
icon: <AlignLeft className="size-4" />,
|
|
onClick: () => editor.chain().focus().toggleTextAlign("left").run(),
|
|
pressed: editor.isActive({ textAlign: "left" }),
|
|
},
|
|
{
|
|
icon: <AlignCenter className="size-4" />,
|
|
onClick: () => editor.chain().focus().toggleTextAlign("center").run(),
|
|
pressed: editor.isActive({ textAlign: "center" }),
|
|
},
|
|
{
|
|
icon: <AlignRight className="size-4" />,
|
|
onClick: () => editor.chain().focus().toggleTextAlign("right").run(),
|
|
pressed: editor.isActive({ textAlign: "right" }),
|
|
},
|
|
{
|
|
icon: <List className="size-4" />,
|
|
onClick: () => editor.chain().focus().toggleBulletList().run(),
|
|
pressed: editor.isActive("bulletList"),
|
|
},
|
|
{
|
|
icon: <ListOrdered className="size-4" />,
|
|
onClick: () => editor.chain().focus().toggleOrderedList().run(),
|
|
pressed: editor.isActive("orderList"),
|
|
},
|
|
{
|
|
icon: <Highlighter className="size-4" />,
|
|
onClick: () => editor.chain().focus().toggleHighlight().run(),
|
|
pressed: editor.isActive("highlight"),
|
|
},
|
|
];
|
|
|
|
return (
|
|
<div className="border rounded-md p-1 mb-1 bg-slate-50 space-x-2 z-50">
|
|
{options.map((option, index) => (
|
|
<Toggle key={index} pressed={option.pressed} onClick={option.onClick}>
|
|
{option.icon}
|
|
</Toggle>
|
|
))}
|
|
</div>
|
|
);
|
|
}
|