ng-configcat-publicapi-ui 5.6.6 → 5.6.8

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.
@@ -2,7 +2,7 @@ import { OVERLAY_DEFAULT_CONFIG, Overlay } from '@angular/cdk/overlay';
2
2
  import { DatePipe, NgClass, NgTemplateOutlet, SlicePipe, KeyValuePipe } from '@angular/common';
3
3
  import { HttpErrorResponse, HttpClient, HTTP_INTERCEPTORS } from '@angular/common/http';
4
4
  import * as i0 from '@angular/core';
5
- import { ChangeDetectionStrategy, Component, inject, Injectable, viewChild, DestroyRef, DOCUMENT, InjectionToken, HostListener, Directive, input, ElementRef, model, effect, output, forwardRef, signal, computed, viewChildren, linkedSignal, ViewEncapsulation, ChangeDetectorRef } from '@angular/core';
5
+ import { ChangeDetectionStrategy, Component, inject, Injectable, DestroyRef, viewChild, SecurityContext, DOCUMENT, InjectionToken, HostListener, Directive, input, ElementRef, model, effect, output, forwardRef, signal, computed, viewChildren, linkedSignal, ViewEncapsulation, ChangeDetectorRef } from '@angular/core';
6
6
  import { MAT_AUTOCOMPLETE_SCROLL_STRATEGY, MatAutocompleteTrigger, MatAutocomplete } from '@angular/material/autocomplete';
7
7
  import { MAT_BUTTON_TOGGLE_DEFAULT_OPTIONS, MatButtonToggleGroup, MatButtonToggle } from '@angular/material/button-toggle';
8
8
  import { MAT_DATEPICKER_SCROLL_STRATEGY, MatDatepicker, MatDatepickerToggle, MatDatepickerInput } from '@angular/material/datepicker';
@@ -16,10 +16,11 @@ import { throwError, timer, Subject, forkJoin, of, tap, catchError as catchError
16
16
  import { retry, catchError, map } from 'rxjs/operators';
17
17
  import { CdkScrollable } from '@angular/cdk/scrolling';
18
18
  import { MatButton, MatIconButton } from '@angular/material/button';
19
- import { MeService, Configuration, ProductsService, ConfigsService, EnvironmentsService, FeatureFlagsSettingsService, FeatureFlagSettingValuesV2Service, FeatureFlagSettingValuesService, IntegrationLinksService, SegmentsService, OrganizationsService, ChangeRequestsApprovalFlowScheduledChangesBetaService, UserComparator, SettingType, PrerequisiteComparator, SegmentComparator, RolloutRuleComparator, EvaluationVersion } from 'ng-configcat-publicapi';
19
+ import { MeService, Configuration, ProductsService, ConfigsService, EnvironmentsService, FeatureFlagsSettingsService, FeatureFlagSettingValuesV2Service, FeatureFlagSettingValuesService, IntegrationLinksService, SegmentsService, OrganizationsService, ChangeRequestsApprovalFlowScheduledChangesService, UserComparator, SettingType, PrerequisiteComparator, SegmentComparator, RolloutRuleComparator, EvaluationVersion } from 'ng-configcat-publicapi';
20
20
  import * as i1 from '@angular/forms';
21
21
  import { FormArray, Validators, FormGroup, FormControl, FormBuilder, NonNullableFormBuilder, FormsModule, ReactiveFormsModule, NG_VALUE_ACCESSOR, NG_VALIDATORS } from '@angular/forms';
22
22
  import { NativeDateAdapter, MatOption, DateAdapter, MatRipple, MatOptgroup } from '@angular/material/core';
23
+ import { DomSanitizer } from '@angular/platform-browser';
23
24
  import { MatIcon } from '@angular/material/icon';
24
25
  import { MatSnackBarRef, MAT_SNACK_BAR_DATA, MatSnackBarAction, MatSnackBarActions, MatSnackBarLabel, MatSnackBar } from '@angular/material/snack-bar';
25
26
  import { takeUntilDestroyed, rxResource } from '@angular/core/rxjs-interop';
@@ -77,7 +78,6 @@ class TooManyRequestInterceptor {
77
78
  if (delayMs > 2000) {
78
79
  return throwError(() => error);
79
80
  }
80
- console.log(`Retrying request after ${delayMs} ms due to 429 Too Many Requests.`);
81
81
  return timer(delayMs);
82
82
  },
83
83
  }), catchError((error) => {
@@ -162,8 +162,8 @@ class PublicApiService {
162
162
  password: basicAuthPassword ?? this.configuration?.basicAuthPassword,
163
163
  }));
164
164
  }
165
- createChangeRequestsApprovalFlowScheduledChangesBetaService(basicAuthUsername = null, basicAuthPassword = null, basePath = null) {
166
- return new ChangeRequestsApprovalFlowScheduledChangesBetaService(this.httpClient, basePath ?? this.configuration?.basePath ?? defaultPublicApiBasePath, new Configuration({
165
+ createChangeRequestsApprovalFlowScheduledChangesService(basicAuthUsername = null, basicAuthPassword = null, basePath = null) {
166
+ return new ChangeRequestsApprovalFlowScheduledChangesService(this.httpClient, basePath ?? this.configuration?.basePath ?? defaultPublicApiBasePath, new Configuration({
167
167
  username: basicAuthUsername ?? this.configuration?.basicAuthUsername,
168
168
  password: basicAuthPassword ?? this.configuration?.basicAuthPassword,
169
169
  }));
@@ -487,6 +487,12 @@ class ConfirmationDialogComponent {
487
487
  constructor() {
488
488
  this.dialogRef = inject(MatDialogRef);
489
489
  this.data = inject(MAT_DIALOG_DATA);
490
+ this.sanitizer = inject(DomSanitizer);
491
+ const { body } = this.data;
492
+ if (typeof body === "string")
493
+ this.bodyText = body;
494
+ else
495
+ this.bodyHtml = body.toSanitizedString(this.sanitizer);
490
496
  }
491
497
  yes() {
492
498
  this.dialogRef.close("yes");
@@ -495,17 +501,23 @@ class ConfirmationDialogComponent {
495
501
  this.dialogRef.close("cancel");
496
502
  }
497
503
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: ConfirmationDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
498
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: ConfirmationDialogComponent, isStandalone: true, selector: "app-confirmation-dialog", ngImport: i0, template: "<h1 mat-dialog-title>{{ data.title }}</h1>\n<div mat-dialog-content>\n <div [innerHTML]=\"data.body\"></div>\n</div>\n<div mat-dialog-actions>\n <button\n type=\"button\"\n mat-flat-button\n data-robot=\"dialogYes\"\n [color]=\"data.yesButtonColor ? data.yesButtonColor : 'primary'\"\n (click)=\"yes()\">\n {{ data.yesButtonLabel ? data.yesButtonLabel : \"Yes\" }}\n </button>\n @if (!data.hideCancelButton) {\n <button type=\"button\" mat-stroked-button data-robot=\"dialogNo\" (click)=\"cancel()\">\n {{ data.cancelButtonLabel ? data.cancelButtonLabel : \"Cancel\" }}\n </button>\n }\n</div>\n", dependencies: [{ kind: "directive", type: MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "directive", type: MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "component", type: 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"] }], changeDetection: i0.ChangeDetectionStrategy.Eager }); }
504
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: ConfirmationDialogComponent, isStandalone: true, selector: "app-confirmation-dialog", ngImport: i0, template: "<h1 mat-dialog-title>{{ data.title }}</h1>\n<div mat-dialog-content>\n @if (bodyText) {\n <div>{{ bodyText }}</div>\n } @else {\n <!-- eslint-disable-next-line @configcat/angular-eslint/no-innerhtml -->\n <div [innerHTML]=\"bodyHtml\"></div>\n }\n</div>\n<div mat-dialog-actions>\n <button\n type=\"button\"\n mat-flat-button\n data-robot=\"dialogYes\"\n [color]=\"data.yesButtonColor ? data.yesButtonColor : 'primary'\"\n (click)=\"yes()\">\n {{ data.yesButtonLabel ? data.yesButtonLabel : \"Yes\" }}\n </button>\n @if (!data.hideCancelButton) {\n <button type=\"button\" mat-stroked-button data-robot=\"dialogNo\" (click)=\"cancel()\">\n {{ data.cancelButtonLabel ? data.cancelButtonLabel : \"Cancel\" }}\n </button>\n }\n</div>\n", dependencies: [{ kind: "directive", type: MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "directive", type: MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "component", type: 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"] }], changeDetection: i0.ChangeDetectionStrategy.Eager }); }
499
505
  }
500
506
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: ConfirmationDialogComponent, decorators: [{
501
507
  type: Component,
502
- args: [{ selector: "app-confirmation-dialog", changeDetection: ChangeDetectionStrategy.Eager, imports: [MatDialogTitle, CdkScrollable, MatDialogContent, MatDialogActions, MatButton], template: "<h1 mat-dialog-title>{{ data.title }}</h1>\n<div mat-dialog-content>\n <div [innerHTML]=\"data.body\"></div>\n</div>\n<div mat-dialog-actions>\n <button\n type=\"button\"\n mat-flat-button\n data-robot=\"dialogYes\"\n [color]=\"data.yesButtonColor ? data.yesButtonColor : 'primary'\"\n (click)=\"yes()\">\n {{ data.yesButtonLabel ? data.yesButtonLabel : \"Yes\" }}\n </button>\n @if (!data.hideCancelButton) {\n <button type=\"button\" mat-stroked-button data-robot=\"dialogNo\" (click)=\"cancel()\">\n {{ data.cancelButtonLabel ? data.cancelButtonLabel : \"Cancel\" }}\n </button>\n }\n</div>\n" }]
503
- }] });
508
+ args: [{ selector: "app-confirmation-dialog", changeDetection: ChangeDetectionStrategy.Eager, imports: [MatDialogTitle, CdkScrollable, MatDialogContent, MatDialogActions, MatButton], template: "<h1 mat-dialog-title>{{ data.title }}</h1>\n<div mat-dialog-content>\n @if (bodyText) {\n <div>{{ bodyText }}</div>\n } @else {\n <!-- eslint-disable-next-line @configcat/angular-eslint/no-innerhtml -->\n <div [innerHTML]=\"bodyHtml\"></div>\n }\n</div>\n<div mat-dialog-actions>\n <button\n type=\"button\"\n mat-flat-button\n data-robot=\"dialogYes\"\n [color]=\"data.yesButtonColor ? data.yesButtonColor : 'primary'\"\n (click)=\"yes()\">\n {{ data.yesButtonLabel ? data.yesButtonLabel : \"Yes\" }}\n </button>\n @if (!data.hideCancelButton) {\n <button type=\"button\" mat-stroked-button data-robot=\"dialogNo\" (click)=\"cancel()\">\n {{ data.cancelButtonLabel ? data.cancelButtonLabel : \"Cancel\" }}\n </button>\n }\n</div>\n" }]
509
+ }], ctorParameters: () => [] });
504
510
 
505
511
  class RerouteDialogComponent {
506
512
  constructor() {
507
513
  this.dialogRef = inject(MatDialogRef);
508
514
  this.data = inject(MAT_DIALOG_DATA);
515
+ this.sanitizer = inject(DomSanitizer);
516
+ const { body } = this.data;
517
+ if (typeof body === "string")
518
+ this.bodyText = body;
519
+ else
520
+ this.bodyHtml = body.toSanitizedString(this.sanitizer);
509
521
  }
510
522
  yes() {
511
523
  this.dialogRef.close("yes");
@@ -514,20 +526,25 @@ class RerouteDialogComponent {
514
526
  this.dialogRef.close("cancel");
515
527
  }
516
528
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: RerouteDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
517
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: RerouteDialogComponent, isStandalone: true, selector: "app-reroute-dialog", ngImport: i0, template: "<h1 mat-dialog-title>{{ data.title }}</h1>\n<div mat-dialog-content>\n <div [innerHTML]=\"data.body\"></div>\n</div>\n<br />\n<div mat-dialog-actions>\n <a class=\"hidden-link\" target=\"_blank\" rel=\"noopener noreferrer\" [href]=\"data.yesButtonLink\">\n <button\n type=\"button\"\n style=\"margin-right: 10px\"\n mat-flat-button\n data-robot=\"dialogYes\"\n [color]=\"data.buttonColor ? data.buttonColor : 'primary'\"\n (click)=\"yes()\">\n {{ data.yesButtonLabel ? data.yesButtonLabel : \"Yes\" }}\n </button>\n </a>\n @if (!data.hideNoButton) {\n <button type=\"button\" mat-stroked-button data-robot=\"dialogNo\" (click)=\"cancel()\">\n {{ data.noButtonLabel ? data.noButtonLabel : \"Cancel\" }}\n </button>\n }\n</div>\n", styles: [".hidden-link:focus{outline:none}\n"], dependencies: [{ kind: "directive", type: MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "directive", type: MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "component", type: 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"] }], changeDetection: i0.ChangeDetectionStrategy.Eager }); }
529
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: RerouteDialogComponent, isStandalone: true, selector: "app-reroute-dialog", ngImport: i0, template: "<h1 mat-dialog-title>{{ data.title }}</h1>\n<div mat-dialog-content>\n @if (bodyText) {\n <div>{{ bodyText }}</div>\n } @else {\n <!-- eslint-disable-next-line @configcat/angular-eslint/no-innerhtml -->\n <div [innerHTML]=\"bodyHtml\"></div>\n }\n</div>\n<br />\n<div mat-dialog-actions>\n <a class=\"hidden-link\" target=\"_blank\" rel=\"noopener noreferrer\" [href]=\"data.yesButtonLink\">\n <button\n type=\"button\"\n style=\"margin-right: 10px\"\n mat-flat-button\n data-robot=\"dialogYes\"\n [color]=\"data.buttonColor ? data.buttonColor : 'primary'\"\n (click)=\"yes()\">\n {{ data.yesButtonLabel ? data.yesButtonLabel : \"Yes\" }}\n </button>\n </a>\n @if (!data.hideNoButton) {\n <button type=\"button\" mat-stroked-button data-robot=\"dialogNo\" (click)=\"cancel()\">\n {{ data.noButtonLabel ? data.noButtonLabel : \"Cancel\" }}\n </button>\n }\n</div>\n", styles: [".hidden-link:focus{outline:none}\n"], dependencies: [{ kind: "directive", type: MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "directive", type: MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "component", type: 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"] }], changeDetection: i0.ChangeDetectionStrategy.Eager }); }
518
530
  }
519
531
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: RerouteDialogComponent, decorators: [{
520
532
  type: Component,
521
- args: [{ selector: "app-reroute-dialog", changeDetection: ChangeDetectionStrategy.Eager, imports: [MatDialogTitle, CdkScrollable, MatDialogContent, MatDialogActions, MatButton], template: "<h1 mat-dialog-title>{{ data.title }}</h1>\n<div mat-dialog-content>\n <div [innerHTML]=\"data.body\"></div>\n</div>\n<br />\n<div mat-dialog-actions>\n <a class=\"hidden-link\" target=\"_blank\" rel=\"noopener noreferrer\" [href]=\"data.yesButtonLink\">\n <button\n type=\"button\"\n style=\"margin-right: 10px\"\n mat-flat-button\n data-robot=\"dialogYes\"\n [color]=\"data.buttonColor ? data.buttonColor : 'primary'\"\n (click)=\"yes()\">\n {{ data.yesButtonLabel ? data.yesButtonLabel : \"Yes\" }}\n </button>\n </a>\n @if (!data.hideNoButton) {\n <button type=\"button\" mat-stroked-button data-robot=\"dialogNo\" (click)=\"cancel()\">\n {{ data.noButtonLabel ? data.noButtonLabel : \"Cancel\" }}\n </button>\n }\n</div>\n", styles: [".hidden-link:focus{outline:none}\n"] }]
522
- }] });
533
+ args: [{ selector: "app-reroute-dialog", changeDetection: ChangeDetectionStrategy.Eager, imports: [MatDialogTitle, CdkScrollable, MatDialogContent, MatDialogActions, MatButton], template: "<h1 mat-dialog-title>{{ data.title }}</h1>\n<div mat-dialog-content>\n @if (bodyText) {\n <div>{{ bodyText }}</div>\n } @else {\n <!-- eslint-disable-next-line @configcat/angular-eslint/no-innerhtml -->\n <div [innerHTML]=\"bodyHtml\"></div>\n }\n</div>\n<br />\n<div mat-dialog-actions>\n <a class=\"hidden-link\" target=\"_blank\" rel=\"noopener noreferrer\" [href]=\"data.yesButtonLink\">\n <button\n type=\"button\"\n style=\"margin-right: 10px\"\n mat-flat-button\n data-robot=\"dialogYes\"\n [color]=\"data.buttonColor ? data.buttonColor : 'primary'\"\n (click)=\"yes()\">\n {{ data.yesButtonLabel ? data.yesButtonLabel : \"Yes\" }}\n </button>\n </a>\n @if (!data.hideNoButton) {\n <button type=\"button\" mat-stroked-button data-robot=\"dialogNo\" (click)=\"cancel()\">\n {{ data.noButtonLabel ? data.noButtonLabel : \"Cancel\" }}\n </button>\n }\n</div>\n", styles: [".hidden-link:focus{outline:none}\n"] }]
534
+ }], ctorParameters: () => [] });
523
535
 
524
536
  class InfoDialogComponent {
525
537
  constructor() {
526
538
  this.dialogRef = inject(MatDialogRef);
527
539
  this.data = inject(MAT_DIALOG_DATA);
528
- const data = this.data;
529
- if (data.iconName) {
530
- this.iconCssClass = "icon-" + data.iconName.replace("_", "-");
540
+ this.sanitizer = inject(DomSanitizer);
541
+ const { body, iconName } = this.data;
542
+ if (typeof body === "string")
543
+ this.bodyText = body;
544
+ else
545
+ this.bodyHtml = body.toSanitizedString(this.sanitizer);
546
+ if (iconName) {
547
+ this.iconCssClass = "icon-" + iconName.replace("_", "-");
531
548
  }
532
549
  }
533
550
  ok() {
@@ -538,11 +555,11 @@ class InfoDialogComponent {
538
555
  this.dialogRef.close("nok");
539
556
  }
540
557
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: InfoDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
541
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: InfoDialogComponent, isStandalone: true, selector: "app-info-dialog", ngImport: i0, template: "<h1 mat-dialog-title>{{ data.title }}</h1>\n<div mat-dialog-content [class.content-with-icon]=\"!!data.iconName\">\n @if (!!data.iconName) {\n <mat-icon class=\"icon\" [ngClass]=\"iconCssClass\" [style.color]=\"data.iconColor\">{{ data.iconName }}</mat-icon>\n }\n <div [innerHTML]=\"data.body\"></div>\n</div>\n<div mat-dialog-actions>\n <button type=\"button\" mat-flat-button color=\"primary\" (click)=\"ok()\">{{ data.primaryButtonLabel ?? \"OK\" }}</button>\n @if (data.secondaryButtonLabel) {\n <button type=\"button\" mat-stroked-button (click)=\"secondaryButtonAction()\">{{ data.secondaryButtonLabel }}</button>\n }\n</div>\n", styles: [".content-with-icon{display:flex;align-items:center}.content-with-icon .icon{font-size:3rem;flex-shrink:0;margin-right:.5rem}.content-with-icon .icon.mat-icon{width:auto;height:auto}.content-with-icon .icon-error{color:#d24c41}.content-with-icon .icon-warning{color:orange}.content-with-icon .icon-info{color:#1c86f8}\n"], dependencies: [{ kind: "directive", type: MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "component", type: 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: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], changeDetection: i0.ChangeDetectionStrategy.Eager }); }
558
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: InfoDialogComponent, isStandalone: true, selector: "app-info-dialog", ngImport: i0, template: "<h1 mat-dialog-title>{{ data.title }}</h1>\n<div mat-dialog-content [class.content-with-icon]=\"!!data.iconName\">\n @if (!!data.iconName) {\n <mat-icon class=\"icon\" [ngClass]=\"iconCssClass\" [style.color]=\"data.iconColor\">{{ data.iconName }}</mat-icon>\n }\n @if (bodyText) {\n <div>{{ bodyText }}</div>\n } @else {\n <!-- eslint-disable-next-line @configcat/angular-eslint/no-innerhtml -->\n <div [innerHTML]=\"bodyHtml\"></div>\n }\n</div>\n<div mat-dialog-actions>\n <button type=\"button\" mat-flat-button color=\"primary\" (click)=\"ok()\">{{ data.primaryButtonLabel ?? \"OK\" }}</button>\n @if (data.secondaryButtonLabel) {\n <button type=\"button\" mat-stroked-button (click)=\"secondaryButtonAction()\">{{ data.secondaryButtonLabel }}</button>\n }\n</div>\n", styles: [".content-with-icon{display:flex;align-items:center}.content-with-icon .icon{font-size:3rem;flex-shrink:0;margin-right:.5rem}.content-with-icon .icon.mat-icon{width:auto;height:auto}.content-with-icon .icon-error{color:#d24c41}.content-with-icon .icon-warning{color:orange}.content-with-icon .icon-info{color:#1c86f8}\n"], dependencies: [{ kind: "directive", type: MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "component", type: 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: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], changeDetection: i0.ChangeDetectionStrategy.Eager }); }
542
559
  }
543
560
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: InfoDialogComponent, decorators: [{
544
561
  type: Component,
545
- args: [{ selector: "app-info-dialog", changeDetection: ChangeDetectionStrategy.Eager, imports: [MatDialogTitle, CdkScrollable, MatDialogContent, MatIcon, MatDialogActions, MatButton, NgClass], template: "<h1 mat-dialog-title>{{ data.title }}</h1>\n<div mat-dialog-content [class.content-with-icon]=\"!!data.iconName\">\n @if (!!data.iconName) {\n <mat-icon class=\"icon\" [ngClass]=\"iconCssClass\" [style.color]=\"data.iconColor\">{{ data.iconName }}</mat-icon>\n }\n <div [innerHTML]=\"data.body\"></div>\n</div>\n<div mat-dialog-actions>\n <button type=\"button\" mat-flat-button color=\"primary\" (click)=\"ok()\">{{ data.primaryButtonLabel ?? \"OK\" }}</button>\n @if (data.secondaryButtonLabel) {\n <button type=\"button\" mat-stroked-button (click)=\"secondaryButtonAction()\">{{ data.secondaryButtonLabel }}</button>\n }\n</div>\n", styles: [".content-with-icon{display:flex;align-items:center}.content-with-icon .icon{font-size:3rem;flex-shrink:0;margin-right:.5rem}.content-with-icon .icon.mat-icon{width:auto;height:auto}.content-with-icon .icon-error{color:#d24c41}.content-with-icon .icon-warning{color:orange}.content-with-icon .icon-info{color:#1c86f8}\n"] }]
562
+ args: [{ selector: "app-info-dialog", changeDetection: ChangeDetectionStrategy.Eager, imports: [MatDialogTitle, CdkScrollable, MatDialogContent, MatIcon, MatDialogActions, MatButton, NgClass], template: "<h1 mat-dialog-title>{{ data.title }}</h1>\n<div mat-dialog-content [class.content-with-icon]=\"!!data.iconName\">\n @if (!!data.iconName) {\n <mat-icon class=\"icon\" [ngClass]=\"iconCssClass\" [style.color]=\"data.iconColor\">{{ data.iconName }}</mat-icon>\n }\n @if (bodyText) {\n <div>{{ bodyText }}</div>\n } @else {\n <!-- eslint-disable-next-line @configcat/angular-eslint/no-innerhtml -->\n <div [innerHTML]=\"bodyHtml\"></div>\n }\n</div>\n<div mat-dialog-actions>\n <button type=\"button\" mat-flat-button color=\"primary\" (click)=\"ok()\">{{ data.primaryButtonLabel ?? \"OK\" }}</button>\n @if (data.secondaryButtonLabel) {\n <button type=\"button\" mat-stroked-button (click)=\"secondaryButtonAction()\">{{ data.secondaryButtonLabel }}</button>\n }\n</div>\n", styles: [".content-with-icon{display:flex;align-items:center}.content-with-icon .icon{font-size:3rem;flex-shrink:0;margin-right:.5rem}.content-with-icon .icon.mat-icon{width:auto;height:auto}.content-with-icon .icon-error{color:#d24c41}.content-with-icon .icon-warning{color:orange}.content-with-icon .icon-info{color:#1c86f8}\n"] }]
546
563
  }], ctorParameters: () => [] });
547
564
 
548
565
  var NotificationSnackColor;
@@ -556,10 +573,16 @@ class NotificationSnackComponent {
556
573
  constructor() {
557
574
  this.snackBarRef = inject(MatSnackBarRef);
558
575
  this.data = inject(MAT_SNACK_BAR_DATA);
576
+ this.sanitizer = inject(DomSanitizer);
577
+ this.destroyRef = inject(DestroyRef);
559
578
  this.NotificationSnackColor = NotificationSnackColor;
560
579
  this.progressBar = viewChild.required("progressBar", /* @ts-ignore */
561
580
  ...(ngDevMode ? [{ debugName: "progressBar" }] : /* istanbul ignore next */ []));
562
- this.destroyRef = inject(DestroyRef);
581
+ const { message } = this.data;
582
+ if (typeof message === "string")
583
+ this.messageText = message;
584
+ else
585
+ this.messageHtml = message?.toSanitizedString(this.sanitizer);
563
586
  this.snackBarRef
564
587
  .afterOpened()
565
588
  .pipe(takeUntilDestroyed(this.destroyRef))
@@ -583,41 +606,142 @@ class NotificationSnackComponent {
583
606
  });
584
607
  }
585
608
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: NotificationSnackComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
586
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.1.1", type: NotificationSnackComponent, isStandalone: true, selector: "app-notification-snack", viewQueries: [{ propertyName: "progressBar", first: true, predicate: ["progressBar"], descendants: true, isSignal: true }], ngImport: i0, template: "<div\n class=\"snack-container\"\n [class.green]=\"data.color === NotificationSnackColor.Green\"\n [class.yellow]=\"data.color === NotificationSnackColor.Yellow\"\n [class.red]=\"data.color === NotificationSnackColor.Red\"\n [class.blue]=\"data.color === NotificationSnackColor.Blue\">\n <div\n class=\"content\"\n matSnackBarLabel\n [class.error]=\"data.color === NotificationSnackColor.Red\"\n [class.warn]=\"data.color === NotificationSnackColor.Yellow\"\n [class.success]=\"data.color === NotificationSnackColor.Green\"\n [class.info]=\"data.color === NotificationSnackColor.Blue\">\n <div [innerHTML]=\"data.message\"></div>\n </div>\n\n <span matSnackBarActions>\n <button\n type=\"button\"\n class=\"dismiss\"\n mat-icon-button\n matSnackBarAction\n [class.green]=\"data.color === NotificationSnackColor.Green\"\n [class.yellow]=\"data.color === NotificationSnackColor.Yellow\"\n [class.red]=\"data.color === NotificationSnackColor.Red\"\n [class.blue]=\"data.color === NotificationSnackColor.Blue\"\n (click)=\"snackBarRef.dismissWithAction()\">\n <mat-icon>close</mat-icon>\n </button>\n </span>\n\n <div class=\"progress-container\">\n <div\n #progressBar\n class=\"progress-bar\"\n [class.green]=\"data.color === NotificationSnackColor.Green\"\n [class.yellow]=\"data.color === NotificationSnackColor.Yellow\"\n [class.red]=\"data.color === NotificationSnackColor.Red\"\n [class.blue]=\"data.color === NotificationSnackColor.Blue\"></div>\n </div>\n</div>\n", styles: [".snack-container{display:flex;border-radius:4px}.snack-container .content{display:flex;align-items:center;color:var(--notification-font-color)}.snack-container .content.error:before{font-family:Material Icons;direction:ltr;display:inline-block;font-size:24px;font-style:normal;font-weight:400;letter-spacing:normal;line-height:1;text-transform:none;white-space:nowrap;word-wrap:normal;content:var(--error-icon-content);color:var(--notification-error-icon-color);margin-right:12px}.snack-container .content.warn:before{font-family:Material Icons;direction:ltr;display:inline-block;font-size:24px;font-style:normal;font-weight:400;letter-spacing:normal;line-height:1;text-transform:none;white-space:nowrap;word-wrap:normal;content:var(--warn-icon-content);color:var(--notification-warn-icon-color);margin-right:12px}.snack-container .content.success:before{font-family:Material Icons;direction:ltr;display:inline-block;font-size:24px;font-style:normal;font-weight:400;letter-spacing:normal;line-height:1;text-transform:none;white-space:nowrap;word-wrap:normal;content:var(--success-icon-content);color:var(--notification-success-icon-color);margin-right:12px}.snack-container .content.info:before{font-family:Material Icons;direction:ltr;display:inline-block;font-size:24px;font-style:normal;font-weight:400;letter-spacing:normal;line-height:1;text-transform:none;white-space:nowrap;word-wrap:normal;content:var(--info-icon-content);color:var(--notification-info-icon-color);margin-right:12px}.snack-container.green{background-color:var(--notification-success-background-color)}.snack-container.red{background-color:var(--notification-error-background-color)}.snack-container.yellow{background-color:var(--notification-warn-background-color)}.snack-container.blue{background-color:var(--notification-info-background-color)}.snack-container .dismiss{margin-right:4px}.snack-container .dismiss.green{color:var(--notification-success-icon-color)}.snack-container .dismiss.red{color:var(--notification-error-icon-color)}.snack-container .dismiss.yellow{color:var(--notification-warn-icon-color)}.snack-container .dismiss.blue{color:var(--notification-info-icon-color)}.snack-container .dismiss mat-icon{font-size:20px;width:20px;height:20px}.snack-container .progress-container{width:100%;height:3px;position:absolute;left:0;bottom:0;border-bottom-left-radius:4px;border-bottom-right-radius:4px;overflow:hidden}.snack-container .progress-container .progress-bar{opacity:.6;width:100%;height:100%;transition:width .5s;transition-timing-function:linear}.snack-container .progress-container .progress-bar.green{background-color:var(--notification-success-icon-color)}.snack-container .progress-container .progress-bar.red{background-color:var(--notification-error-icon-color)}.snack-container .progress-container .progress-bar.yellow{background-color:var(--notification-warn-icon-color)}.snack-container .progress-container .progress-bar.blue{background-color:var(--notification-info-icon-color)}\n"], dependencies: [{ kind: "component", type: MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "directive", type: MatSnackBarAction, selector: "[matSnackBarAction]" }, { kind: "directive", type: MatSnackBarActions, selector: "[matSnackBarActions]" }, { kind: "directive", type: MatSnackBarLabel, selector: "[matSnackBarLabel]" }, { kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }], changeDetection: i0.ChangeDetectionStrategy.Eager }); }
609
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: NotificationSnackComponent, isStandalone: true, selector: "app-notification-snack", viewQueries: [{ propertyName: "progressBar", first: true, predicate: ["progressBar"], descendants: true, isSignal: true }], ngImport: i0, template: "<div\n class=\"snack-container\"\n [class.green]=\"data.color === NotificationSnackColor.Green\"\n [class.yellow]=\"data.color === NotificationSnackColor.Yellow\"\n [class.red]=\"data.color === NotificationSnackColor.Red\"\n [class.blue]=\"data.color === NotificationSnackColor.Blue\">\n <div\n class=\"content\"\n matSnackBarLabel\n [class.error]=\"data.color === NotificationSnackColor.Red\"\n [class.warn]=\"data.color === NotificationSnackColor.Yellow\"\n [class.success]=\"data.color === NotificationSnackColor.Green\"\n [class.info]=\"data.color === NotificationSnackColor.Blue\">\n @if (messageText) {\n <div>{{ messageText }}</div>\n } @else {\n <!-- eslint-disable-next-line @configcat/angular-eslint/no-innerhtml -->\n <div [innerHTML]=\"messageHtml\"></div>\n }\n </div>\n\n <span matSnackBarActions>\n <button\n type=\"button\"\n class=\"dismiss\"\n mat-icon-button\n matSnackBarAction\n [class.green]=\"data.color === NotificationSnackColor.Green\"\n [class.yellow]=\"data.color === NotificationSnackColor.Yellow\"\n [class.red]=\"data.color === NotificationSnackColor.Red\"\n [class.blue]=\"data.color === NotificationSnackColor.Blue\"\n (click)=\"snackBarRef.dismissWithAction()\">\n <mat-icon>close</mat-icon>\n </button>\n </span>\n\n <div class=\"progress-container\">\n <div\n #progressBar\n class=\"progress-bar\"\n [class.green]=\"data.color === NotificationSnackColor.Green\"\n [class.yellow]=\"data.color === NotificationSnackColor.Yellow\"\n [class.red]=\"data.color === NotificationSnackColor.Red\"\n [class.blue]=\"data.color === NotificationSnackColor.Blue\"></div>\n </div>\n</div>\n", styles: [".snack-container{display:flex;border-radius:4px}.snack-container .content{display:flex;align-items:center;color:var(--notification-font-color)}.snack-container .content.error:before{font-family:Material Icons;direction:ltr;display:inline-block;font-size:24px;font-style:normal;font-weight:400;letter-spacing:normal;line-height:1;text-transform:none;white-space:nowrap;word-wrap:normal;content:var(--error-icon-content);color:var(--notification-error-icon-color);margin-right:12px}.snack-container .content.warn:before{font-family:Material Icons;direction:ltr;display:inline-block;font-size:24px;font-style:normal;font-weight:400;letter-spacing:normal;line-height:1;text-transform:none;white-space:nowrap;word-wrap:normal;content:var(--warn-icon-content);color:var(--notification-warn-icon-color);margin-right:12px}.snack-container .content.success:before{font-family:Material Icons;direction:ltr;display:inline-block;font-size:24px;font-style:normal;font-weight:400;letter-spacing:normal;line-height:1;text-transform:none;white-space:nowrap;word-wrap:normal;content:var(--success-icon-content);color:var(--notification-success-icon-color);margin-right:12px}.snack-container .content.info:before{font-family:Material Icons;direction:ltr;display:inline-block;font-size:24px;font-style:normal;font-weight:400;letter-spacing:normal;line-height:1;text-transform:none;white-space:nowrap;word-wrap:normal;content:var(--info-icon-content);color:var(--notification-info-icon-color);margin-right:12px}.snack-container.green{background-color:var(--notification-success-background-color)}.snack-container.red{background-color:var(--notification-error-background-color)}.snack-container.yellow{background-color:var(--notification-warn-background-color)}.snack-container.blue{background-color:var(--notification-info-background-color)}.snack-container .dismiss{margin-right:4px}.snack-container .dismiss.green{color:var(--notification-success-icon-color)}.snack-container .dismiss.red{color:var(--notification-error-icon-color)}.snack-container .dismiss.yellow{color:var(--notification-warn-icon-color)}.snack-container .dismiss.blue{color:var(--notification-info-icon-color)}.snack-container .dismiss mat-icon{font-size:20px;width:20px;height:20px}.snack-container .progress-container{width:100%;height:3px;position:absolute;left:0;bottom:0;border-bottom-left-radius:4px;border-bottom-right-radius:4px;overflow:hidden}.snack-container .progress-container .progress-bar{opacity:.6;width:100%;height:100%;transition:width .5s;transition-timing-function:linear}.snack-container .progress-container .progress-bar.green{background-color:var(--notification-success-icon-color)}.snack-container .progress-container .progress-bar.red{background-color:var(--notification-error-icon-color)}.snack-container .progress-container .progress-bar.yellow{background-color:var(--notification-warn-icon-color)}.snack-container .progress-container .progress-bar.blue{background-color:var(--notification-info-icon-color)}\n"], dependencies: [{ kind: "component", type: MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "directive", type: MatSnackBarAction, selector: "[matSnackBarAction]" }, { kind: "directive", type: MatSnackBarActions, selector: "[matSnackBarActions]" }, { kind: "directive", type: MatSnackBarLabel, selector: "[matSnackBarLabel]" }, { kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }], changeDetection: i0.ChangeDetectionStrategy.Eager }); }
587
610
  }
