View Switcher
The bar of a table's open views, such as Table, List, Board, Chart and Timeline. An “Add view” menu explains each view. Your app decides which views exist, which are open and which can close. The switcher shows exactly what you pass and calls you back.
Seed orders
Choose a tab or use the arrow keys. Add a view from the menu. Hover a tab, or focus it and press Delete, to close it. Table stays.
| Order | Stage | Bulbs |
|---|---|---|
| Queen of Night tulips | Growing | 1,200 |
| Apeldoorn tulips | Planting | 800 |
| Tête-à-tête daffodils | Ordered | 450 |
| Pink Impression tulips | Ready | 600 |
| Blue Spike muscari | Growing | 950 |
Usage
import { Grid } from "reicon-react/icons/Grid";
import { Kanban } from "reicon-react/icons/Kanban";
import { List } from "reicon-react/icons/List";
import { ViewSwitcher, viewSwitcherTabId } from "@tulipfarm/sprout/view-switcher";
const options = [
{ id: "table", label: "Table", icon: Grid, description: "Rows and columns. Edit, group and sum." },
{ id: "list", label: "List", icon: List, description: "One item per row for quick reading." },
{ id: "board", label: "Board", icon: Kanban, description: "Cards in lanes, one lane per stage.",
unavailable: hasSingleSelect ? undefined : "Needs a single-select field." },
];
<ViewSwitcher
views={open.map(id => ({ ...byId[id], closable: id !== "table" }))}
value={layout}
onValueChange={setLayout}
options={options}
onAdd={id => { if (!open.includes(id)) openView(id); setLayout(id); }}
onClose={closeView}
panelId="records"
actions={<Button variant="ghost" size="icon-sm" aria-label="View settings">…</Button>}
/>
<div role="tabpanel" id="records" aria-labelledby={viewSwitcherTabId("records", layout)} tabIndex={0}>
{layout === "board" ? <Board … /> : <Table … />}
</div>views:{ id, label, icon, closable? }[]. The tabs, in order.iconis an icon component that takessize, such as one fromreicon-react.valueandonValueChange: the active view'sid. Click and arrow keys callonValueChange.options:{ id, label, icon, description, unavailable? }[]. The views in the “Add view” menu. Leave it out to hide the menu.onAdd: called with the chosen option. Options that are already inviewsshow a check. Choosing one still callsonAdd; switch to that view there, as the example does.onClose: called by a tab's close control or the Delete key. Only views markedclosablecan close, and only when you passonClose. Remove the view fromviews, and pick a newvalueif the active view closed.actions: controls at the end of the bar, such as a settings button.panelId: theidof your panel. Every tab points to it witharia-controls. Label the panel withviewSwitcherTabId(panelId, value).labelandaddLabel: the tab list's name, “Views” by default, and the menu button's text, “Add view” by default.
Views that cannot be added
Set unavailable to say why an option cannot be added. The menu shows the reason in place of the description, dims the icon and does not let anyone choose it.
Greenhouse log
This table has no single-select field, so Board says what it needs and cannot be chosen.
| Order | Stage | Bulbs |
|---|---|---|
| Queen of Night tulips | Growing | 1,200 |
| Apeldoorn tulips | Planting | 800 |
| Tête-à-tête daffodils | Ordered | 450 |
| Pink Impression tulips | Ready | 600 |
| Blue Spike muscari | Growing | 950 |
Narrow screens
When the tabs do not fit, they scroll sideways and fade at each edge that hides more tabs. The active tab scrolls into view. “Add view” and actions stay at the end. Under 480px the button shows only its plus icon, and the menu uses one column.
Many views in a narrow space
The tabs scroll sideways with faded edges. The active tab stays in view and Add view stays put.
| Order | Stage | Bulbs |
|---|---|---|
| Queen of Night tulips | Growing | 1,200 |
| Apeldoorn tulips | Planting | 800 |
| Tête-à-tête daffodils | Ordered | 450 |
| Pink Impression tulips | Ready | 600 |
| Blue Spike muscari | Growing | 950 |
Accessibility
The bar is a tablist named by label. It is one tab stop. Left and Right move between tabs and choose them at once; Home and End go to the first and last tab. On a closable tab, Delete (or Backspace) calls onClose. When the tab closes, focus moves to the view your app chose, or else to a neighbour. The close control is for the pointer only, so it is hidden from screen readers; the tab announces the Delete shortcut.
The menu lists options in reading order, and the arrow keys move through them one by one. An unavailable option has aria-disabled and its reason as the description. An open option is described as “Already open”. Escape closes the menu and returns focus to “Add view”.
The underline slides to the active tab over 180ms. It does not move when reduced motion is requested.
Theme tokens
Tabs use --sprout-muted and, when active, --sprout-text with a--sprout-text underline over a --sprout-line rule. Hover uses --sprout-hover. The menu uses the Dropdown Menu surface; icon tiles use --sprout-hover and--sprout-radius-control. Motion uses --sprout-motion-normal,--sprout-motion-fast and --sprout-motion-ease.