Evita che le tab Squadra allarghino la pagina: scroll solo sulla barra.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Ivan Cacciari
2026-09-07 17:49:40 +02:00
co-authored by Cursor
parent b4878b7795
commit f5aaa1cccb
+57 -54
View File
@@ -15,14 +15,13 @@ export type VoceSottosezione = {
const transizioneTab = { type: "tween" as const, duration: 0.16, ease: [0.25, 0.1, 0.25, 1] }; const transizioneTab = { type: "tween" as const, duration: 0.16, ease: [0.25, 0.1, 0.25, 1] };
/** /**
* Barra di sottosezioni in un'unica fila scorrevole (swipe orizzontale) e * Barra di sottosezioni in un'unica fila e pannello che mostra una sola sezione
* pannello che mostra una sola sezione alla volta, cambiabile anche con * alla volta, cambiabile anche con swipe sul contenuto.
* swipe sul contenuto.
* *
* `variante="sottolineatura"`: tab giustificate su tutta la larghezza, senza scroll, con * `variante="sottolineatura"`: pillola piena sulla tab attiva (Squadra e Classifica CSI);
* pillola piena sulla tab attiva (Squadra e Classifica CSI). Default `pillole`: tab scorrevoli * su mobile la sola barra tab scorre in orizzontale senza allargare la pagina.
* a larghezza naturale, usate dal Profilo. Il titolo ripetuto sotto la barra non viene mai * Default `pillole`: tab a larghezza naturale (Profilo). Il titolo ripetuto sotto la
* mostrato: letichetta è già nella tab. * barra non viene mai mostrato: letichetta è già nella tab.
*/ */
export function BarraSottosezioni({ export function BarraSottosezioni({
voci, voci,
@@ -45,9 +44,8 @@ export function BarraSottosezioni({
const sottolineatura = variante === "sottolineatura"; const sottolineatura = variante === "sottolineatura";
useEffect(() => { useEffect(() => {
// `auto`: lo smooth competerebbe col tween del pannello sul main thread.
tabRefs.current[attiva]?.scrollIntoView({ tabRefs.current[attiva]?.scrollIntoView({
behavior: "auto", behavior: "smooth",
inline: "center", inline: "center",
block: "nearest", block: "nearest",
}); });
@@ -64,64 +62,69 @@ export function BarraSottosezioni({
if (!voce) return null; if (!voce) return null;
return ( return (
<div> <div className="min-w-0 max-w-full">
<div <div
className={cn( className={cn(
"border-b border-border bg-background", "min-w-0 max-w-full border-b border-border bg-background",
!sottolineatura && "bg-background/80 pt-3 backdrop-blur-md", !sottolineatura && "bg-background/80 pt-3 backdrop-blur-md",
)} )}
> >
{/* Solo la barra tab può scrollare in orizzontale: non allarga il layout pagina. */}
<div <div
role="tablist"
aria-label="Sottosezioni"
className={cn( className={cn(
"flex", "w-full max-w-full min-w-0 overflow-x-auto overflow-y-hidden [-webkit-overflow-scrolling:touch] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden",
sottolineatura !sottolineatura && "pb-3",
? "gap-1 px-2 py-1.5"
: "snap-x snap-mandatory overflow-x-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden -mx-0 gap-1.5 px-5 pb-3",
)} )}
> >
{voci.map((v) => { <div
const selezionata = v.id === attiva; role="tablist"
return ( aria-label="Sottosezioni"
<button className={cn(
key={v.id} "flex w-max min-w-full flex-nowrap",
ref={(el) => { sottolineatura ? "gap-1 px-2 py-1.5" : "snap-x snap-mandatory gap-1.5 px-5",
tabRefs.current[v.id] = el; )}
}} >
type="button" {voci.map((v) => {
role="tab" const selezionata = v.id === attiva;
aria-selected={selezionata} return (
onClick={() => { <button
const i = voci.findIndex((x) => x.id === v.id); key={v.id}
vaiA(i); ref={(el) => {
}} tabRefs.current[v.id] = el;
className={cn( }}
"grow basis-0 touch-manipulation text-sm font-bold uppercase tracking-wide transition-colors", type="button"
"min-h-11", role="tab"
sottolineatura aria-selected={selezionata}
? cn( onClick={() => {
"rounded-xl px-1 py-2.5 text-center", const i = voci.findIndex((x) => x.id === v.id);
selezionata vaiA(i);
? "bg-accent text-accent-foreground shadow-pop" }}
: "text-muted-foreground", className={cn(
) "min-h-11 shrink-0 touch-manipulation whitespace-nowrap text-sm font-bold uppercase tracking-wide transition-colors",
: cn( sottolineatura
"snap-center shrink-0 whitespace-nowrap rounded-full px-3.5 py-2", ? cn(
selezionata "rounded-xl px-1 py-2.5 text-center",
? "bg-accent text-accent-foreground shadow-pop" selezionata
: "bg-secondary text-muted-foreground", ? "bg-accent text-accent-foreground shadow-pop"
), : "text-muted-foreground",
)
: cn(
"snap-center rounded-full px-3.5 py-2",
selezionata
? "bg-accent text-accent-foreground shadow-pop"
: "bg-secondary text-muted-foreground",
),
)} )}
> >
{v.label} {v.label}
</button> </button>
); );
})} })}
</div>
</div> </div>
</div> </div>
<div className="relative overflow-hidden"> <div className="relative min-w-0 overflow-hidden">
<motion.div <motion.div
key={voce.id} key={voce.id}
role="tabpanel" role="tabpanel"