import { FloatingFocusManager, FloatingOverlay, FloatingPortal, useClick, useDismiss, useFloating, useId, useInteractions, useMergeRefs, useRole, } from "@floating-ui/react"; import { useLocal } from "lib/utils/use-local"; import * as React from "react"; interface ModalOptions { initialOpen?: boolean; open?: boolean; onOpenChange?: (open: boolean) => void; fade?: boolean; } export function useModal({ initialOpen = true, open: controlledOpen, onOpenChange: setControlledOpen, }: ModalOptions) { const [uncontrolledOpen, setUncontrolledOpen] = React.useState(initialOpen); const [labelId, setLabelId] = React.useState(); const [descriptionId, setDescriptionId] = React.useState< string | undefined >(); const open = controlledOpen ?? uncontrolledOpen; const setOpen = setControlledOpen ?? setUncontrolledOpen; const data = useFloating({ open, onOpenChange: setOpen, }); const context = data.context; const click = useClick(context, { enabled: controlledOpen == null, }); const dismiss = useDismiss(context, { outsidePressEvent: "mousedown", escapeKey: false, }); const role = useRole(context); const interactions = useInteractions([click, dismiss, role]); return React.useMemo( () => ({ open, setOpen, ...interactions, ...data, labelId, descriptionId, setLabelId, setDescriptionId, }), [open, setOpen, interactions, data, labelId, descriptionId] ); } type ContextType = | (ReturnType & { setLabelId: React.Dispatch>; setDescriptionId: React.Dispatch< React.SetStateAction >; }) | null; const ModalContext = React.createContext(null); export const useModalContext = () => { const context = React.useContext(ModalContext); if (context == null) { throw new Error("Modal components must be wrapped in "); } return context; }; export function Modal({ children, ...options }: { children: React.ReactNode; } & ModalOptions) { const dialog = useModal(options); return ( {children} ); } interface ModalTriggerProps { children: React.ReactNode; asChild?: boolean; } export const ModalTrigger = React.forwardRef< HTMLElement, React.HTMLProps & ModalTriggerProps >(function ModalTrigger({ children, asChild = false, ...props }, propRef) { const context = useModalContext(); const childrenRef = (children as any).ref; const ref = useMergeRefs([context.refs.setReference, propRef, childrenRef]); // `asChild` allows the user to pass any element as the anchor if (asChild && React.isValidElement(children)) { return React.cloneElement( children, context.getReferenceProps({ ref, ...props, ...children.props, "data-state": context.open ? "open" : "closed", }) ); } return ( ); }); export const ModalContent = React.forwardRef< HTMLDivElement, React.HTMLProps & { fade?: boolean } >(function ModalContent(props, propRef) { const local = useLocal({ preview: false, timeout: null as any }); const { context: floatingContext, ...context } = useModalContext(); const ref = useMergeRefs([context.refs.setFloating, propRef]); if (!floatingContext.open) return null; const _props = { ...props }; if (typeof _props.fade !== "undefined") { delete _props.fade; } const floatingDivProps = context.getFloatingProps(_props); return (
{ if (props.fade !== false) { clearTimeout(local.timeout); if (local.preview) { local.preview = false; local.render(); } } }} onPointerLeave={(e) => { // if (Object.keys(w.openedPopupID || {}).length > 0) { // return; // } if (props.fade !== false) { clearTimeout(local.timeout); local.timeout = setTimeout(() => { local.preview = true; local.render(); }, 1000); } }} aria-labelledby={context.labelId} aria-describedby={context.descriptionId} {...floatingDivProps} > {props.children}
); }); export const ModalHeading = React.forwardRef< HTMLHeadingElement, React.HTMLProps >(function ModalHeading({ children, ...props }, ref) { const { setLabelId } = useModalContext(); const id = useId(); // Only sets `aria-labelledby` on the Modal root element // if this component is mounted inside it. React.useLayoutEffect(() => { setLabelId(id); return () => setLabelId(undefined); }, [id, setLabelId]); return (

{children}

); }); export const ModalDescription = React.forwardRef< HTMLParagraphElement, React.HTMLProps >(function ModalDescription({ children, ...props }, ref) { const { setDescriptionId } = useModalContext(); const id = useId(); // Only sets `aria-describedby` on the Modal root element // if this component is mounted inside it. React.useLayoutEffect(() => { setDescriptionId(id); return () => setDescriptionId(undefined); }, [id, setDescriptionId]); return (

{children}

); }); export const ModalClose = React.forwardRef< HTMLButtonElement, React.ButtonHTMLAttributes >(function ModalClose(props, ref) { const { setOpen } = useModalContext(); return (