From e90e35c6780d9ff3ee6726b3530c811fccf99bf6 Mon Sep 17 00:00:00 2001 From: Artem Babak Date: Tue, 26 Oct 2021 07:31:24 +0300 Subject: [PATCH 1/6] Device profiles: implementation of the protobuf editor for MQTT device transport configuration --- ...ile-transport-configuration.component.html | 55 +++-- .../protobuf-content.component.html | 30 +++ .../protobuf-content.component.scss | 43 ++++ .../components/protobuf-content.component.ts | 221 ++++++++++++++++++ .../src/app/shared/models/ace/ace.models.ts | 1 + ui-ngx/src/app/shared/shared.module.ts | 7 +- 6 files changed, 337 insertions(+), 20 deletions(-) create mode 100644 ui-ngx/src/app/shared/components/protobuf-content.component.html create mode 100644 ui-ngx/src/app/shared/components/protobuf-content.component.scss create mode 100644 ui-ngx/src/app/shared/components/protobuf-content.component.ts diff --git a/ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.html b/ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.html index 55f0869dbc..8e7d082ad2 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.html @@ -74,35 +74,54 @@
- - device-profile.telemetry-proto-schema - +
+ + {{ 'device-profile.telemetry-proto-schema-required' | translate}} - - - device-profile.attributes-proto-schema - +
+
+ + {{ 'device-profile.attributes-proto-schema-required' | translate}} - - - device-profile.rpc-request-proto-schema - +
+
+ + - {{ 'device-profile.rpc-request-proto-schema-required' | translate}} + {{ 'device-profile.rpc-request-proto-required' | translate}} - device-profile.rpc-request-proto-schema-hint - - - device-profile.rpc-response-proto-schema - +
+
+ + {{ 'device-profile.rpc-response-proto-schema-required' | translate}} - +
diff --git a/ui-ngx/src/app/shared/components/protobuf-content.component.html b/ui-ngx/src/app/shared/components/protobuf-content.component.html new file mode 100644 index 0000000000..43765c66d7 --- /dev/null +++ b/ui-ngx/src/app/shared/components/protobuf-content.component.html @@ -0,0 +1,30 @@ +
+
+ + + + +
+
+ +
+
+
+
+
+
+
diff --git a/ui-ngx/src/app/shared/components/protobuf-content.component.scss b/ui-ngx/src/app/shared/components/protobuf-content.component.scss new file mode 100644 index 0000000000..70d3e3267d --- /dev/null +++ b/ui-ngx/src/app/shared/components/protobuf-content.component.scss @@ -0,0 +1,43 @@ +:host { + position: relative; + + .fill-height { + height: 100%; + } +} + +.tb-protobuf-content-toolbar { + button.mat-button, button.mat-icon-button, button.mat-icon-button.tb-mat-32 { + align-items: center; + vertical-align: middle; + min-width: 32px; + min-height: 15px; + padding: 4px; + margin: 0; + font-size: .8rem; + line-height: 15px; + color: #7b7b7b; + background: rgba(220, 220, 220, .35); + &:not(:last-child) { + margin-right: 4px; + } + } +} + +.tb-protobuf-content-panel { + height: 100%; + margin-left: 15px; + border: 1px solid #c0c0c0; + overflow: auto; + resize: vertical; + + #tb-protobuf-input { + width: 100%; + min-width: 200px; + min-height: 100px; + + &:not(.fill-height) { + min-height: 200px; + } + } +} diff --git a/ui-ngx/src/app/shared/components/protobuf-content.component.ts b/ui-ngx/src/app/shared/components/protobuf-content.component.ts new file mode 100644 index 0000000000..e640c05331 --- /dev/null +++ b/ui-ngx/src/app/shared/components/protobuf-content.component.ts @@ -0,0 +1,221 @@ +import { + Component, + ElementRef, + forwardRef, + Input, + OnChanges, + OnDestroy, + OnInit, + SimpleChanges, + ViewChild +} from '@angular/core'; +import { ControlValueAccessor, FormControl, NG_VALIDATORS, NG_VALUE_ACCESSOR, Validator } from '@angular/forms'; +import { Ace } from 'ace-builds'; +import { CancelAnimationFrame, RafService } from '@core/services/raf.service'; +import { ResizeObserver } from '@juggle/resize-observer'; +import { guid } from '@core/utils'; +import { ContentType } from '@shared/models/constants'; +import { coerceBooleanProperty } from '@angular/cdk/coercion'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { getAce } from '@shared/models/ace/ace.models'; +import { beautifyJs } from '@shared/models/beautify.models'; +import { ActionNotificationHide, ActionNotificationShow } from '@core/notification/notification.actions'; + +@Component({ + selector: 'tb-protobuf-content', + templateUrl: './protobuf-content.component.html', + styleUrls: ['./protobuf-content.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => ProtobufContentComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => ProtobufContentComponent), + multi: true, + } + ] +}) +export class ProtobufContentComponent implements OnInit, ControlValueAccessor, Validator, OnChanges, OnDestroy { + + @ViewChild('protobufEditor', {static: true}) + protobufEditorElmRef: ElementRef; + + private protobufEditor: Ace.Editor; + private editorsResizeCaf: CancelAnimationFrame; + private editorResize$: ResizeObserver; + private ignoreChange = false; + + toastTargetId = `protobufContentEditor-${guid()}`; + + @Input() label: string; + + contentType: ContentType = ContentType.TEXT; + + @Input() disabled: boolean; + + @Input() fillHeight: boolean; + + @Input() editorStyle: {[klass: string]: any}; + + @Input() tbPlaceholder: string; + + private readonlyValue: boolean; + get readonly(): boolean { + return this.readonlyValue; + } + @Input() + set readonly(value: boolean) { + this.readonlyValue = coerceBooleanProperty(value); + } + + fullscreen = false; + + contentBody: string; + + contentValid: boolean; + + errorShowed = false; + + private propagateChange = null; + + constructor(public elementRef: ElementRef, + protected store: Store, + private raf: RafService) { + } + + ngOnInit(): void { + const editorElement = this.protobufEditorElmRef.nativeElement; + let mode = 'protobuf'; + let editorOptions: Partial = { + mode: `ace/mode/${mode}`, + showGutter: true, + showPrintMargin: false, + readOnly: this.disabled || this.readonly, + }; + + const advancedOptions = { + enableSnippets: true, + enableBasicAutocompletion: true, + enableLiveAutocompletion: true, + autoScrollEditorIntoView: true + }; + + editorOptions = {...editorOptions, ...advancedOptions}; + getAce().subscribe( + (ace) => { + this.protobufEditor = ace.edit(editorElement, editorOptions); + this.protobufEditor.session.setUseWrapMode(true); + this.protobufEditor.setValue(this.contentBody ? this.contentBody : '', -1); + this.protobufEditor.setReadOnly(this.disabled || this.readonly); + this.protobufEditor.on('change', () => { + if (!this.ignoreChange) { + this.updateView(); + } + }); + this.editorResize$ = new ResizeObserver(() => { + this.onAceEditorResize(); + }); + this.editorResize$.observe(editorElement); + } + ); + } + + ngOnDestroy(): void { + if (this.editorResize$) { + this.editorResize$.disconnect(); + } + } + + private onAceEditorResize() { + if (this.editorsResizeCaf) { + this.editorsResizeCaf(); + this.editorsResizeCaf = null; + } + this.editorsResizeCaf = this.raf.raf(() => { + this.protobufEditor.resize(); + this.protobufEditor.renderer.updateFull(); + }); + } + + ngOnChanges(changes: SimpleChanges): void { + for (const propName of Object.keys(changes)) { + const change = changes[propName]; + if (!change.firstChange && change.currentValue !== change.previousValue) { + if (propName === 'contentType') { + if (this.protobufEditor) { + let mode = 'protobuf'; + this.protobufEditor.session.setMode(`ace/mode/${mode}`); + } + } + } + } + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (this.protobufEditor) { + this.protobufEditor.setReadOnly(this.disabled || this.readonly); + } + } + + public validate(c: FormControl) { + return (this.contentValid) ? null : { + contentBody: { + valid: false, + }, + }; + } + + writeValue(value: string): void { + this.contentBody = value; + this.contentValid = true; + if (this.protobufEditor) { + this.ignoreChange = true; + this.protobufEditor.setValue(this.contentBody ? this.contentBody : '', -1); + this.ignoreChange = false; + } + } + + updateView() { + const editorValue = this.protobufEditor.getValue(); + if (this.contentBody !== editorValue) { + this.contentBody = editorValue; + this.propagateChange(this.contentBody); + } + } + + beautifyJSON() { + beautifyJs(this.contentBody, {indent_size: 4, wrap_line_length: 60}).subscribe( + (res) => { + this.protobufEditor.setValue(res ? res : '', -1); + this.updateView(); + } + ); + } + + minifyJSON() { + const res = JSON.stringify(this.contentBody); + this.protobufEditor.setValue(res ? res : '', -1); + this.updateView(); + } + + onFullscreen() { + if (this.protobufEditor) { + setTimeout(() => { + this.protobufEditor.resize(); + }, 0); + } + } + +} diff --git a/ui-ngx/src/app/shared/models/ace/ace.models.ts b/ui-ngx/src/app/shared/models/ace/ace.models.ts index f87275e679..c9136b8e79 100644 --- a/ui-ngx/src/app/shared/models/ace/ace.models.ts +++ b/ui-ngx/src/app/shared/models/ace/ace.models.ts @@ -36,6 +36,7 @@ export function loadAceDependencies(): Observable { aceObservables.push(from(import('ace-builds/src-noconflict/mode-text'))); aceObservables.push(from(import('ace-builds/src-noconflict/mode-markdown'))); aceObservables.push(from(import('ace-builds/src-noconflict/mode-html'))); + aceObservables.push(from(import('ace-builds/src-noconflict/mode-protobuf'))); aceObservables.push(from(import('ace-builds/src-noconflict/snippets/java'))); aceObservables.push(from(import('ace-builds/src-noconflict/snippets/css'))); aceObservables.push(from(import('ace-builds/src-noconflict/snippets/json'))); diff --git a/ui-ngx/src/app/shared/shared.module.ts b/ui-ngx/src/app/shared/shared.module.ts index 2b1de653ec..90b80393dd 100644 --- a/ui-ngx/src/app/shared/shared.module.ts +++ b/ui-ngx/src/app/shared/shared.module.ts @@ -155,6 +155,7 @@ import { MarkedOptionsService } from '@shared/components/marked-options.service' import { TbPopoverService } from '@shared/components/popover.service'; import { HELP_MARKDOWN_COMPONENT_TOKEN, SHARED_MODULE_TOKEN } from '@shared/components/tokens'; import { TbMarkdownComponent } from '@shared/components/markdown.component'; +import { ProtobufContentComponent } from './components/protobuf-content.component'; export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) { return markedOptionsService; @@ -268,7 +269,8 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) OtaPackageAutocompleteComponent, WidgetsBundleSearchComponent, CopyButtonComponent, - TogglePasswordComponent + TogglePasswordComponent, + ProtobufContentComponent ], imports: [ CommonModule, @@ -458,7 +460,8 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) OtaPackageAutocompleteComponent, WidgetsBundleSearchComponent, CopyButtonComponent, - TogglePasswordComponent + TogglePasswordComponent, + ProtobufContentComponent ] }) export class SharedModule { } From 706ace25ad585a7294ecd30858153b94627d1970 Mon Sep 17 00:00:00 2001 From: Artem Babak Date: Tue, 26 Oct 2021 21:52:35 +0300 Subject: [PATCH 2/6] Protobuf editor implementation --- ...ile-transport-configuration.component.html | 20 +++---- ...ofile-transport-configuration.component.ts | 21 +++++++ .../protobuf-content.component.html | 6 +- .../protobuf-content.component.scss | 3 +- .../components/protobuf-content.component.ts | 58 +++++-------------- 5 files changed, 47 insertions(+), 61 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.html b/ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.html index 8e7d082ad2..f12af60efc 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.html @@ -74,9 +74,8 @@
-
+ {{ 'device-profile.telemetry-proto-schema-required' | translate}} -
-
+ + {{ 'device-profile.attributes-proto-schema-required' | translate}} -
-
+ + {{ 'device-profile.rpc-request-proto-required' | translate}} -
-
+ + {{ 'device-profile.rpc-response-proto-schema-required' | translate}} -
+
diff --git a/ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.ts index 5254b7574f..c5ecc8371a 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.ts @@ -56,6 +56,10 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control transportPayloadTypes = Object.keys(TransportPayloadType); + get protobufControls() { + return this.getProtobufControls(); + } + transportPayloadTypeTranslations = transportPayloadTypeTranslationMap; mqttDeviceProfileTransportConfigurationFormGroup: FormGroup; @@ -211,4 +215,21 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control } return null; } + + getProtobufControls() { + return [ + { + name: 'deviceAttributesProtoSchema', + label: 'device-profile.attributes-proto-schema', + errorControl: 'transportPayloadTypeConfiguration.deviceAttributesProtoSchema', + errorText: 'device-profile.attributes-proto-schema-required' + }, + { + deviceTelemetryProtoSchema: 'deviceRpcRequestProtoSchema', + label: 'device-profile.rpc-request-proto-schema', + errorControl: 'transportPayloadTypeConfiguration.deviceRpcRequestProtoSchema', + errorText: 'device-profile.rpc-request-proto-required' + } + ]; + } } diff --git a/ui-ngx/src/app/shared/components/protobuf-content.component.html b/ui-ngx/src/app/shared/components/protobuf-content.component.html index 43765c66d7..450c17e84b 100644 --- a/ui-ngx/src/app/shared/components/protobuf-content.component.html +++ b/ui-ngx/src/app/shared/components/protobuf-content.component.html @@ -5,13 +5,9 @@ -
= { - mode: `ace/mode/${mode}`, + mode: `ace/mode/protobuf`, showGutter: true, showPrintMargin: false, readOnly: this.disabled || this.readonly, @@ -100,8 +95,7 @@ export class ProtobufContentComponent implements OnInit, ControlValueAccessor, V const advancedOptions = { enableSnippets: true, enableBasicAutocompletion: true, - enableLiveAutocompletion: true, - autoScrollEditorIntoView: true + enableLiveAutocompletion: true }; editorOptions = {...editorOptions, ...advancedOptions}; @@ -130,31 +124,6 @@ export class ProtobufContentComponent implements OnInit, ControlValueAccessor, V } } - private onAceEditorResize() { - if (this.editorsResizeCaf) { - this.editorsResizeCaf(); - this.editorsResizeCaf = null; - } - this.editorsResizeCaf = this.raf.raf(() => { - this.protobufEditor.resize(); - this.protobufEditor.renderer.updateFull(); - }); - } - - ngOnChanges(changes: SimpleChanges): void { - for (const propName of Object.keys(changes)) { - const change = changes[propName]; - if (!change.firstChange && change.currentValue !== change.previousValue) { - if (propName === 'contentType') { - if (this.protobufEditor) { - let mode = 'protobuf'; - this.protobufEditor.session.setMode(`ace/mode/${mode}`); - } - } - } - } - } - registerOnChange(fn: any): void { this.propagateChange = fn; } @@ -169,7 +138,7 @@ export class ProtobufContentComponent implements OnInit, ControlValueAccessor, V } } - public validate(c: FormControl) { + validate(c: FormControl) { return (this.contentValid) ? null : { contentBody: { valid: false, @@ -195,7 +164,7 @@ export class ProtobufContentComponent implements OnInit, ControlValueAccessor, V } } - beautifyJSON() { + beautifyProtobuf() { beautifyJs(this.contentBody, {indent_size: 4, wrap_line_length: 60}).subscribe( (res) => { this.protobufEditor.setValue(res ? res : '', -1); @@ -204,12 +173,6 @@ export class ProtobufContentComponent implements OnInit, ControlValueAccessor, V ); } - minifyJSON() { - const res = JSON.stringify(this.contentBody); - this.protobufEditor.setValue(res ? res : '', -1); - this.updateView(); - } - onFullscreen() { if (this.protobufEditor) { setTimeout(() => { @@ -218,4 +181,15 @@ export class ProtobufContentComponent implements OnInit, ControlValueAccessor, V } } + private onAceEditorResize() { + if (this.editorsResizeCaf) { + this.editorsResizeCaf(); + this.editorsResizeCaf = null; + } + this.editorsResizeCaf = this.raf.raf(() => { + this.protobufEditor.resize(); + this.protobufEditor.renderer.updateFull(); + }); + } + } From bc488421a199f79caf8ac09b9fa51a5291cd743a Mon Sep 17 00:00:00 2001 From: Artem Babak Date: Tue, 26 Oct 2021 22:06:48 +0300 Subject: [PATCH 3/6] Protobuf component: removed validation --- ...ofile-transport-configuration.component.ts | 21 ------------------ .../protobuf-content.component.html | 2 +- .../components/protobuf-content.component.ts | 22 ++----------------- 3 files changed, 3 insertions(+), 42 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.ts index c5ecc8371a..5254b7574f 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.ts @@ -56,10 +56,6 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control transportPayloadTypes = Object.keys(TransportPayloadType); - get protobufControls() { - return this.getProtobufControls(); - } - transportPayloadTypeTranslations = transportPayloadTypeTranslationMap; mqttDeviceProfileTransportConfigurationFormGroup: FormGroup; @@ -215,21 +211,4 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control } return null; } - - getProtobufControls() { - return [ - { - name: 'deviceAttributesProtoSchema', - label: 'device-profile.attributes-proto-schema', - errorControl: 'transportPayloadTypeConfiguration.deviceAttributesProtoSchema', - errorText: 'device-profile.attributes-proto-schema-required' - }, - { - deviceTelemetryProtoSchema: 'deviceRpcRequestProtoSchema', - label: 'device-profile.rpc-request-proto-schema', - errorControl: 'transportPayloadTypeConfiguration.deviceRpcRequestProtoSchema', - errorText: 'device-profile.rpc-request-proto-required' - } - ]; - } } diff --git a/ui-ngx/src/app/shared/components/protobuf-content.component.html b/ui-ngx/src/app/shared/components/protobuf-content.component.html index 450c17e84b..b1ec6c8dd4 100644 --- a/ui-ngx/src/app/shared/components/protobuf-content.component.html +++ b/ui-ngx/src/app/shared/components/protobuf-content.component.html @@ -2,7 +2,7 @@ tb-fullscreen [fullscreen]="fullscreen" (fullscreenChanged)="onFullscreen()" fxLayout="column">
- +
diff --git a/ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.html b/ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.html index f12af60efc..d57783f17c 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.html @@ -104,7 +104,7 @@ [fillHeight]="true"> - {{ 'device-profile.rpc-request-proto-required' | translate}} + {{ 'device-profile.rpc-request-proto-schema-required' | translate}} diff --git a/ui-ngx/src/app/shared/models/ace/ace.models.ts b/ui-ngx/src/app/shared/models/ace/ace.models.ts index c9136b8e79..9a3c30766e 100644 --- a/ui-ngx/src/app/shared/models/ace/ace.models.ts +++ b/ui-ngx/src/app/shared/models/ace/ace.models.ts @@ -36,6 +36,7 @@ export function loadAceDependencies(): Observable { aceObservables.push(from(import('ace-builds/src-noconflict/mode-text'))); aceObservables.push(from(import('ace-builds/src-noconflict/mode-markdown'))); aceObservables.push(from(import('ace-builds/src-noconflict/mode-html'))); + aceObservables.push(from(import('ace-builds/src-noconflict/mode-c_cpp'))); aceObservables.push(from(import('ace-builds/src-noconflict/mode-protobuf'))); aceObservables.push(from(import('ace-builds/src-noconflict/snippets/java'))); aceObservables.push(from(import('ace-builds/src-noconflict/snippets/css'))); @@ -44,6 +45,8 @@ export function loadAceDependencies(): Observable { aceObservables.push(from(import('ace-builds/src-noconflict/snippets/text'))); aceObservables.push(from(import('ace-builds/src-noconflict/snippets/markdown'))); aceObservables.push(from(import('ace-builds/src-noconflict/snippets/html'))); + aceObservables.push(from(import('ace-builds/src-noconflict/snippets/c_cpp'))); + aceObservables.push(from(import('ace-builds/src-noconflict/snippets/protobuf'))); aceObservables.push(from(import('ace-builds/src-noconflict/theme-textmate'))); aceObservables.push(from(import('ace-builds/src-noconflict/theme-github'))); return forkJoin(aceObservables).pipe( From 3471a54f8bb89875792f6defb0f363ae97535631 Mon Sep 17 00:00:00 2001 From: deaflynx Date: Wed, 27 Oct 2021 11:23:06 +0300 Subject: [PATCH 5/6] Extend height of protobuf editor to 10 lines --- .../src/app/shared/components/protobuf-content.component.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-ngx/src/app/shared/components/protobuf-content.component.scss b/ui-ngx/src/app/shared/components/protobuf-content.component.scss index 0e44e2373d..b2ebc9bbe6 100644 --- a/ui-ngx/src/app/shared/components/protobuf-content.component.scss +++ b/ui-ngx/src/app/shared/components/protobuf-content.component.scss @@ -32,7 +32,7 @@ #tb-protobuf-input { width: 100%; min-width: 200px; - min-height: 100px; + min-height: 160px; height: 100%; &:not(.fill-height) { From 6f4d837a7e930f1aa43630cd1eadc0bb23281b59 Mon Sep 17 00:00:00 2001 From: Artem Babak Date: Thu, 28 Oct 2021 10:20:50 +0300 Subject: [PATCH 6/6] Added license header --- .../components/protobuf-content.component.html | 17 +++++++++++++++++ .../components/protobuf-content.component.scss | 15 +++++++++++++++ .../components/protobuf-content.component.ts | 16 ++++++++++++++++ 3 files changed, 48 insertions(+) diff --git a/ui-ngx/src/app/shared/components/protobuf-content.component.html b/ui-ngx/src/app/shared/components/protobuf-content.component.html index b1ec6c8dd4..23a16ece43 100644 --- a/ui-ngx/src/app/shared/components/protobuf-content.component.html +++ b/ui-ngx/src/app/shared/components/protobuf-content.component.html @@ -1,3 +1,20 @@ +
diff --git a/ui-ngx/src/app/shared/components/protobuf-content.component.scss b/ui-ngx/src/app/shared/components/protobuf-content.component.scss index b2ebc9bbe6..369d172491 100644 --- a/ui-ngx/src/app/shared/components/protobuf-content.component.scss +++ b/ui-ngx/src/app/shared/components/protobuf-content.component.scss @@ -1,3 +1,18 @@ +/** + * Copyright © 2016-2021 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. + */ :host { position: relative; diff --git a/ui-ngx/src/app/shared/components/protobuf-content.component.ts b/ui-ngx/src/app/shared/components/protobuf-content.component.ts index d3599fc6c3..0c1e7c7fac 100644 --- a/ui-ngx/src/app/shared/components/protobuf-content.component.ts +++ b/ui-ngx/src/app/shared/components/protobuf-content.component.ts @@ -1,3 +1,19 @@ +/// +/// Copyright © 2016-2021 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,