UNPKG

@siemens/ngx-datatable

Version:

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

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