@syncfusion/ej2-grids
Version: 
Feature-rich JavaScript datagrid (datatable) control with built-in support for editing, filtering, grouping, paging, sorting, and exporting to Excel.
524 lines (523 loc) • 26.6 kB
JavaScript
import { removeClass, addClass, extend, EventHandler } from '@syncfusion/ej2-base';
import { closest, classList, isNullOrUndefined } from '@syncfusion/ej2-base';
import { Grid } from '../base/grid';
import { parents, getUid, appendChildren, isComplexField, getObject } from '../base/util';
import * as events from '../base/constant';
import { AriaService } from '../services/aria-service';
import { Row } from '../models/row';
import { Cell } from '../models/cell';
import { CellType } from '../base/enum';
import * as literals from '../base/string-literals';
/**
 * The `DetailRow` module is used to handle detail template and hierarchy Grid operations.
 */
var DetailRow = /** @class */ (function () {
    /**
     * Constructor for the Grid detail template module
     *
     * @param {IGrid} parent - specifies the IGrid
     * @param {ServiceLocator} locator - specifes the serviceLocator
     * @hidden
     */
    function DetailRow(parent, locator) {
        //Internal variables
        this.aria = new AriaService();
        this.childRefs = [];
        this.parent = parent;
        this.serviceLocator = locator;
        this.focus = locator.getService('focus');
        this.addEventListener();
    }
    /**
     * @returns {void}
     * @hidden
     */
    DetailRow.prototype.addEventListener = function () {
        if (this.parent.isDestroyed) {
            return;
        }
        EventHandler.add(this.parent.element, 'auxclick', this.auxilaryclickHandler, this);
        this.parent.on(events.click, this.clickHandler, this);
        this.parent.on(events.destroy, this.destroy, this);
        this.parent.on(events.keyPressed, this.keyPressHandler, this);
        this.parent.on(events.expandChildGrid, this.expand, this);
        this.parent.on(events.columnVisibilityChanged, this.refreshColSpan, this);
        this.parent.on(events.destroy, this.destroyChildGrids, this);
        this.parent.on(events.destroyChildGrid, this.destroyChildGrids, this);
        this.parent.on(events.destroy, this.detachDetailTemplate, this);
        this.parent.on(events.detachDetailTemplate, this.detachDetailTemplate, this);
    };
    DetailRow.prototype.clickHandler = function (e) {
        if ((e.target.classList.contains('e-icon-grightarrow') || e.target.classList.contains('e-icon-gdownarrow'))
            && !this.parent.allowGrouping) {
            e.preventDefault();
        }
        this.toogleExpandcollapse(closest(e.target, 'td'));
    };
    DetailRow.prototype.auxilaryclickHandler = function (e) {
        if ((e.target.classList.contains('e-icon-grightarrow') || e.target.classList.contains('e-icon-gdownarrow'))
            && !this.parent.allowGrouping && (e.button === 1)) {
            e.preventDefault();
        }
    };
    DetailRow.prototype.toogleExpandcollapse = function (target) {
        this.l10n = this.serviceLocator.getService('localization');
        var gObj = this.parent;
        var table = this.parent.getContentTable();
        var lastrowIdx = this.parent.getCurrentViewRecords().length - 1;
        var parent = 'parentDetails';
        var childGrid;
        var isExpanded = target && target.classList.contains('e-detailrowcollapse');
        if (!(target && (target.classList.contains('e-detailrowcollapse') || target.classList.contains('e-detailrowexpand')))
            || (target && target.classList.contains('e-masked-cell'))) {
            return;
        }
        var tr = target.parentElement;
        var uid = tr.getAttribute('data-uid');
        var rowObj = gObj.getRowObjectFromUID(uid);
        var needToRefresh = false;
        var nextRow = this.parent.getContentTable().querySelector(literals.tbody).children[tr.rowIndex + 1];
        if (target.classList.contains('e-detailrowcollapse')) {
            var data_1 = rowObj.data;
            if (this.isDetailRow(nextRow)) {
                nextRow.style.display = '';
                gObj.notify(events.detailStateChange, { data: data_1,
                    childGrid: gObj.childGrid, detailElement: target, isExpanded: isExpanded });
                needToRefresh = true;
            }
            else if (gObj.getDetailTemplate() || gObj.childGrid) {
                var rowId = getUid('grid-row');
                var detailRow = this.parent.createElement('tr', { className: 'e-detailrow', attrs: { 'data-uid': rowId, role: 'row' } });
                var detailCell_1 = this.parent.createElement('th', { className: 'e-detailcell', attrs: { 'scope': 'col', role: 'columnheader' } });
                var colSpan = this.parent.getVisibleColumns().length;
                if (this.parent.allowRowDragAndDrop) {
                    colSpan++;
                }
                detailCell_1.setAttribute('colspan', colSpan.toString());
                var row = new Row({
                    isDataRow: true,
                    isExpand: true,
                    uid: rowId,
                    isDetailRow: true,
                    cells: [new Cell({ cellType: CellType.Indent }), new Cell({ isDataCell: true, visible: true })]
                });
                row.parentUid = rowObj.uid;
                for (var i = 0, len = gObj.groupSettings.columns.length; i < len; i++) {
                    detailRow.appendChild(this.parent.createElement('td', { className: 'e-indentcell' }));
                    row.cells.unshift(new Cell({ cellType: CellType.Indent }));
                }
                detailRow.appendChild(this.parent.createElement('th', { className: 'e-detailindentcell', attrs: { 'scope': 'col' } }));
                detailRow.appendChild(detailCell_1);
                tr.parentNode.insertBefore(detailRow, tr.nextSibling);
                var isReactCompiler = void 0;
                var isReactChild = void 0;
                if (gObj.detailTemplate) {
                    isReactCompiler = this.parent.isReact && typeof (gObj.detailTemplate) !== 'string' &&
                        !(gObj.detailTemplate.prototype && gObj.detailTemplate.prototype.CSPTemplate);
                    isReactChild = this.parent.parentDetails && this.parent.parentDetails.parentInstObj &&
                        this.parent.parentDetails.parentInstObj.isReact;
                    var isReactPrintGrid = this.parent.printGridParent && this.parent.printGridParent.isReact;
                    var detailTemplateID = gObj.element.id + 'detailTemplate';
                    if (isReactCompiler || isReactChild || isReactPrintGrid) {
                        gObj.getDetailTemplate()(data_1, gObj, 'detailTemplate', detailTemplateID, null, null, detailCell_1);
                        this.parent.renderTemplates(function () {
                            gObj.trigger(events.detailDataBound, { detailElement: detailCell_1, data: data_1, childGrid: childGrid });
                        });
                    }
                    else {
                        appendChildren(detailCell_1, gObj.getDetailTemplate()(data_1, gObj, 'detailTemplate', detailTemplateID, undefined, undefined, undefined, this.parent['root']));
                    }
                }
                else {
                    childGrid = new Grid(this.getGridModel(gObj, rowObj, gObj.printMode));
                    childGrid.height = gObj.enableInfiniteScrolling && childGrid.height === 'auto' ? 300 : childGrid.height;
                    childGrid.root = gObj.root ? gObj.root : gObj;
                    this.childRefs.push(childGrid);
                    if (childGrid.query) {
                        childGrid.query = childGrid.query.clone();
                    }
                    childGrid["" + parent] = {
                        parentID: gObj.element.id,
                        parentPrimaryKeys: gObj.getPrimaryKeyFieldNames(),
                        parentKeyField: gObj.childGrid.queryString,
                        parentKeyFieldValue: gObj.childGrid.queryString && isComplexField(gObj.childGrid.queryString) ?
                            getObject(gObj.childGrid.queryString, data_1) : data_1[gObj.childGrid.queryString],
                        parentRowData: data_1
                    };
                    if (gObj.isReact || gObj.isVue) {
                        childGrid.parentDetails.parentInstObj = gObj;
                    }
                    else if (gObj.parentDetails && gObj.parentDetails.parentInstObj && (gObj.parentDetails.parentInstObj.isReact
                        || gObj.parentDetails.parentInstObj.isVue)) {
                        childGrid.parentDetails.parentInstObj = gObj.parentDetails.parentInstObj;
                    }
                    if (gObj.printGridParent && gObj.printGridParent.isReact) {
                        childGrid.printGridParent = gObj.printGridParent;
                    }
                    childGrid.isLegacyTemplate = gObj.isReact
                        || gObj.isLegacyTemplate;
                    if (gObj.isPrinting) {
                        childGrid.isPrinting = true;
                        childGrid.on(events.contentReady, this.promiseResolve(childGrid), this);
                        childGrid.on(events.onEmpty, this.promiseResolve(childGrid), this);
                    }
                    rowObj.childGrid = childGrid;
                    var modules = childGrid.getInjectedModules();
                    var injectedModues = gObj.getInjectedModules();
                    if (!modules || modules.length !== injectedModues.length) {
                        childGrid.setInjectedModules(injectedModues);
                    }
                    var gridElem = this.parent.createElement('div', {
                        id: 'child' + parents(tr, 'e-grid').length +
                            '_grid' + tr.rowIndex + getUid(''), className: 'e-childgrid'
                    });
                    detailCell_1.appendChild(gridElem);
                    childGrid.appendTo(gridElem);
                }
                detailRow.appendChild(detailCell_1);
                if (tr.nextSibling) {
                    tr.parentNode.insertBefore(detailRow, tr.nextSibling);
                }
                else {
                    tr.parentNode.appendChild(detailRow);
                }
                var rowElems = gObj.getRows();
                var rowObjs = gObj.getRowsObject();
                rowElems.splice(rowElems.indexOf(tr) + 1, 0, detailRow);
                if (gObj.enableInfiniteScrolling && gObj.infiniteScrollSettings.enableCache) {
                    var infiniteCache = gObj.contentModule
                        .infiniteCache;
                    var keys = Object.keys(infiniteCache);
                    for (var i = 0; i < keys.length; i++) {
                        var cacheIndex = infiniteCache[parseInt(keys[parseInt(i.toString(), 10)], 10)].indexOf(rowObj);
                        if (cacheIndex !== -1) {
                            infiniteCache[parseInt(keys[parseInt(i.toString(), 10)], 10)].splice(cacheIndex + 1, 0, row);
                            break;
                        }
                    }
                }
                else {
                    rowObjs.splice(rowObjs.indexOf(rowObj) + 1, 0, row);
                }
                if (!isReactCompiler || !isReactChild) {
                    gObj.trigger(events.detailDataBound, { detailElement: detailCell_1, data: data_1, childGrid: childGrid });
                }
                gObj.notify(events.detailDataBound, { rows: rowObjs });
            }
            classList(target, ['e-detailrowexpand'], ['e-detailrowcollapse']);
            classList(target.firstElementChild, ['e-dtdiagonaldown', 'e-icon-gdownarrow'], ['e-dtdiagonalright', 'e-icon-grightarrow']);
            rowObj.isExpand = true;
            if (target.classList.contains('e-lastrowcell') && this.parent.getContent().clientHeight > table.scrollHeight) {
                removeClass(target.parentElement.querySelectorAll('td'), 'e-lastrowcell');
                var detailrowIdx = table.querySelector(literals.tbody).getElementsByClassName('e-detailrow').length - 1;
                addClass(table.querySelector(literals.tbody).getElementsByClassName('e-detailrow')[parseInt(detailrowIdx.toString(), 10)].childNodes, ['e-lastrowcell']);
                this.lastrowcell = true;
            }
            this.aria.setExpand(target, true);
            target.firstElementChild.setAttribute('title', this.l10n.getConstant('Expanded'));
        }
        else {
            if (this.isDetailRow(nextRow)) {
                nextRow.style.display = 'none';
                gObj.notify(events.detailStateChange, { data: rowObj.data,
                    childGrid: gObj.childGrid, detailElement: target, isExpanded: isExpanded });
            }
            classList(target, ['e-detailrowcollapse'], ['e-detailrowexpand']);
            classList(target.firstElementChild, ['e-dtdiagonalright', 'e-icon-grightarrow'], ['e-dtdiagonaldown', 'e-icon-gdownarrow']);
            if (parseInt(tr.getAttribute(literals.ariaRowIndex), 10) - 1 === lastrowIdx && this.lastrowcell) {
                addClass(target.parentElement.querySelectorAll('td'), 'e-lastrowcell');
                this.lastrowcell = false;
            }
            rowObj.isExpand = false;
            needToRefresh = true;
            this.aria.setExpand(target, false);
            target.firstElementChild.setAttribute('title', this.l10n.getConstant('Collapsed'));
        }
        if (!isNullOrUndefined(gObj.detailTemplate) || (gObj.childGrid && needToRefresh)) {
            gObj.updateVisibleExpandCollapseRows();
            gObj.notify(events.refreshExpandandCollapse, { rows: gObj.getRowsObject() });
        }
        if (this.parent.allowTextWrap && this.parent.height === 'auto') {
            if (this.parent.getContentTable().scrollHeight > this.parent.getContent().clientHeight) {
                this.parent.scrollModule.setPadding();
            }
            else {
                this.parent.scrollModule.removePadding();
            }
        }
    };
    /**
     * @hidden
     * @param {IGrid} gObj - specifies the grid Object
     * @param {Row<Column>}rowObj - specifies the row object
     * @param {string} printMode - specifies the printmode
     * @returns {Object} returns the object
     */
    DetailRow.prototype.getGridModel = function (gObj, rowObj, printMode) {
        var gridModel;
        if (gObj.isPrinting && rowObj.isExpand && gObj.expandedRows &&
            gObj.expandedRows[rowObj.index] && gObj.expandedRows[rowObj.index].gridModel) {
            gObj.expandedRows[rowObj.index].gridModel.hierarchyPrintMode = gObj.childGrid.hierarchyPrintMode;
            gridModel = extend({}, gObj.expandedRows[rowObj.index].gridModel, gObj.childGrid, true);
        }
        else {
            if (gObj.isPrinting && gObj.childGrid.allowPaging) {
                gObj.childGrid.allowPaging = printMode === 'CurrentPage';
            }
            gridModel = extend({}, {}, gObj.childGrid, true);
        }
        return gridModel;
    };
    DetailRow.prototype.promiseResolve = function (grid) {
        var _this = this;
        return function () {
            grid.off(events.contentReady, _this.promiseResolve);
            grid.off(events.onEmpty, _this.promiseResolve);
            grid.notify(events.hierarchyPrint, {});
        };
    };
    DetailRow.prototype.isDetailRow = function (row) {
        return row && row.classList.contains('e-detailrow');
    };
    DetailRow.prototype.destroy = function () {
        var gridElement = this.parent.element;
        if (this.parent.isDestroyed || !gridElement || (!gridElement.querySelector('.' + literals.gridHeader) &&
            !gridElement.querySelector('.' + literals.gridContent))) {
            return;
        }
        EventHandler.remove(this.parent.element, 'auxclick', this.auxilaryclickHandler);
        this.parent.off(events.click, this.clickHandler);
        this.parent.off(events.destroy, this.destroy);
        this.parent.off(events.keyPressed, this.keyPressHandler);
        this.parent.off(events.expandChildGrid, this.expand);
        this.parent.off(events.columnVisibilityChanged, this.refreshColSpan);
        this.parent.off(events.destroy, this.destroyChildGrids);
        this.parent.off(events.destroyChildGrid, this.destroyChildGrids);
        this.parent.off(events.destroy, this.detachDetailTemplate);
        this.parent.off(events.detachDetailTemplate, this.detachDetailTemplate);
    };
    DetailRow.prototype.getTDfromIndex = function (index, className) {
        var tr = !isNullOrUndefined(index) ? this.parent.getDataRows()[parseInt(index.toString(), 10)] : undefined;
        if (tr && tr.querySelector(className)) {
            return tr.querySelector(className);
        }
        return null;
    };
    /**
     * Expands a detail row with the given target.
     *
     * @param  {Element} target - Defines the collapsed element to expand.
     * @returns {void}
     */
    DetailRow.prototype.expand = function (target) {
        if (!isNaN(target)) {
            target = this.getTDfromIndex(target, '.e-detailrowcollapse');
        }
        if (target && target.classList.contains('e-detailrowcollapse')) {
            this.toogleExpandcollapse(target);
        }
    };
    /**
     * Collapses a detail row with the given target.
     *
     * @param  {Element} target - Defines the expanded element to collapse.
     * @returns {void}
     */
    DetailRow.prototype.collapse = function (target) {
        if (!isNaN(target)) {
            target = this.getTDfromIndex(target, '.e-detailrowexpand');
        }
        if (target && target.classList.contains('e-detailrowexpand')) {
            this.toogleExpandcollapse(target);
        }
    };
    /**
     * Expands all the detail rows of the Grid.
     *
     * @returns {void}
     */
    DetailRow.prototype.expandAll = function () {
        this.expandCollapse(true);
        this.parent.trigger(events.actionComplete, { requestType: 'expandAllComplete', type: events.actionComplete, moduleObj: this });
    };
    /**
     * Collapses all the detail rows of the Grid.
     *
     * @returns {void}
     */
    DetailRow.prototype.collapseAll = function () {
        this.expandCollapse(false);
        this.parent.trigger(events.actionComplete, { requestType: 'collapseAllComplete', type: events.actionComplete, moduleObj: this });
    };
    DetailRow.prototype.expandCollapse = function (isExpand) {
        var td;
        var rows = this.parent.getDataRows();
        for (var i = 0, len = rows.length; i < len; i++) {
            td = rows[parseInt(i.toString(), 10)].querySelector('.e-detailrowcollapse, .e-detailrowexpand');
            if (isExpand) {
                this.expand(td);
            }
            else {
                this.collapse(td);
            }
        }
    };
    DetailRow.prototype.keyPressHandler = function (e) {
        var gObj = this.parent;
        var isMacLike = /(Mac)/i.test(navigator.platform);
        if (isMacLike && e.metaKey) {
            if (e.action === 'downArrow') {
                e.action = 'ctrlDownArrow';
            }
            else if (e.action === 'upArrow') {
                e.action = 'ctrlUpArrow';
            }
        }
        switch (e.action) {
            case 'ctrlDownArrow':
                this.expandAll();
                break;
            case 'ctrlUpArrow':
                this.collapseAll();
                break;
            case 'altUpArrow':
            case 'altDownArrow':
                // eslint-disable-next-line no-case-declarations
                var selected = gObj.allowSelection ? gObj.getSelectedRowIndexes() : [];
                if (selected.length) {
                    var dataRow = gObj.getDataRows()[selected[selected.length - 1]];
                    var td = dataRow.querySelector('.e-detailrowcollapse, .e-detailrowexpand');
                    if (e.action === 'altDownArrow') {
                        this.expand(td);
                    }
                    else {
                        this.collapse(td);
                    }
                }
                break;
            case 'enter':
                if (this.parent.isEdit) {
                    return;
                }
                // eslint-disable-next-line no-case-declarations
                var element = this.focus.getFocusedElement();
                if (element && (element.classList.contains('e-icon-grightarrow') || element.classList.contains('e-icon-gdownarrow'))) {
                    element = element.parentElement;
                }
                if (element && !element.classList.contains('e-detailrowcollapse') &&
                    !element.classList.contains('e-detailrowexpand')) {
                    break;
                }
                this.toogleExpandcollapse(element);
                break;
        }
    };
    DetailRow.prototype.refreshColSpan = function () {
        var detailrows = this.parent.contentModule.getTable().querySelectorAll('tr.e-detailrow');
        var colSpan = this.parent.getVisibleColumns().length;
        for (var i = 0; i < detailrows.length; i++) {
            detailrows[parseInt(i.toString(), 10)].querySelector('.e-detailcell').setAttribute('colspan', colSpan + '');
        }
    };
    DetailRow.prototype.destroyChildGrids = function (args) {
        var gObj = this.parent;
        if (gObj.enableInfiniteScrolling && (gObj.childGrid || gObj.detailTemplate) && args.requestType === 'infiniteScroll'
            && gObj.infiniteScrollSettings.enableCache) {
            var cacheIndex = args.direction === 'down'
                ? args.currentPage - gObj.infiniteScrollSettings.initialBlocks
                : args.currentPage + gObj.infiniteScrollSettings.initialBlocks;
            var infiniteCache_1 = gObj.contentModule
                .infiniteCache[parseInt(cacheIndex.toString(), 10)];
            var detailRows_1 = infiniteCache_1.filter(function (data) { return data.isDetailRow && data.parentUid; });
            if (gObj.childGrid) {
                var _loop_1 = function (i) {
                    var detailRow = gObj.getContentTable()
                        .querySelector('[data-uid="' + detailRows_1[parseInt(i.toString(), 10)].uid + '"]');
                    var childGridElement = detailRow.querySelector('.e-childgrid');
                    var childGridIndex = this_1.childRefs.findIndex(function (grid) { return grid.element.id === childGridElement.id; });
                    if (!this_1.childRefs[parseInt(childGridIndex.toString(), 10)].isDestroyed) {
                        this_1.childRefs[parseInt(childGridIndex.toString(), 10)].destroy();
                        this_1.childRefs.splice(childGridIndex, 1);
                    }
                    var detailRowIndex = infiniteCache_1.indexOf(detailRows_1[parseInt(i.toString(), 10)]);
                    infiniteCache_1.splice(detailRowIndex, 1);
                    infiniteCache_1[detailRowIndex - 1].childGrid = null;
                    infiniteCache_1[detailRowIndex - 1].isExpand = false;
                    detailRow.remove();
                };
                var this_1 = this;
                for (var i = 0; i < detailRows_1.length; i++) {
                    _loop_1(i);
                }
            }
            if (gObj.detailTemplate && detailRows_1.length) {
                var args_1 = [];
                var _loop_2 = function (i) {
                    args_1.push({
                        detailRow: gObj.getContentTable().querySelector('[data-uid="' + detailRows_1[parseInt(i.toString(), 10)].uid + '"]'),
                        detailRowObject: detailRows_1[parseInt(i.toString(), 10)],
                        parentRowObject: infiniteCache_1.find(function (parent) { return detailRows_1[parseInt(i.toString(), 10)]
                            .parentUid === parent.uid; })
                    });
                };
                for (var i = 0; i < detailRows_1.length; i++) {
                    _loop_2(i);
                }
                this.parent.trigger(events.beforeDetailTemplateDetach, args_1, function () {
                    for (var i = 0; i < detailRows_1.length; i++) {
                        var detailRow = gObj.getContentTable()
                            .querySelector('[data-uid="' + detailRows_1[parseInt(i.toString(), 10)].uid + '"]');
                        var detailRowIndex = infiniteCache_1.indexOf(detailRows_1[parseInt(i.toString(), 10)]);
                        infiniteCache_1.splice(detailRowIndex, 1);
                        infiniteCache_1[detailRowIndex - 1].isExpand = false;
                        detailRow.remove();
                    }
                });
            }
            return;
        }
        var rows = this.parent.getRowsObject();
        for (var i = 0; i < rows.length; i++) {
            rows[parseInt(i.toString(), 10)].childGrid = null;
        }
        for (var i = 0; i < this.childRefs.length; i++) {
            if (!this.childRefs[parseInt(i.toString(), 10)].isDestroyed) {
                this.childRefs[parseInt(i.toString(), 10)].destroy();
            }
        }
        this.childRefs = [];
    };
    DetailRow.prototype.detachDetailTemplate = function () {
        var gObj = this.parent;
        if (gObj.detailTemplate) {
            var rowsObject_1 = gObj.getRowsObject();
            var detailRows_2 = rowsObject_1.filter(function (data) { return data.isDetailRow && data.parentUid; });
            if (detailRows_2.length) {
                var args_2 = [];
                detailRows_2.map(function (data) {
                    args_2.push({
                        detailRow: gObj.getContentTable().querySelector('[data-uid="' + data.uid + '"]'),
                        detailRowObject: data,
                        parentRowObject: rowsObject_1.find(function (parent) { return data.parentUid === parent.uid; })
                    });
                });
                gObj.trigger(events.beforeDetailTemplateDetach, args_2, function () {
                    detailRows_2.map(function (data) {
                        gObj.getContentTable().querySelector('[data-uid="' + data.uid + '"]').remove();
                    });
                });
            }
        }
    };
    /**
     * For internal use only - Get the module name.
     *
     * @returns {string} returns the module name
     * @private
     */
    DetailRow.prototype.getModuleName = function () {
        return 'detailRow';
    };
    return DetailRow;
}());
export { DetailRow };