shadcn.io is not affiliated with official shadcn/ui
Access Control CRUD Block for React Aria
A document tool's roles as disclosure rows of people, with a select that moves a person to another role, a Popover that adds someone who holds no role, a role copy, and a delete that waits until the role is empty.
Installation
Overview
Keep the roles of a document tool and the people who hold them in one place with this access control manager for React with React Aria. A hairline frame with a plus mark on each corner holds five roles as Collapsible rows in one frame of fixed height that scrolls inside itself, and the header's New role is the one Shine Button. A role opens to its name, an Editable, its members with a Native Select each that moves the person to another role, an Add a person Popover over the people who hold no role yet, Duplicate role and Delete role. Moving someone changes both counts and says it in the polite line, "Maya Roy moved from Editor to Viewer", beside an Undo. Duplicate role copies a name as "Editor (copy)" with no people so you can rename it. A role with people can't be deleted: the row says "Move its 4 people first" and a select moves them all, while an empty role deletes at once. Owner is fixed and always keeps one person. For workspace admin, document sharing and any place where a role is a list of people rather than a grid of permissions.
FAQ
How do I fill it with my roles and people?
Edit ROLES and PEOPLE. A role has an id, a name and a locked flag (Owner is the only locked one); a person has an id, a name and the role id they hold, or an empty string when they hold none yet. The counts, the band's "5 roles, 2 without one" and the Add a person list are all derived from those two lists, so nothing is typed twice. New roles get their ids from a counter in state.
What happens when I move a person, and what is refused?
The person leaves one role and joins the other, both counts change, the polite line names the move and focus stays on that person's select in its new place. The only refusal is the last Owner: Latchby, the demo product, keeps at least one, so the select snaps back and the line says to move another person in first.
Why can't I delete a role?
A role with people holds them: its Delete button is aria-disabled, a line under it says "Move its 4 people first", and a select, "Move everyone to", moves them all in one step. Once the role is empty Delete removes it and the foot offers Undo. Owner has no Delete at all. Press an aria-disabled Delete and the polite line says why.
What does Undo restore?
The last change only, whatever it was: a move, an add, a rename, a copy or a delete. The block keeps the whole book as it was before that change and puts it back, so a restored role comes back with its people. A newer change replaces the older Undo, and nothing times out.
What stays local, and how do I wire it to a real API?
Everything lives in React state in this tab and a reload brings the original roles back; nothing is sent. In your app call your API in move, moveAll, add, commit, copy and remove, update the lists from the response, and show a refusal from the server in the polite line. Keep Undo as an optimistic step that calls your restore endpoint.
How does it work with a keyboard and on a phone?
Every role is a button that opens its row, the selects are native, the Popover lists the people as buttons, and each change moves focus to the thing that changed. On a phone the rows stay one column wide, the members keep their select beside the name, and the frame still scrolls inside itself, so the page does not grow.