++ fix e controlli
This commit is contained in:
@@ -2,6 +2,12 @@
|
||||
|
||||
/* ─── TerManager2 Custom UI ─── */
|
||||
|
||||
/* Alpine nasconde con x-cloak solo se il foglio di stile lo dice: la regola non
|
||||
arriva col pacchetto. Senza, gli elementi marcati x-cloak (le barre di
|
||||
avanzamento, i messaggi d'errore) lampeggiano a ogni caricamento prima che
|
||||
Alpine parta. */
|
||||
[x-cloak] { display: none !important; }
|
||||
|
||||
/* Card hover lift effect */
|
||||
.card-hover {
|
||||
transition: transform 0.15s ease, box-shadow 0.15s ease;
|
||||
@@ -23,39 +29,6 @@
|
||||
background-color: #f9fafb;
|
||||
}
|
||||
|
||||
/* Action buttons */
|
||||
.btn-action {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 4px 10px;
|
||||
border-radius: 6px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
transition: all 0.15s ease;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
}
|
||||
.btn-action-green { background: #dcfce7; color: #166534; }
|
||||
.btn-action-green:hover { background: #bbf7d0; }
|
||||
.btn-action-red { background: #fee2e2; color: #991b1b; }
|
||||
.btn-action-red:hover { background: #fecaca; }
|
||||
.btn-action-blue { background: #dbeafe; color: #1e40af; }
|
||||
.btn-action-blue:hover { background: #bfdbfe; }
|
||||
.btn-action-amber { background: #fef3c7; color: #92400e; }
|
||||
.btn-action-amber:hover { background: #fde68a; }
|
||||
.btn-action-gray { background: #f3f4f6; color: #374151; }
|
||||
.btn-action-gray:hover { background: #e5e7eb; }
|
||||
.btn-action-indigo { background: #e0e7ff; color: #4338ca; }
|
||||
.btn-action-indigo:hover { background: #c7d2fe; }
|
||||
|
||||
/* Primary action buttons (filled) */
|
||||
.btn-primary-green { background: #16a34a; color: #fff; }
|
||||
.btn-primary-green:hover { background: #15803d; }
|
||||
.btn-primary-red { background: #dc2626; color: #fff; }
|
||||
.btn-primary-red:hover { background: #b91c1c; }
|
||||
|
||||
/* Gradient header */
|
||||
.header-gradient {
|
||||
background: linear-gradient(135deg, #4338ca 0%, #6366f1 50%, #818cf8 100%);
|
||||
@@ -102,6 +75,304 @@
|
||||
50% { opacity: 0.7; }
|
||||
}
|
||||
|
||||
/* ─── Pulsanti ─────────────────────────────────────────────────────────────
|
||||
Un solo sistema per tutta l'applicazione, definito qui e in nessun altro
|
||||
posto. Si usa dal componente <x-btn>, mai scrivendo queste classi a mano.
|
||||
|
||||
Da dove veniamo: quattro vocabolari in circolazione contemporaneamente —
|
||||
style= inline con i colori scritti a mano (piu' onmouseover per l'hover),
|
||||
catene di utility Tailwind ripetute a memoria, .btn-action* in sei tinte, e
|
||||
.btn-primary-* in due. La stessa azione cambiava aspetto da una pagina
|
||||
all'altra, un "Rientra" era rosso quanto un "Elimina", e nessuno di questi
|
||||
pulsanti aveva uno stato di focus: chi naviga da tastiera non vedeva dove si
|
||||
trovava.
|
||||
|
||||
Due assi, ortogonali:
|
||||
|
||||
variante primary l'azione principale. Una sola per schermata o zona.
|
||||
secondary tutto il resto (dettaglio, modifica, scarica, filtra)
|
||||
danger SOLO dove si cancellano o si sovrascrivono dati
|
||||
ghost annulla, chiudi, pulisci
|
||||
|
||||
tono solido il pieno. Per le azioni isolate.
|
||||
tenue la tinta chiara. Per le azioni dentro le righe di un
|
||||
elenco, dove cinquanta pulsanti pieni urlerebbero.
|
||||
|
||||
La dimensione `sm` e' quella delle righe di elenco; `icona` toglie il testo e
|
||||
lascia il quadrato.
|
||||
|
||||
Le regole stanno fuori da @layer, quindi vincono sulle utility di Tailwind:
|
||||
un pulsante non puo' essere ritoccato per meta' con le classi. */
|
||||
.tm-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 6px;
|
||||
min-height: 40px;
|
||||
padding: 10px 16px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid transparent;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
line-height: 1.2;
|
||||
white-space: nowrap;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
transition: background-color .15s ease, border-color .15s ease, color .15s ease;
|
||||
}
|
||||
.tm-btn svg {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
flex: none;
|
||||
}
|
||||
|
||||
/* Un anello unico per ogni variante: il colore del focus non deve dipendere
|
||||
da quello del pulsante, altrimenti su rosso o su bianco cambia visibilita'
|
||||
proprio dove serve di piu'. Il doppio contorno (chiaro + scuro) lo tiene
|
||||
leggibile sia sulle card bianche sia sui riquadri colorati. */
|
||||
.tm-btn:focus-visible,
|
||||
.tm-console-btn:focus-visible,
|
||||
.tm-nav-zona:focus-visible {
|
||||
outline: 3px solid #1d4ed8;
|
||||
outline-offset: 2px;
|
||||
box-shadow: 0 0 0 5px #fff;
|
||||
}
|
||||
|
||||
.tm-btn:disabled,
|
||||
.tm-btn[aria-disabled="true"] {
|
||||
opacity: .45;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.tm-btn-primary { background: #4f46e5; border-color: #3730a3; color: #fff; }
|
||||
.tm-btn-primary:hover:not(:disabled) { background: #4338ca; }
|
||||
|
||||
.tm-btn-danger { background: #dc2626; border-color: #991b1b; color: #fff; font-weight: 600; }
|
||||
.tm-btn-danger:hover:not(:disabled) { background: #b91c1c; }
|
||||
|
||||
.tm-btn-secondary { background: #fff; border-color: #d1d5db; color: #374151; }
|
||||
.tm-btn-secondary:hover:not(:disabled) { background: #f9fafb; border-color: #9ca3af; }
|
||||
|
||||
.tm-btn-ghost {
|
||||
background: transparent;
|
||||
border-color: transparent;
|
||||
color: #4b5563;
|
||||
min-height: 32px;
|
||||
padding: 4px 8px;
|
||||
font-size: 13px;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.tm-btn-ghost:hover:not(:disabled) { color: #111827; background: #f3f4f6; }
|
||||
|
||||
/* Tono tenue: stessa gerarchia, voce piu' bassa. Nelle righe di un elenco i
|
||||
pulsanti sono tanti quanti i record, e il pieno li trasforma in rumore —
|
||||
soprattutto il rosso, che smette di significare "attenzione" quando compare
|
||||
cinquanta volte di fila. */
|
||||
.tm-btn-tenue { border-color: transparent; font-weight: 600; }
|
||||
.tm-btn-tenue.tm-btn-primary { background: #e0e7ff; color: #4338ca; }
|
||||
.tm-btn-tenue.tm-btn-primary:hover:not(:disabled) { background: #c7d2fe; }
|
||||
.tm-btn-tenue.tm-btn-danger { background: #fee2e2; color: #991b1b; }
|
||||
.tm-btn-tenue.tm-btn-danger:hover:not(:disabled) { background: #fecaca; }
|
||||
.tm-btn-tenue.tm-btn-secondary { background: #f3f4f6; color: #374151; }
|
||||
.tm-btn-tenue.tm-btn-secondary:hover:not(:disabled) { background: #e5e7eb; }
|
||||
|
||||
/* Tono velato: per i comandi che stanno SOPRA una superficie colorata — oggi
|
||||
solo la testata, che ha il suo gradiente. Un pulsante bianco o indaco lì sopra
|
||||
sarebbe una toppa; qui il colore lo dà lo sfondo e il pulsante si limita a
|
||||
velarlo. Il focus diventa bianco, perche' l'anello blu su indaco non si
|
||||
distingue. */
|
||||
.tm-btn-velato {
|
||||
background: rgba(255, 255, 255, .12);
|
||||
border-color: transparent;
|
||||
color: rgba(255, 255, 255, .92);
|
||||
}
|
||||
.tm-btn-velato:hover:not(:disabled) { background: rgba(255, 255, 255, .22); color: #fff; }
|
||||
.tm-btn-velato:focus-visible {
|
||||
outline: 3px solid #fff;
|
||||
outline-offset: 2px;
|
||||
box-shadow: 0 0 0 5px rgba(0, 0, 0, .35);
|
||||
}
|
||||
|
||||
.tm-btn-sm { min-height: 32px; padding: 6px 12px; font-size: 13px; gap: 4px; }
|
||||
.tm-btn-sm svg { width: 14px; height: 14px; }
|
||||
|
||||
/* Solo icona: quadrato, senza testo. Serve sempre un aria-label, altrimenti chi
|
||||
usa un lettore di schermo trova un pulsante senza nome. */
|
||||
.tm-btn-icona { padding-left: 8px; padding-right: 8px; }
|
||||
.tm-btn-sm.tm-btn-icona { padding-left: 7px; padding-right: 7px; }
|
||||
|
||||
/* ─── Zone della pagina ────────────────────────────────────────────────────
|
||||
Ogni sezione e' un bersaglio di navigazione: premendo un pulsante o una voce
|
||||
dell'indice la pagina ci si porta sopra e la zona lampeggia, cosi' e' chiaro
|
||||
*dove* e' finita l'attenzione invece di dover indovinare cosa e' cambiato.
|
||||
|
||||
scroll-margin-top tiene la testata della zona sotto la barra dell'indice,
|
||||
che e' appiccicata in alto: senza, l'ancora finirebbe coperta. */
|
||||
.tm-zona {
|
||||
scroll-margin-top: 84px;
|
||||
}
|
||||
|
||||
/* Lampeggio d'arrivo. Non usa `outline` perche' si somma male ai bordi delle
|
||||
card; box-shadow non occupa spazio e non fa saltare il layout. */
|
||||
.tm-zona-evidenziata {
|
||||
animation: tmEvidenzia 1.6s ease-out;
|
||||
}
|
||||
@keyframes tmEvidenzia {
|
||||
0% { box-shadow: 0 0 0 0 rgba(29, 78, 216, .6); }
|
||||
12% { box-shadow: 0 0 0 7px rgba(29, 78, 216, .35); }
|
||||
100% { box-shadow: 0 0 0 0 rgba(29, 78, 216, 0); }
|
||||
}
|
||||
|
||||
/* Zona con un'operazione in corso: resta marcata finche' dura, cosi' anche
|
||||
tornando sulla pagina dopo un ricaricamento si vede subito chi sta lavorando. */
|
||||
.tm-zona-attiva {
|
||||
box-shadow: 0 0 0 2px #4f46e5;
|
||||
}
|
||||
|
||||
/* Chi ha chiesto meno animazioni riceve comunque il segnale, fermo. */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.tm-zona-evidenziata { animation: none; box-shadow: 0 0 0 3px rgba(29, 78, 216, .5); }
|
||||
.tm-btn { transition: none; }
|
||||
}
|
||||
|
||||
/* Indice delle zone, appiccicato in alto. */
|
||||
.tm-nav-zone {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 20;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
padding: 10px 12px;
|
||||
margin: -4px 0 0;
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 12px;
|
||||
background: rgba(255, 255, 255, .92);
|
||||
backdrop-filter: blur(6px);
|
||||
}
|
||||
.tm-nav-zona {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 5px 10px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid #e5e7eb;
|
||||
background: #fff;
|
||||
color: #374151;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
.tm-nav-zona:hover { background: #eef2ff; border-color: #c7d2fe; color: #3730a3; }
|
||||
.tm-nav-zona-pericolo { color: #991b1b; border-color: #fecaca; }
|
||||
.tm-nav-zona-pericolo:hover { background: #fef2f2; border-color: #fca5a5; color: #7f1d1d; }
|
||||
|
||||
/* ─── Console delle operazioni ─────────────────────────────────────────────
|
||||
Una sola finestra per tutte le operazioni lunghe, agganciata in fondo alla
|
||||
finestra del browser: quello che sta succedendo non deve uscire dallo schermo
|
||||
mentre si scorre la pagina. Chiusa e' una striscia alta 44px.
|
||||
|
||||
Il posto in cui vive e' il corpo della pagina, ma la posizione e' fissa,
|
||||
quindi in fondo alla pagina c'e' uno spaziatore della stessa altezza: senza,
|
||||
coprirebbe l'ultima zona — che e' quella pericolosa. */
|
||||
.tm-console {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 45;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: #0b1020;
|
||||
border-top: 1px solid #1f2937;
|
||||
box-shadow: 0 -8px 24px -12px rgba(0, 0, 0, .5);
|
||||
color: #e5e7eb;
|
||||
}
|
||||
.tm-console-testata {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 8px 12px;
|
||||
min-height: 44px;
|
||||
}
|
||||
.tm-console-titolo {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
background: none;
|
||||
border: 0;
|
||||
padding: 0;
|
||||
color: #e5e7eb;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
}
|
||||
.tm-console-btn {
|
||||
background: transparent;
|
||||
border: 1px solid #374151;
|
||||
border-radius: 8px;
|
||||
padding: 4px 10px;
|
||||
color: #d1d5db;
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.tm-console-btn:hover { background: #1f2937; color: #fff; }
|
||||
|
||||
/* Il corpo scorre; l'altezza e' in vh perche' su un portatile basso una
|
||||
finestra fissa in pixel si mangerebbe mezza pagina. */
|
||||
.tm-console-corpo {
|
||||
max-height: min(38vh, 340px);
|
||||
overflow-y: auto;
|
||||
padding: 4px 12px 12px;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
||||
font-size: 12px;
|
||||
line-height: 1.55;
|
||||
}
|
||||
.tm-console-riga { display: flex; gap: 8px; white-space: pre-wrap; word-break: break-word; }
|
||||
.tm-console-ora { color: #6b7280; flex: none; }
|
||||
.tm-console-sorgente { color: #818cf8; flex: none; }
|
||||
.tm-console-riga-errore .tm-console-testo { color: #fca5a5; font-weight: 600; }
|
||||
.tm-console-riga-ok .tm-console-testo { color: #6ee7b7; }
|
||||
.tm-console-riga-attesa .tm-console-testo { color: #fcd34d; }
|
||||
|
||||
/* Barra di avanzamento nella testata: sottile, sempre allo stesso posto. */
|
||||
.tm-console-barra {
|
||||
flex: 1;
|
||||
height: 6px;
|
||||
min-width: 60px;
|
||||
border-radius: 999px;
|
||||
background: #1f2937;
|
||||
overflow: hidden;
|
||||
}
|
||||
.tm-console-barra > div {
|
||||
height: 100%;
|
||||
border-radius: 999px;
|
||||
background: #6366f1;
|
||||
transition: width .3s ease;
|
||||
}
|
||||
.tm-console-barra-ok > div { background: #10b981; }
|
||||
.tm-console-barra-errore > div { background: #ef4444; }
|
||||
|
||||
/* "Sei tornato indietro a leggere": lo scorrimento automatico si sospende e
|
||||
compare il modo per riagganciarsi. */
|
||||
.tm-console-torna {
|
||||
align-self: center;
|
||||
margin: 0 12px 8px;
|
||||
background: #312e81;
|
||||
border: 1px solid #4f46e5;
|
||||
border-radius: 999px;
|
||||
padding: 3px 12px;
|
||||
color: #e0e7ff;
|
||||
font-size: 11px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.tm-console-corpo { max-height: 45vh; font-size: 11px; }
|
||||
.tm-console-sorgente { display: none; }
|
||||
}
|
||||
|
||||
/* ─── Responsive list tables ───────────────────────────────────────────────
|
||||
Under 640px (a phone held in portrait; rotate to landscape and the width
|
||||
grows past the breakpoint, restoring the normal table) every table with the
|
||||
|
||||
Reference in New Issue
Block a user