Evita che le tab Squadra allarghino la pagina: scroll solo sulla barra.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -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: l’etichetta è già nella tab.
|
* barra non viene mai mostrato: l’etichetta è 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"
|
||||||
|
|||||||
Reference in New Issue
Block a user