UNPKG

@syncfusion/ej2-kanban

Version:

The Kanban board is an efficient way to visualize the workflow at each stage along its path to completion. The most important features available are Swim lane, filtering, and editing.

6,964 lines 329 kB
import { formatUnit, isNullOrUndefined, extend, Property, ChildProperty, closest, classList, removeClass, addClass, remove, createElement, createInstance, detach, SanitizeHtmlHelper, Draggable, EventHandler, append, KeyboardEvents, initializeCSPTemplate, Touch, setStyleAttribute, Browser, debounce, Component, L10n, compile, Collection, Complex, Event, NotifyPropertyChanges } from '@syncfusion/ej2-base';
import { Dialog, Tooltip, Popup, createSpinner, showSpinner, hideSpinner } from '@syncfusion/ej2-popups';
import { DataManager, Query, UrlAdaptor, Deferred, Predicate } from '@syncfusion/ej2-data';
import { DropDownList } from '@syncfusion/ej2-dropdowns';
import { TextBox, NumericTextBox, FormValidator } from '@syncfusion/ej2-inputs';
import { Button } from '@syncfusion/ej2-buttons';
import { TreeView } from '@syncfusion/ej2-navigations';

/**
 * Kanban Constants
 */
// Constants for public events
/** @private */
const actionBegin = 'actionBegin';
/** @private */
const actionComplete = 'actionComplete';
/** @private */
const actionFailure = 'actionFailure';
/** @private */
const cardClick = 'cardClick';
/** @private */
const cardDoubleClick = 'cardDoubleClick';
/** @private */
const cardRendered = 'cardRendered';
/** @private */
const queryCellInfo = 'queryCellInfo';
/** @private */
const dataBinding = 'dataBinding';
/** @private */
const dataBound = 'dataBound';
/** @private */
const dragStart = 'dragStart';
/** @private */
const drag = 'drag';
/** @private */
const dragStop = 'dragStop';
/** @private */
const documentClick = 'document-click';
/** @private */
const dialogOpen = 'dialogOpen';
/** @private */
const dialogClose = 'dialogClose';
// Constants for internal events
/** @private */
const contentReady = 'content-ready';
/** @private */
const dataReady = 'data-ready';
/** @private */
const bottomSpace = 25;
/** @private */
const cardSpace = 16;
/** @private */
const toggleWidth = 50;
/** @hidden */
const dataSourceChanged = 'dataSourceChanged';
/** @hidden */
const dataStateChange = 'dataStateChange';
/** @private */
const columnDragStart = 'columnDragStart';
/** @private */
const columnDrag = 'columnDrag';
/** @private */
const columnDrop = 'columnDrop';

/* eslint-disable @typescript-eslint/no-explicit-any */
/**
 * Kanban data module
 */
class Data {
    /**
     * Constructor for data module
     *
     * @param {Kanban} parent Accepts the instance of the Kanban
     */
    constructor(parent) {
        this.initload = false;
        this.dataState = { isPending: false, resolver: null, isDataChanged: false };
        this.parent = parent;
        this.keyField = this.parent.cardSettings.headerField;
        this.dataState = { isDataChanged: false };
        this.isObservable = false;
        this.initDataManager(parent.dataSource, parent.query);
        this.refreshDataManager();
    }
    /**
     * The function used to initialize dataManager` and query
     *
     * @param {Object[] | DataManager} dataSource Accepts the dataSource as collection of objects or Datamanager instance.
     * @param {Query} query Accepts the query to process the data from collections.
     * @returns {void}
     * @private
     */
    initDataManager(dataSource, query) {
        this.dataManager = dataSource instanceof DataManager ? dataSource : new DataManager(dataSource);
        this.query = query instanceof Query ? query : new Query();
        this.kanbanData = new DataManager(this.parent.kanbanData);
    }
    /**
     * @returns {boolean} returns whether its remote data
     * @hidden
     */
    isRemote() {
        return this.dataManager.dataSource.offline !== true && this.dataManager.dataSource.url !== undefined &&
            this.dataManager.dataSource.url !== '';
    }
    /**
     * @returns {boolean} returns the column key fields
     * @hidden
     */
    columnKeyFields() {
        const columns = [];
        for (const column of this.parent.columns) {
            if (column.keyField.toString().split(',').length > 1) {
                for (const innerColumns of column.keyField.toString().split(',')) {
                    columns.push(innerColumns.trim());
                }
            }
            else {
                columns.push(column.keyField.toString());
            }
        }
        return columns;
    }
    /**
     * The function used to generate updated Query from schedule model
     *
     * @param {string} parameter Accepts the parameter that needs to be sent to the service end.
     * @returns {void}
     * @private
     */
    getQuery(parameter) {
        const query = this.query.clone();
        if (this.isRemote() && this.parent.enableVirtualization) {
            const cardHeight = this.parent.cardHeight === 'auto' ? 100 :
                parseInt(formatUnit(this.parent.cardHeight).split('px')[0], 10);
            const take = this.parent.height === 'auto' ? (Math.ceil(window.innerHeight / cardHeight) * 2) :
                (Math.ceil(parseInt(formatUnit(this.parent.height).split('px')[0], 10) / cardHeight) * 2);
            const columns = this.columnKeyFields();
            for (let i = 0; i < columns.length; i++) {
                query.where(this.parent.keyField, 'equal', columns[i]);
            }
            query.take(take);
            if (isNullOrUndefined(parameter)) {
                parameter = 'KanbanVirtualization';
            }
            query.addParams('KanbanVirtualization', parameter);
        }
        return query;
    }
    /**
     * The function used to get dataSource by executing given Query
     *
     * @param  {Query} query - A Query that specifies to generate dataSource
     * @returns {void}
     * @private
     */
    getData(query) {
        if (this.parent.dataSource && 'result' in this.parent.dataSource) {
            const def = this.eventPromise({ requestType: '' }, query);
            this.isObservable = true;
            return def.promise;
        }
        return this.dataManager.executeQuery(query);
    }
    setState(state) {
        return this.dataState = state;
    }
    getStateEventArgument(query) {
        const adaptr = new UrlAdaptor();
        const dm = new DataManager({ url: '', adaptor: new UrlAdaptor });
        const state = adaptr.processQuery(dm, query);
        const data = JSON.parse(state.data);
        return extend(data, state.pvtData);
    }
    eventPromise(args, query, index) {
        const dataArgs = args;
        const state = this.getStateEventArgument(query);
        const def = new Deferred();
        const deff = new Deferred();
        if (args.requestType !== undefined && this.dataState.isDataChanged !== false) {
            state.action = args;
            if (args.requestType === 'cardChanged' || args.requestType === 'cardRemoved' || args.requestType === 'cardCreated') {
                const editArgs = args;
                editArgs.promise = deff.promise;
                editArgs.state = state;
                editArgs.index = index;
                this.setState({ isPending: true, resolver: deff.resolve });
                dataArgs.endEdit = deff.resolve;
                dataArgs.cancelEdit = deff.reject;
                this.parent.trigger(dataSourceChanged, editArgs);
                deff.promise.then(() => {
                    this.setState({ isPending: true, resolver: def.resolve });
                    this.parent.trigger(dataStateChange, state);
                    editArgs.addedRecords.forEach((data) => {
                        this.parent.kanbanData.push(data);
                    });
                    editArgs.changedRecords.forEach((changedRecord) => {
                        const cardObj = this.parent.kanbanData.filter((data) => data[this.parent.cardSettings.headerField] ===
                            changedRecord[this.parent.cardSettings.headerField])[0];
                        extend(cardObj, changedRecord);
                    });
                    editArgs.deletedRecords.forEach((deletedRecord) => {
                        const index = this.parent.kanbanData.findIndex((data) => data[this.parent.cardSettings.headerField] === deletedRecord[this.parent.cardSettings.headerField]);
                        this.parent.kanbanData.splice(index, 1);
                    });
                }).catch(() => { this.parent.hideSpinner(); });
            }
            else {
                this.setState({ isPending: true, resolver: def.resolve });
                this.parent.trigger(dataStateChange, state);
            }
        }
        else {
            this.setState({});
            def.resolve(this.parent.dataSource);
        }
        return def;
    }
    /**
     * The function used to get the table name from the given Query
     *
     * @returns {string} Returns the table name.
     * @private
     */
    getTable() {
        if (this.parent.query) {
            const query = this.getQuery();
            return query.fromTable;
        }
        else {
            return null;
        }
    }
    /**
     * The function is used to send the request and get response from datamanager
     *
     * @returns {void}
     * @private
     */
    refreshDataManager() {
        const dataManager = this.getData(this.getQuery());
        dataManager.then((e) => this.dataManagerSuccess(e)).catch((e) => this.dataManagerFailure(e));
    }
    /**
     * The function is used to handle the success response from dataManager
     *
     * @param {ReturnType} e Accepts the dataManager success result
     * @param type
     * @returns {void}
     * @private
     */
    // eslint-disable-next-line
    dataManagerSuccess(e, type, offlineArgs, index) {
        if (this.parent.isDestroyed) {
            return;
        }
        if (type) {
            this.updateKanbanData(e);
            if (this.parent.enableVirtualization && this.isRemote()) {
                this.parent.virtualLayoutModule.refresh();
            }
        }
        else {
            this.parent.trigger(dataBinding, e, (args) => {
                this.updateKanbanData(args);
                this.parent.notify(dataReady, { processedData: this.parent.kanbanData });
                this.parent.trigger(dataBound, null, () => this.parent.hideSpinner());
            });
        }
        if (this.initload) {
            this.parent.layoutModule.refresh();
            this.parent.renderTemplates();
        }
        this.initload = true;
    }
    /**
     * The function is used to handle the update the column data count for remote, and update kanbanData while perform the CRUD action
     *
     * @param {ReturnType} args Accepts the dataManager success result
     * @returns {void}
     * @private
     */
    updateKanbanData(args) {
        const resultData = extend([], !isNullOrUndefined(args.result.result) ?
            args.result.result : args.result, null, true);
        if (this.isRemote() && this.parent.enableVirtualization && resultData.length > 0
            && !isNullOrUndefined(args.result.count)) {
            const columnsKeyFields = this.columnKeyFields();
            for (let i = 0; i < columnsKeyFields.length; i++) {
                if (args.result.count[i].Key === columnsKeyFields[i]) {
                    this.parent.columnDataCount[columnsKeyFields[i]] = args.result.count[i].Value;
                }
            }
        }
        this.parent.kanbanData = resultData;
        this.kanbanData = new DataManager(this.parent.kanbanData);
    }
    /**
     * The function is used to handle the failure response from dataManager
     *
     * @param {ReturnType} e Accepts the dataManager failure result
     * @returns {void}
     * @private
     */
    dataManagerFailure(e) {
        if (this.parent.isDestroyed) {
            return;
        }
        this.parent.trigger(actionFailure, { error: e }, () => this.parent.hideSpinner());
    }
    /**
     * The function is used to perform the insert, update, delete and batch actions in datamanager
     *
     * @param {string} updateType Accepts the update type action
     * @param {SaveChanges} params Accepts the savechanges params
     * @param {string} type Accepts the requestType as string
     * @param {Object} data Accepts the data to perform crud action
     * @param {number} index Accepts the index to refresh the data into UI
     * @param {boolean} isDropped Accepts the boolean value based on based if it is dragged and dropped
     * @param {string} dataDropIndexKeyFieldValue Accepts the dropped index key field value card
     * @param {number} draggedKey Accepts the dragged keyfield of the column
     * @param {number} droppedKey Accepts the dropped keyfield of the column
     * @param {number} isMultipleDrag Accepts boolean value based on the multiple drag of the cards
     * @returns {void}
     * @private
     */
    updateDataManager(updateType, params, type, data, index, isDropped, dataDropIndexKeyFieldValue, draggedKey, droppedKey, isMultipleDrag) {
        this.parent.showSpinner();
        let promise;
        const actionArgs = {
            requestType: type, cancel: false, addedRecords: params.addedRecords,
            changedRecords: params.changedRecords, deletedRecords: params.deletedRecords
        };
        this.setState({ isDataChanged: true });
        this.eventPromise(actionArgs, this.query, index);
        this.parent.trigger(actionComplete, actionArgs, (offlineArgs) => {
            if (!offlineArgs.cancel) {
                promise = this.syncDataSource(this.dataManager, updateType, params, data, isDropped, dataDropIndexKeyFieldValue);
                if (this.dataManager.dataSource.offline) {
                    if (!this.isObservable) {
                        this.syncDataSource(this.kanbanData, updateType, params, data, isDropped, dataDropIndexKeyFieldValue);
                        index = draggedKey === droppedKey && isMultipleDrag ? index - 1 : index;
                        this.refreshUI(offlineArgs, index, isDropped);
                        if (this.parent.enableVirtualization) {
                            this.parent.virtualLayoutModule.refreshColumnData(draggedKey, droppedKey, offlineArgs.requestType, data[this.parent.keyField]);
                        }
                    }
                }
                else {
                    promise.then((args) => {
                        if (this.parent.isDestroyed) {
                            return;
                        }
                        const dataManager = this.getData(this.getQuery());
                        dataManager.then((e) => this.dataManagerSuccess(e, 'DataSourceChange', offlineArgs, index)).catch((e) => this.dataManagerFailure(e));
                        if (offlineArgs.requestType === 'cardCreated') {
                            if (!Array.isArray(args)) {
                                offlineArgs.addedRecords[0] = extend(offlineArgs.addedRecords[0], args);
                            }
                            else {
                                this.modifyArrayData(offlineArgs.addedRecords, args);
                            }
                        }
                        else if (offlineArgs.requestType === 'cardChanged') {
                            if (!Array.isArray(args)) {
                                offlineArgs.changedRecords[0] = extend(offlineArgs.changedRecords[0], args);
                            }
                            else {
                                this.modifyArrayData(offlineArgs.changedRecords, args);
                            }
                        }
                        else if (offlineArgs.requestType === 'cardRemoved') {
                            if (!Array.isArray(args)) {
                                offlineArgs.deletedRecords[0] = extend(offlineArgs.deletedRecords[0], args);
                            }
                            else {
                                this.modifyArrayData(offlineArgs.deletedRecords, args);
                            }
                        }
                        index = draggedKey === droppedKey && isMultipleDrag ? index - 1 : index;
                        this.refreshUI(offlineArgs, index, isDropped);
                        if (this.parent.enableVirtualization) {
                            this.parent.virtualLayoutModule.refreshColumnData(draggedKey, droppedKey, offlineArgs.requestType, data[this.parent.keyField]);
                        }
                    }).catch((e) => {
                        this.dataManagerFailure(e);
                    });
                }
            }
        });
    }
    syncDataSource(dataManager, updateType, params, data, isDropped, dataDropIndexKeyFieldValue) {
        let promise;
        switch (updateType) {
            case 'insert':
                return dataManager.insert(data, this.getTable(), this.getQuery());
            case 'update':
                if (this.parent.enableVirtualization && !this.parent.dataModule.isRemote() && isDropped) {
                    promise = dataManager.remove(this.keyField, data, this.getTable(), this.getQuery());
                    promise = dataManager.insert(data, this.getTable(), this.getQuery(), dataManager.dataSource.json.findIndex((data) => data[this.parent.cardSettings.headerField] === dataDropIndexKeyFieldValue));
                    return promise;
                }
                else {
                    return dataManager.update(this.keyField, data, this.getTable(), this.getQuery());
                }
            case 'delete':
                return dataManager.remove(this.keyField, data, this.getTable(), this.getQuery());
            case 'batch':
                if (!this.parent.dataModule.isRemote() && isDropped && this.parent.enableVirtualization && data) {
                    for (let i = 0; i < data.length; i++) {
                        promise = dataManager.remove(this.keyField, data[i], this.getTable(), this.getQuery());
                    }
                    let currentIndex = dataManager.dataSource.json.findIndex((data) => data[this.parent.cardSettings.headerField] === dataDropIndexKeyFieldValue);
                    for (let i = 0; i < data.length; i++, currentIndex++) {
                        promise = dataManager.insert(data[i], this.getTable(), this.getQuery(), currentIndex);
                    }
                    return promise;
                }
                else {
                    return dataManager.saveChanges(params, this.keyField, this.getTable(), this.getQuery());
                }
            default:
                return promise;
        }
    }
    modifyArrayData(onLineData, e) {
        if (onLineData.length === e.length) {
            for (let i = 0; i < e.length; i++) {
                onLineData[i] = extend(onLineData[i], e[i]);
            }
        }
        return onLineData;
    }
    /**
     * The function is used to refresh the UI once the data manager action is completed
     *
     * @param {ActionEventArgs} args Accepts the ActionEventArgs to refresh UI.
     * @param {number} position Accepts the index to refresh UI.
     * @param {boolean} isDropped Accepts the boolean value based on based if it is dragged and dropped
     * @returns {void}
     */
    refreshUI(args, position, isDropped) {
        if (this.parent.enableVirtualization) {
            this.parent.virtualLayoutModule.columnData = this.parent.virtualLayoutModule.getColumnCards();
            args.addedRecords.forEach((data, index) => {
                this.parent.virtualLayoutModule.renderCardBasedOnIndex(data, position + index, isDropped, args.requestType);
            });
            args.changedRecords.forEach((data) => {
                this.parent.virtualLayoutModule.removeCard(data);
                this.parent.virtualLayoutModule.renderCardBasedOnIndex(data, position, isDropped, args.requestType);
                if (this.parent.virtualLayoutModule.isSelectedCard) {
                    this.parent.actionModule.SingleCardSelection(data);
                }
                if (this.parent.sortSettings.field && this.parent.sortSettings.sortBy === 'Index'
                    && this.parent.sortSettings.direction === 'Descending' && position > 0) {
                    --position;
                }
                else {
                    position++;
                }
            });
            args.deletedRecords.forEach((data) => {
                this.parent.virtualLayoutModule.removeCard(data);
            });
            this.parent.virtualLayoutModule.refresh();
        }
        else {
            this.parent.layoutModule.columnData = this.parent.layoutModule.getColumnCards();
            if (this.parent.swimlaneSettings.keyField) {
                this.parent.layoutModule.kanbanRows = this.parent.layoutModule.getRows();
                this.parent.layoutModule.swimlaneData = this.parent.layoutModule.getSwimlaneCards();
            }
            args.addedRecords.forEach((data, index) => {
                if (this.parent.swimlaneSettings.keyField && !data[this.parent.swimlaneSettings.keyField]) {
                    data[this.parent.swimlaneSettings.keyField] = '';
                }
                this.parent.layoutModule.renderCardBasedOnIndex(data, position + index);
            });
            args.changedRecords.forEach((data) => {
                if (this.parent.swimlaneSettings.keyField && !data[this.parent.swimlaneSettings.keyField]) {
                    data[this.parent.swimlaneSettings.keyField] = '';
                }
                this.parent.layoutModule.removeCard(data);
                this.parent.layoutModule.renderCardBasedOnIndex(data, position);
                if (this.parent.layoutModule.isSelectedCard) {
                    this.parent.actionModule.SingleCardSelection(data);
                }
                if (this.parent.sortSettings.field && this.parent.sortSettings.sortBy === 'Index'
                    && this.parent.sortSettings.direction === 'Descending' && position > 0) {
                    --position;
                }
                else {
                    position++;
                }
            });
            args.deletedRecords.forEach((data) => {
                this.parent.layoutModule.removeCard(data);
            });
            this.parent.layoutModule.refresh();
        }
        this.parent.renderTemplates();
        this.parent.notify(contentReady, {});
        this.parent.trigger(dataBound, args, () => this.parent.hideSpinner());
    }
}

var __decorate = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
/**
 * Holds the configuration of swimlane settings in kanban board.
 */
class SwimlaneSettings extends ChildProperty {
}
__decorate([
    Property()
], SwimlaneSettings.prototype, "keyField", void 0);
__decorate([
    Property()
], SwimlaneSettings.prototype, "textField", void 0);
__decorate([
    Property(false)
], SwimlaneSettings.prototype, "showEmptyRow", void 0);
__decorate([
    Property(true)
], SwimlaneSettings.prototype, "showItemCount", void 0);
__decorate([
    Property(false)
], SwimlaneSettings.prototype, "allowDragAndDrop", void 0);
__decorate([
    Property()
], SwimlaneSettings.prototype, "template", void 0);
__decorate([
    Property('Ascending')
], SwimlaneSettings.prototype, "sortDirection", void 0);
__decorate([
    Property()
], SwimlaneSettings.prototype, "sortComparer", void 0);
__decorate([
    Property(true)
], SwimlaneSettings.prototype, "showUnassignedRow", void 0);
__decorate([
    Property(false)
], SwimlaneSettings.prototype, "enableFrozenRows", void 0);

var __decorate$1 = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
/**
 * Holds the configuration of card settings in kanban board.
 */
class CardSettings extends ChildProperty {
}
__decorate$1([
    Property(true)
], CardSettings.prototype, "showHeader", void 0);
__decorate$1([
    Property()
], CardSettings.prototype, "headerField", void 0);
__decorate$1([
    Property()
], CardSettings.prototype, "contentField", void 0);
__decorate$1([
    Property()
], CardSettings.prototype, "tagsField", void 0);
__decorate$1([
    Property()
], CardSettings.prototype, "grabberField", void 0);
__decorate$1([
    Property()
], CardSettings.prototype, "footerCssField", void 0);
__decorate$1([
    Property()
], CardSettings.prototype, "template", void 0);
__decorate$1([
    Property('Single')
], CardSettings.prototype, "selectionType", void 0);

var __decorate$2 = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
/**
 * Holds the configuration of editor settings.
 */
class DialogSettings extends ChildProperty {
}
__decorate$2([
    Property()
], DialogSettings.prototype, "template", void 0);
__decorate$2([
    Property([])
], DialogSettings.prototype, "fields", void 0);
__decorate$2([
    Property(null)
], DialogSettings.prototype, "model", void 0);

var __decorate$3 = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
/**
 * Holds the configuration of columns in kanban board.
 */
class Columns extends ChildProperty {
}
__decorate$3([
    Property()
], Columns.prototype, "keyField", void 0);
__decorate$3([
    Property()
], Columns.prototype, "headerText", void 0);
__decorate$3([
    Property()
], Columns.prototype, "template", void 0);
__decorate$3([
    Property(false)
], Columns.prototype, "allowToggle", void 0);
__decorate$3([
    Property(true)
], Columns.prototype, "isExpanded", void 0);
__decorate$3([
    Property()
], Columns.prototype, "minCount", void 0);
__decorate$3([
    Property()
], Columns.prototype, "maxCount", void 0);
__decorate$3([
    Property(true)
], Columns.prototype, "showItemCount", void 0);
__decorate$3([
    Property(false)
], Columns.prototype, "showAddButton", void 0);
__decorate$3([
    Property(true)
], Columns.prototype, "allowDrag", void 0);
__decorate$3([
    Property(true)
], Columns.prototype, "allowDrop", void 0);
__decorate$3([
    Property([])
], Columns.prototype, "transitionColumns", void 0);

var __decorate$4 = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
/**
 * Holds the configuration of stacked header settings in kanban board.
 */
class StackedHeaders extends ChildProperty {
}
__decorate$4([
    Property()
], StackedHeaders.prototype, "text", void 0);
__decorate$4([
    Property()
], StackedHeaders.prototype, "keyFields", void 0);

var __decorate$5 = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
/**
 * Holds the configuration of sort settings in kanban board.
 */
class SortSettings extends ChildProperty {
}
__decorate$5([
    Property('Index')
], SortSettings.prototype, "sortBy", void 0);
__decorate$5([
    Property()
], SortSettings.prototype, "field", void 0);
__decorate$5([
    Property('Ascending')
], SortSettings.prototype, "direction", void 0);

/**
 * Kanban CSS Constants
 */
/** @private */
const ROOT_CLASS = 'e-kanban';
/** @private */
const RTL_CLASS = 'e-rtl';
/** @private */
const DEVICE_CLASS = 'e-device';
/** @private */
const ICON_CLASS = 'e-icons';
/** @private */
const TEMPLATE_CLASS = 'e-template';
/** @private */
const SWIMLANE_CLASS = 'e-swimlane';
/** @private */
const TABLE_CLASS = 'e-kanban-table';
/** @private */
const HEADER_CLASS = 'e-kanban-header';
/** @private */
const HEADER_TABLE_CLASS = 'e-header-table';
/** @private */
const HEADER_CELLS_CLASS = 'e-header-cells';
/** @private */
const HEADER_WRAP_CLASS = 'e-header-wrap';
/** @private */
const HEADER_TITLE_CLASS = 'e-header-title';
/** @private */
const HEADER_TEXT_CLASS = 'e-header-text';
/** @private */
const HEADER_ICON_CLASS = 'e-header-icon';
/** @private */
const STACKED_HEADER_ROW_CLASS = 'e-stacked-header-row';
/** @private */
const STACKED_HEADER_CELL_CLASS = 'e-stacked-header-cell';
/** @private */
const CONTENT_CELLS_CLASS = 'e-content-cells';
/** @private */
const CONTENT_CLASS = 'e-kanban-content';
/** @private */
const CONTENT_TABLE_CLASS = 'e-content-table';
/** @private */
const HEADER_ROW_TOGGLE_CLASS = 'e-toggle-header';
/** @private */
const HEADER_ROW_CLASS = 'e-header-row';
/** @private */
const CONTENT_ROW_CLASS = 'e-content-row';
/** @private */
const SWIMLANE_ROW_CLASS = 'e-swimlane-row';
/** @private */
const SWIMLANE_ROW_EXPAND_CLASS = 'e-swimlane-row-expand';
/** @private */
const SWIMLANE_ROW_COLLAPSE_CLASS = 'e-swimlane-row-collapse';
/** @private */
const SWIMLANE_ROW_TEXT_CLASS = 'e-swimlane-text';
/** @private */
const CARD_ITEM_COUNT_CLASS = 'e-item-count';
/** @private */
const CARD_WRAPPER_CLASS = 'e-card-wrapper';
/** @private */
const CARD_VIRTUAL_WRAPPER_CLASS = 'e-card-virtual-wrapper';
/** @private */
const CARD_CLASS = 'e-card';
/** @private */
const DROPPABLE_CLASS = 'e-droppable';
/** @private */
const DRAG_CLASS = 'e-drag';
/** @private */
const DROP_CLASS = 'e-drop';
/** @private */
const DISABLED_CLASS = 'e-disabled';
/** @private */
const CARD_HEADER_CLASS = 'e-card-header';
/** @private */
const CARD_CONTENT_CLASS = 'e-card-content';
/** @private */
const CARD_HEADER_TEXT_CLASS = 'e-card-header-caption';
/** @private */
const CARD_HEADER_TITLE_CLASS = 'e-card-header-title';
/** @private */
const CARD_TAGS_CLASS = 'e-card-tags';
/** @private */
const CARD_TAG_CLASS = 'e-card-tag';
/** @private */
const CARD_COLOR_CLASS = 'e-card-color';
/** @private */
const CARD_LABEL_CLASS = 'e-card-label';
/** @private */
const CARD_FOOTER_CLASS = 'e-card-footer';
/** @private */
const EMPTY_CARD_CLASS = 'e-empty-card';
/** @private */
const CARD_FOOTER_CSS_CLASS = 'e-card-footer-css';
/** @private */
const COLUMN_EXPAND_CLASS = 'e-column-expand';
/** @private */
const COLUMN_COLLAPSE_CLASS = 'e-column-collapse';
/** @private */
const COLLAPSE_HEADER_TEXT_CLASS = 'e-collapse-header-text';
/** @private */
const COLLAPSED_CLASS = 'e-collapsed';
/** @private */
const DIALOG_CLASS = 'e-kanban-dialog';
/** @private */
const FORM_CLASS = 'e-kanban-form';
/** @private */
const FORM_WRAPPER_CLASS = 'e-kanban-form-wrapper';
/** @private */
const ERROR_VALIDATION_CLASS = 'e-kanban-error';
/** @private */
const FIELD_CLASS = 'e-field';
/** @private */
const DRAGGED_CLONE_CLASS = 'e-target-dragged-clone';
/** @private */
const CLONED_CARD_CLASS = 'e-cloned-card';
/** @private */
const DRAGGED_CARD_CLASS = 'e-kanban-dragged-card';
/** @private */
const DROPPED_CLONE_CLASS = 'e-target-dropped-clone';
/** @private */
const DROPPING_CLASS = 'e-dropping';
/** @private */
const BORDER_CLASS = 'e-kanban-border';
/** @private */
const TOGGLE_VISIBLE_CLASS = 'e-toggle-visible';
/** @private */
const MULTI_CARD_WRAPPER_CLASS = 'e-multi-card-wrapper';
/** @private */
const MULTI_ACTIVE_CLASS = 'e-multi-active';
/** @private */
const TARGET_MULTI_CLONE_CLASS = 'e-target-multi-clone';
/** @private */
const MULTI_COLUMN_KEY_CLASS = 'e-column-key';
/** @private */
const CARD_SELECTION_CLASS = 'e-selection';
/** @private */
const TOOLTIP_CLASS = 'e-kanban-tooltip';
/** @private */
const TOOLTIP_TEXT_CLASS = 'e-tooltip-text';
/** @private */
const SWIMLANE_HEADER_CLASS = 'e-swimlane-header';
/** @private */
const SWIMLANE_HEADER_TOOLBAR_CLASS = 'e-swimlane-header-toolbar';
/** @private */
const TOOLBAR_MENU_CLASS = 'e-toolbar-menu';
/** @private */
const TOOLBAR_MENU_ICON_CLASS = 'e-icon-menu';
/** @private */
const TOOLBAR_LEVEL_TITLE_CLASS = 'e-toolbar-level-title';
/** @private */
const TOOLBAR_SWIMLANE_NAME_CLASS = 'e-toolbar-swimlane-name';
/** @private */
const SWIMLANE_OVERLAY_CLASS = 'e-swimlane-overlay';
/** @private */
const SWIMLANE_CONTENT_CLASS = 'e-swimlane-content';
/** @private */
const SWIMLANE_RESOURCE_CLASS = 'e-swimlane-resource';
/** @private */
const SWIMLANE_TREE_CLASS = 'e-swimlane-tree';
/** @private */
const LIMITS_CLASS = 'e-limits';
/** @private */
const MAX_COUNT_CLASS = 'e-max-count';
/** @private */
const MIN_COUNT_CLASS = 'e-min-count';
/** @private */
const MAX_COLOR_CLASS = 'e-max-color';
/** @private */
const MIN_COLOR_CLASS = 'e-min-color';
/** @private */
const POPUP_HEADER_CLASS = 'e-popup-header';
/** @private */
const CLOSE_CLASS = 'e-close';
/** @private */
const POPUP_CONTENT_CLASS = 'e-popup-content';
/** @private */
const POPUP_WRAPPER_CLASS = 'e-mobile-popup-wrapper';
/** @private */
const CLOSE_ICON_CLASS = 'e-close-icon';
/** @private */
const POPUP_OPEN_CLASS = 'e-popup-open';
/** @private */
const DIALOG_CONTENT_CONTAINER = 'e-kanban-dialog-content';
/** @private */
const SHOW_ADD_BUTTON = 'e-show-add-button';
/** @private */
const SHOW_ADD_ICON = 'e-show-add-icon';
/** @private */
const SHOW_ADD_FOCUS = 'e-show-add-focus';
/** @private */
const FROZEN_SWIMLANE_ROW_CLASS = 'e-frozen-swimlane-row';
/** @private */
const FROZEN_ROW_CLASS = 'e-frozen-row';
/** @private */
const TOOLBAR_SWIMLANE_ITEM_COUNT_CLASS = 'e-toolbar-swimlane-item-count';

/* eslint-disable @typescript-eslint/no-explicit-any */
/**
 * Action module is used to perform card actions.
 */
