WorkspaceSwitcher

WorkspaceSwitcher opens a labelled disclosure of workspaces. Supply href on a workspace when changing tenant navigates to a URL, including a tenant and environment route. The workspace is then a native link that can be copied or opened in a new tab. Without href, it is a button; handle onChange and update current in application state.

Examples

Controlled current workspace

Pass current with onChange when application state owns the current workspace. Without current, the first workspace is shown.

Create action

onCreate adds a separate create button. createLabel names its action. defaultOpen is intended for visual demos.

Localization and navigation

labels is merged over English defaults. Supply listLabel, placeholder and a members(count) formatter for your locale, including plural forms. createLabel remains a separate prop.

tsx
<WorkspaceSwitcher
  current="acme"
  workspaces={[
    { id: 'acme', name: 'Acme', members: 1, href: '/acme/production' },
    { id: 'studio', name: 'Studio', members: 3, href: '/studio/staging' },
  ]}
  labels={{
    listLabel: 'Espaços de trabalho',
    placeholder: 'Escolha um espaço',
    members: (count) => `${count} membro${count === 1 ? '' : 's'}`,
  }}
  createLabel="Criar espaço"
/>

For Alpine, the application serves the visible text and link destinations. Use <a href="..." class="lyra-wssw__item" data-id="..." x-bind="option"> for navigation, or <button type="button" ...> for selection. Buttons dispatch lyra:change with the data-id; links use native navigation. Keep the current item marked aria-current="true" and update the trigger text when application state changes. Replace older role="listbox", role="option", aria-selected and aria-haspopup="listbox" markup with the disclosure pattern shown below.

Migration: @lyra-ds/react no longer renders role="listbox", role="option" or aria-selected; the panel now uses -popover ids and items are buttons or links marked with aria-current. Update tests, CSS and scripts that select items by those roles to use .lyra-wssw__item[data-id] and .lyra-wssw__pop, and read the current item from aria-current. Alpine still accepts the legacy markup, but it is deprecated.

Accessibility

The trigger is a button with aria-expanded and aria-controls; the popup is a labelled role="group". Each destination retains its native link or button semantics. Enter, Space and arrow keys open and focus the current workspace. Arrow keys, Home and End move focus among workspaces; Escape returns to the trigger. Tab follows native order, including the create button when present. The popup flips upward when needed.

API and code

NameTypeRequiredDescription
workspacesWorkspace[]—Workspaces available for selection.
currentstring—Identifier of the current workspace. Defaults to the first workspace.
onChange(id: string, workspace: Workspace) => void—Called when a workspace is selected.
onCreate() => void—When present, shows a create-workspace action in the popover.
createLabelstring—Create-action label. Default `"Create workspace"`.
labelsWorkspaceSwitcherLabels—Translatable text merged over English defaults.
defaultOpenboolean—Whether the popover starts open. Useful for demos.

x-data="lyraWorkspaceSwitcher({ … })"

OptionTypeRequiredDescription
defaultOpenboolean—Whether the workspace disclosure starts open. Default: `false`.
html
<div id="workspace-switcher" class="lyra-wssw" x-data="lyraWorkspaceSwitcher()">
  <button class="lyra-wssw__trigger" type="button" aria-controls="workspace-list" x-bind="trigger">
    Atlas
  </button>
  <div
    id="workspace-list"
    class="lyra-wssw__pop"
    role="group"
    aria-labelledby="workspace-label"
    style="display: none"
    x-bind="popover"
  >
    <span id="workspace-label" class="lyra-wssw__pop-label">Workspaces</span>
    <a
      class="lyra-wssw__item"
      href="/atlas/production"
      data-id="atlas"
      aria-current="true"
      x-bind="option"
      >Atlas</a
    >
    <a class="lyra-wssw__item" href="/studio/staging" data-id="studio" x-bind="option">Studio</a>
  </div>
</div>

<lyra:workspace-switcher> Generated from lyra-ds/blade v0.10.0.

The behavior comes from lyraWorkspaceSwitcher() — install @lyra-ds/alpine and see the HTML + Alpine tab.

PropDefaultRequiredExample values
workspaces[]——
currentnull——
createfalse——
createLabel'Create workspace'——
createId'create'——
defaultOpenfalse——
blade
<lyra:workspace-switcher
    current="acme"
    create
    create-label="Create workspace"
    :workspaces="[
        ['id' => 'acme', 'name' => 'Acme Inc.', 'plan' => 'Pro', 'members' => 24],
        ['id' => 'lyra', 'name' => 'Lyra Design', 'plan' => 'Starter', 'members' => 6],
    ]"
/>