588
611
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: NotificationSnackComponent, decorators: [{
589
612
  type: Component,
590
- args: [{ selector: "app-notification-snack", changeDetection: ChangeDetectionStrategy.Eager, imports: [MatIconButton, MatSnackBarAction, MatSnackBarActions, MatSnackBarLabel, MatIcon], template: "<div\n class=\"snack-container\"\n [class.green]=\"data.color === NotificationSnackColor.Green\"\n [class.yellow]=\"data.color === NotificationSnackColor.Yellow\"\n [class.red]=\"data.color === NotificationSnackColor.Red\"\n [class.blue]=\"data.color === NotificationSnackColor.Blue\">\n <div\n class=\"content\"\n matSnackBarLabel\n [class.error]=\"data.color === NotificationSnackColor.Red\"\n [class.warn]=\"data.color === NotificationSnackColor.Yellow\"\n [class.success]=\"data.color === NotificationSnackColor.Green\"\n [class.info]=\"data.color === NotificationSnackColor.Blue\">\n <div [innerHTML]=\"data.message\"></div>\n </div>\n\n <span matSnackBarActions>\n <button\n type=\"button\"\n class=\"dismiss\"\n mat-icon-button\n matSnackBarAction\n [class.green]=\"data.color === NotificationSnackColor.Green\"\n [class.yellow]=\"data.color === NotificationSnackColor.Yellow\"\n [class.red]=\"data.color === NotificationSnackColor.Red\"\n [class.blue]=\"data.color === NotificationSnackColor.Blue\"\n (click)=\"snackBarRef.dismissWithAction()\">\n <mat-icon>close</mat-icon>\n </button>\n </span>\n\n <div class=\"progress-container\">\n <div\n #progressBar\n class=\"progress-bar\"\n [class.green]=\"data.color === NotificationSnackColor.Green\"\n [class.yellow]=\"data.color === NotificationSnackColor.Yellow\"\n [class.red]=\"data.color === NotificationSnackColor.Red\"\n [class.blue]=\"data.color === NotificationSnackColor.Blue\"></div>\n </div>\n</div>\n", styles: [".snack-container{display:flex;border-radius:4px}.snack-container .content{display:flex;align-items:center;color:var(--notification-font-color)}.snack-container .content.error:before{font-family:Material Icons;direction:ltr;display:inline-block;font-size:24px;font-style:normal;font-weight:400;letter-spacing:normal;line-height:1;text-transform:none;white-space:nowrap;word-wrap:normal;content:var(--error-icon-content);color:var(--notification-error-icon-color);margin-right:12px}.snack-container .content.warn:before{font-family:Material Icons;direction:ltr;display:inline-block;font-size:24px;font-style:normal;font-weight:400;letter-spacing:normal;line-height:1;text-transform:none;white-space:nowrap;word-wrap:normal;content:var(--warn-icon-content);color:var(--notification-warn-icon-color);margin-right:12px}.snack-container .content.success:before{font-family:Material Icons;direction:ltr;display:inline-block;font-size:24px;font-style:normal;font-weight:400;letter-spacing:normal;line-height:1;text-transform:none;white-space:nowrap;word-wrap:normal;content:var(--success-icon-content);color:var(--notification-success-icon-color);margin-right:12px}.snack-container .content.info:before{font-family:Material Icons;direction:ltr;display:inline-block;font-size:24px;font-style:normal;font-weight:400;letter-spacing:normal;line-height:1;text-transform:none;white-space:nowrap;word-wrap:normal;content:var(--info-icon-content);color:var(--notification-info-icon-color);margin-right:12px}.snack-container.green{background-color:var(--notification-success-background-color)}.snack-container.red{background-color:var(--notification-error-background-color)}.snack-container.yellow{background-color:var(--notification-warn-background-color)}.snack-container.blue{background-color:var(--notification-info-background-color)}.snack-container .dismiss{margin-right:4px}.snack-container .dismiss.green{color:var(--notification-success-icon-color)}.snack-container .dismiss.red{color:var(--notification-error-icon-color)}.snack-container .dismiss.yellow{color:var(--notification-warn-icon-color)}.snack-container .dismiss.blue{color:var(--notification-info-icon-color)}.snack-container .dismiss mat-icon{font-size:20px;width:20px;height:20px}.snack-container .progress-container{width:100%;height:3px;position:absolute;left:0;bottom:0;border-bottom-left-radius:4px;border-bottom-right-radius:4px;overflow:hidden}.snack-container .progress-container .progress-bar{opacity:.6;width:100%;height:100%;transition:width .5s;transition-timing-function:linear}.snack-container .progress-container .progress-bar.green{background-color:var(--notification-success-icon-color)}.snack-container .progress-container .progress-bar.red{background-color:var(--notification-error-icon-color)}.snack-container .progress-container .progress-bar.yellow{background-color:var(--notification-warn-icon-color)}.snack-container .progress-container .progress-bar.blue{background-color:var(--notification-info-icon-color)}\n"] }]
613
+ args: [{ selector: "app-notification-snack", changeDetection: ChangeDetectionStrategy.Eager, imports: [MatIconButton, MatSnackBarAction, MatSnackBarActions, MatSnackBarLabel, MatIcon], template: "<div\n class=\"snack-container\"\n [class.green]=\"data.color === NotificationSnackColor.Green\"\n [class.yellow]=\"data.color === NotificationSnackColor.Yellow\"\n [class.red]=\"data.color === NotificationSnackColor.Red\"\n [class.blue]=\"data.color === NotificationSnackColor.Blue\">\n <div\n class=\"content\"\n matSnackBarLabel\n [class.error]=\"data.color === NotificationSnackColor.Red\"\n [class.warn]=\"data.color === NotificationSnackColor.Yellow\"\n [class.success]=\"data.color === NotificationSnackColor.Green\"\n [class.info]=\"data.color === NotificationSnackColor.Blue\">\n @if (messageText) {\n <div>{{ messageText }}</div>\n } @else {\n <!-- eslint-disable-next-line @configcat/angular-eslint/no-innerhtml -->\n <div [innerHTML]=\"messageHtml\"></div>\n }\n </div>\n\n <span matSnackBarActions>\n <button\n type=\"button\"\n class=\"dismiss\"\n mat-icon-button\n matSnackBarAction\n [class.green]=\"data.color === NotificationSnackColor.Green\"\n [class.yellow]=\"data.color === NotificationSnackColor.Yellow\"\n [class.red]=\"data.color === NotificationSnackColor.Red\"\n [class.blue]=\"data.color === NotificationSnackColor.Blue\"\n (click)=\"snackBarRef.dismissWithAction()\">\n <mat-icon>close</mat-icon>\n </button>\n </span>\n\n <div class=\"progress-container\">\n <div\n #progressBar\n class=\"progress-bar\"\n [class.green]=\"data.color === NotificationSnackColor.Green\"\n [class.yellow]=\"data.color === NotificationSnackColor.Yellow\"\n [class.red]=\"data.color === NotificationSnackColor.Red\"\n [class.blue]=\"data.color === NotificationSnackColor.Blue\"></div>\n </div>\n</div>\n", styles: [".snack-container{display:flex;border-radius:4px}.snack-container .content{display:flex;align-items:center;color:var(--notification-font-color)}.snack-container .content.error:before{font-family:Material Icons;direction:ltr;display:inline-block;font-size:24px;font-style:normal;font-weight:400;letter-spacing:normal;line-height:1;text-transform:none;white-space:nowrap;word-wrap:normal;content:var(--error-icon-content);color:var(--notification-error-icon-color);margin-right:12px}.snack-container .content.warn:before{font-family:Material Icons;direction:ltr;display:inline-block;font-size:24px;font-style:normal;font-weight:400;letter-spacing:normal;line-height:1;text-transform:none;white-space:nowrap;word-wrap:normal;content:var(--warn-icon-content);color:var(--notification-warn-icon-color);margin-right:12px}.snack-container .content.success:before{font-family:Material Icons;direction:ltr;display:inline-block;font-size:24px;font-style:normal;font-weight:400;letter-spacing:normal;line-height:1;text-transform:none;white-space:nowrap;word-wrap:normal;content:var(--success-icon-content);color:var(--notification-success-icon-color);margin-right:12px}.snack-container .content.info:before{font-family:Material Icons;direction:ltr;display:inline-block;font-size:24px;font-style:normal;font-weight:400;letter-spacing:normal;line-height:1;text-transform:none;white-space:nowrap;word-wrap:normal;content:var(--info-icon-content);color:var(--notification-info-icon-color);margin-right:12px}.snack-container.green{background-color:var(--notification-success-background-color)}.snack-container.red{background-color:var(--notification-error-background-color)}.snack-container.yellow{background-color:var(--notification-warn-background-color)}.snack-container.blue{background-color:var(--notification-info-background-color)}.snack-container .dismiss{margin-right:4px}.snack-container .dismiss.green{color:var(--notification-success-icon-color)}.snack-container .dismiss.red{color:var(--notification-error-icon-color)}.snack-container .dismiss.yellow{color:var(--notification-warn-icon-color)}.snack-container .dismiss.blue{color:var(--notification-info-icon-color)}.snack-container .dismiss mat-icon{font-size:20px;width:20px;height:20px}.snack-container .progress-container{width:100%;height:3px;position:absolute;left:0;bottom:0;border-bottom-left-radius:4px;border-bottom-right-radius:4px;overflow:hidden}.snack-container .progress-container .progress-bar{opacity:.6;width:100%;height:100%;transition:width .5s;transition-timing-function:linear}.snack-container .progress-container .progress-bar.green{background-color:var(--notification-success-icon-color)}.snack-container .progress-container .progress-bar.red{background-color:var(--notification-error-icon-color)}.snack-container .progress-container .progress-bar.yellow{background-color:var(--notification-warn-icon-color)}.snack-container .progress-container .progress-bar.blue{background-color:var(--notification-info-icon-color)}\n"] }]
591
614
  }], ctorParameters: () => [], propDecorators: { progressBar: [{ type: i0.ViewChild, args: ["progressBar", { isSignal: true }] }] } });
592
615
 
