UNPKG

@c8y/ngx-components

Version:

Angular modules for Cumulocity IoT applications

243 lines (238 loc) 17.9 kB
import * as i0 from '@angular/core'; import { Input, Optional, Component, ViewChild } from '@angular/core'; import { BehaviorSubject, Subject, takeUntil } from 'rxjs'; import { filter, map, distinctUntilChanged, switchMap, shareReplay, debounceTime } from 'rxjs/operators'; import * as i1 from '@angular/forms'; import { Validators, ReactiveFormsModule } from '@angular/forms'; import * as i2 from '@c8y/ngx-components/context-dashboard'; import * as i3 from '@c8y/ngx-components'; import { DynamicComponentAlert, RangeDisplayModule, MeasurementRealtimeService, CoreModule } from '@c8y/ngx-components'; import { DatapointSelectorModule } from '@c8y/ngx-components/datapoint-selector'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { gettext } from '@c8y/ngx-components/gettext'; import { defaultWidgetIds } from '@c8y/ngx-components/widgets/definitions'; import { NgIf, NgClass, AsyncPipe } from '@angular/common'; class LinearGaugeWidgetViewComponent { constructor(measurementRealtime, dashboard, dynamicComponent) { this.measurementRealtime = measurementRealtime; this.dashboard = dashboard; this.dynamicComponent = dynamicComponent; this.activeDatapoint$ = new BehaviorSubject(null); const activeDatapoint = this.activeDatapoint$.pipe(filter(dp => !!dp), map(dp => this.assignContextFromContextDashboard(dp)), distinctUntilChanged()); this.rangeDisplayConfig$ = activeDatapoint.pipe(switchMap(dp => this.getRangeDisplayConfig$(dp)), shareReplay({ refCount: true, bufferSize: 1 })); this.rangeDisplayConfig$ .pipe(map(data => this.getErrorType(data)), distinctUntilChanged(), takeUntilDestroyed()) .subscribe(inRange => this.updateAlertStatus(inRange)); } ngOnChanges() { const activeDp = this.config.datapoints.find(dp => dp.__active); this.activeDatapoint$.next(activeDp); } getRangeDisplayConfig$(dp) { return this.measurementRealtime .latestValueOfSpecificMeasurement$(dp.fragment, dp.series, dp.__target, 1, true) .pipe(map(m => { if (!m) { return null; } const date = m.time; const measurement = m[dp.fragment][dp.series]; return { current: measurement.value, fractionSize: this.config.fractionSize || 1, max: this.ensureIsNumber(dp.max), min: this.ensureIsNumber(dp.min), redRangeMax: this.ensureIsNumber(dp.redRangeMax), redRangeMin: this.ensureIsNumber(dp.redRangeMin), target: this.ensureIsNumber(dp.target), time: date, yellowRangeMax: this.ensureIsNumber(dp.yellowRangeMax), yellowRangeMin: this.ensureIsNumber(dp.yellowRangeMin), unit: measurement.unit || dp.unit, orientation: this.getOrientation() }; })); } ensureIsNumber(value) { if (typeof value === 'number') { return value; } if (typeof value === 'string' && value.trim().length > 0) { const parsedValue = parseFloat(value); if (!isNaN(parsedValue)) { return parsedValue; } } return undefined; } assignContextFromContextDashboard(datapoint) { if (!this.dashboard?.isDeviceTypeDashboard) { return datapoint; } const context = this.dashboard?.context; if (context?.id) { const { name, id } = context; datapoint.__target = { name, id }; } return datapoint; } getOrientation() { return this.dynamicComponent?.componentId === defaultWidgetIds.LINEAR_GAUGE ? 'horizontal' : 'vertical'; } getErrorType(data) { if (!data) { return 'NOT_FOUND'; } if (!this.isInRange(data)) { return 'OUT_OF_RANGE'; } return 'NONE'; } isInRange(data) { if (!Number.isFinite(data.max) || !Number.isFinite(data.min)) { // default range is 0-100 return data.current <= 100 && data.current >= 0; } return data.current <= data.max && data.current >= data.min; } updateAlertStatus(errorType) { if (!this.alerts) { return; } this.alerts.clear(); let msg; if (errorType === 'OUT_OF_RANGE') { msg = gettext('Current value out of defined range.'); } else if (errorType === 'NOT_FOUND') { msg = gettext('Configured data point not available on the selected device.'); } if (!msg) { return; } this.alerts.addAlerts(new DynamicComponentAlert({ type: 'warning', text: msg })); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: LinearGaugeWidgetViewComponent, deps: [{ token: i3.MeasurementRealtimeService }, { token: i2.ContextDashboardComponent, optional: true }, { token: i3.DynamicComponentComponent, optional: true }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.19", type: LinearGaugeWidgetViewComponent, isStandalone: true, selector: "c8y-linear-gauge-widget-view", inputs: { config: "config" }, providers: [MeasurementRealtimeService], usesOnChanges: true, ngImport: i0, template: "<div\n class=\"p-l-16 p-r-16 p-b-16 fit-h d-flex d-col flex-center\"\n *ngIf=\"rangeDisplayConfig$ | async as rangeDisplayConfig\"\n [ngClass]=\"{\n 'p-t-40 j-c-center': rangeDisplayConfig.orientation === 'horizontal',\n }\"\n>\n <c8y-range-display [config]=\"rangeDisplayConfig\" [ngClass]=\"{'flex-grow': rangeDisplayConfig.orientation == 'vertical'}\"></c8y-range-display>\n</div>\n", dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: RangeDisplayModule }, { kind: "component", type: i3.RangeDisplayComponent, selector: "c8y-range-display", inputs: ["config", "display"] }, { kind: "pipe", type: AsyncPipe, name: "async" }] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: LinearGaugeWidgetViewComponent, decorators: [{ type: Component, args: [{ selector: 'c8y-linear-gauge-widget-view', providers: [MeasurementRealtimeService], standalone: true, imports: [NgIf, NgClass, AsyncPipe, RangeDisplayModule], template: "<div\n class=\"p-l-16 p-r-16 p-b-16 fit-h d-flex d-col flex-center\"\n *ngIf=\"rangeDisplayConfig$ | async as rangeDisplayConfig\"\n [ngClass]=\"{\n 'p-t-40 j-c-center': rangeDisplayConfig.orientation === 'horizontal',\n }\"\n>\n <c8y-range-display [config]=\"rangeDisplayConfig\" [ngClass]=\"{'flex-grow': rangeDisplayConfig.orientation == 'vertical'}\"></c8y-range-display>\n</div>\n" }] }], ctorParameters: () => [{ type: i3.MeasurementRealtimeService }, { type: i2.ContextDashboardComponent, decorators: [{ type: Optional }] }, { type: i3.DynamicComponentComponent, decorators: [{ type: Optional }] }], propDecorators: { config: [{ type: Input }] } }); class LinearGaugeWidgetConfigComponent { 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, showRange: true, showTarget: true }; this.limits = { numberOfDecimalPlacesMax: 10, numberOfDecimalPlacesMin: 0 }; this.destroy$ = new Subject(); } onBeforeSave(config) { if (this.formGroup.valid) { Object.assign(config, this.formGroup.value); return true; } return false; } ngOnChanges(changes) { if (this.formGroup && changes.config) { this.formGroup.controls.datapoints.patchValue(this.config.datapoints || []); } } ngOnInit() { if (this.widgetConfig.context?.id) { this.datapointSelectionConfig.contextAsset = this.widgetConfig?.context; } // Initialize preview config this.previewConfig = { ...this.config }; this.initForm(); if (this.config?.datapoints) { this.formGroup.patchValue({ datapoints: this.config.datapoints }); } if (typeof this.config?.fractionSize === 'number' && !Number.isNaN(this.config?.fractionSize)) { this.formGroup.patchValue({ fractionSize: this.config.fractionSize }); } } ngOnDestroy() { this.destroy$.next(); this.destroy$.complete(); } initForm() { this.formGroup = this.createForm(); this.form.form.addControl('config', this.formGroup); this.formGroup.patchValue(this.config); // Update preview on form changes this.formGroup.valueChanges .pipe(debounceTime(100), takeUntil(this.destroy$)) .subscribe(formValue => { if (formValue.datapoints) { this.previewActiveDatapoint = formValue.datapoints.find(dp => dp.__active); } this.previewConfig = { ...this.config, ...formValue }; Object.assign(this.config, formValue); }); } createForm() { return this.formBuilder.group({ fractionSize: [ 2, [ Validators.required, Validators.min(this.limits.numberOfDecimalPlacesMin), Validators.max(this.limits.numberOfDecimalPlacesMax) ] ], datapoints: this.formBuilder.control(new Array(), [Validators.required]) }); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: LinearGaugeWidgetConfigComponent, deps: [{ token: i1.FormBuilder }, { token: i1.NgForm }, { token: i2.WidgetConfigComponent }, { token: i2.WidgetConfigService }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: LinearGaugeWidgetConfigComponent, isStandalone: true, selector: "c8y-linear-gauge-widget-config", inputs: { config: "config" }, viewQueries: [{ propertyName: "previewMapSet", first: true, predicate: ["linearGaugePreview"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<form\n class=\"no-card-context\"\n [formGroup]=\"formGroup\"\n>\n <fieldset class=\"c8y-fieldset\">\n <legend translate>Decimal places</legend>\n <c8y-form-group class=\"form-group-sm m-b-16\">\n <input\n class=\"form-control\"\n name=\"fractionSize\"\n type=\"number\"\n formControlName=\"fractionSize\"\n step=\"1\"\n />\n <c8y-messages [show]=\"formGroup.controls.fractionSize.errors\"></c8y-messages>\n </c8y-form-group>\n </fieldset>\n</form>\n\n<ng-template #linearGaugePreview>\n @if (formGroup && formGroup.value) {\n @if (formGroup.value.datapoints?.length > 0 && previewActiveDatapoint) {\n <div style=\"height: 300px\">\n @if (previewConfig) {\n <c8y-linear-gauge-widget-view [config]=\"previewConfig\"></c8y-linear-gauge-widget-view>\n }\n </div>\n } @else {\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=\"linear-gauge--empty-state-no-data-point-selected\"\n >\n <p c8y-guide-docs>\n <small\n translate\n ngNonBindable\n >\n Find out more in the\n <a c8y-guide-href=\"/docs/cockpit/widgets-collection/#linear-gauge\">\n user documentation</a\n >.\n </small>\n </p>\n </c8y-ui-empty-state>\n </div>\n }\n }\n</ng-template>\n", dependencies: [{ kind: "ngmodule", type: DatapointSelectorModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { 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.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { 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: 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: "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", "additionalMessages"] }, { 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: "component", type: LinearGaugeWidgetViewComponent, selector: "c8y-linear-gauge-widget-view", inputs: ["config"] }, { kind: "pipe", type: i3.C8yTranslatePipe, name: "translate" }] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: LinearGaugeWidgetConfigComponent, decorators: [{ type: Component, args: [{ selector: 'c8y-linear-gauge-widget-config', standalone: true, imports: [ DatapointSelectorModule, ReactiveFormsModule, CoreModule, LinearGaugeWidgetViewComponent ], template: "<form\n class=\"no-card-context\"\n [formGroup]=\"formGroup\"\n>\n <fieldset class=\"c8y-fieldset\">\n <legend translate>Decimal places</legend>\n <c8y-form-group class=\"form-group-sm m-b-16\">\n <input\n class=\"form-control\"\n name=\"fractionSize\"\n type=\"number\"\n formControlName=\"fractionSize\"\n step=\"1\"\n />\n <c8y-messages [show]=\"formGroup.controls.fractionSize.errors\"></c8y-messages>\n </c8y-form-group>\n </fieldset>\n</form>\n\n<ng-template #linearGaugePreview>\n @if (formGroup && formGroup.value) {\n @if (formGroup.value.datapoints?.length > 0 && previewActiveDatapoint) {\n <div style=\"height: 300px\">\n @if (previewConfig) {\n <c8y-linear-gauge-widget-view [config]=\"previewConfig\"></c8y-linear-gauge-widget-view>\n }\n </div>\n } @else {\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=\"linear-gauge--empty-state-no-data-point-selected\"\n >\n <p c8y-guide-docs>\n <small\n translate\n ngNonBindable\n >\n Find out more in the\n <a c8y-guide-href=\"/docs/cockpit/widgets-collection/#linear-gauge\">\n user documentation</a\n >.\n </small>\n </p>\n </c8y-ui-empty-state>\n </div>\n }\n }\n</ng-template>\n" }] }], ctorParameters: () => [{ type: i1.FormBuilder }, { type: i1.NgForm }, { type: i2.WidgetConfigComponent }, { type: i2.WidgetConfigService }], propDecorators: { previewMapSet: [{ type: ViewChild, args: ['linearGaugePreview'] }], config: [{ type: Input }] } }); /** * Generated bundle index. Do not edit. */ export { LinearGaugeWidgetConfigComponent, LinearGaugeWidgetViewComponent }; //# sourceMappingURL=c8y-ngx-components-widgets-implementations-linear-gauge.mjs.map