import { __decorate } from "tslib";
import { Observable, attr, observable, volatile } from '@ni/fast-element';
import { DesignSystem, FoundationElement } from '@ni/fast-foundation';
import { styles } from './styles';
import { template } from './template';
import { tableCellTag } from '../cell';
import { ColumnInternals, isColumnInternalsProperty } from '../../../table-column/base/models/column-internals';
/**
 * A styled row that is used within the nimble-table.
 * @internal
 */
export class TableRow extends FoundationElement {
    constructor() {
        super(...arguments);
        this.selectable = false;
        this.selected = false;
        this.hideSelection = false;
        this.expanded = false;
        this.reserveCollapseSpace = false;
        /**
         * @internal
         * */
        this.columnNotifiers = [];
        this.columns = [];
        this.nestingLevel = 0;
        this.isParentRow = false;
        this.menuOpen = false;
        this.rowOperationGridCellHidden = false;
        this.loading = false;
        /**
         * @internal
         * An array that parallels the `columns` array and contains the indent
         * level of each column's cell.
         * */
        this.cellIndentLevels = [];
        /**
         * @internal
         * An array that parallels the `columns` array and contains the cell state
         * of each column's cell.
         * */
        this.cellStates = [];
        /**
         * @internal
         */
        this.animationClass = '';
        /**
         * @internal
         */
        this.allowHover = false;
        // Programmatically updating the selection state of a checkbox fires the 'change' event.
        // Therefore, selection change events that occur due to programmatically updating
        // the selection checkbox 'checked' value should be ingored.
        // https://github.com/ni/nimble/issues/2739
        this.ignoreSelectionChangeEvents = false;
        this.removeAnimatingClass = () => {
            this.animationClass = '';
            this.expandIcon?.removeEventListener('transitionend', this.removeAnimatingClass);
        };
    }
    get isTopLevelParentRow() {
        return this.isParentRow && this.nestingLevel === 0;
    }
    get isNestedParent() {
        return this.isParentRow && this.nestingLevel > 0;
    }
    /** @internal */
    get showSelectionCheckbox() {
        return this.selectable && !this.hideSelection;
    }
    get ariaSelected() {
        if (this.selectable) {
            return this.selected ? 'true' : 'false';
        }
        return null;
    }
    disconnectedCallback() {
        super.disconnectedCallback();
        this.removeColumnObservers();
    }
    /** @internal */
    onSelectionCheckboxChange(event) {
        if (this.ignoreSelectionChangeEvents) {
            return;
        }
        const checkbox = event.target;
        const checked = checkbox.checked;
        this.onSelectionChange(!checked, checked);
    }
    /** @internal */
    onSelectionChange(oldState, newState) {
        this.selected = newState;
        const detail = {
            oldState,
            newState
        };
        this.$emit('row-selection-toggle', detail);
    }
    /** @internal */
    onCellActionMenuBeforeToggle(event, column) {
        this.currentActionMenuColumn = column;
        this.emitActionMenuToggleEvent('row-action-menu-beforetoggle', event.detail, column);
    }
    /** @internal */
    onCellActionMenuToggle(event, column) {
        this.menuOpen = event.detail.newState;
        this.emitActionMenuToggleEvent('row-action-menu-toggle', event.detail, column);
    }
    /** @internal */
    handleChange(source, args) {
        if (source instanceof ColumnInternals
            && typeof args === 'string'
            && (isColumnInternalsProperty(args, 'columnConfig')
                || isColumnInternalsProperty(args, 'dataRecordFieldNames'))) {
            this.updateCellStates();
        }
    }
    /** @internal */
    getFocusableElements() {
        return {
            selectionCheckbox: this.showSelectionCheckbox
                ? this.selectionCheckbox
                : undefined,
            cells: Array.from(this.cellContainer.querySelectorAll(tableCellTag)).map(cell => ({
                cell,
                actionMenuButton: cell.hasActionMenu
                    ? cell.actionMenuButton
                    : undefined
            }))
        };
    }
    onRowExpandToggle(event) {
        const expandEventDetail = {
            oldState: this.expanded,
            newState: !this.expanded,
            recordId: this.recordId
        };
        this.$emit('row-expand-toggle', expandEventDetail);
        event?.stopImmediatePropagation();
        // To avoid a visual glitch with improper expand/collapse icons performing an
        // animation (due to visual re-use apparently), we apply a class to the
        // contained expand-collapse button temporarily. We use the 'transitionend' event
        // to remove the temporary class and register a function reference as the handler
        // to avoid issues that may result from the 'transitionend' event not firing, as it
        // will never result in multiple event listeners being registered.
        this.animationClass = 'animating';
        this.expandIcon?.addEventListener('transitionend', this.removeAnimatingClass);
    }
    onCellViewSlotsRequest(column, event) {
        event.stopImmediatePropagation();
        if (typeof this.recordId !== 'string') {
            // The recordId is expected to be defined on any row that can be interacted with, but if
            // it isn't defined, nothing can be done with the request to slot content into the row.
            return;
        }
        const eventDetails = {
            recordId: this.recordId,
            columnInternalId: column.columnInternals.uniqueId,
            slots: event.detail.slots
        };
        this.$emit('row-slots-request', eventDetails);
    }
    emitActionMenuToggleEvent(eventType, menuButtonEventDetail, column) {
        const detail = {
            newState: menuButtonEventDetail.newState,
            oldState: menuButtonEventDetail.oldState,
            recordIds: [this.recordId],
            operatingRecordId: this.recordId,
            columnId: column.columnId
        };
        this.$emit(eventType, detail);
    }
    columnsChanged() {
        this.updateCellIndentLevels();
        this.updateCellStates();
        this.observeColumns();
    }
    dataRecordChanged() {
        this.updateCellStates();
    }
    nestingLevelChanged() {
        this.updateCellIndentLevels();
    }
    updateCellIndentLevels() {
        this.cellIndentLevels = this.columns.map((_, i) => {
            return i === 0 ? this.nestingLevel : 0;
        });
    }
    removeColumnObservers() {
        this.columnNotifiers.forEach(notifier => {
            notifier.unsubscribe(this);
        });
        this.columnNotifiers = [];
    }
    observeColumns() {
        this.removeColumnObservers();
        this.columnNotifiers = this.columns.map(column => {
            const notifier = Observable.getNotifier(column.columnInternals);
            notifier.subscribe(this);
            return notifier;
        });
    }
    updateCellStates() {
        this.cellStates = this.columns.map(column => {
            const fieldNames = column.columnInternals.dataRecordFieldNames;
            let cellState;
            if (this.hasValidFieldNames(fieldNames) && this.dataRecord) {
                const cellDataValues = fieldNames.map(field => this.dataRecord[field]);
                const cellRecord = Object.fromEntries(column.columnInternals.cellRecordFieldNames.map((k, j) => [
                    k,
                    cellDataValues[j]
                ]));
                const columnConfig = column.columnInternals.columnConfig;
                cellState = {
                    cellRecord,
                    columnConfig
                };
            }
            return cellState;
        });
    }
    hasValidFieldNames(keys) {
        return keys.every(key => key !== undefined);
    }
    selectedChanged() {
        this.setSelectionCheckboxState();
    }
    selectionCheckboxChanged() {
        this.setSelectionCheckboxState();
    }
    setSelectionCheckboxState() {
        if (this.selectionCheckbox) {
            this.ignoreSelectionChangeEvents = true;
            this.selectionCheckbox.checked = this.selected;
            this.ignoreSelectionChangeEvents = false;
        }
    }
}
__decorate([
    attr({ attribute: 'record-id' })
], TableRow.prototype, "recordId", void 0);
__decorate([
    attr({ mode: 'boolean' })
], TableRow.prototype, "selectable", void 0);
__decorate([
    attr({ mode: 'boolean' })
], TableRow.prototype, "selected", void 0);
__decorate([
    attr({ attribute: 'hide-selection', mode: 'boolean' })
], TableRow.prototype, "hideSelection", void 0);
__decorate([
    attr({ mode: 'boolean' })
], TableRow.prototype, "expanded", void 0);
__decorate([
    attr({ attribute: 'reserve-collapse-space', mode: 'boolean' })
], TableRow.prototype, "reserveCollapseSpace", void 0);
__decorate([
    observable
], TableRow.prototype, "dataRecord", void 0);
__decorate([
    observable
], TableRow.prototype, "columns", void 0);
__decorate([
    observable
], TableRow.prototype, "currentActionMenuColumn", void 0);
__decorate([
    observable
], TableRow.prototype, "nestingLevel", void 0);
__decorate([
    observable
], TableRow.prototype, "resolvedRowIndex", void 0);
__decorate([
    attr({ attribute: 'is-parent-row', mode: 'boolean' })
], TableRow.prototype, "isParentRow", void 0);
__decorate([
    attr({ attribute: 'menu-open', mode: 'boolean' })
], TableRow.prototype, "menuOpen", void 0);
__decorate([
    attr({ attribute: 'row-operation-grid-cell-hidden', mode: 'boolean' })
], TableRow.prototype, "rowOperationGridCellHidden", void 0);
__decorate([
    attr({ mode: 'boolean' })
], TableRow.prototype, "loading", void 0);
__decorate([
    observable
], TableRow.prototype, "cellIndentLevels", void 0);
__decorate([
    observable
], TableRow.prototype, "cellStates", void 0);
__decorate([
    observable
], TableRow.prototype, "selectionCheckbox", void 0);
__decorate([
    observable
], TableRow.prototype, "animationClass", void 0);
__decorate([
    attr({ attribute: 'allow-hover', mode: 'boolean' })
], TableRow.prototype, "allowHover", void 0);
__decorate([
    volatile
], TableRow.prototype, "isTopLevelParentRow", null);
__decorate([
    volatile
], TableRow.prototype, "isNestedParent", null);
__decorate([
    volatile
], TableRow.prototype, "showSelectionCheckbox", null);
__decorate([
    volatile
], TableRow.prototype, "ariaSelected", null);
const nimbleTableRow = TableRow.compose({
    baseName: 'table-row',
    template,
    styles
});
DesignSystem.getOrCreate().withPrefix('nimble').register(nimbleTableRow());
export const tableRowTag = 'nimble-table-row';
//# sourceMappingURL=index.js.map