fix(recipes): menu d'options d'affichage explicite dans le picker d'ingrédients
Remplace les deux boutons icône seule (régimes/allergènes) par un bouton réglages unique ouvrant un menu avec deux cases à cocher labellisées — les icônes seules n'étaient pas assez explicites sur ce qu'elles activaient/désactivaient. En chemin, corrige un bug réel découvert pendant l'implémentation : les cases à cocher rendaient invisibles (la règle globale "selectable card" de global.scss masque le <input type="checkbox"> natif et attend un <span class="check-mark"> + une classe is-selected sur le <label> pour dessiner l'état coché — mes cases n'avaient ni l'un ni l'autre). Corrigé en suivant exactement le même pattern que AllergySelect.tsx. Supprime AllergenIcon (nav-icons.tsx), devenu inutilisé. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
84003d2009
commit
8d1c763add
4 changed files with 71 additions and 41 deletions
|
|
@ -7,7 +7,7 @@ import {
|
|||
} from "@batch-cooking/shared";
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { AllergenIcon, DietPreferencesIcon } from "../../layouts/nav-icons";
|
||||
import { SettingsIcon } from "../../layouts/nav-icons";
|
||||
import { AllergenBadges } from "./AllergenBadges";
|
||||
import { DietBadges } from "./DietBadges";
|
||||
import { CategoryIcon, IngredientTypeIcon, SubcategoryIcon } from "./ingredient-icons";
|
||||
|
|
@ -32,10 +32,12 @@ const ALL = "ALL" as const;
|
|||
* 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.
|
||||
* The settings menu trailing the search input shows/hides 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. A labeled checkbox menu rather than two
|
||||
* bare icon-only toggle buttons — those turned out too ambiguous on their
|
||||
* own (unclear what each icon meant without a label attached).
|
||||
*/
|
||||
export function IngredientPicker({
|
||||
ingredients,
|
||||
|
|
@ -57,6 +59,7 @@ export function IngredientPicker({
|
|||
// badges per card too noisy while just browsing/searching by name.
|
||||
const [showAllergens, setShowAllergens] = useState(true);
|
||||
const [showDiets, setShowDiets] = useState(true);
|
||||
const [isDisplayMenuOpen, setIsDisplayMenuOpen] = useState(false);
|
||||
|
||||
function selectCategory(next: IngredientCategory | typeof ALL) {
|
||||
setCategory(next);
|
||||
|
|
@ -90,25 +93,38 @@ export function IngredientPicker({
|
|||
onChange={(e) => setQuery(e.target.value)}
|
||||
placeholder={t("recipes.form.searchIngredientPlaceholder")}
|
||||
/>
|
||||
<div className="ingredient-picker__search-toggles">
|
||||
<div className="ingredient-picker__display-options">
|
||||
<button
|
||||
type="button"
|
||||
className={`ingredient-picker__search-toggle${showDiets ? " active" : ""}`}
|
||||
onClick={() => setShowDiets((v) => !v)}
|
||||
aria-pressed={showDiets}
|
||||
title={t("recipes.form.toggleDiets")}
|
||||
className={`ingredient-picker__display-toggle${isDisplayMenuOpen ? " active" : ""}`}
|
||||
onClick={() => setIsDisplayMenuOpen((v) => !v)}
|
||||
aria-expanded={isDisplayMenuOpen}
|
||||
title={t("recipes.form.displayOptions")}
|
||||
>
|
||||
<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 />
|
||||
<SettingsIcon />
|
||||
</button>
|
||||
{isDisplayMenuOpen && (
|
||||
<div className="ingredient-picker__display-menu">
|
||||
<label className={showDiets ? "is-selected" : ""}>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={showDiets}
|
||||
onChange={(e) => setShowDiets(e.target.checked)}
|
||||
/>
|
||||
<span className="check-mark" aria-hidden="true" />
|
||||
{t("recipes.form.showDietsLabel")}
|
||||
</label>
|
||||
<label className={showAllergens ? "is-selected" : ""}>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={showAllergens}
|
||||
onChange={(e) => setShowAllergens(e.target.checked)}
|
||||
/>
|
||||
<span className="check-mark" aria-hidden="true" />
|
||||
{t("recipes.form.showAllergensLabel")}
|
||||
</label>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -726,20 +726,19 @@
|
|||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
// Settings menu for the allergen/diet badge rows on every card below —
|
||||
// trailing the search input. A single gear button + labeled checkboxes
|
||||
// (not two bare icon-only toggles, the earlier design here) — an icon
|
||||
// alone couldn't say clearly enough which badge row it hid.
|
||||
&__display-options {
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
gap: var(--space-xs);
|
||||
}
|
||||
|
||||
&__search-toggle {
|
||||
&__display-toggle {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
width: 2.25rem;
|
||||
height: 2.25rem;
|
||||
padding: 0;
|
||||
|
|
@ -765,6 +764,31 @@
|
|||
}
|
||||
}
|
||||
|
||||
// Anchored below-right of the toggle — same transient-overlay reasoning
|
||||
// as `.app-sidebar__account-menu` (AppLayout.scss).
|
||||
&__display-menu {
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
top: calc(100% + var(--space-xs));
|
||||
right: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-sm);
|
||||
padding: var(--space-sm);
|
||||
background: var(--color-surface);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-base);
|
||||
box-shadow: var(--shadow-md);
|
||||
|
||||
// Box/checkmark look comes from the app-wide "selectable card" rule in
|
||||
// global.scss (`label:has(> input[type="checkbox"])`) — this only adds
|
||||
// what's specific to sitting inside a dropdown menu.
|
||||
label {
|
||||
font-size: var(--font-size-sm);
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
&__categories {
|
||||
display: flex;
|
||||
gap: var(--space-xs);
|
||||
|
|
|
|||
|
|
@ -128,14 +128,3 @@ 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,8 +166,9 @@
|
|||
},
|
||||
"dietsLabel": "Régime(s) associé(s)",
|
||||
"searchIngredientPlaceholder": "Rechercher un ingrédient…",
|
||||
"toggleDiets": "Afficher/masquer les régimes alimentaires",
|
||||
"toggleAllergens": "Afficher/masquer les allergènes",
|
||||
"displayOptions": "Options d'affichage",
|
||||
"showDietsLabel": "Régimes alimentaires",
|
||||
"showAllergensLabel": "Allergènes",
|
||||
"allCategories": "Tout",
|
||||
"allSubcategories": "Tout",
|
||||
"noIngredientFound": "Aucun ingrédient trouvé.",
|
||||
|
|
|
|||
Loading…
Reference in a new issue