Skip to content
Component installs need the registry setup
VegaStack Design

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
stable
Since
0.23.112
Accessibility pattern
menu button; radio items with descriptions; Esc closes

Last updated

AI
Anand Iyeranand@acme.com

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-menu

The 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.

AI
Anand Iyeranand@acme.com

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 — onValueChange and onRemove are the host's.
  • Compose with: PersonAvatar and PeopleInput in 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.

Full access

API Reference

PropTypeDefaultDescription
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-labelstringthe level's labelThe trigger's accessible name; include the visible level, e.g. "Priya's access: Can edit".
classNamestring—Classes for the trigger (or the read-only text).
disabledbooleanfalseDisable 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.
readOnlybooleanfalseShow the level as plain muted text, with no menu — for a built-in row such as the creator.
removeLabelstring"Remove access"The remove item's label.
size"default" | "sm""sm"The trigger's height tier.

Data attributes and CSS variables on PermissionMenu

AttributeValues
data-icon"inline-end"
data-permission-menu""
data-permission-menu-remove""
data-readonly""
data-slot"permission-menu" | "permission-menu-chevron"

PermissionMenuOption

PropTypeDefaultDescription
label*string—The level's name, shown on the trigger and in the menu.
value*Value—The level's value.
descriptionstring—One muted line under the label in the menu, such as "Edit, comment and share".
disabledbooleanfalseShown 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-label that 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.
KeyAction
Enter / SpaceOpen the menu; choose the level
↑ / ↓Move between levels
EscClose without changing
ContractStates tested
Behaviourclosed, open, checked, described, remove, read-only, disabled
Accessibilitylabeled, keyboard, browser-accessibility-test
Visualdefault, hover, focus, open, read-only

Do / Don't

Do
Give each level a one-line description that says what it allows.
Don't
Explain levels in a tooltip on the trigger — the menu is where the choice is made.
Do
Show built-in access (creator, assignee, manager) as readOnly with the reason beside the name.
Don't
Offer Remove access on a row whose access comes from a role the dialog cannot change.

On this page