import React, { useContext, useRef } from "react"; import { AriaModalOverlayProps, useModalOverlay } from "@react-aria/overlays"; import { OverlayTriggerState, useOverlayTriggerState } from "react-stately"; import { useContextProps } from "@hooks/useContextProps"; import { useRenderProps } from "@hooks/useRenderProps"; import { OverlayTriggerStateContext } from "../OverlayTrigger/context"; import { BaseModalProps, ModalChildren } from "./Modal.types"; import { ModalContext } from "./Modal.context"; import { ModalWrapper } from "./Modal.styles"; import { ModalOverlay } from "./components/ModalOverlay"; import { ModalHeader } from "./components/ModalHeader"; import { ModalTitle } from "./components/ModalTitle"; import { ModalBody } from "./components/ModalBody"; import { ModalFooter } from "./components/ModalFooter"; import { useManageModalScroll } from "./hooks/useManageModalScroll"; export interface ModalProps extends BaseModalProps { children?: ModalChildren; } /** * Modal Component to render content overlayed on top of the page content. */ export function Modal(props: ModalProps) { let ref = useRef(null); [props, ref] = useContextProps(ModalContext, props, ref); const { children, centered = false, isCentered = centered, ...rest } = props; const contextState = useContext(OverlayTriggerStateContext); const localState = useOverlayTriggerState(props); const state = contextState ?? localState; if (!state.isOpen) { return null; } return ( {children} ); } interface ModalInternalProps extends BaseModalProps, AriaModalOverlayProps { state: OverlayTriggerState; children: ModalChildren; triggerRef?: React.RefObject; } function ModalInternal(props: ModalInternalProps) { const { children, className, style, state, isCentered, isOpen, triggerRef, ensureVisible, ...rest } = props; const ref = useRef(null); const { modalProps, underlayProps } = useModalOverlay(props, state, ref); useManageModalScroll( { ensureVisible, modalRef: ref, scrollOptions: { behavior: "instant", block: "end", inline: "nearest", }, }, state ); const renderProps = useRenderProps({ componentClassName: "aje-modal", children, className, style, }); return ( ) => e.stopPropagation()} ref={ref} data-placement={isCentered ? "center" : "top"} {...rest} {...modalProps} {...renderProps} > {typeof children === "function" ? children(state.close) : children} ); } /** Wrapper around Flex to provide some sensible defaults for Modals */ Modal.Header = ModalHeader; /** Wrapper around Heading to provide some sensible defaults for Modals */ Modal.Title = ModalTitle; /** Wrapper around View to provide some sensible defaults for Modals */ Modal.Body = ModalBody; /** Wrapper around Flex to provide some sensible defaults for Modals */ Modal.Footer = ModalFooter;