@trudb/tru-common-lib 0.3.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,7 +5,7 @@ import * as i1$1 from '@angular/common';
5
5
  import { CommonModule, DatePipe, NgIf, NgClass } from '@angular/common';
6
6
  import * as i8 from '@angular/forms';
7
7
  import { FormsModule, FormControl, ReactiveFormsModule, FormGroup, Validators } from '@angular/forms';
8
- import { BehaviorSubject, defer, from, of, Subject, Observable, merge, fromEvent, Subscription, interval, skip, forkJoin, first, finalize, throwError } from 'rxjs';
8
+ import { BehaviorSubject, merge, defer, from, of, Subject, Observable, fromEvent, Subscription, interval, skip, forkJoin, first, finalize, throwError } from 'rxjs';
9
9
  import * as _ from 'underscore';
10
10
  import ___default from 'underscore';
11
11
  import * as i1$2 from '@angular/common/http';
@@ -361,7 +361,6 @@ class TruAppEnvironment {
361
361
  _appName;
362
362
  _appDescription;
363
363
  _appUri;
364
- _enablePasswordStrength;
365
364
  _backgroundImageUri;
366
365
  _environmentWarning;
367
366
  _loginFormTitle;
@@ -397,7 +396,6 @@ class TruAppEnvironment {
397
396
  this._appName = '';
398
397
  this._appDescription = '';
399
398
  this._appUri = '';
400
- this._enablePasswordStrength = true;
401
399
  this._backgroundImageUri = '';
402
400
  this._environmentWarning = '';
403
401
  this._loginFormTitle = '';
@@ -434,13 +432,6 @@ class TruAppEnvironment {
434
432
  set appDescription(description) {
435
433
  this._appDescription = description;
436
434
  }
437
- get enablePasswordStrength() {
438
- return this._enablePasswordStrength;
439
- }
440
- ;
441
- set enablePasswordStrength(enable) {
442
- this._enablePasswordStrength = enable;
443
- }
444
435
  get backgroundImageUri() {
445
436
  return this._backgroundImageUri;
446
437
  }
@@ -1010,7 +1001,11 @@ class TruPasswordDialog {
1010
1001
  message = null;
1011
1002
  showPassword = false;
1012
1003
  passwordScore = null;
1004
+ passwordWarning = null;
1005
+ passwordSuggestions = [];
1013
1006
  passwordStrengthLevels = [0, 1, 2, 3, 4];
1007
+ passwordStrengthLabels = ['Very weak', 'Weak', 'Fair', 'Strong', 'Very strong'];
1008
+ minimumPasswordScore = 1;
1014
1009
  errorMsg = null;
1015
1010
  busy = false;
1016
1011
  baseUrl = '';
@@ -1026,28 +1021,24 @@ class TruPasswordDialog {
1026
1021
  onShowPasswordClicked = () => {
1027
1022
  this.showPassword = this.showPasswordControl.value;
1028
1023
  };
1029
- canSubmit = () => {
1030
- let canSubmit = false;
1031
- if (this.admin) {
1032
- canSubmit =
1033
- this.newPasswordControl.value &&
1034
- this.confirmNewPasswordControl.value &&
1035
- this.passwordScore !== null &&
1036
- this.passwordScore > 1 &&
1037
- this.newPasswordControl.value === this.confirmNewPasswordControl.value;
1038
- }
1039
- else {
1040
- canSubmit =
1041
- this.currentPasswordControl.value &&
1042
- this.newPasswordControl.value &&
1043
- this.confirmNewPasswordControl.value &&
1044
- this.passwordScore !== null &&
1045
- this.passwordScore > 1 &&
1046
- this.newPasswordControl.value === this.confirmNewPasswordControl.value;
1047
- }
1048
- return canSubmit;
1049
- };
1024
+ get validationMessage() {
1025
+ if (!this.admin && !this.currentPasswordControl.value)
1026
+ return 'Enter your current password.';
1027
+ if (!this.newPasswordControl.value)
1028
+ return 'Enter a new password.';
1029
+ if (!this.confirmNewPasswordControl.value)
1030
+ return 'Confirm your new password.';
1031
+ if (this.newPasswordControl.value !== this.confirmNewPasswordControl.value)
1032
+ return 'Passwords do not match.';
1033
+ if (this.passwordScore === null || this.passwordScore < this.minimumPasswordScore)
1034
+ return 'Choose a stronger password.';
1035
+ return null;
1036
+ }
1037
+ canSubmit = () => !this.busy && this.validationMessage === null;
1050
1038
  onConfirm = () => {
1039
+ if (!this.canSubmit())
1040
+ return;
1041
+ this.errorMsg = null;
1051
1042
  let passwordChangeRequest = new TruPasswordChangeRequest();
1052
1043
  passwordChangeRequest.currentPassword = this.currentPasswordControl.value;
1053
1044
  passwordChangeRequest.newPassword = this.newPasswordControl.value;
@@ -1067,7 +1058,9 @@ class TruPasswordDialog {
1067
1058
  this.dialogRef.close(true);
1068
1059
  }, (ex) => {
1069
1060
  this.busy = false;
1070
- this.errorMsg = ex.error;
1061
+ this.errorMsg = typeof ex.error === 'string' && ex.error
1062
+ ? ex.error
1063
+ : 'Unable to update the password. Please try again.';
1071
1064
  });
1072
1065
  }
1073
1066
  };
@@ -1075,13 +1068,16 @@ class TruPasswordDialog {
1075
1068
  this.dialogRef.close(false);
1076
1069
  };
1077
1070
  ngOnInit() {
1078
- this.subs.push(this.currentPasswordControl.valueChanges.subscribe(() => {
1071
+ this.subs.push(merge(this.currentPasswordControl.valueChanges, this.newPasswordControl.valueChanges, this.confirmNewPasswordControl.valueChanges).subscribe(() => {
1079
1072
  this.errorMsg = null;
1080
1073
  }));
1081
1074
  this.subs.push(this.newPasswordControl.valueChanges.subscribe((password) => {
1082
- this.passwordScore = password
1083
- ? passwordStrengthEstimator.check(password).score
1075
+ const result = password
1076
+ ? passwordStrengthEstimator.check(password)
1084
1077
  : null;
1078
+ this.passwordScore = result?.score ?? null;
1079
+ this.passwordWarning = result?.feedback.warning ?? null;
1080
+ this.passwordSuggestions = result?.feedback.suggestions ?? [];
1085
1081
  }));
1086
1082
  this.userRef = this.data.userRef;
1087
1083
  this.username = this.data.username;
@@ -1093,7 +1089,7 @@ class TruPasswordDialog {
1093
1089
  this.subs.forEach((s) => s.unsubscribe());
1094
1090
  }
1095
1091
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: TruPasswordDialog, deps: [{ token: i1.MatDialogRef }, { token: i1$2.HttpClient }, { token: TruAppEnvironment }, { token: TruUiNotification }, { token: MAT_DIALOG_DATA }], target: i0.ɵɵFactoryTarget.Component });
1096
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: TruPasswordDialog, isStandalone: true, selector: "tru-password-dialog", ngImport: i0, template: "<div mat-dialog-title class=\"mat-dialog-title\">\r\n <p class=\"tru-password-dialog-title\">{{title}}</p>\r\n <p class=\"tru-password-dialog-username\">for {{username}}</p>\r\n</div>\r\n\r\n<div mat-dialog-content class=\"mat-dialog-content tru-control-container tru-password-dialog\">\r\n <p>{{message}}</p>\r\n\r\n <div class=\"tru-password-dialog-control-wrapper\" *ngIf=\"!admin\">\r\n <mat-label>Current Password</mat-label>\r\n <mat-form-field>\r\n <input #currentInput\r\n matInput\r\n autocomplete=\"off webauthn\"\r\n [formControl]=\"currentPasswordControl\"\r\n [type]=\"showPassword ? 'text' : 'password'\" />\r\n </mat-form-field>\r\n <p class=\"tru-password-dialog-error-msg\" *ngIf=\"errorMsg\">{{errorMsg}}</p>\r\n </div>\r\n <div class=\"tru-password-dialog-control-wrapper\">\r\n <mat-label>New Password</mat-label>\r\n <mat-form-field>\r\n <input #newInput\r\n matInput\r\n autocomplete=\"off webauthn\"\r\n [formControl]=\"newPasswordControl\"\r\n [type]=\"showPassword ? 'text' : 'password'\" />\r\n </mat-form-field>\r\n <div class=\"tru-password-strength-meter score-{{passwordScore}}\"\r\n role=\"meter\"\r\n aria-label=\"Password strength\"\r\n aria-valuemin=\"0\"\r\n aria-valuemax=\"4\"\r\n [attr.aria-valuenow]=\"passwordScore\">\r\n @for (level of passwordStrengthLevels; track level) {\r\n <span [class.active]=\"passwordScore !== null && passwordScore >= level\"></span>\r\n }\r\n </div>\r\n </div>\r\n <div class=\"tru-password-dialog-control-wrapper\">\r\n <mat-label>Confirm New Password</mat-label>\r\n <mat-form-field>\r\n <input #confirmInput\r\n matInput\r\n autocomplete=\"off webauthn\"\r\n [formControl]=\"confirmNewPasswordControl\"\r\n [type]=\"showPassword ? 'text' : 'password'\" />\r\n </mat-form-field>\r\n </div>\r\n <mat-checkbox [formControl]=\"showPasswordControl\"\r\n (click)=\"onShowPasswordClicked()\"\r\n [labelPosition]=\"'before'\">\r\n Show Password\r\n </mat-checkbox>\r\n</div>\r\n\r\n<div mat-dialog-actions class=\"mat-dialog-actions\">\r\n <button mat-raised-button (click)=\"onDismiss()\">Cancel</button>\r\n <button [disabled]=\"!canSubmit() || busy || errorMsg ? 'disabled' : null\" mat-raised-button color=\"primary\" (click)=\"onConfirm()\">Submit</button>\r\n</div>\r\n", styles: ["", "::ng-deep .mat-mdc-dialog-surface{padding:20px}.mat-mdc-dialog-title:before{content:unset!important;height:unset!important;padding:0 0 10px!important}.mdc-dialog__title{padding:20px 0 10px!important}.mdc-dialog__content{padding:10px 0 15px!important;overflow:hidden}.mat-mdc-dialog-content p{margin-bottom:0;font-size:14px;color:#545454}.mat-mdc-dialog-actions{padding:0!important}.mat-mdc-dialog-actions button{margin-right:10px}.tru-password-dialog .tru-password-dialog-control-wrapper{margin-bottom:20px}.tru-password-dialog-title{font-size:26px}.tru-password-dialog-username{font-size:14px;margin:0;color:var(--mat-dialog-supporting-text-color, rgba(0, 0, 0, .6))}.tru-password-dialog-error-msg{color:red!important;font-size:11px!important}.tru-password-strength-meter{display:flex;gap:4px;margin-top:4px}.tru-password-strength-meter span{background-color:#ddd;flex:1;height:4px}.tru-password-strength-meter.score-0 span.active{background-color:#d32f2f}.tru-password-strength-meter.score-1 span.active{background-color:#f57c00}.tru-password-strength-meter.score-2 span.active{background-color:#fbc02d}.tru-password-strength-meter.score-3 span.active{background-color:#7cb342}.tru-password-strength-meter.score-4 span.active{background-color:#388e3c}mat-checkbox{float:right!important}.std-control.edit{margin-bottom:10px;margin-right:10px}::ng-deep .tru-password-dialog .mat-mdc-form-field{display:inline!important}::ng-deep .tru-password-dialog .mat-mdc-form-field-infix{padding:0!important;min-height:unset!important}::ng-deep .tru-password-dialog mat-label{font-size:14px;font-weight:700}::ng-deep .tru-password-dialog input{font-size:12px;padding-left:5px}::ng-deep .tru-password-dialog .mat-mdc-form-field-bottom-align:before{content:unset!important}::ng-deep .tru-password-dialog path{color:#a9a9a9}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i8.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i8.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i8.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MaterialModule }, { kind: "component", type: i3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i4.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "directive", type: i1.MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: i1.MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "directive", type: i1.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "directive", type: i7.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "component", type: i7.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i7.MatLabel, selector: "mat-label" }], changeDetection: i0.ChangeDetectionStrategy.Eager });
1092
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: TruPasswordDialog, isStandalone: true, selector: "tru-password-dialog", ngImport: i0, template: "<div mat-dialog-title class=\"mat-dialog-title\">\r\n <p class=\"tru-password-dialog-title\">{{title}}</p>\r\n <p class=\"tru-password-dialog-username\">for {{username}}</p>\r\n</div>\r\n\r\n<div mat-dialog-content class=\"mat-dialog-content tru-control-container tru-password-dialog\">\r\n <p>{{message}}</p>\r\n\r\n <div class=\"tru-password-dialog-control-wrapper\" *ngIf=\"!admin\">\r\n <mat-label>Current Password</mat-label>\r\n <mat-form-field>\r\n <input #currentInput\r\n matInput\r\n autocomplete=\"off webauthn\"\r\n [formControl]=\"currentPasswordControl\"\r\n [type]=\"showPassword ? 'text' : 'password'\" />\r\n </mat-form-field>\r\n </div>\r\n <div class=\"tru-password-dialog-control-wrapper\">\r\n <mat-label>New Password</mat-label>\r\n <mat-form-field>\r\n <input #newInput\r\n matInput\r\n autocomplete=\"off webauthn\"\r\n [formControl]=\"newPasswordControl\"\r\n [type]=\"showPassword ? 'text' : 'password'\" />\r\n </mat-form-field>\r\n <div class=\"tru-password-strength-meter score-{{passwordScore}}\"\r\n role=\"meter\"\r\n aria-label=\"Password strength\"\r\n aria-valuemin=\"0\"\r\n aria-valuemax=\"4\"\r\n [attr.aria-valuenow]=\"passwordScore\"\r\n [attr.aria-valuetext]=\"passwordScore === null ? 'No password entered' : passwordStrengthLabels[passwordScore]\">\r\n @for (level of passwordStrengthLevels; track level) {\r\n <span [class.active]=\"passwordScore !== null && passwordScore >= level\"></span>\r\n }\r\n </div>\r\n <div class=\"tru-password-feedback\" aria-live=\"polite\">\r\n <p>Minimum strength: {{passwordStrengthLabels[minimumPasswordScore]}} (orange).</p>\r\n @if (passwordScore !== null) {\r\n <p>Strength: {{passwordStrengthLabels[passwordScore]}}.</p>\r\n }\r\n <p *ngIf=\"passwordWarning\">{{passwordWarning}}</p>\r\n @for (suggestion of passwordSuggestions; track suggestion) {\r\n <p>{{suggestion}}</p>\r\n }\r\n </div>\r\n </div>\r\n <div class=\"tru-password-dialog-control-wrapper\">\r\n <mat-label>Confirm New Password</mat-label>\r\n <mat-form-field>\r\n <input #confirmInput\r\n matInput\r\n autocomplete=\"off webauthn\"\r\n [formControl]=\"confirmNewPasswordControl\"\r\n [type]=\"showPassword ? 'text' : 'password'\" />\r\n </mat-form-field>\r\n </div>\r\n <p class=\"tru-password-dialog-status\" role=\"status\">{{busy ? 'Updating password...' : validationMessage}}</p>\r\n <p class=\"tru-password-dialog-error-msg\" role=\"alert\" *ngIf=\"errorMsg\">{{errorMsg}}</p>\r\n <mat-checkbox [formControl]=\"showPasswordControl\"\r\n (click)=\"onShowPasswordClicked()\"\r\n [labelPosition]=\"'before'\">\r\n Show Password\r\n </mat-checkbox>\r\n</div>\r\n\r\n<div mat-dialog-actions class=\"mat-dialog-actions\">\r\n <button mat-raised-button (click)=\"onDismiss()\">Cancel</button>\r\n <button [disabled]=\"!canSubmit()\" mat-raised-button color=\"primary\" (click)=\"onConfirm()\">Submit</button>\r\n</div>\r\n", styles: ["", "::ng-deep .mat-mdc-dialog-surface{padding:20px}.mat-mdc-dialog-title:before{content:unset!important;height:unset!important;padding:0 0 10px!important}.mdc-dialog__title{padding:20px 0 10px!important}.mdc-dialog__content{padding:10px 0 15px!important;overflow-x:hidden;overflow-y:auto}.mat-mdc-dialog-content p{margin-bottom:0;font-size:14px;color:#545454}.mat-mdc-dialog-actions{padding:0!important}.mat-mdc-dialog-actions button{margin-right:10px}.tru-password-dialog .tru-password-dialog-control-wrapper{margin-bottom:20px}.tru-password-dialog-title{font-size:26px}.tru-password-dialog-username{font-size:14px;margin:0;color:var(--mat-dialog-supporting-text-color, rgba(0, 0, 0, .6))}.tru-password-dialog-error-msg{color:red!important;font-size:11px!important}.tru-password-feedback,.tru-password-dialog-status{margin:8px 0}.tru-password-strength-meter{display:flex;gap:4px;margin-top:4px}.tru-password-strength-meter span{background-color:#ddd;flex:1;height:4px}.tru-password-strength-meter.score-0 span.active{background-color:#d32f2f}.tru-password-strength-meter.score-1 span.active{background-color:#f57c00}.tru-password-strength-meter.score-2 span.active{background-color:#fbc02d}.tru-password-strength-meter.score-3 span.active{background-color:#7cb342}.tru-password-strength-meter.score-4 span.active{background-color:#388e3c}mat-checkbox{float:right!important}.std-control.edit{margin-bottom:10px;margin-right:10px}::ng-deep .tru-password-dialog .mat-mdc-form-field{display:inline!important}::ng-deep .tru-password-dialog .mat-mdc-form-field-infix{padding:0!important;min-height:unset!important}::ng-deep .tru-password-dialog mat-label{font-size:14px;font-weight:700}::ng-deep .tru-password-dialog input{font-size:12px;padding-left:5px}::ng-deep .tru-password-dialog .mat-mdc-form-field-bottom-align:before{content:unset!important}::ng-deep .tru-password-dialog path{color:#a9a9a9}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i8.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i8.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i8.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MaterialModule }, { kind: "component", type: i3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i4.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "directive", type: i1.MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: i1.MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "directive", type: i1.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "directive", type: i7.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "component", type: i7.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i7.MatLabel, selector: "mat-label" }], changeDetection: i0.ChangeDetectionStrategy.Eager });
1097
1093
  }
1098
1094
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: TruPasswordDialog, decorators: [{
1099
1095
  type: Component,
@@ -1102,7 +1098,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImpor
1102
1098
  FormsModule,
1103
1099
  ReactiveFormsModule,
1104
1100
  MaterialModule,
1105
- ], template: "<div mat-dialog-title class=\"mat-dialog-title\">\r\n <p class=\"tru-password-dialog-title\">{{title}}</p>\r\n <p class=\"tru-password-dialog-username\">for {{username}}</p>\r\n</div>\r\n\r\n<div mat-dialog-content class=\"mat-dialog-content tru-control-container tru-password-dialog\">\r\n <p>{{message}}</p>\r\n\r\n <div class=\"tru-password-dialog-control-wrapper\" *ngIf=\"!admin\">\r\n <mat-label>Current Password</mat-label>\r\n <mat-form-field>\r\n <input #currentInput\r\n matInput\r\n autocomplete=\"off webauthn\"\r\n [formControl]=\"currentPasswordControl\"\r\n [type]=\"showPassword ? 'text' : 'password'\" />\r\n </mat-form-field>\r\n <p class=\"tru-password-dialog-error-msg\" *ngIf=\"errorMsg\">{{errorMsg}}</p>\r\n </div>\r\n <div class=\"tru-password-dialog-control-wrapper\">\r\n <mat-label>New Password</mat-label>\r\n <mat-form-field>\r\n <input #newInput\r\n matInput\r\n autocomplete=\"off webauthn\"\r\n [formControl]=\"newPasswordControl\"\r\n [type]=\"showPassword ? 'text' : 'password'\" />\r\n </mat-form-field>\r\n <div class=\"tru-password-strength-meter score-{{passwordScore}}\"\r\n role=\"meter\"\r\n aria-label=\"Password strength\"\r\n aria-valuemin=\"0\"\r\n aria-valuemax=\"4\"\r\n [attr.aria-valuenow]=\"passwordScore\">\r\n @for (level of passwordStrengthLevels; track level) {\r\n <span [class.active]=\"passwordScore !== null && passwordScore >= level\"></span>\r\n }\r\n </div>\r\n </div>\r\n <div class=\"tru-password-dialog-control-wrapper\">\r\n <mat-label>Confirm New Password</mat-label>\r\n <mat-form-field>\r\n <input #confirmInput\r\n matInput\r\n autocomplete=\"off webauthn\"\r\n [formControl]=\"confirmNewPasswordControl\"\r\n [type]=\"showPassword ? 'text' : 'password'\" />\r\n </mat-form-field>\r\n </div>\r\n <mat-checkbox [formControl]=\"showPasswordControl\"\r\n (click)=\"onShowPasswordClicked()\"\r\n [labelPosition]=\"'before'\">\r\n Show Password\r\n </mat-checkbox>\r\n</div>\r\n\r\n<div mat-dialog-actions class=\"mat-dialog-actions\">\r\n <button mat-raised-button (click)=\"onDismiss()\">Cancel</button>\r\n <button [disabled]=\"!canSubmit() || busy || errorMsg ? 'disabled' : null\" mat-raised-button color=\"primary\" (click)=\"onConfirm()\">Submit</button>\r\n</div>\r\n", styles: ["::ng-deep .mat-mdc-dialog-surface{padding:20px}.mat-mdc-dialog-title:before{content:unset!important;height:unset!important;padding:0 0 10px!important}.mdc-dialog__title{padding:20px 0 10px!important}.mdc-dialog__content{padding:10px 0 15px!important;overflow:hidden}.mat-mdc-dialog-content p{margin-bottom:0;font-size:14px;color:#545454}.mat-mdc-dialog-actions{padding:0!important}.mat-mdc-dialog-actions button{margin-right:10px}.tru-password-dialog .tru-password-dialog-control-wrapper{margin-bottom:20px}.tru-password-dialog-title{font-size:26px}.tru-password-dialog-username{font-size:14px;margin:0;color:var(--mat-dialog-supporting-text-color, rgba(0, 0, 0, .6))}.tru-password-dialog-error-msg{color:red!important;font-size:11px!important}.tru-password-strength-meter{display:flex;gap:4px;margin-top:4px}.tru-password-strength-meter span{background-color:#ddd;flex:1;height:4px}.tru-password-strength-meter.score-0 span.active{background-color:#d32f2f}.tru-password-strength-meter.score-1 span.active{background-color:#f57c00}.tru-password-strength-meter.score-2 span.active{background-color:#fbc02d}.tru-password-strength-meter.score-3 span.active{background-color:#7cb342}.tru-password-strength-meter.score-4 span.active{background-color:#388e3c}mat-checkbox{float:right!important}.std-control.edit{margin-bottom:10px;margin-right:10px}::ng-deep .tru-password-dialog .mat-mdc-form-field{display:inline!important}::ng-deep .tru-password-dialog .mat-mdc-form-field-infix{padding:0!important;min-height:unset!important}::ng-deep .tru-password-dialog mat-label{font-size:14px;font-weight:700}::ng-deep .tru-password-dialog input{font-size:12px;padding-left:5px}::ng-deep .tru-password-dialog .mat-mdc-form-field-bottom-align:before{content:unset!important}::ng-deep .tru-password-dialog path{color:#a9a9a9}\n"] }]
1101
+ ], template: "<div mat-dialog-title class=\"mat-dialog-title\">\r\n <p class=\"tru-password-dialog-title\">{{title}}</p>\r\n <p class=\"tru-password-dialog-username\">for {{username}}</p>\r\n</div>\r\n\r\n<div mat-dialog-content class=\"mat-dialog-content tru-control-container tru-password-dialog\">\r\n <p>{{message}}</p>\r\n\r\n <div class=\"tru-password-dialog-control-wrapper\" *ngIf=\"!admin\">\r\n <mat-label>Current Password</mat-label>\r\n <mat-form-field>\r\n <input #currentInput\r\n matInput\r\n autocomplete=\"off webauthn\"\r\n [formControl]=\"currentPasswordControl\"\r\n [type]=\"showPassword ? 'text' : 'password'\" />\r\n </mat-form-field>\r\n </div>\r\n <div class=\"tru-password-dialog-control-wrapper\">\r\n <mat-label>New Password</mat-label>\r\n <mat-form-field>\r\n <input #newInput\r\n matInput\r\n autocomplete=\"off webauthn\"\r\n [formControl]=\"newPasswordControl\"\r\n [type]=\"showPassword ? 'text' : 'password'\" />\r\n </mat-form-field>\r\n <div class=\"tru-password-strength-meter score-{{passwordScore}}\"\r\n role=\"meter\"\r\n aria-label=\"Password strength\"\r\n aria-valuemin=\"0\"\r\n aria-valuemax=\"4\"\r\n [attr.aria-valuenow]=\"passwordScore\"\r\n [attr.aria-valuetext]=\"passwordScore === null ? 'No password entered' : passwordStrengthLabels[passwordScore]\">\r\n @for (level of passwordStrengthLevels; track level) {\r\n <span [class.active]=\"passwordScore !== null && passwordScore >= level\"></span>\r\n }\r\n </div>\r\n <div class=\"tru-password-feedback\" aria-live=\"polite\">\r\n <p>Minimum strength: {{passwordStrengthLabels[minimumPasswordScore]}} (orange).</p>\r\n @if (passwordScore !== null) {\r\n <p>Strength: {{passwordStrengthLabels[passwordScore]}}.</p>\r\n }\r\n <p *ngIf=\"passwordWarning\">{{passwordWarning}}</p>\r\n @for (suggestion of passwordSuggestions; track suggestion) {\r\n <p>{{suggestion}}</p>\r\n }\r\n </div>\r\n </div>\r\n <div class=\"tru-password-dialog-control-wrapper\">\r\n <mat-label>Confirm New Password</mat-label>\r\n <mat-form-field>\r\n <input #confirmInput\r\n matInput\r\n autocomplete=\"off webauthn\"\r\n [formControl]=\"confirmNewPasswordControl\"\r\n [type]=\"showPassword ? 'text' : 'password'\" />\r\n </mat-form-field>\r\n </div>\r\n <p class=\"tru-password-dialog-status\" role=\"status\">{{busy ? 'Updating password...' : validationMessage}}</p>\r\n <p class=\"tru-password-dialog-error-msg\" role=\"alert\" *ngIf=\"errorMsg\">{{errorMsg}}</p>\r\n <mat-checkbox [formControl]=\"showPasswordControl\"\r\n (click)=\"onShowPasswordClicked()\"\r\n [labelPosition]=\"'before'\">\r\n Show Password\r\n </mat-checkbox>\r\n</div>\r\n\r\n<div mat-dialog-actions class=\"mat-dialog-actions\">\r\n <button mat-raised-button (click)=\"onDismiss()\">Cancel</button>\r\n <button [disabled]=\"!canSubmit()\" mat-raised-button color=\"primary\" (click)=\"onConfirm()\">Submit</button>\r\n</div>\r\n", styles: ["::ng-deep .mat-mdc-dialog-surface{padding:20px}.mat-mdc-dialog-title:before{content:unset!important;height:unset!important;padding:0 0 10px!important}.mdc-dialog__title{padding:20px 0 10px!important}.mdc-dialog__content{padding:10px 0 15px!important;overflow-x:hidden;overflow-y:auto}.mat-mdc-dialog-content p{margin-bottom:0;font-size:14px;color:#545454}.mat-mdc-dialog-actions{padding:0!important}.mat-mdc-dialog-actions button{margin-right:10px}.tru-password-dialog .tru-password-dialog-control-wrapper{margin-bottom:20px}.tru-password-dialog-title{font-size:26px}.tru-password-dialog-username{font-size:14px;margin:0;color:var(--mat-dialog-supporting-text-color, rgba(0, 0, 0, .6))}.tru-password-dialog-error-msg{color:red!important;font-size:11px!important}.tru-password-feedback,.tru-password-dialog-status{margin:8px 0}.tru-password-strength-meter{display:flex;gap:4px;margin-top:4px}.tru-password-strength-meter span{background-color:#ddd;flex:1;height:4px}.tru-password-strength-meter.score-0 span.active{background-color:#d32f2f}.tru-password-strength-meter.score-1 span.active{background-color:#f57c00}.tru-password-strength-meter.score-2 span.active{background-color:#fbc02d}.tru-password-strength-meter.score-3 span.active{background-color:#7cb342}.tru-password-strength-meter.score-4 span.active{background-color:#388e3c}mat-checkbox{float:right!important}.std-control.edit{margin-bottom:10px;margin-right:10px}::ng-deep .tru-password-dialog .mat-mdc-form-field{display:inline!important}::ng-deep .tru-password-dialog .mat-mdc-form-field-infix{padding:0!important;min-height:unset!important}::ng-deep .tru-password-dialog mat-label{font-size:14px;font-weight:700}::ng-deep .tru-password-dialog input{font-size:12px;padding-left:5px}::ng-deep .tru-password-dialog .mat-mdc-form-field-bottom-align:before{content:unset!important}::ng-deep .tru-password-dialog path{color:#a9a9a9}\n"] }]
1106
1102
  }], ctorParameters: () => [{ type: i1.MatDialogRef }, { type: i1$2.HttpClient }, { type: TruAppEnvironment }, { type: TruUiNotification }, { type: undefined, decorators: [{
1107
1103
  type: Inject,
1108
1104
  args: [MAT_DIALOG_DATA]
@@ -7487,15 +7483,10 @@ class TruDataGridClipboard {
7487
7483
  formatter;
7488
7484
  uiNotification;
7489
7485
  modelPropertyLookup;
7490
- tableName = null;
7491
- columnName = null;
7492
- copiedCellData = null;
7493
- copiedCellValue = null;
7494
- copiedCellEntity = null;
7495
- copiedCellParams = null;
7496
- copiedCellText = null;
7497
- copiedRows = [];
7498
- copiedRowsText = null;
7486
+ copiedData = null;
7487
+ pendingCopy = null;
7488
+ copySequence = 0;
7489
+ animatedElements = [];
7499
7490
  gridElement = null;
7500
7491
  constructor(util, formatter, uiNotification, modelPropertyLookup) {
7501
7492
  this.util = util;
@@ -7503,7 +7494,17 @@ class TruDataGridClipboard {
7503
7494
  this.uiNotification = uiNotification;
7504
7495
  this.modelPropertyLookup = modelPropertyLookup;
7505
7496
  }
7497
+ bindControlEntity = (controlConfig, entity) => {
7498
+ // Off-screen columns may never run their grid value getter. Compound controls bind their child fields.
7499
+ if (controlConfig.subPropertyNames?.length) {
7500
+ controlConfig.subPropertyNames.forEach((name) => this.bindControlEntity(controlConfig[name], entity));
7501
+ }
7502
+ else {
7503
+ controlConfig.entity = entity;
7504
+ }
7505
+ };
7506
7506
  formatCellValue = (controlConfig, propertyConfig, entity, value) => {
7507
+ this.bindControlEntity(controlConfig, entity);
7507
7508
  if (propertyConfig.typeName === 'rich-text-unbounded')
7508
7509
  return this.util.htmlToPlainText(value);
7509
7510
  else if (propertyConfig.typeName === 'foreign-key' || propertyConfig.typeName === 'user-foreign-key')
@@ -7512,10 +7513,67 @@ class TruDataGridClipboard {
7512
7513
  return this.formatter.excelFormula(propertyConfig.formatter(controlConfig));
7513
7514
  };
7514
7515
  normalizeClipboardText = (value) => String(value ?? '').replace(/\r\n/g, '\n').replace(/\r/g, '\n').replace(/\n$/, '');
7515
- readClipboardText = async () => {
7516
- if (!navigator.clipboard?.readText)
7517
- throw new Error('The browser does not support reading from the clipboard.');
7518
- return navigator.clipboard.readText();
7516
+ writeClipboard = (text, data, event) => {
7517
+ const sequence = ++this.copySequence;
7518
+ this.copiedData = null;
7519
+ this.removeBorderAnimation();
7520
+ const selector = data.kind === 'rows' ? '.ag-row-selected' : '.ag-cell-focus';
7521
+ const elements = Array.from(this.gridElement?.querySelectorAll('.ag-center-cols-container ' + selector) ?? []);
7522
+ const copied = () => {
7523
+ // An earlier asynchronous write must not replace a newer row or cell copy.
7524
+ if (sequence !== this.copySequence)
7525
+ return false;
7526
+ this.copiedData = data;
7527
+ this.animatedElements = elements;
7528
+ elements.forEach(element => element.classList.add('animated-border'));
7529
+ return true;
7530
+ };
7531
+ const failed = () => {
7532
+ if (sequence === this.copySequence)
7533
+ this.uiNotification.error('Unable to copy data to the system clipboard. Please copy the selection again.');
7534
+ return false;
7535
+ };
7536
+ try {
7537
+ if (event) {
7538
+ // Native copy must be completed during the event, before the browser copies its text selection.
7539
+ event.preventDefault();
7540
+ if (!event.clipboardData)
7541
+ throw new Error('Clipboard data is unavailable.');
7542
+ event.clipboardData.setData('text/plain', text);
7543
+ this.pendingCopy = null;
7544
+ return Promise.resolve(copied());
7545
+ }
7546
+ // Context-menu actions have no ClipboardEvent. Do not mark them copied until the write succeeds.
7547
+ this.pendingCopy = navigator.clipboard.writeText(text).then(copied, failed).finally(() => {
7548
+ if (sequence === this.copySequence)
7549
+ this.pendingCopy = null;
7550
+ });
7551
+ return this.pendingCopy;
7552
+ }
7553
+ catch {
7554
+ this.pendingCopy = null;
7555
+ return Promise.resolve(failed());
7556
+ }
7557
+ };
7558
+ readClipboard = async (event) => {
7559
+ const pendingCopy = this.pendingCopy;
7560
+ const eventText = event?.clipboardData?.getData('text/plain');
7561
+ if (pendingCopy && !await pendingCopy)
7562
+ return null;
7563
+ const data = this.copiedData;
7564
+ try {
7565
+ // A paste event raised during a context-menu copy can contain the old clipboard contents.
7566
+ const text = !pendingCopy && eventText !== undefined ? eventText : await navigator.clipboard.readText();
7567
+ if (data !== this.copiedData) {
7568
+ this.uiNotification.error('The copied selection changed while pasting. Please paste again.');
7569
+ return null;
7570
+ }
7571
+ return { text: this.normalizeClipboardText(text), data };
7572
+ }
7573
+ catch {
7574
+ this.uiNotification.error('Unable to read data from the system clipboard.');
7575
+ return null;
7576
+ }
7519
7577
  };
7520
7578
  parseExternalCellValue = (clipboardText, controlConfig) => {
7521
7579
  const value = this.normalizeClipboardText(clipboardText);
@@ -7552,30 +7610,8 @@ class TruDataGridClipboard {
7552
7610
  return value;
7553
7611
  };
7554
7612
  removeBorderAnimation = () => {
7555
- var elementsWithAnimatedBorders = this.gridElement?.querySelectorAll('.animated-border');
7556
- if (elementsWithAnimatedBorders && elementsWithAnimatedBorders.length)
7557
- elementsWithAnimatedBorders.forEach((elementsWithAnimatedBorder) => {
7558
- elementsWithAnimatedBorder.classList.remove('animated-border');
7559
- });
7560
- };
7561
- addCellBorderAnimation = () => {
7562
- let rowContainerElement = this.gridElement?.querySelector('.ag-center-cols-container');
7563
- let selectedCellElement = rowContainerElement?.querySelectorAll('.ag-cell-focus')[0];
7564
- if (selectedCellElement)
7565
- selectedCellElement.classList.add('animated-border');
7566
- };
7567
- addRowBorderAnimation = () => {
7568
- var elementsWithAnimatedBorders = this.gridElement?.querySelectorAll('.animated-border');
7569
- if (elementsWithAnimatedBorders && elementsWithAnimatedBorders.length)
7570
- elementsWithAnimatedBorders.forEach((elementsWithAnimatedBorder) => {
7571
- elementsWithAnimatedBorder.classList.remove('animated-border');
7572
- });
7573
- let rowContainerElement = this.gridElement?.querySelector('.ag-center-cols-container');
7574
- let selectedRowElements = rowContainerElement?.querySelectorAll('.ag-row-selected');
7575
- if (selectedRowElements && selectedRowElements.length)
7576
- selectedRowElements.forEach((selectedRowElement) => {
7577
- selectedRowElement.classList.add('animated-border');
7578
- });
7613
+ this.animatedElements.forEach(element => element.classList.remove('animated-border'));
7614
+ this.animatedElements = [];
7579
7615
  };
7580
7616
  setGridElement(el) {
7581
7617
  this.gridElement = el;
@@ -7583,53 +7619,43 @@ class TruDataGridClipboard {
7583
7619
  clearAnimations() {
7584
7620
  this.removeBorderAnimation();
7585
7621
  }
7586
- copyCell = async (params, tableName, columnName, copiedCellData, copiedCellEntity) => {
7587
- this.tableName = tableName;
7588
- this.columnName = columnName;
7589
- this.copiedCellParams = params;
7590
- this.copiedCellData = copiedCellData;
7591
- this.copiedCellValue = copiedCellData[columnName].$;
7592
- this.copiedCellEntity = copiedCellEntity;
7593
- this.copiedCellText = this.normalizeClipboardText(params.value);
7594
- this.removeBorderAnimation();
7595
- this.addCellBorderAnimation();
7596
- try {
7597
- await navigator.clipboard.writeText(params.value);
7598
- }
7599
- catch (err) {
7600
- console.error('Failed to copy text: ', err);
7601
- }
7602
- };
7603
- pasteCell = async (params, pastedCellData, pastedCell) => {
7622
+ copyCell = async (params, tableName, columnName, copiedCellData, copiedCellEntity, event) => {
7623
+ const text = String(params.value ?? '');
7624
+ await this.writeClipboard(text, {
7625
+ kind: 'cell',
7626
+ text: this.normalizeClipboardText(text),
7627
+ typeName: copiedCellData[columnName].property.typeName,
7628
+ value: copiedCellData[columnName].$,
7629
+ entity: copiedCellEntity
7630
+ }, event);
7631
+ };
7632
+ pasteCell = async (params, pastedCellData, pastedCell, event) => {
7604
7633
  let colDef = params.colDef;
7605
7634
  let columnName = colDef.field;
7606
- if (!colDef.editable) {
7635
+ const editable = typeof colDef.editable === 'function'
7636
+ ? colDef.editable({ ...params, colDef, column: params.column })
7637
+ : colDef.editable;
7638
+ if (!editable) {
7607
7639
  this.uiNotification.error('Cannot paste into a non-editable column: ' + colDef.field);
7608
7640
  return;
7609
7641
  }
7610
- let clipboardText;
7611
- try {
7612
- clipboardText = this.normalizeClipboardText(await this.readClipboardText());
7613
- }
7614
- catch (err) {
7615
- this.uiNotification.error('Unable to read data from the system clipboard.');
7642
+ const clipboard = await this.readClipboard(event);
7643
+ if (!clipboard)
7616
7644
  return;
7617
- }
7618
7645
  const pastedCellConfig = pastedCellData[columnName];
7619
- const isInternalCopy = this.copiedCellText !== null && clipboardText === this.copiedCellText;
7620
- if (isInternalCopy && this.copiedCellData && this.copiedCellParams) {
7621
- const copiedColumnName = this.copiedCellParams.colDef?.field;
7622
- const copiedCellDataTypeName = this.copiedCellData[copiedColumnName].property.typeName;
7646
+ const copied = clipboard.data;
7647
+ if (copied?.kind === 'cell' && clipboard.text === copied.text) {
7648
+ const copiedCellDataTypeName = copied.typeName;
7623
7649
  const pastedCellDataTypeName = pastedCellConfig.property.typeName;
7624
7650
  if (copiedCellDataTypeName !== pastedCellDataTypeName) {
7625
7651
  this.uiNotification.error('Clipboard data type does not match the current cell data type: ' + copiedCellDataTypeName + ' vs ' + pastedCellDataTypeName);
7626
7652
  return;
7627
7653
  }
7628
- pastedCellConfig.$ = this.copiedCellValue;
7654
+ pastedCellConfig.$ = copied.value;
7629
7655
  }
7630
7656
  else {
7631
7657
  try {
7632
- pastedCellConfig.$ = this.parseExternalCellValue(clipboardText, pastedCellConfig);
7658
+ pastedCellConfig.$ = this.parseExternalCellValue(clipboard.text, pastedCellConfig);
7633
7659
  }
7634
7660
  catch (err) {
7635
7661
  this.uiNotification.error(err.message);
@@ -7639,72 +7665,73 @@ class TruDataGridClipboard {
7639
7665
  this.removeBorderAnimation();
7640
7666
  params.api.refreshCells({ force: true });
7641
7667
  };
7642
- copyRow = async (tableName, columnDefs, copiedRows, includeHeaders = false) => {
7643
- this.tableName = tableName;
7644
- this.copiedRows = copiedRows;
7645
- this.copiedRowsText = null;
7646
- this.removeBorderAnimation();
7647
- this.addRowBorderAnimation();
7668
+ copyRow = async (tableName, columnDefs, copiedRows, includeHeaders = false, event) => {
7648
7669
  if (copiedRows.length) {
7649
7670
  let multiRowString = '';
7650
- if (includeHeaders) {
7651
- let headerRow = [];
7652
- columnDefs
7653
- .forEach((columnDef) => {
7654
- if (!columnDef.pinned)
7655
- headerRow.push(columnDef.headerName);
7656
- });
7657
- multiRowString += headerRow.join('\t') + '\n';
7658
- }
7659
- this.copiedRows.forEach((copiedRow, index) => {
7660
- let rowPropertyValues = [];
7661
- let properties = copiedRow.$entity.entityType.getPropertyNames();
7662
- columnDefs
7663
- .forEach((columnDef) => {
7664
- let propertyName = columnDef.field;
7665
- let propertyConfig = copiedRow[propertyName]?.property;
7666
- if (propertyConfig && !columnDef.pinned)
7667
- rowPropertyValues.push(this.formatCellValue(copiedRow[propertyName], copiedRow[propertyName].property, copiedRow.$entity, copiedRow.$entity.getProperty(propertyName)));
7668
- });
7669
- multiRowString += rowPropertyValues.join('\t') + '\n';
7670
- });
7671
- this.copiedRowsText = this.normalizeClipboardText(multiRowString);
7672
7671
  try {
7673
- await navigator.clipboard.writeText(multiRowString);
7672
+ if (includeHeaders) {
7673
+ let headerRow = [];
7674
+ columnDefs
7675
+ .forEach((columnDef) => {
7676
+ if (!columnDef.pinned)
7677
+ headerRow.push(columnDef.headerName);
7678
+ });
7679
+ multiRowString += headerRow.join('\t') + '\n';
7680
+ }
7681
+ copiedRows.forEach((copiedRow) => {
7682
+ let rowPropertyValues = [];
7683
+ columnDefs
7684
+ .forEach((columnDef) => {
7685
+ let propertyName = columnDef.field;
7686
+ let propertyConfig = copiedRow[propertyName]?.property;
7687
+ if (propertyConfig && !columnDef.pinned)
7688
+ rowPropertyValues.push(this.formatCellValue(copiedRow[propertyName], copiedRow[propertyName].property, copiedRow.$entity, copiedRow.$entity.getProperty(propertyName)));
7689
+ });
7690
+ multiRowString += rowPropertyValues.join('\t') + '\n';
7691
+ });
7674
7692
  }
7675
7693
  catch (err) {
7676
- console.error('Failed to copy text: ', err);
7694
+ // A failed copy must also invalidate an earlier asynchronous clipboard write.
7695
+ ++this.copySequence;
7696
+ this.copiedData = null;
7697
+ this.pendingCopy = null;
7698
+ this.removeBorderAnimation();
7699
+ event?.preventDefault();
7700
+ this.uiNotification.error('Unable to copy the selected rows.', [err instanceof Error ? err.message : String(err)]);
7701
+ return;
7677
7702
  }
7703
+ await this.writeClipboard(multiRowString, {
7704
+ kind: 'rows',
7705
+ tableName,
7706
+ text: this.normalizeClipboardText(multiRowString),
7707
+ rows: copiedRows.slice()
7708
+ }, event);
7678
7709
  }
7679
7710
  };
7680
- pasteRow = async (gridApi, config, selectedRows) => {
7681
- let clipboardText;
7682
- try {
7683
- clipboardText = this.normalizeClipboardText(await this.readClipboardText());
7684
- }
7685
- catch (err) {
7686
- this.uiNotification.error('Unable to read data from the system clipboard.');
7711
+ pasteRow = async (gridApi, config, selectedRows, event) => {
7712
+ const clipboard = await this.readClipboard(event);
7713
+ if (!clipboard)
7687
7714
  return;
7688
- }
7689
- if (this.copiedRowsText === null || clipboardText !== this.copiedRowsText) {
7715
+ const copied = clipboard.data;
7716
+ if (copied?.kind !== 'rows' || clipboard.text !== copied.text) {
7690
7717
  this.uiNotification.error('External row data cannot be pasted as an internal grid row copy. Paste into an individual cell instead.');
7691
7718
  return;
7692
7719
  }
7693
- if (this.tableName !== config.tableName) {
7694
- this.uiNotification.error('Clipboard data does not match the current table name: ' + this.tableName + ' vs ' + config.tableName);
7720
+ if (copied.tableName !== config.tableName) {
7721
+ this.uiNotification.error('Clipboard data does not match the current table name: ' + copied.tableName + ' vs ' + config.tableName);
7695
7722
  return;
7696
7723
  }
7697
- if (selectedRows && selectedRows.length !== this.copiedRows.length) {
7698
- this.uiNotification.error('Clipboard data does not match the number of selected rows: ' + this.copiedRows.length + ' vs ' + selectedRows.length);
7724
+ if (!selectedRows || selectedRows.length !== copied.rows.length) {
7725
+ this.uiNotification.error('Clipboard data does not match the number of selected rows: ' + copied.rows.length + ' vs ' + (selectedRows?.length ?? 0));
7699
7726
  return;
7700
7727
  }
7701
- if (this.copiedRows.length) {
7702
- this.copiedRows.reverse().forEach((copiedRow, index) => {
7728
+ if (copied.rows.length) {
7729
+ copied.rows.slice().reverse().forEach((copiedRow, index) => {
7703
7730
  let selectedRow = selectedRows[index];
7704
7731
  let properties = copiedRow.$entity.entityType.getPropertyNames();
7705
7732
  properties
7706
7733
  .forEach((propertyName) => {
7707
- let property = this.modelPropertyLookup.lookup.get(this.tableName + '.' + propertyName);
7734
+ let property = this.modelPropertyLookup.lookup.get(copied.tableName + '.' + propertyName);
7708
7735
  let excludedFromCloning = property ? property.excludeFromCloning : false;
7709
7736
  if (typeof selectedRow.$entity.setProperty !== 'undefined' &&
7710
7737
  !excludedFromCloning &&
@@ -7723,17 +7750,20 @@ class TruDataGridClipboard {
7723
7750
  }
7724
7751
  };
7725
7752
  insertRowsAtTop = (dataContext, config, addEntity) => {
7726
- if (this.tableName !== config.tableName) {
7727
- this.uiNotification.error('Clipboard data does not match the current table name: ' + this.tableName + ' vs ' + config.tableName);
7753
+ const copied = this.copiedData;
7754
+ if (copied?.kind !== 'rows')
7755
+ return;
7756
+ if (copied.tableName !== config.tableName) {
7757
+ this.uiNotification.error('Clipboard data does not match the current table name: ' + copied.tableName + ' vs ' + config.tableName);
7728
7758
  return;
7729
7759
  }
7730
- if (this.copiedRows.length) {
7731
- this.copiedRows.reverse().forEach((row) => {
7760
+ if (copied.rows.length) {
7761
+ copied.rows.slice().reverse().forEach((row) => {
7732
7762
  let newEntity = dataContext.entityAccess().add(config.resultConfig.entityType);
7733
7763
  let properties = row.$entity.entityType.getPropertyNames();
7734
7764
  properties
7735
7765
  .filter((propertyName) => {
7736
- let property = this.modelPropertyLookup.lookup.get(this.tableName + '.' + propertyName);
7766
+ let property = this.modelPropertyLookup.lookup.get(copied.tableName + '.' + propertyName);
7737
7767
  let excludedFromCloning = property ? property.excludeFromCloning : false;
7738
7768
  return propertyName !== 'Ref' &&
7739
7769
  !excludedFromCloning &&
@@ -7752,10 +7782,10 @@ class TruDataGridClipboard {
7752
7782
  }
7753
7783
  };
7754
7784
  getCopiedRows = () => {
7755
- return this.copiedRows;
7785
+ return this.copiedData?.kind === 'rows' ? this.copiedData.rows : [];
7756
7786
  };
7757
7787
  getCopiedCell = () => {
7758
- return this.copiedCellEntity;
7788
+ return this.copiedData?.kind === 'cell' ? this.copiedData.entity : null;
7759
7789
  };
7760
7790
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: TruDataGridClipboard, deps: [{ token: TruUtil }, { token: TruFormatter }, { token: TruUiNotification }, { token: TruModelPropertyLookup }], target: i0.ɵɵFactoryTarget.Injectable });
7761
7791
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: TruDataGridClipboard, providedIn: 'root' });
@@ -7806,6 +7836,7 @@ class TruDataGridPkeyCellRenderer {
7806
7836
  let columnDefs = this.params.api.getColumnDefs();
7807
7837
  if (selectedRows.length) {
7808
7838
  let copiedRowData = selectedRows;
7839
+ this.dataGridClipboard.setGridElement(this.params.eGridCell.closest('tru-data-grid'));
7809
7840
  this.dataGridClipboard.copyRow(this.params.context.grid.config.tableName, columnDefs, copiedRowData);
7810
7841
  }
7811
7842
  };
@@ -7814,6 +7845,7 @@ class TruDataGridPkeyCellRenderer {
7814
7845
  let columnDefs = this.params.api.getColumnDefs();
7815
7846
  if (selectedRows.length) {
7816
7847
  let copiedRowData = selectedRows;
7848
+ this.dataGridClipboard.setGridElement(this.params.eGridCell.closest('tru-data-grid'));
7817
7849
  this.dataGridClipboard.copyRow(this.params.context.grid.config.tableName, columnDefs, copiedRowData, true);
7818
7850
  }
7819
7851
  };
@@ -9731,24 +9763,62 @@ class TruDataGrid {
9731
9763
  key: event.code
9732
9764
  });
9733
9765
  }
9734
- else if (params.column.isPinned() && event.ctrlKey && event.code === 'KeyC') {
9735
- let selectedRows = this.api.getSelectedRows();
9736
- let columnDefs = this.api.getColumnDefs();
9737
- if (selectedRows.length)
9738
- this.dataGridClipboard.copyRow(this.config.tableName, columnDefs, selectedRows);
9739
- }
9740
- else if (params.column.isPinned() && event.ctrlKey && event.code === 'KeyV') {
9741
- event.preventDefault();
9742
- void this.dataGridClipboard.pasteRow(this.api, this.config, this.api.getSelectedRows());
9743
- }
9744
- else if (!params.column.isPinned() && event.ctrlKey && event.code === 'KeyC') {
9745
- this.dataGridClipboard.copyCell(params, this.config.tableName, params.colDef?.field, params.data, params.data.$entity);
9766
+ };
9767
+ getClipboardCell = (event) => {
9768
+ const target = event.target;
9769
+ // Editors and filter inputs own their text-selection clipboard operations.
9770
+ if (event.defaultPrevented || !event.clipboardData || !this.api || this.api.getEditingCells().length ||
9771
+ !target?.closest('.ag-cell') || target.closest('ag-grid-angular') !== event.currentTarget ||
9772
+ target.closest('input, textarea, select') || target.isContentEditable)
9773
+ return null;
9774
+ const focused = this.api.getFocusedCell();
9775
+ if (!focused)
9776
+ return null;
9777
+ const node = focused.rowPinned === 'top' ? this.api.getPinnedTopRow(focused.rowIndex)
9778
+ : focused.rowPinned === 'bottom' ? this.api.getPinnedBottomRow(focused.rowIndex)
9779
+ : this.api.getDisplayedRowAtIndex(focused.rowIndex);
9780
+ if (!node?.data)
9781
+ return null;
9782
+ return {
9783
+ api: this.api,
9784
+ context: this.gridOptions.context,
9785
+ column: focused.column,
9786
+ colDef: focused.column.getColDef(),
9787
+ node,
9788
+ data: node.data,
9789
+ value: this.api.getCellValue({ rowNode: node, colKey: focused.column })
9790
+ };
9791
+ };
9792
+ // AG Grid's cellKeyDown notification is asynchronous; native clipboard events must be handled synchronously.
9793
+ onCopy = (event) => {
9794
+ const cell = this.getClipboardCell(event);
9795
+ if (!cell)
9796
+ return;
9797
+ if (cell.column.isPinned()) {
9798
+ const rows = this.api.getSelectedRows();
9799
+ if (!rows.length)
9800
+ return;
9801
+ this.dataGridClipboard.setGridElement(this.hostElement.nativeElement);
9802
+ event.stopPropagation();
9803
+ void this.dataGridClipboard.copyRow(this.config.tableName, this.api.getColumnDefs(), rows, false, event);
9746
9804
  }
9747
- else if (!params.column.isPinned() && event.ctrlKey && event.code === 'KeyV') {
9748
- event.preventDefault();
9749
- void this.dataGridClipboard.pasteCell(params, params.data, params.data.$entity);
9805
+ else if (cell.colDef.field && cell.data[cell.colDef.field]) {
9806
+ this.dataGridClipboard.setGridElement(this.hostElement.nativeElement);
9807
+ event.stopPropagation();
9808
+ void this.dataGridClipboard.copyCell(cell, this.config.tableName, cell.colDef.field, cell.data, cell.data.$entity, event);
9750
9809
  }
9751
9810
  };
9811
+ onPaste = (event) => {
9812
+ const cell = this.getClipboardCell(event);
9813
+ if (!cell || (!cell.column.isPinned() && (!cell.colDef.field || !cell.data[cell.colDef.field])))
9814
+ return;
9815
+ event.preventDefault();
9816
+ event.stopPropagation();
9817
+ if (cell.column.isPinned())
9818
+ void this.dataGridClipboard.pasteRow(this.api, this.config, this.api.getSelectedRows(), event);
9819
+ else
9820
+ void this.dataGridClipboard.pasteCell(cell, cell.data, cell.data.$entity, event);
9821
+ };
9752
9822
  onCellMouseOver(e) {
9753
9823
  let targetElement = e?.event?.target;
9754
9824
  const fieldName = e?.colDef?.field;
@@ -10109,7 +10179,7 @@ class TruDataGrid {
10109
10179
  }
10110
10180
  };
10111
10181
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: TruDataGrid, deps: [{ token: TruDataContext }, { token: TruComponentLookup }, { token: TruSearchResultViewManager }, { token: TruAppEnvironment }, { token: TruSearchViewEventHandler }, { token: TruUiNotification }, { token: TruConnectionHub }, { token: TruWindowEventHandler }, { token: TruDataGridClipboard }, { token: TruUtil }, { token: i0.ApplicationRef }, { token: i0.ChangeDetectorRef }, { token: i0.ElementRef }, { token: TruDesktopViewEventNotifier }, { token: TruTabGroupEventNotifier, optional: true }], target: i0.ɵɵFactoryTarget.Component });
10112
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.7", type: TruDataGrid, isStandalone: true, selector: "tru-data-grid", inputs: { config: "config", entity: "entity", name: "name" }, host: { properties: { "class.tru-data-grid": "true" } }, viewQueries: [{ propertyName: "dataGridContainer", first: true, predicate: ["dataGridContainer"], descendants: true }, { propertyName: "customToolbarContainer", first: true, predicate: ["customToolbarContainer"], descendants: true, read: ViewContainerRef }], usesOnChanges: true, ngImport: i0, template: "<div [ngClass]=\"{'tru-data-grid-label-offset': label}\">\r\n <span class=\"tru-data-grid-label\" *ngIf=\"label\">{{label}}</span>\r\n <div class=\"tru-data-grid-toolbar-container\">\r\n <tru-toolbar>\r\n <tru-toolbar-dropdown [options]=\"unassociatedChoices\"\r\n [(selectedOption)]=\"selectedUnassociatedChoice\"\r\n [disabled]=\"!this.config.tableConfig.canAdd\"\r\n (selectionChange)=\"onUnassociatedChoiceChanged($event)\"\r\n *ngIf=\"gridType === TruDataGridTypes.DetailManyToMany\">\r\n </tru-toolbar-dropdown>\r\n <tru-toolbar-button [icon]=\"'tru-toolbar-add-icon'\" [tooltip]=\"'Add - [Ctrl + I]'\" [disabled]=\"!canAdd()\" (onClick)=\"onAdd()\"></tru-toolbar-button>\r\n <tru-toolbar-button [icon]=\"'tru-toolbar-delete-icon'\" [tooltip]=\"'Delete - [Ctrl + D]'\" (click)=\"onDelete()\"></tru-toolbar-button>\r\n <tru-toolbar-button [icon]=\"'tru-toolbar-keep-icon'\" [tooltip]=\"'Keep selected records - [Ctrl + K]'\" (click)=\"onKeep()\"></tru-toolbar-button>\r\n <tru-toolbar-button [icon]=\"'tru-toolbar-remove-icon'\" [tooltip]=\"'Remove selected records - [Ctrl + R]'\" (click)=\"onRemove()\"></tru-toolbar-button>\r\n <tru-toolbar-separator *ngIf=\"parentToolbarTemplate\"></tru-toolbar-separator>\r\n <ng-container *ngTemplateOutlet=\"parentToolbarTemplate\"></ng-container>\r\n <tru-toolbar-separator></tru-toolbar-separator>\r\n <tru-toolbar-button [icon]=\"'tru-toolbar-export-icon'\" [tooltip]=\"'Export'\" (click)=\"onExport()\"></tru-toolbar-button>\r\n <tru-toolbar-separator *ngIf=\"viewMenuItems.length\"></tru-toolbar-separator>\r\n <div *ngIf=\"viewMenuItems.length\">\r\n <tru-toolbar-menu *ngFor=\"let viewMenuItem of viewMenuItems\" [config]=\"viewMenuItem\"></tru-toolbar-menu>\r\n </div>\r\n <ng-container #customToolbarContainer></ng-container>\r\n </tru-toolbar>\r\n </div>\r\n <div #dataGridContainer class=\"tru-data-grid-container\">\r\n <ag-grid-angular class=\"ag-theme-alpine\"\r\n [gridOptions]=\"gridOptions\"\r\n [rowData]=\"rowData\"\r\n [columnDefs]=\"columnDefs\"\r\n [defaultColDef]=\"defaultColDef\"\r\n [headerHeight]=\"25\"\r\n [rowHeight]=\"22\"\r\n [rowBuffer]=\"50\"\r\n [enterNavigatesVertically]=\"true\"\r\n [enterNavigatesVerticallyAfterEdit]=\"true\"\r\n [suppressRowHoverHighlight]=\"true\"\r\n [suppressRowClickSelection]=\"true\"\r\n [stopEditingWhenCellsLoseFocus]=\"false\"\r\n (cellClicked)=\"onCellClicked($event)\"\r\n (cellDoubleClicked)=\"onCellDoubleClicked($event)\"\r\n (cellKeyDown)=\"onCellKeyDown($event)\"\r\n (rowDataChanged)=\"onRowDataChanged($event)\"\r\n (cellMouseDown)=\"onCellMouseDown($event)\"\r\n (cellMouseOver)=\"onCellMouseOver($event)\"\r\n (cellMouseOut)=\"onCellMouseOut($event)\"\r\n (contextmenu)=\"onRightClick($event)\"\r\n (rowClicked)=\"onRowClicked($event)\">\r\n </ag-grid-angular>\r\n </div>\r\n <div class=\"tru-data-grid-statusbar-container\">\r\n <div class=\"tru-data-grid-statusbar-values-container\">\r\n <p class=\"tru-data-grid-statusbar-values-text\">{{rowCount}}</p>\r\n <p class=\"tru-data-grid-statusbar-values-text\">Selected: {{api ? api.getSelectedRows().length : 0}}</p>\r\n </div>\r\n </div>\r\n</div>\r\n", styles: [".tru-data-grid-label{font-size:14px;font-weight:700;margin-bottom:5px;display:flex}.tru-data-grid:first-of-type+.tru-data-grid{margin-left:5px}.tru-data-grid-toolbar-container{display:inline-flex}.tru-data-grid-container{min-height:200px}.tru-data-grid .ag-body-viewport{padding-bottom:var(--grid-footer-height, 23px)}.tru-data-grid-statusbar-container{position:absolute!important;left:0;right:0;bottom:0;height:23px;background:#006dcc;border-bottom-left-radius:0;border-bottom-right-radius:0;border-top:1px solid #e7e7e7}.tru-data-grid-statusbar-container p{float:left;color:#fff;font-weight:700;margin-left:10px;margin-top:0!important;line-height:23px;font-size:12px}.detail-container .tru-data-grid{position:relative}.detail-container .mat-mdc-tab-body-content{height:100%;overflow:hidden!important}.detail-container .ag-root-wrapper{height:calc(100% - 50px)!important}ag-grid-angular{position:absolute;inset:28px 0 23px;width:100%;height:100%}.tru-data-grid-label-offset ag-grid-angular{top:48px;height:calc(100% - 70px)}.ag-root-wrapper{border-top:0px!important}.ag-root{top:0;bottom:23px!important}::ng-deep .mat-mdc-tab-group .ag-root.ag-layout-normal{height:calc(100% - 50px)!important}.search-container .ag-root.ag-layout-normal{height:calc(100% - 50px)!important}.ag-header-cell{padding-left:0!important;padding-right:0!important}.ag-header-cell-resize:after{top:unset!important;height:25px!important}.ag-header-row{font-weight:500!important}.ag-header-cell-label{justify-content:center}.ag-row{border-color:#d8dbe3}.tru-data-grid .ag-row.validation-dialog-open{z-index:2}.ag-row.popup-editing{z-index:unset!important}.ag-row .ag-cell-value{padding-left:2px!important;padding-right:2px!important}.ag-row .ag-cell-value.invalid{border:1px solid red!important;border-radius:0}.ag-row .ag-cell-value.ag-cell-inline-editing:not(.invalid){border-color:#006dcc!important}.ag-ltr .ag-header-cell-resize{right:-5px}.ag-header-cell-text{font-size:12px!important}.ag-cell-inline-editing{height:21px!important}.ag-cell{height:21px;border-right:solid #d8dbe3 1px!important}.ag-cell ng-component{pointer-events:none}.ag-has-focus .ag-cell-focus:not(.invalid){border:solid #006dcc 1px!important}.ag-pinned-left-cols-container .ag-cell.ag-cell-focus{border:solid #dde2eb 1px!important;border-top:0px!important;border-bottom:0px!important}.ag-pinned-left-cols-container .ag-row-selected .ag-cell,.ag-pinned-left-cols-container .ag-row-focus .ag-cell{border:0px solid #dde2eb!important;border-right:2px solid #006dcc!important;color:#fff;font-weight:700;background-color:#006dcc}.ag-pinned-left-cols-container *:not(.ag-row-selected):not(.ag-row-focus) .ag-cell{background-color:#f5f5f5!important}.ag-pinned-left-cols-container *:not(.ag-row-selected):not(.ag-row-focus) .ag-cell:hover{background-color:#2196f31a!important}.ag-header-cell{background-color:#f5f5f5}.ag-column-focus .ag-header-control-sort-container rect{fill:#ddd}.ag-header-cell.ag-column-focus{border:0px solid #dde2eb!important;border-bottom:2px solid #006dcc!important;color:#006dcc;font-weight:700;background-color:#ddd}.ag-header-cell-comp-wrapper{display:block!important}.ag-cell-label-container{padding:0!important}.ag-pk-aligned-cell{text-align:center}.ag-horizontal-left-spacer{overflow:hidden}.ag-popup-editor{background:#fff;padding:10px;border:solid 1px #006dcc}.ag-overlay-loading-center{border:none!important;box-shadow:none!important}.ag-viewport .ag-row-selected{background-color:#efefef}.ag-viewport .ag-row-selected .ag-cell{background-color:unset!important}.ag-row-selected~.ag-row-selected .ag-cell{border-top-color:#dde2eb}*:not(.ag-row-selected):not(.ag-row-inline-editing)+.ag-row-selected .ag-cell{border-top-color:#006dcc}.ag-row-selected:has(+:not(.ag-row-selected)):not(.ag-row-inline-editing) .ag-cell{border-bottom-color:#006dcc}.ag-cell.invalid .invalid-flag{box-sizing:border-box;position:absolute;left:0;top:0;width:3px;height:3px;border-bottom:solid 3px transparent;border-right:solid 3px transparent;border-left:solid 3px red;border-top:solid 3px red}.ag-grid-row-has-changes-gradient{background-color:#fff;filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr=#fff,endColorstr=#ffffff);background-image:-moz-linear-gradient(top,#fff 0%,#ffffff 25%,#dbffdf 100%);background-image:linear-gradient(top,#fff 0% 25%,#dbffdf);background-image:-webkit-linear-gradient(top,#fff 0%,#ffffff 25%,#dbffdf 100%);background-image:-o-linear-gradient(top,#fff 0%,#ffffff 25%,#dbffdf 100%);background-image:-ms-linear-gradient(top,#fff 0%,#ffffff 25%,#dbffdf 100%);background-image:-webkit-gradient(linear,right top,right bottom,color-stop(0%,#fff),color-stop(25%,#ffffff),color-stop(100%,#dbffdf))}@keyframes ants-marching{0%{background-position:0 0,0 100%,0 0,100% 0}to{background-position:40px 0,-40px 100%,0 -40px,100% 40px}}.animated-border{background-size:10px 0px,10px 0px,1px 10px,1px 10px;background-position:0 0,0 100%,0 0,100% 0;background-repeat:repeat-x,repeat-x,repeat-y,repeat-y;background-image:linear-gradient(to right,#006dcc 50%,transparent 50%),linear-gradient(to right,#006dcc 50%,transparent 50%),linear-gradient(to bottom,#006dcc 50%,transparent 50%),linear-gradient(to bottom,#006dcc 50%,transparent 50%);animation:ants-marching 1s linear infinite}.animated-border.ag-row-selected{background-color:#efefef!important}.animated-border .ag-cell{background-color:unset!important;border-top:none!important;border-bottom:none!important}.animated-border:not(.animated-border+.animated-border){background-size:10px 1px,10px 0px,1px 10px,1px 10px}.animated-border:has(+:not(.animated-border)){background-size:10px 0px,10px 1px,1px 10px,1px 10px}.animated-border:not(.animated-border+.animated-border).animated-border:has(+:not(.animated-border)){background-size:10px 1px,10px 1px,1px 10px,1px 10px}.animated-border:has(+:not(.animated-border)) .ag-cell{height:20px!important}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.spin{animation-name:spin;animation-duration:4s;animation-iteration-count:infinite;animation-timing-function:linear}.detail-container .mat-mdc-tab-body-content tru-data-grid{--grid-scroll-left: 0px;--grid-scroll-right: 0px;--grid-scroll-bottom: 80px;--grid-scroll-visible: 1;display:flex;flex-direction:column;height:100%;min-height:0}.detail-container .mat-mdc-tab-body-content tru-data-grid ag-grid-angular{position:relative!important;inset:0!important;height:calc(100% - var(--grid-footer-height, 23px))!important}.detail-container .mat-mdc-tab-body-content tru-data-grid .ag-root-wrapper,.detail-container .mat-mdc-tab-body-content tru-data-grid .ag-root{height:100%!important;top:0!important;bottom:0!important}.detail-container .mat-mdc-tab-body-content tru-data-grid .tru-data-grid-container{flex:1 1 auto;position:relative;min-height:0}.detail-container .mat-mdc-tab-body-content tru-data-grid .ag-body-viewport{position:relative!important;padding-bottom:calc(var(--grid-footer-height, 23px) + (var(--grid-scroll-bottom, 80px) - var(--grid-footer-height, 23px)) * var(--grid-scroll-visible, 1))!important;overflow-x:auto!important}.detail-container .mat-mdc-tab-body-content tru-data-grid .ag-body-horizontal-scroll-viewport,.detail-container .mat-mdc-tab-body-content tru-data-grid .ag-body-horizontal-scroll{position:fixed!important;left:var(--grid-scroll-left, 0px)!important;right:var(--grid-scroll-right, 0px)!important;width:calc(100% - var(--grid-scroll-left, 0px) - var(--grid-scroll-right, 0px))!important;bottom:var(--grid-scroll-bottom, 80px)!important;height:calc(16px * var(--grid-scroll-visible, 1))!important;min-height:calc(16px * var(--grid-scroll-visible, 1))!important;display:block!important;opacity:var(--grid-scroll-visible, 1)!important;pointer-events:calc(var(--grid-scroll-visible, 1))!important;overflow-x:scroll!important;overflow-y:hidden!important;z-index:9999!important;background:transparent!important}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: MatIconModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: AgGridAngular, selector: "ag-grid-angular", inputs: ["gridOptions", "modules", "statusBar", "sideBar", "suppressContextMenu", "preventDefaultOnContextMenu", "allowContextMenuWithControlKey", "columnMenu", "suppressMenuHide", "enableBrowserTooltips", "tooltipTrigger", "tooltipShowDelay", "tooltipHideDelay", "tooltipMouseTrack", "tooltipShowMode", "tooltipInteraction", "popupParent", "copyHeadersToClipboard", "copyGroupHeadersToClipboard", "clipboardDelimiter", "suppressCopyRowsToClipboard", "suppressCopySingleCellRanges", "suppressLastEmptyLineOnPaste", "suppressClipboardPaste", "suppressClipboardApi", "suppressCutToClipboard", "columnDefs", "defaultColDef", "defaultColGroupDef", "columnTypes", "dataTypeDefinitions", "maintainColumnOrder", "enableStrictPivotColumnOrder", "suppressFieldDotNotation", "headerHeight", "groupHeaderHeight", "floatingFiltersHeight", "pivotHeaderHeight", "pivotGroupHeaderHeight", "allowDragFromColumnsToolPanel", "suppressMovableColumns", "suppressColumnMoveAnimation", "suppressMoveWhenColumnDragging", "suppressDragLeaveHidesColumns", "suppressGroupChangesColumnVisibility", "suppressMakeColumnVisibleAfterUnGroup", "suppressRowGroupHidesColumns", "colResizeDefault", "suppressAutoSize", "autoSizePadding", "skipHeaderOnAutoSize", "autoSizeStrategy", "components", "editType", "singleClickEdit", "suppressClickEdit", "readOnlyEdit", "stopEditingWhenCellsLoseFocus", "enterNavigatesVertically", "enterNavigatesVerticallyAfterEdit", "enableCellEditingOnBackspace", "undoRedoCellEditing", "undoRedoCellEditingLimit", "defaultCsvExportParams", "suppressCsvExport", "defaultExcelExportParams", "suppressExcelExport", "excelStyles", "findSearchValue", "findOptions", "quickFilterText", "cacheQuickFilter", "includeHiddenColumnsInQuickFilter", "quickFilterParser", "quickFilterMatcher", "applyQuickFilterBeforePivotOrAgg", "excludeChildrenWhenTreeDataFiltering", "enableAdvancedFilter", "alwaysPassFilter", "includeHiddenColumnsInAdvancedFilter", "advancedFilterParent", "advancedFilterBuilderParams", "suppressAdvancedFilterEval", "suppressSetFilterByDefault", "enableCharts", "chartThemes", "customChartThemes", "chartThemeOverrides", "chartToolPanelsDef", "chartMenuItems", "loadingCellRenderer", "loadingCellRendererParams", "loadingCellRendererSelector", "localeText", "masterDetail", "keepDetailRows", "keepDetailRowsCount", "detailCellRenderer", "detailCellRendererParams", "detailRowHeight", "detailRowAutoHeight", "context", "alignedGrids", "tabIndex", "rowBuffer", "valueCache", "valueCacheNeverExpires", "enableCellExpressions", "suppressTouch", "suppressFocusAfterRefresh", "suppressBrowserResizeObserver", "suppressPropertyNamesCheck", "suppressChangeDetection", "debug", "loading", "overlayLoadingTemplate", "loadingOverlayComponent", "loadingOverlayComponentParams", "suppressLoadingOverlay", "overlayNoRowsTemplate", "noRowsOverlayComponent", "noRowsOverlayComponentParams", "suppressNoRowsOverlay", "pagination", "paginationPageSize", "paginationPageSizeSelector", "paginationAutoPageSize", "paginateChildRows", "suppressPaginationPanel", "pivotMode", "pivotPanelShow", "pivotMaxGeneratedColumns", "pivotDefaultExpanded", "pivotColumnGroupTotals", "pivotRowTotals", "pivotSuppressAutoColumn", "suppressExpandablePivotGroups", "functionsReadOnly", "aggFuncs", "suppressAggFuncInHeader", "alwaysAggregateAtRootLevel", "aggregateOnlyChangedColumns", "suppressAggFilteredOnly", "removePivotHeaderRowWhenSingleValueColumn", "animateRows", "cellFlashDuration", "cellFadeDuration", "allowShowChangeAfterFilter", "domLayout", "ensureDomOrder", "enableCellSpan", "enableRtl", "suppressColumnVirtualisation", "suppressMaxRenderedRowRestriction", "suppressRowVirtualisation", "rowDragManaged", "suppressRowDrag", "suppressMoveWhenRowDragging", "rowDragEntireRow", "rowDragMultiRow", "rowDragText", "dragAndDropImageComponent", "dragAndDropImageComponentParams", "fullWidthCellRenderer", "fullWidthCellRendererParams", "embedFullWidthRows", "groupDisplayType", "groupDefaultExpanded", "autoGroupColumnDef", "groupMaintainOrder", "groupSelectsChildren", "groupLockGroupColumns", "groupAggFiltering", "groupTotalRow", "grandTotalRow", "suppressStickyTotalRow", "groupSuppressBlankHeader", "groupSelectsFiltered", "showOpenedGroup", "groupHideParentOfSingleChild", "groupRemoveSingleChildren", "groupRemoveLowestSingleChildren", "groupHideOpenParents", "groupAllowUnbalanced", "rowGroupPanelShow", "groupRowRenderer", "groupRowRendererParams", "treeData", "treeDataChildrenField", "treeDataParentIdField", "rowGroupPanelSuppressSort", "suppressGroupRowsSticky", "pinnedTopRowData", "pinnedBottomRowData", "enableRowPinning", "isRowPinnable", "isRowPinned", "rowModelType", "rowData", "asyncTransactionWaitMillis", "suppressModelUpdateAfterUpdateTransaction", "datasource", "cacheOverflowSize", "infiniteInitialRowCount", "serverSideInitialRowCount", "suppressServerSideFullWidthLoadingRow", "cacheBlockSize", "maxBlocksInCache", "maxConcurrentDatasourceRequests", "blockLoadDebounceMillis", "purgeClosedRowNodes", "serverSideDatasource", "serverSideSortAllLevels", "serverSideEnableClientSideSort", "serverSideOnlyRefreshFilteredGroups", "serverSidePivotResultFieldSeparator", "viewportDatasource", "viewportRowModelPageSize", "viewportRowModelBufferSize", "alwaysShowHorizontalScroll", "alwaysShowVerticalScroll", "debounceVerticalScrollbar", "suppressHorizontalScroll", "suppressScrollOnNewData", "suppressScrollWhenPopupsAreOpen", "suppressAnimationFrame", "suppressMiddleClickScrolls", "suppressPreventDefaultOnMouseWheel", "scrollbarWidth", "rowSelection", "cellSelection", "rowMultiSelectWithClick", "suppressRowDeselection", "suppressRowClickSelection", "suppressCellFocus", "suppressHeaderFocus", "selectionColumnDef", "rowNumbers", "suppressMultiRangeSelection", "enableCellTextSelection", "enableRangeSelection", "enableRangeHandle", "enableFillHandle", "fillHandleDirection", "suppressClearOnFillReduction", "sortingOrder", "accentedSort", "unSortIcon", "suppressMultiSort", "alwaysMultiSort", "multiSortKey", "suppressMaintainUnsortedOrder", "icons", "rowHeight", "rowStyle", "rowClass", "rowClassRules", "suppressRowHoverHighlight", "suppressRowTransform", "columnHoverHighlight", "gridId", "deltaSort", "treeDataDisplayType", "enableGroupEdit", "initialState", "theme", "loadThemeGoogleFonts", "themeCssLayer", "styleNonce", "themeStyleContainer", "getContextMenuItems", "getMainMenuItems", "postProcessPopup", "processUnpinnedColumns", "processCellForClipboard", "processHeaderForClipboard", "processGroupHeaderForClipboard", "processCellFromClipboard", "sendToClipboard", "processDataFromClipboard", "isExternalFilterPresent", "doesExternalFilterPass", "getChartToolbarItems", "createChartContainer", "focusGridInnerElement", "navigateToNextHeader", "tabToNextHeader", "navigateToNextCell", "tabToNextCell", "getLocaleText", "getDocument", "paginationNumberFormatter", "getGroupRowAgg", "isGroupOpenByDefault", "initialGroupOrderComparator", "processPivotResultColDef", "processPivotResultColGroupDef", "getDataPath", "getChildCount", "getServerSideGroupLevelParams", "isServerSideGroupOpenByDefault", "isApplyServerSideTransaction", "isServerSideGroup", "getServerSideGroupKey", "getBusinessKeyForNode", "getRowId", "resetRowDataOnUpdate", "processRowPostCreate", "isRowSelectable", "isRowMaster", "fillOperation", "postSortRows", "getRowStyle", "getRowClass", "getRowHeight", "isFullWidthRow"], outputs: ["toolPanelVisibleChanged", "toolPanelSizeChanged", "columnMenuVisibleChanged", "contextMenuVisibleChanged", "cutStart", "cutEnd", "pasteStart", "pasteEnd", "columnVisible", "columnPinned", "columnResized", "columnMoved", "columnValueChanged", "columnPivotModeChanged", "columnPivotChanged", "columnGroupOpened", "newColumnsLoaded", "gridColumnsChanged", "displayedColumnsChanged", "virtualColumnsChanged", "columnEverythingChanged", "columnHeaderMouseOver", "columnHeaderMouseLeave", "columnHeaderClicked", "columnHeaderContextMenu", "componentStateChanged", "cellValueChanged", "cellEditRequest", "rowValueChanged", "cellEditingStarted", "cellEditingStopped", "rowEditingStarted", "rowEditingStopped", "undoStarted", "undoEnded", "redoStarted", "redoEnded", "cellSelectionDeleteStart", "cellSelectionDeleteEnd", "rangeDeleteStart", "rangeDeleteEnd", "fillStart", "fillEnd", "filterOpened", "filterChanged", "filterModified", "advancedFilterBuilderVisibleChanged", "findChanged", "chartCreated", "chartRangeSelectionChanged", "chartOptionsChanged", "chartDestroyed", "cellKeyDown", "gridReady", "firstDataRendered", "gridSizeChanged", "modelUpdated", "virtualRowRemoved", "viewportChanged", "bodyScroll", "bodyScrollEnd", "dragStarted", "dragStopped", "dragCancelled", "stateUpdated", "paginationChanged", "rowDragEnter", "rowDragMove", "rowDragLeave", "rowDragEnd", "rowDragCancel", "rowResizeStarted", "rowResizeEnded", "columnRowGroupChanged", "rowGroupOpened", "expandOrCollapseAll", "pivotMaxColumnsExceeded", "pinnedRowDataChanged", "pinnedRowsChanged", "rowDataUpdated", "asyncTransactionsFlushed", "storeRefreshed", "headerFocused", "cellClicked", "cellDoubleClicked", "cellFocused", "cellMouseOver", "cellMouseOut", "cellMouseDown", "rowClicked", "rowDoubleClicked", "rowSelected", "selectionChanged", "cellContextMenu", "rangeSelectionChanged", "cellSelectionChanged", "tooltipShow", "tooltipHide", "sortChanged"] }, { kind: "component", type: TruToolbar, selector: "tru-toolbar", inputs: ["config"] }, { kind: "component", type: TruToolbarButton, selector: "tru-toolbar-button", inputs: ["config", "icon", "text", "disabled", "tooltip", "type"], outputs: ["onClick", "onKeydown"] }, { kind: "component", type: TruToolbarDropdown, selector: "tru-toolbar-dropdown", inputs: ["config", "options", "tooltip", "multiple", "filterable", "filterTooltip", "width", "selectedOption", "disabled"], outputs: ["selectedOptionChange", "selectionChange"] }, { kind: "component", type: TruToolbarSeparator, selector: "tru-toolbar-separator" }, { kind: "component", type: TruToolbarMenu, selector: "tru-toolbar-menu", inputs: ["config"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
10182
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.7", type: TruDataGrid, isStandalone: true, selector: "tru-data-grid", inputs: { config: "config", entity: "entity", name: "name" }, host: { properties: { "class.tru-data-grid": "true" } }, viewQueries: [{ propertyName: "dataGridContainer", first: true, predicate: ["dataGridContainer"], descendants: true }, { propertyName: "customToolbarContainer", first: true, predicate: ["customToolbarContainer"], descendants: true, read: ViewContainerRef }], usesOnChanges: true, ngImport: i0, template: "<div [ngClass]=\"{'tru-data-grid-label-offset': label}\">\r\n <span class=\"tru-data-grid-label\" *ngIf=\"label\">{{label}}</span>\r\n <div class=\"tru-data-grid-toolbar-container\">\r\n <tru-toolbar>\r\n <tru-toolbar-dropdown [options]=\"unassociatedChoices\"\r\n [(selectedOption)]=\"selectedUnassociatedChoice\"\r\n [disabled]=\"!this.config.tableConfig.canAdd\"\r\n (selectionChange)=\"onUnassociatedChoiceChanged($event)\"\r\n *ngIf=\"gridType === TruDataGridTypes.DetailManyToMany\">\r\n </tru-toolbar-dropdown>\r\n <tru-toolbar-button [icon]=\"'tru-toolbar-add-icon'\" [tooltip]=\"'Add - [Ctrl + I]'\" [disabled]=\"!canAdd()\" (onClick)=\"onAdd()\"></tru-toolbar-button>\r\n <tru-toolbar-button [icon]=\"'tru-toolbar-delete-icon'\" [tooltip]=\"'Delete - [Ctrl + D]'\" (click)=\"onDelete()\"></tru-toolbar-button>\r\n <tru-toolbar-button [icon]=\"'tru-toolbar-keep-icon'\" [tooltip]=\"'Keep selected records - [Ctrl + K]'\" (click)=\"onKeep()\"></tru-toolbar-button>\r\n <tru-toolbar-button [icon]=\"'tru-toolbar-remove-icon'\" [tooltip]=\"'Remove selected records - [Ctrl + R]'\" (click)=\"onRemove()\"></tru-toolbar-button>\r\n <tru-toolbar-separator *ngIf=\"parentToolbarTemplate\"></tru-toolbar-separator>\r\n <ng-container *ngTemplateOutlet=\"parentToolbarTemplate\"></ng-container>\r\n <tru-toolbar-separator></tru-toolbar-separator>\r\n <tru-toolbar-button [icon]=\"'tru-toolbar-export-icon'\" [tooltip]=\"'Export'\" (click)=\"onExport()\"></tru-toolbar-button>\r\n <tru-toolbar-separator *ngIf=\"viewMenuItems.length\"></tru-toolbar-separator>\r\n <div *ngIf=\"viewMenuItems.length\">\r\n <tru-toolbar-menu *ngFor=\"let viewMenuItem of viewMenuItems\" [config]=\"viewMenuItem\"></tru-toolbar-menu>\r\n </div>\r\n <ng-container #customToolbarContainer></ng-container>\r\n </tru-toolbar>\r\n </div>\r\n <div #dataGridContainer class=\"tru-data-grid-container\">\r\n <ag-grid-angular class=\"ag-theme-alpine\"\r\n [gridOptions]=\"gridOptions\"\r\n [rowData]=\"rowData\"\r\n [columnDefs]=\"columnDefs\"\r\n [defaultColDef]=\"defaultColDef\"\r\n [headerHeight]=\"25\"\r\n [rowHeight]=\"22\"\r\n [rowBuffer]=\"50\"\r\n [enterNavigatesVertically]=\"true\"\r\n [enterNavigatesVerticallyAfterEdit]=\"true\"\r\n [suppressRowHoverHighlight]=\"true\"\r\n [suppressRowClickSelection]=\"true\"\r\n [stopEditingWhenCellsLoseFocus]=\"false\"\r\n (cellClicked)=\"onCellClicked($event)\"\r\n (cellDoubleClicked)=\"onCellDoubleClicked($event)\"\r\n (cellKeyDown)=\"onCellKeyDown($event)\"\r\n (copy)=\"onCopy($event)\"\r\n (paste)=\"onPaste($event)\"\r\n (rowDataChanged)=\"onRowDataChanged($event)\"\r\n (cellMouseDown)=\"onCellMouseDown($event)\"\r\n (cellMouseOver)=\"onCellMouseOver($event)\"\r\n (cellMouseOut)=\"onCellMouseOut($event)\"\r\n (contextmenu)=\"onRightClick($event)\"\r\n (rowClicked)=\"onRowClicked($event)\">\r\n </ag-grid-angular>\r\n </div>\r\n <div class=\"tru-data-grid-statusbar-container\">\r\n <div class=\"tru-data-grid-statusbar-values-container\">\r\n <p class=\"tru-data-grid-statusbar-values-text\">{{rowCount}}</p>\r\n <p class=\"tru-data-grid-statusbar-values-text\">Selected: {{api ? api.getSelectedRows().length : 0}}</p>\r\n </div>\r\n </div>\r\n</div>\r\n", styles: [".tru-data-grid-label{font-size:14px;font-weight:700;margin-bottom:5px;display:flex}.tru-data-grid:first-of-type+.tru-data-grid{margin-left:5px}.tru-data-grid-toolbar-container{display:inline-flex}.tru-data-grid-container{min-height:200px}.tru-data-grid .ag-body-viewport{padding-bottom:var(--grid-footer-height, 23px)}.tru-data-grid-statusbar-container{position:absolute!important;left:0;right:0;bottom:0;height:23px;background:#006dcc;border-bottom-left-radius:0;border-bottom-right-radius:0;border-top:1px solid #e7e7e7}.tru-data-grid-statusbar-container p{float:left;color:#fff;font-weight:700;margin-left:10px;margin-top:0!important;line-height:23px;font-size:12px}.detail-container .tru-data-grid{position:relative}.detail-container .mat-mdc-tab-body-content{height:100%;overflow:hidden!important}.detail-container .ag-root-wrapper{height:calc(100% - 50px)!important}ag-grid-angular{position:absolute;inset:28px 0 23px;width:100%;height:100%}.tru-data-grid-label-offset ag-grid-angular{top:48px;height:calc(100% - 70px)}.ag-root-wrapper{border-top:0px!important}.ag-root{top:0;bottom:23px!important}::ng-deep .mat-mdc-tab-group .ag-root.ag-layout-normal{height:calc(100% - 50px)!important}.search-container .ag-root.ag-layout-normal{height:calc(100% - 50px)!important}.ag-header-cell{padding-left:0!important;padding-right:0!important}.ag-header-cell-resize:after{top:unset!important;height:25px!important}.ag-header-row{font-weight:500!important}.ag-header-cell-label{justify-content:center}.ag-row{border-color:#d8dbe3}.tru-data-grid .ag-row.validation-dialog-open{z-index:2}.ag-row.popup-editing{z-index:unset!important}.ag-row .ag-cell-value{padding-left:2px!important;padding-right:2px!important}.ag-row .ag-cell-value.invalid{border:1px solid red!important;border-radius:0}.ag-row .ag-cell-value.ag-cell-inline-editing:not(.invalid){border-color:#006dcc!important}.ag-ltr .ag-header-cell-resize{right:-5px}.ag-header-cell-text{font-size:12px!important}.ag-cell-inline-editing{height:21px!important}.ag-cell{height:21px;border-right:solid #d8dbe3 1px!important}.ag-cell ng-component{pointer-events:none}.ag-has-focus .ag-cell-focus:not(.invalid){border:solid #006dcc 1px!important}.ag-pinned-left-cols-container .ag-cell.ag-cell-focus{border:solid #dde2eb 1px!important;border-top:0px!important;border-bottom:0px!important}.ag-pinned-left-cols-container .ag-row-selected .ag-cell,.ag-pinned-left-cols-container .ag-row-focus .ag-cell{border:0px solid #dde2eb!important;border-right:2px solid #006dcc!important;color:#fff;font-weight:700;background-color:#006dcc}.ag-pinned-left-cols-container *:not(.ag-row-selected):not(.ag-row-focus) .ag-cell{background-color:#f5f5f5!important}.ag-pinned-left-cols-container *:not(.ag-row-selected):not(.ag-row-focus) .ag-cell:hover{background-color:#2196f31a!important}.ag-header-cell{background-color:#f5f5f5}.ag-column-focus .ag-header-control-sort-container rect{fill:#ddd}.ag-header-cell.ag-column-focus{border:0px solid #dde2eb!important;border-bottom:2px solid #006dcc!important;color:#006dcc;font-weight:700;background-color:#ddd}.ag-header-cell-comp-wrapper{display:block!important}.ag-cell-label-container{padding:0!important}.ag-pk-aligned-cell{text-align:center}.ag-horizontal-left-spacer{overflow:hidden}.ag-popup-editor{background:#fff;padding:10px;border:solid 1px #006dcc}.ag-overlay-loading-center{border:none!important;box-shadow:none!important}.ag-viewport .ag-row-selected{background-color:#efefef}.ag-viewport .ag-row-selected .ag-cell{background-color:unset!important}.ag-row-selected~.ag-row-selected .ag-cell{border-top-color:#dde2eb}*:not(.ag-row-selected):not(.ag-row-inline-editing)+.ag-row-selected .ag-cell{border-top-color:#006dcc}.ag-row-selected:has(+:not(.ag-row-selected)):not(.ag-row-inline-editing) .ag-cell{border-bottom-color:#006dcc}.ag-cell.invalid .invalid-flag{box-sizing:border-box;position:absolute;left:0;top:0;width:3px;height:3px;border-bottom:solid 3px transparent;border-right:solid 3px transparent;border-left:solid 3px red;border-top:solid 3px red}.ag-grid-row-has-changes-gradient{background-color:#fff;filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr=#fff,endColorstr=#ffffff);background-image:-moz-linear-gradient(top,#fff 0%,#ffffff 25%,#dbffdf 100%);background-image:linear-gradient(top,#fff 0% 25%,#dbffdf);background-image:-webkit-linear-gradient(top,#fff 0%,#ffffff 25%,#dbffdf 100%);background-image:-o-linear-gradient(top,#fff 0%,#ffffff 25%,#dbffdf 100%);background-image:-ms-linear-gradient(top,#fff 0%,#ffffff 25%,#dbffdf 100%);background-image:-webkit-gradient(linear,right top,right bottom,color-stop(0%,#fff),color-stop(25%,#ffffff),color-stop(100%,#dbffdf))}@keyframes ants-marching{0%{background-position:0 0,0 100%,0 0,100% 0}to{background-position:40px 0,-40px 100%,0 -40px,100% 40px}}.animated-border{background-size:10px 0px,10px 0px,1px 10px,1px 10px;background-position:0 0,0 100%,0 0,100% 0;background-repeat:repeat-x,repeat-x,repeat-y,repeat-y;background-image:linear-gradient(to right,#006dcc 50%,transparent 50%),linear-gradient(to right,#006dcc 50%,transparent 50%),linear-gradient(to bottom,#006dcc 50%,transparent 50%),linear-gradient(to bottom,#006dcc 50%,transparent 50%);animation:ants-marching 1s linear infinite}.animated-border.ag-row-selected{background-color:#efefef!important}.animated-border .ag-cell{background-color:unset!important;border-top:none!important;border-bottom:none!important}.animated-border:not(.animated-border+.animated-border){background-size:10px 1px,10px 0px,1px 10px,1px 10px}.animated-border:has(+:not(.animated-border)){background-size:10px 0px,10px 1px,1px 10px,1px 10px}.animated-border:not(.animated-border+.animated-border).animated-border:has(+:not(.animated-border)){background-size:10px 1px,10px 1px,1px 10px,1px 10px}.animated-border:has(+:not(.animated-border)) .ag-cell{height:20px!important}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.spin{animation-name:spin;animation-duration:4s;animation-iteration-count:infinite;animation-timing-function:linear}.detail-container .mat-mdc-tab-body-content tru-data-grid{--grid-scroll-left: 0px;--grid-scroll-right: 0px;--grid-scroll-bottom: 80px;--grid-scroll-visible: 1;display:flex;flex-direction:column;height:100%;min-height:0}.detail-container .mat-mdc-tab-body-content tru-data-grid ag-grid-angular{position:relative!important;inset:0!important;height:calc(100% - var(--grid-footer-height, 23px))!important}.detail-container .mat-mdc-tab-body-content tru-data-grid .ag-root-wrapper,.detail-container .mat-mdc-tab-body-content tru-data-grid .ag-root{height:100%!important;top:0!important;bottom:0!important}.detail-container .mat-mdc-tab-body-content tru-data-grid .tru-data-grid-container{flex:1 1 auto;position:relative;min-height:0}.detail-container .mat-mdc-tab-body-content tru-data-grid .ag-body-viewport{position:relative!important;padding-bottom:calc(var(--grid-footer-height, 23px) + (var(--grid-scroll-bottom, 80px) - var(--grid-footer-height, 23px)) * var(--grid-scroll-visible, 1))!important;overflow-x:auto!important}.detail-container .mat-mdc-tab-body-content tru-data-grid .ag-body-horizontal-scroll-viewport,.detail-container .mat-mdc-tab-body-content tru-data-grid .ag-body-horizontal-scroll{position:fixed!important;left:var(--grid-scroll-left, 0px)!important;right:var(--grid-scroll-right, 0px)!important;width:calc(100% - var(--grid-scroll-left, 0px) - var(--grid-scroll-right, 0px))!important;bottom:var(--grid-scroll-bottom, 80px)!important;height:calc(16px * var(--grid-scroll-visible, 1))!important;min-height:calc(16px * var(--grid-scroll-visible, 1))!important;display:block!important;opacity:var(--grid-scroll-visible, 1)!important;pointer-events:calc(var(--grid-scroll-visible, 1))!important;overflow-x:scroll!important;overflow-y:hidden!important;z-index:9999!important;background:transparent!important}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: MatIconModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: AgGridAngular, selector: "ag-grid-angular", inputs: ["gridOptions", "modules", "statusBar", "sideBar", "suppressContextMenu", "preventDefaultOnContextMenu", "allowContextMenuWithControlKey", "columnMenu", "suppressMenuHide", "enableBrowserTooltips", "tooltipTrigger", "tooltipShowDelay", "tooltipHideDelay", "tooltipMouseTrack", "tooltipShowMode", "tooltipInteraction", "popupParent", "copyHeadersToClipboard", "copyGroupHeadersToClipboard", "clipboardDelimiter", "suppressCopyRowsToClipboard", "suppressCopySingleCellRanges", "suppressLastEmptyLineOnPaste", "suppressClipboardPaste", "suppressClipboardApi", "suppressCutToClipboard", "columnDefs", "defaultColDef", "defaultColGroupDef", "columnTypes", "dataTypeDefinitions", "maintainColumnOrder", "enableStrictPivotColumnOrder", "suppressFieldDotNotation", "headerHeight", "groupHeaderHeight", "floatingFiltersHeight", "pivotHeaderHeight", "pivotGroupHeaderHeight", "allowDragFromColumnsToolPanel", "suppressMovableColumns", "suppressColumnMoveAnimation", "suppressMoveWhenColumnDragging", "suppressDragLeaveHidesColumns", "suppressGroupChangesColumnVisibility", "suppressMakeColumnVisibleAfterUnGroup", "suppressRowGroupHidesColumns", "colResizeDefault", "suppressAutoSize", "autoSizePadding", "skipHeaderOnAutoSize", "autoSizeStrategy", "components", "editType", "singleClickEdit", "suppressClickEdit", "readOnlyEdit", "stopEditingWhenCellsLoseFocus", "enterNavigatesVertically", "enterNavigatesVerticallyAfterEdit", "enableCellEditingOnBackspace", "undoRedoCellEditing", "undoRedoCellEditingLimit", "defaultCsvExportParams", "suppressCsvExport", "defaultExcelExportParams", "suppressExcelExport", "excelStyles", "findSearchValue", "findOptions", "quickFilterText", "cacheQuickFilter", "includeHiddenColumnsInQuickFilter", "quickFilterParser", "quickFilterMatcher", "applyQuickFilterBeforePivotOrAgg", "excludeChildrenWhenTreeDataFiltering", "enableAdvancedFilter", "alwaysPassFilter", "includeHiddenColumnsInAdvancedFilter", "advancedFilterParent", "advancedFilterBuilderParams", "suppressAdvancedFilterEval", "suppressSetFilterByDefault", "enableCharts", "chartThemes", "customChartThemes", "chartThemeOverrides", "chartToolPanelsDef", "chartMenuItems", "loadingCellRenderer", "loadingCellRendererParams", "loadingCellRendererSelector", "localeText", "masterDetail", "keepDetailRows", "keepDetailRowsCount", "detailCellRenderer", "detailCellRendererParams", "detailRowHeight", "detailRowAutoHeight", "context", "alignedGrids", "tabIndex", "rowBuffer", "valueCache", "valueCacheNeverExpires", "enableCellExpressions", "suppressTouch", "suppressFocusAfterRefresh", "suppressBrowserResizeObserver", "suppressPropertyNamesCheck", "suppressChangeDetection", "debug", "loading", "overlayLoadingTemplate", "loadingOverlayComponent", "loadingOverlayComponentParams", "suppressLoadingOverlay", "overlayNoRowsTemplate", "noRowsOverlayComponent", "noRowsOverlayComponentParams", "suppressNoRowsOverlay", "pagination", "paginationPageSize", "paginationPageSizeSelector", "paginationAutoPageSize", "paginateChildRows", "suppressPaginationPanel", "pivotMode", "pivotPanelShow", "pivotMaxGeneratedColumns", "pivotDefaultExpanded", "pivotColumnGroupTotals", "pivotRowTotals", "pivotSuppressAutoColumn", "suppressExpandablePivotGroups", "functionsReadOnly", "aggFuncs", "suppressAggFuncInHeader", "alwaysAggregateAtRootLevel", "aggregateOnlyChangedColumns", "suppressAggFilteredOnly", "removePivotHeaderRowWhenSingleValueColumn", "animateRows", "cellFlashDuration", "cellFadeDuration", "allowShowChangeAfterFilter", "domLayout", "ensureDomOrder", "enableCellSpan", "enableRtl", "suppressColumnVirtualisation", "suppressMaxRenderedRowRestriction", "suppressRowVirtualisation", "rowDragManaged", "suppressRowDrag", "suppressMoveWhenRowDragging", "rowDragEntireRow", "rowDragMultiRow", "rowDragText", "dragAndDropImageComponent", "dragAndDropImageComponentParams", "fullWidthCellRenderer", "fullWidthCellRendererParams", "embedFullWidthRows", "groupDisplayType", "groupDefaultExpanded", "autoGroupColumnDef", "groupMaintainOrder", "groupSelectsChildren", "groupLockGroupColumns", "groupAggFiltering", "groupTotalRow", "grandTotalRow", "suppressStickyTotalRow", "groupSuppressBlankHeader", "groupSelectsFiltered", "showOpenedGroup", "groupHideParentOfSingleChild", "groupRemoveSingleChildren", "groupRemoveLowestSingleChildren", "groupHideOpenParents", "groupAllowUnbalanced", "rowGroupPanelShow", "groupRowRenderer", "groupRowRendererParams", "treeData", "treeDataChildrenField", "treeDataParentIdField", "rowGroupPanelSuppressSort", "suppressGroupRowsSticky", "pinnedTopRowData", "pinnedBottomRowData", "enableRowPinning", "isRowPinnable", "isRowPinned", "rowModelType", "rowData", "asyncTransactionWaitMillis", "suppressModelUpdateAfterUpdateTransaction", "datasource", "cacheOverflowSize", "infiniteInitialRowCount", "serverSideInitialRowCount", "suppressServerSideFullWidthLoadingRow", "cacheBlockSize", "maxBlocksInCache", "maxConcurrentDatasourceRequests", "blockLoadDebounceMillis", "purgeClosedRowNodes", "serverSideDatasource", "serverSideSortAllLevels", "serverSideEnableClientSideSort", "serverSideOnlyRefreshFilteredGroups", "serverSidePivotResultFieldSeparator", "viewportDatasource", "viewportRowModelPageSize", "viewportRowModelBufferSize", "alwaysShowHorizontalScroll", "alwaysShowVerticalScroll", "debounceVerticalScrollbar", "suppressHorizontalScroll", "suppressScrollOnNewData", "suppressScrollWhenPopupsAreOpen", "suppressAnimationFrame", "suppressMiddleClickScrolls", "suppressPreventDefaultOnMouseWheel", "scrollbarWidth", "rowSelection", "cellSelection", "rowMultiSelectWithClick", "suppressRowDeselection", "suppressRowClickSelection", "suppressCellFocus", "suppressHeaderFocus", "selectionColumnDef", "rowNumbers", "suppressMultiRangeSelection", "enableCellTextSelection", "enableRangeSelection", "enableRangeHandle", "enableFillHandle", "fillHandleDirection", "suppressClearOnFillReduction", "sortingOrder", "accentedSort", "unSortIcon", "suppressMultiSort", "alwaysMultiSort", "multiSortKey", "suppressMaintainUnsortedOrder", "icons", "rowHeight", "rowStyle", "rowClass", "rowClassRules", "suppressRowHoverHighlight", "suppressRowTransform", "columnHoverHighlight", "gridId", "deltaSort", "treeDataDisplayType", "enableGroupEdit", "initialState", "theme", "loadThemeGoogleFonts", "themeCssLayer", "styleNonce", "themeStyleContainer", "getContextMenuItems", "getMainMenuItems", "postProcessPopup", "processUnpinnedColumns", "processCellForClipboard", "processHeaderForClipboard", "processGroupHeaderForClipboard", "processCellFromClipboard", "sendToClipboard", "processDataFromClipboard", "isExternalFilterPresent", "doesExternalFilterPass", "getChartToolbarItems", "createChartContainer", "focusGridInnerElement", "navigateToNextHeader", "tabToNextHeader", "navigateToNextCell", "tabToNextCell", "getLocaleText", "getDocument", "paginationNumberFormatter", "getGroupRowAgg", "isGroupOpenByDefault", "initialGroupOrderComparator", "processPivotResultColDef", "processPivotResultColGroupDef", "getDataPath", "getChildCount", "getServerSideGroupLevelParams", "isServerSideGroupOpenByDefault", "isApplyServerSideTransaction", "isServerSideGroup", "getServerSideGroupKey", "getBusinessKeyForNode", "getRowId", "resetRowDataOnUpdate", "processRowPostCreate", "isRowSelectable", "isRowMaster", "fillOperation", "postSortRows", "getRowStyle", "getRowClass", "getRowHeight", "isFullWidthRow"], outputs: ["toolPanelVisibleChanged", "toolPanelSizeChanged", "columnMenuVisibleChanged", "contextMenuVisibleChanged", "cutStart", "cutEnd", "pasteStart", "pasteEnd", "columnVisible", "columnPinned", "columnResized", "columnMoved", "columnValueChanged", "columnPivotModeChanged", "columnPivotChanged", "columnGroupOpened", "newColumnsLoaded", "gridColumnsChanged", "displayedColumnsChanged", "virtualColumnsChanged", "columnEverythingChanged", "columnHeaderMouseOver", "columnHeaderMouseLeave", "columnHeaderClicked", "columnHeaderContextMenu", "componentStateChanged", "cellValueChanged", "cellEditRequest", "rowValueChanged", "cellEditingStarted", "cellEditingStopped", "rowEditingStarted", "rowEditingStopped", "undoStarted", "undoEnded", "redoStarted", "redoEnded", "cellSelectionDeleteStart", "cellSelectionDeleteEnd", "rangeDeleteStart", "rangeDeleteEnd", "fillStart", "fillEnd", "filterOpened", "filterChanged", "filterModified", "advancedFilterBuilderVisibleChanged", "findChanged", "chartCreated", "chartRangeSelectionChanged", "chartOptionsChanged", "chartDestroyed", "cellKeyDown", "gridReady", "firstDataRendered", "gridSizeChanged", "modelUpdated", "virtualRowRemoved", "viewportChanged", "bodyScroll", "bodyScrollEnd", "dragStarted", "dragStopped", "dragCancelled", "stateUpdated", "paginationChanged", "rowDragEnter", "rowDragMove", "rowDragLeave", "rowDragEnd", "rowDragCancel", "rowResizeStarted", "rowResizeEnded", "columnRowGroupChanged", "rowGroupOpened", "expandOrCollapseAll", "pivotMaxColumnsExceeded", "pinnedRowDataChanged", "pinnedRowsChanged", "rowDataUpdated", "asyncTransactionsFlushed", "storeRefreshed", "headerFocused", "cellClicked", "cellDoubleClicked", "cellFocused", "cellMouseOver", "cellMouseOut", "cellMouseDown", "rowClicked", "rowDoubleClicked", "rowSelected", "selectionChanged", "cellContextMenu", "rangeSelectionChanged", "cellSelectionChanged", "tooltipShow", "tooltipHide", "sortChanged"] }, { kind: "component", type: TruToolbar, selector: "tru-toolbar", inputs: ["config"] }, { kind: "component", type: TruToolbarButton, selector: "tru-toolbar-button", inputs: ["config", "icon", "text", "disabled", "tooltip", "type"], outputs: ["onClick", "onKeydown"] }, { kind: "component", type: TruToolbarDropdown, selector: "tru-toolbar-dropdown", inputs: ["config", "options", "tooltip", "multiple", "filterable", "filterTooltip", "width", "selectedOption", "disabled"], outputs: ["selectedOptionChange", "selectionChange"] }, { kind: "component", type: TruToolbarSeparator, selector: "tru-toolbar-separator" }, { kind: "component", type: TruToolbarMenu, selector: "tru-toolbar-menu", inputs: ["config"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
10113
10183
  }
10114
10184
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: TruDataGrid, decorators: [{
10115
10185
  type: Component,
@@ -10126,7 +10196,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImpor
10126
10196
  TruToolbarDropdown,
10127
10197
  TruToolbarSeparator,
10128
10198
  TruToolbarMenu
10129
- ], template: "<div [ngClass]=\"{'tru-data-grid-label-offset': label}\">\r\n <span class=\"tru-data-grid-label\" *ngIf=\"label\">{{label}}</span>\r\n <div class=\"tru-data-grid-toolbar-container\">\r\n <tru-toolbar>\r\n <tru-toolbar-dropdown [options]=\"unassociatedChoices\"\r\n [(selectedOption)]=\"selectedUnassociatedChoice\"\r\n [disabled]=\"!this.config.tableConfig.canAdd\"\r\n (selectionChange)=\"onUnassociatedChoiceChanged($event)\"\r\n *ngIf=\"gridType === TruDataGridTypes.DetailManyToMany\">\r\n </tru-toolbar-dropdown>\r\n <tru-toolbar-button [icon]=\"'tru-toolbar-add-icon'\" [tooltip]=\"'Add - [Ctrl + I]'\" [disabled]=\"!canAdd()\" (onClick)=\"onAdd()\"></tru-toolbar-button>\r\n <tru-toolbar-button [icon]=\"'tru-toolbar-delete-icon'\" [tooltip]=\"'Delete - [Ctrl + D]'\" (click)=\"onDelete()\"></tru-toolbar-button>\r\n <tru-toolbar-button [icon]=\"'tru-toolbar-keep-icon'\" [tooltip]=\"'Keep selected records - [Ctrl + K]'\" (click)=\"onKeep()\"></tru-toolbar-button>\r\n <tru-toolbar-button [icon]=\"'tru-toolbar-remove-icon'\" [tooltip]=\"'Remove selected records - [Ctrl + R]'\" (click)=\"onRemove()\"></tru-toolbar-button>\r\n <tru-toolbar-separator *ngIf=\"parentToolbarTemplate\"></tru-toolbar-separator>\r\n <ng-container *ngTemplateOutlet=\"parentToolbarTemplate\"></ng-container>\r\n <tru-toolbar-separator></tru-toolbar-separator>\r\n <tru-toolbar-button [icon]=\"'tru-toolbar-export-icon'\" [tooltip]=\"'Export'\" (click)=\"onExport()\"></tru-toolbar-button>\r\n <tru-toolbar-separator *ngIf=\"viewMenuItems.length\"></tru-toolbar-separator>\r\n <div *ngIf=\"viewMenuItems.length\">\r\n <tru-toolbar-menu *ngFor=\"let viewMenuItem of viewMenuItems\" [config]=\"viewMenuItem\"></tru-toolbar-menu>\r\n </div>\r\n <ng-container #customToolbarContainer></ng-container>\r\n </tru-toolbar>\r\n </div>\r\n <div #dataGridContainer class=\"tru-data-grid-container\">\r\n <ag-grid-angular class=\"ag-theme-alpine\"\r\n [gridOptions]=\"gridOptions\"\r\n [rowData]=\"rowData\"\r\n [columnDefs]=\"columnDefs\"\r\n [defaultColDef]=\"defaultColDef\"\r\n [headerHeight]=\"25\"\r\n [rowHeight]=\"22\"\r\n [rowBuffer]=\"50\"\r\n [enterNavigatesVertically]=\"true\"\r\n [enterNavigatesVerticallyAfterEdit]=\"true\"\r\n [suppressRowHoverHighlight]=\"true\"\r\n [suppressRowClickSelection]=\"true\"\r\n [stopEditingWhenCellsLoseFocus]=\"false\"\r\n (cellClicked)=\"onCellClicked($event)\"\r\n (cellDoubleClicked)=\"onCellDoubleClicked($event)\"\r\n (cellKeyDown)=\"onCellKeyDown($event)\"\r\n (rowDataChanged)=\"onRowDataChanged($event)\"\r\n (cellMouseDown)=\"onCellMouseDown($event)\"\r\n (cellMouseOver)=\"onCellMouseOver($event)\"\r\n (cellMouseOut)=\"onCellMouseOut($event)\"\r\n (contextmenu)=\"onRightClick($event)\"\r\n (rowClicked)=\"onRowClicked($event)\">\r\n </ag-grid-angular>\r\n </div>\r\n <div class=\"tru-data-grid-statusbar-container\">\r\n <div class=\"tru-data-grid-statusbar-values-container\">\r\n <p class=\"tru-data-grid-statusbar-values-text\">{{rowCount}}</p>\r\n <p class=\"tru-data-grid-statusbar-values-text\">Selected: {{api ? api.getSelectedRows().length : 0}}</p>\r\n </div>\r\n </div>\r\n</div>\r\n", styles: [".tru-data-grid-label{font-size:14px;font-weight:700;margin-bottom:5px;display:flex}.tru-data-grid:first-of-type+.tru-data-grid{margin-left:5px}.tru-data-grid-toolbar-container{display:inline-flex}.tru-data-grid-container{min-height:200px}.tru-data-grid .ag-body-viewport{padding-bottom:var(--grid-footer-height, 23px)}.tru-data-grid-statusbar-container{position:absolute!important;left:0;right:0;bottom:0;height:23px;background:#006dcc;border-bottom-left-radius:0;border-bottom-right-radius:0;border-top:1px solid #e7e7e7}.tru-data-grid-statusbar-container p{float:left;color:#fff;font-weight:700;margin-left:10px;margin-top:0!important;line-height:23px;font-size:12px}.detail-container .tru-data-grid{position:relative}.detail-container .mat-mdc-tab-body-content{height:100%;overflow:hidden!important}.detail-container .ag-root-wrapper{height:calc(100% - 50px)!important}ag-grid-angular{position:absolute;inset:28px 0 23px;width:100%;height:100%}.tru-data-grid-label-offset ag-grid-angular{top:48px;height:calc(100% - 70px)}.ag-root-wrapper{border-top:0px!important}.ag-root{top:0;bottom:23px!important}::ng-deep .mat-mdc-tab-group .ag-root.ag-layout-normal{height:calc(100% - 50px)!important}.search-container .ag-root.ag-layout-normal{height:calc(100% - 50px)!important}.ag-header-cell{padding-left:0!important;padding-right:0!important}.ag-header-cell-resize:after{top:unset!important;height:25px!important}.ag-header-row{font-weight:500!important}.ag-header-cell-label{justify-content:center}.ag-row{border-color:#d8dbe3}.tru-data-grid .ag-row.validation-dialog-open{z-index:2}.ag-row.popup-editing{z-index:unset!important}.ag-row .ag-cell-value{padding-left:2px!important;padding-right:2px!important}.ag-row .ag-cell-value.invalid{border:1px solid red!important;border-radius:0}.ag-row .ag-cell-value.ag-cell-inline-editing:not(.invalid){border-color:#006dcc!important}.ag-ltr .ag-header-cell-resize{right:-5px}.ag-header-cell-text{font-size:12px!important}.ag-cell-inline-editing{height:21px!important}.ag-cell{height:21px;border-right:solid #d8dbe3 1px!important}.ag-cell ng-component{pointer-events:none}.ag-has-focus .ag-cell-focus:not(.invalid){border:solid #006dcc 1px!important}.ag-pinned-left-cols-container .ag-cell.ag-cell-focus{border:solid #dde2eb 1px!important;border-top:0px!important;border-bottom:0px!important}.ag-pinned-left-cols-container .ag-row-selected .ag-cell,.ag-pinned-left-cols-container .ag-row-focus .ag-cell{border:0px solid #dde2eb!important;border-right:2px solid #006dcc!important;color:#fff;font-weight:700;background-color:#006dcc}.ag-pinned-left-cols-container *:not(.ag-row-selected):not(.ag-row-focus) .ag-cell{background-color:#f5f5f5!important}.ag-pinned-left-cols-container *:not(.ag-row-selected):not(.ag-row-focus) .ag-cell:hover{background-color:#2196f31a!important}.ag-header-cell{background-color:#f5f5f5}.ag-column-focus .ag-header-control-sort-container rect{fill:#ddd}.ag-header-cell.ag-column-focus{border:0px solid #dde2eb!important;border-bottom:2px solid #006dcc!important;color:#006dcc;font-weight:700;background-color:#ddd}.ag-header-cell-comp-wrapper{display:block!important}.ag-cell-label-container{padding:0!important}.ag-pk-aligned-cell{text-align:center}.ag-horizontal-left-spacer{overflow:hidden}.ag-popup-editor{background:#fff;padding:10px;border:solid 1px #006dcc}.ag-overlay-loading-center{border:none!important;box-shadow:none!important}.ag-viewport .ag-row-selected{background-color:#efefef}.ag-viewport .ag-row-selected .ag-cell{background-color:unset!important}.ag-row-selected~.ag-row-selected .ag-cell{border-top-color:#dde2eb}*:not(.ag-row-selected):not(.ag-row-inline-editing)+.ag-row-selected .ag-cell{border-top-color:#006dcc}.ag-row-selected:has(+:not(.ag-row-selected)):not(.ag-row-inline-editing) .ag-cell{border-bottom-color:#006dcc}.ag-cell.invalid .invalid-flag{box-sizing:border-box;position:absolute;left:0;top:0;width:3px;height:3px;border-bottom:solid 3px transparent;border-right:solid 3px transparent;border-left:solid 3px red;border-top:solid 3px red}.ag-grid-row-has-changes-gradient{background-color:#fff;filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr=#fff,endColorstr=#ffffff);background-image:-moz-linear-gradient(top,#fff 0%,#ffffff 25%,#dbffdf 100%);background-image:linear-gradient(top,#fff 0% 25%,#dbffdf);background-image:-webkit-linear-gradient(top,#fff 0%,#ffffff 25%,#dbffdf 100%);background-image:-o-linear-gradient(top,#fff 0%,#ffffff 25%,#dbffdf 100%);background-image:-ms-linear-gradient(top,#fff 0%,#ffffff 25%,#dbffdf 100%);background-image:-webkit-gradient(linear,right top,right bottom,color-stop(0%,#fff),color-stop(25%,#ffffff),color-stop(100%,#dbffdf))}@keyframes ants-marching{0%{background-position:0 0,0 100%,0 0,100% 0}to{background-position:40px 0,-40px 100%,0 -40px,100% 40px}}.animated-border{background-size:10px 0px,10px 0px,1px 10px,1px 10px;background-position:0 0,0 100%,0 0,100% 0;background-repeat:repeat-x,repeat-x,repeat-y,repeat-y;background-image:linear-gradient(to right,#006dcc 50%,transparent 50%),linear-gradient(to right,#006dcc 50%,transparent 50%),linear-gradient(to bottom,#006dcc 50%,transparent 50%),linear-gradient(to bottom,#006dcc 50%,transparent 50%);animation:ants-marching 1s linear infinite}.animated-border.ag-row-selected{background-color:#efefef!important}.animated-border .ag-cell{background-color:unset!important;border-top:none!important;border-bottom:none!important}.animated-border:not(.animated-border+.animated-border){background-size:10px 1px,10px 0px,1px 10px,1px 10px}.animated-border:has(+:not(.animated-border)){background-size:10px 0px,10px 1px,1px 10px,1px 10px}.animated-border:not(.animated-border+.animated-border).animated-border:has(+:not(.animated-border)){background-size:10px 1px,10px 1px,1px 10px,1px 10px}.animated-border:has(+:not(.animated-border)) .ag-cell{height:20px!important}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.spin{animation-name:spin;animation-duration:4s;animation-iteration-count:infinite;animation-timing-function:linear}.detail-container .mat-mdc-tab-body-content tru-data-grid{--grid-scroll-left: 0px;--grid-scroll-right: 0px;--grid-scroll-bottom: 80px;--grid-scroll-visible: 1;display:flex;flex-direction:column;height:100%;min-height:0}.detail-container .mat-mdc-tab-body-content tru-data-grid ag-grid-angular{position:relative!important;inset:0!important;height:calc(100% - var(--grid-footer-height, 23px))!important}.detail-container .mat-mdc-tab-body-content tru-data-grid .ag-root-wrapper,.detail-container .mat-mdc-tab-body-content tru-data-grid .ag-root{height:100%!important;top:0!important;bottom:0!important}.detail-container .mat-mdc-tab-body-content tru-data-grid .tru-data-grid-container{flex:1 1 auto;position:relative;min-height:0}.detail-container .mat-mdc-tab-body-content tru-data-grid .ag-body-viewport{position:relative!important;padding-bottom:calc(var(--grid-footer-height, 23px) + (var(--grid-scroll-bottom, 80px) - var(--grid-footer-height, 23px)) * var(--grid-scroll-visible, 1))!important;overflow-x:auto!important}.detail-container .mat-mdc-tab-body-content tru-data-grid .ag-body-horizontal-scroll-viewport,.detail-container .mat-mdc-tab-body-content tru-data-grid .ag-body-horizontal-scroll{position:fixed!important;left:var(--grid-scroll-left, 0px)!important;right:var(--grid-scroll-right, 0px)!important;width:calc(100% - var(--grid-scroll-left, 0px) - var(--grid-scroll-right, 0px))!important;bottom:var(--grid-scroll-bottom, 80px)!important;height:calc(16px * var(--grid-scroll-visible, 1))!important;min-height:calc(16px * var(--grid-scroll-visible, 1))!important;display:block!important;opacity:var(--grid-scroll-visible, 1)!important;pointer-events:calc(var(--grid-scroll-visible, 1))!important;overflow-x:scroll!important;overflow-y:hidden!important;z-index:9999!important;background:transparent!important}\n"] }]
10199
+ ], template: "<div [ngClass]=\"{'tru-data-grid-label-offset': label}\">\r\n <span class=\"tru-data-grid-label\" *ngIf=\"label\">{{label}}</span>\r\n <div class=\"tru-data-grid-toolbar-container\">\r\n <tru-toolbar>\r\n <tru-toolbar-dropdown [options]=\"unassociatedChoices\"\r\n [(selectedOption)]=\"selectedUnassociatedChoice\"\r\n [disabled]=\"!this.config.tableConfig.canAdd\"\r\n (selectionChange)=\"onUnassociatedChoiceChanged($event)\"\r\n *ngIf=\"gridType === TruDataGridTypes.DetailManyToMany\">\r\n </tru-toolbar-dropdown>\r\n <tru-toolbar-button [icon]=\"'tru-toolbar-add-icon'\" [tooltip]=\"'Add - [Ctrl + I]'\" [disabled]=\"!canAdd()\" (onClick)=\"onAdd()\"></tru-toolbar-button>\r\n <tru-toolbar-button [icon]=\"'tru-toolbar-delete-icon'\" [tooltip]=\"'Delete - [Ctrl + D]'\" (click)=\"onDelete()\"></tru-toolbar-button>\r\n <tru-toolbar-button [icon]=\"'tru-toolbar-keep-icon'\" [tooltip]=\"'Keep selected records - [Ctrl + K]'\" (click)=\"onKeep()\"></tru-toolbar-button>\r\n <tru-toolbar-button [icon]=\"'tru-toolbar-remove-icon'\" [tooltip]=\"'Remove selected records - [Ctrl + R]'\" (click)=\"onRemove()\"></tru-toolbar-button>\r\n <tru-toolbar-separator *ngIf=\"parentToolbarTemplate\"></tru-toolbar-separator>\r\n <ng-container *ngTemplateOutlet=\"parentToolbarTemplate\"></ng-container>\r\n <tru-toolbar-separator></tru-toolbar-separator>\r\n <tru-toolbar-button [icon]=\"'tru-toolbar-export-icon'\" [tooltip]=\"'Export'\" (click)=\"onExport()\"></tru-toolbar-button>\r\n <tru-toolbar-separator *ngIf=\"viewMenuItems.length\"></tru-toolbar-separator>\r\n <div *ngIf=\"viewMenuItems.length\">\r\n <tru-toolbar-menu *ngFor=\"let viewMenuItem of viewMenuItems\" [config]=\"viewMenuItem\"></tru-toolbar-menu>\r\n </div>\r\n <ng-container #customToolbarContainer></ng-container>\r\n </tru-toolbar>\r\n </div>\r\n <div #dataGridContainer class=\"tru-data-grid-container\">\r\n <ag-grid-angular class=\"ag-theme-alpine\"\r\n [gridOptions]=\"gridOptions\"\r\n [rowData]=\"rowData\"\r\n [columnDefs]=\"columnDefs\"\r\n [defaultColDef]=\"defaultColDef\"\r\n [headerHeight]=\"25\"\r\n [rowHeight]=\"22\"\r\n [rowBuffer]=\"50\"\r\n [enterNavigatesVertically]=\"true\"\r\n [enterNavigatesVerticallyAfterEdit]=\"true\"\r\n [suppressRowHoverHighlight]=\"true\"\r\n [suppressRowClickSelection]=\"true\"\r\n [stopEditingWhenCellsLoseFocus]=\"false\"\r\n (cellClicked)=\"onCellClicked($event)\"\r\n (cellDoubleClicked)=\"onCellDoubleClicked($event)\"\r\n (cellKeyDown)=\"onCellKeyDown($event)\"\r\n (copy)=\"onCopy($event)\"\r\n (paste)=\"onPaste($event)\"\r\n (rowDataChanged)=\"onRowDataChanged($event)\"\r\n (cellMouseDown)=\"onCellMouseDown($event)\"\r\n (cellMouseOver)=\"onCellMouseOver($event)\"\r\n (cellMouseOut)=\"onCellMouseOut($event)\"\r\n (contextmenu)=\"onRightClick($event)\"\r\n (rowClicked)=\"onRowClicked($event)\">\r\n </ag-grid-angular>\r\n </div>\r\n <div class=\"tru-data-grid-statusbar-container\">\r\n <div class=\"tru-data-grid-statusbar-values-container\">\r\n <p class=\"tru-data-grid-statusbar-values-text\">{{rowCount}}</p>\r\n <p class=\"tru-data-grid-statusbar-values-text\">Selected: {{api ? api.getSelectedRows().length : 0}}</p>\r\n </div>\r\n </div>\r\n</div>\r\n", styles: [".tru-data-grid-label{font-size:14px;font-weight:700;margin-bottom:5px;display:flex}.tru-data-grid:first-of-type+.tru-data-grid{margin-left:5px}.tru-data-grid-toolbar-container{display:inline-flex}.tru-data-grid-container{min-height:200px}.tru-data-grid .ag-body-viewport{padding-bottom:var(--grid-footer-height, 23px)}.tru-data-grid-statusbar-container{position:absolute!important;left:0;right:0;bottom:0;height:23px;background:#006dcc;border-bottom-left-radius:0;border-bottom-right-radius:0;border-top:1px solid #e7e7e7}.tru-data-grid-statusbar-container p{float:left;color:#fff;font-weight:700;margin-left:10px;margin-top:0!important;line-height:23px;font-size:12px}.detail-container .tru-data-grid{position:relative}.detail-container .mat-mdc-tab-body-content{height:100%;overflow:hidden!important}.detail-container .ag-root-wrapper{height:calc(100% - 50px)!important}ag-grid-angular{position:absolute;inset:28px 0 23px;width:100%;height:100%}.tru-data-grid-label-offset ag-grid-angular{top:48px;height:calc(100% - 70px)}.ag-root-wrapper{border-top:0px!important}.ag-root{top:0;bottom:23px!important}::ng-deep .mat-mdc-tab-group .ag-root.ag-layout-normal{height:calc(100% - 50px)!important}.search-container .ag-root.ag-layout-normal{height:calc(100% - 50px)!important}.ag-header-cell{padding-left:0!important;padding-right:0!important}.ag-header-cell-resize:after{top:unset!important;height:25px!important}.ag-header-row{font-weight:500!important}.ag-header-cell-label{justify-content:center}.ag-row{border-color:#d8dbe3}.tru-data-grid .ag-row.validation-dialog-open{z-index:2}.ag-row.popup-editing{z-index:unset!important}.ag-row .ag-cell-value{padding-left:2px!important;padding-right:2px!important}.ag-row .ag-cell-value.invalid{border:1px solid red!important;border-radius:0}.ag-row .ag-cell-value.ag-cell-inline-editing:not(.invalid){border-color:#006dcc!important}.ag-ltr .ag-header-cell-resize{right:-5px}.ag-header-cell-text{font-size:12px!important}.ag-cell-inline-editing{height:21px!important}.ag-cell{height:21px;border-right:solid #d8dbe3 1px!important}.ag-cell ng-component{pointer-events:none}.ag-has-focus .ag-cell-focus:not(.invalid){border:solid #006dcc 1px!important}.ag-pinned-left-cols-container .ag-cell.ag-cell-focus{border:solid #dde2eb 1px!important;border-top:0px!important;border-bottom:0px!important}.ag-pinned-left-cols-container .ag-row-selected .ag-cell,.ag-pinned-left-cols-container .ag-row-focus .ag-cell{border:0px solid #dde2eb!important;border-right:2px solid #006dcc!important;color:#fff;font-weight:700;background-color:#006dcc}.ag-pinned-left-cols-container *:not(.ag-row-selected):not(.ag-row-focus) .ag-cell{background-color:#f5f5f5!important}.ag-pinned-left-cols-container *:not(.ag-row-selected):not(.ag-row-focus) .ag-cell:hover{background-color:#2196f31a!important}.ag-header-cell{background-color:#f5f5f5}.ag-column-focus .ag-header-control-sort-container rect{fill:#ddd}.ag-header-cell.ag-column-focus{border:0px solid #dde2eb!important;border-bottom:2px solid #006dcc!important;color:#006dcc;font-weight:700;background-color:#ddd}.ag-header-cell-comp-wrapper{display:block!important}.ag-cell-label-container{padding:0!important}.ag-pk-aligned-cell{text-align:center}.ag-horizontal-left-spacer{overflow:hidden}.ag-popup-editor{background:#fff;padding:10px;border:solid 1px #006dcc}.ag-overlay-loading-center{border:none!important;box-shadow:none!important}.ag-viewport .ag-row-selected{background-color:#efefef}.ag-viewport .ag-row-selected .ag-cell{background-color:unset!important}.ag-row-selected~.ag-row-selected .ag-cell{border-top-color:#dde2eb}*:not(.ag-row-selected):not(.ag-row-inline-editing)+.ag-row-selected .ag-cell{border-top-color:#006dcc}.ag-row-selected:has(+:not(.ag-row-selected)):not(.ag-row-inline-editing) .ag-cell{border-bottom-color:#006dcc}.ag-cell.invalid .invalid-flag{box-sizing:border-box;position:absolute;left:0;top:0;width:3px;height:3px;border-bottom:solid 3px transparent;border-right:solid 3px transparent;border-left:solid 3px red;border-top:solid 3px red}.ag-grid-row-has-changes-gradient{background-color:#fff;filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr=#fff,endColorstr=#ffffff);background-image:-moz-linear-gradient(top,#fff 0%,#ffffff 25%,#dbffdf 100%);background-image:linear-gradient(top,#fff 0% 25%,#dbffdf);background-image:-webkit-linear-gradient(top,#fff 0%,#ffffff 25%,#dbffdf 100%);background-image:-o-linear-gradient(top,#fff 0%,#ffffff 25%,#dbffdf 100%);background-image:-ms-linear-gradient(top,#fff 0%,#ffffff 25%,#dbffdf 100%);background-image:-webkit-gradient(linear,right top,right bottom,color-stop(0%,#fff),color-stop(25%,#ffffff),color-stop(100%,#dbffdf))}@keyframes ants-marching{0%{background-position:0 0,0 100%,0 0,100% 0}to{background-position:40px 0,-40px 100%,0 -40px,100% 40px}}.animated-border{background-size:10px 0px,10px 0px,1px 10px,1px 10px;background-position:0 0,0 100%,0 0,100% 0;background-repeat:repeat-x,repeat-x,repeat-y,repeat-y;background-image:linear-gradient(to right,#006dcc 50%,transparent 50%),linear-gradient(to right,#006dcc 50%,transparent 50%),linear-gradient(to bottom,#006dcc 50%,transparent 50%),linear-gradient(to bottom,#006dcc 50%,transparent 50%);animation:ants-marching 1s linear infinite}.animated-border.ag-row-selected{background-color:#efefef!important}.animated-border .ag-cell{background-color:unset!important;border-top:none!important;border-bottom:none!important}.animated-border:not(.animated-border+.animated-border){background-size:10px 1px,10px 0px,1px 10px,1px 10px}.animated-border:has(+:not(.animated-border)){background-size:10px 0px,10px 1px,1px 10px,1px 10px}.animated-border:not(.animated-border+.animated-border).animated-border:has(+:not(.animated-border)){background-size:10px 1px,10px 1px,1px 10px,1px 10px}.animated-border:has(+:not(.animated-border)) .ag-cell{height:20px!important}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.spin{animation-name:spin;animation-duration:4s;animation-iteration-count:infinite;animation-timing-function:linear}.detail-container .mat-mdc-tab-body-content tru-data-grid{--grid-scroll-left: 0px;--grid-scroll-right: 0px;--grid-scroll-bottom: 80px;--grid-scroll-visible: 1;display:flex;flex-direction:column;height:100%;min-height:0}.detail-container .mat-mdc-tab-body-content tru-data-grid ag-grid-angular{position:relative!important;inset:0!important;height:calc(100% - var(--grid-footer-height, 23px))!important}.detail-container .mat-mdc-tab-body-content tru-data-grid .ag-root-wrapper,.detail-container .mat-mdc-tab-body-content tru-data-grid .ag-root{height:100%!important;top:0!important;bottom:0!important}.detail-container .mat-mdc-tab-body-content tru-data-grid .tru-data-grid-container{flex:1 1 auto;position:relative;min-height:0}.detail-container .mat-mdc-tab-body-content tru-data-grid .ag-body-viewport{position:relative!important;padding-bottom:calc(var(--grid-footer-height, 23px) + (var(--grid-scroll-bottom, 80px) - var(--grid-footer-height, 23px)) * var(--grid-scroll-visible, 1))!important;overflow-x:auto!important}.detail-container .mat-mdc-tab-body-content tru-data-grid .ag-body-horizontal-scroll-viewport,.detail-container .mat-mdc-tab-body-content tru-data-grid .ag-body-horizontal-scroll{position:fixed!important;left:var(--grid-scroll-left, 0px)!important;right:var(--grid-scroll-right, 0px)!important;width:calc(100% - var(--grid-scroll-left, 0px) - var(--grid-scroll-right, 0px))!important;bottom:var(--grid-scroll-bottom, 80px)!important;height:calc(16px * var(--grid-scroll-visible, 1))!important;min-height:calc(16px * var(--grid-scroll-visible, 1))!important;display:block!important;opacity:var(--grid-scroll-visible, 1)!important;pointer-events:calc(var(--grid-scroll-visible, 1))!important;overflow-x:scroll!important;overflow-y:hidden!important;z-index:9999!important;background:transparent!important}\n"] }]
10130
10200
  }], ctorParameters: () => [{ type: TruDataContext }, { type: TruComponentLookup }, { type: TruSearchResultViewManager }, { type: TruAppEnvironment }, { type: TruSearchViewEventHandler }, { type: TruUiNotification }, { type: TruConnectionHub }, { type: TruWindowEventHandler }, { type: TruDataGridClipboard }, { type: TruUtil }, { type: i0.ApplicationRef }, { type: i0.ChangeDetectorRef }, { type: i0.ElementRef }, { type: TruDesktopViewEventNotifier }, { type: TruTabGroupEventNotifier, decorators: [{
10131
10201
  type: Optional
10132
10202
  }] }], propDecorators: { dataGridContainer: [{