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

View File

@ -127,11 +127,11 @@ const content = (
</PopoverContent>
);
const Template = (args: PopoverProps) => {
const Template = ({label = "Open Popover", ...args}: PopoverProps & {label: string}) => {
return (
<Popover {...args}>
<PopoverTrigger>
<Button>Open Popover</Button>
<Button>{label}</Button>
</PopoverTrigger>
{content}
</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 = {
render: (args) => {
return (