/*!
 * Custom elements for Nanopore-Digital Web applications
 */
import { Host, Build, } from "@stencil/core";
import { h } from "../../utils/renderer";
import { HasSlotController } from "../../utils/slot";
import { getDirectChildren } from "../../utils/dom";
import { findScrollParent } from "../../utils/scroll";
/**
 * In-page navigation allows users to quickly find different sections on a page.
 *
 * @status new
 * @version 8.0.0
 *
 * @slot default - The default slot.
 * @slot back - For a link to navigate back to the previous section.
 * @slot accessory - To add nav accessories, such as a text input.
 *
 * @part root - The root element of the navigation.
 */
export class NanoInPageNav {
    host;
    autoExternalLinks = false;
    io;
    mobileNavWrapper;
    slotCtrl = new HasSlotController(this, '[default]', 'back', 'accessory');
    /** Whether the active link should be automatically set based on when a corresponding #id is in view. Defaults to true */
    autoActive = true;
    /** Menus that contain links to external pages should have different styling from in-page anchors */
    externalLinks = 'auto';
    createMobileMenu() {
        const ul = getDirectChildren(this.host, 'ul')[0];
        if (!ul)
            return;
        const cloned = ul.cloneNode(true);
        getDirectChildren(cloned, 'li', true).forEach((li) => {
            const link = getDirectChildren(li, 'a', true)[0];
            const submenu = getDirectChildren(li, 'ul', true)[0];
            if (link && submenu) {
                const details = this.createDetails(link.textContent?.trim() || '', submenu);
                link.remove();
                li.prepend(details);
            }
        });
        this.mobileNavWrapper.appendChild(cloned);
    }
    /**
     * Creates a nano-details element with the given label and submenu.
     * @returns a nano-details element
     */
    createDetails(label, submenu) {
        const node = document.createElement('nano-details');
        node.setAttribute('label', label);
        node.classList.add('nav-details');
        node.appendChild(submenu);
        return node;
    }
    assessLinks() {
        if (this.externalLinks !== 'auto')
            return;
        const anchors = this.host.querySelectorAll('a[href^="#"]');
        const allLinks = this.host.querySelectorAll('a[href]');
        if (anchors.length > allLinks.length / 2) {
            this.autoExternalLinks = false;
        }
        else {
            this.autoExternalLinks = true;
        }
    }
    setupIntersectionObserver() {
        if (!this.autoActive)
            return;
        const anchors = this.host.querySelectorAll('a[href^="#"]');
        const sections = [];
        const scrollElement = findScrollParent(this.host);
        anchors.forEach((link) => {
            const id = link.getAttribute('href')?.substring(1);
            if (!id)
                return;
            const section = document.getElementById(id);
            if (section)
                sections.push(section);
        });
        if (this.io)
            this.io.disconnect();
        this.io = new IntersectionObserver((entries) => {
            entries.forEach((entry) => {
                const id = entry.target.getAttribute('id');
                if (!id)
                    return;
                const link = this.host.querySelector(`a[href="#${id}"]`);
                if (!link)
                    return;
                if (entry.isIntersecting) {
                    entry.target.classList.add('in-view');
                }
                else {
                    entry.target.classList.remove('in-view');
                }
            });
            // pick the section nearest to the top
            const visible = sections.filter((s) => s.classList.contains('in-view'));
            if (visible.length) {
                // sort by boundingClientRect.top
                visible.sort((a, b) => b.getBoundingClientRect().top - a.getBoundingClientRect().top);
                anchors.forEach((l) => l.classList.remove('is-active'));
                const topId = visible[0].getAttribute('id');
                const activeLink = this.host.querySelector(`a[href="#${topId}"]`);
                activeLink?.classList.add('is-active');
            }
        }, {
            root: scrollElement === document.documentElement ? null : scrollElement,
            rootMargin: '0px 0px -60% 0px',
            threshold: 0,
        });
        sections.forEach((section) => this.io.observe(section));
    }
    handleSlotChange = () => {
        if (Build.isServer)
            return;
        this.assessLinks();
        this.createMobileMenu();
        this.setupIntersectionObserver();
    };
    componentDidLoad() {
        this.createMobileMenu();
    }
    connectedCallback() {
        this.assessLinks();
        if (Build.isBrowser) {
            this.setupIntersectionObserver();
        }
    }
    disconnectedCallback() {
        if (this.io) {
            this.io.disconnect();
            this.io = null;
        }
    }
    render() {
        return (h(Host, { key: 'a975c3e0b58fe39d504281784dd2f16e086eb0a1', class: {
                'nano-in-page-nav': true,
                'external-links': this.externalLinks === true ||
                    (this.externalLinks === 'auto' && this.autoExternalLinks),
            } }, h("nav", { key: 'd5ecad4befef8a17ab87bc6fd8166548d3bbac02', part: "root", "aria-label": "In-page navigation" }, (this.slotCtrl.has('back') || this.slotCtrl.has('accessory')) && (h("div", { key: 'b8af62ddcfb510d5db84b10fdae6c3abc140adff', class: "header" }, h("slot", { key: 'd54e126201453b1b4ddf0d788193eecd9fb3f32f', name: "back" }), h("slot", { key: 'aabc23912eb3f55c38793596f347de8c1bb72fa0', name: "accessory" }))), h("div", { key: 'dcd9be9561af10b16731a80072ee851f16d1cacd', class: "desktop-nav" }, h("slot", { key: '5af492dafc853a042d14fa752f327303c102be23', onSlotchange: () => this.handleSlotChange })), h("nano-details", { key: 'b04115b88b4f2fbcfad931f82de1fe70030400be', label: "Menu", class: "details-wrapper mobile-nav", ref: (el) => (this.mobileNavWrapper = el) }))));
    }
    static get is() { return "nano-in-page-nav"; }
    static get encapsulation() { return "shadow"; }
    static get originalStyleUrls() {
        return {
            "$": ["in-page-nav.scss"]
        };
    }
    static get styleUrls() {
        return {
            "$": ["in-page-nav.css"]
        };
    }
    static get properties() {
        return {
            "autoActive": {
                "type": "boolean",
                "attribute": "auto-active",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "Whether the active link should be automatically set based on when a corresponding #id is in view. Defaults to true"
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "defaultValue": "true"
            },
            "externalLinks": {
                "type": "any",
                "attribute": "external-links",
                "mutable": false,
                "complexType": {
                    "original": "true | false | 'auto'",
                    "resolved": "\"auto\" | boolean",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "Menus that contain links to external pages should have different styling from in-page anchors"
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "defaultValue": "'auto'"
            }
        };
    }
    static get states() {
        return {
            "autoExternalLinks": {}
        };
    }
    static get elementRef() { return "host"; }
}
