Merge pull request #12987 from vvlladd28/map-widget/improvement/place-item/example
Add new map widgets helps/example
This commit is contained in:
commit
48c3828d5d
@ -37,6 +37,7 @@
|
|||||||
<div class="tb-custom-action-editor" [class.tb-fullscreen-editor]="fullscreen">
|
<div class="tb-custom-action-editor" [class.tb-fullscreen-editor]="fullscreen">
|
||||||
<div *ngIf="!fullscreen; else fullscreenEditor">
|
<div *ngIf="!fullscreen; else fullscreenEditor">
|
||||||
<tb-custom-action-pretty-resources-tabs [hasCustomFunction]="true"
|
<tb-custom-action-pretty-resources-tabs [hasCustomFunction]="true"
|
||||||
|
[helpId]="helpId"
|
||||||
[action]="action" (actionUpdated)="onActionUpdated($event ? true : false)">
|
[action]="action" (actionUpdated)="onActionUpdated($event ? true : false)">
|
||||||
</tb-custom-action-pretty-resources-tabs>
|
</tb-custom-action-pretty-resources-tabs>
|
||||||
</div>
|
</div>
|
||||||
@ -44,6 +45,7 @@
|
|||||||
<div class="tb-fullscreen-panel tb-layout-fill flex flex-row">
|
<div class="tb-fullscreen-panel tb-layout-fill flex flex-row">
|
||||||
<div #leftPanel class="tb-split tb-content">
|
<div #leftPanel class="tb-split tb-content">
|
||||||
<tb-custom-action-pretty-resources-tabs [hasCustomFunction]="false"
|
<tb-custom-action-pretty-resources-tabs [hasCustomFunction]="false"
|
||||||
|
[helpId]="helpId"
|
||||||
[action]="action" (actionUpdated)="onActionUpdated($event ? true : false)">
|
[action]="action" (actionUpdated)="onActionUpdated($event ? true : false)">
|
||||||
</tb-custom-action-pretty-resources-tabs>
|
</tb-custom-action-pretty-resources-tabs>
|
||||||
</div>
|
</div>
|
||||||
@ -58,7 +60,7 @@
|
|||||||
[validationArgs]="[]"
|
[validationArgs]="[]"
|
||||||
[editorCompleter]="customPrettyActionEditorCompleter"
|
[editorCompleter]="customPrettyActionEditorCompleter"
|
||||||
functionTitle="{{ 'widget-action.custom-pretty-function' | translate }}"
|
functionTitle="{{ 'widget-action.custom-pretty-function' | translate }}"
|
||||||
helpId="widget/action/custom_pretty_action_fn">
|
[helpId]="helpId">
|
||||||
</tb-js-func>
|
</tb-js-func>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -14,28 +14,22 @@
|
|||||||
/// limitations under the License.
|
/// limitations under the License.
|
||||||
///
|
///
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/triple-slash-reference
|
|
||||||
/// <reference path="../../../../../../../../../../src/typings/split.js.typings.d.ts" />
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AfterViewInit,
|
AfterViewInit,
|
||||||
Component,
|
Component,
|
||||||
ElementRef,
|
ElementRef,
|
||||||
forwardRef,
|
forwardRef,
|
||||||
Input,
|
Input,
|
||||||
OnDestroy,
|
|
||||||
OnInit,
|
|
||||||
QueryList,
|
QueryList,
|
||||||
ViewChildren,
|
ViewChildren,
|
||||||
ViewEncapsulation
|
ViewEncapsulation
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
|
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||||
import { PageComponent } from '@shared/components/page.component';
|
|
||||||
import { Store } from '@ngrx/store';
|
|
||||||
import { AppState } from '@core/core.state';
|
|
||||||
import { combineLatest } from 'rxjs';
|
import { combineLatest } from 'rxjs';
|
||||||
import { CustomActionDescriptor } from '@shared/models/widget.models';
|
import { CustomActionDescriptor, WidgetActionType } from '@shared/models/widget.models';
|
||||||
import { CustomPrettyActionEditorCompleter } from '@home/components/widget/lib/settings/common/action/custom-action.models';
|
import {
|
||||||
|
CustomPrettyActionEditorCompleter
|
||||||
|
} from '@home/components/widget/lib/settings/common/action/custom-action.models';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'tb-custom-action-pretty-editor',
|
selector: 'tb-custom-action-pretty-editor',
|
||||||
@ -50,7 +44,7 @@ import { CustomPrettyActionEditorCompleter } from '@home/components/widget/lib/s
|
|||||||
],
|
],
|
||||||
encapsulation: ViewEncapsulation.None
|
encapsulation: ViewEncapsulation.None
|
||||||
})
|
})
|
||||||
export class CustomActionPrettyEditorComponent extends PageComponent implements OnInit, AfterViewInit, OnDestroy, ControlValueAccessor {
|
export class CustomActionPrettyEditorComponent implements AfterViewInit, ControlValueAccessor {
|
||||||
|
|
||||||
@Input() disabled: boolean;
|
@Input() disabled: boolean;
|
||||||
|
|
||||||
@ -58,6 +52,17 @@ export class CustomActionPrettyEditorComponent extends PageComponent implements
|
|||||||
|
|
||||||
fullscreen = false;
|
fullscreen = false;
|
||||||
|
|
||||||
|
helpId= 'widget/action/custom_pretty_action_fn';
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
set widgetActionType(type: WidgetActionType) {
|
||||||
|
if (type === WidgetActionType.placeMapItem) {
|
||||||
|
this.helpId = 'widget/action/place_map_item/place_map_item_action';
|
||||||
|
} else {
|
||||||
|
this.helpId = 'widget/action/custom_pretty_action_fn';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ViewChildren('leftPanel')
|
@ViewChildren('leftPanel')
|
||||||
leftPanelElmRef: QueryList<ElementRef<HTMLElement>>;
|
leftPanelElmRef: QueryList<ElementRef<HTMLElement>>;
|
||||||
|
|
||||||
@ -68,15 +73,11 @@ export class CustomActionPrettyEditorComponent extends PageComponent implements
|
|||||||
|
|
||||||
private propagateChange = (_: any) => {};
|
private propagateChange = (_: any) => {};
|
||||||
|
|
||||||
constructor(protected store: Store<AppState>) {
|
constructor() {
|
||||||
super(store);
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngAfterViewInit(): void {
|
ngAfterViewInit(): void {
|
||||||
combineLatest(this.leftPanelElmRef.changes, this.rightPanelElmRef.changes).subscribe(() => {
|
combineLatest([this.leftPanelElmRef.changes, this.rightPanelElmRef.changes]).subscribe(() => {
|
||||||
if (this.leftPanelElmRef.length && this.rightPanelElmRef.length) {
|
if (this.leftPanelElmRef.length && this.rightPanelElmRef.length) {
|
||||||
this.initSplitLayout(this.leftPanelElmRef.first.nativeElement,
|
this.initSplitLayout(this.leftPanelElmRef.first.nativeElement,
|
||||||
this.rightPanelElmRef.first.nativeElement);
|
this.rightPanelElmRef.first.nativeElement);
|
||||||
@ -92,14 +93,11 @@ export class CustomActionPrettyEditorComponent extends PageComponent implements
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
|
||||||
}
|
|
||||||
|
|
||||||
registerOnChange(fn: any): void {
|
registerOnChange(fn: any): void {
|
||||||
this.propagateChange = fn;
|
this.propagateChange = fn;
|
||||||
}
|
}
|
||||||
|
|
||||||
registerOnTouched(fn: any): void {
|
registerOnTouched(_fn: any): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
setDisabledState(isDisabled: boolean): void {
|
setDisabledState(isDisabled: boolean): void {
|
||||||
|
|||||||
@ -101,7 +101,7 @@
|
|||||||
[validationArgs]="[]"
|
[validationArgs]="[]"
|
||||||
[editorCompleter]="customPrettyActionEditorCompleter"
|
[editorCompleter]="customPrettyActionEditorCompleter"
|
||||||
functionTitle="{{ 'widget-action.custom-pretty-function' | translate }}"
|
functionTitle="{{ 'widget-action.custom-pretty-function' | translate }}"
|
||||||
helpId="widget/action/custom_pretty_action_fn">
|
[helpId]="helpId">
|
||||||
</tb-js-func>
|
</tb-js-func>
|
||||||
</mat-tab>
|
</mat-tab>
|
||||||
</mat-tab-group>
|
</mat-tab-group>
|
||||||
|
|||||||
@ -27,16 +27,15 @@ import {
|
|||||||
ViewChild,
|
ViewChild,
|
||||||
ViewEncapsulation
|
ViewEncapsulation
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
|
||||||
import { PageComponent } from '@shared/components/page.component';
|
import { PageComponent } from '@shared/components/page.component';
|
||||||
import { Store } from '@ngrx/store';
|
|
||||||
import { AppState } from '@core/core.state';
|
|
||||||
import { CustomActionDescriptor } from '@shared/models/widget.models';
|
import { CustomActionDescriptor } from '@shared/models/widget.models';
|
||||||
import { Ace } from 'ace-builds';
|
import { Ace } from 'ace-builds';
|
||||||
import { CancelAnimationFrame, RafService } from '@core/services/raf.service';
|
import { CancelAnimationFrame, RafService } from '@core/services/raf.service';
|
||||||
import { CustomPrettyActionEditorCompleter } from '@home/components/widget/lib/settings/common/action/custom-action.models';
|
import {
|
||||||
|
CustomPrettyActionEditorCompleter
|
||||||
|
} from '@home/components/widget/lib/settings/common/action/custom-action.models';
|
||||||
import { Observable } from 'rxjs/internal/Observable';
|
import { Observable } from 'rxjs/internal/Observable';
|
||||||
import { forkJoin, from } from 'rxjs';
|
import { forkJoin } from 'rxjs';
|
||||||
import { map, tap } from 'rxjs/operators';
|
import { map, tap } from 'rxjs/operators';
|
||||||
import { getAce } from '@shared/models/ace/ace.models';
|
import { getAce } from '@shared/models/ace/ace.models';
|
||||||
import { beautifyCss, beautifyHtml } from '@shared/models/beautify.models';
|
import { beautifyCss, beautifyHtml } from '@shared/models/beautify.models';
|
||||||
@ -55,6 +54,9 @@ export class CustomActionPrettyResourcesTabsComponent extends PageComponent impl
|
|||||||
@Input()
|
@Input()
|
||||||
hasCustomFunction: boolean;
|
hasCustomFunction: boolean;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
helpId: string;
|
||||||
|
|
||||||
@Output()
|
@Output()
|
||||||
actionUpdated: EventEmitter<CustomActionDescriptor> = new EventEmitter<CustomActionDescriptor>();
|
actionUpdated: EventEmitter<CustomActionDescriptor> = new EventEmitter<CustomActionDescriptor>();
|
||||||
|
|
||||||
@ -76,10 +78,8 @@ export class CustomActionPrettyResourcesTabsComponent extends PageComponent impl
|
|||||||
|
|
||||||
customPrettyActionEditorCompleter = CustomPrettyActionEditorCompleter;
|
customPrettyActionEditorCompleter = CustomPrettyActionEditorCompleter;
|
||||||
|
|
||||||
constructor(protected store: Store<AppState>,
|
constructor(private raf: RafService) {
|
||||||
private translate: TranslateService,
|
super();
|
||||||
private raf: RafService) {
|
|
||||||
super(store);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
|||||||
@ -22,6 +22,8 @@ import { deepClone, isDefined, isUndefined } from '@core/utils';
|
|||||||
import customSampleJs from './custom-sample-js.raw';
|
import customSampleJs from './custom-sample-js.raw';
|
||||||
import customSampleCss from './custom-sample-css.raw';
|
import customSampleCss from './custom-sample-css.raw';
|
||||||
import customSampleHtml from './custom-sample-html.raw';
|
import customSampleHtml from './custom-sample-html.raw';
|
||||||
|
import placeMapItemSampleHtml from './place-map-item-sample-html.raw';
|
||||||
|
import placeMapItemSampleJs from './place-map-item-sample-js.raw';
|
||||||
|
|
||||||
const customActionCompletions: TbEditorCompletions = {
|
const customActionCompletions: TbEditorCompletions = {
|
||||||
...{
|
...{
|
||||||
@ -96,5 +98,15 @@ export const toCustomAction = (action: WidgetAction): CustomActionDescriptor =>
|
|||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const toPlaceMapItemAction = (action: WidgetAction): CustomActionDescriptor => {
|
||||||
|
const result: CustomActionDescriptor = {
|
||||||
|
customHtml: action?.customHtml ?? placeMapItemSampleHtml,
|
||||||
|
customCss: action?.customCss ?? '',
|
||||||
|
customFunction: action?.customFunction ?? placeMapItemSampleJs
|
||||||
|
};
|
||||||
|
result.customResources = isDefined(action?.customResources) ? deepClone(action.customResources) : [];
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
export const CustomActionEditorCompleter = new TbEditorCompleter(customActionCompletions);
|
export const CustomActionEditorCompleter = new TbEditorCompleter(customActionCompletions);
|
||||||
export const CustomPrettyActionEditorCompleter = new TbEditorCompleter(customPrettyActionCompletions);
|
export const CustomPrettyActionEditorCompleter = new TbEditorCompleter(customPrettyActionCompletions);
|
||||||
|
|||||||
@ -0,0 +1,82 @@
|
|||||||
|
<!--========================================================================-->
|
||||||
|
<!--========================= Add entity example =========================-->
|
||||||
|
<!--========================================================================-->
|
||||||
|
|
||||||
|
<form #addEntityForm="ngForm" [formGroup]="addEntityFormGroup"
|
||||||
|
(ngSubmit)="save()" style="width: 552px">
|
||||||
|
<mat-toolbar class="flex flex-row" color="primary">
|
||||||
|
<h2>Add entity</h2>
|
||||||
|
<span class="flex-1"></span>
|
||||||
|
<button mat-icon-button (click)="cancel()" type="button">
|
||||||
|
<mat-icon class="material-icons">close</mat-icon>
|
||||||
|
</button>
|
||||||
|
</mat-toolbar>
|
||||||
|
<mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async">
|
||||||
|
</mat-progress-bar>
|
||||||
|
<div style="height: 4px;" *ngIf="!(isLoading$ | async)"></div>
|
||||||
|
<div mat-dialog-content class="flex flex-col" style="padding-bottom: 0">
|
||||||
|
<div class="flex flex-row gap-2 xs:flex-col xs:gap-0">
|
||||||
|
<mat-form-field class="mat-block flex-1" appearance="outline">
|
||||||
|
<mat-label>Entity Name</mat-label>
|
||||||
|
<input matInput formControlName="entityName" required>
|
||||||
|
<mat-error *ngIf="addEntityFormGroup.get('entityName').hasError('required')">
|
||||||
|
Entity name is required.
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field class="mat-block flex-1" appearance="outline">
|
||||||
|
<mat-label>Entity Label</mat-label>
|
||||||
|
<input matInput formControlName="entityLabel" >
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-row gap-2 xs:flex-col xs:gap-0">
|
||||||
|
<tb-entity-type-select
|
||||||
|
class="mat-block flex-1"
|
||||||
|
formControlName="entityType"
|
||||||
|
[showLabel]="true"
|
||||||
|
[appearance]="'outline'"
|
||||||
|
[allowedEntityTypes]="allowedEntityTypes"
|
||||||
|
></tb-entity-type-select>
|
||||||
|
<tb-entity-subtype-autocomplete
|
||||||
|
*ngIf="addEntityFormGroup.get('entityType').value == 'ASSET'"
|
||||||
|
class="mat-block flex-1"
|
||||||
|
formControlName="type"
|
||||||
|
[required]="true"
|
||||||
|
[entityType]="'ASSET'"
|
||||||
|
[appearance]="'outline'"
|
||||||
|
></tb-entity-subtype-autocomplete>
|
||||||
|
<tb-entity-subtype-autocomplete
|
||||||
|
*ngIf="addEntityFormGroup.get('entityType').value != 'ASSET'"
|
||||||
|
class="mat-block flex-1"
|
||||||
|
formControlName="type"
|
||||||
|
[required]="true"
|
||||||
|
[entityType]="'DEVICE'"
|
||||||
|
[appearance]="'outline'"
|
||||||
|
></tb-entity-subtype-autocomplete>
|
||||||
|
</div>
|
||||||
|
<div formGroupName="attributes" class="flex flex-col">
|
||||||
|
<div class="flex flex-row gap-2 xs:flex-col xs:gap-0">
|
||||||
|
<mat-form-field class="mat-block flex-1" appearance="outline">
|
||||||
|
<mat-label>Address</mat-label>
|
||||||
|
<input matInput formControlName="address">
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field class="mat-block flex-1" appearance="outline">
|
||||||
|
<mat-label>Owner</mat-label>
|
||||||
|
<input matInput formControlName="owner">
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div mat-dialog-actions class="flex flex-row items-center justify-end">
|
||||||
|
<button mat-button color="primary"
|
||||||
|
type="button"
|
||||||
|
[disabled]="(isLoading$ | async)"
|
||||||
|
(click)="cancel()" cdkFocusInitial>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
<button mat-button mat-raised-button color="primary"
|
||||||
|
type="submit"
|
||||||
|
[disabled]="(isLoading$ | async) || addEntityForm.invalid || !addEntityForm.dirty">
|
||||||
|
Create
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
@ -0,0 +1,89 @@
|
|||||||
|
/*========================================================================*/
|
||||||
|
/*========================= Add entity example =========================*/
|
||||||
|
/*========================================================================*/
|
||||||
|
|
||||||
|
let $injector = widgetContext.$scope.$injector;
|
||||||
|
let customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));
|
||||||
|
let assetService = $injector.get(widgetContext.servicesMap.get('assetService'));
|
||||||
|
let deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));
|
||||||
|
let attributeService = $injector.get(widgetContext.servicesMap.get('attributeService'));
|
||||||
|
|
||||||
|
openAddEntityDialog();
|
||||||
|
|
||||||
|
function openAddEntityDialog() {
|
||||||
|
customDialog.customDialog(htmlTemplate, AddEntityDialogController).subscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
function AddEntityDialogController(instance) {
|
||||||
|
let vm = instance;
|
||||||
|
|
||||||
|
vm.allowedEntityTypes = ['ASSET', 'DEVICE'];
|
||||||
|
|
||||||
|
vm.addEntityFormGroup = vm.fb.group({
|
||||||
|
entityName: ['', [vm.validators.required]],
|
||||||
|
entityType: ['DEVICE'],
|
||||||
|
entityLabel: [null],
|
||||||
|
type: ['', [vm.validators.required]],
|
||||||
|
attributes: vm.fb.group({
|
||||||
|
address: [null],
|
||||||
|
owner: [null]
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
vm.cancel = function() {
|
||||||
|
vm.dialogRef.close(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
vm.save = function() {
|
||||||
|
vm.addEntityFormGroup.markAsPristine();
|
||||||
|
saveEntityObservable().pipe(
|
||||||
|
widgetContext.rxjs.switchMap((entity) => saveAttributes(entity.id))
|
||||||
|
).subscribe(() => {
|
||||||
|
widgetContext.updateAliases();
|
||||||
|
vm.dialogRef.close(null);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
function saveEntityObservable() {
|
||||||
|
const formValues = vm.addEntityFormGroup.value;
|
||||||
|
let entity = {
|
||||||
|
name: formValues.entityName,
|
||||||
|
type: formValues.type,
|
||||||
|
label: formValues.entityLabel
|
||||||
|
};
|
||||||
|
if (formValues.entityType == 'ASSET') {
|
||||||
|
return assetService.saveAsset(entity);
|
||||||
|
} else if (formValues.entityType == 'DEVICE') {
|
||||||
|
return deviceService.saveDevice(entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveAttributes(entityId) {
|
||||||
|
let attributes = vm.addEntityFormGroup.get('attributes').value;
|
||||||
|
let attributesArray = getMapItemLocationAttributes();
|
||||||
|
for (let key in attributes) {
|
||||||
|
if(attributes[key] !== null) {
|
||||||
|
attributesArray.push({key: key, value: attributes[key]});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (attributesArray.length > 0) {
|
||||||
|
return attributeService.saveEntityAttributes(entityId, "SERVER_SCOPE", attributesArray);
|
||||||
|
}
|
||||||
|
return widgetContext.rxjs.of([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMapItemLocationAttributes() {
|
||||||
|
const attributes = [];
|
||||||
|
const mapItemType = $event.shape;
|
||||||
|
if (mapItemType === 'Marker') {
|
||||||
|
const mapType = widgetContext.mapInstance.type();
|
||||||
|
attributes.push({key: mapType === 'image' ? 'xPos' : 'latitude', value: additionalParams.coordinates.x});
|
||||||
|
attributes.push({key: mapType === 'image' ? 'yPos' : 'longitude', value: additionalParams.coordinates.y});
|
||||||
|
} else if (mapItemType === 'Rectangle' || mapItemType === 'Polygon') {
|
||||||
|
attributes.push({key: 'perimeter', value: additionalParams.coordinates});
|
||||||
|
} else if (mapItemType === 'Circle') {
|
||||||
|
attributes.push({key: 'circle', value: additionalParams.coordinates});
|
||||||
|
}
|
||||||
|
return attributes;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -274,7 +274,8 @@
|
|||||||
|| widgetActionFormGroup.get('type').value === widgetActionType.placeMapItem
|
|| widgetActionFormGroup.get('type').value === widgetActionType.placeMapItem
|
||||||
? widgetActionFormGroup.get('type').value : ''">
|
? widgetActionFormGroup.get('type').value : ''">
|
||||||
<tb-custom-action-pretty-editor
|
<tb-custom-action-pretty-editor
|
||||||
formControlName="customAction">
|
[widgetActionType]="widgetActionFormGroup.get('type').value"
|
||||||
|
formControlName="customAction">
|
||||||
</tb-custom-action-pretty-editor>
|
</tb-custom-action-pretty-editor>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
<ng-template [ngSwitchCase]="widgetActionType.mobileAction">
|
<ng-template [ngSwitchCase]="widgetActionType.mobileAction">
|
||||||
|
|||||||
@ -48,7 +48,8 @@ import { TranslateService } from '@ngx-translate/core';
|
|||||||
import { PopoverPlacement, PopoverPlacements } from '@shared/components/popover.models';
|
import { PopoverPlacement, PopoverPlacements } from '@shared/components/popover.models';
|
||||||
import {
|
import {
|
||||||
CustomActionEditorCompleter,
|
CustomActionEditorCompleter,
|
||||||
toCustomAction
|
toCustomAction,
|
||||||
|
toPlaceMapItemAction
|
||||||
} from '@home/components/widget/lib/settings/common/action/custom-action.models';
|
} from '@home/components/widget/lib/settings/common/action/custom-action.models';
|
||||||
import { coerceBoolean } from '@shared/decorators/coercion';
|
import { coerceBoolean } from '@shared/decorators/coercion';
|
||||||
|
|
||||||
@ -336,7 +337,7 @@ export class WidgetActionComponent implements ControlValueAccessor, OnInit, Vali
|
|||||||
);
|
);
|
||||||
this.actionTypeFormGroup.addControl(
|
this.actionTypeFormGroup.addControl(
|
||||||
'customAction',
|
'customAction',
|
||||||
this.fb.control(toCustomAction(action), [Validators.required])
|
this.fb.control(toPlaceMapItemAction(action), [Validators.required])
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -430,7 +430,7 @@
|
|||||||
<ng-container *ngTemplateOutlet="behavior"></ng-container>
|
<ng-container *ngTemplateOutlet="behavior"></ng-container>
|
||||||
}
|
}
|
||||||
<div class="tb-form-panel">
|
<div class="tb-form-panel">
|
||||||
<div class="tb-form-panel-title">{{ 'widgets.maps.data-layer.groups' | translate }}</div>
|
<div class="tb-form-panel-title" tb-hint-tooltip-icon="{{ 'widgets.maps.data-layer.groups-hint' | translate }}">{{ 'widgets.maps.data-layer.groups' | translate }}</div>
|
||||||
<tb-string-items-list class="tb-inline-chips"
|
<tb-string-items-list class="tb-inline-chips"
|
||||||
editable
|
editable
|
||||||
placeholder="{{'widgets.maps.data-layer.groups' | translate}}"
|
placeholder="{{'widgets.maps.data-layer.groups' | translate}}"
|
||||||
@ -462,7 +462,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="tb-form-row">
|
<div class="tb-form-row">
|
||||||
<mat-slide-toggle class="mat-slide" formControlName="snappable">
|
<mat-slide-toggle class="mat-slide" formControlName="snappable">
|
||||||
{{ 'widgets.maps.data-layer.enable-snapping' | translate }}
|
<span tb-hint-tooltip-icon="{{ 'widgets.maps.data-layer.enable-snapping-hint' | translate }}">
|
||||||
|
{{ 'widgets.maps.data-layer.enable-snapping' | translate }}
|
||||||
|
</span>
|
||||||
</mat-slide-toggle>
|
</mat-slide-toggle>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -36,7 +36,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="tb-form-panel">
|
<div class="tb-form-panel">
|
||||||
<div class="flex flex-row items-center justify-between xs:flex-col xs:items-start xs:gap-3">
|
<div class="flex flex-row items-center justify-between xs:flex-col xs:items-start xs:gap-3">
|
||||||
<div class="tb-form-panel-title">
|
<div class="tb-form-panel-title" tb-hint-tooltip-icon="{{ 'widgets.maps.overlays.overlays-hint' | translate }}">
|
||||||
{{ 'widgets.maps.overlays.overlays' | translate }}
|
{{ 'widgets.maps.overlays.overlays' | translate }}
|
||||||
</div>
|
</div>
|
||||||
<tb-toggle-select [(ngModel)]="dataLayerMode"
|
<tb-toggle-select [(ngModel)]="dataLayerMode"
|
||||||
@ -70,7 +70,7 @@
|
|||||||
[mapType]="mapSettingsFormGroup.get('mapType').value"></tb-map-data-layers>
|
[mapType]="mapSettingsFormGroup.get('mapType').value"></tb-map-data-layers>
|
||||||
</div>
|
</div>
|
||||||
<div class="tb-form-panel">
|
<div class="tb-form-panel">
|
||||||
<div class="tb-form-panel-title">
|
<div class="tb-form-panel-title" tb-hint-tooltip-icon="{{ 'widgets.maps.data-layer.additional-datasources-hint' | translate }}">
|
||||||
{{ 'widgets.maps.data-layer.additional-datasources' | translate }}
|
{{ 'widgets.maps.data-layer.additional-datasources' | translate }}
|
||||||
</div>
|
</div>
|
||||||
<tb-map-data-sources formControlName="additionalDataSources"
|
<tb-map-data-sources formControlName="additionalDataSources"
|
||||||
|
|||||||
@ -15,9 +15,9 @@
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
|
|
||||||
-->
|
-->
|
||||||
<mat-form-field [formGroup]="entityTypeFormGroup">
|
<mat-form-field [formGroup]="entityTypeFormGroup" [appearance]="appearance">
|
||||||
<mat-label *ngIf="showLabel">{{ 'entity.type' | translate }}</mat-label>
|
<mat-label *ngIf="showLabel">{{ 'entity.type' | translate }}</mat-label>
|
||||||
<mat-select [required]="required" matInput formControlName="entityType">
|
<mat-select [required]="required" formControlName="entityType">
|
||||||
<mat-option *ngFor="let type of entityTypes" [value]="type">
|
<mat-option *ngFor="let type of entityTypes" [value]="type">
|
||||||
{{ displayEntityTypeFn(type) }}
|
{{ displayEntityTypeFn(type) }}
|
||||||
</mat-option>
|
</mat-option>
|
||||||
|
|||||||
@ -14,24 +14,14 @@
|
|||||||
/// limitations under the License.
|
/// limitations under the License.
|
||||||
///
|
///
|
||||||
|
|
||||||
import {
|
import { Component, DestroyRef, forwardRef, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core';
|
||||||
AfterViewInit,
|
|
||||||
Component,
|
|
||||||
DestroyRef,
|
|
||||||
forwardRef,
|
|
||||||
Input,
|
|
||||||
OnChanges,
|
|
||||||
OnInit,
|
|
||||||
SimpleChanges
|
|
||||||
} from '@angular/core';
|
|
||||||
import { ControlValueAccessor, NG_VALUE_ACCESSOR, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
|
import { ControlValueAccessor, NG_VALUE_ACCESSOR, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
|
||||||
import { Store } from '@ngrx/store';
|
|
||||||
import { AppState } from '@app/core/core.state';
|
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { AliasEntityType, EntityType, entityTypeTranslations } from '@app/shared/models/entity-type.models';
|
import { AliasEntityType, EntityType, entityTypeTranslations } from '@app/shared/models/entity-type.models';
|
||||||
import { EntityService } from '@core/http/entity.service';
|
import { EntityService } from '@core/http/entity.service';
|
||||||
import { coerceBoolean } from '@shared/decorators/coercion';
|
import { coerceBoolean } from '@shared/decorators/coercion';
|
||||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||||
|
import { MatFormFieldAppearance } from '@angular/material/form-field';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'tb-entity-type-select',
|
selector: 'tb-entity-type-select',
|
||||||
@ -43,7 +33,7 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|||||||
multi: true
|
multi: true
|
||||||
}]
|
}]
|
||||||
})
|
})
|
||||||
export class EntityTypeSelectComponent implements ControlValueAccessor, OnInit, AfterViewInit, OnChanges {
|
export class EntityTypeSelectComponent implements ControlValueAccessor, OnInit, OnChanges {
|
||||||
|
|
||||||
entityTypeFormGroup: UntypedFormGroup;
|
entityTypeFormGroup: UntypedFormGroup;
|
||||||
|
|
||||||
@ -72,12 +62,14 @@ export class EntityTypeSelectComponent implements ControlValueAccessor, OnInit,
|
|||||||
@Input()
|
@Input()
|
||||||
additionEntityTypes: {[key in string]: string} = {};
|
additionEntityTypes: {[key in string]: string} = {};
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
appearance: MatFormFieldAppearance = 'fill';
|
||||||
|
|
||||||
entityTypes: Array<EntityType | AliasEntityType | string>;
|
entityTypes: Array<EntityType | AliasEntityType | string>;
|
||||||
|
|
||||||
private propagateChange = (v: any) => { };
|
private propagateChange = (v: any) => { };
|
||||||
|
|
||||||
constructor(private store: Store<AppState>,
|
constructor(private entityService: EntityService,
|
||||||
private entityService: EntityService,
|
|
||||||
public translate: TranslateService,
|
public translate: TranslateService,
|
||||||
private fb: UntypedFormBuilder,
|
private fb: UntypedFormBuilder,
|
||||||
private destroyRef: DestroyRef) {
|
private destroyRef: DestroyRef) {
|
||||||
@ -136,9 +128,6 @@ export class EntityTypeSelectComponent implements ControlValueAccessor, OnInit,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ngAfterViewInit(): void {
|
|
||||||
}
|
|
||||||
|
|
||||||
setDisabledState(isDisabled: boolean): void {
|
setDisabledState(isDisabled: boolean): void {
|
||||||
this.disabled = isDisabled;
|
this.disabled = isDisabled;
|
||||||
if (this.disabled) {
|
if (this.disabled) {
|
||||||
|
|||||||
@ -31,7 +31,14 @@ An optional key/value object holding additional entity parameters depending on w
|
|||||||
presenting <code>formattedTs</code> (a string value of formatted timestamp) and <br> timeseries values for each column declared in widget datasource configuration.
|
presenting <code>formattedTs</code> (a string value of formatted timestamp) and <br> timeseries values for each column declared in widget datasource configuration.
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
<li>Map widgets (<i>On marker/polygon/circle click</i> or <i>Tag action</i>) - <b>additionalParams</b>: <code><a href="https://github.com/thingsboard/thingsboard/blob/b881f1c2985399f9665e033e2479549e97da1f36/ui-ngx/src/app/shared/models/widget.models.ts#L513" target="_blank">FormattedData</a></code>:
|
||||||
|
<ul>
|
||||||
|
<li><b>additionalParams:</b> <code><a href="https://github.com/thingsboard/thingsboard/blob/b881f1c2985399f9665e033e2479549e97da1f36/ui-ngx/src/app/shared/models/widget.models.ts#L513" target="_blank">FormattedData</a></code> - An object associated with a data layer (markers, polygons, circles) or with a specific data point of a route (for trips data layers).<br/>
|
||||||
|
It contains basic entity properties (ex. <code>entityId</code>, <code>entityName</code>) and provides access to additional attributes and timeseries defined in datasource of the data layer configuration.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
<li>Entities hierarchy widget (<i>On node selected</i>) - <b>additionalParams:</b> <code>{ nodeCtx: <a href="https://github.com/thingsboard/thingsboard/blob/e264f7b8ddff05bda85c4833bf497f47f447496e/ui-ngx/src/app/modules/home/components/widget/lib/entities-hierarchy-widget.models.ts#L35" target="_blank">HierarchyNodeContext</a> }</code>:
|
<li>Entities hierarchy widget (<i>On node selected</i>) - <b>additionalParams:</b> <code>{ nodeCtx: <a href="https://github.com/thingsboard/thingsboard/blob/e264f7b8ddff05bda85c4833bf497f47f447496e/ui-ngx/src/app/modules/home/components/widget/lib/entities-hierarchy-widget.models.ts#L35" target="_blank">HierarchyNodeContext</a> }</code>:
|
||||||
<ul>
|
<ul>
|
||||||
<li><b>nodeCtx:</b> <code><a href="https://github.com/thingsboard/thingsboard/blob/e264f7b8ddff05bda85c4833bf497f47f447496e/ui-ngx/src/app/modules/home/components/widget/lib/entities-hierarchy-widget.models.ts#L35" target="_blank">HierarchyNodeContext</a></code> - An
|
<li><b>nodeCtx:</b> <code><a href="https://github.com/thingsboard/thingsboard/blob/e264f7b8ddff05bda85c4833bf497f47f447496e/ui-ngx/src/app/modules/home/components/widget/lib/entities-hierarchy-widget.models.ts#L35" target="_blank">HierarchyNodeContext</a></code> - An
|
||||||
|
|||||||
@ -0,0 +1,87 @@
|
|||||||
|
#### HTML template of dialog to create a device or an asset
|
||||||
|
|
||||||
|
```html
|
||||||
|
{:code-style="max-height: 400px;"}
|
||||||
|
<form #addEntityForm="ngForm" [formGroup]="addEntityFormGroup"
|
||||||
|
(ngSubmit)="save()" style="width: 552px">
|
||||||
|
<mat-toolbar class="flex flex-row" color="primary">
|
||||||
|
<h2>Add entity</h2>
|
||||||
|
<span class="flex-1"></span>
|
||||||
|
<button mat-icon-button (click)="cancel()" type="button">
|
||||||
|
<mat-icon class="material-icons">close</mat-icon>
|
||||||
|
</button>
|
||||||
|
</mat-toolbar>
|
||||||
|
<mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async">
|
||||||
|
</mat-progress-bar>
|
||||||
|
<div style="height: 4px;" *ngIf="!(isLoading$ | async)"></div>
|
||||||
|
<div mat-dialog-content class="flex flex-col" style="padding-bottom: 0">
|
||||||
|
<div class="flex flex-row gap-2 xs:flex-col xs:gap-0">
|
||||||
|
<mat-form-field class="mat-block flex-1" appearance="outline">
|
||||||
|
<mat-label>Entity Name</mat-label>
|
||||||
|
<input matInput formControlName="entityName" required>
|
||||||
|
<mat-error *ngIf="addEntityFormGroup.get('entityName').hasError('required')">
|
||||||
|
Entity name is required.
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field class="mat-block flex-1" appearance="outline">
|
||||||
|
<mat-label>Entity Label</mat-label>
|
||||||
|
<input matInput formControlName="entityLabel" >
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-row gap-2 xs:flex-col xs:gap-0">
|
||||||
|
<tb-entity-type-select
|
||||||
|
class="mat-block flex-1"
|
||||||
|
formControlName="entityType"
|
||||||
|
[showLabel]="true"
|
||||||
|
[appearance]="'outline'"
|
||||||
|
[allowedEntityTypes]="allowedEntityTypes"
|
||||||
|
></tb-entity-type-select>
|
||||||
|
<tb-entity-subtype-autocomplete
|
||||||
|
*ngIf="addEntityFormGroup.get('entityType').value == 'ASSET'"
|
||||||
|
class="mat-block flex-1"
|
||||||
|
formControlName="type"
|
||||||
|
[required]="true"
|
||||||
|
[entityType]="'ASSET'"
|
||||||
|
[appearance]="'outline'"
|
||||||
|
></tb-entity-subtype-autocomplete>
|
||||||
|
<tb-entity-subtype-autocomplete
|
||||||
|
*ngIf="addEntityFormGroup.get('entityType').value != 'ASSET'"
|
||||||
|
class="mat-block flex-1"
|
||||||
|
formControlName="type"
|
||||||
|
[required]="true"
|
||||||
|
[entityType]="'DEVICE'"
|
||||||
|
[appearance]="'outline'"
|
||||||
|
></tb-entity-subtype-autocomplete>
|
||||||
|
</div>
|
||||||
|
<div formGroupName="attributes" class="flex flex-col">
|
||||||
|
<div class="flex flex-row gap-2 xs:flex-col xs:gap-0">
|
||||||
|
<mat-form-field class="mat-block flex-1" appearance="outline">
|
||||||
|
<mat-label>Address</mat-label>
|
||||||
|
<input matInput formControlName="address">
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field class="mat-block flex-1" appearance="outline">
|
||||||
|
<mat-label>Owner</mat-label>
|
||||||
|
<input matInput formControlName="owner">
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div mat-dialog-actions class="flex flex-row items-center justify-end">
|
||||||
|
<button mat-button color="primary"
|
||||||
|
type="button"
|
||||||
|
[disabled]="(isLoading$ | async)"
|
||||||
|
(click)="cancel()" cdkFocusInitial>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
<button mat-button mat-raised-button color="primary"
|
||||||
|
type="submit"
|
||||||
|
[disabled]="(isLoading$ | async) || addEntityForm.invalid || !addEntityForm.dirty">
|
||||||
|
Create
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
{:copy-code}
|
||||||
|
```
|
||||||
|
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
@ -0,0 +1,94 @@
|
|||||||
|
#### Function displaying dialog to create a device or an asset
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
{:code-style="max-height: 400px;"}
|
||||||
|
let $injector = widgetContext.$scope.$injector;
|
||||||
|
let customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));
|
||||||
|
let assetService = $injector.get(widgetContext.servicesMap.get('assetService'));
|
||||||
|
let deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));
|
||||||
|
let attributeService = $injector.get(widgetContext.servicesMap.get('attributeService'));
|
||||||
|
|
||||||
|
openAddEntityDialog();
|
||||||
|
|
||||||
|
function openAddEntityDialog() {
|
||||||
|
customDialog.customDialog(htmlTemplate, AddEntityDialogController).subscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
function AddEntityDialogController(instance) {
|
||||||
|
let vm = instance;
|
||||||
|
|
||||||
|
vm.allowedEntityTypes = ['ASSET', 'DEVICE'];
|
||||||
|
|
||||||
|
vm.addEntityFormGroup = vm.fb.group({
|
||||||
|
entityName: ['', [vm.validators.required]],
|
||||||
|
entityType: ['DEVICE'],
|
||||||
|
entityLabel: [null],
|
||||||
|
type: ['', [vm.validators.required]],
|
||||||
|
attributes: vm.fb.group({
|
||||||
|
address: [null],
|
||||||
|
owner: [null]
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
vm.cancel = function() {
|
||||||
|
vm.dialogRef.close(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
vm.save = function() {
|
||||||
|
vm.addEntityFormGroup.markAsPristine();
|
||||||
|
saveEntityObservable().pipe(
|
||||||
|
widgetContext.rxjs.switchMap((entity) => saveAttributes(entity.id))
|
||||||
|
).subscribe(() => {
|
||||||
|
widgetContext.updateAliases();
|
||||||
|
vm.dialogRef.close(null);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
function saveEntityObservable() {
|
||||||
|
const formValues = vm.addEntityFormGroup.value;
|
||||||
|
let entity = {
|
||||||
|
name: formValues.entityName,
|
||||||
|
type: formValues.type,
|
||||||
|
label: formValues.entityLabel
|
||||||
|
};
|
||||||
|
if (formValues.entityType == 'ASSET') {
|
||||||
|
return assetService.saveAsset(entity);
|
||||||
|
} else if (formValues.entityType == 'DEVICE') {
|
||||||
|
return deviceService.saveDevice(entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveAttributes(entityId) {
|
||||||
|
let attributes = vm.addEntityFormGroup.get('attributes').value;
|
||||||
|
let attributesArray = getMapItemLocationAttributes();
|
||||||
|
for (let key in attributes) {
|
||||||
|
if(attributes[key] !== null) {
|
||||||
|
attributesArray.push({key: key, value: attributes[key]});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (attributesArray.length > 0) {
|
||||||
|
return attributeService.saveEntityAttributes(entityId, "SERVER_SCOPE", attributesArray);
|
||||||
|
}
|
||||||
|
return widgetContext.rxjs.of([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMapItemLocationAttributes() {
|
||||||
|
const attributes = [];
|
||||||
|
const mapItemType = $event.shape;
|
||||||
|
if (mapItemType === 'Marker') {
|
||||||
|
const mapType = widgetContext.mapInstance.type();
|
||||||
|
attributes.push({key: mapType === 'image' ? 'xPos' : 'latitude', value: additionalParams.coordinates.x});
|
||||||
|
attributes.push({key: mapType === 'image' ? 'yPos' : 'longitude', value: additionalParams.coordinates.y});
|
||||||
|
} else if (mapItemType === 'Rectangle' || mapItemType === 'Polygon') {
|
||||||
|
attributes.push({key: 'perimeter', value: additionalParams.coordinates});
|
||||||
|
} else if (mapItemType === 'Circle') {
|
||||||
|
attributes.push({key: 'circle', value: additionalParams.coordinates});
|
||||||
|
}
|
||||||
|
return attributes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{:copy-code}
|
||||||
|
```
|
||||||
|
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
@ -0,0 +1,66 @@
|
|||||||
|
#### Place map item function
|
||||||
|
|
||||||
|
<div class="divider"></div>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
*function ($event, widgetContext, entityId, entityName, htmlTemplate, additionalParams, entityLabel): void*
|
||||||
|
|
||||||
|
A JavaScript function triggered after a map item is placed. Optionally uses an HTML template to render dialog.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
|
||||||
|
<ul style="width: 700px">
|
||||||
|
<li><b>$event:</b> <code>{shape: <a href="https://github.com/geoman-io/leaflet-geoman/blob/6335a8c6cbebfcd06707d3c5da9d3d393cd2d942/leaflet-geoman.d.ts#L829" target="_blank">PM.SUPPORTED_SHAPES</a>; layer: <a href="https://leafletjs.com/reference.html#layer" target="_blank">L.Layer</a>}</code> - Event payload containing the created shape type and its associated map layer.
|
||||||
|
</li>
|
||||||
|
<li><b>widgetContext:</b> <code><a href="https://github.com/thingsboard/thingsboard/blob/5bb6403407aa4898084832d6698aa9ea6d484889/ui-ngx/src/app/modules/home/models/widget-component.models.ts#L107" target="_blank">WidgetContext</a></code> - A reference to <a href="https://github.com/thingsboard/thingsboard/blob/5bb6403407aa4898084832d6698aa9ea6d484889/ui-ngx/src/app/modules/home/models/widget-component.models.ts#L107" target="_blank">WidgetContext</a> that has all necessary API
|
||||||
|
and data used by widget instance.
|
||||||
|
</li>
|
||||||
|
<li><b>entityId:</b> <code>string</code> - An optional string id of the target entity.
|
||||||
|
</li>
|
||||||
|
<li><b>entityName:</b> <code>string</code> - An optional string name of the target entity.
|
||||||
|
</li>
|
||||||
|
<li><b>htmlTemplate:</b> <code>string</code> - An optional HTML template string defined in <b>HTML</b> tab.<br/> Used to render custom dialog (see <b>Examples</b> for more details).
|
||||||
|
</li>
|
||||||
|
<li><b>additionalParams</b>: <code>{coordinates: Coordinates; layer: <a href="https://leafletjs.com/reference.html#layer" target="_blank">L.Layer</a>}</code>:
|
||||||
|
<ul>
|
||||||
|
<li><b>coordinates:</b> <code>Coordinates</code> - Represents geographical coordinates of the placed map item. The actual format of this parameter depends on the type of the selected map item:
|
||||||
|
<ul>
|
||||||
|
<li><b>Marker:</b> <code>{x: number; y: number}</code>, where <code>x</code> represents latitude, and <code>y</code> represents longitude.</li>
|
||||||
|
<li><b>Polygon, Rectangle:</b> <code><a href="https://github.com/thingsboard/thingsboard/blob/61254a68507c6def8c055b7b3ae70413c456a4ac/ui-ngx/src/app/shared/models/widget/maps/map.models.ts#L1099" target="_blank">TbPolygonRawCoordinates</a></code> contains an array of points defining the shape boundaries.</li>
|
||||||
|
<li><b>Circle:</b> <code><a href="https://github.com/thingsboard/thingsboard/blob/61254a68507c6def8c055b7b3ae70413c456a4ac/ui-ngx/src/app/shared/models/widget/maps/map.models.ts#L1104" target="_blank">TbCircleData</a></code> contains center coordinates and radius information.</li>
|
||||||
|
</ul>
|
||||||
|
Note: The coordinates will be automatically converted according to the selected map type.
|
||||||
|
</li>
|
||||||
|
<li><b>layer:</b> <code><a href="https://leafletjs.com/reference.html#layer" target="_blank">L.Layer</a></code> - The Leaflet map layer instance (e.g., marker, polygon, circle) associated with the placed map item. This object provides access to layer properties and methods defined in Leaflet's API.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li><b>entityLabel:</b> <code>string</code> - An optional string label of the target entity.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="divider"></div>
|
||||||
|
|
||||||
|
##### Examples
|
||||||
|
|
||||||
|
###### Display dialog to create a device or an asset
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<div style="padding-left: 64px;"
|
||||||
|
tb-help-popup="widget/action/place_map_item/create_dialog_js"
|
||||||
|
tb-help-popup-placement="top"
|
||||||
|
[tb-help-popup-style]="{maxHeight: '50vh', maxWidth: '50vw'}"
|
||||||
|
trigger-style="font-size: 16px;"
|
||||||
|
trigger-text="JavaScript function">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<div style="padding-left: 64px;"
|
||||||
|
tb-help-popup="widget/action/place_map_item/create_dialog_html"
|
||||||
|
tb-help-popup-placement="top"
|
||||||
|
[tb-help-popup-style]="{maxHeight: '50vh', maxWidth: '50vw'}"
|
||||||
|
trigger-style="font-size: 16px;"
|
||||||
|
trigger-text="HTML code">
|
||||||
|
</div>
|
||||||
@ -7976,6 +7976,7 @@
|
|||||||
},
|
},
|
||||||
"overlays": {
|
"overlays": {
|
||||||
"overlays": "Overlays",
|
"overlays": "Overlays",
|
||||||
|
"overlays-hint": "Configure datasources, appearance, behavior, editing options, and grouping for map entities",
|
||||||
"trips": "Trips",
|
"trips": "Trips",
|
||||||
"markers": "Markers",
|
"markers": "Markers",
|
||||||
"polygons": "Polygons",
|
"polygons": "Polygons",
|
||||||
@ -7985,6 +7986,7 @@
|
|||||||
"source": "Source",
|
"source": "Source",
|
||||||
"additional-data-keys": "Additional data keys",
|
"additional-data-keys": "Additional data keys",
|
||||||
"additional-datasources": "Additional datasources",
|
"additional-datasources": "Additional datasources",
|
||||||
|
"additional-datasources-hint": "Datasource for accessing attributes or telemetry from entities not displayed on the map, usable in map overlay functions.",
|
||||||
"data-keys": "Data keys",
|
"data-keys": "Data keys",
|
||||||
"add-datasource": "Add datasource",
|
"add-datasource": "Add datasource",
|
||||||
"no-datasources": "No datasources configured",
|
"no-datasources": "No datasources configured",
|
||||||
@ -7993,6 +7995,7 @@
|
|||||||
"on-click": "On click",
|
"on-click": "On click",
|
||||||
"on-click-hint": "Action invoked when user clicks on the map item.",
|
"on-click-hint": "Action invoked when user clicks on the map item.",
|
||||||
"groups": "Groups",
|
"groups": "Groups",
|
||||||
|
"groups-hint": "List of group names assigned to the overlay, used to toggle its visibility on the map.",
|
||||||
"color": "Color",
|
"color": "Color",
|
||||||
"fill-color": "Fill color",
|
"fill-color": "Fill color",
|
||||||
"stroke": "Stroke",
|
"stroke": "Stroke",
|
||||||
@ -8030,6 +8033,7 @@
|
|||||||
"edit-instruments": "Instruments",
|
"edit-instruments": "Instruments",
|
||||||
"persist-location-attribute-scope": "Scope of the attribute to persist location",
|
"persist-location-attribute-scope": "Scope of the attribute to persist location",
|
||||||
"enable-snapping": "Enable snapping to other vertices for precision drawing",
|
"enable-snapping": "Enable snapping to other vertices for precision drawing",
|
||||||
|
"enable-snapping-hint": "Automatically aligns new points with existing shapes to make drawing easier and more accurate.",
|
||||||
"drag-drop-mode": "Drag-drop mode",
|
"drag-drop-mode": "Drag-drop mode",
|
||||||
"trip": {
|
"trip": {
|
||||||
"no-trips": "No trips configured",
|
"no-trips": "No trips configured",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user