PresenceStack
Who is here now: a row of avatars in each person's colour.
SourcePackage @weft-js/warp
Preview
tsx
import { PresenceStack } from '@weft-js/warp'import type { Peer } from '@weft-js/warp'export function Overflow() { return ( <PresenceStack peers={PEERS} max={3} /> )}const PEOPLE = [ { id: 'mara', name: 'Mara Lindqvist', email: 'mara@weft.dev', role: 'Owner', team: 'Platform', initials: 'ML' }, { id: 'jun', name: 'Jun Tanaka', email: 'jun@weft.dev', role: 'Admin', team: 'Platform', initials: 'JT' }, { id: 'priya', name: 'Priya Raman', email: 'priya@weft.dev', role: 'Member', team: 'Web', initials: 'PR' }, { id: 'tomas', name: 'Tomás Oliveira', email: 'tomas@weft.dev', role: 'Member', team: 'Web', initials: 'TO' }, { id: 'lena', name: 'Lena Fischer', email: 'lena@weft.dev', role: 'Member', team: 'Data', initials: 'LF' }, { id: 'noor', name: 'Noor Haddad', email: 'noor@weft.dev', role: 'Viewer', team: 'Data', initials: 'NH' }, { id: 'sam', name: 'Sam Okafor', email: 'sam@weft.dev', role: 'Member', team: 'Mobile', initials: 'SO' }, { id: 'ines', name: 'Inés Vidal', email: 'ines@weft.dev', role: 'Viewer', team: 'Mobile', initials: 'IV' },]const PEERS: Peer[] = PEOPLE.map((p, i) => ({ id: p.id, name: p.name, color: i + 1 }))Installation
npm install @weft-js/warpUsage
tsx
import { PresenceStack } from '@weft-js/warp'<PresenceStack peers={PEERS} max={3} />People coming and going
Joins and leaves are announced as one summary.
tsx
import { state } from '@weft-js/core'import { Button, PresenceStack, Row, Stack } from '@weft-js/warp'import type { Peer } from '@weft-js/warp'export function Room() { const peers = state(PEERS.slice(0, 3)) const join = () => (peers.value = PEERS.slice(0, peers.value.length + 1)) const leave = () => (peers.value = peers.value.slice(0, -1)) return ( <Stack gap="related" align="center"> <PresenceStack peers={peers} max={4} /> <Row gap="tight"> <Button size="sm" emphasis="outline" tone="neutral" aria-disabled={peers.value.length === PEERS.length ? 'true' : undefined} onClick={join}>Someone joins</Button> <Button size="sm" emphasis="ghost" tone="neutral" aria-disabled={peers.value.length < 2 ? 'true' : undefined} onClick={leave}>Someone leaves</Button> </Row> </Stack> )}const PEOPLE = [ { id: 'mara', name: 'Mara Lindqvist', email: 'mara@weft.dev', role: 'Owner', team: 'Platform', initials: 'ML' }, { id: 'jun', name: 'Jun Tanaka', email: 'jun@weft.dev', role: 'Admin', team: 'Platform', initials: 'JT' }, { id: 'priya', name: 'Priya Raman', email: 'priya@weft.dev', role: 'Member', team: 'Web', initials: 'PR' }, { id: 'tomas', name: 'Tomás Oliveira', email: 'tomas@weft.dev', role: 'Member', team: 'Web', initials: 'TO' }, { id: 'lena', name: 'Lena Fischer', email: 'lena@weft.dev', role: 'Member', team: 'Data', initials: 'LF' }, { id: 'noor', name: 'Noor Haddad', email: 'noor@weft.dev', role: 'Viewer', team: 'Data', initials: 'NH' }, { id: 'sam', name: 'Sam Okafor', email: 'sam@weft.dev', role: 'Member', team: 'Mobile', initials: 'SO' }, { id: 'ines', name: 'Inés Vidal', email: 'ines@weft.dev', role: 'Viewer', team: 'Mobile', initials: 'IV' },]const PEERS: Peer[] = PEOPLE.map((p, i) => ({ id: p.id, name: p.name, color: i + 1 }))Accessibility
- This has no shortcuts of its own: controls inside it use Tab, Enter and Space as usual, and warp's parts keep their roles and labels.
API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
peers | Live<Peer[]> | — | Who is here. |
max | number | 4 | How many avatars before +n. |
following | string | null | — | The id of the person being followed. |
label | string | 'People here' | Names the group. |
| Event | Type | Default | Description |
|---|---|---|---|
onFollow | ((p: Peer | null) => void) | — | Turns each avatar into a follow toggle. |
Parts
Every element carries a stable name, so you can style it from your own CSS: [data-scope="avatar-stack"] [data-part="sr-only"]
sr-onlymorecontent