import { __esDecorate, __runInitializers } from "tslib";
import { html, LitElement, nothing } from 'lit';
import { property, query, queryAssignedElements, state } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js';
import { styleMap } from 'lit/directives/style-map.js';
import { Easing } from '../../motion/animation.js';
import '../../icon-button/ui-icon-button.js';
import '../../divider/ui-divider.js';
import '../../icons/ui-icon.js';
/**
 * Calculates value in percentages
 * @param w Element width
 * @param w0 Parent width
 * @return The percentage of element's width relative to parent.
 */
export function calcPercent(w, w0) {
    return (100 * w) / w0;
}
let UiTabs = (() => {
    let _classSuper = LitElement;
    let _assignedElements_decorators;
    let _assignedElements_initializers = [];
    let _assignedElements_extraInitializers = [];
    let _pointer_decorators;
    let _pointer_initializers = [];
    let _pointer_extraInitializers = [];
    let _content_decorators;
    let _content_initializers = [];
    let _content_extraInitializers = [];
    let _priority_decorators;
    let _priority_initializers = [];
    let _priority_extraInitializers = [];
    let _scrollable_decorators;
    let _scrollable_initializers = [];
    let _scrollable_extraInitializers = [];
    let _selected_decorators;
    let _selected_initializers = [];
    let _selected_extraInitializers = [];
    let _selectedAttribute_decorators;
    let _selectedAttribute_initializers = [];
    let _selectedAttribute_extraInitializers = [];
    let _pointerStyles_decorators;
    let _pointerStyles_initializers = [];
    let _pointerStyles_extraInitializers = [];
    let _indicated_decorators;
    let _indicated_initializers = [];
    let _indicated_extraInitializers = [];
    let _isVisible_decorators;
    let _isVisible_initializers = [];
    let _isVisible_extraInitializers = [];
    return class UiTabs extends _classSuper {
        static {
            const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
            _assignedElements_decorators = [queryAssignedElements({ flatten: true })];
            _pointer_decorators = [query('.pointer')];
            _content_decorators = [query('.content')];
            _priority_decorators = [property({ type: String, reflect: true })];
            _scrollable_decorators = [property({ type: Boolean })];
            _selected_decorators = [property({ type: String })];
            _selectedAttribute_decorators = [property({ type: String })];
            _pointerStyles_decorators = [state()];
            _indicated_decorators = [state()];
            _isVisible_decorators = [state()];
            __esDecorate(this, null, _assignedElements_decorators, { kind: "accessor", name: "assignedElements", static: false, private: false, access: { has: obj => "assignedElements" in obj, get: obj => obj.assignedElements, set: (obj, value) => { obj.assignedElements = value; } }, metadata: _metadata }, _assignedElements_initializers, _assignedElements_extraInitializers);
            __esDecorate(this, null, _pointer_decorators, { kind: "accessor", name: "pointer", static: false, private: false, access: { has: obj => "pointer" in obj, get: obj => obj.pointer, set: (obj, value) => { obj.pointer = value; } }, metadata: _metadata }, _pointer_initializers, _pointer_extraInitializers);
            __esDecorate(this, null, _content_decorators, { kind: "accessor", name: "content", static: false, private: false, access: { has: obj => "content" in obj, get: obj => obj.content, set: (obj, value) => { obj.content = value; } }, metadata: _metadata }, _content_initializers, _content_extraInitializers);
            __esDecorate(this, null, _priority_decorators, { kind: "accessor", name: "priority", static: false, private: false, access: { has: obj => "priority" in obj, get: obj => obj.priority, set: (obj, value) => { obj.priority = value; } }, metadata: _metadata }, _priority_initializers, _priority_extraInitializers);
            __esDecorate(this, null, _scrollable_decorators, { kind: "accessor", name: "scrollable", static: false, private: false, access: { has: obj => "scrollable" in obj, get: obj => obj.scrollable, set: (obj, value) => { obj.scrollable = value; } }, metadata: _metadata }, _scrollable_initializers, _scrollable_extraInitializers);
            __esDecorate(this, null, _selected_decorators, { kind: "accessor", name: "selected", static: false, private: false, access: { has: obj => "selected" in obj, get: obj => obj.selected, set: (obj, value) => { obj.selected = value; } }, metadata: _metadata }, _selected_initializers, _selected_extraInitializers);
            __esDecorate(this, null, _selectedAttribute_decorators, { kind: "accessor", name: "selectedAttribute", static: false, private: false, access: { has: obj => "selectedAttribute" in obj, get: obj => obj.selectedAttribute, set: (obj, value) => { obj.selectedAttribute = value; } }, metadata: _metadata }, _selectedAttribute_initializers, _selectedAttribute_extraInitializers);
            __esDecorate(this, null, _pointerStyles_decorators, { kind: "accessor", name: "pointerStyles", static: false, private: false, access: { has: obj => "pointerStyles" in obj, get: obj => obj.pointerStyles, set: (obj, value) => { obj.pointerStyles = value; } }, metadata: _metadata }, _pointerStyles_initializers, _pointerStyles_extraInitializers);
            __esDecorate(this, null, _indicated_decorators, { kind: "accessor", name: "indicated", static: false, private: false, access: { has: obj => "indicated" in obj, get: obj => obj.indicated, set: (obj, value) => { obj.indicated = value; } }, metadata: _metadata }, _indicated_initializers, _indicated_extraInitializers);
            __esDecorate(this, null, _isVisible_decorators, { kind: "accessor", name: "isVisible", static: false, private: false, access: { has: obj => "isVisible" in obj, get: obj => obj.isVisible, set: (obj, value) => { obj.isVisible = value; } }, metadata: _metadata }, _isVisible_initializers, _isVisible_extraInitializers);
            if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
        }
        items = [];
        activeItem = null;
        previousItem = null;
        #assignedElements_accessor_storage = __runInitializers(this, _assignedElements_initializers, void 0);
        get assignedElements() { return this.#assignedElements_accessor_storage; }
        set assignedElements(value) { this.#assignedElements_accessor_storage = value; }
        #pointer_accessor_storage = (__runInitializers(this, _assignedElements_extraInitializers), __runInitializers(this, _pointer_initializers, void 0));
        get pointer() { return this.#pointer_accessor_storage; }
        set pointer(value) { this.#pointer_accessor_storage = value; }
        #content_accessor_storage = (__runInitializers(this, _pointer_extraInitializers), __runInitializers(this, _content_initializers, void 0));
        get content() { return this.#content_accessor_storage; }
        set content(value) { this.#content_accessor_storage = value; }
        #priority_accessor_storage = (__runInitializers(this, _content_extraInitializers), __runInitializers(this, _priority_initializers, 'primary'
        /**
         * If true, tabs are scrollable and the tab width is based on the label width.
         * @attribute
         */
        ));
        /**
         * The priority of the tabs.
         *
         * @default primary
         * @attribute
         */
        get priority() { return this.#priority_accessor_storage; }
        set priority(value) { this.#priority_accessor_storage = value; }
        #scrollable_accessor_storage = (__runInitializers(this, _priority_extraInitializers), __runInitializers(this, _scrollable_initializers, void 0));
        /**
         * If true, tabs are scrollable and the tab width is based on the label width.
         * @attribute
         */
        get scrollable() { return this.#scrollable_accessor_storage; }
        set scrollable(value) { this.#scrollable_accessor_storage = value; }
        #selected_accessor_storage = (__runInitializers(this, _scrollable_extraInitializers), __runInitializers(this, _selected_initializers, void 0));
        /**
         * The value of the selected tab.
         * This is matched with the `aria-controls` of the tab.
         * @attribute
         */
        get selected() { return this.#selected_accessor_storage; }
        set selected(value) { this.#selected_accessor_storage = value; }
        #selectedAttribute_accessor_storage = (__runInitializers(this, _selected_extraInitializers), __runInitializers(this, _selectedAttribute_initializers, `aria-controls`));
        /**
         * The attribute on the `ui-tab` that indicates which value for `selected`
         * corresponds to which tab.
         *
         * @default aria-controls
         * @attribute
         */
        get selectedAttribute() { return this.#selectedAttribute_accessor_storage; }
        set selectedAttribute(value) { this.#selectedAttribute_accessor_storage = value; }
        #pointerStyles_accessor_storage = (__runInitializers(this, _selectedAttribute_extraInitializers), __runInitializers(this, _pointerStyles_initializers, void 0));
        get pointerStyles() { return this.#pointerStyles_accessor_storage; }
        set pointerStyles(value) { this.#pointerStyles_accessor_storage = value; }
        contentScroll = __runInitializers(this, _pointerStyles_extraInitializers);
        #indicated_accessor_storage = __runInitializers(this, _indicated_initializers, false);
        get indicated() { return this.#indicated_accessor_storage; }
        set indicated(value) { this.#indicated_accessor_storage = value; }
        observer = __runInitializers(this, _indicated_extraInitializers);
        #isVisible_accessor_storage = __runInitializers(this, _isVisible_initializers, void 0);
        /**
         * This is set by the intersection observer. Once the tabs are in the view it turns to `true`.
         * This we can properly visualize selection.
         */
        get isVisible() { return this.#isVisible_accessor_storage; }
        set isVisible(value) { this.#isVisible_accessor_storage = value; }
        constructor() {
            super();
            this.observer = new IntersectionObserver(this.intersectionCallback.bind(this), {
                threshold: 1.0,
                rootMargin: '0px',
            });
        }
        willUpdate(cp) {
            if (cp.has('isVisible')) {
                this.handleVisibility();
            }
            super.willUpdate(cp);
        }
        connectedCallback() {
            super.connectedCallback();
            if (!this.hasAttribute('role')) {
                this.setAttribute('role', 'tablist');
            }
            this.observer.observe(this);
        }
        disconnectedCallback() {
            super.disconnectedCallback();
            this.observer.unobserve(this);
        }
        intersectionCallback(entries) {
            const [entry] = entries;
            this.isVisible = entry.isIntersecting;
        }
        async updateItems() {
            const elements = this.assignedElements || [];
            const items = elements.filter(this.isTabItem, this);
            this.items = items;
            if (this.activeItem && !items.includes(this.activeItem)) {
                this.activeItem = null;
            }
            await this.updateComplete;
            this.ensureSelection();
        }
        /**
         * @return Whether the given element is a list item element.
         */
        isTabItem(element) {
            if (element.nodeType !== Node.ELEMENT_NODE) {
                return false;
            }
            return element.localName === 'ui-tab';
        }
        isSelectable(element) {
            if (element.disabled) {
                return false;
            }
            if (element.hidden && element.hasAttribute('hidden')) {
                return false;
            }
            return true;
        }
        ensureSelection() {
            const { selected, selectedAttribute, activeItem, items } = this;
            if (!selectedAttribute) {
                return;
            }
            const item = items.find((i) => i.getAttribute(selectedAttribute) === selected);
            if (item && item === activeItem) {
                return;
            }
            this.makeSelection(item);
        }
        makeSelection(tab, focus = false) {
            const { activeItem } = this;
            if (activeItem === tab) {
                tab.highlight();
                return;
            }
            this.previousItem = activeItem;
            if (activeItem) {
                this.deselectItem(activeItem);
            }
            if (tab) {
                this.selectItem(tab, focus);
                this.positionPointer(tab, activeItem);
                if (this.activeItem) {
                    // we set this here so we won't notify selection when initializing.
                    this.notifySelect(tab);
                }
            }
        }
        notifySelect(item) {
            const index = this.items.indexOf(item);
            if (index === -1) {
                return;
            }
            const attrValue = item.getAttribute(this.selectedAttribute);
            this.dispatchEvent(new CustomEvent('select', {
                detail: {
                    item,
                    index,
                    select: attrValue,
                },
            }));
        }
        selectItem(tab, focus = false) {
            this.activeItem = tab;
            if (this.hasAttribute('tabindex')) {
                this.removeAttribute('tabindex');
            }
            tab.setAttribute('aria-selected', 'true');
            tab.setAttribute('tabindex', '0');
            tab.selected = true;
            tab.priority = this.priority;
            tab.scrollIntoView({ inline: 'nearest', behavior: 'auto', block: 'nearest' });
            if (focus) {
                tab.focus();
            }
        }
        deselectItem(tab) {
            tab.setAttribute('aria-selected', 'false');
            tab.setAttribute('tabindex', '-1');
            tab.selected = false;
            tab.indicated = false;
        }
        handleVisibility() {
            const { previousItem, activeItem } = this;
            if (!activeItem) {
                return;
            }
            this.positionPointer(activeItem, previousItem);
        }
        async positionPointer(tab, old) {
            const { items, pointer, isVisible } = this;
            if (!isVisible) {
                return;
            }
            const index = items.indexOf(tab);
            if (index < 0 || !pointer) {
                this.pointerStyles = undefined;
                return;
            }
            if (!old) {
                // no start animation, just show indicator.
                tab.indicated = true;
                return;
            }
            const isPrimary = this.priority === 'primary';
            const final = isPrimary ? this.getPrimaryLeft(tab) : this.getSecondaryLeft(tab);
            if (this.pointerStyles && this.pointerStyles.left === `${final}px`) {
                return;
            }
            // first position this indicator in the place of the old one.
            // update the view and then run the animation.
            this.indicated = true;
            const left = isPrimary ? this.getPrimaryLeft(old) : this.getSecondaryLeft(old);
            this.pointerStyles = { left };
            await this.updateComplete;
            const frames = isPrimary ? this.getPrimaryKeyframes(left, final) : this.getSecondaryKeyframes(left, final);
            if (this.moveAnimation) {
                this.moveAnimation.cancel();
            }
            const moveAnimation = pointer.animate(frames, {
                duration: 360,
                iterations: 1,
                easing: Easing.DECELERATION,
            });
            const finalStyles = {
                left: `${final}px`,
            };
            moveAnimation.addEventListener('finish', () => {
                this.pointerStyles = finalStyles;
                this.moveAnimation = undefined;
                tab.indicated = true;
                this.indicated = false;
            });
            moveAnimation.addEventListener('cancel', () => {
                this.pointerStyles = finalStyles;
            });
            this.moveAnimation = moveAnimation;
        }
        moveAnimation = __runInitializers(this, _isVisible_extraInitializers);
        getPrimaryLeft(tab) {
            const contentBox = this.content.getBoundingClientRect();
            const tabRect = tab.getBoundingClientRect();
            const leftFromParent = tabRect.x - contentBox.x;
            const offset = this.scrollable ? 48 : 0; // left button
            const left = leftFromParent + tabRect.width / 2 - 20 + offset;
            return `${left}px`;
        }
        getSecondaryLeft(tab) {
            const contentBox = this.content.getBoundingClientRect();
            const tabRect = tab.getBoundingClientRect();
            const offset = this.scrollable ? 48 : 0; // left button
            const leftFromParent = tabRect.x - contentBox.x + offset;
            return `${leftFromParent}px`;
        }
        getPrimaryKeyframes(start, final) {
            return [
                {
                    left: start,
                    width: `40px`,
                },
                {
                    width: `80px`,
                },
                {
                    left: final,
                    width: `40px`,
                },
            ];
        }
        getSecondaryKeyframes(start, final) {
            return [
                {
                    left: start,
                },
                {
                    left: final,
                },
            ];
        }
        contentClickHandler(e) {
            this.activateFromEvent(e);
        }
        contentKeyDownHandler(e) {
            if (['Enter', 'Space'].includes(e.code)) {
                this.activateFromEvent(e);
            }
        }
        activateFromEvent(e) {
            const { items } = this;
            const path = e.composedPath();
            let item;
            while (!item) {
                const next = path.shift();
                if (!next) {
                    break;
                }
                if (items.includes(next)) {
                    item = next;
                }
            }
            if (!item) {
                return;
            }
            this.makeSelection(item, true);
        }
        handleScrollLeft() {
            const { contentScroll = 0, content } = this;
            if (contentScroll === 0) {
                return;
            }
            let left = contentScroll - 80;
            if (left < 0) {
                left = 0;
            }
            this.contentScroll = left;
            content.scrollTo({
                behavior: 'smooth',
                left,
            });
        }
        handleScrollRight() {
            const { contentScroll = 0, content } = this;
            let left = contentScroll + 80;
            if (left + content.clientWidth > content.scrollWidth) {
                left = content.scrollWidth - content.clientWidth;
            }
            this.contentScroll = left;
            content.scrollTo({
                behavior: 'smooth',
                left,
            });
        }
        handleScroll(e) {
            const div = e.target;
            this.contentScroll = div.scrollLeft;
        }
        handleKeyDown(e) {
            if (e.code === 'ArrowRight') {
                e.preventDefault();
                const tab = this.activeItem ? this.getNextItem(this.activeItem) : this.items[0];
                this.makeSelection(tab, true);
            }
            else if (e.code === 'ArrowLeft') {
                e.preventDefault();
                const tab = this.activeItem ? this.getPreviousItem(this.activeItem) : this.items[0];
                this.makeSelection(tab, true);
            }
        }
        getPreviousItem(item) {
            const { items } = this;
            const curIndex = items.indexOf(item);
            if (curIndex < 0) {
                return item;
            }
            let i = curIndex;
            let result;
            do {
                i--;
                if (i === curIndex) {
                    // looped back from the end, no active element to find.
                    return item;
                }
                const tmp = items[i];
                if (!tmp) {
                    i = items.length;
                    continue;
                }
                if (this.isSelectable(tmp)) {
                    result = tmp;
                }
            } while (!result);
            return result || item;
        }
        getNextItem(item) {
            const { items } = this;
            const curIndex = items.indexOf(item);
            if (curIndex < 0) {
                return item;
            }
            let i = curIndex;
            let next;
            do {
                i++;
                if (i === curIndex) {
                    // looped back from the start, no active element to find.
                    return item;
                }
                const tmp = items[i];
                if (!tmp) {
                    i = -1;
                    continue;
                }
                if (this.isSelectable(tmp)) {
                    next = tmp;
                }
            } while (!next);
            return next || item;
        }
        render() {
            const classes = {
                surface: true,
                scrollable: !!this.scrollable,
            };
            return html `
      <div class="${classMap(classes)}">
        ${this.renderLeftScrollControl()}
        <div class="content" @scroll="${this.handleScroll}" @keydown="${this.handleKeyDown}">${this.renderSlot()}</div>
        ${this.rightScrollControl()} ${this.renderIndicator()}
        <div class="divider"></div>
      </div>
    `;
        }
        renderSlot() {
            return html `<slot
      @slotchange="${this.updateItems}"
      @click="${this.contentClickHandler}"
      @keydown="${this.contentKeyDownHandler}"
    ></slot>`;
        }
        renderIndicator() {
            const classes = {
                indicator: true,
                indicated: this.indicated,
                primary: this.priority === 'primary',
                secondary: this.priority === 'secondary',
            };
            return html `
      <div class="${classMap(classes)}">
        <span role="presentation" class="pointer" style="${styleMap(this.pointerStyles || {})}"></span>
      </div>
    `;
        }
        renderLeftScrollControl() {
            if (!this.scrollable) {
                return nothing;
            }
            return html ` <div class="scroll-control left">
      <ui-icon-button aria-label="Scroll tabs left" title="Scroll tabs left" @click="${this.handleScrollLeft}">
        <ui-icon icon="chevronLeft"></ui-icon>
      </ui-icon-button>
    </div>`;
        }
        rightScrollControl() {
            if (!this.scrollable) {
                return nothing;
            }
            return html `<div class="scroll-control right">
      <ui-icon-button aria-label="Scroll tabs right" title="Scroll tabs right" @click="${this.handleScrollRight}">
        <ui-icon icon="chevronRight"></ui-icon>
      </ui-icon-button>
    </div>`;
        }
    };
})();
/**
 * A container for tabs.
 *
 * @fires select - A non bubbling event when selection change through user interaction.
 *                The `event.detail` object contains the `item` and `index` properties.
 *                It also has the `select` property with the value of the `selectedAttribute` on the tab.
 */
export default UiTabs;
//# sourceMappingURL=Tabs.js.map