- Affiche le numéro de version (package.json, injecté via Vite) en bas de la sidebar, masqué en mode collapse et en mobile. - Factorise les checkbox/radio dupliqués (AllergySelect, DietTagSelect, IngredientPicker, UserPreferencesPage) en composants partagés CheckboxOption/RadioOption (components/ui/), et inverse le layout pour que la case soit à gauche du label. - Teinte la "zone de danger" de suppression de compte en rouge (fond + bordure), pas seulement le bouton. - Migre les icônes de navigation générale vers lucide-react (nav-icons.tsx devient un fichier de ré-export) ; les pictogrammes d'ingrédients métier restent en SVG custom (pas d'équivalents fins côté lucide). Vérifié : pnpm build, pnpm lint, pnpm --filter web e2e (43/43), et vérification visuelle manuelle (sidebar desktop/collapsed/mobile, light/dark).
41 lines
1.5 KiB
TypeScript
41 lines
1.5 KiB
TypeScript
import type { ReactNode } from "react";
|
|
|
|
/**
|
|
* The app-wide "selectable card" checkbox — see `global.scss`'s
|
|
* `label:has(> input[type="checkbox"])` rule for the actual look (hidden
|
|
* native input, a `.check-mark` that scales in, `is-selected` driving the
|
|
* tinted/bordered state). Factors out the JSX triplet (`label` → hidden
|
|
* `input` → `span.check-mark` → label text) that used to be duplicated
|
|
* across `AllergySelect`, `DietTagSelect`, `IngredientPicker`'s display
|
|
* menu, and `UserPreferencesPage`'s theme picker (see {@link RadioOption}
|
|
* for its `type="radio"` sibling) — one place to get the markup/a11y right
|
|
* instead of four.
|
|
*
|
|
* `is-selected` is applied in JS from the same `checked` boolean the caller
|
|
* already has, not derived via a CSS `:has(:checked)` chain — that turned
|
|
* out unreliable across browsers (see the callers this replaces for the
|
|
* original note).
|
|
*
|
|
* `className` is for the *container* layout only (grid item, flex-wrap
|
|
* chip, stacked list…) — the control's own look never varies, so there's
|
|
* no `variant` prop here.
|
|
*/
|
|
export function CheckboxOption({
|
|
checked,
|
|
onChange,
|
|
children,
|
|
className,
|
|
}: {
|
|
checked: boolean;
|
|
onChange: (checked: boolean) => void;
|
|
children: ReactNode;
|
|
className?: string;
|
|
}) {
|
|
return (
|
|
<label className={[className, checked && "is-selected"].filter(Boolean).join(" ")}>
|
|
<input type="checkbox" checked={checked} onChange={(e) => onChange(e.target.checked)} />
|
|
<span className="check-mark" aria-hidden="true" />
|
|
{children}
|
|
</label>
|
|
);
|
|
}
|