import { useDocumentRect } from '@/Hooks/useDocumentRect' import { useAutoElementRect } from '@/Hooks/useElementRect' import { classNames } from '@standardnotes/utils' import { CSSProperties, useCallback, useLayoutEffect, useRef, useState } from 'react' import Portal from '../Portal/Portal' import { PopoverCSSProperties, getPositionedPopoverStyles } from './GetPositionedPopoverStyles' import { PopoverContentProps } from './Types' import { usePopoverCloseOnClickOutside } from './Utils/usePopoverCloseOnClickOutside' import { useDisableBodyScrollOnMobile } from '@/Hooks/useDisableBodyScrollOnMobile' import { MediaQueryBreakpoints, useMediaQuery } from '@/Hooks/useMediaQuery' import { KeyboardKey } from '@standardnotes/ui-services' import { getAdjustedStylesForNonPortalPopover } from './Utils/getAdjustedStylesForNonPortal' import { DialogWithClose } from '@/Utils/CloseOpenModalsAndPopovers' import { mergeRefs } from '@/Hooks/mergeRefs' const PositionedPopoverContent = ({ align = 'end', anchorElement, anchorPoint, children, childPopovers, className, id, overrideZIndex, side = 'bottom', togglePopover, disableClickOutside, disableMobileFullscreenTakeover, disableFlip, maxHeight, portal = true, offset, hideOnClickInModal = false, setAnimationElement, containerClassName, }: PopoverContentProps) => { const [popoverElement, setPopoverElement] = useState(null) const popoverRect = useAutoElementRect(popoverElement) const resolvedAnchorElement = anchorElement && 'current' in anchorElement ? anchorElement.current : anchorElement const anchorElementRect = useAutoElementRect(resolvedAnchorElement, { updateOnWindowResize: true, }) const anchorPointRect = DOMRect.fromRect({ x: anchorPoint?.x, y: anchorPoint?.y, }) const anchorRect = anchorPoint ? anchorPointRect : anchorElementRect const documentRect = useDocumentRect() const isDesktopScreen = useMediaQuery(MediaQueryBreakpoints.md) const styles = getPositionedPopoverStyles({ align, anchorRect, documentRect, popoverRect: popoverRect ?? popoverElement?.getBoundingClientRect(), side, disableMobileFullscreenTakeover, disableFlip, maxHeightFunction: maxHeight, offset, }) if (!styles) { document.body.style.overflow = 'hidden' } useLayoutEffect(() => { return () => { document.body.style.overflow = '' } }, []) let adjustedStyles: PopoverCSSProperties | undefined = undefined if (!portal && popoverElement && styles) { adjustedStyles = getAdjustedStylesForNonPortalPopover(popoverElement, styles) } usePopoverCloseOnClickOutside({ popoverElement, anchorElement: resolvedAnchorElement, togglePopover, childPopovers, hideOnClickInModal, disabled: disableClickOutside, }) useDisableBodyScrollOnMobile() const canCorrectInitialScroll = useRef(true) const correctInitialScrollForOverflowedContent = useCallback((element: HTMLElement | null) => { if (element && element.scrollTop > 0 && canCorrectInitialScroll.current) { element.scrollTop = 0 canCorrectInitialScroll.current = false } }, []) const addCloseMethod = useCallback( (element: HTMLDivElement | null) => { if (element && togglePopover) { ;(element as DialogWithClose).close = togglePopover } }, [togglePopover], ) return (
{ if (event.key === KeyboardKey.Escape) { event.stopPropagation() togglePopover?.() if (resolvedAnchorElement) { resolvedAnchorElement.focus() } } }} onBlur={() => { setTimeout(() => { if (document.activeElement && document.activeElement.tagName === 'IFRAME') { togglePopover?.() } }) }} >
{ canCorrectInitialScroll.current = false }} > {children}
) } export default PositionedPopoverContent