From 7b550d3b699a7d0627304eb0909d979ba94ed081 Mon Sep 17 00:00:00 2001 From: SpecialX <47072643+wangxiner55@users.noreply.github.com> Date: Sat, 4 Jul 2026 11:14:46 +0800 Subject: [PATCH] =?UTF-8?q?feat(lesson-preparation):=20expanded-slice?= =?UTF-8?q?=EF=BC=88=E8=8A=82=E7=82=B9=E5=B1=95=E5=BC=80=E7=8A=B6=E6=80=81?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hooks/expanded-slice.ts | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 src/modules/lesson-preparation/hooks/expanded-slice.ts diff --git a/src/modules/lesson-preparation/hooks/expanded-slice.ts b/src/modules/lesson-preparation/hooks/expanded-slice.ts new file mode 100644 index 0000000..f4e7e25 --- /dev/null +++ b/src/modules/lesson-preparation/hooks/expanded-slice.ts @@ -0,0 +1,56 @@ +import type { StateCreator } from "zustand"; +import type { EditorState } from "./use-lesson-plan-editor"; + +export interface ExpandedSlice { + /** 已展开到正文流的节点 ID */ + expandedNodeIds: string[]; + /** v3 迁移过来的失效锚点提示(按 planId 记录是否已 dismiss) */ + legacyAnchorDismissed: Record; + toggleExpand: (nodeId: string) => void; + setExpanded: (nodeIds: string[]) => void; + isExpanded: (nodeId: string) => boolean; + dismissLegacyAnchor: (planId: string) => void; + /** 同步 doc.expandedNodeIds(保存/加载时调用) */ + syncFromDoc: (expandedNodeIds: string[]) => void; +} + +export const createExpandedSlice: StateCreator< + EditorState, + [], + [], + ExpandedSlice +> = (set, get) => ({ + expandedNodeIds: [], + legacyAnchorDismissed: {}, + + toggleExpand: (nodeId) => { + get().pushHistory(); + set((s) => { + const isExp = s.expandedNodeIds.includes(nodeId); + const next = isExp + ? s.expandedNodeIds.filter((id) => id !== nodeId) + : [...s.expandedNodeIds, nodeId]; + // 同步到 doc + return { + expandedNodeIds: next, + doc: { ...s.doc, expandedNodeIds: next }, + isDirty: true, + }; + }); + }, + + setExpanded: (nodeIds) => + set((s) => ({ + expandedNodeIds: nodeIds, + doc: { ...s.doc, expandedNodeIds: nodeIds }, + })), + + isExpanded: (nodeId) => get().expandedNodeIds.includes(nodeId), + + dismissLegacyAnchor: (planId) => + set((s) => ({ + legacyAnchorDismissed: { ...s.legacyAnchorDismissed, [planId]: true }, + })), + + syncFromDoc: (expandedNodeIds) => set({ expandedNodeIds }), +});