import { FloatingFocusManager, FloatingPortal, useClick, useDismiss, useFloating, useInteractions, useMergeRefs, useRole, } from '@floating-ui/react'; import * as React from 'react'; import { FloatingContent, FloatingOverlayStyled } from './FloatingUIDialog.styled'; type FloatingUIDialogOptions = { initialOpen?: boolean; onOpenChange?: (open: boolean) => void; open?: boolean; }; function useFloatingUIDialog({ initialOpen = false, open: controlledOpen, onOpenChange: setControlledOpen, }: FloatingUIDialogOptions = {}) { const [uncontrolledOpen, setUncontrolledOpen] = React.useState(initialOpen); const [labelId, setLabelId] = React.useState(); const [descriptionId, setDescriptionId] = React.useState(); const open = controlledOpen ?? uncontrolledOpen; const setOpen = setControlledOpen ?? setUncontrolledOpen; const data = useFloating({ onOpenChange: setOpen, open, }); const context = data.context; const click = useClick(context, { enabled: controlledOpen == null, }); const dismiss = useDismiss(context, { escapeKey: true, outsidePress: false }); const role = useRole(context); const interactions = useInteractions([click, dismiss, role]); return React.useMemo( () => ({ open, setOpen, ...interactions, ...data, descriptionId, labelId, setDescriptionId, setLabelId, }), [open, setOpen, interactions, data, labelId, descriptionId], ); } type ContextType = | (ReturnType & { setDescriptionId: React.Dispatch>; setLabelId: React.Dispatch>; }) | null; const DialogContext = React.createContext(null); const useFloatingUIDialogContext = () => { const context = React.useContext(DialogContext); if (context == null) { throw new Error('Dialog components must be wrapped in '); } return context; }; export function FloatingUIDialog({ children, ...options }: { children: React.ReactNode; } & FloatingUIDialogOptions) { // This can accept any props as options, e.g. `placement`, // or other positioning options. const dialog = useFloatingUIDialog(options); return {children}; } export const FloatingUIDialogContent = React.forwardRef< HTMLDivElement, React.HTMLProps & { onOverlayClose?: () => void } >(function FloatingUIDialogContent(props, propRef) { const { context: floatingContext, ...context } = useFloatingUIDialogContext(); const ref = useMergeRefs([context.refs.setFloating, propRef]); const handleOverlayMouseDown = (e: React.MouseEvent) => { if (e.target === e.currentTarget) { if (props.onOverlayClose) props.onOverlayClose(); else context.setOpen(false); } }; return ( {context.open && ( {props.children} )} ); });