Porta l'azione "nuova ispezione" dentro la barra di navigazione

Il "+" non galleggia piu' sopra la barra e non sta piu' nella testata: e'
la casella centrale della barra, alla stessa altezza delle altre due e
sotto il pollice. La barra guadagna un margine interno, cosi' le voci non
toccano il bordo stondato e la pastiglia d'accento e' concentrica al
guscio invece che incollata sopra.

L'azione vale la stessa cosa in ogni schermata, e senza rete resta al suo
posto spenta: una casella che scompare sposterebbe le altre due sotto il
dito.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-17 15:16:52 +02:00
parent e3c9880d86
commit 9703fca306
3 changed files with 87 additions and 19 deletions
+28 -3
View File
@@ -1,4 +1,5 @@
@implements IDisposable @implements IDisposable
@inject IDialogService Dialog
@* @*
Navigazione flottante in basso (STILE_UI §6.15): il contenuto le scorre Navigazione flottante in basso (STILE_UI §6.15): il contenuto le scorre
@@ -6,9 +7,11 @@
riposo, piena da selezionata, etichetta sempre visibile in entrambe. riposo, piena da selezionata, etichetta sempre visibile in entrambe.
Da 720px diventa una colonna laterale (vedi components.css). Da 720px diventa una colonna laterale (vedi components.css).
Qui stanno SOLO le destinazioni. Le azioni di una schermata appartengono In mezzo alle due destinazioni sta l'unica azione dell'app: aprire una
alla schermata, e si trovano nella sua testata: un "+" incastrato nella nuova ispezione. Non e' un pulsante che galleggia sopra la barra e ne
barra non dice cosa crea, e cambia significato da una pagina all'altra. copre il centro, e' la terza casella della barra, alla stessa altezza
delle altre due. Vale una cosa sola dappertutto: se cambiasse significato
da una pagina all'altra sarebbe un tasto da indovinare.
*@ *@
<nav class="navbar @(IsVisible ? "" : "navbar--hidden")" aria-label="Navigazione principale"> <nav class="navbar @(IsVisible ? "" : "navbar--hidden")" aria-label="Navigazione principale">
@@ -19,6 +22,14 @@
<span class="navbar__label">Ispezioni</span> <span class="navbar__label">Ispezioni</span>
</NavLink> </NavLink>
<button type="button" class="navbar__action" @onclick="NewInspection"
disabled="@(!NetworkService.IsNetworkAvailable())"
aria-label="Nuova ispezione"
title="@(NetworkService.IsNetworkAvailable() ? null : "Serve la connessione per aprire una nuova ispezione")">
<i class="ri-add-line" aria-hidden="true"></i>
<span class="navbar__action-label">Nuova</span>
</button>
<NavLink class="navbar__item pressable" href="user" Match="NavLinkMatch.All"> <NavLink class="navbar__item pressable" href="user" Match="NavLinkMatch.All">
<span class="navbar__icon"> <span class="navbar__icon">
<i class="@(IsOn("user") ? "ri-user-fill" : "ri-user-line")" aria-hidden="true"></i> <i class="@(IsOn("user") ? "ri-user-fill" : "ri-user-line")" aria-hidden="true"></i>
@@ -52,5 +63,19 @@
StateHasChanged(); StateHasChanged();
} }
private async Task NewInspection()
{
if (!await SteupDataService.CanOpenNewInspection())
{
await Dialog.ShowWarning(
"Per aprire una nuova ispezione devi prima concludere tutte le ispezioni aperte da piu' di 20 giorni.");
return;
}
// La scelta del punto vendita naviga da sola sull'ispezione: qui non
// c'e' altro da fare, nemmeno quando l'utente annulla.
await Dialog.OpenSelectShop();
}
public void Dispose() => NavigationManager.LocationChanged -= OnLocationChanged; public void Dispose() => NavigationManager.LocationChanged -= OnLocationChanged;
} }
@@ -7,19 +7,9 @@
@inject IIspezioniService IspezioniService @inject IIspezioniService IspezioniService
@inject IDialogService Dialog @inject IDialogService Dialog
<HeaderLayout Title="Ispezioni" Subtitle="@Subtitle"> @* L'azione "nuova ispezione" non sta qui: vive nella barra in basso, dove
<Actions> il pollice arriva senza attraversare lo schermo. *@
@* L'unica azione della schermata: porta dritta alla scelta del punto <HeaderLayout Title="Ispezioni" Subtitle="@Subtitle"/>
vendita. Un passaggio intermedio per scegliere fra una cosa sola non
e' una scelta, e' un tocco in piu'. *@
<button type="button" class="appbar__action" @onclick="NewInspection"
disabled="@(!NetworkService.IsNetworkAvailable())"
title="@(NetworkService.IsNetworkAvailable() ? null : "Serve la connessione per aprire una nuova ispezione")">
<i class="ri-add-line" aria-hidden="true"></i>
<span>Nuova</span>
</button>
</Actions>
</HeaderLayout>
<div class="app-scroll"> <div class="app-scroll">
<div class="page-width page-body"> <div class="page-width page-body">
+56 -3
View File
@@ -520,11 +520,15 @@
z-index: var(--z-navbar); z-index: var(--z-navbar);
display: flex; display: flex;
align-items: stretch; align-items: stretch;
gap: var(--space-4);
height: var(--navbar-height); height: var(--navbar-height);
border-radius: 24px; /* Il guscio ha il suo margine interno: le voci non toccano il bordo
stondato, e l'azione in mezzo puo' essere una pastiglia piena senza
sembrare una toppa incollata sulla barra. */
padding: var(--space-6);
border-radius: 26px;
background-color: var(--surface); background-color: var(--surface);
border: 1px solid var(--divider); border: 1px solid var(--divider);
overflow: hidden;
transition: transform var(--duration-base) var(--ease-out), transition: transform var(--duration-base) var(--ease-out),
opacity var(--duration-base) var(--ease-out); opacity var(--duration-base) var(--ease-out);
} }
@@ -543,6 +547,7 @@
justify-content: center; justify-content: center;
gap: var(--space-2); gap: var(--space-2);
min-width: var(--touch-min); min-width: var(--touch-min);
border-radius: 20px;
color: var(--ink-muted); color: var(--ink-muted);
text-decoration: none; text-decoration: none;
background: none; background: none;
@@ -555,13 +560,52 @@
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
width: 56px; width: 100%;
max-width: 64px;
height: 30px; height: 30px;
border-radius: var(--radius-pill); border-radius: var(--radius-pill);
font-size: 20px; font-size: 20px;
transition: background-color var(--duration-base) var(--ease-out); transition: background-color var(--duration-base) var(--ease-out);
} }
/* L'azione al centro: e' una casella della barra come le altre, alla stessa
altezza, solo piena d'accento. L'unico accento della schermata sta sul solo
comando che crea qualcosa. */
.navbar__action {
flex: 0 0 auto;
display: flex;
align-items: center;
justify-content: center;
gap: var(--space-6);
min-width: var(--touch-min);
padding-inline: var(--space-md);
border: none;
border-radius: 20px;
background-color: var(--primary);
color: var(--on-primary);
font-family: inherit;
font-size: var(--label-lg-size);
font-weight: 600;
white-space: nowrap;
transition: transform var(--duration-fast) var(--ease-out),
background-color var(--duration-fast) var(--ease-out);
}
.navbar__action i { font-size: 22px; }
.navbar__action:active { transform: scale(0.96); background-color: var(--primary-pressed); }
/* Senza rete l'ispezione non si puo' aprire: il tasto lo dice stando spento,
non sparendo. Una casella che scompare sposta le altre due sotto il dito. */
.navbar__action:disabled {
background-color: var(--surface-muted);
color: var(--ink-muted);
}
@media (prefers-reduced-motion: reduce) {
.navbar__action:active { transform: none; }
}
.navbar__label { .navbar__label {
font-size: var(--label-md-size); font-size: var(--label-md-size);
line-height: 1.2; line-height: 1.2;
@@ -596,6 +640,15 @@
.navbar__item { flex: 0 0 auto; padding-block: var(--space-xs); } .navbar__item { flex: 0 0 auto; padding-block: var(--space-xs); }
/* In colonna l'azione va in cima: e' il primo comando della barra, non
qualcosa incastrato fra due destinazioni. */
.navbar__action {
order: -1;
align-self: stretch;
height: 48px;
margin-bottom: var(--space-xs);
}
.app-shell { padding-left: calc(96px + var(--space-lg)); } .app-shell { padding-left: calc(96px + var(--space-lg)); }
.page-body { padding-bottom: calc(var(--space-lg) + env(safe-area-inset-bottom, 0px)); } .page-body { padding-bottom: calc(var(--space-lg) + env(safe-area-inset-bottom, 0px)); }
} }