UNPKG

@c8y/ngx-components

Version:

Angular modules for Cumulocity IoT applications

261 lines 78.7 kB
import { Component, ViewChild } from '@angular/core'; import { NgForm } from '@angular/forms'; import { DynamicComponentComponent } from '@c8y/ngx-components'; import { clone, cloneDeep, escapeRegExp, get, omit } from 'lodash-es'; import { BsModalRef } from 'ngx-bootstrap/modal'; import { iif, Subject, timer } from 'rxjs'; import { switchMap } from 'rxjs/operators'; import { WIDGET_CONTENT_CLASSES, WIDGET_HEADER_CLASSES } from './context-dashboard.model'; import { WidgetService } from './widget.service'; import { ContextDashboardService } from './context-dashboard.service'; import { InventoryService } from '@c8y/client'; import * as i0 from "@angular/core"; import * as i1 from "./widget.service"; import * as i2 from "ngx-bootstrap/modal"; import * as i3 from "@c8y/client"; import * as i4 from "./context-dashboard.service"; import * as i5 from "@c8y/ngx-components"; import * as i6 from "@angular/common"; import * as i7 from "@angular/forms"; import * as i8 from "@c8y/ngx-components/assets-navigator"; import * as i9 from "./appearance-settings.component"; import * as i10 from "./widget-preview.component"; export class WidgetConfigComponent { get isEdit() { return !!this.current; } get isDeviceTypeDashboard() { return !!this.mo.c8y_Dashboard?.deviceType && !!this.mo.c8y_Dashboard?.deviceTypeValue; } constructor(widgetService, modal, inventory, contextDashboardService) { this.widgetService = widgetService; this.modal = modal; this.inventory = inventory; this.contextDashboardService = contextDashboardService; this.mode = 'select'; this.searchChange$ = new Subject(); this.searchTerm = ''; this.styling = { headerClass: 'panel-title-regular', contentClass: 'panel-content-light' }; this.defaultStyling = { headerClass: 'panel-title-regular', contentClass: 'panel-content-light' }; this.possibleStyling = { WIDGET_HEADER_CLASSES, WIDGET_CONTENT_CLASSES }; this.isUpgrade = false; this.result = new Promise((resolve, reject) => { this._save = resolve; this._cancel = reject; }); } async ngAfterContentInit() { this.components = this.widgetService.getWidgetDefinitions(); if (this.selected) { this.current = clone(this.selected); this.select(this.selected, this.isEdit ? 'config' : 'select'); } this.searchSub = this.searchChange$ .pipe(switchMap((event) => iif(() => event.which !== 13, timer(200), timer(10)))) .subscribe(() => { this.search(); }); this.setStylings(); if (this.widgetConfig?.device) { const { data } = await this.inventory.detail(this.widgetConfig.device.id); this.selectedDevice = data; } } isSaveDisabled() { return this.configForm?.invalid || this.checkIfDeviceRequired(); } checkIfDeviceRequired() { return (!this.widgetConfig || (!this.widgetConfig.settings.deviceTargetNotRequired && !this.widgetConfig.device && !this.widgetConfig.settings.noDeviceTarget)); } selectionChanged(selection) { this.widgetConfig.device = selection?.change?.item; this.dynamicComponent.emitConfigChange({ device: this.widgetConfig.device }); } async save() { const hookSuccess = await this.dynamicComponent?.callLifeCycleHooks().toPromise(); if (!hookSuccess) { return; } const { _x, _y, _width, _height } = this.selected.data; if (this.widgetConfig && this.widgetConfig.device && // serializing will be handled by resolver !this.selected?.resolve?.device) { const { id, name } = this.widgetConfig.device; this.widgetConfig.device = { id, name }; } const widget = { _x, _y, _width, _height, config: omit(this.widgetConfig, ['settings', 'displaySettings']), title: this.selected.data.title, componentId: this.selected.id, id: this.isEdit ? this.current.data.id : String(Math.random()).substr(2), classes: this.getStyle(), ...(!this.isEdit ? this.widgetConfig.settings.widgetDefaults : {}) }; this._save(widget); } select(cmp, mode = 'config') { if (mode === 'config' && this.configForm) { const formGroup = this.configForm.control; for (const control of Object.keys(formGroup.controls)) { formGroup.removeControl(control); } } cmp.data = cmp.data || {}; this.selected = cmp; this.isUpgrade = !!get(cmp, 'data.settings.upgrade'); this.contextDashboardService.formDisabled = this.isUpgrade; if (this.isEdit) { const { _x, _y, _width, _height, classes, title } = this.current.data; this.selected.data = { ...this.selected.data, _x, _y, _width, _height, classes, title }; } /* this.selected.component cannot be checked since it is faulty for widgets like Help and Service, but exists for Applications widget and this would cause chaotic behaviour. */ this.widgetHasConfigComponent = !!this.selected.loadConfigComponent || mode === 'select' || mode === 'config'; this.widgetConfig = cloneDeep(this.composeWidgetConfig(this.selected, this.context)); this.selected.data.title = this.selected.data.title || cmp.label; this.componentLabel = cmp.label; this.mode = mode; } search() { if (this.searchTerm.length > 0) { this.searchResult = this.components.filter(cmp => new RegExp(escapeRegExp(this.searchTerm.trim()), 'i').test(cmp.label)); } else { this.resetSearch(); } } resetSearch() { this.searchTerm = ''; this.searchResult = undefined; } changeMode(mode) { this.mode = mode; } close() { this._cancel(); this.modal.hide(); } getStyle(isPreview = false) { const cssClasses = {}; if (isPreview || !this.isDashboardDefaultStyle(this.styling.headerClass)) { cssClasses[this.styling.headerClass] = true; } if (isPreview || !this.isDashboardDefaultStyle(this.styling.contentClass)) { cssClasses[this.styling.contentClass] = true; } if (isPreview) { cssClasses[`dashboard-theme-${this.defaultStyling.contentClass.split('-').pop()}`] = true; } return cssClasses; } ngOnDestroy() { this.contextDashboardService.formDisabled = true; if (this.searchSub) { this.searchSub.unsubscribe(); } } hasConfig() { if (this.widgetConfig.settings?.upgrade) { return (this.widgetConfig.settings?.configComponent || this.widgetConfig.settings?.configTemplateUrl); } return !this.dynamicComponent?.error; } isAnyOfAlarmsWidget() { return (this.widgetConfig?.settings?.alarmListWidget || this.widgetConfig?.settings?.criticalAlarmsWidget || this.widgetConfig?.settings?.recentAlarmsWidget); } setStylings() { const dashboardClasses = (this.mo.c8y_Dashboard?.classes && Object.keys(this.mo.c8y_Dashboard.classes)) || []; const dashboardWidgetClasses = (this.mo.c8y_Dashboard?.widgetClasses && Object.keys(this.mo.c8y_Dashboard.widgetClasses)) || []; const widgetClasses = this.isEdit ? Object.keys(this.current.data.classes) : []; this.styling = this.setDefaultStyle([ ...dashboardClasses, ...dashboardWidgetClasses, ...widgetClasses ]); this.defaultStyling = this.setDefaultStyle([...dashboardClasses, ...dashboardWidgetClasses]); } isDashboardDefaultStyle(className) { const allClasses = { ...this.mo.c8y_Dashboard.classes, ...this.mo.c8y_Dashboard.widgetClasses }; const styles = Object.keys(allClasses).map(cssClass => ({ class: cssClass })); const style = this.contextDashboardService.getStyling(styles, className.split('-').pop(), undefined); return !!style; } setDefaultStyle(setClasses) { let contentClass = this.styling.contentClass; let headerClass = this.styling.headerClass; const styles = this.contextDashboardService .getFilteredDashboardStyles(setClasses) .map(c => c.split('-').pop()); styles.forEach(styleName => { contentClass = this.contextDashboardService.getStyling(WIDGET_CONTENT_CLASSES, styleName, contentClass); headerClass = this.contextDashboardService.getStyling(WIDGET_HEADER_CLASSES, styleName, headerClass); }); return { headerClass, contentClass }; } composeWidgetConfig(selectedComponent, context = {}) { const setting = { settings: { ...selectedComponent.data.settings, ...get(selectedComponent.data.settings, 'ng1.options'), ...get(selectedComponent.data, 'ng1.options'), context, dashboardMo: this.mo.c8y_Dashboard }, ...selectedComponent.data.config }; return this.applyTargetIfDeviceDashboard(setting); } applyTargetIfDeviceDashboard(widgetConfig) { const isDeviceType = this.contextDashboardService.isDeviceType(this.mo); if (isDeviceType) { widgetConfig.settings.hideTarget = isDeviceType; const noDeviceTarget = widgetConfig.settings.ng1 ? widgetConfig.settings.ng1.options.noDeviceTarget : widgetConfig.settings.noDeviceTarget; if (!noDeviceTarget) { widgetConfig.device = { id: this.context.id, name: this.context.name }; } } return widgetConfig; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: WidgetConfigComponent, deps: [{ token: i1.WidgetService }, { token: i2.BsModalRef }, { token: i3.InventoryService }, { token: i4.ContextDashboardService }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: WidgetConfigComponent, selector: "c8y-widget-config", viewQueries: [{ propertyName: "dynamicComponent", first: true, predicate: ["config"], descendants: true }, { propertyName: "configForm", first: true, predicate: ["configForm"], descendants: true }], ngImport: i0, template: "<div class=\"modal-header\">\n <div\n class=\"h3\"\n title=\"{{ 'Add widget' | translate }}\"\n id=\"modal-title\"\n *ngIf=\"!current\"\n translate\n >\n Add widget\n </div>\n <div\n class=\"h3\"\n title=\"{{ 'Edit widget' | translate }}\"\n id=\"modal-title\"\n *ngIf=\"current\"\n translate\n >\n Edit widget\n </div>\n</div>\n<form\n name=\"form\"\n #configForm=\"ngForm\"\n>\n <div\n class=\"c8y-modal-tabs\"\n id=\"modal-body\"\n >\n <div class=\"tabContainer\">\n <ul class=\"nav nav-tabs nav-tabsc8y p-l-24\">\n <li [class.active]=\"mode === 'select'\">\n <button\n class=\"btn\"\n title=\"{{ 'Select widget' | translate }}\"\n type=\"button\"\n (click)=\"changeMode('select'); (false)\"\n >\n <i c8yIcon=\"th-large\"></i>\n <span translate>Select widget</span>\n </button>\n </li>\n <li [class.active]=\"mode === 'config'\">\n <button\n class=\"btn\"\n title=\"{{ 'Configuration' | translate }}\"\n type=\"button\"\n [disabled]=\"!selected\"\n (click)=\"changeMode('config'); (false)\"\n >\n <i c8yIcon=\"cog\"></i>\n <span translate>Configuration</span>\n </button>\n </li>\n <li [class.active]=\"mode === 'style'\">\n <button\n class=\"btn\"\n title=\"{{ 'Appearance' | translate }}\"\n type=\"button\"\n [disabled]=\"!selected\"\n (click)=\"changeMode('style'); (false)\"\n >\n <i c8yIcon=\"paint-brush\"></i>\n <span translate>Appearance</span>\n </button>\n </li>\n </ul>\n </div>\n </div>\n\n <div class=\"modal-inner-scroll modal-inner-scroll--fixed\">\n <div\n class=\"bg-level-0 p-l-24 p-r-24 p-t-8 p-b-8 sticky-header-top-0 elevation-md\"\n style=\"z-index: 2\"\n *ngIf=\"mode === 'select'\"\n >\n <div class=\"row\">\n <div class=\"col-sm-6\">\n <div class=\"input-group input-group-search\">\n <input\n class=\"form-control\"\n [attr.aria-label]=\"'Search' | translate\"\n placeholder=\"{{ 'Search\u2026' | translate }}\"\n type=\"text\"\n data-cy=\"widget-config--Search\"\n [(ngModel)]=\"searchTerm\"\n [ngModelOptions]=\"{ standalone: true }\"\n (keydown)=\"searchChange$.next($event)\"\n />\n <span class=\"input-group-btn\">\n <button\n class=\"btn btn-dot\"\n title=\"{{ 'Search' | translate }}\"\n type=\"button\"\n (click)=\"resetSearch()\"\n >\n <i [c8yIcon]=\"searchTerm.length === 0 ? 'search' : 'close'\"></i>\n </button>\n </span>\n </div>\n </div>\n </div>\n </div>\n <div\n class=\"modal-body bg-level-2\"\n *ngIf=\"mode === 'select'\"\n >\n <div class=\"card-group card-select m-b-0\">\n <div\n class=\"col-md-3 col-sm-4 col-xs-6\"\n title=\"{{ cmp.description | translate }}\"\n data-cy=\"widget-config--widget-list\"\n *ngFor=\"let cmp of searchResult || components\"\n >\n <button\n class=\"btn-clean d-col card p-8\"\n [class.active]=\"selected === cmp\"\n type=\"button\"\n (click)=\"select(cmp)\"\n >\n <div\n class=\"text-center p-8 m-b-8 d-col flex-center flex-grow bg-level-2\"\n role=\"presentation\"\n >\n <ng-container *ngIf=\"!cmp.previewImage; else previewImage\">\n <div class=\"h1\"><i c8yIcon=\"file-image-o\"></i></div>\n <small translate>Preview not available</small>\n </ng-container>\n <ng-template #previewImage>\n <img\n class=\"widget-thumbnail\"\n alt\n [src]=\"cmp.previewImage\"\n />\n </ng-template>\n </div>\n <p class=\"card-title text-truncate\">\n <c8y-highlight\n text=\"{{ cmp.label | translate }}\"\n [pattern]=\"searchTerm\"\n ></c8y-highlight>\n </p>\n </button>\n </div>\n\n <div\n class=\"c8y-empty-state text-center\"\n *ngIf=\"searchResult && searchResult.length === 0\"\n >\n <div\n class=\"h1\"\n c8yIcon=\"search\"\n ></div>\n <h3 translate>No widgets found.</h3>\n <div class=\"d-flex\">\n <p\n class=\"m-r-8\"\n translate\n >\n Rephrase your search term.\n </p>\n <button\n class=\"btn btn-primary\"\n title=\"{{ 'Reset search' | translate }}\"\n type=\"button\"\n (click)=\"resetSearch()\"\n >\n {{ 'Reset search' | translate }}\n </button>\n </div>\n </div>\n </div>\n </div>\n\n <!-- The following is intentional set to hidden to allow the ViewChild ref in the controller -->\n <div\n class=\"d-flex d-col fit-h\"\n *ngIf=\"selected\"\n [ngClass]=\"{ hidden: mode !== 'config' }\"\n >\n <div class=\"p-t-16 flex-no-shrink separator-bottom p-b-16\">\n <div class=\"row\">\n <div class=\"col-sm-4\">\n <div class=\"p-l-24\">\n <div class=\"text-left text-medium h4\">\n {{ selected.label | translate }}\n </div>\n <p>\n {{ selected.description | translate }}\n </p>\n </div>\n </div>\n <div class=\"col-sm-8\">\n <div class=\"p-r-24\">\n <c8y-form-group>\n <label\n for=\"widgetTitle\"\n translate\n >\n Title\n </label>\n <input\n class=\"form-control\"\n id=\"widgetTitle\"\n placeholder=\"{{ 'e.g.' | translate }} {{ componentLabel | translate }}\"\n name=\"title\"\n type=\"text\"\n required\n [(ngModel)]=\"selected.data.title\"\n />\n </c8y-form-group>\n </div>\n </div>\n </div>\n </div>\n <div\n class=\"row flex-grow\"\n [ngClass]=\"{ 'd-flex': widgetConfig.options || hasConfig() }\"\n >\n <div\n class=\"a-s-stretch\"\n [ngStyle]=\"isLegacyAlarmWidget ? { 'pointer-events': 'none', opacity: '0.5' } : {}\"\n *ngIf=\"!widgetConfig.settings?.noDeviceTarget && mode === 'config'\"\n [ngClass]=\"{\n 'bg-level-1 col-sm-4 p-r-0': hasConfig(),\n 'bg-level-0 col-sm-12': !hasConfig()\n }\"\n >\n <div\n class=\"fit-h bg-inherit p-l-16\"\n [ngClass]=\"{ 'p-r-24': !hasConfig() }\"\n >\n <div class=\"p-relative bg-inherit\">\n <c8y-asset-selector-miller\n class=\"d-block bg-inherit p-relative\"\n style=\"height: calc(100vh - 422px)\"\n (onSelected)=\"selectionChanged($event)\"\n [config]=\"{\n view: 'miller',\n groupsSelectable: this.widgetConfig.settings?.groupsSelectable,\n showChildDevices: true,\n columnHeaders: true,\n showUnassignedDevices: true,\n search: !this.widgetConfig.settings.context?.additionParents,\n showFilter: true,\n singleColumn: !!this.hasConfig(),\n showSelected: true\n }\"\n [asset]=\"widgetConfig.settings?.context\"\n [selectedDevice]=\"selectedDevice\"\n ></c8y-asset-selector-miller>\n </div>\n </div>\n </div>\n <div\n [ngClass]=\"{\n 'col-sm-8': !widgetConfig.settings?.noDeviceTarget,\n 'col-sm-12': widgetConfig.settings?.noDeviceTarget,\n 'sr-only': !hasConfig()\n }\"\n >\n <c8y-dynamic-component\n class=\"d-block\"\n style=\"height: {{ hasConfig() ? 'calc(100vh - 422px)' : '0' }}\"\n [ngClass]=\"{ 'inner-scroll p-r-24 p-t-16': !widgetConfig.settings?.noDeviceTarget }\"\n [componentId]=\"selected.id\"\n mode=\"config\"\n [config]=\"widgetConfig\"\n [notFoundError]=\"false\"\n #config\n ></c8y-dynamic-component>\n </div>\n </div>\n </div>\n\n <div\n class=\"modal-body p-t-0\"\n style=\"height: calc(100vh - 310px)\"\n *ngIf=\"mode === 'style'\"\n >\n <div class=\"row\">\n <div class=\"col-xs-6\">\n <c8y-appearance-settings\n [(themeClass)]=\"styling.contentClass\"\n [(headerClass)]=\"styling.headerClass\"\n [possibleStylingTheme]=\"possibleStyling.WIDGET_CONTENT_CLASSES\"\n [possibleStylingHeader]=\"possibleStyling.WIDGET_HEADER_CLASSES\"\n [defaultThemeClass]=\"defaultStyling.contentClass\"\n [defaultHeaderClass]=\"defaultStyling.headerClass\"\n ></c8y-appearance-settings>\n </div>\n <div class=\"col-xs-6 sticky-header-top-0\">\n <c8y-widget-preview\n style=\"height: calc(100vh - 382px)\"\n [previewClasses]=\"getStyle(true)\"\n ></c8y-widget-preview>\n </div>\n </div>\n </div>\n </div>\n\n <div class=\"modal-footer\">\n <button\n class=\"btn btn-default\"\n title=\"{{ 'Cancel' | translate }}\"\n type=\"button\"\n data-cy=\"widget-config--cancel-widget\"\n (click)=\"close()\"\n >\n {{ 'Cancel' | translate }}\n </button>\n <button\n class=\"btn btn-primary\"\n title=\"{{ 'Save' | translate }}\"\n type=\"submit\"\n data-cy=\"widget-config--save-widget\"\n (click)=\"save()\"\n [disabled]=\"\n !dynamicComponent ||\n (((this.contextDashboardService.formDisabled$ | async) || isSaveDisabled()) &&\n !!widgetHasConfigComponent)\n \"\n c8yProductExperience\n [actionName]=\"current ? 'editWidget' : 'createWidget'\"\n [actionData]=\"{ widgetName: selected && selected.id }\"\n >\n {{ 'Save' | translate }}\n </button>\n </div>\n</form>\n", dependencies: [{ kind: "directive", type: i5.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: i5.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: i6.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i6.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i6.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i6.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: i5.HighlightComponent, selector: "c8y-highlight", inputs: ["pattern", "text", "elementClass", "shouldTrimPattern"] }, { kind: "directive", type: i7.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i7.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: i7.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i7.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i7.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i7.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i7.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: i5.FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "directive", type: i5.RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "component", type: i5.DynamicComponentComponent, selector: "c8y-dynamic-component", inputs: ["componentId", "config", "mode", "notFoundError", "executeResolvers"], outputs: ["updateWidgetClasses"] }, { kind: "directive", type: i5.ProductExperienceDirective, selector: "[c8yProductExperience]", inputs: ["actionName", "actionData", "inherit", "suppressDataOverriding"] }, { kind: "component", type: i8.MillerViewComponent, selector: "c8y-asset-selector-miller", inputs: ["config", "asset", "selectedDevice", "rootNode", "container"], outputs: ["onSelected", "onClearSelected"] }, { kind: "component", type: i9.AppearanceSettingsComponent, selector: "c8y-appearance-settings", inputs: ["themeClass", "headerClass", "defaultThemeClass", "defaultHeaderClass", "dashboardSettings", "possibleStylingTheme", "possibleStylingHeader", "columns"], outputs: ["themeClassChange", "headerClassChange"] }, { kind: "component", type: i10.WidgetPreviewComponent, selector: "c8y-widget-preview", inputs: ["previewClasses", "tab"] }, { kind: "pipe", type: i5.C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: i6.AsyncPipe, name: "async" }] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: WidgetConfigComponent, decorators: [{ type: Component, args: [{ selector: 'c8y-widget-config', template: "<div class=\"modal-header\">\n <div\n class=\"h3\"\n title=\"{{ 'Add widget' | translate }}\"\n id=\"modal-title\"\n *ngIf=\"!current\"\n translate\n >\n Add widget\n </div>\n <div\n class=\"h3\"\n title=\"{{ 'Edit widget' | translate }}\"\n id=\"modal-title\"\n *ngIf=\"current\"\n translate\n >\n Edit widget\n </div>\n</div>\n<form\n name=\"form\"\n #configForm=\"ngForm\"\n>\n <div\n class=\"c8y-modal-tabs\"\n id=\"modal-body\"\n >\n <div class=\"tabContainer\">\n <ul class=\"nav nav-tabs nav-tabsc8y p-l-24\">\n <li [class.active]=\"mode === 'select'\">\n <button\n class=\"btn\"\n title=\"{{ 'Select widget' | translate }}\"\n type=\"button\"\n (click)=\"changeMode('select'); (false)\"\n >\n <i c8yIcon=\"th-large\"></i>\n <span translate>Select widget</span>\n </button>\n </li>\n <li [class.active]=\"mode === 'config'\">\n <button\n class=\"btn\"\n title=\"{{ 'Configuration' | translate }}\"\n type=\"button\"\n [disabled]=\"!selected\"\n (click)=\"changeMode('config'); (false)\"\n >\n <i c8yIcon=\"cog\"></i>\n <span translate>Configuration</span>\n </button>\n </li>\n <li [class.active]=\"mode === 'style'\">\n <button\n class=\"btn\"\n title=\"{{ 'Appearance' | translate }}\"\n type=\"button\"\n [disabled]=\"!selected\"\n (click)=\"changeMode('style'); (false)\"\n >\n <i c8yIcon=\"paint-brush\"></i>\n <span translate>Appearance</span>\n </button>\n </li>\n </ul>\n </div>\n </div>\n\n <div class=\"modal-inner-scroll modal-inner-scroll--fixed\">\n <div\n class=\"bg-level-0 p-l-24 p-r-24 p-t-8 p-b-8 sticky-header-top-0 elevation-md\"\n style=\"z-index: 2\"\n *ngIf=\"mode === 'select'\"\n >\n <div class=\"row\">\n <div class=\"col-sm-6\">\n <div class=\"input-group input-group-search\">\n <input\n class=\"form-control\"\n [attr.aria-label]=\"'Search' | translate\"\n placeholder=\"{{ 'Search\u2026' | translate }}\"\n type=\"text\"\n data-cy=\"widget-config--Search\"\n [(ngModel)]=\"searchTerm\"\n [ngModelOptions]=\"{ standalone: true }\"\n (keydown)=\"searchChange$.next($event)\"\n />\n <span class=\"input-group-btn\">\n <button\n class=\"btn btn-dot\"\n title=\"{{ 'Search' | translate }}\"\n type=\"button\"\n (click)=\"resetSearch()\"\n >\n <i [c8yIcon]=\"searchTerm.length === 0 ? 'search' : 'close'\"></i>\n </button>\n </span>\n </div>\n </div>\n </div>\n </div>\n <div\n class=\"modal-body bg-level-2\"\n *ngIf=\"mode === 'select'\"\n >\n <div class=\"card-group card-select m-b-0\">\n <div\n class=\"col-md-3 col-sm-4 col-xs-6\"\n title=\"{{ cmp.description | translate }}\"\n data-cy=\"widget-config--widget-list\"\n *ngFor=\"let cmp of searchResult || components\"\n >\n <button\n class=\"btn-clean d-col card p-8\"\n [class.active]=\"selected === cmp\"\n type=\"button\"\n (click)=\"select(cmp)\"\n >\n <div\n class=\"text-center p-8 m-b-8 d-col flex-center flex-grow bg-level-2\"\n role=\"presentation\"\n >\n <ng-container *ngIf=\"!cmp.previewImage; else previewImage\">\n <div class=\"h1\"><i c8yIcon=\"file-image-o\"></i></div>\n <small translate>Preview not available</small>\n </ng-container>\n <ng-template #previewImage>\n <img\n class=\"widget-thumbnail\"\n alt\n [src]=\"cmp.previewImage\"\n />\n </ng-template>\n </div>\n <p class=\"card-title text-truncate\">\n <c8y-highlight\n text=\"{{ cmp.label | translate }}\"\n [pattern]=\"searchTerm\"\n ></c8y-highlight>\n </p>\n </button>\n </div>\n\n <div\n class=\"c8y-empty-state text-center\"\n *ngIf=\"searchResult && searchResult.length === 0\"\n >\n <div\n class=\"h1\"\n c8yIcon=\"search\"\n ></div>\n <h3 translate>No widgets found.</h3>\n <div class=\"d-flex\">\n <p\n class=\"m-r-8\"\n translate\n >\n Rephrase your search term.\n </p>\n <button\n class=\"btn btn-primary\"\n title=\"{{ 'Reset search' | translate }}\"\n type=\"button\"\n (click)=\"resetSearch()\"\n >\n {{ 'Reset search' | translate }}\n </button>\n </div>\n </div>\n </div>\n </div>\n\n <!-- The following is intentional set to hidden to allow the ViewChild ref in the controller -->\n <div\n class=\"d-flex d-col fit-h\"\n *ngIf=\"selected\"\n [ngClass]=\"{ hidden: mode !== 'config' }\"\n >\n <div class=\"p-t-16 flex-no-shrink separator-bottom p-b-16\">\n <div class=\"row\">\n <div class=\"col-sm-4\">\n <div class=\"p-l-24\">\n <div class=\"text-left text-medium h4\">\n {{ selected.label | translate }}\n </div>\n <p>\n {{ selected.description | translate }}\n </p>\n </div>\n </div>\n <div class=\"col-sm-8\">\n <div class=\"p-r-24\">\n <c8y-form-group>\n <label\n for=\"widgetTitle\"\n translate\n >\n Title\n </label>\n <input\n class=\"form-control\"\n id=\"widgetTitle\"\n placeholder=\"{{ 'e.g.' | translate }} {{ componentLabel | translate }}\"\n name=\"title\"\n type=\"text\"\n required\n [(ngModel)]=\"selected.data.title\"\n />\n </c8y-form-group>\n </div>\n </div>\n </div>\n </div>\n <div\n class=\"row flex-grow\"\n [ngClass]=\"{ 'd-flex': widgetConfig.options || hasConfig() }\"\n >\n <div\n class=\"a-s-stretch\"\n [ngStyle]=\"isLegacyAlarmWidget ? { 'pointer-events': 'none', opacity: '0.5' } : {}\"\n *ngIf=\"!widgetConfig.settings?.noDeviceTarget && mode === 'config'\"\n [ngClass]=\"{\n 'bg-level-1 col-sm-4 p-r-0': hasConfig(),\n 'bg-level-0 col-sm-12': !hasConfig()\n }\"\n >\n <div\n class=\"fit-h bg-inherit p-l-16\"\n [ngClass]=\"{ 'p-r-24': !hasConfig() }\"\n >\n <div class=\"p-relative bg-inherit\">\n <c8y-asset-selector-miller\n class=\"d-block bg-inherit p-relative\"\n style=\"height: calc(100vh - 422px)\"\n (onSelected)=\"selectionChanged($event)\"\n [config]=\"{\n view: 'miller',\n groupsSelectable: this.widgetConfig.settings?.groupsSelectable,\n showChildDevices: true,\n columnHeaders: true,\n showUnassignedDevices: true,\n search: !this.widgetConfig.settings.context?.additionParents,\n showFilter: true,\n singleColumn: !!this.hasConfig(),\n showSelected: true\n }\"\n [asset]=\"widgetConfig.settings?.context\"\n [selectedDevice]=\"selectedDevice\"\n ></c8y-asset-selector-miller>\n </div>\n </div>\n </div>\n <div\n [ngClass]=\"{\n 'col-sm-8': !widgetConfig.settings?.noDeviceTarget,\n 'col-sm-12': widgetConfig.settings?.noDeviceTarget,\n 'sr-only': !hasConfig()\n }\"\n >\n <c8y-dynamic-component\n class=\"d-block\"\n style=\"height: {{ hasConfig() ? 'calc(100vh - 422px)' : '0' }}\"\n [ngClass]=\"{ 'inner-scroll p-r-24 p-t-16': !widgetConfig.settings?.noDeviceTarget }\"\n [componentId]=\"selected.id\"\n mode=\"config\"\n [config]=\"widgetConfig\"\n [notFoundError]=\"false\"\n #config\n ></c8y-dynamic-component>\n </div>\n </div>\n </div>\n\n <div\n class=\"modal-body p-t-0\"\n style=\"height: calc(100vh - 310px)\"\n *ngIf=\"mode === 'style'\"\n >\n <div class=\"row\">\n <div class=\"col-xs-6\">\n <c8y-appearance-settings\n [(themeClass)]=\"styling.contentClass\"\n [(headerClass)]=\"styling.headerClass\"\n [possibleStylingTheme]=\"possibleStyling.WIDGET_CONTENT_CLASSES\"\n [possibleStylingHeader]=\"possibleStyling.WIDGET_HEADER_CLASSES\"\n [defaultThemeClass]=\"defaultStyling.contentClass\"\n [defaultHeaderClass]=\"defaultStyling.headerClass\"\n ></c8y-appearance-settings>\n </div>\n <div class=\"col-xs-6 sticky-header-top-0\">\n <c8y-widget-preview\n style=\"height: calc(100vh - 382px)\"\n [previewClasses]=\"getStyle(true)\"\n ></c8y-widget-preview>\n </div>\n </div>\n </div>\n </div>\n\n <div class=\"modal-footer\">\n <button\n class=\"btn btn-default\"\n title=\"{{ 'Cancel' | translate }}\"\n type=\"button\"\n data-cy=\"widget-config--cancel-widget\"\n (click)=\"close()\"\n >\n {{ 'Cancel' | translate }}\n </button>\n <button\n class=\"btn btn-primary\"\n title=\"{{ 'Save' | translate }}\"\n type=\"submit\"\n data-cy=\"widget-config--save-widget\"\n (click)=\"save()\"\n [disabled]=\"\n !dynamicComponent ||\n (((this.contextDashboardService.formDisabled$ | async) || isSaveDisabled()) &&\n !!widgetHasConfigComponent)\n \"\n c8yProductExperience\n [actionName]=\"current ? 'editWidget' : 'createWidget'\"\n [actionData]=\"{ widgetName: selected && selected.id }\"\n >\n {{ 'Save' | translate }}\n </button>\n </div>\n</form>\n" }] }], ctorParameters: () => [{ type: i1.WidgetService }, { type: i2.BsModalRef }, { type: i3.InventoryService }, { type: i4.ContextDashboardService }], propDecorators: { dynamicComponent: [{ type: ViewChild, args: ['config', { static: false }] }], configForm: [{ type: ViewChild, args: ['configForm', { static: false }] }] } }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid2lkZ2V0LWNvbmZpZy5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9jb250ZXh0LWRhc2hib2FyZC93aWRnZXQtY29uZmlnLmNvbXBvbmVudC50cyIsIi4uLy4uLy4uL2NvbnRleHQtZGFzaGJvYXJkL3dpZGdldC1jb25maWcuY29tcG9uZW50Lmh0bWwiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFNBQVMsRUFBYSxTQUFTLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDaEUsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQ3hDLE9BQU8sRUFBc0MseUJBQXlCLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUVwRyxPQUFPLEVBQUUsS0FBSyxFQUFFLFNBQVMsRUFBRSxZQUFZLEVBQUUsR0FBRyxFQUFFLElBQUksRUFBRSxNQUFNLFdBQVcsQ0FBQztBQUN0RSxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFDakQsT0FBTyxFQUFFLEdBQUcsRUFBRSxPQUFPLEVBQWdCLEtBQUssRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUN6RCxPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDM0MsT0FBTyxFQUVMLHNCQUFzQixFQUN0QixxQkFBcUIsRUFFdEIsTUFBTSwyQkFBMkIsQ0FBQztBQUNuQyxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFDakQsT0FBTyxFQUFFLHVCQUF1QixFQUFFLE1BQU0sNkJBQTZCLENBQUM7QUFDdEUsT0FBTyxFQUFrQixnQkFBZ0IsRUFBRSxNQUFNLGFBQWEsQ0FBQzs7Ozs7Ozs7Ozs7O0FBTS9ELE1BQU0sT0FBTyxxQkFBcUI7SUErQmhDLElBQUksTUFBTTtRQUNSLE9BQU8sQ0FBQyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUM7SUFDeEIsQ0FBQztJQUVELElBQUkscUJBQXFCO1FBQ3ZCLE9B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