bugfixes (#2705)
This commit is contained in:
parent
c6cf5c43ad
commit
11db772ea2
@ -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 {
|
||||
|
||||
@ -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>) {
|
||||
|
||||
@ -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 => {
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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());
|
||||
});
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -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',
|
||||
|
||||
@ -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[] {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user