@siemens/ngx-datatable
Version:
ngx-datatable is an Angular table grid component for presenting large and complex data.
5,832 lines • 330 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, viewChildren, DestroyRef, model, viewChild, untracked, Input, IterableDiffers, contentChildren, afterNextRender, ContentChild, NgModule } from '@angular/core';
import { NgTemplateOutlet } from '@angular/common';
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 = getTotalFlexGrow(allColumns);
const colsByGroup = columnsByPin(allColumns);
if (columnsWidth !== expectedWidth) {
scaleColumns(colsByGroup, expectedWidth, totalFlexGrow);
}
};
/**
* Resizes columns based on the flexGrow property, while respecting manually set widths
*/
const scaleColumns = (colsByGroup, maxWidth, totalFlexGrow) => {
const columns = Object.values(colsByGroup).flat();
let remainingWidth = maxWidth;
// calculate total width and flexgrow points for columns that can be resized
for (const column of columns) {
if (column.$$oldWidth) {
// when manually resized, switch off auto-resize
column.canAutoResize = false;
}
if (!column.canAutoResize) {
remainingWidth -= column.width();
totalFlexGrow -= column.flexGrow ?? 0;
}
else {
column.width.set(0);
}
}
const hasMinWidth = {};
// resize columns until no width is left to be distributed
do {
const widthPerFlexPoint = remainingWidth / totalFlexGrow;
remainingWidth = 0;
for (const column of columns) {
// if the column can be resize and it hasn't reached its minimum width yet
if (column.canAutoResize && !hasMinWidth[column.prop]) {
const newWidth = column.width() + (column.flexGrow ?? 0) * widthPerFlexPoint;
if (column.minWidth !== undefined && newWidth < column.minWidth) {
remainingWidth += newWidth - column.minWidth;
column.width.set(column.minWidth);
hasMinWidth[column.prop] = true;
}
else {
column.width.set(newWidth);
}
}
}
} while (remainingWidth !== 0);
// Adjust for any remaining offset in computed widths vs maxWidth
const totalWidthAchieved = columns.reduce((acc, col) => acc + col.width(), 0);
const delta = maxWidth - totalWidthAchieved;
if (delta === 0) {
return;
}
// adjust the first column that can be auto-resized respecting the min/max widths
for (const col of columns.filter(c => c.canAutoResize).sort((a, b) => a.width() - b.width())) {
if ((delta > 0 && (!col.maxWidth || col.width() + delta <= col.maxWidth)) ||
(delta < 0 && (!col.minWidth || col.width() + delta >= col.minWidth))) {
col.width.update(value => value + delta);
break;
}
}
};
/**
* Forces the width of the columns to
* distribute equally but overflowing when necessary
*
* Rules:
*
* - If combined withs are less than the total width of the grid,
* proportion the widths given the min / max / normal widths to fill the width.
*
* - If the combined widths, exceed the total width of the grid,
* use the standard widths.
*
* - If a column is resized, it should always use that width
*
* - The proportional widths should never fall below min size if specified.
*
* - If the grid starts off small but then becomes greater than the size ( + / - )
* the width should use the original width; not the newly proportioned widths.
*/
const forceFillColumnWidths = (allColumns, expectedWidth, startIdx, allowBleed, defaultColWidth = 150, verticalScrollWidth = 0) => {
const columnsToResize = allColumns
.slice(startIdx + 1, allColumns.length)
.filter(c => c.canAutoResize !== false);
let additionWidthPerColumn;
let exceedsWindow;
let contentWidth = getContentWidth(allColumns, defaultColWidth);
let remainingWidth = expectedWidth - contentWidth;
const initialRemainingWidth = remainingWidth;
const columnsProcessed = [];
const remainingWidthLimit = 1; // when to stop
// This loop takes care of the
do {
additionWidthPerColumn = remainingWidth / columnsToResize.length;
exceedsWindow = contentWidth >= expectedWidth;
for (const column of columnsToResize) {
// don't bleed if the initialRemainingWidth is same as verticalScrollWidth
if (exceedsWindow && allowBleed && initialRemainingWidth !== -1 * verticalScrollWidth) {
column.width.update(value => value || defaultColWidth);
}
else {
const newSize = (column.width() || defaultColWidth) + additionWidthPerColumn;
if (column.minWidth && newSize < column.minWidth) {
column.width.set(column.minWidth);
columnsProcessed.push(column);
}
else if (column.maxWidth && newSize > column.maxWidth) {
column.width.set(column.maxWidth);
columnsProcessed.push(column);
}
else {
column.width.set(newSize);
}
}
column.width.update(value => Math.max(0, value));
}
contentWidth = getContentWidth(allColumns, defaultColWidth);
remainingWidth = expectedWidth - contentWidth;
removeProcessedColumns(columnsToResize, columnsProcessed);
} while (remainingWidth > remainingWidthLimit && columnsToResize.length !== 0);
};
/**
* Remove the processed columns from the current active columns.
*/
const removeProcessedColumns = (columnsToResize, columnsProcessed) => {
for (const column of columnsProcessed) {
const index = columnsToResize.indexOf(column);
columnsToResize.splice(index, 1);
}
};
/**
* Gets the width of the columns
*/
const getContentWidth = (allColumns, defaultColWidth = 150) => {
let contentWidth = 0;
for (const column of allColumns) {
contentWidth += column.width() || defaultColWidth;
}
return contentWidth;
};
/**
* Same as {@link numberAttribute} but returns `undefined` if the value is `undefined`.
* {@link numberAttribute} would return `NaN` in that case.
* @param value
*/
// Must be a function.
// eslint-disable-next-line prefer-arrow/prefer-arrow-functions
function numberOrUndefinedAttribute(value) {
if (value === undefined) {
return undefined;
}
return numberAttribute(value);
}
/**
* This token is created to break cycling import error which occurs when we import
* DatatableComponent in DataTableRowWrapperComponent.
*/
const DATATABLE_COMPONENT_TOKEN = new InjectionToken('DatatableComponentToken');
const optionalGetterForProp = (prop) => {
return prop ? row => getterForProp(prop)(row, prop) : undefined;
};
/**
* 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: Cyclic relationships are broken by treating one node in the cycle as a root
*
* 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
*
*/
const groupRowsByParents = (rows, from, to) => {
if (from && to) {
const treeRows = rows.filter(row => !!row).map(row => new TreeNode(row));
const uniqIDs = new Map(treeRows.map(node => [to(node.row), node]));
const resolved = new Set();
const resolveLevel = (node, visited) => {
if (resolved.has(node)) {
return node.row.level;
}
if (visited.has(node)) {
return Infinity;
}
visited.add(node);
const parent = uniqIDs.get(from(node.row));
if (parent && parent !== node) {
const parentLevel = resolveLevel(parent, visited);
if (parentLevel !== Infinity) {
node.row.level = parentLevel + 1;
}
else {
node.row.level = 0;
}
}
else {
node.row.level = 0;
}
visited.delete(node);
resolved.add(node);
return node.row.level;
};
for (const node of treeRows) {
if (!resolved.has(node)) {
resolveLevel(node, new Set());
}
}
for (const node of treeRows) {
const parent = uniqIDs.get(from(node.row));
if (parent && parent !== node && node.row.level > 0) {
node.parent = parent;
parent.children.push(node);
}
}
const rootNodes = treeRows.filter(n => !n.parent);
return rootNodes.flatMap(child => child.flatten());
}
else {
return rows;
}
};
class TreeNode {
row;
parent;
children;
constructor(row) {
this.row = row;
this.children = [];
}
flatten() {
if (this.row.treeStatus === 'expanded') {
return [this.row, ...this.children.flatMap(child => child.flatten())];
}
else {
return [this.row];
}
}
}
const expandToRow = (targetRow, rows, from, to) => {
if (from && to) {
const uniqIDs = new Map(rows.filter(row => !!row).map(node => [to(node), node]));
const visitedRowIds = new Set();
let currentRow = targetRow;
while (currentRow) {
const currentRowId = to(currentRow);
if (visitedRowIds.has(currentRowId)) {
// cycle detected, abort to avoid an infinite loop
break;
}
visitedRowIds.add(currentRowId);
if (currentRow.treeStatus === 'collapsed') {
currentRow.treeStatus = 'expanded';
}
currentRow = uniqIDs.get(from(currentRow));
}
}
};
const ARROW_UP = 'ArrowUp';
const ARROW_DOWN = 'ArrowDown';
const ENTER = 'Enter';
const ESCAPE = 'Escape';
const ARROW_LEFT = 'ArrowLeft';
const ARROW_RIGHT = 'ArrowRight';
/**
* 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 {
/**
* 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.
*/
treeArray = [];
/**
* Clear the Tree array.
*/
clearCache() {
this.treeArray = [];
}
/**
* Initialize the Fenwick tree with row Heights.
*/
initCache(details) {
const { rows, rowHeight, detailRowHeight, externalVirtual, indexOffset, rowCount, rowExpansions } = details;
const isFn = typeof rowHeight === 'function';
const isDetailFn = typeof detailRowHeight === 'function';
if (rowHeight === 'auto' || (!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 = isFn ? rowHeight(row) : rowHeight;
// 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) {
currentRowHeight += isDetailFn ? detailRowHeight(row, indexOffset + i) : 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;
}
}
const selectRows = (selected, row, comparefn) => {
const selectedIndex = comparefn(row, selected);
if (selectedIndex > -1) {
selected.splice(selectedIndex, 1);
}
else {
selected.push(row);
}
return selected;
};
const selectRowsBetween = (rows, index, prevIndex) => {
const reverse = index < prevIndex;
const rangeRows = [];
for (let i = 0; i < rows.length; i++) {
const row = rows[i];
const greater = i >= prevIndex && i <= index;
const lesser = i <= prevIndex && i >= index;
let range;
if (reverse) {
range = {
start: index,
end: prevIndex
};
}
else {
range = {
start: prevIndex,
end: index + 1
};
}
if ((reverse && lesser) || (!reverse && greater)) {
if (i >= range.start && i <= range.end && row) {
rangeRows.push(row);
}
}
}
return rangeRows;
};
/** @internal */
class DatatableConfiguration {
datatable;
globalConfiguration;
configuration = computed(() => {
const configuration = this.globalConfiguration;
return {
...configuration,
defaultColumnWidth: configuration.defaultColumnWidth ?? 150,
rowHeight: this.datatable.rowHeight(),
headerHeight: this.datatable.headerHeight(),
footerHeight: this.datatable.footerHeight() ?? 0,
cssClasses: {
sortAscending: 'datatable-icon-up',
sortDescending: 'datatable-icon-down',
sortUnset: 'datatable-icon-sort-unset',
pagerLeftArrow: 'datatable-icon-left',
pagerRightArrow: 'datatable-icon-right',
pagerPrevious: 'datatable-icon-prev',
pagerNext: 'datatable-icon-skip',
treeStatusLoading: 'icon datatable-icon-collapse',
treeStatusExpanded: 'icon datatable-icon-down',
treeStatusCollapsed: 'icon datatable-icon-up',
...configuration.cssClasses,
...this.datatable.cssClasses()
},
messages: {
emptyMessage: 'No data to display',
totalMessage: 'total',
selectedMessage: 'selected',
ariaFirstPageMessage: 'go to first page',
ariaPageNMessage: 'page',
ariaPreviousPageMessage: 'go to previous page',
ariaNextPageMessage: 'go to next page',
ariaLastPageMessage: 'go to last page',
ariaRowCheckboxMessage: 'Select row',
ariaHeaderCheckboxMessage: 'Select all rows',
ariaGroupHeaderCheckboxMessage: 'Select row group',
ariaLoadingMessage: 'Loading',
...configuration.messages,
...this.datatable.messages()
}
};
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "configuration" }] : /* istanbul ignore next */ []));
constructor(datatable, globalConfiguration) {
this.datatable = datatable;
this.globalConfiguration = globalConfiguration;
}
}
class DataTableGroupWrapperComponent {
configuration = inject(DatatableConfiguration).configuration;
groupHeader = input.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "groupHeader" }] : /* istanbul ignore next */ []));
groupHeaderRowHeight = input.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "groupHeaderRowHeight" }] : /* istanbul ignore next */ []));
group = input.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "group" }] : /* istanbul ignore next */ []));
groupedRows = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "groupedRows" }] : /* istanbul ignore next */ []));
allColumnsColspan = input.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "allColumnsColspan" }] : /* istanbul ignore next */ []));
selected = input.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "selected" }] : /* istanbul ignore next */ []));
disabled = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "disabled" }] : /* istanbul ignore next */ []));
rowIndex = input.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "rowIndex" }] : /* istanbul ignore next */ []));
expanded = input(false, { ...(ngDevMode ? { debugName: "expanded" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
groupSelectedChange = output();
context = computed(() => {
return {
group: this.group(),
expanded: this.expanded(),
rowIndex: this.rowIndex()
};
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "context" }] : /* istanbul ignore next */ []));
selectedRowsOfGroup = computed(() => {
const groupValue = this.group().value;
return this.selected().filter(row => groupValue.includes(row));
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "selectedRowsOfGroup" }] : /* istanbul ignore next */ []));
checked = computed(() => this.selectedRowsOfGroup().length === this.group().value.length, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "checked" }] : /* istanbul ignore next */ []));
indeterminate = computed(() => this.selectedRowsOfGroup().length > 0 && !this.checked(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "indeterminate" }] : /* istanbul ignore next */ []));
onCheckboxChange(groupSelected) {
this.groupSelectedChange.emit(groupSelected);
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DataTableGroupWrapperComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: DataTableGroupWrapperComponent, isStandalone: true, selector: "datatable-group-wrapper", inputs: { groupHeader: { classPropertyName: "groupHeader", publicName: "groupHeader", isSignal: true, isRequired: true, transformFunction: null }, groupHeaderRowHeight: { classPropertyName: "groupHeaderRowHeight", publicName: "groupHeaderRowHeight", isSignal: true, isRequired: true, transformFunction: null }, group: { classPropertyName: "group", publicName: "group", isSignal: true, isRequired: true, transformFunction: null }, groupedRows: { classPropertyName: "groupedRows", publicName: "groupedRows", isSignal: true, isRequired: false, transformFunction: null }, allColumnsColspan: { classPropertyName: "allColumnsColspan", publicName: "allColumnsColspan", isSignal: true, isRequired: true, transformFunction: null }, selected: { classPropertyName: "selected", publicName: "selected", isSignal: true, isRequired: true, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, rowIndex: { classPropertyName: "rowIndex", publicName: "rowIndex", isSignal: true, isRequired: true, transformFunction: null }, expanded: { classPropertyName: "expanded", publicName: "expanded", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { groupSelectedChange: "groupSelectedChange" }, host: { attributes: { "role": "presentation" }, classAttribute: "datatable-group-wrapper" }, ngImport: i0, template: `
@let groupHeader = this.groupHeader();
@if (groupHeader && groupHeader.template()) {
<div role="row" class="datatable-group-header" [style.height.px]="groupHeaderRowHeight()">
<div role="cell" class="datatable-group-cell" [attr.aria-colspan]="allColumnsColspan()">
@if (groupHeader.checkboxable()) {
<div>
<label class="datatable-checkbox">
<input
#select
type="checkbox"
[attr.aria-label]="configuration().messages.ariaGroupHeaderCheckboxMessage"
[checked]="checked()"
[indeterminate]="indeterminate()"
(change)="onCheckboxChange(select.checked)"
/>
</label>
</div>
}
<ng-template
[ngTemplateOutlet]="groupHeader.template()"
[ngTemplateOutletContext]="context()"
/>
</div>
</div>
}
@if (expanded()) {
<ng-content />
}
`, isInline: true, styles: [":host{display:grid;grid-template-columns:subgrid;grid-column:1/-1}.datatable-group-header{grid-column:1/-1;inset-inline-start:0;position:sticky}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DataTableGroupWrapperComponent, decorators: [{
type: Component,
args: [{ selector: 'datatable-group-wrapper', imports: [NgTemplateOutlet], template: `
@let groupHeader = this.groupHeader();
@if (groupHeader && groupHeader.template()) {
<div role="row" class="datatable-group-header" [style.height.px]="groupHeaderRowHeight()">
<div role="cell" class="datatable-group-cell" [attr.aria-colspan]="allColumnsColspan()">
@if (groupHeader.checkboxable()) {
<div>
<label class="datatable-checkbox">
<input
#select
type="checkbox"
[attr.aria-label]="configuration().messages.ariaGroupHeaderCheckboxMessage"
[checked]="checked()"
[indeterminate]="indeterminate()"
(change)="onCheckboxChange(select.checked)"
/>
</label>
</div>
}
<ng-template
[ngTemplateOutlet]="groupHeader.template()"
[ngTemplateOutletContext]="context()"
/>
</div>
</div>
}
@if (expanded()) {
<ng-content />
}
`, changeDetection: ChangeDetectionStrategy.OnPush, host: {
class: 'datatable-group-wrapper',
role: 'presentation'
}, styles: [":host{display:grid;grid-template-columns:subgrid;grid-column:1/-1}.datatable-group-header{grid-column:1/-1;inset-inline-start:0;position:sticky}\n"] }]
}], propDecorators: { groupHeader: [{ type: i0.Input, args: [{ isSignal: true, alias: "groupHeader", required: true }] }], groupHeaderRowHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "groupHeaderRowHeight", required: true }] }], group: [{ type: i0.Input, args: [{ isSignal: true, alias: "group", required: true }] }], groupedRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "groupedRows", required: false }] }], allColumnsColspan: [{ type: i0.Input, args: [{ isSignal: true, alias: "allColumnsColspan", required: true }] }], selected: [{ type: i0.Input, args: [{ isSignal: true, alias: "selected", required: true }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], rowIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowIndex", required: true }] }], expanded: [{ type: i0.Input, args: [{ isSignal: true, alias: "expanded", required: false }] }], groupSelectedChange: [{ type: i0.Output, args: ["groupSelectedChange"] }] } });
class DataTableRowWrapperComponent {
rowDiffer = inject(KeyValueDiffers).find({}).create();
elementRef = inject(ElementRef);
rowDetail = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "rowDetail" }] : /* istanbul ignore next */ []));
detailRowHeightFn = input.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "detailRowHeightFn" }] : /* istanbul ignore next */ []));
row = input.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "row" }] : /* istanbul ignore next */ []));
disabled = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "disabled" }] : /* istanbul ignore next */ []));
rowContextmenu = output();
rowIndex = input.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "rowIndex" }] : /* istanbul ignore next */ []));
expanded = input(false, { ...(ngDevMode ? { debugName: "expanded" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
checkRowPropertyChanges = input(true, { ...(ngDevMode ? { debugName: "checkRowPropertyChanges" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
detailsRowHeight = computed(() => this.detailRowHeightFn()(this.row(), this.rowIndex()), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "detailsRowHeight" }] : /* istanbul ignore next */ []));
context = computed(() => {
this.rowDiffedCount(); // This allows us to get re-evaluated when the row was mutated internally.
return {
row: this.row(),
expanded: this.expanded(),
rowIndex: this.rowIndex(),
disabled: this.disabled()
};
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "context" }] : /* istanbul ignore next */ []));
// This counter will be incremented every time the row object is mutated.
rowDiffedCount = signal(0, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "rowDiffedCount" }] : /* istanbul ignore next */ []));
ngDoCheck() {
if (!this.checkRowPropertyChanges()) {
return;
}
const row = this.row();
if (this.rowDiffer.diff(row)) {
this.rowDiffedCount.update(count => count + 1);
}
}
scrollIntoView(options) {
this.elementRef.nativeElement.scrollIntoView({
behavior: options?.behavior,
block: options?.block ?? 'start'
});
}
onContextmenu($event) {
this.rowContextmenu.emit({ event: $event, row: this.row() });
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DataTableRowWrapperComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: DataTableRowWrapperComponent, isStandalone: true, selector: "datatable-row-wrapper", inputs: { rowDetail: { classPropertyName: "rowDetail", publicName: "rowDetail", isSignal: true, isRequired: false, transformFunction: null }, detailRowHeightFn: { classPropertyName: "detailRowHeightFn", publicName: "detailRowHeightFn", isSignal: true, isRequired: true, transformFunction: null }, row: { classPropertyName: "row", publicName: "row", isSignal: true, isRequired: true, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, rowIndex: { classPropertyName: "rowIndex", publicName: "rowIndex", isSignal: true, isRequired: true, transformFunction: null }, expanded: { classPropertyName: "expanded", publicName: "expanded", isSignal: true, isRequired: false, transformFunction: null }, checkRowPropertyChanges: { classPropertyName: "checkRowPropertyChanges", publicName: "checkRowPropertyChanges", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { rowContextmenu: "rowContextmenu" }, host: { listeners: { "contextmenu": "onContextmenu($event)" }, classAttribute: "datatable-row-wrapper" }, ngImport: i0, template: `
<ng-content />
@let rowDetailTemplate = rowDetail()?.template();
@if (rowDetailTemplate && expanded()) {
<div class="datatable-row-detail" [style.height.px]="detailsRowHeight()">
<ng-template [ngTemplateOutlet]="rowDetailTemplate" [ngTemplateOutletContext]="context()" />
</div>
}
`, isInline: true, styles: [":host{display:grid;grid-template-columns:subgrid;grid-column:1/-1}.datatable-row-detail{grid-column:1/-1;overflow-y:hidden}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DataTableRowWrapperComponent, decorators: [{
type: Component,
args: [{ selector: 'datatable-row-wrapper', imports: [NgTemplateOutlet], template: `
<ng-content />
@let rowDetailTemplate = rowDetail()?.template();
@if (rowDetailTemplate && expanded()) {
<div class="datatable-row-detail" [style.height.px]="detailsRowHeight()">
<ng-template [ngTemplateOutlet]="rowDetailTemplate" [ngTemplateOutletContext]="context()" />
</div>
}
`, changeDetection: ChangeDetectionStrategy.OnPush, host: {
class: 'datatable-row-wrapper'
}, styles: [":host{display:grid;grid-template-columns:subgrid;grid-column:1/-1}.datatable-row-detail{grid-column:1/-1;overflow-y:hidden}\n"] }]
}], propDecorators: { rowDetail: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowDetail", required: false }] }], detailRowHeightFn: [{ type: i0.Input, args: [{ isSignal: true, alias: "detailRowHeightFn", required: true }] }], row: [{ type: i0.Input, args: [{ isSignal: true, alias: "row", required: true }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], rowContextmenu: [{ type: i0.Output, args: ["rowContextmenu"] }], rowIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowIndex", required: true }] }], expanded: [{ type: i0.Input, args: [{ isSignal: true, alias: "expanded", required: false }] }], checkRowPropertyChanges: [{ type: i0.Input, args: [{ isSignal: true, alias: "checkRowPropertyChanges", required: false }] }], onContextmenu: [{
type: HostListener,
args: ['contextmenu', ['$event']]
}] } });
class DataTableBodyCellComponent {
_element = inject(ElementRef).nativeElement;
configuration = inject(DatatableConfiguration).configuration;
displayCheck = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "displayCheck" }] : /* istanbul ignore next */ []));
disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
group = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "group" }] : /* istanbul ignore next */ []));
rowHeight = input(0, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "rowHeight" }] : /* istanbul ignore next */ []));
isSelected = input(false, { ...(ngDevMode ? { debugName: "isSelected" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
rowIndex = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "rowIndex" }] : /* istanbul ignore next */ []));
column = input.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "column" }] : /* istanbul ignore next */ []));
row = input.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "row" }] : /* istanbul ignore next */ []));
treeStatus = input('collapsed', /* @ts-ignore */
...(ngDevMode ? [{ debugName: "treeStatus" }] : /* istanbul ignore next */ []));
expanded = input(false, { ...(ngDevMode ? { debugName: "expanded" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
activate = output();
treeAction = output();
publicColumn = computed(() => toPublicColumn(this.column()), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "publicColumn" }] : /* istanbul ignore next */ []));
columnCssClasses = computed(() => {
const column = this.column();
if (!column.cellClass) {
return [];
}
if (typeof column.cellClass === 'string') {
return column.cellClass;
}
return column.cellClass({
row: this.row(),
group: this.group(),
column: this.publicColumn(),
value: this.value(),
rowHeight: this.rowHeight()
});
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "columnCssClasses" }] : /* istanbul ignore next */ []));
sanitizedValue = computed(() => {
const value = this.value();
return value !== null && value !== undefined ? this.stripHtml(value) : value;
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "sanitizedValue" }] : /* istanbul ignore next */ []));
value = linkedSignal(() => this.getComputedValue(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
cellContext = computed(() => {
return {
onCheckboxChangeFn: (event) => this.onCheckboxChange(event),
activateFn: (event) => this.activate.emit(event),
row: this.row(),
group: this.group(),
value: this.value(),
column: this.publicColumn(),
rowHeight: this.rowHeight(),
isSelected: this.isSelected(),
rowIndex: this.rowIndex()?.index ?? 0,
rowInGroupIndex: this.rowIndex()?.indexInGroup,
treeStatus: this.treeStatus() ?? 'collapsed',
disabled: this.disabled(),
expanded: this.expanded(),
onTreeAction: () => this.onTreeAction()
};
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "cellContext" }] : /* istanbul ignore next */ []));
isFocused = signal(false, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "isFocused" }] : /* istanbul ignore next */ []));
ngDoCheck() {
const value = this.getComputedValue();
if (value !== this.value()) {
this.value.set(value);
}
}
focus() {
this._element.focus({ preventScroll: true });
}
getComputedValue() {
let value = '';
const column = this.column();
const row = this.row();
if (!row || column.prop == undefined) {
value = '';
}
else {
const val = column.$$valueGetter(row, column.prop);
const userPipe = column.pipe;
if (userPipe) {
value = userPipe.transform(val);
}
else if (value !== undefined) {
value = val;
}
}
return value;
}
onFocus() {
this.isFocused.set(true);
}
onBlur() {
this.isFocused.set(false);
}
onClick(event) {
this.activate.emit({
type: 'click',
event,
row: this.row(),
group: this.group(),
rowHeight: this.rowHeight(),
column: this.publicColumn(),
value: this.value(),
cellElement: this._element
});
}
onDblClick(event) {
this.activate.emit({
type: 'dblclick',
event,
row: this.row(),
group: this.group(),
rowHeight: this.rowHeight(),
column: this.publicColumn(),
value: this.value(),
cellElement: this._element
});
}
onKeyDown(event) {
const key = event.key;
const isTargetCell = event.target === this._element;
const isAction = key === ENTER ||
key === ARROW_DOWN ||
key === ARROW_UP ||
key === ARROW_LEFT ||
key === ARROW_RIGHT;
if (isAction && isTargetCell) {
event.preventDefault();
event.stopPropagation();
this.activate.emit({
type: 'keydown',
event,
row: this.row(),
group: this.group(),
rowHeight: this.rowHeight(),
column: this.publicColumn(),
value: this.value(),
cellElement: this._element
});
}
}
onCheckboxChange(event) {
this.activate.emit({
type: 'checkbox',
event,
row: this.row(),
group: this.group(),
rowHeight: this.rowHeight(),
column: this.publicColumn(),
value: this.value(),
cellElement: this._element,
treeStatus: 'collapsed'
});
}
stripHtml(html) {
if (!html.replace) {
return html;
}
return html.replace(/<\/?[^>]+(>|$)/g, '');
}
onTreeAction() {
this.treeAction.emit(this.row());
}
calcLeftMargin(column, row) {
const levelIndent = column.treeLevelIndent ?? 50;
return column.isTreeColumn ? row.level * levelIndent : 0;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DataTableBodyCellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: DataTableBodyCellComponent, isStandalone: true, selector: "datatable-body-cell", inputs: { displayCheck: { classPropertyName: "displayCheck", publicName: "displayCheck", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, group: { classPropertyName: "group", publicName: "group", isSignal: true, isRequired: false, transformFunction: null }, rowHeight: { classPropertyName: "rowHeight", publicName: "rowHeight", isSignal: true, isRequired: false, transformFunction: null }, isSelected: { classPropertyName: "isSelected", publicName: "isSelected", isSignal: true, isRequired: false, transformFunction: null }, rowIndex: { classPropertyName: "rowIndex", publicName: "rowIndex", isSignal: true, isRequired: false, transformFunction: null }, column: { classPropertyName: "column", publicName: "column", isSignal: true, isRequired: true, transformFunction: null }, row: { classPropertyName: "row", publicName: "row", isSignal: true, isRequired: true, transformFunction: null }, treeStatus: { classPropertyName: "treeStatus", publicName: "treeStatus", isSignal: true, isRequired: false, transformFunction: null }, expanded: { classPropertyName: "expanded", publicName: "expanded", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { activate: "activate", treeAction: "treeAction" }, host: { listeners: { "focus": "onFocus()", "blur": "onBlur()", "click": "onClick($event)", "dblclick": "onDblClick($event)", "keydown": "onKeyDown($event)" }, properties: { "class": "columnCssClasses()", "class.active": "isFocused() && !disabled()", "class.row-disabled": "disabled()" }, classAttribute: "datatable-body-cell" }, ngImport: i0, template: `
@let column = this.column();
@let row = this.row();
<div class="datatable-body-cell-label" [style.margin-left.px]="calcLeftMargin(column, row)">
@let displayCheck = this.displayCheck();
@if (column.checkboxable && (!displayCheck || displayCheck(row, publicColumn(), value()))) {
<label class="datatable-checkbox">
<input
type="checkbox"
[attr.aria-label]="configuration().messages.ariaRowCheckboxMessage"
[disabled]="disabled()"
[checked]="isSelected()"
(click)="onCheckboxChange($event)"
/>
</label>
}
@if (column.isTreeColumn) {
@if (!column.treeToggleTemplate) {
@let treeStatus = this.treeStatus() ?? 'collapsed';
<button
class="datatable-tree-button"
type="button"
[disabled]="treeStatus === 'disabled'"
[attr.aria-label]="treeStatus"
(click)="onTreeAction()"
>
<span>
@if (treeStatus === 'loading') {
<i [class]="configuration().cssClasses.treeStatusLoading"></i>
}
@if (treeStatus === 'collapsed') {
<i [class]="configuration().cssClasses.treeStatusCollapsed"></i>
}
@if (treeStatus === 'expanded' || treeStatus === 'disabled') {
<i [class]="configuration().cssClasses.treeStatusExpanded"></i>
}
</span>
</button>
} @else {
<ng-template
[ngTemplateOutlet]="column.treeToggleTemplate"
[ngTemplateOutletContext]="{ cellContext: cellContext() }"
/>
}
}
@if (!column.cellTemplate) {
@if (column.bindAsUnsafeHtml) {
<span [title]="sanitizedValue()" [innerHTML]="value()"> </span>
} @else {
<span [title]="sanitizedValue()">{{ value() }}</span>
}
} @else {
<ng-template
[ngTemplateOutlet]="column.cellTemplate"
[ngTemplateOutletContext]="cellContext()"
/>
}
</div>
`, isInline: true, styles: [":host{overflow-x:hidden;min-inline-size:0;vertical-align:top;display:inline-block;line-height:1.625}:host:focus{outline:none}:host-context(ngx-datatable.fixed-row) :host{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DataTableBodyCellComponent, decorators: [{
type: Component,
args: [{ selector: 'datatable-body-cell', imports: [NgTemplateOutlet], template: `
@let column = this.column();
@let row = this.row();
<div class="datatable-body-cell-label" [style.margin-left.px]="calcLeftMargin(column, row)">
@let displayCheck = this.displayCheck();
@if (column.checkboxable && (!displayCheck || displayCheck(row, publicColumn(), value()))) {
<label class="datatable-checkbox">
<input
type="checkbox"
[attr.aria-label]="configuration().messages.ariaRowCheckboxMessage"
[disabled]="disabled()"
[checked]="isSelected()"
(click)="onCheckboxChange($event)"
/>
</label>
}
@if (column.isTreeColumn) {
@if (!column.treeToggleTemplate) {
@let treeStatus = this.treeStatus() ?? 'collapsed';
<button
class="datatable-tree-button"
type="button"
[disabled]="treeStatus === 'disabled'"
[attr.aria-label]="treeStatus"
(click)="onTreeAction()"
>
<span>
@if (treeStatus === 'loading') {
<i [class]="configuration().cssClasses.treeStatusLoading"></i>
}
@if (treeStatus === 'collapsed') {
<i [class]="configuration().cssClasses.treeStatusCollapsed"></i>
}
@if (treeStatus === 'expanded' || treeStatus === 'disabled') {
<i [class]="configuration().cssClasses.treeStatusExpanded"></i>
}
</span>
</button>
} @else {
<ng-template
[ngTemplateOutlet]="column.treeToggleTemplate"
[ngTemplateOutletContext]="{ cellContext: cellContext() }"
/>
}
}
@if (!column.cellTemplate) {
@if (column.bindAsUnsafeHtml) {
<span [title]="sanitizedValue()" [innerHTML]="value()"> </span>
} @else {
<span [title]="sanitizedValue()">{{ value() }}</span>
}
} @else {
<ng-template
[ngTemplateOutlet]="column.cellTemplate"
[ngTemplateOutletContext]="cellContext()"
/>
}
</div>
`, changeDetection: ChangeDetectionStrategy.OnPush, host: {
class: 'datatable-body-cell',
'[class]': 'columnCssClasses()',
'[class.active]': 'isFocused() && !disabled()',
'[class.row-disabled]': 'disabled()',
'(focus)': 'onFocus()',
'(blur)': 'onBlur()',
'(click)': 'onClick($event)',
'(dblclick)': 'onDblClick($event)',
'(keydown)': 'onKeyDown($event)'
}, styles: [":host{overflow-x:hidden;min-inline-size:0;vertical-align:top;display:inline-block;line-height:1.625}:host:focus{outline:none}:host-context(ngx-datatable.fixed-row) :host{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}\n"] }]
}], propDecorators: { displayCheck: [{ type: i0.Input, args: [{ isSignal: true, alias: "displayCheck", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], group: [{ type: i0.Input, args: [{ isSignal: true, alias: "group", required: false }] }], rowHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowHeight", required: false }] }], isSelected: [{ type: i0.Input, args: [{ isSignal: true, alias: "isSelected", required: false }] }], rowIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowIndex", required: false }] }], column: [{ type: i0.Input, args: [{ isSignal: true, alias: "column", required: true }] }], row: [{ type: i0.Input, args: [{ isSignal: true, alias: "row", required: true }] }], treeStatus: [{ type: i0.Input, args: [{ isSignal: true, alias: "treeStatus", required: false }] }], expanded: [{ type: i0.Input, args: [{ isSignal: true, alias: "expanded", required: false }] }], activate: [{ type: i0.Output, args: ["activate"] }], treeAction: [{ type: i0.Output, args: ["treeAction"] }] } });
class DataTableBodyRowComponent {
cd = inject(ChangeDetectorRef);
_element = inject(ElementRef).nativeElement;
_rowDiffer = inject(KeyValueDiffers)
.find({})
.create();
columns = input.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "columns" }] : /* istanbul ignore next */ []));
expanded = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "expanded" }] : /* istanbul ignore next */ []));
rowClass = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "rowClass" }] : /* istanbul ignore next */ []));
row = input.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "row" }] : /* istanbul ignore next */ []));
group = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "group" }] : /* istanbul ignore next */ []));
isSelected = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "isSelected" }] : /* istanbul ignore next */ []));
rowIndex = input.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "rowIndex" }] : /* istanbul ignore next */ []));
displayCheck = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "displayCheck" }] : /* istanbul ignore next */ []));
treeStatus = input('collapsed', /* @ts-ignore */
...(ngDevMode ? [{ debugName: "treeStatus" }] : /* istanbul ignore next */ []));
disabled = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "disabled" }] : /* istanbul ignore next */ []));
checkRowPropertyChanges = input(true, { ...(ngDevMode ? { debugName: "checkRowPropertyChanges" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
cssClass = computed(() => {
const rowClass = this.rowClass();
return rowClass ? rowClass(this.row()) : [];
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "cssClass" }] : /* istanbul ignore next */ []));
rowHeight = input.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "rowHeight" }] : /* istanbul ignore next */ []));
activate = output();
treeAction = output();
_columnGroupWidths = computed(() => {
const colsByPin = columnsByPin(this.columns());
return columnGroupWidths(colsByPin, this.columns());
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "_columnGroupWidths" }] : /* istanbul ignore next */ []));
_columnsByPin = computed(() => {
return columnsByPinArr(this.columns());
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "_columnsByPin" }] : /* istanbul ignore next */ []));
cells = viewChildren(DataTableBodyCellComponent, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "cells" }] : /* istanbul ignore next */ []));
ngDoCheck() {
if (!this.checkRowPropertyChanges()) {
return;
}
if (this._rowDiffer.diff(this.row())) {
this.cd.markForCheck();
}
}
onActivate(event, column) {
this.activate.emit({
...event,
rowElement: this._element,
cellIndex: this.columns().indexOf(column),
renderedCellIndex: this.cells().findIndex(cell => cell.column() === column)
});
}
focus() {
this._element.focus({ preventScroll: true });
}
focusCell(index) {
this.cells()[index]?.focus();
}
scrollIntoView() {
this._element.scrollIntoView({ block: 'nearest' });
}
onKeyDown(event) {
const key = event.key;
const isTargetRow = event.target === this._element;
const isAction = key === ENTER ||
key === ARROW_DOWN ||
key === ARROW_UP ||
key === ARROW_LEFT ||
key === ARROW_RIGHT;
const isCtrlA = event.key === 'a' && (event.ctrlKey || event.metaKey);
if ((isAction && isTargetRow) || isCtrlA) {
event.preventDefault();
event.stopPropagation();
this.activate.emit({
type: 'keydown',
event,
row: this.row(),
rowElement: this._element
});
}
}
onMouseenter(event) {
this.activate.emit({
type: 'mouseenter',
event,
row: this.row(),
rowElement: this._element
});
}
onTreeAction() {
this.treeAction.emit();
}
/** Returns the row index, or if in a group, the index within a group. */
innerRowIndex = computed(() => {
const rowIndex = this.rowIndex();
return rowIndex?.indexInGroup ?? rowIndex?.index ?? 0;
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "innerRowIndex" }] : /* istanbul ignore next */ []));
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DataTableBodyRowComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: DataTableBodyRowComponent, isStandalone: true, selector: "datatable-body-row", inputs: { columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: true, transformFunction: null }, expanded: { classPropertyName: "expanded", publicName: "expanded", isSignal: true, isRequired: false, transformFunction: null }, rowClass: { classPropertyName: "rowClass", publicName: "rowClass", isSignal: true, isRequired: false, transformFunction: null }, row: { classPropertyName: "row", publicName: "row", isSignal: true, isRequired: true, transformFunction: null }, group: { classPropertyName: "group", publicName: "group", isSignal: true, isRequired: false, transformFunction: null }, isSelected: { classPropertyName: "isSelected", publicName: "isSelected", isSignal: true, isRequired: false, transformFunction: null }, rowIndex: { classPropertyName: "rowIndex", publicName: "rowIndex", isSignal: true, isRequired: true, transformFunction: null }, displayCheck: { classPropertyName: "displayCheck", publicName: "displayCheck", isSignal: true, isRequired: false, transformFunction: null }, treeStatus: { classPropertyName: "treeStatus", publicName: "treeStatus", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, checkRowPropertyChanges: { classPropertyName: "checkRowPropertyChanges", publicName: "checkRowPropertyChanges", isSignal: true, isRequired: false, transformFunction: null }, rowHeight: { classPropertyName: "rowHeight", publicName: "rowHeight", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { activate: "activate", treeAction: "treeAction" }, host: { attributes: { "role": "row", "tabindex": "-1" }, listeners: { "keydown": "onKeyDown($event)", "mouseenter": "onMouseenter($event)" }, properties: { "class": "cssClass()", "class.active": "isSelected()", "class.datatable-row-odd": "innerRowIndex() % 2 !== 0", "class.datatable-row-even": "innerRowIndex() % 2 === 0", "class.row-disabled": "disabled()", "style.height.px": "rowHeight()" }, classAttribute: "datatable-body-row" }, viewQueries: [{ propertyName: "cells", predicate: DataTableBodyCellComponent, descendants: true, isSignal: true }], ngImport: i0, template: `
@for (colGroup of _columnsByPin(); track colGroup.type) {
@if (colGroup.columns.length) {
<div
class="datatable-row-group"
[class]="'datatable-row-' + colGroup.type"
[style.grid-column]="'span ' + colGroup.columns.length"
[class.row-disabled]="disabled()"
>
@for (column of colGroup.columns; track column.$$id) {
<datatable-body-cell
role="cell"
tabindex="-1"
[row]="row()"
[group]="group()"
[expanded]="expanded()"
[isSelected]="isSelected()"
[rowIndex]="rowIndex()"
[column]="column"
[rowHeight]="rowHeight()"
[displayCheck]="displayCheck()"
[disabled]="disabled()"
[treeStatus]="treeStatus()"
(activate)="onActivate($event, column)"
(treeAction)="onTreeAction()"
/>
}
</div>
}
}
`, isInline: true, styles: [":host{display:grid;grid-template-columns:subgrid;grid-column:1/-1;grid-template-rows:minmax(0,1fr);outline:none}:host-context(ngx-datatable.fixed-row) :host{white-space:nowrap}.datatable-row-group{display:grid;grid-template-columns:subgrid;grid-template-rows:subgrid;position:relative}.datatable-row-left,.datatable-row-right{position:sticky;z-index:9}.datatable-row-left{inset-inline-start:0}.datatable-row-right{inset-inline-end:0}\n"], dependencies: [{ kind: "component", type: DataTableBodyCellComponent, selector: "datatable-body-cell", inputs: ["displayCheck", "disabled", "group", "rowHeight", "isSelected", "rowIndex", "column", "row", "treeStatus", "expanded"], outputs: ["activate", "treeAction"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DataTableBodyRowComponent, decorators: [{
type: Component,
args: [{ selector: 'datatable-body-row', imports: [DataTableBodyCellComponent], template: `
@for (colGroup of _columnsByPin(); track colGroup.type) {
@if (colGroup.columns.length) {
<div
class="datatable-row-group"
[class]="'datatable-row-' + colGroup.type"
[style.grid-column]="'span ' + colGroup.columns.length"
[class.row-disabled]="disabled()"
>
@for (column of colGroup.columns; track column.$$id) {
<datatable-body-cell
role="cell"
tabindex="-1"
[row]="row()"
[group]="group()"
[expanded]="expanded()"
[isSelected]="isSelected()"
[rowIndex]="rowIndex()"
[column]="column"
[rowHeight]="rowHeight()"
[displayCheck]="displayCheck()"
[disabled]="disabled()"
[treeStatus]="treeStatus()"
(activate)="onActivate($event, column)"
(treeAction)="onTreeAction()"
/>
}
</div>
}
}
`, changeDetection: ChangeDetectionStrategy.OnPush, host: {
class: 'datatable-body-row',
role: 'row',
tabindex: '-1',
'[class]': 'cssClass()',
'[class.active]': 'isSelected()',
'[class.datatable-row-odd]': 'innerRowIndex() % 2 !== 0',
'[class.datatable-row-even]': 'innerRowIndex() % 2 === 0',
'[class.row-disabled]': 'disabled()',
'[style.height.px]': 'rowHeight()'
}, styles: [":host{display:grid;grid-template-columns:subgrid;grid-column:1/-1;grid-template-rows:minmax(0,1fr);outline:none}:host-context(ngx-datatable.fixed-row) :host{white-space:nowrap}.datatable-row-group{display:grid;grid-template-columns:subgrid;grid-template-rows:subgrid;position:relative}.datatable-row-left,.datatable-row-right{position:sticky;z-index:9}.datatable-row-left{inset-inline-start:0}.datatable-row-right{inset-inline-end:0}\n"] }]
}], propDecorators: { columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: true }] }], expanded: [{ type: i0.Input, args: [{ isSignal: true, alias: "expanded", required: false }] }], rowClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowClass", required: false }] }], row: [{ type: i0.Input, args: [{ isSignal: true, alias: "row", required: true }] }], group: [{ type: i0.Input, args: [{ isSignal: true, alias: "group", required: false }] }], isSelected: [{ type: i0.Input, args: [{ isSignal: true, alias: "isSelected", required: false }] }], rowIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowIndex", required: true }] }], displayCheck: [{ type: i0.Input, args: [{ isSignal: true, alias: "displayCheck", required: false }] }], treeStatus: [{ type: i0.Input, args: [{ isSignal: true, alias: "treeStatus", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], checkRowPropertyChanges: [{ type: i0.Input, args: [{ isSignal: true, alias: "checkRowPropertyChanges", required: false }] }], rowHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowHeight", required: true }] }], activate: [{ type: i0.Output, args: ["activate"] }], treeAction: [{ type: i0.Output, args: ["treeAction"] }], cells: [{ type: i0.ViewChildren, args: [i0.forwardRef(() => DataTableBodyCellComponent), { isSignal: true }] }], onKeyDown: [{
type: HostListener,
args: ['keydown', ['$event']]
}], onMouseenter: [{
type: HostListener,
args: ['mouseenter', ['$event']]
}] } });
class DatatableBodyRowDirective {
static ngTemplateContextGuard(directive, context) {
return true;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DatatableBodyRowDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.8", type: DatatableBodyRowDirective, isStandalone: true, selector: "[ngx-datatable-body-row]", ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DatatableBodyRowDirective, decorators: [{
type: Directive,
args: [{
selector: '[ngx-datatable-body-row]'
}]
}] });
class DataTableGhostLoaderComponent {
columns = input.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "columns" }] : /* istanbul ignore next */ []));
pageSize = input.required({ ...(ngDevMode ? { debugName: "pageSize" } : /* istanbul ignore next */ {}), transform: numberAttribute });
rowHeight = input.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "rowHeight" }] : /* istanbul ignore next */ []));
ghostBodyHeight = input(undefined, { ...(ngDevMode ? { debugName: "ghostBodyHeight" } : /* istanbul ignore next */ {}), transform: numberAttribute });
ghostRows = computed(() => Array.from({ length: this.pageSize() }, (_, index) => index), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "ghostRows" }] : /* istanbul ignore next */ []));
rowHeightComputed = () => {
const rowHeight = this.rowHeight();
if (typeof rowHeight === 'function') {
// If rowHeight is a function, we cannot determine a fixed height here.
return 'auto';
}
return rowHeight === 'auto' ? 'auto' : rowHeight + 'px';
};
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DataTableGhostLoaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: DataTableGhostLoaderComponent, isStandalone: true, selector: "ghost-loader", inputs: { columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: true, transformFunction: null }, pageSize: { classPropertyName: "pageSize", publicName: "pageSize", isSignal: true, isRequired: true, transformFunction: null }, rowHeight: { classPropertyName: "rowHeight", publicName: "rowHeight", isSignal: true, isRequired: true, transformFunction: null }, ghostBodyHeight: { classPropertyName: "ghostBodyHeight", publicName: "ghostBodyHeight", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"ghost-loader ghost-cell-container\" [style.height]=\"ghostBodyHeight()\">\n @for (item of ghostRows(); track item) {\n <div class=\"ghost-element datatable-body-row\" [style.height]=\"rowHeightComputed()\">\n @for (col of columns(); track col) {\n <div class=\"ghost-cell datatable-body-cell\" [style.width.px]=\"col.width()\">\n @if (!col.ghostCellTemplate) {\n <div class=\"line ghost-cell-strip\"></div>\n } @else {\n <ng-template [ngTemplateOutlet]=\"col.ghostCellTemplate\" />\n }\n </div>\n }\n </div>\n }\n</div>\n", styles: ["@keyframes ghost{0%{background-position:0 0}to{background-position:100vi 0}}.ghost-loader{overflow:hidden}.ghost-loader .line{inline-size:100%;block-size:12px;animation-name:ghost;animation-iteration-count:infinite;animation-timing-function:linear}.ghost-loader .ghost-element{display:flex;align-items:center}:host.ghost-overlay{position:sticky;inset-block-start:20px}:host.ghost-overlay .ghost-cell{padding-inline: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: "22.0.8", ngImport: i0, type: DataTableGhostLoaderComponent, decorators: [{
type: Component,
args: [{ selector: 'ghost-loader', imports: [NgTemplateOutlet], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"ghost-loader ghost-cell-container\" [style.height]=\"ghostBodyHeight()\">\n @for (item of ghostRows(); track item) {\n <div class=\"ghost-element datatable-body-row\" [style.height]=\"rowHeightComputed()\">\n @for (col of columns(); track col) {\n <div class=\"ghost-cell datatable-body-cell\" [style.width.px]=\"col.width()\">\n @if (!col.ghostCellTemplate) {\n <div class=\"line ghost-cell-strip\"></div>\n } @else {\n <ng-template [ngTemplateOutlet]=\"col.ghostCellTemplate\" />\n }\n </div>\n }\n </div>\n }\n</div>\n", styles: ["@keyframes ghost{0%{background-position:0 0}to{background-position:100vi 0}}.ghost-loader{overflow:hidden}.ghost-loader .line{inline-size:100%;block-size:12px;animation-name:ghost;animation-iteration-count:infinite;animation-timing-function:linear}.ghost-loader .ghost-element{display:flex;align-items:center}:host.ghost-overlay{position:sticky;inset-block-start:20px}:host.ghost-overlay .ghost-cell{padding-inline:1.2rem}\n"] }]
}], propDecorators: { columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: true }] }], pageSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSize", required: true }] }], rowHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowHeight", required: true }] }], ghostBodyHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "ghostBodyHeight", required: false }] }] } });
class ScrollerComponent {
scrollContainer = inject(ScrollContainerDirective);
scrollbarV = input(false, { ...(ngDevMode ? { debugName: "scrollbarV" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
scrollbarH = input(false, { ...(ngDevMode ? { debugName: "scrollbarH" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
scrollHeight = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "scrollHeight" }] : /* istanbul ignore next */ []));
scroll = output();
scrollYPos = 0;
scrollXPos = 0;
prevScrollYPos = 0;
prevScrollXPos = 0;
_removeScrollListener;
get scrollTop() {
return this.scrollContainer.scrollTop;
}
ngOnInit() {
// manual bind so we don't always listen
if (this.scrollbarV() || this.scrollbarH()) {
this._removeScrollListener = this.scrollContainer.listenToScroll(this.onScrolled.bind(this));
}
}
ngOnDestroy() {
this._removeScrollListener?.();
this._removeScrollListener = undefined;
}
setOffset(offsetY) {
this.scrollContainer.setScrollTop(offsetY);
}
scrollTo(top, options) {
this.scrollContainer.scrollTo(top, options);
}
onScrolled(event) {
const dom = event.currentTarget;
this.scrollYPos = dom.scrollTop;
this.scrollXPos = dom.scrollLeft;
this.updateOffset();
}
updateOffset() {
let direction;
if (this.scrollYPos < this.prevScrollYPos) {
direction = 'down';
}
else {
direction = 'up';
}
this.scroll.emit({
direction,
scrollYPos: this.scrollYPos,
scrollXPos: this.scrollXPos
});
this.prevScrollYPos = this.scrollYPos;
this.prevScrollXPos = this.scrollXPos;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: ScrollerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.8", type: ScrollerComponent, isStandalone: true, selector: "datatable-scroller", inputs: { scrollbarV: { classPropertyName: "scrollbarV", publicName: "scrollbarV", isSignal: true, isRequired: false, transformFunction: null }, scrollbarH: { classPropertyName: "scrollbarH", publicName: "scrollbarH", isSignal: true, isRequired: false, transformFunction: null }, scrollHeight: { classPropertyName: "scrollHeight", publicName: "scrollHeight", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { scroll: "scroll" }, host: { properties: { "style.height.px": "scrollHeight()" }, classAttribute: "datatable-scroll" }, ngImport: i0, template: ` <ng-content /> `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: ScrollerComponent, decorators: [{
type: Component,
args: [{
selector: 'datatable-scroller',
template: ` <ng-content /> `,
changeDetection: ChangeDetectionStrategy.OnPush,
host: {
class: 'datatable-scroll',
'[style.height.px]': 'scrollHeight()'
}
}]
}], propDecorators: { scrollbarV: [{ type: i0.Input, args: [{ isSignal: true, alias: "scrollbarV", required: false }] }], scrollbarH: [{ type: i0.Input, args: [{ isSignal: true, alias: "scrollbarH", required: false }] }], scrollHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "scrollHeight", required: false }] }], scroll: [{ type: i0.Output, args: ["scroll"] }] } });
const defaultSumFunc = (cells) => {
const cellsWithValues = cells.filter(cell => !!cell);
if (!cellsWithValues.length) {
return null;
}
if (cellsWithValues.some(cell => typeof cell !== 'number')) {
return null;
}
return cellsWithValues.reduce((res, cell) => res + cell);
};
const noopSumFunc = (cells) => {
return;
};
class DataTableSummaryRowComponent {
rows = input.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "rows" }] : /* istanbul ignore next */ []));
columns = input.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "columns" }] : /* istanbul ignore next */ []));
allColumnsColspan = input.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "allColumnsColspan" }] : /* istanbul ignore next */ []));
rowHeight = input.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "rowHeight" }] : /* istanbul ignore next */ []));
template = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "template" }] : /* istanbul ignore next */ []));
_internalColumns = computed(() => {
return this.columns().map(col => ({
...col,
cellTemplate: col.summaryTemplate
}));
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "_internalColumns" }] : /* istanbul ignore next */ []));
summaryRow = computed(() => this.computeSummaryRowValues(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "summaryRow" }] : /* istanbul ignore next */ []));
computeSummaryRowValues() {
if (!this.columns().length || !this.rows().length) {
return undefined;
}
const summaryRow = {};
this.columns()
.filter(col => !col.summaryTemplate && col.prop)
.forEach(col => {
const cellsFromSingleColumn = this.rows().map(row => row[col.prop]);
const sumFunc = this.getSummaryFunction(col);
summaryRow[col.prop] = col.pipe
? col.pipe.transform(sumFunc(cellsFromSingleColumn))
: sumFunc(cellsFromSingleColumn);
});
return summaryRow;
}
getSummaryFunction(column) {
if (column.summaryFunc === undefined) {
return defaultSumFunc;
}
else if (column.summaryFunc === null) {
return noopSumFunc;
}
else {
return column.summaryFunc;
}
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DataTableSummaryRowComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: DataTableSummaryRowComponent, isStandalone: true, selector: "datatable-summary-row", inputs: { rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: true, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: true, transformFunction: null }, allColumnsColspan: { classPropertyName: "allColumnsColspan", publicName: "allColumnsColspan", isSignal: true, isRequired: true, transformFunction: null }, rowHeight: { classPropertyName: "rowHeight", publicName: "rowHeight", isSignal: true, isRequired: true, transformFunction: null }, template: { classPropertyName: "template", publicName: "template", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "datatable-summary-row" }, ngImport: i0, template: `
@let template = this.template();
@if (template) {
<div class="datatable-body-row" role="row" [style.height.px]="rowHeight()">
<div class="datatable-body-cell" role="cell" [attr.aria-colspan]="allColumnsColspan()">
<ng-container [ngTemplateOutlet]="template" />
</div>
</div>
} @else {
@let summaryRow = this.summaryRow();
@let _internalColumns = this._internalColumns();
@if (summaryRow && _internalColumns.length) {
<datatable-body-row
ariaRowCheckboxMessage=""
[columns]="_internalColumns"
[rowHeight]="rowHeight()"
[row]="summaryRow"
[rowIndex]="{ index: -1 }"
/>
}
}
`, isInline: true, styles: [":host{display:grid;grid-template-columns:subgrid;grid-column:1/-1}:host.sticky{position:sticky;inset-block-start:0;z-index:2}.datatable-body-row{grid-column:1/-1}\n"], dependencies: [{ kind: "component", type: DataTableBodyRowComponent, selector: "datatable-body-row", inputs: ["columns", "expanded", "rowClass", "row", "group", "isSelected", "rowIndex", "displayCheck", "treeStatus", "disabled", "checkRowPropertyChanges", "rowHeight"], outputs: ["activate", "treeAction"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DataTableSummaryRowComponent, decorators: [{
type: Component,
args: [{ selector: 'datatable-summary-row', imports: [DataTableBodyRowComponent, NgTemplateOutlet], template: `
@let template = this.template();
@if (template) {
<div class="datatable-body-row" role="row" [style.height.px]="rowHeight()">
<div class="datatable-body-cell" role="cell" [attr.aria-colspan]="allColumnsColspan()">
<ng-container [ngTemplateOutlet]="template" />
</div>
</div>
} @else {
@let summaryRow = this.summaryRow();
@let _internalColumns = this._internalColumns();
@if (summaryRow && _internalColumns.length) {
<datatable-body-row
ariaRowCheckboxMessage=""
[columns]="_internalColumns"
[rowHeight]="rowHeight()"
[row]="summaryRow"
[rowIndex]="{ index: -1 }"
/>
}
}
`, host: {
class: 'datatable-summary-row'
}, styles: [":host{display:grid;grid-template-columns:subgrid;grid-column:1/-1}:host.sticky{position:sticky;inset-block-start:0;z-index:2}.datatable-body-row{grid-column:1/-1}\n"] }]
}], propDecorators: { rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: true }] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: true }] }], allColumnsColspan: [{ type: i0.Input, args: [{ isSignal: true, alias: "allColumnsColspan", required: true }] }], rowHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowHeight", required: true }] }], template: [{ type: i0.Input, args: [{ isSignal: true, alias: "template", required: false }] }] } });
class DataTableBodyComponent {
cd = inject(ChangeDetectorRef);
destroyRef = inject(DestroyRef);
configuration = inject(DatatableConfiguration).configuration;
rowDefTemplate = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "rowDefTemplate" }] : /* istanbul ignore next */ []));
scrollbarV = input(false, { ...(ngDevMode ? { debugName: "scrollbarV" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
scrollbarH = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "scrollbarH" }] : /* istanbul ignore next */ []));
loadingIndicator = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "loadingIndicator" }] : /* istanbul ignore next */ []));
ghostLoadingIndicator = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "ghostLoadingIndicator" }] : /* istanbul ignore next */ []));
externalPaging = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "externalPaging" }] : /* istanbul ignore next */ []));
offsetX = model.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "offsetX" }] : /* istanbul ignore next */ []));
selectionType = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "selectionType" }] : /* istanbul ignore next */ []));
selected = model([], /* @ts-ignore */
...(ngDevMode ? [{ debugName: "selected" }] : /* istanbul ignore next */ []));
rowIdentity = input.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "rowIdentity" }] : /* istanbul ignore next */ []));
rowDetail = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "rowDetail" }] : /* istanbul ignore next */ []));
groupHeader = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "groupHeader" }] : /* istanbul ignore next */ []));
selectCheck = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "selectCheck" }] : /* istanbul ignore next */ []));
displayCheck = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "displayCheck" }] : /* istanbul ignore next */ []));
trackByProp = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "trackByProp" }] : /* istanbul ignore next */ []));
rowClass = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "rowClass" }] : /* istanbul ignore next */ []));
groupedRows = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "groupedRows" }] : /* istanbul ignore next */ []));
// TODO: Find a better way to handle default expansion state with signal input
// eslint-disable-next-line @angular-eslint/prefer-signals
groupExpansionDefault;
virtualization = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "virtualization" }] : /* istanbul ignore next */ []));
summaryRow = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "summaryRow" }] : /* istanbul ignore next */ []));
summaryPosition = input.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "summaryPosition" }] : /* istanbul ignore next */ []));
summaryHeight = input.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "summaryHeight" }] : /* istanbul ignore next */ []));
summaryRowTemplate = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "summaryRowTemplate" }] : /* istanbul ignore next */ []));
rowDraggable = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "rowDraggable" }] : /* istanbul ignore next */ []));
rowDragEvents = input.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "rowDragEvents" }] : /* istanbul ignore next */ []));
disableRowCheck = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "disableRowCheck" }] : /* istanbul ignore next */ []));
checkRowPropertyChanges = input(true, { ...(ngDevMode ? { debugName: "checkRowPropertyChanges" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
pageSize = input.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "pageSize" }] : /* istanbul ignore next */ []));
rows = input.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "rows" }] : /* istanbul ignore next */ []));
columns = input.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "columns" }] : /* istanbul ignore next */ []));
offset = input(0, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "offset" }] : /* istanbul ignore next */ []));
rowCount = input(0, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "rowCount" }] : /* istanbul ignore next */ []));
bodyHeight = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "bodyHeight" }] : /* istanbul ignore next */ []));
verticalScrollVisible = input(false, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "verticalScrollVisible" }] : /* istanbul ignore next */ []));
scroll = output();
page = output();
activate = output();
rowContextmenu = output();
treeAction = output();
scroller = viewChild(ScrollerComponent, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "scroller" }] : /* istanbul ignore next */ []));
rowWrappers = viewChildren(DataTableRowWrapperComponent, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "rowWrappers" }] : /* istanbul ignore next */ []));
rowComponents = viewChildren(DataTableBodyRowComponent, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "rowComponents" }] : /* istanbul ignore next */ []));
/**
* Returns if selection is enabled.
*/
get selectEnabled() {
return !!this.selectionType();
}
allColumnsColspan = computed(() => Math.max(1, this.columns().length), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "allColumnsColspan" }] : /* istanbul ignore next */ []));
/**
* Property that would calculate the height of scroll bar
* based on the row heights cache for virtual scroll and virtualization. Other scenarios
* calculate scroll height automatically (as height will be undefined).
*/
scrollHeight = computed(() => {
if (this.rowHeightsCache() && this.scrollbarV() && this.virtualization() && this.rowCount()) {
return this.rowHeightsCache().query(this.rowCount() - 1);
}
// avoid TS7030: Not all code paths return a value.
return undefined;
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "scrollHeight" }] : /* istanbul ignore next */ []));
detailRowHeightFn = computed(() => {
const rowDetail = this.rowDetail();
if (!rowDetail) {
return () => 0;
}
const rowHeight = rowDetail.rowHeight();
return typeof rowHeight === 'function' ? rowHeight : () => rowHeight;
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "detailRowHeightFn" }] : /* istanbul ignore next */ []));
rowsToRender = computed(() => {
return this.updateRows();
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "rowsToRender" }] : /* istanbul ignore next */ []));
rowHeightsCache = computed(() => this.computeRowHeightsCache(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "rowHeightsCache" }] : /* istanbul ignore next */ []));
offsetY = signal(0, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "offsetY" }] : /* istanbul ignore next */ []));
indexes = computed(() => this.computeIndexes(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "indexes" }] : /* istanbul ignore next */ []));
pendingFocus = signal(undefined, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "pendingFocus" }] : /* istanbul ignore next */ []));
rowTrackingFn;
rowExpansions = signal([], /* @ts-ignore */
...(ngDevMode ? [{ debugName: "rowExpansions" }] : /* istanbul ignore next */ []));
groupExpansions = signal([], /* @ts-ignore */
...(ngDevMode ? [{ debugName: "groupExpansions" }] : /* istanbul ignore next */ []));
_bodyHeight = computed(() => {
if (this.scrollbarV()) {
return this.bodyHeight() + 'px';
}
else {
return 'auto';
}
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "_bodyHeight" }] : /* istanbul ignore next */ []));
_offsetEvent = -1;
_draggedRow;
_draggedRowElement;
/**
* Creates an instance of DataTableBodyComponent.
*/
constructor() {
// declare fn here so we can get access to the `this` property
this.rowTrackingFn = (index, row) => {
if (this.ghostLoadingIndicator()) {
return index;
}
const trackByProp = this.trackByProp();
if (trackByProp && row && this.isRow(row)) {
return row[trackByProp];
}
else if (row && this.isGroup(row)) {
return row.key ?? index;
}
else {
return row ?? index;
}
};
effect(() => this.defaultGroupExpansionEffect());
effect(() => this.focusPendingRow());
}
ngOnChanges(changes) {
if (changes.bodyHeight || changes.rows || changes.rowCount || changes.pageSize) {
if (changes.pageSize) {
this._offsetEvent = -1;
this.updatePage('up');
this.updatePage('down');
}
}
}
/**
* Called after the constructor, initializing input properties
*/
ngOnInit() {
const rowDetail = this.rowDetail();
if (rowDetail) {
const listener = rowDetail.toggle.subscribe(event => this.rowToggleStateChange(event));
this.destroyRef.onDestroy(() => listener.unsubscribe());
}
const groupHeader = this.groupHeader();
if (groupHeader) {
const listener = groupHeader.toggle.subscribe(event => {
// Remove default expansion state once user starts manual toggle.
this.groupExpansionDefault = false;
this.groupToggleStateChange(event);
});
this.destroyRef.onDestroy(() => listener.unsubscribe());
}
}
defaultGroupExpansionEffect() {
if (this.groupedRows() &&
untracked(() => this.groupExpansions().length) === 0 &&
this.groupExpansionDefault) {
this.groupExpansions.set([...(this.groupedRows() ?? [])]);
}
}
groupToggleStateChange({ type, value }) {
if (type === 'group') {
this.toggleGroupExpansion(value);
}
if (type === 'all') {
this.toggleAllGroups(value);
}
// Refresh rows after toggle
this.cd.markForCheck();
}
rowToggleStateChange({ type, value }) {
if (type === 'row') {
this.toggleRowExpansion(value);
}
if (type === 'all') {
this.toggleAllRows(value);
}
// Refresh rows after toggle
this.cd.markForCheck();
}
/**
* Updates the Y offset given a new offset.
*/
updateOffsetY(offset) {
// scroller is missing on empty table
const scroller = this.scroller();
if (!scroller) {
return;
}
const virtualization = this.virtualization();
if (this.scrollbarV() && virtualization && offset) {
// First get the row Index that we need to move to.
const rowIndex = this.pageSize() * offset;
offset = this.rowHeightsCache().query(rowIndex - 1);
}
else if (this.scrollbarV() && !virtualization) {
offset = 0;
}
scroller.setOffset(offset ?? 0);
}
/**
* Body was scrolled, this is mainly useful for
* when a user is server-side pagination via virtual scroll.
*/
onBodyScroll(event) {
const scrollYPos = event.scrollYPos;
const scrollXPos = event.scrollXPos;
// if scroll change, trigger update
// this is mainly used for header cell positions
if (this.offsetY() !== scrollYPos || this.offsetX() !== scrollXPos) {
this.scroll.emit({
offsetY: scrollYPos,
offsetX: scrollXPos
});
}
this.offsetY.set(scrollYPos);
this.offsetX.set(scrollXPos);
this.updatePage(event.direction);
this.cd.detectChanges();
}
focusPendingRow() {
const pendingFocus = this.pendingFocus();
if (!pendingFocus) {
return;
}
const row = this.getRenderedRow(pendingFocus.location);
if (row) {
this.focusRenderedRow(row, pendingFocus.cellIndex);
this.pendingFocus.set(undefined);
}
}
/**
* Updates the page given a direction.
*/
updatePage(direction) {
let offset = this.indexes().first / this.pageSize();
const scrollInBetween = !Number.isInteger(offset);
if (direction === 'up') {
offset = Math.ceil(offset);
}
else if (direction === 'down') {
offset = Math.floor(offset);
}
if (direction !== undefined && !isNaN(offset) && offset !== this._offsetEvent) {
this._offsetEvent = offset;
// if scroll was done by mouse drag make sure previous row and next row data is also fetched if its not fetched
if (scrollInBetween && this.scrollbarV() && this.virtualization() && this.externalPaging()) {
const upRow = this.rows()[this.indexes().first - 1];
if (!upRow && direction === 'up') {
this.page.emit(offset - 1);
}
const downRow = this.rows()[this.indexes().first + this.pageSize()];
if (!downRow && direction === 'down') {
this.page.emit(offset + 1);
}
}
this.page.emit(offset);
}
}
/**
* Updates the rows in the view port
*/
updateRows() {
const { first, last } = this.indexes();
// if grouprowsby has been specified treat row paging
// parameters as group paging parameters ie if limit 10 has been
// specified treat it as 10 groups rather than 10 rows
const groupedRows = this.groupedRows();
const rows = groupedRows
? groupedRows.slice(first, Math.min(last, groupedRows.length))
: this.rows().slice(first, Math.min(last, this.rowCount()));
rows.length = last - first;
return rows;
}
scrollToIndex(index, options) {
if (this.virtualization()) {
const scroller = this.scroller();
if (!scroller) {
return;
}
const cache = this.rowHeightsCache();
const rowTop = cache.query(index - 1);
const rowBottom = cache.query(index);
const rowHeight = rowBottom - rowTop;
// virtualization always provides a numeric bodyHeight
const viewportHeight = this.bodyHeight();
const currentScrollTop = scroller.scrollTop;
const block = options?.block ?? 'start';
let top;
switch (block) {
case 'center':
top = rowTop - Math.max(0, (viewportHeight - rowHeight) / 2);
break;
case 'end':
top = rowBottom - viewportHeight;
break;
case 'nearest':
if (rowTop < currentScrollTop) {
top = rowTop;
}
else if (rowBottom > currentScrollTop + viewportHeight) {
top = rowBottom - viewportHeight;
}
else {
top = currentScrollTop;
}
break;
case 'start':
default:
top = rowTop;
break;
}
scroller.scrollTo(Math.max(0, top), options);
}
else {
this.rowWrappers()[index]?.scrollIntoView(options);
}
}
/**
* Get the row height
*/
getRowHeight(row) {
// if its a function return it
const rowHeight = this.configuration().rowHeight;
if (typeof rowHeight === 'function') {
return rowHeight(row);
}
return rowHeight;
}
getGroupHeaderRowHeight = (row, index) => {
const groupHeader = this.groupHeader();
if (!groupHeader) {
return 0;
}
const rowHeightValue = groupHeader?.rowHeight();
const rowHeight = rowHeightValue === 0 ? this.configuration().rowHeight : rowHeightValue;
return typeof rowHeight === 'function' ? rowHeight(row, index) : rowHeight;
};
/**
* Calculates the offset of the rendered rows.
* As virtual rows are not shown, we have to move all rendered rows
* by the total size of previous non-rendered rows.
* If each row has a size of 10px and the first 10 rows are not rendered due to scroll,
* then we have a renderOffset of 100px.
*/
renderOffset = computed(() => {
if (this.scrollbarV() && this.virtualization()) {
return `translateY(${this.rowHeightsCache().query(this.indexes().first - 1)}px)`;
}
else {
return '';
}
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "renderOffset" }] : /* istanbul ignore next */ []));
/**
* Updates the index of the rows in the viewport
*/
computeIndexes() {
let first = 0;
let last = this.rowCount();
if (this.scrollbarV()) {
if (this.virtualization()) {
// Calculation of the first and last indexes will be based on where the
// scrollY position would be at. The last index would be the one
// that shows up inside the view port the last.
const height = parseInt(this._bodyHeight(), 10);
first = this.rowHeightsCache().getRowIndex(this.offsetY());
last = this.rowHeightsCache().getRowIndex(height + this.offsetY()) + 1;
}
}
else {
// The server is handling paging and will pass an array that begins with the
// element at a specified offset. first should always be 0 with external paging.
if (!this.externalPaging()) {
first = Math.max(this.offset() * this.pageSize(), 0);
}
last = Math.min(first + this.pageSize(), this.rowCount());
}
return { first, last };
}
/**
* Refreshes the full Row Height cache. Should be used
* when the entire row array state has changed.
*/
computeRowHeightsCache() {
const cache = new RowHeightCache();
if (!this.scrollbarV() || (this.scrollbarV() && !this.virtualization())) {
return cache;
}
// Initialize the tree only if there are rows inside the tree.
if (this.rows().length) {
cache.initCache({
rows: this.rows(), // TODO: RowHeightCache does not support grouping
rowHeight: this.configuration().rowHeight,
detailRowHeight: this.detailRowHeightFn(),
externalVirtual: this.scrollbarV() && this.externalPaging(),
indexOffset: this.externalPaging() ? this.offset() * this.pageSize() : 0,
rowCount: this.rowCount(),
rowExpansions: new Set(this.rowDetail() ? this.rowExpansions() : [])
});
}
return cache;
}
/**
* Toggle the Expansion of the row i.e. if the row is expanded then it will
* collapse and vice versa. Note that the expanded status is stored as
* a part of the row object itself as we have to preserve the expanded row
* status in case of sorting and filtering of the row set.
*/
toggleRowExpansion(row) {
const rowExpandedIdx = this.getExpandedIdx(row, this.rowExpansions());
const expanded = rowExpandedIdx > -1;
// Update the toggled row and update thive nevere heights in the cache.
if (expanded) {
this.rowExpansions.update(expansions => {
expansions.splice(rowExpandedIdx, 1);
return [...expansions];
});
}
else {
this.rowExpansions.update(expansions => [...expansions, row]);
}
}
toggleGroupExpansion(row) {
const groupExpandedIdx = this.getExpandedIdx(row, this.groupExpansions());
const expanded = groupExpandedIdx > -1;
// Update the toggled row and update thive nevere heights in the cache.
if (expanded) {
this.groupExpansions.update(expansions => {
expansions.splice(groupExpandedIdx, 1);
return [...expansions];
});
}
else {
this.groupExpansions.update(expansions => [...expansions, row]);
}
}
/**
* Expand/Collapse all the rows no matter what their state is.
*/
toggleAllRows(expanded) {
// TODO requires fixing. This still does not work with groups.
this.rowExpansions.set(expanded ? [...this.rows()] : []);
}
/**
* Expand/Collapse all the groups no matter what their state is.
*/
toggleAllGroups(expanded) {
this.groupExpansions.set(expanded ? [...this.groupedRows()] : []);
}
/**
* Returns if the row was expanded and set default row expansion when row expansion is empty
*/
getRowExpanded(row) {
return this.getExpandedIdx(row, this.rowExpansions()) > -1;
}
getGroupExpanded(group) {
return this.getExpandedIdx(group, this.groupExpansions()) > -1;
}
getExpandedIdx(row, expanded) {
if (!expanded?.length) {
return -1;
}
const rowId = this.rowIdentity()(row);
return expanded.findIndex(r => {
const id = this.rowIdentity()(r);
return id === rowId;
});
}
onTreeAction(row) {
this.treeAction.emit({ row });
}
dragOver(event, dropRow) {
event.preventDefault();
this.rowDragEvents().emit({
event,
srcElement: this._draggedRowElement,
eventType: 'dragover',
dragRow: this._draggedRow,
dropRow
});
}
drag(event, dragRow, rowComponent) {
this._draggedRow = dragRow;
this._draggedRowElement = rowComponent._element;
this.rowDragEvents().emit({
event,
srcElement: this._draggedRowElement,
eventType: 'dragstart',
dragRow
});
}
drop(event, dropRow, rowComponent) {
event.preventDefault();
this.rowDragEvents().emit({
event,
srcElement: this._draggedRowElement,
targetElement: rowComponent._element,
eventType: 'drop',
dragRow: this._draggedRow,
dropRow
});
}
dragEnter(event, dropRow, rowComponent) {
event.preventDefault();
this.rowDragEvents().emit({
event,
srcElement: this._draggedRowElement,
targetElement: rowComponent._element,
eventType: 'dragenter',
dragRow: this._draggedRow,
dropRow
});
}
dragLeave(event, dropRow, rowComponent) {
event.preventDefault();
this.rowDragEvents().emit({
event,
srcElement: this._draggedRowElement,
targetElement: rowComponent._element,
eventType: 'dragleave',
dragRow: this._draggedRow,
dropRow
});
}
dragEnd(event, dragRow) {
event.preventDefault();
this.rowDragEvents().emit({
event,
srcElement: this._draggedRowElement,
eventType: 'dragend',
dragRow
});
this._draggedRow = undefined;
this._draggedRowElement = undefined;
}
prevIndex;
selectRow(event, index, row) {
if (!this.selectEnabled) {
return;
}
const chkbox = this.selectionType() === 'checkbox';
const multi = this.selectionType() === 'multi';
const multiClick = this.selectionType() === 'multiClick';
let selected;
// TODO: this code needs cleanup. Casting it to KeyboardEvent is not correct as it could also be other types.
if (multi || chkbox || multiClick) {
if (event.shiftKey && this.prevIndex !== undefined) {
const rangeSelection = selectRowsBetween(this.rows(), index, this.prevIndex);
selected = [...this.selected()];
for (const rangeRow of rangeSelection) {
if (this.getRowSelectedIdx(rangeRow, selected) < 0) {
selected.push(rangeRow);
}
}
}
else if (event.key === 'a' &&
(event.ctrlKey || event.metaKey)) {
// select all rows except dummy rows which are added for ghostloader in case of virtual scroll
selected = this.rows().filter(rowItem => !!rowItem);
}
else if (event.ctrlKey ||
event.metaKey ||
multiClick ||
chkbox) {
selected = selectRows([...this.selected()], row, this.getRowSelectedIdx.bind(this));
}
else {
selected = selectRows([], row, this.getRowSelectedIdx.bind(this));
}
}
else {
selected = selectRows([], row, this.getRowSelectedIdx.bind(this));
}
const selectCheck = this.selectCheck();
if (typeof selectCheck === 'function') {
selected = selected.filter(selectCheck.bind(this));
}
if (typeof this.disableRowCheck() === 'function') {
selected = selected.filter(rowData => !this.disableRowCheck()(rowData));
}
this.selected.set(selected);
this.prevIndex = index;
}
onActivate(modelObject, index, indexInGroup) {
const { type, event, row } = modelObject;
const chkbox = this.selectionType() === 'checkbox';
const select = (!chkbox && (type === 'click' || type === 'dblclick')) || (chkbox && type === 'checkbox');
if (select) {
this.selectRow(event, index, row);
}
else if (type === 'keydown') {
if (event.key === ENTER) {
this.selectRow(event, index, row);
}
else if (event.key === 'a' &&
(event.ctrlKey || event.metaKey)) {
this.selectRow(event, 0, row); // The row property is ignored in this case. So we can pass anything.
}
else {
this.onKeyboardFocus(modelObject, index, indexInGroup);
}
}
this.activate.emit(modelObject);
}
groupSelectedChange(selected, group) {
const selectedSet = new Set(this.selected());
if (selected) {
group.value.forEach(row => selectedSet.add(row));
}
else {
group.value.forEach(row => selectedSet.delete(row));
}
this.selected.set(Array.from(selectedSet));
}
onKeyboardFocus(modelObject, index, indexInGroup) {
const { key } = modelObject.event;
const shouldFocus = key === ARROW_UP || key === ARROW_DOWN || key === ARROW_RIGHT || key === ARROW_LEFT;
if (shouldFocus) {
const isCellSelection = this.selectionType() === 'cell';
const disableRowCheck = this.disableRowCheck();
if (typeof disableRowCheck === 'function') {
const isRowDisabled = disableRowCheck(modelObject.row);
if (isRowDisabled) {
return;
}
}
if (!isCellSelection) {
this.focusRow(index, key, indexInGroup);
}
else if (isCellSelection && modelObject.renderedCellIndex !== undefined) {
this.focusCell(index, key, modelObject.renderedCellIndex, indexInGroup);
}
}
}
focusRow(index, key, indexInGroup) {
this.moveFocus({ outerIndex: index, innerIndex: indexInGroup }, key);
}
focusCell(index, key, cellIndex, indexInGroup) {
if (key === ARROW_LEFT) {
this.getRenderedRow({ outerIndex: index, innerIndex: indexInGroup })?.focusCell(cellIndex - 1);
}
else if (key === ARROW_RIGHT) {
this.getRenderedRow({ outerIndex: index, innerIndex: indexInGroup })?.focusCell(cellIndex + 1);
}
else if (key === ARROW_UP || key === ARROW_DOWN) {
this.moveFocus({ outerIndex: index, innerIndex: indexInGroup }, key, cellIndex);
}
}
moveFocus(current, key, cellIndex) {
if (this.pendingFocus()) {
return;
}
const target = this.getAdjacentRowLocation(current, key);
if (!target) {
this.pendingFocus.set(undefined);
return;
}
const row = this.getRenderedRow(target);
if (row) {
this.pendingFocus.set(undefined);
row.scrollIntoView();
this.focusRenderedRow(row, cellIndex);
return;
}
this.pendingFocus.set({ location: target, cellIndex });
this.scrollToIndex(target.outerIndex, { block: 'nearest' });
}
getAdjacentRowLocation(current, key) {
if (key !== ARROW_UP && key !== ARROW_DOWN) {
return undefined;
}
const step = key === ARROW_UP ? -1 : 1;
const groups = this.groupedRows();
if (!groups) {
const outerIndex = current.outerIndex + step;
return outerIndex >= 0 && outerIndex < this.rowCount() ? { outerIndex } : undefined;
}
if (current.innerIndex === undefined) {
return undefined;
}
const group = groups[current.outerIndex];
if (!group) {
return undefined;
}
const innerIndex = current.innerIndex + step;
if (innerIndex >= 0 && innerIndex < group.value.length) {
return { outerIndex: current.outerIndex, innerIndex };
}
for (let outerIndex = current.outerIndex + step; groups[outerIndex]; outerIndex += step) {
const adjacentGroup = groups[outerIndex];
if (adjacentGroup.value.length && this.getGroupExpanded(adjacentGroup)) {
return {
outerIndex,
innerIndex: key === ARROW_UP ? adjacentGroup.value.length - 1 : 0
};
}
}
return undefined;
}
getRenderedRow(location) {
return this.rowComponents().find(row => {
const rowIndex = row.rowIndex();
return (rowIndex.index === location.outerIndex && rowIndex.indexInGroup === location.innerIndex);
});
}
focusRenderedRow(row, cellIndex) {
if (cellIndex === undefined) {
row.focus();
}
else {
row.focusCell(cellIndex);
}
}
getRowSelected(row) {
return this.getRowSelectedIdx(row, this.selected()) > -1;
}
getRowSelectedIdx(row, selected) {
if (!selected?.length) {
return -1;
}
const rowId = this.rowIdentity()(row);
return selected.findIndex(r => {
const id = this.rowIdentity()(r);
return id === rowId;
});
}
isGroup(row) {
return !!this.groupedRows();
}
isRow(row) {
return !this.groupedRows();
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DataTableBodyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: DataTableBodyComponent, isStandalone: true, selector: "datatable-body", inputs: { rowDefTemplate: { classPropertyName: "rowDefTemplate", publicName: "rowDefTemplate", isSignal: true, isRequired: false, transformFunction: null }, scrollbarV: { classPropertyName: "scrollbarV", publicName: "scrollbarV", isSignal: true, isRequired: false, transformFunction: null }, scrollbarH: { classPropertyName: "scrollbarH", publicName: "scrollbarH", isSignal: true, isRequired: false, transformFunction: null }, loadingIndicator: { classPropertyName: "loadingIndicator", publicName: "loadingIndicator", isSignal: true, isRequired: false, transformFunction: null }, ghostLoadingIndicator: { classPropertyName: "ghostLoadingIndicator", publicName: "ghostLoadingIndicator", isSignal: true, isRequired: false, transformFunction: null }, externalPaging: { classPropertyName: "externalPaging", publicName: "externalPaging", isSignal: true, isRequired: false, transformFunction: null }, offsetX: { classPropertyName: "offsetX", publicName: "offsetX", isSignal: true, isRequired: true, transformFunction: null }, selectionType: { classPropertyName: "selectionType", publicName: "selectionType", isSignal: true, isRequired: false, transformFunction: null }, selected: { classPropertyName: "selected", publicName: "selected", isSignal: true, isRequired: false, transformFunction: null }, rowIdentity: { classPropertyName: "rowIdentity", publicName: "rowIdentity", isSignal: true, isRequired: true, transformFunction: null }, rowDetail: { classPropertyName: "rowDetail", publicName: "rowDetail", isSignal: true, isRequired: false, transformFunction: null }, groupHeader: { classPropertyName: "groupHeader", publicName: "groupHeader", isSignal: true, isRequired: false, transformFunction: null }, selectCheck: { classPropertyName: "selectCheck", publicName: "selectCheck", isSignal: true, isRequired: false, transformFunction: null }, displayCheck: { classPropertyName: "displayCheck", publicName: "displayCheck", isSignal: true, isRequired: false, transformFunction: null }, trackByProp: { classPropertyName: "trackByProp", publicName: "trackByProp", isSignal: true, isRequired: false, transformFunction: null }, rowClass: { classPropertyName: "rowClass", publicName: "rowClass", isSignal: true, isRequired: false, transformFunction: null }, groupedRows: { classPropertyName: "groupedRows", publicName: "groupedRows", isSignal: true, isRequired: false, transformFunction: null }, groupExpansionDefault: { classPropertyName: "groupExpansionDefault", publicName: "groupExpansionDefault", isSignal: false, isRequired: false, transformFunction: null }, virtualization: { classPropertyName: "virtualization", publicName: "virtualization", isSignal: true, isRequired: false, transformFunction: null }, summaryRow: { classPropertyName: "summaryRow", publicName: "summaryRow", isSignal: true, isRequired: false, transformFunction: null }, summaryPosition: { classPropertyName: "summaryPosition", publicName: "summaryPosition", isSignal: true, isRequired: true, transformFunction: null }, summaryHeight: { classPropertyName: "summaryHeight", publicName: "summaryHeight", isSignal: true, isRequired: true, transformFunction: null }, summaryRowTemplate: { classPropertyName: "summaryRowTemplate", publicName: "summaryRowTemplate", isSignal: true, isRequired: false, transformFunction: null }, rowDraggable: { classPropertyName: "rowDraggable", publicName: "rowDraggable", isSignal: true, isRequired: false, transformFunction: null }, rowDragEvents: { classPropertyName: "rowDragEvents", publicName: "rowDragEvents", isSignal: true, isRequired: true, transformFunction: null }, disableRowCheck: { classPropertyName: "disableRowCheck", publicName: "disableRowCheck", isSignal: true, isRequired: false, transformFunction: null }, checkRowPropertyChanges: { classPropertyName: "checkRowPropertyChanges", publicName: "checkRowPropertyChanges", isSignal: true, isRequired: false, transformFunction: null }, pageSize: { classPropertyName: "pageSize", publicName: "pageSize", isSignal: true, isRequired: true, transformFunction: null }, rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: true, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: true, transformFunction: null }, offset: { classPropertyName: "offset", publicName: "offset", isSignal: true, isRequired: false, transformFunction: null }, rowCount: { classPropertyName: "rowCount", publicName: "rowCount", isSignal: true, isRequired: false, transformFunction: null }, bodyHeight: { classPropertyName: "bodyHeight", publicName: "bodyHeight", isSignal: true, isRequired: false, transformFunction: null }, verticalScrollVisible: { classPropertyName: "verticalScrollVisible", publicName: "verticalScrollVisible", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { offsetX: "offsetXChange", selected: "selectedChange", scroll: "scroll", page: "page", activate: "activate", rowContextmenu: "rowContextmenu", treeAction: "treeAction" }, host: { classAttribute: "datatable-body" }, viewQueries: [{ propertyName: "scroller", first: true, predicate: ScrollerComponent, descendants: true, isSignal: true }, { propertyName: "rowWrappers", predicate: DataTableRowWrapperComponent, descendants: true, isSignal: true }, { propertyName: "rowComponents", predicate: DataTableBodyRowComponent, descendants: true, isSignal: true }], usesOnChanges: true, ngImport: i0, template: `
@if (loadingIndicator()) {
<div class="custom-loading-indicator-wrapper">
<div class="custom-loading-content">
<ng-content select="[loading-indicator]" />
</div>
</div>
}
@let scrollbarV = this.scrollbarV();
@let columns = this.columns();
@let bodyHeight = this._bodyHeight();
@let rows = this.rows();
@let rowCount = this.rowCount();
@if (ghostLoadingIndicator() && (!rowCount || !virtualization() || !scrollbarV)) {
<ghost-loader
class="ghost-overlay"
[columns]="columns"
[pageSize]="pageSize()"
[rowHeight]="configuration().rowHeight"
[ghostBodyHeight]="bodyHeight"
/>
}
@if (rows.length) {
<datatable-scroller
[scrollbarV]="scrollbarV"
[scrollbarH]="scrollbarH()"
[scrollHeight]="scrollHeight()"
(scroll)="onBodyScroll($event)"
>
@if ((summaryRow() || summaryRowTemplate()) && summaryPosition() === 'top') {
<datatable-summary-row
[class.sticky]="summaryRowTemplate()"
[rowHeight]="summaryHeight()"
[rows]="rows"
[columns]="columns"
[allColumnsColspan]="allColumnsColspan()"
[template]="summaryRowTemplate()"
/>
}
<ng-template
#bodyRow
let-row="row"
let-index="index"
let-indexInGroup="indexInGroup"
let-groupedRows="groupedRows"
let-disabled="disabled"
ngx-datatable-body-row
>
@let absoluteIndex = indexes().first + index;
<datatable-row-wrapper
[attr.hidden]="
ghostLoadingIndicator() && (!rowCount || !virtualization() || !scrollbarV)
? true
: null
"
[rowDetail]="rowDetail()"
[detailRowHeightFn]="detailRowHeightFn()"
[row]="row"
[disabled]="disabled"
[expanded]="getRowExpanded(row)"
[rowIndex]="absoluteIndex"
[checkRowPropertyChanges]="checkRowPropertyChanges()"
(rowContextmenu)="rowContextmenu.emit($event)"
>
<datatable-body-row
#rowElement
[disabled]="disabled"
[isSelected]="getRowSelected(row)"
[columns]="columns"
[rowHeight]="getRowHeight(row)"
[row]="row"
[group]="groupedRows"
[rowIndex]="{ index: absoluteIndex, indexInGroup: indexInGroup }"
[expanded]="getRowExpanded(row)"
[rowClass]="rowClass()"
[displayCheck]="displayCheck()"
[treeStatus]="row?.treeStatus"
[draggable]="rowDraggable()"
[checkRowPropertyChanges]="checkRowPropertyChanges()"
(treeAction)="onTreeAction(row)"
(activate)="onActivate($event, absoluteIndex, indexInGroup)"
(drop)="drop($event, row, rowElement)"
(dragover)="dragOver($event, row)"
(dragenter)="dragEnter($event, row, rowElement)"
(dragleave)="dragLeave($event, row, rowElement)"
(dragstart)="drag($event, row, rowElement)"
(dragend)="dragEnd($event, row)"
/>
</datatable-row-wrapper>
</ng-template>
<div class="datatable-row-render-wrapper" [style.transform]="renderOffset()">
@for (group of rowsToRender(); track rowTrackingFn(i, group); let i = $index) {
@let absoluteIndex = indexes().first + i;
@if (!group && ghostLoadingIndicator()) {
<ghost-loader
[columns]="columns"
[pageSize]="1"
[rowHeight]="configuration().rowHeight"
/>
} @else if (group) {
@let disableRowCheck = this.disableRowCheck();
@let disabled = isRow(group) && disableRowCheck && disableRowCheck(group);
@let rowDefTemplate = this.rowDefTemplate();
@if (rowDefTemplate) {
<ng-container
*rowDefInternal="
{
template: rowDefTemplate,
rowTemplate: bodyRow,
row: group,
index: i
};
disabled: disabled
"
/>
} @else {
@if (isRow(group)) {
<ng-container
[ngTemplateOutlet]="bodyRow"
[ngTemplateOutletContext]="{
row: group,
index: i,
disabled
}"
/>
}
}
@if (isGroup(group)) {
<datatable-group-wrapper
[group]="group"
[attr.hidden]="
ghostLoadingIndicator() && (!rowCount || !virtualization() || !scrollbarV)
? true
: null
"
[groupHeader]="groupHeader()"
[groupHeaderRowHeight]="getGroupHeaderRowHeight(group, absoluteIndex)"
[disabled]="disabled"
[expanded]="getGroupExpanded(group)"
[rowIndex]="absoluteIndex"
[selected]="selected()"
[allColumnsColspan]="allColumnsColspan()"
(groupSelectedChange)="groupSelectedChange($event, group)"
>
@for (row of group.value; track rowTrackingFn($index, row)) {
@let disabled = disableRowCheck && disableRowCheck(row);
<ng-container
[ngTemplateOutlet]="bodyRow"
[ngTemplateOutletContext]="{
row,
groupedRows: group?.value,
index: i,
indexInGroup: $index,
disabled
}"
/>
}
</datatable-group-wrapper>
}
}
}
</div>
</datatable-scroller>
@if ((summaryRow() || summaryRowTemplate()) && summaryPosition() === 'bottom') {
<datatable-summary-row
[rowHeight]="summaryHeight()"
[rows]="rows"
[columns]="columns"
[allColumnsColspan]="allColumnsColspan()"
[template]="summaryRowTemplate()"
/>
}
}
@if (!rows?.length && !loadingIndicator() && !ghostLoadingIndicator()) {
<datatable-scroller
class="datatable-empty-scroller"
[scrollbarV]="scrollbarV"
[scrollbarH]="scrollbarH()"
[scrollHeight]="scrollHeight()"
(scroll)="onBodyScroll($event)"
>
<div role="row" class="datatable-empty-row">
<div role="cell" class="datatable-empty-cell" [attr.aria-colspan]="allColumnsColspan()">
<ng-content select="[empty-content]" />
</div>
</div>
</datatable-scroller>
}
`, isInline: true, styles: [":host{position:relative;z-index:10;display:grid;grid-template-columns:subgrid;grid-column:1/-1;grid-row:2;min-block-size:0}datatable-scroller,.datatable-row-render-wrapper{display:grid;grid-template-columns:subgrid;grid-column:1/-1;align-content:start}:host-context(ngx-datatable.fixed-row) datatable-scroller,:host-context(ngx-datatable.fixed-row) .datatable-row-render-wrapper{white-space:nowrap}.datatable-empty-scroller{display:block}.datatable-empty-row,.datatable-empty-cell{block-size:100%}.custom-loading-indicator-wrapper,ghost-loader{grid-column:1/-1}[hidden]{display:none!important}\n"], dependencies: [{ kind: "component", type: DataTableGhostLoaderComponent, selector: "ghost-loader", inputs: ["columns", "pageSize", "rowHeight", "ghostBodyHeight"] }, { kind: "component", type: ScrollerComponent, selector: "datatable-scroller", inputs: ["scrollbarV", "scrollbarH", "scrollHeight"], outputs: ["scroll"] }, { kind: "component", type: DataTableSummaryRowComponent, selector: "datatable-summary-row", inputs: ["rows", "columns", "allColumnsColspan", "rowHeight", "template"] }, { kind: "component", type: DataTableRowWrapperComponent, selector: "datatable-row-wrapper", inputs: ["rowDetail", "detailRowHeightFn", "row", "disabled", "rowIndex", "expanded", "checkRowPropertyChanges"], outputs: ["rowContextmenu"] }, { kind: "directive", type: DatatableRowDefInternalDirective, selector: "[rowDefInternal]", inputs: ["rowDefInternal", "rowDefInternalDisabled"] }, { kind: "component", type: DataTableBodyRowComponent, selector: "datatable-body-row", inputs: ["columns", "expanded", "rowClass", "row", "group", "isSelected", "rowIndex", "displayCheck", "treeStatus", "disabled", "checkRowPropertyChanges", "rowHeight"], outputs: ["activate", "treeAction"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: DatatableBodyRowDirective, selector: "[ngx-datatable-body-row]" }, { kind: "component", type: DataTableGroupWrapperComponent, selector: "datatable-group-wrapper", inputs: ["groupHeader", "groupHeaderRowHeight", "group", "groupedRows", "allColumnsColspan", "selected", "disabled", "rowIndex", "expanded"], outputs: ["groupSelectedChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DataTableBodyComponent, decorators: [{
type: Component,
args: [{ selector: 'datatable-body', imports: [
DataTableGhostLoaderComponent,
ScrollerComponent,
DataTableSummaryRowComponent,
DataTableRowWrapperComponent,
DatatableRowDefInternalDirective,
DataTableBodyRowComponent,
NgTemplateOutlet,
DatatableBodyRowDirective,
DataTableGroupWrapperComponent
], template: `
@if (loadingIndicator()) {
<div class="custom-loading-indicator-wrapper">
<div class="custom-loading-content">
<ng-content select="[loading-indicator]" />
</div>
</div>
}
@let scrollbarV = this.scrollbarV();
@let columns = this.columns();
@let bodyHeight = this._bodyHeight();
@let rows = this.rows();
@let rowCount = this.rowCount();
@if (ghostLoadingIndicator() && (!rowCount || !virtualization() || !scrollbarV)) {
<ghost-loader
class="ghost-overlay"
[columns]="columns"
[pageSize]="pageSize()"
[rowHeight]="configuration().rowHeight"
[ghostBodyHeight]="bodyHeight"
/>
}
@if (rows.length) {
<datatable-scroller
[scrollbarV]="scrollbarV"
[scrollbarH]="scrollbarH()"
[scrollHeight]="scrollHeight()"
(scroll)="onBodyScroll($event)"
>
@if ((summaryRow() || summaryRowTemplate()) && summaryPosition() === 'top') {
<datatable-summary-row
[class.sticky]="summaryRowTemplate()"
[rowHeight]="summaryHeight()"
[rows]="rows"
[columns]="columns"
[allColumnsColspan]="allColumnsColspan()"
[template]="summaryRowTemplate()"
/>
}
<ng-template
#bodyRow
let-row="row"
let-index="index"
let-indexInGroup="indexInGroup"
let-groupedRows="groupedRows"
let-disabled="disabled"
ngx-datatable-body-row
>
@let absoluteIndex = indexes().first + index;
<datatable-row-wrapper
[attr.hidden]="
ghostLoadingIndicator() && (!rowCount || !virtualization() || !scrollbarV)
? true
: null
"
[rowDetail]="rowDetail()"
[detailRowHeightFn]="detailRowHeightFn()"
[row]="row"
[disabled]="disabled"
[expanded]="getRowExpanded(row)"
[rowIndex]="absoluteIndex"
[checkRowPropertyChanges]="checkRowPropertyChanges()"
(rowContextmenu)="rowContextmenu.emit($event)"
>
<datatable-body-row
#rowElement
[disabled]="disabled"
[isSelected]="getRowSelected(row)"
[columns]="columns"
[rowHeight]="getRowHeight(row)"
[row]="row"
[group]="groupedRows"
[rowIndex]="{ index: absoluteIndex, indexInGroup: indexInGroup }"
[expanded]="getRowExpanded(row)"
[rowClass]="rowClass()"
[displayCheck]="displayCheck()"
[treeStatus]="row?.treeStatus"
[draggable]="rowDraggable()"
[checkRowPropertyChanges]="checkRowPropertyChanges()"
(treeAction)="onTreeAction(row)"
(activate)="onActivate($event, absoluteIndex, indexInGroup)"
(drop)="drop($event, row, rowElement)"
(dragover)="dragOver($event, row)"
(dragenter)="dragEnter($event, row, rowElement)"
(dragleave)="dragLeave($event, row, rowElement)"
(dragstart)="drag($event, row, rowElement)"
(dragend)="dragEnd($event, row)"
/>
</datatable-row-wrapper>
</ng-template>
<div class="datatable-row-render-wrapper" [style.transform]="renderOffset()">
@for (group of rowsToRender(); track rowTrackingFn(i, group); let i = $index) {
@let absoluteIndex = indexes().first + i;
@if (!group && ghostLoadingIndicator()) {
<ghost-loader
[columns]="columns"
[pageSize]="1"
[rowHeight]="configuration().rowHeight"
/>
} @else if (group) {
@let disableRowCheck = this.disableRowCheck();
@let disabled = isRow(group) && disableRowCheck && disableRowCheck(group);
@let rowDefTemplate = this.rowDefTemplate();
@if (rowDefTemplate) {
<ng-container
*rowDefInternal="
{
template: rowDefTemplate,
rowTemplate: bodyRow,
row: group,
index: i
};
disabled: disabled
"
/>
} @else {
@if (isRow(group)) {
<ng-container
[ngTemplateOutlet]="bodyRow"
[ngTemplateOutletContext]="{
row: group,
index: i,
disabled
}"
/>
}
}
@if (isGroup(group)) {
<datatable-group-wrapper
[group]="group"
[attr.hidden]="
ghostLoadingIndicator() && (!rowCount || !virtualization() || !scrollbarV)
? true
: null
"
[groupHeader]="groupHeader()"
[groupHeaderRowHeight]="getGroupHeaderRowHeight(group, absoluteIndex)"
[disabled]="disabled"
[expanded]="getGroupExpanded(group)"
[rowIndex]="absoluteIndex"
[selected]="selected()"
[allColumnsColspan]="allColumnsColspan()"
(groupSelectedChange)="groupSelectedChange($event, group)"
>
@for (row of group.value; track rowTrackingFn($index, row)) {
@let disabled = disableRowCheck && disableRowCheck(row);
<ng-container
[ngTemplateOutlet]="bodyRow"
[ngTemplateOutletContext]="{
row,
groupedRows: group?.value,
index: i,
indexInGroup: $index,
disabled
}"
/>
}
</datatable-group-wrapper>
}
}
}
</div>
</datatable-scroller>
@if ((summaryRow() || summaryRowTemplate()) && summaryPosition() === 'bottom') {
<datatable-summary-row
[rowHeight]="summaryHeight()"
[rows]="rows"
[columns]="columns"
[allColumnsColspan]="allColumnsColspan()"
[template]="summaryRowTemplate()"
/>
}
}
@if (!rows?.length && !loadingIndicator() && !ghostLoadingIndicator()) {
<datatable-scroller
class="datatable-empty-scroller"
[scrollbarV]="scrollbarV"
[scrollbarH]="scrollbarH()"
[scrollHeight]="scrollHeight()"
(scroll)="onBodyScroll($event)"
>
<div role="row" class="datatable-empty-row">
<div role="cell" class="datatable-empty-cell" [attr.aria-colspan]="allColumnsColspan()">
<ng-content select="[empty-content]" />
</div>
</div>
</datatable-scroller>
}
`, changeDetection: ChangeDetectionStrategy.OnPush, host: {
class: 'datatable-body'
}, styles: [":host{position:relative;z-index:10;display:grid;grid-template-columns:subgrid;grid-column:1/-1;grid-row:2;min-block-size:0}datatable-scroller,.datatable-row-render-wrapper{display:grid;grid-template-columns:subgrid;grid-column:1/-1;align-content:start}:host-context(ngx-datatable.fixed-row) datatable-scroller,:host-context(ngx-datatable.fixed-row) .datatable-row-render-wrapper{white-space:nowrap}.datatable-empty-scroller{display:block}.datatable-empty-row,.datatable-empty-cell{block-size:100%}.custom-loading-indicator-wrapper,ghost-loader{grid-column:1/-1}[hidden]{display:none!important}\n"] }]
}], ctorParameters: () => [], propDecorators: { rowDefTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowDefTemplate", required: false }] }], scrollbarV: [{ type: i0.Input, args: [{ isSignal: true, alias: "scrollbarV", required: false }] }], scrollbarH: [{ type: i0.Input, args: [{ isSignal: true, alias: "scrollbarH", required: false }] }], loadingIndicator: [{ type: i0.Input, args: [{ isSignal: true, alias: "loadingIndicator", required: false }] }], ghostLoadingIndicator: [{ type: i0.Input, args: [{ isSignal: true, alias: "ghostLoadingIndicator", required: false }] }], externalPaging: [{ type: i0.Input, args: [{ isSignal: true, alias: "externalPaging", required: false }] }], offsetX: [{ type: i0.Input, args: [{ isSignal: true, alias: "offsetX", required: true }] }, { type: i0.Output, args: ["offsetXChange"] }], selectionType: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectionType", required: false }] }], selected: [{ type: i0.Input, args: [{ isSignal: true, alias: "selected", required: false }] }, { type: i0.Output, args: ["selectedChange"] }], rowIdentity: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowIdentity", required: true }] }], rowDetail: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowDetail", required: false }] }], groupHeader: [{ type: i0.Input, args: [{ isSignal: true, alias: "groupHeader", required: false }] }], selectCheck: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectCheck", required: false }] }], displayCheck: [{ type: i0.Input, args: [{ isSignal: true, alias: "displayCheck", required: false }] }], trackByProp: [{ type: i0.Input, args: [{ isSignal: true, alias: "trackByProp", required: false }] }], rowClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowClass", required: false }] }], groupedRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "groupedRows", required: false }] }], groupExpansionDefault: [{
type: Input
}], virtualization: [{ type: i0.Input, args: [{ isSignal: true, alias: "virtualization", required: false }] }], summaryRow: [{ type: i0.Input, args: [{ isSignal: true, alias: "summaryRow", required: false }] }], summaryPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "summaryPosition", required: true }] }], summaryHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "summaryHeight", required: true }] }], summaryRowTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "summaryRowTemplate", required: false }] }], rowDraggable: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowDraggable", required: false }] }], rowDragEvents: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowDragEvents", required: true }] }], disableRowCheck: [{ type: i0.Input, args: [{ isSignal: true, alias: "disableRowCheck", required: false }] }], checkRowPropertyChanges: [{ type: i0.Input, args: [{ isSignal: true, alias: "checkRowPropertyChanges", required: false }] }], pageSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSize", required: true }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: true }] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: true }] }], offset: [{ type: i0.Input, args: [{ isSignal: true, alias: "offset", required: false }] }], rowCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowCount", required: false }] }], bodyHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "bodyHeight", required: false }] }], verticalScrollVisible: [{ type: i0.Input, args: [{ isSignal: true, alias: "verticalScrollVisible", required: false }] }], scroll: [{ type: i0.Output, args: ["scroll"] }], page: [{ type: i0.Output, args: ["page"] }], activate: [{ type: i0.Output, args: ["activate"] }], rowContextmenu: [{ type: i0.Output, args: ["rowContextmenu"] }], treeAction: [{ type: i0.Output, args: ["treeAction"] }], scroller: [{ type: i0.ViewChild, args: [i0.forwardRef(() => ScrollerComponent), { isSignal: true }] }], rowWrappers: [{ type: i0.ViewChildren, args: [i0.forwardRef(() => DataTableRowWrapperComponent), { isSignal: true }] }], rowComponents: [{ type: i0.ViewChildren, args: [i0.forwardRef(() => DataTableBodyRowComponent), { isSignal: true }] }] } });
class ProgressBarComponent {
configuration = inject(DatatableConfiguration).configuration;
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: ProgressBarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.0.8", type: ProgressBarComponent, isStandalone: true, selector: "datatable-progress", ngImport: i0, template: `
<div
class="progress-linear"
role="progressbar"
[attr.aria-label]="configuration().messages.ariaLoadingMessage"
>
<div class="container">
<div class="bar"></div>
</div>
</div>
`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: ProgressBarComponent, decorators: [{
type: Component,
args: [{
selector: 'datatable-progress',
template: `
<div
class="progress-linear"
role="progressbar"
[attr.aria-label]="configuration().messages.ariaLoadingMessage"
>
<div class="container">
<div class="bar"></div>
</div>
</div>
`,
changeDetection: ChangeDetectionStrategy.OnPush
}]
}] });
/**
* Use this component to construct custom table footer with standard pagination.
*
* It must be used inside the `ngx-datatable-footer`
*
* @example
* ```html
*
* <ngx-datatable>
* ...
* <ngx-datatable-footer>
* <ng-template>
* <app-custom-content />
* <ngx-datatable-pager />
* </ng-template>
* </ngx-datatable-footer>
* </ngx-datatable>
* ```
*/
class DatatablePagerComponent {
// We cannot inject the footer directly as it is not part of the injector when used in a template.
// But the table always is.
// Ideally we can one day fetch those attributes from a global state, but for now this is fine.
datatable = inject(DATATABLE_COMPONENT_TOKEN);
configuration = inject(DatatableConfiguration).configuration;
page = computed(() => this.datatable._footerComponent().curPage(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "page" }] : /* istanbul ignore next */ []));
pageSize = computed(() => this.datatable._footerComponent().pageSize(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "pageSize" }] : /* istanbul ignore next */ []));
count = computed(() => this.datatable._footerComponent().groupCount() ??
this.datatable._footerComponent().rowCount(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "count" }] : /* istanbul ignore next */ []));
pagerNextIcon = computed(() => this.datatable._footerComponent().pagerNextIcon(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "pagerNextIcon" }] : /* istanbul ignore next */ []));
pagerRightArrowIcon = computed(() => this.datatable._footerComponent().pagerRightArrowIcon(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "pagerRightArrowIcon" }] : /* istanbul ignore next */ []));
pagerLeftArrowIcon = computed(() => this.datatable._footerComponent().pagerLeftArrowIcon(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "pagerLeftArrowIcon" }] : /* istanbul ignore next */ []));
pagerPreviousIcon = computed(() => this.datatable._footerComponent().pagerPreviousIcon(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "pagerPreviousIcon" }] : /* istanbul ignore next */ []));
totalPages = computed(() => {
return Math.max((this.pageSize() < 1 ? 1 : Math.ceil(this.count() / this.pageSize())) || 0, 1);
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "totalPages" }] : /* istanbul ignore next */ []));
pages = computed(() => {
const pages = [];
let startPage = 1;
let endPage = this.totalPages();
const maxSize = 5;
const isMaxSized = maxSize < this.totalPages();
const page = this.page();
if (isMaxSized) {
startPage = page - Math.floor(maxSize / 2);
endPage = page + Math.floor(maxSize / 2);
if (startPage < 1) {
startPage = 1;
endPage = Math.min(startPage + maxSize - 1, this.totalPages());
}
else if (endPage > this.totalPages()) {
startPage = Math.max(this.totalPages() - maxSize + 1, 1);
endPage = this.totalPages();
}
}
for (let num = startPage; num <= endPage; num++) {
pages.push({
number: num,
text: num.toString()
});
}
return pages;
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "pages" }] : /* istanbul ignore next */ []));
canPrevious = computed(() => this.page() > 1, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "canPrevious" }] : /* istanbul ignore next */ []));
canNext = computed(() => this.page() < this.totalPages(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "canNext" }] : /* istanbul ignore next */ []));
prevPage() {
this.selectPage(this.page() - 1);
}
nextPage() {
this.selectPage(this.page() + 1);
}
selectPage(page) {
if (page > 0 && page <= this.totalPages() && page !== this.page()) {
this.datatable._footerComponent().page.emit({ page });
}
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DatatablePagerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: DatatablePagerComponent, isStandalone: true, selector: "ngx-datatable-pager", host: { classAttribute: "datatable-pager" }, ngImport: i0, template: `
<ul class="pager">
<li>
<button
type="button"
class="page-button"
[disabled]="!canPrevious()"
[attr.aria-label]="configuration().messages.ariaFirstPageMessage"
(click)="selectPage(1)"
>
<i [class]="pagerPreviousIcon()"></i>
</button>
</li>
<li>
<button
type="button"
class="page-button"
[disabled]="!canPrevious()"
[attr.aria-label]="configuration().messages.ariaPreviousPageMessage"
(click)="prevPage()"
>
<i [class]="pagerLeftArrowIcon()"></i>
</button>
</li>
@for (pg of pages(); track pg.number) {
<li class="pages">
<button
type="button"
class="page-button"
[class.active]="pg.number === page()"
[attr.aria-label]="configuration().messages.ariaPageNMessage + ' ' + pg.number"
(click)="selectPage(pg.number)"
>
{{ pg.text }}
</button>
</li>
}
<li>
<button
type="button"
class="page-button"
[disabled]="!canNext()"
[attr.aria-label]="configuration().messages.ariaNextPageMessage"
(click)="nextPage()"
>
<i [class]="pagerRightArrowIcon()"></i>
</button>
</li>
<li>
<button
type="button"
class="page-button"
[disabled]="!canNext()"
[attr.aria-label]="configuration().messages.ariaLastPageMessage"
(click)="selectPage(totalPages())"
>
<i [class]="pagerNextIcon()"></i>
</button>
</li>
</ul>
`, isInline: true, styles: [":host-context(datatable-footer .selected-count) .datatable-pager{flex:1 1 60%}:host{flex:1 1 80%;text-align:end}.pager,.pager li{padding:0;margin:0;display:inline-block;list-style:none}.page-button{cursor:pointer;display:inline-block}.page-button:disabled{cursor:not-allowed}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DatatablePagerComponent, decorators: [{
type: Component,
args: [{ selector: 'ngx-datatable-pager', template: `
<ul class="pager">
<li>
<button
type="button"
class="page-button"
[disabled]="!canPrevious()"
[attr.aria-label]="configuration().messages.ariaFirstPageMessage"
(click)="selectPage(1)"
>
<i [class]="pagerPreviousIcon()"></i>
</button>
</li>
<li>
<button
type="button"
class="page-button"
[disabled]="!canPrevious()"
[attr.aria-label]="configuration().messages.ariaPreviousPageMessage"
(click)="prevPage()"
>
<i [class]="pagerLeftArrowIcon()"></i>
</button>
</li>
@for (pg of pages(); track pg.number) {
<li class="pages">
<button
type="button"
class="page-button"
[class.active]="pg.number === page()"
[attr.aria-label]="configuration().messages.ariaPageNMessage + ' ' + pg.number"
(click)="selectPage(pg.number)"
>
{{ pg.text }}
</button>
</li>
}
<li>
<button
type="button"
class="page-button"
[disabled]="!canNext()"
[attr.aria-label]="configuration().messages.ariaNextPageMessage"
(click)="nextPage()"
>
<i [class]="pagerRightArrowIcon()"></i>
</button>
</li>
<li>
<button
type="button"
class="page-button"
[disabled]="!canNext()"
[attr.aria-label]="configuration().messages.ariaLastPageMessage"
(click)="selectPage(totalPages())"
>
<i [class]="pagerNextIcon()"></i>
</button>
</li>
</ul>
`, changeDetection: ChangeDetectionStrategy.OnPush, host: {
class: 'datatable-pager'
}, styles: [":host-context(datatable-footer .selected-count) .datatable-pager{flex:1 1 60%}:host{flex:1 1 80%;text-align:end}.pager,.pager li{padding:0;margin:0;display:inline-block;list-style:none}.page-button{cursor:pointer;display:inline-block}.page-button:disabled{cursor:not-allowed}\n"] }]
}] });
class DataTableFooterComponent {
configuration = inject(DatatableConfiguration).configuration;
rowCount = input.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "rowCount" }] : /* istanbul ignore next */ []));
groupCount = input.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "groupCount" }] : /* istanbul ignore next */ []));
pageSize = input.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "pageSize" }] : /* istanbul ignore next */ []));
offset = input.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "offset" }] : /* istanbul ignore next */ []));
pagerLeftArrowIcon = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "pagerLeftArrowIcon" }] : /* istanbul ignore next */ []));
pagerRightArrowIcon = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "pagerRightArrowIcon" }] : /* istanbul ignore next */ []));
pagerPreviousIcon = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "pagerPreviousIcon" }] : /* istanbul ignore next */ []));
pagerNextIcon = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "pagerNextIcon" }] : /* istanbul ignore next */ []));
footerTemplate = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "footerTemplate" }] : /* istanbul ignore next */ []));
selectedCount = input(0, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "selectedCount" }] : /* istanbul ignore next */ []));
selectedMessage = input(false, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "selectedMessage" }] : /* istanbul ignore next */ []));
page = output();
isVisible = computed(() => this.rowCount() / this.pageSize() > 1, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "isVisible" }] : /* istanbul ignore next */ []));
curPage = computed(() => this.offset() + 1, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "curPage" }] : /* istanbul ignore next */ []));
templateContext = computed(() => ({
rowCount: this.rowCount(),
pageSize: this.pageSize(),
selectedCount: this.selectedCount(),
curPage: this.curPage(),
offset: this.offset()
}), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "templateContext" }] : /* istanbul ignore next */ []));
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DataTableFooterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: DataTableFooterComponent, isStandalone: true, selector: "datatable-footer", inputs: { rowCount: { classPropertyName: "rowCount", publicName: "rowCount", isSignal: true, isRequired: true, transformFunction: null }, groupCount: { classPropertyName: "groupCount", publicName: "groupCount", isSignal: true, isRequired: true, transformFunction: null }, pageSize: { classPropertyName: "pageSize", publicName: "pageSize", isSignal: true, isRequired: true, transformFunction: null }, offset: { classPropertyName: "offset", publicName: "offset", isSignal: true, isRequired: true, transformFunction: null }, pagerLeftArrowIcon: { classPropertyName: "pagerLeftArrowIcon", publicName: "pagerLeftArrowIcon", isSignal: true, isRequired: false, transformFunction: null }, pagerRightArrowIcon: { classPropertyName: "pagerRightArrowIcon", publicName: "pagerRightArrowIcon", isSignal: true, isRequired: false, transformFunction: null }, pagerPreviousIcon: { classPropertyName: "pagerPreviousIcon", publicName: "pagerPreviousIcon", isSignal: true, isRequired: false, transformFunction: null }, pagerNextIcon: { classPropertyName: "pagerNextIcon", publicName: "pagerNextIcon", isSignal: true, isRequired: false, transformFunction: null }, footerTemplate: { classPropertyName: "footerTemplate", publicName: "footerTemplate", isSignal: true, isRequired: false, transformFunction: null }, selectedCount: { classPropertyName: "selectedCount", publicName: "selectedCount", isSignal: true, isRequired: false, transformFunction: null }, selectedMessage: { classPropertyName: "selectedMessage", publicName: "selectedMessage", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { page: "page" }, host: { classAttribute: "datatable-footer" }, ngImport: i0, template: `
<div
class="datatable-footer-inner"
[class.selected-count]="selectedMessage()"
[style.height.px]="configuration().footerHeight"
>
@let footerTemplate = this.footerTemplate()?.template();
@if (footerTemplate) {
<ng-template
[ngTemplateOutlet]="footerTemplate"
[ngTemplateOutletContext]="templateContext()"
/>
} @else {
<div class="page-count">
@if (selectedMessage()) {
<span>
{{ selectedCount().toLocaleString() }}
{{ configuration().messages.selectedMessage }} /
</span>
}
{{ rowCount().toLocaleString() }} {{ configuration().messages.totalMessage }}
</div>
@if (isVisible()) {
<ngx-datatable-pager />
}
}
</div>
`, isInline: true, styles: [":host{display:block;inline-size:100%;overflow:auto}.datatable-footer-inner{display:flex;align-items:center;inline-size:100%}.page-count{flex:1 1 20%}.selected-count .page-count{flex:1 1 40%}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: DatatablePagerComponent, selector: "ngx-datatable-pager" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DataTableFooterComponent, decorators: [{
type: Component,
args: [{ selector: 'datatable-footer', imports: [NgTemplateOutlet, DatatablePagerComponent], template: `
<div
class="datatable-footer-inner"
[class.selected-count]="selectedMessage()"
[style.height.px]="configuration().footerHeight"
>
@let footerTemplate = this.footerTemplate()?.template();
@if (footerTemplate) {
<ng-template
[ngTemplateOutlet]="footerTemplate"
[ngTemplateOutletContext]="templateContext()"
/>
} @else {
<div class="page-count">
@if (selectedMessage()) {
<span>
{{ selectedCount().toLocaleString() }}
{{ configuration().messages.selectedMessage }} /
</span>
}
{{ rowCount().toLocaleString() }} {{ configuration().messages.totalMessage }}
</div>
@if (isVisible()) {
<ngx-datatable-pager />
}
}
</div>
`, changeDetection: ChangeDetectionStrategy.OnPush, host: {
class: 'datatable-footer'
}, styles: [":host{display:block;inline-size:100%;overflow:auto}.datatable-footer-inner{display:flex;align-items:center;inline-size:100%}.page-count{flex:1 1 20%}.selected-count .page-count{flex:1 1 40%}\n"] }]
}], propDecorators: { rowCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowCount", required: true }] }], groupCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "groupCount", required: true }] }], pageSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSize", required: true }] }], offset: [{ type: i0.Input, args: [{ isSignal: true, alias: "offset", required: true }] }], pagerLeftArrowIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "pagerLeftArrowIcon", required: false }] }], pagerRightArrowIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "pagerRightArrowIcon", required: false }] }], pagerPreviousIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "pagerPreviousIcon", required: false }] }], pagerNextIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "pagerNextIcon", required: false }] }], footerTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "footerTemplate", required: false }] }], selectedCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedCount", required: false }] }], selectedMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedMessage", required: false }] }], page: [{ type: i0.Output, args: ["page"] }] } });
class DataTableFooterTemplateDirective {
static ngTemplateContextGuard(directive, context) {
return true;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DataTableFooterTemplateDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.8", type: DataTableFooterTemplateDirective, isStandalone: true, selector: "[ngx-datatable-footer-template]", ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DataTableFooterTemplateDirective, decorators: [{
type: Directive,
args: [{
selector: '[ngx-datatable-footer-template]'
}]
}] });
class DatatableFooterDirective {
_templateInput = input(undefined, { ...(ngDevMode ? { debugName: "_templateInput" } : /* istanbul ignore next */ {}), alias: 'template' });
_templateQuery = contentChild(DataTableFooterTemplateDirective, { ...(ngDevMode ? { debugName: "_templateQuery" } : /* istanbul ignore next */ {}), read: TemplateRef });
template = computed(() => this._templateInput() ?? this._templateQuery(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "template" }] : /* istanbul ignore next */ []));
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DatatableFooterDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "22.0.8", type: DatatableFooterDirective, isStandalone: true, selector: "ngx-datatable-footer", inputs: { _templateInput: { classPropertyName: "_templateInput", publicName: "template", isSignal: true, isRequired: false, transformFunction: null } }, queries: [{ propertyName: "_templateQuery", first: true, predicate: DataTableFooterTemplateDirective, descendants: true, read: TemplateRef, isSignal: true }], ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DatatableFooterDirective, decorators: [{
type: Directive,
args: [{
selector: 'ngx-datatable-footer'
}]
}], propDecorators: { _templateInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "template", required: false }] }], _templateQuery: [{ type: i0.ContentChild, args: [i0.forwardRef(() => DataTableFooterTemplateDirective), { ...{
read: TemplateRef
}, isSignal: true }] }] } });
class DatatableDraggableDirective {
document = inject(DOCUMENT);
element = inject(ElementRef).nativeElement;
dragModel = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "dragModel" }] : /* istanbul ignore next */ []));
dragStartDelay = input(0, { ...(ngDevMode ? { debugName: "dragStartDelay" } : /* istanbul ignore next */ {}), transform: numberAttribute });
enabled = input(true, { ...(ngDevMode ? { debugName: "enabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute, alias: 'datatableDraggable' });
dragMove = output();
dragEnd = output();
dragStart = output();
timeoutId;
touchId;
startX = signal(undefined, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "startX" }] : /* istanbul ignore next */ []));
startY = signal(undefined, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "startY" }] : /* istanbul ignore next */ []));
currentX;
currentY;
isLongPressing = computed(() => this.dragStartDelay() !== 0 && this.isDragging(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "isLongPressing" }] : /* istanbul ignore next */ []));
isDragging = computed(() => this.startX() !== undefined, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "isDragging" }] : /* istanbul ignore next */ []));
removePointerListeners;
constructor() {
effect(() => {
if (this.enabled()) {
this.element.addEventListener('mousedown', this.mousedown);
this.element.addEventListener('touchstart', this.touchstart);
this.element.addEventListener('contextmenu', this.contextmenu);
this.removePointerListeners = () => {
this.element.removeEventListener('mousedown', this.mousedown);
this.element.removeEventListener('touchstart', this.touchstart);
this.element.removeEventListener('contextmenu', this.contextmenu);
};
}
else {
this.removePointerListeners?.();
this.removePointerListeners = undefined;
}
});
}
ngOnDestroy() {
clearTimeout(this.timeoutId);
this.removePointerListeners?.();
}
mousedown = (event) => {
if (!this.enabled()) {
return;
}
event.stopPropagation();
event.preventDefault();
this.document.addEventListener('mouseup', this.ending);
this.delay(this.dragStartDelay()).then(() => {
this.document.addEventListener('mousemove', this.mousemove);
this.starting(event.clientX, event.clientY);
this.setDragging(true);
});
};
mousemove = (event) => this.moving(event.clientX, event.clientY);
// Prevent context menu on long-press drag. Since we don't call preventDefault() on
// touchstart to allow click events (sorting), the browser would show a context menu
// after a long press. We prevent this when dragging is active.
contextmenu = (event) => {
if (this.isDragging()) {
event.preventDefault();
}
};
touchstart = (event) => {
if (!this.enabled()) {
return;
}
event.stopPropagation();
const touch = event.touches.item(0);
this.touchId = touch.identifier;
this.document.addEventListener('touchend', this.ending);
this.delay(this.dragStartDelay()).then(() => {
if (this.touchId === touch.identifier) {
this.document.addEventListener('touchmove', this.touchmove, { passive: false });
this.starting(touch.clientX, touch.clientY);
this.setDragging(true);
}
});
};
touchmove = (event) => {
const touchMove = this.findTouch(event);
if (touchMove) {
// Prevent scrolling and other default touch behaviors during drag
event.preventDefault();
this.moving(touchMove.clientX, touchMove.clientY);
}
};
starting(clientX, clientY) {
this.startX.set(clientX);
this.startY.set(clientY);
this.currentX = clientX;
this.currentY = clientY;
this.dragStart.emit(this.dragEvent());
}
moving(clientX, clientY) {
this.currentX = clientX;
this.currentY = clientY;
this.dragMove.emit(this.dragEvent());
}
ending = () => {
const dragged = this.isDragging();
const dragEvent = dragged ? this.dragEvent() : undefined;
this.document.removeEventListener('mousemove', this.mousemove);
this.document.removeEventListener('touchmove', this.touchmove);
this.document.removeEventListener('mouseup', this.ending);
this.document.removeEventListener('touchend', this.ending);
this.touchId = undefined;
this.startX.set(undefined);
this.startY.set(undefined);
clearTimeout(this.timeoutId);
// This function is also called if the long press was aborted before the delay.
// In that case, we don't want to emit dragEnd.
if (dragged) {
this.setDragging(false);
this.dragEnd.emit(dragEvent);
}
};
dragEvent() {
return {
initialX: this.startX(),
initialY: this.startY(),
currentX: this.currentX,
currentY: this.currentY,
element: this.element,
model: this.dragModel()
};
}
setDragging(dragging) {
const model = this.dragModel();
if (model) {
model.dragging = dragging;
}
}
findTouch(event) {
return Array.from(event.touches).find(touch => touch.identifier === this.touchId);
}
delay(ms) {
return new Promise(resolve => (this.timeoutId = window.setTimeout(() => resolve(), ms)));
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DatatableDraggableDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.8", type: DatatableDraggableDirective, isStandalone: true, selector: "[datatableDraggable]", inputs: { dragModel: { classPropertyName: "dragModel", publicName: "dragModel", isSignal: true, isRequired: false, transformFunction: null }, dragStartDelay: { classPropertyName: "dragStartDelay", publicName: "dragStartDelay", isSignal: true, isRequired: false, transformFunction: null }, enabled: { classPropertyName: "enabled", publicName: "datatableDraggable", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { dragMove: "dragMove", dragEnd: "dragEnd", dragStart: "dragStart" }, host: { properties: { "class.draggable": "enabled()", "class.dragging": "isDragging()", "class.longpress": "isLongPressing()" } }, ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DatatableDraggableDirective, decorators: [{
type: Directive,
args: [{
selector: '[datatableDraggable]',
host: {
'[class.draggable]': 'enabled()',
'[class.dragging]': 'isDragging()',
'[class.longpress]': 'isLongPressing()'
}
}]
}], ctorParameters: () => [], propDecorators: { dragModel: [{ type: i0.Input, args: [{ isSignal: true, alias: "dragModel", required: false }] }], dragStartDelay: [{ type: i0.Input, args: [{ isSignal: true, alias: "dragStartDelay", required: false }] }], enabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "datatableDraggable", required: false }] }], dragMove: [{ type: i0.Output, args: ["dragMove"] }], dragEnd: [{ type: i0.Output, args: ["dragEnd"] }], dragStart: [{ type: i0.Output, args: ["dragStart"] }] } });
class DataTableHeaderCellComponent {
element = inject(ElementRef).nativeElement;
configuration = inject(DatatableConfiguration).configuration;
sortType = input.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "sortType" }] : /* istanbul ignore next */ []));
isTarget = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "isTarget" }] : /* istanbul ignore next */ []));
showResizeHandle = input(true, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "showResizeHandle" }] : /* istanbul ignore next */ []));
targetMarkerTemplate = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "targetMarkerTemplate" }] : /* istanbul ignore next */ []));
targetMarkerContext = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "targetMarkerContext" }] : /* istanbul ignore next */ []));
enableClearingSortState = input(false, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "enableClearingSortState" }] : /* istanbul ignore next */ []));
allRowsSelected = input(false, { ...(ngDevMode ? { debugName: "allRowsSelected" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
selectionType = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "selectionType" }] : /* istanbul ignore next */ []));
column = input.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "column" }] : /* istanbul ignore next */ []));
sorts = input([], /* @ts-ignore */
...(ngDevMode ? [{ debugName: "sorts" }] : /* istanbul ignore next */ []));
sort = output();
select = output();
columnContextmenu = output();
resize = output();
resizing = output();
columnCssClasses = computed(() => {
const column = this.column();
if (!column.headerClass) {
return [];
}
if (typeof column.headerClass === 'string') {
return column.headerClass;
}
return column.headerClass({ column: toPublicColumn(column) });
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "columnCssClasses" }] : /* istanbul ignore next */ []));
name = computed(() => {
// guaranteed to have a value by setColumnDefaults() in column-helper.ts
return this.column().headerTemplate === undefined ? this.column().name : undefined;
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "name" }] : /* istanbul ignore next */ []));
isCheckboxable = computed(() => this.column().headerCheckboxable, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "isCheckboxable" }] : /* istanbul ignore next */ []));
sortClass = computed(() => this.calcSortClass(this.sortDir()), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "sortClass" }] : /* istanbul ignore next */ []));
sortDir = computed(() => {
return this.calcSortDir(this.sorts());
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "sortDir" }] : /* istanbul ignore next */ []));
ariaSort = computed(() => {
if (!this.column().sortable) {
return null;
}
switch (this.sortDir()) {
case 'asc':
return 'ascending';
case 'desc':
return 'descending';
default:
return 'none';
}
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "ariaSort" }] : /* istanbul ignore next */ []));
cellContext = computed(() => {
return {
column: toPublicColumn(this.column()),
sortDir: this.sortDir(),
sortFn: () => this.onSort(),
allRowsSelected: this.allRowsSelected(),
selectFn: () => this.select.emit()
};
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "cellContext" }] : /* istanbul ignore next */ []));
initialWidth;
subscription;
onContextmenu($event) {
this.columnContextmenu.emit({ event: $event, column: this.column() });
if (this.column().draggable) {
$event.preventDefault();
}
}
enter() {
this.onSort();
}
ngOnInit() {
// If there is already a default sort then start the counter with 1.
if (this.sortDir()) {
this.totalSortStatesApplied = 1;
}
}
ngOnDestroy() {
this.destroySubscription();
}
calcSortDir(sorts) {
if (sorts && this.column()) {
const sort = sorts.find((s) => s.prop === this.column().prop);
if (sort) {
return sort.dir;
}
}
}
// Counter to reset sort once user sort asc and desc.
totalSortStatesApplied = 0;
onSort() {
if (!this.column().sortable) {
return;
}
this.totalSortStatesApplied++;
let newValue = nextSortDir(this.sortType(), this.sortDir());
// User has done both direction sort so we reset the next sort.
if (this.enableClearingSortState() && this.totalSortStatesApplied === 3) {
newValue = undefined;
this.totalSortStatesApplied = 0;
}
this.sort.emit({
column: this.column(),
prevValue: this.sortDir(),
newValue
});
}
calcSortClass(sortDir) {
if (!this.cellContext().column.sortable) {
return undefined;
}
const base = 'sort-btn';
switch (sortDir) {
case 'asc':
return `${base} sort-asc ${this.configuration().cssClasses.sortAscending}`;
case 'desc':
return `${base} sort-desc ${this.configuration().cssClasses.sortDescending}`;
default:
return `${base} ${this.configuration().cssClasses.sortUnset}`;
}
}
onMousedown() {
this.initialWidth = this.element.clientWidth;
}
onMouseup() {
this.resize.emit({ width: this.element.clientWidth, column: this.column() });
}
move({ currentX, initialX }) {
this.resizing.emit({
width: this.initialWidth + (currentX - initialX),
column: this.column()
});
}
destroySubscription() {
if (this.subscription) {
this.subscription.unsubscribe();
this.subscription = undefined;
}
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DataTableHeaderCellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: DataTableHeaderCellComponent, isStandalone: true, selector: "datatable-header-cell", inputs: { sortType: { classPropertyName: "sortType", publicName: "sortType", isSignal: true, isRequired: true, transformFunction: null }, isTarget: { classPropertyName: "isTarget", publicName: "isTarget", isSignal: true, isRequired: false, transformFunction: null }, showResizeHandle: { classPropertyName: "showResizeHandle", publicName: "showResizeHandle", isSignal: true, isRequired: false, transformFunction: null }, targetMarkerTemplate: { classPropertyName: "targetMarkerTemplate", publicName: "targetMarkerTemplate", isSignal: true, isRequired: false, transformFunction: null }, targetMarkerContext: { classPropertyName: "targetMarkerContext", publicName: "targetMarkerContext", isSignal: true, isRequired: false, transformFunction: null }, enableClearingSortState: { classPropertyName: "enableClearingSortState", publicName: "enableClearingSortState", isSignal: true, isRequired: false, transformFunction: null }, allRowsSelected: { classPropertyName: "allRowsSelected", publicName: "allRowsSelected", isSignal: true, isRequired: false, transformFunction: null }, selectionType: { classPropertyName: "selectionType", publicName: "selectionType", isSignal: true, isRequired: false, transformFunction: null }, column: { classPropertyName: "column", publicName: "column", isSignal: true, isRequired: true, transformFunction: null }, sorts: { classPropertyName: "sorts", publicName: "sorts", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sort: "sort", select: "select", columnContextmenu: "columnContextmenu", resize: "resize", resizing: "resizing" }, host: { listeners: { "contextmenu": "onContextmenu($event)", "keydown.enter": "enter()" }, properties: { "attr.resizeable": "showResizeHandle()", "attr.title": "name()", "attr.tabindex": "column().sortable ? 0 : -1", "attr.aria-sort": "ariaSort()", "class": "columnCssClasses()", "class.sortable": "column().sortable", "class.resizeable": "showResizeHandle()", "class.sort-active": "sortDir()", "class.sort-asc": "sortDir() === \"asc\"", "class.sort-desc": "sortDir() === \"desc\"" }, classAttribute: "datatable-header-cell" }, ngImport: i0, template: `
<div class="datatable-header-cell-template-wrap">
@if (isTarget()) {
<ng-template
[ngTemplateOutlet]="targetMarkerTemplate()!"
[ngTemplateOutletContext]="targetMarkerContext()"
/>
}
@if (isCheckboxable()) {
<label class="datatable-checkbox">
<input
type="checkbox"
[attr.aria-label]="configuration().messages.ariaHeaderCheckboxMessage"
[checked]="allRowsSelected()"
(change)="select.emit()"
/>
</label>
}
@let column = this.column();
@if (column.headerTemplate) {
<ng-template
[ngTemplateOutlet]="column.headerTemplate"
[ngTemplateOutletContext]="cellContext()"
/>
} @else {
<span class="datatable-header-cell-wrapper">
<span class="datatable-header-cell-label draggable" (click)="onSort()">
{{ name() }}
</span>
</span>
}
<span aria-hidden="true" [class]="sortClass()" (click)="onSort()"> </span>
</div>
@if (showResizeHandle()) {
<span
class="resize-handle"
datatableDraggable
(dragStart)="onMousedown()"
(dragMove)="move($event)"
(dragEnd)="onMouseup()"
></span>
}
`, isInline: true, styles: [":host{overflow-x:hidden;min-inline-size:0;vertical-align:top;display:inline-block;line-height:1.625}:host:focus{outline:none}:host{position:relative;display:inline-block}:host.dragging{pointer-events:none}:host-context(ngx-datatable.fixed-header) :host{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}:host.sortable .datatable-header-cell-wrapper{cursor:pointer}:host.longpress .datatable-header-cell-wrapper{cursor:move}.datatable-header-cell-template-wrap{block-size:inherit}.sort-btn{line-height:100%;vertical-align:middle;display:inline-block;cursor:pointer}.resize-handle,.resize-handle--not-resizable{display:inline-block;position:absolute;inset-inline-end:0;inset-block:0;inline-size:5px;padding-block:0;padding-inline:4px;visibility:hidden}.resize-handle{cursor:ew-resize}:host(.resizeable:hover) .resize-handle{visibility:visible}@media(hover:none){:host{touch-action:none}:host .resize-handle{visibility:visible}:host .datatable-header-cell-label.draggable{-webkit-user-select:none;user-select:none}}.resize-handle--not-resizable :host(:hover){visibility:visible}:host::ng-deep .targetMarker{position:absolute;inset-block:0}:host::ng-deep .targetMarker.dragFromLeft{inset-inline-end:0}:host::ng-deep .targetMarker.dragFromRight{inset-inline-start:0}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: DatatableDraggableDirective, selector: "[datatableDraggable]", inputs: ["dragModel", "dragStartDelay", "datatableDraggable"], outputs: ["dragMove", "dragEnd", "dragStart"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DataTableHeaderCellComponent, decorators: [{
type: Component,
args: [{ selector: 'datatable-header-cell', imports: [NgTemplateOutlet, DatatableDraggableDirective], template: `
<div class="datatable-header-cell-template-wrap">
@if (isTarget()) {
<ng-template
[ngTemplateOutlet]="targetMarkerTemplate()!"
[ngTemplateOutletContext]="targetMarkerContext()"
/>
}
@if (isCheckboxable()) {
<label class="datatable-checkbox">
<input
type="checkbox"
[attr.aria-label]="configuration().messages.ariaHeaderCheckboxMessage"
[checked]="allRowsSelected()"
(change)="select.emit()"
/>
</label>
}
@let column = this.column();
@if (column.headerTemplate) {
<ng-template
[ngTemplateOutlet]="column.headerTemplate"
[ngTemplateOutletContext]="cellContext()"
/>
} @else {
<span class="datatable-header-cell-wrapper">
<span class="datatable-header-cell-label draggable" (click)="onSort()">
{{ name() }}
</span>
</span>
}
<span aria-hidden="true" [class]="sortClass()" (click)="onSort()"> </span>
</div>
@if (showResizeHandle()) {
<span
class="resize-handle"
datatableDraggable
(dragStart)="onMousedown()"
(dragMove)="move($event)"
(dragEnd)="onMouseup()"
></span>
}
`, changeDetection: ChangeDetectionStrategy.OnPush, host: {
class: 'datatable-header-cell',
'[attr.resizeable]': 'showResizeHandle()',
'[attr.title]': 'name()',
'[attr.tabindex]': 'column().sortable ? 0 : -1',
'[attr.aria-sort]': 'ariaSort()',
'[class]': 'columnCssClasses()',
'[class.sortable]': 'column().sortable',
'[class.resizeable]': 'showResizeHandle()',
'[class.sort-active]': 'sortDir()',
'[class.sort-asc]': 'sortDir() === "asc"',
'[class.sort-desc]': 'sortDir() === "desc"'
}, styles: [":host{overflow-x:hidden;min-inline-size:0;vertical-align:top;display:inline-block;line-height:1.625}:host:focus{outline:none}:host{position:relative;display:inline-block}:host.dragging{pointer-events:none}:host-context(ngx-datatable.fixed-header) :host{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}:host.sortable .datatable-header-cell-wrapper{cursor:pointer}:host.longpress .datatable-header-cell-wrapper{cursor:move}.datatable-header-cell-template-wrap{block-size:inherit}.sort-btn{line-height:100%;vertical-align:middle;display:inline-block;cursor:pointer}.resize-handle,.resize-handle--not-resizable{display:inline-block;position:absolute;inset-inline-end:0;inset-block:0;inline-size:5px;padding-block:0;padding-inline:4px;visibility:hidden}.resize-handle{cursor:ew-resize}:host(.resizeable:hover) .resize-handle{visibility:visible}@media(hover:none){:host{touch-action:none}:host .resize-handle{visibility:visible}:host .datatable-header-cell-label.draggable{-webkit-user-select:none;user-select:none}}.resize-handle--not-resizable :host(:hover){visibility:visible}:host::ng-deep .targetMarker{position:absolute;inset-block:0}:host::ng-deep .targetMarker.dragFromLeft{inset-inline-end:0}:host::ng-deep .targetMarker.dragFromRight{inset-inline-start:0}\n"] }]
}], propDecorators: { sortType: [{ type: i0.Input, args: [{ isSignal: true, alias: "sortType", required: true }] }], isTarget: [{ type: i0.Input, args: [{ isSignal: true, alias: "isTarget", required: false }] }], showResizeHandle: [{ type: i0.Input, args: [{ isSignal: true, alias: "showResizeHandle", required: false }] }], targetMarkerTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "targetMarkerTemplate", required: false }] }], targetMarkerContext: [{ type: i0.Input, args: [{ isSignal: true, alias: "targetMarkerContext", required: false }] }], enableClearingSortState: [{ type: i0.Input, args: [{ isSignal: true, alias: "enableClearingSortState", required: false }] }], allRowsSelected: [{ type: i0.Input, args: [{ isSignal: true, alias: "allRowsSelected", required: false }] }], selectionType: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectionType", required: false }] }], column: [{ type: i0.Input, args: [{ isSignal: true, alias: "column", required: true }] }], sorts: [{ type: i0.Input, args: [{ isSignal: true, alias: "sorts", required: false }] }], sort: [{ type: i0.Output, args: ["sort"] }], select: [{ type: i0.Output, args: ["select"] }], columnContextmenu: [{ type: i0.Output, args: ["columnContextmenu"] }], resize: [{ type: i0.Output, args: ["resize"] }], resizing: [{ type: i0.Output, args: ["resizing"] }], onContextmenu: [{
type: HostListener,
args: ['contextmenu', ['$event']]
}], enter: [{
type: HostListener,
args: ['keydown.enter']
}] } });
class DataTableHeaderComponent {
document = inject(DOCUMENT);
configuration = inject(DatatableConfiguration).configuration;
headerCells = viewChildren(DataTableHeaderCellComponent, { ...(ngDevMode ? { debugName: "headerCells" } : /* istanbul ignore next */ {}), read: ElementRef });
lastColumnId = computed(() => this.columns().at(-1)?.$$id, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "lastColumnId" }] : /* istanbul ignore next */ []));
scrollbarH = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "scrollbarH" }] : /* istanbul ignore next */ []));
dealsWithGroup = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "dealsWithGroup" }] : /* istanbul ignore next */ []));
targetMarkerTemplate = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "targetMarkerTemplate" }] : /* istanbul ignore next */ []));
enableClearingSortState = input(false, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "enableClearingSortState" }] : /* istanbul ignore next */ []));
sorts = input.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "sorts" }] : /* istanbul ignore next */ []));
sortType = input.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "sortType" }] : /* istanbul ignore next */ []));
allRowsSelected = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "allRowsSelected" }] : /* istanbul ignore next */ []));
selectionType = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "selectionType" }] : /* istanbul ignore next */ []));
reorderable = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "reorderable" }] : /* istanbul ignore next */ []));
verticalScrollVisible = input(false, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "verticalScrollVisible" }] : /* istanbul ignore next */ []));
columns = input.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "columns" }] : /* istanbul ignore next */ []));
sort = output();
reorder = output();
resize = output();
resizing = output();
select = output();
columnContextmenu = output();
columnGroups = computed(() => {
return columnsByPinArr(this.columns());
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "columnGroups" }] : /* istanbul ignore next */ []));
renderedColumns = computed(() => this.columnGroups().flatMap(group => group.columns), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "renderedColumns" }] : /* istanbul ignore next */ []));
dragInitialIndex;
dragTargetIndex;
targetColumn = signal(undefined, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "targetColumn" }] : /* istanbul ignore next */ []));
targetMarkerContext = signal(undefined, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "targetMarkerContext" }] : /* istanbul ignore next */ []));
onColumnResized({ width, column }) {
this.resize.emit(this.makeResizeEvent(width, column));
}
onColumnResizing({ width, column }) {
this.resizing.emit(this.makeResizeEvent(width, column));
}
makeResizeEvent(width, column) {
if (column.minWidth && width <= column.minWidth) {
width = column.minWidth;
}
else if (column.maxWidth && width >= column.maxWidth) {
width = column.maxWidth;
}
return {
column,
prevValue: column.width(),
newValue: width
};
}
onDragStart({ model }) {
this.dragInitialIndex = model ? this.renderedColumns().indexOf(model) : undefined;
}
onDragMove(event) {
const targetIndex = this.getDragTargetIndex(event);
if (targetIndex !== this.dragTargetIndex) {
if (this.dragTargetIndex !== undefined) {
this.targetColumn.set(undefined);
this.targetMarkerContext.set(undefined);
}
if (targetIndex !== undefined && this.dragInitialIndex !== undefined) {
this.targetColumn.set(this.renderedColumns()[targetIndex]);
if (this.dragInitialIndex !== targetIndex) {
this.targetMarkerContext.set({
class: `targetMarker ${this.dragInitialIndex > targetIndex ? 'dragFromRight' : 'dragFromLeft'}`
});
}
}
this.dragTargetIndex = targetIndex;
}
event.element.style.transform = `translateX(${event.currentX - event.initialX}px)`;
}
onDragEnd(event) {
event.element.style.transform = '';
const targetIndex = this.getDragTargetIndex(event);
if (this.dragTargetIndex !== undefined) {
this.targetColumn.set(undefined);
this.targetMarkerContext.set(undefined);
}
if (event.model && this.dragInitialIndex !== undefined && targetIndex !== undefined) {
this.reorder.emit({
prevValue: this.dragInitialIndex,
newValue: targetIndex,
column: event.model
});
}
this.dragInitialIndex = undefined;
this.dragTargetIndex = undefined;
}
getDragTargetIndex({ currentX, currentY, element }) {
const elementsAtPoint = this.document.elementsFromPoint(currentX, currentY);
const index = this.headerCells().findIndex(cell => cell.nativeElement !== element && elementsAtPoint.includes(cell.nativeElement));
return index === -1 ? undefined : index;
}
onSort({ column, prevValue, newValue }) {
// if we are dragging don't sort!
if (column.dragging) {
return;
}
const sorts = this.calcNewSorts(column, prevValue, newValue);
this.sort.emit({
sorts,
column: toPublicColumn(column),
prevValue,
newValue
});
}
calcNewSorts(column, prevValue, newValue) {
let idx = 0;
const sorts = this.sorts().map((s, i) => {
s = { ...s };
if (s.prop === column.prop) {
idx = i;
}
return s;
});
if (newValue === undefined) {
sorts.splice(idx, 1);
}
else if (prevValue) {
sorts[idx].dir = newValue;
}
else {
if (this.sortType() === 'single') {
sorts.splice(0, this.sorts().length);
}
sorts.push({ dir: newValue, prop: column.prop });
}
return sorts;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DataTableHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: DataTableHeaderComponent, isStandalone: true, selector: "datatable-header", inputs: { scrollbarH: { classPropertyName: "scrollbarH", publicName: "scrollbarH", isSignal: true, isRequired: false, transformFunction: null }, dealsWithGroup: { classPropertyName: "dealsWithGroup", publicName: "dealsWithGroup", isSignal: true, isRequired: false, transformFunction: null }, targetMarkerTemplate: { classPropertyName: "targetMarkerTemplate", publicName: "targetMarkerTemplate", isSignal: true, isRequired: false, transformFunction: null }, enableClearingSortState: { classPropertyName: "enableClearingSortState", publicName: "enableClearingSortState", isSignal: true, isRequired: false, transformFunction: null }, sorts: { classPropertyName: "sorts", publicName: "sorts", isSignal: true, isRequired: true, transformFunction: null }, sortType: { classPropertyName: "sortType", publicName: "sortType", isSignal: true, isRequired: true, transformFunction: null }, allRowsSelected: { classPropertyName: "allRowsSelected", publicName: "allRowsSelected", isSignal: true, isRequired: false, transformFunction: null }, selectionType: { classPropertyName: "selectionType", publicName: "selectionType", isSignal: true, isRequired: false, transformFunction: null }, reorderable: { classPropertyName: "reorderable", publicName: "reorderable", isSignal: true, isRequired: false, transformFunction: null }, verticalScrollVisible: { classPropertyName: "verticalScrollVisible", publicName: "verticalScrollVisible", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { sort: "sort", reorder: "reorder", resize: "resize", resizing: "resizing", select: "select", columnContextmenu: "columnContextmenu" }, host: { properties: { "style.height.px": "configuration().headerHeight" }, classAttribute: "datatable-header" }, viewQueries: [{ propertyName: "headerCells", predicate: DataTableHeaderCellComponent, descendants: true, read: ElementRef, isSignal: true }], ngImport: i0, template: `
<div role="row" class="datatable-header-inner">
@for (colGroup of columnGroups(); track colGroup.type) {
@if (colGroup.columns.length) {
<div
class="datatable-row-group"
[class]="'datatable-row-' + colGroup.type"
[style.grid-column]="'span ' + colGroup.columns.length"
>
@for (column of colGroup.columns; track column.$$id) {
<datatable-header-cell
role="columnheader"
dragStartDelay="500"
[datatableDraggable]="reorderable() && column.draggable"
[dragModel]="column"
[isTarget]="targetColumn() === column"
[targetMarkerTemplate]="targetMarkerTemplate()"
[targetMarkerContext]="targetMarkerContext()"
[column]="column"
[showResizeHandle]="lastColumnId() !== column.$$id && column.resizeable"
[sortType]="sortType()"
[sorts]="sorts()"
[selectionType]="selectionType()"
[allRowsSelected]="allRowsSelected()"
[enableClearingSortState]="enableClearingSortState()"
(dragStart)="onDragStart($event)"
(dragMove)="onDragMove($event)"
(dragEnd)="onDragEnd($event)"
(resize)="onColumnResized($event)"
(resizing)="onColumnResizing($event)"
(sort)="onSort($event)"
(select)="select.emit($event)"
(columnContextmenu)="columnContextmenu.emit($event)"
/>
}
</div>
}
}
</div>
`, isInline: true, styles: [":host{display:grid;grid-template-columns:subgrid;grid-column:1/-1;grid-row:1;position:sticky;inset-block-start:0;z-index:11}.datatable-header-inner{display:grid;grid-template-columns:subgrid;grid-column:1/-1}:host-context(ngx-datatable.fixed-header) .datatable-header-inner{white-space:nowrap}.datatable-row-group{display:grid;grid-template-columns:subgrid}.datatable-row-left,.datatable-row-right{position:sticky;z-index:9}.datatable-row-left{inset-inline-start:0}.datatable-row-right{inset-inline-end:0}\n"], dependencies: [{ kind: "component", type: DataTableHeaderCellComponent, selector: "datatable-header-cell", inputs: ["sortType", "isTarget", "showResizeHandle", "targetMarkerTemplate", "targetMarkerContext", "enableClearingSortState", "allRowsSelected", "selectionType", "column", "sorts"], outputs: ["sort", "select", "columnContextmenu", "resize", "resizing"] }, { kind: "directive", type: DatatableDraggableDirective, selector: "[datatableDraggable]", inputs: ["dragModel", "dragStartDelay", "datatableDraggable"], outputs: ["dragMove", "dragEnd", "dragStart"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DataTableHeaderComponent, decorators: [{
type: Component,
args: [{ selector: 'datatable-header', imports: [DataTableHeaderCellComponent, DatatableDraggableDirective], template: `
<div role="row" class="datatable-header-inner">
@for (colGroup of columnGroups(); track colGroup.type) {
@if (colGroup.columns.length) {
<div
class="datatable-row-group"
[class]="'datatable-row-' + colGroup.type"
[style.grid-column]="'span ' + colGroup.columns.length"
>
@for (column of colGroup.columns; track column.$$id) {
<datatable-header-cell
role="columnheader"
dragStartDelay="500"
[datatableDraggable]="reorderable() && column.draggable"
[dragModel]="column"
[isTarget]="targetColumn() === column"
[targetMarkerTemplate]="targetMarkerTemplate()"
[targetMarkerContext]="targetMarkerContext()"
[column]="column"
[showResizeHandle]="lastColumnId() !== column.$$id && column.resizeable"
[sortType]="sortType()"
[sorts]="sorts()"
[selectionType]="selectionType()"
[allRowsSelected]="allRowsSelected()"
[enableClearingSortState]="enableClearingSortState()"
(dragStart)="onDragStart($event)"
(dragMove)="onDragMove($event)"
(dragEnd)="onDragEnd($event)"
(resize)="onColumnResized($event)"
(resizing)="onColumnResizing($event)"
(sort)="onSort($event)"
(select)="select.emit($event)"
(columnContextmenu)="columnContextmenu.emit($event)"
/>
}
</div>
}
}
</div>
`, changeDetection: ChangeDetectionStrategy.OnPush, host: {
class: 'datatable-header',
'[style.height.px]': 'configuration().headerHeight'
}, styles: [":host{display:grid;grid-template-columns:subgrid;grid-column:1/-1;grid-row:1;position:sticky;inset-block-start:0;z-index:11}.datatable-header-inner{display:grid;grid-template-columns:subgrid;grid-column:1/-1}:host-context(ngx-datatable.fixed-header) .datatable-header-inner{white-space:nowrap}.datatable-row-group{display:grid;grid-template-columns:subgrid}.datatable-row-left,.datatable-row-right{position:sticky;z-index:9}.datatable-row-left{inset-inline-start:0}.datatable-row-right{inset-inline-end:0}\n"] }]
}], propDecorators: { headerCells: [{ type: i0.ViewChildren, args: [i0.forwardRef(() => DataTableHeaderCellComponent), { ...{ read: ElementRef }, isSignal: true }] }], scrollbarH: [{ type: i0.Input, args: [{ isSignal: true, alias: "scrollbarH", required: false }] }], dealsWithGroup: [{ type: i0.Input, args: [{ isSignal: true, alias: "dealsWithGroup", required: false }] }], targetMarkerTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "targetMarkerTemplate", required: false }] }], enableClearingSortState: [{ type: i0.Input, args: [{ isSignal: true, alias: "enableClearingSortState", required: false }] }], sorts: [{ type: i0.Input, args: [{ isSignal: true, alias: "sorts", required: true }] }], sortType: [{ type: i0.Input, args: [{ isSignal: true, alias: "sortType", required: true }] }], allRowsSelected: [{ type: i0.Input, args: [{ isSignal: true, alias: "allRowsSelected", required: false }] }], selectionType: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectionType", required: false }] }], reorderable: [{ type: i0.Input, args: [{ isSignal: true, alias: "reorderable", required: false }] }], verticalScrollVisible: [{ type: i0.Input, args: [{ isSignal: true, alias: "verticalScrollVisible", required: false }] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: true }] }], sort: [{ type: i0.Output, args: ["sort"] }], reorder: [{ type: i0.Output, args: ["reorder"] }], resize: [{ type: i0.Output, args: ["resize"] }], resizing: [{ type: i0.Output, args: ["resizing"] }], select: [{ type: i0.Output, args: ["select"] }], columnContextmenu: [{ type: i0.Output, args: ["columnContextmenu"] }] } });
class DatatableRowDetailTemplateDirective {
static ngTemplateContextGuard(directive, context) {
return true;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DatatableRowDetailTemplateDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.8", type: DatatableRowDetailTemplateDirective, isStandalone: true, selector: "[ngx-datatable-row-detail-template]", ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DatatableRowDetailTemplateDirective, decorators: [{
type: Directive,
args: [{
selector: '[ngx-datatable-row-detail-template]'
}]
}] });
class DatatableRowDetailDirective {
/**
* The detail row height is required especially
* when virtual scroll is enabled.
*/
rowHeight = input(0, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "rowHeight" }] : /* istanbul ignore next */ []));
/**
* Template used to render the detail row.
*/
_templateInput = input(undefined, { ...(ngDevMode ? { debugName: "_templateInput" } : /* istanbul ignore next */ {}), alias: 'template' });
_templateQuery = contentChild(DatatableRowDetailTemplateDirective, { ...(ngDevMode ? { debugName: "_templateQuery" } : /* istanbul ignore next */ {}), read: TemplateRef });
template = computed(() => {
return this._templateInput() ?? this._templateQuery();
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "template" }] : /* istanbul ignore next */ []));
/**
* Row detail row visbility was toggled.
*/
toggle = output();
/**
* 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 ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DatatableRowDetailDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "22.0.8", type: DatatableRowDetailDirective, isStandalone: true, selector: "ngx-datatable-row-detail", inputs: { rowHeight: { classPropertyName: "rowHeight", publicName: "rowHeight", 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: DatatableRowDetailTemplateDirective, descendants: true, read: TemplateRef, isSignal: true }], ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DatatableRowDetailDirective, decorators: [{
type: Directive,
args: [{
selector: 'ngx-datatable-row-detail'
}]
}], propDecorators: { rowHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowHeight", required: false }] }], _templateInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "template", required: false }] }], _templateQuery: [{ type: i0.ContentChild, args: [i0.forwardRef(() => DatatableRowDetailTemplateDirective), { ...{
read: TemplateRef
}, isSignal: true }] }], toggle: [{ type: i0.Output, args: ["toggle"] }] } });
class DatatableComponent {
scrollbarHelper = inject(ScrollbarHelper);
cd = inject(ChangeDetectorRef);
element = inject(ElementRef).nativeElement;
rowDiffer = inject(IterableDiffers).find([]).create();
globalConfiguration = inject(NGX_DATATABLE_CONFIG, { optional: true }) ??
// This is the old injection token for backward compatibility.
inject('configuration', { optional: true }) ??
{};
datatableConfiguration = new DatatableConfiguration(this, this.globalConfiguration);
configuration = this.datatableConfiguration.configuration;
/**
* Template for the target marker of drag target columns.
*/
targetMarkerTemplate = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "targetMarkerTemplate" }] : /* istanbul ignore next */ []));
/**
* Rows that are displayed in the table.
*/
rows = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "rows" }] : /* istanbul ignore next */ []));
/**
* This attribute allows the user to set the name of the column to group the data with
*/
groupRowsBy = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "groupRowsBy" }] : /* istanbul ignore next */ []));
/**
* This attribute allows the user to set a grouped array in the following format:
* [
* {groupid=1} [
* {id=1 name="test1"},
* {id=2 name="test2"},
* {id=3 name="test3"}
* ]},
* {groupid=2>[
* {id=4 name="test4"},
* {id=5 name="test5"},
* {id=6 name="test6"}
* ]}
* ]
*/
groupedRows = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "groupedRows" }] : /* istanbul ignore next */ []));
/**
* Columns to be displayed.
*/
columns = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "columns" }] : /* istanbul ignore next */ []));
/**
* List of row objects that should be
* represented as selected in the grid.
* Default value: `[]`
*/
selected = model([], /* @ts-ignore */
...(ngDevMode ? [{ debugName: "selected" }] : /* istanbul ignore next */ []));
/**
* Enable vertical scrollbars
*/
scrollbarV = input(false, { ...(ngDevMode ? { debugName: "scrollbarV" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
/**
* Enable vertical scrollbars dynamically on demand.
* Property `scrollbarV` needs to be set `true` too.
* Width that is gained when no scrollbar is needed
* is added to the inner table width.
*/
scrollbarVDynamic = input(false, { ...(ngDevMode ? { debugName: "scrollbarVDynamic" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
/**
* Enable horz scrollbars
*/
scrollbarH = input(false, { ...(ngDevMode ? { debugName: "scrollbarH" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
/**
* Height of each row. When virtual scrolling is disabled, use `'auto'` for
* fluid heights. With virtual scrolling enabled, provide a number or a
* function that calculates each row's height.
*/
rowHeight = input(this.globalConfiguration.rowHeight ?? 30, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "rowHeight" }] : /* istanbul ignore next */ []));
/**
* Column width distribution mode. `standard` uses configured widths, `flex`
* uses the flex-grow algorithm, and `force` distributes space proportionally.
*/
columnMode = input('standard', /* @ts-ignore */
...(ngDevMode ? [{ debugName: "columnMode" }] : /* istanbul ignore next */ []));
/**
* The minimum header height in pixels.
* Pass a falsey for no header
*/
headerHeight = input(this.globalConfiguration.headerHeight ?? 30, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "headerHeight" }] : /* istanbul ignore next */ []));
/**
* The minimum footer height in pixels.
* Pass falsey for no footer
*/
footerHeight = input(this.globalConfiguration.footerHeight ?? 0, { ...(ngDevMode ? { debugName: "footerHeight" } : /* istanbul ignore next */ {}), transform: numberAttribute });
/**
* If the table should use external paging
* otherwise its assumed that all data is preloaded.
*/
externalPaging = input(false, { ...(ngDevMode ? { debugName: "externalPaging" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
/**
* If the table should use external sorting or
* the built-in basic sorting.
*/
externalSorting = input(false, { ...(ngDevMode ? { debugName: "externalSorting" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
/**
* The page size to be shown.
* Default value: `undefined`
*/
limit = input(undefined, { ...(ngDevMode ? { debugName: "limit" } : /* istanbul ignore next */ {}), transform: numberOrUndefinedAttribute });
/**
* The total count of all rows.
* Default value: `0`
*/
count = input(0, { ...(ngDevMode ? { debugName: "count" } : /* istanbul ignore next */ {}), transform: numberAttribute });
/**
* The current offset ( page - 1 ) shown.
* Default value: `0`
*/
offset = model(0, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "offset" }] : /* istanbul ignore next */ []));
/**
* Show the linear loading bar.
* Default value: `false`
*/
loadingIndicator = input(false, { ...(ngDevMode ? { debugName: "loadingIndicator" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
/**
* Show ghost loaders on each cell.
* Default value: `false`
*/
ghostLoadingIndicator = input(false, { ...(ngDevMode ? { debugName: "ghostLoadingIndicator" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
/**
* Type of row selection. Options are:
*
* - `single`
* - `multi`
* - `checkbox`
* - `multiClick`
* - `cell`
*
* For no selection pass a `falsey`.
* Default value: `undefined`
*/
selectionType = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "selectionType" }] : /* istanbul ignore next */ []));
/**
* Enable/Disable ability to re-order columns
* by dragging them.
*/
reorderable = input(true, { ...(ngDevMode ? { debugName: "reorderable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
/**
* Swap columns on re-order columns or
* move them.
*/
swapColumns = input(true, { ...(ngDevMode ? { debugName: "swapColumns" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
/**
* Sorting mode. In `single` mode, sorting a new column replaces existing
* sorts; in `multi` mode, it adds an additional column sort.
*/
sortType = input('single', /* @ts-ignore */
...(ngDevMode ? [{ debugName: "sortType" }] : /* istanbul ignore next */ []));
/**
* Array of sorted columns by property and type.
* Default value: `[]`
*/
sorts = model([], /* @ts-ignore */
...(ngDevMode ? [{ debugName: "sorts" }] : /* istanbul ignore next */ []));
/**
* CSS class overrides for sort and pager icons.
*/
cssClasses = input({}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "cssClasses" }] : /* istanbul ignore next */ []));
/**
* Message overrides for localization
*
* @defaultValue
* ```
* {
* emptyMessage: 'No data to display',
* totalMessage: 'total',
* selectedMessage: 'selected',
* ariaFirstPageMessage: 'go to first page',
* ariaPreviousPageMessage: 'go to previous page',
* ariaPageNMessage: 'page',
* ariaNextPageMessage: 'go to next page',
* ariaLastPageMessage: 'go to last page',
* ariaRowCheckboxMessage: 'Select row',
* ariaHeaderCheckboxMessage: 'Select all rows',
* ariaGroupHeaderCheckboxMessage: 'Select row group',
* ariaLoadingMessage: 'Loading'
* }
* ```
*/
messages = input({}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "messages" }] : /* istanbul ignore next */ []));
/**
* A function which is called with the row and should return either:
* - a string: `"class-1 class-2`
* - a Record<string, boolean>: `{ 'class-1': true, 'class-2': false }`
*/
rowClass = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "rowClass" }] : /* istanbul ignore next */ []));
/**
* A boolean/function you can use to check whether you want
* to select a particular row based on a criteria. Example:
*
* (selection) => {
* return selection !== 'Ethel Price';
* }
*/
selectCheck = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "selectCheck" }] : /* istanbul ignore next */ []));
/**
* A function you can use to check whether you want
* to show the checkbox for a particular row based on a criteria. Example:
*
* (row, column, value) => {
* return row.name !== 'Ethel Price';
* }
*/
displayCheck = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "displayCheck" }] : /* istanbul ignore next */ []));
/**
* A boolean you can use to set the detault behaviour of rows and groups
* whether they will start expanded or not. If ommited the default is NOT expanded.
*
*/
groupExpansionDefault = input(false, { ...(ngDevMode ? { debugName: "groupExpansionDefault" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
/**
* Property to which you can use for custom tracking of rows.
* Example: 'name'
*/
trackByProp = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "trackByProp" }] : /* istanbul ignore next */ []));
/**
* Property to which you can use for determining select all
* rows on current page or not.
*/
selectAllRowsOnPage = input(false, { ...(ngDevMode ? { debugName: "selectAllRowsOnPage" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
/**
* A flag for row virtualization on / off
*/
virtualization = input(true, { ...(ngDevMode ? { debugName: "virtualization" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
/**
* Tree from relation
*/
treeFromRelation = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "treeFromRelation" }] : /* istanbul ignore next */ []));
/**
* Tree to relation
*/
treeToRelation = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "treeToRelation" }] : /* istanbul ignore next */ []));
/**
* A flag for switching summary row on / off
*/
summaryRow = input(false, { ...(ngDevMode ? { debugName: "summaryRow" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
/**
* A height of summary row
*/
summaryHeight = input(30, { ...(ngDevMode ? { debugName: "summaryHeight" } : /* istanbul ignore next */ {}), transform: numberAttribute });
/**
* A property holds a summary row position: top/bottom
*/
summaryPosition = input('top', /* @ts-ignore */
...(ngDevMode ? [{ debugName: "summaryPosition" }] : /* istanbul ignore next */ []));
/**
* A function you can use to check whether you want
* to disable a row. Example:
*
* (row) => {
* return row.name !== 'Ethel Price';
* }
*/
disableRowCheck = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "disableRowCheck" }] : /* istanbul ignore next */ []));
/**
* A flag to enable drag behavior of native HTML5 drag and drop API on rows.
* If set to true, {@link rowDragEvents} will emit dragstart and dragend events.
*/
rowDraggable = input(false, { ...(ngDevMode ? { debugName: "rowDraggable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
/**
* A flag to controll behavior of sort states.
* By default sort on column toggles between ascending and descending without getting removed.
* Set true to clear sorting of column after performing ascending and descending sort on that column.
*/
enableClearingSortState = input(false, { ...(ngDevMode ? { debugName: "enableClearingSortState" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
/**
* Controls whether the datatable runs an {@link IterableDiffer} against the
* `rows` input on every change detection cycle to detect additions, removals
* and reorderings that happen in-place on the same array reference.
*
* Enabled by default. Set to `false` as a performance optimization when you
* always pass a new `rows` array reference on updates.
*/
checkRowListChanges = input(true, { ...(ngDevMode ? { debugName: "checkRowListChanges" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
/**
* Controls whether each rendered row runs a {@link KeyValueDiffer} against
* its row object on every change detection cycle to detect in-place property
* mutations (e.g. `row.name = 'new'` without replacing the row reference).
*
* Enabled by default. Set to `false` as a performance optimization when row
* objects are treated as immutable.
*/
checkRowPropertyChanges = input(true, { ...(ngDevMode ? { debugName: "checkRowPropertyChanges" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
/**
* Body was scrolled typically in a `scrollbarV:true` scenario.
*/
scroll = output();
/**
* A cell or row was focused via keyboard or mouse click.
*/
activate = output();
/**
* The table was paged either triggered by the pager or the body scroll.
*/
page = output();
/**
* Columns were re-ordered.
*/
reorder = output();
/**
* Column was resized.
*/
resize = output();
/**
* The context menu was invoked on the table.
* type indicates whether the header or the body was clicked.
* content contains either the column or the row that was clicked.
*/
tableContextmenu = output();
/**
* A row was expanded ot collapsed for tree
*/
treeAction = output();
/**
* Emits HTML5 native drag events.
* Only emits dragenter, dragover, drop events by default.
* Set {@link rowDraggable} to true for dragstart and dragend.
*/
rowDragEvents = output();
/**
* Column templates gathered from `ContentChildren`
* if described in your markup.
*/
columnTemplates = contentChildren(DataTableColumnDirective, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "columnTemplates" }] : /* istanbul ignore next */ []));
/**
* Row Detail templates gathered from the ContentChild
*/
rowDetail;
/**
* Group Header templates gathered from the ContentChild
*/
groupHeader;
/**
* Custom summary row template gathered from the ContentChild
*/
summaryRowDirective = contentChild(DatatableSummaryRowDirective, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "summaryRowDirective" }] : /* istanbul ignore next */ []));
/**
* Footer template gathered from the ContentChild
* @internal
*/
_footer = contentChild(DatatableFooterDirective, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "_footer" }] : /* istanbul ignore next */ []));
_bodyComponent = viewChild.required(DataTableBodyComponent, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "_bodyComponent" }] : /* istanbul ignore next */ []));
_headerElement = viewChild(DataTableHeaderComponent, { ...(ngDevMode ? { debugName: "_headerElement" } : /* istanbul ignore next */ {}), read: (ElementRef) });
/**
* The `role="table"` element. This is the css-grid that owns both the
* horizontal and vertical scroll (Option A: single scroll container).
*/
_scrollContainer = viewChild.required(ScrollContainerDirective, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "_scrollContainer" }] : /* istanbul ignore next */ []));
/** @internal */
_rowDefTemplate = contentChild(DatatableRowDefDirective, { ...(ngDevMode ? { debugName: "_rowDefTemplate" } : /* istanbul ignore next */ {}), read: TemplateRef });
/**
* Returns if all rows are selected.
*/
allRowsSelected = computed(() => {
const selected = this.selected();
let allRowsSelected = selected.length === this.rows()?.length;
if (this.selectAllRowsOnPage()) {
const { first, last } = this._bodyComponent().indexes();
const rowsOnPage = last - first;
allRowsSelected = selected.length === rowsOnPage;
}
return !!(selected && this.rows()?.length !== 0 && allRowsSelected);
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "allRowsSelected" }] : /* istanbul ignore next */ []));
_innerWidth = computed(() => this.dimensions().width, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "_innerWidth" }] : /* istanbul ignore next */ []));
pageSize = computed(() => this.calcPageSize(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "pageSize" }] : /* istanbul ignore next */ []));
viewportRowCount = computed(() => {
const size = Math.ceil(this.bodyHeight() / this.rowHeight());
return Math.max(size, 0);
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "viewportRowCount" }] : /* istanbul ignore next */ []));
_isFixedHeader = computed(() => {
const headerHeight = this.headerHeight();
return typeof headerHeight === 'string' ? headerHeight !== 'auto' : true;
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "_isFixedHeader" }] : /* istanbul ignore next */ []));
bodyHeight = computed(() => {
if (this.scrollbarV()) {
let height = this.dimensions().height;
const headerElement = this._headerElement();
if (headerElement) {
height = height - headerElement.nativeElement.getBoundingClientRect().height;
}
return height - this.footerHeight();
}
return 0;
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "bodyHeight" }] : /* istanbul ignore next */ []));
rowCount = computed(() => this.calcRowCount(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "rowCount" }] : /* istanbul ignore next */ []));
/** This counter is increased, when the rowDiffer detects a change. This will cause an update of _internalRows. */
_rowDiffCount = signal(0, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "_rowDiffCount" }] : /* istanbul ignore next */ []));
_offsetX = 0;
_internalRows = computed(() => {
this._rowDiffCount(); // to trigger recalculation when row differ detects a change
let rows = this.rows()?.slice() ?? [];
const sorts = this.sorts();
if (sorts.length && !this.externalSorting()) {
rows = sortRows(rows, this._internalColumns(), this.sorts());
}
if (this.treeFromRelation() && this.treeToRelation()) {
rows = groupRowsByParents(rows, optionalGetterForProp(this.treeFromRelation()), optionalGetterForProp(this.treeToRelation()));
}
if (this.ghostLoadingIndicator() && this.scrollbarV() && !this.externalPaging()) {
const ghostRowCount = Math.max(this.viewportRowCount() - rows.length, 1);
for (let i = 0; i < ghostRowCount; i++) {
rows.push(undefined);
}
}
return rows;
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "_internalRows" }] : /* istanbul ignore next */ []));
_internalGroupedRows = computed(() => {
let groupedRows = this.groupedRows();
const groupRowsBy = this.groupRowsBy();
if (!groupedRows && groupRowsBy) {
this._rowDiffCount(); // to trigger recalculation when row differ detects a change
groupedRows = this.groupArrayBy(this.rows() ?? [], groupRowsBy);
}
if (!groupedRows) {
// return here to prevent subscription to sorts when no grouping
return undefined;
}
const sorts = this.sorts();
if (sorts.length && !this.externalSorting()) {
if (groupedRows?.length) {
groupedRows = sortGroupedRows(groupedRows, this._internalColumns(), sorts, sorts.find(sortColumns => sortColumns.prop === groupRowsBy));
}
}
return groupedRows;
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "_internalGroupedRows" }] : /* istanbul ignore next */ []));
// TODO: consider removing internal modifications of the columns.
// This requires a different strategy for certain properties like width.
_internalColumns = linkedSignal(() => toInternalColumn(this.columnTemplates().length
? this.columnTemplates().map(c => c.column())
: (this.columns() ?? []), this._defaultColumnWidth), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "_internalColumns" }] : /* istanbul ignore next */ []));
/**
* The shared `grid-template-columns` definition for the combined css-grid.
* It is exposed once as a custom property on the scroll container so the
* header and every body row align to the same column tracks via `var()`,
* instead of each row binding its own (identical) template string.
*/
_gridTemplateColumns = computed(() => gridColumnTemplate(columnsByPinArr(this._internalColumns())), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "_gridTemplateColumns" }] : /* istanbul ignore next */ []));
/**
* Computed signal that returns the corrected offset value.
* It ensures the offset is within valid bounds based on rowCount and pageSize.
*/
correctedOffset = computed(() => {
const offset = this.offset();
const rowCount = this.rowCount();
const pageSize = this.pageSize();
return Math.max(Math.min(offset, Math.ceil(rowCount / pageSize) - 1), 0);
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "correctedOffset" }] : /* istanbul ignore next */ []));
totalColumnGroupWidths = computed(() => {
const colsByPin = columnsByPin(this._internalColumns());
return columnGroupWidths(colsByPin, this._internalColumns()).total;
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "totalColumnGroupWidths" }] : /* istanbul ignore next */ []));
_subscriptions = [];
_defaultColumnWidth = this.globalConfiguration.defaultColumnWidth ?? 150;
/**
* To have this available for all components.
* The Footer itself is not available in the injection context in templates,
* so we need to get if from here until we have a state service.
*/
_footerComponent = viewChild(DataTableFooterComponent, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "_footerComponent" }] : /* istanbul ignore next */ []));
verticalScrollVisible = false;
dimensions = signal({ height: 0, width: 0 }, { ...(ngDevMode ? { debugName: "dimensions" } : /* istanbul ignore next */ {}), equal: (a, b) => a.width === b.width && a.height === b.height });
/** Re-measures the table whenever the host element's size changes. */
resizeObserver;
/** Pending debounce timer for the {@link resizeObserver} callback. */
resizeDebounce;
constructor() {
effect(() => this.recalculateColumns());
afterNextRender(() => {
this.resizeObserver = new ResizeObserver(entries => {
const borderBox = entries[entries.length - 1]?.borderBoxSize?.[0];
if (!borderBox) {
return;
}
clearTimeout(this.resizeDebounce);
this.resizeDebounce = setTimeout(() => {
this.dimensions.set({ width: borderBox.inlineSize, height: borderBox.blockSize });
}, 5);
});
this.resizeObserver.observe(this.element);
this.dimensions.set(this.element.getBoundingClientRect());
});
}
/*
* Lifecycle hook that is called when Angular dirty checks a directive.
*/
ngDoCheck() {
const rowDiffers = this.checkRowListChanges() ? this.rowDiffer.diff(this.rows()) : null;
if (rowDiffers || this.disableRowCheck()) {
this._rowDiffCount.update(count => count + 1);
this.cd.markForCheck();
}
}
/**
* Lifecycle hook that is called after a component's
* view has been fully initialized.
*/
ngAfterViewInit() {
// emit page for virtual server-side kickoff
if (this.externalPaging() && this.scrollbarV()) {
queueMicrotask(() => this.page.emit({
count: this.count(),
pageSize: this.pageSize(),
limit: this.limit(),
offset: 0,
sorts: this.sorts()
}));
}
}
/**
* This will be used when displaying or selecting rows.
* when tracking/comparing them, we'll use the value of this fn,
*
* (`fn(x) === fn(y)` instead of `x === y`)
*/
rowIdentity = input(x => {
if (this.groupRowsBy()) {
// each group in groupedRows are stored as {key, value: [rows]},
// where key is the groupRowsBy index
return x.key ?? x;
}
else {
return x;
}
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "rowIdentity" }] : /* istanbul ignore next */ []));
/**
* Creates a map with the data grouped by the user choice of grouping index
*
* @param originalArray the original array passed via parameter
* @param groupBy the key of the column to group the data by
*/
groupArrayBy(originalArray, groupBy) {
// create a map to hold groups with their corresponding results
const map = new Map();
let i = 0;
originalArray.forEach(item => {
if (!item) {
// skip undefined items
return;
}
const key = item[groupBy];
const value = map.get(key);
if (!value) {
map.set(key, [item]);
}
else {
value.push(item);
}
i++;
});
const addGroup = (key, value) => ({ key, value });
// convert map back to a simple array of objects
return Array.from(map, x => addGroup(x[0], x[1]));
}
/**
* @deprecated No-op. Dimensions are kept current by a `ResizeObserver` on the
* host element; this will be removed in a future release.
*/
recalculate() { }
/**
* @deprecated No-op. The `window:resize` listener was replaced by a
* `ResizeObserver`; this will be removed in a future release.
*/
onWindowResize() { }
/**
* Recalulcates the column widths based on column width
* distribution mode and scrollbar offsets.
*/
recalculateColumns(forceIdx = -1, allowBleed = this.scrollbarH()) {
let width = this._innerWidth();
const columns = this._internalColumns();
if (!width) {
return [];
}
this.verticalScrollVisible = this._scrollContainer().verticalScrollVisible;
if (this.scrollbarV() || this.scrollbarVDynamic()) {
width = width - (this.verticalScrollVisible ? this.scrollbarHelper.width : 0);
}
// TODO: this is a temporary workaround to avoid signal writes in a computed.
// Later, a computed adjustedWidth has to be added to the internal column to avoid this.
untracked(() => {
if (this.columnMode() === 'force') {
forceFillColumnWidths(columns, width, forceIdx, allowBleed, this._defaultColumnWidth, this.scrollbarHelper.width);
}
else if (this.columnMode() === 'flex') {
adjustColumnWidths(columns, width);
}
});
return columns;
}
/**
* @deprecated No-op. Dimensions are kept current by a `ResizeObserver` on the
* host element; this will be removed in a future release.
*/
recalculateDims() { }
/**
* Body triggered a page event.
*/
onBodyPage(offset) {
// Avoid pagination caming from body events like scroll when the table
// has no virtualization and the external paging is enable.
// This means, let's the developer handle pagination by my him(her) self
if (this.externalPaging() && !this.virtualization()) {
return;
}
this.offset.set(offset);
if (!isNaN(this.correctedOffset())) {
this.page.emit({
count: this.count(),
pageSize: this.pageSize(),
limit: this.limit(),
offset: this.correctedOffset(),
sorts: this.sorts()
});
}
}
/**
* The body triggered a scroll event.
*/
onBodyScroll(event) {
this._offsetX = event.offsetX;
this.scroll.emit(event);
}
/**
* The footer triggered a page event.
*/
onFooterPage(event) {
this.offset.set(event.page - 1);
this._bodyComponent().updateOffsetY(this.correctedOffset());
this.page.emit({
count: this.count(),
pageSize: this.pageSize(),
limit: this.limit(),
offset: this.correctedOffset(),
sorts: this.sorts()
});
if (this.selectAllRowsOnPage()) {
this.selected.set([]);
}
}
/**
* Recalculates the sizes of the page
*/
calcPageSize() {
if (this.scrollbarV() && this.virtualization()) {
return this.viewportRowCount();
}
// if limit is passed, we are paging
const limit = this.limit();
if (limit !== undefined) {
return limit;
}
// otherwise use row length
return this._internalRows().length;
}
/**
* Calculates the row count.
*/
calcRowCount() {
if (!this.externalPaging()) {
const groupedRows = this._internalGroupedRows();
if (groupedRows) {
return groupedRows.length;
}
else {
return this._internalRows().length;
}
}
return this.count();
}
/**
* The header triggered a contextmenu event.
*/
onColumnContextmenu({ event, column }) {
this.tableContextmenu.emit({
event,
type: 'header',
content: toPublicColumn(column)
});
}
/**
* The body triggered a contextmenu event.
*/
onRowContextmenu({ event, row }) {
this.tableContextmenu.emit({ event, type: 'body', content: row });
}
/**
* The header triggered a column resize event.
*/
onColumnResize({ column, newValue, prevValue }) {
/* Safari/iOS 10.2 workaround */
if (column === undefined) {
return;
}
const idx = this._internalColumns().indexOf(column);
const cols = this._internalColumns();
cols[idx].width.set(newValue);
// set this so we can force the column
// width distribution to be to this value
cols[idx].$$oldWidth = newValue;
this.recalculateColumns(idx);
this.resize.emit({
column: toPublicColumn(column),
newValue,
prevValue
});
}
onColumnResizing({ column, newValue }) {
if (column === undefined) {
return;
}
column.width.set(newValue);
column.$$oldWidth = newValue;
const idx = this._internalColumns().indexOf(column);
this.recalculateColumns(idx);
}
/**
* The header triggered a column re-order event.
*/
onColumnReorder(event) {
const { column, newValue, prevValue } = event;
const cols = this._internalColumns().map(c => ({ ...c }));
const prevCol = cols[newValue];
if (column.frozenLeft !== prevCol.frozenLeft || column.frozenRight !== prevCol.frozenRight) {
return;
}
if (this.swapColumns()) {
cols[newValue] = column;
cols[prevValue] = prevCol;
}
else {
if (newValue > prevValue) {
const movedCol = cols[prevValue];
for (let i = prevValue; i < newValue; i++) {
cols[i] = cols[i + 1];
}
cols[newValue] = movedCol;
}
else {
const movedCol = cols[prevValue];
for (let i = prevValue; i > newValue; i--) {
cols[i] = cols[i - 1];
}
cols[newValue] = movedCol;
}
}
this._internalColumns.set(cols);
this.reorder.emit({ ...event, column: toPublicColumn(event.column) });
}
/**
* The header triggered a column sort event.
*/
onColumnSort(event) {
// clean selected rows
if (this.selectAllRowsOnPage()) {
this.selected.set([]);
}
this.sorts.set(event.sorts);
// Always go to first page when sorting to see the newly sorted data
this.offset.set(0);
this._bodyComponent().updateOffsetY(this.correctedOffset());
// Emit the page object with updated offset value
this.page.emit({
count: this.count(),
pageSize: this.pageSize(),
limit: this.limit(),
offset: this.correctedOffset(),
sorts: this.sorts()
});
}
/**
* Toggle all row selection
*/
onHeaderSelect() {
if (this.selectAllRowsOnPage()) {
// before we splice, chk if we currently have all selected
const { first, last } = this._bodyComponent().indexes();
const allSelected = this.selected().length === last - first;
// do the opposite here
if (!allSelected) {
this.selected.set(this._internalRows()
.slice(first, last)
.filter(row => !!row));
}
else {
this.selected.set([]);
}
}
else {
let relevantRows;
const disableRowCheckFn = this.disableRowCheck();
if (disableRowCheckFn) {
relevantRows = (this.rows() ?? []).filter((row => row && !disableRowCheckFn(row)));
}
else {
relevantRows = (this.rows() ?? []).filter(row => !!row);
}
// before we splice, chk if we currently have all selected
const allSelected = this.selected().length === relevantRows.length;
// do the opposite here
if (!allSelected) {
this.selected.set(relevantRows);
}
else {
this.selected.set([]);
}
}
}
/**
* A row was expanded or collapsed for tree
*/
onTreeAction(event) {
const row = event.row;
// TODO: For duplicated items this will not work
const treeToRel = this.treeToRelation();
const rowIndex = (this.rows() ?? []).findIndex(r => r && r[treeToRel] === event.row[treeToRel]);
this._rowDiffCount.update(v => v + 1);
this.treeAction.emit({ row, rowIndex });
}
ngOnDestroy() {
this.resizeObserver?.disconnect();
clearTimeout(this.resizeDebounce);
this._subscriptions.forEach(subscription => subscription.unsubscribe());
}
scrollToRow(row, options) {
if (!this.scrollbarV()) {
throw new Error('Vertical scrolling is not enabled.');
}
// TODO: We could / should add support for all those cases below.
if (this._internalGroupedRows()?.length) {
throw new Error('Scrolling is not supported with grouped rows.');
}
if (this.limit()) {
throw new Error('Scrolling is not supported with limit');
}
if (this.treeFromRelation() && this.treeToRelation()) {
this.scrollToRowTree(row, options);
return;
}
const index = this._internalRows().indexOf(row);
if (index === -1) {
throw new Error(`Row not found: ${row}`);
}
// Here we have ensured, that we have only one page and the row exists.
// Now we just need to scroll to that row.
this._bodyComponent().scrollToIndex(index, options);
}
scrollToRowTree(row, options, afterExpand = false) {
const index = this._internalRows().indexOf(row);
if (index !== -1) {
this._bodyComponent().scrollToIndex(index, options);
return;
}
if (afterExpand) {
throw new Error(`Row not found: ${row}`);
}
expandToRow(row, this.rows() ?? [], optionalGetterForProp(this.treeFromRelation()), optionalGetterForProp(this.treeToRelation()));
this._rowDiffCount.update(v => v + 1);
// We need a setTimeout to wait until the DOM was updated
setTimeout(() => this.scrollToRowTree(row, options, true));
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DatatableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: DatatableComponent, isStandalone: true, selector: "ngx-datatable", inputs: { targetMarkerTemplate: { classPropertyName: "targetMarkerTemplate", publicName: "targetMarkerTemplate", isSignal: true, isRequired: false, transformFunction: null }, rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: false, transformFunction: null }, groupRowsBy: { classPropertyName: "groupRowsBy", publicName: "groupRowsBy", isSignal: true, isRequired: false, transformFunction: null }, groupedRows: { classPropertyName: "groupedRows", publicName: "groupedRows", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, selected: { classPropertyName: "selected", publicName: "selected", isSignal: true, isRequired: false, transformFunction: null }, scrollbarV: { classPropertyName: "scrollbarV", publicName: "scrollbarV", isSignal: true, isRequired: false, transformFunction: null }, scrollbarVDynamic: { classPropertyName: "scrollbarVDynamic", publicName: "scrollbarVDynamic", isSignal: true, isRequired: false, transformFunction: null }, scrollbarH: { classPropertyName: "scrollbarH", publicName: "scrollbarH", isSignal: true, isRequired: false, transformFunction: null }, rowHeight: { classPropertyName: "rowHeight", publicName: "rowHeight", isSignal: true, isRequired: false, transformFunction: null }, columnMode: { classPropertyName: "columnMode", publicName: "columnMode", isSignal: true, isRequired: false, transformFunction: null }, headerHeight: { classPropertyName: "headerHeight", publicName: "headerHeight", isSignal: true, isRequired: false, transformFunction: null }, footerHeight: { classPropertyName: "footerHeight", publicName: "footerHeight", isSignal: true, isRequired: false, transformFunction: null }, externalPaging: { classPropertyName: "externalPaging", publicName: "externalPaging", isSignal: true, isRequired: false, transformFunction: null }, externalSorting: { classPropertyName: "externalSorting", publicName: "externalSorting", isSignal: true, isRequired: false, transformFunction: null }, limit: { classPropertyName: "limit", publicName: "limit", isSignal: true, isRequired: false, transformFunction: null }, count: { classPropertyName: "count", publicName: "count", isSignal: true, isRequired: false, transformFunction: null }, offset: { classPropertyName: "offset", publicName: "offset", isSignal: true, isRequired: false, transformFunction: null }, loadingIndicator: { classPropertyName: "loadingIndicator", publicName: "loadingIndicator", isSignal: true, isRequired: false, transformFunction: null }, ghostLoadingIndicator: { classPropertyName: "ghostLoadingIndicator", publicName: "ghostLoadingIndicator", isSignal: true, isRequired: false, transformFunction: null }, selectionType: { classPropertyName: "selectionType", publicName: "selectionType", isSignal: true, isRequired: false, transformFunction: null }, reorderable: { classPropertyName: "reorderable", publicName: "reorderable", isSignal: true, isRequired: false, transformFunction: null }, swapColumns: { classPropertyName: "swapColumns", publicName: "swapColumns", isSignal: true, isRequired: false, transformFunction: null }, sortType: { classPropertyName: "sortType", publicName: "sortType", isSignal: true, isRequired: false, transformFunction: null }, sorts: { classPropertyName: "sorts", publicName: "sorts", isSignal: true, isRequired: false, transformFunction: null }, cssClasses: { classPropertyName: "cssClasses", publicName: "cssClasses", isSignal: true, isRequired: false, transformFunction: null }, messages: { classPropertyName: "messages", publicName: "messages", isSignal: true, isRequired: false, transformFunction: null }, rowClass: { classPropertyName: "rowClass", publicName: "rowClass", isSignal: true, isRequired: false, transformFunction: null }, selectCheck: { classPropertyName: "selectCheck", publicName: "selectCheck", isSignal: true, isRequired: false, transformFunction: null }, displayCheck: { classPropertyName: "displayCheck", publicName: "displayCheck", isSignal: true, isRequired: false, transformFunction: null }, groupExpansionDefault: { classPropertyName: "groupExpansionDefault", publicName: "groupExpansionDefault", isSignal: true, isRequired: false, transformFunction: null }, trackByProp: { classPropertyName: "trackByProp", publicName: "trackByProp", isSignal: true, isRequired: false, transformFunction: null }, selectAllRowsOnPage: { classPropertyName: "selectAllRowsOnPage", publicName: "selectAllRowsOnPage", isSignal: true, isRequired: false, transformFunction: null }, virtualization: { classPropertyName: "virtualization", publicName: "virtualization", isSignal: true, isRequired: false, transformFunction: null }, treeFromRelation: { classPropertyName: "treeFromRelation", publicName: "treeFromRelation", isSignal: true, isRequired: false, transformFunction: null }, treeToRelation: { classPropertyName: "treeToRelation", publicName: "treeToRelation", isSignal: true, isRequired: false, transformFunction: null }, summaryRow: { classPropertyName: "summaryRow", publicName: "summaryRow", isSignal: true, isRequired: false, transformFunction: null }, summaryHeight: { classPropertyName: "summaryHeight", publicName: "summaryHeight", isSignal: true, isRequired: false, transformFunction: null }, summaryPosition: { classPropertyName: "summaryPosition", publicName: "summaryPosition", isSignal: true, isRequired: false, transformFunction: null }, disableRowCheck: { classPropertyName: "disableRowCheck", publicName: "disableRowCheck", isSignal: true, isRequired: false, transformFunction: null }, rowDraggable: { classPropertyName: "rowDraggable", publicName: "rowDraggable", isSignal: true, isRequired: false, transformFunction: null }, enableClearingSortState: { classPropertyName: "enableClearingSortState", publicName: "enableClearingSortState", isSignal: true, isRequired: false, transformFunction: null }, checkRowListChanges: { classPropertyName: "checkRowListChanges", publicName: "checkRowListChanges", isSignal: true, isRequired: false, transformFunction: null }, checkRowPropertyChanges: { classPropertyName: "checkRowPropertyChanges", publicName: "checkRowPropertyChanges", isSignal: true, isRequired: false, transformFunction: null }, rowIdentity: { classPropertyName: "rowIdentity", publicName: "rowIdentity", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selected: "selectedChange", offset: "offsetChange", sorts: "sortsChange", scroll: "scroll", activate: "activate", page: "page", reorder: "reorder", resize: "resize", tableContextmenu: "tableContextmenu", treeAction: "treeAction", rowDragEvents: "rowDragEvents" }, host: { properties: { "class.fixed-header": "_isFixedHeader()", "class.fixed-row": "rowHeight() !== \"auto\"", "class.scroll-vertical": "scrollbarV()", "class.virtualized": "virtualization()", "class.scroll-horz": "scrollbarH()", "class.selectable": "selectionType() !== undefined", "class.checkbox-selection": "selectionType() === \"checkbox\"", "class.cell-selection": "selectionType() === \"cell\"", "class.single-selection": "selectionType() === \"single\"", "class.multi-selection": "selectionType() === \"multi\"", "class.multi-click-selection": "selectionType() === \"multiClick\"", "class.horizontal-overflow": "_innerWidth() < totalColumnGroupWidths()" }, classAttribute: "ngx-datatable" }, providers: [
{
provide: DATATABLE_COMPONENT_TOKEN,
useExisting: DatatableComponent
},
{
provide: DatatableConfiguration,
useFactory: () => inject(DatatableComponent).datatableConfiguration
}
], queries: [{ propertyName: "columnTemplates", predicate: DataTableColumnDirective, isSignal: true }, { propertyName: "summaryRowDirective", first: true, predicate: DatatableSummaryRowDirective, descendants: true, isSignal: true }, { propertyName: "_footer", first: true, predicate: DatatableFooterDirective, descendants: true, isSignal: true }, { propertyName: "_rowDefTemplate", first: true, predicate: DatatableRowDefDirective, descendants: true, read: TemplateRef, isSignal: true }, { propertyName: "rowDetail", first: true, predicate: DatatableRowDetailDirective, descendants: true }, { propertyName: "groupHeader", first: true, predicate: DatatableGroupHeaderDirective, descendants: true }], viewQueries: [{ propertyName: "_bodyComponent", first: true, predicate: DataTableBodyComponent, descendants: true, isSignal: true }, { propertyName: "_headerElement", first: true, predicate: DataTableHeaderComponent, descendants: true, read: ElementRef, isSignal: true }, { propertyName: "_scrollContainer", first: true, predicate: ScrollContainerDirective, descendants: true, isSignal: true }, { propertyName: "_footerComponent", first: true, predicate: DataTableFooterComponent, descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"datatable-wrapper\">\n <div\n datatableScrollContainer\n role=\"table\"\n class=\"datatable-grid\"\n [style.--ngx-datatable-grid-template-columns]=\"_gridTemplateColumns()\"\n [style.scroll-padding-block-start.px]=\"_isFixedHeader() ? headerHeight() : null\"\n >\n @if (headerHeight()) {\n <datatable-header\n role=\"rowgroup\"\n [sorts]=\"sorts()\"\n [sortType]=\"sortType()\"\n [scrollbarH]=\"scrollbarH()\"\n [dealsWithGroup]=\"_internalGroupedRows() !== undefined\"\n [columns]=\"_internalColumns()\"\n [reorderable]=\"reorderable()\"\n [targetMarkerTemplate]=\"targetMarkerTemplate()\"\n [allRowsSelected]=\"allRowsSelected()\"\n [selectionType]=\"selectionType()\"\n [verticalScrollVisible]=\"verticalScrollVisible\"\n [enableClearingSortState]=\"enableClearingSortState()\"\n (sort)=\"onColumnSort($event)\"\n (resize)=\"onColumnResize($event)\"\n (resizing)=\"onColumnResizing($event)\"\n (reorder)=\"onColumnReorder($event)\"\n (select)=\"onHeaderSelect()\"\n (columnContextmenu)=\"onColumnContextmenu($event)\"\n />\n }\n <datatable-body\n tabindex=\"0\"\n role=\"rowgroup\"\n [groupedRows]=\"_internalGroupedRows()\"\n [rows]=\"_internalRows()\"\n [groupExpansionDefault]=\"groupExpansionDefault()\"\n [scrollbarV]=\"scrollbarV()\"\n [scrollbarH]=\"scrollbarH()\"\n [virtualization]=\"virtualization()\"\n [loadingIndicator]=\"loadingIndicator()\"\n [ghostLoadingIndicator]=\"ghostLoadingIndicator()\"\n [externalPaging]=\"externalPaging()\"\n [rowCount]=\"rowCount()\"\n [offset]=\"correctedOffset()\"\n [trackByProp]=\"trackByProp()\"\n [columns]=\"_internalColumns()\"\n [pageSize]=\"pageSize()\"\n [offsetX]=\"_offsetX\"\n [rowDetail]=\"rowDetail\"\n [groupHeader]=\"groupHeader\"\n [bodyHeight]=\"bodyHeight()\"\n [selectionType]=\"selectionType()\"\n [rowIdentity]=\"rowIdentity()\"\n [rowClass]=\"rowClass()\"\n [selectCheck]=\"selectCheck()\"\n [displayCheck]=\"displayCheck()\"\n [summaryRow]=\"summaryRow()\"\n [summaryHeight]=\"summaryHeight()\"\n [summaryPosition]=\"summaryPosition()\"\n [summaryRowTemplate]=\"summaryRowDirective()?.template\"\n [verticalScrollVisible]=\"verticalScrollVisible\"\n [disableRowCheck]=\"disableRowCheck()\"\n [checkRowPropertyChanges]=\"checkRowPropertyChanges()\"\n [rowDraggable]=\"rowDraggable()\"\n [rowDragEvents]=\"rowDragEvents\"\n [rowDefTemplate]=\"_rowDefTemplate()\"\n [(selected)]=\"selected\"\n (page)=\"onBodyPage($event)\"\n (activate)=\"activate.emit($event)\"\n (rowContextmenu)=\"onRowContextmenu($event)\"\n (scroll)=\"onBodyScroll($event)\"\n (treeAction)=\"onTreeAction($event)\"\n >\n <ng-content select=\"[loading-indicator]\" ngProjectAs=\"[loading-indicator]\">\n <datatable-progress />\n </ng-content>\n <ng-content select=\"[empty-content]\" ngProjectAs=\"[empty-content]\">\n <div class=\"empty-row\" [innerHTML]=\"configuration().messages.emptyMessage\"></div>\n </ng-content>\n </datatable-body>\n </div>\n @if (footerHeight()) {\n <datatable-footer\n [rowCount]=\"_internalGroupedRows() !== undefined ? _internalRows().length : rowCount()\"\n [groupCount]=\"_internalGroupedRows() !== undefined ? rowCount() : undefined\"\n [pageSize]=\"pageSize()\"\n [offset]=\"correctedOffset()\"\n [footerTemplate]=\"_footer()\"\n [pagerLeftArrowIcon]=\"configuration().cssClasses.pagerLeftArrow\"\n [pagerRightArrowIcon]=\"configuration().cssClasses.pagerRightArrow\"\n [pagerPreviousIcon]=\"configuration().cssClasses.pagerPrevious\"\n [selectedCount]=\"selected().length\"\n [selectedMessage]=\"!!selectionType()\"\n [pagerNextIcon]=\"configuration().cssClasses.pagerNext\"\n (page)=\"onFooterPage($event)\"\n />\n }\n</div>\n", styles: [":host{display:block;overflow:hidden;position:relative;transform:translateZ(0)}.datatable-wrapper{display:flex;flex-direction:column;block-size:100%;min-block-size:0}.datatable-grid{display:grid;grid-template-columns:var(--ngx-datatable-grid-template-columns, auto);grid-template-rows:auto 1fr;position:relative;max-inline-size:100%;min-block-size:0}:host(.scroll-horz) .datatable-grid{overflow-x:auto;-webkit-overflow-scrolling:touch}:host(.scroll-vertical) .datatable-grid{overflow-y:auto;flex:1 1 0}.datatable-empty-row{grid-column:1/-1}\n"], dependencies: [{ kind: "directive", type: ScrollContainerDirective, selector: "[datatableScrollContainer]" }, { kind: "component", type: DataTableHeaderComponent, selector: "datatable-header", inputs: ["scrollbarH", "dealsWithGroup", "targetMarkerTemplate", "enableClearingSortState", "sorts", "sortType", "allRowsSelected", "selectionType", "reorderable", "verticalScrollVisible", "columns"], outputs: ["sort", "reorder", "resize", "resizing", "select", "columnContextmenu"] }, { kind: "component", type: DataTableBodyComponent, selector: "datatable-body", inputs: ["rowDefTemplate", "scrollbarV", "scrollbarH", "loadingIndicator", "ghostLoadingIndicator", "externalPaging", "offsetX", "selectionType", "selected", "rowIdentity", "rowDetail", "groupHeader", "selectCheck", "displayCheck", "trackByProp", "rowClass", "groupedRows", "groupExpansionDefault", "virtualization", "summaryRow", "summaryPosition", "summaryHeight", "summaryRowTemplate", "rowDraggable", "rowDragEvents", "disableRowCheck", "checkRowPropertyChanges", "pageSize", "rows", "columns", "offset", "rowCount", "bodyHeight", "verticalScrollVisible"], outputs: ["offsetXChange", "selectedChange", "scroll", "page", "activate", "rowContextmenu", "treeAction"] }, { kind: "component", type: DataTableFooterComponent, selector: "datatable-footer", inputs: ["rowCount", "groupCount", "pageSize", "offset", "pagerLeftArrowIcon", "pagerRightArrowIcon", "pagerPreviousIcon", "pagerNextIcon", "footerTemplate", "selectedCount", "selectedMessage"], outputs: ["page"] }, { kind: "component", type: ProgressBarComponent, selector: "datatable-progress" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DatatableComponent, decorators: [{
type: Component,
args: [{ selector: 'ngx-datatable', imports: [
ScrollContainerDirective,
DataTableHeaderComponent,
DataTableBodyComponent,
DataTableFooterComponent,
ProgressBarComponent
], providers: [
{
provide: DATATABLE_COMPONENT_TOKEN,
useExisting: DatatableComponent
},
{
provide: DatatableConfiguration,
useFactory: () => inject(DatatableComponent).datatableConfiguration
}
], changeDetection: ChangeDetectionStrategy.OnPush, host: {
class: 'ngx-datatable',
'[class.fixed-header]': '_isFixedHeader()',
'[class.fixed-row]': 'rowHeight() !== "auto"',
'[class.scroll-vertical]': 'scrollbarV()',
'[class.virtualized]': 'virtualization()',
'[class.scroll-horz]': 'scrollbarH()',
'[class.selectable]': 'selectionType() !== undefined',
'[class.checkbox-selection]': 'selectionType() === "checkbox"',
'[class.cell-selection]': 'selectionType() === "cell"',
'[class.single-selection]': 'selectionType() === "single"',
'[class.multi-selection]': 'selectionType() === "multi"',
'[class.multi-click-selection]': 'selectionType() === "multiClick"',
'[class.horizontal-overflow]': '_innerWidth() < totalColumnGroupWidths()'
}, template: "<div class=\"datatable-wrapper\">\n <div\n datatableScrollContainer\n role=\"table\"\n class=\"datatable-grid\"\n [style.--ngx-datatable-grid-template-columns]=\"_gridTemplateColumns()\"\n [style.scroll-padding-block-start.px]=\"_isFixedHeader() ? headerHeight() : null\"\n >\n @if (headerHeight()) {\n <datatable-header\n role=\"rowgroup\"\n [sorts]=\"sorts()\"\n [sortType]=\"sortType()\"\n [scrollbarH]=\"scrollbarH()\"\n [dealsWithGroup]=\"_internalGroupedRows() !== undefined\"\n [columns]=\"_internalColumns()\"\n [reorderable]=\"reorderable()\"\n [targetMarkerTemplate]=\"targetMarkerTemplate()\"\n [allRowsSelected]=\"allRowsSelected()\"\n [selectionType]=\"selectionType()\"\n [verticalScrollVisible]=\"verticalScrollVisible\"\n [enableClearingSortState]=\"enableClearingSortState()\"\n (sort)=\"onColumnSort($event)\"\n (resize)=\"onColumnResize($event)\"\n (resizing)=\"onColumnResizing($event)\"\n (reorder)=\"onColumnReorder($event)\"\n (select)=\"onHeaderSelect()\"\n (columnContextmenu)=\"onColumnContextmenu($event)\"\n />\n }\n <datatable-body\n tabindex=\"0\"\n role=\"rowgroup\"\n [groupedRows]=\"_internalGroupedRows()\"\n [rows]=\"_internalRows()\"\n [groupExpansionDefault]=\"groupExpansionDefault()\"\n [scrollbarV]=\"scrollbarV()\"\n [scrollbarH]=\"scrollbarH()\"\n [virtualization]=\"virtualization()\"\n [loadingIndicator]=\"loadingIndicator()\"\n [ghostLoadingIndicator]=\"ghostLoadingIndicator()\"\n [externalPaging]=\"externalPaging()\"\n [rowCount]=\"rowCount()\"\n [offset]=\"correctedOffset()\"\n [trackByProp]=\"trackByProp()\"\n [columns]=\"_internalColumns()\"\n [pageSize]=\"pageSize()\"\n [offsetX]=\"_offsetX\"\n [rowDetail]=\"rowDetail\"\n [groupHeader]=\"groupHeader\"\n [bodyHeight]=\"bodyHeight()\"\n [selectionType]=\"selectionType()\"\n [rowIdentity]=\"rowIdentity()\"\n [rowClass]=\"rowClass()\"\n [selectCheck]=\"selectCheck()\"\n [displayCheck]=\"displayCheck()\"\n [summaryRow]=\"summaryRow()\"\n [summaryHeight]=\"summaryHeight()\"\n [summaryPosition]=\"summaryPosition()\"\n [summaryRowTemplate]=\"summaryRowDirective()?.template\"\n [verticalScrollVisible]=\"verticalScrollVisible\"\n [disableRowCheck]=\"disableRowCheck()\"\n [checkRowPropertyChanges]=\"checkRowPropertyChanges()\"\n [rowDraggable]=\"rowDraggable()\"\n [rowDragEvents]=\"rowDragEvents\"\n [rowDefTemplate]=\"_rowDefTemplate()\"\n [(selected)]=\"selected\"\n (page)=\"onBodyPage($event)\"\n (activate)=\"activate.emit($event)\"\n (rowContextmenu)=\"onRowContextmenu($event)\"\n (scroll)=\"onBodyScroll($event)\"\n (treeAction)=\"onTreeAction($event)\"\n >\n <ng-content select=\"[loading-indicator]\" ngProjectAs=\"[loading-indicator]\">\n <datatable-progress />\n </ng-content>\n <ng-content select=\"[empty-content]\" ngProjectAs=\"[empty-content]\">\n <div class=\"empty-row\" [innerHTML]=\"configuration().messages.emptyMessage\"></div>\n </ng-content>\n </datatable-body>\n </div>\n @if (footerHeight()) {\n <datatable-footer\n [rowCount]=\"_internalGroupedRows() !== undefined ? _internalRows().length : rowCount()\"\n [groupCount]=\"_internalGroupedRows() !== undefined ? rowCount() : undefined\"\n [pageSize]=\"pageSize()\"\n [offset]=\"correctedOffset()\"\n [footerTemplate]=\"_footer()\"\n [pagerLeftArrowIcon]=\"configuration().cssClasses.pagerLeftArrow\"\n [pagerRightArrowIcon]=\"configuration().cssClasses.pagerRightArrow\"\n [pagerPreviousIcon]=\"configuration().cssClasses.pagerPrevious\"\n [selectedCount]=\"selected().length\"\n [selectedMessage]=\"!!selectionType()\"\n [pagerNextIcon]=\"configuration().cssClasses.pagerNext\"\n (page)=\"onFooterPage($event)\"\n />\n }\n</div>\n", styles: [":host{display:block;overflow:hidden;position:relative;transform:translateZ(0)}.datatable-wrapper{display:flex;flex-direction:column;block-size:100%;min-block-size:0}.datatable-grid{display:grid;grid-template-columns:var(--ngx-datatable-grid-template-columns, auto);grid-template-rows:auto 1fr;position:relative;max-inline-size:100%;min-block-size:0}:host(.scroll-horz) .datatable-grid{overflow-x:auto;-webkit-overflow-scrolling:touch}:host(.scroll-vertical) .datatable-grid{overflow-y:auto;flex:1 1 0}.datatable-empty-row{grid-column:1/-1}\n"] }]
}], ctorParameters: () => [], propDecorators: { targetMarkerTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "targetMarkerTemplate", required: false }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: false }] }], groupRowsBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "groupRowsBy", required: false }] }], groupedRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "groupedRows", required: false }] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: false }] }], selected: [{ type: i0.Input, args: [{ isSignal: true, alias: "selected", required: false }] }, { type: i0.Output, args: ["selectedChange"] }], scrollbarV: [{ type: i0.Input, args: [{ isSignal: true, alias: "scrollbarV", required: false }] }], scrollbarVDynamic: [{ type: i0.Input, args: [{ isSignal: true, alias: "scrollbarVDynamic", required: false }] }], scrollbarH: [{ type: i0.Input, args: [{ isSignal: true, alias: "scrollbarH", required: false }] }], rowHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowHeight", required: false }] }], columnMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "columnMode", required: false }] }], headerHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerHeight", required: false }] }], footerHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "footerHeight", required: false }] }], externalPaging: [{ type: i0.Input, args: [{ isSignal: true, alias: "externalPaging", required: false }] }], externalSorting: [{ type: i0.Input, args: [{ isSignal: true, alias: "externalSorting", required: false }] }], limit: [{ type: i0.Input, args: [{ isSignal: true, alias: "limit", required: false }] }], count: [{ type: i0.Input, args: [{ isSignal: true, alias: "count", required: false }] }], offset: [{ type: i0.Input, args: [{ isSignal: true, alias: "offset", required: false }] }, { type: i0.Output, args: ["offsetChange"] }], loadingIndicator: [{ type: i0.Input, args: [{ isSignal: true, alias: "loadingIndicator", required: false }] }], ghostLoadingIndicator: [{ type: i0.Input, args: [{ isSignal: true, alias: "ghostLoadingIndicator", required: false }] }], selectionType: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectionType", required: false }] }], reorderable: [{ type: i0.Input, args: [{ isSignal: true, alias: "reorderable", required: false }] }], swapColumns: [{ type: i0.Input, args: [{ isSignal: true, alias: "swapColumns", required: false }] }], sortType: [{ type: i0.Input, args: [{ isSignal: true, alias: "sortType", required: false }] }], sorts: [{ type: i0.Input, args: [{ isSignal: true, alias: "sorts", required: false }] }, { type: i0.Output, args: ["sortsChange"] }], cssClasses: [{ type: i0.Input, args: [{ isSignal: true, alias: "cssClasses", required: false }] }], messages: [{ type: i0.Input, args: [{ isSignal: true, alias: "messages", required: false }] }], rowClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowClass", required: false }] }], selectCheck: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectCheck", required: false }] }], displayCheck: [{ type: i0.Input, args: [{ isSignal: true, alias: "displayCheck", required: false }] }], groupExpansionDefault: [{ type: i0.Input, args: [{ isSignal: true, alias: "groupExpansionDefault", required: false }] }], trackByProp: [{ type: i0.Input, args: [{ isSignal: true, alias: "trackByProp", required: false }] }], selectAllRowsOnPage: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectAllRowsOnPage", required: false }] }], virtualization: [{ type: i0.Input, args: [{ isSignal: true, alias: "virtualization", required: false }] }], treeFromRelation: [{ type: i0.Input, args: [{ isSignal: true, alias: "treeFromRelation", required: false }] }], treeToRelation: [{ type: i0.Input, args: [{ isSignal: true, alias: "treeToRelation", required: false }] }], summaryRow: [{ type: i0.Input, args: [{ isSignal: true, alias: "summaryRow", required: false }] }], summaryHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "summaryHeight", required: false }] }], summaryPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "summaryPosition", required: false }] }], disableRowCheck: [{ type: i0.Input, args: [{ isSignal: true, alias: "disableRowCheck", required: false }] }], rowDraggable: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowDraggable", required: false }] }], enableClearingSortState: [{ type: i0.Input, args: [{ isSignal: true, alias: "enableClearingSortState", required: false }] }], checkRowListChanges: [{ type: i0.Input, args: [{ isSignal: true, alias: "checkRowListChanges", required: false }] }], checkRowPropertyChanges: [{ type: i0.Input, args: [{ isSignal: true, alias: "checkRowPropertyChanges", required: false }] }], scroll: [{ type: i0.Output, args: ["scroll"] }], activate: [{ type: i0.Output, args: ["activate"] }], page: [{ type: i0.Output, args: ["page"] }], reorder: [{ type: i0.Output, args: ["reorder"] }], resize: [{ type: i0.Output, args: ["resize"] }], tableContextmenu: [{ type: i0.Output, args: ["tableContextmenu"] }], treeAction: [{ type: i0.Output, args: ["treeAction"] }], rowDragEvents: [{ type: i0.Output, args: ["rowDragEvents"] }], columnTemplates: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => DataTableColumnDirective), { isSignal: true }] }], rowDetail: [{
type: ContentChild,
args: [DatatableRowDetailDirective]
}], groupHeader: [{
type: ContentChild,
args: [DatatableGroupHeaderDirective]
}], summaryRowDirective: [{ type: i0.ContentChild, args: [i0.forwardRef(() => DatatableSummaryRowDirective), { isSignal: true }] }], _footer: [{ type: i0.ContentChild, args: [i0.forwardRef(() => DatatableFooterDirective), { isSignal: true }] }], _bodyComponent: [{ type: i0.ViewChild, args: [i0.forwardRef(() => DataTableBodyComponent), { isSignal: true }] }], _headerElement: [{ type: i0.ViewChild, args: [i0.forwardRef(() => DataTableHeaderComponent), { ...{
read: (ElementRef)
}, isSignal: true }] }], _scrollContainer: [{ type: i0.ViewChild, args: [i0.forwardRef(() => ScrollContainerDirective), { isSignal: true }] }], _rowDefTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => DatatableRowDefDirective), { ...{
read: TemplateRef
}, isSignal: true }] }], _footerComponent: [{ type: i0.ViewChild, args: [i0.forwardRef(() => DataTableFooterComponent), { isSignal: true }] }], rowIdentity: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowIdentity", required: false }] }] } });
/**
* Row Disable Directive
* Use this to disable/enable all children elements
* Usage:
* To disable
* <div [disabled]="true" disable-row >
* </div>
* To enable
* <div [disabled]="false" disable-row >
* </div>
*/
class DisableRowDirective {
elementRef = inject(ElementRef);
disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
constructor() {
effect(() => {
if (this.disabled()) {
this.disableAllElements();
}
});
}
disableAllElements() {
const hostElement = this.elementRef?.nativeElement;
if (!hostElement) {
return;
}
Array.from(hostElement.querySelectorAll('*')).forEach(child => {
child.setAttribute('disabled', '');
});
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DisableRowDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.8", type: DisableRowDirective, isStandalone: true, selector: "[disable-row]", inputs: { disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DisableRowDirective, decorators: [{
type: Directive,
args: [{
selector: '[disable-row]'
}]
}], ctorParameters: () => [], propDecorators: { disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }] } });
class NgxDatatableModule {
/**
* Configure global configuration via NgxDatatableConfig
* @param configuration
*/
static forRoot(configuration) {
return {
ngModule: NgxDatatableModule,
providers: [providedNgxDatatableConfig(configuration)]
};
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: NgxDatatableModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "22.0.8", ngImport: i0, type: NgxDatatableModule, imports: [DataTableFooterTemplateDirective,
DatatableComponent,
DataTableColumnDirective,
DatatableRowDetailDirective,
DatatableGroupHeaderDirective,
DatatableRowDetailTemplateDirective,
DataTableColumnHeaderDirective,
DataTableColumnCellDirective,
DataTableColumnGhostCellDirective,
DataTableColumnCellTreeToggle,
DatatableFooterDirective,
DatatablePagerComponent,
DatatableGroupHeaderTemplateDirective,
DisableRowDirective,
DatatableRowDefComponent,
DatatableRowDefDirective,
DatatableSummaryRowDirective], exports: [DatatableComponent,
DatatableRowDetailDirective,
DatatableGroupHeaderDirective,
DatatableRowDetailTemplateDirective,
DataTableColumnDirective,
DataTableColumnHeaderDirective,
DataTableColumnCellDirective,
DataTableColumnGhostCellDirective,
DataTableColumnCellTreeToggle,
DataTableFooterTemplateDirective,
DatatableFooterDirective,
DatatablePagerComponent,
DatatableGroupHeaderTemplateDirective,
DisableRowDirective,
DatatableRowDefComponent,
DatatableRowDefDirective,
DatatableSummaryRowDirective] });
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: NgxDatatableModule });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: NgxDatatableModule, decorators: [{
type: NgModule,
args: [{
imports: [
DataTableFooterTemplateDirective,
DatatableComponent,
DataTableColumnDirective,
DatatableRowDetailDirective,
DatatableGroupHeaderDirective,
DatatableRowDetailTemplateDirective,
DataTableColumnHeaderDirective,
DataTableColumnCellDirective,
DataTableColumnGhostCellDirective,
DataTableColumnCellTreeToggle,
DatatableFooterDirective,
DatatablePagerComponent,
DatatableGroupHeaderTemplateDirective,
DisableRowDirective,
DatatableRowDefComponent,
DatatableRowDefDirective,
DatatableSummaryRowDirective
],
exports: [
DatatableComponent,
DatatableRowDetailDirective,
DatatableGroupHeaderDirective,
DatatableRowDetailTemplateDirective,
DataTableColumnDirective,
DataTableColumnHeaderDirective,
DataTableColumnCellDirective,
DataTableColumnGhostCellDirective,
DataTableColumnCellTreeToggle,
DataTableFooterTemplateDirective,
DatatableFooterDirective,
DatatablePagerComponent,
DatatableGroupHeaderTemplateDirective,
DisableRowDirective,
DatatableRowDefComponent,
DatatableRowDefDirective,
DatatableSummaryRowDirective
]
}]
}] });
/**
* @deprecated The constant `SortDirection` should no longer be used. Instead use the value directly:
* ```
* // old
* const sortDir: SortDirection = SortDirection.asc;
* // new
* const sortDir: SortDirection = 'asc';
* ```
*/
// eslint-disable-next-line @typescript-eslint/naming-convention
const SortDirection = {
asc: 'asc',
desc: 'desc'
};
/**
* @deprecated The constant `SortType` should no longer be used. Instead use the value directly:
* ```
* // old
* const sortType: SortType = SortType.single;
* // new
* const sortType: SortType = 'single';
* ```
*/
// eslint-disable-next-line @typescript-eslint/naming-convention
const SortType = {
single: 'single',
multi: 'multi'
};
/**
* @deprecated The constant `ColumnMode` should no longer be used. Instead use the value directly:
* ```
* // old
* <ngx-datatable [columnMode]="ColumnMode.force"></ngx-datatable>
* // new
* <ngx-datatable [columnMode]="'force'"></ngx-datatable>
* ```
*/
// eslint-disable-next-line @typescript-eslint/naming-convention
const ColumnMode = {
standard: 'standard',
flex: 'flex',
force: 'force'
};
/**
* @deprecated The constant `ContextmenuType` should no longer be used. Instead use the value directly:
* ```
* // old
* const contextmenuType: ContextmenuType = ContextmenuType.header;
* // new
* const contextmenuType: ContextmenuType = 'header';
* ```
*/
// eslint-disable-next-line @typescript-eslint/naming-convention
const ContextmenuType = {
header: 'header',
body: 'body'
};
/**
* @deprecated The constant `SelectionType` should no longer be used. Instead use the value directly:
* ```
* // old
* <ngx-datatable [selectionType]="SelectionType.multi"></ngx-datatable>
* // new
* <ngx-datatable [selectionType]="'multi'"></ngx-datatable>
* ```
*/
// eslint-disable-next-line @typescript-eslint/naming-convention
const SelectionType = {
single: 'single',
multi: 'multi',
multiClick: 'multiClick',
cell: 'cell',
checkbox: 'checkbox'
};
/*
* Public API Surface of ngx-datatable
*/
// components
/**
* Generated bundle index. Do not edit.
*/
export { ColumnMode, ContextmenuType, DataTableColumnCellDirective, DataTableColumnCellTreeToggle, DataTableColumnDirective, DataTableColumnGhostCellDirective, DataTableColumnHeaderDirective, DataTableFooterTemplateDirective, DatatableComponent, DatatableFooterDirective, DatatableGroupHeaderDirective, DatatableGroupHeaderTemplateDirective, DatatablePagerComponent, DatatableRowDefComponent, DatatableRowDefDirective, DatatableRowDefInternalDirective, DatatableRowDetailDirective, DatatableRowDetailTemplateDirective, DatatableSummaryRowDirective, DisableRowDirective, NgxDatatableModule, SelectionType, SortDirection, SortType, providedNgxDatatableConfig };
//# sourceMappingURL=siemens-ngx-datatable.mjs.map