UNPKG

@rxap/data-grid

Version:

Provides a data grid component for Angular applications. It supports plain and form modes, custom header and cell templates, and integration with Rxap Forms and Data Sources. The component allows for displaying and editing data in a tabular format with fe

469 lines (457 loc) 35.2 kB
import * as i0 from '@angular/core'; import { TemplateRef, Directive, Inject, Input, ContentChild, Pipe, isDevMode, EventEmitter, Component, ChangeDetectionStrategy, Optional, ContentChildren, Output, NgModule } from '@angular/core'; import { getFromObject, clone } from '@rxap/utilities'; import { map, filter, tap, shareReplay, take } from 'rxjs/operators'; import { AsyncPipe, NgIf, NgFor, NgTemplateOutlet, NgClass } from '@angular/common'; import * as i3 from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button'; import * as i5 from '@angular/material/divider'; import { MatDividerModule } from '@angular/material/divider'; import * as i6 from '@angular/material/form-field'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatIconModule } from '@angular/material/icon'; import * as i4 from '@angular/material/progress-spinner'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import * as i1 from '@angular/router'; import { ActivationEnd } from '@angular/router'; import * as i2 from '@rxap/forms'; import { RxapFormsModule } from '@rxap/forms'; import { GetFromObjectPipe, ReplacePipe, EscapeQuotationMarkPipe } from '@rxap/pipes'; import { ToggleSubject } from '@rxap/rxjs'; import { EMPTY, of, BehaviorSubject, merge, debounceTime, combineLatest } from 'rxjs'; class DataGridCellDefDirective { static ngTemplateContextGuard(dir, ctx) { return true; } constructor(template) { this.template = template; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.3", ngImport: i0, type: DataGridCellDefDirective, deps: [{ token: TemplateRef }], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.1.3", type: DataGridCellDefDirective, isStandalone: true, selector: "[rxapDataGridCellDef]", ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.3", ngImport: i0, type: DataGridCellDefDirective, decorators: [{ type: Directive, args: [{ selector: '[rxapDataGridCellDef]', standalone: true, }] }], ctorParameters: () => [{ type: i0.TemplateRef, decorators: [{ type: Inject, args: [TemplateRef] }] }] }); class DataGridEditCellDefDirective { static ngTemplateContextGuard(dir, ctx) { return true; } constructor(template) { this.template = template; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.3", ngImport: i0, type: DataGridEditCellDefDirective, deps: [{ token: TemplateRef }], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.1.3", type: DataGridEditCellDefDirective, isStandalone: true, selector: "[rxapDataGridEditCellDef]", ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.3", ngImport: i0, type: DataGridEditCellDefDirective, decorators: [{ type: Directive, args: [{ selector: '[rxapDataGridEditCellDef]', standalone: true, }] }], ctorParameters: () => [{ type: i0.TemplateRef, decorators: [{ type: Inject, args: [TemplateRef] }] }] }); class DataGridHeaderCellDefDirective { static ngTemplateContextGuard(dir, ctx) { return true; } constructor(template) { this.template = template; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.3", ngImport: i0, type: DataGridHeaderCellDefDirective, deps: [{ token: TemplateRef }], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.1.3", type: DataGridHeaderCellDefDirective, isStandalone: true, selector: "[rxapDataGridHeaderCellDef]", ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.3", ngImport: i0, type: DataGridHeaderCellDefDirective, decorators: [{ type: Directive, args: [{ selector: '[rxapDataGridHeaderCellDef]', standalone: true, }] }], ctorParameters: () => [{ type: i0.TemplateRef, decorators: [{ type: Inject, args: [TemplateRef] }] }] }); class DataGridRowDefDirective { constructor() { this.data = null; /** * true - the header and content cell are "rotated" by 90°. The header is above the content cell and both have a * colspan * * **flip: false** * Label | Value * --- | --- * Header | Content * * **flip: true** * Label | Value * --- | --- * Header (colspan 2) * Content (colspan 2) * */ this.flip = false; } get isSubHeader() { return !this.name; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.3", ngImport: i0, type: DataGridRowDefDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.1.3", type: DataGridRowDefDirective, isStandalone: true, selector: "[rxapDataGridRowDef]", inputs: { data: "data", name: ["rxapDataGridRowDef", "name"], flip: "flip" }, queries: [{ propertyName: "cell", first: true, predicate: DataGridCellDefDirective, descendants: true }, { propertyName: "headerCell", first: true, predicate: DataGridHeaderCellDefDirective, descendants: true }, { propertyName: "editCell", first: true, predicate: DataGridEditCellDefDirective, descendants: true }], ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.3", ngImport: i0, type: DataGridRowDefDirective, decorators: [{ type: Directive, args: [{ selector: '[rxapDataGridRowDef]', standalone: true, }] }], propDecorators: { data: [{ type: Input }], name: [{ type: Input, args: ['rxapDataGridRowDef'] }], flip: [{ type: Input }], cell: [{ type: ContentChild, args: [DataGridCellDefDirective] }], headerCell: [{ type: ContentChild, args: [DataGridHeaderCellDefDirective] }], editCell: [{ type: ContentChild, args: [DataGridEditCellDefDirective] }] } }); class DataGridValuePipe { transform(data, path) { if (!path) { throw new Error('FATAL: The path is required! Ensure the data grid row has a defined name property!'); } return data.pipe(map(source => getFromObject(source, path))); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.3", ngImport: i0, type: DataGridValuePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); } static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.1.3", ngImport: i0, type: DataGridValuePipe, isStandalone: true, name: "dataGridValue" }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.3", ngImport: i0, type: DataGridValuePipe, decorators: [{ type: Pipe, args: [{ name: 'dataGridValue', standalone: true, }] }] }); class IsEmptyPipe { transform(data, path) { if (!path) { throw new Error('FATAL: The path is required! Ensure the data grid row has a defined name property!'); } return data.pipe(map(source => getFromObject(source, path)), map(value => value === null || value === undefined || value === '')); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.3", ngImport: i0, type: IsEmptyPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); } static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.1.3", ngImport: i0, type: IsEmptyPipe, isStandalone: true, name: "isEmpty" }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.3", ngImport: i0, type: IsEmptyPipe, decorators: [{ type: Pipe, args: [{ name: 'isEmpty', standalone: true, }] }] }); var DataGridMode; (function (DataGridMode) { /** * The view cell template is used to display the property value */ DataGridMode["PLAIN"] = "plain"; /** * The edit cell template is used to display the property value, but the form and all controls are marked as disabled */ DataGridMode["FORM"] = "form"; })(DataGridMode || (DataGridMode = {})); function IsDataGridMode(value) { return [DataGridMode.PLAIN, DataGridMode.FORM].includes(value); } class DataGridComponent { constructor(cdr, router, formDirective) { this.cdr = cdr; this.router = router; this.formDirective = formDirective; this.isDevMode = isDevMode(); this.header = false; this.viewer = this; this.displayProperties = null; this.hideEmptyProperties = false; this.editModeChange = new EventEmitter(); this.rows$ = EMPTY; this.hasError$ = of(false); this.dataLoading$ = of(false); this.loading$ = new ToggleSubject(); this._editMode$ = new BehaviorSubject(false); this._mode$ = new BehaviorSubject(DataGridMode.PLAIN); this._routerEventSubscription = null; this.isEditMode$ = this._editMode$.asObservable(); this.mode$ = this._mode$.asObservable(); this.isFormMode$ = this.mode$.pipe(map(mode => mode === DataGridMode.FORM)); this.isPlainMode$ = this.mode$.pipe(map(mode => mode === DataGridMode.PLAIN)); } set mode(value) { if (IsDataGridMode(value)) { this._mode$.next(value); } else { throw new Error(`The data grid mode only support 'plain' and 'form' - given '${value}'`); } } get isFormModeOrHasAnyEditCells() { return this._mode$.value === DataGridMode.FORM || this.hasAnyEditCells; } get isFormMode() { return this._mode$.value === DataGridMode.FORM; } get hasAnyEditCells() { return this.rows?.some(row => !!row.editCell) ?? false; } get isEditMode() { return this._editMode$.value; } set editMode(value) { this._editMode$.next(value); } /** * @deprecated use the loading$ property instead */ get loading() { return this.loading$.value; } ngAfterContentInit() { this.rows$ = merge(of(this.rows), this.rows.changes); } logCurrentFormState() { console.log(clone(this.formDirective?.form.value)); } ngOnInit() { // resets the edit mode if this component is used in a sibling router path // if not reset the edit mode is president after the route changes this._routerEventSubscription = this.router.events.pipe(filter(event => event instanceof ActivationEnd), tap(() => this.disableEditMode())).subscribe(); if (this.dataSource && this.data) { throw new Error('You can not use both dataSource and data input'); } let data$ = EMPTY; if (this.dataSource) { data$ = this.dataSource.connect(this.viewer); } if (this.data) { data$ = of(this.data); } if (data$ === EMPTY && isDevMode()) { console.warn('No data source or data input provided'); } this.data$ = data$.pipe(debounceTime(100), tap(data => this.data = data), tap(data => { if (this.formDirective && this.isFormMode) { this.formDirective.form.patchValue(data, { coerce: true, strict: true, }); if (this.isFormMode) { this.formDirective.form.disable(); } } }), shareReplay(1)); if (this.dataSource) { this.hasError$ = this.dataSource.hasError$ ?? this.hasError$; this.dataLoading$ = this.dataSource.loading$ ?? this.dataLoading$; } if (this.formDirective && this.isFormMode) { this.formDirective.form.disabledWhile(combineLatest([ this._editMode$, this._mode$, ]).pipe(map(([editMode, mode]) => !editMode && mode === 'form')), { onlySelf: false }); } } ngOnDestroy() { this.dataSource?.disconnect(this.viewer); this._routerEventSubscription?.unsubscribe(); } enableEditMode(skipPatchValue = false) { if (!this.isFormModeOrHasAnyEditCells) { if (isDevMode()) { console.warn('Can not enable edit mode if the mode is not form'); } return; } if (!this.formDirective) { if (isDevMode()) { console.warn('Can not enable edit mode without a form directive'); } return; } this.editMode = true; if (!skipPatchValue && this.data) { this.formDirective.form.patchValue(this.data, { coerce: true, strict: true, }); } } disableEditMode() { if (!this.isFormModeOrHasAnyEditCells) { if (isDevMode()) { console.warn('Can not disable edit mode if the mode is not form'); } return; } if (!this.formDirective) { if (isDevMode()) { console.warn('Can not enable edit mode without a form directive'); } return; } this.editMode = false; } submit() { if (!this.isFormModeOrHasAnyEditCells) { if (isDevMode()) { console.warn('Can not submit if the mode is not form'); } return; } if (!this.formDirective) { if (isDevMode()) { console.warn('Can not support without a form directive'); } return; } this.loading$.enable(); this.formDirective.form.markAllAsDirty(); this.formDirective.form.markAllAsTouched(); this.formDirective.cdr.markForCheck(); this.formDirective.rxapSubmit.pipe(take(1), tap(() => { this.disableEditMode(); })).subscribe(); this.formDirective.invalidSubmit.pipe(take(1), tap(() => { this.loading$.disable(); })).subscribe(); this.formDirective.submitError$.pipe(filter(Boolean), // if the error is undefined we do not want to disable the loading take(1), tap(() => { this.loading$.disable(); this.enableEditMode(true); })).subscribe(); this.formDirective.submitSuccessful$.pipe(take(1), tap(() => { this.loading$.disable(); this.refresh(); })).subscribe(); this.formDirective.onSubmit(new Event('submit')); } reset() { if (this.formDirective && this.data && this.isFormModeOrHasAnyEditCells) { this.formDirective.form.patchValue(this.data, { coerce: true, strict: true, }); } } refresh() { if (this.dataSource) { this.dataSource.refresh(); } else if (isDevMode()) { console.warn('can not refresh the data. data source is not defined'); } } cancel() { this.reset(); this.disableEditMode(); } retry() { this.refresh(); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.3", ngImport: i0, type: DataGridComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i1.Router }, { token: i2.FormDirective, optional: true }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.3", type: DataGridComponent, isStandalone: true, selector: "rxap-data-grid", inputs: { header: "header", dataSource: "dataSource", viewer: "viewer", data: "data", displayProperties: "displayProperties", hideEmptyProperties: "hideEmptyProperties", mode: "mode" }, outputs: { editModeChange: "editModeChange" }, queries: [{ propertyName: "rows", predicate: DataGridRowDefDirective }], ngImport: i0, template: "<div *ngIf=\"(hasError$ | async)\" class=\"flex flex-col items-center justify-center gap-y-8\">\n <span i18n>Something has gone wrong!</span>\n <button (click)=\"retry()\" i18n mat-stroked-button type=\"button\">Retry</button>\n</div>\n<div *ngIf=\"(dataLoading$ | async)\" class=\"flex flex-col items-center justify-center gap-y-8\">\n <mat-spinner></mat-spinner>\n</div>\n<!-- if the dataSource input is used. It is possible the the hasError$ or loading$ property is === EMPTY. this would\nresults in an null as output. If the === false check is used this can result into the behavior that the data grid is\nnever shown. -->\n<!-- eslint-disable-next-line @angular-eslint/template/no-negated-async -->\n<table *ngIf=\"!(hasError$ | async) && !(dataLoading$ | async)\" class=\"w-full table-auto\">\n <thead *ngIf=\"header\">\n <tr>\n <th class=\"py-2\" i18n>Label</th>\n <th class=\"py-2\" i18n>Value</th>\n </tr>\n </thead>\n <tbody>\n <ng-template [ngForOf]=\"rows$ | async\" let-first=\"first\" let-row ngFor>\n <ng-template\n [ngIf]=\"(!displayProperties || row.isSubHeader || (row.name && displayProperties.includes(row.name))) && (!hideEmptyProperties || false === (data$ | isEmpty: row.name | async))\">\n <!-- region horizontal view -->\n <ng-template [ngIfElse]=\"normal\" [ngIf]=\"row.flip\">\n <tr>\n <td class=\"py-2\" colspan=\"2\">\n <mat-divider></mat-divider>\n </td>\n </tr>\n <tr [attr.data-name]=\"row.name + '-header'\">\n <ng-container\n *ngTemplateOutlet=\"row.headerCell?.template ?? defaultHeaderCell; context: { $implicit: row.name }\"></ng-container>\n </tr>\n <tr [attr.data-name]=\"row.name + '-value'\">\n <ng-template [ngIfThen]=\"content\" [ngIf]=\"row.name\"></ng-template>\n </tr>\n <tr>\n <td class=\"py-4\" colspan=\"2\">\n <mat-divider></mat-divider>\n </td>\n </tr>\n </ng-template>\n <!-- endregion -->\n <!-- region normal view -->\n <ng-template #normal>\n <tr *ngIf=\"!first && row.isSubHeader\">\n <td class=\"py-4\" colspan=\"2\">\n <mat-divider></mat-divider>\n </td>\n </tr>\n <tr [attr.data-name]=\"row.name\" [ngClass]=\"{ 'sub-header text-2xl': row.isSubHeader }\">\n <ng-container\n *ngTemplateOutlet=\"row.headerCell?.template ?? defaultHeaderCell; context: { $implicit: row.name }\"></ng-container>\n <ng-template [ngIfThen]=\"content\" [ngIf]=\"row.name\"></ng-template>\n </tr>\n </ng-template>\n <!-- endregion -->\n </ng-template>\n <ng-template #content>\n <td [attr.colspan]=\"row.flip ? 2 : 1\" [ngClass]=\"{\n 'h-20': row.editCell?.template && (isEditMode || isFormMode) && false,\n }\" class=\"pl-8 pr-4 w-full py-2\">\n <ng-template [ngIfThen]=\"editMode\" [ngIf]=\"row.editCell?.template && (isEditMode || isFormMode)\"></ng-template>\n <ng-template [ngIfThen]=\"viewMode\"\n [ngIf]=\"!row.editCell?.template || (!isEditMode && !isFormMode)\"></ng-template>\n </td>\n <ng-template #viewMode>\n <ng-container\n *ngTemplateOutlet=\"row.cell?.template ?? defaultCell;context: { $implicit: data$ | dataGridValue: row.name | async, data: data$ | async }\"></ng-container>\n </ng-template>\n <ng-template #editMode>\n <ng-container\n *ngTemplateOutlet=\"row.editCell?.template ?? row.cell?.template ?? defaultCell;context: { $implicit: data$ | dataGridValue: row.name | async, data: data$ | async }\"></ng-container>\n </ng-template>\n </ng-template>\n </ng-template>\n </tbody>\n <tfoot *ngIf=\"hasAnyEditCells\">\n <tr>\n <td class=\"py-2\" colspan=\"2\">\n <ng-template [ngIfElse]=\"viewModeButton\" [ngIf]=\"isEditMode\">\n <div class=\"pt-8 flex flex-col gap-y-6\">\n <div class=\"flex flex-row gap-x-6 items-center justify-start\">\n <button (click)=\"submit()\" [disabled]=\"loading$ | async\" color=\"primary\" mat-raised-button type=\"button\">\n <span class=\"flex flex-row gap-x-6 items-center justify-center\">\n <span i18n>Save</span>\n <mat-spinner *ngIf=\"loading$ | async\" color=\"accent\" diameter=\"15\"></mat-spinner>\n </span>\n </button>\n <button (click)=\"reset()\" [disabled]=\"loading$ | async\" mat-stroked-button type=\"button\">\n <ng-container i18n>Reset</ng-container>\n </button>\n <button (click)=\"cancel()\" [disabled]=\"loading$ | async\" color=\"warn\" mat-stroked-button type=\"button\">\n <ng-container i18n>Cancel</ng-container>\n </button>\n <button (click)=\"logCurrentFormState()\" *ngIf=\"isDevMode\" mat-button type=\"button\">\n <ng-container i18n>Current Form State</ng-container>\n </button>\n </div>\n\n <ng-template rxapFormSubmitInvalid>\n <mat-error i18n>Ensure all formula fields are valid.</mat-error>\n </ng-template>\n\n <ng-template let-error rxapFormSubmitFailed>\n <mat-error>{{ error.error?.message ?? error.message }}</mat-error>\n </ng-template>\n\n <ng-template rxapFormSubmitSuccessful>\n <span i18n>Submit successfully.</span>\n </ng-template>\n\n </div>\n </ng-template>\n <ng-template #viewModeButton>\n <div class=\"pt-8\">\n <button (click)=\"enableEditMode()\" color=\"primary\" mat-raised-button type=\"button\">\n <span class=\"flex flex-row gap-x-6 items-center justify-center\">\n <span i18n>Edit</span>\n <mat-spinner *ngIf=\"loading$ | async\" color=\"accent\" diameter=\"15\"></mat-spinner>\n </span>\n </button>\n </div>\n </ng-template>\n </td>\n </tr>\n </tfoot>\n</table>\n\n<ng-template #defaultCell let-value>\n <span>{{ value }}</span>\n</ng-template>\n<ng-template #defaultHeaderCell let-name>\n <th class=\"py-2 whitespace-nowrap\">{{ name }}</th>\n</ng-template>\n", styles: [":host ::ng-deep th{text-align:right;width:auto}\n"], dependencies: [{ kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "ngmodule", type: RxapFormsModule }, { kind: "directive", type: i2.FormSubmitFailedDirective, selector: "[rxapFormSubmitFailed]" }, { kind: "directive", type: i2.FormSubmitSuccessfulDirective, selector: "[rxapFormSubmitSuccessful]" }, { kind: "directive", type: i2.FormSubmitInvalidDirective, selector: "[rxapFormSubmitInvalid]" }, { kind: "pipe", type: DataGridValuePipe, name: "dataGridValue" }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i4.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "ngmodule", type: MatDividerModule }, { kind: "component", type: i5.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "directive", type: i6.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "pipe", type: IsEmptyPipe, name: "isEmpty" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.3", ngImport: i0, type: DataGridComponent, decorators: [{ type: Component, args: [{ selector: 'rxap-data-grid', changeDetection: ChangeDetectionStrategy.OnPush, imports: [ AsyncPipe, NgIf, NgFor, MatButtonModule, MatIconModule, GetFromObjectPipe, ReplacePipe, EscapeQuotationMarkPipe, RxapFormsModule, DataGridValuePipe, MatProgressSpinnerModule, MatDividerModule, NgTemplateOutlet, MatFormFieldModule, NgClass, IsEmptyPipe, ], template: "<div *ngIf=\"(hasError$ | async)\" class=\"flex flex-col items-center justify-center gap-y-8\">\n <span i18n>Something has gone wrong!</span>\n <button (click)=\"retry()\" i18n mat-stroked-button type=\"button\">Retry</button>\n</div>\n<div *ngIf=\"(dataLoading$ | async)\" class=\"flex flex-col items-center justify-center gap-y-8\">\n <mat-spinner></mat-spinner>\n</div>\n<!-- if the dataSource input is used. It is possible the the hasError$ or loading$ property is === EMPTY. this would\nresults in an null as output. If the === false check is used this can result into the behavior that the data grid is\nnever shown. -->\n<!-- eslint-disable-next-line @angular-eslint/template/no-negated-async -->\n<table *ngIf=\"!(hasError$ | async) && !(dataLoading$ | async)\" class=\"w-full table-auto\">\n <thead *ngIf=\"header\">\n <tr>\n <th class=\"py-2\" i18n>Label</th>\n <th class=\"py-2\" i18n>Value</th>\n </tr>\n </thead>\n <tbody>\n <ng-template [ngForOf]=\"rows$ | async\" let-first=\"first\" let-row ngFor>\n <ng-template\n [ngIf]=\"(!displayProperties || row.isSubHeader || (row.name && displayProperties.includes(row.name))) && (!hideEmptyProperties || false === (data$ | isEmpty: row.name | async))\">\n <!-- region horizontal view -->\n <ng-template [ngIfElse]=\"normal\" [ngIf]=\"row.flip\">\n <tr>\n <td class=\"py-2\" colspan=\"2\">\n <mat-divider></mat-divider>\n </td>\n </tr>\n <tr [attr.data-name]=\"row.name + '-header'\">\n <ng-container\n *ngTemplateOutlet=\"row.headerCell?.template ?? defaultHeaderCell; context: { $implicit: row.name }\"></ng-container>\n </tr>\n <tr [attr.data-name]=\"row.name + '-value'\">\n <ng-template [ngIfThen]=\"content\" [ngIf]=\"row.name\"></ng-template>\n </tr>\n <tr>\n <td class=\"py-4\" colspan=\"2\">\n <mat-divider></mat-divider>\n </td>\n </tr>\n </ng-template>\n <!-- endregion -->\n <!-- region normal view -->\n <ng-template #normal>\n <tr *ngIf=\"!first && row.isSubHeader\">\n <td class=\"py-4\" colspan=\"2\">\n <mat-divider></mat-divider>\n </td>\n </tr>\n <tr [attr.data-name]=\"row.name\" [ngClass]=\"{ 'sub-header text-2xl': row.isSubHeader }\">\n <ng-container\n *ngTemplateOutlet=\"row.headerCell?.template ?? defaultHeaderCell; context: { $implicit: row.name }\"></ng-container>\n <ng-template [ngIfThen]=\"content\" [ngIf]=\"row.name\"></ng-template>\n </tr>\n </ng-template>\n <!-- endregion -->\n </ng-template>\n <ng-template #content>\n <td [attr.colspan]=\"row.flip ? 2 : 1\" [ngClass]=\"{\n 'h-20': row.editCell?.template && (isEditMode || isFormMode) && false,\n }\" class=\"pl-8 pr-4 w-full py-2\">\n <ng-template [ngIfThen]=\"editMode\" [ngIf]=\"row.editCell?.template && (isEditMode || isFormMode)\"></ng-template>\n <ng-template [ngIfThen]=\"viewMode\"\n [ngIf]=\"!row.editCell?.template || (!isEditMode && !isFormMode)\"></ng-template>\n </td>\n <ng-template #viewMode>\n <ng-container\n *ngTemplateOutlet=\"row.cell?.template ?? defaultCell;context: { $implicit: data$ | dataGridValue: row.name | async, data: data$ | async }\"></ng-container>\n </ng-template>\n <ng-template #editMode>\n <ng-container\n *ngTemplateOutlet=\"row.editCell?.template ?? row.cell?.template ?? defaultCell;context: { $implicit: data$ | dataGridValue: row.name | async, data: data$ | async }\"></ng-container>\n </ng-template>\n </ng-template>\n </ng-template>\n </tbody>\n <tfoot *ngIf=\"hasAnyEditCells\">\n <tr>\n <td class=\"py-2\" colspan=\"2\">\n <ng-template [ngIfElse]=\"viewModeButton\" [ngIf]=\"isEditMode\">\n <div class=\"pt-8 flex flex-col gap-y-6\">\n <div class=\"flex flex-row gap-x-6 items-center justify-start\">\n <button (click)=\"submit()\" [disabled]=\"loading$ | async\" color=\"primary\" mat-raised-button type=\"button\">\n <span class=\"flex flex-row gap-x-6 items-center justify-center\">\n <span i18n>Save</span>\n <mat-spinner *ngIf=\"loading$ | async\" color=\"accent\" diameter=\"15\"></mat-spinner>\n </span>\n </button>\n <button (click)=\"reset()\" [disabled]=\"loading$ | async\" mat-stroked-button type=\"button\">\n <ng-container i18n>Reset</ng-container>\n </button>\n <button (click)=\"cancel()\" [disabled]=\"loading$ | async\" color=\"warn\" mat-stroked-button type=\"button\">\n <ng-container i18n>Cancel</ng-container>\n </button>\n <button (click)=\"logCurrentFormState()\" *ngIf=\"isDevMode\" mat-button type=\"button\">\n <ng-container i18n>Current Form State</ng-container>\n </button>\n </div>\n\n <ng-template rxapFormSubmitInvalid>\n <mat-error i18n>Ensure all formula fields are valid.</mat-error>\n </ng-template>\n\n <ng-template let-error rxapFormSubmitFailed>\n <mat-error>{{ error.error?.message ?? error.message }}</mat-error>\n </ng-template>\n\n <ng-template rxapFormSubmitSuccessful>\n <span i18n>Submit successfully.</span>\n </ng-template>\n\n </div>\n </ng-template>\n <ng-template #viewModeButton>\n <div class=\"pt-8\">\n <button (click)=\"enableEditMode()\" color=\"primary\" mat-raised-button type=\"button\">\n <span class=\"flex flex-row gap-x-6 items-center justify-center\">\n <span i18n>Edit</span>\n <mat-spinner *ngIf=\"loading$ | async\" color=\"accent\" diameter=\"15\"></mat-spinner>\n </span>\n </button>\n </div>\n </ng-template>\n </td>\n </tr>\n </tfoot>\n</table>\n\n<ng-template #defaultCell let-value>\n <span>{{ value }}</span>\n</ng-template>\n<ng-template #defaultHeaderCell let-name>\n <th class=\"py-2 whitespace-nowrap\">{{ name }}</th>\n</ng-template>\n", styles: [":host ::ng-deep th{text-align:right;width:auto}\n"] }] }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i1.Router }, { type: i2.FormDirective, decorators: [{ type: Optional }] }], propDecorators: { header: [{ type: Input }], dataSource: [{ type: Input }], viewer: [{ type: Input }], data: [{ type: Input }], displayProperties: [{ type: Input }], hideEmptyProperties: [{ type: Input }], rows: [{ type: ContentChildren, args: [DataGridRowDefDirective] }], editModeChange: [{ type: Output }], mode: [{ type: Input }] } }); class DataGridModule { static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.3", ngImport: i0, type: DataGridModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); } static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.1.3", ngImport: i0, type: DataGridModule, imports: [DataGridComponent, DataGridRowDefDirective, DataGridHeaderCellDefDirective, DataGridCellDefDirective, DataGridEditCellDefDirective], exports: [DataGridComponent, DataGridRowDefDirective, DataGridHeaderCellDefDirective, DataGridCellDefDirective, DataGridEditCellDefDirective] }); } static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.1.3", ngImport: i0, type: DataGridModule, imports: [DataGridComponent] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.3", ngImport: i0, type: DataGridModule, decorators: [{ type: NgModule, args: [{ imports: [ DataGridComponent, DataGridRowDefDirective, DataGridHeaderCellDefDirective, DataGridCellDefDirective, DataGridEditCellDefDirective, ], exports: [ DataGridComponent, DataGridRowDefDirective, DataGridHeaderCellDefDirective, DataGridCellDefDirective, DataGridEditCellDefDirective, ], }] }] }); // region // endregion /** * Generated bundle index. Do not edit. */ export { DataGridCellDefDirective, DataGridComponent, DataGridEditCellDefDirective, DataGridHeaderCellDefDirective, DataGridMode, DataGridModule, DataGridRowDefDirective, DataGridValuePipe, IsEmptyPipe }; //# sourceMappingURL=rxap-data-grid.mjs.map