fix(popover): correct initial animation direction to match fallback placement (#4460)

* fix(popover): correct initial animation direction to match fallback placement

* fix: type error

* chore: add changeset
This commit is contained in:
Ryo Matsukawa 2024-12-30 21:08:09 +09:00 committed by GitHub
parent 4f0ef5818b
commit fb46df2430
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 42 additions and 21 deletions

View File

@ -0,0 +1,5 @@
---
"@nextui-org/popover": patch
---
Fix initial animation direction to match fallback placement (#4290)

View File

@ -14,7 +14,7 @@ import {
PropGetter, PropGetter,
useProviderContext, useProviderContext,
} from "@nextui-org/system"; } from "@nextui-org/system";
import {getArrowPlacement, getShouldUseAxisPlacement} from "@nextui-org/aria-utils"; import {getArrowPlacement} from "@nextui-org/aria-utils";
import {popover} from "@nextui-org/theme"; import {popover} from "@nextui-org/theme";
import {mergeProps, mergeRefs} from "@react-aria/utils"; import {mergeProps, mergeRefs} from "@react-aria/utils";
import {clsx, dataAttr, objectToDeps} from "@nextui-org/shared-utils"; import {clsx, dataAttr, objectToDeps} from "@nextui-org/shared-utils";
@ -81,6 +81,8 @@ export interface Props extends HTMLNextUIProps<"div"> {
onClose?: () => void; onClose?: () => void;
} }
const DEFAULT_PLACEMENT = "top";
export type UsePopoverProps = Props & export type UsePopoverProps = Props &
Omit<ReactAriaPopoverProps, "triggerRef" | "popoverRef"> & Omit<ReactAriaPopoverProps, "triggerRef" | "popoverRef"> &
OverlayTriggerProps & OverlayTriggerProps &
@ -110,7 +112,7 @@ export function usePopover(originalProps: UsePopoverProps) {
portalContainer, portalContainer,
updatePositionDeps, updatePositionDeps,
dialogProps: dialogPropsProp, dialogProps: dialogPropsProp,
placement: placementProp = "top", placement: placementProp = DEFAULT_PLACEMENT,
triggerType = "dialog", triggerType = "dialog",
showArrow = false, showArrow = false,
offset = 7, offset = 7,
@ -150,11 +152,7 @@ export function usePopover(originalProps: UsePopoverProps) {
const state = stateProp || innerState; const state = stateProp || innerState;
const { const {popoverProps, underlayProps, placement} = useReactAriaPopover(
popoverProps,
underlayProps,
placement: ariaPlacement,
} = useReactAriaPopover(
{ {
triggerRef, triggerRef,
isNonModal, isNonModal,
@ -208,7 +206,7 @@ export function usePopover(originalProps: UsePopoverProps) {
"data-focus": dataAttr(isFocused), "data-focus": dataAttr(isFocused),
"data-arrow": dataAttr(showArrow), "data-arrow": dataAttr(showArrow),
"data-focus-visible": dataAttr(isFocusVisible), "data-focus-visible": dataAttr(isFocusVisible),
"data-placement": getArrowPlacement(ariaPlacement || "top", placementProp), "data-placement": getArrowPlacement(placement || DEFAULT_PLACEMENT, placementProp),
...mergeProps(focusProps, dialogPropsProp, props), ...mergeProps(focusProps, dialogPropsProp, props),
className: slots.base({class: clsx(baseStyles)}), className: slots.base({class: clsx(baseStyles)}),
style: { style: {
@ -222,18 +220,10 @@ export function usePopover(originalProps: UsePopoverProps) {
"data-slot": "content", "data-slot": "content",
"data-open": dataAttr(state.isOpen), "data-open": dataAttr(state.isOpen),
"data-arrow": dataAttr(showArrow), "data-arrow": dataAttr(showArrow),
"data-placement": getArrowPlacement(ariaPlacement || "top", placementProp), "data-placement": getArrowPlacement(placement || DEFAULT_PLACEMENT, placementProp),
className: slots.content({class: clsx(classNames?.content, props.className)}), className: slots.content({class: clsx(classNames?.content, props.className)}),
}), }),
[slots, state.isOpen, showArrow, ariaPlacement, placementProp, classNames], [slots, state.isOpen, showArrow, placement, placementProp, classNames],
);
const placement = useMemo(
() =>
getShouldUseAxisPlacement(ariaPlacement || "top", placementProp)
? ariaPlacement || placementProp
: placementProp,
[ariaPlacement, placementProp],
); );
const onPress = useCallback( const onPress = useCallback(
@ -317,7 +307,7 @@ export function usePopover(originalProps: UsePopoverProps) {
classNames, classNames,
showArrow, showArrow,
triggerRef, triggerRef,
placement, placement: placement || DEFAULT_PLACEMENT,
isNonModal, isNonModal,
popoverRef: domRef, popoverRef: domRef,
portalContainer, portalContainer,

View File

@ -127,11 +127,11 @@ const content = (
</PopoverContent> </PopoverContent>
); );
const Template = (args: PopoverProps) => { const Template = ({label = "Open Popover", ...args}: PopoverProps & {label: string}) => {
return ( return (
<Popover {...args}> <Popover {...args}>
<PopoverTrigger> <PopoverTrigger>
<Button>Open Popover</Button> <Button>{label}</Button>
</PopoverTrigger> </PopoverTrigger>
{content} {content}
</Popover> </Popover>
@ -581,6 +581,32 @@ export const CustomMotion = {
}, },
}; };
export const WithFallbackPlacements = {
args: {
...defaultProps,
},
render: (args) => (
<div className="relative h-screen w-screen">
<div className="absolute top-0 left-0 p-8 flex gap-4">
<Template {...args} label="placement: top" placement="top" />
<Template {...args} label="placement: bottom" placement="bottom" />
</div>
<div className="absolute bottom-0 left-0 p-8 flex gap-4">
<Template {...args} label="placement: bottom" placement="bottom" />
<Template {...args} label="placement: top" placement="top" />
</div>
<div className="absolute left-0 top-1/2 -translate-y-1/2 p-8 flex flex-col gap-4">
<Template {...args} label="placement: left" placement="left" />
<Template {...args} label="placement: right" placement="right" />
</div>
<div className="absolute right-0 top-1/2 -translate-y-1/2 p-8 flex flex-col gap-4">
<Template {...args} label="placement: right" placement="right" />
<Template {...args} label="placement: left" placement="left" />
</div>
</div>
),
};
export const WithShouldBlockScroll = { export const WithShouldBlockScroll = {
render: (args) => { render: (args) => {
return ( return (