2023-07-30 19:40:39 +02:00
|
|
|
import { Signal } from "@preact/signals";
|
2025-01-18 00:46:05 +01:00
|
|
|
import type { Ingredient, IngredientGroup } from "@lib/recipeSchema.ts";
|
2023-07-30 19:40:39 +02:00
|
|
|
import { FunctionalComponent } from "preact";
|
2025-01-19 19:22:19 +01:00
|
|
|
import { unitsOfMeasure } from "@lib/parseIngredient.ts";
|
2023-07-30 19:40:39 +02:00
|
|
|
|
2025-01-19 19:22:19 +01:00
|
|
|
function formatAmount(num: number) {
|
|
|
|
if (num === 0) return "";
|
2023-08-04 23:36:35 +02:00
|
|
|
return (Math.floor(num * 4) / 4).toString();
|
|
|
|
}
|
2023-07-30 19:40:39 +02:00
|
|
|
|
2025-01-19 19:22:19 +01:00
|
|
|
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;
|
|
|
|
}
|
2025-01-19 20:15:49 +01:00
|
|
|
if (unitKey !== "cup") {
|
|
|
|
return unitsOfMeasure[unitKey].short;
|
|
|
|
}
|
|
|
|
|
|
|
|
return unitKey.toString();
|
2025-01-19 19:22:19 +01:00
|
|
|
} else {
|
|
|
|
return unit;
|
|
|
|
}
|
2025-01-18 00:46:05 +01:00
|
|
|
}
|
|
|
|
|
2023-07-30 19:40:39 +02:00
|
|
|
const Ingredient = (
|
|
|
|
{ ingredient, amount, key = "", portion = 1 }: {
|
|
|
|
ingredient: Ingredient;
|
|
|
|
amount: Signal<number>;
|
|
|
|
key?: string | number;
|
|
|
|
portion?: number;
|
|
|
|
},
|
|
|
|
) => {
|
2025-01-18 00:46:05 +01:00
|
|
|
const { name, quantity, unit } = ingredient;
|
|
|
|
|
2025-01-19 19:22:19 +01:00
|
|
|
const parsedQuantity = parseFloat(quantity);
|
2023-07-30 19:40:39 +02:00
|
|
|
|
2025-01-18 00:46:05 +01:00
|
|
|
const finalAmount = (typeof parsedQuantity === "number" && amount)
|
|
|
|
? (parsedQuantity / portion) * (amount?.value || 1)
|
2023-07-30 19:40:39 +02:00
|
|
|
: "";
|
|
|
|
|
|
|
|
return (
|
|
|
|
<tr key={key}>
|
|
|
|
<td class="pr-4 py-2">
|
2025-01-19 19:22:19 +01:00
|
|
|
{formatAmount(finalAmount || 0)}
|
|
|
|
<span class="ml-0.5 opacity-50">
|
|
|
|
{formatUnit(unit, finalAmount || 0)}
|
|
|
|
</span>
|
2023-07-30 19:40:39 +02:00
|
|
|
</td>
|
2025-01-18 00:46:05 +01:00
|
|
|
<td class="px-4 py-2">{name}</td>
|
2023-07-30 19:40:39 +02:00
|
|
|
</tr>
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
export const IngredientsList: FunctionalComponent<
|
2025-01-18 00:46:05 +01:00
|
|
|
{
|
|
|
|
ingredients: (Ingredient | IngredientGroup)[];
|
|
|
|
amount: Signal<number>;
|
|
|
|
portion?: number;
|
|
|
|
}
|
2023-07-30 19:40:39 +02:00
|
|
|
> = (
|
|
|
|
{ ingredients, amount, portion },
|
|
|
|
) => {
|
|
|
|
return (
|
|
|
|
<table class="w-full border-collapse table-auto">
|
|
|
|
<tbody>
|
|
|
|
{ingredients.map((item, index) => {
|
2025-01-18 00:46:05 +01:00
|
|
|
if ("items" in item) {
|
2023-07-30 19:40:39 +02:00
|
|
|
// Render IngredientGroup
|
2025-01-18 00:46:05 +01:00
|
|
|
const { name, items: groupIngredients } = item as IngredientGroup;
|
2023-07-30 19:40:39 +02:00
|
|
|
|
|
|
|
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>
|
|
|
|
);
|
|
|
|
};
|