class Action {
    /**
     * Constructor for action module
     *
     * @param {Kanban} parent Accepts the kanban instance
     * @private
     */
    constructor(parent) {
        this.parent = parent;
        this.columnToggleArray = [];
        this.selectionArray = [];
        this.lastCardSelection = null;
        this.lastSelectionRow = null;
        this.lastCard = null;
        this.selectedCardsElement = [];
        this.selectedCardsData = [];
        this.hideColumnKeys = [];
    }
    clickHandler(e) {
        const elementSelector = '.' + CARD_CLASS + ',.' + HEADER_ICON_CLASS + ',.' + CONTENT_ROW_CLASS + '.' +
            SWIMLANE_ROW_CLASS + ',.' + SHOW_ADD_BUTTON + ',.' + FROZEN_SWIMLANE_ROW_CLASS + ',.' + CONTENT_ROW_CLASS +
            ':not(.' + SWIMLANE_ROW_CLASS + ') .' + CONTENT_CELLS_CLASS;
        const target = closest(e.target, elementSelector);
        if (!target) {
            return;
        }
        if (target.classList.contains(CARD_CLASS)) {
            if (this.parent.allowKeyboard) {
                this.parent.keyboardModule.cardTabIndexRemove();
            }
            this.cardClick(e);
        }
        else if (target.classList.contains(HEADER_ICON_CLASS)) {
            this.columnExpandCollapse(e);
        }
        else if (target.classList.contains(CONTENT_ROW_CLASS) && target.classList.contains(SWIMLANE_ROW_CLASS)) {
            this.rowExpandCollapse(e);
        }
        else if (target.classList.contains(SHOW_ADD_BUTTON)) {
            this.addButtonClick(target);
        }
        else if (target.classList.contains(FROZEN_SWIMLANE_ROW_CLASS)) {
            const swimlaneRows = [].slice.call(this.parent.element.querySelectorAll('.' + SWIMLANE_ROW_CLASS));
            const targetIcon = this.parent.layoutModule.frozenSwimlaneRow.querySelector('.' + ICON_CLASS);
            this.rowExpandCollapse(e, swimlaneRows[this.parent.layoutModule.frozenOrder]);
            const isCollapsed = targetIcon.classList.contains(SWIMLANE_ROW_COLLAPSE_CLASS) ? true : false;
            if (isCollapsed) {
                classList(targetIcon, [SWIMLANE_ROW_EXPAND_CLASS], [SWIMLANE_ROW_COLLAPSE_CLASS]);
            }
            else {
                classList(targetIcon, [SWIMLANE_ROW_COLLAPSE_CLASS], [SWIMLANE_ROW_EXPAND_CLASS]);
            }
        }
    }
    addButtonClick(target) {
        const newData = {};
        if (this.parent.kanbanData.length === 0) {
            newData[this.parent.cardSettings.headerField] = 1;
        }
        else if (typeof (this.parent.kanbanData[0])[this.parent.cardSettings.headerField] === 'number') {
            const id = this.parent.kanbanData.map((obj) => parseInt(obj[this.parent.cardSettings.headerField], 10));
            newData[this.parent.cardSettings.headerField] = Math.max(...id) + 1;
        }
        newData[this.parent.keyField] = closest(target, '.' + CONTENT_CELLS_CLASS).getAttribute('data-key');
        if (this.parent.sortSettings.sortBy === 'Index') {
            newData[this.parent.sortSettings.field] = 1;
            if (closest(target, '.' + CONTENT_CELLS_CLASS).querySelector('.' + CARD_CLASS)) {
                const card = this.parent.sortSettings.direction === 'Ascending' ?
                    target.nextElementSibling.classList.contains(BORDER_CLASS) ?
                        target.nextElementSibling.nextElementSibling.lastElementChild : target.nextElementSibling.lastElementChild
                    : target.nextElementSibling.classList.contains(BORDER_CLASS) ?
                        target.nextElementSibling.nextElementSibling.firstElementChild : target.nextElementSibling.firstElementChild;
                const data = this.parent.getCardDetails(card);
                newData[this.parent.sortSettings.field] = data[this.parent.sortSettings.field] + 1;
            }
        }
        if (this.parent.kanbanData.length !== 0 && this.parent.swimlaneSettings.keyField &&
            closest(target, '.' + CONTENT_ROW_CLASS).previousElementSibling) {
            newData[this.parent.swimlaneSettings.keyField] =
                closest(target, '.' + CONTENT_ROW_CLASS).previousElementSibling.getAttribute('data-key');
        }
        this.parent.openDialog('Add', newData);
    }
    doubleClickHandler(e) {
        const target = closest(e.target, '.' + CARD_CLASS);
        if (target) {
            this.cardDoubleClick(e);
        }
    }
    cardClick(e, selectedCard) {
        const target = closest((selectedCard) ? selectedCard : e.target, '.' + CARD_CLASS);
        const cardClickObj = this.parent.getCardDetails(target);
        if (cardClickObj) {
            this.parent.activeCardData = { data: cardClickObj, element: target };
            const args = { data: cardClickObj, element: target, cancel: false, event: e };
            this.parent.trigger(cardClick, args, (clickArgs) => {
                if (!clickArgs.cancel) {
                    if (target.classList.contains(CARD_SELECTION_CLASS) && e.type === 'click') {
                        removeClass([target], CARD_SELECTION_CLASS);
                        if (this.parent.enableVirtualization) {
                            this.parent.virtualLayoutModule.disableAttributeSelection(target);
                        }
                        else {
                            this.parent.layoutModule.disableAttributeSelection(target);
                        }
                    }
                    else {
                        let isCtrlKey = e.ctrlKey;
                        if (this.parent.isAdaptive && this.parent.touchModule) {
                            isCtrlKey = (this.parent.touchModule.mobilePopup && this.parent.touchModule.tabHold) || isCtrlKey;
                        }
                        this.cardSelection(target, isCtrlKey, e.shiftKey);
                    }
                    if (this.parent.isAdaptive && this.parent.touchModule) {
                        this.parent.touchModule.updatePopupContent();
                    }
                    const cell = closest(target, '.' + CONTENT_CELLS_CLASS);
                    if (this.parent.allowKeyboard) {
                        const element = [].slice.call(cell.querySelectorAll('.' + CARD_CLASS));
                        element.forEach((e) => { e.setAttribute('tabindex', '0'); });
                        this.parent.keyboardModule.addRemoveTabIndex('Remove');
                    }
                }
            });
        }
    }
    cardDoubleClick(e) {
        const target = closest(e.target, '.' + CARD_CLASS);
        const cardDoubleClickObj = this.parent.getCardDetails(target);
        this.parent.activeCardData = { data: cardDoubleClickObj, element: target };
        this.cardSelection(target, false, false);
        const args = { data: cardDoubleClickObj, element: target, cancel: false, event: e };
        this.parent.trigger(cardDoubleClick, args, (doubleClickArgs) => {
            if (!doubleClickArgs.cancel) {
                this.parent.dialogModule.openDialog('Edit', args.data);
            }
        });
    }
    rowExpandCollapse(e, isFrozenElem) {
        const headerTarget = (e instanceof HTMLElement) ? e : e.target;
        const currentSwimlaneHeader = !isNullOrUndefined(isFrozenElem) ? isFrozenElem : headerTarget;
        const args = { cancel: false, target: headerTarget, requestType: 'rowExpandCollapse' };
        this.parent.trigger(actionBegin, args, (actionArgs) => {
            if (!actionArgs.cancel) {
                const target = closest(currentSwimlaneHeader, '.' + SWIMLANE_ROW_CLASS);
                const key = target.getAttribute('data-key');
                const tgtRow = this.parent.element.querySelector('.' + CONTENT_ROW_CLASS + `:nth-child(${target.rowIndex + 2})`);
                const targetIcon = target.querySelector(`.${SWIMLANE_ROW_EXPAND_CLASS},.${SWIMLANE_ROW_COLLAPSE_CLASS}`);
                const isCollapsed = target.classList.contains(COLLAPSED_CLASS) ? true : false;
                let tabIndex;
                if (isCollapsed) {
                    removeClass([tgtRow, target], COLLAPSED_CLASS);
                    classList(targetIcon, [SWIMLANE_ROW_EXPAND_CLASS], [SWIMLANE_ROW_COLLAPSE_CLASS]);
                    this.parent.swimlaneToggleArray.splice(this.parent.swimlaneToggleArray.indexOf(key), 1);
                    tabIndex = '0';
                }
                else {
                    addClass([tgtRow, target], COLLAPSED_CLASS);
                    classList(targetIcon, [SWIMLANE_ROW_COLLAPSE_CLASS], [SWIMLANE_ROW_EXPAND_CLASS]);
                    this.parent.swimlaneToggleArray.push(key);
                    tabIndex = '-1';
                }
                targetIcon.setAttribute('aria-label', isCollapsed ? key + ' Expand' : key + ' Collapse');
                target.setAttribute('aria-expanded', isCollapsed.toString());
                tgtRow.setAttribute('aria-expanded', isCollapsed.toString());
                const rows = [].slice.call(tgtRow.querySelectorAll('.' + CONTENT_CELLS_CLASS));
                rows.forEach((cell) => { cell.setAttribute('tabindex', tabIndex); });
                this.parent.notify(contentReady, {});
                this.parent.trigger(actionComplete, { target: headerTarget, requestType: 'rowExpandCollapse' });
            }
        });
    }
    columnExpandCollapse(e) {
        const headerTarget = (e instanceof HTMLElement) ? e : e.target;
        const args = { cancel: false, target: headerTarget, requestType: 'columnExpandCollapse' };
        this.parent.trigger(actionBegin, args, (actionArgs) => {
            if (!actionArgs.cancel) {
                const target = closest(headerTarget, '.' + HEADER_CELLS_CLASS);
                const colIndex = target.cellIndex;
                this.columnToggle(target);
                const collapsed = this.parent.element.querySelectorAll(`.${HEADER_CELLS_CLASS}.${COLLAPSED_CLASS}`).length;
                if (collapsed === (this.parent.columns.length - this.hideColumnKeys.length)) {
                    const index = (colIndex + 1 === collapsed) ? 1 : colIndex + 2;
                    const headerSelector = `.${HEADER_CELLS_CLASS}:not(.${STACKED_HEADER_CELL_CLASS}):nth-child(${index})`;
                    const nextCol = this.parent.element.querySelector(headerSelector);
                    addClass([nextCol], COLLAPSED_CLASS);
                    this.columnToggle(nextCol);
                }
                this.parent.notify(contentReady, {});
                this.parent.trigger(actionComplete, { target: headerTarget, requestType: 'columnExpandCollapse' });
            }
        });
    }
    columnToggle(target) {
        const colIndex = target.cellIndex;
        const elementSelector = `.${CONTENT_ROW_CLASS}:not(.${SWIMLANE_ROW_CLASS})`;
        const targetRow = [].slice.call(this.parent.element.querySelectorAll(elementSelector));
        const colSelector = `.${TABLE_CLASS} col:nth-child(${colIndex + 1})`;
        const targetIcon = target.querySelector(`.${COLUMN_EXPAND_CLASS},.${COLUMN_COLLAPSE_CLASS}`);
        const colGroup = [].slice.call(this.parent.element.querySelectorAll(colSelector));
        if (target.classList.contains(COLLAPSED_CLASS)) {
            removeClass(colGroup, COLLAPSED_CLASS);
            if (this.parent.isAdaptive) {
                if (this.parent.enableVirtualization) {
                    colGroup.forEach((col) => col.style.width = formatUnit(this.parent.virtualLayoutModule.getWidth()));
                }
                else {
                    colGroup.forEach((col) => col.style.width = formatUnit(this.parent.layoutModule.getWidth()));
                }
            }
            classList(targetIcon, [COLUMN_EXPAND_CLASS], [COLUMN_COLLAPSE_CLASS]);
            for (const row of targetRow) {
                const targetCol = row.querySelector(`.${CONTENT_CELLS_CLASS}:nth-child(${colIndex + 1})`);
                removeClass([targetCol, target], COLLAPSED_CLASS);
                remove(targetCol.querySelector('.' + COLLAPSE_HEADER_TEXT_CLASS));
                const showAddButton = targetCol.querySelector('.e-show-add-button');
                if (showAddButton) {
                    removeClass([showAddButton], COLLAPSED_CLASS);
                }
                target.setAttribute('aria-expanded', 'true');
                targetCol.setAttribute('aria-expanded', 'true');
                const collapsedCell = [].slice.call(targetCol.parentElement.querySelectorAll('.' + COLLAPSED_CLASS));
                collapsedCell.forEach((cell) => {
                    const collapasedText = cell.querySelector('.' + COLLAPSE_HEADER_TEXT_CLASS);
                    collapasedText.style.height = 'auto';
                    if (collapasedText && targetCol.getBoundingClientRect().height < (collapasedText.getBoundingClientRect().height + 10)) {
                        collapasedText.style.height = (targetCol.getBoundingClientRect().height - 4) + 'px';
                    }
                });
            }
            if (this.parent.kanbanData.length === 0 && targetRow.length === 0) {
                removeClass([target], COLLAPSED_CLASS);
                target.setAttribute('aria-expanded', 'true');
            }
            this.columnToggleArray.splice(this.columnToggleArray.indexOf(target.getAttribute('data-key')), 1);
            this.parent.columns[colIndex].setProperties({ isExpanded: true }, true);
            target.querySelector('.e-header-icon').setAttribute('aria-label', target.getAttribute('data-key') + ' Expand');
        }
        else {
            addClass(colGroup, COLLAPSED_CLASS);
            if (this.parent.isAdaptive) {
                colGroup.forEach((col) => col.style.width = formatUnit(toggleWidth));
            }
            classList(targetIcon, [COLUMN_COLLAPSE_CLASS], [COLUMN_EXPAND_CLASS]);
            const key = target.getAttribute('data-key');
            for (const row of targetRow) {
                const targetCol = row.querySelector(`.${CONTENT_CELLS_CLASS}[data-key="${key}"]`);
                const index = targetCol.cellIndex;
                const showAddButton = targetCol.querySelector('.e-show-add-button');
                if (showAddButton) {
                    addClass([showAddButton], COLLAPSED_CLASS);
                }
                let text;
                if (!this.parent.enableVirtualization) {
                    text = (this.parent.columns[index].showItemCount ? '[' +
                        targetCol.querySelectorAll('.' + CARD_CLASS).length + '] ' : '') + this.parent.columns[index].headerText;
                }
                else {
                    const value = this.parent.dataModule.isRemote() ?
                        this.parent.columnDataCount[this.parent.columns[index].keyField]
                        : this.parent.virtualLayoutModule.columnData[this.parent.columns[index].keyField].length;
                    text = (this.parent.columns[index].showItemCount ? '[' +
                        value + '] ' : '') + this.parent.columns[index].headerText;
                }
                targetCol.appendChild(createElement('div', { className: COLLAPSE_HEADER_TEXT_CLASS, innerHTML: text }));
                addClass([targetCol, target], COLLAPSED_CLASS);
                target.setAttribute('aria-expanded', 'false');
                targetCol.setAttribute('aria-expanded', 'false');
                const collapsedCell = [].slice.call(targetCol.parentElement.querySelectorAll('.' + COLLAPSED_CLASS));
                collapsedCell.forEach((cell) => {
                    const collapasedText = cell.querySelector('.' + COLLAPSE_HEADER_TEXT_CLASS);
                    if (collapasedText && targetCol.getBoundingClientRect().height < (collapasedText.getBoundingClientRect().height + 10)) {
                        collapasedText.style.height = (targetCol.getBoundingClientRect().height - 4) + 'px';
                    }
                });
            }
            if (this.parent.kanbanData.length === 0 && targetRow.length === 0) {
                addClass([target], COLLAPSED_CLASS);
                target.setAttribute('aria-expanded', 'false');
            }
            this.columnToggleArray.push(target.getAttribute('data-key'));
            this.parent.columns[colIndex].setProperties({ isExpanded: false }, true);
            target.querySelector('.e-header-icon').setAttribute('aria-label', key + ' Collapse');
        }
    }
    cardSelection(target, isCtrl, isShift) {
        if (!target) {
            return;
        }
        const cards = this.parent.getSelectedCards();
        if (this.parent.cardSettings.selectionType !== 'None') {
            const contentRow = closest(target, '.' + CONTENT_ROW_CLASS);
            const index = !isNullOrUndefined(this.lastSelectionRow) ? this.lastSelectionRow.rowIndex : contentRow.rowIndex;
            if (index !== contentRow.rowIndex && (isCtrl || isShift) && this.parent.cardSettings.selectionType === 'Multiple') {
                return;
            }
            if (cards.length !== 0 && (!isCtrl || this.parent.cardSettings.selectionType === 'Single')) {
                removeClass(cards, CARD_SELECTION_CLASS);
                if (this.parent.enableVirtualization) {
                    this.parent.virtualLayoutModule.disableAttributeSelection(cards);
                }
                else {
                    this.parent.layoutModule.disableAttributeSelection(cards);
                }
                cards.forEach((el) => {
                    this.selectionArray.splice(this.selectionArray.indexOf(el.getAttribute('data-id')), 1);
                    this.selectedCardsElement.splice(this.selectedCardsElement.indexOf(el), 1);
                    this.selectedCardsData.splice(this.selectedCardsData.indexOf(this.parent.getCardDetails(el), 1));
                });
            }
            if (cards.length > 0 && isShift && this.parent.cardSettings.selectionType === 'Multiple') {
                const curCards = [];
                let start;
                let end;
                let i;
                const allCards = [].slice.call(contentRow.querySelectorAll('.' + CARD_CLASS));
                allCards.forEach((el) => curCards.push(el.getAttribute('data-id')));
                const curId = target.getAttribute('data-id');
                const lastId = this.lastCard.getAttribute('data-id');
                const curIndex = end = curCards.indexOf(curId);
                const lastIndex = start = curCards.indexOf(lastId);
                const select = curIndex > lastIndex ? 'next' : 'prev';
                if (select === 'prev') {
                    start = curIndex;
                    end = lastIndex;
                }
                for (i = start; i <= end; i++) {
                    const card = allCards[i];
                    addClass([card], CARD_SELECTION_CLASS);
                    card.setAttribute('aria-selected', 'true');
                    card.setAttribute('tabindex', '0');
                    this.selectionArray.push(card.getAttribute('data-id'));
                    this.selectedCardsElement.push(card);
                    this.selectedCardsData.push(this.parent.getCardDetails(card));
                    this.lastCardSelection = card;
                    if (select === 'prev') {
                        this.lastCardSelection = allCards[start];
                    }
                }
            }
            else {
                addClass([target], CARD_SELECTION_CLASS);
                target.setAttribute('aria-selected', 'true');
                target.setAttribute('tabindex', '0');
                this.selectionArray.push(target.getAttribute('data-id'));
                this.selectedCardsElement.push(target);
                this.selectedCardsData.push(this.parent.getCardDetails(target));
                this.lastCard = this.lastCardSelection = target;
                this.lastSelectionRow = closest(target, '.' + CONTENT_ROW_CLASS);
                if (this.lastSelectionRow.previousElementSibling) {
                    const elementSelector = `.${SWIMLANE_ROW_EXPAND_CLASS},.${SWIMLANE_ROW_COLLAPSE_CLASS}`;
                    const parentEle = this.lastSelectionRow.previousElementSibling.querySelector(elementSelector);
                    if (parentEle && parentEle.classList.contains(SWIMLANE_ROW_COLLAPSE_CLASS)) {
                        this.rowExpandCollapse(parentEle);
                    }
                }
            }
        }
    }
    addColumn(columnOptions, index) {
        const addColumn = createInstance(Columns, [this.parent, 'columns', columnOptions, true]);
        this.parent.columns.splice(index, 0, addColumn);
        this.parent.notify(dataReady, { processedData: this.parent.kanbanData });
    }
    deleteColumn(index) {
        const listKey = this.parent.element.querySelectorAll('.' + HEADER_CELLS_CLASS).item(index);
        if (listKey && listKey.classList.contains(HEADER_ROW_TOGGLE_CLASS)) {
            this.columnToggleArray.splice(this.columnToggleArray.indexOf(listKey.getAttribute('data-key'), 0));
        }
        this.parent.columns.splice(index, 1);
        if (this.parent.columns.length === 0) {
            detach(this.parent.element.querySelector('.' + HEADER_CLASS));
            detach(this.parent.element.querySelector('.' + CONTENT_CLASS));
        }
        else {
            this.parent.notify(dataReady, { processedData: this.parent.kanbanData });
        }
    }
    showColumn(key) {
        const index = this.hideColumnKeys.indexOf(key.toString());
        if (index !== -1) {
            this.hideColumnKeys.splice(index, 1);
            this.parent.notify(dataReady, { processedData: this.parent.kanbanData });
        }
    }
    hideColumn(key) {
        this.hideColumnKeys.push(key.toString());
        this.parent.notify(dataReady, { processedData: this.parent.kanbanData });
    }
    /**
     * Maintain the single card selection
     *
     * @param {Record<string, any>} data - Specifies the selected card data.
     * @returns {void}
     * @private
     * @hidden
     */
    SingleCardSelection(data) {
        if (this.parent.cardSettings.selectionType !== 'None' && data[this.parent.cardSettings.headerField]) {
            // eslint-disable-next-line no-useless-escape
            const card = this.parent.element.querySelector('.e-card[data-id=\"' +
                // eslint-disable-next-line no-useless-escape
                data[this.parent.cardSettings.headerField].toString() + '"\]');
            if (card) {
                addClass([card], CARD_SELECTION_CLASS);
                card.setAttribute('aria-selected', 'true');
                card.setAttribute('tabindex', '0');
            }
        }
    }
}

/**
 * Kanban CRUD module
 */
class Crud {
    /**
     * Constructor for CRUD module
     *
     * @param {Kanban} parent Accepts the kanban instance
     * @private
     */
    constructor(parent) {
        this.parent = parent;
    }
    addCard(cardData, index) {
        const args = {
            cancel: false, requestType: 'cardCreate', addedRecords: (cardData instanceof Array) ? cardData : [cardData],
            changedRecords: [], deletedRecords: []
        };
        this.parent.trigger(actionBegin, args, (addArgs) => {
            if (!addArgs.cancel) {
                let modifiedData = [];
                if (this.parent.sortSettings.field && this.parent.sortSettings.sortBy === 'Index') {
                    if (cardData instanceof Array) {
                        modifiedData = cardData;
                    }
                    else {
                        modifiedData.push(cardData);
                    }
                    modifiedData.forEach((data, index) => {
                        if (!data[this.parent.sortSettings.field]) {
                            const columnData = this.parent.getColumnData(data[this.parent.keyField]);
                            if (this.parent.sortSettings.direction === 'Ascending' && columnData.length > 0) {
                                data[this.parent.sortSettings.field] =
                                    (columnData[columnData.length - 1][this.parent.sortSettings.field]) + index + 1;
                            }
                            else if (this.parent.sortSettings.direction === 'Descending' && columnData.length > 0) {
                                data[this.parent.sortSettings.field] = columnData[0][this.parent.sortSettings.field] + index + 1;
                            }
                            if (columnData.length === 0) {
                                data[this.parent.sortSettings.field] = 1;
                            }
                        }
                    });
                    if (!(cardData instanceof Array)) {
                        if (!index && this.parent.sortSettings.direction === 'Descending') {
                            // eslint-disable-next-line max-len
                            this.parent.getColumnData(modifiedData[0][this.parent.keyField]).filter((obj, count) => {
                                if (obj[this.parent.sortSettings.field] === modifiedData[0][this.parent.sortSettings.field]) {
                                    index = count + 1;
                                }
                            });
                        }
                    }
                    if (index !== 0 && !index && this.parent.sortSettings.direction === 'Descending') {
                        index = 0;
                    }
                    modifiedData = this.priorityOrder(modifiedData, index);
                }
                if (this.parent.enableHtmlSanitizer) {
                    const field = this.parent.cardSettings.contentField;
                    if (this.parent.enableHtmlSanitizer && typeof field === 'string') {
                        cardData[field] =
                            SanitizeHtmlHelper.sanitize(cardData[field]);
                    }
                }
                const addedRecords = (cardData instanceof Array) ? cardData : [cardData];
                const changedRecords = (this.parent.sortSettings.field && this.parent.sortSettings.sortBy === 'Index') ? modifiedData : [];
                const editParms = { addedRecords: addedRecords, changedRecords: changedRecords, deletedRecords: [] };
                const type = (cardData instanceof Array || modifiedData.length > 0) ? 'batch' : 'insert';
                this.parent.dataModule.updateDataManager(type, editParms, 'cardCreated', cardData, index);
            }
        });
    }
    getIndexFromData(data) {
        const cardElement = this.parent.element.querySelector(`.${CARD_CLASS}[data-id="${data[this.parent.cardSettings.headerField]}"]`);
        const element = closest(cardElement, '.' + CONTENT_CELLS_CLASS);
        const index = [].slice.call(element.querySelectorAll('.' + CARD_CLASS)).indexOf(cardElement);
        return index;
    }
    updateCard(cardData, index, isDropped, dataDropIndexKeyFieldValue, draggedKey, droppedKey, isMultipleDrag) {
        const args = {
            requestType: 'cardChange', cancel: false, addedRecords: [],
            changedRecords: (cardData instanceof Array) ? cardData : [cardData], deletedRecords: []
        };
        index = isNullOrUndefined(index) ? this.getIndexFromData(args.changedRecords[0]) : index;
        this.parent.trigger(actionBegin, args, (updateArgs) => {
            if (!updateArgs.cancel) {
                if (this.parent.sortSettings.field && this.parent.sortSettings.sortBy === 'Index') {
                    let modifiedData = [];
                    if (cardData instanceof Array) {
                        modifiedData = cardData;
                    }
                    else {
                        modifiedData.push(cardData);
                    }
                    cardData = this.priorityOrder(modifiedData, index);
                }
                const editParms = {
                    addedRecords: [], changedRecords: (cardData instanceof Array) ? cardData : [cardData], deletedRecords: []
                };
                const type = (cardData instanceof Array) ? 'batch' : 'update';
                this.parent.dataModule.updateDataManager(type, editParms, 'cardChanged', cardData, index, isDropped, dataDropIndexKeyFieldValue, draggedKey, droppedKey, isMultipleDrag);
            }
        });
    }
    deleteCard(cardData) {
        const editParms = { addedRecords: [], changedRecords: [], deletedRecords: [] };
        if (typeof cardData === 'string' || typeof cardData === 'number') {
            editParms.deletedRecords = this.parent.kanbanData.filter((data) => data[this.parent.cardSettings.headerField] === cardData);
        }
        else {
            editParms.deletedRecords = (cardData instanceof Array) ? cardData : [cardData];
        }
        const args = {
            requestType: 'cardRemove', cancel: false, addedRecords: [], changedRecords: [], deletedRecords: editParms.deletedRecords
        };
        this.parent.trigger(actionBegin, args, (deleteArgs) => {
            if (!deleteArgs.cancel) {
                const type = (editParms.deletedRecords.length > 1) ? 'batch' : 'delete';
                const cardData = editParms.deletedRecords;
                this.parent.dataModule.updateDataManager(type, editParms, 'cardRemoved', cardData[0]);
            }
        });
    }
    priorityOrder(cardData, cardIndex) {
        const cardsId = cardData.map((obj) => obj[this.parent.cardSettings.headerField]);
        let num = cardData[cardData.length - 1][this.parent.sortSettings.field];
        const allModifiedKeys = cardData.map((obj) => obj[this.parent.keyField]);
        const modifiedKey = allModifiedKeys.filter((key, index) => allModifiedKeys.indexOf(key) === index).sort();
        let columnAllDatas;
        const finalData = [];
        const originalIndex = [];
        for (const columnKey of modifiedKey) {
            const keyData = cardData.filter((cardObj) => cardObj[this.parent.keyField] === columnKey);
            columnAllDatas = this.parent.enableVirtualization ? this.parent.virtualLayoutModule.getColumnData(columnKey)
                : this.parent.layoutModule.getColumnData(columnKey);
            for (const data of keyData) {
                if (this.parent.swimlaneSettings.keyField) {
                    const swimlaneDatas = this.parent.getSwimlaneData(data[this.parent.swimlaneSettings.keyField]);
                    columnAllDatas = this.parent.getColumnData(columnKey, swimlaneDatas);
                }
            }
            keyData.forEach((key) => finalData.push(key));
            if (!isNullOrUndefined(cardIndex)) {
                for (let j = 0; j < cardsId.length; j++) {
                    columnAllDatas.filter((data, index) => {
                        if (data[this.parent.cardSettings.headerField] === cardsId[j] && index <= cardIndex) {
                            originalIndex.push(index);
                        }
                    });
                }
                if (originalIndex.length > 0) {
                    cardIndex = cardIndex + originalIndex.length;
                }
                if (this.parent.sortSettings.direction === 'Ascending') {
                    for (let i = cardIndex; i < columnAllDatas.length; i++) {
                        if (cardsId.indexOf(columnAllDatas[i][this.parent.cardSettings.headerField]) === -1) {
                            columnAllDatas[i][this.parent.sortSettings.field] = ++num;
                            finalData.push(columnAllDatas[i]);
                        }
                    }
                }
                else {
                    for (let i = cardIndex - 1; i >= 0; i--) {
                        if (cardsId.indexOf(columnAllDatas[i][this.parent.cardSettings.headerField]) === -1) {
                            columnAllDatas[i][this.parent.sortSettings.field] = ++num;
                            finalData.push(columnAllDatas[i]);
                        }
                    }
                }
            }
        }
        return finalData;
    }
}

/* eslint-disable @typescript-eslint/no-explicit-any */
/**
 * Drag and Drop module is used to perform card actions.
 */
