From 2a1a0839c05dfefd2761761f3159810602c58025 Mon Sep 17 00:00:00 2001 From: Davide Grilli Date: Sat, 5 Sep 2026 23:41:20 +0200 Subject: [PATCH] Estende alla card degli eventi il fix delle date su iOS. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Il `min-w-0` di 366928b era finito solo in `ProfiloAmministrativo` perché `Campo` e le classi degli input erano ricopiati identici in tre file: il form «Nuovo evento» e la dashboard admin («Data tessera» in `grid-cols-2`) sfondavano ancora la card. Ora `Campo` e `classiInput` stanno una volta sola in `ui-bits` e le tre copie spariscono. La regola CSS che rende ridimensionabili i controlli nativi copre anche `input[type="time"]`, che nel form eventi sta affiancato alla data. Co-Authored-By: Claude Opus 5 --- docs/CHANGELOG.md | 9 ++++++++ .../crapp/ProfiloAmministrativo.tsx | 22 +----------------- src/components/crapp/ui-bits.tsx | 21 +++++++++++++++++ src/routes/admin.tsx | 15 +----------- src/routes/eventi.tsx | 23 +++++-------------- src/styles.css | 17 +++++++------- 6 files changed, 47 insertions(+), 60 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 6c02d2c..736b91f 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -6,6 +6,15 @@ qui: sta in [ROADMAP.md](ROADMAP.md). ## Versione attuale — agosto 2026 +### Form eventi: data e ora non sfondano più la card su iOS + +- Lo stesso difetto già corretto sul tesseramento: `Campo` e le classi degli input erano + ricopiati identici in tre file, quindi il `min-w-0` aggiunto in `ProfiloAmministrativo` + non arrivava né al form «Nuovo evento» né alla dashboard admin. Ora `Campo` e + `classiInput` stanno una volta sola in `ui-bits`. +- La regola CSS che rende ridimensionabili i controlli nativi copre anche + `input[type="time"]`, che nel form eventi sta affiancato alla data in `grid-cols-2`. + ### Profilo: tab Documenti e Opzioni, barra senza scroll - L'etichetta nominava lo scopo (il tesseramento CSI) invece del contenuto: dentro ci sono diff --git a/src/components/crapp/ProfiloAmministrativo.tsx b/src/components/crapp/ProfiloAmministrativo.tsx index 3f1f0a0..3e7dc99 100644 --- a/src/components/crapp/ProfiloAmministrativo.tsx +++ b/src/components/crapp/ProfiloAmministrativo.tsx @@ -3,7 +3,7 @@ import { Link } from "@tanstack/react-router"; import { Check, Eye, Loader2, Upload } from "lucide-react"; import { toast } from "sonner"; import { cn } from "@/lib/utils"; -import { SezioneTendina } from "@/components/crapp/ui-bits"; +import { Campo, classiInput, SezioneTendina } from "@/components/crapp/ui-bits"; import { Reveal } from "@/components/motion/Reveal"; import { caricaFile, @@ -22,26 +22,6 @@ import { const TIPI_DOCUMENTO = ["Carta d'identità", "Patente", "Passaporto"]; -const classiInput = - "w-full min-w-0 rounded-xl border border-border bg-background px-3 py-2 text-sm"; - -/** - * `min-w-0`: dentro `grid-cols-2` questa label è l'elemento di griglia e ha - * `min-width: auto`, quindi si allarga fino al contenuto invece di stare nella - * colonna. Con un controllo nativo largo dentro (una data su iOS) la coppia - * sfonda la card. - */ -function Campo({ label, children }: { label: string; children: React.ReactNode }) { - return ( - - ); -} - function Intestazione({ titolo, completa }: { titolo: string; completa: boolean }) { return (
diff --git a/src/components/crapp/ui-bits.tsx b/src/components/crapp/ui-bits.tsx index fe80e8e..73add18 100644 --- a/src/components/crapp/ui-bits.tsx +++ b/src/components/crapp/ui-bits.tsx @@ -156,6 +156,27 @@ export function SezioneTendina({ ); } +/** Classi condivise di input, select e textarea nei form dell'app. */ +export const classiInput = + "w-full min-w-0 rounded-xl border border-border bg-background px-3 py-2 text-sm"; + +/** + * Etichetta + controllo di un form. `min-w-0`: dentro `grid-cols-2` questa label + * è l'elemento di griglia e ha `min-width: auto`, quindi si allarga fino al + * contenuto invece di stare nella colonna. Con un controllo nativo largo dentro + * (una data o un'ora su iOS) la coppia sfonda la card. + */ +export function Campo({ label, children }: { label: string; children: ReactNode }) { + return ( + + ); +} + export function StatoBadge({ stato, className }: { stato: Stato; className?: string }) { const meta = statoMeta[stato]; return ( diff --git a/src/routes/admin.tsx b/src/routes/admin.tsx index 6d56519..03ed346 100644 --- a/src/routes/admin.tsx +++ b/src/routes/admin.tsx @@ -16,7 +16,7 @@ import { } from "lucide-react"; import { toast } from "sonner"; import { cn } from "@/lib/utils"; -import { PageHeader, Section, StatTile } from "@/components/crapp/ui-bits"; +import { Campo, classiInput, PageHeader, Section, StatTile } from "@/components/crapp/ui-bits"; import { CampiProfilo } from "@/components/crapp/ProfiloAmministrativo"; import { nomeCompleto, @@ -76,8 +76,6 @@ const statoClasse: Record = { assente: "bg-secondary text-muted-foreground", }; -const classiInput = "w-full rounded-xl border border-border bg-background px-3 py-2 text-sm"; - /** L'unico vincolo unique lato database sulla tabella è l'email: messaggio leggibile invece * del codice Postgres (23505). */ function messaggioErrore(e: unknown, fallback: string): string { @@ -87,17 +85,6 @@ function messaggioErrore(e: unknown, fallback: string): string { return e instanceof Error ? e.message : fallback; } -function Campo({ label, children }: { label: string; children: React.ReactNode }) { - return ( - - ); -} - /** * Pannello di modifica dell'admin (DD-017): dati squadra, dati personali e collegamento * all'account. I file restano fuori: l'admin li scarica, non li carica al posto di altri. diff --git a/src/routes/eventi.tsx b/src/routes/eventi.tsx index df9d04c..c8c0732 100644 --- a/src/routes/eventi.tsx +++ b/src/routes/eventi.tsx @@ -3,7 +3,7 @@ import { createFileRoute, Link } from "@tanstack/react-router"; import { ArrowLeft, CalendarPlus, Loader2, Pencil, Trash2 } from "lucide-react"; import { toast } from "sonner"; import { cn } from "@/lib/utils"; -import { PageHeader, Section } from "@/components/crapp/ui-bits"; +import { Campo, classiInput, PageHeader, Section } from "@/components/crapp/ui-bits"; import { formatData } from "@/lib/crapp-data"; import { nomeCompleto, useGiocatoriSquadra } from "@/lib/giocatori-squadra"; import { @@ -151,7 +151,7 @@ function GestioneEventi() { maxLength={80} onChange={(e) => aggiorna({ titolo: e.target.value })} placeholder="Es. CRAP Volley vs Aurora Nera" - className="w-full rounded-xl border border-border bg-background px-3 py-2 text-sm" + className={classiInput} /> @@ -161,7 +161,7 @@ function GestioneEventi() { type="date" value={bozza.data} onChange={(e) => aggiorna({ data: e.target.value })} - className="w-full rounded-xl border border-border bg-background px-3 py-2 text-sm" + className={classiInput} /> @@ -169,7 +169,7 @@ function GestioneEventi() { type="time" value={bozza.ora} onChange={(e) => aggiorna({ ora: e.target.value })} - className="w-full rounded-xl border border-border bg-background px-3 py-2 text-sm" + className={classiInput} />
@@ -179,7 +179,7 @@ function GestioneEventi() { value={bozza.luogo} maxLength={80} onChange={(e) => aggiorna({ luogo: e.target.value })} - className="w-full rounded-xl border border-border bg-background px-3 py-2 text-sm" + className={classiInput} /> @@ -189,7 +189,7 @@ function GestioneEventi() { maxLength={300} rows={2} onChange={(e) => aggiorna({ note: e.target.value })} - className="w-full rounded-xl border border-border bg-background px-3 py-2 text-sm" + className={classiInput} /> @@ -322,17 +322,6 @@ function GestioneEventi() { ); } -function Campo({ label, children }: { label: string; children: React.ReactNode }) { - return ( - - ); -} - function Interruttore({ attivo, onClick, diff --git a/src/styles.css b/src/styles.css index 9fec6e1..4c59dc9 100644 --- a/src/styles.css +++ b/src/styles.css @@ -126,7 +126,7 @@ --ring: oklch(0.35 0.03 265); /* Luminosità scelte perché il testo bianco sopra superi 4.5:1 (WCAG AA). */ /* Compleanni / successi: verde un po' più chiaro e saturo. */ - --success: oklch(0.60 0.17 152); + --success: oklch(0.6 0.17 152); --success-foreground: oklch(0.99 0 0); --warning: oklch(0.78 0.15 78); --warning-foreground: oklch(0.18 0.02 60); @@ -528,15 +528,16 @@ } /* - * Su iOS `input[type="date"]` è un controllo nativo con una larghezza - * intrinseca (lo spazio di «gg/mm/aaaa») che `width: 100%` non riesce a far - * scendere: dentro `grid-cols-2` due date affiancate sfondano la card, e - * l'`overflow-hidden` del pannello a tab taglia via quello che esce. - * `appearance: none` è quello che rende il campo davvero ridimensionabile; - * bordo, sfondo e padding li mette già la classe condivisa. + * Su iOS `input[type="date"]` e `input[type="time"]` sono controlli nativi con + * una larghezza intrinseca (lo spazio di «gg/mm/aaaa», «--:--») che + * `width: 100%` non riesce a far scendere: dentro `grid-cols-2` due campi + * affiancati sfondano la card, e l'`overflow-hidden` del pannello a tab taglia + * via quello che esce. `appearance: none` è quello che rende il campo davvero + * ridimensionabile; bordo, sfondo e padding li mette già la classe condivisa. */ @layer base { - input[type="date"] { + input[type="date"], + input[type="time"] { min-width: 0; appearance: none; -webkit-appearance: none;