import { arrow, autoPlacement, autoUpdate, flip, FloatingArrow, Middleware, offset, OpenChangeReason, Placement, useDismiss, useFloating, useInteractions, } from '@floating-ui/react'; import React, { ReactNode, useState } from 'react'; import { useTheme } from '../../context/MagicBellThemeContext.js'; import { NotificationInboxProps } from '../NotificationInbox/index.js'; export interface Props extends NotificationInboxProps { isOpen: boolean; toggle?: () => void; launcherRef: React.RefObject; placement?: Placement; offset?: number | { mainAxis?: number; crossAxis?: number }; arrowPadding?: number; width?: number; closeOnClickOutside?: boolean; hideArrow?: boolean; layout?: string[]; children: ReactNode | ReactNode[]; } /** * Notification inbox in a popover. * * @example * void} isOpen /> */ export default function FloatingInboxContainer({ launcherRef, isOpen = false, toggle, placement, offset: offsetProp = 10, width = 500, closeOnClickOutside = true, hideArrow = false, arrowPadding = 18, children, }: Props) { const [arrowEl, setArrowEl] = useState(null); const theme = useTheme(); const middleware: Middleware[] = [placement ? flip() : autoPlacement(), offset(offsetProp)]; if (!hideArrow) { middleware.push( arrow({ element: arrowEl, padding: arrowPadding, }), ); } const floating = useFloating({ placement, middleware, strategy: 'absolute', open: isOpen, onOpenChange(open: boolean, event?: Event, reason?: OpenChangeReason) { if (reason === 'outside-press') { toggle?.(); } }, elements: { reference: launcherRef.current }, whileElementsMounted: autoUpdate, }); const dismiss = useDismiss(floating.context, { referencePress: false, outsidePress: closeOnClickOutside, }); const { getFloatingProps } = useInteractions([dismiss]); const arrowColor = /bottom/i.test(floating.placement) ? theme.footer.backgroundColor : theme.header.backgroundColor; return ( <> {isOpen ? (
{children} {hideArrow ? null : ( )}
) : null} ); }