Popover
Floating panel anchored to a trigger button with click-outside dismissal.
components/ui/popover.tsx
'use client'
import * as React from 'react'
import * as PopoverPrimitive from '@radix-ui/react-popover'
import { cn } from '@/lib/utils'
export const PopoverRoot = PopoverPrimitive.Root
export const PopoverTrigger = PopoverPrimitive.Trigger
export const PopoverAnchor = PopoverPrimitive.Anchor
export const PopoverClose = PopoverPrimitive.Close
export const PopoverContent = React.forwardRef<
React.ElementRef<typeof PopoverPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>
>(({ className, align = 'center', sideOffset = 10, ...props }, ref) => (
<PopoverPrimitive.Portal>
<PopoverPrimitive.Content
ref={ref}
align={align}
sideOffset={sideOffset}
className={cn(
'z-[9999] w-72 rounded-[20px] border-3 border-rk-ink bg-white p-4 font-sans text-rk-ink rk-shadow-lg outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95',
className
)}
{...props}
/>
</PopoverPrimitive.Portal>
))
PopoverContent.displayName = PopoverPrimitive.Content.displayName
/* ------------------------------------------------------------------ */
/* Legacy compatibility wrapper (compound root is `PopoverRoot`) */
/* ------------------------------------------------------------------ */
export interface PopoverProps {
trigger: React.ReactNode
children: React.ReactNode
align?: 'start' | 'center' | 'end'
side?: 'top' | 'bottom' | 'left' | 'right'
sideOffset?: number
className?: string
closeOnClickOutside?: boolean
open?: boolean
onOpenChange?: (open: boolean) => void
}
export function Popover({
trigger,
children,
align = 'center',
side = 'bottom',
sideOffset = 10,
className,
closeOnClickOutside = true,
open: controlledOpen,
onOpenChange,
}: PopoverProps) {
return (
<PopoverRoot
open={controlledOpen}
onOpenChange={(next) => {
if (closeOnClickOutside || next) {
onOpenChange?.(next)
}
}}
>
<PopoverTrigger asChild>
<span className="inline-flex cursor-pointer">{trigger}</span>
</PopoverTrigger>
<PopoverContent align={align} side={side} sideOffset={sideOffset} className={className}>
{children}
</PopoverContent>
</PopoverRoot>
)
}
- Interaction
- tooltip
DependenciesBundle weight · medium
- animation
- motion