Commit 516ae3d8142c4c554cd20499bc6a2774300b48eb

Authored by Vladyslav
Committed by GitHub
1 parent 25ba8139

Create ui to support attribute type JSON (#2471)

@@ -168,7 +168,7 @@ @@ -168,7 +168,7 @@
168 class="tb-value-cell" 168 class="tb-value-cell"
169 (click)="editAttribute($event, attribute)"> 169 (click)="editAttribute($event, attribute)">
170 <div fxLayout="row"> 170 <div fxLayout="row">
171 - <span fxFlex>{{attribute.value}}</span> 171 + <span fxFlex>{{attribute.value | tbJson}}</span>
172 <span [fxShow]="!isClientSideTelemetryTypeMap.get(attributeScope)"> 172 <span [fxShow]="!isClientSideTelemetryTypeMap.get(attributeScope)">
173 <mat-icon>edit</mat-icon> 173 <mat-icon>edit</mat-icon>
174 </span> 174 </span>
  1 +<!--
  2 +
  3 + Copyright © 2016-2020 The Thingsboard Authors
  4 +
  5 + Licensed under the Apache License, Version 2.0 (the "License");
  6 + you may not use this file except in compliance with the License.
  7 + You may obtain a copy of the License at
  8 +
  9 + http://www.apache.org/licenses/LICENSE-2.0
  10 +
  11 + Unless required by applicable law or agreed to in writing, software
  12 + distributed under the License is distributed on an "AS IS" BASIS,
  13 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 + See the License for the specific language governing permissions and
  15 + limitations under the License.
  16 +
  17 +-->
  18 +<form [formGroup]="jsonFormGroup" (ngSubmit)="add()" style="min-width: 400px;">
  19 + <mat-toolbar fxLayout="row" color="primary">
  20 + <h2>{{ (this.data.title ? this.data.title : 'details.edit-json') | translate }}</h2>
  21 + <span fxFlex></span>
  22 + <button mat-button mat-icon-button
  23 + (click)="cancel()"
  24 + type="button">
  25 + <mat-icon class="material-icons">close</mat-icon>
  26 + </button>
  27 + </mat-toolbar>
  28 + <mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async">
  29 + </mat-progress-bar>
  30 + <div style="height: 4px;" *ngIf="!(isLoading$ | async)"></div>
  31 + <div mat-dialog-content>
  32 + <fieldset [disabled]="isLoading$ | async">
  33 + <tb-json-object-edit
  34 + formControlName="json"
  35 + label="{{ 'value.json-value' | translate }}"
  36 + validateContent="true"
  37 + [required]="true"
  38 + [fillHeight]="false">
  39 + </tb-json-object-edit>
  40 + </fieldset>
  41 + </div>
  42 + <div mat-dialog-actions fxLayout="row" fxLayoutAlign="end center">
  43 + <span fxFlex></span>
  44 + <button mat-button mat-raised-button color="primary"
  45 + type="submit"
  46 + [disabled]="(isLoading$ | async) || jsonFormGroup.invalid || !jsonFormGroup.dirty">
  47 + {{ 'action.save' | translate }}
  48 + </button>
  49 + <button mat-button color="primary"
  50 + style="margin-right: 20px;"
  51 + type="button"
  52 + [disabled]="(isLoading$ | async)"
  53 + (click)="cancel()" cdkFocusInitial>
  54 + {{ 'action.cancel' | translate }}
  55 + </button>
  56 + </div>
  57 +</form>
  1 +///
  2 +/// Copyright © 2016-2020 The Thingsboard Authors
  3 +///
  4 +/// Licensed under the Apache License, Version 2.0 (the "License");
  5 +/// you may not use this file except in compliance with the License.
  6 +/// You may obtain a copy of the License at
  7 +///
  8 +/// http://www.apache.org/licenses/LICENSE-2.0
  9 +///
  10 +/// Unless required by applicable law or agreed to in writing, software
  11 +/// distributed under the License is distributed on an "AS IS" BASIS,
  12 +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +/// See the License for the specific language governing permissions and
  14 +/// limitations under the License.
  15 +///
  16 +
  17 +import {Component, Inject, OnInit} from "@angular/core";
  18 +import {DialogComponent} from "@shared/components/dialog.component";
  19 +import {Store} from "@ngrx/store";
  20 +import {AppState} from "@core/core.state";
  21 +import {Router} from "@angular/router";
  22 +import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
  23 +import {FormBuilder, FormGroup} from "@angular/forms";
  24 +
  25 +export interface JsonObjectEdittDialogData {
  26 + jsonValue: Object;
  27 + title?: string;
  28 +}
  29 +
  30 +@Component({
  31 + selector: 'tb-object-edit-dialog',
  32 + templateUrl: './json-object-edit-dialog.component.html',
  33 + styleUrls: []
  34 +})
  35 +export class JsonObjectEditDialogComponent extends DialogComponent<JsonObjectEditDialogComponent, Object>
  36 + implements OnInit {
  37 +
  38 + jsonFormGroup: FormGroup;
  39 +
  40 + submitted = false;
  41 +
  42 + constructor(protected store: Store<AppState>,
  43 + protected router: Router,
  44 + @Inject(MAT_DIALOG_DATA) public data: JsonObjectEdittDialogData,
  45 + public dialogRef: MatDialogRef<JsonObjectEditDialogComponent, Object>,
  46 + public fb: FormBuilder) {
  47 + super(store, router, dialogRef);
  48 + }
  49 +
  50 + ngOnInit(): void {
  51 + this.jsonFormGroup = this.fb.group({
  52 + json: [this.data.jsonValue, []]
  53 + });
  54 + }
  55 +
  56 + cancel(): void {
  57 + this.dialogRef.close(undefined);
  58 + }
  59 +
  60 + add(): void {
  61 + this.dialogRef.close(this.jsonFormGroup.get('json').value);
  62 + }
  63 +}
  1 +///
  2 +/// Copyright © 2016-2020 The Thingsboard Authors
  3 +///
  4 +/// Licensed under the Apache License, Version 2.0 (the "License");
  5 +/// you may not use this file except in compliance with the License.
  6 +/// You may obtain a copy of the License at
  7 +///
  8 +/// http://www.apache.org/licenses/LICENSE-2.0
  9 +///
  10 +/// Unless required by applicable law or agreed to in writing, software
  11 +/// distributed under the License is distributed on an "AS IS" BASIS,
  12 +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +/// See the License for the specific language governing permissions and
  14 +/// limitations under the License.
  15 +///
  16 +
  17 +import {Directive, ElementRef, forwardRef, HostListener, Renderer2, SkipSelf} from "@angular/core";
  18 +import {
  19 + ControlValueAccessor,
  20 + FormControl, FormGroupDirective,
  21 + NG_VALIDATORS,
  22 + NG_VALUE_ACCESSOR, NgForm,
  23 + ValidationErrors,
  24 + Validator
  25 +} from "@angular/forms";
  26 +import {ErrorStateMatcher} from "@angular/material/core";
  27 +
  28 +@Directive({
  29 + selector: '[tb-json-to-string]',
  30 + providers: [{
  31 + provide: NG_VALUE_ACCESSOR,
  32 + useExisting: forwardRef(() => TbJsonToStringDirective),
  33 + multi: true
  34 + },
  35 + {
  36 + provide: NG_VALIDATORS,
  37 + useExisting: forwardRef(() => TbJsonToStringDirective),
  38 + multi: true,
  39 + },
  40 + {
  41 + provide: ErrorStateMatcher,
  42 + useExisting: TbJsonToStringDirective
  43 + }]
  44 +})
  45 +
  46 +export class TbJsonToStringDirective implements ControlValueAccessor, Validator, ErrorStateMatcher {
  47 + private propagateChange = null;
  48 + private parseError: boolean;
  49 + private data: any;
  50 +
  51 + @HostListener('input', ['$event.target.value']) input(newValue: any): void {
  52 + try {
  53 + this.data = JSON.parse(newValue);
  54 + this.parseError = false;
  55 + } catch (e) {
  56 + this.parseError = true;
  57 + }
  58 +
  59 + this.propagateChange(this.data);
  60 + }
  61 +
  62 + constructor(private render: Renderer2,
  63 + private element: ElementRef,
  64 + @SkipSelf() private errorStateMatcher: ErrorStateMatcher) {
  65 +
  66 + }
  67 +
  68 + isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
  69 + const originalErrorState = this.errorStateMatcher.isErrorState(control, form);
  70 + const customErrorState = !!(control && control.invalid && this.parseError);
  71 + return originalErrorState || customErrorState;
  72 + }
  73 +
  74 + validate(c: FormControl): ValidationErrors {
  75 + return (!this.parseError) ? null : {
  76 + invalidJSON: {
  77 + valid: false
  78 + }
  79 + };
  80 + }
  81 +
  82 + writeValue(obj: any): void {
  83 + if (obj) {
  84 + this.data = obj;
  85 + this.parseError = false;
  86 + this.render.setProperty(this.element.nativeElement, 'value', JSON.stringify(obj));
  87 + }
  88 + }
  89 +
  90 + registerOnChange(fn: any): void {
  91 + this.propagateChange = fn;
  92 + }
  93 +
  94 + registerOnTouched(fn: any): void {
  95 + }
  96 +}
