UNPKG

@c8y/ngx-components

Version:

Angular modules for Cumulocity IoT applications

295 lines (290 loc) 33.9 kB
import * as i0 from '@angular/core'; import { Input, Optional, Component, ViewChild } from '@angular/core'; import * as i1 from '@angular/forms'; import { Validators, NgForm, ControlContainer } from '@angular/forms'; import * as i3 from '@c8y/ngx-components'; import { CoreModule, MeasurementRealtimeService } from '@c8y/ngx-components'; import * as i2 from '@c8y/ngx-components/context-dashboard'; import * as i5 from '@c8y/ngx-components/datapoint-selector'; import { DatapointSelectorModule } from '@c8y/ngx-components/datapoint-selector'; import { NEVER, combineLatest, Subject } from 'rxjs'; import { map, startWith, distinctUntilChanged, tap, filter, pairwise, debounceTime, takeUntil } from 'rxjs/operators'; import * as i6 from '@c8y/ngx-components/icon-selector'; import { IconSelectorModule } from '@c8y/ngx-components/icon-selector'; import * as i7 from 'ngx-bootstrap/popover'; import { PopoverModule } from 'ngx-bootstrap/popover'; import * as i3$1 from '@angular/common'; 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: "20.3.15", ngImport: i0, type: KpiWidgetViewComponent, deps: [{ token: i3.MeasurementRealtimeService }, { token: i2.ContextDashboardComponent, optional: true }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", 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: i3.EmptyStateComponent, selector: "c8y-ui-empty-state", inputs: ["icon", "title", "subtitle", "horizontal"] }, { kind: "directive", type: i3.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: i3$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i3$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i3$1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: i3.LoadingComponent, selector: "c8y-loading", inputs: ["layout", "progress", "message"] }, { kind: "pipe", type: i3.C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: i3$1.AsyncPipe, name: "async" }, { kind: "pipe", type: i3$1.DecimalPipe, name: "number" }, { kind: "pipe", type: i3$1.DatePipe, name: "date" }] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", 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: i3.MeasurementRealtimeService }, { type: i2.ContextDashboardComponent, decorators: [{ type: Optional }] }], propDecorators: { config: [{ type: Input }] } }); 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 { set previewMapSet(template) { if (template) { this.widgetConfigService.setPreview(template); return; } this.widgetConfigService.setPreview(null); } constructor(formBuilder, form, widgetConfig, widgetConfigService) { this.formBuilder = formBuilder; this.form = form; this.widgetConfig = widgetConfig; this.widgetConfigService = widgetConfigService; this.datapointSelectionConfig = {}; this.defaultFormOptions = { showRedRange: true, showYellowRange: true }; this.availableIcons = []; this.destroy$ = new Subject(); 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.previewConfig = { ...this.config }; this.initForm(); if (this.config?.datapoints) { this.formGroup.patchValue({ datapoints: this.config.datapoints }); if (this.config.datapoints.length > 0) { this.previewActiveDatapoint = this.config.datapoints.find(dp => dp.__active); } } } ngOnDestroy() { this.destroy$.next(); this.destroy$.complete(); } applyLimits(value, min, max) { if (value < min) { return min; } if (value > max) { return max; } return value; } initForm() { this.formGroup = this.createForm(); this.form.form.addControl('config', this.formGroup); this.formGroup.patchValue(this.config); this.formGroup.valueChanges .pipe(debounceTime(100), takeUntil(this.destroy$)) .subscribe(formValue => { if (formValue.datapoints) { this.previewActiveDatapoint = formValue.datapoints.find(dp => dp.__active); } const previewLimitedValue = this.createPreviewLimitedValue(formValue); this.previewConfig = { ...this.config, ...previewLimitedValue }; Object.assign(this.config, formValue); }); } createPreviewLimitedValue(formValue) { const previewValue = { ...formValue }; if (previewValue.numberOfDecimalPlaces !== undefined) { previewValue.numberOfDecimalPlaces = this.applyLimits(previewValue.numberOfDecimalPlaces, this.limits.numberOfDecimalPlacesMin, this.limits.numberOfDecimalPlacesMax); } if (previewValue.fontSize !== undefined) { previewValue.fontSize = this.applyLimits(previewValue.fontSize, this.limits.fontSizeMin, this.limits.fontSizeMax); } return previewValue; } 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: "20.3.15", ngImport: i0, type: KpiWidgetConfigComponent, deps: [{ token: i1.FormBuilder }, { token: i1.NgForm }, { token: i2.WidgetConfigComponent }, { token: i2.WidgetConfigService }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: KpiWidgetConfigComponent, isStandalone: true, selector: "c8y-kpi-widget-config", inputs: { config: "config" }, viewQueries: [{ propertyName: "previewMapSet", first: true, predicate: ["kpiPreview"], descendants: true }], ngImport: i0, template: "<form [formGroup]=\"formGroup\">\n <c8y-datapoint-selection-list\n class=\"bg-inherit no-card-context\"\n name=\"datapoints\"\n [defaultFormOptions]=\"defaultFormOptions\"\n [config]=\"datapointSelectionConfig\"\n [minActiveCount]=\"1\"\n [maxActiveCount]=\"1\"\n formControlName=\"datapoints\"\n ></c8y-datapoint-selection-list>\n\n <fieldset class=\"c8y-fieldset\">\n <legend>{{ 'Layout' | translate }}</legend>\n <div class=\"row tight-grid\">\n <div class=\"col-md-3\">\n <div class=\"form-group form-group-sm d-flex a-i-center gap-8 m-b-0 m-t-8\">\n <label translate>Icon</label>\n <c8y-icon-selector-wrapper\n name=\"icon\"\n formControlName=\"icon\"\n ></c8y-icon-selector-wrapper>\n </div>\n </div>\n <div class=\"col-md-9\">\n <c8y-form-group class=\"form-group-sm m-b-16\">\n <label\n [title]=\"'Font size of measurement value (px)' | translate\"\n translate\n >\n Font size of measurement value (px)\n </label>\n <input\n class=\"form-control\"\n name=\"fontSize\"\n type=\"number\"\n formControlName=\"fontSize\"\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 </fieldset>\n\n <fieldset class=\"c8y-fieldset\">\n <legend>{{ 'Display' | translate }}</legend>\n <div class=\"d-flex gap-16 flex-wrap\">\n <c8y-form-group class=\"m-b-8\">\n <label\n class=\"c8y-checkbox\"\n [title]=\"'Show timestamp' | translate\"\n >\n <input\n name=\"showTimestamp\"\n type=\"checkbox\"\n formControlName=\"showTimestamp\"\n />\n <span></span>\n <span translate>Show timestamp</span>\n </label>\n </c8y-form-group>\n\n <c8y-form-group class=\"m-b-8\">\n <label\n class=\"c8y-checkbox\"\n [title]=\"'Show icon' | translate\"\n >\n <input\n name=\"showIcon\"\n type=\"checkbox\"\n formControlName=\"showIcon\"\n />\n <span></span>\n <span translate>Show icon</span>\n </label>\n </c8y-form-group>\n\n <c8y-form-group class=\"m-b-8\">\n <label\n class=\"c8y-checkbox\"\n [title]=\"'Show trend icon' | translate\"\n >\n <input\n name=\"showTrend\"\n type=\"checkbox\"\n formControlName=\"showTrend\"\n />\n <span></span>\n <span translate>Show trend icon</span>\n <button\n class=\"btn-help btn-help--sm\"\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 type=\"button\"\n ></button>\n </label>\n </c8y-form-group>\n </div>\n </fieldset>\n\n <fieldset class=\"c8y-fieldset\">\n <legend translate>Number of decimal places</legend>\n <c8y-form-group class=\"form-group-sm m-b-16\">\n <input\n class=\"form-control\"\n name=\"numberOfDecimalPlaces\"\n type=\"number\"\n formControlName=\"numberOfDecimalPlaces\"\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 </fieldset>\n</form>\n\n<ng-template #kpiPreview>\n <ng-container *ngIf=\"formGroup && formGroup.value\">\n <div\n style=\"height: 300px\"\n *ngIf=\"formGroup.value.datapoints?.length > 0 && previewActiveDatapoint; else emptyState\"\n >\n <c8y-kpi-widget-view\n *ngIf=\"previewConfig\"\n [config]=\"previewConfig\"\n ></c8y-kpi-widget-view>\n </div>\n <ng-template #emptyState>\n <div class=\"col-md-6 d-col a-i-start j-c-center\">\n <c8y-ui-empty-state\n [icon]=\"'c8y-data-points'\"\n [title]=\"'No data points selected' | translate\"\n [subtitle]=\"'Select data point to render content' | translate\"\n [horizontal]=\"false\"\n data-cy=\"kpi-widget--empty-state-no-data-point-selected\"\n >\n <p c8y-guide-docs>\n <small translate>\n Find out more in the\n <a c8y-guide-href=\"/docs/cockpit/widgets-collection/#kpi\">user documentation</a>\n .\n </small>\n </p>\n </c8y-ui-empty-state>\n </div>\n </ng-template>\n </ng-container>\n</ng-template>\n", dependencies: [{ kind: "ngmodule", type: CoreModule }, { kind: "component", type: i3.EmptyStateComponent, selector: "c8y-ui-empty-state", inputs: ["icon", "title", "subtitle", "horizontal"] }, { kind: "directive", type: i3.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: i3$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { 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: i3.FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "component", type: i3.MessagesComponent, selector: "c8y-messages", inputs: ["show", "defaults", "helpMessage"] }, { kind: "directive", type: i3.RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "directive", type: i3.GuideHrefDirective, selector: "[c8y-guide-href]", inputs: ["c8y-guide-href"] }, { kind: "component", type: i3.GuideDocsComponent, selector: "[c8y-guide-docs]" }, { 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: i5.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: i6.IconSelectorWrapperComponent, selector: "c8y-icon-selector-wrapper", inputs: ["canRemoveIcon", "selectedIcon", "iconSize"], outputs: ["onSelect"] }, { kind: "ngmodule", type: PopoverModule }, { kind: "directive", type: i7.PopoverDirective, selector: "[popover]", inputs: ["adaptivePosition", "boundariesElement", "popover", "popoverContext", "popoverTitle", "placement", "outsideClick", "triggers", "container", "containerClass", "isOpen", "delay"], outputs: ["onShown", "onHidden"], exportAs: ["bs-popover"] }, { kind: "component", type: KpiWidgetViewComponent, selector: "c8y-kpi-widget-view", inputs: ["config"] }, { kind: "pipe", type: i3.C8yTranslatePipe, name: "translate" }], viewProviders: [{ provide: ControlContainer, useExisting: NgForm }] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: KpiWidgetConfigComponent, decorators: [{ type: Component, args: [{ selector: 'c8y-kpi-widget-config', standalone: true, imports: [ CoreModule, DatapointSelectorModule, IconSelectorModule, PopoverModule, KpiWidgetViewComponent ], viewProviders: [{ provide: ControlContainer, useExisting: NgForm }], template: "<form [formGroup]=\"formGroup\">\n <c8y-datapoint-selection-list\n class=\"bg-inherit no-card-context\"\n name=\"datapoints\"\n [defaultFormOptions]=\"defaultFormOptions\"\n [config]=\"datapointSelectionConfig\"\n [minActiveCount]=\"1\"\n [maxActiveCount]=\"1\"\n formControlName=\"datapoints\"\n ></c8y-datapoint-selection-list>\n\n <fieldset class=\"c8y-fieldset\">\n <legend>{{ 'Layout' | translate }}</legend>\n <div class=\"row tight-grid\">\n <div class=\"col-md-3\">\n <div class=\"form-group form-group-sm d-flex a-i-center gap-8 m-b-0 m-t-8\">\n <label translate>Icon</label>\n <c8y-icon-selector-wrapper\n name=\"icon\"\n formControlName=\"icon\"\n ></c8y-icon-selector-wrapper>\n </div>\n </div>\n <div class=\"col-md-9\">\n <c8y-form-group class=\"form-group-sm m-b-16\">\n <label\n [title]=\"'Font size of measurement value (px)' | translate\"\n translate\n >\n Font size of measurement value (px)\n </label>\n <input\n class=\"form-control\"\n name=\"fontSize\"\n type=\"number\"\n formControlName=\"fontSize\"\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 </fieldset>\n\n <fieldset class=\"c8y-fieldset\">\n <legend>{{ 'Display' | translate }}</legend>\n <div class=\"d-flex gap-16 flex-wrap\">\n <c8y-form-group class=\"m-b-8\">\n <label\n class=\"c8y-checkbox\"\n [title]=\"'Show timestamp' | translate\"\n >\n <input\n name=\"showTimestamp\"\n type=\"checkbox\"\n formControlName=\"showTimestamp\"\n />\n <span></span>\n <span translate>Show timestamp</span>\n </label>\n </c8y-form-group>\n\n <c8y-form-group class=\"m-b-8\">\n <label\n class=\"c8y-checkbox\"\n [title]=\"'Show icon' | translate\"\n >\n <input\n name=\"showIcon\"\n type=\"checkbox\"\n formControlName=\"showIcon\"\n />\n <span></span>\n <span translate>Show icon</span>\n </label>\n </c8y-form-group>\n\n <c8y-form-group class=\"m-b-8\">\n <label\n class=\"c8y-checkbox\"\n [title]=\"'Show trend icon' | translate\"\n >\n <input\n name=\"showTrend\"\n type=\"checkbox\"\n formControlName=\"showTrend\"\n />\n <span></span>\n <span translate>Show trend icon</span>\n <button\n class=\"btn-help btn-help--sm\"\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 type=\"button\"\n ></button>\n </label>\n </c8y-form-group>\n </div>\n </fieldset>\n\n <fieldset class=\"c8y-fieldset\">\n <legend translate>Number of decimal places</legend>\n <c8y-form-group class=\"form-group-sm m-b-16\">\n <input\n class=\"form-control\"\n name=\"numberOfDecimalPlaces\"\n type=\"number\"\n formControlName=\"numberOfDecimalPlaces\"\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 </fieldset>\n</form>\n\n<ng-template #kpiPreview>\n <ng-container *ngIf=\"formGroup && formGroup.value\">\n <div\n style=\"height: 300px\"\n *ngIf=\"formGroup.value.datapoints?.length > 0 && previewActiveDatapoint; else emptyState\"\n >\n <c8y-kpi-widget-view\n *ngIf=\"previewConfig\"\n [config]=\"previewConfig\"\n ></c8y-kpi-widget-view>\n </div>\n <ng-template #emptyState>\n <div class=\"col-md-6 d-col a-i-start j-c-center\">\n <c8y-ui-empty-state\n [icon]=\"'c8y-data-points'\"\n [title]=\"'No data points selected' | translate\"\n [subtitle]=\"'Select data point to render content' | translate\"\n [horizontal]=\"false\"\n data-cy=\"kpi-widget--empty-state-no-data-point-selected\"\n >\n <p c8y-guide-docs>\n <small translate>\n Find out more in the\n <a c8y-guide-href=\"/docs/cockpit/widgets-collection/#kpi\">user documentation</a>\n .\n </small>\n </p>\n </c8y-ui-empty-state>\n </div>\n </ng-template>\n </ng-container>\n</ng-template>\n" }] }], ctorParameters: () => [{ type: i1.FormBuilder }, { type: i1.NgForm }, { type: i2.WidgetConfigComponent }, { type: i2.WidgetConfigService }], propDecorators: { previewMapSet: [{ type: ViewChild, args: ['kpiPreview'] }], config: [{ type: Input }] } }); /** * Generated bundle index. Do not edit. */ export { KpiWidgetConfigComponent, KpiWidgetViewComponent, exactlyASingleDatapointActive }; //# sourceMappingURL=c8y-ngx-components-widgets-implementations-kpi.mjs.map