Usage
DrawerHost from @prepared911/ui/blocks/drawer-host is the app drawer a shell mounts once. The shell owns whether it is open and which id it shows. The host renders that one drawer, swaps the content when the id changes, and can show a nav strip under the header.
Use it where a shell used to mount a drawer portal and drive several drawers through one module store. A single panel that is not the shared app drawer still uses Drawer. A record with an unsaved-changes guard uses DetailDrawer.
drawer search param stays on the shell route. Pass open and drawerId, or pass a store the shell created with createDrawerHostStore.nav on the drawer entry. The host renders the same segmented strip as DrawerNav.DetailDrawer.FormLayoutDrawer.drawer search param. They ask the shell, and the shell updates the host.open, drawerId, and onOpenChange, or pass store. Do not pass both. createDrawerHostStore returns a new store each call. The shell holds it. The package does not keep a shared store, so two copies of the package do not share open state.
drawerId while open is true replaces the header and the body in the same dialog.
Escape closes the drawer and focus returns to trigger. Open it from a row and omit trigger: focus returns to the element that was focused before the drawer opened.
The surface is Drawer. Its dialog renders where the host sits in the React tree, and through Portal into the trigger's owner document when the trigger is in another window. OverlayContainerProvider closes the drawer when that window changes. There is no DrawerPortal element to mount.
Mount the host at the shell's root, beside the shell layout, not inside it. A non-modal drawer stacks at --z-overlay in the page's stacking order, below popovers, menus, tooltips, toasts and modal dialogs. An ancestor that creates a stacking context or a containing block (transform, filter, contain, will-change, or a positioned box with a z-index) confines it to that ancestor, so a host rendered inside a panel or a remote's mount covers only that region. See Drawer stacking.
A remote does not render a page-level drawer of its own and does not write the shell's drawer search param. It asks the shell through the navigation domain, and the shell's DrawerHost shows the module:
navigation (from @prepared911/core/mfe/react) has openDrawer(moduleId, options?), closeDrawer(moduleId), getOpenDrawer() and onDrawerChanged(callback). The shell implementation writes the module id into the shell-owned drawer search param and reports every change through onDrawerChanged. The id is a module id from the router's ID_REGISTRY, written <remote>/<module>.
The shell subscribes to that domain, lists one entry of drawers per module it allows in its drawer, and closes through the domain:
<ShellDrawers /> once, beside the shell layout. The pieces:
usePortalIntegrations memoizes on the identity of the array it is given, so the domains array must be stable: declare it once at module scope, as SHELL_DOMAINS is. An inline [navigationDefinition] is a new array every render, which reconnects the domain, re-subscribes the effect, and can render the shell in a loop.onDrawerChanged reports the current drawer as soon as it subscribes, then every change. The shell keeps the last drawerId when the drawer closes, so the panel keeps its content while it slides out. nextDrawerState returns previous itself when a report changes nothing, so a repeated report does not re-render the shell.id is the module id a remote passes to openDrawer, and its content is <RemoteComponent id="…" /> for that module. RemoteComponent takes id, loading (shown while the remote chunk loads) and children; the mount's error boundary renders a failing remote's error inside the drawer body. An id the shell does not list renders no drawer: the search param changes and nothing opens. List every module that may open in the drawer.onOpenChange with false; the shell calls navigation.closeDrawer(moduleId), which clears the search param and reports null. Focus returns to the control that was focused before the drawer opened, such as the remote's button.@prepared911/ui/styles.css stylesheet on the document, data-theme on the document element, and DesignSystemEnvironment for the locale, time zone and direction. A remote renders none of them. See remote composition.