Skip to content
weftGitHub

MembersTable

Everyone in a workspace, with roles, bulk removal and pending invites.

SourcePackage @weft-js/warp

Preview

tsx
import { state } from '@weft-js/core'import { MembersTable, Toaster } from '@weft-js/warp'import type { Invite, Member } from '@weft-js/warp'import type { Role } from '@weft-js/warp'export function Team() {  const people = state<readonly Member[]>(TEAM_MEMBERS)  const invites = state<readonly Invite[]>(TEAM_INVITES)  return (    <>      <MembersTable        members={people} invites={invites} me="mara" roles={TEAM_ROLES} seats={12}        onRoleChange={(m, role) => (people.value = people.value.map(x => (x.id === m.id ? { ...x, role } : x)))}        onRemove={gone => { people.value = people.value.filter(x => !gone.includes(x)) }}        onRestore={back => (people.value = [...people.value, ...back])}        onInvite={(emails, role) => { invites.value = [...invites.value, ...emails.map(email => ({ id: email, email, role, sent: 'Just now' }))] }}        onRevoke={invite => (invites.value = invites.value.filter(x => x !== invite))}      />      <Toaster />    </>  )}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 TEAM_ROLES: Role[] = [{ value: 'owner', label: 'Owner', manages: true }, { value: 'admin', label: 'Admin', manages: true }, { value: 'member', label: 'Member' }, { value: 'viewer', label: 'Viewer' }]const LAST_SEEN = ['Active now', '12 min ago', '1 h ago', 'Today', 'Yesterday', 'Oct 28', 'Oct 24', 'Oct 12']const TEAM_MEMBERS: Member[] = PEOPLE.map((p, i) => ({ id: p.id, name: p.name, email: p.email, role: p.role.toLowerCase(), lastActive: LAST_SEEN[i] }))const TEAM_INVITES: Invite[] = [{ id: 'dara', email: 'dara@weft.dev', role: 'member', sent: '2 days ago' }, { id: 'kai', email: 'kai@contract.dev', role: 'viewer', sent: 'Oct 27' }]const min = 60_000

Installation

npm install @weft-js/warp

Usage

tsx
import { MembersTable } from '@weft-js/warp'

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

PropTypeDefaultDescription
membersreadonly Member[] | State<readonly Member[]>—Everyone in the workspace, one row each.
invitesreadonly Invite[] | State<readonly Invite[]>—Pending invitations, listed under the members.
mestring—The signed-in member, who gets a warning before demoting themselves.
rolesRole[]—The roles to offer.
ownerstring'owner'The value of the role that must always have someone in it.
seatsnumber—Seats on the plan. Pending invites use one each.
EventTypeDefaultDescription
onRoleChange(member: Member, role: string) => void—Called with the member and the role picked for them.
onRemove(members: Member[]) => void | Promise<void>—Called with the members to remove, after people confirm.
onRestore((members: Member[]) => void)—Undo for a removal.
onInvite(emails: string[], role: string) => void | Promise<void>—Called with the emails and role from the invite dialog.
onResend((invite: Invite) => void)—Called with the invite when Resend is pressed.
onRevoke((invite: Invite) => void)—Takes back a pending invite.

RelatedInvite dialogSeats meterRoles matrix