{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"popover","type":"registry:ui","title":"Popover","description":"Rich content in a floating panel, anchored to a trigger.","dependencies":["radix-ui"],"registryDependencies":["https://dashral.com/r/utils.json","https://dashral.com/r/lib-portal.json"],"files":[{"path":"registry/dashral/popover.tsx","type":"registry:file","target":"components/dashral/popover.tsx","content":"\"use client\"\n\nimport * as React from \"react\"\nimport { Popover as PopoverPrimitive } from \"radix-ui\"\n\nimport { cn } from \"./lib/utils\"\nimport { usePortalContainer } from \"./lib/portal\"\n\ntype PopoverProps = Omit<React.ComponentProps<typeof PopoverPrimitive.Root>, \"children\"> & {\n  /** What opens it, e.g. a button; it must take a ref and focus. */\n  trigger: React.ReactElement\n  /** The panel's content. */\n  children: React.ReactNode\n  side?: \"top\" | \"right\" | \"bottom\" | \"left\"\n  align?: \"start\" | \"center\" | \"end\"\n  /** The panel's classes, e.g. a width or no padding. */\n  className?: string\n}\n\n/** A panel that opens from a trigger, in one tag: `<Popover trigger={<Button>Filters</Button>}>…</Popover>`. */\nfunction Popover({ trigger, children, side, align, className, ...props }: PopoverProps) {\n  return (\n    <PopoverPrimitive.Root data-slot=\"popover\" {...props}>\n      <PopoverPrimitive.Trigger asChild>{trigger}</PopoverPrimitive.Trigger>\n      <PopoverContent side={side} align={align} className={className}>\n        {children}\n      </PopoverContent>\n    </PopoverPrimitive.Root>\n  )\n}\n\n/** Building block: the root, for a control of your own (Combobox and DatePicker are built from these). */\nfunction PopoverRoot(props: React.ComponentProps<typeof PopoverPrimitive.Root>) {\n  return <PopoverPrimitive.Root data-slot=\"popover\" {...props} />\n}\n\nfunction PopoverTrigger(props: React.ComponentProps<typeof PopoverPrimitive.Trigger>) {\n  return <PopoverPrimitive.Trigger data-slot=\"popover-trigger\" {...props} />\n}\n\nconst focusable = 'a[href], button:not([disabled]), input:not([disabled]):not([type=\"hidden\"]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])'\n\nfunction PopoverContent({\n  className,\n  align = \"center\",\n  sideOffset = 4,\n  // Keep clear of the screen's edges, so on a phone it never touches them.\n  collisionPadding = 8,\n  onOpenAutoFocus,\n  ref,\n  ...props\n}: React.ComponentProps<typeof PopoverPrimitive.Content>) {\n  const portalContainer = usePortalContainer()\n  const content = React.useRef<HTMLDivElement | null>(null)\n  return (\n    <PopoverPrimitive.Portal container={portalContainer}>\n      <PopoverPrimitive.Content\n        ref={(node) => {\n          content.current = node\n          if (typeof ref === \"function\") ref(node)\n          else if (ref) ref.current = node\n        }}\n        data-slot=\"popover-content\"\n        align={align}\n        sideOffset={sideOffset}\n        collisionPadding={collisionPadding}\n        onOpenAutoFocus={(event) => {\n          onOpenAutoFocus?.(event)\n          if (event.defaultPrevented) return\n          // Radix focuses the first thing it can. When that's a radio, it's the group's first, not the picked one Tab\n          // would land on (a color picker opening on Black while Blue is picked): focus the picked one instead.\n          const first = content.current?.querySelector<HTMLElement>(focusable)\n          if (!(first instanceof HTMLInputElement) || first.type !== \"radio\" || !first.name) return\n          const picked = content.current?.querySelector<HTMLInputElement>(`input[type=\"radio\"][name=\"${CSS.escape(first.name)}\"]:checked`)\n          if (!picked || picked === first) return\n          event.preventDefault()\n          picked.focus()\n        }}\n        className={cn(\n          \"z-50 w-72 origin-(--radix-popover-content-transform-origin) rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-hidden data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 data-[state=open]:blur-in-[2px] data-[state=open]:duration-(--duration-standard) data-[state=open]:ease-spring data-[side=bottom]:slide-in-from-top-1 data-[side=top]:slide-in-from-bottom-1 data-[side=left]:slide-in-from-right-1 data-[side=right]:slide-in-from-left-1 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:duration-(--duration-fast) data-[state=closed]:ease-exit motion-reduce:blur-in-0\",\n          className\n        )}\n        {...props}\n      />\n    </PopoverPrimitive.Portal>\n  )\n}\n\nfunction PopoverAnchor(props: React.ComponentProps<typeof PopoverPrimitive.Anchor>) {\n  return <PopoverPrimitive.Anchor data-slot=\"popover-anchor\" {...props} />\n}\n\nexport { Popover, PopoverRoot, PopoverTrigger, PopoverContent, PopoverAnchor, type PopoverProps }\n"}],"cssVars":{"theme":{"ease-standard":"var(--ease-standard)","ease-enter":"var(--ease-enter)","ease-exit":"var(--ease-exit)","ease-spring":"var(--ease-spring)"},"light":{"duration-fast":"160ms","duration-standard":"240ms","duration-considered":"480ms","ease-standard":"cubic-bezier(0.2, 0, 0, 1)","ease-enter":"cubic-bezier(0, 0, 0, 1)","ease-exit":"cubic-bezier(0.3, 0, 1, 1)","ease-spring":"linear(0, 0.071 4%, 0.234 8%, 0.433 12%, 0.626 17%, 0.791 21%, 0.917 25%, 1.004 29%, 1.055 33%, 1.079 38%, 1.083 42%, 1.075 46%, 1.06 50%, 1.043 54%, 1.027 58%, 1.015 62%, 1.005 67%, 0.998 71%, 0.995 75%, 0.993 79%, 0.993 83%, 0.994 88%, 0.995 92%, 0.997 96%, 1)"},"dark":{}}}