class DragAndDrop {
    /**
     * Constructor for drag and drop module
     *
     * @param {Kanban} parent Accepts the kanban instance
     * @private
     */
    constructor(parent) {
        this.insertClone = 'afterend';
        this.parent = parent;
        this.dragObj = {
            element: null, cloneElement: null, instance: null, targetClone: null, draggedClone: null, targetCloneMulti: null,
            selectedCards: [], pageX: 0, pageY: 0, navigationInterval: null, cardDetails: [], modifiedData: []
        };
        this.dragEdges = { left: false, right: false, top: false, bottom: false };
        this.isDragging = false;
        this.isExternalDrop = false;
    }
    wireDragEvents(element) {
        let dragContainment;
        if (!this.parent.element != null && this.parent.externalDropId.length === 0) {
            dragContainment = this.parent.element.querySelector('.' + CONTENT_CLASS);
        }
        this.dragObj.instance = new Draggable(element, {
            clone: true,
            enableTapHold: this.parent.isAdaptive,
            enableTailMode: true,
            cursorAt: { top: -10, left: -10 },
            dragArea: dragContainment,
            dragStart: this.dragStart.bind(this),
            drag: this.drag.bind(this),
            dragStop: this.dragStop.bind(this),
            enableAutoScroll: false,
            helper: this.dragHelper.bind(this)
        });
    }
    dragHelper(e) {
        if (this.parent.isAdaptive && this.parent.touchModule.mobilePopup &&
            this.parent.touchModule.mobilePopup.element.classList.contains(POPUP_OPEN_CLASS)) {
            this.parent.touchModule.mobilePopup.hide();
        }
        this.dragObj.element = closest(e.sender.target, '.' + CARD_CLASS);
        if (isNullOrUndefined(this.dragObj.element)) {
            return null;
        }
        this.dragObj.element.style.width = formatUnit(this.dragObj.element.offsetWidth);
        const cloneWrapper = createElement('div', { innerHTML: this.dragObj.element.outerHTML });
        this.dragObj.cloneElement = cloneWrapper.children.item(0);
        addClass([this.dragObj.cloneElement], CLONED_CARD_CLASS);
        this.dragObj.element.parentElement.appendChild(this.dragObj.cloneElement);
        this.dragObj.targetCloneMulti = createElement('div', { className: TARGET_MULTI_CLONE_CLASS });
        this.dragObj.targetClone = createElement('div', {
            className: DROPPED_CLONE_CLASS,
            styles: 'width:100%;height:' + formatUnit(this.dragObj.element.offsetHeight)
        });
        this.dragObj.modifiedData = [];
        return this.dragObj.cloneElement;
    }
    dragStart(e) {
        this.dragObj.selectedCards = this.dragObj.element;
        this.borderElm = this.parent.element.querySelectorAll('.' + BORDER_CLASS);
        if (this.dragObj.element.classList.contains(CARD_SELECTION_CLASS)) {
            const className = '.' + CARD_CLASS + '.' + CARD_SELECTION_CLASS + ':not(.' + CLONED_CARD_CLASS + ')';
            const closestEle = closest(this.dragObj.element, '.' + CONTENT_ROW_CLASS);
            this.dragObj.selectedCards = [].slice.call(closestEle.querySelectorAll(className));
            this.dragObj.selectedCards.forEach((element) => {
                this.dragObj.cardDetails.push(this.parent.getCardDetails(element));
            });
        }
        else {
            this.dragObj.cardDetails = [this.parent.getCardDetails(this.dragObj.element)];
        }
        if (!isNullOrUndefined(this.dragObj.selectedCards) && !isNullOrUndefined(this.dragObj.selectedCards.length) &&
            this.dragObj.selectedCards.length >= 1) {
            this.dragObj.selectedCards[0].closest('.e-content-cells').classList.add('e-dragged-column');
        }
        else if (!isNullOrUndefined(this.dragObj.selectedCards) &&
            !isNullOrUndefined(this.dragObj.selectedCards.closest('.e-content-cells'))) {
            this.dragObj.selectedCards.closest('.e-content-cells').classList.add('e-dragged-column');
        }
        const dragArgs = { cancel: false, data: this.dragObj.cardDetails, event: e, element: this.dragObj.selectedCards };
        this.parent.trigger(dragStart, dragArgs, (dragEventArgs) => {
            if (dragEventArgs.cancel) {
                this.removeElement(this.dragObj.cloneElement);
                this.dragObj.instance.intDestroy(e);
                this.dragObj.element = null;
                this.dragObj.targetClone = null;
                this.dragObj.draggedClone = null;
                this.dragObj.cloneElement = null;
                this.dragObj.targetCloneMulti = null;
                return;
            }
            if (this.dragObj.element.classList.contains(CARD_SELECTION_CLASS)) {
                this.dragObj.selectedCards.forEach((element) => { this.draggedClone(element); });
                if (this.dragObj.selectedCards.length > 1) {
                    this.dragObj.cloneElement.innerHTML = '';
                    const drag = createElement('div', {
                        className: 'e-multi-card-text',
                        innerHTML: this.dragObj.selectedCards.length + ' ' + this.parent.localeObj.getConstant('cards')
                    });
                    this.dragObj.cloneElement.appendChild(drag);
                    classList(this.dragObj.cloneElement, ['e-multi-card-clone'], [CARD_SELECTION_CLASS]);
                    if (this.parent.enableVirtualization) {
                        this.parent.virtualLayoutModule.disableAttributeSelection(this.dragObj.cloneElement);
                    }
                    else {
                        this.parent.layoutModule.disableAttributeSelection(this.dragObj.cloneElement);
                    }
                    this.dragObj.cloneElement.style.width = '90px';
                }
            }
            else {
                this.draggedClone(this.dragObj.element);
            }
            EventHandler.add(document.body, 'keydown', this.keydownHandler, this);
            this.parent.notify(contentReady, {});
        });
    }
    draggedClone(element) {
        this.dragObj.draggedClone = createElement('div', {
            className: DRAGGED_CLONE_CLASS,
            styles: 'width:' + formatUnit(element.offsetWidth - 1) + ';height:' + formatUnit(element.offsetHeight)
        });
        element.insertAdjacentElement('afterend', this.dragObj.draggedClone);
        addClass([element], DRAGGED_CARD_CLASS);
    }
    drag(e) {
        if (!e.target) {
            return;
        }
        let cardElement = closest(e.target, '.' + ROOT_CLASS + ' .' + CARD_CLASS);
        if (!isNullOrUndefined(cardElement) && this.parent.enableVirtualization && !isNullOrUndefined(e.target.previousElementSibling) &&
            !isNullOrUndefined(e.target.previousElementSibling.querySelector('.e-target-dropped-clone'))) {
            cardElement = e.target.previousElementSibling.querySelector('.e-target-dropped-clone').nextElementSibling;
        }
        let targetEle = e.target;
        if (!isNullOrUndefined(e.target.parentElement)) {
            if (e.target.nodeName === 'SPAN' && e.target.classList.contains('e-empty-card')) {
                targetEle = e.target.parentElement;
            }
            else if (e.target.nodeName === 'DIV' && e.target.classList.contains('e-kanban-border')) {
                if (!(this.parent.element.querySelector('.e-target-dropped-clone') === e.target.nextElementSibling.firstChild)) {
                    targetEle = e.target.parentElement;
                }
            }
        }
        const target = cardElement || targetEle;
        const selector = '.' + CONTENT_ROW_CLASS + ':not(.' + SWIMLANE_ROW_CLASS + ') .' + CONTENT_CELLS_CLASS
            + '.' + DROPPABLE_CLASS;
        const contentCell = closest(target, selector);
        let cellDimension;
        let borderElem;
        let dropElement;
        if (target.nextElementSibling && target.nextElementSibling.lastChild) {
            dropElement = target.nextElementSibling.lastChild.previousElementSibling;
        }
        this.externalDrop(target);
        this.kanbanObj = this.parent.isExternalKanbanDrop ? this.parent.externalDropObj : this.parent;
        this.calculateArgs(e);
        if (contentCell && document.body.style.cursor !== 'not-allowed') {
            const targetKey = this.getColumnKey(contentCell);
            const keys = targetKey.split(',');
            this.multiCloneRemove();
            const isDrag = (targetKey === this.getColumnKey(closest(this.dragObj.draggedClone, '.' + CONTENT_CELLS_CLASS)))
                ? true : false;
            if (keys.length === 1 || isDrag) {
                if (target.classList.contains(DRAGGED_CLONE_CLASS)) {
                    this.removeElement(this.dragObj.targetClone, this.kanbanObj);
                }
                if (target.classList.contains(CARD_CLASS) || this.insertClone === 'beforebegin') {
                    const element = target.classList.contains(DRAGGED_CLONE_CLASS) ?
                        (target.previousElementSibling.classList.contains(DRAGGED_CARD_CLASS) ? null : target.previousElementSibling)
                        : target.previousElementSibling;
                    this.insertClone = 'afterend';
                    if (isNullOrUndefined(element)) {
                        let pageY = target.classList.contains(DRAGGED_CLONE_CLASS) ? (this.dragObj.pageY / 2) :
                            this.dragObj.pageY;
                        const height = target.classList.contains(DRAGGED_CLONE_CLASS) ? target.offsetHeight :
                            (target.offsetHeight / 2);
                        const kanbanTop = this.kanbanObj.element.getBoundingClientRect().top + window.scrollY;
                        const targetTop = target.getBoundingClientRect().top + window.scrollY;
                        const relativeTop = targetTop - kanbanTop;
                        pageY = pageY - kanbanTop;
                        if ((pageY - relativeTop) < height) {
                            this.insertClone = 'beforebegin';
                        }
                    }
                    if (target.classList.contains(CARD_CLASS)) {
                        if (this.parent.enableVirtualization) {
                            this.insertClone = this.isTargetElementVisible(target) ? this.insertClone : 'beforebegin';
                        }
                        target.insertAdjacentElement(this.insertClone, this.dragObj.targetClone);
                    }
                }
                else if (target.classList.contains(CONTENT_CELLS_CLASS) && !closest(target, '.' + SWIMLANE_ROW_CLASS)) {
                    if (target.querySelectorAll('.' + DRAGGED_CARD_CLASS).length !== 0 &&
                        target.querySelectorAll('.' + CARD_CLASS + ':not(.e-kanban-dragged-card):not(.e-cloned-card)').length === 0) {
                        return;
                    }
                    else {
                        target.querySelector('.' + CARD_WRAPPER_CLASS).appendChild(this.dragObj.targetClone);
                    }
                }
                else if ((target.classList.contains(CARD_WRAPPER_CLASS) ||
                    target.classList.contains(CARD_VIRTUAL_WRAPPER_CLASS)) &&
                    !closest(target, '.' + SWIMLANE_ROW_CLASS)
                    && contentCell.querySelectorAll('.' + CARD_CLASS).length === 0) {
                    target.appendChild(this.dragObj.targetClone);
                }
                else if (target.classList.contains(BORDER_CLASS) && !closest(target, '.' + SWIMLANE_ROW_CLASS)
                    && (target.nextElementSibling && target.nextElementSibling.classList.contains(CARD_WRAPPER_CLASS))
                    && this.dragObj.targetClone && (!dropElement || !dropElement.classList.contains(DROPPED_CLONE_CLASS))) {
                    if (!this.parent.enableVirtualization && !this.isTargetElementVisible(target.nextElementSibling)) {
                        target.nextElementSibling.appendChild(this.dragObj.targetClone);
                    }
                }
            }
            else if (keys.length > 1 && (contentCell.classList.contains(DROPPING_CLASS) ||
                contentCell.firstChild && contentCell.firstChild.classList.contains(DROPPING_CLASS))) {
                this.multiCloneCreate(keys, contentCell);
            }
            this.kanbanObj.notify(contentReady, {});
        }
        if (this.kanbanObj.element.querySelectorAll('.' + DROPPING_CLASS).length === 0) {
            this.cellDropping();
        }
        let isCollapsed = false;
        if (contentCell) {
            isCollapsed = contentCell.classList.contains(COLLAPSED_CLASS) && contentCell.classList.contains(DROPPING_CLASS);
            if (contentCell.getAttribute('aria-expanded') === 'true' || !contentCell.parentElement.hasAttribute('aria-expanded')) {
                cellDimension = contentCell.getBoundingClientRect();
                this.updateDimension(cellDimension);
            }
            borderElem = contentCell.querySelector('.' + BORDER_CLASS);
        }
        if (target && target.tagName === 'TABLE' && !isNullOrUndefined(target.querySelector('.' + CONTENT_ROW_CLASS))) {
            cellDimension = target.querySelector('.' + CONTENT_ROW_CLASS).getBoundingClientRect();
            this.updateDimension(cellDimension, target);
        }
        if (isCollapsed) {
            this.toggleVisible(target);
            addClass([contentCell], TOGGLE_VISIBLE_CLASS);
        }
        const tColumn = [].slice.call(this.kanbanObj.element.querySelectorAll('.' + TOGGLE_VISIBLE_CLASS));
        if (tColumn.length > 0 && !target.classList.contains(TOGGLE_VISIBLE_CLASS)
            && !closest(target, '.' + TOGGLE_VISIBLE_CLASS)) {
            this.toggleVisible(target, tColumn.slice(-1)[0]);
            removeClass(tColumn, TOGGLE_VISIBLE_CLASS);
        }
        this.kanbanObj.notify(contentReady, {});
        const multiKeyTarget = closest(target, '.' + MULTI_COLUMN_KEY_CLASS);
        if (multiKeyTarget) {
            const columnKeys = [].slice.call(this.kanbanObj.element.querySelectorAll('.' + MULTI_COLUMN_KEY_CLASS + ':not(.' +
                DISABLED_CLASS + ')')).filter((element) => this.getColumnKey(element) === this.getColumnKey(multiKeyTarget));
            if (columnKeys.length > 0) {
                addClass(columnKeys, MULTI_ACTIVE_CLASS);
                if (columnKeys[0].previousElementSibling) {
                    addClass([columnKeys[0].previousElementSibling], 'e-multi-bottom-border');
                }
            }
        }
        document.body.style.cursor = (contentCell && contentCell.classList.contains(DROPPING_CLASS) ||
            (contentCell && borderElem && borderElem.classList.contains(DROPPING_CLASS))) ? '' : 'not-allowed';
        if (cardElement && !(closest(cardElement, '.' + CONTENT_CELLS_CLASS)).classList.contains(DROPPING_CLASS) &&
            !(contentCell && borderElem && borderElem.classList.contains(DROPPING_CLASS))) {
            cardElement.style.cursor = 'not-allowed';
            document.body.style.cursor = 'not-allowed';
        }
        if (this.isExternalDrop && document.body.style.cursor === 'not-allowed') {
            document.body.style.cursor = '';
        }
        if (document.body.style.cursor === 'not-allowed') {
            this.removeElement(this.dragObj.targetClone, this.kanbanObj);
            this.multiCloneRemove();
        }
        this.updateScrollPosition();
        const dragArgs = { data: this.dragObj.cardDetails, event: e, element: this.dragObj.selectedCards };
        this.kanbanObj.trigger(drag, dragArgs);
        this.parent.isExternalKanbanDrop = false;
        this.isExternalDrop = false;
    }
    removeElement(element, kanbanObj) {
        kanbanObj = kanbanObj ? kanbanObj : this.parent;
        if (kanbanObj.element.getElementsByClassName(element.className).length > 0) {
            remove(element);
        }
    }
    isTargetElementVisible(targetElem) {
        const wrapperElem = closest(targetElem, '.' + CARD_WRAPPER_CLASS);
        if (!isNullOrUndefined(wrapperElem)) {
            const wrapperElemBottom = wrapperElem.getBoundingClientRect().bottom;
            const targetElemBottom = targetElem.getBoundingClientRect().bottom;
            if (targetElemBottom > wrapperElemBottom) {
                return false;
            }
            return true;
        }
        return true;
    }
    externalDrop(target) {
        this.parent.externalDropId.forEach((externalDropId) => {
            const targetRootElement = closest(target, externalDropId);
            if (targetRootElement) {
                if (targetRootElement.classList.contains('e-kanban')) {
                    this.parent.externalDropObj = document.querySelector(externalDropId).ej2_instances[0];
                    this.parent.isExternalKanbanDrop = true;
                    const className = '.' + CONTENT_ROW_CLASS + ':not(.' + SWIMLANE_ROW_CLASS +
                        '):not(.' + COLLAPSED_CLASS + ') .' + CONTENT_CELLS_CLASS;
                    const cells = [].slice.call(this.parent.externalDropObj.element.querySelectorAll(className));
                    addClass(cells, DROPPING_CLASS);
                }
                else {
                    this.isExternalDrop = true;
                }
            }
        });
    }
    multiCloneCreate(keys, contentCell) {
        let offsetHeight = contentCell.offsetHeight;
        const limitEle = contentCell.querySelector('.' + LIMITS_CLASS);
        if (limitEle) {
            offsetHeight -= limitEle.offsetHeight;
        }
        this.dragObj.targetCloneMulti.style.height = formatUnit(offsetHeight);
        if (contentCell.querySelector('.' + SHOW_ADD_BUTTON)) {
            addClass([contentCell.querySelector('.' + SHOW_ADD_BUTTON)], MULTI_CARD_WRAPPER_CLASS);
        }
        addClass([contentCell.querySelector('.' + CARD_WRAPPER_CLASS)], MULTI_CARD_WRAPPER_CLASS);
        contentCell.querySelector('.' + CARD_WRAPPER_CLASS).style.height = 'auto';
        contentCell.style.borderStyle = 'none';
        this.removeElement(this.dragObj.targetClone);
        for (const key of keys) {
            const dragCell = closest(this.dragObj.draggedClone, '.' + CONTENT_CELLS_CLASS);
            const transition = this.kanbanObj.columns[dragCell.cellIndex].transitionColumns;
            const allowTransition = this.allowedTransition(this.dragObj.element.getAttribute('data-key'), key, transition);
            const name = allowTransition ? '' : ' ' + DISABLED_CLASS;
            const colKey = createElement('div', {
                className: MULTI_COLUMN_KEY_CLASS + name,
                attrs: { 'data-key': key.trim() }
            });
            const text = createElement('div', { className: 'e-text', innerHTML: key.trim() });
            contentCell.appendChild(this.dragObj.targetCloneMulti).appendChild(colKey).appendChild(text);
            colKey.style.cursor = allowTransition ? '' : 'not-allowed';
            colKey.style.lineHeight = colKey.style.height = formatUnit((offsetHeight / keys.length));
            text.style.top = formatUnit((offsetHeight / 2) - (text.offsetHeight / 2));
        }
    }
    allowedTransition(currentCardKey, targetCardKey, allowedKey) {
        let allowTransition = true;
        const targetKey = targetCardKey.split(',');
        for (let i = 0; i < targetKey.length; i++) {
            if (currentCardKey === targetKey[i].trim()) {
                return true;
            }
            if (allowedKey) {
                if (allowedKey.length === 1 && allowedKey[0].length === 0) {
                    return true;
                }
                for (let j = 0; j < allowedKey.length; j++) {
                    if (targetKey[i].trim() === allowedKey[j].trim()) {
                        return true;
                    }
                    else {
                        allowTransition = false;
                    }
                }
            }
        }
        return allowTransition;
    }
    cellDropping() {
        const dragCell = closest(this.dragObj.draggedClone, '.' + CONTENT_CELLS_CLASS);
        const dragRow = closest(this.dragObj.draggedClone, '.' + CONTENT_ROW_CLASS);
        if (dragCell && dragCell.classList.contains(DROP_CLASS)) {
            addClass([dragCell], DROPPING_CLASS);
        }
        this.addDropping(dragRow, dragCell);
        if (this.kanbanObj.swimlaneSettings.keyField && this.kanbanObj.swimlaneSettings.allowDragAndDrop) {
            const className = '.' + CONTENT_ROW_CLASS + ':not(.' + SWIMLANE_ROW_CLASS + '):not(.' + COLLAPSED_CLASS + ')';
            const rows = [].slice.call(this.kanbanObj.element.querySelectorAll(className));
            [].slice.call(rows).forEach((row) => {
                if (dragRow !== row) {
                    this.addDropping(row, dragCell);
                }
            });
        }
    }
    addDropping(dragRow, dragCell) {
        if (dragCell && this.borderElm && this.borderElm.length !== 0) {
            if (dragCell.classList.contains(DROPPING_CLASS)) {
                removeClass([dragCell], DROPPING_CLASS);
            }
            const cellDimension = dragCell.getBoundingClientRect();
            this.updateDimension(cellDimension);
        }
        else if (dragCell && dragRow) {
            [].slice.call(dragRow.children).forEach((cell) => {
                const transition = this.kanbanObj.columns[dragCell.cellIndex].transitionColumns;
                if (cell !== dragCell && cell.classList.contains(DROP_CLASS) &&
                    this.allowedTransition(dragCell.getAttribute('data-key'), cell.getAttribute('data-key'), transition)) {
                    addClass([cell], DROPPING_CLASS);
                }
            });
        }
    }
    updateDimension(dimensions, target) {
        [].slice.call(this.borderElm).forEach((element) => {
            if (element.parentElement && (element.parentElement.getAttribute('aria-expanded') === 'true' || !element.parentElement.hasAttribute('aria-expanded'))) {
                addClass([element], DROPPING_CLASS);
            }
            const hasAddButton = element.previousElementSibling;
            element.style.height = parseInt(dimensions.height.toString(), 10) - (hasAddButton &&
                hasAddButton.classList.contains(SHOW_ADD_BUTTON) ? hasAddButton.offsetHeight + hasAddButton.offsetTop : 0) + 'px';
            if (!target || target.tagName !== 'TABLE') {
                element.style.width = parseInt(dimensions.width.toString(), 10) + 'px';
            }
            element.style.left = (element.parentElement.getBoundingClientRect().left - closest(element, '.e-kanban').getBoundingClientRect().left) + 'px';
        });
    }
    keydownHandler(e) {
        if (e.code === 'Escape' && this.dragObj.cloneElement) {
            EventHandler.remove(this.dragObj.cloneElement, 'keydown', this.keydownHandler);
            this.dragObj.element.removeAttribute('aria-grabbed');
            this.dragStopClear();
            this.dragStopPostClear();
        }
    }
    dragStop(e) {
        let contentCell = closest(this.dragObj.targetClone, '.' + CONTENT_CELLS_CLASS);
        if (this.parent.enableVirtualization && !isNullOrUndefined(contentCell)) {
            contentCell.classList.add('e-dropped-column');
        }
        let columnKey;
        let dropIndex;
        let dataDropIndexKeyfieldValue;
        let isMultipleDrag;
        EventHandler.remove(document.body, 'keydown', this.keydownHandler);
        [].slice.call(this.borderElm).forEach((element) => {
            element.classList.remove(DROPPING_CLASS);
        });
        if (this.dragObj.targetClone.parentElement) {
            isMultipleDrag = (this.dragObj.selectedCards && this.dragObj.selectedCards.length > 1
                && this.parent.sortSettings.sortBy === 'Index');
            const className = !isMultipleDrag ? '.' + CARD_CLASS + ':not(.' + DRAGGED_CARD_CLASS + ', .' + CLONED_CARD_CLASS + '),.' + DROPPED_CLONE_CLASS :
                '.' + CARD_CLASS + ':not(.' + CLONED_CARD_CLASS + '),.' + DROPPED_CLONE_CLASS;
            const element = [].slice.call(this.dragObj.targetClone.parentElement.querySelectorAll(className));
            dropIndex = element.indexOf(this.dragObj.targetClone);
            if (this.parent.enableVirtualization && !isNullOrUndefined(this.dragObj.targetClone.nextElementSibling)) {
                dataDropIndexKeyfieldValue = this.dragObj.targetClone.nextElementSibling.getAttribute('data-id');
            }
        }
        if (!isNullOrUndefined(this.kanbanObj) && this.kanbanObj.element.querySelector('.' + TARGET_MULTI_CLONE_CLASS)) {
            columnKey = closest(e.target, '.' + MULTI_COLUMN_KEY_CLASS + ':not(.' + DISABLED_CLASS + ')');
        }
        if (contentCell || columnKey) {
            let cardStatus;
            if (contentCell) {
                cardStatus = this.getColumnKey(contentCell);
            }
            else {
                cardStatus = this.getColumnKey(columnKey);
                contentCell = closest(columnKey, '.' + CONTENT_CELLS_CLASS);
            }
            if (this.dragObj.selectedCards instanceof HTMLElement) {
                this.updateDroppedData(this.dragObj.selectedCards, cardStatus, contentCell);
            }
            else {
                this.dragObj.selectedCards.forEach((element) => { this.updateDroppedData(element, cardStatus, contentCell); });
            }
            if (this.parent.sortSettings.field && this.parent.sortSettings.sortBy === 'Index') {
                this.changeOrder(this.dragObj.modifiedData, e.helper);
            }
        }
        if (this.dragObj.modifiedData.length === 0) {
            this.dragObj.modifiedData = this.dragObj.cardDetails;
        }
        const dragArgs = {
            cancel: false, data: this.dragObj.modifiedData, event: e, element: this.dragObj.selectedCards,
            dropIndex: dropIndex
        };
        this.parent.trigger(dragStop, dragArgs, (dragEventArgs) => {
            this.dragStopClear();
            if (!dragEventArgs.cancel) {
                if (contentCell || columnKey) {
                    const updateCard = dragEventArgs.data instanceof Array &&
                        dragEventArgs.data.length > 1 ? dragEventArgs.data :
                        dragEventArgs.data[0];
                    let draggedColumnKey;
                    let droppedColumnKey;
                    if (this.parent.enableVirtualization) {
                        draggedColumnKey = contentCell.closest('.e-kanban').querySelector('.e-dragged-column').getAttribute('data-key');
                        droppedColumnKey = contentCell.getAttribute('data-key');
                    }
                    this.parent.crudModule.updateCard(updateCard, dragEventArgs.dropIndex, true, dataDropIndexKeyfieldValue, draggedColumnKey, droppedColumnKey, isMultipleDrag);
                    if (this.parent.enableVirtualization) {
                        this.parent.virtualLayoutModule.refreshColumnData(draggedColumnKey, droppedColumnKey);
                        this.parent.virtualLayoutModule.ensureColumnNotEmpty(draggedColumnKey);
                    }
                }
            }
            this.removeEmptyTrElement();
            this.dragStopPostClear();
        });
    }
    removeEmptyTrElement() {
        if (!this.parent.swimlaneSettings.showEmptyRow) {
            const swimlaneRowList = this.parent.element.querySelectorAll('.e-content-row.e-swimlane-row');
            for (let j = 0; j < swimlaneRowList.length; j++) {
                const cardRowData = swimlaneRowList[j].nextElementSibling.querySelectorAll('.e-card-wrapper .e-card');
                if (!isNullOrUndefined(swimlaneRowList[j].nextElementSibling) && cardRowData.length === 0) {
                    detach(swimlaneRowList[j].nextElementSibling);
                    detach(swimlaneRowList[j]);
                }
            }
        }
    }
    dragStopClear() {
        this.removeElement(this.dragObj.draggedClone);
        this.removeElement(this.dragObj.targetClone, this.kanbanObj);
        this.removeElement(this.dragObj.cloneElement);
        const dragMultiClone = [].slice.call(this.parent.element.querySelectorAll('.' + DRAGGED_CLONE_CLASS));
        dragMultiClone.forEach((clone) => { remove(clone); });
        this.dragObj.element.style.removeProperty('width');
        this.multiCloneRemove();
        if (this.dragObj.selectedCards instanceof HTMLElement) {
            removeClass([this.dragObj.selectedCards], DRAGGED_CARD_CLASS);
        }
        else {
            removeClass(this.dragObj.selectedCards, DRAGGED_CARD_CLASS);
        }
        clearInterval(this.dragObj.navigationInterval);
        this.dragObj.navigationInterval = null;
        if (document.body.style.cursor === 'not-allowed') {
            document.body.style.cursor = '';
        }
        const styleCards = [].slice.call(this.parent.element.querySelectorAll('.' + CARD_CLASS + '[style]'));
        styleCards.forEach((styleCard) => { styleCard.style.cursor = ''; });
        const className = '.' + CONTENT_ROW_CLASS + ':not(.' + SWIMLANE_ROW_CLASS + ')';
        const cells = [].slice.call(this.parent.element.querySelectorAll(className + ' .' + CONTENT_CELLS_CLASS));
        cells.forEach((cell) => removeClass([cell], DROPPING_CLASS));
        if (this.parent.externalDropObj) {
            const externalCells = [].slice.call(this.parent.externalDropObj.element.querySelectorAll(className + ' .' +
                CONTENT_CELLS_CLASS));
            externalCells.forEach((externalCell) => removeClass([externalCell], DROPPING_CLASS));
        }
    }
    dragStopPostClear() {
        if (this.parent.isAdaptive) {
            this.parent.touchModule.tabHold = false;
        }
        if (this.parent.element.querySelector('.e-dragged-column')) {
            this.parent.element.querySelector('.e-dragged-column').classList.remove('e-dragged-column');
        }
        if (this.parent.element.querySelector('.e-dropped-column')) {
            this.parent.element.querySelector('.e-dropped-column').classList.remove('e-dropped-column');
        }
        this.dragObj.cardDetails = this.dragObj.modifiedData = [];
        this.isDragging = false;
        this.parent.isExternalKanbanDrop = false;
        this.parent.externalDropObj = null;
    }
    updateDroppedData(element, cardStatus, contentCell) {
        const crudObj = this.parent.getCardDetails(element);
        const crudData = extend({}, crudObj, null, true);
        if (cardStatus.split(',').length === 1) {
            crudData[this.parent.keyField] = cardStatus;
        }
        if (this.parent.swimlaneSettings.keyField && this.parent.swimlaneSettings.allowDragAndDrop) {
            const prev = closest(contentCell, '.' + CONTENT_ROW_CLASS).previousElementSibling;
            if (this.parent.isAdaptive) {
                const keyField = this.parent.layoutModule.kanbanRows[this.parent.layoutModule.swimlaneIndex].keyField;
                crudData[this.parent.swimlaneSettings.keyField] = keyField;
            }
            else {
                crudData[this.parent.swimlaneSettings.keyField] = this.getColumnKey(prev);
            }
        }
        this.dragObj.modifiedData.push(crudData);
    }
    changeOrder(modifieddata, draggedCard) {
        let prevele = false;
        let element;
        if (this.kanbanObj.sortSettings.direction === 'Ascending') {
            element = (draggedCard === this.dragObj.targetClone.previousElementSibling) &&
                (this.dragObj.targetClone.previousElementSibling &&
                    this.dragObj.targetClone.previousElementSibling.previousElementSibling) ?
                this.dragObj.targetClone.previousElementSibling.previousElementSibling : this.dragObj.targetClone.previousElementSibling;
        }
        else {
            element = this.dragObj.targetClone.nextElementSibling;
        }
        if (element && !element.classList.contains(DRAGGED_CARD_CLASS) && !element.classList.contains(CLONED_CARD_CLASS)
            && !element.classList.contains(DRAGGED_CLONE_CLASS)) {
            prevele = true;
        }
        else if (this.dragObj.targetClone.nextElementSibling && this.kanbanObj.sortSettings.direction === 'Ascending') {
            element = this.dragObj.targetClone.nextElementSibling;
        }
        else if (this.dragObj.targetClone.previousElementSibling && this.kanbanObj.sortSettings.direction === 'Descending') {
            element = this.dragObj.targetClone.previousElementSibling;
        }
        else {
            return;
        }
        if (element.classList.contains(CARD_CLASS)) {
            const obj = this.kanbanObj.getCardDetails(element);
            let keyIndex = obj[this.kanbanObj.sortSettings.field];
            if (modifieddata.length > 1 && this.kanbanObj.sortSettings.direction === 'Descending') {
                modifieddata = modifieddata.reverse();
            }
            modifieddata.forEach((data, index) => {
                if (prevele) {
                    data[this.kanbanObj.sortSettings.field] = ++keyIndex;
                }
                else if (keyIndex !== 1 && index <= data[this.kanbanObj.sortSettings.field]) {
                    data[this.kanbanObj.sortSettings.field] = --keyIndex;
                }
                else if (keyIndex === 1) {
                    data[this.kanbanObj.sortSettings.field] = index + 1;
                }
            });
        }
    }
    toggleVisible(target, tColumn) {
        const headerCells = '.' + HEADER_CELLS_CLASS + ':not(.' + STACKED_HEADER_CELL_CLASS + ')';
        const lists = [].slice.call(this.kanbanObj.element.querySelectorAll(headerCells));
        lists.forEach((list) => {
            if (this.getColumnKey(list) === this.getColumnKey(tColumn || target)) {
                this.kanbanObj.actionModule.columnToggle(list);
            }
        });
        const cloneTarget = closest(this.dragObj.draggedClone, '.' + CONTENT_CELLS_CLASS);
        if (cloneTarget) {
            const width = formatUnit(cloneTarget.offsetWidth - cardSpace);
            this.dragObj.draggedClone.style.width = width;
            this.dragObj.cloneElement.style.width = width;
        }
    }
    multiCloneRemove() {
        const cloneMulti = !isNullOrUndefined(this.kanbanObj) ? [].slice.call(this.kanbanObj.element.querySelectorAll('.' + TARGET_MULTI_CLONE_CLASS)) : [];
        if (cloneMulti.length > 0) {
            const columnKey = [].slice.call(this.kanbanObj.element.querySelectorAll('.' + MULTI_COLUMN_KEY_CLASS));
            columnKey.forEach((node) => remove(node));
            cloneMulti.forEach((node) => {
                const cell = closest(node, '.' + CONTENT_CELLS_CLASS);
                if (cell) {
                    cell.style.borderStyle = '';
                    if (cell.querySelector('.' + SHOW_ADD_BUTTON)) {
                        removeClass([cell.querySelector('.' + SHOW_ADD_BUTTON)], MULTI_CARD_WRAPPER_CLASS);
                    }
                    removeClass([cell.querySelector('.' + CARD_WRAPPER_CLASS)], MULTI_CARD_WRAPPER_CLASS);
                }
            });
            this.removeElement(this.dragObj.targetCloneMulti, this.kanbanObj);
        }
    }
    calculateArgs(e) {
        const eventArgs = this.getPageCoordinates(e);
        this.dragObj.pageY = eventArgs.pageY;
        this.dragObj.pageX = eventArgs.pageX;
        this.isDragging = true;
        if (this.kanbanObj.isAdaptive && this.kanbanObj.tooltipModule) {
            this.kanbanObj.tooltipModule.tooltipObj.close();
        }
    }
    getPageCoordinates(e) {
        const eventArgs = e.event;
        return eventArgs && eventArgs.changedTouches ? eventArgs.changedTouches[0] : e.changedTouches ? e.changedTouches[0] :
            eventArgs || e;
    }
    getColumnKey(target) {
        if (target && target.getAttribute('data-key')) {
            return target.getAttribute('data-key').trim();
        }
        return '';
    }
    updateScrollPosition() {
        if (isNullOrUndefined(this.dragObj.navigationInterval)) {
            this.dragObj.navigationInterval = window.setInterval(() => { this.autoScroll(); }, 100);
        }
    }
    autoScrollValidation() {
        const pageY = this.dragObj.pageY;
        const pageX = this.dragObj.pageX;
        const autoScrollDistance = 30;
        const dragEdges = { left: false, right: false, top: false, bottom: false };
        const viewBoundaries = this.kanbanObj.element.querySelector('.' + CONTENT_CLASS).getBoundingClientRect();
        if ((pageY < viewBoundaries.top + autoScrollDistance + window.pageYOffset) &&
            (pageY > viewBoundaries.top + window.pageYOffset)) {
            dragEdges.top = true;
        }
        if ((pageY > (viewBoundaries.bottom - autoScrollDistance) + window.pageYOffset) &&
            (pageY < viewBoundaries.bottom + window.pageYOffset)) {
            dragEdges.bottom = true;
        }
        if ((pageX < viewBoundaries.left + autoScrollDistance + window.pageXOffset) &&
            (pageX > viewBoundaries.left + window.pageXOffset)) {
            dragEdges.left = true;
        }
        if ((pageX > (viewBoundaries.right - autoScrollDistance) + window.pageXOffset) &&
            (pageX < viewBoundaries.right + window.pageXOffset)) {
            dragEdges.right = true;
        }
        this.dragEdges = dragEdges;
    }
    autoScroll() {
        this.autoScrollValidation();
        const scrollSensitivity = 30;
        if (this.kanbanObj.isAdaptive) {
            let parent;
            if (this.dragEdges.top || this.dragEdges.bottom) {
                if (this.dragObj.targetClone) {
                    parent = closest(this.dragObj.targetClone, '.' + CARD_WRAPPER_CLASS);
                }
                else {
                    parent = closest(this.dragObj.draggedClone, '.' + CARD_WRAPPER_CLASS);
                }
            }
            else if (this.dragEdges.right || this.dragEdges.left) {
                parent = this.kanbanObj.element.querySelector('.' + CONTENT_CLASS);
            }
            if (parent) {
                const yIsScrollable = parent.offsetHeight <= parent.scrollHeight;
                const xIsScrollable = parent.offsetWidth <= parent.scrollWidth;
                const yInBounds = parent.scrollTop >= 0 && parent.scrollTop + parent.offsetHeight <= parent.scrollHeight;
                const xInBounds = parent.scrollLeft >= 0 && parent.scrollLeft + parent.offsetWidth <= parent.scrollWidth;
                if (yIsScrollable && yInBounds && (this.dragEdges.top || this.dragEdges.bottom)) {
                    parent.scrollTop += this.dragEdges.top ? -(scrollSensitivity + 36) : scrollSensitivity;
                }
                if (xIsScrollable && xInBounds && (this.dragEdges.left || this.dragEdges.right)) {
                    const width = this.parent.enableVirtualization ? this.kanbanObj.virtualLayoutModule.getWidth() :
                        this.kanbanObj.layoutModule.getWidth();
                    const scroll = (width * (this.kanbanObj.columns.length - 1)) >
                        parent.scrollLeft;
                    if (scroll || this.dragEdges.left) {
                        parent.scrollLeft += this.dragEdges.left ? -scrollSensitivity : scrollSensitivity;
                    }
                }
            }
        }
        else {
            const parent = this.kanbanObj.element.querySelector('.' + CONTENT_CLASS);
            const column = this.dragObj.targetClone.parentElement;
            const yScrollable = parent.offsetHeight <= parent.scrollHeight;
            const xScrollable = parent.offsetWidth <= parent.scrollWidth;
            const yBounds = yScrollable && parent.scrollTop >= 0 && parent.scrollTop + parent.offsetHeight <= parent.scrollHeight;
            const xBounds = xScrollable && parent.scrollLeft >= 0 && parent.scrollLeft + parent.offsetWidth <= parent.scrollWidth;
            if (yBounds && (this.dragEdges.top || this.dragEdges.bottom)) {
                parent.scrollTop += this.dragEdges.top ? -scrollSensitivity : scrollSensitivity;
                if (this.parent.swimlaneSettings.enableFrozenRows) {
                    this.dragObj.cloneElement.style.top = !this.dragEdges.top ? (parseInt(this.dragObj.cloneElement.style.top, 10) + scrollSensitivity) + 'px' : (parseInt(this.dragObj.cloneElement.style.top, 10) - scrollSensitivity) + 'px';
                }
                if (column) {
                    column.scrollTop += this.dragEdges.top ? -scrollSensitivity : scrollSensitivity;
                }
            }
            if (xBounds && (this.dragEdges.left || this.dragEdges.right)) {
                parent.scrollLeft += this.dragEdges.left ? -scrollSensitivity : scrollSensitivity;
                if (column) {
                    column.scrollLeft += this.dragEdges.left ? -scrollSensitivity : scrollSensitivity;
                }
            }
            if (this.dragObj.pageY - window.scrollY < scrollSensitivity) {
                window.scrollTo(window.scrollX, window.scrollY - scrollSensitivity);
            }
            else if (window.innerHeight - (this.dragObj.pageY - window.scrollY) < scrollSensitivity) {
                window.scrollTo(window.scrollX, window.scrollY + scrollSensitivity);
            }
        }
        const x = this.dragObj.pageX - window.scrollX;
        const scrollStep = 30;
        // Near left edge?
        if (x < scrollSensitivity) {
            window.scrollTo(window.scrollX - scrollStep, window.scrollY);
        }
        // Near right edge?
        else if (window.innerWidth - x < scrollSensitivity) {
            window.scrollTo(window.scrollX + scrollStep, window.scrollY);
        }
    }
    unWireDragEvents(element) {
        if (!isNullOrUndefined(element) && !isNullOrUndefined(element.ej2_instances[0])) {
            const dragInstance = element.ej2_instances[0];
            if (dragInstance && !dragInstance.isDestroyed) {
                dragInstance.destroy();
            }
        }
    }
}

/* eslint-disable @typescript-eslint/no-explicit-any */
/**
 * ColumnDragAndDrop module is used to perform column reordering actions.
 */
class ColumnDragAndDrop {
    /**
     * Constructor for column drag and drop module
     *
     * @param {Kanban} parent Accepts the kanban instance
     * @private
     */
    constructor(parent) {
        this.parent = parent;
        this.isDragging = false;
    }
    wireColumnDragEvents(headerCell) {
        this.dragArea = this.parent.element;
        this.dragInstance = new Draggable(headerCell, {
            clone: true,
            dragArea: this.dragArea,
            dragStart: this.columnDragStart.bind(this),
            drag: this.columnDrag.bind(this),
            dragStop: this.columnDragStop.bind(this),
            helper: this.columnDragHelper.bind(this),
            enableTapHold: this.parent.isAdaptive
        });
        if (!this.dropIndicator) {
            this.dropIndicator = createElement('div', {
                className: 'e-kanban-column-indicator'
            });
            document.body.appendChild(this.dropIndicator);
        }
    }
    columnDragHelper(e) {
        this.draggedColumn = closest(e.sender.target, '.' + HEADER_CELLS_CLASS);
        if (isNullOrUndefined(this.draggedColumn)) {
            return null;
        }
        this.isStackedHeader = this.draggedColumn.classList.contains(STACKED_HEADER_CELL_CLASS);
        if (this.isStackedHeader) {
            const stackedHeaderIndex = this.getColumnIndex(this.draggedColumn);
            const stackedHeader = this.parent.stackedHeaders[stackedHeaderIndex];
            const keyFields = stackedHeader.keyFields.split(',').map((field) => field.trim());
            this.contentCells = [];
            keyFields.forEach((key) => {
                const cellsForKey = Array.from(this.parent.element.querySelectorAll('.e-content-cells[data-key="' + key + '"]'));
                this.contentCells.push(...cellsForKey);
            });
        }
        else {
            const columnKey = this.draggedColumn.getAttribute('data-key');
            this.contentCells = Array.from(this.parent.element.querySelectorAll('.e-content-cells[data-key="' + columnKey + '"]'));
        }
        addClass([this.draggedColumn], 'e-kanban-dragging-header');
        this.draggedIndex = this.getColumnIndex(this.draggedColumn);
        if (!this.skeletonElement) {
            this.skeletonElement = createElement('div', {
                className: 'e-kanban-column-skeleton'
            });
            document.body.appendChild(this.skeletonElement);
        }
        this.createColumnSkeleton();
        return this.skeletonElement;
    }
    columnDragStart(e) {
        if (!this.parent.allowColumnDragAndDrop) {
            return;
        }
        this.isDragging = true;
        const dragStartArgs = {
            cancel: false,
            event: e,
            element: this.draggedColumn,
            fromIndex: this.draggedIndex,
            column: this.isStackedHeader ? this.parent.stackedHeaders[this.draggedIndex] : this.parent.columns[this.draggedIndex]
        };
        this.parent.trigger(columnDragStart, dragStartArgs);
        if (dragStartArgs.cancel) {
            this.resetDragState();
            this.dragInstance.intDestroy(e);
            return;
        }
        addClass([this.draggedColumn], 'e-dragged-header');
        this.contentCells.forEach((cell) => {
            addClass([cell], 'e-dragged-content');
        });
        this.showColumnSkeleton(e);
    }
    columnDrag(e) {
        if (!this.isDragging) {
            return;
        }
        const targetHeader = this.findTargetHeader(e);
        if (!targetHeader) {
            this.dropIndicator.style.display = 'none';
            return;
        }
        const draggedColumnKey = this.draggedColumn.getAttribute('data-key');
        const targetColumnKey = targetHeader.getAttribute('data-key');
        const draggedParentHeader = this.getParentStackedHeader(draggedColumnKey);
        const targetParentHeader = this.getParentStackedHeader(targetColumnKey);
        if (!this.isStackedHeader && draggedParentHeader) {
            if (!targetParentHeader) {
                this.dropIndicator.style.display = 'none';
                return;
            }
            else if (draggedParentHeader.keyFields !== targetParentHeader.keyFields) {
                this.dropIndicator.style.display = 'none';
                return;
            }
        }
        if (!this.isStackedHeader && draggedParentHeader && targetParentHeader) {
            if (draggedParentHeader.keyFields !== targetParentHeader.keyFields) {
                this.dropIndicator.style.display = 'none';
                return;
            }
        }
        this.targetIndex = this.getColumnIndex(targetHeader);
        const rect = targetHeader.getBoundingClientRect();
        const dragArgs = {
            event: e,
            element: this.draggedColumn,
            fromIndex: this.draggedIndex,
            toIndex: this.targetIndex,
            column: this.isStackedHeader ? this.parent.stackedHeaders[this.draggedIndex] : this.parent.columns[this.draggedIndex]
        };
        this.parent.trigger(columnDrag, dragArgs);
        if (dragArgs.cancel) {
            this.resetDragState();
            return;
        }
        this.dropIndicator.style.display = 'block';
        this.dropIndicator.style.left = (rect.left + window.pageXOffset) + 'px';
        this.dropIndicator.style.top = (rect.top + window.pageYOffset) + 'px';
        this.dropIndicator.style.height = rect.height + 'px';
    }
    columnDragStop(e) {
        if (!this.isDragging) {
            return;
        }
        const targetHeader = this.findTargetHeader(e);
        if (!targetHeader) {
            this.resetDragState();
            return;
        }
        const draggedColumnKey = this.draggedColumn.getAttribute('data-key');
        const targetColumnKey = targetHeader.getAttribute('data-key');
        const draggedParentHeader = this.getParentStackedHeader(draggedColumnKey);
        const targetParentHeader = this.getParentStackedHeader(targetColumnKey);
        if (!this.isStackedHeader && draggedParentHeader) {
            if (!targetParentHeader) {
                this.resetDragState();
                return;
            }
            else if (draggedParentHeader.keyFields !== targetParentHeader.keyFields) {
                this.resetDragState();
                return;
            }
        }
        if (!this.isStackedHeader && draggedParentHeader && targetParentHeader) {
            if (draggedParentHeader.keyFields !== targetParentHeader.keyFields) {
                this.resetDragState();
                return;
            }
        }
        const targetIndex = this.getColumnIndex(targetHeader);
        const dropIndex = targetIndex;
        const dropArgs = {
            cancel: false,
            event: e,
            element: this.draggedColumn,
            fromIndex: this.draggedIndex,
            toIndex: targetIndex,
            dropIndex: dropIndex,
            column: this.isStackedHeader ? this.parent.stackedHeaders[this.draggedIndex] : this.parent.columns[this.draggedIndex]
        };
        this.parent.trigger(columnDrop, dropArgs);
        if (!dropArgs.cancel && dropIndex !== this.draggedIndex) {
            if (this.isStackedHeader) {
                this.reorderStackedHeaders(this.draggedIndex, targetIndex);
            }
            else {
                this.reorderColumns(this.draggedIndex, targetIndex);
            }
        }
        this.resetDragState();
    }
    createColumnSkeleton() {
        if (!this.skeletonElement || !this.draggedColumn) {
            return;
        }
        this.skeletonElement.innerHTML = '';
        this.skeletonElement.style.display = 'block';
        this.skeletonElement.style.position = 'absolute';
        this.skeletonElement.style.zIndex = '999';
        this.skeletonElement.style.width = formatUnit(this.draggedColumn.offsetWidth);
        // Clone header
        const headerClone = this.draggedColumn.cloneNode(true);
        headerClone.classList.add('e-kanban-skeleton-header');
        this.skeletonElement.appendChild(headerClone);
        // Get the column key
        const columnKey = this.draggedColumn.getAttribute('data-key');
        const calculatedHeigt = this.parent.element.getBoundingClientRect().height;
        // Create content wrapper
        const contentWrapper = createElement('div', {
            className: 'e-kanban-skeleton-content'
        });
        const availableSpace = calculatedHeigt - 16; // 16px for padding
        if (this.isStackedHeader) {
            const cardHeight = this.contentCells[0].childNodes[1].firstChild
                ? this.contentCells[0].childNodes[1].firstChild.offsetHeight : 0;
            const numCards = cardHeight === 0 ? 0 : Math.floor(calculatedHeigt / (cardHeight));
            for (let i = 0; i < numCards; i++) {
                const placeholderCard = createElement('div', {
                    className: 'e-kanban-skeleton-card',
                    styles: 'height: ' + cardHeight + 'px;'
                });
                contentWrapper.appendChild(placeholderCard);
            }
        }
        else {
            const columnModel = this.parent.columns.find((col) => col.keyField.toString() === columnKey);
            const isCollapsed = columnModel && columnModel.allowToggle && !columnModel.isExpanded;
            if (isCollapsed) {
                // For collapsed columns, create a single placeholder with full content height
                const emptyContainer = createElement('div', {
                    className: 'e-kanban-skeleton-empty-content',
                    styles: 'height: ' + availableSpace + 'px;'
                });
                contentWrapper.appendChild(emptyContainer);
            }
            else {
                // Get the first real card height
                const firstCard = this.parent.element.querySelector('.e-content-cells[data-key="' + columnKey + '"] .e-card');
                let cardHeight;
                if (firstCard && firstCard.offsetHeight) {
                    cardHeight = Math.min(150, firstCard.offsetHeight); // Max 150px
                }
                const numCards = firstCard === null ? 0 : Math.floor(calculatedHeigt / (cardHeight));
                for (let i = 0; i < numCards; i++) {
                    const placeholderCard = createElement('div', {
                        className: 'e-kanban-skeleton-card',
                        styles: 'height: ' + cardHeight + 'px;'
                    });
                    contentWrapper.appendChild(placeholderCard);
                }
            }
        }
        // Explicitly set the height of the content wrapper to match contentTd height
        contentWrapper.style.height = calculatedHeigt + 'px';
        // Add content wrapper to skeleton
        this.skeletonElement.appendChild(contentWrapper);
        // Set skeleton height to match header + content
        this.skeletonElement.style.height = calculatedHeigt + 'px';
    }
    showColumnSkeleton(e) {
        if (!this.skeletonElement) {
            return;
        }
        if (this.draggedColumn) {
            const headerElement = closest(this.draggedColumn, '.e-kanban-header');
            if (headerElement && this.skeletonElement.parentElement !== headerElement) {
                headerElement.appendChild(this.skeletonElement);
            }
        }
        this.skeletonElement.style.display = 'block';
    }
    findTargetHeader(e) {
        const target = e.target;
        if (this.isStackedHeader) {
            const stackedHeader = closest(target, '.' + STACKED_HEADER_CELL_CLASS);
            if (stackedHeader && stackedHeader !== this.draggedColumn) {
                return stackedHeader;
            }
        }
        else {
            const headerCell = closest(target, '.' + HEADER_CELLS_CLASS);
            if (headerCell && headerCell !== this.draggedColumn) {
                return headerCell;
            }
        }
        return null;
    }
    getParentStackedHeader(columnKey) {
        if (!this.parent.stackedHeaders || this.parent.stackedHeaders.length === 0) {
            return null;
        }
        return this.parent.stackedHeaders.find((header) => {
            const keyFields = header.keyFields.split(',').map((key) => key.trim());
            return keyFields.indexOf(columnKey) !== -1;
        });
    }
    getColumnIndex(headerCell) {
        return Array.from(headerCell.parentElement.children).indexOf(headerCell);
    }
    isColumnVisible(column) {
        return this.parent.enableVirtualization
            ? this.parent.virtualLayoutModule.isColumnVisible(column)
            : this.parent.layoutModule.isColumnVisible(column);
    }
    getVisibleColumns(columns) {
        return columns
            .map((column, index) => ({ column, index }))
            .filter(({ column }) => this.isColumnVisible(column));
    }
    reorderColumns(fromIndex, toIndex) {
        const visibleColumns = this.getVisibleColumns(this.parent.columns);
        const sourceIndex = visibleColumns[fromIndex].index;
        const targetIndex = visibleColumns[toIndex].index;
        const columns = this.parent.columns.slice();
        const column = columns.splice(sourceIndex, 1)[0];
        columns.splice(targetIndex, 0, column);
        this.parent.columns = columns;
        if (this.parent.enableVirtualization) {
            this.parent.virtualLayoutModule.refresh();
        }
        else {
            this.parent.layoutModule.refresh();
        }
    }
    reorderStackedHeaders(sourceIndex, targetIndex) {
        const stackedHeadersList = this.parent.stackedHeaders.slice();
        const movingStackedHeader = stackedHeadersList.splice(sourceIndex, 1)[0];
        stackedHeadersList.splice(targetIndex, 0, movingStackedHeader);
        this.parent.stackedHeaders = stackedHeadersList;
        const headerToColumnsMap = new Map();
        stackedHeadersList.forEach((header) => {
            const keyFields = header.keyFields.split(',').map((key) => key.trim());
            headerToColumnsMap.set(header.keyFields, []);
            keyFields.forEach((keyField) => {
                const columnIndex = this.parent.columns.findIndex((column) => column.keyField.toString() === keyField);
                if (columnIndex !== -1) {
                    headerToColumnsMap.get(header.keyFields).push({
                        column: this.parent.columns[columnIndex],
                        index: columnIndex
                    });
                }
            });
        });
        const newColumnsArray = [];
        stackedHeadersList.forEach((header) => {
            const columnsForHeader = headerToColumnsMap.get(header.keyFields) || [];
            columnsForHeader.sort((a, b) => {
                return a.index - b.index;
            });
            columnsForHeader.forEach((item) => {
                newColumnsArray.push(item.column);
            });
        });
        this.parent.columns.forEach((column) => {
            const isInStackedHeader = stackedHeadersList.some((header) => {
                const keyFields = header.keyFields.split(',').map((key) => key.trim());
                return keyFields.indexOf(column.keyField.toString()) !== -1;
            });
            if (!isInStackedHeader && newColumnsArray.indexOf(column) === -1) {
                newColumnsArray.push(column);
            }
        });
        this.parent.columns = newColumnsArray;
        if (this.parent.enableVirtualization) {
            this.parent.virtualLayoutModule.refresh();
        }
        else {
            this.parent.layoutModule.refresh();
        }
    }
    resetDragState() {
        this.isDragging = false;
        if (this.dropIndicator) {
            this.dropIndicator.style.display = 'none';
        }
        if (this.skeletonElement) {
            this.skeletonElement.style.display = 'none';
        }
        if (this.draggedColumn) {
            removeClass([this.draggedColumn], ['e-kanban-dragging-header', 'e-dragged-header']);
        }
        if (this.contentCells && this.contentCells.length) {
            removeClass(this.contentCells, 'e-dragged-content');
        }
        this.draggedColumn = null;
        this.contentCells = null;
        this.isStackedHeader = false;
    }
    unwireColumnDragEvents(headerCell) {
        if (!isNullOrUndefined(headerCell)) {
            const ej2Instances = headerCell.ej2_instances;
            if (!isNullOrUndefined(ej2Instances) && ej2Instances.length > 0) {
                const dragInstance = ej2Instances[0];
                if (dragInstance && !dragInstance.isDestroyed) {
                    dragInstance.destroy();
                }
            }
        }
        // Remove drop indicator if this is the last header being unwired
        if (this.dropIndicator && this.dropIndicator.parentElement) {
            remove(this.dropIndicator);
            this.dropIndicator = null;
        }
        this.resetDragState();
    }
}

