"use client"; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, } from "@kandev/ui/dialog"; import { Drawer, DrawerContent, DrawerDescription, DrawerHeader, DrawerTitle, } from "@kandev/ui/drawer"; import { TooltipProvider } from "@kandev/ui/tooltip"; import { pluginModalManager, usePluginModals, type OpenPluginModal, } from "@/lib/plugins/modal-manager"; import type { PluginModalOptions } from "@/lib/plugins/types"; import { t } from "@/lib/i18n"; import { PluginErrorBoundary } from "./plugin-error-boundary"; /** Maps `PluginModalOptions.size` to the host's Dialog width classes. */ const SIZE_CLASSES: Record, string> = { sm: "sm:max-w-sm", md: "sm:max-w-xl", lg: "sm:max-w-3xl", xl: "sm:max-w-5xl", }; function preventWhenNotDismissible(dismissible: boolean) { return (event: Event) => { if (!dismissible) event.preventDefault(); }; } function pluginDialogLabel(): string { return t("plugins:pluginDialog"); } type ModalSurfaceProps = { modal: OpenPluginModal; dismissible: boolean; onOpenChange(open: boolean): void; }; function PluginDrawer({ modal, dismissible, onOpenChange }: ModalSurfaceProps) { const { instanceId, pluginId, options } = modal; const Content = options.content; const noDescriptionProps = options.description ? {} : { "aria-describedby": undefined }; return ( {(options.title || options.description) && ( {options.title ? ( {options.title} ) : ( {pluginDialogLabel()} )} {options.description && {options.description}} )} {!options.title && !options.description && ( {pluginDialogLabel()} )}
); } function PluginDialog({ modal, dismissible, onOpenChange }: ModalSurfaceProps) { const { instanceId, pluginId, options } = modal; const Content = options.content; const guardClose = preventWhenNotDismissible(dismissible); const noDescriptionProps = options.description ? {} : { "aria-describedby": undefined }; return ( {/* Plugin-owned title/description; render either when supplied. */} {(options.title || options.description) && ( {options.title ? ( {options.title} ) : ( {pluginDialogLabel()} )} {options.description && {options.description}} )} {!options.title && !options.description && ( {pluginDialogLabel()} )} ); } function PluginModalInstance({ modal }: { modal: OpenPluginModal }) { const dismissible = modal.options.dismissible ?? true; const handleOpenChange = (open: boolean) => { if (open || !dismissible) return; pluginModalManager.close(modal.instanceId); }; const props = { modal, dismissible, onOpenChange: handleOpenChange }; return modal.options.presentation === "drawer" ? ( ) : ( ); } /** * Renders every open plugin modal (`host.openModal(...)`) in a `@kandev/ui` * `Dialog`, each isolated behind its own `PluginErrorBoundary`. Mounted once * inside ``, where plugin-owned forms inherit the app providers. * * Keep the local `TooltipProvider` so this host is also safe in isolated * mounts and tests. Radix supports nesting it under AppShell's provider. */ export function PluginModalHost() { const modals = usePluginModals(); if (modals.length === 0) return null; return ( {modals.map((modal) => ( ))} ); }