Browse Source

Direct access of core services from widget context. JavaScript editors code autocompletion and tooltips.

pull/1722/head
Igor Kulikov 6 years ago
parent
commit
6131000c8c
  1. 3
      ui-ngx/src/app/modules/home/components/widget/action/custom-action-pretty-editor.component.html
  2. 3
      ui-ngx/src/app/modules/home/components/widget/action/custom-action-pretty-editor.component.ts
  3. 3
      ui-ngx/src/app/modules/home/components/widget/action/custom-action-pretty-resources-tabs.component.html
  4. 3
      ui-ngx/src/app/modules/home/components/widget/action/custom-action-pretty-resources-tabs.component.ts
  5. 77
      ui-ngx/src/app/modules/home/components/widget/action/custom-action.models.ts
  6. 1
      ui-ngx/src/app/modules/home/components/widget/action/widget-action-dialog.component.html
  7. 3
      ui-ngx/src/app/modules/home/components/widget/action/widget-action-dialog.component.ts
  8. 30
      ui-ngx/src/app/modules/home/components/widget/dynamic-widget.component.ts
  9. 8
      ui-ngx/src/app/modules/home/models/services.map.ts
  10. 30
      ui-ngx/src/app/modules/home/models/widget-component.models.ts
  11. 2
      ui-ngx/src/app/modules/home/pages/widget/widget-editor.component.ts
  12. 93
      ui-ngx/src/app/modules/home/pages/widget/widget-editor.models.ts
  13. 7
      ui-ngx/src/app/shared/components/js-func.component.ts
  14. 215
      ui-ngx/src/app/shared/models/ace/completion.models.ts
  15. 346
      ui-ngx/src/app/shared/models/ace/service-completion.models.ts
  16. 615
      ui-ngx/src/app/shared/models/ace/widget-completion.models.ts
  17. 52
      ui-ngx/src/styles.scss

3
ui-ngx/src/app/modules/home/components/widget/action/custom-action-pretty-editor.component.html

@ -44,7 +44,8 @@
(ngModelChange)="onActionUpdated()" (ngModelChange)="onActionUpdated()"
[fillHeight]="true" [fillHeight]="true"
[functionArgs]="['$event', 'widgetContext', 'entityId', 'entityName', 'htmlTemplate', 'additionalParams', 'entityLabel']" [functionArgs]="['$event', 'widgetContext', 'entityId', 'entityName', 'htmlTemplate', 'additionalParams', 'entityLabel']"
[validationArgs]="[]"> [validationArgs]="[]"
[editorCompleter]="customPrettyActionEditorCompleter">
</tb-js-func> </tb-js-func>
</div> </div>
</div> </div>

3
ui-ngx/src/app/modules/home/components/widget/action/custom-action-pretty-editor.component.ts

