/* @oracle/oraclejet-preact: undefined */
import { jsx, jsxs, Fragment } from 'preact/jsx-runtime';
import { forwardRef } from 'preact/compat';
import { W as WindowOverlay } from './WindowOverlay-a07b2e24.js';
import { u as useTestId } from './useTestId-9b504481.js';
import { u as useAnimation } from './useAnimation-a5eb7604.js';
import { useRef, useState, useCallback, useEffect, useMemo } from 'preact/hooks';
import { m as mergeRefs } from './refUtils-b9d8d91a.js';
import { DialogRedwoodTheme } from './UNSAFE_Dialog/themes/redwood/DialogTheme.js';
import { F as FocusTrap } from './FocusTrap-9e80c124.js';
import { L as Layer } from './Layer-446bf21e.js';
import './LayerHost-e6402730.js';
import { M as Modal } from './Modal-24a0bf4b.js';
import { c as classNames } from './classNames-4e12b00d.js';
import { g as globalImages } from './ImageVars.css-7b3180c8.js';
import { u as useComponentTheme } from './useComponentTheme-d2f9e47f.js';
import { u as useId } from './useId-03dbfdf0.js';
import { d as dimensionInterpolations } from './dimensions-d4159ace.js';
import { m as mergeInterpolations } from './mergeInterpolations-e2db2a66.js';
import { u as useResizeObserver } from './useResizeObserver-838164c1.js';
import { u as useDraggable } from './useDraggable-e1aaa8f3.js';
import { F as Floating } from './Floating-f02677ba.js';
import { I as IconButton } from './IconButton-19fcf0a8.js';
import { S as SvgClose } from './Close-0791782f.js';
import { R as Resizable } from './Resizable-ea74f2fd.js';
import { u as useBreakpoints } from './useBreakpoints-13aa87e6.js';
import { u as useTranslationBundle } from './useTranslationBundle-09302c20.js';
import { a as SLIDE_UP_XLARGE, D as DURATION_LARGE, b as SLIDE_DOWN_XLARGE } from './animationUtils-e6081007.js';
import { a as allTabbableElements, f as focusWithin, b as focusOn, g as getActiveElement } from './tabbableUtils-6ceb767f.js';

