ng-configcat-publicapi-ui 5.6.5 → 5.6.7

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.
@@ -1,30 +1,31 @@
1
1
  import { OVERLAY_DEFAULT_CONFIG, Overlay } from '@angular/cdk/overlay';
2
2
  import { DatePipe, NgClass, NgTemplateOutlet, SlicePipe, KeyValuePipe } from '@angular/common';
3
+ import { HttpErrorResponse, HttpClient, HTTP_INTERCEPTORS } from '@angular/common/http';
3
4
  import * as i0 from '@angular/core';
4
- import { inject, Injectable, ChangeDetectionStrategy, Component, 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';
5
6
  import { MAT_AUTOCOMPLETE_SCROLL_STRATEGY, MatAutocompleteTrigger, MatAutocomplete } from '@angular/material/autocomplete';
6
7
  import { MAT_BUTTON_TOGGLE_DEFAULT_OPTIONS, MatButtonToggleGroup, MatButtonToggle } from '@angular/material/button-toggle';
7
8
  import { MAT_DATEPICKER_SCROLL_STRATEGY, MatDatepicker, MatDatepickerToggle, MatDatepickerInput } from '@angular/material/datepicker';
8
- import { MatDialogRef, MAT_DIALOG_DATA, MatDialogTitle, MatDialogContent, MatDialogActions, MatDialogClose, MatDialog } from '@angular/material/dialog';
9
+ import { MatDialogContent, MatDialogActions, MatDialogClose, MatDialog, MatDialogRef, MAT_DIALOG_DATA, MatDialogTitle } from '@angular/material/dialog';
9
10
  import { MAT_FORM_FIELD_DEFAULT_OPTIONS, MatFormField, MatLabel, MatError, MatHint, MatSuffix, MatPrefix } from '@angular/material/form-field';
10
11
  import { MAT_MENU_SCROLL_STRATEGY, MatMenuTrigger, MatMenu, MatMenuContent, MatMenuItem } from '@angular/material/menu';
11
12
  import { MAT_SELECT_CONFIG, MAT_SELECT_SCROLL_STRATEGY, MatSelect, MatSelectTrigger, MatOption as MatOption$1 } from '@angular/material/select';
12
13
  import { MAT_TIMEPICKER_SCROLL_STRATEGY, MatTimepicker, MatTimepickerInput, MatTimepickerToggle } from '@angular/material/timepicker';
13
14
  import { MAT_TOOLTIP_DEFAULT_OPTIONS, MAT_TOOLTIP_SCROLL_STRATEGY, MatTooltip } from '@angular/material/tooltip';
14
- import { HttpClient, HttpErrorResponse } from '@angular/common/http';
15
+ import { throwError, timer, Subject, forkJoin, of, tap, catchError as catchError$1, distinctUntilChanged } from 'rxjs';
16
+ import { retry, catchError, map } from 'rxjs/operators';
17
+ import { CdkScrollable } from '@angular/cdk/scrolling';
18
+ import { MatButton, MatIconButton } from '@angular/material/button';
15
19
  import { MeService, Configuration, ProductsService, ConfigsService, EnvironmentsService, FeatureFlagsSettingsService, FeatureFlagSettingValuesV2Service, FeatureFlagSettingValuesService, IntegrationLinksService, SegmentsService, OrganizationsService, ChangeRequestsApprovalFlowScheduledChangesBetaService, UserComparator, SettingType, PrerequisiteComparator, SegmentComparator, RolloutRuleComparator, EvaluationVersion } from 'ng-configcat-publicapi';
16
20
  import * as i1 from '@angular/forms';
17
21
  import { FormArray, Validators, FormGroup, FormControl, FormBuilder, NonNullableFormBuilder, FormsModule, ReactiveFormsModule, NG_VALUE_ACCESSOR, NG_VALIDATORS } from '@angular/forms';
18
22
  import { NativeDateAdapter, MatOption, DateAdapter, MatRipple, MatOptgroup } from '@angular/material/core';
19
- import { CdkScrollable } from '@angular/cdk/scrolling';
20
- import { MatButton, MatIconButton } from '@angular/material/button';
23
+ import { DomSanitizer } from '@angular/platform-browser';
21
24
  import { MatIcon } from '@angular/material/icon';
22
25
  import { MatSnackBarRef, MAT_SNACK_BAR_DATA, MatSnackBarAction, MatSnackBarActions, MatSnackBarLabel, MatSnackBar } from '@angular/material/snack-bar';
23
26
  import { takeUntilDestroyed, rxResource } from '@angular/core/rxjs-interop';
24
- import { timer, Subject, forkJoin, of, tap, catchError as catchError$1, distinctUntilChanged, throwError } from 'rxjs';
25
27
  import Papa from 'papaparse';
26
28
  import { valid } from 'semver';
27
- import { map, catchError } from 'rxjs/operators';
28
29
  import * as i2 from '@rxweb/reactive-form-validators';
29
30
  import { RxReactiveFormsModule } from '@rxweb/reactive-form-validators';
30
31
  import { Router } from '@angular/router';
@@ -45,6 +46,55 @@ import { ENTER, COMMA } from '@angular/cdk/keycodes';
45
46
  import { MatRadioGroup, MatRadioButton } from '@angular/material/radio';
46
47
  import { MatPaginator } from '@angular/material/paginator';
47
48
 
49
+ class RateLimitDialogComponent {
50
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: RateLimitDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
51
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.1", type: RateLimitDialogComponent, isStandalone: true, selector: "app-rate-limit-dialog", ngImport: i0, template: "<div mat-dialog-content class=\"content\">\n <p>You've reached the rate limit of this operation, please try again in a few minutes.</p>\n <p>\n If this issue persists, please\n <a href=\"https://configcat.com/support\" target=\"_blank\" rel=\"noopener noreferrer\">tell us what happened.</a>\n </p>\n</div>\n<div class=\"buttons\" mat-dialog-actions>\n <button type=\"button\" mat-button mat-flat-button color=\"primary\" [mat-dialog-close]>Close</button>\n</div>\n", styles: [".content{display:flex;flex-direction:column}\n"], dependencies: [{ 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: "directive", type: MatDialogClose, selector: "[mat-dialog-close], [matDialogClose]", inputs: ["aria-label", "type", "mat-dialog-close", "matDialogClose"], exportAs: ["matDialogClose"] }], changeDetection: i0.ChangeDetectionStrategy.Eager }); }
52
+ }
53
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: RateLimitDialogComponent, decorators: [{
54
+ type: Component,
55
+ args: [{ selector: "app-rate-limit-dialog", changeDetection: ChangeDetectionStrategy.Eager, imports: [CdkScrollable, MatDialogContent, MatDialogActions, MatButton, MatDialogClose], template: "<div mat-dialog-content class=\"content\">\n <p>You've reached the rate limit of this operation, please try again in a few minutes.</p>\n <p>\n If this issue persists, please\n <a href=\"https://configcat.com/support\" target=\"_blank\" rel=\"noopener noreferrer\">tell us what happened.</a>\n </p>\n</div>\n<div class=\"buttons\" mat-dialog-actions>\n <button type=\"button\" mat-button mat-flat-button color=\"primary\" [mat-dialog-close]>Close</button>\n</div>\n", styles: [".content{display:flex;flex-direction:column}\n"] }]
56
+ }] });
57
+
58
+ const MAX_RETRIES = 2;
59
+ class TooManyRequestInterceptor {
60
+ constructor() {
61
+ this.dialog = inject(MatDialog);
62
+ }
63
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
64
+ intercept(req, next) {
65
+ return next.handle(req).pipe(retry({
66
+ count: MAX_RETRIES,
67
+ delay: (error) => {
68
+ if (!(error instanceof HttpErrorResponse) || error.status !== 429) {
69
+ return throwError(() => error);
70
+ }
71
+ const retryAfter = error.headers.get("Retry-After");
72
+ // Retry-After is either delta-seconds or an HTTP date
73
+ const delayMs = retryAfter
74
+ ? isNaN(Number(retryAfter))
75
+ ? Math.max(0, new Date(retryAfter).getTime() - Date.now())
76
+ : parseInt(retryAfter, 10) * 1000
77
+ : 0;
78
+ if (delayMs > 2000) {
79
+ return throwError(() => error);
80
+ }
81
+ return timer(delayMs);
82
+ },
83
+ }), catchError((error) => {
84
+ if (!(error instanceof HttpErrorResponse) || error.status !== 429) {
85
+ return throwError(() => error);
86
+ }
87
+ this.dialog.open(RateLimitDialogComponent, { width: "400px" });
88
+ return throwError(() => error);
89
+ }));
90
+ }
91
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: TooManyRequestInterceptor, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
92
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: TooManyRequestInterceptor }); }
93
+ }
94
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: TooManyRequestInterceptor, decorators: [{
95
+ type: Injectable
96
+ }] });
97
+
48
98
  const defaultPublicApiBasePath = "https://api.configcat.com";
49
99
  const defaultDashboardBasePath = "https://app.configcat.com";
50
100
  class PublicApiService {
@@ -128,9 +178,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImpor
128
178
  type: Injectable
129
179
  }] });
130
180
 
131
- const percentageRegex = "^[0-9]*$";
132
- const wholeNumberRegex = "^[-+]?[0-9]\\d*$";
133
- const featureFlagKeyRegex = "^[a-zA-Z]+[a-zA-Z0-9_-]*$";
181
+ // NOTE: In the following cases, we can't directly use `RegExp` objects because we need to check against them
182
+ // in `getErrorMessage`, which only works for strings.
183
+ const percentageRegex = /^\d{1,3}$/.source;
184
+ const wholeNumberRegex = /^[-+]?\d+$/.source;
185
+ const featureFlagKeyRegex = /^[a-zA-Z][a-zA-Z0-9_-]*$/.source;
134
186
  const defaultErrorMessageTypes = [
135
187
  "email",
136
188
  "compare",
@@ -279,7 +331,7 @@ class FormHelper {
279
331
  return "The percentage value must be a whole number.";
280
332
  }
281
333
  if (patternError.requiredPattern === wholeNumberRegex) {
282
- return "Please enter a whole number e.g. 42.";
334
+ return "Please enter a whole number (e.g., 42).";
283
335
  }
284
336
  if (patternError.requiredPattern === featureFlagKeyRegex) {
285
337
  return "Invalid key. Keys must start with a letter, followed by a combination of numbers, letters, underscores and hyphens.";
@@ -435,6 +487,12 @@ class ConfirmationDialogComponent {
435
487
  constructor() {
436
488
  this.dialogRef = inject(MatDialogRef);
437
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);
438
496
  }
439
497
  yes() {
440
498
  this.dialogRef.close("yes");
@@ -443,17 +501,23 @@ class ConfirmationDialogComponent {
443
501
  this.dialogRef.close("cancel");
444
502
  }
445
503
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: ConfirmationDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
446
- 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 }); }
447
505
  }
448
506
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: ConfirmationDialogComponent, decorators: [{
449
507
  type: Component,
450
- 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" }]
451
- }] });
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: () => [] });
452
510
 
453
511
  class RerouteDialogComponent {
454
512
  constructor() {
455
513
  this.dialogRef = inject(MatDialogRef);
456
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);
457
521
  }
458
522
  yes() {
459
523
  this.dialogRef.close("yes");
@@ -462,20 +526,25 @@ class RerouteDialogComponent {
462
526
  this.dialogRef.close("cancel");
463
527
  }
464
528
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: RerouteDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
465
- 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 }); }
466
530
  }
467
531
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: RerouteDialogComponent, decorators: [{
468
532
  type: Component,
469
- 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"] }]
470
- }] });
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: () => [] });
471
535
 
472
536
  class InfoDialogComponent {
473
537
  constructor() {
474
538
  this.dialogRef = inject(MatDialogRef);
475
539
  this.data = inject(MAT_DIALOG_DATA);
476
- const data = this.data;
477
- if (data.iconName) {
478
- 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("_", "-");
479
548
  }
480
549
  }
481
550
  ok() {
@@ -486,11 +555,11 @@ class InfoDialogComponent {
486
555
  this.dialogRef.close("nok");
487
556
  }
488
557
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: InfoDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
489
- 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 }); }
490
559
  }
491
560
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: InfoDialogComponent, decorators: [{
492
561
  type: Component,
493
- 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"] }]
494
563
  }], ctorParameters: () => [] });
495
564
 
496
565
  var NotificationSnackColor;
@@ -504,10 +573,16 @@ class NotificationSnackComponent {
504
573
  constructor() {
505
574
  this.snackBarRef = inject(MatSnackBarRef);
506
575
  this.data = inject(MAT_SNACK_BAR_DATA);
576
+ this.sanitizer = inject(DomSanitizer);
577
+ this.destroyRef = inject(DestroyRef);
507
578
  this.NotificationSnackColor = NotificationSnackColor;
508
579
  this.progressBar = viewChild.required("progressBar", /* @ts-ignore */
509
580
  ...(ngDevMode ? [{ debugName: "progressBar" }] : /* istanbul ignore next */ []));
510
- 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);
511
586
  this.snackBarRef
512
587
  .afterOpened()
513
588
  .pipe(takeUntilDestroyed(this.destroyRef))
@@ -531,41 +606,142 @@ class NotificationSnackComponent {
531
606
  });
532
607
  }
533
608
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: NotificationSnackComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
534
- 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 }); }
535
610
  }
536
611
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: NotificationSnackComponent, decorators: [{
537
612
  type: Component,
538
- 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"] }]
539
614
  }], ctorParameters: () => [], propDecorators: { progressBar: [{ type: i0.ViewChild, args: ["progressBar", { isSignal: true }] }] } });
540
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
+
541
696
  class MessagingService {
542
697
  constructor() {
543
698
  this.snackBar = inject(MatSnackBar);
544
699
  }
545
- 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
+ }
546
710
  this.snackBar.openFromComponent(NotificationSnackComponent, {
547
711
  duration: durationMs,
548
712
  data: {
549
713
  color: NotificationSnackColor.Red,
550
- message: `Ooops. ${message} Please try again or contact us.`,
714
+ message,
551
715
  },
552
716
  });
553
717
  }
718
+ /** @param message Either a plain `string` that will be fully HTML escaped or a `SafeHtmlString` whose dynamic values will be HTML escaped. */
554
719
  showSuccessSnack(message = "Success.", durationMs = 2500) {
555
720
  this.snackBar.openFromComponent(NotificationSnackComponent, {
556
721
  duration: durationMs,
557
722
  data: {
558
723
  color: NotificationSnackColor.Green,
559
- message: message,
724
+ message,
560
725
  },
561
726
  });
562
727
  }
563
- 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) {
564
730
  this.snackBar.openFromComponent(NotificationSnackComponent, {
565
731
  duration: durationMs,
566
732
  data: {
567
733
  color: NotificationSnackColor.Yellow,
568
- 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,
569
745
  },
570
746
  });
571
747
  }