@ -35,6 +35,7 @@ import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { combineLatest } from 'rxjs'; import { combineLatest } from 'rxjs';
import { CustomActionDescriptor } from '@shared/models/widget.models'; import { CustomActionDescriptor } from '@shared/models/widget.models';
import { CustomPrettyActionEditorCompleter } from '@home/components/widget/action/custom-action.models';
@Component({ @Component({
selector: 'tb-custom-action-pretty-editor', selector: 'tb-custom-action-pretty-editor',
@ -63,6 +64,8 @@ export class CustomActionPrettyEditorComponent extends PageComponent implements
@ViewChildren('rightPanel') @ViewChildren('rightPanel')
rightPanelElmRef: QueryList<ElementRef<HTMLElement>>; rightPanelElmRef: QueryList<ElementRef<HTMLElement>>;
customPrettyActionEditorCompleter = CustomPrettyActionEditorCompleter;
private propagateChange = (_: any) => {}; private propagateChange = (_: any) => {};
constructor(protected store: Store<AppState>) { constructor(protected store: Store<AppState>) {

3
ui-ngx/src/app/modules/home/components/widget/action/custom-action-pretty-resources-tabs.component.html

@ -94,7 +94,8 @@
[(ngModel)]="action.customFunction" [(ngModel)]="action.customFunction"
(ngModelChange)="notifyActionUpdated()" (ngModelChange)="notifyActionUpdated()"
[functionArgs]="['$event', 'widgetContext', 'entityId', 'entityName', 'htmlTemplate', 'additionalParams', 'entityLabel']" [functionArgs]="['$event', 'widgetContext', 'entityId', 'entityName', 'htmlTemplate', 'additionalParams', 'entityLabel']"
[validationArgs]="[]"> [validationArgs]="[]"
[editorCompleter]="customPrettyActionEditorCompleter">
</tb-js-func> </tb-js-func>
</mat-tab> </mat-tab>
</mat-tab-group> </mat-tab-group>

3
ui-ngx/src/app/modules/home/components/widget/action/custom-action-pretty-resources-tabs.component.ts

@ -36,6 +36,7 @@ import * as ace from 'ace-builds';
import { CancelAnimationFrame, RafService } from '@core/services/raf.service'; import { CancelAnimationFrame, RafService } from '@core/services/raf.service';
import { css_beautify, html_beautify } from 'js-beautify'; import { css_beautify, html_beautify } from 'js-beautify';
import { ResizeObserver } from '@juggle/resize-observer'; import { ResizeObserver } from '@juggle/resize-observer';
import { CustomPrettyActionEditorCompleter } from '@home/components/widget/action/custom-action.models';
@Component({ @Component({
selector: 'tb-custom-action-pretty-resources-tabs', selector: 'tb-custom-action-pretty-resources-tabs',
@ -70,6 +71,8 @@ export class CustomActionPrettyResourcesTabsComponent extends PageComponent impl
cssEditor: ace.Ace.Editor; cssEditor: ace.Ace.Editor;
setValuesPending = false; setValuesPending = false;
customPrettyActionEditorCompleter = CustomPrettyActionEditorCompleter;
constructor(protected store: Store<AppState>, constructor(protected store: Store<AppState>,
private translate: TranslateService, private translate: TranslateService,
private raf: RafService) { private raf: RafService) {

77
ui-ngx/src/app/modules/home/components/widget/action/custom-action.models.ts

@ -0,0 +1,77 @@
///
/// 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 { TbEditorCompleter, TbEditorCompletions } from '@shared/models/ace/completion.models';
import { widgetContextCompletions } from '@shared/models/ace/widget-completion.models';
import { entityIdHref, entityTypeHref, serviceCompletions } from '@shared/models/ace/service-completion.models';
const customActionCompletions: TbEditorCompletions = {
...{
$event: {
meta: 'argument',
type: 'Event',
description: 'The DOM event that triggered this action.'
},
widgetContext: widgetContextCompletions.ctx,
entityId: {
meta: 'argument',
type: entityIdHref,
description: 'Id of the entity for which the action was triggered.',
children: {
id: {
meta: 'property',
type: 'string',
description: 'UUID Id string'
},
entityType: {
meta: 'property',
type: entityTypeHref,
description: 'Entity type'
}
}
},
entityName: {
meta: 'argument',
type: 'string',
description: 'Name of the entity for which the action was triggered.'
},
additionalParams: {
meta: 'argument',
type: 'object',
description: 'Optional object holding additional information.'
},
entityLabel: {
meta: 'argument',
type: 'string',
description: 'Label of the entity for which the action was triggered.'
}
},
...serviceCompletions
};
const customPrettyActionCompletions: TbEditorCompletions = {
...{
htmlTemplate: {
meta: 'argument',
type: 'string',
description: 'HTML template used to render custom dialog.'
}
},
...customActionCompletions
}
export const CustomActionEditorCompleter = new TbEditorCompleter(customActionCompletions);
export const CustomPrettyActionEditorCompleter = new TbEditorCompleter(customPrettyActionCompletions);

1
ui-ngx/src/app/modules/home/components/widget/action/widget-action-dialog.component.html

@ -135,6 +135,7 @@
formControlName="customFunction" formControlName="customFunction"
[functionArgs]="['$event', 'widgetContext', 'entityId', 'entityName', 'additionalParams', 'entityLabel']" [functionArgs]="['$event', 'widgetContext', 'entityId', 'entityName', 'additionalParams', 'entityLabel']"
[validationArgs]="[]" [validationArgs]="[]"
[editorCompleter]="customActionEditorCompleter"
></tb-js-func> ></tb-js-func>
</ng-template> </ng-template>
<ng-template [ngSwitchCase]="widgetActionType.customPretty"> <ng-template [ngSwitchCase]="widgetActionType.customPretty">

3
ui-ngx/src/app/modules/home/components/widget/action/widget-action-dialog.component.ts

@ -43,6 +43,7 @@ import { map, mergeMap, startWith, tap } from 'rxjs/operators';
import { DashboardService } from '@core/http/dashboard.service'; import { DashboardService } from '@core/http/dashboard.service';
import { Dashboard } from '@shared/models/dashboard.models'; import { Dashboard } from '@shared/models/dashboard.models';
import { DashboardUtilsService } from '@core/services/dashboard-utils.service'; import { DashboardUtilsService } from '@core/services/dashboard-utils.service';
import { CustomActionEditorCompleter } from '@home/components/widget/action/custom-action.models';
export interface WidgetActionDialogData { export interface WidgetActionDialogData {
isAdd: boolean; isAdd: boolean;
@ -76,6 +77,8 @@ export class WidgetActionDialogComponent extends DialogComponent<WidgetActionDia
targetDashboardStateSearchText = ''; targetDashboardStateSearchText = '';
selectedDashboardStateIds: Observable<Array<string>>; selectedDashboardStateIds: Observable<Array<string>>;
customActionEditorCompleter = CustomActionEditorCompleter;
submitted = false; submitted = false;
constructor(protected store: Store<AppState>, constructor(protected store: Store<AppState>,

30
ui-ngx/src/app/modules/home/components/widget/dynamic-widget.component.ts

@ -19,7 +19,7 @@ import { Inject, Injector, OnDestroy, OnInit } from '@angular/core';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { IDynamicWidgetComponent, WidgetContext } from '@home/models/widget-component.models'; import { IDynamicWidgetComponent, WidgetContext } from '@home/models/widget-component.models';
import { HttpErrorResponse } from '@angular/common/http'; import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { RafService } from '@core/services/raf.service'; import { RafService } from '@core/services/raf.service';
import { import {
NotificationHorizontalPosition, NotificationHorizontalPosition,
@ -27,6 +27,19 @@ import {
NotificationVerticalPosition NotificationVerticalPosition
} from '@core/notification/notification.models'; } from '@core/notification/notification.models';
import { FormBuilder, Validators } from '@angular/forms'; import { FormBuilder, Validators } from '@angular/forms';
import { DeviceService } from '@core/http/device.service';
import { AssetService } from '@core/http/asset.service';
import { EntityViewService } from '@core/http/entity-view.service';
import { CustomerService } from '@core/http/customer.service';
import { DashboardService } from '@core/http/dashboard.service';
import { UserService } from '@core/http/user.service';
import { AttributeService } from '@core/http/attribute.service';
import { EntityRelationService } from '@core/http/entity-relation.service';
import { EntityService } from '@core/http/entity.service';
import { DialogService } from '@core/services/dialog.service';
import { CustomDialogService } from '@home/components/widget/dialog/custom-dialog.service';
import { DatePipe } from '@angular/common';
import { TranslateService } from '@ngx-translate/core';
export class DynamicWidgetComponent extends PageComponent implements IDynamicWidgetComponent, OnInit, OnDestroy { export class DynamicWidgetComponent extends PageComponent implements IDynamicWidgetComponent, OnInit, OnDestroy {
@ -47,6 +60,21 @@ export class DynamicWidgetComponent extends PageComponent implements IDynamicWid
@Inject('errorMessages') public readonly errorMessages: string[]) { @Inject('errorMessages') public readonly errorMessages: string[]) {
super(store); super(store);
this.ctx.$injector = $injector; this.ctx.$injector = $injector;
this.ctx.deviceService = $injector.get(DeviceService);
this.ctx.assetService = $injector.get(AssetService);
this.ctx.entityViewService = $injector.get(EntityViewService);
this.ctx.customerService = $injector.get(CustomerService);
this.ctx.dashboardService = $injector.get(DashboardService);
this.ctx.userService = $injector.get(UserService);
this.ctx.attributeService = $injector.get(AttributeService);
this.ctx.entityRelationService = $injector.get(EntityRelationService);
this.ctx.entityService = $injector.get(EntityService);
this.ctx.dialogs = $injector.get(DialogService);
this.ctx.customDialog = $injector.get(CustomDialogService);
this.ctx.date = $injector.get(DatePipe);
this.ctx.translate = $injector.get(TranslateService);
this.ctx.http = $injector.get(HttpClient);
this.ctx.$scope = this; this.ctx.$scope = this;
if (this.ctx.defaultSubscription) { if (this.ctx.defaultSubscription) {
this.executingRpcRequest = this.ctx.defaultSubscription.executingRpcRequest; this.executingRpcRequest = this.ctx.defaultSubscription.executingRpcRequest;

8
ui-ngx/src/app/modules/home/models/services.map.ts

@ -26,11 +26,19 @@ import { DatePipe } from '@angular/common';
import { UtilsService } from '@core/services/utils.service'; import { UtilsService } from '@core/services/utils.service';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { EntityViewService } from '@core/http/entity-view.service';
import { CustomerService } from '@core/http/customer.service';
import { DashboardService } from '@core/http/dashboard.service';
import { UserService } from '@core/http/user.service';
export const ServicesMap = new Map<string, Type<any>>( export const ServicesMap = new Map<string, Type<any>>(
[ [
['deviceService', DeviceService], ['deviceService', DeviceService],
['assetService', AssetService], ['assetService', AssetService],
['entityViewService', EntityViewService],
['customerService', CustomerService],
['dashboardService', DashboardService],
['userService', UserService],
['attributeService', AttributeService], ['attributeService', AttributeService],
['entityRelationService', EntityRelationService], ['entityRelationService', EntityRelationService],
['entityService', EntityService], ['entityService', EntityService],

30
ui-ngx/src/app/modules/home/models/widget-component.models.ts

@ -43,7 +43,7 @@ import {
WidgetSubscriptionApi WidgetSubscriptionApi
} from '@core/api/widget-api.models'; } from '@core/api/widget-api.models';
import { ChangeDetectorRef, ComponentFactory, Injector, NgZone, Type } from '@angular/core'; import { ChangeDetectorRef, ComponentFactory, Injector, NgZone, Type } from '@angular/core';
import { HttpErrorResponse } from '@angular/common/http'; import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { RafService } from '@core/services/raf.service'; import { RafService } from '@core/services/raf.service';
import { WidgetTypeId } from '@shared/models/id/widget-type-id'; import { WidgetTypeId } from '@shared/models/id/widget-type-id';
import { TenantId } from '@shared/models/id/tenant-id'; import { TenantId } from '@shared/models/id/tenant-id';
@ -60,6 +60,19 @@ import {
import { ActionNotificationShow } from '@core/notification/notification.actions'; import { ActionNotificationShow } from '@core/notification/notification.actions';
import { AuthUser } from '@shared/models/user.model'; import { AuthUser } from '@shared/models/user.model';
import { getCurrentAuthUser } from '@core/auth/auth.selectors'; import { getCurrentAuthUser } from '@core/auth/auth.selectors';
import { DeviceService } from '@core/http/device.service';
import { AssetService } from '@core/http/asset.service';
import { EntityViewService } from '@core/http/entity-view.service';
import { CustomerService } from '@core/http/customer.service';
import { DashboardService } from '@core/http/dashboard.service';
import { UserService } from '@core/http/user.service';
import { AttributeService } from '@core/http/attribute.service';
import { EntityRelationService } from '@core/http/entity-relation.service';
import { EntityService } from '@core/http/entity.service';
import { DialogService } from '@core/services/dialog.service';
import { CustomDialogService } from '@home/components/widget/dialog/custom-dialog.service';
import { DatePipe } from '@angular/common';
import { TranslateService } from '@ngx-translate/core';
export interface IWidgetAction { export interface IWidgetAction {
name: string; name: string;
@ -126,6 +139,21 @@ export class WidgetContext {
} }
} }
deviceService: DeviceService;
assetService: AssetService;
entityViewService: EntityViewService;
customerService: CustomerService;
dashboardService: DashboardService;
userService: UserService;
attributeService: AttributeService;
entityRelationService: EntityRelationService;
entityService: EntityService;
dialogs: DialogService;
customDialog: CustomDialogService;
date: DatePipe;
translate: TranslateService;
http: HttpClient;
private changeDetectorValue: ChangeDetectorRef; private changeDetectorValue: ChangeDetectorRef;
inited = false; inited = false;

2
ui-ngx/src/app/modules/home/pages/widget/widget-editor.component.ts

@ -47,6 +47,7 @@ import {
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { ResizeObserver } from '@juggle/resize-observer'; import { ResizeObserver } from '@juggle/resize-observer';
import Timeout = NodeJS.Timeout; import Timeout = NodeJS.Timeout;
import { widgetEditorCompleter } from '@home/pages/widget/widget-editor.models';
// @dynamic // @dynamic
@Component({ @Component({
@ -319,6 +320,7 @@ export class WidgetEditorComponent extends PageComponent implements OnInit, OnDe
this.jsEditor.on('change', () => { this.jsEditor.on('change', () => {
this.cleanupJsErrors(); this.cleanupJsErrors();
}); });
this.jsEditor.completers = [widgetEditorCompleter, ...(this.jsEditor.completers || [])];
this.setAceEditorValues(); this.setAceEditorValues();
} }

93
ui-ngx/src/app/modules/home/pages/widget/widget-editor.models.ts

@ -0,0 +1,93 @@
///
/// 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 { TbEditorCompleter, TbEditorCompletions } from '@shared/models/ace/completion.models';
import { widgetContextCompletions } from '@shared/models/ace/widget-completion.models';
import { serviceCompletions } from '@shared/models/ace/service-completion.models';
const widgetEditorCompletions: TbEditorCompletions = {
... {self: {
description: 'Built-in variable <b>self</b> that is a reference to the widget instance',
type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/modules/home/models/widget-component.models.ts#L350">WidgetTypeInstance</a>',
meta: 'object',
children: {
...{
onInit: {
description: 'The first function which is called when widget is ready for initialization.<br>Should be used to prepare widget DOM, process widget settings and initial subscription information.',
meta: 'function'
},
onDataUpdated: {
description: 'Called when the new data is available from the widget subscription.<br>Latest data can be accessed from ' +
'the <code>defaultSubscription</code> property of <a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/modules/home/models/widget-component.models.ts#L83">widget context (<code>ctx</code>)</a>.',
meta: 'function'
},
onResize: {
description: 'Called when widget container is resized. Latest <code>width</code> and <code>height</code> can be obtained from <a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/modules/home/models/widget-component.models.ts#L83">widget context (<code>ctx</code>)</a>.',
meta: 'function'
},
onEditModeChanged: {
description: 'Called when dashboard editing mode is changed. Latest mode is handled by <code>isEdit</code> property of <a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/modules/home/models/widget-component.models.ts#L83">widget context (<code>ctx</code>)</a>.',
meta: 'function'
},
onMobileModeChanged: {
description: 'Called when dashboard view width crosses mobile breakpoint. Latest state is handled by <code>isMobile</code> property of <a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/modules/home/models/widget-component.models.ts#L83">widget context (<code>ctx</code>)</a>.',
meta: 'function'
},
onDestroy: {
description: 'Called when widget element is destroyed. Should be used to cleanup all resources if necessary.',
meta: 'function'
},
getSettingsSchema: {
description: 'Optional function returning widget settings schema json as alternative to <b>Settings tab</b> of <a href="https://thingsboard.io/docs/user-guide/contribution/widgets-development/#settings-schema-section">Settings schema section</a>.',
meta: 'function',
return: {
description: 'An widget settings schema json',
type: 'object'
}
},
getDataKeySettingsSchema: {
description: 'Optional function returning particular data key settings schema json as alternative to <b>Data key settings schema</b> of <a href="https://thingsboard.io/docs/user-guide/contribution/widgets-development/#settings-schema-section">Settings schema section</a>.',
meta: 'function',
return: {
description: 'A particular data key settings schema json',
type: 'object'
}
},
typeParameters: {
description: 'Returns object describing widget datasource parameters.',
meta: 'function',
return: {
description: 'An object describing widget datasource parameters.',
type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/widget.models.ts#L146">WidgetTypeParameters</a>'
}
},
actionSources: {
description: 'Returns map describing available widget action sources used to define user actions.',
meta: 'function',
return: {
description: 'A map of action sources by action source id.',
type: '{[actionSourceId: string]: <a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/widget.models.ts#L118">WidgetActionSource</a>}'
}
}
},
...widgetContextCompletions
}
}},
...widgetContextCompletions,
...serviceCompletions
};
export const widgetEditorCompleter = new TbEditorCompleter(widgetEditorCompletions);

7
ui-ngx/src/app/shared/components/js-func.component.ts

@ -35,6 +35,8 @@ import { guid, isUndefined } from '@app/core/utils';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { CancelAnimationFrame, RafService } from '@core/services/raf.service'; import { CancelAnimationFrame, RafService } from '@core/services/raf.service';
import { ResizeObserver } from '@juggle/resize-observer'; import { ResizeObserver } from '@juggle/resize-observer';
import { TbEditorCompleter } from '@shared/models/ace/completion.models';
import { widgetEditorCompleter } from '@home/pages/widget/widget-editor.models';
@Component({ @Component({
selector: 'tb-js-func', selector: 'tb-js-func',
@ -77,6 +79,8 @@ export class JsFuncComponent implements OnInit, OnDestroy, ControlValueAccessor,
@Input() fillHeight: boolean; @Input() fillHeight: boolean;
@Input() editorCompleter: TbEditorCompleter;
private noValidateValue: boolean; private noValidateValue: boolean;
get noValidate(): boolean { get noValidate(): boolean {
return this.noValidateValue; return this.noValidateValue;
@ -153,6 +157,9 @@ export class JsFuncComponent implements OnInit, OnDestroy, ControlValueAccessor,
this.cleanupJsErrors(); this.cleanupJsErrors();
this.updateView(); this.updateView();
}); });
if (this.editorCompleter) {
this.jsEditor.completers = [this.editorCompleter, ...(this.jsEditor.completers || [])];
}
this.editorResize$ = new ResizeObserver(() => { this.editorResize$ = new ResizeObserver(() => {
this.onAceEditorResize(); this.onAceEditorResize();
}); });

215
ui-ngx/src/app/shared/models/ace/completion.models.ts

@ -0,0 +1,215 @@
///
/// 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 * as ace from 'ace-builds';
export type tbMetaType = 'object' | 'function' | 'service' | 'property' | 'argument';
export type TbEditorCompletions = {[name: string]: TbEditorCompletion};
export interface FunctionArgType {
type?: string;
description?: string;
}
export interface FunctionArg extends FunctionArgType {
name: string;
type?: string;
description?: string;
optional?: boolean;
}
export interface TbEditorCompletion {
meta: tbMetaType;
description?: string;
type?: string;
args?: FunctionArg[];
return?: FunctionArgType;
children?: TbEditorCompletions;
}
interface TbEditorAceCompletion extends ace.Ace.Completion {
isTbEditorAceCompletion: true;
snippet: string;
description?: string;
type?: string;
args?: FunctionArg[];
return?: FunctionArgType;
}
export class TbEditorCompleter implements ace.Ace.Completer {
identifierRegexps: RegExp[] = [
/[a-zA-Z_0-9\$\-\u00A2-\u2000\u2070-\uFFFF.]/
];
constructor(private editorCompletions: TbEditorCompletions) {
}
getCompletions(editor: ace.Ace.Editor, session: ace.Ace.EditSession,
position: ace.Ace.Point, prefix: string, callback: ace.Ace.CompleterCallback): void {
const result = this.prepareCompletions(prefix);
if (result) {
callback(null, result);
}
}
private resolvePath(prefix: string): string[] {
if (!prefix || !prefix.length) {
return [];
}
let parts = prefix.split('.');
if (parts.length) {
parts.pop();
} else {
parts = [];
}
let currentCompletions = this.editorCompletions;
for (const part of parts) {
if (currentCompletions[part]) {
currentCompletions = currentCompletions[part].children;
if (!currentCompletions) {
return null;
}
} else {
return null;
}
}
return parts;
}
private prepareCompletions(prefix: string): ace.Ace.Completion[] {
const path = this.resolvePath(prefix);
if (path !== null) {
return this.toAceCompletionsList(this.editorCompletions, path);
} else {
return [];
}
}
private toAceCompletionsList(completions: TbEditorCompletions, parentPath: string[]): ace.Ace.Completion[] {
const result: ace.Ace.Completion[] = [];
let targetCompletions = completions;
let parentPrefix = '';
if (parentPath.length) {
parentPrefix = parentPath.join('.') + '.';
for (const path of parentPath) {
targetCompletions = targetCompletions[path].children;
}
}
for (const key of Object.keys(targetCompletions)) {
result.push(this.toAceCompletion(key, targetCompletions[key], parentPrefix));
}
return result;
}
private toAceCompletion(name: string, completion: TbEditorCompletion, parentPrefix: string): ace.Ace.Completion {
const aceCompletion: TbEditorAceCompletion = {
isTbEditorAceCompletion: true,
snippet: parentPrefix + name,
name,
caption: parentPrefix + name,
score: 100000,
value: parentPrefix + name,
meta: completion.meta,
type: completion.type,
description: completion.description,
args: completion.args,
return: completion.return
};
return aceCompletion;
}
getDocTooltip(completion: TbEditorAceCompletion) {
if (completion && completion.isTbEditorAceCompletion) {
return {
docHTML: this.createDocHTML(completion)
};
}
}
private createDocHTML(completion: TbEditorAceCompletion): string {
let title = `<b>${completion.name}</b>`;
if (completion.meta === 'function') {
title += '(';
if (completion.args) {
const strArgs: string[] = [];
for (const arg of completion.args) {
let strArg = `${arg.name}`;
if (arg.optional) {
strArg += '?';
}
if (arg.type) {
strArg += `: ${arg.type}`;
}
strArgs.push(strArg);
}
title += strArgs.join(', ');
}
title += '): ';
if (completion.return) {
title += completion.return.type;
} else {
title += 'void';
}
} else {
title += `: ${completion.type ? completion.type : completion.meta}`;
}
let html = `<div class="tb-ace-doc-tooltip"><code class="title">${title}</code>`;
if (completion.description) {
html += `<hr><div>${completion.description}</div>`;
}
if (completion.args || completion.return) {
let functionInfoBlock = '<div class="tb-function-info">';
if (completion.args) {
functionInfoBlock += '<div class="tb-api-title">Parameters</div>'
let argsTable = '<table class="tb-api-table"><tbody>';
const strArgs: string[] = [];
for (const arg of completion.args) {
let strArg = `<tr><td class="arg-name"><code>${arg.name}`;
if (arg.optional) {
strArg += ' (optional)';
}
strArg += '</code></td><td>';
if (arg.type) {
strArg += `<code>${arg.type}</code>`
}
strArg += '</td><td class="arg-description">';
if (arg.description) {
strArg += `${arg.description}`;
}
strArg += '</td></tr>';
strArgs.push(strArg);
}
argsTable += strArgs.join('') + '</tbody></table>';
functionInfoBlock += argsTable;
}
if (completion.return) {
let returnStr = '<div class="tb-api-title">Returns</div>';
returnStr += `<div class="tb-function-return"><code>${completion.return.type}</code>`;
if (completion.return.description) {
returnStr += `: ${completion.return.description}`;
}
returnStr += '</div>';
functionInfoBlock += returnStr;
}
functionInfoBlock += '</div>';
html += functionInfoBlock;
}
html += '</div>';
return html;
}
}

346
ui-ngx/src/app/shared/models/ace/service-completion.models.ts

@ -0,0 +1,346 @@
///
/// 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 { FunctionArg, FunctionArgType, TbEditorCompletions } from '@shared/models/ace/completion.models';
export const entityIdHref = '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/id/entity-id.ts#L20">EntityId</a>';
export const entityTypeHref = '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/entity-type.models.ts#L36">EntityType</a>';
export const pageDataHref = '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/page/page-data.ts#L17">PageData</a>';
export const deviceInfoHref = '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/device.models.ts#L33">DeviceInfo</a>';
export const deviceHref = '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/device.models.ts#L24">Device</a>';
export const deviceCredentialsHref = '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/device.models.ts#L50">DeviceCredentials</a>';
export const pageLinkArg: FunctionArg = {
name: 'pageLink',
type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/page/page-link.ts#L68">PageLink</a>',
description: 'Page link object used to perform paginated request.'
};
export const requestConfigArg: FunctionArg = {
name: 'config',
type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/http/http-utils.ts#L21">RequestConfig</a>',
description: 'HTTP request configuration.',
optional: true
};
export function observableReturnType(objectType: string): FunctionArgType {
return {
type: `Observable&lt;${objectType}&gt;`,
description: `An <code>Observable</code> of <code>${objectType}</code> object.`
};
}
export function observableVoid(): FunctionArgType {
return {
type: `Observable&lt;void&gt;`,
description: `An <code>Observable</code>.`
};
}
export function observableArrayReturnType(objectType: string): FunctionArgType {
return {
type: `Observable&lt;Array&lt;${objectType}&gt;&gt;`,
description: `An <code>Observable</code> of array of <code>${objectType}</code> objects.`
};
}
export function observablePageDataReturnType(objectType: string): FunctionArgType {
return {
type: `Observable&lt;${pageDataHref}&lt;${objectType}&gt;&gt;`,
description: `An <code>Observable</code> of page result as a <code>${pageDataHref}</code> holding array of <code>${objectType}</code> objects.`
};
}
export const serviceCompletions: TbEditorCompletions = {
deviceService: {
description: 'Device Service API<br>' +
'See <a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/http/device.service.ts#L37">DeviceService</a> for API reference.',
meta: 'service',
type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/http/device.service.ts#L37">DeviceService</a>',
children: {
getTenantDeviceInfos: {
description: 'Get tenant devices',
meta: 'function',
args: [
pageLinkArg,
{ name: 'type', type: 'string', optional: true, description: 'Device type'},
requestConfigArg
],
return: observablePageDataReturnType(deviceInfoHref)
},
getCustomerDeviceInfos: {
description: 'Get customer devices',
meta: 'function',
args: [
{ name: 'customerId', type: 'string', description: 'Id of the customer'},
pageLinkArg,
{ name: 'type', type: 'string', optional: true, description: 'Device type'},
requestConfigArg
],
return: observablePageDataReturnType(deviceInfoHref)
},
getDevice: {
description: 'Get device by id',
meta: 'function',
args: [
{ name: 'deviceId', type: 'string', description: 'Id of the device'},
requestConfigArg
],
return: observableReturnType(deviceHref)
},
getDevices: {
description: 'Get devices by ids',
meta: 'function',
args: [
{ name: 'deviceIds', type: 'Array<string>', description: 'List of device ids'},
requestConfigArg
],
return: observableArrayReturnType(deviceHref)
},
getDeviceInfo: {
description: 'Get device info by id',
meta: 'function',
args: [
{ name: 'deviceId', type: 'string', description: 'Id of the device'},
requestConfigArg
],
return: observableReturnType(deviceInfoHref)
},
saveDevice: {
description: 'Save device',
meta: 'function',
args: [
{ name: 'device', type: deviceHref, description: 'Device object to save'},
requestConfigArg
],
return: observableReturnType(deviceHref)
},
deleteDevice: {
description: 'Delete device by id',
meta: 'function',
args: [
{ name: 'deviceId', type: 'string', description: 'Id of the device'},
requestConfigArg
],
return: observableVoid()
},
getDeviceTypes: {
description: 'Get all available devices types',
meta: 'function',
args: [
requestConfigArg
],
return: observableArrayReturnType('<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/entity-type.models.ts#L295">EntitySubtype</a>')
},
getDeviceCredentials: {
description: 'Get device credentials by device id',
meta: 'function',
args: [
{ name: 'deviceId', type: 'string', description: 'Id of the device'},
{ name: 'sync', type: 'boolean', description: 'Whether to execute HTTP request synchronously (false by default)', optional: true},
requestConfigArg
],
return: observableReturnType(deviceCredentialsHref)
},
saveDeviceCredentials: {
description: 'Save device credentials',
meta: 'function',
args: [
{ name: 'deviceCredentials', type: deviceCredentialsHref, description: 'Device credentials object to save'},
requestConfigArg
],
return: observableReturnType(deviceCredentialsHref)
},
makeDevicePublic: {
description: 'Make device public (available from public dashboard)',
meta: 'function',
args: [
{ name: 'deviceId', type: 'string', description: 'Id of the device'},
requestConfigArg
],
return: observableReturnType(deviceHref)
},
assignDeviceToCustomer: {
description: 'Assign device to specific customer',
meta: 'function',
args: [
{ name: 'customerId', type: 'string', description: 'Id of the customer'},
{ name: 'deviceId', type: 'string', description: 'Id of the device'},
requestConfigArg
],
return: observableReturnType(deviceHref)
},
unassignDeviceFromCustomer: {
description: 'Unassign device from any customer',
meta: 'function',
args: [
{ name: 'deviceId', type: 'string', description: 'Id of the device'},
requestConfigArg
],
return: observableVoid()
},
sendOneWayRpcCommand: {
description: 'Send one way (without response) RPC command to the device.',
meta: 'function',
args: [
{ name: 'deviceId', type: 'string', description: 'Id of the device'},
{ name: 'requestBody', type: 'object', description: 'Request body to be sent to device'},
requestConfigArg
],
return: {
type: `Observable&lt;any&gt;`,
description: `A command execution <code>Observable</code>.`
}
},
sendTwoWayRpcCommand: {
description: 'Sends two way (with response) RPC command to the device.',
meta: 'function',
args: [
{ name: 'deviceId', type: 'string', description: 'Id of the device'},
{ name: 'requestBody', type: 'object', description: 'Request body to be sent to device'},
requestConfigArg
],
return: {
type: `Observable&lt;any&gt;`,
description: `A command execution <code>Observable</code> of response body.`
}
},
findByQuery: {
description: 'Find devices by search query',
meta: 'function',
args: [
{ name: 'query', type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/device.models.ts#L57">DeviceSearchQuery</a>',
description: 'Device search query object'},
requestConfigArg
],
return: observableArrayReturnType(deviceHref)
},
findByName: {
description: 'Find device by name',
meta: 'function',
args: [
{ name: 'deviceName', type: 'string',
description: 'Search device name'},
requestConfigArg
],
return: observableReturnType(deviceHref)
},
claimDevice: {
description: 'Send claim device request',
meta: 'function',
args: [
{ name: 'deviceName', type: 'string',
description: 'Claiming device name'},
requestConfigArg
],
return: observableReturnType('<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/device.models.ts#L71">ClaimResult</a>')
},
unclaimDevice: {
description: 'Send un-claim device request',
meta: 'function',
args: [
{ name: 'deviceName', type: 'string',
description: 'Device name to un-claim'},
requestConfigArg
],
return: observableVoid()
}
}
},
assetService: {
description: 'Asset Service API<br>' +
'See <a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/http/asset.service.ts#L29">AssetService</a> for API reference.',
meta: 'service',
type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/http/asset.service.ts#L29">AssetService</a>'
},
entityViewService: {
description: 'EntityView Service API<br>' +
'See <a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/http/entity-view.service.ts#L29">EntityViewService</a> for API reference.',
meta: 'service',
type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/http/entity-view.service.ts#L29">EntityViewService</a>'
},
customerService: {
description: 'Customer Service API<br>' +
'See <a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/http/customer.service.ts#L28">CustomerService</a> for API reference.',
meta: 'service',
type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/http/customer.service.ts#L28">CustomerService</a>'
},
dashboardService: {
description: 'Dashboard Service API<br>' +
'See <a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/http/dashboard.service.ts#L32">DashboardService</a> for API reference.',
meta: 'service',
type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/http/dashboard.service.ts#L32">DashboardService</a>'
},
userService: {
description: 'User Service API<br>' +
'See <a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/http/user.service.ts#L29">UserService</a> for API reference.',
meta: 'service',
type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/http/user.service.ts#L29">UserService</a>'
},
attributeService: {
description: 'Attribute Service API<br>' +
'See <a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/http/attribute.service.ts#L28">AttributeService</a> for API reference.',
meta: 'service',
type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/http/attribute.service.ts#L28">AttributeService</a>'
},
entityRelationService: {
description: 'Entity Relation Service API<br>' +
'See <a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/http/entity-relation.service.ts#L27">EntityRelationService</a> for API reference.',
meta: 'service',
type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/http/entity-relation.service.ts#L27">EntityRelationService</a>'
},
entityService: {
description: 'Entity Service API<br>' +
'See <a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/http/entity.service.ts#L64">EntityService</a> for API reference.',
meta: 'service',
type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/http/entity.service.ts#L64">EntityService</a>'
},
dialogs: {
description: 'Dialogs Service API<br>' +
'See <a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/services/dialog.service.ts#L39">DialogService</a> for API reference.',
meta: 'service',
type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/services/dialog.service.ts#L39">DialogService</a>'
},
customDialog: {
description: 'Custom Dialog Service API<br>' +
'See <a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/modules/home/components/widget/dialog/custom-dialog.service.ts#L33">CustomDialogService</a> for API reference.',
meta: 'service',
type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/modules/home/components/widget/dialog/custom-dialog.service.ts#L33">CustomDialogService</a>'
},
date: {
description: 'Date Pipe<br>Formats a date value according to locale rules.<br>' +
'See <a href="https://angular.io/api/common/DatePipe">DatePipe</a> for API reference.',
meta: 'service',
type: '<a href="https://angular.io/api/common/DatePipe">DatePipe</a>'
},
translate: {
description: 'Translate Service API<br>' +
'See <a href="https://github.com/ngx-translate/core#translateservice">TranslateService</a> for API reference.',
meta: 'service',
type: '<a href="https://github.com/ngx-translate/core#translateservice">TranslateService</a>'
},
http: {
description: 'HTTP Client Service<br>' +
'See <a href="https://angular.io/api/common/http/HttpClient">HttpClient</a> for API reference.',
meta: 'service',
type: '<a href="https://angular.io/api/common/http/HttpClient">HttpClient</a>'
}
}

615
ui-ngx/src/app/shared/models/ace/widget-completion.models.ts

@ -0,0 +1,615 @@
///
/// 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 { TbEditorCompletion, TbEditorCompletions } from '@shared/models/ace/completion.models';
import { entityIdHref, serviceCompletions } from '@shared/models/ace/service-completion.models';
export const timewindowCompletion: TbEditorCompletion = {
description: 'Timewindow configuration object',
meta: 'property',
type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/time/time.models.ts#L80">Timewindow</a>',
children: {
displayValue: {
description: 'Current timewindow display value.',
meta: 'property',
type: 'string'
},
hideInterval: {
description: 'Whether to hide interval selection in timewindow panel.',
meta: 'property',
type: 'boolean'
},
hideAggregation: {
description: 'Whether to hide aggregation selection in timewindow panel.',
meta: 'property',
type: 'boolean'
},
hideAggInterval: {
description: 'Whether to hide aggregation interval selection in timewindow panel.',
meta: 'property',
type: 'boolean'
},
selectedTab: {
description: 'Current selected timewindow type (0 - realtime, 1 - history).',
meta: 'property',
type: 'number'
},
realtime: {
description: 'Realtime timewindow configuration object.',
meta: 'property',
type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/time/time.models.ts#L39">IntervalWindow</a>',
children: {
interval: {
description: 'Timewindow aggregation interval in milliseconds',
meta: 'property',
type: 'number'
},
timewindowMs: {
description: 'Timewindow interval in milliseconds',
meta: 'property',
type: 'number'
}
}
},
history: {
description: 'History timewindow configuration object.',
meta: 'property',
type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/time/time.models.ts#L49">HistoryWindow</a>',
children: {
historyType: {
description: 'History timewindow type (0 - last interval, 1 - fixed)',
meta: 'property',
type: 'number'
},
interval: {
description: 'Timewindow aggregation interval in milliseconds',
meta: 'property',
type: 'number'
},
timewindowMs: {
description: 'Timewindow interval in milliseconds',
meta: 'property',
type: 'number'
},
fixedTimewindow: {
description: 'Fixed history timewindow configuration object',
meta: 'property',
type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/time/time.models.ts#L44">FixedWindow</a>',
children: {
startTimeMs: {
description: 'Timewindow start time in UTC milliseconds',
meta: 'property',
type: 'number'
},
endTimeMs: {
description: 'Timewindow end time in UTC milliseconds',
meta: 'property',
type: 'number'
}
}
}
}
},
aggregation: {
description: 'Timewindow aggregation configuration object.',
meta: 'property',
type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/time/time.models.ts#L74">Aggregation</a>',
children: {
interval: {
description: 'Aggregation interval in milliseconds',
meta: 'property',
type: 'number'
},
type: {
description: 'Aggregation type',
meta: 'property',
type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/time/time.models.ts#L54">AggregationType</a>'
},
limit: {
description: 'Maximum allowed datapoints when aggregation is disabled (<code>AggregationType == \'NONE\'</code>)',
meta: 'property',
type: 'number'
}
}
}
}
}
export const widgetContextCompletions: TbEditorCompletions = {
ctx: {
description: 'A reference to widget context that has all necessary API<br>and data used by widget instance.',
meta: 'object',
type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/modules/home/models/widget-component.models.ts#L83">WidgetContext</a>',
children: {
...{
$container: {
description: 'Container element of the widget.<br>Can be used to dynamically access or modify widget DOM using jQuery API.',
meta: 'property',
type: 'jQuery Object'
},
$scope: {
description: 'Reference to the current widget component.<br>Can be used to access/modify component properties when widget is built using Angular approach.',
meta: 'property',
type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/modules/home/models/widget-component.models.ts#L274">IDynamicWidgetComponent</a>'
},
width: {
description: 'Current width of widget container in pixels.',
meta: 'property',
type: 'number'
},
height: {
description: 'Current height of widget container in pixels.',
meta: 'property',
type: 'number'
},
isEdit: {
description: 'Indicates whether the dashboard is in in the view or editing state.',
meta: 'property',
type: 'boolean'
},
isMobile: {
description: 'Indicates whether the dashboard view is less then 960px width (default mobile breakpoint).',
meta: 'property',
type: 'boolean'
},
widgetConfig: {
description: 'Common widget configuration containing properties such as <code>color</code> (text color), <code>backgroundColor</code> (widget background color), etc.',
meta: 'property',
type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/widget.models.ts#L341">WidgetConfig</a>',
children: {
title: {
description: 'Widget title.',
meta: 'property',
type: 'string'
},
titleIcon: {
description: 'Widget title icon.',
meta: 'property',
type: 'string'
},
showTitle: {
description: 'Whether to show widget title.',
meta: 'property',
type: 'boolean'
},
showTitleIcon: {
description: 'Whether to show widget title icon.',
meta: 'property',
type: 'boolean'
},
iconColor: {
description: 'Widget title icon color.',
meta: 'property',
type: 'string'
},
iconSize: {
description: 'Widget title icon size.',
meta: 'property',
type: 'string'
},
titleTooltip: {
description: 'Widget title tooltip content.',
meta: 'property',
type: 'string'
},
dropShadow: {
description: 'Enable/disable widget card shadow.',
meta: 'property',
type: 'boolean'
},
enableFullscreen: {
description: 'Whether to enable fullscreen button on widget.',
meta: 'property',
type: 'boolean'
},
useDashboardTimewindow: {
description: 'Whether to use dashboard timewindow (applicable for timeseries widgets).',
meta: 'property',
type: 'boolean'
},
displayTimewindow: {
description: 'Whether to display timewindow (applicable for timeseries widgets).',
meta: 'property',
type: 'boolean'
},
showLegend: {
description: 'Whether to show legend.',
meta: 'property',
type: 'boolean'
},
legendConfig: {
description: 'Legend configuration.',
meta: 'property',
type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/widget.models.ts#L198">LegendConfig</a>',
children: {
position: {
description: 'Legend position. Possible values: <code>\'top\', \'bottom\', \'left\', \'right\'</code>',
meta: 'property',
type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/widget.models.ts#L182">LegendPosition</a>',
},
direction: {
description: 'Legend direction. Possible values: <code>\'column\', \'row\'</code>',
meta: 'property',
type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/widget.models.ts#L170">LegendDirection</a>',
},
showMin: {
description: 'Whether to display aggregated min values.',
meta: 'property',
type: 'boolean',
},
showMax: {
description: 'Whether to display aggregated max values.',
meta: 'property',
type: 'boolean',
},
showAvg: {
description: 'Whether to display aggregated average values.',
meta: 'property',
type: 'boolean',
},
showTotal: {
description: 'Whether to display aggregated total values.',
meta: 'property',
type: 'boolean',
}
}
},
timewindow: timewindowCompletion,
mobileHeight: {
description: 'Widget height in mobile mode.',
meta: 'property',
type: 'number'
},
mobileOrder: {
description: 'Widget order in mobile mode.',
meta: 'property',
type: 'number'
},
color: {
description: 'Widget text color.',
meta: 'property',
type: 'string'
},
backgroundColor: {
description: 'Widget background color.',
meta: 'property',
type: 'string'
},
padding: {
description: 'Widget card padding.',
meta: 'property',
type: 'string'
},
margin: {
description: 'Widget card margin.',
meta: 'property',
type: 'string'
},
widgetStyle: {
description: 'Widget element style object.',
meta: 'property',
type: 'object'
},
titleStyle: {
description: 'Widget title element style object.',
meta: 'property',
type: 'object'
},
units: {
description: 'Optional property defining units text of values displayed by widget. Useful for simple widgets like cards or gauges.',
meta: 'property',
type: 'string'
},
decimals: {
description: 'Optional property defining how many positions should be used to display decimal part of the value number.',
meta: 'property',
type: 'number'
},
actions: {
description: 'Map of configured widget actions.',
meta: 'property',
type: 'object'
},
settings: {
description: 'Object holding widget settings according to widget type.',
meta: 'property',
type: 'object'
},
alarmSource: {
description: 'Configured alarm source for alarm widget type.',
meta: 'property',
type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/widget.models.ts#L250">Datasource</a>'
},
alarmSearchStatus: {
description: 'Configured default alarm search status for alarm widget type.',
meta: 'property',
type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/alarm.models.ts#L41">AlarmSearchStatus</a>'
},
alarmsPollingInterval: {
description: 'Configured alarms polling interval for alarm widget type.',
meta: 'property',
type: 'number'
},
alarmsMaxCountLoad: {
description: 'Configured maximum alarms to load for alarm widget type.',
meta: 'property',
type: 'number'
},
alarmsFetchSize: {
description: 'Configured alarms page size used to load alarms.',
meta: 'property',
type: 'number'
},
datasources: {
description: 'Array of configured widget datasources.',
meta: 'property',
type: 'Array&lt;<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/widget.models.ts#L250">Datasource</a>&gt;'
}
}
},
settings: {
description: 'Widget settings containing widget specific properties according to the defined <a href="https://thingsboard.io/docs/user-guide/contribution/widgets-development/#settings-schema-section">settings json schema</a>',
meta: 'property',
type: 'object'
},
units: {
description: 'Optional property defining units text of values displayed by widget. Useful for simple widgets like cards or gauges.',
meta: 'property',
type: 'string'
},
decimals: {
description: 'Optional property defining how many positions should be used to display decimal part of the value number.',
meta: 'property',
type: 'number'
},
hideTitlePanel: {
description: 'Manages visibility of widget title panel. Useful for widget with custom title panels or different states.',
meta: 'property',
type: 'boolean'
},
defaultSubscription: {
description: 'Default widget subscription object contains all subscription information,<br>including current data, according to the widget type.',
meta: 'property',
type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/api/widget-api.models.ts#L220">IWidgetSubscription</a>'
},
timewindowFunctions: {
description: 'Object with timewindow functions used to manage widget data time frame. Can by used by Time-series or Alarm widgets.',
meta: 'property',
type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/api/widget-api.models.ts#L45">TimewindowFunctions</a>',
children: {
onUpdateTimewindow: {
description: 'This function can be used to update current subscription time frame<br>to historical one identified by <code>startTimeMs</code> and <code>endTimeMs</code> arguments.',
meta: 'function',
args: [
{
name: 'startTimeMs',
description: 'Timewindow start time in UTC milliseconds',
type: 'number'
},
{
name: 'endTimeMs',
description: 'Timewindow end time in UTC milliseconds',
type: 'number'
}
]
},
onResetTimewindow: {
description: 'Resets subscription time frame to default defined by widget timewindow component<br>or dashboard timewindow depending on widget settings.',
meta: 'function'
}
}
},
controlApi: {
description: 'Object that provides API functions for RPC (Control) widgets.',
meta: 'property',
type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/api/widget-api.models.ts#L58">RpcApi</a>',
children: {
sendOneWayCommand: {
description: 'Sends one way (without response) RPC command to the device.',
meta: 'function',
args: [
{
name: 'method',
description: 'RPC method name',
type: 'string'
},
{
name: 'params',
description: 'RPC method params, custom json object',
type: 'object',
optional: true
},
{
name: 'timeout',
description: 'Maximum delay in milliseconds to wait until response/acknowledgement is received.',
type: 'number',
optional: true
}
],
return: {
description: 'A command execution Observable.',
type: 'Observable&lt;any&gt;'
}
},
sendTwoWayCommand: {
description: 'Sends two way (with response) RPC command to the device.',
meta: 'function',
args: [
{
name: 'method',
description: 'RPC method name',
type: 'string'
},
{
name: 'params',
description: 'RPC method params, custom json object',
type: 'object',
optional: true
},
{
name: 'timeout',
description: 'Maximum delay in milliseconds to wait until response/acknowledgement is received.',
type: 'number',
optional: true
}
],
return: {
description: 'A command execution Observable of response body.',
type: 'Observable&lt;any&gt;'
}
}
}
},
actionsApi: {
description: 'Set of API functions to work with user defined actions.',
meta: 'property',
type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/api/widget-api.models.ts#L67">WidgetActionsApi</a>',
children: {
getActionDescriptors: {
description: 'Get list of action descriptors for provided <code>actionSourceId</code>.',
meta: 'function',
args: [
{
name: 'actionSourceId',
description: 'Id of widget action source',
type: 'string'
}
],
return: {
description: 'The list of action descriptors',
type: 'Array&lt;<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/widget.models.ts#L323">WidgetActionDescriptor</a>&gt;'
}
},
handleWidgetAction: {
description: 'Handle action produced by particular action source.',
meta: 'function',
args: [
{
name: '$event',
description: 'DOM event object associated with action.',
type: 'Event'
},
{
name: 'descriptor',
description: 'An action descriptor.',
type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/widget.models.ts#L323">WidgetActionDescriptor</a>'
},
{
name: 'entityId',
description: 'Current entity id provided by action source if available.',
type: entityIdHref,
optional: true
},
{
name: 'entityName',
description: 'Current entity name provided by action source if available.',
type: 'string',
optional: true
}
]
}
}
},
stateController: {
description: 'Reference to Dashboard state controller, providing API to manage current dashboard state.',
meta: 'property',
type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/api/widget-api.models.ts#L121">IStateController</a>',
children: {
openState: {
description: 'Navigate to new dashboard state.',
meta: 'function',
args: [
{
name: 'id',
description: 'An id of the target dashboard state.',
type: 'string'
},
{
name: 'params',
description: 'An object with state parameters to use by the new state.',
type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/api/widget-api.models.ts#L111">StateParams</a>',
optional: true
},
{
name: 'openRightLayout',
description: 'An optional boolean argument to force open right dashboard layout if present in mobile view mode.',
type: 'boolean',
optional: true
}
]
},
updateState: {
description: 'Updates current dashboard state.',
meta: 'function',
args: [
{
name: 'id',
description: 'An optional id of the target dashboard state to replace current state id.',
type: 'string',
optional: true
},
{
name: 'params',
description: 'An object with state parameters to update current state parameters.',
type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/api/widget-api.models.ts#L111">StateParams</a>',
optional: true
},
{
name: 'openRightLayout',
description: 'An optional boolean argument to force open right dashboard layout if present in mobile view mode.',
type: 'boolean',
optional: true
}
]
},
getStateId: {
description: 'Get current dashboard state id.',
meta: 'function',
return: {
description: 'current dashboard state id.',
type: 'string'
}
},
getStateParams: {
description: 'Get current dashboard state parameters.',
meta: 'function',
return: {
description: 'current dashboard state parameters.',
type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/api/widget-api.models.ts#L111">StateParams</a>'
}
},
getStateParamsByStateId: {
description: 'Get state parameters for particular dashboard state identified by <code>id</code>.',
meta: 'function',
args: [
{
name: 'id',
description: 'An id of the target dashboard state.',
type: 'string'
}
],
return: {
description: 'current dashboard state parameters.',
type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/api/widget-api.models.ts#L111">StateParams</a>'
}
}
}
}
},
...serviceCompletions
}
}
}

52
ui-ngx/src/styles.scss

@ -416,6 +416,58 @@ mat-label {
} }
} }
.tb-ace-doc-tooltip {
code {
color: #444;
&.title {
font-size: 14px;
}
}
div.tb-function-info {
font-size: 14px;
}
div.tb-function-return {
font-size: 1rem;
letter-spacing: 0.03rem;
color: #444;
code {
font-size: 14px;
letter-spacing: normal;
}
}
div.tb-api-title {
font-weight: bold;
font-size: 16px;
color: #6e6e6e;
padding-top: 12px;
padding-bottom: 12px;
}
table.tb-api-table {
width: 100%;
border-collapse: collapse;
tr {
border-bottom: 1px solid #a8a8a8;
&:last-child {
border-bottom: none;
}
td {
font-size: 14px;
line-height: 1.6rem;
&:first-child {
font-weight: 600;
padding-left: 16px;
width: 20%;
}
&.arg-description {
font-size: 1rem;
letter-spacing: .03rem;
color: #444;
}
}
}
}
}
.tb-default, .tb-dark { .tb-default, .tb-dark {
/********************************* /*********************************

Loading…
Cancel
Save