77 lines
3.3 KiB
TypeScript
77 lines
3.3 KiB
TypeScript
/**
|
|
* Desktop editor using drag-and-drop via @dnd-kit
|
|
*/
|
|
|
|
import {closestCenter, DndContext, PointerSensor, useSensor, useSensors,} from "@dnd-kit/core";
|
|
import {arrayMove, SortableContext, verticalListSortingStrategy,} from "@dnd-kit/sortable";
|
|
import type {InstructionStepModel} from "../../models/InstructionStepModel";
|
|
import {InstructionStepDesktopListItem} from "./InstructionStepDesktopListItem";
|
|
import Button from "../basics/Button";
|
|
import {Plus} from "lucide-react";
|
|
import {ButtonType} from "../basics/BasicButtonDefinitions";
|
|
import {instructionStepListEditorMethods} from "./InstructionStepListEditorMethods.ts";
|
|
|
|
type InstructionStepListDesktopEditorProps = {
|
|
instructionStepList: InstructionStepModel[];
|
|
onChange: (steps: InstructionStepModel[]) => void;
|
|
};
|
|
|
|
export function InstructionStepListDesktopEditor({
|
|
instructionStepList,
|
|
onChange,
|
|
}: InstructionStepListDesktopEditorProps) {
|
|
const {handleUpdate, handleAdd, handleRemove} = instructionStepListEditorMethods(
|
|
instructionStepList,
|
|
onChange
|
|
);
|
|
|
|
const sensors = useSensors(useSensor(PointerSensor, {activationConstraint: {distance: 8}}));
|
|
|
|
const handleDragEnd = (event: any) => {
|
|
const {active, over} = event;
|
|
if (active.id !== over.id) {
|
|
/* find element by internal id
|
|
* Caution! Due to new elements not having a real ID yet, each list item has an internal ID
|
|
* in order to give it a unique identifier before saving it to the backend. We have to compare
|
|
* the id of the drag item to the internalId of the list item here!
|
|
*/
|
|
const oldIndex = instructionStepList.findIndex((i) => i.internalId === active.id);
|
|
const newIndex = instructionStepList.findIndex((i) => i.internalId === over.id);
|
|
// move element to new position
|
|
const newOrder = arrayMove(instructionStepList, oldIndex, newIndex);
|
|
onChange(newOrder);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div>
|
|
<h2 className="section-heading mb-2">Zubereitung</h2>
|
|
<DndContext sensors={sensors} collisionDetection={closestCenter} onDragEnd={handleDragEnd}>
|
|
<SortableContext
|
|
items={instructionStepList.map((i) => i.internalId)}
|
|
strategy={verticalListSortingStrategy}
|
|
>
|
|
<div className="flex flex-col gap-3">
|
|
{instructionStepList.map((step, index) => (
|
|
<InstructionStepDesktopListItem
|
|
key={step.internalId}
|
|
id={step.internalId}
|
|
index={index}
|
|
step={step}
|
|
onUpdate={handleUpdate}
|
|
onRemove={handleRemove}
|
|
/>
|
|
))}
|
|
</div>
|
|
</SortableContext>
|
|
</DndContext>
|
|
<Button
|
|
onClick={handleAdd}
|
|
icon={Plus}
|
|
text="Schritt hinzufügen"
|
|
buttonType={ButtonType.PrimaryButton}
|
|
className="mt-4"
|
|
/>
|
|
</div>
|
|
);
|
|
}
|