@@ -841,8 +1017,8 @@ function generatePercentageOptionFormGroup(model, settingType, predefinedVariati
841
1017
  percentage: new FormControl({ value: model.percentage, disabled: readOnly }, {
842
1018
  nonNullable: true,
843
1019
  validators: [
844
- validateNumberNan,
845
1020
  Validators.required,
1021
+ validateNumberNan,
846
1022
  Validators.min(0),
847
1023
  Validators.max(100),
848
1024
  Validators.pattern(percentageRegex),
@@ -965,8 +1141,8 @@ function generateComparisonValueFormGroup(value, comparator, readOnly, featureFl
965
1141
  doubleValue: new FormControl({ value: value.doubleValue ?? 0, disabled: readOnly }, {
966
1142
  nonNullable: true,
967
1143
  validators: [
968
- validateNumberNan,
969
1144
  Validators.required,
1145
+ validateNumberNan,
970
1146
  Validators.min(-Number.MAX_VALUE),
971
1147
  Validators.max(Number.MAX_VALUE),
972
1148
  ],
@@ -1127,16 +1303,16 @@ function getValidatorsForSettingType(settingType, featureFlagLimitations, isJson
1127
1303
  switch (settingType) {
1128
1304
  case SettingType.Int:
1129
1305
  return [
1130
- validateNumberNan,
1131
1306
  Validators.required,
1307
+ validateNumberNan,
1132
1308
  Validators.min(-2147483648),
1133
1309
  Validators.max(2147483647),
1134
1310
  Validators.pattern(wholeNumberRegex),
1135
1311
  ];
1136
1312
  case SettingType.Double:
1137
1313
  return [
1138
- validateNumberNan,
1139
1314
  Validators.required,
1315
+ validateNumberNan,
1140
1316
  Validators.min(-Number.MAX_VALUE),
1141
1317
  Validators.max(Number.MAX_VALUE),
1142
1318
  ];
@@ -1326,6 +1502,7 @@ class SettingListFormHelper {
1326
1502
  nonNullable: true,
1327
1503
  validators: [
1328
1504
  Validators.required,
1505
+ validateNumberNan,
1329
1506
  Validators.min(0),
1330
1507
  Validators.max(100),
1331
1508
  Validators.pattern(percentageRegex),
@@ -1345,6 +1522,7 @@ class SettingListFormHelper {
1345
1522
  nonNullable: true,
1346
1523
  validators: [
1347
1524
  Validators.required,
1525
+ validateNumberNan,
1348
1526
  Validators.min(0),
1349
1527
  Validators.max(100),
1350
1528
  Validators.pattern(percentageRegex),
@@ -1385,6 +1563,7 @@ class SettingListFormHelper {
1385
1563
  case SettingType.Int:
1386
1564
  return [
1387
1565
  Validators.required,
1566
+ validateNumberNan,
1388
1567
  Validators.min(-2147483648),
1389
1568
  Validators.max(2147483647),
1390
1569
  Validators.pattern(wholeNumberRegex),
@@ -1536,6 +1715,11 @@ function provideConfigCatPublicApiUi() {
1536
1715
  provide: MAT_BUTTON_TOGGLE_DEFAULT_OPTIONS,
1537
1716
  useValue: { hideSingleSelectionIndicator: true, hideMultipleSelectionIndicator: true },
1538
1717
  },
1718
+ {
1719
+ provide: HTTP_INTERCEPTORS,
1720
+ useClass: TooManyRequestInterceptor,
1721
+ multi: true,
1722
+ },
1539
1723
  { provide: DatePipe },
1540
1724
  AppDateAdapter,
1541
1725
  MessagingService,
@@ -1718,250 +1902,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImpor
1718
1902
  ], template: "<h1 mat-dialog-title>Add notes</h1>\n<form [formGroup]=\"formGroup\" (ngSubmit)=\"handleSaveButton()\">\n <div mat-dialog-content class=\"container\">\n <p>This will appear in the Audit Log (in the Notes section when you expand the corresponding entry).</p>\n <mat-form-field appearance=\"outline\">\n <mat-label>Note</mat-label>\n <textarea\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 @if (formGroup.controls.reason.invalid) {\n <mat-error>The field is required.</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\">Add notes & Save</button>\n <button type=\"button\" mat-stroked-button (click)=\"cancel()\">Cancel</button>\n </div>\n</form>\n", styles: [".container>*{width:100%}\n"] }]
1719
1903
  }] });
1720
1904
 
1721
- /* eslint-disable @typescript-eslint/no-explicit-any */
1722
- class DigitOnlyDirective {
1723
- constructor() {
1724
- this.el = inject(ElementRef);
1725
- this.document = inject(DOCUMENT);
1726
- this.hasDecimalPoint = false;
1727
- this.hasNegativeSign = false;
1728
- this.navigationKeys = [
1729
- "Backspace",
1730
- "Delete",
1731
- "Tab",
1732
- "Escape",
1733
- "Enter",
1734
- "Home",
1735
- "End",
1736
- "ArrowLeft",
1737
- "ArrowRight",
1738
- "Clear",
1739
- "Copy",
1740
- "Paste",
1741
- ];
1742
- this.digitOnlyDecimal = input(false, /* @ts-ignore */
1743
- ...(ngDevMode ? [{ debugName: "digitOnlyDecimal" }] : /* istanbul ignore next */ []));
1744
- this.digitOnlyDecimalSeparator = input(".", /* @ts-ignore */
1745
- ...(ngDevMode ? [{ debugName: "digitOnlyDecimalSeparator" }] : /* istanbul ignore next */ []));
1746
- this.digitOnlyAllowNegatives = input(false, /* @ts-ignore */
1747
- ...(ngDevMode ? [{ debugName: "digitOnlyAllowNegatives" }] : /* istanbul ignore next */ []));
1748
- this.digitOnlyAllowPaste = input(true, /* @ts-ignore */
1749
- ...(ngDevMode ? [{ debugName: "digitOnlyAllowPaste" }] : /* istanbul ignore next */ []));
1750
- this.digitOnlyNegativeSign = input("-", /* @ts-ignore */
1751
- ...(ngDevMode ? [{ debugName: "digitOnlyNegativeSign" }] : /* istanbul ignore next */ []));
1752
- this.digitOnlyMin = -Infinity;
1753
- this.digitOnlyMax = Infinity;
1754
- const el = this.el;
1755
- this.inputElement = el.nativeElement;
1756
- }
1757
- onBeforeInput(e) {
1758
- if (isNaN(Number(e.data))) {
1759
- if (e.data === this.digitOnlyDecimalSeparator()
1760
- || (e.data === this.digitOnlyNegativeSign() && this.digitOnlyAllowNegatives())) {
1761
- return; // go on
1762
- }
1763
- e.preventDefault();
1764
- e.stopPropagation();
1765
- }
1766
- }
1767
- onKeyDown(e) {
1768
- if (this.navigationKeys.includes(e.key) // Allow: navigation keys: backspace, delete, arrows etc.
1769
- || ((e.key === "a" || e.code === "KeyA") && e.ctrlKey) // Allow: Ctrl+A
1770
- || ((e.key === "c" || e.code === "KeyC") && e.ctrlKey) // Allow: Ctrl+C
1771
- || ((e.key === "v" || e.code === "KeyV") && e.ctrlKey) // Allow: Ctrl+V
1772
- || ((e.key === "x" || e.code === "KeyX") && e.ctrlKey) // Allow: Ctrl+X
1773
- || ((e.key === "a" || e.code === "KeyA") && e.metaKey) // Allow: Cmd+A (Mac)
1774
- || ((e.key === "c" || e.code === "KeyC") && e.metaKey) // Allow: Cmd+C (Mac)
1775
- || ((e.key === "v" || e.code === "KeyV") && e.metaKey) // Allow: Cmd+V (Mac)
1776
- || ((e.key === "x" || e.code === "KeyX") && e.metaKey) // Allow: Cmd+X (Mac)
1777
- ) {
1778
- // let it happen, don't do anything
1779
- return;
1780
- }
1781
- let newValue = "";
1782
- const digitOnlyDecimalSeparator = this.digitOnlyDecimalSeparator();
1783
- if (this.digitOnlyDecimal() && e.key === digitOnlyDecimalSeparator) {
1784
- newValue = this.forecastValue(e.key);
1785
- if (newValue.split(digitOnlyDecimalSeparator).length > 2) {
1786
- // has two or more decimal points
1787
- e.preventDefault();
1788
- return;
1789
- }
1790
- else {
1791
- this.hasDecimalPoint = newValue.includes(digitOnlyDecimalSeparator);
1792
- return; // Allow: only one decimal point
1793
- }
1794
- }
1795
- const digitOnlyNegativeSign = this.digitOnlyNegativeSign();
1796
- if (e.key === digitOnlyNegativeSign && this.digitOnlyAllowNegatives()) {
1797
- newValue = this.forecastValue(e.key);
1798
- if (!newValue.startsWith(digitOnlyNegativeSign) || newValue.split(digitOnlyNegativeSign).length > 2) {
1799
- e.preventDefault();
1800
- return;
1801
- }
1802
- else {
1803
- this.hasNegativeSign = newValue.split(digitOnlyNegativeSign).length > -1;
1804
- return;
1805
- }
1806
- }
1807
- // Ensure that it is a number and stop the keypress
1808
- if (e.key === " " || isNaN(Number(e.key))) {
1809
- e.preventDefault();
1810
- return;
1811
- }
1812
- newValue = newValue || this.forecastValue(e.key);
1813
- const newNumber = Number(newValue);
1814
- if (newNumber > this.digitOnlyMax || newNumber < this.digitOnlyMin) {
1815
- e.preventDefault();
1816
- }
1817
- }
1818
- onPaste(event) {
1819
- if (this.digitOnlyAllowPaste()) {
1820
- let pastedInput = "";
1821
- if (this.document.defaultView.clipboardData) {
1822
- // Browser is IE
1823
- pastedInput = this.document.defaultView.clipboardData.getData("text");
1824
- }
1825
- else if (event.clipboardData?.getData) {
1826
- // Other browsers
1827
- pastedInput = event.clipboardData.getData("text/plain");
1828
- }
1829
- this.pasteData(pastedInput);
1830
- event.preventDefault();
1831
- }
1832
- else {
1833
- // this prevents the paste
1834
- event.preventDefault();
1835
- event.stopPropagation();
1836
- }
1837
- }
1838
- onDrop(event) {
1839
- const textData = event.dataTransfer?.getData("text") ?? "";
1840
- this.inputElement.focus();
1841
- this.pasteData(textData);
1842
- event.preventDefault();
1843
- }
1844
- pasteData(pastedContent) {
1845
- const sanitizedContent = this.sanitizeInput(pastedContent);
1846
- const digitOnlyNegativeSign = this.digitOnlyNegativeSign();
1847
- if (sanitizedContent.includes(digitOnlyNegativeSign)
1848
- && this.hasNegativeSign
1849
- && !this.getSelection().includes(digitOnlyNegativeSign)) {
1850
- return;
1851
- }
1852
- // eslint-disable-next-line @typescript-eslint/no-deprecated, sonarjs/deprecation
1853
- const pasted = document.execCommand("insertText", false, sanitizedContent);
1854
- if (!pasted) {
1855
- if (this.inputElement.setRangeText) {
1856
- const { selectionStart: start, selectionEnd: end } = this.inputElement;
1857
- this.inputElement.setRangeText(sanitizedContent, start ?? 0, end ?? 0, "end");
1858
- // Angular's Reactive Form relies on "input" event, but on Firefox, the setRangeText method doesn't trigger it
1859
- // so we have to trigger it ourself.
1860
- if (typeof window["InstallTrigger"] !== "undefined") {
1861
- this.inputElement.dispatchEvent(new Event("input", { cancelable: true }));
1862
- }
1863
- }
1864
- else {
1865
- // Browser does not support setRangeText, e.g. IE
1866
- this.insertAtCursor(this.inputElement, sanitizedContent);
1867
- }
1868
- }
1869
- if (this.digitOnlyDecimal()) {
1870
- this.hasDecimalPoint = this.inputElement.value.includes(this.digitOnlyDecimalSeparator());
1871
- }
1872
- this.hasNegativeSign = this.inputElement.value.includes(digitOnlyNegativeSign);
1873
- }
1874
- // The following 2 methods were added from the below article for browsers that do not support setRangeText
1875
- // https://stackoverflow.com/questions/11076975/how-to-insert-text-into-the-textarea-at-the-current-cursor-position
1876
- insertAtCursor(myField, myValue) {
1877
- const startPos = myField.selectionStart ?? 0;
1878
- const endPos = myField.selectionEnd ?? 0;
1879
- myField.value =
1880
- myField.value.substring(0, startPos) + myValue + myField.value.substring(endPos, myField.value.length);
1881
- const pos = startPos + myValue.length;
1882
- myField.focus();
1883
- myField.setSelectionRange(pos, pos);
1884
- this.triggerEvent(myField, "input");
1885
- }
1886
- triggerEvent(el, type) {
1887
- if ("createEvent" in document) {
1888
- // modern browsers, IE9+
1889
- const e = document.createEvent("HTMLEvents");
1890
- // eslint-disable-next-line @typescript-eslint/no-deprecated, sonarjs/deprecation
1891
- e.initEvent(type, false, true);
1892
- el.dispatchEvent(e);
1893
- }
1894
- }
1895
- // end stack overflow code
1896
- sanitizeInput(input) {
1897
- let result = "";
1898
- let regex;
1899
- if (this.digitOnlyDecimal() && this.isValidDecimal(input)) {
1900
- regex = new RegExp(`${this.getNegativeSignRegExp()}[^0-9${this.digitOnlyDecimalSeparator()}]`, "g");
1901
- }
1902
- else {
1903
- regex = new RegExp(`${this.getNegativeSignRegExp()}[^0-9]`, "g");
1904
- }
1905
- result = input.replace(regex, "");
1906
- const maxLength = this.inputElement.maxLength;
1907
- if (maxLength > 0) {
1908
- // the input element has maxLength limit
1909
- const allowedLength = maxLength - this.inputElement.value.length + (result.includes(this.digitOnlyNegativeSign()) ? 1 : 0);
1910
- result = allowedLength > 0 ? result.substring(0, allowedLength) : "";
1911
- }
1912
- return result;
1913
- }
1914
- getNegativeSignRegExp() {
1915
- const digitOnlyNegativeSign = this.digitOnlyNegativeSign();
1916
- return this.digitOnlyAllowNegatives()
1917
- && (!this.hasNegativeSign || this.getSelection().includes(digitOnlyNegativeSign))
1918
- ? `(?!^${digitOnlyNegativeSign})`
1919
- : "";
1920
- }
1921
- isValidDecimal(string) {
1922
- if (!this.hasDecimalPoint) {
1923
- return string.split(this.digitOnlyDecimalSeparator()).length <= 2;
1924
- }
1925
- else {
1926
- // the input element already has a decimal separator
1927
- const selectedText = this.getSelection();
1928
- if (selectedText.includes(this.digitOnlyDecimalSeparator())) {
1929
- return string.split(this.digitOnlyDecimalSeparator()).length <= 2;
1930
- }
1931
- else {
1932
- return !string.includes(this.digitOnlyDecimalSeparator());
1933
- }
1934
- }
1935
- }
1936
- getSelection() {
1937
- return this.inputElement.value.substring(this.inputElement.selectionStart ?? 0, this.inputElement.selectionEnd ?? 0);
1938
- }
1939
- forecastValue(key) {
1940
- const selectionStart = this.inputElement.selectionStart ?? 0;
1941
- const selectionEnd = this.inputElement.selectionEnd ?? 0;
1942
- const oldValue = this.inputElement.value;
1943
- return oldValue.substring(0, selectionStart) + key + oldValue.substring(selectionEnd);
1944
- }
1945
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: DigitOnlyDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1946
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.1", type: DigitOnlyDirective, isStandalone: true, selector: "[appDigitOnly]", inputs: { digitOnlyDecimal: { classPropertyName: "digitOnlyDecimal", publicName: "digitOnlyDecimal", isSignal: true, isRequired: false, transformFunction: null }, digitOnlyDecimalSeparator: { classPropertyName: "digitOnlyDecimalSeparator", publicName: "digitOnlyDecimalSeparator", isSignal: true, isRequired: false, transformFunction: null }, digitOnlyAllowNegatives: { classPropertyName: "digitOnlyAllowNegatives", publicName: "digitOnlyAllowNegatives", isSignal: true, isRequired: false, transformFunction: null }, digitOnlyAllowPaste: { classPropertyName: "digitOnlyAllowPaste", publicName: "digitOnlyAllowPaste", isSignal: true, isRequired: false, transformFunction: null }, digitOnlyNegativeSign: { classPropertyName: "digitOnlyNegativeSign", publicName: "digitOnlyNegativeSign", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "beforeinput": "onBeforeInput($event)", "keydown": "onKeyDown($event)", "paste": "onPaste($event)", "drop": "onDrop($event)" } }, ngImport: i0 }); }
1947
- }
1948
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: DigitOnlyDirective, decorators: [{
1949
- type: Directive,
1950
- args: [{ selector: "[appDigitOnly]" }]
1951
- }], ctorParameters: () => [], propDecorators: { digitOnlyDecimal: [{ type: i0.Input, args: [{ isSignal: true, alias: "digitOnlyDecimal", required: false }] }], digitOnlyDecimalSeparator: [{ type: i0.Input, args: [{ isSignal: true, alias: "digitOnlyDecimalSeparator", required: false }] }], digitOnlyAllowNegatives: [{ type: i0.Input, args: [{ isSignal: true, alias: "digitOnlyAllowNegatives", required: false }] }], digitOnlyAllowPaste: [{ type: i0.Input, args: [{ isSignal: true, alias: "digitOnlyAllowPaste", required: false }] }], digitOnlyNegativeSign: [{ type: i0.Input, args: [{ isSignal: true, alias: "digitOnlyNegativeSign", required: false }] }], onBeforeInput: [{
1952
- type: HostListener,
1953
- args: ["beforeinput", ["$event"]]
1954
- }], onKeyDown: [{
1955
- type: HostListener,
1956
- args: ["keydown", ["$event"]]
1957
- }], onPaste: [{
1958
- type: HostListener,
1959
- args: ["paste", ["$event"]]
1960
- }], onDrop: [{
1961
- type: HostListener,
1962
- args: ["drop", ["$event"]]
1963
- }] } });
1964
-
1965
1905
  class AutofocusDirective {
1966
1906
  constructor() {
1967
1907
  this.host = inject((ElementRef));
@@ -2222,7 +2162,7 @@ class ValueComponent {
2222
2162
  });
2223
2163
  }
2224
2164
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: ValueComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2225
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: ValueComponent, isStandalone: true, selector: "app-value", inputs: { valueFormControl: { classPropertyName: "valueFormControl", publicName: "valueFormControl", isSignal: true, isRequired: true, transformFunction: null }, setting: { classPropertyName: "setting", publicName: "setting", isSignal: true, isRequired: true, transformFunction: null }, featureFlagLimitations: { classPropertyName: "featureFlagLimitations", publicName: "featureFlagLimitations", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<div class=\"flex\">\n <div class=\"container\">\n @switch (setting().settingType) {\n @case (SettingType.String) {\n <div class=\"with-validation\">\n @if (valueFormControl().invalid) {\n <span class=\"error\">*</span>\n }\n <input\n type=\"text\"\n placeholder=\"my value\"\n name=\"value\"\n [ngClass]=\"{ invalid: valueFormControl().invalid }\"\n [formControl]=\"valueFormControl()\" />\n <div class=\"expand\" matTooltip=\"Pop-out editor\" (click)=\"popOut()\">\n <mat-icon>height</mat-icon>\n </div>\n </div>\n }\n @case (SettingType.Int) {\n <div class=\"with-validation\">\n @if (valueFormControl().invalid) {\n <span class=\"error\">*</span>\n }\n <input\n type=\"number\"\n placeholder=\"42\"\n name=\"value\"\n appDigitOnly\n [digitOnlyAllowNegatives]=\"true\"\n [ngClass]=\"{ invalid: valueFormControl().invalid }\"\n [formControl]=\"valueFormControl()\" />\n </div>\n }\n @case (SettingType.Double) {\n <div class=\"with-validation\">\n @if (valueFormControl().invalid) {\n <span class=\"error\">*</span>\n }\n <input\n type=\"number\"\n placeholder=\"3.14\"\n name=\"value\"\n inputDisplayNormalizer\n [ngClass]=\"{ invalid: valueFormControl().invalid }\"\n [formControl]=\"valueFormControl()\" />\n </div>\n }\n @case (SettingType.Boolean) {\n <div class=\"toggle\">\n <label class=\"switch\">\n <input type=\"checkbox\" name=\"value\" [formControl]=\"valueFormControl()\" />\n <span class=\"slider\"></span>\n </label>\n </div>\n }\n }\n </div>\n @if (setting().settingType === SettingType.Boolean) {\n <div class=\"spacer\"></div>\n }\n</div>\n", styles: [".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", ".flex{display:flex;margin-right:10px;align-items:center}.flex .spacer{min-width:11px}.flex .container .with-validation{display:flex;flex-wrap:nowrap}.flex .container input{background-color:var(--input-background);border:1px solid var(--flag-v1-input-border);min-height:28px;padding:0 30px 0 5px;min-width:150px;margin:0 3px;font-family:Montserrat,Roboto,Helvetica Neue,sans-serif}.flex .container input[type=number]{padding:0 2px 0 5px}.flex .container input::placeholder{opacity:var(--flag-v1-input-placeholder-opacity)!important}.flex .container .invalid{border:1px solid var(--flag-validation-error)}.flex .container .error{color:var(--flag-validation-error);margin-right:5px}.flex .variationId{margin:0 0 0 8px;cursor:pointer;color:var(--action-icon-button-color);display:flex;align-items:center}.flex .variationId.bool{margin-left:4px}.flex .expand{position:relative;top:2px;height:24px;cursor:pointer;margin-left:-28px;z-index:1;color:var(--action-icon-button-color)}.flex .expand mat-icon{transform:rotate(45deg)}.flex .toggle{margin-left:4px}@media(max-width:920px){.flex{margin-right:0!important}.flex .spacer{min-width:1px}}@media(max-width:510px){.flex .container .error{display:none}}\n"], dependencies: [{ 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.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: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { 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: 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: DigitOnlyDirective, selector: "[appDigitOnly]", inputs: ["digitOnlyDecimal", "digitOnlyDecimalSeparator", "digitOnlyAllowNegatives", "digitOnlyAllowPaste", "digitOnlyNegativeSign"] }, { kind: "directive", type: InputDisplayNormalizerDirective, selector: "input[inputDisplayNormalizer]" }], changeDetection: i0.ChangeDetectionStrategy.Eager }); }
2165
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: ValueComponent, isStandalone: true, selector: "app-value", inputs: { valueFormControl: { classPropertyName: "valueFormControl", publicName: "valueFormControl", isSignal: true, isRequired: true, transformFunction: null }, setting: { classPropertyName: "setting", publicName: "setting", isSignal: true, isRequired: true, transformFunction: null }, featureFlagLimitations: { classPropertyName: "featureFlagLimitations", publicName: "featureFlagLimitations", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<div class=\"flex\">\n <div class=\"container\">\n @switch (setting().settingType) {\n @case (SettingType.String) {\n <div class=\"with-validation\">\n @if (valueFormControl().invalid) {\n <span class=\"error\">*</span>\n }\n <input\n type=\"text\"\n placeholder=\"my value\"\n name=\"value\"\n [ngClass]=\"{ invalid: valueFormControl().invalid }\"\n [formControl]=\"valueFormControl()\" />\n <div class=\"expand\" matTooltip=\"Pop-out editor\" (click)=\"popOut()\">\n <mat-icon>height</mat-icon>\n </div>\n </div>\n }\n @case (SettingType.Int) {\n <div class=\"with-validation\">\n @if (valueFormControl().invalid) {\n <span class=\"error\">*</span>\n }\n <input\n type=\"number\"\n placeholder=\"42\"\n name=\"value\"\n inputDisplayNormalizer\n [ngClass]=\"{ invalid: valueFormControl().invalid }\"\n [formControl]=\"valueFormControl()\" />\n </div>\n }\n @case (SettingType.Double) {\n <div class=\"with-validation\">\n @if (valueFormControl().invalid) {\n <span class=\"error\">*</span>\n }\n <input\n type=\"number\"\n placeholder=\"3.14\"\n name=\"value\"\n inputDisplayNormalizer\n [ngClass]=\"{ invalid: valueFormControl().invalid }\"\n [formControl]=\"valueFormControl()\" />\n </div>\n }\n @case (SettingType.Boolean) {\n <div class=\"toggle\">\n <label class=\"switch\">\n <input type=\"checkbox\" name=\"value\" [formControl]=\"valueFormControl()\" />\n <span class=\"slider\"></span>\n </label>\n </div>\n }\n }\n </div>\n @if (setting().settingType === SettingType.Boolean) {\n <div class=\"spacer\"></div>\n }\n</div>\n", styles: [".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", ".flex{display:flex;margin-right:10px;align-items:center}.flex .spacer{min-width:11px}.flex .container .with-validation{display:flex;flex-wrap:nowrap}.flex .container input{background-color:var(--input-background);border:1px solid var(--flag-v1-input-border);min-height:28px;padding:0 30px 0 5px;min-width:150px;margin:0 3px;font-family:Montserrat,Roboto,Helvetica Neue,sans-serif}.flex .container input[type=number]{padding:0 2px 0 5px}.flex .container input::placeholder{opacity:var(--flag-v1-input-placeholder-opacity)!important}.flex .container .invalid{border:1px solid var(--flag-validation-error)}.flex .container .error{color:var(--flag-validation-error);margin-right:5px}.flex .variationId{margin:0 0 0 8px;cursor:pointer;color:var(--action-icon-button-color);display:flex;align-items:center}.flex .variationId.bool{margin-left:4px}.flex .expand{position:relative;top:2px;height:24px;cursor:pointer;margin-left:-28px;z-index:1;color:var(--action-icon-button-color)}.flex .expand mat-icon{transform:rotate(45deg)}.flex .toggle{margin-left:4px}@media(max-width:920px){.flex{margin-right:0!important}.flex .spacer{min-width:1px}}@media(max-width:510px){.flex .container .error{display:none}}\n"], dependencies: [{ 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.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: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { 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: 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: InputDisplayNormalizerDirective, selector: "input[inputDisplayNormalizer]" }], changeDetection: i0.ChangeDetectionStrategy.Eager }); }
2226
2166
  }
2227
2167
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: ValueComponent, decorators: [{
2228
2168
  type: Component,
@@ -2233,9 +2173,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImpor
2233
2173
  RxReactiveFormsModule,
2234
2174
  MatTooltip,
2235
2175
  MatIcon,
2236
- DigitOnlyDirective,
2237
2176
  InputDisplayNormalizerDirective,
2238
- ], template: "<div class=\"flex\">\n <div class=\"container\">\n @switch (setting().settingType) {\n @case (SettingType.String) {\n <div class=\"with-validation\">\n @if (valueFormControl().invalid) {\n <span class=\"error\">*</span>\n }\n <input\n type=\"text\"\n placeholder=\"my value\"\n name=\"value\"\n [ngClass]=\"{ invalid: valueFormControl().invalid }\"\n [formControl]=\"valueFormControl()\" />\n <div class=\"expand\" matTooltip=\"Pop-out editor\" (click)=\"popOut()\">\n <mat-icon>height</mat-icon>\n </div>\n </div>\n }\n @case (SettingType.Int) {\n <div class=\"with-validation\">\n @if (valueFormControl().invalid) {\n <span class=\"error\">*</span>\n }\n <input\n type=\"number\"\n placeholder=\"42\"\n name=\"value\"\n appDigitOnly\n [digitOnlyAllowNegatives]=\"true\"\n [ngClass]=\"{ invalid: valueFormControl().invalid }\"\n [formControl]=\"valueFormControl()\" />\n </div>\n }\n @case (SettingType.Double) {\n <div class=\"with-validation\">\n @if (valueFormControl().invalid) {\n <span class=\"error\">*</span>\n }\n <input\n type=\"number\"\n placeholder=\"3.14\"\n name=\"value\"\n inputDisplayNormalizer\n [ngClass]=\"{ invalid: valueFormControl().invalid }\"\n [formControl]=\"valueFormControl()\" />\n </div>\n }\n @case (SettingType.Boolean) {\n <div class=\"toggle\">\n <label class=\"switch\">\n <input type=\"checkbox\" name=\"value\" [formControl]=\"valueFormControl()\" />\n <span class=\"slider\"></span>\n </label>\n </div>\n }\n }\n </div>\n @if (setting().settingType === SettingType.Boolean) {\n <div class=\"spacer\"></div>\n }\n</div>\n", styles: [".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", ".flex{display:flex;margin-right:10px;align-items:center}.flex .spacer{min-width:11px}.flex .container .with-validation{display:flex;flex-wrap:nowrap}.flex .container input{background-color:var(--input-background);border:1px solid var(--flag-v1-input-border);min-height:28px;padding:0 30px 0 5px;min-width:150px;margin:0 3px;font-family:Montserrat,Roboto,Helvetica Neue,sans-serif}.flex .container input[type=number]{padding:0 2px 0 5px}.flex .container input::placeholder{opacity:var(--flag-v1-input-placeholder-opacity)!important}.flex .container .invalid{border:1px solid var(--flag-validation-error)}.flex .container .error{color:var(--flag-validation-error);margin-right:5px}.flex .variationId{margin:0 0 0 8px;cursor:pointer;color:var(--action-icon-button-color);display:flex;align-items:center}.flex .variationId.bool{margin-left:4px}.flex .expand{position:relative;top:2px;height:24px;cursor:pointer;margin-left:-28px;z-index:1;color:var(--action-icon-button-color)}.flex .expand mat-icon{transform:rotate(45deg)}.flex .toggle{margin-left:4px}@media(max-width:920px){.flex{margin-right:0!important}.flex .spacer{min-width:1px}}@media(max-width:510px){.flex .container .error{display:none}}\n"] }]
2177
+ ], template: "<div class=\"flex\">\n <div class=\"container\">\n @switch (setting().settingType) {\n @case (SettingType.String) {\n <div class=\"with-validation\">\n @if (valueFormControl().invalid) {\n <span class=\"error\">*</span>\n }\n <input\n type=\"text\"\n placeholder=\"my value\"\n name=\"value\"\n [ngClass]=\"{ invalid: valueFormControl().invalid }\"\n [formControl]=\"valueFormControl()\" />\n <div class=\"expand\" matTooltip=\"Pop-out editor\" (click)=\"popOut()\">\n <mat-icon>height</mat-icon>\n </div>\n </div>\n }\n @case (SettingType.Int) {\n <div class=\"with-validation\">\n @if (valueFormControl().invalid) {\n <span class=\"error\">*</span>\n }\n <input\n type=\"number\"\n placeholder=\"42\"\n name=\"value\"\n inputDisplayNormalizer\n [ngClass]=\"{ invalid: valueFormControl().invalid }\"\n [formControl]=\"valueFormControl()\" />\n </div>\n }\n @case (SettingType.Double) {\n <div class=\"with-validation\">\n @if (valueFormControl().invalid) {\n <span class=\"error\">*</span>\n }\n <input\n type=\"number\"\n placeholder=\"3.14\"\n name=\"value\"\n inputDisplayNormalizer\n [ngClass]=\"{ invalid: valueFormControl().invalid }\"\n [formControl]=\"valueFormControl()\" />\n </div>\n }\n @case (SettingType.Boolean) {\n <div class=\"toggle\">\n <label class=\"switch\">\n <input type=\"checkbox\" name=\"value\" [formControl]=\"valueFormControl()\" />\n <span class=\"slider\"></span>\n </label>\n </div>\n }\n }\n </div>\n @if (setting().settingType === SettingType.Boolean) {\n <div class=\"spacer\"></div>\n }\n</div>\n", styles: [".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", ".flex{display:flex;margin-right:10px;align-items:center}.flex .spacer{min-width:11px}.flex .container .with-validation{display:flex;flex-wrap:nowrap}.flex .container input{background-color:var(--input-background);border:1px solid var(--flag-v1-input-border);min-height:28px;padding:0 30px 0 5px;min-width:150px;margin:0 3px;font-family:Montserrat,Roboto,Helvetica Neue,sans-serif}.flex .container input[type=number]{padding:0 2px 0 5px}.flex .container input::placeholder{opacity:var(--flag-v1-input-placeholder-opacity)!important}.flex .container .invalid{border:1px solid var(--flag-validation-error)}.flex .container .error{color:var(--flag-validation-error);margin-right:5px}.flex .variationId{margin:0 0 0 8px;cursor:pointer;color:var(--action-icon-button-color);display:flex;align-items:center}.flex .variationId.bool{margin-left:4px}.flex .expand{position:relative;top:2px;height:24px;cursor:pointer;margin-left:-28px;z-index:1;color:var(--action-icon-button-color)}.flex .expand mat-icon{transform:rotate(45deg)}.flex .toggle{margin-left:4px}@media(max-width:920px){.flex{margin-right:0!important}.flex .spacer{min-width:1px}}@media(max-width:510px){.flex .container .error{display:none}}\n"] }]
2239
2178
  }], propDecorators: { valueFormControl: [{ type: i0.Input, args: [{ isSignal: true, alias: "valueFormControl", required: true }] }], setting: [{ type: i0.Input, args: [{ isSignal: true, alias: "setting", required: true }] }], featureFlagLimitations: [{ type: i0.Input, args: [{ isSignal: true, alias: "featureFlagLimitations", required: true }] }] } });
2240
2179
 
