UI: Improve map widget import/export. Add help assets for new maps.

This commit is contained in:
Igor Kulikov 2025-03-05 18:42:25 +02:00
parent dabd7cca5b
commit e5b8a24598
93 changed files with 1214 additions and 349 deletions

View File

@ -16,7 +16,7 @@
import { Injectable } from '@angular/core';
import { BreakpointId, Dashboard, DashboardLayoutId } from '@app/shared/models/dashboard.models';
import { AliasesInfo, EntityAlias, EntityAliases, EntityAliasInfo } from '@shared/models/alias.models';
import { AliasesInfo, EntityAlias, EntityAliases, EntityAliasInfo, getEntityAliasId } from '@shared/models/alias.models';
import {
Datasource,
DatasourceType,
@ -34,7 +34,8 @@ import { map } from 'rxjs/operators';
import { FcRuleNode, ruleNodeTypeDescriptors } from '@shared/models/rule-node.models';
import { RuleChainService } from '@core/http/rule-chain.service';
import { RuleChainImport } from '@shared/models/rule-chain.models';
import { Filter, FilterInfo, Filters, FiltersInfo } from '@shared/models/query/query.models';
import { Filter, FilterInfo, Filters, FiltersInfo, getFilterId } from '@shared/models/query/query.models';
import { getWidgetExportDefinition } from '@shared/models/widget/widget-export.models';
const WIDGET_ITEM = 'widget_item';
const WIDGET_REFERENCE = 'widget_reference';
@ -47,6 +48,7 @@ export interface WidgetItem {
filtersInfo: FiltersInfo;
originalSize: WidgetSize;
originalColumns: number;
widgetExportInfo?: any;
}
export interface WidgetReference {
@ -139,12 +141,18 @@ export class ItemBufferService {
}
}
}
let widgetExportInfo: any;
const exportDefinition = getWidgetExportDefinition(widget);
if (exportDefinition) {
widgetExportInfo = exportDefinition.prepareExportInfo(dashboard, widget);
}
return {
widget,
aliasesInfo,
filtersInfo,
originalSize,
originalColumns
originalColumns,
widgetExportInfo
};
}
@ -189,6 +197,7 @@ export class ItemBufferService {
const filtersInfo = widgetItem.filtersInfo;
const originalColumns = widgetItem.originalColumns;
const originalSize = widgetItem.originalSize;
const widgetExportInfo = widgetItem.widgetExportInfo;
let targetRow = -1;
let targetColumn = -1;
if (position) {
@ -199,7 +208,7 @@ export class ItemBufferService {
return this.addWidgetToDashboard(targetDashboard, targetState,
targetLayout, widget, aliasesInfo, filtersInfo,
onAliasesUpdateFunction, onFiltersUpdateFunction,
originalColumns, originalSize, targetRow, targetColumn, breakpoint).pipe(
originalColumns, originalSize, targetRow, targetColumn, breakpoint, widgetExportInfo).pipe(
map(() => widget)
);
} else {
@ -248,7 +257,8 @@ export class ItemBufferService {
originalSize: WidgetSize,
row: number,
column: number,
breakpoint = 'default'): Observable<Dashboard> {
breakpoint = 'default',
widgetExportInfo?: any): Observable<Dashboard> {
let theDashboard: Dashboard;
if (dashboard) {
theDashboard = dashboard;
@ -258,26 +268,39 @@ export class ItemBufferService {
theDashboard = this.dashboardUtils.validateAndUpdateDashboard(theDashboard);
let callAliasUpdateFunction = false;
let callFilterUpdateFunction = false;
let newEntityAliases: EntityAliases;
let newFilters: Filters;
const exportDefinition = getWidgetExportDefinition(widget);
if (exportDefinition && widgetExportInfo || aliasesInfo) {
newEntityAliases = deepClone(dashboard.configuration.entityAliases);
}
if (exportDefinition && widgetExportInfo || filtersInfo) {
newFilters = deepClone(dashboard.configuration.filters);
}
if (aliasesInfo) {
const newEntityAliases = this.updateAliases(theDashboard, widget, aliasesInfo);
const aliasesUpdated = !isEqual(newEntityAliases, theDashboard.configuration.entityAliases);
if (aliasesUpdated) {
theDashboard.configuration.entityAliases = newEntityAliases;
if (onAliasesUpdateFunction) {
callAliasUpdateFunction = true;
}
}
this.updateAliases(widget, newEntityAliases, aliasesInfo);
}
if (filtersInfo) {
const newFilters = this.updateFilters(theDashboard, widget, filtersInfo);
const filtersUpdated = !isEqual(newFilters, theDashboard.configuration.filters);
if (filtersUpdated) {
theDashboard.configuration.filters = newFilters;
if (onFiltersUpdateFunction) {
callFilterUpdateFunction = true;
}
this.updateFilters(widget, newFilters, filtersInfo);
}
if (exportDefinition && widgetExportInfo) {
exportDefinition.updateFromExportInfo(widget, newEntityAliases, newFilters, widgetExportInfo);
}
const aliasesUpdated = newEntityAliases && !isEqual(newEntityAliases, theDashboard.configuration.entityAliases);
if (aliasesUpdated) {
theDashboard.configuration.entityAliases = newEntityAliases;
if (onAliasesUpdateFunction) {
callAliasUpdateFunction = true;
}
}
const filtersUpdated = newFilters && !isEqual(newFilters, theDashboard.configuration.filters);
if (filtersUpdated) {
theDashboard.configuration.filters = newFilters;
if (onFiltersUpdateFunction) {
callFilterUpdateFunction = true;
}
}
this.dashboardUtils.addWidgetToLayout(theDashboard, targetState, targetLayout, widget,
originalColumns, originalSize, row, column, breakpoint);
if (callAliasUpdateFunction) {
@ -430,14 +453,13 @@ export class ItemBufferService {
};
}
private updateAliases(dashboard: Dashboard, widget: Widget, aliasesInfo: AliasesInfo): EntityAliases {
const entityAliases = deepClone(dashboard.configuration.entityAliases);
private updateAliases(widget: Widget, entityAliases: EntityAliases, aliasesInfo: AliasesInfo): void {
let aliasInfo: EntityAliasInfo;
let newAliasId: string;
for (const datasourceIndexStr of Object.keys(aliasesInfo.datasourceAliases)) {
const datasourceIndex = Number(datasourceIndexStr);
aliasInfo = aliasesInfo.datasourceAliases[datasourceIndex];
newAliasId = this.getEntityAliasId(entityAliases, aliasInfo);
newAliasId = getEntityAliasId(entityAliases, aliasInfo);
if (widget.type === widgetType.alarm) {
widget.config.alarmSource.entityAliasId = newAliasId;
} else {
@ -446,7 +468,7 @@ export class ItemBufferService {
}
if (aliasesInfo.targetDeviceAlias) {
aliasInfo = aliasesInfo.targetDeviceAlias;
newAliasId = this.getEntityAliasId(entityAliases, aliasInfo);
newAliasId = getEntityAliasId(entityAliases, aliasInfo);
if (widget.config.targetDevice?.type !== TargetDeviceType.entity) {
widget.config.targetDevice = {
type: TargetDeviceType.entity
@ -454,101 +476,21 @@ export class ItemBufferService {
}
widget.config.targetDevice.entityAliasId = newAliasId;
}
return entityAliases;
}
private updateFilters(dashboard: Dashboard, widget: Widget, filtersInfo: FiltersInfo): Filters {
const filters = deepClone(dashboard.configuration.filters);
private updateFilters(widget: Widget, filters: Filters, filtersInfo: FiltersInfo): void {
let filterInfo: FilterInfo;
let newFilterId: string;
for (const datasourceIndexStr of Object.keys(filtersInfo.datasourceFilters)) {
const datasourceIndex = Number(datasourceIndexStr);
filterInfo = filtersInfo.datasourceFilters[datasourceIndex];
newFilterId = this.getFilterId(filters, filterInfo);
newFilterId = getFilterId(filters, filterInfo);
if (widget.type === widgetType.alarm) {
widget.config.alarmSource.filterId = newFilterId;
} else {
widget.config.datasources[datasourceIndex].filterId = newFilterId;
}
}
return filters;
}
private isEntityAliasEqual(alias1: EntityAliasInfo, alias2: EntityAliasInfo): boolean {
return isEqual(alias1.filter, alias2.filter);
}
private getEntityAliasId(entityAliases: EntityAliases, aliasInfo: EntityAliasInfo): string {
let newAliasId: string;
for (const aliasId of Object.keys(entityAliases)) {
if (this.isEntityAliasEqual(entityAliases[aliasId], aliasInfo)) {
newAliasId = aliasId;
break;
}
}
if (!newAliasId) {
const newAliasName = this.createEntityAliasName(entityAliases, aliasInfo.alias);
newAliasId = this.utils.guid();
entityAliases[newAliasId] = {id: newAliasId, alias: newAliasName, filter: aliasInfo.filter};
}
return newAliasId;
}
private createEntityAliasName(entityAliases: EntityAliases, alias: string): string {
let c = 0;
let newAlias = alias;
let unique = false;
while (!unique) {
unique = true;
for (const entAliasId of Object.keys(entityAliases)) {
const entAlias = entityAliases[entAliasId];
if (newAlias === entAlias.alias) {
c++;
newAlias = alias + c;
unique = false;
}
}
}
return newAlias;
}
private isFilterEqual(filter1: FilterInfo, filter2: FilterInfo): boolean {
return isEqual(filter1.keyFilters, filter2.keyFilters);
}
private getFilterId(filters: Filters, filterInfo: FilterInfo): string {
let newFilterId: string;
for (const filterId of Object.keys(filters)) {
if (this.isFilterEqual(filters[filterId], filterInfo)) {
newFilterId = filterId;
break;
}
}
if (!newFilterId) {
const newFilterName = this.createFilterName(filters, filterInfo.filter);
newFilterId = this.utils.guid();
filters[newFilterId] = {id: newFilterId, filter: newFilterName,
keyFilters: filterInfo.keyFilters, editable: filterInfo.editable};
}
return newFilterId;
}
private createFilterName(filters: Filters, filter: string): string {
let c = 0;
let newFilter = filter;
let unique = false;
while (!unique) {
unique = true;
for (const entFilterId of Object.keys(filters)) {
const entFilter = filters[entFilterId];
if (newFilter === entFilter.filter) {
c++;
newFilter = filter + c;
unique = false;
}
}
}
return newFilter;
}
private storeSet(key: string, elem: any) {

View File

@ -20,7 +20,7 @@ import {
isJSON, MapDataLayerType,
TbCircleData,
TbMapDatasource
} from '@home/components/widget/lib/maps/models/map.models';
} from '@shared/models/widget/maps/map.models';
import L from 'leaflet';
import { FormattedData } from '@shared/models/widget.models';
import { TbShapesDataLayer } from '@home/components/widget/lib/maps/data-layer/shapes-data-layer';

View File

@ -18,7 +18,7 @@ import {
DataLayerTooltipSettings,
DataLayerTooltipTrigger, processTooltipTemplate,
TbMapDatasource
} from '@home/components/widget/lib/maps/models/map.models';
} from '@shared/models/widget/maps/map.models';
import { TbMap } from '@home/components/widget/lib/maps/map';
import { FormattedData } from '@shared/models/widget.models';
import L from 'leaflet';

View File

@ -18,7 +18,7 @@ import {
DataLayerEditAction,
MapDataLayerSettings,
TbMapDatasource
} from '@home/components/widget/lib/maps/models/map.models';
} from '@shared/models/widget/maps/map.models';
import { TbMap } from '@home/components/widget/lib/maps/map';
import { FormattedData, WidgetActionType } from '@shared/models/widget.models';
import { Observable } from 'rxjs';

View File

@ -20,7 +20,7 @@ import {
MapDataLayerSettings, MapDataLayerType, mapDataSourceSettingsToDatasource,
MapStringFunction, MapType,
TbMapDatasource
} from '@home/components/widget/lib/maps/models/map.models';
} from '@shared/models/widget/maps/map.models';
import {
createLabelFromPattern,
guid, isDefined,
@ -82,6 +82,7 @@ export class DataLayerColorProcessor {
private settings: DataLayerColorSettings) {}
public setup(): Observable<void> {
this.color = this.settings.color;
if (this.settings.type === DataLayerColorType.function) {
return parseTbFunction<MapStringFunction>(this.dataLayer.getCtx().http, this.settings.colorFunction, ['data', 'dsData']).pipe(
map((parsed) => {
@ -90,7 +91,6 @@ export class DataLayerColorProcessor {
})
);
} else {
this.color = this.settings.color;
return of(null)
}
}
@ -99,6 +99,9 @@ export class DataLayerColorProcessor {
let color: string;
if (this.settings.type === DataLayerColorType.function) {
color = safeExecuteTbFunction(this.colorFunction, [data, dsData]);
if (!color) {
color = this.color;
}
} else {
color = this.color;
}

View File

@ -34,7 +34,7 @@ import {
MarkerShapeSettings,
MarkerType,
TbMapDatasource
} from '@home/components/widget/lib/maps/models/map.models';
} from '@shared/models/widget/maps/map.models';
import L, { FeatureGroup } from 'leaflet';
import { FormattedData } from '@shared/models/widget.models';
import { forkJoin, Observable, of } from 'rxjs';
@ -55,7 +55,7 @@ import {
createColorMarkerIconElement,
createColorMarkerShapeURI,
MarkerShape
} from '@home/components/widget/lib/maps/models/marker-shape.models';
} from '@shared/models/widget/maps/marker-shape.models';
import { MatIconRegistry } from '@angular/material/icon';
import { DomSanitizer } from '@angular/platform-browser';
import {

View File

@ -19,7 +19,7 @@ import {
isCutPolygon, isJSON, MapDataLayerType,
PolygonsDataLayerSettings,
TbMapDatasource, TbPolyData, TbPolygonCoordinates, TbPolygonRawCoordinates
} from '@home/components/widget/lib/maps/models/map.models';
} from '@shared/models/widget/maps/map.models';
import L from 'leaflet';
import { FormattedData } from '@shared/models/widget.models';
import { TbShapesDataLayer } from '@home/components/widget/lib/maps/data-layer/shapes-data-layer';

View File

@ -14,7 +14,7 @@
/// limitations under the License.
///
import { ShapeDataLayerSettings, TbMapDatasource } from '@home/components/widget/lib/maps/models/map.models';
import { ShapeDataLayerSettings, TbMapDatasource } from '@shared/models/widget/maps/map.models';
import L from 'leaflet';
import { TbMap } from '@home/components/widget/lib/maps/map';
import { forkJoin, Observable } from 'rxjs';

View File

@ -23,7 +23,7 @@ import {
MapDataLayerType,
TbMapDatasource,
TripsDataLayerSettings
} from '@home/components/widget/lib/maps/models/map.models';
} from '@shared/models/widget/maps/map.models';
import { forkJoin, Observable } from 'rxjs';
import { FormattedData, WidgetActionType } from '@shared/models/widget.models';
import { map } from 'rxjs/operators';

View File

@ -25,7 +25,7 @@ import {
TbPolygonCoordinates,
TbPolygonRawCoordinate,
TbPolygonRawCoordinates
} from '@home/components/widget/lib/maps/models/map.models';
} from '@shared/models/widget/maps/map.models';
import { WidgetContext } from '@home/models/widget-component.models';
import { DeepPartial } from '@shared/models/common';
import { forkJoin, Observable, of } from 'rxjs';

View File

@ -24,7 +24,7 @@ import {
loadImageWithAspect,
MapZoomAction,
TbCircleData, TbPolygonCoordinate, TbPolygonCoordinates, TbPolygonRawCoordinate, TbPolygonRawCoordinates
} from '@home/components/widget/lib/maps/models/map.models';
} from '@shared/models/widget/maps/map.models';
import { WidgetContext } from '@home/models/widget-component.models';
import { DeepPartial } from '@shared/models/common';
import { Observable, of, ReplaySubject, switchMap } from 'rxjs';

View File

@ -28,7 +28,7 @@ import {
MapProvider,
OpenStreetMapLayerSettings,
TencentMapLayerSettings
} from '@home/components/widget/lib/maps/models/map.models';
} from '@shared/models/widget/maps/map.models';
import { WidgetContext } from '@home/models/widget-component.models';
import { DeepPartial } from '@shared/models/common';
import { mergeDeep } from '@core/utils';

View File

@ -15,14 +15,12 @@
///
import {
AfterViewInit,
ChangeDetectorRef,
Component,
ElementRef,
Input,
OnDestroy,
OnInit,
Renderer2,
TemplateRef,
ViewChild,
ViewEncapsulation
@ -36,7 +34,7 @@ import { WidgetContext } from '@home/models/widget-component.models';
import { Observable } from 'rxjs';
import { backgroundStyle, ComponentStyle, overlayStyle } from '@shared/models/widget-settings.models';
import { TbMap } from '@home/components/widget/lib/maps/map';
import { MapSetting } from '@home/components/widget/lib/maps/models/map.models';
import { MapSetting } from '@shared/models/widget/maps/map.models';
import { WidgetComponent } from '@home/components/widget/widget.component';
import { ImagePipe } from '@shared/pipe/image.pipe';
import { DomSanitizer } from '@angular/platform-browser';
@ -69,7 +67,6 @@ export class MapWidgetComponent implements OnInit, OnDestroy {
constructor(public widgetComponent: WidgetComponent,
private imagePipe: ImagePipe,
private sanitizer: DomSanitizer,
private renderer: Renderer2,
private cd: ChangeDetectorRef) {
}

View File

@ -14,7 +14,7 @@
/// limitations under the License.
///
import { defaultMapSettings, MapSetting, MapType } from '@home/components/widget/lib/maps/models/map.models';
import { defaultMapSettings, MapSetting, MapType } from '@shared/models/widget/maps/map.models';
import { BackgroundSettings, BackgroundType } from '@shared/models/widget-settings.models';
import { mergeDeep } from '@core/utils';
import { WidgetContext } from '@home/models/widget-component.models';

View File

@ -27,7 +27,7 @@ import {
TbMapDatasource,
TbPolygonCoordinates,
TbPolygonRawCoordinates
} from '@home/components/widget/lib/maps/models/map.models';
} from '@shared/models/widget/maps/map.models';
import { WidgetContext } from '@home/models/widget-component.models';
import {
formattedDataArrayFromDatasourceData,
@ -63,7 +63,7 @@ import {
SelectMapEntityPanelComponent
} from '@home/components/widget/lib/maps/panels/select-map-entity-panel.component';
import { TbPopoverComponent } from '@shared/components/popover.component';
import { createPlaceItemIcon } from '@home/components/widget/lib/maps/models/marker-shape.models';
import { createPlaceItemIcon } from '@shared/models/widget/maps/marker-shape.models';
import { MatIconRegistry } from '@angular/material/icon';
import { DomSanitizer } from '@angular/platform-browser';
import { MapTimelinePanelComponent } from '@home/components/widget/lib/maps/panels/map-timeline-panel.component';
@ -464,7 +464,7 @@ export abstract class TbMap<S extends BaseMapSettings> {
}
}
private toggleDragMode(e: MouseEvent, button: L.TB.ToolbarButton): void {
private toggleDragMode(_e: MouseEvent, button: L.TB.ToolbarButton): void {
if (this.dragMode) {
this.disableDragMode();
} else {

View File

@ -25,7 +25,7 @@ import {
Output,
ViewEncapsulation
} from '@angular/core';
import { TripTimelineSettings } from '@home/components/widget/lib/maps/models/map.models';
import { TripTimelineSettings } from '@shared/models/widget/maps/map.models';
import { DateFormatProcessor } from '@shared/models/widget-settings.models';
import { interval, Subscription } from 'rxjs';
import { filter } from 'rxjs/operators';

View File

@ -63,7 +63,7 @@
[functionArgs]="['data', 'dsData']"
[globalVariables]="functionScopeVariables"
functionTitle="{{ 'widgets.maps.data-layer.color-function' | translate }}"
helpId="widget/lib/map/color_fn">
[helpId]="helpId">
</tb-js-func>
</div>
</ng-template>

View File

@ -22,7 +22,7 @@ import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { WidgetService } from '@core/http/widget.service';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { DataLayerColorSettings, DataLayerColorType } from '@home/components/widget/lib/maps/models/map.models';
import { DataLayerColorSettings, DataLayerColorType } from '@shared/models/widget/maps/map.models';
@Component({
selector: 'tb-data-layer-color-settings-panel',
@ -36,6 +36,9 @@ export class DataLayerColorSettingsPanelComponent extends PageComponent implemen
@Input()
colorSettings: DataLayerColorSettings;
@Input()
helpId = 'widget/lib/map/color_fn';
@Input()
popover: TbPopoverComponent<DataLayerColorSettingsPanelComponent>;

View File

@ -19,7 +19,7 @@ import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { ComponentStyle } from '@shared/models/widget-settings.models';
import { MatButton } from '@angular/material/button';
import { TbPopoverService } from '@shared/components/popover.service';
import { DataLayerColorSettings, DataLayerColorType } from '@home/components/widget/lib/maps/models/map.models';
import { DataLayerColorSettings, DataLayerColorType } from '@shared/models/widget/maps/map.models';
import {
DataLayerColorSettingsPanelComponent
} from '@home/components/widget/lib/settings/common/map/data-layer-color-settings-panel.component';
@ -41,6 +41,9 @@ export class DataLayerColorSettingsComponent implements ControlValueAccessor {
@Input()
disabled: boolean;
@Input()
helpId = 'widget/lib/map/color_fn';
DataLayerColorType = DataLayerColorType;
modelValue: DataLayerColorSettings;
@ -82,6 +85,7 @@ export class DataLayerColorSettingsComponent implements ControlValueAccessor {
} else {
const ctx: any = {
colorSettings: this.modelValue,
helpId: this.helpId
};
const colorSettingsPanelPopover = this.popoverService.displayPopover(trigger, this.renderer,
this.viewContainerRef, DataLayerColorSettingsPanelComponent, 'left', true, null,

View File

@ -46,7 +46,7 @@
[globalVariables]="functionScopeVariables"
[functionArgs]="['data', 'dsData']"
functionTitle="{{ (patternType === 'label' ? 'widgets.maps.data-layer.label-function' : 'widgets.maps.data-layer.tooltip-function') | translate }}"
helpId="{{ patternType === 'label' ? 'widget/lib/map/label_fn' : 'widget/lib/map/tooltip_fn' }}">
[helpId]="helpId">
</tb-js-func>
<ng-container *ngIf="patternType === 'tooltip'">
<div class="tb-form-row space-between column-xs">

View File

@ -32,7 +32,7 @@ import {
DataLayerPatternSettings,
DataLayerPatternType,
DataLayerTooltipSettings, dataLayerTooltipTriggers, dataLayerTooltipTriggerTranslationMap
} from '@home/components/widget/lib/maps/models/map.models';
} from '@shared/models/widget/maps/map.models';
import { coerceBoolean } from '@shared/decorators/coercion';
import { MapSettingsContext } from '@home/components/widget/lib/settings/common/map/map-settings.component.models';
@ -71,6 +71,9 @@ export class DataLayerPatternSettingsComponent implements OnInit, ControlValueAc
@Input()
patternType: 'label' | 'tooltip' = 'label';
@Input()
helpId = 'widget/lib/map/label_fn';
@Input()
patternTitle: string;

View File

@ -14,7 +14,7 @@
/// limitations under the License.
///
import { Component, DestroyRef, forwardRef, Input, OnInit, ViewEncapsulation } from '@angular/core';
import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core';
import {
ControlValueAccessor,
NG_VALIDATORS,
@ -26,7 +26,7 @@ import {
Validators
} from '@angular/forms';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { ImageMapSourceSettings, ImageSourceType } from '@home/components/widget/lib/maps/models/map.models';
import { ImageMapSourceSettings, ImageSourceType } from '@shared/models/widget/maps/map.models';
import { DataKey, DatasourceType, widgetType } from '@shared/models/widget.models';
import { MapSettingsContext } from '@home/components/widget/lib/settings/common/map/map-settings.component.models';
import { DataKeyType } from '@shared/models/telemetry/telemetry.models';

View File

@ -24,7 +24,7 @@ import {
ValidationErrors,
Validator
} from '@angular/forms';
import { MapActionButtonSettings } from '@home/components/widget/lib/maps/models/map.models';
import { MapActionButtonSettings } from '@shared/models/widget/maps/map.models';
import { WidgetAction, WidgetActionType, widgetType } from '@shared/models/widget.models';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { isEmptyStr } from '@core/utils';

View File

@ -26,7 +26,7 @@ import {
import {
defaultMapActionButtonSettings,
MapActionButtonSettings
} from '@home/components/widget/lib/maps/models/map.models';
} from '@shared/models/widget/maps/map.models';
import { CdkDragDrop } from '@angular/cdk/drag-drop';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';

View File

@ -256,7 +256,7 @@
<input matInput type="number" min="0" formControlName="pathStrokeWeight" placeholder="{{ 'widget-config.set' | translate }}">
<span matSuffix>px</span>
</mat-form-field>
<tb-data-layer-color-settings formControlName="pathStrokeColor"></tb-data-layer-color-settings>
<tb-data-layer-color-settings helpId="widget/lib/map/path_color_fn" formControlName="pathStrokeColor"></tb-data-layer-color-settings>
</div>
</div>
<div class="tb-form-panel tb-slide-toggle stroked">
@ -334,13 +334,14 @@
<input matInput type="number" min="0" formControlName="pointSize" placeholder="{{ 'widget-config.set' | translate }}">
<span matSuffix>px</span>
</mat-form-field>
<tb-data-layer-color-settings formControlName="pointColor"></tb-data-layer-color-settings>
<tb-data-layer-color-settings helpId="widget/lib/map/path_point_color_fn" formControlName="pointColor"></tb-data-layer-color-settings>
</div>
</div>
<tb-data-layer-pattern-settings
patternType="tooltip"
patternTitle="{{ 'widgets.maps.data-layer.points.point-tooltip' | translate }}"
[context]="context"
helpId="widget/lib/map/path_point_tooltip_fn"
formControlName="pointTooltip">
</tb-data-layer-pattern-settings>
</ng-template>
@ -350,7 +351,7 @@
<ng-container *ngIf="['polygons', 'circles'].includes(dataLayerType)">
<div class="tb-form-row space-between">
<div translate>widgets.maps.data-layer.fill-color</div>
<tb-data-layer-color-settings formControlName="fillColor"></tb-data-layer-color-settings>
<tb-data-layer-color-settings helpId="{{ dataLayerType === 'polygons' ? 'widget/lib/map/polygon_fill_color_fn' : 'widget/lib/map/circle_fill_color_fn' }}" formControlName="fillColor"></tb-data-layer-color-settings>
</div>
<div class="tb-form-row space-between">
<div translate>widgets.maps.data-layer.stroke</div>
@ -359,18 +360,20 @@
<input matInput type="number" min="0" formControlName="strokeWeight" placeholder="{{ 'widget-config.set' | translate }}">
<span matSuffix>px</span>
</mat-form-field>
<tb-data-layer-color-settings formControlName="strokeColor"></tb-data-layer-color-settings>
<tb-data-layer-color-settings helpId="{{ dataLayerType === 'polygons' ? 'widget/lib/map/polygon_stroke_color_fn' : 'widget/lib/map/circle_stroke_color_fn' }}" formControlName="strokeColor"></tb-data-layer-color-settings>
</div>
</div>
</ng-container>
<tb-data-layer-pattern-settings
patternType="label"
[context]="context"
[helpId]="labelHelpId"
formControlName="label">
</tb-data-layer-pattern-settings>
<tb-data-layer-pattern-settings
patternType="tooltip"
[context]="context"
[helpId]="tooltipHelpId"
[hasTooltipOffset]="['trips', 'markers'].includes(dataLayerType)"
formControlName="tooltip">
</tb-data-layer-pattern-settings>

View File

@ -29,7 +29,7 @@ import {
MarkerType, pathDecoratorSymbols, pathDecoratorSymbolTranslationMap,
PolygonsDataLayerSettings,
ShapeDataLayerSettings, TripsDataLayerSettings
} from '@home/components/widget/lib/maps/models/map.models';
} from '@shared/models/widget/maps/map.models';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { Router } from '@angular/router';
@ -94,6 +94,36 @@ export class MapDataLayerDialogComponent extends DialogComponent<MapDataLayerDia
dialogTitle: string;
get labelHelpId(): string {
switch (this.dataLayerType) {
case 'trips':
return 'widget/lib/map/label_fn';
case 'markers':
return 'widget/lib/map/label_fn';
case 'polygons':
return 'widget/lib/map/polygon_label_fn';
case 'circles':
return 'widget/lib/map/circle_label_fn';
default:
return 'widget/lib/map/label_fn';
}
}
get tooltipHelpId(): string {
switch (this.dataLayerType) {
case 'trips':
return 'widget/lib/map/tooltip_fn';
case 'markers':
return 'widget/lib/map/tooltip_fn';
case 'polygons':
return 'widget/lib/map/polygon_tooltip_fn';
case 'circles':
return 'widget/lib/map/circle_tooltip_fn';
default:
return 'widget/lib/map/tooltip_fn';
}
}
constructor(protected store: Store<AppState>,
protected router: Router,
@Inject(MAT_DIALOG_DATA) public data: MapDataLayerDialogData,

View File

@ -42,7 +42,7 @@ import {
MarkersDataLayerSettings,
PolygonsDataLayerSettings,
TripsDataLayerSettings
} from '@home/components/widget/lib/maps/models/map.models';
} from '@shared/models/widget/maps/map.models';
import { DataKey, DatasourceType, datasourceTypeTranslationMap, widgetType } from '@shared/models/widget.models';
import { EntityType } from '@shared/models/entity-type.models';
import { DataKeyType } from '@shared/models/telemetry/telemetry.models';

View File

@ -35,7 +35,7 @@ import {
mapDataLayerValid,
mapDataLayerValidator,
MapType
} from '@home/components/widget/lib/maps/models/map.models';
} from '@shared/models/widget/maps/map.models';
import { MapSettingsComponent } from '@home/components/widget/lib/settings/common/map/map-settings.component';
import { MapSettingsContext } from '@home/components/widget/lib/settings/common/map/map-settings.component.models';

View File

@ -33,7 +33,7 @@ import {
Validators
} from '@angular/forms';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { AdditionalMapDataSourceSettings } from '@home/components/widget/lib/maps/models/map.models';
import { AdditionalMapDataSourceSettings } from '@shared/models/widget/maps/map.models';
import { DataKey, DatasourceType, datasourceTypeTranslationMap, widgetType } from '@shared/models/widget.models';
import { EntityType } from '@shared/models/entity-type.models';
import { DataKeyType } from '@shared/models/telemetry/telemetry.models';

View File

@ -33,7 +33,7 @@ import {
additionalMapDataSourceValid,
additionalMapDataSourceValidator,
defaultAdditionalMapDataSourceSettings
} from '@home/components/widget/lib/maps/models/map.models';
} from '@shared/models/widget/maps/map.models';
import { MapSettingsContext } from '@home/components/widget/lib/settings/common/map/map-settings.component.models';
@Component({

View File

@ -53,7 +53,7 @@ import {
openStreetMapLayerTranslationMap,
tencentLayerTranslationMap,
tencentLayerTypes
} from '@home/components/widget/lib/maps/models/map.models';
} from '@shared/models/widget/maps/map.models';
import { deepClone } from '@core/utils';
import {
MapLayerSettingsPanelComponent

View File

@ -33,7 +33,7 @@ import {
openStreetMapLayerTranslationMap,
tencentLayerTranslationMap,
tencentLayerTypes
} from '@home/components/widget/lib/maps/models/map.models';
} from '@shared/models/widget/maps/map.models';
import { TranslateService } from '@ngx-translate/core';
@Component({

View File

@ -35,7 +35,7 @@ import {
mapLayerValid,
mapLayerValidator,
MapProvider
} from '@home/components/widget/lib/maps/models/map.models';
} from '@shared/models/widget/maps/map.models';
@Component({
selector: 'tb-map-layers',

View File

@ -38,7 +38,7 @@ import {
MapType,
mapZoomActions,
mapZoomActionTranslationMap
} from '@home/components/widget/lib/maps/models/map.models';
} from '@shared/models/widget/maps/map.models';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { merge, Observable } from 'rxjs';
import { coerceBoolean } from '@shared/decorators/coercion';
@ -254,7 +254,6 @@ export class MapSettingsComponent implements OnInit, ControlValueAccessor, Valid
private updateDragButtonModeSettings() {
const markers: MapDataLayerSettings[] = this.mapSettingsFormGroup.get('markers').value;
const circles: MapDataLayerSettings[] = this.mapSettingsFormGroup.get('circles').value;
let dragModeButtonSettingsEnabled = markers.some(d => d.edit && d.edit.enabledActions && d.edit.enabledActions.includes(DataLayerEditAction.move));
if (!dragModeButtonSettingsEnabled) {
const polygons: MapDataLayerSettings[] = this.mapSettingsFormGroup.get('polygons').value;

View File

@ -27,7 +27,7 @@ import {
} from '@angular/forms';
import { WidgetService } from '@core/http/widget.service';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { MarkerClusteringSettings } from '@home/components/widget/lib/maps/models/map.models';
import { MarkerClusteringSettings } from '@shared/models/widget/maps/map.models';
import { merge } from 'rxjs';
@Component({

View File

@ -22,7 +22,7 @@ import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { WidgetService } from '@core/http/widget.service';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { MarkerImageSettings, MarkerImageType } from '@home/components/widget/lib/maps/models/map.models';
import { MarkerImageSettings, MarkerImageType } from '@shared/models/widget/maps/map.models';
@Component({
selector: 'tb-marker-image-settings-panel',

View File

@ -18,7 +18,7 @@ import { ChangeDetectorRef, Component, forwardRef, Input, Renderer2, ViewContain
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { MatButton } from '@angular/material/button';
import { TbPopoverService } from '@shared/components/popover.service';
import { MarkerImageSettings, MarkerImageType } from '@home/components/widget/lib/maps/models/map.models';
import { MarkerImageSettings, MarkerImageType } from '@shared/models/widget/maps/map.models';
import {
MarkerImageSettingsPanelComponent
} from '@home/components/widget/lib/settings/common/map/marker-image-settings-panel.component';

View File

@ -30,5 +30,5 @@
<div *ngIf="markerType === MarkerType.icon" matButtonIcon style="object-fit: contain; width: 24px; height: 24px;" [innerHTML]="iconPreview$ | async" [class.disabled]="disabled">
</div>
</button>
<tb-data-layer-color-settings formControlName="color"></tb-data-layer-color-settings>
<tb-data-layer-color-settings helpId="widget/lib/map/color_fn" formControlName="color"></tb-data-layer-color-settings>
</div>

View File

@ -33,7 +33,7 @@ import {
} from '@angular/forms';
import { MatButton } from '@angular/material/button';
import { TbPopoverService } from '@shared/components/popover.service';
import { MarkerIconSettings, MarkerShapeSettings, MarkerType } from '@home/components/widget/lib/maps/models/map.models';
import { MarkerIconSettings, MarkerShapeSettings, MarkerType } from '@shared/models/widget/maps/map.models';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { Observable } from 'rxjs';
import { DomSanitizer, SafeHtml, SafeUrl } from '@angular/platform-browser';
@ -41,7 +41,7 @@ import { MatIconRegistry } from '@angular/material/icon';
import {
createColorMarkerIconElement,
createColorMarkerShapeURI
} from '@home/components/widget/lib/maps/models/marker-shape.models';
} from '@shared/models/widget/maps/marker-shape.models';
import tinycolor from 'tinycolor2';
import { map, share } from 'rxjs/operators';
import { MarkerShapesComponent } from '@home/components/widget/lib/settings/common/map/marker-shapes.component';

View File

@ -23,7 +23,7 @@ import {
createColorMarkerShapeURI,
MarkerShape, markerShapes,
tripMarkerShapes
} from '@home/components/widget/lib/maps/models/marker-shape.models';
} from '@shared/models/widget/maps/marker-shape.models';
import { Observable } from 'rxjs';
import { DomSanitizer, SafeUrl } from '@angular/platform-browser';
import { MatIconRegistry } from '@angular/material/icon';

View File

@ -29,16 +29,8 @@ import { merge } from 'rxjs';
import { WidgetService } from '@core/http/widget.service';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import {
DataLayerPatternSettings,
DataLayerPatternType,
DataLayerTooltipSettings,
dataLayerTooltipTriggers,
dataLayerTooltipTriggerTranslationMap,
pathDecoratorSymbols, pathDecoratorSymbolTranslationMap,
TripTimelineSettings
} from '@home/components/widget/lib/maps/models/map.models';
import { coerceBoolean } from '@shared/decorators/coercion';
import { MapSettingsContext } from '@home/components/widget/lib/settings/common/map/map-settings.component.models';
} from '@shared/models/widget/maps/map.models';
@Component({
selector: 'tb-trip-timeline-settings',
@ -166,7 +158,4 @@ export class TripTimelineSettingsComponent implements OnInit, ControlValueAccess
this.modelValue = this.tripTimelineSettingsFormGroup.getRawValue();
this.propagateChange(this.modelValue);
}
protected readonly pathDecoratorSymbols = pathDecoratorSymbols;
protected readonly pathDecoratorSymbolTranslationMap = pathDecoratorSymbolTranslationMap;
}

View File

@ -66,7 +66,7 @@
[globalVariables]="functionScopeVariables"
[functionArgs]="['data', 'dsData', 'dsIndex']"
functionTitle="{{ 'widgets.maps.circle-label-function' | translate }}"
helpId="widget/lib/map/label_fn">
helpId="widget/lib/map-legacy/label_fn">
</tb-js-func>
</ng-template>
</mat-expansion-panel>
@ -110,7 +110,7 @@
[globalVariables]="functionScopeVariables"
[functionArgs]="['data', 'dsData', 'dsIndex']"
functionTitle="{{ 'widgets.maps.circle-tooltip-function' | translate }}"
helpId="widget/lib/map/polygon_tooltip_fn">
helpId="widget/lib/map-legacy/polygon_tooltip_fn">
</tb-js-func>
</ng-template>
</mat-expansion-panel>
@ -146,7 +146,7 @@
[globalVariables]="functionScopeVariables"
[functionArgs]="['data', 'dsData', 'dsIndex']"
functionTitle="{{ 'widgets.maps.circle-fill-color-function' | translate }}"
helpId="widget/lib/map/polygon_color_fn">
helpId="widget/lib/map-legacy/polygon_color_fn">
</tb-js-func>
</ng-template>
</mat-expansion-panel>
@ -186,7 +186,7 @@
[globalVariables]="functionScopeVariables"
[functionArgs]="['data', 'dsData', 'dsIndex']"
functionTitle="{{ 'widgets.maps.circle-stroke-color-function' | translate }}"
helpId="widget/lib/map/polygon_color_fn">
helpId="widget/lib/map-legacy/polygon_color_fn">
</tb-js-func>
</ng-template>
</mat-expansion-panel>

View File

@ -82,7 +82,7 @@
[globalVariables]="functionScopeVariables"
[functionArgs]="['data', 'childCount']"
functionTitle="{{ 'widgets.maps.marker-color-function' | translate }}"
helpId="widget/lib/map/clustering_color_fn">
helpId="widget/lib/map-legacy/clustering_color_fn">
</tb-js-func>
</ng-template>
</mat-expansion-panel>

View File

@ -35,7 +35,7 @@
[globalVariables]="functionScopeVariables"
[functionArgs]="['origXPos', 'origYPos', 'data', 'dsData', 'dsIndex', 'aspect']"
functionTitle="{{ 'widgets.maps.position-function' | translate }}"
helpId="widget/lib/map/position_fn">
helpId="widget/lib/map-legacy/position_fn">
</tb-js-func>
<mat-checkbox formControlName="draggableMarker">
{{ 'widgets.maps.draggable-marker' | translate }}
@ -68,7 +68,7 @@
[globalVariables]="functionScopeVariables"
[functionArgs]="['data', 'dsData', 'dsIndex']"
functionTitle="{{ 'widgets.maps.label-function' | translate }}"
helpId="widget/lib/map/label_fn">
helpId="widget/lib/map-legacy/label_fn">
</tb-js-func>
</ng-template>
</mat-expansion-panel>
@ -112,7 +112,7 @@
[globalVariables]="functionScopeVariables"
[functionArgs]="['data', 'dsData', 'dsIndex']"
functionTitle="{{ 'widgets.maps.tooltip-function' | translate }}"
helpId="widget/lib/map/tooltip_fn">
helpId="widget/lib/map-legacy/tooltip_fn">
</tb-js-func>
<section class="flex flex-row xs:flex-col gt-xs:gap-2">
<mat-form-field class="flex-1">
@ -151,7 +151,7 @@
[globalVariables]="functionScopeVariables"
[functionArgs]="['data', 'dsData', 'dsIndex']"
functionTitle="{{ 'widgets.maps.color-function' | translate }}"
helpId="widget/lib/map/color_fn">
helpId="widget/lib/map-legacy/color_fn">
</tb-js-func>
</ng-template>
</mat-expansion-panel>
@ -185,7 +185,7 @@
[globalVariables]="functionScopeVariables"
[functionArgs]="['data', 'images', 'dsData', 'dsIndex']"
functionTitle="{{ 'widgets.maps.marker-image-function' | translate }}"
helpId="widget/lib/map/marker_image_fn">
helpId="widget/lib/map-legacy/marker_image_fn">
</tb-js-func>
<tb-multiple-gallery-image-input [class.!hidden]="!markersSettingsFormGroup.get('useMarkerImageFunction').value"
label="{{ 'widgets.maps.marker-images' | translate }}"

View File

@ -66,7 +66,7 @@
[globalVariables]="functionScopeVariables"
[functionArgs]="['data', 'dsData', 'dsIndex']"
functionTitle="{{ 'widgets.maps.polygon-label-function' | translate }}"
helpId="widget/lib/map/label_fn">
helpId="widget/lib/map-legacy/label_fn">
</tb-js-func>
</ng-template>
</mat-expansion-panel>
@ -110,7 +110,7 @@
[globalVariables]="functionScopeVariables"
[functionArgs]="['data', 'dsData', 'dsIndex']"
functionTitle="{{ 'widgets.maps.polygon-tooltip-function' | translate }}"
helpId="widget/lib/map/polygon_tooltip_fn">
helpId="widget/lib/map-legacy/polygon_tooltip_fn">
</tb-js-func>
</ng-template>
</mat-expansion-panel>
@ -146,7 +146,7 @@
[globalVariables]="functionScopeVariables"
[functionArgs]="['data', 'dsData', 'dsIndex']"
functionTitle="{{ 'widgets.maps.polygon-color-function' | translate }}"
helpId="widget/lib/map/polygon_color_fn">
helpId="widget/lib/map-legacy/polygon_color_fn">
</tb-js-func>
</ng-template>
</mat-expansion-panel>
@ -186,7 +186,7 @@
[globalVariables]="functionScopeVariables"
[functionArgs]="['data', 'dsData', 'dsIndex']"
functionTitle="{{ 'widgets.maps.polygon-stroke-color-function' | translate }}"
helpId="widget/lib/map/polygon_color_fn">
helpId="widget/lib/map-legacy/polygon_color_fn">
</tb-js-func>
</ng-template>
</mat-expansion-panel>

View File

@ -85,7 +85,7 @@
[globalVariables]="functionScopeVariables"
[functionArgs]="['data', 'dsData', 'dsIndex']"
functionTitle="{{ 'widgets.maps.tooltip-function' | translate }}"
helpId="widget/lib/map/tooltip_fn">
helpId="widget/lib/map-legacy/tooltip_fn">
</tb-js-func>
</ng-template>
</mat-expansion-panel>

View File

@ -50,7 +50,7 @@
[globalVariables]="functionScopeVariables"
[functionArgs]="['data', 'dsData', 'dsIndex']"
functionTitle="{{ 'widgets.maps.label-function' | translate }}"
helpId="widget/lib/map/label_fn">
helpId="widget/lib/map-legacy/label_fn">
</tb-js-func>
</ng-template>
</mat-expansion-panel>
@ -84,7 +84,7 @@
[globalVariables]="functionScopeVariables"
[functionArgs]="['data', 'images', 'dsData', 'dsIndex']"
functionTitle="{{ 'widgets.maps.marker-image-function' | translate }}"
helpId="widget/lib/map/marker_image_fn">
helpId="widget/lib/map-legacy/marker_image_fn">
</tb-js-func>
<tb-multiple-gallery-image-input [class.!hidden]="!tripAnimationMarkerSettingsFormGroup.get('useMarkerImageFunction').value"
label="{{ 'widgets.maps.marker-images' | translate }}"

View File

@ -51,7 +51,7 @@
[globalVariables]="functionScopeVariables"
[functionArgs]="['data', 'dsData', 'dsIndex']"
functionTitle="{{ 'widgets.maps.path-color-function' | translate }}"
helpId="widget/lib/map/path_color_fn">
helpId="widget/lib/map-legacy/path_color_fn">
</tb-js-func>
</ng-template>
</mat-expansion-panel>

View File

@ -59,7 +59,7 @@
[globalVariables]="functionScopeVariables"
[functionArgs]="['data', 'dsData', 'dsIndex']"
functionTitle="{{ 'widgets.maps.point-color-function' | translate }}"
helpId="widget/lib/map/path_point_color_fn">
helpId="widget/lib/map-legacy/path_point_color_fn">
</tb-js-func>
</ng-template>
</mat-expansion-panel>
@ -80,7 +80,7 @@
[globalVariables]="functionScopeVariables"
[functionArgs]="['data', 'dsData', 'dsIndex']"
functionTitle="{{ 'widgets.maps.point-as-anchor-function' | translate }}"
helpId="widget/lib/map/trip_point_as_anchor_fn">
helpId="widget/lib/map-legacy/trip_point_as_anchor_fn">
</tb-js-func>
</ng-template>
</mat-expansion-panel>

View File

@ -19,7 +19,7 @@ import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.m
import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { isDefinedAndNotNull, mergeDeep, mergeDeepIgnoreArray } from '@core/utils';
import { isDefinedAndNotNull, mergeDeepIgnoreArray } from '@core/utils';
import { mapWidgetDefaultSettings, MapWidgetSettings } from '@home/components/widget/lib/maps/map-widget.models';
import { WidgetConfigComponentData } from '@home/models/widget-component.models';

View File

@ -307,23 +307,23 @@ export class ImportExportService {
}
}
return this.addImportedWidget(dashboard, targetState, targetLayoutFunction, widget,
aliasesInfo, filtersInfo, onAliasesUpdateFunction, onFiltersUpdateFunction, originalColumns, originalSize);
aliasesInfo, filtersInfo, onAliasesUpdateFunction, onFiltersUpdateFunction, originalColumns, originalSize, widgetItem.widgetExportInfo);
}
));
} else {
return this.addImportedWidget(dashboard, targetState, targetLayoutFunction, widget,
aliasesInfo, filtersInfo, onAliasesUpdateFunction, onFiltersUpdateFunction, originalColumns, originalSize);
aliasesInfo, filtersInfo, onAliasesUpdateFunction, onFiltersUpdateFunction, originalColumns, originalSize, widgetItem.widgetExportInfo);
}
}
)
);
} else {
return this.addImportedWidget(dashboard, targetState, targetLayoutFunction, widget,
aliasesInfo, filtersInfo, onAliasesUpdateFunction, onFiltersUpdateFunction, originalColumns, originalSize);
aliasesInfo, filtersInfo, onAliasesUpdateFunction, onFiltersUpdateFunction, originalColumns, originalSize, widgetItem.widgetExportInfo);
}
} else {
return this.addImportedWidget(dashboard, targetState, targetLayoutFunction, widget,
aliasesInfo, filtersInfo, onAliasesUpdateFunction, onFiltersUpdateFunction, originalColumns, originalSize);
aliasesInfo, filtersInfo, onAliasesUpdateFunction, onFiltersUpdateFunction, originalColumns, originalSize, widgetItem.widgetExportInfo);
}
}
}),
@ -1033,11 +1033,11 @@ export class ImportExportService {
filtersInfo: FiltersInfo,
onAliasesUpdateFunction: () => void,
onFiltersUpdateFunction: () => void,
originalColumns: number, originalSize: WidgetSize): Observable<ImportWidgetResult> {
originalColumns: number, originalSize: WidgetSize, widgetExportInfo: any): Observable<ImportWidgetResult> {
return targetLayoutFunction().pipe(
mergeMap((targetLayout) => this.itembuffer.addWidgetToDashboard(dashboard, targetState, targetLayout,
widget, aliasesInfo, filtersInfo, onAliasesUpdateFunction, onFiltersUpdateFunction,
originalColumns, originalSize, -1, -1).pipe(
originalColumns, originalSize, -1, -1, 'default', widgetExportInfo).pipe(
map(() => ({widget, layoutId: targetLayout} as ImportWidgetResult))
)
));

View File

@ -18,6 +18,7 @@ import { EntityType } from '@shared/models/entity-type.models';
import { EntityId } from '@shared/models/id/entity-id';
import { EntitySearchDirection, RelationEntityTypeFilter } from '@shared/models/relation.models';
import { EntityFilter } from '@shared/models/query/query.models';
import { guid, isEqual } from '@core/utils';
export enum AliasFilterType {
singleEntity = 'singleEntity',
@ -210,3 +211,41 @@ export interface EntityAliasFilterResult {
entityFilter: EntityFilter;
entityParamName?: string;
}
export const getEntityAliasId = (entityAliases: EntityAliases, aliasInfo: EntityAliasInfo): string => {
let newAliasId: string;
for (const aliasId of Object.keys(entityAliases)) {
if (isEntityAliasEqual(entityAliases[aliasId], aliasInfo)) {
newAliasId = aliasId;
break;
}
}
if (!newAliasId) {
const newAliasName = createEntityAliasName(entityAliases, aliasInfo.alias);
newAliasId = guid();
entityAliases[newAliasId] = {id: newAliasId, alias: newAliasName, filter: aliasInfo.filter};
}
return newAliasId;
}
const isEntityAliasEqual = (alias1: EntityAliasInfo, alias2: EntityAliasInfo): boolean => {
return isEqual(alias1.filter, alias2.filter);
}
const createEntityAliasName = (entityAliases: EntityAliases, alias: string): string => {
let c = 0;
let newAlias = alias;
let unique = false;
while (!unique) {
unique = true;
for (const entAliasId of Object.keys(entityAliases)) {
const entAlias = entityAliases[entAliasId];
if (newAlias === entAlias.alias) {
c++;
newAlias = alias + c;
unique = false;
}
}
}
return newAlias;
}

View File

@ -23,6 +23,7 @@ import { EntityType } from '@shared/models/entity-type.models';
import { DataKey, Datasource, DatasourceType } from '@shared/models/widget.models';
import { PageData } from '@shared/models/page/page-data';
import {
guid,
isArraysEqualIgnoreUndefined,
isDefined,
isDefinedAndNotNull,
@ -921,3 +922,42 @@ export function updateDatasourceFromEntityInfo(datasource: Datasource, entity: E
}
}
}
export const getFilterId = (filters: Filters, filterInfo: FilterInfo): string => {
let newFilterId: string;
for (const filterId of Object.keys(filters)) {
if (isFilterEqual(filters[filterId], filterInfo)) {
newFilterId = filterId;
break;
}
}
if (!newFilterId) {
const newFilterName = createFilterName(filters, filterInfo.filter);
newFilterId = guid();
filters[newFilterId] = {id: newFilterId, filter: newFilterName,
keyFilters: filterInfo.keyFilters, editable: filterInfo.editable};
}
return newFilterId;
}
const isFilterEqual = (filter1: FilterInfo, filter2: FilterInfo): boolean => {
return isEqual(filter1.keyFilters, filter2.keyFilters);
}
const createFilterName = (filters: Filters, filter: string): string => {
let c = 0;
let newFilter = filter;
let unique = false;
while (!unique) {
unique = true;
for (const entFilterId of Object.keys(filters)) {
const entFilter = filters[entFilterId];
if (newFilter === entFilter.filter) {
c++;
newFilter = filter + c;
unique = false;
}
}
}
return newFilter;
}

View File

@ -0,0 +1,151 @@
///
/// Copyright © 2016-2025 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 { EntityAliases, EntityAliasInfo, getEntityAliasId } from '@shared/models/alias.models';
import { FilterInfo, Filters, getFilterId } from '@shared/models/query/query.models';
import { Dashboard } from '@shared/models/dashboard.models';
import { DatasourceType, Widget } from '@shared/models/widget.models';
import { BaseMapSettings, MapDataSourceSettings, MapType } from '@shared/models/widget/maps/map.models';
import { WidgetExportDefinition } from '@shared/models/widget/widget-export.models';
interface ExportDataSourceInfo {
aliases: {[dataLayerIndex: number]: EntityAliasInfo};
filters: {[dataLayerIndex: number]: FilterInfo};
}
interface MapDatasourcesInfo {
trips?: ExportDataSourceInfo;
markers?: ExportDataSourceInfo;
polygons?: ExportDataSourceInfo;
circles?: ExportDataSourceInfo;
additionalDataSources?: ExportDataSourceInfo;
}
export const MapExportDefinition: WidgetExportDefinition<MapDatasourcesInfo> = {
testWidget(widget: Widget): boolean {
if (widget?.config?.settings) {
const settings = widget.config.settings;
if (settings.mapType && [MapType.image, MapType.geoMap].includes(settings.mapType)) {
if (settings.trips && Array.isArray(settings.trips)) {
return true;
}
if (settings.markers && Array.isArray(settings.markers)) {
return true;
}
if (settings.polygons && Array.isArray(settings.polygons)) {
return true;
}
if (settings.circles && Array.isArray(settings.circles)) {
return true;
}
}
}
return false;
},
prepareExportInfo(dashboard: Dashboard, widget: Widget): MapDatasourcesInfo {
const settings: BaseMapSettings = widget.config.settings as BaseMapSettings;
const info: MapDatasourcesInfo = {};
if (settings.trips?.length) {
info.trips = prepareExportDataSourcesInfo(dashboard, settings.trips);
}
if (settings.markers?.length) {
info.markers = prepareExportDataSourcesInfo(dashboard, settings.markers);
}
if (settings.polygons?.length) {
info.polygons = prepareExportDataSourcesInfo(dashboard, settings.polygons);
}
if (settings.circles?.length) {
info.circles = prepareExportDataSourcesInfo(dashboard, settings.circles);
}
if (settings.additionalDataSources?.length) {
info.additionalDataSources = prepareExportDataSourcesInfo(dashboard, settings.additionalDataSources);
}
return info;
},
updateFromExportInfo(widget: Widget, entityAliases: EntityAliases, filters: Filters, info: MapDatasourcesInfo): void {
const settings: BaseMapSettings = widget.config.settings as BaseMapSettings;
if (info?.trips) {
updateMapDatasourceFromExportInfo(entityAliases, filters, settings.trips, info.trips);
}
if (info?.markers) {
updateMapDatasourceFromExportInfo(entityAliases, filters, settings.markers, info.markers);
}
if (info?.polygons) {
updateMapDatasourceFromExportInfo(entityAliases, filters, settings.polygons, info.polygons);
}
if (info?.circles) {
updateMapDatasourceFromExportInfo(entityAliases, filters, settings.circles, info.circles);
}
if (info?.additionalDataSources) {
updateMapDatasourceFromExportInfo(entityAliases, filters, settings.additionalDataSources, info.additionalDataSources);
}
}
};
const updateMapDatasourceFromExportInfo = (entityAliases: EntityAliases,
filters: Filters, settings: MapDataSourceSettings[], info: ExportDataSourceInfo): void => {
if (info.aliases) {
for (const dsIndexStr of Object.keys(info.aliases)) {
const dsIndex = Number(dsIndexStr);
if (settings[dsIndex] && settings[dsIndex].dsType === DatasourceType.entity) {
const aliasInfo = info.aliases[dsIndex];
settings[dsIndex].dsEntityAliasId = getEntityAliasId(entityAliases, aliasInfo);
}
}
}
if (info.filters) {
for (const dsIndexStr of Object.keys(info.filters)) {
const dsIndex = Number(dsIndexStr);
if (settings[dsIndex] && settings[dsIndex].dsType === DatasourceType.entity) {
const filterInfo = info.filters[dsIndex];
settings[dsIndex].dsFilterId = getFilterId(filters, filterInfo);
}
}
}
}
const prepareExportDataSourcesInfo = (dashboard: Dashboard, settings: MapDataSourceSettings[]): ExportDataSourceInfo => {
const info: ExportDataSourceInfo = {
aliases: {},
filters: {}
};
settings.forEach((dsSettings, index) => {
prepareExportDataSourceInfo(dashboard, info, dsSettings, index);
});
return info;
}
const prepareExportDataSourceInfo = (dashboard: Dashboard, info: ExportDataSourceInfo, settings: MapDataSourceSettings, index: number): void => {
if (settings.dsType === DatasourceType.entity) {
const entityAlias = dashboard.configuration.entityAliases[settings.dsEntityAliasId];
if (entityAlias) {
info.aliases[index] = {
alias: entityAlias.alias,
filter: entityAlias.filter
};
}
if (settings.dsFilterId && dashboard.configuration.filters) {
const filter = dashboard.configuration.filters[settings.dsFilterId];
if (filter) {
info.filters[index] = {
filter: filter.filter,
keyFilters: filter.keyFilters,
editable: filter.editable
};
}
}
}
}

View File

@ -39,7 +39,7 @@ import { TbFunction } from '@shared/models/js-function.models';
import { Observable, Observer, of, switchMap } from 'rxjs';
import { map } from 'rxjs/operators';
import { ImagePipe } from '@shared/pipe/image.pipe';
import { MarkerShape } from '@home/components/widget/lib/maps/models/marker-shape.models';
import { MarkerShape } from '@shared/models/widget/maps/marker-shape.models';
import { DateFormatSettings, simpleDateFormat } from '@shared/models/widget-settings.models';
export enum MapType {

View File

@ -205,7 +205,7 @@ export const createColorMarkerIconElement = (iconRegistry: MatIconRegistry, domS
let placeItemIconURI$: Observable<string>;
export const createPlaceItemIcon= (iconRegistry: MatIconRegistry, domSanitizer: DomSanitizer): Observable<string> => {
export const createPlaceItemIcon = (iconRegistry: MatIconRegistry, domSanitizer: DomSanitizer): Observable<string> => {
if (placeItemIconURI$) {
return placeItemIconURI$;
}

View File

@ -0,0 +1,35 @@
///
/// Copyright © 2016-2025 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 { Widget } from '@shared/models/widget.models';
import { Dashboard } from '@shared/models/dashboard.models';
import { EntityAliases } from '@shared/models/alias.models';
import { Filters } from '@shared/models/query/query.models';
import { MapExportDefinition } from '@shared/models/widget/maps/map-export.models';
export interface WidgetExportDefinition<T = any> {
testWidget(widget: Widget): boolean;
prepareExportInfo(dashboard: Dashboard, widget: Widget): T;
updateFromExportInfo(widget: Widget, entityAliases: EntityAliases, filters: Filters, info: T): void;
}
const widgetExportDefinitions: WidgetExportDefinition[] = [
MapExportDefinition
];
export const getWidgetExportDefinition = (widget: Widget): WidgetExportDefinition => {
return widgetExportDefinitions.find(def => def.testWidget(widget));
}

View File

@ -0,0 +1,65 @@
#### Clustering marker function
<div class="divider"></div>
<br/>
*function (data, childCount): string*
A JavaScript function used to compute clustering marker color.
**Parameters:**
<ul>
<li><b>data:</b> <code><a href="https://github.com/thingsboard/thingsboard/blob/5bb6403407aa4898084832d6698aa9ea6d484889/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts#L108" target="_blank">FormattedData[]</a></code>
- the array of total markers contained within each cluster.<br/>
Represents basic entity properties (ex. <code>entityId</code>, <code>entityName</code>)<br/>and provides access to other entity attributes/timeseries declared in widget datasource configuration.
</li>
<li><b>childCount:</b> <code>number</code> - the total number of markers contained within that cluster
</li>
</ul>
**Returns:**
Should return string value presenting color of the marker.
In case no data is returned, color value from **Color** settings field will be used.
<div class="divider"></div>
##### Examples
<ul>
<li>
Calculate color depending on temperature telemetry value:
</li>
```javascript
let customColor;
for (let markerData of data) {
if (markerData.temperature > 40) {
customColor = 'red'
}
}
return customColor ? customColor : 'green';
{:copy-code}
```
<li>
Calculate color depending on childCount:
</li>
```javascript
if (childCount < 10) {
return 'green';
} else if (childCount < 100) {
return 'yellow';
} else {
return 'red';
}
{:copy-code}
```
</ul>
<br>
<br>

View File

@ -0,0 +1,42 @@
#### Marker color function
<div class="divider"></div>
<br/>
*function (data, dsData, dsIndex): string*
A JavaScript function used to compute color of the marker.
**Parameters:**
<ul>
{% include widget/lib/map-legacy/map_fn_args %}
</ul>
**Returns:**
Should return string value presenting color of the marker.
In case no data is returned, color value from **Color** settings field will be used.
<div class="divider"></div>
##### Examples
* Calculate color depending on `temperature` telemetry value for `colorpin` device type:
```javascript
var type = data['Type'];
if (type == 'colorpin') {
var temperature = data['temperature'];
if (typeof temperature !== undefined) {
var percent = (temperature + 60)/120 * 100;
return tinycolor.mix('blue', 'red', percent).toHexString();
}
return 'blue';
}
{:copy-code}
```
<br>
<br>

View File

@ -0,0 +1,40 @@
#### Marker label function
<div class="divider"></div>
<br/>
*function (data, dsData, dsIndex): string*
A JavaScript function used to compute text or HTML code of the marker label.
**Parameters:**
<ul>
{% include widget/lib/map-legacy/map_fn_args %}
</ul>
**Returns:**
Should return string value presenting text or HTML of the marker label.
<div class="divider"></div>
##### Examples
* Display styled label with corresponding latest telemetry data for `energy meter` or `thermometer` device types:
```javascript
var deviceType = data['Type'];
if (typeof deviceType !== undefined) {
if (deviceType == "energy meter") {
return '<span style="color:orange;">${entityName}, ${energy:2} kWt</span>';
} else if (deviceType == "thermometer") {
return '<span style="color:blue;">${entityName}, ${temperature:2} °C</span>';
}
}
return data.entityName;
{:copy-code}
```
<br>
<br>

View File

@ -0,0 +1,10 @@
<li><b>data:</b> <code><a href="https://github.com/thingsboard/thingsboard/blob/5bb6403407aa4898084832d6698aa9ea6d484889/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts#L108" target="_blank">FormattedData</a></code> - A <a href="https://github.com/thingsboard/thingsboard/blob/5bb6403407aa4898084832d6698aa9ea6d484889/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts#L108" target="_blank">FormattedData</a> object associated with marker or data point of the route.<br/>
Represents basic entity properties (ex. <code>entityId</code>, <code>entityName</code>)<br/>and provides access to other entity attributes/timeseries declared in widget datasource configuration.
</li>
<li><b>dsData:</b> <code><a href="https://github.com/thingsboard/thingsboard/blob/5bb6403407aa4898084832d6698aa9ea6d484889/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts#L108" target="_blank">FormattedData[]</a></code> - All available data associated with markers or routes data points as array of <a href="https://github.com/thingsboard/thingsboard/blob/5bb6403407aa4898084832d6698aa9ea6d484889/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts#L108" target="_blank">FormattedData</a> objects<br/>
resolved from configured datasources. Each object represents basic entity properties (ex. <code>entityId</code>, <code>entityName</code>)<br/>
and provides access to other entity attributes/timeseries declared in widget datasource configuration.
</li>
<li><b>dsIndex</b> <code>number</code> - index of the current marker data or route data point in <code>dsData</code> array.<br>
<strong>Note: </strong> The <code>data</code> argument is equivalent to <code>dsData[dsIndex]</code> expression.
</li>

View File

@ -0,0 +1,62 @@
#### Marker image function
<div class="divider"></div>
<br/>
*function (data, images, dsData, dsIndex): {url: string, size: number}*
A JavaScript function used to compute marker image.
**Parameters:**
<ul>
{% include widget/lib/map-legacy/map_fn_args %}
</ul>
**Returns:**
Should return marker image data having the following structure:
```typescript
{
url: string,
size: number
}
```
- *url* - marker image url;
- *size* - marker image size;
In case no data is returned, default marker image will be used.
<div class="divider"></div>
##### Examples
<ul>
<li>
Calculate image url depending on <code>temperature</code> telemetry value for <code>thermometer</code> device type.<br>
Let's assume 4 images are defined in <b>Marker images</b> section. Each image corresponds to particular temperature level:
</li>
</ul>
```javascript
var type = data['Type'];
if (type == 'thermometer') {
var res = {
url: images[0],
size: 40
}
var temperature = data['temperature'];
if (typeof temperature !== undefined) {
var percent = (temperature + 60)/120;
var index = Math.min(3, Math.floor(4 * percent));
res.url = images[index];
}
return res;
}
{:copy-code}
```
<br>
<br>

View File

@ -0,0 +1,42 @@
#### Path color function
<div class="divider"></div>
<br/>
*function (data, dsData, dsIndex): string*
A JavaScript function used to compute color of the trip path.
**Parameters:**
<ul>
{% include widget/lib/map-legacy/map_fn_args %}
</ul>
**Returns:**
Should return string value presenting color of the trip path.
In case no data is returned, color value from **Path color** settings field will be used.
<div class="divider"></div>
##### Examples
* Calculate color depending on `temperature` telemetry value for `colorpin` device type:
```javascript
var type = data['Type'];
if (type == 'colorpin') {
var temperature = data['temperature'];
if (typeof temperature !== undefined) {
var percent = (temperature + 60)/120 * 100;
return tinycolor.mix('blue', 'red', percent).toHexString();
}
return 'blue';
}
{:copy-code}
```
<br>
<br>

View File

@ -0,0 +1,43 @@
#### Path point color function
<div class="divider"></div>
<br/>
*function (data, dsData, dsIndex): string*
A JavaScript function used to compute color of the trip path point.
**Parameters:**
<ul>
{% include widget/lib/map-legacy/map_fn_args %}
</ul>
**Returns:**
Should return string value presenting color of the trip path point.
In case no data is returned, color value from **Point color** settings field will be used.
<div class="divider"></div>
##### Examples
* Calculate color depending on `temperature` telemetry value for `colorpin` device type:
```javascript
var type = data['Type'];
if (type == 'colorpin') {
var temperature = data['temperature'];
if (typeof temperature !== undefined) {
var percent = (temperature + 60)/120 * 100;
return tinycolor.mix('blue', 'red', percent).toHexString();
}
return 'blue';
}
{:copy-code}
```
<br>
<br>

View File

@ -10,7 +10,7 @@ A JavaScript function used to compute color of the polygon.
**Parameters:**
<ul>
{% include widget/lib/map/map_fn_args %}
{% include widget/lib/map-legacy/map_fn_args %}
</ul>
**Returns:**

View File

@ -0,0 +1,40 @@
#### Polygon tooltip function
<div class="divider"></div>
<br/>
*function (data, dsData, dsIndex): string*
A JavaScript function used to compute text or HTML code to be displayed in the polygon tooltip.
**Parameters:**
<ul>
{% include widget/lib/map-legacy/map_fn_args %}
</ul>
**Returns:**
Should return string value presenting text or HTML for the polygon tooltip.
<div class="divider"></div>
##### Examples
* Display details with corresponding telemetry data for `energy meter` or `thermostat` device types:
```javascript
var deviceType = data['Type'];
if (typeof deviceType !== undefined) {
if (deviceType == "energy meter") {
return '<b>${entityName}</b><br/><b>Energy:</b> ${energy:2} kWt<br/>';
} else if (deviceType == "thermostat") {
return '<b>${entityName}</b><br/><b>Temperature:</b> ${temperature:2} °C<br/>';
}
}
return data.entityName;
{:copy-code}
```
<br>
<br>

View File

@ -0,0 +1,65 @@
#### Position conversion function
<div class="divider"></div>
<br/>
*function (origXPos, origYPos, data, dsData, dsIndex, aspect): {x: number, y: number}*
A JavaScript function used to convert original relative x, y coordinates of the marker.
**Parameters:**
<ul>
<li><b>origXPos:</b> <code>number</code> - original relative x coordinate as double from 0 to 1.</li>
<li><b>origYPos:</b> <code>number</code> - original relative y coordinate as double from 0 to 1.</li>
{% include widget/lib/map-legacy/map_fn_args %}
<li><b>aspect:</b> <code>number</code> - image map aspect ratio.</li>
</ul>
**Returns:**
Should return position data having the following structure:
```typescript
{
x: number,
y: number
}
```
- *x* - new relative x coordinate as double from 0 to 1;
- *y* - new relative y coordinate as double from 0 to 1;
<div class="divider"></div>
##### Examples
* Scale the coordinates to half the original:
```javascript
return {x: origXPos / 2, y: origYPos / 2};
{:copy-code}
```
* Detect markers with same positions and place them with minimum overlap:
```javascript
var xPos = data.xPos;
var yPos = data.yPos;
var locationGroup = dsData.filter((item) => item.xPos === xPos && item.yPos === yPos);
if (locationGroup.length > 1) {
const count = locationGroup.length;
const index = locationGroup.indexOf(data);
const radius = 0.035;
const angle = (360 / count) * index - 45;
const x = xPos + radius * Math.sin(angle*Math.PI/180) / aspect;
const y = yPos + radius * Math.cos(angle*Math.PI/180);
return {x: x, y: y};
} else {
return {x: xPos, y: yPos};
}
{:copy-code}
```
<br>
<br>

View File

@ -0,0 +1,40 @@
#### Marker tooltip function
<div class="divider"></div>
<br/>
*function (data, dsData, dsIndex): string*
A JavaScript function used to compute text or HTML code to be displayed in the marker, point or polygon tooltip.
**Parameters:**
<ul>
{% include widget/lib/map-legacy/map_fn_args %}
</ul>
**Returns:**
Should return string value presenting text or HTML for the tooltip.
<div class="divider"></div>
##### Examples
* Display details with corresponding telemetry data for `thermostat` device type:
```javascript
var deviceType = data['Type'];
if (typeof deviceType !== undefined) {
if (deviceType == "energy meter") {
return '<b>${entityName}</b><br/><b>Energy:</b> ${energy:2} kWt<br/>';
} else if (deviceType == "thermometer") {
return '<b>${entityName}</b><br/><b>Temperature:</b> ${temperature:2} °C<br/>';
}
}
return data.entityName;
{:copy-code}
```
<br>
<br>

View File

@ -0,0 +1,34 @@
#### Point as anchor function
<div class="divider"></div>
<br/>
*function (data, dsData, dsIndex): boolean*
A JavaScript function evaluating whether to use trip point as time anchor used in time selector.
**Parameters:**
<ul>
{% include widget/lib/map-legacy/map_fn_args %}
</ul>
**Returns:**
`true` if the point should be decided as anchor, `false` otherwise.
In case no data is returned, the point is not used as anchor.
<div class="divider"></div>
##### Examples
* Make anchors with 5 seconds step interval:
```javascript
return data.time % 5000 < 1000;
{:copy-code}
```
<br>
<br>

View File

@ -0,0 +1,24 @@
#### Circle fill color function
<div class="divider"></div>
<br/>
*function (data, dsData): string*
A JavaScript function used to compute fill color of the circle.
**Parameters:**
<ul>
{% include widget/lib/map/map_fn_args %}
</ul>
**Returns:**
Should return string value presenting fill color of the circle.
In case no data is returned, color value from **Color** settings field will be used.
<div class="divider"></div>
{% include widget/lib/map/color_fn_examples %}

View File

@ -0,0 +1,22 @@
#### Circle label function
<div class="divider"></div>
<br/>
*function (data, dsData): string*
A JavaScript function used to compute text or HTML code of the circle label.
**Parameters:**
<ul>
{% include widget/lib/map/map_fn_args %}
</ul>
**Returns:**
Should return string value presenting text or HTML of the circle label.
<div class="divider"></div>
{% include widget/lib/map/label_fn_examples %}

View File

@ -0,0 +1,24 @@
#### Circle stroke color function
<div class="divider"></div>
<br/>
*function (data, dsData): string*
A JavaScript function used to compute stroke color of the circle.
**Parameters:**
<ul>
{% include widget/lib/map/map_fn_args %}
</ul>
**Returns:**
Should return string value presenting stroke color of the circle.
In case no data is returned, color value from **Color** settings field will be used.
<div class="divider"></div>
{% include widget/lib/map/color_fn_examples %}

View File

@ -0,0 +1,22 @@
#### Circle tooltip function
<div class="divider"></div>
<br/>
*function (data, dsData): string*
A JavaScript function used to compute text or HTML code to be displayed in the circle tooltip.
**Parameters:**
<ul>
{% include widget/lib/map/map_fn_args %}
</ul>
**Returns:**
Should return string value presenting text or HTML for the tooltip.
<div class="divider"></div>
{% include widget/lib/map/tooltip_fn_examples %}

View File

@ -10,9 +10,9 @@ A JavaScript function used to compute clustering marker color.
**Parameters:**
<ul>
<li><b>data:</b> <code><a href="https://github.com/thingsboard/thingsboard/blob/5bb6403407aa4898084832d6698aa9ea6d484889/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts#L108" target="_blank">FormattedData[]</a></code>
<li><b>data:</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>
- the array of total markers contained within each cluster.<br/>
Represents basic entity properties (ex. <code>entityId</code>, <code>entityName</code>)<br/>and provides access to other entity attributes/timeseries declared in widget datasource configuration.
Represents basic entity properties (ex. <code>entityId</code>, <code>entityName</code>)<br/>and provides access to other entity attributes/timeseries declared in datasource of the data layer configuration.
</li>
<li><b>childCount:</b> <code>number</code> - the total number of markers contained within that cluster
</li>
@ -22,7 +22,7 @@ A JavaScript function used to compute clustering marker color.
Should return string value presenting color of the marker.
In case no data is returned, color value from **Color** settings field will be used.
In case no data is returned, default colors will be used depending on number of markers within that cluster.
<div class="divider"></div>

View File

@ -3,7 +3,7 @@
<div class="divider"></div>
<br/>
*function (data, dsData, dsIndex): string*
*function (data, dsData): string*
A JavaScript function used to compute color of the marker.
@ -21,22 +21,4 @@ In case no data is returned, color value from **Color** settings field will be u
<div class="divider"></div>
##### Examples
* Calculate color depending on `temperature` telemetry value for `colorpin` device type:
```javascript
var type = data['Type'];
if (type == 'colorpin') {
var temperature = data['temperature'];
if (typeof temperature !== undefined) {
var percent = (temperature + 60)/120 * 100;
return tinycolor.mix('blue', 'red', percent).toHexString();
}
return 'blue';
}
{:copy-code}
```
<br>
<br>
{% include widget/lib/map/color_fn_examples %}

View File

@ -0,0 +1,19 @@
##### Examples
* Calculate color depending on `temperature` telemetry value for `thermostat` device type:
```javascript
var type = data.Type;
if (type == 'thermostat') {
var temperature = data.temperature;
if (typeof temperature !== undefined) {
var percent = (temperature + 60)/120 * 100;
return tinycolor.mix('blue', 'red', percent).toHexString();
}
return 'blue';
}
{:copy-code}
```
<br>
<br>

View File

@ -3,7 +3,7 @@
<div class="divider"></div>
<br/>
*function (data, dsData, dsIndex): string*
*function (data, dsData): string*
A JavaScript function used to compute text or HTML code of the marker label.
@ -19,22 +19,4 @@ Should return string value presenting text or HTML of the marker label.
<div class="divider"></div>
##### Examples
* Display styled label with corresponding latest telemetry data for `energy meter` or `thermometer` device types:
```javascript
var deviceType = data['Type'];
if (typeof deviceType !== undefined) {
if (deviceType == "energy meter") {
return '<span style="color:orange;">${entityName}, ${energy:2} kWt</span>';
} else if (deviceType == "thermometer") {
return '<span style="color:blue;">${entityName}, ${temperature:2} °C</span>';
}
}
return data.entityName;
{:copy-code}
```
<br>
<br>
{% include widget/lib/map/label_fn_examples %}

View File

@ -0,0 +1,19 @@
##### Examples
* Display styled label with corresponding latest telemetry data for `energy meter` or `thermometer` device types:
```javascript
var deviceType = data.Type;
if (typeof deviceType !== undefined) {
if (deviceType == "energy meter") {
return '<span style="color:orange;">${entityName}, ${energy:2} kWt</span>';
} else if (deviceType == "thermometer") {
return '<span style="color:blue;">${entityName}, ${temperature:2} °C</span>';
}
}
return data.entityName;
{:copy-code}
```
<br>
<br>

View File

@ -1,10 +1,8 @@
<li><b>data:</b> <code><a href="https://github.com/thingsboard/thingsboard/blob/5bb6403407aa4898084832d6698aa9ea6d484889/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts#L108" target="_blank">FormattedData</a></code> - A <a href="https://github.com/thingsboard/thingsboard/blob/5bb6403407aa4898084832d6698aa9ea6d484889/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts#L108" target="_blank">FormattedData</a> object associated with marker or data point of the route.<br/>
Represents basic entity properties (ex. <code>entityId</code>, <code>entityName</code>)<br/>and provides access to other entity attributes/timeseries declared in widget datasource configuration.
<li><b>data:</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> object associated with data layer (markers/polygons/circles) or data point of the route (trips data layer).<br/>
Represents basic entity properties (ex. <code>entityId</code>, <code>entityName</code>)<br/>and provides access to other entity attributes/timeseries declared in datasource of the data layer configuration.
</li>
<li><b>dsData:</b> <code><a href="https://github.com/thingsboard/thingsboard/blob/5bb6403407aa4898084832d6698aa9ea6d484889/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts#L108" target="_blank">FormattedData[]</a></code> - All available data associated with markers or routes data points as array of <a href="https://github.com/thingsboard/thingsboard/blob/5bb6403407aa4898084832d6698aa9ea6d484889/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts#L108" target="_blank">FormattedData</a> objects<br/>
<li><b>dsData:</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> - All available data associated with data layers including additional datasources as array of <a href="https://github.com/thingsboard/thingsboard/blob/b881f1c2985399f9665e033e2479549e97da1f36/ui-ngx/src/app/shared/models/widget.models.ts#L513" target="_blank">FormattedData</a> objects<br/>
resolved from configured datasources. Each object represents basic entity properties (ex. <code>entityId</code>, <code>entityName</code>)<br/>
and provides access to other entity attributes/timeseries declared in widget datasource configuration.
</li>
<li><b>dsIndex</b> <code>number</code> - index of the current marker data or route data point in <code>dsData</code> array.<br>
<strong>Note: </strong> The <code>data</code> argument is equivalent to <code>dsData[dsIndex]</code> expression.
and provides access to other entity attributes/timeseries declared in datasources of data layers configuration including additional datasources of the map configuration.
</li>

View File

@ -3,14 +3,14 @@
<div class="divider"></div>
<br/>
*function (data, images, dsData, dsIndex): {url: string, size: number}*
*function (data, images, dsData): {url: string, size: number}*
A JavaScript function used to compute marker image.
**Parameters:**
<ul>
{% include widget/lib/map/map_fn_args %}
{% include widget/lib/map/marker_image_fn_args %}
</ul>
**Returns:**
@ -19,13 +19,17 @@ Should return marker image data having the following structure:
```typescript
{
url: string,
size: number
url: string;
size: number;
markerOffset?: [number, number];
tooltipOffset?: [number, number];
}
```
- *url* - marker image url;
- *size* - marker image size;
- *markerOffset* - optional array of two numbers presenting relative horizontal and vertical offset of the marker image;
- *tooltipOffset* - optional array of two numbers presenting relative horizontal and vertical offset of the marker image tooltip;
In case no data is returned, default marker image will be used.
@ -41,13 +45,13 @@ Let's assume 4 images are defined in <b>Marker images</b> section. Each image co
</ul>
```javascript
var type = data['Type'];
var type = data.Type;
if (type == 'thermometer') {
var res = {
url: images[0],
size: 40
}
var temperature = data['temperature'];
var temperature = data.temperature;
if (typeof temperature !== undefined) {
var percent = (temperature + 60)/120;
var index = Math.min(3, Math.floor(4 * percent));

View File

@ -0,0 +1,10 @@
<li><b>data:</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> object associated with data layer (markers/polygons/circles) or data point of the route (trips data layer).<br/>
Represents basic entity properties (ex. <code>entityId</code>, <code>entityName</code>)<br/>and provides access to other entity attributes/timeseries declared in datasource of the data layer configuration.
</li>
<li><b>images:</b> <code>string[]</code> - array of image urls configured in the <b>Marker images</b> section.
</li>
<li><b>dsData:</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> - All available data associated with data layers including additional datasources as array of <a href="https://github.com/thingsboard/thingsboard/blob/b881f1c2985399f9665e033e2479549e97da1f36/ui-ngx/src/app/shared/models/widget.models.ts#L513" target="_blank">FormattedData</a> objects<br/>
resolved from configured datasources. Each object represents basic entity properties (ex. <code>entityId</code>, <code>entityName</code>)<br/>
and provides access to other entity attributes/timeseries declared in datasources of data layers configuration including additional datasources of the map configuration.
</li>

View File

@ -3,7 +3,7 @@
<div class="divider"></div>
<br/>
*function (data, dsData, dsIndex): string*
*function (data, dsData): string*
A JavaScript function used to compute color of the trip path.
@ -17,26 +17,8 @@ A JavaScript function used to compute color of the trip path.
Should return string value presenting color of the trip path.
In case no data is returned, color value from **Path color** settings field will be used.
In case no data is returned, color value from **Color** settings field will be used.
<div class="divider"></div>
##### Examples
* Calculate color depending on `temperature` telemetry value for `colorpin` device type:
```javascript
var type = data['Type'];
if (type == 'colorpin') {
var temperature = data['temperature'];
if (typeof temperature !== undefined) {
var percent = (temperature + 60)/120 * 100;
return tinycolor.mix('blue', 'red', percent).toHexString();
}
return 'blue';
}
{:copy-code}
```
<br>
<br>
{% include widget/lib/map/color_fn_examples %}

View File

@ -3,7 +3,7 @@
<div class="divider"></div>
<br/>
*function (data, dsData, dsIndex): string*
*function (data, dsData): string*
A JavaScript function used to compute color of the trip path point.
@ -17,27 +17,8 @@ A JavaScript function used to compute color of the trip path point.
Should return string value presenting color of the trip path point.
In case no data is returned, color value from **Point color** settings field will be used.
In case no data is returned, color value from **Color** settings field will be used.
<div class="divider"></div>
##### Examples
* Calculate color depending on `temperature` telemetry value for `colorpin` device type:
```javascript
var type = data['Type'];
if (type == 'colorpin') {
var temperature = data['temperature'];
if (typeof temperature !== undefined) {
var percent = (temperature + 60)/120 * 100;
return tinycolor.mix('blue', 'red', percent).toHexString();
}
return 'blue';
}
{:copy-code}
```
<br>
<br>
{% include widget/lib/map/color_fn_examples %}

View File

@ -0,0 +1,22 @@
#### Path point tooltip function
<div class="divider"></div>
<br/>
*function (data, dsData): string*
A JavaScript function used to compute text or HTML code to be displayed in the trip path point tooltip.
**Parameters:**
<ul>
{% include widget/lib/map/map_fn_args %}
</ul>
**Returns:**
Should return string value presenting text or HTML for the tooltip.
<div class="divider"></div>
{% include widget/lib/map/tooltip_fn_examples %}

View File

@ -0,0 +1,24 @@
#### Polygon fill color function
<div class="divider"></div>
<br/>
*function (data, dsData): string*
A JavaScript function used to compute fill color of the polygon.
**Parameters:**
<ul>
{% include widget/lib/map/map_fn_args %}
</ul>
**Returns:**
Should return string value presenting fill color of the polygon.
In case no data is returned, color value from **Color** settings field will be used.
<div class="divider"></div>
{% include widget/lib/map/color_fn_examples %}

View File

@ -0,0 +1,22 @@
#### Polygon label function
<div class="divider"></div>
<br/>
*function (data, dsData): string*
A JavaScript function used to compute text or HTML code of the polygon label.
**Parameters:**
<ul>
{% include widget/lib/map/map_fn_args %}
</ul>
**Returns:**
Should return string value presenting text or HTML of the polygon label.
<div class="divider"></div>
{% include widget/lib/map/label_fn_examples %}

View File

@ -0,0 +1,24 @@
#### Polygon stroke color function
<div class="divider"></div>
<br/>
*function (data, dsData): string*
A JavaScript function used to compute stroke color of the polygon.
**Parameters:**
<ul>
{% include widget/lib/map/map_fn_args %}
</ul>
**Returns:**
Should return string value presenting stroke color of the polygon.
In case no data is returned, color value from **Color** settings field will be used.
<div class="divider"></div>
{% include widget/lib/map/color_fn_examples %}

View File

@ -3,7 +3,7 @@
<div class="divider"></div>
<br/>
*function (data, dsData, dsIndex): string*
*function (data, dsData): string*
A JavaScript function used to compute text or HTML code to be displayed in the polygon tooltip.
@ -15,26 +15,8 @@ A JavaScript function used to compute text or HTML code to be displayed in the p
**Returns:**
Should return string value presenting text or HTML for the polygon tooltip.
Should return string value presenting text or HTML for the tooltip.
<div class="divider"></div>
##### Examples
* Display details with corresponding telemetry data for `energy meter` or `thermostat` device types:
```javascript
var deviceType = data['Type'];
if (typeof deviceType !== undefined) {
if (deviceType == "energy meter") {
return '<b>${entityName}</b><br/><b>Energy:</b> ${energy:2} kWt<br/>';
} else if (deviceType == "thermostat") {
return '<b>${entityName}</b><br/><b>Temperature:</b> ${temperature:2} °C<br/>';
}
}
return data.entityName;
{:copy-code}
```
<br>
<br>
{% include widget/lib/map/tooltip_fn_examples %}

View File

@ -3,7 +3,7 @@
<div class="divider"></div>
<br/>
*function (origXPos, origYPos, data, dsData, dsIndex, aspect): {x: number, y: number}*
*function (origXPos, origYPos, data, dsData, aspect): {x: number, y: number}*
A JavaScript function used to convert original relative x, y coordinates of the marker.
@ -22,8 +22,8 @@ Should return position data having the following structure:
```typescript
{
x: number,
y: number
x: number;
y: number;
}
```

View File

@ -3,9 +3,9 @@
<div class="divider"></div>
<br/>
*function (data, dsData, dsIndex): string*
*function (data, dsData): string*
A JavaScript function used to compute text or HTML code to be displayed in the marker, point or polygon tooltip.
A JavaScript function used to compute text or HTML code to be displayed in the marker tooltip.
**Parameters:**
@ -19,22 +19,4 @@ Should return string value presenting text or HTML for the tooltip.
<div class="divider"></div>
##### Examples
* Display details with corresponding telemetry data for `thermostat` device type:
```javascript
var deviceType = data['Type'];
if (typeof deviceType !== undefined) {
if (deviceType == "energy meter") {
return '<b>${entityName}</b><br/><b>Energy:</b> ${energy:2} kWt<br/>';
} else if (deviceType == "thermometer") {
return '<b>${entityName}</b><br/><b>Temperature:</b> ${temperature:2} °C<br/>';
}
}
return data.entityName;
{:copy-code}
```
<br>
<br>
{% include widget/lib/map/tooltip_fn_examples %}

View File

@ -0,0 +1,19 @@
##### Examples
* Display details with corresponding telemetry data for `energy meter` or `thermometer` device types:
```javascript
var deviceType = data.Type;
if (typeof deviceType !== undefined) {
if (deviceType == "energy meter") {
return '<b>${entityName}</b><br/><b>Energy:</b> ${energy:2} kWt<br/>';
} else if (deviceType == "thermometer") {
return '<b>${entityName}</b><br/><b>Temperature:</b> ${temperature:2} °C<br/>';
}
}
return data.entityName;
{:copy-code}
```
<br>
<br>

View File

@ -1,9 +1,9 @@
#### Point as anchor function
#### Location snap filter function
<div class="divider"></div>
<br/>
*function (data, dsData, dsIndex): boolean*
*function (data, dsData): boolean*
A JavaScript function evaluating whether to use trip point as time anchor used in time selector.

View File

@ -15,8 +15,8 @@
///
import { FormattedData } from '@shared/models/widget.models';
import L, { Control, ControlOptions } from 'leaflet';
import { TbMapDatasource } from '@home/components/widget/lib/maps/models/map.models';
import L from 'leaflet';
import { TbMapDatasource } from '@shared/models/widget/maps/map.models';
import { MatIconRegistry } from '@angular/material/icon';
// redeclare module, maintains compatibility with @types/leaflet