import { __esDecorate, __runInitializers } from "tslib";
import { html, LitElement } from 'lit';
import { property, queryAssignedElements } from 'lit/decorators.js';
const NAVIGATION_KEYS = {
    ArrowDown: 'ArrowDown',
    ArrowUp: 'ArrowUp',
    Home: 'Home',
    End: 'End',
};
const ACTIVATION_KEYS = {
    Enter: 'Enter',
    Space: ' ',
};
let UiList = (() => {
    let _classSuper = LitElement;
    let _selector_decorators;
    let _selector_initializers = [];
    let _selector_extraInitializers = [];
    let _selectActive_decorators;
    let _selectActive_initializers = [];
    let _selectActive_extraInitializers = [];
    let _assignedElements_decorators;
    let _assignedElements_initializers = [];
    let _assignedElements_extraInitializers = [];
    return class UiList extends _classSuper {
        static {
            const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
            _selector_decorators = [property({ type: String })];
            _selectActive_decorators = [property({ type: Boolean })];
            _assignedElements_decorators = [queryAssignedElements({ flatten: true })];
            __esDecorate(this, null, _selector_decorators, { kind: "accessor", name: "selector", static: false, private: false, access: { has: obj => "selector" in obj, get: obj => obj.selector, set: (obj, value) => { obj.selector = value; } }, metadata: _metadata }, _selector_initializers, _selector_extraInitializers);
            __esDecorate(this, null, _selectActive_decorators, { kind: "accessor", name: "selectActive", static: false, private: false, access: { has: obj => "selectActive" in obj, get: obj => obj.selectActive, set: (obj, value) => { obj.selectActive = value; } }, metadata: _metadata }, _selectActive_initializers, _selectActive_extraInitializers);
            __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);
            if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
        }
        #items_accessor_storage = [];
        /**
         * The computed list of list items to render.
         */
        get items() { return this.#items_accessor_storage; }
        set items(value) { this.#items_accessor_storage = value; }
        activeListItem = null;
        highlightListItem = null;
        #selector_accessor_storage = __runInitializers(this, _selector_initializers, void 0);
        /**
         * The CSS selector that is used to recognize which items are
         * active list items (can be selected, focused, etc.)
         * @attribute
         */
        get selector() { return this.#selector_accessor_storage; }
        set selector(value) { this.#selector_accessor_storage = value; }
        #selectActive_accessor_storage = (__runInitializers(this, _selector_extraInitializers), __runInitializers(this, _selectActive_initializers, void 0));
        /**
         * When set it marks last activated list item as selected.
         * @attribute
         */
        get selectActive() { return this.#selectActive_accessor_storage; }
        set selectActive(value) { this.#selectActive_accessor_storage = value; }
        #assignedElements_accessor_storage = (__runInitializers(this, _selectActive_extraInitializers), __runInitializers(this, _assignedElements_initializers, void 0));
        get assignedElements() { return this.#assignedElements_accessor_storage; }
        set assignedElements(value) { this.#assignedElements_accessor_storage = value; }
        constructor() {
            super();
            __runInitializers(this, _assignedElements_extraInitializers);
            this.selector = 'ui-list-item';
            this.addEventListener('keydown', this.handleKeydown.bind(this));
            this.addEventListener('click', this.handleClick.bind(this));
        }
        connectedCallback() {
            super.connectedCallback();
            if (!this.hasAttribute('tabindex')) {
                this.setAttribute('tabindex', '0');
            }
        }
        focus(options) {
            const { activeListItem } = this;
            if (activeListItem) {
                activeListItem.focus(options);
                return;
            }
            this.activateFirstItem();
        }
        firstUpdated(changedProperties) {
            super.firstUpdated(changedProperties);
            this.updateItems();
        }
        activateFirstItem() {
            this.activeListItem = this.getFirstItem();
            this.activeListItem?.activate();
        }
        activateLastItem() {
            this.activeListItem = this.getLastItem();
            this.activeListItem?.activate();
        }
        resetActiveListItem() {
            this.activeListItem = null;
            this.setAttribute('tabindex', '0');
        }
        updateItems() {
            const elements = this.assignedElements || [];
            const items = elements.filter(this.isListItem, this);
            this.items = items;
            if (this.activeListItem && !items.includes(this.activeListItem)) {
                this.activeListItem = null;
            }
            if (this.highlightListItem && !items.includes(this.highlightListItem)) {
                this.highlightListItem = null;
            }
            this.dispatchEvent(new CustomEvent('itemschange', { bubbles: false, composed: false, detail: { items } }));
        }
        /**
         * @return Whether the given element is a list item element.
         */
        isListItem(element) {
            if (element.nodeType !== Node.ELEMENT_NODE) {
                return false;
            }
            return element.matches(this.selector);
        }
        getFirstItem() {
            return this.items[0];
        }
        getLastItem() {
            return this.items[this.items.length - 1];
        }
        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;
        }
        isSelectable(element) {
            if (element.disabled) {
                return false;
            }
            if (element.hasAttribute('disabled')) {
                return false;
            }
            if (element.hidden && element.hasAttribute('hidden')) {
                return false;
            }
            return true;
        }
        isListItemActive(item) {
            return item.isActive();
        }
        deactivateListItem(item) {
            item.deactivate();
        }
        activateListItem(item) {
            if (!item) {
                return;
            }
            this.removeAttribute('tabindex');
            item.activate();
            item.scrollIntoView({ block: 'end', inline: 'nearest' });
        }
        handleKeydown(event) {
            if (Object.values(ACTIVATION_KEYS).includes(event.key)) {
                this.activateFromEvent(event);
                return;
            }
            if (Object.values(NAVIGATION_KEYS).indexOf(event.key) === -1)
                return;
            for (const item of this.items) {
                if (this.isListItemActive(item)) {
                    this.activeListItem = item;
                }
                this.deactivateListItem(item);
            }
            if (event.key === NAVIGATION_KEYS.ArrowDown) {
                event.preventDefault();
                this.activateNext();
            }
            if (event.key === NAVIGATION_KEYS.ArrowUp) {
                event.preventDefault();
                this.activatePrevious();
            }
            if (event.key === NAVIGATION_KEYS.Home) {
                event.preventDefault();
                this.activateFirst();
            }
            if (event.key === NAVIGATION_KEYS.End) {
                event.preventDefault();
                this.activateLast();
            }
        }
        activateFirst() {
            this.activeListItem = this.getFirstItem();
            if (this.activeListItem) {
                this.activateListItem(this.activeListItem);
            }
        }
        activateLast() {
            this.activeListItem = this.getLastItem();
            if (this.activeListItem) {
                this.activateListItem(this.activeListItem);
            }
        }
        activateNext(item = this.activeListItem) {
            this.activeListItem = item ? this.getNextItem(item) : this.getFirstItem();
            if (this.activeListItem) {
                this.activateListItem(this.activeListItem);
            }
        }
        activatePrevious(item = this.activeListItem) {
            this.activeListItem = item ? this.getPreviousItem(item) : this.getLastItem();
            if (this.activeListItem) {
                this.activateListItem(this.activeListItem);
            }
        }
        /**
         * Sets `highlight` class on the next item.
         * @param item When not set it highlights the first item. Default to `this.highlightListItem`.
         *             Pass `null` to select first item.
         */
        highlightNext(item = this.highlightListItem) {
            const next = item ? this.getNextItem(item) : this.getFirstItem();
            this.highlightItem(next);
        }
        /**
         * Sets `highlight` class on the previous item.
         * @param item When not set it highlights the last item. Default to `this.highlightListItem`.
         *             Pass `null` to select last item.
         */
        highlightPrevious(item = this.highlightListItem) {
            const previous = item ? this.getPreviousItem(item) : this.getLastItem();
            this.highlightItem(previous);
        }
        highlightFirst() {
            const item = this.getFirstItem();
            this.highlightItem(item);
        }
        highlightLast() {
            const item = this.getLastItem();
            this.highlightItem(item);
        }
        handleClick(event) {
            this.activateFromEvent(event);
        }
        highlightItem(item) {
            if (this.highlightListItem) {
                this.highlightListItem.classList.remove('highlight');
            }
            this.highlightListItem = item || null;
            if (this.highlightListItem) {
                this.highlightListItem.classList.add('highlight');
                this.highlightListItem.scrollIntoView({ block: 'end', inline: 'nearest' });
            }
        }
        /**
         * Activates a list item from an Event.
         * Activate means dispatches a non-bubbling CustomEvent with the item in the detail.
         */
        activateFromEvent(e) {
            const path = e.composedPath();
            let item;
            while (!item) {
                const next = path.shift();
                if (next === this) {
                    break;
                }
                if (!this.isListItem(next) || !this.isSelectable(next)) {
                    continue;
                }
                item = next;
            }
            if (!item) {
                return;
            }
            this.manageSelection(item);
            item.activate();
            this.activeListItem = item;
            if (this.notifySelect(item)) {
                e.preventDefault();
            }
        }
        /**
         * @param item The UiListItem that is selected.
         * @returns True when the event was canceled.
         */
        notifySelect(item, index) {
            const resolvedIndex = index ?? this.items.indexOf(item);
            if (resolvedIndex === -1) {
                return false;
            }
            const event = new CustomEvent('select', {
                cancelable: true,
                detail: {
                    item,
                    index: resolvedIndex,
                },
            });
            this.dispatchEvent(event);
            return event.defaultPrevented;
        }
        manageSelection(item) {
            if (!this.selectActive) {
                return;
            }
            const { items } = this;
            items.forEach((current) => current.classList.remove('select'));
            item.classList.add('select');
        }
        render() {
            return html `<slot @slotchange="${this.updateItems}"></slot>`;
        }
    };
})();
/**
 * @fires select - Dispatched when the user click or press `Enter` or `Space` on any active list item.
 *                 The `event.detail` object contains the `item` and `index` properties.
 * @fires itemschange - Dispatched when the list items change, e.g. when the slot changes.
 *                The `event.detail` object contains the `items` property with the list of items.
 */
export default UiList;
//# sourceMappingURL=List.js.map