Summary#
You reuse a kit's pages and still decide where they stand and how they look. Design the shell once:
component DeskShellDesign() : WorkspaceShell {
render { AppShell(new AppChrome { Product = "Harbour", Nav = [new NavItem { Kind = NavKind.Section, Label = "Desk",
Children = Nav.Pages() }] }) { Slot; } }
}…and every kit page in [Layout(WorkspaceFrame)] — and every settings page, since SettingsFrame stands in the same
shell — is drawn inside it, with the kits' [Nav] pages listed beside your own.
Signature#
public abstract component WorkspaceShell(); // Osysharp.Ui — the frame; `Slot` is the page
component MyShell() : WorkspaceShell { render { … Slot; … } } // your design of it, once (the kit's own: nothing around)
[Layout] public component WorkspaceFrame(); // what a kit's working page names: your shell, the page inside
[Nav("Reports", Icon = Icons.Chart, Group = "Desk", Order = 40)] // on a [Route] with no route parameters
Nav.Pages() → NavItem[] // every [Nav] page the reader may open, in order
Nav.Pages("Desk") → NavItem[] // the ones whose Group is "Desk"
app.Nav = [ReportsPage, KnowledgeDeskPage]; // your order: these first, in this order, then the restDescription#
Where do a kit's pages stand?#
A kit page names [Layout(WorkspaceFrame)]: Osysharp.Ui's layout, which draws your design of WorkspaceShell with the
page inside, scrolling, across the width. Your design is a look contract implementation (Looks — a site's whole design, picked per version): it takes no
parameters and draws Slot where the page goes.
If your app has ONE layout its pages stand in, you need not write a design at all. With no : WorkspaceShell of
your own, the compiler writes one from that layout — the same component, its state and live reads included, with its
Outlet as the Slot — so a kit's page (the settings area, /settings/people) stands in your sidebar and account
menu exactly as your own pages do. An app with two layouts (a public one and a staff one) is not guessed for: write
the design, as below, and it is the one used. With no layout and no design, a kit's page is the page and nothing
around it.
How does a page get into the navigation?#
[Nav] on a page says its row's words (else its [Title], else its name in words), its icon, its group and its place —
Order, lower first, then the package order (a kit before the kits built on it, your own last), then the name. Your
shell asks Nav.Pages() and gets nav rows, written out at compile time: a reader the page's [Authorize] refuses is not
shown its row, and nothing is fetched to decide it. A page whose route takes a value cannot be a nav row — mark the page
a person starts from. Because the rows are written out, a [Render(SSR)] page that lists them is painted by the server
with its navigation in place.
How does the app put a kit's page somewhere else in the nav?#
Say the order in app.osy, naming the pages as code:
app.Nav = [ReportsPage, KnowledgeDeskPage];The pages it names lead the navigation in the order written; every other [Nav] page follows in its own order. A
kit's page keeps everything else the kit gave it — its words, its icon, its group, its [Authorize] — and stays the
kit's page: re-ordering it is not taking it over. Nav.Pages("Group") keeps the same order within the group. A name that
is no page in the navigation is refused, listing the ones there are; a page without leaves out cannot be placed.
How do you still decide?#
- Where they stand — your
WorkspaceShell, once; or name a different layout by taking a page over by name. - Where they are listed —
Nav.Pages("Group")lists one group, so you lay groups out yourself; a kit's page you do not want listed is left out withwithout KnowledgeDeskPage;on itsuseline, andapp.Nav = [ … ];puts the pages in your order (above). - How they look — your theme's tokens paint every kit page (theme tokens).
- One page your own way — a component of the same name as the kit's page takes it over, at its route or yours.
Examples#
component AppFrameDesign() : WorkspaceShell {
render {
Stack(gap: 0, w: "100%", minH: "100vh", bg: Colors.Bg) {
Row(gap: 4, p: 3, bg: Colors.Surface, label: "Top bar") {
Text("Larkspur", fontWeight: FontWeight.Semibold);
foreach (var n in Nav.Pages()) { TextLink(n.Label, to: n.To); }
}
Slot;
}
}
}
[Route("/inbox")] [Layout(WorkspaceFrame)] [Nav("Inbox", Icon = Icons.Inbox, Order = 10)]
component InboxPage() { render { Text("Everything waiting for you."); } }
[Route("/reports")] [Layout(WorkspaceFrame)] [Nav("Reports", Icon = Icons.Chart, Order = 40)]
component ReportsPage() { render { Text("How the week went."); } }[Route("/inbox")] [Nav("Inbox", Icon = Icons.Inbox, Order = 10)]
component InboxPage() {
render { Stack { foreach (var n in Nav.Pages()) { TextLink(n.Label, to: n.To); } Text("Everything waiting for you."); } }
}
[Route("/reports")] [Nav("Reports", Icon = Icons.Chart, Order = 40)]
component ReportsPage() { render { Text("How the week went."); } }
app.Nav = [ReportsPage, InboxPage];See also#
- [Slot] and contributions — a place on a page that other kits, and the app, add to — slots: a place on a kit's page that other kits and the app add to
- ISettingsProvider and [OrgPolicy]: kits announce their settings, and a setting says whether an organisation may govern it — the settings area, which stands in the same shell
- Looks — a site's whole design, picked per version — designing a kit's contract
- layout primitives — layouts and
Outlet