import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
/*
 * Copyright (c) 2023-2025 Datalayer, Inc.
 * Distributed under the terms of the Modified BSD License.
 */
import React, { Children, createContext, forwardRef, isValidElement, memo, useCallback, useContext, useEffect, useMemo, useRef, useState, } from 'react';
import { createPortal } from 'react-dom';
import { Button, ButtonGroup, Text, ThemeProvider, useWindowSize, } from '@primer/react-brand';
import { ChevronDownIcon, ChevronUpIcon } from '@primer/octicons-react';
import { default as clsx } from 'clsx';
import { useId } from '../../hooks/index.js';
import { useKeyboardEscape, useOnClickOutside, useProvidedRefOrCreate, useContainsFocus, } from '../../hooks/index.js';
/**
 * Design tokens
 */
import '@primer/brand-primitives/lib/design-tokens/css/tokens/functional/components/sub-nav/base.css';
import '@primer/brand-primitives/lib/design-tokens/css/tokens/functional/components/sub-nav/colors-with-modes.css';
/** * Main Stylesheet (as a CSS Module) */
import styles from './SubNav.module.css';
const testIds = {
    root: 'SubNav-root',
    get button() {
        return `${this.root}-button`;
    },
    get overlay() {
        return `${this.root}-overlay`;
    },
    get link() {
        return `${this.root}-link`;
    },
    get heading() {
        return `${this.root}-heading`;
    },
    get action() {
        return `${this.root}-action`;
    },
    get subMenu() {
        return `${this.root}-sub-menu`;
    },
};
export const SubNavSubMenuVariants = ['dropdown', 'anchor'];
const SubNavContext = createContext(undefined);
export const useSubNavContext = () => {
    const context = useContext(SubNavContext);
    if (!context) {
        throw new Error('useSubNavContext must be used within a SubNavProvider');
    }
    return context;
};
function SubNavProvider({ children }) {
    const anchoredNavOuterPortalRef = React.useRef(null);
    const anchoredNavPortalRef = React.useRef(null);
    const value = useMemo(() => ({
        portalRef: anchoredNavPortalRef,
    }), []);
    useEffect(() => {
        const menuContainer = anchoredNavOuterPortalRef.current;
        const observer = new IntersectionObserver(([entry]) => {
            entry.target.classList.toggle(styles['SubNav__anchor-menu-outer-container--stuck'], entry.intersectionRatio < 1);
        }, { threshold: [1] });
        if (menuContainer) {
            observer.observe(menuContainer);
        }
        return () => {
            if (menuContainer) {
                observer.unobserve(menuContainer);
            }
        };
    }, []);
    return (_jsxs(SubNavContext.Provider, { value: value, children: [children, _jsx("div", { className: styles['SubNav__anchor-menu-outer-container'], ref: anchoredNavOuterPortalRef, children: _jsx("div", { className: clsx(styles['SubNav__anchor-menu-container']), ref: anchoredNavPortalRef }) })] }));
}
const _SubNavRoot = memo(({ id, children, className, 'data-testid': testId, fullWidth, hasShadow, }) => {
    const rootRef = React.useRef(null);
    const navRef = React.useRef(null);
    const overlayRef = React.useRef(null);
    const [isOpenAtNarrow, setIsOpenAtNarrow] = useState(false);
    const idForLinkContainer = useId();
    const [hasAnchoredNav, setHasAnchoredNav] = useState(false);
    const { isLarge } = useWindowSize();
    const childrenArr = Children.toArray(children);
    const closeMenuCallback = useCallback(() => {
        if (isLarge)
            return;
        setIsOpenAtNarrow(false);
    }, [isLarge]);
    const handleMenuToggle = useCallback(() => {
        if (isLarge)
            return;
        setIsOpenAtNarrow(prev => !prev);
    }, [isLarge]);
    useOnClickOutside(rootRef, closeMenuCallback);
    useKeyboardEscape(closeMenuCallback);
    useEffect(() => {
        if (isOpenAtNarrow && !isLarge) {
            document.body.style.overflow = 'hidden';
        }
        else {
            document.body.style.overflow = 'auto';
        }
    }, [isOpenAtNarrow, isLarge]);
    const activeLink = childrenArr.find(child => {
        if (isValidElement(child)) {
            return child.props['aria-current'];
        }
    });
    useEffect(() => {
        // check if there is an anchored nav in the SubNav.SubMenu child
        const hasAnchorVariant = childrenArr.some(child => {
            if (isValidElement(child) && child.type === SubNavLink) {
                const [, subMenu] = child.props.children;
                if (subMenu?.props?.variant === 'anchor') {
                    return true;
                }
            }
        });
        setHasAnchoredNav(hasAnchorVariant);
    }, [childrenArr]);
    const { heading: HeadingChild, links: LinkChildren, action: ActionChild, } = childrenArr.reduce((acc, child) => {
        if (isValidElement(child)) {
            if (child.type === SubNavHeading) {
                acc.heading = child;
            }
            else if (child.type === SubNavLink) {
                const [link, subMenu] = child.props.children;
                if (subMenu?.props?.variant === 'anchor') {
                    acc.links.push(React.cloneElement(child, {
                        children: [link],
                        onClick: child.props['aria-current']
                            ? closeMenuCallback
                            : child.props.onClick,
                    }));
                }
                else {
                    acc.links.push(React.cloneElement(child, {
                        onClick: child.props['aria-current']
                            ? closeMenuCallback
                            : child.props.onClick,
                    }));
                }
            }
            else if (child.type === _SubNavAction) {
                acc.action = child;
            }
        }
        return acc;
    }, { heading: undefined, links: [], action: undefined });
    const activeLinklabel = typeof activeLink?.props.children === 'string'
        ? activeLink.props.children
        : activeLink?.props.children[0];
    // needed to prevent rendering of anchor subnav inside the narrow <Button variant="invisible"> element
    const MaybeSubNav = activeLink?.props.children?.[1]?.props?.variant === 'anchor' &&
        activeLink.props.children?.[1];
    return (_jsx("div", { className: clsx(styles['SubNav__container'], hasAnchoredNav && styles['SubNav__container--with-anchor-nav']), children: _jsx(SubNavProvider, { children: _jsx("nav", { ref: navRef, id: id, className: clsx(styles.SubNav, isOpenAtNarrow && styles['SubNav--open'], hasShadow && styles['SubNav--has-shadow'], fullWidth && styles['SubNav--full-width'], className), "data-testid": testId || testIds.root, children: _jsxs("div", { ref: rootRef, className: styles['SubNav--header-container-outer'], children: [_jsxs("div", { className: styles['SubNav__header-container'], children: [HeadingChild && (_jsx("div", { className: styles['SubNav__heading-container'], children: HeadingChild })), !isLarge && (_jsxs("button", { className: clsx(styles['SubNav__overlay-toggle'], isOpenAtNarrow && styles['SubNav__overlay-toggle--open']), "data-testid": testIds.button, onClick: isOpenAtNarrow ? closeMenuCallback : handleMenuToggle, "aria-expanded": isOpenAtNarrow ? 'true' : 'false', "aria-controls": idForLinkContainer, children: [activeLinklabel && (_jsxs("span", { className: "visually-hidden", children: ["Navigation menu. Current page:", ' '] })), _jsxs("span", { className: clsx(styles['SubNav__overlay-toggle-content'], !activeLinklabel &&
                                                styles['SubNav__overlay-toggle-content--end']), children: [activeLinklabel && (_jsx(Text, { as: "span", size: "200", children: activeLinklabel })), isOpenAtNarrow ? (_jsx(ChevronUpIcon, { className: styles['SubNav__overlay-toggle-icon'], size: 24 })) : (_jsx(ChevronDownIcon, { className: styles['SubNav__overlay-toggle-icon'], size: 24 }))] })] })), MaybeSubNav && MaybeSubNav] }), LinkChildren.length && (_jsxs("ul", { ref: overlayRef, id: idForLinkContainer, className: clsx(styles['SubNav__links-overlay'], isOpenAtNarrow && styles['SubNav__links-overlay--open']), "data-testid": testIds.overlay, children: [ActionChild ? (_jsx("li", { className: styles['SubNav__action-container'], children: ActionChild })) : (_jsx("li", { className: styles['SubNav__action-container'] })), LinkChildren, _jsx(ButtonGroup, { buttonSize: "small", children: _jsx(Button, { href: "#", onClick: e => {
                                            window.location.assign('https://datalayer.ai');
                                        }, hasArrow: true, variant: "subtle", children: "Login" }) })] }))] }) }) }) }));
});
const SubNavHeading = ({ href, children, className, 'data-testid': testID, ...props }) => {
    return (_jsx("a", { href: href, className: clsx(styles['SubNav__heading'], className), "data-testid": testIds.heading || testID, ...props, children: children }));
};
const SubNavLinkWithSubmenu = forwardRef(({ children, href, 'aria-current': ariaCurrent, 'data-testid': testId, className, _subMenuVariant, variant, ...props }, forwardedRef) => {
    const submenuId = useId();
    const { isLarge } = useWindowSize();
    const [isExpanded, setIsExpanded] = useState(false);
    const ref = useProvidedRefOrCreate(forwardedRef);
    useContainsFocus(ref, (containsFocus) => {
        if (!containsFocus) {
            setIsExpanded(false);
        }
    });
    const [label, SubMenuChildren] = children;
    const handleOnClick = useCallback(() => {
        setIsExpanded(prev => !prev);
    }, []);
    return (_jsxs("div", { className: clsx(styles['SubNav__link--has-sub-menu'], isExpanded && styles['SubNav__link--expanded']), "data-testid": testId || testIds.subMenu, ref: ref, onMouseOver: () => setIsExpanded(true), onMouseOut: () => setIsExpanded(false), 
        /**
         * onFocus and onBlur need to be defined to keep the jsx-a11y/mouse-events-have-key-events
         * eslint rule happy. The focus/blur behaviour is handled by useContainsFocus
         */
        onFocus: () => null, onBlur: () => null, children: [_jsx("a", { href: href, className: clsx(styles['SubNav__link'], ariaCurrent && styles['SubNav__link--active'], className), "aria-current": ariaCurrent, ...props, children: _jsx(Text, { as: "span", size: "200", weight: "semibold", className: styles['SubNav__link-label'], variant: ariaCurrent === 'page' || variant === 'default'
                        ? 'default'
                        : 'muted', children: label }) }), isLarge && (_jsx("button", { className: styles['SubNav__sub-menu-toggle'], onClick: handleOnClick, "aria-expanded": isExpanded ? 'true' : 'false', "aria-controls": submenuId, "aria-label": `${isExpanded ? 'Close' : 'Open'} submenu`, children: _jsx(ChevronDownIcon, { className: styles['SubNav__sub-menu-icon'], size: 16 }) })), _jsx("div", { id: submenuId, className: styles['SubNav__sub-menu-children'], children: SubMenuChildren })] }));
});
const SubNavLink = forwardRef((props, ref) => {
    const [isInView, setIsInView] = useState(false);
    const childrenArr = Children.toArray(props.children);
    const hasSubMenu = childrenArr.some(child => {
        if (isValidElement(child)) {
            return child.type === SubMenu;
        }
    });
    useEffect(() => {
        if (hasSubMenu)
            return;
        const targetId = props.href.replace('#', '');
        const target = document.getElementById(targetId);
        if (!target)
            return;
        const topOfWindow = '0px 0px -100%';
        const observerParams = {
            threshold: 0,
            root: null,
            rootMargin: topOfWindow,
        };
        const handleIntersectionUpdate = ([entry,]) => {
            setIsInView(entry.isIntersecting);
        };
        const observer = new IntersectionObserver(handleIntersectionUpdate, observerParams);
        observer.observe(target);
        return () => observer.disconnect();
    }, [hasSubMenu, props.href]);
    if (hasSubMenu) {
        const isAnchorVariantSubMenu = childrenArr.some(child => {
            if (isValidElement(child)) {
                return child.type === SubMenu && child.props.variant === 'anchor';
            }
        });
        return (_jsx("li", { children: _jsx(SubNavLinkWithSubmenu, { ...props, ref: ref, _subMenuVariant: isAnchorVariantSubMenu ? 'anchor' : undefined }) }));
    }
    const { children, href, 'aria-current': ariaCurrent, 'data-testid': testId, variant, className, ...rest } = props;
    return (_jsx("li", { children: _jsx("a", { href: href, className: clsx(styles['SubNav__link'], ariaCurrent && styles['SubNav__link--active'], isInView && styles['SubNav__link--is-in-view'], className), "aria-current": ariaCurrent, "data-testid": testId || testIds.link, ref: ref, ...rest, children: _jsx(Text, { as: "span", size: "100", weight: "semibold", className: styles['SubNav__link-label'], variant: ariaCurrent === 'page' || variant === 'default'
                    ? 'default'
                    : 'muted', children: children }) }) }));
});
function SubMenu({ children, className, variant = 'dropdown', ...props }) {
    const context = React.useContext(SubNavContext);
    const navRef = useRef(null);
    const { isLarge } = useWindowSize();
    /**
     * Effect is needed to prevent the bubbling of onClick events to the overlay trigger.
     * Removing this effect will cause clicks on the anchor nav element to toggle the overlay.
     */
    useEffect(() => {
        const handleClick = (e) => {
            if (navRef.current && !navRef.current.contains(e.target)) {
                return;
            }
            if (!(e.target instanceof HTMLAnchorElement)) {
                e.stopPropagation();
            }
        };
        if (variant === 'anchor') {
            document.addEventListener('click', handleClick, true); // Capture phase
        }
        return () => {
            document.removeEventListener('click', handleClick, true);
        };
    }, [variant]);
    if (variant === 'anchor' && context?.portalRef.current) {
        return createPortal(_jsx("nav", { ref: navRef, className: clsx(styles['SubNav__sub-menu'], styles['SubNav__sub-menu--anchor'], className), role: "navigation", "aria-label": "Sub navigation", children: _jsx("ul", { className: styles['SubNav__sub-menu-list'], ...props, children: React.Children.map(children, child => {
                    if (isValidElement(child)) {
                        return React.cloneElement(child, {
                            onClick: e => {
                                if (child.props.onClick) {
                                    child.props.onClick(e);
                                }
                            },
                        });
                    }
                }) }) }), context.portalRef.current);
    }
    else {
        const Tag = isLarge ? ThemeProvider : React.Fragment;
        return (_jsx(Tag, { ...(isLarge ? { colorMode: 'light' } : {}), children: _jsx("ul", { className: clsx(styles['SubNav__sub-menu'], styles[`SubNav__sub-menu--${variant}`], className), ...props, children: children }) }));
    }
}
function _SubNavAction({ children, href, variant = 'primary', size = 'small', ...rest }) {
    return (_jsx(Button, { className: styles['SubNav__action'], as: "a", href: href, variant: variant, hasArrow: false, "data-testid": testIds.action, size: size, ...rest, children: children }));
}
/**
 * Use SubNav to display a secondary navigation beneath a primary header.
 * @see https://primer.style/brand/components/SubNav
 */
export const SubNav = Object.assign(_SubNavRoot, {
    Heading: SubNavHeading,
    Link: SubNavLink,
    Action: _SubNavAction,
    SubMenu: SubMenu,
    testIds,
});
