{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"popover","type":"registry:ui","title":"Popover","description":"A frosted panel that scales in from its trigger and animates out before it closes.","dependencies":["@radix-ui/react-popover@^1.1.23","motion@^14.0.0"],"files":[{"path":"components/ui/popover.tsx","type":"registry:ui","content":"\"use client\";\n\nimport * as PopoverPrimitive from \"@radix-ui/react-popover\";\nimport { AnimatePresence, motion } from \"motion/react\";\nimport * as React from \"react\";\nimport { createAnimatedRoot, popMotion } from \"@/lib/motion\";\nimport { cn } from \"@/lib/utils\";\n\nconst [Popover, usePopoverOpenState] = createAnimatedRoot(PopoverPrimitive.Root, \"popover\");\n\nfunction PopoverTrigger({ ...props }: React.ComponentProps<typeof PopoverPrimitive.Trigger>) {\n  return <PopoverPrimitive.Trigger data-slot=\"popover-trigger\" {...props} />;\n}\n\nfunction PopoverContent({\n  className,\n  align = \"center\",\n  sideOffset = 4,\n  children,\n  ...props\n}: React.ComponentProps<typeof PopoverPrimitive.Content>) {\n  const { open, onExitComplete } = usePopoverOpenState();\n\n  return (\n    <AnimatePresence onExitComplete={onExitComplete}>\n      {open && (\n        <PopoverPrimitive.Portal forceMount>\n          <PopoverPrimitive.Content\n            data-slot=\"popover-content\"\n            align={align}\n            sideOffset={sideOffset}\n            forceMount\n            className=\"z-50 outline-hidden\"\n            {...props}\n          >\n            <motion.div\n              {...popMotion}\n              className={cn(\n                \"frosted text-popover-foreground w-72 origin-(--radix-popover-content-transform-origin) rounded-md border p-4 shadow-float\",\n                className,\n              )}\n            >\n              {children}\n            </motion.div>\n          </PopoverPrimitive.Content>\n        </PopoverPrimitive.Portal>\n      )}\n    </AnimatePresence>\n  );\n}\n\nfunction PopoverAnchor({ ...props }: React.ComponentProps<typeof PopoverPrimitive.Anchor>) {\n  return <PopoverPrimitive.Anchor data-slot=\"popover-anchor\" {...props} />;\n}\n\nexport { Popover, PopoverAnchor, PopoverContent, PopoverTrigger };\n"},{"path":"lib/motion.ts","type":"registry:lib","content":"\"use client\";\n\nimport * as React from \"react\";\nimport { useControllableState } from \"@/hooks/use-controllable-state\";\nimport type { MotionProps, Transition } from \"motion/react\";\n\nexport const chewyEase = [0.32, 0.72, 0, 1] as const;\n\nexport const growTransition = { duration: 0.6, ease: chewyEase };\n\nconst springIn: Transition = { type: \"spring\", bounce: 0.2, duration: 0.3 };\nconst quickOut: Transition = { duration: 0.12, ease: \"easeIn\" };\n\nexport const popMotion = {\n  initial: { opacity: 0, scale: 0.96 },\n  animate: { opacity: 1, scale: 1, transition: springIn },\n  exit: { opacity: 0, scale: 0.96, transition: quickOut },\n};\n\nexport const tooltipMotion = {\n  initial: { opacity: 0, scale: 0.94 },\n  animate: { opacity: 1, scale: 1, transition: { ...springIn, duration: 0.2 } },\n  exit: { opacity: 0, scale: 0.94, transition: { duration: 0.08 } },\n};\n\nexport const dialogMotion = {\n  initial: { opacity: 0, scale: 0.95, y: 8 },\n  animate: { opacity: 1, scale: 1, y: 0, transition: { type: \"spring\", bounce: 0.25, duration: 0.4 } as Transition },\n  exit: { opacity: 0, scale: 0.97, y: 4, transition: { duration: 0.15, ease: \"easeIn\" } as Transition },\n};\n\nexport const overlayMotion = {\n  initial: { opacity: 0 },\n  animate: { opacity: 1, transition: { duration: 0.2 } },\n  exit: { opacity: 0, transition: { duration: 0.15 } },\n};\n\nexport type PanelSide = \"top\" | \"right\" | \"bottom\" | \"left\";\n\nexport function panelMotion(side: PanelSide): Pick<MotionProps, \"initial\" | \"animate\" | \"exit\"> {\n  const offscreen = side === \"left\" || side === \"top\" ? \"calc(-100% - 1rem)\" : \"calc(100% + 1rem)\";\n  const horizontal = side === \"left\" || side === \"right\";\n  const hidden = horizontal ? { x: offscreen } : { y: offscreen };\n  const shown = horizontal ? { x: 0 } : { y: 0 };\n\n  return {\n    initial: hidden,\n    animate: { ...shown, transition: { duration: 0.45, ease: chewyEase } },\n    exit: { ...hidden, transition: { duration: 0.3, ease: chewyEase } },\n  };\n}\n\nexport function useAnimatedOpen(\n  openProp: boolean | undefined,\n  defaultOpen: boolean | undefined,\n  onOpenChange: ((open: boolean) => void) | undefined,\n) {\n  const [open, setOpen] = useControllableState(openProp, defaultOpen ?? false, onOpenChange);\n  const [exiting, setExiting] = React.useState(false);\n  const [wasOpen, setWasOpen] = React.useState(open);\n\n  if (open !== wasOpen) {\n    setWasOpen(open);\n    setExiting(!open);\n  }\n\n  const onExitComplete = React.useCallback(() => setExiting(false), []);\n\n  return { open, setOpen, radixOpen: open || exiting, onExitComplete };\n}\n\ntype AnimatedOpenState = { open: boolean; onExitComplete: () => void };\n\ntype OpenProps = { open?: boolean; defaultOpen?: boolean; onOpenChange?: (open: boolean) => void };\n\nexport function createAnimatedRoot<P extends OpenProps>(Primitive: React.ComponentType<P>, slot: string) {\n  const Context = React.createContext<AnimatedOpenState>({ open: false, onExitComplete: () => {} });\n\n  function AnimatedRoot({ open: openProp, defaultOpen, onOpenChange, ...props }: P) {\n    const { open, setOpen, radixOpen, onExitComplete } = useAnimatedOpen(openProp, defaultOpen, onOpenChange);\n    const value = React.useMemo(() => ({ open, onExitComplete }), [open, onExitComplete]);\n    const primitiveProps = { \"data-slot\": slot, ...props, open: radixOpen, onOpenChange: setOpen } as unknown as P;\n\n    return React.createElement(Context.Provider, { value }, React.createElement(Primitive, primitiveProps));\n  }\n\n  return [AnimatedRoot, () => React.useContext(Context)] as const;\n}\n"},{"path":"hooks/use-controllable-state.ts","type":"registry:hook","content":"import * as React from \"react\";\n\nexport function useControllableState<T>(prop: T | undefined, defaultProp: T, onChange?: (value: T) => void) {\n  const [uncontrolled, setUncontrolled] = React.useState(defaultProp);\n  const isControlled = prop !== undefined;\n  const value = isControlled ? prop : uncontrolled;\n\n  const setValue = React.useCallback(\n    (next: T) => {\n      if (!isControlled) setUncontrolled(next);\n      onChange?.(next);\n    },\n    [isControlled, onChange],\n  );\n\n  return [value, setValue] as const;\n}\n"}],"cssVars":{"theme":{"shadow-float":"0 16px 36px -12px oklch(0 0 0 / 0.2), 0 2px 6px -2px oklch(0 0 0 / 0.08)"}},"css":{"@utility frosted":{"background-color":"color-mix(in oklab, var(--popover) 65%, transparent)","backdrop-filter":"blur(20px) saturate(1.6)"}}}