Share
The share sheet — a bottom sheet on a phone, a centred dialog on a desktop, built on Dialog so the focus trap, the drag-to-dismiss and the morphing height all come for free.
Walkthrough
1. Basic
Three fields and a binding. The preview, the copyable link, the QR code and seven working destinations are all derived from them.
<script lang="ts"> import { Button, Share } from 'omaris'; let open = $state(false);</script><Button onclick={() => (open = true)}>Share this page</Button><Share bind:open url="https://omaris.dev/docs/components/share" title="Share" text="A bottom sheet on a phone, a dialog on a desktop, and nothing to wire."/> 2. People
people puts a row of faces above the apps, each badged with the channel it would send over. Someone with nothing to send to stays on the rail, unpressable, rather than disappearing from it.
<script lang="ts"> import { Button, Share, shareGlyphs, type ShareTarget } from 'omaris'; let open = $state(false); /** Who it went to. The row is the record, so nothing else announces it. */ let sentTo = $state<string | null>(null); const people: ShareTarget[] = [ { id: 'rania', label: 'Rania', detail: 'WhatsApp', tint: '#25d366', glyph: shareGlyphs.bubble, onselect: () => { sentTo = 'Rania'; } }, { id: 'dilan', label: 'Dilan', detail: 'Telegram', tint: '#2aabee', glyph: shareGlyphs.plane, onselect: () => { sentTo = 'Dilan'; } }, { id: 'karwan', label: 'Karwan', detail: 'Email', glyph: shareGlyphs.mail, onselect: () => { sentTo = 'Karwan'; } }, { id: 'hemin', label: 'Hemin', detail: 'Offline', disabled: true } ];</script><Button variant="tonal" onclick={() => (open = true)}>Send the report</Button>{#if sentTo} <p class="mt-3 text-body-sm text-muted-foreground">Sent to {sentTo}.</p>{/if}<Share bind:open url="https://omaris.dev/reports/q3" title="Q3 revenue" text="Up 14% on Q2, with Erbil carrying most of it." {people} targets={['whatsapp', 'telegram', 'email']}/> 3. Your own targets
targets takes built-in names and your own objects in the same array, and actions are the rows underneath it. Revoking the link is the one press whose result would otherwise be invisible, so the row says it — and the link bar goes.
<script lang="ts"> import { Button, Share, shareGlyphs, shareTargets, type ShareTarget } from 'omaris'; let open = $state(false); let revoked = $state(false); const intranet: ShareTarget = { id: 'intranet', label: 'Intranet', tint: 'var(--primary)', ink: 'var(--primary-foreground)', glyph: shareGlyphs.globe, href: (data) => `https://intranet.example.com/post?url=${encodeURIComponent(data.url ?? '')}` };</script><Button variant="tonal" onclick={() => (open = true)}>Send invoice</Button><Share bind:open heading="Send invoice" url="https://omaris.dev/invoices/INV-2291" title="Invoice INV-2291" text={revoked ? 'The link has been revoked.' : 'Due 30 September.'} link={!revoked} qr={!revoked} targets={['whatsapp', 'email', shareTargets.telegram, intranet]} actions={[ { id: 'pdf', label: 'Download PDF', detail: '184 KB', glyph: shareGlyphs.download }, { id: 'revoke', label: revoked ? 'Issue a new link' : 'Revoke this link', // Re-issuing brings the link bar back, which says it by itself. done: revoked ? undefined : 'Link revoked', glyph: shareGlyphs.cross, destructive: !revoked, onselect: () => { revoked = !revoked; } } ]}/> 4. Overridden
The overridden case: a wider dialog, density="compact", no destination rail at all, and a consumer's classes on three separate parts. class still covers the dialog itself.
<script lang="ts"> import { Button, Share, shareGlyphs } from 'omaris'; let open = $state(false);</script><Button variant="outlined" onclick={() => (open = true)}>Share a snapshot</Button><Share bind:open heading="Snapshot" url="https://omaris.dev/dashboards/fleet" title="Fleet dashboard" text="Live, and shared read-only." size="lg" density="compact" targets={false} class="max-w-xl" classes={{ thumb: 'rounded-full bg-tertiary-container text-tertiary-container-foreground', link: 'bg-primary-container', actions: 'bg-transparent ring-1 ring-border' }} actions={[ { id: 'png', label: 'Save as PNG', detail: '1200 × 630', glyph: shareGlyphs.download }, { id: 'open', label: 'Open in a new tab', glyph: shareGlyphs.globe } ]}/>