Permission Matrix
Roles against permissions, in a grid. Inherited grants are drawn differently from direct ones, because "why can this role still delete things" is the question a flat list of ticks cannot answer.
src/components/ui/permission-matrix.tsxAbout
From the component's own source, where it was written.
Roles against permissions, as a grid.
Inherited permissions render differently from directly granted ones. A role that gets billing.read because it implies billing.write is not the same as one granted it explicitly, and an admin who cannot tell them apart will revoke the wrong thing and be surprised when access remains.
Locked cells are shown, not hidden. A permission an admin cannot change still needs to be visible, or they will assume it is off and grant it elsewhere.
Install
npx astralyx-ui add permission-matrix
Usage
import { PermissionMatrix } from '@/components/ui/permission-matrix'
<PermissionMatrix permissions={permissions} roles={roles} onToggle={toggle} />Composer
Change the props and the source below follows.
| Permission | Ownerlocked | Admin | Member | Viewer |
|---|---|---|---|---|
| Records | ||||
| View records | ||||
| Edit records | ||||
| Delete recordsPermanent after 30 days. | ||||
| Export data | ||||
| Team | ||||
| Invite members | ||||
| Manage roles | ||||
| Account | ||||
| Manage billing | ||||
| Read audit log | ||||
<PermissionMatrix permissions={permissions} roles={roles} onToggle={toggle} />Examples
Roles against permissions
<PermissionMatrix permissions={permissions} roles={roles} />API
Generated from the source, so it cannot fall behind the code. One table per exported component, then hooks, functions and types.
PermissionMatrix
Extends Omit<ComponentProps<'div'>, 'onToggle'>.
| Prop | Type | Default | Description |
|---|---|---|---|
| permissions | Permission[] | required | { id, label, group?, description? }. Groups become labelled row sections. |
| roles | PermissionRole[] | required | { id, label, granted, inherited?, locked?, lockedPermissions? } — one column each. |
| onToggle | (roleId: string, permissionId: string, granted: boolean) => void | — | Omit for a read-only matrix, which is the right thing for a permissions reference page. |
| permissionHeader | ReactNode | 'Permission' | Heading over the permission column. |
| lockedLabel | ReactNode | 'locked' | Note under a role that cannot be edited. |
| inheritedLabel | string | 'Inherited' | — |
| inheritedTitle | string | 'Inherited from another permission' | Tooltip on an inherited grant. |
| lockedPermissionTitle | string | — | Tooltip on a cell locked by lockedPermissions, saying why it cannot be granted. |
| grantedLabel | string | 'Granted' | — |
| notGrantedLabel | string | 'Not granted' | — |
Types
Permission
Roles against permissions, as a grid.
| Field | Type | Description |
|---|---|---|
| id | string | — |
| label | ReactNode | — |
| group? | string | — |
| description? | ReactNode | — |
PermissionRole
| Field | Type | Description |
|---|---|---|
| id | string | — |
| label | ReactNode | — |
| granted | string[] | Directly granted permission ids. |
| inherited? | string[] | Held via implication. Rendered distinctly and not directly revocable, because revoking it here would silently do nothing. |
| locked? | boolean | For a role that cannot be edited at all — an Owner that must keep every permission. |
| lockedPermissions? | string[] | Permissions one role can never hold. Those cells are read-only while the rest of the column stays editable — a Viewer that must not be handed billing. |