From bc84912e4881f0864cab856c749841b6434d62bc Mon Sep 17 00:00:00 2001 From: Maofeng Date: Sat, 8 Aug 2026 21:13:43 +0800 Subject: [PATCH] fix(ui): preserve popover arrows around scrollable content Keep the anchored surface overflow-visible so the arrow is not clipped. Move height-constrained scrolling into an inner viewport and expose viewportClassName for content-specific styling. --- packages/ui/src/components/popover.tsx | 30 ++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/packages/ui/src/components/popover.tsx b/packages/ui/src/components/popover.tsx index 4498419..0d23e89 100644 --- a/packages/ui/src/components/popover.tsx +++ b/packages/ui/src/components/popover.tsx @@ -41,6 +41,11 @@ type PopoverContentProps = PopoverPrimitive.Popup.Props & * with the `render` prop when both are provided. */ showArrow?: boolean | Omit + /** + * Styles the scrollable content viewport when `showArrow` is enabled. + * The outer surface stays overflow-visible so its tail is not clipped. + */ + viewportClassName?: string } function PopoverContent({ @@ -55,6 +60,7 @@ function PopoverContent({ showArrow = false, side = "bottom", sideOffset, + viewportClassName, ...props }: PopoverContentProps) { const usesAnchoredSurface = Boolean(showArrow) @@ -77,6 +83,10 @@ function PopoverContent({ ) : ( ) + const anchoredSurfaceBorderWidth = Math.max( + 0, + anchoredSurfaceProps?.borderWidth ?? 1 + ) return ( @@ -94,7 +104,8 @@ function PopoverContent({ data-slot="popover-content" className={cn( "z-50 flex w-72 origin-(--transform-origin) flex-col gap-4 rounded-md bg-popover p-4 text-sm text-popover-foreground shadow-md ring-1 ring-foreground/10 outline-hidden duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-start-2 data-[side=inline-start]:slide-in-from-end-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", - usesAnchoredSurface && "bg-transparent shadow-none ring-0", + usesAnchoredSurface && + "!overflow-visible bg-transparent shadow-none ring-0", className )} render={resolvedRender} @@ -103,7 +114,22 @@ function PopoverContent({ {usesAnchoredSurface && ( } /> )} - {children} + {usesAnchoredSurface ? ( +
+ {children} +
+ ) : ( + children + )}