shadcn.io is not affiliated with official shadcn/ui
Share Receipt Dialog Block for React
A payments app's Payment sent page with a dialog shown open that keeps the receipt three honest ways: a paper receipt drawn in mono type, Copy as text, Save .txt and Print, each doing what the browser really does.
Installation
Overview
Let people keep proof of a payment, with this share-receipt dialog for React with Base UI. It opens from the Payment sent page of Slipfold, a tipping and payments app: the page's header holds an outline Share receipt Button, and the page itself, a check, the amount, a few lines and a recent payments list, is drawn under a soft scrim inside a hairline frame with a plus mark on each corner. The dialog shows the receipt as a paper slip in mono type with a torn lower edge, four rows: amount, who it went to, the date and a reference. Under it come three actions that really do something. Copy as text, the only Shine Button, puts the receipt on the clipboard as five plain lines, Save .txt is a download trigger that hands the browser a real file of those lines, and Print opens the print dialog for the receipt alone. Each says what it did on its own button or in a polite status message, and the drawn page behind notes how the receipt was kept. There are no social buttons: nothing is posted anywhere. For preview the dialog is drawn open in place when the page loads; the header button opens the real Dialog, with the same receipt. For payments, tipping and invoicing apps and any screen after money changed hands.
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 page: a native dialog element that isn't modal, so nothing is trapped, hidden or focused, and the page around the preview keeps working. The Share receipt 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 Keepsake component and share the block's state, so what you did in the copy shows 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.
Where does the receipt come from, and what is copied?
From one typed constant, PAYMENT: the amount in whole cents, the payee, an ISO date and a reference. The paper slip, the five lines that are copied and saved (RECEIPT_TEXT) and the text that is printed are all built from it, so they cannot disagree. The date is formatted with Intl.DateTimeFormat in UTC, the amount with Intl.NumberFormat, and the torn lower edge is a CSS mask, not an image. Pass your own payment and the whole dialog follows.
What do the three actions really do?
Copy as text calls navigator.clipboard.writeText with the five lines and its button reads Copied for a moment. Save .txt gives the browser a plain-text file named receipt-SF-8841.txt to download. Print builds a hidden frame that holds only the five lines and opens the browser's print dialog for it, so the page behind the dialog is never printed; the frame is removed a moment later. The browser decides what happens next: where a download goes, which printer, whether you cancel. The message says what was asked of it, not that it finished.
What if the browser refuses to copy?
Browsers only allow clipboard writes on a secure page, and some embedded views refuse them. When that happens the copy button keeps its label and a message with an icon appears in the slot under the three actions, which is always there, so nothing moves: the browser refused the copy, and the receipt text can be selected by hand instead. It speaks once, as an alert, and the other two actions keep working.
Is anything shared or sent, and how do I wire it up?
No. Nothing is posted, mailed or uploaded, and the payment is example data; the drawn page behind the dialog only notes which of the three you used. In your app build the receipt text, or a PDF, on your server so it is the record and not a copy of the screen, keep the three actions as they are, and add a mail link or a share link with your own address on the end if you want one. Do not build share links to social networks from a receipt: it holds a person's payment.
Is it accessible from the keyboard?
Yes. The real dialog is named by its title and described by its sentence, focus starts on Copy as text and stays inside until it closes, and Escape and the close button return focus to Share receipt. The trigger is passed to DialogTrigger through render, and the dialog is controlled with open and onOpenChange. The receipt is a list of terms and values a screen reader reads in order, the three actions are ordinary buttons, and one polite status message says what each did. The copy shown on load moves no focus; Escape inside it and its close button close it and put focus on the header button.
Related
- Pro
Success Dialog
Status and errorsAn invoice page with its payment confirmation shown open: a dialog with a drawn check, a receipt list with a copy button for the reference, and one button that settles the invoice.
- Pro
Receipt Slip Awards
Themed formatsAwards printed as a thermal receipt: a torn-edge slip with mono line items, dotted leaders, a derived subtotal, a verified stamp and a drawn barcode.
- Pro
API Key Dialog
Account and securityA keys page with its create-key dialog shown open: a name, scopes and an expiry in the first step, then the secret shown once, masked behind an eye button, with a Copy button and a box that has to be ticked before Done.
- Pro
Contact Card Dialog
Share and inviteA team directory with a contact card shown open that pages through six people: initials avatar, role, email, phone, desk and the time where they are, copy buttons, real mailto and tel links, and a Save contact button that downloads a vCard.
- Pro
Invite Members Dialog
Share and inviteA household calendar's Members page with its invite dialog shown open: one email field that knows the household's domain, so an outside address joins as a guest, the people who already have access, and a Send invite button that adds a pending row.
- Pro
Invite Team Dialog
Share and inviteA crew page with its bulk invite dialog shown open: a pasted list becomes one chip per address with bad ones flagged, one role for the batch, a seat meter that refuses a batch that does not fit, and a Pending tab with Resend, Revoke and Undo.