95 lines
3.3 KiB
TypeScript
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>
|
|
);
|
|
}
|