@@ -22,9 +22,13 @@ @@ -22,9 +22,13 @@
22 <label class="tb-title no-padding">{{ label }}</label> 22 <label class="tb-title no-padding">{{ label }}</label>
23 <span fxFlex></span> 23 <span fxFlex></span>
24 <button type="button" 24 <button type="button"
25 - mat-button *ngIf="!readonly" class="tidy" (click)="beautifyJson()"> 25 + mat-button *ngIf="!readonly" class="tidy" (click)="beautifyJSON()">
26 {{'js-func.tidy' | translate }} 26 {{'js-func.tidy' | translate }}
27 </button> 27 </button>
  28 + <button type="button"
  29 + mat-button *ngIf="!readonly" class="tidy" (click)="minifyJSON()">
  30 + {{'js-func.mini' | translate }}
  31 + </button>
28 <button type='button' mat-button mat-icon-button (click)="fullscreen = !fullscreen" 32 <button type='button' mat-button mat-icon-button (click)="fullscreen = !fullscreen"
29 class="tb-mat-32" 33 class="tb-mat-32"
30 matTooltip="{{(fullscreen ? 'fullscreen.exit' : 'fullscreen.expand') | translate}}" 34 matTooltip="{{(fullscreen ? 'fullscreen.exit' : 'fullscreen.expand') | translate}}"
@@ -257,12 +257,18 @@ export class JsonContentComponent implements OnInit, ControlValueAccessor, Valid @@ -257,12 +257,18 @@ export class JsonContentComponent implements OnInit, ControlValueAccessor, Valid
257 } 257 }
258 } 258 }
259 259
260 - beautifyJson() { 260 + beautifyJSON() {
261 const res = js_beautify(this.contentBody, {indent_size: 4, wrap_line_length: 60}); 261 const res = js_beautify(this.contentBody, {indent_size: 4, wrap_line_length: 60});
262 this.jsonEditor.setValue(res ? res : '', -1); 262 this.jsonEditor.setValue(res ? res : '', -1);
263 this.updateView(); 263 this.updateView();
264 } 264 }
265 265
  266 + minifyJSON() {
  267 + const res = JSON.stringify(this.contentBody);
  268 + this.jsonEditor.setValue(res ? res : '', -1);
  269 + this.updateView();
  270 + }
  271 +