2241
2180
  class RolloutPercentageItemsComponent {
@@ -2288,7 +2227,7 @@ class RolloutPercentageItemsComponent {
2288
2227
  this.updateSum();
2289
2228
  }
2290
2229
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: RolloutPercentageItemsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2291
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: RolloutPercentageItemsComponent, isStandalone: true, selector: "app-rollout-percentage-items", inputs: { settingModel: { classPropertyName: "settingModel", publicName: "settingModel", isSignal: true, isRequired: true, transformFunction: null }, formGroup: { classPropertyName: "formGroup", publicName: "formGroup", isSignal: true, isRequired: true, transformFunction: null }, featureFlagLimitations: { classPropertyName: "featureFlagLimitations", publicName: "featureFlagLimitations", isSignal: true, isRequired: true, transformFunction: null }, readOnly: { classPropertyName: "readOnly", publicName: "readOnly", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"percentage-wrapper\" [formGroup]=\"formGroup()\">\n @if (formGroup().controls.rolloutRules.controls.length > 0) {\n <div class=\"if\">\n <mat-chip-set>\n <mat-chip class=\"chip\">ELSE IF</mat-chip>\n </mat-chip-set>\n </div>\n }\n <div class=\"grow\">\n @for (rolloutPercentageItem of formGroup().controls.rolloutPercentageItems.controls; track $index; let i = $index) {\n <div class=\"percentage-row flex\">\n <div class=\"flex\">\n @if (rolloutPercentageItem.controls.percentage.invalid) {\n <span class=\"attribute-error\">*</span>\n }\n <input\n type=\"number\"\n appDigitOnly\n [ngClass]=\"{ invalid: rolloutPercentageItem.controls.percentage.invalid }\"\n [formControl]=\"rolloutPercentageItem.controls.percentage\"\n (input)=\"percentageChanged(i)\" />\n %\n <span class=\"users\">\n of\n <a href=\"https://configcat.com/docs/V1/advanced/user-object/\" target=\"_blank\" rel=\"noopener noreferrer\">\n users\n </a>\n </span>\n </div>\n @if (!readOnly() && formGroup().controls.rolloutPercentageItems.controls.length > 2) {\n <div class=\"delete-option\" matTooltip=\"Remove this % option\" (click)=\"removeRolloutPercentageItem(i)\">\n <mat-icon class=\"icon-transform-rotate\">cancel</mat-icon>\n </div>\n }\n @if (\n i === formGroup().controls.rolloutPercentageItems.controls.length - 1 &&\n formGroup().controls.rolloutPercentageItemsSum.invalid\n ) {\n <span matTooltipPosition=\"above\" matTooltip=\"The sum of percentages must be equal to 100.\">\n <mat-icon color=\"warn\">error_outline</mat-icon>\n </span>\n }\n <div class=\"value flex\">\n @if (settingModel().settingType !== SettingType.Boolean) {\n <span class=\"value-name\">Served value:</span>\n }\n <app-value\n class=\"served-value\"\n [valueFormControl]=\"value(rolloutPercentageItem)\"\n [setting]=\"settingModel()\"\n [featureFlagLimitations]=\"featureFlagLimitations()\" />\n </div>\n @if (!readOnly()) {\n <div class=\"delete\" matTooltip=\"Remove % options\" (click)=\"removeAll()\">\n <mat-icon class=\"icon-transform-rotate\">cancel</mat-icon>\n </div>\n }\n </div>\n }\n </div>\n</div>\n", styles: [".percentage-wrapper{border:1px solid var(--flag-v1-rule-border);background-color:var(--flag-v1-rule-background);margin:.5em 1em;position:relative;padding:.5em 0;display:flex;flex-wrap:wrap}.percentage-wrapper .delete{position:absolute;cursor:pointer;top:-9px;right:-10px;z-index:999;border-radius:50%;background-color:var(--flag-delete-background);height:20px;width:20px;color:var(--flag-delete)}.percentage-wrapper .delete mat-icon{font-size:20px;height:20px;width:20px;opacity:var(--flag-v1-colored-berry-opacity)}.percentage-wrapper .delete:hover mat-icon{opacity:1}.percentage-wrapper .delete-option{color:var(--flag-delete);cursor:pointer;margin-left:1rem;margin-top:4px}.percentage-wrapper .delete-option:hover mat-icon{opacity:1}.percentage-wrapper .delete-option mat-icon{opacity:.5;font-size:20px}.if{display:flex;align-items:center}.if>*{margin:0 .5em}.if .spacing{margin-right:44px}.if .chip{background-color:var(--flag-v1-chip-background)}.grow{flex-grow:1}.percentage-row{min-height:40px;margin-left:.5em}.percentage-row .invalid{border:1px solid var(--flag-validation-error)}.attribute-error{color:var(--flag-validation-error);margin:0 5px}.flex{display:flex;flex-wrap:wrap;align-items:center}.value{margin-left:auto}.value .value-name{display:none}input{background-color:var(--input-background);border:1px solid var(--flag-v1-input-border);min-height:28px;width:40px;margin-top:3px;margin-right:5px;padding:0 5px;font-family:Montserrat,Roboto,Helvetica Neue,sans-serif}input::placeholder{opacity:var(--flag-v1-input-placeholder-opacity)!important}.users{margin-left:20px}@media(max-width:920px){.value{min-width:0!important}.value .served-value{margin:3px 7px 3px 0}}@media(max-width:650px){.users{margin-left:5px}}@media(max-width:540px){.users{font-size:12px}}\n"], dependencies: [{ 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: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { 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: "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: MatChipSet, selector: "mat-chip-set", inputs: ["disabled", "role", "tabIndex"] }, { kind: "component", type: MatChip, selector: "mat-basic-chip, [mat-basic-chip], mat-chip, [mat-chip]", inputs: ["role", "id", "aria-label", "aria-description", "value", "color", "removable", "highlighted", "disableRipple", "disabled"], outputs: ["removed", "destroyed"], exportAs: ["matChip"] }, { kind: "directive", type: DigitOnlyDirective, selector: "[appDigitOnly]", inputs: ["digitOnlyDecimal", "digitOnlyDecimalSeparator", "digitOnlyAllowNegatives", "digitOnlyAllowPaste", "digitOnlyNegativeSign"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { 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: "component", type: ValueComponent, selector: "app-value", inputs: ["valueFormControl", "setting", "featureFlagLimitations"] }], changeDetection: i0.ChangeDetectionStrategy.Eager }); }
2230
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: RolloutPercentageItemsComponent, isStandalone: true, selector: "app-rollout-percentage-items", inputs: { settingModel: { classPropertyName: "settingModel", publicName: "settingModel", isSignal: true, isRequired: true, transformFunction: null }, formGroup: { classPropertyName: "formGroup", publicName: "formGroup", isSignal: true, isRequired: true, transformFunction: null }, featureFlagLimitations: { classPropertyName: "featureFlagLimitations", publicName: "featureFlagLimitations", isSignal: true, isRequired: true, transformFunction: null }, readOnly: { classPropertyName: "readOnly", publicName: "readOnly", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"percentage-wrapper\" [formGroup]=\"formGroup()\">\n @if (formGroup().controls.rolloutRules.controls.length > 0) {\n <div class=\"if\">\n <mat-chip-set>\n <mat-chip class=\"chip\">ELSE IF</mat-chip>\n </mat-chip-set>\n </div>\n }\n <div class=\"grow\">\n @for (rolloutPercentageItem of formGroup().controls.rolloutPercentageItems.controls; track $index; let i = $index) {\n <div class=\"percentage-row flex\">\n <div class=\"flex\">\n @if (rolloutPercentageItem.controls.percentage.invalid) {\n <span class=\"attribute-error\">*</span>\n }\n <input\n type=\"number\"\n inputDisplayNormalizer\n [ngClass]=\"{ invalid: rolloutPercentageItem.controls.percentage.invalid }\"\n [formControl]=\"rolloutPercentageItem.controls.percentage\"\n (input)=\"percentageChanged(i)\" />\n %\n <span class=\"users\">\n of\n <a href=\"https://configcat.com/docs/V1/advanced/user-object/\" target=\"_blank\" rel=\"noopener noreferrer\">\n users\n </a>\n </span>\n </div>\n @if (!readOnly() && formGroup().controls.rolloutPercentageItems.controls.length > 2) {\n <div class=\"delete-option\" matTooltip=\"Remove this % option\" (click)=\"removeRolloutPercentageItem(i)\">\n <mat-icon class=\"icon-transform-rotate\">cancel</mat-icon>\n </div>\n }\n @if (\n i === formGroup().controls.rolloutPercentageItems.controls.length - 1 &&\n formGroup().controls.rolloutPercentageItemsSum.invalid\n ) {\n <span matTooltipPosition=\"above\" matTooltip=\"The sum of percentages must be equal to 100.\">\n <mat-icon color=\"warn\">error_outline</mat-icon>\n </span>\n }\n <div class=\"value flex\">\n @if (settingModel().settingType !== SettingType.Boolean) {\n <span class=\"value-name\">Served value:</span>\n }\n <app-value\n class=\"served-value\"\n [valueFormControl]=\"value(rolloutPercentageItem)\"\n [setting]=\"settingModel()\"\n [featureFlagLimitations]=\"featureFlagLimitations()\" />\n </div>\n @if (!readOnly()) {\n <div class=\"delete\" matTooltip=\"Remove % options\" (click)=\"removeAll()\">\n <mat-icon class=\"icon-transform-rotate\">cancel</mat-icon>\n </div>\n }\n </div>\n }\n </div>\n</div>\n", styles: [".percentage-wrapper{border:1px solid var(--flag-v1-rule-border);background-color:var(--flag-v1-rule-background);margin:.5em 1em;position:relative;padding:.5em 0;display:flex;flex-wrap:wrap}.percentage-wrapper .delete{position:absolute;cursor:pointer;top:-9px;right:-10px;z-index:999;border-radius:50%;background-color:var(--flag-delete-background);height:20px;width:20px;color:var(--flag-delete)}.percentage-wrapper .delete mat-icon{font-size:20px;height:20px;width:20px;opacity:var(--flag-v1-colored-berry-opacity)}.percentage-wrapper .delete:hover mat-icon{opacity:1}.percentage-wrapper .delete-option{color:var(--flag-delete);cursor:pointer;margin-left:1rem;margin-top:4px}.percentage-wrapper .delete-option:hover mat-icon{opacity:1}.percentage-wrapper .delete-option mat-icon{opacity:.5;font-size:20px}.if{display:flex;align-items:center}.if>*{margin:0 .5em}.if .spacing{margin-right:44px}.if .chip{background-color:var(--flag-v1-chip-background)}.grow{flex-grow:1}.percentage-row{min-height:40px;margin-left:.5em}.percentage-row .invalid{border:1px solid var(--flag-validation-error)}.attribute-error{color:var(--flag-validation-error);margin:0 5px}.flex{display:flex;flex-wrap:wrap;align-items:center}.value{margin-left:auto}.value .value-name{display:none}input{background-color:var(--input-background);border:1px solid var(--flag-v1-input-border);min-height:28px;width:40px;margin-top:3px;margin-right:5px;padding:0 5px;font-family:Montserrat,Roboto,Helvetica Neue,sans-serif}input::placeholder{opacity:var(--flag-v1-input-placeholder-opacity)!important}.users{margin-left:20px}@media(max-width:920px){.value{min-width:0!important}.value .served-value{margin:3px 7px 3px 0}}@media(max-width:650px){.users{margin-left:5px}}@media(max-width:540px){.users{font-size:12px}}\n"], dependencies: [{ 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: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { 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: "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: MatChipSet, selector: "mat-chip-set", inputs: ["disabled", "role", "tabIndex"] }, { kind: "component", type: MatChip, selector: "mat-basic-chip, [mat-basic-chip], mat-chip, [mat-chip]", inputs: ["role", "id", "aria-label", "aria-description", "value", "color", "removable", "highlighted", "disableRipple", "disabled"], outputs: ["removed", "destroyed"], exportAs: ["matChip"] }, { kind: "directive", type: InputDisplayNormalizerDirective, selector: "input[inputDisplayNormalizer]" }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { 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: "component", type: ValueComponent, selector: "app-value", inputs: ["valueFormControl", "setting", "featureFlagLimitations"] }], changeDetection: i0.ChangeDetectionStrategy.Eager }); }
2292
2231
  }
2293
2232
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: RolloutPercentageItemsComponent, decorators: [{
2294
2233
  type: Component,
@@ -2298,12 +2237,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImpor
2298
2237
  RxReactiveFormsModule,
2299
2238
  MatChipSet,
2300
2239
  MatChip,
2301
- DigitOnlyDirective,
2240
+ InputDisplayNormalizerDirective,
2302
2241
  NgClass,
2303
2242
  MatTooltip,
2304
2243
  MatIcon,
2305
2244
  ValueComponent,
2306
- ], template: "<div class=\"percentage-wrapper\" [formGroup]=\"formGroup()\">\n @if (formGroup().controls.rolloutRules.controls.length > 0) {\n <div class=\"if\">\n <mat-chip-set>\n <mat-chip class=\"chip\">ELSE IF</mat-chip>\n </mat-chip-set>\n </div>\n }\n <div class=\"grow\">\n @for (rolloutPercentageItem of formGroup().controls.rolloutPercentageItems.controls; track $index; let i = $index) {\n <div class=\"percentage-row flex\">\n <div class=\"flex\">\n @if (rolloutPercentageItem.controls.percentage.invalid) {\n <span class=\"attribute-error\">*</span>\n }\n <input\n type=\"number\"\n appDigitOnly\n [ngClass]=\"{ invalid: rolloutPercentageItem.controls.percentage.invalid }\"\n [formControl]=\"rolloutPercentageItem.controls.percentage\"\n (input)=\"percentageChanged(i)\" />\n %\n <span class=\"users\">\n of\n <a href=\"https://configcat.com/docs/V1/advanced/user-object/\" target=\"_blank\" rel=\"noopener noreferrer\">\n users\n </a>\n </span>\n </div>\n @if (!readOnly() && formGroup().controls.rolloutPercentageItems.controls.length > 2) {\n <div class=\"delete-option\" matTooltip=\"Remove this % option\" (click)=\"removeRolloutPercentageItem(i)\">\n <mat-icon class=\"icon-transform-rotate\">cancel</mat-icon>\n </div>\n }\n @if (\n i === formGroup().controls.rolloutPercentageItems.controls.length - 1 &&\n formGroup().controls.rolloutPercentageItemsSum.invalid\n ) {\n <span matTooltipPosition=\"above\" matTooltip=\"The sum of percentages must be equal to 100.\">\n <mat-icon color=\"warn\">error_outline</mat-icon>\n </span>\n }\n <div class=\"value flex\">\n @if (settingModel().settingType !== SettingType.Boolean) {\n <span class=\"value-name\">Served value:</span>\n }\n <app-value\n class=\"served-value\"\n [valueFormControl]=\"value(rolloutPercentageItem)\"\n [setting]=\"settingModel()\"\n [featureFlagLimitations]=\"featureFlagLimitations()\" />\n </div>\n @if (!readOnly()) {\n <div class=\"delete\" matTooltip=\"Remove % options\" (click)=\"removeAll()\">\n <mat-icon class=\"icon-transform-rotate\">cancel</mat-icon>\n </div>\n }\n </div>\n }\n </div>\n</div>\n", styles: [".percentage-wrapper{border:1px solid var(--flag-v1-rule-border);background-color:var(--flag-v1-rule-background);margin:.5em 1em;position:relative;padding:.5em 0;display:flex;flex-wrap:wrap}.percentage-wrapper .delete{position:absolute;cursor:pointer;top:-9px;right:-10px;z-index:999;border-radius:50%;background-color:var(--flag-delete-background);height:20px;width:20px;color:var(--flag-delete)}.percentage-wrapper .delete mat-icon{font-size:20px;height:20px;width:20px;opacity:var(--flag-v1-colored-berry-opacity)}.percentage-wrapper .delete:hover mat-icon{opacity:1}.percentage-wrapper .delete-option{color:var(--flag-delete);cursor:pointer;margin-left:1rem;margin-top:4px}.percentage-wrapper .delete-option:hover mat-icon{opacity:1}.percentage-wrapper .delete-option mat-icon{opacity:.5;font-size:20px}.if{display:flex;align-items:center}.if>*{margin:0 .5em}.if .spacing{margin-right:44px}.if .chip{background-color:var(--flag-v1-chip-background)}.grow{flex-grow:1}.percentage-row{min-height:40px;margin-left:.5em}.percentage-row .invalid{border:1px solid var(--flag-validation-error)}.attribute-error{color:var(--flag-validation-error);margin:0 5px}.flex{display:flex;flex-wrap:wrap;align-items:center}.value{margin-left:auto}.value .value-name{display:none}input{background-color:var(--input-background);border:1px solid var(--flag-v1-input-border);min-height:28px;width:40px;margin-top:3px;margin-right:5px;padding:0 5px;font-family:Montserrat,Roboto,Helvetica Neue,sans-serif}input::placeholder{opacity:var(--flag-v1-input-placeholder-opacity)!important}.users{margin-left:20px}@media(max-width:920px){.value{min-width:0!important}.value .served-value{margin:3px 7px 3px 0}}@media(max-width:650px){.users{margin-left:5px}}@media(max-width:540px){.users{font-size:12px}}\n"] }]
2245
+ ], template: "<div class=\"percentage-wrapper\" [formGroup]=\"formGroup()\">\n @if (formGroup().controls.rolloutRules.controls.length > 0) {\n <div class=\"if\">\n <mat-chip-set>\n <mat-chip class=\"chip\">ELSE IF</mat-chip>\n </mat-chip-set>\n </div>\n }\n <div class=\"grow\">\n @for (rolloutPercentageItem of formGroup().controls.rolloutPercentageItems.controls; track $index; let i = $index) {\n <div class=\"percentage-row flex\">\n <div class=\"flex\">\n @if (rolloutPercentageItem.controls.percentage.invalid) {\n <span class=\"attribute-error\">*</span>\n }\n <input\n type=\"number\"\n inputDisplayNormalizer\n [ngClass]=\"{ invalid: rolloutPercentageItem.controls.percentage.invalid }\"\n [formControl]=\"rolloutPercentageItem.controls.percentage\"\n (input)=\"percentageChanged(i)\" />\n %\n <span class=\"users\">\n of\n <a href=\"https://configcat.com/docs/V1/advanced/user-object/\" target=\"_blank\" rel=\"noopener noreferrer\">\n users\n </a>\n </span>\n </div>\n @if (!readOnly() && formGroup().controls.rolloutPercentageItems.controls.length > 2) {\n <div class=\"delete-option\" matTooltip=\"Remove this % option\" (click)=\"removeRolloutPercentageItem(i)\">\n <mat-icon class=\"icon-transform-rotate\">cancel</mat-icon>\n </div>\n }\n @if (\n i === formGroup().controls.rolloutPercentageItems.controls.length - 1 &&\n formGroup().controls.rolloutPercentageItemsSum.invalid\n ) {\n <span matTooltipPosition=\"above\" matTooltip=\"The sum of percentages must be equal to 100.\">\n <mat-icon color=\"warn\">error_outline</mat-icon>\n </span>\n }\n <div class=\"value flex\">\n @if (settingModel().settingType !== SettingType.Boolean) {\n <span class=\"value-name\">Served value:</span>\n }\n <app-value\n class=\"served-value\"\n [valueFormControl]=\"value(rolloutPercentageItem)\"\n [setting]=\"settingModel()\"\n [featureFlagLimitations]=\"featureFlagLimitations()\" />\n </div>\n @if (!readOnly()) {\n <div class=\"delete\" matTooltip=\"Remove % options\" (click)=\"removeAll()\">\n <mat-icon class=\"icon-transform-rotate\">cancel</mat-icon>\n </div>\n }\n </div>\n }\n </div>\n</div>\n", styles: [".percentage-wrapper{border:1px solid var(--flag-v1-rule-border);background-color:var(--flag-v1-rule-background);margin:.5em 1em;position:relative;padding:.5em 0;display:flex;flex-wrap:wrap}.percentage-wrapper .delete{position:absolute;cursor:pointer;top:-9px;right:-10px;z-index:999;border-radius:50%;background-color:var(--flag-delete-background);height:20px;width:20px;color:var(--flag-delete)}.percentage-wrapper .delete mat-icon{font-size:20px;height:20px;width:20px;opacity:var(--flag-v1-colored-berry-opacity)}.percentage-wrapper .delete:hover mat-icon{opacity:1}.percentage-wrapper .delete-option{color:var(--flag-delete);cursor:pointer;margin-left:1rem;margin-top:4px}.percentage-wrapper .delete-option:hover mat-icon{opacity:1}.percentage-wrapper .delete-option mat-icon{opacity:.5;font-size:20px}.if{display:flex;align-items:center}.if>*{margin:0 .5em}.if .spacing{margin-right:44px}.if .chip{background-color:var(--flag-v1-chip-background)}.grow{flex-grow:1}.percentage-row{min-height:40px;margin-left:.5em}.percentage-row .invalid{border:1px solid var(--flag-validation-error)}.attribute-error{color:var(--flag-validation-error);margin:0 5px}.flex{display:flex;flex-wrap:wrap;align-items:center}.value{margin-left:auto}.value .value-name{display:none}input{background-color:var(--input-background);border:1px solid var(--flag-v1-input-border);min-height:28px;width:40px;margin-top:3px;margin-right:5px;padding:0 5px;font-family:Montserrat,Roboto,Helvetica Neue,sans-serif}input::placeholder{opacity:var(--flag-v1-input-placeholder-opacity)!important}.users{margin-left:20px}@media(max-width:920px){.value{min-width:0!important}.value .served-value{margin:3px 7px 3px 0}}@media(max-width:650px){.users{margin-left:5px}}@media(max-width:540px){.users{font-size:12px}}\n"] }]
2307
2246
  }], propDecorators: { settingModel: [{ type: i0.Input, args: [{ isSignal: true, alias: "settingModel", required: true }] }], formGroup: [{ type: i0.Input, args: [{ isSignal: true, alias: "formGroup", required: true }] }], featureFlagLimitations: [{ type: i0.Input, args: [{ isSignal: true, alias: "featureFlagLimitations", required: true }] }], readOnly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readOnly", required: false }] }] } });
2308
2247
 
2309
2248
  class DeselectDirective {
@@ -2757,7 +2696,8 @@ class SettingListComponent {
2757
2696
  this.dialog.open(RerouteDialogComponent, {
2758
2697
  data: {
2759
2698
  title: "You have reached the limits of your plan",
2760
- 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.`,
2761
2701
  yesButtonLabel: "See plans",
2762
2702
  yesButtonLink: this.dashboardBasePath() + "/organization/plans",
2763
2703
  noButtonLabel: "Close",
@@ -2931,6 +2871,7 @@ class SettingItemComponent extends BaseComponent {
2931
2871
  this.loadSucceeded = output();
2932
2872
  this.saveSucceeded = output();
2933
2873
  this.componentError = output();
2874
+ this.loadFailed = output();
2934
2875
  this.formValuesChanged = output();
2935
2876
  this.loadSettingValues = () => {
2936
2877
  return this.createSettingValuesService()
@@ -2946,7 +2887,7 @@ class SettingItemComponent extends BaseComponent {
2946
2887
  };
2947
2888
  return settingValues;
2948
2889
  }), catchError((error) => {
2949
- this.componentError.emit(error);
2890
+ this.loadFailed.emit(error);
2950
2891
  throw error;
2951
2892
  }));
2952
2893
  };
@@ -2999,12 +2940,12 @@ class SettingItemComponent extends BaseComponent {
2999
2940
  this.formValuesChanged.emit();
3000
2941
  }
3001
2942
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: SettingItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3002
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.1", type: SettingItemComponent, isStandalone: true, selector: "app-setting-item", inputs: { productId: { classPropertyName: "productId", publicName: "productId", isSignal: true, isRequired: true, transformFunction: null }, environmentId: { classPropertyName: "environmentId", publicName: "environmentId", isSignal: true, isRequired: true, transformFunction: null }, configId: { classPropertyName: "configId", publicName: "configId", isSignal: true, isRequired: true, transformFunction: null }, settingId: { classPropertyName: "settingId", publicName: "settingId", isSignal: true, isRequired: true, transformFunction: null }, canDeleteSetting: { classPropertyName: "canDeleteSetting", publicName: "canDeleteSetting", isSignal: true, isRequired: false, transformFunction: null }, deleteSettingText: { classPropertyName: "deleteSettingText", publicName: "deleteSettingText", isSignal: true, isRequired: false, transformFunction: null }, showEnvironmentName: { classPropertyName: "showEnvironmentName", publicName: "showEnvironmentName", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { deleteSettingRequested: "deleteSettingRequested", loadSucceeded: "loadSucceeded", saveSucceeded: "saveSucceeded", componentError: "componentError", formValuesChanged: "formValuesChanged" }, usesInheritance: true, ngImport: i0, template: "<app-setting-list\n [canDeleteSetting]=\"canDeleteSetting()\"\n [onSave]=\"handleSave\"\n [loadSettingValues]=\"loadSettingValues\"\n [loadSegments]=\"loadSegments\"\n [deleteSettingText]=\"deleteSettingText()\"\n [showEnvironmentName]=\"showEnvironmentName()\"\n [productId]=\"productId()\"\n [configId]=\"configId()\"\n [environmentId]=\"environmentId()\"\n [dashboardBasePath]=\"dashboardBasePath\"\n (loadSucceeded)=\"itemLoadSucceeded()\"\n (saveFailed)=\"itemSaveFailed($event)\"\n (deleteSettingRequested)=\"onDeleteSettingRequested($event)\"\n (formValuesChanged)=\"onFormValuesChanged()\" />\n", styles: [".end{margin-left:auto}.flex{display:flex;flex-wrap:wrap;align-items:center}.show-hide-toggle{margin-left:1em;cursor:pointer}@media(max-width:1200px){.setting-full .setting-details{width:300px}}@media(max-width:700px){.setting-full{flex-wrap:wrap}.setting-full .setting-details{padding:.2em 0 .2em .2em}.setting-full .all-other{margin:.5em .3em}}\n"], dependencies: [{ kind: "component", type: SettingListComponent, selector: "app-setting-list", inputs: ["productId", "environmentId", "configId", "dashboardBasePath", "canDeleteSetting", "onSave", "loadSettingValues", "loadSegments", "deleteSettingText", "showEnvironmentName"], outputs: ["loadSucceeded", "deleteSettingRequested", "formValuesChanged", "saveFailed"] }], changeDetection: i0.ChangeDetectionStrategy.Eager }); }
2943
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.1", type: SettingItemComponent, isStandalone: true, selector: "app-setting-item", inputs: { productId: { classPropertyName: "productId", publicName: "productId", isSignal: true, isRequired: true, transformFunction: null }, environmentId: { classPropertyName: "environmentId", publicName: "environmentId", isSignal: true, isRequired: true, transformFunction: null }, configId: { classPropertyName: "configId", publicName: "configId", isSignal: true, isRequired: true, transformFunction: null }, settingId: { classPropertyName: "settingId", publicName: "settingId", isSignal: true, isRequired: true, transformFunction: null }, canDeleteSetting: { classPropertyName: "canDeleteSetting", publicName: "canDeleteSetting", isSignal: true, isRequired: false, transformFunction: null }, deleteSettingText: { classPropertyName: "deleteSettingText", publicName: "deleteSettingText", isSignal: true, isRequired: false, transformFunction: null }, showEnvironmentName: { classPropertyName: "showEnvironmentName", publicName: "showEnvironmentName", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { deleteSettingRequested: "deleteSettingRequested", loadSucceeded: "loadSucceeded", saveSucceeded: "saveSucceeded", componentError: "componentError", loadFailed: "loadFailed", formValuesChanged: "formValuesChanged" }, usesInheritance: true, ngImport: i0, template: "<app-setting-list\n [canDeleteSetting]=\"canDeleteSetting()\"\n [onSave]=\"handleSave\"\n [loadSettingValues]=\"loadSettingValues\"\n [loadSegments]=\"loadSegments\"\n [deleteSettingText]=\"deleteSettingText()\"\n [showEnvironmentName]=\"showEnvironmentName()\"\n [productId]=\"productId()\"\n [configId]=\"configId()\"\n [environmentId]=\"environmentId()\"\n [dashboardBasePath]=\"dashboardBasePath\"\n (loadSucceeded)=\"itemLoadSucceeded()\"\n (saveFailed)=\"itemSaveFailed($event)\"\n (deleteSettingRequested)=\"onDeleteSettingRequested($event)\"\n (formValuesChanged)=\"onFormValuesChanged()\" />\n", styles: [".end{margin-left:auto}.flex{display:flex;flex-wrap:wrap;align-items:center}.show-hide-toggle{margin-left:1em;cursor:pointer}@media(max-width:1200px){.setting-full .setting-details{width:300px}}@media(max-width:700px){.setting-full{flex-wrap:wrap}.setting-full .setting-details{padding:.2em 0 .2em .2em}.setting-full .all-other{margin:.5em .3em}}\n"], dependencies: [{ kind: "component", type: SettingListComponent, selector: "app-setting-list", inputs: ["productId", "environmentId", "configId", "dashboardBasePath", "canDeleteSetting", "onSave", "loadSettingValues", "loadSegments", "deleteSettingText", "showEnvironmentName"], outputs: ["loadSucceeded", "deleteSettingRequested", "formValuesChanged", "saveFailed"] }], changeDetection: i0.ChangeDetectionStrategy.Eager }); }
3003
2944
  }
3004
2945
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: SettingItemComponent, decorators: [{
3005
2946
  type: Component,
3006
2947
  args: [{ selector: "app-setting-item", changeDetection: ChangeDetectionStrategy.Eager, imports: [SettingListComponent], template: "<app-setting-list\n [canDeleteSetting]=\"canDeleteSetting()\"\n [onSave]=\"handleSave\"\n [loadSettingValues]=\"loadSettingValues\"\n [loadSegments]=\"loadSegments\"\n [deleteSettingText]=\"deleteSettingText()\"\n [showEnvironmentName]=\"showEnvironmentName()\"\n [productId]=\"productId()\"\n [configId]=\"configId()\"\n [environmentId]=\"environmentId()\"\n [dashboardBasePath]=\"dashboardBasePath\"\n (loadSucceeded)=\"itemLoadSucceeded()\"\n (saveFailed)=\"itemSaveFailed($event)\"\n (deleteSettingRequested)=\"onDeleteSettingRequested($event)\"\n (formValuesChanged)=\"onFormValuesChanged()\" />\n", styles: [".end{margin-left:auto}.flex{display:flex;flex-wrap:wrap;align-items:center}.show-hide-toggle{margin-left:1em;cursor:pointer}@media(max-width:1200px){.setting-full .setting-details{width:300px}}@media(max-width:700px){.setting-full{flex-wrap:wrap}.setting-full .setting-details{padding:.2em 0 .2em .2em}.setting-full .all-other{margin:.5em .3em}}\n"] }]
3007
- }], ctorParameters: () => [], propDecorators: { productId: [{ type: i0.Input, args: [{ isSignal: true, alias: "productId", required: true }] }], environmentId: [{ type: i0.Input, args: [{ isSignal: true, alias: "environmentId", required: true }] }], configId: [{ type: i0.Input, args: [{ isSignal: true, alias: "configId", required: true }] }], settingId: [{ type: i0.Input, args: [{ isSignal: true, alias: "settingId", required: true }] }], canDeleteSetting: [{ type: i0.Input, args: [{ isSignal: true, alias: "canDeleteSetting", required: false }] }], deleteSettingText: [{ type: i0.Input, args: [{ isSignal: true, alias: "deleteSettingText", required: false }] }], showEnvironmentName: [{ type: i0.Input, args: [{ isSignal: true, alias: "showEnvironmentName", required: false }] }], deleteSettingRequested: [{ type: i0.Output, args: ["deleteSettingRequested"] }], loadSucceeded: [{ type: i0.Output, args: ["loadSucceeded"] }], saveSucceeded: [{ type: i0.Output, args: ["saveSucceeded"] }], componentError: [{ type: i0.Output, args: ["componentError"] }], formValuesChanged: [{ type: i0.Output, args: ["formValuesChanged"] }] } });
2948
+ }], ctorParameters: () => [], propDecorators: { productId: [{ type: i0.Input, args: [{ isSignal: true, alias: "productId", required: true }] }], environmentId: [{ type: i0.Input, args: [{ isSignal: true, alias: "environmentId", required: true }] }], configId: [{ type: i0.Input, args: [{ isSignal: true, alias: "configId", required: true }] }], settingId: [{ type: i0.Input, args: [{ isSignal: true, alias: "settingId", required: true }] }], canDeleteSetting: [{ type: i0.Input, args: [{ isSignal: true, alias: "canDeleteSetting", required: false }] }], deleteSettingText: [{ type: i0.Input, args: [{ isSignal: true, alias: "deleteSettingText", required: false }] }], showEnvironmentName: [{ type: i0.Input, args: [{ isSignal: true, alias: "showEnvironmentName", required: false }] }], deleteSettingRequested: [{ type: i0.Output, args: ["deleteSettingRequested"] }], loadSucceeded: [{ type: i0.Output, args: ["loadSucceeded"] }], saveSucceeded: [{ type: i0.Output, args: ["saveSucceeded"] }], componentError: [{ type: i0.Output, args: ["componentError"] }], loadFailed: [{ type: i0.Output, args: ["loadFailed"] }], formValuesChanged: [{ type: i0.Output, args: ["formValuesChanged"] }] } });
3008
2949
 
3009
2950
  class AuthorizationComponent {
3010
2951
  constructor() {
@@ -3484,11 +3425,11 @@ class DeleteSettingDialogComponent {
3484
3425
  this.dialogRef.close({ button: "cancel" });
3485
3426
  }
3486
3427
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: DeleteSettingDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3487
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.1", type: DeleteSettingDialogComponent, isStandalone: true, selector: "app-delete-setting-dialog", ngImport: i0, template: "<h1 mat-dialog-title>Remove Feature Flag or Setting from {{ data.system }}</h1>\n<div mat-dialog-actions class=\"container\">\n <button type=\"button\" mat-flat-button color=\"primary\" (click)=\"remove()\">Remove from {{ data.ticketType }}</button>\n <button type=\"button\" mat-stroked-button (click)=\"cancel()\">Cancel</button>\n</div>\n", dependencies: [{ kind: "directive", type: MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { 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 }); }
3428
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.1", type: DeleteSettingDialogComponent, isStandalone: true, selector: "app-delete-setting-dialog", ngImport: i0, template: "<h1 mat-dialog-title>Remove Feature Flag or Setting from {{ data.system }}?</h1>\n<div mat-dialog-actions class=\"container\">\n <button type=\"button\" mat-flat-button color=\"primary\" (click)=\"remove()\">Remove from {{ data.ticketType }}</button>\n <button type=\"button\" mat-stroked-button (click)=\"cancel()\">Cancel</button>\n</div>\n", dependencies: [{ kind: "directive", type: MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { 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 }); }
3488
3429
  }
3489
3430
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: DeleteSettingDialogComponent, decorators: [{
3490
3431
  type: Component,
3491
- args: [{ selector: "app-delete-setting-dialog", changeDetection: ChangeDetectionStrategy.Eager, imports: [MatDialogTitle, MatDialogActions, MatButton], template: "<h1 mat-dialog-title>Remove Feature Flag or Setting from {{ data.system }}</h1>\n<div mat-dialog-actions class=\"container\">\n <button type=\"button\" mat-flat-button color=\"primary\" (click)=\"remove()\">Remove from {{ data.ticketType }}</button>\n <button type=\"button\" mat-stroked-button (click)=\"cancel()\">Cancel</button>\n</div>\n" }]
3432
+ args: [{ selector: "app-delete-setting-dialog", changeDetection: ChangeDetectionStrategy.Eager, imports: [MatDialogTitle, MatDialogActions, MatButton], template: "<h1 mat-dialog-title>Remove Feature Flag or Setting from {{ data.system }}?</h1>\n<div mat-dialog-actions class=\"container\">\n <button type=\"button\" mat-flat-button color=\"primary\" (click)=\"remove()\">Remove from {{ data.ticketType }}</button>\n <button type=\"button\" mat-stroked-button (click)=\"cancel()\">Cancel</button>\n</div>\n" }]
3492
3433
  }] });
3493
3434
 
3494
3435
  class TrackingDirective {
@@ -3560,17 +3501,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImpor
3560
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" }]
3561
3502
  }] });
3562
3503
 
3563
- class HtmlEscaper {
3564
- static escapeHtml(unsafe) {
3565
- return unsafe
3566
- .replace(/&/g, "&amp;")
3567
- .replace(/</g, "&lt;")
3568
- .replace(/>/g, "&gt;")
3569
- .replace(/"/g, "&quot;")
3570
- .replace(/'/g, "&#039;");
3571
- }
3572
- }
3573
-
3574
3504
  class BoxComponent {
3575
3505
  constructor() {
3576
3506
  this.type = input.required(/* @ts-ignore */
@@ -3856,9 +3786,10 @@ class CreateChangeRequestDialogComponent {
3856
3786
  width: "450px",
3857
3787
  data: {
3858
3788
  title: "Cannot create change request",
3859
- body: "Circular dependency detected between the following feature flags:<p>"
3860
- + HtmlEscaper.escapeHtml(errorMessage)
3861
- + "</p>Please <strong>eliminate the circular dependency</strong> before creating your change request.",
3789
+ body: safeHtml `\
3790
+ Circular dependency detected between the following feature flags:
3791
+ <p>${errorMessage}</p>
3792
+ Please <strong>eliminate the circular dependency</strong> before creating your change request.`,
3862
3793
  yesButtonLabel: "I understand",
3863
3794
  hideCancelButton: true,
3864
3795
  },
@@ -4024,13 +3955,13 @@ class SaveAndPublishDialogComponent {
4024
3955
  this.dialogRef.close({ button: "cancel", reason: null });
4025
3956
  }
4026
3957
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: SaveAndPublishDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4027
- 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"] }] }); }
3958
+ 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"] }] }); }
4028
3959
  }
4029
3960
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: SaveAndPublishDialogComponent, decorators: [{
4030
3961
  type: Component,
4031
3962
  args: [{ selector: "app-save-and-publish-dialog", imports: [MatDialogTitle, FormsModule, ReactiveFormsModule, RxReactiveFormsModule, CdkScrollable, MatDialogContent,
4032
3963
  MatFormField, MatInput, CdkTextareaAutosize, MatError, MatDialogActions, MatButton, CdkDrag, CdkDragHandle,
4033
- 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"] }]
3964
+ 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"] }]
4034
3965
  }], propDecorators: { reasonInput: [{ type: i0.ViewChild, args: ["reason", { isSignal: true }] }] } });
4035
3966
 
4036
3967
  function getRawPredefinedVariationValue(formGroup) {
@@ -4204,7 +4135,8 @@ class PastedDataTrimmerDirective {
4204
4135
  const dialogRef = this.dialog.open(ConfirmationDialogComponent, {
4205
4136
  data: {
4206
4137
  title: "Trim extra spaces?",
4207
- body: "The pasted value contains extra spaces at the beginning or end.<br/>This may cause unexpected behavior, for example when comparing values.",
4138
+ body: safeHtml `\
4139
+ The pasted value contains extra spaces at the beginning or end.<br/>This may cause unexpected behavior, for example when comparing values.`,
4208
4140
  yesButtonLabel: "Trim extra spaces",
4209
4141
  cancelButtonLabel: "Keep spaces",
4210
4142
  },
@@ -4624,7 +4556,7 @@ class VariationsDialogComponent {
4624
4556
  return getPredefinedVariationColorIndex(this.settingType, getRawPredefinedVariationValue(row.controls.value), index);
4625
4557
  }
4626
4558
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: VariationsDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4627
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: VariationsDialogComponent, isStandalone: true, selector: "app-variations-dialog", ngImport: i0, template: "@if (setting.isLoading()) {\n <div mat-dialog-content class=\"content\">\n <app-loader />\n </div>\n} @else {\n <form [formGroup]=\"formGroup\" (ngSubmit)=\"onSubmit()\">\n <h1 mat-dialog-title>{{ data.manage ? \"Manage predefined variations\" : \"View predefined variations\" }}</h1>\n <div class=\"content\">\n <div class=\"dialog-description\" mat-dialog-content>\n @if (data.manage) {\n <p>\n Add, delete, and update predefined variations of the\n <strong>{{ data.settingName }}</strong>\n setting.\n <br />\n The\n <strong>served value</strong>\n is sent to SDKs and received by your applications. The\n <strong>display name</strong>\n appears in the targeting UI.\n <br />\n You can update a value or delete a variation only if it is not in use. As a help, you can see where each\n variation is used.\n </p>\n } @else {\n <p>\n The\n <strong>served value</strong>\n is sent to SDKs and received by your applications. The\n <strong>display name</strong>\n appears in the targeting UI.\n </p>\n }\n </div>\n\n <div class=\"dialog-table\" mat-dialog-content>\n <table\n mat-table\n cdkDropList\n [dataSource]=\"dataSource\"\n [cdkDropListData]=\"dataSource\"\n [cdkDropListDisabled]=\"!data.manage || settingType === SettingType.Boolean\"\n (cdkDropListDropped)=\"dropTable($event)\">\n <ng-container matColumnDef=\"reorder\">\n <th *matHeaderCellDef mat-header-cell></th>\n <td *matCellDef=\"let row; let i = index\" mat-cell class=\"row align-top\">\n <div class=\"drag-handle\">\n <div cdkDragHandle matTooltip=\"Drag here to reorder\" matTooltipPosition=\"above\">\n <mat-icon>drag_indicator</mat-icon>\n </div>\n </div>\n </td>\n </ng-container>\n\n <ng-container matColumnDef=\"color\">\n <th *matHeaderCellDef mat-header-cell></th>\n <td *matCellDef=\"let row; let i = index\" mat-cell class=\"row align-top\">\n <div class=\"predefined-component\">\n <span [class]=\"'variation-indicator variation-color-' + getColorIndex(row, i)\"></span>\n </div>\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 (settingType) {\n @case (SettingType.Boolean) {\n <ng-container\n [ngTemplateOutlet]=\"settingValueTemplate\"\n [ngTemplateOutletContext]=\"{\n formControl: row.controls.value.controls.boolValue,\n setFocus: i === focusIndex,\n }\" />\n }\n @case (SettingType.String) {\n <ng-container\n [ngTemplateOutlet]=\"settingValueTemplate\"\n [ngTemplateOutletContext]=\"{\n formControl: row.controls.value.controls.stringValue,\n setFocus: i === focusIndex,\n }\" />\n }\n @case (SettingType.Int) {\n <ng-container\n [ngTemplateOutlet]=\"settingValueTemplate\"\n [ngTemplateOutletContext]=\"{\n formControl: row.controls.value.controls.intValue,\n setFocus: i === focusIndex,\n }\" />\n }\n @case (SettingType.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 <div class=\"predefined-component\">\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\" class=\"small-form-field suffixed\">\n <input matInput [formControl]=\"row.controls.hint\" />\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 @if (row.controls.hint.invalid) {\n <mat-error>\n {{ formHelper.getErrorMessage(row.controls.hint) }}\n </mat-error>\n }\n </mat-form-field>\n </div>\n </td>\n </ng-container>\n <ng-container matColumnDef=\"variationId\">\n <th *matHeaderCellDef mat-header-cell>Variation ID</th>\n <td *matCellDef=\"let row\" mat-cell class=\"row align-top\">\n <app-copyable-value [value]=\"row.controls.variationId.value\" [small]=\"true\" />\n </td>\n </ng-container>\n <ng-container matColumnDef=\"usages\">\n <th *matHeaderCellDef mat-header-cell>Usages</th>\n <td *matCellDef=\"let row\" mat-cell class=\"row align-top\">\n <div class=\"usages\">\n <button\n type=\"button\"\n class=\"usage-button\"\n mat-icon-button\n [disabled]=\"\n !row.controls.usages.controls.length &&\n !row.controls.usagesInOtherEnvironments.value &&\n !row.controls.changeRequestUsagesInOtherEnvironments.value\n \"\n (click)=\"row.controls.usagesExpanded.setValue(!row.controls.usagesExpanded.value)\">\n @if (row.controls.usagesExpanded.value) {\n <mat-icon>expand_less</mat-icon>\n } @else {\n <mat-icon>expand_more</mat-icon>\n }\n </button>\n <div>\n @if (row.controls.usagesExpanded.value) {\n @for (usage of row.controls.usages.controls; track $index) {\n <div class=\"usage\">\n <a target=\"_blank\" rel=\"noopener noreferrer\" [href]=\"usage.controls.routerLink.value\">\n {{ usage.controls.displayValue.value }}\n </a>\n </div>\n }\n @if (row.controls.usagesInOtherEnvironments.value > 0) {\n {{ row.controls.usagesInOtherEnvironments.value }} usage{{\n row.controls.usagesInOtherEnvironments.value > 1 ? \"s\" : \"\"\n }}\n in environments you don't have access to.\n }\n @if (row.controls.changeRequestUsagesInOtherEnvironments.value > 0) {\n {{ row.controls.changeRequestUsagesInOtherEnvironments.value }} change request usage{{\n row.controls.changeRequestUsagesInOtherEnvironments.value > 1 ? \"s\" : \"\"\n }}\n in environments you don't have access to.\n }\n\n @if (\n row.controls.usagesInOtherEnvironments.value === 0 &&\n row.controls.usages.controls.length === 0 &&\n row.controls.changeRequestUsagesInOtherEnvironments.value === 0\n ) {\n <div>no usages</div>\n }\n } @else {\n <div>\n @if (\n row.controls.usages.controls.length +\n row.controls.usagesInOtherEnvironments.value +\n row.controls.changeRequestUsagesInOtherEnvironments.value >\n 0\n ) {\n {{\n row.controls.usages.controls.length +\n row.controls.usagesInOtherEnvironments.value +\n row.controls.changeRequestUsagesInOtherEnvironments.value\n }}\n usage{{\n row.controls.usages.controls.length +\n row.controls.usagesInOtherEnvironments.value +\n row.controls.changeRequestUsagesInOtherEnvironments.value >\n 1\n ? \"s\"\n : \"\"\n }}\n } @else {\n <div>no usages</div>\n }\n </div>\n }\n </div>\n </div>\n </td>\n </ng-container>\n\n <ng-container matColumnDef=\"actions\">\n <th *matHeaderCellDef mat-header-cell>{{ dataSource.data.length }} / {{ maxPredefinedVariations }}</th>\n <td *matCellDef=\"let row; let i = index\" mat-cell class=\"row align-top\">\n <div\n class=\"delete\"\n [matTooltip]=\"\n formGroup.controls.predefinedVariations.controls.length > 2\n ? 'Cannot remove a variation that is already in use.'\n : 'At least 2 predefined variations should be set.'\n \"\n [matTooltipDisabled]=\"\n row.controls.usages.controls.length === 0 &&\n row.controls.usagesInOtherEnvironments.value === 0 &&\n row.controls.changeRequestUsagesInOtherEnvironments.value === 0 &&\n formGroup.controls.predefinedVariations.controls.length > 2\n \">\n <button\n mat-icon-button\n type=\"button\"\n matTooltip=\"Remove variation\"\n [disabled]=\"\n formGroup.controls.predefinedVariations.controls.length <= 2 ||\n row.controls.usages.controls.length > 0 ||\n row.controls.usagesInOtherEnvironments.value > 0 ||\n row.controls.changeRequestUsagesInOtherEnvironments.value > 0\n \"\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 cdkDrag [cdkDragData]=\"row\"></tr>\n </table>\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>\n @if (data.manage && settingType !== SettingType.Boolean) {\n <div class=\"add-variation\" mat-dialog-content>\n <div\n [matTooltip]=\"'Maximum ' + maxPredefinedVariations + ' 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 </div>\n }\n @if (formGroup.errors && formGroup.errors[\"serverSide\"]) {\n <div class=\"error\" mat-dialog-content>\n <span>{{ formGroup.errors[\"serverSide\"] }}</span>\n </div>\n }\n </div>\n\n <div mat-dialog-actions>\n @if (data.manage) {\n <button\n type=\"submit\"\n mat-flat-button\n color=\"primary\"\n [disabled]=\"submitting() || !formGroup.dirty || !formGroup.valid\">\n Save\n </button>\n <button type=\"button\" mat-stroked-button [mat-dialog-close]>Cancel</button>\n } @else {\n <button type=\"button\" color=\"primary\" mat-flat-button [mat-dialog-close]>Close</button>\n }\n </div>\n </form>\n}\n\n<ng-template #settingValueTemplate let-formControl=\"formControl\" let-setFocus=\"setFocus\">\n @switch (settingType) {\n @case (SettingType.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 matTooltip=\"This variation is in use. The value cannot be changed.\"\n [matTooltipDisabled]=\"!formControl.disabled || !data.manage\"\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 (SettingType.Int) {\n <mat-form-field\n class=\"small-form-field\"\n appearance=\"outline\"\n subscriptSizing=\"dynamic\"\n matTooltip=\"This variation is in use. The value cannot be changed.\"\n [matTooltipDisabled]=\"!formControl.disabled || !data.manage\">\n @if (formControl.invalid) {\n <mat-error>\n {{ formHelper.getErrorMessage(formControl) }}\n </mat-error>\n }\n <input\n matInput\n type=\"number\"\n required\n name=\"index\"\n appDigitOnly\n focus\n [isFocused]=\"setFocus\"\n [formControl]=\"formControl\"\n [digitOnlyAllowNegatives]=\"true\" />\n </mat-form-field>\n }\n @case (SettingType.Double) {\n <mat-form-field\n class=\"small-form-field\"\n appearance=\"outline\"\n subscriptSizing=\"dynamic\"\n matTooltip=\"This variation is in use. The value cannot be changed.\"\n [matTooltipDisabled]=\"!formControl.disabled || !data.manage\">\n @if (formControl.invalid) {\n <mat-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 (SettingType.Boolean) {\n <div\n class=\"toggle\"\n matTooltip=\"This variation is in use. The value cannot be changed.\"\n [matTooltipDisabled]=\"!formControl.disabled || !data.manage\">\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", styles: [".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", ".content .mat-column-color{padding-left:4px!important;padding-right:4px!important;padding-top:12px}.content .mat-column-reorder{padding-right:0!important;padding-top:16px}.content .mat-column-usages{min-width:120px}.content .row.align-top{vertical-align:top}.content .delete{padding-top:6px}.content .drag-handle{cursor:-webkit-grab;cursor:-moz-grab;line-height:8px}.content .toggle{padding-left:4px}.content .usages{display:flex;align-items:center;padding:6px 0}.content .usages .usage{padding:2px 8px 2px 0}.content .usages .usage-button{align-self:flex-start}.content .predefined-component{padding:12px 0}.content .predefined-component.variationid{padding-top:15px}.content .small-error{max-width:180px}.content .add-variation{padding-top:8px;padding-bottom:8px;display:flex}.content .dialog-description{padding-top:8px;padding-bottom:16px}.content .centered{display:flex;align-items:center}.content mat-icon.info-icon{color:var(--info-icon-color);margin-left:8px}.content .dialog-table{padding-top:0;padding-bottom:0}.content .dialog-table .header-tooltip-icon{font-size:14px;width:14px;height:14px}.content .error{color:var(--flag-validation-error);padding-top:8px;padding-bottom:8px}\n"], dependencies: [{ kind: "component", type: LoaderComponent, selector: "app-loader", inputs: ["skipTimeOut"] }, { kind: "directive", type: MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { 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: "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: MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { 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: 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: MatDialogClose, selector: "[mat-dialog-close], [matDialogClose]", inputs: ["aria-label", "type", "mat-dialog-close", "matDialogClose"], exportAs: ["matDialogClose"] }, { 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: "component", type: MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", 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: DigitOnlyDirective, selector: "[appDigitOnly]", inputs: ["digitOnlyDecimal", "digitOnlyDecimalSeparator", "digitOnlyAllowNegatives", "digitOnlyAllowPaste", "digitOnlyNegativeSign"] }, { kind: "directive", type: MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "directive", type: CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { 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: CopyableValueComponent, selector: "app-copyable-value", inputs: ["tooltip", "tooltipClass", "hint", "warnHint", "name", "value", "small", "bold", "borderless", "smallest", "dark", "wholeControlCopyable"] }, { kind: "directive", type: AutofocusDirective, selector: "[focus]", inputs: ["isFocused"] }], changeDetection: i0.ChangeDetectionStrategy.Eager }); }
4559
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: VariationsDialogComponent, isStandalone: true, selector: "app-variations-dialog", ngImport: i0, template: "@if (setting.isLoading()) {\n <div mat-dialog-content class=\"content\">\n <app-loader />\n </div>\n} @else {\n <form [formGroup]=\"formGroup\" (ngSubmit)=\"onSubmit()\">\n <h1 mat-dialog-title>{{ data.manage ? \"Manage predefined variations\" : \"View predefined variations\" }}</h1>\n <div class=\"content\">\n <div class=\"dialog-description\" mat-dialog-content>\n @if (data.manage) {\n <p>\n Add, delete, and update predefined variations of the\n <strong>{{ data.settingName }}</strong>\n setting.\n <br />\n The\n <strong>served value</strong>\n is sent to SDKs and received by your applications. The\n <strong>display name</strong>\n appears in the targeting UI.\n <br />\n You can update a value or delete a variation only if it is not in use. As a help, you can see where each\n variation is used.\n </p>\n } @else {\n <p>\n The\n <strong>served value</strong>\n is sent to SDKs and received by your applications. The\n <strong>display name</strong>\n appears in the targeting UI.\n </p>\n }\n </div>\n\n <div class=\"dialog-table\" mat-dialog-content>\n <table\n mat-table\n cdkDropList\n [dataSource]=\"dataSource\"\n [cdkDropListData]=\"dataSource\"\n [cdkDropListDisabled]=\"!data.manage || settingType === SettingType.Boolean\"\n (cdkDropListDropped)=\"dropTable($event)\">\n <ng-container matColumnDef=\"reorder\">\n <th *matHeaderCellDef mat-header-cell></th>\n <td *matCellDef=\"let row; let i = index\" mat-cell class=\"row align-top\">\n <div class=\"drag-handle\">\n <div cdkDragHandle matTooltip=\"Drag here to reorder\" matTooltipPosition=\"above\">\n <mat-icon>drag_indicator</mat-icon>\n </div>\n </div>\n </td>\n </ng-container>\n\n <ng-container matColumnDef=\"color\">\n <th *matHeaderCellDef mat-header-cell></th>\n <td *matCellDef=\"let row; let i = index\" mat-cell class=\"row align-top\">\n <div class=\"predefined-component\">\n <span [class]=\"'variation-indicator variation-color-' + getColorIndex(row, i)\"></span>\n </div>\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 (settingType) {\n @case (SettingType.Boolean) {\n <ng-container\n [ngTemplateOutlet]=\"settingValueTemplate\"\n [ngTemplateOutletContext]=\"{\n formControl: row.controls.value.controls.boolValue,\n setFocus: i === focusIndex,\n }\" />\n }\n @case (SettingType.String) {\n <ng-container\n [ngTemplateOutlet]=\"settingValueTemplate\"\n [ngTemplateOutletContext]=\"{\n formControl: row.controls.value.controls.stringValue,\n setFocus: i === focusIndex,\n }\" />\n }\n @case (SettingType.Int) {\n <ng-container\n [ngTemplateOutlet]=\"settingValueTemplate\"\n [ngTemplateOutletContext]=\"{\n formControl: row.controls.value.controls.intValue,\n setFocus: i === focusIndex,\n }\" />\n }\n @case (SettingType.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 <div class=\"predefined-component\">\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\" class=\"small-form-field suffixed\">\n <input matInput [formControl]=\"row.controls.hint\" />\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 @if (row.controls.hint.invalid) {\n <mat-error>\n {{ formHelper.getErrorMessage(row.controls.hint) }}\n </mat-error>\n }\n </mat-form-field>\n </div>\n </td>\n </ng-container>\n <ng-container matColumnDef=\"variationId\">\n <th *matHeaderCellDef mat-header-cell>Variation ID</th>\n <td *matCellDef=\"let row\" mat-cell class=\"row align-top\">\n <app-copyable-value [value]=\"row.controls.variationId.value\" [small]=\"true\" />\n </td>\n </ng-container>\n <ng-container matColumnDef=\"usages\">\n <th *matHeaderCellDef mat-header-cell>Usages</th>\n <td *matCellDef=\"let row\" mat-cell class=\"row align-top\">\n <div class=\"usages\">\n <button\n type=\"button\"\n class=\"usage-button\"\n mat-icon-button\n [disabled]=\"\n !row.controls.usages.controls.length &&\n !row.controls.usagesInOtherEnvironments.value &&\n !row.controls.changeRequestUsagesInOtherEnvironments.value\n \"\n (click)=\"row.controls.usagesExpanded.setValue(!row.controls.usagesExpanded.value)\">\n @if (row.controls.usagesExpanded.value) {\n <mat-icon>expand_less</mat-icon>\n } @else {\n <mat-icon>expand_more</mat-icon>\n }\n </button>\n <div>\n @if (row.controls.usagesExpanded.value) {\n @for (usage of row.controls.usages.controls; track $index) {\n <div class=\"usage\">\n <a target=\"_blank\" rel=\"noopener noreferrer\" [href]=\"usage.controls.routerLink.value\">\n {{ usage.controls.displayValue.value }}\n </a>\n </div>\n }\n @if (row.controls.usagesInOtherEnvironments.value > 0) {\n {{ row.controls.usagesInOtherEnvironments.value }} usage{{\n row.controls.usagesInOtherEnvironments.value > 1 ? \"s\" : \"\"\n }}\n in environments you don't have access to.\n }\n @if (row.controls.changeRequestUsagesInOtherEnvironments.value > 0) {\n {{ row.controls.changeRequestUsagesInOtherEnvironments.value }} change request usage{{\n row.controls.changeRequestUsagesInOtherEnvironments.value > 1 ? \"s\" : \"\"\n }}\n in environments you don't have access to.\n }\n\n @if (\n row.controls.usagesInOtherEnvironments.value === 0 &&\n row.controls.usages.controls.length === 0 &&\n row.controls.changeRequestUsagesInOtherEnvironments.value === 0\n ) {\n <div>no usages</div>\n }\n } @else {\n <div>\n @if (\n row.controls.usages.controls.length +\n row.controls.usagesInOtherEnvironments.value +\n row.controls.changeRequestUsagesInOtherEnvironments.value >\n 0\n ) {\n {{\n row.controls.usages.controls.length +\n row.controls.usagesInOtherEnvironments.value +\n row.controls.changeRequestUsagesInOtherEnvironments.value\n }}\n usage{{\n row.controls.usages.controls.length +\n row.controls.usagesInOtherEnvironments.value +\n row.controls.changeRequestUsagesInOtherEnvironments.value >\n 1\n ? \"s\"\n : \"\"\n }}\n } @else {\n <div>no usages</div>\n }\n </div>\n }\n </div>\n </div>\n </td>\n </ng-container>\n\n <ng-container matColumnDef=\"actions\">\n <th *matHeaderCellDef mat-header-cell>{{ dataSource.data.length }} / {{ maxPredefinedVariations }}</th>\n <td *matCellDef=\"let row; let i = index\" mat-cell class=\"row align-top\">\n <div\n class=\"delete\"\n [matTooltip]=\"\n formGroup.controls.predefinedVariations.controls.length > 2\n ? 'Cannot remove a variation that is already in use.'\n : 'At least 2 predefined variations should be set.'\n \"\n [matTooltipDisabled]=\"\n row.controls.usages.controls.length === 0 &&\n row.controls.usagesInOtherEnvironments.value === 0 &&\n row.controls.changeRequestUsagesInOtherEnvironments.value === 0 &&\n formGroup.controls.predefinedVariations.controls.length > 2\n \">\n <button\n mat-icon-button\n type=\"button\"\n matTooltip=\"Remove variation\"\n [disabled]=\"\n formGroup.controls.predefinedVariations.controls.length <= 2 ||\n row.controls.usages.controls.length > 0 ||\n row.controls.usagesInOtherEnvironments.value > 0 ||\n row.controls.changeRequestUsagesInOtherEnvironments.value > 0\n \"\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 cdkDrag [cdkDragData]=\"row\"></tr>\n </table>\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>\n @if (data.manage && settingType !== SettingType.Boolean) {\n <div class=\"add-variation\" mat-dialog-content>\n <div\n [matTooltip]=\"'Maximum ' + maxPredefinedVariations + ' 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 </div>\n }\n @if (formGroup.errors && formGroup.errors[\"serverSide\"]) {\n <div class=\"error\" mat-dialog-content>\n <span>{{ formGroup.errors[\"serverSide\"] }}</span>\n </div>\n }\n </div>\n\n <div mat-dialog-actions>\n @if (data.manage) {\n <button\n type=\"submit\"\n mat-flat-button\n color=\"primary\"\n [disabled]=\"submitting() || !formGroup.dirty || !formGroup.valid\">\n Save\n </button>\n <button type=\"button\" mat-stroked-button [mat-dialog-close]>Cancel</button>\n } @else {\n <button type=\"button\" color=\"primary\" mat-flat-button [mat-dialog-close]>Close</button>\n }\n </div>\n </form>\n}\n\n<ng-template #settingValueTemplate let-formControl=\"formControl\" let-setFocus=\"setFocus\">\n @switch (settingType) {\n @case (SettingType.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 matTooltip=\"This variation is in use. The value cannot be changed.\"\n [matTooltipDisabled]=\"!formControl.disabled || !data.manage\"\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 (SettingType.Int) {\n <mat-form-field\n class=\"small-form-field\"\n appearance=\"outline\"\n subscriptSizing=\"dynamic\"\n matTooltip=\"This variation is in use. The value cannot be changed.\"\n [matTooltipDisabled]=\"!formControl.disabled || !data.manage\">\n @if (formControl.invalid) {\n <mat-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 (SettingType.Double) {\n <mat-form-field\n class=\"small-form-field\"\n appearance=\"outline\"\n subscriptSizing=\"dynamic\"\n matTooltip=\"This variation is in use. The value cannot be changed.\"\n [matTooltipDisabled]=\"!formControl.disabled || !data.manage\">\n @if (formControl.invalid) {\n <mat-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 (SettingType.Boolean) {\n <div\n class=\"toggle\"\n matTooltip=\"This variation is in use. The value cannot be changed.\"\n [matTooltipDisabled]=\"!formControl.disabled || !data.manage\">\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", styles: [".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", ".content .mat-column-color{padding-left:4px!important;padding-right:4px!important;padding-top:12px}.content .mat-column-reorder{padding-right:0!important;padding-top:16px}.content .mat-column-usages{min-width:120px}.content .row.align-top{vertical-align:top}.content .delete{padding-top:6px}.content .drag-handle{cursor:-webkit-grab;cursor:-moz-grab;line-height:8px}.content .toggle{padding-left:4px}.content .usages{display:flex;align-items:center;padding:6px 0}.content .usages .usage{padding:2px 8px 2px 0}.content .usages .usage-button{align-self:flex-start}.content .predefined-component{padding:12px 0}.content .predefined-component.variationid{padding-top:15px}.content .small-error{max-width:180px}.content .add-variation{padding-top:8px;padding-bottom:8px;display:flex}.content .dialog-description{padding-top:8px;padding-bottom:16px}.content .centered{display:flex;align-items:center}.content mat-icon.info-icon{color:var(--info-icon-color);margin-left:8px}.content .dialog-table{padding-top:0;padding-bottom:0}.content .dialog-table .header-tooltip-icon{font-size:14px;width:14px;height:14px}.content .error{color:var(--flag-validation-error);padding-top:8px;padding-bottom:8px}\n"], dependencies: [{ kind: "component", type: LoaderComponent, selector: "app-loader", inputs: ["skipTimeOut"] }, { kind: "directive", type: MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { 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: "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: MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { 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: 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: MatDialogClose, selector: "[mat-dialog-close], [matDialogClose]", inputs: ["aria-label", "type", "mat-dialog-close", "matDialogClose"], exportAs: ["matDialogClose"] }, { 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: "component", type: MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", 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: InputDisplayNormalizerDirective, selector: "input[inputDisplayNormalizer]" }, { kind: "directive", type: MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "directive", type: CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { 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: CopyableValueComponent, selector: "app-copyable-value", inputs: ["tooltip", "tooltipClass", "hint", "warnHint", "name", "value", "small", "bold", "borderless", "smallest", "dark", "wholeControlCopyable"] }, { kind: "directive", type: AutofocusDirective, selector: "[focus]", inputs: ["isFocused"] }], changeDetection: i0.ChangeDetectionStrategy.Eager }); }
4628
4560
  }
4629
4561
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: VariationsDialogComponent, decorators: [{
4630
4562
  type: Component,
@@ -4656,14 +4588,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImpor
4656
4588
  MatHeaderRow,
4657
4589
  MatRowDef,
4658
4590
  MatRow,
4659
- DigitOnlyDirective,
4591
+ InputDisplayNormalizerDirective,
4660
4592
  MatSuffix,
4661
4593
  CdkDropList,
4662
4594
  CdkDrag,
4663
4595
  CdkDragHandle,
4664
4596
  CopyableValueComponent,
4665
4597
  AutofocusDirective,
4666
- ], changeDetection: ChangeDetectionStrategy.Eager, template: "@if (setting.isLoading()) {\n <div mat-dialog-content class=\"content\">\n <app-loader />\n </div>\n} @else {\n <form [formGroup]=\"formGroup\" (ngSubmit)=\"onSubmit()\">\n <h1 mat-dialog-title>{{ data.manage ? \"Manage predefined variations\" : \"View predefined variations\" }}</h1>\n <div class=\"content\">\n <div class=\"dialog-description\" mat-dialog-content>\n @if (data.manage) {\n <p>\n Add, delete, and update predefined variations of the\n <strong>{{ data.settingName }}</strong>\n setting.\n <br />\n The\n <strong>served value</strong>\n is sent to SDKs and received by your applications. The\n <strong>display name</strong>\n appears in the targeting UI.\n <br />\n You can update a value or delete a variation only if it is not in use. As a help, you can see where each\n variation is used.\n </p>\n } @else {\n <p>\n The\n <strong>served value</strong>\n is sent to SDKs and received by your applications. The\n <strong>display name</strong>\n appears in the targeting UI.\n </p>\n }\n </div>\n\n <div class=\"dialog-table\" mat-dialog-content>\n <table\n mat-table\n cdkDropList\n [dataSource]=\"dataSource\"\n [cdkDropListData]=\"dataSource\"\n [cdkDropListDisabled]=\"!data.manage || settingType === SettingType.Boolean\"\n (cdkDropListDropped)=\"dropTable($event)\">\n <ng-container matColumnDef=\"reorder\">\n <th *matHeaderCellDef mat-header-cell></th>\n <td *matCellDef=\"let row; let i = index\" mat-cell class=\"row align-top\">\n <div class=\"drag-handle\">\n <div cdkDragHandle matTooltip=\"Drag here to reorder\" matTooltipPosition=\"above\">\n <mat-icon>drag_indicator</mat-icon>\n </div>\n </div>\n </td>\n </ng-container>\n\n <ng-container matColumnDef=\"color\">\n <th *matHeaderCellDef mat-header-cell></th>\n <td *matCellDef=\"let row; let i = index\" mat-cell class=\"row align-top\">\n <div class=\"predefined-component\">\n <span [class]=\"'variation-indicator variation-color-' + getColorIndex(row, i)\"></span>\n </div>\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 (settingType) {\n @case (SettingType.Boolean) {\n <ng-container\n [ngTemplateOutlet]=\"settingValueTemplate\"\n [ngTemplateOutletContext]=\"{\n formControl: row.controls.value.controls.boolValue,\n setFocus: i === focusIndex,\n }\" />\n }\n @case (SettingType.String) {\n <ng-container\n [ngTemplateOutlet]=\"settingValueTemplate\"\n [ngTemplateOutletContext]=\"{\n formControl: row.controls.value.controls.stringValue,\n setFocus: i === focusIndex,\n }\" />\n }\n @case (SettingType.Int) {\n <ng-container\n [ngTemplateOutlet]=\"settingValueTemplate\"\n [ngTemplateOutletContext]=\"{\n formControl: row.controls.value.controls.intValue,\n setFocus: i === focusIndex,\n }\" />\n }\n @case (SettingType.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 <div class=\"predefined-component\">\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\" class=\"small-form-field suffixed\">\n <input matInput [formControl]=\"row.controls.hint\" />\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 @if (row.controls.hint.invalid) {\n <mat-error>\n {{ formHelper.getErrorMessage(row.controls.hint) }}\n </mat-error>\n }\n </mat-form-field>\n </div>\n </td>\n </ng-container>\n <ng-container matColumnDef=\"variationId\">\n <th *matHeaderCellDef mat-header-cell>Variation ID</th>\n <td *matCellDef=\"let row\" mat-cell class=\"row align-top\">\n <app-copyable-value [value]=\"row.controls.variationId.value\" [small]=\"true\" />\n </td>\n </ng-container>\n <ng-container matColumnDef=\"usages\">\n <th *matHeaderCellDef mat-header-cell>Usages</th>\n <td *matCellDef=\"let row\" mat-cell class=\"row align-top\">\n <div class=\"usages\">\n <button\n type=\"button\"\n class=\"usage-button\"\n mat-icon-button\n [disabled]=\"\n !row.controls.usages.controls.length &&\n !row.controls.usagesInOtherEnvironments.value &&\n !row.controls.changeRequestUsagesInOtherEnvironments.value\n \"\n (click)=\"row.controls.usagesExpanded.setValue(!row.controls.usagesExpanded.value)\">\n @if (row.controls.usagesExpanded.value) {\n <mat-icon>expand_less</mat-icon>\n } @else {\n <mat-icon>expand_more</mat-icon>\n }\n </button>\n <div>\n @if (row.controls.usagesExpanded.value) {\n @for (usage of row.controls.usages.controls; track $index) {\n <div class=\"usage\">\n <a target=\"_blank\" rel=\"noopener noreferrer\" [href]=\"usage.controls.routerLink.value\">\n {{ usage.controls.displayValue.value }}\n </a>\n </div>\n }\n @if (row.controls.usagesInOtherEnvironments.value > 0) {\n {{ row.controls.usagesInOtherEnvironments.value }} usage{{\n row.controls.usagesInOtherEnvironments.value > 1 ? \"s\" : \"\"\n }}\n in environments you don't have access to.\n }\n @if (row.controls.changeRequestUsagesInOtherEnvironments.value > 0) {\n {{ row.controls.changeRequestUsagesInOtherEnvironments.value }} change request usage{{\n row.controls.changeRequestUsagesInOtherEnvironments.value > 1 ? \"s\" : \"\"\n }}\n in environments you don't have access to.\n }\n\n @if (\n row.controls.usagesInOtherEnvironments.value === 0 &&\n row.controls.usages.controls.length === 0 &&\n row.controls.changeRequestUsagesInOtherEnvironments.value === 0\n ) {\n <div>no usages</div>\n }\n } @else {\n <div>\n @if (\n row.controls.usages.controls.length +\n row.controls.usagesInOtherEnvironments.value +\n row.controls.changeRequestUsagesInOtherEnvironments.value >\n 0\n ) {\n {{\n row.controls.usages.controls.length +\n row.controls.usagesInOtherEnvironments.value +\n row.controls.changeRequestUsagesInOtherEnvironments.value\n }}\n usage{{\n row.controls.usages.controls.length +\n row.controls.usagesInOtherEnvironments.value +\n row.controls.changeRequestUsagesInOtherEnvironments.value >\n 1\n ? \"s\"\n : \"\"\n }}\n } @else {\n <div>no usages</div>\n }\n </div>\n }\n </div>\n </div>\n </td>\n </ng-container>\n\n <ng-container matColumnDef=\"actions\">\n <th *matHeaderCellDef mat-header-cell>{{ dataSource.data.length }} / {{ maxPredefinedVariations }}</th>\n <td *matCellDef=\"let row; let i = index\" mat-cell class=\"row align-top\">\n <div\n class=\"delete\"\n [matTooltip]=\"\n formGroup.controls.predefinedVariations.controls.length > 2\n ? 'Cannot remove a variation that is already in use.'\n : 'At least 2 predefined variations should be set.'\n \"\n [matTooltipDisabled]=\"\n row.controls.usages.controls.length === 0 &&\n row.controls.usagesInOtherEnvironments.value === 0 &&\n row.controls.changeRequestUsagesInOtherEnvironments.value === 0 &&\n formGroup.controls.predefinedVariations.controls.length > 2\n \">\n <button\n mat-icon-button\n type=\"button\"\n matTooltip=\"Remove variation\"\n [disabled]=\"\n formGroup.controls.predefinedVariations.controls.length <= 2 ||\n row.controls.usages.controls.length > 0 ||\n row.controls.usagesInOtherEnvironments.value > 0 ||\n row.controls.changeRequestUsagesInOtherEnvironments.value > 0\n \"\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 cdkDrag [cdkDragData]=\"row\"></tr>\n </table>\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>\n @if (data.manage && settingType !== SettingType.Boolean) {\n <div class=\"add-variation\" mat-dialog-content>\n <div\n [matTooltip]=\"'Maximum ' + maxPredefinedVariations + ' 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 </div>\n }\n @if (formGroup.errors && formGroup.errors[\"serverSide\"]) {\n <div class=\"error\" mat-dialog-content>\n <span>{{ formGroup.errors[\"serverSide\"] }}</span>\n </div>\n }\n </div>\n\n <div mat-dialog-actions>\n @if (data.manage) {\n <button\n type=\"submit\"\n mat-flat-button\n color=\"primary\"\n [disabled]=\"submitting() || !formGroup.dirty || !formGroup.valid\">\n Save\n </button>\n <button type=\"button\" mat-stroked-button [mat-dialog-close]>Cancel</button>\n } @else {\n <button type=\"button\" color=\"primary\" mat-flat-button [mat-dialog-close]>Close</button>\n }\n </div>\n </form>\n}\n\n<ng-template #settingValueTemplate let-formControl=\"formControl\" let-setFocus=\"setFocus\">\n @switch (settingType) {\n @case (SettingType.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 matTooltip=\"This variation is in use. The value cannot be changed.\"\n [matTooltipDisabled]=\"!formControl.disabled || !data.manage\"\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 (SettingType.Int) {\n <mat-form-field\n class=\"small-form-field\"\n appearance=\"outline\"\n subscriptSizing=\"dynamic\"\n matTooltip=\"This variation is in use. The value cannot be changed.\"\n [matTooltipDisabled]=\"!formControl.disabled || !data.manage\">\n @if (formControl.invalid) {\n <mat-error>\n {{ formHelper.getErrorMessage(formControl) }}\n </mat-error>\n }\n <input\n matInput\n type=\"number\"\n required\n name=\"index\"\n appDigitOnly\n focus\n [isFocused]=\"setFocus\"\n [formControl]=\"formControl\"\n [digitOnlyAllowNegatives]=\"true\" />\n </mat-form-field>\n }\n @case (SettingType.Double) {\n <mat-form-field\n class=\"small-form-field\"\n appearance=\"outline\"\n subscriptSizing=\"dynamic\"\n matTooltip=\"This variation is in use. The value cannot be changed.\"\n [matTooltipDisabled]=\"!formControl.disabled || !data.manage\">\n @if (formControl.invalid) {\n <mat-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 (SettingType.Boolean) {\n <div\n class=\"toggle\"\n matTooltip=\"This variation is in use. The value cannot be changed.\"\n [matTooltipDisabled]=\"!formControl.disabled || !data.manage\">\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", styles: [".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", ".content .mat-column-color{padding-left:4px!important;padding-right:4px!important;padding-top:12px}.content .mat-column-reorder{padding-right:0!important;padding-top:16px}.content .mat-column-usages{min-width:120px}.content .row.align-top{vertical-align:top}.content .delete{padding-top:6px}.content .drag-handle{cursor:-webkit-grab;cursor:-moz-grab;line-height:8px}.content .toggle{padding-left:4px}.content .usages{display:flex;align-items:center;padding:6px 0}.content .usages .usage{padding:2px 8px 2px 0}.content .usages .usage-button{align-self:flex-start}.content .predefined-component{padding:12px 0}.content .predefined-component.variationid{padding-top:15px}.content .small-error{max-width:180px}.content .add-variation{padding-top:8px;padding-bottom:8px;display:flex}.content .dialog-description{padding-top:8px;padding-bottom:16px}.content .centered{display:flex;align-items:center}.content mat-icon.info-icon{color:var(--info-icon-color);margin-left:8px}.content .dialog-table{padding-top:0;padding-bottom:0}.content .dialog-table .header-tooltip-icon{font-size:14px;width:14px;height:14px}.content .error{color:var(--flag-validation-error);padding-top:8px;padding-bottom:8px}\n"] }]
4598
+ ], changeDetection: ChangeDetectionStrategy.Eager, template: "@if (setting.isLoading()) {\n <div mat-dialog-content class=\"content\">\n <app-loader />\n </div>\n} @else {\n <form [formGroup]=\"formGroup\" (ngSubmit)=\"onSubmit()\">\n <h1 mat-dialog-title>{{ data.manage ? \"Manage predefined variations\" : \"View predefined variations\" }}</h1>\n <div class=\"content\">\n <div class=\"dialog-description\" mat-dialog-content>\n @if (data.manage) {\n <p>\n Add, delete, and update predefined variations of the\n <strong>{{ data.settingName }}</strong>\n setting.\n <br />\n The\n <strong>served value</strong>\n is sent to SDKs and received by your applications. The\n <strong>display name</strong>\n appears in the targeting UI.\n <br />\n You can update a value or delete a variation only if it is not in use. As a help, you can see where each\n variation is used.\n </p>\n } @else {\n <p>\n The\n <strong>served value</strong>\n is sent to SDKs and received by your applications. The\n <strong>display name</strong>\n appears in the targeting UI.\n </p>\n }\n </div>\n\n <div class=\"dialog-table\" mat-dialog-content>\n <table\n mat-table\n cdkDropList\n [dataSource]=\"dataSource\"\n [cdkDropListData]=\"dataSource\"\n [cdkDropListDisabled]=\"!data.manage || settingType === SettingType.Boolean\"\n (cdkDropListDropped)=\"dropTable($event)\">\n <ng-container matColumnDef=\"reorder\">\n <th *matHeaderCellDef mat-header-cell></th>\n <td *matCellDef=\"let row; let i = index\" mat-cell class=\"row align-top\">\n <div class=\"drag-handle\">\n <div cdkDragHandle matTooltip=\"Drag here to reorder\" matTooltipPosition=\"above\">\n <mat-icon>drag_indicator</mat-icon>\n </div>\n </div>\n </td>\n </ng-container>\n\n <ng-container matColumnDef=\"color\">\n <th *matHeaderCellDef mat-header-cell></th>\n <td *matCellDef=\"let row; let i = index\" mat-cell class=\"row align-top\">\n <div class=\"predefined-component\">\n <span [class]=\"'variation-indicator variation-color-' + getColorIndex(row, i)\"></span>\n </div>\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 (settingType) {\n @case (SettingType.Boolean) {\n <ng-container\n [ngTemplateOutlet]=\"settingValueTemplate\"\n [ngTemplateOutletContext]=\"{\n formControl: row.controls.value.controls.boolValue,\n setFocus: i === focusIndex,\n }\" />\n }\n @case (SettingType.String) {\n <ng-container\n [ngTemplateOutlet]=\"settingValueTemplate\"\n [ngTemplateOutletContext]=\"{\n formControl: row.controls.value.controls.stringValue,\n setFocus: i === focusIndex,\n }\" />\n }\n @case (SettingType.Int) {\n <ng-container\n [ngTemplateOutlet]=\"settingValueTemplate\"\n [ngTemplateOutletContext]=\"{\n formControl: row.controls.value.controls.intValue,\n setFocus: i === focusIndex,\n }\" />\n }\n @case (SettingType.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 <div class=\"predefined-component\">\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\" class=\"small-form-field suffixed\">\n <input matInput [formControl]=\"row.controls.hint\" />\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 @if (row.controls.hint.invalid) {\n <mat-error>\n {{ formHelper.getErrorMessage(row.controls.hint) }}\n </mat-error>\n }\n </mat-form-field>\n </div>\n </td>\n </ng-container>\n <ng-container matColumnDef=\"variationId\">\n <th *matHeaderCellDef mat-header-cell>Variation ID</th>\n <td *matCellDef=\"let row\" mat-cell class=\"row align-top\">\n <app-copyable-value [value]=\"row.controls.variationId.value\" [small]=\"true\" />\n </td>\n </ng-container>\n <ng-container matColumnDef=\"usages\">\n <th *matHeaderCellDef mat-header-cell>Usages</th>\n <td *matCellDef=\"let row\" mat-cell class=\"row align-top\">\n <div class=\"usages\">\n <button\n type=\"button\"\n class=\"usage-button\"\n mat-icon-button\n [disabled]=\"\n !row.controls.usages.controls.length &&\n !row.controls.usagesInOtherEnvironments.value &&\n !row.controls.changeRequestUsagesInOtherEnvironments.value\n \"\n (click)=\"row.controls.usagesExpanded.setValue(!row.controls.usagesExpanded.value)\">\n @if (row.controls.usagesExpanded.value) {\n <mat-icon>expand_less</mat-icon>\n } @else {\n <mat-icon>expand_more</mat-icon>\n }\n </button>\n <div>\n @if (row.controls.usagesExpanded.value) {\n @for (usage of row.controls.usages.controls; track $index) {\n <div class=\"usage\">\n <a target=\"_blank\" rel=\"noopener noreferrer\" [href]=\"usage.controls.routerLink.value\">\n {{ usage.controls.displayValue.value }}\n </a>\n </div>\n }\n @if (row.controls.usagesInOtherEnvironments.value > 0) {\n {{ row.controls.usagesInOtherEnvironments.value }} usage{{\n row.controls.usagesInOtherEnvironments.value > 1 ? \"s\" : \"\"\n }}\n in environments you don't have access to.\n }\n @if (row.controls.changeRequestUsagesInOtherEnvironments.value > 0) {\n {{ row.controls.changeRequestUsagesInOtherEnvironments.value }} change request usage{{\n row.controls.changeRequestUsagesInOtherEnvironments.value > 1 ? \"s\" : \"\"\n }}\n in environments you don't have access to.\n }\n\n @if (\n row.controls.usagesInOtherEnvironments.value === 0 &&\n row.controls.usages.controls.length === 0 &&\n row.controls.changeRequestUsagesInOtherEnvironments.value === 0\n ) {\n <div>no usages</div>\n }\n } @else {\n <div>\n @if (\n row.controls.usages.controls.length +\n row.controls.usagesInOtherEnvironments.value +\n row.controls.changeRequestUsagesInOtherEnvironments.value >\n 0\n ) {\n {{\n row.controls.usages.controls.length +\n row.controls.usagesInOtherEnvironments.value +\n row.controls.changeRequestUsagesInOtherEnvironments.value\n }}\n usage{{\n row.controls.usages.controls.length +\n row.controls.usagesInOtherEnvironments.value +\n row.controls.changeRequestUsagesInOtherEnvironments.value >\n 1\n ? \"s\"\n : \"\"\n }}\n } @else {\n <div>no usages</div>\n }\n </div>\n }\n </div>\n </div>\n </td>\n </ng-container>\n\n <ng-container matColumnDef=\"actions\">\n <th *matHeaderCellDef mat-header-cell>{{ dataSource.data.length }} / {{ maxPredefinedVariations }}</th>\n <td *matCellDef=\"let row; let i = index\" mat-cell class=\"row align-top\">\n <div\n class=\"delete\"\n [matTooltip]=\"\n formGroup.controls.predefinedVariations.controls.length > 2\n ? 'Cannot remove a variation that is already in use.'\n : 'At least 2 predefined variations should be set.'\n \"\n [matTooltipDisabled]=\"\n row.controls.usages.controls.length === 0 &&\n row.controls.usagesInOtherEnvironments.value === 0 &&\n row.controls.changeRequestUsagesInOtherEnvironments.value === 0 &&\n formGroup.controls.predefinedVariations.controls.length > 2\n \">\n <button\n mat-icon-button\n type=\"button\"\n matTooltip=\"Remove variation\"\n [disabled]=\"\n formGroup.controls.predefinedVariations.controls.length <= 2 ||\n row.controls.usages.controls.length > 0 ||\n row.controls.usagesInOtherEnvironments.value > 0 ||\n row.controls.changeRequestUsagesInOtherEnvironments.value > 0\n \"\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 cdkDrag [cdkDragData]=\"row\"></tr>\n </table>\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>\n @if (data.manage && settingType !== SettingType.Boolean) {\n <div class=\"add-variation\" mat-dialog-content>\n <div\n [matTooltip]=\"'Maximum ' + maxPredefinedVariations + ' 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 </div>\n }\n @if (formGroup.errors && formGroup.errors[\"serverSide\"]) {\n <div class=\"error\" mat-dialog-content>\n <span>{{ formGroup.errors[\"serverSide\"] }}</span>\n </div>\n }\n </div>\n\n <div mat-dialog-actions>\n @if (data.manage) {\n <button\n type=\"submit\"\n mat-flat-button\n color=\"primary\"\n [disabled]=\"submitting() || !formGroup.dirty || !formGroup.valid\">\n Save\n </button>\n <button type=\"button\" mat-stroked-button [mat-dialog-close]>Cancel</button>\n } @else {\n <button type=\"button\" color=\"primary\" mat-flat-button [mat-dialog-close]>Close</button>\n }\n </div>\n </form>\n}\n\n<ng-template #settingValueTemplate let-formControl=\"formControl\" let-setFocus=\"setFocus\">\n @switch (settingType) {\n @case (SettingType.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 matTooltip=\"This variation is in use. The value cannot be changed.\"\n [matTooltipDisabled]=\"!formControl.disabled || !data.manage\"\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 (SettingType.Int) {\n <mat-form-field\n class=\"small-form-field\"\n appearance=\"outline\"\n subscriptSizing=\"dynamic\"\n matTooltip=\"This variation is in use. The value cannot be changed.\"\n [matTooltipDisabled]=\"!formControl.disabled || !data.manage\">\n @if (formControl.invalid) {\n <mat-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 (SettingType.Double) {\n <mat-form-field\n class=\"small-form-field\"\n appearance=\"outline\"\n subscriptSizing=\"dynamic\"\n matTooltip=\"This variation is in use. The value cannot be changed.\"\n [matTooltipDisabled]=\"!formControl.disabled || !data.manage\">\n @if (formControl.invalid) {\n <mat-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 (SettingType.Boolean) {\n <div\n class=\"toggle\"\n matTooltip=\"This variation is in use. The value cannot be changed.\"\n [matTooltipDisabled]=\"!formControl.disabled || !data.manage\">\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", styles: [".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", ".content .mat-column-color{padding-left:4px!important;padding-right:4px!important;padding-top:12px}.content .mat-column-reorder{padding-right:0!important;padding-top:16px}.content .mat-column-usages{min-width:120px}.content .row.align-top{vertical-align:top}.content .delete{padding-top:6px}.content .drag-handle{cursor:-webkit-grab;cursor:-moz-grab;line-height:8px}.content .toggle{padding-left:4px}.content .usages{display:flex;align-items:center;padding:6px 0}.content .usages .usage{padding:2px 8px 2px 0}.content .usages .usage-button{align-self:flex-start}.content .predefined-component{padding:12px 0}.content .predefined-component.variationid{padding-top:15px}.content .small-error{max-width:180px}.content .add-variation{padding-top:8px;padding-bottom:8px;display:flex}.content .dialog-description{padding-top:8px;padding-bottom:16px}.content .centered{display:flex;align-items:center}.content mat-icon.info-icon{color:var(--info-icon-color);margin-left:8px}.content .dialog-table{padding-top:0;padding-bottom:0}.content .dialog-table .header-tooltip-icon{font-size:14px;width:14px;height:14px}.content .error{color:var(--flag-validation-error);padding-top:8px;padding-bottom:8px}\n"] }]
4667
4599
  }] });
4668
4600
 
4669
4601
  class DependeesDialogComponent {
@@ -4792,7 +4724,7 @@ class FeatureFlagValueComponent {
4792
4724
  }
4793
4725
  }
4794
4726
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: FeatureFlagValueComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4795
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: FeatureFlagValueComponent, isStandalone: true, selector: "app-feature-flag-value", inputs: { formGroup: { classPropertyName: "formGroup", publicName: "formGroup", isSignal: true, isRequired: true, transformFunction: null }, setting: { classPropertyName: "setting", publicName: "setting", isSignal: true, isRequired: true, transformFunction: null }, canCreateOrUpdateSetting: { classPropertyName: "canCreateOrUpdateSetting", publicName: "canCreateOrUpdateSetting", isSignal: true, isRequired: true, transformFunction: null }, notAvailablePredefinedVariationIds: { classPropertyName: "notAvailablePredefinedVariationIds", publicName: "notAvailablePredefinedVariationIds", isSignal: true, isRequired: false, transformFunction: null }, featureFlagLimitations: { classPropertyName: "featureFlagLimitations", publicName: "featureFlagLimitations", isSignal: true, isRequired: true, transformFunction: null }, isPrerequisite: { classPropertyName: "isPrerequisite", publicName: "isPrerequisite", isSignal: true, isRequired: false, transformFunction: null }, tooltip: { classPropertyName: "tooltip", publicName: "tooltip", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { viewVariationsRequested: "viewVariationsRequested" }, viewQueries: [{ propertyName: "tooltips", predicate: MatTooltip, descendants: true, isSignal: true }], ngImport: i0, template: "@let formGroupValue = formGroup();\n\n<div class=\"feature-flag-value\">\n @if (formGroupValue.controls.predefinedVariationId) {\n <div class=\"setting-variation\" [ngClass]=\"{ prerequisite: isPrerequisite() }\">\n <mat-form-field appearance=\"outline\" class=\"small-form-field select-suffixed\" subscriptSizing=\"dynamic\">\n @let selectedPredefinedVariation = getPredefinedVariation(formGroupValue.controls.predefinedVariationId.value);\n @let variationName = selectedPredefinedVariation?.name;\n @let variationHint = selectedPredefinedVariation?.hint;\n <mat-select\n #predefinedVariationSelect=\"matSelect\"\n disableOptionCentering\n [formControl]=\"formGroupValue.controls.predefinedVariationId\"\n [matTooltip]=\"\n `${tooltip()}: ${getServedValueText(selectedPredefinedVariation?.value)} (type: ${setting().settingType})` +\n `\\nName: ${variationName || getDefaultDisplayNameText(selectedPredefinedVariation?.value)}` +\n `${variationHint ? `\\nHint: ${variationHint}` : ''}`\n \"\n [matTooltipClass]=\"['wide-tooltip', 'multiline-tooltip', 'left-aligned']\"\n (openedChange)=\"onPredefinedVariationSelectOpenedChange($event)\">\n <mat-select-trigger>\n @if (selectedPredefinedVariation) {\n <div class=\"variation-option\">\n <span [class]=\"'variation-indicator variation-color-' + selectedPredefinedVariation.colorIndex\"></span>\n @if (selectedPredefinedVariation.name) {\n <em class=\"variation-name\">\n {{ selectedPredefinedVariation.name }}\n </em>\n } @else {\n <span>\n {{\n getPredefinedVariationValueDisplayValue(setting().settingType, selectedPredefinedVariation.value)\n }}\n </span>\n }\n </div>\n }\n </mat-select-trigger>\n\n @for (predefinedVariation of predefinedVariations(); track predefinedVariation.predefinedVariationId) {\n <span\n [matTooltipClass]=\"['wide-tooltip', 'multiline-tooltip', 'left-aligned']\"\n [matTooltip]=\"\n !predefinedVariation.isAvailable\n ? 'The predefined variation is already selected in another percentage option.'\n : (predefinedVariation.hint ?? '')\n \"\n [matTooltipDisabled]=\"\n !predefinedVariationSelect.panelOpen || (predefinedVariation.isAvailable && !predefinedVariation.hint)\n \">\n <mat-option\n [value]=\"predefinedVariation.predefinedVariationId\"\n [disabled]=\"!predefinedVariation.isAvailable\">\n <div class=\"variation-option\">\n <span [class]=\"'variation-indicator variation-color-' + predefinedVariation.colorIndex\"></span>\n @if (predefinedVariation.name) {\n <em class=\"variation-name\">\n {{ predefinedVariation.name }}\n </em>\n } @else {\n <span>\n {{ getPredefinedVariationValueDisplayValue(setting().settingType, predefinedVariation.value) }}\n </span>\n }\n </div>\n </mat-option>\n </span>\n }\n </mat-select>\n <button\n matSuffix\n class=\"input-suffix-button\"\n type=\"button\"\n mat-icon-button\n [matTooltip]=\"canCreateOrUpdateSetting() ? 'Manage predefined variations' : 'View predefined variations'\"\n (click)=\"\n $event.stopPropagation();\n viewVariationsRequested.emit({ settingId: setting().settingId, settingName: setting().name })\n \">\n <mat-icon class=\"icon-transform-rotate-90\">call_split</mat-icon>\n </button>\n @if (formGroupValue.controls.predefinedVariationId.invalid) {\n <mat-error>\n {{ FormHelper.getErrorMessage(formGroupValue.controls.predefinedVariationId) }}\n </mat-error>\n }\n </mat-form-field>\n </div>\n } @else if (formGroupValue.controls.stringValue) {\n <mat-form-field appearance=\"outline\" class=\"small-form-field suffixed\" subscriptSizing=\"dynamic\">\n <input\n matInput\n type=\"text\"\n placeholder=\"Add text ...\"\n appPastedDataTrimmer\n [formControl]=\"formGroupValue.controls.stringValue\"\n [matTooltip]=\"`${tooltip()}: ${getServedValueText(formGroupValue.controls)} (type: ${setting().settingType})`\"\n [matTooltipClass]=\"['wide-tooltip', 'multiline-tooltip', 'left-aligned']\" />\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 (formGroupValue.controls.stringValue.invalid) {\n <mat-error>\n {{ FormHelper.getErrorMessage(formGroupValue.controls.stringValue) }}\n </mat-error>\n }\n </mat-form-field>\n } @else if (formGroupValue.controls.intValue) {\n <mat-form-field appearance=\"outline\" class=\"small-form-field\" subscriptSizing=\"dynamic\">\n <input\n matInput\n type=\"number\"\n appDigitOnly\n inputDisplayNormalizer\n placeholder=\"Add number ...\"\n [formControl]=\"formGroupValue.controls.intValue\"\n [digitOnlyAllowNegatives]=\"true\"\n [matTooltip]=\"`${tooltip()}: ${getServedValueText(formGroupValue.controls)} (type: ${setting().settingType})`\"\n [matTooltipClass]=\"['wide-tooltip', 'multiline-tooltip', 'left-aligned']\" />\n @if (formGroupValue.controls.intValue.invalid) {\n <mat-error>\n {{ FormHelper.getErrorMessage(formGroupValue.controls.intValue) }}\n </mat-error>\n }\n </mat-form-field>\n } @else if (formGroupValue.controls.doubleValue) {\n <mat-form-field appearance=\"outline\" class=\"small-form-field\" subscriptSizing=\"dynamic\">\n <input\n matInput\n type=\"number\"\n inputDisplayNormalizer\n placeholder=\"Add number ...\"\n [formControl]=\"formGroupValue.controls.doubleValue\"\n [matTooltip]=\"`${tooltip()}: ${getServedValueText(formGroupValue.controls)} (type: ${setting().settingType})`\"\n [matTooltipClass]=\"['wide-tooltip', 'multiline-tooltip', 'left-aligned']\" />\n @if (formGroupValue.controls.doubleValue.invalid) {\n <mat-error>\n {{ FormHelper.getErrorMessage(formGroupValue.controls.doubleValue) }}\n </mat-error>\n }\n </mat-form-field>\n } @else if (formGroupValue.controls.boolValue) {\n <div class=\"bool\" [class.readonly]=\"formGroupValue.controls.boolValue.disabled\">\n <label\n class=\"switch\"\n [matTooltip]=\"`${tooltip()}: ${getServedValueText(formGroupValue.controls)} (type: ${setting().settingType})`\"\n [matTooltipClass]=\"['wide-tooltip', 'multiline-tooltip', 'left-aligned']\">\n <input type=\"checkbox\" [formControl]=\"formGroupValue.controls.boolValue\" />\n <span class=\"slider\"></span>\n </label>\n </div>\n }\n</div>\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", ".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", ":host{display:flex;flex-grow:1}.feature-flag-value{display:flex;flex-grow:1;margin:2px 0;min-width:0}.feature-flag-value .bool{flex-grow:1;align-items:flex-end;display:flex;flex-direction:column}.feature-flag-value .setting-variation{display:flex;flex-direction:column;flex-grow:1;min-width:0}.feature-flag-value .setting-variation:not(.prerequisite){align-items:flex-end}.feature-flag-value .setting-variation mat-form-field{width:212px}.feature-flag-value .readonly{opacity:.6}.feature-flag-value .variationId{margin:4px 0 0 8px;cursor:pointer;color:var(--action-icon-button-color);display:flex}.feature-flag-value mat-form-field{width:100%}.variation-option{display:flex;align-items:center}.variation-option .variation-name{padding-right:2px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.variation-option .variation-indicator{margin-right:8px}mat-select{pointer-events:auto}@media(max-width:500px){.setting-variation mat-form-field{max-width:calc(100% - 4px)}}\n"], dependencies: [{ 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: "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.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: "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: MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { 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: MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: InputDisplayNormalizerDirective, selector: "input[inputDisplayNormalizer]" }, { kind: "directive", type: DigitOnlyDirective, selector: "[appDigitOnly]", inputs: ["digitOnlyDecimal", "digitOnlyDecimalSeparator", "digitOnlyAllowNegatives", "digitOnlyAllowPaste", "digitOnlyNegativeSign"] }, { 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: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: MatSelectTrigger, selector: "mat-select-trigger" }, { kind: "directive", type: PastedDataTrimmerDirective, selector: "[appPastedDataTrimmer]" }], changeDetection: i0.ChangeDetectionStrategy.Eager }); }
4727
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: FeatureFlagValueComponent, isStandalone: true, selector: "app-feature-flag-value", inputs: { formGroup: { classPropertyName: "formGroup", publicName: "formGroup", isSignal: true, isRequired: true, transformFunction: null }, setting: { classPropertyName: "setting", publicName: "setting", isSignal: true, isRequired: true, transformFunction: null }, canCreateOrUpdateSetting: { classPropertyName: "canCreateOrUpdateSetting", publicName: "canCreateOrUpdateSetting", isSignal: true, isRequired: true, transformFunction: null }, notAvailablePredefinedVariationIds: { classPropertyName: "notAvailablePredefinedVariationIds", publicName: "notAvailablePredefinedVariationIds", isSignal: true, isRequired: false, transformFunction: null }, featureFlagLimitations: { classPropertyName: "featureFlagLimitations", publicName: "featureFlagLimitations", isSignal: true, isRequired: true, transformFunction: null }, isPrerequisite: { classPropertyName: "isPrerequisite", publicName: "isPrerequisite", isSignal: true, isRequired: false, transformFunction: null }, tooltip: { classPropertyName: "tooltip", publicName: "tooltip", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { viewVariationsRequested: "viewVariationsRequested" }, viewQueries: [{ propertyName: "tooltips", predicate: MatTooltip, descendants: true, isSignal: true }], ngImport: i0, template: "@let formGroupValue = formGroup();\n\n<div class=\"feature-flag-value\">\n @if (formGroupValue.controls.predefinedVariationId) {\n <div class=\"setting-variation\" [ngClass]=\"{ prerequisite: isPrerequisite() }\">\n <mat-form-field appearance=\"outline\" class=\"small-form-field select-suffixed\" subscriptSizing=\"dynamic\">\n @let selectedPredefinedVariation = getPredefinedVariation(formGroupValue.controls.predefinedVariationId.value);\n @let variationName = selectedPredefinedVariation?.name;\n @let variationHint = selectedPredefinedVariation?.hint;\n <mat-select\n #predefinedVariationSelect=\"matSelect\"\n disableOptionCentering\n [formControl]=\"formGroupValue.controls.predefinedVariationId\"\n [matTooltip]=\"\n `${tooltip()}: ${getServedValueText(selectedPredefinedVariation?.value)} (type: ${setting().settingType})` +\n `\\nName: ${variationName || getDefaultDisplayNameText(selectedPredefinedVariation?.value)}` +\n `${variationHint ? `\\nHint: ${variationHint}` : ''}`\n \"\n [matTooltipClass]=\"['wide-tooltip', 'multiline-tooltip', 'left-aligned']\"\n (openedChange)=\"onPredefinedVariationSelectOpenedChange($event)\">\n <mat-select-trigger>\n @if (selectedPredefinedVariation) {\n <div class=\"variation-option\">\n <span [class]=\"'variation-indicator variation-color-' + selectedPredefinedVariation.colorIndex\"></span>\n @if (selectedPredefinedVariation.name) {\n <em class=\"variation-name\">\n {{ selectedPredefinedVariation.name }}\n </em>\n } @else {\n <span>\n {{\n getPredefinedVariationValueDisplayValue(setting().settingType, selectedPredefinedVariation.value)\n }}\n </span>\n }\n </div>\n }\n </mat-select-trigger>\n\n @for (predefinedVariation of predefinedVariations(); track predefinedVariation.predefinedVariationId) {\n <span\n [matTooltipClass]=\"['wide-tooltip', 'multiline-tooltip', 'left-aligned']\"\n [matTooltip]=\"\n !predefinedVariation.isAvailable\n ? 'The predefined variation is already selected in another percentage option.'\n : (predefinedVariation.hint ?? '')\n \"\n [matTooltipDisabled]=\"\n !predefinedVariationSelect.panelOpen || (predefinedVariation.isAvailable && !predefinedVariation.hint)\n \">\n <mat-option\n [value]=\"predefinedVariation.predefinedVariationId\"\n [disabled]=\"!predefinedVariation.isAvailable\">\n <div class=\"variation-option\">\n <span [class]=\"'variation-indicator variation-color-' + predefinedVariation.colorIndex\"></span>\n @if (predefinedVariation.name) {\n <em class=\"variation-name\">\n {{ predefinedVariation.name }}\n </em>\n } @else {\n <span>\n {{ getPredefinedVariationValueDisplayValue(setting().settingType, predefinedVariation.value) }}\n </span>\n }\n </div>\n </mat-option>\n </span>\n }\n </mat-select>\n <button\n matSuffix\n class=\"input-suffix-button\"\n type=\"button\"\n mat-icon-button\n [matTooltip]=\"canCreateOrUpdateSetting() ? 'Manage predefined variations' : 'View predefined variations'\"\n (click)=\"\n $event.stopPropagation();\n viewVariationsRequested.emit({ settingId: setting().settingId, settingName: setting().name })\n \">\n <mat-icon class=\"icon-transform-rotate-90\">call_split</mat-icon>\n </button>\n @if (formGroupValue.controls.predefinedVariationId.invalid) {\n <mat-error>\n {{ FormHelper.getErrorMessage(formGroupValue.controls.predefinedVariationId) }}\n </mat-error>\n }\n </mat-form-field>\n </div>\n } @else if (formGroupValue.controls.stringValue) {\n <mat-form-field appearance=\"outline\" class=\"small-form-field suffixed\" subscriptSizing=\"dynamic\">\n <input\n matInput\n type=\"text\"\n placeholder=\"Add text ...\"\n appPastedDataTrimmer\n [formControl]=\"formGroupValue.controls.stringValue\"\n [matTooltip]=\"`${tooltip()}: ${getServedValueText(formGroupValue.controls)} (type: ${setting().settingType})`\"\n [matTooltipClass]=\"['wide-tooltip', 'multiline-tooltip', 'left-aligned']\" />\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 (formGroupValue.controls.stringValue.invalid) {\n <mat-error>\n {{ FormHelper.getErrorMessage(formGroupValue.controls.stringValue) }}\n </mat-error>\n }\n </mat-form-field>\n } @else if (formGroupValue.controls.intValue) {\n <mat-form-field appearance=\"outline\" class=\"small-form-field\" subscriptSizing=\"dynamic\">\n <input\n matInput\n type=\"number\"\n inputDisplayNormalizer\n placeholder=\"Add number ...\"\n [formControl]=\"formGroupValue.controls.intValue\"\n [matTooltip]=\"`${tooltip()}: ${getServedValueText(formGroupValue.controls)} (type: ${setting().settingType})`\"\n [matTooltipClass]=\"['wide-tooltip', 'multiline-tooltip', 'left-aligned']\" />\n @if (formGroupValue.controls.intValue.invalid) {\n <mat-error>\n {{ FormHelper.getErrorMessage(formGroupValue.controls.intValue) }}\n </mat-error>\n }\n </mat-form-field>\n } @else if (formGroupValue.controls.doubleValue) {\n <mat-form-field appearance=\"outline\" class=\"small-form-field\" subscriptSizing=\"dynamic\">\n <input\n matInput\n type=\"number\"\n inputDisplayNormalizer\n placeholder=\"Add number ...\"\n [formControl]=\"formGroupValue.controls.doubleValue\"\n [matTooltip]=\"`${tooltip()}: ${getServedValueText(formGroupValue.controls)} (type: ${setting().settingType})`\"\n [matTooltipClass]=\"['wide-tooltip', 'multiline-tooltip', 'left-aligned']\" />\n @if (formGroupValue.controls.doubleValue.invalid) {\n <mat-error>\n {{ FormHelper.getErrorMessage(formGroupValue.controls.doubleValue) }}\n </mat-error>\n }\n </mat-form-field>\n } @else if (formGroupValue.controls.boolValue) {\n <div class=\"bool\" [class.readonly]=\"formGroupValue.controls.boolValue.disabled\">\n <label\n class=\"switch\"\n [matTooltip]=\"`${tooltip()}: ${getServedValueText(formGroupValue.controls)} (type: ${setting().settingType})`\"\n [matTooltipClass]=\"['wide-tooltip', 'multiline-tooltip', 'left-aligned']\">\n <input type=\"checkbox\" [formControl]=\"formGroupValue.controls.boolValue\" />\n <span class=\"slider\"></span>\n </label>\n </div>\n }\n</div>\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", ".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", ":host{display:flex;flex-grow:1}.feature-flag-value{display:flex;flex-grow:1;margin:2px 0;min-width:0}.feature-flag-value .bool{flex-grow:1;align-items:flex-end;display:flex;flex-direction:column}.feature-flag-value .setting-variation{display:flex;flex-direction:column;flex-grow:1;min-width:0}.feature-flag-value .setting-variation:not(.prerequisite){align-items:flex-end}.feature-flag-value .setting-variation mat-form-field{width:212px}.feature-flag-value .readonly{opacity:.6}.feature-flag-value .variationId{margin:4px 0 0 8px;cursor:pointer;color:var(--action-icon-button-color);display:flex}.feature-flag-value mat-form-field{width:100%}.variation-option{display:flex;align-items:center}.variation-option .variation-name{padding-right:2px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.variation-option .variation-indicator{margin-right:8px}mat-select{pointer-events:auto}@media(max-width:500px){.setting-variation mat-form-field{max-width:calc(100% - 4px)}}\n"], dependencies: [{ 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: "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.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: "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: MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { 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: MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: InputDisplayNormalizerDirective, selector: "input[inputDisplayNormalizer]" }, { 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: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: MatSelectTrigger, selector: "mat-select-trigger" }, { kind: "directive", type: PastedDataTrimmerDirective, selector: "[appPastedDataTrimmer]" }], changeDetection: i0.ChangeDetectionStrategy.Eager }); }
4796
4728
  }
4797
4729
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: FeatureFlagValueComponent, decorators: [{
4798
4730
  type: Component,
@@ -4808,13 +4740,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImpor
4808
4740
  MatIcon,
4809
4741
  MatError,
4810
4742
  InputDisplayNormalizerDirective,
4811
- DigitOnlyDirective,
4812
4743
  MatSelect,
4813
4744
  MatOption$1,
4814
4745
  NgClass,
4815
4746
  MatSelectTrigger,
4816
4747
  PastedDataTrimmerDirective,
4817
- ], template: "@let formGroupValue = formGroup();\n\n<div class=\"feature-flag-value\">\n @if (formGroupValue.controls.predefinedVariationId) {\n <div class=\"setting-variation\" [ngClass]=\"{ prerequisite: isPrerequisite() }\">\n <mat-form-field appearance=\"outline\" class=\"small-form-field select-suffixed\" subscriptSizing=\"dynamic\">\n @let selectedPredefinedVariation = getPredefinedVariation(formGroupValue.controls.predefinedVariationId.value);\n @let variationName = selectedPredefinedVariation?.name;\n @let variationHint = selectedPredefinedVariation?.hint;\n <mat-select\n #predefinedVariationSelect=\"matSelect\"\n disableOptionCentering\n [formControl]=\"formGroupValue.controls.predefinedVariationId\"\n [matTooltip]=\"\n `${tooltip()}: ${getServedValueText(selectedPredefinedVariation?.value)} (type: ${setting().settingType})` +\n `\\nName: ${variationName || getDefaultDisplayNameText(selectedPredefinedVariation?.value)}` +\n `${variationHint ? `\\nHint: ${variationHint}` : ''}`\n \"\n [matTooltipClass]=\"['wide-tooltip', 'multiline-tooltip', 'left-aligned']\"\n (openedChange)=\"onPredefinedVariationSelectOpenedChange($event)\">\n <mat-select-trigger>\n @if (selectedPredefinedVariation) {\n <div class=\"variation-option\">\n <span [class]=\"'variation-indicator variation-color-' + selectedPredefinedVariation.colorIndex\"></span>\n @if (selectedPredefinedVariation.name) {\n <em class=\"variation-name\">\n {{ selectedPredefinedVariation.name }}\n </em>\n } @else {\n <span>\n {{\n getPredefinedVariationValueDisplayValue(setting().settingType, selectedPredefinedVariation.value)\n }}\n </span>\n }\n </div>\n }\n </mat-select-trigger>\n\n @for (predefinedVariation of predefinedVariations(); track predefinedVariation.predefinedVariationId) {\n <span\n [matTooltipClass]=\"['wide-tooltip', 'multiline-tooltip', 'left-aligned']\"\n [matTooltip]=\"\n !predefinedVariation.isAvailable\n ? 'The predefined variation is already selected in another percentage option.'\n : (predefinedVariation.hint ?? '')\n \"\n [matTooltipDisabled]=\"\n !predefinedVariationSelect.panelOpen || (predefinedVariation.isAvailable && !predefinedVariation.hint)\n \">\n <mat-option\n [value]=\"predefinedVariation.predefinedVariationId\"\n [disabled]=\"!predefinedVariation.isAvailable\">\n <div class=\"variation-option\">\n <span [class]=\"'variation-indicator variation-color-' + predefinedVariation.colorIndex\"></span>\n @if (predefinedVariation.name) {\n <em class=\"variation-name\">\n {{ predefinedVariation.name }}\n </em>\n } @else {\n <span>\n {{ getPredefinedVariationValueDisplayValue(setting().settingType, predefinedVariation.value) }}\n </span>\n }\n </div>\n </mat-option>\n </span>\n }\n </mat-select>\n <button\n matSuffix\n class=\"input-suffix-button\"\n type=\"button\"\n mat-icon-button\n [matTooltip]=\"canCreateOrUpdateSetting() ? 'Manage predefined variations' : 'View predefined variations'\"\n (click)=\"\n $event.stopPropagation();\n viewVariationsRequested.emit({ settingId: setting().settingId, settingName: setting().name })\n \">\n <mat-icon class=\"icon-transform-rotate-90\">call_split</mat-icon>\n </button>\n @if (formGroupValue.controls.predefinedVariationId.invalid) {\n <mat-error>\n {{ FormHelper.getErrorMessage(formGroupValue.controls.predefinedVariationId) }}\n </mat-error>\n }\n </mat-form-field>\n </div>\n } @else if (formGroupValue.controls.stringValue) {\n <mat-form-field appearance=\"outline\" class=\"small-form-field suffixed\" subscriptSizing=\"dynamic\">\n <input\n matInput\n type=\"text\"\n placeholder=\"Add text ...\"\n appPastedDataTrimmer\n [formControl]=\"formGroupValue.controls.stringValue\"\n [matTooltip]=\"`${tooltip()}: ${getServedValueText(formGroupValue.controls)} (type: ${setting().settingType})`\"\n [matTooltipClass]=\"['wide-tooltip', 'multiline-tooltip', 'left-aligned']\" />\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 (formGroupValue.controls.stringValue.invalid) {\n <mat-error>\n {{ FormHelper.getErrorMessage(formGroupValue.controls.stringValue) }}\n </mat-error>\n }\n </mat-form-field>\n } @else if (formGroupValue.controls.intValue) {\n <mat-form-field appearance=\"outline\" class=\"small-form-field\" subscriptSizing=\"dynamic\">\n <input\n matInput\n type=\"number\"\n appDigitOnly\n inputDisplayNormalizer\n placeholder=\"Add number ...\"\n [formControl]=\"formGroupValue.controls.intValue\"\n [digitOnlyAllowNegatives]=\"true\"\n [matTooltip]=\"`${tooltip()}: ${getServedValueText(formGroupValue.controls)} (type: ${setting().settingType})`\"\n [matTooltipClass]=\"['wide-tooltip', 'multiline-tooltip', 'left-aligned']\" />\n @if (formGroupValue.controls.intValue.invalid) {\n <mat-error>\n {{ FormHelper.getErrorMessage(formGroupValue.controls.intValue) }}\n </mat-error>\n }\n </mat-form-field>\n } @else if (formGroupValue.controls.doubleValue) {\n <mat-form-field appearance=\"outline\" class=\"small-form-field\" subscriptSizing=\"dynamic\">\n <input\n matInput\n type=\"number\"\n inputDisplayNormalizer\n placeholder=\"Add number ...\"\n [formControl]=\"formGroupValue.controls.doubleValue\"\n [matTooltip]=\"`${tooltip()}: ${getServedValueText(formGroupValue.controls)} (type: ${setting().settingType})`\"\n [matTooltipClass]=\"['wide-tooltip', 'multiline-tooltip', 'left-aligned']\" />\n @if (formGroupValue.controls.doubleValue.invalid) {\n <mat-error>\n {{ FormHelper.getErrorMessage(formGroupValue.controls.doubleValue) }}\n </mat-error>\n }\n </mat-form-field>\n } @else if (formGroupValue.controls.boolValue) {\n <div class=\"bool\" [class.readonly]=\"formGroupValue.controls.boolValue.disabled\">\n <label\n class=\"switch\"\n [matTooltip]=\"`${tooltip()}: ${getServedValueText(formGroupValue.controls)} (type: ${setting().settingType})`\"\n [matTooltipClass]=\"['wide-tooltip', 'multiline-tooltip', 'left-aligned']\">\n <input type=\"checkbox\" [formControl]=\"formGroupValue.controls.boolValue\" />\n <span class=\"slider\"></span>\n </label>\n </div>\n }\n</div>\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", ".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", ":host{display:flex;flex-grow:1}.feature-flag-value{display:flex;flex-grow:1;margin:2px 0;min-width:0}.feature-flag-value .bool{flex-grow:1;align-items:flex-end;display:flex;flex-direction:column}.feature-flag-value .setting-variation{display:flex;flex-direction:column;flex-grow:1;min-width:0}.feature-flag-value .setting-variation:not(.prerequisite){align-items:flex-end}.feature-flag-value .setting-variation mat-form-field{width:212px}.feature-flag-value .readonly{opacity:.6}.feature-flag-value .variationId{margin:4px 0 0 8px;cursor:pointer;color:var(--action-icon-button-color);display:flex}.feature-flag-value mat-form-field{width:100%}.variation-option{display:flex;align-items:center}.variation-option .variation-name{padding-right:2px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.variation-option .variation-indicator{margin-right:8px}mat-select{pointer-events:auto}@media(max-width:500px){.setting-variation mat-form-field{max-width:calc(100% - 4px)}}\n"] }]
4748
+ ], template: "@let formGroupValue = formGroup();\n\n<div class=\"feature-flag-value\">\n @if (formGroupValue.controls.predefinedVariationId) {\n <div class=\"setting-variation\" [ngClass]=\"{ prerequisite: isPrerequisite() }\">\n <mat-form-field appearance=\"outline\" class=\"small-form-field select-suffixed\" subscriptSizing=\"dynamic\">\n @let selectedPredefinedVariation = getPredefinedVariation(formGroupValue.controls.predefinedVariationId.value);\n @let variationName = selectedPredefinedVariation?.name;\n @let variationHint = selectedPredefinedVariation?.hint;\n <mat-select\n #predefinedVariationSelect=\"matSelect\"\n disableOptionCentering\n [formControl]=\"formGroupValue.controls.predefinedVariationId\"\n [matTooltip]=\"\n `${tooltip()}: ${getServedValueText(selectedPredefinedVariation?.value)} (type: ${setting().settingType})` +\n `\\nName: ${variationName || getDefaultDisplayNameText(selectedPredefinedVariation?.value)}` +\n `${variationHint ? `\\nHint: ${variationHint}` : ''}`\n \"\n [matTooltipClass]=\"['wide-tooltip', 'multiline-tooltip', 'left-aligned']\"\n (openedChange)=\"onPredefinedVariationSelectOpenedChange($event)\">\n <mat-select-trigger>\n @if (selectedPredefinedVariation) {\n <div class=\"variation-option\">\n <span [class]=\"'variation-indicator variation-color-' + selectedPredefinedVariation.colorIndex\"></span>\n @if (selectedPredefinedVariation.name) {\n <em class=\"variation-name\">\n {{ selectedPredefinedVariation.name }}\n </em>\n } @else {\n <span>\n {{\n getPredefinedVariationValueDisplayValue(setting().settingType, selectedPredefinedVariation.value)\n }}\n </span>\n }\n </div>\n }\n </mat-select-trigger>\n\n @for (predefinedVariation of predefinedVariations(); track predefinedVariation.predefinedVariationId) {\n <span\n [matTooltipClass]=\"['wide-tooltip', 'multiline-tooltip', 'left-aligned']\"\n [matTooltip]=\"\n !predefinedVariation.isAvailable\n ? 'The predefined variation is already selected in another percentage option.'\n : (predefinedVariation.hint ?? '')\n \"\n [matTooltipDisabled]=\"\n !predefinedVariationSelect.panelOpen || (predefinedVariation.isAvailable && !predefinedVariation.hint)\n \">\n <mat-option\n [value]=\"predefinedVariation.predefinedVariationId\"\n [disabled]=\"!predefinedVariation.isAvailable\">\n <div class=\"variation-option\">\n <span [class]=\"'variation-indicator variation-color-' + predefinedVariation.colorIndex\"></span>\n @if (predefinedVariation.name) {\n <em class=\"variation-name\">\n {{ predefinedVariation.name }}\n </em>\n } @else {\n <span>\n {{ getPredefinedVariationValueDisplayValue(setting().settingType, predefinedVariation.value) }}\n </span>\n }\n </div>\n </mat-option>\n </span>\n }\n </mat-select>\n <button\n matSuffix\n class=\"input-suffix-button\"\n type=\"button\"\n mat-icon-button\n [matTooltip]=\"canCreateOrUpdateSetting() ? 'Manage predefined variations' : 'View predefined variations'\"\n (click)=\"\n $event.stopPropagation();\n viewVariationsRequested.emit({ settingId: setting().settingId, settingName: setting().name })\n \">\n <mat-icon class=\"icon-transform-rotate-90\">call_split</mat-icon>\n </button>\n @if (formGroupValue.controls.predefinedVariationId.invalid) {\n <mat-error>\n {{ FormHelper.getErrorMessage(formGroupValue.controls.predefinedVariationId) }}\n </mat-error>\n }\n </mat-form-field>\n </div>\n } @else if (formGroupValue.controls.stringValue) {\n <mat-form-field appearance=\"outline\" class=\"small-form-field suffixed\" subscriptSizing=\"dynamic\">\n <input\n matInput\n type=\"text\"\n placeholder=\"Add text ...\"\n appPastedDataTrimmer\n [formControl]=\"formGroupValue.controls.stringValue\"\n [matTooltip]=\"`${tooltip()}: ${getServedValueText(formGroupValue.controls)} (type: ${setting().settingType})`\"\n [matTooltipClass]=\"['wide-tooltip', 'multiline-tooltip', 'left-aligned']\" />\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 (formGroupValue.controls.stringValue.invalid) {\n <mat-error>\n {{ FormHelper.getErrorMessage(formGroupValue.controls.stringValue) }}\n </mat-error>\n }\n </mat-form-field>\n } @else if (formGroupValue.controls.intValue) {\n <mat-form-field appearance=\"outline\" class=\"small-form-field\" subscriptSizing=\"dynamic\">\n <input\n matInput\n type=\"number\"\n inputDisplayNormalizer\n placeholder=\"Add number ...\"\n [formControl]=\"formGroupValue.controls.intValue\"\n [matTooltip]=\"`${tooltip()}: ${getServedValueText(formGroupValue.controls)} (type: ${setting().settingType})`\"\n [matTooltipClass]=\"['wide-tooltip', 'multiline-tooltip', 'left-aligned']\" />\n @if (formGroupValue.controls.intValue.invalid) {\n <mat-error>\n {{ FormHelper.getErrorMessage(formGroupValue.controls.intValue) }}\n </mat-error>\n }\n </mat-form-field>\n } @else if (formGroupValue.controls.doubleValue) {\n <mat-form-field appearance=\"outline\" class=\"small-form-field\" subscriptSizing=\"dynamic\">\n <input\n matInput\n type=\"number\"\n inputDisplayNormalizer\n placeholder=\"Add number ...\"\n [formControl]=\"formGroupValue.controls.doubleValue\"\n [matTooltip]=\"`${tooltip()}: ${getServedValueText(formGroupValue.controls)} (type: ${setting().settingType})`\"\n [matTooltipClass]=\"['wide-tooltip', 'multiline-tooltip', 'left-aligned']\" />\n @if (formGroupValue.controls.doubleValue.invalid) {\n <mat-error>\n {{ FormHelper.getErrorMessage(formGroupValue.controls.doubleValue) }}\n </mat-error>\n }\n </mat-form-field>\n } @else if (formGroupValue.controls.boolValue) {\n <div class=\"bool\" [class.readonly]=\"formGroupValue.controls.boolValue.disabled\">\n <label\n class=\"switch\"\n [matTooltip]=\"`${tooltip()}: ${getServedValueText(formGroupValue.controls)} (type: ${setting().settingType})`\"\n [matTooltipClass]=\"['wide-tooltip', 'multiline-tooltip', 'left-aligned']\">\n <input type=\"checkbox\" [formControl]=\"formGroupValue.controls.boolValue\" />\n <span class=\"slider\"></span>\n </label>\n </div>\n }\n</div>\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", ".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", ":host{display:flex;flex-grow:1}.feature-flag-value{display:flex;flex-grow:1;margin:2px 0;min-width:0}.feature-flag-value .bool{flex-grow:1;align-items:flex-end;display:flex;flex-direction:column}.feature-flag-value .setting-variation{display:flex;flex-direction:column;flex-grow:1;min-width:0}.feature-flag-value .setting-variation:not(.prerequisite){align-items:flex-end}.feature-flag-value .setting-variation mat-form-field{width:212px}.feature-flag-value .readonly{opacity:.6}.feature-flag-value .variationId{margin:4px 0 0 8px;cursor:pointer;color:var(--action-icon-button-color);display:flex}.feature-flag-value mat-form-field{width:100%}.variation-option{display:flex;align-items:center}.variation-option .variation-name{padding-right:2px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.variation-option .variation-indicator{margin-right:8px}mat-select{pointer-events:auto}@media(max-width:500px){.setting-variation mat-form-field{max-width:calc(100% - 4px)}}\n"] }]
4818
4749
  }], propDecorators: { formGroup: [{ type: i0.Input, args: [{ isSignal: true, alias: "formGroup", required: true }] }], setting: [{ type: i0.Input, args: [{ isSignal: true, alias: "setting", required: true }] }], canCreateOrUpdateSetting: [{ type: i0.Input, args: [{ isSignal: true, alias: "canCreateOrUpdateSetting", required: true }] }], notAvailablePredefinedVariationIds: [{ type: i0.Input, args: [{ isSignal: true, alias: "notAvailablePredefinedVariationIds", required: false }] }], featureFlagLimitations: [{ type: i0.Input, args: [{ isSignal: true, alias: "featureFlagLimitations", required: true }] }], isPrerequisite: [{ type: i0.Input, args: [{ isSignal: true, alias: "isPrerequisite", required: false }] }], tooltip: [{ type: i0.Input, args: [{ isSignal: true, alias: "tooltip", required: false }] }], viewVariationsRequested: [{ type: i0.Output, args: ["viewVariationsRequested"] }], tooltips: [{ type: i0.ViewChildren, args: [i0.forwardRef(() => MatTooltip), { isSignal: true }] }] } });
4819
4750
 
4820
4751
  class ConditionChipComponent {
@@ -5859,9 +5790,10 @@ class UserConditionComponent {
5859
5790
  width: "600px",
5860
5791
  data: {
5861
5792
  title: "Consider using confidential comparators",
5862
- body: `Comparison value will be presented as plain text
5863
- in the <i>config JSON</i>. If you have sensitive data, consider using one of the confidential (hashed) comparators.<br/><br/>
5864
- See the <a href="https://configcat.com/docs/targeting/targeting-rule/user-condition/#confidential-text-comparators" target="_blank">documentation</a> for more information.`,
5793
+ body: safeHtml `\
5794
+ Comparison value will be presented as plain text in the <i>config JSON</i>.
5795
+ If you have sensitive data, consider using one of the confidential (hashed) comparators.<br/><br/>
5796
+ See the <a href="https://configcat.com/docs/targeting/targeting-rule/user-condition/#confidential-text-comparators" target="_blank">documentation</a> for more information.`,
5865
5797
  secondaryButtonLabel: "Open comparator selector",
5866
5798
  secondaryButtonAction: () => {
5867
5799
  this.compDialog();
@@ -5882,7 +5814,9 @@ class UserConditionComponent {
5882
5814
  const dialogRef = this.dialog.open(ConfirmationDialogComponent, {
5883
5815
  data: {
5884
5816
  title: "How would you like to add the items?",
5885
- 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?",
5817
+ body: safeHtml `\
5818
+ We've noticed a comma in the value you want to add. <br/>
5819
+ Do you want to add it as a single item, or would you prefer to insert multiple items separated by commas?`,
5886
5820
  yesButtonLabel: "Add multiple items",
5887
5821
  cancelButtonLabel: "Add as a single item",
5888
5822
  },
@@ -6102,7 +6036,7 @@ class UserConditionComponent {
6102
6036
  return !compVal.doubleValue && !compVal.listValue && !compVal.stringValue;
6103
6037
  }
6104
6038
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: UserConditionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
6105
- 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 }); }
6039
+ 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 }); }
6106
6040
  }
6107
6041
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: UserConditionComponent, decorators: [{
6108
6042
  type: Component,
@@ -6140,7 +6074,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImpor
6140
6074
  PastedDataTrimmerDirective,
6141
6075
  MatAutocomplete,
6142
6076
  MatAutocompleteTrigger,
6143
- ], 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"] }]
6077
+ ], 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"] }]
6144
6078
  }], 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 }] }] } });
6145
6079
 
6146
6080
  class ConditionsComponent {
@@ -6327,7 +6261,7 @@ class PercentageOptionsComponent {
6327
6261
  .map(p => p.controls.value.controls.predefinedVariationId.value);
6328
6262
  }
6329
6263
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: PercentageOptionsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
6330
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: PercentageOptionsComponent, isStandalone: true, selector: "app-percentage-options", inputs: { formArray: { classPropertyName: "formArray", publicName: "formArray", isSignal: true, isRequired: true, transformFunction: null }, flagState: { classPropertyName: "flagState", publicName: "flagState", isSignal: true, isRequired: true, transformFunction: null }, featureFlagLimitations: { classPropertyName: "featureFlagLimitations", publicName: "featureFlagLimitations", isSignal: true, isRequired: true, transformFunction: null }, percentageEvaluationAttribute: { classPropertyName: "percentageEvaluationAttribute", publicName: "percentageEvaluationAttribute", isSignal: true, isRequired: true, transformFunction: null }, isPercentageOnlyTargetingRule: { classPropertyName: "isPercentageOnlyTargetingRule", publicName: "isPercentageOnlyTargetingRule", isSignal: true, isRequired: true, transformFunction: null }, canCreateOrUpdateSetting: { classPropertyName: "canCreateOrUpdateSetting", publicName: "canCreateOrUpdateSetting", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { lastPercentageOptionLeft: "lastPercentageOptionLeft", subscriptionLimitReached: "subscriptionLimitReached", changePercentageEvaluationAttributeRequested: "changePercentageEvaluationAttributeRequested", viewVariationsRequested: "viewVariationsRequested" }, ngImport: i0, template: "@for (percentageOption of formArray().controls; track $index; let i = $index) {\n <div class=\"targeting-rule-line\">\n <div class=\"wrapper-group\">\n <div class=\"targeting-rule-component flex\">\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\" class=\"small-form-field perc-input\">\n <input\n matInput\n type=\"number\"\n min=\"0\"\n max=\"100\"\n matTooltip=\"Percentage\"\n appDigitOnly\n inputDisplayNormalizer\n [formControl]=\"percentageOption.controls.percentage\"\n (input)=\"percentageChanged(i)\" />\n <span matTextSuffix>%</span>\n @if (percentageOption.controls.percentage.invalid) {\n <mat-error>\n {{ FormHelper.getErrorMessage(percentageOption.controls.percentage) }}\n </mat-error>\n }\n </mat-form-field>\n </div>\n @if (!percentageEvaluationAttribute()) {\n <div class=\"attribute-wrapper flex\">\n of&nbsp;\n <a href=\"https://configcat.com/docs/targeting/percentage-options/\" target=\"_blank\" rel=\"noopener noreferrer\">\n users\n </a>\n </div>\n } @else {\n <div class=\"attribute-wrapper wrapper-group flex\">\n <div class=\"small-pad-right\">of all</div>\n <div class=\"small-pad-right\">\n <button\n mat-flat-button\n type=\"button\"\n class=\"attribute\"\n matTooltip=\"Change percentage evaluation attribute\"\n [disabled]=\"flagState().readOnly\"\n (click)=\"changePercentageEvaluationAttribute()\">\n {{ percentageEvaluationAttribute() }}\n </button>\n </div>\n <div>attributes</div>\n </div>\n }\n @if (!flagState().readOnly) {\n <div class=\"delete-button\">\n <button type=\"button\" mat-icon-button matTooltip=\"Remove option\" (click)=\"remove(i)\">\n <mat-icon class=\"icon-transform-rotate\">cancel</mat-icon>\n </button>\n </div>\n }\n </div>\n <app-feature-flag-value\n class=\"value\"\n tooltip=\"Served value\"\n [setting]=\"flagState().settingValue.setting\"\n [formGroup]=\"percentageOption.controls.value\"\n [featureFlagLimitations]=\"featureFlagLimitations()\"\n [notAvailablePredefinedVariationIds]=\"getNotAvailablePredefinedVariationIds(i)\"\n [canCreateOrUpdateSetting]=\"canCreateOrUpdateSetting()\"\n (viewVariationsRequested)=\"viewVariationsRequested.emit($event)\" />\n </div>\n}\n@if (formArray().invalid && !!FormHelper.getErrorMessage(formArray())) {\n <mat-error class=\"sumerror\">\n {{ FormHelper.getErrorMessage(formArray()) }}\n </mat-error>\n}\n@if (percentageEvaluationAttribute()) {\n @if (isPercentageOnlyTargetingRule()) {\n <div class=\"small small-pad-bottom\">\n If the {{ percentageEvaluationAttribute() }} attribute is not set in the\n <a href=\"https://configcat.com/docs/targeting/user-object/\" target=\"_blank\" rel=\"noopener noreferrer\">\n User Object\n </a>\n , the percentage options are skipped and the evaluation continues with the next rule.\n </div>\n } @else {\n <div class=\"small small-pad-bottom\">\n If the {{ percentageEvaluationAttribute() }} attribute is not set in the\n <a href=\"https://configcat.com/docs/targeting/user-object/\" target=\"_blank\" rel=\"noopener noreferrer\">\n User Object\n </a>\n , the current targeting rule is skipped and the evaluation continues with the next one.\n </div>\n }\n}\n\n@if (\n flagState().settingValue.setting.settingType !== SettingType.Boolean &&\n !flagState().readOnly &&\n (!flagState().settingValue.setting.predefinedVariations.length ||\n flagState().settingValue.setting.predefinedVariations.length > percentageOptionsCount())\n) {\n <div class=\"add-perc\">\n <!-- In the integrations we remove the enter sign to spare some space-->\n <!-- <div class=\"enter-sign\"><mat-icon>subdirectory_arrow_right</mat-icon></div> -->\n <div class=\"add-btn add-perc-btn\" (click)=\"addOption()\">+ % OPTION</div>\n </div>\n}\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", ".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;--mat-icon-button-touch-target-display: none}.targeting-rule-line .mat-mdc-icon-button.mat-mdc-button-base{--mdc-icon-button-state-layer-size: 32px;--mat-icon-button-state-layer-size: 32px;width:var(--mat-icon-button-state-layer-size);height:var(--mat-icon-button-state-layer-size);padding:4px}.targeting-rule-line{align-items:center}.targeting-rule-line .flex{display:flex;align-items:center;margin-right:4px}.targeting-rule-line .targeting-rule-component{max-width:84px}.targeting-rule-line .value{flex-grow:1}.targeting-rule-line .perc-input{margin-right:5px}.targeting-rule-line .delete-button{margin-right:4px;margin-top:2px}.add-perc{display:flex;width:100%;padding-top:5px}.add-perc .enter-sign{color:var(--flag-enter-sign);margin:2px 3px 0 8px}.add-perc .enter-sign mat-icon{font-size:16px;width:16px;height:16px}.add-perc .add-perc-btn{min-width:63px;padding:3px 4px 2px 8px;background-color:var(--flag-box-rule-control-background);border:var(--flag-box-rule-control-border)}.sumerror{margin-bottom:8px;font-size:12px}.small{font-size:.7rem;color:var(--flag-meta)}.attribute-wrapper{color:var(--flag-description)}.attribute{height:32px!important;border:1px solid var(--input-border-color);background-color:var(--input-background);text-transform:none!important;color:var(--flag-description);font-size:12px;padding:0 5px}.small-pad-right{margin-right:5px}.small-pad-bottom{margin-bottom:5px}\n", ".chip-container{display:flex;margin-top:2px;align-self:start}.chip-container.fixed-width{width:110px}.chip-container.fixed-width-wide{width:130px}.chip-container mat-chip-set{margin-top:2px;width:100%}.chip-container mat-chip-set mat-chip{width:100%;margin-top:0;margin-bottom:0;height:24px;font-size:12px;background-color:var(--flag-chip-background);opacity:1}.chip-container .enter-sign{color:var(--flag-enter-sign);margin:6px 3px 0 8px}.chip-container .enter-sign mat-icon{font-size:16px;width:16px;height:16px}\n", ".add-btn{cursor:pointer;background-color:transparent;display:flex;font-size:10px;align-items:center;border:var(--flag-box-control-button-border);color:var(--flag-box-control);border-radius:15px}.add-btn:hover{background-color:var(--flag-box-control-hover)!important}\n"], dependencies: [{ 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: "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: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1.MaxValidator, selector: "input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]", inputs: ["max"] }, { kind: "directive", type: InputDisplayNormalizerDirective, selector: "input[inputDisplayNormalizer]" }, { kind: "directive", type: MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { 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: "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: MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { 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: MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: FeatureFlagValueComponent, selector: "app-feature-flag-value", inputs: ["formGroup", "setting", "canCreateOrUpdateSetting", "notAvailablePredefinedVariationIds", "featureFlagLimitations", "isPrerequisite", "tooltip"], outputs: ["viewVariationsRequested"] }, { kind: "directive", type: DigitOnlyDirective, selector: "[appDigitOnly]", inputs: ["digitOnlyDecimal", "digitOnlyDecimalSeparator", "digitOnlyAllowNegatives", "digitOnlyAllowPaste", "digitOnlyNegativeSign"] }], changeDetection: i0.ChangeDetectionStrategy.Eager }); }
6264
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: PercentageOptionsComponent, isStandalone: true, selector: "app-percentage-options", inputs: { formArray: { classPropertyName: "formArray", publicName: "formArray", isSignal: true, isRequired: true, transformFunction: null }, flagState: { classPropertyName: "flagState", publicName: "flagState", isSignal: true, isRequired: true, transformFunction: null }, featureFlagLimitations: { classPropertyName: "featureFlagLimitations", publicName: "featureFlagLimitations", isSignal: true, isRequired: true, transformFunction: null }, percentageEvaluationAttribute: { classPropertyName: "percentageEvaluationAttribute", publicName: "percentageEvaluationAttribute", isSignal: true, isRequired: true, transformFunction: null }, isPercentageOnlyTargetingRule: { classPropertyName: "isPercentageOnlyTargetingRule", publicName: "isPercentageOnlyTargetingRule", isSignal: true, isRequired: true, transformFunction: null }, canCreateOrUpdateSetting: { classPropertyName: "canCreateOrUpdateSetting", publicName: "canCreateOrUpdateSetting", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { lastPercentageOptionLeft: "lastPercentageOptionLeft", subscriptionLimitReached: "subscriptionLimitReached", changePercentageEvaluationAttributeRequested: "changePercentageEvaluationAttributeRequested", viewVariationsRequested: "viewVariationsRequested" }, ngImport: i0, template: "@for (percentageOption of formArray().controls; track $index; let i = $index) {\n <div class=\"targeting-rule-line\">\n <div class=\"wrapper-group\">\n <div class=\"targeting-rule-component flex\">\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\" class=\"small-form-field perc-input\">\n <input\n matInput\n type=\"number\"\n min=\"0\"\n max=\"100\"\n matTooltip=\"Percentage\"\n inputDisplayNormalizer\n [formControl]=\"percentageOption.controls.percentage\"\n (input)=\"percentageChanged(i)\" />\n <span matTextSuffix>%</span>\n @if (percentageOption.controls.percentage.invalid) {\n <mat-error>\n {{ FormHelper.getErrorMessage(percentageOption.controls.percentage) }}\n </mat-error>\n }\n </mat-form-field>\n </div>\n @if (!percentageEvaluationAttribute()) {\n <div class=\"attribute-wrapper flex\">\n of&nbsp;\n <a href=\"https://configcat.com/docs/targeting/percentage-options/\" target=\"_blank\" rel=\"noopener noreferrer\">\n users\n </a>\n </div>\n } @else {\n <div class=\"attribute-wrapper wrapper-group flex\">\n <div class=\"small-pad-right\">of all</div>\n <div class=\"small-pad-right\">\n <button\n mat-flat-button\n type=\"button\"\n class=\"attribute\"\n matTooltip=\"Change percentage evaluation attribute\"\n [disabled]=\"flagState().readOnly\"\n (click)=\"changePercentageEvaluationAttribute()\">\n {{ percentageEvaluationAttribute() }}\n </button>\n </div>\n <div>attributes</div>\n </div>\n }\n @if (!flagState().readOnly) {\n <div class=\"delete-button\">\n <button type=\"button\" mat-icon-button matTooltip=\"Remove option\" (click)=\"remove(i)\">\n <mat-icon class=\"icon-transform-rotate\">cancel</mat-icon>\n </button>\n </div>\n }\n </div>\n <app-feature-flag-value\n class=\"value\"\n tooltip=\"Served value\"\n [setting]=\"flagState().settingValue.setting\"\n [formGroup]=\"percentageOption.controls.value\"\n [featureFlagLimitations]=\"featureFlagLimitations()\"\n [notAvailablePredefinedVariationIds]=\"getNotAvailablePredefinedVariationIds(i)\"\n [canCreateOrUpdateSetting]=\"canCreateOrUpdateSetting()\"\n (viewVariationsRequested)=\"viewVariationsRequested.emit($event)\" />\n </div>\n}\n@if (formArray().invalid && !!FormHelper.getErrorMessage(formArray())) {\n <mat-error class=\"sumerror\">\n {{ FormHelper.getErrorMessage(formArray()) }}\n </mat-error>\n}\n@if (percentageEvaluationAttribute()) {\n @if (isPercentageOnlyTargetingRule()) {\n <div class=\"small small-pad-bottom\">\n If the {{ percentageEvaluationAttribute() }} attribute is not set in the\n <a href=\"https://configcat.com/docs/targeting/user-object/\" target=\"_blank\" rel=\"noopener noreferrer\">\n User Object\n </a>\n , the percentage options are skipped and the evaluation continues with the next rule.\n </div>\n } @else {\n <div class=\"small small-pad-bottom\">\n If the {{ percentageEvaluationAttribute() }} attribute is not set in the\n <a href=\"https://configcat.com/docs/targeting/user-object/\" target=\"_blank\" rel=\"noopener noreferrer\">\n User Object\n </a>\n , the current targeting rule is skipped and the evaluation continues with the next one.\n </div>\n }\n}\n\n@if (\n flagState().settingValue.setting.settingType !== SettingType.Boolean &&\n !flagState().readOnly &&\n (!flagState().settingValue.setting.predefinedVariations.length ||\n flagState().settingValue.setting.predefinedVariations.length > percentageOptionsCount())\n) {\n <div class=\"add-perc\">\n <!-- In the integrations we remove the enter sign to spare some space-->\n <!-- <div class=\"enter-sign\"><mat-icon>subdirectory_arrow_right</mat-icon></div> -->\n <div class=\"add-btn add-perc-btn\" (click)=\"addOption()\">+ % OPTION</div>\n </div>\n}\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", ".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;--mat-icon-button-touch-target-display: none}.targeting-rule-line .mat-mdc-icon-button.mat-mdc-button-base{--mdc-icon-button-state-layer-size: 32px;--mat-icon-button-state-layer-size: 32px;width:var(--mat-icon-button-state-layer-size);height:var(--mat-icon-button-state-layer-size);padding:4px}.targeting-rule-line{align-items:center}.targeting-rule-line .flex{display:flex;align-items:center;margin-right:4px}.targeting-rule-line .targeting-rule-component{max-width:84px}.targeting-rule-line .value{flex-grow:1}.targeting-rule-line .perc-input{margin-right:5px}.targeting-rule-line .delete-button{margin-right:4px;margin-top:2px}.add-perc{display:flex;width:100%;padding-top:5px}.add-perc .enter-sign{color:var(--flag-enter-sign);margin:2px 3px 0 8px}.add-perc .enter-sign mat-icon{font-size:16px;width:16px;height:16px}.add-perc .add-perc-btn{min-width:63px;padding:3px 4px 2px 8px;background-color:var(--flag-box-rule-control-background);border:var(--flag-box-rule-control-border)}.sumerror{margin-bottom:8px;font-size:12px}.small{font-size:.7rem;color:var(--flag-meta)}.attribute-wrapper{color:var(--flag-description)}.attribute{height:32px!important;border:1px solid var(--input-border-color);background-color:var(--input-background);text-transform:none!important;color:var(--flag-description);font-size:12px;padding:0 5px}.small-pad-right{margin-right:5px}.small-pad-bottom{margin-bottom:5px}\n", ".chip-container{display:flex;margin-top:2px;align-self:start}.chip-container.fixed-width{width:110px}.chip-container.fixed-width-wide{width:130px}.chip-container mat-chip-set{margin-top:2px;width:100%}.chip-container mat-chip-set mat-chip{width:100%;margin-top:0;margin-bottom:0;height:24px;font-size:12px;background-color:var(--flag-chip-background);opacity:1}.chip-container .enter-sign{color:var(--flag-enter-sign);margin:6px 3px 0 8px}.chip-container .enter-sign mat-icon{font-size:16px;width:16px;height:16px}\n", ".add-btn{cursor:pointer;background-color:transparent;display:flex;font-size:10px;align-items:center;border:var(--flag-box-control-button-border);color:var(--flag-box-control);border-radius:15px}.add-btn:hover{background-color:var(--flag-box-control-hover)!important}\n"], dependencies: [{ 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: "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: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1.MaxValidator, selector: "input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]", inputs: ["max"] }, { kind: "directive", type: InputDisplayNormalizerDirective, selector: "input[inputDisplayNormalizer]" }, { kind: "directive", type: MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { 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: "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: MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { 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: MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: FeatureFlagValueComponent, selector: "app-feature-flag-value", inputs: ["formGroup", "setting", "canCreateOrUpdateSetting", "notAvailablePredefinedVariationIds", "featureFlagLimitations", "isPrerequisite", "tooltip"], outputs: ["viewVariationsRequested"] }], changeDetection: i0.ChangeDetectionStrategy.Eager }); }
6331
6265
  }
6332
6266
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: PercentageOptionsComponent, decorators: [{
6333
6267
  type: Component,
@@ -6345,8 +6279,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImpor
6345
6279
  MatIconButton,
6346
6280
  MatIcon,
6347
6281
  FeatureFlagValueComponent,
6348
- DigitOnlyDirective,
6349
- ], template: "@for (percentageOption of formArray().controls; track $index; let i = $index) {\n <div class=\"targeting-rule-line\">\n <div class=\"wrapper-group\">\n <div class=\"targeting-rule-component flex\">\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\" class=\"small-form-field perc-input\">\n <input\n matInput\n type=\"number\"\n min=\"0\"\n max=\"100\"\n matTooltip=\"Percentage\"\n appDigitOnly\n inputDisplayNormalizer\n [formControl]=\"percentageOption.controls.percentage\"\n (input)=\"percentageChanged(i)\" />\n <span matTextSuffix>%</span>\n @if (percentageOption.controls.percentage.invalid) {\n <mat-error>\n {{ FormHelper.getErrorMessage(percentageOption.controls.percentage) }}\n </mat-error>\n }\n </mat-form-field>\n </div>\n @if (!percentageEvaluationAttribute()) {\n <div class=\"attribute-wrapper flex\">\n of&nbsp;\n <a href=\"https://configcat.com/docs/targeting/percentage-options/\" target=\"_blank\" rel=\"noopener noreferrer\">\n users\n </a>\n </div>\n } @else {\n <div class=\"attribute-wrapper wrapper-group flex\">\n <div class=\"small-pad-right\">of all</div>\n <div class=\"small-pad-right\">\n <button\n mat-flat-button\n type=\"button\"\n class=\"attribute\"\n matTooltip=\"Change percentage evaluation attribute\"\n [disabled]=\"flagState().readOnly\"\n (click)=\"changePercentageEvaluationAttribute()\">\n {{ percentageEvaluationAttribute() }}\n </button>\n </div>\n <div>attributes</div>\n </div>\n }\n @if (!flagState().readOnly) {\n <div class=\"delete-button\">\n <button type=\"button\" mat-icon-button matTooltip=\"Remove option\" (click)=\"remove(i)\">\n <mat-icon class=\"icon-transform-rotate\">cancel</mat-icon>\n </button>\n </div>\n }\n </div>\n <app-feature-flag-value\n class=\"value\"\n tooltip=\"Served value\"\n [setting]=\"flagState().settingValue.setting\"\n [formGroup]=\"percentageOption.controls.value\"\n [featureFlagLimitations]=\"featureFlagLimitations()\"\n [notAvailablePredefinedVariationIds]=\"getNotAvailablePredefinedVariationIds(i)\"\n [canCreateOrUpdateSetting]=\"canCreateOrUpdateSetting()\"\n (viewVariationsRequested)=\"viewVariationsRequested.emit($event)\" />\n </div>\n}\n@if (formArray().invalid && !!FormHelper.getErrorMessage(formArray())) {\n <mat-error class=\"sumerror\">\n {{ FormHelper.getErrorMessage(formArray()) }}\n </mat-error>\n}\n@if (percentageEvaluationAttribute()) {\n @if (isPercentageOnlyTargetingRule()) {\n <div class=\"small small-pad-bottom\">\n If the {{ percentageEvaluationAttribute() }} attribute is not set in the\n <a href=\"https://configcat.com/docs/targeting/user-object/\" target=\"_blank\" rel=\"noopener noreferrer\">\n User Object\n </a>\n , the percentage options are skipped and the evaluation continues with the next rule.\n </div>\n } @else {\n <div class=\"small small-pad-bottom\">\n If the {{ percentageEvaluationAttribute() }} attribute is not set in the\n <a href=\"https://configcat.com/docs/targeting/user-object/\" target=\"_blank\" rel=\"noopener noreferrer\">\n User Object\n </a>\n , the current targeting rule is skipped and the evaluation continues with the next one.\n </div>\n }\n}\n\n@if (\n flagState().settingValue.setting.settingType !== SettingType.Boolean &&\n !flagState().readOnly &&\n (!flagState().settingValue.setting.predefinedVariations.length ||\n flagState().settingValue.setting.predefinedVariations.length > percentageOptionsCount())\n) {\n <div class=\"add-perc\">\n <!-- In the integrations we remove the enter sign to spare some space-->\n <!-- <div class=\"enter-sign\"><mat-icon>subdirectory_arrow_right</mat-icon></div> -->\n <div class=\"add-btn add-perc-btn\" (click)=\"addOption()\">+ % OPTION</div>\n </div>\n}\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", ".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;--mat-icon-button-touch-target-display: none}.targeting-rule-line .mat-mdc-icon-button.mat-mdc-button-base{--mdc-icon-button-state-layer-size: 32px;--mat-icon-button-state-layer-size: 32px;width:var(--mat-icon-button-state-layer-size);height:var(--mat-icon-button-state-layer-size);padding:4px}.targeting-rule-line{align-items:center}.targeting-rule-line .flex{display:flex;align-items:center;margin-right:4px}.targeting-rule-line .targeting-rule-component{max-width:84px}.targeting-rule-line .value{flex-grow:1}.targeting-rule-line .perc-input{margin-right:5px}.targeting-rule-line .delete-button{margin-right:4px;margin-top:2px}.add-perc{display:flex;width:100%;padding-top:5px}.add-perc .enter-sign{color:var(--flag-enter-sign);margin:2px 3px 0 8px}.add-perc .enter-sign mat-icon{font-size:16px;width:16px;height:16px}.add-perc .add-perc-btn{min-width:63px;padding:3px 4px 2px 8px;background-color:var(--flag-box-rule-control-background);border:var(--flag-box-rule-control-border)}.sumerror{margin-bottom:8px;font-size:12px}.small{font-size:.7rem;color:var(--flag-meta)}.attribute-wrapper{color:var(--flag-description)}.attribute{height:32px!important;border:1px solid var(--input-border-color);background-color:var(--input-background);text-transform:none!important;color:var(--flag-description);font-size:12px;padding:0 5px}.small-pad-right{margin-right:5px}.small-pad-bottom{margin-bottom:5px}\n", ".chip-container{display:flex;margin-top:2px;align-self:start}.chip-container.fixed-width{width:110px}.chip-container.fixed-width-wide{width:130px}.chip-container mat-chip-set{margin-top:2px;width:100%}.chip-container mat-chip-set mat-chip{width:100%;margin-top:0;margin-bottom:0;height:24px;font-size:12px;background-color:var(--flag-chip-background);opacity:1}.chip-container .enter-sign{color:var(--flag-enter-sign);margin:6px 3px 0 8px}.chip-container .enter-sign mat-icon{font-size:16px;width:16px;height:16px}\n", ".add-btn{cursor:pointer;background-color:transparent;display:flex;font-size:10px;align-items:center;border:var(--flag-box-control-button-border);color:var(--flag-box-control);border-radius:15px}.add-btn:hover{background-color:var(--flag-box-control-hover)!important}\n"] }]
6282
+ ], template: "@for (percentageOption of formArray().controls; track $index; let i = $index) {\n <div class=\"targeting-rule-line\">\n <div class=\"wrapper-group\">\n <div class=\"targeting-rule-component flex\">\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\" class=\"small-form-field perc-input\">\n <input\n matInput\n type=\"number\"\n min=\"0\"\n max=\"100\"\n matTooltip=\"Percentage\"\n inputDisplayNormalizer\n [formControl]=\"percentageOption.controls.percentage\"\n (input)=\"percentageChanged(i)\" />\n <span matTextSuffix>%</span>\n @if (percentageOption.controls.percentage.invalid) {\n <mat-error>\n {{ FormHelper.getErrorMessage(percentageOption.controls.percentage) }}\n </mat-error>\n }\n </mat-form-field>\n </div>\n @if (!percentageEvaluationAttribute()) {\n <div class=\"attribute-wrapper flex\">\n of&nbsp;\n <a href=\"https://configcat.com/docs/targeting/percentage-options/\" target=\"_blank\" rel=\"noopener noreferrer\">\n users\n </a>\n </div>\n } @else {\n <div class=\"attribute-wrapper wrapper-group flex\">\n <div class=\"small-pad-right\">of all</div>\n <div class=\"small-pad-right\">\n <button\n mat-flat-button\n type=\"button\"\n class=\"attribute\"\n matTooltip=\"Change percentage evaluation attribute\"\n [disabled]=\"flagState().readOnly\"\n (click)=\"changePercentageEvaluationAttribute()\">\n {{ percentageEvaluationAttribute() }}\n </button>\n </div>\n <div>attributes</div>\n </div>\n }\n @if (!flagState().readOnly) {\n <div class=\"delete-button\">\n <button type=\"button\" mat-icon-button matTooltip=\"Remove option\" (click)=\"remove(i)\">\n <mat-icon class=\"icon-transform-rotate\">cancel</mat-icon>\n </button>\n </div>\n }\n </div>\n <app-feature-flag-value\n class=\"value\"\n tooltip=\"Served value\"\n [setting]=\"flagState().settingValue.setting\"\n [formGroup]=\"percentageOption.controls.value\"\n [featureFlagLimitations]=\"featureFlagLimitations()\"\n [notAvailablePredefinedVariationIds]=\"getNotAvailablePredefinedVariationIds(i)\"\n [canCreateOrUpdateSetting]=\"canCreateOrUpdateSetting()\"\n (viewVariationsRequested)=\"viewVariationsRequested.emit($event)\" />\n </div>\n}\n@if (formArray().invalid && !!FormHelper.getErrorMessage(formArray())) {\n <mat-error class=\"sumerror\">\n {{ FormHelper.getErrorMessage(formArray()) }}\n </mat-error>\n}\n@if (percentageEvaluationAttribute()) {\n @if (isPercentageOnlyTargetingRule()) {\n <div class=\"small small-pad-bottom\">\n If the {{ percentageEvaluationAttribute() }} attribute is not set in the\n <a href=\"https://configcat.com/docs/targeting/user-object/\" target=\"_blank\" rel=\"noopener noreferrer\">\n User Object\n </a>\n , the percentage options are skipped and the evaluation continues with the next rule.\n </div>\n } @else {\n <div class=\"small small-pad-bottom\">\n If the {{ percentageEvaluationAttribute() }} attribute is not set in the\n <a href=\"https://configcat.com/docs/targeting/user-object/\" target=\"_blank\" rel=\"noopener noreferrer\">\n User Object\n </a>\n , the current targeting rule is skipped and the evaluation continues with the next one.\n </div>\n }\n}\n\n@if (\n flagState().settingValue.setting.settingType !== SettingType.Boolean &&\n !flagState().readOnly &&\n (!flagState().settingValue.setting.predefinedVariations.length ||\n flagState().settingValue.setting.predefinedVariations.length > percentageOptionsCount())\n) {\n <div class=\"add-perc\">\n <!-- In the integrations we remove the enter sign to spare some space-->\n <!-- <div class=\"enter-sign\"><mat-icon>subdirectory_arrow_right</mat-icon></div> -->\n <div class=\"add-btn add-perc-btn\" (click)=\"addOption()\">+ % OPTION</div>\n </div>\n}\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", ".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;--mat-icon-button-touch-target-display: none}.targeting-rule-line .mat-mdc-icon-button.mat-mdc-button-base{--mdc-icon-button-state-layer-size: 32px;--mat-icon-button-state-layer-size: 32px;width:var(--mat-icon-button-state-layer-size);height:var(--mat-icon-button-state-layer-size);padding:4px}.targeting-rule-line{align-items:center}.targeting-rule-line .flex{display:flex;align-items:center;margin-right:4px}.targeting-rule-line .targeting-rule-component{max-width:84px}.targeting-rule-line .value{flex-grow:1}.targeting-rule-line .perc-input{margin-right:5px}.targeting-rule-line .delete-button{margin-right:4px;margin-top:2px}.add-perc{display:flex;width:100%;padding-top:5px}.add-perc .enter-sign{color:var(--flag-enter-sign);margin:2px 3px 0 8px}.add-perc .enter-sign mat-icon{font-size:16px;width:16px;height:16px}.add-perc .add-perc-btn{min-width:63px;padding:3px 4px 2px 8px;background-color:var(--flag-box-rule-control-background);border:var(--flag-box-rule-control-border)}.sumerror{margin-bottom:8px;font-size:12px}.small{font-size:.7rem;color:var(--flag-meta)}.attribute-wrapper{color:var(--flag-description)}.attribute{height:32px!important;border:1px solid var(--input-border-color);background-color:var(--input-background);text-transform:none!important;color:var(--flag-description);font-size:12px;padding:0 5px}.small-pad-right{margin-right:5px}.small-pad-bottom{margin-bottom:5px}\n", ".chip-container{display:flex;margin-top:2px;align-self:start}.chip-container.fixed-width{width:110px}.chip-container.fixed-width-wide{width:130px}.chip-container mat-chip-set{margin-top:2px;width:100%}.chip-container mat-chip-set mat-chip{width:100%;margin-top:0;margin-bottom:0;height:24px;font-size:12px;background-color:var(--flag-chip-background);opacity:1}.chip-container .enter-sign{color:var(--flag-enter-sign);margin:6px 3px 0 8px}.chip-container .enter-sign mat-icon{font-size:16px;width:16px;height:16px}\n", ".add-btn{cursor:pointer;background-color:transparent;display:flex;font-size:10px;align-items:center;border:var(--flag-box-control-button-border);color:var(--flag-box-control);border-radius:15px}.add-btn:hover{background-color:var(--flag-box-control-hover)!important}\n"] }]
6350
6283
  }], propDecorators: { formArray: [{ type: i0.Input, args: [{ isSignal: true, alias: "formArray", required: true }] }], flagState: [{ type: i0.Input, args: [{ isSignal: true, alias: "flagState", required: true }] }], featureFlagLimitations: [{ type: i0.Input, args: [{ isSignal: true, alias: "featureFlagLimitations", required: true }] }], percentageEvaluationAttribute: [{ type: i0.Input, args: [{ isSignal: true, alias: "percentageEvaluationAttribute", required: true }] }], isPercentageOnlyTargetingRule: [{ type: i0.Input, args: [{ isSignal: true, alias: "isPercentageOnlyTargetingRule", required: true }] }], canCreateOrUpdateSetting: [{ type: i0.Input, args: [{ isSignal: true, alias: "canCreateOrUpdateSetting", required: true }] }], lastPercentageOptionLeft: [{ type: i0.Output, args: ["lastPercentageOptionLeft"] }], subscriptionLimitReached: [{ type: i0.Output, args: ["subscriptionLimitReached"] }], changePercentageEvaluationAttributeRequested: [{ type: i0.Output, args: ["changePercentageEvaluationAttributeRequested"] }], viewVariationsRequested: [{ type: i0.Output, args: ["viewVariationsRequested"] }] } });
6351
6284
 
6352
6285
  class TargetingRuleComponent {
@@ -7115,8 +7048,8 @@ class FeatureFlagItemComponent extends BaseComponent {
7115
7048
  this.loadSucceeded.emit();
7116
7049
  },
7117
7050
  error: (error) => {
7051
+ this.loading = false;
7118
7052
  this.loadFailed.emit(error);
7119
- throw error;
7120
7053
  },
7121
7054
  });
7122
7055
  }
@@ -7379,9 +7312,10 @@ class FeatureFlagItemComponent extends BaseComponent {
7379
7312
  && error.error
7380
7313
  && ("" + error.error).startsWith("Circular dependency detected.")) {
7381
7314
  const errorMessage = ("" + error.error).slice("Circular dependency detected.".length + 1);
7382
- this.openDiscardDialog("Cannot save feature flags", "Circular dependency detected between the following feature flags:<p>"
7383
- + errorMessage
7384
- + "</p>Please <strong>eliminate the circular dependency</strong> before saving your feature flags.", "I understand", "", true, "450px");
7315
+ this.openDiscardDialog("Cannot save feature flags", safeHtml `\
7316
+ Circular dependency detected between the following feature flags:
7317
+ <p>${errorMessage}</p>
7318
+ Please <strong>eliminate the circular dependency</strong> before saving your feature flags.`, "I understand", "", true, "450px");
7385
7319
  return;
7386
7320
  }
7387
7321
  if (error instanceof HttpErrorResponse && error.status === 404) {
@@ -7420,7 +7354,8 @@ class FeatureFlagItemComponent extends BaseComponent {
7420
7354
  this.dialog.open(RerouteDialogComponent, {
7421
7355
  data: {
7422
7356
  title: "You have reached the limits of your plan",
7423
- 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.',
7357
+ body: safeHtml `\
7358
+ Upgrade your plan or join our <a href="https://configcat.com/referrals" target="_blank" rel="noopener noreferrer">referral program</a> to increase your limits.`,
7424
7359
  yesButtonLabel: "See plans",
7425
7360
  yesButtonLink: this.dashboardBasePath + "/organization/plans",
7426
7361
  noButtonLabel: "Close",
@@ -8137,6 +8072,19 @@ class CreateFeatureFlagComponent extends BaseComponent {
8137
8072
  if (error instanceof HttpErrorResponse && error.status === 401) {
8138
8073
  this.componentError.emit(error);
8139
8074
  }
8075
+ if (error instanceof HttpErrorResponse && error.status === 402) {
8076
+ //override the form validity and show dialog
8077
+ this.formGroup.updateValueAndValidity();
8078
+ this.dialog.open(RerouteDialogComponent, {
8079
+ data: {
8080
+ title: "You have reached the limits of your plan",
8081
+ 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.',
8082
+ yesButtonLabel: "See plans",
8083
+ yesButtonLink: this.getDashboardBasePath() + "/organization/plans",
8084
+ noButtonLabel: "Close",
8085
+ },
8086
+ });
8087
+ }
8140
8088
  },
8141
8089
  });
8142
8090
  }
@@ -8255,13 +8203,13 @@ class CreateFeatureFlagComponent extends BaseComponent {
8255
8203
  this.componentError.emit(error);
8256
8204
  }
8257
8205
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: CreateFeatureFlagComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
8258
- 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 appDigitOnly\n focus\n [isFocused]=\"setFocus\"\n [formControl]=\"formControl\"\n [digitOnlyAllowNegatives]=\"true\" />\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: DigitOnlyDirective, selector: "[appDigitOnly]", inputs: ["digitOnlyDecimal", "digitOnlyDecimalSeparator", "digitOnlyAllowNegatives", "digitOnlyAllowPaste", "digitOnlyNegativeSign"] }, { 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 }); }
8206
+ 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 }); }
8259
8207
  }
8260
8208
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: CreateFeatureFlagComponent, decorators: [{
8261
8209
  type: Component,
8262
8210
  args: [{ selector: "app-create-feature-flag", changeDetection: ChangeDetectionStrategy.Eager, imports: [
8263
8211
  AutofocusDirective,
8264
- DigitOnlyDirective,
8212
+ InputDisplayNormalizerDirective,
8265
8213
  ProductSelectComponent,
8266
8214
  ConfigSelectComponent,
8267
8215
  EnvironmentSelectComponent,
@@ -8294,7 +8242,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImpor
8294
8242
  LoaderComponent,
8295
8243
  BoxComponent,
8296
8244
  MatCheckbox,
8297
- ], 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 appDigitOnly\n focus\n [isFocused]=\"setFocus\"\n [formControl]=\"formControl\"\n [digitOnlyAllowNegatives]=\"true\" />\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"] }]
8245
+ ], 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"] }]
8298
8246
  }], 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"] }] } });
8299
8247
 
8300
8248
  const DEFAULT_CUSTOMIZE_CREATE_CONFIG = {
@@ -8308,6 +8256,7 @@ class CreateConfigComponent extends BaseComponent {
8308
8256
  constructor() {
8309
8257
  super(...arguments);
8310
8258
  this.formBuilder = inject(NonNullableFormBuilder);
8259
+ this.dialog = inject(MatDialog);
8311
8260
  this.customize = input(DEFAULT_CUSTOMIZE_CREATE_CONFIG, /* @ts-ignore */
8312
8261
  ...(ngDevMode ? [{ debugName: "customize" }] : /* istanbul ignore next */ []));
8313
8262
  this.presetProduct = input(/* @ts-ignore */
@@ -8375,6 +8324,19 @@ class CreateConfigComponent extends BaseComponent {
8375
8324
  if (error instanceof HttpErrorResponse && error.status === 401) {
8376
8325
  this.componentError.emit(error);
8377
8326
  }
8327
+ if (error instanceof HttpErrorResponse && error.status === 402) {
8328
+ //override the form validity and show dialog
8329
+ this.formGroup.updateValueAndValidity();
8330
+ this.dialog.open(RerouteDialogComponent, {
8331
+ data: {
8332
+ title: "You have reached the limits of your plan",
8333
+ 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.',
8334
+ yesButtonLabel: "See plans",
8335
+ yesButtonLink: this.getDashboardBasePath() + "/organization/plans",
8336
+ noButtonLabel: "Close",
8337
+ },
8338
+ });
8339
+ }
8378
8340
  },
8379
8341
  });
8380
8342
  }