Permission Menu
An access level on a sharing row — levels with one-line descriptions and a check, optional Remove access, and read-only text.
- Status
- Since
0.23.112- Accessibility pattern
- menu button; radio items with descriptions; Esc closes
Last updated
Install
Add Permission Menu from the VegaStack registry. The CLI verifies the item's integrity hash before writing it.
pnpm dlx shadcn@latest add @vegastack/permission-menuThe same command installs the registry items it composes: @vegastack/button, @vegastack/dropdown-menu, @vegastack/item.
Usage
import { PermissionMenu } from "@/components/ui/permission-menu";
<PermissionMenu
value={entry.level}
options={LEVELS}
onValueChange={(level) => setLevel(entry.id, level)}
onRemove={() => removeAccess(entry.id)}
aria-label={`${entry.name}'s access: ${label}`}
/>;The last control on a sharing row. The trigger is a ghost button reading the current level and a
chevron; the menu lists each level with a one-line muted description and a check on the current
one. With onRemove, a separator and a destructive "Remove access" end the menu. A built-in row —
the creator, an assignee, a manager — is readOnly: the level as plain muted text, no menu.
Scope
- Owns: the trigger, the level list with descriptions and the check, Remove access, the read-only text.
- Does not own: what a level means or saving it —
onValueChangeandonRemoveare the host's. - Compose with:
PersonAvatarandPeopleInputin a share dialog (share-01).
Examples
Plain levels
Without descriptions the menu is a plain radio list; size="default" takes the 32px tier.
Read-only and disabled
readOnly for a built-in row; disabled while a change saves.
API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
options* | readonly PermissionMenuOption<Value>[] | — | The levels, in order — usually from most to least access. |
value* | Value | — | The current level. |
align | "center" | "end" | "start" | "end" | Which edge of the trigger the menu lines up with. |
aria-label | string | the level's label | The trigger's accessible name; include the visible level, e.g. "Priya's access: Can edit". |
className | string | — | Classes for the trigger (or the read-only text). |
disabled | boolean | false | Disable the trigger — a change is saving, say. |
onRemove | (() => void) | — | Adds a destructive last item that removes this row's access. Omit for no remove item. |
onValueChange | ((value: Value) => void) | — | Called with the chosen level. |
readOnly | boolean | false | Show the level as plain muted text, with no menu — for a built-in row such as the creator. |
removeLabel | string | "Remove access" | The remove item's label. |
size | "default" | "sm" | "sm" | The trigger's height tier. |
Data attributes and CSS variables on PermissionMenu
| Attribute | Values |
|---|---|
data-icon | "inline-end" |
data-permission-menu | "" |
data-permission-menu-remove | "" |
data-readonly | "" |
data-slot | "permission-menu" | "permission-menu-chevron" |
PermissionMenuOption
| Prop | Type | Default | Description |
|---|---|---|---|
label* | string | — | The level's name, shown on the trigger and in the menu. |
value* | Value | — | The level's value. |
description | string | — | One muted line under the label in the menu, such as "Edit, comment and share". |
disabled | boolean | false | Shown but not choosable. |
Accessibility
- The trigger is a menu button. In a list of rows every trigger reads "Can edit", so give each an
aria-labelthat names the row and keeps the visible level: "Priya's access: Can edit". - Levels are radio items (
menuitemradio,aria-checked), and each description is the item's accessible description rather than part of its name. - Read-only text is not focusable.
| Key | Action |
|---|---|
| Enter / Space | Open the menu; choose the level |
| ↑ / ↓ | Move between levels |
| Esc | Close without changing |
| Contract | States tested |
|---|---|
| Behaviour | closed, open, checked, described, remove, read-only, disabled |
| Accessibility | labeled, keyboard, browser-accessibility-test |
| Visual | default, hover, focus, open, read-only |