@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
JavaScript
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';
}