266 onFullscreen() { 272 onFullscreen() {
267 if (this.jsonEditor) { 273 if (this.jsonEditor) {
268 setTimeout(() => { 274 setTimeout(() => {
@@ -18,12 +18,20 @@ @@ -18,12 +18,20 @@
18 <div style="background: #fff;" [ngClass]="{'fill-height': fillHeight}" 18 <div style="background: #fff;" [ngClass]="{'fill-height': fillHeight}"
19 tb-fullscreen 19 tb-fullscreen
20 [fullscreen]="fullscreen" (fullscreenChanged)="onFullscreen()" fxLayout="column"> 20 [fullscreen]="fullscreen" (fullscreenChanged)="onFullscreen()" fxLayout="column">
21 - <div fxLayout="row" fxLayoutAlign="start center"> 21 + <div fxLayout="row" fxLayoutAlign="start center" class="tb-json-object-toolbar">
22 <label class="tb-title no-padding" 22 <label class="tb-title no-padding"
23 ng-class="{'tb-required': required, 23 ng-class="{'tb-required': required,
24 'tb-readonly': readonly, 24 'tb-readonly': readonly,
25 'tb-error': !objectValid}">{{ label }}</label> 25 'tb-error': !objectValid}">{{ label }}</label>
26 <span fxFlex></span> 26 <span fxFlex></span>
  27 + <button type="button"
  28 + mat-button *ngIf="!readonly" class="tidy" (click)="beautifyJSON()">
  29 + {{'js-func.tidy' | translate }}
  30 + </button>
  31 + <button type="button"
  32 + mat-button *ngIf="!readonly" class="tidy" (click)="minifyJSON()">
  33 + {{'js-func.mini' | translate }}
  34 + </button>
27 <button mat-button mat-icon-button (click)="fullscreen = !fullscreen" 35 <button mat-button mat-icon-button (click)="fullscreen = !fullscreen"
28 matTooltip="{{(fullscreen ? 'fullscreen.exit' : 'fullscreen.expand') | translate}}" 36 matTooltip="{{(fullscreen ? 'fullscreen.exit' : 'fullscreen.expand') | translate}}"
29 matTooltipPosition="above"> 37 matTooltipPosition="above">
@@ -21,6 +21,25 @@ @@ -21,6 +21,25 @@
21 } 21 }
22 } 22 }
23 23
  24 +.tb-json-object-toolbar {
  25 + button.mat-button, button.mat-icon-button, button.mat-icon-button.tb-mat-32 {
  26 + align-items: center;
  27 + vertical-align: middle;
  28 + min-width: 32px;
  29 + min-height: 15px;
  30 + padding: 4px;
  31 + margin: 0;
  32 + font-size: .8rem;
  33 + line-height: 15px;
  34 + color: #7b7b7b;
  35 + background: rgba(220, 220, 220, .35);
  36 +
  37 + &:not(:last-child) {
  38 + margin-right: 4px;
  39 + }
  40 + }
  41 +}
  42 +
24 .tb-json-object-panel { 43 .tb-json-object-panel {
25 height: 100%; 44 height: 100%;
26 margin-left: 15px; 45 margin-left: 15px;
@@ -195,6 +195,22 @@ export class JsonObjectEditComponent implements OnInit, ControlValueAccessor, Va @@ -195,6 +195,22 @@ export class JsonObjectEditComponent implements OnInit, ControlValueAccessor, Va
195 } 195 }
196 } 196 }
197 197
  198 + beautifyJSON() {
  199 + const res = JSON.stringify(this.modelValue, null, 2);
  200 + if (this.jsonEditor) {
  201 + this.jsonEditor.setValue(res ? res : '', -1);
  202 + }
  203 + this.updateView();
  204 + }
  205 +
  206 + minifyJSON() {
  207 + const res = JSON.stringify(this.modelValue);
  208 + if (this.jsonEditor) {
  209 + this.jsonEditor.setValue(res ? res : '', -1);
  210 + }
  211 + this.updateView();
  212 + }
  213 +
198 writeValue(value: any): void { 214 writeValue(value: any): void {
199 this.modelValue = value; 215 this.modelValue = value;
200 this.contentValue = ''; 216 this.contentValue = '';
@@ -59,5 +59,21 @@ @@ -59,5 +59,21 @@
59 {{ (modelValue ? 'value.true' : 'value.false') | translate }} 59 {{ (modelValue ? 'value.true' : 'value.false') | translate }}
60 </mat-checkbox> 60 </mat-checkbox>
61 </div> 61 </div>
  62 + <div fxLayout="row" fxLayoutAlign="center" fxFlex="60" *ngIf="valueType === valueTypeEnum.JSON" class="mat-block">
  63 + <mat-form-field fxFlex class="mat-block">
  64 + <mat-label translate>value.json-value</mat-label>
  65 + <input [disabled]="disabled" matInput tb-json-to-string required name="value" #value="ngModel"
  66 + [(ngModel)]="modelValue" (ngModelChange)="onValueChanged()"/>
  67 + <button matSuffix mat-button mat-icon-button (click)="openEditJSONDialog($event)">
  68 + <mat-icon>open_in_new</mat-icon>
  69 + </button>
  70 + <mat-error *ngIf="value.hasError('required')">
  71 + {{ (requiredText ? requiredText : 'value.json-value-required') | translate }}
  72 + </mat-error>
  73 + <mat-error *ngIf="value.hasError('invalidJSON')">
  74 + {{ 'value.json-value-invalid' | translate }}
  75 + </mat-error>
  76 + </mat-form-field>
  77 + </div>
62 </section> 78 </section>
63 </form> 79 </form>
@@ -17,6 +17,12 @@ @@ -17,6 +17,12 @@
17 import { Component, forwardRef, Input, OnInit, ViewChild } from '@angular/core'; 17 import { Component, forwardRef, Input, OnInit, ViewChild } from '@angular/core';
18 import { ControlValueAccessor, NG_VALUE_ACCESSOR, NgForm } from '@angular/forms'; 18 import { ControlValueAccessor, NG_VALUE_ACCESSOR, NgForm } from '@angular/forms';
19 import { ValueType, valueTypesMap } from '@shared/models/constants'; 19 import { ValueType, valueTypesMap } from '@shared/models/constants';
  20 +import { isObject } from "@core/utils";
  21 +import { MatDialog } from "@angular/material/dialog";
  22 +import {
  23 + JsonObjectEditDialogComponent,
  24 + JsonObjectEdittDialogData
  25 +} from "@shared/components/dialog/json-object-edit-dialog.component";
20 26
21 @Component({ 27 @Component({
22 selector: 'tb-value-input', 28 selector: 'tb-value-input',
@@ -50,13 +56,35 @@ export class ValueInputComponent implements OnInit, ControlValueAccessor { @@ -50,13 +56,35 @@ export class ValueInputComponent implements OnInit, ControlValueAccessor {
50 56
51 private propagateChange = null; 57 private propagateChange = null;
52 58
53 - constructor() { 59 + constructor(
  60 + public dialog: MatDialog,
  61 + ) {
54 62
55 } 63 }
56 64
57 ngOnInit(): void { 65 ngOnInit(): void {
58 } 66 }
59 67
  68 + openEditJSONDialog($event: Event) {
  69 + if ($event) {
  70 + $event.stopPropagation();
  71 + }
  72 + this.dialog.open<JsonObjectEditDialogComponent, JsonObjectEdittDialogData, Object>(JsonObjectEditDialogComponent, {
  73 + disableClose: true,
  74 + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
  75 + data: {
  76 + jsonValue: this.modelValue
  77 + }
  78 + }).afterClosed().subscribe(
  79 + (res) => {
  80 + if (res) {
  81 + this.modelValue = res;
  82 + this.inputForm.control.patchValue({'value': this.modelValue});
  83 + }
  84 + }
  85 + );
  86 + }
  87 +
60 registerOnChange(fn: any): void { 88 registerOnChange(fn: any): void {
61 this.propagateChange = fn; 89 this.propagateChange = fn;
62 } 90 }
@@ -78,6 +106,8 @@ export class ValueInputComponent implements OnInit, ControlValueAccessor { @@ -78,6 +106,8 @@ export class ValueInputComponent implements OnInit, ControlValueAccessor {
78 } else { 106 } else {
79 this.valueType = ValueType.DOUBLE; 107 this.valueType = ValueType.DOUBLE;
80 } 108 }
  109 + } else if (isObject(this.modelValue)) {
  110 + this.valueType = ValueType.JSON;
81 } else { 111 } else {
82 this.valueType = ValueType.STRING; 112 this.valueType = ValueType.STRING;
83 } 113 }
@@ -94,6 +124,8 @@ export class ValueInputComponent implements OnInit, ControlValueAccessor { @@ -94,6 +124,8 @@ export class ValueInputComponent implements OnInit, ControlValueAccessor {
94 onValueTypeChanged() { 124 onValueTypeChanged() {
95 if (this.valueType === ValueType.BOOLEAN) { 125 if (this.valueType === ValueType.BOOLEAN) {
96 this.modelValue = false; 126 this.modelValue = false;
  127 + } if (this.valueType === ValueType.JSON) {
  128 + this.modelValue = {};
97 } else { 129 } else {
98 this.modelValue = null; 130 this.modelValue = null;
99 } 131 }
@@ -121,7 +121,8 @@ export enum ValueType { @@ -121,7 +121,8 @@ export enum ValueType {
121 STRING = 'STRING', 121 STRING = 'STRING',
122 INTEGER = 'INTEGER', 122 INTEGER = 'INTEGER',
123 DOUBLE = 'DOUBLE', 123 DOUBLE = 'DOUBLE',
124 - BOOLEAN = 'BOOLEAN' 124 + BOOLEAN = 'BOOLEAN',
  125 + JSON = 'JSON'
125 } 126 }
126 127
127 export const valueTypesMap = new Map<ValueType, ValueTypeData>( 128 export const valueTypesMap = new Map<ValueType, ValueTypeData>(
@@ -153,6 +154,13 @@ export const valueTypesMap = new Map<ValueType, ValueTypeData>( @@ -153,6 +154,13 @@ export const valueTypesMap = new Map<ValueType, ValueTypeData>(
153 name: 'value.boolean', 154 name: 'value.boolean',
154 icon: 'mdi:checkbox-marked-outline' 155 icon: 'mdi:checkbox-marked-outline'
155 } 156 }
  157 + ],
  158 + [
  159 + ValueType.JSON,
  160 + {
  161 + name: 'value.json',
  162 + icon: 'mdi:json'
  163 + }
156 ] 164 ]
157 ] 165 ]
158 ); 166 );
  1 +///
  2 +/// Copyright © 2016-2020 The Thingsboard Authors
  3 +///
  4 +/// Licensed under the Apache License, Version 2.0 (the "License");
  5 +/// you may not use this file except in compliance with the License.
  6 +/// You may obtain a copy of the License at
  7 +///
  8 +/// http://www.apache.org/licenses/LICENSE-2.0
  9 +///
  10 +/// Unless required by applicable law or agreed to in writing, software
  11 +/// distributed under the License is distributed on an "AS IS" BASIS,
  12 +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +/// See the License for the specific language governing permissions and
  14 +/// limitations under the License.
  15 +///
  16 +
  17 +import {Pipe, PipeTransform} from '@angular/core';
  18 +import {isObject, isNumber} from "@core/utils";
  19 +
  20 +@Pipe({name: 'tbJson'})
  21 +export class TbJsonPipe implements PipeTransform {
  22 + transform(value: any): string {
  23 + if (isObject(value)) {
  24 + return JSON.stringify(value);
  25 + } else if (isNumber(value)) {
  26 + return value.toString();
  27 + }
  28 + return value;
  29 + }
  30 +}
@@ -104,6 +104,7 @@ import { TbErrorComponent } from '@shared/components/tb-error.component'; @@ -104,6 +104,7 @@ import { TbErrorComponent } from '@shared/components/tb-error.component';
104 import { EntityTypeListComponent } from '@shared/components/entity/entity-type-list.component'; 104 import { EntityTypeListComponent } from '@shared/components/entity/entity-type-list.component';
105 import { EntitySubTypeListComponent } from '@shared/components/entity/entity-subtype-list.component'; 105 import { EntitySubTypeListComponent } from '@shared/components/entity/entity-subtype-list.component';
106 import { TruncatePipe } from '@shared/pipe/truncate.pipe'; 106 import { TruncatePipe } from '@shared/pipe/truncate.pipe';
  107 +import { TbJsonPipe } from "@shared/pipe/tbJson.pipe";
107 import { ColorPickerDialogComponent } from '@shared/components/dialog/color-picker-dialog.component'; 108 import { ColorPickerDialogComponent } from '@shared/components/dialog/color-picker-dialog.component';
108 import { MatChipDraggableDirective } from '@shared/components/mat-chip-draggable.directive'; 109 import { MatChipDraggableDirective } from '@shared/components/mat-chip-draggable.directive';
109 import { ColorInputComponent } from '@shared/components/color-input.component'; 110 import { ColorInputComponent } from '@shared/components/color-input.component';
@@ -124,6 +125,8 @@ import { TbCheatSheetComponent } from '@shared/components/cheatsheet.component'; @@ -124,6 +125,8 @@ import { TbCheatSheetComponent } from '@shared/components/cheatsheet.component';
124 import { TbHotkeysDirective } from '@shared/components/hotkeys.directive'; 125 import { TbHotkeysDirective } from '@shared/components/hotkeys.directive';
125 import { NavTreeComponent } from '@shared/components/nav-tree.component'; 126 import { NavTreeComponent } from '@shared/components/nav-tree.component';
126 import { LedLightComponent } from '@shared/components/led-light.component'; 127 import { LedLightComponent } from '@shared/components/led-light.component';
  128 +import { TbJsonToStringDirective } from "@shared/components/directives/tb-json-to-string.directive";
  129 +import { JsonObjectEditDialogComponent } from "@shared/components/dialog/json-object-edit-dialog.component";
127 130
128 @NgModule({ 131 @NgModule({
129 providers: [ 132 providers: [
@@ -132,6 +135,7 @@ import { LedLightComponent } from '@shared/components/led-light.component'; @@ -132,6 +135,7 @@ import { LedLightComponent } from '@shared/components/led-light.component';
132 EnumToArrayPipe, 135 EnumToArrayPipe,
133 HighlightPipe, 136 HighlightPipe,
134 TruncatePipe, 137 TruncatePipe,
  138 + TbJsonPipe,
135 { 139 {
136 provide: FlowInjectionToken, 140 provide: FlowInjectionToken,
137 useValue: Flow 141 useValue: Flow
@@ -202,7 +206,10 @@ import { LedLightComponent } from '@shared/components/led-light.component'; @@ -202,7 +206,10 @@ import { LedLightComponent } from '@shared/components/led-light.component';
202 EnumToArrayPipe, 206 EnumToArrayPipe,
203 HighlightPipe, 207 HighlightPipe,
204 TruncatePipe, 208 TruncatePipe,
205 - KeyboardShortcutPipe 209 + TbJsonPipe,
  210 + KeyboardShortcutPipe,
  211 + TbJsonToStringDirective,
  212 + JsonObjectEditDialogComponent
206 ], 213 ],
207 imports: [ 214 imports: [
208 CommonModule, 215 CommonModule,
@@ -357,8 +364,10 @@ import { LedLightComponent } from '@shared/components/led-light.component'; @@ -357,8 +364,10 @@ import { LedLightComponent } from '@shared/components/led-light.component';
357 EnumToArrayPipe, 364 EnumToArrayPipe,
358 HighlightPipe, 365 HighlightPipe,
359 TruncatePipe, 366 TruncatePipe,
  367 + TbJsonPipe,
360 KeyboardShortcutPipe, 368 KeyboardShortcutPipe,
361 - TranslateModule 369 + TranslateModule,
  370 + JsonObjectEditDialogComponent
362 ] 371 ]
363 }) 372 })
364 export class SharedModule { } 373 export class SharedModule { }
@@ -626,6 +626,7 @@ @@ -626,6 +626,7 @@
626 "details": { 626 "details": {
627 "details": "Details", 627 "details": "Details",
628 "edit-mode": "Edit mode", 628 "edit-mode": "Edit mode",
  629 + "edit-json": "Edit JSON",
629 "toggle-edit-mode": "Toggle edit mode" 630 "toggle-edit-mode": "Toggle edit mode"
630 }, 631 },
631 "device": { 632 "device": {
@@ -1298,7 +1299,8 @@ @@ -1298,7 +1299,8 @@
1298 "js-func": { 1299 "js-func": {
1299 "no-return-error": "Function must return value!", 1300 "no-return-error": "Function must return value!",
1300 "return-type-mismatch": "Function must return value of '{{type}}' type!", 1301 "return-type-mismatch": "Function must return value of '{{type}}' type!",
1301 - "tidy": "Tidy" 1302 + "tidy": "Tidy",
  1303 + "mini": "Mini"
1302 }, 1304 },
1303 "key-val": { 1305 "key-val": {
1304 "key": "Key", 1306 "key": "Key",
@@ -1635,7 +1637,11 @@ @@ -1635,7 +1637,11 @@
1635 "boolean-value": "Boolean value", 1637 "boolean-value": "Boolean value",
1636 "false": "False", 1638 "false": "False",
1637 "true": "True", 1639 "true": "True",
1638 - "long": "Long" 1640 + "long": "Long",
  1641 + "json": "JSON",
  1642 + "json-value": "JSON value",
  1643 + "json-value-invalid": "JSON value has an invalid format",
  1644 + "json-value-required": "JSON value is required."
1639 }, 1645 },
1640 "widget": { 1646 "widget": {
1641 "widget-library": "Widgets Library", 1647 "widget-library": "Widgets Library",
@@ -607,6 +607,7 @@ @@ -607,6 +607,7 @@
607 }, 607 },
608 "details": { 608 "details": {
609 "edit-mode": "Режим редактирования", 609 "edit-mode": "Режим редактирования",
  610 + "edit-json": "Редактировать JSON",
610 "toggle-edit-mode": "Режим редактирования" 611 "toggle-edit-mode": "Режим редактирования"
611 }, 612 },
612 "device": { 613 "device": {
@@ -1191,8 +1192,7 @@ @@ -1191,8 +1192,7 @@
1191 }, 1192 },
1192 "js-func": { 1193 "js-func": {
1193 "no-return-error": "Функция должна возвращать значение!", 1194 "no-return-error": "Функция должна возвращать значение!",
1194 - "return-type-mismatch": "Функция должна возвращать значение типа '{{type}}'!",  
1195 - "tidy": "Tidy" 1195 + "return-type-mismatch": "Функция должна возвращать значение типа '{{type}}'!"
1196 }, 1196 },
1197 "key-val": { 1197 "key-val": {
1198 "key": "Ключ", 1198 "key": "Ключ",
@@ -724,6 +724,7 @@ @@ -724,6 +724,7 @@
724 "details": { 724 "details": {
725 "details": "Деталі", 725 "details": "Деталі",
726 "edit-mode": "Режим редагування", 726 "edit-mode": "Режим редагування",
  727 + "edit-json": "Редагувати JSON",
727 "toggle-edit-mode": "Перемкнути режим редагування" 728 "toggle-edit-mode": "Перемкнути режим редагування"
728 }, 729 },
729 "device": { 730 "device": {
@@ -1606,8 +1607,7 @@ @@ -1606,8 +1607,7 @@
1606 }, 1607 },
1607 "js-func": { 1608 "js-func": {
1608 "no-return-error": "Функція повинна повертати значення!", 1609 "no-return-error": "Функція повинна повертати значення!",
1609 - "return-type-mismatch": "Функція повинна повернути значення типу '{{type}}'!",  
1610 - "tidy": "Tidy" 1610 + "return-type-mismatch": "Функція повинна повернути значення типу '{{type}}'!"
1611 }, 1611 },
1612 "key-val": { 1612 "key-val": {
1613 "key": "Ключ", 1613 "key": "Ключ",