- 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)
88 lines
2.5 KiB
TypeScript
88 lines
2.5 KiB
TypeScript
"use client";
|
|
|
|
import { useTranslations } from "next-intl";
|
|
import { Plus, Trash2 } from "lucide-react";
|
|
import type { ObjectiveBlockData, ObjectiveItem } from "../../types";
|
|
import { Button } from "@/shared/components/ui/button";
|
|
|
|
interface Props {
|
|
data: ObjectiveBlockData;
|
|
onUpdate: (data: ObjectiveBlockData) => void;
|
|
}
|
|
|
|
const DIMENSIONS: ObjectiveItem["dimension"][] = ["knowledge", "process", "emotion"];
|
|
|
|
export function ObjectiveBlock({ data, onUpdate }: Props) {
|
|
const t = useTranslations("lessonPreparation");
|
|
|
|
function updateItem(index: number, patch: Partial<ObjectiveItem>) {
|
|
const next = data.objectives.map((it, i) =>
|
|
i === index ? { ...it, ...patch } : it,
|
|
);
|
|
onUpdate({ ...data, objectives: next });
|
|
}
|
|
|
|
function addItem() {
|
|
onUpdate({
|
|
...data,
|
|
objectives: [
|
|
...data.objectives,
|
|
{ dimension: "knowledge", text: "" },
|
|
],
|
|
});
|
|
}
|
|
|
|
function removeItem(index: number) {
|
|
onUpdate({
|
|
...data,
|
|
objectives: data.objectives.filter((_, i) => i !== index),
|
|
});
|
|
}
|
|
|
|
return (
|
|
<div className="space-y-2">
|
|
<div className="text-xs text-on-surface-variant">
|
|
{t("objective.hint")}
|
|
</div>
|
|
{data.objectives.map((item, idx) => (
|
|
<div key={idx} className="flex items-start gap-2">
|
|
<select
|
|
value={item.dimension}
|
|
onChange={(e) =>
|
|
updateItem(idx, {
|
|
dimension: e.target.value as ObjectiveItem["dimension"],
|
|
})
|
|
}
|
|
className="text-xs border border-outline-variant rounded px-1 py-1 bg-surface"
|
|
>
|
|
{DIMENSIONS.map((d) => (
|
|
<option key={d} value={d}>
|
|
{t(`objective.dimension.${d}`)}
|
|
</option>
|
|
))}
|
|
</select>
|
|
<textarea
|
|
value={item.text}
|
|
onChange={(e) => updateItem(idx, { text: e.target.value })}
|
|
className="flex-1 text-sm border border-outline-variant rounded px-2 py-1 resize-y min-h-[40px]"
|
|
placeholder={t("objective.textPlaceholder")}
|
|
/>
|
|
<Button
|
|
variant="ghost"
|
|
size="sm"
|
|
className="!p-1 text-error"
|
|
onClick={() => removeItem(idx)}
|
|
aria-label={t("action.delete")}
|
|
>
|
|
<Trash2 className="w-3 h-3" />
|
|
</Button>
|
|
</div>
|
|
))}
|
|
<Button variant="outline" size="sm" onClick={addItem}>
|
|
<Plus className="w-3 h-3 mr-1" />
|
|
{t("objective.addItem")}
|
|
</Button>
|
|
</div>
|
|
);
|
|
}
|