This commit is contained in:
ArtemHalushko 2020-05-04 11:40:54 +03:00 committed by GitHub
parent c6cf5c43ad
commit 11db772ea2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 112 additions and 73 deletions

View File

@ -17,7 +17,7 @@
import { AliasInfo, IAliasController, StateControllerHolder, StateEntityInfo } from '@core/api/widget-api.models';
import { forkJoin, Observable, of, ReplaySubject, Subject } from 'rxjs';
import { DataKey, Datasource, DatasourceType } from '@app/shared/models/widget.models';
import { deepClone, isEqual } from '@core/utils';
import { deepClone, isEqual, createLabelFromDatasource } from '@core/utils';
import { EntityService } from '@core/http/entity.service';
import { UtilsService } from '@core/services/utils.service';
import { EntityAliases } from '@shared/models/alias.models';
@ -329,7 +329,7 @@ export class AliasController implements IAliasController {
if (!dataKey.pattern) {
dataKey.pattern = deepClone(dataKey.label);
}
dataKey.label = this.utils.createLabelFromDatasource(datasource, dataKey.pattern);
dataKey.label = createLabelFromDatasource(datasource, dataKey.pattern);
}
getInstantAliasInfo(aliasId: string): AliasInfo {

View File

@ -20,7 +20,7 @@
import { Inject, Injectable, NgZone } from '@angular/core';
import { WINDOW } from '@core/services/window.service';
import { ExceptionData } from '@app/shared/models/error.models';
import { deepClone, deleteNullProperties, guid, isDefined, isDefinedAndNotNull, isUndefined } from '@core/utils';
import { deepClone, deleteNullProperties, guid, isDefined, isDefinedAndNotNull, isUndefined, createLabelFromDatasource } from '@core/utils';
import { WindowMessage } from '@shared/models/window-message.model';
import { TranslateService } from '@ngx-translate/core';
import { customTranslationsPrefix } from '@app/shared/models/constants';
@ -363,7 +363,7 @@ export class UtilsService {
datasources: Datasource[], additionalKeysNumber: number): DataKey {
const additionalDataKey = deepClone(dataKey);
if (dataKey.settings.comparisonSettings.comparisonValuesLabel) {
additionalDataKey.label = this.createLabelFromDatasource(datasource, dataKey.settings.comparisonSettings.comparisonValuesLabel);
additionalDataKey.label = createLabelFromDatasource(datasource, dataKey.settings.comparisonSettings.comparisonValuesLabel);
} else {
additionalDataKey.label = dataKey.label + ' ' + this.translate.instant('legend.comparison-time-ago.' + timeUnit);
}
@ -380,30 +380,7 @@ export class UtilsService {
}
public createLabelFromDatasource(datasource: Datasource, pattern: string) {
let label = pattern;
if (!datasource) {
return label;
}
let match = varsRegex.exec(pattern);
while (match !== null) {
const variable = match[0];
const variableName = match[1];
if (variableName === 'dsName') {
label = label.split(variable).join(datasource.name);
} else if (variableName === 'entityName') {
label = label.split(variable).join(datasource.entityName);
} else if (variableName === 'deviceName') {
label = label.split(variable).join(datasource.entityName);
} else if (variableName === 'entityLabel') {
label = label.split(variable).join(datasource.entityLabel || datasource.entityName);
} else if (variableName === 'aliasName') {
label = label.split(variable).join(datasource.aliasName);
} else if (variableName === 'entityDescription') {
label = label.split(variable).join(datasource.entityDescription);
}
match = varsRegex.exec(pattern);
}
return label;
return createLabelFromDatasource(datasource, pattern);
}
public generateColors(datasources: Array<Datasource>) {

View File

@ -18,6 +18,7 @@ import _ from 'lodash';
import { Observable, Subject, fromEvent, of } from 'rxjs';
import { finalize, share, map } from 'rxjs/operators';
import base64js from 'base64-js';
import { Datasource } from '@app/shared/models/widget.models';
export function onParentScrollOrWindowResize(el: Node): Observable<Event> {
const scrollSubject = new Subject<Event>();
@ -435,6 +436,34 @@ export function imageLoader(imageUrl: string): Observable<HTMLImageElement> {
return imageLoad$;
}
export function createLabelFromDatasource(datasource: Datasource, pattern: string) {
const varsRegex = /\$\{([^}]*)\}/g;
let label = pattern;
if (!datasource) {
return label;
}
let match = varsRegex.exec(pattern);
while (match !== null) {
const variable = match[0];
const variableName = match[1];
if (variableName === 'dsName') {
label = label.split(variable).join(datasource.name);
} else if (variableName === 'entityName') {
label = label.split(variable).join(datasource.entityName);
} else if (variableName === 'deviceName') {
label = label.split(variable).join(datasource.entityName);
} else if (variableName === 'entityLabel') {
label = label.split(variable).join(datasource.entityLabel || datasource.entityName);
} else if (variableName === 'aliasName') {
label = label.split(variable).join(datasource.aliasName);
} else if (variableName === 'entityDescription') {
label = label.split(variable).join(datasource.entityDescription);
}
match = varsRegex.exec(pattern);
}
return label;
}
const imageAspectMap = {};
export function aspectCache(imageUrl: string): Observable<number> {
@ -452,7 +481,6 @@ export function aspectCache(imageUrl: string): Observable<number> {
}
}
export function parseArray(input: any[]): any[] {
return _(input).groupBy(el => el?.datasource?.entityName)
.values().value().map((entityArray, dsIndex) =>
@ -523,15 +551,18 @@ export function parseFunction(source: any, params: string[] = ['def']): Function
return res;
}
export function parseTemplate(template: string, data: object, translateFn?: (key: string) => string) {
export function parseTemplate(template: string, data: { $datasource?: Datasource, [key: string]: any },
translateFn?: (key: string) => string) {
let res = '';
try {
if (template.match(/<link-act/g)) {
template = template.replace(/<link-act/g, '<a').replace(/link-act>/g, 'a>').replace(/name=(\'|")(.*?)(\'|")/g, `class='tb-custom-action' id='$2'`);
template = template.replace(/<link-act/g, '<a').replace(/link-act>/g, 'a>')
.replace(/name=(\'|")(.*?)(\'|")/g, `class='tb-custom-action' id='$2'`);
}
if (translateFn) {
template = translateFn(template);
}
template = createLabelFromDatasource(data.$datasource, template);
const formatted = template.match(/\$\{([^}]*)\:\d*\}/g);
if (formatted)
formatted.forEach(value => {

View File

@ -33,7 +33,7 @@ import { Datasource, WidgetActionDescriptor, WidgetConfig } from '@shared/models
import { IWidgetSubscription } from '@core/api/widget-api.models';
import { UtilsService } from '@core/services/utils.service';
import { TranslateService } from '@ngx-translate/core';
import { deepClone, isDefined, isNumber } from '@core/utils';
import { deepClone, isDefined, isNumber, createLabelFromDatasource } from '@core/utils';
import cssjs from '@core/css/css';
import { PageLink } from '@shared/models/page/page-link';
import { Direction, SortOrder, sortOrderFromString } from '@shared/models/page/sort-order';
@ -282,7 +282,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
alarmsTitle = this.translate.instant('alarm.alarms');
}
this.ctx.widgetTitle = this.utils.createLabelFromDatasource(this.alarmSource, alarmsTitle);
this.ctx.widgetTitle = createLabelFromDatasource(this.alarmSource, alarmsTitle);
this.enableSelection = isDefined(this.settings.enableSelection) ? this.settings.enableSelection : true;
if (!this.allowAcknowledgment && !this.allowClear) {

View File

@ -39,7 +39,7 @@ import {
import { IWidgetSubscription } from '@core/api/widget-api.models';
import { UtilsService } from '@core/services/utils.service';
import { TranslateService } from '@ngx-translate/core';
import { deepClone, isDefined, isNumber } from '@core/utils';
import { deepClone, isDefined, isNumber, createLabelFromDatasource } from '@core/utils';
import cssjs from '@core/css/css';
import { PageLink } from '@shared/models/page/page-link';
import { Direction, SortOrder, sortOrderFromString } from '@shared/models/page/sort-order';
@ -210,7 +210,7 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
}
const datasource = this.subscription.datasources[0];
this.ctx.widgetTitle = this.utils.createLabelFromDatasource(datasource, entitiesTitle);
this.ctx.widgetTitle = createLabelFromDatasource(datasource, entitiesTitle);
this.searchAction.show = isDefined(this.settings.enableSearch) ? this.settings.enableSearch : true;
this.displayPagination = isDefined(this.settings.displayPagination) ? this.settings.displayPagination : true;

View File

@ -26,6 +26,7 @@ import { filter } from 'rxjs/operators';
import { Polyline } from './polyline';
import { Polygon } from './polygon';
import { DatasourceData } from '@app/shared/models/widget.models';
import { safeExecute } from '@app/core/utils';
export default abstract class LeafletMap {
@ -87,12 +88,14 @@ export default abstract class LeafletMap {
if (this.options.draggableMarker) {
let mousePositionOnMap: L.LatLng;
let addMarker: L.Control;
this.map.on('mouseup', (e: L.LeafletMouseEvent) => {
this.map.on('mousemove', (e: L.LeafletMouseEvent) => {
mousePositionOnMap = e.latlng;
});
const dragListener = (e: L.DragEndEvent) => {
if (e.type === 'dragend' && mousePositionOnMap) {
const newMarker = L.marker(mousePositionOnMap).addTo(this.map);
const icon = new L.Icon.Default();
icon.options.shadowSize = [0, 0];
const newMarker = L.marker(mousePositionOnMap, { icon }).addTo(this.map);
const datasourcesList = document.createElement('div');
const customLatLng = this.convertToCustomFormat(mousePositionOnMap);
this.datasources.forEach(ds => {
@ -195,15 +198,18 @@ export default abstract class LeafletMap {
fitBounds(bounds: LatLngBounds, useDefaultZoom = false, padding?: LatLngTuple) {
if (bounds.isValid()) {
if ((!this.options.fitMapBounds || useDefaultZoom) && this.options.defaultZoomLevel) {
if ((!this.options.fitMapBounds || this.options.useDefaultCenterPosition) && this.options.defaultZoomLevel) {
this.map.setZoom(this.options.defaultZoomLevel, { animate: false });
this.map.panTo(bounds.getCenter(), { animate: false });
this.map.panTo(this.options.defaultCenterPosition, { animate: false });
} else {
this.map.once('zoomend', () => {
if (!this.options.defaultZoomLevel && this.map.getZoom() > this.options.minZoomLevel) {
this.map.setZoom(this.options.minZoomLevel, { animate: false });
}
});
if (this.options.useDefaultCenterPosition) {
bounds = bounds.extend(this.options.defaultCenterPosition);
}
this.map.fitBounds(bounds, { padding: padding || [50, 50], animate: false });
}
this.bounds = bounds;
@ -231,8 +237,16 @@ export default abstract class LeafletMap {
updateMarkers(markersData) {
markersData.filter(mdata => !!this.convertPosition(mdata)).forEach(data => {
if (data.rotationAngle || data.rotationAngle === 0) {
const currentImage = this.options.useMarkerImageFunction ?
safeExecute(this.options.markerImageFunction,
[data, this.options.markerImages, markersData, data.dsIndex]) : this.options.currentImage;
const style = currentImage ? 'background-image: url(' + currentImage.url + ');' : '';
this.options.icon = L.divIcon({
html: `<div class="arrow" style="transform: translate(-10px, -10px) rotate(${data.rotationAngle}deg);"><div>`
html: `<div class="arrow"
style="transform: translate(-10px, -10px);
${style}
rotate(${data.rotationAngle}deg);
"><div>`
})
}
else {
@ -335,31 +349,28 @@ export default abstract class LeafletMap {
data.data = JSON.parse(data.data[0][1]) as LatLngTuple[];
}
if (this.polygons.get(data.datasource.entityName)) {
this.updatePolygon(data.datasource.entityName, data.data, polyData, this.options);
this.updatePolygon(data, polyData, this.options);
}
else {
this.createPolygon(data.datasource.entityName, data.data, polyData, this.options);
this.createPolygon(data, polyData, this.options);
}
}
});
}
createPolygon(key: string, data: LatLngTuple[], dataSources: DatasourceData[], settings: PolygonSettings) {
createPolygon(polyData: DatasourceData, dataSources: DatasourceData[], settings: PolygonSettings) {
this.ready$.subscribe(() => {
const polygon = new Polygon(this.map, data, dataSources, settings);
const polygon = new Polygon(this.map, polyData, dataSources, settings);
const bounds = this.bounds.extend(polygon.leafletPoly.getBounds());
if (bounds.isValid()) {
this.map.fitBounds(bounds);
this.bounds = bounds;
}
this.polygons.set(key, polygon);
this.fitBounds(bounds);
this.polygons.set(polyData.datasource.entityName, polygon);
});
}
updatePolygon(key: string, data: LatLngTuple[], dataSources: DatasourceData[], settings: PolygonSettings) {
updatePolygon(polyData: DatasourceData, dataSources: DatasourceData[], settings: PolygonSettings) {
this.ready$.subscribe(() => {
const poly = this.polygons.get(key);
poly.updatePolygon(data, dataSources, settings);
const poly = this.polygons.get(polyData.datasource.entityName);
poly.updatePolygon(polyData.data, dataSources, settings);
this.fitBounds(poly.leafletPoly.getBounds());
});
}

View File

@ -111,9 +111,13 @@ export type PolygonSettings = {
polygonStrokeWeight: number;
polygonStrokeColor: string;
polygonColor: string;
showPolygonTooltip: boolean;
autocloseTooltip: boolean;
tooltipFunction: GenericFunction;
showTooltipAction: string;
tooltipAction: object;
tooltipPattern: string;
useTooltipFunction: boolean;
polygonClick: { [name: string]: actionsHandler };
polygonColorFunction?: GenericFunction;
}

View File

@ -16,8 +16,9 @@
import L, { LatLngExpression, LatLngTuple } from 'leaflet';
import { createTooltip } from './maps-utils';
import { PolygonSettings } from './map-models';
import { PolygonSettings, FormattedData } from './map-models';
import { DatasourceData } from '@app/shared/models/widget.models';
import { safeExecute, parseWithTranslation } from '@app/core/utils';
export class Polygon {
@ -26,8 +27,8 @@ export class Polygon {
data;
dataSources;
constructor(public map, coordinates, dataSources, settings: PolygonSettings, onClickListener?) {
this.leafletPoly = L.polygon(coordinates, {
constructor(public map, polyData: DatasourceData, dataSources, private settings: PolygonSettings, onClickListener?) {
this.leafletPoly = L.polygon(polyData.data, {
fill: true,
fillColor: settings.polygonColor,
color: settings.polygonStrokeColor,
@ -35,19 +36,29 @@ export class Polygon {
fillOpacity: settings.polygonOpacity,
opacity: settings.polygonStrokeOpacity
}).addTo(this.map);
if (settings.showTooltip) {
this.dataSources = dataSources;
this.data = polyData;
if (settings.showPolygonTooltip) {
this.tooltip = createTooltip(this.leafletPoly, settings);
this.updateTooltip(polyData);
}
if (onClickListener) {
this.leafletPoly.on('click', onClickListener);
}
}
updateTooltip(data: DatasourceData) {
const pattern = this.settings.useTooltipFunction ?
safeExecute(this.settings.tooltipFunction, [this.data, this.dataSources, this.data.dsIndex]) : this.settings.tooltipPattern;
this.tooltip.setContent(parseWithTranslation.parseTemplate(pattern, data, true));
}
updatePolygon(data: LatLngTuple[], dataSources: DatasourceData[], settings: PolygonSettings) {
this.data = data;
this.dataSources = dataSources;
this.leafletPoly.setLatLngs(data);
if (settings.showPolygonTooltip)
this.updateTooltip(this.data);
this.updatePolygonColor(settings);
}

View File

@ -477,6 +477,11 @@ export const mapPolygonSchema =
type: 'number',
default: 1
},
showPolygonTooltip: {
title: 'Show polygon tooltip',
type: 'boolean',
default: false
},
usePolygonColorFunction: {
title: 'Use polygon color function',
type: 'boolean',
@ -501,7 +506,7 @@ export const mapPolygonSchema =
key: 'polygonStrokeColor',
type: 'color'
},
'polygonStrokeOpacity', 'polygonStrokeWeight', 'usePolygonColorFunction',
'polygonStrokeOpacity', 'polygonStrokeWeight', 'usePolygonColorFunction', 'showPolygonTooltip',
{
key: 'polygonColorFunction',
type: 'javascript'
@ -1137,7 +1142,7 @@ export const tripAnimationSchema = {
rotationAngle: {
title: 'Set additional rotation angle for marker (deg)',
type: 'number',
default: 180
default: 0
},
useMarkerImageFunction: {
title: 'Use marker image function',

View File

@ -24,7 +24,7 @@ import { UtilsService } from '@core/services/utils.service';
import { TranslateService } from '@ngx-translate/core';
import { DataKey, Datasource, DatasourceData, DatasourceType, WidgetConfig } from '@shared/models/widget.models';
import { IWidgetSubscription } from '@core/api/widget-api.models';
import { isDefined, isEqual, isUndefined } from '@core/utils';
import { isDefined, isEqual, isUndefined, createLabelFromDatasource } from '@core/utils';
import { EntityType } from '@shared/models/entity-type.models';
import * as _moment from 'moment';
import { FormBuilder, FormGroup, ValidatorFn, Validators } from '@angular/forms';
@ -331,7 +331,7 @@ export class MultipleInputWidgetComponent extends PageComponent implements OnIni
}
public getGroupTitle(datasource: Datasource): string {
return this.utils.createLabelFromDatasource(datasource, this.settings.groupTitle);
return createLabelFromDatasource(datasource, this.settings.groupTitle);
}
public visibleKeys(source: MultipleInputWidgetSource): MultipleInputWidgetDataKey[] {