@trail-ui/react
Version:
117 lines (107 loc) • 3.27 kB
text/typescript
/**
* This code comes from @react-aria/overlays
*/
import {
AriaOverlayProps,
AriaPositionProps,
ariaHideOutside,
useOverlay,
useOverlayPosition,
usePreventScroll,
} from '@react-aria/overlays';
import { mergeProps, useLayoutEffect } from '@react-aria/utils';
import { DOMAttributes } from '@react-types/shared';
import { RefObject } from 'react';
import { PlacementAxis } from 'react-aria';
import { OverlayTriggerState } from 'react-stately';
export interface AriaPopoverProps
extends AriaOverlayProps,
Omit<AriaPositionProps, 'isOpen' | 'onClose' | 'targetRef' | 'overlayRef'> {
/**
* The ref for the element which the popover positions itself with respect to.
*/
triggerRef: RefObject<Element>;
/**
* The ref for the popover element.
*/
popoverRef: RefObject<Element>;
/**
* Whether the popover is non-modal, i.e. elements outside the popover may be
* interacted with by assistive technologies.
*
* Most popovers should not use this option as it may negatively impact the screen
* reader experience. Only use with components such as combobox, which are designed
* to handle this situation carefully.
*/
isNonModal?: boolean;
/**
* Whether pressing the escape key to close the popover should be disabled.
*
* Most popovers should not use this option. When set to true, an alternative
* way to close the popover with a keyboard must be provided.
*
* @default false
*/
isKeyboardDismissDisabled?: boolean;
}
export interface PopoverAria {
/** Props for the popover element. */
popoverProps: DOMAttributes;
/** Props for the popover tip arrow if any. */
arrowProps: DOMAttributes;
/** Props to apply to the underlay element, if any. */
underlayProps: DOMAttributes;
/** Placement of the popover with respect to the trigger. */
placement: PlacementAxis;
}
/**
* Provides the behavior and accessibility implementation for a popover component.
* A popover is an overlay element positioned relative to a trigger.
*/
export function usePopover(props: AriaPopoverProps, state: OverlayTriggerState): PopoverAria {
const {
triggerRef,
popoverRef,
isNonModal,
isKeyboardDismissDisabled,
shouldCloseOnInteractOutside,
...otherProps
} = props;
const { overlayProps, underlayProps } = useOverlay(
{
isOpen: state.isOpen,
onClose: state.close,
shouldCloseOnBlur: true,
// isDismissable: !isNonModal,
isDismissable: true,
isKeyboardDismissDisabled,
shouldCloseOnInteractOutside: shouldCloseOnInteractOutside,
},
popoverRef,
);
const {
overlayProps: positionProps,
arrowProps,
placement,
} = useOverlayPosition({
...otherProps,
targetRef: triggerRef,
overlayRef: popoverRef,
isOpen: state.isOpen,
onClose: () => {},
});
usePreventScroll({
isDisabled: isNonModal || !state.isOpen,
});
useLayoutEffect(() => {
if (state.isOpen && !isNonModal && popoverRef.current) {
return ariaHideOutside([popoverRef.current]);
}
}, [isNonModal, state.isOpen, popoverRef]);
return {
popoverProps: mergeProps(overlayProps, positionProps),
arrowProps,
underlayProps,
placement: placement as PlacementAxis,
};
}