const StyledDialogHeader = ({ children, id, cancelBehavior, onClose, draggableProps, headerDecoration = 'on' }) => {
    const { styles } = useComponentTheme(DialogRedwoodTheme);
    const classes = classNames([
        styles.dialogHeaderStyle,
        ...(headerDecoration === 'on' ? [styles.dialogHeaderDecorationStyle] : [])
    ]);
    const content = typeof children === 'string' ? (jsx("h1", { id: id, className: styles.dialogTitleStyle, children: children })) : (jsx(Fragment, { children: children }));
    const translations = useTranslationBundle('@oracle/oraclejet-preact');
    const handleIconClose = () => {
        onClose?.({ reason: 'icon' });
    };
    const closeIcon = cancelBehavior === 'icon' ? (jsx("div", { children: jsx("div", { className: styles.dialogHeaderCloseIconStyle, children: jsx(IconButton, { "aria-label": translations.message_close(), size: "sm", variant: "ghost", onAction: handleIconClose, children: jsx(SvgClose, {}) }) }) })) : null;
    return (jsxs("div", { className: classes, ...draggableProps, children: [content, closeIcon] }));
};
const StyledDialogFooter = forwardRef(({ children, style }, ref) => {
    return (jsx("div", { ref: ref, className: style, children: children }));
});
const StyledDialog = forwardRef(({ children, header, footer, visibility, labelId, role, ariaLabelledBy, ariaDescribedBy, cancelBehavior, draggableProps, isResizable, headerDecoration, containerRef, onClose, onResizeStart, onResize, onResizeEnd, onKeyDown, testId, style, footerRef }, ref) => {
    const elementRef = useRef(null);
    const mergedRef = mergeRefs(ref, elementRef);
    const [isSmallHeight, setSmallHeight] = useState(false);
    const isSmallWidth = !useBreakpoints()['md'];
    const { classes: styleClasses, styles } = useComponentTheme(DialogRedwoodTheme);
    const resizeHandler = useCallback(() => {
        if (elementRef.current) {
            if (elementRef.current.clientHeight < 400) {
                setSmallHeight(true);
            }
            else {
                setSmallHeight(false);
            }
        }
    }, []);
    // set initial Height
    useEffect(() => {
        resizeHandler();
    }, [resizeHandler]);
    useResizeObserver(elementRef, resizeHandler);
    // for small widths (mobile), use the 'smallWidth' dialog style
    // otherwise, check for small height to apply 'smallWidth' or 'desktop' styles accordingly
    const baseStyle = isSmallWidth ? styles.baseStyleSmallWidth : styles.baseStyleDesktop;
    // content should scroll depending on the viewport height
    // on large screens, the header and footer should be "sticky",
    // at small viewport heights, the entire content should be scrollable
    const contentStyle = isSmallHeight
        ? styles.dialogContentStyleNoShrink
        : styles.dialogContentStyle;
    const visibilityStyle = visibility === 'visible' ? styles.visibilityVisible : styles.visibilityHidden;
    const testIdProps = useTestId(testId);
    return (jsx(FocusTrap, { restoreFocusRef: false, children: jsxs("div", { ref: mergedRef, tabIndex: -1, className: classNames([styleClasses, baseStyle, visibilityStyle, globalImages]), style: style, onKeyDown: onKeyDown, role: role, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, ...testIdProps, children: [jsxs("div", { className: styles.dialogContainerStyle, children: [jsx(StyledDialogHeader, { id: labelId, onClose: onClose, headerDecoration: headerDecoration, cancelBehavior: cancelBehavior, draggableProps: draggableProps, children: header }), jsx("div", { className: contentStyle, children: jsx("div", { className: styles.dialogBodyStyle, children: children }) }), jsx(StyledDialogFooter, { ref: footerRef, style: styles.dialogFooterStyle, children: footer })] }), isResizable && (jsx(Resizable, { onResizeStart: onResizeStart, onResize: onResize, onResizeEnd: onResizeEnd, containerRef: containerRef }))] }) }));
});
// main Dialog component
/**
 * WAI-ARIA-compliant dialog component. A dialog is a floating window that typically contains
 * a title bar and a content area.
 * The dialog is generally placed on top of the rest of the page content. It can be either modal
 * (only the content in the dialog can be interacted with) or non-modal (it's still possible
 * to interact with content outside of the dialog).
 */
