<div class="datapoint-selector__skip-links">
<button
class="btn btn-default btn-sm sr-only sr-only-focusable"
type="button"
[c8ySkipLink]="'datapoint-selector-available'"
>
{{ 'Skip to available data points' | translate }}
</button>
@if (!hideSelection) {
<button
class="btn btn-default btn-sm sr-only sr-only-focusable"
type="button"
[c8ySkipLink]="'datapoint-selector-selected'"
>
{{ 'Skip to selected data points' | translate }}
</button>
}
</div>
<div
class="d-grid grid__row--1 fit-h"
[ngClass]="{
'grid__col--3-6-3--md': allowChangingContext && !hideSelection,
'grid__col--8-4--md': !allowChangingContext && !hideSelection,
'grid__col--4-8--md': allowChangingContext && hideSelection
}"
>
@if (allowChangingContext) {
<div
class="d-flex d-col p-relative bg-level-1"
[attr.aria-label]="'Assets' | translate"
role="region"
>
<c8y-asset-selector-miller
class="d-contents"
[(ngModel)]="contextAsset"
[asset]="contextAsset"
(onSelected)="selectionChanged($event)"
[container]="''"
[config]="{
view: 'miller',
groupsSelectable: true,
columnHeaders: true,
showChildDevices: true,
showUnassignedDevices: true,
singleColumn: true,
search: allowSearch,
showFilter: true
}"
></c8y-asset-selector-miller>
</div>
}
<!-- center column -->
<!-- named by aria-label rather than by the heading below, because the heading is replaced by an
empty state until an asset is picked and a region without a name is not a landmark -->
<div
class="inner-scroll bg-component"
[attr.aria-label]="selectorTitle | translate"
id="datapoint-selector-available"
tabindex="-1"
role="region"
>
@if (assetSelection | async; as asset) {
<div class="bg-inherit">
<div class="p-l-16 p-r-16 p-t-8 p-b-8 sticky-top bg-inherit separator-bottom">
<h2
class="h5 text-medium text-truncate"
[title]="selectorTitle | translate"
>
{{ selectorTitle | translate }}
</h2>
@if (!loadingDatapoints) {
<div
class="input-group input-group-search m-t-4"
id="search"
>
<input
class="form-control"
[attr.aria-label]="'Search' | translate"
placeholder="{{ 'Search…' | translate }}"
type="search"
[ngModel]="searchString"
(ngModelChange)="searchStringChanged($event)"
/>
<span class="input-group-addon">
@if (!searchString) {
<i c8yIcon="search"></i>
} @else {
<i
class="text-muted"
c8yIcon="times"
(click)="searchStringChanged()"
></i>
}
</span>
</div>
}
</div>
@if (filteredDatapoints$ | async; as filteredDatapoints) {
@if (!loadingDatapoints) {
@if (datapoints$ | async; as datapoints) {
@if (!filteredDatapoints.length) {
<div class="p-16">
<c8y-ui-empty-state
[icon]="'c8y-data-points'"
[title]="emptyStateTitle | translate"
[subtitle]="
datapoints.length
? (emptyStateSubtitleWhenNoMatchingDataPoints | translate)
: (emptyStateSubtitleWhenNoDataPointsInAsset | translate)
"
[horizontal]="true"
></c8y-ui-empty-state>
</div>
}
<c8y-list-group>
@if (
datapoints.length > maxNumberOfDatapoints &&
filteredDatapoints.length >= maxNumberOfDatapoints
) {
<c8y-list-item
class="sticky-top"
style="top: 72px"
>
<div class="alert alert-warning m-b-0">
{{
'Due to the large number, only a subset of data points is displayed. Use search to narrow down the number of results.'
| translate
}}
</div>
</c8y-list-item>
}
@for (dp of filteredDatapoints; track trackByFn($index, dp)) {
<c8y-datapoint-selector-list-item
class="d-contents"
[ngModel]="dp"
[isSelected]="selectedDatapoints | includesDatapoint: dp"
[datapointLibraryEntries]="datapointLibraryEntries"
[disableTypeaheadIfSelected]="true"
[addButtonType]="
hideSelection ? AddButtonTypes.select : AddButtonTypes.addRemove
"
(added)="datapointAdded($event)"
(removed)="datapointRemoved($event)"
[highlightText]="searchStringChanges$ | async"
[editable]="itemsEditable"
></c8y-datapoint-selector-list-item>
}
</c8y-list-group>
}
} @else {
<div class="p-16 text-center">
<c8y-loading></c8y-loading>
</div>
}
} @else {
<div class="p-16 text-center">
<c8y-loading></c8y-loading>
</div>
}
</div>
} @else {
<div class="p-16">
<c8y-ui-empty-state
[icon]="'c8y-data-points'"
[title]="emptyStateTitle | translate"
[subtitle]="'Select an asset from the list.' | translate"
[horizontal]="true"
></c8y-ui-empty-state>
</div>
}
</div>
<!-- last column -->
@if (!hideSelection) {
<div
class="inner-scroll bg-level-1"
aria-labelledby="datapoint-selector-selected-title"
id="datapoint-selector-selected"
tabindex="-1"
role="region"
>
<h2
class="h5 text-medium p-l-16 p-r-16 p-t-8 p-b-8 separator-bottom sticky-top text-truncate"
[title]="selectedListTitle | translate"
id="datapoint-selector-selected-title"
>
{{ selectedListTitle | translate }}
</h2>
@if (selectedDatapoints?.length) {
<div class="d-flex flex-wrap gap-8 p-l-16 p-r-16 p-t-8 p-b-16">
@for (selectedDp of selectedDatapoints; track selectedDp) {
<div class="c8y-datapoint-pill">
<button
class="c8y-datapoint-pill__btn"
[title]="'Remove' | translate"
type="button"
(click)="datapointRemoved(selectedDp)"
>
<i
class="icon-14"
c8yIcon="remove"
></i>
</button>
<div
class="c8y-datapoint-pill__label"
[title]="selectedDp | datapointLabel: { doNotUseLabel: true, includeDevice: true }"
>
<i
class="m-r-4 icon-14"
c8yIcon="circle"
[style.color]="selectedDp.color"
></i>
<span class="text-truncate">
<span class="text-truncate">{{ selectedDp | datapointLabel }}</span>
@if (selectedDp?.__target?.name) {
<small class="text-muted text-10">
{{ selectedDp?.__target?.name }}
</small>
}
</span>
</div>
</div>
}
</div>
}
@if (!selectedDatapoints || !selectedDatapoints.length) {
<div class="p-r-8">
<c8y-ui-empty-state
[icon]="'c8y-data-points'"
[title]="emptyStateTitle | translate"
[subtitle]="
'Select the asset, then on the available data points list, click on the plus button on the desired data point.'
| translate
"
[horizontal]="true"
></c8y-ui-empty-state>
</div>
}
</div>
}
</div>