feat(lesson-preparation): 树节点行组件
This commit is contained in:
@@ -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>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user