Rifacimento della UI secondo STILE_UI.md

Adotta lo stile condiviso delle app Integry: superfici piatte, raggi ampi,
spaziature a scala fissa, gerarchia data dallo spazio prima che dalla
decorazione. La palette storica di SteUP resta intatta; sono stati aggiunti
solo i ruoli che mancavano (workspace, surfaceMuted, divider, inkMuted e le
triplette dei toni semantici).

Fondamenta
- tokens.css: ruoli colore, scale di spazio/raggi/tipografia, curve di
  movimento e scala z-index semantica. Unica fonte di verita': pilotano
  anche le variabili CSS di MudBlazor.
- Inter variabile ospitata in locale (133KB) al posto di Nunito da Google
  Fonts: l'app lavora offline, un font che arriva dalla rete in campo non
  arriva.
- base.css: shell applicativa e comportamenti da app nativa — niente
  selezione testo, niente menu al tocco lungo, niente lampo grigio sul tap,
  niente rimbalzo elastico, niente zoom a doppio tocco, scroll con inerzia.
- mudblazor-overrides.css: MudBlazor appiattito (elevation 0, nessun tint,
  target 44/48, etichette in caso naturale).
- Rimossi Bootstrap (CSS, icone e JS) e i 996 righe di CSS con ambito per
  componente del vecchio sistema: erano tutti inutilizzati.

Struttura
- Navigazione flottante in basso con icona piena sulla voce attiva; da 720px
  diventa colonna laterale. Il MudFabMenu e' sostituito da un bottom sheet:
  stessa funzione, bersagli molto piu' grandi.
- Testata alta col titolo a sinistra, fusa con la pagina.
- Nel form scheda il salvataggio sta in una barra ancorata in fondo, non in
  coda a uno scroll lungo.
- Scelta di punto vendita e articoli a schermo intero con ricerca; la
  selezione multipla usa righe da 56px invece di un menu a tendina.
- Urgenza scelta a pillole invece che da una select.

Stato e riscontro
- StatusBadgeHelper: tono, icona ed etichetta di ogni stato in un punto solo,
  cosi' lo stesso stato si presenta identico ovunque.
- Scheletri al posto degli spinner; l'overlay bloccante dice sempre cosa sta
  facendo; gli stati vuoti suggeriscono la mossa successiva.
- Rimosso il ritardo artificiale di 250ms in InspectionCard, che non
  attendeva nulla.

DESIGN.md riscritto sul nuovo sistema.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-17 11:09:26 +02:00
parent 3a9174f6fc
commit 2a0054aa30
77 changed files with 4595 additions and 3060 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+19 -97
View File
@@ -1,105 +1,14 @@
// Funzione goBack
// Interoperabilità minima con l'host. Il feedback al tocco e gli stati sono
// gestiti in CSS: qui resta solo ciò che il CSS non può fare.
window.goBack = function () {
console.log("goBack");
history.back();
};
// Funzione per monitorare bottom-sheet-container e gestire la navbar
function monitorBottomSheetClass(mutations) {
const bottomSheet = document.querySelector(".bottom-sheet-container");
const navbar = document.querySelector(".animated-navbar");
if (!bottomSheet || !navbar) return;
mutations.forEach(function (mutation) {
if (mutation.type === 'attributes' && mutation.attributeName === 'class' && mutation.target === bottomSheet) {
if (bottomSheet.classList.contains("show")) {
navbar.classList.remove("show-nav");
navbar.classList.add("hide-nav");
console.log("Navbar nascosta (hide-nav)");
} else {
navbar.classList.remove("hide-nav");
navbar.classList.add("show-nav");
console.log("Navbar mostrata (show-nav)");
}
}
});
}
// Observer combinato per tutte le funzionalità
const observer = new MutationObserver((mutations) => {
// Monitora bottom-sheet-container
monitorBottomSheetClass(mutations);
});
// Osserva sia i cambiamenti nel DOM che gli attributi
observer.observe(document.body, {
childList: true,
subtree: true,
attributes: true,
attributeFilter: ['class']
});
// Sync iniziale per la navbar (nel caso la pagina parte già con .show)
document.addEventListener("DOMContentLoaded", () => {
const bottomSheet = document.querySelector(".bottom-sheet-container");
const navbar = document.querySelector(".animated-navbar");
if (bottomSheet && navbar) {
if (bottomSheet.classList.contains("show")) {
navbar.classList.remove("show-nav");
navbar.classList.add("hide-nav");
} else {
navbar.classList.remove("hide-nav");
navbar.classList.add("show-nav");
}
}
});
(function () {
// Ascoltiamo l'evento 'mousedown' su tutto il documento
document.addEventListener('mousedown', function (event) {
// Cerca se l'elemento cliccato (o un suo genitore) ha la classe .ripple-container
const target = event.target.closest('.ripple-container');
if (target) {
createRipple(event, target);
}
});
function createRipple(event, element) {
const circle = document.createElement("span");
const diameter = Math.max(element.clientWidth, element.clientHeight);
const radius = diameter / 2;
const rect = element.getBoundingClientRect();
circle.style.width = circle.style.height = `${diameter}px`;
circle.style.left = `${event.clientX - rect.left - radius}px`;
circle.style.top = `${event.clientY - rect.top - radius}px`;
circle.classList.add("ripple");
// Rimuovi l'elemento dal DOM alla fine dell'animazione per non intasare la memoria
const ripple = element.getElementsByClassName("ripple")[0];
if (ripple) {
ripple.remove();
}
element.appendChild(circle);
// Pulizia automatica dopo 600ms (durata animazione CSS)
setTimeout(() => {
circle.remove();
}, 600);
}
})();
// Tastiera: porta il campo attivo dentro l'area visibile.
// Il layout e' a 100vh con overflow:hidden, quindi quando l'host riduce la
// WebView per la tastiera il browser non scrolla da solo sul campo col focus.
// Il layout e' a piena altezza con lo scroll in un contenitore, quindi quando
// l'host riduce la WebView per la tastiera il browser non scrolla da solo sul
// campo col focus.
(function () {
const SELECTOR = 'input, textarea, [contenteditable="true"]';
let pending = null;
@@ -130,3 +39,16 @@ document.addEventListener("DOMContentLoaded", () => {
window.addEventListener('resize', schedule);
}
})();
// Un doppio tocco ravvicinato su una WebView fa zoomare la pagina: e' il
// gesto del browser, non quello di un'app. Il viewport lo vieta gia', questo
// copre i casi in cui Android lo reintroduce.
(function () {
let lastTouch = 0;
document.addEventListener('touchend', function (event) {
const now = Date.now();
if (now - lastTouch <= 320) event.preventDefault();
lastTouch = now;
}, {passive: false});
})();