Add control for moving items up and down a list.

This commit is contained in:
araemer 2025-10-21 15:09:15 +02:00
parent 05595fba94
commit 3c9c94957f
4 changed files with 113 additions and 29 deletions

View file

@ -1,21 +1,53 @@
import Button from "../basics/Button.tsx";
import {ArrowDown, ArrowUp, X} from "lucide-react";
import {X} from "lucide-react";
import {ButtonType} from "../basics/BasicButtonDefinitions.ts";
import type {InstructionStepModel} from "../../models/InstructionStepModel.ts";
import {MoveButtonControl} from "../basics/MoveButtonControl.tsx";
type InstructionStepMobileListItemProps = {
/** Index of the instruction step */
index: number;
step: InstructionStepModel;
/** Model holding the instruction step data, e.g., instruction text */
stepModel: InstructionStepModel;
/**
* Method to call on moving the instruction step.
* @param index Current index of the step to move
* @param direction Direction to move the step in (either "up" or "down")
*/
onMove(index: number, direction: "up" | "down"): void;
onUpdate: (index: number, value: string) => void;
/**
* Method to call on updating the instruction step.
* @param index Index of the step to be updated.
* @param instructionText new instruction text
*/
onUpdate: (index: number, instructionText: string) => void;
/**
* Method to call on removing the instruction step.
* @param index Index of the instruction step to be removed.
*/
onRemove: (index: number) => void;
/** Indicates whether this is the first instruction step. Used to restrict movement. */
isFirst: boolean;
/** Indicates whether this is the last instruction step. Used to restrict movement. */
isLast: boolean;
};
/**
* Numbered list item for instructions step list editor for mobile devices.
*
* Describes a single instruction step of a recipe. As drag & drop usually is a bit clumsy on
* mobile devices, the steps can be reordered with small up and down buttons below the step number.
* @param index Index of this instruction step
* @param stepModel Model containing the data for the current instruction step, e.g., instruction text.
* @param onMove Method to call on moving the instruction step.
* @param onUpdate Method to call on updating the instruction step.
* @param onRemove Method to call on removing the instruction step.
* @param isFirst Indicates whether this is the first instruction step. In this case, the step cannot be moved up.
* @param isLast Indicates whether this is the last instruction step. In this case, the step cannot be moved down.
*/
export function InstructionStepMobileListItem({
index,
step,
stepModel,
onMove,
onUpdate,
onRemove,
@ -24,33 +56,27 @@ export function InstructionStepMobileListItem({
}: InstructionStepMobileListItemProps) {
return (
<div
key={step.id}
key={stepModel.id}
className="flex items-start gap-3 bg-gray-50 rounded-xl p-3 shadow-sm"
>
{/* Left column: Number of the instruction step and move controls */}
<div className="flex flex-col items-center pt-1">
<div className="circular-container">
{index + 1}
</div>
<div className="flex flex-col mt-2">
<Button
icon={ArrowUp}
onClick={() => onMove(index, "up")}
buttonType={ButtonType.TransparentButton}
disabled={isFirst} // disable if first item
/>
<Button
icon={ArrowDown}
onClick={() => onMove(index, "down")}
buttonType={ButtonType.TransparentButton}
disabled={isLast} // disable if last item
/>
</div>
<MoveButtonControl
isUpDisabled={isFirst}
isDownDisabled={isLast}
onMoveUp={() => onMove(index, "up")}
onMoveDown={() => onMove(index, "down")}
/>
</div>
{/* Center column: Instruction step */}
<textarea
className="input-field w-full min-h-[120px] resize-none overflow-hidden"
placeholder={`Schritt ${index + 1}`}
value={step.text}
value={stepModel.text}
onChange={(e) => onUpdate(index, e.target.value)}
onInput={(e) => {
const el = e.target as HTMLTextAreaElement;
@ -59,6 +85,7 @@ export function InstructionStepMobileListItem({
}}
/>
{/* Right column: Remove step button */}
<Button
onClick={() => onRemove(index)}
icon={X}