refactor(web): align overlay primitive content prop pass-through

- add positioner/popup/list pass-through props for select/popover/dropdown wrappers

- keep existing semantic placement API while enabling Base UI behavioral extensibility

- align wrapper ergonomics across overlay primitives
This commit is contained in:
yyh 2026-03-02 20:08:09 +08:00
parent a9266fb7ed
commit 4562a11903
No known key found for this signature in database
3 changed files with 59 additions and 3 deletions

View File

@ -25,14 +25,24 @@ type DropdownMenuContentProps = {
alignOffset?: number
className?: string
popupClassName?: string
positionerProps?: Omit<
React.ComponentPropsWithoutRef<typeof Menu.Positioner>,
'children' | 'className' | 'side' | 'align' | 'sideOffset' | 'alignOffset'
>
popupProps?: Omit<
React.ComponentPropsWithoutRef<typeof Menu.Popup>,
'children' | 'className'
>
}
type DropdownMenuPopupProps = Required<Pick<DropdownMenuContentProps, 'children'>> & {
type DropdownMenuPopupRenderProps = Required<Pick<DropdownMenuContentProps, 'children'>> & {
placement: Placement
sideOffset: number
alignOffset: number
className?: string
popupClassName?: string
positionerProps?: DropdownMenuContentProps['positionerProps']
popupProps?: DropdownMenuContentProps['popupProps']
}
function renderDropdownMenuPopup({
@ -42,7 +52,9 @@ function renderDropdownMenuPopup({
alignOffset,
className,
popupClassName,
}: DropdownMenuPopupProps) {
positionerProps,
popupProps,
}: DropdownMenuPopupRenderProps) {
const { side, align } = parsePlacement(placement)
return (
@ -53,6 +65,7 @@ function renderDropdownMenuPopup({
sideOffset={sideOffset}
alignOffset={alignOffset}
className={cn('outline-none', className)}
{...positionerProps}
>
<Menu.Popup
className={cn(
@ -60,6 +73,7 @@ function renderDropdownMenuPopup({
'origin-[var(--transform-origin)] transition-[transform,scale,opacity] data-[ending-style]:scale-95 data-[starting-style]:scale-95 data-[ending-style]:opacity-0 data-[starting-style]:opacity-0',
popupClassName,
)}
{...popupProps}
>
{children}
</Menu.Popup>
@ -75,6 +89,8 @@ export function DropdownMenuContent({
alignOffset = 0,
className,
popupClassName,
positionerProps,
popupProps,
}: DropdownMenuContentProps) {
return renderDropdownMenuPopup({
children,
@ -83,6 +99,8 @@ export function DropdownMenuContent({
alignOffset,
className,
popupClassName,
positionerProps,
popupProps,
})
}
@ -115,6 +133,8 @@ type DropdownMenuSubContentProps = {
alignOffset?: number
className?: string
popupClassName?: string
positionerProps?: DropdownMenuContentProps['positionerProps']
popupProps?: DropdownMenuContentProps['popupProps']
}
export function DropdownMenuSubContent({
@ -124,6 +144,8 @@ export function DropdownMenuSubContent({
alignOffset = 0,
className,
popupClassName,
positionerProps,
popupProps,
}: DropdownMenuSubContentProps) {
return renderDropdownMenuPopup({
children,
@ -132,6 +154,8 @@ export function DropdownMenuSubContent({
alignOffset,
className,
popupClassName,
positionerProps,
popupProps,
})
}

View File

@ -19,6 +19,14 @@ type PopoverContentProps = {
alignOffset?: number
className?: string
popupClassName?: string
positionerProps?: Omit<
React.ComponentPropsWithoutRef<typeof BasePopover.Positioner>,
'children' | 'className' | 'side' | 'align' | 'sideOffset' | 'alignOffset'
>
popupProps?: Omit<
React.ComponentPropsWithoutRef<typeof BasePopover.Popup>,
'children' | 'className'
>
}
export function PopoverContent({
@ -28,6 +36,8 @@ export function PopoverContent({
alignOffset = 0,
className,
popupClassName,
positionerProps,
popupProps,
}: PopoverContentProps) {
const { side, align } = parsePlacement(placement)
@ -39,6 +49,7 @@ export function PopoverContent({
sideOffset={sideOffset}
alignOffset={alignOffset}
className={cn('outline-none', className)}
{...positionerProps}
>
<BasePopover.Popup
className={cn(
@ -46,6 +57,7 @@ export function PopoverContent({
'origin-[var(--transform-origin)] transition-[transform,scale,opacity] data-[ending-style]:scale-95 data-[starting-style]:scale-95 data-[ending-style]:opacity-0 data-[starting-style]:opacity-0',
popupClassName,
)}
{...popupProps}
>
{children}
</BasePopover.Popup>

View File

@ -42,6 +42,18 @@ type SelectContentProps = {
className?: string
popupClassName?: string
listClassName?: string
positionerProps?: Omit<
React.ComponentPropsWithoutRef<typeof BaseSelect.Positioner>,
'children' | 'className' | 'side' | 'align' | 'sideOffset' | 'alignOffset'
>
popupProps?: Omit<
React.ComponentPropsWithoutRef<typeof BaseSelect.Popup>,
'children' | 'className'
>
listProps?: Omit<
React.ComponentPropsWithoutRef<typeof BaseSelect.List>,
'children' | 'className'
>
}
export function SelectContent({
@ -52,6 +64,9 @@ export function SelectContent({
className,
popupClassName,
listClassName,
positionerProps,
popupProps,
listProps,
}: SelectContentProps) {
const { side, align } = parsePlacement(placement)
@ -63,6 +78,7 @@ export function SelectContent({
sideOffset={sideOffset}
alignOffset={alignOffset}
className={cn('outline-none', className)}
{...positionerProps}
>
<BaseSelect.Popup
className={cn(
@ -70,8 +86,12 @@ export function SelectContent({
'origin-[var(--transform-origin)] transition-[transform,scale,opacity] data-[ending-style]:scale-95 data-[starting-style]:scale-95 data-[ending-style]:opacity-0 data-[starting-style]:opacity-0',
popupClassName,
)}
{...popupProps}
>
<BaseSelect.List className={cn('max-h-80 min-w-[10rem] overflow-auto p-1 outline-none', listClassName)}>
<BaseSelect.List
className={cn('max-h-80 min-w-[10rem] overflow-auto p-1 outline-none', listClassName)}
{...listProps}
>
{children}
</BaseSelect.List>
</BaseSelect.Popup>