import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
import { forwardRef, useState, useCallback, useEffect, useMemo, useRef, useLayoutEffect } from 'react';
import { Alert } from '../Badges';
import Flex from '../Flex';
import Icon, { registerIcon } from '../Icon';
import * as timesIcon from '../Icon/icons/times.icon';
import * as plusIcon from '../Icon/icons/plus.icon';
import * as caseSolidIcon from '../Icon/icons/case-solid.icon';
import * as barsIcon from '../Icon/icons/bars.icon';
import * as caretLeftIcon from '../Icon/icons/caret-left.icon';
import * as caretRightIcon from '../Icon/icons/caret-right.icon';
import * as sunIcon from '../Icon/icons/sun.icon';
import * as moonIcon from '../Icon/icons/moon.icon';
import * as circleMixedLeftIcon from '../Icon/icons/circle-mixed-left.icon';
import * as shareIcon from '../Icon/icons/share.icon';
import { useI18n, useBreakpoint, useConsolidatedRef, useElement, useTransitionState, useEvent, useOuterEvent, useScrollToggle, useTheme, useUID, usePreviewManager, useDirection } from '../../hooks';
import { openCloseStates } from '../../hooks/useTransitionState';
import SkipLinks from '../SkipLinks';
import Progress from '../Progress';
import { registerAction } from '../ShortcutManager';
import useShortcut from '../../hooks/useShortcut';
import { debounce, getChildOfRootNode, isValidElement } from '../../utils';
import useKeyboardCommands from '../KeyboardCommands';
import Tooltip from '../Tooltip';
import Drawer from '../Drawer';
import useThemeMode from '../../hooks/useThemeMode';
import AppShellContext, { AppShellDrawerContext } from './AppShellContext';
import AppHeader from './AppHeader';
import AppShellDrawer from './Drawer';
import AppTopNav from './AppTopNav';
import { StyledAppMain, StyledBannerRegion, StyledMobileNav, mobileHeaderHeight, StyledMobileMoreMenuButton, menuButtonWidth, StyledMobileMoreMenu, StyledAppShellToggleButton, StyledAppShellTooltip, StyledSidebarNav, headerHeight, StyledShareIcon, topNavHeight, AppShellGlobalStyle } from './AppShell.styles';
import { followingNodes, focusRegion } from './utils';
import AppNavigationPanel, { CaseTypes, Links } from './AppNavigationPanel';
import NavigationList from './NavigationList';
registerIcon(timesIcon, plusIcon, caseSolidIcon, barsIcon, caretRightIcon, caretLeftIcon, sunIcon, moonIcon, circleMixedLeftIcon, shareIcon);
registerAction({ id: 'NextRegion', defaultKeyBinding: 'F9' }, { id: 'PrevRegion', defaultKeyBinding: 'Shift+F9' }, { id: 'KeyboardCommands', defaultKeyBinding: 'CommandOrControl+/' });
function TopNavDrawerProvider({ children, topOffset }) {
    const [item, setItem] = useState(undefined);
    const [drawerOpen, setDrawerOpen] = useState(false);
    // Delay opening the drawer by one render pass after item is set.
    // The cosmos-core Drawer only fires onAfterOpen on a false→true open transition.
    // Batching setItem + setDrawerOpen(true) in the same event handler causes the
    // Drawer to mount with open=true from the start (no transition), so onAfterOpen
    // never fires and item.onDrawerOpen() (data fetching) is never called.
    // Setting drawerOpen in a separate useEffect gives the Drawer a proper transition.
    useEffect(() => {
        if (item)
            setDrawerOpen(true);
    }, [item]);
    const ctxValue = useMemo(() => ({
        drawerOpen,
        openItemName: item?.name,
        closeDrawer: () => setDrawerOpen(false),
        openDrawer: (next) => {
            // Spread to create a new object reference each time so useEffect([item])
            // always fires — even when the same item is re-opened after the drawer closes.
            setItem({ ...next });
        },
        refreshDrawer: (next) => {
            setItem({ ...next });
        }
    }), [drawerOpen, item?.name]);
    return (_jsxs(AppShellDrawerContext.Provider, { value: ctxValue, children: [children, item && (_jsx(AppShellDrawer, { drawerOpen: drawerOpen, setDrawerOpen: setDrawerOpen, content: item.drawerView, header: item.drawerHeader || item.name, topOffset: topOffset, onDrawerClose: () => {
                    setItem(undefined);
                    item.onDrawerClose?.();
                }, onDrawerOpen: item.onDrawerOpen }))] }));
}
const AppShell = forwardRef(function AppShell(props, ref) {
    const t = useI18n();
    const { appInfo, contextSwitcher, searchInput, appHeader, searchPage, searchLabel = t('sitewide'), caseTypes, links, agent, banners, progress, main, operator, utils, headerUtils, helpMenu, cases, defaultExpanded = false, collapsedHoverMenus = false, shareApp, navOrientation = 'side-rail', ...restProps } = props;
    const { disableScroll, enableScroll } = useScrollToggle();
    const navRef = useConsolidatedRef(ref);
    const mobileButtonRef = useRef(null);
    const navId = useUID();
    const [expandCollapseEl, setExpandCollapseEl] = useElement();
    const [mobileNavOpen, setMobileNavOpen] = useState(false);
    const [drawerOpen, setDrawerOpen] = useState(false);
    const [mobileNavLinksCount, setMobileNavLinksCount] = useState(0);
    const { previewActive, setPreviewActive } = usePreviewManager();
    const focusedImperatively = useRef(false);
    const [headerEl, setHeaderEl] = useElement();
    const [mobileHeaderEl, setMobileHeaderEl] = useElement();
    const isSmallOrAbove = useBreakpoint('sm');
    const isTopBar = navOrientation === 'top-bar' && isSmallOrAbove;
    const showAppHeader = isTopBar ? isSmallOrAbove : !!appHeader && isSmallOrAbove;
    const showMainContent = showAppHeader ? !!(headerEl || mobileHeaderEl) : true;
    const openNavTimeoutId = useRef();
    const closeNavTimeoutId = useRef();
    const [searchContainerEl, setSearchContainerEl] = useState(null);
    const openKeyboardCommands = useKeyboardCommands();
    const theme = useTheme();
    const { state: sidebarNavState, set: setSidebarNavState } = useTransitionState({
        ref: navRef,
        property: 'width',
        states: openCloseStates,
        defaultState: defaultExpanded ? 'open' : 'closed'
    });
    const skipLinks = useMemo(() => [
        {
            label: t('go_to_main_content'),
            target: { ariaLabel: t('main_content'), selector: 'main' }
        }
    ], [t]);
    const openNav = useCallback((delay = 0) => {
        if (closeNavTimeoutId.current)
            clearTimeout(closeNavTimeoutId.current);
        openNavTimeoutId.current = setTimeout(() => {
            setSidebarNavState('open');
        }, delay);
    }, []);
    const closeNav = useCallback((delay = 0) => {
        if (openNavTimeoutId.current)
            clearTimeout(openNavTimeoutId.current);
        closeNavTimeoutId.current = setTimeout(() => {
            setSidebarNavState('closed');
        }, delay);
    }, []);
    const onFocus = useCallback(() => {
        focusedImperatively.current = false;
    }, []);
    const mobileNavRef = useRef(null);
    useOuterEvent('focusin', [mobileNavRef, mobileButtonRef], () => setMobileNavOpen(false));
    useEffect(() => {
        if (mobileNavOpen) {
            disableScroll();
        }
        else {
            enableScroll();
        }
    }, [mobileNavOpen]);
    useEffect(() => {
        if (!navRef.current)
            return;
        const targetNode = getChildOfRootNode(navRef);
        if (!targetNode)
            return;
        if (!targetNode.style.getPropertyValue('--app-header-height')) {
            targetNode.style.setProperty('--app-header-height', showAppHeader ? headerHeight : '0');
        }
    }, [showAppHeader]);
    useEffect(() => {
        if (mobileNavOpen) {
            const onKeyDown = ({ key }) => {
                if (key === 'Escape')
                    setMobileNavOpen(false);
            };
            document.addEventListener('keydown', onKeyDown);
            return () => {
                document.removeEventListener('keydown', onKeyDown);
            };
        }
    }, [mobileNavOpen]);
    useEffect(() => {
        if (isSmallOrAbove && mobileNavOpen)
            setMobileNavOpen(false);
    }, [isSmallOrAbove, mobileNavOpen]);
    useLayoutEffect(() => {
        if (!mobileHeaderEl)
            return;
        const resizeObserver = new ResizeObserver(debounce(() => {
            const screenWidth = document.documentElement.clientWidth;
            const regionsPaddingInlinePixels = 2 *
                Number.parseInt(window.getComputedStyle(mobileHeaderEl).getPropertyValue('padding-inline-start'), 10);
            const availableWidth = screenWidth * 0.9 - regionsPaddingInlinePixels;
            setMobileNavLinksCount(Math.max(Math.floor(availableWidth / menuButtonWidth(theme)) - (caseTypes?.length ? 2 : 1), 0));
        }, 0));
        resizeObserver.observe(document.documentElement);
        return () => {
            resizeObserver.disconnect();
        };
    }, [mobileHeaderEl, caseTypes?.length]);
    const activatedEl = useRef(document.body);
    useEvent('click', e => {
        activatedEl.current = document.elementFromPoint(e.clientX, e.clientY) ?? document.body;
    });
    useShortcut('NextRegion', () => {
        const activeElement = document.activeElement && document.activeElement !== document.body
            ? document.activeElement
            : activatedEl.current;
        const regionEls = followingNodes(document.createNodeIterator(document.body, NodeFilter.SHOW_ELEMENT, node => 
        // safe to treat as Element, as we requested to give us only elements
        node.hasAttribute('data-app-region') || node === activeElement
            ? NodeFilter.FILTER_ACCEPT
            : NodeFilter.FILTER_SKIP), activeElement);
        // try to focus first visibleregion
        const focusedNode = regionEls.find(node => focusRegion(node));
        if (!focusedNode)
            activeElement.focus();
    });
    useShortcut('PrevRegion', () => {
        const activeElement = document.activeElement && document.activeElement !== document.body
            ? document.activeElement
            : activatedEl.current;
        const regionEls = followingNodes(document.createNodeIterator(document.body, NodeFilter.SHOW_ELEMENT, node => 
        // safe to treat as Element, as we requested to give us only elements
        node.hasAttribute('data-app-region') || node === activeElement
            ? NodeFilter.FILTER_ACCEPT
            : NodeFilter.FILTER_SKIP), activeElement);
        regionEls.reverse();
        // removing active element parent from the list to avoid focusing back to the same region
        // but only if element is not region himself - in that case we want to keep its parent on the list
        // as it is different focusable region
        if (regionEls[0].contains(activeElement) && !activeElement.hasAttribute('data-app-region'))
            regionEls.splice(0, 1);
        const focusedNode = regionEls.find(node => focusRegion(node));
        if (!focusedNode)
            activeElement.focus();
    });
    useShortcut('KeyboardCommands', () => {
        openKeyboardCommands();
    });
    const contextSwitcherUtilItem = useMemo(() => {
        const { contexts, onContextClick, label: contextSwitcherLabel = t('switch_to') } = contextSwitcher ?? {};
        if (!showAppHeader && contexts && contexts?.length > 1 && !!onContextClick) {
            return {
                name: contextSwitcherLabel,
                primary: contextSwitcherLabel,
                visual: _jsx(Icon, { name: 'dot-9-solid' }),
                drawerView: (_jsx(NavigationList, { items: contexts.map(context => ({
                        ...context,
                        primary: context.primary,
                        'aria-current': !!context.selected,
                        visual: context.visual,
                        onClick: e => {
                            onContextClick(context.id, e);
                        }
                    })), singleSelect: true }))
            };
        }
    }, [
        showAppHeader,
        contextSwitcher?.contexts,
        contextSwitcher?.label,
        contextSwitcher?.onContextClick
    ]);
    const shareLabel = shareApp?.label ?? t('app_shell_share');
    const shareApplicationUtilItem = useMemo(() => {
        if (!showAppHeader && shareApp) {
            return {
                name: shareLabel,
                primary: shareLabel,
                visual: _jsx(StyledShareIcon, { name: 'share' }),
                onClick: shareApp.onClick
            };
        }
    }, [showAppHeader, shareApp, shareLabel]);
    const operatorInfoUtilItem = useMemo(() => {
        if (!showAppHeader && operator) {
            return {
                name: operator.name,
                primary: t('app_shell_user_account'),
                visual: operator.avatar,
                items: operator.actions
            };
        }
    }, [showAppHeader, operator]);
    const { themeMode, onThemeModeChange } = useThemeMode();
    const themeSwitcherUtilItem = useMemo(() => {
        if (!showAppHeader && onThemeModeChange) {
            let currentThemeIcon = 'circle-mixed-left'; // default for system
            if (themeMode === 'dark') {
                currentThemeIcon = 'moon';
            }
            else if (themeMode === 'light') {
                currentThemeIcon = 'sun';
            }
            return {
                name: t('theme_mode'),
                primary: t('theme_mode'),
                visual: _jsx(Icon, { name: currentThemeIcon }),
                items: [
                    {
                        id: 'light',
                        primary: t('theme_light'),
                        visual: _jsx(Icon, { name: 'sun' }),
                        onClick: () => onThemeModeChange('light')
                    },
                    {
                        id: 'dark',
                        primary: t('theme_dark'),
                        visual: _jsx(Icon, { name: 'moon' }),
                        onClick: () => onThemeModeChange('dark')
                    },
                    {
                        id: 'system',
                        primary: t('theme_system'),
                        visual: _jsx(Icon, { name: 'circle-mixed-left' }),
                        onClick: () => onThemeModeChange('system')
                    }
                ]
            };
        }
    }, [showAppHeader, onThemeModeChange, themeMode]);
    const { start, end } = useDirection();
    const utilsList = useMemo(() => [
        ...(utils ?? []),
        ...(contextSwitcherUtilItem ? [contextSwitcherUtilItem] : []),
        ...(shareApplicationUtilItem ? [shareApplicationUtilItem] : []),
        ...(operatorInfoUtilItem ? [operatorInfoUtilItem] : []),
        ...(themeSwitcherUtilItem ? [themeSwitcherUtilItem] : [])
    ], [
        utils,
        contextSwitcherUtilItem,
        shareApplicationUtilItem,
        operatorInfoUtilItem,
        themeSwitcherUtilItem
    ]);
    return (_jsxs(AppShellContext.Provider, { value: useMemo(() => ({
            navOpen: sidebarNavState === 'open' || sidebarNavState === 'opening' || mobileNavOpen,
            drawerOpen,
            setDrawerOpen,
            collapsedHoverMenus,
            navState: sidebarNavState,
            focusedImperatively,
            headerEl: headerEl || mobileHeaderEl,
            appHeaderEl: headerEl ?? null,
            searchContainerEl,
            setSearchContainerEl,
            isMobileNav: !isSmallOrAbove,
            previewActive,
            setPreviewActive
        }), [
            sidebarNavState,
            drawerOpen,
            mobileNavOpen,
            isSmallOrAbove,
            headerEl,
            mobileHeaderEl,
            searchContainerEl,
            previewActive
        ]), children: [_jsx(SkipLinks, { items: skipLinks }), showAppHeader && (_jsx(AppHeader, { ...appInfo, contextSwitcher: contextSwitcher, searchInput: searchInput, searchLabel: searchLabel, operator: operator, headerUtils: headerUtils, helpMenu: helpMenu, searchPage: searchPage, shareApp: shareApp, ref: setHeaderEl })), isTopBar && (_jsx(TopNavDrawerProvider, { topOffset: showAppHeader ? `calc(${headerHeight} + ${topNavHeight})` : topNavHeight, children: _jsx(AppTopNav, { appInfo: appInfo, links: links, caseTypes: caseTypes, cases: cases, agent: agent, utils: utils, searchInput: searchInput, searchPage: searchPage, searchLabel: searchLabel }) })), !isTopBar && isSmallOrAbove && (_jsxs(Flex, { ...restProps, container: {
                    direction: 'column',
                    justify: 'between'
                }, id: navId, as: StyledSidebarNav, withAppHeader: showAppHeader, portalAgent: !!agent, ref: navRef, "data-app-region": true, onFocus: onFocus, "aria-label": t('app_shell_main_navigation_label'), children: [_jsx(AppNavigationPanel, { navState: sidebarNavState, appInfo: !showAppHeader ? appInfo : undefined, searchInput: !showAppHeader ? searchInput : undefined, searchLabel: searchLabel, caseTypes: caseTypes, links: links, cases: cases, utils: utilsList, onClick: () => {
                            if (['closing', 'closed'].includes(sidebarNavState)) {
                                openNav(0);
                            }
                            else {
                                closeNav(0);
                            }
                        }, agent: agent }), _jsxs(Flex, { container: { justify: 'center' }, children: [_jsx(StyledAppShellToggleButton, { icon: true, ref: setExpandCollapseEl, compact: true, variant: 'simple', onClick: () => {
                                    setSidebarNavState(sidebarNavState === 'closed' ? 'open' : 'closed');
                                }, "aria-label": sidebarNavState === 'closed' ? t('expand_navigation') : t('collapse_navigation'), "aria-expanded": sidebarNavState === 'open' || sidebarNavState === 'opening', "aria-controls": navId, children: ['closed', 'closing'].includes(sidebarNavState) ? (_jsx(Icon, { name: `arrow-micro-${end}` })) : (_jsx(Icon, { name: `arrow-micro-${start}` })) }), ['open', 'closed'].includes(sidebarNavState) && (_jsx(Tooltip, { target: expandCollapseEl, placement: 'right', as: StyledAppShellTooltip, showDelay: 'none', hideDelay: 'none', children: sidebarNavState === 'closed' ? t('expand_navigation') : t('collapse_navigation') }))] })] })), !isSmallOrAbove && (_jsxs(_Fragment, { children: [_jsxs(Flex, { as: StyledMobileNav, container: { alignItems: 'center', gap: 0.25 }, ref: setMobileHeaderEl, "data-app-region": true, children: [caseTypes && caseTypes.length > 0 && _jsx(CaseTypes, { caseTypes: caseTypes, mobileNavBar: true }), links && _jsx(Links, { links: links.slice(0, mobileNavLinksCount), mobileNavBar: true }), _jsxs(StyledMobileMoreMenuButton, { ref: mobileButtonRef, variant: 'simple', onClick: () => setMobileNavOpen(prev => !prev), label: t('app_shell_more'), icon: true, "aria-expanded": mobileNavOpen, "aria-controls": navId, children: [_jsx(Icon, { name: 'bars' }), !mobileNavOpen &&
                                        utils?.some(item => (isValidElement(item) ? false : !!item.count)) && (_jsx(Alert, { variant: 'urgent' })), _jsx("span", { children: t('app_shell_more') })] })] }), _jsx(Drawer, { open: mobileNavOpen, ref: mobileNavRef, onAfterClose: () => {
                            setMobileNavOpen(false);
                        }, position: 'fixed', placement: 'bottom', size: '100dvh', nullWhenClosed: true, children: _jsx(Flex, { ...restProps, container: {
                                direction: 'column',
                                justify: 'between'
                            }, id: navId, as: StyledMobileMoreMenu, ref: ref, "aria-label": t('app_shell_main_navigation_label'), children: _jsx(AppNavigationPanel, { appInfo: appInfo, navState: 'open', searchInput: searchInput, searchLabel: searchLabel, links: links?.slice(mobileNavLinksCount), cases: cases, utils: utilsList, agent: agent }) }) })] })), showMainContent && (_jsxs(StyledAppMain, { appHeader: showAppHeader, previewActive: previewActive, headerOffset: isSmallOrAbove ? headerEl?.offsetHeight : undefined, "$navOrientation": navOrientation, "aria-busy": !!progress, "data-app-region": true, children: [_jsx(Progress, { visible: !!progress, placement: 'local', message: typeof progress === 'string' ? progress : t('loading') }), banners && _jsx(StyledBannerRegion, { children: banners }), main] })), _jsx(AppShellGlobalStyle, { navHeight: isSmallOrAbove ? '0rem' : mobileHeaderHeight })] }));
});
export default AppShell;
//# sourceMappingURL=AppShell.js.map