feat(lesson-preparation): 浮动工具条 + 锚点/inline-node/对话体样式

This commit is contained in:
SpecialX
2026-07-04 11:26:55 +08:00
parent 66a60213bf
commit 9b0b7ef619
2 changed files with 181 additions and 0 deletions

View File

@@ -0,0 +1,47 @@
"use client";
import type { Editor } from "@tiptap/react";
interface Props {
editor: Editor;
}
/**
* V4 浮动工具条:粘在纸顶部,毛玻璃半透明。
* 仅在编辑器聚焦时显示(由父组件控制)。
*/
export function PaperToolbar({ editor }: Props) {
const btn = (
label: string,
action: () => void,
isActive: boolean,
className: string,
title: string,
) => (
<button
type="button"
onClick={action}
title={title}
className={`lp-tb-btn ${isActive ? "is-active" : ""} ${className}`}
onMouseDown={(e) => e.preventDefault()} // 防止失焦
>
{label}
</button>
);
return (
<div className="lp-paper-toolbar">
{btn("B", () => editor.chain().focus().toggleBold().run(), editor.isActive("bold"), "b", "Bold")}
{btn("I", () => editor.chain().focus().toggleItalic().run(), editor.isActive("italic"), "i", "Italic")}
<span className="lp-tb-divider" />
{btn("H1", () => editor.chain().focus().toggleHeading({ level: 1 }).run(), editor.isActive("heading", { level: 1 }), "h1", "Heading 1")}
{btn("H2", () => editor.chain().focus().toggleHeading({ level: 2 }).run(), editor.isActive("heading", { level: 2 }), "h2", "Heading 2")}
<span className="lp-tb-divider" />
{btn("•", () => editor.chain().focus().toggleBulletList().run(), editor.isActive("bulletList"), "ul", "Bullet list")}
{btn("1.", () => editor.chain().focus().toggleOrderedList().run(), editor.isActive("orderedList"), "ol", "Ordered list")}
<span className="lp-tb-divider" />
{btn('"', () => editor.chain().focus().toggleBlockquote().run(), editor.isActive("blockquote"), "quote", "Quote")}
{btn("—", () => editor.chain().focus().setHorizontalRule().run(), false, "hr", "Divider")}
</div>
);
}