@c8y/ngx-components
Version:
Angular modules for Cumulocity IoT applications
1,486 lines • 150 kB
JavaScript
import { moveItemInArray, CdkDropList, CdkDrag, CdkDragHandle, DragDropModule } from '@angular/cdk/drag-drop';
import * as i0 from '@angular/core';
import { Injectable, forwardRef, Input, Component, EventEmitter, ContentChild, Output, Pipe, Optional, NgModule } from '@angular/core';
import * as i2$1 from '@c8y/ngx-components';
import { gettext, C8yValidators, C8yTranslateDirective, FormGroupComponent, RequiredInputPlaceholderDirective, MessagesComponent, MessageDirective, C8yTranslatePipe, MAX_PAGE_SIZE, IconDirective, ListItemComponent, ListItemDragHandleComponent, ListItemCheckboxComponent, HighlightComponent, ListItemActionComponent, ListItemCollapseComponent, TypeaheadComponent, ForOfDirective, ListItemIconComponent, EmptyStateComponent, LoadingComponent, ListGroupComponent, CoreModule } from '@c8y/ngx-components';
import { MillerViewComponent, AssetSelectorModule } from '@c8y/ngx-components/assets-navigator';
import { CollapseModule } from 'ngx-bootstrap/collapse';
import { BsDropdownModule } from 'ngx-bootstrap/dropdown';
import * as i1$1 from 'ngx-bootstrap/modal';
import { ModalModule } from 'ngx-bootstrap/modal';
import { PopoverDirective, PopoverModule } from 'ngx-bootstrap/popover';
import { TooltipDirective, TooltipModule } from 'ngx-bootstrap/tooltip';
import * as i2 from '@angular/forms';
import { Validators, FormsModule, ReactiveFormsModule, NG_VALUE_ACCESSOR, NG_VALIDATORS } from '@angular/forms';
import { map, take, filter, startWith, tap, switchMap, shareReplay, distinctUntilChanged, debounceTime } from 'rxjs/operators';
import { NgIf, NgFor, NgClass, NgTemplateOutlet, KeyValuePipe, NgSwitch, NgSwitchCase, NgSwitchDefault, AsyncPipe } from '@angular/common';
import * as i4 from '@c8y/ngx-components/context-dashboard';
import { pipe, BehaviorSubject, from, combineLatest } from 'rxjs';
import * as i1 from '@c8y/client';
import { sortBy, get, uniq } from 'lodash-es';
const AXIS_TYPES = [
{ val: undefined, text: gettext('Auto') },
{ val: 'left', text: gettext('Left') },
{ val: 'right', text: gettext('Right') }
];
const CHART_LINE_TYPES = [
{ val: 'line', text: gettext('Line') },
{ val: 'points', text: gettext('Points') },
{ val: 'linePoints', text: gettext('Line and points') },
{ val: 'bars', text: gettext('Bars') },
{ val: 'step-before', text: gettext('Step before') },
{ val: 'step-after', text: gettext('Step after') }
];
const CHART_RENDER_TYPES = [
{ val: 'min', text: gettext('Minimum') },
{ val: 'max', text: gettext('Maximum') },
{ val: 'area', text: gettext('Minimum and maximum') }
];
class DatapointAttributesFormValidationService {
constructor(formBuilder) {
this.formBuilder = formBuilder;
}
getDefaultFormGroup(fieldsToRemove = []) {
const formFields = {
__active: [true, []],
__target: this.getTargetFormGroup(),
__template: [undefined, []],
color: ['', this.getColorValidators()],
label: ['', this.getLabelValidators()],
description: ['', this.getDescriptionValidators()],
fragment: ['', this.getFragmentValidators()],
series: ['', this.getSeriesValidators()],
range: this.getMinMaxFormGroup(),
unit: [undefined, this.getUnitValidators()],
target: [undefined, this.getTargetValidators()],
redRange: this.getMinMaxFormGroup(),
yellowRange: this.getMinMaxFormGroup(),
chart: this.getChartFormGroup(),
display: this.getDisplayFormGroup()
};
if (fieldsToRemove.length) {
for (const field of fieldsToRemove) {
delete formFields[field];
}
}
return this.formBuilder.group(formFields, {
validators: this.getOverallValidators()
});
}
convertToBackendFormat(formDataStructure, showChart) {
if (!formDataStructure) {
return {};
}
const { __active, __target, __template, color, label, description, fragment, series, range, unit, target, redRange, yellowRange, chart, display } = formDataStructure;
const obj = {
__active,
__target,
__template,
color,
label,
description,
fragment,
series,
min: range?.min,
max: range?.max,
unit,
target,
redRangeMin: redRange?.min,
redRangeMax: redRange?.max,
yellowRangeMin: yellowRange?.min,
yellowRangeMax: yellowRange?.max,
renderType: showChart ? chart?.renderType : display?.renderType,
lineType: chart?.lineType,
yAxisType: chart?.yAxisType
};
return obj;
}
convertToFormGroupFormat(backendDataStructure) {
if (!backendDataStructure) {
return {};
}
const { __active, __target, __template, color, label, description, fragment, series, min, max, unit, target, redRangeMin, redRangeMax, yellowRangeMin, yellowRangeMax, renderType, lineType, yAxisType } = backendDataStructure;
const obj = {
__active,
__target,
__template,
color,
label,
description,
fragment,
series,
range: {
min: this.convertStringToNumber(min),
max: this.convertStringToNumber(max)
},
unit,
target: this.convertStringToNumber(target),
redRange: {
min: this.convertStringToNumber(redRangeMin),
max: this.convertStringToNumber(redRangeMax)
},
yellowRange: {
min: this.convertStringToNumber(yellowRangeMin),
max: this.convertStringToNumber(yellowRangeMax)
},
chart: renderType || lineType || yAxisType ? { renderType, lineType, yAxisType } : undefined,
display: renderType ? { renderType } : undefined
};
return obj;
}
getColorValidators() {
return [Validators.required, Validators.minLength(4)];
}
getLabelValidators() {
return [Validators.required, Validators.minLength(1), Validators.maxLength(120)];
}
getDescriptionValidators() {
return [];
}
getFragmentValidators() {
return [
Validators.required,
Validators.minLength(1),
Validators.maxLength(120),
Validators.pattern(/^[^.]*$/)
];
}
getSeriesValidators() {
return [
Validators.required,
Validators.minLength(1),
Validators.maxLength(120),
(control) => {
const forbidden = control.value?.includes('.') || '';
return forbidden
? { noPeriods: { message: gettext('Series cannot contain periods.') } }
: null;
}
];
}
getMinMaxValidators() {
return [C8yValidators.minMaxValidator(), C8yValidators.requireBothMinAndMax()];
}
getUnitValidators() {
return [];
}
getTargetValidators() {
return [];
}
getOverallValidators() {
return [
C8yValidators.withinScale('redRange.min'),
C8yValidators.withinScale('redRange.max'),
C8yValidators.withinScale('yellowRange.min'),
C8yValidators.withinScale('yellowRange.max'),
C8yValidators.withinScale('target')
];
}
getMinMaxFormGroup() {
return this.formBuilder.group({ min: [undefined, []], max: [undefined, []] }, { validators: this.getMinMaxValidators() });
}
getChartFormGroup() {
return this.formBuilder.group({
renderType: [CHART_RENDER_TYPES[0].val, []],
lineType: [CHART_LINE_TYPES[0].val, []],
yAxisType: [AXIS_TYPES[0].val, []]
});
}
getDisplayFormGroup() {
return this.formBuilder.group({
renderType: [CHART_RENDER_TYPES[0].val, []]
});
}
getTargetFormGroup() {
return this.formBuilder.group({
id: [undefined, []],
name: [undefined, []]
});
}
convertStringToNumber(possibleString) {
if (typeof possibleString === 'string') {
try {
return Number.parseFloat(possibleString);
}
catch {
return undefined;
}
}
else {
return possibleString;
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DatapointAttributesFormValidationService, deps: [{ token: i2.FormBuilder }], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DatapointAttributesFormValidationService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DatapointAttributesFormValidationService, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}], ctorParameters: () => [{ type: i2.FormBuilder }] });
class DatapointAttributesFormComponent {
constructor(formValidations) {
this.formValidations = formValidations;
this.selectableChartRenderTypes = [];
this.selectableChartLineTypes = [];
this.selectableAxisTypes = [];
this.showTarget = true;
this.showRange = true;
this.showYellowRange = true;
this.showRedRange = true;
this.showChart = true;
this.showFormIfTemplateWasSelected = false;
this.rawValue = {};
this.CHART_RENDER_TYPES = Array.from(CHART_RENDER_TYPES);
this.CHART_LINE_TYPES = Array.from(CHART_LINE_TYPES);
this.AXIS_TYPES = Array.from(AXIS_TYPES);
this.customValidationErrorMessages = {};
this.shouldForceInitialValidation = true;
this.formGroup = this.formValidations.getDefaultFormGroup();
this.setSubForms();
}
ngOnInit() {
this.initializeFormVisibility();
this.filterChartTypes();
}
validate(_control) {
if (this.formGroup?.get('series')?.errors?.noPeriods) {
return this.formGroup?.get('series')?.errors;
}
return this.formGroup?.valid ? null : { formInvalid: {} };
}
writeValue(obj) {
this.rawValue = obj || {};
if (obj) {
this.formGroup.patchValue(this.formValidations.convertToFormGroupFormat(obj), {
emitEvent: false
});
}
if (this.shouldForceInitialValidation) {
queueMicrotask(() => {
this.formGroup.updateValueAndValidity();
});
this.shouldForceInitialValidation = false;
}
}
registerOnChange(fn) {
this.formGroup.valueChanges
.pipe(map(formValue => this.formValidations.convertToBackendFormat(formValue, this.showChart)), map(formValue => Object.assign(this.rawValue, formValue)))
.subscribe(fn);
}
registerOnTouched(fn) {
this.formGroup.valueChanges.pipe(take(1)).subscribe(fn);
}
setDisabledState(isDisabled) {
if (this.formGroup?.disabled === isDisabled) {
return;
}
isDisabled ? this.formGroup.disable() : this.formGroup.enable();
}
setSubForms() {
if (!this.formGroup) {
this.range = this.yellowRange = this.redRange = this.chart = undefined;
return;
}
this.range = this.formGroup.get('range');
this.yellowRange = this.formGroup.get('yellowRange');
this.redRange = this.formGroup.get('redRange');
this.chart = this.formGroup.get('chart');
this.display = this.formGroup.get('display');
}
initializeFormVisibility() {
this.showChartForm = this.chart && this.showChart;
const hasLineTypes = !!this.selectableChartLineTypes?.length;
const hasAxisTypes = !!this.selectableAxisTypes?.length;
const hasRenderTypes = this.selectableChartRenderTypes?.length > 0 || this.selectableChartRenderTypes === undefined;
this.showOnlyDisplayForm =
!this.showChartForm && !hasLineTypes && !hasAxisTypes && hasRenderTypes;
}
filterChartTypes() {
this.filterRenderTypes();
this.filterLineTypes();
this.filterAxisTypes();
}
filterRenderTypes() {
if (!!this.selectableChartRenderTypes?.length) {
this.CHART_RENDER_TYPES = this.CHART_RENDER_TYPES.filter(renderType => this.selectableChartRenderTypes.includes(renderType.val));
}
}
filterLineTypes() {
if (!!this.selectableChartLineTypes?.length) {
this.CHART_LINE_TYPES = this.CHART_LINE_TYPES.filter(lineType => this.selectableChartLineTypes.includes(lineType.val));
}
}
filterAxisTypes() {
if (!!this.selectableAxisTypes?.length) {
this.AXIS_TYPES = this.AXIS_TYPES.filter(axisType => this.selectableAxisTypes.includes(axisType.val));
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DatapointAttributesFormComponent, deps: [{ token: DatapointAttributesFormValidationService }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: DatapointAttributesFormComponent, isStandalone: true, selector: "c8y-datapoint-attributes-form", inputs: { selectableChartRenderTypes: "selectableChartRenderTypes", selectableChartLineTypes: "selectableChartLineTypes", selectableAxisTypes: "selectableAxisTypes", showTarget: "showTarget", showRange: "showRange", showYellowRange: "showYellowRange", showRedRange: "showRedRange", showChart: "showChart", showFormIfTemplateWasSelected: "showFormIfTemplateWasSelected" }, providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => DatapointAttributesFormComponent),
multi: true
},
{
provide: NG_VALIDATORS,
useExisting: forwardRef(() => DatapointAttributesFormComponent),
multi: true
}
], ngImport: i0, template: "<div [formGroup]=\"formGroup\">\n <ng-container *ngIf=\"!rawValue?.__template || showFormIfTemplateWasSelected\">\n <fieldset\n class=\"c8y-fieldset\"\n *ngIf=\"formGroup.controls?.label || formGroup.controls?.unit || formGroup.controls?.target\"\n >\n <legend translate>Details</legend>\n <div class=\"row\">\n <div\n class=\"col-md-6\"\n *ngIf=\"formGroup.controls?.label\"\n >\n <c8y-form-group class=\"form-group-sm\">\n <label translate>Label</label>\n <input\n class=\"form-control\"\n name=\"label\"\n formControlName=\"label\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: 'Temperature' }\"\n />\n <c8y-messages\n [show]=\"formGroup.controls.label.touched && formGroup.controls.label.errors\"\n >\n <c8y-message\n *ngFor=\"let item of customValidationErrorMessages['label'] | keyvalue\"\n [name]=\"item.key\"\n [text]=\"item.value\"\n ></c8y-message>\n </c8y-messages>\n </c8y-form-group>\n </div>\n\n <div\n class=\"col-md-6\"\n *ngIf=\"formGroup.controls?.unit\"\n >\n <c8y-form-group class=\"form-group-sm\">\n <label translate>Unit</label>\n <input\n class=\"form-control\"\n name=\"unit\"\n formControlName=\"unit\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: '\u00BAC' }\"\n />\n <c8y-messages\n [show]=\"formGroup.controls.unit.touched && formGroup.controls.unit.errors\"\n >\n <c8y-message\n *ngFor=\"let item of customValidationErrorMessages['unit'] | keyvalue\"\n [name]=\"item.key\"\n [text]=\"item.value\"\n ></c8y-message>\n </c8y-messages>\n </c8y-form-group>\n </div>\n\n <div\n class=\"col-md-6\"\n *ngIf=\"formGroup.controls?.target && showTarget\"\n >\n <c8y-form-group class=\"form-group-sm\"\n [ngClass]=\"{\n 'has-error':\n (range?.touched || formGroup.controls.target.touched) &&\n formGroup.controls?.target?.errors\n }\"\n >\n <label translate>Target</label>\n <input\n class=\"form-control\"\n name=\"target\"\n type=\"number\"\n formControlName=\"target\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: 25 }\"\n />\n <c8y-messages\n [show]=\"\n (range?.touched || formGroup.controls.target.touched) &&\n formGroup.controls.target.errors\n \"\n >\n <c8y-message\n *ngFor=\"let item of customValidationErrorMessages['target'] | keyvalue\"\n [name]=\"item.key\"\n [text]=\"item.value\"\n ></c8y-message>\n </c8y-messages>\n </c8y-form-group>\n </div>\n </div>\n </fieldset>\n <fieldset\n class=\"c8y-fieldset\"\n *ngIf=\"range && showRange\"\n >\n <legend translate>Range</legend>\n <div\n class=\"row\"\n formGroupName=\"range\"\n >\n <div class=\"col-md-6\">\n <c8y-form-group class=\"form-group-sm\"\n [ngClass]=\"{ 'has-error': range?.touched && range?.controls?.min?.errors }\"\n >\n <label translate>Min</label>\n <input\n class=\"form-control\"\n name=\"min\"\n type=\"number\"\n formControlName=\"min\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: 0 }\"\n />\n <c8y-messages [show]=\"range?.touched && range.controls?.min?.errors\"></c8y-messages>\n </c8y-form-group>\n </div>\n <div class=\"col-md-6\">\n <c8y-form-group class=\"form-group-sm\"\n [ngClass]=\"{ 'has-error': range?.touched && range?.controls?.max?.errors }\"\n >\n <label translate>Max</label>\n <input\n class=\"form-control\"\n name=\"max\"\n type=\"number\"\n formControlName=\"max\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: 100 }\"\n />\n <c8y-messages [show]=\"range?.touched && range.controls?.max?.errors\">\n <c8y-message\n *ngFor=\"let item of customValidationErrorMessages['max'] | keyvalue\"\n [name]=\"item.key\"\n [text]=\"item.value\"\n ></c8y-message>\n </c8y-messages>\n </c8y-form-group>\n </div>\n </div>\n </fieldset>\n\n <fieldset\n class=\"c8y-fieldset\"\n formGroupName=\"yellowRange\"\n *ngIf=\"yellowRange && showYellowRange\"\n >\n <legend translate>Yellow range</legend>\n <div class=\"row\">\n <div class=\"col-md-6\">\n <c8y-form-group class=\"form-group-sm\"\n [ngClass]=\"{\n 'has-error':\n (range?.touched || yellowRange?.touched) && yellowRange?.controls?.min?.errors\n }\"\n >\n <label translate>Min</label>\n <input\n class=\"form-control\"\n name=\"min\"\n type=\"number\"\n formControlName=\"min\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: 50 }\"\n />\n <c8y-messages\n [show]=\"(range?.touched || yellowRange?.touched) && yellowRange.controls?.min?.errors\"\n ></c8y-messages>\n </c8y-form-group>\n </div>\n\n <div class=\"col-md-6\">\n <c8y-form-group class=\"form-group-sm\"\n [ngClass]=\"{\n 'has-error':\n (range?.touched || yellowRange?.touched) && yellowRange?.controls?.max?.errors\n }\"\n >\n <label translate>Max</label>\n <input\n class=\"form-control\"\n name=\"max\"\n type=\"number\"\n formControlName=\"max\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: 75 }\"\n />\n <c8y-messages\n [show]=\"(range?.touched || yellowRange?.touched) && yellowRange.controls?.max?.errors\"\n ></c8y-messages>\n </c8y-form-group>\n </div>\n </div>\n </fieldset>\n\n <fieldset\n class=\"c8y-fieldset\"\n formGroupName=\"redRange\"\n *ngIf=\"redRange && showRedRange\"\n >\n <legend translate>Red range</legend>\n <div class=\"row\">\n <div class=\"col-md-6\">\n <c8y-form-group class=\"form-group-sm\"\n [ngClass]=\"{\n 'has-error': (range?.touched || redRange?.touched) && redRange?.controls?.min?.errors\n }\"\n >\n <label translate>Min</label>\n <input\n class=\"form-control\"\n name=\"min\"\n type=\"number\"\n formControlName=\"min\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: 75 }\"\n />\n <c8y-messages\n [show]=\"(range?.touched || redRange?.touched) && redRange.controls?.min?.errors\"\n ></c8y-messages>\n </c8y-form-group>\n </div>\n\n <div class=\"col-md-6\">\n <c8y-form-group class=\"form-group-sm\"\n [ngClass]=\"{\n 'has-error': (range?.touched || redRange?.touched) && redRange?.controls?.max?.errors\n }\"\n >\n <label translate>Max</label>\n <input\n class=\"form-control\"\n name=\"max\"\n type=\"number\"\n formControlName=\"max\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: 100 }\"\n />\n <c8y-messages\n [show]=\"(range?.touched || redRange?.touched) && redRange.controls?.max?.errors\"\n ></c8y-messages>\n </c8y-form-group>\n </div>\n </div>\n </fieldset>\n </ng-container>\n\n <fieldset\n class=\"c8y-fieldset\"\n *ngIf=\"showChartForm\"\n formGroupName=\"chart\"\n >\n <legend translate>Chart</legend>\n <div class=\"tight-grid\">\n <div\n class=\"col-xs-6 col-sm-4\"\n *ngIf=\"selectableChartRenderTypes?.length !== 0\"\n >\n <c8y-form-group class=\"form-group-sm\">\n <ng-container *ngTemplateOutlet=\"displayHelpButton\"></ng-container>\n <div class=\"c8y-select-wrapper\">\n <select\n class=\"form-control\"\n formControlName=\"renderType\"\n >\n <option\n *ngFor=\"let type of CHART_RENDER_TYPES\"\n [ngValue]=\"type.val\"\n >\n {{ type.text | translate }}\n </option>\n </select>\n </div>\n </c8y-form-group>\n </div>\n <div\n class=\"col-xs-6 col-sm-4\"\n *ngIf=\"selectableChartLineTypes?.length !== 0\"\n >\n <c8y-form-group class=\"form-group-sm\">\n <label\n for=\"chartType\"\n translate\n >\n Chart type\n </label>\n <div class=\"c8y-select-wrapper\">\n <select\n class=\"form-control\"\n formControlName=\"lineType\"\n >\n <option\n *ngFor=\"let type of CHART_LINE_TYPES\"\n [ngValue]=\"type.val\"\n >\n {{ type.text | translate }}\n </option>\n </select>\n <span></span>\n </div>\n </c8y-form-group>\n </div>\n <div\n class=\"col-xs-6 col-sm-4\"\n *ngIf=\"selectableAxisTypes?.length !== 0\"\n >\n <c8y-form-group class=\"form-group-sm\">\n <label\n for=\"yAxis\"\n translate\n >\n Y-axis\n </label>\n <div class=\"c8y-select-wrapper\">\n <select\n class=\"form-control\"\n formControlName=\"yAxisType\"\n >\n <option\n *ngFor=\"let type of AXIS_TYPES\"\n [ngValue]=\"type.val\"\n >\n {{ type.text | translate }}\n </option>\n </select>\n <span></span>\n </div>\n </c8y-form-group>\n </div>\n </div>\n </fieldset>\n <fieldset\n class=\"c8y-fieldset\"\n *ngIf=\"showOnlyDisplayForm\"\n formGroupName=\"display\"\n >\n <legend>\n <ng-container *ngTemplateOutlet=\"displayHelpButton\"></ng-container>\n </legend>\n <div class=\"tight-grid\">\n <div class=\"col-xs-6 col-sm-4\">\n <c8y-form-group class=\"form-group-sm\">\n <div class=\"c8y-select-wrapper\">\n <select\n class=\"form-control\"\n formControlName=\"renderType\"\n >\n <option\n *ngFor=\"let type of CHART_RENDER_TYPES\"\n [ngValue]=\"type.val\"\n >\n {{ type.text | translate }}\n </option>\n </select>\n </div>\n </c8y-form-group>\n </div>\n </div>\n </fieldset>\n</div>\n\n<ng-template #displayHelpButton>\n <label>\n {{ 'Display' | translate }}\n <button\n class=\"btn-help btn-help--sm\"\n [attr.aria-label]=\"'Help' | translate\"\n popover=\"{{ 'Value displayed when data is aggregated' | translate }}\"\n triggers=\"focus\"\n type=\"button\"\n placement=\"right\"\n ></button>\n </label>\n</ng-template>\n", dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.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: i2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i2.FormGroupName, selector: "[formGroupName]", inputs: ["formGroupName"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "component", type: FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "directive", type: RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "component", type: MessagesComponent, selector: "c8y-messages", inputs: ["show", "defaults", "helpMessage"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: MessageDirective, selector: "c8y-message", inputs: ["name", "text"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: PopoverDirective, selector: "[popover]", inputs: ["adaptivePosition", "boundariesElement", "popover", "popoverContext", "popoverTitle", "placement", "outsideClick", "triggers", "container", "containerClass", "isOpen", "delay"], outputs: ["onShown", "onHidden"], exportAs: ["bs-popover"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: KeyValuePipe, name: "keyvalue" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DatapointAttributesFormComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-datapoint-attributes-form', providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => DatapointAttributesFormComponent),
multi: true
},
{
provide: NG_VALIDATORS,
useExisting: forwardRef(() => DatapointAttributesFormComponent),
multi: true
}
], imports: [
FormsModule,
ReactiveFormsModule,
NgIf,
C8yTranslateDirective,
FormGroupComponent,
RequiredInputPlaceholderDirective,
MessagesComponent,
NgFor,
MessageDirective,
NgClass,
NgTemplateOutlet,
PopoverDirective,
C8yTranslatePipe,
KeyValuePipe
], template: "<div [formGroup]=\"formGroup\">\n <ng-container *ngIf=\"!rawValue?.__template || showFormIfTemplateWasSelected\">\n <fieldset\n class=\"c8y-fieldset\"\n *ngIf=\"formGroup.controls?.label || formGroup.controls?.unit || formGroup.controls?.target\"\n >\n <legend translate>Details</legend>\n <div class=\"row\">\n <div\n class=\"col-md-6\"\n *ngIf=\"formGroup.controls?.label\"\n >\n <c8y-form-group class=\"form-group-sm\">\n <label translate>Label</label>\n <input\n class=\"form-control\"\n name=\"label\"\n formControlName=\"label\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: 'Temperature' }\"\n />\n <c8y-messages\n [show]=\"formGroup.controls.label.touched && formGroup.controls.label.errors\"\n >\n <c8y-message\n *ngFor=\"let item of customValidationErrorMessages['label'] | keyvalue\"\n [name]=\"item.key\"\n [text]=\"item.value\"\n ></c8y-message>\n </c8y-messages>\n </c8y-form-group>\n </div>\n\n <div\n class=\"col-md-6\"\n *ngIf=\"formGroup.controls?.unit\"\n >\n <c8y-form-group class=\"form-group-sm\">\n <label translate>Unit</label>\n <input\n class=\"form-control\"\n name=\"unit\"\n formControlName=\"unit\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: '\u00BAC' }\"\n />\n <c8y-messages\n [show]=\"formGroup.controls.unit.touched && formGroup.controls.unit.errors\"\n >\n <c8y-message\n *ngFor=\"let item of customValidationErrorMessages['unit'] | keyvalue\"\n [name]=\"item.key\"\n [text]=\"item.value\"\n ></c8y-message>\n </c8y-messages>\n </c8y-form-group>\n </div>\n\n <div\n class=\"col-md-6\"\n *ngIf=\"formGroup.controls?.target && showTarget\"\n >\n <c8y-form-group class=\"form-group-sm\"\n [ngClass]=\"{\n 'has-error':\n (range?.touched || formGroup.controls.target.touched) &&\n formGroup.controls?.target?.errors\n }\"\n >\n <label translate>Target</label>\n <input\n class=\"form-control\"\n name=\"target\"\n type=\"number\"\n formControlName=\"target\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: 25 }\"\n />\n <c8y-messages\n [show]=\"\n (range?.touched || formGroup.controls.target.touched) &&\n formGroup.controls.target.errors\n \"\n >\n <c8y-message\n *ngFor=\"let item of customValidationErrorMessages['target'] | keyvalue\"\n [name]=\"item.key\"\n [text]=\"item.value\"\n ></c8y-message>\n </c8y-messages>\n </c8y-form-group>\n </div>\n </div>\n </fieldset>\n <fieldset\n class=\"c8y-fieldset\"\n *ngIf=\"range && showRange\"\n >\n <legend translate>Range</legend>\n <div\n class=\"row\"\n formGroupName=\"range\"\n >\n <div class=\"col-md-6\">\n <c8y-form-group class=\"form-group-sm\"\n [ngClass]=\"{ 'has-error': range?.touched && range?.controls?.min?.errors }\"\n >\n <label translate>Min</label>\n <input\n class=\"form-control\"\n name=\"min\"\n type=\"number\"\n formControlName=\"min\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: 0 }\"\n />\n <c8y-messages [show]=\"range?.touched && range.controls?.min?.errors\"></c8y-messages>\n </c8y-form-group>\n </div>\n <div class=\"col-md-6\">\n <c8y-form-group class=\"form-group-sm\"\n [ngClass]=\"{ 'has-error': range?.touched && range?.controls?.max?.errors }\"\n >\n <label translate>Max</label>\n <input\n class=\"form-control\"\n name=\"max\"\n type=\"number\"\n formControlName=\"max\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: 100 }\"\n />\n <c8y-messages [show]=\"range?.touched && range.controls?.max?.errors\">\n <c8y-message\n *ngFor=\"let item of customValidationErrorMessages['max'] | keyvalue\"\n [name]=\"item.key\"\n [text]=\"item.value\"\n ></c8y-message>\n </c8y-messages>\n </c8y-form-group>\n </div>\n </div>\n </fieldset>\n\n <fieldset\n class=\"c8y-fieldset\"\n formGroupName=\"yellowRange\"\n *ngIf=\"yellowRange && showYellowRange\"\n >\n <legend translate>Yellow range</legend>\n <div class=\"row\">\n <div class=\"col-md-6\">\n <c8y-form-group class=\"form-group-sm\"\n [ngClass]=\"{\n 'has-error':\n (range?.touched || yellowRange?.touched) && yellowRange?.controls?.min?.errors\n }\"\n >\n <label translate>Min</label>\n <input\n class=\"form-control\"\n name=\"min\"\n type=\"number\"\n formControlName=\"min\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: 50 }\"\n />\n <c8y-messages\n [show]=\"(range?.touched || yellowRange?.touched) && yellowRange.controls?.min?.errors\"\n ></c8y-messages>\n </c8y-form-group>\n </div>\n\n <div class=\"col-md-6\">\n <c8y-form-group class=\"form-group-sm\"\n [ngClass]=\"{\n 'has-error':\n (range?.touched || yellowRange?.touched) && yellowRange?.controls?.max?.errors\n }\"\n >\n <label translate>Max</label>\n <input\n class=\"form-control\"\n name=\"max\"\n type=\"number\"\n formControlName=\"max\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: 75 }\"\n />\n <c8y-messages\n [show]=\"(range?.touched || yellowRange?.touched) && yellowRange.controls?.max?.errors\"\n ></c8y-messages>\n </c8y-form-group>\n </div>\n </div>\n </fieldset>\n\n <fieldset\n class=\"c8y-fieldset\"\n formGroupName=\"redRange\"\n *ngIf=\"redRange && showRedRange\"\n >\n <legend translate>Red range</legend>\n <div class=\"row\">\n <div class=\"col-md-6\">\n <c8y-form-group class=\"form-group-sm\"\n [ngClass]=\"{\n 'has-error': (range?.touched || redRange?.touched) && redRange?.controls?.min?.errors\n }\"\n >\n <label translate>Min</label>\n <input\n class=\"form-control\"\n name=\"min\"\n type=\"number\"\n formControlName=\"min\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: 75 }\"\n />\n <c8y-messages\n [show]=\"(range?.touched || redRange?.touched) && redRange.controls?.min?.errors\"\n ></c8y-messages>\n </c8y-form-group>\n </div>\n\n <div class=\"col-md-6\">\n <c8y-form-group class=\"form-group-sm\"\n [ngClass]=\"{\n 'has-error': (range?.touched || redRange?.touched) && redRange?.controls?.max?.errors\n }\"\n >\n <label translate>Max</label>\n <input\n class=\"form-control\"\n name=\"max\"\n type=\"number\"\n formControlName=\"max\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: 100 }\"\n />\n <c8y-messages\n [show]=\"(range?.touched || redRange?.touched) && redRange.controls?.max?.errors\"\n ></c8y-messages>\n </c8y-form-group>\n </div>\n </div>\n </fieldset>\n </ng-container>\n\n <fieldset\n class=\"c8y-fieldset\"\n *ngIf=\"showChartForm\"\n formGroupName=\"chart\"\n >\n <legend translate>Chart</legend>\n <div class=\"tight-grid\">\n <div\n class=\"col-xs-6 col-sm-4\"\n *ngIf=\"selectableChartRenderTypes?.length !== 0\"\n >\n <c8y-form-group class=\"form-group-sm\">\n <ng-container *ngTemplateOutlet=\"displayHelpButton\"></ng-container>\n <div class=\"c8y-select-wrapper\">\n <select\n class=\"form-control\"\n formControlName=\"renderType\"\n >\n <option\n *ngFor=\"let type of CHART_RENDER_TYPES\"\n [ngValue]=\"type.val\"\n >\n {{ type.text | translate }}\n </option>\n </select>\n </div>\n </c8y-form-group>\n </div>\n <div\n class=\"col-xs-6 col-sm-4\"\n *ngIf=\"selectableChartLineTypes?.length !== 0\"\n >\n <c8y-form-group class=\"form-group-sm\">\n <label\n for=\"chartType\"\n translate\n >\n Chart type\n </label>\n <div class=\"c8y-select-wrapper\">\n <select\n class=\"form-control\"\n formControlName=\"lineType\"\n >\n <option\n *ngFor=\"let type of CHART_LINE_TYPES\"\n [ngValue]=\"type.val\"\n >\n {{ type.text | translate }}\n </option>\n </select>\n <span></span>\n </div>\n </c8y-form-group>\n </div>\n <div\n class=\"col-xs-6 col-sm-4\"\n *ngIf=\"selectableAxisTypes?.length !== 0\"\n >\n <c8y-form-group class=\"form-group-sm\">\n <label\n for=\"yAxis\"\n translate\n >\n Y-axis\n </label>\n <div class=\"c8y-select-wrapper\">\n <select\n class=\"form-control\"\n formControlName=\"yAxisType\"\n >\n <option\n *ngFor=\"let type of AXIS_TYPES\"\n [ngValue]=\"type.val\"\n >\n {{ type.text | translate }}\n </option>\n </select>\n <span></span>\n </div>\n </c8y-form-group>\n </div>\n </div>\n </fieldset>\n <fieldset\n class=\"c8y-fieldset\"\n *ngIf=\"showOnlyDisplayForm\"\n formGroupName=\"display\"\n >\n <legend>\n <ng-container *ngTemplateOutlet=\"displayHelpButton\"></ng-container>\n </legend>\n <div class=\"tight-grid\">\n <div class=\"col-xs-6 col-sm-4\">\n <c8y-form-group class=\"form-group-sm\">\n <div class=\"c8y-select-wrapper\">\n <select\n class=\"form-control\"\n formControlName=\"renderType\"\n >\n <option\n *ngFor=\"let type of CHART_RENDER_TYPES\"\n [ngValue]=\"type.val\"\n >\n {{ type.text | translate }}\n </option>\n </select>\n </div>\n </c8y-form-group>\n </div>\n </div>\n </fieldset>\n</div>\n\n<ng-template #displayHelpButton>\n <label>\n {{ 'Display' | translate }}\n <button\n class=\"btn-help btn-help--sm\"\n [attr.aria-label]=\"'Help' | translate\"\n popover=\"{{ 'Value displayed when data is aggregated' | translate }}\"\n triggers=\"focus\"\n type=\"button\"\n placement=\"right\"\n ></button>\n </label>\n</ng-template>\n" }]
}], ctorParameters: () => [{ type: DatapointAttributesFormValidationService }], propDecorators: { selectableChartRenderTypes: [{
type: Input
}], selectableChartLineTypes: [{
type: Input
}], selectableAxisTypes: [{
type: Input
}], showTarget: [{
type: Input
}], showRange: [{
type: Input
}], showYellowRange: [{
type: Input
}], showRedRange: [{
type: Input
}], showChart: [{
type: Input
}], showFormIfTemplateWasSelected: [{
type: Input
}] } });
const DATAPOINT_LIBRARY_FRAGMENT = 'c8y_Kpi';
class DatapointLibraryService {
constructor(inventory, appState, measurements, color) {
this.inventory = inventory;
this.appState = appState;
this.measurements = measurements;
this.color = color;
this.appState.currentUser.pipe(filter(user => !user)).subscribe(() => {
this.cache = undefined;
});
}
async getAllDatapointLibraryEntriesCached(forceCacheRenew = false) {
if (forceCacheRenew) {
this.cache = undefined;
}
if (!this.cache) {
this.cache = this.getAllDatapointLibraryEntries();
}
return this.cache;
}
async getFirstDatapointLibraryPage() {
const filterObj = {
currentPage: 1,
pageSize: 50,
fragmentType: DATAPOINT_LIBRARY_FRAGMENT,
withTotalPages: true
};
return (await this.inventory.list(filterObj));
}
async getAllDatapointLibraryItemsCached() {
if (!this.cache) {
this.cache = this.getAllDatapointLibraryEntries();
}
const res = await this.cache;
return res.map(tmp => tmp[DATAPOINT_LIBRARY_FRAGMENT]);
}
async updateDatapoints(datapoints, skipUpdatingTarget = false) {
if (!Array.isArray(datapoints)) {
return datapoints;
}
const currentTargetsPromise = !skipUpdatingTarget
? this.getCurrentVersionOfTargetsFromDatapoints(datapoints)
: Promise.resolve([]);
const [currentTemplates, currentTargets] = await Promise.all([
this.getCurrentTemplatesFromDatapoints(datapoints),
currentTargetsPromise
]);
const currentTemplateVersions = currentTemplates
.map(tmp => this.mapDatapointLibraryEntry(tmp))
.filter(tmp => !!tmp);
for (const datapoint of datapoints) {
const { fragment, series, __active, __target, color, label, __template } = datapoint;
const foundCurrentTemplateVersion = currentTemplateVersions.find(tmp => tmp.__template === datapoint.__template);
if (foundCurrentTemplateVersion) {
Object.assign(datapoint, foundCurrentTemplateVersion);
Object.assign(datapoint, {
fragment,
series,
__active,
__target,
color,
label,
__template
});
}
const foundCurrentTarget = currentTargets.find(target => target.id === __target?.id);
if (foundCurrentTarget) {
const { id, name } = foundCurrentTarget;
datapoint.__target = { id, name };
}
}
return datapoints;
}
async getDatapointsOfAsset(parentReference, ignoreDatapointTemplates, datapointTemplatesOnly = false) {
const [kpiResponse, details] = await Promise.all([
(ignoreDatapointTemplates
? Promise.resolve(null)
: this.inventory.assetKPIsList(parentReference, { pageSize: MAX_PAGE_SIZE })),
this.inventory.getMeasurementsAndSeries(parentReference)
]);
const kpis = kpiResponse && kpiResponse.data ? kpiResponse.data : [];
const sortedDetails = sortBy(details, ['fragment', 'series']);
return await this.combineFragmentSeriesTuplesWithDetails(sortedDetails, parentReference, kpis, datapointTemplatesOnly);
}
/**
* Requests the last measurement with the given fragment and series to extract it's unit.
* If the source attribute is provided, it will check the last measurement for this specific source.
* @returns found unit or an empty string instead
*/
async guessUnitOfDatapoint(fragment, series, source) {
const measurementfilter = {
valueFragmentSeries: series,
valueFragmentType: fragment,
pageSize: 1,
revert: true,
dateFrom: '1970-01-01'
};
if (source?.id) {
measurementfilter.source = source?.id;
}
try {
const { data: lastMeasurements } = await this.measurements.list(measurementfilter);
const measurement = lastMeasurements[0];
if (measurement) {
const pathToUnit = `${fragment}.${series}.unit`;
const unit = get(measurement, pathToUnit);
if (unit?.length && typeof unit === 'string') {
return unit;
}
}
}
catch {
// nothing to do
}
return '';
}
async combineFragmentSeriesTuplesWithDetails(tuples, target, kpis, datapointTemplatesOnly = false) {
const datapoints = tuples
.map(tuple => {
const foundDatapointLibraryEntry = kpis.find(kpi => kpi[DATAPOINT_LIBRARY_FRAGMENT] &&
kpi[DATAPOINT_LIBRARY_FRAGMENT].fragment === tuple.fragment &&
kpi[DATAPOINT_LIBRARY_FRAGMENT].series === tuple.series);
if (!foundDatapointLibraryEntry && datapointTemplatesOnly) {
return null;
}
const datapoint = this.mapDatapointLibraryEntry(foundDatapointLibraryEntry) || tuple;
if (!datapoint.label) {
datapoint.label = `${datapoint.fragment} → ${datapoint.series}`;
}
if (!datapoint.unit?.length) {
datapoint.unit = '';
}
datapoint.__target = target;
return datapoint;
})
.filter(Boolean);
await this.assignColorToDatapoints(datapoints);
return datapoints;
}
async assignColorToDatapoints(datapoints) {
const datapointsWithoutColor = datapoints.filter(datapoint => !datapoint.color);
await Promise.all(datapointsWithoutColor.map(datapoint => this.color
.generateColorForDatapoint(datapoint.fragment, datapoint.series)
.then(color => (datapoint.color = color))));
}
async getAllDatapointLibraryEntries() {
const entries = new Array();
const filterObj = {
currentPage: 1,
pageSize: MAX_PAGE_SIZE,
fragmentType: DATAPOINT_LIBRARY_FRAGMENT
};
let res = await this.inventory.list(filterObj);
while (res.data.length) {
entries.push(...res.data);
if (res.data.length < res.paging.pageSize) {
break;
}
if (!res.paging.nextPage) {
break;
}
res = await res.paging.next();
}
return entries;
}
mapDatapointLibraryEntry(entry) {
if (!entry || !entry[DATAPOINT_LIBRARY_FRAGMENT]) {
return null;
}
const datapoint = entry[DATAPOINT_LIBRARY_FRAGMENT];
datapoint.__template = entry.id;
return datapoint;
}
async getCurrentTemplatesFromDatapoints(datapoints) {
const datapointsWithTemplateId = datapoints.filter(dp => !!dp.__template);
const usedTemplateIds = datapointsWithTemplateId.map(dp => dp.__template);
return await this.getMOsByIds(usedTemplateIds);
}
async getCurrentVersionOfTargetsFromDatapoints(datapoints) {
const datapointsWithTarget = datapoints.filter(dp => !!dp.__target?.id);
const usedTargetIds = datapointsWithTarget.map(dp => dp.__target.id);
return await this.getMOsByIds(usedTargetIds);
}
async getMOsByIds(ids) {
const uniqManagedObjectIds = uniq(ids);
if (!uniqManagedObjectIds.length) {
return [];
}
try {
const { data: managedObjects } = await this.inventory.list({
ids: uniqManagedObjectIds.join(),
pageSize: MAX_PAGE_SIZE
});
return managedObjects;
}
catch {
// Fail silently in case we are not able to talk to the inventory API.
// Should only be reached in case of an server side error.
// instead of failing, pretend like we didn't receive any items.
console.warn(`Failed to get the current version of the following managedObjects: ${uniqManagedObjectIds.join()}.`);
return [];
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DatapointLibraryService, deps: [{ token: i1.InventoryService }, { token: i2$1.AppStateService }, { token: i1.MeasurementService }, { token: i2$1.ColorService }], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DatapointLibraryService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DatapointLibraryService, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}], ctorParameters: () => [{ type: i1.InventoryService }, { type: i2$1.AppStateService }, { type: i1.MeasurementService }, { type: i2$1.ColorService }] });
class DatapointTemplatePopoverComponent {
constructor() {
this.attributes = [
{
label: gettext('Fragment'),
key: 'fragment'
},
{
label: gettext('Series'),
key: 'series'
},
{
label: gettext('Unit'),
key: 'unit'
},
{
label: gettext('Range'),
key: 'min'
},
{
label: gettext('Target'),
key: 'target'
},
{
label: gettext('Yellow range'),
labelColor: 'yellow',
key: 'yellowRangeMin'
},
{
label: gettext('Red range'),
labelColor: 'red',
key: 'redRangeMin'
}
];
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DatapointTemplatePopoverComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: DatapointTemplatePopoverComponent, isStandalone: true, selector: "c8y-datapoint-template-popover", inputs: { datapoint: "datapoint", attributes: "attributes" }, ngImport: i0, template: "<p class=\"text-medium\">\n <i c8yIcon=\"circle\" [style.color]=\"datapoint.color\"></i>\n {{ datapoint.label }}\n</p>\n<p>{{ datapoint.description }}</p>\n\n<ul class=\"list-unstyled small p-t-16\">\n <ng-container *ngFor=\"let attribute of attributes; let i = index\">\n <li\n class=\"p-t-4 p-b-4 d-flex separator-bottom text-nowrap\"\n *ngIf=\"datapoint[attribute.key] !== undefined\"\n >\n <label class=\"small m-b-0 m-r-8\">\n <i *ngIf=\"attribute.labelColor\" c8yIcon=\"square\" [style.color]=\"attribute.labelColor\"></i>\n {{ attribute.label | translate }}\n </label>\n <ng-container [ngSwitch]=\"attribute.key\">\n <span *ngSwitchCase=\"'min'\" class=\"m-l-auto\">\n {{ datapoint['min'] }} \u2014 {{ datapoint['max'] }}\n </span>\n <span *ngSwitchCase=\"'yellowRangeMin'\" class=\"m-l-auto\">\n {{ datapoint['yellowRangeMin'] }} \u2014 {{ datapoint['yellowRangeMax'] }}\n </span>\n <span *ngSwitchCase=\"'redRangeMin'\" class=\"m-l-auto\">\n {{ datapoint['redRangeMin'] }} \u2014 {{ datapoint['redRangeMax'] }}\n </span>\n <span *ngSwitchDefault class=\"m-l-auto\">\n {{ datapoint[attribute.key] }}\n </span>\n </ng-container>\n </li>\n </ng-container>\n</ul>\n", dependencies: [{ kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: NgSwitchDefault, selector: "[ngSwitchDefault]" }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DatapointTemplatePopoverComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-datapoint-template-popover', imports: [IconDirective, NgFor, NgIf, NgSwitch, NgSwitchCase, NgSwitchDefault, C8yTranslatePipe], template: "<p class=\"text-medium\">\n <i c8yIcon=\"circle\" [style.color]=\"datapoint.color\"></i>\n {{ datapoint.label }}\n</p>\n<p>{{ datapoint.description }}</p>\n\n<ul class=\"list-unstyled small p-t-16\">\n <ng-container *ngFor=\"let attribute of attributes; let i = index\">\n <li\n class=\"p-t-4 p-b-4 d-flex separator-bottom text-nowrap\"\n *ngIf=\"datapoint[attribute.key] !== undefined\"\n >\n <label class=\"small m-b-0 m-r-8\">\n <i *ngIf=\"attribute.labelColor\" c8yIcon=\"square\" [style.color]=\"attribute.labelColor\"></i>\n {{ attribute.label | translate }}\n </label>\n <ng-container [ngSwitch]=\"attribute.key\">\n <span *ngSwitchCase=\"'min'\" class=\"m-l-auto\">\n {{ datapoint['min'] }} \u2014 {{ datapoint['max'] }}\n </span>\n <span *ngSwitchCase=\"'yellowRangeMin'\" class=\"m-l-auto\">\n {{ datapoint['yellowRangeMin'] }} \u2014 {{ datapoint['yellowRangeMax'] }}\n </span>\n <span *ngSwitchCase=\"'redRangeMin'\" class=\"m-l-auto\">\n {{ datapoint['redRangeMin'] }} \u2014 {{ datapoint['redRangeMax'] }}\n </span>\n <span *ngSwitchDefault class=\"m-l-auto\">\n {{ datapoint[attribute.key] }}\n </span>\n </ng-container>\n </li>\n </ng-container>\n</ul>\n" }]
}], propDecorators: { datapoint: [{
type: Input
}], attributes: [{
type: Input
}] } });
const AddButtonTypes = {
none: 'none',
addRemove: 'add-remove',
select: 'select'
};
class DatapointSelectorListItemComponent {
constructor(formBuilder) {
this.formBuilder = formBuilder;
this.defaultFormOptions = {};
this.isSelected = false;
this.isCollapsed = true;
this.addButtonType = AddButtonTypes.addRemove;
this.editable = true;
this.showActiveToggle = false;
this.activeToggleDisabled = false;
this.showOptions = false;
this.actions = [];
this.optionToRemove = false;
this.hasUnlinkTemplateOption = false;
this.added = new EventEmitter();
this.removed = new EventEmitter();
this.colorPickerDisabled = true;
this.disableTypeaheadIfSelected = false;
this.pattern = '';
this.AddButtonTypes = AddButtonTypes;
this.formGroup = this.formBuilder.group({
details: [],
color: [],
__active: [],
__target: [],
fragment: [],
series: [],
__template: []
});
this.isValid$ = this.formGroup.statusChanges.pipe(map(status => status === 'VALID'), startWith(this.formGroup.valid));
}
validate(_control) {
if (this.formGroup?.invalid) {
Object.keys(this.formGroup.controls).forEach(controlName => {
const control = this.formGroup.controls[controlName];
if (control.invalid) {
const errors = control.errors;
if (errors) {
this.errorMessage = Object.values(errors)[0].message;
}
}
});
}
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))).subscribe(fn);
}
registerOnTouched(fn) {
this.formGroup.valueChanges.pipe(take(1)).subscribe(fn);
}
setDisabledState(isDisabled) {
if (this.formGroup?.disabled === isDisabled) {
return;
}
isDisabled ? this.formGroup.disable() : this.formGroup.enable();
}
collapse() {
this.isCollapsed = !this.isCollapsed;
}
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));
}
setPipe(filterStr) {
this.pattern = filterStr;
this.filterPipe = pipe(map((data) => {
return this.filterDatapointLabel(data, filterStr);
}));
}
unlinkDatapointTemplate() {
const details = this.formGroup.value.details || {};
this.resetUnusedProperties(details);
this.formGroup.patchValue({ __template: undefined, details });
}
dataPointTemplateSelected(template) {
const attributesToAssign = [
'color',
'label',
'min',
'max',
'yellowRangeMax',
'yellowRangeMin',
'redRangeMax',
'redRangeMin',
'target',
'orientation',
'unit'
];
const { fragment, series, __target, __active } = this.formGroup.value;
const dataPoint = {
fragment,
series,
__active,
__target,
__template: template.id
};
for (const attribute of attributesToAssign) {
const value = template[DATAPOINT_LIBRARY_FRAGMENT][attribute];
dataPoint[attribute] = value;
}
this.writeValue(dataPoint);
this.setPipe('');
}
resetUnusedProperties(details) {
const { showTarget, showYellowRange, showRedRange } = this.defaultFormOptions;
details.__template = undefined;
if (!showTarget) {
details.target = undefined;
}
if (!showYellowRange) {
details.yellowRangeMin = undefined;
details.yellowRangeMax = undefined;
}
if (!showRedRange) {
details.redRangeMin = undefined;
details.redRangeMax = undefined;
}
}
filterDatapointLabel(kpis, filterStr) {
return kpis.filter((mo) => mo[DATAPOINT_LIBRARY_FRAGMENT] &&
mo[DATAPOINT_LIBRARY_FRAGMENT].label &&
typeof mo[DATAPOINT_LIBRARY_FRAGMENT].label === 'string' &&
mo[DATAPOINT_LIBRARY_FRAGMENT].label.toLowerCase().indexOf(filterStr.toLowerCase()) > -1);
}
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: DatapointSelectorListItemComponent, deps: [{ token: i2.FormBuilder }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: DatapointSelectorListItemComponent, isStandalone: true, selector: "c8y-datapoint-selector-list-item", inputs: { defaultFormOptions: "defaultFormOptions", isSelected: "isSelected", isCollapsed: "isCollapsed", addButtonType: "addButtonType", editable: "editable", showActiveToggle: "showActiveToggle", activeToggleDisabled: "activeToggleDisabled", showOptions: "showOptions", datapointLibraryEntries: "datapointLibraryEntries", actions: "actions", optionToRemove: "optionToRemove", hasUnlinkTemplateOption: "hasUnlinkTemplateOption", colorPickerDisabled: "colorPickerDisabled", disableTypeaheadIfSelected: "disableTypeaheadIfSelected", highlightText: "highlightText" }, outputs: { added: "added", removed: "removed" }, providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => DatapointSelectorListItemComponent),
multi: true
},
{
provide: NG_VALIDATORS,
useExisting: forwardRef(() => DatapointSelectorListItemComponent),
multi: true
}
], queries: [{ propertyName: "dragHandle", first: true, predicate: ListItemDragHandleComponent, descendants: true }], ngImport: i0, template: "<c8y-li class=\"c8y-list__item__collapse--container-small\" [formGroup]=\"formGroup\" #li>\n <c8y-li-drag-handle><ng-content select=\"c8y-li-drag-handle\"></ng-content></c8y-li-drag-handle>\n <c8y-li-checkbox\n class=\"a-s-center p-r-0\"\n *ngIf=\"showActiveToggle\"\n [displayAsSwitch]=\"true\"\n formControlName=\"__active\"\n (click)=\"$event.stopPropagation()\"\n ></c8y-li-checkbox>\n\n <div class=\"d-flex a-i-center \">\n <div class=\"c8y-list__item__colorpicker p-t-0 p-b-0 p-l-16\" [title]=\"'Change color' | translate\">\n <div class=\"c8y-colorpicker\">\n <input\n type=\"color\"\n [attr.aria-label]=\"'Color' | translate\"\n formControlName=\"color\"\n (click)=\"$event.stopPropagation()\"\n />\n <span [style.background-color]=\"formGroup.value.color\"></span>\n </div>\n </div>\n <button\n class=\"btn-clean data-point-label text-truncate\"\n type=\"button\"\n [attr.aria-expanded]=\"!li.collapsed\"\n (click)=\"li.collapsed = !li.collapsed\"\n >\n <span class=\"text-truncate\" [title]=\"formGroup.value.details?.label\">\n <c8y-highlight\n [text]=\"formGroup.value.details?.label\"\n [pattern]=\"highlightText\"\n [shouldTrimPattern]=\"true\"\n ></c8y-highlight>\n </span>\n <small class=\"text-truncate text-muted icon-flex\" *ngIf=\"formGroup.value.__target\">\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 btn-dot btn-dot--danger m-l-auto\"\n type=\"button\"\n [attr.aria-label]=\"'Invalid' | translate\"\n [popover]=\"\n errorMessage\n ? errorMessage\n : ('Some entries are invalid. Check the input fields with red borders.' | translate)\n \"\n triggers=\"focus\"\n placement=\"left\"\n container=\"body\"\n *ngIf=\"!(isValid$ | async)\"\n >\n <i c8yIcon=\"warning\"></i>\n </button>\n <div class=\"m-l-auto a-s-center p-r-4\" *ngIf=\"addButtonType === AddButtonTypes.addRemove\">\n <button\n class=\"btn btn-dot btn-dot--danger\"\n type=\"button\"\n [attr.aria-label]=\"'Remove' | translate\"\n [tooltip]=\"'Remove from selected data points' | translate\"\n [delay]=\"500\"\n (click)=\"addOrRemoveItem()\"\n *ngIf=\"isSelected\"\n [attr.data-cy]=\"'datapoint-selector-list-item--remove-datapoint-button'\"\n >\n <i class=\"icon-20\" c8yIcon=\"minus-circle\"></i>\n </button>\n <button\n class=\"btn btn-dot text-primary\"\n type=\"button\"\n [attr.aria-label]=\"'Add to selected data points' | translate\"\n [tooltip]=\"'Add to selected data points' | translate\"\n [delay]=\"500\"\n [disabled]=\"!(isValid$ | async)\"\n (click)=\"addOrRemoveItem()\"\n *ngIf=\"!isSelected\"\n [attr.data-cy]=\"'datapoint-selector-list-item--add-datapoint-button'\"\n >\n <i class=\"text-primary icon-20\" c8yIcon=\"plus-circle\"></i>\n </button>\n </div>\n <button\n *ngIf=\"addButtonType === AddButtonTypes.select\"\n class=\"btn btn-default btn-sm m-l-auto\"\n type=\"button\"\n [attr.aria-label]=\"'Select' | translate\"\n [tooltip]=\"'Select data point' | translate\"\n [delay]=\"500\"\n (click)=\"addOrRemoveItem()\"\n >{{ 'Select' | translate }}</button>\n </div>\n\n <c8y-li-action\n *ngIf=\"optionToRemove\"\n [icon]=\"'minus-circle'\"\n [label]=\"'Remove from list' | translate\"\n (click)=\"remove()\"\n ></c8y-li-action>\n <c8y-li-action\n *ngFor=\"let action of actions\"\n [icon]=\"action.icon\"\n [label]=\"action.label\"\n (click)=\"action.callback(formGroup.value)\"\n ></c8y-li-action>\n <c8y-li-collapse *ngIf=\"editable\">\n <div class=\"data-point-details\">\n <ul class=\"list-unstyled small m-b-16\">\n <li class=\"p-t-4 p-b-4 d-flex separator-top-bottom\">\n <label class=\"small m-b-0 m-r-8 a-s-start text-muted\" translate>Fragment</label>\n <span class=\"m-l-auto\">\n <c8y-highlight\n [text]=\"formGroup.value.fragment\"\n [pattern]=\"highlightText\"\n [shouldTrimPattern]=\"true\"\n ></c8y-highlight>\n </span>\n </li>\n <li class=\"p-t-4 p-b-4 d-flex separator-bottom\">\n <label class=\"small m-b-0 m-r-8 a-s-start text-muted\" translate>Series</label>\n <span class=\"m-l-auto\">\n <c8y-highlight\n [text]=\"formGroup.value.series\"\n [pattern]=\"highlightText\"\n [shouldTrimPattern]=\"true\"\n ></c8y-highlight>\n </span>\n </li>\n </ul>\n <div\n class=\"form-group form-group-sm\"\n *ngIf=\"datapointLibraryEntries && datapointLibraryEntries | async as libraryEntries\"\n >\n <label>\n {{ 'Data point template' | translate }} \n <button\n class=\"btn-help btn-help--sm\"\n type=\"button\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"datapointHintPopoverTemplate\"\n (click)=\"$event.stopPropagation()\"\n triggers=\"focus\"\n placement=\"top\"\n ></button>\n </label>\n \n\n <ng-template #datapointHintPopoverTemplate>\n {{\n 'Using a data point template sets color, label, unit and ranges. Removing the template allows you to set the values manually.'\n | translate\n }}\n </ng-template>\n <div class=\"input-group input-group-sm\">\n <c8y-typeahead\n class=\"flex-grow\"\n [placeholder]=\"'No template' | translate\"\n [ngModel]=\"formGroup.value.__template ? formGroup.value.details : undefined\"\n [ngModelOptions]=\"{ standalone: true }\"\n (onSearch)=\"setPipe($event)\"\n [displayProperty]=\"'label'\"\n [hideNew]=\"true\"\n *ngIf=\"datapointLibraryEntries\"\n [disabled]=\"isSelected && disableTypeaheadIfSelected\"\n >\n <c8y-li\n class=\"p-l-8 p-r-8 c8y-list__item--link interact\"\n *c8yFor=\"\n let item of datapointLibraryEntries;\n loadMore: 'auto';\n pipe: filterPipe;\n notFound: notFoundTemplate\n \"\n (click)=\"dataPointTemplateSelected(item)\"\n [active]=\"formGroup.value.__template === item.id\"\n [attr.role]=\"'menuitem'\"\n >\n <c8y-highlight [text]=\"item.c8y_Kpi?.label\" [pattern]=\"pattern\"></c8y-highlight>\n <c8y-li-icon icon=\"circle\" [style.color]=\"item.c8y_Kpi?.color\"></c8y-li-icon>\n </c8y-li>\n <ng-template #notFoundTemplate>\n <c8y-li class=\"bg-level-2 p-8\" *ngIf=\"pattern.length > 0\">\n <span>No match found.</span>\n </c8y-li>\n </ng-template>\n </c8y-typeahead>\n <div class=\"input-group-btn\">\n <button\n class=\"btn btn-default\"\n type=\"button\"\n [attr.aria-label]=\"'Info' | translate\"\n [popover]=\"datapointOverviewPopoverTemplate\"\n placement=\"left\"\n triggers=\"focus\"\n container=\"body\"\n *ngIf=\"formGroup.value.__template\"\n >\n <i class=\"text-info\" c8yIcon=\"info\"></i>\n </button>\n </div>\n <div class=\"input-group-btn\">\n <button\n class=\"btn btn-default\"\n type=\"button\"\n tooltip=\"{{ 'Unlink data point template' | translate }}\"\n [attr.aria-label]=\"'Unlink data point template' | translate\"\n [delay]=\"500\"\n placement=\"left\"\n container=\"body\"\n [adaptivePosition]=\"false\"\n *ngIf=\"formGroup.value.__template && hasUnlinkTemplateOption\"\n (click)=\"unlinkDatapointTemplate()\"\n >\n <i c8yIcon=\"unlink\"></i>\n </button>\n </div>\n </div>\n </div>\n <ng-template #datapointOverviewPopoverTemplate>\n <c8y-datapoint-template-popover\n [datapoint]=\"formGroup.value.details\"\n ></c8y-datapoint-template-popover>\n </ng-template>\n <c8y-datapoint-attributes-form\n *ngIf=\"defaultFormOptions\"\n [showTarget]=\"defaultFormOptions.showTarget\"\n [showRange]=\"defaultFormOptions.showRange\"\n [showYellowRange]=\"defaultFormOptions.showYellowRange\"\n [showRedRange]=\"defaultFormOptions.showRedRange\"\n [showChart]=\"defaultFormOptions.showChart\"\n [showFormIfTemplateWasSelected]=\"defaultFormOptions.showFormIfTemplateWasSelected\"\n [selectableChartRenderTypes]=\"defaultFormOptions.selectableChartRenderTypes\"\n [selectableChartLineTypes]=\"defaultFormOptions.selectableChartLineTypes\"\n [selectableAxisTypes]=\"defaultFormOptions.selectableAxisTypes\"\n formControlName=\"details\"\n ></c8y-datapoint-attributes-form>\n </div>\n </c8y-li-collapse>\n</c8y-li>\n", dependencies: [{ kind: "component", type: ListItemComponent, selector: "c8y-list-item, c8y-li", inputs: ["active", "highlighted", "emptyActions", "dense", "collapsed", "selectable"], outputs: ["collapsedChange"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.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: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: ListItemDragHandleComponent, selector: "c8y-list-item-drag-handle, c8y-li-drag-handle" }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: ListItemCheckboxComponent, selector: "c8y-list-item-checkbox, c8y-li-checkbox", inputs: ["selected", "indeterminate", "disabled", "displayAsSwitch"], outputs: ["onSelect"] }, { kind: "directive", type: RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "component", type: HighlightComponent, selector: "c8y-highlight", inputs: ["pattern", "text", "elementClass", "shouldTrimPattern"] }, { kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: PopoverDirective, selector: "[popover]", inputs: ["adaptivePosition", "boundariesElement", "popover", "popoverContext", "popoverTitle", "placement", "outsideClick", "triggers", "container", "containerClass", "isOpen", "delay"], outputs: ["onShown", "onHidden"], exportAs: ["bs-popover"] }, { kind: "directive", type: TooltipDirective, selector: "[tooltip], [tooltipHtml]", inputs: ["adaptivePosition", "tooltip", "placement", "triggers", "container", "containerClass", "boundariesElement", "isOpen", "isDisabled", "delay", "tooltipHtml", "tooltipPlacement", "tooltipIsOpen", "tooltipEnable", "tooltipAppendToBody", "tooltipAnimation", "tooltipClass", "tooltipContext", "tooltipPopupDelay", "tooltipFadeDuration", "tooltipTrigger"], outputs: ["tooltipChange", "onShown", "onHidden", "tooltipStateChanged"], exportAs: ["bs-tooltip"] }, { kind: "component", type: ListItemActionComponent, selector: "c8y-list-item-action, c8y-li-action", inputs: ["label", "icon", "disabled"], outputs: ["click"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "component", type: ListItemCollapseComponent, selector: "c8y-list-item-collapse, c8y-li-collapse", inputs: ["collapseWay"] }, { kind: "directive", type: C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "component", type: TypeaheadComponent, selector: "c8y-typeahead", inputs: ["required", "maxlength", "disabled", "allowFreeEntries", "placeholder", "displayProperty", "icon", "name", "autoClose", "hideNew", "container", "selected", "highlightFirstItem"], outputs: ["onSearch", "onIconClick"] }, { kind: "directive", type: ForOfDirective, selector: "[c8yFor]", inputs: ["c8yForOf", "c8yForLoadMore", "c8yForPipe", "c8yForNotFound", "c8yForMaxIterations", "c8yForLoadingTemplate", "c8yForLoadNextLabel", "c8yForLoadingLabel", "c8yForRealtime", "c8yForRealtimeOptions", "c8yForComparator", "c8yForEnableVirtualScroll", "c8yForVirtualScrollElementSize", "c8yForVirtualScrollStrategy", "c8yForVirtualScrollContainerHeight"], outputs: ["c8yForCount", "c8yForChange", "c8yForLoadMoreComponent"] }, { kind: "component", type: ListItemIconComponent, selector: "c8y-list-item-icon, c8y-li-icon", inputs: ["icon", "status"] }, { kind: "component", type: DatapointTemplatePopoverComponent, selector: "c8y-datapoint-template-popover", inputs: ["datapoint", "attributes"] }, { kind: "component", type: DatapointAttributesFormComponent, selector: "c8y-datapoint-attributes-form", inputs: ["selectableChartRenderTypes", "selectableChartLineTypes", "selectableAxisTypes", "showTarget", "showRange", "showYellowRange", "showRedRange", "showChart", "showFormIfTemplateWasSelected"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: AsyncPipe, name: "async" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DatapointSelectorListItemComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-datapoint-selector-list-item', providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => DatapointSelectorListItemComponent),
multi: true
},
{
provide: NG_VALIDATORS,
useExisting: forwardRef(() => DatapointSelectorListItemComponent),
multi: true
}
], imports: [
ListItemComponent,
FormsModule,
ReactiveFormsModule,
ListItemDragHandleComponent,
NgIf,
ListItemCheckboxComponent,
RequiredInputPlaceholderDirective,
HighlightComponent,
IconDirective,
PopoverDirective,
TooltipDirective,
ListItemActionComponent,
NgFor,
ListItemCollapseComponent,
C8yTranslateDirective,
TypeaheadComponent,
ForOfDirective,
ListItemIconComponent,
DatapointTemplatePopoverComponent,
DatapointAttributesFormComponent,
C8yTranslatePipe,
AsyncPipe
], template: "<c8y-li class=\"c8y-list__item__collapse--container-small\" [formGroup]=\"formGroup\" #li>\n <c8y-li-drag-handle><ng-content select=\"c8y-li-drag-handle\"></ng-content></c8y-li-drag-handle>\n <c8y-li-checkbox\n class=\"a-s-center p-r-0\"\n *ngIf=\"showActiveToggle\"\n [displayAsSwitch]=\"true\"\n formControlName=\"__active\"\n (click)=\"$event.stopPropagation()\"\n ></c8y-li-checkbox>\n\n <div class=\"d-flex a-i-center \">\n <div class=\"c8y-list__item__colorpicker p-t-0 p-b-0 p-l-16\" [title]=\"'Change color' | translate\">\n <div class=\"c8y-colorpicker\">\n <input\n type=\"color\"\n [attr.aria-label]=\"'Color' | translate\"\n formControlName=\"color\"\n (click)=\"$event.stopPropagation()\"\n />\n <span [style.background-color]=\"formGroup.value.color\"></span>\n </div>\n </div>\n <button\n class=\"btn-clean data-point-label text-truncate\"\n type=\"button\"\n [attr.aria-expanded]=\"!li.collapsed\"\n (click)=\"li.collapsed = !li.collapsed\"\n >\n <span class=\"text-truncate\" [title]=\"formGroup.value.details?.label\">\n <c8y-highlight\n [text]=\"formGroup.value.details?.label\"\n [pattern]=\"highlightText\"\n [shouldTrimPattern]=\"true\"\n ></c8y-highlight>\n </span>\n <small class=\"text-truncate text-muted icon-flex\" *ngIf=\"formGroup.value.__target\">\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 btn-dot btn-dot--danger m-l-auto\"\n type=\"button\"\n [attr.aria-label]=\"'Invalid' | translate\"\n [popover]=\"\n errorMessage\n ? errorMessage\n : ('Some entries are invalid. Check the input fields with red borders.' | translate)\n \"\n triggers=\"focus\"\n placement=\"left\"\n container=\"body\"\n *ngIf=\"!(isValid$ | async)\"\n >\n <i c8yIcon=\"warning\"></i>\n </button>\n <div class=\"m-l-auto a-s-center p-r-4\" *ngIf=\"addButtonType === AddButtonTypes.addRemove\">\n <button\n class=\"btn btn-dot btn-dot--danger\"\n type=\"button\"\n [attr.aria-label]=\"'Remove' | translate\"\n [tooltip]=\"'Remove from selected data points' | translate\"\n [delay]=\"500\"\n (click)=\"addOrRemoveItem()\"\n *ngIf=\"isSelected\"\n [attr.data-cy]=\"'datapoint-selector-list-item--remove-datapoint-button'\"\n >\n <i class=\"icon-20\" c8yIcon=\"minus-circle\"></i>\n </button>\n <button\n class=\"btn btn-dot text-primary\"\n type=\"button\"\n [attr.aria-label]=\"'Add to selected data points' | translate\"\n [tooltip]=\"'Add to selected data points' | translate\"\n [delay]=\"500\"\n [disabled]=\"!(isValid$ | async)\"\n (click)=\"addOrRemoveItem()\"\n *ngIf=\"!isSelected\"\n [attr.data-cy]=\"'datapoint-selector-list-item--add-datapoint-button'\"\n >\n <i class=\"text-primary icon-20\" c8yIcon=\"plus-circle\"></i>\n </button>\n </div>\n <button\n *ngIf=\"addButtonType === AddButtonTypes.select\"\n class=\"btn btn-default btn-sm m-l-auto\"\n type=\"button\"\n [attr.aria-label]=\"'Select' | translate\"\n [tooltip]=\"'Select data point' | translate\"\n [delay]=\"500\"\n (click)=\"addOrRemoveItem()\"\n >{{ 'Select' | translate }}</button>\n </div>\n\n <c8y-li-action\n *ngIf=\"optionToRemove\"\n [icon]=\"'minus-circle'\"\n [label]=\"'Remove from list' | translate\"\n (click)=\"remove()\"\n ></c8y-li-action>\n <c8y-li-action\n *ngFor=\"let action of actions\"\n [icon]=\"action.icon\"\n [label]=\"action.label\"\n (click)=\"action.callback(formGroup.value)\"\n ></c8y-li-action>\n <c8y-li-collapse *ngIf=\"editable\">\n <div class=\"data-point-details\">\n <ul class=\"list-unstyled small m-b-16\">\n <li class=\"p-t-4 p-b-4 d-flex separator-top-bottom\">\n <label class=\"small m-b-0 m-r-8 a-s-start text-muted\" translate>Fragment</label>\n <span class=\"m-l-auto\">\n <c8y-highlight\n [text]=\"formGroup.value.fragment\"\n [pattern]=\"highlightText\"\n [shouldTrimPattern]=\"true\"\n ></c8y-highlight>\n </span>\n </li>\n <li class=\"p-t-4 p-b-4 d-flex separator-bottom\">\n <label class=\"small m-b-0 m-r-8 a-s-start text-muted\" translate>Series</label>\n <span class=\"m-l-auto\">\n <c8y-highlight\n [text]=\"formGroup.value.series\"\n [pattern]=\"highlightText\"\n [shouldTrimPattern]=\"true\"\n ></c8y-highlight>\n </span>\n </li>\n </ul>\n <div\n class=\"form-group form-group-sm\"\n *ngIf=\"datapointLibraryEntries && datapointLibraryEntries | async as libraryEntries\"\n >\n <label>\n {{ 'Data point template' | translate }} \n <button\n class=\"btn-help btn-help--sm\"\n type=\"button\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"datapointHintPopoverTemplate\"\n (click)=\"$event.stopPropagation()\"\n triggers=\"focus\"\n placement=\"top\"\n ></button>\n </label>\n \n\n <ng-template #datapointHintPopoverTemplate>\n {{\n 'Using a data point template sets color, label, unit and ranges. Removing the template allows you to set the values manually.'\n | translate\n }}\n </ng-template>\n <div class=\"input-group input-group-sm\">\n <c8y-typeahead\n class=\"flex-grow\"\n [placeholder]=\"'No template' | translate\"\n [ngModel]=\"formGroup.value.__template ? formGroup.value.details : undefined\"\n [ngModelOptions]=\"{ standalone: true }\"\n (onSearch)=\"setPipe($event)\"\n [displayProperty]=\"'label'\"\n [hideNew]=\"true\"\n *ngIf=\"datapointLibraryEntries\"\n [disabled]=\"isSelected && disableTypeaheadIfSelected\"\n >\n <c8y-li\n class=\"p-l-8 p-r-8 c8y-list__item--link interact\"\n *c8yFor=\"\n let item of datapointLibraryEntries;\n loadMore: 'auto';\n pipe: filterPipe;\n notFound: notFoundTemplate\n \"\n (click)=\"dataPointTemplateSelected(item)\"\n [active]=\"formGroup.value.__template === item.id\"\n [attr.role]=\"'menuitem'\"\n >\n <c8y-highlight [text]=\"item.c8y_Kpi?.label\" [pattern]=\"pattern\"></c8y-highlight>\n <c8y-li-icon icon=\"circle\" [style.color]=\"item.c8y_Kpi?.color\"></c8y-li-icon>\n </c8y-li>\n <ng-template #notFoundTemplate>\n <c8y-li class=\"bg-level-2 p-8\" *ngIf=\"pattern.length > 0\">\n <span>No match found.</span>\n </c8y-li>\n </ng-template>\n </c8y-typeahead>\n <div class=\"input-group-btn\">\n <button\n class=\"btn btn-default\"\n type=\"button\"\n [attr.aria-label]=\"'Info' | translate\"\n [popover]=\"datapointOverviewPopoverTemplate\"\n placement=\"left\"\n triggers=\"focus\"\n container=\"body\"\n *ngIf=\"formGroup.value.__template\"\n >\n <i class=\"text-info\" c8yIcon=\"info\"></i>\n </button>\n </div>\n <div class=\"input-group-btn\">\n <button\n class=\"btn btn-default\"\n type=\"button\"\n tooltip=\"{{ 'Unlink data point template' | translate }}\"\n [attr.aria-label]=\"'Unlink data point template' | translate\"\n [delay]=\"500\"\n placement=\"left\"\n container=\"body\"\n [adaptivePosition]=\"false\"\n *ngIf=\"formGroup.value.__template && hasUnlinkTemplateOption\"\n (click)=\"unlinkDatapointTemplate()\"\n >\n <i c8yIcon=\"unlink\"></i>\n </button>\n </div>\n </div>\n </div>\n <ng-template #datapointOverviewPopoverTemplate>\n <c8y-datapoint-template-popover\n [datapoint]=\"formGroup.value.details\"\n ></c8y-datapoint-template-popover>\n </ng-template>\n <c8y-datapoint-attributes-form\n *ngIf=\"defaultFormOptions\"\n [showTarget]=\"defaultFormOptions.showTarget\"\n [showRange]=\"defaultFormOptions.showRange\"\n [showYellowRange]=\"defaultFormOptions.showYellowRange\"\n [showRedRange]=\"defaultFormOptions.showRedRange\"\n [showChart]=\"defaultFormOptions.showChart\"\n [showFormIfTemplateWasSelected]=\"defaultFormOptions.showFormIfTemplateWasSelected\"\n [selectableChartRenderTypes]=\"defaultFormOptions.selectableChartRenderTypes\"\n [selectableChartLineTypes]=\"defaultFormOptions.selectableChartLineTypes\"\n [selectableAxisTypes]=\"defaultFormOptions.selectableAxisTypes\"\n formControlName=\"details\"\n ></c8y-datapoint-attributes-form>\n </div>\n </c8y-li-collapse>\n</c8y-li>\n" }]
}], ctorParameters: () => [{ type: i2.FormBuilder }], propDecorators: { defaultFormOptions: [{
type: Input
}], isSelected: [{
type: Input
}], isCollapsed: [{
type: Input
}], addButtonType: [{
type: Input
}], editable: [{
type: Input
}], showActiveToggle: [{
type: Input
}], activeToggleDisabled: [{
type: Input
}], showOptions: [{
type: Input
}], datapointLibraryEntries: [{
type: Input
}], actions: [{
type: Input
}], optionToRemove: [{
type: Input
}], hasUnlinkTemplateOption: [{
type: Input
}], added: [{
type: Output
}], removed: [{
type: Output
}], colorPickerDisabled: [{
type: Input
}], disableTypeaheadIfSelected: [{
type: Input
}], highlightText: [{
type: Input
}], dragHandle: [{
type: ContentChild,
args: [ListItemDragHandleComponent]
}] } });
class IncludesDatapointPipe {
transform(datapointList, datapoint) {
if (!Array.isArray(datapointList) || !datapoint) {
return false;
}
return datapointList.some(tmp => tmp.fragment === datapoint.fragment &&
tmp.series === datapoint.series &&
tmp.__target?.id === datapoint.__target?.id);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: IncludesDatapointPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: IncludesDatapointPipe, isStandalone: true, name: "includesDatapoint" }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: IncludesDatapointPipe, decorators: [{
type: Pipe,
args: [{ name: 'includesDatapoint' }]
}] });
class DatapointLabelPipe {
transform(datapoint, options = { includeDevice: false, doNotUseLabel: false }) {
let label = '';
try {
if (!options?.doNotUseLabel && datapoint.label) {
label = datapoint.label;
}
else {
label = `${datapoint.fragment} → ${datapoint.series}`;
}
if (options?.includeDevice && (datapoint.__target?.name || datapoint.__target?.id)) {
label += ` (${datapoint.__target?.name || datapoint.__target?.id})`;
}
}
catch (e) {
// nothing to do
}
return label;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DatapointLabelPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: DatapointLabelPipe, isStandalone: true, name: "datapointLabel" }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DatapointLabelPipe, decorators: [{
type: Pipe,
args: [{ name: 'datapointLabel' }]
}] });
class DatapointSelectorComponent {
constructor(datapointService) {
this.datapointService = datapointService;
this.allowChangingContext = true;
this.allowDatapointsFromMultipleAssets = true;
this.selectedDatapoints = new Array();
this.defaultActiveState = true;
this.ignoreDatapointTemplates = false;
this.datapointTemplatesOnly = false;
this.guessDatapointUnit = true;
this.allowSearch = true;
this.hideSelection = false;
this.itemsEditable = true;
this.searchString = '';
this.maxNumberOfDatapoints = 50;
this.AddButtonTypes = AddButtonTypes;
this.loadingDatapoints = false;
this.assetSelection = new BehaviorSubject(null);
this.emptyStateSubtitleWhenNoMatchingDataPoints = gettext('Try another search term.');
this.emptyStateSubtitleWhenNoDataPointsInAsset = gettext('Select an asset with data points from the list.');
this.searchString$ = new BehaviorSubject('');
this.touched = false;
}
ngOnInit() {
this.selectorTitle = this.datapointTemplatesOnly
? gettext('Available data point templates')
: gettext('Available data points');
this.emptyStateTitle = this.datapointTemplatesOnly
? gettext('No data point templates to display.')
: gettext('No data points to display.');
this.selectedListTitle = this.datapointTemplatesOnly
? gettext('Selected data point templates')
: gettext('Selected data points');
this.setupObservables();
if (!this.ignoreDatapointTemplates) {
this.datapointLibraryEntries = from(this.datapointService.getFirstDatapointLibraryPage());
}
if (this.contextAsset) {
this.selectionChanged(this.contextAsset);
}
}
writeValue(obj) {
this.selectedDatapoints = obj;
}
registerOnChange(fn) {
this.onChange = fn;
}
registerOnTouched(fn) {
this.onTouched = fn;
}
datapointAdded(dp) {
this.markAsTouched();
dp.__active = this.defaultActiveState;
if (this.guessDatapointUnit && !dp.unit) {
this.datapointService.guessUnitOfDatapoint(dp.fragment, dp.series, dp.__target).then(unit => {
dp.unit = unit;
});
}
this.selectedDatapoints = [...this.selectedDatapoints, dp];
this.emitCurrentSelection();
}
datapointRemoved(dp) {
this.markAsTouched();
this.selectedDatapoints = this.selectedDatapoints.filter(tmp => tmp.fragment !== dp.fragment ||
tmp.series !== dp.series ||
tmp.__target?.id !== dp.__target?.id);
this.emitCurrentSelection();
}
selectionChanged(evt) {
if (Array.isArray(evt) && evt.length !== 0) {
return this.selectAsset(evt[0]);
}
if (!Array.isArray(evt) && evt.items) {
return this.selectionChanged(evt.items);
}
if (!Array.isArray(evt) && evt.id) {
return this.selectAsset(evt);
}
// reset selection
this.assetSelection.next(null);
}
trackByFn(_index, item) {
return `${item.fragment}-${item.__target?.id}-${item.series}`;
}
searchStringChanged(newValue = '') {
this.searchString$.next(newValue);
this.searchString = newValue;
}
setupObservables() {
this.datapoints$ = this.assetSelection.pipe(tap(() => {
this.loadingDatapoints = true;
}), switchMap(asset => asset?.id
? this.datapointService.getDatapointsOfAsset(asset, this.ignoreDatapointTemplates, this.datapointTemplatesOnly)
: []), tap(() => (this.loadingDatapoints = false)), shareReplay(1));
this.searchStringChanges$ = this.searchString$.pipe(distinctUntilChanged(), debounceTime(500), shareReplay(1));
this.filteredDatapoints$ = combineLatest([this.searchStringChanges$, this.datapoints$]).pipe(map(([searchString, datapoints]) => {
if (!searchString) {
return datapoints;
}
const lowerCaseSearchString = searchString.toLowerCase();
return datapoints.filter(datapoint => this.includesSearchString(datapoint, lowerCaseSearchString));
}), map(filtered => filtered.slice(0, this.maxNumberOfDatapoints)));
}
selectAsset(asset) {
this.assetSelection.next(asset);
this.searchStringChanged();
if (!this.allowDatapointsFromMultipleAssets) {
this.clearSelection();
}
}
clearSelection() {
this.selectedDatapoints = [];
this.emitCurrentSelection();
}
emitCurrentSelection() {
this.onChange(this.selectedDatapoints);
}
markAsTouched() {
if (!this.touched) {
this.onTouched();
this.touched = true;
}
}
includesSearchString(datapoint, lowerCaseSearchString) {
const label = datapoint.label?.toLowerCase();
if (label && label.includes(lowerCaseSearchString)) {
return true;
}
const fragment = datapoint.fragment?.toLowerCase();
if (fragment && fragment.includes(lowerCaseSearchString)) {
return true;
}
const series = datapoint.series?.toLowerCase();
if (series && series.includes(lowerCaseSearchString)) {
return true;
}
return false;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DatapointSelectorComponent, deps: [{ token: DatapointLibraryService }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: DatapointSelectorComponent, isStandalone: true, selector: "c8y-datapoint-selector", inputs: { contextAsset: "contextAsset", allowChangingContext: "allowChangingContext", allowDatapointsFromMultipleAssets: "allowDatapointsFromMultipleAssets", defaultActiveState: "defaultActiveState", ignoreDatapointTemplates: "ignoreDatapointTemplates", datapointTemplatesOnly: "datapointTemplatesOnly", guessDatapointUnit: "guessDatapointUnit", allowSearch: "allowSearch", hideSelection: "hideSelection", itemsEditable: "itemsEditable" }, providers: [
{
provide: NG_VALUE_ACCESSOR,
multi: true,
useExisting: forwardRef(() => DatapointSelectorComponent)
}
], ngImport: i0, template: "<div\n class=\"d-grid grid__row--1 fit-h\"\n [ngClass]=\"{\n 'grid__col--3-6-3--md': allowChangingContext && !hideSelection,\n 'grid__col--8-4--md': !allowChangingContext && !hideSelection,\n 'grid__col--4-8--md': allowChangingContext && hideSelection\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)=\"selectionChanged($event)\"\n [container]=\"''\"\n [config]=\"{\n view: 'miller',\n groupsSelectable: true,\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\">\n <ng-template #noDeviceEmptyState>\n <div class=\"p-16\">\n <c8y-ui-empty-state\n [icon]=\"'c8y-data-points'\"\n [title]=\"emptyStateTitle | translate\"\n [subtitle]=\"'Select an asset from the list.' | translate\"\n [horizontal]=\"true\"\n ></c8y-ui-empty-state>\n </div>\n </ng-template>\n <ng-template #loadingData>\n <div class=\"p-16 text-center\">\n <c8y-loading></c8y-loading>\n </div>\n </ng-template>\n <div\n class=\"bg-inherit\"\n *ngIf=\"assetSelection | async as asset; else noDeviceEmptyState\"\n >\n <div class=\"p-l-16 p-r-16 p-t-8 p-b-8 sticky-top bg-inherit separator-bottom\">\n <p\n class=\"text-medium text-truncate\"\n [title]=\"selectorTitle | translate\"\n >\n {{ selectorTitle | translate }}\n </p>\n <div\n class=\"input-group input-group-search m-t-4\"\n id=\"search\"\n *ngIf=\"!loadingDatapoints\"\n >\n <input\n class=\"form-control\"\n placeholder=\"Search\u2026\"\n type=\"search\"\n [ngModel]=\"searchString\"\n (ngModelChange)=\"searchStringChanged($event)\"\n />\n <span class=\"input-group-addon\">\n <i\n c8yIcon=\"search\"\n *ngIf=\"!searchString; else clearSearchString\"\n ></i>\n <ng-template #clearSearchString>\n <i\n class=\"text-muted\"\n c8yIcon=\"times\"\n (click)=\"searchStringChanged()\"\n ></i>\n </ng-template>\n </span>\n </div>\n </div>\n <ng-container *ngIf=\"filteredDatapoints$ | async as filteredDatapoints; else loadingData\">\n <ng-container *ngIf=\"!loadingDatapoints; else loadingData\">\n <ng-container *ngIf=\"datapoints$ | async as datapoints\">\n <div\n class=\"p-16\"\n *ngIf=\"!filteredDatapoints.length\"\n >\n <c8y-ui-empty-state\n [icon]=\"'c8y-data-points'\"\n [title]=\"emptyStateTitle | translate\"\n [subtitle]=\"\n datapoints.length\n ? (emptyStateSubtitleWhenNoMatchingDataPoints | translate)\n : (emptyStateSubtitleWhenNoDataPointsInAsset | translate)\n \"\n [horizontal]=\"true\"\n ></c8y-ui-empty-state>\n </div>\n\n <c8y-list-group>\n <c8y-list-item\n class=\"sticky-top\"\n style=\"top: 72px\"\n *ngIf=\"\n datapoints.length > maxNumberOfDatapoints &&\n filteredDatapoints.length >= maxNumberOfDatapoints\n \"\n >\n <div class=\"alert alert-warning m-b-0\">\n {{\n 'Due to the large number, only a subset of data points is displayed. Use search to narrow down the number of results.'\n | translate\n }}\n </div>\n </c8y-list-item>\n <c8y-datapoint-selector-list-item\n class=\"d-contents\"\n [ngModel]=\"dp\"\n [isSelected]=\"selectedDatapoints | includesDatapoint: dp\"\n [datapointLibraryEntries]=\"datapointLibraryEntries\"\n [disableTypeaheadIfSelected]=\"true\"\n [addButtonType]=\"hideSelection ? AddButtonTypes.select : AddButtonTypes.addRemove\"\n (added)=\"datapointAdded($event)\"\n (removed)=\"datapointRemoved($event)\"\n [highlightText]=\"searchStringChanges$ | async\"\n *ngFor=\"let dp of filteredDatapoints; trackBy: trackByFn\"\n [editable]=\"itemsEditable\"\n ></c8y-datapoint-selector-list-item>\n </c8y-list-group>\n </ng-container>\n </ng-container>\n </ng-container>\n </div>\n </div>\n <!-- last column -->\n <div\n class=\"inner-scroll bg-level-1\"\n *ngIf=\"!hideSelection\"\n >\n <p\n class=\"text-medium p-l-16 p-r-16 p-t-8 p-b-8 separator-bottom sticky-top text-truncate\"\n [title]=\"selectedListTitle | translate\"\n >\n {{ selectedListTitle | translate }}\n </p>\n <div\n class=\"d-flex flex-wrap gap-8 p-l-16 p-r-16 p-t-8 p-b-16\"\n *ngIf=\"selectedDatapoints?.length\"\n >\n <div\n class=\"c8y-datapoint-pill\"\n *ngFor=\"let selectedDp of selectedDatapoints\"\n >\n <button\n class=\"c8y-datapoint-pill__btn\"\n [title]=\"'Remove' | translate\"\n type=\"button\"\n (click)=\"datapointRemoved(selectedDp)\"\n >\n <i\n class=\"icon-14\"\n c8yIcon=\"remove\"\n ></i>\n </button>\n <div\n class=\"c8y-datapoint-pill__label\"\n [title]=\"selectedDp | datapointLabel: { doNotUseLabel: true, includeDevice: true }\"\n >\n <i\n class=\"m-r-4 icon-14\"\n c8yIcon=\"circle\"\n [style.color]=\"selectedDp.color\"\n ></i>\n <span class=\"text-truncate\">\n <span class=\"text-truncate\">{{ selectedDp | datapointLabel }}</span>\n <small\n class=\"text-muted text-10\"\n *ngIf=\"selectedDp?.__target?.name\"\n >\n {{ selectedDp?.__target?.name }}\n </small>\n </span>\n </div>\n </div>\n </div>\n <div\n class=\"p-r-8\"\n *ngIf=\"!selectedDatapoints || !selectedDatapoints.length\"\n >\n <c8y-ui-empty-state\n [icon]=\"'c8y-data-points'\"\n [title]=\"emptyStateTitle | translate\"\n [subtitle]=\"\n 'Select the asset, then on the available data points list, click on the plus button on the desired data point.'\n | translate\n \"\n [horizontal]=\"true\"\n ></c8y-ui-empty-state>\n </div>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: MillerViewComponent, selector: "c8y-asset-selector-miller", inputs: ["config", "asset", "selectedDevice", "rootNode", "container"], outputs: ["onSelected", "onClearSelected"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.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: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: EmptyStateComponent, selector: "c8y-ui-empty-state", inputs: ["icon", "title", "subtitle", "horizontal"] }, { kind: "component", type: LoadingComponent, selector: "c8y-loading", inputs: ["layout", "progress", "message"] }, { kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "component", type: ListGroupComponent, selector: "c8y-list-group" }, { kind: "component", type: ListItemComponent, selector: "c8y-list-item, c8y-li", inputs: ["active", "highlighted", "emptyActions", "dense", "collapsed", "selectable"], outputs: ["collapsedChange"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "component", type: DatapointSelectorListItemComponent, selector: "c8y-datapoint-selector-list-item", inputs: ["defaultFormOptions", "isSelected", "isCollapsed", "addButtonType", "editable", "showActiveToggle", "activeToggleDisabled", "showOptions", "datapointLibraryEntries", "actions", "optionToRemove", "hasUnlinkTemplateOption", "colorPickerDisabled", "disableTypeaheadIfSelected", "highlightText"], outputs: ["added", "removed"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: IncludesDatapointPipe, name: "includesDatapoint" }, { kind: "pipe", type: DatapointLabelPipe, name: "datapointLabel" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DatapointSelectorComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-datapoint-selector', providers: [
{
provide: NG_VALUE_ACCESSOR,
multi: true,
useExisting: forwardRef(() => DatapointSelectorComponent)
}
], imports: [
NgClass,
NgIf,
MillerViewComponent,
FormsModule,
EmptyStateComponent,
LoadingComponent,
IconDirective,
ListGroupComponent,
ListItemComponent,
NgFor,
DatapointSelectorListItemComponent,
C8yTranslatePipe,
AsyncPipe,
IncludesDatapointPipe,
DatapointLabelPipe
], template: "<div\n class=\"d-grid grid__row--1 fit-h\"\n [ngClass]=\"{\n 'grid__col--3-6-3--md': allowChangingContext && !hideSelection,\n 'grid__col--8-4--md': !allowChangingContext && !hideSelection,\n 'grid__col--4-8--md': allowChangingContext && hideSelection\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)=\"selectionChanged($event)\"\n [container]=\"''\"\n [config]=\"{\n view: 'miller',\n groupsSelectable: true,\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\">\n <ng-template #noDeviceEmptyState>\n <div class=\"p-16\">\n <c8y-ui-empty-state\n [icon]=\"'c8y-data-points'\"\n [title]=\"emptyStateTitle | translate\"\n [subtitle]=\"'Select an asset from the list.' | translate\"\n [horizontal]=\"true\"\n ></c8y-ui-empty-state>\n </div>\n </ng-template>\n <ng-template #loadingData>\n <div class=\"p-16 text-center\">\n <c8y-loading></c8y-loading>\n </div>\n </ng-template>\n <div\n class=\"bg-inherit\"\n *ngIf=\"assetSelection | async as asset; else noDeviceEmptyState\"\n >\n <div class=\"p-l-16 p-r-16 p-t-8 p-b-8 sticky-top bg-inherit separator-bottom\">\n <p\n class=\"text-medium text-truncate\"\n [title]=\"selectorTitle | translate\"\n >\n {{ selectorTitle | translate }}\n </p>\n <div\n class=\"input-group input-group-search m-t-4\"\n id=\"search\"\n *ngIf=\"!loadingDatapoints\"\n >\n <input\n class=\"form-control\"\n placeholder=\"Search\u2026\"\n type=\"search\"\n [ngModel]=\"searchString\"\n (ngModelChange)=\"searchStringChanged($event)\"\n />\n <span class=\"input-group-addon\">\n <i\n c8yIcon=\"search\"\n *ngIf=\"!searchString; else clearSearchString\"\n ></i>\n <ng-template #clearSearchString>\n <i\n class=\"text-muted\"\n c8yIcon=\"times\"\n (click)=\"searchStringChanged()\"\n ></i>\n </ng-template>\n </span>\n </div>\n </div>\n <ng-container *ngIf=\"filteredDatapoints$ | async as filteredDatapoints; else loadingData\">\n <ng-container *ngIf=\"!loadingDatapoints; else loadingData\">\n <ng-container *ngIf=\"datapoints$ | async as datapoints\">\n <div\n class=\"p-16\"\n *ngIf=\"!filteredDatapoints.length\"\n >\n <c8y-ui-empty-state\n [icon]=\"'c8y-data-points'\"\n [title]=\"emptyStateTitle | translate\"\n [subtitle]=\"\n datapoints.length\n ? (emptyStateSubtitleWhenNoMatchingDataPoints | translate)\n : (emptyStateSubtitleWhenNoDataPointsInAsset | translate)\n \"\n [horizontal]=\"true\"\n ></c8y-ui-empty-state>\n </div>\n\n <c8y-list-group>\n <c8y-list-item\n class=\"sticky-top\"\n style=\"top: 72px\"\n *ngIf=\"\n datapoints.length > maxNumberOfDatapoints &&\n filteredDatapoints.length >= maxNumberOfDatapoints\n \"\n >\n <div class=\"alert alert-warning m-b-0\">\n {{\n 'Due to the large number, only a subset of data points is displayed. Use search to narrow down the number of results.'\n | translate\n }}\n </div>\n </c8y-list-item>\n <c8y-datapoint-selector-list-item\n class=\"d-contents\"\n [ngModel]=\"dp\"\n [isSelected]=\"selectedDatapoints | includesDatapoint: dp\"\n [datapointLibraryEntries]=\"datapointLibraryEntries\"\n [disableTypeaheadIfSelected]=\"true\"\n [addButtonType]=\"hideSelection ? AddButtonTypes.select : AddButtonTypes.addRemove\"\n (added)=\"datapointAdded($event)\"\n (removed)=\"datapointRemoved($event)\"\n [highlightText]=\"searchStringChanges$ | async\"\n *ngFor=\"let dp of filteredDatapoints; trackBy: trackByFn\"\n [editable]=\"itemsEditable\"\n ></c8y-datapoint-selector-list-item>\n </c8y-list-group>\n </ng-container>\n </ng-container>\n </ng-container>\n </div>\n </div>\n <!-- last column -->\n <div\n class=\"inner-scroll bg-level-1\"\n *ngIf=\"!hideSelection\"\n >\n <p\n class=\"text-medium p-l-16 p-r-16 p-t-8 p-b-8 separator-bottom sticky-top text-truncate\"\n [title]=\"selectedListTitle | translate\"\n >\n {{ selectedListTitle | translate }}\n </p>\n <div\n class=\"d-flex flex-wrap gap-8 p-l-16 p-r-16 p-t-8 p-b-16\"\n *ngIf=\"selectedDatapoints?.length\"\n >\n <div\n class=\"c8y-datapoint-pill\"\n *ngFor=\"let selectedDp of selectedDatapoints\"\n >\n <button\n class=\"c8y-datapoint-pill__btn\"\n [title]=\"'Remove' | translate\"\n type=\"button\"\n (click)=\"datapointRemoved(selectedDp)\"\n >\n <i\n class=\"icon-14\"\n c8yIcon=\"remove\"\n ></i>\n </button>\n <div\n class=\"c8y-datapoint-pill__label\"\n [title]=\"selectedDp | datapointLabel: { doNotUseLabel: true, includeDevice: true }\"\n >\n <i\n class=\"m-r-4 icon-14\"\n c8yIcon=\"circle\"\n [style.color]=\"selectedDp.color\"\n ></i>\n <span class=\"text-truncate\">\n <span class=\"text-truncate\">{{ selectedDp | datapointLabel }}</span>\n <small\n class=\"text-muted text-10\"\n *ngIf=\"selectedDp?.__target?.name\"\n >\n {{ selectedDp?.__target?.name }}\n </small>\n </span>\n </div>\n </div>\n </div>\n <div\n class=\"p-r-8\"\n *ngIf=\"!selectedDatapoints || !selectedDatapoints.length\"\n >\n <c8y-ui-empty-state\n [icon]=\"'c8y-data-points'\"\n [title]=\"emptyStateTitle | translate\"\n [subtitle]=\"\n 'Select the asset, then on the available data points list, click on the plus button on the desired data point.'\n | translate\n \"\n [horizontal]=\"true\"\n ></c8y-ui-empty-state>\n </div>\n </div>\n</div>\n" }]
}], ctorParameters: () => [{ type: DatapointLibraryService }], propDecorators: { contextAsset: [{
type: Input
}], allowChangingContext: [{
type: Input
}], allowDatapointsFromMultipleAssets: [{
type: Input
}], defaultActiveState: [{
type: Input
}], ignoreDatapointTemplates: [{
type: Input
}], datapointTemplatesOnly: [{
type: Input
}], guessDatapointUnit: [{
type: Input
}], allowSearch: [{
type: Input
}], hideSelection: [{
type: Input
}], itemsEditable: [{
type: Input
}] } });
class DatapointSelectorModalComponent {
constructor(bsModal, elementRef) {
this.bsModal = bsModal;
this.elementRef = elementRef;
this.allowChangingContext = true;
this.allowSearch = true;
this.allowDatapointsFromMultipleAssets = true;
this.selectedDatapoints = new Array();
this.title = gettext('Data point selector');
this.saveButtonLabel = gettext('Add data points');
this.guessDatapointUnit = true;
this.defaultActiveState = true;
this.finishWithFirstSelection = false;
this.ignoreDatapointTemplates = false;
this.itemsEditable = true;
this.datapointTemplatesOnly = false;
this.assetSelectorConfig = {};
this.result = new Promise((resolve, reject) => {
this.save = resolve;
this.cancel = reject;
});
}
ngOnInit() {
this.applyWorkaroundForAngularJSModalsThatMightBeOpenedInParallel();
}
saveChanges() {
this.bsModal.hide();
this.save(this.selectedDatapoints);
}
close() {
this.bsModal.hide();
this.cancel();
}
selectionChange(selection) {
this.selectedDatapoints = selection;
if (this.finishWithFirstSelection) {
this.saveChanges();
}
}
/**
* changes the z-index of the modal, to make sure this modal is on top.
* required as angularJS modals are placing them selves on top via adjusting z-index.
* while angular modals use the order within the dom for this..
*/
applyWorkaroundForAngularJSModalsThatMightBeOpenedInParallel() {
const modalContainer = get(this.elementRef, 'nativeElement.parentElement.parentElement.parentElement');
modalContainer.style.zIndex = '1060';
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DatapointSelectorModalComponent, deps: [{ token: i1$1.BsModalRef }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: DatapointSelectorModalComponent, isStandalone: true, selector: "c8y-datapoint-selector-modal", ngImport: i0, template: "<div class=\"modal-header separator\">\n <h4 id=\"modal-title\" class=\"text-medium\">{{ title | translate }}</h4>\n</div>\n<div class=\"modal-inner-scroll modal-inner-scroll--fixed\" id=\"modal-body\">\n <c8y-datapoint-selector\n [contextAsset]=\"contextAsset\"\n [guessDatapointUnit]=\"guessDatapointUnit\"\n [allowDatapointsFromMultipleAssets]=\"allowDatapointsFromMultipleAssets\"\n [allowChangingContext]=\"allowChangingContext\"\n [defaultActiveState]=\"defaultActiveState\"\n [allowSearch]=\"allowSearch\"\n [hideSelection]=\"finishWithFirstSelection\"\n [ignoreDatapointTemplates]=\"ignoreDatapointTemplates\"\n [datapointTemplatesOnly]=\"datapointTemplatesOnly\"\n [itemsEditable]=\"itemsEditable\"\n [ngModel]=\"selectedDatapoints\"\n (ngModelChange)=\"selectionChange($event)\"\n ></c8y-datapoint-selector>\n</div>\n<div class=\"modal-footer\">\n <button\n type=\"button\"\n [title]=\"'Cancel' | translate\"\n class=\"btn btn-default\"\n (click)=\"close()\"\n translate\n >\n Cancel\n </button>\n <button\n [title]=\"saveButtonLabel | translate\"\n class=\"btn btn-primary\"\n [disabled]=\"!selectedDatapoints?.length\"\n (click)=\"saveChanges()\"\n *ngIf=\"!finishWithFirstSelection\"\n >\n {{ saveButtonLabel | translate }}\n </button>\n</div>\n", dependencies: [{ kind: "component", type: DatapointSelectorComponent, selector: "c8y-datapoint-selector", inputs: ["contextAsset", "allowChangingContext", "allowDatapointsFromMultipleAssets", "defaultActiveState", "ignoreDatapointTemplates", "datapointTemplatesOnly", "guessDatapointUnit", "allowSearch", "hideSelection", "itemsEditable"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DatapointSelectorModalComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-datapoint-selector-modal', imports: [DatapointSelectorComponent, FormsModule, C8yTranslateDirective, NgIf, C8yTranslatePipe], template: "<div class=\"modal-header separator\">\n <h4 id=\"modal-title\" class=\"text-medium\">{{ title | translate }}</h4>\n</div>\n<div class=\"modal-inner-scroll modal-inner-scroll--fixed\" id=\"modal-body\">\n <c8y-datapoint-selector\n [contextAsset]=\"contextAsset\"\n [guessDatapointUnit]=\"guessDatapointUnit\"\n [allowDatapointsFromMultipleAssets]=\"allowDatapointsFromMultipleAssets\"\n [allowChangingContext]=\"allowChangingContext\"\n [defaultActiveState]=\"defaultActiveState\"\n [allowSearch]=\"allowSearch\"\n [hideSelection]=\"finishWithFirstSelection\"\n [ignoreDatapointTemplates]=\"ignoreDatapointTemplates\"\n [datapointTemplatesOnly]=\"datapointTemplatesOnly\"\n [itemsEditable]=\"itemsEditable\"\n [ngModel]=\"selectedDatapoints\"\n (ngModelChange)=\"selectionChange($event)\"\n ></c8y-datapoint-selector>\n</div>\n<div class=\"modal-footer\">\n <button\n type=\"button\"\n [title]=\"'Cancel' | translate\"\n class=\"btn btn-default\"\n (click)=\"close()\"\n translate\n >\n Cancel\n </button>\n <button\n [title]=\"saveButtonLabel | translate\"\n class=\"btn btn-primary\"\n [disabled]=\"!selectedDatapoints?.length\"\n (click)=\"saveChanges()\"\n *ngIf=\"!finishWithFirstSelection\"\n >\n {{ saveButtonLabel | translate }}\n </button>\n</div>\n" }]
}], ctorParameters: () => [{ type: i1$1.BsModalRef }, { type: i0.ElementRef }] });
class DatapointSelectorService {
constructor(modal) {
this.modal = modal;
}
selectDataPoints(initialState = {}) {
const modal = this.modal.show(DatapointSelectorModalComponent, {
ignoreBackdropClick: true,
keyboard: false,
initialState,
ariaDescribedby: 'modal-body',
ariaLabelledBy: 'modal-title',
class: 'modal-lg'
});
const content = modal.content;
return content.result;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DatapointSelectorService, deps: [{ token: i1$1.BsModalService }], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DatapointSelectorService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DatapointSelectorService, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}], ctorParameters: () => [{ type: i1$1.BsModalService }] });
class DatapointSelectionListComponent {
constructor(datapointSelector, datapointLibrary, formBuilder, widgetComponent) {
this.datapointSelector = datapointSelector;
this.datapointLibrary = datapointLibrary;
this.formBuilder = formBuilder;
this.widgetComponent = widgetComponent;
this.actions = [];
/**
* Enable or disable drag and drop functionality.
*/
this.allowDragAndDrop = true;
this.config = {};
this.defaultFormOptions = {};
/**
* Minimum number of active datapoints required in the list.
*/
this.minActiveCount = 1;
this.resolveContext = true;
this.listTitle = '';
this.maxActiveCountReached = false;
this.AddButtonTypes = AddButtonTypes;
this.usedValidators = {};
this.formArray = this.formBuilder.array([]);
this.isValid = this.formArray.statusChanges.pipe(map(status => status === 'VALID'));
this.datapointLibraryEntries = from(this.datapointLibrary.getFirstDatapointLibraryPage()).pipe(shareReplay());
this.change = this.formArray.valueChanges.pipe(map(res => this.transformValue(res)));
}
ngOnChanges(changes) {
if (!changes.maxActiveCount && !changes.minActiveCount) {
return;
}
if (changes.maxActiveCount) {
this.usedValidators.maxActiveCount = C8yValidators.maxActiveCount(this.maxActiveCount);
}
if (changes.minActiveCount) {
this.usedValidators.minActiveCount = C8yValidators.minActiveCount(this.minActiveCount);
}
const validators = Object.values(this.usedValidators);
this.formArray.setValidators(validators);
}
registerOnTouched(fn) {
this.formArray.valueChanges.pipe(take(1)).subscribe(fn);
}
validate(_control) {
return this.formArray.valid ? null : { formInvalid: {} };
}
ngOnInit() {
const context = this.widgetComponent?.context;
if (context?.id && this.resolveContext) {
const { name, id, c8y_IsDevice } = context;
this.config.contextAsset = { name, id, c8y_IsDevice };
}
}
writeValue(obj) {
this.formArray.clear();
if (obj?.length) {
obj.forEach(val => {
const formgroup = this.formBuilder.group({ details: [] });
formgroup.patchValue({ details: val });
this.formArray.push(formgroup);
});
}
this.calculateMaxActiveCount();
}
registerOnChange(fn) {
this.formArray.valueChanges
.pipe(map(res => this.transformValue(res)),
// check maxActiveCount
tap(() => {
this.calculateMaxActiveCount();
}))
.subscribe(fn);
}
add() {
const allowChangingContext = !this.widgetComponent?.isDeviceTypeDashboard && this.config?.allowChangingContext !== false;
this.datapointSelector
.selectDataPoints({
...(this.config || {}),
selectedDatapoints: this.transformValue(this.formArray.value),
defaultActiveState: true,
allowChangingContext,
allowSearch: !this.config?.contextAsset
})
.then(result => {
this.writeValue(result);
}, () => {
// nothing to do, modal was closed
});
}
onItemRemoved(index) {
this.formArray.removeAt(index);
}
drop(event) {
const currentSorting = this.formArray.value;
moveItemInArray(currentSorting, event.previousIndex, event.currentIndex);
this.formArray.setValue(currentSorting);
}
transformValue(formArrayValue) {
if (!formArrayValue) {
return [];
}
return formArrayValue.map(tmp => Object.assign({}, ...Object.values(tmp)));
}
calculateMaxActiveCount() {
if (this.maxActiveCount) {
const currentlyActive = this.formArray.value.filter(tmp => tmp.details?.__active).length;
this.maxActiveCountReached = currentlyActive >= this.maxActiveCount;
}
this.maxActiveCountReached = false;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DatapointSelectionListComponent, deps: [{ token: DatapointSelectorService }, { token: DatapointLibraryService }, { token: i2.FormBuilder }, { token: i4.WidgetConfigComponent, optional: true }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: DatapointSelectionListComponent, isStandalone: true, selector: "c8y-datapoint-selection-list", inputs: { actions: "actions", allowDragAndDrop: "allowDragAndDrop", config: "config", defaultFormOptions: "defaultFormOptions", maxActiveCount: "maxActiveCount", minActiveCount: "minActiveCount", resolveContext: "resolveContext", listTitle: "listTitle" }, outputs: { isValid: "isValid", change: "change" }, providers: [
{
provide: NG_VALUE_ACCESSOR,
multi: true,
useExisting: forwardRef(() => DatapointSelectionListComponent)
},
{
provide: NG_VALIDATORS,
useExisting: forwardRef(() => DatapointSelectionListComponent),
multi: true
}
], usesOnChanges: true, ngImport: i0, template: "<div class=\"card-header separator sticky-top bg-inherit\">\n <span\n class=\"card-title h4\"\n *ngIf=\"listTitle\"\n >\n {{ listTitle | translate }}\n </span>\n <span\n class=\"card-title h4\"\n *ngIf=\"!listTitle\"\n >\n {{ 'Data points' | translate }}\n </span>\n</div>\n\n<c8y-list-group\n class=\"flex-grow ff-scroll-fix cdk-droplist\"\n cdkDropList\n (cdkDropListDropped)=\"drop($event)\"\n [cdkDropListDisabled]=\"!allowDragAndDrop || formArray.controls?.length < 2\"\n>\n <div\n class=\"alert alert-warning m-t-8\"\n role=\"alert\"\n ngNonBindable\n *ngIf=\"formArray.errors?.minActiveCount\"\n translate\n [translateParams]=\"formArray.errors?.minActiveCount\"\n >\n At least {{ minActive }} active data points must be selected.\n </div>\n\n <div\n class=\"alert alert-warning m-t-8\"\n role=\"alert\"\n ngNonBindable\n *ngIf=\"formArray.errors?.maxActiveCount\"\n translate\n [translateParams]=\"formArray.errors?.maxActiveCount\"\n >\n At maximum {{ maxActive }} active data points are allowed to be selected.\n </div>\n\n <ng-content select=\".alert\"></ng-content>\n\n <div\n class=\"p-t-8\"\n *ngIf=\"!formArray.controls?.length\"\n >\n <c8y-ui-empty-state\n class=\"p-t-8\"\n [icon]=\"'c8y-data-points'\"\n [title]=\"'No data points to display.' | translate\"\n [subtitle]=\"'Add your first data point.' | translate\"\n [horizontal]=\"true\"\n ></c8y-ui-empty-state>\n </div>\n <div\n [formGroup]=\"dpForm\"\n *ngFor=\"let dpForm of formArray.controls; let index = index\"\n >\n <c8y-datapoint-selector-list-item\n class=\"d-block\"\n [defaultFormOptions]=\"defaultFormOptions\"\n [activeToggleDisabled]=\"maxActiveCountReached\"\n [showActiveToggle]=\"true\"\n [addButtonType]=\"AddButtonTypes.none\"\n [showOptions]=\"true\"\n [editable]=\"true\"\n [colorPickerDisabled]=\"false\"\n [actions]=\"actions\"\n [optionToRemove]=\"true\"\n [datapointLibraryEntries]=\"datapointLibraryEntries\"\n [hasUnlinkTemplateOption]=\"true\"\n formControlName=\"details\"\n (removed)=\"onItemRemoved(index)\"\n cdkDrag\n >\n <c8y-li-drag-handle\n title=\"{{ 'Click and drag to reorder' | translate }}\"\n cdkDragHandle\n >\n <i c8yIcon=\"drag-reorder\"></i>\n </c8y-li-drag-handle>\n </c8y-datapoint-selector-list-item>\n </div>\n</c8y-list-group>\n\n<div class=\"card-footer bg-inherit\">\n <button\n class=\"btn btn-default btn-sm\"\n [title]=\"'Add data point' | translate\"\n type=\"button\"\n data-cy=\"c8y-datapoint-selection-list--add-datapoint-button\"\n (click)=\"add()\"\n >\n <i c8yIcon=\"plus-circle\"></i>\n {{ 'Add data point' | translate }}\n </button>\n</div>\n", dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: ListGroupComponent, selector: "c8y-list-group" }, { kind: "directive", type: CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "component", type: EmptyStateComponent, selector: "c8y-ui-empty-state", inputs: ["icon", "title", "subtitle", "horizontal"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: DatapointSelectorListItemComponent, selector: "c8y-datapoint-selector-list-item", inputs: ["defaultFormOptions", "isSelected", "isCollapsed", "addButtonType", "editable", "showActiveToggle", "activeToggleDisabled", "showOptions", "datapointLibraryEntries", "actions", "optionToRemove", "hasUnlinkTemplateOption", "colorPickerDisabled", "disableTypeaheadIfSelected", "highlightText"], outputs: ["added", "removed"] }, { kind: "directive", type: CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "component", type: ListItemDragHandleComponent, selector: "c8y-list-item-drag-handle, c8y-li-drag-handle" }, { kind: "directive", type: CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DatapointSelectionListComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-datapoint-selection-list', providers: [
{
provide: NG_VALUE_ACCESSOR,
multi: true,
useExisting: forwardRef(() => DatapointSelectionListComponent)
},
{
provide: NG_VALIDATORS,
useExisting: forwardRef(() => DatapointSelectionListComponent),
multi: true
}
], imports: [
NgIf,
ListGroupComponent,
CdkDropList,
C8yTranslateDirective,
EmptyStateComponent,
NgFor,
FormsModule,
ReactiveFormsModule,
DatapointSelectorListItemComponent,
CdkDrag,
ListItemDragHandleComponent,
CdkDragHandle,
IconDirective,
C8yTranslatePipe
], template: "<div class=\"card-header separator sticky-top bg-inherit\">\n <span\n class=\"card-title h4\"\n *ngIf=\"listTitle\"\n >\n {{ listTitle | translate }}\n </span>\n <span\n class=\"card-title h4\"\n *ngIf=\"!listTitle\"\n >\n {{ 'Data points' | translate }}\n </span>\n</div>\n\n<c8y-list-group\n class=\"flex-grow ff-scroll-fix cdk-droplist\"\n cdkDropList\n (cdkDropListDropped)=\"drop($event)\"\n [cdkDropListDisabled]=\"!allowDragAndDrop || formArray.controls?.length < 2\"\n>\n <div\n class=\"alert alert-warning m-t-8\"\n role=\"alert\"\n ngNonBindable\n *ngIf=\"formArray.errors?.minActiveCount\"\n translate\n [translateParams]=\"formArray.errors?.minActiveCount\"\n >\n At least {{ minActive }} active data points must be selected.\n </div>\n\n <div\n class=\"alert alert-warning m-t-8\"\n role=\"alert\"\n ngNonBindable\n *ngIf=\"formArray.errors?.maxActiveCount\"\n translate\n [translateParams]=\"formArray.errors?.maxActiveCount\"\n >\n At maximum {{ maxActive }} active data points are allowed to be selected.\n </div>\n\n <ng-content select=\".alert\"></ng-content>\n\n <div\n class=\"p-t-8\"\n *ngIf=\"!formArray.controls?.length\"\n >\n <c8y-ui-empty-state\n class=\"p-t-8\"\n [icon]=\"'c8y-data-points'\"\n [title]=\"'No data points to display.' | translate\"\n [subtitle]=\"'Add your first data point.' | translate\"\n [horizontal]=\"true\"\n ></c8y-ui-empty-state>\n </div>\n <div\n [formGroup]=\"dpForm\"\n *ngFor=\"let dpForm of formArray.controls; let index = index\"\n >\n <c8y-datapoint-selector-list-item\n class=\"d-block\"\n [defaultFormOptions]=\"defaultFormOptions\"\n [activeToggleDisabled]=\"maxActiveCountReached\"\n [showActiveToggle]=\"true\"\n [addButtonType]=\"AddButtonTypes.none\"\n [showOptions]=\"true\"\n [editable]=\"true\"\n [colorPickerDisabled]=\"false\"\n [actions]=\"actions\"\n [optionToRemove]=\"true\"\n [datapointLibraryEntries]=\"datapointLibraryEntries\"\n [hasUnlinkTemplateOption]=\"true\"\n formControlName=\"details\"\n (removed)=\"onItemRemoved(index)\"\n cdkDrag\n >\n <c8y-li-drag-handle\n title=\"{{ 'Click and drag to reorder' | translate }}\"\n cdkDragHandle\n >\n <i c8yIcon=\"drag-reorder\"></i>\n </c8y-li-drag-handle>\n </c8y-datapoint-selector-list-item>\n </div>\n</c8y-list-group>\n\n<div class=\"card-footer bg-inherit\">\n <button\n class=\"btn btn-default btn-sm\"\n [title]=\"'Add data point' | translate\"\n type=\"button\"\n data-cy=\"c8y-datapoint-selection-list--add-datapoint-button\"\n (click)=\"add()\"\n >\n <i c8yIcon=\"plus-circle\"></i>\n {{ 'Add data point' | translate }}\n </button>\n</div>\n" }]
}], ctorParameters: () => [{ type: DatapointSelectorService }, { type: DatapointLibraryService }, { type: i2.FormBuilder }, { type: i4.WidgetConfigComponent, decorators: [{
type: Optional
}] }], propDecorators: { actions: [{
type: Input
}], allowDragAndDrop: [{
type: Input
}], config: [{
type: Input
}], defaultFormOptions: [{
type: Input
}], maxActiveCount: [{
type: Input
}], minActiveCount: [{
type: Input
}], resolveContext: [{
type: Input
}], listTitle: [{
type: Input
}], isValid: [{
type: Output
}], change: [{
type: Output
}] } });
class FilterDatapointsPipe {
transform(datapoints, searchString) {
if (!searchString || !datapoints || !Array.isArray(datapoints)) {
return datapoints;
}
const lowerCaseSearchString = searchString.toLowerCase().trim();
return datapoints.filter(datapoint => this.includesSearchStringIgnoringCasing(datapoint, lowerCaseSearchString));
}
includesSearchStringIgnoringCasing(datapoint, lowerCaseSearchString) {
const label = datapoint.label?.toLowerCase();
if (label && label.includes(lowerCaseSearchString)) {
return true;
}
const fragment = datapoint.fragment?.toLowerCase();
if (fragment && fragment.includes(lowerCaseSearchString)) {
return true;
}
const series = datapoint.series?.toLowerCase();
if (series && series.includes(lowerCaseSearchString)) {
return true;
}
return false;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FilterDatapointsPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: FilterDatapointsPipe, isStandalone: true, name: "filterDatapoints" }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FilterDatapointsPipe, decorators: [{
type: Pipe,
args: [{ name: 'filterDatapoints' }]
}] });
class DatapointSelectorModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DatapointSelectorModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: DatapointSelectorModule, imports: [AssetSelectorModule,
CollapseModule,
CoreModule,
BsDropdownModule,
ModalModule,
TooltipModule,
PopoverModule,
DragDropModule,
DatapointSelectorComponent,
DatapointSelectorModalComponent,
DatapointSelectorListItemComponent,
DatapointSelectionListComponent,
DatapointTemplatePopoverComponent,
IncludesDatapointPipe,
DatapointLabelPipe,
FilterDatapointsPipe,
DatapointAttributesFormComponent], exports: [DatapointSelectorComponent,
DatapointSelectorModalComponent,
DatapointSelectorListItemComponent,
DatapointSelectionListComponent,
DatapointTemplatePopoverComponent,
IncludesDatapointPipe,
DatapointLabelPipe,
DatapointAttributesFormComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DatapointSelectorModule, imports: [AssetSelectorModule,
CollapseModule,
CoreModule,
BsDropdownModule,
ModalModule,
TooltipModule,
PopoverModule,
DragDropModule,
DatapointSelectorComponent,
DatapointSelectorModalComponent,
DatapointSelectorListItemComponent,
DatapointSelectionListComponent,
DatapointAttributesFormComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DatapointSelectorModule, decorators: [{
type: NgModule,
args: [{
imports: [
AssetSelectorModule,
CollapseModule,
CoreModule,
BsDropdownModule,
ModalModule,
TooltipModule,
PopoverModule,
DragDropModule,
DatapointSelectorComponent,
DatapointSelectorModalComponent,
DatapointSelectorListItemComponent,
DatapointSelectionListComponent,
DatapointTemplatePopoverComponent,
IncludesDatapointPipe,
DatapointLabelPipe,
FilterDatapointsPipe,
DatapointAttributesFormComponent
],
exports: [
DatapointSelectorComponent,
DatapointSelectorModalComponent,
DatapointSelectorListItemComponent,
DatapointSelectionListComponent,
DatapointTemplatePopoverComponent,
IncludesDatapointPipe,
DatapointLabelPipe,
DatapointAttributesFormComponent
]
}]
}] });
/**
* Generated bundle index. Do not edit.
*/
export { AXIS_TYPES, AddButtonTypes, CHART_LINE_TYPES, CHART_RENDER_TYPES, DATAPOINT_LIBRARY_FRAGMENT, DatapointAttributesFormComponent, DatapointAttributesFormValidationService, DatapointLabelPipe, DatapointLibraryService, DatapointSelectionListComponent, DatapointSelectorComponent, DatapointSelectorListItemComponent, DatapointSelectorModalComponent, DatapointSelectorModule, DatapointSelectorService, DatapointTemplatePopoverComponent, IncludesDatapointPipe };
//# sourceMappingURL=c8y-ngx-components-datapoint-selector.mjs.map