@trudb/tru-common-lib 0.3.2 → 0.4.1

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.
@@ -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
  }
@@ -1030,9 +1021,6 @@ class TruPasswordDialog {
1030
1021
  onShowPasswordClicked = () => {
1031
1022
  this.showPassword = this.showPasswordControl.value;
1032
1023
  };
1033
- get enablePasswordStrength() {
1034
- return this.appEnvironment.enablePasswordStrength;
1035
- }
1036
1024
  get validationMessage() {
1037
1025
  if (!this.admin && !this.currentPasswordControl.value)
1038
1026
  return 'Enter your current password.';
@@ -1041,10 +1029,9 @@ class TruPasswordDialog {
1041
1029
  if (!this.confirmNewPasswordControl.value)
1042
1030
  return 'Confirm your new password.';
1043
1031
  if (this.newPasswordControl.value !== this.confirmNewPasswordControl.value)
1044
- return 'The new passwords do not match.';
1045
- if (this.enablePasswordStrength &&
1046
- (this.passwordScore === null || this.passwordScore < this.minimumPasswordScore))
1047
- return `Choose a password rated ${this.passwordStrengthLabels[this.minimumPasswordScore]} or stronger.`;
1032
+ return 'Passwords do not match.';
1033
+ if (this.passwordScore === null || this.passwordScore < this.minimumPasswordScore)
1034
+ return 'Choose a stronger password.';
1048
1035
  return null;
1049
1036
  }
1050
1037
  canSubmit = () => !this.busy && this.validationMessage === null;
@@ -1090,7 +1077,7 @@ class TruPasswordDialog {
1090
1077
  : null;
1091
1078
  this.passwordScore = result?.score ?? null;
1092
1079
  this.passwordWarning = result?.feedback.warning ?? null;
1093
- this.passwordSuggestions = result?.feedback.suggestions ?? [];
1080
+ this.passwordSuggestions = result?.feedback.suggestions.filter(suggestion => suggestion !== translations.suggestions.anotherWord) ?? [];
1094
1081
  }));
1095
1082
  this.userRef = this.data.userRef;
1096
1083
  this.username = this.data.username;
@@ -1102,7 +1089,7 @@ class TruPasswordDialog {
1102
1089
  this.subs.forEach((s) => s.unsubscribe());
1103
1090
  }
1104
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 });
1105
- 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 @if (enablePasswordStrength) {\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 @if (passwordScore !== null) {\r\n <p>Strength: {{passwordStrengthLabels[passwordScore]}}.\r\n {{passwordScore >= minimumPasswordScore ? 'Minimum strength met.' : 'Minimum required: ' + passwordStrengthLabels[minimumPasswordScore] + '.'}}</p>\r\n } @else {\r\n <p>Minimum strength: {{passwordStrengthLabels[minimumPasswordScore]}}. Use a longer password or several unrelated words.</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 }\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 });
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 @if (passwordScore !== null) {\r\n <p>Strength: {{passwordStrengthLabels[passwordScore]}}.</p>\r\n @if (passwordScore < minimumPasswordScore) {\r\n <p>Use a password rated {{passwordStrengthLabels[minimumPasswordScore]}} or stronger.</p>\r\n <p *ngIf=\"passwordWarning\">{{passwordWarning}}</p>\r\n @for (suggestion of passwordSuggestions; track suggestion) {\r\n <p>{{suggestion}}</p>\r\n }\r\n }\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 });
1106
1093
  }
1107
1094
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: TruPasswordDialog, decorators: [{
1108
1095
  type: Component,
@@ -1111,7 +1098,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImpor
1111
1098
  FormsModule,
1112
1099
  ReactiveFormsModule,
1113
1100
  MaterialModule,
1114
- ], 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 @if (enablePasswordStrength) {\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 @if (passwordScore !== null) {\r\n <p>Strength: {{passwordStrengthLabels[passwordScore]}}.\r\n {{passwordScore >= minimumPasswordScore ? 'Minimum strength met.' : 'Minimum required: ' + passwordStrengthLabels[minimumPasswordScore] + '.'}}</p>\r\n } @else {\r\n <p>Minimum strength: {{passwordStrengthLabels[minimumPasswordScore]}}. Use a longer password or several unrelated words.</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 }\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"] }]
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 @if (passwordScore !== null) {\r\n <p>Strength: {{passwordStrengthLabels[passwordScore]}}.</p>\r\n @if (passwordScore < minimumPasswordScore) {\r\n <p>Use a password rated {{passwordStrengthLabels[minimumPasswordScore]}} or stronger.</p>\r\n <p *ngIf=\"passwordWarning\">{{passwordWarning}}</p>\r\n @for (suggestion of passwordSuggestions; track suggestion) {\r\n <p>{{suggestion}}</p>\r\n }\r\n }\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"] }]
1115
1102
  }], ctorParameters: () => [{ type: i1.MatDialogRef }, { type: i1$2.HttpClient }, { type: TruAppEnvironment }, { type: TruUiNotification }, { type: undefined, decorators: [{
1116
1103
  type: Inject,
1117
1104
  args: [MAT_DIALOG_DATA]
@@ -7507,7 +7494,17 @@ class TruDataGridClipboard {
7507
7494
  this.uiNotification = uiNotification;
7508
7495
  this.modelPropertyLookup = modelPropertyLookup;
7509
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
+ };
7510
7506
  formatCellValue = (controlConfig, propertyConfig, entity, value) => {
7507
+ this.bindControlEntity(controlConfig, entity);
7511
7508
  if (propertyConfig.typeName === 'rich-text-unbounded')
7512
7509
  return this.util.htmlToPlainText(value);
7513
7510
  else if (propertyConfig.typeName === 'foreign-key' || propertyConfig.typeName === 'user-foreign-key')
@@ -7671,26 +7668,38 @@ class TruDataGridClipboard {
7671
7668
  copyRow = async (tableName, columnDefs, copiedRows, includeHeaders = false, event) => {
7672
7669
  if (copiedRows.length) {
7673
7670
  let multiRowString = '';
7674
- if (includeHeaders) {
7675
- let headerRow = [];
7676
- columnDefs
7677
- .forEach((columnDef) => {
7678
- if (!columnDef.pinned)
7679
- headerRow.push(columnDef.headerName);
7680
- });
7681
- multiRowString += headerRow.join('\t') + '\n';
7682
- }
7683
- copiedRows.forEach((copiedRow) => {
7684
- let rowPropertyValues = [];
7685
- columnDefs
7686
- .forEach((columnDef) => {
7687
- let propertyName = columnDef.field;
7688
- let propertyConfig = copiedRow[propertyName]?.property;
7689
- if (propertyConfig && !columnDef.pinned)
7690
- rowPropertyValues.push(this.formatCellValue(copiedRow[propertyName], copiedRow[propertyName].property, copiedRow.$entity, copiedRow.$entity.getProperty(propertyName)));
7671
+ try {
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
7691
  });
7692
- multiRowString += rowPropertyValues.join('\t') + '\n';
7693
- });
7692
+ }
7693
+ catch (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;
7702
+ }
7694
7703
  await this.writeClipboard(multiRowString, {
7695
7704
  kind: 'rows',
7696
7705
  tableName,