feat(recipes): toggles afficher/masquer allergènes et régimes dans le picker
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 <noreply@anthropic.com>
This commit is contained in:
parent
5890c62462
commit
11b03b0e56
4 changed files with 96 additions and 3 deletions
|
|
@ -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")}
|
||||
/>
|
||||
<div className="ingredient-picker__search-toggles">
|
||||
<button
|
||||
type="button"
|
||||
className={`ingredient-picker__search-toggle${showDiets ? " active" : ""}`}
|
||||
onClick={() => setShowDiets((v) => !v)}
|
||||
aria-pressed={showDiets}
|
||||
title={t("recipes.form.toggleDiets")}
|
||||
>
|
||||
<DietPreferencesIcon />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={`ingredient-picker__search-toggle${showAllergens ? " active" : ""}`}
|
||||
onClick={() => setShowAllergens((v) => !v)}
|
||||
aria-pressed={showAllergens}
|
||||
title={t("recipes.form.toggleAllergens")}
|
||||
>
|
||||
<AllergenIcon />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="ingredient-picker__categories">
|
||||
|
|
@ -100,8 +133,8 @@ export function IngredientPicker({
|
|||
{ingredient.icon}
|
||||
</span>
|
||||
<span className="ingredient-picker__card-name">{ingredient.name}</span>
|
||||
<AllergenBadges allergens={ingredient.allergens} />
|
||||
<DietBadges diets={ingredient.diets} />
|
||||
{showAllergens && <AllergenBadges allergens={ingredient.allergens} />}
|
||||
{showDiets && <DietBadges diets={ingredient.diets} />}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -128,3 +128,14 @@ export function PublicIcon() {
|
|||
</Icon>
|
||||
);
|
||||
}
|
||||
|
||||
/** Allergens — the show/hide allergen-badges toggle on `IngredientPicker`'s search bar. */
|
||||
export function AllergenIcon() {
|
||||
return (
|
||||
<Icon>
|
||||
<path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z" />
|
||||
<path d="M12 9v4" />
|
||||
<path d="M12 17h.01" />
|
||||
</Icon>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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": {
|
||||
|
|
|
|||
Loading…
Reference in a new issue