/* eslint-disable @typescript-eslint/no-explicit-any */
/**
 * Dialog module is used to perform card actions.
 */
class KanbanDialog {
    /**
     * Constructor for dialog module
     *
     * @param {Kanban} parent Accepts the kanban instance
     */
    constructor(parent) {
        this.preventUpdate = false;
        this.parent = parent;
    }
    openDialog(action, data) {
        this.action = action;
        this.parent.activeCardData.data = data;
        this.renderDialog(data, action);
        this.dialogObj.show();
    }
    closeDialog() {
        this.dialogObj.hide();
    }
    renderDialog(args, action) {
        this.element = createElement('div', { id: this.parent.element.id + '_dialog_wrapper' });
        this.parent.element.appendChild(this.element);
        const dialogModel = {
            buttons: this.getDialogButtons(action),
            content: this.getDialogContent(args, action),
            cssClass: DIALOG_CLASS,
            enableRtl: this.parent.enableRtl,
            header: this.parent.localeObj.getConstant(action === 'Add' ? 'addTitle' : action === 'Edit' ? 'editTitle' : 'deleteTitle'),
            height: 'auto',
            isModal: true,
            showCloseIcon: true,
            width: (action === 'Delete') ? 400 : 350,
            visible: false,
            beforeOpen: this.onBeforeDialogOpen.bind(this),
            beforeClose: this.onBeforeDialogClose.bind(this)
        };
        this.dialogObj = new Dialog(extend(dialogModel, action !== 'Delete' ? (this.parent.dialogSettings.model || {}) : {}), this.element);
        if (action !== 'Delete') {
            this.applyFormValidation();
        }
        this.dialogObj.element.querySelector('.e-dlg-closeicon-btn').title = this.parent.localeObj.getConstant('close');
    }
    getDialogContent(args, action) {
        if (action === 'Delete') {
            return this.parent.localeObj.getConstant('deleteContent');
        }
        else {
            const container = createElement('div', { className: FORM_WRAPPER_CLASS });
            const form = createElement('form', {
                id: this.parent.element.id + 'EditForm',
                className: FORM_CLASS, attrs: { onsubmit: 'return false;' }
            });
            if (this.parent.dialogSettings.template) {
                if (args) {
                    this.destroyComponents();
                    [].slice.call(form.childNodes).forEach((node) => remove(node));
                }
                const templateId = this.parent.element.id + '_dialogTemplate';
                const dialogTemplate = this.parent.templateParser(this.parent.dialogSettings.template)(args, this.parent, 'dialogTemplate', templateId, false);
                append(dialogTemplate, form);
                this.parent.renderTemplates();
            }
            else {
                const dialogWrapper = createElement('div', { className: DIALOG_CONTENT_CONTAINER });
                form.appendChild(dialogWrapper);
                const table = createElement('table');
                dialogWrapper.appendChild(table);
                const dialogFields = this.getDialogFields();
                for (const field of dialogFields) {
                    const tr = createElement('tr');
                    table.appendChild(tr);
                    tr.appendChild(createElement('td', { className: 'e-label', innerHTML: field.text ? field.text : field.key }));
                    const td = createElement('td');
                    tr.appendChild(td);
                    td.appendChild(this.renderComponents(field));
                }
            }
            container.appendChild(form);
            return container;
        }
    }
    getDialogFields() {
        let fields = this.parent.dialogSettings.fields;
        if (fields.length === 0) {
            fields = [
                { text: 'ID', key: this.parent.cardSettings.headerField, type: 'TextBox' },
                { key: this.parent.keyField, type: 'DropDown' },
                { key: this.parent.cardSettings.contentField, type: 'TextArea' }
            ];
            if (this.parent.sortSettings.field) {
                fields.splice(fields.length - 1, 0, { key: this.parent.sortSettings.field, type: 'TextBox' });
            }
            if (this.parent.swimlaneSettings.keyField) {
                fields.splice(fields.length - 1, 0, { key: this.parent.swimlaneSettings.keyField, type: 'DropDown' });
            }
        }
        return fields;
    }
    getDialogButtons(action) {
        const primaryButtonClass = action === 'Delete' ? 'e-dialog-yes' : action === 'Add' ? 'e-dialog-add' : 'e-dialog-edit';
        const flatButtonClass = action === 'Delete' ? 'e-dialog-no' : 'e-dialog-cancel';
        const dialogButtons = [
            {
                buttonModel: {
                    cssClass: 'e-flat ' + primaryButtonClass, isPrimary: true,
                    content: this.parent.localeObj.getConstant(action === 'Add' || action === 'Edit' ? 'save' : 'yes')
                },
                click: this.dialogButtonClick.bind(this)
            }, {
                buttonModel: {
                    cssClass: 'e-flat ' + flatButtonClass, isPrimary: false,
                    content: this.parent.localeObj.getConstant(action === 'Add' || action === 'Edit' ? 'cancel' : 'no')
                },
                click: this.dialogButtonClick.bind(this)
            }
        ];
        if (action === 'Edit') {
            const deleteButton = {
                buttonModel: { cssClass: 'e-flat e-dialog-delete', isPrimary: false, content: this.parent.localeObj.getConstant('delete') },
                click: this.dialogButtonClick.bind(this)
            };
            dialogButtons.splice(0, 0, deleteButton);
        }
        return dialogButtons;
    }
    renderComponents(field) {
        const wrapper = createElement('div', { className: field.key + '_wrapper' });
        let element = createElement('input', { className: FIELD_CLASS, attrs: { 'name': field.key } });
        wrapper.appendChild(element);
        let divElement;
        let dropDownOptions;
        let controlObj;
        const fieldValue = this.parent.activeCardData.data ?
            this.parent.activeCardData.data[field.key] : null;
        switch (field.type) {
            case 'DropDown':
                if (field.key === this.parent.keyField) {
                    let currentKeys = this.parent.enableVirtualization ?
                        this.parent.virtualLayoutModule.columnKeys : this.parent.layoutModule.columnKeys;
                    if (this.parent.actionModule.hideColumnKeys.length > 0) {
                        currentKeys = [];
                        for (let i = 0; i < this.parent.columns.length; i++) {
                            const isColumnVisible = this.parent.enableVirtualization ?
                                this.parent.virtualLayoutModule.isColumnVisible(this.parent.columns[i]) :
                                this.parent.layoutModule.isColumnVisible(this.parent.columns[i]);
                            if (isColumnVisible) {
                                const isNumeric = typeof this.parent.columns[i].keyField === 'number';
                                if (isNumeric) {
                                    currentKeys = currentKeys.concat(this.parent.columns[i].keyField.toString());
                                }
                                else {
                                    currentKeys = currentKeys.concat(this.parent.columns[i].keyField.split(',').map((e) => e.trim()));
                                }
                            }
                        }
                    }
                    dropDownOptions = { dataSource: currentKeys, value: fieldValue ? fieldValue.toString() : fieldValue };
                }
                else if (field.key === this.parent.swimlaneSettings.keyField) {
                    dropDownOptions = {
                        dataSource: [].slice.call(this.parent.enableVirtualization ? this.parent.virtualLayoutModule.kanbanRows :
                            this.parent.layoutModule.kanbanRows),
                        fields: { text: 'textField', value: 'keyField' }, value: fieldValue
                    };
                }
                controlObj = new DropDownList(dropDownOptions);
                break;
            case 'Numeric':
                controlObj = new NumericTextBox({ value: fieldValue });
                break;
            case 'TextBox':
                controlObj = new TextBox({ value: fieldValue });
                if (fieldValue && this.parent.cardSettings.headerField === field.key) {
                    controlObj.enabled = false;
                }
                break;
            case 'TextArea':
                remove(element);
                divElement = createElement('div');
                element = createElement('textarea', {
                    className: FIELD_CLASS, attrs: { 'name': field.key, 'rows': '3', 'aria-label': this.parent.cardSettings.contentField },
                    innerHTML: fieldValue
                });
                wrapper.appendChild(divElement).appendChild(element);
                break;
        }
        if (controlObj) {
            controlObj.appendTo(element);
        }
        return wrapper;
    }
    onBeforeDialogOpen(args) {
        const eventProp = {
            data: this.parent.activeCardData.data,
            cancel: false, element: this.element,
            target: this.parent.activeCardData.element,
            requestType: this.action
        };
        this.storeElement = document.activeElement;
        if (parseInt(args.maxHeight, 10) <= 250) {
            args.maxHeight = '250px';
        }
        this.parent.trigger(dialogOpen, eventProp, (openArgs) => {
            args.cancel = openArgs.cancel;
            if (openArgs.cancel) {
                this.destroy();
            }
        });
    }
    onBeforeDialogClose(args) {
        const formInputs = this.getFormElements();
        let cardObj = {};
        if (args.isInteracted) {
            /* close icon preventing data update */
            this.preventUpdate = true;
        }
        if (!this.preventUpdate) {
            for (const input of formInputs) {
                const columnName = input.name || this.getColumnName(input);
                if (!isNullOrUndefined(columnName) && columnName !== '') {
                    let value = this.getValueFromElement(input);
                    if (columnName === this.parent.cardSettings.headerField) {
                        value = this.getIDType() === 'string' ? value : parseInt(value, 10);
                    }
                    cardObj[columnName] = value;
                }
            }
        }
        this.preventUpdate = false;
        cardObj = extend(this.parent.activeCardData.data, cardObj);
        const eventProp = { data: cardObj, cancel: false, element: this.element, requestType: this.action };
        this.parent.trigger(dialogClose, eventProp, (closeArgs) => {
            args.cancel = closeArgs.cancel;
            if (!closeArgs.cancel) {
                this.cardData = eventProp.data;
                this.destroy();
                this.parent.actionModule.SingleCardSelection(this.cardData);
            }
        });
    }
    getIDType() {
        if (this.parent.kanbanData.length !== 0) {
            return typeof (this.parent.kanbanData[0][this.parent.cardSettings.headerField]);
        }
        return 'string';
    }
    applyFormValidation() {
        const form = this.element.querySelector('.' + FORM_CLASS);
        const rules = {};
        for (const field of this.parent.dialogSettings.fields) {
            rules[field.key] = (field.validationRules && Object.keys(field.validationRules).length > 0) ? field.validationRules : null;
        }
        this.formObj = new FormValidator(form, {
            rules: rules,
            customPlacement: (inputElement, error) => {
                const id = error.getAttribute('for');
                const elem = this.element.querySelector('#' + id + '_Error');
                if (!elem) {
                    this.createTooltip(inputElement, error, id, '');
                }
            },
            validationComplete: (args) => {
                const elem = this.element.querySelector('#' + args.inputName + '_Error');
                if (elem) {
                    elem.style.display = (args.status === 'failure') ? '' : 'none';
                }
            }
        });
    }
    createTooltip(element, error, name, display) {
        let dlgContent;
        let client;
        const inputClient = element.parentElement.getBoundingClientRect();
        if (this.element.classList.contains(DIALOG_CLASS)) {
            dlgContent = this.element;
            client = this.element.getBoundingClientRect();
        }
        else {
            dlgContent = this.element.querySelector('.e-kanban-dialog .e-dlg-content');
            client = dlgContent.getBoundingClientRect();
        }
        const div = createElement('div', {
            className: 'e-tooltip-wrap e-popup ' + ERROR_VALIDATION_CLASS,
            id: name + '_Error',
            styles: 'display:' + display + ';top:' +
                (inputClient.bottom - client.top + dlgContent.scrollTop + 9) + 'px;left:' +
                (inputClient.left - client.left + dlgContent.scrollLeft + inputClient.width / 2) + 'px;'
        });
        const content = createElement('div', { className: 'e-tip-content' });
        content.appendChild(error);
        const arrow = createElement('div', { className: 'e-arrow-tip e-tip-top' });
        arrow.appendChild(createElement('div', { className: 'e-arrow-tip-outer e-tip-top' }));
        arrow.appendChild(createElement('div', { className: 'e-arrow-tip-inner e-tip-top' }));
        div.appendChild(content);
        div.appendChild(arrow);
        dlgContent.appendChild(div);
        div.style.left = (parseInt(div.style.left, 10) - div.offsetWidth / 2) + 'px';
    }
    destroyToolTip() {
        if (this.element) {
            this.element.querySelectorAll('.' + ERROR_VALIDATION_CLASS).forEach((node) => remove(node));
        }
        if (this.formObj && this.formObj.element) {
            this.formObj.reset();
        }
    }
    dialogButtonClick(event) {
        let target = event.target.cloneNode(true);
        if (!isNullOrUndefined(event.keyCode) && event.keyCode === 13) {
            const valTrg = this.dialogObj.element.querySelector('.e-footer-content button.e-primary');
            target = valTrg.cloneNode(true);
        }
        const id = this.formObj.element.id;
        if (document.getElementById(id) && this.formObj.validate() &&
            (target.classList.contains('e-dialog-edit') || target.classList.contains('e-dialog-add'))) {
            this.dialogObj.hide();
            if (!isNullOrUndefined(this.cardData)) {
                if (target.classList.contains('e-dialog-edit')) {
                    const activeCard = this.parent.activeCardData;
                    let updateIndex;
                    if (activeCard.data[this.parent.keyField] === this.cardData[this.parent.keyField]
                        && activeCard.element) {
                        updateIndex = [].slice.call(activeCard.element.parentElement.children).indexOf(activeCard.element);
                    }
                    if (this.parent.enableHtmlSanitizer) {
                        if (typeof this.cardData[this.parent.cardSettings.contentField] === 'string') {
                            this.cardData[this.parent.cardSettings.contentField] =
                                SanitizeHtmlHelper.sanitize(this.cardData[this.parent.cardSettings.contentField]);
                        }
                    }
                    this.parent.crudModule.updateCard(this.cardData, updateIndex);
                }
                if (target.classList.contains('e-dialog-add')) {
                    this.parent.crudModule.addCard(this.cardData);
                }
                this.parent.actionModule.SingleCardSelection(this.cardData);
                this.cardData = null;
            }
        }
        if (!target.classList.contains('e-dialog-edit') && !target.classList.contains('e-dialog-add')) {
            if (target.classList.contains('e-dialog-cancel')) {
                this.preventUpdate = true;
            }
            this.dialogObj.hide();
            if (target.classList.contains('e-dialog-yes')) {
                this.parent.crudModule.deleteCard(this.parent.activeCardData.data);
            }
            else if (target.classList.contains('e-dialog-no')) {
                this.openDialog('Edit', this.parent.activeCardData.data);
            }
            else if (target.classList.contains('e-dialog-delete')) {
                this.openDialog('Delete', this.parent.activeCardData.data);
            }
        }
    }
    getFormElements() {
        const elements = [].slice.call(this.element.querySelectorAll('.' + FIELD_CLASS));
        const validElements = [];
        for (const element of elements) {
            if (element.classList.contains('e-control')) {
                validElements.push(element);
            }
            else if (element.querySelector('.e-control')) {
                validElements.push(element.querySelector('.e-control'));
            }
            else {
                validElements.push(element);
            }
        }
        return validElements;
    }
    getColumnName(element) {
        let attrName = element.getAttribute('data-name') || '';
        if (attrName === '') {
            let isDropDowns = false;
            let fieldSelector = '';
            if (element.classList.contains('e-dropdownlist') || element.classList.contains('e-multiselect')) {
                fieldSelector = element.classList.contains('e-dropdownlist') ? 'e-ddl' : 'e-multiselect';
                isDropDowns = true;
            }
            else if (element.classList.contains('e-numerictextbox')) {
                fieldSelector = 'e-numeric';
            }
            const classSelector = isDropDowns ? `.${fieldSelector}:not(.e-control)` : `.${fieldSelector}`;
            const control = closest(element, classSelector) || element.querySelector(`.${fieldSelector}`);
            if (control) {
                const attrEle = control.querySelector('[name]');
                if (attrEle) {
                    attrName = attrEle.name;
                }
            }
        }
        return attrName;
    }
    getValueFromElement(element) {
        let value;
        if (element.classList.contains('e-dropdownlist')) {
            value = element.ej2_instances[0].value;
        }
        else if (element.classList.contains('e-multiselect')) {
            value = element.ej2_instances[0].value;
        }
        else if (element.classList.contains('e-checkbox')) {
            value = element.ej2_instances[0].checked;
        }
        else {
            if (element.type === 'checkbox') {
                value = element.checked;
            }
            else {
                value = element.value;
            }
        }
        return value;
    }
    destroyComponents() {
        const formelement = this.getFormElements();
        for (const element of formelement) {
            const instance = element.ej2_instances;
            if (instance && instance.length > 0) {
                instance.forEach((node) => node.destroy());
            }
            if (this.parent.isReact && this.formObj) {
                this.formObj.element.remove();
            }
        }
    }
    destroy() {
        this.destroyToolTip();
        this.destroyComponents();
        if (this.dialogObj) {
            this.dialogObj.destroy();
            this.storeElement.focus();
            this.dialogObj = null;
            remove(this.element);
            this.element = null;
        }
    }
}

/* eslint-disable @typescript-eslint/no-explicit-any */
/**
 * Kanban keyboard module
 */
class Keyboard {
    /**
     * Constructor for keyboard module
     *
     * @param {Kanban} parent Accepts the kanban instance
     */
    constructor(parent) {
        this.keyConfigs = {
            firstCardSelection: '36',
            lastCardSelection: '35',
            upArrow: '38',
            downArrow: '40',
            rightArrow: '39',
            leftArrow: '37',
            multiSelectionEnter: 'ctrl+13',
            multiSelectionSpace: 'ctrl+32',
            multiSelectionByUpArrow: 'shift+38',
            multiSelectionByDownArrow: 'shift+40',
            shiftTab: 'shift+tab',
            enter: '13',
            tab: 'tab',
            delete: '46',
            escape: '27',
            space: '32'
        };
        this.parent = parent;
        this.parent.element.tabIndex = this.parent.element.tabIndex === -1 ? 0 : this.parent.element.tabIndex;
        this.keyboardModule = new KeyboardEvents(this.parent.element, {
            keyAction: this.keyActionHandler.bind(this),
            keyConfigs: this.keyConfigs,
            eventName: 'keydown'
        });
        this.multiSelection = false;
    }
    keyActionHandler(e) {
        const selectedCard = this.parent.element.querySelectorAll(`.${CARD_CLASS}.${CARD_SELECTION_CLASS}`).item(0);
        if (!selectedCard && !closest(document.activeElement, `.${ROOT_CLASS}`)) {
            return;
        }
        let contentCell;
        let selectedCards;
        const selectedCardsData = [];
        switch (e.action) {
            case 'upArrow':
            case 'downArrow':
            case 'multiSelectionByUpArrow':
            case 'multiSelectionByDownArrow':
                e.preventDefault();
                this.processUpDownArrow(e.action, selectedCard);
                break;
            case 'rightArrow':
            case 'leftArrow':
                this.processLeftRightArrow(e);
                break;
            case 'firstCardSelection':
            case 'lastCardSelection':
                this.processCardSelection(e.action, selectedCard);
                break;
            case 'multiSelectionEnter':
            case 'multiSelectionSpace':
                if (document.activeElement) {
                    this.parent.actionModule.cardSelection(document.activeElement, true, false);
                }
                break;
            case 'space':
            case 'enter':
                this.processEnter(e, selectedCard);
                break;
            case 'escape':
                if (document.activeElement.classList.contains(CARD_CLASS) ||
                    document.activeElement.classList.contains(SHOW_ADD_BUTTON)) {
                    if (document.activeElement.classList.contains(CARD_SELECTION_CLASS)) {
                        removeClass([document.activeElement], CARD_SELECTION_CLASS);
                        document.activeElement.focus();
                    }
                    else {
                        const ele = closest(document.activeElement, '.' + CONTENT_CELLS_CLASS);
                        const cards = [].slice.call(ele.querySelectorAll('.' + CARD_CLASS));
                        removeClass(cards, CARD_SELECTION_CLASS);
                        ele.focus();
                        this.cardTabIndexRemove();
                        this.addRemoveTabIndex('Add');
                    }
                }
                break;
            case 'tab':
            case 'shiftTab':
                contentCell = closest(document.activeElement, '.' + CONTENT_CELLS_CLASS);
                if (document.activeElement.classList.contains(CARD_CLASS)) {
                    if (!document.activeElement.nextElementSibling && e.action === 'tab') {
                        e.preventDefault();
                    }
                    if (!document.activeElement.previousElementSibling && contentCell.querySelector('.' + SHOW_ADD_BUTTON)
                        && e.action === 'tab') {
                        addClass([contentCell.querySelector('.' + SHOW_ADD_BUTTON)], SHOW_ADD_FOCUS);
                    }
                }
                if (document.activeElement.classList.contains(SHOW_ADD_BUTTON)) {
                    if ((!contentCell.querySelector('.' + CARD_CLASS) && e.action === 'tab') || e.action === 'shiftTab') {
                        e.preventDefault();
                    }
                }
                if (document.activeElement.classList.contains(ROOT_CLASS)) {
                    this.cardTabIndexRemove();
                    this.parent.keyboardModule.addRemoveTabIndex('Add');
                }
                break;
            case 'delete':
                selectedCards = [].slice.call(this.parent.element.querySelectorAll(`.${CARD_CLASS}.${CARD_SELECTION_CLASS}`));
                selectedCards.forEach((selected) => { selectedCardsData.push(this.parent.getCardDetails(selected)); });
                this.parent.crudModule.deleteCard(selectedCardsData);
                break;
        }
    }
    processCardSelection(action, selectedCard) {
        if (selectedCard) {
            removeClass([selectedCard], CARD_SELECTION_CLASS);
            if (this.parent.enableVirtualization) {
                this.parent.virtualLayoutModule.disableAttributeSelection(selectedCard);
            }
            else {
                this.parent.layoutModule.disableAttributeSelection(selectedCard);
            }
            const selection = this.parent.actionModule.selectionArray;
            selection.splice(selection.indexOf(selectedCard.getAttribute('data-id')), 1);
        }
        this.cardTabIndexRemove();
        const cards = [].slice.call(this.parent.element.querySelectorAll('.' + CARD_CLASS));
        const element = action === 'firstCardSelection' ? cards[0] : cards[cards.length - 1];
        this.parent.actionModule.cardSelection(element, false, false);
        this.addRemoveTabIndex('Remove');
        element.focus();
        const card = [].slice.call(closest(element, '.' + CONTENT_CELLS_CLASS).querySelectorAll('.' + CARD_CLASS));
        card.forEach((element) => { element.setAttribute('tabindex', '0'); });
    }
    processLeftRightArrow(e) {
        if (document.activeElement.classList.contains(CONTENT_CELLS_CLASS)) {
            if (e.action === 'rightArrow' && document.activeElement.nextElementSibling) {
                document.activeElement.nextElementSibling.focus();
            }
            else if (e.action === 'leftArrow' && document.activeElement.previousElementSibling) {
                document.activeElement.previousElementSibling.focus();
            }
        }
    }
    processUpDownArrow(action, selectedCard) {
        if (action === 'upArrow' && document.activeElement) {
            if (document.activeElement.classList.contains(CARD_CLASS) && document.activeElement.previousElementSibling) {
                document.activeElement.previousElementSibling.focus();
            }
            else if (document.activeElement.classList.contains(SHOW_ADD_BUTTON)) {
                document.activeElement.setAttribute('tabindex', '-1');
                removeClass([document.activeElement], SHOW_ADD_FOCUS);
                const cell = closest(document.activeElement, '.' + CONTENT_CELLS_CLASS);
                if (cell.querySelectorAll('.' + CARD_CLASS).length > 0) {
                    [].slice.call(cell.querySelectorAll('.' + CARD_CLASS)).slice(-1)[0].focus();
                }
            }
            this.removeSelection();
        }
        else if (action === 'downArrow' && document.activeElement &&
            document.activeElement.classList.contains(CARD_CLASS)) {
            if (document.activeElement.nextElementSibling) {
                document.activeElement.nextElementSibling.focus();
            }
            else if (closest(document.activeElement, '.' + CARD_WRAPPER_CLASS).nextElementSibling) {
                const ele = closest(document.activeElement, '.' + CARD_WRAPPER_CLASS).nextElementSibling;
                ele.setAttribute('tabindex', '0');
                addClass([ele], SHOW_ADD_FOCUS);
                ele.focus();
            }
            this.removeSelection();
        }
        if ((action === 'multiSelectionByUpArrow' || action === 'multiSelectionByDownArrow')
            && selectedCard && this.parent.cardSettings.selectionType === 'Multiple') {
            let card;
            if (action === 'multiSelectionByUpArrow') {
                card = document.activeElement.previousElementSibling;
            }
            else {
                card = document.activeElement.nextElementSibling;
            }
            if (card) {
                this.parent.actionModule.cardSelection(card, false, true);
                card.focus();
                this.multiSelection = true;
            }
        }
    }
    removeSelection() {
        if (this.multiSelection) {
            const cards = this.parent.getSelectedCards();
            if (cards.length > 0) {
                removeClass(cards, CARD_SELECTION_CLASS);
                if (this.parent.enableVirtualization) {
                    this.parent.virtualLayoutModule.disableAttributeSelection(cards);
                }
                else {
                    this.parent.layoutModule.disableAttributeSelection(cards);
                }
            }
            this.multiSelection = false;
        }
    }
    cardTabIndexRemove() {
        const cards = [].slice.call(this.parent.element.querySelectorAll('.' + CARD_CLASS));
        cards.forEach((card) => { card.setAttribute('tabindex', '-1'); });
        const addButton = [].slice.call(this.parent.element.querySelectorAll('.' + SHOW_ADD_BUTTON));
        addButton.forEach((add) => {
            add.setAttribute('tabindex', '-1');
            removeClass([add], SHOW_ADD_FOCUS);
        });
    }
    processEnter(e, selectedCard) {
        if (e.action === 'space') {
            e.preventDefault();
        }
        const element = (e.target);
        if (element.classList.contains(HEADER_ICON_CLASS)) {
            this.parent.actionModule.columnExpandCollapse(e);
        }
        if (element.classList.contains(SWIMLANE_ROW_EXPAND_CLASS) || element.classList.contains(SWIMLANE_ROW_COLLAPSE_CLASS)) {
            this.parent.actionModule.rowExpandCollapse(e);
        }
        if (document.activeElement.classList.contains(CARD_CLASS)) {
            this.parent.actionModule.cardSelection(document.activeElement, false, false);
        }
        if (document.activeElement.classList.contains(SHOW_ADD_BUTTON)) {
            if (!this.parent.dialogModule.dialogObj) {
                this.parent.actionModule.addButtonClick(document.activeElement);
            }
            document.activeElement.focus();
        }
        if (element.classList.contains(CONTENT_CELLS_CLASS)) {
            const cards = [].slice.call(element.querySelectorAll('.' + CARD_CLASS));
            this.addRemoveTabIndex('Remove');
            if (cards.length > 0) {
                element.querySelector('.' + CARD_CLASS).focus();
                cards.forEach((element) => { element.setAttribute('tabindex', '0'); });
            }
            if (element.querySelector('.' + SHOW_ADD_BUTTON)) {
                element.querySelector('.' + SHOW_ADD_BUTTON).setAttribute('tabindex', '0');
                element.querySelector('.' + SHOW_ADD_BUTTON).focus();
            }
        }
        if (selectedCard === document.activeElement && this.parent.element.querySelectorAll('.' + CARD_SELECTION_CLASS).length === 1) {
            this.parent.activeCardData = {
                data: this.parent.getCardDetails(selectedCard), element: selectedCard
            };
            if (!this.parent.dialogModule.dialogObj) {
                this.parent.dialogModule.openDialog('Edit', this.parent.getCardDetails(selectedCard));
            }
            selectedCard.focus();
        }
    }
    addRemoveTabIndex(action) {
        const attribute = action === 'Add' ? '0' : '-1';
        const headerIcon = [].slice.call(this.parent.element.querySelectorAll('.' + HEADER_ICON_CLASS));
        if (headerIcon.length > 0) {
            headerIcon.forEach((element) => { element.setAttribute('tabindex', attribute); });
        }
        const swimlaneIcon = [].slice.call(this.parent.element.querySelectorAll('.' + SWIMLANE_ROW_EXPAND_CLASS));
        if (swimlaneIcon.length > 0) {
            swimlaneIcon.forEach((element) => { element.setAttribute('tabindex', attribute); });
        }
        const className = '.' + CONTENT_ROW_CLASS + ':not(.' + SWIMLANE_ROW_CLASS + ') .' + CONTENT_CELLS_CLASS;
        const contentCell = [].slice.call(this.parent.element.querySelectorAll(className));
        contentCell.forEach((element) => { element.setAttribute('tabindex', attribute); });
    }
    destroy() {
        this.keyboardModule.destroy();
    }
}

/* eslint-disable @typescript-eslint/no-explicit-any */
/**
 * Tooltip for Kanban board
 */
class KanbanTooltip {
    /**
     * Constructor for tooltip module
     *
     * @param {Kanban} parent Accepts the kanban instance
     */
    constructor(parent) {
        this.parent = parent;
        this.renderTooltip();
    }
    renderTooltip() {
        this.tooltipObj = new Tooltip({
            cssClass: this.parent.cssClass + ' ' + TOOLTIP_CLASS,
            enableRtl: this.parent.enableRtl,
            mouseTrail: !this.parent.isAdaptive,
            offsetY: 5,
            position: 'BottomCenter',
            showTipPointer: true,
            target: '.' + TOOLTIP_TEXT_CLASS,
            beforeRender: this.onBeforeRender.bind(this),
            beforeClose: this.onBeforeClose.bind(this)
        });
        this.tooltipObj.appendTo(this.parent.element);
        this.tooltipObj.isStringTemplate = true;
    }
    onBeforeRender(args) {
        if (this.parent.dragAndDropModule.isDragging) {
            args.cancel = true;
            return;
        }
        let tooltipContent;
        if (this.parent.tooltipTemplate) {
            tooltipContent = createElement('div');
            const target = closest(args.target, '.' + CARD_CLASS);
            const data = this.parent.getCardDetails(target);
            const templateId = this.parent.element.id + '_tooltipTemplate';
            const tooltipTemplate = this.parent.templateParser(this.parent.tooltipTemplate)(data, this.parent, 'tooltipTemplate', templateId, false);
            append(tooltipTemplate, tooltipContent);
            this.parent.renderTemplates();
        }
        else {
            tooltipContent = initializeCSPTemplate(function () {
                return `<div class="e-card-header-caption">${args.target.innerText}</div>`;
            });
        }
        this.tooltipObj.setProperties({ content: tooltipContent }, true);
    }
    onBeforeClose() {
        this.parent.resetTemplates(['tooltipTemplate']);
    }
    destroy() {
        this.tooltipObj.destroy();
        addClass([this.parent.element], 'e-control');
        this.tooltipObj = null;
    }
}

/**
 * Kanban touch module
 */
