/*!
 * Custom elements for Nanopore-Digital Web applications
 */
import { Host, Build, forceUpdate, } from "@stencil/core";
import { getDirectChildren } from "../../utils/dom";
import { ComponentStore, } from "../../utils/store/component-store";
import { transformTag, h } from "../../utils/renderer";
let id = 0;
/**
 * Tab groups organize content into a container that shows one section at a time.
 *
 * @version 1.0
 * @status stable
 *
 * @slot tabs - Add `nano-tab` elements to add clickable tabs.
 * @slot - Add `nano-tab-content` elements to add tabbable content.
 *
 * @part base - the main tab-group wrapper
 * @part nav - the bar surrounding all the tabs and nav buttons
 * @part tabs - the wrapper around the `nano-tab` components
 * @part scroll-buttons - the button which appear when the tabs overflow
 * @part active-tab-indicator - the active indicator line
 * @part body - the wrapper around the `nano-tab-content` components
 */
export class NanoTabGroup {
    baseId = `nano-tab-group-${++id}`;
    activeTab;
    activePanel;
    isRtl;
    initialTouchX = null;
    initialTouchY = null;
    init = true;
    _tab;
    get tab() {
        return this._tab;
    }
    set tab(tabName) {
        if (this.activeTab && this.activeTab.panel === this._tab)
            return;
        const selectedTabEle = this.getAllActiveTabs.find((el) => el.panel === tabName);
        if (!selectedTabEle)
            return;
        if (!this.init && this.storeId) {
            ComponentStore.update(this.storeId);
        }
        this._tab = tabName;
        this.setActiveTab(selectedTabEle);
    }
    host;
    /**
     * The placement of the tabs.
     */
    placement = 'top';
    /**
     * Disables the scroll arrows that appear when tabs overflow.
     */
    noScrollControls = false;
    /** Make all tabs equal size. This is only recommended when there are a small number of tabs */
    equalTabSize = false;
    /** Size of tabs. Default is `regular`. `large` is only available when `placement="top"` */
    size;
    /**
     * Store the open tab against this ID in the component store.
     * Use in combination with `storeMethod`
     */
    storeId;
    /**
     * The method of storage.
     * Either session storage, url hash (after an '#') or url query (after an '?').
     */
    storeMethod = 'url-hash';
    /**
     * Disable 'swipe to change tab' on devices with touch
     */
    disableSwipe;
    /** Emitted when a tab is shown. */
    nanoTabShow;
    /** Emitted when a tab is hidden. */
    nanoTabHide;
    /** Emitted when the tab is closable and the close button is activated. */
    nanoTabWillClose;
    /** Emitted when a closable tab is removed. */
    nanoTabClose;
    /** Shows the specified tab panel. */
    async show(panel) {
        if (this.activeTab && this.activeTab.panel === panel)
            return;
        const tab = this.getAllActiveTabs.find((el) => el.panel === panel);
        if (tab)
            this.setActiveTab(tab);
    }
    // Internal Methods
    get getAllActiveTabs() {
        return this.getAllTabs.filter((el) => !el.disabled);
    }
    get getAllTabs() {
        return getDirectChildren(this.host, transformTag('nano-tab'));
    }
    get getAllPanels() {
        return getDirectChildren(this.host, transformTag('nano-tab-content'));
    }
    get getActiveTab() {
        return this.getAllActiveTabs.find((el) => el.active);
    }
    setActiveTab(tab, emitEvents = true) {
        if (tab &&
            tab !== this.activeTab &&
            !tab.disabled &&
            this.getAllActiveTabs.includes(tab)) {
            const previousTab = this.activeTab;
            this.activeTab = tab;
            this.tab = tab.panel;
            // Sync tabs and panels
            this.getAllActiveTabs.map((el) => (el.active = el === this.activeTab));
            this.getAllPanels.map((el) => {
                if (el.name === this.activeTab.panel) {
                    el.active = true;
                    this.activePanel = el;
                }
                else
                    el.active = false;
                el.removeAttribute('animation-dir');
            });
            // Emit events
            if (emitEvents) {
                if (previousTab) {
                    this.nanoTabHide.emit({ name: previousTab.panel });
                }
                this.nanoTabShow.emit({ name: this.activeTab.panel });
            }
        }
    }
    setAriaLabels() {
        const tabs = this.getAllActiveTabs;
        const panels = this.getAllPanels;
        // Link each tab with its corresponding panel
        tabs.map((tab, i) => {
            const panel = panels.find((el) => el.name === tab.panel);
            if (panel) {
                if (!panel.id)
                    panel.id = `${this.baseId}-panel-${i}`;
                if (!tab.id)
                    tab.id = `${this.baseId}-tab-${i}`;
                tab.setAttribute('aria-controls', panel.id);
                panel.setAttribute('aria-labelledby', tab.id);
            }
        });
    }
    // Event handlers
    handleTabClose(ev) {
        let tabIndex = this.getAllActiveTabs.indexOf(ev.target);
        const tab = ev.target;
        if (tabIndex < 0)
            return;
        const panel = this.getAllPanels.find((el) => el.name === tab.panel);
        if (!panel)
            return;
        ev.stopImmediatePropagation();
        const closeEv = this.nanoTabWillClose.emit({ name: tab.panel });
        if (closeEv.defaultPrevented)
            return;
        // Show the previous tab if the tab is currently active
        if (tab.active) {
            tabIndex = tabIndex === 0 ? 1 : tabIndex - 1;
            const prevTab = this.getAllActiveTabs[tabIndex]?.panel;
            if (prevTab)
                this.host.show(prevTab);
        }
        tab.remove();
        panel.remove();
        this.nanoTabClose.emit({ name: tab.panel });
        forceUpdate(this.host);
    }
    handleClick = (event) => {
        const target = event.target;
        const tab = target.closest(transformTag('nano-tab'));
        if (tab)
            this.setActiveTab(tab);
    };
    handleKeyDown = (event) => {
        const activeEl = document.activeElement;
        if (!activeEl ||
            activeEl.tagName.toLowerCase() !== transformTag('nano-tab'))
            return;
        // Activate a tab
        if (['Enter', ' '].includes(event.key)) {
            const target = event.target;
            const tab = target.closest(transformTag('nano-tab'));
            if (tab) {
                this.setActiveTab(tab);
                event.preventDefault();
                event.stopPropagation();
            }
        }
        // Move focus left or right
        if (![
            'ArrowLeft',
            'ArrowRight',
            'ArrowUp',
            'ArrowDown',
            'Home',
            'End',
        ].includes(event.key))
            return;
        const tabs = this.getAllActiveTabs;
        let index = tabs.indexOf(activeEl);
        if (event.key === 'Home')
            index = 0;
        if (event.key === 'End')
            index = tabs.length - 1;
        if (this.placement === 'top') {
            if ((this.isRtl && event.key === 'ArrowRight') ||
                (!this.isRtl && event.key === 'ArrowLeft')) {
                index = Math.max(0, index - 1);
            }
            if ((this.isRtl && event.key === 'ArrowLeft') ||
                (!this.isRtl && event.key === 'ArrowRight')) {
                index = Math.min(tabs.length - 1, index + 1);
            }
        }
        if (this.placement === 'start') {
            if (event.key === 'ArrowUp')
                index = Math.max(0, index - 1);
            if (event.key === 'ArrowDown')
                index = Math.min(tabs.length - 1, index + 1);
        }
        tabs[index].setFocus();
        // need to stop bubbling otherwise it will focus on parent tabs if nested
        event.stopPropagation();
        // stop the browser moving about
        event.preventDefault();
    };
    handleTouchStart = (event) => {
        if (this.disableSwipe)
            return;
        const touch = event.changedTouches[0];
        this.initialTouchX = touch.pageX;
        this.initialTouchY = touch.pageY;
    };
    handleTouchEnd = (event) => {
        if (this.disableSwipe)
            return;
        const touch = event.changedTouches[0];
        const distX = touch.pageX - this.initialTouchX; // get horizontal dist travelled
        const threshold = 70;
        const xDiff = this.initialTouchX - touch.clientX;
        const yDiff = this.initialTouchY - touch.clientY;
        const isHorizontalSwipe = Math.abs(distX) >= threshold && Math.abs(yDiff) <= 50;
        const tabs = this.getAllActiveTabs;
        const currIndex = tabs.findIndex((el) => el.active);
        if (isHorizontalSwipe) {
            const toGo = (distX < 0 && this.placement === 'top') ||
                (distX > 0 && this.placement !== 'top')
                ? 1
                : -1;
            if (tabs[currIndex + toGo]) {
                this.setActiveTab(tabs[currIndex + toGo]);
                if (Math.abs(xDiff) > Math.abs(yDiff)) {
                    if (xDiff > 0)
                        this.activePanel.setAttribute('animation-dir', 'left');
                    else
                        this.activePanel.setAttribute('animation-dir', 'right');
                }
            }
        }
        this.initialTouchX = null;
        this.initialTouchY = null;
    };
    handleContentSlotChange = () => {
        setTimeout(() => {
            this.setAriaLabels();
            this.setActiveTab(this.getActiveTab || this.getAllActiveTabs[0], false);
        });
    };
    handleTabSlotChange = () => {
        setTimeout(() => {
            this.setAriaLabels();
            this.setActiveTab(this.getActiveTab || this.getAllActiveTabs[0], false);
        }, 500);
    };
    // Stencil hooks
    componentWillLoad() {
        this.init = true;
    }
    componentDidLoad() {
        // Initial IO - watching for all content to be loaded
        const observer = new IntersectionObserver((entries, observer) => {
            if (entries[0].intersectionRatio > 0) {
                this.setAriaLabels();
                if (this.storeId) {
                    ComponentStore.init(this, ['tab'], this.storeMethod, this.storeId);
                    const store = ComponentStore.get(this.storeId);
                    const storedTab = store.retrieve()?.tab;
                    // if a tab is in storage, set it as active now
                    if (storedTab && storedTab !== store.initialState?.tab) {
                        this.getAllActiveTabs.forEach((element) => {
                            if (element.panel === storedTab) {
                                element.active = true;
                            }
                            else {
                                element.active = false;
                            }
                        });
                    }
                }
                this.setActiveTab(this.getActiveTab || this.getAllActiveTabs[0], false);
                observer.unobserve(entries[0].target);
                this.init = false;
            }
        });
        observer.observe(this.host);
    }
    connectedCallback() {
        this.isRtl =
            this.host.dir === 'rtl' || this.host.ownerDocument.dir === 'rtl';
        if (Build.isServer) {
            this.setActiveTab(this.getActiveTab || this.getAllActiveTabs[0], false);
            this.setAriaLabels();
        }
    }
    render() {
        this.isRtl = this.host.ownerDocument.dir === 'rtl';
        if (this.getAllTabs.length === 0)
            return;
        return (h(Host, { dir: this.isRtl ? 'rtl' : null, class: "nano-tab-group" }, h("div", { part: "base", class: {
                group: true,
                'group--top': this.placement === 'top',
                'group--start': this.placement === 'start',
            }, onClick: this.handleClick, onKeyDown: this.handleKeyDown }, h("nano-masked-overflow", { role: "navigation", class: "group__tabs", part: "tabs__wrapper", exportparts: "items:tabs, scroll-button:tabs__scroll-button, indicator:active-tab-indicator", orientation: this.placement === 'top' ? 'horizontal' : 'vertical', isActiveHandler: (itm) => this.activeTab === itm, scrollControls: !this.noScrollControls, hideScrollbars: true }, h("slot", { name: "tabs", onSlotchange: this.handleTabSlotChange }), h("div", { class: "group__tabs-border" })), h("div", { part: "body", class: "group__body", onTouchStart: this.handleTouchStart, onTouchEnd: this.handleTouchEnd }, h("slot", { onSlotchange: this.handleContentSlotChange })))));
    }
    static get is() { return "nano-tab-group"; }
    static get encapsulation() { return "shadow"; }
    static get originalStyleUrls() {
        return {
            "$": ["tab-group.scss"]
        };
    }
    static get styleUrls() {
        return {
            "$": ["tab-group.css"]
        };
    }
    static get properties() {
        return {
            "placement": {
                "type": "string",
                "attribute": "placement",
                "mutable": false,
                "complexType": {
                    "original": "'top' | 'start'",
                    "resolved": "\"start\" | \"top\"",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "The placement of the tabs."
                },
                "getter": false,
                "setter": false,
                "reflect": true,
                "defaultValue": "'top'"
            },
            "noScrollControls": {
                "type": "boolean",
                "attribute": "no-scroll-controls",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "Disables the scroll arrows that appear when tabs overflow."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "defaultValue": "false"
            },
            "equalTabSize": {
                "type": "boolean",
                "attribute": "equal-tab-size",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "Make all tabs equal size. This is only recommended when there are a small number of tabs"
                },
                "getter": false,
                "setter": false,
                "reflect": true,
                "defaultValue": "false"
            },
            "size": {
                "type": "string",
                "attribute": "size",
                "mutable": false,
                "complexType": {
                    "original": "'regular' | 'large'",
                    "resolved": "\"large\" | \"regular\"",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "Size of tabs. Default is `regular`. `large` is only available when `placement=\"top\"`"
                },
                "getter": false,
                "setter": false,
                "reflect": true
            },
            "storeId": {
                "type": "string",
                "attribute": "store-id",
                "mutable": false,
                "complexType": {
                    "original": "string",
                    "resolved": "string",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "Store the open tab against this ID in the component store.\nUse in combination with `storeMethod`"
                },
                "getter": false,
                "setter": false,
                "reflect": false
            },
            "storeMethod": {
                "type": "string",
                "attribute": "store-method",
                "mutable": false,
                "complexType": {
                    "original": "StorageMethods",
                    "resolved": "\"local\" | \"session\" | \"url-hash\" | \"url-hash-push\" | \"url-query\" | \"url-query-push\"",
                    "references": {
                        "StorageMethods": {
                            "location": "import",
                            "path": "../../utils/store/component-store",
                            "id": "src/utils/store/component-store.ts::StorageMethods"
                        }
                    }
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "The method of storage.\nEither session storage, url hash (after an '#') or url query (after an '?')."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "defaultValue": "'url-hash'"
            },
            "disableSwipe": {
                "type": "boolean",
                "attribute": "disable-swipe",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "Disable 'swipe to change tab' on devices with touch"
                },
                "getter": false,
                "setter": false,
                "reflect": false
            }
        };
    }
    static get events() {
        return [{
                "method": "nanoTabShow",
                "name": "nanoTabShow",
                "bubbles": true,
                "cancelable": true,
                "composed": true,
                "docs": {
                    "tags": [],
                    "text": "Emitted when a tab is shown."
                },
                "complexType": {
                    "original": "{ name: string }",
                    "resolved": "{ name: string; }",
                    "references": {}
                }
            }, {
                "method": "nanoTabHide",
                "name": "nanoTabHide",
                "bubbles": true,
                "cancelable": true,
                "composed": true,
                "docs": {
                    "tags": [],
                    "text": "Emitted when a tab is hidden."
                },
                "complexType": {
                    "original": "{ name: string }",
                    "resolved": "{ name: string; }",
                    "references": {}
                }
            }, {
                "method": "nanoTabWillClose",
                "name": "nanoTabWillClose",
                "bubbles": true,
                "cancelable": true,
                "composed": true,
                "docs": {
                    "tags": [],
                    "text": "Emitted when the tab is closable and the close button is activated."
                },
                "complexType": {
                    "original": "{ name: string }",
                    "resolved": "{ name: string; }",
                    "references": {}
                }
            }, {
                "method": "nanoTabClose",
                "name": "nanoTabClose",
                "bubbles": true,
                "cancelable": true,
                "composed": true,
                "docs": {
                    "tags": [],
                    "text": "Emitted when a closable tab is removed."
                },
                "complexType": {
                    "original": "{ name: string }",
                    "resolved": "{ name: string; }",
                    "references": {}
                }
            }];
    }
    static get methods() {
        return {
            "show": {
                "complexType": {
                    "signature": "(panel: string) => Promise<void>",
                    "parameters": [{
                            "name": "panel",
                            "type": "string",
                            "docs": ""
                        }],
                    "references": {
                        "Promise": {
                            "location": "global",
                            "id": "global::Promise"
                        }
                    },
                    "return": "Promise<void>"
                },
                "docs": {
                    "text": "Shows the specified tab panel.",
                    "tags": []
                }
            }
        };
    }
    static get elementRef() { return "host"; }
    static get listeners() {
        return [{
                "name": "nanoTabClose",
                "method": "handleTabClose",
                "target": undefined,
                "capture": false,
                "passive": false
            }];
    }
}
