Summary#
A record's tabs say WHICH part of it you are looking at; SubNav says which ONE of that part's things. It is a
vertical list of SubNavItems down the side of a tab's body — each with its label, an optional hint under it (a date,
an amount) and an optional count — and the chosen one is selected. Put the content beside it in a grid or a row.
class Bill { public string Number; public string Due; public string Total; }
List<Bill> Bills() {
return [
new Bill { Number = "F-2041", Due = "31 Oct", Total = "1,840.00" },
new Bill { Number = "F-2007", Due = "30 Sep", Total = "1,840.00" },
];
}
[Route("/client")] [Render(CSR)] [AllowAnonymous]
component ClientPage() {
string open = "F-2041";
action Show(string number) { open = number; }
render {
Stack(gap: 4, p: 6) {
Tabs { Tab("Overview"); Tab("Invoices", selected: true); }
Row(gap: 5, align: Align.Start) {
SubNav("Client invoices") {
foreach (var b in Bills()) {
SubNavItem(b.Number, selected: open == b.Number, onPress: () => Show(b.Number), hint: "Due " + b.Due);
}
}
Card("Invoice " + open) { Text(Bills().First(b => b.Number == open).Total); }
}
}
}
}Signature#
SubNav(string label) { SubNavItem(…); … }
SubNavItem(string label, bool selected = false, Action onPress = null, string hint = "", string count = "")Description#
When is this better than another row of tabs?#
When the places are DATA rather than a fixed set — a record's invoices, its contacts, a list of saved views — or when there are more than a handful. A second tab strip under the first reads as two levels of the same thing and runs out of width; a list down the side scales with the data and says its places in full.
What does it become on a phone?#
Below Tablet the list lays out as a row of chips that scrolls sideways, above the content — the same items, the same
selection, in the width a phone has.
How is it announced?#
As a tab list named by label, each item a tab that is selected or not — which is what it is. Give the SubNav a
label that differs from the tab that holds it ("Client invoices" inside the "Invoices" tab), so a scope naming one
does not match both.
How do I drive it in a test?#
Ui.Click("F-2007", within: "Client invoices"), then Assert.Selected("F-2007").
See also#
- Arrangements — five ways to build a whole app, and which look wears each — the document desk, whose record goes deeper this way
- Paper — showing the chosen item as the document it is
- Dock — the other way to show one item beside a list