UNPKG

@gluestack-ui/core

Version:

Universal UI components for React Native, Expo, and Next.js

87 lines 3.5 kB
import React from 'react'; import { useControllableState } from '@gluestack-ui/utils/hooks'; import { Overlay } from '../../../overlay/creator'; import { PopoverProvider } from './PopoverContext'; import { FocusScope as FocusScopeAria } from '@gluestack-ui/utils/aria'; import { PopoverContent } from './PopoverContent'; import { MenuContext } from '../MenuContext'; export const Popover = ({ state, onOpen, trigger, children, defaultIsOpen = false, initialFocusRef, finalFocusRef, useRNModal, trapFocus = true, placement = 'bottom', shouldOverlapWithTrigger = false, crossOffset, offset, triggerRef, shouldFlip, focusScope = true, StyledBackdrop, }) => { const [isOpen, setIsOpen] = useControllableState({ value: state === null || state === void 0 ? void 0 : state.isOpen, defaultValue: defaultIsOpen, onChange: (value) => { value ? onOpen && onOpen() : state.close && state.close(); }, }); const { onClose } = React.useContext(MenuContext); const [bodyMounted, setBodyMounted] = React.useState(false); const [headerMounted, setHeaderMounted] = React.useState(false); let idCounter = 0; function uniqueId(prefix = '') { const id = ++idCounter; return prefix + id; } const id = uniqueId(); const popoverContentId = `${id}-content`; const headerId = `${popoverContentId}-header`; const bodyId = `${popoverContentId}-body`; const handleOpen = React.useCallback(() => { setIsOpen(true); }, [setIsOpen]); const handleClose = React.useCallback(() => { setIsOpen(false); }, [setIsOpen]); const updatedTrigger = (reference) => { if (trigger) { return trigger({ 'ref': reference, 'onPress': handleOpen, 'aria-expanded': isOpen ? true : false, 'aria-controls': isOpen ? popoverContentId : undefined, 'aria-haspopup': true, }, { open: isOpen }); } return null; }; const targetRefTemp = React.useRef(null); const targetRef = triggerRef || targetRefTemp; return (<> {updatedTrigger(targetRef)} <Overlay isOpen={isOpen} onRequestClose={handleClose} isKeyboardDismissable useRNModal={useRNModal}> <PopoverProvider value={{ onClose: handleClose, targetRef, strategy: 'absolute', handleClose: handleClose, initialFocusRef, finalFocusRef, popoverContentId, bodyId, headerId, headerMounted, bodyMounted, setBodyMounted, setHeaderMounted, isOpen, placement, shouldOverlapWithTrigger, crossOffset, offset, shouldFlip, }}> <StyledBackdrop onPress={onClose} tabIndex={-1} accessibilityElementsHidden importantForAccessibility="no-hide-descendants" aria-hidden={true}/> <FocusScopeComponent trapFocus={trapFocus} focusScope={focusScope}> <PopoverContent>{children}</PopoverContent> </FocusScopeComponent> </PopoverProvider> </Overlay> </>); }; const FocusScopeComponent = ({ trapFocus, focusScope, children }) => { if (focusScope) return (<FocusScopeAria contain={trapFocus} restoreFocus autoFocus> {children} </FocusScopeAria>); return children; }; //# sourceMappingURL=Popover.jsx.map