@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.
@@ -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, BottomDrawerRef, FormsModule as FormsModule$1, LoadMoreComponent, TabComponent, TabsOutletComponent, BottomDrawerService } from '@c8y/ngx-components';
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 keyPath1 = property1.keyPath;
941
- const keyPath2 = property2.keyPath;
942
- if (keyPath1 && keyPath2) {
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
- this.selectedProperties.next(selectedProperties);
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 || this.config.selectedProperties.length === 0) {
2271
+ if (!this.config.selectedProperties) {
2279
2272
  return;
2280
2273
  }
2281
- this.config.selectedProperties.forEach((configProperty, index) => {
2282
- const matchingNode = this.dataSource.data.find(node => this.assetPropertiesService.propertiesMatch(node.property, configProperty));
2283
- if (matchingNode) {
2284
- if (this.config.selectMode === 'single') {
2285
- // For single mode, only select the first property and break
2286
- if (index === 0) {
2287
- this.onSelectSingle(true, matchingNode);
2288
- }
2289
- return;
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 if (this.config.selectMode === 'multi') {
2292
- this.onSelectMulti(true, matchingNode);
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 drawer component for selecting asset properties.
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 CustomPropertiesDrawerComponent {
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
- * Emits the selected properties when saved.
2652
+ * Currently selected properties to be highlighted in the list.
2645
2653
  */
2646
- this.savePropertySelection = new EventEmitter();
2654
+ this.selectedProperties = [];
2647
2655
  /**
2648
- * Emits an event when the selection is canceled.
2656
+ * Controls which tabs are hidden.
2649
2657
  */
2650
- this.cancelPropertySelection = new EventEmitter();
2658
+ this.hiddenTabs = {};
2651
2659
  /**
2652
- * List of selected properties.
2660
+ * The name of the tabs outlet.
2653
2661
  */
2654
- this.selectedProperties = [];
2662
+ this.tabsOutletName = 'libraryPropertiesTabs';
2655
2663
  /**
2656
- * Reference to the bottom drawer.
2664
+ * Whether to show the search input.
2657
2665
  */
2658
- this.bottomDrawerRef = inject(BottomDrawerRef);
2666
+ this.showSearch = true;
2659
2667
  /**
2660
- * Promise resolving to the selected properties.
2668
+ * Emits when the selected properties change.
2661
2669
  */
2662
- this.result = new Promise((resolve, reject) => {
2663
- this._save = resolve;
2664
- this._cancel = reject;
2665
- });
2670
+ this.selectedPropertiesChange = new EventEmitter();
2666
2671
  this.tabNames = {
2667
- regular: gettext('Properties'),
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 = 'regular';
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.hiddenTabs = {};
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
- return this.showRegular && this.showComputed;
2704
+ const visibleTabs = [this.showAsset, this.showRegular, this.showComputed].filter(Boolean);
2705
+ return visibleTabs.length > 1;
2695
2706
  }
2696
2707
  async ngOnInit() {
2697
- if (!this.showRegular) {
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
- const computedPropsResult = await this.computedPropertiesService.getByContext(this.asset);
2702
- this.allComputedProperties = computedPropsResult.map(({ prop }) => prop);
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
- this.selectedProperties = cloneDeep(properties);
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
- * Checks if the select button should be disabled.
2770
- * @returns True if all selected properties are inactive.
2771
- */
2772
- selectIsDisabled() {
2773
- return this.selectedProperties?.every(({ active }) => !active);
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.displayedProperties =
2801
- this.selectedTab === 'regular' ? this.regularProperties : this.computedProperties;
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: CustomPropertiesDrawerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2813
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: CustomPropertiesDrawerComponent, isStandalone: true, selector: "c8y-custom-properties-drawer-component", inputs: { title: "title" }, 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<div class=\"form-group m-b-8 p-16 d-flex\">\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@if (showTabBar) {\n <c8y-tabs-outlet\n class=\"elevation-none m-b-16\"\n outletName=\"assetPropertiesDrawerTabs\"\n orientation=\"horizontal\"\n ></c8y-tabs-outlet>\n\n @if (showRegular) {\n <c8y-tab\n [isActive]=\"selectedTab === 'regular'\"\n [tabsOutlet]=\"'assetPropertiesDrawerTabs'\"\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]=\"'assetPropertiesDrawerTabs'\"\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<div class=\"inner-scroll flex-grow\">\n <c8y-asset-property-list\n class=\"bg-component\"\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</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$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" }] }); }
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: CustomPropertiesDrawerComponent, decorators: [{
2930
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AssetPropertyTabsComponent, decorators: [{
2816
2931
  type: Component,
2817
- args: [{ selector: 'c8y-custom-properties-drawer-component', host: {
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: "<div class=\"card-header separator\">\n <span class=\"h4 card-title\">{{ title | translate }}</span>\n</div>\n\n<div class=\"form-group m-b-8 p-16 d-flex\">\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@if (showTabBar) {\n <c8y-tabs-outlet\n class=\"elevation-none m-b-16\"\n outletName=\"assetPropertiesDrawerTabs\"\n orientation=\"horizontal\"\n ></c8y-tabs-outlet>\n\n @if (showRegular) {\n <c8y-tab\n [isActive]=\"selectedTab === 'regular'\"\n [tabsOutlet]=\"'assetPropertiesDrawerTabs'\"\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]=\"'assetPropertiesDrawerTabs'\"\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<div class=\"inner-scroll flex-grow\">\n <c8y-asset-property-list\n class=\"bg-component\"\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</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" }]
2828
- }], propDecorators: { title: [{
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
- }], savePropertySelection: [{
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
- * Emits the selected properties when saved.
3397
+ * List of selected properties.
2884
3398
  */
2885
- this.savePropertySelection = new EventEmitter();
3399
+ this.selectedProperties = [];
2886
3400
  /**
2887
- * Emits an event when the selection is canceled.
3401
+ * Flag to allow changing the context (asset) within the drawer.
2888
3402
  */
2889
- this.cancelPropertySelection = new EventEmitter();
3403
+ this.allowChangingContext = false;
2890
3404
  /**
2891
- * List of selected properties.
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.selectedProperties = [];
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
- * Updates the selected properties.
2908
- * @param properties The selected properties.
3432
+ * Handles selection changes from the selector component.
3433
+ * @param selection The selected properties.
2909
3434
  */
2910
- onSelectedProperties(properties) {
2911
- this.selectedProperties = cloneDeep(properties);
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 all selected properties are inactive.
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" }, 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<div class=\"inner-scroll flex-grow\">\n <c8y-asset-property-list\n class=\"bg-component\"\n [asset]=\"asset\"\n [config]=\"config\"\n [extraProperties]=\"extraProperties\"\n (selectedProperties)=\"onSelectedProperties($event)\"\n ></c8y-asset-property-list>\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$1 }, { kind: "component", type: AssetPropertyListComponent, selector: "c8y-asset-property-list", inputs: ["config", "asset", "extraProperties"], outputs: ["selectedProperties"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }] }); }
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$1, AssetPropertyListComponent], template: "<div class=\"card-header separator\">\n <span class=\"h4 card-title\">{{ title | translate }}</span>\n</div>\n\n<div class=\"inner-scroll flex-grow\">\n <c8y-asset-property-list\n class=\"bg-component\"\n [asset]=\"asset\"\n [config]=\"config\"\n [extraProperties]=\"extraProperties\"\n (selectedProperties)=\"onSelectedProperties($event)\"\n ></c8y-asset-property-list>\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" }]
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