shadcn.io is not affiliated with official shadcn/ui
Contact Picker Dialog Block for Vue
A compose window with its recipient picker shown open: a search over fourteen contacts, four recent ones as toggles, an alphabetical list with pinned letters, a To panel of removable chips, and an Add recipients button that fills the message's To line.
Installation
Overview
Choose who a message goes to without leaving it, with this recipient picker for Vue with Ark UI. It opens from a message being written in Postbag, an email client: the page's header holds an outline Choose recipients Button, and the compose window itself, a title bar, a To line, a subject and a body, is drawn under a soft scrim inside a hairline frame with a plus mark on each corner. The Dialog is a split. At the start are a search Input that narrows fourteen contacts by name and address, four recent contacts as toggle chips, and the whole address book in alphabetical groups with each letter pinned as you scroll, every row a checkbox with initials, a name and an address. At the end a "To (3)" panel lists the chosen people as chips you can remove, with a Clear button. Add 3 recipients, the only Shine Button, counts as you choose and puts the chips on the drawn To line. For preview the dialog is drawn open in place when the page loads; the Choose recipients button opens the real Dialog, with the same choice. For mail, sharing and message tools, and anywhere a person is picked from a book.
FAQ
Why is the dialog already open, and what do I keep?
So you can see it without a click. On load the dialog is a copy drawn in place over the compose window: a native dialog element that isn't modal, so nothing is trapped, hidden or focused, and the page around the preview keeps working. The Choose recipients button opens the real Dialog, modal, with its focus trap, Escape and focus return, and the copy stays closed until a reload. Both show the same PickerFields component and share the block's state, so what you chose in the copy is there in the real one. In your app keep the Dialog and drop the copy: the "in-place" state, the scrim and the dialog element inside the frame.
How does the search work?
It folds case and accents on both sides, so "elif" and "Élif" find the same row, and it matches the name or the address. Groups with no match disappear and the pinned letters follow. When nothing matches, the list shows an empty state that names what you typed and has a Clear search button. The box keeps its height, so the chosen panel beside it never moves. A polite count ("2 contacts match, 3 chosen.") is said 600 ms after you stop typing, never on every key. In your app call your own search and keep the same rows.
How do I choose and remove people?
Each contact is a native checkbox inside a label, so Space toggles the focused row and pressing anywhere on it works too. The four recent chips toggle the same people, and are pressed when they are chosen. The To panel lists the chosen people in the order you picked them, each with a Remove button named for the person, and Clear takes them all off. After a removal focus moves to the next Remove button, or to the search when none is left. Add recipients with nobody chosen says so under the lists and puts focus in the search.
What stays local in the demo, and how do I wire it up?
All of it. The fourteen contacts and the four recent ones are typed constants, and Add recipients only puts the chosen people on the drawn To line and says in a polite message that nothing was sent. Cancel and Escape put the choice back to what the message already has. In your app load the book from your API, keep the choice in picked, and on add write the addresses into your message's To field.
Is it accessible from the keyboard?
Yes. The real dialog is named by its title and described by its sentence, focus starts in the search and stays inside until it closes, and Escape, Cancel and the close button return focus to Choose recipients. The trigger is the button wrapped in DialogTrigger with asChild, and the dialog is controlled with open and onOpenChange. The copy shown on load moves no focus; Escape inside it, Cancel and its close button close it and put focus on the button.
How does it look on a phone?
The copy shown on load docks to the foot of the page as a bottom sheet, with Add recipients pinned under the body. The To panel becomes a single row of chips under the list that scrolls sideways, so the list keeps its height. The real dialog keeps the Dialog's own phone layout: a sheet stuck to the bottom of the screen.