@c8y/ngx-components
Version:
Angular modules for Cumulocity IoT applications
438 lines (436 loc) • 68 kB
JavaScript
import * as i0 from '@angular/core';
import { inject, Component, Optional, Input, ViewChild } from '@angular/core';
import * as i4 from '@angular/forms';
import { FormBuilder, NgForm, Validators } from '@angular/forms';
import * as i2 from '@c8y/ngx-components';
import { WidgetTimeContextDateRangeService, gettext, CommonModule, CoreModule, FormsModule, AGGREGATION_ICONS, AGGREGATION_TEXTS } from '@c8y/ngx-components';
import * as i2$1 from '@ngx-translate/core';
import { TranslateService } from '@ngx-translate/core';
import { takeUntil } from 'rxjs/operators';
import { Subject } from 'rxjs';
import * as i7 from '@c8y/ngx-components/datapoint-selector';
import { DatapointSelectorModule } from '@c8y/ngx-components/datapoint-selector';
import { omit, cloneDeep } from 'lodash-es';
import * as i1 from '@c8y/ngx-components/context-dashboard';
import * as i8 from '@c8y/ngx-components/alarm-event-selector';
import { AlarmEventSelectorModule } from '@c8y/ngx-components/alarm-event-selector';
import * as i5$1 from 'ngx-bootstrap/tooltip';
import { TooltipModule } from 'ngx-bootstrap/tooltip';
import * as i6 from 'ngx-bootstrap/popover';
import { PopoverModule } from 'ngx-bootstrap/popover';
import { DATE_SELECTION_EXTENDED, ChartEventsService, ChartAlarmsService, ChartsComponent } from '@c8y/ngx-components/echart';
import { TimeContextComponent } from '@c8y/ngx-components/time-context';
import * as i5 from '@angular/common';
import { CommonModule as CommonModule$1 } from '@angular/common';
import { ALARM_STATUS_LABELS } from '@c8y/client';
import { A11yModule } from '@angular/cdk/a11y';
import { BsDropdownModule } from 'ngx-bootstrap/dropdown';
import * as i8$1 from '@c8y/ngx-components/alarms';
import { AlarmsModule } from '@c8y/ngx-components/alarms';
class DatapointsGraphWidgetConfigComponent {
constructor(widgetConfig, dashboardContextComponent) {
this.widgetConfig = widgetConfig;
this.dashboardContextComponent = dashboardContextComponent;
this.formBuilder = inject(FormBuilder);
this.form = inject(NgForm);
this.translate = inject(TranslateService);
this.widgetTimeContextDateRangeService = inject(WidgetTimeContextDateRangeService);
this.DATE_SELECTION = DATE_SELECTION_EXTENDED;
this.dateSelectionHelp = this.translate.instant(gettext(`Choose how to select a date range, the available options are:
<ul class="m-l-0 p-l-8 m-t-8 m-b-0">
<li>
<b>Widget configuration:</b>
restricts the date selection only to the widget configuration
</li>
<li>
<b>Widget and widget configuration:</b>
restricts the date selection to the widget view and widget configuration only
</li>
<li>
<b>Dashboard time range:</b>
restricts date selection to the global dashboard configuration only
</li>
</ul>`));
this.datapointSelectDefaultFormOptions = {
showRange: true,
showChart: true
};
this.datapointSelectionConfig = {};
this.activeDatapointsExists = false;
this.alarmsOrEventsHaveNoMatchingDps = false;
this.destroy$ = new Subject();
this.formGroup = this.initForm();
}
ngOnInit() {
this.config?.datapoints?.forEach(dp => this.assignContextFromContextDashboard(dp));
this.form.form.addControl('config', this.formGroup);
this.formGroup.patchValue(this.config || {});
this.formGroup.controls.alarms.setValue(this.config?.alarmsEventsConfigs?.filter(ae => ae.timelineType === 'ALARM'));
this.formGroup.controls.events.setValue(this.config?.alarmsEventsConfigs?.filter(ae => ae.timelineType === 'EVENT'));
this.initDateSelection();
this.setActiveDatapointsExists();
this.checkForMatchingDatapoints();
this.formGroup.valueChanges.pipe(takeUntil(this.destroy$)).subscribe(value => {
this.config = {
...value,
alarmsEventsConfigs: [
...(this.formGroup.value.alarms || []),
...(this.formGroup.value.events || [])
]
};
this.setActiveDatapointsExists();
this.checkForMatchingDatapoints();
});
if (this.config?.widgetInstanceGlobalTimeContext) {
this.updateDashboardTimeContext(this.widgetTimeContextDateRangeService.initialTimeRange());
}
if (this.config.dateFrom && this.config.dateTo) {
this.timeProps = {
dateFrom: new Date(this.config?.dateFrom),
dateTo: new Date(this.config?.dateTo),
interval: this.config?.interval,
realtime: this.config?.realtime,
aggregation: this.config?.realtime ? null : this.config?.aggregation
};
}
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
onBeforeSave(config) {
if (this.formGroup.valid && config) {
Object.assign(config, omit(this.formGroup.value, ['alarms', 'events']), {
alarmsEventsConfigs: [
...(this.formGroup.value.alarms || []),
...(this.formGroup.value.events || [])
]
});
return true;
}
return false;
}
timePropsChanged(timeProps) {
if (timeProps.realtime !== this.config.realtime) {
this.formGroup.patchValue({ realtime: timeProps.realtime });
}
if (timeProps.realtime) {
if (timeProps.currentDateContextInterval !== this.formGroup.value.interval) {
this.formGroup.patchValue({ interval: timeProps.currentDateContextInterval });
}
return;
}
const patchValues = {
dateFrom: new Date(timeProps.currentDateContextFromDate),
dateTo: new Date(timeProps.currentDateContextToDate),
interval: timeProps.currentDateContextInterval,
...(timeProps.aggregation && { aggregation: timeProps.aggregation }),
...(timeProps.realtime && { realtime: timeProps.realtime })
};
this.formGroup.patchValue(patchValues);
}
updateDashboardTimeContext(timeProps) {
const initialTimeRange = {
dateFrom: timeProps.dateFrom,
dateTo: timeProps.dateTo,
interval: timeProps.interval || 'custom'
};
if (!this.widgetTimeContextDateRangeService.initialTimeRange()) {
this.widgetTimeContextDateRangeService.updateInitialTimeRange(initialTimeRange);
}
this.formGroup.patchValue({ ...timeProps, ...initialTimeRange });
}
updateTimeRangeOnRealtime(timeRange) {
this.formGroup.patchValue(timeRange, { emitEvent: false });
}
dateSelectionChange(dateSelection) {
this.dateSelection = dateSelection;
if (dateSelection === DATE_SELECTION_EXTENDED.CONFIG) {
this.formGroup.controls.displayDateSelection.enable();
this.formGroup.patchValue({ widgetInstanceGlobalTimeContext: false });
return;
}
// displayDateSelection should be false and disabled when dateSelection is not CONFIG
this.formGroup.controls.displayDateSelection.disable();
this.formGroup.patchValue({
widgetInstanceGlobalTimeContext: true,
realtime: false,
displayDateSelection: false
});
}
assignContextFromContextDashboard(datapoint) {
if (!this.dashboardContextComponent?.isDeviceTypeDashboard) {
return;
}
const context = this.widgetConfig?.context;
if (context?.id) {
const { name, id } = context;
datapoint.__target = { name, id };
this.datapointSelectionConfig.contextAsset = { id };
}
}
checkForMatchingDatapoints() {
const allMatch = this.config?.alarmsEventsConfigs?.every(ae => this.formGroup.value.datapoints?.some(dp => dp.__target?.id === ae.__target?.id));
queueMicrotask(() => {
if (allMatch) {
this.alarmsOrEventsHaveNoMatchingDps = false;
}
else {
this.alarmsOrEventsHaveNoMatchingDps = true;
}
});
}
initForm() {
const form = this.formBuilder.group({
datapoints: [
[],
[Validators.required, Validators.minLength(1)]
],
alarms: [[]],
events: [[]],
displayMarkedLine: [true, []],
displayMarkedPoint: [true, []],
mergeMatchingDatapoints: [true, []],
showLabelAndUnit: [true, []],
displayDateSelection: [false, []],
displayAggregationSelection: [false, []],
widgetInstanceGlobalTimeContext: [false, []],
canDecoupleGlobalTimeContext: [false, []],
dateFrom: [null, []],
dateTo: [null, []],
interval: ['days', [Validators.required]],
aggregation: [null, []],
realtime: [false, [Validators.required]],
showSlider: [true, [Validators.required]],
yAxisSplitLines: [false, [Validators.required]],
xAxisSplitLines: [false, [Validators.required]]
});
return form;
}
initDateSelection() {
if (!this.config?.widgetInstanceGlobalTimeContext) {
this.dateSelection = DATE_SELECTION_EXTENDED.CONFIG;
return;
}
this.dateSelection = DATE_SELECTION_EXTENDED.DASHBOARD_CONTEXT;
this.formGroup.controls.displayDateSelection.disable();
}
setActiveDatapointsExists() {
this.activeDatapointsExists =
(this.config?.datapoints?.filter(dp => dp.__active)?.length || 0) > 0;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DatapointsGraphWidgetConfigComponent, deps: [{ token: i1.WidgetConfigComponent, optional: true }, { token: i1.ContextDashboardComponent, optional: true }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: DatapointsGraphWidgetConfigComponent, isStandalone: true, selector: "c8y-datapoints-graph-widget-config", inputs: { config: "config" }, host: { classAttribute: "d-contents" }, providers: [ChartEventsService, ChartAlarmsService], ngImport: i0, template: "<div class=\"no-card-context d-flex-md fit-h--md\">\n <div class=\"col-md-5 bg-level-1 conf-col inner-scroll p-l-0\">\n <div class=\"p-l-24\">\n <form [formGroup]=\"formGroup\">\n <c8y-datapoint-selection-list\n class=\"bg-level-1 separator-bottom d-block\"\n name=\"datapoints\"\n [minActiveCount]=\"1\"\n [defaultFormOptions]=\"datapointSelectDefaultFormOptions\"\n [config]=\"datapointSelectionConfig\"\n formControlName=\"datapoints\"\n ></c8y-datapoint-selection-list>\n\n <c8y-alarm-event-selection-list\n class=\"bg-level-1 separator-bottom d-block\"\n name=\"alarms\"\n formControlName=\"alarms\"\n [timelineType]=\"'ALARM'\"\n [datapoints]=\"config?.datapoints\"\n ></c8y-alarm-event-selection-list>\n\n <c8y-alarm-event-selection-list\n class=\"bg-inherit\"\n name=\"events\"\n formControlName=\"events\"\n [timelineType]=\"'EVENT'\"\n [datapoints]=\"config?.datapoints\"\n ></c8y-alarm-event-selection-list>\n </form>\n </div>\n </div>\n\n <div class=\"col-md-7 sticky-top p-t-8 inner-scroll widget-preview\">\n <div class=\"p-r-24 d-col fit-h\">\n <div class=\"form-group p-t-8 form-group-sm d-flex a-i-center m-b-8\">\n <div class=\"d-flex a-i-center m-r-4\">\n <label\n class=\"m-b-0\"\n translate\n >\n Date selection\n </label>\n <button\n class=\"btn-help btn-help--sm\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"dateSelectionHelpTemplate\"\n placement=\"bottom\"\n triggers=\"focus\"\n container=\"body\"\n [adaptivePosition]=\"false\"\n ></button>\n </div>\n <div class=\"c8y-select-wrapper\">\n <select\n class=\"form-control input-sm\"\n [ngModel]=\"dateSelection\"\n (ngModelChange)=\"dateSelectionChange($event)\"\n [ngModelOptions]=\"{ standalone: true }\"\n >\n <option\n title=\"{{ 'Dashboard time range' | translate }}\"\n [value]=\"DATE_SELECTION.DASHBOARD_CONTEXT\"\n >\n {{ 'Dashboard time range' | translate }}\n </option>\n <option\n title=\"{{ 'Widget configuration' | translate }}\"\n [value]=\"DATE_SELECTION.CONFIG\"\n >\n {{ 'Widget configuration' | translate }}\n </option>\n </select>\n <span></span>\n </div>\n </div>\n <label class=\"text-12\">{{ 'Options' | translate }}</label>\n <c8y-time-context\n *ngIf=\"\n dateSelection === DATE_SELECTION.CONFIG &&\n formGroup.get('displayDateSelection').value === true\n \"\n [changedDateContext]=\"timeProps\"\n [controlsAvailable]=\"{\n realtime: true,\n timeRange: config?.displayDateSelection,\n interval: config?.displayDateSelection,\n aggregation: config?.displayAggregationSelection\n }\"\n (contextChange)=\"timePropsChanged($event)\"\n ></c8y-time-context>\n <c8y-charts\n class=\"d-block p-relative\"\n *ngIf=\"activeDatapointsExists\"\n [config]=\"config\"\n [alerts]=\"alerts\"\n (timeRangeChangeOnRealtime)=\"updateTimeRangeOnRealtime($event)\"\n (configChangeOnZoomOut)=\"updateDashboardTimeContext($event)\"\n ></c8y-charts>\n\n <c8y-ui-empty-state\n class=\"d-block m-t-24\"\n [icon]=\"'search'\"\n [title]=\"'No data points selected' | translate\"\n [subtitle]=\"'Select data point to render chart' | translate\"\n *ngIf=\"!activeDatapointsExists\"\n ></c8y-ui-empty-state>\n\n <form\n class=\"d-block p-t-8\"\n [formGroup]=\"formGroup\"\n >\n <label>{{ 'Display options' | translate }}</label>\n <fieldset class=\"c8y-fieldset m-b-24 m-t-0\">\n <legend>{{ 'Axis' | translate }}</legend>\n <c8y-form-group class=\"p-b-16 m-b-0 p-t-8 form-group-sm\">\n <label\n class=\"c8y-checkbox\"\n [title]=\"'Y axis helper lines' | translate\"\n >\n <input\n name=\"yAxisSplitLines\"\n type=\"checkbox\"\n formControlName=\"yAxisSplitLines\"\n />\n <span></span>\n <span translate>Y axis helper lines</span>\n </label>\n <label\n class=\"c8y-checkbox\"\n [title]=\"'X axis helper lines' | translate\"\n >\n <input\n name=\"xAxisSplitLines\"\n type=\"checkbox\"\n formControlName=\"xAxisSplitLines\"\n />\n <span></span>\n <span translate>X axis helper lines</span>\n </label>\n <label\n class=\"c8y-checkbox\"\n [title]=\"'Merge axis' | translate\"\n >\n <input\n name=\"mergeMatchingDatapoints\"\n type=\"checkbox\"\n formControlName=\"mergeMatchingDatapoints\"\n />\n <span></span>\n <span translate>Merge matching datapoints into single axis.</span>\n </label>\n </c8y-form-group>\n </fieldset>\n <fieldset class=\"c8y-fieldset m-b-24 m-t-0\">\n <legend>{{ 'Alarms & Events' | translate }}</legend>\n <c8y-form-group class=\"p-b-16 m-b-0 p-t-8 form-group-sm\">\n <label\n class=\"c8y-checkbox\"\n [title]=\"'Show vertical line on every occurrence' | translate\"\n >\n <input\n name=\"displayMarkedLine\"\n type=\"checkbox\"\n formControlName=\"displayMarkedLine\"\n />\n <span></span>\n <span translate>Show vertical line on every occurrence</span>\n </label>\n <label\n class=\"c8y-checkbox\"\n [title]=\"'Show icon when triggered' | translate\"\n >\n <input\n name=\"displayMarkedPoint\"\n type=\"checkbox\"\n formControlName=\"displayMarkedPoint\"\n />\n <span></span>\n <span translate>Show icon when triggered</span>\n <button\n class=\"btn-clean m-l-8\"\n [attr.aria-label]=\"\n 'Some alarms or events have no matching data points. No icons will be shown for them.'\n | translate\n \"\n [tooltip]=\"\n 'Some alarms or events have no matching data points. No icons will be shown for them.'\n | translate\n \"\n container=\"body\"\n type=\"button\"\n *ngIf=\"alarmsOrEventsHaveNoMatchingDps\"\n (click)=\"$event.stopPropagation()\"\n [adaptivePosition]=\"false\"\n >\n <i\n class=\"text-warning\"\n c8yIcon=\"exclamation-triangle\"\n ></i>\n </button>\n </label>\n </c8y-form-group>\n </fieldset>\n <fieldset class=\"c8y-fieldset m-b-24 m-t-0\">\n <legend>{{ 'Chart' | translate }}</legend>\n <c8y-form-group class=\"p-b-16 m-b-0 p-t-8 form-group-sm\">\n <label\n class=\"c8y-checkbox\"\n [title]=\"'Enable date selection in the widget view.' | translate\"\n >\n <input\n name=\"displayDateSelection\"\n type=\"checkbox\"\n formControlName=\"displayDateSelection\"\n />\n <span></span>\n <span translate>Date selection in the widget view.</span>\n <button\n class=\"btn-clean m-l-8\"\n [attr.aria-label]=\"\n 'Date selection in widget view is not possible when using dashboard time range.'\n | translate\n \"\n [tooltip]=\"\n 'Date selection in widget view is not possible when using dashboard time range.'\n | translate\n \"\n container=\"body\"\n type=\"button\"\n *ngIf=\"dateSelection === DATE_SELECTION.DASHBOARD_CONTEXT\"\n (click)=\"$event.stopPropagation()\"\n [adaptivePosition]=\"false\"\n >\n <i\n class=\"text-info\"\n c8yIcon=\"info\"\n ></i>\n </button>\n </label>\n <label\n class=\"c8y-checkbox\"\n [title]=\"'Aggregation selection' | translate\"\n >\n <input\n name=\"displayAggregationSelection\"\n type=\"checkbox\"\n formControlName=\"displayAggregationSelection\"\n />\n <span></span>\n <span translate>Aggregation selection</span>\n </label>\n <label\n class=\"c8y-checkbox\"\n [title]=\"'Show labels and units' | translate\"\n >\n <input\n name=\"showLabelAndUnit\"\n type=\"checkbox\"\n formControlName=\"showLabelAndUnit\"\n />\n <span></span>\n <span translate>Display labels and units on y-axis</span>\n </label>\n <label\n class=\"c8y-checkbox\"\n [title]=\"'Show slider' | translate\"\n >\n <input\n name=\"showSlider\"\n type=\"checkbox\"\n formControlName=\"showSlider\"\n />\n <span></span>\n <span translate>Show slider</span>\n </label>\n </c8y-form-group>\n </fieldset>\n </form>\n </div>\n </div>\n</div>\n<ng-template #dateSelectionHelpTemplate>\n <div [innerHTML]=\"dateSelectionHelp\"></div>\n</ng-template>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: i2.EmptyStateComponent, selector: "c8y-ui-empty-state", inputs: ["icon", "title", "subtitle", "horizontal"] }, { kind: "directive", type: i2.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "pipe", type: i2.C8yTranslatePipe, name: "translate" }, { kind: "directive", type: i2.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: CoreModule }, { kind: "directive", type: i4.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i4.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i4.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i4.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i4.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i4.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i4.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i4.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i2.FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "directive", type: i2.RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "directive", type: i4.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i4.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i5$1.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: "ngmodule", type: PopoverModule }, { kind: "directive", type: i6.PopoverDirective, selector: "[popover]", inputs: ["adaptivePosition", "boundariesElement", "popover", "popoverContext", "popoverTitle", "placement", "outsideClick", "triggers", "container", "containerClass", "isOpen", "delay"], outputs: ["onShown", "onHidden"], exportAs: ["bs-popover"] }, { kind: "component", type: ChartsComponent, selector: "c8y-charts", inputs: ["config", "alerts"], outputs: ["configChangeOnZoomOut", "timeRangeChangeOnRealtime", "datapointOutOfSync", "updateAlarmsAndEvents", "isMarkedAreaEnabled"] }, { kind: "ngmodule", type: DatapointSelectorModule }, { kind: "component", type: i7.DatapointSelectionListComponent, selector: "c8y-datapoint-selection-list", inputs: ["actions", "allowDragAndDrop", "config", "defaultFormOptions", "maxActiveCount", "minActiveCount", "resolveContext", "listTitle"], outputs: ["isValid", "change"] }, { kind: "ngmodule", type: AlarmEventSelectorModule }, { kind: "component", type: i8.AlarmEventSelectionListComponent, selector: "c8y-alarm-event-selection-list", inputs: ["timelineType", "canRemove", "canEdit", "canDragAndDrop", "title", "addButtonLabel", "hideSource", "inline", "activeToggleAsSwitch", "omitProperties", "datapoints", "config"] }, { kind: "component", type: TimeContextComponent, selector: "c8y-time-context", inputs: ["changedDateContext", "controlsAvailable", "context"], outputs: ["contextChange"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DatapointsGraphWidgetConfigComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-datapoints-graph-widget-config', host: { class: 'd-contents' }, standalone: true, imports: [
CommonModule,
CoreModule,
FormsModule,
TooltipModule,
PopoverModule,
ChartsComponent,
DatapointSelectorModule,
AlarmEventSelectorModule,
TimeContextComponent
], providers: [ChartEventsService, ChartAlarmsService], template: "<div class=\"no-card-context d-flex-md fit-h--md\">\n <div class=\"col-md-5 bg-level-1 conf-col inner-scroll p-l-0\">\n <div class=\"p-l-24\">\n <form [formGroup]=\"formGroup\">\n <c8y-datapoint-selection-list\n class=\"bg-level-1 separator-bottom d-block\"\n name=\"datapoints\"\n [minActiveCount]=\"1\"\n [defaultFormOptions]=\"datapointSelectDefaultFormOptions\"\n [config]=\"datapointSelectionConfig\"\n formControlName=\"datapoints\"\n ></c8y-datapoint-selection-list>\n\n <c8y-alarm-event-selection-list\n class=\"bg-level-1 separator-bottom d-block\"\n name=\"alarms\"\n formControlName=\"alarms\"\n [timelineType]=\"'ALARM'\"\n [datapoints]=\"config?.datapoints\"\n ></c8y-alarm-event-selection-list>\n\n <c8y-alarm-event-selection-list\n class=\"bg-inherit\"\n name=\"events\"\n formControlName=\"events\"\n [timelineType]=\"'EVENT'\"\n [datapoints]=\"config?.datapoints\"\n ></c8y-alarm-event-selection-list>\n </form>\n </div>\n </div>\n\n <div class=\"col-md-7 sticky-top p-t-8 inner-scroll widget-preview\">\n <div class=\"p-r-24 d-col fit-h\">\n <div class=\"form-group p-t-8 form-group-sm d-flex a-i-center m-b-8\">\n <div class=\"d-flex a-i-center m-r-4\">\n <label\n class=\"m-b-0\"\n translate\n >\n Date selection\n </label>\n <button\n class=\"btn-help btn-help--sm\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"dateSelectionHelpTemplate\"\n placement=\"bottom\"\n triggers=\"focus\"\n container=\"body\"\n [adaptivePosition]=\"false\"\n ></button>\n </div>\n <div class=\"c8y-select-wrapper\">\n <select\n class=\"form-control input-sm\"\n [ngModel]=\"dateSelection\"\n (ngModelChange)=\"dateSelectionChange($event)\"\n [ngModelOptions]=\"{ standalone: true }\"\n >\n <option\n title=\"{{ 'Dashboard time range' | translate }}\"\n [value]=\"DATE_SELECTION.DASHBOARD_CONTEXT\"\n >\n {{ 'Dashboard time range' | translate }}\n </option>\n <option\n title=\"{{ 'Widget configuration' | translate }}\"\n [value]=\"DATE_SELECTION.CONFIG\"\n >\n {{ 'Widget configuration' | translate }}\n </option>\n </select>\n <span></span>\n </div>\n </div>\n <label class=\"text-12\">{{ 'Options' | translate }}</label>\n <c8y-time-context\n *ngIf=\"\n dateSelection === DATE_SELECTION.CONFIG &&\n formGroup.get('displayDateSelection').value === true\n \"\n [changedDateContext]=\"timeProps\"\n [controlsAvailable]=\"{\n realtime: true,\n timeRange: config?.displayDateSelection,\n interval: config?.displayDateSelection,\n aggregation: config?.displayAggregationSelection\n }\"\n (contextChange)=\"timePropsChanged($event)\"\n ></c8y-time-context>\n <c8y-charts\n class=\"d-block p-relative\"\n *ngIf=\"activeDatapointsExists\"\n [config]=\"config\"\n [alerts]=\"alerts\"\n (timeRangeChangeOnRealtime)=\"updateTimeRangeOnRealtime($event)\"\n (configChangeOnZoomOut)=\"updateDashboardTimeContext($event)\"\n ></c8y-charts>\n\n <c8y-ui-empty-state\n class=\"d-block m-t-24\"\n [icon]=\"'search'\"\n [title]=\"'No data points selected' | translate\"\n [subtitle]=\"'Select data point to render chart' | translate\"\n *ngIf=\"!activeDatapointsExists\"\n ></c8y-ui-empty-state>\n\n <form\n class=\"d-block p-t-8\"\n [formGroup]=\"formGroup\"\n >\n <label>{{ 'Display options' | translate }}</label>\n <fieldset class=\"c8y-fieldset m-b-24 m-t-0\">\n <legend>{{ 'Axis' | translate }}</legend>\n <c8y-form-group class=\"p-b-16 m-b-0 p-t-8 form-group-sm\">\n <label\n class=\"c8y-checkbox\"\n [title]=\"'Y axis helper lines' | translate\"\n >\n <input\n name=\"yAxisSplitLines\"\n type=\"checkbox\"\n formControlName=\"yAxisSplitLines\"\n />\n <span></span>\n <span translate>Y axis helper lines</span>\n </label>\n <label\n class=\"c8y-checkbox\"\n [title]=\"'X axis helper lines' | translate\"\n >\n <input\n name=\"xAxisSplitLines\"\n type=\"checkbox\"\n formControlName=\"xAxisSplitLines\"\n />\n <span></span>\n <span translate>X axis helper lines</span>\n </label>\n <label\n class=\"c8y-checkbox\"\n [title]=\"'Merge axis' | translate\"\n >\n <input\n name=\"mergeMatchingDatapoints\"\n type=\"checkbox\"\n formControlName=\"mergeMatchingDatapoints\"\n />\n <span></span>\n <span translate>Merge matching datapoints into single axis.</span>\n </label>\n </c8y-form-group>\n </fieldset>\n <fieldset class=\"c8y-fieldset m-b-24 m-t-0\">\n <legend>{{ 'Alarms & Events' | translate }}</legend>\n <c8y-form-group class=\"p-b-16 m-b-0 p-t-8 form-group-sm\">\n <label\n class=\"c8y-checkbox\"\n [title]=\"'Show vertical line on every occurrence' | translate\"\n >\n <input\n name=\"displayMarkedLine\"\n type=\"checkbox\"\n formControlName=\"displayMarkedLine\"\n />\n <span></span>\n <span translate>Show vertical line on every occurrence</span>\n </label>\n <label\n class=\"c8y-checkbox\"\n [title]=\"'Show icon when triggered' | translate\"\n >\n <input\n name=\"displayMarkedPoint\"\n type=\"checkbox\"\n formControlName=\"displayMarkedPoint\"\n />\n <span></span>\n <span translate>Show icon when triggered</span>\n <button\n class=\"btn-clean m-l-8\"\n [attr.aria-label]=\"\n 'Some alarms or events have no matching data points. No icons will be shown for them.'\n | translate\n \"\n [tooltip]=\"\n 'Some alarms or events have no matching data points. No icons will be shown for them.'\n | translate\n \"\n container=\"body\"\n type=\"button\"\n *ngIf=\"alarmsOrEventsHaveNoMatchingDps\"\n (click)=\"$event.stopPropagation()\"\n [adaptivePosition]=\"false\"\n >\n <i\n class=\"text-warning\"\n c8yIcon=\"exclamation-triangle\"\n ></i>\n </button>\n </label>\n </c8y-form-group>\n </fieldset>\n <fieldset class=\"c8y-fieldset m-b-24 m-t-0\">\n <legend>{{ 'Chart' | translate }}</legend>\n <c8y-form-group class=\"p-b-16 m-b-0 p-t-8 form-group-sm\">\n <label\n class=\"c8y-checkbox\"\n [title]=\"'Enable date selection in the widget view.' | translate\"\n >\n <input\n name=\"displayDateSelection\"\n type=\"checkbox\"\n formControlName=\"displayDateSelection\"\n />\n <span></span>\n <span translate>Date selection in the widget view.</span>\n <button\n class=\"btn-clean m-l-8\"\n [attr.aria-label]=\"\n 'Date selection in widget view is not possible when using dashboard time range.'\n | translate\n \"\n [tooltip]=\"\n 'Date selection in widget view is not possible when using dashboard time range.'\n | translate\n \"\n container=\"body\"\n type=\"button\"\n *ngIf=\"dateSelection === DATE_SELECTION.DASHBOARD_CONTEXT\"\n (click)=\"$event.stopPropagation()\"\n [adaptivePosition]=\"false\"\n >\n <i\n class=\"text-info\"\n c8yIcon=\"info\"\n ></i>\n </button>\n </label>\n <label\n class=\"c8y-checkbox\"\n [title]=\"'Aggregation selection' | translate\"\n >\n <input\n name=\"displayAggregationSelection\"\n type=\"checkbox\"\n formControlName=\"displayAggregationSelection\"\n />\n <span></span>\n <span translate>Aggregation selection</span>\n </label>\n <label\n class=\"c8y-checkbox\"\n [title]=\"'Show labels and units' | translate\"\n >\n <input\n name=\"showLabelAndUnit\"\n type=\"checkbox\"\n formControlName=\"showLabelAndUnit\"\n />\n <span></span>\n <span translate>Display labels and units on y-axis</span>\n </label>\n <label\n class=\"c8y-checkbox\"\n [title]=\"'Show slider' | translate\"\n >\n <input\n name=\"showSlider\"\n type=\"checkbox\"\n formControlName=\"showSlider\"\n />\n <span></span>\n <span translate>Show slider</span>\n </label>\n </c8y-form-group>\n </fieldset>\n </form>\n </div>\n </div>\n</div>\n<ng-template #dateSelectionHelpTemplate>\n <div [innerHTML]=\"dateSelectionHelp\"></div>\n</ng-template>\n" }]
}], ctorParameters: () => [{ type: i1.WidgetConfigComponent, decorators: [{
type: Optional
}] }, { type: i1.ContextDashboardComponent, decorators: [{
type: Optional
}] }], propDecorators: { config: [{
type: Input
}] } });
class DatapointsGraphWidgetViewComponent {
set config(value) {
this.displayConfig = cloneDeep(value);
}
get config() {
throw Error('"config" property should not be referenced in view component to avoid mutating data.');
}
constructor(formBuilder, translate, widgetTimeContextDateRangeService, dashboardContextComponent) {
this.formBuilder = formBuilder;
this.translate = translate;
this.widgetTimeContextDateRangeService = widgetTimeContextDateRangeService;
this.dashboardContextComponent = dashboardContextComponent;
this.events = [];
this.alarms = [];
this.AGGREGATION_ICONS = AGGREGATION_ICONS;
this.AGGREGATION_TEXTS = AGGREGATION_TEXTS;
this.datapointsOutOfSync = new Map();
this.hasAtLeastOneDatapointActive = true;
this.hasAtLeastOneAlarmActive = true;
this.isMarkedAreaEnabled = false;
this.legendHelp = this.translate.instant(gettext(`<ul class="m-l-0 p-l-8 m-t-8 m-b-0">
<li>
<b>Visibility:</b>
use visibility icon to toggle datapoint, alarm or event visibility on chart. At least one datapoint is required to display chart.
</li>
<li>
<b>Alarm details</b>
Click alarm legend item to highlight area between alarm raised timestamp and alarm cleared timestamp.
You can also click alarm markline on chart to highlight alarm and to pause tooltip. Click on highlighted area or legend item to cancel highlighting.
</li>
</ul>`));
this.disableZoomInLabel = gettext('Disable zoom in');
this.enableZoomInLabel = gettext('Click to enable zoom, then click and drag on the desired area in the chart.');
this.hideDatapointLabel = gettext('Hide data point');
this.showDatapointLabel = gettext('Show data point');
this.destroy$ = new Subject();
this.timeControlsFormGroup = this.initForm();
this.timeControlsFormGroup.valueChanges.pipe(takeUntil(this.destroy$)).subscribe(value => {
this.displayConfig = { ...this.displayConfig, ...value };
});
}
ngOnInit() {
this.displayConfig?.datapoints?.forEach(dp => this.assignContextFromContextDashboard(dp));
if (this.displayConfig.dateFrom && this.displayConfig.dateTo) {
this.timeProps = {
dateFrom: new Date(this.displayConfig?.dateFrom),
dateTo: new Date(this.displayConfig?.dateTo),
interval: this.displayConfig?.interval,
realtime: this.displayConfig?.realtime,
aggregation: this.displayConfig?.realtime ? null : this.displayConfig?.aggregation
};
}
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
ngOnChanges(changes) {
this.timeControlsFormGroup.patchValue(this.displayConfig || {});
const config = changes['config']?.currentValue;
if (config?.date && config?.widgetInstanceGlobalTimeContext && this.displayConfig?.date) {
if (!this.displayConfig.sliderChange) {
this.widgetTimeContextDateRangeService.updateInitialTimeRange(null);
}
this.timePropsChanged({
currentDateContextFromDate: this.displayConfig?.date[0].toISOString(),
currentDateContextToDate: this.displayConfig?.date[1].toISOString(),
currentDateContextInterval: this.displayConfig?.interval,
realtime: this.displayConfig?.realtime,
aggregation: this.displayConfig?.aggregation
});
}
}
timePropsChanged(timeProps) {
const patchValues = {
dateFrom: new Date(timeProps.currentDateContextFromDate),
dateTo: new Date(timeProps.currentDateContextToDate),
interval: timeProps.currentDateContextInterval,
...(timeProps.aggregation && { aggregation: timeProps.aggregation }),
...(timeProps.realtime && { realtime: timeProps.realtime })
};
this.timeControlsFormGroup.patchValue(patchValues);
}
updateDashboardTimeContext(timeProps) {
if (this.displayConfig?.widgetInstanceGlobalTimeContext) {
this.widgetTimeContextDateRangeService.emitPropertyUpdate(timeProps);
}
this.timeControlsFormGroup.patchValue(timeProps);
this.timeProps = { ...timeProps, realtime: false };
}
updateTimeRangeOnRealtime(timeRange) {
this.timeControlsFormGroup.patchValue(timeRange, { emitEvent: false });
}
toggleChart(datapoint) {
if (this.displayConfig?.datapoints?.filter(dp => dp.__active).length === 1 &&
datapoint.__active) {
// at least 1 datapoint should be active
this.hasAtLeastOneDatapointActive = false;
return;
}
datapoint.__active = !datapoint.__active;
this.hasAtLeastOneDatapointActive = true;
this.displayConfig = { ...this.displayConfig };
}
handleDatapointOutOfSync(dpOutOfSync) {
const key = (dp) => dp.__target?.id + dp.fragment + dp.series;
const dpMatch = this.displayConfig?.datapoints?.find(dp => key(dp) === key(dpOutOfSync));
if (!dpMatch) {
return;
}
this.datapointsOutOfSync.set(dpMatch, true);
}
toggleMarkedArea(alarm) {
this.enabledMarkedAreaAlarmType = alarm.filters.type;
const params = {
data: {
itemType: alarm.filters.type
}
};
this.chartComponent.onChartClick(params);
}
toggleAlarmEventType(alarmOrEvent) {
if (alarmOrEvent.timelineType === 'ALARM') {
this.alarms = this.alarms.map(alarm => {
if (alarm.filters.type === alarmOrEvent.filters.type) {
alarm.__hidden = !alarm.__hidden;
}
return alarm;
});
}
else {
this.events = this.events.map(event => {
if (event.filters.type === alarmOrEvent.filters.type) {
event.__hidden = !event.__hidden;
}
return event;
});
}
this.displayConfig = { ...this.displayConfig };
}
updateAlarmsAndEvents(alarmsEventsConfigs) {
this.alarms = alarmsEventsConfigs.filter(alarm => alarm.timelineType === 'ALARM');
this.events = alarmsEventsConfigs.filter(event => event.timelineType === 'EVENT');
if (this.alarms.length === 0 || !this.alarms.find(alarm => alarm.__active)) {
this.hasAtLeastOneAlarmActive = false;
}
}
filterSeverity(eventTarget) {
this.alarms = this.alarms.map(alarm => {
if (!alarm.__severity) {
alarm.__severity = [];
}
alarm.__severity = Object.keys(eventTarget.severityOptions).filter((severity) => eventTarget.severityOptions[severity]);
if (!alarm.__status) {
alarm.__status = [];
}
const statuses = Object.keys(ALARM_STATUS_LABELS);
const filteredStatuses = eventTarget.showCleared
? statuses
: statuses.filter(status => status !== 'CLEARED');
alarm.__status = filteredStatuses;
return alarm;
});
this.displayConfig = { ...this.displayConfig };
}
assignContextFromContextDashboard(datapoint) {
if (!this.dashboardContextComponent?.isDeviceTypeDashboard) {
return;
}
const context = this.dashboardContextComponent?.context;
if (context?.id) {
const { name, id } = context;
datapoint.__target = { name, id };
}
}
initForm() {
const form = this.formBuilder.group({
dateFrom: [undefined, [Validators.required]],
dateTo: [undefined, [Validators.required]],
interval: [
this.displayConfig?.interval || 'hours',
[Validators.required]
],
aggregation: [null, []],
realtime: [false, [Validators.required]],
widgetInstanceGlobalTimeContext: [false, []]
});
form.patchValue(this.displayConfig || {});
return form;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DatapointsGraphWidgetViewComponent, deps: [{ token: i4.FormBuilder }, { token: i2$1.TranslateService }, { token: i2.WidgetTimeContextDateRangeService }, { token: i1.ContextDashboardComponent, optional: true }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: DatapointsGraphWidgetViewComponent, isStandalone: true, selector: "c8y-datapoints-graph-widget-view", inputs: { config: "config" }, providers: [ChartEventsService, ChartAlarmsService], viewQueries: [{ propertyName: "chartComponent", first: true, predicate: ChartsComponent, descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"p-l-16 p-r-16\">\n <div class=\"d-flex gap-16 a-i-start\">\n <div\n class=\"btn-group btn-group-sm flex-no-shrink\"\n *ngIf=\"!displayConfig?.widgetInstanceGlobalTimeContext\"\n >\n <button\n class=\"btn btn-default\"\n [attr.aria-label]=\"'Aggregation' | translate\"\n tooltip=\"{{\n (displayConfig?.aggregation\n ? AGGREGATION_TEXTS[displayConfig.aggregation]\n : AGGREGATION_TEXTS.undefined\n ) | translate\n }}\"\n placement=\"top\"\n container=\"body\"\n type=\"button\"\n [adaptivePosition]=\"false\"\n [delay]=\"500\"\n >\n <i\n class=\"icon-14\"\n [c8yIcon]=\"\n displayConfig?.aggregation\n ? AGGREGATION_ICONS[displayConfig.aggregation]\n : AGGREGATION_ICONS.undefined\n \"\n ></i>\n </button>\n\n <c8y-time-context\n class=\"d-contents\"\n (contextChange)=\"timePropsChanged($event)\"\n [changedDateContext]=\"timeProps\"\n [controlsAvailable]=\"{\n realtime: true,\n timeRange: displayConfig?.displayDateSelection,\n interval: displayConfig?.displayDateSelection,\n aggregation: displayConfig?.displayAggregationSelection\n }\"\n ></c8y-time-context>\n </div>\n <c8y-alarms-filter\n class=\"d-contents form-group-sm\"\n *ngIf=\"hasAtLeastOneAlarmActive\"\n (filterApplied)=\"filterSeverity($event)\"\n ></c8y-alarms-filter>\n\n <div class=\"m-l-auto btn-group btn-group-sm flex-no-shrink\">\n <button\n class=\"btn btn-default\"\n [attr.aria-label]=\"'Save as image' | translate\"\n tooltip=\"{{ 'Save as image' | translate }}\"\n container=\"body\"\n type=\"button\"\n (click)=\"chart.saveAsImage()\"\n [adaptivePosition]=\"false\"\n >\n <i\n class=\"icon-14\"\n c8yIcon=\"image-file-checked\"\n ></i>\n </button>\n </div>\n </div>\n <div\n class=\"d-flex\"\n style=\"align-items: center\"\n >\n <button\n class=\"btn-help btn-help--sm m-r-8\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"legendHelpTemplate\"\n placement=\"bottom\"\n triggers=\"focus\"\n container=\"body\"\n [adaptivePosition]=\"false\"\n ></button>\n <div class=\"inner-scroll\">\n <div class=\"flex-grow p-t-8 d-flex a-i-start gap-8 p-b-4\">\n <div\n class=\"c8y-datapoint-pill flex-no-shrink\"\n title=\"{{ datapoint.label }} - {{ datapoint.__target.name }}\"\n *ngFor=\"let datapoint of displayConfig.datapoints\"\n [ngClass]=\"{ active: datapoint.__active }\"\n >\n <i\n class=\"text-warning m-l-4\"\n c8yIcon=\"exclamation-triangle\"\n [tooltip]=\"'At least 1 active data points must be active.' | translate\"\n container=\"body\"\n *ngIf=\"!hasAtLeastOneDatapointActive && datapoint.__active\"\n [adaptivePosition]=\"false\"\n ></i>\n <button\n class=\"c8y-datapoint-pill__btn\"\n title=\"{{\n (datapoint.__active ? hideDatapointLabel : showDatapointLabel) | translate\n }} \"\n type=\"button\"\n (click)=\"toggleChart(datapoint)\"\n >\n <i\n class=\"icon-14\"\n [c8yIcon]=\"datapoint.__active ? 'eye text-primary' : 'eye-slash text-muted'\"\n ></i>\n </button>\n <div class=\"c8y-datapoint-pill__label c8y-datapoint-pill__btn\">\n <i\n class=\"m-r-4 icon-14\"\n c8yIcon=\"circle\"\n