import { ActionButton, Avatar as DesignSystemAvatar, Dialog as DesignSystemDialog, IconButton, Picker, Status, TextArea, TextField, } from "@agent-native/toolkit/design-system"; import { IconCheck, IconCode, IconCopy, IconLink, IconLock, IconMail, IconTrash, IconUsersGroup, IconWorld, } from "@tabler/icons-react"; import type { ReactNode } from "react"; import { cn } from "../utils.js"; import { useShareDialogController, type ResourceShare, type ShareDialogController, type ShareDialogPerson, type ShareDialogTab, type ShareVisibility, } from "./useShareDialogController.js"; export interface ShareDialogProps { open: boolean; onClose: () => void; resourceType: string; resourceId: string; resourceTitle?: string; /** * When provided, enables the "Link" tab with a copy-link field. * Pass the user-facing share URL (e.g. `https://…/share/`). */ shareUrl?: string; /** * When provided, enables the "Embed" tab with a default iframe snippet. * For richer per-resource controls (autoplay, start time, responsive / * fixed size), pass `embedTabContent` instead (or in addition) — it * replaces the default embed body. */ embedUrl?: string; /** Advanced: fully custom Embed tab body. Requires `embedUrl` to enable the tab. */ embedTabContent?: ReactNode; /** Extra content appended to the bottom of the Link tab (e.g. download buttons). */ linkTabExtras?: ReactNode; } const BUTTON_BASE = "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors active:!scale-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0"; const BUTTON_OUTLINE_SM = cn( BUTTON_BASE, "!h-9 !px-3 border border-input bg-background hover:bg-accent hover:text-accent-foreground", ); const BUTTON_GHOST_ICON = cn( BUTTON_BASE, "!h-8 !w-8 !p-0 text-muted-foreground hover:bg-accent hover:text-accent-foreground", ); const VIS_ICONS: Record = { private: IconLock, org: IconUsersGroup, public: IconWorld, }; const TAB_ICONS: Record = { link: IconLink, invite: IconMail, embed: IconCode, }; /** * Framework share dialog. Drop into any template via * ``. Passing `shareUrl` * lights up a Link tab with a copy field; passing `embedUrl` lights up an * Embed tab. With neither prop, renders a single Invite + general-access * panel (Google-Docs-lite). */ export function ShareDialog({ open, onClose, resourceType, resourceId, resourceTitle, shareUrl, embedUrl, embedTabContent, linkTabExtras, }: ShareDialogProps) { const controller = useShareDialogController({ open, onClose, resourceType, resourceId, resourceTitle, shareUrl, embedUrl, }); if (!open) return null; return ( {controller.title} } closeLabel={controller.labels.close} size="large" className="!top-4 !z-[2010] !block !max-h-none !w-[calc(100vw-2rem)] !max-w-lg !translate-y-0 !gap-0 !overflow-visible !rounded-xl !border-border !bg-popover !p-0 !text-popover-foreground !shadow-2xl sm:!top-1/2 sm:!-translate-y-1/2" aria-label={controller.title} >
{controller.ownerLabel ? (
{controller.ownerLabel}
) : null}
{controller.tabsEnabled ? (
{controller.tabs.map((tab) => { const Icon = TAB_ICONS[tab.value]; return ( controller.setActiveTab(tab.value)} icon={} label={tab.label} /> ); })}
) : null}
{controller.tabsEnabled && controller.activeTab === "link" ? ( ) : null} {!controller.tabsEnabled || controller.activeTab === "invite" ? ( ) : null} {controller.tabsEnabled && controller.activeTab === "embed" ? (embedTabContent ?? ) : null}
); } function TabTrigger({ active, onClick, icon, label, }: { active: boolean; onClick: () => void; icon: ReactNode; label: string; }) { return ( {icon} {label} ); } function LinkTab({ controller, extras, }: { controller: ShareDialogController; extras?: ReactNode; }) { const Icon = VIS_ICONS[controller.visibility.value]; return (
{controller.labels.generalAccess}
{controller.visibility.description}
{extras}
); } function InviteTab({ controller, showVisibility, }: { controller: ShareDialogController; showVisibility: boolean; }) { const Icon = VIS_ICONS[controller.visibility.value]; return (
{controller.canManage ? (
controller.invite.setEmail(event.currentTarget.value) } onKeyDown={(event) => { if (event.key === "Enter") controller.invite.submit(); }} autoComplete="off" className="flex-1 min-w-0 h-9 rounded-md border border-input bg-background px-3 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:ring-offset-background" />
{controller.invite.showNotifyPeople ? ( ) : null}
) : null}
{controller.labels.peopleWithAccess}
    {controller.people.map((person) => ( ))} {!controller.people.length ? (
  • {controller.labels.noAccess}
  • ) : null}
{showVisibility ? (
{controller.labels.generalAccess}
{controller.visibility.description}
) : null}
); } function PersonRow({ person, canManage, removeLabel, onRemove, }: { person: ShareDialogPerson; canManage: boolean; removeLabel: string; onRemove: (share: ResourceShare) => void; }) { return (
  • ) : ( person.avatarText ) } size="compact" className="inline-flex h-7 w-7 shrink-0 text-[11px] font-semibold" /> {person.label} {person.roleLabel} {canManage && person.share ? ( } label={removeLabel} aria-label={removeLabel} onPress={() => onRemove(person.share!)} className={cn(BUTTON_GHOST_ICON, "[&_svg]:!size-auto")} /> ) : null}
  • ); } function DefaultEmbedBody({ controller, }: { controller: ShareDialogController; }) { return (
    ); } function CopyField({ field, label, value, controller, multiline, }: { field: string; label: string; value: string; controller: ShareDialogController; multiline?: boolean; }) { const copied = controller.copiedField === field; return (
    {label}
    {multiline ? (