const Dialog = forwardRef(({ children, isOpen, header = '', footer, modality = 'modal', launcherRef, anchorRef, placement, offset = 0, cancelBehavior = 'none', dragAffordance = 'none', resizeBehavior = 'none', headerDecoration = 'on', onDragStart, onDragMove, onDragEnd, onResizeStart, onResize, onResizeEnd, onFocusSet, onClose, onTransitionEnd, role = 'dialog', 'aria-labelledby': ariaLabelledBy, 'aria-describedby': ariaDescribedBy, testId, ...props }, ref) => {
    const [status, setStatus] = useState(isOpen ? 'initial' : 'unmounted');
    const [visibility, setVisibility] = useState('hidden');
    const prevStatusRef = useRef('unmounted');
    const floatingRef = useRef(null);
    const contentWrapperRef = useRef(null);
    const localLauncherRef = useRef(null);
    const footerRef = useRef(null);
    const isSmallWidth = !useBreakpoints()['md'];
    const derivedPlacement = isSmallWidth ? 'bottom' : placement ? placement : 'center';
    const animationType = isSmallWidth
        ? status === 'opening'
            ? 'slideUp'
            : 'slideDown'
        : status === 'opening'
            ? 'zoomIn'
            : 'zoomOut';
    const uniqueId = useId();
    const labelId = ariaLabelledBy ? ariaLabelledBy : uniqueId;
    const interpolations = [...Object.values(dimensionInterpolations)];
    const styleInterpolations = mergeInterpolations(interpolations);
    const { ...styles } = styleInterpolations(props);
    const shouldReturnFocus = useRef(false);
    const setInitialFocus = useCallback(() => {
        // Focus on first tabbable in the Dialog container or container itself if there is none
        if (footerRef.current && allTabbableElements(footerRef.current).length > 0) {
            focusWithin(footerRef.current);
        }
        else {
            focusWithin(contentWrapperRef.current);
        }
        onFocusSet?.();
    }, [onFocusSet]);
    const returnFocus = useCallback(() => {
        const launcherEl = localLauncherRef.current;
        if (shouldReturnFocus.current && launcherEl) {
            focusOn(launcherEl);
            shouldReturnFocus.current = false;
        }
    }, []);
    // Animation
    const { nodeRef } = useAnimation(animationType, {
        animationStates,
        isAnimatedOnMount: true,
        onAnimationEnd: () => {
            if (isOpen) {
                onTransitionEnd?.(true);
            }
            else if (!isOpen) {
                // Should return focus?
                // Modeless: check whether Dialog contains focus before it unmounts
                // Modal: always return focus
                shouldReturnFocus.current =
                    contentWrapperRef.current?.contains(getActiveElement()) || false;
                nodeRef(null);
                setVisibility('hidden');
                setStatus('unmounted');
                onTransitionEnd?.(false);
            }
        }
    });
    const mergedRef = useMemo(() => mergeRefs(contentWrapperRef, ref), [contentWrapperRef, ref]);
    const launcherKeyDownCallback = useCallback((event) => {
        if (localLauncherRef.current === getActiveElement() && event.code === 'F6') {
            // Prevent default F6 handlers.
            // F6 is a standard Chrome address bar shortcut on Windows.
            event.preventDefault();
            event.stopPropagation();
            focusWithin(contentWrapperRef.current);
            onFocusSet?.();
        }
    }, 
    // eslint-disable-next-line react-hooks/exhaustive-deps
    [modality]);
    const { draggableProps } = useDraggable({
        isDisabled: dragAffordance === 'none',
        containerRef: floatingRef,
        onDragStart,
        onDragMove,
        onDragEnd
    });
    useEffect(() => {
        // 1. Ignore status update on initial render or return focus on component unmount
        if (status === 'unmounted' && !isOpen) {
            if (status !== prevStatusRef.current) {
                returnFocus();
            }
        }
        // 2. Mount visually hidden component
        // Component gets mounted, but will be visually hidden to avoid flickering
        // Flickering happens because it gets rendered before animation hook repositions
        // it in next render cycle
        else if (status === 'unmounted' && isOpen) {
            setStatus('initial');
        }
        // 3. Setup animation
        // Component has been mounted.
        // We will kick out animation as node is already present in DOM.
        // To avoid flickering, we still keep it visually hidden until the next render cycle.
        else if (status === 'initial' && isOpen) {
            nodeRef(contentWrapperRef.current);
            setStatus('opening');
        }
        // 3. Unhide component. It becomes visible.
        else if (status === 'opening' && isOpen) {
            setVisibility('visible');
        }
        // 4. Closing
        else if (!isOpen) {
            setStatus('closing');
        }
        return () => {
            // remember the previous status
            prevStatusRef.current = status;
        };
    }, [isOpen, status, nodeRef, returnFocus]);
    // Destroy case handler
    useEffect(() => {
        return () => {
            const launcherEl = localLauncherRef.current;
            if (
            // eslint-disable-next-line react-hooks/exhaustive-deps
            (contentWrapperRef.current?.contains(getActiveElement()) || false) &&
                launcherEl) {
                // In destroy case ModalManager is blocking returning of the focus.
                // We need to reschedule it to workaround that
                setTimeout(() => {
                    focusOn(launcherEl);
                }, 0);
            }
        };
    }, []);
    // Initialize launcher
    useEffect(() => {
        if (isOpen) {
            if (!launcherRef?.current) {
                // 1. If launcher is not defined, most frequent scenario is,
                // that element, that has focus before opening the dialog is considered launcher
                localLauncherRef.current = getActiveElement();
            }
            else if (launcherRef.current instanceof Element) {
                // 2. launcherRef element was provided.
                // Verify, if it exists in DOM, otherwise use the last active element
                localLauncherRef.current = document.body.contains(launcherRef.current)
                    ? launcherRef.current
                    : getActiveElement();
            }
            else {
                localLauncherRef.current = getActiveElement();
            }
            // Register F6 key handler to enter a modeless dialog
            if (modality === 'modeless') {
                localLauncherRef.current?.addEventListener('keydown', launcherKeyDownCallback);
            }
        }
        return () => {
            // Deregister F6 key handler
            if (modality === 'modeless') {
                localLauncherRef.current?.removeEventListener('keydown', launcherKeyDownCallback);
            }
        };
    }, [isOpen, launcherRef, launcherKeyDownCallback, modality]);
    // setInitialFocus when the dialog becomes visible
    useEffect(() => {
        if (visibility === 'visible') {
            setInitialFocus();
        }
    }, [visibility, setInitialFocus]);
    const handleKeyDown = (event) => {
        switch (event.code) {
            case 'Escape':
                onClose?.({ reason: 'escapeKey' });
                break;
            case 'F6':
                // Focus launcher
                if (localLauncherRef.current && modality === 'modeless') {
                    focusOn(localLauncherRef.current);
                }
                // Prevent default F6 handlers. F6 is a standard Chrome address bar shortcut on Windows.
                event.preventDefault();
                event.stopPropagation();
                break;
        }
    };
    const handleBackdropClick = () => {
        // in case of a backdrop click, move focus to dialog's root element
        contentWrapperRef.current?.focus();
    };
    const renderDialogContent = () => {
        return (jsx(StyledDialog, { ref: mergedRef, labelId: labelId, header: header, footer: footer, role: role, ariaLabelledBy: labelId, ariaDescribedBy: ariaDescribedBy, onKeyDown: handleKeyDown, testId: testId, style: styles, visibility: visibility, cancelBehavior: cancelBehavior, draggableProps: draggableProps, isResizable: resizeBehavior === 'resizable', headerDecoration: headerDecoration, containerRef: floatingRef, onClose: onClose, onResizeStart: onResizeStart, onResize: onResize, onResizeEnd: onResizeEnd, footerRef: footerRef, children: children }));
    };
    const renderDialog = () => {
        if (anchorRef && anchorRef.current) {
            return (jsx(Floating, { ref: floatingRef, anchorRef: anchorRef, placement: 'bottom-start', flipOptions: { mainAxis: false, crossAxis: false }, shiftOptions: { mainAxis: false, crossAxis: false }, children: renderDialogContent() }));
        }
        else {
            return (jsx(WindowOverlay, { placement: derivedPlacement, offset: offset, children: renderDialogContent() }));
        }
    };
    if (modality === 'modal') {
        return (jsx(Modal, { isOpen: status !== 'unmounted', onBackdropClick: handleBackdropClick, children: renderDialog() }));
    }
    else {
        return status !== 'unmounted' ? jsx(Layer, { children: renderDialog() }) : null;
    }
});
const animationStates = {
    zoomIn: {
        from: {
            scaleX: 0.9,
            scaleY: 0.9,
            transformOrigin: 'center',
            opacity: 0
        },
        to: {
            scaleX: 1,
            scaleY: 1,
            opacity: 1
        },
        options: {
            duration: 200
        }
    },
    zoomOut: {
        from: {
            scaleX: 1,
            scaleY: 1,
            transformOrigin: 'center',
            opacity: 1
        },
        to: {
            scaleX: 0.9,
            scaleY: 0.9,
            opacity: 0
        },
        options: {
            duration: 200
        }
    },
    slideUp: {
        ...SLIDE_UP_XLARGE,
        options: {
            duration: DURATION_LARGE
        }
    },
    slideDown: {
        ...SLIDE_DOWN_XLARGE,
        options: {
            duration: DURATION_LARGE
        }
    }
};

export { Dialog as D };
//# sourceMappingURL=Dialog-fbbcc306.js.map
