@siemens/ngx-datatable
Version:
ngx-datatable is an Angular table grid component for presenting large and complex data.
952 lines (933 loc) • 338 kB
JavaScript
import * as i0 from '@angular/core';
import { Directive, input, contentChild, TemplateRef, computed, output, inject, ElementRef, InjectionToken, booleanAttribute, effect, Component, ViewContainerRef, Injector, numberAttribute, DOCUMENT, Service, signal, ChangeDetectionStrategy, KeyValueDiffers, HostListener, linkedSignal, ChangeDetectorRef, DestroyRef, model, viewChild, viewChildren, untracked, Input, ContentChildren, IterableDiffers, contentChildren, afterNextRender, ContentChild, NgModule } from '@angular/core';
import { NgTemplateOutlet } from '@angular/common';
import { startWith } from 'rxjs';
class DatatableGroupHeaderTemplateDirective {
static ngTemplateContextGuard(directive, context) {
return true;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DatatableGroupHeaderTemplateDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.8", type: DatatableGroupHeaderTemplateDirective, isStandalone: true, selector: "[ngx-datatable-group-header-template]", ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DatatableGroupHeaderTemplateDirective, decorators: [{
type: Directive,
args: [{
selector: '[ngx-datatable-group-header-template]'
}]
}] });
class DatatableGroupHeaderDirective {
/**
* Row height is required when virtual scroll is enabled.
*/
rowHeight = input(0, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "rowHeight" }] : /* istanbul ignore next */ []));
/**
* Show checkbox at group header to select all rows of the group.
*/
checkboxable = input(false, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "checkboxable" }] : /* istanbul ignore next */ []));
_templateInput = input(undefined, { ...(ngDevMode ? { debugName: "_templateInput" } : /* istanbul ignore next */ {}), alias: 'template' });
_templateQuery = contentChild(DatatableGroupHeaderTemplateDirective, { ...(ngDevMode ? { debugName: "_templateQuery" } : /* istanbul ignore next */ {}), read: TemplateRef });
template = computed(() => this._templateInput() ?? this._templateQuery() ?? null, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "template" }] : /* istanbul ignore next */ []));
/**
* Track toggling of group visibility
*/
toggle = output();
/**
* 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 ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DatatableGroupHeaderDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "22.0.8", type: DatatableGroupHeaderDirective, isStandalone: true, selector: "ngx-datatable-group-header", inputs: { rowHeight: { classPropertyName: "rowHeight", publicName: "rowHeight", isSignal: true, isRequired: false, transformFunction: null }, checkboxable: { classPropertyName: "checkboxable", publicName: "checkboxable", isSignal: true, isRequired: false, transformFunction: null }, _templateInput: { classPropertyName: "_templateInput", publicName: "template", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { toggle: "toggle" }, queries: [{ propertyName: "_templateQuery", first: true, predicate: DatatableGroupHeaderTemplateDirective, descendants: true, read: TemplateRef, isSignal: true }], ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DatatableGroupHeaderDirective, decorators: [{
type: Directive,
args: [{
selector: 'ngx-datatable-group-header'
}]
}], propDecorators: { rowHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowHeight", required: false }] }], checkboxable: [{ type: i0.Input, args: [{ isSignal: true, alias: "checkboxable", required: false }] }], _templateInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "template", required: false }] }], _templateQuery: [{ type: i0.ContentChild, args: [i0.forwardRef(() => DatatableGroupHeaderTemplateDirective), { ...{
read: TemplateRef
}, isSignal: true }] }], toggle: [{ type: i0.Output, args: ["toggle"] }] } });
/**
* Renders the datatable's normal row inside a custom row wrapper declared with
* {@link DatatableRowDefDirective}. Use this as the root content of the
* `ng-template` and apply row-level directives or classes to it.
*
* @example
* ```html
* <ngx-datatable>
* <ng-template rowDef>
* <datatable-row-def appCustomRowDirective class="custom-row" />
* </ng-template>
* </ngx-datatable>
* ```
*/
class DatatableRowDefComponent {
host = inject(ElementRef).nativeElement;
rowDef = inject(ROW_DEF_TOKEN);
rowContext = {
...this.rowDef.rowDefInternal(),
disabled: this.rowDef.rowDefInternalDisabled()
};
/**
* When `true`, clones of this row get the measured column widths stamped onto them.
* The clone is detached from the table grid, it has no parent tracks to inherit.
*/
preserveColumnWidthsOnClone = input(false, { ...(ngDevMode ? { debugName: "preserveColumnWidthsOnClone" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
constructor() {
effect(onCleanup => {
if (!this.preserveColumnWidthsOnClone()) {
return;
}
const originalCloneNode = this.host.cloneNode;
this.host.cloneNode = (deep) => {
const clone = originalCloneNode.call(this.host, deep);
const gridTemplateColumns = this.measureGridTemplateColumns();
if (gridTemplateColumns) {
clone.style.gridTemplateColumns = gridTemplateColumns;
}
return clone;
};
onCleanup(() => (this.host.cloneNode = originalCloneNode));
});
}
measureGridTemplateColumns() {
const cells = this.host.querySelectorAll('datatable-body-cell');
return Array.from(cells)
.map(cell => `${cell.getBoundingClientRect().width}px`)
.join(' ');
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DatatableRowDefComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: DatatableRowDefComponent, isStandalone: true, selector: "datatable-row-def", inputs: { preserveColumnWidthsOnClone: { classPropertyName: "preserveColumnWidthsOnClone", publicName: "preserveColumnWidthsOnClone", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `@if (rowDef.rowDefInternal().rowTemplate) {
<ng-container
[ngTemplateOutlet]="rowDef.rowDefInternal().rowTemplate"
[ngTemplateOutletContext]="rowContext"
/>
}`, isInline: true, styles: [":host{display:grid;grid-template-columns:subgrid;grid-column:1/-1}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DatatableRowDefComponent, decorators: [{
type: Component,
args: [{ selector: 'datatable-row-def', imports: [NgTemplateOutlet], template: `@if (rowDef.rowDefInternal().rowTemplate) {
<ng-container
[ngTemplateOutlet]="rowDef.rowDefInternal().rowTemplate"
[ngTemplateOutletContext]="rowContext"
/>
}`, styles: [":host{display:grid;grid-template-columns:subgrid;grid-column:1/-1}\n"] }]
}], ctorParameters: () => [], propDecorators: { preserveColumnWidthsOnClone: [{ type: i0.Input, args: [{ isSignal: true, alias: "preserveColumnWidthsOnClone", required: false }] }] } });
/**
* Marks an `ng-template` as the custom wrapper for each rendered table row.
*
* The template must contain a {@link DatatableRowDefComponent}, which renders
* the table's regular row inside the wrapper. Apply row-level directives or
* classes to that component.
*
* @example
* ```html
* <ngx-datatable>
* <ng-template rowDef>
* <datatable-row-def appCustomRowDirective class="custom-row" />
* </ng-template>
* </ngx-datatable>
* ```
*/
class DatatableRowDefDirective {
static ngTemplateContextGuard(_dir, ctx) {
return true;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DatatableRowDefDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.8", type: DatatableRowDefDirective, isStandalone: true, selector: "[rowDef]", ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DatatableRowDefDirective, decorators: [{
type: Directive,
args: [{
selector: '[rowDef]'
}]
}] });
/**
* @internal To be used internally by ngx-datatable.
*/
class DatatableRowDefInternalDirective {
vc = inject(ViewContainerRef);
rowDefInternal = input.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "rowDefInternal" }] : /* istanbul ignore next */ []));
rowDefInternalDisabled = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "rowDefInternalDisabled" }] : /* istanbul ignore next */ []));
ngOnInit() {
this.vc.createEmbeddedView(this.rowDefInternal().template, {
...this.rowDefInternal()
}, {
injector: Injector.create({
providers: [
{
provide: ROW_DEF_TOKEN,
useValue: this
}
]
})
});
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DatatableRowDefInternalDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.8", type: DatatableRowDefInternalDirective, isStandalone: true, selector: "[rowDefInternal]", inputs: { rowDefInternal: { classPropertyName: "rowDefInternal", publicName: "rowDefInternal", isSignal: true, isRequired: true, transformFunction: null }, rowDefInternalDisabled: { classPropertyName: "rowDefInternalDisabled", publicName: "rowDefInternalDisabled", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DatatableRowDefInternalDirective, decorators: [{
type: Directive,
args: [{
selector: '[rowDefInternal]'
}]
}], propDecorators: { rowDefInternal: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowDefInternal", required: true }] }], rowDefInternalDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowDefInternalDisabled", required: false }] }] } });
const ROW_DEF_TOKEN = new InjectionToken('RowDef');
/**
* Directive that provides custom content for the summary row.
* When applied, the summary row renders the provided template instead of
* computing per-column aggregate values, enabling use cases like summary action bars.
*
* The row is rendered with sticky positioning at the top of the datatable body.
*
* @example
* ```html
* <ngx-datatable [rows]="rows" selectionType="checkbox" [(selected)]="selected">
* @if (selected.length) {
* <ng-template ngx-datatable-summary-row>
* <span>{{ selected.length }} selected</span>
* <button (click)="delete()">Delete</button>
* </ng-template>
* }
* </ngx-datatable>
* ```
*/
class DatatableSummaryRowDirective {
template = inject((TemplateRef));
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DatatableSummaryRowDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.8", type: DatatableSummaryRowDirective, isStandalone: true, selector: "[ngx-datatable-summary-row]", ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DatatableSummaryRowDirective, decorators: [{
type: Directive,
args: [{
selector: '[ngx-datatable-summary-row]'
}]
}] });
class DataTableColumnCellDirective {
template = inject(TemplateRef);
static ngTemplateContextGuard(dir, ctx) {
return true;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DataTableColumnCellDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.8", type: DataTableColumnCellDirective, isStandalone: true, selector: "[ngx-datatable-cell-template]", ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DataTableColumnCellDirective, decorators: [{
type: Directive,
args: [{
selector: '[ngx-datatable-cell-template]'
}]
}] });
class DataTableColumnGhostCellDirective {
static ngTemplateContextGuard(directive, context) {
return true;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DataTableColumnGhostCellDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.8", type: DataTableColumnGhostCellDirective, isStandalone: true, selector: "[ngx-datatable-ghost-cell-template]", ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DataTableColumnGhostCellDirective, decorators: [{
type: Directive,
args: [{
selector: '[ngx-datatable-ghost-cell-template]'
}]
}] });
class DataTableColumnHeaderDirective {
static ngTemplateContextGuard(directive, context) {
return true;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DataTableColumnHeaderDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.8", type: DataTableColumnHeaderDirective, isStandalone: true, selector: "[ngx-datatable-header-template]", ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DataTableColumnHeaderDirective, decorators: [{
type: Directive,
args: [{
selector: '[ngx-datatable-header-template]'
}]
}] });
class DataTableColumnCellTreeToggle {
template = inject(TemplateRef);
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DataTableColumnCellTreeToggle, deps: [], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.8", type: DataTableColumnCellTreeToggle, isStandalone: true, selector: "[ngx-datatable-tree-toggle]", ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DataTableColumnCellTreeToggle, decorators: [{
type: Directive,
args: [{
selector: '[ngx-datatable-tree-toggle]'
}]
}] });
class DataTableColumnDirective {
/**
* Column label. When omitted, the property value is used and decamelized.
*/
name = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "name" }] : /* istanbul ignore next */ []));
/**
* Property used to bind row values. When omitted, the name is converted to camel case.
*/
prop = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "prop" }] : /* istanbul ignore next */ []));
bindAsUnsafeHtml = input(false, { ...(ngDevMode ? { debugName: "bindAsUnsafeHtml" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
/**
* Whether the column is frozen to the left. Default value: `false`.
*/
frozenLeft = input(false, { ...(ngDevMode ? { debugName: "frozenLeft" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
/**
* Whether the column is frozen to the right. Default value: `false`.
*/
frozenRight = input(false, { ...(ngDevMode ? { debugName: "frozenRight" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
/**
* Grow factor relative to other columns. Available extra width is distributed
* proportionally according to all columns' `flexGrow` values. Default value: `0`.
*/
flexGrow = input(undefined, { ...(ngDevMode ? { debugName: "flexGrow" } : /* istanbul ignore next */ {}), transform: numberAttribute });
/**
* Whether the user can manually resize the column. Default value: `true`.
*/
resizeable = input(undefined, { ...(ngDevMode ? { debugName: "resizeable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
/**
* Custom client-side sort comparator. It receives cell values and their
* respective rows; a standard two-argument comparison function is also supported.
*/
comparator = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "comparator" }] : /* istanbul ignore next */ []));
/**
* Custom pipe used to transform cell values.
*/
pipe = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "pipe" }] : /* istanbul ignore next */ []));
/**
* Whether row values can be sorted by this column. Default value: `true`.
*/
sortable = input(undefined, { ...(ngDevMode ? { debugName: "sortable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
/**
* Whether the column can be dragged to reorder it. Default value: `true`.
*/
draggable = input(undefined, { ...(ngDevMode ? { debugName: "draggable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
/**
* Whether the column can automatically resize to fill extra space. Default value: `true`.
*/
canAutoResize = input(undefined, { ...(ngDevMode ? { debugName: "canAutoResize" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
/**
* Minimum column width in pixels.
*/
minWidth = input(undefined, { ...(ngDevMode ? { debugName: "minWidth" } : /* istanbul ignore next */ {}), transform: numberAttribute });
/**
* Default column width in pixels. Default value: `150`.
*/
width = input(undefined, { ...(ngDevMode ? { debugName: "width" } : /* istanbul ignore next */ {}), transform: numberAttribute });
/**
* Maximum column width in pixels.
*/
maxWidth = input(undefined, { ...(ngDevMode ? { debugName: "maxWidth" } : /* istanbul ignore next */ {}), transform: numberAttribute });
/**
* Whether the column displays a selection checkbox. Only applies when selection mode is `checkbox`.
*/
checkboxable = input(false, { ...(ngDevMode ? { debugName: "checkboxable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
/**
* Whether the header displays a selection checkbox. Only applies when selection mode is `checkbox`.
*/
headerCheckboxable = input(false, { ...(ngDevMode ? { debugName: "headerCheckboxable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
/**
* CSS classes to apply to the header cell.
*/
headerClass = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "headerClass" }] : /* istanbul ignore next */ []));
/**
* CSS classes to apply to the body cell.
*/
cellClass = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "cellClass" }] : /* istanbul ignore next */ []));
isTreeColumn = input(false, { ...(ngDevMode ? { debugName: "isTreeColumn" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
treeLevelIndent = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "treeLevelIndent" }] : /* istanbul ignore next */ []));
summaryFunc = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "summaryFunc" }] : /* istanbul ignore next */ []));
summaryTemplate = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "summaryTemplate" }] : /* istanbul ignore next */ []));
/**
* Template used to render body cells.
*/
cellTemplateInput = input(undefined, { ...(ngDevMode ? { debugName: "cellTemplateInput" } : /* istanbul ignore next */ {}), alias: 'cellTemplate' });
cellTemplateQuery = contentChild(DataTableColumnCellDirective, { ...(ngDevMode ? { debugName: "cellTemplateQuery" } : /* istanbul ignore next */ {}), read: TemplateRef });
/**
* Template used to render header cells.
*/
headerTemplateInput = input(undefined, { ...(ngDevMode ? { debugName: "headerTemplateInput" } : /* istanbul ignore next */ {}), alias: 'headerTemplate' });
headerTemplateQuery = contentChild(DataTableColumnHeaderDirective, { ...(ngDevMode ? { debugName: "headerTemplateQuery" } : /* istanbul ignore next */ {}), read: TemplateRef });
treeToggleTemplateInput = input(undefined, { ...(ngDevMode ? { debugName: "treeToggleTemplateInput" } : /* istanbul ignore next */ {}), alias: 'treeToggleTemplate' });
treeToggleTemplateQuery = contentChild(DataTableColumnCellTreeToggle, { ...(ngDevMode ? { debugName: "treeToggleTemplateQuery" } : /* istanbul ignore next */ {}), read: TemplateRef });
ghostCellTemplateInput = input(undefined, { ...(ngDevMode ? { debugName: "ghostCellTemplateInput" } : /* istanbul ignore next */ {}), alias: 'ghostCellTemplate' });
ghostCellTemplateQuery = contentChild(DataTableColumnGhostCellDirective, { ...(ngDevMode ? { debugName: "ghostCellTemplateQuery" } : /* istanbul ignore next */ {}), read: TemplateRef });
/**
* Computed property that returns the column configuration as a TableColumn object
*/
column = computed(() => ({
name: this.name(),
prop: this.prop(),
bindAsUnsafeHtml: this.bindAsUnsafeHtml(),
frozenLeft: this.frozenLeft(),
frozenRight: this.frozenRight(),
flexGrow: this.flexGrow(),
resizeable: this.resizeable(),
comparator: this.comparator(),
pipe: this.pipe(),
sortable: this.sortable(),
draggable: this.draggable(),
canAutoResize: this.canAutoResize(),
minWidth: this.minWidth(),
width: this.width(),
maxWidth: this.maxWidth(),
checkboxable: this.checkboxable(),
headerCheckboxable: this.headerCheckboxable(),
headerClass: this.headerClass(),
cellClass: this.cellClass(),
isTreeColumn: this.isTreeColumn(),
treeLevelIndent: this.treeLevelIndent(),
summaryFunc: this.summaryFunc(),
summaryTemplate: this.summaryTemplate(),
cellTemplate: this.cellTemplateInput() ?? this.cellTemplateQuery(),
headerTemplate: this.headerTemplateInput() ?? this.headerTemplateQuery(),
treeToggleTemplate: this.treeToggleTemplateInput() ?? this.treeToggleTemplateQuery(),
ghostCellTemplate: this.ghostCellTemplateInput() ?? this.ghostCellTemplateQuery()
}), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "column" }] : /* istanbul ignore next */ []));
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DataTableColumnDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "22.0.8", type: DataTableColumnDirective, isStandalone: true, selector: "ngx-datatable-column", inputs: { name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, prop: { classPropertyName: "prop", publicName: "prop", isSignal: true, isRequired: false, transformFunction: null }, bindAsUnsafeHtml: { classPropertyName: "bindAsUnsafeHtml", publicName: "bindAsUnsafeHtml", isSignal: true, isRequired: false, transformFunction: null }, frozenLeft: { classPropertyName: "frozenLeft", publicName: "frozenLeft", isSignal: true, isRequired: false, transformFunction: null }, frozenRight: { classPropertyName: "frozenRight", publicName: "frozenRight", isSignal: true, isRequired: false, transformFunction: null }, flexGrow: { classPropertyName: "flexGrow", publicName: "flexGrow", isSignal: true, isRequired: false, transformFunction: null }, resizeable: { classPropertyName: "resizeable", publicName: "resizeable", isSignal: true, isRequired: false, transformFunction: null }, comparator: { classPropertyName: "comparator", publicName: "comparator", isSignal: true, isRequired: false, transformFunction: null }, pipe: { classPropertyName: "pipe", publicName: "pipe", isSignal: true, isRequired: false, transformFunction: null }, sortable: { classPropertyName: "sortable", publicName: "sortable", isSignal: true, isRequired: false, transformFunction: null }, draggable: { classPropertyName: "draggable", publicName: "draggable", isSignal: true, isRequired: false, transformFunction: null }, canAutoResize: { classPropertyName: "canAutoResize", publicName: "canAutoResize", isSignal: true, isRequired: false, transformFunction: null }, minWidth: { classPropertyName: "minWidth", publicName: "minWidth", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, maxWidth: { classPropertyName: "maxWidth", publicName: "maxWidth", isSignal: true, isRequired: false, transformFunction: null }, checkboxable: { classPropertyName: "checkboxable", publicName: "checkboxable", isSignal: true, isRequired: false, transformFunction: null }, headerCheckboxable: { classPropertyName: "headerCheckboxable", publicName: "headerCheckboxable", isSignal: true, isRequired: false, transformFunction: null }, headerClass: { classPropertyName: "headerClass", publicName: "headerClass", isSignal: true, isRequired: false, transformFunction: null }, cellClass: { classPropertyName: "cellClass", publicName: "cellClass", isSignal: true, isRequired: false, transformFunction: null }, isTreeColumn: { classPropertyName: "isTreeColumn", publicName: "isTreeColumn", isSignal: true, isRequired: false, transformFunction: null }, treeLevelIndent: { classPropertyName: "treeLevelIndent", publicName: "treeLevelIndent", isSignal: true, isRequired: false, transformFunction: null }, summaryFunc: { classPropertyName: "summaryFunc", publicName: "summaryFunc", isSignal: true, isRequired: false, transformFunction: null }, summaryTemplate: { classPropertyName: "summaryTemplate", publicName: "summaryTemplate", isSignal: true, isRequired: false, transformFunction: null }, cellTemplateInput: { classPropertyName: "cellTemplateInput", publicName: "cellTemplate", isSignal: true, isRequired: false, transformFunction: null }, headerTemplateInput: { classPropertyName: "headerTemplateInput", publicName: "headerTemplate", isSignal: true, isRequired: false, transformFunction: null }, treeToggleTemplateInput: { classPropertyName: "treeToggleTemplateInput", publicName: "treeToggleTemplate", isSignal: true, isRequired: false, transformFunction: null }, ghostCellTemplateInput: { classPropertyName: "ghostCellTemplateInput", publicName: "ghostCellTemplate", isSignal: true, isRequired: false, transformFunction: null } }, queries: [{ propertyName: "cellTemplateQuery", first: true, predicate: DataTableColumnCellDirective, descendants: true, read: TemplateRef, isSignal: true }, { propertyName: "headerTemplateQuery", first: true, predicate: DataTableColumnHeaderDirective, descendants: true, read: TemplateRef, isSignal: true }, { propertyName: "treeToggleTemplateQuery", first: true, predicate: DataTableColumnCellTreeToggle, descendants: true, read: TemplateRef, isSignal: true }, { propertyName: "ghostCellTemplateQuery", first: true, predicate: DataTableColumnGhostCellDirective, descendants: true, read: TemplateRef, isSignal: true }], ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DataTableColumnDirective, decorators: [{
type: Directive,
args: [{
selector: 'ngx-datatable-column'
}]
}], propDecorators: { name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], prop: [{ type: i0.Input, args: [{ isSignal: true, alias: "prop", required: false }] }], bindAsUnsafeHtml: [{ type: i0.Input, args: [{ isSignal: true, alias: "bindAsUnsafeHtml", required: false }] }], frozenLeft: [{ type: i0.Input, args: [{ isSignal: true, alias: "frozenLeft", required: false }] }], frozenRight: [{ type: i0.Input, args: [{ isSignal: true, alias: "frozenRight", required: false }] }], flexGrow: [{ type: i0.Input, args: [{ isSignal: true, alias: "flexGrow", required: false }] }], resizeable: [{ type: i0.Input, args: [{ isSignal: true, alias: "resizeable", required: false }] }], comparator: [{ type: i0.Input, args: [{ isSignal: true, alias: "comparator", required: false }] }], pipe: [{ type: i0.Input, args: [{ isSignal: true, alias: "pipe", required: false }] }], sortable: [{ type: i0.Input, args: [{ isSignal: true, alias: "sortable", required: false }] }], draggable: [{ type: i0.Input, args: [{ isSignal: true, alias: "draggable", required: false }] }], canAutoResize: [{ type: i0.Input, args: [{ isSignal: true, alias: "canAutoResize", required: false }] }], minWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "minWidth", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], maxWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxWidth", required: false }] }], checkboxable: [{ type: i0.Input, args: [{ isSignal: true, alias: "checkboxable", required: false }] }], headerCheckboxable: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerCheckboxable", required: false }] }], headerClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerClass", required: false }] }], cellClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "cellClass", required: false }] }], isTreeColumn: [{ type: i0.Input, args: [{ isSignal: true, alias: "isTreeColumn", required: false }] }], treeLevelIndent: [{ type: i0.Input, args: [{ isSignal: true, alias: "treeLevelIndent", required: false }] }], summaryFunc: [{ type: i0.Input, args: [{ isSignal: true, alias: "summaryFunc", required: false }] }], summaryTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "summaryTemplate", required: false }] }], cellTemplateInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "cellTemplate", required: false }] }], cellTemplateQuery: [{ type: i0.ContentChild, args: [i0.forwardRef(() => DataTableColumnCellDirective), { ...{ read: TemplateRef }, isSignal: true }] }], headerTemplateInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerTemplate", required: false }] }], headerTemplateQuery: [{ type: i0.ContentChild, args: [i0.forwardRef(() => DataTableColumnHeaderDirective), { ...{
read: TemplateRef
}, isSignal: true }] }], treeToggleTemplateInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "treeToggleTemplate", required: false }] }], treeToggleTemplateQuery: [{ type: i0.ContentChild, args: [i0.forwardRef(() => DataTableColumnCellTreeToggle), { ...{
read: TemplateRef
}, isSignal: true }] }], ghostCellTemplateInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "ghostCellTemplate", required: false }] }], ghostCellTemplateQuery: [{ type: i0.ContentChild, args: [i0.forwardRef(() => DataTableColumnGhostCellDirective), { ...{
read: TemplateRef
}, isSignal: true }] }] } });
/**
* Marks the `role="table"` css-grid as the single scroll container that owns
* both the horizontal and vertical scroll. Descendant components (e.g. the
* scroller) and the datatable interact with the container through these methods
* instead of reaching for the native element.
*/
class ScrollContainerDirective {
element = inject(ElementRef).nativeElement;
/** Current vertical scroll offset of the container. */
get scrollTop() {
return this.element.scrollTop;
}
/** Whether the container's content overflows it vertically. */
get verticalScrollVisible() {
return this.element.scrollHeight > this.element.clientHeight;
}
setScrollTop(value) {
this.element.scrollTop = value;
}
scrollTo(top, options) {
this.element.scrollTo({ top, behavior: options?.behavior });
}
/** Subscribes to scroll events, returning a function that unsubscribes. */
listenToScroll(listener) {
this.element.addEventListener('scroll', listener, { passive: true });
return () => this.element.removeEventListener('scroll', listener);
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: ScrollContainerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.8", type: ScrollContainerDirective, isStandalone: true, selector: "[datatableScrollContainer]", ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: ScrollContainerDirective, decorators: [{
type: Directive,
args: [{
selector: '[datatableScrollContainer]'
}]
}] });
const NGX_DATATABLE_CONFIG = new InjectionToken('ngx-datatable.config');
/**
* Provides a global configuration for ngx-datatable.
*
* @param overrides The overrides of the table configuration.
*/
const providedNgxDatatableConfig = (overrides) => {
return {
provide: NGX_DATATABLE_CONFIG,
useValue: overrides
};
};
/**
* Gets the width of the scrollbar. Nesc for windows
* http://stackoverflow.com/a/13382873/888165
*/
class ScrollbarHelper {
document = inject(DOCUMENT);
width = this.getWidth();
getWidth() {
const outer = this.document.createElement('div');
outer.style.visibility = 'hidden';
outer.style.width = '100px';
this.document.body.appendChild(outer);
const widthNoScroll = outer.offsetWidth;
outer.style.overflow = 'scroll';
const inner = this.document.createElement('div');
inner.style.width = '100%';
outer.appendChild(inner);
const widthWithScroll = inner.offsetWidth;
this.document.body.removeChild(outer);
return widthNoScroll - widthWithScroll;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: ScrollbarHelper, deps: [], target: i0.ɵɵFactoryTarget.Service });
static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.0.8", ngImport: i0, type: ScrollbarHelper });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: ScrollbarHelper, decorators: [{
type: Service
}] });
/**
* Returns the columns by pin.
*/
const 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
*/
const 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
*/
const columnTotalWidth = (columns) => {
return columns?.reduce((total, column) => total + column.width(), 0) ?? 0;
};
/**
* Builds the `grid-template-columns` value for the combined css-grid.
* Produces one track per column, sized by the column's current width, in the
* pinned render order (left, center, right). Header and body rows share this
* single definition so every cell aligns to the same column tracks.
*
* `minWidth`/`maxWidth` are baked into the track via `clamp()`/`min()`/`max()`
* so the constraints are enforced once on the shared grid instead of per cell.
*/
const gridColumnTemplate = (cols) => {
return cols
.flatMap(group => group.columns)
.map(column => gridColumnTrack(column))
.join(' ');
};
const gridColumnTrack = (column) => {
const width = `${column.width()}px`;
const min = column.minWidth ? `${column.minWidth}px` : undefined;
const max = column.maxWidth ? `${column.maxWidth}px` : undefined;
if (min && max) {
return `clamp(${min}, ${width}, ${max})`;
}
if (min) {
return `max(${min}, ${width})`;
}
if (max) {
return `min(${max}, ${width})`;
}
return width;
};
const columnsByPinArr = (val) => {
const colsByPin = columnsByPin(val);
return [
{ type: 'left', columns: colsByPin.left },
{ type: 'center', columns: colsByPin.center },
{ type: 'right', columns: colsByPin.right }
];
};
/**
* Converts strings from something to camel case
* http://stackoverflow.com/questions/10425287/convert-dash-separated-string-to-camelcase
*/
const 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, (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
*/
const deCamelCase = (str) => {
return str.replace(/([A-Z])/g, match => ` ${match}`).replace(/^./, match => match.toUpperCase());
};
/**
* Always returns the empty string ''
*/
const emptyStringGetter = () => {
return '';
};
/**
* Returns the appropriate getter function for this kind of prop.
* If prop == null, returns the emptyStringGetter.
*/
const getterForProp = (prop) => {
// TODO requires better typing which will also involve adjust TableColum. So postponing it.
if (prop == null) {
return emptyStringGetter;
}
if (typeof prop === 'number') {
return numericIndexGetter;
}
else {
// deep or simple
if (prop.includes('.')) {
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
*/
const 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
*/
const 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']
*/
const 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 splits = path.split('.');
if (splits.length) {
for (const split of splits) {
current = current[split];
// if found undefined, return empty string
if (current === undefined || current === null) {
return '';
}
}
}
return current;
};
/**
* Creates a unique object id.
* http://stackoverflow.com/questions/6248666/how-to-generate-short-uid-like-ax4j9z-in-js
*/
const id = () => {
return ('0000' + ((Math.random() * Math.pow(36, 4)) << 0).toString(36)).slice(-4);
};
/**
* Gets the next sort direction
*/
const nextSortDir = (sortType, current) => {
if (sortType === 'single') {
if (current === 'asc') {
return 'desc';
}
else {
return 'asc';
}
}
else {
if (!current) {
return 'asc';
}
else if (current === 'asc') {
return 'desc';
}
else if (current === 'desc') {
return undefined;
}
// avoid TS7030: Not all code paths return a value.
return undefined;
}
};
/**
* Adapted from fueld-ui on 6/216
* https://github.com/FuelInteractive/fuel-ui/tree/master/src/pipes/OrderBy
*/
const orderByComparator = (a, b) => {
if (a === null || typeof a === 'undefined') {
a = 0;
}
if (b === null || typeof b === 'undefined') {
b = 0;
}
if (a instanceof Date && b instanceof Date) {
if (a < b) {
return -1;
}
if (a > b) {
return 1;
}
}
else if (isNaN(parseFloat(a)) || !isFinite(a) || isNaN(parseFloat(b)) || !isFinite(b)) {
// Convert to string in case of a=0 or b=0
a = String(a);
b = String(b);
// Isn't a number so lowercase the string to properly compare
if (a.toLowerCase() < b.toLowerCase()) {
return -1;
}
if (a.toLowerCase() > b.toLowerCase()) {
return 1;
}
}
else {
// Parse strings as numbers to compare properly
if (parseFloat(a) < parseFloat(b)) {
return -1;
}
if (parseFloat(a) > parseFloat(b)) {
return 1;
}
}
// equal each other
return 0;
};
/**
* creates a shallow copy of the `rows` input and returns the sorted copy. this function
* does not sort the `rows` argument in place
*/
const sortRows = (rows, columns, dirs, sortOnGroupHeader) => {
if (!rows) {
return [];
}
if (!dirs?.length || !columns) {
return [...rows];
}
const temp = [...rows];
const cols = columns.reduce((obj, col) => {
if (col.sortable) {
obj[col.prop] = col.comparator;
}
return obj;
}, {});
// cache valueGetter and compareFn so that they
// do not need to be looked-up in the sort function body
const cachedDirs = dirs.map(dir => {
// When sorting on group header, override prop to 'key'
const prop = sortOnGroupHeader?.prop === dir.prop ? 'key' : dir.prop;
// SortDirs may contain columns that are not sortable, so compareFn would be undefined. In that case just return a comparator that returns 0.
const compareFn = cols[dir.prop] ?? (() => 0);
return {
prop,
dir: dir.dir,
valueGetter: getterForProp(prop),
compareFn
};
});
return temp.sort((rowA, rowB) => {
for (const cachedDir of cachedDirs) {
// Get property and valuegetters for column to be sorted
const { prop, valueGetter } = cachedDir;
// Get A and B cell values from rows based on properties of the columns
const propA = valueGetter(rowA, prop);
const propB = valueGetter(rowB, prop);
// Compare function gets five parameters:
// Two cell values to be compared as propA and propB
// Two rows corresponding to the cells as rowA and rowB
// Direction of the sort for this column as SortDirection
// Compare can be a standard JS comparison function (a,b) => -1|0|1
// as additional parameters are silently ignored. The whole row and sort
// direction enable more complex sort logic.
const comparison = cachedDir.dir !== 'desc'
? cachedDir.compareFn(propA, propB, rowA, rowB)
: -cachedDir.compareFn(propA, propB, rowA, rowB);
// Don't return 0 yet in case of needing to sort by next property
if (comparison !== 0) {
return comparison;
}
}
return 0;
});
};
const sortGroupedRows = (groupedRows, columns, dirs, sortOnGroupHeader) => {
if (sortOnGroupHeader) {
groupedRows = sortRows(groupedRows, columns, dirs, sortOnGroupHeader);
}
return groupedRows.map(group => ({ ...group, value: sortRows(group.value, columns, dirs) }));
};
const toInternalColumn = (columns, defaultColumnWidth = 150) => {
let hasTreeColumn = false;
// TS fails to infer the type here.
return columns.map(column => {
const prop = column.prop ?? (column.name ? camelCase(column.name) : undefined);
// Only one column should hold the tree view,
// Thus if multiple columns are provided with
// isTreeColumn as true, we take only the first one
const isTreeColumn = !!column.isTreeColumn && !hasTreeColumn;
hasTreeColumn = hasTreeColumn || isTreeColumn;
// TODO: add check if prop or name is provided if sorting is enabled.
return {
...column,
$$id: id(),
$$originalColumn: column,
$$valueGetter: getterForProp(prop),
prop,
name: column.name ?? (prop ? deCamelCase(String(prop)) : ''),
resizeable: column.resizeable ?? true,
sortable: column.sortable ?? true,
comparator: column.comparator ?? orderByComparator,
draggable: column.draggable ?? true,
canAutoResize: column.canAutoResize ?? true,
width: signal(column.width ?? defaultColumnWidth),
isTreeColumn,
// in case of the directive, those are getters, so call them explicitly.
headerTemplate: column.headerTemplate,
cellTemplate: column.cellTemplate,
summaryTemplate: column.summaryTemplate,
ghostCellTemplate: column.ghostCellTemplate,
treeToggleTemplate: column.treeToggleTemplate
}; // TS cannot cast here
});
};
const toPublicColumn = (column) => {
return {
...column.$$originalColumn,
checkboxable: column.checkboxable,
frozenLeft: column.frozenLeft,
frozenRight: column.frozenRight,
flexGrow: column.flexGrow,
minWidth: column.minWidth,
maxWidth: column.maxWidth,
width: column.width(),
resizeable: column.resizeable,
comparator: column.comparator,
pipe: column.pipe,
sortable: column.sortable,
draggable: column.draggable,
canAutoResize: column.canAutoResize,
name: column.name,
prop: column.prop,
bindAsUnsafeHtml: column.bindAsUnsafeHtml,
cellTemplate: column.cellTemplate,
ghostCellTemplate: column.ghostCellTemplate,
headerTemplate: column.headerTemplate,
treeToggleTemplate: column.treeToggleTemplate,
cellClass: column.cellClass,
headerClass: column.headerClass,
headerCheckboxable: column.headerCheckboxable,
isTreeColumn: column.isTreeColumn,
treeLevelIndent: column.treeLevelIndent,
summaryFunc: column.summaryFunc,
summaryTemplate: column.summaryTemplate
};
};
/**
* Calculates the Total Flex Grow
*/
const getTotalFlexGrow = (columns) => {
let totalFlexGrow = 0;
for (const c of columns) {
totalFlexGrow += c.flexGrow ?? 0;
}
return totalFlexGrow;
};
/**
* Adjusts the column widths.
* Inspired by: https://github.com/facebookarchive/fixed-data-table/blob/master/src/FixedDataTableWidthHelper.js
*/
const adjustColumnWidths = (allColumns, expectedWidth) => {
const columnsWidth = columnTotalWidth(allColumns);
const totalFlexGrow = getTotalFl