feat(lesson-preparation): add readonly view, anchor node selector, and type guards
- Add lesson-plan-readonly-view for viewing published plans - Add anchor-node-selector and textbook-segments for canvas anchor positioning - Add i18n-errors and type-guards lib utilities - Add lesson-plan-provider-setup for provider initialization - Update actions, data-access (knowledge, versions, main), publish-service - Update blocks (blackboard, exercise, homework, import, key-point, objective, reflection) - Update editor, node-editor, node-edit-panel, pickers, and providers
This commit is contained in:
@@ -1,19 +1,21 @@
|
||||
import type { ReactElement } from "react";
|
||||
import type {
|
||||
BlackboardBlockData,
|
||||
BlockData,
|
||||
BlockType,
|
||||
ExerciseBlockData,
|
||||
HomeworkBlockData,
|
||||
ImportBlockData,
|
||||
KeyPointBlockData,
|
||||
NewTeachingBlockData,
|
||||
ObjectiveBlockData,
|
||||
ReflectionBlockData,
|
||||
RichTextBlockData,
|
||||
SummaryBlockData,
|
||||
TextStudyBlockData,
|
||||
} from "../types";
|
||||
import {
|
||||
isBlackboardBlockData,
|
||||
isExerciseBlockData,
|
||||
isHomeworkBlockData,
|
||||
isImportBlockData,
|
||||
isKeyPointBlockData,
|
||||
isNewTeachingBlockData,
|
||||
isObjectiveBlockData,
|
||||
isReflectionBlockData,
|
||||
isRichTextBlockData,
|
||||
isSummaryBlockData,
|
||||
isTextStudyBlockData,
|
||||
} from "../lib/type-guards";
|
||||
import { RichTextBlock } from "../components/blocks/rich-text-block";
|
||||
import { ExerciseBlock } from "../components/blocks/exercise-block";
|
||||
import { TextStudyBlock } from "../components/blocks/text-study-block";
|
||||
@@ -75,92 +77,117 @@ export function isRichTextBlock(type: BlockType): boolean {
|
||||
* 根据 type 从注册表查找并渲染对应 Block,所有组件引用均为模块顶层静态声明,
|
||||
* 满足 react-hooks/static-components 规则。
|
||||
* 新增 Block 类型时,在此 switch 中添加对应 case 即可。
|
||||
*
|
||||
* V3 修复:使用类型守卫替代 `as` 断言,安全收窄 BlockData 联合类型。
|
||||
* 类型守卫失败时返回 null(理论上不会发生,因为 type 与 data 由调用方保证一致)。
|
||||
*/
|
||||
export function BlockRenderer(props: BlockRenderProps & { type: BlockType }): ReactElement | null {
|
||||
const { type, ...rest } = props;
|
||||
switch (type) {
|
||||
case "objective":
|
||||
case "objective": {
|
||||
if (!isObjectiveBlockData(rest.data)) return null;
|
||||
return (
|
||||
<ObjectiveBlock
|
||||
data={rest.data as ObjectiveBlockData}
|
||||
data={rest.data}
|
||||
onUpdate={(d) => rest.onUpdate(d)}
|
||||
/>
|
||||
);
|
||||
case "key_point":
|
||||
}
|
||||
case "key_point": {
|
||||
if (!isKeyPointBlockData(rest.data)) return null;
|
||||
return (
|
||||
<KeyPointBlock
|
||||
data={rest.data as KeyPointBlockData}
|
||||
data={rest.data}
|
||||
onUpdate={(d) => rest.onUpdate(d)}
|
||||
/>
|
||||
);
|
||||
case "import":
|
||||
}
|
||||
case "import": {
|
||||
if (!isImportBlockData(rest.data)) return null;
|
||||
return (
|
||||
<ImportBlock
|
||||
data={rest.data as ImportBlockData}
|
||||
data={rest.data}
|
||||
onUpdate={(d) => rest.onUpdate(d)}
|
||||
/>
|
||||
);
|
||||
case "new_teaching":
|
||||
}
|
||||
case "new_teaching": {
|
||||
if (!isNewTeachingBlockData(rest.data)) return null;
|
||||
return (
|
||||
<NewTeachingBlock
|
||||
data={rest.data as NewTeachingBlockData}
|
||||
data={rest.data}
|
||||
textbookId={rest.textbookId}
|
||||
chapterId={rest.chapterId}
|
||||
onUpdate={(d) => rest.onUpdate(d)}
|
||||
/>
|
||||
);
|
||||
case "summary":
|
||||
}
|
||||
case "summary": {
|
||||
if (!isSummaryBlockData(rest.data)) return null;
|
||||
return (
|
||||
<SummaryBlock
|
||||
data={rest.data as SummaryBlockData}
|
||||
data={rest.data}
|
||||
onUpdate={(d) => rest.onUpdate(d)}
|
||||
/>
|
||||
);
|
||||
case "homework":
|
||||
}
|
||||
case "homework": {
|
||||
if (!isHomeworkBlockData(rest.data)) return null;
|
||||
return (
|
||||
<HomeworkBlock
|
||||
data={rest.data as HomeworkBlockData}
|
||||
data={rest.data}
|
||||
onUpdate={(d) => rest.onUpdate(d)}
|
||||
/>
|
||||
);
|
||||
case "blackboard":
|
||||
}
|
||||
case "blackboard": {
|
||||
if (!isBlackboardBlockData(rest.data)) return null;
|
||||
return (
|
||||
<BlackboardBlock
|
||||
data={rest.data as BlackboardBlockData}
|
||||
data={rest.data}
|
||||
textbookId={rest.textbookId}
|
||||
chapterId={rest.chapterId}
|
||||
onUpdate={(d) => rest.onUpdate(d)}
|
||||
/>
|
||||
);
|
||||
case "reflection":
|
||||
}
|
||||
case "reflection": {
|
||||
if (!isReflectionBlockData(rest.data)) return null;
|
||||
return (
|
||||
<ReflectionBlock
|
||||
data={rest.data as ReflectionBlockData}
|
||||
data={rest.data}
|
||||
onUpdate={(d) => rest.onUpdate(d)}
|
||||
/>
|
||||
);
|
||||
case "exercise":
|
||||
}
|
||||
case "exercise": {
|
||||
if (!isExerciseBlockData(rest.data)) return null;
|
||||
return (
|
||||
<ExerciseBlock
|
||||
blockId={rest.blockId}
|
||||
data={rest.data as ExerciseBlockData}
|
||||
data={rest.data}
|
||||
classes={rest.classes ?? []}
|
||||
textbookId={rest.textbookId}
|
||||
chapterId={rest.chapterId}
|
||||
/>
|
||||
);
|
||||
case "text_study":
|
||||
return <TextStudyBlock blockId={rest.blockId} data={rest.data as TextStudyBlockData} />;
|
||||
}
|
||||
case "text_study": {
|
||||
if (!isTextStudyBlockData(rest.data)) return null;
|
||||
return <TextStudyBlock blockId={rest.blockId} data={rest.data} />;
|
||||
}
|
||||
case "rich_text":
|
||||
case "consolidation":
|
||||
case "consolidation": {
|
||||
if (!isRichTextBlockData(rest.data)) return null;
|
||||
return (
|
||||
<RichTextBlock
|
||||
data={rest.data as RichTextBlockData}
|
||||
data={rest.data}
|
||||
textbookId={rest.textbookId}
|
||||
chapterId={rest.chapterId}
|
||||
onUpdate={(d) => rest.onUpdate(d)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user