ng-configcat-publicapi-ui 5.6.3 → 5.6.5

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.
@@ -35,7 +35,7 @@ import { config, edit } from 'ace-builds/src-min-noconflict/ace';
35
35
  import 'ace-builds/src-noconflict/mode-json';
36
36
  import 'ace-builds/src-noconflict/theme-tomorrow';
37
37
  import 'ace-builds/src-noconflict/theme-github_dark';
38
- import { moveItemInArray, CdkDropList, CdkDrag, CdkDragHandle } from '@angular/cdk/drag-drop';
38
+ import { CdkDropList, CdkDrag, CdkDragHandle } from '@angular/cdk/drag-drop';
39
39
  import { MatDivider } from '@angular/material/divider';
40
40
  import { MatCheckbox } from '@angular/material/checkbox';
41
41
  import { CdkCopyToClipboard, Clipboard } from '@angular/cdk/clipboard';
@@ -2326,6 +2326,22 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImpor
2326
2326
  }]
2327
2327
  }], propDecorators: { selectedItem: [{ type: i0.Input, args: [{ isSignal: true, alias: "appDeselect", required: false }] }] } });
2328
2328
 
2329
+ // When moving items in a formArray, we should not do it via the recommended way on the angular dragndrop documentations.
2330
+ // It recommends a simple moveItemInArray(formArray.controls). But doing that will not fire change events, etc.
2331
+ // This item is working on the FormArray's recommended way to push, remove items - do it directly on the FormArray.
2332
+ // It is not an array, so we cannot call moveItemInArray on it, but it has methods to modify the array.
2333
+ function moveItemInFormArray(formArray, previousIndex, currentIndex) {
2334
+ if (previousIndex === currentIndex)
2335
+ return;
2336
+ const draggedItem = formArray.at(previousIndex);
2337
+ formArray.removeAt(previousIndex);
2338
+ // Insert at the drop index. CDK's currentIndex is the intended insertion position
2339
+ // after removal, inserting at event.currentIndex places the control in the
2340
+ // position equivalent to the CDK moveItemInArray behavior.
2341
+ formArray.insert(currentIndex, draggedItem);
2342
+ formArray.markAsDirty();
2343
+ }
2344
+
2329
2345
  class RolloutRulesComponent {
2330
2346
  constructor() {
2331
2347
  this.dialog = inject(MatDialog);
@@ -2375,9 +2391,7 @@ class RolloutRulesComponent {
2375
2391
  this.formGroup().controls.rolloutRules.markAsDirty();
2376
2392
  }
2377
2393
  onDrop(event) {
2378
- const rolloutRules = this.formGroup().controls.rolloutRules.controls;
2379
- moveItemInArray(rolloutRules, event.previousIndex, event.currentIndex);
2380
- this.formGroup().markAsDirty();
2394
+ moveItemInFormArray(this.formGroup().controls.rolloutRules, event.previousIndex, event.currentIndex);
2381
2395
  }
2382
2396
  attributeChanged(eventTarget, comparisonAttribute) {
2383
2397
  const event = eventTarget.value;
@@ -4389,15 +4403,26 @@ class VariationsDialogComponent {
4389
4403
  value: getPredefinedVariationValueDisplayValue(this.settingType, predefinedVariation.value),
4390
4404
  disabled: true,
4391
4405
  }),
4392
- usages: this.formBuilder.array(predefinedVariation.usages
4393
- .map(u => this.createPredefinedVariationUsageFormGroup(u, environments))
4394
- .filter(u => u !== null)
4395
- .sort((a, b) => a.environmentIndex - b.environmentIndex)
4396
- .map(u => u.formGroup)),
4406
+ usages: this.formBuilder.array([
4407
+ ...predefinedVariation.usages
4408
+ .map(u => this.createPredefinedVariationUsageFormGroup(u, environments))
4409
+ .filter(u => u !== null)
4410
+ .sort((a, b) => a.environmentIndex - b.environmentIndex)
4411
+ .map(u => u.formGroup),
4412
+ ...predefinedVariation.changeRequestUsages
4413
+ .map(u => this.createPredefinedVariationChangeRequestUsageFormGroup(u, environments))
4414
+ .filter(u => u !== null)
4415
+ .sort((a, b) => a.environmentIndex - b.environmentIndex)
4416
+ .map(u => u.formGroup),
4417
+ ]),
4397
4418
  usagesInOtherEnvironments: new FormControl({
4398
4419
  value: predefinedVariation.usagesInOtherEnvironments,
4399
4420
  disabled: true,
4400
4421
  }, { nonNullable: true }),
4422
+ changeRequestUsagesInOtherEnvironments: new FormControl({
4423
+ value: predefinedVariation.changeRequestUsagesInOtherEnvironments,
4424
+ disabled: true,
4425
+ }, { nonNullable: true }),
4401
4426
  variationId: new FormControl({
4402
4427
  value: getVariationIdData(this.settingKey, this.settingType, predefinedVariation.value).variationId,
4403
4428
  disabled: true,
@@ -4442,6 +4467,30 @@ class VariationsDialogComponent {
4442
4467
  environmentIndex: index,
4443
4468
  };
4444
4469
  }
4470
+ createPredefinedVariationChangeRequestUsageFormGroup(usage, environments) {
4471
+ const routerLink = this.getChangeRequestUrl(usage);
4472
+ const index = environments.findIndex(e => e.environmentId === usage.environmentId);
4473
+ if (index === -1)
4474
+ return null;
4475
+ const environment = environments[index];
4476
+ const displayValue = usage.changeRequestTitle + " (" + environment.name + ")";
4477
+ return {
4478
+ formGroup: this.formBuilder.group({
4479
+ routerLink: new FormControl({ value: routerLink, disabled: true }, { nonNullable: true }),
4480
+ displayValue: new FormControl({ value: displayValue, disabled: true }, { nonNullable: true }),
4481
+ }),
4482
+ environmentIndex: index,
4483
+ };
4484
+ }
4485
+ getChangeRequestUrl(usage) {
4486
+ const urlTree = this.router.createUrlTree([
4487
+ "product",
4488
+ this.data.productId,
4489
+ "change-requests",
4490
+ usage.changeRequestId,
4491
+ ]);
4492
+ return this.publicApiService.getDashboardBasePath() + this.router.serializeUrl(urlTree);
4493
+ }
4445
4494
  getDashboardUrl(usage) {
4446
4495
  const urlTree = this.router.createUrlTree([
4447
4496
  "v2",
@@ -4505,7 +4554,9 @@ class VariationsDialogComponent {
4505
4554
  hint: null,
4506
4555
  value: this.createEmptyPredefinedVariationValueModel(),
4507
4556
  usages: [],
4557
+ changeRequestUsages: [],
4508
4558
  usagesInOtherEnvironments: 0,
4559
+ changeRequestUsagesInOtherEnvironments: 0,
4509
4560
  }, []);
4510
4561
  this.formGroup.controls.predefinedVariations.push(predefinedVariationFormGroup);
4511
4562
  this.formGroup.controls.predefinedVariations.markAsDirty();
@@ -4566,15 +4617,14 @@ class VariationsDialogComponent {
4566
4617
  });
4567
4618
  }
4568
4619
  dropTable(event) {
4569
- moveItemInArray(this.formGroup.controls.predefinedVariations.controls, event.previousIndex, event.currentIndex);
4570
- this.formGroup.controls.predefinedVariations.markAsDirty();
4620
+ moveItemInFormArray(this.formGroup.controls.predefinedVariations, event.previousIndex, event.currentIndex);
4571
4621
  this.setDataSource();
4572
4622
  }
4573
4623
  getColorIndex(row, index) {
4574
4624
  return getPredefinedVariationColorIndex(this.settingType, getRawPredefinedVariationValue(row.controls.value), index);
4575
4625
  }
4576
4626
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: VariationsDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4577
- 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]=\"!row.controls.usages.controls.length && !row.controls.usagesInOtherEnvironments.value\"\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 } @else if (row.controls.usages.controls.length === 0) {\n <div>no usages</div>\n }\n } @else {\n <div>\n @if (row.controls.usages.controls.length + row.controls.usagesInOtherEnvironments.value > 0) {\n {{ row.controls.usages.controls.length + row.controls.usagesInOtherEnvironments.value }}\n usage{{\n row.controls.usages.controls.length + row.controls.usagesInOtherEnvironments.value > 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 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 \"\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 }); }
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 }); }
4578
4628
  }
