/* @oracle/oraclejet-preact: undefined */
import { jsxs, jsx } from 'preact/jsx-runtime';
import { useState, useRef, useCallback, useEffect } from 'preact/hooks';
import { n as normalizePosition } from './positionUtils-f26caad9.js';
import { F as Floating } from './Floating-f02677ba.js';
import { L as Layer } from './Layer-446bf21e.js';
import 'preact/compat';
import './LayerHost-e6402730.js';
import { g as getActiveElement, a as allTabbableElements, b as focusOn, f as focusWithin, d as getAutofocusFocusables, e as focusOnElementOrNearestAncestor } from './tabbableUtils-6ceb767f.js';
import { g as getClientHints } from './clientHints-f850225e.js';
import { u as useAnimation } from './useAnimation-a5eb7604.js';
import { D as DURATION_LARGE } from './animationUtils-e6081007.js';
import { u as useUser } from './useUser-1cd223a9.js';
import { u as useTestId } from './useTestId-9b504481.js';
import { u as useBreakpointValues } from './useBreakpointValues-de03c6fa.js';
import { u as useResizeObserver } from './useResizeObserver-838164c1.js';
import { u as useComponentTheme } from './useComponentTheme-d2f9e47f.js';
import { c as classNames } from './classNames-4e12b00d.js';
import { DrawerLayoutRedwoodTheme } from './UNSAFE_DrawerLayout/themes/redwood/DrawerLayoutTheme.js';
import './useFloating-953362cf.js';
import './refUtils-b9d8d91a.js';
import './useOutsideClick-fd9ad806.js';
import './arrayUtils-35a58161.js';
import './useModal-953082f2.js';
import './utils-a4102e03.js';
import './Common/themes/themeContract.css.js';
import './colorUtils-16eb823f.js';
import './_curry1-25297e59.js';
import './UNSAFE_Floating/themes/redwood/FloatingTheme.js';
import './UNSAFE_Floating/themes/FloatingStyles.css.js';
import './FloatingStyles.styles.css';
import './UNSAFE_Floating/themes/redwood/FloatingBaseTheme.css.js';
import './UNSAFE_Floating/themes/redwood/FloatingBaseTheme.styles.css';
import './UNSAFE_Floating/themes/redwood/FloatingVariants.css.js';
import './vanilla-extract-recipes-createRuntimeFn.esm-2aaf8c98.js';
import './vanilla-extract-dynamic.esm-2955d60a.js';
import './UNSAFE_Floating/themes/FloatingContract.css.js';
import './UNSAFE_Layer/themes/LayerStyles.css.js';
import './LayerStyles.styles.css';
import './useThemeInterpolations-8dd8029f.js';
import './useColorScheme-c4e237ad.js';
import './useScale-83881775.js';
import './theme-63551f30.js';
import './Theme-e6dec6db.js';
import './mergeInterpolations-e2db2a66.js';
import './mergeDeepWithKey-08531060.js';
import './_curry3-0b4222d7.js';
import './_curry2-34316bcf.js';
import './_isObject-1fab0f5b.js';
import './useDensity-c17603b2.js';
import './Common/themes/redwood/theme.js';
import 'preact';
import './UNSAFE_Layer/themes/LayerHostStyles.css.js';
import './LayerHostStyles.styles.css';
import './head-6f17c50c.js';
import './_arity-be492b9e.js';
import './_isArray-694cc52d.js';
import './_isString-675f1de9.js';
import './useBreakpoints-13aa87e6.js';
import './useVisAddBusyState-994e0324.js';
import './VisBusyStateContext-9ae84b1d.js';
import './useVisBusyStateContext-a1df0764.js';
import './logger-c92f309c.js';
import './UNSAFE_DrawerLayout/themes/DrawerLayoutStyles.css.js';
import './DrawerLayoutStyles.styles.css';
import './UNSAFE_DrawerLayout/themes/redwood/DrawerLayoutBaseTheme.css.js';
import './UNSAFE_DrawerLayout/themes/redwood/DrawerLayoutBaseTheme.styles.css';
import './UNSAFE_DrawerLayout/themes/redwood/DrawerLayoutVariants.css.js';

/**
 * A drawer layout adds expandable side contents (drawers) alongside some primary content.
 *
 * These drawers automatically swap 'reflow' and 'overlay' display mode based on width of the page and can be placed at the 'start', 'end' or 'bottom' edge.
 */
