memorium/islands/IngredientsList.tsx
2025-01-19 20:15:49 +01:00

101 lines
2.6 KiB
TypeScript

import { Signal } from "@preact/signals";
import type { Ingredient, IngredientGroup } from "@lib/recipeSchema.ts";
import { FunctionalComponent } from "preact";
import { unitsOfMeasure } from "@lib/parseIngredient.ts";
function formatAmount(num: number) {
if (num === 0) return "";
return (Math.floor(num * 4) / 4).toString();
}
function formatUnit(unit: string, amount: number) {
const unitKey = unit.toLowerCase() as keyof typeof unitsOfMeasure;
if (unitKey in unitsOfMeasure) {
if (amount > 1 && unitsOfMeasure[unitKey].plural !== undefined) {
return unitsOfMeasure[unitKey].plural;
}
if (unitKey !== "cup") {
return unitsOfMeasure[unitKey].short;
}
return unitKey.toString();
} else {
return unit;
}
}
const Ingredient = (
{ ingredient, amount, key = "", portion = 1 }: {
ingredient: Ingredient;
amount: Signal<number>;
key?: string | number;
portion?: number;
},
) => {
const { name, quantity, unit } = ingredient;
const parsedQuantity = parseFloat(quantity);
const finalAmount = (typeof parsedQuantity === "number" && amount)
? (parsedQuantity / portion) * (amount?.value || 1)
: "";
return (
<tr key={key}>
<td class="pr-4 py-2">
{formatAmount(finalAmount || 0)}
<span class="ml-0.5 opacity-50">
{formatUnit(unit, finalAmount || 0)}
</span>
</td>
<td class="px-4 py-2">{name}</td>
</tr>
);
};
export const IngredientsList: FunctionalComponent<
{
ingredients: (Ingredient | IngredientGroup)[];
amount: Signal<number>;
portion?: number;
}
> = (
{ ingredients, amount, portion },
) => {
return (
<table class="w-full border-collapse table-auto">
<tbody>
{ingredients.map((item, index) => {
if ("items" in item) {
// Render IngredientGroup
const { name, items: groupIngredients } = item as IngredientGroup;
return (
<>
<tr key={index}>
<td colSpan={3} class="pr-4 py-2 font-italic">{name}</td>
</tr>
{groupIngredients.map((item, index) => {
// Render Ingredient
return (
<Ingredient
key={index}
ingredient={item}
amount={amount}
portion={portion}
/>
);
})}
</>
);
} else {
return (
<Ingredient ingredient={item} amount={amount} portion={portion} />
);
}
})}
</tbody>
</table>
);
};