platform-bible-react
    Preparing search index...

    Function PopoverPortalContainerProvider

    • Overrides the container that descendant PopoverContent components portal into. Use it to keep popovers inside a Radix DialogContent, DropdownMenuContent, or any other ancestor that owns a focus trap or dismiss-on-outside-click layer.

      Parameters

      • __namedParameters: { children: ReactNode; container: null | HTMLElement }

      Returns Element

      Radix Popover portals its content to document.body by default, which works fine for top-level UI. The default breaks down whenever a popover trigger lives inside an ancestor that:

      • Runs a focus trap (Dialog, AlertDialog, modal DropdownMenu) — the trap yanks focus back out of the popover the instant it opens because the portal'd content is outside the trap's DOM subtree.
      • Listens for outside-clicks (Radix DismissableLayer, used by every *Menu/Dialog) — a click inside the popover reads as "outside the menu" and dismisses the parent immediately.

      Wrapping the children of the trapping ancestor in this provider, with that ancestor's element as container, makes nested PopoverContent portal as a DOM descendant of the trap so both focus and dismiss-layer logic accept it.

      Single descendant scope: a PopoverPortalContainerProvider only affects PopoverContent mounts rendered as React children. It does not retroactively re-portal already-mounted popovers, and it does not affect popovers in sibling subtrees.

      Initial-mount behavior: pass null for container (the initial value of a `useState<HTMLElement

      | null>(null)` paired with a ref callback on the ancestor) to keep Radix's default

      document.body behavior until the ancestor mounts. Once the element exists, future popover opens portal into it. The triggering ancestor (the trap owner) must wrap, not be wrapped by, this provider.

      function ScopeMenu() {
      const [dialogEl, setDialogEl] = useState<HTMLDivElement | null>(null);

      return (
      <Dialog open={isOpen} onOpenChange={setIsOpen}>
      <DialogContent ref={setDialogEl}>
      <PopoverPortalContainerProvider container={dialogEl}>
      <BookChapterControl ... />
      </PopoverPortalContainerProvider>
      </DialogContent>
      </Dialog>
      );
      }
      // Dropdown variant: same pattern, container is the DropdownMenuContent.
      const [contentEl, setContentEl] = useState<HTMLDivElement | null>(null);
      <DropdownMenu>
      <DropdownMenuTrigger>...</DropdownMenuTrigger>
      <DropdownMenuContent ref={setContentEl}>
      <PopoverPortalContainerProvider container={contentEl}>
      <BookChapterControl ... />
      </PopoverPortalContainerProvider>
      </DropdownMenuContent>
      </DropdownMenu>