101b68123c
La MudSelect apriva un riquadro bianco che galleggiava sopra le card bianche, senza titolo, senza sfondo oscurato, con la barra di scorrimento del desktop e le righe alte quanto una riga di testo. Era la cosa che piu' di tutte faceva sembrare l'app una pagina web. Ora il campo e' una riga tappabile che mostra il valore, e la scelta arriva da un bottom sheet con il titolo del campo e righe da 56 px con il segno tondo della scelta singola: la stessa forma degli altri selettori dell'app, usabile in piedi e con i guanti. Il confronto per capire quale opzione e' quella corrente passa per l'etichetta: Scheda.Reparto ricostruisce l'oggetto al volo da codice e descrizione, quindi non e' mai lo stesso oggetto di quelli in elenco e il confronto di riferimento non avrebbe segnato mai nulla. Reparto e motivo non passano piu' dalla validazione di MudForm, perche' non sono piu' input: il controllo degli obbligatori e' esplicito e l'errore si vede sul gruppo interessato, non solo nel messaggio in cima. Si accende dopo il primo tentativo di salvataggio, non prima: un form rosso all'apertura accusa l'utente di un errore che non ha ancora commesso. La scelta fatta si segna col tono informativo: un bordo corallo intorno a un riempimento azzurro si leggeva come un errore. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
449 lines
13 KiB
CSS
449 lines
13 KiB
CSS
/* =============================================================================
|
|
SteUP — Form
|
|
Il form scheda e' il cuore dell'app: si compila in piedi, con una mano.
|
|
Ogni campo e' un blocco largo e tappabile, non una riga di tabella.
|
|
========================================================================== */
|
|
|
|
/* -----------------------------------------------------------------------------
|
|
Gruppo di campo — un Panel con la sua etichetta
|
|
-------------------------------------------------------------------------- */
|
|
|
|
.form-group {
|
|
background-color: var(--surface);
|
|
border-radius: var(--radius-surface);
|
|
padding: var(--space-md);
|
|
}
|
|
|
|
/* Su schermo stretto il form e' lungo: ogni pixel di padding e' una riga di
|
|
lavoro in meno visibile a colpo d'occhio. */
|
|
@media (max-width: 380px) {
|
|
.form-group { padding: var(--space-sm) var(--space-14); }
|
|
}
|
|
|
|
.form-group__label {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: var(--space-xs);
|
|
font-size: var(--label-md-size);
|
|
line-height: 1.25;
|
|
font-weight: 500;
|
|
color: var(--ink-muted);
|
|
margin-bottom: var(--space-6);
|
|
}
|
|
|
|
.form-group__count {
|
|
font-size: var(--label-md-size);
|
|
font-weight: 500;
|
|
color: var(--ink-muted);
|
|
}
|
|
|
|
/* Il valore del campo: testo grande, tocco comodo. */
|
|
.form-group .mud-input-control { margin: 0; }
|
|
|
|
.form-group .mud-input-slot,
|
|
.form-group input,
|
|
.form-group .mud-select-input {
|
|
font-size: var(--body-lg-size);
|
|
font-weight: 500;
|
|
color: var(--ink);
|
|
min-height: var(--touch-min);
|
|
}
|
|
|
|
.form-group--required .form-group__label::after {
|
|
content: "obbligatorio";
|
|
font-size: var(--label-sm-size);
|
|
font-weight: 500;
|
|
color: var(--ink-muted);
|
|
}
|
|
|
|
.form-group--invalid { box-shadow: inset 0 0 0 1px var(--danger-solid); }
|
|
|
|
.form-error {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--space-6);
|
|
margin-top: var(--space-xs);
|
|
font-size: var(--body-md-size);
|
|
font-weight: 500;
|
|
color: var(--danger-ink);
|
|
}
|
|
|
|
/* -----------------------------------------------------------------------------
|
|
Riga di scelta rapida (scadenza, priorita'): pillole invece di una select
|
|
-------------------------------------------------------------------------- */
|
|
|
|
.option-row {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: var(--space-xs);
|
|
}
|
|
|
|
.option {
|
|
flex: 1 1 auto;
|
|
min-width: 96px;
|
|
min-height: var(--touch-min);
|
|
padding: var(--space-xs) var(--space-sm);
|
|
border-radius: var(--radius-pill);
|
|
border: 1px solid var(--divider);
|
|
background-color: var(--surface-muted);
|
|
color: var(--ink-muted);
|
|
font-family: inherit;
|
|
font-size: var(--label-lg-size);
|
|
font-weight: 500;
|
|
transition: background-color var(--duration-fast) var(--ease-out),
|
|
border-color var(--duration-fast) var(--ease-out),
|
|
color var(--duration-fast) var(--ease-out),
|
|
transform var(--duration-fast) var(--ease-out);
|
|
}
|
|
|
|
.option:active { transform: scale(0.97); }
|
|
|
|
/* La scelta fatta si segna col tono informativo, non con laccento: un bordo
|
|
corallo intorno a un riempimento azzurro si legge come un errore, non come
|
|
"questa e la risposta". Laccento resta del solo pulsante primario. */
|
|
.option[aria-pressed="true"] {
|
|
font-weight: 600;
|
|
background-color: var(--info-surface);
|
|
border-color: var(--info-solid);
|
|
color: var(--info-ink);
|
|
}
|
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.option:active { transform: none; }
|
|
}
|
|
|
|
/* -----------------------------------------------------------------------------
|
|
Campo barcode manuale — affiancato al pulsante di invio
|
|
-------------------------------------------------------------------------- */
|
|
|
|
.barcode-field {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--space-xs);
|
|
padding: var(--space-xs) var(--space-xs) var(--space-xs) var(--space-md);
|
|
border-radius: var(--radius-pill);
|
|
background-color: var(--surface-muted);
|
|
border: 1.5px solid transparent;
|
|
transition: border-color var(--duration-fast) var(--ease-out);
|
|
}
|
|
|
|
.barcode-field:focus-within { border-color: var(--primary); }
|
|
|
|
.barcode-field input {
|
|
flex: 1 1 auto;
|
|
min-width: 0;
|
|
min-height: var(--touch-min);
|
|
border: none;
|
|
background: none;
|
|
outline: none;
|
|
font-family: inherit;
|
|
font-size: var(--body-lg-size);
|
|
font-weight: 500;
|
|
color: var(--ink);
|
|
/* Il codice si legge cifra per cifra: cifre a larghezza fissa. */
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
|
|
.barcode-field input::placeholder { color: var(--ink-muted); font-weight: 400; }
|
|
|
|
.barcode-field__send {
|
|
flex: 0 0 auto;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: var(--touch-min);
|
|
height: var(--touch-min);
|
|
border: none;
|
|
border-radius: var(--radius-pill);
|
|
background-color: var(--primary);
|
|
color: var(--on-primary);
|
|
font-size: 20px;
|
|
transition: transform var(--duration-fast) var(--ease-out),
|
|
background-color var(--duration-fast) var(--ease-out);
|
|
}
|
|
|
|
.barcode-field__send:active { transform: scale(0.92); background-color: var(--primary-pressed); }
|
|
.barcode-field__send:disabled { background-color: var(--divider); color: var(--ink-muted); }
|
|
|
|
/* -----------------------------------------------------------------------------
|
|
Articoli scansionati
|
|
L'ultimo letto entra dall'alto: e' la conferma visiva della scansione.
|
|
-------------------------------------------------------------------------- */
|
|
|
|
.art-list {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--space-xs);
|
|
}
|
|
|
|
.art-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--space-sm);
|
|
padding: var(--space-10) var(--space-sm);
|
|
border-radius: var(--radius-control);
|
|
background-color: var(--surface-muted);
|
|
animation: art-enter var(--duration-slow) var(--ease-out);
|
|
}
|
|
|
|
@keyframes art-enter {
|
|
from { opacity: 0; transform: translateY(-6px); }
|
|
to { opacity: 1; transform: none; }
|
|
}
|
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.art-item { animation: none; }
|
|
}
|
|
|
|
.art-item__text { flex: 1 1 auto; min-width: 0; }
|
|
|
|
.art-item__name {
|
|
font-size: var(--body-lg-size);
|
|
line-height: 1.25;
|
|
font-weight: 600;
|
|
color: var(--ink);
|
|
}
|
|
|
|
.art-item__code {
|
|
margin-top: var(--space-2);
|
|
font-size: var(--label-md-size);
|
|
font-weight: 400;
|
|
color: var(--ink-muted);
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
|
|
.art-item__remove {
|
|
flex: 0 0 auto;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: var(--touch-min);
|
|
height: var(--touch-min);
|
|
border: none;
|
|
border-radius: var(--radius-pill);
|
|
background: none;
|
|
color: var(--ink-muted);
|
|
font-size: 20px;
|
|
}
|
|
|
|
.art-item__remove:active { background-color: var(--danger-surface); color: var(--danger-ink); }
|
|
|
|
/* -----------------------------------------------------------------------------
|
|
Foto allegate — striscia orizzontale scorrevole
|
|
-------------------------------------------------------------------------- */
|
|
|
|
.attach-strip {
|
|
display: flex;
|
|
gap: var(--space-xs);
|
|
overflow-x: auto;
|
|
scrollbar-width: none;
|
|
-webkit-overflow-scrolling: touch;
|
|
padding-inline: var(--space-md);
|
|
margin-inline: calc(var(--space-md) * -1);
|
|
}
|
|
|
|
.attach-strip::-webkit-scrollbar { display: none; }
|
|
|
|
.attach-item {
|
|
position: relative;
|
|
flex: 0 0 auto;
|
|
width: 104px;
|
|
border-radius: var(--radius-control);
|
|
overflow: hidden;
|
|
background-color: var(--surface-muted);
|
|
}
|
|
|
|
.attach-item__img {
|
|
width: 104px;
|
|
height: 104px;
|
|
object-fit: cover;
|
|
display: block;
|
|
}
|
|
|
|
.attach-item__placeholder {
|
|
width: 104px;
|
|
height: 104px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
color: var(--ink-muted);
|
|
font-size: 26px;
|
|
}
|
|
|
|
.attach-item__remove {
|
|
position: absolute;
|
|
top: var(--space-4);
|
|
right: var(--space-4);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 30px;
|
|
height: 30px;
|
|
border: none;
|
|
border-radius: var(--radius-pill);
|
|
background-color: rgba(0, 0, 0, 0.62);
|
|
color: #ffffff;
|
|
font-size: 20px;
|
|
}
|
|
|
|
.attach-item__remove:active { background-color: var(--danger-solid); }
|
|
|
|
/* -----------------------------------------------------------------------------
|
|
Bottone di azione secondaria con icona in pastiglia
|
|
-------------------------------------------------------------------------- */
|
|
|
|
.action-tile {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--space-sm);
|
|
width: 100%;
|
|
min-height: 56px;
|
|
padding: var(--space-sm) var(--space-md);
|
|
border: 1px solid var(--divider);
|
|
border-radius: var(--radius-control);
|
|
background-color: var(--surface);
|
|
font-family: inherit;
|
|
font-size: var(--body-lg-size);
|
|
font-weight: 500;
|
|
color: var(--ink);
|
|
text-align: left;
|
|
transition: transform var(--duration-fast) var(--ease-out),
|
|
background-color var(--duration-fast) var(--ease-out);
|
|
}
|
|
|
|
.action-tile:active { transform: scale(0.99); background-color: var(--surface-muted); }
|
|
.action-tile:disabled { color: var(--ink-muted); }
|
|
|
|
.action-tile__icon {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 36px;
|
|
height: 36px;
|
|
border-radius: var(--radius-compact);
|
|
font-size: 20px;
|
|
flex: 0 0 auto;
|
|
}
|
|
|
|
.action-tile__icon--neutral { background-color: var(--neutral-surface); color: var(--neutral-ink); }
|
|
/* Le azioni costruttive secondarie non portano l'accento: il corallo resta del
|
|
pulsante primario. Un fondo corallo tenue sarebbe comunque indistinguibile
|
|
dal tono danger, perche' il primario di SteUP e' gia' un rosso — ed e'
|
|
esattamente cosi' che "Nuova scheda" finiva identica a "Elimina ispezione". */
|
|
.action-tile__icon--primary { background-color: var(--info-surface); color: var(--info-ink); }
|
|
.action-tile__icon--success { background-color: var(--success-surface); color: var(--success-ink); }
|
|
.action-tile__icon--danger { background-color: var(--danger-surface); color: var(--danger-ink); }
|
|
.action-tile__icon--info { background-color: var(--info-surface); color: var(--info-ink); }
|
|
|
|
.action-tile__label { flex: 1 1 auto; min-width: 0; }
|
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.action-tile:active { transform: none; }
|
|
}
|
|
|
|
/* -----------------------------------------------------------------------------
|
|
Riga di selezione multipla
|
|
Una riga larga e tappabile al posto di un menu a tendina: con i guanti si
|
|
centra un bersaglio da 56px, non una casella da 18.
|
|
-------------------------------------------------------------------------- */
|
|
|
|
.select-row {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--space-sm);
|
|
width: 100%;
|
|
min-height: 56px;
|
|
padding: var(--space-10) var(--space-sm);
|
|
border: none;
|
|
border-radius: var(--radius-control);
|
|
background-color: var(--surface);
|
|
font-family: inherit;
|
|
text-align: left;
|
|
transition: background-color var(--duration-fast) var(--ease-out);
|
|
}
|
|
|
|
.select-row[aria-pressed="true"] { background-color: var(--info-surface); }
|
|
|
|
.select-row__check {
|
|
flex: 0 0 auto;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 26px;
|
|
height: 26px;
|
|
border-radius: var(--radius-compact);
|
|
border: 2px solid var(--divider);
|
|
background-color: var(--surface);
|
|
color: transparent;
|
|
font-size: 18px;
|
|
transition: background-color var(--duration-fast) var(--ease-out),
|
|
border-color var(--duration-fast) var(--ease-out),
|
|
color var(--duration-fast) var(--ease-out);
|
|
}
|
|
|
|
.select-row[aria-pressed="true"] .select-row__check {
|
|
background-color: var(--primary);
|
|
border-color: var(--primary);
|
|
color: var(--on-primary);
|
|
}
|
|
|
|
.select-row__text { flex: 1 1 auto; min-width: 0; }
|
|
|
|
.select-row__title {
|
|
font-size: var(--body-lg-size);
|
|
line-height: 1.3;
|
|
font-weight: 500;
|
|
color: var(--ink);
|
|
}
|
|
|
|
.select-row__meta {
|
|
margin-top: var(--space-2);
|
|
font-size: var(--label-md-size);
|
|
font-weight: 400;
|
|
color: var(--ink-muted);
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
|
|
/* -----------------------------------------------------------------------------
|
|
Campo a scelta singola — la riga che apre il bottom sheet
|
|
Ha l'aspetto di un valore, non di un controllo: il chevron basta a dire
|
|
che si tocca.
|
|
-------------------------------------------------------------------------- */
|
|
|
|
.select-field {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--space-xs);
|
|
width: 100%;
|
|
min-height: var(--touch-min);
|
|
padding: var(--space-xs) 0;
|
|
border: none;
|
|
background: none;
|
|
font-family: inherit;
|
|
text-align: left;
|
|
transition: opacity var(--duration-fast) var(--ease-out);
|
|
}
|
|
|
|
.select-field:active { opacity: 0.6; }
|
|
.select-field:disabled { opacity: 1; }
|
|
|
|
.select-field__value {
|
|
flex: 1 1 auto;
|
|
min-width: 0;
|
|
font-size: var(--body-lg-size);
|
|
line-height: 1.3;
|
|
font-weight: 500;
|
|
color: var(--ink);
|
|
}
|
|
|
|
/* Il segnaposto e' testo secondario, non un valore: si distingue a colpo
|
|
d'occhio da un campo gia' compilato. */
|
|
.select-field__value--empty { font-weight: 400; color: var(--ink-muted); }
|
|
|
|
.select-field__icon { flex: 0 0 auto; font-size: 22px; color: var(--ink-muted); }
|
|
|
|
/* Scelta singola: il segno e' tondo come un radio, non quadrato come una
|
|
casella. La forma dice gia' che si sceglie una cosa sola. */
|
|
.select-row--single .select-row__check { border-radius: var(--radius-pill); }
|