UNPKG

@c8y/ngx-components

Version:

Angular modules for Cumulocity IoT applications

255 lines (248 loc) 28 kB
import { NgIf, NgStyle, NgClass, NgForOf, AsyncPipe } from '@angular/common'; import * as i0 from '@angular/core'; import { Injectable, ViewChildren, Input, Optional, Component } from '@angular/core'; import * as i1$1 from '@angular/forms'; import { Validators, NgForm, ControlContainer, ReactiveFormsModule } from '@angular/forms'; import { gettext } from '@c8y/ngx-components/gettext'; import * as i2 from '@c8y/ngx-components'; import { C8yValidators, FormGroupComponent, MessagesComponent, MessageDirective, DropAreaComponent, LoadingComponent, C8yTranslatePipe, EmptyStateComponent } from '@c8y/ngx-components'; import * as i4 from '@c8y/ngx-components/context-dashboard'; import { get } from 'lodash-es'; import { BehaviorSubject, Subject, merge } from 'rxjs'; import { filter, distinctUntilChanged, tap, switchMap, map, shareReplay, takeUntil } from 'rxjs/operators'; import * as i1 from '@c8y/client'; import * as i3 from '@ngx-translate/core'; const defaultObjectFitValue = 'contain'; const defaultObjectPositionValue = 'center'; class ImageWidgetService { constructor(inventory, fileService, alert, translate, binary) { this.inventory = inventory; this.fileService = fileService; this.alert = alert; this.translate = translate; this.binary = binary; } async getImageDetails(imageBinaryId) { if (!imageBinaryId) { return null; } try { const { data: imageBinaryMo } = await this.inventory.detail(imageBinaryId); const file = await this.fileService.getFile(imageBinaryMo); const base64 = await this.fileService.toBase64(file); return { file, base64: base64, c8y_SHA256: imageBinaryMo.c8y_SHA256 }; } catch (e) { const text = this.translate.instant(gettext('Unable to retrieve image with id: {{ imageBinaryId }}'), { imageBinaryId }); this.alert.danger(text, e?.data); } return null; } async uploadFile(file, options = {}) { const hash = await this.fileService.getHashSumOfFile(file); const fileDetails = { c8y_SHA256: hash }; const globalImage = !options?.dashboardMoId || options?.isDeviceTypeDashboard; const partialBinaryMo = !globalImage ? { ...fileDetails } : { ...fileDetails, c8y_Global: {} }; const { data: mo } = await this.binary.create(file, partialBinaryMo); if (options?.dashboardMoId) { await this.inventory.childAdditionsAdd(mo, options?.dashboardMoId); } return mo.id; } getStyling(config) { if (!config.styling?.objectFit || config.styling?.objectFit === 'full-width') { return null; } return { 'object-fit': config.styling.objectFit, 'object-position': `${config.styling?.objectPositionX || defaultObjectPositionValue} ${config.styling?.objectPositionY || defaultObjectPositionValue}` }; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: ImageWidgetService, deps: [{ token: i1.InventoryService }, { token: i2.FilesService }, { token: i2.AlertService }, { token: i3.TranslateService }, { token: i1.InventoryBinaryService }], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: ImageWidgetService, providedIn: 'root' }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: ImageWidgetService, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }], ctorParameters: () => [{ type: i1.InventoryService }, { type: i2.FilesService }, { type: i2.AlertService }, { type: i3.TranslateService }, { type: i1.InventoryBinaryService }] }); class ImageWidgetConfigComponent { constructor(formBuilder, form, fileService, alert, imageWidget, widgetConfig, widgetConfigService) { this.formBuilder = formBuilder; this.form = form; this.fileService = fileService; this.alert = alert; this.imageWidget = imageWidget; this.widgetConfig = widgetConfig; this.widgetConfigService = widgetConfigService; this.imageBinaryId$ = new BehaviorSubject(null); this.loading = false; this.destroyed$ = new Subject(); this.objectFitOptions = [ { label: gettext('Contain`verb, image fitting option`'), value: 'contain', description: gettext('The image is entirely displayed within the widget while preserving the aspect ratio.') }, { label: gettext('Cover`verb, image fitting option`'), value: 'cover', description: gettext('The image is resized to fill the widget while preserving the aspect ratio. Overflowing areas are clipped.') }, { label: gettext('Fill`verb, image fitting option`'), value: 'fill', description: gettext('The image is stretched to fill the widget, overriding the aspect-ratio.') }, { label: gettext('Full width`image fitting option`'), value: 'full-width', description: gettext(`The image is resized to fit the widget's width while preserving the aspect ratio. Overflowing area is scrollable.`) } ]; } async onBeforeSave(config) { if (this.formGroup.invalid) { return false; } const fileFromForm = this.getFileFromFormValue(this.formGroup.value); if (fileFromForm && fileFromForm !== this.fileFromConfig) { try { const imageBinaryId = await this.imageWidget.uploadFile(fileFromForm, { dashboardMoId: this.getDashboardMoId(), isDeviceTypeDashboard: this.widgetConfig?.isDeviceTypeDashboard }); Object.assign(config, { imageBinaryId }); } catch (e) { this.alert.danger(gettext('Unable to upload image.'), e?.data); return false; } } const styling = this.formGroup.value.styling; Object.assign(config, { styling }); return true; } ngOnInit() { this.initForm(); const imageFromConfig = this.imageBinaryId$.pipe(filter(id => !!id), distinctUntilChanged(), tap(() => (this.loading = true)), switchMap(imageBinaryId => this.imageWidget.getImageDetails(imageBinaryId)), tap(details => { this.loading = false; this.fileFromConfig = details.file; if (details) { this.formGroup.patchValue({ images: [{ file: details.file, name: details.file.name }] }); } }), map(details => details?.base64), shareReplay(1)); const selectedFile = this.formGroup.valueChanges.pipe(map(value => this.getFileFromFormValue(value)), distinctUntilChanged(), switchMap(file => (file ? this.fileService.toBase64(file) : Promise.resolve('')))); this.imageSrc$ = merge(imageFromConfig, selectedFile).pipe(map(base64 => (base64 ? base64 : ''))); this.imageBinaryId$.next(this.config?.imageBinaryId); } ngAfterViewInit() { this.imagePreviewTemplate.changes.pipe(takeUntil(this.destroyed$)).subscribe(template => { this.widgetConfigService.setPreview(template.first); }); } ngOnDestroy() { this.destroyed$.next(); this.destroyed$.complete(); } getDashboardMoId() { return get(this.widgetConfig, 'mo.id', null); } async initForm() { const stylingFormGroup = this.formBuilder.group({ objectFit: [ // fallback to 'full-width' for old legacy widgets, newly created widgets should use defaultObjectFitValue this.config.imageBinaryId && !this.config.styling ? 'full-width' : defaultObjectFitValue, [Validators.required] ], objectPositionX: [defaultObjectPositionValue, [Validators.required]], objectPositionY: [defaultObjectPositionValue, [Validators.required]] }); this.formGroup = this.formBuilder.group({ images: [ null, [ Validators.required, Validators.minLength(1), Validators.maxLength(1), C8yValidators.filesValidator({ maximumFileSizeInKb: 1000, typePrefix: 'image/' }) ] ], styling: stylingFormGroup }); this.form.form.addControl('config', this.formGroup); this.formGroup.patchValue(this.config); this.formGroup.valueChanges .pipe(takeUntil(this.destroyed$)) .subscribe(changes => this.setStyling(changes)); } getFileFromFormValue(formValue) { const images = formValue?.images || []; return images[0]?.file || null; } setStyling(config) { this.styling = this.imageWidget.getStyling(config); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: ImageWidgetConfigComponent, deps: [{ token: i1$1.FormBuilder }, { token: i1$1.NgForm }, { token: i2.FilesService }, { token: i2.AlertService }, { token: ImageWidgetService }, { token: i4.WidgetConfigComponent, optional: true }, { token: i4.WidgetConfigService }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: ImageWidgetConfigComponent, isStandalone: true, selector: "c8y-image-widget-config", inputs: { config: "config" }, viewQueries: [{ propertyName: "imagePreviewTemplate", predicate: ["imagePreview"], descendants: true }], ngImport: i0, template: "<div [formGroup]=\"formGroup\">\n <fieldset class=\"c8y-fieldset\">\n <legend>{{ 'Image' | translate }}</legend>\n <c8y-form-group class=\"m-b-16\">\n <c8y-drop-area\n class=\"drop-area-sm\"\n [icon]=\"'upload'\"\n formControlName=\"images\"\n [accept]=\"'image'\"\n [maxAllowedFiles]=\"1\"\n ></c8y-drop-area>\n <c8y-messages>\n <c8y-message\n name=\"maxFileSizeReached\"\n [text]=\"\n 'The maximum image size is {{ maxFileSize }} kB. The selected image has a size of {{ actualFileSize }} kB.'\n | translate\n \"\n ></c8y-message>\n <c8y-message\n name=\"wrongFileType\"\n [text]=\"'Wrong file type, select an image.' | translate\"\n ></c8y-message>\n <c8y-message\n name=\"maxlength\"\n [text]=\"'Only select one image.' | translate\"\n ></c8y-message>\n <c8y-message\n name=\"required\"\n [text]=\"'An image is required.' | translate\"\n ></c8y-message>\n <c8y-message\n name=\"minlength\"\n [text]=\"'An image is required.' | translate\"\n ></c8y-message>\n </c8y-messages>\n </c8y-form-group>\n </fieldset>\n\n <div *ngIf=\"imageSrc$ | async as src\">\n <fieldset class=\"c8y-fieldset\">\n <legend>\n {{ 'Size and alignment' | translate }}\n </legend>\n <div [formGroupName]=\"'styling'\">\n <c8y-form-group class=\"form-group-sm m-b-0\">\n <label class=\"m-b-4\">\n {{ 'Image display' | translate }}\n </label>\n <ul class=\"list-group separator-top-bottom\">\n <li\n class=\"list-group-item d-flex a-i-center p-l-0\"\n *ngFor=\"let objectFitOption of objectFitOptions; let i = index\"\n >\n <label class=\"c8y-radio\">\n <input\n type=\"radio\"\n [id]=\"'groupradiocontentclass' + i\"\n formControlName=\"objectFit\"\n [value]=\"objectFitOption.value\"\n />\n <span></span>\n <span class=\"l-h-1\">\n {{ objectFitOption.label | translate }}\n <br />\n <small class=\"text-muted\">\n {{ objectFitOption.description | translate }}\n </small>\n </span>\n </label>\n </li>\n </ul>\n </c8y-form-group>\n <div class=\"row\">\n <div class=\"col-md-6\">\n <c8y-form-group class=\"form-group-sm\">\n <label>{{ 'Horizontal alignment' | translate }}</label>\n <div class=\"c8y-select-wrapper\">\n <select\n class=\"form-control\"\n formControlName=\"objectPositionX\"\n >\n <option [ngValue]=\"'left'\">\n {{ 'left`horizontal alignment`' | translate }}\n </option>\n <option [ngValue]=\"'center'\">\n {{ 'center`horizontal alignment`' | translate }}\n </option>\n <option [ngValue]=\"'right'\">\n {{ 'right`horizontal alignment`' | translate }}\n </option>\n </select>\n </div>\n </c8y-form-group>\n </div>\n <div class=\"col-md-6\">\n <c8y-form-group class=\"form-group-sm\">\n <label>{{ 'Vertical alignment' | translate }}</label>\n <div class=\"c8y-select-wrapper\">\n <select\n class=\"form-control\"\n formControlName=\"objectPositionY\"\n >\n <option [ngValue]=\"'top'\">\n {{ 'top`vertical alignment`' | translate }}\n </option>\n <option [ngValue]=\"'center'\">\n {{ 'center`vertical alignment`' | translate }}\n </option>\n <option [ngValue]=\"'bottom'\">\n {{ 'bottom`vertical alignment`' | translate }}\n </option>\n </select>\n </div>\n </c8y-form-group>\n </div>\n </div>\n </div>\n </fieldset>\n <ng-template #imagePreview>\n <img\n [ngStyle]=\"styling\"\n [src]=\"src\"\n [ngClass]=\"styling ? 'fit-h fit-w' : 'img-responsive'\"\n />\n </ng-template>\n </div>\n <div\n class=\"d-flex j-c-center\"\n *ngIf=\"loading\"\n >\n <c8y-loading></c8y-loading>\n </div>\n</div>\n", dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$1NgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1$1.RadioControlValueAccessor, selector: "input[type=radio][formControlName],input[type=radio][formControl],input[type=radio][ngModel]", inputs: ["name", "formControlName", "value"] }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i1$1.FormGroupName, selector: "[formGroupName]", inputs: ["formGroupName"] }, { kind: "component", type: FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "component", type: MessagesComponent, selector: "c8y-messages", inputs: ["show", "defaults", "helpMessage"] }, { kind: "directive", type: MessageDirective, selector: "c8y-message", inputs: ["name", "text"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "component", type: DropAreaComponent, selector: "c8y-drop-area", inputs: ["formControl", "title", "message", "icon", "loadingMessage", "forceHideList", "alwaysShow", "clickToOpen", "loading", "progress", "maxAllowedFiles", "files", "maxFileSizeInMegaBytes", "accept"], outputs: ["dropped"] }, { kind: "component", type: LoadingComponent, selector: "c8y-loading", inputs: ["layout", "progress", "message"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: AsyncPipe, name: "async" }], viewProviders: [{ provide: ControlContainer, useExisting: NgForm }] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: ImageWidgetConfigComponent, decorators: [{ type: Component, args: [{ selector: 'c8y-image-widget-config', viewProviders: [{ provide: ControlContainer, useExisting: NgForm }], standalone: true, imports: [ ReactiveFormsModule, FormGroupComponent, MessagesComponent, MessageDirective, C8yTranslatePipe, AsyncPipe, NgIf, NgStyle, NgClass, NgForOf, DropAreaComponent, LoadingComponent ], template: "<div [formGroup]=\"formGroup\">\n <fieldset class=\"c8y-fieldset\">\n <legend>{{ 'Image' | translate }}</legend>\n <c8y-form-group class=\"m-b-16\">\n <c8y-drop-area\n class=\"drop-area-sm\"\n [icon]=\"'upload'\"\n formControlName=\"images\"\n [accept]=\"'image'\"\n [maxAllowedFiles]=\"1\"\n ></c8y-drop-area>\n <c8y-messages>\n <c8y-message\n name=\"maxFileSizeReached\"\n [text]=\"\n 'The maximum image size is {{ maxFileSize }} kB. The selected image has a size of {{ actualFileSize }} kB.'\n | translate\n \"\n ></c8y-message>\n <c8y-message\n name=\"wrongFileType\"\n [text]=\"'Wrong file type, select an image.' | translate\"\n ></c8y-message>\n <c8y-message\n name=\"maxlength\"\n [text]=\"'Only select one image.' | translate\"\n ></c8y-message>\n <c8y-message\n name=\"required\"\n [text]=\"'An image is required.' | translate\"\n ></c8y-message>\n <c8y-message\n name=\"minlength\"\n [text]=\"'An image is required.' | translate\"\n ></c8y-message>\n </c8y-messages>\n </c8y-form-group>\n </fieldset>\n\n <div *ngIf=\"imageSrc$ | async as src\">\n <fieldset class=\"c8y-fieldset\">\n <legend>\n {{ 'Size and alignment' | translate }}\n </legend>\n <div [formGroupName]=\"'styling'\">\n <c8y-form-group class=\"form-group-sm m-b-0\">\n <label class=\"m-b-4\">\n {{ 'Image display' | translate }}\n </label>\n <ul class=\"list-group separator-top-bottom\">\n <li\n class=\"list-group-item d-flex a-i-center p-l-0\"\n *ngFor=\"let objectFitOption of objectFitOptions; let i = index\"\n >\n <label class=\"c8y-radio\">\n <input\n type=\"radio\"\n [id]=\"'groupradiocontentclass' + i\"\n formControlName=\"objectFit\"\n [value]=\"objectFitOption.value\"\n />\n <span></span>\n <span class=\"l-h-1\">\n {{ objectFitOption.label | translate }}\n <br />\n <small class=\"text-muted\">\n {{ objectFitOption.description | translate }}\n </small>\n </span>\n </label>\n </li>\n </ul>\n </c8y-form-group>\n <div class=\"row\">\n <div class=\"col-md-6\">\n <c8y-form-group class=\"form-group-sm\">\n <label>{{ 'Horizontal alignment' | translate }}</label>\n <div class=\"c8y-select-wrapper\">\n <select\n class=\"form-control\"\n formControlName=\"objectPositionX\"\n >\n <option [ngValue]=\"'left'\">\n {{ 'left`horizontal alignment`' | translate }}\n </option>\n <option [ngValue]=\"'center'\">\n {{ 'center`horizontal alignment`' | translate }}\n </option>\n <option [ngValue]=\"'right'\">\n {{ 'right`horizontal alignment`' | translate }}\n </option>\n </select>\n </div>\n </c8y-form-group>\n </div>\n <div class=\"col-md-6\">\n <c8y-form-group class=\"form-group-sm\">\n <label>{{ 'Vertical alignment' | translate }}</label>\n <div class=\"c8y-select-wrapper\">\n <select\n class=\"form-control\"\n formControlName=\"objectPositionY\"\n >\n <option [ngValue]=\"'top'\">\n {{ 'top`vertical alignment`' | translate }}\n </option>\n <option [ngValue]=\"'center'\">\n {{ 'center`vertical alignment`' | translate }}\n </option>\n <option [ngValue]=\"'bottom'\">\n {{ 'bottom`vertical alignment`' | translate }}\n </option>\n </select>\n </div>\n </c8y-form-group>\n </div>\n </div>\n </div>\n </fieldset>\n <ng-template #imagePreview>\n <img\n [ngStyle]=\"styling\"\n [src]=\"src\"\n [ngClass]=\"styling ? 'fit-h fit-w' : 'img-responsive'\"\n />\n </ng-template>\n </div>\n <div\n class=\"d-flex j-c-center\"\n *ngIf=\"loading\"\n >\n <c8y-loading></c8y-loading>\n </div>\n</div>\n" }] }], ctorParameters: () => [{ type: i1$1.FormBuilder }, { type: i1$1.NgForm }, { type: i2.FilesService }, { type: i2.AlertService }, { type: ImageWidgetService }, { type: i4.WidgetConfigComponent, decorators: [{ type: Optional }] }, { type: i4.WidgetConfigService }], propDecorators: { config: [{ type: Input }], imagePreviewTemplate: [{ type: ViewChildren, args: ['imagePreview'] }] } }); class ImageWidgetViewComponent { constructor(imageWidget) { this.imageWidget = imageWidget; this.imageBinaryId$ = new BehaviorSubject(null); this.loading = true; this.imageDetails$ = this.imageBinaryId$.pipe(distinctUntilChanged(), tap(() => (this.loading = true)), switchMap(imageBinaryId => this.imageWidget.getImageDetails(imageBinaryId)), tap(() => (this.loading = false)), shareReplay(1)); } ngOnChanges(changes) { if (changes.config) { const value = changes.config.currentValue; this.imageBinaryId$.next(value.imageBinaryId); this.setStyling(value); } } setStyling(config) { this.styling = this.imageWidget.getStyling(config); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: ImageWidgetViewComponent, deps: [{ token: ImageWidgetService }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: ImageWidgetViewComponent, isStandalone: true, selector: "c8y-image-widget-view", inputs: { config: "config" }, usesOnChanges: true, ngImport: i0, template: "<ng-container *ngIf=\"imageDetails$ | async as details; else empty\">\n <img\n [src]=\"details.base64\"\n [title]=\"details.file.name\"\n [ngClass]=\"styling ? 'fit-h fit-w' : 'img-responsive'\"\n [ngStyle]=\"styling\"\n />\n</ng-container>\n\n<ng-template #empty>\n <div class=\"d-flex fit-h fit-w j-c-center a-i-center\" *ngIf=\"!loading\">\n <c8y-ui-empty-state\n class=\"fit-w\"\n [icon]=\"'no-image'\"\n [title]=\"'No image to display.' | translate\"\n [subtitle]=\"'Upload an image.' | translate\"\n [horizontal]=\"true\"\n ></c8y-ui-empty-state>\n </div>\n</ng-template>\n<div class=\"d-flex fit-h fit-w j-c-center a-i-center\" *ngIf=\"loading\">\n <c8y-loading></c8y-loading>\n</div>\n", dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: LoadingComponent, selector: "c8y-loading", inputs: ["layout", "progress", "message"] }, { kind: "component", type: EmptyStateComponent, selector: "c8y-ui-empty-state", inputs: ["icon", "title", "subtitle", "horizontal"] }, { kind: "pipe", type: AsyncPipe, name: "async" }] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: ImageWidgetViewComponent, decorators: [{ type: Component, args: [{ selector: 'c8y-image-widget-view', standalone: true, imports: [NgIf, NgClass, AsyncPipe, NgStyle, LoadingComponent, EmptyStateComponent], template: "<ng-container *ngIf=\"imageDetails$ | async as details; else empty\">\n <img\n [src]=\"details.base64\"\n [title]=\"details.file.name\"\n [ngClass]=\"styling ? 'fit-h fit-w' : 'img-responsive'\"\n [ngStyle]=\"styling\"\n />\n</ng-container>\n\n<ng-template #empty>\n <div class=\"d-flex fit-h fit-w j-c-center a-i-center\" *ngIf=\"!loading\">\n <c8y-ui-empty-state\n class=\"fit-w\"\n [icon]=\"'no-image'\"\n [title]=\"'No image to display.' | translate\"\n [subtitle]=\"'Upload an image.' | translate\"\n [horizontal]=\"true\"\n ></c8y-ui-empty-state>\n </div>\n</ng-template>\n<div class=\"d-flex fit-h fit-w j-c-center a-i-center\" *ngIf=\"loading\">\n <c8y-loading></c8y-loading>\n</div>\n" }] }], ctorParameters: () => [{ type: ImageWidgetService }], propDecorators: { config: [{ type: Input }] } }); /** * Generated bundle index. Do not edit. */ export { ImageWidgetConfigComponent, ImageWidgetService, ImageWidgetViewComponent, defaultObjectFitValue, defaultObjectPositionValue }; //# sourceMappingURL=c8y-ngx-components-widgets-implementations-image.mjs.map