import type {
IngredientCategory,
IngredientIcon as IngredientIconType,
IngredientSubcategory,
} from "@batch-cooking/shared";
import type { ReactNode } from "react";
// Generic pictograms for ingredients — one per `IngredientIcon` value
// (schema.prisma), reused across the category chips, subcategory chips and
// ingredient cards in `IngredientPicker`/`IngredientRow`. Replaces the
// earlier per-ingredient emoji (437 different characters, one per
// ingredient) with a small vocabulary grouped by *what kind of thing* an
// ingredient is (a vegetable, a bottle of oil, a wedge of cheese…) rather
// than a literal picture of each one — 437 unique, professional icons
// isn't a realistic set to draw or license one-by-one, but ~20 generic
// shapes reused across them is.
//
// Most of these (everything using `FilledIcon` below) are solid-fill
// glyphs from foodiconpack.com's free "Common ingredient icons"/"Common
// Utensils" collections, licensed CC BY 4.0 — see the in-app credits page
// (`pages/CreditsPage.tsx`) for the required attribution. The three with
// no good match in that pack (`BreadIcon`, `DoughIcon`, `SproutIcon`) stay
// hand-drawn, using the stroke-based `Icon` wrapper that used to cover all
// 22 — both wrappers render at the same on-screen size (sized entirely via
// CSS, no width/height baked into either), so the mix reads as one set.
function Icon({ children }: { children: ReactNode }) {
return (
);
}
/** Wrapper for the foodiconpack.com icons — solid-fill glyphs (2048×2048 source viewBox) rather than `Icon`'s stroke-only ones. `fill="currentColor"` on the root cascades to every child `` (none set their own), so these theme/recolor exactly like the hand-drawn set. */
function FilledIcon({ children }: { children: ReactNode }) {
return (
);
}
/** Carrot (foodiconpack.com, CC BY 4.0 — see the credits page) — root vegetables, leafy greens, and produce generally (`freshProduce`/`vegetables`). */
export function VegetableIcon() {
return (
);
}
/** Apple (foodiconpack.com, CC BY 4.0) — `freshProduce`/`fruits`. */
export function FruitIcon() {
return (
);
}
/** Basil sprig (foodiconpack.com, CC BY 4.0) — `freshProduce`/`freshHerbs`. */
export function HerbIcon() {
return (
);
}
/** Cut of beef (foodiconpack.com, CC BY 4.0) — `meatAndSeafood`/`meats`. */
export function MeatIcon() {
return (
);
}
/** Chicken (foodiconpack.com, CC BY 4.0) — `meatAndSeafood`/`poultry`. */
export function PoultryIcon() {
return (
);
}
/** Salmon (foodiconpack.com, CC BY 4.0) — `meatAndSeafood`/`fish`. */
export function FishIcon() {
return (
);
}
/** Shrimp (foodiconpack.com, CC BY 4.0) — `meatAndSeafood`/`shellfish`. */
export function ShellfishIcon() {
return (
);
}
/** Bowl of rice (foodiconpack.com, CC BY 4.0) — grains, pasta, rice, flour (`dryGoods`/`starches`, and flours under `cookingEssentials`/`bases`). */
export function GrainIcon() {
return (
);
}
/** Chickpeas (foodiconpack.com, CC BY 4.0) — `dryGoods`/`legumes`. */
export function LegumeIcon() {
return (
);
}
/** Almonds (foodiconpack.com, CC BY 4.0) — nuts, seeds, dried fruit (`dryGoods`/`nutsAndSeeds`). */
export function NutSeedIcon() {
return (
);
}
/** A loaf, scored on top — `bakery`/`breads`. */
export function BreadIcon() {
return (
);
}
/** Rolling pin — raw, uncooked pastry (`bakery`/`rawDough`). */
export function DoughIcon() {
return (
);
}
/** Milk carton (foodiconpack.com, CC BY 4.0) — `dairyAndCheese`/`dairy` (non-cheese items). */
export function MilkIcon() {
return (
);
}
/** Wedge of cheddar (foodiconpack.com, CC BY 4.0) — `dairyAndCheese`/`dairy` (cheese items). */
export function CheeseIcon() {
return (
);
}
/** Eggs (foodiconpack.com, CC BY 4.0) — `dairyAndCheese`/`eggs`. */
export function EggIcon() {
return (
);
}
/** A seedling — plant-based dairy/meat alternatives (`dairyAndCheese`/`plantBasedAlternatives`). */
export function SproutIcon() {
return (
);
}
/** Cinnamon sticks (foodiconpack.com, CC BY 4.0) — dried spices/herbs (`condimentsAndSpices`/`spices`). */
export function SpiceIcon() {
return (
);
}
/** Honey jar (foodiconpack.com, CC BY 4.0) — sauces, pickles, tinned/preserved goods (`condimentsAndSpices`/`sauces` and the `dryGoods`/`other` catch-all). */
export function JarIcon() {
return (
);
}
/** Oil bottle (foodiconpack.com, CC BY 4.0) — oils, vinegars (`condimentsAndSpices`/`seasonings`, the pourable subset). */
export function BottleIcon() {
return (
);
}
/** Glass (foodiconpack.com, CC BY 4.0) — juices, coffee/tea, cooking alcohols, water (`condimentsAndSpices`/`seasonings`'s drinkable subset). */
export function DrinkIcon() {
return (
);
}
/** Stockpot (foodiconpack.com, CC BY 4.0) — broths, stocks, water bases (`cookingEssentials`/`bases`'s liquid-base subset). */
export function StockPotIcon() {
return (
);
}
/** Sugar (foodiconpack.com, CC BY 4.0) — `cookingEssentials`/`sugars`. */
export function SugarIcon() {
return (
);
}
/** Every icon component, keyed by {@link IngredientIconType} — the single lookup `IngredientPicker`/`IngredientRow`/category chips render through. */
export const INGREDIENT_ICON_COMPONENTS: Record JSX.Element> = {
VEGETABLE: VegetableIcon,
FRUIT: FruitIcon,
HERB: HerbIcon,
MEAT: MeatIcon,
POULTRY: PoultryIcon,
FISH: FishIcon,
SHELLFISH: ShellfishIcon,
GRAIN: GrainIcon,
LEGUME: LegumeIcon,
NUT_SEED: NutSeedIcon,
BREAD: BreadIcon,
DOUGH: DoughIcon,
MILK: MilkIcon,
CHEESE: CheeseIcon,
EGG: EggIcon,
SPROUT: SproutIcon,
SPICE: SpiceIcon,
JAR: JarIcon,
BOTTLE: BottleIcon,
DRINK: DrinkIcon,
STOCK_POT: StockPotIcon,
SUGAR: SugarIcon,
};
/** Renders the generic pictogram for one {@link IngredientIconType} — the actual component `IngredientPicker`'s cards and `IngredientRow` use, rather than importing {@link INGREDIENT_ICON_COMPONENTS} directly everywhere. */
export function IngredientTypeIcon({ icon }: { icon: IngredientIconType }) {
const Component = INGREDIENT_ICON_COMPONENTS[icon];
return ;
}
/**
* One representative {@link IngredientIconType} per {@link IngredientCategory}
* aisle, for `IngredientPicker`'s top-level category chips — a category
* mixes several icon types (e.g. "Aides culinaires" has flour, stock, and
* sugar items), so this is a deliberate, illustrative pick rather than a
* derived fact.
*/
export const CATEGORY_ICON: Record = {
freshProduce: "VEGETABLE",
meatAndSeafood: "MEAT",
dryGoods: "GRAIN",
bakery: "BREAD",
dairyAndCheese: "CHEESE",
condimentsAndSpices: "SPICE",
cookingEssentials: "STOCK_POT",
};
/** Renders {@link CATEGORY_ICON}'s pictogram for one category — used by the category chip row. */
export function CategoryIcon({ category }: { category: IngredientCategory }) {
return ;
}
/**
* One representative {@link IngredientIconType} per {@link IngredientSubcategory}
* rack, for `IngredientPicker`'s second-tier subcategory chips. Most map
* 1:1 onto their subcategory's dominant shape; a couple of heterogeneous
* subcategories (`seasonings` mixes oils with juices and coffee,
* `bases` mixes flour with stock and canned tomato) get one illustrative
* pick rather than a derived fact, same reasoning as {@link CATEGORY_ICON}.
*/
export const SUBCATEGORY_ICON: Record = {
vegetables: "VEGETABLE",
fruits: "FRUIT",
freshHerbs: "HERB",
meats: "MEAT",
poultry: "POULTRY",
fish: "FISH",
shellfish: "SHELLFISH",
starches: "GRAIN",
legumes: "LEGUME",
nutsAndSeeds: "NUT_SEED",
other: "JAR",
breads: "BREAD",
rawDough: "DOUGH",
dairy: "MILK",
eggs: "EGG",
plantBasedAlternatives: "SPROUT",
spices: "SPICE",
sauces: "JAR",
seasonings: "BOTTLE",
bases: "STOCK_POT",
thickeners: "JAR",
sugars: "SUGAR",
};
/** Renders {@link SUBCATEGORY_ICON}'s pictogram for one subcategory — used by the subcategory chip row. */
export function SubcategoryIcon({ subcategory }: { subcategory: IngredientSubcategory }) {
return ;
}