class KanbanTouch {
    /**
     * Constructor for touch module
     *
     * @param {Kanban} parent Accepts the kanban instance
     * @private
     */
    constructor(parent) {
        this.parent = parent;
        this.tabHold = false;
    }
    wireTouchEvents() {
        this.element = this.parent.element.querySelector('.' + CONTENT_CLASS);
        this.touchObj = new Touch(this.element, { tapHold: this.tapHoldHandler.bind(this) });
    }
    tapHoldHandler(e) {
        this.tabHold = true;
        const target = closest(e.originalEvent.target, '.' + CARD_CLASS);
        if (target && this.parent.cardSettings.selectionType === 'Multiple') {
            this.parent.actionModule.cardSelection(target, true, false);
            if (!this.mobilePopup) {
                this.renderMobilePopup();
                this.mobilePopup.show();
            }
            this.updatePopupContent();
        }
    }
    renderMobilePopup() {
        if (this.parent.cardSettings.selectionType === 'Multiple') {
            const mobilePopupWrapper = createElement('div', {
                className: POPUP_WRAPPER_CLASS + ' e-popup-close',
                innerHTML: `<div class="${POPUP_HEADER_CLASS}"><button class="${CLOSE_CLASS}"></button></div>` +
                    `<div class="${POPUP_CONTENT_CLASS}"></div>`
            });
            document.body.appendChild(mobilePopupWrapper);
            addClass([mobilePopupWrapper], DEVICE_CLASS);
            this.mobilePopup = new Popup(mobilePopupWrapper, {
                targetType: 'container',
                enableRtl: this.parent.enableRtl,
                hideAnimation: { name: 'ZoomOut' },
                showAnimation: { name: 'ZoomIn' },
                collision: { X: 'fit', Y: 'fit' },
                position: { X: 'left', Y: 'top' },
                viewPortElement: document.body,
                zIndex: 1004,
                close: this.popupClose.bind(this)
            });
            const closeIcon = this.mobilePopup.element.querySelector('.' + CLOSE_CLASS);
            const buttonObj = new Button({
                cssClass: 'e-flat e-round e-small',
                enableRtl: this.parent.enableRtl,
                iconCss: ICON_CLASS + ' ' + CLOSE_ICON_CLASS
            });
            buttonObj.appendTo(closeIcon);
            buttonObj.isStringTemplate = true;
            EventHandler.add(closeIcon, 'click', this.closeClick, this);
        }
    }
    getPopupContent() {
        let popupContent;
        const selectedCards = this.parent.getSelectedCards();
        if (selectedCards.length > 1) {
            popupContent = '(' + selectedCards.length + ') ' + this.parent.localeObj.getConstant('cardsSelected');
        }
        else if (selectedCards.length === 1) {
            popupContent = ' ' + this.parent.getCardDetails(selectedCards[0])[this.parent.cardSettings.headerField];
        }
        return popupContent;
    }
    updatePopupContent() {
        if (!this.mobilePopup) {
            return;
        }
        const popupContent = this.getPopupContent();
        if (popupContent) {
            this.mobilePopup.element.querySelector('.' + POPUP_CONTENT_CLASS).textContent = popupContent;
        }
        else {
            this.mobilePopup.hide();
        }
    }
    closeClick() {
        this.parent.touchModule.mobilePopup.hide();
    }
    popupClose() {
        this.popupDestroy();
    }
    popupDestroy() {
        if (this.mobilePopup && this.mobilePopup.element) {
            const instance = this.mobilePopup.element.querySelector('.e-control.e-btn').ej2_instances[0];
            if (instance) {
                instance.destroy();
            }
            this.mobilePopup.destroy();
            remove(this.mobilePopup.element);
            this.mobilePopup = null;
        }
    }
    unWireTouchEvents() {
        if (this.touchObj) {
            this.touchObj.destroy();
        }
        this.touchObj = null;
        this.element = null;
    }
    destroy() {
        this.popupDestroy();
        this.unWireTouchEvents();
        this.tabHold = false;
    }
}

/**
 * Kanban mobile layout rendering module
 *
 */
class MobileLayout {
    constructor(parent) {
        this.parent = parent;
    }
    renderSwimlaneHeader() {
        const toolbarWrapper = createElement('div', {
            className: SWIMLANE_HEADER_CLASS,
            innerHTML: '<div class="' + SWIMLANE_HEADER_TOOLBAR_CLASS + '"><div class="' + TOOLBAR_MENU_CLASS +
                '"><div class="e-icons ' + TOOLBAR_MENU_ICON_CLASS + '"></div></div><div class="' + TOOLBAR_LEVEL_TITLE_CLASS +
                '"><div class="' + TOOLBAR_SWIMLANE_NAME_CLASS + '"></div></div></div>'
        });
        this.parent.element.appendChild(toolbarWrapper);
        EventHandler.add(toolbarWrapper.querySelector('.' + TOOLBAR_MENU_ICON_CLASS), 'click', this.menuClick, this);
    }
    renderSwimlaneTree() {
        const height = this.parent.element.querySelector('.' + SWIMLANE_HEADER_CLASS).offsetHeight;
        const treeHeight = window.innerHeight - height;
        this.popupOverlay = createElement('div', { className: SWIMLANE_OVERLAY_CLASS, styles: 'height: ' + treeHeight + 'px;' });
        const wrapper = createElement('div', { className: SWIMLANE_CONTENT_CLASS, styles: 'top:' + height + 'px;' });
        const treeWrapper = createElement('div', {
            className: SWIMLANE_RESOURCE_CLASS + ' e-popup-close', styles: 'height: ' + treeHeight + 'px;'
        });
        wrapper.appendChild(treeWrapper);
        wrapper.appendChild(this.popupOverlay);
        this.parent.element.appendChild(wrapper);
        const swimlaneTree = createElement('div', { className: SWIMLANE_TREE_CLASS });
        treeWrapper.appendChild(swimlaneTree);
        const dataSource = this.parent.enableVirtualization ?
            this.parent.virtualLayoutModule.kanbanRows : this.parent.layoutModule.kanbanRows;
        this.treeViewObj = new TreeView({
            cssClass: this.parent.cssClass,
            enableRtl: this.parent.enableRtl,
            fields: {
                dataSource: dataSource,
                id: 'keyField',
                text: 'textField'
            },
            nodeTemplate: this.parent.swimlaneSettings.template,
            nodeClicked: this.treeSwimlaneClick.bind(this),
            drawNode: this.drawNode.bind(this)
        });
        this.treeViewObj.appendTo(swimlaneTree);
        const popupObj = {
            targetType: 'relative',
            actionOnScroll: 'none',
            enableRtl: this.parent.enableRtl,
            zIndex: 10,
            hideAnimation: { name: 'SlideLeftOut', duration: 500 },
            showAnimation: { name: 'SlideLeftIn', duration: 500 },
            viewPortElement: this.parent.element.querySelector('.' + CONTENT_CLASS)
        };
        popupObj.content = this.treeViewObj.element;
        this.treePopup = new Popup(treeWrapper, popupObj);
    }
    menuClick() {
        if (this.parent.element.querySelector('.' + SWIMLANE_RESOURCE_CLASS).classList.contains('e-popup-open')) {
            this.treePopup.hide();
            removeClass([this.popupOverlay], 'e-enable');
        }
        else {
            const treeNodes = [].slice.call(this.treeViewObj.element.querySelectorAll('.e-list-item'));
            removeClass(treeNodes, 'e-active');
            addClass([treeNodes[this.parent.layoutModule.swimlaneIndex]], 'e-active');
            this.treePopup.show();
            addClass([this.popupOverlay], 'e-enable');
        }
    }
    treeSwimlaneClick(args) {
        this.treePopup.hide();
        const treeNodes = [].slice.call(this.treeViewObj.element.querySelectorAll('.e-list-item'));
        this.parent.layoutModule.swimlaneIndex = treeNodes.indexOf(args.node);
        this.parent.layoutModule.scrollLeft = 0;
        this.parent.notify(dataReady, { processedData: this.parent.kanbanData });
        args.event.preventDefault();
    }
    hidePopup() {
        this.treePopup.hide();
        removeClass([this.popupOverlay], 'e-enable');
    }
    getWidth() {
        return (window.innerWidth * 80) / 100;
    }
    drawNode(args) {
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        if (this.parent.swimlaneSettings.template && this.parent.isReact) {
            const templateId = this.parent.element.id + '_treeviewTemplate';
            const treeViewTemplate = this.parent.templateParser(this.parent.swimlaneSettings.template)(args.nodeData, this.parent, 'nodeTemplate', templateId, false);
            append(treeViewTemplate, args.node.querySelector('.e-list-text'));
        }
    }
}

/* eslint-disable @typescript-eslint/no-explicit-any */
/**
 * Kanban layout rendering module
 *
 */
class LayoutRender extends MobileLayout {
    constructor(parent) {
        super(parent);
        this.kanbanRows = [];
        this.parent = parent;
        this.columnKeys = [];
        this.swimlaneIndex = 0;
        this.swimlaneData = {};
        this.scrollLeft = 0;
        this.frozenOrder = 0;
        this.parent.on(dataReady, this.initRender, this);
        this.parent.on(contentReady, this.scrollUiUpdate, this);
    }
    initRender() {
        if (this.parent.columns.length === 0) {
            return;
        }
        this.columnData = this.getColumnCards();
        this.kanbanRows = this.getRows();
        this.swimlaneData = this.getSwimlaneCards();
        if (this.parent.isAdaptive) {
            const parent = this.parent.element.querySelector('.' + CONTENT_CLASS);
            if (parent) {
                this.scrollLeft = parent.scrollLeft;
            }
        }
        this.destroy();
        this.parent.on(dataReady, this.initRender, this);
        this.parent.on(contentReady, this.scrollUiUpdate, this);
        if (this.parent.isAdaptive && this.parent.swimlaneSettings.keyField && this.parent.kanbanData.length !== 0) {
            this.renderSwimlaneHeader();
        }
        const header = createElement('div', { className: HEADER_CLASS });
        this.parent.element.appendChild(header);
        this.renderHeader(header);
        this.renderContent();
        this.renderCards();
        this.renderValidation();
        this.parent.renderTemplates();
        this.parent.notify(contentReady, {});
        this.wireEvents();
        if (this.parent.isInitialRender) {
            this.parent.isInitialRender = false;
        }
    }
    renderHeader(header) {
        const headerWrap = createElement('div', { className: this.parent.swimlaneSettings.keyField ? SWIMLANE_CLASS : '' });
        header.appendChild(headerWrap);
        const headerTable = createElement('table', {
            className: TABLE_CLASS + ' ' + HEADER_TABLE_CLASS
        });
        headerWrap.appendChild(headerTable);
        this.renderColGroup(headerTable);
        const tableHead = createElement('thead');
        const tableBody = createElement('tbody', { className: 'e-hide', innerHTML: '<tr><td></td></tr>', attrs: { 'role': 'rowgroup' } });
        headerTable.appendChild(tableBody);
        headerTable.appendChild(tableHead);
        if (this.parent.stackedHeaders.length > 0) {
            tableHead.appendChild(this.createStackedRow(this.parent.stackedHeaders));
        }
        const tr = createElement('tr', { className: HEADER_ROW_CLASS });
        tableHead.appendChild(tr);
        for (const column of this.parent.columns) {
            if (this.isColumnVisible(column)) {
                const index = this.parent.actionModule.columnToggleArray.indexOf(column.keyField.toString());
                const th = createElement('th', {
                    className: index === -1 ? HEADER_CELLS_CLASS : HEADER_CELLS_CLASS + ' ' + COLLAPSED_CLASS,
                    attrs: { 'data-role': 'kanban-column', 'data-key': column.keyField.toString(), 'scope': 'col' }
                });
                const classList = [];
                if (column.allowToggle) {
                    classList.push(HEADER_ROW_TOGGLE_CLASS);
                    if (!column.isExpanded) {
                        classList.push(COLLAPSED_CLASS);
                    }
                }
                addClass([th], classList);
                const headerWrapper = createElement('div', { className: HEADER_WRAP_CLASS });
                th.appendChild(headerWrapper);
                this.columnData = this.getColumnCards(this.parent.kanbanData);
                const noOfCard = this.columnData[column.keyField].length;
                const headerTitle = createElement('div', { className: HEADER_TITLE_CLASS });
                headerWrapper.appendChild(headerTitle);
                if (column.template) {
                    const templateArgs = {
                        keyField: column.keyField, headerText: column.headerText, minCount: column.minCount, maxCount: column.maxCount,
                        allowToggle: column.allowToggle, isExpanded: column.isExpanded, showItemCount: column.showItemCount, count: noOfCard
                    };
                    addClass([th], TEMPLATE_CLASS);
                    const templateId = this.parent.element.id + '_columnTemplate';
                    const templateHeader = this.parent.templateParser(column.template)(templateArgs, this.parent, 'columnTemplate', templateId, false);
                    append(templateHeader, headerTitle);
                }
                else {
                    const header = createElement('div', { className: HEADER_TEXT_CLASS, innerHTML: column.headerText });
                    headerTitle.appendChild(header);
                    if (column.showItemCount) {
                        const itemCount = createElement('div', {
                            className: CARD_ITEM_COUNT_CLASS,
                            innerHTML: '- ' + noOfCard.toString() + ' ' + this.parent.localeObj.getConstant('items')
                        });
                        headerTitle.appendChild(itemCount);
                    }
                }
                if (column.allowToggle) {
                    const isExpand = (column.isExpanded && index === -1) ? true : false;
                    const name = (isExpand) ? COLUMN_EXPAND_CLASS : COLUMN_COLLAPSE_CLASS;
                    const icon = createElement('div', {
                        className: HEADER_ICON_CLASS + ' ' + ICON_CLASS + ' ' + name,
                        attrs: { 'tabindex': '0', 'role': 'button' }
                    });
                    icon.setAttribute('aria-label', isExpand ? column.keyField + ' Expand' : column.keyField + ' Collapse');
                    th.setAttribute('aria-expanded', isExpand.toString());
                    headerWrapper.appendChild(icon);
                }
                const dataObj = [{ keyField: column.keyField, textField: column.headerText, count: noOfCard }];
                const args = { data: dataObj, element: tr, cancel: false, requestType: 'headerRow' };
                this.parent.trigger(queryCellInfo, args, (columnArgs) => {
                    if (!columnArgs.cancel) {
                        tr.appendChild(th);
                    }
                });
            }
        }
    }
    renderContent() {
        const content = createElement('div', { className: CONTENT_CLASS });
        this.parent.element.appendChild(content);
        const contentWrap = createElement('div', { className: this.parent.swimlaneSettings.keyField ? SWIMLANE_CLASS : '' });
        content.appendChild(contentWrap);
        const contentTable = createElement('table', {
            className: TABLE_CLASS + ' ' + CONTENT_TABLE_CLASS, attrs: { 'role': 'presentation' }
        });
        contentWrap.appendChild(contentTable);
        this.renderColGroup(contentTable);
        const tHead = createElement('thead', { className: 'e-hide', attrs: { 'role': 'none' } });
        for (const column of this.parent.columns) {
            const thElem = createElement('th', { id: column.keyField, innerHTML: column.keyField, attrs: { 'scope': 'col' } });
            thElem.style.display = 'none';
            tHead.appendChild(thElem);
        }
        contentTable.appendChild(tHead);
        const tBody = createElement('tbody', { attrs: { 'role': 'treegrid', 'aria-label': 'Kanban Content' } });
        contentTable.appendChild(tBody);
        let isCollaspsed = false;
        this.swimlaneRow = this.kanbanRows;
        this.initializeSwimlaneTree();
        for (const row of this.swimlaneRow) {
            if (this.parent.swimlaneSettings.keyField && this.parent.swimlaneToggleArray.length !== 0) {
                const index = this.parent.swimlaneToggleArray.indexOf(row.keyField);
                isCollaspsed = index !== -1;
            }
            if (this.parent.swimlaneSettings.keyField && !this.parent.isAdaptive) {
                this.renderSwimlaneRow(tBody, row, isCollaspsed);
            }
            this.renderSingleContent(tBody, row, isCollaspsed);
        }
    }
    renderSingleContent(tBody, row, isCollaspsed) {
        const className = isCollaspsed ? CONTENT_ROW_CLASS + ' ' + COLLAPSED_CLASS : CONTENT_ROW_CLASS;
        const tr = createElement('tr', { className: className,
            attrs: { 'role': 'row', 'aria-label': row.keyField + 'row content' } });
        for (const column of this.parent.columns) {
            if (this.isColumnVisible(column)) {
                const index = this.parent.actionModule.columnToggleArray.indexOf(column.keyField.toString());
                const className = index === -1 ? CONTENT_CELLS_CLASS : CONTENT_CELLS_CLASS + ' ' + COLLAPSED_CLASS;
                const dragClass = (column.allowDrag ? ' ' + DRAG_CLASS : '') + (column.allowDrop ? ' ' + DROP_CLASS
                    + ' ' + DROPPABLE_CLASS : '');
                const td = createElement('td', {
                    className: className + dragClass, attrs: { 'data-role': 'kanban-column', 'data-key': column.keyField.toString(), 'tabindex': '0',
                        'aria-describedby': column.keyField.toString(), 'role': 'gridcell' }
                });
                if (column.allowToggle && !column.isExpanded || index !== -1) {
                    addClass([td], COLLAPSED_CLASS);
                    const text = (column.showItemCount ? '[' +
                        this.getColumnData(column.keyField, this.swimlaneData[row.keyField]).length + '] ' : '') + column.headerText;
                    td.appendChild(createElement('div', { className: COLLAPSE_HEADER_TEXT_CLASS, innerHTML: text }));
                    td.setAttribute('aria-expanded', 'false');
                }
                if (column.showAddButton) {
                    const button = createElement('div', { className: SHOW_ADD_BUTTON, attrs: { 'tabindex': '-1' } });
                    button.appendChild(createElement('div', { className: SHOW_ADD_ICON + ' ' + ICON_CLASS }));
                    if (!column.isExpanded) {
                        addClass([button], COLLAPSED_CLASS);
                    }
                    td.appendChild(button);
                }
                tr.appendChild(td);
            }
        }
        const dataObj = [{ keyField: row.keyField, textField: row.textField, count: row.count }];
        const args = { data: dataObj, element: tr, cancel: false, requestType: 'contentRow' };
        this.parent.trigger(queryCellInfo, args, (columnArgs) => {
            if (!columnArgs.cancel) {
                if (tBody.classList.contains('e-swimlane-row')) {
                    tBody.insertAdjacentElement('beforebegin', tr);
                }
                else {
                    tBody.appendChild(tr);
                }
            }
        });
    }
    initializeSwimlaneTree() {
        if (this.parent.swimlaneSettings.keyField && this.parent.isAdaptive && this.parent.kanbanData.length !== 0) {
            const swimlaneHeaderName = this.parent.element.querySelector('.' + TOOLBAR_SWIMLANE_NAME_CLASS);
            this.swimlaneRow = [this.kanbanRows[this.swimlaneIndex]];
            this.renderSwimlaneTree();
            if (this.parent.swimlaneSettings.template) {
                const cardCount = this.swimlaneData[this.swimlaneRow[0].keyField].length;
                const templateArgs = extend({}, this.swimlaneRow[0], { count: cardCount }, true);
                const swimlaneTemplate = this.parent.templateParser(this.parent.swimlaneSettings.template)(templateArgs, this.parent, 'swimlaneTemplate', '', false);
                swimlaneHeaderName.appendChild(swimlaneTemplate[0]);
            }
            else {
                swimlaneHeaderName.innerHTML = this.swimlaneRow[0].textField;
                if (this.parent.swimlaneSettings.showItemCount) {
                    const cardCount = this.swimlaneData[this.swimlaneRow[0].keyField].length;
                    const targetItemCountElement = this.parent.element.querySelector('.' + SWIMLANE_HEADER_TOOLBAR_CLASS);
                    let itemCountElement;
                    let itemCountInnerElement;
                    if (!isNullOrUndefined(targetItemCountElement)) {
                        itemCountElement = createElement('div', { className: TOOLBAR_SWIMLANE_ITEM_COUNT_CLASS });
                        itemCountInnerElement = createElement('div', { className: CARD_ITEM_COUNT_CLASS });
                        itemCountElement.appendChild(itemCountInnerElement);
                        targetItemCountElement.appendChild(itemCountElement);
                    }
                    itemCountInnerElement.innerHTML = `- ${cardCount} ${this.parent.localeObj.getConstant('items')}`;
                }
            }
        }
    }
    renderSwimlaneRow(tBody, row, isCollapsed) {
        const name = CONTENT_ROW_CLASS + ' ' + SWIMLANE_ROW_CLASS;
        const className = isCollapsed ? ' ' + COLLAPSED_CLASS : '';
        const tr = createElement('tr', {
            className: name + className, attrs: { 'aria-label': row.keyField + ' row header',
                'role': 'row', 'data-key': row.keyField, 'aria-expanded': (!isCollapsed).toString() }
        });
        const col = this.parent.columns.length - this.parent.actionModule.hideColumnKeys.length;
        const td = createElement('td', { className: CONTENT_CELLS_CLASS,
            attrs: { 'data-role': 'kanban-column', 'role': 'gridcell', colspan: col.toString() } });
        const swimlaneHeader = createElement('div', { className: SWIMLANE_HEADER_CLASS });
        td.appendChild(swimlaneHeader);
        const iconClass = isCollapsed ? SWIMLANE_ROW_COLLAPSE_CLASS : SWIMLANE_ROW_EXPAND_CLASS;
        const iconDiv = createElement('div', {
            className: ICON_CLASS + ' ' + iconClass, attrs: {
                'tabindex': '0', 'role': 'button', 'aria-label': isCollapsed ? row.keyField + ' Collapse' : row.keyField + ' Expand'
            }
        });
        swimlaneHeader.appendChild(iconDiv);
        const headerWrap = createElement('div', { className: HEADER_WRAP_CLASS });
        swimlaneHeader.appendChild(headerWrap);
        const cardCount = this.swimlaneData[row.keyField].length;
        if (this.parent.swimlaneSettings.template) {
            const templateArgs = extend({}, row, { count: cardCount }, true);
            addClass([td], TEMPLATE_CLASS);
            const templateId = this.parent.element.id + '_swimlaneTemplate';
            const swimlaneTemplate = this.parent.templateParser(this.parent.swimlaneSettings.template)(templateArgs, this.parent, 'swimlaneTemplate', templateId, false);
            append(swimlaneTemplate, headerWrap);
        }
        else {
            headerWrap.appendChild(createElement('div', {
                className: SWIMLANE_ROW_TEXT_CLASS,
                innerHTML: row.textField,
                attrs: { 'data-role': row.textField }
            }));
        }
        if (this.parent.swimlaneSettings.showItemCount) {
            swimlaneHeader.appendChild(createElement('div', {
                className: CARD_ITEM_COUNT_CLASS,
                innerHTML: `- ${cardCount.toString()} ${this.parent.localeObj.getConstant('items')}`
            }));
        }
        tr.appendChild(td);
        const dataObj = [{ keyField: row.keyField, textField: row.textField, count: row.count }];
        const args = { data: dataObj, element: tr, cancel: false, requestType: 'swimlaneRow' };
        this.parent.trigger(queryCellInfo, args, (columnArgs) => {
            if (!columnArgs.cancel) {
                if (tBody.classList.contains('e-swimlane-row')) {
                    tBody.insertAdjacentElement('beforebegin', tr);
                }
                else {
                    tBody.appendChild(tr);
                }
            }
        });
    }
    renderCards() {
        const rows = this.swimlaneRow;
        const cardRows = [].slice.call(this.parent.element.querySelectorAll('.e-content-row:not(.e-swimlane-row)'));
        const swimlaneRows = [].slice.call(this.parent.element.querySelectorAll('.e-content-row.e-swimlane-row'));
        const removeTrs = [];
        let columnTransition = false;
        cardRows.forEach((tr, index) => {
            let dataCount = 0;
            for (const column of this.parent.columns) {
                if (this.isColumnVisible(column)) {
                    const columnData = this.parent.swimlaneSettings.keyField ?
                        this.getColumnData(column.keyField, (this.parent.swimlaneSettings.showEmptyRow &&
                            isNullOrUndefined(this.swimlaneData[rows[index].keyField])) ? []
                            : this.swimlaneData[rows[index].keyField]) : this.columnData[column.keyField];
                    dataCount += columnData.length;
                    const columnWrapper = tr.querySelector('[data-key="' + column.keyField + '"]');
                    const cardWrapper = createElement('div', {
                        className: CARD_WRAPPER_CLASS, attrs: { 'role': 'listbox', 'tabindex': '0',
                            'aria-label': column.keyField.toString()
                        }
                    });
                    if (column.transitionColumns.length > 0) {
                        columnTransition = true;
                    }
                    if (!columnTransition && isNullOrUndefined(this.parent.swimlaneSettings.keyField)) {
                        const borderElem = createElement('div', { className: BORDER_CLASS });
                        columnWrapper.appendChild(borderElem);
                    }
                    columnWrapper.appendChild(cardWrapper);
                    if (this.parent.enableHtmlSanitizer) {
                        if (columnData.length > 0 &&
                            this.parent.cardSettings &&
                            this.parent.cardSettings.contentField &&
                            typeof columnData[0][this.parent.cardSettings.contentField] === 'string') {
                            columnData[0][this.parent.cardSettings.contentField] =
                                SanitizeHtmlHelper.sanitize(columnData[0][this.parent.cardSettings.contentField]);
                        }
                    }
                    if (columnData.length > 0) {
                        for (const data of columnData) {
                            const cardText = data[this.parent.cardSettings.headerField];
                            const cardIndex = this.parent.actionModule.selectionArray.indexOf(cardText);
                            const cardElement = this.renderCard(data);
                            if (cardIndex !== -1) {
                                cardElement.setAttribute('aria-selected', 'true');
                                addClass([cardElement], CARD_SELECTION_CLASS);
                            }
                            const args = { data: data, element: cardElement, cancel: false };
                            this.parent.trigger(cardRendered, args, (cardArgs) => {
                                if (!cardArgs.cancel) {
                                    cardWrapper.appendChild(cardElement);
                                }
                            });
                        }
                    }
                    else {
                        cardWrapper.appendChild(this.renderEmptyCard());
                    }
                }
            }
            if (dataCount === 0) {
                removeTrs.push(tr);
                if (swimlaneRows.length > 0) {
                    removeTrs.push(swimlaneRows[index]);
                }
            }
        });
        if (!this.parent.swimlaneSettings.showEmptyRow && (this.parent.kanbanData.length === 0 && !this.parent.showEmptyColumn)) {
            removeTrs.forEach((tr) => remove(tr));
        }
    }
    renderCard(data) {
        const cardElement = createElement('div', {
            className: CARD_CLASS,
            attrs: { 'data-id': data[this.parent.cardSettings.headerField], 'data-key': data[this.parent.keyField],
                'aria-selected': 'false', 'tabindex': '-1', 'role': 'option', 'aria-roledescription': 'Card'
            }
        });
        if (this.parent.cardHeight !== 'auto') {
            cardElement.style.height = formatUnit(this.parent.cardHeight);
        }
        if (this.parent.cardSettings.template) {
            addClass([cardElement], TEMPLATE_CLASS);
            const templateId = this.parent.element.id + '_cardTemplate';
            const cardTemplate = this.parent.templateParser(this.parent.cardSettings.template)(data, this.parent, 'cardTemplate', templateId, false);
            append(cardTemplate, cardElement);
        }
        else {
            const tooltipClass = this.parent.enableTooltip ? ' ' + TOOLTIP_TEXT_CLASS : '';
            if (this.parent.cardSettings.showHeader) {
                const cardHeader = createElement('div', { className: CARD_HEADER_CLASS });
                const cardCaption = createElement('div', { className: CARD_HEADER_TEXT_CLASS });
                const cardText = createElement('div', {
                    className: CARD_HEADER_TITLE_CLASS + tooltipClass,
                    innerHTML: data[this.parent.cardSettings.headerField] || ''
                });
                cardHeader.appendChild(cardCaption);
                cardCaption.appendChild(cardText);
                cardElement.appendChild(cardHeader);
            }
            const cardContent = createElement('div', {
                className: CARD_CONTENT_CLASS + tooltipClass,
                innerHTML: data[this.parent.cardSettings.contentField] || ''
            });
            cardElement.appendChild(cardContent);
            if (this.parent.cardSettings.tagsField && data[this.parent.cardSettings.tagsField]) {
                const cardTags = createElement('div', { className: CARD_TAGS_CLASS });
                const tags = data[this.parent.cardSettings.tagsField].toString().split(',');
                for (const tag of tags) {
                    cardTags.appendChild(createElement('div', {
                        className: CARD_TAG_CLASS + ' ' + CARD_LABEL_CLASS, innerHTML: tag
                    }));
                }
                cardElement.appendChild(cardTags);
            }
            if (this.parent.cardSettings.grabberField && data[this.parent.cardSettings.grabberField]) {
                addClass([cardElement], CARD_COLOR_CLASS);
                cardElement.style.borderLeftColor = data[this.parent.cardSettings.grabberField];
            }
            if (this.parent.cardSettings.footerCssField) {
                const cardFields = createElement('div', { className: CARD_FOOTER_CLASS });
                const keys = data[this.parent.cardSettings.footerCssField].split(',');
                for (const key of keys) {
                    cardFields.appendChild(createElement('div', {
                        className: key.trim() + ' ' + CARD_FOOTER_CSS_CLASS
                    }));
                }
                cardElement.appendChild(cardFields);
            }
        }
        return cardElement;
    }
    renderEmptyCard() {
        const emptyCard = createElement('span', {
            className: EMPTY_CARD_CLASS, innerHTML: this.parent.localeObj.getConstant('noCard'),
            attrs: { 'aria-label': this.parent.localeObj.getConstant('noCard'), 'role': 'option' }
        });
        return emptyCard;
    }
    renderColGroup(table) {
        const colGroup = createElement('colgroup');
        this.parent.columns.forEach((column) => {
            if (this.isColumnVisible(column)) {
                const index = this.parent.actionModule.columnToggleArray.indexOf(column.keyField.toString());
                const isToggle = column.allowToggle && !column.isExpanded;
                const className = index === -1 ? (isToggle ? COLLAPSED_CLASS : '') : COLLAPSED_CLASS;
                const col = createElement('col', {
                    className: className,
                    attrs: { 'data-key': column.keyField.toString() }
                });
                if (this.parent.isAdaptive) {
                    const width = isToggle ? formatUnit(toggleWidth) : formatUnit(this.getWidth());
                    col.style.width = width;
                }
                colGroup.appendChild(col);
            }
        });
        table.appendChild(colGroup);
    }
    getRows() {
        let kanbanRows = [];
        if (this.parent.swimlaneSettings.keyField) {
            this.parent.kanbanData.map((obj) => {
                if (!this.parent.swimlaneSettings.showEmptyRow) {
                    if ((isNullOrUndefined(obj[this.parent.keyField])) || (obj[this.parent.keyField] === '') ||
                        (obj[this.parent.keyField] && this.columnKeys.indexOf(obj[this.parent.keyField].toString()) === -1)) {
                        return;
                    }
                }
                let textField = obj[this.parent.swimlaneSettings.textField] || obj[this.parent.swimlaneSettings.keyField];
                let keyField = obj[this.parent.swimlaneSettings.keyField];
                if (!obj[this.parent.swimlaneSettings.keyField]) {
                    if (this.parent.swimlaneSettings.showUnassignedRow) {
                        textField = this.parent.localeObj.getConstant('unassigned');
                        keyField = '';
                    }
                    else {
                        return;
                    }
                }
                kanbanRows.push({ keyField: keyField, textField: textField });
            });
            kanbanRows = kanbanRows.filter((item, index, arr) => index === arr.map((item) => item.keyField).indexOf(item.keyField));
            kanbanRows = this.swimlaneSorting(kanbanRows);
            kanbanRows.forEach((row) => {
                row.count = this.parent.kanbanData.filter((obj) => this.columnKeys.indexOf(obj[this.parent.keyField]) > -1 &&
                    obj[this.parent.swimlaneSettings.keyField] === row.keyField).length;
            });
            if (kanbanRows.length === 0) {
                kanbanRows.push({ keyField: '', textField: '' });
            }
        }
        else {
            kanbanRows.push({ keyField: '', textField: '' });
        }
        return kanbanRows;
    }
    swimlaneSorting(rows) {
        if (this.parent.swimlaneSettings.sortComparer) {
            rows = this.parent.swimlaneSettings.sortComparer.call(this.parent, rows);
        }
        else {
            rows.sort((a, b) => a.textField.localeCompare(b.textField, undefined, { numeric: true }));
            if (this.parent.swimlaneSettings.sortDirection === 'Descending') {
                rows.reverse();
            }
        }
        return rows;
    }
    createStackedRow(rows) {
        const tr = createElement('tr', { className: HEADER_ROW_CLASS + ' ' + STACKED_HEADER_ROW_CLASS });
        const stackedHeaders = [];
        this.parent.columns.forEach((column) => {
            let headerText = '';
            for (const row of rows) {
                if (row.keyFields.indexOf(column.keyField.toString()) !== -1) {
                    headerText = row.text;
                }
            }
            stackedHeaders.push(headerText);
        });
        for (let h = 0; h < stackedHeaders.length; h++) {
            let colSpan = 1;
            for (let j = h + 1; j < stackedHeaders.length; j++) {
                if ((stackedHeaders[h] !== '') && (stackedHeaders[j] !== '') && stackedHeaders[h] === stackedHeaders[j]) {
                    colSpan++;
                }
                else {
                    break;
                }
            }
            const div = createElement('div', { className: HEADER_TEXT_CLASS, innerHTML: stackedHeaders[h] });
            const th = createElement('th', {
                className: HEADER_CELLS_CLASS + ' ' + STACKED_HEADER_CELL_CLASS,
                attrs: { 'colspan': colSpan.toString(), 'scope': 'col' }
            });
            tr.appendChild(th).appendChild(div);
            h += colSpan - 1;
        }
        return tr;
    }
    scrollUiUpdate() {
        const header = this.parent.element.querySelector('.' + HEADER_CLASS);
        const content = this.parent.element.querySelector('.' + CONTENT_CLASS);
        let height = this.parent.element.offsetHeight - header.offsetHeight;
        if (this.parent.isAdaptive) {
            height = window.innerHeight - (header.offsetHeight + bottomSpace);
            const swimlaneToolbar = this.parent.element.querySelector('.' + SWIMLANE_HEADER_CLASS);
            if (swimlaneToolbar) {
                height -= swimlaneToolbar.offsetHeight;
            }
            const cardWrappers = [].slice.call(this.parent.element.querySelectorAll('.' + CONTENT_CELLS_CLASS));
            cardWrappers.forEach((cell) => {
                const cardWrapper = cell.querySelector('.' + CARD_WRAPPER_CLASS);
                if (!cardWrapper.classList.contains(MULTI_CARD_WRAPPER_CLASS)) {
                    cardWrapper.style.height = formatUnit(height);
                    EventHandler.add(cell, 'touchmove', this.onAdaptiveScroll, this);
                }
            });
        }
        if (this.parent.height !== 'auto' && this.parent.height !== '100%') {
            content.style.height = formatUnit(height);
        }
        [].slice.call(header.children).forEach((node) => {
            let paddingValue = 0;
            const scrollBarWidth = content.offsetWidth - content.clientWidth;
            if ((scrollBarWidth) > 0) {
                paddingValue = scrollBarWidth;
                if ((content.offsetHeight - content.clientHeight) > 0) {
                    node.style.width = formatUnit(content.clientWidth);
                }
            }
            if (this.parent.enableRtl) {
                node.style.paddingLeft = formatUnit(paddingValue);
            }
            else {
                node.style.paddingRight = formatUnit(paddingValue);
            }
        });
        this.updateScrollPosition();
    }
    onContentScroll(e) {
        const target = e.target;
        const header = this.parent.element.querySelector('.' + HEADER_CLASS);
        [].slice.call(header.children).forEach((node) => { node.scrollLeft = target.scrollLeft; });
        this.parent.scrollPosition.content = { left: target.scrollLeft, top: target.scrollTop };
        if (!isNullOrUndefined(this.parent.swimlaneSettings.keyField) && this.parent.swimlaneSettings.enableFrozenRows) {
            this.frozenRows(e);
        }
    }
    addFrozenSwimlaneDataKey(currentElem) {
        const frozenKey = currentElem.getAttribute('data-key');
        if (!isNullOrUndefined(frozenKey)) {
            this.frozenSwimlaneRow.setAttribute('data-key', frozenKey);
        }
    }
    frozenRows(e) {
        const firstSwimlane = this.parent.element.querySelector('.' + SWIMLANE_ROW_CLASS);
        const header = this.parent.element.querySelector('.' + HEADER_CLASS);
        const content = this.parent.element.querySelector('.' + CONTENT_CLASS);
        if (isNullOrUndefined(this.frozenSwimlaneRow)) {
            this.frozenSwimlaneRow = createElement('div', { className: FROZEN_SWIMLANE_ROW_CLASS });
            const frozenRow = createElement('div', { className: FROZEN_ROW_CLASS });
            this.frozenSwimlaneRow.appendChild(frozenRow);
            this.parent.element.insertBefore(this.frozenSwimlaneRow, this.parent.element.firstElementChild);
            frozenRow.appendChild(firstSwimlane.querySelector('.' + SWIMLANE_HEADER_CLASS).cloneNode(true));
            this.addFrozenSwimlaneDataKey(firstSwimlane);
            setStyleAttribute(this.frozenSwimlaneRow, { height: formatUnit(firstSwimlane.getBoundingClientRect().height),
                width: formatUnit(content.querySelector('.e-swimlane').getBoundingClientRect().width),
                top: formatUnit(header.getBoundingClientRect().height.toString())
            });
            setStyleAttribute(header, { position: 'relative', top: formatUnit((-this.frozenSwimlaneRow.getBoundingClientRect().height)) });
            setStyleAttribute(content, { position: 'relative', top: formatUnit((-this.frozenSwimlaneRow.getBoundingClientRect().height)) });
        }
        else {
            const swimlaneRows = [].slice.call(this.parent.element.querySelectorAll('.' + SWIMLANE_ROW_CLASS));
            const curSwim = swimlaneRows[this.frozenOrder];
            const prevSwim = swimlaneRows[this.frozenOrder - 1];
            const nextSwim = swimlaneRows[this.frozenOrder + 1];
            let curSwimHeight;
            let prevSwimHeight;
            let nextSwimHeight;
            if (curSwim) {
                curSwimHeight = curSwim.getBoundingClientRect().top + curSwim.getBoundingClientRect().height;
            }
            if (prevSwim) {
                prevSwimHeight = prevSwim.getBoundingClientRect().top + prevSwim.getBoundingClientRect().height;
            }
            if (nextSwim) {
                nextSwimHeight = nextSwim.getBoundingClientRect().top + nextSwim.getBoundingClientRect().height;
            }
            const frozenSwimHeight = content.getBoundingClientRect().top + this.frozenSwimlaneRow.getBoundingClientRect().height;
            const frozenRowsElement = this.frozenSwimlaneRow.querySelector('.' + FROZEN_ROW_CLASS);
            if (nextSwimHeight && frozenSwimHeight >= nextSwimHeight && this.frozenOrder < swimlaneRows.length - 1) {
                if (frozenRowsElement) {
                    remove(frozenRowsElement.querySelector('.' + SWIMLANE_HEADER_CLASS));
                    frozenRowsElement.appendChild(nextSwim.querySelector('.' + SWIMLANE_HEADER_CLASS).cloneNode(true));
                    this.addFrozenSwimlaneDataKey(nextSwim);
                }
                ++this.frozenOrder;
            }
            else if (prevSwimHeight && frozenSwimHeight < curSwimHeight && frozenSwimHeight > prevSwimHeight && this.frozenOrder > 0) {
                if (frozenRowsElement) {
                    remove(frozenRowsElement.querySelector('.' + SWIMLANE_HEADER_CLASS));
                    frozenRowsElement.appendChild(prevSwim.querySelector('.' + SWIMLANE_HEADER_CLASS).cloneNode(true));
                    this.addFrozenSwimlaneDataKey(prevSwim);
                }
                --this.frozenOrder;
            }
        }
        if (e && e.target.scrollTop === 0) {
            this.removeFrozenRows();
        }
    }
    removeFrozenRows() {
        remove(this.frozenSwimlaneRow);
        this.frozenSwimlaneRow = null;
        const header = this.parent.element.querySelector('.' + HEADER_CLASS);
        const content = this.parent.element.querySelector('.' + CONTENT_CLASS);
        setStyleAttribute(header, { position: '', top: '' });
        setStyleAttribute(content, { position: '', top: '' });
        this.parent.scrollPosition.content = { left: this.parent.scrollPosition.content.left, top: 0 };
        content.scrollTop = 0;
        this.frozenOrder = 0;
    }
    onColumnScroll(e) {
        const target = e.target;
        if (target.offsetParent) {
            const columnKey = target.offsetParent.getAttribute('data-key');
            this.parent.scrollPosition.column[`${columnKey}`] = { left: target.scrollLeft, top: target.scrollTop };
        }
    }
    onAdaptiveScroll(e) {
        if (this.parent.touchModule.tabHold && !this.parent.touchModule.mobilePopup) {
            e.preventDefault();
        }
    }
    /**
     * Check column is visible or not.
     *
     * @param {ColumnsModel} column - specifies the column.
     * @returns {boolean}
     * @private
     * @hidden
     */
    isColumnVisible(column) {
        let isVisible = false;
        const isNumeric = typeof column.keyField === 'number';
        if (isNumeric) {
            isVisible = this.parent.actionModule.hideColumnKeys.indexOf(column.keyField.toString()) === -1;
        }
        else {
            column.keyField.split(',').forEach((key) => { isVisible = this.parent.actionModule.hideColumnKeys.indexOf(key) === -1; });
        }
        return isVisible;
    }
    renderLimits(column, target) {
        const limits = createElement('div', { className: LIMITS_CLASS });
        if (column.minCount) {
            limits.appendChild(createElement('div', {
                className: MIN_COUNT_CLASS,
                innerHTML: this.parent.localeObj.getConstant('min') + ': ' + column.minCount.toString()
            }));
        }
        if (column.maxCount) {
            limits.appendChild(createElement('div', {
                className: MAX_COUNT_CLASS,
                innerHTML: this.parent.localeObj.getConstant('max') + ': ' + column.maxCount.toString()
            }));
        }
        if (limits.childElementCount > 0) {
            if (target.querySelector('.' + CARD_WRAPPER_CLASS)) {
                target.insertBefore(limits, target.firstElementChild);
            }
            else {
                target.appendChild(limits);
            }
        }
    }
    renderValidation() {
        this.parent.columns.forEach((column) => {
            if (!column.minCount && !column.maxCount) {
                return;
            }
            const cardData = this.columnData[column.keyField];
            const keySelector = `[data-key="${column.keyField}"]`;
            const headerCell = this.parent.element.querySelector(`.${HEADER_CELLS_CLASS + keySelector}`);
            const rowCells = [].slice.call(this.parent.element.querySelectorAll(`.${CONTENT_CELLS_CLASS + keySelector}`));
            if (this.parent.constraintType === 'Swimlane' && this.parent.swimlaneSettings.keyField) {
                this.swimlaneRow.forEach((row, index) => {
                    this.renderLimits(column, rowCells[index]);
                    const rowCards = cardData.filter((card) => card[this.parent.swimlaneSettings.keyField] === row.keyField);
                    const colorClass = this.getValidationClass(column, rowCards.length);
                    if (colorClass) {
                        addClass([rowCells[index]], colorClass);
                    }
                });
            }
            else {
                this.renderLimits(column, headerCell);
                const colorClass = this.getValidationClass(column, cardData.length);
                if (colorClass) {
                    addClass(rowCells.concat(headerCell), colorClass);
                }
            }
        });
    }
    getValidationClass(column, count) {
        let colorClass;
        if (column.maxCount && count > column.maxCount) {
            colorClass = MAX_COLOR_CLASS;
        }
        else if (column.minCount && count < column.minCount) {
            colorClass = MIN_COLOR_CLASS;
        }
        return colorClass;
    }
    refreshValidation() {
        const validations = [].slice.call(this.parent.element.querySelectorAll('.' + LIMITS_CLASS));
        validations.forEach((node) => { remove(node); });
        const minClass = [].slice.call(this.parent.element.querySelectorAll('.' + MIN_COLOR_CLASS));
        removeClass(minClass, MIN_COLOR_CLASS);
        const maxClass = [].slice.call(this.parent.element.querySelectorAll('.' + MAX_COLOR_CLASS));
        removeClass(maxClass, MAX_COLOR_CLASS);
        this.renderValidation();
    }
    getColumnData(columnValue, dataSource = this.parent.kanbanData) {
        let cardData = [];
        const isNumeric = typeof columnValue === 'number';
        if (isNumeric) {
            const keyData = dataSource.filter((cardObj) => cardObj[this.parent.keyField] === columnValue);
            cardData = cardData.concat(keyData);
        }
        else {
            const columnKeys = columnValue.split(',');
            for (const key of columnKeys) {
                const keyData = dataSource.filter((cardObj) => cardObj[this.parent.keyField] === key.trim());
                cardData = cardData.concat(keyData);
            }
        }
        this.sortCategory(cardData);
        return cardData;
    }
    sortCategory(cardData) {
        let key = this.parent.cardSettings.headerField;
        const direction = this.parent.sortSettings.direction;
        switch (this.parent.sortSettings.sortBy) {
            case 'DataSourceOrder':
                this.sortOrder(key, direction, cardData);
                break;
            case 'Custom':
            case 'Index':
                if (this.parent.sortSettings.field) {
                    key = this.parent.sortSettings.field;
                }
                this.sortOrder(key, direction, cardData);
                break;
        }
        return cardData;
    }
    sortOrder(key, direction, cardData) {
        let isNumeric = true;
        if (this.parent.kanbanData.length > 0) {
            isNumeric = typeof (this.parent.kanbanData[0])[`${key}`] === 'number';
        }
        if (!isNumeric && this.parent.sortSettings.sortBy === 'Index') {
            return cardData;
        }
        let first;
        let second;
        cardData = cardData.sort((firstData, secondData) => {
            if (!isNumeric) {
                first = firstData[`${key}`] ? firstData[`${key}`].toLowerCase() : '';
                second = secondData[`${key}`] ? secondData[`${key}`].toLowerCase() : '';
            }
            else {
                first = firstData[`${key}`];
                second = secondData[`${key}`];
            }
            return (first > second) ? 1 : ((second > first) ? -1 : 0);
        });
        if (direction === 'Descending') {
            cardData.reverse();
        }
        return cardData;
    }
    documentClick(args) {
        if (args.target.classList.contains(SWIMLANE_OVERLAY_CLASS) &&
            this.parent.element.querySelector('.' + SWIMLANE_RESOURCE_CLASS).classList.contains('e-popup-open')) {
            this.treePopup.hide();
            removeClass([this.popupOverlay], 'e-enable');
        }
        if (closest(args.target, `.${ROOT_CLASS}`)) {
            return;
        }
        const cards = [].slice.call(this.parent.element.querySelectorAll(`.${CARD_CLASS}.${CARD_SELECTION_CLASS}`));
        removeClass(cards, CARD_SELECTION_CLASS);
        this.disableAttributeSelection(cards);
    }
    disableAttributeSelection(cards) {
        if (cards instanceof Element) {
            cards.setAttribute('aria-selected', 'false');
        }
        else {
            cards.forEach((card) => { card.setAttribute('aria-selected', 'false'); });
        }
    }
    getColumnCards(data) {
        const columnData = {};
        this.columnKeys = [];
        this.parent.columns.forEach((column) => {
            const isNumeric = typeof column.keyField === 'number';
            if (isNumeric) {
                this.columnKeys = this.columnKeys.concat(column.keyField.toString());
            }
            else {
                this.columnKeys = this.columnKeys.concat(column.keyField.split(',').map((e) => e.trim()));
            }
            const cardData = this.getColumnData(column.keyField, data);
            columnData[column.keyField] = cardData;
        });
        return columnData;
    }
    getSwimlaneCards() {
        const swimlaneData = {};
        if (this.parent.swimlaneSettings.keyField) {
            this.kanbanRows.forEach((row) => swimlaneData[row.keyField] = this.parent.kanbanData.filter((obj) => !isNullOrUndefined(obj[this.parent.keyField]) &&
                this.columnKeys.indexOf(obj[this.parent.keyField].toString()) > -1 &&
                ((!obj[this.parent.swimlaneSettings.keyField] && this.parent.swimlaneSettings.showUnassignedRow) ?
                    '' : obj[this.parent.swimlaneSettings.keyField]) === row.keyField));
        }
        return swimlaneData;
    }
    refreshHeaders() {
        const header = this.parent.element.querySelector('.' + HEADER_CLASS);
        [].slice.call(header.children).forEach((child) => remove(child));
        this.renderHeader(header);
    }
    refreshCards() {
        this.parent.resetTemplates(['cardTemplate']);
        const cards = [].slice.call(this.parent.element.querySelectorAll('.' + CARD_WRAPPER_CLASS));
        cards.forEach((card) => remove(card));
        this.renderCards();
        this.wireDragEvent();
        this.parent.renderTemplates();
    }
    refresh() {
        let isColumnTemplateRefreshed = false;
        this.parent.columns.forEach((column) => {
            if (column.showItemCount) {
                if (column && column.template && !isColumnTemplateRefreshed && !Browser.isDevice) {
                    this.refreshHeaders();
                    isColumnTemplateRefreshed = true;
                }
                const countSelector = `.${HEADER_CELLS_CLASS}[data-key="${column.keyField}"] .${CARD_ITEM_COUNT_CLASS}`;
                const itemCount = this.parent.element.querySelector(countSelector);
                if (itemCount) {
                    const isNumeric = typeof column.keyField === 'number';
                    let cardLength = 0;
                    if (isNumeric) {
                        // eslint-disable-next-line no-useless-escape
                        cardLength = ([].slice.call(this.parent.element.querySelectorAll('.' + CARD_CLASS + '[data-key=\"' + column.keyField + '\"]'))).length;
                    }
                    else {
                        const keys = column.keyField.split(',');
                        for (const key of keys) {
                            // eslint-disable-next-line no-useless-escape
                            const cards = [].slice.call(this.parent.element.querySelectorAll('.' + CARD_CLASS + '[data-key=\"' + key.trim() + '\"]'));
                            cardLength = cards.length + cardLength;
                        }
                    }
                    itemCount.innerHTML = '- ' + cardLength + ' ' + this.parent.localeObj.getConstant('items');
                }
            }
        });
        if (this.parent.swimlaneSettings.keyField) {
            const swimlaneRows = [].slice.call(this.parent.element.querySelectorAll(`.${SWIMLANE_ROW_CLASS}`));
            swimlaneRows.forEach((swimlane) => {
                const swimlaneKey = swimlane.getAttribute('data-key');
                const itemCount = swimlane.querySelector(`.${CARD_ITEM_COUNT_CLASS}`);
                if (itemCount && swimlaneKey) {
                    const cards = [].slice.call(swimlane.nextElementSibling.querySelectorAll('.' + CARD_CLASS));
                    itemCount.innerHTML = '- ' + cards.length + ' ' + this.parent.localeObj.getConstant('items');
                }
            });
        }
        this.refreshValidation();
    }
    updateScrollPosition() {
        const content = this.parent.element.querySelector('.' + CONTENT_CLASS);
        if (content) {
            if (!Browser.isIE) {
                content.scrollTo(this.parent.scrollPosition.content.left, this.parent.scrollPosition.content.top);
            }
            else {
                content.scrollTop = this.parent.scrollPosition.content.top;
                content.scrollLeft = this.parent.scrollPosition.content.left;
            }
        }
        const cardWrapper = [].slice.call(this.parent.element.querySelectorAll('.' + CARD_WRAPPER_CLASS));
        cardWrapper.forEach((wrapper) => {
            if (wrapper.offsetParent) {
                const scrollData = this.parent.scrollPosition.column[wrapper.offsetParent.getAttribute('data-key')];
                if (scrollData) {
                    if (!Browser.isIE) {
                        wrapper.scrollTo(scrollData.left, scrollData.top);
                    }
                    else {
                        wrapper.scrollTop = scrollData.top;
                        wrapper.scrollLeft = scrollData.left;
                    }
                }
            }
        });
    }
    renderCardBasedOnIndex(data, index) {
        const key = data[this.parent.keyField];
        let cardRow = this.parent.element.querySelector('.e-content-row:not(.e-swimlane-row)');
        if (this.parent.swimlaneSettings.keyField && !this.parent.isAdaptive) {
            const rowSelector = `.e-content-row.e-swimlane-row[data-key="${data[this.parent.swimlaneSettings.keyField]}"]`;
            if (this.parent.element.querySelector(rowSelector)) {
                cardRow = this.parent.element.querySelector(rowSelector).nextElementSibling;
            }
            else {
                const columnIndex = this.columnKeys.indexOf(key);
                if (columnIndex !== -1 && this.parent.actionModule.hideColumnKeys.indexOf(key) === -1) {
                    const index = this.kanbanRows.findIndex((rowData) => rowData['keyField'] === data[this.parent.swimlaneSettings.keyField]);
                    const swim = [].slice.call(this.parent.element.querySelectorAll('.e-swimlane-row'));
                    let swimRow = this.parent.element.querySelector('.' + CONTENT_TABLE_CLASS + ' tbody');
                    if (swim[index]) {
                        swimRow = swim[index];
                    }
                    this.renderSwimlaneRow(swimRow, this.kanbanRows[index], false);
                    this.renderSingleContent(swimRow, this.kanbanRows[index], false);
                }
                if (!isNullOrUndefined(this.parent.element.querySelector(rowSelector))) {
                    cardRow = this.parent.element.querySelector(rowSelector).nextElementSibling;
                }
                [].slice.call(cardRow.children).forEach((cell) => {
                    const cardWrapper = createElement('div', { className: CARD_WRAPPER_CLASS });
                    cell.appendChild(cardWrapper);
                    cardWrapper.appendChild(this.renderEmptyCard());
                });
            }
        }
        if (this.parent.sortSettings.sortBy !== 'Index') {
            let field = this.parent.cardSettings.headerField;
            if (this.parent.sortSettings.sortBy === 'Custom') {
                field = this.parent.sortSettings.field;
            }
            if (isNullOrUndefined(this.parent.swimlaneSettings.keyField)) {
                index = this.getColumnData(key, this.parent.kanbanData).findIndex((colData) => colData[`${field}`] === data[`${field}`]);
            }
            else {
                const swimlaneDatas = this.parent.getSwimlaneData(data[this.parent.swimlaneSettings.keyField]);
                index = this.getColumnData(key, swimlaneDatas).findIndex((colData) => colData[`${field}`] === data[`${field}`]);
            }
        }
        else if (this.parent.sortSettings.sortBy === 'Index' &&
            this.parent.sortSettings.field && this.parent.sortSettings.direction === 'Ascending') {
            index = data[this.parent.sortSettings.field] - 1;
        }
        if (cardRow) {
            const td = [].slice.call(cardRow.children).filter((e) => e.getAttribute('data-key').replace(/\s/g, '').split(',').indexOf(key.toString().replace(/\s/g, '')) !== -1)[0];
            if (!isNullOrUndefined(td)) {
                const cardWrapper = td.querySelector('.' + CARD_WRAPPER_CLASS);
                const emptyCard = cardWrapper.querySelector('.' + EMPTY_CARD_CLASS);
                if (emptyCard) {
                    remove(emptyCard);
                }
                const cardElement = this.renderCard(data);
                if (this.parent.allowDragAndDrop && td.classList.contains(DRAG_CLASS)) {
                    this.parent.dragAndDropModule.wireDragEvents(cardElement);
                    addClass([cardElement], DROPPABLE_CLASS);
                }
                const args = { data: data, element: cardElement, cancel: false };
                this.parent.trigger(cardRendered, args, (cardArgs) => {
                    if (!cardArgs.cancel) {
                        if (isNullOrUndefined(index) || cardWrapper.children.length === 0) {
                            cardWrapper.appendChild(cardElement);
                        }
                        else {
                            cardWrapper.insertBefore(cardElement, cardWrapper.childNodes[index]);
                        }
                    }
                });
            }
        }
    }
    removeCard(data) {
        const cardKey = data[this.parent.cardSettings.headerField];
        const cardElement = this.parent.element.querySelector(`.${CARD_CLASS}[data-id="${cardKey}"]`);
        if (cardElement) {
            this.isSelectedCard = cardElement.classList.contains(CARD_SELECTION_CLASS) ? true : false;
            const cardContainer = cardElement.parentElement;
            remove(cardElement);
            if (cardContainer.querySelectorAll('.' + CARD_CLASS + ':not(.' + CLONED_CARD_CLASS + ')').length === 0) {
                cardContainer.appendChild(this.renderEmptyCard());
            }
        }
    }
    wireEvents() {
        EventHandler.add(this.parent.element, 'click', this.parent.actionModule.clickHandler, this.parent.actionModule);
        EventHandler.add(this.parent.element, 'dblclick', this.parent.actionModule.doubleClickHandler, this.parent.actionModule);
        EventHandler.add(document, Browser.touchStartEvent, this.documentClick, this);
        const content = this.parent.element.querySelector('.' + CONTENT_CLASS);
        EventHandler.add(content, 'scroll', this.onContentScroll, this);
        const cardWrapper = [].slice.call(this.parent.element.querySelectorAll('.' + CARD_WRAPPER_CLASS));
        cardWrapper.forEach((wrapper) => {
            if (this.parent.isInitialRender && wrapper.offsetParent) {
                this.parent.scrollPosition.column[wrapper.offsetParent.getAttribute('data-key')] = { left: 0, top: 0 };
            }
            EventHandler.add(wrapper, 'scroll', this.onColumnScroll, this);
        });
        if (this.parent.isAdaptive) {
            this.parent.touchModule.wireTouchEvents();
            content.scrollLeft = this.scrollLeft;
        }
        this.wireDragEvent();
        if (this.parent.allowColumnDragAndDrop) {
            this.parent.wireColumnDragEvent();
        }
    }
    unWireEvents() {
        EventHandler.remove(this.parent.element, 'click', this.parent.actionModule.clickHandler);
        EventHandler.remove(this.parent.element, 'dblclick', this.parent.actionModule.doubleClickHandler);
        EventHandler.remove(document, Browser.touchStartEvent, this.documentClick);
        const content = this.parent.element.querySelector('.' + CONTENT_CLASS);
        if (content) {
            EventHandler.remove(content, 'scroll', this.onContentScroll);
            if (this.parent.allowDragAndDrop) {
                this.unWireDragEvent();
            }
        }
        const cardWrapper = [].slice.call(this.parent.element.querySelectorAll('.' + CARD_WRAPPER_CLASS));
        if (cardWrapper.length > 0) {
            cardWrapper.forEach((wrapper) => { EventHandler.remove(wrapper, 'scroll', this.onColumnScroll); });
        }
        if (this.parent.isAdaptive) {
            this.parent.touchModule.unWireTouchEvents();
        }
        if (this.parent.allowColumnDragAndDrop) {
            this.parent.unwireColumnDragEvent();
        }
    }
    wireDragEvent() {
        if (this.parent.allowDragAndDrop) {
            const cards = [].slice.call(this.parent.element.querySelectorAll('.' + CONTENT_CELLS_CLASS
                + '.' + DRAG_CLASS + ' .' + CARD_CLASS));
            addClass(cards, DROPPABLE_CLASS);
            cards.forEach((card) => this.parent.dragAndDropModule.wireDragEvents(card));
        }
    }
    unWireDragEvent() {
        const cards = [].slice.call(this.parent.element.querySelectorAll('.' + CONTENT_CELLS_CLASS
            + '.' + DRAG_CLASS + ' .' + CARD_CLASS));
        removeClass(cards, DROPPABLE_CLASS);
        cards.forEach((card) => this.parent.dragAndDropModule.unWireDragEvents(card));
    }
    destroy() {
        this.parent.resetTemplates();
        this.parent.off(dataReady, this.initRender);
        this.parent.off(contentReady, this.scrollUiUpdate);
        this.unWireEvents();
        const header = this.parent.element.querySelector('.' + HEADER_CLASS);
        if (header) {
            remove(header);
        }
        const content = this.parent.element.querySelector('.' + CONTENT_CLASS);
        if (content) {
            remove(content);
        }
        if (this.treeViewObj) {
            this.treeViewObj.destroy();
            this.treeViewObj = null;
        }
        if (this.treePopup) {
            this.treePopup.destroy();
            this.treePopup = null;
        }
        const swimlaneToolBarEle = this.parent.element.querySelector('.' + SWIMLANE_HEADER_CLASS);
        if (swimlaneToolBarEle) {
            remove(swimlaneToolBarEle);
        }
        const swimlaneContent = this.parent.element.querySelector('.' + SWIMLANE_CONTENT_CLASS);
        if (swimlaneContent) {
            remove(swimlaneContent);
        }
        const swimlaneFrozenRow = this.parent.element.querySelector('.' + FROZEN_SWIMLANE_ROW_CLASS);
        if (swimlaneFrozenRow) {
            remove(swimlaneFrozenRow);
            this.frozenSwimlaneRow = null;
        }
    }
}

