@if (!isInPreviewMode()) {
<div class="d-flex gap-16 p-r-16 inner-scroll h-auto min-width-0">
@if (displayMode() === GLOBAL_CONTEXT_DISPLAY_MODE.DASHBOARD) {
<c8y-global-context-connector
[controls]="widgetControls()"
[config]="contextConfig()"
[isLoading]="isLoading()"
[dashboardChild]="getDashboardChild()!"
[linked]="isLinkedToGlobal()"
(configChange)="onContextChange($event)"
(refresh)="onRefresh()"
></c8y-global-context-connector>
} @else {
<c8y-local-controls
[controls]="widgetControls()"
[displayMode]="displayMode()"
[config]="contextConfig()"
[isLoading]="isLoading()"
(configChange)="onContextChange($event)"
(refresh)="onRefresh()"
></c8y-local-controls>
}
</div>
}
@if (!isInPreviewMode() && exportConfig(); as config) {
<c8y-widget-action>
<c8y-datapoints-export-selector
[displayMode]="'icon-only'"
[exportConfig]="config"
[containerClass]="'d-contents'"
(isOpen)="onExportModalOpen($event)"
></c8y-datapoints-export-selector>
</c8y-widget-action>
}
@if (!hasNoPermissionsToReadAnyMeasurement()) {
<!-- the .page-sticky-header -->
@if (dataPoints().length > 0) {
<div class="hidden-xs hidden-sm c8y-list__item">
<div class="c8y-list__item__block flex-grow min-width-0">
<div class="c8y-list__item__icon">
<i style="width: 22px"></i>
</div>
<div class="c8y-list__item__body">
<div class="d-flex-md row">
@for (column of visibleColumns(); track column.id) {
<div
[class]="
column.id === 'kpi' || column.id === 'asset'
? 'col-md-3 flex-grow min-width-0'
: column.id == 'diff' || column.id == 'diffPercentage'
? 'col-md-1 flex-grow min-width-0'
: 'col-md-2 flex-grow min-width-0'
"
[class.text-right]="column.id !== 'asset' && column.id !== 'kpi'"
>
<span class="text-medium text-truncate">
{{ column.label | translate }}
</span>
</div>
}
</div>
</div>
</div>
</div>
}
<!-- The record list -->
@if (isLoading() && !hasLoadedOnce()) {
<!-- Initial load: full spinner -->
<ng-container [ngTemplateOutlet]="loading"></ng-container>
} @else {
@if (isLoading()) {
<!-- Refresh: inline loading overlay -->
<div class="p-absolute fit-w overflow-hidden p-b-4">
<c8y-loading [layout]="'page'"></c8y-loading>
</div>
}
@if (dataPoints().length) {
<c8y-list-group
class="flex-grow"
role="list"
c8yVirtualScrollListener
(scrolled)="onListScrolled()"
(scrolledToTop)="onListScrolledToTop()"
>
<c8y-li
role="listitem"
*c8yFor="
let dp of { data: dataPoints(), res: null! };
enableVirtualScroll: true;
virtualScrollElementSize: 48;
virtualScrollStrategy: 'fixed'
"
>
<c8y-li-icon>
<i
c8yIcon="circle"
[style.color]="dp.color"
></i>
</c8y-li-icon>
<c8y-li-body>
<div class="d-flex-md row">
@for (column of visibleColumns(); track column.id) {
@switch (column.id) {
@case ('kpi') {
<div
class="col-md-3 flex-grow"
[attr.data-cy]="'datapointlist-' + column.id"
>
<div class="d-flex a-i-center d-contents-md p-t-4 p-b-4 separator-bottom">
<small
class="text-label-small flex-grow visible-xs-inline-block visible-sm-inline-block"
>{{ column.label | translate }}</small
>
{{ dp.label | translate }}
@if (dp.unit) {
<small class="text-muted">{{ dp.unit }}</small>
}
</div>
</div>
}
@case ('target') {
<div
class="col-md-2 text-right-md flex-grow"
[attr.data-cy]="'datapointlist-' + column.id"
>
<div class="d-flex a-i-center d-contents-md p-t-4 p-b-4 separator-bottom">
<small
class="text-label-small flex-grow visible-xs-inline-block visible-sm-inline-block"
>{{ column.label | translate }}</small
>
<span>{{ dp.target }}</span>
</div>
</div>
}
@case ('current') {
<div
class="col-md-2 text-right-md flex-grow"
[attr.data-cy]="'datapointlist-' + column.id"
>
<div class="d-flex a-i-center d-contents-md p-t-4 p-b-4 separator-bottom">
<small
class="text-label-small flex-grow visible-xs-inline-block visible-sm-inline-block"
>{{ column.label | translate }}</small
>
@let ariaLabel =
'Last updated: {{ date }}'
| translate: { date: dp.timestamp | c8yDate: 'medium' };
<span
[class]="dp.currentValue | applyRangeClass: getRangeValues(dp)"
[title]="dp.timestamp | c8yDate: 'medium'"
[attr.aria-label]="ariaLabel"
>
{{ dp.currentValue | number: dp.currentFractionSize }}
</span>
</div>
</div>
}
@case ('diff') {
<div
class="col-md-1 text-right-md flex-grow"
[attr.data-cy]="'datapointlist-' + column.id"
>
<div class="d-flex a-i-center d-contents-md p-t-4 p-b-4 separator-bottom">
<small
class="text-label-small flex-grow visible-xs-inline-block visible-sm-inline-block"
>{{ column.label | translate }}</small
>
<span>
{{ dp.diffValue | number: dp.diffFractionSize }}
</span>
</div>
</div>
}
@case ('diffPercentage') {
<div
class="col-md-1 text-right-md flex-grow"
[attr.data-cy]="'datapointlist-' + column.id"
>
<div class="d-flex a-i-center d-contents-md p-t-4 p-b-4 separator-bottom">
<small
class="text-label-small flex-grow visible-xs-inline-block visible-sm-inline-block"
>{{ column.label | translate }}</small
>
<span>
{{ dp.diffPercentValue | number: dp.diffPercentFractionSize }}
</span>
</div>
</div>
}
@case ('asset') {
<div
class="col-md-3 flex-grow"
[attr.data-cy]="'datapointlist-' + column.id"
>
<div class="d-flex a-i-center d-contents-md p-t-4 p-b-4 separator-bottom">
<small
class="text-label-small flex-grow visible-xs-inline-block visible-sm-inline-block"
>{{ column.label | translate }}</small
>
@let ariaLabel =
'Navigate to asset: "{{ name }}"'
| translate: { name: dp.__target?.name };
<button
class="btn-clean d-flex a-i-center gap-4 text-muted"
[attr.aria-label]="ariaLabel"
type="button"
(click)="redirectToAsset(dp.__target?.id)"
>
@if (dp.__target?.id && getTargetManagedObject(dp.__target.id); as mo) {
<c8y-device-status
[mo]="mo"
[size]="16"
></c8y-device-status>
}
<small
class="text-truncate"
[title]="dp.__target?.name"
>{{ dp.__target?.name }}</small
>
</button>
</div>
</div>
}
}
}
</div>
</c8y-li-body>
</c8y-li>
</c8y-list-group>
} @else {
<ng-container [ngTemplateOutlet]="emptyState"></ng-container>
}
}
} @else {
<div class="p-t-24 p-r-16 p-l-16 p-b-16 d-flex">
<div class="center-block">
<c8y-dynamic-component-alerts
[alerts]="missingAllPermissionsAlert"
></c8y-dynamic-component-alerts>
</div>
</div>
}
<ng-template #loading>
<c8y-loading></c8y-loading>
</ng-template>
<ng-template #emptyState>
<div class="p-relative p-l-24">
<c8y-ui-empty-state
[icon]="'c8y-alert-idle'"
[title]="'No data to display.' | translate"
[horizontal]="true"
data-cy="datapoints-list--empty-state"
>
<p c8y-guide-docs>
<small
translate
ngNonBindable
>
Find out more in the
<a c8y-guide-href="/docs/cockpit/widgets-collection/#data-point-list"
>user documentation</a
>.
</small>
</p>
</c8y-ui-empty-state>
</div>
</ng-template>