@openziti/ziti-console-lib 1.5.2 → 1.5.3
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.
|
@@ -30,6 +30,8 @@ import * as i5$3 from '@angular/material/select';
|
|
|
30
30
|
import { MatSelect, MatSelectModule } from '@angular/material/select';
|
|
31
31
|
import * as i1$4 from '@angular/cdk/overlay';
|
|
32
32
|
import { OverlayModule } from '@angular/cdk/overlay';
|
|
33
|
+
import * as i6$1 from '@angular/cdk/drag-drop';
|
|
34
|
+
import { moveItemInArray, DragDropModule } from '@angular/cdk/drag-drop';
|
|
33
35
|
import { catchError, map as map$1, distinctUntilChanged, debounceTime, skip, takeUntil, throttleTime, filter as filter$1 } from 'rxjs/operators';
|
|
34
36
|
import * as i1$2 from 'ngx-logger';
|
|
35
37
|
import { NgxLoggerLevel } from 'ngx-logger';
|
|
@@ -2914,9 +2916,13 @@ class DataTableFilterService {
|
|
|
2914
2916
|
this.activatedRoute = activatedRoute;
|
|
2915
2917
|
this.filters = [];
|
|
2916
2918
|
this.currentPage = 1;
|
|
2919
|
+
/** Rows requested per page. Defaults to the legacy page size; the list-table's
|
|
2920
|
+
* rows-per-page control changes it via {@link changePageSize}. */
|
|
2921
|
+
this.pageSize = 50;
|
|
2917
2922
|
this.filtersChanged = new BehaviorSubject(this.filters);
|
|
2918
2923
|
this.filtering = new BehaviorSubject(false);
|
|
2919
2924
|
this.pageChanged = new BehaviorSubject(this.currentPage);
|
|
2925
|
+
this.pageSizeChanged = new BehaviorSubject(this.pageSize);
|
|
2920
2926
|
this.currentQueryParams = [];
|
|
2921
2927
|
}
|
|
2922
2928
|
updateFilter(filterObj) {
|
|
@@ -2962,6 +2968,11 @@ class DataTableFilterService {
|
|
|
2962
2968
|
this.currentPage = page;
|
|
2963
2969
|
this.pageChanged.next(page);
|
|
2964
2970
|
}
|
|
2971
|
+
changePageSize(size) {
|
|
2972
|
+
this.pageSize = size;
|
|
2973
|
+
this.currentPage = 1;
|
|
2974
|
+
this.pageSizeChanged.next(size);
|
|
2975
|
+
}
|
|
2965
2976
|
clearFilters() {
|
|
2966
2977
|
this.filters = [];
|
|
2967
2978
|
this.updateUrlParameters();
|
|
@@ -3510,6 +3521,14 @@ let ListPageServiceClass = class ListPageServiceClass {
|
|
|
3510
3521
|
this.basePath = pathSegments[0];
|
|
3511
3522
|
}
|
|
3512
3523
|
});
|
|
3524
|
+
// The subscription above misses the NavigationEnd that already fired before
|
|
3525
|
+
// this root-provided service was lazily constructed (first page load), which
|
|
3526
|
+
// would leave basePath empty and send name links / openEditForm to "/<id>".
|
|
3527
|
+
// Seed it from the current route immediately.
|
|
3528
|
+
const currentConfig = this.router?.routerState?.snapshot?.root?.firstChild?.routeConfig;
|
|
3529
|
+
if (currentConfig?.path) {
|
|
3530
|
+
this.basePath = currentConfig.path.split('/')[0];
|
|
3531
|
+
}
|
|
3513
3532
|
}
|
|
3514
3533
|
/**
|
|
3515
3534
|
* Root-provided page services survive route changes; restore baseline menus before each table init
|
|
@@ -3702,6 +3721,7 @@ class ListPageComponent {
|
|
|
3702
3721
|
this.filterApplied = false;
|
|
3703
3722
|
this.modalOpen = false;
|
|
3704
3723
|
this.gridObj = {};
|
|
3724
|
+
this.tableObj = null;
|
|
3705
3725
|
this.subscription = new Subscription();
|
|
3706
3726
|
this.growlerSvc = inject(GrowlerService);
|
|
3707
3727
|
this.managementPermissions = inject(ManagementPermissionsService);
|
|
@@ -3712,7 +3732,7 @@ class ListPageComponent {
|
|
|
3712
3732
|
this.svc.refreshData = this.refreshData.bind(this);
|
|
3713
3733
|
this.svc.resetMenusForInit();
|
|
3714
3734
|
this.svc.initMenuActions();
|
|
3715
|
-
this.columnDefs = this.
|
|
3735
|
+
this.columnDefs = this.loadTableColumns();
|
|
3716
3736
|
this.svc.capturePermissionSnapshots();
|
|
3717
3737
|
this.subscription.add(this.managementPermissions.stateVersion$.subscribe(() => {
|
|
3718
3738
|
this.svc.rebuildMenusAndHeaders(this.managementPermissions);
|
|
@@ -3731,6 +3751,9 @@ class ListPageComponent {
|
|
|
3731
3751
|
this.subscription.add(this.filterService.pageChanged.pipe(skip(1)).subscribe(page => {
|
|
3732
3752
|
this.refreshData();
|
|
3733
3753
|
}));
|
|
3754
|
+
this.subscription.add(this.filterService.pageSizeChanged.pipe(skip(1)).subscribe(() => {
|
|
3755
|
+
this.refreshData();
|
|
3756
|
+
}));
|
|
3734
3757
|
this.subscription.add(this.consoleEvents.closeSideModal.subscribe((event) => {
|
|
3735
3758
|
this.closeModal();
|
|
3736
3759
|
}));
|
|
@@ -3751,7 +3774,8 @@ class ListPageComponent {
|
|
|
3751
3774
|
const index = this.svc.tableHeaderActions.findIndex((a) => a.action === 'download-selected');
|
|
3752
3775
|
const allow = this.managementPermissions.canRead(this.svc.resourceType);
|
|
3753
3776
|
if (hasSelection && allow && index === -1) {
|
|
3754
|
-
|
|
3777
|
+
// icon (SVG) is rendered by the new list-table's header menu; legacy ignores it
|
|
3778
|
+
this.svc.tableHeaderActions.push({ name: 'Download Selected', action: 'download-selected', icon: '<svg viewBox="0 0 16 16" width="15" height="15"><path fill="none" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round" d="M8 2.5v7M5 6.6 8 9.6l3-3M3.2 12.6h9.6"/></svg>' });
|
|
3755
3779
|
}
|
|
3756
3780
|
if ((!hasSelection || !allow) && index !== -1) {
|
|
3757
3781
|
this.svc.tableHeaderActions.splice(index, 1);
|
|
@@ -3782,7 +3806,7 @@ class ListPageComponent {
|
|
|
3782
3806
|
}
|
|
3783
3807
|
this.isLoading = true;
|
|
3784
3808
|
sort = sort ? sort : this.svc.currentSort;
|
|
3785
|
-
this.svc.getData(this.filterService.filters, sort, this.filterService.currentPage)
|
|
3809
|
+
this.svc.getData(this.filterService.filters, sort, this.filterService.currentPage, this.filterService.pageSize)
|
|
3786
3810
|
.then((data) => {
|
|
3787
3811
|
this.rowData = [];
|
|
3788
3812
|
if (hardRefresh) {
|
|
@@ -3868,9 +3892,22 @@ class ListPageComponent {
|
|
|
3868
3892
|
}
|
|
3869
3893
|
return extensionFound;
|
|
3870
3894
|
}
|
|
3895
|
+
/**
|
|
3896
|
+
* Column set loaded into the table on init. Defaults to the service's ag-grid
|
|
3897
|
+
* column defs (the legacy {@link DataTableComponent}). A page that renders the
|
|
3898
|
+
* new {@link ListTableComponent} overrides this to return the `ListColumn` set
|
|
3899
|
+
* instead, leaving every other page untouched.
|
|
3900
|
+
*/
|
|
3901
|
+
loadTableColumns() {
|
|
3902
|
+
return this.svc.initTableColumns();
|
|
3903
|
+
}
|
|
3871
3904
|
gridReady(event) {
|
|
3872
3905
|
this.gridObj = event;
|
|
3873
3906
|
}
|
|
3907
|
+
/** Ready hook for {@link ListTableComponent}; keeps the instance for extension use. */
|
|
3908
|
+
tableReady(event) {
|
|
3909
|
+
this.tableObj = event?.component;
|
|
3910
|
+
}
|
|
3874
3911
|
refreshCells() {
|
|
3875
3912
|
defer(() => {
|
|
3876
3913
|
var params = {
|
|
@@ -6292,6 +6329,320 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
6292
6329
|
args: [ZITI_DATA_SERVICE]
|
|
6293
6330
|
}] }, { type: GrowlerService }] });
|
|
6294
6331
|
|
|
6332
|
+
/**
|
|
6333
|
+
* Name cell for {@link ListTableComponent}: a link to the entity's detail route,
|
|
6334
|
+
* with optional identity/router status circles and a prefix icon. Inputs come from
|
|
6335
|
+
* the column's `cellInputs(ctx)`: `{ row, cellValue, cellConfig }`, where
|
|
6336
|
+
* `cellConfig` carries `pathRoot`, `itemProp?`, `showIdentityIcons?`,
|
|
6337
|
+
* `showRouterIcons?`, `getNamePrefix?`, `cellNamePreCheck?`.
|
|
6338
|
+
*/
|
|
6339
|
+
class NameCellComponent {
|
|
6340
|
+
set row(v) {
|
|
6341
|
+
this.item = v;
|
|
6342
|
+
}
|
|
6343
|
+
set cellValue(v) {
|
|
6344
|
+
this.value = v;
|
|
6345
|
+
}
|
|
6346
|
+
set cellConfig(cfg) {
|
|
6347
|
+
this.cellParams = cfg || {};
|
|
6348
|
+
}
|
|
6349
|
+
constructor(router) {
|
|
6350
|
+
this.router = router;
|
|
6351
|
+
this.cellParams = {};
|
|
6352
|
+
}
|
|
6353
|
+
get namePrefix() {
|
|
6354
|
+
const fn = this.cellParams?.getNamePrefix;
|
|
6355
|
+
if (typeof fn !== 'function') {
|
|
6356
|
+
return null;
|
|
6357
|
+
}
|
|
6358
|
+
const result = fn(this.item);
|
|
6359
|
+
if (!result?.show) {
|
|
6360
|
+
return null;
|
|
6361
|
+
}
|
|
6362
|
+
return result;
|
|
6363
|
+
}
|
|
6364
|
+
linkClicked(event) {
|
|
6365
|
+
event.stopPropagation();
|
|
6366
|
+
event.preventDefault();
|
|
6367
|
+
if (isFunction(this.cellParams?.cellNamePreCheck)) {
|
|
6368
|
+
this.cellParams?.cellNamePreCheck(this.item).then((result) => {
|
|
6369
|
+
if (!result?.confirmed) {
|
|
6370
|
+
return;
|
|
6371
|
+
}
|
|
6372
|
+
this.navigate();
|
|
6373
|
+
});
|
|
6374
|
+
return;
|
|
6375
|
+
}
|
|
6376
|
+
this.navigate();
|
|
6377
|
+
}
|
|
6378
|
+
navigate() {
|
|
6379
|
+
this.router.navigateByUrl(`${this.cellParams.pathRoot}/${get(this.item, this.cellParams.itemProp, this.item.id)}`);
|
|
6380
|
+
}
|
|
6381
|
+
get cellText() {
|
|
6382
|
+
let val = this.value;
|
|
6383
|
+
if (isEmpty(val)) {
|
|
6384
|
+
val = this.item?.name;
|
|
6385
|
+
}
|
|
6386
|
+
return val;
|
|
6387
|
+
}
|
|
6388
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: NameCellComponent, deps: [{ token: i4.Router }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
6389
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: NameCellComponent, isStandalone: false, selector: "lib-name-cell", inputs: { row: "row", cellValue: "cellValue", cellConfig: "cellConfig" }, ngImport: i0, template: "<span class=\"cell-name-container\">\n <a href=\"./{{cellParams.pathRoot}}/{{item.id}}\" class=\"cell-name-link\" (click)=\"linkClicked($event)\">\n <div class=\"col cell-name-renderer\" data-id=\"{{item.id}}\">\n @if (cellParams.showIdentityIcons && item.edgeRouterConnectionStatus) {\n <div class=\"cell-status-container\">\n <span class=\"circle {{item.edgeRouterConnectionStatus}}\" title=\"Edge Router Connection Status\"></span>\n </div>\n }\n @if (cellParams.showIdentityIcons && !item.edgeRouterConnectionStatus) {\n <div class=\"cell-status-container\">\n <span class=\"circle {{item.hasApiSession}}\" title=\"Api Session\"></span>\n <span class=\"circle {{item.hasEdgeRouterConnection}}\" title=\"Edge Router Connected\"></span>\n </div>\n }\n @if (cellParams.showRouterIcons) {\n <div class=\"cell-status-container\">\n <span class=\"circle {{item.isVerified}}\" title=\"{{item.isVerified ? 'Verified' : 'Unverified'}}\"></span>\n <span class=\"circle {{item.isOnline}}\" title=\"{{item.isOnline ? 'Online' : 'Offline'}}\"></span>\n </div>\n }\n @if (namePrefix) {\n <div class=\"cell-name-prefix-wrap\" [matTooltip]=\"namePrefix.tooltip\" [matTooltipShowDelay]=\"0\">\n <span class=\"cell-name-prefix-icon\" [ngClass]=\"namePrefix.iconClass\"></span>\n </div>\n }\n <strong>{{cellText}}</strong>\n </div>\n </a>\n</span>\n", styles: [".cell-name-renderer{display:flex;flex-direction:row;align-items:center;gap:.3125rem}.cell-name-container{display:flex;flex-direction:row;flex-wrap:nowrap}.cell-name-link{display:inline-flex;align-items:center}.cell-status-container{position:relative;display:flex;align-items:center;justify-content:center;gap:.25rem}.cell-name-prefix-wrap{display:flex;align-items:center;justify-content:center}.cell-name-prefix-icon{color:var(--highlighted);font-size:1rem}.circle.online{background-color:var(--green)}.circle.unknown{background-color:var(--highlighted)}\n"], dependencies: [{ kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i3.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }] }); }
|
|
6390
|
+
}
|
|
6391
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: NameCellComponent, decorators: [{
|
|
6392
|
+
type: Component,
|
|
6393
|
+
args: [{ selector: 'lib-name-cell', standalone: false, template: "<span class=\"cell-name-container\">\n <a href=\"./{{cellParams.pathRoot}}/{{item.id}}\" class=\"cell-name-link\" (click)=\"linkClicked($event)\">\n <div class=\"col cell-name-renderer\" data-id=\"{{item.id}}\">\n @if (cellParams.showIdentityIcons && item.edgeRouterConnectionStatus) {\n <div class=\"cell-status-container\">\n <span class=\"circle {{item.edgeRouterConnectionStatus}}\" title=\"Edge Router Connection Status\"></span>\n </div>\n }\n @if (cellParams.showIdentityIcons && !item.edgeRouterConnectionStatus) {\n <div class=\"cell-status-container\">\n <span class=\"circle {{item.hasApiSession}}\" title=\"Api Session\"></span>\n <span class=\"circle {{item.hasEdgeRouterConnection}}\" title=\"Edge Router Connected\"></span>\n </div>\n }\n @if (cellParams.showRouterIcons) {\n <div class=\"cell-status-container\">\n <span class=\"circle {{item.isVerified}}\" title=\"{{item.isVerified ? 'Verified' : 'Unverified'}}\"></span>\n <span class=\"circle {{item.isOnline}}\" title=\"{{item.isOnline ? 'Online' : 'Offline'}}\"></span>\n </div>\n }\n @if (namePrefix) {\n <div class=\"cell-name-prefix-wrap\" [matTooltip]=\"namePrefix.tooltip\" [matTooltipShowDelay]=\"0\">\n <span class=\"cell-name-prefix-icon\" [ngClass]=\"namePrefix.iconClass\"></span>\n </div>\n }\n <strong>{{cellText}}</strong>\n </div>\n </a>\n</span>\n", styles: [".cell-name-renderer{display:flex;flex-direction:row;align-items:center;gap:.3125rem}.cell-name-container{display:flex;flex-direction:row;flex-wrap:nowrap}.cell-name-link{display:inline-flex;align-items:center}.cell-status-container{position:relative;display:flex;align-items:center;justify-content:center;gap:.25rem}.cell-name-prefix-wrap{display:flex;align-items:center;justify-content:center}.cell-name-prefix-icon{color:var(--highlighted);font-size:1rem}.circle.online{background-color:var(--green)}.circle.unknown{background-color:var(--highlighted)}\n"] }]
|
|
6394
|
+
}], ctorParameters: () => [{ type: i4.Router }], propDecorators: { row: [{
|
|
6395
|
+
type: Input
|
|
6396
|
+
}], cellValue: [{
|
|
6397
|
+
type: Input
|
|
6398
|
+
}], cellConfig: [{
|
|
6399
|
+
type: Input
|
|
6400
|
+
}] } });
|
|
6401
|
+
|
|
6402
|
+
/*
|
|
6403
|
+
Copyright NetFoundry Inc.
|
|
6404
|
+
|
|
6405
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
6406
|
+
you may not use this file except in compliance with the License.
|
|
6407
|
+
You may obtain a copy of the License at
|
|
6408
|
+
|
|
6409
|
+
https://www.apache.org/licenses/LICENSE-2.0
|
|
6410
|
+
|
|
6411
|
+
Unless required by applicable law or agreed to in writing, software
|
|
6412
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
6413
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
6414
|
+
See the License for the specific language governing permissions and
|
|
6415
|
+
limitations under the License.
|
|
6416
|
+
*/
|
|
6417
|
+
/**
|
|
6418
|
+
* Modern hover card for the identities O/S cell — shows the environment info
|
|
6419
|
+
* (OS, version, release, arch) in a clean key/value layout. Rendered by
|
|
6420
|
+
* ListTableComponent's hover-tooltip mechanism (column `tooltip`).
|
|
6421
|
+
*/
|
|
6422
|
+
class OsInfoTooltipComponent {
|
|
6423
|
+
get envInfo() {
|
|
6424
|
+
return this.row?.envInfo || this.row?.versionInfo || {};
|
|
6425
|
+
}
|
|
6426
|
+
get osClass() {
|
|
6427
|
+
const os = `${this.envInfo?.os || ''}`.toLowerCase();
|
|
6428
|
+
const ver = `${this.envInfo?.osVersion || ''}`.toLowerCase();
|
|
6429
|
+
if (ver.includes('windows') || os.includes('mingw') || os.includes('windows')) {
|
|
6430
|
+
return 'windows';
|
|
6431
|
+
}
|
|
6432
|
+
if (os.includes('darwin')) {
|
|
6433
|
+
return 'apple';
|
|
6434
|
+
}
|
|
6435
|
+
if (os.includes('linux')) {
|
|
6436
|
+
return 'linux';
|
|
6437
|
+
}
|
|
6438
|
+
if (os.includes('android')) {
|
|
6439
|
+
return 'android';
|
|
6440
|
+
}
|
|
6441
|
+
return 'other';
|
|
6442
|
+
}
|
|
6443
|
+
get osTitle() {
|
|
6444
|
+
switch (this.osClass) {
|
|
6445
|
+
case 'apple': return 'macOS';
|
|
6446
|
+
case 'windows': return 'Windows';
|
|
6447
|
+
case 'linux': return 'Linux';
|
|
6448
|
+
case 'android': return 'Android';
|
|
6449
|
+
default: return this.envInfo?.os || 'Unknown OS';
|
|
6450
|
+
}
|
|
6451
|
+
}
|
|
6452
|
+
/** Whether there is any environment info worth showing. */
|
|
6453
|
+
get hasInfo() {
|
|
6454
|
+
const e = this.envInfo;
|
|
6455
|
+
return !!(e && (e.os || e.osVersion || e.osRelease || e.arch));
|
|
6456
|
+
}
|
|
6457
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: OsInfoTooltipComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
6458
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: OsInfoTooltipComponent, isStandalone: false, selector: "lib-os-info-tooltip", inputs: { row: "row" }, ngImport: i0, template: "<div class=\"os-tip\">\n <div class=\"os-tip-head\">\n <span class=\"os-tip-icon {{ osClass }}\"></span>\n <span class=\"os-tip-title\">{{ osTitle }}</span>\n </div>\n @if (hasInfo) {\n <div class=\"os-tip-body\">\n @if (envInfo?.osVersion) {\n <div class=\"os-tip-row\"><span class=\"os-tip-key\">Version</span><span class=\"os-tip-val\">{{ envInfo.osVersion }}</span></div>\n }\n @if (envInfo?.osRelease) {\n <div class=\"os-tip-row\"><span class=\"os-tip-key\">Release</span><span class=\"os-tip-val\">{{ envInfo.osRelease }}</span></div>\n }\n @if (envInfo?.arch) {\n <div class=\"os-tip-row\"><span class=\"os-tip-key\">Arch</span><span class=\"os-tip-val\">{{ envInfo.arch }}</span></div>\n }\n </div>\n } @else {\n <div class=\"os-tip-empty\">No environment info reported</div>\n }\n</div>\n", styles: [":host{display:block}.os-tip{min-width:11rem;max-width:16rem;padding:.625rem .75rem;background:var(--lt-control-bg);border:1px solid var(--lt-border);border-radius:var(--lt-radius);box-shadow:var(--shadowLarge);font-family:var(--lt-font)}.os-tip-head{display:flex;align-items:center;gap:.5rem;padding-bottom:.5rem;margin-bottom:.5rem;border-bottom:1px solid var(--lt-border)}.os-tip-icon{flex:0 0 auto;width:1.25rem;height:1.25rem;background-size:contain;background-repeat:no-repeat;background-position:center}.os-tip-icon.apple{background-image:url(/assets/images/apple.png)}.os-tip-icon.windows{background-image:url(/assets/images/windows.png)}.os-tip-icon.linux{background-image:url(/assets/images/linux.png)}.os-tip-icon.android{background-image:url(/assets/images/android.png)}.os-tip-icon.other{background-image:url(/assets/images/other.png)}.os-tip-title{font-size:.875rem;font-weight:700;color:var(--lt-cell-text)}.os-tip-body{display:flex;flex-direction:column;gap:.3125rem}.os-tip-row{display:flex;align-items:baseline;justify-content:space-between;gap:.75rem;font-size:.75rem}.os-tip-key{flex:0 0 auto;color:var(--groupText);font-size:.75rem}.os-tip-val{flex:1 1 auto;min-width:0;text-align:right;font-weight:600;color:var(--lt-cell-text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.os-tip-empty{font-size:.75rem;font-style:italic;color:var(--groupText)}\n"] }); }
|
|
6459
|
+
}
|
|
6460
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: OsInfoTooltipComponent, decorators: [{
|
|
6461
|
+
type: Component,
|
|
6462
|
+
args: [{ selector: 'lib-os-info-tooltip', standalone: false, template: "<div class=\"os-tip\">\n <div class=\"os-tip-head\">\n <span class=\"os-tip-icon {{ osClass }}\"></span>\n <span class=\"os-tip-title\">{{ osTitle }}</span>\n </div>\n @if (hasInfo) {\n <div class=\"os-tip-body\">\n @if (envInfo?.osVersion) {\n <div class=\"os-tip-row\"><span class=\"os-tip-key\">Version</span><span class=\"os-tip-val\">{{ envInfo.osVersion }}</span></div>\n }\n @if (envInfo?.osRelease) {\n <div class=\"os-tip-row\"><span class=\"os-tip-key\">Release</span><span class=\"os-tip-val\">{{ envInfo.osRelease }}</span></div>\n }\n @if (envInfo?.arch) {\n <div class=\"os-tip-row\"><span class=\"os-tip-key\">Arch</span><span class=\"os-tip-val\">{{ envInfo.arch }}</span></div>\n }\n </div>\n } @else {\n <div class=\"os-tip-empty\">No environment info reported</div>\n }\n</div>\n", styles: [":host{display:block}.os-tip{min-width:11rem;max-width:16rem;padding:.625rem .75rem;background:var(--lt-control-bg);border:1px solid var(--lt-border);border-radius:var(--lt-radius);box-shadow:var(--shadowLarge);font-family:var(--lt-font)}.os-tip-head{display:flex;align-items:center;gap:.5rem;padding-bottom:.5rem;margin-bottom:.5rem;border-bottom:1px solid var(--lt-border)}.os-tip-icon{flex:0 0 auto;width:1.25rem;height:1.25rem;background-size:contain;background-repeat:no-repeat;background-position:center}.os-tip-icon.apple{background-image:url(/assets/images/apple.png)}.os-tip-icon.windows{background-image:url(/assets/images/windows.png)}.os-tip-icon.linux{background-image:url(/assets/images/linux.png)}.os-tip-icon.android{background-image:url(/assets/images/android.png)}.os-tip-icon.other{background-image:url(/assets/images/other.png)}.os-tip-title{font-size:.875rem;font-weight:700;color:var(--lt-cell-text)}.os-tip-body{display:flex;flex-direction:column;gap:.3125rem}.os-tip-row{display:flex;align-items:baseline;justify-content:space-between;gap:.75rem;font-size:.75rem}.os-tip-key{flex:0 0 auto;color:var(--groupText);font-size:.75rem}.os-tip-val{flex:1 1 auto;min-width:0;text-align:right;font-weight:600;color:var(--lt-cell-text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.os-tip-empty{font-size:.75rem;font-style:italic;color:var(--groupText)}\n"] }]
|
|
6463
|
+
}], propDecorators: { row: [{
|
|
6464
|
+
type: Input
|
|
6465
|
+
}] } });
|
|
6466
|
+
|
|
6467
|
+
/*
|
|
6468
|
+
Copyright NetFoundry Inc.
|
|
6469
|
+
|
|
6470
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
6471
|
+
you may not use this file except in compliance with the License.
|
|
6472
|
+
You may obtain a copy of the License at
|
|
6473
|
+
|
|
6474
|
+
https://www.apache.org/licenses/LICENSE-2.0
|
|
6475
|
+
|
|
6476
|
+
Unless required by applicable law or agreed to in writing, software
|
|
6477
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
6478
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
6479
|
+
See the License for the specific language governing permissions and
|
|
6480
|
+
limitations under the License.
|
|
6481
|
+
*/
|
|
6482
|
+
/**
|
|
6483
|
+
* Hover card for the identities SDK cell — shows the reported SDK details
|
|
6484
|
+
* (app id/version, version, type, branch, revision), the same fields the legacy
|
|
6485
|
+
* ag-grid SDK tooltip surfaced. Rendered by ListTableComponent's hover-tooltip
|
|
6486
|
+
* mechanism (column `tooltip`).
|
|
6487
|
+
*/
|
|
6488
|
+
class SdkInfoTooltipComponent {
|
|
6489
|
+
get sdkInfo() {
|
|
6490
|
+
return this.row?.sdkInfo || {};
|
|
6491
|
+
}
|
|
6492
|
+
get title() {
|
|
6493
|
+
const s = this.sdkInfo;
|
|
6494
|
+
return s?.appId || s?.type || 'SDK';
|
|
6495
|
+
}
|
|
6496
|
+
get hasInfo() {
|
|
6497
|
+
const s = this.sdkInfo;
|
|
6498
|
+
return !!(s && (s.appId || s.appVersion || s.version || s.type || s.branch || s.revision));
|
|
6499
|
+
}
|
|
6500
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: SdkInfoTooltipComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
6501
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: SdkInfoTooltipComponent, isStandalone: false, selector: "lib-sdk-info-tooltip", inputs: { row: "row" }, ngImport: i0, template: "<div class=\"sdk-tip\">\n <div class=\"sdk-tip-head\">\n <span class=\"sdk-tip-title\">{{ title }}</span>\n </div>\n @if (hasInfo) {\n <div class=\"sdk-tip-body\">\n @if (sdkInfo?.appId) {\n <div class=\"sdk-tip-row\"><span class=\"sdk-tip-key\">App ID</span><span class=\"sdk-tip-val\">{{ sdkInfo.appId }}</span></div>\n }\n @if (sdkInfo?.appVersion) {\n <div class=\"sdk-tip-row\"><span class=\"sdk-tip-key\">App Version</span><span class=\"sdk-tip-val\">{{ sdkInfo.appVersion }}</span></div>\n }\n @if (sdkInfo?.version) {\n <div class=\"sdk-tip-row\"><span class=\"sdk-tip-key\">Version</span><span class=\"sdk-tip-val\">{{ sdkInfo.version }}</span></div>\n }\n @if (sdkInfo?.type) {\n <div class=\"sdk-tip-row\"><span class=\"sdk-tip-key\">Type</span><span class=\"sdk-tip-val\">{{ sdkInfo.type }}</span></div>\n }\n @if (sdkInfo?.branch) {\n <div class=\"sdk-tip-row\"><span class=\"sdk-tip-key\">Branch</span><span class=\"sdk-tip-val\">{{ sdkInfo.branch }}</span></div>\n }\n @if (sdkInfo?.revision) {\n <div class=\"sdk-tip-row\"><span class=\"sdk-tip-key\">Revision</span><span class=\"sdk-tip-val\">{{ sdkInfo.revision }}</span></div>\n }\n </div>\n } @else {\n <div class=\"sdk-tip-empty\">No SDK info reported</div>\n }\n</div>\n", styles: [":host{display:block}.sdk-tip{min-width:11rem;max-width:18rem;padding:.625rem .75rem;background:var(--lt-control-bg);border:1px solid var(--lt-border);border-radius:var(--lt-radius);box-shadow:var(--shadowLarge);font-family:var(--lt-font)}.sdk-tip-head{display:flex;align-items:center;gap:.5rem;padding-bottom:.5rem;margin-bottom:.5rem;border-bottom:1px solid var(--lt-border)}.sdk-tip-title{font-size:.875rem;font-weight:700;color:var(--lt-cell-text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sdk-tip-body{display:flex;flex-direction:column;gap:.3125rem}.sdk-tip-row{display:flex;align-items:baseline;justify-content:space-between;gap:.75rem;font-size:.75rem}.sdk-tip-key{flex:0 0 auto;color:var(--groupText);font-size:.75rem}.sdk-tip-val{flex:1 1 auto;min-width:0;text-align:right;font-weight:600;color:var(--lt-cell-text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sdk-tip-empty{font-size:.75rem;font-style:italic;color:var(--groupText)}\n"] }); }
|
|
6502
|
+
}
|
|
6503
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: SdkInfoTooltipComponent, decorators: [{
|
|
6504
|
+
type: Component,
|
|
6505
|
+
args: [{ selector: 'lib-sdk-info-tooltip', standalone: false, template: "<div class=\"sdk-tip\">\n <div class=\"sdk-tip-head\">\n <span class=\"sdk-tip-title\">{{ title }}</span>\n </div>\n @if (hasInfo) {\n <div class=\"sdk-tip-body\">\n @if (sdkInfo?.appId) {\n <div class=\"sdk-tip-row\"><span class=\"sdk-tip-key\">App ID</span><span class=\"sdk-tip-val\">{{ sdkInfo.appId }}</span></div>\n }\n @if (sdkInfo?.appVersion) {\n <div class=\"sdk-tip-row\"><span class=\"sdk-tip-key\">App Version</span><span class=\"sdk-tip-val\">{{ sdkInfo.appVersion }}</span></div>\n }\n @if (sdkInfo?.version) {\n <div class=\"sdk-tip-row\"><span class=\"sdk-tip-key\">Version</span><span class=\"sdk-tip-val\">{{ sdkInfo.version }}</span></div>\n }\n @if (sdkInfo?.type) {\n <div class=\"sdk-tip-row\"><span class=\"sdk-tip-key\">Type</span><span class=\"sdk-tip-val\">{{ sdkInfo.type }}</span></div>\n }\n @if (sdkInfo?.branch) {\n <div class=\"sdk-tip-row\"><span class=\"sdk-tip-key\">Branch</span><span class=\"sdk-tip-val\">{{ sdkInfo.branch }}</span></div>\n }\n @if (sdkInfo?.revision) {\n <div class=\"sdk-tip-row\"><span class=\"sdk-tip-key\">Revision</span><span class=\"sdk-tip-val\">{{ sdkInfo.revision }}</span></div>\n }\n </div>\n } @else {\n <div class=\"sdk-tip-empty\">No SDK info reported</div>\n }\n</div>\n", styles: [":host{display:block}.sdk-tip{min-width:11rem;max-width:18rem;padding:.625rem .75rem;background:var(--lt-control-bg);border:1px solid var(--lt-border);border-radius:var(--lt-radius);box-shadow:var(--shadowLarge);font-family:var(--lt-font)}.sdk-tip-head{display:flex;align-items:center;gap:.5rem;padding-bottom:.5rem;margin-bottom:.5rem;border-bottom:1px solid var(--lt-border)}.sdk-tip-title{font-size:.875rem;font-weight:700;color:var(--lt-cell-text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sdk-tip-body{display:flex;flex-direction:column;gap:.3125rem}.sdk-tip-row{display:flex;align-items:baseline;justify-content:space-between;gap:.75rem;font-size:.75rem}.sdk-tip-key{flex:0 0 auto;color:var(--groupText);font-size:.75rem}.sdk-tip-val{flex:1 1 auto;min-width:0;text-align:right;font-weight:600;color:var(--lt-cell-text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sdk-tip-empty{font-size:.75rem;font-style:italic;color:var(--groupText)}\n"] }]
|
|
6506
|
+
}], propDecorators: { row: [{
|
|
6507
|
+
type: Input
|
|
6508
|
+
}] } });
|
|
6509
|
+
|
|
6510
|
+
/*
|
|
6511
|
+
Copyright NetFoundry Inc.
|
|
6512
|
+
|
|
6513
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
6514
|
+
you may not use this file except in compliance with the License.
|
|
6515
|
+
You may obtain a copy of the License at
|
|
6516
|
+
|
|
6517
|
+
https://www.apache.org/licenses/LICENSE-2.0
|
|
6518
|
+
|
|
6519
|
+
Unless required by applicable law or agreed to in writing, software
|
|
6520
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
6521
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
6522
|
+
See the License for the specific language governing permissions and
|
|
6523
|
+
limitations under the License.
|
|
6524
|
+
*/
|
|
6525
|
+
/**
|
|
6526
|
+
* Renders a truthy boolean as a green check + domain label (via `trueLabel`);
|
|
6527
|
+
* a false value renders nothing, so the column reads as a clean presence marker.
|
|
6528
|
+
*/
|
|
6529
|
+
class BooleanCellComponent {
|
|
6530
|
+
constructor() {
|
|
6531
|
+
this.trueLabel = 'Yes';
|
|
6532
|
+
}
|
|
6533
|
+
get isTrue() {
|
|
6534
|
+
return this.value === true || this.value === 'true';
|
|
6535
|
+
}
|
|
6536
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: BooleanCellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
6537
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: BooleanCellComponent, isStandalone: false, selector: "lib-boolean-cell", inputs: { value: "value", trueLabel: "trueLabel" }, ngImport: i0, template: "@if (isTrue) {\n <span class=\"bool bool-true\">\n <svg class=\"bool-check\" viewBox=\"0 0 14 14\" width=\"12\" height=\"12\" aria-hidden=\"true\">\n <path fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M2.5 7.6 5.7 10.8 11.5 3.6\"/>\n </svg>\n <span class=\"bool-label\">{{ trueLabel }}</span>\n </span>\n}\n", styles: [":host{display:inline-flex;align-items:center}.bool-true{display:inline-flex;align-items:center;gap:.3125rem;padding:.1875rem .5rem .1875rem .4375rem;font-family:var(--lt-font);font-size:.75rem;font-weight:600;line-height:1.2;color:#15803d;background:color-mix(in srgb,#16a34a 12%,transparent);border-radius:999px}.bool-check{flex:0 0 auto;display:block;color:#16a34a}\n"] }); }
|
|
6538
|
+
}
|
|
6539
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: BooleanCellComponent, decorators: [{
|
|
6540
|
+
type: Component,
|
|
6541
|
+
args: [{ selector: 'lib-boolean-cell', standalone: false, template: "@if (isTrue) {\n <span class=\"bool bool-true\">\n <svg class=\"bool-check\" viewBox=\"0 0 14 14\" width=\"12\" height=\"12\" aria-hidden=\"true\">\n <path fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M2.5 7.6 5.7 10.8 11.5 3.6\"/>\n </svg>\n <span class=\"bool-label\">{{ trueLabel }}</span>\n </span>\n}\n", styles: [":host{display:inline-flex;align-items:center}.bool-true{display:inline-flex;align-items:center;gap:.3125rem;padding:.1875rem .5rem .1875rem .4375rem;font-family:var(--lt-font);font-size:.75rem;font-weight:600;line-height:1.2;color:#15803d;background:color-mix(in srgb,#16a34a 12%,transparent);border-radius:999px}.bool-check{flex:0 0 auto;display:block;color:#16a34a}\n"] }]
|
|
6542
|
+
}], propDecorators: { value: [{
|
|
6543
|
+
type: Input
|
|
6544
|
+
}], trueLabel: [{
|
|
6545
|
+
type: Input
|
|
6546
|
+
}] } });
|
|
6547
|
+
|
|
6548
|
+
/*
|
|
6549
|
+
Copyright NetFoundry Inc.
|
|
6550
|
+
|
|
6551
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
6552
|
+
you may not use this file except in compliance with the License.
|
|
6553
|
+
You may obtain a copy of the License at
|
|
6554
|
+
|
|
6555
|
+
https://www.apache.org/licenses/LICENSE-2.0
|
|
6556
|
+
|
|
6557
|
+
Unless required by applicable law or agreed to in writing, software
|
|
6558
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
6559
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
6560
|
+
See the License for the specific language governing permissions and
|
|
6561
|
+
limitations under the License.
|
|
6562
|
+
*/
|
|
6563
|
+
/**
|
|
6564
|
+
* Enrollment-token cell for the identities list. Shows a status chip (Available /
|
|
6565
|
+
* Expired / —) that doubles as a dropdown of enrollment actions (Download JWT, Show
|
|
6566
|
+
* QR, Re-issue). Actions are emitted via the cell context's `emitAction`, so they run
|
|
6567
|
+
* through the same page handlers as the row ⋯ menu.
|
|
6568
|
+
*/
|
|
6569
|
+
class TokenCellComponent {
|
|
6570
|
+
constructor() {
|
|
6571
|
+
this.open = false;
|
|
6572
|
+
this.menuLeft = 0;
|
|
6573
|
+
this.menuTop = 0;
|
|
6574
|
+
}
|
|
6575
|
+
get status() {
|
|
6576
|
+
if (!this.hasEnrolmentToken) {
|
|
6577
|
+
return 'none';
|
|
6578
|
+
}
|
|
6579
|
+
return this.tokenExpired ? 'expired' : 'available';
|
|
6580
|
+
}
|
|
6581
|
+
get statusLabel() {
|
|
6582
|
+
return this.status === 'expired' ? 'Expired' : 'Available';
|
|
6583
|
+
}
|
|
6584
|
+
get actions() {
|
|
6585
|
+
if (this.status === 'available') {
|
|
6586
|
+
return [
|
|
6587
|
+
{ label: 'Download JWT', action: 'download-enrollment', icon: 'icon-download-jwt' },
|
|
6588
|
+
{ label: 'Show QR code', action: 'qr-code', icon: 'icon-qr' },
|
|
6589
|
+
{ label: 'Re-issue enrollment', action: 'reissue-enrollment', icon: 'icon-refresh' },
|
|
6590
|
+
];
|
|
6591
|
+
}
|
|
6592
|
+
if (this.status === 'expired') {
|
|
6593
|
+
return [{ label: 'Re-issue enrollment', action: 'reissue-enrollment', icon: 'icon-refresh' }];
|
|
6594
|
+
}
|
|
6595
|
+
return [];
|
|
6596
|
+
}
|
|
6597
|
+
toggleMenu(event) {
|
|
6598
|
+
event.stopPropagation();
|
|
6599
|
+
if (this.open) {
|
|
6600
|
+
this.open = false;
|
|
6601
|
+
return;
|
|
6602
|
+
}
|
|
6603
|
+
const rect = this.trigger?.nativeElement.getBoundingClientRect();
|
|
6604
|
+
if (rect) {
|
|
6605
|
+
this.menuLeft = rect.left;
|
|
6606
|
+
this.menuTop = rect.bottom + 4;
|
|
6607
|
+
}
|
|
6608
|
+
this.open = true;
|
|
6609
|
+
}
|
|
6610
|
+
closeMenu() {
|
|
6611
|
+
this.open = false;
|
|
6612
|
+
}
|
|
6613
|
+
runAction(action, event) {
|
|
6614
|
+
event.stopPropagation();
|
|
6615
|
+
this.open = false;
|
|
6616
|
+
this.emitAction?.(action, this.row);
|
|
6617
|
+
}
|
|
6618
|
+
get hasEnrolmentToken() {
|
|
6619
|
+
const token = this.row?.enrollment?.ott?.jwt ||
|
|
6620
|
+
this.row?.enrollment?.ottca?.jwt ||
|
|
6621
|
+
this.row?.enrollment?.updb?.jwt;
|
|
6622
|
+
return !!(this.row?.enrollmentToken || token);
|
|
6623
|
+
}
|
|
6624
|
+
get tokenExpired() {
|
|
6625
|
+
const expiration = this.row?.enrollmentExpiresAt ||
|
|
6626
|
+
this.row?.enrollment?.ott?.expiresAt ||
|
|
6627
|
+
this.row?.enrollment?.ottca?.expiresAt ||
|
|
6628
|
+
this.row?.enrollment?.updb?.expiresAt;
|
|
6629
|
+
return expiration ? moment$1(expiration).isBefore() : false;
|
|
6630
|
+
}
|
|
6631
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: TokenCellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
6632
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: TokenCellComponent, isStandalone: false, selector: "lib-token-cell", inputs: { row: "row", emitAction: "emitAction" }, viewQueries: [{ propertyName: "trigger", first: true, predicate: ["trigger"], descendants: true }], ngImport: i0, template: "@if (status !== 'none') {\n <button\n #trigger\n type=\"button\"\n class=\"token-chip {{ status }}\"\n [class.is-open]=\"open\"\n (click)=\"toggleMenu($event)\"\n aria-haspopup=\"menu\"\n [attr.aria-expanded]=\"open\"\n >\n <span class=\"token-chip-label\">{{ statusLabel }}</span>\n <svg class=\"token-caret\" viewBox=\"0 0 10 6\" width=\"9\" height=\"6\" aria-hidden=\"true\">\n <path fill=\"currentColor\" d=\"M0 0h10L5 6z\"/>\n </svg>\n </button>\n\n @if (open) {\n <div class=\"token-menu\" [ngStyle]=\"{ left: menuLeft + 'px', top: menuTop + 'px' }\" (clickOutside)=\"closeMenu()\">\n @for (a of actions; track a.action) {\n <button type=\"button\" class=\"token-menu-item\" (click)=\"runAction(a.action, $event)\">\n <span class=\"token-menu-icon {{ a.icon }}\" aria-hidden=\"true\"></span>\n <span class=\"token-menu-label\">{{ a.label }}</span>\n </button>\n }\n </div>\n }\n} @else {\n <span class=\"token-empty\">—</span>\n}\n", styles: [":host{display:inline-flex;align-items:center}.token-chip{display:inline-flex;align-items:center;gap:.3125rem;height:1.5rem;padding:0 .5rem;font-family:var(--lt-font);font-size:.75rem;font-weight:600;border:none;border-radius:.75rem;cursor:pointer;white-space:nowrap;transition:filter .15s}.token-chip .token-caret{display:block;opacity:.75}.token-chip:hover,.token-chip.is-open{filter:brightness(.96)}.token-chip.available{color:var(--lt-link);background:color-mix(in srgb,var(--lt-link) 12%,var(--lt-control-bg))}.token-chip.expired{color:color-mix(in srgb,var(--highlighted) 62%,#000);background:color-mix(in srgb,var(--highlighted) 26%,var(--lt-control-bg))}.token-empty{color:var(--groupText)}.token-menu{position:fixed;z-index:1000;min-width:11rem;padding:.25rem;background:var(--lt-control-bg);border:1px solid var(--lt-border);border-radius:var(--lt-radius);box-shadow:var(--shadowLarge);animation:token-in .1s ease-out}@keyframes token-in{0%{opacity:0;transform:translateY(-.25rem)}to{opacity:1;transform:translateY(0)}}.token-menu-item{display:flex;align-items:center;gap:.5rem;width:100%;padding:.4375rem .5rem;font-family:var(--lt-font);font-size:.8125rem;color:var(--lt-cell-text);text-align:left;background:transparent;border:none;border-radius:var(--lt-radius);cursor:pointer}.token-menu-item:hover{background:var(--lt-row-hover);color:var(--lt-link)}.token-menu-item:hover .token-menu-icon{color:var(--lt-link)}.token-menu-icon{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;width:1rem;font-size:.9375rem;color:var(--lt-icon)}\n"], dependencies: [{ kind: "directive", type: i1$1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i3$1.ClickOutsideDirective, selector: "[clickOutside]", inputs: ["clickOutsideEnabled", "attachOutsideOnClick", "delayClickOutsideInit", "emitOnBlur", "exclude", "excludeBeforeClick", "clickOutsideEvents"], outputs: ["clickOutside"] }] }); }
|
|
6633
|
+
}
|
|
6634
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: TokenCellComponent, decorators: [{
|
|
6635
|
+
type: Component,
|
|
6636
|
+
args: [{ selector: 'lib-token-cell', standalone: false, template: "@if (status !== 'none') {\n <button\n #trigger\n type=\"button\"\n class=\"token-chip {{ status }}\"\n [class.is-open]=\"open\"\n (click)=\"toggleMenu($event)\"\n aria-haspopup=\"menu\"\n [attr.aria-expanded]=\"open\"\n >\n <span class=\"token-chip-label\">{{ statusLabel }}</span>\n <svg class=\"token-caret\" viewBox=\"0 0 10 6\" width=\"9\" height=\"6\" aria-hidden=\"true\">\n <path fill=\"currentColor\" d=\"M0 0h10L5 6z\"/>\n </svg>\n </button>\n\n @if (open) {\n <div class=\"token-menu\" [ngStyle]=\"{ left: menuLeft + 'px', top: menuTop + 'px' }\" (clickOutside)=\"closeMenu()\">\n @for (a of actions; track a.action) {\n <button type=\"button\" class=\"token-menu-item\" (click)=\"runAction(a.action, $event)\">\n <span class=\"token-menu-icon {{ a.icon }}\" aria-hidden=\"true\"></span>\n <span class=\"token-menu-label\">{{ a.label }}</span>\n </button>\n }\n </div>\n }\n} @else {\n <span class=\"token-empty\">—</span>\n}\n", styles: [":host{display:inline-flex;align-items:center}.token-chip{display:inline-flex;align-items:center;gap:.3125rem;height:1.5rem;padding:0 .5rem;font-family:var(--lt-font);font-size:.75rem;font-weight:600;border:none;border-radius:.75rem;cursor:pointer;white-space:nowrap;transition:filter .15s}.token-chip .token-caret{display:block;opacity:.75}.token-chip:hover,.token-chip.is-open{filter:brightness(.96)}.token-chip.available{color:var(--lt-link);background:color-mix(in srgb,var(--lt-link) 12%,var(--lt-control-bg))}.token-chip.expired{color:color-mix(in srgb,var(--highlighted) 62%,#000);background:color-mix(in srgb,var(--highlighted) 26%,var(--lt-control-bg))}.token-empty{color:var(--groupText)}.token-menu{position:fixed;z-index:1000;min-width:11rem;padding:.25rem;background:var(--lt-control-bg);border:1px solid var(--lt-border);border-radius:var(--lt-radius);box-shadow:var(--shadowLarge);animation:token-in .1s ease-out}@keyframes token-in{0%{opacity:0;transform:translateY(-.25rem)}to{opacity:1;transform:translateY(0)}}.token-menu-item{display:flex;align-items:center;gap:.5rem;width:100%;padding:.4375rem .5rem;font-family:var(--lt-font);font-size:.8125rem;color:var(--lt-cell-text);text-align:left;background:transparent;border:none;border-radius:var(--lt-radius);cursor:pointer}.token-menu-item:hover{background:var(--lt-row-hover);color:var(--lt-link)}.token-menu-item:hover .token-menu-icon{color:var(--lt-link)}.token-menu-icon{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;width:1rem;font-size:.9375rem;color:var(--lt-icon)}\n"] }]
|
|
6637
|
+
}], propDecorators: { row: [{
|
|
6638
|
+
type: Input
|
|
6639
|
+
}], emitAction: [{
|
|
6640
|
+
type: Input
|
|
6641
|
+
}], trigger: [{
|
|
6642
|
+
type: ViewChild,
|
|
6643
|
+
args: ['trigger']
|
|
6644
|
+
}] } });
|
|
6645
|
+
|
|
6295
6646
|
/*
|
|
6296
6647
|
Copyright NetFoundry Inc.
|
|
6297
6648
|
|
|
@@ -6343,19 +6694,21 @@ class IdentitiesPageService extends ListPageServiceClass {
|
|
|
6343
6694
|
os: '',
|
|
6344
6695
|
createdAt: '',
|
|
6345
6696
|
};
|
|
6697
|
+
// `icon` (SVG markup) is rendered before the label in the new list-table's row kebab
|
|
6698
|
+
// (3-dot) menu. The legacy ag-grid menu ignores it.
|
|
6346
6699
|
this.menuItems = [
|
|
6347
|
-
{ name: 'Edit', action: 'update' },
|
|
6348
|
-
{ name: 'Download JWT', action: 'download-enrollment' },
|
|
6349
|
-
{ name: 'View QR', action: 'qr-code' },
|
|
6350
|
-
{ name: 'Visualizer', action: 'identity-service-path' },
|
|
6351
|
-
{ name: 'Reset Enrollment', action: 'reset-enrollment' },
|
|
6352
|
-
{ name: 'Reissue Enrollment', action: 'reissue-enrollment' },
|
|
6353
|
-
{ name: 'Reset MFA', action: 'reset-mfa' },
|
|
6354
|
-
{ name: 'Override', action: 'override' },
|
|
6355
|
-
{ name: 'Delete', action: 'delete' },
|
|
6700
|
+
{ name: 'Edit', action: 'update', icon: '<svg viewBox="0 0 16 16" width="15" height="15"><path fill="none" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round" d="M11.2 2.6 13.4 4.8 6 12.2l-2.8.6.6-2.8z"/></svg>' },
|
|
6701
|
+
{ name: 'Download JWT', action: 'download-enrollment', icon: '<svg viewBox="0 0 16 16" width="15" height="15"><path fill="none" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round" d="M8 2.5v7M5 6.6 8 9.6l3-3M3.2 12.6h9.6"/></svg>' },
|
|
6702
|
+
{ name: 'View QR', action: 'qr-code', icon: '<svg viewBox="0 0 16 16" width="15" height="15"><g fill="none" stroke="currentColor" stroke-width="1.3"><rect x="2.5" y="2.5" width="4" height="4" rx="0.5"/><rect x="9.5" y="2.5" width="4" height="4" rx="0.5"/><rect x="2.5" y="9.5" width="4" height="4" rx="0.5"/></g><rect x="10" y="10" width="3.5" height="3.5" fill="currentColor"/></svg>' },
|
|
6703
|
+
{ name: 'Visualizer', action: 'identity-service-path', icon: '<svg viewBox="0 0 16 16" width="15" height="15"><path fill="none" stroke="currentColor" stroke-width="1.3" d="M5.4 7.3 10.6 4.6M5.4 8.7l5.2 2.7"/><circle cx="4" cy="8" r="1.8" fill="currentColor"/><circle cx="12" cy="4" r="1.8" fill="currentColor"/><circle cx="12" cy="12" r="1.8" fill="currentColor"/></svg>' },
|
|
6704
|
+
{ name: 'Reset Enrollment', action: 'reset-enrollment', icon: '<svg viewBox="0 0 16 16" width="15" height="15"><path fill="none" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" d="M3.2 8a4.8 4.8 0 1 0 1.3-3.3"/><path fill="currentColor" d="M2.8 2.8l-.3 2.9 2.8-.4z"/></svg>' },
|
|
6705
|
+
{ name: 'Reissue Enrollment', action: 'reissue-enrollment', icon: '<svg viewBox="0 0 16 16" width="15" height="15"><path fill="none" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" d="M12.8 8a4.8 4.8 0 1 1-1.3-3.3"/><path fill="currentColor" d="M13.2 2.8l.3 2.9-2.8-.4z"/></svg>' },
|
|
6706
|
+
{ name: 'Reset MFA', action: 'reset-mfa', icon: '<svg viewBox="0 0 16 16" width="15" height="15"><path fill="none" stroke="currentColor" stroke-width="1.3" stroke-linejoin="round" d="M8 2.3 12.8 4v3.8c0 3-2.1 4.9-4.8 5.9-2.7-1-4.8-2.9-4.8-5.9V4z"/></svg>' },
|
|
6707
|
+
{ name: 'Override', action: 'override', icon: '<svg viewBox="0 0 16 16" width="15" height="15"><path fill="none" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" d="M2.8 5h10.4M2.8 11h10.4"/><circle cx="6" cy="5" r="1.7" fill="currentColor"/><circle cx="10" cy="11" r="1.7" fill="currentColor"/></svg>' },
|
|
6708
|
+
{ name: 'Delete', action: 'delete', icon: '<svg viewBox="0 0 16 16" width="15" height="15"><path fill="none" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round" d="M3.4 4.6h9.2M6.4 4.6V3.2h3.2v1.4M4.6 4.6 5 13h6l.4-8.4"/></svg>' },
|
|
6356
6709
|
];
|
|
6357
6710
|
this.tableHeaderActions = [
|
|
6358
|
-
{ name: 'Download All', action: 'download-all' },
|
|
6711
|
+
{ name: 'Download All', action: 'download-all', icon: '<svg viewBox="0 0 16 16" width="15" height="15"><path fill="none" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round" d="M8 2.5v7M5 6.6 8 9.6l3-3M3.2 12.6h9.6"/></svg>' },
|
|
6359
6712
|
];
|
|
6360
6713
|
this.resourceType = 'identities';
|
|
6361
6714
|
this.validate = (formData) => {
|
|
@@ -6611,15 +6964,280 @@ class IdentitiesPageService extends ListPageServiceClass {
|
|
|
6611
6964
|
}
|
|
6612
6965
|
return tableColumns;
|
|
6613
6966
|
}
|
|
6967
|
+
/**
|
|
6968
|
+
* Column set for the new `lib-list-table` (the "beta" Identities page). Mirrors
|
|
6969
|
+
* {@link initTableColumns} but emits the declarative `ListColumn` shape instead of
|
|
6970
|
+
* ag-grid `ColDef`s: component cells via `cell`/`cellInputs`, legacy string
|
|
6971
|
+
* renderers via `cellHtml`, and header filters declared with `filterType`.
|
|
6972
|
+
* Loaded by {@link IdentitiesPageComponent} when the Beta Features toggle is on;
|
|
6973
|
+
* the legacy {@link initTableColumns} above is left untouched for the ag-grid table.
|
|
6974
|
+
*/
|
|
6975
|
+
initListTableColumns() {
|
|
6976
|
+
this.initMenuActions();
|
|
6977
|
+
const identityRolesHeaderComponentParams = {
|
|
6978
|
+
filterType: 'ATTRIBUTE',
|
|
6979
|
+
enableSorting: true,
|
|
6980
|
+
appendAttributeHash: false,
|
|
6981
|
+
getRoleAttributes: () => this.identityRoleAttributes,
|
|
6982
|
+
getNamedAttributes: () => [],
|
|
6983
|
+
getSelectedRoleAttributes: () => this.selectedIdentityRoleAttributes,
|
|
6984
|
+
getSelectedNamedAttributes: () => [],
|
|
6985
|
+
setSelectedRoleAttributes: (attributes) => {
|
|
6986
|
+
this.selectedIdentityRoleAttributes = attributes;
|
|
6987
|
+
},
|
|
6988
|
+
setSelectedNamedAttributes: (attributes) => {
|
|
6989
|
+
// no-op
|
|
6990
|
+
},
|
|
6991
|
+
getNamedAttributesMap: () => ({}),
|
|
6992
|
+
};
|
|
6993
|
+
const osRenderer = (row) => {
|
|
6994
|
+
const os = (row?.data?.envInfo?.os || '').toLowerCase();
|
|
6995
|
+
const ver = (row?.data?.envInfo?.osVersion || '').toLowerCase();
|
|
6996
|
+
let cls = 'other';
|
|
6997
|
+
if (ver.includes('windows') || os.includes('windows') || os.includes('mingw'))
|
|
6998
|
+
cls = 'windows';
|
|
6999
|
+
else if (os.includes('darwin'))
|
|
7000
|
+
cls = 'apple';
|
|
7001
|
+
else if (os.includes('linux'))
|
|
7002
|
+
cls = 'linux';
|
|
7003
|
+
else if (os.includes('android'))
|
|
7004
|
+
cls = 'android';
|
|
7005
|
+
return `<span class="os ${cls}"></span>`;
|
|
7006
|
+
};
|
|
7007
|
+
const sdkRenderer = (row) => {
|
|
7008
|
+
// detail moves to the hover popover (SdkInfoTooltipComponent); the cell
|
|
7009
|
+
// just shows the version, so drop the legacy balloon attributes
|
|
7010
|
+
const version = row?.data?.sdkInfo?.version || '-';
|
|
7011
|
+
return `<span class="oneline">${version}</span>`;
|
|
7012
|
+
};
|
|
7013
|
+
const isAdminHeaderComponentParams = {
|
|
7014
|
+
filterType: 'SELECT',
|
|
7015
|
+
enableSorting: true,
|
|
7016
|
+
filterOptions: [
|
|
7017
|
+
{ label: 'All', value: '' },
|
|
7018
|
+
{ label: 'Admins', value: true },
|
|
7019
|
+
{ label: 'Non-Admins', value: false },
|
|
7020
|
+
]
|
|
7021
|
+
};
|
|
7022
|
+
// Online when any of the connection signals is live (the status icon is drawn
|
|
7023
|
+
// via CSS ::before, since [innerHTML] strips inline <svg>).
|
|
7024
|
+
const isOnline = (v) => v === true || /online|connected|true/i.test(String(v ?? ''));
|
|
7025
|
+
const statusRenderer = (row) => {
|
|
7026
|
+
const d = row?.data || {};
|
|
7027
|
+
const online = isOnline(d.edgeRouterConnectionStatus) || isOnline(d.hasEdgeRouterConnection) || isOnline(d.hasApiSession);
|
|
7028
|
+
return `<span class="lt-status lt-status-${online ? 'online' : 'offline'}">${online ? 'Online' : 'Offline'}</span>`;
|
|
7029
|
+
};
|
|
7030
|
+
// Color-coded Type pill with an icon: Default = blue, Router = green. Icon drawn
|
|
7031
|
+
// via CSS (::before) since [innerHTML] strips inline <svg>.
|
|
7032
|
+
const typeBadgeRenderer = (row) => {
|
|
7033
|
+
const t = row?.data?.type?.name || row?.data?.type || '';
|
|
7034
|
+
const key = String(t).toLowerCase();
|
|
7035
|
+
const cls = key === 'router' ? 'lt-type-router'
|
|
7036
|
+
: key === 'default' ? 'lt-type-default'
|
|
7037
|
+
: 'lt-type-other';
|
|
7038
|
+
return `<span class="lt-type-badge ${cls}">${t}</span>`;
|
|
7039
|
+
};
|
|
7040
|
+
// Light skinned role chips (the legacy rolesRenderer paints
|
|
7041
|
+
// heavy dark overlap chips; this stays scoped to the beta page).
|
|
7042
|
+
const rolesChipRenderer = (row) => {
|
|
7043
|
+
const colId = row?.column?.colId;
|
|
7044
|
+
const attrs = row?.data?.[colId + 'Display'] || row?.data?.[colId] || [];
|
|
7045
|
+
if (!attrs?.length) {
|
|
7046
|
+
return '';
|
|
7047
|
+
}
|
|
7048
|
+
return attrs.map((attr) => {
|
|
7049
|
+
const raw = String(attr?.name ?? attr ?? '').trim();
|
|
7050
|
+
if (!raw) {
|
|
7051
|
+
return '';
|
|
7052
|
+
}
|
|
7053
|
+
// named attributes (@) use the secondary color; role attributes (#)
|
|
7054
|
+
// the primary color, matching the tag selector's distinction
|
|
7055
|
+
const isNamed = raw.charAt(0) === '@';
|
|
7056
|
+
const label = isNamed ? raw : (raw.charAt(0) === '#' ? raw : '#' + raw);
|
|
7057
|
+
const cls = isNamed ? 'lt-chip lt-chip-at' : 'lt-chip';
|
|
7058
|
+
return `<span class="${cls}">${label}</span>`;
|
|
7059
|
+
}).join('');
|
|
7060
|
+
};
|
|
7061
|
+
let tableColumns = [
|
|
7062
|
+
{
|
|
7063
|
+
key: 'name',
|
|
7064
|
+
field: 'name',
|
|
7065
|
+
label: 'Name',
|
|
7066
|
+
filterType: 'TEXTINPUT',
|
|
7067
|
+
cell: NameCellComponent,
|
|
7068
|
+
cellInputs: (ctx) => ({
|
|
7069
|
+
row: ctx.row,
|
|
7070
|
+
cellValue: ctx.value,
|
|
7071
|
+
// status now lives in its own Status column (skinned), so the
|
|
7072
|
+
// name cell no longer carries the connection dots
|
|
7073
|
+
cellConfig: { pathRoot: this.basePath, showIdentityIcons: false },
|
|
7074
|
+
}),
|
|
7075
|
+
onCellClick: (ctx) => {
|
|
7076
|
+
if (this.hasSelectedText()) {
|
|
7077
|
+
return;
|
|
7078
|
+
}
|
|
7079
|
+
this.openEditForm(ctx.row?.id);
|
|
7080
|
+
},
|
|
7081
|
+
resizable: true,
|
|
7082
|
+
cellClass: 'nf-cell-vert-align',
|
|
7083
|
+
sortable: true,
|
|
7084
|
+
onSort: this.sort.bind(this),
|
|
7085
|
+
sortDir: 'asc',
|
|
7086
|
+
width: 300,
|
|
7087
|
+
},
|
|
7088
|
+
{
|
|
7089
|
+
key: 'status',
|
|
7090
|
+
field: 'hasEdgeRouterConnection',
|
|
7091
|
+
label: 'Status',
|
|
7092
|
+
cellHtml: statusRenderer,
|
|
7093
|
+
resizable: true,
|
|
7094
|
+
cellClass: 'nf-cell-vert-align',
|
|
7095
|
+
width: 120,
|
|
7096
|
+
},
|
|
7097
|
+
{
|
|
7098
|
+
key: 'roleAttributes',
|
|
7099
|
+
field: 'roleAttributes',
|
|
7100
|
+
label: 'Roles',
|
|
7101
|
+
filterType: 'ATTRIBUTE',
|
|
7102
|
+
headerParams: identityRolesHeaderComponentParams,
|
|
7103
|
+
cellHtml: rolesChipRenderer,
|
|
7104
|
+
onCellClick: (ctx) => {
|
|
7105
|
+
if (this.hasSelectedText()) {
|
|
7106
|
+
return;
|
|
7107
|
+
}
|
|
7108
|
+
this.openEditForm(ctx.row?.id);
|
|
7109
|
+
},
|
|
7110
|
+
resizable: true,
|
|
7111
|
+
cellClass: 'nf-cell-vert-align',
|
|
7112
|
+
sortable: false,
|
|
7113
|
+
},
|
|
7114
|
+
{
|
|
7115
|
+
key: 'os',
|
|
7116
|
+
field: 'os',
|
|
7117
|
+
label: 'O/S',
|
|
7118
|
+
width: 100,
|
|
7119
|
+
cellHtml: osRenderer,
|
|
7120
|
+
tooltip: OsInfoTooltipComponent,
|
|
7121
|
+
tooltipInputs: (ctx) => ({ row: ctx.row }),
|
|
7122
|
+
// envInfo.os is a JSON blob field the controller cannot filter on
|
|
7123
|
+
// ("unknown symbol") - no O/S filter control.
|
|
7124
|
+
resizable: true,
|
|
7125
|
+
cellClass: 'nf-cell-vert-align lt-cell-center',
|
|
7126
|
+
},
|
|
7127
|
+
{
|
|
7128
|
+
key: 'sdk',
|
|
7129
|
+
field: 'sdk',
|
|
7130
|
+
label: 'SDK',
|
|
7131
|
+
cellHtml: sdkRenderer,
|
|
7132
|
+
tooltip: SdkInfoTooltipComponent,
|
|
7133
|
+
tooltipInputs: (ctx) => ({ row: ctx.row }),
|
|
7134
|
+
resizable: true,
|
|
7135
|
+
cellClass: 'nf-cell-vert-align',
|
|
7136
|
+
width: 125,
|
|
7137
|
+
},
|
|
7138
|
+
{
|
|
7139
|
+
key: 'type',
|
|
7140
|
+
field: 'type.name',
|
|
7141
|
+
label: 'Type',
|
|
7142
|
+
filterType: 'SELECT',
|
|
7143
|
+
filterField: 'type',
|
|
7144
|
+
filterPlaceholder: 'Any type',
|
|
7145
|
+
// Only Default + Router exist as identity types in Ziti 2.x (confirmed
|
|
7146
|
+
// via /edge/management/v1/identity-types); Service/Device/User are legacy
|
|
7147
|
+
// and always return 0.
|
|
7148
|
+
filterOptions: [
|
|
7149
|
+
{ label: 'Show All', value: '' },
|
|
7150
|
+
{ label: 'Default', value: 'Default', chipClass: 'lt-type-badge lt-type-default' },
|
|
7151
|
+
{ label: 'Router', value: 'Router', chipClass: 'lt-type-badge lt-type-router' },
|
|
7152
|
+
],
|
|
7153
|
+
cellHtml: typeBadgeRenderer,
|
|
7154
|
+
resizable: true,
|
|
7155
|
+
sortable: true,
|
|
7156
|
+
cellClass: 'nf-cell-vert-align',
|
|
7157
|
+
onSort: this.sort.bind(this),
|
|
7158
|
+
},
|
|
7159
|
+
{
|
|
7160
|
+
key: 'isAdmin',
|
|
7161
|
+
field: 'isAdmin',
|
|
7162
|
+
label: 'Is Admin',
|
|
7163
|
+
filterType: 'SELECT',
|
|
7164
|
+
filterOptions: isAdminHeaderComponentParams.filterOptions,
|
|
7165
|
+
cell: BooleanCellComponent,
|
|
7166
|
+
cellInputs: (ctx) => ({ value: ctx.value, trueLabel: 'Admin' }),
|
|
7167
|
+
resizable: true,
|
|
7168
|
+
sortable: true,
|
|
7169
|
+
onSort: this.sort.bind(this),
|
|
7170
|
+
cellClass: 'nf-cell-vert-align',
|
|
7171
|
+
width: 100,
|
|
7172
|
+
},
|
|
7173
|
+
{
|
|
7174
|
+
key: 'createdAt',
|
|
7175
|
+
field: 'createdAt',
|
|
7176
|
+
label: 'Created At',
|
|
7177
|
+
valueFormatter: this.createdAtFormatter,
|
|
7178
|
+
filterType: 'DATETIME',
|
|
7179
|
+
filterPlaceholder: 'Any time',
|
|
7180
|
+
resizable: true,
|
|
7181
|
+
sortable: true,
|
|
7182
|
+
onSort: this.sort.bind(this),
|
|
7183
|
+
cellClass: 'nf-cell-vert-align',
|
|
7184
|
+
},
|
|
7185
|
+
{
|
|
7186
|
+
key: 'token',
|
|
7187
|
+
field: 'token',
|
|
7188
|
+
label: 'Token',
|
|
7189
|
+
cell: TokenCellComponent,
|
|
7190
|
+
cellInputs: (ctx) => ({ row: ctx.row, emitAction: ctx.emitAction }),
|
|
7191
|
+
resizable: true,
|
|
7192
|
+
cellClass: 'nf-cell-vert-align',
|
|
7193
|
+
},
|
|
7194
|
+
{
|
|
7195
|
+
key: 'isMfaEnabled',
|
|
7196
|
+
field: 'isMfaEnabled',
|
|
7197
|
+
label: 'MFA',
|
|
7198
|
+
// isMfaEnabled is a computed field the controller cannot filter on
|
|
7199
|
+
// ("unknown symbol") - no MFA filter control.
|
|
7200
|
+
cell: BooleanCellComponent,
|
|
7201
|
+
cellInputs: (ctx) => ({ value: ctx.value, trueLabel: 'Enabled' }),
|
|
7202
|
+
resizable: true,
|
|
7203
|
+
cellClass: 'nf-cell-vert-align',
|
|
7204
|
+
width: 100,
|
|
7205
|
+
},
|
|
7206
|
+
{
|
|
7207
|
+
key: 'id',
|
|
7208
|
+
field: 'id',
|
|
7209
|
+
label: 'ID',
|
|
7210
|
+
filterType: 'TEXTINPUT',
|
|
7211
|
+
cellClass: 'nf-cell-vert-align',
|
|
7212
|
+
resizable: true,
|
|
7213
|
+
sortable: true,
|
|
7214
|
+
onSort: this.sort.bind(this),
|
|
7215
|
+
onCellClick: (ctx) => {
|
|
7216
|
+
if (this.hasSelectedText()) {
|
|
7217
|
+
return;
|
|
7218
|
+
}
|
|
7219
|
+
this.openEditForm(ctx.row?.id);
|
|
7220
|
+
},
|
|
7221
|
+
hidden: true,
|
|
7222
|
+
},
|
|
7223
|
+
];
|
|
7224
|
+
if (this.extService.processTableColumns) {
|
|
7225
|
+
tableColumns = this.extService.processTableColumns(tableColumns);
|
|
7226
|
+
}
|
|
7227
|
+
return tableColumns;
|
|
7228
|
+
}
|
|
6614
7229
|
getIdentityRoleAttributes() {
|
|
6615
7230
|
return this.zitiService.get('identity-role-attributes', {}, []).then((result) => {
|
|
6616
7231
|
this.identityRoleAttributes = result.data;
|
|
6617
7232
|
return result;
|
|
6618
7233
|
});
|
|
6619
7234
|
}
|
|
6620
|
-
getData(filters, sort, page) {
|
|
7235
|
+
getData(filters, sort, page, pageSize) {
|
|
6621
7236
|
// we can customize filters or sorting here before moving on...
|
|
6622
7237
|
this.paging.page = page || this.paging.page;
|
|
7238
|
+
if (pageSize) {
|
|
7239
|
+
this.paging.total = pageSize;
|
|
7240
|
+
}
|
|
6623
7241
|
return super.getTableData('identities', this.paging, filters, sort)
|
|
6624
7242
|
.then((results) => {
|
|
6625
7243
|
return this.processData(results);
|
|
@@ -9289,6 +9907,91 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
9289
9907
|
args: [SHAREDZ_EXTENSION]
|
|
9290
9908
|
}] }] });
|
|
9291
9909
|
|
|
9910
|
+
/*
|
|
9911
|
+
Copyright NetFoundry Inc.
|
|
9912
|
+
|
|
9913
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
9914
|
+
you may not use this file except in compliance with the License.
|
|
9915
|
+
You may obtain a copy of the License at
|
|
9916
|
+
|
|
9917
|
+
https://www.apache.org/licenses/LICENSE-2.0
|
|
9918
|
+
|
|
9919
|
+
Unless required by applicable law or agreed to in writing, software
|
|
9920
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
9921
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
9922
|
+
See the License for the specific language governing permissions and
|
|
9923
|
+
limitations under the License.
|
|
9924
|
+
*/
|
|
9925
|
+
/**
|
|
9926
|
+
* Header for the new list-table pages: an icon tile + title + one-line subtitle
|
|
9927
|
+
* (with an optional info affordance) on the left, and a primary "Create ..." action
|
|
9928
|
+
* on the right, over an optional tab strip. Emits the same `add` / `delete` actions
|
|
9929
|
+
* as {@link ListPageHeaderComponent} so pages can swap headers without changing their
|
|
9930
|
+
* handlers. Project the page's icon into the tile with a `header-icon` attribute.
|
|
9931
|
+
*/
|
|
9932
|
+
class ListTableHeaderComponent {
|
|
9933
|
+
constructor() {
|
|
9934
|
+
this.title = '';
|
|
9935
|
+
this.subtitle = '';
|
|
9936
|
+
/** Label for the primary create button, e.g. "Create Identity". */
|
|
9937
|
+
this.createLabel = 'Create';
|
|
9938
|
+
/** Optional short helper text shown behind the (i) affordance next to the title. */
|
|
9939
|
+
this.infoText = '';
|
|
9940
|
+
this.tabs = [];
|
|
9941
|
+
this.showAdd = true;
|
|
9942
|
+
/** Hide the create control even when showAdd is true. */
|
|
9943
|
+
this.blockCreate = false;
|
|
9944
|
+
/** Hide the bulk-delete control shown when rows are selected. */
|
|
9945
|
+
this.blockDelete = false;
|
|
9946
|
+
this.actionClicked = new EventEmitter();
|
|
9947
|
+
}
|
|
9948
|
+
clickAction(value) {
|
|
9949
|
+
this.actionClicked.emit(value);
|
|
9950
|
+
}
|
|
9951
|
+
tabSelected(tab) {
|
|
9952
|
+
const parsedUrl = new URL(window.location.href);
|
|
9953
|
+
let path = parsedUrl.pathname;
|
|
9954
|
+
if (document.baseURI) {
|
|
9955
|
+
const base = new URL(document.baseURI).pathname;
|
|
9956
|
+
if (path.startsWith(base)) {
|
|
9957
|
+
path = path.slice(base.length);
|
|
9958
|
+
}
|
|
9959
|
+
}
|
|
9960
|
+
let tabUrl = tab.url;
|
|
9961
|
+
if (tab.url.startsWith('/')) {
|
|
9962
|
+
tabUrl = tab.url.slice(1);
|
|
9963
|
+
}
|
|
9964
|
+
if (path.startsWith('/')) {
|
|
9965
|
+
path = path.slice(1);
|
|
9966
|
+
}
|
|
9967
|
+
return tabUrl === path;
|
|
9968
|
+
}
|
|
9969
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ListTableHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
9970
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: ListTableHeaderComponent, isStandalone: false, selector: "lib-list-table-header", inputs: { title: "title", subtitle: "subtitle", createLabel: "createLabel", infoText: "infoText", tabs: "tabs", showAdd: "showAdd", blockCreate: "blockCreate", blockDelete: "blockDelete" }, outputs: { actionClicked: "actionClicked" }, ngImport: i0, template: "<div class=\"lth\">\n <div class=\"lth-main\">\n <div class=\"lth-icon\" aria-hidden=\"true\">\n <ng-content select=\"[header-icon]\"></ng-content>\n </div>\n <div class=\"lth-titles\">\n <div class=\"lth-title-row\">\n <h1 class=\"lth-title\">{{ title }}</h1>\n @if (infoText) {\n <span class=\"lth-info\" [title]=\"infoText\" tabindex=\"0\" role=\"img\" [attr.aria-label]=\"infoText\">\n <span class=\"lth-info-badge\" aria-hidden=\"true\">i</span>\n </span>\n }\n </div>\n @if (subtitle) {\n <p class=\"lth-subtitle\">{{ subtitle }}</p>\n }\n </div>\n <div class=\"lth-actions\">\n @if (showAdd && !blockCreate) {\n <button type=\"button\" class=\"lth-create\" (click)=\"clickAction('add')\" data-action=\"add\" id=\"CreateButton\">\n <span class=\"lth-create-plus\" aria-hidden=\"true\">+</span>\n <span>{{ createLabel }}</span>\n </button>\n }\n @if (!showAdd && !blockDelete) {\n <button type=\"button\" class=\"lth-delete\" (click)=\"clickAction('delete')\" data-action=\"delete\" id=\"DeleteButton\">\n <span class=\"lth-delete-icon icon-minus\" aria-hidden=\"true\"></span>\n <span>Delete</span>\n </button>\n }\n </div>\n </div>\n @if (tabs?.length) {\n <div class=\"lth-tabs\">\n @for (tab of tabs; track tab) {\n <a\n class=\"lth-tab\"\n [ngClass]=\"{ selected: tabSelected(tab) }\"\n [routerLink]=\"tab.url\"\n [hidden]=\"tab.hidden\"\n >{{ tab.label }}</a>\n }\n </div>\n }\n</div>\n", styles: [":host{display:block;width:100%}.lth{display:flex;flex-direction:column;gap:.75rem;padding-bottom:.5rem}.lth-main{display:flex;align-items:center;gap:.875rem}.lth-icon{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;width:2.75rem;height:2.75rem;border-radius:.75rem;color:var(--primary);background:color-mix(in srgb,var(--primary) 12%,var(--background));border:1px solid color-mix(in srgb,var(--primary) 22%,var(--background))}.lth-icon svg,.lth-icon img{display:block;width:1.5rem;height:1.5rem}:host ::ng-deep .lth-icon [class^=icon-],:host ::ng-deep .lth-icon [class*=\" icon-\"]{font-size:1.5rem;line-height:1}:host ::ng-deep .lth-icon [class^=icon-]:before,:host ::ng-deep .lth-icon [class*=\" icon-\"]:before{color:var(--primary)}.lth-titles{flex:1 1 auto;min-width:0}.lth-title-row{display:flex;align-items:center;gap:.5rem}.lth-title{margin:0;font-family:var(--lt-font, \"Open Sans\", sans-serif);font-size:1.5rem;font-weight:700;line-height:1.15;color:var(--tableText, var(--text))}.lth-info{display:inline-flex;align-items:center;justify-content:center;padding:.25rem;background:var(--background);border:1px solid var(--stroke);border-radius:.375rem;box-shadow:0 1px 2px #0000000f;cursor:help}.lth-info:hover{border-color:var(--groupText)}.lth-info-badge{display:inline-flex;align-items:center;justify-content:center;width:.9375rem;height:.9375rem;font-size:.625rem;font-weight:700;font-style:italic;font-family:Georgia,Times New Roman,serif;line-height:1;color:#fff;background:var(--dark);border-radius:50%}.lth-subtitle{margin:.125rem 0 0;font-family:var(--lt-font, \"Open Sans\", sans-serif);font-size:.8125rem;color:var(--groupText)}.lth-actions{flex:0 0 auto;display:flex;align-items:center;gap:.5rem}.lth-create{display:inline-flex;align-items:center;gap:.4375rem;height:2.5rem;padding:0 1rem;font-family:var(--lt-font, \"Open Sans\", sans-serif);font-size:.8125rem;font-weight:600;color:#fff;background:var(--primary);border:none;border-radius:var(--inputBorderRadius, .5rem);cursor:pointer;transition:filter .15s}.lth-create:hover{filter:brightness(1.06)}.lth-create-plus{font-size:1.125rem;line-height:1}.lth-delete{display:inline-flex;align-items:center;gap:.4375rem;height:2.5rem;padding:0 1rem;font-family:var(--lt-font, \"Open Sans\", sans-serif);font-size:.8125rem;font-weight:600;color:var(--redText, #9c0027);background:transparent;border:1px solid var(--redText, #9c0027);border-radius:var(--inputBorderRadius, .5rem);cursor:pointer;transition:background .15s}.lth-delete:hover{background:color-mix(in srgb,var(--redText, #9c0027) 8%,transparent)}.lth-tabs{display:flex;align-items:center;gap:1.25rem;border-bottom:1px solid var(--stroke)}.lth-tab{padding:.4375rem .125rem;font-family:var(--lt-font, \"Open Sans\", sans-serif);font-size:.8125rem;font-weight:600;color:var(--groupText);text-decoration:none;border-bottom:2px solid transparent;margin-bottom:-1px;cursor:pointer;transition:color .15s,border-color .15s}.lth-tab:hover{color:var(--text)}.lth-tab.selected{color:var(--primary);border-bottom-color:var(--primary)}\n"], dependencies: [{ kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i4.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }] }); }
|
|
9971
|
+
}
|
|
9972
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ListTableHeaderComponent, decorators: [{
|
|
9973
|
+
type: Component,
|
|
9974
|
+
args: [{ selector: 'lib-list-table-header', standalone: false, template: "<div class=\"lth\">\n <div class=\"lth-main\">\n <div class=\"lth-icon\" aria-hidden=\"true\">\n <ng-content select=\"[header-icon]\"></ng-content>\n </div>\n <div class=\"lth-titles\">\n <div class=\"lth-title-row\">\n <h1 class=\"lth-title\">{{ title }}</h1>\n @if (infoText) {\n <span class=\"lth-info\" [title]=\"infoText\" tabindex=\"0\" role=\"img\" [attr.aria-label]=\"infoText\">\n <span class=\"lth-info-badge\" aria-hidden=\"true\">i</span>\n </span>\n }\n </div>\n @if (subtitle) {\n <p class=\"lth-subtitle\">{{ subtitle }}</p>\n }\n </div>\n <div class=\"lth-actions\">\n @if (showAdd && !blockCreate) {\n <button type=\"button\" class=\"lth-create\" (click)=\"clickAction('add')\" data-action=\"add\" id=\"CreateButton\">\n <span class=\"lth-create-plus\" aria-hidden=\"true\">+</span>\n <span>{{ createLabel }}</span>\n </button>\n }\n @if (!showAdd && !blockDelete) {\n <button type=\"button\" class=\"lth-delete\" (click)=\"clickAction('delete')\" data-action=\"delete\" id=\"DeleteButton\">\n <span class=\"lth-delete-icon icon-minus\" aria-hidden=\"true\"></span>\n <span>Delete</span>\n </button>\n }\n </div>\n </div>\n @if (tabs?.length) {\n <div class=\"lth-tabs\">\n @for (tab of tabs; track tab) {\n <a\n class=\"lth-tab\"\n [ngClass]=\"{ selected: tabSelected(tab) }\"\n [routerLink]=\"tab.url\"\n [hidden]=\"tab.hidden\"\n >{{ tab.label }}</a>\n }\n </div>\n }\n</div>\n", styles: [":host{display:block;width:100%}.lth{display:flex;flex-direction:column;gap:.75rem;padding-bottom:.5rem}.lth-main{display:flex;align-items:center;gap:.875rem}.lth-icon{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;width:2.75rem;height:2.75rem;border-radius:.75rem;color:var(--primary);background:color-mix(in srgb,var(--primary) 12%,var(--background));border:1px solid color-mix(in srgb,var(--primary) 22%,var(--background))}.lth-icon svg,.lth-icon img{display:block;width:1.5rem;height:1.5rem}:host ::ng-deep .lth-icon [class^=icon-],:host ::ng-deep .lth-icon [class*=\" icon-\"]{font-size:1.5rem;line-height:1}:host ::ng-deep .lth-icon [class^=icon-]:before,:host ::ng-deep .lth-icon [class*=\" icon-\"]:before{color:var(--primary)}.lth-titles{flex:1 1 auto;min-width:0}.lth-title-row{display:flex;align-items:center;gap:.5rem}.lth-title{margin:0;font-family:var(--lt-font, \"Open Sans\", sans-serif);font-size:1.5rem;font-weight:700;line-height:1.15;color:var(--tableText, var(--text))}.lth-info{display:inline-flex;align-items:center;justify-content:center;padding:.25rem;background:var(--background);border:1px solid var(--stroke);border-radius:.375rem;box-shadow:0 1px 2px #0000000f;cursor:help}.lth-info:hover{border-color:var(--groupText)}.lth-info-badge{display:inline-flex;align-items:center;justify-content:center;width:.9375rem;height:.9375rem;font-size:.625rem;font-weight:700;font-style:italic;font-family:Georgia,Times New Roman,serif;line-height:1;color:#fff;background:var(--dark);border-radius:50%}.lth-subtitle{margin:.125rem 0 0;font-family:var(--lt-font, \"Open Sans\", sans-serif);font-size:.8125rem;color:var(--groupText)}.lth-actions{flex:0 0 auto;display:flex;align-items:center;gap:.5rem}.lth-create{display:inline-flex;align-items:center;gap:.4375rem;height:2.5rem;padding:0 1rem;font-family:var(--lt-font, \"Open Sans\", sans-serif);font-size:.8125rem;font-weight:600;color:#fff;background:var(--primary);border:none;border-radius:var(--inputBorderRadius, .5rem);cursor:pointer;transition:filter .15s}.lth-create:hover{filter:brightness(1.06)}.lth-create-plus{font-size:1.125rem;line-height:1}.lth-delete{display:inline-flex;align-items:center;gap:.4375rem;height:2.5rem;padding:0 1rem;font-family:var(--lt-font, \"Open Sans\", sans-serif);font-size:.8125rem;font-weight:600;color:var(--redText, #9c0027);background:transparent;border:1px solid var(--redText, #9c0027);border-radius:var(--inputBorderRadius, .5rem);cursor:pointer;transition:background .15s}.lth-delete:hover{background:color-mix(in srgb,var(--redText, #9c0027) 8%,transparent)}.lth-tabs{display:flex;align-items:center;gap:1.25rem;border-bottom:1px solid var(--stroke)}.lth-tab{padding:.4375rem .125rem;font-family:var(--lt-font, \"Open Sans\", sans-serif);font-size:.8125rem;font-weight:600;color:var(--groupText);text-decoration:none;border-bottom:2px solid transparent;margin-bottom:-1px;cursor:pointer;transition:color .15s,border-color .15s}.lth-tab:hover{color:var(--text)}.lth-tab.selected{color:var(--primary);border-bottom-color:var(--primary)}\n"] }]
|
|
9975
|
+
}], propDecorators: { title: [{
|
|
9976
|
+
type: Input
|
|
9977
|
+
}], subtitle: [{
|
|
9978
|
+
type: Input
|
|
9979
|
+
}], createLabel: [{
|
|
9980
|
+
type: Input
|
|
9981
|
+
}], infoText: [{
|
|
9982
|
+
type: Input
|
|
9983
|
+
}], tabs: [{
|
|
9984
|
+
type: Input
|
|
9985
|
+
}], showAdd: [{
|
|
9986
|
+
type: Input
|
|
9987
|
+
}], blockCreate: [{
|
|
9988
|
+
type: Input
|
|
9989
|
+
}], blockDelete: [{
|
|
9990
|
+
type: Input
|
|
9991
|
+
}], actionClicked: [{
|
|
9992
|
+
type: Output
|
|
9993
|
+
}] } });
|
|
9994
|
+
|
|
9292
9995
|
/*
|
|
9293
9996
|
Copyright NetFoundry Inc.
|
|
9294
9997
|
|
|
@@ -10769,6 +11472,1430 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
10769
11472
|
args: ['document:keydown.escape', ['$event']]
|
|
10770
11473
|
}] } });
|
|
10771
11474
|
|
|
11475
|
+
/*
|
|
11476
|
+
Copyright NetFoundry Inc.
|
|
11477
|
+
|
|
11478
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
11479
|
+
you may not use this file except in compliance with the License.
|
|
11480
|
+
You may obtain a copy of the License at
|
|
11481
|
+
|
|
11482
|
+
https://www.apache.org/licenses/LICENSE-2.0
|
|
11483
|
+
|
|
11484
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11485
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
11486
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11487
|
+
See the License for the specific language governing permissions and
|
|
11488
|
+
limitations under the License.
|
|
11489
|
+
*/
|
|
11490
|
+
/**
|
|
11491
|
+
* Per-instance state + localStorage persistence for {@link ListTableComponent}.
|
|
11492
|
+
* Reuses the legacy `ziti_<tableId>_table_state` / `_column_widths` keys so saved
|
|
11493
|
+
* layouts carry over; unknown (ag-only) fields are ignored.
|
|
11494
|
+
*/
|
|
11495
|
+
class ListTableService {
|
|
11496
|
+
constructor() {
|
|
11497
|
+
this.tableId = '';
|
|
11498
|
+
/** Coordinates the toolbar dropdowns so only one (filter pill or column chooser)
|
|
11499
|
+
* is open at a time. Openers emit themselves; others close when they see an
|
|
11500
|
+
* owner that isn't them. */
|
|
11501
|
+
this.menuOpenedSubject = new Subject();
|
|
11502
|
+
this.menuOpened$ = this.menuOpenedSubject.asObservable();
|
|
11503
|
+
}
|
|
11504
|
+
notifyMenuOpened(owner) {
|
|
11505
|
+
this.menuOpenedSubject.next(owner);
|
|
11506
|
+
}
|
|
11507
|
+
stateKey() {
|
|
11508
|
+
return `ziti_${this.tableId}_table_state`;
|
|
11509
|
+
}
|
|
11510
|
+
widthsKey() {
|
|
11511
|
+
return `ziti_${this.tableId}_column_widths`;
|
|
11512
|
+
}
|
|
11513
|
+
/**
|
|
11514
|
+
* Returns a shallow-cloned copy of `columns` with any persisted width, visibility,
|
|
11515
|
+
* and order applied. Columns not present in the saved state keep their declared
|
|
11516
|
+
* position (appended after the saved ones).
|
|
11517
|
+
*/
|
|
11518
|
+
applyPersistedState(columns) {
|
|
11519
|
+
let cols = columns.map((c) => ({ ...c }));
|
|
11520
|
+
// widths
|
|
11521
|
+
try {
|
|
11522
|
+
const raw = localStorage.getItem(this.widthsKey());
|
|
11523
|
+
if (raw) {
|
|
11524
|
+
const widths = JSON.parse(raw) || {};
|
|
11525
|
+
cols.forEach((c) => {
|
|
11526
|
+
if (widths[c.key] != null) {
|
|
11527
|
+
c.width = widths[c.key];
|
|
11528
|
+
}
|
|
11529
|
+
});
|
|
11530
|
+
}
|
|
11531
|
+
}
|
|
11532
|
+
catch {
|
|
11533
|
+
// ignore malformed / unavailable storage
|
|
11534
|
+
}
|
|
11535
|
+
// order + visibility
|
|
11536
|
+
try {
|
|
11537
|
+
const raw = localStorage.getItem(this.stateKey());
|
|
11538
|
+
if (raw) {
|
|
11539
|
+
const state = JSON.parse(raw) || [];
|
|
11540
|
+
const ordered = [];
|
|
11541
|
+
state.forEach((s) => {
|
|
11542
|
+
const found = cols.find((c) => c.key === s.colId);
|
|
11543
|
+
if (found && !ordered.includes(found)) {
|
|
11544
|
+
if (s.hide != null) {
|
|
11545
|
+
found.hidden = s.hide;
|
|
11546
|
+
}
|
|
11547
|
+
if (s.width != null) {
|
|
11548
|
+
found.width = s.width;
|
|
11549
|
+
}
|
|
11550
|
+
ordered.push(found);
|
|
11551
|
+
}
|
|
11552
|
+
});
|
|
11553
|
+
cols.forEach((c) => {
|
|
11554
|
+
if (!ordered.includes(c)) {
|
|
11555
|
+
ordered.push(c);
|
|
11556
|
+
}
|
|
11557
|
+
});
|
|
11558
|
+
if (ordered.length) {
|
|
11559
|
+
cols = ordered;
|
|
11560
|
+
}
|
|
11561
|
+
}
|
|
11562
|
+
}
|
|
11563
|
+
catch {
|
|
11564
|
+
// ignore malformed / unavailable storage
|
|
11565
|
+
}
|
|
11566
|
+
return cols;
|
|
11567
|
+
}
|
|
11568
|
+
/** Persists the current order + visibility (and widths embedded in each entry). */
|
|
11569
|
+
saveState(columns) {
|
|
11570
|
+
const state = columns.map((c) => ({ colId: c.key, hide: !!c.hidden, width: c.width }));
|
|
11571
|
+
try {
|
|
11572
|
+
localStorage.setItem(this.stateKey(), JSON.stringify(state));
|
|
11573
|
+
}
|
|
11574
|
+
catch {
|
|
11575
|
+
// ignore
|
|
11576
|
+
}
|
|
11577
|
+
}
|
|
11578
|
+
/** Persists a single column's width, then rewrites the ordered state. */
|
|
11579
|
+
saveWidth(colId, width, columns) {
|
|
11580
|
+
try {
|
|
11581
|
+
const raw = localStorage.getItem(this.widthsKey());
|
|
11582
|
+
const widths = raw ? JSON.parse(raw) : {};
|
|
11583
|
+
widths[colId] = width;
|
|
11584
|
+
localStorage.setItem(this.widthsKey(), JSON.stringify(widths));
|
|
11585
|
+
}
|
|
11586
|
+
catch {
|
|
11587
|
+
// ignore
|
|
11588
|
+
}
|
|
11589
|
+
this.saveState(columns);
|
|
11590
|
+
}
|
|
11591
|
+
/** Clears all persisted layout for this table ("Restore Default Table"). */
|
|
11592
|
+
reset() {
|
|
11593
|
+
try {
|
|
11594
|
+
localStorage.removeItem(this.stateKey());
|
|
11595
|
+
localStorage.removeItem(this.widthsKey());
|
|
11596
|
+
}
|
|
11597
|
+
catch {
|
|
11598
|
+
// ignore
|
|
11599
|
+
}
|
|
11600
|
+
}
|
|
11601
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ListTableService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
11602
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ListTableService }); }
|
|
11603
|
+
}
|
|
11604
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ListTableService, decorators: [{
|
|
11605
|
+
type: Injectable
|
|
11606
|
+
}] });
|
|
11607
|
+
|
|
11608
|
+
/**
|
|
11609
|
+
* Generic, theme-driven action menu popover. Presentational only: the host owns
|
|
11610
|
+
* open state and position and handles `(itemClick)` / `(closed)`.
|
|
11611
|
+
*/
|
|
11612
|
+
class ListMenuComponent {
|
|
11613
|
+
constructor() {
|
|
11614
|
+
/** Whether the menu is rendered. */
|
|
11615
|
+
this.open = false;
|
|
11616
|
+
/** Rows to render. */
|
|
11617
|
+
this.items = [];
|
|
11618
|
+
/** Fixed-position left offset (px). */
|
|
11619
|
+
this.left = 0;
|
|
11620
|
+
/** Fixed-position top offset (px). */
|
|
11621
|
+
this.top = 0;
|
|
11622
|
+
/** Emitted when a clickable row is selected. */
|
|
11623
|
+
this.itemClick = new EventEmitter();
|
|
11624
|
+
/** Emitted when the user clicks outside the open menu. */
|
|
11625
|
+
this.closed = new EventEmitter();
|
|
11626
|
+
this.trackItem = (_, item) => item.action ?? item.label;
|
|
11627
|
+
}
|
|
11628
|
+
onItem(item, event) {
|
|
11629
|
+
if (item.disabled) {
|
|
11630
|
+
return;
|
|
11631
|
+
}
|
|
11632
|
+
this.itemClick.emit({ item, event });
|
|
11633
|
+
}
|
|
11634
|
+
onClickOutside(event) {
|
|
11635
|
+
this.closed.emit(event);
|
|
11636
|
+
}
|
|
11637
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ListMenuComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
11638
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: ListMenuComponent, isStandalone: false, selector: "lib-list-menu", inputs: { open: "open", items: "items", left: "left", top: "top", menuId: "menuId" }, outputs: { itemClick: "itemClick", closed: "closed" }, ngImport: i0, template: "@if (open) {\n <div\n class=\"lt-menu\"\n [attr.id]=\"menuId || null\"\n [ngStyle]=\"{ left: left + 'px', top: top + 'px' }\"\n (clickOutside)=\"onClickOutside($event)\"\n >\n @for (item of items; track trackItem($index, item)) {\n <button\n type=\"button\"\n class=\"lt-menu-item\"\n [class.has-icon]=\"!!item.icon || !!item.iconSvg\"\n [class.is-hidden]=\"item.hidden\"\n [disabled]=\"!!item.disabled\"\n [attr.id]=\"item.id || null\"\n (click)=\"onItem(item, $event)\"\n >\n @if (item.iconSvg) {\n <span class=\"lt-menu-icon\" aria-hidden=\"true\" [innerHTML]=\"item.iconSvg\"></span>\n } @else if (item.icon) {\n <span class=\"lt-menu-icon {{ item.icon }}\" aria-hidden=\"true\"></span>\n }\n <span class=\"lt-menu-label\">{{ item.label }}</span>\n </button>\n }\n </div>\n}\n", styles: [":host{display:contents}.lt-menu{position:fixed;z-index:1000;min-width:11rem;padding:.25rem;background:var(--lt-control-bg);border:1px solid var(--lt-border);border-radius:var(--lt-radius);box-shadow:var(--shadowLarge);font-family:var(--lt-font);animation:lt-menu-in .1s ease-out}@keyframes lt-menu-in{0%{opacity:0;transform:translateY(-.25rem)}to{opacity:1;transform:translateY(0)}}.lt-menu-item{display:flex;align-items:center;gap:.5rem;width:100%;padding:.4375rem .5rem;font-family:var(--lt-font);font-size:.8125rem;color:var(--lt-cell-text);text-align:left;background:transparent;border:none;border-radius:var(--lt-radius);cursor:pointer}.lt-menu-item:hover{background:var(--lt-row-hover);color:var(--lt-link)}.lt-menu-item:hover .lt-menu-icon{color:var(--lt-link)}.lt-menu-item.is-hidden{display:none}.lt-menu-item:disabled{opacity:.45;cursor:default}.lt-menu-item:disabled:hover{background:transparent;color:var(--lt-cell-text)}.lt-menu-icon{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;width:1rem;font-size:.9375rem;color:var(--lt-icon)}.lt-menu-icon ::ng-deep svg{display:block;width:.9375rem;height:.9375rem}\n"], dependencies: [{ kind: "directive", type: i1$1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i3$1.ClickOutsideDirective, selector: "[clickOutside]", inputs: ["clickOutsideEnabled", "attachOutsideOnClick", "delayClickOutsideInit", "emitOnBlur", "exclude", "excludeBeforeClick", "clickOutsideEvents"], outputs: ["clickOutside"] }] }); }
|
|
11639
|
+
}
|
|
11640
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ListMenuComponent, decorators: [{
|
|
11641
|
+
type: Component,
|
|
11642
|
+
args: [{ selector: 'lib-list-menu', standalone: false, template: "@if (open) {\n <div\n class=\"lt-menu\"\n [attr.id]=\"menuId || null\"\n [ngStyle]=\"{ left: left + 'px', top: top + 'px' }\"\n (clickOutside)=\"onClickOutside($event)\"\n >\n @for (item of items; track trackItem($index, item)) {\n <button\n type=\"button\"\n class=\"lt-menu-item\"\n [class.has-icon]=\"!!item.icon || !!item.iconSvg\"\n [class.is-hidden]=\"item.hidden\"\n [disabled]=\"!!item.disabled\"\n [attr.id]=\"item.id || null\"\n (click)=\"onItem(item, $event)\"\n >\n @if (item.iconSvg) {\n <span class=\"lt-menu-icon\" aria-hidden=\"true\" [innerHTML]=\"item.iconSvg\"></span>\n } @else if (item.icon) {\n <span class=\"lt-menu-icon {{ item.icon }}\" aria-hidden=\"true\"></span>\n }\n <span class=\"lt-menu-label\">{{ item.label }}</span>\n </button>\n }\n </div>\n}\n", styles: [":host{display:contents}.lt-menu{position:fixed;z-index:1000;min-width:11rem;padding:.25rem;background:var(--lt-control-bg);border:1px solid var(--lt-border);border-radius:var(--lt-radius);box-shadow:var(--shadowLarge);font-family:var(--lt-font);animation:lt-menu-in .1s ease-out}@keyframes lt-menu-in{0%{opacity:0;transform:translateY(-.25rem)}to{opacity:1;transform:translateY(0)}}.lt-menu-item{display:flex;align-items:center;gap:.5rem;width:100%;padding:.4375rem .5rem;font-family:var(--lt-font);font-size:.8125rem;color:var(--lt-cell-text);text-align:left;background:transparent;border:none;border-radius:var(--lt-radius);cursor:pointer}.lt-menu-item:hover{background:var(--lt-row-hover);color:var(--lt-link)}.lt-menu-item:hover .lt-menu-icon{color:var(--lt-link)}.lt-menu-item.is-hidden{display:none}.lt-menu-item:disabled{opacity:.45;cursor:default}.lt-menu-item:disabled:hover{background:transparent;color:var(--lt-cell-text)}.lt-menu-icon{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;width:1rem;font-size:.9375rem;color:var(--lt-icon)}.lt-menu-icon ::ng-deep svg{display:block;width:.9375rem;height:.9375rem}\n"] }]
|
|
11643
|
+
}], propDecorators: { open: [{
|
|
11644
|
+
type: Input
|
|
11645
|
+
}], items: [{
|
|
11646
|
+
type: Input
|
|
11647
|
+
}], left: [{
|
|
11648
|
+
type: Input
|
|
11649
|
+
}], top: [{
|
|
11650
|
+
type: Input
|
|
11651
|
+
}], menuId: [{
|
|
11652
|
+
type: Input
|
|
11653
|
+
}], itemClick: [{
|
|
11654
|
+
type: Output
|
|
11655
|
+
}], closed: [{
|
|
11656
|
+
type: Output
|
|
11657
|
+
}] } });
|
|
11658
|
+
|
|
11659
|
+
/**
|
|
11660
|
+
* Row selection toggle for {@link ListTableComponent}. Router / exclusive /
|
|
11661
|
+
* reserved / system rows cannot be toggled.
|
|
11662
|
+
*/
|
|
11663
|
+
class SelectCellComponent {
|
|
11664
|
+
constructor() {
|
|
11665
|
+
this.item = {
|
|
11666
|
+
id: '',
|
|
11667
|
+
name: '',
|
|
11668
|
+
selected: false,
|
|
11669
|
+
hideSelect: false,
|
|
11670
|
+
};
|
|
11671
|
+
this.disableToggle = false;
|
|
11672
|
+
}
|
|
11673
|
+
set row(v) {
|
|
11674
|
+
this.item = v || this.item;
|
|
11675
|
+
this.disableToggle =
|
|
11676
|
+
_.get(this.item, 'typeId') === 'Router' ||
|
|
11677
|
+
_.get(this.item, 'exclusiveTo') ||
|
|
11678
|
+
_.get(this.item, 'reserved') === true ||
|
|
11679
|
+
_.get(this.item, 'isSystem') === true;
|
|
11680
|
+
}
|
|
11681
|
+
toggle(item) {
|
|
11682
|
+
if (this.disableToggle || !this.toggleItem) {
|
|
11683
|
+
return;
|
|
11684
|
+
}
|
|
11685
|
+
this.toggleItem(item);
|
|
11686
|
+
}
|
|
11687
|
+
get tooltip() {
|
|
11688
|
+
let tooltip = '';
|
|
11689
|
+
if (this.item?.isSystem) {
|
|
11690
|
+
tooltip = 'Deleting "system" entities is not allowed';
|
|
11691
|
+
}
|
|
11692
|
+
else if (this.item?.typeId === 'Router') {
|
|
11693
|
+
tooltip = 'Unable to delete "Router" Identities. Please delete the associated Router instead.';
|
|
11694
|
+
}
|
|
11695
|
+
return tooltip;
|
|
11696
|
+
}
|
|
11697
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: SelectCellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
11698
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: SelectCellComponent, isStandalone: false, selector: "lib-select-cell", inputs: { row: "row", toggleItem: "toggleItem" }, ngImport: i0, template: "@if (!item.hideSelect) {\n <div\n #toggleButton\n (click)=\"toggle(item)\"\n [ngClass]=\"{ selected: item.selected, disabled: disableToggle }\"\n attr.name=\"RowSelect0_{{ item.name }}\"\n class=\"row-toggle tSelect\"\n id=\"RowSelect0_{{ item.id }}\"\n attr.data-id=\"{{item.id}}\"\n [matTooltip]=\"tooltip\"\n ></div>\n}\n", styles: [".row-toggle.tSelect{box-sizing:border-box;width:1rem;height:1rem;border:1px solid var(--lt-border);border-radius:.25rem;cursor:pointer;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);background-color:var(--lt-control-bg);background-repeat:no-repeat;background-position:center center;transition:border-color .12s,background-color .12s}.row-toggle.tSelect:hover{border-color:var(--lt-link)}.row-toggle.tSelect.selected{border-color:var(--lt-link);background-color:var(--lt-link);background-size:.75rem;background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 14'%3E%3Cpath fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' d='M2.5 7.6 5.7 10.8 11.5 3.6'/%3E%3C/svg%3E\")}.row-toggle.tSelect.disabled{opacity:.3;cursor:not-allowed;transform:translate(-50%,-50%)!important}.row-toggle.tSelect.disabled:hover{border-color:var(--lt-border);background-color:var(--lt-control-bg)}\n"], dependencies: [{ kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i3.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }] }); }
|
|
11699
|
+
}
|
|
11700
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: SelectCellComponent, decorators: [{
|
|
11701
|
+
type: Component,
|
|
11702
|
+
args: [{ selector: 'lib-select-cell', standalone: false, template: "@if (!item.hideSelect) {\n <div\n #toggleButton\n (click)=\"toggle(item)\"\n [ngClass]=\"{ selected: item.selected, disabled: disableToggle }\"\n attr.name=\"RowSelect0_{{ item.name }}\"\n class=\"row-toggle tSelect\"\n id=\"RowSelect0_{{ item.id }}\"\n attr.data-id=\"{{item.id}}\"\n [matTooltip]=\"tooltip\"\n ></div>\n}\n", styles: [".row-toggle.tSelect{box-sizing:border-box;width:1rem;height:1rem;border:1px solid var(--lt-border);border-radius:.25rem;cursor:pointer;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);background-color:var(--lt-control-bg);background-repeat:no-repeat;background-position:center center;transition:border-color .12s,background-color .12s}.row-toggle.tSelect:hover{border-color:var(--lt-link)}.row-toggle.tSelect.selected{border-color:var(--lt-link);background-color:var(--lt-link);background-size:.75rem;background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 14'%3E%3Cpath fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' d='M2.5 7.6 5.7 10.8 11.5 3.6'/%3E%3C/svg%3E\")}.row-toggle.tSelect.disabled{opacity:.3;cursor:not-allowed;transform:translate(-50%,-50%)!important}.row-toggle.tSelect.disabled:hover{border-color:var(--lt-border);background-color:var(--lt-control-bg)}\n"] }]
|
|
11703
|
+
}], propDecorators: { row: [{
|
|
11704
|
+
type: Input
|
|
11705
|
+
}], toggleItem: [{
|
|
11706
|
+
type: Input
|
|
11707
|
+
}] } });
|
|
11708
|
+
|
|
11709
|
+
/*
|
|
11710
|
+
Copyright NetFoundry Inc.
|
|
11711
|
+
|
|
11712
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
11713
|
+
you may not use this file except in compliance with the License.
|
|
11714
|
+
You may obtain a copy of the License at
|
|
11715
|
+
|
|
11716
|
+
https://www.apache.org/licenses/LICENSE-2.0
|
|
11717
|
+
|
|
11718
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11719
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
11720
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11721
|
+
See the License for the specific language governing permissions and
|
|
11722
|
+
limitations under the License.
|
|
11723
|
+
*/
|
|
11724
|
+
/** Header cell for {@link ListTableComponent}: label, 3-state sort (asc/desc), and hide. */
|
|
11725
|
+
class ListHeaderCellComponent {
|
|
11726
|
+
constructor() {
|
|
11727
|
+
this.sortChanged = new EventEmitter();
|
|
11728
|
+
this.hideColumn = new EventEmitter();
|
|
11729
|
+
}
|
|
11730
|
+
get enableSorting() {
|
|
11731
|
+
return !!(this.column?.sortable || this.column?.enableSorting);
|
|
11732
|
+
}
|
|
11733
|
+
sort() {
|
|
11734
|
+
if (!this.enableSorting) {
|
|
11735
|
+
return;
|
|
11736
|
+
}
|
|
11737
|
+
const dir = !this.sortDir || this.sortDir === 'desc' ? 'asc' : 'desc';
|
|
11738
|
+
this.sortDir = dir;
|
|
11739
|
+
this.column.onSort?.(this.column.key, dir);
|
|
11740
|
+
this.sortChanged.emit({ colId: this.column.key, dir });
|
|
11741
|
+
}
|
|
11742
|
+
hide(event) {
|
|
11743
|
+
event.stopPropagation();
|
|
11744
|
+
this.hideColumn.emit(this.column);
|
|
11745
|
+
}
|
|
11746
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ListHeaderCellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
11747
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: ListHeaderCellComponent, isStandalone: false, selector: "lib-list-header-cell", inputs: { column: "column", sortDir: "sortDir" }, outputs: { sortChanged: "sortChanged", hideColumn: "hideColumn" }, ngImport: i0, template: "<div\n (click)=\"sort()\"\n [ngClass]=\"{ 'nf-can-sort': enableSorting }\"\n class=\"lt-header-container\"\n id=\"TableHeaderContainer_{{ column.key }}\"\n>\n @if (column.hideable !== false) {\n <span\n (click)=\"hide($event)\"\n class=\"lt-header-icon lt-col-hide-button\"\n id=\"HeaderHide_{{ column.key }}\"\n title=\"Hide Column\"\n >\n <svg viewBox=\"0 0 16 16\" width=\"14\" height=\"14\" aria-hidden=\"true\">\n <path fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.3\"\n d=\"M1 8s2.7-4.5 7-4.5S15 8 15 8s-2.7 4.5-7 4.5S1 8 1 8Z\"/>\n <circle cx=\"8\" cy=\"8\" r=\"1.9\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.3\"/>\n <line x1=\"2.5\" y1=\"13.5\" x2=\"13.5\" y2=\"2.5\" stroke=\"currentColor\" stroke-width=\"1.3\"/>\n </svg>\n </span>\n }\n <div class=\"lt-header-label\">\n <span\n [title]=\"column.label\"\n class=\"lt-header-text\"\n [class.is-sorted]=\"sortDir === 'asc' || sortDir === 'desc'\"\n id=\"HeaderName_{{ column.key }}\"\n unselectable=\"on\"\n >{{ column.label }}</span>\n @if (enableSorting) {\n <span class=\"lt-sort\" [class.asc]=\"sortDir === 'asc'\" [class.desc]=\"sortDir === 'desc'\">\n <svg viewBox=\"0 0 8 14\" width=\"8\" height=\"13\" aria-hidden=\"true\">\n <path class=\"lt-sort-up\" d=\"M4 0 7.5 4.5H0.5z\"/>\n <path class=\"lt-sort-down\" d=\"M4 14 0.5 9.5H7.5z\"/>\n </svg>\n </span>\n }\n </div>\n</div>\n", styles: [":host{display:flex;width:100%;align-items:center}.lt-header-container{display:flex;align-items:center;width:100%;height:100%;cursor:default;gap:.25rem}.lt-header-container.nf-can-sort{cursor:pointer}.lt-header-label{display:flex;align-items:center;flex:1 1 auto;min-width:0;gap:.25rem}.lt-header-text{color:var(--lt-header-text);font-family:var(--lt-font);font-size:.6875rem;font-weight:600;letter-spacing:.045em;text-transform:uppercase;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;transition:color .15s}.lt-header-text.is-sorted{color:var(--lt-icon-active);font-weight:700}.lt-header-text-container{display:flex;align-items:center;min-width:0;flex:1 1 auto}.lt-header-text-container.show-input-filter{width:100%}.lt-header-icon{display:inline-flex;align-items:center;justify-content:center;color:var(--lt-icon);cursor:pointer;transition:opacity .2s,color .2s}.lt-header-icon svg{display:block}.lt-header-icon:hover{color:var(--lt-icon-active)}.lt-col-hide-button,.lt-col-filter-button{opacity:0}.lt-header-container:hover .lt-col-hide-button,.lt-header-container:hover .lt-col-filter-button{opacity:1}.lt-col-filter-button.nf-filter-applied{opacity:1;color:var(--lt-icon-active)}.lt-sort{display:inline-flex;align-items:center;flex:0 0 auto;margin-left:auto;margin-right:.375rem}.lt-sort svg{display:block}.lt-sort .lt-sort-up,.lt-sort .lt-sort-down{fill:var(--lt-icon);opacity:.35;transition:opacity .15s,fill .15s}.lt-sort.asc .lt-sort-up,.lt-sort.desc .lt-sort-down{fill:var(--lt-icon-active);opacity:1}.nf-hidden{display:none}\n"], dependencies: [{ kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] }); }
|
|
11748
|
+
}
|
|
11749
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ListHeaderCellComponent, decorators: [{
|
|
11750
|
+
type: Component,
|
|
11751
|
+
args: [{ selector: 'lib-list-header-cell', standalone: false, template: "<div\n (click)=\"sort()\"\n [ngClass]=\"{ 'nf-can-sort': enableSorting }\"\n class=\"lt-header-container\"\n id=\"TableHeaderContainer_{{ column.key }}\"\n>\n @if (column.hideable !== false) {\n <span\n (click)=\"hide($event)\"\n class=\"lt-header-icon lt-col-hide-button\"\n id=\"HeaderHide_{{ column.key }}\"\n title=\"Hide Column\"\n >\n <svg viewBox=\"0 0 16 16\" width=\"14\" height=\"14\" aria-hidden=\"true\">\n <path fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.3\"\n d=\"M1 8s2.7-4.5 7-4.5S15 8 15 8s-2.7 4.5-7 4.5S1 8 1 8Z\"/>\n <circle cx=\"8\" cy=\"8\" r=\"1.9\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.3\"/>\n <line x1=\"2.5\" y1=\"13.5\" x2=\"13.5\" y2=\"2.5\" stroke=\"currentColor\" stroke-width=\"1.3\"/>\n </svg>\n </span>\n }\n <div class=\"lt-header-label\">\n <span\n [title]=\"column.label\"\n class=\"lt-header-text\"\n [class.is-sorted]=\"sortDir === 'asc' || sortDir === 'desc'\"\n id=\"HeaderName_{{ column.key }}\"\n unselectable=\"on\"\n >{{ column.label }}</span>\n @if (enableSorting) {\n <span class=\"lt-sort\" [class.asc]=\"sortDir === 'asc'\" [class.desc]=\"sortDir === 'desc'\">\n <svg viewBox=\"0 0 8 14\" width=\"8\" height=\"13\" aria-hidden=\"true\">\n <path class=\"lt-sort-up\" d=\"M4 0 7.5 4.5H0.5z\"/>\n <path class=\"lt-sort-down\" d=\"M4 14 0.5 9.5H7.5z\"/>\n </svg>\n </span>\n }\n </div>\n</div>\n", styles: [":host{display:flex;width:100%;align-items:center}.lt-header-container{display:flex;align-items:center;width:100%;height:100%;cursor:default;gap:.25rem}.lt-header-container.nf-can-sort{cursor:pointer}.lt-header-label{display:flex;align-items:center;flex:1 1 auto;min-width:0;gap:.25rem}.lt-header-text{color:var(--lt-header-text);font-family:var(--lt-font);font-size:.6875rem;font-weight:600;letter-spacing:.045em;text-transform:uppercase;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;transition:color .15s}.lt-header-text.is-sorted{color:var(--lt-icon-active);font-weight:700}.lt-header-text-container{display:flex;align-items:center;min-width:0;flex:1 1 auto}.lt-header-text-container.show-input-filter{width:100%}.lt-header-icon{display:inline-flex;align-items:center;justify-content:center;color:var(--lt-icon);cursor:pointer;transition:opacity .2s,color .2s}.lt-header-icon svg{display:block}.lt-header-icon:hover{color:var(--lt-icon-active)}.lt-col-hide-button,.lt-col-filter-button{opacity:0}.lt-header-container:hover .lt-col-hide-button,.lt-header-container:hover .lt-col-filter-button{opacity:1}.lt-col-filter-button.nf-filter-applied{opacity:1;color:var(--lt-icon-active)}.lt-sort{display:inline-flex;align-items:center;flex:0 0 auto;margin-left:auto;margin-right:.375rem}.lt-sort svg{display:block}.lt-sort .lt-sort-up,.lt-sort .lt-sort-down{fill:var(--lt-icon);opacity:.35;transition:opacity .15s,fill .15s}.lt-sort.asc .lt-sort-up,.lt-sort.desc .lt-sort-down{fill:var(--lt-icon-active);opacity:1}.nf-hidden{display:none}\n"] }]
|
|
11752
|
+
}], propDecorators: { column: [{
|
|
11753
|
+
type: Input
|
|
11754
|
+
}], sortDir: [{
|
|
11755
|
+
type: Input
|
|
11756
|
+
}], sortChanged: [{
|
|
11757
|
+
type: Output
|
|
11758
|
+
}], hideColumn: [{
|
|
11759
|
+
type: Output
|
|
11760
|
+
}] } });
|
|
11761
|
+
|
|
11762
|
+
/*
|
|
11763
|
+
Copyright NetFoundry Inc.
|
|
11764
|
+
|
|
11765
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
11766
|
+
you may not use this file except in compliance with the License.
|
|
11767
|
+
You may obtain a copy of the License at
|
|
11768
|
+
|
|
11769
|
+
https://www.apache.org/licenses/LICENSE-2.0
|
|
11770
|
+
|
|
11771
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11772
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
11773
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11774
|
+
See the License for the specific language governing permissions and
|
|
11775
|
+
limitations under the License.
|
|
11776
|
+
*/
|
|
11777
|
+
const DATE_PRESETS = [
|
|
11778
|
+
{ label: 'Any time', value: '' },
|
|
11779
|
+
{ label: 'Last Hour', value: 'hour' },
|
|
11780
|
+
{ label: 'Last Day', value: 'day' },
|
|
11781
|
+
{ label: 'Last Week', value: 'week' },
|
|
11782
|
+
{ label: 'Last Month', value: 'month' },
|
|
11783
|
+
];
|
|
11784
|
+
/**
|
|
11785
|
+
* A single control in the table's filter row. Renders the right widget for the
|
|
11786
|
+
* column's `filterType` (text input / single-select / multi-select / date-range)
|
|
11787
|
+
* and pushes changes through {@link DataTableFilterService}, whose value contract the
|
|
11788
|
+
* controller query-builder already understands (icontains / = / anyOf() / datetime range).
|
|
11789
|
+
*/
|
|
11790
|
+
class ListFilterCellComponent {
|
|
11791
|
+
constructor(filterService, listSvc) {
|
|
11792
|
+
this.filterService = filterService;
|
|
11793
|
+
this.listSvc = listSvc;
|
|
11794
|
+
this.mode = 'text';
|
|
11795
|
+
this.options = [];
|
|
11796
|
+
this.textValue = '';
|
|
11797
|
+
this.selectedValue = '';
|
|
11798
|
+
this.selectedValues = [];
|
|
11799
|
+
/** In-dropdown search text for the multi-select attribute combo. */
|
|
11800
|
+
this.attrSearch = '';
|
|
11801
|
+
this.open = false;
|
|
11802
|
+
this.menuLeft = 0;
|
|
11803
|
+
this.menuTop = 0;
|
|
11804
|
+
this.menuWidth = 0;
|
|
11805
|
+
// custom date range (daterange mode)
|
|
11806
|
+
this.dateRangeStart = null;
|
|
11807
|
+
this.dateRangeEnd = null;
|
|
11808
|
+
this.sub = new Subscription();
|
|
11809
|
+
this.textDebounced = _.debounce(() => this.emitText(), 400);
|
|
11810
|
+
}
|
|
11811
|
+
ngOnInit() {
|
|
11812
|
+
this.mode = this.resolveMode();
|
|
11813
|
+
this.options = this.resolveOptions();
|
|
11814
|
+
this.sub.add(this.filterService.filtersChanged.subscribe((filters) => this.syncFromFilters(filters)));
|
|
11815
|
+
// close this menu whenever another dropdown opens
|
|
11816
|
+
this.sub.add(this.listSvc.menuOpened$.subscribe((owner) => {
|
|
11817
|
+
if (owner !== this) {
|
|
11818
|
+
this.open = false;
|
|
11819
|
+
}
|
|
11820
|
+
}));
|
|
11821
|
+
}
|
|
11822
|
+
ngOnDestroy() {
|
|
11823
|
+
this.sub.unsubscribe();
|
|
11824
|
+
}
|
|
11825
|
+
// ------------------------------------------------------------------ config
|
|
11826
|
+
get field() {
|
|
11827
|
+
return this.column.filterField || this.column.field || this.column.key;
|
|
11828
|
+
}
|
|
11829
|
+
get placeholder() {
|
|
11830
|
+
return this.column.filterPlaceholder || 'All';
|
|
11831
|
+
}
|
|
11832
|
+
resolveMode() {
|
|
11833
|
+
switch (this.column.filterType) {
|
|
11834
|
+
case 'TEXTINPUT':
|
|
11835
|
+
return 'text';
|
|
11836
|
+
case 'ATTRIBUTE':
|
|
11837
|
+
return 'multi';
|
|
11838
|
+
case 'DATETIME':
|
|
11839
|
+
return 'daterange';
|
|
11840
|
+
default:
|
|
11841
|
+
return 'single';
|
|
11842
|
+
}
|
|
11843
|
+
}
|
|
11844
|
+
resolveOptions() {
|
|
11845
|
+
if (this.mode === 'daterange') {
|
|
11846
|
+
return DATE_PRESETS;
|
|
11847
|
+
}
|
|
11848
|
+
if (this.mode === 'multi') {
|
|
11849
|
+
const getAttrs = this.column.headerParams?.['getRoleAttributes'];
|
|
11850
|
+
const attrs = (typeof getAttrs === 'function' ? getAttrs() : null) || this.column.filterOptions || [];
|
|
11851
|
+
return attrs.map((a) => (typeof a === 'string' ? { label: a, value: a } : a));
|
|
11852
|
+
}
|
|
11853
|
+
const raw = this.column.getFilterOptions ? this.column.getFilterOptions() : this.column.filterOptions || [];
|
|
11854
|
+
return raw;
|
|
11855
|
+
}
|
|
11856
|
+
// ------------------------------------------------------------------ display
|
|
11857
|
+
/** Short display of the current selection, shown after the field label when set. */
|
|
11858
|
+
get valueLabel() {
|
|
11859
|
+
if (this.mode === 'multi') {
|
|
11860
|
+
if (this.selectedValues.length === 1) {
|
|
11861
|
+
return this.selectedValues[0];
|
|
11862
|
+
}
|
|
11863
|
+
return `${this.selectedValues.length}`;
|
|
11864
|
+
}
|
|
11865
|
+
if (this.mode === 'daterange') {
|
|
11866
|
+
if (this.selectedValue === 'custom' && this.dateRangeStart) {
|
|
11867
|
+
const start = moment$1(this.dateRangeStart).format('M/D/YY');
|
|
11868
|
+
const end = this.dateRangeEnd ? moment$1(this.dateRangeEnd).format('M/D/YY') : '';
|
|
11869
|
+
return end ? `${start} – ${end}` : start;
|
|
11870
|
+
}
|
|
11871
|
+
const found = this.options.find((o) => o.value === this.selectedValue);
|
|
11872
|
+
return found && found.value ? found.label : '';
|
|
11873
|
+
}
|
|
11874
|
+
const match = this.options.find((o) => `${o.value}` === `${this.selectedValue}`);
|
|
11875
|
+
return match && match.value !== '' && match.value != null ? match.label : '';
|
|
11876
|
+
}
|
|
11877
|
+
get hasValue() {
|
|
11878
|
+
if (this.mode === 'multi') {
|
|
11879
|
+
return this.selectedValues.length > 0;
|
|
11880
|
+
}
|
|
11881
|
+
return this.selectedValue !== '' && this.selectedValue != null;
|
|
11882
|
+
}
|
|
11883
|
+
isOptionSelected(opt) {
|
|
11884
|
+
if (this.mode === 'multi') {
|
|
11885
|
+
return this.selectedValues.includes(opt.value);
|
|
11886
|
+
}
|
|
11887
|
+
return `${opt.value}` === `${this.selectedValue}`;
|
|
11888
|
+
}
|
|
11889
|
+
/** Attribute options filtered by the in-dropdown search box (multi mode). */
|
|
11890
|
+
get filteredOptions() {
|
|
11891
|
+
if (this.mode !== 'multi' || !this.attrSearch) {
|
|
11892
|
+
return this.options;
|
|
11893
|
+
}
|
|
11894
|
+
const q = this.attrSearch.toLowerCase();
|
|
11895
|
+
return this.options.filter((o) => `${o.label ?? o.value}`.toLowerCase().includes(q));
|
|
11896
|
+
}
|
|
11897
|
+
/** A named attribute (rendered with @ + secondary color) vs a role attribute (# + primary). */
|
|
11898
|
+
isNamedOption(opt) {
|
|
11899
|
+
return String(opt?.value ?? opt?.label ?? '').charAt(0) === '@';
|
|
11900
|
+
}
|
|
11901
|
+
/** Option label with its #/@ prefix, matching the Roles column chips. */
|
|
11902
|
+
attrLabel(opt) {
|
|
11903
|
+
const raw = String(opt?.label ?? opt?.value ?? '');
|
|
11904
|
+
if (raw.charAt(0) === '@' || raw.charAt(0) === '#') {
|
|
11905
|
+
return raw;
|
|
11906
|
+
}
|
|
11907
|
+
return '#' + raw;
|
|
11908
|
+
}
|
|
11909
|
+
// ------------------------------------------------------------------ open/close
|
|
11910
|
+
toggleMenu(event) {
|
|
11911
|
+
event.stopPropagation();
|
|
11912
|
+
if (this.open) {
|
|
11913
|
+
this.open = false;
|
|
11914
|
+
return;
|
|
11915
|
+
}
|
|
11916
|
+
const rect = this.trigger?.nativeElement.getBoundingClientRect();
|
|
11917
|
+
if (rect) {
|
|
11918
|
+
this.menuLeft = rect.left;
|
|
11919
|
+
this.menuTop = rect.bottom + 4;
|
|
11920
|
+
this.menuWidth = Math.max(rect.width, this.mode === 'multi' ? 220 : 160);
|
|
11921
|
+
}
|
|
11922
|
+
// refresh dynamic option sources (e.g. role attributes) each open
|
|
11923
|
+
this.options = this.resolveOptions();
|
|
11924
|
+
this.attrSearch = '';
|
|
11925
|
+
this.open = true;
|
|
11926
|
+
this.listSvc.notifyMenuOpened(this);
|
|
11927
|
+
}
|
|
11928
|
+
closeMenu() {
|
|
11929
|
+
this.open = false;
|
|
11930
|
+
}
|
|
11931
|
+
// ------------------------------------------------------------------ text
|
|
11932
|
+
onTextInput() {
|
|
11933
|
+
this.textDebounced();
|
|
11934
|
+
}
|
|
11935
|
+
emitText() {
|
|
11936
|
+
this.filterService.updateFilter({
|
|
11937
|
+
filterName: this.column.label,
|
|
11938
|
+
columnId: this.field,
|
|
11939
|
+
value: this.textValue,
|
|
11940
|
+
label: this.textValue,
|
|
11941
|
+
type: 'TEXTINPUT',
|
|
11942
|
+
});
|
|
11943
|
+
}
|
|
11944
|
+
// ------------------------------------------------------------------ single
|
|
11945
|
+
selectOption(opt) {
|
|
11946
|
+
this.selectedValue = opt.value;
|
|
11947
|
+
this.open = false;
|
|
11948
|
+
if (opt.value === '' || opt.value == null) {
|
|
11949
|
+
this.removeThis();
|
|
11950
|
+
return;
|
|
11951
|
+
}
|
|
11952
|
+
this.filterService.updateFilter({
|
|
11953
|
+
filterName: this.column.label,
|
|
11954
|
+
columnId: this.field,
|
|
11955
|
+
value: opt.value,
|
|
11956
|
+
label: opt.label,
|
|
11957
|
+
type: this.column.filterType || 'SELECT',
|
|
11958
|
+
});
|
|
11959
|
+
}
|
|
11960
|
+
// ------------------------------------------------------------------ multi
|
|
11961
|
+
toggleOption(opt) {
|
|
11962
|
+
const idx = this.selectedValues.indexOf(opt.value);
|
|
11963
|
+
if (idx >= 0) {
|
|
11964
|
+
this.selectedValues.splice(idx, 1);
|
|
11965
|
+
}
|
|
11966
|
+
else {
|
|
11967
|
+
this.selectedValues.push(opt.value);
|
|
11968
|
+
}
|
|
11969
|
+
this.emitMulti();
|
|
11970
|
+
}
|
|
11971
|
+
emitMulti() {
|
|
11972
|
+
if (!this.selectedValues.length) {
|
|
11973
|
+
this.removeThis();
|
|
11974
|
+
return;
|
|
11975
|
+
}
|
|
11976
|
+
const filter = {
|
|
11977
|
+
filterName: this.column.label,
|
|
11978
|
+
columnId: this.field,
|
|
11979
|
+
value: [...this.selectedValues],
|
|
11980
|
+
label: this.selectedValues.join(', '),
|
|
11981
|
+
type: 'ATTRIBUTE',
|
|
11982
|
+
semantic: 'AnyOf',
|
|
11983
|
+
};
|
|
11984
|
+
this.filterService.updateFilter(filter);
|
|
11985
|
+
}
|
|
11986
|
+
// ------------------------------------------------------------------ date range
|
|
11987
|
+
selectDatePreset(opt) {
|
|
11988
|
+
this.selectedValue = opt.value;
|
|
11989
|
+
this.dateRangeStart = null;
|
|
11990
|
+
this.dateRangeEnd = null;
|
|
11991
|
+
this.open = false;
|
|
11992
|
+
if (!opt.value) {
|
|
11993
|
+
this.removeThis();
|
|
11994
|
+
return;
|
|
11995
|
+
}
|
|
11996
|
+
const end = moment$1();
|
|
11997
|
+
let start;
|
|
11998
|
+
switch (opt.value) {
|
|
11999
|
+
case 'hour':
|
|
12000
|
+
start = moment$1().subtract(1, 'hours');
|
|
12001
|
+
break;
|
|
12002
|
+
case 'week':
|
|
12003
|
+
start = moment$1().subtract(7, 'days');
|
|
12004
|
+
break;
|
|
12005
|
+
case 'month':
|
|
12006
|
+
start = moment$1().subtract(1, 'month');
|
|
12007
|
+
break;
|
|
12008
|
+
case 'day':
|
|
12009
|
+
default:
|
|
12010
|
+
start = moment$1().subtract(24, 'hours');
|
|
12011
|
+
break;
|
|
12012
|
+
}
|
|
12013
|
+
this.filterService.updateFilter({
|
|
12014
|
+
filterName: this.column.label,
|
|
12015
|
+
columnId: this.field,
|
|
12016
|
+
value: [encodeURIComponent(start.toISOString()), encodeURIComponent(end.toISOString())],
|
|
12017
|
+
label: opt.label,
|
|
12018
|
+
type: 'DATETIME',
|
|
12019
|
+
});
|
|
12020
|
+
}
|
|
12021
|
+
openCustomRange(event) {
|
|
12022
|
+
event.stopPropagation();
|
|
12023
|
+
this.open = false;
|
|
12024
|
+
// open the Material calendar on the next tick (after the menu closes)
|
|
12025
|
+
_.delay(() => this.rangePicker?.open(), 0);
|
|
12026
|
+
}
|
|
12027
|
+
onRangeStartChanged(date) {
|
|
12028
|
+
this.dateRangeStart = date;
|
|
12029
|
+
this.emitCustomRange();
|
|
12030
|
+
}
|
|
12031
|
+
onRangeEndChanged(date) {
|
|
12032
|
+
this.dateRangeEnd = date;
|
|
12033
|
+
this.emitCustomRange();
|
|
12034
|
+
}
|
|
12035
|
+
emitCustomRange() {
|
|
12036
|
+
if (!this.dateRangeStart || !this.dateRangeEnd) {
|
|
12037
|
+
return;
|
|
12038
|
+
}
|
|
12039
|
+
this.selectedValue = 'custom';
|
|
12040
|
+
const start = moment$1(this.dateRangeStart).startOf('day');
|
|
12041
|
+
const end = moment$1(this.dateRangeEnd).endOf('day');
|
|
12042
|
+
this.filterService.updateFilter({
|
|
12043
|
+
filterName: this.column.label,
|
|
12044
|
+
columnId: this.field,
|
|
12045
|
+
value: [encodeURIComponent(start.toISOString()), encodeURIComponent(end.toISOString())],
|
|
12046
|
+
label: `${start.format('M/D/YY')} – ${end.format('M/D/YY')}`,
|
|
12047
|
+
type: 'DATETIME',
|
|
12048
|
+
});
|
|
12049
|
+
}
|
|
12050
|
+
// ------------------------------------------------------------------ clear
|
|
12051
|
+
clear(event) {
|
|
12052
|
+
event.stopPropagation();
|
|
12053
|
+
this.selectedValue = '';
|
|
12054
|
+
this.selectedValues = [];
|
|
12055
|
+
this.textValue = '';
|
|
12056
|
+
this.dateRangeStart = null;
|
|
12057
|
+
this.dateRangeEnd = null;
|
|
12058
|
+
this.removeThis();
|
|
12059
|
+
}
|
|
12060
|
+
removeThis() {
|
|
12061
|
+
this.filterService.removeFilter({
|
|
12062
|
+
filterName: this.column.label,
|
|
12063
|
+
columnId: this.field,
|
|
12064
|
+
value: '',
|
|
12065
|
+
label: '',
|
|
12066
|
+
});
|
|
12067
|
+
}
|
|
12068
|
+
// ------------------------------------------------------------------ sync
|
|
12069
|
+
syncFromFilters(filters) {
|
|
12070
|
+
const match = filters.find((f) => f.columnId === this.field);
|
|
12071
|
+
if (!match) {
|
|
12072
|
+
this.textValue = '';
|
|
12073
|
+
this.selectedValue = '';
|
|
12074
|
+
this.selectedValues = [];
|
|
12075
|
+
this.dateRangeStart = null;
|
|
12076
|
+
this.dateRangeEnd = null;
|
|
12077
|
+
return;
|
|
12078
|
+
}
|
|
12079
|
+
if (this.mode === 'text') {
|
|
12080
|
+
this.textValue = match.value ?? '';
|
|
12081
|
+
}
|
|
12082
|
+
else if (this.mode === 'multi') {
|
|
12083
|
+
this.selectedValues = _.isArray(match.value) ? [...match.value] : [match.value];
|
|
12084
|
+
}
|
|
12085
|
+
else if (this.mode === 'daterange') {
|
|
12086
|
+
// keep the locally chosen preset label; match.value is the raw [start,end] range
|
|
12087
|
+
}
|
|
12088
|
+
else {
|
|
12089
|
+
this.selectedValue = match.value;
|
|
12090
|
+
}
|
|
12091
|
+
}
|
|
12092
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ListFilterCellComponent, deps: [{ token: DataTableFilterService }, { token: ListTableService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
12093
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: ListFilterCellComponent, isStandalone: false, selector: "lib-list-filter-cell", inputs: { column: "column" }, viewQueries: [{ propertyName: "trigger", first: true, predicate: ["trigger"], descendants: true }, { propertyName: "rangePicker", first: true, predicate: ["rangePicker"], descendants: true }], ngImport: i0, template: "@switch (mode) {\n @case ('text') {\n <div class=\"fc-text\">\n <input\n class=\"fc-input\"\n type=\"text\"\n [placeholder]=\"column.filterPlaceholder || 'Filter'\"\n [(ngModel)]=\"textValue\"\n (keyup)=\"onTextInput()\"\n />\n @if (textValue) {\n <span class=\"fc-clear\" (click)=\"clear($event)\" title=\"Clear\" aria-label=\"Clear filter\">×</span>\n }\n </div>\n }\n @default {\n <button\n #trigger\n type=\"button\"\n class=\"fc-pill\"\n [class.has-value]=\"hasValue\"\n [class.is-open]=\"open\"\n (click)=\"toggleMenu($event)\"\n aria-haspopup=\"listbox\"\n [attr.aria-expanded]=\"open\"\n >\n <span class=\"fc-pill-field\">{{ column.label }}</span>\n @if (hasValue) {\n <span class=\"fc-pill-value\" [title]=\"valueLabel\">{{ valueLabel }}</span>\n <span class=\"fc-clear\" (click)=\"clear($event)\" title=\"Clear\" aria-label=\"Clear filter\">×</span>\n } @else {\n <span class=\"fc-caret\" aria-hidden=\"true\">\n <svg viewBox=\"0 0 10 6\" width=\"9\" height=\"6\"><path fill=\"currentColor\" d=\"M0 0h10L5 6z\"/></svg>\n </span>\n }\n </button>\n\n @if (open) {\n <div\n class=\"fc-menu\"\n (clickOutside)=\"closeMenu()\"\n [ngStyle]=\"{ left: menuLeft + 'px', top: menuTop + 'px', 'min-width.px': menuWidth }\"\n >\n @if (mode === 'multi') {\n <div class=\"fc-search\">\n <svg class=\"fc-search-icon\" viewBox=\"0 0 16 16\" width=\"13\" height=\"13\" aria-hidden=\"true\">\n <circle cx=\"7\" cy=\"7\" r=\"4.3\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"/>\n <line x1=\"10.4\" y1=\"10.4\" x2=\"14.6\" y2=\"14.6\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\"/>\n </svg>\n <input\n class=\"fc-search-input\"\n type=\"text\"\n [(ngModel)]=\"attrSearch\"\n placeholder=\"Search attributes\"\n (click)=\"$event.stopPropagation()\"\n autocomplete=\"off\"\n />\n </div>\n @if (!filteredOptions.length) {\n <div class=\"fc-empty\">{{ options.length ? 'No matches' : 'No attributes available' }}</div>\n }\n <div class=\"fc-opt-list\">\n @for (opt of filteredOptions; track opt.value) {\n <button type=\"button\" class=\"fc-opt\" [class.selected]=\"isOptionSelected(opt)\" (click)=\"toggleOption(opt)\">\n <span class=\"fc-check\" aria-hidden=\"true\">\n @if (isOptionSelected(opt)) {\n <svg viewBox=\"0 0 12 12\" width=\"10\" height=\"10\"><path fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M2 6.3 4.6 9 10 3\"/></svg>\n }\n </span>\n <span class=\"fc-attr\" [class.at]=\"isNamedOption(opt)\">{{ attrLabel(opt) }}</span>\n </button>\n }\n </div>\n } @else if (mode === 'daterange') {\n @for (opt of options; track opt.value) {\n <button type=\"button\" class=\"fc-opt\" [class.selected]=\"isOptionSelected(opt)\" (click)=\"selectDatePreset(opt)\">\n <span class=\"fc-opt-label\">{{ opt.label }}</span>\n </button>\n }\n <div class=\"fc-divider\"></div>\n <button type=\"button\" class=\"fc-opt\" [class.selected]=\"selectedValue === 'custom'\" (click)=\"openCustomRange($event)\">\n <span class=\"fc-opt-label\">Custom range\u2026</span>\n </button>\n } @else {\n @for (opt of options; track opt.value) {\n <button type=\"button\" class=\"fc-opt\" [class.selected]=\"isOptionSelected(opt)\" (click)=\"selectOption(opt)\">\n @if (opt.chipClass) {\n <span [ngClass]=\"opt.chipClass\">{{ opt.label }}</span>\n } @else {\n <span class=\"fc-opt-label\">{{ opt.label }}</span>\n }\n </button>\n }\n }\n </div>\n }\n }\n}\n\n@if (mode === 'daterange') {\n <!-- hidden Material range input; the calendar is opened from the \"Custom range\u2026\" option -->\n <mat-date-range-input class=\"fc-hidden-range\" [rangePicker]=\"rangePicker\">\n <input matStartDate [(ngModel)]=\"dateRangeStart\" (dateChange)=\"onRangeStartChanged($event.value)\" tabindex=\"-1\" aria-hidden=\"true\" />\n <input matEndDate [(ngModel)]=\"dateRangeEnd\" (dateChange)=\"onRangeEndChanged($event.value)\" tabindex=\"-1\" aria-hidden=\"true\" />\n </mat-date-range-input>\n <mat-date-range-picker #rangePicker xPosition=\"start\" yPosition=\"below\"></mat-date-range-picker>\n}\n", styles: [":host{display:inline-flex;position:relative}.fc-hidden-range{position:absolute;left:0;top:100%;width:1px;height:1px;opacity:0;pointer-events:none;overflow:hidden}.fc-divider{height:1px;margin:.25rem;background:var(--lt-border)}.fc-text{position:relative;display:flex;align-items:center}.fc-input{width:100%;height:1.875rem;padding:0 1.5rem 0 .5rem;font-family:var(--lt-font);font-size:.75rem;color:var(--lt-cell-text);background:var(--lt-control-bg);border:1px solid var(--lt-border);border-radius:var(--lt-radius);outline:none}.fc-input::placeholder{color:var(--placeholder)}.fc-input:focus{border-color:var(--lt-link)}.fc-pill{display:inline-flex;align-items:center;gap:.375rem;height:var(--lt-toolbar-height);max-width:14rem;padding:0 .75rem;font-family:var(--lt-font);font-size:.8125rem;font-weight:600;color:var(--lt-cell-text);background:var(--lt-control-bg);border:1px solid var(--lt-border);border-radius:var(--lt-radius);cursor:pointer;white-space:nowrap;transition:border-color .15s,background .15s,color .15s}.fc-pill:hover,.fc-pill.is-open{border-color:var(--lt-link)}.fc-pill.has-value{border-color:var(--lt-link);background:color-mix(in srgb,var(--lt-link) 8%,var(--lt-control-bg))}.fc-pill-field{color:var(--lt-cell-text)}.fc-pill.has-value .fc-pill-field{color:var(--groupText)}.fc-pill.has-value .fc-pill-field:after{content:\":\"}.fc-pill-value{color:var(--lt-link);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fc-caret{flex:0 0 auto;display:inline-flex;color:var(--lt-icon)}.fc-clear{flex:0 0 auto;font-size:1rem;line-height:1;color:var(--lt-icon);cursor:pointer}.fc-clear:hover{color:var(--lt-icon-active)}.fc-text .fc-clear{position:absolute;right:.5rem}.fc-menu{position:fixed;z-index:1000;max-height:18rem;overflow-y:auto;padding:.25rem;background:var(--lt-control-bg);border:1px solid var(--lt-border);border-radius:var(--lt-radius);box-shadow:var(--shadowLarge);animation:fc-in .1s ease-out}@keyframes fc-in{0%{opacity:0;transform:translateY(-.25rem)}to{opacity:1;transform:translateY(0)}}.fc-opt{display:flex;align-items:center;gap:.5rem;width:100%;padding:.4375rem .5rem;font-family:var(--lt-font);font-size:.8125rem;color:var(--lt-cell-text);background:transparent;border:none;border-radius:var(--lt-radius);cursor:pointer;text-align:left}.fc-opt:hover{background:var(--lt-row-hover)}.fc-opt.selected{color:var(--lt-link);font-weight:600}.fc-opt-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fc-check{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;width:1rem;height:1rem;box-sizing:border-box;border:.09375rem solid var(--lt-border);border-radius:.25rem;color:var(--lt-control-bg);background:var(--lt-control-bg)}.fc-opt.selected .fc-check{background:var(--lt-link);border-color:var(--lt-link)}.fc-empty{padding:.5rem;font-size:.75rem;color:var(--groupText)}.fc-search{position:sticky;top:0;z-index:1;display:flex;align-items:center;gap:.375rem;padding:.125rem .25rem .375rem;background:var(--lt-control-bg)}.fc-search-icon{flex:0 0 auto;margin-left:.25rem;color:var(--lt-icon)}.fc-search-input{flex:1 1 auto;min-width:0;height:1.75rem;padding:0 .5rem;font-family:var(--lt-font);font-size:.75rem;color:var(--lt-cell-text);background:var(--lt-control-bg);border:1px solid var(--lt-border);border-radius:var(--lt-radius);outline:none}.fc-search-input::placeholder{color:var(--placeholder)}.fc-search-input:focus{border-color:var(--lt-link)}.fc-opt-list{display:flex;flex-direction:column}.fc-attr{display:inline-flex;align-items:center;padding:.0625rem .375rem;font-size:.75rem;font-weight:600;line-height:1.4;border-radius:.25rem;color:var(--lt-link);background:color-mix(in srgb,var(--lt-link) 12%,transparent);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fc-attr.at{color:var(--secondary);background:color-mix(in srgb,var(--secondary) 14%,transparent)}.fc-opt.selected .fc-attr{color:var(--lt-link)}.fc-opt.selected .fc-attr.at{color:var(--secondary)}\n"], dependencies: [{ kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i6.MatDateRangeInput, selector: "mat-date-range-input", inputs: ["rangePicker", "required", "dateFilter", "min", "max", "disabled", "separator", "comparisonStart", "comparisonEnd"], exportAs: ["matDateRangeInput"] }, { kind: "directive", type: i6.MatStartDate, selector: "input[matStartDate]", outputs: ["dateChange", "dateInput"] }, { kind: "directive", type: i6.MatEndDate, selector: "input[matEndDate]", outputs: ["dateChange", "dateInput"] }, { kind: "component", type: i6.MatDateRangePicker, selector: "mat-date-range-picker", exportAs: ["matDateRangePicker"] }, { kind: "directive", type: i3$1.ClickOutsideDirective, selector: "[clickOutside]", inputs: ["clickOutsideEnabled", "attachOutsideOnClick", "delayClickOutsideInit", "emitOnBlur", "exclude", "excludeBeforeClick", "clickOutsideEvents"], outputs: ["clickOutside"] }] }); }
|
|
12094
|
+
}
|
|
12095
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ListFilterCellComponent, decorators: [{
|
|
12096
|
+
type: Component,
|
|
12097
|
+
args: [{ selector: 'lib-list-filter-cell', standalone: false, template: "@switch (mode) {\n @case ('text') {\n <div class=\"fc-text\">\n <input\n class=\"fc-input\"\n type=\"text\"\n [placeholder]=\"column.filterPlaceholder || 'Filter'\"\n [(ngModel)]=\"textValue\"\n (keyup)=\"onTextInput()\"\n />\n @if (textValue) {\n <span class=\"fc-clear\" (click)=\"clear($event)\" title=\"Clear\" aria-label=\"Clear filter\">×</span>\n }\n </div>\n }\n @default {\n <button\n #trigger\n type=\"button\"\n class=\"fc-pill\"\n [class.has-value]=\"hasValue\"\n [class.is-open]=\"open\"\n (click)=\"toggleMenu($event)\"\n aria-haspopup=\"listbox\"\n [attr.aria-expanded]=\"open\"\n >\n <span class=\"fc-pill-field\">{{ column.label }}</span>\n @if (hasValue) {\n <span class=\"fc-pill-value\" [title]=\"valueLabel\">{{ valueLabel }}</span>\n <span class=\"fc-clear\" (click)=\"clear($event)\" title=\"Clear\" aria-label=\"Clear filter\">×</span>\n } @else {\n <span class=\"fc-caret\" aria-hidden=\"true\">\n <svg viewBox=\"0 0 10 6\" width=\"9\" height=\"6\"><path fill=\"currentColor\" d=\"M0 0h10L5 6z\"/></svg>\n </span>\n }\n </button>\n\n @if (open) {\n <div\n class=\"fc-menu\"\n (clickOutside)=\"closeMenu()\"\n [ngStyle]=\"{ left: menuLeft + 'px', top: menuTop + 'px', 'min-width.px': menuWidth }\"\n >\n @if (mode === 'multi') {\n <div class=\"fc-search\">\n <svg class=\"fc-search-icon\" viewBox=\"0 0 16 16\" width=\"13\" height=\"13\" aria-hidden=\"true\">\n <circle cx=\"7\" cy=\"7\" r=\"4.3\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"/>\n <line x1=\"10.4\" y1=\"10.4\" x2=\"14.6\" y2=\"14.6\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\"/>\n </svg>\n <input\n class=\"fc-search-input\"\n type=\"text\"\n [(ngModel)]=\"attrSearch\"\n placeholder=\"Search attributes\"\n (click)=\"$event.stopPropagation()\"\n autocomplete=\"off\"\n />\n </div>\n @if (!filteredOptions.length) {\n <div class=\"fc-empty\">{{ options.length ? 'No matches' : 'No attributes available' }}</div>\n }\n <div class=\"fc-opt-list\">\n @for (opt of filteredOptions; track opt.value) {\n <button type=\"button\" class=\"fc-opt\" [class.selected]=\"isOptionSelected(opt)\" (click)=\"toggleOption(opt)\">\n <span class=\"fc-check\" aria-hidden=\"true\">\n @if (isOptionSelected(opt)) {\n <svg viewBox=\"0 0 12 12\" width=\"10\" height=\"10\"><path fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M2 6.3 4.6 9 10 3\"/></svg>\n }\n </span>\n <span class=\"fc-attr\" [class.at]=\"isNamedOption(opt)\">{{ attrLabel(opt) }}</span>\n </button>\n }\n </div>\n } @else if (mode === 'daterange') {\n @for (opt of options; track opt.value) {\n <button type=\"button\" class=\"fc-opt\" [class.selected]=\"isOptionSelected(opt)\" (click)=\"selectDatePreset(opt)\">\n <span class=\"fc-opt-label\">{{ opt.label }}</span>\n </button>\n }\n <div class=\"fc-divider\"></div>\n <button type=\"button\" class=\"fc-opt\" [class.selected]=\"selectedValue === 'custom'\" (click)=\"openCustomRange($event)\">\n <span class=\"fc-opt-label\">Custom range\u2026</span>\n </button>\n } @else {\n @for (opt of options; track opt.value) {\n <button type=\"button\" class=\"fc-opt\" [class.selected]=\"isOptionSelected(opt)\" (click)=\"selectOption(opt)\">\n @if (opt.chipClass) {\n <span [ngClass]=\"opt.chipClass\">{{ opt.label }}</span>\n } @else {\n <span class=\"fc-opt-label\">{{ opt.label }}</span>\n }\n </button>\n }\n }\n </div>\n }\n }\n}\n\n@if (mode === 'daterange') {\n <!-- hidden Material range input; the calendar is opened from the \"Custom range\u2026\" option -->\n <mat-date-range-input class=\"fc-hidden-range\" [rangePicker]=\"rangePicker\">\n <input matStartDate [(ngModel)]=\"dateRangeStart\" (dateChange)=\"onRangeStartChanged($event.value)\" tabindex=\"-1\" aria-hidden=\"true\" />\n <input matEndDate [(ngModel)]=\"dateRangeEnd\" (dateChange)=\"onRangeEndChanged($event.value)\" tabindex=\"-1\" aria-hidden=\"true\" />\n </mat-date-range-input>\n <mat-date-range-picker #rangePicker xPosition=\"start\" yPosition=\"below\"></mat-date-range-picker>\n}\n", styles: [":host{display:inline-flex;position:relative}.fc-hidden-range{position:absolute;left:0;top:100%;width:1px;height:1px;opacity:0;pointer-events:none;overflow:hidden}.fc-divider{height:1px;margin:.25rem;background:var(--lt-border)}.fc-text{position:relative;display:flex;align-items:center}.fc-input{width:100%;height:1.875rem;padding:0 1.5rem 0 .5rem;font-family:var(--lt-font);font-size:.75rem;color:var(--lt-cell-text);background:var(--lt-control-bg);border:1px solid var(--lt-border);border-radius:var(--lt-radius);outline:none}.fc-input::placeholder{color:var(--placeholder)}.fc-input:focus{border-color:var(--lt-link)}.fc-pill{display:inline-flex;align-items:center;gap:.375rem;height:var(--lt-toolbar-height);max-width:14rem;padding:0 .75rem;font-family:var(--lt-font);font-size:.8125rem;font-weight:600;color:var(--lt-cell-text);background:var(--lt-control-bg);border:1px solid var(--lt-border);border-radius:var(--lt-radius);cursor:pointer;white-space:nowrap;transition:border-color .15s,background .15s,color .15s}.fc-pill:hover,.fc-pill.is-open{border-color:var(--lt-link)}.fc-pill.has-value{border-color:var(--lt-link);background:color-mix(in srgb,var(--lt-link) 8%,var(--lt-control-bg))}.fc-pill-field{color:var(--lt-cell-text)}.fc-pill.has-value .fc-pill-field{color:var(--groupText)}.fc-pill.has-value .fc-pill-field:after{content:\":\"}.fc-pill-value{color:var(--lt-link);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fc-caret{flex:0 0 auto;display:inline-flex;color:var(--lt-icon)}.fc-clear{flex:0 0 auto;font-size:1rem;line-height:1;color:var(--lt-icon);cursor:pointer}.fc-clear:hover{color:var(--lt-icon-active)}.fc-text .fc-clear{position:absolute;right:.5rem}.fc-menu{position:fixed;z-index:1000;max-height:18rem;overflow-y:auto;padding:.25rem;background:var(--lt-control-bg);border:1px solid var(--lt-border);border-radius:var(--lt-radius);box-shadow:var(--shadowLarge);animation:fc-in .1s ease-out}@keyframes fc-in{0%{opacity:0;transform:translateY(-.25rem)}to{opacity:1;transform:translateY(0)}}.fc-opt{display:flex;align-items:center;gap:.5rem;width:100%;padding:.4375rem .5rem;font-family:var(--lt-font);font-size:.8125rem;color:var(--lt-cell-text);background:transparent;border:none;border-radius:var(--lt-radius);cursor:pointer;text-align:left}.fc-opt:hover{background:var(--lt-row-hover)}.fc-opt.selected{color:var(--lt-link);font-weight:600}.fc-opt-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fc-check{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;width:1rem;height:1rem;box-sizing:border-box;border:.09375rem solid var(--lt-border);border-radius:.25rem;color:var(--lt-control-bg);background:var(--lt-control-bg)}.fc-opt.selected .fc-check{background:var(--lt-link);border-color:var(--lt-link)}.fc-empty{padding:.5rem;font-size:.75rem;color:var(--groupText)}.fc-search{position:sticky;top:0;z-index:1;display:flex;align-items:center;gap:.375rem;padding:.125rem .25rem .375rem;background:var(--lt-control-bg)}.fc-search-icon{flex:0 0 auto;margin-left:.25rem;color:var(--lt-icon)}.fc-search-input{flex:1 1 auto;min-width:0;height:1.75rem;padding:0 .5rem;font-family:var(--lt-font);font-size:.75rem;color:var(--lt-cell-text);background:var(--lt-control-bg);border:1px solid var(--lt-border);border-radius:var(--lt-radius);outline:none}.fc-search-input::placeholder{color:var(--placeholder)}.fc-search-input:focus{border-color:var(--lt-link)}.fc-opt-list{display:flex;flex-direction:column}.fc-attr{display:inline-flex;align-items:center;padding:.0625rem .375rem;font-size:.75rem;font-weight:600;line-height:1.4;border-radius:.25rem;color:var(--lt-link);background:color-mix(in srgb,var(--lt-link) 12%,transparent);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fc-attr.at{color:var(--secondary);background:color-mix(in srgb,var(--secondary) 14%,transparent)}.fc-opt.selected .fc-attr{color:var(--lt-link)}.fc-opt.selected .fc-attr.at{color:var(--secondary)}\n"] }]
|
|
12098
|
+
}], ctorParameters: () => [{ type: DataTableFilterService }, { type: ListTableService }], propDecorators: { column: [{
|
|
12099
|
+
type: Input
|
|
12100
|
+
}], trigger: [{
|
|
12101
|
+
type: ViewChild,
|
|
12102
|
+
args: ['trigger']
|
|
12103
|
+
}], rangePicker: [{
|
|
12104
|
+
type: ViewChild,
|
|
12105
|
+
args: ['rangePicker']
|
|
12106
|
+
}] } });
|
|
12107
|
+
|
|
12108
|
+
/*
|
|
12109
|
+
Copyright NetFoundry Inc.
|
|
12110
|
+
|
|
12111
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
12112
|
+
you may not use this file except in compliance with the License.
|
|
12113
|
+
You may obtain a copy of the License at
|
|
12114
|
+
|
|
12115
|
+
https://www.apache.org/licenses/LICENSE-2.0
|
|
12116
|
+
|
|
12117
|
+
Unless required by applicable law or agreed to in writing, software
|
|
12118
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12119
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12120
|
+
See the License for the specific language governing permissions and
|
|
12121
|
+
limitations under the License.
|
|
12122
|
+
*/
|
|
12123
|
+
/**
|
|
12124
|
+
* Custom, token-skinnable list table. Columns are declared with {@link ListColumn};
|
|
12125
|
+
* sort, filter, and pagination flow through {@link DataTableFilterService}.
|
|
12126
|
+
*/
|
|
12127
|
+
class ListTableComponent {
|
|
12128
|
+
set tableId(id) {
|
|
12129
|
+
this._tableId = id;
|
|
12130
|
+
this.svc.tableId = id;
|
|
12131
|
+
}
|
|
12132
|
+
get tableId() {
|
|
12133
|
+
return this._tableId;
|
|
12134
|
+
}
|
|
12135
|
+
set columns(value) {
|
|
12136
|
+
this._rawColumns = value || [];
|
|
12137
|
+
if (this._initialized) {
|
|
12138
|
+
this.buildColumns();
|
|
12139
|
+
}
|
|
12140
|
+
}
|
|
12141
|
+
constructor(svc, tableFilterService, el, ngZone, sanitizer) {
|
|
12142
|
+
this.svc = svc;
|
|
12143
|
+
this.tableFilterService = tableFilterService;
|
|
12144
|
+
this.el = el;
|
|
12145
|
+
this.ngZone = ngZone;
|
|
12146
|
+
this.sanitizer = sanitizer;
|
|
12147
|
+
this._tableId = '';
|
|
12148
|
+
this.rowData = [];
|
|
12149
|
+
this.isLoading = false;
|
|
12150
|
+
this.options = { noSelect: false, noMenu: false };
|
|
12151
|
+
this.startCount = '-';
|
|
12152
|
+
this.endCount = '-';
|
|
12153
|
+
this.totalCount = '-';
|
|
12154
|
+
this.filterApplied = false;
|
|
12155
|
+
this.menuItems = [];
|
|
12156
|
+
this.headerActions = [];
|
|
12157
|
+
this.noItemsImage = 'nodata';
|
|
12158
|
+
this.showNoItemsAdd = true;
|
|
12159
|
+
this.noItemsReadonly = false;
|
|
12160
|
+
this.accessDenied = false;
|
|
12161
|
+
this.entityTypeName = '';
|
|
12162
|
+
this.filterName = 'name';
|
|
12163
|
+
this.filterColumn = 'name';
|
|
12164
|
+
this.actionRequested = new EventEmitter();
|
|
12165
|
+
this.tableReady = new EventEmitter();
|
|
12166
|
+
/** Full column list (includes hidden), after persisted state. */
|
|
12167
|
+
this.allColumns = [];
|
|
12168
|
+
/** Currently rendered columns (visible + ordered). */
|
|
12169
|
+
this.visibleColumns = [];
|
|
12170
|
+
/** Hidden columns, shaped for the shared hidden-columns bar (`headerName`). */
|
|
12171
|
+
this.hiddenColumns = [];
|
|
12172
|
+
this.entityTypeLabel = '';
|
|
12173
|
+
// ---- action menus ----
|
|
12174
|
+
this.openMenu = false;
|
|
12175
|
+
this.openHeaderMenu = false;
|
|
12176
|
+
this.menuLeft = 0;
|
|
12177
|
+
this.menuTop = 0;
|
|
12178
|
+
this.selectedItem = { actionList: [] };
|
|
12179
|
+
// ---- selection ----
|
|
12180
|
+
this.allToggled = false;
|
|
12181
|
+
// ---- column chooser (skinned show/hide + reorder panel) ----
|
|
12182
|
+
this.showColumnChooser = false;
|
|
12183
|
+
// ---- integrated toolbar: search + filter chips + pagination ----
|
|
12184
|
+
this.searchString = '';
|
|
12185
|
+
this.activeFilters = [];
|
|
12186
|
+
this.filtering = false;
|
|
12187
|
+
this.inputChangedDebounced = _.debounce(() => this.applySearch(), 400);
|
|
12188
|
+
// ---- responsive filter overflow: controls that don't fit collapse into "More" ----
|
|
12189
|
+
this.visibleFilterColumns = [];
|
|
12190
|
+
this.overflowFilterColumns = [];
|
|
12191
|
+
this.showFilterOverflow = false;
|
|
12192
|
+
this.filterWidths = new Map();
|
|
12193
|
+
this.filtersDirty = true;
|
|
12194
|
+
this.overflowRaf = 0;
|
|
12195
|
+
this.FILTER_GAP = 8; // px, matches the .lt-filter-zone/.lt-filters gap
|
|
12196
|
+
this.MORE_BTN_W = 92; // px reserved for the "More" trigger when overflowing
|
|
12197
|
+
this.onPointerMove = (e) => this.handleResizeMove(e);
|
|
12198
|
+
this.onPointerUp = () => this.handleResizeEnd();
|
|
12199
|
+
this._rawColumns = [];
|
|
12200
|
+
this._initialColumns = [];
|
|
12201
|
+
this._initialized = false;
|
|
12202
|
+
this.subscription = new Subscription();
|
|
12203
|
+
this.trackRow = (index, row) => row?.id ?? row?.name ?? index;
|
|
12204
|
+
this.trackCol = (index, col) => col.key;
|
|
12205
|
+
// ---------------------------------------------------------------- hover tooltip
|
|
12206
|
+
this.hoverTooltip = null;
|
|
12207
|
+
// ---------------------------------------------------------------- selection
|
|
12208
|
+
this.toggleItem = (item) => {
|
|
12209
|
+
item.selected = !item.selected;
|
|
12210
|
+
this.allToggled = !_.isEmpty(this.rowData) && _.every(this.rowData, { selected: true });
|
|
12211
|
+
this.actionRequested.emit({ action: 'toggleItem', item });
|
|
12212
|
+
};
|
|
12213
|
+
// ---------------------------------------------------------------- action menus
|
|
12214
|
+
this.openActionMenu = (event, item) => {
|
|
12215
|
+
this.selectedItem = item;
|
|
12216
|
+
this.openMenu = true;
|
|
12217
|
+
_.delay(() => {
|
|
12218
|
+
const height = this.contextMenu?.nativeElement?.offsetHeight || 120;
|
|
12219
|
+
const windowOffset = window.innerHeight - event.clientY;
|
|
12220
|
+
const menuOffset = windowOffset <= height ? height - windowOffset : 0;
|
|
12221
|
+
this.menuLeft = event.clientX - 100;
|
|
12222
|
+
this.menuTop = event.clientY + 5 - menuOffset;
|
|
12223
|
+
}, 10);
|
|
12224
|
+
};
|
|
12225
|
+
this.closeActionMenu = () => {
|
|
12226
|
+
this.selectedItem = { actionList: [] };
|
|
12227
|
+
this.openMenu = false;
|
|
12228
|
+
this.openHeaderMenu = false;
|
|
12229
|
+
};
|
|
12230
|
+
this.closeHeaderActionMenu = () => {
|
|
12231
|
+
this.openHeaderMenu = false;
|
|
12232
|
+
};
|
|
12233
|
+
/** Restore-default (rotate) icon for the built-in header action. */
|
|
12234
|
+
this.RESET_TABLE_ICON = '<svg viewBox="0 0 16 16" width="15" height="15"><path fill="none" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" d="M3.2 8a4.8 4.8 0 1 0 1.3-3.3"/><path fill="currentColor" d="M2.8 2.8l-.3 2.9 2.8-.4z"/></svg>';
|
|
12235
|
+
// ---- rows-per-page + numbered pagination (the skin footer) ----
|
|
12236
|
+
this.pageSizeOptions = [25, 50, 100];
|
|
12237
|
+
// ---- auto-refresh (the skin toolbar toggle) ----
|
|
12238
|
+
this.autoRefresh = false;
|
|
12239
|
+
this.autoRefreshIntervalMs = 30000;
|
|
12240
|
+
// ---- row action icons ----
|
|
12241
|
+
// A menu item's host-supplied `icon` (SVG markup) is rendered before its label in
|
|
12242
|
+
// the row's kebab menu. Sanitized once per unique SVG and cached.
|
|
12243
|
+
this.iconCache = new Map();
|
|
12244
|
+
}
|
|
12245
|
+
ngOnInit() {
|
|
12246
|
+
this._initialized = true;
|
|
12247
|
+
this.buildColumns();
|
|
12248
|
+
this.updateEntityTypeLabel();
|
|
12249
|
+
this.subscription.add(this.tableFilterService.filtersChanged.subscribe((filters) => {
|
|
12250
|
+
// chips: every non-hidden filter except the free-text search (shown in the box)
|
|
12251
|
+
this.activeFilters = filters.filter((f) => f.hidden !== true && f.columnId !== this.filterColumn);
|
|
12252
|
+
const match = filters.find((f) => f.columnId === this.filterColumn);
|
|
12253
|
+
this.searchString = match ? match.value : '';
|
|
12254
|
+
// an activated filter changes which controls are pinned and how wide they render
|
|
12255
|
+
this.filtersDirty = true;
|
|
12256
|
+
this.scheduleFilterOverflow();
|
|
12257
|
+
}));
|
|
12258
|
+
this.subscription.add(this.tableFilterService.filtering.subscribe((f) => (this.filtering = f)));
|
|
12259
|
+
// close the column chooser whenever a filter dropdown (or anything else) opens
|
|
12260
|
+
this.subscription.add(this.svc.menuOpened$.subscribe((owner) => {
|
|
12261
|
+
if (owner !== this) {
|
|
12262
|
+
this.showColumnChooser = false;
|
|
12263
|
+
}
|
|
12264
|
+
}));
|
|
12265
|
+
this.tableReady.emit({ component: this });
|
|
12266
|
+
}
|
|
12267
|
+
ngAfterViewInit() {
|
|
12268
|
+
this.ensureFilterResizeObserver();
|
|
12269
|
+
}
|
|
12270
|
+
ngAfterViewChecked() {
|
|
12271
|
+
// the whole table sits behind @if(showTable), so the toolbar may not exist
|
|
12272
|
+
// until row data arrives; (re)attach the observer once/whenever it appears
|
|
12273
|
+
this.ensureFilterResizeObserver();
|
|
12274
|
+
}
|
|
12275
|
+
ngOnDestroy() {
|
|
12276
|
+
this.subscription.unsubscribe();
|
|
12277
|
+
this.detachResizeListeners();
|
|
12278
|
+
this.filterRO?.disconnect();
|
|
12279
|
+
if (this.overflowRaf) {
|
|
12280
|
+
cancelAnimationFrame(this.overflowRaf);
|
|
12281
|
+
}
|
|
12282
|
+
if (this.autoRefreshTimer) {
|
|
12283
|
+
clearInterval(this.autoRefreshTimer);
|
|
12284
|
+
}
|
|
12285
|
+
}
|
|
12286
|
+
get showTable() {
|
|
12287
|
+
return this.isLoading || this.filterApplied || this.rowData?.length > 0;
|
|
12288
|
+
}
|
|
12289
|
+
/** Columns surfaced as inline filter controls in the toolbar. Text filters are
|
|
12290
|
+
* excluded because the free-text search box already covers the primary text field. */
|
|
12291
|
+
get filterableColumns() {
|
|
12292
|
+
return this.allColumns.filter((c) => c.filterType && c.filterType !== 'TEXTINPUT');
|
|
12293
|
+
}
|
|
12294
|
+
// -------------------------------------------------- responsive filter overflow
|
|
12295
|
+
/** A filter column is "active" when it has an applied filter. */
|
|
12296
|
+
isFilterActive(col) {
|
|
12297
|
+
return this.activeFilters.some((f) => f.columnId === col.key);
|
|
12298
|
+
}
|
|
12299
|
+
/** Applied filters that are currently hidden in the overflow dropdown. */
|
|
12300
|
+
get hiddenActiveCount() {
|
|
12301
|
+
return this.overflowFilterColumns.filter((c) => this.isFilterActive(c)).length;
|
|
12302
|
+
}
|
|
12303
|
+
toggleFilterOverflow(event) {
|
|
12304
|
+
event.stopPropagation();
|
|
12305
|
+
this.showFilterOverflow = !this.showFilterOverflow;
|
|
12306
|
+
if (this.showFilterOverflow) {
|
|
12307
|
+
this.svc.notifyMenuOpened(this);
|
|
12308
|
+
}
|
|
12309
|
+
}
|
|
12310
|
+
closeFilterOverflow(event) {
|
|
12311
|
+
// keep open when the click lands in a filter's own Material overlay (e.g. the
|
|
12312
|
+
// date-range calendar), which renders outside this popover's DOM subtree
|
|
12313
|
+
const target = event?.target;
|
|
12314
|
+
if (target?.closest?.('.cdk-overlay-container')) {
|
|
12315
|
+
return;
|
|
12316
|
+
}
|
|
12317
|
+
this.showFilterOverflow = false;
|
|
12318
|
+
}
|
|
12319
|
+
ensureFilterResizeObserver() {
|
|
12320
|
+
// observe the toolbar row (not the content-sized zone) so a window resize,
|
|
12321
|
+
// which changes the row's available width, actually fires the observer
|
|
12322
|
+
const target = this.filterZone?.nativeElement?.parentElement ?? undefined;
|
|
12323
|
+
if (!target || typeof ResizeObserver === 'undefined' || target === this.observedToolbar) {
|
|
12324
|
+
return;
|
|
12325
|
+
}
|
|
12326
|
+
// ResizeObserver fires outside Angular's zone; re-enter so the recomputed
|
|
12327
|
+
// visible/overflow split actually triggers change detection.
|
|
12328
|
+
if (!this.filterRO) {
|
|
12329
|
+
this.filterRO = new ResizeObserver(() => this.ngZone.run(() => this.scheduleFilterOverflow()));
|
|
12330
|
+
}
|
|
12331
|
+
else {
|
|
12332
|
+
this.filterRO.disconnect();
|
|
12333
|
+
}
|
|
12334
|
+
this.filterRO.observe(target);
|
|
12335
|
+
this.observedToolbar = target;
|
|
12336
|
+
// compute the split for the newly-observed row (deferred, so it never mutates
|
|
12337
|
+
// bound state inside the change-detection pass that called us)
|
|
12338
|
+
this.scheduleFilterOverflow();
|
|
12339
|
+
}
|
|
12340
|
+
scheduleFilterOverflow() {
|
|
12341
|
+
if (this.overflowRaf) {
|
|
12342
|
+
cancelAnimationFrame(this.overflowRaf);
|
|
12343
|
+
}
|
|
12344
|
+
this.overflowRaf = requestAnimationFrame(() => {
|
|
12345
|
+
this.overflowRaf = 0;
|
|
12346
|
+
this.ensureFilterResizeObserver();
|
|
12347
|
+
this.recomputeFilterOverflow();
|
|
12348
|
+
});
|
|
12349
|
+
}
|
|
12350
|
+
recomputeFilterOverflow() {
|
|
12351
|
+
const cols = this.filterableColumns;
|
|
12352
|
+
if (!cols.length) {
|
|
12353
|
+
this.visibleFilterColumns = [];
|
|
12354
|
+
this.overflowFilterColumns = [];
|
|
12355
|
+
return;
|
|
12356
|
+
}
|
|
12357
|
+
const needMeasure = this.filtersDirty || cols.some((c) => !this.filterWidths.has(c.key));
|
|
12358
|
+
if (!needMeasure) {
|
|
12359
|
+
this.applyFilterSplit();
|
|
12360
|
+
return;
|
|
12361
|
+
}
|
|
12362
|
+
// render every control inline for one frame so each can be measured, then collapse
|
|
12363
|
+
this.visibleFilterColumns = cols;
|
|
12364
|
+
this.overflowFilterColumns = [];
|
|
12365
|
+
requestAnimationFrame(() => {
|
|
12366
|
+
const row = this.filtersRow?.nativeElement;
|
|
12367
|
+
if (row) {
|
|
12368
|
+
row.querySelectorAll('[data-filter-key]').forEach((el) => {
|
|
12369
|
+
const key = el.getAttribute('data-filter-key');
|
|
12370
|
+
if (key) {
|
|
12371
|
+
this.filterWidths.set(key, Math.ceil(el.getBoundingClientRect().width));
|
|
12372
|
+
}
|
|
12373
|
+
});
|
|
12374
|
+
}
|
|
12375
|
+
this.filtersDirty = false;
|
|
12376
|
+
this.applyFilterSplit();
|
|
12377
|
+
});
|
|
12378
|
+
}
|
|
12379
|
+
/** Split filter controls into inline vs. overflow, pinning active filters and
|
|
12380
|
+
* dropping inactive (then, only if forced, active) from the right until they fit. */
|
|
12381
|
+
applyFilterSplit() {
|
|
12382
|
+
const zone = this.filterZone?.nativeElement;
|
|
12383
|
+
const cols = this.filterableColumns;
|
|
12384
|
+
if (!zone || !cols.length) {
|
|
12385
|
+
return;
|
|
12386
|
+
}
|
|
12387
|
+
// budget = the toolbar row's width minus its fixed siblings (search / divider /
|
|
12388
|
+
// Columns) and the gaps between them, i.e. the space filters may take before
|
|
12389
|
+
// they'd push the Columns control off the row
|
|
12390
|
+
const toolbarLeft = zone.parentElement;
|
|
12391
|
+
const siblingWidth = (sel) => {
|
|
12392
|
+
const node = toolbarLeft?.querySelector(sel);
|
|
12393
|
+
return node ? Math.ceil(node.getBoundingClientRect().width) : 0;
|
|
12394
|
+
};
|
|
12395
|
+
const available = (toolbarLeft?.clientWidth ?? zone.clientWidth) -
|
|
12396
|
+
siblingWidth('.lt-search') -
|
|
12397
|
+
siblingWidth('.lt-divider') -
|
|
12398
|
+
siblingWidth('.lt-columns') -
|
|
12399
|
+
3 * this.FILTER_GAP;
|
|
12400
|
+
const widthOf = (c) => this.filterWidths.get(c.key) ?? 0;
|
|
12401
|
+
const totalOf = (list, withMore) => {
|
|
12402
|
+
let w = 0;
|
|
12403
|
+
list.forEach((c, i) => (w += widthOf(c) + (i > 0 ? this.FILTER_GAP : 0)));
|
|
12404
|
+
if (withMore) {
|
|
12405
|
+
w += (list.length ? this.FILTER_GAP : 0) + this.MORE_BTN_W;
|
|
12406
|
+
}
|
|
12407
|
+
return w;
|
|
12408
|
+
};
|
|
12409
|
+
if (totalOf(cols, false) <= available) {
|
|
12410
|
+
this.visibleFilterColumns = cols;
|
|
12411
|
+
this.overflowFilterColumns = [];
|
|
12412
|
+
return;
|
|
12413
|
+
}
|
|
12414
|
+
// removal priority: inactive right-to-left first, then active right-to-left
|
|
12415
|
+
const inactive = cols.filter((c) => !this.isFilterActive(c)).reverse();
|
|
12416
|
+
const active = cols.filter((c) => this.isFilterActive(c)).reverse();
|
|
12417
|
+
const removalOrder = [...inactive, ...active];
|
|
12418
|
+
const overflow = new Set();
|
|
12419
|
+
for (const c of removalOrder) {
|
|
12420
|
+
overflow.add(c.key);
|
|
12421
|
+
const visible = cols.filter((x) => !overflow.has(x.key));
|
|
12422
|
+
if (totalOf(visible, true) <= available) {
|
|
12423
|
+
break;
|
|
12424
|
+
}
|
|
12425
|
+
}
|
|
12426
|
+
this.visibleFilterColumns = cols.filter((c) => !overflow.has(c.key));
|
|
12427
|
+
this.overflowFilterColumns = cols.filter((c) => overflow.has(c.key));
|
|
12428
|
+
if (!this.overflowFilterColumns.length) {
|
|
12429
|
+
this.showFilterOverflow = false;
|
|
12430
|
+
}
|
|
12431
|
+
}
|
|
12432
|
+
// ---------------------------------------------------------------- columns
|
|
12433
|
+
buildColumns() {
|
|
12434
|
+
this._initialColumns = this._rawColumns.map((c) => ({ ...c }));
|
|
12435
|
+
this.allColumns = this.svc.applyPersistedState(this._rawColumns);
|
|
12436
|
+
this.updateVisible();
|
|
12437
|
+
// start with every filter control visible; the overflow pass collapses to fit
|
|
12438
|
+
this.visibleFilterColumns = this.filterableColumns;
|
|
12439
|
+
this.overflowFilterColumns = [];
|
|
12440
|
+
this.filtersDirty = true;
|
|
12441
|
+
this.scheduleFilterOverflow();
|
|
12442
|
+
}
|
|
12443
|
+
updateVisible() {
|
|
12444
|
+
this.visibleColumns = this.allColumns.filter((c) => !c.hidden);
|
|
12445
|
+
this.hiddenColumns = this.allColumns
|
|
12446
|
+
.filter((c) => c.hidden)
|
|
12447
|
+
.map((c) => ({ ...c, headerName: c.label }));
|
|
12448
|
+
}
|
|
12449
|
+
colWidth(col) {
|
|
12450
|
+
return col.width != null ? `${col.width}px` : null;
|
|
12451
|
+
}
|
|
12452
|
+
// ---------------------------------------------------------------- cells
|
|
12453
|
+
rawValue(col, row) {
|
|
12454
|
+
return _.get(row, col.field || col.key);
|
|
12455
|
+
}
|
|
12456
|
+
cellContext(col, row, i) {
|
|
12457
|
+
return {
|
|
12458
|
+
row,
|
|
12459
|
+
value: this.rawValue(col, row),
|
|
12460
|
+
colId: col.key,
|
|
12461
|
+
rowIndex: i,
|
|
12462
|
+
emitAction: (action, item = row) => this.actionRequested.emit({ action, item }),
|
|
12463
|
+
};
|
|
12464
|
+
}
|
|
12465
|
+
legacyShim(col, row, i) {
|
|
12466
|
+
return { data: row, value: this.rawValue(col, row), column: { colId: col.key }, rowIndex: i };
|
|
12467
|
+
}
|
|
12468
|
+
renderMode(col) {
|
|
12469
|
+
if (col.cell) {
|
|
12470
|
+
return 'component';
|
|
12471
|
+
}
|
|
12472
|
+
if (col.cellHtml) {
|
|
12473
|
+
return 'html';
|
|
12474
|
+
}
|
|
12475
|
+
return 'text';
|
|
12476
|
+
}
|
|
12477
|
+
cellInputs(col, row, i) {
|
|
12478
|
+
if (col.cellInputs) {
|
|
12479
|
+
return col.cellInputs(this.cellContext(col, row, i));
|
|
12480
|
+
}
|
|
12481
|
+
return { row, value: this.rawValue(col, row) };
|
|
12482
|
+
}
|
|
12483
|
+
cellHtml(col, row, i) {
|
|
12484
|
+
return col.cellHtml ? col.cellHtml(this.legacyShim(col, row, i)) : '';
|
|
12485
|
+
}
|
|
12486
|
+
cellText(col, row, i) {
|
|
12487
|
+
if (col.valueFormatter) {
|
|
12488
|
+
return col.valueFormatter(this.legacyShim(col, row, i));
|
|
12489
|
+
}
|
|
12490
|
+
return this.rawValue(col, row);
|
|
12491
|
+
}
|
|
12492
|
+
cellClass(col) {
|
|
12493
|
+
return col.cellClass || '';
|
|
12494
|
+
}
|
|
12495
|
+
onCellClick(col, row, i) {
|
|
12496
|
+
col.onCellClick?.(this.cellContext(col, row, i));
|
|
12497
|
+
}
|
|
12498
|
+
tooltipInputs(col, row, i) {
|
|
12499
|
+
return col.tooltipInputs ? col.tooltipInputs(this.cellContext(col, row, i)) : { row };
|
|
12500
|
+
}
|
|
12501
|
+
onCellHover(col, row, i, event) {
|
|
12502
|
+
if (!col.tooltip) {
|
|
12503
|
+
return;
|
|
12504
|
+
}
|
|
12505
|
+
const rect = event.currentTarget.getBoundingClientRect();
|
|
12506
|
+
this.hoverTooltip = {
|
|
12507
|
+
component: col.tooltip,
|
|
12508
|
+
inputs: this.tooltipInputs(col, row, i),
|
|
12509
|
+
left: rect.left,
|
|
12510
|
+
top: rect.bottom + 6,
|
|
12511
|
+
};
|
|
12512
|
+
}
|
|
12513
|
+
onCellLeave() {
|
|
12514
|
+
this.hoverTooltip = null;
|
|
12515
|
+
}
|
|
12516
|
+
/** Some rows selected but the header select-all isn't fully on (indeterminate). */
|
|
12517
|
+
get someSelected() {
|
|
12518
|
+
return !this.allToggled && _.some(this.rowData, { selected: true });
|
|
12519
|
+
}
|
|
12520
|
+
toggleAll() {
|
|
12521
|
+
const newState = !this.allToggled;
|
|
12522
|
+
this.allToggled = newState;
|
|
12523
|
+
_.forEach(this.rowData, (row) => {
|
|
12524
|
+
row.selected = newState;
|
|
12525
|
+
});
|
|
12526
|
+
this.actionRequested.emit({ action: 'toggleAll' });
|
|
12527
|
+
}
|
|
12528
|
+
// ---------------------------------------------------------------- sort
|
|
12529
|
+
onHeaderSort(event) {
|
|
12530
|
+
this.allColumns.forEach((c) => {
|
|
12531
|
+
c.sortDir = c.key === event.colId ? event.dir : undefined;
|
|
12532
|
+
});
|
|
12533
|
+
}
|
|
12534
|
+
// ---------------------------------------------------------------- hide / show / reorder
|
|
12535
|
+
hideColumn(col) {
|
|
12536
|
+
const target = this.allColumns.find((c) => c.key === col.key);
|
|
12537
|
+
if (!target) {
|
|
12538
|
+
return;
|
|
12539
|
+
}
|
|
12540
|
+
target.hidden = true;
|
|
12541
|
+
this.updateVisible();
|
|
12542
|
+
this.svc.saveState(this.allColumns);
|
|
12543
|
+
}
|
|
12544
|
+
showColumn(event) {
|
|
12545
|
+
const target = this.allColumns.find((c) => c.key === event.column?.key);
|
|
12546
|
+
if (!target) {
|
|
12547
|
+
return;
|
|
12548
|
+
}
|
|
12549
|
+
target.hidden = false;
|
|
12550
|
+
this.updateVisible();
|
|
12551
|
+
this.svc.saveState(this.allColumns);
|
|
12552
|
+
}
|
|
12553
|
+
onReorder(event) {
|
|
12554
|
+
if (event.previousIndex === event.currentIndex) {
|
|
12555
|
+
return;
|
|
12556
|
+
}
|
|
12557
|
+
moveItemInArray(this.visibleColumns, event.previousIndex, event.currentIndex);
|
|
12558
|
+
const nextVisible = [...this.visibleColumns];
|
|
12559
|
+
let vi = 0;
|
|
12560
|
+
this.allColumns = this.allColumns.map((c) => (c.hidden ? c : nextVisible[vi++]));
|
|
12561
|
+
this.updateVisible();
|
|
12562
|
+
this.svc.saveState(this.allColumns);
|
|
12563
|
+
}
|
|
12564
|
+
resetTableColumns() {
|
|
12565
|
+
this.svc.reset();
|
|
12566
|
+
this.allColumns = this._initialColumns.map((c) => ({ ...c }));
|
|
12567
|
+
this.updateVisible();
|
|
12568
|
+
this.closeActionMenu();
|
|
12569
|
+
}
|
|
12570
|
+
// ---------------------------------------------------------------- column chooser
|
|
12571
|
+
/** Count of columns the user has hidden beyond the defaults (drives the badge).
|
|
12572
|
+
* Columns hidden by default don't count, so a fresh/default load shows no badge. */
|
|
12573
|
+
get hiddenColumnCount() {
|
|
12574
|
+
return this.allColumns.filter((c) => c.hidden && !this.isDefaultHidden(c)).length;
|
|
12575
|
+
}
|
|
12576
|
+
/** True when visibility + order match the originally declared columns. */
|
|
12577
|
+
get columnsAtDefault() {
|
|
12578
|
+
if (this.allColumns.length !== this._initialColumns.length) {
|
|
12579
|
+
return false;
|
|
12580
|
+
}
|
|
12581
|
+
return this.allColumns.every((c, i) => {
|
|
12582
|
+
const init = this._initialColumns[i];
|
|
12583
|
+
return init && init.key === c.key && !!init.hidden === !!c.hidden;
|
|
12584
|
+
});
|
|
12585
|
+
}
|
|
12586
|
+
/** True when the column is hidden in the originally declared defaults. */
|
|
12587
|
+
isDefaultHidden(col) {
|
|
12588
|
+
const init = this._initialColumns.find((c) => c.key === col.key);
|
|
12589
|
+
return !!init?.hidden;
|
|
12590
|
+
}
|
|
12591
|
+
toggleColumnChooser(event) {
|
|
12592
|
+
event.stopPropagation();
|
|
12593
|
+
this.showColumnChooser = !this.showColumnChooser;
|
|
12594
|
+
if (this.showColumnChooser) {
|
|
12595
|
+
this.svc.notifyMenuOpened(this);
|
|
12596
|
+
}
|
|
12597
|
+
}
|
|
12598
|
+
closeColumnChooser() {
|
|
12599
|
+
this.showColumnChooser = false;
|
|
12600
|
+
}
|
|
12601
|
+
toggleColumnVisible(col) {
|
|
12602
|
+
if (col.hideable === false) {
|
|
12603
|
+
return;
|
|
12604
|
+
}
|
|
12605
|
+
const target = this.allColumns.find((c) => c.key === col.key);
|
|
12606
|
+
if (!target) {
|
|
12607
|
+
return;
|
|
12608
|
+
}
|
|
12609
|
+
target.hidden = !target.hidden;
|
|
12610
|
+
this.updateVisible();
|
|
12611
|
+
this.svc.saveState(this.allColumns);
|
|
12612
|
+
}
|
|
12613
|
+
chooserReorder(event) {
|
|
12614
|
+
if (event.previousIndex === event.currentIndex) {
|
|
12615
|
+
return;
|
|
12616
|
+
}
|
|
12617
|
+
moveItemInArray(this.allColumns, event.previousIndex, event.currentIndex);
|
|
12618
|
+
this.updateVisible();
|
|
12619
|
+
this.svc.saveState(this.allColumns);
|
|
12620
|
+
}
|
|
12621
|
+
// ---------------------------------------------------------------- resize
|
|
12622
|
+
startResize(col, event) {
|
|
12623
|
+
if (col.resizable === false) {
|
|
12624
|
+
return;
|
|
12625
|
+
}
|
|
12626
|
+
event.preventDefault();
|
|
12627
|
+
event.stopPropagation();
|
|
12628
|
+
const th = event.target?.closest('th');
|
|
12629
|
+
const startWidth = col.width ?? th?.offsetWidth ?? 150;
|
|
12630
|
+
this.resizing = { col, startX: event.clientX, startWidth };
|
|
12631
|
+
document.addEventListener('pointermove', this.onPointerMove);
|
|
12632
|
+
document.addEventListener('pointerup', this.onPointerUp);
|
|
12633
|
+
}
|
|
12634
|
+
handleResizeMove(event) {
|
|
12635
|
+
if (!this.resizing) {
|
|
12636
|
+
return;
|
|
12637
|
+
}
|
|
12638
|
+
const delta = event.clientX - this.resizing.startX;
|
|
12639
|
+
const min = this.resizing.col.minWidth ?? 40;
|
|
12640
|
+
this.resizing.col.width = Math.max(min, Math.round(this.resizing.startWidth + delta));
|
|
12641
|
+
}
|
|
12642
|
+
handleResizeEnd() {
|
|
12643
|
+
if (this.resizing) {
|
|
12644
|
+
this.svc.saveWidth(this.resizing.col.key, this.resizing.col.width, this.allColumns);
|
|
12645
|
+
}
|
|
12646
|
+
this.resizing = undefined;
|
|
12647
|
+
this.detachResizeListeners();
|
|
12648
|
+
}
|
|
12649
|
+
detachResizeListeners() {
|
|
12650
|
+
document.removeEventListener('pointermove', this.onPointerMove);
|
|
12651
|
+
document.removeEventListener('pointerup', this.onPointerUp);
|
|
12652
|
+
}
|
|
12653
|
+
openHeaderActionMenu(event) {
|
|
12654
|
+
this.menuLeft = event.clientX - 150;
|
|
12655
|
+
this.menuTop = event.clientY + 5;
|
|
12656
|
+
_.delay(() => {
|
|
12657
|
+
this.openHeaderMenu = true;
|
|
12658
|
+
}, 100);
|
|
12659
|
+
}
|
|
12660
|
+
hideMenuItem(menuItem, selectedItem) {
|
|
12661
|
+
if (_.isFunction(menuItem.hidden)) {
|
|
12662
|
+
return menuItem.hidden(selectedItem);
|
|
12663
|
+
}
|
|
12664
|
+
else if (_.isBoolean(menuItem.hidden)) {
|
|
12665
|
+
return menuItem.hidden;
|
|
12666
|
+
}
|
|
12667
|
+
return !selectedItem.actionList?.includes(menuItem.action);
|
|
12668
|
+
}
|
|
12669
|
+
/** Header action menu rows: the reset action followed by host-provided header actions.
|
|
12670
|
+
* A host action's `icon` (SVG markup) is rendered before its label, like the row menu. */
|
|
12671
|
+
get headerMenuItems() {
|
|
12672
|
+
const items = [
|
|
12673
|
+
{ label: 'Restore Default Table', action: '__reset_table__', id: 'ResetTableButton', iconSvg: this.safeIcon(this.RESET_TABLE_ICON) },
|
|
12674
|
+
];
|
|
12675
|
+
_.forEach(this.headerActions, (ha) => {
|
|
12676
|
+
items.push({
|
|
12677
|
+
label: ha.name || ha.label,
|
|
12678
|
+
action: ha.action,
|
|
12679
|
+
id: 'HeaderAction_' + ha.action,
|
|
12680
|
+
iconSvg: ha.icon ? this.safeIcon(ha.icon) : undefined,
|
|
12681
|
+
data: ha,
|
|
12682
|
+
});
|
|
12683
|
+
});
|
|
12684
|
+
return items;
|
|
12685
|
+
}
|
|
12686
|
+
onHeaderMenuClick(evt) {
|
|
12687
|
+
if (evt.item.action === '__reset_table__') {
|
|
12688
|
+
this.resetTableColumns();
|
|
12689
|
+
}
|
|
12690
|
+
else {
|
|
12691
|
+
this.actionRequested.emit({ action: evt.item.action });
|
|
12692
|
+
}
|
|
12693
|
+
this.closeHeaderActionMenu();
|
|
12694
|
+
}
|
|
12695
|
+
/** Row action menu rows, built from `menuItems` against the currently selected row.
|
|
12696
|
+
* A menu item's `icon` (SVG markup) is rendered before its label in the menu. */
|
|
12697
|
+
get rowMenuItems() {
|
|
12698
|
+
return _.map(this.menuItems, (mi) => ({
|
|
12699
|
+
label: mi.name || mi.label,
|
|
12700
|
+
action: mi.action,
|
|
12701
|
+
id: 'TableActionButton_' + mi.action,
|
|
12702
|
+
hidden: this.hideMenuItem(mi, this.selectedItem),
|
|
12703
|
+
iconSvg: mi.icon ? this.safeIcon(mi.icon) : undefined,
|
|
12704
|
+
data: mi,
|
|
12705
|
+
}));
|
|
12706
|
+
}
|
|
12707
|
+
onRowMenuClick(evt) {
|
|
12708
|
+
this.actionRequested.emit({ action: evt.item.action, item: this.selectedItem });
|
|
12709
|
+
this.closeActionMenu();
|
|
12710
|
+
}
|
|
12711
|
+
openCreate() {
|
|
12712
|
+
this.actionRequested.emit({ action: 'create' });
|
|
12713
|
+
}
|
|
12714
|
+
// ---------------------------------------------------------------- toolbar search + pagination
|
|
12715
|
+
onSearchInput() {
|
|
12716
|
+
this.inputChangedDebounced();
|
|
12717
|
+
}
|
|
12718
|
+
applySearch() {
|
|
12719
|
+
this.tableFilterService.currentPage = 1;
|
|
12720
|
+
this.tableFilterService.updateFilter({
|
|
12721
|
+
filterName: this.filterName,
|
|
12722
|
+
columnId: this.filterColumn,
|
|
12723
|
+
value: this.searchString,
|
|
12724
|
+
label: this.searchString,
|
|
12725
|
+
});
|
|
12726
|
+
}
|
|
12727
|
+
nextPage() {
|
|
12728
|
+
if (this.nextDisabled) {
|
|
12729
|
+
return;
|
|
12730
|
+
}
|
|
12731
|
+
this.tableFilterService.changePage((this.tableFilterService.currentPage || 1) + 1);
|
|
12732
|
+
}
|
|
12733
|
+
prevPage() {
|
|
12734
|
+
if (this.prevDisabled) {
|
|
12735
|
+
return;
|
|
12736
|
+
}
|
|
12737
|
+
this.tableFilterService.changePage((this.tableFilterService.currentPage || 1) - 1);
|
|
12738
|
+
}
|
|
12739
|
+
get nextDisabled() {
|
|
12740
|
+
if (!_.isNumber(this.totalCount) || !_.isNumber(this.endCount) || this.filtering) {
|
|
12741
|
+
return true;
|
|
12742
|
+
}
|
|
12743
|
+
return Number(this.endCount) >= Number(this.totalCount);
|
|
12744
|
+
}
|
|
12745
|
+
get prevDisabled() {
|
|
12746
|
+
if (!_.isNumber(this.startCount) || !_.isNumber(this.totalCount) || this.filtering) {
|
|
12747
|
+
return true;
|
|
12748
|
+
}
|
|
12749
|
+
return Number(this.startCount) <= 1;
|
|
12750
|
+
}
|
|
12751
|
+
get pageSize() {
|
|
12752
|
+
return this.tableFilterService.pageSize;
|
|
12753
|
+
}
|
|
12754
|
+
get currentPageNum() {
|
|
12755
|
+
return this.tableFilterService.currentPage || 1;
|
|
12756
|
+
}
|
|
12757
|
+
get totalPages() {
|
|
12758
|
+
if (!_.isNumber(this.totalCount) || Number(this.totalCount) <= 0) {
|
|
12759
|
+
return 1;
|
|
12760
|
+
}
|
|
12761
|
+
return Math.max(1, Math.ceil(Number(this.totalCount) / this.pageSize));
|
|
12762
|
+
}
|
|
12763
|
+
/** Page numbers to render, with `0` marking a gap ("..."). Shows the first and
|
|
12764
|
+
* last page plus a window around the current page so long lists stay compact. */
|
|
12765
|
+
get pageWindow() {
|
|
12766
|
+
const total = this.totalPages;
|
|
12767
|
+
const cur = this.currentPageNum;
|
|
12768
|
+
if (total <= 7) {
|
|
12769
|
+
return Array.from({ length: total }, (_v, i) => i + 1);
|
|
12770
|
+
}
|
|
12771
|
+
const pages = new Set([1, total, cur, cur - 1, cur + 1]);
|
|
12772
|
+
const sorted = [...pages].filter((p) => p >= 1 && p <= total).sort((a, b) => a - b);
|
|
12773
|
+
const out = [];
|
|
12774
|
+
let prev = 0;
|
|
12775
|
+
for (const p of sorted) {
|
|
12776
|
+
if (prev && p - prev > 1) {
|
|
12777
|
+
out.push(0); // gap sentinel
|
|
12778
|
+
}
|
|
12779
|
+
out.push(p);
|
|
12780
|
+
prev = p;
|
|
12781
|
+
}
|
|
12782
|
+
return out;
|
|
12783
|
+
}
|
|
12784
|
+
goToPage(page) {
|
|
12785
|
+
if (page < 1 || page > this.totalPages || page === this.currentPageNum || this.filtering) {
|
|
12786
|
+
return;
|
|
12787
|
+
}
|
|
12788
|
+
this.tableFilterService.changePage(page);
|
|
12789
|
+
}
|
|
12790
|
+
onPageSizeChange(size) {
|
|
12791
|
+
if (!size || size === this.pageSize) {
|
|
12792
|
+
return;
|
|
12793
|
+
}
|
|
12794
|
+
this.tableFilterService.changePageSize(size);
|
|
12795
|
+
}
|
|
12796
|
+
/** Re-fetch the current page (re-emits the page so the host reloads its data). */
|
|
12797
|
+
refresh() {
|
|
12798
|
+
if (this.filtering) {
|
|
12799
|
+
return;
|
|
12800
|
+
}
|
|
12801
|
+
this.tableFilterService.changePage(this.currentPageNum);
|
|
12802
|
+
}
|
|
12803
|
+
toggleAutoRefresh() {
|
|
12804
|
+
this.autoRefresh = !this.autoRefresh;
|
|
12805
|
+
if (this.autoRefresh) {
|
|
12806
|
+
this.autoRefreshTimer = setInterval(() => this.ngZone.run(() => this.refresh()), this.autoRefreshIntervalMs);
|
|
12807
|
+
}
|
|
12808
|
+
else if (this.autoRefreshTimer) {
|
|
12809
|
+
clearInterval(this.autoRefreshTimer);
|
|
12810
|
+
this.autoRefreshTimer = undefined;
|
|
12811
|
+
}
|
|
12812
|
+
}
|
|
12813
|
+
safeIcon(svg) {
|
|
12814
|
+
let safe = this.iconCache.get(svg);
|
|
12815
|
+
if (!safe) {
|
|
12816
|
+
safe = this.sanitizer.bypassSecurityTrustHtml(svg);
|
|
12817
|
+
this.iconCache.set(svg, safe);
|
|
12818
|
+
}
|
|
12819
|
+
return safe;
|
|
12820
|
+
}
|
|
12821
|
+
updateEntityTypeLabel() {
|
|
12822
|
+
const map = {
|
|
12823
|
+
'api-sessions': 'API Sessions',
|
|
12824
|
+
'auth-policies': 'Auth Policies',
|
|
12825
|
+
'certificate-authorities': 'Certificate Authorities',
|
|
12826
|
+
'ext-jwt-signers': 'External JWT Signers',
|
|
12827
|
+
identities: 'Identities',
|
|
12828
|
+
'posture-checks': 'Posture Checks',
|
|
12829
|
+
'edge-routers': 'Edge Routers',
|
|
12830
|
+
services: 'Services',
|
|
12831
|
+
configurations: 'Configs',
|
|
12832
|
+
'config-types': 'Config Types',
|
|
12833
|
+
'service-policies': 'Service Policies',
|
|
12834
|
+
'edge-router-policies': 'Edge Router Policies',
|
|
12835
|
+
'service-edge-router-policies': 'Service Edge Router Policies',
|
|
12836
|
+
sessions: 'Sessions',
|
|
12837
|
+
terminators: 'Terminators',
|
|
12838
|
+
'transit-routers': 'Transit Routers',
|
|
12839
|
+
};
|
|
12840
|
+
this.entityTypeLabel = map[this._tableId] || this.entityTypeName || '';
|
|
12841
|
+
}
|
|
12842
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ListTableComponent, deps: [{ token: ListTableService }, { token: DataTableFilterService }, { token: i0.ElementRef }, { token: i0.NgZone }, { token: i1.DomSanitizer }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
12843
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: ListTableComponent, isStandalone: false, selector: "lib-list-table", inputs: { tableId: "tableId", columns: "columns", rowData: "rowData", isLoading: "isLoading", options: "options", startCount: "startCount", endCount: "endCount", totalCount: "totalCount", filterApplied: "filterApplied", menuItems: "menuItems", headerActions: "headerActions", noItemsImage: "noItemsImage", showNoItemsAdd: "showNoItemsAdd", noItemsReadonly: "noItemsReadonly", accessDenied: "accessDenied", entityTypeName: "entityTypeName", filterName: "filterName", filterColumn: "filterColumn" }, outputs: { actionRequested: "actionRequested", tableReady: "tableReady" }, providers: [ListTableService], viewQueries: [{ propertyName: "contextMenu", first: true, predicate: ["contextMenu"], descendants: true }, { propertyName: "filterZone", first: true, predicate: ["filterZone"], descendants: true }, { propertyName: "filtersRow", first: true, predicate: ["filtersRow"], descendants: true }], ngImport: i0, template: "@if (showTable && !accessDenied) {\n <div class=\"lt-container\">\n <div class=\"lt-toolbar\">\n <div class=\"lt-toolbar-left\">\n <div class=\"lt-search\">\n <svg class=\"lt-search-icon\" viewBox=\"0 0 16 16\" width=\"14\" height=\"14\" aria-hidden=\"true\">\n <circle cx=\"7\" cy=\"7\" r=\"4.3\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"/>\n <line x1=\"10.4\" y1=\"10.4\" x2=\"14.6\" y2=\"14.6\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\"/>\n </svg>\n <input\n class=\"lt-search-input\"\n id=\"SearchFilter\"\n type=\"text\"\n [placeholder]=\"'Search ' + (entityTypeLabel || 'items')\"\n [(ngModel)]=\"searchString\"\n (keyup)=\"onSearchInput()\"\n />\n </div>\n @if (filterableColumns.length) {\n <div class=\"lt-filter-zone\" #filterZone>\n <div class=\"lt-filters\" #filtersRow>\n @for (col of visibleFilterColumns; track col.key) {\n <span class=\"lt-filter-item\" [attr.data-filter-key]=\"col.key\">\n <lib-list-filter-cell [column]=\"col\"></lib-list-filter-cell>\n </span>\n }\n </div>\n @if (overflowFilterColumns.length) {\n <div class=\"lt-filter-more\">\n <button\n type=\"button\"\n class=\"lt-filter-more-btn\"\n [class.is-on]=\"hiddenActiveCount > 0\"\n [class.is-open]=\"showFilterOverflow\"\n (click)=\"toggleFilterOverflow($event)\"\n aria-haspopup=\"true\"\n [attr.aria-expanded]=\"showFilterOverflow\"\n [attr.aria-label]=\"overflowFilterColumns.length + ' more filters'\"\n id=\"FilterOverflowButton\"\n >\n <span class=\"lt-filter-more-icon\" aria-hidden=\"true\">\n <svg viewBox=\"0 0 16 16\" width=\"14\" height=\"14\"><path fill=\"currentColor\" d=\"M2 3h12l-4.6 5.6v4l-2.8 1.4v-5.4z\"/></svg>\n </span>\n <span class=\"lt-filter-more-label\">More</span>\n <span class=\"lt-filter-more-count\">{{ overflowFilterColumns.length }}</span>\n </button>\n @if (showFilterOverflow) {\n <div class=\"lt-filter-overflow\" (clickOutside)=\"closeFilterOverflow($event)\">\n <div class=\"lt-filter-overflow-title\">Filters</div>\n @for (col of overflowFilterColumns; track col.key) {\n <div class=\"lt-filter-overflow-row\" [class.is-active]=\"isFilterActive(col)\">\n <lib-list-filter-cell [column]=\"col\"></lib-list-filter-cell>\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n <div class=\"lt-toolbar-right\">\n <ng-template #refreshIcon>\n <svg viewBox=\"0 0 16 16\" width=\"14\" height=\"14\" aria-hidden=\"true\">\n <path fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" d=\"M13.4 8a5.4 5.4 0 1 1-1.5-3.8\"/>\n <path fill=\"currentColor\" d=\"M13.9 2.1l.5 3.2-3.2-.5z\"/>\n </svg>\n </ng-template>\n <button\n type=\"button\"\n class=\"lt-refresh-btn\"\n (click)=\"refresh()\"\n [disabled]=\"filtering\"\n aria-label=\"Refresh\"\n title=\"Refresh\"\n id=\"RefreshTableButton\"\n >\n <ng-container [ngTemplateOutlet]=\"refreshIcon\"></ng-container>\n </button>\n <button\n type=\"button\"\n class=\"lt-autorefresh-btn\"\n [class.is-on]=\"autoRefresh\"\n (click)=\"toggleAutoRefresh()\"\n [attr.aria-pressed]=\"autoRefresh\"\n id=\"AutoRefreshButton\"\n >\n <ng-container [ngTemplateOutlet]=\"refreshIcon\"></ng-container>\n <span>Auto-refresh: {{ autoRefresh ? 'On' : 'Off' }}</span>\n </button>\n <div class=\"lt-columns\">\n <button\n type=\"button\"\n class=\"lt-columns-btn\"\n [class.is-on]=\"hiddenColumnCount > 0\"\n [class.is-open]=\"showColumnChooser\"\n (click)=\"toggleColumnChooser($event)\"\n aria-haspopup=\"true\"\n [attr.aria-expanded]=\"showColumnChooser\"\n id=\"ColumnChooserButton\"\n >\n <svg viewBox=\"0 0 16 16\" width=\"14\" height=\"14\" aria-hidden=\"true\">\n <path fill=\"currentColor\" d=\"M2 3.5h9v1.4H2zM2 7.3h6v1.4H2zM2 11.1h11v1.4H2z\"/>\n <circle cx=\"13\" cy=\"4.2\" r=\"1.7\" fill=\"currentColor\"/>\n <circle cx=\"10\" cy=\"8\" r=\"1.7\" fill=\"currentColor\"/>\n <circle cx=\"14.4\" cy=\"11.8\" r=\"1.7\" fill=\"currentColor\"/>\n </svg>\n <span>Columns</span>\n @if (hiddenColumnCount > 0) {\n <span class=\"lt-columns-count\">{{ hiddenColumnCount }}</span>\n }\n </button>\n @if (showColumnChooser) {\n <div class=\"lt-column-chooser\" (clickOutside)=\"closeColumnChooser()\">\n <div class=\"lt-chooser-header\">\n <span class=\"lt-chooser-title\">Columns</span>\n <span class=\"lt-chooser-subtitle\">Customize order and visibility</span>\n </div>\n <ul class=\"lt-chooser-list\" cdkDropList cdkDropListLockAxis=\"y\" (cdkDropListDropped)=\"chooserReorder($event)\">\n @for (col of allColumns; track col.key) {\n <li class=\"lt-chooser-row\" cdkDrag cdkDragLockAxis=\"y\" [cdkDragDisabled]=\"col.reorderable === false\">\n <button\n type=\"button\"\n class=\"lt-chooser-toggle\"\n [class.checked]=\"!col.hidden\"\n [disabled]=\"col.hideable === false\"\n (click)=\"toggleColumnVisible(col)\"\n [attr.aria-pressed]=\"!col.hidden\"\n >\n <span class=\"lt-chooser-box\" aria-hidden=\"true\">\n @if (!col.hidden) {\n <svg viewBox=\"0 0 12 12\" width=\"10\" height=\"10\">\n <path fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M2 6.3 4.6 9 10 3\"/>\n </svg>\n }\n </span>\n <span class=\"lt-chooser-text\">{{ col.label }}</span>\n @if (isDefaultHidden(col)) {\n <span class=\"lt-chooser-default-tag\">hidden by default</span>\n }\n </button>\n @if (col.reorderable !== false) {\n <span class=\"lt-chooser-handle\" cdkDragHandle aria-label=\"Drag to reorder\" title=\"Drag to reorder\">\n <svg viewBox=\"0 0 10 16\" width=\"10\" height=\"15\" fill=\"currentColor\" aria-hidden=\"true\">\n <circle cx=\"3\" cy=\"3\" r=\"1.25\"/><circle cx=\"7\" cy=\"3\" r=\"1.25\"/>\n <circle cx=\"3\" cy=\"8\" r=\"1.25\"/><circle cx=\"7\" cy=\"8\" r=\"1.25\"/>\n <circle cx=\"3\" cy=\"13\" r=\"1.25\"/><circle cx=\"7\" cy=\"13\" r=\"1.25\"/>\n </svg>\n </span>\n }\n </li>\n }\n </ul>\n <div class=\"lt-chooser-footer\">\n <button\n type=\"button\"\n class=\"lt-chooser-reset\"\n [disabled]=\"columnsAtDefault\"\n (click)=\"resetTableColumns()\"\n id=\"ResetColumnsButton\"\n >\n Reset to default\n </button>\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n\n <div class=\"lt-scroll\" id=\"ItemTable\">\n <table class=\"lt-table\">\n <colgroup>\n @if (!options.noSelect) { <col class=\"lt-col-select\" /> }\n @for (col of visibleColumns; track trackCol($index, col)) {\n <col [style.width]=\"colWidth(col)\" />\n }\n @if (!options.noMenu) { <col class=\"lt-col-menu\" /> }\n </colgroup>\n <thead class=\"lt-head\">\n <tr cdkDropList cdkDropListOrientation=\"horizontal\" (cdkDropListDropped)=\"onReorder($event)\">\n @if (!options.noSelect) {\n <th class=\"lt-select-cell\">\n <div\n class=\"lt-select-all\"\n [class.checked]=\"allToggled\"\n [class.indeterminate]=\"someSelected\"\n (click)=\"toggleAll()\"\n id=\"SelectAllRows\"\n ></div>\n </th>\n }\n @for (col of visibleColumns; track trackCol($index, col)) {\n <th\n cdkDrag\n [cdkDragDisabled]=\"col.reorderable === false\"\n class=\"lt-th\"\n [ngClass]=\"col.cellClass\"\n id=\"Header_{{ col.key }}\"\n >\n <lib-list-header-cell\n [column]=\"col\"\n [sortDir]=\"col.sortDir\"\n (sortChanged)=\"onHeaderSort($event)\"\n (hideColumn)=\"hideColumn($event)\"\n ></lib-list-header-cell>\n @if (col.resizable !== false) {\n <span class=\"lt-resize\" (pointerdown)=\"startResize(col, $event)\"></span>\n }\n </th>\n }\n @if (!options.noMenu) {\n <th class=\"lt-menu-cell\">\n <div class=\"lt-header-menu-trigger\" (click)=\"openHeaderActionMenu($event)\" id=\"ColumnMenuHeader\">\n ⋮\n </div>\n </th>\n }\n </tr>\n </thead>\n <tbody>\n @for (row of rowData; track trackRow($index, row); let i = $index) {\n <tr\n class=\"lt-row\"\n [class.lt-row-odd]=\"i % 2 === 1\"\n [class.lt-row-selected]=\"row.selected\"\n [class.row-invalid]=\"row.invalid\"\n [class.row-copied]=\"row.copied\"\n >\n @if (!options.noSelect) {\n <td class=\"lt-select-cell\">\n <lib-select-cell [row]=\"row\" [toggleItem]=\"toggleItem\"></lib-select-cell>\n </td>\n }\n @for (col of visibleColumns; track trackCol($index, col)) {\n <td\n class=\"lt-cell\"\n [ngClass]=\"cellClass(col)\"\n (click)=\"onCellClick(col, row, i)\"\n (mouseenter)=\"onCellHover(col, row, i, $event)\"\n (mouseleave)=\"onCellLeave()\"\n >\n @switch (renderMode(col)) {\n @case ('component') {\n <ng-container *ngComponentOutlet=\"col.cell; inputs: cellInputs(col, row, i)\"></ng-container>\n }\n @case ('html') {\n <span class=\"lt-cell-html\" [innerHTML]=\"cellHtml(col, row, i)\"></span>\n }\n @default {\n <span class=\"lt-cell-text\">{{ cellText(col, row, i) }}</span>\n }\n }\n </td>\n }\n @if (!options.noMenu) {\n <td class=\"lt-menu-cell\">\n @if (row.actionList?.length) {\n <button\n type=\"button\"\n class=\"lt-row-menu-trigger\"\n (click)=\"$event.stopPropagation(); openActionMenu($event, row)\"\n attr.id=\"RowMenu_{{ row.id }}\"\n aria-label=\"Row actions\"\n >⋮</button>\n }\n </td>\n }\n </tr>\n }\n </tbody>\n </table>\n @if (!isLoading && filterApplied && (!rowData || rowData.length === 0)) {\n <div class=\"no-rows-message\">No Rows To Show</div>\n }\n </div>\n\n <div class=\"lt-footer\">\n <div class=\"lt-footer-count\">\n Showing <span id=\"Start\">{{ startCount }}</span>–<span id=\"End\">{{ endCount }}</span>\n of <span id=\"Total\">{{ totalCount }}</span>\n </div>\n <div class=\"lt-footer-controls\">\n <div class=\"lt-rpp\">\n <span class=\"lt-rpp-label\">Rows per page</span>\n <select\n class=\"lt-rpp-select\"\n [disabled]=\"filtering\"\n [value]=\"pageSize\"\n (change)=\"onPageSizeChange(+$any($event.target).value)\"\n aria-label=\"Rows per page\"\n >\n @for (opt of pageSizeOptions; track opt) {\n <option [value]=\"opt\">{{ opt }}</option>\n }\n </select>\n </div>\n <div class=\"lt-pager\">\n <button type=\"button\" class=\"lt-page-btn icon-prev\" [disabled]=\"prevDisabled\" (click)=\"prevPage()\" aria-label=\"Previous page\"></button>\n @for (p of pageWindow; track $index) {\n @if (p === 0) {\n <span class=\"lt-page-gap\">…</span>\n } @else {\n <button\n type=\"button\"\n class=\"lt-page-num\"\n [class.is-current]=\"p === currentPageNum\"\n (click)=\"goToPage(p)\"\n >{{ p }}</button>\n }\n }\n <button type=\"button\" class=\"lt-page-btn icon-next\" [disabled]=\"nextDisabled\" (click)=\"nextPage()\" aria-label=\"Next page\"></button>\n </div>\n </div>\n </div>\n\n <!-- header action menu -->\n <lib-list-menu\n menuId=\"HeaderActionMenu\"\n [open]=\"openHeaderMenu\"\n [items]=\"headerMenuItems\"\n [left]=\"menuLeft\"\n [top]=\"menuTop\"\n (itemClick)=\"onHeaderMenuClick($event)\"\n (closed)=\"closeHeaderActionMenu()\"\n ></lib-list-menu>\n\n <!-- row action menu -->\n <lib-list-menu\n menuId=\"RowActionMenu\"\n [open]=\"openMenu\"\n [items]=\"rowMenuItems\"\n [left]=\"menuLeft\"\n [top]=\"menuTop\"\n (itemClick)=\"onRowMenuClick($event)\"\n (closed)=\"closeActionMenu()\"\n ></lib-list-menu>\n </div>\n} @else {\n <lib-no-items\n (clickEmit)=\"openCreate()\"\n [image]=\"noItemsImage\"\n [isEmpty]=\"!rowData || rowData.length === 0\"\n [isLoading]=\"isLoading\"\n [typeName]=\"entityTypeLabel\"\n [hasAdd]=\"showNoItemsAdd\"\n [readonly]=\"noItemsReadonly\"\n [accessDenied]=\"accessDenied\"\n ></lib-no-items>\n}\n\n@if (hoverTooltip) {\n <div class=\"lt-tooltip\" [ngStyle]=\"{ left: hoverTooltip.left + 'px', top: hoverTooltip.top + 'px' }\">\n <ng-container *ngComponentOutlet=\"hoverTooltip.component; inputs: hoverTooltip.inputs\"></ng-container>\n </div>\n}\n", styles: [":host{display:flex;flex-direction:column;width:100%;height:100%}.lt-columns-btn,.lt-autorefresh-btn,.lt-refresh-btn,.lt-filter-more-btn{display:inline-flex;align-items:center;gap:.4375rem;height:var(--lt-toolbar-height);padding:0 .75rem;font-family:var(--lt-font);font-size:.8125rem;font-weight:600;color:var(--lt-cell-text);background:var(--lt-control-bg);border:var(--inputBorderWidth) solid var(--lt-border);border-radius:var(--lt-radius);cursor:pointer;white-space:nowrap;transition:border-color .15s,background .15s,color .15s}.lt-columns-btn svg,.lt-autorefresh-btn svg,.lt-refresh-btn svg,.lt-filter-more-btn svg{display:block}.lt-columns-btn:hover,.lt-autorefresh-btn:hover,.lt-refresh-btn:hover,.lt-filter-more-btn:hover,.is-open.lt-columns-btn,.is-open.lt-autorefresh-btn,.is-open.lt-refresh-btn,.is-open.lt-filter-more-btn,.is-on.lt-columns-btn,.is-on.lt-autorefresh-btn,.is-on.lt-refresh-btn,.is-on.lt-filter-more-btn{border-color:var(--lt-link);color:var(--lt-link)}.lt-columns-count,.lt-filter-more-count{display:inline-flex;align-items:center;justify-content:center;min-width:1.125rem;height:1.125rem;padding:0 .3125rem;font-size:.6875rem;font-weight:700;color:var(--lt-control-bg);background:var(--lt-link);border-radius:.5625rem}.lt-container{display:flex;position:relative;height:calc(100% - 5.625rem);width:100%;flex-direction:column}.lt-scroll{position:relative;z-index:1;width:100%;flex:0 1 auto;min-height:0;overflow:auto;background-color:var(--lt-bg)}.lt-scroll .no-rows-message{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);color:var(--lt-cell-text);font-size:.875rem;pointer-events:none}.lt-table{width:100%;border-collapse:collapse;table-layout:fixed;font-family:var(--lt-font);font-size:.8125rem;color:var(--lt-cell-text)}.lt-head{position:sticky;top:0;z-index:10}.lt-head th{position:relative;box-sizing:border-box;height:2.75rem;padding:0 .3125rem 0 .625rem;text-align:left;vertical-align:middle;background-color:var(--lt-header-bg);border-bottom:1px solid var(--lt-header-underline);white-space:nowrap;-webkit-user-select:none;user-select:none}.lt-head th.lt-th{border-right:1px solid var(--lt-border)}.lt-head .lt-select-cell,.lt-head .lt-menu-cell{width:3.75rem;padding:0}.lt-resize{position:absolute;right:0;top:50%;transform:translateY(-50%);width:.625rem;height:.875rem;cursor:col-resize}.lt-resize:before,.lt-resize:after{content:\"\";position:absolute;top:0;height:100%;width:1px;background:transparent;transition:background .15s}.lt-resize:before{right:.25rem}.lt-resize:after{right:.125rem}.lt-resize:hover:before,.lt-resize:hover:after{background:var(--lt-icon)}.lt-head th:hover .lt-resize:before,.lt-head th:hover .lt-resize:after{background:var(--lt-border)}.lt-select-all{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);box-sizing:border-box;width:1rem;height:1rem;border:1px solid var(--lt-border);border-radius:.25rem;background:var(--lt-control-bg);background-repeat:no-repeat;background-position:center center;cursor:pointer;transition:border-color .12s,background-color .12s}.lt-select-all:hover{border-color:var(--lt-link)}.lt-select-all.checked{background-color:var(--lt-link);border-color:var(--lt-link);background-size:.75rem;background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 14'%3E%3Cpath fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' d='M2.5 7.6 5.7 10.8 11.5 3.6'/%3E%3C/svg%3E\")}.lt-select-all.indeterminate{background-color:var(--lt-link);border-color:var(--lt-link);background-image:linear-gradient(#fff,#fff);background-size:.5rem .125rem}.lt-header-menu-trigger{display:flex;align-items:center;justify-content:center;height:100%;font-size:1.25rem;color:var(--lt-icon);cursor:pointer}.lt-header-menu-trigger:hover{color:var(--lt-icon-active)}.lt-row-menu-trigger{display:inline-flex;align-items:center;justify-content:center;width:1.75rem;height:1.75rem;padding:0;font-size:1.25rem;line-height:1;color:var(--lt-icon);background:transparent;border:none;border-radius:var(--lt-radius);cursor:pointer;transition:color .15s,background .15s}.lt-row-menu-trigger:hover{color:var(--lt-icon-active);background:var(--lt-row-hover)}.cdk-drag-preview{box-sizing:border-box;background-color:var(--lt-header-bg);color:var(--lt-header-text);box-shadow:var(--shadowLarge);padding:0 .625rem;display:flex;align-items:center;border-radius:var(--lt-radius)}.cdk-drag-placeholder{opacity:.3}.cdk-drag-animating{transition:transform .2s cubic-bezier(0,0,.2,1)}.lt-row{height:var(--lt-row-height);background-color:var(--lt-row-bg);color:var(--lt-cell-text)}.lt-row td{border-bottom:1px solid var(--lt-border)}.lt-row.lt-row-odd{background-color:var(--lt-row-alt-bg)}.lt-row:hover{background-color:var(--lt-row-hover)}.lt-row.lt-row-selected{background-color:var(--lt-row-selected)}.lt-row.lt-row-selected:hover{background-color:var(--lt-row-hover)}.lt-row.row-invalid{background-color:var(--invalid)!important}.lt-row.row-copied{background-color:var(--copiedRow)!important}.lt-cell{height:var(--lt-row-height);padding:0 .625rem 0 1.25rem;text-align:left;vertical-align:middle;border:none;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;-webkit-user-select:text;user-select:text}.lt-cell-text{display:inline-block;overflow:hidden;text-overflow:ellipsis;max-width:100%;vertical-align:middle}.lt-cell.lt-cell-center{text-align:center}.lt-badge-cell .lt-cell-text{padding:.1875rem .5rem;font-size:.75rem;font-weight:600;line-height:1.2;color:var(--lt-cell-text);background:color-mix(in srgb,var(--lt-border) 45%,transparent);border:1px solid var(--lt-border);border-radius:999px}.lt-badge-cell .lt-cell-text:empty{display:none}.lt-select-cell,.lt-menu-cell{width:3.75rem;padding:0;text-align:center;vertical-align:middle;overflow:visible;position:relative}.lt-toolbar{display:flex;align-items:center;justify-content:space-between;gap:.75rem;padding:0 0 .625rem;min-height:var(--lt-toolbar-height);position:relative;z-index:30}.lt-toolbar-left{display:flex;align-items:center;gap:.5rem;flex:1 1 auto;min-width:0;flex-wrap:nowrap}.lt-filter-zone{display:flex;align-items:center;flex-wrap:nowrap;gap:.5rem;flex:0 1 auto;min-width:0}.lt-filters{display:flex;align-items:center;flex-wrap:nowrap;gap:.5rem;min-width:0;flex:0 1 auto;overflow:hidden}.lt-filter-item{display:inline-flex;flex:0 0 auto}.lt-filter-more{position:relative;flex:0 0 auto}.lt-filter-more-btn{gap:.375rem;padding:0 .625rem}.lt-filter-more-btn .lt-filter-more-icon{display:inline-flex}.lt-filter-overflow{position:absolute;top:calc(100% + .25rem);right:0;z-index:1000;min-width:15rem;max-width:min(22rem,90vw);display:flex;flex-direction:column;gap:.5rem;padding:.625rem;background:var(--lt-control-bg);border:1px solid var(--lt-border);border-radius:var(--lt-radius);box-shadow:var(--shadowLarge);animation:lt-chooser-in .12s ease-out}.lt-filter-overflow-title{font-family:var(--lt-font);font-size:.6875rem;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--groupText)}.lt-filter-overflow-row{display:flex}.lt-filter-overflow-row lib-list-filter-cell,.lt-filter-overflow-row .lt-filter-item{flex:1 1 auto;min-width:0}.lt-toolbar-right{display:flex;align-items:center;gap:.75rem;flex:0 0 auto}.lt-divider{flex:0 0 auto;width:1px;height:1.5rem;background:var(--lt-border)}.lt-search{position:relative;display:flex;align-items:center;width:18rem;max-width:40vw;flex:0 1 18rem}.lt-search-icon{position:absolute;left:.625rem;display:block;color:var(--lt-icon);pointer-events:none}.lt-search-input{width:100%;height:var(--lt-toolbar-height);padding:0 .75rem 0 1.75rem;font-family:var(--lt-font);font-size:.8125rem;color:var(--lt-cell-text);background:var(--lt-control-bg);border:var(--inputBorderWidth) solid var(--lt-border);border-radius:var(--lt-radius);outline:none;transition:border-color .15s,box-shadow .15s}.lt-search-input::placeholder{color:var(--placeholder)}.lt-search-input:focus{border-color:var(--lt-link);box-shadow:0 0 0 .1875rem color-mix(in srgb,var(--lt-link) 18%,transparent)}.lt-count{font-size:.8125rem;color:var(--groupText);white-space:nowrap}.lt-count .lt-count-of{color:var(--groupText)}.lt-pager{display:flex;align-items:center;gap:.25rem}.lt-page-btn{display:inline-flex;align-items:center;justify-content:center;width:var(--lt-toolbar-height);height:var(--lt-toolbar-height);color:var(--lt-icon);background:var(--lt-control-bg);border:var(--inputBorderWidth) solid var(--lt-border);border-radius:var(--lt-radius);cursor:pointer;font-size:.9375rem;transition:border-color .15s,color .15s}.lt-page-btn:not(:disabled):hover{border-color:var(--lt-link);color:var(--lt-link)}.lt-page-btn:disabled{opacity:.4;cursor:default}.lt-refresh-btn{padding:0;width:var(--lt-toolbar-height);justify-content:center}.lt-refresh-btn:disabled{opacity:.5;cursor:default;border-color:var(--lt-border);color:var(--lt-cell-text)}.lt-columns{position:relative;flex:0 0 auto}.lt-column-chooser{position:absolute;top:calc(100% + .25rem);right:0;z-index:1000;width:15rem;max-height:calc(100vh - 9rem);display:flex;flex-direction:column;background:var(--lt-control-bg);border:1px solid var(--lt-border);border-radius:var(--lt-radius);box-shadow:var(--shadowLarge);overflow:hidden;animation:lt-chooser-in .12s ease-out}@keyframes lt-chooser-in{0%{opacity:0;transform:translateY(-.25rem)}to{opacity:1;transform:translateY(0)}}.lt-chooser-header{flex:0 0 auto;display:flex;flex-direction:column;padding:.625rem .75rem .5rem;border-bottom:1px solid var(--lt-border)}.lt-chooser-title{font-size:.8125rem;font-weight:700;color:var(--lt-cell-text)}.lt-chooser-subtitle{font-size:.6875rem;color:var(--groupText)}.lt-chooser-list{flex:1 1 auto;list-style:none;margin:0;padding:.375rem .5rem;min-height:0;overflow-y:auto}.lt-chooser-row{display:flex;align-items:center;justify-content:space-between;min-height:2.5rem;padding:.5rem;border-radius:var(--lt-radius);background:var(--lt-control-bg)}.lt-chooser-row:hover{background:var(--lt-row-hover)}.lt-chooser-row:hover .lt-chooser-handle{opacity:1}.lt-chooser-toggle{display:flex;align-items:center;gap:.625rem;flex:1 1 auto;min-width:0;padding:0;background:transparent;border:none;font:inherit;font-size:.875rem;color:var(--lt-cell-text);text-align:left;cursor:pointer}.lt-chooser-toggle:disabled{cursor:default;opacity:.5}.lt-chooser-box{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;box-sizing:border-box;width:1rem;height:1rem;border:.09375rem solid var(--lt-border);border-radius:.25rem;color:var(--lt-control-bg);background:var(--lt-control-bg);transition:background .12s,border-color .12s}.lt-chooser-toggle.checked .lt-chooser-box{background:var(--lt-link);border-color:var(--lt-link)}.lt-chooser-toggle:not(:disabled):hover .lt-chooser-box{border-color:var(--lt-link)}.lt-chooser-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.lt-chooser-default-tag{flex:0 0 auto;margin-left:.375rem;font-size:.6875rem;font-weight:400;font-style:italic;color:var(--groupText);white-space:nowrap}.lt-chooser-handle{flex:0 0 auto;display:inline-flex;align-items:center;color:var(--lt-icon);cursor:grab;padding-left:.5rem;-webkit-user-select:none;user-select:none}.lt-chooser-handle:active{cursor:grabbing}.lt-chooser-footer{flex:0 0 auto;padding:.5rem .75rem;border-top:1px solid var(--lt-border)}.lt-chooser-reset{width:100%;height:1.875rem;font-size:.75rem;font-weight:600;color:var(--lt-link);background:transparent;border:1px solid var(--lt-border);border-radius:var(--lt-radius);cursor:pointer}.lt-chooser-reset:disabled{color:var(--groupText);cursor:default;opacity:.6}.lt-chooser-reset:not(:disabled):hover{border-color:var(--lt-link)}.lt-footer{flex:0 0 auto;display:flex;align-items:center;justify-content:space-between;gap:1rem;flex-wrap:wrap;padding:.625rem .125rem 0;font-family:var(--lt-font);font-size:.8125rem;color:var(--groupText)}.lt-footer-count{white-space:nowrap}.lt-footer-count span{color:var(--lt-cell-text);font-weight:600}.lt-footer-controls{display:flex;align-items:center;gap:1.25rem;flex-wrap:nowrap}.lt-rpp{display:inline-flex;align-items:center;gap:.5rem;white-space:nowrap}.lt-rpp-label{text-transform:none;font-weight:400;color:var(--groupText)}.lt-rpp-select{width:auto;min-width:4rem;height:var(--lt-toolbar-height);padding:0 1.75rem 0 .625rem;font-family:var(--lt-font);font-size:.8125rem;color:var(--lt-cell-text);background-color:var(--lt-control-bg);background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='none' stroke='%236b7280' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round' d='M3 4.5 6 7.5 9 4.5'/%3E%3C/svg%3E\");background-repeat:no-repeat;background-position:right .5rem center;border:var(--inputBorderWidth) solid var(--lt-border);border-radius:var(--lt-radius);appearance:none;-webkit-appearance:none;cursor:pointer}.lt-rpp-select:focus{outline:none;border-color:var(--lt-link)}.lt-page-num{display:inline-flex;align-items:center;justify-content:center;min-width:var(--lt-toolbar-height);height:var(--lt-toolbar-height);padding:0 .5rem;font-family:var(--lt-font);font-size:.8125rem;font-weight:600;color:var(--lt-cell-text);background:var(--lt-control-bg);border:var(--inputBorderWidth) solid var(--lt-border);border-radius:var(--lt-radius);cursor:pointer;transition:border-color .15s,color .15s,background .15s}.lt-page-num:hover{border-color:var(--lt-link);color:var(--lt-link)}.lt-page-num.is-current{color:#fff;background:var(--lt-link);border-color:var(--lt-link);cursor:default}.lt-page-gap{display:inline-flex;align-items:center;justify-content:center;min-width:1.25rem;color:var(--groupText)}.lt-tooltip{position:fixed;z-index:1100;pointer-events:none;animation:lt-tooltip-in .12s ease-out}@keyframes lt-tooltip-in{0%{opacity:0;transform:translateY(-.1875rem)}to{opacity:1;transform:translateY(0)}}\n"], dependencies: [{ kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"], exportAs: ["ngComponentOutlet"] }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i1$1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i6$1.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: i6$1.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: i6$1.CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "directive", type: i3$1.ClickOutsideDirective, selector: "[clickOutside]", inputs: ["clickOutsideEnabled", "attachOutsideOnClick", "delayClickOutsideInit", "emitOnBlur", "exclude", "excludeBeforeClick", "clickOutsideEvents"], outputs: ["clickOutside"] }, { kind: "component", type: ListMenuComponent, selector: "lib-list-menu", inputs: ["open", "items", "left", "top", "menuId"], outputs: ["itemClick", "closed"] }, { kind: "component", type: SelectCellComponent, selector: "lib-select-cell", inputs: ["row", "toggleItem"] }, { kind: "component", type: ListHeaderCellComponent, selector: "lib-list-header-cell", inputs: ["column", "sortDir"], outputs: ["sortChanged", "hideColumn"] }, { kind: "component", type: ListFilterCellComponent, selector: "lib-list-filter-cell", inputs: ["column"] }, { kind: "component", type: NoItemsComponent, selector: "lib-no-items", inputs: ["image", "typeName", "isEmpty", "hasAdd", "hiddenResults", "isLoading", "readonly", "accessDenied"], outputs: ["clickEmit", "refresh"] }] }); }
|
|
12844
|
+
}
|
|
12845
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ListTableComponent, decorators: [{
|
|
12846
|
+
type: Component,
|
|
12847
|
+
args: [{ selector: 'lib-list-table', standalone: false, providers: [ListTableService], template: "@if (showTable && !accessDenied) {\n <div class=\"lt-container\">\n <div class=\"lt-toolbar\">\n <div class=\"lt-toolbar-left\">\n <div class=\"lt-search\">\n <svg class=\"lt-search-icon\" viewBox=\"0 0 16 16\" width=\"14\" height=\"14\" aria-hidden=\"true\">\n <circle cx=\"7\" cy=\"7\" r=\"4.3\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"/>\n <line x1=\"10.4\" y1=\"10.4\" x2=\"14.6\" y2=\"14.6\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\"/>\n </svg>\n <input\n class=\"lt-search-input\"\n id=\"SearchFilter\"\n type=\"text\"\n [placeholder]=\"'Search ' + (entityTypeLabel || 'items')\"\n [(ngModel)]=\"searchString\"\n (keyup)=\"onSearchInput()\"\n />\n </div>\n @if (filterableColumns.length) {\n <div class=\"lt-filter-zone\" #filterZone>\n <div class=\"lt-filters\" #filtersRow>\n @for (col of visibleFilterColumns; track col.key) {\n <span class=\"lt-filter-item\" [attr.data-filter-key]=\"col.key\">\n <lib-list-filter-cell [column]=\"col\"></lib-list-filter-cell>\n </span>\n }\n </div>\n @if (overflowFilterColumns.length) {\n <div class=\"lt-filter-more\">\n <button\n type=\"button\"\n class=\"lt-filter-more-btn\"\n [class.is-on]=\"hiddenActiveCount > 0\"\n [class.is-open]=\"showFilterOverflow\"\n (click)=\"toggleFilterOverflow($event)\"\n aria-haspopup=\"true\"\n [attr.aria-expanded]=\"showFilterOverflow\"\n [attr.aria-label]=\"overflowFilterColumns.length + ' more filters'\"\n id=\"FilterOverflowButton\"\n >\n <span class=\"lt-filter-more-icon\" aria-hidden=\"true\">\n <svg viewBox=\"0 0 16 16\" width=\"14\" height=\"14\"><path fill=\"currentColor\" d=\"M2 3h12l-4.6 5.6v4l-2.8 1.4v-5.4z\"/></svg>\n </span>\n <span class=\"lt-filter-more-label\">More</span>\n <span class=\"lt-filter-more-count\">{{ overflowFilterColumns.length }}</span>\n </button>\n @if (showFilterOverflow) {\n <div class=\"lt-filter-overflow\" (clickOutside)=\"closeFilterOverflow($event)\">\n <div class=\"lt-filter-overflow-title\">Filters</div>\n @for (col of overflowFilterColumns; track col.key) {\n <div class=\"lt-filter-overflow-row\" [class.is-active]=\"isFilterActive(col)\">\n <lib-list-filter-cell [column]=\"col\"></lib-list-filter-cell>\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n <div class=\"lt-toolbar-right\">\n <ng-template #refreshIcon>\n <svg viewBox=\"0 0 16 16\" width=\"14\" height=\"14\" aria-hidden=\"true\">\n <path fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" d=\"M13.4 8a5.4 5.4 0 1 1-1.5-3.8\"/>\n <path fill=\"currentColor\" d=\"M13.9 2.1l.5 3.2-3.2-.5z\"/>\n </svg>\n </ng-template>\n <button\n type=\"button\"\n class=\"lt-refresh-btn\"\n (click)=\"refresh()\"\n [disabled]=\"filtering\"\n aria-label=\"Refresh\"\n title=\"Refresh\"\n id=\"RefreshTableButton\"\n >\n <ng-container [ngTemplateOutlet]=\"refreshIcon\"></ng-container>\n </button>\n <button\n type=\"button\"\n class=\"lt-autorefresh-btn\"\n [class.is-on]=\"autoRefresh\"\n (click)=\"toggleAutoRefresh()\"\n [attr.aria-pressed]=\"autoRefresh\"\n id=\"AutoRefreshButton\"\n >\n <ng-container [ngTemplateOutlet]=\"refreshIcon\"></ng-container>\n <span>Auto-refresh: {{ autoRefresh ? 'On' : 'Off' }}</span>\n </button>\n <div class=\"lt-columns\">\n <button\n type=\"button\"\n class=\"lt-columns-btn\"\n [class.is-on]=\"hiddenColumnCount > 0\"\n [class.is-open]=\"showColumnChooser\"\n (click)=\"toggleColumnChooser($event)\"\n aria-haspopup=\"true\"\n [attr.aria-expanded]=\"showColumnChooser\"\n id=\"ColumnChooserButton\"\n >\n <svg viewBox=\"0 0 16 16\" width=\"14\" height=\"14\" aria-hidden=\"true\">\n <path fill=\"currentColor\" d=\"M2 3.5h9v1.4H2zM2 7.3h6v1.4H2zM2 11.1h11v1.4H2z\"/>\n <circle cx=\"13\" cy=\"4.2\" r=\"1.7\" fill=\"currentColor\"/>\n <circle cx=\"10\" cy=\"8\" r=\"1.7\" fill=\"currentColor\"/>\n <circle cx=\"14.4\" cy=\"11.8\" r=\"1.7\" fill=\"currentColor\"/>\n </svg>\n <span>Columns</span>\n @if (hiddenColumnCount > 0) {\n <span class=\"lt-columns-count\">{{ hiddenColumnCount }}</span>\n }\n </button>\n @if (showColumnChooser) {\n <div class=\"lt-column-chooser\" (clickOutside)=\"closeColumnChooser()\">\n <div class=\"lt-chooser-header\">\n <span class=\"lt-chooser-title\">Columns</span>\n <span class=\"lt-chooser-subtitle\">Customize order and visibility</span>\n </div>\n <ul class=\"lt-chooser-list\" cdkDropList cdkDropListLockAxis=\"y\" (cdkDropListDropped)=\"chooserReorder($event)\">\n @for (col of allColumns; track col.key) {\n <li class=\"lt-chooser-row\" cdkDrag cdkDragLockAxis=\"y\" [cdkDragDisabled]=\"col.reorderable === false\">\n <button\n type=\"button\"\n class=\"lt-chooser-toggle\"\n [class.checked]=\"!col.hidden\"\n [disabled]=\"col.hideable === false\"\n (click)=\"toggleColumnVisible(col)\"\n [attr.aria-pressed]=\"!col.hidden\"\n >\n <span class=\"lt-chooser-box\" aria-hidden=\"true\">\n @if (!col.hidden) {\n <svg viewBox=\"0 0 12 12\" width=\"10\" height=\"10\">\n <path fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M2 6.3 4.6 9 10 3\"/>\n </svg>\n }\n </span>\n <span class=\"lt-chooser-text\">{{ col.label }}</span>\n @if (isDefaultHidden(col)) {\n <span class=\"lt-chooser-default-tag\">hidden by default</span>\n }\n </button>\n @if (col.reorderable !== false) {\n <span class=\"lt-chooser-handle\" cdkDragHandle aria-label=\"Drag to reorder\" title=\"Drag to reorder\">\n <svg viewBox=\"0 0 10 16\" width=\"10\" height=\"15\" fill=\"currentColor\" aria-hidden=\"true\">\n <circle cx=\"3\" cy=\"3\" r=\"1.25\"/><circle cx=\"7\" cy=\"3\" r=\"1.25\"/>\n <circle cx=\"3\" cy=\"8\" r=\"1.25\"/><circle cx=\"7\" cy=\"8\" r=\"1.25\"/>\n <circle cx=\"3\" cy=\"13\" r=\"1.25\"/><circle cx=\"7\" cy=\"13\" r=\"1.25\"/>\n </svg>\n </span>\n }\n </li>\n }\n </ul>\n <div class=\"lt-chooser-footer\">\n <button\n type=\"button\"\n class=\"lt-chooser-reset\"\n [disabled]=\"columnsAtDefault\"\n (click)=\"resetTableColumns()\"\n id=\"ResetColumnsButton\"\n >\n Reset to default\n </button>\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n\n <div class=\"lt-scroll\" id=\"ItemTable\">\n <table class=\"lt-table\">\n <colgroup>\n @if (!options.noSelect) { <col class=\"lt-col-select\" /> }\n @for (col of visibleColumns; track trackCol($index, col)) {\n <col [style.width]=\"colWidth(col)\" />\n }\n @if (!options.noMenu) { <col class=\"lt-col-menu\" /> }\n </colgroup>\n <thead class=\"lt-head\">\n <tr cdkDropList cdkDropListOrientation=\"horizontal\" (cdkDropListDropped)=\"onReorder($event)\">\n @if (!options.noSelect) {\n <th class=\"lt-select-cell\">\n <div\n class=\"lt-select-all\"\n [class.checked]=\"allToggled\"\n [class.indeterminate]=\"someSelected\"\n (click)=\"toggleAll()\"\n id=\"SelectAllRows\"\n ></div>\n </th>\n }\n @for (col of visibleColumns; track trackCol($index, col)) {\n <th\n cdkDrag\n [cdkDragDisabled]=\"col.reorderable === false\"\n class=\"lt-th\"\n [ngClass]=\"col.cellClass\"\n id=\"Header_{{ col.key }}\"\n >\n <lib-list-header-cell\n [column]=\"col\"\n [sortDir]=\"col.sortDir\"\n (sortChanged)=\"onHeaderSort($event)\"\n (hideColumn)=\"hideColumn($event)\"\n ></lib-list-header-cell>\n @if (col.resizable !== false) {\n <span class=\"lt-resize\" (pointerdown)=\"startResize(col, $event)\"></span>\n }\n </th>\n }\n @if (!options.noMenu) {\n <th class=\"lt-menu-cell\">\n <div class=\"lt-header-menu-trigger\" (click)=\"openHeaderActionMenu($event)\" id=\"ColumnMenuHeader\">\n ⋮\n </div>\n </th>\n }\n </tr>\n </thead>\n <tbody>\n @for (row of rowData; track trackRow($index, row); let i = $index) {\n <tr\n class=\"lt-row\"\n [class.lt-row-odd]=\"i % 2 === 1\"\n [class.lt-row-selected]=\"row.selected\"\n [class.row-invalid]=\"row.invalid\"\n [class.row-copied]=\"row.copied\"\n >\n @if (!options.noSelect) {\n <td class=\"lt-select-cell\">\n <lib-select-cell [row]=\"row\" [toggleItem]=\"toggleItem\"></lib-select-cell>\n </td>\n }\n @for (col of visibleColumns; track trackCol($index, col)) {\n <td\n class=\"lt-cell\"\n [ngClass]=\"cellClass(col)\"\n (click)=\"onCellClick(col, row, i)\"\n (mouseenter)=\"onCellHover(col, row, i, $event)\"\n (mouseleave)=\"onCellLeave()\"\n >\n @switch (renderMode(col)) {\n @case ('component') {\n <ng-container *ngComponentOutlet=\"col.cell; inputs: cellInputs(col, row, i)\"></ng-container>\n }\n @case ('html') {\n <span class=\"lt-cell-html\" [innerHTML]=\"cellHtml(col, row, i)\"></span>\n }\n @default {\n <span class=\"lt-cell-text\">{{ cellText(col, row, i) }}</span>\n }\n }\n </td>\n }\n @if (!options.noMenu) {\n <td class=\"lt-menu-cell\">\n @if (row.actionList?.length) {\n <button\n type=\"button\"\n class=\"lt-row-menu-trigger\"\n (click)=\"$event.stopPropagation(); openActionMenu($event, row)\"\n attr.id=\"RowMenu_{{ row.id }}\"\n aria-label=\"Row actions\"\n >⋮</button>\n }\n </td>\n }\n </tr>\n }\n </tbody>\n </table>\n @if (!isLoading && filterApplied && (!rowData || rowData.length === 0)) {\n <div class=\"no-rows-message\">No Rows To Show</div>\n }\n </div>\n\n <div class=\"lt-footer\">\n <div class=\"lt-footer-count\">\n Showing <span id=\"Start\">{{ startCount }}</span>–<span id=\"End\">{{ endCount }}</span>\n of <span id=\"Total\">{{ totalCount }}</span>\n </div>\n <div class=\"lt-footer-controls\">\n <div class=\"lt-rpp\">\n <span class=\"lt-rpp-label\">Rows per page</span>\n <select\n class=\"lt-rpp-select\"\n [disabled]=\"filtering\"\n [value]=\"pageSize\"\n (change)=\"onPageSizeChange(+$any($event.target).value)\"\n aria-label=\"Rows per page\"\n >\n @for (opt of pageSizeOptions; track opt) {\n <option [value]=\"opt\">{{ opt }}</option>\n }\n </select>\n </div>\n <div class=\"lt-pager\">\n <button type=\"button\" class=\"lt-page-btn icon-prev\" [disabled]=\"prevDisabled\" (click)=\"prevPage()\" aria-label=\"Previous page\"></button>\n @for (p of pageWindow; track $index) {\n @if (p === 0) {\n <span class=\"lt-page-gap\">…</span>\n } @else {\n <button\n type=\"button\"\n class=\"lt-page-num\"\n [class.is-current]=\"p === currentPageNum\"\n (click)=\"goToPage(p)\"\n >{{ p }}</button>\n }\n }\n <button type=\"button\" class=\"lt-page-btn icon-next\" [disabled]=\"nextDisabled\" (click)=\"nextPage()\" aria-label=\"Next page\"></button>\n </div>\n </div>\n </div>\n\n <!-- header action menu -->\n <lib-list-menu\n menuId=\"HeaderActionMenu\"\n [open]=\"openHeaderMenu\"\n [items]=\"headerMenuItems\"\n [left]=\"menuLeft\"\n [top]=\"menuTop\"\n (itemClick)=\"onHeaderMenuClick($event)\"\n (closed)=\"closeHeaderActionMenu()\"\n ></lib-list-menu>\n\n <!-- row action menu -->\n <lib-list-menu\n menuId=\"RowActionMenu\"\n [open]=\"openMenu\"\n [items]=\"rowMenuItems\"\n [left]=\"menuLeft\"\n [top]=\"menuTop\"\n (itemClick)=\"onRowMenuClick($event)\"\n (closed)=\"closeActionMenu()\"\n ></lib-list-menu>\n </div>\n} @else {\n <lib-no-items\n (clickEmit)=\"openCreate()\"\n [image]=\"noItemsImage\"\n [isEmpty]=\"!rowData || rowData.length === 0\"\n [isLoading]=\"isLoading\"\n [typeName]=\"entityTypeLabel\"\n [hasAdd]=\"showNoItemsAdd\"\n [readonly]=\"noItemsReadonly\"\n [accessDenied]=\"accessDenied\"\n ></lib-no-items>\n}\n\n@if (hoverTooltip) {\n <div class=\"lt-tooltip\" [ngStyle]=\"{ left: hoverTooltip.left + 'px', top: hoverTooltip.top + 'px' }\">\n <ng-container *ngComponentOutlet=\"hoverTooltip.component; inputs: hoverTooltip.inputs\"></ng-container>\n </div>\n}\n", styles: [":host{display:flex;flex-direction:column;width:100%;height:100%}.lt-columns-btn,.lt-autorefresh-btn,.lt-refresh-btn,.lt-filter-more-btn{display:inline-flex;align-items:center;gap:.4375rem;height:var(--lt-toolbar-height);padding:0 .75rem;font-family:var(--lt-font);font-size:.8125rem;font-weight:600;color:var(--lt-cell-text);background:var(--lt-control-bg);border:var(--inputBorderWidth) solid var(--lt-border);border-radius:var(--lt-radius);cursor:pointer;white-space:nowrap;transition:border-color .15s,background .15s,color .15s}.lt-columns-btn svg,.lt-autorefresh-btn svg,.lt-refresh-btn svg,.lt-filter-more-btn svg{display:block}.lt-columns-btn:hover,.lt-autorefresh-btn:hover,.lt-refresh-btn:hover,.lt-filter-more-btn:hover,.is-open.lt-columns-btn,.is-open.lt-autorefresh-btn,.is-open.lt-refresh-btn,.is-open.lt-filter-more-btn,.is-on.lt-columns-btn,.is-on.lt-autorefresh-btn,.is-on.lt-refresh-btn,.is-on.lt-filter-more-btn{border-color:var(--lt-link);color:var(--lt-link)}.lt-columns-count,.lt-filter-more-count{display:inline-flex;align-items:center;justify-content:center;min-width:1.125rem;height:1.125rem;padding:0 .3125rem;font-size:.6875rem;font-weight:700;color:var(--lt-control-bg);background:var(--lt-link);border-radius:.5625rem}.lt-container{display:flex;position:relative;height:calc(100% - 5.625rem);width:100%;flex-direction:column}.lt-scroll{position:relative;z-index:1;width:100%;flex:0 1 auto;min-height:0;overflow:auto;background-color:var(--lt-bg)}.lt-scroll .no-rows-message{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);color:var(--lt-cell-text);font-size:.875rem;pointer-events:none}.lt-table{width:100%;border-collapse:collapse;table-layout:fixed;font-family:var(--lt-font);font-size:.8125rem;color:var(--lt-cell-text)}.lt-head{position:sticky;top:0;z-index:10}.lt-head th{position:relative;box-sizing:border-box;height:2.75rem;padding:0 .3125rem 0 .625rem;text-align:left;vertical-align:middle;background-color:var(--lt-header-bg);border-bottom:1px solid var(--lt-header-underline);white-space:nowrap;-webkit-user-select:none;user-select:none}.lt-head th.lt-th{border-right:1px solid var(--lt-border)}.lt-head .lt-select-cell,.lt-head .lt-menu-cell{width:3.75rem;padding:0}.lt-resize{position:absolute;right:0;top:50%;transform:translateY(-50%);width:.625rem;height:.875rem;cursor:col-resize}.lt-resize:before,.lt-resize:after{content:\"\";position:absolute;top:0;height:100%;width:1px;background:transparent;transition:background .15s}.lt-resize:before{right:.25rem}.lt-resize:after{right:.125rem}.lt-resize:hover:before,.lt-resize:hover:after{background:var(--lt-icon)}.lt-head th:hover .lt-resize:before,.lt-head th:hover .lt-resize:after{background:var(--lt-border)}.lt-select-all{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);box-sizing:border-box;width:1rem;height:1rem;border:1px solid var(--lt-border);border-radius:.25rem;background:var(--lt-control-bg);background-repeat:no-repeat;background-position:center center;cursor:pointer;transition:border-color .12s,background-color .12s}.lt-select-all:hover{border-color:var(--lt-link)}.lt-select-all.checked{background-color:var(--lt-link);border-color:var(--lt-link);background-size:.75rem;background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 14'%3E%3Cpath fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' d='M2.5 7.6 5.7 10.8 11.5 3.6'/%3E%3C/svg%3E\")}.lt-select-all.indeterminate{background-color:var(--lt-link);border-color:var(--lt-link);background-image:linear-gradient(#fff,#fff);background-size:.5rem .125rem}.lt-header-menu-trigger{display:flex;align-items:center;justify-content:center;height:100%;font-size:1.25rem;color:var(--lt-icon);cursor:pointer}.lt-header-menu-trigger:hover{color:var(--lt-icon-active)}.lt-row-menu-trigger{display:inline-flex;align-items:center;justify-content:center;width:1.75rem;height:1.75rem;padding:0;font-size:1.25rem;line-height:1;color:var(--lt-icon);background:transparent;border:none;border-radius:var(--lt-radius);cursor:pointer;transition:color .15s,background .15s}.lt-row-menu-trigger:hover{color:var(--lt-icon-active);background:var(--lt-row-hover)}.cdk-drag-preview{box-sizing:border-box;background-color:var(--lt-header-bg);color:var(--lt-header-text);box-shadow:var(--shadowLarge);padding:0 .625rem;display:flex;align-items:center;border-radius:var(--lt-radius)}.cdk-drag-placeholder{opacity:.3}.cdk-drag-animating{transition:transform .2s cubic-bezier(0,0,.2,1)}.lt-row{height:var(--lt-row-height);background-color:var(--lt-row-bg);color:var(--lt-cell-text)}.lt-row td{border-bottom:1px solid var(--lt-border)}.lt-row.lt-row-odd{background-color:var(--lt-row-alt-bg)}.lt-row:hover{background-color:var(--lt-row-hover)}.lt-row.lt-row-selected{background-color:var(--lt-row-selected)}.lt-row.lt-row-selected:hover{background-color:var(--lt-row-hover)}.lt-row.row-invalid{background-color:var(--invalid)!important}.lt-row.row-copied{background-color:var(--copiedRow)!important}.lt-cell{height:var(--lt-row-height);padding:0 .625rem 0 1.25rem;text-align:left;vertical-align:middle;border:none;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;-webkit-user-select:text;user-select:text}.lt-cell-text{display:inline-block;overflow:hidden;text-overflow:ellipsis;max-width:100%;vertical-align:middle}.lt-cell.lt-cell-center{text-align:center}.lt-badge-cell .lt-cell-text{padding:.1875rem .5rem;font-size:.75rem;font-weight:600;line-height:1.2;color:var(--lt-cell-text);background:color-mix(in srgb,var(--lt-border) 45%,transparent);border:1px solid var(--lt-border);border-radius:999px}.lt-badge-cell .lt-cell-text:empty{display:none}.lt-select-cell,.lt-menu-cell{width:3.75rem;padding:0;text-align:center;vertical-align:middle;overflow:visible;position:relative}.lt-toolbar{display:flex;align-items:center;justify-content:space-between;gap:.75rem;padding:0 0 .625rem;min-height:var(--lt-toolbar-height);position:relative;z-index:30}.lt-toolbar-left{display:flex;align-items:center;gap:.5rem;flex:1 1 auto;min-width:0;flex-wrap:nowrap}.lt-filter-zone{display:flex;align-items:center;flex-wrap:nowrap;gap:.5rem;flex:0 1 auto;min-width:0}.lt-filters{display:flex;align-items:center;flex-wrap:nowrap;gap:.5rem;min-width:0;flex:0 1 auto;overflow:hidden}.lt-filter-item{display:inline-flex;flex:0 0 auto}.lt-filter-more{position:relative;flex:0 0 auto}.lt-filter-more-btn{gap:.375rem;padding:0 .625rem}.lt-filter-more-btn .lt-filter-more-icon{display:inline-flex}.lt-filter-overflow{position:absolute;top:calc(100% + .25rem);right:0;z-index:1000;min-width:15rem;max-width:min(22rem,90vw);display:flex;flex-direction:column;gap:.5rem;padding:.625rem;background:var(--lt-control-bg);border:1px solid var(--lt-border);border-radius:var(--lt-radius);box-shadow:var(--shadowLarge);animation:lt-chooser-in .12s ease-out}.lt-filter-overflow-title{font-family:var(--lt-font);font-size:.6875rem;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--groupText)}.lt-filter-overflow-row{display:flex}.lt-filter-overflow-row lib-list-filter-cell,.lt-filter-overflow-row .lt-filter-item{flex:1 1 auto;min-width:0}.lt-toolbar-right{display:flex;align-items:center;gap:.75rem;flex:0 0 auto}.lt-divider{flex:0 0 auto;width:1px;height:1.5rem;background:var(--lt-border)}.lt-search{position:relative;display:flex;align-items:center;width:18rem;max-width:40vw;flex:0 1 18rem}.lt-search-icon{position:absolute;left:.625rem;display:block;color:var(--lt-icon);pointer-events:none}.lt-search-input{width:100%;height:var(--lt-toolbar-height);padding:0 .75rem 0 1.75rem;font-family:var(--lt-font);font-size:.8125rem;color:var(--lt-cell-text);background:var(--lt-control-bg);border:var(--inputBorderWidth) solid var(--lt-border);border-radius:var(--lt-radius);outline:none;transition:border-color .15s,box-shadow .15s}.lt-search-input::placeholder{color:var(--placeholder)}.lt-search-input:focus{border-color:var(--lt-link);box-shadow:0 0 0 .1875rem color-mix(in srgb,var(--lt-link) 18%,transparent)}.lt-count{font-size:.8125rem;color:var(--groupText);white-space:nowrap}.lt-count .lt-count-of{color:var(--groupText)}.lt-pager{display:flex;align-items:center;gap:.25rem}.lt-page-btn{display:inline-flex;align-items:center;justify-content:center;width:var(--lt-toolbar-height);height:var(--lt-toolbar-height);color:var(--lt-icon);background:var(--lt-control-bg);border:var(--inputBorderWidth) solid var(--lt-border);border-radius:var(--lt-radius);cursor:pointer;font-size:.9375rem;transition:border-color .15s,color .15s}.lt-page-btn:not(:disabled):hover{border-color:var(--lt-link);color:var(--lt-link)}.lt-page-btn:disabled{opacity:.4;cursor:default}.lt-refresh-btn{padding:0;width:var(--lt-toolbar-height);justify-content:center}.lt-refresh-btn:disabled{opacity:.5;cursor:default;border-color:var(--lt-border);color:var(--lt-cell-text)}.lt-columns{position:relative;flex:0 0 auto}.lt-column-chooser{position:absolute;top:calc(100% + .25rem);right:0;z-index:1000;width:15rem;max-height:calc(100vh - 9rem);display:flex;flex-direction:column;background:var(--lt-control-bg);border:1px solid var(--lt-border);border-radius:var(--lt-radius);box-shadow:var(--shadowLarge);overflow:hidden;animation:lt-chooser-in .12s ease-out}@keyframes lt-chooser-in{0%{opacity:0;transform:translateY(-.25rem)}to{opacity:1;transform:translateY(0)}}.lt-chooser-header{flex:0 0 auto;display:flex;flex-direction:column;padding:.625rem .75rem .5rem;border-bottom:1px solid var(--lt-border)}.lt-chooser-title{font-size:.8125rem;font-weight:700;color:var(--lt-cell-text)}.lt-chooser-subtitle{font-size:.6875rem;color:var(--groupText)}.lt-chooser-list{flex:1 1 auto;list-style:none;margin:0;padding:.375rem .5rem;min-height:0;overflow-y:auto}.lt-chooser-row{display:flex;align-items:center;justify-content:space-between;min-height:2.5rem;padding:.5rem;border-radius:var(--lt-radius);background:var(--lt-control-bg)}.lt-chooser-row:hover{background:var(--lt-row-hover)}.lt-chooser-row:hover .lt-chooser-handle{opacity:1}.lt-chooser-toggle{display:flex;align-items:center;gap:.625rem;flex:1 1 auto;min-width:0;padding:0;background:transparent;border:none;font:inherit;font-size:.875rem;color:var(--lt-cell-text);text-align:left;cursor:pointer}.lt-chooser-toggle:disabled{cursor:default;opacity:.5}.lt-chooser-box{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;box-sizing:border-box;width:1rem;height:1rem;border:.09375rem solid var(--lt-border);border-radius:.25rem;color:var(--lt-control-bg);background:var(--lt-control-bg);transition:background .12s,border-color .12s}.lt-chooser-toggle.checked .lt-chooser-box{background:var(--lt-link);border-color:var(--lt-link)}.lt-chooser-toggle:not(:disabled):hover .lt-chooser-box{border-color:var(--lt-link)}.lt-chooser-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.lt-chooser-default-tag{flex:0 0 auto;margin-left:.375rem;font-size:.6875rem;font-weight:400;font-style:italic;color:var(--groupText);white-space:nowrap}.lt-chooser-handle{flex:0 0 auto;display:inline-flex;align-items:center;color:var(--lt-icon);cursor:grab;padding-left:.5rem;-webkit-user-select:none;user-select:none}.lt-chooser-handle:active{cursor:grabbing}.lt-chooser-footer{flex:0 0 auto;padding:.5rem .75rem;border-top:1px solid var(--lt-border)}.lt-chooser-reset{width:100%;height:1.875rem;font-size:.75rem;font-weight:600;color:var(--lt-link);background:transparent;border:1px solid var(--lt-border);border-radius:var(--lt-radius);cursor:pointer}.lt-chooser-reset:disabled{color:var(--groupText);cursor:default;opacity:.6}.lt-chooser-reset:not(:disabled):hover{border-color:var(--lt-link)}.lt-footer{flex:0 0 auto;display:flex;align-items:center;justify-content:space-between;gap:1rem;flex-wrap:wrap;padding:.625rem .125rem 0;font-family:var(--lt-font);font-size:.8125rem;color:var(--groupText)}.lt-footer-count{white-space:nowrap}.lt-footer-count span{color:var(--lt-cell-text);font-weight:600}.lt-footer-controls{display:flex;align-items:center;gap:1.25rem;flex-wrap:nowrap}.lt-rpp{display:inline-flex;align-items:center;gap:.5rem;white-space:nowrap}.lt-rpp-label{text-transform:none;font-weight:400;color:var(--groupText)}.lt-rpp-select{width:auto;min-width:4rem;height:var(--lt-toolbar-height);padding:0 1.75rem 0 .625rem;font-family:var(--lt-font);font-size:.8125rem;color:var(--lt-cell-text);background-color:var(--lt-control-bg);background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='none' stroke='%236b7280' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round' d='M3 4.5 6 7.5 9 4.5'/%3E%3C/svg%3E\");background-repeat:no-repeat;background-position:right .5rem center;border:var(--inputBorderWidth) solid var(--lt-border);border-radius:var(--lt-radius);appearance:none;-webkit-appearance:none;cursor:pointer}.lt-rpp-select:focus{outline:none;border-color:var(--lt-link)}.lt-page-num{display:inline-flex;align-items:center;justify-content:center;min-width:var(--lt-toolbar-height);height:var(--lt-toolbar-height);padding:0 .5rem;font-family:var(--lt-font);font-size:.8125rem;font-weight:600;color:var(--lt-cell-text);background:var(--lt-control-bg);border:var(--inputBorderWidth) solid var(--lt-border);border-radius:var(--lt-radius);cursor:pointer;transition:border-color .15s,color .15s,background .15s}.lt-page-num:hover{border-color:var(--lt-link);color:var(--lt-link)}.lt-page-num.is-current{color:#fff;background:var(--lt-link);border-color:var(--lt-link);cursor:default}.lt-page-gap{display:inline-flex;align-items:center;justify-content:center;min-width:1.25rem;color:var(--groupText)}.lt-tooltip{position:fixed;z-index:1100;pointer-events:none;animation:lt-tooltip-in .12s ease-out}@keyframes lt-tooltip-in{0%{opacity:0;transform:translateY(-.1875rem)}to{opacity:1;transform:translateY(0)}}\n"] }]
|
|
12848
|
+
}], ctorParameters: () => [{ type: ListTableService }, { type: DataTableFilterService }, { type: i0.ElementRef }, { type: i0.NgZone }, { type: i1.DomSanitizer }], propDecorators: { tableId: [{
|
|
12849
|
+
type: Input
|
|
12850
|
+
}], columns: [{
|
|
12851
|
+
type: Input
|
|
12852
|
+
}], rowData: [{
|
|
12853
|
+
type: Input
|
|
12854
|
+
}], isLoading: [{
|
|
12855
|
+
type: Input
|
|
12856
|
+
}], options: [{
|
|
12857
|
+
type: Input
|
|
12858
|
+
}], startCount: [{
|
|
12859
|
+
type: Input
|
|
12860
|
+
}], endCount: [{
|
|
12861
|
+
type: Input
|
|
12862
|
+
}], totalCount: [{
|
|
12863
|
+
type: Input
|
|
12864
|
+
}], filterApplied: [{
|
|
12865
|
+
type: Input
|
|
12866
|
+
}], menuItems: [{
|
|
12867
|
+
type: Input
|
|
12868
|
+
}], headerActions: [{
|
|
12869
|
+
type: Input
|
|
12870
|
+
}], noItemsImage: [{
|
|
12871
|
+
type: Input
|
|
12872
|
+
}], showNoItemsAdd: [{
|
|
12873
|
+
type: Input
|
|
12874
|
+
}], noItemsReadonly: [{
|
|
12875
|
+
type: Input
|
|
12876
|
+
}], accessDenied: [{
|
|
12877
|
+
type: Input
|
|
12878
|
+
}], entityTypeName: [{
|
|
12879
|
+
type: Input
|
|
12880
|
+
}], filterName: [{
|
|
12881
|
+
type: Input
|
|
12882
|
+
}], filterColumn: [{
|
|
12883
|
+
type: Input
|
|
12884
|
+
}], actionRequested: [{
|
|
12885
|
+
type: Output
|
|
12886
|
+
}], tableReady: [{
|
|
12887
|
+
type: Output
|
|
12888
|
+
}], contextMenu: [{
|
|
12889
|
+
type: ViewChild,
|
|
12890
|
+
args: ['contextMenu']
|
|
12891
|
+
}], filterZone: [{
|
|
12892
|
+
type: ViewChild,
|
|
12893
|
+
args: ['filterZone']
|
|
12894
|
+
}], filtersRow: [{
|
|
12895
|
+
type: ViewChild,
|
|
12896
|
+
args: ['filtersRow']
|
|
12897
|
+
}] } });
|
|
12898
|
+
|
|
10772
12899
|
class Link {
|
|
10773
12900
|
constructor() {
|
|
10774
12901
|
this.statusReason = null;
|
|
@@ -12828,11 +14955,11 @@ class SideModalComponent {
|
|
|
12828
14955
|
this.openChange.emit(this.open);
|
|
12829
14956
|
}
|
|
12830
14957
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: SideModalComponent, deps: [{ token: ConsoleEventsService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
12831
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: SideModalComponent, isStandalone: false, selector: "lib-side-modal", inputs: { showClose: "showClose", open: "open" }, outputs: { openChange: "openChange" }, ngImport: i0, template: "<div class=\"side-modal-container\" [ngClass]=\"{'side-modal-open': open}\">\n @if (showClose) {\n <div class=\"close icon-close\" (click)=\"close()\"></div>\n }\n <ng-content></ng-content>\n</div>\n\n", styles: [".side-modal-container{position:fixed;right:-
|
|
14958
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: SideModalComponent, isStandalone: false, selector: "lib-side-modal", inputs: { showClose: "showClose", open: "open" }, outputs: { openChange: "openChange" }, ngImport: i0, template: "<div class=\"side-modal-container\" [ngClass]=\"{'side-modal-open': open}\">\n @if (showClose) {\n <div class=\"close icon-close\" (click)=\"close()\"></div>\n }\n <ng-content></ng-content>\n</div>\n\n", styles: [".side-modal-container{position:fixed;right:-42rem;top:0;padding:1.875rem;height:100%;width:30%;min-width:28.125rem;max-width:37.5rem;overflow-y:auto;transition:right .3s ease-in-out;z-index:999999;background-color:var(--formBackground);box-shadow:-.25rem 0 1.25rem #0003;border-left:.0625rem solid var(--tableBorder)}.side-modal-container.side-modal-open{right:0}.side-modal-container .close{position:absolute;top:1rem;right:1rem;font-size:1.5rem;cursor:pointer;color:var(--tableText);transition:all .2s ease;z-index:10}.side-modal-container .close:hover{color:var(--primary);transform:scale(1.1)}::ng-deep .sideNavClosed .side-modal-container.side-modal-open{right:0}\n"], dependencies: [{ kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] }); }
|
|
12832
14959
|
}
|
|
12833
14960
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: SideModalComponent, decorators: [{
|
|
12834
14961
|
type: Component,
|
|
12835
|
-
args: [{ selector: 'lib-side-modal', standalone: false, template: "<div class=\"side-modal-container\" [ngClass]=\"{'side-modal-open': open}\">\n @if (showClose) {\n <div class=\"close icon-close\" (click)=\"close()\"></div>\n }\n <ng-content></ng-content>\n</div>\n\n", styles: [".side-modal-container{position:fixed;right:-
|
|
14962
|
+
args: [{ selector: 'lib-side-modal', standalone: false, template: "<div class=\"side-modal-container\" [ngClass]=\"{'side-modal-open': open}\">\n @if (showClose) {\n <div class=\"close icon-close\" (click)=\"close()\"></div>\n }\n <ng-content></ng-content>\n</div>\n\n", styles: [".side-modal-container{position:fixed;right:-42rem;top:0;padding:1.875rem;height:100%;width:30%;min-width:28.125rem;max-width:37.5rem;overflow-y:auto;transition:right .3s ease-in-out;z-index:999999;background-color:var(--formBackground);box-shadow:-.25rem 0 1.25rem #0003;border-left:.0625rem solid var(--tableBorder)}.side-modal-container.side-modal-open{right:0}.side-modal-container .close{position:absolute;top:1rem;right:1rem;font-size:1.5rem;cursor:pointer;color:var(--tableText);transition:all .2s ease;z-index:10}.side-modal-container .close:hover{color:var(--primary);transform:scale(1.1)}::ng-deep .sideNavClosed .side-modal-container.side-modal-open{right:0}\n"] }]
|
|
12836
14963
|
}], ctorParameters: () => [{ type: ConsoleEventsService }], propDecorators: { showClose: [{
|
|
12837
14964
|
type: Input
|
|
12838
14965
|
}], open: [{
|
|
@@ -13317,6 +15444,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
13317
15444
|
See the License for the specific language governing permissions and
|
|
13318
15445
|
limitations under the License.
|
|
13319
15446
|
*/
|
|
15447
|
+
/**
|
|
15448
|
+
* Legacy Identities page (ag-grid `lib-data-table`). Selected by the route when the
|
|
15449
|
+
* "Beta Features" flag is off. Slated for removal once the new list-table page (see
|
|
15450
|
+
* {@link IdentitiesListPageComponent}) graduates — at which point this file and its
|
|
15451
|
+
* template are deleted outright.
|
|
15452
|
+
*/
|
|
13320
15453
|
class IdentitiesPageComponent extends ListPageComponent {
|
|
13321
15454
|
constructor(svc, filterService, dialogForm, tabNames, router, consoleEvents, extService) {
|
|
13322
15455
|
super(filterService, svc, consoleEvents, dialogForm, extService);
|
|
@@ -13513,6 +15646,229 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
13513
15646
|
args: [IDENTITY_EXTENSION_SERVICE]
|
|
13514
15647
|
}] }] });
|
|
13515
15648
|
|
|
15649
|
+
/*
|
|
15650
|
+
Copyright NetFoundry Inc.
|
|
15651
|
+
|
|
15652
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
15653
|
+
you may not use this file except in compliance with the License.
|
|
15654
|
+
You may obtain a copy of the License at
|
|
15655
|
+
|
|
15656
|
+
https://www.apache.org/licenses/LICENSE-2.0
|
|
15657
|
+
|
|
15658
|
+
Unless required by applicable law or agreed to in writing, software
|
|
15659
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
15660
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15661
|
+
See the License for the specific language governing permissions and
|
|
15662
|
+
limitations under the License.
|
|
15663
|
+
*/
|
|
15664
|
+
/**
|
|
15665
|
+
* New Identities page, rendered on the shared `lib-list-table`. Extends the common
|
|
15666
|
+
* {@link ListPageComponent} (which future list-table pages extend the same way).
|
|
15667
|
+
* Selected by the route when the "Beta Features" flag is on; the legacy ag-grid page
|
|
15668
|
+
* ({@link IdentitiesPageComponent}) handles the flag-off case. The two pages carry
|
|
15669
|
+
* their own copies of the identity actions during the migration so the legacy one can
|
|
15670
|
+
* be deleted outright once this graduates.
|
|
15671
|
+
*/
|
|
15672
|
+
class IdentitiesListPageComponent extends ListPageComponent {
|
|
15673
|
+
constructor(svc, filterService, dialogForm, tabNames, router, consoleEvents, extService) {
|
|
15674
|
+
super(filterService, svc, consoleEvents, dialogForm, extService);
|
|
15675
|
+
this.svc = svc;
|
|
15676
|
+
this.tabNames = tabNames;
|
|
15677
|
+
this.router = router;
|
|
15678
|
+
this.extService = extService;
|
|
15679
|
+
this.title = 'Identities';
|
|
15680
|
+
this.isLoading = false;
|
|
15681
|
+
this.formDataChanged = false;
|
|
15682
|
+
}
|
|
15683
|
+
/** The new table consumes the declarative ListColumn set. */
|
|
15684
|
+
loadTableColumns() {
|
|
15685
|
+
return this.svc.initListTableColumns();
|
|
15686
|
+
}
|
|
15687
|
+
ngOnInit() {
|
|
15688
|
+
this.tabs = this.tabNames.getTabs('identities');
|
|
15689
|
+
this.svc.refreshData = this.refreshData;
|
|
15690
|
+
this.subscription.add(this.router.events.subscribe((event) => {
|
|
15691
|
+
if (event instanceof NavigationEnd) {
|
|
15692
|
+
this.refreshData();
|
|
15693
|
+
}
|
|
15694
|
+
}));
|
|
15695
|
+
this.svc.getIdentityRoleAttributes();
|
|
15696
|
+
super.ngOnInit();
|
|
15697
|
+
}
|
|
15698
|
+
headerActionClicked(action) {
|
|
15699
|
+
switch (action) {
|
|
15700
|
+
case 'add':
|
|
15701
|
+
this.svc.openEditForm();
|
|
15702
|
+
break;
|
|
15703
|
+
case 'edit':
|
|
15704
|
+
this.svc.openUpdate();
|
|
15705
|
+
break;
|
|
15706
|
+
case 'delete':
|
|
15707
|
+
const selectedItems = this.rowData.filter((row) => {
|
|
15708
|
+
return row.selected;
|
|
15709
|
+
});
|
|
15710
|
+
const label = selectedItems.length > 1 ? 'identities' : 'identity';
|
|
15711
|
+
this.openBulkDelete(selectedItems, label);
|
|
15712
|
+
break;
|
|
15713
|
+
default:
|
|
15714
|
+
}
|
|
15715
|
+
}
|
|
15716
|
+
closeModal(event) {
|
|
15717
|
+
this.svc.sideModalOpen = false;
|
|
15718
|
+
if (event?.refresh) {
|
|
15719
|
+
this.refreshData();
|
|
15720
|
+
this.svc.getIdentityRoleAttributes();
|
|
15721
|
+
}
|
|
15722
|
+
}
|
|
15723
|
+
dataChanged(event) {
|
|
15724
|
+
this.formDataChanged = event;
|
|
15725
|
+
}
|
|
15726
|
+
tableAction(event) {
|
|
15727
|
+
const extensionActionFound = this.handleExtensionActions(event);
|
|
15728
|
+
this.trackMenuAction(event?.action, event?.item);
|
|
15729
|
+
if (extensionActionFound) {
|
|
15730
|
+
return;
|
|
15731
|
+
}
|
|
15732
|
+
switch (event?.action) {
|
|
15733
|
+
case 'toggleAll':
|
|
15734
|
+
this.itemToggled(event.item);
|
|
15735
|
+
this.rowData.forEach((item) => {
|
|
15736
|
+
if (item.typeId === 'Router') {
|
|
15737
|
+
item.selected = false;
|
|
15738
|
+
}
|
|
15739
|
+
});
|
|
15740
|
+
break;
|
|
15741
|
+
case 'toggleItem':
|
|
15742
|
+
this.itemToggled(event.item);
|
|
15743
|
+
break;
|
|
15744
|
+
case 'update':
|
|
15745
|
+
this.svc.openEditForm(event?.item?.id);
|
|
15746
|
+
break;
|
|
15747
|
+
case 'create':
|
|
15748
|
+
this.svc.openEditForm();
|
|
15749
|
+
break;
|
|
15750
|
+
case 'override':
|
|
15751
|
+
this.svc.openOverridesModal(event.item);
|
|
15752
|
+
break;
|
|
15753
|
+
case 'delete':
|
|
15754
|
+
this.deleteItem(event.item);
|
|
15755
|
+
break;
|
|
15756
|
+
case 'download-enrollment':
|
|
15757
|
+
this.downloadJWT(event.item);
|
|
15758
|
+
break;
|
|
15759
|
+
case 'reset-enrollment':
|
|
15760
|
+
this.resetEnrollment(event.item);
|
|
15761
|
+
break;
|
|
15762
|
+
case 'reissue-enrollment':
|
|
15763
|
+
this.reissueEnrollment(event.item);
|
|
15764
|
+
break;
|
|
15765
|
+
case "identity-service-path":
|
|
15766
|
+
this.openIdentityVisualizer(event.item);
|
|
15767
|
+
break;
|
|
15768
|
+
case "qr-code":
|
|
15769
|
+
this.showQRCode(event.item);
|
|
15770
|
+
break;
|
|
15771
|
+
case 'download-all':
|
|
15772
|
+
this.downloadAllItems();
|
|
15773
|
+
break;
|
|
15774
|
+
case 'download-selected':
|
|
15775
|
+
this.svc.downloadItems(this.selectedItems);
|
|
15776
|
+
break;
|
|
15777
|
+
case 'reset-mfa':
|
|
15778
|
+
this.isLoading = true;
|
|
15779
|
+
this.svc.resetMFA(event.item).finally(() => {
|
|
15780
|
+
this.isLoading = false;
|
|
15781
|
+
this.refreshData();
|
|
15782
|
+
});
|
|
15783
|
+
break;
|
|
15784
|
+
default:
|
|
15785
|
+
break;
|
|
15786
|
+
}
|
|
15787
|
+
}
|
|
15788
|
+
downloadJWT(item) {
|
|
15789
|
+
const jwt = this.svc.getJWT(item);
|
|
15790
|
+
this.svc.downloadJWT(jwt, item.name);
|
|
15791
|
+
}
|
|
15792
|
+
showQRCode(item) {
|
|
15793
|
+
const data = {
|
|
15794
|
+
jwt: this.svc.getJWT(item),
|
|
15795
|
+
expiration: this.svc.getEnrollmentExpiration(item),
|
|
15796
|
+
qrCodeSize: 500,
|
|
15797
|
+
identity: item,
|
|
15798
|
+
qrOnly: true,
|
|
15799
|
+
};
|
|
15800
|
+
this.dialogRef = this.dialogForm.open(QrCodeComponent, {
|
|
15801
|
+
data: data,
|
|
15802
|
+
autoFocus: false,
|
|
15803
|
+
});
|
|
15804
|
+
}
|
|
15805
|
+
trackMenuAction(action, item) {
|
|
15806
|
+
this.extService?.consoleActionTriggered?.emit({
|
|
15807
|
+
action,
|
|
15808
|
+
page: 'identity',
|
|
15809
|
+
item,
|
|
15810
|
+
type: 'menu-action'
|
|
15811
|
+
});
|
|
15812
|
+
}
|
|
15813
|
+
deleteItem(item) {
|
|
15814
|
+
this.openBulkDelete([item], 'identity');
|
|
15815
|
+
}
|
|
15816
|
+
openIdentityVisualizer(identity) {
|
|
15817
|
+
this.dialogRef = this.dialogForm.open(IdentityServicePathComponent, {
|
|
15818
|
+
data: {
|
|
15819
|
+
identity: identity,
|
|
15820
|
+
},
|
|
15821
|
+
autoFocus: false,
|
|
15822
|
+
});
|
|
15823
|
+
}
|
|
15824
|
+
resetEnrollment(identity) {
|
|
15825
|
+
this.dialogRef = this.dialogForm.open(ResetEnrollmentComponent, {
|
|
15826
|
+
data: {
|
|
15827
|
+
identity: identity,
|
|
15828
|
+
type: 'reset',
|
|
15829
|
+
},
|
|
15830
|
+
autoFocus: false,
|
|
15831
|
+
});
|
|
15832
|
+
this.dialogRef.afterClosed().subscribe((result) => {
|
|
15833
|
+
if (result) {
|
|
15834
|
+
this.refreshData(undefined, true);
|
|
15835
|
+
}
|
|
15836
|
+
});
|
|
15837
|
+
}
|
|
15838
|
+
reissueEnrollment(identity) {
|
|
15839
|
+
this.dialogRef = this.dialogForm.open(ResetEnrollmentComponent, {
|
|
15840
|
+
data: {
|
|
15841
|
+
identity: identity,
|
|
15842
|
+
type: 'reissue',
|
|
15843
|
+
},
|
|
15844
|
+
autoFocus: false,
|
|
15845
|
+
});
|
|
15846
|
+
this.dialogRef.afterClosed().subscribe((result) => {
|
|
15847
|
+
if (result) {
|
|
15848
|
+
this.refreshData(undefined, true);
|
|
15849
|
+
}
|
|
15850
|
+
});
|
|
15851
|
+
}
|
|
15852
|
+
ngOnDestroy() {
|
|
15853
|
+
super.ngOnDestroy();
|
|
15854
|
+
}
|
|
15855
|
+
canDeactivate() {
|
|
15856
|
+
if (this.formDataChanged && this.svc.sideModalOpen) {
|
|
15857
|
+
return confirm('You have unsaved changes. Do you want to leave this page and discard your changes or stay on this page?');
|
|
15858
|
+
}
|
|
15859
|
+
return true;
|
|
15860
|
+
}
|
|
15861
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: IdentitiesListPageComponent, deps: [{ token: IdentitiesPageService }, { token: DataTableFilterService }, { token: i5.MatDialog }, { token: TabNameService }, { token: i4.Router }, { token: ConsoleEventsService }, { token: IDENTITY_EXTENSION_SERVICE }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
15862
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: IdentitiesListPageComponent, isStandalone: false, selector: "lib-identities-list", usesInheritance: true, ngImport: i0, template: "<div class=\"ziti-page-container ziti-identities-container lt-skin-page\">\n <lib-list-table-header\n [title]=\"title\"\n subtitle=\"Manage identities across the network\"\n createLabel=\"Create Identity\"\n infoText=\"Identities are the clients, services, and routers enrolled in this network.\"\n [tabs]=\"tabs\"\n [showAdd]=\"!itemsSelected\"\n [blockCreate]=\"headerBlockCreate\"\n [blockDelete]=\"headerBlockDelete\"\n (actionClicked)=\"headerActionClicked($event)\"\n >\n <span header-icon class=\"icon-identity\" aria-hidden=\"true\"></span>\n </lib-list-table-header>\n <ng-content select=\"[slot=list-page-subheader]\"></ng-content>\n <lib-list-table class=\"lt-skin\" [tableId]=\"'identities'\"\n [noItemsReadonly]=\"headerBlockCreate\"\n [accessDenied]=\"headerBlockRead\"\n [rowData]=\"rowData\"\n [isLoading]=\"isLoading\"\n [columns]=\"columnDefs\"\n (actionRequested)=\"tableAction($event)\"\n [startCount]=\"startCount\"\n [endCount]=\"endCount\"\n [totalCount]=\"totalCount\"\n [filterApplied]=\"filterApplied\"\n [menuItems]=\"svc.menuItems\"\n [headerActions]=\"svc.tableHeaderActions\"\n [noItemsImage]=\"'No_Clients'\"\n (tableReady)=\"tableReady($event)\"\n >\n </lib-list-table>\n</div>\n<lib-side-modal [(open)]=\"svc.sideModalOpen\" [showClose]=\"false\">\n @if (svc.modalType === 'overrides' && svc.sideModalOpen) {\n <lib-overrides\n [identity]=\"svc.selectedIdentity\"\n (close)=\"closeModal($event)\"\n ></lib-overrides>\n }\n</lib-side-modal>\n@if (isLoading) {\n <lib-loading-indicator [isLoading]=\"isLoading\"></lib-loading-indicator>\n}\n", styles: [".ziti-page-container .action{line-height:3.75rem;top:1.5625rem;right:1.5625rem;z-index:999}.ziti-identities-container ::ng-deep .zac-wrapper-container{height:0rem;left:-62499.9375rem;position:fixed}.ziti-identities-container ::ng-deep .zac-wrapper-container .action{display:none}.ziti-ag-grid{height:100%;position:relative;width:100%}.ziti-ag-grid .ziti-ag-grid-table{height:100%;width:100%}.ziti-ag-grid .ziti-ag-grid-table .ag-root-wrapper{border-bottom-style:none}.ziti-ag-grid .ziti-ag-grid-table .ag-cell{align-items:center;display:flex}.system-policies-toggle,.hidden-results-container{position:relative;right:0;display:flex;flex-direction:row;justify-content:center;align-items:flex-start;margin-top:.625rem;padding-left:1.25rem;border-radius:var(--inputBorderRadius);padding-bottom:0;padding-right:0;padding-top:0;flex-wrap:nowrap;flex:1 0 auto}.system-policies-toggle .toggle-text,.hidden-results-container .toggle-text{font-weight:600;font-size:.75rem}.system-policies-toggle .toggle-text a,.hidden-results-container .toggle-text a{cursor:pointer}.system-policies-toggle .toggle,.hidden-results-container .toggle{width:4.6875rem;margin-left:.625rem}.system-policies-toggle .toggle.on .switch,.hidden-results-container .toggle.on .switch{left:3.25rem}.system-policies-toggle .spinner,.hidden-results-container .spinner{display:inline-block;width:.625rem;height:.625rem;border-radius:50%;background:transparent;border-top:.125rem solid var(--tableText);border-right:.125rem solid var(--tableText);border-bottom:.125rem solid transparent;border-left:.125rem solid transparent;animation:loading .5s infinite linear;margin-left:.3125rem}.hidden-results-container.loading-results{opacity:.7}.nf-cell-vert-align,.ag-row .ag-cell{align-items:center;display:flex}.hidden-columns-list{align-items:center;display:flex;flex-direction:row;margin-left:.4375rem;min-height:1.875rem;flex-wrap:wrap;flex:0 1 auto}.hidden-columns-list.hidden-results{flex-wrap:nowrap;overflow:auto}.hidden-columns-list.hidden-results .hidden-column-item{flex:1 0 auto}.hidden-columns-list::-webkit-scrollbar{height:.875rem}.hidden-columns-list .hidden-column-item{align-items:center;background-color:var(--background);border:.125rem solid var(--background);border-radius:.75rem;cursor:pointer;color:var(----stroke);font-weight:600;font-family:Open Sans,sans-serif;display:flex;flex:0 1 auto;height:1.5625rem!important;margin-left:.625rem;margin-top:.3125rem;padding:.125rem .625rem .125rem .3125rem;position:relative;text-align:center;box-shadow:unset;transform:translateY(0)}.hidden-columns-list .ag-dnd-ghost:hover{border:.125rem solid var(--primaryColor)!important}.hidden-columns-list .ag-dnd-ghost:hover .ag-dnd-ghost-label,.hidden-columns-list .ag-dnd-ghost:hover .ag-dnd-ghost-icon .ag-icon-eye-slash{color:var(--ag-alpine-active-color, var(--primaryColor))}.hidden-columns-list .ag-dnd-ghost:hover .ag-dnd-ghost-icon .ag-icon-eye-slash:before{content:\"\\f113\"}.hidden-columns-list .ag-dnd-ghost-label{line-height:initial}::ng-deep .darkmode .ag-dnd-ghost-icon .ag-icon-eye-slash{color:#fff!important}.tMenu{transition-property:opacity;z-index:99999999999;height:fit-content}.tMenu.action-menu{position:fixed;width:10.9375rem}.tMenu .tActionRow{transition-property:opacity}.tMenu .tActionRow.has-icon{display:flex;flex-direction:row;align-items:center}.tMenu .tActionRow .v7Offline{background-color:var(--placeholder)}.tMenu .tActionRow .v7OnlineOffline{width:.875rem;height:.875rem;border-color:var(--placeholder)}.tMenu .tActionRow .v7OnlineOffline:hover{border-color:var(--placeholder)}.hidden-items-container{display:flex;flex-direction:row;width:100%;flex-wrap:nowrap;justify-content:space-between}.os-icon{width:1.4375rem;height:1.4375rem;background-size:1.4375rem 1.4375rem;display:inline-flex;margin-right:.5rem;margin-top:.3125rem;margin-bottom:.3125rem}::ng-deep .date-time-container{position:fixed}::ng-deep .date-time-container .p-calendar .p-inputtext{height:.0625rem;width:.0625rem;padding:0}::ng-deep .date-time-container .filter-button-container{height:3.125rem;display:flex;flex-direction:row;justify-content:center;align-items:center}::ng-deep .date-time-container .filter-button-container .toggleButton{padding-left:.3125rem;padding-right:.3125rem;width:unset}::ng-deep .date-time-container .p-calendar{height:1.875rem}::ng-deep .date-time-container .p-calendar .p-inputtext{height:1.875rem;border-top-left-radius:1.25rem;border-bottom-left-radius:1.25rem;text-indent:.3125rem}::ng-deep .date-time-container .p-calendar .p-button-icon-only{border-top-right-radius:1.25rem;border-bottom-right-radius:1.25rem}#TimeSearch{position:relative;float:left;display:inline-block;margin-left:.625rem;width:3.125rem;height:2.5rem;background-color:var(--primary)}#TimeSearch #TimeSearchList{width:2.75rem;height:0;position:absolute;top:0;left:-.125rem;overflow:hidden;transition:var(--transition);background-color:var(--menu);border-radius:.5rem;z-index:30}#TimeSearch #TimeSearchList .time{width:2.5rem;background-color:var(--background);padding-left:.5rem;padding-right:.5rem;border-radius:.5rem;line-height:1.625rem;font-size:.75rem;font-weight:700;text-align:center;margin-top:.125rem;margin-left:.125rem;left:-.125rem;top:-3.1875rem;cursor:pointer;transition:var(--transition)}#TimeSearch #TimeSearchList.open{top:-3.1875rem;width:2.75rem;height:14.125rem}#TimeSearch #TimeSelectButton{width:2.5rem;background-color:var(--background);padding-left:.5rem;padding-right:.5rem;border-radius:.5rem;line-height:1.625rem;text-align:center;font-size:.75rem;font-weight:700;margin-top:.4375rem;cursor:pointer;transition:var(--transition)}#TimeSearch #TimeSelectButton:hover,#TimeSearch #TimeSearchList .time:hover{background-color:var(--primaryColor);color:var(--white)}::ng-deep p-togglebutton .p-togglebutton.p-button{border-radius:var(--inputBorderRadius)!important;box-shadow:0 .3125rem 1.25rem #0000000d!important;margin-top:-.125rem!important}::ng-deep p-togglebutton .p-togglebutton.p-button:hover{filter:brightness(99%)!important;box-shadow:0 .3125rem 1.25rem #0000000d!important;transition:all .3s ease}::ng-deep p-togglebutton .p-togglebutton.p-button:active{filter:brightness(95%)!important;box-shadow:none!important;transform:translateY(.0625rem)!important;transition:all .3s ease}#TimeSearch #TimeSelectButton{margin-top:.25rem!important}::ng-deep .p-calendar .p-inputtext{height:2.3125rem}::ng-deep .p-inputtext{border-radius:var(--inputBorderRadius)}.calendar{display:inline-block;padding-left:.3125rem;float:left;padding-right:.625rem;margin-top:-.125rem}::ng-deep .p-datepicker:not(.p-datepicker-inline){background:var(--table);border:0 none;box-shadow:#091e4226 0 1.25rem 2rem -.5rem;border-radius:.75rem}::ng-deep .p-button.p-button-text{background-color:transparent;color:var(--primaryColor);border-color:transparent}::ng-deep .p-button.p-button-text:enabled:hover{background:var(--primaryColorShadowLight);color:var(--primaryColor);border-color:transparent}::ng-deep .p-datepicker table td>span.p-highlight{color:var(--tableText);background:var(--primaryColorShadowLight)}::ng-deep .p-inputtext:enabled:hover{border-color:var(--primaryColor)}::ng-deep .p-datepicker{padding:.5rem;padding-bottom:0;background:var(--table);color:var(--tableText);border:.0625rem solid var(--inputBorder);border-radius:.1875rem}::ng-deep .p-inputtext:enabled:focus{outline:0 none;outline-offset:0;box-shadow:0 0 0 1 var(--primaryColorShadowLight);border-color:var(--primaryColor)}::ng-deep .p-inputtext{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\",Segoe UI Symbol;font-size:1rem;color:var(--tableText);background:var(--table);padding:.5rem;border:.0625rem solid var(--inputBorder);transition:background-color .2s,color .2s,border-color .2s,box-shadow .2s;-webkit-appearance:none;-moz-appearance:none;appearance:none;border-radius:var(--inputBorderRadius)}::ng-deep .p-datepicker:not(.p-datepicker-inline) .p-datepicker-header{background:var(--table)}::ng-deep .p-datepicker .p-datepicker-header{padding:.5rem;color:var(--tableText);background:var(--table);font-weight:600;margin:0;border-bottom:.0625rem solid var(--inputBorder);border-top-right-radius:.75rem;border-top-left-radius:.75rem}::ng-deep .p-datepicker:not(.p-disabled) table td span:not(.p-highlight):not(.p-disabled):hover{background:#e9ecef;color:var(--table)}::ng-deep .p-datepicker .p-timepicker{border-top:.0625rem solid var(--background);padding:.5rem}::ng-deep .p-togglebutton.p-button{background:var(--background);border:.0625rem solid var(--inputBorder);color:var(--tableText);transition:background-color .2s,color .2s,border-color .2s,box-shadow .2s}::ng-deep .p-datepicker .p-datepicker-buttonbar{padding:1rem 0;border-top:.0625rem solid var(--inputBorder)}::ng-deep .p-button{color:#fff;background:var(--primaryColor);border:.0625rem solid var(--primaryColor);padding:.495rem 1rem;font-size:1rem;transition:background-color .2s,color .2s,border-color .2s,box-shadow .2s;border-radius:var(--inputBorderRadius)}::ng-deep .p-togglebutton.p-button.p-highlight{background:var(--primaryColor);border:.0625rem solid var(--primaryColor);color:var(--background)}::ng-deep .p-togglebutton.p-button.p-highlight:hover{background:var(--primaryColor);border:.0625rem solid var(--primaryColor);color:var(--background)}app-zac-wrapper{z-index:999999}\n"], dependencies: [{ kind: "component", type: ListTableHeaderComponent, selector: "lib-list-table-header", inputs: ["title", "subtitle", "createLabel", "infoText", "tabs", "showAdd", "blockCreate", "blockDelete"], outputs: ["actionClicked"] }, { kind: "component", type: ListTableComponent, selector: "lib-list-table", inputs: ["tableId", "columns", "rowData", "isLoading", "options", "startCount", "endCount", "totalCount", "filterApplied", "menuItems", "headerActions", "noItemsImage", "showNoItemsAdd", "noItemsReadonly", "accessDenied", "entityTypeName", "filterName", "filterColumn"], outputs: ["actionRequested", "tableReady"] }, { kind: "component", type: SideModalComponent, selector: "lib-side-modal", inputs: ["showClose", "open"], outputs: ["openChange"] }, { kind: "component", type: LoadingIndicatorComponent, selector: "lib-loading-indicator", inputs: ["isLoading"] }, { kind: "component", type: OverridesComponent, selector: "lib-overrides", inputs: ["identity"], outputs: ["close"] }] }); }
|
|
15863
|
+
}
|
|
15864
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: IdentitiesListPageComponent, decorators: [{
|
|
15865
|
+
type: Component,
|
|
15866
|
+
args: [{ selector: 'lib-identities-list', standalone: false, template: "<div class=\"ziti-page-container ziti-identities-container lt-skin-page\">\n <lib-list-table-header\n [title]=\"title\"\n subtitle=\"Manage identities across the network\"\n createLabel=\"Create Identity\"\n infoText=\"Identities are the clients, services, and routers enrolled in this network.\"\n [tabs]=\"tabs\"\n [showAdd]=\"!itemsSelected\"\n [blockCreate]=\"headerBlockCreate\"\n [blockDelete]=\"headerBlockDelete\"\n (actionClicked)=\"headerActionClicked($event)\"\n >\n <span header-icon class=\"icon-identity\" aria-hidden=\"true\"></span>\n </lib-list-table-header>\n <ng-content select=\"[slot=list-page-subheader]\"></ng-content>\n <lib-list-table class=\"lt-skin\" [tableId]=\"'identities'\"\n [noItemsReadonly]=\"headerBlockCreate\"\n [accessDenied]=\"headerBlockRead\"\n [rowData]=\"rowData\"\n [isLoading]=\"isLoading\"\n [columns]=\"columnDefs\"\n (actionRequested)=\"tableAction($event)\"\n [startCount]=\"startCount\"\n [endCount]=\"endCount\"\n [totalCount]=\"totalCount\"\n [filterApplied]=\"filterApplied\"\n [menuItems]=\"svc.menuItems\"\n [headerActions]=\"svc.tableHeaderActions\"\n [noItemsImage]=\"'No_Clients'\"\n (tableReady)=\"tableReady($event)\"\n >\n </lib-list-table>\n</div>\n<lib-side-modal [(open)]=\"svc.sideModalOpen\" [showClose]=\"false\">\n @if (svc.modalType === 'overrides' && svc.sideModalOpen) {\n <lib-overrides\n [identity]=\"svc.selectedIdentity\"\n (close)=\"closeModal($event)\"\n ></lib-overrides>\n }\n</lib-side-modal>\n@if (isLoading) {\n <lib-loading-indicator [isLoading]=\"isLoading\"></lib-loading-indicator>\n}\n", styles: [".ziti-page-container .action{line-height:3.75rem;top:1.5625rem;right:1.5625rem;z-index:999}.ziti-identities-container ::ng-deep .zac-wrapper-container{height:0rem;left:-62499.9375rem;position:fixed}.ziti-identities-container ::ng-deep .zac-wrapper-container .action{display:none}.ziti-ag-grid{height:100%;position:relative;width:100%}.ziti-ag-grid .ziti-ag-grid-table{height:100%;width:100%}.ziti-ag-grid .ziti-ag-grid-table .ag-root-wrapper{border-bottom-style:none}.ziti-ag-grid .ziti-ag-grid-table .ag-cell{align-items:center;display:flex}.system-policies-toggle,.hidden-results-container{position:relative;right:0;display:flex;flex-direction:row;justify-content:center;align-items:flex-start;margin-top:.625rem;padding-left:1.25rem;border-radius:var(--inputBorderRadius);padding-bottom:0;padding-right:0;padding-top:0;flex-wrap:nowrap;flex:1 0 auto}.system-policies-toggle .toggle-text,.hidden-results-container .toggle-text{font-weight:600;font-size:.75rem}.system-policies-toggle .toggle-text a,.hidden-results-container .toggle-text a{cursor:pointer}.system-policies-toggle .toggle,.hidden-results-container .toggle{width:4.6875rem;margin-left:.625rem}.system-policies-toggle .toggle.on .switch,.hidden-results-container .toggle.on .switch{left:3.25rem}.system-policies-toggle .spinner,.hidden-results-container .spinner{display:inline-block;width:.625rem;height:.625rem;border-radius:50%;background:transparent;border-top:.125rem solid var(--tableText);border-right:.125rem solid var(--tableText);border-bottom:.125rem solid transparent;border-left:.125rem solid transparent;animation:loading .5s infinite linear;margin-left:.3125rem}.hidden-results-container.loading-results{opacity:.7}.nf-cell-vert-align,.ag-row .ag-cell{align-items:center;display:flex}.hidden-columns-list{align-items:center;display:flex;flex-direction:row;margin-left:.4375rem;min-height:1.875rem;flex-wrap:wrap;flex:0 1 auto}.hidden-columns-list.hidden-results{flex-wrap:nowrap;overflow:auto}.hidden-columns-list.hidden-results .hidden-column-item{flex:1 0 auto}.hidden-columns-list::-webkit-scrollbar{height:.875rem}.hidden-columns-list .hidden-column-item{align-items:center;background-color:var(--background);border:.125rem solid var(--background);border-radius:.75rem;cursor:pointer;color:var(----stroke);font-weight:600;font-family:Open Sans,sans-serif;display:flex;flex:0 1 auto;height:1.5625rem!important;margin-left:.625rem;margin-top:.3125rem;padding:.125rem .625rem .125rem .3125rem;position:relative;text-align:center;box-shadow:unset;transform:translateY(0)}.hidden-columns-list .ag-dnd-ghost:hover{border:.125rem solid var(--primaryColor)!important}.hidden-columns-list .ag-dnd-ghost:hover .ag-dnd-ghost-label,.hidden-columns-list .ag-dnd-ghost:hover .ag-dnd-ghost-icon .ag-icon-eye-slash{color:var(--ag-alpine-active-color, var(--primaryColor))}.hidden-columns-list .ag-dnd-ghost:hover .ag-dnd-ghost-icon .ag-icon-eye-slash:before{content:\"\\f113\"}.hidden-columns-list .ag-dnd-ghost-label{line-height:initial}::ng-deep .darkmode .ag-dnd-ghost-icon .ag-icon-eye-slash{color:#fff!important}.tMenu{transition-property:opacity;z-index:99999999999;height:fit-content}.tMenu.action-menu{position:fixed;width:10.9375rem}.tMenu .tActionRow{transition-property:opacity}.tMenu .tActionRow.has-icon{display:flex;flex-direction:row;align-items:center}.tMenu .tActionRow .v7Offline{background-color:var(--placeholder)}.tMenu .tActionRow .v7OnlineOffline{width:.875rem;height:.875rem;border-color:var(--placeholder)}.tMenu .tActionRow .v7OnlineOffline:hover{border-color:var(--placeholder)}.hidden-items-container{display:flex;flex-direction:row;width:100%;flex-wrap:nowrap;justify-content:space-between}.os-icon{width:1.4375rem;height:1.4375rem;background-size:1.4375rem 1.4375rem;display:inline-flex;margin-right:.5rem;margin-top:.3125rem;margin-bottom:.3125rem}::ng-deep .date-time-container{position:fixed}::ng-deep .date-time-container .p-calendar .p-inputtext{height:.0625rem;width:.0625rem;padding:0}::ng-deep .date-time-container .filter-button-container{height:3.125rem;display:flex;flex-direction:row;justify-content:center;align-items:center}::ng-deep .date-time-container .filter-button-container .toggleButton{padding-left:.3125rem;padding-right:.3125rem;width:unset}::ng-deep .date-time-container .p-calendar{height:1.875rem}::ng-deep .date-time-container .p-calendar .p-inputtext{height:1.875rem;border-top-left-radius:1.25rem;border-bottom-left-radius:1.25rem;text-indent:.3125rem}::ng-deep .date-time-container .p-calendar .p-button-icon-only{border-top-right-radius:1.25rem;border-bottom-right-radius:1.25rem}#TimeSearch{position:relative;float:left;display:inline-block;margin-left:.625rem;width:3.125rem;height:2.5rem;background-color:var(--primary)}#TimeSearch #TimeSearchList{width:2.75rem;height:0;position:absolute;top:0;left:-.125rem;overflow:hidden;transition:var(--transition);background-color:var(--menu);border-radius:.5rem;z-index:30}#TimeSearch #TimeSearchList .time{width:2.5rem;background-color:var(--background);padding-left:.5rem;padding-right:.5rem;border-radius:.5rem;line-height:1.625rem;font-size:.75rem;font-weight:700;text-align:center;margin-top:.125rem;margin-left:.125rem;left:-.125rem;top:-3.1875rem;cursor:pointer;transition:var(--transition)}#TimeSearch #TimeSearchList.open{top:-3.1875rem;width:2.75rem;height:14.125rem}#TimeSearch #TimeSelectButton{width:2.5rem;background-color:var(--background);padding-left:.5rem;padding-right:.5rem;border-radius:.5rem;line-height:1.625rem;text-align:center;font-size:.75rem;font-weight:700;margin-top:.4375rem;cursor:pointer;transition:var(--transition)}#TimeSearch #TimeSelectButton:hover,#TimeSearch #TimeSearchList .time:hover{background-color:var(--primaryColor);color:var(--white)}::ng-deep p-togglebutton .p-togglebutton.p-button{border-radius:var(--inputBorderRadius)!important;box-shadow:0 .3125rem 1.25rem #0000000d!important;margin-top:-.125rem!important}::ng-deep p-togglebutton .p-togglebutton.p-button:hover{filter:brightness(99%)!important;box-shadow:0 .3125rem 1.25rem #0000000d!important;transition:all .3s ease}::ng-deep p-togglebutton .p-togglebutton.p-button:active{filter:brightness(95%)!important;box-shadow:none!important;transform:translateY(.0625rem)!important;transition:all .3s ease}#TimeSearch #TimeSelectButton{margin-top:.25rem!important}::ng-deep .p-calendar .p-inputtext{height:2.3125rem}::ng-deep .p-inputtext{border-radius:var(--inputBorderRadius)}.calendar{display:inline-block;padding-left:.3125rem;float:left;padding-right:.625rem;margin-top:-.125rem}::ng-deep .p-datepicker:not(.p-datepicker-inline){background:var(--table);border:0 none;box-shadow:#091e4226 0 1.25rem 2rem -.5rem;border-radius:.75rem}::ng-deep .p-button.p-button-text{background-color:transparent;color:var(--primaryColor);border-color:transparent}::ng-deep .p-button.p-button-text:enabled:hover{background:var(--primaryColorShadowLight);color:var(--primaryColor);border-color:transparent}::ng-deep .p-datepicker table td>span.p-highlight{color:var(--tableText);background:var(--primaryColorShadowLight)}::ng-deep .p-inputtext:enabled:hover{border-color:var(--primaryColor)}::ng-deep .p-datepicker{padding:.5rem;padding-bottom:0;background:var(--table);color:var(--tableText);border:.0625rem solid var(--inputBorder);border-radius:.1875rem}::ng-deep .p-inputtext:enabled:focus{outline:0 none;outline-offset:0;box-shadow:0 0 0 1 var(--primaryColorShadowLight);border-color:var(--primaryColor)}::ng-deep .p-inputtext{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\",Segoe UI Symbol;font-size:1rem;color:var(--tableText);background:var(--table);padding:.5rem;border:.0625rem solid var(--inputBorder);transition:background-color .2s,color .2s,border-color .2s,box-shadow .2s;-webkit-appearance:none;-moz-appearance:none;appearance:none;border-radius:var(--inputBorderRadius)}::ng-deep .p-datepicker:not(.p-datepicker-inline) .p-datepicker-header{background:var(--table)}::ng-deep .p-datepicker .p-datepicker-header{padding:.5rem;color:var(--tableText);background:var(--table);font-weight:600;margin:0;border-bottom:.0625rem solid var(--inputBorder);border-top-right-radius:.75rem;border-top-left-radius:.75rem}::ng-deep .p-datepicker:not(.p-disabled) table td span:not(.p-highlight):not(.p-disabled):hover{background:#e9ecef;color:var(--table)}::ng-deep .p-datepicker .p-timepicker{border-top:.0625rem solid var(--background);padding:.5rem}::ng-deep .p-togglebutton.p-button{background:var(--background);border:.0625rem solid var(--inputBorder);color:var(--tableText);transition:background-color .2s,color .2s,border-color .2s,box-shadow .2s}::ng-deep .p-datepicker .p-datepicker-buttonbar{padding:1rem 0;border-top:.0625rem solid var(--inputBorder)}::ng-deep .p-button{color:#fff;background:var(--primaryColor);border:.0625rem solid var(--primaryColor);padding:.495rem 1rem;font-size:1rem;transition:background-color .2s,color .2s,border-color .2s,box-shadow .2s;border-radius:var(--inputBorderRadius)}::ng-deep .p-togglebutton.p-button.p-highlight{background:var(--primaryColor);border:.0625rem solid var(--primaryColor);color:var(--background)}::ng-deep .p-togglebutton.p-button.p-highlight:hover{background:var(--primaryColor);border:.0625rem solid var(--primaryColor);color:var(--background)}app-zac-wrapper{z-index:999999}\n"] }]
|
|
15867
|
+
}], ctorParameters: () => [{ type: IdentitiesPageService }, { type: DataTableFilterService }, { type: i5.MatDialog }, { type: TabNameService }, { type: i4.Router }, { type: ConsoleEventsService }, { type: undefined, decorators: [{
|
|
15868
|
+
type: Inject,
|
|
15869
|
+
args: [IDENTITY_EXTENSION_SERVICE]
|
|
15870
|
+
}] }] });
|
|
15871
|
+
|
|
13516
15872
|
class EdgeRoutersPageComponent extends ListPageComponent {
|
|
13517
15873
|
constructor(svc, filterService, dialogForm, tabNames, consoleEvents, extService) {
|
|
13518
15874
|
super(filterService, svc, consoleEvents, dialogForm);
|
|
@@ -19888,13 +22244,16 @@ class EdgeRouterFormComponent extends ProjectableForm {
|
|
|
19888
22244
|
get tunnelerEnabledReadOnly() {
|
|
19889
22245
|
return this.config.isOpenZiti ? true : !this.isTunnelerDisabled();
|
|
19890
22246
|
}
|
|
22247
|
+
get isCustomerHosted() {
|
|
22248
|
+
return this.extService.disabledComponents.some(item => item.key === 'customer-hosted');
|
|
22249
|
+
}
|
|
19891
22250
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: EdgeRouterFormComponent, deps: [{ token: SETTINGS_SERVICE }, { token: EdgeRouterFormService }, { token: ZITI_DATA_SERVICE }, { token: GrowlerService }, { token: EDGE_ROUTER_EXTENSION_SERVICE }, { token: DEFAULT_APP_CONFIG }, { token: i4.Router }, { token: i4.ActivatedRoute }, { token: i1$1.Location }, { token: i5.MatDialog }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
19892
22251
|
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: EdgeRouterFormComponent, isStandalone: false, selector: "lib-edge-router-form", inputs: { edgeRouterRoleAttributes: "edgeRouterRoleAttributes" }, outputs: { close: "close" }, providers: [
|
|
19893
22252
|
{
|
|
19894
22253
|
provide: MatDialogRef,
|
|
19895
22254
|
useValue: {}
|
|
19896
22255
|
}
|
|
19897
|
-
], usesInheritance: true, usesOnChanges: true, ngImport: i0, template: "<div class=\"projectable-form-wrapper\"\n (keyup.enter)=\"save($event)\"\n (keyup.escape)=\"closeModal(false)\"\n [class.projectable-form-hidden]=\"!dataInit\"\n >\n <lib-form-header\n [data]=\"formData\"\n [title]=\"formData.id ? 'Edit Edge Router: ' : 'Create New Edge Router'\"\n [moreActions]=\"moreActions\"\n [multiActions]=\"multiActions\"\n (actionRequested)=\"headerActionRequested($event)\"\n (multiActionRequested)=\"multiActionRequested($event)\"\n [(formView)]=\"formView\"\n [class.is-scrolled]=\"isScrolled\"\n [saveDisabled]=\"formPermissionSaveDisabled\"\n [saveTooltip]=\"formPermissionSaveTooltip\"\n [strictReadonlyUi]=\"useStrictReadonlyForm\"\n ></lib-form-header>\n <ng-content select=\"[slot=tabs-slot]\"></ng-content>\n <div class=\"edge-router-form-container projectable-form-container\" #formContainer>\n @if (formView === 'simple') {\n <div class=\"projectable-form-main-column form-group-row\">\n <div class=\"form-group-column three-fifths\">\n <lib-form-field-container [title]=\"'Edge Router Name'\" [label]=\"useStrictReadonlyForm ? '' : 'REQUIRED'\">\n <input\n class=\"form-field-input\"\n placeholder=\"Name this edge router\"\n [ngClass]=\"{error: errors['name']}\"\n [(ngModel)]=\"formData.name\"\n autofocus\n #nameFieldInput\n [disabled]=\"useStrictReadonlyForm\"\n />\n </lib-form-field-container>\n <lib-form-field-container\n [title]=\"'Select or create Edge Router attributes'\"\n [label]=\"'OPTIONAL'\"\n [contentStyle]=\"'z-index: 99999999'\"\n [helpText]=\"'Attributes are tags applied to an resource. Apply the same tag to other Edge Routers to form a group of Edge Routers.'\"\n >\n <lib-tag-selector\n [disableField]=\"useStrictReadonlyForm\"\n [disableCreate]=\"useStrictReadonlyForm\"\n [(selectedRoleAttributes)]=\"formData.roleAttributes\"\n [availableRoleAttributes]=\"edgeRouterRoleAttributes\"\n [placeholder]=\"'Add attributes to group Edge Routers'\"\n ></lib-tag-selector>\n </lib-form-field-container>\n <ng-content select=\"[slot=column-1-slot-1]\"></ng-content>\n <lib-form-field-toggle [(toggleOn)]=\"showMore\" style=\"margin: 0px 10px\"></lib-form-field-toggle>\n @if (showMore) {\n <div class=\"form-group-column\">\n <lib-form-field-container\n [title]=\"'TUNNELER'\"\n [title2]=\"'TRAVERSAL'\"\n [layout]=\"'row'\"\n class=\"form-field-advanced\"\n >\n <div class=\"config-item\" [class.disabled]=\"!tunnelerEnabledReadOnly\">\n <div class=\"config-container toggle-container\">\n <div class=\"config-container-label\">TUNNELER ENABLED</div>\n <div (click)=\"tunnelerEnabledReadOnly && !useStrictReadonlyForm && toggleTunnelerEnabled()\"\n [ngClass]=\"{ on: formData.isTunnelerEnabled, disabled: !tunnelerEnabledReadOnly, 'toggle-readonly': useStrictReadonlyForm }\" class=\"toggle\"\n [matTooltip]=\"useStrictReadonlyForm ? '' : (!tunnelerEnabledReadOnly ? 'Disabled because already hosted by netfoundry ' : '')\"\n matTooltipPosition=\"above\" style=\"pointer-events: auto;\">\n <span [hidden]=\"!formData.isTunnelerEnabled\" class=\"on-label\">YES</span>\n <span [hidden]=\"formData.isTunnelerEnabled\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n <div class=\"config-item\">\n <div class=\"config-container toggle-container\">\n <div class=\"config-container-label\">ALLOW TRAVERSAL</div>\n <div\n (click)=\"!useStrictReadonlyForm && toggleNoTraversal()\"\n [ngClass]=\"{ on: !formData.noTraversal, 'toggle-readonly': useStrictReadonlyForm }\"\n class=\"toggle\"\n >\n <span [hidden]=\"formData.noTraversal\" class=\"on-label\">YES</span>\n <span [hidden]=\"!formData.noTraversal\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n </lib-form-field-container>\n @if (formData?.hostname || formData?.supportedProtocols?.tls || formData?.supportedProtocols?.wss) {\n <lib-form-field-container [showHeader]=\"false\">\n @if (formData?.hostname) {\n <div class=\"config-item\">\n <div class=\"config-item-label-container\">\n <span class=\"config-title\">Domain Name</span>\n </div>\n <div class=\"config-container domain-container\">\n <div class=\"config-container-label domain-label\">\n {{ formData?.hostname }}\n </div>\n </div>\n </div>\n }\n @if (formData?.supportedProtocols?.tls || formData?.supportedProtocols?.wss) {\n <div class=\"config-item\">\n <div class=\"config-item-label-container\">\n <span class=\"config-title\">ADVERTISE ADDRESS</span>\n </div>\n <div class=\"config-container advertise-address-container\">\n <div class=\"config-container-label advertise-address-label\">\n {{ formData?.supportedProtocols?.tls }}\n <br />\n {{ formData?.supportedProtocols?.wss }}\n </div>\n </div>\n </div>\n }\n </lib-form-field-container>\n }\n <lib-form-field-container\n [title]=\"'Custom Tags'\"\n [label]=\"'OPTIONAL'\"\n class=\"form-field-advanced\"\n >\n <lib-custom-tags [(tags)]=\"formData.tags\" [readOnly]=\"useStrictReadonlyForm\"></lib-custom-tags>\n </lib-form-field-container>\n <lib-form-field-container\n [title]=\"'App Data'\"\n [label]=\"'OPTIONAL'\"\n class=\"form-field-advanced\"\n >\n <lib-json-view [(data)]=\"formData.appData\" [readOnly]=\"useStrictReadonlyForm\"></lib-json-view>\n </lib-form-field-container>\n </div>\n }\n </div>\n <div class=\"form-group-column two-fifths\">\n <ng-content select=\"[slot=column-2-slot-1]\"></ng-content>\n @if (hasEnrolmentToken && !extService['hideZitiRegistration']) {\n <lib-form-field-container\n [showHeader]=\"false\"\n >\n <lib-qr-code\n [identity]=\"formData\"\n [jwt]=\"formData.enrollmentJwt\"\n [token]=\"formData.enrollmentToken\"\n [expiration]=\"formData.enrollmentExpiresAt\"\n (doRefresh)=\"refreshEdgeRouter()\"\n [type]=\"'router'\"\n ></lib-qr-code>\n </lib-form-field-container>\n }\n @if (formData.id) {\n <lib-form-field-container\n [title]=\"'Associated Services'\"\n [count]=\"svc.associatedServices.length\"\n [class]=\"'hide-overflow'\"\n [helpText]=\"'Preview list of all services that this Edge Router is associated with.'\"\n >\n <lib-preview-list\n [clickable]=\"false\"\n [isLoading]=\"servicesLoading\"\n [allNames]=\"svc.associatedServiceNames\"\n ></lib-preview-list>\n </lib-form-field-container>\n }\n @if (formData.id) {\n <lib-form-field-container\n [title]=\"'Associated Identities'\"\n [count]=\"svc.associatedIdentities.length\"\n [helpText]=\"'Preview list of all Identities that this Edge Router is associated with.'\"\n >\n <lib-preview-list\n [clickable]=\"false\"\n [isLoading]=\"identitiesLoading\"\n [allNames]=\"svc.associatedIdentityNames\"\n ></lib-preview-list>\n </lib-form-field-container>\n }\n @if (showApiCalls) {\n <lib-form-field-container\n [title]=\"'API Calls'\"\n [label]=\"'OPTIONAL'\"\n >\n <div class=\"form-row\">\n <input class=\"form-field-input\" [value]=\"apiCallURL\"/>\n <div class=\"icon-copy copy-text\" (click)=\"copyToClipboard(apiCallURL)\"></div>\n </div>\n @if (formData) {\n <lib-json-view [(data)]=\"apiData\" [readOnly]=\"true\" [showCopy]=\"true\"></lib-json-view>\n }\n </lib-form-field-container>\n }\n </div>\n </div>\n }\n @if (formView === 'raw') {\n <div class=\"form-group-column\">\n @if (formData) {\n <lib-json-view [(data)]=\"formData\" [readOnly]=\"useStrictReadonlyForm\"></lib-json-view>\n }\n </div>\n }\n </div>\n</div>\n@if (isLoading || !dataInit) {\n <lib-loading-indicator [isLoading]=\"isLoading || !dataInit\"></lib-loading-indicator>\n}\n", styles: [".edge-router-form-container{display:flex;flex-direction:column;height:100%;width:100%;align-items:center;gap:.9375rem}.form-field-dropdown{background-image:url(/assets/svgs/ArrowDown.svg);background-position:center right .625rem;background-repeat:no-repeat;background-size:1.125rem;padding-right:1.75rem}.disabled{opacity:.5;pointer-events:none}.toggle.disabled{cursor:not-allowed}::ng-deep .admin-toggle{width:100%;width:35%;display:flex}::ng-deep .admin-toggle .form-field-line{display:none}::ng-deep .admin-toggle .toggle{width:100%}::ng-deep .is-admin-container .form-field-title-container.title-container-2{width:35%!important}.svc-test-result{position:absolute;transition:var(--transition);height:0;width:100%;background-color:var(--background);bottom:0;font-weight:600;padding-left:.625rem;padding-right:.625rem;border-color:var(--inputBorder);border-style:solid;border-width:var(--inputBorderWidth);border-bottom-right-radius:var(--inputBorderRadius);border-bottom-left-radius:var(--inputBorderRadius);border-top-style:none}.svc-test-result.test-result-open{height:fit-content;box-shadow:0 -.125rem .6875rem 0 var(--boxShadow);padding-bottom:.9375rem;padding-top:.9375rem}.svc-test-result .close.icon-close{top:.625rem;right:.625rem;font-size:1.125rem;height:1.125rem;width:1.125rem;line-height:1.125rem}::ng-deep .admin-toggle .form-field-toggle-container:focus .form-field-label{color:var(--primaryColor)}::ng-deep .admin-toggle .form-field-toggle-container:focus .form-field-line{border-color:var(--primaryColor)}::ng-deep .admin-toggle .form-field-toggle-container:focus .toggle{border-color:var(--primaryColor);border-style:solid;border-width:var(--inputBorderWidth)}::ng-deep .admin-toggle .form-field-toggle-container:focus .toggle .switch{top:.0625rem}.config-container-label.domain-label,.config-container-label.advertise-address-label{text-transform:lowercase}.config-container.domain-container,.config-container.advertise-address-container{background-color:var(--stroke)}\n"], dependencies: [{ kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i3.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: FormHeaderComponent, selector: "lib-form-header", inputs: ["data", "badges", "title", "moreActions", "formView", "saveDisabled", "saveTooltip", "actionButtonText", "moreActionsText", "showHeaderToggle", "showHeaderButton", "strictReadonlyUi", "multiActions"], outputs: ["formViewChange", "actionRequested", "multiActionRequested"] }, { kind: "component", type: FormFieldContainerComponent, selector: "lib-form-field-container", inputs: ["title", "title2", "titleIcon", "layout", "helpText", "helpText2", "label", "count", "action", "actionLabel", "class", "contentStyle", "contentClass", "showHeader", "showToggle", "headerActions", "headerToggle"], outputs: ["actionRequested", "headerToggleChange", "helpClicked"] }, { kind: "component", type: PreviewListComponent, selector: "lib-preview-list", inputs: ["label", "clickable", "isLoading", "allNames", "items", "allowRemove", "tooltip", "showfilter", "localFilter"], outputs: ["itemSelected", "itemRemoved", "filterChanged"] }, { kind: "component", type: LoadingIndicatorComponent, selector: "lib-loading-indicator", inputs: ["isLoading"] }, { kind: "component", type: FormFieldToggleComponent, selector: "lib-form-field-toggle", inputs: ["label", "orientation", "labelOn", "labelOff", "toggleOn"], outputs: ["toggleOnChange"] }, { kind: "component", type: JsonViewComponent, selector: "lib-json-view", inputs: ["data", "schema", "readOnly", "jsonInvalid", "showCopy", "showLineNumbers"], outputs: ["dataChange", "jsonChange", "jsonInvalidChange"] }, { kind: "component", type: TagSelectorComponent, selector: "lib-tag-selector", inputs: ["placeholder", "disableField", "disableCreate", "hideOption", "isLoading", "errorMessage", "focusOnInit", "namedAttributesServerFiltered", "selectedRoleAttributes", "selectedNamedAttributes", "hasError", "availableRoleAttributes", "availableNamedAttributes"], outputs: ["addRoleAttribute", "addNamedAttribute", "roleAttributeRemoved", "namedAttributeRemoved", "selectedRoleAttributesChange", "selectedNamedAttributesChange", "filterChange"] }, { kind: "component", type: QrCodeComponent, selector: "lib-qr-code", inputs: ["jwt", "token", "expiration", "identity", "type", "authenticators", "qrOnly", "canExpand"], outputs: ["doRefresh"] }, { kind: "component", type: CustomTagsComponent, selector: "lib-custom-tags", inputs: ["tags", "readOnly"], outputs: ["tagsChange"] }] }); }
|
|
22256
|
+
], usesInheritance: true, usesOnChanges: true, ngImport: i0, template: "<div class=\"projectable-form-wrapper\"\n (keyup.enter)=\"save($event)\"\n (keyup.escape)=\"closeModal(false)\"\n [class.projectable-form-hidden]=\"!dataInit\"\n >\n <lib-form-header\n [data]=\"formData\"\n [title]=\"formData.id ? 'Edit Edge Router: ' : 'Create New Edge Router'\"\n [moreActions]=\"moreActions\"\n [multiActions]=\"multiActions\"\n (actionRequested)=\"headerActionRequested($event)\"\n (multiActionRequested)=\"multiActionRequested($event)\"\n [(formView)]=\"formView\"\n [class.is-scrolled]=\"isScrolled\"\n [saveDisabled]=\"formPermissionSaveDisabled\"\n [saveTooltip]=\"formPermissionSaveTooltip\"\n [strictReadonlyUi]=\"useStrictReadonlyForm\"\n ></lib-form-header>\n <ng-content select=\"[slot=tabs-slot]\"></ng-content>\n <div class=\"edge-router-form-container projectable-form-container\" #formContainer>\n @if (formView === 'simple') {\n <div class=\"projectable-form-main-column form-group-row\">\n <div class=\"form-group-column three-fifths\">\n <lib-form-field-container [title]=\"'Edge Router Name'\" [label]=\"useStrictReadonlyForm ? '' : 'REQUIRED'\">\n <input\n class=\"form-field-input\"\n placeholder=\"Name this edge router\"\n [ngClass]=\"{error: errors['name']}\"\n [(ngModel)]=\"formData.name\"\n autofocus\n #nameFieldInput\n [disabled]=\"useStrictReadonlyForm\"\n />\n </lib-form-field-container>\n <lib-form-field-container\n [title]=\"'Select or create Edge Router attributes'\"\n [label]=\"'OPTIONAL'\"\n [contentStyle]=\"'z-index: 99999999'\"\n [helpText]=\"'Attributes are tags applied to an resource. Apply the same tag to other Edge Routers to form a group of Edge Routers.'\"\n >\n <lib-tag-selector\n [disableField]=\"useStrictReadonlyForm\"\n [disableCreate]=\"useStrictReadonlyForm\"\n [(selectedRoleAttributes)]=\"formData.roleAttributes\"\n [availableRoleAttributes]=\"edgeRouterRoleAttributes\"\n [placeholder]=\"'Add attributes to group Edge Routers'\"\n ></lib-tag-selector>\n </lib-form-field-container>\n <ng-content select=\"[slot=column-1-slot-1]\"></ng-content>\n <lib-form-field-toggle [(toggleOn)]=\"showMore\" style=\"margin: 0px 10px\"></lib-form-field-toggle>\n @if (showMore) {\n <div class=\"form-group-column\">\n <lib-form-field-container\n [title]=\"'TUNNELER'\"\n [title2]=\"'TRAVERSAL'\"\n [layout]=\"'row'\"\n class=\"form-field-advanced\"\n >\n <div class=\"config-item\" [class.disabled]=\"!tunnelerEnabledReadOnly\">\n <div class=\"config-container toggle-container\">\n <div class=\"config-container-label\">TUNNELER ENABLED</div>\n <div (click)=\"tunnelerEnabledReadOnly && !useStrictReadonlyForm && toggleTunnelerEnabled()\"\n [ngClass]=\"{ on: formData.isTunnelerEnabled, disabled: !tunnelerEnabledReadOnly, 'toggle-readonly': useStrictReadonlyForm }\" class=\"toggle\"\n [matTooltip]=\"useStrictReadonlyForm ? '' : (!tunnelerEnabledReadOnly ? 'Disabled because already hosted by netfoundry ' : '')\"\n matTooltipPosition=\"above\" style=\"pointer-events: auto;\">\n <span [hidden]=\"!formData.isTunnelerEnabled\" class=\"on-label\">YES</span>\n <span [hidden]=\"formData.isTunnelerEnabled\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n <div class=\"config-item\">\n <div class=\"config-container toggle-container\">\n <div class=\"config-container-label\">ALLOW TRAVERSAL</div>\n <div\n (click)=\"!useStrictReadonlyForm && toggleNoTraversal()\"\n [ngClass]=\"{ on: !formData.noTraversal, 'toggle-readonly': useStrictReadonlyForm }\"\n class=\"toggle\"\n >\n <span [hidden]=\"formData.noTraversal\" class=\"on-label\">YES</span>\n <span [hidden]=\"!formData.noTraversal\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n </lib-form-field-container>\n @if (isCustomerHosted) {\n <lib-form-field-container\n [title]=\"'LINK LISTENER'\"\n class=\"form-field-advanced\"\n >\n <div class=\"config-item disabled\">\n <div class=\"config-container toggle-container\">\n <div class=\"config-container-label\">ENABLE LINK LISTENER</div>\n <div\n [ngClass]=\"{ on: formData.linkListener, disabled: true }\"\n class=\"toggle\"\n role=\"switch\"\n aria-disabled=\"true\"\n [attr.aria-checked]=\"!!formData.linkListener\"\n matTooltip=\"This value is reported by the router and can't be changed here.\"\n matTooltipPosition=\"above\"\n >\n <span [hidden]=\"!formData.linkListener\" class=\"on-label\">YES</span>\n <span [hidden]=\"formData.linkListener\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n </lib-form-field-container>\n }\n @if (formData?.hostname || formData?.supportedProtocols?.tls || formData?.supportedProtocols?.wss) {\n <lib-form-field-container [showHeader]=\"false\">\n @if (formData?.hostname) {\n <div class=\"config-item\">\n <div class=\"config-item-label-container\">\n <span class=\"config-title\">Domain Name</span>\n </div>\n <div class=\"config-container domain-container\">\n <div class=\"config-container-label domain-label\">\n {{ formData?.hostname }}\n </div>\n </div>\n </div>\n }\n @if (formData?.supportedProtocols?.tls || formData?.supportedProtocols?.wss) {\n <div class=\"config-item\">\n <div class=\"config-item-label-container\">\n <span class=\"config-title\">ADVERTISE ADDRESS</span>\n </div>\n <div class=\"config-container advertise-address-container\">\n <div class=\"config-container-label advertise-address-label\">\n {{ formData?.supportedProtocols?.tls }}\n <br />\n {{ formData?.supportedProtocols?.wss }}\n </div>\n </div>\n </div>\n }\n </lib-form-field-container>\n }\n <lib-form-field-container\n [title]=\"'Custom Tags'\"\n [label]=\"'OPTIONAL'\"\n class=\"form-field-advanced\"\n >\n <lib-custom-tags [(tags)]=\"formData.tags\" [readOnly]=\"useStrictReadonlyForm\"></lib-custom-tags>\n </lib-form-field-container>\n <lib-form-field-container\n [title]=\"'App Data'\"\n [label]=\"'OPTIONAL'\"\n class=\"form-field-advanced\"\n >\n <lib-json-view [(data)]=\"formData.appData\" [readOnly]=\"useStrictReadonlyForm\"></lib-json-view>\n </lib-form-field-container>\n </div>\n }\n </div>\n <div class=\"form-group-column two-fifths\">\n <ng-content select=\"[slot=column-2-slot-1]\"></ng-content>\n @if (hasEnrolmentToken && !extService['hideZitiRegistration']) {\n <lib-form-field-container\n [showHeader]=\"false\"\n >\n <lib-qr-code\n [identity]=\"formData\"\n [jwt]=\"formData.enrollmentJwt\"\n [token]=\"formData.enrollmentToken\"\n [expiration]=\"formData.enrollmentExpiresAt\"\n (doRefresh)=\"refreshEdgeRouter()\"\n [type]=\"'router'\"\n ></lib-qr-code>\n </lib-form-field-container>\n }\n @if (formData.id) {\n <lib-form-field-container\n [title]=\"'Associated Services'\"\n [count]=\"svc.associatedServices.length\"\n [class]=\"'hide-overflow'\"\n [helpText]=\"'Preview list of all services that this Edge Router is associated with.'\"\n >\n <lib-preview-list\n [clickable]=\"false\"\n [isLoading]=\"servicesLoading\"\n [allNames]=\"svc.associatedServiceNames\"\n ></lib-preview-list>\n </lib-form-field-container>\n }\n @if (formData.id) {\n <lib-form-field-container\n [title]=\"'Associated Identities'\"\n [count]=\"svc.associatedIdentities.length\"\n [helpText]=\"'Preview list of all Identities that this Edge Router is associated with.'\"\n >\n <lib-preview-list\n [clickable]=\"false\"\n [isLoading]=\"identitiesLoading\"\n [allNames]=\"svc.associatedIdentityNames\"\n ></lib-preview-list>\n </lib-form-field-container>\n }\n @if (showApiCalls) {\n <lib-form-field-container\n [title]=\"'API Calls'\"\n [label]=\"'OPTIONAL'\"\n >\n <div class=\"form-row\">\n <input class=\"form-field-input\" [value]=\"apiCallURL\"/>\n <div class=\"icon-copy copy-text\" (click)=\"copyToClipboard(apiCallURL)\"></div>\n </div>\n @if (formData) {\n <lib-json-view [(data)]=\"apiData\" [readOnly]=\"true\" [showCopy]=\"true\"></lib-json-view>\n }\n </lib-form-field-container>\n }\n </div>\n </div>\n }\n @if (formView === 'raw') {\n <div class=\"form-group-column\">\n @if (formData) {\n <lib-json-view [(data)]=\"formData\" [readOnly]=\"useStrictReadonlyForm\"></lib-json-view>\n }\n </div>\n }\n </div>\n</div>\n@if (isLoading || !dataInit) {\n <lib-loading-indicator [isLoading]=\"isLoading || !dataInit\"></lib-loading-indicator>\n}\n", styles: [".edge-router-form-container{display:flex;flex-direction:column;height:100%;width:100%;align-items:center;gap:.9375rem}.form-field-dropdown{background-image:url(/assets/svgs/ArrowDown.svg);background-position:center right .625rem;background-repeat:no-repeat;background-size:1.125rem;padding-right:1.75rem}.disabled{opacity:.5;pointer-events:none}.toggle.disabled{cursor:not-allowed}::ng-deep .admin-toggle{width:100%;width:35%;display:flex}::ng-deep .admin-toggle .form-field-line{display:none}::ng-deep .admin-toggle .toggle{width:100%}::ng-deep .is-admin-container .form-field-title-container.title-container-2{width:35%!important}.svc-test-result{position:absolute;transition:var(--transition);height:0;width:100%;background-color:var(--background);bottom:0;font-weight:600;padding-left:.625rem;padding-right:.625rem;border-color:var(--inputBorder);border-style:solid;border-width:var(--inputBorderWidth);border-bottom-right-radius:var(--inputBorderRadius);border-bottom-left-radius:var(--inputBorderRadius);border-top-style:none}.svc-test-result.test-result-open{height:fit-content;box-shadow:0 -.125rem .6875rem 0 var(--boxShadow);padding-bottom:.9375rem;padding-top:.9375rem}.svc-test-result .close.icon-close{top:.625rem;right:.625rem;font-size:1.125rem;height:1.125rem;width:1.125rem;line-height:1.125rem}::ng-deep .admin-toggle .form-field-toggle-container:focus .form-field-label{color:var(--primaryColor)}::ng-deep .admin-toggle .form-field-toggle-container:focus .form-field-line{border-color:var(--primaryColor)}::ng-deep .admin-toggle .form-field-toggle-container:focus .toggle{border-color:var(--primaryColor);border-style:solid;border-width:var(--inputBorderWidth)}::ng-deep .admin-toggle .form-field-toggle-container:focus .toggle .switch{top:.0625rem}.config-container-label.domain-label,.config-container-label.advertise-address-label{text-transform:lowercase}.config-container-label.advertise-address-label{height:auto;padding:.125rem 0}.config-container.domain-container,.config-container.advertise-address-container{background-color:var(--stroke)}.config-container.advertise-address-container{height:auto;min-height:var(--defaultInputHeight);padding:0rem 1.25rem}\n"], dependencies: [{ kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i3.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: FormHeaderComponent, selector: "lib-form-header", inputs: ["data", "badges", "title", "moreActions", "formView", "saveDisabled", "saveTooltip", "actionButtonText", "moreActionsText", "showHeaderToggle", "showHeaderButton", "strictReadonlyUi", "multiActions"], outputs: ["formViewChange", "actionRequested", "multiActionRequested"] }, { kind: "component", type: FormFieldContainerComponent, selector: "lib-form-field-container", inputs: ["title", "title2", "titleIcon", "layout", "helpText", "helpText2", "label", "count", "action", "actionLabel", "class", "contentStyle", "contentClass", "showHeader", "showToggle", "headerActions", "headerToggle"], outputs: ["actionRequested", "headerToggleChange", "helpClicked"] }, { kind: "component", type: PreviewListComponent, selector: "lib-preview-list", inputs: ["label", "clickable", "isLoading", "allNames", "items", "allowRemove", "tooltip", "showfilter", "localFilter"], outputs: ["itemSelected", "itemRemoved", "filterChanged"] }, { kind: "component", type: LoadingIndicatorComponent, selector: "lib-loading-indicator", inputs: ["isLoading"] }, { kind: "component", type: FormFieldToggleComponent, selector: "lib-form-field-toggle", inputs: ["label", "orientation", "labelOn", "labelOff", "toggleOn"], outputs: ["toggleOnChange"] }, { kind: "component", type: JsonViewComponent, selector: "lib-json-view", inputs: ["data", "schema", "readOnly", "jsonInvalid", "showCopy", "showLineNumbers"], outputs: ["dataChange", "jsonChange", "jsonInvalidChange"] }, { kind: "component", type: TagSelectorComponent, selector: "lib-tag-selector", inputs: ["placeholder", "disableField", "disableCreate", "hideOption", "isLoading", "errorMessage", "focusOnInit", "namedAttributesServerFiltered", "selectedRoleAttributes", "selectedNamedAttributes", "hasError", "availableRoleAttributes", "availableNamedAttributes"], outputs: ["addRoleAttribute", "addNamedAttribute", "roleAttributeRemoved", "namedAttributeRemoved", "selectedRoleAttributesChange", "selectedNamedAttributesChange", "filterChange"] }, { kind: "component", type: QrCodeComponent, selector: "lib-qr-code", inputs: ["jwt", "token", "expiration", "identity", "type", "authenticators", "qrOnly", "canExpand"], outputs: ["doRefresh"] }, { kind: "component", type: CustomTagsComponent, selector: "lib-custom-tags", inputs: ["tags", "readOnly"], outputs: ["tagsChange"] }] }); }
|
|
19898
22257
|
}
|
|
19899
22258
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: EdgeRouterFormComponent, decorators: [{
|
|
19900
22259
|
type: Component,
|
|
@@ -19903,7 +22262,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
19903
22262
|
provide: MatDialogRef,
|
|
19904
22263
|
useValue: {}
|
|
19905
22264
|
}
|
|
19906
|
-
], standalone: false, template: "<div class=\"projectable-form-wrapper\"\n (keyup.enter)=\"save($event)\"\n (keyup.escape)=\"closeModal(false)\"\n [class.projectable-form-hidden]=\"!dataInit\"\n >\n <lib-form-header\n [data]=\"formData\"\n [title]=\"formData.id ? 'Edit Edge Router: ' : 'Create New Edge Router'\"\n [moreActions]=\"moreActions\"\n [multiActions]=\"multiActions\"\n (actionRequested)=\"headerActionRequested($event)\"\n (multiActionRequested)=\"multiActionRequested($event)\"\n [(formView)]=\"formView\"\n [class.is-scrolled]=\"isScrolled\"\n [saveDisabled]=\"formPermissionSaveDisabled\"\n [saveTooltip]=\"formPermissionSaveTooltip\"\n [strictReadonlyUi]=\"useStrictReadonlyForm\"\n ></lib-form-header>\n <ng-content select=\"[slot=tabs-slot]\"></ng-content>\n <div class=\"edge-router-form-container projectable-form-container\" #formContainer>\n @if (formView === 'simple') {\n <div class=\"projectable-form-main-column form-group-row\">\n <div class=\"form-group-column three-fifths\">\n <lib-form-field-container [title]=\"'Edge Router Name'\" [label]=\"useStrictReadonlyForm ? '' : 'REQUIRED'\">\n <input\n class=\"form-field-input\"\n placeholder=\"Name this edge router\"\n [ngClass]=\"{error: errors['name']}\"\n [(ngModel)]=\"formData.name\"\n autofocus\n #nameFieldInput\n [disabled]=\"useStrictReadonlyForm\"\n />\n </lib-form-field-container>\n <lib-form-field-container\n [title]=\"'Select or create Edge Router attributes'\"\n [label]=\"'OPTIONAL'\"\n [contentStyle]=\"'z-index: 99999999'\"\n [helpText]=\"'Attributes are tags applied to an resource. Apply the same tag to other Edge Routers to form a group of Edge Routers.'\"\n >\n <lib-tag-selector\n [disableField]=\"useStrictReadonlyForm\"\n [disableCreate]=\"useStrictReadonlyForm\"\n [(selectedRoleAttributes)]=\"formData.roleAttributes\"\n [availableRoleAttributes]=\"edgeRouterRoleAttributes\"\n [placeholder]=\"'Add attributes to group Edge Routers'\"\n ></lib-tag-selector>\n </lib-form-field-container>\n <ng-content select=\"[slot=column-1-slot-1]\"></ng-content>\n <lib-form-field-toggle [(toggleOn)]=\"showMore\" style=\"margin: 0px 10px\"></lib-form-field-toggle>\n @if (showMore) {\n <div class=\"form-group-column\">\n <lib-form-field-container\n [title]=\"'TUNNELER'\"\n [title2]=\"'TRAVERSAL'\"\n [layout]=\"'row'\"\n class=\"form-field-advanced\"\n >\n <div class=\"config-item\" [class.disabled]=\"!tunnelerEnabledReadOnly\">\n <div class=\"config-container toggle-container\">\n <div class=\"config-container-label\">TUNNELER ENABLED</div>\n <div (click)=\"tunnelerEnabledReadOnly && !useStrictReadonlyForm && toggleTunnelerEnabled()\"\n [ngClass]=\"{ on: formData.isTunnelerEnabled, disabled: !tunnelerEnabledReadOnly, 'toggle-readonly': useStrictReadonlyForm }\" class=\"toggle\"\n [matTooltip]=\"useStrictReadonlyForm ? '' : (!tunnelerEnabledReadOnly ? 'Disabled because already hosted by netfoundry ' : '')\"\n matTooltipPosition=\"above\" style=\"pointer-events: auto;\">\n <span [hidden]=\"!formData.isTunnelerEnabled\" class=\"on-label\">YES</span>\n <span [hidden]=\"formData.isTunnelerEnabled\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n <div class=\"config-item\">\n <div class=\"config-container toggle-container\">\n <div class=\"config-container-label\">ALLOW TRAVERSAL</div>\n <div\n (click)=\"!useStrictReadonlyForm && toggleNoTraversal()\"\n [ngClass]=\"{ on: !formData.noTraversal, 'toggle-readonly': useStrictReadonlyForm }\"\n class=\"toggle\"\n >\n <span [hidden]=\"formData.noTraversal\" class=\"on-label\">YES</span>\n <span [hidden]=\"!formData.noTraversal\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n </lib-form-field-container>\n @if (formData?.hostname || formData?.supportedProtocols?.tls || formData?.supportedProtocols?.wss) {\n <lib-form-field-container [showHeader]=\"false\">\n @if (formData?.hostname) {\n <div class=\"config-item\">\n <div class=\"config-item-label-container\">\n <span class=\"config-title\">Domain Name</span>\n </div>\n <div class=\"config-container domain-container\">\n <div class=\"config-container-label domain-label\">\n {{ formData?.hostname }}\n </div>\n </div>\n </div>\n }\n @if (formData?.supportedProtocols?.tls || formData?.supportedProtocols?.wss) {\n <div class=\"config-item\">\n <div class=\"config-item-label-container\">\n <span class=\"config-title\">ADVERTISE ADDRESS</span>\n </div>\n <div class=\"config-container advertise-address-container\">\n <div class=\"config-container-label advertise-address-label\">\n {{ formData?.supportedProtocols?.tls }}\n <br />\n {{ formData?.supportedProtocols?.wss }}\n </div>\n </div>\n </div>\n }\n </lib-form-field-container>\n }\n <lib-form-field-container\n [title]=\"'Custom Tags'\"\n [label]=\"'OPTIONAL'\"\n class=\"form-field-advanced\"\n >\n <lib-custom-tags [(tags)]=\"formData.tags\" [readOnly]=\"useStrictReadonlyForm\"></lib-custom-tags>\n </lib-form-field-container>\n <lib-form-field-container\n [title]=\"'App Data'\"\n [label]=\"'OPTIONAL'\"\n class=\"form-field-advanced\"\n >\n <lib-json-view [(data)]=\"formData.appData\" [readOnly]=\"useStrictReadonlyForm\"></lib-json-view>\n </lib-form-field-container>\n </div>\n }\n </div>\n <div class=\"form-group-column two-fifths\">\n <ng-content select=\"[slot=column-2-slot-1]\"></ng-content>\n @if (hasEnrolmentToken && !extService['hideZitiRegistration']) {\n <lib-form-field-container\n [showHeader]=\"false\"\n >\n <lib-qr-code\n [identity]=\"formData\"\n [jwt]=\"formData.enrollmentJwt\"\n [token]=\"formData.enrollmentToken\"\n [expiration]=\"formData.enrollmentExpiresAt\"\n (doRefresh)=\"refreshEdgeRouter()\"\n [type]=\"'router'\"\n ></lib-qr-code>\n </lib-form-field-container>\n }\n @if (formData.id) {\n <lib-form-field-container\n [title]=\"'Associated Services'\"\n [count]=\"svc.associatedServices.length\"\n [class]=\"'hide-overflow'\"\n [helpText]=\"'Preview list of all services that this Edge Router is associated with.'\"\n >\n <lib-preview-list\n [clickable]=\"false\"\n [isLoading]=\"servicesLoading\"\n [allNames]=\"svc.associatedServiceNames\"\n ></lib-preview-list>\n </lib-form-field-container>\n }\n @if (formData.id) {\n <lib-form-field-container\n [title]=\"'Associated Identities'\"\n [count]=\"svc.associatedIdentities.length\"\n [helpText]=\"'Preview list of all Identities that this Edge Router is associated with.'\"\n >\n <lib-preview-list\n [clickable]=\"false\"\n [isLoading]=\"identitiesLoading\"\n [allNames]=\"svc.associatedIdentityNames\"\n ></lib-preview-list>\n </lib-form-field-container>\n }\n @if (showApiCalls) {\n <lib-form-field-container\n [title]=\"'API Calls'\"\n [label]=\"'OPTIONAL'\"\n >\n <div class=\"form-row\">\n <input class=\"form-field-input\" [value]=\"apiCallURL\"/>\n <div class=\"icon-copy copy-text\" (click)=\"copyToClipboard(apiCallURL)\"></div>\n </div>\n @if (formData) {\n <lib-json-view [(data)]=\"apiData\" [readOnly]=\"true\" [showCopy]=\"true\"></lib-json-view>\n }\n </lib-form-field-container>\n }\n </div>\n </div>\n }\n @if (formView === 'raw') {\n <div class=\"form-group-column\">\n @if (formData) {\n <lib-json-view [(data)]=\"formData\" [readOnly]=\"useStrictReadonlyForm\"></lib-json-view>\n }\n </div>\n }\n </div>\n</div>\n@if (isLoading || !dataInit) {\n <lib-loading-indicator [isLoading]=\"isLoading || !dataInit\"></lib-loading-indicator>\n}\n", styles: [".edge-router-form-container{display:flex;flex-direction:column;height:100%;width:100%;align-items:center;gap:.9375rem}.form-field-dropdown{background-image:url(/assets/svgs/ArrowDown.svg);background-position:center right .625rem;background-repeat:no-repeat;background-size:1.125rem;padding-right:1.75rem}.disabled{opacity:.5;pointer-events:none}.toggle.disabled{cursor:not-allowed}::ng-deep .admin-toggle{width:100%;width:35%;display:flex}::ng-deep .admin-toggle .form-field-line{display:none}::ng-deep .admin-toggle .toggle{width:100%}::ng-deep .is-admin-container .form-field-title-container.title-container-2{width:35%!important}.svc-test-result{position:absolute;transition:var(--transition);height:0;width:100%;background-color:var(--background);bottom:0;font-weight:600;padding-left:.625rem;padding-right:.625rem;border-color:var(--inputBorder);border-style:solid;border-width:var(--inputBorderWidth);border-bottom-right-radius:var(--inputBorderRadius);border-bottom-left-radius:var(--inputBorderRadius);border-top-style:none}.svc-test-result.test-result-open{height:fit-content;box-shadow:0 -.125rem .6875rem 0 var(--boxShadow);padding-bottom:.9375rem;padding-top:.9375rem}.svc-test-result .close.icon-close{top:.625rem;right:.625rem;font-size:1.125rem;height:1.125rem;width:1.125rem;line-height:1.125rem}::ng-deep .admin-toggle .form-field-toggle-container:focus .form-field-label{color:var(--primaryColor)}::ng-deep .admin-toggle .form-field-toggle-container:focus .form-field-line{border-color:var(--primaryColor)}::ng-deep .admin-toggle .form-field-toggle-container:focus .toggle{border-color:var(--primaryColor);border-style:solid;border-width:var(--inputBorderWidth)}::ng-deep .admin-toggle .form-field-toggle-container:focus .toggle .switch{top:.0625rem}.config-container-label.domain-label,.config-container-label.advertise-address-label{text-transform:lowercase}.config-container.domain-container,.config-container.advertise-address-container{background-color:var(--stroke)}\n"] }]
|
|
22265
|
+
], standalone: false, template: "<div class=\"projectable-form-wrapper\"\n (keyup.enter)=\"save($event)\"\n (keyup.escape)=\"closeModal(false)\"\n [class.projectable-form-hidden]=\"!dataInit\"\n >\n <lib-form-header\n [data]=\"formData\"\n [title]=\"formData.id ? 'Edit Edge Router: ' : 'Create New Edge Router'\"\n [moreActions]=\"moreActions\"\n [multiActions]=\"multiActions\"\n (actionRequested)=\"headerActionRequested($event)\"\n (multiActionRequested)=\"multiActionRequested($event)\"\n [(formView)]=\"formView\"\n [class.is-scrolled]=\"isScrolled\"\n [saveDisabled]=\"formPermissionSaveDisabled\"\n [saveTooltip]=\"formPermissionSaveTooltip\"\n [strictReadonlyUi]=\"useStrictReadonlyForm\"\n ></lib-form-header>\n <ng-content select=\"[slot=tabs-slot]\"></ng-content>\n <div class=\"edge-router-form-container projectable-form-container\" #formContainer>\n @if (formView === 'simple') {\n <div class=\"projectable-form-main-column form-group-row\">\n <div class=\"form-group-column three-fifths\">\n <lib-form-field-container [title]=\"'Edge Router Name'\" [label]=\"useStrictReadonlyForm ? '' : 'REQUIRED'\">\n <input\n class=\"form-field-input\"\n placeholder=\"Name this edge router\"\n [ngClass]=\"{error: errors['name']}\"\n [(ngModel)]=\"formData.name\"\n autofocus\n #nameFieldInput\n [disabled]=\"useStrictReadonlyForm\"\n />\n </lib-form-field-container>\n <lib-form-field-container\n [title]=\"'Select or create Edge Router attributes'\"\n [label]=\"'OPTIONAL'\"\n [contentStyle]=\"'z-index: 99999999'\"\n [helpText]=\"'Attributes are tags applied to an resource. Apply the same tag to other Edge Routers to form a group of Edge Routers.'\"\n >\n <lib-tag-selector\n [disableField]=\"useStrictReadonlyForm\"\n [disableCreate]=\"useStrictReadonlyForm\"\n [(selectedRoleAttributes)]=\"formData.roleAttributes\"\n [availableRoleAttributes]=\"edgeRouterRoleAttributes\"\n [placeholder]=\"'Add attributes to group Edge Routers'\"\n ></lib-tag-selector>\n </lib-form-field-container>\n <ng-content select=\"[slot=column-1-slot-1]\"></ng-content>\n <lib-form-field-toggle [(toggleOn)]=\"showMore\" style=\"margin: 0px 10px\"></lib-form-field-toggle>\n @if (showMore) {\n <div class=\"form-group-column\">\n <lib-form-field-container\n [title]=\"'TUNNELER'\"\n [title2]=\"'TRAVERSAL'\"\n [layout]=\"'row'\"\n class=\"form-field-advanced\"\n >\n <div class=\"config-item\" [class.disabled]=\"!tunnelerEnabledReadOnly\">\n <div class=\"config-container toggle-container\">\n <div class=\"config-container-label\">TUNNELER ENABLED</div>\n <div (click)=\"tunnelerEnabledReadOnly && !useStrictReadonlyForm && toggleTunnelerEnabled()\"\n [ngClass]=\"{ on: formData.isTunnelerEnabled, disabled: !tunnelerEnabledReadOnly, 'toggle-readonly': useStrictReadonlyForm }\" class=\"toggle\"\n [matTooltip]=\"useStrictReadonlyForm ? '' : (!tunnelerEnabledReadOnly ? 'Disabled because already hosted by netfoundry ' : '')\"\n matTooltipPosition=\"above\" style=\"pointer-events: auto;\">\n <span [hidden]=\"!formData.isTunnelerEnabled\" class=\"on-label\">YES</span>\n <span [hidden]=\"formData.isTunnelerEnabled\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n <div class=\"config-item\">\n <div class=\"config-container toggle-container\">\n <div class=\"config-container-label\">ALLOW TRAVERSAL</div>\n <div\n (click)=\"!useStrictReadonlyForm && toggleNoTraversal()\"\n [ngClass]=\"{ on: !formData.noTraversal, 'toggle-readonly': useStrictReadonlyForm }\"\n class=\"toggle\"\n >\n <span [hidden]=\"formData.noTraversal\" class=\"on-label\">YES</span>\n <span [hidden]=\"!formData.noTraversal\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n </lib-form-field-container>\n @if (isCustomerHosted) {\n <lib-form-field-container\n [title]=\"'LINK LISTENER'\"\n class=\"form-field-advanced\"\n >\n <div class=\"config-item disabled\">\n <div class=\"config-container toggle-container\">\n <div class=\"config-container-label\">ENABLE LINK LISTENER</div>\n <div\n [ngClass]=\"{ on: formData.linkListener, disabled: true }\"\n class=\"toggle\"\n role=\"switch\"\n aria-disabled=\"true\"\n [attr.aria-checked]=\"!!formData.linkListener\"\n matTooltip=\"This value is reported by the router and can't be changed here.\"\n matTooltipPosition=\"above\"\n >\n <span [hidden]=\"!formData.linkListener\" class=\"on-label\">YES</span>\n <span [hidden]=\"formData.linkListener\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n </lib-form-field-container>\n }\n @if (formData?.hostname || formData?.supportedProtocols?.tls || formData?.supportedProtocols?.wss) {\n <lib-form-field-container [showHeader]=\"false\">\n @if (formData?.hostname) {\n <div class=\"config-item\">\n <div class=\"config-item-label-container\">\n <span class=\"config-title\">Domain Name</span>\n </div>\n <div class=\"config-container domain-container\">\n <div class=\"config-container-label domain-label\">\n {{ formData?.hostname }}\n </div>\n </div>\n </div>\n }\n @if (formData?.supportedProtocols?.tls || formData?.supportedProtocols?.wss) {\n <div class=\"config-item\">\n <div class=\"config-item-label-container\">\n <span class=\"config-title\">ADVERTISE ADDRESS</span>\n </div>\n <div class=\"config-container advertise-address-container\">\n <div class=\"config-container-label advertise-address-label\">\n {{ formData?.supportedProtocols?.tls }}\n <br />\n {{ formData?.supportedProtocols?.wss }}\n </div>\n </div>\n </div>\n }\n </lib-form-field-container>\n }\n <lib-form-field-container\n [title]=\"'Custom Tags'\"\n [label]=\"'OPTIONAL'\"\n class=\"form-field-advanced\"\n >\n <lib-custom-tags [(tags)]=\"formData.tags\" [readOnly]=\"useStrictReadonlyForm\"></lib-custom-tags>\n </lib-form-field-container>\n <lib-form-field-container\n [title]=\"'App Data'\"\n [label]=\"'OPTIONAL'\"\n class=\"form-field-advanced\"\n >\n <lib-json-view [(data)]=\"formData.appData\" [readOnly]=\"useStrictReadonlyForm\"></lib-json-view>\n </lib-form-field-container>\n </div>\n }\n </div>\n <div class=\"form-group-column two-fifths\">\n <ng-content select=\"[slot=column-2-slot-1]\"></ng-content>\n @if (hasEnrolmentToken && !extService['hideZitiRegistration']) {\n <lib-form-field-container\n [showHeader]=\"false\"\n >\n <lib-qr-code\n [identity]=\"formData\"\n [jwt]=\"formData.enrollmentJwt\"\n [token]=\"formData.enrollmentToken\"\n [expiration]=\"formData.enrollmentExpiresAt\"\n (doRefresh)=\"refreshEdgeRouter()\"\n [type]=\"'router'\"\n ></lib-qr-code>\n </lib-form-field-container>\n }\n @if (formData.id) {\n <lib-form-field-container\n [title]=\"'Associated Services'\"\n [count]=\"svc.associatedServices.length\"\n [class]=\"'hide-overflow'\"\n [helpText]=\"'Preview list of all services that this Edge Router is associated with.'\"\n >\n <lib-preview-list\n [clickable]=\"false\"\n [isLoading]=\"servicesLoading\"\n [allNames]=\"svc.associatedServiceNames\"\n ></lib-preview-list>\n </lib-form-field-container>\n }\n @if (formData.id) {\n <lib-form-field-container\n [title]=\"'Associated Identities'\"\n [count]=\"svc.associatedIdentities.length\"\n [helpText]=\"'Preview list of all Identities that this Edge Router is associated with.'\"\n >\n <lib-preview-list\n [clickable]=\"false\"\n [isLoading]=\"identitiesLoading\"\n [allNames]=\"svc.associatedIdentityNames\"\n ></lib-preview-list>\n </lib-form-field-container>\n }\n @if (showApiCalls) {\n <lib-form-field-container\n [title]=\"'API Calls'\"\n [label]=\"'OPTIONAL'\"\n >\n <div class=\"form-row\">\n <input class=\"form-field-input\" [value]=\"apiCallURL\"/>\n <div class=\"icon-copy copy-text\" (click)=\"copyToClipboard(apiCallURL)\"></div>\n </div>\n @if (formData) {\n <lib-json-view [(data)]=\"apiData\" [readOnly]=\"true\" [showCopy]=\"true\"></lib-json-view>\n }\n </lib-form-field-container>\n }\n </div>\n </div>\n }\n @if (formView === 'raw') {\n <div class=\"form-group-column\">\n @if (formData) {\n <lib-json-view [(data)]=\"formData\" [readOnly]=\"useStrictReadonlyForm\"></lib-json-view>\n }\n </div>\n }\n </div>\n</div>\n@if (isLoading || !dataInit) {\n <lib-loading-indicator [isLoading]=\"isLoading || !dataInit\"></lib-loading-indicator>\n}\n", styles: [".edge-router-form-container{display:flex;flex-direction:column;height:100%;width:100%;align-items:center;gap:.9375rem}.form-field-dropdown{background-image:url(/assets/svgs/ArrowDown.svg);background-position:center right .625rem;background-repeat:no-repeat;background-size:1.125rem;padding-right:1.75rem}.disabled{opacity:.5;pointer-events:none}.toggle.disabled{cursor:not-allowed}::ng-deep .admin-toggle{width:100%;width:35%;display:flex}::ng-deep .admin-toggle .form-field-line{display:none}::ng-deep .admin-toggle .toggle{width:100%}::ng-deep .is-admin-container .form-field-title-container.title-container-2{width:35%!important}.svc-test-result{position:absolute;transition:var(--transition);height:0;width:100%;background-color:var(--background);bottom:0;font-weight:600;padding-left:.625rem;padding-right:.625rem;border-color:var(--inputBorder);border-style:solid;border-width:var(--inputBorderWidth);border-bottom-right-radius:var(--inputBorderRadius);border-bottom-left-radius:var(--inputBorderRadius);border-top-style:none}.svc-test-result.test-result-open{height:fit-content;box-shadow:0 -.125rem .6875rem 0 var(--boxShadow);padding-bottom:.9375rem;padding-top:.9375rem}.svc-test-result .close.icon-close{top:.625rem;right:.625rem;font-size:1.125rem;height:1.125rem;width:1.125rem;line-height:1.125rem}::ng-deep .admin-toggle .form-field-toggle-container:focus .form-field-label{color:var(--primaryColor)}::ng-deep .admin-toggle .form-field-toggle-container:focus .form-field-line{border-color:var(--primaryColor)}::ng-deep .admin-toggle .form-field-toggle-container:focus .toggle{border-color:var(--primaryColor);border-style:solid;border-width:var(--inputBorderWidth)}::ng-deep .admin-toggle .form-field-toggle-container:focus .toggle .switch{top:.0625rem}.config-container-label.domain-label,.config-container-label.advertise-address-label{text-transform:lowercase}.config-container-label.advertise-address-label{height:auto;padding:.125rem 0}.config-container.domain-container,.config-container.advertise-address-container{background-color:var(--stroke)}.config-container.advertise-address-container{height:auto;min-height:var(--defaultInputHeight);padding:0rem 1.25rem}\n"] }]
|
|
19907
22266
|
}], ctorParameters: () => [{ type: SettingsService, decorators: [{
|
|
19908
22267
|
type: Inject,
|
|
19909
22268
|
args: [SETTINGS_SERVICE]
|
|
@@ -26403,6 +28762,9 @@ class JwtSigner {
|
|
|
26403
28762
|
this.targetToken = 'ACCESS';
|
|
26404
28763
|
this.enrollToCertEnabled = false;
|
|
26405
28764
|
this.enrollToTokenEnabled = false;
|
|
28765
|
+
this.enrollAuthPolicyId = '';
|
|
28766
|
+
this.enrollNameClaimsSelector = '';
|
|
28767
|
+
this.enrollAttributeClaimsSelector = '';
|
|
26406
28768
|
}
|
|
26407
28769
|
}
|
|
26408
28770
|
;
|
|
@@ -26616,6 +28978,7 @@ class JwtSignerFormComponent extends ProjectableForm {
|
|
|
26616
28978
|
this.configKey = 'oauth_test_callback_config';
|
|
26617
28979
|
this.tokenTypeKey = 'oauth_test_callback_token_type';
|
|
26618
28980
|
this.oidcVerified = false;
|
|
28981
|
+
this.authPolicies = [];
|
|
26619
28982
|
this.usePreviousLocation = false;
|
|
26620
28983
|
this.entityType = 'external-jwt-signers';
|
|
26621
28984
|
this.entityClass = JwtSigner;
|
|
@@ -26627,10 +28990,25 @@ class JwtSignerFormComponent extends ProjectableForm {
|
|
|
26627
28990
|
this.settingsService.settingsChange.subscribe((results) => {
|
|
26628
28991
|
this.settings = results;
|
|
26629
28992
|
});
|
|
28993
|
+
this.getAuthPolicies();
|
|
26630
28994
|
if (window.location.href.indexOf('test-auth') > 0) {
|
|
26631
28995
|
this.handleOAuthCallback();
|
|
26632
28996
|
}
|
|
26633
28997
|
}
|
|
28998
|
+
getAuthPolicies() {
|
|
28999
|
+
const paging = {
|
|
29000
|
+
filter: "",
|
|
29001
|
+
noSearch: true,
|
|
29002
|
+
order: "asc",
|
|
29003
|
+
page: 1,
|
|
29004
|
+
searchOn: "name",
|
|
29005
|
+
sort: "name",
|
|
29006
|
+
total: 100
|
|
29007
|
+
};
|
|
29008
|
+
this.zitiService.get('auth-policies', paging, []).then((result) => {
|
|
29009
|
+
this.authPolicies = [...result.data];
|
|
29010
|
+
});
|
|
29011
|
+
}
|
|
26634
29012
|
ngAfterViewInit() {
|
|
26635
29013
|
super.ngAfterViewInit(true);
|
|
26636
29014
|
}
|
|
@@ -26796,6 +29174,18 @@ class JwtSignerFormComponent extends ProjectableForm {
|
|
|
26796
29174
|
}
|
|
26797
29175
|
this.formData.targetToken = val;
|
|
26798
29176
|
}
|
|
29177
|
+
get enrollAuthPolicyId() {
|
|
29178
|
+
if (this.formData && isNil(this.formData.enrollAuthPolicyId)) {
|
|
29179
|
+
this.formData.enrollAuthPolicyId = '';
|
|
29180
|
+
}
|
|
29181
|
+
return this.formData?.enrollAuthPolicyId;
|
|
29182
|
+
}
|
|
29183
|
+
set enrollAuthPolicyId(val) {
|
|
29184
|
+
if (!this.formData) {
|
|
29185
|
+
return;
|
|
29186
|
+
}
|
|
29187
|
+
this.formData.enrollAuthPolicyId = val;
|
|
29188
|
+
}
|
|
26799
29189
|
get apiCallURL() {
|
|
26800
29190
|
return this.settings.selectedEdgeController + '/edge/management/v1/external-jwt-signers' + (this.formData.id ? `/${this.formData.id}` : '');
|
|
26801
29191
|
}
|
|
@@ -26822,6 +29212,11 @@ class JwtSignerFormComponent extends ProjectableForm {
|
|
|
26822
29212
|
if (!isEmpty(this.formData.targetToken) && this.showTokenType) {
|
|
26823
29213
|
data.targetToken = this.formData.targetToken;
|
|
26824
29214
|
}
|
|
29215
|
+
if (this.showAutoEnrollment) {
|
|
29216
|
+
data.enrollAuthPolicyId = this.formData.enrollAuthPolicyId || '';
|
|
29217
|
+
data.enrollNameClaimsSelector = this.formData.enrollNameClaimsSelector || '';
|
|
29218
|
+
data.enrollAttributeClaimsSelector = this.formData.enrollAttributeClaimsSelector || '';
|
|
29219
|
+
}
|
|
26825
29220
|
if (!isEmpty(this.formData.id)) {
|
|
26826
29221
|
data.id = this.formData.id;
|
|
26827
29222
|
}
|
|
@@ -27091,11 +29486,11 @@ class JwtSignerFormComponent extends ProjectableForm {
|
|
|
27091
29486
|
return !this.errors.jwksEndpoint;
|
|
27092
29487
|
}
|
|
27093
29488
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: JwtSignerFormComponent, deps: [{ token: JwtSignerFormService }, { token: SchemaService }, { token: GrowlerService }, { token: SHAREDZ_EXTENSION }, { token: SETTINGS_SERVICE }, { token: ZITI_DATA_SERVICE }, { token: i4.Router }, { token: i4.ActivatedRoute }, { token: i1$1.Location }, { token: ValidationService }, { token: AuthService }, { token: i1$6.OAuthService }, { token: i1$3.HttpClient }, { token: ZAC_LOGIN_SERVICE }, { token: i5.MatDialog }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
27094
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: JwtSignerFormComponent, isStandalone: false, selector: "lib-configuration", inputs: { errors: "errors" }, outputs: { close: "close" }, viewQueries: [{ propertyName: "filterInput", first: true, predicate: ["fileSelect"], descendants: true }, { propertyName: "oidcVerification", first: true, predicate: ["oidcVerification"], descendants: true }], usesInheritance: true, ngImport: i0, template: "<div class=\"projectable-form-wrapper\"\n (keyup.enter)=\"save($event)\"\n (keyup.escape)=\"closeModal(false)\"\n tabindex=\"0\"\n >\n <lib-form-header\n [data]=\"formData\"\n [title]=\"(formData.id ? 'JWT Signer Details' : 'Create JWT Signer')\"\n [moreActions]=\"moreActions\"\n [multiActions]=\"multiActions\"\n (actionRequested)=\"headerActionRequested($event)\"\n (multiActionRequested)=\"multiActionRequested($event)\"\n [(formView)]=\"formView\"\n [saveDisabled]=\"svc.saveDisabled || formDataInvalid || formPermissionSaveDisabled\"\n [saveTooltip]=\"formPermissionSaveTooltip\"\n [strictReadonlyUi]=\"useStrictReadonlyForm\"\n ></lib-form-header>\n <div class=\"jwt-signer-form-container projectable-form-container\" #scrollContainer>\n <div class=\"projectable-form-main-column form-group-row\" [hidden]=\"formView !== 'simple'\">\n <div class=\"form-group-column three-fifths\">\n <lib-form-field-container\n [title]=\"'Name'\"\n [label]=\"useStrictReadonlyForm ? '' : 'REQUIRED'\"\n >\n @if (!useStrictReadonlyForm) {\n <input class=\"form-field-input\" [(ngModel)]=\"formData.name\" [ngClass]=\"{error: errors['name']}\" placeholder=\"Name this JWT Signer\" #nameFieldInput/>\n } @else {\n <div class=\"form-field-readonly-value\">{{ formData.name }}</div>\n }\n </lib-form-field-container>\n <lib-form-field-container\n [layout]=\"'column'\"\n [showHeader]=\"false\"\n >\n <div class=\"form-field-label-container\">\n <div class=\"form-field-header\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Issuer</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The "issuer" field on the JWT Signer must match the "iss" field on the JWT\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <span class=\"form-field-label\" >{{ useStrictReadonlyForm ? '' : 'REQUIRED' }}</span>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter issuer\" [(ngModel)]=\"formData.issuer\" [ngClass]=\"{error: errors['issuer']}\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Audience</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The expected "aud" (audience) field on the JWT\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter audience\" [(ngModel)]=\"formData.audience\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-row\">\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Claims Property</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The field on the JWT for mapping to an identity. Matches the identity's id or externalId if "Use External Id" is set to true. Defaults to "sub"\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter claims property\" [(ngModel)]=\"formData.claimsProperty\" (keyup)=\"checkExternalIdToggle()\" [ngClass]=\"{error: errors['claimsProperty']}\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-header\">\n <div class=\"form-field-title-container\">\n <div class=\"config-container-label\" style=\"font-weight: 600; font-size: 14px;\">External ID</div>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"If enabled, will match the "claimsProperty" to an identity's "externalId" field. If false, it will match to the identity's id field.\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n </div>\n <div class=\"config-item\">\n <div class=\"config-container toggle-container\">\n <div class=\"form-field-title-container\">\n <div class=\"config-container-label\">Use External ID</div>\n </div>\n <div\n (click)=\"!useStrictReadonlyForm && toggleUseExternalId()\"\n [ngClass]=\"{ on: formData.useExternalId, 'toggle-readonly': useStrictReadonlyForm }\"\n class=\"toggle\"\n >\n <span [hidden]=\"!formData.useExternalId\" class=\"on-label\">YES</span>\n <span [hidden]=\"formData.useExternalId\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </lib-form-field-container>\n <lib-form-field-container\n [showHeader]=\"false\"\n >\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Client ID</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The client id to use when authenticating to the IDP\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter client ID\" [(ngModel)]=\"formData.clientId\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">External Auth Url</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The URL that users are directed to, to obtain a JWT\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter external auth URL\" [(ngModel)]=\"formData.externalAuthUrl\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Scopes</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"Enter as a comma or space seperated list of claims/permissions\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <lib-chips-input\n (keyup)=\"scopesOnKeyup($event)\"\n [(ngModel)]=\"formData.scopes\"\n [allowDuplicate]=\"false\"\n placeholder=\"Enter scopes\"\n [addOnBlur]=\"true\"\n separator=\",\"\n [disabled]=\"useStrictReadonlyForm\"\n ></lib-chips-input>\n </div>\n </lib-form-field-container>\n @if (showAutoEnrollment) {\n <lib-form-field-container\n [title]=\"'Auto Enrollment'\"\n [helpText]=\"'Click for more information'\"\n (helpClicked)=\"showAutoEnrollHelp()\"\n >\n <div class=\"form-field-row\">\n <div class=\"config-item\">\n <div class=\"config-container toggle-container\">\n <div class=\"config-container-label\">Auto-Enroll to Cert</div>\n <div\n (click)=\"toggleEnrollToCertEnabled()\"\n [ngClass]=\"{ on: formData.enrollToCertEnabled, 'toggle-readonly': useStrictReadonlyForm }\"\n class=\"toggle\"\n >\n <span [hidden]=\"!formData.enrollToCertEnabled\" class=\"on-label\">YES</span>\n <span [hidden]=\"formData.enrollToCertEnabled\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n <div class=\"config-item\">\n <div class=\"config-container toggle-container\">\n <div class=\"config-container-label\">Auto-Enroll to Token</div>\n <div\n (click)=\"toggleEnrollToTokenEnabled()\"\n [ngClass]=\"{ on: formData.enrollToTokenEnabled, 'toggle-readonly': useStrictReadonlyForm }\"\n class=\"toggle\"\n >\n <span [hidden]=\"!formData.enrollToTokenEnabled\" class=\"on-label\">YES</span>\n <span [hidden]=\"formData.enrollToTokenEnabled\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n </div>\n </lib-form-field-container>\n }\n <lib-form-field-toggle [(toggleOn)]=\"showMore\" (toggleOnChange)=\"showMoreChanged($event)\" style=\"margin: 0px 10px\"></lib-form-field-toggle>\n @if (showMore && showTokenType) {\n <lib-form-field-container\n [title]=\"'Target Token Type'\"\n [label]=\"'Required'\"\n [helpText]=\"'The target token type. Can be either Access or ID'\"\n class=\"form-field-advanced\"\n >\n <select\n [(ngModel)]=\"targetToken\"\n id=\"authPolicyId\"\n class=\"form-field-dropdown\"\n [disabled]=\"useStrictReadonlyForm\"\n >\n <option value=\"ACCESS\">Access</option>\n <option value=\"ID\">ID</option>\n </select>\n </lib-form-field-container>\n }\n <div [hidden]=\"!showMore\" class=\"form-group-column\">\n <lib-form-field-container\n [title]=\"'Verify OIDC Authentication'\"\n class=\"form-field-advanced\"\n [ngClass]=\"{'formFieldError': !oidcVerified && oidcErrorMessageDetail, 'formFieldSuccess': oidcVerified}\"\n >\n <div class=\"form-field-row\" #oidcVerification>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <div class=\"form-field-title\">Callback URL</div>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"This URL must be added to the list of allowed callback URLs in your OAuth provider configuration\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <div class=\"copy-link-container\">\n <pre class=\"read-only-2\" (click)=\"copyCallbackURL()\">{{ callbackURL }}</pre>\n <div class=\"copy-button\" (click)=\"copyCallbackURL()\"></div>\n </div>\n </div>\n @if (!useStrictReadonlyForm) {\n <div class=\"test-oauth-button\"\n [ngClass]=\"{'oidc-verified': oidcVerified}\"\n (click)=\"testOIDCAuthentication()\"\n >\n {{oidcVerified ? 'Verified' : 'Authenticate' }}\n @if (oauthLoading) {\n <div class=\"spinner\"></div>\n }\n @if (oidcVerified) {\n <input type=\"checkbox\" [disabled]=\"true\" [checked]=\"true\" class=\"success-check\">\n }\n </div>\n }\n </div>\n @if (oidcErrorMessageDetail) {\n <div class=\"oidc-error-message-container\">\n <code class=\"oidc-error-message-source\">{{oidcErrorMessageSource}}: </code>\n <code class=\"oidc-error-message-detail\">{{oidcErrorMessageDetail}}</code>\n @if (oidcErrorMessageDetail2) {\n <code class=\"oidc-error-message-detail2\">{{oidcErrorMessageDetail2}}</code>\n }\n @if (oidcErrorMessageDetail3) {\n <code class=\"oidc-error-message-detail2\">{{oidcErrorMessageDetail3}}</code>\n }\n </div>\n }\n @if (oidcAuthTokenClaims) {\n <div class=\"oidc-token-claims-container\">\n <code class=\"oidc-token-claims-title\">OAuth Token Claims: </code>\n <lib-json-view [showLineNumbers]=\"false\" [(data)]=\"oidcAuthTokenClaims\" [readOnly]=\"useStrictReadonlyForm\"></lib-json-view>\n </div>\n }\n </lib-form-field-container>\n <lib-form-field-container\n [title]=\"'Custom Tags'\"\n [label]=\"'OPTIONAL'\"\n class=\"form-field-advanced\"\n >\n <lib-custom-tags [(tags)]=\"formData.tags\" [readOnly]=\"useStrictReadonlyForm\"></lib-custom-tags>\n </lib-form-field-container>\n </div>\n </div>\n <div class=\"form-group-column two-fifths\">\n <lib-form-field-container\n [title]=\"'Verification'\"\n [label]=\"useStrictReadonlyForm ? '' : 'REQUIRED'\"\n >\n <div\n class=\"radio-group-container\"\n [class.radio-group-readonly]=\"useStrictReadonlyForm\"\n (keydown)=\"radioKeyDownHandler($event)\"\n [attr.tabindex]=\"useStrictReadonlyForm ? -1 : 0\"\n >\n <div\n class=\"radio-button-container\"\n [ngClass]=\"{'selected': signatureMethod === 'JWKS_ENDPOINT', 'toggle-readonly': useStrictReadonlyForm}\"\n (click)=\"!useStrictReadonlyForm && selectSignatureMethod('JWKS_ENDPOINT')\"\n >\n <div class=\"radio-button-circle\">\n <div class=\"radio-button-inner-circle\"></div>\n </div>\n <span class=\"radio-button-label\">JWKS Endpoint</span>\n </div>\n <div\n class=\"radio-button-container\"\n [ngClass]=\"{'selected': signatureMethod === 'CERT_PEM', 'toggle-readonly': useStrictReadonlyForm}\"\n (click)=\"!useStrictReadonlyForm && selectSignatureMethod('CERT_PEM')\"\n >\n <div class=\"radio-button-circle\">\n <div class=\"radio-button-inner-circle\"></div>\n </div>\n <span class=\"radio-button-label\">Cert PEM</span>\n </div>\n </div>\n @if (signatureMethod === 'JWKS_ENDPOINT') {\n <div class=\"form-field-input-group\">\n <div class=\"form-field-label-container select-file-label-container\">\n <div class=\"form-field-header\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">JWKS Endpoint</span>\n </div>\n </div>\n <input\n class=\"form-field-input\"\n placeholder=\"Enter endpoint URL\"\n [(ngModel)]=\"formData.jwksEndpoint\"\n [ngClass]=\"{error: errors['jwksEndpoint']}\"\n [disabled]=\"useStrictReadonlyForm\"\n />\n </div>\n </div>\n }\n @if (signatureMethod === 'CERT_PEM') {\n <div class=\"form-field-input-group\">\n <div class=\"form-field-label-container select-file-label-container\">\n <div class=\"form-field-header\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">PEM</span>\n </div>\n <span\n class=\"select-file-button\"\n [class.toggle-readonly]=\"useStrictReadonlyForm\"\n (click)=\"!useStrictReadonlyForm && openFileSelect($event)\"\n >\n Select File\n @if (fileSelectOpening) {\n <div class=\"spinner\"></div>\n }\n </span>\n <input\n #fileSelect\n type=\"file\"\n style=\"display:none\"\n [disabled]=\"useStrictReadonlyForm\"\n (change)=\"selectPemFile($event)\"\n >\n </div>\n <textarea\n placeholder=\"Paste pem contents or select file\"\n [(ngModel)]=\"formData.certPem\"\n [ngClass]=\"{error: errors['certPem']}\"\n [disabled]=\"useStrictReadonlyForm\"\n ></textarea>\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-header\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">KID</span>\n </div>\n <span class=\"form-field-label\" >Optional</span>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter KID\" [(ngModel)]=\"formData.kid\" [disabled]=\"useStrictReadonlyForm\"/>\n </div>\n </div>\n }\n </lib-form-field-container>\n <lib-form-field-container\n [showHeader]=\"false\"\n >\n <div class=\"config-item\">\n <div class=\"config-container toggle-container\">\n <div class=\"config-container-label\">ENABLED</div>\n <div\n (click)=\"!useStrictReadonlyForm && toggleEnabled()\"\n [ngClass]=\"{ on: formData.enabled, 'toggle-readonly': useStrictReadonlyForm }\"\n class=\"toggle\"\n >\n <span [hidden]=\"!formData.enabled\" class=\"on-label\">YES</span>\n <span [hidden]=\"formData.enabled\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n </lib-form-field-container>\n @if (showApiCalls) {\n <lib-form-field-container\n [title]=\"'API Calls'\"\n [headerActions]=\"apiOptions\"\n (actionRequested)=\"apiActionRequested($event)\"\n [class]=\"'api-data-no-wrap'\"\n >\n <div class=\"form-row\">\n <input class=\"form-field-input\" [value]=\"apiCallURL\"/>\n <div class=\"icon-copy copy-text\" (click)=\"copyToClipboard(apiCallURL)\"></div>\n </div>\n @if (formData) {\n <lib-json-view [showLineNumbers]=\"false\" [(data)]=\"apiData\" [readOnly]=\"true\" [showCopy]=\"true\"></lib-json-view>\n }\n </lib-form-field-container>\n }\n </div>\n </div>\n @if (formView === 'raw') {\n <div class=\"form-group-column\">\n @if (formData) {\n <lib-json-view [(data)]=\"formData\" [readOnly]=\"useStrictReadonlyForm\"></lib-json-view>\n }\n </div>\n }\n </div>\n</div>\n@if (isLoading) {\n <lib-loading-indicator [isLoading]=\"isLoading\"></lib-loading-indicator>\n}\n<lib-side-modal [(open)]=\"showAutoEnrollHelpModal\" [showClose]=\"true\">\n <div class=\"help-panel-content\">\n <h2 class=\"help-panel-title\">What is Auto Enrollment?</h2>\n <p class=\"help-panel-intro\">\n Auto Enrollment enables automatic identity creation when users authenticate via JWT tokens.\n </p>\n\n <div class=\"help-panel-section\">\n <h3 class=\"help-panel-subtitle\">\n <span class=\"icon-certificate\"></span>\n Auto-Enroll to Cert\n </h3>\n <p class=\"help-panel-text\">\n When enabled, the controller will automatically create an identity and enroll it with a certificate when a user authenticates using a JWT token. This is useful for certificate-based authentication workflows.\n </p>\n </div>\n\n <div class=\"help-panel-section\">\n <h3 class=\"help-panel-subtitle\">\n <span class=\"icon-key\"></span>\n Auto-Enroll to Token\n </h3>\n <p class=\"help-panel-text\">\n When enabled, the controller will automatically create an identity and enroll it with an authentication token when a user authenticates using a JWT token. This is useful for token-based authentication workflows.\n </p>\n </div>\n\n <div class=\"help-panel-note\">\n <strong>Note:</strong> These options are only available in Ziti 2.0 and later versions.\n </div>\n </div>\n</lib-side-modal>\n", styles: ["@charset \"UTF-8\";.jwt-signer-form-container{display:flex;flex-direction:column;height:100%;width:100%;align-items:center;gap:var(--gapXL)}.jwt-signer-form-container .select-file-button{padding-left:var(--paddingMedium);padding-right:var(--paddingMedium);height:1.5625rem;width:fit-content;white-space:nowrap;display:flex;align-items:center;justify-content:center;font-weight:600;font-size:.875rem;background:var(--primary);border-style:solid;border-width:.0625rem;border-color:var(--stroke);color:var(--white);cursor:pointer;border-radius:var(--inputBorderRadius);gap:var(--marginMedium);margin-top:-.3125rem}.jwt-signer-form-container .select-file-button:hover{filter:brightness(.8)}.jwt-signer-form-container .select-file-button:active{filter:grayscale(1);transform:translateY(.0625rem)}.jwt-signer-form-container .select-file-button .spinner{display:inline-block;width:.5rem;height:.5rem;border-radius:50%;background:transparent;border-top:.125rem solid white;border-right:.125rem solid white;border-bottom:.125rem solid transparent;border-left:.125rem solid transparent;-webkit-animation:loading .5s infinite linear;animation:loading .5s infinite linear}.jwt-signer-form-container .select-file-label-container{gap:var(--marginMedium)}::ng-deep .jwt-signer-form-container select:disabled,::ng-deep .jwt-signer-form-container input:disabled{opacity:.75!important;cursor:text;background-color:var(--stroke)!important}::ng-deep .api-data-no-wrap .jse-text-mode{width:100%}::ng-deep .api-data-no-wrap .jse-text-mode .cm-editor{width:100%}::ng-deep .api-data-no-wrap .jse-text-mode .cm-editor .cm-scroller{width:100%}::ng-deep .api-data-no-wrap .jse-text-mode .cm-editor .cm-scroller .cm-content{width:100%;overflow:auto}::ng-deep .api-data-no-wrap .jse-text-mode .cm-editor .cm-scroller .cm-content .\\37cr{white-space:nowrap}.radio-group-container{display:flex;gap:var(--marginLarge)}.radio-group-container:focus .radio-button-container .radio-button-circle{border-color:var(--primaryColor)}.radio-group-container .radio-button-container{display:flex;flex-direction:row;align-items:center;background-color:var(--formGroup);border-radius:var(--inputBorderRadius);width:100%;padding-left:var(--paddingMedium);padding-right:var(--paddingMedium);padding-top:var(--paddingLarge);padding-bottom:var(--paddingLarge);cursor:pointer;gap:var(--marginMedium)}.radio-group-container .radio-button-container .radio-button-circle{display:flex;width:1.3125rem;height:1.3125rem;border-radius:1.3125rem;border-width:.125rem;border-style:solid;border-color:var(--offWhite_ALWAYS);align-items:center;justify-content:center;background-color:var(--offWhite)}.radio-group-container .radio-button-container .radio-button-circle .radio-button-inner-circle{display:none;width:.9375rem;height:.9375rem;border-radius:.9375rem;align-items:center;justify-content:center;background-color:var(--primaryColor)}.radio-group-container .radio-button-container.selected .radio-button-circle .radio-button-inner-circle{display:flex}.radio-group-container .radio-button-container:active .radio-button-circle .radio-button-inner-circle{display:flex;background-color:var(--menu)}.radio-group-container .radio-button-container .radio-button-label{color:var(--offWhite_ALWAYS);font-size:.875rem;font-weight:600}.radio-button-container.toggle-readonly{cursor:default;pointer-events:none;opacity:.92}.select-file-button.toggle-readonly{cursor:default;pointer-events:none;opacity:.85}.test-oauth-button{color:var(--offWhite_ALWAYS);height:var(--defaultInputHeight);border-style:solid;border-color:var(--inputBorder);border-radius:var(--inputBorderRadius);vertical-align:middle;text-align:center;position:relative;display:flex;align-items:center;justify-content:center;overflow:hidden;transition:color .2s;font-size:.9375rem;font-weight:600;font-family:Open Sans,sans-serif;text-transform:uppercase;cursor:pointer;width:100%;background-color:var(--primaryColor);margin-top:1.5rem;max-width:15rem}.test-oauth-button.oidc-verified{filter:brightness(.95);color:var(--offWhite_ALWAYS);background-color:var(--green)}.test-oauth-button .spinner{display:inline-block;width:var(--spinnerSizeMedium);height:var(--spinnerSizeMedium);border-radius:50%;background:transparent;border-top:var(--spinnerBorderTopMedium);border-right:var(--spinnerBorderRightMedium);border-bottom:var(--spinnerBorderBottomMedium);border-left:var(--spinnerBorderLeftMedium);animation:loading .5s infinite linear;margin-left:var(--marginSmall)}.test-oauth-button:hover{filter:brightness(.95)}.test-oauth-button:active{transform:translateY(1px)}.success-check{width:1.0625rem;height:1.0625rem;cursor:pointer;background-color:var(--navigation);margin:0;margin-top:.25rem;border-radius:2rem;margin-left:var(--marginSmall)}.success-check:checked{background-color:var(--green)!important;background-image:url(/assets/images/check.png);background-size:.675rem;background-position:center center;background-repeat:no-repeat;border:none}.success-check:checked:disabled{opacity:1!important}.oidc-token-claims-container{background-color:var(--backgroundMuted);height:fit-content;display:flex;flex-direction:column;align-items:flex-start;justify-content:flex-start;padding:var(--paddingMedium);border-radius:var(--inputBorderRadius);color:var(--tableText);font-weight:600;overflow:auto}.oidc-token-claims-container .oidc-token-claims-title{font-weight:600;font-size:1rem;white-space:nowrap;margin-bottom:var(--marginMedium)}.oidc-error-message-container{background-color:var(--lightRed);height:fit-content;display:flex;flex-direction:column;align-items:flex-start;justify-content:flex-start;padding:var(--paddingMedium);border-radius:var(--inputBorderRadius);color:var(--red);font-weight:600;overflow:auto}.oidc-error-message-container .oidc-error-message-source{font-weight:600;font-size:1rem;white-space:nowrap}.oidc-error-message-container .oidc-error-message-detail2,.oidc-error-message-container .oidc-error-message-detail{margin-top:var(--marginSmall);font-weight:400;white-space:nowrap}.oidc-error-message-container::-webkit-scrollbar{height:6px;width:6px}.oidc-error-message-container::-webkit-scrollbar-thumb{background-color:var(--menu);border-radius:10px}.oidc-error-message-container:hover::-webkit-scrollbar-thumb{background-clip:unset!important}.read-only-2{display:flex;overflow:auto;white-space:nowrap;opacity:1!important;cursor:pointer;padding:var(--paddingMedium);padding-bottom:var(--paddingSmall)}.read-only-2::-webkit-scrollbar{height:6px;width:6px}.read-only-2::-webkit-scrollbar-thumb{background-color:var(--menu);border-radius:10px}.read-only-2:hover{text-decoration:underline}.read-only-2:hover::-webkit-scrollbar-thumb{background-clip:unset}.copy-link-container{display:flex;flex-direction:row;justify-content:space-between;align-items:center;opacity:1!important;cursor:text;border-radius:var(--inputBorderRadius);height:var(--defaultInputHeight);background-color:var(--backgroundMuted)!important}.copy-button{display:flex;align-items:center;justify-content:center;height:100%;width:2.575rem;margin-left:.125rem;background:url(/assets/svgs/copy.svg) no-repeat;background-size:1.125rem 1.125rem;background-position:center;cursor:pointer;border-top-right-radius:var(--inputBorderRadius);border-bottom-right-radius:var(--inputBorderRadius);background-color:#0000000f}.copy-button:hover{filter:brightness(.95);background-color:#0000001f}.help-panel-content{display:flex;flex-direction:column;padding-top:3rem}.help-panel-content .help-panel-title{margin:0 0 .25rem;color:var(--primary);font-size:1.5rem;font-weight:600;line-height:1.3;padding-right:2rem}.help-panel-content .help-panel-intro{line-height:1.6;margin-bottom:1.5rem;color:var(--tableText);font-size:.9375rem}.help-panel-content .help-panel-section{margin-bottom:1.5rem;padding:1rem;background-color:var(--navigation);border-radius:var(--inputBorderRadius);border-left:.25rem solid var(--primary)}.help-panel-content .help-panel-section .help-panel-subtitle{margin:0 0 .75rem;color:var(--primary);font-size:1.125rem;font-weight:600;display:flex;align-items:center;gap:.5rem}.help-panel-content .help-panel-section .help-panel-subtitle span[class^=icon-]{font-size:1rem;opacity:.8}.help-panel-content .help-panel-section .help-panel-text{margin:0;line-height:1.6;color:var(--tableText);font-size:.875rem}.help-panel-content .help-panel-note{padding:1rem;background-color:#ffc1071a;border-radius:var(--inputBorderRadius);border-left:.25rem solid #ffc107;color:var(--tableText);font-size:.875rem;line-height:1.6}.help-panel-content .help-panel-note strong{color:#ffc107}\n"], dependencies: [{ kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i3.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: ChipsInputComponent, selector: "lib-chips-input", inputs: ["placeholder", "inputId", "addOnBlur", "allowDuplicate", "separator", "invalid"], outputs: ["onAdd", "onRemove", "onModelChange", "onBlur", "keyup"] }, { kind: "component", type: SideModalComponent, selector: "lib-side-modal", inputs: ["showClose", "open"], outputs: ["openChange"] }, { kind: "component", type: FormHeaderComponent, selector: "lib-form-header", inputs: ["data", "badges", "title", "moreActions", "formView", "saveDisabled", "saveTooltip", "actionButtonText", "moreActionsText", "showHeaderToggle", "showHeaderButton", "strictReadonlyUi", "multiActions"], outputs: ["formViewChange", "actionRequested", "multiActionRequested"] }, { kind: "component", type: FormFieldContainerComponent, selector: "lib-form-field-container", inputs: ["title", "title2", "titleIcon", "layout", "helpText", "helpText2", "label", "count", "action", "actionLabel", "class", "contentStyle", "contentClass", "showHeader", "showToggle", "headerActions", "headerToggle"], outputs: ["actionRequested", "headerToggleChange", "helpClicked"] }, { kind: "component", type: LoadingIndicatorComponent, selector: "lib-loading-indicator", inputs: ["isLoading"] }, { kind: "component", type: FormFieldToggleComponent, selector: "lib-form-field-toggle", inputs: ["label", "orientation", "labelOn", "labelOff", "toggleOn"], outputs: ["toggleOnChange"] }, { kind: "component", type: JsonViewComponent, selector: "lib-json-view", inputs: ["data", "schema", "readOnly", "jsonInvalid", "showCopy", "showLineNumbers"], outputs: ["dataChange", "jsonChange", "jsonInvalidChange"] }, { kind: "component", type: CustomTagsComponent, selector: "lib-custom-tags", inputs: ["tags", "readOnly"], outputs: ["tagsChange"] }] }); }
|
|
29489
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: JwtSignerFormComponent, isStandalone: false, selector: "lib-configuration", inputs: { errors: "errors" }, outputs: { close: "close" }, viewQueries: [{ propertyName: "filterInput", first: true, predicate: ["fileSelect"], descendants: true }, { propertyName: "oidcVerification", first: true, predicate: ["oidcVerification"], descendants: true }], usesInheritance: true, ngImport: i0, template: "<div class=\"projectable-form-wrapper\"\n (keyup.enter)=\"save($event)\"\n (keyup.escape)=\"closeModal(false)\"\n tabindex=\"0\"\n >\n <lib-form-header\n [data]=\"formData\"\n [title]=\"(formData.id ? 'JWT Signer Details' : 'Create JWT Signer')\"\n [moreActions]=\"moreActions\"\n [multiActions]=\"multiActions\"\n (actionRequested)=\"headerActionRequested($event)\"\n (multiActionRequested)=\"multiActionRequested($event)\"\n [(formView)]=\"formView\"\n [saveDisabled]=\"svc.saveDisabled || formDataInvalid || formPermissionSaveDisabled\"\n [saveTooltip]=\"formPermissionSaveTooltip\"\n [strictReadonlyUi]=\"useStrictReadonlyForm\"\n ></lib-form-header>\n <div class=\"jwt-signer-form-container projectable-form-container\" #scrollContainer>\n <div class=\"projectable-form-main-column form-group-row\" [hidden]=\"formView !== 'simple'\">\n <div class=\"form-group-column three-fifths\">\n <lib-form-field-container\n [title]=\"'Name'\"\n [label]=\"useStrictReadonlyForm ? '' : 'REQUIRED'\"\n >\n @if (!useStrictReadonlyForm) {\n <input class=\"form-field-input\" [(ngModel)]=\"formData.name\" [ngClass]=\"{error: errors['name']}\" placeholder=\"Name this JWT Signer\" #nameFieldInput/>\n } @else {\n <div class=\"form-field-readonly-value\">{{ formData.name }}</div>\n }\n </lib-form-field-container>\n <lib-form-field-container\n [layout]=\"'column'\"\n [showHeader]=\"false\"\n >\n <div class=\"form-field-label-container\">\n <div class=\"form-field-header\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Issuer</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The "issuer" field on the JWT Signer must match the "iss" field on the JWT\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <span class=\"form-field-label\" >{{ useStrictReadonlyForm ? '' : 'REQUIRED' }}</span>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter issuer\" [(ngModel)]=\"formData.issuer\" [ngClass]=\"{error: errors['issuer']}\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Audience</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The expected "aud" (audience) field on the JWT\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter audience\" [(ngModel)]=\"formData.audience\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-row\">\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Claims Property</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The field on the JWT for mapping to an identity. Matches the identity's id or externalId if "Use External Id" is set to true. Defaults to "sub"\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter claims property\" [(ngModel)]=\"formData.claimsProperty\" (keyup)=\"checkExternalIdToggle()\" [ngClass]=\"{error: errors['claimsProperty']}\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-header\">\n <div class=\"form-field-title-container\">\n <div class=\"config-container-label\" style=\"font-weight: 600; font-size: 14px;\">External ID</div>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"If enabled, will match the "claimsProperty" to an identity's "externalId" field. If false, it will match to the identity's id field.\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n </div>\n <div class=\"config-item\">\n <div class=\"config-container toggle-container\">\n <div class=\"form-field-title-container\">\n <div class=\"config-container-label\">Use External ID</div>\n </div>\n <div\n (click)=\"!useStrictReadonlyForm && toggleUseExternalId()\"\n [ngClass]=\"{ on: formData.useExternalId, 'toggle-readonly': useStrictReadonlyForm }\"\n class=\"toggle\"\n >\n <span [hidden]=\"!formData.useExternalId\" class=\"on-label\">YES</span>\n <span [hidden]=\"formData.useExternalId\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </lib-form-field-container>\n <lib-form-field-container\n [showHeader]=\"false\"\n >\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Client ID</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The client id to use when authenticating to the IDP\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter client ID\" [(ngModel)]=\"formData.clientId\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">External Auth Url</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The URL that users are directed to, to obtain a JWT\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter external auth URL\" [(ngModel)]=\"formData.externalAuthUrl\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Scopes</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"Enter as a comma or space seperated list of claims/permissions\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <lib-chips-input\n (keyup)=\"scopesOnKeyup($event)\"\n [(ngModel)]=\"formData.scopes\"\n [allowDuplicate]=\"false\"\n placeholder=\"Enter scopes\"\n [addOnBlur]=\"true\"\n separator=\",\"\n [disabled]=\"useStrictReadonlyForm\"\n ></lib-chips-input>\n </div>\n </lib-form-field-container>\n @if (showAutoEnrollment) {\n <lib-form-field-container\n [title]=\"'Auto Enrollment'\"\n [helpText]=\"'Click for more information'\"\n (helpClicked)=\"showAutoEnrollHelp()\"\n >\n <div class=\"form-field-row\">\n <div class=\"config-item\">\n <div class=\"config-container toggle-container\">\n <div class=\"config-container-label\">Auto-Enroll to Cert</div>\n <div\n (click)=\"toggleEnrollToCertEnabled()\"\n [ngClass]=\"{ on: formData.enrollToCertEnabled, 'toggle-readonly': useStrictReadonlyForm }\"\n class=\"toggle\"\n >\n <span [hidden]=\"!formData.enrollToCertEnabled\" class=\"on-label\">YES</span>\n <span [hidden]=\"formData.enrollToCertEnabled\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n <div class=\"config-item\">\n <div class=\"config-container toggle-container\">\n <div class=\"config-container-label\">Auto-Enroll to Token</div>\n <div\n (click)=\"toggleEnrollToTokenEnabled()\"\n [ngClass]=\"{ on: formData.enrollToTokenEnabled, 'toggle-readonly': useStrictReadonlyForm }\"\n class=\"toggle\"\n >\n <span [hidden]=\"!formData.enrollToTokenEnabled\" class=\"on-label\">YES</span>\n <span [hidden]=\"formData.enrollToTokenEnabled\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n </div>\n </lib-form-field-container>\n }\n <lib-form-field-toggle [(toggleOn)]=\"showMore\" (toggleOnChange)=\"showMoreChanged($event)\" style=\"margin: 0px 10px\"></lib-form-field-toggle>\n @if (showMore && showTokenType) {\n <lib-form-field-container\n [title]=\"'Target Token Type'\"\n [label]=\"'Required'\"\n [helpText]=\"'The target token type. Can be either Access or ID'\"\n class=\"form-field-advanced\"\n >\n <select\n [(ngModel)]=\"targetToken\"\n id=\"authPolicyId\"\n class=\"form-field-dropdown\"\n [disabled]=\"useStrictReadonlyForm\"\n >\n <option value=\"ACCESS\">Access</option>\n <option value=\"ID\">ID</option>\n </select>\n </lib-form-field-container>\n }\n <div [hidden]=\"!showMore\" class=\"form-group-column\">\n @if (showAutoEnrollment) {\n <lib-form-field-container\n [title]=\"'Auto Enrollment Options'\"\n class=\"form-field-advanced\"\n >\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Enrollment Auth Policy</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The authentication policy assigned to identities created through auto-enrollment. Defaults to the "default" policy.\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <select\n [(ngModel)]=\"enrollAuthPolicyId\"\n class=\"form-field-dropdown\"\n [disabled]=\"useStrictReadonlyForm\"\n >\n <option value=\"\">Select an Authentication Policy</option>\n @for (policy of authPolicies; track policy.id) {\n <option [value]=\"policy.id\">{{ policy.name }}</option>\n }\n </select>\n </div>\n <div class=\"form-field-row\">\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Name Claims Selector</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The claims JSON pointer selector or top level property used for the name of enrolling identities. Defaults to /sub\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter name claims selector\" [(ngModel)]=\"formData.enrollNameClaimsSelector\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Attribute Claims Selector</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The claims JSON pointer selector or top level property used for the attributes of enrolling identities.\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter attribute claims selector\" [(ngModel)]=\"formData.enrollAttributeClaimsSelector\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n </div>\n </lib-form-field-container>\n }\n <lib-form-field-container\n [title]=\"'Verify OIDC Authentication'\"\n class=\"form-field-advanced\"\n [ngClass]=\"{'formFieldError': !oidcVerified && oidcErrorMessageDetail, 'formFieldSuccess': oidcVerified}\"\n >\n <div class=\"form-field-row\" #oidcVerification>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <div class=\"form-field-title\">Callback URL</div>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"This URL must be added to the list of allowed callback URLs in your OAuth provider configuration\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <div class=\"copy-link-container\">\n <pre class=\"read-only-2\" (click)=\"copyCallbackURL()\">{{ callbackURL }}</pre>\n <div class=\"copy-button\" (click)=\"copyCallbackURL()\"></div>\n </div>\n </div>\n @if (!useStrictReadonlyForm) {\n <div class=\"test-oauth-button\"\n [ngClass]=\"{'oidc-verified': oidcVerified}\"\n (click)=\"testOIDCAuthentication()\"\n >\n {{oidcVerified ? 'Verified' : 'Authenticate' }}\n @if (oauthLoading) {\n <div class=\"spinner\"></div>\n }\n @if (oidcVerified) {\n <input type=\"checkbox\" [disabled]=\"true\" [checked]=\"true\" class=\"success-check\">\n }\n </div>\n }\n </div>\n @if (oidcErrorMessageDetail) {\n <div class=\"oidc-error-message-container\">\n <code class=\"oidc-error-message-source\">{{oidcErrorMessageSource}}: </code>\n <code class=\"oidc-error-message-detail\">{{oidcErrorMessageDetail}}</code>\n @if (oidcErrorMessageDetail2) {\n <code class=\"oidc-error-message-detail2\">{{oidcErrorMessageDetail2}}</code>\n }\n @if (oidcErrorMessageDetail3) {\n <code class=\"oidc-error-message-detail2\">{{oidcErrorMessageDetail3}}</code>\n }\n </div>\n }\n @if (oidcAuthTokenClaims) {\n <div class=\"oidc-token-claims-container\">\n <code class=\"oidc-token-claims-title\">OAuth Token Claims: </code>\n <lib-json-view [showLineNumbers]=\"false\" [(data)]=\"oidcAuthTokenClaims\" [readOnly]=\"useStrictReadonlyForm\"></lib-json-view>\n </div>\n }\n </lib-form-field-container>\n <lib-form-field-container\n [title]=\"'Custom Tags'\"\n [label]=\"'OPTIONAL'\"\n class=\"form-field-advanced\"\n >\n <lib-custom-tags [(tags)]=\"formData.tags\" [readOnly]=\"useStrictReadonlyForm\"></lib-custom-tags>\n </lib-form-field-container>\n </div>\n </div>\n <div class=\"form-group-column two-fifths\">\n <lib-form-field-container\n [title]=\"'Verification'\"\n [label]=\"useStrictReadonlyForm ? '' : 'REQUIRED'\"\n >\n <div\n class=\"radio-group-container\"\n [class.radio-group-readonly]=\"useStrictReadonlyForm\"\n (keydown)=\"radioKeyDownHandler($event)\"\n [attr.tabindex]=\"useStrictReadonlyForm ? -1 : 0\"\n >\n <div\n class=\"radio-button-container\"\n [ngClass]=\"{'selected': signatureMethod === 'JWKS_ENDPOINT', 'toggle-readonly': useStrictReadonlyForm}\"\n (click)=\"!useStrictReadonlyForm && selectSignatureMethod('JWKS_ENDPOINT')\"\n >\n <div class=\"radio-button-circle\">\n <div class=\"radio-button-inner-circle\"></div>\n </div>\n <span class=\"radio-button-label\">JWKS Endpoint</span>\n </div>\n <div\n class=\"radio-button-container\"\n [ngClass]=\"{'selected': signatureMethod === 'CERT_PEM', 'toggle-readonly': useStrictReadonlyForm}\"\n (click)=\"!useStrictReadonlyForm && selectSignatureMethod('CERT_PEM')\"\n >\n <div class=\"radio-button-circle\">\n <div class=\"radio-button-inner-circle\"></div>\n </div>\n <span class=\"radio-button-label\">Cert PEM</span>\n </div>\n </div>\n @if (signatureMethod === 'JWKS_ENDPOINT') {\n <div class=\"form-field-input-group\">\n <div class=\"form-field-label-container select-file-label-container\">\n <div class=\"form-field-header\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">JWKS Endpoint</span>\n </div>\n </div>\n <input\n class=\"form-field-input\"\n placeholder=\"Enter endpoint URL\"\n [(ngModel)]=\"formData.jwksEndpoint\"\n [ngClass]=\"{error: errors['jwksEndpoint']}\"\n [disabled]=\"useStrictReadonlyForm\"\n />\n </div>\n </div>\n }\n @if (signatureMethod === 'CERT_PEM') {\n <div class=\"form-field-input-group\">\n <div class=\"form-field-label-container select-file-label-container\">\n <div class=\"form-field-header\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">PEM</span>\n </div>\n <span\n class=\"select-file-button\"\n [class.toggle-readonly]=\"useStrictReadonlyForm\"\n (click)=\"!useStrictReadonlyForm && openFileSelect($event)\"\n >\n Select File\n @if (fileSelectOpening) {\n <div class=\"spinner\"></div>\n }\n </span>\n <input\n #fileSelect\n type=\"file\"\n style=\"display:none\"\n [disabled]=\"useStrictReadonlyForm\"\n (change)=\"selectPemFile($event)\"\n >\n </div>\n <textarea\n placeholder=\"Paste pem contents or select file\"\n [(ngModel)]=\"formData.certPem\"\n [ngClass]=\"{error: errors['certPem']}\"\n [disabled]=\"useStrictReadonlyForm\"\n ></textarea>\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-header\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">KID</span>\n </div>\n <span class=\"form-field-label\" >Optional</span>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter KID\" [(ngModel)]=\"formData.kid\" [disabled]=\"useStrictReadonlyForm\"/>\n </div>\n </div>\n }\n </lib-form-field-container>\n <lib-form-field-container\n [showHeader]=\"false\"\n >\n <div class=\"config-item\">\n <div class=\"config-container toggle-container\">\n <div class=\"config-container-label\">ENABLED</div>\n <div\n (click)=\"!useStrictReadonlyForm && toggleEnabled()\"\n [ngClass]=\"{ on: formData.enabled, 'toggle-readonly': useStrictReadonlyForm }\"\n class=\"toggle\"\n >\n <span [hidden]=\"!formData.enabled\" class=\"on-label\">YES</span>\n <span [hidden]=\"formData.enabled\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n </lib-form-field-container>\n @if (showApiCalls) {\n <lib-form-field-container\n [title]=\"'API Calls'\"\n [headerActions]=\"apiOptions\"\n (actionRequested)=\"apiActionRequested($event)\"\n [class]=\"'api-data-no-wrap'\"\n >\n <div class=\"form-row\">\n <input class=\"form-field-input\" [value]=\"apiCallURL\"/>\n <div class=\"icon-copy copy-text\" (click)=\"copyToClipboard(apiCallURL)\"></div>\n </div>\n @if (formData) {\n <lib-json-view [showLineNumbers]=\"false\" [(data)]=\"apiData\" [readOnly]=\"true\" [showCopy]=\"true\"></lib-json-view>\n }\n </lib-form-field-container>\n }\n </div>\n </div>\n @if (formView === 'raw') {\n <div class=\"form-group-column\">\n @if (formData) {\n <lib-json-view [(data)]=\"formData\" [readOnly]=\"useStrictReadonlyForm\"></lib-json-view>\n }\n </div>\n }\n </div>\n</div>\n@if (isLoading) {\n <lib-loading-indicator [isLoading]=\"isLoading\"></lib-loading-indicator>\n}\n<lib-side-modal [(open)]=\"showAutoEnrollHelpModal\" [showClose]=\"true\">\n <div class=\"help-panel-content\">\n <h2 class=\"help-panel-title\">What is Auto Enrollment?</h2>\n <p class=\"help-panel-intro\">\n Auto Enrollment enables automatic identity creation when users authenticate via JWT tokens.\n </p>\n\n <div class=\"help-panel-section\">\n <h3 class=\"help-panel-subtitle\">\n <span class=\"icon-certificate\"></span>\n Auto-Enroll to Cert\n </h3>\n <p class=\"help-panel-text\">\n When enabled, the controller will automatically create an identity and enroll it with a certificate when a user authenticates using a JWT token. This is useful for certificate-based authentication workflows.\n </p>\n </div>\n\n <div class=\"help-panel-section\">\n <h3 class=\"help-panel-subtitle\">\n <span class=\"icon-key\"></span>\n Auto-Enroll to Token\n </h3>\n <p class=\"help-panel-text\">\n When enabled, the controller will automatically create an identity and enroll it with an authentication token when a user authenticates using a JWT token. This is useful for token-based authentication workflows.\n </p>\n </div>\n\n <div class=\"help-panel-section\">\n <h3 class=\"help-panel-subtitle\">\n <span class=\"icon-lock\"></span>\n Enrollment Auth Policy\n </h3>\n <p class=\"help-panel-text\">\n The authentication policy assigned to identities that are created through auto-enrollment. When left unset, the controller's \"default\" authentication policy is used.\n </p>\n </div>\n\n <div class=\"help-panel-section\">\n <h3 class=\"help-panel-subtitle\">\n <span class=\"icon-identity\"></span>\n Name Claims Selector\n </h3>\n <p class=\"help-panel-text\">\n A JSON pointer selector (or top-level claim property) identifying which JWT claim supplies the name for enrolling identities. Defaults to <code>/sub</code> when unset.\n </p>\n </div>\n\n <div class=\"help-panel-section\">\n <h3 class=\"help-panel-subtitle\">\n <span class=\"icon-identity\"></span>\n Attribute Claims Selector\n </h3>\n <p class=\"help-panel-text\">\n A JSON pointer selector (or top-level claim property) identifying which JWT claim supplies the role attributes for enrolling identities.\n </p>\n </div>\n\n <div class=\"help-panel-note\">\n <strong>Note:</strong> These options are only available in Ziti 2.0 and later versions.\n </div>\n </div>\n</lib-side-modal>\n", styles: ["@charset \"UTF-8\";.jwt-signer-form-container{display:flex;flex-direction:column;height:100%;width:100%;align-items:center;gap:var(--gapXL)}.jwt-signer-form-container .select-file-button{padding-left:var(--paddingMedium);padding-right:var(--paddingMedium);height:1.5625rem;width:fit-content;white-space:nowrap;display:flex;align-items:center;justify-content:center;font-weight:600;font-size:.875rem;background:var(--primary);border-style:solid;border-width:.0625rem;border-color:var(--stroke);color:var(--white);cursor:pointer;border-radius:var(--inputBorderRadius);gap:var(--marginMedium);margin-top:-.3125rem}.jwt-signer-form-container .select-file-button:hover{filter:brightness(.8)}.jwt-signer-form-container .select-file-button:active{filter:grayscale(1);transform:translateY(.0625rem)}.jwt-signer-form-container .select-file-button .spinner{display:inline-block;width:.5rem;height:.5rem;border-radius:50%;background:transparent;border-top:.125rem solid white;border-right:.125rem solid white;border-bottom:.125rem solid transparent;border-left:.125rem solid transparent;-webkit-animation:loading .5s infinite linear;animation:loading .5s infinite linear}.jwt-signer-form-container .select-file-label-container{gap:var(--marginMedium)}::ng-deep .jwt-signer-form-container select:disabled,::ng-deep .jwt-signer-form-container input:disabled{opacity:.75!important;cursor:text;background-color:var(--stroke)!important}::ng-deep .api-data-no-wrap .jse-text-mode{width:100%}::ng-deep .api-data-no-wrap .jse-text-mode .cm-editor{width:100%}::ng-deep .api-data-no-wrap .jse-text-mode .cm-editor .cm-scroller{width:100%}::ng-deep .api-data-no-wrap .jse-text-mode .cm-editor .cm-scroller .cm-content{width:100%;overflow:auto}::ng-deep .api-data-no-wrap .jse-text-mode .cm-editor .cm-scroller .cm-content .\\37cr{white-space:nowrap}.radio-group-container{display:flex;gap:var(--marginLarge)}.radio-group-container:focus .radio-button-container .radio-button-circle{border-color:var(--primaryColor)}.radio-group-container .radio-button-container{display:flex;flex-direction:row;align-items:center;background-color:var(--formGroup);border-radius:var(--inputBorderRadius);width:100%;padding-left:var(--paddingMedium);padding-right:var(--paddingMedium);padding-top:var(--paddingLarge);padding-bottom:var(--paddingLarge);cursor:pointer;gap:var(--marginMedium)}.radio-group-container .radio-button-container .radio-button-circle{display:flex;width:1.3125rem;height:1.3125rem;border-radius:1.3125rem;border-width:.125rem;border-style:solid;border-color:var(--offWhite_ALWAYS);align-items:center;justify-content:center;background-color:var(--offWhite)}.radio-group-container .radio-button-container .radio-button-circle .radio-button-inner-circle{display:none;width:.9375rem;height:.9375rem;border-radius:.9375rem;align-items:center;justify-content:center;background-color:var(--primaryColor)}.radio-group-container .radio-button-container.selected .radio-button-circle .radio-button-inner-circle{display:flex}.radio-group-container .radio-button-container:active .radio-button-circle .radio-button-inner-circle{display:flex;background-color:var(--menu)}.radio-group-container .radio-button-container .radio-button-label{color:var(--offWhite_ALWAYS);font-size:.875rem;font-weight:600}.radio-button-container.toggle-readonly{cursor:default;pointer-events:none;opacity:.92}.select-file-button.toggle-readonly{cursor:default;pointer-events:none;opacity:.85}.test-oauth-button{color:var(--offWhite_ALWAYS);height:var(--defaultInputHeight);border-style:solid;border-color:var(--inputBorder);border-radius:var(--inputBorderRadius);vertical-align:middle;text-align:center;position:relative;display:flex;align-items:center;justify-content:center;overflow:hidden;transition:color .2s;font-size:.9375rem;font-weight:600;font-family:Open Sans,sans-serif;text-transform:uppercase;cursor:pointer;width:100%;background-color:var(--primaryColor);margin-top:1.5rem;max-width:15rem}.test-oauth-button.oidc-verified{filter:brightness(.95);color:var(--offWhite_ALWAYS);background-color:var(--green)}.test-oauth-button .spinner{display:inline-block;width:var(--spinnerSizeMedium);height:var(--spinnerSizeMedium);border-radius:50%;background:transparent;border-top:var(--spinnerBorderTopMedium);border-right:var(--spinnerBorderRightMedium);border-bottom:var(--spinnerBorderBottomMedium);border-left:var(--spinnerBorderLeftMedium);animation:loading .5s infinite linear;margin-left:var(--marginSmall)}.test-oauth-button:hover{filter:brightness(.95)}.test-oauth-button:active{transform:translateY(1px)}.success-check{width:1.0625rem;height:1.0625rem;cursor:pointer;background-color:var(--navigation);margin:0;margin-top:.25rem;border-radius:2rem;margin-left:var(--marginSmall)}.success-check:checked{background-color:var(--green)!important;background-image:url(/assets/images/check.png);background-size:.675rem;background-position:center center;background-repeat:no-repeat;border:none}.success-check:checked:disabled{opacity:1!important}.oidc-token-claims-container{background-color:var(--backgroundMuted);height:fit-content;display:flex;flex-direction:column;align-items:flex-start;justify-content:flex-start;padding:var(--paddingMedium);border-radius:var(--inputBorderRadius);color:var(--tableText);font-weight:600;overflow:auto}.oidc-token-claims-container .oidc-token-claims-title{font-weight:600;font-size:1rem;white-space:nowrap;margin-bottom:var(--marginMedium)}.oidc-error-message-container{background-color:var(--lightRed);height:fit-content;display:flex;flex-direction:column;align-items:flex-start;justify-content:flex-start;padding:var(--paddingMedium);border-radius:var(--inputBorderRadius);color:var(--red);font-weight:600;overflow:auto}.oidc-error-message-container .oidc-error-message-source{font-weight:600;font-size:1rem;white-space:nowrap}.oidc-error-message-container .oidc-error-message-detail2,.oidc-error-message-container .oidc-error-message-detail{margin-top:var(--marginSmall);font-weight:400;white-space:nowrap}.oidc-error-message-container::-webkit-scrollbar{height:6px;width:6px}.oidc-error-message-container::-webkit-scrollbar-thumb{background-color:var(--menu);border-radius:10px}.oidc-error-message-container:hover::-webkit-scrollbar-thumb{background-clip:unset!important}.read-only-2{display:flex;overflow:auto;white-space:nowrap;opacity:1!important;cursor:pointer;padding:var(--paddingMedium);padding-bottom:var(--paddingSmall)}.read-only-2::-webkit-scrollbar{height:6px;width:6px}.read-only-2::-webkit-scrollbar-thumb{background-color:var(--menu);border-radius:10px}.read-only-2:hover{text-decoration:underline}.read-only-2:hover::-webkit-scrollbar-thumb{background-clip:unset}.copy-link-container{display:flex;flex-direction:row;justify-content:space-between;align-items:center;opacity:1!important;cursor:text;border-radius:var(--inputBorderRadius);height:var(--defaultInputHeight);background-color:var(--backgroundMuted)!important}.copy-button{display:flex;align-items:center;justify-content:center;height:100%;width:2.575rem;margin-left:.125rem;background:url(/assets/svgs/copy.svg) no-repeat;background-size:1.125rem 1.125rem;background-position:center;cursor:pointer;border-top-right-radius:var(--inputBorderRadius);border-bottom-right-radius:var(--inputBorderRadius);background-color:#0000000f}.copy-button:hover{filter:brightness(.95);background-color:#0000001f}.help-panel-content{display:flex;flex-direction:column;padding-top:3rem}.help-panel-content .help-panel-title{margin:0 0 .25rem;color:var(--primary);font-size:1.5rem;font-weight:600;line-height:1.3;padding-right:2rem}.help-panel-content .help-panel-intro{line-height:1.6;margin-bottom:1.5rem;color:var(--tableText);font-size:.9375rem}.help-panel-content .help-panel-section{margin-bottom:1.5rem;padding:1rem;background-color:var(--navigation);border-radius:var(--inputBorderRadius);border-left:.25rem solid var(--primary)}.help-panel-content .help-panel-section .help-panel-subtitle{margin:0 0 .75rem;color:var(--primary);font-size:1.125rem;font-weight:600;display:flex;align-items:center;gap:.5rem}.help-panel-content .help-panel-section .help-panel-subtitle span[class^=icon-]{font-size:1rem;opacity:.8}.help-panel-content .help-panel-section .help-panel-text{margin:0;line-height:1.6;color:var(--tableText);font-size:.875rem}.help-panel-content .help-panel-note{padding:1rem;background-color:#ffc1071a;border-radius:var(--inputBorderRadius);border-left:.25rem solid #ffc107;color:var(--tableText);font-size:.875rem;line-height:1.6}.help-panel-content .help-panel-note strong{color:#ffc107}\n"], dependencies: [{ kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i3.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: ChipsInputComponent, selector: "lib-chips-input", inputs: ["placeholder", "inputId", "addOnBlur", "allowDuplicate", "separator", "invalid"], outputs: ["onAdd", "onRemove", "onModelChange", "onBlur", "keyup"] }, { kind: "component", type: SideModalComponent, selector: "lib-side-modal", inputs: ["showClose", "open"], outputs: ["openChange"] }, { kind: "component", type: FormHeaderComponent, selector: "lib-form-header", inputs: ["data", "badges", "title", "moreActions", "formView", "saveDisabled", "saveTooltip", "actionButtonText", "moreActionsText", "showHeaderToggle", "showHeaderButton", "strictReadonlyUi", "multiActions"], outputs: ["formViewChange", "actionRequested", "multiActionRequested"] }, { kind: "component", type: FormFieldContainerComponent, selector: "lib-form-field-container", inputs: ["title", "title2", "titleIcon", "layout", "helpText", "helpText2", "label", "count", "action", "actionLabel", "class", "contentStyle", "contentClass", "showHeader", "showToggle", "headerActions", "headerToggle"], outputs: ["actionRequested", "headerToggleChange", "helpClicked"] }, { kind: "component", type: LoadingIndicatorComponent, selector: "lib-loading-indicator", inputs: ["isLoading"] }, { kind: "component", type: FormFieldToggleComponent, selector: "lib-form-field-toggle", inputs: ["label", "orientation", "labelOn", "labelOff", "toggleOn"], outputs: ["toggleOnChange"] }, { kind: "component", type: JsonViewComponent, selector: "lib-json-view", inputs: ["data", "schema", "readOnly", "jsonInvalid", "showCopy", "showLineNumbers"], outputs: ["dataChange", "jsonChange", "jsonInvalidChange"] }, { kind: "component", type: CustomTagsComponent, selector: "lib-custom-tags", inputs: ["tags", "readOnly"], outputs: ["tagsChange"] }] }); }
|
|
27095
29490
|
}
|
|
27096
29491
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: JwtSignerFormComponent, decorators: [{
|
|
27097
29492
|
type: Component,
|
|
27098
|
-
args: [{ selector: 'lib-configuration', standalone: false, template: "<div class=\"projectable-form-wrapper\"\n (keyup.enter)=\"save($event)\"\n (keyup.escape)=\"closeModal(false)\"\n tabindex=\"0\"\n >\n <lib-form-header\n [data]=\"formData\"\n [title]=\"(formData.id ? 'JWT Signer Details' : 'Create JWT Signer')\"\n [moreActions]=\"moreActions\"\n [multiActions]=\"multiActions\"\n (actionRequested)=\"headerActionRequested($event)\"\n (multiActionRequested)=\"multiActionRequested($event)\"\n [(formView)]=\"formView\"\n [saveDisabled]=\"svc.saveDisabled || formDataInvalid || formPermissionSaveDisabled\"\n [saveTooltip]=\"formPermissionSaveTooltip\"\n [strictReadonlyUi]=\"useStrictReadonlyForm\"\n ></lib-form-header>\n <div class=\"jwt-signer-form-container projectable-form-container\" #scrollContainer>\n <div class=\"projectable-form-main-column form-group-row\" [hidden]=\"formView !== 'simple'\">\n <div class=\"form-group-column three-fifths\">\n <lib-form-field-container\n [title]=\"'Name'\"\n [label]=\"useStrictReadonlyForm ? '' : 'REQUIRED'\"\n >\n @if (!useStrictReadonlyForm) {\n <input class=\"form-field-input\" [(ngModel)]=\"formData.name\" [ngClass]=\"{error: errors['name']}\" placeholder=\"Name this JWT Signer\" #nameFieldInput/>\n } @else {\n <div class=\"form-field-readonly-value\">{{ formData.name }}</div>\n }\n </lib-form-field-container>\n <lib-form-field-container\n [layout]=\"'column'\"\n [showHeader]=\"false\"\n >\n <div class=\"form-field-label-container\">\n <div class=\"form-field-header\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Issuer</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The "issuer" field on the JWT Signer must match the "iss" field on the JWT\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <span class=\"form-field-label\" >{{ useStrictReadonlyForm ? '' : 'REQUIRED' }}</span>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter issuer\" [(ngModel)]=\"formData.issuer\" [ngClass]=\"{error: errors['issuer']}\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Audience</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The expected "aud" (audience) field on the JWT\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter audience\" [(ngModel)]=\"formData.audience\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-row\">\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Claims Property</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The field on the JWT for mapping to an identity. Matches the identity's id or externalId if "Use External Id" is set to true. Defaults to "sub"\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter claims property\" [(ngModel)]=\"formData.claimsProperty\" (keyup)=\"checkExternalIdToggle()\" [ngClass]=\"{error: errors['claimsProperty']}\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-header\">\n <div class=\"form-field-title-container\">\n <div class=\"config-container-label\" style=\"font-weight: 600; font-size: 14px;\">External ID</div>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"If enabled, will match the "claimsProperty" to an identity's "externalId" field. If false, it will match to the identity's id field.\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n </div>\n <div class=\"config-item\">\n <div class=\"config-container toggle-container\">\n <div class=\"form-field-title-container\">\n <div class=\"config-container-label\">Use External ID</div>\n </div>\n <div\n (click)=\"!useStrictReadonlyForm && toggleUseExternalId()\"\n [ngClass]=\"{ on: formData.useExternalId, 'toggle-readonly': useStrictReadonlyForm }\"\n class=\"toggle\"\n >\n <span [hidden]=\"!formData.useExternalId\" class=\"on-label\">YES</span>\n <span [hidden]=\"formData.useExternalId\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </lib-form-field-container>\n <lib-form-field-container\n [showHeader]=\"false\"\n >\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Client ID</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The client id to use when authenticating to the IDP\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter client ID\" [(ngModel)]=\"formData.clientId\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">External Auth Url</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The URL that users are directed to, to obtain a JWT\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter external auth URL\" [(ngModel)]=\"formData.externalAuthUrl\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Scopes</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"Enter as a comma or space seperated list of claims/permissions\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <lib-chips-input\n (keyup)=\"scopesOnKeyup($event)\"\n [(ngModel)]=\"formData.scopes\"\n [allowDuplicate]=\"false\"\n placeholder=\"Enter scopes\"\n [addOnBlur]=\"true\"\n separator=\",\"\n [disabled]=\"useStrictReadonlyForm\"\n ></lib-chips-input>\n </div>\n </lib-form-field-container>\n @if (showAutoEnrollment) {\n <lib-form-field-container\n [title]=\"'Auto Enrollment'\"\n [helpText]=\"'Click for more information'\"\n (helpClicked)=\"showAutoEnrollHelp()\"\n >\n <div class=\"form-field-row\">\n <div class=\"config-item\">\n <div class=\"config-container toggle-container\">\n <div class=\"config-container-label\">Auto-Enroll to Cert</div>\n <div\n (click)=\"toggleEnrollToCertEnabled()\"\n [ngClass]=\"{ on: formData.enrollToCertEnabled, 'toggle-readonly': useStrictReadonlyForm }\"\n class=\"toggle\"\n >\n <span [hidden]=\"!formData.enrollToCertEnabled\" class=\"on-label\">YES</span>\n <span [hidden]=\"formData.enrollToCertEnabled\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n <div class=\"config-item\">\n <div class=\"config-container toggle-container\">\n <div class=\"config-container-label\">Auto-Enroll to Token</div>\n <div\n (click)=\"toggleEnrollToTokenEnabled()\"\n [ngClass]=\"{ on: formData.enrollToTokenEnabled, 'toggle-readonly': useStrictReadonlyForm }\"\n class=\"toggle\"\n >\n <span [hidden]=\"!formData.enrollToTokenEnabled\" class=\"on-label\">YES</span>\n <span [hidden]=\"formData.enrollToTokenEnabled\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n </div>\n </lib-form-field-container>\n }\n <lib-form-field-toggle [(toggleOn)]=\"showMore\" (toggleOnChange)=\"showMoreChanged($event)\" style=\"margin: 0px 10px\"></lib-form-field-toggle>\n @if (showMore && showTokenType) {\n <lib-form-field-container\n [title]=\"'Target Token Type'\"\n [label]=\"'Required'\"\n [helpText]=\"'The target token type. Can be either Access or ID'\"\n class=\"form-field-advanced\"\n >\n <select\n [(ngModel)]=\"targetToken\"\n id=\"authPolicyId\"\n class=\"form-field-dropdown\"\n [disabled]=\"useStrictReadonlyForm\"\n >\n <option value=\"ACCESS\">Access</option>\n <option value=\"ID\">ID</option>\n </select>\n </lib-form-field-container>\n }\n <div [hidden]=\"!showMore\" class=\"form-group-column\">\n <lib-form-field-container\n [title]=\"'Verify OIDC Authentication'\"\n class=\"form-field-advanced\"\n [ngClass]=\"{'formFieldError': !oidcVerified && oidcErrorMessageDetail, 'formFieldSuccess': oidcVerified}\"\n >\n <div class=\"form-field-row\" #oidcVerification>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <div class=\"form-field-title\">Callback URL</div>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"This URL must be added to the list of allowed callback URLs in your OAuth provider configuration\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <div class=\"copy-link-container\">\n <pre class=\"read-only-2\" (click)=\"copyCallbackURL()\">{{ callbackURL }}</pre>\n <div class=\"copy-button\" (click)=\"copyCallbackURL()\"></div>\n </div>\n </div>\n @if (!useStrictReadonlyForm) {\n <div class=\"test-oauth-button\"\n [ngClass]=\"{'oidc-verified': oidcVerified}\"\n (click)=\"testOIDCAuthentication()\"\n >\n {{oidcVerified ? 'Verified' : 'Authenticate' }}\n @if (oauthLoading) {\n <div class=\"spinner\"></div>\n }\n @if (oidcVerified) {\n <input type=\"checkbox\" [disabled]=\"true\" [checked]=\"true\" class=\"success-check\">\n }\n </div>\n }\n </div>\n @if (oidcErrorMessageDetail) {\n <div class=\"oidc-error-message-container\">\n <code class=\"oidc-error-message-source\">{{oidcErrorMessageSource}}: </code>\n <code class=\"oidc-error-message-detail\">{{oidcErrorMessageDetail}}</code>\n @if (oidcErrorMessageDetail2) {\n <code class=\"oidc-error-message-detail2\">{{oidcErrorMessageDetail2}}</code>\n }\n @if (oidcErrorMessageDetail3) {\n <code class=\"oidc-error-message-detail2\">{{oidcErrorMessageDetail3}}</code>\n }\n </div>\n }\n @if (oidcAuthTokenClaims) {\n <div class=\"oidc-token-claims-container\">\n <code class=\"oidc-token-claims-title\">OAuth Token Claims: </code>\n <lib-json-view [showLineNumbers]=\"false\" [(data)]=\"oidcAuthTokenClaims\" [readOnly]=\"useStrictReadonlyForm\"></lib-json-view>\n </div>\n }\n </lib-form-field-container>\n <lib-form-field-container\n [title]=\"'Custom Tags'\"\n [label]=\"'OPTIONAL'\"\n class=\"form-field-advanced\"\n >\n <lib-custom-tags [(tags)]=\"formData.tags\" [readOnly]=\"useStrictReadonlyForm\"></lib-custom-tags>\n </lib-form-field-container>\n </div>\n </div>\n <div class=\"form-group-column two-fifths\">\n <lib-form-field-container\n [title]=\"'Verification'\"\n [label]=\"useStrictReadonlyForm ? '' : 'REQUIRED'\"\n >\n <div\n class=\"radio-group-container\"\n [class.radio-group-readonly]=\"useStrictReadonlyForm\"\n (keydown)=\"radioKeyDownHandler($event)\"\n [attr.tabindex]=\"useStrictReadonlyForm ? -1 : 0\"\n >\n <div\n class=\"radio-button-container\"\n [ngClass]=\"{'selected': signatureMethod === 'JWKS_ENDPOINT', 'toggle-readonly': useStrictReadonlyForm}\"\n (click)=\"!useStrictReadonlyForm && selectSignatureMethod('JWKS_ENDPOINT')\"\n >\n <div class=\"radio-button-circle\">\n <div class=\"radio-button-inner-circle\"></div>\n </div>\n <span class=\"radio-button-label\">JWKS Endpoint</span>\n </div>\n <div\n class=\"radio-button-container\"\n [ngClass]=\"{'selected': signatureMethod === 'CERT_PEM', 'toggle-readonly': useStrictReadonlyForm}\"\n (click)=\"!useStrictReadonlyForm && selectSignatureMethod('CERT_PEM')\"\n >\n <div class=\"radio-button-circle\">\n <div class=\"radio-button-inner-circle\"></div>\n </div>\n <span class=\"radio-button-label\">Cert PEM</span>\n </div>\n </div>\n @if (signatureMethod === 'JWKS_ENDPOINT') {\n <div class=\"form-field-input-group\">\n <div class=\"form-field-label-container select-file-label-container\">\n <div class=\"form-field-header\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">JWKS Endpoint</span>\n </div>\n </div>\n <input\n class=\"form-field-input\"\n placeholder=\"Enter endpoint URL\"\n [(ngModel)]=\"formData.jwksEndpoint\"\n [ngClass]=\"{error: errors['jwksEndpoint']}\"\n [disabled]=\"useStrictReadonlyForm\"\n />\n </div>\n </div>\n }\n @if (signatureMethod === 'CERT_PEM') {\n <div class=\"form-field-input-group\">\n <div class=\"form-field-label-container select-file-label-container\">\n <div class=\"form-field-header\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">PEM</span>\n </div>\n <span\n class=\"select-file-button\"\n [class.toggle-readonly]=\"useStrictReadonlyForm\"\n (click)=\"!useStrictReadonlyForm && openFileSelect($event)\"\n >\n Select File\n @if (fileSelectOpening) {\n <div class=\"spinner\"></div>\n }\n </span>\n <input\n #fileSelect\n type=\"file\"\n style=\"display:none\"\n [disabled]=\"useStrictReadonlyForm\"\n (change)=\"selectPemFile($event)\"\n >\n </div>\n <textarea\n placeholder=\"Paste pem contents or select file\"\n [(ngModel)]=\"formData.certPem\"\n [ngClass]=\"{error: errors['certPem']}\"\n [disabled]=\"useStrictReadonlyForm\"\n ></textarea>\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-header\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">KID</span>\n </div>\n <span class=\"form-field-label\" >Optional</span>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter KID\" [(ngModel)]=\"formData.kid\" [disabled]=\"useStrictReadonlyForm\"/>\n </div>\n </div>\n }\n </lib-form-field-container>\n <lib-form-field-container\n [showHeader]=\"false\"\n >\n <div class=\"config-item\">\n <div class=\"config-container toggle-container\">\n <div class=\"config-container-label\">ENABLED</div>\n <div\n (click)=\"!useStrictReadonlyForm && toggleEnabled()\"\n [ngClass]=\"{ on: formData.enabled, 'toggle-readonly': useStrictReadonlyForm }\"\n class=\"toggle\"\n >\n <span [hidden]=\"!formData.enabled\" class=\"on-label\">YES</span>\n <span [hidden]=\"formData.enabled\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n </lib-form-field-container>\n @if (showApiCalls) {\n <lib-form-field-container\n [title]=\"'API Calls'\"\n [headerActions]=\"apiOptions\"\n (actionRequested)=\"apiActionRequested($event)\"\n [class]=\"'api-data-no-wrap'\"\n >\n <div class=\"form-row\">\n <input class=\"form-field-input\" [value]=\"apiCallURL\"/>\n <div class=\"icon-copy copy-text\" (click)=\"copyToClipboard(apiCallURL)\"></div>\n </div>\n @if (formData) {\n <lib-json-view [showLineNumbers]=\"false\" [(data)]=\"apiData\" [readOnly]=\"true\" [showCopy]=\"true\"></lib-json-view>\n }\n </lib-form-field-container>\n }\n </div>\n </div>\n @if (formView === 'raw') {\n <div class=\"form-group-column\">\n @if (formData) {\n <lib-json-view [(data)]=\"formData\" [readOnly]=\"useStrictReadonlyForm\"></lib-json-view>\n }\n </div>\n }\n </div>\n</div>\n@if (isLoading) {\n <lib-loading-indicator [isLoading]=\"isLoading\"></lib-loading-indicator>\n}\n<lib-side-modal [(open)]=\"showAutoEnrollHelpModal\" [showClose]=\"true\">\n <div class=\"help-panel-content\">\n <h2 class=\"help-panel-title\">What is Auto Enrollment?</h2>\n <p class=\"help-panel-intro\">\n Auto Enrollment enables automatic identity creation when users authenticate via JWT tokens.\n </p>\n\n <div class=\"help-panel-section\">\n <h3 class=\"help-panel-subtitle\">\n <span class=\"icon-certificate\"></span>\n Auto-Enroll to Cert\n </h3>\n <p class=\"help-panel-text\">\n When enabled, the controller will automatically create an identity and enroll it with a certificate when a user authenticates using a JWT token. This is useful for certificate-based authentication workflows.\n </p>\n </div>\n\n <div class=\"help-panel-section\">\n <h3 class=\"help-panel-subtitle\">\n <span class=\"icon-key\"></span>\n Auto-Enroll to Token\n </h3>\n <p class=\"help-panel-text\">\n When enabled, the controller will automatically create an identity and enroll it with an authentication token when a user authenticates using a JWT token. This is useful for token-based authentication workflows.\n </p>\n </div>\n\n <div class=\"help-panel-note\">\n <strong>Note:</strong> These options are only available in Ziti 2.0 and later versions.\n </div>\n </div>\n</lib-side-modal>\n", styles: ["@charset \"UTF-8\";.jwt-signer-form-container{display:flex;flex-direction:column;height:100%;width:100%;align-items:center;gap:var(--gapXL)}.jwt-signer-form-container .select-file-button{padding-left:var(--paddingMedium);padding-right:var(--paddingMedium);height:1.5625rem;width:fit-content;white-space:nowrap;display:flex;align-items:center;justify-content:center;font-weight:600;font-size:.875rem;background:var(--primary);border-style:solid;border-width:.0625rem;border-color:var(--stroke);color:var(--white);cursor:pointer;border-radius:var(--inputBorderRadius);gap:var(--marginMedium);margin-top:-.3125rem}.jwt-signer-form-container .select-file-button:hover{filter:brightness(.8)}.jwt-signer-form-container .select-file-button:active{filter:grayscale(1);transform:translateY(.0625rem)}.jwt-signer-form-container .select-file-button .spinner{display:inline-block;width:.5rem;height:.5rem;border-radius:50%;background:transparent;border-top:.125rem solid white;border-right:.125rem solid white;border-bottom:.125rem solid transparent;border-left:.125rem solid transparent;-webkit-animation:loading .5s infinite linear;animation:loading .5s infinite linear}.jwt-signer-form-container .select-file-label-container{gap:var(--marginMedium)}::ng-deep .jwt-signer-form-container select:disabled,::ng-deep .jwt-signer-form-container input:disabled{opacity:.75!important;cursor:text;background-color:var(--stroke)!important}::ng-deep .api-data-no-wrap .jse-text-mode{width:100%}::ng-deep .api-data-no-wrap .jse-text-mode .cm-editor{width:100%}::ng-deep .api-data-no-wrap .jse-text-mode .cm-editor .cm-scroller{width:100%}::ng-deep .api-data-no-wrap .jse-text-mode .cm-editor .cm-scroller .cm-content{width:100%;overflow:auto}::ng-deep .api-data-no-wrap .jse-text-mode .cm-editor .cm-scroller .cm-content .\\37cr{white-space:nowrap}.radio-group-container{display:flex;gap:var(--marginLarge)}.radio-group-container:focus .radio-button-container .radio-button-circle{border-color:var(--primaryColor)}.radio-group-container .radio-button-container{display:flex;flex-direction:row;align-items:center;background-color:var(--formGroup);border-radius:var(--inputBorderRadius);width:100%;padding-left:var(--paddingMedium);padding-right:var(--paddingMedium);padding-top:var(--paddingLarge);padding-bottom:var(--paddingLarge);cursor:pointer;gap:var(--marginMedium)}.radio-group-container .radio-button-container .radio-button-circle{display:flex;width:1.3125rem;height:1.3125rem;border-radius:1.3125rem;border-width:.125rem;border-style:solid;border-color:var(--offWhite_ALWAYS);align-items:center;justify-content:center;background-color:var(--offWhite)}.radio-group-container .radio-button-container .radio-button-circle .radio-button-inner-circle{display:none;width:.9375rem;height:.9375rem;border-radius:.9375rem;align-items:center;justify-content:center;background-color:var(--primaryColor)}.radio-group-container .radio-button-container.selected .radio-button-circle .radio-button-inner-circle{display:flex}.radio-group-container .radio-button-container:active .radio-button-circle .radio-button-inner-circle{display:flex;background-color:var(--menu)}.radio-group-container .radio-button-container .radio-button-label{color:var(--offWhite_ALWAYS);font-size:.875rem;font-weight:600}.radio-button-container.toggle-readonly{cursor:default;pointer-events:none;opacity:.92}.select-file-button.toggle-readonly{cursor:default;pointer-events:none;opacity:.85}.test-oauth-button{color:var(--offWhite_ALWAYS);height:var(--defaultInputHeight);border-style:solid;border-color:var(--inputBorder);border-radius:var(--inputBorderRadius);vertical-align:middle;text-align:center;position:relative;display:flex;align-items:center;justify-content:center;overflow:hidden;transition:color .2s;font-size:.9375rem;font-weight:600;font-family:Open Sans,sans-serif;text-transform:uppercase;cursor:pointer;width:100%;background-color:var(--primaryColor);margin-top:1.5rem;max-width:15rem}.test-oauth-button.oidc-verified{filter:brightness(.95);color:var(--offWhite_ALWAYS);background-color:var(--green)}.test-oauth-button .spinner{display:inline-block;width:var(--spinnerSizeMedium);height:var(--spinnerSizeMedium);border-radius:50%;background:transparent;border-top:var(--spinnerBorderTopMedium);border-right:var(--spinnerBorderRightMedium);border-bottom:var(--spinnerBorderBottomMedium);border-left:var(--spinnerBorderLeftMedium);animation:loading .5s infinite linear;margin-left:var(--marginSmall)}.test-oauth-button:hover{filter:brightness(.95)}.test-oauth-button:active{transform:translateY(1px)}.success-check{width:1.0625rem;height:1.0625rem;cursor:pointer;background-color:var(--navigation);margin:0;margin-top:.25rem;border-radius:2rem;margin-left:var(--marginSmall)}.success-check:checked{background-color:var(--green)!important;background-image:url(/assets/images/check.png);background-size:.675rem;background-position:center center;background-repeat:no-repeat;border:none}.success-check:checked:disabled{opacity:1!important}.oidc-token-claims-container{background-color:var(--backgroundMuted);height:fit-content;display:flex;flex-direction:column;align-items:flex-start;justify-content:flex-start;padding:var(--paddingMedium);border-radius:var(--inputBorderRadius);color:var(--tableText);font-weight:600;overflow:auto}.oidc-token-claims-container .oidc-token-claims-title{font-weight:600;font-size:1rem;white-space:nowrap;margin-bottom:var(--marginMedium)}.oidc-error-message-container{background-color:var(--lightRed);height:fit-content;display:flex;flex-direction:column;align-items:flex-start;justify-content:flex-start;padding:var(--paddingMedium);border-radius:var(--inputBorderRadius);color:var(--red);font-weight:600;overflow:auto}.oidc-error-message-container .oidc-error-message-source{font-weight:600;font-size:1rem;white-space:nowrap}.oidc-error-message-container .oidc-error-message-detail2,.oidc-error-message-container .oidc-error-message-detail{margin-top:var(--marginSmall);font-weight:400;white-space:nowrap}.oidc-error-message-container::-webkit-scrollbar{height:6px;width:6px}.oidc-error-message-container::-webkit-scrollbar-thumb{background-color:var(--menu);border-radius:10px}.oidc-error-message-container:hover::-webkit-scrollbar-thumb{background-clip:unset!important}.read-only-2{display:flex;overflow:auto;white-space:nowrap;opacity:1!important;cursor:pointer;padding:var(--paddingMedium);padding-bottom:var(--paddingSmall)}.read-only-2::-webkit-scrollbar{height:6px;width:6px}.read-only-2::-webkit-scrollbar-thumb{background-color:var(--menu);border-radius:10px}.read-only-2:hover{text-decoration:underline}.read-only-2:hover::-webkit-scrollbar-thumb{background-clip:unset}.copy-link-container{display:flex;flex-direction:row;justify-content:space-between;align-items:center;opacity:1!important;cursor:text;border-radius:var(--inputBorderRadius);height:var(--defaultInputHeight);background-color:var(--backgroundMuted)!important}.copy-button{display:flex;align-items:center;justify-content:center;height:100%;width:2.575rem;margin-left:.125rem;background:url(/assets/svgs/copy.svg) no-repeat;background-size:1.125rem 1.125rem;background-position:center;cursor:pointer;border-top-right-radius:var(--inputBorderRadius);border-bottom-right-radius:var(--inputBorderRadius);background-color:#0000000f}.copy-button:hover{filter:brightness(.95);background-color:#0000001f}.help-panel-content{display:flex;flex-direction:column;padding-top:3rem}.help-panel-content .help-panel-title{margin:0 0 .25rem;color:var(--primary);font-size:1.5rem;font-weight:600;line-height:1.3;padding-right:2rem}.help-panel-content .help-panel-intro{line-height:1.6;margin-bottom:1.5rem;color:var(--tableText);font-size:.9375rem}.help-panel-content .help-panel-section{margin-bottom:1.5rem;padding:1rem;background-color:var(--navigation);border-radius:var(--inputBorderRadius);border-left:.25rem solid var(--primary)}.help-panel-content .help-panel-section .help-panel-subtitle{margin:0 0 .75rem;color:var(--primary);font-size:1.125rem;font-weight:600;display:flex;align-items:center;gap:.5rem}.help-panel-content .help-panel-section .help-panel-subtitle span[class^=icon-]{font-size:1rem;opacity:.8}.help-panel-content .help-panel-section .help-panel-text{margin:0;line-height:1.6;color:var(--tableText);font-size:.875rem}.help-panel-content .help-panel-note{padding:1rem;background-color:#ffc1071a;border-radius:var(--inputBorderRadius);border-left:.25rem solid #ffc107;color:var(--tableText);font-size:.875rem;line-height:1.6}.help-panel-content .help-panel-note strong{color:#ffc107}\n"] }]
|
|
29493
|
+
args: [{ selector: 'lib-configuration', standalone: false, template: "<div class=\"projectable-form-wrapper\"\n (keyup.enter)=\"save($event)\"\n (keyup.escape)=\"closeModal(false)\"\n tabindex=\"0\"\n >\n <lib-form-header\n [data]=\"formData\"\n [title]=\"(formData.id ? 'JWT Signer Details' : 'Create JWT Signer')\"\n [moreActions]=\"moreActions\"\n [multiActions]=\"multiActions\"\n (actionRequested)=\"headerActionRequested($event)\"\n (multiActionRequested)=\"multiActionRequested($event)\"\n [(formView)]=\"formView\"\n [saveDisabled]=\"svc.saveDisabled || formDataInvalid || formPermissionSaveDisabled\"\n [saveTooltip]=\"formPermissionSaveTooltip\"\n [strictReadonlyUi]=\"useStrictReadonlyForm\"\n ></lib-form-header>\n <div class=\"jwt-signer-form-container projectable-form-container\" #scrollContainer>\n <div class=\"projectable-form-main-column form-group-row\" [hidden]=\"formView !== 'simple'\">\n <div class=\"form-group-column three-fifths\">\n <lib-form-field-container\n [title]=\"'Name'\"\n [label]=\"useStrictReadonlyForm ? '' : 'REQUIRED'\"\n >\n @if (!useStrictReadonlyForm) {\n <input class=\"form-field-input\" [(ngModel)]=\"formData.name\" [ngClass]=\"{error: errors['name']}\" placeholder=\"Name this JWT Signer\" #nameFieldInput/>\n } @else {\n <div class=\"form-field-readonly-value\">{{ formData.name }}</div>\n }\n </lib-form-field-container>\n <lib-form-field-container\n [layout]=\"'column'\"\n [showHeader]=\"false\"\n >\n <div class=\"form-field-label-container\">\n <div class=\"form-field-header\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Issuer</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The "issuer" field on the JWT Signer must match the "iss" field on the JWT\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <span class=\"form-field-label\" >{{ useStrictReadonlyForm ? '' : 'REQUIRED' }}</span>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter issuer\" [(ngModel)]=\"formData.issuer\" [ngClass]=\"{error: errors['issuer']}\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Audience</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The expected "aud" (audience) field on the JWT\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter audience\" [(ngModel)]=\"formData.audience\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-row\">\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Claims Property</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The field on the JWT for mapping to an identity. Matches the identity's id or externalId if "Use External Id" is set to true. Defaults to "sub"\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter claims property\" [(ngModel)]=\"formData.claimsProperty\" (keyup)=\"checkExternalIdToggle()\" [ngClass]=\"{error: errors['claimsProperty']}\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-header\">\n <div class=\"form-field-title-container\">\n <div class=\"config-container-label\" style=\"font-weight: 600; font-size: 14px;\">External ID</div>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"If enabled, will match the "claimsProperty" to an identity's "externalId" field. If false, it will match to the identity's id field.\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n </div>\n <div class=\"config-item\">\n <div class=\"config-container toggle-container\">\n <div class=\"form-field-title-container\">\n <div class=\"config-container-label\">Use External ID</div>\n </div>\n <div\n (click)=\"!useStrictReadonlyForm && toggleUseExternalId()\"\n [ngClass]=\"{ on: formData.useExternalId, 'toggle-readonly': useStrictReadonlyForm }\"\n class=\"toggle\"\n >\n <span [hidden]=\"!formData.useExternalId\" class=\"on-label\">YES</span>\n <span [hidden]=\"formData.useExternalId\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </lib-form-field-container>\n <lib-form-field-container\n [showHeader]=\"false\"\n >\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Client ID</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The client id to use when authenticating to the IDP\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter client ID\" [(ngModel)]=\"formData.clientId\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">External Auth Url</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The URL that users are directed to, to obtain a JWT\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter external auth URL\" [(ngModel)]=\"formData.externalAuthUrl\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Scopes</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"Enter as a comma or space seperated list of claims/permissions\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <lib-chips-input\n (keyup)=\"scopesOnKeyup($event)\"\n [(ngModel)]=\"formData.scopes\"\n [allowDuplicate]=\"false\"\n placeholder=\"Enter scopes\"\n [addOnBlur]=\"true\"\n separator=\",\"\n [disabled]=\"useStrictReadonlyForm\"\n ></lib-chips-input>\n </div>\n </lib-form-field-container>\n @if (showAutoEnrollment) {\n <lib-form-field-container\n [title]=\"'Auto Enrollment'\"\n [helpText]=\"'Click for more information'\"\n (helpClicked)=\"showAutoEnrollHelp()\"\n >\n <div class=\"form-field-row\">\n <div class=\"config-item\">\n <div class=\"config-container toggle-container\">\n <div class=\"config-container-label\">Auto-Enroll to Cert</div>\n <div\n (click)=\"toggleEnrollToCertEnabled()\"\n [ngClass]=\"{ on: formData.enrollToCertEnabled, 'toggle-readonly': useStrictReadonlyForm }\"\n class=\"toggle\"\n >\n <span [hidden]=\"!formData.enrollToCertEnabled\" class=\"on-label\">YES</span>\n <span [hidden]=\"formData.enrollToCertEnabled\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n <div class=\"config-item\">\n <div class=\"config-container toggle-container\">\n <div class=\"config-container-label\">Auto-Enroll to Token</div>\n <div\n (click)=\"toggleEnrollToTokenEnabled()\"\n [ngClass]=\"{ on: formData.enrollToTokenEnabled, 'toggle-readonly': useStrictReadonlyForm }\"\n class=\"toggle\"\n >\n <span [hidden]=\"!formData.enrollToTokenEnabled\" class=\"on-label\">YES</span>\n <span [hidden]=\"formData.enrollToTokenEnabled\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n </div>\n </lib-form-field-container>\n }\n <lib-form-field-toggle [(toggleOn)]=\"showMore\" (toggleOnChange)=\"showMoreChanged($event)\" style=\"margin: 0px 10px\"></lib-form-field-toggle>\n @if (showMore && showTokenType) {\n <lib-form-field-container\n [title]=\"'Target Token Type'\"\n [label]=\"'Required'\"\n [helpText]=\"'The target token type. Can be either Access or ID'\"\n class=\"form-field-advanced\"\n >\n <select\n [(ngModel)]=\"targetToken\"\n id=\"authPolicyId\"\n class=\"form-field-dropdown\"\n [disabled]=\"useStrictReadonlyForm\"\n >\n <option value=\"ACCESS\">Access</option>\n <option value=\"ID\">ID</option>\n </select>\n </lib-form-field-container>\n }\n <div [hidden]=\"!showMore\" class=\"form-group-column\">\n @if (showAutoEnrollment) {\n <lib-form-field-container\n [title]=\"'Auto Enrollment Options'\"\n class=\"form-field-advanced\"\n >\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Enrollment Auth Policy</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The authentication policy assigned to identities created through auto-enrollment. Defaults to the "default" policy.\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <select\n [(ngModel)]=\"enrollAuthPolicyId\"\n class=\"form-field-dropdown\"\n [disabled]=\"useStrictReadonlyForm\"\n >\n <option value=\"\">Select an Authentication Policy</option>\n @for (policy of authPolicies; track policy.id) {\n <option [value]=\"policy.id\">{{ policy.name }}</option>\n }\n </select>\n </div>\n <div class=\"form-field-row\">\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Name Claims Selector</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The claims JSON pointer selector or top level property used for the name of enrolling identities. Defaults to /sub\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter name claims selector\" [(ngModel)]=\"formData.enrollNameClaimsSelector\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Attribute Claims Selector</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The claims JSON pointer selector or top level property used for the attributes of enrolling identities.\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter attribute claims selector\" [(ngModel)]=\"formData.enrollAttributeClaimsSelector\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n </div>\n </lib-form-field-container>\n }\n <lib-form-field-container\n [title]=\"'Verify OIDC Authentication'\"\n class=\"form-field-advanced\"\n [ngClass]=\"{'formFieldError': !oidcVerified && oidcErrorMessageDetail, 'formFieldSuccess': oidcVerified}\"\n >\n <div class=\"form-field-row\" #oidcVerification>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <div class=\"form-field-title\">Callback URL</div>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"This URL must be added to the list of allowed callback URLs in your OAuth provider configuration\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <div class=\"copy-link-container\">\n <pre class=\"read-only-2\" (click)=\"copyCallbackURL()\">{{ callbackURL }}</pre>\n <div class=\"copy-button\" (click)=\"copyCallbackURL()\"></div>\n </div>\n </div>\n @if (!useStrictReadonlyForm) {\n <div class=\"test-oauth-button\"\n [ngClass]=\"{'oidc-verified': oidcVerified}\"\n (click)=\"testOIDCAuthentication()\"\n >\n {{oidcVerified ? 'Verified' : 'Authenticate' }}\n @if (oauthLoading) {\n <div class=\"spinner\"></div>\n }\n @if (oidcVerified) {\n <input type=\"checkbox\" [disabled]=\"true\" [checked]=\"true\" class=\"success-check\">\n }\n </div>\n }\n </div>\n @if (oidcErrorMessageDetail) {\n <div class=\"oidc-error-message-container\">\n <code class=\"oidc-error-message-source\">{{oidcErrorMessageSource}}: </code>\n <code class=\"oidc-error-message-detail\">{{oidcErrorMessageDetail}}</code>\n @if (oidcErrorMessageDetail2) {\n <code class=\"oidc-error-message-detail2\">{{oidcErrorMessageDetail2}}</code>\n }\n @if (oidcErrorMessageDetail3) {\n <code class=\"oidc-error-message-detail2\">{{oidcErrorMessageDetail3}}</code>\n }\n </div>\n }\n @if (oidcAuthTokenClaims) {\n <div class=\"oidc-token-claims-container\">\n <code class=\"oidc-token-claims-title\">OAuth Token Claims: </code>\n <lib-json-view [showLineNumbers]=\"false\" [(data)]=\"oidcAuthTokenClaims\" [readOnly]=\"useStrictReadonlyForm\"></lib-json-view>\n </div>\n }\n </lib-form-field-container>\n <lib-form-field-container\n [title]=\"'Custom Tags'\"\n [label]=\"'OPTIONAL'\"\n class=\"form-field-advanced\"\n >\n <lib-custom-tags [(tags)]=\"formData.tags\" [readOnly]=\"useStrictReadonlyForm\"></lib-custom-tags>\n </lib-form-field-container>\n </div>\n </div>\n <div class=\"form-group-column two-fifths\">\n <lib-form-field-container\n [title]=\"'Verification'\"\n [label]=\"useStrictReadonlyForm ? '' : 'REQUIRED'\"\n >\n <div\n class=\"radio-group-container\"\n [class.radio-group-readonly]=\"useStrictReadonlyForm\"\n (keydown)=\"radioKeyDownHandler($event)\"\n [attr.tabindex]=\"useStrictReadonlyForm ? -1 : 0\"\n >\n <div\n class=\"radio-button-container\"\n [ngClass]=\"{'selected': signatureMethod === 'JWKS_ENDPOINT', 'toggle-readonly': useStrictReadonlyForm}\"\n (click)=\"!useStrictReadonlyForm && selectSignatureMethod('JWKS_ENDPOINT')\"\n >\n <div class=\"radio-button-circle\">\n <div class=\"radio-button-inner-circle\"></div>\n </div>\n <span class=\"radio-button-label\">JWKS Endpoint</span>\n </div>\n <div\n class=\"radio-button-container\"\n [ngClass]=\"{'selected': signatureMethod === 'CERT_PEM', 'toggle-readonly': useStrictReadonlyForm}\"\n (click)=\"!useStrictReadonlyForm && selectSignatureMethod('CERT_PEM')\"\n >\n <div class=\"radio-button-circle\">\n <div class=\"radio-button-inner-circle\"></div>\n </div>\n <span class=\"radio-button-label\">Cert PEM</span>\n </div>\n </div>\n @if (signatureMethod === 'JWKS_ENDPOINT') {\n <div class=\"form-field-input-group\">\n <div class=\"form-field-label-container select-file-label-container\">\n <div class=\"form-field-header\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">JWKS Endpoint</span>\n </div>\n </div>\n <input\n class=\"form-field-input\"\n placeholder=\"Enter endpoint URL\"\n [(ngModel)]=\"formData.jwksEndpoint\"\n [ngClass]=\"{error: errors['jwksEndpoint']}\"\n [disabled]=\"useStrictReadonlyForm\"\n />\n </div>\n </div>\n }\n @if (signatureMethod === 'CERT_PEM') {\n <div class=\"form-field-input-group\">\n <div class=\"form-field-label-container select-file-label-container\">\n <div class=\"form-field-header\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">PEM</span>\n </div>\n <span\n class=\"select-file-button\"\n [class.toggle-readonly]=\"useStrictReadonlyForm\"\n (click)=\"!useStrictReadonlyForm && openFileSelect($event)\"\n >\n Select File\n @if (fileSelectOpening) {\n <div class=\"spinner\"></div>\n }\n </span>\n <input\n #fileSelect\n type=\"file\"\n style=\"display:none\"\n [disabled]=\"useStrictReadonlyForm\"\n (change)=\"selectPemFile($event)\"\n >\n </div>\n <textarea\n placeholder=\"Paste pem contents or select file\"\n [(ngModel)]=\"formData.certPem\"\n [ngClass]=\"{error: errors['certPem']}\"\n [disabled]=\"useStrictReadonlyForm\"\n ></textarea>\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-header\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">KID</span>\n </div>\n <span class=\"form-field-label\" >Optional</span>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter KID\" [(ngModel)]=\"formData.kid\" [disabled]=\"useStrictReadonlyForm\"/>\n </div>\n </div>\n }\n </lib-form-field-container>\n <lib-form-field-container\n [showHeader]=\"false\"\n >\n <div class=\"config-item\">\n <div class=\"config-container toggle-container\">\n <div class=\"config-container-label\">ENABLED</div>\n <div\n (click)=\"!useStrictReadonlyForm && toggleEnabled()\"\n [ngClass]=\"{ on: formData.enabled, 'toggle-readonly': useStrictReadonlyForm }\"\n class=\"toggle\"\n >\n <span [hidden]=\"!formData.enabled\" class=\"on-label\">YES</span>\n <span [hidden]=\"formData.enabled\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n </lib-form-field-container>\n @if (showApiCalls) {\n <lib-form-field-container\n [title]=\"'API Calls'\"\n [headerActions]=\"apiOptions\"\n (actionRequested)=\"apiActionRequested($event)\"\n [class]=\"'api-data-no-wrap'\"\n >\n <div class=\"form-row\">\n <input class=\"form-field-input\" [value]=\"apiCallURL\"/>\n <div class=\"icon-copy copy-text\" (click)=\"copyToClipboard(apiCallURL)\"></div>\n </div>\n @if (formData) {\n <lib-json-view [showLineNumbers]=\"false\" [(data)]=\"apiData\" [readOnly]=\"true\" [showCopy]=\"true\"></lib-json-view>\n }\n </lib-form-field-container>\n }\n </div>\n </div>\n @if (formView === 'raw') {\n <div class=\"form-group-column\">\n @if (formData) {\n <lib-json-view [(data)]=\"formData\" [readOnly]=\"useStrictReadonlyForm\"></lib-json-view>\n }\n </div>\n }\n </div>\n</div>\n@if (isLoading) {\n <lib-loading-indicator [isLoading]=\"isLoading\"></lib-loading-indicator>\n}\n<lib-side-modal [(open)]=\"showAutoEnrollHelpModal\" [showClose]=\"true\">\n <div class=\"help-panel-content\">\n <h2 class=\"help-panel-title\">What is Auto Enrollment?</h2>\n <p class=\"help-panel-intro\">\n Auto Enrollment enables automatic identity creation when users authenticate via JWT tokens.\n </p>\n\n <div class=\"help-panel-section\">\n <h3 class=\"help-panel-subtitle\">\n <span class=\"icon-certificate\"></span>\n Auto-Enroll to Cert\n </h3>\n <p class=\"help-panel-text\">\n When enabled, the controller will automatically create an identity and enroll it with a certificate when a user authenticates using a JWT token. This is useful for certificate-based authentication workflows.\n </p>\n </div>\n\n <div class=\"help-panel-section\">\n <h3 class=\"help-panel-subtitle\">\n <span class=\"icon-key\"></span>\n Auto-Enroll to Token\n </h3>\n <p class=\"help-panel-text\">\n When enabled, the controller will automatically create an identity and enroll it with an authentication token when a user authenticates using a JWT token. This is useful for token-based authentication workflows.\n </p>\n </div>\n\n <div class=\"help-panel-section\">\n <h3 class=\"help-panel-subtitle\">\n <span class=\"icon-lock\"></span>\n Enrollment Auth Policy\n </h3>\n <p class=\"help-panel-text\">\n The authentication policy assigned to identities that are created through auto-enrollment. When left unset, the controller's \"default\" authentication policy is used.\n </p>\n </div>\n\n <div class=\"help-panel-section\">\n <h3 class=\"help-panel-subtitle\">\n <span class=\"icon-identity\"></span>\n Name Claims Selector\n </h3>\n <p class=\"help-panel-text\">\n A JSON pointer selector (or top-level claim property) identifying which JWT claim supplies the name for enrolling identities. Defaults to <code>/sub</code> when unset.\n </p>\n </div>\n\n <div class=\"help-panel-section\">\n <h3 class=\"help-panel-subtitle\">\n <span class=\"icon-identity\"></span>\n Attribute Claims Selector\n </h3>\n <p class=\"help-panel-text\">\n A JSON pointer selector (or top-level claim property) identifying which JWT claim supplies the role attributes for enrolling identities.\n </p>\n </div>\n\n <div class=\"help-panel-note\">\n <strong>Note:</strong> These options are only available in Ziti 2.0 and later versions.\n </div>\n </div>\n</lib-side-modal>\n", styles: ["@charset \"UTF-8\";.jwt-signer-form-container{display:flex;flex-direction:column;height:100%;width:100%;align-items:center;gap:var(--gapXL)}.jwt-signer-form-container .select-file-button{padding-left:var(--paddingMedium);padding-right:var(--paddingMedium);height:1.5625rem;width:fit-content;white-space:nowrap;display:flex;align-items:center;justify-content:center;font-weight:600;font-size:.875rem;background:var(--primary);border-style:solid;border-width:.0625rem;border-color:var(--stroke);color:var(--white);cursor:pointer;border-radius:var(--inputBorderRadius);gap:var(--marginMedium);margin-top:-.3125rem}.jwt-signer-form-container .select-file-button:hover{filter:brightness(.8)}.jwt-signer-form-container .select-file-button:active{filter:grayscale(1);transform:translateY(.0625rem)}.jwt-signer-form-container .select-file-button .spinner{display:inline-block;width:.5rem;height:.5rem;border-radius:50%;background:transparent;border-top:.125rem solid white;border-right:.125rem solid white;border-bottom:.125rem solid transparent;border-left:.125rem solid transparent;-webkit-animation:loading .5s infinite linear;animation:loading .5s infinite linear}.jwt-signer-form-container .select-file-label-container{gap:var(--marginMedium)}::ng-deep .jwt-signer-form-container select:disabled,::ng-deep .jwt-signer-form-container input:disabled{opacity:.75!important;cursor:text;background-color:var(--stroke)!important}::ng-deep .api-data-no-wrap .jse-text-mode{width:100%}::ng-deep .api-data-no-wrap .jse-text-mode .cm-editor{width:100%}::ng-deep .api-data-no-wrap .jse-text-mode .cm-editor .cm-scroller{width:100%}::ng-deep .api-data-no-wrap .jse-text-mode .cm-editor .cm-scroller .cm-content{width:100%;overflow:auto}::ng-deep .api-data-no-wrap .jse-text-mode .cm-editor .cm-scroller .cm-content .\\37cr{white-space:nowrap}.radio-group-container{display:flex;gap:var(--marginLarge)}.radio-group-container:focus .radio-button-container .radio-button-circle{border-color:var(--primaryColor)}.radio-group-container .radio-button-container{display:flex;flex-direction:row;align-items:center;background-color:var(--formGroup);border-radius:var(--inputBorderRadius);width:100%;padding-left:var(--paddingMedium);padding-right:var(--paddingMedium);padding-top:var(--paddingLarge);padding-bottom:var(--paddingLarge);cursor:pointer;gap:var(--marginMedium)}.radio-group-container .radio-button-container .radio-button-circle{display:flex;width:1.3125rem;height:1.3125rem;border-radius:1.3125rem;border-width:.125rem;border-style:solid;border-color:var(--offWhite_ALWAYS);align-items:center;justify-content:center;background-color:var(--offWhite)}.radio-group-container .radio-button-container .radio-button-circle .radio-button-inner-circle{display:none;width:.9375rem;height:.9375rem;border-radius:.9375rem;align-items:center;justify-content:center;background-color:var(--primaryColor)}.radio-group-container .radio-button-container.selected .radio-button-circle .radio-button-inner-circle{display:flex}.radio-group-container .radio-button-container:active .radio-button-circle .radio-button-inner-circle{display:flex;background-color:var(--menu)}.radio-group-container .radio-button-container .radio-button-label{color:var(--offWhite_ALWAYS);font-size:.875rem;font-weight:600}.radio-button-container.toggle-readonly{cursor:default;pointer-events:none;opacity:.92}.select-file-button.toggle-readonly{cursor:default;pointer-events:none;opacity:.85}.test-oauth-button{color:var(--offWhite_ALWAYS);height:var(--defaultInputHeight);border-style:solid;border-color:var(--inputBorder);border-radius:var(--inputBorderRadius);vertical-align:middle;text-align:center;position:relative;display:flex;align-items:center;justify-content:center;overflow:hidden;transition:color .2s;font-size:.9375rem;font-weight:600;font-family:Open Sans,sans-serif;text-transform:uppercase;cursor:pointer;width:100%;background-color:var(--primaryColor);margin-top:1.5rem;max-width:15rem}.test-oauth-button.oidc-verified{filter:brightness(.95);color:var(--offWhite_ALWAYS);background-color:var(--green)}.test-oauth-button .spinner{display:inline-block;width:var(--spinnerSizeMedium);height:var(--spinnerSizeMedium);border-radius:50%;background:transparent;border-top:var(--spinnerBorderTopMedium);border-right:var(--spinnerBorderRightMedium);border-bottom:var(--spinnerBorderBottomMedium);border-left:var(--spinnerBorderLeftMedium);animation:loading .5s infinite linear;margin-left:var(--marginSmall)}.test-oauth-button:hover{filter:brightness(.95)}.test-oauth-button:active{transform:translateY(1px)}.success-check{width:1.0625rem;height:1.0625rem;cursor:pointer;background-color:var(--navigation);margin:0;margin-top:.25rem;border-radius:2rem;margin-left:var(--marginSmall)}.success-check:checked{background-color:var(--green)!important;background-image:url(/assets/images/check.png);background-size:.675rem;background-position:center center;background-repeat:no-repeat;border:none}.success-check:checked:disabled{opacity:1!important}.oidc-token-claims-container{background-color:var(--backgroundMuted);height:fit-content;display:flex;flex-direction:column;align-items:flex-start;justify-content:flex-start;padding:var(--paddingMedium);border-radius:var(--inputBorderRadius);color:var(--tableText);font-weight:600;overflow:auto}.oidc-token-claims-container .oidc-token-claims-title{font-weight:600;font-size:1rem;white-space:nowrap;margin-bottom:var(--marginMedium)}.oidc-error-message-container{background-color:var(--lightRed);height:fit-content;display:flex;flex-direction:column;align-items:flex-start;justify-content:flex-start;padding:var(--paddingMedium);border-radius:var(--inputBorderRadius);color:var(--red);font-weight:600;overflow:auto}.oidc-error-message-container .oidc-error-message-source{font-weight:600;font-size:1rem;white-space:nowrap}.oidc-error-message-container .oidc-error-message-detail2,.oidc-error-message-container .oidc-error-message-detail{margin-top:var(--marginSmall);font-weight:400;white-space:nowrap}.oidc-error-message-container::-webkit-scrollbar{height:6px;width:6px}.oidc-error-message-container::-webkit-scrollbar-thumb{background-color:var(--menu);border-radius:10px}.oidc-error-message-container:hover::-webkit-scrollbar-thumb{background-clip:unset!important}.read-only-2{display:flex;overflow:auto;white-space:nowrap;opacity:1!important;cursor:pointer;padding:var(--paddingMedium);padding-bottom:var(--paddingSmall)}.read-only-2::-webkit-scrollbar{height:6px;width:6px}.read-only-2::-webkit-scrollbar-thumb{background-color:var(--menu);border-radius:10px}.read-only-2:hover{text-decoration:underline}.read-only-2:hover::-webkit-scrollbar-thumb{background-clip:unset}.copy-link-container{display:flex;flex-direction:row;justify-content:space-between;align-items:center;opacity:1!important;cursor:text;border-radius:var(--inputBorderRadius);height:var(--defaultInputHeight);background-color:var(--backgroundMuted)!important}.copy-button{display:flex;align-items:center;justify-content:center;height:100%;width:2.575rem;margin-left:.125rem;background:url(/assets/svgs/copy.svg) no-repeat;background-size:1.125rem 1.125rem;background-position:center;cursor:pointer;border-top-right-radius:var(--inputBorderRadius);border-bottom-right-radius:var(--inputBorderRadius);background-color:#0000000f}.copy-button:hover{filter:brightness(.95);background-color:#0000001f}.help-panel-content{display:flex;flex-direction:column;padding-top:3rem}.help-panel-content .help-panel-title{margin:0 0 .25rem;color:var(--primary);font-size:1.5rem;font-weight:600;line-height:1.3;padding-right:2rem}.help-panel-content .help-panel-intro{line-height:1.6;margin-bottom:1.5rem;color:var(--tableText);font-size:.9375rem}.help-panel-content .help-panel-section{margin-bottom:1.5rem;padding:1rem;background-color:var(--navigation);border-radius:var(--inputBorderRadius);border-left:.25rem solid var(--primary)}.help-panel-content .help-panel-section .help-panel-subtitle{margin:0 0 .75rem;color:var(--primary);font-size:1.125rem;font-weight:600;display:flex;align-items:center;gap:.5rem}.help-panel-content .help-panel-section .help-panel-subtitle span[class^=icon-]{font-size:1rem;opacity:.8}.help-panel-content .help-panel-section .help-panel-text{margin:0;line-height:1.6;color:var(--tableText);font-size:.875rem}.help-panel-content .help-panel-note{padding:1rem;background-color:#ffc1071a;border-radius:var(--inputBorderRadius);border-left:.25rem solid #ffc107;color:var(--tableText);font-size:.875rem;line-height:1.6}.help-panel-content .help-panel-note strong{color:#ffc107}\n"] }]
|
|
27099
29494
|
}], ctorParameters: () => [{ type: JwtSignerFormService }, { type: SchemaService }, { type: GrowlerService }, { type: undefined, decorators: [{
|
|
27100
29495
|
type: Inject,
|
|
27101
29496
|
args: [SHAREDZ_EXTENSION]
|
|
@@ -31923,11 +34318,13 @@ class SettingsComponent {
|
|
|
31923
34318
|
this.hideTags = false;
|
|
31924
34319
|
this.transitions = true;
|
|
31925
34320
|
this.isFilterAfterSave = false;
|
|
34321
|
+
this.betaFeatures = false;
|
|
31926
34322
|
}
|
|
31927
34323
|
ngOnInit() {
|
|
31928
34324
|
this.isDark = localStorage.getItem('mode') === 'dark';
|
|
31929
34325
|
this.hideTags = localStorage.getItem('hideTags') === 'yes';
|
|
31930
34326
|
this.isFilterAfterSave = localStorage.getItem('filterAfterSave') === 'on';
|
|
34327
|
+
this.betaFeatures = localStorage.getItem('betaFeatures') === 'on';
|
|
31931
34328
|
const transitions = localStorage.getItem('Transitions');
|
|
31932
34329
|
this.transitions = transitions !== 'off';
|
|
31933
34330
|
this.updateTransitions();
|
|
@@ -31945,6 +34342,10 @@ class SettingsComponent {
|
|
|
31945
34342
|
const mode = this.isFilterAfterSave ? 'on' : 'off';
|
|
31946
34343
|
localStorage.setItem('filterAfterSave', mode);
|
|
31947
34344
|
}
|
|
34345
|
+
betaFeaturesChanged() {
|
|
34346
|
+
const mode = this.betaFeatures ? 'on' : 'off';
|
|
34347
|
+
localStorage.setItem('betaFeatures', mode);
|
|
34348
|
+
}
|
|
31948
34349
|
transitionsChange() {
|
|
31949
34350
|
const mode = this.transitions ? 'on' : 'off';
|
|
31950
34351
|
localStorage.setItem('Transitions', mode);
|
|
@@ -31967,11 +34368,11 @@ class SettingsComponent {
|
|
|
31967
34368
|
document.getElementsByTagName('html')[0].style.setProperty('--secondary', color);
|
|
31968
34369
|
}
|
|
31969
34370
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: SettingsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
31970
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.17", type: SettingsComponent, isStandalone: false, selector: "lib-settings", ngImport: i0, template: "<div class=\"projectable-form-wrapper\">\n <div class=\"header\">\n <div class=\"title\">Manage <span>{{pageTitle}}</span></div>\n <div class=\"line\"></div>\n <div class=\"tabs\">\n <div class=\"tab selected\">Manage <span>{{pageTitle}}</span></div>\n <div class=\"tab\" routerLink=\"/profile\">Manage Profile</div>\n </div>\n <div class=\"constrained\">\n <div class=\"grid split\">\n <div>\n <label>Filter after Save</label>\n <div class=\"toggle\" [class.on]=\"isFilterAfterSave\" (click)=\"isFilterAfterSave = !isFilterAfterSave; filterAfter()\">\n <div class=\"switch\"></div>\n <div class=\"label\"></div>\n </div>\n </div>\n <div>\n <label>Dark mode</label>\n <div class=\"toggle\" [class.on]=\"isDark\" (click)=\"isDark = !isDark; swap()\">\n <div class=\"switch\"></div>\n <div class=\"label\"></div>\n </div>\n </div>\n <div>\n <label>Hide Tags</label>\n <div class=\"toggle\" [class.on]=\"hideTags\" (click)=\"hideTags = !hideTags; hide()\">\n <div class=\"switch\"></div>\n <div class=\"label\"></div>\n </div>\n </div>\n <div>\n <label>Transitions</label>\n <div class=\"toggle\" [class.on]=\"transitions\" (click)=\"transitions = !transitions; transitionsChange()\">\n <div class=\"switch\"></div>\n <div class=\"label\"></div>\n </div>\n </div>\n </div>\n </div>\n </div>\n</div>\n", styles: [".header .constrained .grid.split{grid-template-columns:1fr 1fr}label{color:var(--dark);text-transform:uppercase;margin-top:var(--marginMedium);position:relative;display:inline-block;width:100%;margin-bottom:.1875rem;font-size:.875rem;white-space:nowrap;font-weight:600;font-family:Open Sans;overflow:hidden;text-overflow:ellipsis;line-height:var(--defaultLineHeight)}.toggle{width:3.75rem;height:1.875rem;background-color:var(--black);color:var(--white);cursor:pointer;border-radius:6.25rem;position:relative;display:inline-block;margin-top:var(--marginMedium);transition:var(--transition);float:left;margin-right:var(--marginXL)}.toggle.on{background-color:var(--green)}.toggle.on .switch{left:2.1875rem}.toggle.on .label{left:.625rem;content:\"YES\"}.toggle.on .label:before{content:\"YES\"}.toggle .switch{width:1.25rem;height:1.25rem;border-radius:.625rem;background-color:var(--white);transition:var(--transition);position:absolute;left:.3125rem;top:.3125rem;z-index:10}.toggle .label{right:.625rem;line-height:1.875rem;font-size:.75rem;color:var(--white);position:absolute;z-index:8;transition:var(--transition);text-transform:uppercase}.toggleLabel{line-height:3.125rem}.toggle .label:before{content:\"NO\"}.constrained{width:37.5rem;position:relative;margin-left:auto;margin-right:auto}.split{display:grid;grid-gap:.625rem;grid-template-columns:auto 50%}.header{position:relative;display:inline-block;width:100%}.line{position:absolute;width:100%;top:2.5rem;height:.25rem;background-color:var(--stroke)}.tabs{position:relative;display:inline-block;width:100%;font-size:.875rem;min-height:2.5rem;padding-top:.9375rem}.tabs .tab{position:relative;float:left;margin-right:1.25rem;padding-bottom:.3125rem;margin-bottom:.625rem;font-size:.875rem;font-weight:600;cursor:pointer;transition:var(--transition)}.tabs .tab.selected{border-bottom-width:.25rem;border-bottom-color:var(--primary);border-bottom-style:solid}\n"], dependencies: [{ kind: "directive", type: i4.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }] }); }
|
|
34371
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.17", type: SettingsComponent, isStandalone: false, selector: "lib-settings", ngImport: i0, template: "<div class=\"projectable-form-wrapper\">\n <div class=\"header\">\n <div class=\"title\">Manage <span>{{pageTitle}}</span></div>\n <div class=\"line\"></div>\n <div class=\"tabs\">\n <div class=\"tab selected\">Manage <span>{{pageTitle}}</span></div>\n <div class=\"tab\" routerLink=\"/profile\">Manage Profile</div>\n </div>\n <div class=\"constrained\">\n <div class=\"grid split\">\n <div>\n <label>Filter after Save</label>\n <div class=\"toggle\" [class.on]=\"isFilterAfterSave\" (click)=\"isFilterAfterSave = !isFilterAfterSave; filterAfter()\">\n <div class=\"switch\"></div>\n <div class=\"label\"></div>\n </div>\n </div>\n <div>\n <label>Dark mode</label>\n <div class=\"toggle\" [class.on]=\"isDark\" (click)=\"isDark = !isDark; swap()\">\n <div class=\"switch\"></div>\n <div class=\"label\"></div>\n </div>\n </div>\n <div>\n <label>Hide Tags</label>\n <div class=\"toggle\" [class.on]=\"hideTags\" (click)=\"hideTags = !hideTags; hide()\">\n <div class=\"switch\"></div>\n <div class=\"label\"></div>\n </div>\n </div>\n <div>\n <label>Transitions</label>\n <div class=\"toggle\" [class.on]=\"transitions\" (click)=\"transitions = !transitions; transitionsChange()\">\n <div class=\"switch\"></div>\n <div class=\"label\"></div>\n </div>\n </div>\n <div>\n <label>Beta Features</label>\n <div class=\"toggle\" [class.on]=\"betaFeatures\" (click)=\"betaFeatures = !betaFeatures; betaFeaturesChanged()\">\n <div class=\"switch\"></div>\n <div class=\"label\"></div>\n </div>\n </div>\n </div>\n </div>\n </div>\n</div>\n", styles: [".header .constrained .grid.split{grid-template-columns:1fr 1fr}label{color:var(--dark);text-transform:uppercase;margin-top:var(--marginMedium);position:relative;display:inline-block;width:100%;margin-bottom:.1875rem;font-size:.875rem;white-space:nowrap;font-weight:600;font-family:Open Sans;overflow:hidden;text-overflow:ellipsis;line-height:var(--defaultLineHeight)}.toggle{width:3.75rem;height:1.875rem;background-color:var(--black);color:var(--white);cursor:pointer;border-radius:6.25rem;position:relative;display:inline-block;margin-top:var(--marginMedium);transition:var(--transition);float:left;margin-right:var(--marginXL)}.toggle.on{background-color:var(--green)}.toggle.on .switch{left:2.1875rem}.toggle.on .label{left:.625rem;content:\"YES\"}.toggle.on .label:before{content:\"YES\"}.toggle .switch{width:1.25rem;height:1.25rem;border-radius:.625rem;background-color:var(--white);transition:var(--transition);position:absolute;left:.3125rem;top:.3125rem;z-index:10}.toggle .label{right:.625rem;line-height:1.875rem;font-size:.75rem;color:var(--white);position:absolute;z-index:8;transition:var(--transition);text-transform:uppercase}.toggleLabel{line-height:3.125rem}.toggle .label:before{content:\"NO\"}.constrained{width:37.5rem;position:relative;margin-left:auto;margin-right:auto}.split{display:grid;grid-gap:.625rem;grid-template-columns:auto 50%}.header{position:relative;display:inline-block;width:100%}.line{position:absolute;width:100%;top:2.5rem;height:.25rem;background-color:var(--stroke)}.tabs{position:relative;display:inline-block;width:100%;font-size:.875rem;min-height:2.5rem;padding-top:.9375rem}.tabs .tab{position:relative;float:left;margin-right:1.25rem;padding-bottom:.3125rem;margin-bottom:.625rem;font-size:.875rem;font-weight:600;cursor:pointer;transition:var(--transition)}.tabs .tab.selected{border-bottom-width:.25rem;border-bottom-color:var(--primary);border-bottom-style:solid}\n"], dependencies: [{ kind: "directive", type: i4.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }] }); }
|
|
31971
34372
|
}
|
|
31972
34373
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: SettingsComponent, decorators: [{
|
|
31973
34374
|
type: Component,
|
|
31974
|
-
args: [{ selector: 'lib-settings', standalone: false, template: "<div class=\"projectable-form-wrapper\">\n <div class=\"header\">\n <div class=\"title\">Manage <span>{{pageTitle}}</span></div>\n <div class=\"line\"></div>\n <div class=\"tabs\">\n <div class=\"tab selected\">Manage <span>{{pageTitle}}</span></div>\n <div class=\"tab\" routerLink=\"/profile\">Manage Profile</div>\n </div>\n <div class=\"constrained\">\n <div class=\"grid split\">\n <div>\n <label>Filter after Save</label>\n <div class=\"toggle\" [class.on]=\"isFilterAfterSave\" (click)=\"isFilterAfterSave = !isFilterAfterSave; filterAfter()\">\n <div class=\"switch\"></div>\n <div class=\"label\"></div>\n </div>\n </div>\n <div>\n <label>Dark mode</label>\n <div class=\"toggle\" [class.on]=\"isDark\" (click)=\"isDark = !isDark; swap()\">\n <div class=\"switch\"></div>\n <div class=\"label\"></div>\n </div>\n </div>\n <div>\n <label>Hide Tags</label>\n <div class=\"toggle\" [class.on]=\"hideTags\" (click)=\"hideTags = !hideTags; hide()\">\n <div class=\"switch\"></div>\n <div class=\"label\"></div>\n </div>\n </div>\n <div>\n <label>Transitions</label>\n <div class=\"toggle\" [class.on]=\"transitions\" (click)=\"transitions = !transitions; transitionsChange()\">\n <div class=\"switch\"></div>\n <div class=\"label\"></div>\n </div>\n </div>\n </div>\n </div>\n </div>\n</div>\n", styles: [".header .constrained .grid.split{grid-template-columns:1fr 1fr}label{color:var(--dark);text-transform:uppercase;margin-top:var(--marginMedium);position:relative;display:inline-block;width:100%;margin-bottom:.1875rem;font-size:.875rem;white-space:nowrap;font-weight:600;font-family:Open Sans;overflow:hidden;text-overflow:ellipsis;line-height:var(--defaultLineHeight)}.toggle{width:3.75rem;height:1.875rem;background-color:var(--black);color:var(--white);cursor:pointer;border-radius:6.25rem;position:relative;display:inline-block;margin-top:var(--marginMedium);transition:var(--transition);float:left;margin-right:var(--marginXL)}.toggle.on{background-color:var(--green)}.toggle.on .switch{left:2.1875rem}.toggle.on .label{left:.625rem;content:\"YES\"}.toggle.on .label:before{content:\"YES\"}.toggle .switch{width:1.25rem;height:1.25rem;border-radius:.625rem;background-color:var(--white);transition:var(--transition);position:absolute;left:.3125rem;top:.3125rem;z-index:10}.toggle .label{right:.625rem;line-height:1.875rem;font-size:.75rem;color:var(--white);position:absolute;z-index:8;transition:var(--transition);text-transform:uppercase}.toggleLabel{line-height:3.125rem}.toggle .label:before{content:\"NO\"}.constrained{width:37.5rem;position:relative;margin-left:auto;margin-right:auto}.split{display:grid;grid-gap:.625rem;grid-template-columns:auto 50%}.header{position:relative;display:inline-block;width:100%}.line{position:absolute;width:100%;top:2.5rem;height:.25rem;background-color:var(--stroke)}.tabs{position:relative;display:inline-block;width:100%;font-size:.875rem;min-height:2.5rem;padding-top:.9375rem}.tabs .tab{position:relative;float:left;margin-right:1.25rem;padding-bottom:.3125rem;margin-bottom:.625rem;font-size:.875rem;font-weight:600;cursor:pointer;transition:var(--transition)}.tabs .tab.selected{border-bottom-width:.25rem;border-bottom-color:var(--primary);border-bottom-style:solid}\n"] }]
|
|
34375
|
+
args: [{ selector: 'lib-settings', standalone: false, template: "<div class=\"projectable-form-wrapper\">\n <div class=\"header\">\n <div class=\"title\">Manage <span>{{pageTitle}}</span></div>\n <div class=\"line\"></div>\n <div class=\"tabs\">\n <div class=\"tab selected\">Manage <span>{{pageTitle}}</span></div>\n <div class=\"tab\" routerLink=\"/profile\">Manage Profile</div>\n </div>\n <div class=\"constrained\">\n <div class=\"grid split\">\n <div>\n <label>Filter after Save</label>\n <div class=\"toggle\" [class.on]=\"isFilterAfterSave\" (click)=\"isFilterAfterSave = !isFilterAfterSave; filterAfter()\">\n <div class=\"switch\"></div>\n <div class=\"label\"></div>\n </div>\n </div>\n <div>\n <label>Dark mode</label>\n <div class=\"toggle\" [class.on]=\"isDark\" (click)=\"isDark = !isDark; swap()\">\n <div class=\"switch\"></div>\n <div class=\"label\"></div>\n </div>\n </div>\n <div>\n <label>Hide Tags</label>\n <div class=\"toggle\" [class.on]=\"hideTags\" (click)=\"hideTags = !hideTags; hide()\">\n <div class=\"switch\"></div>\n <div class=\"label\"></div>\n </div>\n </div>\n <div>\n <label>Transitions</label>\n <div class=\"toggle\" [class.on]=\"transitions\" (click)=\"transitions = !transitions; transitionsChange()\">\n <div class=\"switch\"></div>\n <div class=\"label\"></div>\n </div>\n </div>\n <div>\n <label>Beta Features</label>\n <div class=\"toggle\" [class.on]=\"betaFeatures\" (click)=\"betaFeatures = !betaFeatures; betaFeaturesChanged()\">\n <div class=\"switch\"></div>\n <div class=\"label\"></div>\n </div>\n </div>\n </div>\n </div>\n </div>\n</div>\n", styles: [".header .constrained .grid.split{grid-template-columns:1fr 1fr}label{color:var(--dark);text-transform:uppercase;margin-top:var(--marginMedium);position:relative;display:inline-block;width:100%;margin-bottom:.1875rem;font-size:.875rem;white-space:nowrap;font-weight:600;font-family:Open Sans;overflow:hidden;text-overflow:ellipsis;line-height:var(--defaultLineHeight)}.toggle{width:3.75rem;height:1.875rem;background-color:var(--black);color:var(--white);cursor:pointer;border-radius:6.25rem;position:relative;display:inline-block;margin-top:var(--marginMedium);transition:var(--transition);float:left;margin-right:var(--marginXL)}.toggle.on{background-color:var(--green)}.toggle.on .switch{left:2.1875rem}.toggle.on .label{left:.625rem;content:\"YES\"}.toggle.on .label:before{content:\"YES\"}.toggle .switch{width:1.25rem;height:1.25rem;border-radius:.625rem;background-color:var(--white);transition:var(--transition);position:absolute;left:.3125rem;top:.3125rem;z-index:10}.toggle .label{right:.625rem;line-height:1.875rem;font-size:.75rem;color:var(--white);position:absolute;z-index:8;transition:var(--transition);text-transform:uppercase}.toggleLabel{line-height:3.125rem}.toggle .label:before{content:\"NO\"}.constrained{width:37.5rem;position:relative;margin-left:auto;margin-right:auto}.split{display:grid;grid-gap:.625rem;grid-template-columns:auto 50%}.header{position:relative;display:inline-block;width:100%}.line{position:absolute;width:100%;top:2.5rem;height:.25rem;background-color:var(--stroke)}.tabs{position:relative;display:inline-block;width:100%;font-size:.875rem;min-height:2.5rem;padding-top:.9375rem}.tabs .tab{position:relative;float:left;margin-right:1.25rem;padding-bottom:.3125rem;margin-bottom:.625rem;font-size:.875rem;font-weight:600;cursor:pointer;transition:var(--transition)}.tabs .tab.selected{border-bottom-width:.25rem;border-bottom-color:var(--primary);border-bottom-style:solid}\n"] }]
|
|
31975
34376
|
}], ctorParameters: () => [] });
|
|
31976
34377
|
|
|
31977
34378
|
class ProfileComponent {
|
|
@@ -39542,12 +41943,24 @@ class OpenZitiConsoleLibModule {
|
|
|
39542
41943
|
ConfigTypesPageComponent,
|
|
39543
41944
|
ConfigTypeFormComponent,
|
|
39544
41945
|
ListPageHeaderComponent,
|
|
41946
|
+
ListTableHeaderComponent,
|
|
39545
41947
|
FilterBarComponent,
|
|
39546
41948
|
ListPageFormComponent,
|
|
39547
41949
|
IdentitiesPageComponent,
|
|
41950
|
+
IdentitiesListPageComponent,
|
|
39548
41951
|
EdgeRoutersPageComponent,
|
|
39549
41952
|
TransitRoutersPageComponent,
|
|
39550
41953
|
DataTableComponent,
|
|
41954
|
+
ListTableComponent,
|
|
41955
|
+
ListMenuComponent,
|
|
41956
|
+
NameCellComponent,
|
|
41957
|
+
SelectCellComponent,
|
|
41958
|
+
ListHeaderCellComponent,
|
|
41959
|
+
ListFilterCellComponent,
|
|
41960
|
+
OsInfoTooltipComponent,
|
|
41961
|
+
SdkInfoTooltipComponent,
|
|
41962
|
+
BooleanCellComponent,
|
|
41963
|
+
TokenCellComponent,
|
|
39551
41964
|
TableCellSelectComponent,
|
|
39552
41965
|
TableColumnSelectComponent,
|
|
39553
41966
|
TableCellMenuComponent,
|
|
@@ -39650,6 +42063,7 @@ class OpenZitiConsoleLibModule {
|
|
|
39650
42063
|
MatButtonModule,
|
|
39651
42064
|
MatIconModule,
|
|
39652
42065
|
OverlayModule,
|
|
42066
|
+
DragDropModule,
|
|
39653
42067
|
AgGridModule,
|
|
39654
42068
|
QRCodeComponent,
|
|
39655
42069
|
ClickOutsideModule,
|
|
@@ -39678,6 +42092,7 @@ class OpenZitiConsoleLibModule {
|
|
|
39678
42092
|
EdgeRouterFormComponent,
|
|
39679
42093
|
TransitRouterFormComponent,
|
|
39680
42094
|
IdentitiesPageComponent,
|
|
42095
|
+
IdentitiesListPageComponent,
|
|
39681
42096
|
EdgeRoutersPageComponent,
|
|
39682
42097
|
TransitRoutersPageComponent,
|
|
39683
42098
|
ServicesPageComponent,
|
|
@@ -39721,7 +42136,8 @@ class OpenZitiConsoleLibModule {
|
|
|
39721
42136
|
SideNavigatorComponent,
|
|
39722
42137
|
FilterSelectComponent,
|
|
39723
42138
|
FilterSelectOptionTemplateDirective,
|
|
39724
|
-
FilterSelectTriggerTemplateDirective
|
|
42139
|
+
FilterSelectTriggerTemplateDirective,
|
|
42140
|
+
ListTableComponent] }); }
|
|
39725
42141
|
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: OpenZitiConsoleLibModule, providers: [
|
|
39726
42142
|
{ provide: SHAREDZ_EXTENSION, useClass: ExtensionsNoopService },
|
|
39727
42143
|
{ provide: ZITI_NAVIGATOR, useValue: {} },
|
|
@@ -39746,6 +42162,7 @@ class OpenZitiConsoleLibModule {
|
|
|
39746
42162
|
MatButtonModule,
|
|
39747
42163
|
MatIconModule,
|
|
39748
42164
|
OverlayModule,
|
|
42165
|
+
DragDropModule,
|
|
39749
42166
|
AgGridModule,
|
|
39750
42167
|
QRCodeComponent,
|
|
39751
42168
|
ClickOutsideModule,
|
|
@@ -39777,12 +42194,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
39777
42194
|
ConfigTypesPageComponent,
|
|
39778
42195
|
ConfigTypeFormComponent,
|
|
39779
42196
|
ListPageHeaderComponent,
|
|
42197
|
+
ListTableHeaderComponent,
|
|
39780
42198
|
FilterBarComponent,
|
|
39781
42199
|
ListPageFormComponent,
|
|
39782
42200
|
IdentitiesPageComponent,
|
|
42201
|
+
IdentitiesListPageComponent,
|
|
39783
42202
|
EdgeRoutersPageComponent,
|
|
39784
42203
|
TransitRoutersPageComponent,
|
|
39785
42204
|
DataTableComponent,
|
|
42205
|
+
ListTableComponent,
|
|
42206
|
+
ListMenuComponent,
|
|
42207
|
+
NameCellComponent,
|
|
42208
|
+
SelectCellComponent,
|
|
42209
|
+
ListHeaderCellComponent,
|
|
42210
|
+
ListFilterCellComponent,
|
|
42211
|
+
OsInfoTooltipComponent,
|
|
42212
|
+
SdkInfoTooltipComponent,
|
|
42213
|
+
BooleanCellComponent,
|
|
42214
|
+
TokenCellComponent,
|
|
39786
42215
|
TableCellSelectComponent,
|
|
39787
42216
|
TableColumnSelectComponent,
|
|
39788
42217
|
TableCellMenuComponent,
|
|
@@ -39896,6 +42325,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
39896
42325
|
EdgeRouterFormComponent,
|
|
39897
42326
|
TransitRouterFormComponent,
|
|
39898
42327
|
IdentitiesPageComponent,
|
|
42328
|
+
IdentitiesListPageComponent,
|
|
39899
42329
|
EdgeRoutersPageComponent,
|
|
39900
42330
|
TransitRoutersPageComponent,
|
|
39901
42331
|
ServicesPageComponent,
|
|
@@ -39939,7 +42369,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
39939
42369
|
SideNavigatorComponent,
|
|
39940
42370
|
FilterSelectComponent,
|
|
39941
42371
|
FilterSelectOptionTemplateDirective,
|
|
39942
|
-
FilterSelectTriggerTemplateDirective
|
|
42372
|
+
FilterSelectTriggerTemplateDirective,
|
|
42373
|
+
ListTableComponent
|
|
39943
42374
|
], imports: [
|
|
39944
42375
|
CommonModule,
|
|
39945
42376
|
FormsModule,
|
|
@@ -39954,6 +42385,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
39954
42385
|
MatButtonModule,
|
|
39955
42386
|
MatIconModule,
|
|
39956
42387
|
OverlayModule,
|
|
42388
|
+
DragDropModule,
|
|
39957
42389
|
AgGridModule,
|
|
39958
42390
|
QRCodeComponent,
|
|
39959
42391
|
ClickOutsideModule,
|
|
@@ -41410,8 +43842,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
41410
43842
|
args: [SETTINGS_SERVICE]
|
|
41411
43843
|
}] }, { type: ZitiOidcService }, { type: GrowlerService }, { type: i0.Injector }] });
|
|
41412
43844
|
|
|
43845
|
+
/*
|
|
43846
|
+
Copyright NetFoundry Inc.
|
|
43847
|
+
|
|
43848
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
43849
|
+
you may not use this file except in compliance with the License.
|
|
43850
|
+
You may obtain a copy of the License at
|
|
43851
|
+
|
|
43852
|
+
https://www.apache.org/licenses/LICENSE-2.0
|
|
43853
|
+
|
|
43854
|
+
Unless required by applicable law or agreed to in writing, software
|
|
43855
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
43856
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
43857
|
+
See the License for the specific language governing permissions and
|
|
43858
|
+
limitations under the License.
|
|
43859
|
+
*/
|
|
43860
|
+
|
|
41413
43861
|
const ZAC_VERSION = {
|
|
41414
|
-
"version": "4.5.
|
|
43862
|
+
"version": "4.5.3"
|
|
41415
43863
|
};
|
|
41416
43864
|
|
|
41417
43865
|
/*
|
|
@@ -41422,5 +43870,5 @@ const ZAC_VERSION = {
|
|
|
41422
43870
|
* Generated bundle index. Do not edit.
|
|
41423
43871
|
*/
|
|
41424
43872
|
|
|
41425
|
-
export { APISessionFormComponent, APISessionsPageComponent, API_RESOURCE_TO_ENTITY_PERMISSION, ApiSessionsPageService, AttributesComponent, AuthPoliciesPageComponent, AuthPoliciesPageService, AuthPolicyFormComponent, AuthPolicyFormService, AuthService, BooleanToggleInputComponent, CallbackComponent, CardListComponent, CertificateAuthoritiesPageComponent, CertificateAuthoritiesPageService, CertificateAuthorityFormComponent, CertificateAuthorityFormService, CheckboxListInputComponent, ChipsInputComponent, ConfigEditorComponent, ConfigTypeFormComponent, ConfigTypesPageComponent, ConfigurationFormComponent, ConfigurationService, ConfigurationsPageComponent, CsvDownloadService, DEACTIVATE_GUARD, DEFAULT_APP_CONFIG, DataTableFilterService, DeactivateGuardService, EDGE_ROUTER_EXTENSION_SERVICE, EDGE_ROUTER_POLICY_EXTENSION_SERVICE, EdgeRouter, EdgeRouterFormComponent, EdgeRouterFormService, EdgeRouterPoliciesPageComponent, EdgeRouterPoliciesPageService, EdgeRouterPolicyFormComponent, EdgeRouterPolicyFormService, EdgeRoutersPageComponent, EdgeRoutersPageService, Entity, ExtendableComponent, ExtensionsNoopService, FilterSelectComponent, FilterSelectOptionTemplateDirective, FilterSelectTriggerTemplateDirective, FormFieldContainerComponent, FormFieldToggleComponent, FormHeaderComponent, GeolocateComponent, GrowlerComponent, GrowlerModel, GrowlerModule, GrowlerService, HAControllerService, HaStatusIndicatorComponent, HeaderBarComponent, IDENTITY_EXTENSION_SERVICE, IdentitiesPageComponent, IdentitiesPageService, Identity$1 as Identity, IdentityFormComponent, IdentityFormService, IdentityServicePathComponent, InsecureContextError, JsonViewComponent, JwtSignerFormComponent, JwtSignerFormService, JwtSignersPageComponent, JwtSignersPageService, KEY_CODES, ListPageComponent, ListPageServiceClass, LoadingIndicatorComponent, LoggerService, LoginDialogComponent, LoginServiceClass, ManagementPermissionsService, NetworkVisualizerComponent, NodeDataService, NoopLoginService, NoopTabInterceptorService, NumberInputComponent, OIDC_CLIENT_ID, OIDC_CLIENT_ID_LEGACY, ObjectComponent, OidcEnvironmentalError, OidcSessionUnrecoverableError, OpenZitiConsoleLibModule, PasswordInputComponent, PostureCheckFormComponent, PostureChecksPageComponent, PostureChecksPageService, PreviewListComponent, ProfileComponent, ProgressMeterComponent, ProgressMeterStep, ProgressWizardComponent, ProgressWizardStep, ProjectableForm, ProtocolAddressPortInputComponent, QuickAddComponent, ResetEnrollmentService, SAVE_DENIED_BY_PERMISSION_TOOLTIP, SERVICE_EDGE_ROUTER_POLICY_EXTENSION_SERVICE, SERVICE_EXTENSION_SERVICE, SERVICE_POLICY_EXTENSION_SERVICE, SETTINGS_SERVICE, SHAREDZ_EXTENSION, SelectorInputComponent, ServiceEdgeRouterPoliciesPageComponent, ServiceEdgeRouterPoliciesPageService, ServiceEdgeRouterPolicyFormComponent, ServiceEdgeRouterPolicyFormService, ServiceFormComponent, ServiceFormService, ServicePoliciesPageComponent, ServicePoliciesPageService, ServicePolicyFormComponent, ServicePolicyFormService, ServicesPageComponent, ServicesPageService, Session, SessionFormComponent, SessionRefreshService, SessionsPageComponent, SessionsPageService, SettingsComponent, SettingsService, SettingsServiceClass, SideModalComponent, SideNavbarComponent, SideNavigatorComponent, SimpleServiceComponent, StringInputComponent, TabNameService, TagSelectorComponent, TerminatorFormComponent, TerminatorFormService, TerminatorsPageComponent, TerminatorsPageService, TextListInputComponent, TransitRouterFormComponent, TransitRouterFormService, TransitRoutersPageComponent, TransitRoutersPageService, ValidationService, VerifyCertificateComponent, VisualizerServiceClass, ZAC_LOGIN_SERVICE, ZAC_VERSION, ZITI_DATA_SERVICE, ZITI_DOMAIN_CONTROLLER, ZITI_NAVIGATOR, ZITI_TAB_OVERRIDES, ZITI_URLS, ZacRoutingModule, ZitiControllerDataService, ZitiDataService, ZitiOidcService, decodeJwtPayload, jwtExpMs, jwtIatMs, pkceChallengeS256, playerFactory, randomUrlSafeString };
|
|
43873
|
+
export { APISessionFormComponent, APISessionsPageComponent, API_RESOURCE_TO_ENTITY_PERMISSION, ApiSessionsPageService, AttributesComponent, AuthPoliciesPageComponent, AuthPoliciesPageService, AuthPolicyFormComponent, AuthPolicyFormService, AuthService, BooleanCellComponent, BooleanToggleInputComponent, CallbackComponent, CardListComponent, CertificateAuthoritiesPageComponent, CertificateAuthoritiesPageService, CertificateAuthorityFormComponent, CertificateAuthorityFormService, CheckboxListInputComponent, ChipsInputComponent, ConfigEditorComponent, ConfigTypeFormComponent, ConfigTypesPageComponent, ConfigurationFormComponent, ConfigurationService, ConfigurationsPageComponent, CsvDownloadService, DEACTIVATE_GUARD, DEFAULT_APP_CONFIG, DataTableFilterService, DeactivateGuardService, EDGE_ROUTER_EXTENSION_SERVICE, EDGE_ROUTER_POLICY_EXTENSION_SERVICE, EdgeRouter, EdgeRouterFormComponent, EdgeRouterFormService, EdgeRouterPoliciesPageComponent, EdgeRouterPoliciesPageService, EdgeRouterPolicyFormComponent, EdgeRouterPolicyFormService, EdgeRoutersPageComponent, EdgeRoutersPageService, Entity, ExtendableComponent, ExtensionsNoopService, FilterSelectComponent, FilterSelectOptionTemplateDirective, FilterSelectTriggerTemplateDirective, FormFieldContainerComponent, FormFieldToggleComponent, FormHeaderComponent, GeolocateComponent, GrowlerComponent, GrowlerModel, GrowlerModule, GrowlerService, HAControllerService, HaStatusIndicatorComponent, HeaderBarComponent, IDENTITY_EXTENSION_SERVICE, IdentitiesListPageComponent, IdentitiesPageComponent, IdentitiesPageService, Identity$1 as Identity, IdentityFormComponent, IdentityFormService, IdentityServicePathComponent, InsecureContextError, JsonViewComponent, JwtSignerFormComponent, JwtSignerFormService, JwtSignersPageComponent, JwtSignersPageService, KEY_CODES, ListFilterCellComponent, ListHeaderCellComponent, ListMenuComponent, ListPageComponent, ListPageServiceClass, ListTableComponent, ListTableService, LoadingIndicatorComponent, LoggerService, LoginDialogComponent, LoginServiceClass, ManagementPermissionsService, NameCellComponent, NetworkVisualizerComponent, NodeDataService, NoopLoginService, NoopTabInterceptorService, NumberInputComponent, OIDC_CLIENT_ID, OIDC_CLIENT_ID_LEGACY, ObjectComponent, OidcEnvironmentalError, OidcSessionUnrecoverableError, OpenZitiConsoleLibModule, OsInfoTooltipComponent, PasswordInputComponent, PostureCheckFormComponent, PostureChecksPageComponent, PostureChecksPageService, PreviewListComponent, ProfileComponent, ProgressMeterComponent, ProgressMeterStep, ProgressWizardComponent, ProgressWizardStep, ProjectableForm, ProtocolAddressPortInputComponent, QuickAddComponent, ResetEnrollmentService, SAVE_DENIED_BY_PERMISSION_TOOLTIP, SERVICE_EDGE_ROUTER_POLICY_EXTENSION_SERVICE, SERVICE_EXTENSION_SERVICE, SERVICE_POLICY_EXTENSION_SERVICE, SETTINGS_SERVICE, SHAREDZ_EXTENSION, SdkInfoTooltipComponent, SelectCellComponent, SelectorInputComponent, ServiceEdgeRouterPoliciesPageComponent, ServiceEdgeRouterPoliciesPageService, ServiceEdgeRouterPolicyFormComponent, ServiceEdgeRouterPolicyFormService, ServiceFormComponent, ServiceFormService, ServicePoliciesPageComponent, ServicePoliciesPageService, ServicePolicyFormComponent, ServicePolicyFormService, ServicesPageComponent, ServicesPageService, Session, SessionFormComponent, SessionRefreshService, SessionsPageComponent, SessionsPageService, SettingsComponent, SettingsService, SettingsServiceClass, SideModalComponent, SideNavbarComponent, SideNavigatorComponent, SimpleServiceComponent, StringInputComponent, TabNameService, TagSelectorComponent, TerminatorFormComponent, TerminatorFormService, TerminatorsPageComponent, TerminatorsPageService, TextListInputComponent, TokenCellComponent, TransitRouterFormComponent, TransitRouterFormService, TransitRoutersPageComponent, TransitRoutersPageService, ValidationService, VerifyCertificateComponent, VisualizerServiceClass, ZAC_LOGIN_SERVICE, ZAC_VERSION, ZITI_DATA_SERVICE, ZITI_DOMAIN_CONTROLLER, ZITI_NAVIGATOR, ZITI_TAB_OVERRIDES, ZITI_URLS, ZacRoutingModule, ZitiControllerDataService, ZitiDataService, ZitiOidcService, decodeJwtPayload, jwtExpMs, jwtIatMs, pkceChallengeS256, playerFactory, randomUrlSafeString };
|
|
41426
43874
|
//# sourceMappingURL=openziti-ziti-console-lib.mjs.map
|