[3.0] Add inputs widgets (#2526)
* Add location widget * Fix translate, clear code * Fix translate * Add date input widgets * Add image input widgets * Init web camera input widget * Add functional web camera input widget * Add styles to webcamera iputs widget * Add link code
This commit is contained in:
parent
adc7194669
commit
1af55e8400
File diff suppressed because one or more lines are too long
@ -0,0 +1,74 @@
|
||||
<!--
|
||||
|
||||
Copyright © 2016-2020 The Thingsboard Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
-->
|
||||
<div fxLayout="column" fxLayoutAlign="center center" class="tb-web-camera" tb-fullscreen [fullscreen]="isShowCamera">
|
||||
<div [fxShow]="isEntityDetected && dataKeyDetected && isCameraSupport && isDeviceDetect" fxFlexFill>
|
||||
<div [fxShow]="!isShowCamera" fxLayout="column" fxLayoutAlign="space-between center" fxFlexFill>
|
||||
<div class="tb-web-camera__last-photo" fxFlex>
|
||||
<span [fxShow]="!lastPhoto" class="tb-web-camera__last-photo_text" translate>widgets.input-widgets.no-image</span>
|
||||
<img [fxShow]="lastPhoto" class="tb-web-camera__last-photo_img" [src]="lastPhoto" alt="last photo"/>
|
||||
</div>
|
||||
<button mat-raised-button color="primary" (click)="takePhoto()">
|
||||
{{ "widgets.input-widgets.take-photo" | translate }}
|
||||
</button>
|
||||
</div>
|
||||
<div [fxShow]="isShowCamera" fxLayout="column" fxLayoutAlign="center center" class="camera-container">
|
||||
<div class="camera" [fxShow]="!isPreviewPhoto">
|
||||
<video autoplay muted playsinline class="camera-stream" #videoStream></video>
|
||||
<div class="camera-controls" fxLayout="row wrap" fxLayoutAlign="space-between end">
|
||||
<div fxFlex></div>
|
||||
<button mat-mini-fab color="primary" (click)="switchWebCamera()" [disabled]="singleDevice">
|
||||
<mat-icon>switch_camera</mat-icon>
|
||||
</button>
|
||||
<button mat-fab color="accent" (click)="createPhoto()">
|
||||
<mat-icon>photo_camera</mat-icon>
|
||||
</button>
|
||||
<button mat-mini-fab color="primary" (click)="closeCamera()">
|
||||
<mat-icon>close</mat-icon>
|
||||
</button>
|
||||
<div fxFlex></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="camera" [fxShow]="isPreviewPhoto">
|
||||
<img alt="preview photo" class="camera-stream" [src]="previewPhoto">
|
||||
<canvas #canvas style="display:none;"></canvas>
|
||||
<div class="camera-controls" fxLayout="row" fxLayoutAlign="space-between end">
|
||||
<div fxFlex></div>
|
||||
<button mat-fab color="primary" [disabled]="updatePhoto" (click)="cancelPhoto()">
|
||||
<mat-icon>close</mat-icon>
|
||||
</button>
|
||||
<button mat-fab color="accent" [disabled]="updatePhoto" (click)="savePhoto()">
|
||||
<mat-icon>check</mat-icon>
|
||||
</button>
|
||||
<div fxFlex></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="message-text" [fxHide]="isEntityDetected">
|
||||
{{ 'widgets.input-widgets.no-entity-selected' | translate }}
|
||||
</div>
|
||||
<div class="message-text" [fxShow]="isEntityDetected && !dataKeyDetected">
|
||||
{{ 'widgets.input-widgets.no-datakey-selected' | translate }}
|
||||
</div>
|
||||
<div class="message-text" [fxShow]="isEntityDetected && dataKeyDetected && !isCameraSupport">
|
||||
{{ 'widgets.input-widgets.no-support-web-camera' | translate }}
|
||||
</div>
|
||||
<div class="message-text" [fxShow]="isEntityDetected && dataKeyDetected && isCameraSupport && !isDeviceDetect">
|
||||
{{ 'widgets.input-widgets.no-support-web-camera' | translate }}
|
||||
</div>
|
||||
</div>
|
||||
@ -0,0 +1,74 @@
|
||||
/**
|
||||
* Copyright © 2016-2020 The Thingsboard Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
.tb-web-camera {
|
||||
height: 100%;
|
||||
|
||||
&__last-photo {
|
||||
width: 100%;
|
||||
margin: 5px 0;
|
||||
text-align: center;
|
||||
border: solid 1px;
|
||||
|
||||
&_text {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
margin-top: -.625em;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
&_img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
}
|
||||
|
||||
.camera-container{
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.camera {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
|
||||
.camera-stream {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.camera-controls {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
padding: 0 5px 5px;
|
||||
|
||||
.mat-button-base{
|
||||
margin: 6px 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.message-text {
|
||||
font-size: 18px;
|
||||
color: #a0a0a0;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,274 @@
|
||||
///
|
||||
/// Copyright © 2016-2020 The Thingsboard Authors
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
///
|
||||
|
||||
import {
|
||||
Component,
|
||||
ElementRef,
|
||||
Inject,
|
||||
Input,
|
||||
NgZone,
|
||||
OnDestroy,
|
||||
OnInit,
|
||||
ViewChild,
|
||||
ViewEncapsulation
|
||||
} from '@angular/core';
|
||||
import { PageComponent } from '@shared/components/page.component';
|
||||
import { WidgetContext } from '@home/models/widget-component.models';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { AppState } from '@core/core.state';
|
||||
import { Overlay } from '@angular/cdk/overlay';
|
||||
import { UtilsService } from '@core/services/utils.service';
|
||||
import { Datasource, DatasourceData, DatasourceType } from '@shared/models/widget.models';
|
||||
import { WINDOW } from '@core/services/window.service';
|
||||
import { AttributeService } from '@core/http/attribute.service';
|
||||
import { EntityId } from '@shared/models/id/entity-id';
|
||||
import { AttributeScope, DataKeyType } from '@shared/models/telemetry/telemetry.models';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
interface WebCameraInputWidgetSettings {
|
||||
widgetTitle: string;
|
||||
imageQuality: number;
|
||||
imageFormat: string;
|
||||
maxWidth: number;
|
||||
maxHeight: number;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'tb-web-camera-widget',
|
||||
templateUrl: './web-camera-input.component.html',
|
||||
styleUrls: ['./web-camera-input.component.scss'],
|
||||
encapsulation: ViewEncapsulation.None
|
||||
})
|
||||
export class WebCameraInputWidgetComponent extends PageComponent implements OnInit, OnDestroy {
|
||||
|
||||
constructor(@Inject(WINDOW) private window: Window,
|
||||
protected store: Store<AppState>,
|
||||
private elementRef: ElementRef,
|
||||
private ngZone: NgZone,
|
||||
private overlay: Overlay,
|
||||
private utils: UtilsService,
|
||||
private attributeService: AttributeService,
|
||||
) {
|
||||
super(store);
|
||||
}
|
||||
|
||||
public get videoElement() {
|
||||
return this.videoStreamRef.nativeElement;
|
||||
}
|
||||
|
||||
public get canvasElement() {
|
||||
return this.canvasRef.nativeElement;
|
||||
}
|
||||
|
||||
public get videoWidth() {
|
||||
const videoRatio = this.getVideoAspectRatio();
|
||||
return Math.min(this.width, this.height * videoRatio);
|
||||
}
|
||||
|
||||
public get videoHeight() {
|
||||
const videoRatio = this.getVideoAspectRatio();
|
||||
return Math.min(this.height, this.width / videoRatio);
|
||||
}
|
||||
|
||||
private static DEFAULT_IMAGE_TYPE = 'image/jpeg';
|
||||
private static DEFAULT_IMAGE_QUALITY = 0.92;
|
||||
|
||||
@Input()
|
||||
ctx: WidgetContext;
|
||||
|
||||
@ViewChild('videoStream', {static: true}) videoStreamRef: ElementRef<HTMLVideoElement>;
|
||||
@ViewChild('canvas', {static: true}) canvasRef: ElementRef<HTMLCanvasElement>;
|
||||
|
||||
private videoInputsIndex = 0;
|
||||
private settings: WebCameraInputWidgetSettings;
|
||||
private datasource: Datasource;
|
||||
private width = 640;
|
||||
private height = 480;
|
||||
private availableVideoInputs: MediaDeviceInfo[];
|
||||
private mediaStream: MediaStream;
|
||||
|
||||
isEntityDetected = false;
|
||||
dataKeyDetected = false;
|
||||
isCameraSupport = false;
|
||||
isDeviceDetect = false;
|
||||
isShowCamera = false;
|
||||
isPreviewPhoto = false;
|
||||
singleDevice = true;
|
||||
updatePhoto = false;
|
||||
previewPhoto: any;
|
||||
lastPhoto: any;
|
||||
|
||||
private static hasGetUserMedia(): boolean {
|
||||
return !!(window.navigator.mediaDevices && window.navigator.mediaDevices.getUserMedia);
|
||||
}
|
||||
|
||||
private static getAvailableVideoInputs(): Promise<MediaDeviceInfo[]> {
|
||||
return new Promise((resolve, reject) => {
|
||||
navigator.mediaDevices.enumerateDevices()
|
||||
.then((devices: MediaDeviceInfo[]) => {
|
||||
resolve(devices.filter((device: MediaDeviceInfo) => device.kind === 'videoinput'));
|
||||
})
|
||||
.catch(err => {
|
||||
reject(err.message || err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.ctx.$scope.webCameraInputWidget = this;
|
||||
this.settings = this.ctx.settings;
|
||||
this.datasource = this.ctx.datasources[0];
|
||||
|
||||
if (this.settings.widgetTitle && this.settings.widgetTitle.length) {
|
||||
this.ctx.widgetTitle = this.utils.customTranslation(this.settings.widgetTitle, this.settings.widgetTitle);
|
||||
} else {
|
||||
this.ctx.widgetTitle = this.ctx.widgetConfig.title;
|
||||
}
|
||||
|
||||
this.width = this.settings.maxWidth ? this.settings.maxWidth : 640;
|
||||
this.height = this.settings.maxHeight ? this.settings.maxWidth : 480;
|
||||
|
||||
if (this.datasource.type === DatasourceType.entity) {
|
||||
if (this.datasource.entityType && this.datasource.entityId) {
|
||||
this.isEntityDetected = true;
|
||||
}
|
||||
}
|
||||
if (this.datasource.dataKeys.length) {
|
||||
this.dataKeyDetected = true;
|
||||
}
|
||||
this.detectAvailableDevices();
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.stopMediaTracks();
|
||||
}
|
||||
|
||||
private updateWidgetData(data: Array<DatasourceData>) {
|
||||
const keyData = data[0].data;
|
||||
if (keyData && keyData.length) {
|
||||
this.lastPhoto = keyData[0][1];
|
||||
}
|
||||
}
|
||||
|
||||
public onDataUpdated() {
|
||||
this.ngZone.run(() => {
|
||||
this.updateWidgetData(this.ctx.defaultSubscription.data);
|
||||
this.ctx.detectChanges();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private detectAvailableDevices(): void {
|
||||
if (WebCameraInputWidgetComponent.hasGetUserMedia()) {
|
||||
this.isCameraSupport = true;
|
||||
WebCameraInputWidgetComponent.getAvailableVideoInputs().then((devices) => {
|
||||
this.isDeviceDetect = !!devices.length;
|
||||
this.singleDevice = devices.length < 2;
|
||||
this.availableVideoInputs = devices;
|
||||
this.ctx.detectChanges();
|
||||
}, () => {
|
||||
this.availableVideoInputs = [];
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private getVideoAspectRatio(): number {
|
||||
if (this.videoElement.videoWidth && this.videoElement.videoWidth > 0 &&
|
||||
this.videoElement.videoHeight && this.videoElement.videoHeight > 0) {
|
||||
return this.videoElement.videoWidth / this.videoElement.videoHeight;
|
||||
}
|
||||
return this.width / this.height;
|
||||
}
|
||||
|
||||
private stopMediaTracks() {
|
||||
if (this.mediaStream && this.mediaStream.getTracks) {
|
||||
this.mediaStream.getTracks()
|
||||
.forEach((track: MediaStreamTrack) => track.stop());
|
||||
}
|
||||
}
|
||||
|
||||
takePhoto() {
|
||||
this.isShowCamera = true;
|
||||
this.initWebCamera(this.availableVideoInputs[this.videoInputsIndex].deviceId);
|
||||
}
|
||||
|
||||
closeCamera() {
|
||||
this.stopMediaTracks();
|
||||
this.videoElement.srcObject = null;
|
||||
this.isShowCamera = false;
|
||||
}
|
||||
|
||||
cancelPhoto() {
|
||||
this.isPreviewPhoto = false;
|
||||
this.previewPhoto = '';
|
||||
}
|
||||
|
||||
savePhoto() {
|
||||
this.updatePhoto = true;
|
||||
let task: Observable<any>;
|
||||
const entityId: EntityId = {
|
||||
entityType: this.datasource.entityType,
|
||||
id: this.datasource.entityId
|
||||
};
|
||||
const saveData = [{
|
||||
key: this.datasource.dataKeys[0].name,
|
||||
value: this.previewPhoto
|
||||
}];
|
||||
if (this.datasource.dataKeys[0].type === DataKeyType.attribute) {
|
||||
task = this.attributeService.saveEntityAttributes(entityId, AttributeScope.SERVER_SCOPE, saveData);
|
||||
} else if (this.datasource.dataKeys[0].type === DataKeyType.timeseries) {
|
||||
task = this.attributeService.saveEntityTimeseries(entityId, 'scope', saveData);
|
||||
}
|
||||
task.subscribe(() => {
|
||||
this.isPreviewPhoto = false;
|
||||
this.updatePhoto = false;
|
||||
this.closeCamera();
|
||||
}, () => {
|
||||
this.updatePhoto = false;
|
||||
})
|
||||
}
|
||||
|
||||
switchWebCamera() {
|
||||
this.videoInputsIndex = (this.videoInputsIndex + 1) % this.availableVideoInputs.length;
|
||||
this.stopMediaTracks();
|
||||
this.initWebCamera(this.availableVideoInputs[this.videoInputsIndex].deviceId)
|
||||
}
|
||||
|
||||
createPhoto() {
|
||||
this.canvasElement.width = this.videoWidth;
|
||||
this.canvasElement.height = this.videoHeight;
|
||||
this.canvasElement.getContext('2d').drawImage(this.videoElement, 0, 0, this.videoWidth, this.videoHeight);
|
||||
|
||||
const mimeType: string = this.settings.imageFormat ? this.settings.imageFormat : WebCameraInputWidgetComponent.DEFAULT_IMAGE_TYPE;
|
||||
const quality: number = this.settings.imageQuality ? this.settings.imageQuality : WebCameraInputWidgetComponent.DEFAULT_IMAGE_QUALITY;
|
||||
this.previewPhoto = this.canvasElement.toDataURL(mimeType, quality);
|
||||
this.isPreviewPhoto = true;
|
||||
}
|
||||
|
||||
private initWebCamera(deviceId?: string) {
|
||||
if (window.navigator.mediaDevices && window.navigator.mediaDevices.getUserMedia) {
|
||||
const videoTrackConstraints = {
|
||||
video: {deviceId: deviceId !== '' ? {exact: deviceId} : undefined}
|
||||
};
|
||||
|
||||
window.navigator.mediaDevices.getUserMedia(videoTrackConstraints).then((stream: MediaStream) => {
|
||||
this.mediaStream = stream;
|
||||
this.videoElement.srcObject = stream;
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -31,6 +31,7 @@ import {
|
||||
DateRangeNavigatorWidgetComponent
|
||||
} from '@home/components/widget/lib/date-range-navigator/date-range-navigator.component';
|
||||
import { MultipleInputWidgetComponent } from './lib/multiple-input-widget.component';
|
||||
import { WebCameraInputWidgetComponent } from './lib/web-camera-input.component';
|
||||
|
||||
@NgModule({
|
||||
declarations:
|
||||
@ -43,7 +44,8 @@ import { MultipleInputWidgetComponent } from './lib/multiple-input-widget.compon
|
||||
EntitiesHierarchyWidgetComponent,
|
||||
DateRangeNavigatorWidgetComponent,
|
||||
DateRangeNavigatorPanelComponent,
|
||||
MultipleInputWidgetComponent
|
||||
MultipleInputWidgetComponent,
|
||||
WebCameraInputWidgetComponent
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
@ -58,7 +60,8 @@ import { MultipleInputWidgetComponent } from './lib/multiple-input-widget.compon
|
||||
EntitiesHierarchyWidgetComponent,
|
||||
RpcWidgetsModule,
|
||||
DateRangeNavigatorWidgetComponent,
|
||||
MultipleInputWidgetComponent
|
||||
MultipleInputWidgetComponent,
|
||||
WebCameraInputWidgetComponent
|
||||
],
|
||||
providers: [
|
||||
CustomDialogService
|
||||
|
||||
@ -16,15 +16,15 @@
|
||||
|
||||
-->
|
||||
<div class="tb-container">
|
||||
<label class="tb-title">{{label}}</label>
|
||||
<label class="tb-title" *ngIf="label">{{label}}</label>
|
||||
<ng-container #flow="flow"
|
||||
[flowConfig]="{singleFile: true, allowDuplicateUploads: true}">
|
||||
<div class="tb-image-select-container">
|
||||
<div class="tb-image-preview-container">
|
||||
<div *ngIf="!safeImageUrl" translate>dashboard.no-image</div>
|
||||
<img *ngIf="safeImageUrl" class="tb-image-preview" [src]="safeImageUrl" />
|
||||
<div *ngIf="showPreview" class="tb-image-preview-container">
|
||||
<div *ngIf="!safeImageUrl;else elseBlock" translate>dashboard.no-image</div>
|
||||
<ng-template #elseBlock><img class="tb-image-preview" [src]="safeImageUrl" /></ng-template>
|
||||
</div>
|
||||
<div class="tb-image-clear-container">
|
||||
<div *ngIf="showClearButton" class="tb-image-clear-container">
|
||||
<button mat-button mat-icon-button color="primary"
|
||||
type="button"
|
||||
(click)="clearImage()"
|
||||
@ -37,8 +37,8 @@
|
||||
<div class="drop-area tb-flow-drop"
|
||||
flowDrop
|
||||
[flow]="flow.flowJs">
|
||||
<label for="select" translate>dashboard.drop-image</label>
|
||||
<input class="file-input" flowButton type="file" [flow]="flow.flowJs" [flowAttributes]="{accept: 'image/*'}" id="select">
|
||||
<label for="{{inputId}}" translate>dashboard.drop-image</label>
|
||||
<input class="file-input" flowButton type="file" [flow]="flow.flowJs" [flowAttributes]="{accept: 'image/*'}" id="{{inputId}}">
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
@ -35,9 +35,9 @@ $previewSize: 100px !default;
|
||||
|
||||
.tb-image-preview {
|
||||
width: auto;
|
||||
max-width: $previewSize;
|
||||
max-width: $previewSize - 2;
|
||||
height: auto;
|
||||
max-height: $previewSize;
|
||||
max-height: $previewSize - 2;
|
||||
}
|
||||
|
||||
.tb-image-preview-container {
|
||||
|
||||
@ -14,34 +14,16 @@
|
||||
/// limitations under the License.
|
||||
///
|
||||
|
||||
import { Component, ElementRef, forwardRef, Input, OnInit, ViewChild, AfterViewInit, OnDestroy } from '@angular/core';
|
||||
import { AfterViewInit, Component, forwardRef, Input, OnDestroy, ViewChild } from '@angular/core';
|
||||
import { PageComponent } from '@shared/components/page.component';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { AppState } from '@core/core.state';
|
||||
import { DataKey, DatasourceType } from '@shared/models/widget.models';
|
||||
import {
|
||||
ControlValueAccessor,
|
||||
FormBuilder,
|
||||
FormControl,
|
||||
FormGroup,
|
||||
NG_VALIDATORS,
|
||||
NG_VALUE_ACCESSOR,
|
||||
Validator,
|
||||
Validators
|
||||
} from '@angular/forms';
|
||||
import { UtilsService } from '@core/services/utils.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { EntityService } from '@core/http/entity.service';
|
||||
import { DataKeysCallbacks } from '@home/components/widget/data-keys.component.models';
|
||||
import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
|
||||
import { Observable, of, Subscription } from 'rxjs';
|
||||
import { map, mergeMap, tap } from 'rxjs/operators';
|
||||
import { alarmFields } from '@shared/models/alarm.models';
|
||||
import { ControlValueAccessor, NG_VALUE_ACCESSOR, } from '@angular/forms';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { coerceBooleanProperty } from '@angular/cdk/coercion';
|
||||
import { DialogService } from '@core/services/dialog.service';
|
||||
import { FlowDirective } from '@flowjs/ngx-flow';
|
||||
import { DomSanitizer, SafeUrl } from '@angular/platform-browser';
|
||||
import { UtilsService } from '@core/services/utils.service';
|
||||
|
||||
@Component({
|
||||
selector: 'tb-image-input',
|
||||
@ -61,9 +43,11 @@ export class ImageInputComponent extends PageComponent implements AfterViewInit,
|
||||
label: string;
|
||||
|
||||
private requiredValue: boolean;
|
||||
|
||||
get required(): boolean {
|
||||
return this.requiredValue;
|
||||
}
|
||||
|
||||
@Input()
|
||||
set required(value: boolean) {
|
||||
const newVal = coerceBooleanProperty(value);
|
||||
@ -75,6 +59,15 @@ export class ImageInputComponent extends PageComponent implements AfterViewInit,
|
||||
@Input()
|
||||
disabled: boolean;
|
||||
|
||||
@Input()
|
||||
showClearButton = true;
|
||||
|
||||
@Input()
|
||||
showPreview = true;
|
||||
|
||||
@Input()
|
||||
inputId = this.utils.guid();
|
||||
|
||||
imageUrl: string;
|
||||
safeImageUrl: SafeUrl;
|
||||
|
||||
@ -86,6 +79,7 @@ export class ImageInputComponent extends PageComponent implements AfterViewInit,
|
||||
private propagateChange = null;
|
||||
|
||||
constructor(protected store: Store<AppState>,
|
||||
private utils: UtilsService,
|
||||
private sanitizer: DomSanitizer) {
|
||||
super(store);
|
||||
}
|
||||
|
||||
@ -75,6 +75,7 @@
|
||||
import './zone-flags';
|
||||
import 'zone.js/dist/zone'; // Included with Angular CLI.
|
||||
import 'core-js/es/array';
|
||||
import moment from 'moment';
|
||||
|
||||
/***************************************************************************************************
|
||||
* APPLICATION IMPORTS
|
||||
@ -99,6 +100,7 @@ const tinycolor = tinycolor_;
|
||||
|
||||
(window as any).tinycolor = tinycolor;
|
||||
(window as any).cssjs = cssjs;
|
||||
(window as any).moment = moment;
|
||||
(window as any).TbFlot = TbFlot;
|
||||
(window as any).TbAnalogueCompass = TbAnalogueCompass;
|
||||
(window as any).TbAnalogueRadialGauge = TbAnalogueRadialGauge;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user