UNPKG

@swimlane/ngx-datatable

Version:

ngx-datatable is an Angular table grid component for presenting large and complex data.

1,265 lines (1,240 loc) 290 kB
import * as i0 from '@angular/core'; import { Directive, EventEmitter, TemplateRef, Output, ContentChild, Input, inject, Injectable, booleanAttribute, numberAttribute, Renderer2, ElementRef, HostBinding, ChangeDetectionStrategy, Component, ChangeDetectorRef, ViewContainerRef, HostListener, ViewChild, KeyValueDiffers, InjectionToken, Injector, signal, IterableDiffers, computed, ContentChildren, NgZone, ViewEncapsulation, NgModule } from '@angular/core'; import { __decorate } from 'tslib'; import { Subject, fromEvent, BehaviorSubject } from 'rxjs'; import { NgTemplateOutlet, AsyncPipe, NgStyle, DOCUMENT, NgClass } from '@angular/common'; import { takeUntil } from 'rxjs/operators'; class DataTableFooterTemplateDirective { static ngTemplateContextGuard(directive, context) { return true; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.9", ngImport: i0, type: DataTableFooterTemplateDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.9", type: DataTableFooterTemplateDirective, isStandalone: true, selector: "[ngx-datatable-footer-template]", ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.9", ngImport: i0, type: DataTableFooterTemplateDirective, decorators: [{ type: Directive, args: [{ selector: '[ngx-datatable-footer-template]', standalone: true }] }] }); class DatatableGroupHeaderTemplateDirective { static ngTemplateContextGuard(directive, context) { return true; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.9", ngImport: i0, type: DatatableGroupHeaderTemplateDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.9", type: DatatableGroupHeaderTemplateDirective, isStandalone: true, selector: "[ngx-datatable-group-header-template]", ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.9", ngImport: i0, type: DatatableGroupHeaderTemplateDirective, decorators: [{ type: Directive, args: [{ selector: '[ngx-datatable-group-header-template]', standalone: true }] }] }); class DatatableGroupHeaderDirective { constructor() { /** * Row height is required when virtual scroll is enabled. */ this.rowHeight = 0; /** * Show checkbox at group header to select all rows of the group. */ this.checkboxable = false; /** * Track toggling of group visibility */ this.toggle = new EventEmitter(); } get template() { return this._templateInput || this._templateQuery; } /** * Toggle the expansion of a group */ toggleExpandGroup(group) { this.toggle.emit({ type: 'group', value: group }); } /** * Expand all groups */ expandAllGroups() { this.toggle.emit({ type: 'all', value: true }); } /** * Collapse all groups */ collapseAllGroups() { this.toggle.emit({ type: 'all', value: false }); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.9", ngImport: i0, type: DatatableGroupHeaderDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.9", type: DatatableGroupHeaderDirective, isStandalone: true, selector: "ngx-datatable-group-header", inputs: { rowHeight: "rowHeight", checkboxable: "checkboxable", _templateInput: ["template", "_templateInput"] }, outputs: { toggle: "toggle" }, queries: [{ propertyName: "_templateQuery", first: true, predicate: DatatableGroupHeaderTemplateDirective, descendants: true, read: TemplateRef, static: true }], ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.9", ngImport: i0, type: DatatableGroupHeaderDirective, decorators: [{ type: Directive, args: [{ selector: 'ngx-datatable-group-header', standalone: true }] }], propDecorators: { rowHeight: [{ type: Input }], checkboxable: [{ type: Input }], _templateInput: [{ type: Input, args: ['template'] }], _templateQuery: [{ type: ContentChild, args: [DatatableGroupHeaderTemplateDirective, { read: TemplateRef, static: true }] }], toggle: [{ type: Output }] } }); /** * Always returns the empty string '' */ function emptyStringGetter() { return ''; } /** * Returns the appropriate getter function for this kind of prop. * If prop == null, returns the emptyStringGetter. */ function getterForProp(prop) { if (prop == null) { return emptyStringGetter; } if (typeof prop === 'number') { return numericIndexGetter; } else { // deep or simple if (prop.indexOf('.') !== -1) { return deepValueGetter; } else { return shallowValueGetter; } } } /** * Returns the value at this numeric index. * @param row array of values * @param index numeric index * @returns any or '' if invalid index */ function numericIndexGetter(row, index) { if (row == null) { return ''; } // mimic behavior of deepValueGetter if (!row || index == null) { return row; } const value = row[index]; if (value == null) { return ''; } return value; } /** * Returns the value of a field. * (more efficient than deepValueGetter) * @param obj object containing the field * @param fieldName field name string */ function shallowValueGetter(obj, fieldName) { if (obj == null) { return ''; } if (!obj || !fieldName) { return obj; } const value = obj[fieldName]; if (value == null) { return ''; } return value; } /** * Returns a deep object given a string. zoo['animal.type'] */ function deepValueGetter(obj, path) { if (obj == null) { return ''; } if (!obj || !path) { return obj; } // check if path matches a root-level field // { "a.b.c": 123 } let current = obj[path]; if (current !== undefined) { return current; } current = obj; const split = path.split('.'); if (split.length) { for (let i = 0; i < split.length; i++) { current = current[split[i]]; // if found undefined, return empty string if (current === undefined || current === null) { return ''; } } } return current; } function optionalGetterForProp(prop) { return prop && (row => getterForProp(prop)(row, prop)); } /** * This functions rearrange items by their parents * Also sets the level value to each of the items * * Note: Expecting each item has a property called parentId * Note: This algorithm will fail if a list has two or more items with same ID * NOTE: This algorithm will fail if there is a deadlock of relationship * * For example, * * Input * * id -> parent * 1 -> 0 * 2 -> 0 * 3 -> 1 * 4 -> 1 * 5 -> 2 * 7 -> 8 * 6 -> 3 * * * Output * id -> level * 1 -> 0 * --3 -> 1 * ----6 -> 2 * --4 -> 1 * 2 -> 0 * --5 -> 1 * 7 -> 8 * * * @param rows * */ function groupRowsByParents(rows, from, to) { if (from && to) { const nodeById = {}; const l = rows.length; let node = null; nodeById[0] = new TreeNode(); // that's the root node const uniqIDs = rows.reduce((arr, item) => { const toValue = to(item); if (arr.indexOf(toValue) === -1) { arr.push(toValue); } return arr; }, []); for (let i = 0; i < l; i++) { // make TreeNode objects for each item nodeById[to(rows[i])] = new TreeNode(rows[i]); } for (let i = 0; i < l; i++) { // link all TreeNode objects node = nodeById[to(rows[i])]; let parent = 0; const fromValue = from(node.row); if (!!fromValue && uniqIDs.indexOf(fromValue) > -1) { parent = fromValue; } node.parent = nodeById[parent]; node.row['level'] = node.parent.row['level'] + 1; node.parent.children.push(node); } let resolvedRows = []; nodeById[0].flatten(function () { resolvedRows = [...resolvedRows, this.row]; }, true); return resolvedRows; } else { return rows; } } class TreeNode { constructor(row = null) { if (!row) { row = { level: -1, treeStatus: 'expanded' }; } this.row = row; this.parent = null; this.children = []; } flatten(f, recursive) { if (this.row['treeStatus'] === 'expanded') { for (let i = 0, l = this.children.length; i < l; i++) { const child = this.children[i]; f.apply(child, Array.prototype.slice.call(arguments, 2)); if (recursive) { child.flatten.apply(child, arguments); } } } } } /** * Converts strings from something to camel case * http://stackoverflow.com/questions/10425287/convert-dash-separated-string-to-camelcase */ function camelCase(str) { // Replace special characters with a space str = str.replace(/[^a-zA-Z0-9 ]/g, ' '); // put a space before an uppercase letter str = str.replace(/([a-z](?=[A-Z]))/g, '$1 '); // Lower case first character and some other stuff str = str .replace(/([^a-zA-Z0-9 ])|^[0-9]+/g, '') .trim() .toLowerCase(); // uppercase characters preceded by a space or number str = str.replace(/([ 0-9]+)([a-zA-Z])/g, function (a, b, c) { return b.trim() + c.toUpperCase(); }); return str; } /** * Converts strings from camel case to words * http://stackoverflow.com/questions/7225407/convert-camelcasetext-to-camel-case-text */ function deCamelCase(str) { return str.replace(/([A-Z])/g, match => ` ${match}`).replace(/^./, match => match.toUpperCase()); } /** * Creates a unique object id. * http://stackoverflow.com/questions/6248666/how-to-generate-short-uid-like-ax4j9z-in-js */ function id() { return ('0000' + ((Math.random() * Math.pow(36, 4)) << 0).toString(36)).slice(-4); } /** * Sets the column defaults */ function setColumnDefaults(columns, defaultColumnWidth = 150) { if (!columns) { return; } // Only one column should hold the tree view // Thus if multiple columns are provided with // isTreeColumn as true we take only the first one let treeColumnFound = false; for (const column of columns) { if (!column.$$id) { column.$$id = id(); } // prop can be numeric; zero is valid not a missing prop // translate name => prop if (isNullOrUndefined(column.prop) && column.name) { column.prop = camelCase(column.name); } if (!column.$$valueGetter) { column.$$valueGetter = getterForProp(column.prop); } // format props if no name passed if (!isNullOrUndefined(column.prop) && isNullOrUndefined(column.name)) { column.name = deCamelCase(String(column.prop)); } if (isNullOrUndefined(column.prop) && isNullOrUndefined(column.name)) { column.name = ''; // Fixes IE and Edge displaying `null` } if (!('resizeable' in column)) { column.resizeable = true; } if (!('sortable' in column)) { column.sortable = true; } if (!('draggable' in column)) { column.draggable = true; } if (!('canAutoResize' in column)) { column.canAutoResize = true; } if (!('width' in column)) { column.width = defaultColumnWidth; } if (!('isTreeColumn' in column)) { column.isTreeColumn = false; } else { if (column.isTreeColumn && !treeColumnFound) { // If the first column with isTreeColumn is true found // we mark that treeCoulmn is found treeColumnFound = true; } else { // After that isTreeColumn property for any other column // will be set as false column.isTreeColumn = false; } } } } function isNullOrUndefined(value) { return value === null || value === undefined; } /** * Translates templates definitions to objects */ function translateTemplates(templates) { const result = []; for (const temp of templates) { const col = {}; const props = Object.getOwnPropertyNames(temp); for (const prop of props) { col[prop] = temp[prop]; } if (temp.headerTemplate) { col.headerTemplate = temp.headerTemplate; } if (temp.cellTemplate) { col.cellTemplate = temp.cellTemplate; } if (temp.ghostCellTemplate) { col.ghostCellTemplate = temp.ghostCellTemplate; } if (temp.summaryFunc) { col.summaryFunc = temp.summaryFunc; } if (temp.summaryTemplate) { col.summaryTemplate = temp.summaryTemplate; } result.push(col); } return result; } class DataTableColumnHeaderDirective { static ngTemplateContextGuard(directive, context) { return true; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.9", ngImport: i0, type: DataTableColumnHeaderDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.9", type: DataTableColumnHeaderDirective, isStandalone: true, selector: "[ngx-datatable-header-template]", ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.9", ngImport: i0, type: DataTableColumnHeaderDirective, decorators: [{ type: Directive, args: [{ selector: '[ngx-datatable-header-template]', standalone: true }] }] }); class DataTableColumnCellDirective { constructor() { this.template = inject(TemplateRef); } static ngTemplateContextGuard(dir, ctx) { return true; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.9", ngImport: i0, type: DataTableColumnCellDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.9", type: DataTableColumnCellDirective, isStandalone: true, selector: "[ngx-datatable-cell-template]", ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.9", ngImport: i0, type: DataTableColumnCellDirective, decorators: [{ type: Directive, args: [{ selector: '[ngx-datatable-cell-template]', standalone: true }] }] }); class DataTableColumnCellTreeToggle { constructor() { this.template = inject(TemplateRef); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.9", ngImport: i0, type: DataTableColumnCellTreeToggle, deps: [], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.9", type: DataTableColumnCellTreeToggle, isStandalone: true, selector: "[ngx-datatable-tree-toggle]", ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.9", ngImport: i0, type: DataTableColumnCellTreeToggle, decorators: [{ type: Directive, args: [{ selector: '[ngx-datatable-tree-toggle]', standalone: true }] }] }); /** * service to make DatatableComponent aware of changes to * input bindings of DataTableColumnDirective */ class ColumnChangesService { constructor() { this.columnInputChanges = new Subject(); } get columnInputChanges$() { return this.columnInputChanges.asObservable(); } onInputChange() { this.columnInputChanges.next(undefined); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.9", ngImport: i0, type: ColumnChangesService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.9", ngImport: i0, type: ColumnChangesService }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.9", ngImport: i0, type: ColumnChangesService, decorators: [{ type: Injectable }] }); class DataTableColumnGhostCellDirective { static ngTemplateContextGuard(directive, context) { return true; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.9", ngImport: i0, type: DataTableColumnGhostCellDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.9", type: DataTableColumnGhostCellDirective, isStandalone: true, selector: "[ngx-datatable-ghost-cell-template]", ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.9", ngImport: i0, type: DataTableColumnGhostCellDirective, decorators: [{ type: Directive, args: [{ selector: '[ngx-datatable-ghost-cell-template]', standalone: true }] }] }); class DataTableColumnDirective { constructor() { this.columnChangesService = inject(ColumnChangesService); this.isFirstChange = true; } get cellTemplate() { return this._cellTemplateInput || this._cellTemplateQuery; } get headerTemplate() { return this._headerTemplateInput || this._headerTemplateQuery; } get treeToggleTemplate() { return this._treeToggleTemplateInput || this._treeToggleTemplateQuery; } get ghostCellTemplate() { return this._ghostCellTemplateInput || this._ghostCellTemplateQuery; } ngOnChanges() { if (this.isFirstChange) { this.isFirstChange = false; } else { this.columnChangesService.onInputChange(); } } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.9", ngImport: i0, type: DataTableColumnDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "16.1.0", version: "19.2.9", type: DataTableColumnDirective, isStandalone: true, selector: "ngx-datatable-column", inputs: { name: "name", prop: "prop", bindAsUnsafeHtml: ["bindAsUnsafeHtml", "bindAsUnsafeHtml", booleanAttribute], frozenLeft: ["frozenLeft", "frozenLeft", booleanAttribute], frozenRight: ["frozenRight", "frozenRight", booleanAttribute], flexGrow: ["flexGrow", "flexGrow", numberAttribute], resizeable: ["resizeable", "resizeable", booleanAttribute], comparator: "comparator", pipe: "pipe", sortable: ["sortable", "sortable", booleanAttribute], draggable: ["draggable", "draggable", booleanAttribute], canAutoResize: ["canAutoResize", "canAutoResize", booleanAttribute], minWidth: ["minWidth", "minWidth", numberAttribute], width: ["width", "width", numberAttribute], maxWidth: ["maxWidth", "maxWidth", numberAttribute], checkboxable: ["checkboxable", "checkboxable", booleanAttribute], headerCheckboxable: ["headerCheckboxable", "headerCheckboxable", booleanAttribute], headerClass: "headerClass", cellClass: "cellClass", isTreeColumn: ["isTreeColumn", "isTreeColumn", booleanAttribute], treeLevelIndent: "treeLevelIndent", summaryFunc: "summaryFunc", summaryTemplate: "summaryTemplate", _cellTemplateInput: ["cellTemplate", "_cellTemplateInput"], _headerTemplateInput: ["headerTemplate", "_headerTemplateInput"], _treeToggleTemplateInput: ["treeToggleTemplate", "_treeToggleTemplateInput"], _ghostCellTemplateInput: ["ghostCellTemplate", "_ghostCellTemplateInput"] }, queries: [{ propertyName: "_cellTemplateQuery", first: true, predicate: DataTableColumnCellDirective, descendants: true, read: TemplateRef, static: true }, { propertyName: "_headerTemplateQuery", first: true, predicate: DataTableColumnHeaderDirective, descendants: true, read: TemplateRef, static: true }, { propertyName: "_treeToggleTemplateQuery", first: true, predicate: DataTableColumnCellTreeToggle, descendants: true, read: TemplateRef, static: true }, { propertyName: "_ghostCellTemplateQuery", first: true, predicate: DataTableColumnGhostCellDirective, descendants: true, read: TemplateRef, static: true }], usesOnChanges: true, ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.9", ngImport: i0, type: DataTableColumnDirective, decorators: [{ type: Directive, args: [{ selector: 'ngx-datatable-column', standalone: true }] }], propDecorators: { name: [{ type: Input }], prop: [{ type: Input }], bindAsUnsafeHtml: [{ type: Input, args: [{ transform: booleanAttribute }] }], frozenLeft: [{ type: Input, args: [{ transform: booleanAttribute }] }], frozenRight: [{ type: Input, args: [{ transform: booleanAttribute }] }], flexGrow: [{ type: Input, args: [{ transform: numberAttribute }] }], resizeable: [{ type: Input, args: [{ transform: booleanAttribute }] }], comparator: [{ type: Input }], pipe: [{ type: Input }], sortable: [{ type: Input, args: [{ transform: booleanAttribute }] }], draggable: [{ type: Input, args: [{ transform: booleanAttribute }] }], canAutoResize: [{ type: Input, args: [{ transform: booleanAttribute }] }], minWidth: [{ type: Input, args: [{ transform: numberAttribute }] }], width: [{ type: Input, args: [{ transform: numberAttribute }] }], maxWidth: [{ type: Input, args: [{ transform: numberAttribute }] }], checkboxable: [{ type: Input, args: [{ transform: booleanAttribute }] }], headerCheckboxable: [{ type: Input, args: [{ transform: booleanAttribute }] }], headerClass: [{ type: Input }], cellClass: [{ type: Input }], isTreeColumn: [{ type: Input, args: [{ transform: booleanAttribute }] }], treeLevelIndent: [{ type: Input }], summaryFunc: [{ type: Input }], summaryTemplate: [{ type: Input }], _cellTemplateInput: [{ type: Input, args: ['cellTemplate'] }], _cellTemplateQuery: [{ type: ContentChild, args: [DataTableColumnCellDirective, { read: TemplateRef, static: true }] }], _headerTemplateInput: [{ type: Input, args: ['headerTemplate'] }], _headerTemplateQuery: [{ type: ContentChild, args: [DataTableColumnHeaderDirective, { read: TemplateRef, static: true }] }], _treeToggleTemplateInput: [{ type: Input, args: ['treeToggleTemplate'] }], _treeToggleTemplateQuery: [{ type: ContentChild, args: [DataTableColumnCellTreeToggle, { read: TemplateRef, static: true }] }], _ghostCellTemplateInput: [{ type: Input, args: ['ghostCellTemplate'] }], _ghostCellTemplateQuery: [{ type: ContentChild, args: [DataTableColumnGhostCellDirective, { read: TemplateRef, static: true }] }] } }); class DatatableRowDetailTemplateDirective { static ngTemplateContextGuard(directive, context) { return true; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.9", ngImport: i0, type: DatatableRowDetailTemplateDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.9", type: DatatableRowDetailTemplateDirective, isStandalone: true, selector: "[ngx-datatable-row-detail-template]", ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.9", ngImport: i0, type: DatatableRowDetailTemplateDirective, decorators: [{ type: Directive, args: [{ selector: '[ngx-datatable-row-detail-template]', standalone: true }] }] }); class DatatableRowDetailDirective { constructor() { /** * The detail row height is required especially * when virtual scroll is enabled. */ this.rowHeight = 0; /** * Row detail row visbility was toggled. */ this.toggle = new EventEmitter(); } get template() { return this._templateInput || this._templateQuery; } /** * Toggle the expansion of the row */ toggleExpandRow(row) { this.toggle.emit({ type: 'row', value: row }); } /** * API method to expand all the rows. */ expandAllRows() { this.toggle.emit({ type: 'all', value: true }); } /** * API method to collapse all the rows. */ collapseAllRows() { this.toggle.emit({ type: 'all', value: false }); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.9", ngImport: i0, type: DatatableRowDetailDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.9", type: DatatableRowDetailDirective, isStandalone: true, selector: "ngx-datatable-row-detail", inputs: { rowHeight: "rowHeight", _templateInput: ["template", "_templateInput"] }, outputs: { toggle: "toggle" }, queries: [{ propertyName: "_templateQuery", first: true, predicate: DatatableRowDetailTemplateDirective, descendants: true, read: TemplateRef, static: true }], ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.9", ngImport: i0, type: DatatableRowDetailDirective, decorators: [{ type: Directive, args: [{ selector: 'ngx-datatable-row-detail', standalone: true }] }], propDecorators: { rowHeight: [{ type: Input }], _templateInput: [{ type: Input, args: ['template'] }], _templateQuery: [{ type: ContentChild, args: [DatatableRowDetailTemplateDirective, { read: TemplateRef, static: true }] }], toggle: [{ type: Output }] } }); class DatatableFooterDirective { get template() { return this._templateInput || this._templateQuery; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.9", ngImport: i0, type: DatatableFooterDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "16.1.0", version: "19.2.9", type: DatatableFooterDirective, isStandalone: true, selector: "ngx-datatable-footer", inputs: { footerHeight: ["footerHeight", "footerHeight", numberAttribute], totalMessage: "totalMessage", selectedMessage: "selectedMessage", pagerLeftArrowIcon: "pagerLeftArrowIcon", pagerRightArrowIcon: "pagerRightArrowIcon", pagerPreviousIcon: "pagerPreviousIcon", pagerNextIcon: "pagerNextIcon", _templateInput: ["template", "_templateInput"] }, queries: [{ propertyName: "_templateQuery", first: true, predicate: DataTableFooterTemplateDirective, descendants: true, read: TemplateRef }], ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.9", ngImport: i0, type: DatatableFooterDirective, decorators: [{ type: Directive, args: [{ selector: 'ngx-datatable-footer', standalone: true }] }], propDecorators: { footerHeight: [{ type: Input, args: [{ transform: numberAttribute }] }], totalMessage: [{ type: Input }], selectedMessage: [{ type: Input }], pagerLeftArrowIcon: [{ type: Input }], pagerRightArrowIcon: [{ type: Input }], pagerPreviousIcon: [{ type: Input }], pagerNextIcon: [{ type: Input }], _templateInput: [{ type: Input, args: ['template'] }], _templateQuery: [{ type: ContentChild, args: [DataTableFooterTemplateDirective, { read: TemplateRef }] }] } }); class ScrollerComponent { constructor() { this.renderer = inject(Renderer2); this.scrollbarV = false; this.scrollbarH = false; this.scroll = new EventEmitter(); this.scrollYPos = 0; this.scrollXPos = 0; this.prevScrollYPos = 0; this.prevScrollXPos = 0; this.element = inject(ElementRef).nativeElement; this._scrollEventListener = null; } ngOnInit() { // manual bind so we don't always listen if (this.scrollbarV || this.scrollbarH) { const renderer = this.renderer; this.parentElement = renderer.parentNode(renderer.parentNode(this.element)); this._scrollEventListener = this.onScrolled.bind(this); this.parentElement.addEventListener('scroll', this._scrollEventListener); } } ngOnDestroy() { if (this._scrollEventListener) { this.parentElement.removeEventListener('scroll', this._scrollEventListener); this._scrollEventListener = null; } } setOffset(offsetY) { if (this.parentElement) { this.parentElement.scrollTop = offsetY; } } onScrolled(event) { const dom = event.currentTarget; requestAnimationFrame(() => { this.scrollYPos = dom.scrollTop; this.scrollXPos = dom.scrollLeft; this.updateOffset(); }); } updateOffset() { let direction; if (this.scrollYPos < this.prevScrollYPos) { direction = 'down'; } else if (this.scrollYPos > this.prevScrollYPos) { direction = 'up'; } this.scroll.emit({ direction, scrollYPos: this.scrollYPos, scrollXPos: this.scrollXPos }); this.prevScrollYPos = this.scrollYPos; this.prevScrollXPos = this.scrollXPos; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.9", ngImport: i0, type: ScrollerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.9", type: ScrollerComponent, isStandalone: true, selector: "datatable-scroller", inputs: { scrollbarV: "scrollbarV", scrollbarH: "scrollbarH", scrollHeight: "scrollHeight", scrollWidth: "scrollWidth" }, outputs: { scroll: "scroll" }, host: { properties: { "style.height.px": "this.scrollHeight", "style.width.px": "this.scrollWidth" }, classAttribute: "datatable-scroll" }, ngImport: i0, template: ` <ng-content></ng-content> `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.9", ngImport: i0, type: ScrollerComponent, decorators: [{ type: Component, args: [{ selector: 'datatable-scroller', template: ` <ng-content></ng-content> `, host: { class: 'datatable-scroll' }, changeDetection: ChangeDetectionStrategy.OnPush, standalone: true }] }], propDecorators: { scrollbarV: [{ type: Input }], scrollbarH: [{ type: Input }], scrollHeight: [{ type: HostBinding, args: ['style.height.px'] }, { type: Input }], scrollWidth: [{ type: HostBinding, args: ['style.width.px'] }, { type: Input }], scroll: [{ type: Output }] } }); /** * Returns the columns by pin. */ function columnsByPin(cols) { const ret = { left: [], center: [], right: [] }; if (cols) { for (const col of cols) { if (col.frozenLeft) { ret.left.push(col); } else if (col.frozenRight) { ret.right.push(col); } else { ret.center.push(col); } } } return ret; } /** * Returns the widths of all group sets of a column */ function columnGroupWidths(groups, all) { return { left: columnTotalWidth(groups.left), center: columnTotalWidth(groups.center), right: columnTotalWidth(groups.right), total: Math.floor(columnTotalWidth(all)) }; } /** * Calculates the total width of all columns and their groups */ function columnTotalWidth(columns, prop) { let totalWidth = 0; if (columns) { for (const c of columns) { const has = prop && c[prop]; const width = has ? c[prop] : c.width; totalWidth = totalWidth + parseFloat(width); } } return totalWidth; } /** * Calculates the total width of all columns and their groups */ function columnsTotalWidth(columns, prop) { let totalWidth = 0; for (const column of columns) { const has = prop && column[prop]; totalWidth = totalWidth + (has ? column[prop] : column.width); } return totalWidth; } function columnsByPinArr(val) { const colsByPin = columnsByPin(val); return [ { type: 'left', columns: colsByPin.left }, { type: 'center', columns: colsByPin.center }, { type: 'right', columns: colsByPin.right } ]; } /** * This object contains the cache of the various row heights that are present inside * the data table. Its based on Fenwick tree data structure that helps with * querying sums that have time complexity of log n. * * Fenwick Tree Credits: http://petr-mitrichev.blogspot.com/2013/05/fenwick-tree-range-updates.html * https://github.com/mikolalysenko/fenwick-tree * */ class RowHeightCache { constructor() { /** * Tree Array stores the cumulative information of the row heights to perform efficient * range queries and updates. Currently the tree is initialized to the base row * height instead of the detail row height. */ this.treeArray = []; } /** * Clear the Tree array. */ clearCache() { this.treeArray = []; } /** * Initialize the Fenwick tree with row Heights. * * @param rows The array of rows which contain the expanded status. * @param rowHeight The row height. * @param detailRowHeight The detail row height. */ initCache(details) { const { rows, rowHeight, detailRowHeight, externalVirtual, rowCount, rowIndexes, rowExpansions } = details; const isFn = typeof rowHeight === 'function'; const isDetailFn = typeof detailRowHeight === 'function'; if (!isFn && isNaN(rowHeight)) { throw new Error(`Row Height cache initialization failed. Please ensure that 'rowHeight' is a valid number or function value: (${rowHeight}) when 'scrollbarV' is enabled.`); } // Add this additional guard in case detailRowHeight is set to 'auto' as it wont work. if (!isDetailFn && isNaN(detailRowHeight)) { throw new Error(`Row Height cache initialization failed. Please ensure that 'detailRowHeight' is a valid number or function value: (${detailRowHeight}) when 'scrollbarV' is enabled.`); } const n = externalVirtual ? rowCount : rows.length; this.treeArray = new Array(n); for (let i = 0; i < n; ++i) { this.treeArray[i] = 0; } for (let i = 0; i < n; ++i) { const row = rows[i]; let currentRowHeight = rowHeight; if (isFn) { currentRowHeight = rowHeight(row); } // Add the detail row height to the already expanded rows. // This is useful for the table that goes through a filter or sort. const expanded = rowExpansions.has(row); if (row && expanded) { if (isDetailFn) { const index = rowIndexes.get(row); currentRowHeight += detailRowHeight(row, index); } else { currentRowHeight += detailRowHeight; } } this.update(i, currentRowHeight); } } /** * Given the ScrollY position i.e. sum, provide the rowIndex * that is present in the current view port. Below handles edge cases. */ getRowIndex(scrollY) { if (scrollY === 0) { return 0; } return this.calcRowIndex(scrollY); } /** * When a row is expanded or rowHeight is changed, update the height. This can * be utilized in future when Angular Data table supports dynamic row heights. */ update(atRowIndex, byRowHeight) { if (!this.treeArray.length) { throw new Error(`Update at index ${atRowIndex} with value ${byRowHeight} failed: Row Height cache not initialized.`); } const n = this.treeArray.length; atRowIndex |= 0; while (atRowIndex < n) { this.treeArray[atRowIndex] += byRowHeight; atRowIndex |= atRowIndex + 1; } } /** * Range Sum query from 1 to the rowIndex */ query(atIndex) { if (!this.treeArray.length) { throw new Error(`query at index ${atIndex} failed: Fenwick tree array not initialized.`); } let sum = 0; atIndex |= 0; while (atIndex >= 0) { sum += this.treeArray[atIndex]; atIndex = (atIndex & (atIndex + 1)) - 1; } return sum; } /** * Find the total height between 2 row indexes */ queryBetween(atIndexA, atIndexB) { return this.query(atIndexB) - this.query(atIndexA - 1); } /** * Given the ScrollY position i.e. sum, provide the rowIndex * that is present in the current view port. */ calcRowIndex(sum) { if (!this.treeArray.length) { return 0; } let pos = -1; const dataLength = this.treeArray.length; // Get the highest bit for the block size. const highestBit = Math.pow(2, dataLength.toString(2).length - 1); for (let blockSize = highestBit; blockSize !== 0; blockSize >>= 1) { const nextPos = pos + blockSize; if (nextPos < dataLength && sum >= this.treeArray[nextPos]) { sum -= this.treeArray[nextPos]; pos = nextPos; } } return pos + 1; } } var Keys; (function (Keys) { Keys["up"] = "ArrowUp"; Keys["down"] = "ArrowDown"; Keys["return"] = "Enter"; Keys["escape"] = "Escape"; Keys["left"] = "ArrowLeft"; Keys["right"] = "ArrowRight"; })(Keys || (Keys = {})); var SortDirection; (function (SortDirection) { SortDirection["asc"] = "asc"; SortDirection["desc"] = "desc"; })(SortDirection || (SortDirection = {})); var SortType; (function (SortType) { SortType["single"] = "single"; SortType["multi"] = "multi"; })(SortType || (SortType = {})); var ColumnMode; (function (ColumnMode) { ColumnMode["standard"] = "standard"; ColumnMode["flex"] = "flex"; ColumnMode["force"] = "force"; })(ColumnMode || (ColumnMode = {})); var ContextmenuType; (function (ContextmenuType) { ContextmenuType["header"] = "header"; ContextmenuType["body"] = "body"; })(ContextmenuType || (ContextmenuType = {})); var SelectionType; (function (SelectionType) { SelectionType["single"] = "single"; SelectionType["multi"] = "multi"; SelectionType["multiClick"] = "multiClick"; SelectionType["cell"] = "cell"; SelectionType["checkbox"] = "checkbox"; })(SelectionType || (SelectionType = {})); class DataTableGhostLoaderComponent { static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.9", ngImport: i0, type: DataTableGhostLoaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.9", type: DataTableGhostLoaderComponent, isStandalone: true, selector: "ghost-loader", inputs: { columns: "columns", pageSize: ["pageSize", "pageSize", numberAttribute], rowHeight: "rowHeight", ghostBodyHeight: ["ghostBodyHeight", "ghostBodyHeight", numberAttribute] }, ngImport: i0, template: "<div [style.height]=\"ghostBodyHeight + 'px'\" class=\"ghost-loader ghost-cell-container\">\n @for (item of [].constructor(pageSize); track item) {\n <div [style.height]=\"rowHeight + 'px'\" class=\"ghost-element\">\n @for (col of columns; track col) {\n @if (!col.ghostCellTemplate) {\n <div class=\"line ghost-cell-strip\" [style.width]=\"col?.width + 'px'\"> </div>\n } @else {\n <ng-template [ngTemplateOutlet]=\"col.ghostCellTemplate\"> </ng-template>\n }\n }\n </div>\n }\n</div>\n", styles: ["@keyframes ghost{0%{background-position:0vw 0}to{background-position:100vw 0}}.ghost-loader{overflow:hidden}.ghost-loader .line{width:100%;height:12px;animation-name:ghost;animation-iteration-count:infinite;animation-timing-function:linear}.ghost-loader .ghost-element{display:flex}:host.ghost-overlay{position:sticky;top:20px}:host.ghost-overlay .ghost-loader .line{margin:.9rem 1.2rem}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.9", ngImport: i0, type: DataTableGhostLoaderComponent, decorators: [{ type: Component, args: [{ selector: `ghost-loader`, changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgTemplateOutlet], template: "<div [style.height]=\"ghostBodyHeight + 'px'\" class=\"ghost-loader ghost-cell-container\">\n @for (item of [].constructor(pageSize); track item) {\n <div [style.height]=\"rowHeight + 'px'\" class=\"ghost-element\">\n @for (col of columns; track col) {\n @if (!col.ghostCellTemplate) {\n <div class=\"line ghost-cell-strip\" [style.width]=\"col?.width + 'px'\"> </div>\n } @else {\n <ng-template [ngTemplateOutlet]=\"col.ghostCellTemplate\"> </ng-template>\n }\n }\n </div>\n }\n</div>\n", styles: ["@keyframes ghost{0%{background-position:0vw 0}to{background-position:100vw 0}}.ghost-loader{overflow:hidden}.ghost-loader .line{width:100%;height:12px;animation-name:ghost;animation-iteration-count:infinite;animation-timing-function:linear}.ghost-loader .ghost-element{display:flex}:host.ghost-overlay{position:sticky;top:20px}:host.ghost-overlay .ghost-loader .line{margin:.9rem 1.2rem}\n"] }] }], propDecorators: { columns: [{ type: Input }], pageSize: [{ type: Input, args: [{ transform: numberAttribute }] }], rowHeight: [{ type: Input }], ghostBodyHeight: [{ type: Input, args: [{ transform: numberAttribute }] }] } }); class DataTableBodyCellComponent { set disable$(val) { this._disable$ = val; this.cellContext.disable$ = val; } get disable$() { return this._disable$; } set group(group) { this._group = group; this.cellContext.group = group; this.checkValueUpdates(); this.cd.markForCheck(); } get group() { return this._group; } set rowHeight(val) { this._rowHeight = val; this.cellContext.rowHeight = val; this.checkValueUpdates(); this.cd.markForCheck(); } get rowHeight() { return this._rowHeight; } set isSelected(val) { this._isSelected = val; this.cellContext.isSelected = val; this.cd.markForCheck(); } get isSelected() { return this._isSelected; } set expanded(val) { this._expanded = val; this.cellContext.expanded = val; this.cd.markForCheck(); } get expanded() { return this._expanded; } set rowIndex(val) { this._rowIndex = val; this.cellContext.rowIndex = val; this.checkValueUpdates(); this.cd.markForCheck(); } get rowIndex() { return this._rowIndex; } set column(column) { this._column = column; this.cellContext.column = column; this.checkValueUpdates(); this.cd.markForCheck(); } get column() { return this._column; } set row(row) { this._row = row; this.cellContext.row = row; this.checkValueUpdates(); this.cd.markForCheck(); } get row() { return this._row; } set sorts(val) { this._sorts = val; this.sortDir = this.calcSortDir(val); } get sorts() { return this._sorts; } set treeStatus(status) { if (status !== 'collapsed' && status !== 'expanded' && status !== 'loading' && status !== 'disabled') { this._treeStatus = 'collapsed'; } else { this._treeStatus = status; } this.cellContext.treeStatus = this._treeStatus; this.checkValueUpdates(); this.cd.markForCheck(); } get treeStatus() { return this._treeStatus; } get columnCssClasses() { let cls = 'datatable-body-cell'; if (this.column.cellClass) { if (typeof this.column.cellClass === 'string') { cls += ' ' + this.column.cellClass; } else if (typeof this.column.cellClass === 'function') { const res = this.column.cellClass({ row: this.row, group: this.group, column: this.column, value: this.value, rowHeight: this.rowHeight }); if (typeof res === 'string') { cls += ' ' + res; } else if (typeof res === 'object') { const keys = Object.keys(res); for (const k of keys) { if (res[k] === true) { cls += ` ${k}`; } } } } } if (!this.sortDir) { cls += ' sort-active'; } if (this.isFocused && !this.disable$?.value) { cls += ' active'; } if (this.sortDir === SortDirection.asc) { cls += ' sort-asc'; } if (this.sortDir === SortDirection.desc) { cls += ' sort-desc'; } if (this.disable$?.value) { cls += ' row-disabled'; } return cls; } get width() { return this.column.width; } get minWidth() { return this.column.minWidth; } get maxWidth() { return this.column.maxWidth; } get height() { const height = this.rowHeight; if (isNaN(height)) { return height; } return height + 'px'; }