Page Widgets
Four classes that produce more than a single HTML tag — they generate styled markup, scripts, or modal dialogs. Two of them (AlertDialog and PleaseWaitOverlay) are framework infrastructure you trigger indirectly; the other two (KpiCard and PostbackScroll) you add to the page directly.
KpiCard
Dashboard tile that shows a big value over a smaller label. Renders three nested divs (kpi-card / kpi-value / kpi-label) and pulls its styling from CoreStyles, so it works in any FlightPlan app without app-level CSS.
// Three KPI cards arranged in a row
var kpiRow = content.Add(Panel.Row().Fixed());
kpiRow.Add(new KpiCard("1,247", "Active Users")
.WithStyle(s => s.MarginRight("30px")));
kpiRow.Add(new KpiCard("$182K", "Revenue (Q3)")
.WithStyle(s => s.MarginRight("30px")));
kpiRow.Add(new KpiCard("98.2%", "Uptime"));
KpiCard works well as a child of a fixed-height Panel.Row at the top of a dashboard. See the Panel guide for layout patterns.
AlertDialog (via ShowAlert)
Modal dialog for showing user feedback after an action — "Saved successfully", "Cannot delete: record is in use", etc. You don't construct AlertDialog directly. Instead, call ShowAlert(message, type) from your page code; the framework adds the alert to BasePage.Alerts and renders an AlertDialog automatically if the list is non-empty when the page renders.
Multiple alerts queue up and display FIFO — the user clicks OK to advance through them.
// Inside HandleAction or BuildContent:
ShowAlert("Settings saved.", AlertType.Success);
ShowAlert("Email sent to 12 recipients.", AlertType.Info);
ShowAlert("3 records were skipped — see log for details.", AlertType.Warning);
ShowAlert("Database connection failed. Try again.", AlertType.Error);
The four AlertType values produce different colored icons:
In production, these render as a single centered modal dialog with a backdrop overlay. The mockup above shows just the icon + message portion of each type so you can see the color coding without an actual popup blocking this page.
PleaseWaitOverlay
Hidden full-screen overlay with a spinner that activates when the user clicks any element with a data-please-wait attribute. Useful for slow operations — report generation, big downloads, multi-step submits — where you want to prevent double-clicks and reassure the user something is happening.
BasePage injects PleaseWaitOverlay on every page automatically. You don't construct it. Trigger it by calling .WithPleaseWait("message") on a Button or LinkButton:
// On a submit button:
form.Add(new Button(ButtonType.Submit, "Generate Report")
.WithName("btnReport")
.WithPleaseWait("Building report — this can take a minute..."));
// On a download link:
content.Add(new LinkButton("Download Q3 Data", "/Reports/Q3.xlsx")
.WithPleaseWait("Preparing your download..."));
When the trigger is clicked, the overlay fades in (50ms after the form submit / navigation fires), darkens the page, disables all inputs/buttons/links, and shows the message under a spinning indicator. The overlay disappears automatically when the server's response arrives and the new page renders.
Cross-reference: the WithPleaseWait() option is documented on both Button and LinkButton in the Forms & Inputs guide.
PostbackScroll
A full-page postback reloads the document, which resets the scroll position to the top. On a long page — a multi-step wizard, a tall form — that means every Save or Next throws the user back to the top and they have to scroll down again. PostbackScroll is the opt-in fix: add it to a page and it renders a small inline script that restores a sensible scroll position after the reload. It produces no visible markup.
It is strictly opt-in, so it has zero blast radius — pages that do not add it behave exactly as before. There are two modes, exposed as factory methods:
// Wizard feel — after a postback, scroll the newest matching "step" into view.
// Pass the CSS selector that identifies your repeating step containers.
content.Add(PostbackScroll.ToNewest(".wizard-step"));
// General — preserve the exact scroll position across the postback and restore it.
content.Add(PostbackScroll.Restore());
ToNewest(selector) finds the last element matching the selector and scrolls it into view — ideal for a step-reveal wizard, where each postback adds the next step and you want the user looking at it. It is guarded so a fresh load (only one step) does not scroll past the page heading, and the browser clamps to the end of the document, so it never overscrolls into empty space. Restore() instead stashes the scroll position on the way out and puts the user back exactly where they were — better for a tall page that posts back in place.
Implementation detail worth preserving so nobody "simplifies" it back to a bug: Restore stashes the position on the beforeunload event, NOT the submit event. Auto-submitting controls (a dropdown or date picker with WithAutoSubmit) post via this.form.submit(), and a programmatic form.submit() does not fire the submit event — so a submit-based version would silently miss exactly those controls. beforeunload fires for every navigation away, including those auto-posts.
Placement: add it once, anywhere in the page's content. If you ever decide the behavior should be universal, it can be promoted to the base page — but that is a deliberate, separate change, since it would alter the scroll behavior of every existing page at once.