/* eslint-disable @typescript-eslint/no-explicit-any */
/**
 * Kanban layout rendering module
 *
 */
class VirtualLayoutRender extends MobileLayout {
    constructor(parent) {
        super(parent);
        this.parent = parent;
        this.kanbanRows = [];
        this.scrollStatus = {};
        this.offsets = {};
        this.tempOffsets = {};
        this.offsetKeys = [];
        this.columnKeys = [];
        this.scrollLeft = 0;
        this.frozenOrder = 0;
        this.winResize = this.windowResize.bind(this);
        if (this.parent.enableVirtualization) {
            this.parent.on(dataReady, this.initRender, this);
            this.parent.on(contentReady, this.scrollUiUpdate, this);
        }
    }
    initRender() {
        this.isSwimlane = !isNullOrUndefined(this.parent.swimlaneSettings.keyField) &&
            this.parent.swimlaneSettings.keyField.trim().length > 1 ? true : false;
        this.query = this.parent.query instanceof Query ? this.parent.query : new Query();
        if (this.parent.columns.length === 0) {
            return;
        }
        this.cardHeight = this.cardHeightCalculate();
        this.columnData = this.getColumnCards();
        this.kanbanRows = this.getRows();
        if (this.parent.isAdaptive) {
            const parent = this.parent.element.querySelector('.' + CONTENT_CLASS);
            if (parent) {
                this.scrollLeft = parent.scrollLeft;
            }
        }
        this.destroy();
        this.parent.on(dataReady, this.initRender, this);
        this.parent.on(contentReady, this.scrollUiUpdate, this);
        const header = createElement('div', { className: HEADER_CLASS });
        this.parent.element.appendChild(header);
        this.renderHeader(header);
        if (!this.isSwimlane) {
            this.renderContent();
            this.renderCards();
        }
        this.renderValidation();
        this.parent.renderTemplates();
        this.parent.notify(contentReady, {});
        this.wireEvents();
        if (this.parent.isInitialRender) {
            this.parent.isInitialRender = false;
        }
    }
    cardHeightCalculate() {
        let cardHeight;
        if (this.parent.cardHeight === 'auto') {
            cardHeight = 100 + 8; // 8 is the margin bottom value of the card.
        }
        else {
            cardHeight = parseInt(formatUnit(this.parent.cardHeight).split('px')[0], 10) + 8;
        }
        return cardHeight;
    }
    renderHeader(header) {
        const headerWrap = createElement('div', { className: this.parent.swimlaneSettings.keyField ? SWIMLANE_CLASS : '' });
        header.appendChild(headerWrap);
        const headerTable = createElement('table', {
            className: TABLE_CLASS + ' ' + HEADER_TABLE_CLASS,
            attrs: { 'role': 'presentation' }
        });
        headerWrap.appendChild(headerTable);
        this.renderColGroup(headerTable);
        const tableHead = createElement('thead');
        headerTable.appendChild(tableHead);
        if (this.parent.stackedHeaders.length > 0) {
            tableHead.appendChild(this.createStackedRow(this.parent.stackedHeaders));
        }
        const tr = createElement('tr', { className: HEADER_ROW_CLASS });
        tableHead.appendChild(tr);
        for (const column of this.parent.columns) {
            if (this.isColumnVisible(column)) {
                const index = this.parent.actionModule.columnToggleArray.indexOf(column.keyField.toString());
                const th = createElement('th', {
                    className: index === -1 ? HEADER_CELLS_CLASS : HEADER_CELLS_CLASS + ' ' + COLLAPSED_CLASS,
                    attrs: { 'data-role': 'kanban-column', 'data-key': column.keyField.toString() }
                });
                const classList = [];
                if (column.allowToggle) {
                    classList.push(HEADER_ROW_TOGGLE_CLASS);
                    if (!column.isExpanded) {
                        classList.push(COLLAPSED_CLASS);
                    }
                }
                addClass([th], classList);
                const headerWrapper = createElement('div', { className: HEADER_WRAP_CLASS });
                th.appendChild(headerWrapper);
                const noOfCard = this.parent.dataModule.isRemote() ?
                    this.parent.columnDataCount[column.keyField] : this.columnData[column.keyField].length;
                const headerTitle = createElement('div', { className: HEADER_TITLE_CLASS });
                headerWrapper.appendChild(headerTitle);
                if (column.template) {
                    const templateArgs = {
                        keyField: column.keyField, headerText: column.headerText, minCount: column.minCount, maxCount: column.maxCount,
                        allowToggle: column.allowToggle, isExpanded: column.isExpanded, showItemCount: column.showItemCount, count: noOfCard
                    };
                    addClass([th], TEMPLATE_CLASS);
                    const templateId = this.parent.element.id + '_columnTemplate';
                    const templateHeader = this.parent.templateParser(column.template)(templateArgs, this.parent, 'columnTemplate', templateId, false);
                    append(templateHeader, headerTitle);
                }
                else {
                    const header = createElement('div', { className: HEADER_TEXT_CLASS, innerHTML: column.headerText });
                    headerTitle.appendChild(header);
                    if (column.showItemCount) {
                        const itemCount = createElement('div', {
                            className: CARD_ITEM_COUNT_CLASS,
                            innerHTML: '- ' + noOfCard.toString() + ' ' + this.parent.localeObj.getConstant('items')
                        });
                        headerTitle.appendChild(itemCount);
                    }
                }
                if (column.allowToggle) {
                    const isExpand = (column.isExpanded && index === -1) ? true : false;
                    const name = (isExpand) ? COLUMN_EXPAND_CLASS : COLUMN_COLLAPSE_CLASS;
                    const icon = createElement('div', {
                        className: HEADER_ICON_CLASS + ' ' + ICON_CLASS + ' ' + name,
                        attrs: { 'tabindex': '0' }
                    });
                    icon.setAttribute('aria-label', isExpand ? column.keyField + ' Expand' : column.keyField + ' Collapse');
                    th.setAttribute('aria-expanded', isExpand.toString());
                    headerWrapper.appendChild(icon);
                }
                const dataObj = [{ keyField: column.keyField, textField: column.headerText, count: noOfCard }];
                const args = { data: dataObj, element: tr, cancel: false, requestType: 'headerRow' };
                this.parent.trigger(queryCellInfo, args, (columnArgs) => {
                    if (!columnArgs.cancel) {
                        tr.appendChild(th);
                    }
                });
            }
        }
    }
    renderContent() {
        const content = createElement('div', { className: CONTENT_CLASS });
        this.parent.element.appendChild(content);
        const contentWrap = createElement('div', { className: this.parent.swimlaneSettings.keyField ? SWIMLANE_CLASS : '' });
        content.appendChild(contentWrap);
        const contentTable = createElement('table', {
            className: TABLE_CLASS + ' ' + CONTENT_TABLE_CLASS,
            attrs: { 'role': 'presentation' }
        });
        contentWrap.appendChild(contentTable);
        this.renderColGroup(contentTable);
        const tBody = createElement('tbody');
        contentTable.appendChild(tBody);
        let isCollaspsed = false;
        for (const row of this.kanbanRows) {
            if (this.parent.swimlaneSettings.keyField && this.parent.swimlaneToggleArray.length !== 0) {
                const index = this.parent.swimlaneToggleArray.indexOf(row.keyField);
                isCollaspsed = index !== -1;
            }
            this.renderSingleContent(tBody, row, isCollaspsed);
        }
    }
    renderSingleContent(tBody, row, isCollaspsed) {
        const className = isCollaspsed ? CONTENT_ROW_CLASS + ' ' + COLLAPSED_CLASS : CONTENT_ROW_CLASS;
        const tr = createElement('tr', { className: className, attrs: { 'aria-expanded': 'true' } });
        for (const column of this.parent.columns) {
            if (this.isColumnVisible(column)) {
                const index = this.parent.actionModule.columnToggleArray.indexOf(column.keyField.toString());
                const className = index === -1 ? CONTENT_CELLS_CLASS : CONTENT_CELLS_CLASS + ' ' + COLLAPSED_CLASS;
                const dragClass = (column.allowDrag ? ' ' + DRAG_CLASS : '') + (column.allowDrop ? ' ' + DROP_CLASS
                    + ' ' + DROPPABLE_CLASS : '');
                const td = createElement('td', {
                    className: className + dragClass,
                    attrs: { 'data-role': 'kanban-column', 'data-key': column.keyField.toString(), 'aria-expanded': 'true',
                        'tabindex': '0', 'role': 'navigation' }
                });
                if (column.allowToggle && !column.isExpanded || index !== -1) {
                    addClass([td], COLLAPSED_CLASS);
                    const text = (column.showItemCount ? '[' + (this.parent.dataModule.isRemote() ?
                        this.parent.columnDataCount[column.keyField] : this.getColumnData(column.keyField).length) +
                        '] ' : '') + column.headerText;
                    td.appendChild(createElement('div', { className: COLLAPSE_HEADER_TEXT_CLASS, innerHTML: text }));
                    td.setAttribute('aria-expanded', 'false');
                }
                if (column.showAddButton) {
                    const button = createElement('div', { className: SHOW_ADD_BUTTON, attrs: { 'tabindex': '-1' } });
                    button.appendChild(createElement('div', { className: SHOW_ADD_ICON + ' ' + ICON_CLASS }));
                    td.appendChild(button);
                }
                tr.appendChild(td);
                if (this.parent.enableVirtualization) {
                    const headerHeight = this.parent.element.querySelector('.e-kanban-header').getBoundingClientRect().height;
                    //'15' is reduced for optimal padding in the bottom and to avoid page scrollbar appear in the height auto case.
                    if (this.parent.height === 'auto') {
                        td.style.height = window.innerHeight - (headerHeight + this.parent.element.getBoundingClientRect().top + 15) + 'px';
                    }
                    else if (typeof this.parent.height === 'string' && this.parent.height.endsWith('%') && this.parent.element.parentElement && (this.parent.element.parentElement.style.height !== '' || this.parent.element.parentElement.style.minHeight !== '')) {
                        const parentHeight = this.parent.element.parentElement.clientHeight;
                        const percentageValue = parseFloat(this.parent.height) / 100;
                        const pixelHeight = parentHeight * percentageValue;
                        td.style.height = pixelHeight - (headerHeight + 15) + 'px';
                    }
                    else {
                        td.style.height = parseInt(formatUnit(this.parent.height).split('px')[0], 10) - (headerHeight + 15) + 'px';
                    }
                }
            }
        }
        const dataObj = [{ keyField: row.keyField, textField: row.textField, count: row.count }];
        const args = { data: dataObj, element: tr, cancel: false, requestType: 'contentRow' };
        this.parent.trigger(queryCellInfo, args, (columnArgs) => {
            if (!columnArgs.cancel) {
                if (tBody.classList.contains('e-swimlane-row')) {
                    tBody.insertAdjacentElement('beforebegin', tr);
                }
                else {
                    tBody.appendChild(tr);
                }
            }
        });
    }
    windowResize() {
        const cloumnsTDElem = this.parent.element.querySelectorAll('.' + CONTENT_CELLS_CLASS);
        const headerHeight = this.parent.element.querySelector('.e-kanban-header').getBoundingClientRect().height;
        for (let j = 0; j < cloumnsTDElem.length; j++) {
            if (this.parent.height === 'auto') {
                cloumnsTDElem[j].style.height = window.innerHeight - (headerHeight + this.parent.element.getBoundingClientRect().top + 15) + 'px';
            }
            else {
                cloumnsTDElem[j].style.height = parseInt(formatUnit(this.parent.height).split('px')[0], 10) - (headerHeight + 15) + 'px';
            }
        }
    }
    refreshColumnData(draggedColumnKey, droppedColumnKey, requestType, crudKeyField) {
        const cardRows = [].slice.call(this.parent.element.querySelectorAll('.e-content-row:not(.e-swimlane-row)'));
        const isCRUD = (requestType === 'cardChanged' || requestType === 'cardCreated' || requestType === 'cardRemoved')
            && !isNullOrUndefined(crudKeyField);
        cardRows.forEach((tr) => {
            for (const column of this.parent.columns) {
                if (this.isColumnVisible(column) && (column.keyField === draggedColumnKey || column.keyField === droppedColumnKey)
                    || isCRUD) {
                    let cards = 0;
                    let blocks = [];
                    const columnData = this.getColumnCards()[column.keyField];
                    const currentColumnDataCount = this.parent.dataModule.isRemote() ?
                        this.parent.columnDataCount[column.keyField] : columnData.length;
                    const overallHeight = this.cardHeight * currentColumnDataCount;
                    // eslint-disable-next-line prefer-spread
                    blocks = Array.apply(null, Array(currentColumnDataCount)).map(() => ++cards);
                    const columnWrapper = tr.querySelector('[data-key="' + column.keyField + '"]');
                    const singleIndexCardCount = Math.ceil(parseInt(columnWrapper.style.height.split('px')[0], 10) / this.cardHeight);
                    this.offsets[1] = singleIndexCardCount * this.cardHeight;
                    for (let i = 1; i < blocks.length; i++) {
                        this.offsets[blocks[i]] = (this.offsets[blocks[i - 1]]) + (singleIndexCardCount * this.cardHeight);
                        this.tempOffsets[blocks[i]] = this.offsets[blocks[i] - 1] | 0;
                    }
                    const cardWrapper = columnWrapper.querySelector('.' + CARD_WRAPPER_CLASS);
                    const maxBlock = currentColumnDataCount % 2 === 0 ? currentColumnDataCount - 2 : currentColumnDataCount - 1;
                    const viewInfo = this.getInfoFromView(this.scrollStatus[column.keyField]);
                    const transformY = this.getTranslateY(viewInfo);
                    const cardVirtualElement = cardWrapper.querySelector('.' + CARD_VIRTUAL_WRAPPER_CLASS);
                    cardVirtualElement.style.maxHeight = currentColumnDataCount * this.cardHeight + 'px';
                    this.setPadding(transformY, cardVirtualElement, currentColumnDataCount);
                    this.currentStatus = {
                        column: column.keyField,
                        columnOverAllHeight: overallHeight,
                        columnHeight: parseInt(columnWrapper.style.height.split('px')[0], 10),
                        previousScrollTop: this.scrollStatus[column.keyField].currentScrollTop,
                        currentScrollTop: cardWrapper.scrollTop,
                        scrollDirection: this.scrollStatus[column.keyField].scrollDirection,
                        currentBlockIndex: this.scrollStatus[column.keyField].currentBlockIndex,
                        oldBlockIndex: this.scrollStatus[column.keyField].oldBlockIndex,
                        offsets: this.offsets,
                        tempOffsets: this.tempOffsets,
                        totalColumnData: currentColumnDataCount,
                        singleIndexCardCount: singleIndexCardCount,
                        maxBlock: maxBlock
                    };
                    this.scrollStatus[column.keyField] = this.currentStatus;
                }
            }
        });
    }
    renderCards() {
        const cardRows = [].slice.call(this.parent.element.querySelectorAll('.e-content-row:not(.e-swimlane-row)'));
        const swimlaneRows = [].slice.call(this.parent.element.querySelectorAll('.e-content-row.e-swimlane-row'));
        const removeTrs = [];
        let columnTransition = false;
        cardRows.forEach((tr, index) => {
            let dataCount = 0;
            for (const column of this.parent.columns) {
                if (this.isColumnVisible(column)) {
                    let cards = 0;
                    const currentScrollIndex = 0;
                    let blocks = [];
                    this.offsets = {};
                    this.tempOffsets = {};
                    const columnData = this.columnData[column.keyField];
                    const currentColumnDataCount = this.parent.dataModule.isRemote()
                        ? this.parent.columnDataCount[column.keyField] : columnData.length;
                    dataCount += currentColumnDataCount;
                    const overallHeight = (this.cardHeight * currentColumnDataCount) + 7; //7 is difference between top space of the scroll element
                    const columnWrapper = tr.querySelector('[data-key="' + column.keyField + '"]');
                    const singleIndexCardCount = Math.ceil(parseFloat(columnWrapper.style.height.split('px')[0]) / this.cardHeight);
                    const currentColumnBlock = singleIndexCardCount > currentColumnDataCount ? currentColumnDataCount :
                        Math.floor(currentColumnDataCount / singleIndexCardCount);
                    // eslint-disable-next-line prefer-spread
                    blocks = Array.apply(null, Array(currentColumnDataCount)).map(() => ++cards);
                    this.offsets[1] = singleIndexCardCount * this.cardHeight + 7;
                    for (let i = 1; i < blocks.length; i++) {
                        this.offsets[blocks[i]] = (this.offsets[blocks[i - 1]]) + (singleIndexCardCount * this.cardHeight);
                        this.tempOffsets[blocks[i]] = this.offsets[blocks[i] - 1] | 0;
                    }
                    const cardWrapper = createElement('div', {
                        className: CARD_WRAPPER_CLASS, attrs: { 'role': 'listbox' }
                    });
                    const cardVirtualWrapper = createElement('div', {
                        className: CARD_VIRTUAL_WRAPPER_CLASS, attrs: { 'role': 'listbox' }
                    });
                    cardWrapper.appendChild(cardVirtualWrapper);
                    const maxBlock = currentColumnBlock % 2 === 0 ? currentColumnBlock : currentColumnBlock + 1;
                    this.currentStatus = {
                        column: column.keyField,
                        columnOverAllHeight: overallHeight,
                        columnHeight: parseInt(columnWrapper.style.height.split('px')[0], 10),
                        previousScrollTop: null,
                        currentScrollTop: cardWrapper.scrollTop,
                        scrollDirection: null,
                        currentBlockIndex: [1, 2],
                        oldBlockIndex: [1, 2],
                        offsets: this.offsets,
                        tempOffsets: this.tempOffsets,
                        totalColumnData: currentColumnDataCount,
                        singleIndexCardCount: singleIndexCardCount,
                        maxBlock: maxBlock
                    };
                    this.scrollStatus[column.keyField] = this.currentStatus;
                    if (column.transitionColumns.length > 0) {
                        columnTransition = true;
                    }
                    if (!columnTransition && isNullOrUndefined(this.parent.swimlaneSettings.keyField)) {
                        const borderElem = createElement('div', { className: BORDER_CLASS });
                        columnWrapper.appendChild(borderElem);
                    }
                    columnWrapper.appendChild(cardWrapper);
                    if (currentColumnDataCount > 0) {
                        for (let i = currentScrollIndex; i < singleIndexCardCount * 2 && i < columnData.length; i++) {
                            const cardText = columnData[i][this.parent.cardSettings.headerField];
                            const cardIndex = this.parent.actionModule.selectionArray.indexOf(cardText);
                            const cardElement = this.renderCard(columnData[i]);
                            if (cardIndex !== -1) {
                                cardElement.setAttribute('aria-selected', 'true');
                                addClass([cardElement], CARD_SELECTION_CLASS);
                            }
                            const args = { data: columnData[i], element: cardElement, cancel: false };
                            this.parent.trigger(cardRendered, args, (cardArgs) => {
                                if (!cardArgs.cancel) {
                                    cardVirtualWrapper.appendChild(cardElement);
                                }
                            });
                        }
                        cardVirtualWrapper.style.maxHeight = this.cardHeight * currentColumnDataCount + 'px';
                    }
                    else {
                        cardVirtualWrapper.appendChild(this.renderEmptyCard());
                    }
                    this.setPadding(0, cardVirtualWrapper, currentColumnDataCount);
                }
            }
            if (dataCount === 0) {
                removeTrs.push(tr);
                if (swimlaneRows.length > 0) {
                    removeTrs.push(swimlaneRows[index]);
                }
            }
        });
        if (!this.parent.swimlaneSettings.showEmptyRow && (this.parent.kanbanData.length === 0 && !this.parent.showEmptyColumn)) {
            removeTrs.forEach((tr) => remove(tr));
        }
    }
    renderCard(data) {
        const cardElement = createElement('div', {
            className: CARD_CLASS,
            attrs: {
                'data-id': data[this.parent.cardSettings.headerField], 'data-key': data[this.parent.keyField],
                'aria-selected': 'false', 'tabindex': '-1', 'role': 'option'
            }
        });
        cardElement.style.height = this.cardHeight - 8 + 'px'; // Since in the public card height calculation margin bottom is added, so it reduced here.
        if (this.parent.cardSettings.template) {
            addClass([cardElement], TEMPLATE_CLASS);
            const templateId = this.parent.element.id + '_cardTemplate';
            const cardTemplate = this.parent.templateParser(this.parent.cardSettings.template)(data, this.parent, 'cardTemplate', templateId, false);
            append(cardTemplate, cardElement);
        }
        else {
            const tooltipClass = this.parent.enableTooltip ? ' ' + TOOLTIP_TEXT_CLASS : '';
            if (this.parent.cardSettings.showHeader) {
                const cardHeader = createElement('div', { className: CARD_HEADER_CLASS });
                const cardCaption = createElement('div', { className: CARD_HEADER_TEXT_CLASS });
                const cardText = createElement('div', {
                    className: CARD_HEADER_TITLE_CLASS + tooltipClass,
                    innerHTML: data[this.parent.cardSettings.headerField] || ''
                });
                cardHeader.appendChild(cardCaption);
                cardCaption.appendChild(cardText);
                cardElement.appendChild(cardHeader);
            }
            const cardContent = createElement('div', {
                className: CARD_CONTENT_CLASS + tooltipClass,
                innerHTML: data[this.parent.cardSettings.contentField] || ''
            });
            cardElement.appendChild(cardContent);
            if (this.parent.cardSettings.tagsField && data[this.parent.cardSettings.tagsField]) {
                const cardTags = createElement('div', { className: CARD_TAGS_CLASS });
                const tags = data[this.parent.cardSettings.tagsField].toString().split(',');
                for (const tag of tags) {
                    cardTags.appendChild(createElement('div', {
                        className: CARD_TAG_CLASS + ' ' + CARD_LABEL_CLASS, innerHTML: tag
                    }));
                }
                cardElement.appendChild(cardTags);
            }
            if (this.parent.cardSettings.grabberField && data[this.parent.cardSettings.grabberField]) {
                addClass([cardElement], CARD_COLOR_CLASS);
                cardElement.style.borderLeftColor = data[this.parent.cardSettings.grabberField];
            }
            if (this.parent.cardSettings.footerCssField) {
                const cardFields = createElement('div', { className: CARD_FOOTER_CLASS });
                const keys = data[this.parent.cardSettings.footerCssField].split(',');
                for (const key of keys) {
                    cardFields.appendChild(createElement('div', {
                        className: key.trim() + ' ' + CARD_FOOTER_CSS_CLASS
                    }));
                }
                cardElement.appendChild(cardFields);
            }
        }
        return cardElement;
    }
    renderEmptyCard() {
        const emptyCard = createElement('span', {
            className: EMPTY_CARD_CLASS,
            innerHTML: this.parent.localeObj.getConstant('noCard')
        });
        return emptyCard;
    }
    renderColGroup(table) {
        const colGroup = createElement('colgroup');
        this.parent.columns.forEach((column) => {
            if (this.isColumnVisible(column)) {
                const index = this.parent.actionModule.columnToggleArray.indexOf(column.keyField.toString());
                const isToggle = column.allowToggle && !column.isExpanded;
                const className = index === -1 ? (isToggle ? COLLAPSED_CLASS : '') : COLLAPSED_CLASS;
                const col = createElement('col', {
                    className: className,
                    attrs: { 'data-key': column.keyField.toString() },
                    styles: this.parent.isAdaptive ? 'width: ' +
                        (isToggle ? formatUnit(toggleWidth) : formatUnit(this.getWidth())) : ''
                });
                colGroup.appendChild(col);
            }
        });
        table.appendChild(colGroup);
    }
    getRows() {
        const kanbanRows = [];
        kanbanRows.push({ keyField: '', textField: '' });
        return kanbanRows;
    }
    createStackedRow(rows) {
        const tr = createElement('tr', { className: HEADER_ROW_CLASS + ' ' + STACKED_HEADER_ROW_CLASS });
        const stackedHeaders = [];
        this.parent.columns.forEach((column) => {
            let headerText = '';
            for (const row of rows) {
                if (row.keyFields.indexOf(column.keyField.toString()) !== -1) {
                    headerText = row.text;
                }
            }
            stackedHeaders.push(headerText);
        });
        for (let h = 0; h < stackedHeaders.length; h++) {
            let colSpan = 1;
            for (let j = h + 1; j < stackedHeaders.length; j++) {
                if ((stackedHeaders[h] !== '') && (stackedHeaders[j] !== '') && stackedHeaders[h] === stackedHeaders[j]) {
                    colSpan++;
                }
                else {
                    break;
                }
            }
            const div = createElement('div', { className: HEADER_TEXT_CLASS, innerHTML: stackedHeaders[h] });
            const th = createElement('th', {
                className: HEADER_CELLS_CLASS + ' ' + STACKED_HEADER_CELL_CLASS,
                attrs: { 'colspan': colSpan.toString() }
            });
            tr.appendChild(th).appendChild(div);
            h += colSpan - 1;
        }
        return tr;
    }
    scrollUiUpdate() {
        const header = this.parent.element.querySelector('.' + HEADER_CLASS);
        const content = this.parent.element.querySelector('.' + CONTENT_CLASS);
        let height = this.parent.element.offsetHeight - header.offsetHeight;
        if (this.parent.isAdaptive) {
            height = window.innerHeight - (header.offsetHeight + bottomSpace);
            const swimlaneToolbar = this.parent.element.querySelector('.' + SWIMLANE_HEADER_CLASS);
            if (swimlaneToolbar) {
                height -= swimlaneToolbar.offsetHeight;
            }
            const cardWrappers = [].slice.call(this.parent.element.querySelectorAll('.' + CONTENT_CELLS_CLASS));
            cardWrappers.forEach((cell) => {
                const cardWrapper = cell.querySelector('.' + CARD_WRAPPER_CLASS);
                if (!cardWrapper.classList.contains(MULTI_CARD_WRAPPER_CLASS)) {
                    cardWrapper.style.height = formatUnit(height);
                    EventHandler.add(cell, 'touchmove', this.onAdaptiveScroll, this);
                }
            });
        }
        if (this.parent.height !== 'auto' && this.parent.height !== '100%') {
            content.style.height = formatUnit(height);
        }
        [].slice.call(header.children).forEach((node) => {
            let paddingValue = 0;
            if ((content.offsetWidth - content.clientWidth) > 0) {
                paddingValue = 17;
                if ((content.offsetHeight - content.clientHeight) > 0) {
                    node.style.width = formatUnit(content.clientWidth);
                }
            }
            if (this.parent.enableRtl) {
                node.style.paddingLeft = formatUnit(paddingValue);
            }
            else {
                node.style.paddingRight = formatUnit(paddingValue);
            }
        });
        this.updateScrollPosition();
    }
    onContentScroll(e) {
        const target = e.target;
        const header = this.parent.element.querySelector('.' + HEADER_CLASS);
        [].slice.call(header.children).forEach((node) => { node.scrollLeft = target.scrollLeft; });
        this.parent.scrollPosition.content = { left: target.scrollLeft, top: target.scrollTop };
    }
    getOffset(block, viewInfo) {
        return Math.min(viewInfo.offsets[block] | 0, viewInfo.offsets[viewInfo.maxBlock] | 0);
    }
    getTranslateY(viewInfo) {
        const block = (viewInfo.newBlockIndex[0] || 1) - 1;
        const translate = this.getOffset(block, viewInfo);
        const endTranslate = this.getOffset(viewInfo.newBlockIndex[viewInfo.newBlockIndex.length - 1], viewInfo);
        const result = translate > viewInfo.currentScrollTop ?
            this.getOffset(block - 1, viewInfo) : endTranslate < (viewInfo.currentScrollTop + viewInfo.columnHeight) ?
            this.getOffset(block + 1, viewInfo) : translate;
        return result;
    }
    setPadding(paddingTop, scrollElem, dataCount, isScrolledToLast, direction) {
        if (isScrolledToLast && direction === 'down') {
            scrollElem.style.paddingTop = `${paddingTop}px`;
            scrollElem.style.paddingBottom = '0px';
        }
        else {
            scrollElem.style.paddingTop = `${paddingTop}px`;
            scrollElem.style.paddingBottom = `${this.cardHeight * dataCount - paddingTop}px`;
        }
    }
    getData(keyField, column, take, skip) {
        const query = this.query.clone();
        const predicate = new Predicate(keyField, 'equal', column, true);
        query.where(predicate);
        query.take(take);
        query.skip(skip);
        query.addParams('KanbanVirtualScroll', 'KanbanVirtualScroll');
        if (this.parent.dataSource && 'result' in this.parent.dataSource) {
            const def = this.eventPromise({ requestType: '' }, query);
            return def.promise;
        }
        return this.parent.dataModule.dataManager.executeQuery(query);
    }
    eventPromise(args, query) {
        const state = this.getStateEventArgument(query);
        const def = new Deferred();
        state.updateData = def.resolve;
        state.action = args;
        return def;
    }
    getStateEventArgument(query) {
        const adaptr = new UrlAdaptor();
        const dm = new DataManager({ url: '', adaptor: new UrlAdaptor });
        const state = adaptr.processQuery(dm, query);
        const data = JSON.parse(state.data);
        return extend(data, state.pvtData);
    }
    dataManagerSuccess(e, type) {
        let resultData;
        if (type) {
            resultData = extend([], !isNullOrUndefined(e.result.result) ?
                e.result.result : e.result, null, true);
        }
        else {
            this.parent.trigger(dataBinding, e, (args) => {
                resultData = extend([], !isNullOrUndefined(args.result.result) ?
                    args.result.result : args.result, null, true);
                this.parent.trigger(dataBound, null, () => this.parent.hideSpinner());
            });
        }
        return resultData;
    }
    dataManagerFailure(e) {
        if (this.parent.isDestroyed) {
            return;
        }
        this.parent.trigger(actionFailure, { error: e }, () => this.parent.hideSpinner());
    }
    onColScrollShowSkeleton(args) {
        const target = args.target;
        if (this.parent.element.querySelectorAll('.e-card-skeleton-wrapper').length > 0) {
            return;
        }
        const key = target.parentElement.getAttribute('data-key');
        const previousScrollTop = this.scrollStatus[key].previousScrollTop;
        const parentElemHeight = target.parentElement.clientHeight;
        if ((target.scrollTop - previousScrollTop) > parentElemHeight || (previousScrollTop - target.scrollTop) > parentElemHeight) {
            this.showSkeleton(target, this.scrollStatus[key].singleIndexCardCount);
        }
    }
    showSkeleton(cardWrapper, skeletonCount) {
        const cardVirtualSkeletonWrapper = createElement('div', {
            className: 'e-card-virtual-skeleton-wrapper', attrs: { 'role': 'listbox' }
        });
        cardWrapper.parentElement.insertBefore(cardVirtualSkeletonWrapper, cardWrapper);
        cardVirtualSkeletonWrapper.style.position = 'absolute';
        cardVirtualSkeletonWrapper.style.zIndex = '10';
        for (let j = 0; j < skeletonCount; j++) {
            const skeletonWrapper = createElement('div', { className: 'e-card-skeleton-wrapper' });
            const skeleton = createElement('span', { className: 'e-skeleton e-skeleton-text e-shimmer-wave' });
            skeleton.style.height = this.cardHeight + 'px';
            // Assumption fix, issue reproduce in rare cases only .
            if (!isNullOrUndefined(cardWrapper.querySelector('.e-card'))) {
                skeleton.style.width = cardWrapper.querySelector('.e-card').getBoundingClientRect().width + 'px';
            }
            skeletonWrapper.appendChild(skeleton);
            cardVirtualSkeletonWrapper.appendChild(skeletonWrapper);
        }
    }
    hideSkeleton(cardWrapper) {
        setTimeout(() => {
            const skeletonWrapper = cardWrapper.querySelectorAll('.e-card-virtual-skeleton-wrapper');
            for (let i = 0; i < skeletonWrapper.length; i++) {
                detach(skeletonWrapper[i]);
            }
        }, 50);
    }
    onColumnScroll(e) {
        const target = e.target;
        const currentScrolledHeight = target.scrollTop;
        let columnKey;
        if (target.offsetParent) {
            columnKey = target.offsetParent.getAttribute('data-key');
            this.parent.scrollPosition.column[columnKey] = { left: target.scrollLeft, top: target.scrollTop };
        }
        if (this.parent.enableVirtualization) {
            const cardWrapper = target;
            let dataCount = 0;
            const columnData = this.getColumnCards()[columnKey];
            const currentColumnDataCount = this.parent.dataModule.isRemote() ?
                this.parent.columnDataCount[columnKey] : columnData.length;
            dataCount += currentColumnDataCount;
            const overallHeight = this.cardHeight * dataCount;
            let removeIndex = [];
            let addIndex = [];
            this.checkScrollDirection(columnKey, currentScrolledHeight);
            if (this.findScrollSpeed(target, columnKey) === 'fast' && currentScrolledHeight > overallHeight) {
                this.hideSkeleton(cardWrapper.parentElement);
                return;
            }
            const maxBlock = this.scrollStatus[columnKey].maxBlock;
            const isLastBlockRendered = this.scrollStatus[columnKey].currentBlockIndex.indexOf(maxBlock) > -1;
            const isDuplicateScroll = e.timeStamp - this.scrollStatus[columnKey].previousTimeStamps < 300;
            if (isLastBlockRendered && !isNullOrUndefined(this.scrollStatus[columnKey].previousTimeStamps) && isDuplicateScroll) {
                this.hideSkeleton(cardWrapper.parentElement);
                return;
            }
            this.scrollStatus[columnKey].previousTimeStamps = e.timeStamp;
            const viewInfo = this.getInfoFromView(this.scrollStatus[columnKey]);
            removeIndex = viewInfo.currentBlockIndex.filter(function (val) {
                return viewInfo.newBlockIndex.indexOf(val) === -1;
            });
            addIndex = viewInfo.newBlockIndex.filter(function (val) {
                return viewInfo.currentBlockIndex.indexOf(val) === -1;
            });
            const isScrolledToLast = currentScrolledHeight + target.clientHeight >= overallHeight;
            const transformY = isScrolledToLast ? overallHeight - (cardWrapper.querySelector('.' + CARD_VIRTUAL_WRAPPER_CLASS).childElementCount * this.cardHeight)
                : this.getTranslateY(viewInfo);
            const cardVirtualElement = cardWrapper.querySelector('.' + CARD_VIRTUAL_WRAPPER_CLASS);
            if (removeIndex.length > 0) {
                let removeStartIndex;
                let removeEndIndex;
                if (removeIndex[0] === 1) {
                    removeStartIndex = 0;
                    removeEndIndex = (removeIndex.length * this.scrollStatus[columnKey].singleIndexCardCount) - 1;
                }
                else {
                    removeStartIndex = ((removeIndex[0] - 1) * this.scrollStatus[columnKey].singleIndexCardCount);
                    removeEndIndex = removeStartIndex + (removeIndex.length * this.scrollStatus[columnKey].singleIndexCardCount);
                }
                this.removeCardsOnScroll(cardVirtualElement, this.scrollStatus[columnKey].scrollDirection === 'down' ? true : false, removeStartIndex, removeEndIndex);
            }
            if (addIndex.length > 0) {
                if (this.parent.dataModule.isRemote()) {
                    const visibleStartIndex = ((addIndex[0] - 1) * this.scrollStatus[columnKey].singleIndexCardCount);
                    let resultData = [];
                    const dataManager = this.getData(this.parent.keyField, columnKey, (this.scrollStatus[columnKey].singleIndexCardCount * addIndex.length), visibleStartIndex);
                    dataManager.then((e) => {
                        resultData = this.dataManagerSuccess(e);
                        this.scrollCardInsert(columnKey, cardVirtualElement, target, currentScrolledHeight, cardWrapper, this.scrollStatus[columnKey].scrollDirection === 'down' ? 0 : (resultData.length - 1), true, resultData, null);
                    }).catch((e) => this.dataManagerFailure(e));
                }
                else {
                    const visibleStartIndex = this.scrollStatus[columnKey].scrollDirection === 'down' ? ((addIndex[0] - 1) * this.scrollStatus[columnKey].singleIndexCardCount) :
                        ((addIndex[0] - 1) * this.scrollStatus[columnKey].singleIndexCardCount)
                            + (this.scrollStatus[columnKey].singleIndexCardCount * addIndex.length) - 1;
                    const visibleLength = this.scrollStatus[columnKey].scrollDirection === 'down' ? visibleStartIndex + (this.scrollStatus[columnKey].singleIndexCardCount * addIndex.length) :
                        ((addIndex[0] - 1) * this.scrollStatus[columnKey].singleIndexCardCount);
                    this.scrollCardInsert(columnKey, cardVirtualElement, target, currentScrolledHeight, cardWrapper, visibleStartIndex, false, columnData, visibleLength);
                }
            }
            this.scrollStatus[columnKey].currentBlockIndex = this.scrollStatus[columnKey].newBlockIndex;
            this.setPadding(transformY, cardVirtualElement, currentColumnDataCount, isScrolledToLast, this.scrollStatus[columnKey].scrollDirection);
            viewInfo.currentBlockIndex = viewInfo.newBlockIndex;
            this.parent.renderTemplates();
            this.hideSkeleton(cardWrapper.parentElement);
        }
    }
    checkScrollDirection(columnKey, currentScrolledHeight) {
        // Update the previous and current scroll top value
        this.scrollStatus[columnKey].previousScrollTop = this.scrollStatus[columnKey].currentScrollTop;
        this.scrollStatus[columnKey].currentScrollTop = currentScrolledHeight;
        // Check the scroll direction
        if (currentScrolledHeight > this.scrollStatus[columnKey].previousScrollTop) {
            this.scrollStatus[columnKey].scrollDirection = 'down';
        }
        else {
            this.scrollStatus[columnKey].scrollDirection = 'up';
        }
    }
    findScrollSpeed(target, columnKey) {
        // Find the scroll speed
        if (this.scrollStatus[columnKey].scrollDirection === 'down' &&
            (target.scrollTop - this.scrollStatus[columnKey].previousScrollTop > target.clientHeight)) {
            return 'fast';
        }
        else if (this.scrollStatus[columnKey].scrollDirection === 'up' &&
            (this.scrollStatus[columnKey].previousScrollTop - target.scrollTop > target.clientHeight)) {
            return 'fast';
        }
        return 'slow';
    }
    removeCardsOnScroll(cardVirtualElement, isDown, removeStartIndex, removeEndIndex) {
        const columnKey = cardVirtualElement.parentElement.parentElement.getAttribute('data-key');
        const columnCardCount = this.scrollStatus[columnKey].totalColumnData;
        const columnThreshold = 30;
        if (columnCardCount <= columnThreshold) {
            return;
        }
        for (let j = removeStartIndex; j < removeEndIndex; j++) {
            let removableElem = isDown ? cardVirtualElement.firstChild
                : cardVirtualElement.lastChild;
            while (!isNullOrUndefined(removableElem) && (removableElem.classList.contains(DRAGGED_CARD_CLASS) ||
                removableElem.classList.contains(DRAGGED_CLONE_CLASS) ||
                removableElem.classList.contains(DROPPED_CLONE_CLASS) ||
                removableElem.classList.contains(CLONED_CARD_CLASS))) {
                removableElem = isDown ? removableElem.nextSibling : removableElem.previousSibling;
            }
            if (!isNullOrUndefined(removableElem)) {
                detach(removableElem);
            }
        }
    }
    scrollCardInsert(columnKey, cardVirtualElement, target, currentScrolledHeight, cardWrapper, startNumber, isRemote, resultData, visibleLength) {
        const conditonsScrollDownCase = isRemote ? resultData.length : visibleLength;
        const conditonsScrollUpCase = isRemote ? 0 : visibleLength;
        if (resultData.length > 0) {
            for (let j = startNumber; this.scrollStatus[columnKey].scrollDirection === 'down' ? (j < conditonsScrollDownCase) :
                j >= conditonsScrollUpCase; this.scrollStatus[columnKey].scrollDirection === 'down' ? j++ : j--) {
                if (!isNullOrUndefined(resultData[j])) {
                    const cardText = resultData[j][this.parent.cardSettings.headerField];
                    const cardIndex = this.parent.actionModule.selectionArray.indexOf(cardText);
                    const cardElement = this.renderCard(resultData[j]);
                    if (cardIndex !== -1) {
                        cardElement.setAttribute('aria-selected', 'true');
                        addClass([cardElement], CARD_SELECTION_CLASS);
                    }
                    const args = { data: resultData[j], element: cardElement, cancel: false };
                    this.parent.trigger(cardRendered, args, (cardArgs) => {
                        if (!cardArgs.cancel) {
                            if (this.scrollStatus[columnKey].scrollDirection === 'down') {
                                cardVirtualElement.appendChild(cardElement);
                            }
                            else {
                                cardVirtualElement.insertBefore(cardElement, cardVirtualElement.firstChild);
                            }
                            this.parent.dragAndDropModule.wireDragEvents(cardElement);
                            addClass([cardElement], DROPPABLE_CLASS);
                        }
                    });
                }
            }
            target.scrollTop = currentScrolledHeight;
        }
        else {
            cardWrapper.appendChild(this.renderEmptyCard());
        }
    }
    ensureColumnNotEmpty(draggedColumnKey) {
        const singleIndexCardCount = this.scrollStatus[draggedColumnKey].singleIndexCardCount;
        const draggedColumnData = this.columnData[draggedColumnKey];
        const draggedTdColummElement = this.parent.element.querySelector('.e-content-row:not(.e-swimlane-row) [data-key="' + draggedColumnKey + '"]');
        const wrapperELement = draggedTdColummElement.querySelector('.' + CARD_VIRTUAL_WRAPPER_CLASS);
        const cardsList = wrapperELement.querySelectorAll('.' + CARD_CLASS);
        if (cardsList.length > 0) {
            const lastCardDataId = cardsList[cardsList.length - 1].getAttribute('data-id');
            const firstCardDataId = cardsList[0].getAttribute('data-id');
            let lastCardIndex;
            let firstCardIndex;
            if (cardsList.length < singleIndexCardCount * 2) {
                for (let i = 0; i < draggedColumnData.length; i++) {
                    if (lastCardDataId === (draggedColumnData[i][this.parent.cardSettings.headerField]).toString()) {
                        lastCardIndex = i;
                    }
                    if (firstCardDataId === (draggedColumnData[i][this.parent.cardSettings.headerField]).toString()) {
                        firstCardIndex = i;
                    }
                }
                const cardCount = cardsList.length;
                for (let i = cardCount; i < singleIndexCardCount * 2; i++) {
                    const isLast = lastCardIndex === draggedColumnData.length - 1 ? true : false;
                    const nextCardIndex = lastCardIndex < draggedColumnData.length ? lastCardIndex + 1 : firstCardIndex - 1;
                    if (nextCardIndex <= draggedColumnData.length) {
                        const nextCardData = draggedColumnData[nextCardIndex];
                        if (!isNullOrUndefined(nextCardData)) {
                            const nextCard = this.renderCard(nextCardData);
                            this.triggerCardRendering(nextCard, nextCardIndex, draggedColumnData, wrapperELement, isLast);
                            if (isLast) {
                                firstCardIndex = nextCardIndex;
                            }
                            else {
                                lastCardIndex = nextCardIndex;
                            }
                        }
                    }
                }
            }
        }
    }
    triggerCardRendering(nextCard, nextCardIndex, draggedColumnData, wrapperELement, isLast) {
        const cardText = draggedColumnData[nextCardIndex][this.parent.cardSettings.headerField];
        const cardIndex = this.parent.actionModule.selectionArray.indexOf(cardText);
        if (cardIndex !== -1) {
            nextCard.setAttribute('aria-selected', 'true');
            addClass([nextCard], CARD_SELECTION_CLASS);
        }
        const args = { data: draggedColumnData[nextCardIndex], element: nextCard, cancel: false };
        this.parent.trigger(cardRendered, args, (cardArgs) => {
            if (!cardArgs.cancel) {
                if (!isLast) {
                    wrapperELement.appendChild(nextCard);
                }
                else {
                    wrapperELement.insertBefore(nextCard, wrapperELement.querySelectorAll('.' + CARD_CLASS)[0]);
                }
                this.parent.dragAndDropModule.wireDragEvents(nextCard);
                addClass([nextCard], DROPPABLE_CLASS);
            }
        });
    }
    ensureBlocks(info) {
        let index = info.newBlockIndex[info.block];
        const maxPage = Math.ceil(info.totalColumnData / info.singleIndexCardCount);
        const max = Math.max;
        let indexes;
        if (info.scrollDirection === 'down') {
            indexes = index >= maxPage ? [max(index, 1), --index, --index].reverse() :
                (index + 1 >= maxPage ? [max(index - 1, 1), index, ++index] :
                    [max(index, 1), ++index, ++index]);
        }
        else {
            indexes = index === maxPage ? [max(index - 2, 1), max(index - 1, 1), index] :
                [max(index - 1, 1), index, index + 1];
        }
        // eslint-disable-next-line
        return indexes.filter(indexRemoveZero => indexRemoveZero > 0);
    }
    getInfoFromView(scrollStatus) {
        let isBlockAdded = false;
        const infoType = scrollStatus;
        infoType.page = this.getPageFromTop(scrollStatus);
        infoType.newBlockIndex = this.getBlockIndexes(infoType.page);
        const blocks = this.ensureBlocks(infoType);
        if (infoType.newBlockIndex.toString() !== blocks.toString()) {
            // To avoid dupilcate row index problem in key focus support
            const newBlock = blocks[blocks.length - 1];
            if (infoType.newBlockIndex.indexOf(newBlock) === -1) {
                isBlockAdded = true;
            }
        }
        infoType.newBlockIndex = isBlockAdded ? blocks : infoType.newBlockIndex;
        return infoType;
    }
    getBlockIndexes(page) {
        return [page + (page - 1), page * 2];
    }
    getPageFromTop(info) {
        const total = info.totalColumnData;
        let page = 0;
        this.offsetKeys = Object.keys(info.offsets);
        this.offsetKeys.some((offset) => {
            let iOffset = Number(offset);
            const border = info.currentScrollTop <= info.offsets[parseInt(offset, 10)]
                || (iOffset === total && info.currentScrollTop > info.offsets[parseInt(offset, 10)]);
            if (border) {
                const maxPage = Math.ceil(total / info.singleIndexCardCount);
                if (this.offsetKeys.length % 2 !== 0 && iOffset.toString() === this.offsetKeys[this.offsetKeys.length - 2]
                    && info.currentScrollTop <= info.offsets[this.offsetKeys.length - 1]) {
                    iOffset = (iOffset + 1) > maxPage ? maxPage : iOffset + 1;
                }
                iOffset = iOffset > maxPage ? maxPage : iOffset;
                info.block = iOffset % 2 === 0 ? 1 : 0;
                page = Math.max(1, Math.min(this.getPage(iOffset, maxPage), maxPage));
            }
            return border;
        });
        return page;
    }
    getPage(block, maxPage) {
        if (block + 1 > maxPage) {
            return block % 2 === 0 ? block / 2 : (block - 1) / 2;
        }
        else {
            return block % 2 === 0 ? block / 2 : (block + 1) / 2;
        }
    }
    onAdaptiveScroll(e) {
        if (this.parent.touchModule.tabHold && !this.parent.touchModule.mobilePopup) {
            e.preventDefault();
        }
    }
    /**
     * Check column is visible or not.
     *
     * @param {ColumnsModel} column - specifies the column.
     * @returns {boolean} - Check column is visible or not.
     * @private
     * @hidden
     */
    isColumnVisible(column) {
        let isVisible = false;
        const isNumeric = typeof column.keyField === 'number';
        if (isNumeric) {
            isVisible = this.parent.actionModule.hideColumnKeys.indexOf(column.keyField.toString()) === -1;
        }
        else {
            column.keyField.split(',').forEach((key) => { isVisible = this.parent.actionModule.hideColumnKeys.indexOf(key) === -1; });
        }
        return isVisible;
    }
    renderLimits(column, target) {
        const limits = createElement('div', { className: LIMITS_CLASS });
        if (column.minCount) {
            limits.appendChild(createElement('div', {
                className: MIN_COUNT_CLASS,
                innerHTML: this.parent.localeObj.getConstant('min') + ': ' + column.minCount.toString()
            }));
        }
        if (column.maxCount) {
            limits.appendChild(createElement('div', {
                className: MAX_COUNT_CLASS,
                innerHTML: this.parent.localeObj.getConstant('max') + ': ' + column.maxCount.toString()
            }));
        }
        if (limits.childElementCount > 0) {
            if (target.querySelector('.' + CARD_WRAPPER_CLASS)) {
                target.insertBefore(limits, target.firstElementChild);
            }
            else {
                target.appendChild(limits);
            }
        }
    }
    renderValidation() {
        this.parent.columns.forEach((column) => {
            if (!column.minCount && !column.maxCount) {
                return;
            }
            const cardData = this.columnData[column.keyField];
            const keySelector = `[data-key="${column.keyField}"]`;
            const headerCell = this.parent.element.querySelector(`.${HEADER_CELLS_CLASS + keySelector}`);
            const rowCells = [].slice.call(this.parent.element.querySelectorAll(`.${CONTENT_CELLS_CLASS + keySelector}`));
            this.renderLimits(column, headerCell);
            const colorClass = this.getValidationClass(column, cardData.length);
            if (colorClass) {
                addClass(rowCells.concat(headerCell), colorClass);
            }
        });
    }
    getValidationClass(column, count) {
        let colorClass;
        if (column.maxCount && count > column.maxCount) {
            colorClass = MAX_COLOR_CLASS;
        }
        else if (column.minCount && count < column.minCount) {
            colorClass = MIN_COLOR_CLASS;
        }
        return colorClass;
    }
    refreshValidation() {
        const validations = [].slice.call(this.parent.element.querySelectorAll('.' + LIMITS_CLASS));
        validations.forEach((node) => { remove(node); });
        const minClass = [].slice.call(this.parent.element.querySelectorAll('.' + MIN_COLOR_CLASS));
        removeClass(minClass, MIN_COLOR_CLASS);
        const maxClass = [].slice.call(this.parent.element.querySelectorAll('.' + MAX_COLOR_CLASS));
        removeClass(maxClass, MAX_COLOR_CLASS);
        this.renderValidation();
    }
    getColumnData(columnValue, dataSource = this.parent.kanbanData) {
        let cardData = [];
        const isNumeric = typeof columnValue === 'number';
        if (isNumeric) {
            const keyData = dataSource.filter((cardObj) => cardObj[this.parent.keyField] === columnValue);
            cardData = cardData.concat(keyData);
        }
        else {
            const columnKeys = columnValue.split(',');
            for (const key of columnKeys) {
                const keyData = dataSource.filter((cardObj) => cardObj[this.parent.keyField] === key.trim());
                cardData = cardData.concat(keyData);
            }
        }
        this.sortCategory(cardData);
        return cardData;
    }
    sortCategory(cardData) {
        let key = this.parent.cardSettings.headerField;
        const direction = this.parent.sortSettings.direction;
        switch (this.parent.sortSettings.sortBy) {
            case 'DataSourceOrder':
                this.sortOrder(key, direction, cardData);
                break;
            case 'Custom':
            case 'Index':
                if (this.parent.sortSettings.field) {
                    key = this.parent.sortSettings.field;
                }
                this.sortOrder(key, direction, cardData);
                break;
        }
        return cardData;
    }
    sortOrder(key, direction, cardData) {
        let isNumeric = true;
        if (this.parent.kanbanData.length > 0) {
            isNumeric = typeof (this.parent.kanbanData[0])[key] === 'number';
        }
        if (!isNumeric && this.parent.sortSettings.sortBy === 'Index') {
            return cardData;
        }
        let first;
        let second;
        cardData = cardData.sort((firstData, secondData) => {
            if (!isNumeric) {
                first = firstData[key].toLowerCase();
                second = secondData[key].toLowerCase();
            }
            else {
                first = firstData[key];
                second = secondData[key];
            }
            return (first > second) ? 1 : ((second > first) ? -1 : 0);
        });
        if (direction === 'Descending') {
            cardData.reverse();
        }
        return cardData;
    }
    documentClick(args) {
        if (args.target.classList.contains(SWIMLANE_OVERLAY_CLASS) &&
            this.parent.element.querySelector('.' + SWIMLANE_RESOURCE_CLASS).classList.contains('e-popup-open')) {
            this.treePopup.hide();
            removeClass([this.popupOverlay], 'e-enable');
        }
        if (closest(args.target, `.${ROOT_CLASS}`)) {
            return;
        }
        const cards = [].slice.call(this.parent.element.querySelectorAll(`.${CARD_CLASS}.${CARD_SELECTION_CLASS}`));
        removeClass(cards, CARD_SELECTION_CLASS);
        this.disableAttributeSelection(cards);
    }
    disableAttributeSelection(cards) {
        if (cards instanceof Element) {
            cards.setAttribute('aria-selected', 'false');
        }
        else {
            cards.forEach((card) => { card.setAttribute('aria-selected', 'false'); });
        }
    }
    getColumnCards(data) {
        const columnData = {};
        this.columnKeys = [];
        this.parent.columns.forEach((column) => {
            const isNumeric = typeof column.keyField === 'number';
            if (isNumeric) {
                this.columnKeys = this.columnKeys.concat(column.keyField.toString());
            }
            else {
                this.columnKeys = this.columnKeys.concat(column.keyField.split(',').map((e) => e.trim()));
            }
            const cardData = this.getColumnData(column.keyField, data);
            columnData[column.keyField] = cardData;
        });
        return columnData;
    }
    refreshHeaders() {
        const header = this.parent.element.querySelector('.' + HEADER_CLASS);
        [].slice.call(header.children).forEach((child) => remove(child));
        this.renderHeader(header);
    }
    refreshCards() {
        this.parent.resetTemplates(['cardTemplate']);
        const cards = [].slice.call(this.parent.element.querySelectorAll('.' + CARD_VIRTUAL_WRAPPER_CLASS));
        cards.forEach((card) => remove(card));
        this.renderCards();
        this.wireDragEvent();
        this.parent.renderTemplates();
    }
    refresh() {
        let isColumnTemplateRefreshed = false;
        this.parent.columns.forEach((column) => {
            if (column.showItemCount) {
                if (column && column.template && !isColumnTemplateRefreshed) {
                    this.refreshHeaders();
                    isColumnTemplateRefreshed = true;
                }
                const countSelector = `.${HEADER_CELLS_CLASS}[data-key="${column.keyField}"] .${CARD_ITEM_COUNT_CLASS}`;
                const itemCount = this.parent.element.querySelector(countSelector);
                if (itemCount) {
                    const columnDataLength = this.parent.dataModule.isRemote() ? this.parent.columnDataCount[column.keyField]
                        : this.columnData[column.keyField].length;
                    const isNumeric = typeof column.keyField === 'number';
                    let cardLength = 0;
                    if (isNumeric) {
                        cardLength = ([].slice.call(this.parent.element.querySelectorAll('.' + CARD_CLASS + '[data-key="' + column.keyField + '"]'))).length;
                    }
                    else {
                        const keys = column.keyField.split(',');
                        for (const key of keys) {
                            const cards = [].slice.call(this.parent.element.querySelectorAll('.' + CARD_CLASS + '[data-key="' + key.trim() + '"]'));
                            cardLength = cards.length + cardLength;
                        }
                    }
                    itemCount.innerHTML = '- ' + columnDataLength + ' ' + this.parent.localeObj.getConstant('items');
                }
            }
        });
        this.refreshValidation();
    }
    updateScrollPosition() {
        const content = this.parent.element.querySelector('.' + CONTENT_CLASS);
        if (content) {
            if (!Browser.isIE) {
                content.scrollTo(this.parent.scrollPosition.content.left, this.parent.scrollPosition.content.top);
            }
            else {
                content.scrollTop = this.parent.scrollPosition.content.top;
                content.scrollLeft = this.parent.scrollPosition.content.left;
            }
        }
        const cardWrapper = [].slice.call(this.parent.element.querySelectorAll('.' + CARD_WRAPPER_CLASS));
        cardWrapper.forEach((wrapper) => {
            if (wrapper.offsetParent) {
                const scrollData = this.parent.scrollPosition.column[wrapper.offsetParent.getAttribute('data-key')];
                if (scrollData) {
                    if (!Browser.isIE) {
                        wrapper.scrollTo(scrollData.left, scrollData.top);
                    }
                    else {
                        wrapper.scrollTop = scrollData.top;
                        wrapper.scrollLeft = scrollData.left;
                    }
                }
            }
        });
    }
    renderCardBasedOnIndex(data, index, isDropped, requestType) {
        const key = data[this.parent.keyField];
        const cardRow = this.parent.element.querySelector('.e-content-row:not(.e-swimlane-row)');
        if (this.parent.sortSettings.sortBy !== 'Index') {
            let field = this.parent.cardSettings.headerField;
            if (this.parent.sortSettings.sortBy === 'Custom') {
                field = this.parent.sortSettings.field;
            }
            if (isNullOrUndefined(this.parent.swimlaneSettings.keyField)) {
                index = this.getColumnData(key, this.parent.kanbanData).findIndex((colData) => colData[field] === data[field]);
            }
            else {
                const swimlaneDatas = this.parent.getSwimlaneData(data[this.parent.swimlaneSettings.keyField]);
                index = this.getColumnData(key, swimlaneDatas).findIndex((colData) => colData[field] === data[field]);
            }
        }
        else if (this.parent.sortSettings.sortBy === 'Index' &&
            this.parent.sortSettings.field && this.parent.sortSettings.direction === 'Ascending') {
            index = data[this.parent.sortSettings.field] - 1;
        }
        if (cardRow) {
            const td = [].slice.call(cardRow.children).filter((e) => e.getAttribute('data-key').replace(/\s/g, '').split(',').indexOf(key.toString().replace(/\s/g, '')) !== -1)[0];
            const cardWrapper = td.querySelector('.' + CARD_VIRTUAL_WRAPPER_CLASS);
            const emptyCard = cardWrapper.querySelector('.' + EMPTY_CARD_CLASS);
            if (emptyCard) {
                remove(emptyCard);
            }
            const cardElement = this.renderCard(data);
            if (this.parent.allowDragAndDrop && td.classList.contains(DRAG_CLASS)) {
                this.parent.dragAndDropModule.wireDragEvents(cardElement);
                addClass([cardElement], DROPPABLE_CLASS);
            }
            const args = { data: data, element: cardElement, cancel: false };
            this.parent.trigger(cardRendered, args, (cardArgs) => {
                const addCardCondition = isDropped ? true : cardWrapper.childNodes.length
                    < this.scrollStatus[key].singleIndexCardCount;
                if (!cardArgs.cancel && addCardCondition || !isNullOrUndefined(requestType)) {
                    if (isNullOrUndefined(index) || cardWrapper.children.length === 0) {
                        cardWrapper.appendChild(cardElement);
                    }
                    else {
                        cardWrapper.insertBefore(cardElement, cardWrapper.childNodes[index]);
                    }
                }
            });
        }
    }
    removeCard(data) {
        const cardKey = data[this.parent.cardSettings.headerField];
        const cardElement = this.parent.element.querySelector(`.${CARD_CLASS}[data-id="${cardKey}"]`);
        if (cardElement) {
            this.isSelectedCard = cardElement.classList.contains(CARD_SELECTION_CLASS) ? true : false;
            const cardContainer = cardElement.parentElement;
            remove(cardElement);
            if (cardContainer.querySelectorAll('.' + CARD_CLASS + ':not(.' + CLONED_CARD_CLASS + ')').length === 0) {
                cardContainer.appendChild(this.renderEmptyCard());
            }
        }
    }
    wireEvents() {
        EventHandler.add(this.parent.element, 'click', this.parent.actionModule.clickHandler, this.parent.actionModule);
        EventHandler.add(this.parent.element, 'dblclick', this.parent.actionModule.doubleClickHandler, this.parent.actionModule);
        EventHandler.add(document, Browser.touchStartEvent, this.documentClick, this);
        window.addEventListener('resize', this.winResize);
        const content = this.parent.element.querySelector('.' + CONTENT_CLASS);
        EventHandler.add(content, 'scroll', this.onContentScroll, this);
        const cardWrapper = [].slice.call(this.parent.element.querySelectorAll('.' + CARD_WRAPPER_CLASS));
        cardWrapper.forEach((wrapper) => {
            if (this.parent.isInitialRender && wrapper.offsetParent) {
                this.parent.scrollPosition.column[wrapper.offsetParent.getAttribute('data-key')] = { left: 0, top: 0 };
            }
            EventHandler.add(wrapper, 'scroll', this.onColScrollShowSkeleton, this);
            EventHandler.add(wrapper, 'scroll', debounce(this.onColumnScroll, 200), this);
        });
        if (this.parent.isAdaptive) {
            this.parent.touchModule.wireTouchEvents();
            content.scrollLeft = this.scrollLeft;
        }
        this.wireDragEvent();
        if (this.parent.allowColumnDragAndDrop) {
            this.parent.wireColumnDragEvent();
        }
    }
    unWireEvents() {
        EventHandler.remove(this.parent.element, 'click', this.parent.actionModule.clickHandler);
        EventHandler.remove(this.parent.element, 'dblclick', this.parent.actionModule.doubleClickHandler);
        EventHandler.remove(document, Browser.touchStartEvent, this.documentClick);
        window.removeEventListener('resize', this.winResize);
        this.winResize = null;
        const content = this.parent.element.querySelector('.' + CONTENT_CLASS);
        if (content) {
            EventHandler.remove(content, 'scroll', this.onContentScroll);
            EventHandler.remove(content, 'scroll', this.onColScrollShowSkeleton);
            if (this.parent.allowDragAndDrop) {
                this.unWireDragEvent();
            }
        }
        const cardWrapper = [].slice.call(this.parent.element.querySelectorAll('.' + CARD_WRAPPER_CLASS));
        if (cardWrapper.length > 0) {
            cardWrapper.forEach((wrapper) => { EventHandler.remove(wrapper, 'scroll', debounce(this.onColumnScroll, 200)); });
        }
        if (this.parent.isAdaptive) {
            this.parent.touchModule.unWireTouchEvents();
        }
        if (this.parent.allowColumnDragAndDrop) {
            this.parent.unwireColumnDragEvent();
        }
    }
    wireDragEvent() {
        if (this.parent.allowDragAndDrop) {
            const cards = [].slice.call(this.parent.element.querySelectorAll('.' + CONTENT_CELLS_CLASS
                + '.' + DRAG_CLASS + ' .' + CARD_CLASS));
            addClass(cards, DROPPABLE_CLASS);
            if (cards.length > 0) {
                cards.forEach((card) => this.parent.dragAndDropModule.wireDragEvents(card));
            }
        }
    }
    unWireDragEvent() {
        const cards = [].slice.call(this.parent.element.querySelectorAll('.' + CONTENT_CELLS_CLASS
            + '.' + DRAG_CLASS + ' .' + CARD_CLASS));
        removeClass(cards, DROPPABLE_CLASS);
        if (cards.length > 0) {
            cards.forEach((card) => this.parent.dragAndDropModule.unWireDragEvents(card));
        }
    }
    destroy() {
        this.parent.resetTemplates();
        this.parent.off(dataReady, this.initRender);
        this.parent.off(contentReady, this.scrollUiUpdate);
        this.unWireEvents();
        const header = this.parent.element.querySelector('.' + HEADER_CLASS);
        if (header) {
            remove(header);
        }
        const content = this.parent.element.querySelector('.' + CONTENT_CLASS);
        if (content) {
            remove(content);
        }
        if (this.treeViewObj) {
            this.treeViewObj.destroy();
            this.treeViewObj = null;
        }
        if (this.treePopup) {
            this.treePopup.destroy();
            this.treePopup = null;
        }
        const swimlaneToolBarEle = this.parent.element.querySelector('.' + SWIMLANE_HEADER_CLASS);
        if (swimlaneToolBarEle) {
            remove(swimlaneToolBarEle);
        }
        const swimlaneContent = this.parent.element.querySelector('.' + SWIMLANE_CONTENT_CLASS);
        if (swimlaneContent) {
            remove(swimlaneContent);
        }
    }
}

