UNPKG

@c8y/ngx-components

Version:

Angular modules for Cumulocity IoT applications

567 lines (559 loc) 94.3 kB
import * as i0 from '@angular/core'; import { Injectable, forwardRef, Input, Component, EventEmitter, Output, Pipe, ContentChild, Optional, NgModule } from '@angular/core'; import * as i1 from '@angular/forms'; import { FormControl, Validators, NG_VALUE_ACCESSOR, NG_VALIDATORS } from '@angular/forms'; import { take, takeUntil, map, startWith, tap, switchMap, shareReplay, distinctUntilChanged, debounceTime } from 'rxjs/operators'; import * as i3 from '@angular/common'; import * as i2 from '@c8y/ngx-components'; import { gettext, ViewContext, EmptyStateComponent, CommonModule, CoreModule } from '@c8y/ngx-components'; import * as i8 from '@angular/cdk/drag-drop'; import { moveItemInArray, DragDropModule } from '@angular/cdk/drag-drop'; import * as i6$1 from '@c8y/ngx-components/context-dashboard'; import * as i1$2 from 'ngx-bootstrap/modal'; import { Subject, BehaviorSubject, combineLatest } from 'rxjs'; import * as i1$1 from '@c8y/client'; import { uniqBy } from 'lodash-es'; import * as i5 from '@c8y/ngx-components/assets-navigator'; import { AssetSelectorModule } from '@c8y/ngx-components/assets-navigator'; import * as i6 from 'ngx-bootstrap/collapse'; import { CollapseModule } from 'ngx-bootstrap/collapse'; import * as i4 from 'ngx-bootstrap/popover'; import { PopoverModule } from 'ngx-bootstrap/popover'; import * as i4$1 from '@angular/router'; import { RouterModule } from '@angular/router'; import { TooltipModule } from 'ngx-bootstrap/tooltip'; class AlarmEventAttributesFormService { duplicateTypeValidator(selectedItems) { return (control) => { if (!control.value || !selectedItems?.length) { return null; } const isDuplicate = selectedItems.some(item => item.filters?.type?.toLowerCase() === control.value.toLowerCase()); return isDuplicate ? { duplicateType: true } : null; }; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmEventAttributesFormService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmEventAttributesFormService, providedIn: 'root' }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmEventAttributesFormService, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }] }); class AlarmEventAttributesFormComponent { constructor(formBuilder, alarmEventAttributesFormService) { this.formBuilder = formBuilder; this.alarmEventAttributesFormService = alarmEventAttributesFormService; this.omitProperties = {}; this.selectedItems = []; this.datapoints = []; } ngOnInit() { this.formGroup = this.formBuilder.group({ filters: this.formBuilder.group({ type: new FormControl('', [ Validators.required, this.alarmEventAttributesFormService.duplicateTypeValidator(this.selectedItems) ]) }), timelineType: new FormControl(''), selectedDatapoint: new FormControl(null) }); if (!this.omitProperties.label) { this.formGroup.addControl('label', new FormControl('', Validators.required)); } if (this.datapoints?.length > 0) { this.filterKpis(); } } validate(_control) { return this.formGroup?.valid ? null : { formInvalid: {} }; } writeValue(obj) { this.formGroup.patchValue(obj); } registerOnChange(fn) { this.formGroup.valueChanges.subscribe(fn); } registerOnTouched(fn) { this.formGroup.valueChanges.pipe(take(1)).subscribe(fn); } setDisabledState(isDisabled) { isDisabled ? this.formGroup.disable() : this.formGroup.enable(); } changeDatapointSelection(event) { const selectElement = event.target; const selectedIndex = selectElement.selectedIndex; const selectedDatapoint = this.datapoints[selectedIndex]; const trimmedDatapoint = { target: selectedDatapoint.__target.id, fragment: selectedDatapoint.fragment, series: selectedDatapoint.series }; this.formGroup.patchValue({ selectedDatapoint: trimmedDatapoint }); } trackByFn(_index, item) { return `${item.fragment}-${item.series}-${item.__target?.id}`; } filterKpis() { if (this.target && this.target.id) { this.datapoints = this.datapoints.filter(dp => dp.__target?.id === this.target.id); } } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmEventAttributesFormComponent, deps: [{ token: i1.FormBuilder }, { token: AlarmEventAttributesFormService }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: AlarmEventAttributesFormComponent, isStandalone: false, selector: "c8y-alarm-event-attributes-form", inputs: { timelineType: "timelineType", omitProperties: "omitProperties", selectedItems: "selectedItems", target: "target", datapoints: "datapoints" }, providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => AlarmEventAttributesFormComponent), multi: true }, { provide: NG_VALIDATORS, useExisting: forwardRef(() => AlarmEventAttributesFormComponent), multi: true } ], ngImport: i0, template: "<div [formGroup]=\"formGroup\">\n <c8y-form-group\n class=\"form-group-sm\"\n *ngIf=\"formGroup.controls.label\"\n >\n <label class=\"m-0\">{{ 'Label' | translate }}</label>\n <input\n class=\"form-control flex-grow\"\n name=\"label\"\n formControlName=\"label\"\n [placeholder]=\"\n 'e.g. {{ example }}'\n | translate\n : {\n example: timelineType === 'ALARM' ? 'Alarm unavailable' : 'Location update'\n }\n \"\n />\n </c8y-form-group>\n\n <div formGroupName=\"filters\">\n <c8y-form-group class=\"form-group-sm\">\n <label class=\"m-0\">{{ 'Type' | translate }}</label>\n <input\n class=\"form-control flex-grow\"\n name=\"type\"\n formControlName=\"type\"\n [placeholder]=\"\n 'e.g. {{ example }}'\n | translate\n : {\n example:\n timelineType === 'ALARM' ? 'c8y_UnavailabilityAlarm' : 'c8y_LocationUpdate'\n }\n \"\n />\n <c8y-messages>\n <c8y-message\n name=\"duplicateType\"\n [text]=\"'Type already used.' | translate\"\n ></c8y-message>\n </c8y-messages>\n </c8y-form-group>\n </div>\n\n <c8y-form-group\n class=\"form-group-sm\"\n *ngIf=\"datapoints?.length > 0\"\n >\n <label>{{ 'Select data point' | translate }}</label>\n <div class=\"c8y-select-wrapper\">\n <select\n class=\"form-control input-sm\"\n (change)=\"changeDatapointSelection($event)\"\n >\n <option\n title=\"{{ 'Widget configuration' | translate }}\"\n *ngFor=\"let datapoint of datapoints; trackBy: trackByFn\"\n [ngValue]=\"{\n target: datapoint.__target.id,\n fragment: datapoint.fragment,\n series: datapoint.series\n }\"\n [selected]=\"\n datapoint.__target.id === formGroup.value.selectedDatapoint?.target &&\n datapoint.fragment === formGroup.value.selectedDatapoint?.fragment &&\n datapoint.series === formGroup.value.selectedDatapoint?.series\n \"\n >\n {{ datapoint?.label }}\n </option>\n </select>\n </div>\n </c8y-form-group>\n</div>\n", dependencies: [{ kind: "directive", type: i3.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { 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.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "component", type: i2.FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "directive", type: i2.MessageDirective, selector: "c8y-message", inputs: ["name", "text"] }, { kind: "component", type: i2.MessagesComponent, selector: "c8y-messages", inputs: ["show", "defaults", "helpMessage"] }, { kind: "directive", type: i2.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: "directive", type: i1.FormGroupName, selector: "[formGroupName]", inputs: ["formGroupName"] }, { kind: "pipe", type: i2.C8yTranslatePipe, name: "translate" }] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmEventAttributesFormComponent, decorators: [{ type: Component, args: [{ selector: 'c8y-alarm-event-attributes-form', providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => AlarmEventAttributesFormComponent), multi: true }, { provide: NG_VALIDATORS, useExisting: forwardRef(() => AlarmEventAttributesFormComponent), multi: true } ], standalone: false, template: "<div [formGroup]=\"formGroup\">\n <c8y-form-group\n class=\"form-group-sm\"\n *ngIf=\"formGroup.controls.label\"\n >\n <label class=\"m-0\">{{ 'Label' | translate }}</label>\n <input\n class=\"form-control flex-grow\"\n name=\"label\"\n formControlName=\"label\"\n [placeholder]=\"\n 'e.g. {{ example }}'\n | translate\n : {\n example: timelineType === 'ALARM' ? 'Alarm unavailable' : 'Location update'\n }\n \"\n />\n </c8y-form-group>\n\n <div formGroupName=\"filters\">\n <c8y-form-group class=\"form-group-sm\">\n <label class=\"m-0\">{{ 'Type' | translate }}</label>\n <input\n class=\"form-control flex-grow\"\n name=\"type\"\n formControlName=\"type\"\n [placeholder]=\"\n 'e.g. {{ example }}'\n | translate\n : {\n example:\n timelineType === 'ALARM' ? 'c8y_UnavailabilityAlarm' : 'c8y_LocationUpdate'\n }\n \"\n />\n <c8y-messages>\n <c8y-message\n name=\"duplicateType\"\n [text]=\"'Type already used.' | translate\"\n ></c8y-message>\n </c8y-messages>\n </c8y-form-group>\n </div>\n\n <c8y-form-group\n class=\"form-group-sm\"\n *ngIf=\"datapoints?.length > 0\"\n >\n <label>{{ 'Select data point' | translate }}</label>\n <div class=\"c8y-select-wrapper\">\n <select\n class=\"form-control input-sm\"\n (change)=\"changeDatapointSelection($event)\"\n >\n <option\n title=\"{{ 'Widget configuration' | translate }}\"\n *ngFor=\"let datapoint of datapoints; trackBy: trackByFn\"\n [ngValue]=\"{\n target: datapoint.__target.id,\n fragment: datapoint.fragment,\n series: datapoint.series\n }\"\n [selected]=\"\n datapoint.__target.id === formGroup.value.selectedDatapoint?.target &&\n datapoint.fragment === formGroup.value.selectedDatapoint?.fragment &&\n datapoint.series === formGroup.value.selectedDatapoint?.series\n \"\n >\n {{ datapoint?.label }}\n </option>\n </select>\n </div>\n </c8y-form-group>\n</div>\n" }] }], ctorParameters: () => [{ type: i1.FormBuilder }, { type: AlarmEventAttributesFormService }], propDecorators: { timelineType: [{ type: Input }], omitProperties: [{ type: Input }], selectedItems: [{ type: Input }], target: [{ type: Input }], datapoints: [{ type: Input }] } }); const EVENT_TEXTS = { listTitle: gettext('Events'), emptyStateIcon: 'online1', emptyStateTitle: gettext('No events to display.'), emptyStateSubtitle: gettext('Add your first event.'), addButtonLabel: gettext('Add event'), addCustomItemButtonLabel: gettext('Add custom event'), selectorTitle: gettext('Events selector'), availableItemsTitle: gettext('Available events'), assetWithNoItemsEmptyStateSubtitle: gettext('Select an asset with events from the list.'), largeNumberOfItemsInfo: gettext('Due to the large number, only a subset of events are displayed. Use search to narrow down the number of results.'), selectedItemsTitle: gettext('Selected events'), noSelectedItemsTitle: gettext('No events selected.'), recentItemsWarningTitle: gettext('The list below may not be complete.'), recentItemsWarningText: gettext('Recent events are displayed below. Past events might not be shown.'), addCustomText: gettext('Optionally you can add a custom event.') }; const ALARM_TEXTS = { listTitle: gettext('Alarms'), emptyStateIcon: 'c8y-alarm', emptyStateTitle: gettext('No alarms to display.'), emptyStateSubtitle: gettext('Add your first alarm.'), addButtonLabel: gettext('Add alarm'), addCustomItemButtonLabel: gettext('Add custom alarm'), selectorTitle: gettext('Alarms selector'), availableItemsTitle: gettext('Available alarms'), assetWithNoItemsEmptyStateSubtitle: gettext('Select an asset with alarms from the list.'), largeNumberOfItemsInfo: gettext('Due to the large number, only a subset of alarms are displayed. Use search to narrow down the number of results.'), selectedItemsTitle: gettext('Selected alarms'), noSelectedItemsTitle: gettext('No alarms selected.'), recentItemsWarningTitle: gettext('The list below may not be complete.'), recentItemsWarningText: gettext('Recent alarms are displayed below. Past alarms might not be shown.'), addCustomText: gettext('Optionally you can add a custom alarm.') }; class AlarmEventSelectorService { constructor(alarmService, eventsService, color) { this.alarmService = alarmService; this.eventsService = eventsService; this.color = color; this.timelineTypeTextsMap = new Map([ ['ALARM', ALARM_TEXTS], ['EVENT', EVENT_TEXTS] ]); } /** * This method returns the texts for the timeline type. * @param timelineType The timeline type. * @returns The texts for the timeline type. */ timelineTypeTexts(timelineType) { return this.timelineTypeTextsMap.get(timelineType); } /** * This method returns all alarms or events of the platform based on the timeline type * @param timelineType The timeline type. * @returns The alarms or events of the asset. */ async getItems(timelineType) { const filters = { pageSize: 1000 }; return timelineType === 'ALARM' ? await this.getAlarmsOfAsset(filters) : await this.getEventsOfAsset(filters); } /** * This method returns the items of the asset based on the timeline type. * @param parentReference The parent reference. * @param timelineType The timeline type. * @returns The alarms or events of the asset. */ async getAlarmsOrEvents(parentReference, timelineType) { const filters = { source: parentReference.id, pageSize: 1000 }; return timelineType === 'ALARM' ? await this.getAlarmsOfAsset(filters) : await this.getEventsOfAsset(filters); } async getUniqueAlarmsOnly(data) { return Promise.all(uniqBy(data, 'type').map(async (alarm) => this.createItem('ALARM', alarm))); } async getUniqueEventsOnly(data) { return Promise.all(uniqBy(data, 'type').map(async (event) => this.createItem('EVENT', event))); } async createItem(timelineType, item) { const color = await this.color.generateColor(item.type); if (item.source && !item.source.name) { item.source.name = `Device ${item.source.id}`; } return { timelineType, color, label: item.type, filters: { type: item.type }, __target: item.source }; } async getAlarmsOfAsset(filters) { const res = await this.alarmService.list({ ...filters, withSourceAssets: !!filters.source, withSourceDevices: !!filters.source, pageSize: 1000 }); const alarms = await this.getUniqueAlarmsOnly(res.data); return alarms; } async getEventsOfAsset(filters) { const res = await this.eventsService.list(filters); const events = this.getUniqueEventsOnly(res.data); return events; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmEventSelectorService, deps: [{ token: i1$1.AlarmService }, { token: i1$1.EventService }, { token: i2.ColorService }], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmEventSelectorService, providedIn: 'root' }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmEventSelectorService, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }], ctorParameters: () => [{ type: i1$1.AlarmService }, { type: i1$1.EventService }, { type: i2.ColorService }] }); class AlarmEventSelectorListItemComponent { constructor(formBuilder) { this.formBuilder = formBuilder; this.showAddRemoveButton = true; this.isSelected = false; this.optionToRemove = false; this.showActiveToggle = false; this.allowItemEdit = false; this.hideSource = false; this.displayAsSwitch = false; this.omitProperties = {}; this.added = new EventEmitter(); this.removed = new EventEmitter(); this.destroy$ = new Subject(); this.formGroup = this.formBuilder.group({ details: [], color: [], __active: [], __target: [] }); this.valid$ = this.formGroup.statusChanges.pipe(takeUntil(this.destroy$), map(val => val === 'VALID')); } ngOnInit() { this.colorPickerTitle = this.allowItemEdit ? gettext('Change color') : ''; } ngOnDestroy() { this.destroy$.next(); this.destroy$.complete(); } toggleActive() { this.formGroup.controls.__active.setValue(!this.formGroup.value.__active); } validate(_control) { return this.formGroup?.valid ? null : { formInvalid: {} }; } writeValue(obj) { this.formGroup.patchValue({ ...obj, details: obj }); } registerOnChange(fn) { this.formGroup.valueChanges .pipe(map(tmp => this.transformFormValue(tmp)), takeUntil(this.destroy$)) .subscribe(fn); } registerOnTouched(fn) { this.formGroup.valueChanges.pipe(take(1)).subscribe(fn); } setDisabledState(isDisabled) { isDisabled ? this.formGroup.disable() : this.formGroup.enable(); } addOrRemoveItem() { const value = this.transformFormValue(this.formGroup.value); if (this.isSelected) { this.removed.emit(value); } else { this.added.emit(value); } } remove() { this.removed.emit(this.transformFormValue(this.formGroup.value)); } transformFormValue(formValue) { const obj = Object.assign({}, formValue.details || {}, formValue); delete obj.details; return obj; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmEventSelectorListItemComponent, deps: [{ token: i1.FormBuilder }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: AlarmEventSelectorListItemComponent, isStandalone: false, selector: "c8y-alarm-event-selector-list-item", inputs: { timelineType: "timelineType", datapoints: "datapoints", highlightText: "highlightText", showAddRemoveButton: "showAddRemoveButton", isSelected: "isSelected", optionToRemove: "optionToRemove", showActiveToggle: "showActiveToggle", allowItemEdit: "allowItemEdit", hideSource: "hideSource", displayAsSwitch: "displayAsSwitch", omitProperties: "omitProperties" }, outputs: { added: "added", removed: "removed" }, providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => AlarmEventSelectorListItemComponent), multi: true }, { provide: NG_VALIDATORS, useExisting: forwardRef(() => AlarmEventSelectorListItemComponent), multi: true } ], ngImport: i0, template: "<c8y-li\n class=\"c8y-list__item__collapse--container-small\"\n [formGroup]=\"formGroup\"\n #li\n>\n <c8y-li-drag-handle><ng-content select=\"c8y-li-drag-handle\"></ng-content></c8y-li-drag-handle>\n <c8y-li-icon\n class=\"a-s-center p-r-4\"\n *ngIf=\"showAddRemoveButton\"\n >\n <button\n class=\"btn btn-clean text-primary\"\n [title]=\"'Remove' | translate\"\n *ngIf=\"isSelected\"\n (click)=\"addOrRemoveItem()\"\n >\n <i\n class=\"text-danger\"\n c8yIcon=\"minus-circle\"\n ></i>\n </button>\n <button\n class=\"btn btn-clean text-primary\"\n [title]=\"'Select' | translate\"\n *ngIf=\"!isSelected\"\n (click)=\"addOrRemoveItem()\"\n [disabled]=\"(valid$ | async) === false\"\n >\n <i\n class=\"text-primary\"\n c8yIcon=\"plus-circle\"\n ></i>\n </button>\n </c8y-li-icon>\n\n <c8y-li-checkbox\n class=\"a-s-center m-t-4 p-r-0\"\n *ngIf=\"showActiveToggle\"\n [displayAsSwitch]=\"displayAsSwitch\"\n formControlName=\"__active\"\n (click)=\"$event.stopPropagation()\"\n ></c8y-li-checkbox>\n\n <div class=\"d-flex a-i-center p-l-0\">\n <div\n class=\"c8y-list__item__colorpicker p-t-0 p-b-0 p-l-4\"\n [title]=\"colorPickerTitle | translate\"\n >\n <div\n class=\"c8y-colorpicker\"\n [ngClass]=\"{\n 'c8y-colorpicker--alarm': timelineType === 'ALARM',\n 'c8y-colorpicker--event': timelineType === 'EVENT'\n }\"\n >\n <input\n [ngStyle]=\"{ 'pointer-events': allowItemEdit && !omitProperties.color ? 'auto' : 'none' }\"\n type=\"color\"\n formControlName=\"color\"\n (click)=\"$event.stopPropagation()\"\n />\n <span\n class=\"circle-icon-wrapper circle-icon-wrapper--medium\"\n [ngStyle]=\"{ 'background-color': formGroup.value.color }\"\n >\n <i\n class=\"stroked-icon\"\n [c8yIcon]=\"timelineType === 'EVENT' ? 'online1' : 'bell'\"\n ></i>\n </span>\n </div>\n </div>\n <button\n class=\"btn-clean text-truncate\"\n [title]=\"formGroup.value.details?.label\"\n (click)=\"\n allowItemEdit\n ? (li.collapsed = !li.collapsed)\n : showActiveToggle\n ? toggleActive()\n : addOrRemoveItem()\n \"\n >\n <span class=\"text-truncate\">\n <c8y-highlight\n [text]=\"\n omitProperties.label\n ? formGroup.value.details?.filters?.type\n : formGroup.value.details?.label\n \"\n [pattern]=\"highlightText\"\n [shouldTrimPattern]=\"true\"\n ></c8y-highlight>\n </span>\n <small\n class=\"text-truncate text-muted icon-flex\"\n *ngIf=\"formGroup.value.__target && !hideSource\"\n >\n <i c8yIcon=\"exchange\"></i>\n <span class=\"text-truncate\">{{ formGroup.value.__target.name }}</span>\n </small>\n </button>\n\n <button\n class=\"btn-dot btn-dot--danger m-l-auto\"\n title=\"{{ 'Invalid entries' | translate }}\"\n [popover]=\"'Some entries are invalid. Check the required input fields.' | translate\"\n container=\"body\"\n *ngIf=\"(valid$ | async) === false && li.collapsed\"\n [outsideClick]=\"true\"\n >\n <i c8yIcon=\"exclamation-circle\"></i>\n </button>\n </div>\n\n <c8y-li-action\n [icon]=\"'minus-circle'\"\n *ngIf=\"optionToRemove\"\n [label]=\"'Remove from list' | translate\"\n (click)=\"remove()\"\n ></c8y-li-action>\n <c8y-li-collapse *ngIf=\"allowItemEdit\">\n <div class=\"data-point-details\">\n <c8y-alarm-event-attributes-form\n formControlName=\"details\"\n [timelineType]=\"timelineType\"\n [omitProperties]=\"omitProperties\"\n [datapoints]=\"datapoints\"\n [target]=\"formGroup.value.__target\"\n ></c8y-alarm-event-attributes-form>\n </div>\n </c8y-li-collapse>\n</c8y-li>\n", dependencies: [{ kind: "directive", type: i2.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { 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: "component", type: i2.HighlightComponent, selector: "c8y-highlight", inputs: ["pattern", "text", "elementClass", "shouldTrimPattern"] }, { 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.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "component", type: i2.ListItemComponent, selector: "c8y-list-item, c8y-li", inputs: ["active", "highlighted", "emptyActions", "dense", "collapsed", "selectable"], outputs: ["collapsedChange"] }, { kind: "component", type: i2.ListItemIconComponent, selector: "c8y-list-item-icon, c8y-li-icon", inputs: ["icon", "status"] }, { kind: "component", type: i2.ListItemActionComponent, selector: "c8y-list-item-action, c8y-li-action", inputs: ["label", "icon", "disabled"], outputs: ["click"] }, { kind: "component", type: i2.ListItemCollapseComponent, selector: "c8y-list-item-collapse, c8y-li-collapse", inputs: ["collapseWay"] }, { kind: "component", type: i2.ListItemCheckboxComponent, selector: "c8y-list-item-checkbox, c8y-li-checkbox", inputs: ["selected", "indeterminate", "disabled", "displayAsSwitch"], outputs: ["onSelect"] }, { kind: "component", type: i2.ListItemDragHandleComponent, selector: "c8y-list-item-drag-handle, c8y-li-drag-handle" }, { 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: "directive", type: i4.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: AlarmEventAttributesFormComponent, selector: "c8y-alarm-event-attributes-form", inputs: ["timelineType", "omitProperties", "selectedItems", "target", "datapoints"] }, { kind: "pipe", type: i2.C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: i3.AsyncPipe, name: "async" }] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmEventSelectorListItemComponent, decorators: [{ type: Component, args: [{ selector: 'c8y-alarm-event-selector-list-item', providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => AlarmEventSelectorListItemComponent), multi: true }, { provide: NG_VALIDATORS, useExisting: forwardRef(() => AlarmEventSelectorListItemComponent), multi: true } ], standalone: false, template: "<c8y-li\n class=\"c8y-list__item__collapse--container-small\"\n [formGroup]=\"formGroup\"\n #li\n>\n <c8y-li-drag-handle><ng-content select=\"c8y-li-drag-handle\"></ng-content></c8y-li-drag-handle>\n <c8y-li-icon\n class=\"a-s-center p-r-4\"\n *ngIf=\"showAddRemoveButton\"\n >\n <button\n class=\"btn btn-clean text-primary\"\n [title]=\"'Remove' | translate\"\n *ngIf=\"isSelected\"\n (click)=\"addOrRemoveItem()\"\n >\n <i\n class=\"text-danger\"\n c8yIcon=\"minus-circle\"\n ></i>\n </button>\n <button\n class=\"btn btn-clean text-primary\"\n [title]=\"'Select' | translate\"\n *ngIf=\"!isSelected\"\n (click)=\"addOrRemoveItem()\"\n [disabled]=\"(valid$ | async) === false\"\n >\n <i\n class=\"text-primary\"\n c8yIcon=\"plus-circle\"\n ></i>\n </button>\n </c8y-li-icon>\n\n <c8y-li-checkbox\n class=\"a-s-center m-t-4 p-r-0\"\n *ngIf=\"showActiveToggle\"\n [displayAsSwitch]=\"displayAsSwitch\"\n formControlName=\"__active\"\n (click)=\"$event.stopPropagation()\"\n ></c8y-li-checkbox>\n\n <div class=\"d-flex a-i-center p-l-0\">\n <div\n class=\"c8y-list__item__colorpicker p-t-0 p-b-0 p-l-4\"\n [title]=\"colorPickerTitle | translate\"\n >\n <div\n class=\"c8y-colorpicker\"\n [ngClass]=\"{\n 'c8y-colorpicker--alarm': timelineType === 'ALARM',\n 'c8y-colorpicker--event': timelineType === 'EVENT'\n }\"\n >\n <input\n [ngStyle]=\"{ 'pointer-events': allowItemEdit && !omitProperties.color ? 'auto' : 'none' }\"\n type=\"color\"\n formControlName=\"color\"\n (click)=\"$event.stopPropagation()\"\n />\n <span\n class=\"circle-icon-wrapper circle-icon-wrapper--medium\"\n [ngStyle]=\"{ 'background-color': formGroup.value.color }\"\n >\n <i\n class=\"stroked-icon\"\n [c8yIcon]=\"timelineType === 'EVENT' ? 'online1' : 'bell'\"\n ></i>\n </span>\n </div>\n </div>\n <button\n class=\"btn-clean text-truncate\"\n [title]=\"formGroup.value.details?.label\"\n (click)=\"\n allowItemEdit\n ? (li.collapsed = !li.collapsed)\n : showActiveToggle\n ? toggleActive()\n : addOrRemoveItem()\n \"\n >\n <span class=\"text-truncate\">\n <c8y-highlight\n [text]=\"\n omitProperties.label\n ? formGroup.value.details?.filters?.type\n : formGroup.value.details?.label\n \"\n [pattern]=\"highlightText\"\n [shouldTrimPattern]=\"true\"\n ></c8y-highlight>\n </span>\n <small\n class=\"text-truncate text-muted icon-flex\"\n *ngIf=\"formGroup.value.__target && !hideSource\"\n >\n <i c8yIcon=\"exchange\"></i>\n <span class=\"text-truncate\">{{ formGroup.value.__target.name }}</span>\n </small>\n </button>\n\n <button\n class=\"btn-dot btn-dot--danger m-l-auto\"\n title=\"{{ 'Invalid entries' | translate }}\"\n [popover]=\"'Some entries are invalid. Check the required input fields.' | translate\"\n container=\"body\"\n *ngIf=\"(valid$ | async) === false && li.collapsed\"\n [outsideClick]=\"true\"\n >\n <i c8yIcon=\"exclamation-circle\"></i>\n </button>\n </div>\n\n <c8y-li-action\n [icon]=\"'minus-circle'\"\n *ngIf=\"optionToRemove\"\n [label]=\"'Remove from list' | translate\"\n (click)=\"remove()\"\n ></c8y-li-action>\n <c8y-li-collapse *ngIf=\"allowItemEdit\">\n <div class=\"data-point-details\">\n <c8y-alarm-event-attributes-form\n formControlName=\"details\"\n [timelineType]=\"timelineType\"\n [omitProperties]=\"omitProperties\"\n [datapoints]=\"datapoints\"\n [target]=\"formGroup.value.__target\"\n ></c8y-alarm-event-attributes-form>\n </div>\n </c8y-li-collapse>\n</c8y-li>\n" }] }], ctorParameters: () => [{ type: i1.FormBuilder }], propDecorators: { timelineType: [{ type: Input }], datapoints: [{ type: Input }], highlightText: [{ type: Input }], showAddRemoveButton: [{ type: Input }], isSelected: [{ type: Input }], optionToRemove: [{ type: Input }], showActiveToggle: [{ type: Input }], allowItemEdit: [{ type: Input }], hideSource: [{ type: Input }], displayAsSwitch: [{ type: Input }], omitProperties: [{ type: Input }], added: [{ type: Output }], removed: [{ type: Output }] } }); class CustomAlarmEventFormComponent { constructor(formBuilder) { this.formBuilder = formBuilder; this.omitProperties = {}; this.selectedItems = []; this.added = new EventEmitter(); this.cancel = new EventEmitter(); this.destroy$ = new Subject(); this.formGroup = this.formBuilder.group({ details: [], color: [], __active: [], __target: [] }); this.valid$ = this.formGroup.statusChanges.pipe(takeUntil(this.destroy$), map(val => val === 'VALID'), startWith(false)); } ngOnInit() { this.formGroup.patchValue({ color: this.defaultColor, __target: this.target, details: { timelineType: this.timelineType } }); } ngOnDestroy() { this.destroy$.next(); this.destroy$.complete(); } add() { if (this.formGroup.valid) { const formValue = this.transformFormValue(this.formGroup.value); this.added.emit(formValue); } } transformFormValue(formValue) { const obj = Object.assign({}, formValue.details || {}, formValue); delete obj.details; return obj; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: CustomAlarmEventFormComponent, deps: [{ token: i1.FormBuilder }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: CustomAlarmEventFormComponent, isStandalone: false, selector: "c8y-custom-alarm-event-form", inputs: { timelineType: "timelineType", target: "target", omitProperties: "omitProperties", selectedItems: "selectedItems", defaultColor: "defaultColor" }, outputs: { added: "added", cancel: "cancel" }, ngImport: i0, template: "<form [formGroup]=\"formGroup\" class=\"p-16\">\n <div\n *ngIf=\"!omitProperties.color\"\n class=\"form-group d-flex a-i-center gap-8\"\n [title]=\"'Change color' | translate\"\n >\n <label class=\"m-0\">{{ 'Color' | translate }}</label>\n <div class=\"c8y-colorpicker\"\n [ngClass]=\"{\n 'c8y-colorpicker--event': timelineType === 'EVENT',\n 'c8y-colorpicker--alarm': timelineType !== 'EVENT'\n }\"\n >\n <input\n type=\"color\"\n formControlName=\"color\"\n (click)=\"$event.stopPropagation()\"\n />\n <span [style.background-color]=\"formGroup.value.color\">\n <i [c8yIcon]=\"timelineType === 'EVENT' ? 'online1' : 'bell'\"></i>\n </span>\n </div>\n </div>\n\n <c8y-alarm-event-attributes-form\n formControlName=\"details\"\n [timelineType]=\"timelineType\"\n [omitProperties]=\"omitProperties\"\n [selectedItems]=\"selectedItems\"\n ></c8y-alarm-event-attributes-form>\n\n <div class=\"d-flex p-t-16\">\n <button class=\"btn btn-default btn-sm\" (click)=\"cancel.emit()\">\n {{ 'Cancel' | translate }}\n </button>\n <button\n class=\"btn btn-primary btn-sm\"\n [disabled]=\"(valid$ | async) === false\"\n (click)=\"add()\"\n >\n <i c8yIcon=\"plus-circle\"></i>\n {{ 'Select' | translate }}\n </button>\n </div>\n</form>\n", dependencies: [{ kind: "directive", type: i2.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: i3.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i3.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.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.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: "component", type: AlarmEventAttributesFormComponent, selector: "c8y-alarm-event-attributes-form", inputs: ["timelineType", "omitProperties", "selectedItems", "target", "datapoints"] }, { kind: "pipe", type: i2.C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: i3.AsyncPipe, name: "async" }] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: CustomAlarmEventFormComponent, decorators: [{ type: Component, args: [{ selector: 'c8y-custom-alarm-event-form', standalone: false, template: "<form [formGroup]=\"formGroup\" class=\"p-16\">\n <div\n *ngIf=\"!omitProperties.color\"\n class=\"form-group d-flex a-i-center gap-8\"\n [title]=\"'Change color' | translate\"\n >\n <label class=\"m-0\">{{ 'Color' | translate }}</label>\n <div class=\"c8y-colorpicker\"\n [ngClass]=\"{\n 'c8y-colorpicker--event': timelineType === 'EVENT',\n 'c8y-colorpicker--alarm': timelineType !== 'EVENT'\n }\"\n >\n <input\n type=\"color\"\n formControlName=\"color\"\n (click)=\"$event.stopPropagation()\"\n />\n <span [style.background-color]=\"formGroup.value.color\">\n <i [c8yIcon]=\"timelineType === 'EVENT' ? 'online1' : 'bell'\"></i>\n </span>\n </div>\n </div>\n\n <c8y-alarm-event-attributes-form\n formControlName=\"details\"\n [timelineType]=\"timelineType\"\n [omitProperties]=\"omitProperties\"\n [selectedItems]=\"selectedItems\"\n ></c8y-alarm-event-attributes-form>\n\n <div class=\"d-flex p-t-16\">\n <button class=\"btn btn-default btn-sm\" (click)=\"cancel.emit()\">\n {{ 'Cancel' | translate }}\n </button>\n <button\n class=\"btn btn-primary btn-sm\"\n [disabled]=\"(valid$ | async) === false\"\n (click)=\"add()\"\n >\n <i c8yIcon=\"plus-circle\"></i>\n {{ 'Select' | translate }}\n </button>\n </div>\n</form>\n" }] }], ctorParameters: () => [{ type: i1.FormBuilder }], propDecorators: { timelineType: [{ type: Input }], target: [{ type: Input }], omitProperties: [{ type: Input }], selectedItems: [{ type: Input }], defaultColor: [{ type: Input }], added: [{ type: Output }], cancel: [{ type: Output }] } }); class IncludesAlarmOrEventPipe { transform(itemList, item) { if (!Array.isArray(itemList) || !item) { return false; } return itemList.some(tmp => tmp.label === item.label && tmp.filters.type === item.filters.type && tmp.__target?.id === item.__target?.id); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: IncludesAlarmOrEventPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); } static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: IncludesAlarmOrEventPipe, isStandalone: false, name: "includesAlarmOrEvent" }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: IncludesAlarmOrEventPipe, decorators: [{ type: Pipe, args: [{ name: 'includesAlarmOrEvent', standalone: false }] }] }); class AlarmEventSelectorComponent { constructor(alarmEventSelectorService) { this.alarmEventSelectorService = alarmEventSelectorService; this.timelineType = 'ALARM'; this.allowChangingContext = true; this.selectedItems = new Array(); this.allowSearch = true; this.groupsSelectable = true; this.hideSource = false; this.omitProperties = {}; this.selectionChange = new EventEmitter(); this.filterString = ''; this.maxNumberOfItems = 50; this.loadingItems$ = new BehaviorSubject(false); this.assetSelection = new BehaviorSubject({}); this.isExpanded = false; this.defaultColor = getComputedStyle(document.documentElement).getPropertyValue('--brand-primary') || getComputedStyle(document.documentElement).getPropertyValue('--c8y-brand-primary') || '#1776BF'; this.filterString$ = new BehaviorSubject(''); } ngOnInit() { this.timelineTypeTexts = this.alarmEventSelectorService.timelineTypeTexts(this.timelineType); this.setupObservables(); if (this.contextAsset) { this.selectAsset(this.contextAsset); } } itemAdded(item) { item.__active = true; this.selectedItems = [...this.selectedItems, item]; this.emitCurrentSelection(); } itemRemoved(alarm) { this.selectedItems = this.selectedItems.filter(tmp => tmp.label !== alarm.label || tmp.filters.type !== alarm.filters.type || tmp.__target?.id !== alarm.__target?.id); this.emitCurrentSelection(); } assetSelectionChanged(evt) { if (evt.items) { return this.selectAsset(evt.items.length ? evt.items[0] : evt.items); } // reset selection this.assetSelection.next(null); } trackByFn(_index, item) { return `${item.filters.type}-${item.__target?.id}`; } filterStringChanged(newValue = '') { this.filterString$.next(newValue); this.filterString = newValue; } setLoadingState(state) { queueMicrotask(() => this.loadingItems$.next(state)); } setupObservables() { this.items$ = this.assetSelection.pipe(tap(() => this.setLoadingState(true)), switchMap((asset) => asset?.id ? this.alarmEventSelectorService.getAlarmsOrEvents(asset, this.timelineType) : this.alarmEventSelectorService.getItems(this.timelineType)), tap(() => this.setLoadingState(false)), map(items => { if (this.omitProperties.color) { items.forEach(i => (i.color = this.defaultColor)); } return items; }), shareReplay(1)); this.filterStringChanges$ = this.filterString$.pipe(distinctUntilChanged(), debounceTime(500), shareReplay(1)); this.filteredItems$ = combineLatest([this.filterStringChanges$, this.items$]).pipe(map(([filterString, items]) => { if (!filterString) { return items; } const lowerCaseFilterString = filterString.toLowerCase(); return items.filter(item => this.includesFilterString(item, lowerCaseFilterString)); }), map(filtered => filtered.slice(0, this.maxNumberOfItems))); } selectAsset(asset) { this.assetSelection.next(asset); this.filterStringChanged(); } emitCurrentSelection() { this.selectionChange.emit(this.selectedItems); } includesFilterString(alarm, lowerCaseFilterString) { const label = alarm.label?.toLowerCase(); if (label && label.includes(lowerCaseFilterString)) { return true; } const type = alarm.filters.type?.toLowerCase(); if (type && type.includes(lowerCaseFilterString)) { return true; } return false; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AlarmEventSelectorComponent, deps: [{ token: AlarmEventSelectorService }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: AlarmEventSelectorComponent, isStandalone: false, selector: "c8y-alarm-event-selector", inputs: { timelineType: "timelineType", contextAsset: "contextAsset", allowChangingContext: "allowChangingContext", selectedItems: "selectedItems", allowSearch: "allowSearch", groupsSelectable: "groupsSelectable", hideSource: "hideSource", omitProperties: "omitProperties" }, outputs: { selectionChange: "selectionChange" }, providers: [ { provide: NG_VALUE_ACCESSOR, multi: true, useExisting: forwardRef(() => AlarmEventSelectorComponent) } ], ngImport: i0, template: "<div\n class=\"d-grid grid__row--1 fit-h\"\n [ngClass]=\"{\n 'grid__col--3-6-3--md': allowChangingContext,\n 'grid__col--8-4--md': !allowChangingContext\n }\"\n>\n <div\n class=\"d-flex d-col p-relative bg-level-1\"\n *ngIf=\"allowChangingContext\"\n >\n <c8y-asset-selector-miller\n class=\"d-contents\"\n [(ngModel)]=\"contextAsset\"\n [asset]=\"contextAsset\"\n (onSelected)=\"assetSelectionChanged($event)\"\n [container]=\"''\"\n [config]=\"{\n view: 'miller',\n groupsSelectable: groupsSelectable,\n columnHeaders: true,\n showChildDevices: true,\n showUnassignedDevices: true,\n singleColumn: true,\n search: allowSearch,\n showFilter: true\n }\"\n ></c8y-asset-selector-miller>\n </div>\n <!-- center column -->\n <div class=\"inner-scroll bg-component\" data-cy=\"c8y-alarm-event-selector--inner-column\">\n <ng-template #noDeviceEmptyState>\n <div class=\"p-16\">\n <c8y-ui-empty-state\n [icon]=\"timelineTypeTexts.emptyStateIcon\"\n [title]=\"timelineTypeTexts.emptyStateTitle | translate\"\n [subtitle]=\"timelineTypeTexts.assetWithNoItemsEmptyStateSubtitle | translate\"\n [horizontal]=\"true\"\n ></c8y-ui-empty-state>\n </div>\n </ng-template>\n\n <ng-template #loadingData>\n <div class=\"p-16 text-center\">\n <c8y-loading></c8y-loading>\n </div>\n </ng-template>\n\n <div\n