// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
 * @fileoverview 'cr-infinite-list' is a thin wrapper around 'cr-lazy-list' that
 * emulates some of the behavior of 'iron-list'.
 */
import '../cr_lazy_list/cr_lazy_list.js';
import { assert } from '//resources/js/assert.js';
import { CrLitElement, html, render } from '//resources/lit/v3_0/lit.rollup.js';
import { getCss } from './cr_infinite_list.css.js';
export class CrInfiniteListElement extends CrLitElement {
    static get is() {
        return 'cr-infinite-list';
    }
    static get styles() {
        return getCss();
    }
    render() {
        // Render items into light DOM using the client provided template
        render(html `<cr-lazy-list id="list" .scrollTarget="${this.scrollTarget}"
          .chunkSize="${this.chunkSize}"
          .scrollOffset="${this.scrollOffset}"
          .listItemHost="${this.getRootNode().host}"
          .items="${this.items}" .itemSize="${this.itemSize}"
          .template="${(item, index) => this.template(item, index, index === this.focusedIndex ? 0 : -1)}"
          .restoreFocusElement="${this.focusedItem_}"
          @keydown="${this.onKeyDown_}"
          @focusin="${this.onItemFocus_}"
          @viewport-filled="${this.updateFocusedItem_}">
        </cr-lazy-list>`, this, {
            host: this,
        });
        return html `<slot></slot>`;
    }
    static get properties() {
        return {
            chunkSize: { type: Number },
            scrollOffset: { type: Number },
            scrollTarget: { type: Object },
            usingDefaultScrollTarget: {
                type: Boolean,
                reflect: true,
            },
            items: { type: Array },
            focusedIndex: { type: Number },
            itemSize: { type: Number },
            template: { type: Object },
            focusedItem_: { type: Object },
        };
    }
    #chunkSize_accessor_storage = 0;
    get chunkSize() { return this.#chunkSize_accessor_storage; }
    set chunkSize(value) { this.#chunkSize_accessor_storage = value; }
    #scrollOffset_accessor_storage = 0;
    get scrollOffset() { return this.#scrollOffset_accessor_storage; }
    set scrollOffset(value) { this.#scrollOffset_accessor_storage = value; }
    #scrollTarget_accessor_storage = this;
    get scrollTarget() { return this.#scrollTarget_accessor_storage; }
    set scrollTarget(value) { this.#scrollTarget_accessor_storage = value; }
    #usingDefaultScrollTarget_accessor_storage = true;
    get usingDefaultScrollTarget() { return this.#usingDefaultScrollTarget_accessor_storage; }
    set usingDefaultScrollTarget(value) { this.#usingDefaultScrollTarget_accessor_storage = value; }
    #items_accessor_storage = [];
    get items() { return this.#items_accessor_storage; }
    set items(value) { this.#items_accessor_storage = value; }
    #itemSize_accessor_storage = undefined;
    get itemSize() { return this.#itemSize_accessor_storage; }
    set itemSize(value) { this.#itemSize_accessor_storage = value; }
    #template_accessor_storage = () => html ``;
    // Unlike cr-lazy-list, cr-infinite-list provides a tabindex parameter for
    // clients as is provided by iron-list. Like iron-list, cr-infinite-list will
    // pass 0 for this parameter if the list item should be keyboard focusable,
    // and -1 otherwise.
    get template() { return this.#template_accessor_storage; }
    set template(value) { this.#template_accessor_storage = value; }
    #focusedIndex_accessor_storage = -1;
    get focusedIndex() { return this.#focusedIndex_accessor_storage; }
    set focusedIndex(value) { this.#focusedIndex_accessor_storage = value; }
    #focusedItem__accessor_storage = null;
    get focusedItem_() { return this.#focusedItem__accessor_storage; }
    set focusedItem_(value) { this.#focusedItem__accessor_storage = value; }
    willUpdate(changedProperties) {
        super.willUpdate(changedProperties);
        if (changedProperties.has('scrollTarget')) {
            this.usingDefaultScrollTarget = this.scrollTarget === this;
        }
        if (changedProperties.has('items')) {
            if (this.focusedIndex >= this.items.length) {
                this.focusedIndex = this.items.length - 1;
            }
            else if (this.focusedIndex === -1 && this.items.length > 0) {
                this.focusedIndex = 0;
            }
        }
    }
    updated(changedProperties) {
        super.updated(changedProperties);
        if (changedProperties.has('focusedIndex')) {
            this.updateFocusedItem_();
        }
    }
    fillCurrentViewport() {
        const list = this.querySelector('cr-lazy-list');
        assert(list);
        return list.fillCurrentViewport();
    }
    ensureItemRendered(index) {
        const list = this.querySelector('cr-lazy-list');
        assert(list);
        return list.ensureItemRendered(index);
    }
    updateFocusedItem_() {
        if (this.focusedIndex === -1) {
            this.focusedItem_ = null;
            return;
        }
        const list = this.querySelector('cr-lazy-list');
        assert(list);
        this.focusedItem_ =
            list.domItems()[this.focusedIndex + 1] ||
                null;
    }
    onItemFocus_(e) {
        const list = this.querySelector('cr-lazy-list');
        assert(list);
        const renderedItems = list.domItems();
        const focusedIdx = Array.from(renderedItems).findIndex(item => {
            return item === e.target || item.shadowRoot?.activeElement === e.target;
        });
        if (focusedIdx !== -1) {
            this.focusedIndex = focusedIdx;
        }
    }
    /**
     * Handles key events when list item elements have focus.
     */
    async onKeyDown_(e) {
        // Do not interfere with any parent component that manages 'shift' related
        // key events.
        if (e.shiftKey || (e.key !== 'ArrowUp' && e.key !== 'ArrowDown')) {
            return;
        }
        e.stopPropagation();
        e.preventDefault();
        // Identify the new focused index.
        this.focusedIndex = e.key === 'ArrowUp' ?
            Math.max(0, this.focusedIndex - 1) :
            Math.min(this.items.length - 1, this.focusedIndex + 1);
        const list = this.querySelector('cr-lazy-list');
        assert(list);
        const element = await list.ensureItemRendered(this.focusedIndex);
        element.focus();
        element.scrollIntoViewIfNeeded();
    }
}
customElements.define(CrInfiniteListElement.is, CrInfiniteListElement);
