@c8y/ngx-components
Version:
Angular modules for Cumulocity IoT applications
240 lines (235 loc) • 28.2 kB
JavaScript
import * as i0 from '@angular/core';
import { Component, Input, Optional } from '@angular/core';
import * as i1 from '@angular/forms';
import { Validators, ControlContainer, NgForm } from '@angular/forms';
import * as i1$1 from '@c8y/ngx-components';
import { CoreModule, MeasurementRealtimeService } from '@c8y/ngx-components';
import * as i2 from '@c8y/ngx-components/context-dashboard';
import * as i4 from '@c8y/ngx-components/datapoint-selector';
import { DatapointSelectorModule } from '@c8y/ngx-components/datapoint-selector';
import * as i5 from '@c8y/ngx-components/icon-selector';
import { IconSelectorModule } from '@c8y/ngx-components/icon-selector';
import * as i6 from 'ngx-bootstrap/popover';
import { PopoverModule } from 'ngx-bootstrap/popover';
import { NEVER, combineLatest } from 'rxjs';
import { map, startWith, distinctUntilChanged, tap, filter, pairwise } from 'rxjs/operators';
import * as i3 from '@angular/common';
function exactlyASingleDatapointActive() {
return (control) => {
const datapoints = control.value;
if (!datapoints || !datapoints.length) {
return null;
}
const activeDatapoints = datapoints.filter(datapoint => datapoint.__active);
if (activeDatapoints.length === 1) {
return null;
}
return { exactlyOneDatapointNeedsToBeActive: true };
};
}
class KpiWidgetConfigComponent {
constructor(formBuilder, form, widgetConfig) {
this.formBuilder = formBuilder;
this.form = form;
this.widgetConfig = widgetConfig;
this.datapointSelectionConfig = {};
this.defaultFormOptions = {
showRedRange: true,
showYellowRange: true
};
this.availableIcons = [];
this.limits = {
fontSizeMax: 72,
fontSizeMin: 18,
numberOfDecimalPlacesMax: 10,
numberOfDecimalPlacesMin: 0
};
}
onBeforeSave(config) {
if (this.formGroup.valid) {
Object.assign(config, this.formGroup.value);
return true;
}
return false;
}
async ngOnInit() {
if (this.widgetConfig.context?.id) {
this.datapointSelectionConfig.contextAsset = this.widgetConfig?.context;
}
this.initForm();
if (this.config?.datapoints) {
this.formGroup.patchValue({ datapoints: this.config.datapoints });
}
}
initForm() {
this.formGroup = this.createForm();
this.form.form.addControl('config', this.formGroup);
this.formGroup.patchValue(this.config);
}
createForm() {
return this.formBuilder.group({
numberOfDecimalPlaces: [
2,
[
Validators.required,
Validators.min(this.limits.numberOfDecimalPlacesMin),
Validators.max(this.limits.numberOfDecimalPlacesMax)
]
],
showTimestamp: [true, []],
showTrend: [true, []],
showIcon: [true, []],
icon: ['water', [Validators.required, Validators.minLength(1)]],
fontSize: [
36,
[
Validators.required,
Validators.min(this.limits.fontSizeMin),
Validators.max(this.limits.fontSizeMax)
]
],
datapoints: this.formBuilder.control(new Array(), [
Validators.required,
Validators.minLength(1),
exactlyASingleDatapointActive()
])
});
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: KpiWidgetConfigComponent, deps: [{ token: i1.FormBuilder }, { token: i1.NgForm }, { token: i2.WidgetConfigComponent }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: KpiWidgetConfigComponent, isStandalone: true, selector: "c8y-kpi-widget-config", inputs: { config: "config" }, ngImport: i0, template: "<div class=\"p-l-24 p-r-24\">\n <form [formGroup]=\"formGroup\" class=\"row no-card-context\">\n <div class=\"col-sm-6 bg-level-0\">\n <c8y-datapoint-selection-list\n [defaultFormOptions]=\"defaultFormOptions\"\n [config]=\"datapointSelectionConfig\"\n [minActiveCount]=\"1\"\n [maxActiveCount]=\"1\"\n formControlName=\"datapoints\"\n name=\"datapoints\"\n class=\"bg-inherit\"\n ></c8y-datapoint-selection-list>\n </div>\n <div class=\"col-sm-6\">\n <div class=\"card-header separator\">\n <div class=\"card-title h4\">{{ 'Layout' | translate }}</div>\n </div>\n <label translate>Icon</label>\n <div class=\"d-flex a-i-center\">\n <c8y-icon-selector-wrapper name=\"icon\" formControlName=\"icon\"></c8y-icon-selector-wrapper>\n </div>\n <c8y-form-group>\n <label [title]=\"'Number of decimal places' | translate\" translate>\n Number of decimal places\n </label>\n <input\n class=\"form-control\"\n formControlName=\"numberOfDecimalPlaces\"\n name=\"numberOfDecimalPlaces\"\n type=\"number\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: 1 }\"\n />\n <c8y-messages\n [show]=\"\n formGroup.controls?.numberOfDecimalPlaces?.touched &&\n formGroup?.controls?.numberOfDecimalPlaces?.errors\n \"\n ></c8y-messages>\n </c8y-form-group>\n <div>\n <label>{{ 'Display' | translate }}</label>\n <div class=\"d-flex gap-16 flex-wrap\">\n <c8y-form-group>\n <label [title]=\"'Show timestamp' | translate\" class=\"c8y-checkbox\">\n <input type=\"checkbox\" formControlName=\"showTimestamp\" name=\"showTimestamp\" />\n <span></span>\n <span translate>Show timestamp</span>\n </label>\n </c8y-form-group>\n\n <c8y-form-group>\n <label [title]=\"'Show icon' | translate\" class=\"c8y-checkbox\">\n <input type=\"checkbox\" formControlName=\"showIcon\" name=\"showIcon\" />\n <span></span>\n <span translate>Show icon</span>\n </label>\n </c8y-form-group>\n\n <c8y-form-group>\n <label [title]=\"'Show trend icon' | translate\" class=\"c8y-checkbox\">\n <input type=\"checkbox\" formControlName=\"showTrend\" name=\"showTrend\" />\n <span></span>\n <span translate>Show trend icon</span>\n <button\n class=\"btn-help btn-help--sm\"\n type=\"button\"\n [attr.aria-label]=\"'Help' | translate\"\n popover=\"{{\n 'Indicates the trend between the last two measurement values.' | translate\n }}\"\n placement=\"right\"\n triggers=\"focus\"\n container=\"body\"\n ></button>\n </label>\n </c8y-form-group>\n </div>\n\n <c8y-form-group>\n <label [title]=\"'Font size of measurement value (px)' | translate\" translate>\n Font size of measurement value (px)\n </label>\n <input\n class=\"form-control\"\n formControlName=\"fontSize\"\n name=\"fontSize\"\n type=\"number\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: 36 }\"\n />\n <c8y-messages\n [show]=\"formGroup.controls?.fontSize?.touched && formGroup?.controls?.fontSize?.errors\"\n ></c8y-messages>\n </c8y-form-group>\n </div>\n </div>\n </form>\n</div>\n", dependencies: [{ kind: "ngmodule", type: CoreModule }, { kind: "pipe", type: i1$1.C8yTranslatePipe, name: "translate" }, { kind: "directive", type: i1$1.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.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.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "component", type: i1$1.FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "component", type: i1$1.MessagesComponent, selector: "c8y-messages", inputs: ["show", "defaults", "helpMessage"] }, { kind: "directive", type: i1$1.RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: DatapointSelectorModule }, { kind: "component", type: i4.DatapointSelectionListComponent, selector: "c8y-datapoint-selection-list", inputs: ["actions", "allowDragAndDrop", "config", "defaultFormOptions", "maxActiveCount", "minActiveCount", "resolveContext", "listTitle"], outputs: ["isValid", "change"] }, { kind: "ngmodule", type: IconSelectorModule }, { kind: "component", type: i5.IconSelectorWrapperComponent, selector: "c8y-icon-selector-wrapper", inputs: ["canRemoveIcon", "selectedIcon", "iconSize"], outputs: ["onSelect"] }, { kind: "ngmodule", type: PopoverModule }, { kind: "directive", type: i6.PopoverDirective, selector: "[popover]", inputs: ["adaptivePosition", "boundariesElement", "popover", "popoverContext", "popoverTitle", "placement", "outsideClick", "triggers", "container", "containerClass", "isOpen", "delay"], outputs: ["onShown", "onHidden"], exportAs: ["bs-popover"] }], viewProviders: [{ provide: ControlContainer, useExisting: NgForm }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: KpiWidgetConfigComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-kpi-widget-config', standalone: true, imports: [CoreModule, DatapointSelectorModule, IconSelectorModule, PopoverModule], viewProviders: [{ provide: ControlContainer, useExisting: NgForm }], template: "<div class=\"p-l-24 p-r-24\">\n <form [formGroup]=\"formGroup\" class=\"row no-card-context\">\n <div class=\"col-sm-6 bg-level-0\">\n <c8y-datapoint-selection-list\n [defaultFormOptions]=\"defaultFormOptions\"\n [config]=\"datapointSelectionConfig\"\n [minActiveCount]=\"1\"\n [maxActiveCount]=\"1\"\n formControlName=\"datapoints\"\n name=\"datapoints\"\n class=\"bg-inherit\"\n ></c8y-datapoint-selection-list>\n </div>\n <div class=\"col-sm-6\">\n <div class=\"card-header separator\">\n <div class=\"card-title h4\">{{ 'Layout' | translate }}</div>\n </div>\n <label translate>Icon</label>\n <div class=\"d-flex a-i-center\">\n <c8y-icon-selector-wrapper name=\"icon\" formControlName=\"icon\"></c8y-icon-selector-wrapper>\n </div>\n <c8y-form-group>\n <label [title]=\"'Number of decimal places' | translate\" translate>\n Number of decimal places\n </label>\n <input\n class=\"form-control\"\n formControlName=\"numberOfDecimalPlaces\"\n name=\"numberOfDecimalPlaces\"\n type=\"number\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: 1 }\"\n />\n <c8y-messages\n [show]=\"\n formGroup.controls?.numberOfDecimalPlaces?.touched &&\n formGroup?.controls?.numberOfDecimalPlaces?.errors\n \"\n ></c8y-messages>\n </c8y-form-group>\n <div>\n <label>{{ 'Display' | translate }}</label>\n <div class=\"d-flex gap-16 flex-wrap\">\n <c8y-form-group>\n <label [title]=\"'Show timestamp' | translate\" class=\"c8y-checkbox\">\n <input type=\"checkbox\" formControlName=\"showTimestamp\" name=\"showTimestamp\" />\n <span></span>\n <span translate>Show timestamp</span>\n </label>\n </c8y-form-group>\n\n <c8y-form-group>\n <label [title]=\"'Show icon' | translate\" class=\"c8y-checkbox\">\n <input type=\"checkbox\" formControlName=\"showIcon\" name=\"showIcon\" />\n <span></span>\n <span translate>Show icon</span>\n </label>\n </c8y-form-group>\n\n <c8y-form-group>\n <label [title]=\"'Show trend icon' | translate\" class=\"c8y-checkbox\">\n <input type=\"checkbox\" formControlName=\"showTrend\" name=\"showTrend\" />\n <span></span>\n <span translate>Show trend icon</span>\n <button\n class=\"btn-help btn-help--sm\"\n type=\"button\"\n [attr.aria-label]=\"'Help' | translate\"\n popover=\"{{\n 'Indicates the trend between the last two measurement values.' | translate\n }}\"\n placement=\"right\"\n triggers=\"focus\"\n container=\"body\"\n ></button>\n </label>\n </c8y-form-group>\n </div>\n\n <c8y-form-group>\n <label [title]=\"'Font size of measurement value (px)' | translate\" translate>\n Font size of measurement value (px)\n </label>\n <input\n class=\"form-control\"\n formControlName=\"fontSize\"\n name=\"fontSize\"\n type=\"number\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: 36 }\"\n />\n <c8y-messages\n [show]=\"formGroup.controls?.fontSize?.touched && formGroup?.controls?.fontSize?.errors\"\n ></c8y-messages>\n </c8y-form-group>\n </div>\n </div>\n </form>\n</div>\n" }]
}], ctorParameters: () => [{ type: i1.FormBuilder }, { type: i1.NgForm }, { type: i2.WidgetConfigComponent }], propDecorators: { config: [{
type: Input
}] } });
var ColorClass;
(function (ColorClass) {
ColorClass["danger"] = "text-danger";
ColorClass["warning"] = "text-warning";
ColorClass["unknown"] = "";
})(ColorClass || (ColorClass = {}));
class KpiWidgetViewComponent {
constructor(measurementRealtime, dashboard) {
this.measurementRealtime = measurementRealtime;
this.dashboard = dashboard;
this.config = { datapoints: [] };
this.state$ = NEVER;
// used to differentiate between loading state and empty state
this.noDataInitiallyInDB = false;
}
async ngOnInit() {
const datapoints = this.config.datapoints || [];
const datapoint = datapoints.find(tmp => tmp.__active);
if (!datapoint) {
return;
}
this.state$ = this.setupObservable(datapoint);
}
setupObservable(datapoint) {
this.assignContextFromContextDashboard(datapoint);
const latestMeasurement$ = this.getLatestMeasurement$(datapoint);
const lastTwoValues$ = this.getLastTwoValuesOfObservable$(latestMeasurement$);
const previousValue$ = lastTwoValues$.pipe(map(([previousVal]) => previousVal), startWith(undefined));
const unit$ = latestMeasurement$.pipe(map(latestMeasurementValue => datapoint.unit || latestMeasurementValue.unit || ''), startWith(''), distinctUntilChanged());
return combineLatest([
latestMeasurement$,
previousValue$,
this.getTrendOfLatestMeasurements$(lastTwoValues$),
unit$,
this.getColorClass$(latestMeasurement$, datapoint)
]).pipe(map(([latestMeasurement, previousValue, trend, unit, colorClass]) => {
return {
latestMeasurement,
previousValue,
trend,
unit,
colorClass
};
}));
}
getLatestMeasurement$(datapoint) {
return this.measurementRealtime
.latestValueOfSpecificMeasurement$(datapoint.fragment, datapoint.series, datapoint.__target,
// we only need the last two values in case we want to show a trend
this.config.showTrend ? 2 : 1,
// null will be emitted in case no measurement was found initially
true)
.pipe(tap(measurement => {
if (!measurement) {
this.noDataInitiallyInDB = true;
}
}), filter(measurement => !!measurement), map(measurement => {
return {
unit: measurement[datapoint.fragment][datapoint.series].unit,
value: measurement[datapoint.fragment][datapoint.series].value,
date: measurement.time
};
}));
}
getColorClass$(measurementAndDatapointCombination$, datapoint) {
return measurementAndDatapointCombination$.pipe(map(latestMeasurementValue => {
if (this.inRangeOf(datapoint, latestMeasurementValue.value, 'redRangeMin', 'redRangeMax')) {
return ColorClass.danger;
}
if (this.inRangeOf(datapoint, latestMeasurementValue.value, 'yellowRangeMin', 'yellowRangeMax')) {
return ColorClass.warning;
}
return ColorClass.unknown;
}), startWith(ColorClass.unknown), distinctUntilChanged());
}
getLastTwoValuesOfObservable$(input$) {
return input$.pipe(pairwise());
}
getTrendOfLatestMeasurements$(latestMeasurement$) {
return latestMeasurement$.pipe(map(res => {
if (res.length === 2) {
const oldValue = res[0].value;
const newValue = res[1].value;
if (oldValue < newValue) {
return '45deg';
}
if (oldValue > newValue) {
return '135deg';
}
}
return '90deg';
}), startWith('90deg'), distinctUntilChanged());
}
inRangeOf(datapoint, measurementValue, minAttribute, maxAttribute) {
if (typeof datapoint[minAttribute] === 'number' &&
typeof datapoint[maxAttribute] === 'number') {
if (measurementValue >= datapoint[minAttribute] &&
measurementValue < datapoint[maxAttribute]) {
return true;
}
}
return false;
}
assignContextFromContextDashboard(datapoint) {
if (!this.dashboard?.isDeviceTypeDashboard) {
return;
}
const context = this.dashboard?.context;
if (context?.id) {
const { name, id } = context;
datapoint.__target = { name, id };
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: KpiWidgetViewComponent, deps: [{ token: i1$1.MeasurementRealtimeService }, { token: i2.ContextDashboardComponent, optional: true }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: KpiWidgetViewComponent, isStandalone: true, selector: "c8y-kpi-widget-view", inputs: { config: "config" }, providers: [MeasurementRealtimeService], ngImport: i0, template: "<div\n class=\"kpi-widget__container d-flex d-col fit-h fit-w a-i-center j-c-center\"\n *ngIf=\"state$ | async as lastState; else noMeasurementFound\"\n>\n <div class=\"d-flex a-i-center j-c-center fit-w\">\n <div\n class=\"m-r-16 flex-no-shrink text-muted\"\n [ngClass]=\"lastState.colorClass\"\n *ngIf=\"config.icon && config.showIcon\"\n >\n <i class=\"icon-32\" [c8yIcon]=\"config.icon\"></i>\n </div>\n <div class=\"text-truncate\">\n <span\n class=\"text-truncate text-medium\"\n [ngClass]=\"lastState.colorClass\"\n [ngStyle]=\"{ 'font-size': (config.fontSize || '36') + 'px' }\"\n title=\"{{\n lastState.colorClass === 'text-danger'\n ? ('Within red range:' | translate)\n : lastState.colorClass === 'text-warning'\n ? ('Within yellow range:' | translate)\n : ''\n }} {{\n lastState.latestMeasurement.value\n | number\n : '1.' +\n (config.numberOfDecimalPlaces || '0') +\n '-' +\n (config.numberOfDecimalPlaces || '0')\n }} {{ lastState.unit || '' }}\"\n >\n {{\n lastState.latestMeasurement.value\n | number\n : '1.' +\n (config.numberOfDecimalPlaces || '0') +\n '-' +\n (config.numberOfDecimalPlaces || '0')\n }}\n <small class=\"text-regular\">{{ lastState.unit || '' }}</small>\n </span>\n </div>\n <div\n class=\"dot dot-info dot-30 m-l-16 flex-no-shrink\"\n *ngIf=\"config?.showTrend && lastState.previousValue as previousValue\"\n >\n <i\n class=\"icon-20\"\n [title]=\"\n ('Previous value' | translate) +\n ': ' +\n (previousValue.value\n | number\n : '1.' +\n (config.numberOfDecimalPlaces || '0') +\n '-' +\n (config.numberOfDecimalPlaces || '0')) +\n ' (' +\n (previousValue.date | date: 'medium') +\n ')'\n \"\n c8yIcon=\"arrow-dotted-up\"\n [ngStyle]=\"{ transform: 'rotate(' + lastState.trend + ')' }\"\n ></i>\n </div>\n </div>\n <div class=\"d-flex j-c-center\">\n <p *ngIf=\"config?.showTimestamp\" class=\"icon-flex text-center text-muted small\">\n <i c8yIcon=\"calendar\"></i>\n {{ lastState.latestMeasurement.date | date: 'medium' }}\n </p>\n </div>\n</div>\n\n<ng-template #noMeasurementFound>\n <div class=\"d-flex fit-h fit-w j-c-center a-i-center\">\n <c8y-ui-empty-state\n *ngIf=\"noDataInitiallyInDB\"\n class=\"fit-w\"\n [icon]=\"'line-chart'\"\n [title]=\"'No measurement to display.' | translate\"\n [subtitle]=\"'Waiting for measurements to be created.' | translate\"\n [horizontal]=\"true\"\n ></c8y-ui-empty-state>\n <c8y-loading *ngIf=\"!noDataInitiallyInDB\"></c8y-loading>\n </div>\n</ng-template>\n", dependencies: [{ kind: "ngmodule", type: CoreModule }, { kind: "component", type: i1$1.EmptyStateComponent, selector: "c8y-ui-empty-state", inputs: ["icon", "title", "subtitle", "horizontal"] }, { kind: "directive", type: i1$1.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "pipe", type: i1$1.C8yTranslatePipe, name: "translate" }, { kind: "directive", type: i3.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i3.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "pipe", type: i3.AsyncPipe, name: "async" }, { kind: "pipe", type: i3.DecimalPipe, name: "number" }, { kind: "pipe", type: i3.DatePipe, name: "date" }, { kind: "component", type: i1$1.LoadingComponent, selector: "c8y-loading", inputs: ["layout", "progress", "message"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: KpiWidgetViewComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-kpi-widget-view', standalone: true, imports: [CoreModule], providers: [MeasurementRealtimeService], template: "<div\n class=\"kpi-widget__container d-flex d-col fit-h fit-w a-i-center j-c-center\"\n *ngIf=\"state$ | async as lastState; else noMeasurementFound\"\n>\n <div class=\"d-flex a-i-center j-c-center fit-w\">\n <div\n class=\"m-r-16 flex-no-shrink text-muted\"\n [ngClass]=\"lastState.colorClass\"\n *ngIf=\"config.icon && config.showIcon\"\n >\n <i class=\"icon-32\" [c8yIcon]=\"config.icon\"></i>\n </div>\n <div class=\"text-truncate\">\n <span\n class=\"text-truncate text-medium\"\n [ngClass]=\"lastState.colorClass\"\n [ngStyle]=\"{ 'font-size': (config.fontSize || '36') + 'px' }\"\n title=\"{{\n lastState.colorClass === 'text-danger'\n ? ('Within red range:' | translate)\n : lastState.colorClass === 'text-warning'\n ? ('Within yellow range:' | translate)\n : ''\n }} {{\n lastState.latestMeasurement.value\n | number\n : '1.' +\n (config.numberOfDecimalPlaces || '0') +\n '-' +\n (config.numberOfDecimalPlaces || '0')\n }} {{ lastState.unit || '' }}\"\n >\n {{\n lastState.latestMeasurement.value\n | number\n : '1.' +\n (config.numberOfDecimalPlaces || '0') +\n '-' +\n (config.numberOfDecimalPlaces || '0')\n }}\n <small class=\"text-regular\">{{ lastState.unit || '' }}</small>\n </span>\n </div>\n <div\n class=\"dot dot-info dot-30 m-l-16 flex-no-shrink\"\n *ngIf=\"config?.showTrend && lastState.previousValue as previousValue\"\n >\n <i\n class=\"icon-20\"\n [title]=\"\n ('Previous value' | translate) +\n ': ' +\n (previousValue.value\n | number\n : '1.' +\n (config.numberOfDecimalPlaces || '0') +\n '-' +\n (config.numberOfDecimalPlaces || '0')) +\n ' (' +\n (previousValue.date | date: 'medium') +\n ')'\n \"\n c8yIcon=\"arrow-dotted-up\"\n [ngStyle]=\"{ transform: 'rotate(' + lastState.trend + ')' }\"\n ></i>\n </div>\n </div>\n <div class=\"d-flex j-c-center\">\n <p *ngIf=\"config?.showTimestamp\" class=\"icon-flex text-center text-muted small\">\n <i c8yIcon=\"calendar\"></i>\n {{ lastState.latestMeasurement.date | date: 'medium' }}\n </p>\n </div>\n</div>\n\n<ng-template #noMeasurementFound>\n <div class=\"d-flex fit-h fit-w j-c-center a-i-center\">\n <c8y-ui-empty-state\n *ngIf=\"noDataInitiallyInDB\"\n class=\"fit-w\"\n [icon]=\"'line-chart'\"\n [title]=\"'No measurement to display.' | translate\"\n [subtitle]=\"'Waiting for measurements to be created.' | translate\"\n [horizontal]=\"true\"\n ></c8y-ui-empty-state>\n <c8y-loading *ngIf=\"!noDataInitiallyInDB\"></c8y-loading>\n </div>\n</ng-template>\n" }]
}], ctorParameters: () => [{ type: i1$1.MeasurementRealtimeService }, { type: i2.ContextDashboardComponent, decorators: [{
type: Optional
}] }], propDecorators: { config: [{
type: Input
}] } });
/**
* Generated bundle index. Do not edit.
*/
export { KpiWidgetConfigComponent, KpiWidgetViewComponent, exactlyASingleDatapointActive };
//# sourceMappingURL=c8y-ngx-components-widgets-implementations-kpi.mjs.map