feat(lesson-preparation): add anchor canvas design, new blocks, and textbook content node
- Add anchor injector for canvas-based anchor positioning - Add new block components: blackboard, homework, import, key-point, new-teaching, objective, summary - Add textbook content node for React Flow canvas - Update actions (kp, publish, main), data-access (templates, versions, main) - Update editor, node-editor, block-renderer, and picker components - Update schema, types, hooks, and lib utilities (document-migration, node-summary, rf-mappers)
This commit is contained in:
@@ -1,13 +1,29 @@
|
||||
import type { ReactElement } from "react";
|
||||
import type {
|
||||
BlackboardBlockData,
|
||||
BlockData,
|
||||
BlockType,
|
||||
ExerciseBlockData,
|
||||
HomeworkBlockData,
|
||||
ImportBlockData,
|
||||
KeyPointBlockData,
|
||||
NewTeachingBlockData,
|
||||
ObjectiveBlockData,
|
||||
ReflectionBlockData,
|
||||
RichTextBlockData,
|
||||
SummaryBlockData,
|
||||
TextStudyBlockData,
|
||||
} from "../types";
|
||||
import { RichTextBlock } from "../components/blocks/rich-text-block";
|
||||
import { ExerciseBlock } from "../components/blocks/exercise-block";
|
||||
import { TextStudyBlock } from "../components/blocks/text-study-block";
|
||||
import { ObjectiveBlock } from "../components/blocks/objective-block";
|
||||
import { KeyPointBlock } from "../components/blocks/key-point-block";
|
||||
import { ImportBlock } from "../components/blocks/import-block";
|
||||
import { NewTeachingBlock } from "../components/blocks/new-teaching-block";
|
||||
import { SummaryBlock } from "../components/blocks/summary-block";
|
||||
import { HomeworkBlock } from "../components/blocks/homework-block";
|
||||
import { BlackboardBlock } from "../components/blocks/blackboard-block";
|
||||
import { ReflectionBlock } from "../components/blocks/reflection-block";
|
||||
|
||||
/**
|
||||
@@ -17,11 +33,11 @@ import { ReflectionBlock } from "../components/blocks/reflection-block";
|
||||
|
||||
export interface BlockRenderProps {
|
||||
blockId: string;
|
||||
data: RichTextBlockData | ExerciseBlockData | TextStudyBlockData;
|
||||
data: BlockData;
|
||||
textbookId?: string;
|
||||
chapterId?: string;
|
||||
classes?: { id: string; name: string }[];
|
||||
onUpdate: (data: RichTextBlockData | ExerciseBlockData | TextStudyBlockData) => void;
|
||||
onUpdate: (data: BlockData) => void;
|
||||
}
|
||||
|
||||
export interface BlockRegistryEntry {
|
||||
@@ -29,34 +45,24 @@ export interface BlockRegistryEntry {
|
||||
isRichText?: boolean;
|
||||
}
|
||||
|
||||
const RICH_TEXT_TYPES: BlockType[] = [
|
||||
"objective",
|
||||
"key_point",
|
||||
"import",
|
||||
"new_teaching",
|
||||
"consolidation",
|
||||
"summary",
|
||||
"homework",
|
||||
"blackboard",
|
||||
"rich_text",
|
||||
];
|
||||
const RICH_TEXT_TYPES: BlockType[] = ["rich_text", "consolidation"];
|
||||
|
||||
/**
|
||||
* Block 注册表元数据(用于查询 isRichText 等属性)。
|
||||
* 组件渲染由 BlockRenderer 统一处理,避免在 render 中动态获取组件引用。
|
||||
*/
|
||||
export const BLOCK_REGISTRY: Record<BlockType, BlockRegistryEntry> = {
|
||||
objective: { isRichText: true },
|
||||
key_point: { isRichText: true },
|
||||
import: { isRichText: true },
|
||||
new_teaching: { isRichText: true },
|
||||
objective: {},
|
||||
key_point: {},
|
||||
import: {},
|
||||
new_teaching: {},
|
||||
consolidation: { isRichText: true },
|
||||
summary: { isRichText: true },
|
||||
homework: { isRichText: true },
|
||||
blackboard: { isRichText: true },
|
||||
rich_text: { isRichText: true },
|
||||
exercise: {},
|
||||
summary: {},
|
||||
homework: {},
|
||||
blackboard: {},
|
||||
text_study: {},
|
||||
exercise: {},
|
||||
rich_text: { isRichText: true },
|
||||
reflection: {},
|
||||
};
|
||||
|
||||
@@ -73,6 +79,66 @@ export function isRichTextBlock(type: BlockType): boolean {
|
||||
export function BlockRenderer(props: BlockRenderProps & { type: BlockType }): ReactElement | null {
|
||||
const { type, ...rest } = props;
|
||||
switch (type) {
|
||||
case "objective":
|
||||
return (
|
||||
<ObjectiveBlock
|
||||
data={rest.data as ObjectiveBlockData}
|
||||
onUpdate={(d) => rest.onUpdate(d)}
|
||||
/>
|
||||
);
|
||||
case "key_point":
|
||||
return (
|
||||
<KeyPointBlock
|
||||
data={rest.data as KeyPointBlockData}
|
||||
onUpdate={(d) => rest.onUpdate(d)}
|
||||
/>
|
||||
);
|
||||
case "import":
|
||||
return (
|
||||
<ImportBlock
|
||||
data={rest.data as ImportBlockData}
|
||||
onUpdate={(d) => rest.onUpdate(d)}
|
||||
/>
|
||||
);
|
||||
case "new_teaching":
|
||||
return (
|
||||
<NewTeachingBlock
|
||||
data={rest.data as NewTeachingBlockData}
|
||||
textbookId={rest.textbookId}
|
||||
chapterId={rest.chapterId}
|
||||
onUpdate={(d) => rest.onUpdate(d)}
|
||||
/>
|
||||
);
|
||||
case "summary":
|
||||
return (
|
||||
<SummaryBlock
|
||||
data={rest.data as SummaryBlockData}
|
||||
onUpdate={(d) => rest.onUpdate(d)}
|
||||
/>
|
||||
);
|
||||
case "homework":
|
||||
return (
|
||||
<HomeworkBlock
|
||||
data={rest.data as HomeworkBlockData}
|
||||
onUpdate={(d) => rest.onUpdate(d)}
|
||||
/>
|
||||
);
|
||||
case "blackboard":
|
||||
return (
|
||||
<BlackboardBlock
|
||||
data={rest.data as BlackboardBlockData}
|
||||
textbookId={rest.textbookId}
|
||||
chapterId={rest.chapterId}
|
||||
onUpdate={(d) => rest.onUpdate(d)}
|
||||
/>
|
||||
);
|
||||
case "reflection":
|
||||
return (
|
||||
<ReflectionBlock
|
||||
data={rest.data as ReflectionBlockData}
|
||||
onUpdate={(d) => rest.onUpdate(d)}
|
||||
/>
|
||||
);
|
||||
case "exercise":
|
||||
return (
|
||||
<ExerciseBlock
|
||||
@@ -85,23 +151,14 @@ export function BlockRenderer(props: BlockRenderProps & { type: BlockType }): Re
|
||||
);
|
||||
case "text_study":
|
||||
return <TextStudyBlock blockId={rest.blockId} data={rest.data as TextStudyBlockData} />;
|
||||
case "reflection":
|
||||
return <ReflectionBlock data={rest.data as RichTextBlockData} onUpdate={rest.onUpdate} />;
|
||||
case "objective":
|
||||
case "key_point":
|
||||
case "import":
|
||||
case "new_teaching":
|
||||
case "consolidation":
|
||||
case "summary":
|
||||
case "homework":
|
||||
case "blackboard":
|
||||
case "rich_text":
|
||||
case "consolidation":
|
||||
return (
|
||||
<RichTextBlock
|
||||
data={rest.data as RichTextBlockData}
|
||||
textbookId={rest.textbookId}
|
||||
chapterId={rest.chapterId}
|
||||
onUpdate={rest.onUpdate}
|
||||
onUpdate={(d) => rest.onUpdate(d)}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
|
||||
Reference in New Issue
Block a user