@c8y/ngx-components 1023.52.0 → 1023.53.0
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.
- package/asset-properties/index.d.ts +246 -27
- package/asset-properties/index.d.ts.map +1 -1
- package/fesm2022/c8y-ngx-components-asset-properties.mjs +640 -119
- package/fesm2022/c8y-ngx-components-asset-properties.mjs.map +1 -1
- package/locales/de.po +6 -0
- package/locales/es.po +6 -0
- package/locales/fr.po +6 -0
- package/locales/ja_JP.po +6 -0
- package/locales/ko.po +6 -0
- package/locales/locales.pot +12 -0
- package/locales/nl.po +6 -0
- package/locales/pl.po +6 -0
- package/locales/pt_BR.po +6 -0
- package/locales/zh_CN.po +6 -0
- package/locales/zh_TW.po +6 -0
- package/package.json +1 -1
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { gettext } from '@c8y/ngx-components/gettext';
|
|
2
2
|
import * as i0 from '@angular/core';
|
|
3
|
-
import { InjectionToken, inject, Injectable, Directive, Injector, runInInjectionContext, Pipe, viewChildren, ViewContainerRef, effect, reflectComponentType, Input, Component, viewChild, EventEmitter, ContentChild, Output } from '@angular/core';
|
|
3
|
+
import { InjectionToken, inject, Injectable, Directive, Injector, runInInjectionContext, Pipe, viewChildren, ViewContainerRef, effect, reflectComponentType, Input, Component, viewChild, EventEmitter, ContentChild, Output, forwardRef } from '@angular/core';
|
|
4
4
|
import { InventoryService } from '@c8y/client';
|
|
5
5
|
import * as i2 from '@c8y/ngx-components';
|
|
6
|
-
import { hookGeneric, ExtensionPointForPlugins, GroupService, fromTriggerOnce, getInjectedHooks, stateToFactory, AssetTypesRealtimeService, AlertService, isPromise, IconDirective, C8yTranslatePipe, ListGroupModule, EmptyStateComponent,
|
|
7
|
-
import { isArray, isObjectLike, isEmpty, find, forOwn, get, isUndefined, cloneDeep } from 'lodash-es';
|
|
6
|
+
import { hookGeneric, ExtensionPointForPlugins, GroupService, fromTriggerOnce, getInjectedHooks, stateToFactory, AssetTypesRealtimeService, AlertService, isPromise, IconDirective, C8yTranslatePipe, ListGroupModule, EmptyStateComponent, FormsModule as FormsModule$1, LoadMoreComponent, TabComponent, TabsOutletComponent, BottomDrawerRef, BottomDrawerService } from '@c8y/ngx-components';
|
|
7
|
+
import { isEqual, omit, isArray, isObjectLike, isEmpty, find, forOwn, get, isUndefined, cloneDeep } from 'lodash-es';
|
|
8
8
|
import { firstValueFrom, mergeMap, filter, take, map, distinctUntilChanged, shareReplay, BehaviorSubject, of, from, switchMap, isObservable, Subject, takeUntil, debounceTime } from 'rxjs';
|
|
9
9
|
import * as i1 from '@angular/router';
|
|
10
|
-
import { NgFor, NgClass, NgTemplateOutlet, AsyncPipe } from '@angular/common';
|
|
10
|
+
import { NgFor, NgClass, NgTemplateOutlet, AsyncPipe, JsonPipe } from '@angular/common';
|
|
11
11
|
import { CdkDropList, CdkDrag } from '@angular/cdk/drag-drop';
|
|
12
12
|
import * as i1$1 from '@angular/forms';
|
|
13
|
-
import { NgForm, FormsModule } from '@angular/forms';
|
|
13
|
+
import { NgForm, FormsModule, NG_VALUE_ACCESSOR } from '@angular/forms';
|
|
14
14
|
import * as i3 from 'ngx-bootstrap/tooltip';
|
|
15
15
|
import { TooltipModule } from 'ngx-bootstrap/tooltip';
|
|
16
16
|
import * as i4 from '@angular/cdk/tree';
|
|
@@ -18,6 +18,7 @@ import { CdkTreeModule } from '@angular/cdk/tree';
|
|
|
18
18
|
import { DataSource } from '@angular/cdk/collections';
|
|
19
19
|
import { TranslateService } from '@ngx-translate/core';
|
|
20
20
|
import { BsModalRef, BsModalService } from 'ngx-bootstrap/modal';
|
|
21
|
+
import { MillerViewComponent } from '@c8y/ngx-components/assets-navigator';
|
|
21
22
|
|
|
22
23
|
const defaultAssetPropertyListConfig = {
|
|
23
24
|
filterable: true,
|
|
@@ -937,20 +938,9 @@ class AssetPropertiesService {
|
|
|
937
938
|
*/
|
|
938
939
|
propertiesMatch(property1, property2) {
|
|
939
940
|
if (property1.name === property2.name) {
|
|
940
|
-
const
|
|
941
|
-
const
|
|
942
|
-
|
|
943
|
-
return keyPath1.join('.') === keyPath2.join('.');
|
|
944
|
-
}
|
|
945
|
-
else if (!keyPath1 && !keyPath2) {
|
|
946
|
-
if (property1.computed &&
|
|
947
|
-
property2.computed &&
|
|
948
|
-
property1.instanceId &&
|
|
949
|
-
property2.instanceId) {
|
|
950
|
-
return property1.instanceId === property2.instanceId;
|
|
951
|
-
}
|
|
952
|
-
return true;
|
|
953
|
-
}
|
|
941
|
+
const propertiesToOmit = ['active'];
|
|
942
|
+
const areEqual = isEqual(omit(property1, propertiesToOmit), omit(property2, propertiesToOmit));
|
|
943
|
+
return areEqual;
|
|
954
944
|
}
|
|
955
945
|
return false;
|
|
956
946
|
}
|
|
@@ -1866,7 +1856,7 @@ class AssetPropertyListComponent {
|
|
|
1866
1856
|
* Opens the drawer to add new custom properties.
|
|
1867
1857
|
*/
|
|
1868
1858
|
async addProperty() {
|
|
1869
|
-
const propertiesToAdd = await this.customPropertiesDrawerService.getCustomProperties(this.asset, { hiddenTabs: this.config.hiddenTabs });
|
|
1859
|
+
const propertiesToAdd = await this.customPropertiesDrawerService.getCustomProperties(this.asset, { hiddenTabs: { asset: true, ...this.config.hiddenTabs } });
|
|
1870
1860
|
if (!propertiesToAdd || propertiesToAdd.length === 0) {
|
|
1871
1861
|
return;
|
|
1872
1862
|
}
|
|
@@ -2064,6 +2054,7 @@ class AssetPropertyListComponent {
|
|
|
2064
2054
|
this.expansionState.set(key, !currentState);
|
|
2065
2055
|
this.rebuildVisibleData();
|
|
2066
2056
|
this.updateAreAllNodesExpandedState();
|
|
2057
|
+
this.applySelectedPropertiesFromConfig();
|
|
2067
2058
|
}
|
|
2068
2059
|
/**
|
|
2069
2060
|
* Selects or deselects all nodes.
|
|
@@ -2086,7 +2077,7 @@ class AssetPropertyListComponent {
|
|
|
2086
2077
|
* @param selected True if the node is selected.
|
|
2087
2078
|
* @param node The node to select.
|
|
2088
2079
|
*/
|
|
2089
|
-
onSelectSingle(selected, node) {
|
|
2080
|
+
onSelectSingle(selected, node, emit = true) {
|
|
2090
2081
|
this.allNodesComplete.forEach(n => {
|
|
2091
2082
|
n.property.active = false;
|
|
2092
2083
|
});
|
|
@@ -2097,7 +2088,7 @@ class AssetPropertyListComponent {
|
|
|
2097
2088
|
this.rebuildVisibleData();
|
|
2098
2089
|
this.updateAreAllNodesExpandedState();
|
|
2099
2090
|
}
|
|
2100
|
-
if (selected) {
|
|
2091
|
+
if (selected && emit) {
|
|
2101
2092
|
this.selectedProperties.next([node.property]);
|
|
2102
2093
|
}
|
|
2103
2094
|
}
|
|
@@ -2106,7 +2097,7 @@ class AssetPropertyListComponent {
|
|
|
2106
2097
|
* @param selected True if the node is selected.
|
|
2107
2098
|
* @param node The node to select.
|
|
2108
2099
|
*/
|
|
2109
|
-
onSelectMulti(selected, node) {
|
|
2100
|
+
onSelectMulti(selected, node, emit = true) {
|
|
2110
2101
|
node.property.active = selected;
|
|
2111
2102
|
if (selected && node.expandable && !this.isNodeExpanded(node)) {
|
|
2112
2103
|
const key = this.getNodeKey(node);
|
|
@@ -2121,7 +2112,9 @@ class AssetPropertyListComponent {
|
|
|
2121
2112
|
const selectedProperties = this.allNodesComplete
|
|
2122
2113
|
.filter(n => n.property.active)
|
|
2123
2114
|
.map(n => n.property);
|
|
2124
|
-
|
|
2115
|
+
if (emit) {
|
|
2116
|
+
this.selectedProperties.next(selectedProperties);
|
|
2117
|
+
}
|
|
2125
2118
|
}
|
|
2126
2119
|
/**
|
|
2127
2120
|
* Initiates a filter operation.
|
|
@@ -2275,24 +2268,42 @@ class AssetPropertyListComponent {
|
|
|
2275
2268
|
});
|
|
2276
2269
|
}
|
|
2277
2270
|
applySelectedPropertiesFromConfig() {
|
|
2278
|
-
if (!this.config.selectedProperties
|
|
2271
|
+
if (!this.config.selectedProperties) {
|
|
2279
2272
|
return;
|
|
2280
2273
|
}
|
|
2281
|
-
this.config.selectedProperties
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2274
|
+
if (this.config.selectedProperties?.length === 0) {
|
|
2275
|
+
this.allNodesComplete.forEach(node => {
|
|
2276
|
+
node.property.active = false;
|
|
2277
|
+
});
|
|
2278
|
+
this.updateSelectAllState();
|
|
2279
|
+
return;
|
|
2280
|
+
}
|
|
2281
|
+
let selectedPropertiesToApply = this.config.selectedProperties;
|
|
2282
|
+
if (this.config.selectMode === 'single') {
|
|
2283
|
+
selectedPropertiesToApply = this.config.selectedProperties.slice(0, 1);
|
|
2284
|
+
// For single mode, find the matching node and only call onSelectSingle once
|
|
2285
|
+
const nodeToSelect = this.dataSource.data.find(node => selectedPropertiesToApply.some(configProperty => this.assetPropertiesService.propertiesMatch(node.property, configProperty)));
|
|
2286
|
+
if (nodeToSelect) {
|
|
2287
|
+
this.onSelectSingle(true, nodeToSelect, false);
|
|
2288
|
+
}
|
|
2289
|
+
else {
|
|
2290
|
+
this.allNodesComplete.forEach(node => {
|
|
2291
|
+
node.property.active = false;
|
|
2292
|
+
});
|
|
2293
|
+
}
|
|
2294
|
+
}
|
|
2295
|
+
else {
|
|
2296
|
+
// Multi or plus mode - iterate through all nodes
|
|
2297
|
+
this.dataSource.data.forEach(node => {
|
|
2298
|
+
const matchingNode = selectedPropertiesToApply.find(configProperty => this.assetPropertiesService.propertiesMatch(node.property, configProperty));
|
|
2299
|
+
if (matchingNode) {
|
|
2300
|
+
this.onSelectMulti(true, node, false);
|
|
2290
2301
|
}
|
|
2291
|
-
else
|
|
2292
|
-
this.onSelectMulti(
|
|
2302
|
+
else {
|
|
2303
|
+
this.onSelectMulti(false, node, false);
|
|
2293
2304
|
}
|
|
2294
|
-
}
|
|
2295
|
-
}
|
|
2305
|
+
});
|
|
2306
|
+
}
|
|
2296
2307
|
}
|
|
2297
2308
|
/**
|
|
2298
2309
|
* Updates the display added properties based on compatibility with current asset.
|
|
@@ -2583,7 +2594,7 @@ class AssetPropertyListComponent {
|
|
|
2583
2594
|
return [...computed, ...simple, ...complex];
|
|
2584
2595
|
}
|
|
2585
2596
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AssetPropertyListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2586
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: AssetPropertyListComponent, isStandalone: true, selector: "c8y-asset-property-list", inputs: { config: "config", asset: "asset", extraProperties: "extraProperties" }, outputs: { selectedProperties: "selectedProperties" }, queries: [{ propertyName: "assetPropertyAction", first: true, predicate: AssetPropertyActionDirective, descendants: true }], viewQueries: [{ propertyName: "tree", first: true, predicate: ["tree"], descendants: true, isSignal: true }], usesOnChanges: true, ngImport: i0, template: "<div\n class=\"tree-container bg-inherit\"\n [attr.aria-label]=\"'Asset property list' | translate\"\n role=\"tree\"\n>\n @if (config.showHeader || config.filterable) {\n <div\n class=\"select-all-container bg-inherit sticky-top\"\n [ngClass]=\"{\n 'separator-bottom': config.filterable || (config.showHeader && dataSource.data.length)\n }\"\n >\n @if (config.filterable) {\n <div\n class=\"form-group m-b-0 p-16 d-flex\"\n [ngClass]=\"{ 'separator-bottom': config.showHeader }\"\n >\n <div class=\"input-group input-group-search\">\n <input\n class=\"form-control\"\n [attr.aria-label]=\"'Filter properties' | translate\"\n placeholder=\"{{ 'Filter properties' | translate }}\"\n role=\"searchbox\"\n type=\"search\"\n autocomplete=\"off\"\n [(ngModel)]=\"filterText\"\n (input)=\"onFilter()\"\n [disabled]=\"!dataSource.data.length\"\n #filter\n />\n @if (filter.value.length === 0) {\n <span\n class=\"input-group-addon\"\n [attr.aria-hidden]=\"true\"\n >\n <i\n class=\"dlt-c8y-icon-search\"\n [c8yIcon]=\"'search'\"\n ></i>\n </span>\n }\n @if (filter.value.length > 0) {\n <span\n class=\"input-group-addon\"\n [attr.aria-hidden]=\"true\"\n >\n <i\n class=\"text-muted dlt-c8y-icon-times\"\n [c8yIcon]=\"'times'\"\n [attr.aria-label]=\"'Clear filter' | translate\"\n tabindex=\"0\"\n role=\"button\"\n (click)=\"clearFilter()\"\n ></i>\n </span>\n }\n </div>\n </div>\n }\n @if (config.showHeader && dataSource.data.length) {\n <div class=\"d-flex a-i-center overflow-hidden\">\n <div\n class=\"flex-no-shrink d-flex a-i-center\"\n [ngClass]=\"{\n 'p-l-40': !hasExpandableNodes && config.expansionMode !== 'nonCollapsible',\n 'p-r-8': config.selectMode !== 'none' && config.expansionMode === 'nonCollapsible'\n }\"\n >\n @if (config.expansionMode !== 'nonCollapsible' && hasExpandableNodes) {\n @let collapseAllButtonTitle =\n (areAllNodesExpanded ? collapseAllLabel : expandAllLabel) | translate;\n <button\n class=\"collapse-btn btn-dot flex-no-shrink\"\n [title]=\"collapseAllButtonTitle\"\n [attr.aria-label]=\"collapseAllButtonTitle\"\n type=\"button\"\n [ngClass]=\"{ active: areAllNodesExpanded }\"\n (click)=\"toggleExpandCollapseAll()\"\n >\n <i [c8yIcon]=\"'chevron-right'\"></i>\n </button>\n }\n </div>\n @if (config.selectMode === 'multi') {\n <c8y-list-item-checkbox\n class=\"p-l-4 p-r-4\"\n [ngModel]=\"allSelected\"\n [indeterminate]=\"indeterminate\"\n (onSelect)=\"selectAll($event)\"\n ></c8y-list-item-checkbox>\n }\n <div\n class=\"p-t-40\"\n [ngClass]=\"{\n 'p-l-24': config.selectMode === 'single',\n 'p-l-8': config.selectMode === 'none' && config.expansionMode === 'nonCollapsible'\n }\"\n ></div>\n <div class=\"content-flex-30 fit-w m-t-4 m-b-4 m-0\">\n <div class=\"col-6 d-flex a-i-center m-l-0 m-r-0\">\n <span class=\"flex-no-shrink p-l-4 p-r-8\">\n <div class=\"c8y-icon\"></div>\n </span>\n <span class=\"text-medium m-l-8\">\n {{ 'Property' | translate }}\n </span>\n </div>\n @if (config.showKey) {\n <div [ngClass]=\"{ 'col-3': config.showValue, 'col-6': !config.showValue }\">\n <span class=\"text-medium\">{{ 'Key' | translate }}</span>\n </div>\n }\n @if (config.showValue) {\n <div [ngClass]=\"{ 'col-3': config.showKey, 'col-6': !config.showKey }\">\n <span class=\"text-medium\">{{ 'Value' | translate }}</span>\n </div>\n }\n </div>\n @if (assetPropertyAction) {\n <div\n class=\"m-l-8 p-l-24 asset-property-list__actions\"\n [ngClass]=\"{\n 'p-r-40': config.allowAddingCustomProperties,\n 'p-r-8': !config.allowAddingCustomProperties\n }\"\n ></div>\n }\n </div>\n }\n </div>\n }\n\n @if (dataSource.data.length) {\n <cdk-tree\n role=\"presentation\"\n #tree\n [dataSource]=\"dataSource\"\n [levelAccessor]=\"getLevel\"\n cdkDropList\n cdkDropListOrientation=\"vertical\"\n (cdkDropListDropped)=\"drop($event)\"\n [cdkDropListDisabled]=\"!config.allowDragAndDrop || dataSource.data.length < 2\"\n >\n <!-- Tree Node Definition -->\n <cdk-tree-node\n class=\"c8y-list__item--dense\"\n [attr.tabindex]=\"0\"\n [attr.aria-level]=\"getLevel(node) + 1\"\n [attr.aria-expanded]=\"hasChild(node) ? isNodeExpanded(node) : null\"\n [attr.aria-selected]=\"config.selectMode !== 'none' ? node.property.active : null\"\n role=\"treeitem\"\n *cdkTreeNodeDef=\"let node\"\n cdkTreeNodePadding\n [cdkTreeNodePaddingIndent]=\"TREE_NODE_INDENT\"\n [ngClass]=\"{\n nonCollapsible: config.expansionMode === 'nonCollapsible',\n nonSelectable: config.selectMode === 'none'\n }\"\n cdkDrag\n cdkDragLockAxis=\"y\"\n [cdkDragDisabled]=\"cdkDragDisabled\"\n >\n <div class=\"d-flex p-relative overflow-visible bg-inherit fit-h\">\n @if (config.allowDragAndDrop && dataSource.data.length > 1) {\n <div\n class=\"drag-handle-wrapper\"\n [ngClass]=\"{ 'drag-handle-disabled': cdkDragDisabled }\"\n >\n <button\n class=\"drag-handle btn-clean\"\n [title]=\"\n (cdkDragDisabled ? dragHandleDisabledLabel : dragHandleEnabledLabel) | translate\n \"\n [attr.aria-label]=\"\n (cdkDragDisabled ? dragHandleDisabledLabel : dragHandleEnabledLabel) | translate\n \"\n tabindex=\"-1\"\n type=\"button\"\n [disabled]=\"cdkDragDisabled\"\n >\n <i [c8yIcon]=\"'drag-reorder'\"></i>\n </button>\n </div>\n }\n <!-- Toggle Button for expandable nodes -->\n @if (config.expansionMode !== 'nonCollapsible' && hasChild(node)) {\n @let collapseButtonTitle =\n isNodeExpanded(node) | c8yAssetPropertyCollapseButtonTitle: node;\n <button\n class=\"collapse-btn btn-dot flex-no-shrink\"\n [title]=\"collapseButtonTitle\"\n [attr.aria-label]=\"collapseButtonTitle\"\n [attr.aria-expanded]=\"isNodeExpanded(node)\"\n type=\"button\"\n (click)=\"toggleNode(node)\"\n [ngClass]=\"{ active: isNodeExpanded(node) }\"\n >\n <i [c8yIcon]=\"'chevron-right'\"></i>\n </button>\n }\n <!-- Placeholder for non-expandable nodes to maintain alignment -->\n @if (!hasChild(node) || config.expansionMode === 'nonCollapsible') {\n <div\n class=\"flex-no-shrink\"\n [ngClass]=\"{\n 'p-r-40': config.expansionMode !== 'nonCollapsible',\n 'p-r-8': config.expansionMode === 'nonCollapsible'\n }\"\n ></div>\n }\n <!-- Selection Controls -->\n @if (config.selectMode !== 'none') {\n <div class=\"d-contents\">\n @if (config.selectMode === 'single') {\n <c8y-list-item-radio\n class=\"p-l-4 p-r-4\"\n type=\"radio\"\n [selected]=\"node.property.active\"\n (onSelect)=\"onSelectSingle($event, node)\"\n ></c8y-list-item-radio>\n } @else if (config.selectMode === 'multi') {\n <c8y-list-item-checkbox\n class=\"p-l-4 p-r-4\"\n [ngModel]=\"node.property.active\"\n [indeterminate]=\"node.indeterminate\"\n (onSelect)=\"onSelectMulti($event, node)\"\n ></c8y-list-item-checkbox>\n }\n </div>\n }\n <div class=\"content-flex-30 fit-w bg-inherit m-0 min-width-0\">\n <div\n class=\"d-flex a-i-center bg-inherit m-0 flex-grow\"\n [style.max-width]=\"getColumnMaxWidth(node)\"\n >\n <c8y-li-icon\n class=\"p-r-4 p-l-4\"\n [icon]=\"node.property | c8yAssetPropertyIcon\"\n tooltip=\"{{ node.property | c8yAssetPropertyIconTooltip }}\"\n container=\"body\"\n [delay]=\"500\"\n [ngClass]=\"{\n 'p-l-4': config.selectMode !== 'none',\n 'p-l-8': config.selectMode === 'none'\n }\"\n ></c8y-li-icon>\n\n <span class=\"p-r-8 min-width-0 d-flex a-i-center\">\n @if (isContextMismatch(node)) {\n @let tooltipText =\n 'Property configured for a different asset. Reconfigure to match current asset.'\n | translate;\n <button\n class=\"btn-clean m-r-4\"\n [attr.aria-label]=\"tooltipText\"\n [tooltip]=\"tooltipText\"\n placement=\"top\"\n type=\"button\"\n [container]=\"'body'\"\n >\n <i\n class=\"status critical stroked-icon\"\n c8yIcon=\"exclamation-circle\"\n ></i>\n </button>\n }\n\n <div\n class=\"text-truncate\"\n title=\"{{\n node.property.label || node.property.title || node.property.name | translate\n }}\"\n [ngClass]=\"{\n 'text-muted': node.property.temporary,\n 'text-danger': isContextMismatch(node)\n }\"\n >\n {{ node.property.label || node.property.title || node.property.name | translate }}\n </div>\n </span>\n </div>\n @if (config.showKey) {\n <div\n class=\"d-flex a-i-center\"\n [ngClass]=\"{ 'col-3': config.showValue, 'col-6': !config.showValue }\"\n >\n <span\n class=\"d-inline-block tag tag--default a-s-center text-truncate\"\n title=\"{{\n node.property.keyPath?.join('.') || (node.property?.name | translate)\n }}\"\n >\n {{ node.property.keyPath?.join('.') || (node.property?.name | translate) }}\n </span>\n </div>\n }\n @if (asset && config.showValue) {\n <div\n class=\"d-flex a-i-center\"\n [ngClass]=\"{ 'col-3': config.showKey, 'col-6': !config.showKey }\"\n >\n @let value = node.property | c8yAssetPropertyValue: asset | async;\n <span\n class=\"tag tag--info d-inline-block a-s-center text-truncate\"\n title=\"{{ value }}\"\n >\n {{ value }}\n </span>\n </div>\n }\n </div>\n @if (assetPropertyAction || node.property.temporary) {\n <div\n class=\"m-l-8 showOnHover d-flex a-i-center j-c-end asset-property-list__actions\"\n [ngClass]=\"{\n 'p-l-32': !node.property.temporary && config.allowAddingCustomProperties,\n 'has-computed': node.property.computed && node.property.config\n }\"\n >\n @if (node.property.computed && node.property.config) {\n <button\n class=\"btn btn-dot m-l-auto\"\n title=\"{{ 'Configure' | translate }}\"\n (click)=\"editProperty(node.property)\"\n >\n <i [c8yIcon]=\"'cog'\"></i>\n </button>\n }\n @if (node.property.temporary) {\n <button\n class=\"btn btn-dot btn-dot--danger\"\n [attr.aria-label]=\"'Remove' | translate\"\n tooltip=\"{{ 'Remove' | translate }}\"\n container=\"body\"\n type=\"button\"\n [ngClass]=\"{ 'm-l-auto': !node.property.computed || !node.property.config }\"\n [delay]=\"500\"\n (click)=\"removeProperty(node.property)\"\n >\n <i c8yIcon=\"minus-circle\"></i>\n </button>\n }\n <ng-container\n *ngTemplateOutlet=\"\n assetPropertyAction?.template;\n context: {\n $implicit: node.property\n }\n \"\n ></ng-container>\n </div>\n }\n </div>\n </cdk-tree-node>\n </cdk-tree>\n } @else {\n <c8y-ui-empty-state\n icon=\"list\"\n title=\"{{ 'No properties to display' | translate }}\"\n subtitle=\"{{ 'Select an asset to see the available properties.' | translate }}\"\n [horizontal]=\"true\"\n ></c8y-ui-empty-state>\n }\n @if (config.allowAddingCustomProperties) {\n <div class=\"sticky-bottom bg-inherit separator-top p-16\">\n <button\n class=\"btn btn-default btn-sm m-l-8\"\n (click)=\"addProperty()\"\n data-cy=\"asset-property-item-add-button\"\n >\n <i [c8yIcon]=\"'plus'\"></i>\n {{ 'Add property' | translate }}\n </button>\n </div>\n }\n</div>\n", dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.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: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ListGroupModule }, { kind: "component", type: i2.ListItemIconComponent, selector: "c8y-list-item-icon, c8y-li-icon", inputs: ["icon", "status"] }, { kind: "component", type: i2.ListItemCheckboxComponent, selector: "c8y-list-item-checkbox, c8y-li-checkbox", inputs: ["selected", "indeterminate", "disabled", "displayAsSwitch"], outputs: ["onSelect"] }, { kind: "component", type: i2.ListItemRadioComponent, selector: "c8y-list-item-radio, c8y-li-radio", inputs: ["selected", "name", "disabled", "value"], outputs: ["onSelect"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i3.TooltipDirective, selector: "[tooltip], [tooltipHtml]", inputs: ["adaptivePosition", "tooltip", "placement", "triggers", "container", "containerClass", "boundariesElement", "isOpen", "isDisabled", "delay", "tooltipHtml", "tooltipPlacement", "tooltipIsOpen", "tooltipEnable", "tooltipAppendToBody", "tooltipAnimation", "tooltipClass", "tooltipContext", "tooltipPopupDelay", "tooltipFadeDuration", "tooltipTrigger"], outputs: ["tooltipChange", "onShown", "onHidden", "tooltipStateChanged"], exportAs: ["bs-tooltip"] }, { kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "ngmodule", type: CdkTreeModule }, { kind: "directive", type: i4.CdkTreeNodeDef, selector: "[cdkTreeNodeDef]", inputs: ["cdkTreeNodeDefWhen"] }, { kind: "directive", type: i4.CdkTreeNodePadding, selector: "[cdkTreeNodePadding]", inputs: ["cdkTreeNodePadding", "cdkTreeNodePaddingIndent"] }, { kind: "component", type: i4.CdkTree, selector: "cdk-tree", inputs: ["dataSource", "treeControl", "levelAccessor", "childrenAccessor", "trackBy", "expansionKey"], exportAs: ["cdkTree"] }, { kind: "directive", type: i4.CdkTreeNode, selector: "cdk-tree-node", inputs: ["role", "isExpandable", "isExpanded", "isDisabled", "cdkTreeNodeTypeaheadLabel"], outputs: ["activation", "expandedChange"], exportAs: ["cdkTreeNode"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: EmptyStateComponent, selector: "c8y-ui-empty-state", inputs: ["icon", "title", "subtitle", "horizontal"] }, { kind: "directive", type: CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: AssetPropertyValuePipe, name: "c8yAssetPropertyValue" }, { kind: "pipe", type: AssetPropertyIconPipe, name: "c8yAssetPropertyIcon" }, { kind: "pipe", type: AssetPropertyIconTooltipPipe, name: "c8yAssetPropertyIconTooltip" }, { kind: "pipe", type: AssetPropertyCollapseButtonTitlePipe, name: "c8yAssetPropertyCollapseButtonTitle" }] }); }
|
|
2597
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: AssetPropertyListComponent, isStandalone: true, selector: "c8y-asset-property-list", inputs: { config: "config", asset: "asset", extraProperties: "extraProperties" }, outputs: { selectedProperties: "selectedProperties" }, queries: [{ propertyName: "assetPropertyAction", first: true, predicate: AssetPropertyActionDirective, descendants: true }], viewQueries: [{ propertyName: "tree", first: true, predicate: ["tree"], descendants: true, isSignal: true }], usesOnChanges: true, ngImport: i0, template: "<div\n class=\"tree-container bg-inherit\"\n [attr.aria-label]=\"'Asset property list' | translate\"\n role=\"tree\"\n>\n @if (config.showHeader || config.filterable) {\n <div\n class=\"select-all-container bg-inherit sticky-top\"\n [ngClass]=\"{\n 'separator-bottom': config.filterable || (config.showHeader && dataSource.data.length)\n }\"\n >\n @if (config.filterable) {\n <div\n class=\"form-group m-b-0 p-16 d-flex\"\n [ngClass]=\"{ 'separator-bottom': config.showHeader }\"\n >\n <div class=\"input-group input-group-search\">\n <input\n class=\"form-control\"\n [attr.aria-label]=\"'Filter properties' | translate\"\n placeholder=\"{{ 'Filter properties' | translate }}\"\n role=\"searchbox\"\n type=\"search\"\n autocomplete=\"off\"\n [(ngModel)]=\"filterText\"\n (input)=\"onFilter()\"\n [disabled]=\"!dataSource.data.length\"\n #filter\n />\n @if (filter.value.length === 0) {\n <span\n class=\"input-group-addon\"\n [attr.aria-hidden]=\"true\"\n >\n <i\n class=\"dlt-c8y-icon-search\"\n [c8yIcon]=\"'search'\"\n ></i>\n </span>\n }\n @if (filter.value.length > 0) {\n <span\n class=\"input-group-addon\"\n [attr.aria-hidden]=\"true\"\n >\n <i\n class=\"text-muted dlt-c8y-icon-times\"\n [c8yIcon]=\"'times'\"\n [attr.aria-label]=\"'Clear filter' | translate\"\n tabindex=\"0\"\n role=\"button\"\n (click)=\"clearFilter()\"\n ></i>\n </span>\n }\n </div>\n </div>\n }\n @if (config.showHeader && dataSource.data.length) {\n <div class=\"d-flex a-i-center overflow-hidden\">\n <div\n class=\"flex-no-shrink d-flex a-i-center\"\n [ngClass]=\"{\n 'p-l-40': !hasExpandableNodes && config.expansionMode !== 'nonCollapsible',\n 'p-r-8': config.selectMode !== 'none' && config.expansionMode === 'nonCollapsible'\n }\"\n >\n @if (config.expansionMode !== 'nonCollapsible' && hasExpandableNodes) {\n @let collapseAllButtonTitle =\n (areAllNodesExpanded ? collapseAllLabel : expandAllLabel) | translate;\n <button\n class=\"collapse-btn btn-dot flex-no-shrink\"\n [title]=\"collapseAllButtonTitle\"\n [attr.aria-label]=\"collapseAllButtonTitle\"\n type=\"button\"\n [ngClass]=\"{ active: areAllNodesExpanded }\"\n (click)=\"toggleExpandCollapseAll()\"\n >\n <i [c8yIcon]=\"'chevron-right'\"></i>\n </button>\n }\n </div>\n @if (config.selectMode === 'multi') {\n <c8y-list-item-checkbox\n class=\"p-l-4 p-r-4\"\n [ngModel]=\"allSelected\"\n [indeterminate]=\"indeterminate\"\n (onSelect)=\"selectAll($event)\"\n ></c8y-list-item-checkbox>\n }\n <div\n class=\"p-t-40\"\n [ngClass]=\"{\n 'p-l-24': config.selectMode === 'single',\n 'p-l-8': config.selectMode === 'none' && config.expansionMode === 'nonCollapsible'\n }\"\n ></div>\n <div class=\"content-flex-30 fit-w m-t-4 m-b-4 m-0\">\n <div class=\"col-6 d-flex a-i-center m-l-0 m-r-0\">\n <span class=\"flex-no-shrink p-l-4 p-r-8\">\n <div class=\"c8y-icon\"></div>\n </span>\n <span class=\"text-medium m-l-8\">\n {{ 'Property' | translate }}\n </span>\n </div>\n @if (config.showKey) {\n <div [ngClass]=\"{ 'col-3': config.showValue, 'col-6': !config.showValue }\">\n <span class=\"text-medium\">{{ 'Key' | translate }}</span>\n </div>\n }\n @if (config.showValue) {\n <div [ngClass]=\"{ 'col-3': config.showKey, 'col-6': !config.showKey }\">\n <span class=\"text-medium\">{{ 'Value' | translate }}</span>\n </div>\n }\n </div>\n @if (assetPropertyAction) {\n <div\n class=\"m-l-8 p-l-24 asset-property-list__actions\"\n [ngClass]=\"{\n 'p-r-40': config.allowAddingCustomProperties,\n 'p-r-8': !config.allowAddingCustomProperties\n }\"\n ></div>\n }\n </div>\n }\n </div>\n }\n\n @if (dataSource.data.length) {\n <cdk-tree\n role=\"presentation\"\n #tree\n [dataSource]=\"dataSource\"\n [levelAccessor]=\"getLevel\"\n cdkDropList\n cdkDropListOrientation=\"vertical\"\n (cdkDropListDropped)=\"drop($event)\"\n [cdkDropListDisabled]=\"!config.allowDragAndDrop || dataSource.data.length < 2\"\n >\n <!-- Tree Node Definition -->\n <cdk-tree-node\n class=\"c8y-list__item--dense\"\n [attr.tabindex]=\"0\"\n [attr.aria-level]=\"getLevel(node) + 1\"\n [attr.aria-expanded]=\"hasChild(node) ? isNodeExpanded(node) : null\"\n [attr.aria-selected]=\"config.selectMode !== 'none' ? node.property.active : null\"\n role=\"treeitem\"\n *cdkTreeNodeDef=\"let node\"\n cdkTreeNodePadding\n [cdkTreeNodePaddingIndent]=\"TREE_NODE_INDENT\"\n [ngClass]=\"{\n nonCollapsible: config.expansionMode === 'nonCollapsible',\n nonSelectable: config.selectMode === 'none'\n }\"\n cdkDrag\n cdkDragLockAxis=\"y\"\n [cdkDragDisabled]=\"cdkDragDisabled\"\n >\n <div class=\"d-flex p-relative overflow-visible bg-inherit fit-h\">\n @if (config.allowDragAndDrop && dataSource.data.length > 1) {\n <div\n class=\"drag-handle-wrapper\"\n [ngClass]=\"{ 'drag-handle-disabled': cdkDragDisabled }\"\n >\n <button\n class=\"drag-handle btn-clean\"\n [title]=\"\n (cdkDragDisabled ? dragHandleDisabledLabel : dragHandleEnabledLabel) | translate\n \"\n [attr.aria-label]=\"\n (cdkDragDisabled ? dragHandleDisabledLabel : dragHandleEnabledLabel) | translate\n \"\n tabindex=\"-1\"\n type=\"button\"\n [disabled]=\"cdkDragDisabled\"\n >\n <i [c8yIcon]=\"'drag-reorder'\"></i>\n </button>\n </div>\n }\n <!-- Toggle Button for expandable nodes -->\n @if (config.expansionMode !== 'nonCollapsible' && hasChild(node)) {\n @let collapseButtonTitle =\n isNodeExpanded(node) | c8yAssetPropertyCollapseButtonTitle: node;\n <button\n class=\"collapse-btn btn-dot flex-no-shrink\"\n [title]=\"collapseButtonTitle\"\n [attr.aria-label]=\"collapseButtonTitle\"\n [attr.aria-expanded]=\"isNodeExpanded(node)\"\n type=\"button\"\n (click)=\"toggleNode(node)\"\n [ngClass]=\"{ active: isNodeExpanded(node) }\"\n >\n <i [c8yIcon]=\"'chevron-right'\"></i>\n </button>\n }\n <!-- Placeholder for non-expandable nodes to maintain alignment -->\n @if (!hasChild(node) || config.expansionMode === 'nonCollapsible') {\n <div\n class=\"flex-no-shrink\"\n [ngClass]=\"{\n 'p-r-40': config.expansionMode !== 'nonCollapsible',\n 'p-r-8': config.expansionMode === 'nonCollapsible'\n }\"\n ></div>\n }\n <!-- Selection Controls -->\n @if (config.selectMode !== 'none') {\n <div class=\"d-contents\">\n @if (config.selectMode === 'single') {\n <c8y-list-item-radio\n class=\"p-l-4 p-r-4\"\n type=\"radio\"\n [selected]=\"node.property.active\"\n (onSelect)=\"onSelectSingle($event, node)\"\n ></c8y-list-item-radio>\n } @else if (config.selectMode === 'multi') {\n <c8y-list-item-checkbox\n class=\"p-l-4 p-r-4\"\n [ngModel]=\"node.property.active\"\n [indeterminate]=\"node.indeterminate\"\n (onSelect)=\"onSelectMulti($event, node)\"\n ></c8y-list-item-checkbox>\n } @else if (config.selectMode === 'plus') {\n @if (node.property.active) {\n <button\n class=\"btn btn-dot btn-dot--danger m-l-4 m-r-4 p-relative a-s-center\"\n style=\"z-index: 5\"\n [attr.aria-label]=\"'Remove' | translate\"\n [tooltip]=\"'Remove from selected properties' | translate\"\n type=\"button\"\n [delay]=\"500\"\n (click)=\"onSelectMulti(false, node)\"\n >\n <i\n class=\"icon-20\"\n c8yIcon=\"minus-circle\"\n ></i>\n </button>\n }\n @if (!node.property.active) {\n <button\n class=\"btn btn-dot text-primary m-l-4 m-r-4 p-relative a-s-center\"\n style=\"z-index: 5\"\n [attr.aria-label]=\"'Add to selected properties' | translate\"\n [tooltip]=\"\n (node.property.config\n ? 'Add to selected properties (requires configuration)'\n : 'Add to selected properties'\n ) | translate\n \"\n container=\"body\"\n type=\"button\"\n [delay]=\"500\"\n (click)=\"onSelectMulti(true, node)\"\n >\n <i\n class=\"text-primary icon-20\"\n [c8yIcon]=\"node.property.config ? 'plus-circle-o' : 'plus-circle'\"\n ></i>\n </button>\n }\n }\n </div>\n }\n <div class=\"content-flex-30 fit-w bg-inherit m-0 min-width-0\">\n <div\n class=\"d-flex a-i-center bg-inherit m-0 flex-grow\"\n [style.max-width]=\"getColumnMaxWidth(node)\"\n >\n <c8y-li-icon\n class=\"p-r-4 p-l-4\"\n [icon]=\"node.property | c8yAssetPropertyIcon\"\n tooltip=\"{{ node.property | c8yAssetPropertyIconTooltip }}\"\n container=\"body\"\n [delay]=\"500\"\n [ngClass]=\"{\n 'p-l-4': config.selectMode !== 'none',\n 'p-l-8': config.selectMode === 'none'\n }\"\n ></c8y-li-icon>\n\n <span class=\"p-r-8 min-width-0 d-flex a-i-center\">\n @if (isContextMismatch(node)) {\n @let tooltipText =\n 'Property configured for a different asset. Reconfigure to match current asset.'\n | translate;\n <button\n class=\"btn-clean m-r-4\"\n [attr.aria-label]=\"tooltipText\"\n [tooltip]=\"tooltipText\"\n placement=\"top\"\n type=\"button\"\n [container]=\"'body'\"\n >\n <i\n class=\"status critical stroked-icon\"\n c8yIcon=\"exclamation-circle\"\n ></i>\n </button>\n }\n\n <div\n class=\"text-truncate\"\n title=\"{{\n node.property.label || node.property.title || node.property.name | translate\n }}\"\n [ngClass]=\"{\n 'text-muted': node.property.temporary,\n 'text-danger': isContextMismatch(node)\n }\"\n >\n {{ node.property.label || node.property.title || node.property.name | translate }}\n </div>\n </span>\n </div>\n @if (config.showKey) {\n <div\n class=\"d-flex a-i-center\"\n [ngClass]=\"{ 'col-3': config.showValue, 'col-6': !config.showValue }\"\n >\n <span\n class=\"d-inline-block tag tag--default a-s-center text-truncate\"\n title=\"{{\n node.property.keyPath?.join('.') || (node.property?.name | translate)\n }}\"\n >\n {{ node.property.keyPath?.join('.') || (node.property?.name | translate) }}\n </span>\n </div>\n }\n @if (asset && config.showValue) {\n <div\n class=\"d-flex a-i-center\"\n [ngClass]=\"{ 'col-3': config.showKey, 'col-6': !config.showKey }\"\n >\n @let value = node.property | c8yAssetPropertyValue: asset | async;\n <span\n class=\"tag tag--info d-inline-block a-s-center text-truncate\"\n title=\"{{ value }}\"\n >\n {{ value }}\n </span>\n </div>\n }\n </div>\n @if (assetPropertyAction || node.property.temporary) {\n <div\n class=\"m-l-8 showOnHover d-flex a-i-center j-c-end asset-property-list__actions\"\n [ngClass]=\"{\n 'p-l-32': !node.property.temporary && config.allowAddingCustomProperties,\n 'has-computed': node.property.computed && node.property.config\n }\"\n >\n @if (node.property.computed && node.property.config) {\n <button\n class=\"btn btn-dot m-l-auto\"\n title=\"{{ 'Configure' | translate }}\"\n (click)=\"editProperty(node.property)\"\n >\n <i [c8yIcon]=\"'cog'\"></i>\n </button>\n }\n @if (node.property.temporary) {\n <button\n class=\"btn btn-dot btn-dot--danger\"\n [attr.aria-label]=\"'Remove' | translate\"\n tooltip=\"{{ 'Remove' | translate }}\"\n container=\"body\"\n type=\"button\"\n [ngClass]=\"{ 'm-l-auto': !node.property.computed || !node.property.config }\"\n [delay]=\"500\"\n (click)=\"removeProperty(node.property)\"\n >\n <i c8yIcon=\"minus-circle\"></i>\n </button>\n }\n <ng-container\n *ngTemplateOutlet=\"\n assetPropertyAction?.template;\n context: {\n $implicit: node.property\n }\n \"\n ></ng-container>\n </div>\n }\n </div>\n </cdk-tree-node>\n </cdk-tree>\n } @else {\n <c8y-ui-empty-state\n icon=\"list\"\n title=\"{{ 'No properties to display' | translate }}\"\n subtitle=\"{{ 'Select an asset to see the available properties.' | translate }}\"\n [horizontal]=\"true\"\n ></c8y-ui-empty-state>\n }\n @if (config.allowAddingCustomProperties) {\n <div class=\"sticky-bottom bg-inherit separator-top p-16\">\n <button\n class=\"btn btn-default btn-sm m-l-8\"\n (click)=\"addProperty()\"\n data-cy=\"asset-property-item-add-button\"\n >\n <i [c8yIcon]=\"'plus'\"></i>\n {{ 'Add property' | translate }}\n </button>\n </div>\n }\n</div>\n", dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.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: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ListGroupModule }, { kind: "component", type: i2.ListItemIconComponent, selector: "c8y-list-item-icon, c8y-li-icon", inputs: ["icon", "status"] }, { kind: "component", type: i2.ListItemCheckboxComponent, selector: "c8y-list-item-checkbox, c8y-li-checkbox", inputs: ["selected", "indeterminate", "disabled", "displayAsSwitch"], outputs: ["onSelect"] }, { kind: "component", type: i2.ListItemRadioComponent, selector: "c8y-list-item-radio, c8y-li-radio", inputs: ["selected", "name", "disabled", "value"], outputs: ["onSelect"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i3.TooltipDirective, selector: "[tooltip], [tooltipHtml]", inputs: ["adaptivePosition", "tooltip", "placement", "triggers", "container", "containerClass", "boundariesElement", "isOpen", "isDisabled", "delay", "tooltipHtml", "tooltipPlacement", "tooltipIsOpen", "tooltipEnable", "tooltipAppendToBody", "tooltipAnimation", "tooltipClass", "tooltipContext", "tooltipPopupDelay", "tooltipFadeDuration", "tooltipTrigger"], outputs: ["tooltipChange", "onShown", "onHidden", "tooltipStateChanged"], exportAs: ["bs-tooltip"] }, { kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "ngmodule", type: CdkTreeModule }, { kind: "directive", type: i4.CdkTreeNodeDef, selector: "[cdkTreeNodeDef]", inputs: ["cdkTreeNodeDefWhen"] }, { kind: "directive", type: i4.CdkTreeNodePadding, selector: "[cdkTreeNodePadding]", inputs: ["cdkTreeNodePadding", "cdkTreeNodePaddingIndent"] }, { kind: "component", type: i4.CdkTree, selector: "cdk-tree", inputs: ["dataSource", "treeControl", "levelAccessor", "childrenAccessor", "trackBy", "expansionKey"], exportAs: ["cdkTree"] }, { kind: "directive", type: i4.CdkTreeNode, selector: "cdk-tree-node", inputs: ["role", "isExpandable", "isExpanded", "isDisabled", "cdkTreeNodeTypeaheadLabel"], outputs: ["activation", "expandedChange"], exportAs: ["cdkTreeNode"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: EmptyStateComponent, selector: "c8y-ui-empty-state", inputs: ["icon", "title", "subtitle", "horizontal"] }, { kind: "directive", type: CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: AssetPropertyValuePipe, name: "c8yAssetPropertyValue" }, { kind: "pipe", type: AssetPropertyIconPipe, name: "c8yAssetPropertyIcon" }, { kind: "pipe", type: AssetPropertyIconTooltipPipe, name: "c8yAssetPropertyIconTooltip" }, { kind: "pipe", type: AssetPropertyCollapseButtonTitlePipe, name: "c8yAssetPropertyCollapseButtonTitle" }] }); }
|
|
2587
2598
|
}
|
|
2588
2599
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AssetPropertyListComponent, decorators: [{
|
|
2589
2600
|
type: Component,
|
|
@@ -2605,7 +2616,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
|
|
|
2605
2616
|
AssetPropertyCollapseButtonTitlePipe,
|
|
2606
2617
|
CdkDropList,
|
|
2607
2618
|
CdkDrag
|
|
2608
|
-
], template: "<div\n class=\"tree-container bg-inherit\"\n [attr.aria-label]=\"'Asset property list' | translate\"\n role=\"tree\"\n>\n @if (config.showHeader || config.filterable) {\n <div\n class=\"select-all-container bg-inherit sticky-top\"\n [ngClass]=\"{\n 'separator-bottom': config.filterable || (config.showHeader && dataSource.data.length)\n }\"\n >\n @if (config.filterable) {\n <div\n class=\"form-group m-b-0 p-16 d-flex\"\n [ngClass]=\"{ 'separator-bottom': config.showHeader }\"\n >\n <div class=\"input-group input-group-search\">\n <input\n class=\"form-control\"\n [attr.aria-label]=\"'Filter properties' | translate\"\n placeholder=\"{{ 'Filter properties' | translate }}\"\n role=\"searchbox\"\n type=\"search\"\n autocomplete=\"off\"\n [(ngModel)]=\"filterText\"\n (input)=\"onFilter()\"\n [disabled]=\"!dataSource.data.length\"\n #filter\n />\n @if (filter.value.length === 0) {\n <span\n class=\"input-group-addon\"\n [attr.aria-hidden]=\"true\"\n >\n <i\n class=\"dlt-c8y-icon-search\"\n [c8yIcon]=\"'search'\"\n ></i>\n </span>\n }\n @if (filter.value.length > 0) {\n <span\n class=\"input-group-addon\"\n [attr.aria-hidden]=\"true\"\n >\n <i\n class=\"text-muted dlt-c8y-icon-times\"\n [c8yIcon]=\"'times'\"\n [attr.aria-label]=\"'Clear filter' | translate\"\n tabindex=\"0\"\n role=\"button\"\n (click)=\"clearFilter()\"\n ></i>\n </span>\n }\n </div>\n </div>\n }\n @if (config.showHeader && dataSource.data.length) {\n <div class=\"d-flex a-i-center overflow-hidden\">\n <div\n class=\"flex-no-shrink d-flex a-i-center\"\n [ngClass]=\"{\n 'p-l-40': !hasExpandableNodes && config.expansionMode !== 'nonCollapsible',\n 'p-r-8': config.selectMode !== 'none' && config.expansionMode === 'nonCollapsible'\n }\"\n >\n @if (config.expansionMode !== 'nonCollapsible' && hasExpandableNodes) {\n @let collapseAllButtonTitle =\n (areAllNodesExpanded ? collapseAllLabel : expandAllLabel) | translate;\n <button\n class=\"collapse-btn btn-dot flex-no-shrink\"\n [title]=\"collapseAllButtonTitle\"\n [attr.aria-label]=\"collapseAllButtonTitle\"\n type=\"button\"\n [ngClass]=\"{ active: areAllNodesExpanded }\"\n (click)=\"toggleExpandCollapseAll()\"\n >\n <i [c8yIcon]=\"'chevron-right'\"></i>\n </button>\n }\n </div>\n @if (config.selectMode === 'multi') {\n <c8y-list-item-checkbox\n class=\"p-l-4 p-r-4\"\n [ngModel]=\"allSelected\"\n [indeterminate]=\"indeterminate\"\n (onSelect)=\"selectAll($event)\"\n ></c8y-list-item-checkbox>\n }\n <div\n class=\"p-t-40\"\n [ngClass]=\"{\n 'p-l-24': config.selectMode === 'single',\n 'p-l-8': config.selectMode === 'none' && config.expansionMode === 'nonCollapsible'\n }\"\n ></div>\n <div class=\"content-flex-30 fit-w m-t-4 m-b-4 m-0\">\n <div class=\"col-6 d-flex a-i-center m-l-0 m-r-0\">\n <span class=\"flex-no-shrink p-l-4 p-r-8\">\n <div class=\"c8y-icon\"></div>\n </span>\n <span class=\"text-medium m-l-8\">\n {{ 'Property' | translate }}\n </span>\n </div>\n @if (config.showKey) {\n <div [ngClass]=\"{ 'col-3': config.showValue, 'col-6': !config.showValue }\">\n <span class=\"text-medium\">{{ 'Key' | translate }}</span>\n </div>\n }\n @if (config.showValue) {\n <div [ngClass]=\"{ 'col-3': config.showKey, 'col-6': !config.showKey }\">\n <span class=\"text-medium\">{{ 'Value' | translate }}</span>\n </div>\n }\n </div>\n @if (assetPropertyAction) {\n <div\n class=\"m-l-8 p-l-24 asset-property-list__actions\"\n [ngClass]=\"{\n 'p-r-40': config.allowAddingCustomProperties,\n 'p-r-8': !config.allowAddingCustomProperties\n }\"\n ></div>\n }\n </div>\n }\n </div>\n }\n\n @if (dataSource.data.length) {\n <cdk-tree\n role=\"presentation\"\n #tree\n [dataSource]=\"dataSource\"\n [levelAccessor]=\"getLevel\"\n cdkDropList\n cdkDropListOrientation=\"vertical\"\n (cdkDropListDropped)=\"drop($event)\"\n [cdkDropListDisabled]=\"!config.allowDragAndDrop || dataSource.data.length < 2\"\n >\n <!-- Tree Node Definition -->\n <cdk-tree-node\n class=\"c8y-list__item--dense\"\n [attr.tabindex]=\"0\"\n [attr.aria-level]=\"getLevel(node) + 1\"\n [attr.aria-expanded]=\"hasChild(node) ? isNodeExpanded(node) : null\"\n [attr.aria-selected]=\"config.selectMode !== 'none' ? node.property.active : null\"\n role=\"treeitem\"\n *cdkTreeNodeDef=\"let node\"\n cdkTreeNodePadding\n [cdkTreeNodePaddingIndent]=\"TREE_NODE_INDENT\"\n [ngClass]=\"{\n nonCollapsible: config.expansionMode === 'nonCollapsible',\n nonSelectable: config.selectMode === 'none'\n }\"\n cdkDrag\n cdkDragLockAxis=\"y\"\n [cdkDragDisabled]=\"cdkDragDisabled\"\n >\n <div class=\"d-flex p-relative overflow-visible bg-inherit fit-h\">\n @if (config.allowDragAndDrop && dataSource.data.length > 1) {\n <div\n class=\"drag-handle-wrapper\"\n [ngClass]=\"{ 'drag-handle-disabled': cdkDragDisabled }\"\n >\n <button\n class=\"drag-handle btn-clean\"\n [title]=\"\n (cdkDragDisabled ? dragHandleDisabledLabel : dragHandleEnabledLabel) | translate\n \"\n [attr.aria-label]=\"\n (cdkDragDisabled ? dragHandleDisabledLabel : dragHandleEnabledLabel) | translate\n \"\n tabindex=\"-1\"\n type=\"button\"\n [disabled]=\"cdkDragDisabled\"\n >\n <i [c8yIcon]=\"'drag-reorder'\"></i>\n </button>\n </div>\n }\n <!-- Toggle Button for expandable nodes -->\n @if (config.expansionMode !== 'nonCollapsible' && hasChild(node)) {\n @let collapseButtonTitle =\n isNodeExpanded(node) | c8yAssetPropertyCollapseButtonTitle: node;\n <button\n class=\"collapse-btn btn-dot flex-no-shrink\"\n [title]=\"collapseButtonTitle\"\n [attr.aria-label]=\"collapseButtonTitle\"\n [attr.aria-expanded]=\"isNodeExpanded(node)\"\n type=\"button\"\n (click)=\"toggleNode(node)\"\n [ngClass]=\"{ active: isNodeExpanded(node) }\"\n >\n <i [c8yIcon]=\"'chevron-right'\"></i>\n </button>\n }\n <!-- Placeholder for non-expandable nodes to maintain alignment -->\n @if (!hasChild(node) || config.expansionMode === 'nonCollapsible') {\n <div\n class=\"flex-no-shrink\"\n [ngClass]=\"{\n 'p-r-40': config.expansionMode !== 'nonCollapsible',\n 'p-r-8': config.expansionMode === 'nonCollapsible'\n }\"\n ></div>\n }\n <!-- Selection Controls -->\n @if (config.selectMode !== 'none') {\n <div class=\"d-contents\">\n @if (config.selectMode === 'single') {\n <c8y-list-item-radio\n class=\"p-l-4 p-r-4\"\n type=\"radio\"\n [selected]=\"node.property.active\"\n (onSelect)=\"onSelectSingle($event, node)\"\n ></c8y-list-item-radio>\n } @else if (config.selectMode === 'multi') {\n <c8y-list-item-checkbox\n class=\"p-l-4 p-r-4\"\n [ngModel]=\"node.property.active\"\n [indeterminate]=\"node.indeterminate\"\n (onSelect)=\"onSelectMulti($event, node)\"\n ></c8y-list-item-checkbox>\n }\n </div>\n }\n <div class=\"content-flex-30 fit-w bg-inherit m-0 min-width-0\">\n <div\n class=\"d-flex a-i-center bg-inherit m-0 flex-grow\"\n [style.max-width]=\"getColumnMaxWidth(node)\"\n >\n <c8y-li-icon\n class=\"p-r-4 p-l-4\"\n [icon]=\"node.property | c8yAssetPropertyIcon\"\n tooltip=\"{{ node.property | c8yAssetPropertyIconTooltip }}\"\n container=\"body\"\n [delay]=\"500\"\n [ngClass]=\"{\n 'p-l-4': config.selectMode !== 'none',\n 'p-l-8': config.selectMode === 'none'\n }\"\n ></c8y-li-icon>\n\n <span class=\"p-r-8 min-width-0 d-flex a-i-center\">\n @if (isContextMismatch(node)) {\n @let tooltipText =\n 'Property configured for a different asset. Reconfigure to match current asset.'\n | translate;\n <button\n class=\"btn-clean m-r-4\"\n [attr.aria-label]=\"tooltipText\"\n [tooltip]=\"tooltipText\"\n placement=\"top\"\n type=\"button\"\n [container]=\"'body'\"\n >\n <i\n class=\"status critical stroked-icon\"\n c8yIcon=\"exclamation-circle\"\n ></i>\n </button>\n }\n\n <div\n class=\"text-truncate\"\n title=\"{{\n node.property.label || node.property.title || node.property.name | translate\n }}\"\n [ngClass]=\"{\n 'text-muted': node.property.temporary,\n 'text-danger': isContextMismatch(node)\n }\"\n >\n {{ node.property.label || node.property.title || node.property.name | translate }}\n </div>\n </span>\n </div>\n @if (config.showKey) {\n <div\n class=\"d-flex a-i-center\"\n [ngClass]=\"{ 'col-3': config.showValue, 'col-6': !config.showValue }\"\n >\n <span\n class=\"d-inline-block tag tag--default a-s-center text-truncate\"\n title=\"{{\n node.property.keyPath?.join('.') || (node.property?.name | translate)\n }}\"\n >\n {{ node.property.keyPath?.join('.') || (node.property?.name | translate) }}\n </span>\n </div>\n }\n @if (asset && config.showValue) {\n <div\n class=\"d-flex a-i-center\"\n [ngClass]=\"{ 'col-3': config.showKey, 'col-6': !config.showKey }\"\n >\n @let value = node.property | c8yAssetPropertyValue: asset | async;\n <span\n class=\"tag tag--info d-inline-block a-s-center text-truncate\"\n title=\"{{ value }}\"\n >\n {{ value }}\n </span>\n </div>\n }\n </div>\n @if (assetPropertyAction || node.property.temporary) {\n <div\n class=\"m-l-8 showOnHover d-flex a-i-center j-c-end asset-property-list__actions\"\n [ngClass]=\"{\n 'p-l-32': !node.property.temporary && config.allowAddingCustomProperties,\n 'has-computed': node.property.computed && node.property.config\n }\"\n >\n @if (node.property.computed && node.property.config) {\n <button\n class=\"btn btn-dot m-l-auto\"\n title=\"{{ 'Configure' | translate }}\"\n (click)=\"editProperty(node.property)\"\n >\n <i [c8yIcon]=\"'cog'\"></i>\n </button>\n }\n @if (node.property.temporary) {\n <button\n class=\"btn btn-dot btn-dot--danger\"\n [attr.aria-label]=\"'Remove' | translate\"\n tooltip=\"{{ 'Remove' | translate }}\"\n container=\"body\"\n type=\"button\"\n [ngClass]=\"{ 'm-l-auto': !node.property.computed || !node.property.config }\"\n [delay]=\"500\"\n (click)=\"removeProperty(node.property)\"\n >\n <i c8yIcon=\"minus-circle\"></i>\n </button>\n }\n <ng-container\n *ngTemplateOutlet=\"\n assetPropertyAction?.template;\n context: {\n $implicit: node.property\n }\n \"\n ></ng-container>\n </div>\n }\n </div>\n </cdk-tree-node>\n </cdk-tree>\n } @else {\n <c8y-ui-empty-state\n icon=\"list\"\n title=\"{{ 'No properties to display' | translate }}\"\n subtitle=\"{{ 'Select an asset to see the available properties.' | translate }}\"\n [horizontal]=\"true\"\n ></c8y-ui-empty-state>\n }\n @if (config.allowAddingCustomProperties) {\n <div class=\"sticky-bottom bg-inherit separator-top p-16\">\n <button\n class=\"btn btn-default btn-sm m-l-8\"\n (click)=\"addProperty()\"\n data-cy=\"asset-property-item-add-button\"\n >\n <i [c8yIcon]=\"'plus'\"></i>\n {{ 'Add property' | translate }}\n </button>\n </div>\n }\n</div>\n" }]
|
|
2619
|
+
], template: "<div\n class=\"tree-container bg-inherit\"\n [attr.aria-label]=\"'Asset property list' | translate\"\n role=\"tree\"\n>\n @if (config.showHeader || config.filterable) {\n <div\n class=\"select-all-container bg-inherit sticky-top\"\n [ngClass]=\"{\n 'separator-bottom': config.filterable || (config.showHeader && dataSource.data.length)\n }\"\n >\n @if (config.filterable) {\n <div\n class=\"form-group m-b-0 p-16 d-flex\"\n [ngClass]=\"{ 'separator-bottom': config.showHeader }\"\n >\n <div class=\"input-group input-group-search\">\n <input\n class=\"form-control\"\n [attr.aria-label]=\"'Filter properties' | translate\"\n placeholder=\"{{ 'Filter properties' | translate }}\"\n role=\"searchbox\"\n type=\"search\"\n autocomplete=\"off\"\n [(ngModel)]=\"filterText\"\n (input)=\"onFilter()\"\n [disabled]=\"!dataSource.data.length\"\n #filter\n />\n @if (filter.value.length === 0) {\n <span\n class=\"input-group-addon\"\n [attr.aria-hidden]=\"true\"\n >\n <i\n class=\"dlt-c8y-icon-search\"\n [c8yIcon]=\"'search'\"\n ></i>\n </span>\n }\n @if (filter.value.length > 0) {\n <span\n class=\"input-group-addon\"\n [attr.aria-hidden]=\"true\"\n >\n <i\n class=\"text-muted dlt-c8y-icon-times\"\n [c8yIcon]=\"'times'\"\n [attr.aria-label]=\"'Clear filter' | translate\"\n tabindex=\"0\"\n role=\"button\"\n (click)=\"clearFilter()\"\n ></i>\n </span>\n }\n </div>\n </div>\n }\n @if (config.showHeader && dataSource.data.length) {\n <div class=\"d-flex a-i-center overflow-hidden\">\n <div\n class=\"flex-no-shrink d-flex a-i-center\"\n [ngClass]=\"{\n 'p-l-40': !hasExpandableNodes && config.expansionMode !== 'nonCollapsible',\n 'p-r-8': config.selectMode !== 'none' && config.expansionMode === 'nonCollapsible'\n }\"\n >\n @if (config.expansionMode !== 'nonCollapsible' && hasExpandableNodes) {\n @let collapseAllButtonTitle =\n (areAllNodesExpanded ? collapseAllLabel : expandAllLabel) | translate;\n <button\n class=\"collapse-btn btn-dot flex-no-shrink\"\n [title]=\"collapseAllButtonTitle\"\n [attr.aria-label]=\"collapseAllButtonTitle\"\n type=\"button\"\n [ngClass]=\"{ active: areAllNodesExpanded }\"\n (click)=\"toggleExpandCollapseAll()\"\n >\n <i [c8yIcon]=\"'chevron-right'\"></i>\n </button>\n }\n </div>\n @if (config.selectMode === 'multi') {\n <c8y-list-item-checkbox\n class=\"p-l-4 p-r-4\"\n [ngModel]=\"allSelected\"\n [indeterminate]=\"indeterminate\"\n (onSelect)=\"selectAll($event)\"\n ></c8y-list-item-checkbox>\n }\n <div\n class=\"p-t-40\"\n [ngClass]=\"{\n 'p-l-24': config.selectMode === 'single',\n 'p-l-8': config.selectMode === 'none' && config.expansionMode === 'nonCollapsible'\n }\"\n ></div>\n <div class=\"content-flex-30 fit-w m-t-4 m-b-4 m-0\">\n <div class=\"col-6 d-flex a-i-center m-l-0 m-r-0\">\n <span class=\"flex-no-shrink p-l-4 p-r-8\">\n <div class=\"c8y-icon\"></div>\n </span>\n <span class=\"text-medium m-l-8\">\n {{ 'Property' | translate }}\n </span>\n </div>\n @if (config.showKey) {\n <div [ngClass]=\"{ 'col-3': config.showValue, 'col-6': !config.showValue }\">\n <span class=\"text-medium\">{{ 'Key' | translate }}</span>\n </div>\n }\n @if (config.showValue) {\n <div [ngClass]=\"{ 'col-3': config.showKey, 'col-6': !config.showKey }\">\n <span class=\"text-medium\">{{ 'Value' | translate }}</span>\n </div>\n }\n </div>\n @if (assetPropertyAction) {\n <div\n class=\"m-l-8 p-l-24 asset-property-list__actions\"\n [ngClass]=\"{\n 'p-r-40': config.allowAddingCustomProperties,\n 'p-r-8': !config.allowAddingCustomProperties\n }\"\n ></div>\n }\n </div>\n }\n </div>\n }\n\n @if (dataSource.data.length) {\n <cdk-tree\n role=\"presentation\"\n #tree\n [dataSource]=\"dataSource\"\n [levelAccessor]=\"getLevel\"\n cdkDropList\n cdkDropListOrientation=\"vertical\"\n (cdkDropListDropped)=\"drop($event)\"\n [cdkDropListDisabled]=\"!config.allowDragAndDrop || dataSource.data.length < 2\"\n >\n <!-- Tree Node Definition -->\n <cdk-tree-node\n class=\"c8y-list__item--dense\"\n [attr.tabindex]=\"0\"\n [attr.aria-level]=\"getLevel(node) + 1\"\n [attr.aria-expanded]=\"hasChild(node) ? isNodeExpanded(node) : null\"\n [attr.aria-selected]=\"config.selectMode !== 'none' ? node.property.active : null\"\n role=\"treeitem\"\n *cdkTreeNodeDef=\"let node\"\n cdkTreeNodePadding\n [cdkTreeNodePaddingIndent]=\"TREE_NODE_INDENT\"\n [ngClass]=\"{\n nonCollapsible: config.expansionMode === 'nonCollapsible',\n nonSelectable: config.selectMode === 'none'\n }\"\n cdkDrag\n cdkDragLockAxis=\"y\"\n [cdkDragDisabled]=\"cdkDragDisabled\"\n >\n <div class=\"d-flex p-relative overflow-visible bg-inherit fit-h\">\n @if (config.allowDragAndDrop && dataSource.data.length > 1) {\n <div\n class=\"drag-handle-wrapper\"\n [ngClass]=\"{ 'drag-handle-disabled': cdkDragDisabled }\"\n >\n <button\n class=\"drag-handle btn-clean\"\n [title]=\"\n (cdkDragDisabled ? dragHandleDisabledLabel : dragHandleEnabledLabel) | translate\n \"\n [attr.aria-label]=\"\n (cdkDragDisabled ? dragHandleDisabledLabel : dragHandleEnabledLabel) | translate\n \"\n tabindex=\"-1\"\n type=\"button\"\n [disabled]=\"cdkDragDisabled\"\n >\n <i [c8yIcon]=\"'drag-reorder'\"></i>\n </button>\n </div>\n }\n <!-- Toggle Button for expandable nodes -->\n @if (config.expansionMode !== 'nonCollapsible' && hasChild(node)) {\n @let collapseButtonTitle =\n isNodeExpanded(node) | c8yAssetPropertyCollapseButtonTitle: node;\n <button\n class=\"collapse-btn btn-dot flex-no-shrink\"\n [title]=\"collapseButtonTitle\"\n [attr.aria-label]=\"collapseButtonTitle\"\n [attr.aria-expanded]=\"isNodeExpanded(node)\"\n type=\"button\"\n (click)=\"toggleNode(node)\"\n [ngClass]=\"{ active: isNodeExpanded(node) }\"\n >\n <i [c8yIcon]=\"'chevron-right'\"></i>\n </button>\n }\n <!-- Placeholder for non-expandable nodes to maintain alignment -->\n @if (!hasChild(node) || config.expansionMode === 'nonCollapsible') {\n <div\n class=\"flex-no-shrink\"\n [ngClass]=\"{\n 'p-r-40': config.expansionMode !== 'nonCollapsible',\n 'p-r-8': config.expansionMode === 'nonCollapsible'\n }\"\n ></div>\n }\n <!-- Selection Controls -->\n @if (config.selectMode !== 'none') {\n <div class=\"d-contents\">\n @if (config.selectMode === 'single') {\n <c8y-list-item-radio\n class=\"p-l-4 p-r-4\"\n type=\"radio\"\n [selected]=\"node.property.active\"\n (onSelect)=\"onSelectSingle($event, node)\"\n ></c8y-list-item-radio>\n } @else if (config.selectMode === 'multi') {\n <c8y-list-item-checkbox\n class=\"p-l-4 p-r-4\"\n [ngModel]=\"node.property.active\"\n [indeterminate]=\"node.indeterminate\"\n (onSelect)=\"onSelectMulti($event, node)\"\n ></c8y-list-item-checkbox>\n } @else if (config.selectMode === 'plus') {\n @if (node.property.active) {\n <button\n class=\"btn btn-dot btn-dot--danger m-l-4 m-r-4 p-relative a-s-center\"\n style=\"z-index: 5\"\n [attr.aria-label]=\"'Remove' | translate\"\n [tooltip]=\"'Remove from selected properties' | translate\"\n type=\"button\"\n [delay]=\"500\"\n (click)=\"onSelectMulti(false, node)\"\n >\n <i\n class=\"icon-20\"\n c8yIcon=\"minus-circle\"\n ></i>\n </button>\n }\n @if (!node.property.active) {\n <button\n class=\"btn btn-dot text-primary m-l-4 m-r-4 p-relative a-s-center\"\n style=\"z-index: 5\"\n [attr.aria-label]=\"'Add to selected properties' | translate\"\n [tooltip]=\"\n (node.property.config\n ? 'Add to selected properties (requires configuration)'\n : 'Add to selected properties'\n ) | translate\n \"\n container=\"body\"\n type=\"button\"\n [delay]=\"500\"\n (click)=\"onSelectMulti(true, node)\"\n >\n <i\n class=\"text-primary icon-20\"\n [c8yIcon]=\"node.property.config ? 'plus-circle-o' : 'plus-circle'\"\n ></i>\n </button>\n }\n }\n </div>\n }\n <div class=\"content-flex-30 fit-w bg-inherit m-0 min-width-0\">\n <div\n class=\"d-flex a-i-center bg-inherit m-0 flex-grow\"\n [style.max-width]=\"getColumnMaxWidth(node)\"\n >\n <c8y-li-icon\n class=\"p-r-4 p-l-4\"\n [icon]=\"node.property | c8yAssetPropertyIcon\"\n tooltip=\"{{ node.property | c8yAssetPropertyIconTooltip }}\"\n container=\"body\"\n [delay]=\"500\"\n [ngClass]=\"{\n 'p-l-4': config.selectMode !== 'none',\n 'p-l-8': config.selectMode === 'none'\n }\"\n ></c8y-li-icon>\n\n <span class=\"p-r-8 min-width-0 d-flex a-i-center\">\n @if (isContextMismatch(node)) {\n @let tooltipText =\n 'Property configured for a different asset. Reconfigure to match current asset.'\n | translate;\n <button\n class=\"btn-clean m-r-4\"\n [attr.aria-label]=\"tooltipText\"\n [tooltip]=\"tooltipText\"\n placement=\"top\"\n type=\"button\"\n [container]=\"'body'\"\n >\n <i\n class=\"status critical stroked-icon\"\n c8yIcon=\"exclamation-circle\"\n ></i>\n </button>\n }\n\n <div\n class=\"text-truncate\"\n title=\"{{\n node.property.label || node.property.title || node.property.name | translate\n }}\"\n [ngClass]=\"{\n 'text-muted': node.property.temporary,\n 'text-danger': isContextMismatch(node)\n }\"\n >\n {{ node.property.label || node.property.title || node.property.name | translate }}\n </div>\n </span>\n </div>\n @if (config.showKey) {\n <div\n class=\"d-flex a-i-center\"\n [ngClass]=\"{ 'col-3': config.showValue, 'col-6': !config.showValue }\"\n >\n <span\n class=\"d-inline-block tag tag--default a-s-center text-truncate\"\n title=\"{{\n node.property.keyPath?.join('.') || (node.property?.name | translate)\n }}\"\n >\n {{ node.property.keyPath?.join('.') || (node.property?.name | translate) }}\n </span>\n </div>\n }\n @if (asset && config.showValue) {\n <div\n class=\"d-flex a-i-center\"\n [ngClass]=\"{ 'col-3': config.showKey, 'col-6': !config.showKey }\"\n >\n @let value = node.property | c8yAssetPropertyValue: asset | async;\n <span\n class=\"tag tag--info d-inline-block a-s-center text-truncate\"\n title=\"{{ value }}\"\n >\n {{ value }}\n </span>\n </div>\n }\n </div>\n @if (assetPropertyAction || node.property.temporary) {\n <div\n class=\"m-l-8 showOnHover d-flex a-i-center j-c-end asset-property-list__actions\"\n [ngClass]=\"{\n 'p-l-32': !node.property.temporary && config.allowAddingCustomProperties,\n 'has-computed': node.property.computed && node.property.config\n }\"\n >\n @if (node.property.computed && node.property.config) {\n <button\n class=\"btn btn-dot m-l-auto\"\n title=\"{{ 'Configure' | translate }}\"\n (click)=\"editProperty(node.property)\"\n >\n <i [c8yIcon]=\"'cog'\"></i>\n </button>\n }\n @if (node.property.temporary) {\n <button\n class=\"btn btn-dot btn-dot--danger\"\n [attr.aria-label]=\"'Remove' | translate\"\n tooltip=\"{{ 'Remove' | translate }}\"\n container=\"body\"\n type=\"button\"\n [ngClass]=\"{ 'm-l-auto': !node.property.computed || !node.property.config }\"\n [delay]=\"500\"\n (click)=\"removeProperty(node.property)\"\n >\n <i c8yIcon=\"minus-circle\"></i>\n </button>\n }\n <ng-container\n *ngTemplateOutlet=\"\n assetPropertyAction?.template;\n context: {\n $implicit: node.property\n }\n \"\n ></ng-container>\n </div>\n }\n </div>\n </cdk-tree-node>\n </cdk-tree>\n } @else {\n <c8y-ui-empty-state\n icon=\"list\"\n title=\"{{ 'No properties to display' | translate }}\"\n subtitle=\"{{ 'Select an asset to see the available properties.' | translate }}\"\n [horizontal]=\"true\"\n ></c8y-ui-empty-state>\n }\n @if (config.allowAddingCustomProperties) {\n <div class=\"sticky-bottom bg-inherit separator-top p-16\">\n <button\n class=\"btn btn-default btn-sm m-l-8\"\n (click)=\"addProperty()\"\n data-cy=\"asset-property-item-add-button\"\n >\n <i [c8yIcon]=\"'plus'\"></i>\n {{ 'Add property' | translate }}\n </button>\n </div>\n }\n</div>\n" }]
|
|
2609
2620
|
}], ctorParameters: () => [], propDecorators: { tree: [{ type: i0.ViewChild, args: ['tree', { isSignal: true }] }], config: [{
|
|
2610
2621
|
type: Input
|
|
2611
2622
|
}], asset: [{
|
|
@@ -2620,14 +2631,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
|
|
|
2620
2631
|
}] } });
|
|
2621
2632
|
|
|
2622
2633
|
/**
|
|
2623
|
-
* Represents a
|
|
2634
|
+
* Represents a reusable component for displaying and managing library properties (regular and computed).
|
|
2635
|
+
* This component handles the search, tabs, and property list display logic.
|
|
2624
2636
|
*/
|
|
2625
|
-
class
|
|
2637
|
+
class AssetPropertyTabsComponent {
|
|
2626
2638
|
constructor() {
|
|
2627
|
-
/**
|
|
2628
|
-
* Title of the drawer.
|
|
2629
|
-
*/
|
|
2630
|
-
this.title = gettext('Select property');
|
|
2631
2639
|
/**
|
|
2632
2640
|
* Configuration for the asset property list.
|
|
2633
2641
|
*/
|
|
@@ -2641,48 +2649,50 @@ class CustomPropertiesDrawerComponent {
|
|
|
2641
2649
|
inputPropertiesHandle: 'override'
|
|
2642
2650
|
};
|
|
2643
2651
|
/**
|
|
2644
|
-
*
|
|
2652
|
+
* Currently selected properties to be highlighted in the list.
|
|
2645
2653
|
*/
|
|
2646
|
-
this.
|
|
2654
|
+
this.selectedProperties = [];
|
|
2647
2655
|
/**
|
|
2648
|
-
*
|
|
2656
|
+
* Controls which tabs are hidden.
|
|
2649
2657
|
*/
|
|
2650
|
-
this.
|
|
2658
|
+
this.hiddenTabs = {};
|
|
2651
2659
|
/**
|
|
2652
|
-
*
|
|
2660
|
+
* The name of the tabs outlet.
|
|
2653
2661
|
*/
|
|
2654
|
-
this.
|
|
2662
|
+
this.tabsOutletName = 'libraryPropertiesTabs';
|
|
2655
2663
|
/**
|
|
2656
|
-
*
|
|
2664
|
+
* Whether to show the search input.
|
|
2657
2665
|
*/
|
|
2658
|
-
this.
|
|
2666
|
+
this.showSearch = true;
|
|
2659
2667
|
/**
|
|
2660
|
-
*
|
|
2668
|
+
* Emits when the selected properties change.
|
|
2661
2669
|
*/
|
|
2662
|
-
this.
|
|
2663
|
-
this._save = resolve;
|
|
2664
|
-
this._cancel = reject;
|
|
2665
|
-
});
|
|
2670
|
+
this.selectedPropertiesChange = new EventEmitter();
|
|
2666
2671
|
this.tabNames = {
|
|
2667
|
-
|
|
2672
|
+
asset: gettext('Asset properties'),
|
|
2673
|
+
regular: gettext('Custom properties'),
|
|
2668
2674
|
computed: gettext('Computed properties')
|
|
2669
2675
|
};
|
|
2670
2676
|
this.inputText = '';
|
|
2671
|
-
this.selectedTab = '
|
|
2677
|
+
this.selectedTab = 'asset';
|
|
2678
|
+
this.assetProperties = [];
|
|
2672
2679
|
this.regularProperties = [];
|
|
2673
2680
|
this.computedProperties = [];
|
|
2674
2681
|
this.displayedProperties = [];
|
|
2682
|
+
this.assetCount = 0;
|
|
2675
2683
|
this.regularCount = 0;
|
|
2676
2684
|
this.computedCount = 0;
|
|
2677
2685
|
this.allComputedProperties = [];
|
|
2678
|
-
this.
|
|
2679
|
-
this.searchSubject$ = new BehaviorSubject('');
|
|
2680
|
-
/**
|
|
2681
|
-
* Subject for handling component destruction.
|
|
2682
|
-
*/
|
|
2686
|
+
this.searchSubject$ = new Subject();
|
|
2683
2687
|
this.destroy$ = new Subject();
|
|
2684
2688
|
this.assetPropertiesService = inject(AssetPropertiesService);
|
|
2685
2689
|
this.computedPropertiesService = inject(ComputedPropertiesService);
|
|
2690
|
+
this.selectedAssetProperties = [];
|
|
2691
|
+
this.selectedRegularProperties = [];
|
|
2692
|
+
this.selectedComputedProperties = [];
|
|
2693
|
+
}
|
|
2694
|
+
get showAsset() {
|
|
2695
|
+
return !this.hiddenTabs.asset;
|
|
2686
2696
|
}
|
|
2687
2697
|
get showRegular() {
|
|
2688
2698
|
return !this.hiddenTabs.regular;
|
|
@@ -2691,21 +2701,47 @@ class CustomPropertiesDrawerComponent {
|
|
|
2691
2701
|
return !this.hiddenTabs.computed;
|
|
2692
2702
|
}
|
|
2693
2703
|
get showTabBar() {
|
|
2694
|
-
|
|
2704
|
+
const visibleTabs = [this.showAsset, this.showRegular, this.showComputed].filter(Boolean);
|
|
2705
|
+
return visibleTabs.length > 1;
|
|
2695
2706
|
}
|
|
2696
2707
|
async ngOnInit() {
|
|
2697
|
-
|
|
2708
|
+
this.config = { ...this.config, selectedProperties: this.selectedProperties };
|
|
2709
|
+
if (this.showAsset) {
|
|
2710
|
+
this.selectedTab = 'asset';
|
|
2711
|
+
}
|
|
2712
|
+
else if (this.showRegular) {
|
|
2713
|
+
this.selectedTab = 'regular';
|
|
2714
|
+
}
|
|
2715
|
+
else if (this.showComputed) {
|
|
2698
2716
|
this.selectedTab = 'computed';
|
|
2699
2717
|
}
|
|
2700
2718
|
if (this.showComputed) {
|
|
2701
|
-
|
|
2702
|
-
|
|
2719
|
+
this.loadComputedProperties();
|
|
2720
|
+
}
|
|
2721
|
+
if (this.showAsset && this.asset) {
|
|
2722
|
+
await this.loadAssetProperties();
|
|
2703
2723
|
}
|
|
2704
2724
|
await this.updateProperties();
|
|
2725
|
+
this.categorizeSelectedProperties();
|
|
2726
|
+
this.updateConfigWithCurrentTabSelections();
|
|
2705
2727
|
this.searchSubject$
|
|
2706
2728
|
.pipe(takeUntil(this.destroy$), debounceTime(350))
|
|
2707
2729
|
.subscribe(() => this.updateProperties());
|
|
2708
2730
|
}
|
|
2731
|
+
async ngOnChanges(changes) {
|
|
2732
|
+
this.config = { ...this.config, allowAddingCustomProperties: false };
|
|
2733
|
+
if (changes['selectedProperties']) {
|
|
2734
|
+
this.categorizeSelectedProperties();
|
|
2735
|
+
this.updateConfigWithCurrentTabSelections();
|
|
2736
|
+
}
|
|
2737
|
+
if (changes['asset'] && !changes['asset'].firstChange && this.showAsset) {
|
|
2738
|
+
await this.loadAssetProperties();
|
|
2739
|
+
await this.loadComputedProperties();
|
|
2740
|
+
this.updateProperties();
|
|
2741
|
+
this.categorizeSelectedProperties();
|
|
2742
|
+
this.updateDisplayedProperties();
|
|
2743
|
+
}
|
|
2744
|
+
}
|
|
2709
2745
|
ngOnDestroy() {
|
|
2710
2746
|
this.destroy$.next();
|
|
2711
2747
|
this.destroy$.complete();
|
|
@@ -2715,7 +2751,22 @@ class CustomPropertiesDrawerComponent {
|
|
|
2715
2751
|
* @param properties The selected properties.
|
|
2716
2752
|
*/
|
|
2717
2753
|
onSelectedProperties(properties) {
|
|
2718
|
-
|
|
2754
|
+
// Update the selected properties for the current tab
|
|
2755
|
+
if (this.selectedTab === 'asset') {
|
|
2756
|
+
this.selectedAssetProperties = cloneDeep(properties);
|
|
2757
|
+
}
|
|
2758
|
+
else if (this.selectedTab === 'regular') {
|
|
2759
|
+
this.selectedRegularProperties = cloneDeep(properties);
|
|
2760
|
+
}
|
|
2761
|
+
else {
|
|
2762
|
+
this.selectedComputedProperties = cloneDeep(properties);
|
|
2763
|
+
}
|
|
2764
|
+
const allSelected = [
|
|
2765
|
+
...this.selectedAssetProperties,
|
|
2766
|
+
...this.selectedRegularProperties,
|
|
2767
|
+
...this.selectedComputedProperties
|
|
2768
|
+
];
|
|
2769
|
+
this.selectedPropertiesChange.emit(cloneDeep(allSelected));
|
|
2719
2770
|
}
|
|
2720
2771
|
onTabChange(tabName) {
|
|
2721
2772
|
this.selectedTab = tabName;
|
|
@@ -2728,20 +2779,6 @@ class CustomPropertiesDrawerComponent {
|
|
|
2728
2779
|
this.inputText = '';
|
|
2729
2780
|
this.onSearch();
|
|
2730
2781
|
}
|
|
2731
|
-
/**
|
|
2732
|
-
* Saves the selected properties and closes the drawer.
|
|
2733
|
-
*/
|
|
2734
|
-
onSave() {
|
|
2735
|
-
this._save(this.selectedProperties);
|
|
2736
|
-
this.bottomDrawerRef.close();
|
|
2737
|
-
}
|
|
2738
|
-
/**
|
|
2739
|
-
* Cancels the selection and closes the drawer.
|
|
2740
|
-
*/
|
|
2741
|
-
onCancel() {
|
|
2742
|
-
this._cancel();
|
|
2743
|
-
this.bottomDrawerRef.close();
|
|
2744
|
-
}
|
|
2745
2782
|
async loadMore() {
|
|
2746
2783
|
if (this.selectedTab !== 'regular' || !this.paging) {
|
|
2747
2784
|
return;
|
|
@@ -2765,12 +2802,22 @@ class CustomPropertiesDrawerComponent {
|
|
|
2765
2802
|
this.displayedProperties = this.regularProperties;
|
|
2766
2803
|
}
|
|
2767
2804
|
}
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2805
|
+
async loadComputedProperties() {
|
|
2806
|
+
const computedPropsResult = await this.computedPropertiesService.getByContext(this.asset);
|
|
2807
|
+
this.allComputedProperties = computedPropsResult.map(({ prop }) => prop);
|
|
2808
|
+
}
|
|
2809
|
+
async loadAssetProperties() {
|
|
2810
|
+
if (!this.asset) {
|
|
2811
|
+
this.assetProperties = [];
|
|
2812
|
+
this.assetCount = 0;
|
|
2813
|
+
return;
|
|
2814
|
+
}
|
|
2815
|
+
const properties = await this.assetPropertiesService.getInitialProperties(this.asset);
|
|
2816
|
+
const categorized = this.categorizeAndFlattenHierarchicalProperties(properties);
|
|
2817
|
+
// Filter to apply search
|
|
2818
|
+
const filtered = categorized.filter(prop => prop.label.toLowerCase().includes(this.inputText?.trim().toLowerCase() || ''));
|
|
2819
|
+
this.assetProperties = filtered;
|
|
2820
|
+
this.assetCount = categorized.length;
|
|
2774
2821
|
}
|
|
2775
2822
|
async updateProperties() {
|
|
2776
2823
|
let propertiesFromLibrary = [];
|
|
@@ -2785,6 +2832,9 @@ class CustomPropertiesDrawerComponent {
|
|
|
2785
2832
|
if (this.showComputed) {
|
|
2786
2833
|
filteredComputedProperties = this.allComputedProperties.filter(prop => prop.label.toLowerCase().includes(this.inputText?.trim().toLowerCase() || ''));
|
|
2787
2834
|
}
|
|
2835
|
+
if (this.showAsset) {
|
|
2836
|
+
await this.loadAssetProperties();
|
|
2837
|
+
}
|
|
2788
2838
|
const allProperties = this.categorizeAndFlattenHierarchicalProperties([
|
|
2789
2839
|
...filteredComputedProperties,
|
|
2790
2840
|
...propertiesFromLibrary,
|
|
@@ -2797,8 +2847,73 @@ class CustomPropertiesDrawerComponent {
|
|
|
2797
2847
|
this.updateDisplayedProperties();
|
|
2798
2848
|
}
|
|
2799
2849
|
updateDisplayedProperties() {
|
|
2800
|
-
this.
|
|
2801
|
-
this.
|
|
2850
|
+
if (this.selectedTab === 'asset') {
|
|
2851
|
+
this.displayedProperties = this.assetProperties;
|
|
2852
|
+
}
|
|
2853
|
+
else if (this.selectedTab === 'regular') {
|
|
2854
|
+
this.displayedProperties = this.regularProperties;
|
|
2855
|
+
}
|
|
2856
|
+
else {
|
|
2857
|
+
this.displayedProperties = this.computedProperties;
|
|
2858
|
+
}
|
|
2859
|
+
this.updateConfigWithCurrentTabSelections();
|
|
2860
|
+
}
|
|
2861
|
+
/**
|
|
2862
|
+
* Updates the config to show only the current tab's selected properties.
|
|
2863
|
+
*/
|
|
2864
|
+
updateConfigWithCurrentTabSelections() {
|
|
2865
|
+
let currentTabSelections = [];
|
|
2866
|
+
if (this.selectedTab === 'asset') {
|
|
2867
|
+
currentTabSelections = this.selectedAssetProperties;
|
|
2868
|
+
}
|
|
2869
|
+
else if (this.selectedTab === 'regular') {
|
|
2870
|
+
currentTabSelections = this.selectedRegularProperties;
|
|
2871
|
+
}
|
|
2872
|
+
else {
|
|
2873
|
+
currentTabSelections = this.selectedComputedProperties;
|
|
2874
|
+
}
|
|
2875
|
+
this.config = { ...this.config, selectedProperties: currentTabSelections };
|
|
2876
|
+
}
|
|
2877
|
+
/**
|
|
2878
|
+
* Categorizes incoming selectedProperties by matching them against loaded properties.
|
|
2879
|
+
*/
|
|
2880
|
+
categorizeSelectedProperties() {
|
|
2881
|
+
if (!this.selectedProperties) {
|
|
2882
|
+
return;
|
|
2883
|
+
}
|
|
2884
|
+
const assetSelected = [];
|
|
2885
|
+
const regularSelected = [];
|
|
2886
|
+
const computedSelected = [];
|
|
2887
|
+
for (const selectedProp of this.selectedProperties) {
|
|
2888
|
+
// Check if it's in asset properties
|
|
2889
|
+
const inAsset = this.assetProperties.some(prop => this.assetPropertiesService.propertiesMatch(prop, selectedProp));
|
|
2890
|
+
if (inAsset) {
|
|
2891
|
+
assetSelected.push(selectedProp);
|
|
2892
|
+
continue;
|
|
2893
|
+
}
|
|
2894
|
+
const inComputed = this.computedProperties.some(prop => this.assetPropertiesService.propertiesMatch(prop, selectedProp));
|
|
2895
|
+
if (inComputed) {
|
|
2896
|
+
computedSelected.push(selectedProp);
|
|
2897
|
+
continue;
|
|
2898
|
+
}
|
|
2899
|
+
// Check if it's in regular properties
|
|
2900
|
+
const inRegular = this.regularProperties.some(prop => this.assetPropertiesService.propertiesMatch(prop, selectedProp));
|
|
2901
|
+
if (inRegular) {
|
|
2902
|
+
regularSelected.push(selectedProp);
|
|
2903
|
+
continue;
|
|
2904
|
+
}
|
|
2905
|
+
// If computed property but not yet loaded, check by computed flag
|
|
2906
|
+
if (selectedProp.computed) {
|
|
2907
|
+
computedSelected.push(selectedProp);
|
|
2908
|
+
}
|
|
2909
|
+
else {
|
|
2910
|
+
// Default to regular if can't determine
|
|
2911
|
+
regularSelected.push(selectedProp);
|
|
2912
|
+
}
|
|
2913
|
+
}
|
|
2914
|
+
this.selectedAssetProperties = assetSelected;
|
|
2915
|
+
this.selectedRegularProperties = regularSelected;
|
|
2916
|
+
this.selectedComputedProperties = computedSelected;
|
|
2802
2917
|
}
|
|
2803
2918
|
/**
|
|
2804
2919
|
* Categorizes and flattens hierarchical properties.
|
|
@@ -2809,12 +2924,12 @@ class CustomPropertiesDrawerComponent {
|
|
|
2809
2924
|
const { computed, simple, complex } = this.assetPropertiesService.categorizeAndFlattenHierarchicalProperties(properties);
|
|
2810
2925
|
return [...computed, ...simple, ...complex];
|
|
2811
2926
|
}
|
|
2812
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type:
|
|
2813
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type:
|
|
2927
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AssetPropertyTabsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2928
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: AssetPropertyTabsComponent, isStandalone: true, selector: "c8y-asset-property-tabs", inputs: { asset: "asset", config: "config", selectedProperties: "selectedProperties", hiddenTabs: "hiddenTabs", tabsOutletName: "tabsOutletName", showSearch: "showSearch" }, outputs: { selectedPropertiesChange: "selectedPropertiesChange" }, host: { classAttribute: "d-contents" }, usesOnChanges: true, ngImport: i0, template: "@if (showSearch) {\n <div class=\"form-group p-16 m-b-0 d-flex sticky-top bg-component\">\n <div class=\"input-group input-group-search\">\n <input\n class=\"form-control\"\n [attr.aria-label]=\"'Search properties' | translate\"\n placeholder=\"{{ 'Search properties' | translate }}\"\n role=\"searchbox\"\n type=\"search\"\n autocomplete=\"off\"\n [(ngModel)]=\"inputText\"\n (input)=\"onSearch()\"\n #filter\n />\n @if (filter.value.length === 0) {\n <span\n class=\"input-group-addon\"\n [attr.aria-hidden]=\"true\"\n >\n <i\n class=\"dlt-c8y-icon-search\"\n [c8yIcon]=\"'search'\"\n ></i>\n </span>\n }\n @if (filter.value.length > 0) {\n <span\n class=\"input-group-addon\"\n [attr.aria-hidden]=\"true\"\n >\n <i\n class=\"text-muted dlt-c8y-icon-times\"\n [c8yIcon]=\"'times'\"\n [attr.aria-label]=\"'Clear search' | translate\"\n tabindex=\"0\"\n role=\"button\"\n (click)=\"clearSearch()\"\n ></i>\n </span>\n }\n </div>\n </div>\n}\n\n@if (showTabBar) {\n <c8y-tabs-outlet\n class=\"elevation-none m-b-16 p-sticky bg-component d-block\"\n [outletName]=\"tabsOutletName\"\n orientation=\"horizontal\"\n ></c8y-tabs-outlet>\n\n @if (showAsset) {\n <c8y-tab\n [isActive]=\"selectedTab === 'asset'\"\n [tabsOutlet]=\"tabsOutletName\"\n [priority]=\"1100\"\n (onSelect)=\"onTabChange('asset')\"\n >\n <i [c8yIcon]=\"'cube'\"></i>\n <span>\n {{ tabNames.asset | translate }}\n <span\n class=\"m-l-4 badge badge-default flex-no-shrink\"\n aria-live=\"assertive\"\n >\n {{ assetCount }}\n </span>\n </span>\n </c8y-tab>\n }\n\n @if (showRegular) {\n <c8y-tab\n [isActive]=\"selectedTab === 'regular'\"\n [tabsOutlet]=\"tabsOutletName\"\n [priority]=\"1000\"\n (onSelect)=\"onTabChange('regular')\"\n >\n <i [c8yIcon]=\"'new-property'\"></i>\n <span>\n {{ tabNames.regular | translate }}\n <span\n class=\"m-l-4 badge badge-default flex-no-shrink\"\n aria-live=\"assertive\"\n >\n {{ regularCount }}\n </span>\n </span>\n </c8y-tab>\n }\n\n @if (showComputed) {\n <c8y-tab\n [isActive]=\"selectedTab === 'computed'\"\n [tabsOutlet]=\"tabsOutletName\"\n [priority]=\"900\"\n (onSelect)=\"onTabChange('computed')\"\n >\n <i [c8yIcon]=\"'bolt'\"></i>\n <span>\n {{ tabNames.computed | translate }}\n <span\n class=\"m-l-4 badge badge-default flex-no-shrink\"\n aria-live=\"assertive\"\n >\n {{ computedCount }}\n </span>\n </span>\n </c8y-tab>\n }\n}\n\n<c8y-asset-property-list\n class=\"bg-component d-block\"\n [config]=\"config\"\n [extraProperties]=\"displayedProperties\"\n (selectedProperties)=\"onSelectedProperties($event)\"\n></c8y-asset-property-list>\n@if (selectedTab === 'regular') {\n <c8y-load-more\n [paging]=\"paging\"\n [useIntersection]=\"true\"\n (onLoad)=\"loadMore()\"\n ></c8y-load-more>\n}\n", dependencies: [{ kind: "ngmodule", type: FormsModule$1 }, { kind: "directive", type: i1$1.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: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: AssetPropertyListComponent, selector: "c8y-asset-property-list", inputs: ["config", "asset", "extraProperties"], outputs: ["selectedProperties"] }, { kind: "component", type: LoadMoreComponent, selector: "c8y-load-more", inputs: ["paging", "useIntersection", "hidden", "container", "class", "maxIterations", "noMoreDataHint", "loadingTemplate", "hideNoMoreDataHint", "loadNextLabel", "loadingLabel"], outputs: ["onLoad"] }, { kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "component", type: TabComponent, selector: "c8y-tab", inputs: ["path", "label", "icon", "priority", "orientation", "injector", "tabsOutlet", "isActive", "text", "showAlways"], outputs: ["onSelect"] }, { kind: "component", type: TabsOutletComponent, selector: "c8y-tabs-outlet,c8y-ui-tabs", inputs: ["tabs", "orientation", "navigatorOpen", "outletName", "context", "openFirstTab", "hasHeader"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }] }); }
|
|
2814
2929
|
}
|
|
2815
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type:
|
|
2930
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AssetPropertyTabsComponent, decorators: [{
|
|
2816
2931
|
type: Component,
|
|
2817
|
-
args: [{ selector: 'c8y-
|
|
2932
|
+
args: [{ selector: 'c8y-asset-property-tabs', host: {
|
|
2818
2933
|
class: 'd-contents'
|
|
2819
2934
|
}, standalone: true, imports: [
|
|
2820
2935
|
C8yTranslatePipe,
|
|
@@ -2824,10 +2939,110 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
|
|
|
2824
2939
|
IconDirective,
|
|
2825
2940
|
TabComponent,
|
|
2826
2941
|
TabsOutletComponent
|
|
2827
|
-
], template: "
|
|
2828
|
-
}], propDecorators: {
|
|
2942
|
+
], template: "@if (showSearch) {\n <div class=\"form-group p-16 m-b-0 d-flex sticky-top bg-component\">\n <div class=\"input-group input-group-search\">\n <input\n class=\"form-control\"\n [attr.aria-label]=\"'Search properties' | translate\"\n placeholder=\"{{ 'Search properties' | translate }}\"\n role=\"searchbox\"\n type=\"search\"\n autocomplete=\"off\"\n [(ngModel)]=\"inputText\"\n (input)=\"onSearch()\"\n #filter\n />\n @if (filter.value.length === 0) {\n <span\n class=\"input-group-addon\"\n [attr.aria-hidden]=\"true\"\n >\n <i\n class=\"dlt-c8y-icon-search\"\n [c8yIcon]=\"'search'\"\n ></i>\n </span>\n }\n @if (filter.value.length > 0) {\n <span\n class=\"input-group-addon\"\n [attr.aria-hidden]=\"true\"\n >\n <i\n class=\"text-muted dlt-c8y-icon-times\"\n [c8yIcon]=\"'times'\"\n [attr.aria-label]=\"'Clear search' | translate\"\n tabindex=\"0\"\n role=\"button\"\n (click)=\"clearSearch()\"\n ></i>\n </span>\n }\n </div>\n </div>\n}\n\n@if (showTabBar) {\n <c8y-tabs-outlet\n class=\"elevation-none m-b-16 p-sticky bg-component d-block\"\n [outletName]=\"tabsOutletName\"\n orientation=\"horizontal\"\n ></c8y-tabs-outlet>\n\n @if (showAsset) {\n <c8y-tab\n [isActive]=\"selectedTab === 'asset'\"\n [tabsOutlet]=\"tabsOutletName\"\n [priority]=\"1100\"\n (onSelect)=\"onTabChange('asset')\"\n >\n <i [c8yIcon]=\"'cube'\"></i>\n <span>\n {{ tabNames.asset | translate }}\n <span\n class=\"m-l-4 badge badge-default flex-no-shrink\"\n aria-live=\"assertive\"\n >\n {{ assetCount }}\n </span>\n </span>\n </c8y-tab>\n }\n\n @if (showRegular) {\n <c8y-tab\n [isActive]=\"selectedTab === 'regular'\"\n [tabsOutlet]=\"tabsOutletName\"\n [priority]=\"1000\"\n (onSelect)=\"onTabChange('regular')\"\n >\n <i [c8yIcon]=\"'new-property'\"></i>\n <span>\n {{ tabNames.regular | translate }}\n <span\n class=\"m-l-4 badge badge-default flex-no-shrink\"\n aria-live=\"assertive\"\n >\n {{ regularCount }}\n </span>\n </span>\n </c8y-tab>\n }\n\n @if (showComputed) {\n <c8y-tab\n [isActive]=\"selectedTab === 'computed'\"\n [tabsOutlet]=\"tabsOutletName\"\n [priority]=\"900\"\n (onSelect)=\"onTabChange('computed')\"\n >\n <i [c8yIcon]=\"'bolt'\"></i>\n <span>\n {{ tabNames.computed | translate }}\n <span\n class=\"m-l-4 badge badge-default flex-no-shrink\"\n aria-live=\"assertive\"\n >\n {{ computedCount }}\n </span>\n </span>\n </c8y-tab>\n }\n}\n\n<c8y-asset-property-list\n class=\"bg-component d-block\"\n [config]=\"config\"\n [extraProperties]=\"displayedProperties\"\n (selectedProperties)=\"onSelectedProperties($event)\"\n></c8y-asset-property-list>\n@if (selectedTab === 'regular') {\n <c8y-load-more\n [paging]=\"paging\"\n [useIntersection]=\"true\"\n (onLoad)=\"loadMore()\"\n ></c8y-load-more>\n}\n" }]
|
|
2943
|
+
}], propDecorators: { asset: [{
|
|
2944
|
+
type: Input
|
|
2945
|
+
}], config: [{
|
|
2946
|
+
type: Input
|
|
2947
|
+
}], selectedProperties: [{
|
|
2948
|
+
type: Input
|
|
2949
|
+
}], hiddenTabs: [{
|
|
2950
|
+
type: Input
|
|
2951
|
+
}], tabsOutletName: [{
|
|
2829
2952
|
type: Input
|
|
2830
|
-
}],
|
|
2953
|
+
}], showSearch: [{
|
|
2954
|
+
type: Input
|
|
2955
|
+
}], selectedPropertiesChange: [{
|
|
2956
|
+
type: Output
|
|
2957
|
+
}] } });
|
|
2958
|
+
|
|
2959
|
+
/**
|
|
2960
|
+
* Represents a drawer component for selecting asset properties.
|
|
2961
|
+
* This is a thin wrapper around the LibraryPropertiesTabsComponent.
|
|
2962
|
+
*/
|
|
2963
|
+
class CustomPropertiesDrawerComponent {
|
|
2964
|
+
constructor() {
|
|
2965
|
+
/**
|
|
2966
|
+
* Title of the drawer.
|
|
2967
|
+
*/
|
|
2968
|
+
this.title = gettext('Select property');
|
|
2969
|
+
/**
|
|
2970
|
+
* Configuration for the asset property list.
|
|
2971
|
+
*/
|
|
2972
|
+
this.config = {
|
|
2973
|
+
filterable: false,
|
|
2974
|
+
selectMode: 'multi',
|
|
2975
|
+
expansionMode: 'collapsedByDefault',
|
|
2976
|
+
showValue: false,
|
|
2977
|
+
emptyStateContent: 'default-properties',
|
|
2978
|
+
allowAddingCustomProperties: false,
|
|
2979
|
+
inputPropertiesHandle: 'override'
|
|
2980
|
+
};
|
|
2981
|
+
/**
|
|
2982
|
+
* Emits the selected properties when saved.
|
|
2983
|
+
*/
|
|
2984
|
+
this.savePropertySelection = new EventEmitter();
|
|
2985
|
+
/**
|
|
2986
|
+
* Emits an event when the selection is canceled.
|
|
2987
|
+
*/
|
|
2988
|
+
this.cancelPropertySelection = new EventEmitter();
|
|
2989
|
+
/**
|
|
2990
|
+
* List of selected properties.
|
|
2991
|
+
*/
|
|
2992
|
+
this.selectedProperties = [];
|
|
2993
|
+
/**
|
|
2994
|
+
* Reference to the bottom drawer.
|
|
2995
|
+
*/
|
|
2996
|
+
this.bottomDrawerRef = inject(BottomDrawerRef);
|
|
2997
|
+
/**
|
|
2998
|
+
* Promise resolving to the selected properties.
|
|
2999
|
+
*/
|
|
3000
|
+
this.result = new Promise((resolve, reject) => {
|
|
3001
|
+
this._save = resolve;
|
|
3002
|
+
this._cancel = reject;
|
|
3003
|
+
});
|
|
3004
|
+
/**
|
|
3005
|
+
* Controls which tabs are hidden.
|
|
3006
|
+
*/
|
|
3007
|
+
this.hiddenTabs = {};
|
|
3008
|
+
}
|
|
3009
|
+
/**
|
|
3010
|
+
* Updates the selected properties.
|
|
3011
|
+
* @param properties The selected properties.
|
|
3012
|
+
*/
|
|
3013
|
+
onSelectedProperties(properties) {
|
|
3014
|
+
this.selectedProperties = cloneDeep(properties);
|
|
3015
|
+
}
|
|
3016
|
+
/**
|
|
3017
|
+
* Saves the selected properties and closes the drawer.
|
|
3018
|
+
*/
|
|
3019
|
+
onSave() {
|
|
3020
|
+
this._save(this.selectedProperties);
|
|
3021
|
+
this.bottomDrawerRef.close();
|
|
3022
|
+
}
|
|
3023
|
+
/**
|
|
3024
|
+
* Cancels the selection and closes the drawer.
|
|
3025
|
+
*/
|
|
3026
|
+
onCancel() {
|
|
3027
|
+
this._cancel();
|
|
3028
|
+
this.bottomDrawerRef.close();
|
|
3029
|
+
}
|
|
3030
|
+
/**
|
|
3031
|
+
* Checks if the select button should be disabled.
|
|
3032
|
+
* @returns True if all selected properties are inactive.
|
|
3033
|
+
*/
|
|
3034
|
+
selectIsDisabled() {
|
|
3035
|
+
return this.selectedProperties?.every(({ active }) => !active);
|
|
3036
|
+
}
|
|
3037
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CustomPropertiesDrawerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
3038
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.16", type: CustomPropertiesDrawerComponent, isStandalone: true, selector: "c8y-custom-properties-drawer-component", outputs: { savePropertySelection: "savePropertySelection", cancelPropertySelection: "cancelPropertySelection" }, host: { classAttribute: "d-contents" }, ngImport: i0, template: "<div class=\"card-header separator\">\n <span class=\"h4 card-title\">{{ title | translate }}</span>\n</div>\n\n<c8y-asset-property-tabs\n [asset]=\"asset\"\n [config]=\"config\"\n [selectedProperties]=\"selectedProperties\"\n [hiddenTabs]=\"hiddenTabs\"\n [tabsOutletName]=\"'assetPropertiesDrawerTabs'\"\n [showSearch]=\"true\"\n (selectedPropertiesChange)=\"onSelectedProperties($event)\"\n></c8y-asset-property-tabs>\n\n<div class=\"card-footer text-center p-24 separator flex-no-shrink\">\n <button\n class=\"btn btn-default\"\n title=\"{{ 'Cancel' | translate }}\"\n type=\"button\"\n (click)=\"onCancel()\"\n >\n {{ 'Cancel' | translate }}\n </button>\n <button\n class=\"btn btn-primary\"\n title=\"{{ 'Select' | translate }}\"\n type=\"button\"\n [disabled]=\"selectIsDisabled()\"\n (click)=\"onSave()\"\n >\n {{ 'Select' | translate }}\n </button>\n</div>\n", dependencies: [{ kind: "component", type: AssetPropertyTabsComponent, selector: "c8y-asset-property-tabs", inputs: ["asset", "config", "selectedProperties", "hiddenTabs", "tabsOutletName", "showSearch"], outputs: ["selectedPropertiesChange"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }] }); }
|
|
3039
|
+
}
|
|
3040
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CustomPropertiesDrawerComponent, decorators: [{
|
|
3041
|
+
type: Component,
|
|
3042
|
+
args: [{ selector: 'c8y-custom-properties-drawer-component', host: {
|
|
3043
|
+
class: 'd-contents'
|
|
3044
|
+
}, standalone: true, imports: [C8yTranslatePipe, AssetPropertyTabsComponent], template: "<div class=\"card-header separator\">\n <span class=\"h4 card-title\">{{ title | translate }}</span>\n</div>\n\n<c8y-asset-property-tabs\n [asset]=\"asset\"\n [config]=\"config\"\n [selectedProperties]=\"selectedProperties\"\n [hiddenTabs]=\"hiddenTabs\"\n [tabsOutletName]=\"'assetPropertiesDrawerTabs'\"\n [showSearch]=\"true\"\n (selectedPropertiesChange)=\"onSelectedProperties($event)\"\n></c8y-asset-property-tabs>\n\n<div class=\"card-footer text-center p-24 separator flex-no-shrink\">\n <button\n class=\"btn btn-default\"\n title=\"{{ 'Cancel' | translate }}\"\n type=\"button\"\n (click)=\"onCancel()\"\n >\n {{ 'Cancel' | translate }}\n </button>\n <button\n class=\"btn btn-primary\"\n title=\"{{ 'Select' | translate }}\"\n type=\"button\"\n [disabled]=\"selectIsDisabled()\"\n (click)=\"onSave()\"\n >\n {{ 'Select' | translate }}\n </button>\n</div>\n" }]
|
|
3045
|
+
}], propDecorators: { savePropertySelection: [{
|
|
2831
3046
|
type: Output
|
|
2832
3047
|
}], cancelPropertySelection: [{
|
|
2833
3048
|
type: Output
|
|
@@ -2866,8 +3081,307 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
|
|
|
2866
3081
|
}]
|
|
2867
3082
|
}] });
|
|
2868
3083
|
|
|
3084
|
+
/**
|
|
3085
|
+
* Represents a component for selecting asset properties.
|
|
3086
|
+
* This component handles all selection logic and can be used standalone or within drawer/modal wrappers.
|
|
3087
|
+
*/
|
|
3088
|
+
class AssetPropertySelectorComponent {
|
|
3089
|
+
constructor() {
|
|
3090
|
+
/**
|
|
3091
|
+
* Configuration for the asset property list.
|
|
3092
|
+
*/
|
|
3093
|
+
this.config = {};
|
|
3094
|
+
/**
|
|
3095
|
+
* Extra properties to be displayed in the list.
|
|
3096
|
+
*/
|
|
3097
|
+
this.extraProperties = [];
|
|
3098
|
+
/**
|
|
3099
|
+
* Flag to allow changing the context (asset) within the selector.
|
|
3100
|
+
*/
|
|
3101
|
+
this.allowChangingContext = false;
|
|
3102
|
+
/**
|
|
3103
|
+
* Flag to allow properties from multiple assets to be selected at once.
|
|
3104
|
+
*/
|
|
3105
|
+
this.allowPropertiesFromMultipleAssets = true;
|
|
3106
|
+
/**
|
|
3107
|
+
* Flag to hide the selection panel.
|
|
3108
|
+
*/
|
|
3109
|
+
this.hideSelection = true;
|
|
3110
|
+
/**
|
|
3111
|
+
* Flag to allow search in asset selector.
|
|
3112
|
+
*/
|
|
3113
|
+
this.allowSearch = true;
|
|
3114
|
+
/**
|
|
3115
|
+
* List of selected properties.
|
|
3116
|
+
*/
|
|
3117
|
+
this.selectedProperties = [];
|
|
3118
|
+
/**
|
|
3119
|
+
* Current asset selection as observable.
|
|
3120
|
+
*/
|
|
3121
|
+
this.assetSelection = new BehaviorSubject(null);
|
|
3122
|
+
this.touched = false;
|
|
3123
|
+
this.modalService = inject(BsModalService);
|
|
3124
|
+
this.computedPropertiesService = inject(ComputedPropertiesService);
|
|
3125
|
+
this.assetPropertiesService = inject(AssetPropertiesService);
|
|
3126
|
+
}
|
|
3127
|
+
ngOnInit() {
|
|
3128
|
+
if (this.contextAsset) {
|
|
3129
|
+
this.assetSelection.next(this.contextAsset);
|
|
3130
|
+
}
|
|
3131
|
+
this.hiddenTabs = this.config.allowAddingCustomProperties
|
|
3132
|
+
? {}
|
|
3133
|
+
: { regular: true, computed: true };
|
|
3134
|
+
}
|
|
3135
|
+
/**
|
|
3136
|
+
* ControlValueAccessor implementation - write value from form model.
|
|
3137
|
+
*/
|
|
3138
|
+
writeValue(obj) {
|
|
3139
|
+
this.selectedProperties = obj || [];
|
|
3140
|
+
}
|
|
3141
|
+
/**
|
|
3142
|
+
* ControlValueAccessor implementation - register onChange callback.
|
|
3143
|
+
*/
|
|
3144
|
+
registerOnChange(fn) {
|
|
3145
|
+
this.onChange = fn;
|
|
3146
|
+
}
|
|
3147
|
+
/**
|
|
3148
|
+
* ControlValueAccessor implementation - register onTouched callback.
|
|
3149
|
+
*/
|
|
3150
|
+
registerOnTouched(fn) {
|
|
3151
|
+
this.onTouched = fn;
|
|
3152
|
+
}
|
|
3153
|
+
/**
|
|
3154
|
+
* Updates the selected properties.
|
|
3155
|
+
* @param properties The selected properties.
|
|
3156
|
+
*/
|
|
3157
|
+
async onSelectedProperties(properties) {
|
|
3158
|
+
this.markAsTouched();
|
|
3159
|
+
// Handle computed properties configuration
|
|
3160
|
+
const configuredProperties = await this.handleComputedPropertiesConfiguration(properties);
|
|
3161
|
+
this.selectedProperties = cloneDeep(configuredProperties);
|
|
3162
|
+
this.emitCurrentSelection();
|
|
3163
|
+
}
|
|
3164
|
+
/**
|
|
3165
|
+
* Handles asset selection changes.
|
|
3166
|
+
* @param evt Asset selection change event.
|
|
3167
|
+
*/
|
|
3168
|
+
selectionChanged(evt) {
|
|
3169
|
+
if (evt.change.item) {
|
|
3170
|
+
return this.selectAsset(evt.change.item);
|
|
3171
|
+
}
|
|
3172
|
+
// reset selection
|
|
3173
|
+
this.assetSelection.next(null);
|
|
3174
|
+
}
|
|
3175
|
+
/**
|
|
3176
|
+
* Removes a property from the selection.
|
|
3177
|
+
* @param property The property to remove.
|
|
3178
|
+
*/
|
|
3179
|
+
propertyRemoved(property) {
|
|
3180
|
+
this.markAsTouched();
|
|
3181
|
+
this.selectedProperties = this.selectedProperties.filter(prop => prop !== property);
|
|
3182
|
+
this.emitCurrentSelection();
|
|
3183
|
+
}
|
|
3184
|
+
/**
|
|
3185
|
+
* Edits (reconfigures) a computed property.
|
|
3186
|
+
* @param property The property to edit.
|
|
3187
|
+
*/
|
|
3188
|
+
async editProperty(property) {
|
|
3189
|
+
const definition = await this.computedPropertiesService.getByName(property.name);
|
|
3190
|
+
const configured = await this.configureProperty(property, definition);
|
|
3191
|
+
if (configured) {
|
|
3192
|
+
const currentAsset = this.assetSelection.getValue();
|
|
3193
|
+
if (currentAsset?.id) {
|
|
3194
|
+
property.contextAssetId = currentAsset.id;
|
|
3195
|
+
}
|
|
3196
|
+
this.emitCurrentSelection();
|
|
3197
|
+
}
|
|
3198
|
+
}
|
|
3199
|
+
/**
|
|
3200
|
+
* Selects an asset and updates the selection.
|
|
3201
|
+
* @param asset The asset to select.
|
|
3202
|
+
*/
|
|
3203
|
+
selectAsset(asset) {
|
|
3204
|
+
this.assetSelection.next(asset);
|
|
3205
|
+
if (!this.allowPropertiesFromMultipleAssets) {
|
|
3206
|
+
this.clearSelection();
|
|
3207
|
+
}
|
|
3208
|
+
}
|
|
3209
|
+
/**
|
|
3210
|
+
* Clears all selected properties.
|
|
3211
|
+
*/
|
|
3212
|
+
clearSelection() {
|
|
3213
|
+
this.selectedProperties = [];
|
|
3214
|
+
this.emitCurrentSelection();
|
|
3215
|
+
}
|
|
3216
|
+
/**
|
|
3217
|
+
* Emits the current selection to the form model.
|
|
3218
|
+
*/
|
|
3219
|
+
emitCurrentSelection() {
|
|
3220
|
+
if (this.onChange) {
|
|
3221
|
+
this.onChange(this.selectedProperties);
|
|
3222
|
+
}
|
|
3223
|
+
}
|
|
3224
|
+
/**
|
|
3225
|
+
* Marks the control as touched.
|
|
3226
|
+
*/
|
|
3227
|
+
markAsTouched() {
|
|
3228
|
+
if (!this.touched && this.onTouched) {
|
|
3229
|
+
this.onTouched();
|
|
3230
|
+
this.touched = true;
|
|
3231
|
+
}
|
|
3232
|
+
}
|
|
3233
|
+
/**
|
|
3234
|
+
* Handles configuration for newly added computed properties.
|
|
3235
|
+
* @param properties The selected properties.
|
|
3236
|
+
* @returns The properties with configured ones included.
|
|
3237
|
+
*/
|
|
3238
|
+
async handleComputedPropertiesConfiguration(properties) {
|
|
3239
|
+
// Combine previously selected properties with newly added/configured ones
|
|
3240
|
+
const previouslySelected = this.selectedProperties.filter(existing => {
|
|
3241
|
+
if (existing.computed && existing.instanceId) {
|
|
3242
|
+
// computed props that has config (so they get instanceId) can be added multiple times and are not removed from middle list
|
|
3243
|
+
return true;
|
|
3244
|
+
}
|
|
3245
|
+
return properties.some(prop => this.assetPropertiesService.propertiesMatch(existing, prop));
|
|
3246
|
+
});
|
|
3247
|
+
if (properties.length === 0) {
|
|
3248
|
+
// selector might emit properties array as empty array, but we should keep previously selected computed properties with config in that case, as they are not removed from middle list
|
|
3249
|
+
return previouslySelected?.length !== 0 ? previouslySelected : [];
|
|
3250
|
+
}
|
|
3251
|
+
// Find newly added computed properties (not in previous selection)
|
|
3252
|
+
const newlyAdded = properties.filter(prop => prop.computed &&
|
|
3253
|
+
!this.selectedProperties.some(existing => this.assetPropertiesService.propertiesMatch(existing, prop)));
|
|
3254
|
+
if (newlyAdded.length === 0) {
|
|
3255
|
+
return properties;
|
|
3256
|
+
}
|
|
3257
|
+
const propertiesNeedingConfig = [];
|
|
3258
|
+
const definitionsNeedingConfig = [];
|
|
3259
|
+
const propertiesToActuallyAdd = [];
|
|
3260
|
+
// Separate properties that need configuration from those that don't
|
|
3261
|
+
for (const prop of newlyAdded) {
|
|
3262
|
+
const definition = await this.computedPropertiesService.getByName(prop.name);
|
|
3263
|
+
if (definition.configComponent || definition.loadConfigComponent) {
|
|
3264
|
+
propertiesNeedingConfig.push(prop);
|
|
3265
|
+
definitionsNeedingConfig.push(definition);
|
|
3266
|
+
}
|
|
3267
|
+
else {
|
|
3268
|
+
propertiesToActuallyAdd.push(cloneDeep(prop));
|
|
3269
|
+
}
|
|
3270
|
+
}
|
|
3271
|
+
// Configure properties that need it
|
|
3272
|
+
if (propertiesNeedingConfig.length > 0) {
|
|
3273
|
+
try {
|
|
3274
|
+
const configs = await this.configureMultipleProperties(propertiesNeedingConfig, definitionsNeedingConfig);
|
|
3275
|
+
propertiesNeedingConfig.forEach((prop, index) => {
|
|
3276
|
+
const clonedProp = cloneDeep(prop);
|
|
3277
|
+
clonedProp.config = configs[index];
|
|
3278
|
+
const currentAsset = this.assetSelection.getValue();
|
|
3279
|
+
if (currentAsset?.id) {
|
|
3280
|
+
clonedProp.contextAssetId = currentAsset.id;
|
|
3281
|
+
}
|
|
3282
|
+
// Generate instanceId for computed properties to allow multiple instances
|
|
3283
|
+
if (!clonedProp.instanceId) {
|
|
3284
|
+
clonedProp.instanceId = crypto.randomUUID();
|
|
3285
|
+
}
|
|
3286
|
+
propertiesToActuallyAdd.push(clonedProp);
|
|
3287
|
+
});
|
|
3288
|
+
}
|
|
3289
|
+
catch {
|
|
3290
|
+
// User cancelled configuration, don't add these properties
|
|
3291
|
+
// propertiesToActuallyAdd already contains properties that didn't need config
|
|
3292
|
+
}
|
|
3293
|
+
}
|
|
3294
|
+
const nonComputedNewlyAdded = properties.filter(prop => !prop.computed &&
|
|
3295
|
+
!this.selectedProperties.some(existing => this.assetPropertiesService.propertiesMatch(existing, prop)));
|
|
3296
|
+
return [...previouslySelected, ...propertiesToActuallyAdd, ...nonComputedNewlyAdded];
|
|
3297
|
+
}
|
|
3298
|
+
/**
|
|
3299
|
+
* Displays the configuration modal for multiple computed properties and updates their configurations.
|
|
3300
|
+
* @param properties Properties to configure.
|
|
3301
|
+
* @param definitions Computed property definitions including config components.
|
|
3302
|
+
* @returns Promise resolving to array of configurations.
|
|
3303
|
+
*/
|
|
3304
|
+
configureMultipleProperties(properties, definitions) {
|
|
3305
|
+
const currentAsset = this.assetSelection.getValue();
|
|
3306
|
+
const modalRef = this.modalService.show(ComputedPropertiesConfigComponent, {
|
|
3307
|
+
ignoreBackdropClick: true,
|
|
3308
|
+
class: 'modal-lg',
|
|
3309
|
+
ariaDescribedby: 'modal-body',
|
|
3310
|
+
ariaLabelledBy: 'modal-title',
|
|
3311
|
+
initialState: {
|
|
3312
|
+
properties,
|
|
3313
|
+
definitions,
|
|
3314
|
+
asset: currentAsset
|
|
3315
|
+
}
|
|
3316
|
+
}).content;
|
|
3317
|
+
return modalRef.result;
|
|
3318
|
+
}
|
|
3319
|
+
/**
|
|
3320
|
+
* Displays the configuration modal for a single computed property and updates its configuration.
|
|
3321
|
+
* @param property Property to configure.
|
|
3322
|
+
* @param definition Computed property definition including config component.
|
|
3323
|
+
* @returns true if the property was configured, false if cancelled.
|
|
3324
|
+
*/
|
|
3325
|
+
async configureProperty(property, definition) {
|
|
3326
|
+
try {
|
|
3327
|
+
const configs = await this.configureMultipleProperties([property], [definition]);
|
|
3328
|
+
property.config = configs[0];
|
|
3329
|
+
return true;
|
|
3330
|
+
}
|
|
3331
|
+
catch (err) {
|
|
3332
|
+
return false;
|
|
3333
|
+
}
|
|
3334
|
+
}
|
|
3335
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AssetPropertySelectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
3336
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: AssetPropertySelectorComponent, isStandalone: true, selector: "c8y-asset-property-selector", inputs: { contextAsset: "contextAsset", config: "config", extraProperties: "extraProperties", allowChangingContext: "allowChangingContext", allowPropertiesFromMultipleAssets: "allowPropertiesFromMultipleAssets", hideSelection: "hideSelection", allowSearch: "allowSearch" }, providers: [
|
|
3337
|
+
{
|
|
3338
|
+
provide: NG_VALUE_ACCESSOR,
|
|
3339
|
+
multi: true,
|
|
3340
|
+
useExisting: forwardRef(() => AssetPropertySelectorComponent)
|
|
3341
|
+
}
|
|
3342
|
+
], ngImport: i0, template: "<div\n class=\"d-grid grid__row--1 fit-h\"\n [ngClass]=\"{\n 'grid__col--3-6-3--md': allowChangingContext && !hideSelection,\n 'grid__col--8-4--md': !allowChangingContext && !hideSelection,\n 'grid__col--4-8--md': allowChangingContext && hideSelection\n }\"\n>\n @if (allowChangingContext) {\n <div class=\"d-flex d-col p-relative bg-level-1\">\n <c8y-asset-selector-miller\n class=\"d-contents\"\n [(ngModel)]=\"contextAsset\"\n [asset]=\"contextAsset\"\n (onSelected)=\"selectionChanged($event)\"\n [container]=\"''\"\n [config]=\"{\n view: 'miller',\n groupsSelectable: true,\n columnHeaders: true,\n showChildDevices: true,\n showUnassignedDevices: true,\n singleColumn: true,\n search: allowSearch,\n showFilter: true\n }\"\n ></c8y-asset-selector-miller>\n </div>\n }\n\n <div class=\"bg-component inner-scroll\">\n <c8y-asset-property-tabs\n class=\"bg-component d-contents\"\n [asset]=\"assetSelection | async\"\n [config]=\"config\"\n [selectedProperties]=\"selectedProperties\"\n [hiddenTabs]=\"hiddenTabs\"\n [showSearch]=\"true\"\n (selectedPropertiesChange)=\"onSelectedProperties($event)\"\n ></c8y-asset-property-tabs>\n </div>\n\n @if (!hideSelection) {\n <div class=\"inner-scroll bg-level-1\">\n <p\n class=\"text-medium p-l-16 p-r-16 p-t-8 p-b-8 separator-bottom sticky-top text-truncate\"\n [title]=\"'Selected properties' | translate\"\n >\n {{ 'Selected properties' | translate }}\n </p>\n @if (selectedProperties?.length) {\n <div class=\"d-flex flex-wrap gap-8 p-l-16 p-r-16 p-t-8 p-b-16\">\n @for (selectedProp of selectedProperties; track selectedProp.label) {\n <div\n [ngClass]=\"{\n 'c8y-datapoint-pill': selectedProp.computed && selectedProp.config,\n 'c8y-alarm-pill': !(selectedProp.computed && selectedProp.config)\n }\"\n >\n <button\n class=\"c8y-alarm-pill__btn\"\n [title]=\"'Remove' | translate\"\n type=\"button\"\n (click)=\"propertyRemoved(selectedProp)\"\n >\n <i\n class=\"icon-14\"\n c8yIcon=\"remove\"\n ></i>\n </button>\n @if (selectedProp.computed && selectedProp.config) {\n <button\n class=\"c8y-datapoint-pill__btn\"\n title=\"{{ 'Configure' | translate }}\"\n (click)=\"editProperty(selectedProp)\"\n >\n <i [c8yIcon]=\"'cog'\"></i>\n </button>\n }\n @let label =\n selectedProp.label || selectedProp.title || selectedProp.name | translate;\n <div\n class=\"c8y-alarm-pill__label\"\n [title]=\"label\"\n >\n <button\n class=\"btn-clean m-r-4\"\n [attr.aria-label]=\"selectedProp | c8yAssetPropertyIconTooltip\"\n tooltip=\"{{ selectedProp | c8yAssetPropertyIconTooltip }}\"\n container=\"body\"\n type=\"button\"\n [delay]=\"500\"\n >\n <i [c8yIcon]=\"selectedProp | c8yAssetPropertyIcon\"></i>\n </button>\n <span class=\"text-truncate\">\n <span class=\"text-truncate\">{{ label }}</span>\n @if (selectedProp.config) {\n <small\n class=\"text-muted\"\n title=\"{{ selectedProp.config | json }}\"\n >{{ selectedProp.config | json }}</small\n >\n }\n </span>\n </div>\n </div>\n }\n </div>\n }\n\n @if (!selectedProperties || !selectedProperties.length) {\n <div class=\"p-r-8\">\n <c8y-ui-empty-state\n [icon]=\"'c8y-data-points'\"\n [title]=\"'No properties to display' | translate\"\n [subtitle]=\"\n 'Select the asset, then on the available properties list, select desired property.'\n | translate\n \"\n [horizontal]=\"true\"\n ></c8y-ui-empty-state>\n </div>\n }\n </div>\n }\n</div>\n", dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: AssetPropertyTabsComponent, selector: "c8y-asset-property-tabs", inputs: ["asset", "config", "selectedProperties", "hiddenTabs", "tabsOutletName", "showSearch"], outputs: ["selectedPropertiesChange"] }, { kind: "component", type: MillerViewComponent, selector: "c8y-asset-selector-miller", inputs: ["config", "asset", "selectedDevice", "rootNode", "container"], outputs: ["onSelected", "onClearSelected"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: EmptyStateComponent, selector: "c8y-ui-empty-state", inputs: ["icon", "title", "subtitle", "horizontal"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i3.TooltipDirective, selector: "[tooltip], [tooltipHtml]", inputs: ["adaptivePosition", "tooltip", "placement", "triggers", "container", "containerClass", "boundariesElement", "isOpen", "isDisabled", "delay", "tooltipHtml", "tooltipPlacement", "tooltipIsOpen", "tooltipEnable", "tooltipAppendToBody", "tooltipAnimation", "tooltipClass", "tooltipContext", "tooltipPopupDelay", "tooltipFadeDuration", "tooltipTrigger"], outputs: ["tooltipChange", "onShown", "onHidden", "tooltipStateChanged"], exportAs: ["bs-tooltip"] }, { kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: AssetPropertyIconPipe, name: "c8yAssetPropertyIcon" }, { kind: "pipe", type: AssetPropertyIconTooltipPipe, name: "c8yAssetPropertyIconTooltip" }, { kind: "pipe", type: JsonPipe, name: "json" }] }); }
|
|
3343
|
+
}
|
|
3344
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AssetPropertySelectorComponent, decorators: [{
|
|
3345
|
+
type: Component,
|
|
3346
|
+
args: [{ selector: 'c8y-asset-property-selector', standalone: true, providers: [
|
|
3347
|
+
{
|
|
3348
|
+
provide: NG_VALUE_ACCESSOR,
|
|
3349
|
+
multi: true,
|
|
3350
|
+
useExisting: forwardRef(() => AssetPropertySelectorComponent)
|
|
3351
|
+
}
|
|
3352
|
+
], imports: [
|
|
3353
|
+
C8yTranslatePipe,
|
|
3354
|
+
FormsModule,
|
|
3355
|
+
AssetPropertyTabsComponent,
|
|
3356
|
+
MillerViewComponent,
|
|
3357
|
+
NgClass,
|
|
3358
|
+
AsyncPipe,
|
|
3359
|
+
EmptyStateComponent,
|
|
3360
|
+
AssetPropertyIconPipe,
|
|
3361
|
+
TooltipModule,
|
|
3362
|
+
AssetPropertyIconTooltipPipe,
|
|
3363
|
+
IconDirective,
|
|
3364
|
+
JsonPipe
|
|
3365
|
+
], template: "<div\n class=\"d-grid grid__row--1 fit-h\"\n [ngClass]=\"{\n 'grid__col--3-6-3--md': allowChangingContext && !hideSelection,\n 'grid__col--8-4--md': !allowChangingContext && !hideSelection,\n 'grid__col--4-8--md': allowChangingContext && hideSelection\n }\"\n>\n @if (allowChangingContext) {\n <div class=\"d-flex d-col p-relative bg-level-1\">\n <c8y-asset-selector-miller\n class=\"d-contents\"\n [(ngModel)]=\"contextAsset\"\n [asset]=\"contextAsset\"\n (onSelected)=\"selectionChanged($event)\"\n [container]=\"''\"\n [config]=\"{\n view: 'miller',\n groupsSelectable: true,\n columnHeaders: true,\n showChildDevices: true,\n showUnassignedDevices: true,\n singleColumn: true,\n search: allowSearch,\n showFilter: true\n }\"\n ></c8y-asset-selector-miller>\n </div>\n }\n\n <div class=\"bg-component inner-scroll\">\n <c8y-asset-property-tabs\n class=\"bg-component d-contents\"\n [asset]=\"assetSelection | async\"\n [config]=\"config\"\n [selectedProperties]=\"selectedProperties\"\n [hiddenTabs]=\"hiddenTabs\"\n [showSearch]=\"true\"\n (selectedPropertiesChange)=\"onSelectedProperties($event)\"\n ></c8y-asset-property-tabs>\n </div>\n\n @if (!hideSelection) {\n <div class=\"inner-scroll bg-level-1\">\n <p\n class=\"text-medium p-l-16 p-r-16 p-t-8 p-b-8 separator-bottom sticky-top text-truncate\"\n [title]=\"'Selected properties' | translate\"\n >\n {{ 'Selected properties' | translate }}\n </p>\n @if (selectedProperties?.length) {\n <div class=\"d-flex flex-wrap gap-8 p-l-16 p-r-16 p-t-8 p-b-16\">\n @for (selectedProp of selectedProperties; track selectedProp.label) {\n <div\n [ngClass]=\"{\n 'c8y-datapoint-pill': selectedProp.computed && selectedProp.config,\n 'c8y-alarm-pill': !(selectedProp.computed && selectedProp.config)\n }\"\n >\n <button\n class=\"c8y-alarm-pill__btn\"\n [title]=\"'Remove' | translate\"\n type=\"button\"\n (click)=\"propertyRemoved(selectedProp)\"\n >\n <i\n class=\"icon-14\"\n c8yIcon=\"remove\"\n ></i>\n </button>\n @if (selectedProp.computed && selectedProp.config) {\n <button\n class=\"c8y-datapoint-pill__btn\"\n title=\"{{ 'Configure' | translate }}\"\n (click)=\"editProperty(selectedProp)\"\n >\n <i [c8yIcon]=\"'cog'\"></i>\n </button>\n }\n @let label =\n selectedProp.label || selectedProp.title || selectedProp.name | translate;\n <div\n class=\"c8y-alarm-pill__label\"\n [title]=\"label\"\n >\n <button\n class=\"btn-clean m-r-4\"\n [attr.aria-label]=\"selectedProp | c8yAssetPropertyIconTooltip\"\n tooltip=\"{{ selectedProp | c8yAssetPropertyIconTooltip }}\"\n container=\"body\"\n type=\"button\"\n [delay]=\"500\"\n >\n <i [c8yIcon]=\"selectedProp | c8yAssetPropertyIcon\"></i>\n </button>\n <span class=\"text-truncate\">\n <span class=\"text-truncate\">{{ label }}</span>\n @if (selectedProp.config) {\n <small\n class=\"text-muted\"\n title=\"{{ selectedProp.config | json }}\"\n >{{ selectedProp.config | json }}</small\n >\n }\n </span>\n </div>\n </div>\n }\n </div>\n }\n\n @if (!selectedProperties || !selectedProperties.length) {\n <div class=\"p-r-8\">\n <c8y-ui-empty-state\n [icon]=\"'c8y-data-points'\"\n [title]=\"'No properties to display' | translate\"\n [subtitle]=\"\n 'Select the asset, then on the available properties list, select desired property.'\n | translate\n \"\n [horizontal]=\"true\"\n ></c8y-ui-empty-state>\n </div>\n }\n </div>\n }\n</div>\n" }]
|
|
3366
|
+
}], propDecorators: { contextAsset: [{
|
|
3367
|
+
type: Input
|
|
3368
|
+
}], config: [{
|
|
3369
|
+
type: Input
|
|
3370
|
+
}], extraProperties: [{
|
|
3371
|
+
type: Input
|
|
3372
|
+
}], allowChangingContext: [{
|
|
3373
|
+
type: Input
|
|
3374
|
+
}], allowPropertiesFromMultipleAssets: [{
|
|
3375
|
+
type: Input
|
|
3376
|
+
}], hideSelection: [{
|
|
3377
|
+
type: Input
|
|
3378
|
+
}], allowSearch: [{
|
|
3379
|
+
type: Input
|
|
3380
|
+
}] } });
|
|
3381
|
+
|
|
2869
3382
|
/**
|
|
2870
3383
|
* Represents a drawer component for selecting asset properties.
|
|
3384
|
+
* This is a thin wrapper around AssetPropertySelectorComponent that handles drawer-specific logic.
|
|
2871
3385
|
*/
|
|
2872
3386
|
class AssetPropertySelectorDrawerComponent {
|
|
2873
3387
|
constructor() {
|
|
@@ -2880,17 +3394,25 @@ class AssetPropertySelectorDrawerComponent {
|
|
|
2880
3394
|
*/
|
|
2881
3395
|
this.extraProperties = [];
|
|
2882
3396
|
/**
|
|
2883
|
-
*
|
|
3397
|
+
* List of selected properties.
|
|
2884
3398
|
*/
|
|
2885
|
-
this.
|
|
3399
|
+
this.selectedProperties = [];
|
|
2886
3400
|
/**
|
|
2887
|
-
*
|
|
3401
|
+
* Flag to allow changing the context (asset) within the drawer.
|
|
2888
3402
|
*/
|
|
2889
|
-
this.
|
|
3403
|
+
this.allowChangingContext = false;
|
|
2890
3404
|
/**
|
|
2891
|
-
*
|
|
3405
|
+
* Flag to hide the selection
|
|
3406
|
+
* */
|
|
3407
|
+
this.hideSelection = true;
|
|
3408
|
+
/**
|
|
3409
|
+
* Flag to allow selecting properties from multiple assets (if context change is allowed).
|
|
2892
3410
|
*/
|
|
2893
|
-
this.
|
|
3411
|
+
this.allowPropertiesFromMultipleAssets = true;
|
|
3412
|
+
/**
|
|
3413
|
+
* Flag to enable search functionality for asset properties.
|
|
3414
|
+
*/
|
|
3415
|
+
this.allowSearch = true;
|
|
2894
3416
|
/**
|
|
2895
3417
|
* Reference to the bottom drawer.
|
|
2896
3418
|
*/
|
|
@@ -2903,12 +3425,15 @@ class AssetPropertySelectorDrawerComponent {
|
|
|
2903
3425
|
this._cancel = reject;
|
|
2904
3426
|
});
|
|
2905
3427
|
}
|
|
3428
|
+
ngOnInit() {
|
|
3429
|
+
this.selectedProperties = this.config?.selectedProperties ?? [];
|
|
3430
|
+
}
|
|
2906
3431
|
/**
|
|
2907
|
-
*
|
|
2908
|
-
* @param
|
|
3432
|
+
* Handles selection changes from the selector component.
|
|
3433
|
+
* @param selection The selected properties.
|
|
2909
3434
|
*/
|
|
2910
|
-
|
|
2911
|
-
this.selectedProperties =
|
|
3435
|
+
selectionChange(selection) {
|
|
3436
|
+
this.selectedProperties = selection;
|
|
2912
3437
|
}
|
|
2913
3438
|
/**
|
|
2914
3439
|
* Saves the selected properties and closes the drawer.
|
|
@@ -2926,30 +3451,26 @@ class AssetPropertySelectorDrawerComponent {
|
|
|
2926
3451
|
}
|
|
2927
3452
|
/**
|
|
2928
3453
|
* Checks if the select button should be disabled.
|
|
2929
|
-
* @returns True if
|
|
3454
|
+
* @returns True if no active properties are selected.
|
|
2930
3455
|
*/
|
|
2931
3456
|
selectIsDisabled() {
|
|
2932
|
-
return this.selectedProperties?.every(({ active }) => !active);
|
|
3457
|
+
return (!this.selectedProperties?.length || this.selectedProperties?.every(({ active }) => !active));
|
|
2933
3458
|
}
|
|
2934
3459
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AssetPropertySelectorDrawerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2935
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.16", type: AssetPropertySelectorDrawerComponent, isStandalone: true, selector: "c8y-asset-property-selector-drawer-component", inputs: { title: "title" },
|
|
3460
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.16", type: AssetPropertySelectorDrawerComponent, isStandalone: true, selector: "c8y-asset-property-selector-drawer-component", inputs: { title: "title" }, host: { classAttribute: "d-contents" }, ngImport: i0, template: "<div class=\"card-header separator\">\n <span class=\"h4 card-title\">{{ title | translate }}</span>\n</div>\n\n<div class=\"card-inner-scroll fit-h\">\n <c8y-asset-property-selector\n [contextAsset]=\"contextAsset\"\n [config]=\"config\"\n [extraProperties]=\"extraProperties\"\n [allowChangingContext]=\"allowChangingContext\"\n [allowPropertiesFromMultipleAssets]=\"allowPropertiesFromMultipleAssets\"\n [hideSelection]=\"hideSelection\"\n [allowSearch]=\"allowSearch\"\n [ngModel]=\"selectedProperties\"\n (ngModelChange)=\"selectionChange($event)\"\n ></c8y-asset-property-selector>\n</div>\n\n<div class=\"card-footer text-center p-24 separator flex-no-shrink\">\n <button\n class=\"btn btn-default\"\n title=\"{{ 'Cancel' | translate }}\"\n type=\"button\"\n (click)=\"onCancel()\"\n >\n {{ 'Cancel' | translate }}\n </button>\n <button\n class=\"btn btn-primary\"\n title=\"{{ 'Select' | translate }}\"\n type=\"button\"\n [disabled]=\"selectIsDisabled()\"\n (click)=\"onSave()\"\n >\n {{ 'Select' | translate }}\n </button>\n</div>\n", dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: AssetPropertySelectorComponent, selector: "c8y-asset-property-selector", inputs: ["contextAsset", "config", "extraProperties", "allowChangingContext", "allowPropertiesFromMultipleAssets", "hideSelection", "allowSearch"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }] }); }
|
|
2936
3461
|
}
|
|
2937
3462
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AssetPropertySelectorDrawerComponent, decorators: [{
|
|
2938
3463
|
type: Component,
|
|
2939
3464
|
args: [{ selector: 'c8y-asset-property-selector-drawer-component', host: {
|
|
2940
3465
|
class: 'd-contents'
|
|
2941
|
-
}, standalone: true, imports: [C8yTranslatePipe, FormsModule
|
|
3466
|
+
}, standalone: true, imports: [C8yTranslatePipe, FormsModule, AssetPropertySelectorComponent], template: "<div class=\"card-header separator\">\n <span class=\"h4 card-title\">{{ title | translate }}</span>\n</div>\n\n<div class=\"card-inner-scroll fit-h\">\n <c8y-asset-property-selector\n [contextAsset]=\"contextAsset\"\n [config]=\"config\"\n [extraProperties]=\"extraProperties\"\n [allowChangingContext]=\"allowChangingContext\"\n [allowPropertiesFromMultipleAssets]=\"allowPropertiesFromMultipleAssets\"\n [hideSelection]=\"hideSelection\"\n [allowSearch]=\"allowSearch\"\n [ngModel]=\"selectedProperties\"\n (ngModelChange)=\"selectionChange($event)\"\n ></c8y-asset-property-selector>\n</div>\n\n<div class=\"card-footer text-center p-24 separator flex-no-shrink\">\n <button\n class=\"btn btn-default\"\n title=\"{{ 'Cancel' | translate }}\"\n type=\"button\"\n (click)=\"onCancel()\"\n >\n {{ 'Cancel' | translate }}\n </button>\n <button\n class=\"btn btn-primary\"\n title=\"{{ 'Select' | translate }}\"\n type=\"button\"\n [disabled]=\"selectIsDisabled()\"\n (click)=\"onSave()\"\n >\n {{ 'Select' | translate }}\n </button>\n</div>\n" }]
|
|
2942
3467
|
}], propDecorators: { title: [{
|
|
2943
3468
|
type: Input
|
|
2944
|
-
}], savePropertySelection: [{
|
|
2945
|
-
type: Output
|
|
2946
|
-
}], cancelPropertySelection: [{
|
|
2947
|
-
type: Output
|
|
2948
3469
|
}] } });
|
|
2949
3470
|
|
|
2950
3471
|
/**
|
|
2951
3472
|
* Generated bundle index. Do not edit.
|
|
2952
3473
|
*/
|
|
2953
3474
|
|
|
2954
|
-
export { AssetPropertiesService, AssetPropertyActionDirective, AssetPropertyListComponent, AssetPropertySelectorDrawerComponent, ComputedPropertiesService, CustomPropertiesDrawerService, HOOK_COMPUTED_PROPERTY, RESULT_TYPES, defaultAssetProperties, defaultAssetPropertyListConfig, hookComputedProperty };
|
|
3475
|
+
export { AssetPropertiesService, AssetPropertyActionDirective, AssetPropertyListComponent, AssetPropertySelectorComponent, AssetPropertySelectorDrawerComponent, AssetPropertyTabsComponent, ComputedPropertiesService, CustomPropertiesDrawerService, HOOK_COMPUTED_PROPERTY, RESULT_TYPES, defaultAssetProperties, defaultAssetPropertyListConfig, hookComputedProperty };
|
|
2955
3476
|
//# sourceMappingURL=c8y-ngx-components-asset-properties.mjs.map
|