UNPKG

@swimlane/ngx-datatable

Version:

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

933 lines (915 loc) 335 kB
import * as i0 from '@angular/core'; import { Directive, input, contentChild, TemplateRef, computed, output, inject, InjectionToken, Component, ViewContainerRef, Injector, booleanAttribute, numberAttribute, ElementRef, NgZone, signal, DOCUMENT, Injectable, ChangeDetectionStrategy, KeyValueDiffers, HostListener, linkedSignal, ChangeDetectorRef, HostBinding, Renderer2, model, viewChildren, DestroyRef, effect, untracked, ViewChild, Input, ContentChildren, contentChildren, viewChild, IterableDiffers, ContentChild, NgModule } from '@angular/core'; import { NgTemplateOutlet, NgStyle } from '@angular/common'; import { __decorate } from 'tslib'; import { startWith } from 'rxjs'; class DatatableGroupHeaderTemplateDirective { static ngTemplateContextGuard(directive, context) { return true; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DatatableGroupHeaderTemplateDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: DatatableGroupHeaderTemplateDirective, isStandalone: true, selector: "[ngx-datatable-group-header-template]", ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DatatableGroupHeaderTemplateDirective, decorators: [{ type: Directive, args: [{ selector: '[ngx-datatable-group-header-template]' }] }] }); class DatatableGroupHeaderDirective { constructor() { /** * Row height is required when virtual scroll is enabled. */ this.rowHeight = input(0, ...(ngDevMode ? [{ debugName: "rowHeight" }] : /* istanbul ignore next */ [])); /** * Show checkbox at group header to select all rows of the group. */ this.checkboxable = input(false, ...(ngDevMode ? [{ debugName: "checkboxable" }] : /* istanbul ignore next */ [])); this._templateInput = input(undefined, { ...(ngDevMode ? { debugName: "_templateInput" } : /* istanbul ignore next */ {}), alias: 'template' }); this._templateQuery = contentChild(DatatableGroupHeaderTemplateDirective, { ...(ngDevMode ? { debugName: "_templateQuery" } : /* istanbul ignore next */ {}), read: TemplateRef }); this.template = computed(() => this._templateInput() ?? this._templateQuery() ?? null, ...(ngDevMode ? [{ debugName: "template" }] : /* istanbul ignore next */ [])); /** * Track toggling of group visibility */ this.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 { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DatatableGroupHeaderDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "21.2.9", 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: "21.2.9", 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"] }] } }); /** * This component is passed as ng-template and rendered by BodyComponent. * BodyComponent uses rowDefInternal to first inject actual row template. * This component will render that actual row template. */ class DatatableRowDefComponent { constructor() { this.rowDef = inject(ROW_DEF_TOKEN); this.rowContext = { ...this.rowDef.rowDefInternal(), disabled: this.rowDef.rowDefInternalDisabled() }; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DatatableRowDefComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: DatatableRowDefComponent, isStandalone: true, selector: "datatable-row-def", ngImport: i0, template: `@if (rowDef.rowDefInternal().rowTemplate) { <ng-container [ngTemplateOutlet]="rowDef.rowDefInternal().rowTemplate" [ngTemplateOutletContext]="rowContext" /> }`, isInline: true, dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", 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" /> }` }] }] }); class DatatableRowDefDirective { static ngTemplateContextGuard(_dir, ctx) { return true; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DatatableRowDefDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: DatatableRowDefDirective, isStandalone: true, selector: "[rowDef]", ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DatatableRowDefDirective, decorators: [{ type: Directive, args: [{ selector: '[rowDef]' }] }] }); /** * @internal To be used internally by ngx-datatable. */ class DatatableRowDefInternalDirective { constructor() { this.vc = inject(ViewContainerRef); this.rowDefInternal = input.required(...(ngDevMode ? [{ debugName: "rowDefInternal" }] : /* istanbul ignore next */ [])); this.rowDefInternalDisabled = input(...(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 { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DatatableRowDefInternalDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", 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: "21.2.9", 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'); class DataTableColumnCellDirective { constructor() { this.template = inject(TemplateRef); } static ngTemplateContextGuard(dir, ctx) { return true; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DataTableColumnCellDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: DataTableColumnCellDirective, isStandalone: true, selector: "[ngx-datatable-cell-template]", ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DataTableColumnCellDirective, decorators: [{ type: Directive, args: [{ selector: '[ngx-datatable-cell-template]' }] }] }); class DataTableColumnGhostCellDirective { static ngTemplateContextGuard(directive, context) { return true; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DataTableColumnGhostCellDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: DataTableColumnGhostCellDirective, isStandalone: true, selector: "[ngx-datatable-ghost-cell-template]", ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DataTableColumnGhostCellDirective, decorators: [{ type: Directive, args: [{ selector: '[ngx-datatable-ghost-cell-template]' }] }] }); class DataTableColumnHeaderDirective { static ngTemplateContextGuard(directive, context) { return true; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DataTableColumnHeaderDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: DataTableColumnHeaderDirective, isStandalone: true, selector: "[ngx-datatable-header-template]", ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DataTableColumnHeaderDirective, decorators: [{ type: Directive, args: [{ selector: '[ngx-datatable-header-template]' }] }] }); class DataTableColumnCellTreeToggle { constructor() { this.template = inject(TemplateRef); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DataTableColumnCellTreeToggle, deps: [], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: DataTableColumnCellTreeToggle, isStandalone: true, selector: "[ngx-datatable-tree-toggle]", ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DataTableColumnCellTreeToggle, decorators: [{ type: Directive, args: [{ selector: '[ngx-datatable-tree-toggle]' }] }] }); class DataTableColumnDirective { constructor() { this.name = input(...(ngDevMode ? [undefined, { debugName: "name" }] : /* istanbul ignore next */ [])); this.prop = input(...(ngDevMode ? [undefined, { debugName: "prop" }] : /* istanbul ignore next */ [])); this.bindAsUnsafeHtml = input(false, { ...(ngDevMode ? { debugName: "bindAsUnsafeHtml" } : /* istanbul ignore next */ {}), transform: booleanAttribute }); this.frozenLeft = input(false, { ...(ngDevMode ? { debugName: "frozenLeft" } : /* istanbul ignore next */ {}), transform: booleanAttribute }); this.frozenRight = input(false, { ...(ngDevMode ? { debugName: "frozenRight" } : /* istanbul ignore next */ {}), transform: booleanAttribute }); this.flexGrow = input(undefined, { ...(ngDevMode ? { debugName: "flexGrow" } : /* istanbul ignore next */ {}), transform: numberAttribute }); this.resizeable = input(undefined, { ...(ngDevMode ? { debugName: "resizeable" } : /* istanbul ignore next */ {}), transform: booleanAttribute }); this.comparator = input(...(ngDevMode ? [undefined, { debugName: "comparator" }] : /* istanbul ignore next */ [])); this.pipe = input(...(ngDevMode ? [undefined, { debugName: "pipe" }] : /* istanbul ignore next */ [])); this.sortable = input(undefined, { ...(ngDevMode ? { debugName: "sortable" } : /* istanbul ignore next */ {}), transform: booleanAttribute }); this.draggable = input(undefined, { ...(ngDevMode ? { debugName: "draggable" } : /* istanbul ignore next */ {}), transform: booleanAttribute }); this.canAutoResize = input(undefined, { ...(ngDevMode ? { debugName: "canAutoResize" } : /* istanbul ignore next */ {}), transform: booleanAttribute }); this.minWidth = input(undefined, { ...(ngDevMode ? { debugName: "minWidth" } : /* istanbul ignore next */ {}), transform: numberAttribute }); this.width = input(undefined, { ...(ngDevMode ? { debugName: "width" } : /* istanbul ignore next */ {}), transform: numberAttribute }); this.maxWidth = input(undefined, { ...(ngDevMode ? { debugName: "maxWidth" } : /* istanbul ignore next */ {}), transform: numberAttribute }); this.checkboxable = input(false, { ...(ngDevMode ? { debugName: "checkboxable" } : /* istanbul ignore next */ {}), transform: booleanAttribute }); this.headerCheckboxable = input(false, { ...(ngDevMode ? { debugName: "headerCheckboxable" } : /* istanbul ignore next */ {}), transform: booleanAttribute }); this.headerClass = input(...(ngDevMode ? [undefined, { debugName: "headerClass" }] : /* istanbul ignore next */ [])); this.cellClass = input(...(ngDevMode ? [undefined, { debugName: "cellClass" }] : /* istanbul ignore next */ [])); this.isTreeColumn = input(false, { ...(ngDevMode ? { debugName: "isTreeColumn" } : /* istanbul ignore next */ {}), transform: booleanAttribute }); this.treeLevelIndent = input(...(ngDevMode ? [undefined, { debugName: "treeLevelIndent" }] : /* istanbul ignore next */ [])); this.summaryFunc = input(...(ngDevMode ? [undefined, { debugName: "summaryFunc" }] : /* istanbul ignore next */ [])); this.summaryTemplate = input(...(ngDevMode ? [undefined, { debugName: "summaryTemplate" }] : /* istanbul ignore next */ [])); this.cellTemplateInput = input(undefined, { ...(ngDevMode ? { debugName: "cellTemplateInput" } : /* istanbul ignore next */ {}), alias: 'cellTemplate' }); this.cellTemplateQuery = contentChild(DataTableColumnCellDirective, { ...(ngDevMode ? { debugName: "cellTemplateQuery" } : /* istanbul ignore next */ {}), read: TemplateRef }); this.headerTemplateInput = input(undefined, { ...(ngDevMode ? { debugName: "headerTemplateInput" } : /* istanbul ignore next */ {}), alias: 'headerTemplate' }); this.headerTemplateQuery = contentChild(DataTableColumnHeaderDirective, { ...(ngDevMode ? { debugName: "headerTemplateQuery" } : /* istanbul ignore next */ {}), read: TemplateRef }); this.treeToggleTemplateInput = input(undefined, { ...(ngDevMode ? { debugName: "treeToggleTemplateInput" } : /* istanbul ignore next */ {}), alias: 'treeToggleTemplate' }); this.treeToggleTemplateQuery = contentChild(DataTableColumnCellTreeToggle, { ...(ngDevMode ? { debugName: "treeToggleTemplateQuery" } : /* istanbul ignore next */ {}), read: TemplateRef }); this.ghostCellTemplateInput = input(undefined, { ...(ngDevMode ? { debugName: "ghostCellTemplateInput" } : /* istanbul ignore next */ {}), alias: 'ghostCellTemplate' }); this.ghostCellTemplateQuery = contentChild(DataTableColumnGhostCellDirective, { ...(ngDevMode ? { debugName: "ghostCellTemplateQuery" } : /* istanbul ignore next */ {}), read: TemplateRef }); /** * Computed property that returns the column configuration as a TableColumn object */ this.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() }), ...(ngDevMode ? [{ debugName: "column" }] : /* istanbul ignore next */ [])); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DataTableColumnDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "21.2.9", 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: "21.2.9", 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 }] }] } }); /** * Visibility Observer Directive * * Usage: * * <div * visibilityObserver * (visible)="onVisible($event)"> * </div> * */ class VisibilityDirective { constructor() { this.element = inject(ElementRef); this.zone = inject(NgZone); this.isVisible = signal(false, ...(ngDevMode ? [{ debugName: "isVisible" }] : /* istanbul ignore next */ [])); this.visible = output(); } ngOnInit() { this.runCheck(); } ngOnDestroy() { clearTimeout(this.timeout); } onVisibilityChange() { // trigger zone recalc for columns this.zone.run(() => { this.isVisible.set(true); this.visible.emit(true); }); } runCheck() { const check = () => { // https://davidwalsh.name/offsetheight-visibility const { offsetHeight, offsetWidth } = this.element.nativeElement; if (offsetHeight && offsetWidth) { clearTimeout(this.timeout); this.onVisibilityChange(); } else { clearTimeout(this.timeout); this.zone.runOutsideAngular(() => { this.timeout = window.setTimeout(() => check(), 50); }); } }; this.timeout = window.setTimeout(() => check()); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: VisibilityDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: VisibilityDirective, isStandalone: true, selector: "[visibilityObserver]", outputs: { visible: "visible" }, host: { properties: { "class.visible": "isVisible()" } }, ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: VisibilityDirective, decorators: [{ type: Directive, args: [{ selector: '[visibilityObserver]', host: { '[class.visible]': 'isVisible()' } }] }], propDecorators: { visible: [{ type: i0.Output, args: ["visible"] }] } }); 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 { constructor() { this.document = inject(DOCUMENT); this.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 { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: ScrollbarHelper, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: ScrollbarHelper, providedIn: 'root' }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: ScrollbarHelper, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }] }); /** * 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 split = path.split('.'); if (split.length) { for (let i = 0; i < split.length; i++) { current = current[split[i]]; // if found undefined, return empty string if (current === undefined || current === null) { return ''; } } } return current; }; /** * 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 }; }; /** * 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; }; const columnsByPinArr = (val) => { const colsByPin = columnsByPin(val); return [ { type: 'left', columns: colsByPin.left }, { type: 'center', columns: colsByPin.center }, { type: 'right', columns: colsByPin.right } ]; }; /** * 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/facebook/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); for (const column of columnsToResize) { if (!column.$$oldWidth) { column.$$oldWidth = column.width(); } } let additionWidthPerColumn = 0; let exceedsWindow = false; 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); // reset so we don't have stale values for (const column of columnsToResize) { column.$$oldWidth = 0; } }