init
This commit is contained in:
commit
c19b910c36
80 changed files with 16134 additions and 0 deletions
58
app/lib/display.ts
Normal file
58
app/lib/display.ts
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
import type { Recipe, RecipeIngredient } from "@/app/types";
|
||||
import { CATALOG_BY_ID } from "./catalog";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Human-friendly ingredient lines, scaled to a serving count.
|
||||
//
|
||||
// ing("white_rice", 1.5, "cup") for 4 → shown for 2 as "¾ cup Long Grain White Rice"
|
||||
// ing("black_beans", 1, "each") → "1 × Black Beans, canned (15 oz can)"
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const FRACTIONS: [number, string][] = [
|
||||
[0, ""],
|
||||
[1 / 8, "⅛"],
|
||||
[1 / 4, "¼"],
|
||||
[1 / 3, "⅓"],
|
||||
[1 / 2, "½"],
|
||||
[2 / 3, "⅔"],
|
||||
[3 / 4, "¾"],
|
||||
[1, ""],
|
||||
];
|
||||
|
||||
/** 0.75 → "¾", 1.5 → "1½", 2.33 → "2⅓", 13.2 → "13". */
|
||||
export function formatQuantity(value: number): string {
|
||||
if (value >= 10) return String(Math.round(value));
|
||||
const whole = Math.floor(value);
|
||||
const fraction = value - whole;
|
||||
const [nearest, glyph] = FRACTIONS.reduce((best, f) => (Math.abs(f[0] - fraction) < Math.abs(best[0] - fraction) ? f : best));
|
||||
const total = whole + (nearest === 1 ? 1 : 0);
|
||||
if (total === 0 && glyph === "") return "⅛"; // never show "0" for a real ingredient
|
||||
return `${total > 0 ? total : ""}${glyph}` || "0";
|
||||
}
|
||||
|
||||
export interface IngredientLine {
|
||||
quantity: string;
|
||||
/** e.g. "cup", "lb", or "×" for counted items. */
|
||||
unit: string;
|
||||
name: string;
|
||||
/** Package hint for counted items, e.g. "15 oz can". */
|
||||
detail?: string;
|
||||
}
|
||||
|
||||
export function ingredientLine(ingredient: RecipeIngredient, scale: number): IngredientLine {
|
||||
const item = CATALOG_BY_ID.get(ingredient.itemId);
|
||||
const name = item?.name ?? ingredient.itemId;
|
||||
const quantity = formatQuantity(ingredient.amount * scale);
|
||||
|
||||
if (ingredient.unit === "each") {
|
||||
// Single-unit packages (a can, a jar) read better with the package size.
|
||||
const detail = item && item.packageSize === 1 && item.packageLabel !== "each" ? item.packageLabel : undefined;
|
||||
return { quantity, unit: "×", name, detail };
|
||||
}
|
||||
return { quantity, unit: ingredient.unit, name };
|
||||
}
|
||||
|
||||
export function ingredientLines(recipe: Recipe, servings: number): IngredientLine[] {
|
||||
const scale = servings / recipe.servings;
|
||||
return recipe.ingredients.map((i) => ingredientLine(i, scale));
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue