/* @oracle/oraclejet-preact: undefined */
'use strict';

var jsxRuntime = require('preact/jsx-runtime');
var hooks = require('preact/hooks');
var compat = require('preact/compat');
var refUtils = require('./refUtils-915b985d.js');
var Layer = require('./Layer-2352de5e.js');
require('./LayerHost-8ec73638.js');
var Modal = require('./Modal-64a0608b.js');
var FocusTrap = require('./FocusTrap-3d81c5e3.js');
var tabbableUtils = require('./tabbableUtils-66ae6ba3.js');
var positionUtils = require('./positionUtils-a780137e.js');
var clientHints = require('./clientHints-89c01238.js');
var useOutsideClick = require('./useOutsideClick-8e9209c6.js');
var useAnimation = require('./useAnimation-fb11e4cc.js');
var useUser = require('./useUser-c85f9587.js');
var useTestId = require('./useTestId-32c35bff.js');
var useSwipe = require('./useSwipe-d917dcba.js');
var useComponentTheme = require('./useComponentTheme-082fc8e4.js');
var colors = require('./colors-846e841c.js');
var mergeInterpolations = require('./mergeInterpolations-6727b536.js');
var UNSAFE_DrawerPopup_themes_redwood_DrawerPopupTheme = require('./UNSAFE_DrawerPopup/themes/redwood/DrawerPopupTheme.js');

// Constants
const displayModefullWidthThreshold = 600;
const zero = '0px';
const duration = 250;
const getElementHeight = (element) => {
    return Math.round(element.offsetHeight);
};
const getElementWidth = (element) => {
    return Math.round(element.getBoundingClientRect().width);
};
/**
 * A drawer popup adds a single slide-in side content alongside some primary content to an application window.
 *
 * This drawer always overlays the page and can be placed at the 'start', 'end' or 'bottom' edge.
 */
const DrawerPopup = compat.forwardRef(({ children, isOpen = false, autoFocusRef, placement = 'start', modality = 'modal', onClose, onTransitionEnd, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, 'aria-describedby': ariaDescribedBy, testId, ...props }, ref) => {
    // Initialize states
    const [status, setStatus] = hooks.useState(isOpen ? 'initial' : 'unmounted');
    const [visibility, setVisibility] = hooks.useState('hidden');
    const [isOutsideClickDisabled, setIsOutsideClickDisabled] = hooks.useState(!isOpen);
    const [displayModeHorizontal, setDisplayModeHorizontal] = hooks.useState('overlay');
    const prevStatusRef = hooks.useRef('unmounted');
    const rootRef = hooks.useRef(null);
    const launcherRef = hooks.useRef(null);
    const mergedRef = hooks.useMemo(() => refUtils.mergeRefs(rootRef, ref), [rootRef, ref]);
    const isBottomPlacement = placement === 'bottom';
    const shouldReturnFocus = hooks.useRef(false);
    // Animation config
    const animationStates = {
        opening: () => {
            if (isBottomPlacement) {
                return {
                    from: {
                        translateY: getTranslateY()
                    },
                    to: {
                        translateY: zero
                    },
                    options: {
                        duration: duration
                    }
                };
            }
            else {
                return {
                    from: {
                        translateX: getTranslateX()
                    },
                    to: {
                        translateX: zero
                    },
                    options: {
                        duration: duration
                    }
                };
            }
        },
        closing: () => {
            if (isBottomPlacement) {
                return {
                    from: {
                        translateY: zero
                    },
                    to: {
                        translateY: getTranslateY()
                    },
                    options: {
                        duration: duration
                    }
                };
            }
            else {
                return {
                    from: {
                        translateX: zero
                    },
                    to: {
                        translateX: getTranslateX()
                    },
                    options: {
                        duration: duration
                    }
                };
            }
        }
    };
    // testId support
    const testIdProps = useTestId.useTestId(testId);
    // Normalizing position
    const { direction } = useUser.useUser();
    const getNormalizedPlacement = () => {
        return positionUtils.normalizePosition(placement, direction);
    };
    const getTranslateX = () => {
        return `${getNormalizedPlacement() === 'right' ? '' : '-'}${getElementWidth(rootRef.current)}px`;
    };
    const getTranslateY = () => {
        return `${getElementHeight(rootRef.current)}px`;
    };
    const setInitialFocus = () => {
        // Set focus to the first match:
        // 1. First element inside the drawer matching [autofocus]
        const autofocusItems = tabbableUtils.getAutofocusFocusables(rootRef.current);
        if (autofocusItems.length > 0) {
            tabbableUtils.focusOn(autofocusItems[0]);
            return;
        }
        // 2. Tabbable element inside the content element
        // 3. The drawer itself
        tabbableUtils.focusWithin(rootRef.current);
    };
    const returnFocusToLauncher = () => {
        if (shouldReturnFocus.current) {
            tabbableUtils.focusOnElementOrNearestAncestor(launcherRef.current);
            shouldReturnFocus.current = false;
        }
    };
    // Remove style attribute left on the element by the Animation util
    const removeAnimationInlineStyles = hooks.useCallback(() => {
        rootRef.current?.style?.removeProperty('transform');
    }, []);
    // Animation
    const { nodeRef } = useAnimation.useAnimation(status, {
        animationStates,
        isAnimatedOnMount: true,
        onAnimationEnd: () => {
            if (status === 'opening') {
                removeAnimationInlineStyles();
                setInitialFocus();
                onTransitionEnd?.(true);
            }
            else if (!isOpen) {
                shouldReturnFocus.current =
                    rootRef.current?.contains(tabbableUtils.getActiveElement()) || false;
                nodeRef(null);
                setVisibility('hidden');
                setStatus('unmounted');
                onTransitionEnd?.(false);
            }
        }
    });
    hooks.useEffect(() => {
        // 1. Ignore status update on initial render
        if (status === 'unmounted' && !isOpen && status !== prevStatusRef.current) {
            returnFocusToLauncher();
        }
        // 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(rootRef.current);
            setIsOutsideClickDisabled(false);
            setStatus('opening');
        }
        // 3. Unhide component. It becomes visible.
        else if (status === 'opening' && isOpen) {
            setVisibility('visible');
        }
        // 4. Closing
        else if (!isOpen && status !== 'unmounted') {
            setIsOutsideClickDisabled(true);
            setStatus('closing');
        }
        return () => {
            // remember the previous status
            prevStatusRef.current = status;
        };
    }, [isOpen, status, nodeRef]);
    // Destroy case handler
    hooks.useEffect(() => {
        return () => {
            // eslint-disable-next-line react-hooks/exhaustive-deps
            if (rootRef.current?.contains(tabbableUtils.getActiveElement()) || false) {
                // In destroy case ModalManager is blocking returning of the focus.
                // We need to reschedule it to workaround that
                setTimeout(() => {
                    tabbableUtils.focusOnElementOrNearestAncestor(launcherRef.current);
                }, 0);
            }
        };
    }, []);
    // Launcher handler
    hooks.useEffect(() => {
        if (status === 'opening') {
            // Remember launcher
            launcherRef.current = tabbableUtils.getActiveElement();
            // Register F6 key handler. It is modeless only feature.
            if (modality === 'modeless') {
                launcherRef.current?.addEventListener('keydown', launcherKeyDownCallback);
            }
        }
        if (status === 'closing' && modality === 'modeless') {
            return () => {
                // Deregister F6 key handler
                launcherRef.current?.removeEventListener('keydown', launcherKeyDownCallback);
            };
        }
        return;
    }, [modality, status]);
    const launcherKeyDownCallback = (event) => {
        // Moves focus from launcher (must have focus) back to the Drawer
        if (launcherRef.current === tabbableUtils.getActiveElement() && event.code === 'F6') {
            // Prevent default F6 handlers.
            // F6 is a standard Chrome address bar shortcut on Windows.
            event.preventDefault();
            event.stopPropagation();
            // Focus on first tabbable in a container or container itself if there is none
            tabbableUtils.focusWithin(rootRef.current);
        }
    };
    const handleKeyDown = hooks.useCallback((event) => {
        const launcherEl = launcherRef.current;
        switch (event.code) {
            case 'Escape': {
                onClose?.({ reason: 'escapeKey' });
                break;
            }
            case 'F6':
                if (modality === 'modeless') {
                    // Prevent default F6 handlers.
                    // F6 is a standard Chrome address bar shortcut on Windows.
                    event.preventDefault();
                    event.stopPropagation();
                    // Focus launcher
                    if (launcherEl) {
                        tabbableUtils.focusOn(launcherEl);
                    }
                    break;
                }
        }
    }, [onClose, modality]);
    // OutsideClick handler
    const handleOutsideClick = hooks.useCallback(() => {
        // Trigger onClose event with outside click detail
        onClose?.({ reason: 'outsideClick' });
    }, [onClose]);
    const insideRefs = [rootRef];
    // launcher should be considered an "inside" element
    if (launcherRef.current && launcherRef.current !== document.body) {
        insideRefs.push(launcherRef);
    }
    useOutsideClick.useOutsideClick({
        isDisabled: isOutsideClickDisabled,
        ref: insideRefs,
        handler: handleOutsideClick
    });
    // Drawer placement
    const getDrawerPlacement = () => {
        if (isBottomPlacement) {
            return placement;
        }
        else {
            if (direction === 'rtl') {
                return placement === 'start' ? 'right' : 'left';
            }
            else {
                return placement === 'start' ? 'left' : 'right';
            }
        }
    };
    const clientHints$1 = clientHints.getClientHints();
    const getViewportWidth = hooks.useCallback(() => {
        if (clientHints$1.platform === 'ios') {
            // On ios window.innerWidth is not recommended way of measuring the viewport
            return document.documentElement.clientWidth;
        }
        return window.innerWidth;
    }, [clientHints$1.platform]);
    // Window resize handler
    // Handles display mode: Overlay <> Full overlay (full width side drawers)
    const handleResize = hooks.useCallback(() => {
        const viewportWidth = getViewportWidth();
        if (displayModeHorizontal === 'overlay' && viewportWidth < displayModefullWidthThreshold) {
            setDisplayModeHorizontal('fullOverlay');
        }
        if (displayModeHorizontal === 'fullOverlay' &&
            viewportWidth > displayModefullWidthThreshold) {
            setDisplayModeHorizontal('overlay');
        }
    }, [getViewportWidth, displayModeHorizontal]);
    hooks.useEffect(() => {
        const root = rootRef.current;
        if (root && status === 'opening') {
            const resizeObserver = new ResizeObserver(() => {
                handleResize();
            });
            resizeObserver.observe(document.body);
            return () => {
                resizeObserver.unobserve(document.body);
            };
        }
        return undefined;
    }, [status, handleResize]);
    const dispatchSwipeOnClose = hooks.useCallback(() => {
        onClose?.({ reason: 'swipe' });
    }, [onClose]);
    // OutsideClick handler
    const swipeHandler = hooks.useCallback((event) => {
        const swipeDir = event.direction;
        // LTR
        if (direction === 'ltr') {
            if ((placement === 'start' && swipeDir === 'left') ||
                (placement === 'end' && swipeDir === 'right')) {
                dispatchSwipeOnClose();
            }
        }
        // RTL
        if (direction === 'rtl') {
            if ((placement === 'start' && swipeDir === 'right') ||
                (placement === 'end' && swipeDir === 'left')) {
                dispatchSwipeOnClose();
            }
        }
        // Bottom
        if (placement === 'bottom' && swipeDir === 'down') {
            dispatchSwipeOnClose();
        }
    }, [direction, placement, dispatchSwipeOnClose]);
    const swipeOptions = {
        threshold: 50,
        maximumTime: 750,
        tolerance: 40
    };
    const { swipeProps } = useSwipe.useSwipe(swipeHandler, swipeOptions);
    // Styles
    const { classes } = useComponentTheme.useComponentTheme(UNSAFE_DrawerPopup_themes_redwood_DrawerPopupTheme.DrawerPopupRedwoodTheme, {
        placement: getDrawerPlacement(),
        visibility,
        displayModeHorizontal: ['start', 'end'].indexOf(placement) > -1 ? displayModeHorizontal : undefined
    });
    const interpolations = [...Object.values(colors.colorInterpolations)];
    const styleInterpolations = mergeInterpolations.mergeInterpolations(interpolations);
    const { ...styles } = styleInterpolations(props);
    const renderDrawer = () => {
        return (jsxRuntime.jsx(FocusTrap.FocusTrap, { autoFocusRef: autoFocusRef, restoreFocusRef: false, children: jsxRuntime.jsx("div", { ref: mergedRef, className: classes, style: styles, tabIndex: -1, role: "dialog", onKeyDown: handleKeyDown, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, ...swipeProps, ...testIdProps, children: children }) }));
    };
    const isMounted = status !== 'unmounted';
    if (isMounted) {
        // Modal vs. modelss upper wrapper
        if (modality === 'modal') {
            return jsxRuntime.jsx(Modal.Modal, { isOpen: isMounted, children: renderDrawer() });
        }
        else {
            return jsxRuntime.jsx(Layer.Layer, { children: renderDrawer() });
        }
    }
    return null;
});

exports.DrawerPopup = DrawerPopup;
//# sourceMappingURL=DrawerPopup-2d34b341.js.map
