import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { forwardRef, useEffect, useRef, useCallback, useState, useLayoutEffect } from 'react';
import Progress, {} from '../Progress';
import { getFocusables } from '../../utils';
import Backdrop from '../Backdrop';
import Flex from '../Flex';
import Button from '../Button';
import { useConsolidatedRef, useI18n, useOuterEvent, useUID, useFocusTrap, usePrevious, useModalContext, useEvent, useTheme } from '../../hooks';
import Icon, { registerIcon } from '../Icon';
import * as timesIcon from '../Icon/icons/times.icon';
import * as minusIcon from '../Icon/icons/minus.icon';
import * as dockIcon from '../Icon/icons/dock.icon';
import * as undockIcon from '../Icon/icons/undock.icon';
import Text from '../Text';
import { Count } from '../Badges';
import ResponsiveActions from '../ResponsiveActions';
import { getCSSTimeInMilliseconds } from '../../styles';
import MinimizedModal from './MinimizedModal';
import { StyledModal, StyledModalHeader, StyledModalContent, StyledModalChildrenWrap } from './Modal.styles';
registerIcon(timesIcon, minusIcon, dockIcon, undockIcon);
const Modal = forwardRef(function Modal(props, ref) {
    const { as = 'div', progress = false, heading, headingTag = 'h2', count, children, actions, stretch = false, center = false, defaultFocus, onBeforeOpen, onAfterOpen, onBeforeClose, onAfterClose, onRequestDismiss, onRequestMinimize, onRequestMaximize, onRequestDock, onRequestActivate, ...restProps } = props;
    const { alert, dismissible, minimizable, maximizable, dockable, state, dismiss, minimize, maximize, dock, unmount, top } = useModalContext();
    const modalRef = useConsolidatedRef(ref);
    const modalContentRef = useRef(null);
    const modalActionsRef = useRef(null);
    const backdropRef = useRef(null);
    const closeButtonRef = useRef(null);
    const previousState = usePrevious(state);
    const id = useUID();
    const t = useI18n();
    const [progressMessage, setProgressMessage] = useState(undefined);
    const [isCloseButtonDisabled, setIsCloseButtonDisabled] = useState(true);
    const theme = useTheme();
    const tryDismiss = () => {
        if (onRequestDismiss?.() !== false) {
            dismiss();
        }
    };
    const setFocus = () => {
        if (progress)
            return;
        const focusables = getFocusables(modalRef);
        if (defaultFocus?.current) {
            defaultFocus.current.focus();
        }
        else {
            const focusable = focusables.find(el => {
                return modalContentRef.current?.contains(el);
            }) ??
                closeButtonRef.current ??
                focusables[0];
            focusable?.focus();
        }
    };
    useFocusTrap(modalRef, false);
    useEvent('shortcut', e => e.stopPropagation(), { eventOptions: { capture: true } });
    useOuterEvent('mousedown', [modalRef], e => {
        if (['minimized', 'docked', 'closed'].includes(state))
            return;
        if (dismissible) {
            if (e.target === backdropRef.current) {
                tryDismiss();
            }
        }
        else {
            modalRef.current?.focus();
        }
    });
    useEffect(() => {
        if (state === 'minimized')
            return;
        if (state === 'open' || state === 'maximized' || state === 'docked')
            setFocus();
        if (state === 'docked')
            setIsCloseButtonDisabled(false);
        // This won't actually run until potential changes to docked modal handling.
        if (state === 'closed' && previousState === 'docked') {
            onBeforeClose?.();
            unmount();
            onAfterClose?.();
        }
    }, [state]);
    useEffect(() => {
        // If a modal is already in a closed state on initial mount, unmount it.
        // This only happens when a docked modal closes since the ModalManager
        // remounts it outside of the DockedModals container if state !== 'docked'.
        if (state === 'closed') {
            onBeforeClose?.();
            unmount();
            onAfterClose?.();
        }
    }, []);
    // Dismiss on Escape needs to be handled using React event handling to be cancelable using SyntheticEvent.stopPropagation.
    const onModalKeyDown = useCallback((e) => {
        if (dismissible && (state === 'open' || state === 'maximized') && e.key === 'Escape' && top) {
            tryDismiss();
        }
    }, [dismissible, state, top, tryDismiss]);
    const onHeaderActionKeyDown = useCallback((e) => {
        if (e.repeat && (e.key === 'Enter' || e.key === ' '))
            e.preventDefault();
    }, []);
    useEffect(() => {
        if (!progress) {
            setFocus();
        }
        if (typeof progress === 'object' && progress.message !== progressMessage) {
            setProgressMessage(progress.message);
        }
        else if (progress === true) {
            setProgressMessage(t('loading'));
        }
    }, [progress]);
    // Animate enter and exit
    useLayoutEffect(() => {
        if (!modalRef.current)
            return;
        const translate = ['0 -4rem', '0 0'];
        const options = {
            // Multiplier needed to coordinate with Backdrop's transition speed multiplier
            duration: getCSSTimeInMilliseconds(theme.base.animation.speed) * 2,
            easing: theme.base.animation.timing.ease,
            fill: 'forwards'
        };
        let animation;
        if (state === 'open') {
            animation = modalRef.current.animate({ translate }, options);
        }
        else if (state === 'closed' && previousState === 'open') {
            animation = modalRef.current.animate({ translate: translate.reverse() }, options);
        }
        // Handler will not leak since animation object is garbage collected
        animation?.addEventListener('finish', () => {
            // Removes the element from the compositor.
            animation.cancel();
        });
    }, [state]);
    const maximized = stretch || state === 'maximized';
    const modal = (_jsxs(Flex, { ...restProps, container: { direction: 'column' }, ref: modalRef, as: StyledModal, forwardedAs: as, onClick: (e) => e.stopPropagation(), onKeyDown: onModalKeyDown, alert: alert, role: alert ? 'alertdialog' : 'dialog', stretch: maximized, center: center, "aria-labelledby": `${id}-heading`, "aria-describedby": alert ? `${id}-content` : undefined, "aria-modal": state !== 'docked', state: state, "aria-busy": !!progress, children: [_jsxs(Flex, { container: { justify: 'between', alignItems: 'start', gap: 1 }, item: { shrink: 0 }, as: StyledModalHeader, role: 'none', hasAction: minimizable || dismissible, children: [_jsxs(Flex, { container: { alignItems: 'center', gap: 1 }, as: Text, variant: headingTag, id: `${id}-heading`, children: [_jsx("span", { children: heading }), typeof count === 'number' && _jsx(Count, { children: count })] }), _jsxs(Flex, { container: true, children: [maximizable && !alert && state !== 'maximized' && (_jsx(Button, { icon: true, variant: 'simple', label: t('maximize'), "aria-label": t('modal_maximize'), onKeyDown: onHeaderActionKeyDown, onClick: () => {
                                    if (onRequestMaximize?.() !== false) {
                                        maximize();
                                    }
                                }, children: _jsx(Icon, { name: 'undock' }) })), dockable && !alert && state !== 'docked' && (_jsx(Button, { icon: true, variant: 'simple', label: t('dock'), "aria-label": t('modal_dock'), onKeyDown: onHeaderActionKeyDown, onClick: () => {
                                    if (onRequestDock?.() !== false) {
                                        dock();
                                    }
                                }, children: _jsx(Icon, { name: 'dock' }) })), minimizable && !alert && (_jsx(Button, { icon: true, variant: 'simple', label: t('minimize'), "aria-label": t('modal_minimize'), onKeyDown: onHeaderActionKeyDown, onClick: () => {
                                    if (onRequestMinimize?.() !== false) {
                                        minimize();
                                    }
                                }, children: _jsx(Icon, { name: 'minus' }) })), dismissible && !alert && (_jsx(Button, { icon: true, variant: 'simple', label: t('close'), "aria-label": t('modal_close'), onKeyDown: onHeaderActionKeyDown, onClick: () => {
                                    if (!isCloseButtonDisabled) {
                                        tryDismiss();
                                    }
                                }, ref: closeButtonRef, children: _jsx(Icon, { name: 'times' }) }))] })] }), _jsxs(Flex, { item: { grow: 1 }, as: StyledModalContent, ref: modalContentRef, style: modalContentRef.current
                    ? { '--scroll-top': `${modalContentRef.current.scrollTop}px` }
                    : undefined, children: [children && (_jsx(StyledModalChildrenWrap, { id: `${id}-content`, inert: progress ? '' : undefined, children: children })), _jsx(Progress, { visible: !!progress, focusOnVisible: true, delay: true, placement: 'local', message: progressMessage, onTransitionEndOut: () => setProgressMessage(undefined) })] }), actions && (_jsx(ResponsiveActions, { as: 'footer', role: 'none', actionsRef: modalActionsRef, actionsEl: actions }))] }));
    const renderModal = state === 'docked' ? (modal) : (_jsx(Backdrop, { ref: backdropRef, container: {
            alignItems: center || maximized ? 'center' : 'start'
        }, transitionSpeed: 'slow', open: state === 'open' || state === 'maximized', onBeforeTransitionIn: onBeforeOpen, onAfterTransitionIn: () => {
            setIsCloseButtonDisabled(false);
            onAfterOpen?.();
        }, onBeforeTransitionOut: onBeforeClose, onAfterTransitionOut: () => {
            unmount();
            onAfterClose?.();
        }, children: modal }));
    return state === 'minimized' ? _jsx(MinimizedModal, { ...props, ...restProps }) : renderModal;
});
export default Modal;
//# sourceMappingURL=Modal.js.map