Potion/web/src/components/forms/IngredientItem.tsx
2025-12-28 22:20:39 -07:00

95 lines
3.3 KiB
TypeScript

import { Reorder, useDragControls } from "motion/react";
import { INGREDIENT_UNITS, type RecipeIngredient } from "../../types/recipe";
import DeleteIconSmall from "../icons/DeleteIconSmall";
import DragIconSmall from "../icons/DragIconSmall";
interface IngredientItemProps {
classes: string;
ingredient: RecipeIngredient;
onChange: (id: string, name: "Amount" | "Unit" | "Name", value: string) => void;
removeIngredientHandler: (id: string) => void;
allowDelete: boolean;
valid: boolean;
dirty: boolean;
markDirty: (id: string) => void;
}
export default function IngredientItem({ classes, ingredient, onChange, removeIngredientHandler, allowDelete, valid, dirty, markDirty }: IngredientItemProps) {
const controls = useDragControls();
const changeHandler = (name: "Amount" | "Unit" | "Name", value: string) => {
if (!dirty) markDirty(ingredient.Id);
onChange(ingredient.Id, name, value);
}
return (
<Reorder.Item
key={ingredient.Id}
value={ingredient}
dragListener={false}
dragControls={controls}
className="select-none p-2 flex gap-2 flex-col"
>
<div className="flex gap-2">
<div className="flex-col md:flex-row flex-grow flex gap-2 flex-wrap">
<div className="flex gap-2">
<input
type="number"
step="0.25"
min="0"
placeholder="amount"
required
value={ingredient.Amount}
onChange={(e) => changeHandler("Amount", e.target.value)}
className={`w-1/2 md:w-28 ${classes} ${dirty && ingredient.Amount <= 0 ? "border-red-500" : ""}`}
/>
<select
onChange={(e) => changeHandler("Unit", e.target.value)}
className={`w-1/2 md:w-fit ${classes} ${dirty && ingredient.Unit === "" ? "border-red-500" : ""}`}
>
{INGREDIENT_UNITS.map(unit => (
<option key={unit} value={unit}>{unit ? unit : "Select"}</option>
))}
</select>
</div>
<input
type="text"
placeholder="Ingredient name"
required
value={ingredient.Name}
onChange={(e) => changeHandler("Name", e.target.value)}
className={`flex-grow ${classes} ${dirty && ingredient.Name.trim() === "" ? "border-red-500" : ""}`}
/>
</div>
<div className="flex-col md:flex-row flex gap-x-2 items-center justify-evenly md:justify-center">
<button
tabIndex={-1}
disabled={!allowDelete}
onClick={() => removeIngredientHandler(ingredient.Id)}
className="p-1 md:p-2 md:pr-0 cursor-pointer text-gray-500 hover:text-red-500 disabled:text-gray-200 disabled:cursor-not-allowed duration-300"
>
<DeleteIconSmall />
</button>
<div
tabIndex={-1}
onPointerDown={(e) => {
e.preventDefault();
controls.start(e);
}}
className="p-1 md:p-0 cursor-pointer touch-none"
>
<DragIconSmall />
</div>
</div>
</div>
{(dirty && !valid) && (
<p className="text-sm text-red-500"> Please fill out all fields. </p>
)}
</Reorder.Item>
);
}