4579
4629
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: VariationsDialogComponent, decorators: [{
4580
4630
  type: Component,
@@ -4613,7 +4663,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImpor
4613
4663
  CdkDragHandle,
4614
4664
  CopyableValueComponent,
4615
4665
  AutofocusDirective,
4616
- ], 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]=\"!row.controls.usages.controls.length && !row.controls.usagesInOtherEnvironments.value\"\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 } @else if (row.controls.usages.controls.length === 0) {\n <div>no usages</div>\n }\n } @else {\n <div>\n @if (row.controls.usages.controls.length + row.controls.usagesInOtherEnvironments.value > 0) {\n {{ row.controls.usages.controls.length + row.controls.usagesInOtherEnvironments.value }}\n usage{{\n row.controls.usages.controls.length + row.controls.usagesInOtherEnvironments.value > 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 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 \"\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"] }]
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"] }]
4617
4667
  }] });
4618
4668
 
4619
4669
  class DependeesDialogComponent {
@@ -6596,9 +6646,7 @@ class EvaluationFormulaComponent {
6596
6646
  return true;
6597
6647
  }
6598
6648
  onDrop(event) {
6599
- const targetingRules = this.formGroup().controls.targetingRules.controls;
6600
- moveItemInArray(targetingRules, event.previousIndex, event.currentIndex);
6601
- this.formGroup().markAsDirty();
6649
+ moveItemInFormArray(this.formGroup().controls.targetingRules, event.previousIndex, event.currentIndex);
6602
6650
  this.recalcAddButtons.emit();
6603
6651
  }
6604
6652
  isDragDisabled(targetingRule) {
@@ -7457,7 +7505,7 @@ class FeatureFlagItemComponent extends BaseComponent {
7457
7505
  return dialogRef.afterClosed();
7458
7506
  }
7459
7507
  canDeactivate() {
7460
- return !this.formGroup?.dirty;
7508
+ return !this.formGroupDirty();
7461
7509
  }
7462
7510
  viewVariations(event) {
7463
7511
  if (!this.canDeactivate()) {