ComponentsNavigation

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.

OrderStageBulbs
Queen of Night tulipsGrowing1,200
Apeldoorn tulipsPlanting800
Tête-à-tête daffodilsOrdered450
Pink Impression tulipsReady600
Blue Spike muscariGrowing950

Usage

Views of a tableTSX
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. icon is an icon component that takes size, such as one from reicon-react.
  • value and onValueChange: the active view's id. 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 in views show a check. Choosing one still calls onAdd; switch to that view there, as the example does.
  • onClose: called by a tab's close control or the Delete key. Only views markedclosable can close, and only when you pass onClose. Remove the view fromviews, and pick a new value if the active view closed.
  • actions: controls at the end of the bar, such as a settings button.
  • panelId: the id of your panel. Every tab points to it with aria-controls. Label the panel with viewSwitcherTabId(panelId, value).
  • label and addLabel: 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.

OrderStageBulbs
Queen of Night tulipsGrowing1,200
Apeldoorn tulipsPlanting800
Tête-à-tête daffodilsOrdered450
Pink Impression tulipsReady600
Blue Spike muscariGrowing950

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.

OrderStageBulbs
Queen of Night tulipsGrowing1,200
Apeldoorn tulipsPlanting800
Tête-à-tête daffodilsOrdered450
Pink Impression tulipsReady600
Blue Spike muscariGrowing950

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.