KQ-1708 · WORKSPACE/LOBBY
Workspace Lobby
The screen after sign-in: your workspaces as tiles with role, people and last visit, invitations in their own row, and a way to create one. Choosing a tile grows its real box from the tile's measured place to fill the panel on springs.glide while every other tile steps back on the same spring, the workspace's spaces cascade in under a loading line, and the answer resolves to a check drawn on springs.flick — or folds the tile back with the reason; a single accent plate travels between tiles on springs.snap. The tiles are a listbox: arrows walk them by their rendered rows, letters jump by name, Enter opens and Escape folds back.
Choose a workspace
3 workspaces · 2 invitations
3 workspaces · Coldbrook selected
Install
One command — the source lands in your repo. Or copy it from the Code tab.
$ pnpm dlx shadcn@latest add @kinetiq/workspace-lobby
Props
- workspaces
- LobbyWorkspace[]
- default defaultLobbyWorkspaces
- The workspaces you belong to: { id, name, handle?, members, people?, role, lastVisit?, unread?, tint?, spaces? }.
- invitations
- LobbyInvitation[]
- default defaultLobbyInvitations
- Invitations waiting for an answer: { id, name, handle?, invitedBy, role, members, tint?, sentAt? }.
- account
- { name, email }
- default defaultLobbyAccount
- Who is signed in, shown in the header.
- now
- number
- default defaultLobbyNow
- The clock (epoch ms) every last visit and invitation age is measured from.
- value / defaultValue
- string | null
- default — / first workspace
- The active tile — the option the arrows are on — controlled or initial.
- onValueChange
- (id) => void
- default —
- Fires from the arrow, type-ahead or press that moved the active tile.
- onSelect
- (workspace) => void | Promise<void>
- default —
- A workspace was chosen. A promise holds the expanded tile while it loads; a rejection folds it back and shows the message on the tile.
- onCancel
- (workspace) => void
- default —
- Cancel, Switch or Escape folded the open workspace back into its tile.
- onCreate
- () => void
- default —
- Create a workspace was pressed (the dashed row, or the empty state's button).
- onJoin
- (invitation) => void | Promise<void>
- default —
- Join was pressed. While a promise runs the card reads Joining…; when the host moves it into workspaces the tile lands from the card's place.
- onDecline
- (invitation) => void
- default —
- Decline was pressed on an invitation.
- onSwitchAccount
- () => void
- default —
- Switch account was pressed in the header.
- status
- "ready" | "loading" | "error"
- default "ready"
- Skeleton tiles while loading; a message and Try again on error.
- onRetry
- () => void
- default —
- Try again was pressed after loading failed.
- tileStyle
- "card" | "row" | "poster"
- default "card"
- A card, a compact row, or a poster with a seeded contour band.
- columns
- number
- default 3
- Tile columns at wide widths, 1 to 4; two at most below 44rem and one below 30rem of container.
- accent
- string
- default "var(--accent-bright)"
- The selection plate, the loading line, unread dots and Join, any CSS colour.
- title
- string
- default "Choose a workspace"
- The heading that names the listbox.
- maxHeight
- number
- default 560
- Tallest the panel grows, in px, before its body scrolls.
- disabled
- boolean
- default false
- Nothing can be opened, joined or created; the tiles stay readable and reachable.
- className
- string
- default —
- Classes for the wrapper.
| Prop | Type | Default | Description |
|---|---|---|---|
| workspaces | LobbyWorkspace[] | defaultLobbyWorkspaces | The workspaces you belong to: { id, name, handle?, members, people?, role, lastVisit?, unread?, tint?, spaces? }. |
| invitations | LobbyInvitation[] | defaultLobbyInvitations | Invitations waiting for an answer: { id, name, handle?, invitedBy, role, members, tint?, sentAt? }. |
| account | { name, email } | defaultLobbyAccount | Who is signed in, shown in the header. |
| now | number | defaultLobbyNow | The clock (epoch ms) every last visit and invitation age is measured from. |
| value / defaultValue | string | null | — / first workspace | The active tile — the option the arrows are on — controlled or initial. |
| onValueChange | (id) => void | — | Fires from the arrow, type-ahead or press that moved the active tile. |
| onSelect | (workspace) => void | Promise<void> | — | A workspace was chosen. A promise holds the expanded tile while it loads; a rejection folds it back and shows the message on the tile. |
| onCancel | (workspace) => void | — | Cancel, Switch or Escape folded the open workspace back into its tile. |
| onCreate | () => void | — | Create a workspace was pressed (the dashed row, or the empty state's button). |
| onJoin | (invitation) => void | Promise<void> | — | Join was pressed. While a promise runs the card reads Joining…; when the host moves it into workspaces the tile lands from the card's place. |
| onDecline | (invitation) => void | — | Decline was pressed on an invitation. |
| onSwitchAccount | () => void | — | Switch account was pressed in the header. |
| status | "ready" | "loading" | "error" | "ready" | Skeleton tiles while loading; a message and Try again on error. |
| onRetry | () => void | — | Try again was pressed after loading failed. |
| tileStyle | "card" | "row" | "poster" | "card" | A card, a compact row, or a poster with a seeded contour band. |
| columns | number | 3 | Tile columns at wide widths, 1 to 4; two at most below 44rem and one below 30rem of container. |
| accent | string | "var(--accent-bright)" | The selection plate, the loading line, unread dots and Join, any CSS colour. |
| title | string | "Choose a workspace" | The heading that names the listbox. |
| maxHeight | number | 560 | Tallest the panel grows, in px, before its body scrolls. |
| disabled | boolean | false | Nothing can be opened, joined or created; the tiles stay readable and reachable. |
| className | string | — | Classes for the wrapper. |