const DrawerLayout = ({ children, startDrawer, endDrawer, bottomDrawer, isStartOpen = false, isEndOpen = false, isBottomOpen = false, startDisplay, endDisplay, bottomDisplay, onClose, onTransitionEnd, testId // ...props
 }) => {
    // Constants
    const zeroPx = '0px';
    const verticalBreakpoint = 600;
    const stringStart = 'start';
    const stringEnd = 'end';
    const stringBottom = 'bottom';
    const stringInitial = 'initial';
    const stringOpening = 'opening';
    const stringOpen = 'open';
    const stringClosing = 'closing';
    const stringUnmounted = 'unmounted';
    const stringFullOverlay = 'full-overlay';
    const stringOverlay = 'overlay';
    const stringReflow = 'reflow';
    const stringHidden = 'hidden';
    const stringVisible = 'visible';
    // Initialize states
    const [startDrawerStatus, setStartDrawerStatus] = useState(isStartOpen ? stringInitial : stringUnmounted);
    const [endDrawerStatus, setEndDrawerStatus] = useState(isEndOpen ? stringInitial : stringUnmounted);
    const [bottomDrawerStatus, setBottomDrawerStatus] = useState(isBottomOpen ? stringInitial : stringUnmounted);
    const [startVisibility, setStartVisibility] = useState(stringHidden);
    const [endVisibility, setEndVisibility] = useState(stringHidden);
    const [bottomVisibility, setBottomVisibility] = useState(stringHidden);
    const [startOffset, setStartOffset] = useState();
    const [endOffset, setEndOffset] = useState();
    const [bottomOffset, setBottomOffset] = useState();
    // Breakpoints:
    // sm: 0-600px (full-overlay)
    // md: 600-1024px (overlay)
    // lg: >1024px (reflow)
    const displayMode = useBreakpointValues({
        sm: stringFullOverlay,
        md: stringOverlay,
        lg: stringReflow
    });
    const [viewportDisplayModeHorizontal, setViewportDisplayModeHorizontal] = useState(displayMode);
    const [viewportDisplayModeVertical, setViewportDisplayModeVertical] = useState(displayMode);
    // Refs
    const rootRef = useRef(null);
    const centerRef = useRef(null);
    const startDrawerRef = useRef(null);
    const endDrawerRef = useRef(null);
    const bottomDrawerRef = useRef(null);
    const placementOfLastlyFocusedRef = useRef(null);
    const indexOfLastlyFocusedRef = useRef(null);
    const startLauncherRef = useRef(null);
    const endLauncherRef = useRef(null);
    const bottomLauncherRef = useRef(null);
    const elementWithFocusBeforeDrawerClosesRef = useRef(null);
    const [startResolvedDisplayMode, setStartResolvedDisplayMode] = useState(null);
    const [endResolvedDisplayMode, setEndResolvedDisplayMode] = useState(null);
    const [bottomResolvedDisplayMode, setBottomResolvedDisplayMode] = useState(null);
    const getDrawerEl = useCallback((placement) => {
        return getDrawerRef(placement).current;
    }, []);
    const getPlacementOfDrawerWithFocus = useCallback(() => {
        const startDrawerEl = getDrawerEl(stringStart);
        const endDrawerEl = getDrawerEl(stringEnd);
        const bottomDrawerEl = getDrawerEl(stringBottom);
        const activeElement = getActiveElement();
        if (startDrawerEl && startDrawerEl.contains(activeElement)) {
            return stringStart;
        }
        else if (endDrawerEl && endDrawerEl.contains(activeElement)) {
            return stringEnd;
        }
        else if (bottomDrawerEl && bottomDrawerEl.contains(activeElement)) {
            return stringBottom;
        }
        else
            return null;
    }, [getDrawerEl]);
    const triggerFocusTransferStrategy = useCallback(() => {
        // Focus strategy - find focused element.
        // Save what drawer and what element had focus before it was eventually conditionally re-rendered
        // In 'auto' display mode we swap 'reflow' and 'overlay' automatically
        // When this change happens, conditional rendering mechanism destroys
        // reflow drawer DOM and creates overlay drawer DOM. This, of course,
        // applies vice versa. In such cases focus gets lost.
        // In DOM destroy cases focus is usually moved to <body>.
        // In standard open cases we trigger focus strategy from animationEnd callback.
        // However, in 'reflow' <> 'overlay' cases animation hook is not used/wanted at all.
        // In addition, we should keep focus on the lastly/customly focused elemenet,
        // e.g. the lastly focused element of a form
        // 1. useBreakpoint hook is triggered even there's no drawer present.
        // Handle focus only if there's at least one drawer open
        if (isStartOpen || isEndOpen || isBottomOpen) {
            // 2. Focus might not be in drawers.
            // In modeless cases user can bring the focus out of open drawers by clicking outside them.
            // Check whether focused element lives in a drawer (one of drawers).
            // Do not handle focus at all if it is not within a drawer.
            // Note: returns null if focus is not within a drawer
            const activeElementPlacement = getPlacementOfDrawerWithFocus();
            if (activeElementPlacement) {
                // Save in which drawer focus lives (save its placement).
                placementOfLastlyFocusedRef.current = activeElementPlacement;
                // 3. Focus was detected in a particular drawer
                // Save index of this element
                const allTabbables = allTabbableElements(getDrawerRef(placementOfLastlyFocusedRef.current).current, true);
                // Save index of lastly focused element
                indexOfLastlyFocusedRef.current = allTabbables.indexOf(getActiveElement());
            }
        }
    }, [getPlacementOfDrawerWithFocus, isStartOpen, isBottomOpen, isEndOpen]);
    const isDrawerOpen = useCallback((placement) => {
        let isOpen = false;
        switch (placement) {
            case stringStart:
                isOpen = isStartOpen;
                break;
            case stringEnd:
                isOpen = isEndOpen;
                break;
            case stringBottom:
                isOpen = isBottomOpen;
                break;
        }
        return isOpen;
    }, [isStartOpen, isEndOpen, isBottomOpen]);
    useEffect(() => {
        // Focus strategy - apply focus back
        // Return focus to a drawer if applicable
        // 1a. useBreakpoint hook, which monitors viewport resolution, is triggered even drawer(s) are not open.
        // This hook causes changes to 'viewportDisplayModeHorizontal', a dependency of this effect.
        // 1b. This effect can be also triggered by a change to
        // start-display/end-display/bottom-display properties of the DrawerLayout.
        // Make sure that drawer, in which we previously found focus, is still open.
        const placementOflastlyFocusedDrawer = placementOfLastlyFocusedRef.current;
        if (placementOflastlyFocusedDrawer) {
            const drawerRefReceivingFocus = getDrawerRef(placementOflastlyFocusedDrawer);
            if (drawerRefReceivingFocus.current && isDrawerOpen(placementOflastlyFocusedDrawer)) {
                // 2. Find all tabbables in newly rendered focus receiving focus
                const allTabbables = allTabbableElements(drawerRefReceivingFocus.current, true);
                // 3. Try to focus the same element that had focus in previous display mode
                // In theory drawer's content could be modified.
                // Check whether there are more tababble elements than the index value we saved
                if (indexOfLastlyFocusedRef.current != null &&
                    allTabbables.length > indexOfLastlyFocusedRef.current) {
                    // Focus element using the saved index
                    focusOn(allTabbables[indexOfLastlyFocusedRef.current]);
                }
                else {
                    // Focus first tabbable
                    focusWithin(drawerRefReceivingFocus.current);
                }
                // Reset saved values to avoid possible side effects
                placementOfLastlyFocusedRef.current = null;
                indexOfLastlyFocusedRef.current = null;
            }
        }
    }, [startResolvedDisplayMode, endResolvedDisplayMode, bottomResolvedDisplayMode, isDrawerOpen]);
    useEffect(() => {
        // Size breakpoint crossed
        triggerFocusTransferStrategy();
        // Reschedules the effect to occur after the ResizeObserver’s loop has completed.
        setTimeout(() => {
            setViewportDisplayModeHorizontal(displayMode);
        }, 0);
    }, [displayMode, triggerFocusTransferStrategy]);
    const getElementHeight = (element) => {
        return element ? Math.round(element.offsetHeight) : 0;
    };
    const getElementHeightPx = useCallback((element) => {
        return `${getElementHeight(element)}px`;
    }, []);
    const getElementWidth = useCallback((element) => {
        return element ? Math.round(element.getBoundingClientRect().width) : 0;
    }, []);
    const getElementWidthPx = useCallback((element) => {
        return `${getElementWidth(element)}px`;
    }, [getElementWidth]);
    // Normalizing position
    const { direction } = useUser();
    const isLtr = direction === 'ltr';
    const getNormalizedPlacement = useCallback((placement) => {
        return normalizePosition(placement, direction);
    }, [direction]);
    const isSidePlacement = (placement) => {
        return [stringStart, stringEnd].indexOf(placement) > -1;
    };
    const isBottomPlacement = (placement) => {
        return placement === stringBottom;
    };
    const isAnimationPending = (status) => {
        return [stringOpening, stringClosing].indexOf(status) > -1;
    };
    const isDrawerAnimationPending = (placement) => {
        switch (placement) {
            case stringStart:
                return isAnimationPending(startDrawerStatus);
            case stringEnd:
                return isAnimationPending(endDrawerStatus);
            case stringBottom:
                return isAnimationPending(bottomDrawerStatus);
        }
    };
    const getDrawerVisibility = (placement) => {
        switch (placement) {
            case stringStart:
                return startVisibility;
            case stringEnd:
                return endVisibility;
            case stringBottom:
                return bottomVisibility;
        }
    };
    const setDrawerVisibility = (placement, visibility) => {
        switch (placement) {
            case stringStart:
                return setStartVisibility(visibility);
            case stringEnd:
                return setEndVisibility(visibility);
            case stringBottom:
                return setBottomVisibility(visibility);
        }
    };
    const getDrawerRef = (placement) => {
        switch (placement) {
            case stringStart:
                return startDrawerRef;
            case stringEnd:
                return endDrawerRef;
            case stringBottom:
                return bottomDrawerRef;
        }
    };
    const getAnimatedNodeRef = (placement) => {
        switch (placement) {
            case stringStart:
                return startAnimatedNodeRef;
            case stringEnd:
                return endAnimatedNodeRef;
            case stringBottom:
                return bottomAnimatedNodeRef;
        }
    };
    const getDrawerStatus = (placement) => {
        switch (placement) {
            case stringStart:
                return startDrawerStatus;
            case stringEnd:
                return endDrawerStatus;
            case stringBottom:
                return bottomDrawerStatus;
        }
    };
    const setDrawerStatus = (placement, status) => {
        switch (placement) {
            case stringStart:
                setStartDrawerStatus(status);
                break;
            case stringEnd:
                setEndDrawerStatus(status);
                break;
            case stringBottom:
                setBottomDrawerStatus(status);
                break;
        }
    };
    const resetAnimatedNodeRef = (placement) => {
        switch (placement) {
            case stringStart:
                startAnimatedNodeRef(null);
                break;
            case stringEnd:
                endAnimatedNodeRef(null);
                break;
            case stringBottom:
                bottomAnimatedNodeRef(null);
                break;
        }
    };
    const getDrawerDisplayProp = useCallback((placement) => {
        switch (placement) {
            case stringStart:
                return startDisplay;
            case stringEnd:
                return endDisplay;
            case stringBottom:
                return bottomDisplay;
        }
    }, [startDisplay, endDisplay, bottomDisplay]);
    const getDrawerResolvedDisplayMode = useCallback((placement) => {
        const displayProp = getDrawerDisplayProp(placement);
        if (displayProp) {
            return displayProp;
        }
        else {
            if (isSidePlacement(placement)) {
                return viewportDisplayModeHorizontal;
            }
            return viewportDisplayModeVertical;
        }
    }, [getDrawerDisplayProp, viewportDisplayModeHorizontal, viewportDisplayModeVertical]);
    // Animation effects config
    const getAnimationStates = (ref) => {
        return {
            expandVertically: {
                from: {
                    maxHeight: zeroPx
                },
                to: {
                    maxHeight: '100%'
                },
                options: {
                    duration: DURATION_LARGE
                }
            },
            collapseVertically: {
                from: {
                    maxHeight: '100%'
                },
                to: {
                    maxHeight: zeroPx
                },
                options: {
                    duration: DURATION_LARGE
                }
            },
            expandHorizontally: () => {
                return {
                    from: {
                        maxWidth: zeroPx
                    },
                    to: {
                        // We need a function here as the width is dynamic
                        // and percentage can't be used in reflow mode
                        maxWidth: getElementWidthPx(ref.current)
                    },
                    options: {
                        duration: DURATION_LARGE
                    }
                };
            },
            collapseHorizontally: () => {
                return {
                    from: {
                        // We need a function here as the width is dynamic
                        // and percentage can't be used in reflow mode
                        maxWidth: getElementWidthPx(ref.current)
                    },
                    to: {
                        maxWidth: zeroPx
                    },
                    options: {
                        duration: DURATION_LARGE
                    }
                };
            },
            slideUp: {
                from: {
                    // We need a function here as the height is dynamic
                    // and percentage can't be used in reflow mode
                    translateY: '100%'
                },
                to: {
                    translateY: zeroPx
                },
                options: {
                    duration: DURATION_LARGE
                }
            },
            slideDown: {
                from: {
                    translateY: zeroPx
                },
                to: {
                    translateY: '100%'
                },
                options: {
                    duration: DURATION_LARGE
                }
            },
            slideOpenLeft: {
                from: {
                    translateX: '-100%'
                },
                to: {
                    translateX: zeroPx
                },
                options: {
                    duration: DURATION_LARGE
                }
            },
            slideCloseLeft: {
                from: {
                    translateX: '0'
                },
                to: {
                    translateX: '-100%'
                },
                options: {
                    duration: DURATION_LARGE
                }
            },
            slideOpenRight: {
                from: {
                    translateX: '100%'
                },
                to: {
                    translateX: zeroPx
                },
                options: {
                    duration: DURATION_LARGE
                }
            },
            slideCloseRight: {
                from: {
                    translateX: '0'
                },
                to: {
                    translateX: '100%'
                },
                options: {
                    duration: DURATION_LARGE
                }
            }
        };
    };
    const removeAnimationInlineStyles = (placement) => {
        // Remove style attribute left on the element by the Animation util
        const drawerRef = getDrawerRef(placement);
        drawerRef.current.removeAttribute('style');
    };
    const setInitialFocus = (placement) => {
        // Set focus to the first match:
        const drawerRef = getDrawerRef(placement);
        // 1. First element inside the drawer matching [autofocus]
        const autofocusItems = getAutofocusFocusables(drawerRef.current);
        if (autofocusItems.length > 0) {
            focusOn(autofocusItems[0]);
            return;
        }
        // 2. Tabbable element inside the content element
        // 3. The drawer itself
        focusWithin(drawerRef.current);
    };
    const getLauncherRef = (placement) => {
        switch (placement) {
            case stringStart:
                return startLauncherRef;
            case stringEnd:
                return endLauncherRef;
            case stringBottom:
                return bottomLauncherRef;
        }
    };
    const returnFocusToLauncher = (placement) => {
        // Return the focus to the drawer's launcher only if the focus
        // is currently living within a drawer.
        // As we do not apply focus-trap in modeless mode, user is able to
        // get the focus out of the drawer. In this case we do not want to handle it.
        if (getDrawerRef(placement).current?.contains(elementWithFocusBeforeDrawerClosesRef.current)) {
            focusOnElementOrNearestAncestor(getLauncherRef(placement).current);
        }
    };
    // Animation effect
    const getAnimationEffect = (placement) => {
        const bottomPlacement = isBottomPlacement(placement);
        const status = getDrawerStatus(placement);
        if (getDrawerResolvedDisplayMode(placement) === stringReflow) {
            // Reflow display mode
            // Effect: Expand/Collapse
            if (bottomPlacement) {
                // useAnimation hook ignores to apply effect if it matches the previous one.
                // if (currentAnimationState.current === animationState) return;
                // That's why we need to pair particular with particualar status and
                // send empty stirng in all other cases
                if (status === 'opening') {
                    return 'expandVertically';
                }
                if (status === 'closing') {
                    return 'collapseVertically';
                }
                return '';
            }
            else {
                if (status === 'opening') {
                    return 'expandHorizontally';
                }
                if (status === 'closing') {
                    return 'collapseHorizontally';
                }
                return '';
            }
        }
        else {
            // Overlay display mode
            // Effect: SlideIn/SlideOut
            if (bottomPlacement) {
                if (status === 'opening') {
                    return 'slideUp';
                }
                if (status === 'closing') {
                    return 'slideDown';
                }
                return '';
            }
            else {
                if (status === 'opening') {
                    return getNormalizedPlacement(placement) === 'left' ? 'slideOpenLeft' : 'slideOpenRight';
                }
                if (status === 'closing') {
                    return getNormalizedPlacement(placement) === 'left'
                        ? 'slideCloseLeft'
                        : 'slideCloseRight';
                }
                return '';
            }
        }
    };
    // Custom hook to be shared for each side
    // It can't be a function as a hook can only be called within a custom hook
    function useNodeRef(placement, ref) {
        const isOpen = isDrawerOpen(placement);
        const status = getDrawerStatus(placement);
        return useAnimation(getAnimationEffect(placement), {
            animationStates: getAnimationStates(ref),
            isAnimatedOnMount: true,
            onAnimationEnd: () => {
                if (status === stringOpening) {
                    if (isOverlay(placement)) {
                        setInitialFocus(placement);
                    }
                    // Remove animation's inline style
                    removeAnimationInlineStyles(placement);
                    // In case drawer display mode changed at runtime (reflow <> overlay)
                    // we need to reset animation node ref otherwise animation state is not refelected.
                    // Case:
                    // <oj-c-drawer-layout> opens on wide screen by default in reflow mode (flex layout).
                    // useAnimation effect was 'expand'
                    // Viewport is shrinked under 1024px, display mode changes to overlay.
                    // This means DOM is reparented.
                    // For overlay mode, closing animation effect should be 'slideout'
                    // If animation nodeRef is not reset, closing effect is neither 'collapse' nor 'slideout'.
                    resetAnimatedNodeRef(placement);
                    setDrawerStatus(placement, stringOpen);
                    onTransitionEnd?.({
                        placement: placement,
                        value: true
                    });
                }
                else if (!isOpen) {
                    if (isOverlay(placement)) {
                        returnFocusToLauncher(placement);
                    }
                    resetAnimatedNodeRef(placement);
                    setDrawerVisibility(placement, stringHidden);
                    setDrawerStatus(placement, stringUnmounted);
                    onTransitionEnd?.({
                        placement: placement,
                        value: false
                    });
                }
            }
        }).nodeRef;
    }
    // Drawer refs
    const startAnimatedNodeRef = useNodeRef(stringStart, startDrawerRef);
    const endAnimatedNodeRef = useNodeRef(stringEnd, endDrawerRef);
    const bottomAnimatedNodeRef = useNodeRef(stringBottom, bottomDrawerRef);
    //testId support
    const testIdProps = useTestId(testId);
    const setLauncherRef = (placement) => {
        const activeElement = getActiveElement();
        switch (placement) {
            case stringStart:
                startLauncherRef.current = activeElement;
                break;
            case stringEnd:
                endLauncherRef.current = activeElement;
                break;
            case stringBottom:
                bottomLauncherRef.current = activeElement;
                break;
        }
    };
    // Custom hook to be shared for each side
    // It can't be a function as a hook can only be called within a custom hook
    function useDrawerStatus(placement) {
        const isOpen = isDrawerOpen(placement);
        const drawerStatus = getDrawerStatus(placement);
        useEffect(() => {
            // Ignore initial render
            // 1. Ignore status update on initial render
            if (drawerStatus === stringUnmounted && !isOpen) {
                return;
            }
            // 2. Unmounted
            // 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
            if (drawerStatus === stringUnmounted && isOpen) {
                setDrawerStatus(placement, stringInitial);
                return;
            }
            // 3. Initial
            // 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.
            if (drawerStatus === stringInitial && isOpen) {
                const drawerRef = getDrawerRef(placement);
                const animatedNodeRef = getAnimatedNodeRef(placement);
                animatedNodeRef(drawerRef.current);
                setDrawerStatus(placement, stringOpening);
                return;
            }
            // 4. Opening
            // Unhide component. It becomes visible.
            if (drawerStatus === stringOpening && isOpen) {
                setDrawerVisibility(placement, stringVisible);
                // Remember launcher
                setLauncherRef(placement);
                return;
            }
            // 5. Open
            // Status 'open' is set in 'onAnimationEnd' handler
            // 6. Closing
            if (!isOpen) {
                // As animatedNodeRef was reset in onAnimationEnd we need to set it again
                // see useAnimation transition end for details
                const drawerRef = getDrawerRef(placement);
                const animatedNodeRef = getAnimatedNodeRef(placement);
                animatedNodeRef(drawerRef.current);
                elementWithFocusBeforeDrawerClosesRef.current = getActiveElement();
                setDrawerStatus(placement, stringClosing);
            }
        }, [isOpen, drawerStatus, placement]);
    }
    // Drawers' status handler±
    useDrawerStatus(stringStart);
    useDrawerStatus(stringEnd);
    useDrawerStatus(stringBottom);
    // Keydown handlers
    const handleKeyDownStart = (event) => {
        handleKeyDown(stringStart, event);
    };
    const handleKeyDownEnd = (event) => {
        handleKeyDown(stringEnd, event);
    };
    const handleKeyDownBottom = (event) => {
        handleKeyDown(stringBottom, event);
    };
    const handleKeyDown = (placement, event) => {
        if (event.code === 'Escape') {
            // Handle only in overlay mode
            if (isOverlay(placement)) {
                onClose?.({
                    placement,
                    reason: 'escapeKey'
                });
            }
        }
    };
    // Styles
    const { classes, styles } = useComponentTheme(DrawerLayoutRedwoodTheme);
    const getVisibilityClass = (visibility) => {
        return visibility === stringHidden ? styles.visibilityHidden : styles.visibilityVisible;
    };
    const isOverlay = useCallback((placement) => {
        return ([stringFullOverlay, stringOverlay].indexOf(getDrawerResolvedDisplayMode(placement)) > -1);
    }, [getDrawerResolvedDisplayMode]);
    const isFullOverlay = (placement) => {
        return getDrawerResolvedDisplayMode(placement) === stringFullOverlay;
    };
    const getDrawerClassNames = (placement) => {
        // Visibility style is always added
        const visibility = getDrawerVisibility(placement);
        const classNamesArr = [getVisibilityClass(visibility)];
        // Styles for overlay vs. full-overlay vs. reflow
        if (isOverlay(placement)) {
            classNamesArr.push(styles.overlayDrawer);
            if (isSidePlacement(placement) && !isFullOverlay(placement)) {
                classNamesArr.push(styles.overlaySideDrawerNonFullWidth);
            }
        }
        else {
            classNamesArr.push(styles.reflowDrawer);
        }
        // Styles for animation
        if (isDrawerAnimationPending(placement)) {
            classNamesArr.push(styles.animatedDrawer);
        }
        const getPlacementSpecificStyles = (placement) => {
            switch (placement) {
                case stringStart:
                    if (isOverlay(stringStart)) {
                        return isLtr ? styles.overlayDrawerLeft : styles.overlayDrawerRight;
                    }
                    else {
                        return isLtr ? styles.reflowDrawerLeft : styles.reflowDrawerRight;
                    }
                case stringEnd:
                    if (isOverlay(stringEnd)) {
                        return isLtr ? styles.overlayDrawerRight : styles.overlayDrawerLeft;
                    }
                    else {
                        return isLtr ? styles.reflowDrawerRight : styles.reflowDrawerLeft;
                    }
                case stringBottom:
                    return isOverlay(stringBottom) ? styles.overlayDrawerBottom : styles.reflowDrawerBottom;
            }
        };
        // Placement specific styles
        classNamesArr.push(getPlacementSpecificStyles(placement));
        return classNames(classNamesArr);
    };
    const getDrawerProp = (placement) => {
        switch (placement) {
            case stringStart:
                return startDrawer;
            case stringEnd:
                return endDrawer;
            case stringBottom:
                return bottomDrawer;
        }
    };
    const getKeyDownHandler = (placement) => {
        switch (placement) {
            case stringStart:
                return handleKeyDownStart;
            case stringEnd:
                return handleKeyDownEnd;
            case stringBottom:
                return handleKeyDownBottom;
        }
    };
    // Single drawer template
    const renderDrawer = (placement) => {
        return (jsx("div", { ref: getDrawerRef(placement), className: getDrawerClassNames(placement), tabIndex: isOverlay(placement) ? -1 : undefined, onKeyDown: getKeyDownHandler(placement), children: getDrawerProp(placement) }));
    };
    const clientHints = getClientHints();
    const getViewportHeight = useCallback(() => {
        if (clientHints.platform === 'ios') {
            // On ios window.innerHeight is not recommended way of measuring the viewport
            return document.documentElement.clientHeight;
        }
        return window.innerHeight;
    }, [clientHints.platform]);
    // We faced error in KM test environment
    // Error: ResizeObserver loop completed with undelivered notifications.
    // This happens when an observed element is resized again during a notification.
    // Another cause can be rounding values and then setting the rounded value back.
    // If this is in the same loop as the resize notification, it will cause another resize on the element.
    // Wrapping ResizeObserver’s callback in a setTimeout with a delay of 0 milliseconds
    // reschedules the effect to occur after the ResizeObserver’s loop has completed.
    const rescheduleObserverHandler = (callback) => {
        setTimeout(() => {
            callback();
        }, 0);
    };
    const handleRescheduledVerticalResize = useCallback(() => {
        const viewportHeight = getViewportHeight();
        if (viewportHeight >= verticalBreakpoint) {
            if (viewportDisplayModeVertical != stringReflow) {
                triggerFocusTransferStrategy();
                setViewportDisplayModeVertical(stringReflow);
            }
        }
        else {
            if (viewportDisplayModeVertical != stringOverlay) {
                triggerFocusTransferStrategy();
                setViewportDisplayModeVertical(stringOverlay);
            }
        }
    }, [viewportDisplayModeVertical, getViewportHeight, triggerFocusTransferStrategy]);
    const handleVerticalResize = useCallback(() => {
        rescheduleObserverHandler(handleRescheduledVerticalResize);
    }, [handleRescheduledVerticalResize]);
    // Window resize listener
    // useBreakpoint hook is designed for horizontal breakpoints only
    // This listener is used for 600px (overlay vs. reflow) breakpoint
    // used for bottom drawer
    useEffect(() => {
        const resizeObserver = new ResizeObserver(() => {
            handleVerticalResize();
        });
        resizeObserver.observe(document.body);
        // Deregister resize observer when Drawer layout unmounts
        return () => {
            resizeObserver.unobserve(document.body);
        };
    }, [handleVerticalResize]);
    const getOffset = (placement) => {
        const drawerEl = getDrawerEl(placement);
        if (drawerEl) {
            return isSidePlacement(placement) ? -getElementWidth(drawerEl) : -getElementHeight(drawerEl);
        }
        return 0;
    };
    const getOffsetState = (placement) => {
        switch (placement) {
            case stringStart:
                return startOffset;
            case stringEnd:
                return endOffset;
            case stringBottom:
                return bottomOffset;
        }
    };
    const setSideOverlayDrawerFullHeight = useCallback((placement) => {
        // By default PopupElements have no predefined height.
        // In 'overlay' mode drawers should be
        // of the same height as their parent <oj-drawer-layout>
        const centerSectionHeight = getElementHeightPx(centerRef.current);
        const drawerEl = getDrawerRef(placement).current;
        if (drawerEl && isDrawerOpen(placement) && isOverlay(placement)) {
            drawerEl.style.height = centerSectionHeight;
        }
    }, [getElementHeightPx, isDrawerOpen, isOverlay]);
    const handleSideDrawerFullWidth = (placement) => {
        const drawerEl = getDrawerRef(placement).current;
        if (drawerEl) {
            if (isDrawerOpen(placement)) {
                const drawerInlineStyle = drawerEl.style;
                if (isFullOverlay(placement)) {
                    drawerInlineStyle.width = getElementWidthPx(rootRef.current);
                }
                else {
                    drawerInlineStyle.removeProperty('width');
                }
            }
        }
    };
    const setBottomOverlayDrawerWidth = useCallback(() => {
        const bottomDrawerElement = bottomDrawerRef.current;
        if (bottomDrawerElement && isDrawerOpen(stringBottom) && isOverlay(stringBottom)) {
            const centerSectionWidth = getElementWidthPx(centerRef.current);
            bottomDrawerElement.style.width = centerSectionWidth;
        }
    }, [getElementWidthPx, isDrawerOpen, isOverlay]);
    const setDrawerSize = (placement) => {
        if (isSidePlacement(placement)) {
            setSideOverlayDrawerFullHeight(placement);
            handleSideDrawerFullWidth(placement);
        }
        if (isBottomPlacement(placement)) {
            setBottomOverlayDrawerWidth();
        }
    };
    // DrawerLayout resize observer
    const handleDrawerLayoutResize = () => {
        rescheduleObserverHandler(handleRescheduledDrawerLayoutResize);
    };
    const handleRescheduledDrawerLayoutResize = useCallback(() => {
        // If the DrawerLayout (page content) changes size,
        // adjust OVERLAY side drawers (start/end) height to fit the drawer
        // Note that these two functions checks that 'overlay' condition is fulfilled
        setSideOverlayDrawerFullHeight(stringStart);
        setSideOverlayDrawerFullHeight(stringEnd);
        setBottomOverlayDrawerWidth();
    }, [setSideOverlayDrawerFullHeight, setBottomOverlayDrawerWidth]);
    useResizeObserver(rootRef, handleDrawerLayoutResize);
    const setOffsetState = (placement) => {
        // This method sets offset used only in overlay mode
        if (isOverlay(placement)) {
            const offset = getOffset(placement);
            switch (placement) {
                case stringStart:
                    setStartOffset(offset);
                    break;
                case stringEnd:
                    setEndOffset(offset);
                    break;
                case stringBottom:
                    setBottomOffset(offset);
                    break;
            }
        }
    };
    // Start drawer resize observer
    const handleStartDrawerResize = () => {
        rescheduleObserverHandler(handleRescheduledStartDrawerResize);
    };
    const handleRescheduledStartDrawerResize = () => {
        setOffsetState(stringStart);
        setBottomOverlayDrawerWidth();
    };
    useResizeObserver(startDrawerRef, handleStartDrawerResize);
    // End drawer resize observer
    const handleEndDrawerResize = () => {
        rescheduleObserverHandler(handleRescheduledEndDrawerResize);
    };
    const handleRescheduledEndDrawerResize = () => {
        setOffsetState(stringEnd);
        setBottomOverlayDrawerWidth();
    };
    useResizeObserver(endDrawerRef, handleEndDrawerResize);
    // Bottom drawer resize observer
    const handleBottomDrawerResize = () => {
        rescheduleObserverHandler(handleRescheduledBottomDrawerResize);
    };
    const handleRescheduledBottomDrawerResize = () => {
        setOffsetState(stringBottom);
    };
    useResizeObserver(bottomDrawerRef, handleBottomDrawerResize);
    const getDrawerPreviousResolvedDisplayMode = (placement) => {
        switch (placement) {
            case stringStart:
                return startResolvedDisplayMode;
            case stringEnd:
                return endResolvedDisplayMode;
            case stringBottom:
                return bottomResolvedDisplayMode;
        }
    };
    const saveResolvedDisplayMode = (placement) => {
        const currentResolvedDisplayMode = getDrawerResolvedDisplayMode(placement);
        if (getDrawerPreviousResolvedDisplayMode(placement) != currentResolvedDisplayMode) {
            switch (placement) {
                case stringStart:
                    setStartResolvedDisplayMode(currentResolvedDisplayMode);
                case stringEnd:
                    setEndResolvedDisplayMode(currentResolvedDisplayMode);
                case stringBottom:
                    setBottomResolvedDisplayMode(currentResolvedDisplayMode);
            }
            // In case of 'reflow <> overlay' change conditional rendering of a drawer happens.
            // The old DOM is destoryed and a new DOM is created. In this case focus gets lost.
            // Browser moves the focus to body.
            // We try to prevent it by eventually applying focus in the newly created DOM.
            // If at least one drawer is open, we will check whether focus lives whithin.
            // See the following method for more details.
            triggerFocusTransferStrategy();
        }
    };
    const getReflowOrOverlayDrawer = (placement) => {
        const shiftOptions = {
            mainAxis: false,
            crossAxis: false
        };
        const flipOptions = {
            mainAxis: false,
            crossAxis: false
        };
        // Save resolved display mode of a drawer so that we can compare it with 'next' value
        saveResolvedDisplayMode(placement);
        // Reflow vs. overlay drawer
        if (isOverlay(placement)) {
            setDrawerSize(placement);
            // Initial offset
            if (!getOffsetState(placement)) {
                setOffsetState(placement);
            }
            return (
            // Reconciliation issue. Reparented node must
            // be a single child of a <div>
            jsx("div", { children: jsx(Layer, { children: jsx(Floating, { anchorRef: placement === stringBottom ? centerRef : rootRef, placement: placement, class: isDrawerAnimationPending(placement) ? styles.floating : '', offsetValue: getOffsetState(placement), flipOptions: flipOptions, shiftOptions: shiftOptions, children: renderDrawer(placement) }) }) }));
        }
        else {
            return renderDrawer(placement);
        }
    };
    // Render drawer conditionally
    const getDrawer = (placement) => {
        const isMounted = getDrawerStatus(placement) !== stringUnmounted;
        return isMounted ? getReflowOrOverlayDrawer(placement) : null;
    };
    return (jsxs("div", { ref: rootRef, className: classes, ...testIdProps, children: [getDrawer(stringStart), jsxs("div", { ref: centerRef, className: styles.centerSection, children: [jsx("div", { className: styles.mainContent, children: children }), getDrawer(stringBottom)] }), getDrawer(stringEnd)] }));
};

export { DrawerLayout };
//# sourceMappingURL=UNSAFE_DrawerLayout.js.map
