Uniforma l'altezza dei <select> agli altri campi dei form su Android.

Su Android il controllo nativo di <select> ignora parte del padding di
classiInput e risultava più alto o più basso degli input accanto.
appearance-none lo riporta a una scatola CSS normale; la freccia va
ridisegnata a mano perché appearance-none la fa sparire.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-06 10:57:05 +02:00
co-authored by Claude Sonnet 5
parent ee21278e82
commit 77f587b53b
3 changed files with 31 additions and 14 deletions
@@ -3,7 +3,7 @@ import { Link } from "@tanstack/react-router";
import { Check, Eye, Loader2, Upload } from "lucide-react"; import { Check, Eye, Loader2, Upload } from "lucide-react";
import { toast } from "sonner"; import { toast } from "sonner";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { Campo, classiInput, SezioneTendina } from "@/components/crapp/ui-bits"; import { Campo, classiInput, Select, SezioneTendina } from "@/components/crapp/ui-bits";
import { Reveal } from "@/components/motion/Reveal"; import { Reveal } from "@/components/motion/Reveal";
import { import {
caricaFile, caricaFile,
@@ -180,10 +180,9 @@ export function CampiProfilo({
<Intestazione titolo="Documento di identità" completa={sezioni.documento} /> <Intestazione titolo="Documento di identità" completa={sezioni.documento} />
<div className="grid grid-cols-2 gap-3"> <div className="grid grid-cols-2 gap-3">
<Campo label="Tipo"> <Campo label="Tipo">
<select <Select
value={corrente.documentoTipo ?? ""} value={corrente.documentoTipo ?? ""}
onChange={(e) => aggiorna({ documentoTipo: e.target.value })} onChange={(e) => aggiorna({ documentoTipo: e.target.value })}
className={classiInput}
> >
<option value=""></option> <option value=""></option>
{TIPI_DOCUMENTO.map((t) => ( {TIPI_DOCUMENTO.map((t) => (
@@ -191,7 +190,7 @@ export function CampiProfilo({
{t} {t}
</option> </option>
))} ))}
</select> </Select>
</Campo> </Campo>
<Campo label="Numero"> <Campo label="Numero">
<input <input
+16
View File
@@ -177,6 +177,22 @@ export function Campo({ label, children }: { label: string; children: ReactNode
); );
} }
/**
* `<select>` nativo con la stessa altezza degli altri campi del form. Il
* controllo nativo di un `<select>` ignora parzialmente il padding di
* `classiInput` (soprattutto su Android), risultando più alto o più basso
* degli input accanto: `appearance-none` lo riporta a una scatola CSS
* normale, la freccia va poi ridisegnata a mano perché sparisce con lui.
*/
export function Select(props: ComponentPropsWithoutRef<"select">) {
return (
<span className="relative block">
<select {...props} className={cn(classiInput, "appearance-none pr-8", props.className)} />
<ChevronDown className="pointer-events-none absolute right-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
</span>
);
}
export function StatoBadge({ stato, className }: { stato: Stato; className?: string }) { export function StatoBadge({ stato, className }: { stato: Stato; className?: string }) {
const meta = statoMeta[stato]; const meta = statoMeta[stato];
return ( return (
+12 -10
View File
@@ -16,7 +16,14 @@ import {
} from "lucide-react"; } from "lucide-react";
import { toast } from "sonner"; import { toast } from "sonner";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { Campo, classiInput, PageHeader, Section, StatTile } from "@/components/crapp/ui-bits"; import {
Campo,
classiInput,
PageHeader,
Section,
Select,
StatTile,
} from "@/components/crapp/ui-bits";
import { CampiProfilo } from "@/components/crapp/ProfiloAmministrativo"; import { CampiProfilo } from "@/components/crapp/ProfiloAmministrativo";
import { import {
nomeCompleto, nomeCompleto,
@@ -212,10 +219,9 @@ function ModificaGiocatore({ g, profilo }: { g: GiocatoreSquadra; profilo: Profi
/> />
</Campo> </Campo>
<Campo label="Ruolo"> <Campo label="Ruolo">
<select <Select
value={squadraCorrente.ruolo} value={squadraCorrente.ruolo}
onChange={(e) => setDatiSquadra({ ...squadraCorrente, ruolo: e.target.value })} onChange={(e) => setDatiSquadra({ ...squadraCorrente, ruolo: e.target.value })}
className={classiInput}
> >
<option value=""></option> <option value=""></option>
{RUOLI.map((r) => ( {RUOLI.map((r) => (
@@ -223,7 +229,7 @@ function ModificaGiocatore({ g, profilo }: { g: GiocatoreSquadra; profilo: Profi
{r} {r}
</option> </option>
))} ))}
</select> </Select>
</Campo> </Campo>
</div> </div>
<Campo label="Email (collegamento automatico al login, DD-018)"> <Campo label="Email (collegamento automatico al login, DD-018)">
@@ -536,18 +542,14 @@ function AggiungiGiocatore({ righe }: { righe: GiocatoreSquadra[] }) {
/> />
</Campo> </Campo>
<Campo label="Ruolo"> <Campo label="Ruolo">
<select <Select value={dati.ruolo} onChange={(e) => setDati({ ...dati, ruolo: e.target.value })}>
value={dati.ruolo}
onChange={(e) => setDati({ ...dati, ruolo: e.target.value })}
className={classiInput}
>
<option value=""></option> <option value=""></option>
{RUOLI.map((r) => ( {RUOLI.map((r) => (
<option key={r} value={r}> <option key={r} value={r}>
{r} {r}
</option> </option>
))} ))}
</select> </Select>
</Campo> </Campo>
</div> </div>
<Campo label="Email (collegamento automatico al login, opzionale)"> <Campo label="Email (collegamento automatico al login, opzionale)">