From 11b03b0e5644310a67da03e344e5d04c4d581a20 Mon Sep 17 00:00:00 2001 From: Nicolas Date: Tue, 18 Aug 2026 11:17:55 +0200 Subject: [PATCH] =?UTF-8?q?feat(recipes):=20toggles=20afficher/masquer=20a?= =?UTF-8?q?llerg=C3=A8nes=20et=20r=C3=A9gimes=20dans=20le=20picker?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Deux boutons icône en bout de la barre de recherche de l'IngredientPicker (régimes/feuille, allergènes/triangle), même pattern bouton-icône + état actif que FavoriteStarButton. Préférence d'affichage locale au picker (non persistée) — n'affecte pas quels ingrédients apparaissent, seulement si leurs badges allergènes/régimes sont visibles sur les cartes. Nouvelle icône AllergenIcon dans nav-icons.tsx, réutilise DietPreferencesIcon existante. Co-Authored-By: Claude Sonnet 5 --- .../src/features/recipes/IngredientPicker.tsx | 37 +++++++++++++- apps/web/src/features/recipes/recipes.scss | 49 ++++++++++++++++++- apps/web/src/layouts/nav-icons.tsx | 11 +++++ apps/web/src/locales/fr/translation.json | 2 + 4 files changed, 96 insertions(+), 3 deletions(-) diff --git a/apps/web/src/features/recipes/IngredientPicker.tsx b/apps/web/src/features/recipes/IngredientPicker.tsx index bb47d3f..6b24c22 100644 --- a/apps/web/src/features/recipes/IngredientPicker.tsx +++ b/apps/web/src/features/recipes/IngredientPicker.tsx @@ -5,6 +5,7 @@ import { } from "@batch-cooking/shared"; import { useState } from "react"; import { useTranslation } from "react-i18next"; +import { AllergenIcon, DietPreferencesIcon } from "../../layouts/nav-icons"; import { AllergenBadges } from "./AllergenBadges"; import { DietBadges } from "./DietBadges"; import "./recipes.scss"; @@ -23,6 +24,11 @@ const ALL_CATEGORIES = "ALL" as const; * rationale as `AllergySelect`/`DietSelect`. `excludeIds` (already-selected * ingredients — a recipe's ingredient list, or a profile's disliked list) * keeps the same one from being added twice. + * + * The two toggle buttons trailing the search input show/hide the + * allergen/diet badge rows on every card — a display preference local to + * this picker (not persisted), for whoever finds two rows of badges per + * card too noisy while just browsing/searching by name. */ export function IngredientPicker({ ingredients, @@ -38,6 +44,13 @@ export function IngredientPicker({ ALL_CATEGORIES, ); const [query, setQuery] = useState(""); + // Purely a display preference for this picker's own card grid — doesn't + // touch which ingredients `visible` includes, only whether their + // allergen/diet badges render. Defaults to shown (the previous, only + // behavior); collapsing them is an opt-in for whoever finds two rows of + // badges per card too noisy while just browsing/searching by name. + const [showAllergens, setShowAllergens] = useState(true); + const [showDiets, setShowDiets] = useState(true); const normalizedQuery = query.trim().toLowerCase(); const visible = ingredients.filter((ingredient) => { @@ -63,6 +76,26 @@ export function IngredientPicker({ onChange={(e) => setQuery(e.target.value)} placeholder={t("recipes.form.searchIngredientPlaceholder")} /> +
+ + +
@@ -100,8 +133,8 @@ export function IngredientPicker({ {ingredient.icon} {ingredient.name} - - + {showAllergens && } + {showDiets && } ))}
diff --git a/apps/web/src/features/recipes/recipes.scss b/apps/web/src/features/recipes/recipes.scss index 99caf71..e073e73 100644 --- a/apps/web/src/features/recipes/recipes.scss +++ b/apps/web/src/features/recipes/recipes.scss @@ -684,8 +684,16 @@ border-radius: var(--radius-base); &__search { + display: flex; + align-items: center; + gap: var(--space-xs); + input { - width: 100%; + flex: 1 1 auto; + // Own min-width: 0 (not just relying on the flex row's) — same + // shrink-to-fit gotcha as `.disliked-ingredients-field`, belt and + // suspenders since a text input's natural min width is its content. + min-width: 0; padding: var(--space-sm); font-family: var(--font-body); font-size: var(--font-size-base); @@ -696,6 +704,45 @@ } } + // Show/hide toggles for the allergen/diet badge rows on every card below + // — trailing the search input, same "icon button with an active state" + // pattern as `FavoriteStarButton`. + &__search-toggles { + display: flex; + flex-shrink: 0; + gap: var(--space-xs); + } + + &__search-toggle { + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + width: 2.25rem; + height: 2.25rem; + padding: 0; + cursor: pointer; + border: 1px solid var(--color-border); + border-radius: var(--radius-base); + background: var(--color-surface-alt); + color: var(--color-text-muted); + + svg { + width: 1.1rem; + height: 1.1rem; + } + + &:hover { + border-color: var(--color-primary); + color: var(--color-text); + } + + &.active { + border-color: var(--color-primary); + color: var(--color-primary); + } + } + &__categories { display: flex; gap: var(--space-xs); diff --git a/apps/web/src/layouts/nav-icons.tsx b/apps/web/src/layouts/nav-icons.tsx index 90adceb..25de06d 100644 --- a/apps/web/src/layouts/nav-icons.tsx +++ b/apps/web/src/layouts/nav-icons.tsx @@ -128,3 +128,14 @@ export function PublicIcon() { ); } + +/** Allergens — the show/hide allergen-badges toggle on `IngredientPicker`'s search bar. */ +export function AllergenIcon() { + return ( + + + + + + ); +} diff --git a/apps/web/src/locales/fr/translation.json b/apps/web/src/locales/fr/translation.json index 4daf56d..ff3637a 100644 --- a/apps/web/src/locales/fr/translation.json +++ b/apps/web/src/locales/fr/translation.json @@ -166,6 +166,8 @@ }, "dietsLabel": "Régime(s) associé(s)", "searchIngredientPlaceholder": "Rechercher un ingrédient…", + "toggleDiets": "Afficher/masquer les régimes alimentaires", + "toggleAllergens": "Afficher/masquer les allergènes", "allCategories": "Tout", "noIngredientFound": "Aucun ingrédient trouvé.", "category": {