feat(lesson-preparation): 树节点行组件

This commit is contained in:
SpecialX
2026-07-04 11:30:57 +08:00
parent 6fa712ad15
commit c7cbc86fd4

View File

@@ -0,0 +1,149 @@
"use client";
import { useTranslations } from "next-intl";
import type { Block } from "../../types";
import { useLessonPlanEditor } from "../../hooks/use-lesson-plan-editor";
interface Props {
node: Block;
/** 是否展开到正文 */
isExpanded: boolean;
/** 是否有子节点(如师生交互的对话轮次) */
hasChildren?: boolean;
isChildrenExpanded?: boolean;
onToggleChildren?: () => void;
/** 子节点(如对话轮次) */
children?: { id: string; label: string; kind: string }[];
}
export function TreeNodeRow({
node,
isExpanded,
hasChildren,
isChildrenExpanded,
onToggleChildren,
children,
}: Props) {
const t = useTranslations("lessonPreparation");
const selectedNodeId = useLessonPlanEditor((s) => s.selectedNodeId);
const selectNode = useLessonPlanEditor((s) => s.selectNode);
const toggleExpand = useLessonPlanEditor((s) => s.toggleExpand);
const isActive = selectedNodeId === node.id;
const dotColor = `var(--lp-dot-${node.type})`;
return (
<div>
<div
role="button"
tabIndex={0}
onClick={() => selectNode(node.id)}
onKeyDown={(e) => {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
selectNode(node.id);
}
}}
style={{
display: "flex",
alignItems: "center",
gap: 6,
padding: "6px 8px",
borderRadius: 5,
cursor: "pointer",
background: isActive ? "var(--muted)" : "transparent",
lineHeight: 1.3,
fontSize: 13,
}}
className="lp-tree-row"
>
<span
style={{
width: 14,
height: 14,
display: "inline-flex",
alignItems: "center",
justifyContent: "center",
fontSize: 9,
color: "var(--muted-foreground)",
cursor: hasChildren ? "pointer" : "default",
visibility: hasChildren ? "visible" : "hidden",
}}
onClick={(e) => {
if (hasChildren && onToggleChildren) {
e.stopPropagation();
onToggleChildren();
}
}}
>
{hasChildren ? (isChildrenExpanded ? "▾" : "▸") : ""}
</span>
<span style={{ width: 6, height: 6, borderRadius: "50%", background: dotColor, flexShrink: 0 }} />
<span
style={{
flex: 1,
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
color: "var(--foreground)",
}}
>
{node.title}
</span>
<span style={{ fontSize: 10, color: "var(--muted-foreground)", fontWeight: 500 }}>
{node.type}
</span>
{isExpanded && (
<button
type="button"
onClick={(e) => {
e.stopPropagation();
toggleExpand(node.id);
}}
style={{
fontSize: 9,
color: "var(--foreground)",
background: "var(--muted)",
padding: "1px 5px",
borderRadius: 2,
fontWeight: 500,
border: "none",
cursor: "pointer",
}}
>
{t("v4.tree.onPaper")}
</button>
)}
</div>
{hasChildren && isChildrenExpanded && children && (
<div
style={{
marginLeft: 10,
paddingLeft: 8,
borderLeft: "1px solid var(--border)",
}}
>
{children.map((c) => (
<div
key={c.id}
style={{
display: "flex",
alignItems: "center",
gap: 6,
padding: "6px 8px",
borderRadius: 5,
fontSize: 12,
color: "var(--muted-foreground)",
}}
>
<span style={{ width: 6, height: 6, borderRadius: "50%", background: "var(--lp-dot-default)", opacity: 0.5 }} />
<span style={{ flex: 1 }}>{c.label}</span>
<span style={{ fontSize: 10 }}>{c.kind}</span>
</div>
))}
</div>
)}
</div>
);
}