{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "popover",
  "type": "registry:ui",
  "title": "Popover",
  "description": "Popover overlay for Tile UI.",
  "dependencies": [
    "@radix-ui/react-slot",
    "react-dom"
  ],
  "devDependencies": [
    "sass"
  ],
  "registryDependencies": [
    "@tile-ui/core",
    "@tile-ui/portal",
    "@tile-ui/styles"
  ],
  "files": [
    {
      "path": "packages/react/src/components/popover/popover.tsx",
      "type": "registry:ui",
      "target": "components/ui/popover/popover.tsx",
      "content": "import React, { createContext, useCallback, useContext, useEffect, useId, useLayoutEffect, useRef, useState } from 'react';\nimport { createPortal } from 'react-dom';\nimport { Slot } from '@radix-ui/react-slot';\nimport { getPopoverPosition, getPopoverState, popoverStyleKeys } from '../lib/core';\nimport type { PopoverBaseProps, PopoverContentBaseProps, PopoverPosition, PopoverTriggerBaseProps } from '../lib/core';\nimport { usePortalContainer, type PortalContainer } from '../portal';\nimport styles from './popover.module.scss';\n\ninterface PopoverContextValue {\n\topen: boolean;\n\ttriggerRef: React.RefObject<HTMLElement | null>;\n\tcontentId: string;\n\tsetOpen: (open: boolean) => void;\n}\n\nconst PopoverContext = createContext<PopoverContextValue | null>(null);\n\nfunction usePopoverContext(): PopoverContextValue {\n\tconst context = useContext(PopoverContext);\n\tif (!context) {\n\t\tthrow new Error('Popover sub-components must be used within <Popover>.');\n\t}\n\treturn context;\n}\n\nexport interface PopoverProps extends React.HTMLAttributes<HTMLDivElement>, PopoverBaseProps {}\n\nconst Popover = React.forwardRef<HTMLDivElement, PopoverProps>(({ className = '', open, defaultOpen = false, onOpenChange, children, ...props }, ref) => {\n\tconst [internalOpen, setInternalOpen] = useState(defaultOpen);\n\tconst isOpen = open !== undefined ? open : internalOpen;\n\tconst triggerRef = useRef<HTMLElement | null>(null);\n\tconst contentId = useId();\n\n\tconst setOpen = useCallback(\n\t\t(next: boolean) => {\n\t\t\tif (open === undefined) {\n\t\t\t\tsetInternalOpen(next);\n\t\t\t}\n\t\t\tonOpenChange?.(next);\n\t\t},\n\t\t[open, onOpenChange],\n\t);\n\n\treturn (\n\t\t<PopoverContext.Provider value={{ open: isOpen, triggerRef, contentId, setOpen }}>\n\t\t\t<div ref={ref} className={`${styles[popoverStyleKeys.root]} ${className}`} {...props}>\n\t\t\t\t{children}\n\t\t\t</div>\n\t\t</PopoverContext.Provider>\n\t);\n});\nPopover.displayName = 'Popover';\n\nexport interface PopoverTriggerProps extends React.ButtonHTMLAttributes<HTMLButtonElement>, PopoverTriggerBaseProps {}\n\nconst PopoverTrigger = React.forwardRef<HTMLButtonElement, PopoverTriggerProps>(({ className = '', asChild = false, children, onClick, ...props }, ref) => {\n\tconst context = usePopoverContext();\n\n\tfunction setRef(element: HTMLElement | null) {\n\t\tcontext.triggerRef.current = element;\n\t\tif (typeof ref === 'function') {\n\t\t\tref(element as HTMLButtonElement | null);\n\t\t} else if (ref) {\n\t\t\tref.current = element as HTMLButtonElement | null;\n\t\t}\n\t}\n\n\tfunction handleClick(event: React.MouseEvent<HTMLButtonElement>) {\n\t\tcontext.setOpen(!context.open);\n\t\tonClick?.(event);\n\t}\n\n\tconst Comp = asChild ? Slot : 'button';\n\tconst state = getPopoverState(context.open);\n\n\treturn (\n\t\t<Comp\n\t\t\tref={setRef}\n\t\t\taria-haspopup=\"dialog\"\n\t\t\taria-expanded={context.open}\n\t\t\taria-controls={context.contentId}\n\t\t\tdata-state={state}\n\t\t\tclassName={asChild ? undefined : `${styles[popoverStyleKeys.trigger]} ${className}`}\n\t\t\tonClick={handleClick}\n\t\t\t{...props}>\n\t\t\t{children}\n\t\t</Comp>\n\t);\n});\nPopoverTrigger.displayName = 'PopoverTrigger';\n\nexport interface PopoverContentProps extends React.HTMLAttributes<HTMLDivElement>, PopoverContentBaseProps {\n\tcontainer?: PortalContainer;\n}\n\nconst PopoverContent = React.forwardRef<HTMLDivElement, PopoverContentProps>(\n\t({ className = '', side = 'bottom', align = 'center', sideOffset = 4, container, children, ...props }, ref) => {\n\t\tconst { open, triggerRef, contentId, setOpen } = usePopoverContext();\n\t\tconst portalContainer = usePortalContainer(container);\n\t\tconst [position, setPosition] = useState<PopoverPosition | null>(null);\n\t\tconst contentRef = useRef<HTMLDivElement | null>(null);\n\n\t\tfunction setRef(element: HTMLDivElement | null) {\n\t\t\tcontentRef.current = element;\n\t\t\tif (typeof ref === 'function') {\n\t\t\t\tref(element);\n\t\t\t} else if (ref) {\n\t\t\t\tref.current = element;\n\t\t\t}\n\t\t}\n\n\t\tuseLayoutEffect(() => {\n\t\t\tif (!open) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tfunction updatePosition() {\n\t\t\t\tconst trigger = triggerRef.current;\n\t\t\t\tconst content = contentRef.current;\n\t\t\t\tif (!trigger || !content) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst triggerRect = trigger.getBoundingClientRect();\n\t\t\t\tconst contentSize = { width: content.offsetWidth, height: content.offsetHeight };\n\t\t\t\tconst viewport = { width: window.innerWidth, height: window.innerHeight };\n\t\t\t\tsetPosition(getPopoverPosition({ triggerRect, contentSize, side, align, sideOffset, viewport }));\n\t\t\t}\n\n\t\t\tupdatePosition();\n\n\t\t\tconst content = contentRef.current;\n\t\t\tif (content && !content.contains(document.activeElement)) {\n\t\t\t\tcontent.focus();\n\t\t\t}\n\n\t\t\twindow.addEventListener('resize', updatePosition);\n\t\t\tdocument.addEventListener('scroll', updatePosition, true);\n\n\t\t\treturn () => {\n\t\t\t\twindow.removeEventListener('resize', updatePosition);\n\t\t\t\tdocument.removeEventListener('scroll', updatePosition, true);\n\t\t\t};\n\t\t}, [open, side, align, sideOffset, triggerRef]);\n\n\t\tuseEffect(() => {\n\t\t\tif (!open) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tfunction handlePointerDown(event: PointerEvent) {\n\t\t\t\tconst target = event.target as Node | null;\n\t\t\t\tconst content = contentRef.current;\n\t\t\t\tconst trigger = triggerRef.current;\n\t\t\t\tif (!target) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (content && content.contains(target)) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (trigger && trigger.contains(target)) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tsetOpen(false);\n\t\t\t}\n\n\t\t\tfunction handleKeyDown(event: KeyboardEvent) {\n\t\t\t\tif (event.key === 'Escape') {\n\t\t\t\t\tsetOpen(false);\n\t\t\t\t\ttriggerRef.current?.focus();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tdocument.addEventListener('pointerdown', handlePointerDown);\n\t\t\tdocument.addEventListener('keydown', handleKeyDown);\n\n\t\t\treturn () => {\n\t\t\t\tdocument.removeEventListener('pointerdown', handlePointerDown);\n\t\t\t\tdocument.removeEventListener('keydown', handleKeyDown);\n\t\t\t};\n\t\t}, [open, triggerRef, setOpen]);\n\n\t\tconst state = getPopoverState(open);\n\t\tconst classes = [styles[popoverStyleKeys.content], className].filter(Boolean).join(' ');\n\n\t\tconst content = (\n\t\t\t<div\n\t\t\t\tref={setRef}\n\t\t\t\tid={contentId}\n\t\t\t\trole=\"dialog\"\n\t\t\t\taria-modal=\"false\"\n\t\t\t\ttabIndex={-1}\n\t\t\t\tdata-state={state}\n\t\t\t\tdata-side={side}\n\t\t\t\tdata-align={align}\n\t\t\t\tclassName={classes}\n\t\t\t\tstyle={position ? { top: `${position.top}px`, left: `${position.left}px` } : undefined}\n\t\t\t\t{...props}>\n\t\t\t\t{children}\n\t\t\t</div>\n\t\t);\n\n\t\tif (!open || !portalContainer) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn createPortal(content, portalContainer);\n\t},\n);\nPopoverContent.displayName = 'PopoverContent';\n\nexport { Popover, PopoverTrigger, PopoverContent };\nexport default Popover;\n"
    },
    {
      "path": "packages/styles/scss/components/popover.module.scss",
      "type": "registry:file",
      "target": "components/ui/popover/popover.module.scss",
      "content": "@use '../../../styles/variables/colors' as *;\n\n.root {\n\tdisplay: inline-flex;\n}\n\n.trigger {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tborder: none;\n\tbackground: transparent;\n\tpadding: 0;\n\tcolor: inherit;\n\tfont: inherit;\n\tcursor: pointer;\n}\n\n.content {\n\tposition: fixed;\n\tz-index: $z-popover;\n\twidth: 18rem;\n\tmax-width: calc(100vw - 2rem);\n\tborder-radius: var(--tile-popover-radius, $radius-md);\n\tborder: 1px solid var(--tile-popover-border, $border);\n\tbackground-color: var(--tile-popover-background, $popover);\n\tcolor: $popover-foreground;\n\tpadding: var(--tile-popover-padding, $spacing-4);\n\tfont-size: var(--tile-popover-font-size, inherit);\n\tline-height: var(--tile-popover-leading, inherit);\n\tbox-shadow: var(--tile-popover-shadow, $shadow-md);\n\toutline: none;\n\ttransition:\n\t\topacity $transition-fast,\n\t\tvisibility $transition-fast,\n\t\ttransform $transition-fast;\n\n\t&[data-state='closed'] {\n\t\topacity: 0;\n\t\tvisibility: hidden;\n\t\tpointer-events: none;\n\n\t\t&[data-side='top'] {\n\t\t\ttransform: translateY(0.25rem);\n\t\t}\n\n\t\t&[data-side='right'] {\n\t\t\ttransform: translateX(-0.25rem);\n\t\t}\n\n\t\t&[data-side='bottom'] {\n\t\t\ttransform: translateY(-0.25rem);\n\t\t}\n\n\t\t&[data-side='left'] {\n\t\t\ttransform: translateX(0.25rem);\n\t\t}\n\t}\n\n\t&[data-state='open'] {\n\t\topacity: 1;\n\t\tvisibility: visible;\n\t\tpointer-events: auto;\n\t\ttransform: translate(0, 0);\n\t}\n}\n"
    },
    {
      "path": "packages/react/src/components/popover/index.ts",
      "type": "registry:ui",
      "target": "components/ui/popover/index.ts",
      "content": "export { Popover, PopoverTrigger, PopoverContent, type PopoverProps, type PopoverTriggerProps, type PopoverContentProps } from './popover';\n"
    }
  ]
}