616
+ /**
617
+ * A utility class that renders HTML strings safely by automatically escaping dynamic values while preserving raw HTML
618
+ * in literal segments.
619
+ * @remarks Not intended for direct instantiation. Use tagged templates with the `safeHtml` tag function or the
620
+ * `SafeHtmlStringBuilder` class.
621
+ */
622
+ class SafeHtmlString {
623
+ /**
624
+ * Creates a `SafeHtmlString` instance from the specified HTML string so it will not be escaped but will be rendered
625
+ * directly as HTML.
626
+ * @remarks USE THIS WITH CAUTION, and only when absolutely necessary!
627
+ */
628
+ static raw(html) {
629
+ return new SafeHtmlString([html], []);
630
+ }
631
+ static join(items, rawSeparator = "") {
632
+ const builder = new SafeHtmlStringBuilder();
633
+ if (items.length > 0) {
634
+ builder.add(items[0]);
635
+ if (items.length > 1) {
636
+ const separator = SafeHtmlString.raw(rawSeparator);
637
+ for (let index = 1; index < items.length; index++) {
638
+ builder.add(separator);
639
+ builder.add(items[index]);
640
+ }
641
+ }
642
+ }
643
+ return builder.toSafeHtmlString();
644
+ }
645
+ constructor(literals, values) {
646
+ this.literals = literals;
647
+ this.values = values;
648
+ }
649
+ toSanitizedString(sanitizer) {
650
+ return sanitizer.sanitize(SecurityContext.HTML, this.toString());
651
+ }
652
+ toString() {
653
+ let s = "";
654
+ const { literals, values } = this;
655
+ let i = 0;
656
+ for (; i < literals.length - 1; i++) {
657
+ s += literals[i];
658
+ const value = values[i];
659
+ s += value instanceof SafeHtmlString ? value.toString() : escapeHtml(String(value));
660
+ }
661
+ s += literals[i];
662
+ return s;
663
+ }
664
+ }
665
+ /** A utility class for building a `SafeHtmlString` instance across multiple steps. */
666
+ class SafeHtmlStringBuilder {
667
+ constructor() {
668
+ this.literals = [""];
669
+ this.values = [];
670
+ }
671
+ add(html) {
672
+ this.literals[this.literals.length - 1] += html["literals"][0];
673
+ this.literals.push(...html["literals"].slice(1));
674
+ this.values.push(...html["values"]);
675
+ return this;
676
+ }
677
+ toSafeHtmlString() { return new SafeHtmlString(this.literals, this.values); }
678
+ }
679
+ /** A tag function for creating a `SafeHtmlString` instance in a foolproof way. */
680
+ function safeHtml(literals, ...values) {
681
+ return new SafeHtmlString(literals, values);
682
+ }
683
+ function escapeHtml(unsafe) {
684
+ return unsafe.replace(/[&<>"']/g, m => {
685
+ switch (m) {
686
+ case "&": return "&amp;";
687
+ case "<": return "&lt;";
688
+ case ">": return "&gt;";
689
+ case '"': return "&quot;";
690
+ case "'": return "&#039;";
691
+ default: throw Error("Unexpected match.");
692
+ }
693
+ });
694
+ }
695
+
593
696
  class MessagingService {
594
697
  constructor() {
595
698
  this.snackBar = inject(MatSnackBar);
596
699
  }
597
- showErrorSnack(message = "Something went wrong.", durationMs = 60000) {
700
+ /** @param message Either a plain `string` that will be fully HTML escaped or a `SafeHtmlString` whose dynamic values will be HTML escaped. */
701
+ showErrorSnack(message = "Something went wrong.", durationMs = 60000, prologue = "Ooops.", epilogue = "Please try again or contact us.") {
702
+ if (prologue || epilogue) {
703
+ if (typeof message === "string") {
704
+ message = `${prologue} ${message} ${epilogue}`;
705
+ }
706
+ else {
707
+ message = SafeHtmlString.join([safeHtml `${prologue}`, message, safeHtml `${epilogue}`], " ");
708
+ }
709
+ }
598
710
  this.snackBar.openFromComponent(NotificationSnackComponent, {
599
711
  duration: durationMs,
600
712
  data: {
601
713
  color: NotificationSnackColor.Red,
602
- message: `Ooops. ${message} Please try again or contact us.`,
714
+ message,
603
715
  },
604
716
  });
605
717
  }
718
+ /** @param message Either a plain `string` that will be fully HTML escaped or a `SafeHtmlString` whose dynamic values will be HTML escaped. */
606
719
  showSuccessSnack(message = "Success.", durationMs = 2500) {
607
720
  this.snackBar.openFromComponent(NotificationSnackComponent, {
608
721
  duration: durationMs,
609
722
  data: {
610
723
  color: NotificationSnackColor.Green,
611
- message: message,
724
+ message,
612
725
  },
613
726
  });
614
727
  }
615
- showWarningSnack(message = "Success.", durationMs = 5000) {
728
+ /** @param message Either a plain `string` that will be fully HTML escaped or a `SafeHtmlString` whose dynamic values will be HTML escaped. */
729
+ showWarningSnack(message, durationMs = 5000) {
616
730
  this.snackBar.openFromComponent(NotificationSnackComponent, {
617
731
  duration: durationMs,
618
732
  data: {
619
733
  color: NotificationSnackColor.Yellow,
620
- message: message,
734
+ message,
735
+ },
736
+ });
737
+ }
738
+ /** @param message Either a plain `string` that will be fully HTML escaped or a `SafeHtmlString` whose dynamic values will be HTML escaped. */
739
+ showInfoSnack(message, durationMs = 20000) {
740
+ this.snackBar.openFromComponent(NotificationSnackComponent, {
741
+ duration: durationMs,
742
+ data: {
743
+ color: NotificationSnackColor.Blue,
744
+ message,
621
745
  },
622
746
  });
623
747
  }
@@ -893,8 +1017,8 @@ function generatePercentageOptionFormGroup(model, settingType, predefinedVariati
893
1017
  percentage: new FormControl({ value: model.percentage, disabled: readOnly }, {
894
1018
  nonNullable: true,
895
1019
  validators: [
896
- validateNumberNan,
897
1020
  Validators.required,
1021
+ validateNumberNan,
898
1022
  Validators.min(0),
899
1023
  Validators.max(100),
900
1024
  Validators.pattern(percentageRegex),
@@ -1017,8 +1141,8 @@ function generateComparisonValueFormGroup(value, comparator, readOnly, featureFl
1017
1141
  doubleValue: new FormControl({ value: value.doubleValue ?? 0, disabled: readOnly }, {
1018
1142
  nonNullable: true,
1019
1143
  validators: [
1020
- validateNumberNan,
1021
1144
  Validators.required,
1145
+ validateNumberNan,
1022
1146
  Validators.min(-Number.MAX_VALUE),
1023
1147
  Validators.max(Number.MAX_VALUE),
1024
1148
  ],
@@ -1179,16 +1303,16 @@ function getValidatorsForSettingType(settingType, featureFlagLimitations, isJson
1179
1303
  switch (settingType) {
1180
1304
  case SettingType.Int:
1181
1305
  return [
1182
- validateNumberNan,
1183
1306
  Validators.required,
1307
+ validateNumberNan,
1184
1308
  Validators.min(-2147483648),
1185
1309
  Validators.max(2147483647),
1186
1310
  Validators.pattern(wholeNumberRegex),
1187
1311
  ];
1188
1312
  case SettingType.Double:
1189
1313
  return [
1190
- validateNumberNan,
1191
1314
  Validators.required,
1315
+ validateNumberNan,
1192
1316
  Validators.min(-Number.MAX_VALUE),
1193
1317
  Validators.max(Number.MAX_VALUE),
1194
1318
  ];
@@ -1378,6 +1502,7 @@ class SettingListFormHelper {
1378
1502
  nonNullable: true,
1379
1503
  validators: [
1380
1504
  Validators.required,
1505
+ validateNumberNan,
1381
1506
  Validators.min(0),
1382
1507
  Validators.max(100),
1383
1508
  Validators.pattern(percentageRegex),
@@ -1397,6 +1522,7 @@ class SettingListFormHelper {
1397
1522
  nonNullable: true,
1398
1523
  validators: [
1399
1524
  Validators.required,
1525
+ validateNumberNan,
1400
1526
  Validators.min(0),
1401
1527
  Validators.max(100),
1402
1528
  Validators.pattern(percentageRegex),
@@ -1437,6 +1563,7 @@ class SettingListFormHelper {
1437
1563
  case SettingType.Int:
1438
1564
  return [
1439
1565
  Validators.required,
1566
+ validateNumberNan,
1440
1567
  Validators.min(-2147483648),
1441
1568
  Validators.max(2147483647),
1442
1569
  Validators.pattern(wholeNumberRegex),
@@ -1672,7 +1799,7 @@ class BaseComponent {
1672
1799
  return this.publicApiService.createOrganizationService(this.basicAuthUsername(), this.basicAuthPassword(), this.basePath());
1673
1800
  }
1674
1801
  createChangeRequestsService() {
1675
- return this.publicApiService.createChangeRequestsApprovalFlowScheduledChangesBetaService(this.basicAuthUsername(), this.basicAuthPassword(), this.basePath());
1802
+ return this.publicApiService.createChangeRequestsApprovalFlowScheduledChangesService(this.basicAuthUsername(), this.basicAuthPassword(), this.basePath());
1676
1803
  }
1677
1804
  getDashboardBasePath() {
1678
1805
  return this.publicApiService.getDashboardBasePath();
@@ -2569,7 +2696,8 @@ class SettingListComponent {
2569
2696
  this.dialog.open(RerouteDialogComponent, {
2570
2697
  data: {
2571
2698
  title: "You have reached the limits of your plan",
2572
- body: ' Upgrade your plan or join our <a href="https://configcat.com/referrals" target="_blank" rel="noopener noreferrer">referral program</a> to increase your limits.',
2699
+ body: safeHtml `\
2700
+ Upgrade your plan or join our <a href="https://configcat.com/referrals" target="_blank" rel="noopener noreferrer">referral program</a> to increase your limits.`,
2573
2701
  yesButtonLabel: "See plans",
2574
2702
  yesButtonLink: this.dashboardBasePath() + "/organization/plans",
2575
2703
  noButtonLabel: "Close",
@@ -3373,17 +3501,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImpor
3373
3501
  ], template: "<h1 mat-dialog-title cdkDrag cdkDragRootElement=\".cdk-overlay-pane\" cdkDragHandle class=\"draggable\">\n Concurrent update detected\n</h1>\n<div mat-dialog-content>\n <p>Somebody updated the feature flags & settings while you were editing them.</p>\n <ul>\n <li>\n To discard your local changes and reload the updated values, click\n <strong>Discard my changes & reload</strong>\n </li>\n <li>\n To save you local values and overwrite the remote changes, click\n <strong>Save anyway</strong>\n </li>\n </ul>\n <p>\n For more info about the changes check the\n <a target=\"_blank\" rel=\"noopener noreferrer\" [href]=\"openHistory()\" [mat-dialog-close]>audit logs</a>\n in a new tab.\n </p>\n</div>\n<div mat-dialog-actions>\n <button type=\"button\" style=\"margin-right: 10px\" mat-flat-button color=\"primary\" (click)=\"discard()\">\n Discard local changes & reload\n </button>\n <button type=\"button\" mat-stroked-button (click)=\"save()\">Save anyway</button>\n</div>\n" }]
3374
3502
  }] });
3375
3503
 
3376
- class HtmlEscaper {
3377
- static escapeHtml(unsafe) {
3378
- return unsafe
3379
- .replace(/&/g, "&amp;")
3380
- .replace(/</g, "&lt;")
3381
- .replace(/>/g, "&gt;")
3382
- .replace(/"/g, "&quot;")
3383
- .replace(/'/g, "&#039;");
3384
- }
3385
- }
3386
-
3387
3504
  class BoxComponent {
3388
3505
  constructor() {
3389
3506
  this.type = input.required(/* @ts-ignore */
@@ -3561,6 +3678,7 @@ class CreateChangeRequestDialogComponent {
3561
3678
  reason: ["", this.data.reasonRequired ? [Validators.required, Validators.maxLength(1000)] : [Validators.maxLength(1000)]],
3562
3679
  applyAt: this.formBuilder.control(this.data.isSchedule ? this.tenMinutesFromNow : null, [this.mustBeInFuture(5)]),
3563
3680
  bypassApproval: [false],
3681
+ sendNotificationsToApprovers: [false],
3564
3682
  });
3565
3683
  this.submitLabel = this.data.isSchedule ? "Schedule changes" : "Propose changes";
3566
3684
  }
@@ -3573,12 +3691,13 @@ class CreateChangeRequestDialogComponent {
3573
3691
  return;
3574
3692
  }
3575
3693
  this.saving.set(true);
3576
- const { title, reason, applyAt, bypassApproval } = this.formGroup.getRawValue();
3577
- this.publicApiService.createChangeRequestsApprovalFlowScheduledChangesBetaService(this.data.basicAuthUsername, this.data.basicAuthPassword, this.data.basePath).createChangeRequest(this.data.configId, this.data.environmentId, {
3694
+ const { title, reason, applyAt, bypassApproval, sendNotificationsToApprovers } = this.formGroup.getRawValue();
3695
+ this.publicApiService.createChangeRequestsApprovalFlowScheduledChangesService(this.data.basicAuthUsername, this.data.basicAuthPassword, this.data.basePath).createChangeRequest(this.data.configId, this.data.environmentId, {
3578
3696
  title,
3579
3697
  reason,
3580
3698
  applyAt: applyAt ? new Date(applyAt * 1000).toUTCString() : null,
3581
3699
  bypassApproval: bypassApproval,
3700
+ sendNotificationsToApprovers: sendNotificationsToApprovers,
3582
3701
  proposedChanges: this.data.dirtySettingValues.map(sv => ({
3583
3702
  proposedChange: sv,
3584
3703
  })),
@@ -3669,9 +3788,10 @@ class CreateChangeRequestDialogComponent {
3669
3788
  width: "450px",
3670
3789
  data: {
3671
3790
  title: "Cannot create change request",
3672
- body: "Circular dependency detected between the following feature flags:<p>"
3673
- + HtmlEscaper.escapeHtml(errorMessage)
3674
- + "</p>Please <strong>eliminate the circular dependency</strong> before creating your change request.",
3791
+ body: safeHtml `\
3792
+ Circular dependency detected between the following feature flags:
3793
+ <p>${errorMessage}</p>
3794
+ Please <strong>eliminate the circular dependency</strong> before creating your change request.`,
3675
3795
  yesButtonLabel: "I understand",
3676
3796
  hideCancelButton: true,
3677
3797
  },
@@ -3696,7 +3816,7 @@ class CreateChangeRequestDialogComponent {
3696
3816
  };
3697
3817
  }
3698
3818
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: CreateChangeRequestDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3699
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: CreateChangeRequestDialogComponent, isStandalone: true, selector: "app-create-change-request-dialog", viewQueries: [{ propertyName: "reasonInput", first: true, predicate: ["reason"], descendants: true, isSignal: true }], ngImport: i0, template: "<div mat-dialog-title cdkDrag cdkDragRootElement=\".cdk-overlay-pane\" cdkDragHandle class=\"draggable title\">\n @if (data.isSchedule) {\n <div>Schedule change request</div>\n } @else {\n <div>Create change request</div>\n }\n</div>\n\n<div mat-dialog-content>\n <form class=\"form\" [formGroup]=\"formGroup\">\n <mat-form-field appearance=\"outline\" class=\"field-name\">\n <mat-label>Title</mat-label>\n <mat-hint>A short name your team will see on the ConfigCat Dashboard.</mat-hint>\n <input matInput formControlName=\"title\" placeholder=\"e.g. Enable dark mode for beta users\" maxlength=\"255\" />\n @if (formGroup.controls.title.invalid) {\n <mat-error>\n A short name your team will see on the ConfigCat Dashboard.\n {{ FormHelper.getErrorMessage(formGroup.controls.title) }}\n </mat-error>\n }\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\" class=\"field-reason\" subscriptSizing=\"dynamic\">\n <mat-label>\n Notes\n @if (!data.reasonRequired) {\n <span class=\"optional\">(optional)</span>\n }\n </mat-label>\n <mat-hint>\n This will appear in the Audit Log (in the Notes section when you expand the corresponding entry) upon applying\n the change request.\n </mat-hint>\n <textarea\n #reason\n matInput\n formControlName=\"reason\"\n rows=\"2\"\n maxlength=\"1000\"\n placeholder=\"Why is this change needed?\"></textarea>\n @if (formGroup.controls.reason.invalid) {\n <mat-error>\n Notes are mandatory for the target environment. {{ FormHelper.getErrorMessage(formGroup.controls.reason) }}\n </mat-error>\n }\n </mat-form-field>\n\n @if (data.isSchedule) {\n <div class=\"section\">\n <h3>When should the proposed changes be applied?</h3>\n <div class=\"scheduled\">\n <div class=\"date-selector\">\n <app-date-time-picker [formControl]=\"formGroup.controls.applyAt\" />\n @if (formGroup.controls.applyAt.invalid) {\n @let scheduleErrorMessage = FormHelper.getErrorMessage(formGroup.controls.applyAt);\n @if (scheduleErrorMessage) {\n <mat-error class=\"error\">\n {{ scheduleErrorMessage }}\n </mat-error>\n }\n }\n </div>\n @if (data.environmentApproveRequired && data.canBypassApproval) {\n <div>\n <mat-checkbox\n matInput\n color=\"primary\"\n class=\"bypass-approval-checkbox\"\n formControlName=\"bypassApproval\"\n matTooltip=\"Enabling this option will bypass the approval requirement of the target environment and \n lets the change request be applied on schedule even if it's not approved.\"\n matTooltipPosition=\"below\">\n Bypass approval requirement for scheduled changes\n </mat-checkbox>\n </div>\n }\n <app-box type=\"info\">\n The proposed changes will be applied at the scheduled time.\n <br />\n If conflicting changes occur before the scheduled time, they will prevent applying the scheduled changes.\n <br />\n You can always cancel the scheduled operation before it is executed and apply the changes manually.\n </app-box>\n </div>\n </div>\n }\n @if (formGroup.errors && formGroup.errors[\"serverSide\"]) {\n <mat-error>\n <span>{{ formGroup.errors[\"serverSide\"] }}</span>\n </mat-error>\n }\n </form>\n</div>\n\n<div mat-dialog-actions>\n <button class=\"submit-button\" mat-flat-button type=\"submit\" color=\"success\" [disabled]=\"saving()\" (click)=\"submit()\">\n @if (data.isSchedule) {\n <mat-icon class=\"icon-transform-rotate\">schedule</mat-icon>\n } @else {\n <mat-icon>merge_type</mat-icon>\n }\n <span>{{ submitLabel }}</span>\n </button>\n <button type=\"button\" mat-stroked-button [mat-dialog-close]>Cancel</button>\n</div>\n", styles: [".title{display:flex;justify-content:space-between;align-items:center;gap:16px}.title .sub-title{font-size:13px;font-weight:400;color:var(--panel-description-color)}.form{display:flex;flex-direction:column;gap:10px;margin-top:10px;margin-bottom:10px}.form .field-name,.form .field-reason,.form .field-apply-at{width:100%}.form .optional{font-size:12px;font-weight:400;color:var(--panel-description-color)}h3{font-size:14px;font-weight:500;color:var(--text-color)}.changes-title{display:flex;justify-content:space-between;align-items:center}.error{font-size:10px;color:var(--flag-validation-error)}.section{margin-top:.8rem}.section .scheduled{display:flex;flex-direction:column;flex-wrap:wrap;gap:12px}.section .scheduled .bypass-approval-checkbox{--mat-checkbox-label-text-size: 12px;--mat-checkbox-label-text-color: var(--panel-description-color);margin-left:-2px}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.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: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: MatDialogClose, selector: "[mat-dialog-close], [matDialogClose]", inputs: ["aria-label", "type", "mat-dialog-close", "matDialogClose"], exportAs: ["matDialogClose"] }, { kind: "directive", type: MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "directive", type: MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "component", type: 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: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: MatLabel, selector: "mat-label" }, { kind: "directive", type: 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: "directive", type: MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "component", type: 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: "component", type: BoxComponent, selector: "app-box", inputs: ["type", "title"] }, { kind: "directive", type: MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "directive", type: CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "component", type: DateTimePickerComponent, selector: "app-date-time-picker", inputs: ["dateTooltip", "timeTooltip"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3819
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: CreateChangeRequestDialogComponent, isStandalone: true, selector: "app-create-change-request-dialog", viewQueries: [{ propertyName: "reasonInput", first: true, predicate: ["reason"], descendants: true, isSignal: true }], ngImport: i0, template: "<div mat-dialog-title cdkDrag cdkDragRootElement=\".cdk-overlay-pane\" cdkDragHandle class=\"draggable title\">\n @if (data.isSchedule) {\n <div>Schedule change request</div>\n } @else {\n <div>Create change request</div>\n }\n</div>\n\n<div mat-dialog-content>\n <form class=\"form\" [formGroup]=\"formGroup\">\n <mat-form-field appearance=\"outline\" class=\"field-name\">\n <mat-label>Title</mat-label>\n <mat-hint>A short name your team will see on the ConfigCat Dashboard.</mat-hint>\n <input matInput formControlName=\"title\" placeholder=\"e.g. Enable dark mode for beta users\" maxlength=\"255\" />\n @if (formGroup.controls.title.invalid) {\n <mat-error>\n A short name your team will see on the ConfigCat Dashboard.\n {{ FormHelper.getErrorMessage(formGroup.controls.title) }}\n </mat-error>\n }\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\" class=\"field-reason\" subscriptSizing=\"dynamic\">\n <mat-label>\n Notes\n @if (!data.reasonRequired) {\n <span class=\"optional\">(optional)</span>\n }\n </mat-label>\n <mat-hint>\n This will appear in the Audit Log (in the Notes section when you expand the corresponding entry) upon applying\n the change request.\n </mat-hint>\n <textarea\n #reason\n matInput\n formControlName=\"reason\"\n rows=\"2\"\n maxlength=\"1000\"\n placeholder=\"Why is this change needed?\"></textarea>\n @if (formGroup.controls.reason.invalid) {\n <mat-error>\n Notes are mandatory for the target environment. {{ FormHelper.getErrorMessage(formGroup.controls.reason) }}\n </mat-error>\n }\n </mat-form-field>\n\n @if (data.isSchedule) {\n <div class=\"section\">\n <h3>When should the proposed changes be applied?</h3>\n <div class=\"scheduled\">\n <div class=\"date-selector\">\n <app-date-time-picker [formControl]=\"formGroup.controls.applyAt\" />\n @if (formGroup.controls.applyAt.invalid) {\n @let scheduleErrorMessage = FormHelper.getErrorMessage(formGroup.controls.applyAt);\n @if (scheduleErrorMessage) {\n <mat-error class=\"error\">\n {{ scheduleErrorMessage }}\n </mat-error>\n }\n }\n </div>\n @if (data.environmentApproveRequired && data.canBypassApproval) {\n <div>\n <mat-checkbox\n matInput\n color=\"primary\"\n class=\"checkbox\"\n formControlName=\"bypassApproval\"\n matTooltip=\"Enabling this option will bypass the approval requirement of the target environment and \n lets the change request be applied on schedule even if it's not approved.\"\n matTooltipPosition=\"below\">\n Bypass approval requirement for scheduled changes\n </mat-checkbox>\n </div>\n }\n <app-box type=\"info\">\n The proposed changes will be applied at the scheduled time.\n <br />\n If conflicting changes occur before the scheduled time, they will prevent applying the scheduled changes.\n <br />\n You can always cancel the scheduled operation before it is executed and apply the changes manually.\n </app-box>\n </div>\n </div>\n }\n\n <div class=\"section\">\n <h3>Notification</h3>\n <div class=\"notification-checkbox\">\n <mat-checkbox class=\"checkbox\" matInput color=\"primary\" formControlName=\"sendNotificationsToApprovers\">\n Send a notification email about this change request to everyone with approval permission\n </mat-checkbox>\n </div>\n </div>\n\n @if (formGroup.errors && formGroup.errors[\"serverSide\"]) {\n <mat-error>\n <span>{{ formGroup.errors[\"serverSide\"] }}</span>\n </mat-error>\n }\n </form>\n</div>\n\n<div mat-dialog-actions>\n <button class=\"submit-button\" mat-flat-button type=\"submit\" color=\"success\" [disabled]=\"saving()\" (click)=\"submit()\">\n @if (data.isSchedule) {\n <mat-icon class=\"icon-transform-rotate\">schedule</mat-icon>\n } @else {\n <mat-icon>merge_type</mat-icon>\n }\n <span>{{ submitLabel }}</span>\n </button>\n <button type=\"button\" mat-stroked-button [mat-dialog-close]>Cancel</button>\n</div>\n", styles: [".title{display:flex;justify-content:space-between;align-items:center;gap:16px}.title .sub-title{font-size:13px;font-weight:400;color:var(--panel-description-color)}.form{display:flex;flex-direction:column;gap:10px;margin-top:10px;margin-bottom:10px}.form .field-name,.form .field-reason,.form .field-apply-at{width:100%}.form .optional{font-size:12px;font-weight:400;color:var(--panel-description-color)}h3{font-size:14px;font-weight:500;color:var(--text-color)}.changes-title{display:flex;justify-content:space-between;align-items:center}.error{font-size:10px;color:var(--flag-validation-error)}.section{margin-top:.8rem}.section .scheduled{display:flex;flex-direction:column;flex-wrap:wrap;gap:12px}.section .scheduled .checkbox{--mat-checkbox-label-text-size: 12px;--mat-checkbox-label-text-color: var(--panel-description-color);margin-left:-2px}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.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: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: MatDialogClose, selector: "[mat-dialog-close], [matDialogClose]", inputs: ["aria-label", "type", "mat-dialog-close", "matDialogClose"], exportAs: ["matDialogClose"] }, { kind: "directive", type: MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "directive", type: MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "component", type: 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: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: MatLabel, selector: "mat-label" }, { kind: "directive", type: 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: "directive", type: MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "component", type: 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: "component", type: BoxComponent, selector: "app-box", inputs: ["type", "title"] }, { kind: "directive", type: MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "directive", type: CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "component", type: DateTimePickerComponent, selector: "app-date-time-picker", inputs: ["dateTooltip", "timeTooltip"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3700
3820
  }
3701
3821
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: CreateChangeRequestDialogComponent, decorators: [{
3702
3822
  type: Component,
@@ -3720,7 +3840,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImpor
3720
3840
  CdkDrag,
3721
3841
  CdkDragHandle,
3722
3842
  DateTimePickerComponent,
3723
- ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div mat-dialog-title cdkDrag cdkDragRootElement=\".cdk-overlay-pane\" cdkDragHandle class=\"draggable title\">\n @if (data.isSchedule) {\n <div>Schedule change request</div>\n } @else {\n <div>Create change request</div>\n }\n</div>\n\n<div mat-dialog-content>\n <form class=\"form\" [formGroup]=\"formGroup\">\n <mat-form-field appearance=\"outline\" class=\"field-name\">\n <mat-label>Title</mat-label>\n <mat-hint>A short name your team will see on the ConfigCat Dashboard.</mat-hint>\n <input matInput formControlName=\"title\" placeholder=\"e.g. Enable dark mode for beta users\" maxlength=\"255\" />\n @if (formGroup.controls.title.invalid) {\n <mat-error>\n A short name your team will see on the ConfigCat Dashboard.\n {{ FormHelper.getErrorMessage(formGroup.controls.title) }}\n </mat-error>\n }\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\" class=\"field-reason\" subscriptSizing=\"dynamic\">\n <mat-label>\n Notes\n @if (!data.reasonRequired) {\n <span class=\"optional\">(optional)</span>\n }\n </mat-label>\n <mat-hint>\n This will appear in the Audit Log (in the Notes section when you expand the corresponding entry) upon applying\n the change request.\n </mat-hint>\n <textarea\n #reason\n matInput\n formControlName=\"reason\"\n rows=\"2\"\n maxlength=\"1000\"\n placeholder=\"Why is this change needed?\"></textarea>\n @if (formGroup.controls.reason.invalid) {\n <mat-error>\n Notes are mandatory for the target environment. {{ FormHelper.getErrorMessage(formGroup.controls.reason) }}\n </mat-error>\n }\n </mat-form-field>\n\n @if (data.isSchedule) {\n <div class=\"section\">\n <h3>When should the proposed changes be applied?</h3>\n <div class=\"scheduled\">\n <div class=\"date-selector\">\n <app-date-time-picker [formControl]=\"formGroup.controls.applyAt\" />\n @if (formGroup.controls.applyAt.invalid) {\n @let scheduleErrorMessage = FormHelper.getErrorMessage(formGroup.controls.applyAt);\n @if (scheduleErrorMessage) {\n <mat-error class=\"error\">\n {{ scheduleErrorMessage }}\n </mat-error>\n }\n }\n </div>\n @if (data.environmentApproveRequired && data.canBypassApproval) {\n <div>\n <mat-checkbox\n matInput\n color=\"primary\"\n class=\"bypass-approval-checkbox\"\n formControlName=\"bypassApproval\"\n matTooltip=\"Enabling this option will bypass the approval requirement of the target environment and \n lets the change request be applied on schedule even if it's not approved.\"\n matTooltipPosition=\"below\">\n Bypass approval requirement for scheduled changes\n </mat-checkbox>\n </div>\n }\n <app-box type=\"info\">\n The proposed changes will be applied at the scheduled time.\n <br />\n If conflicting changes occur before the scheduled time, they will prevent applying the scheduled changes.\n <br />\n You can always cancel the scheduled operation before it is executed and apply the changes manually.\n </app-box>\n </div>\n </div>\n }\n @if (formGroup.errors && formGroup.errors[\"serverSide\"]) {\n <mat-error>\n <span>{{ formGroup.errors[\"serverSide\"] }}</span>\n </mat-error>\n }\n </form>\n</div>\n\n<div mat-dialog-actions>\n <button class=\"submit-button\" mat-flat-button type=\"submit\" color=\"success\" [disabled]=\"saving()\" (click)=\"submit()\">\n @if (data.isSchedule) {\n <mat-icon class=\"icon-transform-rotate\">schedule</mat-icon>\n } @else {\n <mat-icon>merge_type</mat-icon>\n }\n <span>{{ submitLabel }}</span>\n </button>\n <button type=\"button\" mat-stroked-button [mat-dialog-close]>Cancel</button>\n</div>\n", styles: [".title{display:flex;justify-content:space-between;align-items:center;gap:16px}.title .sub-title{font-size:13px;font-weight:400;color:var(--panel-description-color)}.form{display:flex;flex-direction:column;gap:10px;margin-top:10px;margin-bottom:10px}.form .field-name,.form .field-reason,.form .field-apply-at{width:100%}.form .optional{font-size:12px;font-weight:400;color:var(--panel-description-color)}h3{font-size:14px;font-weight:500;color:var(--text-color)}.changes-title{display:flex;justify-content:space-between;align-items:center}.error{font-size:10px;color:var(--flag-validation-error)}.section{margin-top:.8rem}.section .scheduled{display:flex;flex-direction:column;flex-wrap:wrap;gap:12px}.section .scheduled .bypass-approval-checkbox{--mat-checkbox-label-text-size: 12px;--mat-checkbox-label-text-color: var(--panel-description-color);margin-left:-2px}\n"] }]
3843
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div mat-dialog-title cdkDrag cdkDragRootElement=\".cdk-overlay-pane\" cdkDragHandle class=\"draggable title\">\n @if (data.isSchedule) {\n <div>Schedule change request</div>\n } @else {\n <div>Create change request</div>\n }\n</div>\n\n<div mat-dialog-content>\n <form class=\"form\" [formGroup]=\"formGroup\">\n <mat-form-field appearance=\"outline\" class=\"field-name\">\n <mat-label>Title</mat-label>\n <mat-hint>A short name your team will see on the ConfigCat Dashboard.</mat-hint>\n <input matInput formControlName=\"title\" placeholder=\"e.g. Enable dark mode for beta users\" maxlength=\"255\" />\n @if (formGroup.controls.title.invalid) {\n <mat-error>\n A short name your team will see on the ConfigCat Dashboard.\n {{ FormHelper.getErrorMessage(formGroup.controls.title) }}\n </mat-error>\n }\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\" class=\"field-reason\" subscriptSizing=\"dynamic\">\n <mat-label>\n Notes\n @if (!data.reasonRequired) {\n <span class=\"optional\">(optional)</span>\n }\n </mat-label>\n <mat-hint>\n This will appear in the Audit Log (in the Notes section when you expand the corresponding entry) upon applying\n the change request.\n </mat-hint>\n <textarea\n #reason\n matInput\n formControlName=\"reason\"\n rows=\"2\"\n maxlength=\"1000\"\n placeholder=\"Why is this change needed?\"></textarea>\n @if (formGroup.controls.reason.invalid) {\n <mat-error>\n Notes are mandatory for the target environment. {{ FormHelper.getErrorMessage(formGroup.controls.reason) }}\n </mat-error>\n }\n </mat-form-field>\n\n @if (data.isSchedule) {\n <div class=\"section\">\n <h3>When should the proposed changes be applied?</h3>\n <div class=\"scheduled\">\n <div class=\"date-selector\">\n <app-date-time-picker [formControl]=\"formGroup.controls.applyAt\" />\n @if (formGroup.controls.applyAt.invalid) {\n @let scheduleErrorMessage = FormHelper.getErrorMessage(formGroup.controls.applyAt);\n @if (scheduleErrorMessage) {\n <mat-error class=\"error\">\n {{ scheduleErrorMessage }}\n </mat-error>\n }\n }\n </div>\n @if (data.environmentApproveRequired && data.canBypassApproval) {\n <div>\n <mat-checkbox\n matInput\n color=\"primary\"\n class=\"checkbox\"\n formControlName=\"bypassApproval\"\n matTooltip=\"Enabling this option will bypass the approval requirement of the target environment and \n lets the change request be applied on schedule even if it's not approved.\"\n matTooltipPosition=\"below\">\n Bypass approval requirement for scheduled changes\n </mat-checkbox>\n </div>\n }\n <app-box type=\"info\">\n The proposed changes will be applied at the scheduled time.\n <br />\n If conflicting changes occur before the scheduled time, they will prevent applying the scheduled changes.\n <br />\n You can always cancel the scheduled operation before it is executed and apply the changes manually.\n </app-box>\n </div>\n </div>\n }\n\n <div class=\"section\">\n <h3>Notification</h3>\n <div class=\"notification-checkbox\">\n <mat-checkbox class=\"checkbox\" matInput color=\"primary\" formControlName=\"sendNotificationsToApprovers\">\n Send a notification email about this change request to everyone with approval permission\n </mat-checkbox>\n </div>\n </div>\n\n @if (formGroup.errors && formGroup.errors[\"serverSide\"]) {\n <mat-error>\n <span>{{ formGroup.errors[\"serverSide\"] }}</span>\n </mat-error>\n }\n </form>\n</div>\n\n<div mat-dialog-actions>\n <button class=\"submit-button\" mat-flat-button type=\"submit\" color=\"success\" [disabled]=\"saving()\" (click)=\"submit()\">\n @if (data.isSchedule) {\n <mat-icon class=\"icon-transform-rotate\">schedule</mat-icon>\n } @else {\n <mat-icon>merge_type</mat-icon>\n }\n <span>{{ submitLabel }}</span>\n </button>\n <button type=\"button\" mat-stroked-button [mat-dialog-close]>Cancel</button>\n</div>\n", styles: [".title{display:flex;justify-content:space-between;align-items:center;gap:16px}.title .sub-title{font-size:13px;font-weight:400;color:var(--panel-description-color)}.form{display:flex;flex-direction:column;gap:10px;margin-top:10px;margin-bottom:10px}.form .field-name,.form .field-reason,.form .field-apply-at{width:100%}.form .optional{font-size:12px;font-weight:400;color:var(--panel-description-color)}h3{font-size:14px;font-weight:500;color:var(--text-color)}.changes-title{display:flex;justify-content:space-between;align-items:center}.error{font-size:10px;color:var(--flag-validation-error)}.section{margin-top:.8rem}.section .scheduled{display:flex;flex-direction:column;flex-wrap:wrap;gap:12px}.section .scheduled .checkbox{--mat-checkbox-label-text-size: 12px;--mat-checkbox-label-text-color: var(--panel-description-color);margin-left:-2px}\n"] }]
3724
3844
  }], propDecorators: { reasonInput: [{ type: i0.ViewChild, args: ["reason", { isSignal: true }] }] } });
3725
3845
 
3726
3846
  class CopyableValueComponent {
@@ -3837,13 +3957,13 @@ class SaveAndPublishDialogComponent {
3837
3957
  this.dialogRef.close({ button: "cancel", reason: null });
3838
3958
  }
3839
3959
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: SaveAndPublishDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3840
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: SaveAndPublishDialogComponent, isStandalone: true, selector: "app-save-and-publish-dialog", viewQueries: [{ propertyName: "reasonInput", first: true, predicate: ["reason"], descendants: true, isSignal: true }], ngImport: i0, template: "<form [formGroup]=\"formGroup\" (ngSubmit)=\"handleSaveButton()\">\n <h1 mat-dialog-title cdkDrag cdkDragRootElement=\".cdk-overlay-pane\" cdkDragHandle class=\"draggable\">\n @if (data.bypassApproval) {\n Save & publish without approval?\n } @else {\n Save & publish changes?\n }\n </h1>\n <div mat-dialog-content class=\"container\">\n <div>\n You have changed\n <strong>1 feature flag</strong>\n in the\n <strong>{{ data.environmentName }}</strong>\n environment.&nbsp;\n </div>\n\n <p>The changes will be published to your applications after you click \"Save & publish changes\".</p>\n <h3 class=\"notes\">\n @if (!data.reasonRequired) {\n Optional\n } @else {\n Add\n }\n notes\n </h3>\n <mat-form-field appearance=\"outline\" class=\"reason\">\n <textarea\n #reason\n matInput\n formControlName=\"reason\"\n name=\"reason\"\n placeholder=\"I'm changing the values because...\"\n rows=\"3\"\n cdkTextareaAutosize\n cdkAutosizeMinRows=\"3\"></textarea>\n <mat-hint>\n This will appear in the Audit Log (in the Notes section when you expand the corresponding entry).\n </mat-hint>\n @if (formGroup.controls.reason.invalid) {\n <mat-error>\n This will appear in the Audit Log (in the Notes section when you expand the corresponding entry). The field is\n required.\n </mat-error>\n }\n </mat-form-field>\n </div>\n <div mat-dialog-actions>\n <button type=\"submit\" style=\"margin-right: 10px\" mat-flat-button color=\"primary\">Save & publish changes</button>\n <button type=\"button\" mat-stroked-button (click)=\"cancel()\">Cancel</button>\n </div>\n</form>\n", styles: [".reason{width:100%}.diff-type-selector,.diff-wrapper{margin-top:1rem}.notes{margin-top:22px}\n"], dependencies: [{ kind: "directive", type: MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.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: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: RxReactiveFormsModule }, { kind: "directive", type: i2.AsyncValidationDirective, selector: "[ngModel],[formControlName],[formControl]", inputs: ["async"] }, { kind: "directive", type: i2.RxwebFormDirective, selector: "[formGroup],[rxwebForm]", inputs: ["formGroup", "rxwebForm"] }, { kind: "directive", type: i2.RxFormControlDirective, selector: "[ngModel],[formControlName],[formControl]", inputs: ["rxalpha", "rxalphaNumeric", "rxascii", "rxcompare", "rxcompose", "rxcontains", "rxcreditCard", "rxdataUri", "rxdifferent", "rxdigit", "rxemail", "rxendsWith", "rxeven", "rxextension", "rxfactor", "rxfileSize", "rxgreaterThanEqualTo", "rxgreaterThan", "rxhexColor", "rxjson", "rxlatitude", "rxlatLong", "rxleapYear", "rxlessThan", "rxlessThanEqualTo", "rxlongitude", "rxlowerCase", "rxmac", "rxmaxDate", "rxmaxLength", "rxmaxNumber", "rxminDate", "rxminLength", "rxminNumber", "rxnumeric", "rxodd", "rxpassword", "rxport", "rxprimeNumber", "rxrequired", "rxrange", "rxrule", "rxstartsWith", "rxtime", "rxupperCase", "rxurl", "rxunique", "rxnotEmpty", "rxcusip", "rxgrid", "rxdate"] }, { kind: "directive", type: MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "component", type: MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: 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: "directive", type: CdkTextareaAutosize, selector: "textarea[cdkTextareaAutosize]", inputs: ["cdkAutosizeMinRows", "cdkAutosizeMaxRows", "cdkTextareaAutosize", "placeholder"], exportAs: ["cdkTextareaAutosize"] }, { kind: "directive", type: MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "component", type: 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: "directive", type: CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "directive", type: MatHint, selector: "mat-hint", inputs: ["align", "id"] }] }); }
3960
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: SaveAndPublishDialogComponent, isStandalone: true, selector: "app-save-and-publish-dialog", viewQueries: [{ propertyName: "reasonInput", first: true, predicate: ["reason"], descendants: true, isSignal: true }], ngImport: i0, template: "<form [formGroup]=\"formGroup\" (ngSubmit)=\"handleSaveButton()\">\n <h1 mat-dialog-title cdkDrag cdkDragRootElement=\".cdk-overlay-pane\" cdkDragHandle class=\"draggable\">\n @if (data.bypassApproval) {\n Save & publish without approval?\n } @else {\n Save & publish changes?\n }\n </h1>\n <div mat-dialog-content class=\"container\">\n <div>\n You have changed\n <strong>1 feature flag</strong>\n in the\n <strong>{{ data.environmentName }}</strong>\n environment.&nbsp;\n </div>\n\n <p>If you proceed, the changes will be published and propagated to your applications.</p>\n <h3 class=\"notes\">\n @if (!data.reasonRequired) {\n Optional\n } @else {\n Add\n }\n notes\n </h3>\n <mat-form-field appearance=\"outline\" class=\"reason\">\n <textarea\n #reason\n matInput\n formControlName=\"reason\"\n name=\"reason\"\n placeholder=\"I'm changing the values because...\"\n rows=\"3\"\n cdkTextareaAutosize\n cdkAutosizeMinRows=\"3\"></textarea>\n <mat-hint>\n This will appear in the Audit Log (in the Notes section when you expand the corresponding entry).\n </mat-hint>\n @if (formGroup.controls.reason.invalid) {\n <mat-error>\n This will appear in the Audit Log (in the Notes section when you expand the corresponding entry). The field is\n required.\n </mat-error>\n }\n </mat-form-field>\n </div>\n <div mat-dialog-actions>\n <button type=\"submit\" style=\"margin-right: 10px\" mat-flat-button color=\"primary\">Save & publish changes</button>\n <button type=\"button\" mat-stroked-button (click)=\"cancel()\">Cancel</button>\n </div>\n</form>\n", styles: [".reason{width:100%}.diff-type-selector,.diff-wrapper{margin-top:1rem}.notes{margin-top:22px}\n"], dependencies: [{ kind: "directive", type: MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.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: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: RxReactiveFormsModule }, { kind: "directive", type: i2.AsyncValidationDirective, selector: "[ngModel],[formControlName],[formControl]", inputs: ["async"] }, { kind: "directive", type: i2.RxwebFormDirective, selector: "[formGroup],[rxwebForm]", inputs: ["formGroup", "rxwebForm"] }, { kind: "directive", type: i2.RxFormControlDirective, selector: "[ngModel],[formControlName],[formControl]", inputs: ["rxalpha", "rxalphaNumeric", "rxascii", "rxcompare", "rxcompose", "rxcontains", "rxcreditCard", "rxdataUri", "rxdifferent", "rxdigit", "rxemail", "rxendsWith", "rxeven", "rxextension", "rxfactor", "rxfileSize", "rxgreaterThanEqualTo", "rxgreaterThan", "rxhexColor", "rxjson", "rxlatitude", "rxlatLong", "rxleapYear", "rxlessThan", "rxlessThanEqualTo", "rxlongitude", "rxlowerCase", "rxmac", "rxmaxDate", "rxmaxLength", "rxmaxNumber", "rxminDate", "rxminLength", "rxminNumber", "rxnumeric", "rxodd", "rxpassword", "rxport", "rxprimeNumber", "rxrequired", "rxrange", "rxrule", "rxstartsWith", "rxtime", "rxupperCase", "rxurl", "rxunique", "rxnotEmpty", "rxcusip", "rxgrid", "rxdate"] }, { kind: "directive", type: MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "component", type: MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: 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: "directive", type: CdkTextareaAutosize, selector: "textarea[cdkTextareaAutosize]", inputs: ["cdkAutosizeMinRows", "cdkAutosizeMaxRows", "cdkTextareaAutosize", "placeholder"], exportAs: ["cdkTextareaAutosize"] }, { kind: "directive", type: MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "component", type: 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: "directive", type: CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "directive", type: MatHint, selector: "mat-hint", inputs: ["align", "id"] }] }); }
3841
3961
  }
3842
3962
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: SaveAndPublishDialogComponent, decorators: [{
3843
3963
  type: Component,
3844
3964
  args: [{ selector: "app-save-and-publish-dialog", imports: [MatDialogTitle, FormsModule, ReactiveFormsModule, RxReactiveFormsModule, CdkScrollable, MatDialogContent,
3845
3965
  MatFormField, MatInput, CdkTextareaAutosize, MatError, MatDialogActions, MatButton, CdkDrag, CdkDragHandle,
3846
- MatHint], template: "<form [formGroup]=\"formGroup\" (ngSubmit)=\"handleSaveButton()\">\n <h1 mat-dialog-title cdkDrag cdkDragRootElement=\".cdk-overlay-pane\" cdkDragHandle class=\"draggable\">\n @if (data.bypassApproval) {\n Save & publish without approval?\n } @else {\n Save & publish changes?\n }\n </h1>\n <div mat-dialog-content class=\"container\">\n <div>\n You have changed\n <strong>1 feature flag</strong>\n in the\n <strong>{{ data.environmentName }}</strong>\n environment.&nbsp;\n </div>\n\n <p>The changes will be published to your applications after you click \"Save & publish changes\".</p>\n <h3 class=\"notes\">\n @if (!data.reasonRequired) {\n Optional\n } @else {\n Add\n }\n notes\n </h3>\n <mat-form-field appearance=\"outline\" class=\"reason\">\n <textarea\n #reason\n matInput\n formControlName=\"reason\"\n name=\"reason\"\n placeholder=\"I'm changing the values because...\"\n rows=\"3\"\n cdkTextareaAutosize\n cdkAutosizeMinRows=\"3\"></textarea>\n <mat-hint>\n This will appear in the Audit Log (in the Notes section when you expand the corresponding entry).\n </mat-hint>\n @if (formGroup.controls.reason.invalid) {\n <mat-error>\n This will appear in the Audit Log (in the Notes section when you expand the corresponding entry). The field is\n required.\n </mat-error>\n }\n </mat-form-field>\n </div>\n <div mat-dialog-actions>\n <button type=\"submit\" style=\"margin-right: 10px\" mat-flat-button color=\"primary\">Save & publish changes</button>\n <button type=\"button\" mat-stroked-button (click)=\"cancel()\">Cancel</button>\n </div>\n</form>\n", styles: [".reason{width:100%}.diff-type-selector,.diff-wrapper{margin-top:1rem}.notes{margin-top:22px}\n"] }]
3966
+ MatHint], template: "<form [formGroup]=\"formGroup\" (ngSubmit)=\"handleSaveButton()\">\n <h1 mat-dialog-title cdkDrag cdkDragRootElement=\".cdk-overlay-pane\" cdkDragHandle class=\"draggable\">\n @if (data.bypassApproval) {\n Save & publish without approval?\n } @else {\n Save & publish changes?\n }\n </h1>\n <div mat-dialog-content class=\"container\">\n <div>\n You have changed\n <strong>1 feature flag</strong>\n in the\n <strong>{{ data.environmentName }}</strong>\n environment.&nbsp;\n </div>\n\n <p>If you proceed, the changes will be published and propagated to your applications.</p>\n <h3 class=\"notes\">\n @if (!data.reasonRequired) {\n Optional\n } @else {\n Add\n }\n notes\n </h3>\n <mat-form-field appearance=\"outline\" class=\"reason\">\n <textarea\n #reason\n matInput\n formControlName=\"reason\"\n name=\"reason\"\n placeholder=\"I'm changing the values because...\"\n rows=\"3\"\n cdkTextareaAutosize\n cdkAutosizeMinRows=\"3\"></textarea>\n <mat-hint>\n This will appear in the Audit Log (in the Notes section when you expand the corresponding entry).\n </mat-hint>\n @if (formGroup.controls.reason.invalid) {\n <mat-error>\n This will appear in the Audit Log (in the Notes section when you expand the corresponding entry). The field is\n required.\n </mat-error>\n }\n </mat-form-field>\n </div>\n <div mat-dialog-actions>\n <button type=\"submit\" style=\"margin-right: 10px\" mat-flat-button color=\"primary\">Save & publish changes</button>\n <button type=\"button\" mat-stroked-button (click)=\"cancel()\">Cancel</button>\n </div>\n</form>\n", styles: [".reason{width:100%}.diff-type-selector,.diff-wrapper{margin-top:1rem}.notes{margin-top:22px}\n"] }]
3847
3967
  }], propDecorators: { reasonInput: [{ type: i0.ViewChild, args: ["reason", { isSignal: true }] }] } });
3848
3968
 
3849
3969
  function getRawPredefinedVariationValue(formGroup) {
@@ -4017,7 +4137,8 @@ class PastedDataTrimmerDirective {
4017
4137
  const dialogRef = this.dialog.open(ConfirmationDialogComponent, {
4018
4138
  data: {
4019
4139
  title: "Trim extra spaces?",
4020
- body: "The pasted value contains extra spaces at the beginning or end.<br/>This may cause unexpected behavior, for example when comparing values.",
4140
+ body: safeHtml `\
4141
+ The pasted value contains extra spaces at the beginning or end.<br/>This may cause unexpected behavior, for example when comparing values.`,
4021
4142
  yesButtonLabel: "Trim extra spaces",
4022
4143
  cancelButtonLabel: "Keep spaces",
4023
4144
  },
@@ -5671,9 +5792,10 @@ class UserConditionComponent {
5671
5792
  width: "600px",
5672
5793
  data: {
5673
5794
  title: "Consider using confidential comparators",
5674
- body: `Comparison value will be presented as plain text
5675
- in the <i>config JSON</i>. If you have sensitive data, consider using one of the confidential (hashed) comparators.<br/><br/>
5676
- See the <a href="https://configcat.com/docs/targeting/targeting-rule/user-condition/#confidential-text-comparators" target="_blank">documentation</a> for more information.`,
5795
+ body: safeHtml `\
5796
+ Comparison value will be presented as plain text in the <i>config JSON</i>.
5797
+ If you have sensitive data, consider using one of the confidential (hashed) comparators.<br/><br/>
5798
+ See the <a href="https://configcat.com/docs/targeting/targeting-rule/user-condition/#confidential-text-comparators" target="_blank">documentation</a> for more information.`,
5677
5799
  secondaryButtonLabel: "Open comparator selector",
5678
5800
  secondaryButtonAction: () => {
5679
5801
  this.compDialog();
@@ -5694,7 +5816,9 @@ class UserConditionComponent {
5694
5816
  const dialogRef = this.dialog.open(ConfirmationDialogComponent, {
5695
5817
  data: {
5696
5818
  title: "How would you like to add the items?",
5697
- body: "We've noticed a comma in the value you want to add. <br/> Do you want to add it as a single item, or would you prefer to insert multiple items separated by commas?",
5819
+ body: safeHtml `\
5820
+ We've noticed a comma in the value you want to add. <br/>
5821
+ Do you want to add it as a single item, or would you prefer to insert multiple items separated by commas?`,
5698
5822
  yesButtonLabel: "Add multiple items",
5699
5823
  cancelButtonLabel: "Add as a single item",
5700
5824
  },
@@ -5914,7 +6038,7 @@ class UserConditionComponent {
5914
6038
  return !compVal.doubleValue && !compVal.listValue && !compVal.stringValue;
5915
6039
  }
5916
6040
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: UserConditionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
5917
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: UserConditionComponent, isStandalone: true, selector: "app-user-condition", inputs: { targetingRuleIndex: { classPropertyName: "targetingRuleIndex", publicName: "targetingRuleIndex", isSignal: true, isRequired: true, transformFunction: null }, conditionIndex: { classPropertyName: "conditionIndex", publicName: "conditionIndex", isSignal: true, isRequired: true, transformFunction: null }, readOnly: { classPropertyName: "readOnly", publicName: "readOnly", isSignal: true, isRequired: true, transformFunction: null }, featureFlagLimitations: { classPropertyName: "featureFlagLimitations", publicName: "featureFlagLimitations", isSignal: true, isRequired: true, transformFunction: null }, formGroup: { classPropertyName: "formGroup", publicName: "formGroup", isSignal: true, isRequired: true, transformFunction: null }, comparisonAttributes: { classPropertyName: "comparisonAttributes", publicName: "comparisonAttributes", isSignal: true, isRequired: true, transformFunction: null } }, providers: [{ provide: DateAdapter, useClass: AppDateAdapter }], viewQueries: [{ propertyName: "matChipGrid", first: true, predicate: ["chipGrid"], descendants: true, isSignal: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"targeting-rule-line\">\n <div class=\"explanation-wrapper\">\n <app-condition-chip [targetingRuleIndex]=\"targetingRuleIndex()\" [conditionIndex]=\"conditionIndex()\" />\n\n <div class=\"explanation\">\n <a\n href=\"https://configcat.com/docs/targeting/targeting-rule/user-condition/\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"user-link\">\n user's\n </a>\n </div>\n </div>\n\n <div class=\"wrapper-group\">\n <div class=\"wrapper-group\">\n <div\n class=\"targeting-rule-component pad-right\"\n [ngClass]=\"{\n small: comparisionAttributeCustom === selectedAttribute,\n }\">\n <mat-form-field appearance=\"outline\" class=\"small-form-field select\" subscriptSizing=\"dynamic\">\n <mat-select\n matTooltip=\"Comparison attribute\"\n [disabled]=\"formGroup().controls.comparisonAttribute.disabled\"\n [(value)]=\"selectedAttribute\"\n (selectionChange)=\"attributeChanged(selectedAttribute)\">\n @for (comparisonAttribute of defaultComparisonAttributes; track $index) {\n <mat-option [value]=\"comparisonAttribute\">{{ comparisonAttribute }}</mat-option>\n }\n <mat-option value=\"Custom\">Custom</mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n <div class=\"targeting-rule-component pad-right small\" [hidden]=\"comparisionAttributeCustom !== selectedAttribute\">\n <mat-form-field appearance=\"outline\" class=\"small-form-field\" subscriptSizing=\"dynamic\">\n <input\n matInput\n type=\"text\"\n matTooltip=\"Custom comparison attribute\"\n [matAutocomplete]=\"auto\"\n [formControl]=\"formGroup().controls.comparisonAttribute\"\n (blur)=\"calcComparators(true)\" />\n @if (formGroup().controls.comparisonAttribute.invalid) {\n <mat-error>\n {{ FormHelper.getErrorMessage(formGroup().controls.comparisonAttribute) }}\n </mat-error>\n }\n </mat-form-field>\n <mat-autocomplete #auto=\"matAutocomplete\" panelWidth=\"auto\">\n @for (option of comparisonAttributes(); track $index) {\n <mat-option [value]=\"option\">\n {{ option }}\n </mat-option>\n }\n </mat-autocomplete>\n </div>\n </div>\n\n <div class=\"targeting-rule-component pad-right suffixed\">\n <mat-form-field appearance=\"outline\" class=\"small-form-field select-suffixed\" subscriptSizing=\"dynamic\">\n <mat-select\n matTooltip=\"Comparator\"\n [disabled]=\"readOnly()\"\n [(value)]=\"selectedComparator\"\n (selectionChange)=\"comparatorChanged(selectedComparator)\">\n @for (group of comparatorSuggestions | keyvalue: keepOrder; track group) {\n <mat-optgroup [label]=\"group.key\">\n @for (comparator of group.value; track comparator) {\n <mat-option [value]=\"comparator.value\">\n {{ comparator.label }}\n </mat-option>\n }\n </mat-optgroup>\n }\n <mat-divider />\n <mat-option value=\"showall\">\n <mat-icon>list_alt</mat-icon>\n SHOW ALL COMPARATORS\n </mat-option>\n </mat-select>\n <button\n matSuffix\n class=\"input-suffix-button\"\n type=\"button\"\n mat-icon-button\n matTooltip=\"All comparators\"\n (click)=\"compDialog(); $event.stopPropagation()\">\n <mat-icon>list_alt</mat-icon>\n </button>\n @if (formGroup().controls.comparator.invalid) {\n <mat-error>\n {{ FormHelper.getErrorMessage(formGroup().controls.comparator) }}\n </mat-error>\n }\n </mat-form-field>\n </div>\n </div>\n @switch (rolloutRuleComparatorType) {\n @case (RolloutRuleComparatorV2Type.String) {\n <ng-container [ngTemplateOutlet]=\"textTemplate\" />\n }\n @case (RolloutRuleComparatorV2Type.Semver) {\n <ng-container [ngTemplateOutlet]=\"semverTemplate\" />\n }\n @case (RolloutRuleComparatorV2Type.StringList) {\n <ng-container [ngTemplateOutlet]=\"listTemplate\" />\n }\n @case (RolloutRuleComparatorV2Type.SemverList) {\n <ng-container [ngTemplateOutlet]=\"listTemplate\" />\n }\n @case (RolloutRuleComparatorV2Type.Number) {\n <ng-container [ngTemplateOutlet]=\"numberTemplate\" />\n }\n @case (RolloutRuleComparatorV2Type.DateTime) {\n <ng-container [ngTemplateOutlet]=\"dateTimeTemplate\" />\n }\n }\n</div>\n\n<ng-template #textTemplate>\n @if (formGroup().controls.comparisonValue.controls.stringValue) {\n <div class=\"targeting-rule-component grow\">\n <mat-form-field\n appearance=\"outline\"\n class=\"small-form-field\"\n subscriptSizing=\"dynamic\"\n [class.suffixed]=\"!isClearTextComparator\"\n [class.suffixed-prefixed]=\"isClearTextComparator\">\n <input\n matInput\n type=\"text\"\n matTooltip=\"Comparison value (text)\"\n placeholder=\"Add text ...\"\n appPastedDataTrimmer\n [formControl]=\"formGroup().controls.comparisonValue.controls.stringValue!\" />\n @if (isClearTextComparator) {\n <button\n matPrefix\n class=\"prefix-button\"\n type=\"button\"\n mat-icon-button\n matTooltip=\"This comparison value won't be hashed. Click for more info.\"\n (click)=\"openCleartextInfoDialog()\">\n <mat-icon>lock_open_right</mat-icon>\n </button>\n }\n <button\n matSuffix\n class=\"input-suffix-button\"\n type=\"button\"\n mat-icon-button\n matTooltip=\"Advanced editor\"\n (click)=\"expandText()\">\n <mat-icon class=\"icon-transform-rotate\">edit_note</mat-icon>\n </button>\n @if (formGroup().controls.comparisonValue.controls.stringValue!.invalid) {\n <mat-error>\n {{ FormHelper.getErrorMessage(formGroup().controls.comparisonValue.controls.stringValue!) }}\n </mat-error>\n }\n </mat-form-field>\n </div>\n }\n</ng-template>\n\n<ng-template #semverTemplate>\n @if (formGroup().controls.comparisonValue.controls.stringValue) {\n <div class=\"targeting-rule-component grow\">\n <mat-form-field appearance=\"outline\" class=\"small-form-field\" subscriptSizing=\"dynamic\">\n <input\n matInput\n type=\"text\"\n matTooltip=\"Comparison value (semantic version)\"\n placeholder=\"Add semantic version ... (1.2.3)\"\n [formControl]=\"formGroup().controls.comparisonValue.controls.stringValue!\" />\n @if (formGroup().controls.comparisonValue.controls.stringValue!.invalid) {\n <mat-error>\n {{ FormHelper.getErrorMessage(formGroup().controls.comparisonValue.controls.stringValue!) }}\n </mat-error>\n }\n </mat-form-field>\n </div>\n }\n</ng-template>\n\n<ng-template #listTemplate>\n @if (formGroup().controls.comparisonValue.controls.listValue) {\n <div class=\"targeting-rule-component grow small-chip-input top-icon-suffix\">\n <mat-form-field\n appearance=\"outline\"\n class=\"small-form-field suffixed\"\n subscriptSizing=\"dynamic\"\n [class.suffixed]=\"!isClearTextComparator\"\n [class.suffixed-prefixed]=\"isClearTextComparator\">\n <mat-chip-grid #chipGrid [formControl]=\"fakeFormControl\" [errorStateMatcher]=\"errorStateMatcher\">\n @for (\n listValueItem of formGroup().controls.comparisonValue.controls.listValue!.controls | slice: 0 : 20;\n track $index;\n let i = $index\n ) {\n <span class=\"chip-container\" [matTooltip]=\"getListValueItemTooltipMessage(listValueItem)\">\n <mat-chip-row\n class=\"chip\"\n [disabled]=\"readOnly()\"\n [removable]=\"!readOnly()\"\n [editable]=\"!readOnly()\"\n [ngClass]=\"{ invalid: isListValueItemInvalid(listValueItem) }\"\n (removed)=\"remove(i)\"\n (edited)=\"edit(i, $event)\">\n {{ listValueItem.controls.value.value }}\n @if (!readOnly()) {\n <button type=\"button\" matChipRemove>\n <mat-icon class=\"icon-transform-rotate\">cancel</mat-icon>\n </button>\n }\n </mat-chip-row>\n </span>\n }\n @if (formGroup().controls.comparisonValue.controls.listValue!.controls.length > 20) {\n <button mat-stroked-button class=\"show-all\" type=\"button\" (click)=\"expandList()\">\n show all {{ formGroup().controls.comparisonValue.controls.listValue!.controls.length }} items\n </button>\n }\n <input\n matChipInputAddOnBlur\n matTooltip=\"Comparison values (list)\"\n [disabled]=\"readOnly()\"\n [placeholder]=\"!readOnly() ? 'Add item to list ...' : ''\"\n [matChipInputFor]=\"chipGrid\"\n [matChipInputSeparatorKeyCodes]=\"separatorKeysCodes\"\n (matChipInputTokenEnd)=\"add($event)\" />\n </mat-chip-grid>\n @if (isClearTextComparator) {\n <button\n matIconPrefix\n class=\"prefix-button\"\n type=\"button\"\n mat-icon-button\n matTooltip=\"This comparison value won't be hashed. Click for more info.\"\n (click)=\"openCleartextInfoDialog()\">\n <mat-icon>lock_open_right</mat-icon>\n </button>\n }\n <button\n matIconSuffix\n class=\"input-suffix-button\"\n type=\"button\"\n mat-icon-button\n matTooltip=\"Advanced editor\"\n (click)=\"expandList()\">\n <mat-icon class=\"icon-transform-rotate\">edit_note</mat-icon>\n </button>\n <button\n matIconSuffix\n class=\"input-suffix-button\"\n type=\"button\"\n mat-icon-button\n matTooltip=\"More...\"\n [matMenuTriggerFor]=\"moreMenu\">\n <mat-icon>more_vert</mat-icon>\n </button>\n @if (\n formGroup().controls.comparisonValue.controls.listValue!.invalid &&\n formGroup().controls.comparisonValue.controls.listValue!.errors\n ) {\n <mat-error>\n {{ FormHelper.getErrorMessage(formGroup().controls.comparisonValue.controls.listValue!) }}\n </mat-error>\n }\n @if (\n formGroup().controls.comparisonValue.controls.listValue!.invalid &&\n !formGroup().controls.comparisonValue.controls.listValue!.errors\n ) {\n <mat-error>Invalid items</mat-error>\n }\n </mat-form-field>\n </div>\n }\n</ng-template>\n\n<ng-template #numberTemplate>\n @if (formGroup().controls.comparisonValue.controls.doubleValue) {\n <div class=\"targeting-rule-component grow\">\n <mat-form-field appearance=\"outline\" class=\"small-form-field\" subscriptSizing=\"dynamic\">\n <input\n matInput\n type=\"number\"\n matTooltip=\"Comparison value (number)\"\n placeholder=\"Add number ...\"\n inputDisplayNormalizer\n [formControl]=\"formGroup().controls.comparisonValue.controls.doubleValue!\" />\n @if (formGroup().controls.comparisonValue.controls.doubleValue!.invalid) {\n <mat-error>\n {{ FormHelper.getErrorMessage(formGroup().controls.comparisonValue.controls.doubleValue!) }}\n </mat-error>\n }\n </mat-form-field>\n </div>\n }\n</ng-template>\n\n<ng-template #dateTimeTemplate>\n @if (formGroup().controls.comparisonValue.controls.doubleValue) {\n <div class=\"targeting-rule-component datetime\">\n <app-date-time-picker\n dateTooltip=\"Comparison value (date)\"\n timeTooltip=\"Comparison value (time)\"\n [formControl]=\"formGroup().controls.comparisonValue.controls.doubleValue!\" />\n </div>\n }\n</ng-template>\n\n<mat-menu #moreMenu>\n <a mat-menu-item (click)=\"expandList()\">\n <mat-icon class=\"icon-transform-rotate\">edit_note</mat-icon>\n Open advanced editor\n </a>\n <a mat-menu-item (click)=\"copyValuesAsCsv()\">\n <mat-icon>content_copy</mat-icon>\n Copy as CSV (values only)\n </a>\n <a mat-menu-item [disabled]=\"readOnly()\" (click)=\"removeAll()\">\n <mat-icon>delete</mat-icon>\n Remove all\n </a>\n</mat-menu>\n", styles: [".targeting-rule-line{display:flex;flex-wrap:wrap;min-width:0;margin:2px 0}.targeting-rule-line>*{min-width:0;max-width:100%}.targeting-rule-line .wrapper-group{display:flex;flex-wrap:wrap;min-width:0}.targeting-rule-line .wrapper-group.grow{flex-grow:1;min-width:0}.targeting-rule-component{margin:2px 0;min-width:0;max-width:100%}.targeting-rule-component[hidden]{display:none}.targeting-rule-component{width:212px}.targeting-rule-component.small{width:102px}.targeting-rule-component.grow{flex-grow:1;min-width:0}.targeting-rule-component.pad-right{margin-right:8px}.targeting-rule-component mat-form-field{width:100%;min-width:0;max-width:100%}.targeting-rule-component mat-form-field ::ng-deep .mat-mdc-form-field-infix{min-width:0;width:auto}.targeting-rule-component mat-select,.targeting-rule-component input{min-width:0;max-width:100%}.delete-button{display:flex;color:var(--flag-delete-btn);margin-top:4px;margin-left:4px}.delete-button button{display:flex;align-items:center;justify-content:center}.delete-button mat-icon{font-size:18px;height:18px;width:18px}.explanation-wrapper{display:flex;margin-top:2px;margin-bottom:3px}.explanation-wrapper .explanation{min-width:44px;margin-top:7px;margin-right:8px}@media(max-width:500px){.targeting-rule-component{max-width:calc(100% - 4px);width:160px}.targeting-rule-component.small{width:78px}.targeting-rule-component.pad-right{margin-right:4px}}\n", ".mat-mdc-chip.mat-mdc-standard-chip{--mat-chip-container-height: 24px}.targeting-rule-line{--mat-button-filled-container-height: 24px;--mat-button-filled-touch-target-display: none;--mat-button-outlined-container-height: 24px;--mat-button-outlined-touch-target-display: none;--mat-button-protected-container-height: 24px;--mat-button-protected-touch-target-display: none;--mat-button-text-container-height: 24px;--mat-button-text-touch-target-display: none;--mat-button-tonal-container-height: 24px;--mat-button-tonal-touch-target-display: none}.targeting-rule-component.datetime{width:auto;margin:0}mat-chip-row.invalid{background-color:var(--flag-box-rule-list-chip-validation-error-background)}.show-all{font-size:.8em;margin-left:4px}.prefix-button{--mat-icon-button-touch-target-display: none}.prefix-button .mat-mdc-icon-button.mat-mdc-button-base{--mdc-icon-button-state-layer-size: 28px;--mat-icon-button-state-layer-size: 28px;width:var(--mat-icon-button-state-layer-size);height:var(--mat-icon-button-state-layer-size);padding:2px}.prefix-button{color:var(--action-icon-button-color);margin-left:4px;margin-right:2px;height:20px;width:20px;padding:0;line-height:1;display:inline-flex;align-items:center;justify-content:center}.prefix-button mat-icon{font-size:18px;height:18px;width:18px}.chip{background-color:var(--flag-box-rule-list-chip)}.chip mat-icon{color:var(--action-icon-button-color)}\n"], dependencies: [{ kind: "component", type: ConditionChipComponent, selector: "app-condition-chip", inputs: ["targetingRuleIndex", "conditionIndex"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: 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: "directive", type: MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.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: i1.NumberValueAccessor, selector: "input[type=number]:not([ngNoCva])[formControlName],input[type=number]:not([ngNoCva])[formControl],input[type=number]:not([ngNoCva])[ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: RxReactiveFormsModule }, { kind: "directive", type: i2.AsyncValidationDirective, selector: "[ngModel],[formControlName],[formControl]", inputs: ["async"] }, { kind: "directive", type: i2.RxFormControlDirective, selector: "[ngModel],[formControlName],[formControl]", inputs: ["rxalpha", "rxalphaNumeric", "rxascii", "rxcompare", "rxcompose", "rxcontains", "rxcreditCard", "rxdataUri", "rxdifferent", "rxdigit", "rxemail", "rxendsWith", "rxeven", "rxextension", "rxfactor", "rxfileSize", "rxgreaterThanEqualTo", "rxgreaterThan", "rxhexColor", "rxjson", "rxlatitude", "rxlatLong", "rxleapYear", "rxlessThan", "rxlessThanEqualTo", "rxlongitude", "rxlowerCase", "rxmac", "rxmaxDate", "rxmaxLength", "rxmaxNumber", "rxminDate", "rxminLength", "rxminNumber", "rxnumeric", "rxodd", "rxpassword", "rxport", "rxprimeNumber", "rxrequired", "rxrange", "rxrule", "rxstartsWith", "rxtime", "rxupperCase", "rxurl", "rxunique", "rxnotEmpty", "rxcusip", "rxgrid", "rxdate"] }, { kind: "directive", type: MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "component", type: MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "component", type: MatOptgroup, selector: "mat-optgroup", inputs: ["label", "disabled"], exportAs: ["matOptgroup"] }, { kind: "component", type: MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "directive", type: MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: MatPrefix, selector: "[matPrefix], [matIconPrefix], [matTextPrefix]", inputs: ["matTextPrefix"] }, { kind: "component", type: MatChipGrid, selector: "mat-chip-grid", inputs: ["disabled", "placeholder", "required", "value", "errorStateMatcher"], outputs: ["change", "valueChange"] }, { kind: "component", type: MatChipRow, selector: "mat-chip-row, [mat-chip-row], mat-basic-chip-row, [mat-basic-chip-row]", inputs: ["editable"], outputs: ["edited"] }, { kind: "directive", type: MatChipRemove, selector: "[matChipRemove]" }, { kind: "component", type: 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: "directive", type: MatChipInput, selector: "input[matChipInputFor]", inputs: ["matChipInputFor", "matChipInputAddOnBlur", "matChipInputSeparatorKeyCodes", "placeholder", "id", "disabled", "readonly", "matChipInputDisabledInteractive"], outputs: ["matChipInputTokenEnd"], exportAs: ["matChipInput", "matChipInputFor"] }, { kind: "directive", type: MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "directive", type: InputDisplayNormalizerDirective, selector: "input[inputDisplayNormalizer]" }, { kind: "component", type: DateTimePickerComponent, selector: "app-date-time-picker", inputs: ["dateTooltip", "timeTooltip"] }, { kind: "component", type: MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "component", type: MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "directive", type: PastedDataTrimmerDirective, selector: "[appPastedDataTrimmer]" }, { kind: "component", type: MatAutocomplete, selector: "mat-autocomplete", inputs: ["aria-label", "aria-labelledby", "displayWith", "autoActiveFirstOption", "autoSelectActiveOption", "requireSelection", "panelWidth", "disableRipple", "class", "hideSingleSelectionIndicator"], outputs: ["optionSelected", "opened", "closed", "optionActivated"], exportAs: ["matAutocomplete"] }, { kind: "directive", type: MatAutocompleteTrigger, selector: "input[matAutocomplete], textarea[matAutocomplete]", inputs: ["matAutocomplete", "matAutocompletePosition", "matAutocompleteConnectedTo", "autocomplete", "matAutocompleteDisabled"], exportAs: ["matAutocompleteTrigger"] }, { kind: "pipe", type: SlicePipe, name: "slice" }, { kind: "pipe", type: KeyValuePipe, name: "keyvalue" }], changeDetection: i0.ChangeDetectionStrategy.Eager }); }
6041
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: UserConditionComponent, isStandalone: true, selector: "app-user-condition", inputs: { targetingRuleIndex: { classPropertyName: "targetingRuleIndex", publicName: "targetingRuleIndex", isSignal: true, isRequired: true, transformFunction: null }, conditionIndex: { classPropertyName: "conditionIndex", publicName: "conditionIndex", isSignal: true, isRequired: true, transformFunction: null }, readOnly: { classPropertyName: "readOnly", publicName: "readOnly", isSignal: true, isRequired: true, transformFunction: null }, featureFlagLimitations: { classPropertyName: "featureFlagLimitations", publicName: "featureFlagLimitations", isSignal: true, isRequired: true, transformFunction: null }, formGroup: { classPropertyName: "formGroup", publicName: "formGroup", isSignal: true, isRequired: true, transformFunction: null }, comparisonAttributes: { classPropertyName: "comparisonAttributes", publicName: "comparisonAttributes", isSignal: true, isRequired: true, transformFunction: null } }, providers: [{ provide: DateAdapter, useClass: AppDateAdapter }], viewQueries: [{ propertyName: "matChipGrid", first: true, predicate: ["chipGrid"], descendants: true, isSignal: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"targeting-rule-line\">\n <div class=\"explanation-wrapper\">\n <app-condition-chip [targetingRuleIndex]=\"targetingRuleIndex()\" [conditionIndex]=\"conditionIndex()\" />\n\n <div class=\"explanation\">\n <a\n href=\"https://configcat.com/docs/targeting/targeting-rule/user-condition/\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"user-link\">\n user's\n </a>\n </div>\n </div>\n\n <div class=\"wrapper-group\">\n <div class=\"wrapper-group\">\n <div\n class=\"targeting-rule-component pad-right\"\n [ngClass]=\"{\n small: comparisionAttributeCustom === selectedAttribute,\n }\">\n <mat-form-field appearance=\"outline\" class=\"small-form-field select\" subscriptSizing=\"dynamic\">\n <mat-select\n matTooltip=\"Comparison attribute\"\n [disabled]=\"formGroup().controls.comparisonAttribute.disabled\"\n [(value)]=\"selectedAttribute\"\n (selectionChange)=\"attributeChanged(selectedAttribute)\">\n @for (comparisonAttribute of defaultComparisonAttributes; track $index) {\n <mat-option [value]=\"comparisonAttribute\">{{ comparisonAttribute }}</mat-option>\n }\n <mat-option value=\"Custom\">Custom</mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n <div class=\"targeting-rule-component pad-right small\" [hidden]=\"comparisionAttributeCustom !== selectedAttribute\">\n <mat-form-field appearance=\"outline\" class=\"small-form-field\" subscriptSizing=\"dynamic\">\n <input\n matInput\n type=\"text\"\n matTooltip=\"Custom comparison attribute\"\n [matAutocomplete]=\"auto\"\n [formControl]=\"formGroup().controls.comparisonAttribute\"\n (blur)=\"calcComparators(true)\" />\n @if (formGroup().controls.comparisonAttribute.invalid) {\n <mat-error>\n {{ FormHelper.getErrorMessage(formGroup().controls.comparisonAttribute) }}\n </mat-error>\n }\n </mat-form-field>\n <mat-autocomplete #auto=\"matAutocomplete\" panelWidth=\"auto\">\n @for (option of comparisonAttributes(); track $index) {\n <mat-option [value]=\"option\">\n {{ option }}\n </mat-option>\n }\n </mat-autocomplete>\n </div>\n </div>\n\n <div class=\"targeting-rule-component pad-right suffixed\">\n <mat-form-field appearance=\"outline\" class=\"small-form-field select-suffixed\" subscriptSizing=\"dynamic\">\n <mat-select\n matTooltip=\"Comparator\"\n [disabled]=\"readOnly()\"\n [(value)]=\"selectedComparator\"\n (selectionChange)=\"comparatorChanged(selectedComparator)\">\n @for (group of comparatorSuggestions | keyvalue: keepOrder; track group) {\n <mat-optgroup [label]=\"group.key\">\n @for (comparator of group.value; track comparator) {\n <mat-option [value]=\"comparator.value\">\n {{ comparator.label }}\n </mat-option>\n }\n </mat-optgroup>\n }\n <mat-divider />\n <mat-option value=\"showall\">\n <mat-icon>list_alt</mat-icon>\n SHOW ALL COMPARATORS\n </mat-option>\n </mat-select>\n <button\n matSuffix\n class=\"input-suffix-button\"\n type=\"button\"\n mat-icon-button\n matTooltip=\"All comparators\"\n (click)=\"compDialog(); $event.stopPropagation()\">\n <mat-icon>list_alt</mat-icon>\n </button>\n @if (formGroup().controls.comparator.invalid) {\n <mat-error>\n {{ FormHelper.getErrorMessage(formGroup().controls.comparator) }}\n </mat-error>\n }\n </mat-form-field>\n </div>\n </div>\n @switch (rolloutRuleComparatorType) {\n @case (RolloutRuleComparatorV2Type.String) {\n <ng-container [ngTemplateOutlet]=\"textTemplate\" />\n }\n @case (RolloutRuleComparatorV2Type.Semver) {\n <ng-container [ngTemplateOutlet]=\"semverTemplate\" />\n }\n @case (RolloutRuleComparatorV2Type.StringList) {\n <ng-container [ngTemplateOutlet]=\"listTemplate\" />\n }\n @case (RolloutRuleComparatorV2Type.SemverList) {\n <ng-container [ngTemplateOutlet]=\"listTemplate\" />\n }\n @case (RolloutRuleComparatorV2Type.Number) {\n <ng-container [ngTemplateOutlet]=\"numberTemplate\" />\n }\n @case (RolloutRuleComparatorV2Type.DateTime) {\n <ng-container [ngTemplateOutlet]=\"dateTimeTemplate\" />\n }\n }\n</div>\n\n<ng-template #textTemplate>\n @if (formGroup().controls.comparisonValue.controls.stringValue) {\n <div class=\"targeting-rule-component grow\">\n <mat-form-field\n appearance=\"outline\"\n class=\"small-form-field\"\n subscriptSizing=\"dynamic\"\n [class.suffixed]=\"!isClearTextComparator\"\n [class.suffixed-prefixed]=\"isClearTextComparator\">\n <input\n matInput\n type=\"text\"\n matTooltip=\"Comparison value (text)\"\n placeholder=\"Add text ...\"\n appPastedDataTrimmer\n [formControl]=\"formGroup().controls.comparisonValue.controls.stringValue!\" />\n @if (isClearTextComparator) {\n <button\n matPrefix\n class=\"prefix-button\"\n type=\"button\"\n mat-icon-button\n matTooltip=\"This comparison value won't be hashed. Click for more info.\"\n (click)=\"openCleartextInfoDialog()\">\n <mat-icon>lock_open_right</mat-icon>\n </button>\n }\n <button\n matSuffix\n class=\"input-suffix-button\"\n type=\"button\"\n mat-icon-button\n matTooltip=\"Advanced editor\"\n (click)=\"expandText()\">\n <mat-icon class=\"icon-transform-rotate\">edit_note</mat-icon>\n </button>\n @if (formGroup().controls.comparisonValue.controls.stringValue!.invalid) {\n <mat-error>\n {{ FormHelper.getErrorMessage(formGroup().controls.comparisonValue.controls.stringValue!) }}\n </mat-error>\n }\n </mat-form-field>\n </div>\n }\n</ng-template>\n\n<ng-template #semverTemplate>\n @if (formGroup().controls.comparisonValue.controls.stringValue) {\n <div class=\"targeting-rule-component grow\">\n <mat-form-field appearance=\"outline\" class=\"small-form-field\" subscriptSizing=\"dynamic\">\n <input\n matInput\n type=\"text\"\n matTooltip=\"Comparison value (semantic version)\"\n placeholder=\"Add semantic version ... (1.2.3)\"\n [formControl]=\"formGroup().controls.comparisonValue.controls.stringValue!\" />\n @if (formGroup().controls.comparisonValue.controls.stringValue!.invalid) {\n <mat-error>\n {{ FormHelper.getErrorMessage(formGroup().controls.comparisonValue.controls.stringValue!) }}\n </mat-error>\n }\n </mat-form-field>\n </div>\n }\n</ng-template>\n\n<ng-template #listTemplate>\n @if (formGroup().controls.comparisonValue.controls.listValue) {\n <div class=\"targeting-rule-component grow small-chip-input top-icon-suffix\">\n <mat-form-field\n appearance=\"outline\"\n class=\"small-form-field suffixed\"\n subscriptSizing=\"dynamic\"\n [class.suffixed]=\"!isClearTextComparator\"\n [class.suffixed-prefixed]=\"isClearTextComparator\">\n <mat-chip-grid #chipGrid [formControl]=\"fakeFormControl\" [errorStateMatcher]=\"errorStateMatcher\">\n @for (\n listValueItem of formGroup().controls.comparisonValue.controls.listValue!.controls | slice: 0 : 20;\n track $index;\n let i = $index\n ) {\n <span class=\"chip-container\" [matTooltip]=\"getListValueItemTooltipMessage(listValueItem)\">\n <mat-chip-row\n class=\"chip\"\n [disabled]=\"readOnly()\"\n [removable]=\"!readOnly()\"\n [editable]=\"!readOnly()\"\n [ngClass]=\"{ invalid: isListValueItemInvalid(listValueItem) }\"\n (removed)=\"remove(i)\"\n (edited)=\"edit(i, $event)\">\n {{ listValueItem.controls.value.value }}\n @if (!readOnly()) {\n <button type=\"button\" matChipRemove>\n <mat-icon class=\"icon-transform-rotate\">cancel</mat-icon>\n </button>\n }\n </mat-chip-row>\n </span>\n }\n @if (formGroup().controls.comparisonValue.controls.listValue!.controls.length > 20) {\n <button mat-stroked-button class=\"show-all\" type=\"button\" (click)=\"expandList()\">\n show all {{ formGroup().controls.comparisonValue.controls.listValue!.controls.length }} items\n </button>\n }\n <input\n matChipInputAddOnBlur\n matTooltip=\"Comparison values (list)\"\n [disabled]=\"readOnly()\"\n [placeholder]=\"!readOnly() ? 'Add item to list ...' : ''\"\n [matChipInputFor]=\"chipGrid\"\n [matChipInputSeparatorKeyCodes]=\"separatorKeysCodes\"\n (matChipInputTokenEnd)=\"add($event)\" />\n </mat-chip-grid>\n @if (isClearTextComparator) {\n <button\n matIconPrefix\n class=\"prefix-button\"\n type=\"button\"\n mat-icon-button\n matTooltip=\"This comparison value won't be hashed. Click for more info.\"\n (click)=\"openCleartextInfoDialog()\">\n <mat-icon>lock_open_right</mat-icon>\n </button>\n }\n <button\n matIconSuffix\n class=\"input-suffix-button\"\n type=\"button\"\n mat-icon-button\n matTooltip=\"Advanced editor\"\n (click)=\"expandList()\">\n <mat-icon class=\"icon-transform-rotate\">edit_note</mat-icon>\n </button>\n <button\n matIconSuffix\n class=\"input-suffix-button\"\n type=\"button\"\n mat-icon-button\n matTooltip=\"More...\"\n [matMenuTriggerFor]=\"moreMenu\">\n <mat-icon>more_vert</mat-icon>\n </button>\n @if (\n formGroup().controls.comparisonValue.controls.listValue!.invalid &&\n formGroup().controls.comparisonValue.controls.listValue!.errors\n ) {\n <mat-error>\n {{ FormHelper.getErrorMessage(formGroup().controls.comparisonValue.controls.listValue!) }}\n </mat-error>\n }\n @if (\n formGroup().controls.comparisonValue.controls.listValue!.invalid &&\n !formGroup().controls.comparisonValue.controls.listValue!.errors\n ) {\n <mat-error>Invalid items</mat-error>\n }\n </mat-form-field>\n </div>\n }\n</ng-template>\n\n<ng-template #numberTemplate>\n @if (formGroup().controls.comparisonValue.controls.doubleValue) {\n <div class=\"targeting-rule-component grow\">\n <mat-form-field appearance=\"outline\" class=\"small-form-field\" subscriptSizing=\"dynamic\">\n <input\n matInput\n type=\"number\"\n matTooltip=\"Comparison value (number)\"\n placeholder=\"Add number ...\"\n inputDisplayNormalizer\n [formControl]=\"formGroup().controls.comparisonValue.controls.doubleValue!\" />\n @if (formGroup().controls.comparisonValue.controls.doubleValue!.invalid) {\n <mat-error>\n {{ FormHelper.getErrorMessage(formGroup().controls.comparisonValue.controls.doubleValue!) }}\n </mat-error>\n }\n </mat-form-field>\n </div>\n }\n</ng-template>\n\n<ng-template #dateTimeTemplate>\n @if (formGroup().controls.comparisonValue.controls.doubleValue) {\n <div class=\"targeting-rule-component datetime\">\n <app-date-time-picker\n dateTooltip=\"Comparison value (date)\"\n timeTooltip=\"Comparison value (time)\"\n [formControl]=\"formGroup().controls.comparisonValue.controls.doubleValue!\" />\n </div>\n }\n</ng-template>\n\n<mat-menu #moreMenu>\n <a mat-menu-item (click)=\"expandList()\">\n <mat-icon class=\"icon-transform-rotate\">edit_note</mat-icon>\n Open advanced editor\n </a>\n <a mat-menu-item (click)=\"copyValuesAsCsv()\">\n <mat-icon>content_copy</mat-icon>\n Copy as CSV (values only)\n </a>\n <a mat-menu-item [disabled]=\"readOnly()\" (click)=\"removeAll()\">\n <mat-icon>delete</mat-icon>\n Remove all\n </a>\n</mat-menu>\n", styles: [".targeting-rule-line{display:flex;flex-wrap:wrap;min-width:0;margin:2px 0}.targeting-rule-line>*{min-width:0;max-width:100%}.targeting-rule-line .wrapper-group{display:flex;flex-wrap:wrap;min-width:0}.targeting-rule-line .wrapper-group.grow{flex-grow:1;min-width:0}.targeting-rule-component{margin:2px 0;min-width:0;max-width:100%}.targeting-rule-component[hidden]{display:none}.targeting-rule-component{width:212px}.targeting-rule-component.small{width:102px}.targeting-rule-component.grow{flex-grow:1;min-width:0}.targeting-rule-component.pad-right{margin-right:8px}.targeting-rule-component mat-form-field{width:100%;min-width:0;max-width:100%}.targeting-rule-component mat-form-field ::ng-deep .mat-mdc-form-field-infix{min-width:0;width:auto}.targeting-rule-component mat-select,.targeting-rule-component input{min-width:0;max-width:100%}.delete-button{display:flex;color:var(--flag-delete-btn);margin-top:4px;margin-left:4px}.delete-button button{display:flex;align-items:center;justify-content:center}.delete-button mat-icon{font-size:18px;height:18px;width:18px}.explanation-wrapper{display:flex;margin-top:2px;margin-bottom:3px}.explanation-wrapper .explanation{min-width:44px;margin-top:7px;margin-right:8px}@media(max-width:500px){.targeting-rule-component{max-width:calc(100% - 4px);width:160px}.targeting-rule-component.small{width:78px}.targeting-rule-component.pad-right{margin-right:4px}}\n", ".mat-mdc-chip.mat-mdc-standard-chip{--mat-chip-container-height: 24px}.targeting-rule-line{--mat-button-filled-container-height: 24px;--mat-button-filled-touch-target-display: none;--mat-button-outlined-container-height: 24px;--mat-button-outlined-touch-target-display: none;--mat-button-protected-container-height: 24px;--mat-button-protected-touch-target-display: none;--mat-button-text-container-height: 24px;--mat-button-text-touch-target-display: none;--mat-button-tonal-container-height: 24px;--mat-button-tonal-touch-target-display: none}.targeting-rule-component.datetime{width:auto;margin:0}mat-chip-row.invalid{background-color:var(--flag-box-rule-list-chip-validation-error-background)}.show-all{height:22px!important;margin:1px 0 1px 4px}.prefix-button{--mat-icon-button-touch-target-display: none}.prefix-button .mat-mdc-icon-button.mat-mdc-button-base{--mdc-icon-button-state-layer-size: 28px;--mat-icon-button-state-layer-size: 28px;width:var(--mat-icon-button-state-layer-size);height:var(--mat-icon-button-state-layer-size);padding:2px}.prefix-button{color:var(--action-icon-button-color);margin-left:4px;margin-right:2px;height:20px;width:20px;padding:0;line-height:1;display:inline-flex;align-items:center;justify-content:center}.prefix-button mat-icon{font-size:18px;height:18px;width:18px}.chip{background-color:var(--flag-box-rule-list-chip)}.chip mat-icon{color:var(--action-icon-button-color)}\n"], dependencies: [{ kind: "component", type: ConditionChipComponent, selector: "app-condition-chip", inputs: ["targetingRuleIndex", "conditionIndex"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: 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: "directive", type: MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.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: i1.NumberValueAccessor, selector: "input[type=number]:not([ngNoCva])[formControlName],input[type=number]:not([ngNoCva])[formControl],input[type=number]:not([ngNoCva])[ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: RxReactiveFormsModule }, { kind: "directive", type: i2.AsyncValidationDirective, selector: "[ngModel],[formControlName],[formControl]", inputs: ["async"] }, { kind: "directive", type: i2.RxFormControlDirective, selector: "[ngModel],[formControlName],[formControl]", inputs: ["rxalpha", "rxalphaNumeric", "rxascii", "rxcompare", "rxcompose", "rxcontains", "rxcreditCard", "rxdataUri", "rxdifferent", "rxdigit", "rxemail", "rxendsWith", "rxeven", "rxextension", "rxfactor", "rxfileSize", "rxgreaterThanEqualTo", "rxgreaterThan", "rxhexColor", "rxjson", "rxlatitude", "rxlatLong", "rxleapYear", "rxlessThan", "rxlessThanEqualTo", "rxlongitude", "rxlowerCase", "rxmac", "rxmaxDate", "rxmaxLength", "rxmaxNumber", "rxminDate", "rxminLength", "rxminNumber", "rxnumeric", "rxodd", "rxpassword", "rxport", "rxprimeNumber", "rxrequired", "rxrange", "rxrule", "rxstartsWith", "rxtime", "rxupperCase", "rxurl", "rxunique", "rxnotEmpty", "rxcusip", "rxgrid", "rxdate"] }, { kind: "directive", type: MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "component", type: MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "component", type: MatOptgroup, selector: "mat-optgroup", inputs: ["label", "disabled"], exportAs: ["matOptgroup"] }, { kind: "component", type: MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "directive", type: MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: MatPrefix, selector: "[matPrefix], [matIconPrefix], [matTextPrefix]", inputs: ["matTextPrefix"] }, { kind: "component", type: MatChipGrid, selector: "mat-chip-grid", inputs: ["disabled", "placeholder", "required", "value", "errorStateMatcher"], outputs: ["change", "valueChange"] }, { kind: "component", type: MatChipRow, selector: "mat-chip-row, [mat-chip-row], mat-basic-chip-row, [mat-basic-chip-row]", inputs: ["editable"], outputs: ["edited"] }, { kind: "directive", type: MatChipRemove, selector: "[matChipRemove]" }, { kind: "component", type: 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: "directive", type: MatChipInput, selector: "input[matChipInputFor]", inputs: ["matChipInputFor", "matChipInputAddOnBlur", "matChipInputSeparatorKeyCodes", "placeholder", "id", "disabled", "readonly", "matChipInputDisabledInteractive"], outputs: ["matChipInputTokenEnd"], exportAs: ["matChipInput", "matChipInputFor"] }, { kind: "directive", type: MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "directive", type: InputDisplayNormalizerDirective, selector: "input[inputDisplayNormalizer]" }, { kind: "component", type: DateTimePickerComponent, selector: "app-date-time-picker", inputs: ["dateTooltip", "timeTooltip"] }, { kind: "component", type: MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "component", type: MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "directive", type: PastedDataTrimmerDirective, selector: "[appPastedDataTrimmer]" }, { kind: "component", type: MatAutocomplete, selector: "mat-autocomplete", inputs: ["aria-label", "aria-labelledby", "displayWith", "autoActiveFirstOption", "autoSelectActiveOption", "requireSelection", "panelWidth", "disableRipple", "class", "hideSingleSelectionIndicator"], outputs: ["optionSelected", "opened", "closed", "optionActivated"], exportAs: ["matAutocomplete"] }, { kind: "directive", type: MatAutocompleteTrigger, selector: "input[matAutocomplete], textarea[matAutocomplete]", inputs: ["matAutocomplete", "matAutocompletePosition", "matAutocompleteConnectedTo", "autocomplete", "matAutocompleteDisabled"], exportAs: ["matAutocompleteTrigger"] }, { kind: "pipe", type: SlicePipe, name: "slice" }, { kind: "pipe", type: KeyValuePipe, name: "keyvalue" }], changeDetection: i0.ChangeDetectionStrategy.Eager }); }
5918
6042
  }
5919
6043
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: UserConditionComponent, decorators: [{
5920
6044
  type: Component,
@@ -5952,7 +6076,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImpor
5952
6076
  PastedDataTrimmerDirective,
5953
6077
  MatAutocomplete,
5954
6078
  MatAutocompleteTrigger,
5955
- ], template: "<div class=\"targeting-rule-line\">\n <div class=\"explanation-wrapper\">\n <app-condition-chip [targetingRuleIndex]=\"targetingRuleIndex()\" [conditionIndex]=\"conditionIndex()\" />\n\n <div class=\"explanation\">\n <a\n href=\"https://configcat.com/docs/targeting/targeting-rule/user-condition/\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"user-link\">\n user's\n </a>\n </div>\n </div>\n\n <div class=\"wrapper-group\">\n <div class=\"wrapper-group\">\n <div\n class=\"targeting-rule-component pad-right\"\n [ngClass]=\"{\n small: comparisionAttributeCustom === selectedAttribute,\n }\">\n <mat-form-field appearance=\"outline\" class=\"small-form-field select\" subscriptSizing=\"dynamic\">\n <mat-select\n matTooltip=\"Comparison attribute\"\n [disabled]=\"formGroup().controls.comparisonAttribute.disabled\"\n [(value)]=\"selectedAttribute\"\n (selectionChange)=\"attributeChanged(selectedAttribute)\">\n @for (comparisonAttribute of defaultComparisonAttributes; track $index) {\n <mat-option [value]=\"comparisonAttribute\">{{ comparisonAttribute }}</mat-option>\n }\n <mat-option value=\"Custom\">Custom</mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n <div class=\"targeting-rule-component pad-right small\" [hidden]=\"comparisionAttributeCustom !== selectedAttribute\">\n <mat-form-field appearance=\"outline\" class=\"small-form-field\" subscriptSizing=\"dynamic\">\n <input\n matInput\n type=\"text\"\n matTooltip=\"Custom comparison attribute\"\n [matAutocomplete]=\"auto\"\n [formControl]=\"formGroup().controls.comparisonAttribute\"\n (blur)=\"calcComparators(true)\" />\n @if (formGroup().controls.comparisonAttribute.invalid) {\n <mat-error>\n {{ FormHelper.getErrorMessage(formGroup().controls.comparisonAttribute) }}\n </mat-error>\n }\n </mat-form-field>\n <mat-autocomplete #auto=\"matAutocomplete\" panelWidth=\"auto\">\n @for (option of comparisonAttributes(); track $index) {\n <mat-option [value]=\"option\">\n {{ option }}\n </mat-option>\n }\n </mat-autocomplete>\n </div>\n </div>\n\n <div class=\"targeting-rule-component pad-right suffixed\">\n <mat-form-field appearance=\"outline\" class=\"small-form-field select-suffixed\" subscriptSizing=\"dynamic\">\n <mat-select\n matTooltip=\"Comparator\"\n [disabled]=\"readOnly()\"\n [(value)]=\"selectedComparator\"\n (selectionChange)=\"comparatorChanged(selectedComparator)\">\n @for (group of comparatorSuggestions | keyvalue: keepOrder; track group) {\n <mat-optgroup [label]=\"group.key\">\n @for (comparator of group.value; track comparator) {\n <mat-option [value]=\"comparator.value\">\n {{ comparator.label }}\n </mat-option>\n }\n </mat-optgroup>\n }\n <mat-divider />\n <mat-option value=\"showall\">\n <mat-icon>list_alt</mat-icon>\n SHOW ALL COMPARATORS\n </mat-option>\n </mat-select>\n <button\n matSuffix\n class=\"input-suffix-button\"\n type=\"button\"\n mat-icon-button\n matTooltip=\"All comparators\"\n (click)=\"compDialog(); $event.stopPropagation()\">\n <mat-icon>list_alt</mat-icon>\n </button>\n @if (formGroup().controls.comparator.invalid) {\n <mat-error>\n {{ FormHelper.getErrorMessage(formGroup().controls.comparator) }}\n </mat-error>\n }\n </mat-form-field>\n </div>\n </div>\n @switch (rolloutRuleComparatorType) {\n @case (RolloutRuleComparatorV2Type.String) {\n <ng-container [ngTemplateOutlet]=\"textTemplate\" />\n }\n @case (RolloutRuleComparatorV2Type.Semver) {\n <ng-container [ngTemplateOutlet]=\"semverTemplate\" />\n }\n @case (RolloutRuleComparatorV2Type.StringList) {\n <ng-container [ngTemplateOutlet]=\"listTemplate\" />\n }\n @case (RolloutRuleComparatorV2Type.SemverList) {\n <ng-container [ngTemplateOutlet]=\"listTemplate\" />\n }\n @case (RolloutRuleComparatorV2Type.Number) {\n <ng-container [ngTemplateOutlet]=\"numberTemplate\" />\n }\n @case (RolloutRuleComparatorV2Type.DateTime) {\n <ng-container [ngTemplateOutlet]=\"dateTimeTemplate\" />\n }\n }\n</div>\n\n<ng-template #textTemplate>\n @if (formGroup().controls.comparisonValue.controls.stringValue) {\n <div class=\"targeting-rule-component grow\">\n <mat-form-field\n appearance=\"outline\"\n class=\"small-form-field\"\n subscriptSizing=\"dynamic\"\n [class.suffixed]=\"!isClearTextComparator\"\n [class.suffixed-prefixed]=\"isClearTextComparator\">\n <input\n matInput\n type=\"text\"\n matTooltip=\"Comparison value (text)\"\n placeholder=\"Add text ...\"\n appPastedDataTrimmer\n [formControl]=\"formGroup().controls.comparisonValue.controls.stringValue!\" />\n @if (isClearTextComparator) {\n <button\n matPrefix\n class=\"prefix-button\"\n type=\"button\"\n mat-icon-button\n matTooltip=\"This comparison value won't be hashed. Click for more info.\"\n (click)=\"openCleartextInfoDialog()\">\n <mat-icon>lock_open_right</mat-icon>\n </button>\n }\n <button\n matSuffix\n class=\"input-suffix-button\"\n type=\"button\"\n mat-icon-button\n matTooltip=\"Advanced editor\"\n (click)=\"expandText()\">\n <mat-icon class=\"icon-transform-rotate\">edit_note</mat-icon>\n </button>\n @if (formGroup().controls.comparisonValue.controls.stringValue!.invalid) {\n <mat-error>\n {{ FormHelper.getErrorMessage(formGroup().controls.comparisonValue.controls.stringValue!) }}\n </mat-error>\n }\n </mat-form-field>\n </div>\n }\n</ng-template>\n\n<ng-template #semverTemplate>\n @if (formGroup().controls.comparisonValue.controls.stringValue) {\n <div class=\"targeting-rule-component grow\">\n <mat-form-field appearance=\"outline\" class=\"small-form-field\" subscriptSizing=\"dynamic\">\n <input\n matInput\n type=\"text\"\n matTooltip=\"Comparison value (semantic version)\"\n placeholder=\"Add semantic version ... (1.2.3)\"\n [formControl]=\"formGroup().controls.comparisonValue.controls.stringValue!\" />\n @if (formGroup().controls.comparisonValue.controls.stringValue!.invalid) {\n <mat-error>\n {{ FormHelper.getErrorMessage(formGroup().controls.comparisonValue.controls.stringValue!) }}\n </mat-error>\n }\n </mat-form-field>\n </div>\n }\n</ng-template>\n\n<ng-template #listTemplate>\n @if (formGroup().controls.comparisonValue.controls.listValue) {\n <div class=\"targeting-rule-component grow small-chip-input top-icon-suffix\">\n <mat-form-field\n appearance=\"outline\"\n class=\"small-form-field suffixed\"\n subscriptSizing=\"dynamic\"\n [class.suffixed]=\"!isClearTextComparator\"\n [class.suffixed-prefixed]=\"isClearTextComparator\">\n <mat-chip-grid #chipGrid [formControl]=\"fakeFormControl\" [errorStateMatcher]=\"errorStateMatcher\">\n @for (\n listValueItem of formGroup().controls.comparisonValue.controls.listValue!.controls | slice: 0 : 20;\n track $index;\n let i = $index\n ) {\n <span class=\"chip-container\" [matTooltip]=\"getListValueItemTooltipMessage(listValueItem)\">\n <mat-chip-row\n class=\"chip\"\n [disabled]=\"readOnly()\"\n [removable]=\"!readOnly()\"\n [editable]=\"!readOnly()\"\n [ngClass]=\"{ invalid: isListValueItemInvalid(listValueItem) }\"\n (removed)=\"remove(i)\"\n (edited)=\"edit(i, $event)\">\n {{ listValueItem.controls.value.value }}\n @if (!readOnly()) {\n <button type=\"button\" matChipRemove>\n <mat-icon class=\"icon-transform-rotate\">cancel</mat-icon>\n </button>\n }\n </mat-chip-row>\n </span>\n }\n @if (formGroup().controls.comparisonValue.controls.listValue!.controls.length > 20) {\n <button mat-stroked-button class=\"show-all\" type=\"button\" (click)=\"expandList()\">\n show all {{ formGroup().controls.comparisonValue.controls.listValue!.controls.length }} items\n </button>\n }\n <input\n matChipInputAddOnBlur\n matTooltip=\"Comparison values (list)\"\n [disabled]=\"readOnly()\"\n [placeholder]=\"!readOnly() ? 'Add item to list ...' : ''\"\n [matChipInputFor]=\"chipGrid\"\n [matChipInputSeparatorKeyCodes]=\"separatorKeysCodes\"\n (matChipInputTokenEnd)=\"add($event)\" />\n </mat-chip-grid>\n @if (isClearTextComparator) {\n <button\n matIconPrefix\n class=\"prefix-button\"\n type=\"button\"\n mat-icon-button\n matTooltip=\"This comparison value won't be hashed. Click for more info.\"\n (click)=\"openCleartextInfoDialog()\">\n <mat-icon>lock_open_right</mat-icon>\n </button>\n }\n <button\n matIconSuffix\n class=\"input-suffix-button\"\n type=\"button\"\n mat-icon-button\n matTooltip=\"Advanced editor\"\n (click)=\"expandList()\">\n <mat-icon class=\"icon-transform-rotate\">edit_note</mat-icon>\n </button>\n <button\n matIconSuffix\n class=\"input-suffix-button\"\n type=\"button\"\n mat-icon-button\n matTooltip=\"More...\"\n [matMenuTriggerFor]=\"moreMenu\">\n <mat-icon>more_vert</mat-icon>\n </button>\n @if (\n formGroup().controls.comparisonValue.controls.listValue!.invalid &&\n formGroup().controls.comparisonValue.controls.listValue!.errors\n ) {\n <mat-error>\n {{ FormHelper.getErrorMessage(formGroup().controls.comparisonValue.controls.listValue!) }}\n </mat-error>\n }\n @if (\n formGroup().controls.comparisonValue.controls.listValue!.invalid &&\n !formGroup().controls.comparisonValue.controls.listValue!.errors\n ) {\n <mat-error>Invalid items</mat-error>\n }\n </mat-form-field>\n </div>\n }\n</ng-template>\n\n<ng-template #numberTemplate>\n @if (formGroup().controls.comparisonValue.controls.doubleValue) {\n <div class=\"targeting-rule-component grow\">\n <mat-form-field appearance=\"outline\" class=\"small-form-field\" subscriptSizing=\"dynamic\">\n <input\n matInput\n type=\"number\"\n matTooltip=\"Comparison value (number)\"\n placeholder=\"Add number ...\"\n inputDisplayNormalizer\n [formControl]=\"formGroup().controls.comparisonValue.controls.doubleValue!\" />\n @if (formGroup().controls.comparisonValue.controls.doubleValue!.invalid) {\n <mat-error>\n {{ FormHelper.getErrorMessage(formGroup().controls.comparisonValue.controls.doubleValue!) }}\n </mat-error>\n }\n </mat-form-field>\n </div>\n }\n</ng-template>\n\n<ng-template #dateTimeTemplate>\n @if (formGroup().controls.comparisonValue.controls.doubleValue) {\n <div class=\"targeting-rule-component datetime\">\n <app-date-time-picker\n dateTooltip=\"Comparison value (date)\"\n timeTooltip=\"Comparison value (time)\"\n [formControl]=\"formGroup().controls.comparisonValue.controls.doubleValue!\" />\n </div>\n }\n</ng-template>\n\n<mat-menu #moreMenu>\n <a mat-menu-item (click)=\"expandList()\">\n <mat-icon class=\"icon-transform-rotate\">edit_note</mat-icon>\n Open advanced editor\n </a>\n <a mat-menu-item (click)=\"copyValuesAsCsv()\">\n <mat-icon>content_copy</mat-icon>\n Copy as CSV (values only)\n </a>\n <a mat-menu-item [disabled]=\"readOnly()\" (click)=\"removeAll()\">\n <mat-icon>delete</mat-icon>\n Remove all\n </a>\n</mat-menu>\n", styles: [".targeting-rule-line{display:flex;flex-wrap:wrap;min-width:0;margin:2px 0}.targeting-rule-line>*{min-width:0;max-width:100%}.targeting-rule-line .wrapper-group{display:flex;flex-wrap:wrap;min-width:0}.targeting-rule-line .wrapper-group.grow{flex-grow:1;min-width:0}.targeting-rule-component{margin:2px 0;min-width:0;max-width:100%}.targeting-rule-component[hidden]{display:none}.targeting-rule-component{width:212px}.targeting-rule-component.small{width:102px}.targeting-rule-component.grow{flex-grow:1;min-width:0}.targeting-rule-component.pad-right{margin-right:8px}.targeting-rule-component mat-form-field{width:100%;min-width:0;max-width:100%}.targeting-rule-component mat-form-field ::ng-deep .mat-mdc-form-field-infix{min-width:0;width:auto}.targeting-rule-component mat-select,.targeting-rule-component input{min-width:0;max-width:100%}.delete-button{display:flex;color:var(--flag-delete-btn);margin-top:4px;margin-left:4px}.delete-button button{display:flex;align-items:center;justify-content:center}.delete-button mat-icon{font-size:18px;height:18px;width:18px}.explanation-wrapper{display:flex;margin-top:2px;margin-bottom:3px}.explanation-wrapper .explanation{min-width:44px;margin-top:7px;margin-right:8px}@media(max-width:500px){.targeting-rule-component{max-width:calc(100% - 4px);width:160px}.targeting-rule-component.small{width:78px}.targeting-rule-component.pad-right{margin-right:4px}}\n", ".mat-mdc-chip.mat-mdc-standard-chip{--mat-chip-container-height: 24px}.targeting-rule-line{--mat-button-filled-container-height: 24px;--mat-button-filled-touch-target-display: none;--mat-button-outlined-container-height: 24px;--mat-button-outlined-touch-target-display: none;--mat-button-protected-container-height: 24px;--mat-button-protected-touch-target-display: none;--mat-button-text-container-height: 24px;--mat-button-text-touch-target-display: none;--mat-button-tonal-container-height: 24px;--mat-button-tonal-touch-target-display: none}.targeting-rule-component.datetime{width:auto;margin:0}mat-chip-row.invalid{background-color:var(--flag-box-rule-list-chip-validation-error-background)}.show-all{font-size:.8em;margin-left:4px}.prefix-button{--mat-icon-button-touch-target-display: none}.prefix-button .mat-mdc-icon-button.mat-mdc-button-base{--mdc-icon-button-state-layer-size: 28px;--mat-icon-button-state-layer-size: 28px;width:var(--mat-icon-button-state-layer-size);height:var(--mat-icon-button-state-layer-size);padding:2px}.prefix-button{color:var(--action-icon-button-color);margin-left:4px;margin-right:2px;height:20px;width:20px;padding:0;line-height:1;display:inline-flex;align-items:center;justify-content:center}.prefix-button mat-icon{font-size:18px;height:18px;width:18px}.chip{background-color:var(--flag-box-rule-list-chip)}.chip mat-icon{color:var(--action-icon-button-color)}\n"] }]
6079
+ ], template: "<div class=\"targeting-rule-line\">\n <div class=\"explanation-wrapper\">\n <app-condition-chip [targetingRuleIndex]=\"targetingRuleIndex()\" [conditionIndex]=\"conditionIndex()\" />\n\n <div class=\"explanation\">\n <a\n href=\"https://configcat.com/docs/targeting/targeting-rule/user-condition/\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"user-link\">\n user's\n </a>\n </div>\n </div>\n\n <div class=\"wrapper-group\">\n <div class=\"wrapper-group\">\n <div\n class=\"targeting-rule-component pad-right\"\n [ngClass]=\"{\n small: comparisionAttributeCustom === selectedAttribute,\n }\">\n <mat-form-field appearance=\"outline\" class=\"small-form-field select\" subscriptSizing=\"dynamic\">\n <mat-select\n matTooltip=\"Comparison attribute\"\n [disabled]=\"formGroup().controls.comparisonAttribute.disabled\"\n [(value)]=\"selectedAttribute\"\n (selectionChange)=\"attributeChanged(selectedAttribute)\">\n @for (comparisonAttribute of defaultComparisonAttributes; track $index) {\n <mat-option [value]=\"comparisonAttribute\">{{ comparisonAttribute }}</mat-option>\n }\n <mat-option value=\"Custom\">Custom</mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n <div class=\"targeting-rule-component pad-right small\" [hidden]=\"comparisionAttributeCustom !== selectedAttribute\">\n <mat-form-field appearance=\"outline\" class=\"small-form-field\" subscriptSizing=\"dynamic\">\n <input\n matInput\n type=\"text\"\n matTooltip=\"Custom comparison attribute\"\n [matAutocomplete]=\"auto\"\n [formControl]=\"formGroup().controls.comparisonAttribute\"\n (blur)=\"calcComparators(true)\" />\n @if (formGroup().controls.comparisonAttribute.invalid) {\n <mat-error>\n {{ FormHelper.getErrorMessage(formGroup().controls.comparisonAttribute) }}\n </mat-error>\n }\n </mat-form-field>\n <mat-autocomplete #auto=\"matAutocomplete\" panelWidth=\"auto\">\n @for (option of comparisonAttributes(); track $index) {\n <mat-option [value]=\"option\">\n {{ option }}\n </mat-option>\n }\n </mat-autocomplete>\n </div>\n </div>\n\n <div class=\"targeting-rule-component pad-right suffixed\">\n <mat-form-field appearance=\"outline\" class=\"small-form-field select-suffixed\" subscriptSizing=\"dynamic\">\n <mat-select\n matTooltip=\"Comparator\"\n [disabled]=\"readOnly()\"\n [(value)]=\"selectedComparator\"\n (selectionChange)=\"comparatorChanged(selectedComparator)\">\n @for (group of comparatorSuggestions | keyvalue: keepOrder; track group) {\n <mat-optgroup [label]=\"group.key\">\n @for (comparator of group.value; track comparator) {\n <mat-option [value]=\"comparator.value\">\n {{ comparator.label }}\n </mat-option>\n }\n </mat-optgroup>\n }\n <mat-divider />\n <mat-option value=\"showall\">\n <mat-icon>list_alt</mat-icon>\n SHOW ALL COMPARATORS\n </mat-option>\n </mat-select>\n <button\n matSuffix\n class=\"input-suffix-button\"\n type=\"button\"\n mat-icon-button\n matTooltip=\"All comparators\"\n (click)=\"compDialog(); $event.stopPropagation()\">\n <mat-icon>list_alt</mat-icon>\n </button>\n @if (formGroup().controls.comparator.invalid) {\n <mat-error>\n {{ FormHelper.getErrorMessage(formGroup().controls.comparator) }}\n </mat-error>\n }\n </mat-form-field>\n </div>\n </div>\n @switch (rolloutRuleComparatorType) {\n @case (RolloutRuleComparatorV2Type.String) {\n <ng-container [ngTemplateOutlet]=\"textTemplate\" />\n }\n @case (RolloutRuleComparatorV2Type.Semver) {\n <ng-container [ngTemplateOutlet]=\"semverTemplate\" />\n }\n @case (RolloutRuleComparatorV2Type.StringList) {\n <ng-container [ngTemplateOutlet]=\"listTemplate\" />\n }\n @case (RolloutRuleComparatorV2Type.SemverList) {\n <ng-container [ngTemplateOutlet]=\"listTemplate\" />\n }\n @case (RolloutRuleComparatorV2Type.Number) {\n <ng-container [ngTemplateOutlet]=\"numberTemplate\" />\n }\n @case (RolloutRuleComparatorV2Type.DateTime) {\n <ng-container [ngTemplateOutlet]=\"dateTimeTemplate\" />\n }\n }\n</div>\n\n<ng-template #textTemplate>\n @if (formGroup().controls.comparisonValue.controls.stringValue) {\n <div class=\"targeting-rule-component grow\">\n <mat-form-field\n appearance=\"outline\"\n class=\"small-form-field\"\n subscriptSizing=\"dynamic\"\n [class.suffixed]=\"!isClearTextComparator\"\n [class.suffixed-prefixed]=\"isClearTextComparator\">\n <input\n matInput\n type=\"text\"\n matTooltip=\"Comparison value (text)\"\n placeholder=\"Add text ...\"\n appPastedDataTrimmer\n [formControl]=\"formGroup().controls.comparisonValue.controls.stringValue!\" />\n @if (isClearTextComparator) {\n <button\n matPrefix\n class=\"prefix-button\"\n type=\"button\"\n mat-icon-button\n matTooltip=\"This comparison value won't be hashed. Click for more info.\"\n (click)=\"openCleartextInfoDialog()\">\n <mat-icon>lock_open_right</mat-icon>\n </button>\n }\n <button\n matSuffix\n class=\"input-suffix-button\"\n type=\"button\"\n mat-icon-button\n matTooltip=\"Advanced editor\"\n (click)=\"expandText()\">\n <mat-icon class=\"icon-transform-rotate\">edit_note</mat-icon>\n </button>\n @if (formGroup().controls.comparisonValue.controls.stringValue!.invalid) {\n <mat-error>\n {{ FormHelper.getErrorMessage(formGroup().controls.comparisonValue.controls.stringValue!) }}\n </mat-error>\n }\n </mat-form-field>\n </div>\n }\n</ng-template>\n\n<ng-template #semverTemplate>\n @if (formGroup().controls.comparisonValue.controls.stringValue) {\n <div class=\"targeting-rule-component grow\">\n <mat-form-field appearance=\"outline\" class=\"small-form-field\" subscriptSizing=\"dynamic\">\n <input\n matInput\n type=\"text\"\n matTooltip=\"Comparison value (semantic version)\"\n placeholder=\"Add semantic version ... (1.2.3)\"\n [formControl]=\"formGroup().controls.comparisonValue.controls.stringValue!\" />\n @if (formGroup().controls.comparisonValue.controls.stringValue!.invalid) {\n <mat-error>\n {{ FormHelper.getErrorMessage(formGroup().controls.comparisonValue.controls.stringValue!) }}\n </mat-error>\n }\n </mat-form-field>\n </div>\n }\n</ng-template>\n\n<ng-template #listTemplate>\n @if (formGroup().controls.comparisonValue.controls.listValue) {\n <div class=\"targeting-rule-component grow small-chip-input top-icon-suffix\">\n <mat-form-field\n appearance=\"outline\"\n class=\"small-form-field suffixed\"\n subscriptSizing=\"dynamic\"\n [class.suffixed]=\"!isClearTextComparator\"\n [class.suffixed-prefixed]=\"isClearTextComparator\">\n <mat-chip-grid #chipGrid [formControl]=\"fakeFormControl\" [errorStateMatcher]=\"errorStateMatcher\">\n @for (\n listValueItem of formGroup().controls.comparisonValue.controls.listValue!.controls | slice: 0 : 20;\n track $index;\n let i = $index\n ) {\n <span class=\"chip-container\" [matTooltip]=\"getListValueItemTooltipMessage(listValueItem)\">\n <mat-chip-row\n class=\"chip\"\n [disabled]=\"readOnly()\"\n [removable]=\"!readOnly()\"\n [editable]=\"!readOnly()\"\n [ngClass]=\"{ invalid: isListValueItemInvalid(listValueItem) }\"\n (removed)=\"remove(i)\"\n (edited)=\"edit(i, $event)\">\n {{ listValueItem.controls.value.value }}\n @if (!readOnly()) {\n <button type=\"button\" matChipRemove>\n <mat-icon class=\"icon-transform-rotate\">cancel</mat-icon>\n </button>\n }\n </mat-chip-row>\n </span>\n }\n @if (formGroup().controls.comparisonValue.controls.listValue!.controls.length > 20) {\n <button mat-stroked-button class=\"show-all\" type=\"button\" (click)=\"expandList()\">\n show all {{ formGroup().controls.comparisonValue.controls.listValue!.controls.length }} items\n </button>\n }\n <input\n matChipInputAddOnBlur\n matTooltip=\"Comparison values (list)\"\n [disabled]=\"readOnly()\"\n [placeholder]=\"!readOnly() ? 'Add item to list ...' : ''\"\n [matChipInputFor]=\"chipGrid\"\n [matChipInputSeparatorKeyCodes]=\"separatorKeysCodes\"\n (matChipInputTokenEnd)=\"add($event)\" />\n </mat-chip-grid>\n @if (isClearTextComparator) {\n <button\n matIconPrefix\n class=\"prefix-button\"\n type=\"button\"\n mat-icon-button\n matTooltip=\"This comparison value won't be hashed. Click for more info.\"\n (click)=\"openCleartextInfoDialog()\">\n <mat-icon>lock_open_right</mat-icon>\n </button>\n }\n <button\n matIconSuffix\n class=\"input-suffix-button\"\n type=\"button\"\n mat-icon-button\n matTooltip=\"Advanced editor\"\n (click)=\"expandList()\">\n <mat-icon class=\"icon-transform-rotate\">edit_note</mat-icon>\n </button>\n <button\n matIconSuffix\n class=\"input-suffix-button\"\n type=\"button\"\n mat-icon-button\n matTooltip=\"More...\"\n [matMenuTriggerFor]=\"moreMenu\">\n <mat-icon>more_vert</mat-icon>\n </button>\n @if (\n formGroup().controls.comparisonValue.controls.listValue!.invalid &&\n formGroup().controls.comparisonValue.controls.listValue!.errors\n ) {\n <mat-error>\n {{ FormHelper.getErrorMessage(formGroup().controls.comparisonValue.controls.listValue!) }}\n </mat-error>\n }\n @if (\n formGroup().controls.comparisonValue.controls.listValue!.invalid &&\n !formGroup().controls.comparisonValue.controls.listValue!.errors\n ) {\n <mat-error>Invalid items</mat-error>\n }\n </mat-form-field>\n </div>\n }\n</ng-template>\n\n<ng-template #numberTemplate>\n @if (formGroup().controls.comparisonValue.controls.doubleValue) {\n <div class=\"targeting-rule-component grow\">\n <mat-form-field appearance=\"outline\" class=\"small-form-field\" subscriptSizing=\"dynamic\">\n <input\n matInput\n type=\"number\"\n matTooltip=\"Comparison value (number)\"\n placeholder=\"Add number ...\"\n inputDisplayNormalizer\n [formControl]=\"formGroup().controls.comparisonValue.controls.doubleValue!\" />\n @if (formGroup().controls.comparisonValue.controls.doubleValue!.invalid) {\n <mat-error>\n {{ FormHelper.getErrorMessage(formGroup().controls.comparisonValue.controls.doubleValue!) }}\n </mat-error>\n }\n </mat-form-field>\n </div>\n }\n</ng-template>\n\n<ng-template #dateTimeTemplate>\n @if (formGroup().controls.comparisonValue.controls.doubleValue) {\n <div class=\"targeting-rule-component datetime\">\n <app-date-time-picker\n dateTooltip=\"Comparison value (date)\"\n timeTooltip=\"Comparison value (time)\"\n [formControl]=\"formGroup().controls.comparisonValue.controls.doubleValue!\" />\n </div>\n }\n</ng-template>\n\n<mat-menu #moreMenu>\n <a mat-menu-item (click)=\"expandList()\">\n <mat-icon class=\"icon-transform-rotate\">edit_note</mat-icon>\n Open advanced editor\n </a>\n <a mat-menu-item (click)=\"copyValuesAsCsv()\">\n <mat-icon>content_copy</mat-icon>\n Copy as CSV (values only)\n </a>\n <a mat-menu-item [disabled]=\"readOnly()\" (click)=\"removeAll()\">\n <mat-icon>delete</mat-icon>\n Remove all\n </a>\n</mat-menu>\n", styles: [".targeting-rule-line{display:flex;flex-wrap:wrap;min-width:0;margin:2px 0}.targeting-rule-line>*{min-width:0;max-width:100%}.targeting-rule-line .wrapper-group{display:flex;flex-wrap:wrap;min-width:0}.targeting-rule-line .wrapper-group.grow{flex-grow:1;min-width:0}.targeting-rule-component{margin:2px 0;min-width:0;max-width:100%}.targeting-rule-component[hidden]{display:none}.targeting-rule-component{width:212px}.targeting-rule-component.small{width:102px}.targeting-rule-component.grow{flex-grow:1;min-width:0}.targeting-rule-component.pad-right{margin-right:8px}.targeting-rule-component mat-form-field{width:100%;min-width:0;max-width:100%}.targeting-rule-component mat-form-field ::ng-deep .mat-mdc-form-field-infix{min-width:0;width:auto}.targeting-rule-component mat-select,.targeting-rule-component input{min-width:0;max-width:100%}.delete-button{display:flex;color:var(--flag-delete-btn);margin-top:4px;margin-left:4px}.delete-button button{display:flex;align-items:center;justify-content:center}.delete-button mat-icon{font-size:18px;height:18px;width:18px}.explanation-wrapper{display:flex;margin-top:2px;margin-bottom:3px}.explanation-wrapper .explanation{min-width:44px;margin-top:7px;margin-right:8px}@media(max-width:500px){.targeting-rule-component{max-width:calc(100% - 4px);width:160px}.targeting-rule-component.small{width:78px}.targeting-rule-component.pad-right{margin-right:4px}}\n", ".mat-mdc-chip.mat-mdc-standard-chip{--mat-chip-container-height: 24px}.targeting-rule-line{--mat-button-filled-container-height: 24px;--mat-button-filled-touch-target-display: none;--mat-button-outlined-container-height: 24px;--mat-button-outlined-touch-target-display: none;--mat-button-protected-container-height: 24px;--mat-button-protected-touch-target-display: none;--mat-button-text-container-height: 24px;--mat-button-text-touch-target-display: none;--mat-button-tonal-container-height: 24px;--mat-button-tonal-touch-target-display: none}.targeting-rule-component.datetime{width:auto;margin:0}mat-chip-row.invalid{background-color:var(--flag-box-rule-list-chip-validation-error-background)}.show-all{height:22px!important;margin:1px 0 1px 4px}.prefix-button{--mat-icon-button-touch-target-display: none}.prefix-button .mat-mdc-icon-button.mat-mdc-button-base{--mdc-icon-button-state-layer-size: 28px;--mat-icon-button-state-layer-size: 28px;width:var(--mat-icon-button-state-layer-size);height:var(--mat-icon-button-state-layer-size);padding:2px}.prefix-button{color:var(--action-icon-button-color);margin-left:4px;margin-right:2px;height:20px;width:20px;padding:0;line-height:1;display:inline-flex;align-items:center;justify-content:center}.prefix-button mat-icon{font-size:18px;height:18px;width:18px}.chip{background-color:var(--flag-box-rule-list-chip)}.chip mat-icon{color:var(--action-icon-button-color)}\n"] }]
5956
6080
  }], propDecorators: { targetingRuleIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "targetingRuleIndex", required: true }] }], conditionIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "conditionIndex", required: true }] }], readOnly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readOnly", required: true }] }], featureFlagLimitations: [{ type: i0.Input, args: [{ isSignal: true, alias: "featureFlagLimitations", required: true }] }], formGroup: [{ type: i0.Input, args: [{ isSignal: true, alias: "formGroup", required: true }] }], comparisonAttributes: [{ type: i0.Input, args: [{ isSignal: true, alias: "comparisonAttributes", required: true }] }], matChipGrid: [{ type: i0.ViewChild, args: ["chipGrid", { isSignal: true }] }] } });
5957
6081
 
5958
6082
  class ConditionsComponent {
@@ -7190,9 +7314,10 @@ class FeatureFlagItemComponent extends BaseComponent {
7190
7314
  && error.error
7191
7315
  && ("" + error.error).startsWith("Circular dependency detected.")) {
7192
7316
  const errorMessage = ("" + error.error).slice("Circular dependency detected.".length + 1);
7193
- this.openDiscardDialog("Cannot save feature flags", "Circular dependency detected between the following feature flags:<p>"
7194
- + errorMessage
7195
- + "</p>Please <strong>eliminate the circular dependency</strong> before saving your feature flags.", "I understand", "", true, "450px");
7317
+ this.openDiscardDialog("Cannot save feature flags", safeHtml `\
7318
+ Circular dependency detected between the following feature flags:
7319
+ <p>${errorMessage}</p>
7320
+ Please <strong>eliminate the circular dependency</strong> before saving your feature flags.`, "I understand", "", true, "450px");
7196
7321
  return;
7197
7322
  }
7198
7323
  if (error instanceof HttpErrorResponse && error.status === 404) {
@@ -7231,7 +7356,8 @@ class FeatureFlagItemComponent extends BaseComponent {
7231
7356
  this.dialog.open(RerouteDialogComponent, {
7232
7357
  data: {
7233
7358
  title: "You have reached the limits of your plan",
7234
- body: ' Upgrade your plan or join our <a href="https://configcat.com/referrals" target="_blank" rel="noopener noreferrer">referral program</a> to increase your limits.',
7359
+ body: safeHtml `\
7360
+ Upgrade your plan or join our <a href="https://configcat.com/referrals" target="_blank" rel="noopener noreferrer">referral program</a> to increase your limits.`,
7235
7361
  yesButtonLabel: "See plans",
7236
7362
  yesButtonLink: this.dashboardBasePath + "/organization/plans",
7237
7363
  noButtonLabel: "Close",
@@ -7948,6 +8074,19 @@ class CreateFeatureFlagComponent extends BaseComponent {
7948
8074
  if (error instanceof HttpErrorResponse && error.status === 401) {
7949
8075
  this.componentError.emit(error);
7950
8076
  }
8077
+ if (error instanceof HttpErrorResponse && error.status === 402) {
8078
+ //override the form validity and show dialog
8079
+ this.formGroup.updateValueAndValidity();
8080
+ this.dialog.open(RerouteDialogComponent, {
8081
+ data: {
8082
+ title: "You have reached the limits of your plan",
8083
+ body: ' Upgrade your plan or join our <a href="https://configcat.com/referrals" target="_blank" rel="noopener noreferrer">referral program</a> to increase your limits.',
8084
+ yesButtonLabel: "See plans",
8085
+ yesButtonLink: this.getDashboardBasePath() + "/organization/plans",
8086
+ noButtonLabel: "Close",
8087
+ },
8088
+ });
8089
+ }
7951
8090
  },
7952
8091
  });
7953
8092
  }
@@ -8066,7 +8205,7 @@ class CreateFeatureFlagComponent extends BaseComponent {
8066
8205
  this.componentError.emit(error);
8067
8206
  }
8068
8207
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: CreateFeatureFlagComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
8069
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: CreateFeatureFlagComponent, isStandalone: true, selector: "app-create-feature-flag", inputs: { customize: { classPropertyName: "customize", publicName: "customize", isSignal: true, isRequired: false, transformFunction: null }, presetProductAndConfig: { classPropertyName: "presetProductAndConfig", publicName: "presetProductAndConfig", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { createInitiated: "createInitiated", resizeRequested: "resizeRequested", cancelInitiated: "cancelInitiated", selectDropdownPanelChangedReqested: "selectDropdownPanelChangedReqested", componentError: "componentError" }, usesInheritance: true, ngImport: i0, template: "<div class=\"container\">\n <form class=\"form\" [formGroup]=\"formGroup\" (ngSubmit)=\"create()\">\n <div>\n <div class=\"header\">1. {{ customize().targetSectionHeader }}</div>\n @if (customize().targetSectionDescription) {\n <p>{{ customize().targetSectionDescription }}</p>\n }\n @if (this.presetProductAndConfig()) {\n <p>\n Pre selected product\n <b>{{ this.presetProductAndConfig()!.productName }}</b>\n </p>\n <p>\n Pre selected config\n <b>{{ this.presetProductAndConfig()!.configName }}</b>\n </p>\n }\n <app-product-select\n name=\"productId\"\n [hidden]=\"this.presetProductAndConfig()\"\n [basicAuthUsername]=\"basicAuthUsername()\"\n [basicAuthPassword]=\"basicAuthPassword()\"\n [preSelectedProductId]=\"this.presetProductAndConfig()?.productId\"\n [valueFormControl]=\"formGroup.controls.productId\"\n [customDropdown]=\"true\"\n (componentError)=\"dropdownComponentFailed($event)\" />\n @if (formGroup.controls.productId.value) {\n <app-config-select\n [hidden]=\"this.presetProductAndConfig()\"\n [basicAuthUsername]=\"basicAuthUsername()\"\n [basicAuthPassword]=\"basicAuthPassword()\"\n [preSelectedConfigId]=\"this.presetProductAndConfig()?.configId\"\n [productId]=\"formGroup.controls.productId.value\"\n [valueFormControl]=\"formGroup.controls.configId\"\n [customDropdown]=\"true\"\n (componentError)=\"dropdownComponentFailed($event)\" />\n }\n\n <div class=\"header\">2. {{ customize().flagSectionHeader }}</div>\n @if (customize().flagSectionDescription) {\n <p>{{ customize().flagSectionDescription }}</p>\n }\n @if (loadingComputed()) {\n <app-loader />\n } @else {\n <mat-form-field class=\"form-field\" appearance=\"outline\">\n <mat-label>Type</mat-label>\n <mat-select placeholder=\"Type\" formControlName=\"settingType\" panelClass=\"'custom-dropdown-below'\">\n <mat-option [value]=\"SettingTypeEnum.Boolean\">Feature Flag (bool)</mat-option>\n <mat-option [value]=\"SettingTypeEnum.String\">Text (string)</mat-option>\n <mat-option [value]=\"SettingTypeEnum.Int\">Whole Number (integer)</mat-option>\n <mat-option [value]=\"SettingTypeEnum.Double\">Decimal Number (double)</mat-option>\n </mat-select>\n </mat-form-field>\n <mat-form-field class=\"form-field name\" appearance=\"outline\">\n <mat-label>Name for humans</mat-label>\n <input matInput placeholder=\"My awesome feature\" formControlName=\"name\" />\n <mat-hint>A short name your team will see on the ConfigCat Dashboard.</mat-hint>\n @if (formGroup.controls.name.invalid) {\n <mat-error>\n {{ FormHelper.getErrorMessage(formGroup.controls.name) }}\n </mat-error>\n }\n </mat-form-field>\n <mat-form-field class=\"form-field key\" appearance=\"outline\">\n <mat-label>Key for programs</mat-label>\n <input matInput placeholder=\"isMyAwesomeFeatureEnabled\" formControlName=\"key\" />\n <mat-hint>Your application will use this key to access the setting.</mat-hint>\n @if (formGroup.controls.key.invalid) {\n <mat-error>\n {{ FormHelper.getErrorMessage(formGroup.controls.key) }}\n </mat-error>\n }\n </mat-form-field>\n <mat-form-field class=\"form-field hint last\" appearance=\"outline\">\n @if (!reasonRequired()) {\n <mat-label>Hint (Optional)</mat-label>\n } @else {\n <mat-label>Hint</mat-label>\n }\n <textarea matInput placeholder=\"Hint or description (Optional)\" formControlName=\"hint\"></textarea>\n <mat-hint>Explain what this setting controls. This helps your team use it correctly later.</mat-hint>\n @if (formGroup.controls.hint.invalid) {\n <mat-error>\n {{ FormHelper.getErrorMessage(formGroup.controls.hint) }}\n </mat-error>\n }\n </mat-form-field>\n\n <div class=\"init-value-section\">\n @if (configEvaluationVersion === EvaluationVersion.V2) {\n @if (true && formGroup.controls.settingType.value === SettingTypeEnum.String) {\n <h4>Require valid JSON?</h4>\n <div class=\"centered\">\n <mat-checkbox color=\"primary\" formControlName=\"isJson\">\n <span>Only valid JSON values can be saved</span>\n </mat-checkbox>\n <mat-icon\n color=\"primary\"\n class=\"info-icon\"\n matTooltip=\"Your application will get a string value. You will need to parse it into JSON if necessary.\">\n info\n </mat-icon>\n </div>\n }\n\n <h4>How to set values?</h4>\n <div class=\"value-mode-question\">\n <mat-form-field appearance=\"outline\" class=\"small-form-field value-mode-select\" subscriptSizing=\"dynamic\">\n <mat-select formControlName=\"useVariations\" class=\"choose\">\n <mat-option [value]=\"false\">\n {{\n formGroup.controls.settingType.value === SettingTypeEnum.Boolean\n ? \"use an ON/OFF toggle\"\n : \"enter free-form values\"\n }}\n </mat-option>\n <mat-option [value]=\"true\">choose from predefined variations</mat-option>\n </mat-select>\n </mat-form-field>\n <a href=\"https://configcat.com/docs/advanced/predefined-variations\" target=\"_blank\">\n <mat-icon class=\"info-icon\" color=\"primary\" matTooltip=\"Click to read more about value-modes.\">\n info\n </mat-icon>\n </a>\n </div>\n @if (formGroup.controls.useVariations.value) {\n <p>Define the values team members can choose from. At least two variations are required.</p>\n <div class=\"variations\">\n <div class=\"predef-var-table-scroll\">\n <table mat-table [dataSource]=\"dataSource\">\n <ng-container matColumnDef=\"color\">\n <th *matHeaderCellDef mat-header-cell></th>\n <td *matCellDef=\"let row; let i = index\" mat-cell class=\"row\">\n <span [class]=\"'variation-indicator variation-color-' + getColorIndex(row, i)\"></span>\n </td>\n </ng-container>\n\n <ng-container matColumnDef=\"value\">\n <th *matHeaderCellDef mat-header-cell>\n <div class=\"centered\">\n <span>Served value *</span>\n <mat-icon\n matTooltip=\"Your application will get this value when evaluating the setting.\"\n class=\"info-icon header-tooltip-icon\">\n info\n </mat-icon>\n </div>\n </th>\n <td *matCellDef=\"let row; let i = index\" mat-cell class=\"row align-top\">\n <div class=\"predefined-component\">\n @switch (formGroup.controls.settingType.value) {\n @case (SettingTypeEnum.Boolean) {\n <ng-container\n [ngTemplateOutlet]=\"settingValueTemplate\"\n [ngTemplateOutletContext]=\"{\n formControl: row.controls.value.controls.boolValue,\n setFocus: i === focusIndex,\n }\" />\n }\n @case (SettingTypeEnum.String) {\n <ng-container\n [ngTemplateOutlet]=\"settingValueTemplate\"\n [ngTemplateOutletContext]=\"{\n formControl: row.controls.value.controls.stringValue,\n setFocus: i === focusIndex,\n }\" />\n }\n @case (SettingTypeEnum.Int) {\n <ng-container\n [ngTemplateOutlet]=\"settingValueTemplate\"\n [ngTemplateOutletContext]=\"{\n formControl: row.controls.value.controls.intValue,\n setFocus: i === focusIndex,\n }\" />\n }\n @case (SettingTypeEnum.Double) {\n <ng-container\n [ngTemplateOutlet]=\"settingValueTemplate\"\n [ngTemplateOutletContext]=\"{\n formControl: row.controls.value.controls.doubleValue,\n setFocus: i === focusIndex,\n }\" />\n }\n }\n </div>\n </td>\n </ng-container>\n\n <ng-container matColumnDef=\"name\">\n <th *matHeaderCellDef mat-header-cell>\n <div class=\"centered\">\n <span>Display name (optional)</span>\n <mat-icon\n matTooltip=\"Optional friendly name. This will be displayed on the ConfigCat Dashboard.\"\n class=\"info-icon header-tooltip-icon\">\n info\n </mat-icon>\n </div>\n </th>\n <td *matCellDef=\"let row\" mat-cell class=\"row align-top\">\n <div class=\"predefined-component\">\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\" class=\"small-form-field\">\n <input\n matInput\n [formControl]=\"row.controls.name\"\n [placeholder]=\"row.controls.valueForComparison.value\" />\n @if (row.controls.name.invalid) {\n <mat-error class=\"small-error\">\n {{ FormHelper.getErrorMessage(row.controls.name) }}\n </mat-error>\n }\n </mat-form-field>\n </div>\n </td>\n </ng-container>\n\n <ng-container matColumnDef=\"hint\">\n <th *matHeaderCellDef mat-header-cell>\n <div class=\"centered\">\n <span>Hint (optional)</span>\n <mat-icon\n matTooltip=\"Optional hint. This will be displayed in a tooltip.\"\n class=\"info-icon header-tooltip-icon\">\n info\n </mat-icon>\n </div>\n </th>\n <td *matCellDef=\"let row\" mat-cell class=\"row align-top\">\n <mat-form-field\n appearance=\"outline\"\n subscriptSizing=\"dynamic\"\n class=\"small-form-field suffixed\">\n <button\n matSuffix\n class=\"input-suffix-button\"\n type=\"button\"\n mat-icon-button\n matTooltip=\"Advanced editor\"\n (click)=\"expandHint(row.controls.hint)\">\n <mat-icon class=\"icon-transform-rotate\">edit_note</mat-icon>\n </button>\n <input matInput [formControl]=\"row.controls.hint\" />\n @if (row.controls.hint.invalid) {\n <mat-error>\n {{ FormHelper.getErrorMessage(row.controls.hint) }}\n </mat-error>\n }\n </mat-form-field>\n </td>\n </ng-container>\n <ng-container matColumnDef=\"actions\">\n <th *matHeaderCellDef mat-header-cell>\n <span>{{ dataSource.data.length }} / {{ maxPredefinedVariations }}</span>\n </th>\n <td *matCellDef=\"let row; let i = index\" mat-cell class=\"row align-top\">\n <div\n [matTooltip]=\"\n formGroup.controls.predefinedVariations.controls.length > 2\n ? 'Remove item'\n : 'At least 2 predefined variations should be set.'\n \">\n <button\n mat-icon-button\n type=\"button\"\n [disabled]=\"formGroup.controls.predefinedVariations.controls.length <= 2\"\n (click)=\"removeVariation(i)\">\n <mat-icon>delete</mat-icon>\n </button>\n </div>\n </td>\n </ng-container>\n <tr *matHeaderRowDef=\"displayedColumns\" mat-header-row></tr>\n <tr *matRowDef=\"let row; columns: displayedColumns\" mat-row></tr>\n </table>\n </div>\n\n @if (\n formGroup.controls.predefinedVariations.invalid &&\n !!FormHelper.getErrorMessage(formGroup.controls.predefinedVariations)\n ) {\n <mat-error>\n {{ FormHelper.getErrorMessage(formGroup.controls.predefinedVariations) }}\n </mat-error>\n }\n <div class=\"add-variation\">\n @if (this.formGroup.controls.settingType.value !== SettingTypeEnum.Boolean) {\n <div\n [matTooltip]=\"'Maximum ' + maxPredefinedVariations + ' predefined variations can be added.'\"\n [matTooltipDisabled]=\"dataSource.data.length < maxPredefinedVariations\">\n <button\n type=\"button\"\n mat-stroked-button\n color=\"primary\"\n [disabled]=\"dataSource.data.length >= maxPredefinedVariations\"\n (click)=\"addVariation()\">\n <mat-icon>add</mat-icon>\n Add variation\n </button>\n </div>\n } @else {\n <app-box type=\"info\" class=\"bool-variation-info\">\n For more variations, create a flag of another type.\n <a\n href=\"https://configcat.com/docs/main-concepts/#about-setting-types\"\n target=\"_blank\"\n rel=\"noopener noreferrer\">\n Read more\n </a>\n about setting types.\n </app-box>\n }\n </div>\n </div>\n }\n }\n\n <h4>Initial values</h4>\n <div class=\"initial-value-first-line\">\n The initial value\n <mat-form-field\n appearance=\"outline\"\n class=\"small-form-field initial-value-environment-type\"\n subscriptSizing=\"dynamic\">\n <mat-select formControlName=\"initialValuesPerEnvironment\">\n <mat-option [value]=\"false\">in all environments</mat-option>\n <mat-option [value]=\"true\">per environment</mat-option>\n </mat-select>\n </mat-form-field>\n will be set to\n </div>\n @if (!formGroup.controls.initialValuesPerEnvironment.value) {\n @if (formGroup.controls.useVariations.value) {\n <div class=\"value-field all\">\n <ng-container\n [ngTemplateOutlet]=\"predefinedVariationTemplate\"\n [ngTemplateOutletContext]=\"{ formControl: formGroup.controls.initialValueForAllVariationIndex }\" />\n </div>\n } @else {\n <div class=\"value-field all\">\n <ng-container\n [ngTemplateOutlet]=\"settingValueTemplate\"\n [ngTemplateOutletContext]=\"{ formControl: formGroup.controls.initialValueForAll }\" />\n </div>\n }\n }\n\n @if (formGroup.controls.initialValuesPerEnvironment.value) {\n @for (initialValue of formGroup.controls.initialValues.controls; track $index; let index = $index) {\n <div formArrayName=\"initialValues\">\n <div class=\"value-field\" [formGroupName]=\"index\">\n @if (formGroup.controls.useVariations.value) {\n <ng-container\n [ngTemplateOutlet]=\"predefinedVariationTemplate\"\n [ngTemplateOutletContext]=\"{ formControl: initialValue.controls.predefinedVariationIndex }\" />\n } @else {\n <ng-container\n [ngTemplateOutlet]=\"settingValueTemplate\"\n [ngTemplateOutletContext]=\"{ formControl: initialValue.controls.settingValue }\" />\n }\n <div class=\"environment\">\n in\n <strong [matTooltip]=\"initialValue.controls.environmentName.value\">\n {{ initialValue.controls.environmentName.value }}\n </strong>\n </div>\n </div>\n </div>\n }\n }\n </div>\n }\n\n @if (!customize().hideLinkSection) {\n <div class=\"header\">3. {{ customize().linkSectionHeader }}</div>\n @if (customize().linkSectionDescription) {\n <p>{{ customize().linkSectionDescription }}</p>\n }\n @if (formGroup.controls.productId.value) {\n <app-environment-select\n [basicAuthUsername]=\"basicAuthUsername()\"\n [productId]=\"formGroup.controls.productId.value\"\n [basicAuthPassword]=\"basicAuthPassword()\"\n [valueFormControl]=\"formGroup.controls.environmentId\"\n [customDropdown]=\"true\"\n (componentError)=\"dropdownComponentFailed($event)\" />\n }\n }\n </div>\n @if (formGroup.errors && formGroup.errors[\"serverSide\"]) {\n <div class=\"error\">\n <span>{{ formGroup.errors[\"serverSide\"] }}</span>\n </div>\n }\n <div class=\"buttons\">\n <button\n mat-raised-button\n color=\"primary\"\n type=\"submit\"\n [disabled]=\"submitting() || loadingComputed() || !formGroup.valid\">\n {{ customize().createButtonLabel }}\n </button>\n @if (!customize().hideCancelButton) {\n <button mat-stroked-button type=\"button\" (click)=\"cancel()\">\n {{ customize().cancelButtonLabel }}\n </button>\n }\n </div>\n </form>\n</div>\n\n<ng-template #settingValueTemplate let-formControl=\"formControl\" let-setFocus=\"setFocus\">\n @switch (formGroup.controls.settingType.value) {\n @case (SettingTypeEnum.String) {\n <mat-form-field class=\"small-form-field text suffixed\" appearance=\"outline\" subscriptSizing=\"dynamic\">\n <input\n matInput\n type=\"text\"\n placeholder=\"Add text ...\"\n focus\n [isFocused]=\"setFocus\"\n [formControl]=\"formControl\" />\n <button\n matSuffix\n class=\"input-suffix-button\"\n type=\"button\"\n mat-icon-button\n matTooltip=\"Advanced editor\"\n (click)=\"expandText(formControl)\">\n <mat-icon class=\"icon-transform-rotate\">edit_note</mat-icon>\n </button>\n @if (formControl.invalid) {\n <mat-error class=\"small-error\">\n {{ FormHelper.getErrorMessage(formControl) }}\n </mat-error>\n }\n </mat-form-field>\n }\n @case (SettingTypeEnum.Int) {\n <mat-form-field class=\"small-form-field\" appearance=\"outline\" subscriptSizing=\"dynamic\">\n @if (formControl.invalid) {\n <mat-error class=\"small-error\">\n {{ FormHelper.getErrorMessage(formControl) }}\n </mat-error>\n }\n <input\n matInput\n type=\"number\"\n required\n name=\"index\"\n inputDisplayNormalizer\n focus\n [isFocused]=\"setFocus\"\n [formControl]=\"formControl\" />\n </mat-form-field>\n }\n @case (SettingTypeEnum.Double) {\n <mat-form-field class=\"small-form-field\" appearance=\"outline\" subscriptSizing=\"dynamic\">\n @if (formControl.invalid) {\n <mat-error class=\"small-error\">\n {{ FormHelper.getErrorMessage(formControl) }}\n </mat-error>\n }\n <input\n matInput\n type=\"number\"\n required\n name=\"index\"\n inputDisplayNormalizer\n focus\n [isFocused]=\"setFocus\"\n [formControl]=\"formControl\" />\n </mat-form-field>\n }\n @case (SettingTypeEnum.Boolean) {\n <div class=\"toggle\">\n <label class=\"switch\">\n <input type=\"checkbox\" name=\"index\" [formControl]=\"formControl\" />\n <span class=\"slider\"></span>\n </label>\n </div>\n }\n }\n</ng-template>\n\n<ng-template #predefinedVariationTemplate let-formControl=\"formControl\">\n <mat-form-field class=\"small-form-field\" appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-select [formControl]=\"formControl\">\n @for (predefinedVariation of formGroup.controls.predefinedVariations.controls; let idx = $index; track $index) {\n <mat-option [value]=\"idx\">\n @if (predefinedVariation.controls.name.value) {\n {{ predefinedVariation.controls.name.value }}\n } @else {\n {{ predefinedVariation.controls.valueForComparison.value }}\n }\n </mat-option>\n }\n </mat-select>\n @if (formControl.invalid) {\n <mat-error>\n {{ FormHelper.getErrorMessage(formControl) }}\n </mat-error>\n }\n </mat-form-field>\n</ng-template>\n", styles: [".container{margin:8px 8px 16px}.container .form>*{width:100%}.container .form>* .form-field{width:100%;padding-bottom:8px}.container .form>* .form-field.key{margin-bottom:16px}.container .form>* .form-field.name{margin-bottom:16px}.container .form>* .initial-value-first-line{align-items:center;min-height:34px;margin-bottom:8px}.container .form>* .initial-value-first-line .initial-value-environment-type{margin:0 6px;min-width:160px}.container .form>* .value-field{display:flex;align-items:center;width:100%}.container .form>* .value-field.all{display:inline-flex;margin-left:6px}.container .form>* .value-field.all mat-form-field{min-width:200px}.container .form>* .value-field.all mat-form-field.text{flex-grow:1;width:100%;min-width:100%}.container .form>* .value-field:not(.all){margin-bottom:8px}.container .form>* .value-field:not(.all) mat-form-field{min-width:200px}.container .form>* .value-field:not(.all) mat-form-field.text{min-width:330px}.container .form>* .value-field .environment{margin-left:1em;white-space:nowrap;text-overflow:ellipsis;overflow-x:hidden}.container .form>* .init-value-section{margin-bottom:16px}.container .form>* .init-value-section .predefined-component{padding:8px 0}.container .form>* .init-value-section .small-error{max-width:180px}.container .form>* .init-value-section .readmore{margin-left:8px}.container .form>* .init-value-section .value-mode-question{display:flex;align-items:center}.container .form>* .init-value-section .value-mode-question a{display:flex;align-items:center}.container .form>* .init-value-section .value-mode-question .value-mode-select{min-width:240px}.container .form>* .init-value-section .centered{display:flex;align-items:center}.container .form>* .init-value-section mat-icon.info-icon{color:var(--info-icon-color);margin-left:8px}.container .form>* .init-value-section .variations{display:flex;flex-direction:column}.container .form>* .init-value-section .variations .mat-column-color{padding-right:0!important}.container .form>* .init-value-section .variations .variation-header{margin-bottom:8px}.container .form>* .init-value-section .variations .add-variation{margin-top:12px;display:flex}.container .form>* .init-value-section .variations .toggle{margin-left:4px}.container .form>* .init-value-section .variations .header-tooltip-icon{font-size:14px;width:14px;height:14px}.container .form>* .init-value-section .variations .predef-var-table-scroll{display:block;overflow-x:auto}.container .form>* .init-value-section .variations .predef-var-table-scroll .mat-mdc-table{min-width:100%}.container .error{color:var(--flag-validation-error);margin:0 5px 5px}.container .header{font-size:18px;margin-top:16px;margin-bottom:16px}.container .header.small{font-size:16px;margin-top:16px;margin-bottom:16px}h4{margin-bottom:8px}.buttons{margin-bottom:16px}.buttons>*{margin-right:16px}\n", ".switch{display:inline-flex;align-items:center;width:66px;height:30px}.switch input{display:none}.switch input:checked+.slider{background-color:var(--flag-bool-slider-on)}.switch input:checked+.slider:before{transform:translate(36px)}.switch input:checked+.slider:after{content:\"On\";padding-left:0;padding-right:12px}.switch .slider{display:flex;align-items:center;justify-content:center;cursor:pointer;width:66px;height:30px;background-color:var(--flag-bool-slider-off);transition:background-color .4s;border-radius:34px;position:relative}.switch .slider:before{content:\"\";position:absolute;left:4px;height:22px;width:22px;background-color:var(--flag-bool-slider-remaining);transition:transform .4s;border-radius:50%}.switch .slider:after{content:\"Off\";color:var(--flag-bool-slider-remaining);font-size:10px;font-family:Verdana,sans-serif;padding-right:0;padding-left:11px}\n"], dependencies: [{ kind: "directive", type: AutofocusDirective, selector: "[focus]", inputs: ["isFocused"] }, { kind: "directive", type: InputDisplayNormalizerDirective, selector: "input[inputDisplayNormalizer]" }, { kind: "component", type: ProductSelectComponent, selector: "app-product-select", inputs: ["valueFormControl", "preSelectedProductId"], outputs: ["componentError"] }, { kind: "component", type: ConfigSelectComponent, selector: "app-config-select", inputs: ["valueFormControl", "productId", "preSelectedConfigId"], outputs: ["componentError"] }, { kind: "component", type: EnvironmentSelectComponent, selector: "app-environment-select", inputs: ["valueFormControl", "productId"], outputs: ["componentError"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.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: i1.NumberValueAccessor, selector: "input[type=number]:not([ngNoCva])[formControlName],input[type=number]:not([ngNoCva])[formControl],input[type=number]:not([ngNoCva])[ngModel]" }, { kind: "directive", type: i1.CheckboxControlValueAccessor, selector: "input[type=checkbox]:not([ngNoCva])[formControlName],input[type=checkbox]:not([ngNoCva])[formControl],input[type=checkbox]:not([ngNoCva])[ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i1.FormGroupName, selector: "[formGroupName]", inputs: ["formGroupName"] }, { kind: "directive", type: i1.FormArrayName, selector: "[formArrayName]", inputs: ["formArrayName"] }, { kind: "ngmodule", type: RxReactiveFormsModule }, { kind: "directive", type: i2.AsyncValidationDirective, selector: "[ngModel],[formControlName],[formControl]", inputs: ["async"] }, { kind: "directive", type: i2.RxwebFormDirective, selector: "[formGroup],[rxwebForm]", inputs: ["formGroup", "rxwebForm"] }, { kind: "directive", type: i2.RxFormControlDirective, selector: "[ngModel],[formControlName],[formControl]", inputs: ["rxalpha", "rxalphaNumeric", "rxascii", "rxcompare", "rxcompose", "rxcontains", "rxcreditCard", "rxdataUri", "rxdifferent", "rxdigit", "rxemail", "rxendsWith", "rxeven", "rxextension", "rxfactor", "rxfileSize", "rxgreaterThanEqualTo", "rxgreaterThan", "rxhexColor", "rxjson", "rxlatitude", "rxlatLong", "rxleapYear", "rxlessThan", "rxlessThanEqualTo", "rxlongitude", "rxlowerCase", "rxmac", "rxmaxDate", "rxmaxLength", "rxmaxNumber", "rxminDate", "rxminLength", "rxminNumber", "rxnumeric", "rxodd", "rxpassword", "rxport", "rxprimeNumber", "rxrequired", "rxrange", "rxrule", "rxstartsWith", "rxtime", "rxupperCase", "rxurl", "rxunique", "rxnotEmpty", "rxcusip", "rxgrid", "rxdate"] }, { kind: "component", type: MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: MatLabel, selector: "mat-label" }, { kind: "component", type: MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: MatOption$1, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: 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: "directive", type: MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "component", type: 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: MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: MatColumnDef, selector: "[matColumnDef]", inputs: ["matColumnDef"] }, { kind: "directive", type: MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "directive", type: MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "component", type: MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "directive", type: MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "component", type: MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "directive", type: MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "component", type: MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: LoaderComponent, selector: "app-loader", inputs: ["skipTimeOut"] }, { kind: "component", type: BoxComponent, selector: "app-box", inputs: ["type", "title"] }, { kind: "component", type: 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"] }], changeDetection: i0.ChangeDetectionStrategy.Eager }); }
8208
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: CreateFeatureFlagComponent, isStandalone: true, selector: "app-create-feature-flag", inputs: { customize: { classPropertyName: "customize", publicName: "customize", isSignal: true, isRequired: false, transformFunction: null }, presetProductAndConfig: { classPropertyName: "presetProductAndConfig", publicName: "presetProductAndConfig", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { createInitiated: "createInitiated", resizeRequested: "resizeRequested", cancelInitiated: "cancelInitiated", selectDropdownPanelChangedReqested: "selectDropdownPanelChangedReqested", componentError: "componentError" }, usesInheritance: true, ngImport: i0, template: "<div class=\"container\">\n <form class=\"form\" [formGroup]=\"formGroup\" (ngSubmit)=\"create()\">\n <div>\n <div class=\"header\">1. {{ customize().targetSectionHeader }}</div>\n @if (customize().targetSectionDescription) {\n <p>{{ customize().targetSectionDescription }}</p>\n }\n @if (this.presetProductAndConfig()) {\n <p>\n Pre selected product\n <b>{{ this.presetProductAndConfig()!.productName }}</b>\n </p>\n <p>\n Pre selected config\n <b>{{ this.presetProductAndConfig()!.configName }}</b>\n </p>\n }\n <app-product-select\n name=\"productId\"\n [hidden]=\"this.presetProductAndConfig()\"\n [basicAuthUsername]=\"basicAuthUsername()\"\n [basicAuthPassword]=\"basicAuthPassword()\"\n [preSelectedProductId]=\"this.presetProductAndConfig()?.productId\"\n [valueFormControl]=\"formGroup.controls.productId\"\n [customDropdown]=\"true\"\n (componentError)=\"dropdownComponentFailed($event)\" />\n @if (formGroup.controls.productId.value) {\n <app-config-select\n [hidden]=\"this.presetProductAndConfig()\"\n [basicAuthUsername]=\"basicAuthUsername()\"\n [basicAuthPassword]=\"basicAuthPassword()\"\n [preSelectedConfigId]=\"this.presetProductAndConfig()?.configId\"\n [productId]=\"formGroup.controls.productId.value\"\n [valueFormControl]=\"formGroup.controls.configId\"\n [customDropdown]=\"true\"\n (componentError)=\"dropdownComponentFailed($event)\" />\n }\n\n <div class=\"header\">2. {{ customize().flagSectionHeader }}</div>\n @if (customize().flagSectionDescription) {\n <p>{{ customize().flagSectionDescription }}</p>\n }\n @if (loadingComputed()) {\n <app-loader />\n } @else {\n <mat-form-field class=\"form-field\" appearance=\"outline\">\n <mat-label>Type</mat-label>\n <mat-select placeholder=\"Type\" formControlName=\"settingType\" panelClass=\"'custom-dropdown-below'\">\n <mat-option [value]=\"SettingTypeEnum.Boolean\">Feature Flag (bool)</mat-option>\n <mat-option [value]=\"SettingTypeEnum.String\">Text (string)</mat-option>\n <mat-option [value]=\"SettingTypeEnum.Int\">Whole Number (integer)</mat-option>\n <mat-option [value]=\"SettingTypeEnum.Double\">Decimal Number (double)</mat-option>\n </mat-select>\n </mat-form-field>\n <mat-form-field class=\"form-field name\" appearance=\"outline\">\n <mat-label>Name for humans</mat-label>\n <input matInput placeholder=\"My awesome feature\" formControlName=\"name\" />\n <mat-hint>A short name your team will see on the ConfigCat Dashboard.</mat-hint>\n @if (formGroup.controls.name.invalid) {\n <mat-error>\n {{ FormHelper.getErrorMessage(formGroup.controls.name) }}\n </mat-error>\n }\n </mat-form-field>\n <mat-form-field class=\"form-field key\" appearance=\"outline\">\n <mat-label>Key for programs</mat-label>\n <input matInput placeholder=\"isMyAwesomeFeatureEnabled\" formControlName=\"key\" />\n <mat-hint>Your application will use this key to access the setting.</mat-hint>\n @if (formGroup.controls.key.invalid) {\n <mat-error>\n {{ FormHelper.getErrorMessage(formGroup.controls.key) }}\n </mat-error>\n }\n </mat-form-field>\n <mat-form-field class=\"form-field hint last\" appearance=\"outline\">\n @if (!reasonRequired()) {\n <mat-label>Hint (Optional)</mat-label>\n } @else {\n <mat-label>Hint</mat-label>\n }\n <textarea matInput placeholder=\"Hint or description (Optional)\" formControlName=\"hint\"></textarea>\n <mat-hint>Explain what this setting controls. This helps your team use it correctly later.</mat-hint>\n @if (formGroup.controls.hint.invalid) {\n <mat-error>\n {{ FormHelper.getErrorMessage(formGroup.controls.hint) }}\n </mat-error>\n }\n </mat-form-field>\n\n <div class=\"init-value-section\">\n @if (configEvaluationVersion === EvaluationVersion.V2) {\n @if (true && formGroup.controls.settingType.value === SettingTypeEnum.String) {\n <h4>Require valid JSON?</h4>\n <div class=\"centered\">\n <mat-checkbox color=\"primary\" formControlName=\"isJson\">\n <span>Only valid JSON values can be saved</span>\n </mat-checkbox>\n <mat-icon\n color=\"primary\"\n class=\"info-icon\"\n matTooltip=\"Your application will get a string value. You will need to parse it into JSON if necessary.\">\n info\n </mat-icon>\n </div>\n }\n\n <h4>How to set values?</h4>\n <div class=\"value-mode-question\">\n <mat-form-field appearance=\"outline\" class=\"small-form-field value-mode-select\" subscriptSizing=\"dynamic\">\n <mat-select formControlName=\"useVariations\" class=\"choose\">\n <mat-option [value]=\"false\">\n {{\n formGroup.controls.settingType.value === SettingTypeEnum.Boolean\n ? \"use an ON/OFF toggle\"\n : \"enter free-form values\"\n }}\n </mat-option>\n <mat-option [value]=\"true\">choose from predefined variations</mat-option>\n </mat-select>\n </mat-form-field>\n <a href=\"https://configcat.com/docs/advanced/predefined-variations\" target=\"_blank\">\n <mat-icon class=\"info-icon\" color=\"primary\" matTooltip=\"Click to read more about value-modes.\">\n info\n </mat-icon>\n </a>\n </div>\n @if (formGroup.controls.useVariations.value) {\n <p>\n Define the values team members can choose from.\n {{\n formGroup.controls.settingType.value !== SettingTypeEnum.Boolean\n ? \"At least two variations are required.\"\n : null\n }}\n </p>\n <div class=\"variations\">\n <div class=\"predef-var-table-scroll\">\n <table mat-table [dataSource]=\"dataSource\">\n <ng-container matColumnDef=\"color\">\n <th *matHeaderCellDef mat-header-cell></th>\n <td *matCellDef=\"let row; let i = index\" mat-cell class=\"row\">\n <span [class]=\"'variation-indicator variation-color-' + getColorIndex(row, i)\"></span>\n </td>\n </ng-container>\n\n <ng-container matColumnDef=\"value\">\n <th *matHeaderCellDef mat-header-cell>\n <div class=\"centered\">\n <span>Served value *</span>\n <mat-icon\n matTooltip=\"Your application will get this value when evaluating the setting.\"\n class=\"info-icon header-tooltip-icon\">\n info\n </mat-icon>\n </div>\n </th>\n <td *matCellDef=\"let row; let i = index\" mat-cell class=\"row align-top\">\n <div class=\"predefined-component\">\n @switch (formGroup.controls.settingType.value) {\n @case (SettingTypeEnum.Boolean) {\n <ng-container\n [ngTemplateOutlet]=\"settingValueTemplate\"\n [ngTemplateOutletContext]=\"{\n formControl: row.controls.value.controls.boolValue,\n setFocus: i === focusIndex,\n }\" />\n }\n @case (SettingTypeEnum.String) {\n <ng-container\n [ngTemplateOutlet]=\"settingValueTemplate\"\n [ngTemplateOutletContext]=\"{\n formControl: row.controls.value.controls.stringValue,\n setFocus: i === focusIndex,\n }\" />\n }\n @case (SettingTypeEnum.Int) {\n <ng-container\n [ngTemplateOutlet]=\"settingValueTemplate\"\n [ngTemplateOutletContext]=\"{\n formControl: row.controls.value.controls.intValue,\n setFocus: i === focusIndex,\n }\" />\n }\n @case (SettingTypeEnum.Double) {\n <ng-container\n [ngTemplateOutlet]=\"settingValueTemplate\"\n [ngTemplateOutletContext]=\"{\n formControl: row.controls.value.controls.doubleValue,\n setFocus: i === focusIndex,\n }\" />\n }\n }\n </div>\n </td>\n </ng-container>\n\n <ng-container matColumnDef=\"name\">\n <th *matHeaderCellDef mat-header-cell>\n <div class=\"centered\">\n <span>Display name (optional)</span>\n <mat-icon\n matTooltip=\"Optional friendly name. This will be displayed on the ConfigCat Dashboard.\"\n class=\"info-icon header-tooltip-icon\">\n info\n </mat-icon>\n </div>\n </th>\n <td *matCellDef=\"let row\" mat-cell class=\"row align-top\">\n <div class=\"predefined-component\">\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\" class=\"small-form-field\">\n <input\n matInput\n [formControl]=\"row.controls.name\"\n [placeholder]=\"row.controls.valueForComparison.value\" />\n @if (row.controls.name.invalid) {\n <mat-error class=\"small-error\">\n {{ FormHelper.getErrorMessage(row.controls.name) }}\n </mat-error>\n }\n </mat-form-field>\n </div>\n </td>\n </ng-container>\n\n <ng-container matColumnDef=\"hint\">\n <th *matHeaderCellDef mat-header-cell>\n <div class=\"centered\">\n <span>Hint (optional)</span>\n <mat-icon\n matTooltip=\"Optional hint. This will be displayed in a tooltip.\"\n class=\"info-icon header-tooltip-icon\">\n info\n </mat-icon>\n </div>\n </th>\n <td *matCellDef=\"let row\" mat-cell class=\"row align-top\">\n <mat-form-field\n appearance=\"outline\"\n subscriptSizing=\"dynamic\"\n class=\"small-form-field suffixed\">\n <button\n matSuffix\n class=\"input-suffix-button\"\n type=\"button\"\n mat-icon-button\n matTooltip=\"Advanced editor\"\n (click)=\"expandHint(row.controls.hint)\">\n <mat-icon class=\"icon-transform-rotate\">edit_note</mat-icon>\n </button>\n <input matInput [formControl]=\"row.controls.hint\" />\n @if (row.controls.hint.invalid) {\n <mat-error>\n {{ FormHelper.getErrorMessage(row.controls.hint) }}\n </mat-error>\n }\n </mat-form-field>\n </td>\n </ng-container>\n <ng-container matColumnDef=\"actions\">\n <th *matHeaderCellDef mat-header-cell>\n <span>{{ dataSource.data.length }} / {{ maxPredefinedVariations }}</span>\n </th>\n <td *matCellDef=\"let row; let i = index\" mat-cell class=\"row align-top\">\n <div\n [matTooltip]=\"\n formGroup.controls.predefinedVariations.controls.length > 2\n ? 'Remove item'\n : 'At least 2 predefined variations should be set.'\n \">\n <button\n mat-icon-button\n type=\"button\"\n [disabled]=\"formGroup.controls.predefinedVariations.controls.length <= 2\"\n (click)=\"removeVariation(i)\">\n <mat-icon>delete</mat-icon>\n </button>\n </div>\n </td>\n </ng-container>\n <tr *matHeaderRowDef=\"displayedColumns\" mat-header-row></tr>\n <tr *matRowDef=\"let row; columns: displayedColumns\" mat-row></tr>\n </table>\n </div>\n\n @if (\n formGroup.controls.predefinedVariations.invalid &&\n !!FormHelper.getErrorMessage(formGroup.controls.predefinedVariations)\n ) {\n <mat-error>\n {{ FormHelper.getErrorMessage(formGroup.controls.predefinedVariations) }}\n </mat-error>\n }\n <div class=\"add-variation\">\n @if (this.formGroup.controls.settingType.value !== SettingTypeEnum.Boolean) {\n <div\n [matTooltip]=\"'Maximum ' + maxPredefinedVariations + ' predefined variations can be added.'\"\n [matTooltipDisabled]=\"dataSource.data.length < maxPredefinedVariations\">\n <button\n type=\"button\"\n mat-stroked-button\n color=\"primary\"\n [disabled]=\"dataSource.data.length >= maxPredefinedVariations\"\n (click)=\"addVariation()\">\n <mat-icon>add</mat-icon>\n Add variation\n </button>\n </div>\n } @else {\n <app-box type=\"info\" class=\"bool-variation-info\">\n For more variations, create a flag of another type.\n <a\n href=\"https://configcat.com/docs/main-concepts/#about-setting-types\"\n target=\"_blank\"\n rel=\"noopener noreferrer\">\n Read more\n </a>\n about setting types.\n </app-box>\n }\n </div>\n </div>\n }\n }\n\n <h4>Initial values</h4>\n <div class=\"initial-value-first-line\">\n The initial value\n <mat-form-field\n appearance=\"outline\"\n class=\"small-form-field initial-value-environment-type\"\n subscriptSizing=\"dynamic\">\n <mat-select formControlName=\"initialValuesPerEnvironment\">\n <mat-option [value]=\"false\">in all environments</mat-option>\n <mat-option [value]=\"true\">per environment</mat-option>\n </mat-select>\n </mat-form-field>\n will be set to\n </div>\n @if (!formGroup.controls.initialValuesPerEnvironment.value) {\n @if (formGroup.controls.useVariations.value) {\n <div class=\"value-field all\">\n <ng-container\n [ngTemplateOutlet]=\"predefinedVariationTemplate\"\n [ngTemplateOutletContext]=\"{ formControl: formGroup.controls.initialValueForAllVariationIndex }\" />\n </div>\n } @else {\n <div class=\"value-field all\">\n <ng-container\n [ngTemplateOutlet]=\"settingValueTemplate\"\n [ngTemplateOutletContext]=\"{ formControl: formGroup.controls.initialValueForAll }\" />\n </div>\n }\n }\n\n @if (formGroup.controls.initialValuesPerEnvironment.value) {\n @for (initialValue of formGroup.controls.initialValues.controls; track $index; let index = $index) {\n <div formArrayName=\"initialValues\">\n <div class=\"value-field\" [formGroupName]=\"index\">\n @if (formGroup.controls.useVariations.value) {\n <ng-container\n [ngTemplateOutlet]=\"predefinedVariationTemplate\"\n [ngTemplateOutletContext]=\"{ formControl: initialValue.controls.predefinedVariationIndex }\" />\n } @else {\n <ng-container\n [ngTemplateOutlet]=\"settingValueTemplate\"\n [ngTemplateOutletContext]=\"{ formControl: initialValue.controls.settingValue }\" />\n }\n <div class=\"environment\">\n in\n <strong [matTooltip]=\"initialValue.controls.environmentName.value\">\n {{ initialValue.controls.environmentName.value }}\n </strong>\n </div>\n </div>\n </div>\n }\n }\n </div>\n }\n\n @if (!customize().hideLinkSection) {\n <div class=\"header\">3. {{ customize().linkSectionHeader }}</div>\n @if (customize().linkSectionDescription) {\n <p>{{ customize().linkSectionDescription }}</p>\n }\n @if (formGroup.controls.productId.value) {\n <app-environment-select\n [basicAuthUsername]=\"basicAuthUsername()\"\n [productId]=\"formGroup.controls.productId.value\"\n [basicAuthPassword]=\"basicAuthPassword()\"\n [valueFormControl]=\"formGroup.controls.environmentId\"\n [customDropdown]=\"true\"\n (componentError)=\"dropdownComponentFailed($event)\" />\n }\n }\n </div>\n @if (formGroup.errors && formGroup.errors[\"serverSide\"]) {\n <div class=\"error\">\n <span>{{ formGroup.errors[\"serverSide\"] }}</span>\n </div>\n }\n <div class=\"buttons\">\n <button\n mat-raised-button\n color=\"primary\"\n type=\"submit\"\n [disabled]=\"submitting() || loadingComputed() || !formGroup.valid\">\n {{ customize().createButtonLabel }}\n </button>\n @if (!customize().hideCancelButton) {\n <button mat-stroked-button type=\"button\" (click)=\"cancel()\">\n {{ customize().cancelButtonLabel }}\n </button>\n }\n </div>\n </form>\n</div>\n\n<ng-template #settingValueTemplate let-formControl=\"formControl\" let-setFocus=\"setFocus\">\n @switch (formGroup.controls.settingType.value) {\n @case (SettingTypeEnum.String) {\n <mat-form-field class=\"small-form-field text suffixed\" appearance=\"outline\" subscriptSizing=\"dynamic\">\n <input\n matInput\n type=\"text\"\n placeholder=\"Add text ...\"\n focus\n [isFocused]=\"setFocus\"\n [formControl]=\"formControl\" />\n <button\n matSuffix\n class=\"input-suffix-button\"\n type=\"button\"\n mat-icon-button\n matTooltip=\"Advanced editor\"\n (click)=\"expandText(formControl)\">\n <mat-icon class=\"icon-transform-rotate\">edit_note</mat-icon>\n </button>\n @if (formControl.invalid) {\n <mat-error class=\"small-error\">\n {{ FormHelper.getErrorMessage(formControl) }}\n </mat-error>\n }\n </mat-form-field>\n }\n @case (SettingTypeEnum.Int) {\n <mat-form-field class=\"small-form-field\" appearance=\"outline\" subscriptSizing=\"dynamic\">\n @if (formControl.invalid) {\n <mat-error class=\"small-error\">\n {{ FormHelper.getErrorMessage(formControl) }}\n </mat-error>\n }\n <input\n matInput\n type=\"number\"\n required\n name=\"index\"\n inputDisplayNormalizer\n focus\n [isFocused]=\"setFocus\"\n [formControl]=\"formControl\" />\n </mat-form-field>\n }\n @case (SettingTypeEnum.Double) {\n <mat-form-field class=\"small-form-field\" appearance=\"outline\" subscriptSizing=\"dynamic\">\n @if (formControl.invalid) {\n <mat-error class=\"small-error\">\n {{ FormHelper.getErrorMessage(formControl) }}\n </mat-error>\n }\n <input\n matInput\n type=\"number\"\n required\n name=\"index\"\n inputDisplayNormalizer\n focus\n [isFocused]=\"setFocus\"\n [formControl]=\"formControl\" />\n </mat-form-field>\n }\n @case (SettingTypeEnum.Boolean) {\n <div class=\"toggle\">\n <label class=\"switch\">\n <input type=\"checkbox\" name=\"index\" [formControl]=\"formControl\" />\n <span class=\"slider\"></span>\n </label>\n </div>\n }\n }\n</ng-template>\n\n<ng-template #predefinedVariationTemplate let-formControl=\"formControl\">\n <mat-form-field class=\"small-form-field\" appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-select [formControl]=\"formControl\">\n @for (predefinedVariation of formGroup.controls.predefinedVariations.controls; let idx = $index; track $index) {\n <mat-option [value]=\"idx\">\n @if (predefinedVariation.controls.name.value) {\n {{ predefinedVariation.controls.name.value }}\n } @else {\n {{ predefinedVariation.controls.valueForComparison.value }}\n }\n </mat-option>\n }\n </mat-select>\n @if (formControl.invalid) {\n <mat-error>\n {{ FormHelper.getErrorMessage(formControl) }}\n </mat-error>\n }\n </mat-form-field>\n</ng-template>\n", styles: [".container{margin:8px 8px 16px}.container .form>*{width:100%}.container .form>* .form-field{width:100%;padding-bottom:8px}.container .form>* .form-field.key{margin-bottom:16px}.container .form>* .form-field.name{margin-bottom:16px}.container .form>* .initial-value-first-line{align-items:center;min-height:34px;margin-bottom:8px}.container .form>* .initial-value-first-line .initial-value-environment-type{margin:0 6px;min-width:160px}.container .form>* .value-field{display:flex;align-items:center;width:100%}.container .form>* .value-field.all{display:inline-flex;margin-left:6px}.container .form>* .value-field.all mat-form-field{min-width:200px}.container .form>* .value-field.all mat-form-field.text{flex-grow:1;width:100%;min-width:100%}.container .form>* .value-field:not(.all){margin-bottom:8px}.container .form>* .value-field:not(.all) mat-form-field{min-width:200px}.container .form>* .value-field:not(.all) mat-form-field.text{min-width:330px}.container .form>* .value-field .environment{margin-left:1em;white-space:nowrap;text-overflow:ellipsis;overflow-x:hidden}.container .form>* .init-value-section{margin-bottom:16px}.container .form>* .init-value-section .predefined-component{padding:8px 0}.container .form>* .init-value-section .small-error{max-width:180px}.container .form>* .init-value-section .readmore{margin-left:8px}.container .form>* .init-value-section .value-mode-question{display:flex;align-items:center}.container .form>* .init-value-section .value-mode-question a{display:flex;align-items:center}.container .form>* .init-value-section .value-mode-question .value-mode-select{min-width:240px}.container .form>* .init-value-section .centered{display:flex;align-items:center}.container .form>* .init-value-section mat-icon.info-icon{color:var(--info-icon-color);margin-left:8px}.container .form>* .init-value-section .variations{display:flex;flex-direction:column}.container .form>* .init-value-section .variations .mat-column-color{padding-right:0!important}.container .form>* .init-value-section .variations .variation-header{margin-bottom:8px}.container .form>* .init-value-section .variations .add-variation{margin-top:12px;display:flex}.container .form>* .init-value-section .variations .toggle{margin-left:4px}.container .form>* .init-value-section .variations .header-tooltip-icon{font-size:14px;width:14px;height:14px}.container .form>* .init-value-section .variations .predef-var-table-scroll{display:block;overflow-x:auto}.container .form>* .init-value-section .variations .predef-var-table-scroll .mat-mdc-table{min-width:100%}.container .error{color:var(--flag-validation-error);margin:0 5px 5px}.container .header{font-size:18px;margin-top:16px;margin-bottom:16px}.container .header.small{font-size:16px;margin-top:16px;margin-bottom:16px}h4{margin-bottom:8px}.buttons{margin-bottom:16px}.buttons>*{margin-right:16px}\n", ".switch{display:inline-flex;align-items:center;width:66px;height:30px}.switch input{display:none}.switch input:checked+.slider{background-color:var(--flag-bool-slider-on)}.switch input:checked+.slider:before{transform:translate(36px)}.switch input:checked+.slider:after{content:\"On\";padding-left:0;padding-right:12px}.switch .slider{display:flex;align-items:center;justify-content:center;cursor:pointer;width:66px;height:30px;background-color:var(--flag-bool-slider-off);transition:background-color .4s;border-radius:34px;position:relative}.switch .slider:before{content:\"\";position:absolute;left:4px;height:22px;width:22px;background-color:var(--flag-bool-slider-remaining);transition:transform .4s;border-radius:50%}.switch .slider:after{content:\"Off\";color:var(--flag-bool-slider-remaining);font-size:10px;font-family:Verdana,sans-serif;padding-right:0;padding-left:11px}\n"], dependencies: [{ kind: "directive", type: AutofocusDirective, selector: "[focus]", inputs: ["isFocused"] }, { kind: "directive", type: InputDisplayNormalizerDirective, selector: "input[inputDisplayNormalizer]" }, { kind: "component", type: ProductSelectComponent, selector: "app-product-select", inputs: ["valueFormControl", "preSelectedProductId"], outputs: ["componentError"] }, { kind: "component", type: ConfigSelectComponent, selector: "app-config-select", inputs: ["valueFormControl", "productId", "preSelectedConfigId"], outputs: ["componentError"] }, { kind: "component", type: EnvironmentSelectComponent, selector: "app-environment-select", inputs: ["valueFormControl", "productId"], outputs: ["componentError"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.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: i1.NumberValueAccessor, selector: "input[type=number]:not([ngNoCva])[formControlName],input[type=number]:not([ngNoCva])[formControl],input[type=number]:not([ngNoCva])[ngModel]" }, { kind: "directive", type: i1.CheckboxControlValueAccessor, selector: "input[type=checkbox]:not([ngNoCva])[formControlName],input[type=checkbox]:not([ngNoCva])[formControl],input[type=checkbox]:not([ngNoCva])[ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i1.FormGroupName, selector: "[formGroupName]", inputs: ["formGroupName"] }, { kind: "directive", type: i1.FormArrayName, selector: "[formArrayName]", inputs: ["formArrayName"] }, { kind: "ngmodule", type: RxReactiveFormsModule }, { kind: "directive", type: i2.AsyncValidationDirective, selector: "[ngModel],[formControlName],[formControl]", inputs: ["async"] }, { kind: "directive", type: i2.RxwebFormDirective, selector: "[formGroup],[rxwebForm]", inputs: ["formGroup", "rxwebForm"] }, { kind: "directive", type: i2.RxFormControlDirective, selector: "[ngModel],[formControlName],[formControl]", inputs: ["rxalpha", "rxalphaNumeric", "rxascii", "rxcompare", "rxcompose", "rxcontains", "rxcreditCard", "rxdataUri", "rxdifferent", "rxdigit", "rxemail", "rxendsWith", "rxeven", "rxextension", "rxfactor", "rxfileSize", "rxgreaterThanEqualTo", "rxgreaterThan", "rxhexColor", "rxjson", "rxlatitude", "rxlatLong", "rxleapYear", "rxlessThan", "rxlessThanEqualTo", "rxlongitude", "rxlowerCase", "rxmac", "rxmaxDate", "rxmaxLength", "rxmaxNumber", "rxminDate", "rxminLength", "rxminNumber", "rxnumeric", "rxodd", "rxpassword", "rxport", "rxprimeNumber", "rxrequired", "rxrange", "rxrule", "rxstartsWith", "rxtime", "rxupperCase", "rxurl", "rxunique", "rxnotEmpty", "rxcusip", "rxgrid", "rxdate"] }, { kind: "component", type: MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: MatLabel, selector: "mat-label" }, { kind: "component", type: MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: MatOption$1, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: 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: "directive", type: MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "component", type: 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: MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: MatColumnDef, selector: "[matColumnDef]", inputs: ["matColumnDef"] }, { kind: "directive", type: MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "directive", type: MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "component", type: MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "directive", type: MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "component", type: MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "directive", type: MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "component", type: MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: LoaderComponent, selector: "app-loader", inputs: ["skipTimeOut"] }, { kind: "component", type: BoxComponent, selector: "app-box", inputs: ["type", "title"] }, { kind: "component", type: 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"] }], changeDetection: i0.ChangeDetectionStrategy.Eager }); }
8070
8209
  }
8071
8210
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: CreateFeatureFlagComponent, decorators: [{
8072
8211
  type: Component,
@@ -8105,7 +8244,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImpor
8105
8244
  LoaderComponent,
8106
8245
  BoxComponent,
8107
8246
  MatCheckbox,
8108
- ], template: "<div class=\"container\">\n <form class=\"form\" [formGroup]=\"formGroup\" (ngSubmit)=\"create()\">\n <div>\n <div class=\"header\">1. {{ customize().targetSectionHeader }}</div>\n @if (customize().targetSectionDescription) {\n <p>{{ customize().targetSectionDescription }}</p>\n }\n @if (this.presetProductAndConfig()) {\n <p>\n Pre selected product\n <b>{{ this.presetProductAndConfig()!.productName }}</b>\n </p>\n <p>\n Pre selected config\n <b>{{ this.presetProductAndConfig()!.configName }}</b>\n </p>\n }\n <app-product-select\n name=\"productId\"\n [hidden]=\"this.presetProductAndConfig()\"\n [basicAuthUsername]=\"basicAuthUsername()\"\n [basicAuthPassword]=\"basicAuthPassword()\"\n [preSelectedProductId]=\"this.presetProductAndConfig()?.productId\"\n [valueFormControl]=\"formGroup.controls.productId\"\n [customDropdown]=\"true\"\n (componentError)=\"dropdownComponentFailed($event)\" />\n @if (formGroup.controls.productId.value) {\n <app-config-select\n [hidden]=\"this.presetProductAndConfig()\"\n [basicAuthUsername]=\"basicAuthUsername()\"\n [basicAuthPassword]=\"basicAuthPassword()\"\n [preSelectedConfigId]=\"this.presetProductAndConfig()?.configId\"\n [productId]=\"formGroup.controls.productId.value\"\n [valueFormControl]=\"formGroup.controls.configId\"\n [customDropdown]=\"true\"\n (componentError)=\"dropdownComponentFailed($event)\" />\n }\n\n <div class=\"header\">2. {{ customize().flagSectionHeader }}</div>\n @if (customize().flagSectionDescription) {\n <p>{{ customize().flagSectionDescription }}</p>\n }\n @if (loadingComputed()) {\n <app-loader />\n } @else {\n <mat-form-field class=\"form-field\" appearance=\"outline\">\n <mat-label>Type</mat-label>\n <mat-select placeholder=\"Type\" formControlName=\"settingType\" panelClass=\"'custom-dropdown-below'\">\n <mat-option [value]=\"SettingTypeEnum.Boolean\">Feature Flag (bool)</mat-option>\n <mat-option [value]=\"SettingTypeEnum.String\">Text (string)</mat-option>\n <mat-option [value]=\"SettingTypeEnum.Int\">Whole Number (integer)</mat-option>\n <mat-option [value]=\"SettingTypeEnum.Double\">Decimal Number (double)</mat-option>\n </mat-select>\n </mat-form-field>\n <mat-form-field class=\"form-field name\" appearance=\"outline\">\n <mat-label>Name for humans</mat-label>\n <input matInput placeholder=\"My awesome feature\" formControlName=\"name\" />\n <mat-hint>A short name your team will see on the ConfigCat Dashboard.</mat-hint>\n @if (formGroup.controls.name.invalid) {\n <mat-error>\n {{ FormHelper.getErrorMessage(formGroup.controls.name) }}\n </mat-error>\n }\n </mat-form-field>\n <mat-form-field class=\"form-field key\" appearance=\"outline\">\n <mat-label>Key for programs</mat-label>\n <input matInput placeholder=\"isMyAwesomeFeatureEnabled\" formControlName=\"key\" />\n <mat-hint>Your application will use this key to access the setting.</mat-hint>\n @if (formGroup.controls.key.invalid) {\n <mat-error>\n {{ FormHelper.getErrorMessage(formGroup.controls.key) }}\n </mat-error>\n }\n </mat-form-field>\n <mat-form-field class=\"form-field hint last\" appearance=\"outline\">\n @if (!reasonRequired()) {\n <mat-label>Hint (Optional)</mat-label>\n } @else {\n <mat-label>Hint</mat-label>\n }\n <textarea matInput placeholder=\"Hint or description (Optional)\" formControlName=\"hint\"></textarea>\n <mat-hint>Explain what this setting controls. This helps your team use it correctly later.</mat-hint>\n @if (formGroup.controls.hint.invalid) {\n <mat-error>\n {{ FormHelper.getErrorMessage(formGroup.controls.hint) }}\n </mat-error>\n }\n </mat-form-field>\n\n <div class=\"init-value-section\">\n @if (configEvaluationVersion === EvaluationVersion.V2) {\n @if (true && formGroup.controls.settingType.value === SettingTypeEnum.String) {\n <h4>Require valid JSON?</h4>\n <div class=\"centered\">\n <mat-checkbox color=\"primary\" formControlName=\"isJson\">\n <span>Only valid JSON values can be saved</span>\n </mat-checkbox>\n <mat-icon\n color=\"primary\"\n class=\"info-icon\"\n matTooltip=\"Your application will get a string value. You will need to parse it into JSON if necessary.\">\n info\n </mat-icon>\n </div>\n }\n\n <h4>How to set values?</h4>\n <div class=\"value-mode-question\">\n <mat-form-field appearance=\"outline\" class=\"small-form-field value-mode-select\" subscriptSizing=\"dynamic\">\n <mat-select formControlName=\"useVariations\" class=\"choose\">\n <mat-option [value]=\"false\">\n {{\n formGroup.controls.settingType.value === SettingTypeEnum.Boolean\n ? \"use an ON/OFF toggle\"\n : \"enter free-form values\"\n }}\n </mat-option>\n <mat-option [value]=\"true\">choose from predefined variations</mat-option>\n </mat-select>\n </mat-form-field>\n <a href=\"https://configcat.com/docs/advanced/predefined-variations\" target=\"_blank\">\n <mat-icon class=\"info-icon\" color=\"primary\" matTooltip=\"Click to read more about value-modes.\">\n info\n </mat-icon>\n </a>\n </div>\n @if (formGroup.controls.useVariations.value) {\n <p>Define the values team members can choose from. At least two variations are required.</p>\n <div class=\"variations\">\n <div class=\"predef-var-table-scroll\">\n <table mat-table [dataSource]=\"dataSource\">\n <ng-container matColumnDef=\"color\">\n <th *matHeaderCellDef mat-header-cell></th>\n <td *matCellDef=\"let row; let i = index\" mat-cell class=\"row\">\n <span [class]=\"'variation-indicator variation-color-' + getColorIndex(row, i)\"></span>\n </td>\n </ng-container>\n\n <ng-container matColumnDef=\"value\">\n <th *matHeaderCellDef mat-header-cell>\n <div class=\"centered\">\n <span>Served value *</span>\n <mat-icon\n matTooltip=\"Your application will get this value when evaluating the setting.\"\n class=\"info-icon header-tooltip-icon\">\n info\n </mat-icon>\n </div>\n </th>\n <td *matCellDef=\"let row; let i = index\" mat-cell class=\"row align-top\">\n <div class=\"predefined-component\">\n @switch (formGroup.controls.settingType.value) {\n @case (SettingTypeEnum.Boolean) {\n <ng-container\n [ngTemplateOutlet]=\"settingValueTemplate\"\n [ngTemplateOutletContext]=\"{\n formControl: row.controls.value.controls.boolValue,\n setFocus: i === focusIndex,\n }\" />\n }\n @case (SettingTypeEnum.String) {\n <ng-container\n [ngTemplateOutlet]=\"settingValueTemplate\"\n [ngTemplateOutletContext]=\"{\n formControl: row.controls.value.controls.stringValue,\n setFocus: i === focusIndex,\n }\" />\n }\n @case (SettingTypeEnum.Int) {\n <ng-container\n [ngTemplateOutlet]=\"settingValueTemplate\"\n [ngTemplateOutletContext]=\"{\n formControl: row.controls.value.controls.intValue,\n setFocus: i === focusIndex,\n }\" />\n }\n @case (SettingTypeEnum.Double) {\n <ng-container\n [ngTemplateOutlet]=\"settingValueTemplate\"\n [ngTemplateOutletContext]=\"{\n formControl: row.controls.value.controls.doubleValue,\n setFocus: i === focusIndex,\n }\" />\n }\n }\n </div>\n </td>\n </ng-container>\n\n <ng-container matColumnDef=\"name\">\n <th *matHeaderCellDef mat-header-cell>\n <div class=\"centered\">\n <span>Display name (optional)</span>\n <mat-icon\n matTooltip=\"Optional friendly name. This will be displayed on the ConfigCat Dashboard.\"\n class=\"info-icon header-tooltip-icon\">\n info\n </mat-icon>\n </div>\n </th>\n <td *matCellDef=\"let row\" mat-cell class=\"row align-top\">\n <div class=\"predefined-component\">\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\" class=\"small-form-field\">\n <input\n matInput\n [formControl]=\"row.controls.name\"\n [placeholder]=\"row.controls.valueForComparison.value\" />\n @if (row.controls.name.invalid) {\n <mat-error class=\"small-error\">\n {{ FormHelper.getErrorMessage(row.controls.name) }}\n </mat-error>\n }\n </mat-form-field>\n </div>\n </td>\n </ng-container>\n\n <ng-container matColumnDef=\"hint\">\n <th *matHeaderCellDef mat-header-cell>\n <div class=\"centered\">\n <span>Hint (optional)</span>\n <mat-icon\n matTooltip=\"Optional hint. This will be displayed in a tooltip.\"\n class=\"info-icon header-tooltip-icon\">\n info\n </mat-icon>\n </div>\n </th>\n <td *matCellDef=\"let row\" mat-cell class=\"row align-top\">\n <mat-form-field\n appearance=\"outline\"\n subscriptSizing=\"dynamic\"\n class=\"small-form-field suffixed\">\n <button\n matSuffix\n class=\"input-suffix-button\"\n type=\"button\"\n mat-icon-button\n matTooltip=\"Advanced editor\"\n (click)=\"expandHint(row.controls.hint)\">\n <mat-icon class=\"icon-transform-rotate\">edit_note</mat-icon>\n </button>\n <input matInput [formControl]=\"row.controls.hint\" />\n @if (row.controls.hint.invalid) {\n <mat-error>\n {{ FormHelper.getErrorMessage(row.controls.hint) }}\n </mat-error>\n }\n </mat-form-field>\n </td>\n </ng-container>\n <ng-container matColumnDef=\"actions\">\n <th *matHeaderCellDef mat-header-cell>\n <span>{{ dataSource.data.length }} / {{ maxPredefinedVariations }}</span>\n </th>\n <td *matCellDef=\"let row; let i = index\" mat-cell class=\"row align-top\">\n <div\n [matTooltip]=\"\n formGroup.controls.predefinedVariations.controls.length > 2\n ? 'Remove item'\n : 'At least 2 predefined variations should be set.'\n \">\n <button\n mat-icon-button\n type=\"button\"\n [disabled]=\"formGroup.controls.predefinedVariations.controls.length <= 2\"\n (click)=\"removeVariation(i)\">\n <mat-icon>delete</mat-icon>\n </button>\n </div>\n </td>\n </ng-container>\n <tr *matHeaderRowDef=\"displayedColumns\" mat-header-row></tr>\n <tr *matRowDef=\"let row; columns: displayedColumns\" mat-row></tr>\n </table>\n </div>\n\n @if (\n formGroup.controls.predefinedVariations.invalid &&\n !!FormHelper.getErrorMessage(formGroup.controls.predefinedVariations)\n ) {\n <mat-error>\n {{ FormHelper.getErrorMessage(formGroup.controls.predefinedVariations) }}\n </mat-error>\n }\n <div class=\"add-variation\">\n @if (this.formGroup.controls.settingType.value !== SettingTypeEnum.Boolean) {\n <div\n [matTooltip]=\"'Maximum ' + maxPredefinedVariations + ' predefined variations can be added.'\"\n [matTooltipDisabled]=\"dataSource.data.length < maxPredefinedVariations\">\n <button\n type=\"button\"\n mat-stroked-button\n color=\"primary\"\n [disabled]=\"dataSource.data.length >= maxPredefinedVariations\"\n (click)=\"addVariation()\">\n <mat-icon>add</mat-icon>\n Add variation\n </button>\n </div>\n } @else {\n <app-box type=\"info\" class=\"bool-variation-info\">\n For more variations, create a flag of another type.\n <a\n href=\"https://configcat.com/docs/main-concepts/#about-setting-types\"\n target=\"_blank\"\n rel=\"noopener noreferrer\">\n Read more\n </a>\n about setting types.\n </app-box>\n }\n </div>\n </div>\n }\n }\n\n <h4>Initial values</h4>\n <div class=\"initial-value-first-line\">\n The initial value\n <mat-form-field\n appearance=\"outline\"\n class=\"small-form-field initial-value-environment-type\"\n subscriptSizing=\"dynamic\">\n <mat-select formControlName=\"initialValuesPerEnvironment\">\n <mat-option [value]=\"false\">in all environments</mat-option>\n <mat-option [value]=\"true\">per environment</mat-option>\n </mat-select>\n </mat-form-field>\n will be set to\n </div>\n @if (!formGroup.controls.initialValuesPerEnvironment.value) {\n @if (formGroup.controls.useVariations.value) {\n <div class=\"value-field all\">\n <ng-container\n [ngTemplateOutlet]=\"predefinedVariationTemplate\"\n [ngTemplateOutletContext]=\"{ formControl: formGroup.controls.initialValueForAllVariationIndex }\" />\n </div>\n } @else {\n <div class=\"value-field all\">\n <ng-container\n [ngTemplateOutlet]=\"settingValueTemplate\"\n [ngTemplateOutletContext]=\"{ formControl: formGroup.controls.initialValueForAll }\" />\n </div>\n }\n }\n\n @if (formGroup.controls.initialValuesPerEnvironment.value) {\n @for (initialValue of formGroup.controls.initialValues.controls; track $index; let index = $index) {\n <div formArrayName=\"initialValues\">\n <div class=\"value-field\" [formGroupName]=\"index\">\n @if (formGroup.controls.useVariations.value) {\n <ng-container\n [ngTemplateOutlet]=\"predefinedVariationTemplate\"\n [ngTemplateOutletContext]=\"{ formControl: initialValue.controls.predefinedVariationIndex }\" />\n } @else {\n <ng-container\n [ngTemplateOutlet]=\"settingValueTemplate\"\n [ngTemplateOutletContext]=\"{ formControl: initialValue.controls.settingValue }\" />\n }\n <div class=\"environment\">\n in\n <strong [matTooltip]=\"initialValue.controls.environmentName.value\">\n {{ initialValue.controls.environmentName.value }}\n </strong>\n </div>\n </div>\n </div>\n }\n }\n </div>\n }\n\n @if (!customize().hideLinkSection) {\n <div class=\"header\">3. {{ customize().linkSectionHeader }}</div>\n @if (customize().linkSectionDescription) {\n <p>{{ customize().linkSectionDescription }}</p>\n }\n @if (formGroup.controls.productId.value) {\n <app-environment-select\n [basicAuthUsername]=\"basicAuthUsername()\"\n [productId]=\"formGroup.controls.productId.value\"\n [basicAuthPassword]=\"basicAuthPassword()\"\n [valueFormControl]=\"formGroup.controls.environmentId\"\n [customDropdown]=\"true\"\n (componentError)=\"dropdownComponentFailed($event)\" />\n }\n }\n </div>\n @if (formGroup.errors && formGroup.errors[\"serverSide\"]) {\n <div class=\"error\">\n <span>{{ formGroup.errors[\"serverSide\"] }}</span>\n </div>\n }\n <div class=\"buttons\">\n <button\n mat-raised-button\n color=\"primary\"\n type=\"submit\"\n [disabled]=\"submitting() || loadingComputed() || !formGroup.valid\">\n {{ customize().createButtonLabel }}\n </button>\n @if (!customize().hideCancelButton) {\n <button mat-stroked-button type=\"button\" (click)=\"cancel()\">\n {{ customize().cancelButtonLabel }}\n </button>\n }\n </div>\n </form>\n</div>\n\n<ng-template #settingValueTemplate let-formControl=\"formControl\" let-setFocus=\"setFocus\">\n @switch (formGroup.controls.settingType.value) {\n @case (SettingTypeEnum.String) {\n <mat-form-field class=\"small-form-field text suffixed\" appearance=\"outline\" subscriptSizing=\"dynamic\">\n <input\n matInput\n type=\"text\"\n placeholder=\"Add text ...\"\n focus\n [isFocused]=\"setFocus\"\n [formControl]=\"formControl\" />\n <button\n matSuffix\n class=\"input-suffix-button\"\n type=\"button\"\n mat-icon-button\n matTooltip=\"Advanced editor\"\n (click)=\"expandText(formControl)\">\n <mat-icon class=\"icon-transform-rotate\">edit_note</mat-icon>\n </button>\n @if (formControl.invalid) {\n <mat-error class=\"small-error\">\n {{ FormHelper.getErrorMessage(formControl) }}\n </mat-error>\n }\n </mat-form-field>\n }\n @case (SettingTypeEnum.Int) {\n <mat-form-field class=\"small-form-field\" appearance=\"outline\" subscriptSizing=\"dynamic\">\n @if (formControl.invalid) {\n <mat-error class=\"small-error\">\n {{ FormHelper.getErrorMessage(formControl) }}\n </mat-error>\n }\n <input\n matInput\n type=\"number\"\n required\n name=\"index\"\n inputDisplayNormalizer\n focus\n [isFocused]=\"setFocus\"\n [formControl]=\"formControl\" />\n </mat-form-field>\n }\n @case (SettingTypeEnum.Double) {\n <mat-form-field class=\"small-form-field\" appearance=\"outline\" subscriptSizing=\"dynamic\">\n @if (formControl.invalid) {\n <mat-error class=\"small-error\">\n {{ FormHelper.getErrorMessage(formControl) }}\n </mat-error>\n }\n <input\n matInput\n type=\"number\"\n required\n name=\"index\"\n inputDisplayNormalizer\n focus\n [isFocused]=\"setFocus\"\n [formControl]=\"formControl\" />\n </mat-form-field>\n }\n @case (SettingTypeEnum.Boolean) {\n <div class=\"toggle\">\n <label class=\"switch\">\n <input type=\"checkbox\" name=\"index\" [formControl]=\"formControl\" />\n <span class=\"slider\"></span>\n </label>\n </div>\n }\n }\n</ng-template>\n\n<ng-template #predefinedVariationTemplate let-formControl=\"formControl\">\n <mat-form-field class=\"small-form-field\" appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-select [formControl]=\"formControl\">\n @for (predefinedVariation of formGroup.controls.predefinedVariations.controls; let idx = $index; track $index) {\n <mat-option [value]=\"idx\">\n @if (predefinedVariation.controls.name.value) {\n {{ predefinedVariation.controls.name.value }}\n } @else {\n {{ predefinedVariation.controls.valueForComparison.value }}\n }\n </mat-option>\n }\n </mat-select>\n @if (formControl.invalid) {\n <mat-error>\n {{ FormHelper.getErrorMessage(formControl) }}\n </mat-error>\n }\n </mat-form-field>\n</ng-template>\n", styles: [".container{margin:8px 8px 16px}.container .form>*{width:100%}.container .form>* .form-field{width:100%;padding-bottom:8px}.container .form>* .form-field.key{margin-bottom:16px}.container .form>* .form-field.name{margin-bottom:16px}.container .form>* .initial-value-first-line{align-items:center;min-height:34px;margin-bottom:8px}.container .form>* .initial-value-first-line .initial-value-environment-type{margin:0 6px;min-width:160px}.container .form>* .value-field{display:flex;align-items:center;width:100%}.container .form>* .value-field.all{display:inline-flex;margin-left:6px}.container .form>* .value-field.all mat-form-field{min-width:200px}.container .form>* .value-field.all mat-form-field.text{flex-grow:1;width:100%;min-width:100%}.container .form>* .value-field:not(.all){margin-bottom:8px}.container .form>* .value-field:not(.all) mat-form-field{min-width:200px}.container .form>* .value-field:not(.all) mat-form-field.text{min-width:330px}.container .form>* .value-field .environment{margin-left:1em;white-space:nowrap;text-overflow:ellipsis;overflow-x:hidden}.container .form>* .init-value-section{margin-bottom:16px}.container .form>* .init-value-section .predefined-component{padding:8px 0}.container .form>* .init-value-section .small-error{max-width:180px}.container .form>* .init-value-section .readmore{margin-left:8px}.container .form>* .init-value-section .value-mode-question{display:flex;align-items:center}.container .form>* .init-value-section .value-mode-question a{display:flex;align-items:center}.container .form>* .init-value-section .value-mode-question .value-mode-select{min-width:240px}.container .form>* .init-value-section .centered{display:flex;align-items:center}.container .form>* .init-value-section mat-icon.info-icon{color:var(--info-icon-color);margin-left:8px}.container .form>* .init-value-section .variations{display:flex;flex-direction:column}.container .form>* .init-value-section .variations .mat-column-color{padding-right:0!important}.container .form>* .init-value-section .variations .variation-header{margin-bottom:8px}.container .form>* .init-value-section .variations .add-variation{margin-top:12px;display:flex}.container .form>* .init-value-section .variations .toggle{margin-left:4px}.container .form>* .init-value-section .variations .header-tooltip-icon{font-size:14px;width:14px;height:14px}.container .form>* .init-value-section .variations .predef-var-table-scroll{display:block;overflow-x:auto}.container .form>* .init-value-section .variations .predef-var-table-scroll .mat-mdc-table{min-width:100%}.container .error{color:var(--flag-validation-error);margin:0 5px 5px}.container .header{font-size:18px;margin-top:16px;margin-bottom:16px}.container .header.small{font-size:16px;margin-top:16px;margin-bottom:16px}h4{margin-bottom:8px}.buttons{margin-bottom:16px}.buttons>*{margin-right:16px}\n", ".switch{display:inline-flex;align-items:center;width:66px;height:30px}.switch input{display:none}.switch input:checked+.slider{background-color:var(--flag-bool-slider-on)}.switch input:checked+.slider:before{transform:translate(36px)}.switch input:checked+.slider:after{content:\"On\";padding-left:0;padding-right:12px}.switch .slider{display:flex;align-items:center;justify-content:center;cursor:pointer;width:66px;height:30px;background-color:var(--flag-bool-slider-off);transition:background-color .4s;border-radius:34px;position:relative}.switch .slider:before{content:\"\";position:absolute;left:4px;height:22px;width:22px;background-color:var(--flag-bool-slider-remaining);transition:transform .4s;border-radius:50%}.switch .slider:after{content:\"Off\";color:var(--flag-bool-slider-remaining);font-size:10px;font-family:Verdana,sans-serif;padding-right:0;padding-left:11px}\n"] }]
8247
+ ], template: "<div class=\"container\">\n <form class=\"form\" [formGroup]=\"formGroup\" (ngSubmit)=\"create()\">\n <div>\n <div class=\"header\">1. {{ customize().targetSectionHeader }}</div>\n @if (customize().targetSectionDescription) {\n <p>{{ customize().targetSectionDescription }}</p>\n }\n @if (this.presetProductAndConfig()) {\n <p>\n Pre selected product\n <b>{{ this.presetProductAndConfig()!.productName }}</b>\n </p>\n <p>\n Pre selected config\n <b>{{ this.presetProductAndConfig()!.configName }}</b>\n </p>\n }\n <app-product-select\n name=\"productId\"\n [hidden]=\"this.presetProductAndConfig()\"\n [basicAuthUsername]=\"basicAuthUsername()\"\n [basicAuthPassword]=\"basicAuthPassword()\"\n [preSelectedProductId]=\"this.presetProductAndConfig()?.productId\"\n [valueFormControl]=\"formGroup.controls.productId\"\n [customDropdown]=\"true\"\n (componentError)=\"dropdownComponentFailed($event)\" />\n @if (formGroup.controls.productId.value) {\n <app-config-select\n [hidden]=\"this.presetProductAndConfig()\"\n [basicAuthUsername]=\"basicAuthUsername()\"\n [basicAuthPassword]=\"basicAuthPassword()\"\n [preSelectedConfigId]=\"this.presetProductAndConfig()?.configId\"\n [productId]=\"formGroup.controls.productId.value\"\n [valueFormControl]=\"formGroup.controls.configId\"\n [customDropdown]=\"true\"\n (componentError)=\"dropdownComponentFailed($event)\" />\n }\n\n <div class=\"header\">2. {{ customize().flagSectionHeader }}</div>\n @if (customize().flagSectionDescription) {\n <p>{{ customize().flagSectionDescription }}</p>\n }\n @if (loadingComputed()) {\n <app-loader />\n } @else {\n <mat-form-field class=\"form-field\" appearance=\"outline\">\n <mat-label>Type</mat-label>\n <mat-select placeholder=\"Type\" formControlName=\"settingType\" panelClass=\"'custom-dropdown-below'\">\n <mat-option [value]=\"SettingTypeEnum.Boolean\">Feature Flag (bool)</mat-option>\n <mat-option [value]=\"SettingTypeEnum.String\">Text (string)</mat-option>\n <mat-option [value]=\"SettingTypeEnum.Int\">Whole Number (integer)</mat-option>\n <mat-option [value]=\"SettingTypeEnum.Double\">Decimal Number (double)</mat-option>\n </mat-select>\n </mat-form-field>\n <mat-form-field class=\"form-field name\" appearance=\"outline\">\n <mat-label>Name for humans</mat-label>\n <input matInput placeholder=\"My awesome feature\" formControlName=\"name\" />\n <mat-hint>A short name your team will see on the ConfigCat Dashboard.</mat-hint>\n @if (formGroup.controls.name.invalid) {\n <mat-error>\n {{ FormHelper.getErrorMessage(formGroup.controls.name) }}\n </mat-error>\n }\n </mat-form-field>\n <mat-form-field class=\"form-field key\" appearance=\"outline\">\n <mat-label>Key for programs</mat-label>\n <input matInput placeholder=\"isMyAwesomeFeatureEnabled\" formControlName=\"key\" />\n <mat-hint>Your application will use this key to access the setting.</mat-hint>\n @if (formGroup.controls.key.invalid) {\n <mat-error>\n {{ FormHelper.getErrorMessage(formGroup.controls.key) }}\n </mat-error>\n }\n </mat-form-field>\n <mat-form-field class=\"form-field hint last\" appearance=\"outline\">\n @if (!reasonRequired()) {\n <mat-label>Hint (Optional)</mat-label>\n } @else {\n <mat-label>Hint</mat-label>\n }\n <textarea matInput placeholder=\"Hint or description (Optional)\" formControlName=\"hint\"></textarea>\n <mat-hint>Explain what this setting controls. This helps your team use it correctly later.</mat-hint>\n @if (formGroup.controls.hint.invalid) {\n <mat-error>\n {{ FormHelper.getErrorMessage(formGroup.controls.hint) }}\n </mat-error>\n }\n </mat-form-field>\n\n <div class=\"init-value-section\">\n @if (configEvaluationVersion === EvaluationVersion.V2) {\n @if (true && formGroup.controls.settingType.value === SettingTypeEnum.String) {\n <h4>Require valid JSON?</h4>\n <div class=\"centered\">\n <mat-checkbox color=\"primary\" formControlName=\"isJson\">\n <span>Only valid JSON values can be saved</span>\n </mat-checkbox>\n <mat-icon\n color=\"primary\"\n class=\"info-icon\"\n matTooltip=\"Your application will get a string value. You will need to parse it into JSON if necessary.\">\n info\n </mat-icon>\n </div>\n }\n\n <h4>How to set values?</h4>\n <div class=\"value-mode-question\">\n <mat-form-field appearance=\"outline\" class=\"small-form-field value-mode-select\" subscriptSizing=\"dynamic\">\n <mat-select formControlName=\"useVariations\" class=\"choose\">\n <mat-option [value]=\"false\">\n {{\n formGroup.controls.settingType.value === SettingTypeEnum.Boolean\n ? \"use an ON/OFF toggle\"\n : \"enter free-form values\"\n }}\n </mat-option>\n <mat-option [value]=\"true\">choose from predefined variations</mat-option>\n </mat-select>\n </mat-form-field>\n <a href=\"https://configcat.com/docs/advanced/predefined-variations\" target=\"_blank\">\n <mat-icon class=\"info-icon\" color=\"primary\" matTooltip=\"Click to read more about value-modes.\">\n info\n </mat-icon>\n </a>\n </div>\n @if (formGroup.controls.useVariations.value) {\n <p>\n Define the values team members can choose from.\n {{\n formGroup.controls.settingType.value !== SettingTypeEnum.Boolean\n ? \"At least two variations are required.\"\n : null\n }}\n </p>\n <div class=\"variations\">\n <div class=\"predef-var-table-scroll\">\n <table mat-table [dataSource]=\"dataSource\">\n <ng-container matColumnDef=\"color\">\n <th *matHeaderCellDef mat-header-cell></th>\n <td *matCellDef=\"let row; let i = index\" mat-cell class=\"row\">\n <span [class]=\"'variation-indicator variation-color-' + getColorIndex(row, i)\"></span>\n </td>\n </ng-container>\n\n <ng-container matColumnDef=\"value\">\n <th *matHeaderCellDef mat-header-cell>\n <div class=\"centered\">\n <span>Served value *</span>\n <mat-icon\n matTooltip=\"Your application will get this value when evaluating the setting.\"\n class=\"info-icon header-tooltip-icon\">\n info\n </mat-icon>\n </div>\n </th>\n <td *matCellDef=\"let row; let i = index\" mat-cell class=\"row align-top\">\n <div class=\"predefined-component\">\n @switch (formGroup.controls.settingType.value) {\n @case (SettingTypeEnum.Boolean) {\n <ng-container\n [ngTemplateOutlet]=\"settingValueTemplate\"\n [ngTemplateOutletContext]=\"{\n formControl: row.controls.value.controls.boolValue,\n setFocus: i === focusIndex,\n }\" />\n }\n @case (SettingTypeEnum.String) {\n <ng-container\n [ngTemplateOutlet]=\"settingValueTemplate\"\n [ngTemplateOutletContext]=\"{\n formControl: row.controls.value.controls.stringValue,\n setFocus: i === focusIndex,\n }\" />\n }\n @case (SettingTypeEnum.Int) {\n <ng-container\n [ngTemplateOutlet]=\"settingValueTemplate\"\n [ngTemplateOutletContext]=\"{\n formControl: row.controls.value.controls.intValue,\n setFocus: i === focusIndex,\n }\" />\n }\n @case (SettingTypeEnum.Double) {\n <ng-container\n [ngTemplateOutlet]=\"settingValueTemplate\"\n [ngTemplateOutletContext]=\"{\n formControl: row.controls.value.controls.doubleValue,\n setFocus: i === focusIndex,\n }\" />\n }\n }\n </div>\n </td>\n </ng-container>\n\n <ng-container matColumnDef=\"name\">\n <th *matHeaderCellDef mat-header-cell>\n <div class=\"centered\">\n <span>Display name (optional)</span>\n <mat-icon\n matTooltip=\"Optional friendly name. This will be displayed on the ConfigCat Dashboard.\"\n class=\"info-icon header-tooltip-icon\">\n info\n </mat-icon>\n </div>\n </th>\n <td *matCellDef=\"let row\" mat-cell class=\"row align-top\">\n <div class=\"predefined-component\">\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\" class=\"small-form-field\">\n <input\n matInput\n [formControl]=\"row.controls.name\"\n [placeholder]=\"row.controls.valueForComparison.value\" />\n @if (row.controls.name.invalid) {\n <mat-error class=\"small-error\">\n {{ FormHelper.getErrorMessage(row.controls.name) }}\n </mat-error>\n }\n </mat-form-field>\n </div>\n </td>\n </ng-container>\n\n <ng-container matColumnDef=\"hint\">\n <th *matHeaderCellDef mat-header-cell>\n <div class=\"centered\">\n <span>Hint (optional)</span>\n <mat-icon\n matTooltip=\"Optional hint. This will be displayed in a tooltip.\"\n class=\"info-icon header-tooltip-icon\">\n info\n </mat-icon>\n </div>\n </th>\n <td *matCellDef=\"let row\" mat-cell class=\"row align-top\">\n <mat-form-field\n appearance=\"outline\"\n subscriptSizing=\"dynamic\"\n class=\"small-form-field suffixed\">\n <button\n matSuffix\n class=\"input-suffix-button\"\n type=\"button\"\n mat-icon-button\n matTooltip=\"Advanced editor\"\n (click)=\"expandHint(row.controls.hint)\">\n <mat-icon class=\"icon-transform-rotate\">edit_note</mat-icon>\n </button>\n <input matInput [formControl]=\"row.controls.hint\" />\n @if (row.controls.hint.invalid) {\n <mat-error>\n {{ FormHelper.getErrorMessage(row.controls.hint) }}\n </mat-error>\n }\n </mat-form-field>\n </td>\n </ng-container>\n <ng-container matColumnDef=\"actions\">\n <th *matHeaderCellDef mat-header-cell>\n <span>{{ dataSource.data.length }} / {{ maxPredefinedVariations }}</span>\n </th>\n <td *matCellDef=\"let row; let i = index\" mat-cell class=\"row align-top\">\n <div\n [matTooltip]=\"\n formGroup.controls.predefinedVariations.controls.length > 2\n ? 'Remove item'\n : 'At least 2 predefined variations should be set.'\n \">\n <button\n mat-icon-button\n type=\"button\"\n [disabled]=\"formGroup.controls.predefinedVariations.controls.length <= 2\"\n (click)=\"removeVariation(i)\">\n <mat-icon>delete</mat-icon>\n </button>\n </div>\n </td>\n </ng-container>\n <tr *matHeaderRowDef=\"displayedColumns\" mat-header-row></tr>\n <tr *matRowDef=\"let row; columns: displayedColumns\" mat-row></tr>\n </table>\n </div>\n\n @if (\n formGroup.controls.predefinedVariations.invalid &&\n !!FormHelper.getErrorMessage(formGroup.controls.predefinedVariations)\n ) {\n <mat-error>\n {{ FormHelper.getErrorMessage(formGroup.controls.predefinedVariations) }}\n </mat-error>\n }\n <div class=\"add-variation\">\n @if (this.formGroup.controls.settingType.value !== SettingTypeEnum.Boolean) {\n <div\n [matTooltip]=\"'Maximum ' + maxPredefinedVariations + ' predefined variations can be added.'\"\n [matTooltipDisabled]=\"dataSource.data.length < maxPredefinedVariations\">\n <button\n type=\"button\"\n mat-stroked-button\n color=\"primary\"\n [disabled]=\"dataSource.data.length >= maxPredefinedVariations\"\n (click)=\"addVariation()\">\n <mat-icon>add</mat-icon>\n Add variation\n </button>\n </div>\n } @else {\n <app-box type=\"info\" class=\"bool-variation-info\">\n For more variations, create a flag of another type.\n <a\n href=\"https://configcat.com/docs/main-concepts/#about-setting-types\"\n target=\"_blank\"\n rel=\"noopener noreferrer\">\n Read more\n </a>\n about setting types.\n </app-box>\n }\n </div>\n </div>\n }\n }\n\n <h4>Initial values</h4>\n <div class=\"initial-value-first-line\">\n The initial value\n <mat-form-field\n appearance=\"outline\"\n class=\"small-form-field initial-value-environment-type\"\n subscriptSizing=\"dynamic\">\n <mat-select formControlName=\"initialValuesPerEnvironment\">\n <mat-option [value]=\"false\">in all environments</mat-option>\n <mat-option [value]=\"true\">per environment</mat-option>\n </mat-select>\n </mat-form-field>\n will be set to\n </div>\n @if (!formGroup.controls.initialValuesPerEnvironment.value) {\n @if (formGroup.controls.useVariations.value) {\n <div class=\"value-field all\">\n <ng-container\n [ngTemplateOutlet]=\"predefinedVariationTemplate\"\n [ngTemplateOutletContext]=\"{ formControl: formGroup.controls.initialValueForAllVariationIndex }\" />\n </div>\n } @else {\n <div class=\"value-field all\">\n <ng-container\n [ngTemplateOutlet]=\"settingValueTemplate\"\n [ngTemplateOutletContext]=\"{ formControl: formGroup.controls.initialValueForAll }\" />\n </div>\n }\n }\n\n @if (formGroup.controls.initialValuesPerEnvironment.value) {\n @for (initialValue of formGroup.controls.initialValues.controls; track $index; let index = $index) {\n <div formArrayName=\"initialValues\">\n <div class=\"value-field\" [formGroupName]=\"index\">\n @if (formGroup.controls.useVariations.value) {\n <ng-container\n [ngTemplateOutlet]=\"predefinedVariationTemplate\"\n [ngTemplateOutletContext]=\"{ formControl: initialValue.controls.predefinedVariationIndex }\" />\n } @else {\n <ng-container\n [ngTemplateOutlet]=\"settingValueTemplate\"\n [ngTemplateOutletContext]=\"{ formControl: initialValue.controls.settingValue }\" />\n }\n <div class=\"environment\">\n in\n <strong [matTooltip]=\"initialValue.controls.environmentName.value\">\n {{ initialValue.controls.environmentName.value }}\n </strong>\n </div>\n </div>\n </div>\n }\n }\n </div>\n }\n\n @if (!customize().hideLinkSection) {\n <div class=\"header\">3. {{ customize().linkSectionHeader }}</div>\n @if (customize().linkSectionDescription) {\n <p>{{ customize().linkSectionDescription }}</p>\n }\n @if (formGroup.controls.productId.value) {\n <app-environment-select\n [basicAuthUsername]=\"basicAuthUsername()\"\n [productId]=\"formGroup.controls.productId.value\"\n [basicAuthPassword]=\"basicAuthPassword()\"\n [valueFormControl]=\"formGroup.controls.environmentId\"\n [customDropdown]=\"true\"\n (componentError)=\"dropdownComponentFailed($event)\" />\n }\n }\n </div>\n @if (formGroup.errors && formGroup.errors[\"serverSide\"]) {\n <div class=\"error\">\n <span>{{ formGroup.errors[\"serverSide\"] }}</span>\n </div>\n }\n <div class=\"buttons\">\n <button\n mat-raised-button\n color=\"primary\"\n type=\"submit\"\n [disabled]=\"submitting() || loadingComputed() || !formGroup.valid\">\n {{ customize().createButtonLabel }}\n </button>\n @if (!customize().hideCancelButton) {\n <button mat-stroked-button type=\"button\" (click)=\"cancel()\">\n {{ customize().cancelButtonLabel }}\n </button>\n }\n </div>\n </form>\n</div>\n\n<ng-template #settingValueTemplate let-formControl=\"formControl\" let-setFocus=\"setFocus\">\n @switch (formGroup.controls.settingType.value) {\n @case (SettingTypeEnum.String) {\n <mat-form-field class=\"small-form-field text suffixed\" appearance=\"outline\" subscriptSizing=\"dynamic\">\n <input\n matInput\n type=\"text\"\n placeholder=\"Add text ...\"\n focus\n [isFocused]=\"setFocus\"\n [formControl]=\"formControl\" />\n <button\n matSuffix\n class=\"input-suffix-button\"\n type=\"button\"\n mat-icon-button\n matTooltip=\"Advanced editor\"\n (click)=\"expandText(formControl)\">\n <mat-icon class=\"icon-transform-rotate\">edit_note</mat-icon>\n </button>\n @if (formControl.invalid) {\n <mat-error class=\"small-error\">\n {{ FormHelper.getErrorMessage(formControl) }}\n </mat-error>\n }\n </mat-form-field>\n }\n @case (SettingTypeEnum.Int) {\n <mat-form-field class=\"small-form-field\" appearance=\"outline\" subscriptSizing=\"dynamic\">\n @if (formControl.invalid) {\n <mat-error class=\"small-error\">\n {{ FormHelper.getErrorMessage(formControl) }}\n </mat-error>\n }\n <input\n matInput\n type=\"number\"\n required\n name=\"index\"\n inputDisplayNormalizer\n focus\n [isFocused]=\"setFocus\"\n [formControl]=\"formControl\" />\n </mat-form-field>\n }\n @case (SettingTypeEnum.Double) {\n <mat-form-field class=\"small-form-field\" appearance=\"outline\" subscriptSizing=\"dynamic\">\n @if (formControl.invalid) {\n <mat-error class=\"small-error\">\n {{ FormHelper.getErrorMessage(formControl) }}\n </mat-error>\n }\n <input\n matInput\n type=\"number\"\n required\n name=\"index\"\n inputDisplayNormalizer\n focus\n [isFocused]=\"setFocus\"\n [formControl]=\"formControl\" />\n </mat-form-field>\n }\n @case (SettingTypeEnum.Boolean) {\n <div class=\"toggle\">\n <label class=\"switch\">\n <input type=\"checkbox\" name=\"index\" [formControl]=\"formControl\" />\n <span class=\"slider\"></span>\n </label>\n </div>\n }\n }\n</ng-template>\n\n<ng-template #predefinedVariationTemplate let-formControl=\"formControl\">\n <mat-form-field class=\"small-form-field\" appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-select [formControl]=\"formControl\">\n @for (predefinedVariation of formGroup.controls.predefinedVariations.controls; let idx = $index; track $index) {\n <mat-option [value]=\"idx\">\n @if (predefinedVariation.controls.name.value) {\n {{ predefinedVariation.controls.name.value }}\n } @else {\n {{ predefinedVariation.controls.valueForComparison.value }}\n }\n </mat-option>\n }\n </mat-select>\n @if (formControl.invalid) {\n <mat-error>\n {{ FormHelper.getErrorMessage(formControl) }}\n </mat-error>\n }\n </mat-form-field>\n</ng-template>\n", styles: [".container{margin:8px 8px 16px}.container .form>*{width:100%}.container .form>* .form-field{width:100%;padding-bottom:8px}.container .form>* .form-field.key{margin-bottom:16px}.container .form>* .form-field.name{margin-bottom:16px}.container .form>* .initial-value-first-line{align-items:center;min-height:34px;margin-bottom:8px}.container .form>* .initial-value-first-line .initial-value-environment-type{margin:0 6px;min-width:160px}.container .form>* .value-field{display:flex;align-items:center;width:100%}.container .form>* .value-field.all{display:inline-flex;margin-left:6px}.container .form>* .value-field.all mat-form-field{min-width:200px}.container .form>* .value-field.all mat-form-field.text{flex-grow:1;width:100%;min-width:100%}.container .form>* .value-field:not(.all){margin-bottom:8px}.container .form>* .value-field:not(.all) mat-form-field{min-width:200px}.container .form>* .value-field:not(.all) mat-form-field.text{min-width:330px}.container .form>* .value-field .environment{margin-left:1em;white-space:nowrap;text-overflow:ellipsis;overflow-x:hidden}.container .form>* .init-value-section{margin-bottom:16px}.container .form>* .init-value-section .predefined-component{padding:8px 0}.container .form>* .init-value-section .small-error{max-width:180px}.container .form>* .init-value-section .readmore{margin-left:8px}.container .form>* .init-value-section .value-mode-question{display:flex;align-items:center}.container .form>* .init-value-section .value-mode-question a{display:flex;align-items:center}.container .form>* .init-value-section .value-mode-question .value-mode-select{min-width:240px}.container .form>* .init-value-section .centered{display:flex;align-items:center}.container .form>* .init-value-section mat-icon.info-icon{color:var(--info-icon-color);margin-left:8px}.container .form>* .init-value-section .variations{display:flex;flex-direction:column}.container .form>* .init-value-section .variations .mat-column-color{padding-right:0!important}.container .form>* .init-value-section .variations .variation-header{margin-bottom:8px}.container .form>* .init-value-section .variations .add-variation{margin-top:12px;display:flex}.container .form>* .init-value-section .variations .toggle{margin-left:4px}.container .form>* .init-value-section .variations .header-tooltip-icon{font-size:14px;width:14px;height:14px}.container .form>* .init-value-section .variations .predef-var-table-scroll{display:block;overflow-x:auto}.container .form>* .init-value-section .variations .predef-var-table-scroll .mat-mdc-table{min-width:100%}.container .error{color:var(--flag-validation-error);margin:0 5px 5px}.container .header{font-size:18px;margin-top:16px;margin-bottom:16px}.container .header.small{font-size:16px;margin-top:16px;margin-bottom:16px}h4{margin-bottom:8px}.buttons{margin-bottom:16px}.buttons>*{margin-right:16px}\n", ".switch{display:inline-flex;align-items:center;width:66px;height:30px}.switch input{display:none}.switch input:checked+.slider{background-color:var(--flag-bool-slider-on)}.switch input:checked+.slider:before{transform:translate(36px)}.switch input:checked+.slider:after{content:\"On\";padding-left:0;padding-right:12px}.switch .slider{display:flex;align-items:center;justify-content:center;cursor:pointer;width:66px;height:30px;background-color:var(--flag-bool-slider-off);transition:background-color .4s;border-radius:34px;position:relative}.switch .slider:before{content:\"\";position:absolute;left:4px;height:22px;width:22px;background-color:var(--flag-bool-slider-remaining);transition:transform .4s;border-radius:50%}.switch .slider:after{content:\"Off\";color:var(--flag-bool-slider-remaining);font-size:10px;font-family:Verdana,sans-serif;padding-right:0;padding-left:11px}\n"] }]
8109
8248
  }], propDecorators: { customize: [{ type: i0.Input, args: [{ isSignal: true, alias: "customize", required: false }] }], presetProductAndConfig: [{ type: i0.Input, args: [{ isSignal: true, alias: "presetProductAndConfig", required: false }] }], createInitiated: [{ type: i0.Output, args: ["createInitiated"] }], resizeRequested: [{ type: i0.Output, args: ["resizeRequested"] }], cancelInitiated: [{ type: i0.Output, args: ["cancelInitiated"] }], selectDropdownPanelChangedReqested: [{ type: i0.Output, args: ["selectDropdownPanelChangedReqested"] }], componentError: [{ type: i0.Output, args: ["componentError"] }] } });
8110
8249
 
8111
8250
  const DEFAULT_CUSTOMIZE_CREATE_CONFIG = {
@@ -8119,6 +8258,7 @@ class CreateConfigComponent extends BaseComponent {
8119
8258
  constructor() {
8120
8259
  super(...arguments);
8121
8260
  this.formBuilder = inject(NonNullableFormBuilder);
8261
+ this.dialog = inject(MatDialog);
8122
8262
  this.customize = input(DEFAULT_CUSTOMIZE_CREATE_CONFIG, /* @ts-ignore */
8123
8263
  ...(ngDevMode ? [{ debugName: "customize" }] : /* istanbul ignore next */ []));
8124
8264
  this.presetProduct = input(/* @ts-ignore */
@@ -8186,6 +8326,19 @@ class CreateConfigComponent extends BaseComponent {
8186
8326
  if (error instanceof HttpErrorResponse && error.status === 401) {
8187
8327
  this.componentError.emit(error);
8188
8328
  }
8329
+ if (error instanceof HttpErrorResponse && error.status === 402) {
8330
+ //override the form validity and show dialog
8331
+ this.formGroup.updateValueAndValidity();
8332
+ this.dialog.open(RerouteDialogComponent, {
8333
+ data: {
8334
+ title: "You have reached the limits of your plan",
8335
+ body: ' Upgrade your plan or join our <a href="https://configcat.com/referrals" target="_blank" rel="noopener noreferrer">referral program</a> to increase your limits.',
8336
+ yesButtonLabel: "See plans",
8337
+ yesButtonLink: this.getDashboardBasePath() + "/organization/plans",
8338
+ noButtonLabel: "Close",
8339
+ },
8340
+ });
8341
+ }
8189
8342
  },
8190
8343
  });
8191
8344
  }