var __decorate$6 = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
/**
 * The Kanban board is an efficient way to visually depict various stages of a process using cards with transparent workflows.
 * The Kanban board has rich set of APIs, methods, and events used to enable or disable its features and customize them.
 * ```html
 * <div id="kanban"></div>
 * ```
 * ```typescript
 * <script>
 *   var kanbanObj = new Kanban();
 *   kanbanObj.appendTo("#kanban");
 * </script>
 * ```
 */
let Kanban = class Kanban extends Component {
    /**
     * Constructor for creating the Kanban widget
     *
     * @param {KanbanModel} options Accepts the kanban properties to render the Kanban board.
     * @param {string | HTMLElement} element Accepts the DOM element reference as either selector or element to render the Kanban Board.
     */
    constructor(options, element) {
        super(options, element);
        this.columnDataCount = {};
        this.needsID = true;
    }
    /**
     * Initializes the values of private members.
     *
     * @returns {void}
     * @private
     */
    preRender() {
        this.isAdaptive = Browser.isDevice;
        this.kanbanData = [];
        if (!this.enablePersistence || !this.swimlaneToggleArray) {
            this.swimlaneToggleArray = [];
        }
        this.activeCardData = { data: null, element: null };
        const defaultLocale = {
            items: 'items',
            min: 'Min',
            max: 'Max',
            cardsSelected: 'Cards Selected',
            addTitle: 'Add New Card',
            editTitle: 'Edit Card Details',
            deleteTitle: 'Delete Card',
            deleteContent: 'Are you sure you want to delete this card?',
            save: 'Save',
            delete: 'Delete',
            cancel: 'Cancel',
            yes: 'Yes',
            no: 'No',
            close: 'Close',
            noCard: 'No cards to display',
            unassigned: 'Unassigned',
            cards: 'Cards'
        };
        this.localeObj = new L10n(this.getModuleName(), defaultLocale, this.locale);
        this.scrollPosition = { content: { left: 0, top: 0 }, column: {} };
        this.isInitialRender = true;
    }
    /**
     * To provide the array of modules needed for control rendering
     *
     * @returns {ModuleDeclaration[]} Returns the declared modules.
     * @private
     */
    requiredModules() {
        const modules = [];
        return modules;
    }
    /**
     * Returns the properties to be maintained in the persisted state.
     *
     * @returns {string} Returns the persistance state.
     * @private
     */
    getPersistData() {
        if (this.dataSource.length > 0) {
            return this.addOnPersist(['columns', 'dataSource', 'swimlaneToggleArray']);
        }
        else {
            return this.addOnPersist(['columns', 'kanbanData', 'swimlaneToggleArray']);
        }
    }
    /**
     * Core method to return the module name.
     *
     * @returns {string} Returns the module name.
     * @private
     */
    getModuleName() {
        return 'kanban';
    }
    /**
     * Core method that initializes the control rendering.
     *
     * @returns {void}
     * @private
     */
    render() {
        const addClasses = [ROOT_CLASS];
        const removeClasses = [];
        if (this.enableRtl) {
            addClasses.push(RTL_CLASS);
        }
        else {
            removeClasses.push(RTL_CLASS);
        }
        if (this.isAdaptive) {
            addClasses.push(DEVICE_CLASS);
        }
        else {
            removeClasses.push(DEVICE_CLASS);
        }
        if (this.cssClass) {
            addClasses.push(this.cssClass);
        }
        classList(this.element, addClasses, removeClasses);
        this.element.style.width = formatUnit(this.width);
        this.element.style.height = formatUnit(this.height);
        this.element.setAttribute('role', 'application');
        this.element.setAttribute('aria-label', 'Kanban Board');
        createSpinner({ target: this.element });
        this.showSpinner();
        this.initializeModules();
    }
    /**
     * Called internally, if any of the property value changed.
     *
     * @param {KanbanModel} newProp Gets the updated values
     * @param {KanbanModel} oldProp Gets the previous values
     * @returns {void}
     * @private
     */
    onPropertyChanged(newProp, oldProp) {
        for (const prop of Object.keys(newProp)) {
            switch (prop) {
                case 'cssClass':
                    if (oldProp.cssClass) {
                        removeClass([this.element], oldProp.cssClass);
                    }
                    if (newProp.cssClass) {
                        addClass([this.element], newProp.cssClass);
                    }
                    break;
                case 'enableRtl':
                case 'locale':
                    this.refresh();
                    break;
                case 'width':
                    this.element.style.width = formatUnit(newProp.width);
                    this.element.querySelector('.' + HEADER_CLASS).firstElementChild.style.width = 'auto';
                    this.notify(contentReady, {});
                    break;
                case 'height':
                    this.element.style.height = formatUnit(newProp.height);
                    this.element.querySelector('.' + CONTENT_CLASS).style.height = 'auto';
                    this.notify(contentReady, {});
                    break;
                case 'dataSource':
                case 'query':
                    if (this.dataModule) {
                        this.dataModule.setState({ isDataChanged: false });
                    }
                    this.dataModule = new Data(this);
                    break;
                case 'columns':
                case 'constraintType':
                    this.notify(dataReady, { processedData: this.kanbanData });
                    break;
                case 'swimlaneSettings':
                    this.onSwimlaneSettingsPropertyChanged(newProp.swimlaneSettings, oldProp.swimlaneSettings);
                    break;
                case 'cardSettings':
                    this.onCardSettingsPropertyChanged(newProp.cardSettings, oldProp.cardSettings);
                    break;
                case 'allowDragAndDrop':
                    if (newProp.allowDragAndDrop) {
                        if (this.enableVirtualization) {
                            this.virtualLayoutModule.wireDragEvent();
                        }
                        else {
                            this.layoutModule.wireDragEvent();
                        }
                    }
                    else {
                        if (this.enableVirtualization) {
                            this.virtualLayoutModule.unWireDragEvent();
                        }
                        else {
                            this.layoutModule.unWireDragEvent();
                        }
                    }
                    break;
                case 'allowColumnDragAndDrop':
                    if (newProp.allowColumnDragAndDrop) {
                        if (this.enableVirtualization) {
                            this.wireColumnDragEvent();
                        }
                        else {
                            this.wireColumnDragEvent();
                        }
                    }
                    else {
                        if (this.enableVirtualization) {
                            this.unwireColumnDragEvent();
                        }
                        else {
                            this.unwireColumnDragEvent();
                        }
                    }
                    break;
                case 'enableTooltip':
                    if (this.tooltipModule) {
                        this.tooltipModule.destroy();
                        this.tooltipModule = null;
                    }
                    if (newProp.enableTooltip) {
                        this.tooltipModule = new KanbanTooltip(this);
                        if (this.enableVirtualization) {
                            this.virtualLayoutModule.refreshCards();
                        }
                        else {
                            this.layoutModule.refreshCards();
                        }
                    }
                    break;
                case 'dialogSettings':
                    if (newProp.dialogSettings) {
                        this.dialogModule = new KanbanDialog(this);
                    }
                    break;
                case 'allowKeyboard':
                    if (this.keyboardModule) {
                        this.keyboardModule.destroy();
                        this.keyboardModule = null;
                    }
                    if (newProp.allowKeyboard) {
                        this.keyboardModule = new Keyboard(this);
                    }
                    break;
                case 'stackedHeaders':
                    if (this.enableVirtualization) {
                        this.virtualLayoutModule.refreshHeaders();
                    }
                    else {
                        this.layoutModule.refreshHeaders();
                    }
                    break;
                case 'sortSettings':
                    this.notify(dataReady, { processedData: this.kanbanData });
                    break;
            }
        }
    }
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    onSwimlaneSettingsPropertyChanged(newProp, _oldProp) {
        for (const prop of Object.keys(newProp)) {
            switch (prop) {
                case 'keyField':
                case 'textField':
                case 'showEmptyRow':
                case 'showItemCount':
                case 'template':
                case 'sortDirection':
                    this.notify(dataReady, { processedData: this.kanbanData });
                    break;
                case 'enableFrozenRows':
                    if (this.layoutModule.frozenSwimlaneRow && !this.swimlaneSettings.enableFrozenRows) {
                        this.layoutModule.removeFrozenRows();
                    }
                    break;
            }
        }
    }
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    onCardSettingsPropertyChanged(newProp, _oldProp) {
        let cards = [];
        for (const prop of Object.keys(newProp)) {
            switch (prop) {
                case 'showHeader':
                case 'headerField':
                case 'contentField':
                case 'template':
                case 'tagsField':
                case 'grabberField':
                case 'footerCssField':
                    if (this.enableVirtualization) {
                        this.virtualLayoutModule.refreshCards();
                    }
                    else {
                        this.layoutModule.refreshCards();
                    }
                    break;
                case 'selectionType':
                    cards = this.getSelectedCards();
                    if (cards.length > 0) {
                        removeClass(cards, CARD_SELECTION_CLASS);
                        if (this.enableVirtualization) {
                            this.virtualLayoutModule.disableAttributeSelection(cards);
                        }
                        else {
                            this.layoutModule.disableAttributeSelection(cards);
                        }
                    }
                    break;
            }
        }
    }
    initializeModules() {
        this.dataModule = new Data(this);
        if (this.enableVirtualization) {
            this.virtualLayoutModule = new VirtualLayoutRender(this);
        }
        else {
            this.layoutModule = new LayoutRender(this);
        }
        if (this.allowKeyboard) {
            this.keyboardModule = new Keyboard(this);
        }
        this.actionModule = new Action(this);
        this.crudModule = new Crud(this);
        this.dragAndDropModule = new DragAndDrop(this);
        if (this.allowColumnDragAndDrop) {
            this.columnDragAndDropModule = new ColumnDragAndDrop(this);
        }
        this.dialogModule = new KanbanDialog(this);
        if (this.enableTooltip) {
            this.tooltipModule = new KanbanTooltip(this);
        }
        if (Browser.isDevice || Browser.isTouch) {
            this.touchModule = new KanbanTouch(this);
        }
    }
    renderTemplates() {
        if (this.isReact) {
            this.renderReactTemplates();
        }
    }
    resetTemplates(templates) {
        if (this.isReact) {
            this.clearTemplate(templates);
        }
    }
    destroyModules() {
        if (this.layoutModule) {
            this.layoutModule.destroy();
            this.layoutModule = null;
        }
        if (this.keyboardModule) {
            this.keyboardModule.destroy();
            this.keyboardModule = null;
        }
        if (this.virtualLayoutModule) {
            this.virtualLayoutModule.destroy();
            this.virtualLayoutModule = null;
        }
        if (this.touchModule) {
            this.touchModule.destroy();
            this.touchModule = null;
        }
        if (this.tooltipModule) {
            this.tooltipModule.destroy();
            this.tooltipModule = null;
        }
        this.dialogModule = null;
        this.actionModule = null;
        this.crudModule = null;
        this.dataModule = null;
        this.dragAndDropModule = null;
    }
    wireColumnDragEvent() {
        if (this.allowColumnDragAndDrop) {
            const headerContainer = this.element.querySelector('.' + HEADER_CLASS);
            if (headerContainer) {
                if (this.stackedHeaders && this.stackedHeaders.length > 0) {
                    // Wire stacked headers for dragging stacked header groups
                    const stackedHeaderSelector = '.' + STACKED_HEADER_CELL_CLASS;
                    const stackedHeaderCells = [].slice.call(this.element.querySelectorAll(stackedHeaderSelector));
                    addClass(stackedHeaderCells, DRAG_CLASS);
                    // Wire events to stacked headers
                    stackedHeaderCells.forEach((headerCell) => {
                        this.columnDragAndDropModule.wireColumnDragEvents(headerCell);
                    });
                    // ALSO wire regular column headers to enable dragging within stacked headers
                    const columnHeaderSelector = '.' + HEADER_CELLS_CLASS + ':not(.' + STACKED_HEADER_CELL_CLASS + ')';
                    const columnHeaderCells = [].slice.call(this.element.querySelectorAll(columnHeaderSelector));
                    addClass(columnHeaderCells, DRAG_CLASS);
                    // Wire events to column headers
                    columnHeaderCells.forEach((headerCell) => {
                        this.columnDragAndDropModule.wireColumnDragEvents(headerCell);
                    });
                }
                else {
                    // If no stacked headers, only wire normal headers
                    const headerSelector = '.' + HEADER_CELLS_CLASS;
                    const headerCells = [].slice.call(this.element.querySelectorAll(headerSelector));
                    addClass(headerCells, DRAG_CLASS);
                    // Loop through each header cell and wire drag events
                    headerCells.forEach((headerCell) => {
                        this.columnDragAndDropModule.wireColumnDragEvents(headerCell);
                    });
                }
            }
        }
    }
    unwireColumnDragEvent() {
        if (this.stackedHeaders && this.stackedHeaders.length > 0) {
            // Unwire stacked header cells
            const stackedHeaderSelector = '.' + STACKED_HEADER_CELL_CLASS;
            const stackedHeaderCells = [].slice.call(this.element.querySelectorAll(stackedHeaderSelector));
            removeClass(stackedHeaderCells, DRAG_CLASS);
            // Unwire events from stacked headers
            stackedHeaderCells.forEach((headerCell) => {
                this.columnDragAndDropModule.unwireColumnDragEvents(headerCell);
            });
            // ALSO unwire regular column headers
            const columnHeaderSelector = '.' + HEADER_CELLS_CLASS + ':not(.' + STACKED_HEADER_CELL_CLASS + ')';
            const columnHeaderCells = [].slice.call(this.element.querySelectorAll(columnHeaderSelector));
            removeClass(columnHeaderCells, DRAG_CLASS);
            // Unwire events from column headers
            columnHeaderCells.forEach((headerCell) => {
                this.columnDragAndDropModule.unwireColumnDragEvents(headerCell);
            });
        }
        else {
            // If no stacked headers, only unwire normal headers
            const headerSelector = '.' + HEADER_CELLS_CLASS;
            const headerCells = [].slice.call(this.element.querySelectorAll(headerSelector));
            removeClass(headerCells, DRAG_CLASS);
            // Loop through each header cell and unwire drag events
            headerCells.forEach((headerCell) => {
                this.columnDragAndDropModule.unwireColumnDragEvents(headerCell);
            });
        }
    }
    templateParser(template) {
        if (template) {
            try {
                if (typeof template === 'function') {
                    return compile(template);
                }
                else if (document.querySelectorAll(template).length) {
                    return compile(document.querySelector(template).innerHTML.trim());
                }
                else {
                    return compile(template);
                }
            }
            catch (error) {
                return compile(template);
            }
        }
        return undefined;
    }
    /**
     * Returns the card details based on card ID from the board.
     *
     * @function getCardDetails
     * @param {Element} target Accepts the card element to get the details.
     * @returns {Object} Returns the card details based on given target.
     */
    getCardDetails(target) {
        const isNumeric = typeof (this.kanbanData[0])[this.cardSettings.headerField] === 'number';
        const targetId = isNumeric ? parseInt(target.getAttribute('data-id'), 10) : target.getAttribute('data-id');
        const cardObj = this.kanbanData.filter((data) => data[this.cardSettings.headerField] === targetId)[0];
        return cardObj;
    }
    /**
     * Returns the column data based on column key input.
     *
     * @function getColumnData
     * @param {string | number} columnKey Accepts the column key to get the objects.
     * @param {Object[]} dataSource Accepts the collection of objects to get the results based on given columnKey.
     * @returns {Object[]} Returns the collection of card objects based on given inputs.
     */
    getColumnData(columnKey, dataSource) {
        if (this.enableVirtualization) {
            return this.virtualLayoutModule.getColumnCards(dataSource)[`${columnKey}`] || [];
        }
        return this.layoutModule.getColumnCards(dataSource)[`${columnKey}`] || [];
    }
    /**
     * Returns the swimlane column data based on swimlane keyField input.
     *
     * @function getSwimlaneData
     * @param {string} keyField Accepts the swimlane keyField to get the objects.
     * @returns {Object[]} Returns the collection of card objects based on given inputs.
     */
    getSwimlaneData(keyField) {
        return this.layoutModule.getSwimlaneCards()[`${keyField}`] || [];
    }
    /**
     * Gets the list of selected cards from the board.
     *
     * @function getSelectedCards
     * @returns {HTMLElement[]} Returns the card elements based on selection.
     */
    getSelectedCards() {
        return [].slice.call(this.element.querySelectorAll('.' + CARD_CLASS + '.' + CARD_SELECTION_CLASS));
    }
    /**
     * Allows you to show the spinner on Kanban at the required scenarios.
     *
     * @function showSpinner
     * @returns {void}
     */
    showSpinner() {
        showSpinner(this.element);
    }
    /**
     * When the spinner is shown manually using the showSpinner method, it can be hidden using this `hideSpinner` method.
     *
     * @function hideSpinner
     * @returns {void}
     */
    hideSpinner() {
        hideSpinner(this.element);
    }
    /**
     * To manually open the dialog.
     *
     * @function openDialog
     * @param {CurrentAction} action Accepts the action for which the dialog needs to be opened such as either for new card creation or
     *  editing of existing cards. The applicable action names are `Add` and `Edit`.
     * @param {Object} data It can be card data.
     * @returns {void}
     */
    openDialog(action, data) {
        this.dialogModule.openDialog(action, data);
    }
    /**
     * To manually close the dialog.
     *
     * @function closeDialog
     * @returns {void}
     */
    closeDialog() {
        this.dialogModule.closeDialog();
    }
    /**
     * Adds the new card to the data source of Kanban and layout.
     *
     * @function addCard
     * @param {Object | Object[]} cardData Accepts Single card object or Collection of card objects to be added into Kanban.
     * @param {number} index Accepts the index to insert the card in column.
     * @returns {void}
     */
    addCard(cardData, index) {
        this.crudModule.addCard(cardData, index);
    }
    /**
     * Updates the changes made in the card object by passing it as a parameter to the data source.
     *
     * @function updateCard
     * @param {Object | Object[]} cardData Accepts Single card object or Collection of card objects to be updated into Kanban.
     * @param {number} index Accepts the index to update the card in column.
     * @returns {void}
     */
    updateCard(cardData, index) {
        this.crudModule.updateCard(cardData, index);
    }
    /**
     * Deletes the card based on the provided ID or card collection in the argument list.
     *
     * @function deleteCard
     * @param {string | number | Object | Object[]} cardData Accepts the ID of the remove card in string or number type or
     * Single card object or Collection of card objects to be removed from Kanban
     * @returns {void}
     */
    deleteCard(cardData) {
        this.crudModule.deleteCard(cardData);
    }
    /**
     * Add the column to Kanban board dynamically based on the provided column options and index in the argument list.
     *
     * @function addColumn
     * @param {ColumnsModel} columnOptions Accepts the properties to new column that are going to be added in the board.
     * @param {number} index Accepts the index of column to add the new column.
     * @returns {void}
     */
    addColumn(columnOptions, index) {
        this.actionModule.addColumn(columnOptions, index);
    }
    /**
     * Deletes the column based on the provided index value.
     *
     * @function deleteColumn
     * @param {number} index Accepts the index of column to delete the existing column from Kanban board.
     * @returns {void}
     */
    deleteColumn(index) {
        this.actionModule.deleteColumn(index);
    }
    /**
     * Shows the column from hidden based on the provided key in the columns.
     *
     * @function showColumn
     * @param {string | number} key Accepts the hidden column key name to be shown from the hidden state in board.
     * @returns {void}
     */
    showColumn(key) {
        this.actionModule.showColumn(key);
    }
    /**
     * Hides the column from Kanban board based on the provided key in the columns.
     *
     * @function hideColumn
     * @param {string | number} key Accepts the visible column key name to be hidden from the board.
     * @returns {void}
     */
    hideColumn(key) {
        this.actionModule.hideColumn(key);
    }
    /**
     * Method to refresh the Kanban UI based on modified records.
     *
     * @function refreshUI
     * @param {ActionEventArgs} args Accepts the added, changed or deleted data.
     * @param {number} index Accepts the index of the changed items.
     * @returns {void}
     */
    refreshUI(args, index) {
        index = index ? index : 0;
        this.dataModule.refreshUI(args, index);
    }
    /**
     * Method to refresh the column header.
     *
     * @method refreshHeader
     * @returns {void}
     */
    refreshHeader() {
        this.resetTemplates(['columnTemplate']);
        if (this.enableVirtualization) {
            this.virtualLayoutModule.refreshHeaders();
        }
        else {
            this.layoutModule.refreshHeaders();
        }
        this.renderTemplates();
    }
    /**
     * Removes the control from the DOM and detaches all its related event handlers. Also, it removes the attributes and classes.
     *
     * @function destroy
     * @returns {void}
     */
    destroy() {
        this.destroyModules();
        [].slice.call(this.element.childNodes).forEach((node) => { detach(node); });
        let removeClasses = [ROOT_CLASS];
        if (this.cssClass) {
            removeClasses = removeClasses.concat(this.cssClass.split(' '));
        }
        removeClass([this.element], removeClasses);
        super.destroy();
    }
};
__decorate$6([
    Property()
], Kanban.prototype, "cssClass", void 0);
__decorate$6([
    Property('auto')
], Kanban.prototype, "width", void 0);
__decorate$6([
    Property('auto')
], Kanban.prototype, "height", void 0);
__decorate$6([
    Property('auto')
], Kanban.prototype, "cardHeight", void 0);
__decorate$6([
    Property()
], Kanban.prototype, "enableVirtualization", void 0);
__decorate$6([
    Property([])
], Kanban.prototype, "dataSource", void 0);
__decorate$6([
    Property()
], Kanban.prototype, "query", void 0);
__decorate$6([
    Property()
], Kanban.prototype, "keyField", void 0);
__decorate$6([
    Property('Column')
], Kanban.prototype, "constraintType", void 0);
__decorate$6([
    Property([])
], Kanban.prototype, "externalDropId", void 0);
__decorate$6([
    Collection([], Columns)
], Kanban.prototype, "columns", void 0);
__decorate$6([
    Property(true)
], Kanban.prototype, "allowKeyboard", void 0);
__decorate$6([
    Property(true)
], Kanban.prototype, "enableHtmlSanitizer", void 0);
__decorate$6([
    Collection([], StackedHeaders)
], Kanban.prototype, "stackedHeaders", void 0);
__decorate$6([
    Complex({}, SwimlaneSettings)
], Kanban.prototype, "swimlaneSettings", void 0);
__decorate$6([
    Complex({}, CardSettings)
], Kanban.prototype, "cardSettings", void 0);
__decorate$6([
    Complex({}, SortSettings)
], Kanban.prototype, "sortSettings", void 0);
__decorate$6([
    Complex({}, DialogSettings)
], Kanban.prototype, "dialogSettings", void 0);
__decorate$6([
    Property(true)
], Kanban.prototype, "allowDragAndDrop", void 0);
__decorate$6([
    Property(false)
], Kanban.prototype, "allowColumnDragAndDrop", void 0);
__decorate$6([
    Property(false)
], Kanban.prototype, "enableTooltip", void 0);
__decorate$6([
    Property(false)
], Kanban.prototype, "showEmptyColumn", void 0);
__decorate$6([
    Property(false)
], Kanban.prototype, "enablePersistence", void 0);
__decorate$6([
    Property()
], Kanban.prototype, "tooltipTemplate", void 0);
__decorate$6([
    Event()
], Kanban.prototype, "actionBegin", void 0);
__decorate$6([
    Event()
], Kanban.prototype, "actionComplete", void 0);
__decorate$6([
    Event()
], Kanban.prototype, "actionFailure", void 0);
__decorate$6([
    Event()
], Kanban.prototype, "created", void 0);
__decorate$6([
    Event()
], Kanban.prototype, "dataBinding", void 0);
__decorate$6([
    Event()
], Kanban.prototype, "dataBound", void 0);
__decorate$6([
    Event()
], Kanban.prototype, "cardClick", void 0);
__decorate$6([
    Event()
], Kanban.prototype, "cardDoubleClick", void 0);
__decorate$6([
    Event()
], Kanban.prototype, "queryCellInfo", void 0);
__decorate$6([
    Event()
], Kanban.prototype, "cardRendered", void 0);
__decorate$6([
    Event()
], Kanban.prototype, "dragStart", void 0);
__decorate$6([
    Event()
], Kanban.prototype, "drag", void 0);
__decorate$6([
    Event()
], Kanban.prototype, "dragStop", void 0);
__decorate$6([
    Event()
], Kanban.prototype, "dialogOpen", void 0);
__decorate$6([
    Event()
], Kanban.prototype, "dialogClose", void 0);
__decorate$6([
    Event()
], Kanban.prototype, "columnDragStart", void 0);
__decorate$6([
    Event()
], Kanban.prototype, "columnDrag", void 0);
__decorate$6([
    Event()
], Kanban.prototype, "columnDrop", void 0);
__decorate$6([
    Event()
], Kanban.prototype, "dataStateChange", void 0);
__decorate$6([
    Event()
], Kanban.prototype, "dataSourceChanged", void 0);
Kanban = __decorate$6([
    NotifyPropertyChanges
], Kanban);

export { Kanban, actionBegin, actionComplete, actionFailure, bottomSpace, cardClick, cardDoubleClick, cardRendered, cardSpace, columnDrag, columnDragStart, columnDrop, contentReady, dataBinding, dataBound, dataReady, dataSourceChanged, dataStateChange, dialogClose, dialogOpen, documentClick, drag, dragStart, dragStop, queryCellInfo, toggleWidth };
//# sourceMappingURL=ej2-kanban.es2015.js.map