Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Workspace switcher menu

Linear: AUT-125

Popover anchored to the rail's WorkspaceBadge. Pure composition of UI-03 menu primitives + UI-01 surface tokens — no bespoke CSS.

Open as live demo →

States covered

StateStory
Default — one selected workspaceworkspace-menu-default
Many workspacesworkspace-menu-many
Long names truncateworkspace-menu-long-names
No selectionworkspace-menu-no-selection

State lives outside the component

selected_id is a prop. The menu doesn't know which workspace is "current" — app-ui passes the active workspace id down and the menu compares against each row's id to render the ✓.

Composition

flowchart TD
    Menu[WorkspaceSwitcherMenu] --> Popover[PopoverSurface]
    Popover --> ListBody[MenuList]
    ListBody --> Group[MenuSection — Your workspaces]
    Group --> Row[MenuRow × N]
    Row --> Tile[span.icon-tile-workspace]
    Row --> Title[name]
    Row --> Sub[member count]
    Row --> Plan[Badge — Plan]
    ListBody --> Actions[MenuSection — Actions]
    Actions --> New[MenuRow — New workspace]
    Actions --> Settings[MenuRow — Workspace settings]
    Popover --> Footer[MenuFooter]

API

#![allow(unused)]
fn main() {
use ui_storybook::components::{WorkspaceSwitcherMenu, WorkspaceView};

view! {
    <WorkspaceSwitcherMenu
        workspaces=fixtures::workspaces::sample_workspace_views()
        selected_id="ws-northwind"
    />
}
}

Member count formatting

format_member_count(u32) -> String renders 1 member (singular) vs N members (plural). Unit-tested for both branches + zero.