{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "popover",
  "type": "registry:ui",
  "title": "Popover",
  "description": "React Aria backed anchored popover primitive with provider-aware portals, token-backed content and arrow styling, controlled open state, placement props, dismissal controls, reduced-motion transitions, and accessible dialog anatomy.",
  "dependencies": [
    "react-aria@^3.52.1",
    "react-aria-components@^1.21.1"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://components.dethink.co.uk/r/dethink-base.json",
    "https://components.dethink.co.uk/r/button.json"
  ],
  "files": [
    {
      "path": "packages/components/src/components/popover/popover.tsx",
      "type": "registry:file",
      "target": "~/components/dethink/components/popover/popover.tsx",
      "content": "import {\n  Button as AriaButton,\n  Dialog as AriaDialog,\n  DialogTrigger as AriaDialogTrigger,\n  Heading as AriaHeading,\n  Popover as AriaPopover,\n  type ButtonProps as AriaButtonProps,\n  type DialogRenderProps as AriaDialogRenderProps,\n  type DialogTriggerProps as AriaDialogTriggerProps,\n  type HeadingProps as AriaHeadingProps,\n  type PopoverProps as AriaPopoverProps,\n} from \"react-aria-components\";\nimport {\n  createContext,\n  forwardRef,\n  type HTMLAttributes,\n  type ReactNode,\n  type Ref,\n  useContext,\n  useEffect,\n  useId,\n  useMemo,\n  useRef,\n  useState,\n} from \"react\";\nimport {\n  buttonClassNames,\n  type ButtonSize,\n  type ButtonVariant,\n} from \"../button\";\nimport {\n  DethinkPortalProvider,\n  useProviderPortalRoot,\n  useScopedOverlayState,\n} from \"../../utils/provider-portal\";\nimport {\n  PositionedOverlayArrow,\n  type PositionedOverlayArrowProps,\n  positionedOverlayArrowClassNames,\n  positionedOverlayPopoverDefaults,\n  positionedOverlaySurfaceClassNames,\n  resolvePositionedOverlayPositionProps,\n} from \"../../utils/positioned-overlay\";\nimport { cn } from \"../../utils/cn\";\n\nexport interface PopoverProps extends Omit<\n  AriaDialogTriggerProps,\n  \"children\" | \"isOpen\" | \"onOpenChange\"\n> {\n  anchorRef?: AriaPopoverProps[\"triggerRef\"];\n  \"data-slot\"?: string;\n  children?: ReactNode;\n  className?: string;\n  onOpenChange?: (open: boolean) => void;\n  open?: boolean;\n}\n\nexport interface PopoverTriggerProps extends Omit<\n  AriaButtonProps,\n  \"children\" | \"className\" | \"isDisabled\"\n> {\n  children?: ReactNode;\n  className?: string;\n  disabled?: boolean;\n  size?: ButtonSize;\n  variant?: ButtonVariant;\n}\n\nexport interface PopoverContentProps extends Omit<\n  AriaPopoverProps,\n  | \"children\"\n  | \"className\"\n  | \"defaultOpen\"\n  | \"isKeyboardDismissDisabled\"\n  | \"isOpen\"\n  | \"onOpenChange\"\n  | \"UNSTABLE_portalContainer\"\n> {\n  \"aria-describedby\"?: string;\n  \"aria-label\"?: string;\n  \"aria-labelledby\"?: string;\n  arrowClassName?: string;\n  arrowShapeClassName?: string;\n  children?: ReactNode | ((opts: AriaDialogRenderProps) => ReactNode);\n  className?: string;\n  keyboardDismissDisabled?: boolean;\n  panelClassName?: string;\n  showArrow?: boolean;\n}\n\nexport interface PopoverArrowProps extends PositionedOverlayArrowProps {}\n\nexport interface PopoverHeaderProps extends HTMLAttributes<HTMLDivElement> {}\n\nexport interface PopoverFooterProps extends HTMLAttributes<HTMLDivElement> {}\n\nexport interface PopoverTitleProps extends Omit<AriaHeadingProps, \"className\"> {\n  className?: string;\n  visuallyHidden?: boolean;\n}\n\nexport interface PopoverDescriptionProps extends HTMLAttributes<HTMLParagraphElement> {}\n\nexport interface PopoverCloseProps extends PopoverTriggerProps {}\n\ninterface PopoverContentContextValue {\n  defaultTitleId: string;\n  setDescriptionId: (id: string | null) => void;\n  setTitleId: (id: string | null) => void;\n}\n\nconst PopoverContentContext = createContext<PopoverContentContextValue | null>(\n  null,\n);\n\ninterface PopoverRootContextValue {\n  anchorRef?: AriaPopoverProps[\"triggerRef\"];\n  isAnchored: boolean;\n  onOpenChange: (open: boolean) => void;\n  open: boolean;\n  setTriggerElement: (element: HTMLButtonElement | null) => void;\n}\n\nconst PopoverRootContext = createContext<PopoverRootContextValue | null>(null);\n\nconst popoverRootClasses = \"contents\";\n\nconst popoverContentClasses =\n  \"relative flex min-w-0 w-[var(--dt-popover-width,20rem)] max-w-[min(var(--dt-popover-max-width,24rem),calc(100vw_-_var(--dt-space-6)))] flex-col overflow-visible rounded-xl p-[var(--dt-space-4)]\";\n\nconst popoverPanelClasses =\n  \"grid min-h-0 min-w-0 gap-[var(--dt-space-4)] overflow-auto overscroll-contain [overflow-wrap:anywhere] outline-none focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring\";\n\nconst popoverHeaderClasses = \"grid gap-[var(--dt-space-1)] text-start\";\n\nconst popoverFooterClasses =\n  \"flex flex-wrap items-center justify-end gap-density-gap border-t border-border pt-[var(--dt-space-3)]\";\n\nconst popoverTitleClasses =\n  \"text-sm font-semibold leading-6 tracking-normal text-foreground\";\n\nconst popoverDescriptionClasses =\n  \"text-sm leading-relaxed text-muted-foreground\";\n\nconst visuallyHiddenClasses = \"sr-only\";\n\nconst popoverCloseIconClasses = \"pointer-events-none size-4 shrink-0\";\n\n// Overlap the surface border by a pixel so the arrow shares its outline.\nconst popoverArrowClasses =\n  \"[filter:none] data-[placement=top]:-translate-y-px data-[placement=bottom]:translate-y-px data-[placement=left]:-translate-x-px data-[placement=right]:translate-x-px\";\n\nfunction renderPopoverChildren(\n  children: PopoverContentProps[\"children\"],\n  opts: AriaDialogRenderProps,\n) {\n  return typeof children === \"function\" ? children(opts) : children;\n}\n\nfunction assignRef<T>(ref: Ref<T> | undefined, value: T | null) {\n  if (typeof ref === \"function\") {\n    ref(value);\n  } else if (ref) {\n    // React 18 types RefObject.current as read-only.\n    (ref as { current: T | null }).current = value;\n  }\n}\n\nfunction composeRefs<T>(...refs: Array<Ref<T> | undefined>) {\n  return (node: T | null) => {\n    for (const ref of refs) {\n      assignRef(ref, node);\n    }\n  };\n}\n\nfunction CloseIcon() {\n  return (\n    <svg\n      aria-hidden=\"true\"\n      className={popoverCloseIconClasses}\n      fill=\"none\"\n      viewBox=\"0 0 16 16\"\n    >\n      <path\n        d=\"m4.25 4.25 7.5 7.5m0-7.5-7.5 7.5\"\n        stroke=\"currentColor\"\n        strokeLinecap=\"round\"\n        strokeWidth=\"1.75\"\n      />\n    </svg>\n  );\n}\n\nexport function popoverClassNames({\n  className,\n}: Pick<PopoverProps, \"className\"> = {}) {\n  return cn(popoverRootClasses, className);\n}\n\nexport function popoverTriggerClassNames({\n  className,\n  size = \"md\",\n  variant = \"solid\",\n}: Pick<PopoverTriggerProps, \"className\" | \"size\" | \"variant\"> = {}) {\n  return buttonClassNames({ className, size, variant });\n}\n\nexport function popoverContentClassNames({\n  className,\n}: Pick<PopoverContentProps, \"className\"> = {}) {\n  return cn(\n    positionedOverlaySurfaceClassNames(),\n    popoverContentClasses,\n    className,\n  );\n}\n\nexport function popoverPanelClassNames({\n  className,\n}: {\n  className?: string;\n} = {}) {\n  return cn(popoverPanelClasses, className);\n}\n\nexport function popoverArrowClassNames({\n  className,\n}: Pick<PopoverArrowProps, \"className\"> = {}) {\n  return positionedOverlayArrowClassNames({\n    className: cn(popoverArrowClasses, className),\n  });\n}\n\nexport function popoverHeaderClassNames({\n  className,\n}: Pick<PopoverHeaderProps, \"className\"> = {}) {\n  return cn(popoverHeaderClasses, className);\n}\n\nexport function popoverFooterClassNames({\n  className,\n}: Pick<PopoverFooterProps, \"className\"> = {}) {\n  return cn(popoverFooterClasses, className);\n}\n\nexport function popoverTitleClassNames({\n  className,\n  visuallyHidden = false,\n}: Pick<PopoverTitleProps, \"className\" | \"visuallyHidden\"> = {}) {\n  return cn(\n    popoverTitleClasses,\n    visuallyHidden && visuallyHiddenClasses,\n    className,\n  );\n}\n\nexport function popoverDescriptionClassNames({\n  className,\n}: Pick<PopoverDescriptionProps, \"className\"> = {}) {\n  return cn(popoverDescriptionClasses, className);\n}\n\nexport function popoverCloseClassNames({\n  className,\n  size = \"md\",\n  variant = \"ghost\",\n}: Pick<PopoverCloseProps, \"className\" | \"size\" | \"variant\"> = {}) {\n  return popoverTriggerClassNames({ className, size, variant });\n}\n\nexport const Popover = forwardRef<HTMLDivElement, PopoverProps>(\n  (\n    {\n      \"data-slot\": dataSlot,\n      anchorRef,\n      children,\n      className,\n      defaultOpen,\n      onOpenChange,\n      open,\n      ...props\n    },\n    ref,\n  ) => {\n    const [resolvedOpen, handleOpenChange] = useScopedOverlayState({\n      open,\n      defaultOpen,\n      onOpenChange,\n    });\n    const isAnchored = anchorRef !== undefined;\n    const previousOpenRef = useRef(resolvedOpen);\n    const triggerElementRef = useRef<HTMLButtonElement | null>(null);\n    const { portalContainer, rootRef } = useProviderPortalRoot<HTMLDivElement>({\n      forwardedRef: ref,\n      portalSlot: \"popover-portal-container\",\n    });\n    const rootContextValue = useMemo<PopoverRootContextValue>(\n      () => ({\n        anchorRef,\n        isAnchored,\n        onOpenChange: handleOpenChange,\n        open: resolvedOpen,\n        setTriggerElement: (element) => {\n          triggerElementRef.current = element;\n        },\n      }),\n      [anchorRef, handleOpenChange, isAnchored, resolvedOpen],\n    );\n\n    useEffect(() => {\n      const wasOpen = previousOpenRef.current;\n\n      previousOpenRef.current = resolvedOpen;\n\n      if (wasOpen && !resolvedOpen && typeof window !== \"undefined\") {\n        const restoreFocus = window.setTimeout(() => {\n          if (isAnchored) {\n            const anchorElement = anchorRef?.current;\n\n            if (anchorElement instanceof HTMLElement) {\n              anchorElement.focus();\n            }\n\n            return;\n          }\n\n          triggerElementRef.current?.focus();\n        }, 0);\n\n        return () => {\n          window.clearTimeout(restoreFocus);\n        };\n      }\n\n      return undefined;\n    }, [anchorRef, isAnchored, resolvedOpen]);\n\n    return (\n      <div\n        ref={rootRef}\n        data-slot={dataSlot ?? \"popover\"}\n        data-open={resolvedOpen ? \"\" : undefined}\n        className={popoverClassNames({ className })}\n      >\n        <DethinkPortalProvider container={portalContainer}>\n          <PopoverRootContext.Provider value={rootContextValue}>\n            {isAnchored ? (\n              children\n            ) : (\n              <AriaDialogTrigger\n                {...props}\n                isOpen={resolvedOpen}\n                onOpenChange={handleOpenChange}\n              >\n                {children}\n              </AriaDialogTrigger>\n            )}\n          </PopoverRootContext.Provider>\n        </DethinkPortalProvider>\n      </div>\n    );\n  },\n);\n\nPopover.displayName = \"Popover\";\n\nexport const PopoverTrigger = forwardRef<\n  HTMLButtonElement,\n  PopoverTriggerProps\n>(\n  (\n    {\n      children,\n      className,\n      disabled = false,\n      size = \"md\",\n      variant = \"solid\",\n      ...props\n    },\n    ref,\n  ) => {\n    const rootContext = useContext(PopoverRootContext);\n    const setTriggerRef = (node: HTMLButtonElement | null) => {\n      rootContext?.setTriggerElement(node);\n    };\n\n    return (\n      <AriaButton\n        {...props}\n        ref={composeRefs(ref, setTriggerRef)}\n        data-slot=\"popover-trigger\"\n        isDisabled={disabled}\n        className={popoverTriggerClassNames({ className, size, variant })}\n      >\n        {children}\n      </AriaButton>\n    );\n  },\n);\n\nPopoverTrigger.displayName = \"PopoverTrigger\";\n\nexport const PopoverContent = forwardRef<HTMLElement, PopoverContentProps>(\n  (\n    {\n      \"aria-describedby\": ariaDescribedBy,\n      \"aria-label\": ariaLabel,\n      \"aria-labelledby\": ariaLabelledBy,\n      arrowClassName,\n      arrowShapeClassName,\n      children,\n      className,\n      containerPadding,\n      crossOffset,\n      arrowBoundaryOffset,\n      keyboardDismissDisabled = false,\n      offset,\n      panelClassName,\n      placement,\n      shouldFlip,\n      showArrow = false,\n      ...props\n    },\n    ref,\n  ) => {\n    const defaultTitleId = useId();\n    const [titleId, setTitleId] = useState<string | null>(null);\n    const [descriptionId, setDescriptionId] = useState<string | null>(null);\n    const labelledBy =\n      ariaLabelledBy ?? (ariaLabel ? undefined : (titleId ?? undefined));\n    const describedBy = ariaDescribedBy ?? descriptionId ?? undefined;\n    const positionProps = resolvePositionedOverlayPositionProps(\n      {\n        arrowBoundaryOffset,\n        containerPadding,\n        crossOffset,\n        offset,\n        placement,\n        shouldFlip,\n      },\n      positionedOverlayPopoverDefaults,\n    );\n    const contextValue = useMemo(\n      () => ({\n        defaultTitleId,\n        setDescriptionId,\n        setTitleId,\n      }),\n      [defaultTitleId],\n    );\n    const rootContext = useContext(PopoverRootContext);\n    const anchoredPopoverProps: Partial<\n      Pick<AriaPopoverProps, \"isOpen\" | \"onOpenChange\" | \"triggerRef\">\n    > = {};\n\n    if (rootContext?.isAnchored && rootContext.anchorRef) {\n      anchoredPopoverProps.isOpen = rootContext.open;\n      anchoredPopoverProps.onOpenChange = rootContext.onOpenChange;\n      anchoredPopoverProps.triggerRef = rootContext.anchorRef;\n    }\n\n    return (\n      <AriaPopover\n        {...props}\n        {...anchoredPopoverProps}\n        {...positionProps}\n        ref={ref}\n        data-slot=\"popover-content\"\n        isKeyboardDismissDisabled={keyboardDismissDisabled}\n        className={popoverContentClassNames({ className })}\n      >\n        {showArrow ? (\n          <PopoverArrow\n            className={arrowClassName}\n            shapeClassName={arrowShapeClassName}\n          />\n        ) : null}\n        <AriaDialog\n          aria-describedby={describedBy}\n          aria-label={ariaLabel}\n          aria-labelledby={labelledBy}\n          data-slot=\"popover-panel\"\n          className={popoverPanelClassNames({ className: panelClassName })}\n        >\n          {(opts) => (\n            <PopoverContentContext.Provider value={contextValue}>\n              {renderPopoverChildren(children, opts)}\n            </PopoverContentContext.Provider>\n          )}\n        </AriaDialog>\n      </AriaPopover>\n    );\n  },\n);\n\nPopoverContent.displayName = \"PopoverContent\";\n\nexport const PopoverArrow = forwardRef<HTMLDivElement, PopoverArrowProps>(\n  ({ className, ...props }, ref) => (\n    <PositionedOverlayArrow\n      {...props}\n      ref={ref}\n      data-slot=\"popover-arrow\"\n      className={cn(popoverArrowClasses, className)}\n    />\n  ),\n);\n\nPopoverArrow.displayName = \"PopoverArrow\";\n\nexport const PopoverHeader = forwardRef<HTMLDivElement, PopoverHeaderProps>(\n  ({ className, ...props }, ref) => (\n    <div\n      {...props}\n      ref={ref}\n      data-slot=\"popover-header\"\n      className={popoverHeaderClassNames({ className })}\n    />\n  ),\n);\n\nPopoverHeader.displayName = \"PopoverHeader\";\n\nexport const PopoverFooter = forwardRef<HTMLDivElement, PopoverFooterProps>(\n  ({ className, ...props }, ref) => (\n    <div\n      {...props}\n      ref={ref}\n      data-slot=\"popover-footer\"\n      className={popoverFooterClassNames({ className })}\n    />\n  ),\n);\n\nPopoverFooter.displayName = \"PopoverFooter\";\n\nexport const PopoverTitle = forwardRef<HTMLHeadingElement, PopoverTitleProps>(\n  ({ className, id, level = 2, visuallyHidden = false, ...props }, ref) => {\n    const context = useContext(PopoverContentContext);\n    const resolvedId = id ?? context?.defaultTitleId;\n\n    useEffect(() => {\n      if (!resolvedId) {\n        return undefined;\n      }\n\n      context?.setTitleId(resolvedId);\n\n      return () => {\n        context?.setTitleId(null);\n      };\n    }, [context, resolvedId]);\n\n    return (\n      <AriaHeading\n        {...props}\n        ref={ref}\n        id={resolvedId}\n        level={level}\n        data-slot=\"popover-title\"\n        className={popoverTitleClassNames({ className, visuallyHidden })}\n      />\n    );\n  },\n);\n\nPopoverTitle.displayName = \"PopoverTitle\";\n\nexport const PopoverDescription = forwardRef<\n  HTMLParagraphElement,\n  PopoverDescriptionProps\n>(({ className, id, ...props }, ref) => {\n  const context = useContext(PopoverContentContext);\n  const generatedId = useId();\n  const resolvedId = id ?? generatedId;\n\n  useEffect(() => {\n    context?.setDescriptionId(resolvedId);\n\n    return () => {\n      context?.setDescriptionId(null);\n    };\n  }, [context, resolvedId]);\n\n  return (\n    <p\n      {...props}\n      ref={ref}\n      id={resolvedId}\n      data-slot=\"popover-description\"\n      className={popoverDescriptionClassNames({ className })}\n    />\n  );\n});\n\nPopoverDescription.displayName = \"PopoverDescription\";\n\nexport const PopoverClose = forwardRef<HTMLButtonElement, PopoverCloseProps>(\n  (\n    {\n      \"aria-label\": ariaLabel,\n      children,\n      className,\n      disabled = false,\n      onPress,\n      size,\n      variant = \"ghost\",\n      ...props\n    },\n    ref,\n  ) => {\n    const hasVisibleChildren = children != null;\n    const resolvedSize = size ?? (hasVisibleChildren ? \"md\" : \"icon\");\n    const rootContext = useContext(PopoverRootContext);\n\n    return (\n      <AriaButton\n        {...props}\n        ref={ref}\n        aria-label={\n          ariaLabel ?? (hasVisibleChildren ? undefined : \"Close popover\")\n        }\n        isDisabled={disabled}\n        onPress={(event) => {\n          onPress?.(event);\n\n          if (rootContext?.isAnchored) {\n            rootContext.onOpenChange(false);\n          }\n        }}\n        slot=\"close\"\n        data-slot=\"popover-close\"\n        className={popoverCloseClassNames({\n          className,\n          size: resolvedSize,\n          variant,\n        })}\n      >\n        {hasVisibleChildren ? children : <CloseIcon />}\n      </AriaButton>\n    );\n  },\n);\n\nPopoverClose.displayName = \"PopoverClose\";\n"
    },
    {
      "path": "packages/components/src/components/popover/index.ts",
      "type": "registry:file",
      "target": "~/components/dethink/components/popover/index.ts",
      "content": "export {\n  Popover,\n  PopoverArrow,\n  PopoverClose,\n  PopoverContent,\n  PopoverDescription,\n  PopoverFooter,\n  PopoverHeader,\n  PopoverTitle,\n  PopoverTrigger,\n  popoverArrowClassNames,\n  popoverClassNames,\n  popoverCloseClassNames,\n  popoverContentClassNames,\n  popoverDescriptionClassNames,\n  popoverFooterClassNames,\n  popoverHeaderClassNames,\n  popoverPanelClassNames,\n  popoverTitleClassNames,\n  popoverTriggerClassNames,\n  type PopoverArrowProps,\n  type PopoverCloseProps,\n  type PopoverContentProps,\n  type PopoverDescriptionProps,\n  type PopoverFooterProps,\n  type PopoverHeaderProps,\n  type PopoverProps,\n  type PopoverTitleProps,\n  type PopoverTriggerProps,\n} from \"./popover\";\n"
    },
    {
      "path": "packages/components/src/utils/provider-portal.tsx",
      "type": "registry:file",
      "target": "~/components/dethink/utils/provider-portal.tsx",
      "content": "import {\n  createContext,\n  type ForwardedRef,\n  type ReactNode,\n  type RefCallback,\n  useCallback,\n  useContext,\n  useEffect,\n  useRef,\n  useState,\n} from \"react\";\nimport { UNSAFE_PortalProvider } from \"react-aria/PortalProvider\";\nimport { setDethinkPortalOwner } from \"./portal-owner\";\n\nconst providerPortalDefaultClasses = \"bg-background font-sans text-foreground\";\nconst providerPortalMirroredAttributes = [\n  \"data-theme\",\n  \"data-density\",\n  \"dir\",\n] as const;\n\nexport type ProviderPortalMirroredAttribute =\n  (typeof providerPortalMirroredAttributes)[number];\n\nexport interface ProviderPortalRootOptions<T extends HTMLElement> {\n  forwardedRef?: ForwardedRef<T>;\n  portalSlot: string;\n}\n\nexport interface ProviderPortalRootState<T extends HTMLElement> {\n  portalContainer: HTMLElement | null;\n  rootRef: RefCallback<T>;\n}\n\nexport interface DethinkPortalProviderProps {\n  children?: ReactNode;\n  container: HTMLElement | null;\n}\n\nconst DethinkPortalContext = createContext(false);\nconst OverlayVisibilityContext = createContext(true);\n\n/** Persistent surfaces can dismiss overlays without unmounting their content. */\nexport function DethinkOverlayScope({\n  visible,\n  children,\n}: {\n  visible: boolean;\n  children?: ReactNode;\n}) {\n  const parentVisible = useContext(OverlayVisibilityContext);\n  return (\n    <OverlayVisibilityContext.Provider value={visible && parentVisible}>\n      {children}\n    </OverlayVisibilityContext.Provider>\n  );\n}\n\nexport function useScopedOverlayState({\n  open,\n  defaultOpen = false,\n  onOpenChange,\n}: {\n  open?: boolean;\n  defaultOpen?: boolean;\n  onOpenChange?: (open: boolean) => void;\n}): readonly [boolean, (open: boolean) => void] {\n  const visible = useContext(OverlayVisibilityContext);\n  const [localOpen, setLocalOpen] = useState(defaultOpen);\n  const requestedOpen = open ?? localOpen;\n  const closeRequested = useRef(false);\n  const setOpen = useCallback(\n    (next: boolean) => {\n      if (next && !visible) return;\n      if (open === undefined) setLocalOpen(next);\n      onOpenChange?.(next);\n    },\n    [open, onOpenChange, visible],\n  );\n  useEffect(() => {\n    if (visible) closeRequested.current = false;\n    else if (requestedOpen && !closeRequested.current) {\n      closeRequested.current = true;\n      // Synchronize overlay state with the containing surface's lifecycle.\n      setOpen(false);\n    }\n  }, [visible, requestedOpen, setOpen]);\n  return [visible && requestedOpen, setOpen];\n}\n\nfunction assignForwardedRef<T>(\n  ref: ForwardedRef<T> | undefined,\n  value: T | null,\n) {\n  if (typeof ref === \"function\") {\n    ref(value);\n  } else if (ref) {\n    ref.current = value;\n  }\n}\n\nfunction fallbackProviderAttribute(name: ProviderPortalMirroredAttribute) {\n  if (typeof document === \"undefined\") {\n    return null;\n  }\n\n  if (name === \"data-density\") {\n    return \"default\";\n  }\n\n  if (name === \"data-theme\") {\n    return document.documentElement.getAttribute(name) ?? \"system\";\n  }\n\n  return document.documentElement.getAttribute(\"dir\") ?? document.dir ?? \"ltr\";\n}\n\nexport function syncDethinkPortalContainer({\n  container,\n  portalSlot,\n  provider,\n}: {\n  container: HTMLElement;\n  portalSlot: string;\n  provider: HTMLElement | null;\n}) {\n  const source =\n    provider ??\n    (typeof document === \"undefined\" ? null : document.documentElement);\n\n  container.setAttribute(\"data-slot\", portalSlot);\n  container.setAttribute(\"data-dethink-provider\", \"\");\n  container.className = provider?.className || providerPortalDefaultClasses;\n  container.style.cssText = provider?.getAttribute(\"style\") ?? \"\";\n  container.style.display = \"contents\";\n\n  for (const attribute of providerPortalMirroredAttributes) {\n    const value =\n      source?.getAttribute(attribute) ?? fallbackProviderAttribute(attribute);\n\n    if (value) {\n      container.setAttribute(attribute, value);\n    } else {\n      container.removeAttribute(attribute);\n    }\n  }\n}\n\nexport function useProviderPortalRoot<T extends HTMLElement>({\n  forwardedRef,\n  portalSlot,\n}: ProviderPortalRootOptions<T>): ProviderPortalRootState<T> {\n  const [portalContainer] = useState<HTMLElement | null>(() => {\n    if (typeof document === \"undefined\") {\n      return null;\n    }\n\n    return document.createElement(\"div\");\n  });\n  const [rootElement, setRootElement] = useState<T | null>(null);\n\n  const rootRef = useCallback(\n    (node: T | null) => {\n      setRootElement(node);\n      assignForwardedRef(forwardedRef, node);\n\n      if (node && portalContainer && typeof document !== \"undefined\") {\n        setDethinkPortalOwner(portalContainer, node);\n        const provider =\n          node.closest<HTMLElement>(\"[data-dethink-provider]\") ?? null;\n\n        syncDethinkPortalContainer({\n          container: portalContainer,\n          portalSlot,\n          provider,\n        });\n\n        if (!portalContainer.isConnected) {\n          document.body.appendChild(portalContainer);\n        }\n      }\n    },\n    [forwardedRef, portalContainer, portalSlot],\n  );\n\n  useEffect(() => {\n    if (!portalContainer || typeof document === \"undefined\") {\n      return undefined;\n    }\n\n    const provider =\n      rootElement?.closest<HTMLElement>(\"[data-dethink-provider]\") ?? null;\n    const syncContainer = () => {\n      syncDethinkPortalContainer({\n        container: portalContainer,\n        portalSlot,\n        provider,\n      });\n    };\n\n    syncContainer();\n    document.body.appendChild(portalContainer);\n\n    const observerTarget = provider ?? document.documentElement;\n    const observer = new MutationObserver(syncContainer);\n    observer.observe(observerTarget, {\n      attributeFilter: [\"class\", \"data-density\", \"data-theme\", \"dir\", \"style\"],\n      attributes: true,\n    });\n\n    return () => {\n      observer.disconnect();\n      portalContainer.remove();\n    };\n  }, [portalContainer, portalSlot, rootElement]);\n\n  return {\n    portalContainer,\n    rootRef,\n  };\n}\n\nexport function DethinkPortalProvider({\n  children,\n  container,\n}: DethinkPortalProviderProps) {\n  const hasDethinkPortalParent = useContext(DethinkPortalContext);\n  const content = hasDethinkPortalParent ? (\n    children\n  ) : (\n    <UNSAFE_PortalProvider getContainer={() => container}>\n      {children}\n    </UNSAFE_PortalProvider>\n  );\n\n  return (\n    <DethinkPortalContext.Provider value>\n      {content}\n    </DethinkPortalContext.Provider>\n  );\n}\n"
    },
    {
      "path": "packages/components/src/utils/portal-owner.ts",
      "type": "registry:file",
      "target": "~/components/dethink/utils/portal-owner.ts",
      "content": "/**\n * Links a Dethink portal container on <body> to the component root that owns\n * it, so a modal surface (the mobile sidebar) can keep overlays opened from\n * inside it usable while it makes the rest of the page inert. A global symbol\n * key, so components copied from the registry agree without sharing imports.\n */\nconst PORTAL_OWNER: unique symbol = Symbol.for(\"dethink.portal-owner\");\n\ntype OwnedElement = Element & { [PORTAL_OWNER]?: Element };\n\nexport function setDethinkPortalOwner(container: Element, owner: Element) {\n  (container as OwnedElement)[PORTAL_OWNER] = owner;\n}\n\n/** The element owning a Dethink portal container, or null for any other element. */\nexport function getDethinkPortalOwner(element: Element): Element | null {\n  return (element as OwnedElement)[PORTAL_OWNER] ?? null;\n}\n"
    },
    {
      "path": "packages/components/src/utils/positioned-overlay.tsx",
      "type": "registry:file",
      "target": "~/components/dethink/utils/positioned-overlay.tsx",
      "content": "import { forwardRef } from \"react\";\nimport {\n  OverlayArrow as AriaOverlayArrow,\n  type OverlayArrowProps as AriaOverlayArrowProps,\n} from \"react-aria-components\";\nimport type { Placement } from \"react-aria/useOverlayPosition\";\nimport { cn } from \"./cn\";\n\nexport type PositionedOverlayPlacement = Placement;\n\nexport interface PositionedOverlayPositionProps {\n  arrowBoundaryOffset?: number;\n  containerPadding?: number;\n  crossOffset?: number;\n  offset?: number;\n  placement?: PositionedOverlayPlacement;\n  shouldFlip?: boolean;\n}\n\nexport interface PositionedOverlaySurfaceClassNameOptions {\n  animation?: \"css\" | \"none\";\n  className?: string;\n}\n\nexport interface PositionedOverlayArrowClassNameOptions {\n  className?: string;\n}\n\nexport interface PositionedOverlayArrowProps extends Omit<\n  AriaOverlayArrowProps,\n  \"children\" | \"className\"\n> {\n  \"data-slot\"?: string;\n  className?: string;\n  shapeDataSlot?: string;\n  shapeClassName?: string;\n}\n\nexport const positionedOverlayPositionDefaults = {\n  arrowBoundaryOffset: 0,\n  containerPadding: 12,\n  crossOffset: 0,\n  offset: 0,\n  placement: \"bottom\",\n  shouldFlip: true,\n} satisfies Required<PositionedOverlayPositionProps>;\n\nexport const positionedOverlayPopoverDefaults = {\n  ...positionedOverlayPositionDefaults,\n  offset: 8,\n} satisfies Required<PositionedOverlayPositionProps>;\n\nexport const positionedOverlayTooltipDefaults = {\n  ...positionedOverlayPositionDefaults,\n  offset: 8,\n  placement: \"top\",\n} satisfies Required<PositionedOverlayPositionProps>;\n\nexport const positionedOverlayDropdownMenuDefaults = {\n  ...positionedOverlayPositionDefaults,\n  offset: 8,\n  placement: \"bottom start\",\n} satisfies Required<PositionedOverlayPositionProps>;\n\nexport const positionedOverlayDropdownSubmenuDefaults = {\n  ...positionedOverlayPositionDefaults,\n  crossOffset: -4,\n  offset: -2,\n  placement: \"right top\",\n} satisfies Required<PositionedOverlayPositionProps>;\n\nconst positionedOverlaySurfaceBaseClasses =\n  \"z-50 max-h-[min(var(--dt-overlay-max-height,18rem),calc(100dvh_-_var(--dt-space-4)))] min-w-[var(--dt-overlay-min-width,12rem)] overflow-auto rounded-md border border-border bg-background p-[var(--dt-space-3)] text-foreground shadow-lg outline-none focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring [--dt-overlay-motion-x:0px] [--dt-overlay-motion-y:var(--dt-space-1)] data-[placement=bottom]:[--dt-overlay-motion-y:calc(0px_-_var(--dt-space-1))] data-[placement=top]:[--dt-overlay-motion-y:var(--dt-space-1)] data-[placement=left]:[--dt-overlay-motion-x:var(--dt-space-1)] data-[placement=left]:[--dt-overlay-motion-y:0px] data-[placement=right]:[--dt-overlay-motion-x:calc(0px_-_var(--dt-space-1))] data-[placement=right]:[--dt-overlay-motion-y:0px] origin-center data-[placement=top]:origin-bottom data-[placement=bottom]:origin-top data-[placement=left]:origin-right data-[placement=right]:origin-left\";\n\nconst positionedOverlaySurfaceCssAnimationClasses =\n  \"motion-safe:data-[entering]:animate-overlay-in motion-safe:data-[exiting]:animate-overlay-out motion-reduce:animate-none\";\n\nconst positionedOverlayArrowBaseClasses =\n  \"group z-50 flex size-3 items-center justify-center text-background [filter:drop-shadow(0_1px_0.5px_rgb(0_0_0_/_0.06))]\";\n\nconst positionedOverlayArrowShapeBaseClasses =\n  \"block size-3 fill-background stroke-border group-data-[placement=bottom]:rotate-180 group-data-[placement=left]:-rotate-90 group-data-[placement=right]:rotate-90 [stroke-linejoin:round] [stroke-width:1px]\";\n\nexport function resolvePositionedOverlayPositionProps(\n  props: PositionedOverlayPositionProps = {},\n  defaults: Required<PositionedOverlayPositionProps> = positionedOverlayPositionDefaults,\n): Required<PositionedOverlayPositionProps> {\n  return {\n    arrowBoundaryOffset:\n      props.arrowBoundaryOffset ?? defaults.arrowBoundaryOffset,\n    containerPadding: props.containerPadding ?? defaults.containerPadding,\n    crossOffset: props.crossOffset ?? defaults.crossOffset,\n    offset: props.offset ?? defaults.offset,\n    placement: props.placement ?? defaults.placement,\n    shouldFlip: props.shouldFlip ?? defaults.shouldFlip,\n  };\n}\n\nexport function positionedOverlaySurfaceClassNames({\n  animation = \"css\",\n  className,\n}: PositionedOverlaySurfaceClassNameOptions = {}) {\n  return cn(\n    positionedOverlaySurfaceBaseClasses,\n    animation === \"css\" && positionedOverlaySurfaceCssAnimationClasses,\n    className,\n  );\n}\n\nexport function positionedOverlayArrowClassNames({\n  className,\n}: PositionedOverlayArrowClassNameOptions = {}) {\n  return cn(positionedOverlayArrowBaseClasses, className);\n}\n\nexport function positionedOverlayArrowShapeClassNames({\n  className,\n}: PositionedOverlayArrowClassNameOptions = {}) {\n  return cn(positionedOverlayArrowShapeBaseClasses, className);\n}\n\nexport const PositionedOverlayArrow = forwardRef<\n  HTMLDivElement,\n  PositionedOverlayArrowProps\n>(\n  (\n    {\n      \"data-slot\": dataSlot = \"positioned-overlay-arrow\",\n      className,\n      shapeClassName,\n      shapeDataSlot,\n      ...props\n    },\n    ref,\n  ) => (\n    <AriaOverlayArrow\n      {...props}\n      ref={ref}\n      data-slot={dataSlot}\n      className={positionedOverlayArrowClassNames({ className })}\n    >\n      <svg\n        aria-hidden=\"true\"\n        data-slot={shapeDataSlot ?? `${dataSlot}-shape`}\n        focusable=\"false\"\n        viewBox=\"0 0 12 12\"\n        className={positionedOverlayArrowShapeClassNames({\n          className: shapeClassName,\n        })}\n      >\n        <path d=\"M0 0 L6 12 L12 0\" />\n      </svg>\n    </AriaOverlayArrow>\n  ),\n);\n\nPositionedOverlayArrow.displayName = \"PositionedOverlayArrow\";\n"
    },
    {
      "path": "packages/components/src/utils/cn.ts",
      "type": "registry:file",
      "target": "~/components/dethink/utils/cn.ts",
      "content": "import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n  return twMerge(clsx(inputs));\n}\n"
    }
  ]
}
