shadcn.io is not affiliated with official shadcn/ui
Edit User Sheet CRUD Block for Vue
A rowing club's member list with an Edit button on each row that opens a side sheet for name, email, role and status: a changed email says it is unverified, Suspended asks a reason and an end date, and Save updates the row with an Undo.
Installation
Overview
Let an admin change a member and see what the change does, with this edit user sheet for Vue with Ark UI. It starts from the Members page of Memberhurst, a rowing club's membership desk: five real rows, each with its name, email, role, a status Badge and an outline Edit Button, under a soft scrim inside a hairline frame with a plus mark on each corner. The panel is a Sheet from the end edge, whichever way the page reads. It holds a name and an email Input (change the email and the field says "Unverified until they confirm"), a role Select and a status Segment Group: Suspended asks for a reason and an end date after the as-of day, and Deactivated says "Their sign-in ends; their work stays". Save changes, the only Shine Button, waits until something changed, then updates the row behind the sheet; the foot of the list says "Saved Ines Okafor in this demo." beside an Undo. Closing with unsaved changes turns the sheet's foot into "Discard changes?" instead of opening a second dialog. For the first screen the sheet is drawn open in place; a row's Edit button opens the real Sheet. For membership desks, admin consoles and any list whose edits have side effects.
FAQ
Why is the sheet already open, and what do I keep?
So you can see it without a click. On load the sheet is a copy drawn in place over the member list: a native dialog element that isn't modal, so nothing is trapped, hidden or focused. A row's Edit button opens the real Sheet, modal, with its focus trap, Escape and focus return to that row, and the copy stays closed until a reload. Both show the same MemberFields component and share the block's state. In your app keep the Sheet and drop the copy: the "in-place" state, the scrim and the dialog element in the frame.
What do the statuses ask for?
Active asks for nothing. Suspended shows a reason select (unpaid fees, safety review or the member's request) and an end date that must be after the as-of day, 14 October 2026; the date is the browser's own date field, so it works the same on every target and in every language. Deactivated shows one sentence that says their sign-in ends and their work stays. Switching back and forth keeps what you typed.
What does changing the email do?
Once the new address looks valid, a line under the field says "Unverified until they confirm". Saving writes the address and marks the member unverified, and the row behind the sheet shows "(unverified)" after the address until your server confirms it. A name can't be empty and the email must have the shape of an address; a failed save lists the fields and focuses the first.
What happens if I close the sheet with unsaved changes?
The foot swaps in place from Cancel and Save changes to "Discard changes?" with Keep editing, which takes focus, and Discard. Close, Cancel and Escape all do this (and so does a click on the scrim of the real sheet), so a stray keypress never loses work and no second dialog stacks on the first. With nothing changed they close at once.
What stays local, and how do I wire a real save?
The five members are typed in MEMBERS and saved into state; a reload restores them and nothing is sent. In your app post the draft in save, show the server's refusal on the field it names, and update the row from the response. Undo here puts the old values back in the list; against a server it should call a restore endpoint.
Is it accessible from the keyboard?
Yes. The real sheet is named by its title and described by its sentence, focus starts in the Name field and stays inside until it closes, and Escape returns focus to the row's Edit button. Each field has a label and a reason slot that is always rendered, and the status group is named. The copy shown on load moves no focus; Escape inside it and its Close button behave like the real sheet's.