From 9a993d286e27d2b90883247a1376a0a8cb84f6f5 Mon Sep 17 00:00:00 2001 From: Artem Halushko Date: Thu, 30 Apr 2020 18:52:57 +0300 Subject: [PATCH 1/4] bugfixes --- ui-ngx/src/app/core/api/alias-controller.ts | 4 +- ui-ngx/src/app/core/services/utils.service.ts | 53 ++++++------------- ui-ngx/src/app/core/utils.ts | 37 +++++++++++-- .../lib/alarms-table-widget.component.ts | 4 +- .../lib/entities-table-widget.component.ts | 4 +- .../components/widget/lib/maps/leaflet-map.ts | 45 ++++++++++------ .../components/widget/lib/maps/map-models.ts | 4 ++ .../components/widget/lib/maps/polygon.ts | 21 ++++++-- .../components/widget/lib/maps/schemes.ts | 9 +++- .../lib/multiple-input-widget.component.ts | 4 +- 10 files changed, 112 insertions(+), 73 deletions(-) diff --git a/ui-ngx/src/app/core/api/alias-controller.ts b/ui-ngx/src/app/core/api/alias-controller.ts index 848da23861..095335839c 100644 --- a/ui-ngx/src/app/core/api/alias-controller.ts +++ b/ui-ngx/src/app/core/api/alias-controller.ts @@ -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 { diff --git a/ui-ngx/src/app/core/services/utils.service.ts b/ui-ngx/src/app/core/services/utils.service.ts index 24e2c81d03..01341c0941 100644 --- a/ui-ngx/src/app/core/services/utils.service.ts +++ b/ui-ngx/src/app/core/services/utils.service.ts @@ -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'; @@ -36,7 +36,7 @@ import { Observable, of, ReplaySubject } from 'rxjs'; const varsRegex = /\$\{([^}]*)\}/g; -const predefinedFunctions: {[func: string]: string} = { +const predefinedFunctions: { [func: string]: string } = { Sin: 'return Math.round(1000*Math.sin(time/5000));', Cos: 'return Math.round(1000*Math.cos(time/5000));', Random: 'var value = prevValue + Math.random() * 100 - 50;\n' + @@ -63,12 +63,12 @@ const defaultAlarmFields: Array = [ alarmFields.status.keyName ]; -const commonMaterialIcons: Array = [ 'more_horiz', 'more_vert', 'open_in_new', +const commonMaterialIcons: Array = ['more_horiz', 'more_vert', 'open_in_new', 'visibility', 'play_arrow', 'arrow_back', 'arrow_downward', 'arrow_forward', 'arrow_upwards', 'close', 'refresh', 'menu', 'show_chart', 'multiline_chart', 'pie_chart', 'insert_chart', 'people', 'person', 'domain', 'devices_other', 'now_widgets', 'dashboards', 'map', 'pin_drop', 'my_location', 'extension', 'search', 'settings', 'notifications', 'notifications_active', 'info', 'info_outline', 'warning', 'list', 'file_download', 'import_export', - 'share', 'add', 'edit', 'done' ]; + 'share', 'add', 'edit', 'done']; // @dynamic @Injectable({ @@ -101,8 +101,8 @@ export class UtilsService { materialIcons: Array = []; constructor(@Inject(WINDOW) private window: Window, - private zone: NgZone, - private translate: TranslateService) { + private zone: NgZone, + private translate: TranslateService) { let frame: Element = null; try { frame = window.frameElement; @@ -302,10 +302,10 @@ export class UtilsService { .split('\n') .filter((codepoint) => codepoint && codepoint.length); codepointsArray.forEach((codepoint) => { - const values = codepoint.split(' '); - if (values && values.length === 2) { - this.materialIcons.push(values[0]); - } + const values = codepoint.split(' '); + if (values && values.length === 2) { + this.materialIcons.push(values[0]); + } }); materialIconsSubject.next(this.materialIcons); }); @@ -360,12 +360,12 @@ export class UtilsService { } public createAdditionalDataKey(dataKey: DataKey, datasource: Datasource, timeUnit: string, - datasources: Datasource[], additionalKeysNumber: number): DataKey { + 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); + additionalDataKey.label = dataKey.label + ' ' + this.translate.instant('legend.comparison-time-ago.' + timeUnit); } additionalDataKey.pattern = additionalDataKey.label; if (dataKey.settings.comparisonSettings.color) { @@ -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) { @@ -456,7 +433,7 @@ export class UtilsService { params = urlQueryString + '&' + newParam; } } else if (newParam) { - params = '?' + newParam; + params = '?' + newParam; } this.window.history.replaceState({}, '', baseUrl + params); } diff --git a/ui-ngx/src/app/core/utils.ts b/ui-ngx/src/app/core/utils.ts index 490f157ac3..ecde160b13 100644 --- a/ui-ngx/src/app/core/utils.ts +++ b/ui-ngx/src/app/core/utils.ts @@ -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 { const scrollSubject = new Subject(); @@ -435,6 +436,34 @@ export function imageLoader(imageUrl: string): Observable { 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 { @@ -452,7 +481,6 @@ export function aspectCache(imageUrl: string): Observable { } } - 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(//g, 'a>').replace(/name=(\'|")(.*?)(\'|")/g, `class='tb-custom-action' id='$2'`); + template = template.replace(//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 => { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.ts index ee8f72048b..1eb7b39ca9 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.ts @@ -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) { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.ts index bbdb145835..54f8344890 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.ts @@ -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; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/leaflet-map.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/leaflet-map.ts index 48909639ef..d98b564c2f 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/leaflet-map.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/leaflet-map.ts @@ -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: `
` + html: `
` }) } 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()); }); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts index b50ff189fb..eee38e54ed 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts @@ -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; } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/polygon.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/polygon.ts index ca601e3e15..e9e11acf48 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/polygon.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/polygon.ts @@ -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); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/schemes.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/schemes.ts index 51740ae2ca..5e55af55f3 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/schemes.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/schemes.ts @@ -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', diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/multiple-input-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/multiple-input-widget.component.ts index 01adeacdce..890a91e76f 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/multiple-input-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/multiple-input-widget.component.ts @@ -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[] { From 19b0da39cacf3a28119809c701c698bafdb2b013 Mon Sep 17 00:00:00 2001 From: Artem Halushko Date: Mon, 4 May 2020 11:51:20 +0300 Subject: [PATCH 2/4] provide entity to custom actions --- ui-ngx/src/app/core/utils.ts | 7 ++++--- .../components/widget/lib/maps/map-models.ts | 9 +++++---- .../components/widget/lib/maps/map-widget2.ts | 16 ++++++++-------- .../components/widget/lib/maps/maps-utils.ts | 11 +++++++---- .../home/components/widget/lib/maps/markers.ts | 4 ++-- .../home/components/widget/lib/maps/polygon.ts | 16 +++++++++++----- 6 files changed, 37 insertions(+), 26 deletions(-) diff --git a/ui-ngx/src/app/core/utils.ts b/ui-ngx/src/app/core/utils.ts index ecde160b13..c47dc8165d 100644 --- a/ui-ngx/src/app/core/utils.ts +++ b/ui-ngx/src/app/core/utils.ts @@ -19,6 +19,7 @@ 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'; +import { FormattedData } from '@app/modules/home/components/widget/lib/maps/map-models'; export function onParentScrollOrWindowResize(el: Node): Observable { const scrollSubject = new Subject(); @@ -504,12 +505,12 @@ export function parseArray(input: any[]): any[] { ); } -export function parseData(input: any[]): any[] { +export function parseData(input: any[]): FormattedData[] { return _(input).groupBy(el => el?.datasource?.entityName) .values().value().map((entityArray, i) => { const obj = { entityName: entityArray[0]?.datasource?.entityName, - $datasource: entityArray[0]?.datasource, + $datasource: entityArray[0]?.datasource as Datasource, dsIndex: i, deviceType: null }; @@ -568,7 +569,7 @@ export function parseTemplate(template: string, data: { $datasource?: Datasource formatted.forEach(value => { const [variable, digits] = value.replace('${', '').replace('}', '').split(':'); data[variable] = padValue(data[variable], +digits); - if (isNaN(data[variable])) data[value] = ''; + if (data[variable] === 'NaN') data[variable] = ''; template = template.replace(value, '${' + variable + '}'); }); const variables = template.match(/\$\{.*?\}/g); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts index eee38e54ed..c3219f90c2 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts @@ -15,6 +15,7 @@ /// import { LatLngTuple, LeafletMouseEvent } from 'leaflet'; +import { Datasource } from '@app/shared/models/widget.models'; export type GenericFunction = (data: FormattedData, dsData: FormattedData[], dsIndex: number) => string; export type MarkerImageFunction = (data: FormattedData, dsData: FormattedData[], dsIndex: number) => string; @@ -96,11 +97,11 @@ export type MarkerSettings = { } export interface FormattedData { - aliasName: string; + $datasource: Datasource; entityName: string; - $datasource: string; dsIndex: number; - deviceType: string + deviceType: string; + [key: string]: any } export type PolygonSettings = { @@ -151,6 +152,6 @@ export interface HistorySelectSettings { buttonColor: string; } -export type actionsHandler = ($event: Event | LeafletMouseEvent) => void; +export type actionsHandler = ($event: Event | LeafletMouseEvent, datasource: Datasource) => void; export type UnitedMapSettings = MapSettings & PolygonSettings & MarkerSettings & PolylineSettings; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget2.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget2.ts index f45150b4f0..82db9c2b29 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget2.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget2.ts @@ -36,7 +36,7 @@ import { initSchema, addToSchema, mergeSchemes, addCondition, addGroupInfo } fro import { of, Subject } from 'rxjs'; import { WidgetContext } from '@app/modules/home/models/widget-component.models'; import { getDefCenterPosition } from './maps-utils'; -import { JsonSettingsSchema, WidgetActionDescriptor, DatasourceType, widgetType } from '@shared/models/widget.models'; +import { JsonSettingsSchema, WidgetActionDescriptor, DatasourceType, widgetType, Datasource } from '@shared/models/widget.models'; import { EntityId } from '@shared/models/id/entity-id'; import { AttributeScope, DataKeyType, LatestTelemetry } from '@shared/models/telemetry/telemetry.models'; import { AttributeService } from '@core/http/attribute.service'; @@ -142,7 +142,7 @@ export class MapWidgetController implements MapWidgetInterface { const descriptors = this.ctx.actionsApi.getActionDescriptors(name); const actions = {}; descriptors.forEach(descriptor => { - actions[descriptor.name] = ($event: Event) => this.onCustomAction(descriptor, $event); + actions[descriptor.name] = ($event: Event, datasource: Datasource) => this.onCustomAction(descriptor, $event, datasource); }, actions); return actions; } @@ -150,15 +150,15 @@ export class MapWidgetController implements MapWidgetInterface { onInit() { } - private onCustomAction(descriptor: WidgetActionDescriptor, $event: any) { + private onCustomAction(descriptor: WidgetActionDescriptor, $event: any, entityInfo: Datasource) { if ($event && $event.stopPropagation) { $event?.stopPropagation(); } - // safeExecute(parseFunction(descriptor.customFunction, ['$event', 'widgetContext']), [$event, this.ctx]) - const entityInfo = this.ctx.actionsApi.getActiveEntityInfo(); - const entityId = entityInfo ? entityInfo.entityId : null; - const entityName = entityInfo ? entityInfo.entityName : null; - const entityLabel = entityInfo ? entityInfo.entityLabel : null; + const { id, entityName, entityLabel, entityType } = entityInfo; + const entityId: EntityId = { + entityType, + id + }; this.ctx.actionsApi.handleWidgetAction($event, descriptor, entityId, entityName, null, entityLabel); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/maps-utils.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/maps-utils.ts index f3c825c069..0cb3f8765a 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/maps-utils.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/maps-utils.ts @@ -16,19 +16,22 @@ import L from 'leaflet'; import { MarkerSettings, PolygonSettings, PolylineSettings } from './map-models'; +import { Datasource } from '@app/shared/models/widget.models'; export function createTooltip(target: L.Layer, settings: MarkerSettings | PolylineSettings | PolygonSettings, - content?: string | HTMLElement): L.Popup { + datasource: Datasource, + content?: string | HTMLElement +): L.Popup { const popup = L.popup(); popup.setContent(content); target.bindPopup(popup, { autoClose: settings.autocloseTooltip, closeOnClick: false }); if (settings.showTooltipAction === 'hover') { target.off('click'); - target.on('mouseover', function () { + target.on('mouseover', () => { target.openPopup(); }); - target.on('mouseout', function () { + target.on('mouseout', () => { target.closePopup(); }); } @@ -37,7 +40,7 @@ export function createTooltip(target: L.Layer, Array.from(actions).forEach( (element: HTMLElement) => { if (element && settings.tooltipAction[element.id]) { - element.addEventListener('click', settings.tooltipAction[element.id]) + element.addEventListener('click', ($event) => settings.tooltipAction[element.id]($event, datasource)); } }); }); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.ts index 6fb272acf5..cc2da88514 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.ts @@ -42,7 +42,7 @@ export class Marker { }); if (settings.showTooltip) { - this.tooltip = createTooltip(this.leafletMarker, settings); + this.tooltip = createTooltip(this.leafletMarker, settings, data.$datasource); this.updateMarkerTooltip(data); } @@ -50,7 +50,7 @@ export class Marker { this.leafletMarker.on('click', (event: LeafletMouseEvent) => { for (const action in this.settings.markerClick) { if (typeof (this.settings.markerClick[action]) === 'function') { - this.settings.markerClick[action](event); + this.settings.markerClick[action](event, this.data.$datasource); } } }); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/polygon.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/polygon.ts index e9e11acf48..31843c0eaf 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/polygon.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/polygon.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import L, { LatLngExpression, LatLngTuple } from 'leaflet'; +import L, { LatLngExpression, LatLngTuple, LeafletMouseEvent } from 'leaflet'; import { createTooltip } from './maps-utils'; import { PolygonSettings, FormattedData } from './map-models'; import { DatasourceData } from '@app/shared/models/widget.models'; @@ -27,7 +27,7 @@ export class Polygon { data; dataSources; - constructor(public map, polyData: DatasourceData, dataSources, private settings: PolygonSettings, onClickListener?) { + constructor(public map, polyData: DatasourceData, dataSources, private settings: PolygonSettings) { this.leafletPoly = L.polygon(polyData.data, { fill: true, fillColor: settings.polygonColor, @@ -39,11 +39,17 @@ export class Polygon { this.dataSources = dataSources; this.data = polyData; if (settings.showPolygonTooltip) { - this.tooltip = createTooltip(this.leafletPoly, settings); + this.tooltip = createTooltip(this.leafletPoly, settings, polyData.datasource); this.updateTooltip(polyData); } - if (onClickListener) { - this.leafletPoly.on('click', onClickListener); + if (settings.polygonClick) { + this.leafletPoly.on('click', (event: LeafletMouseEvent) => { + for (const action in this.settings.polygonClick) { + if (typeof (this.settings.polygonClick[action]) === 'function') { + this.settings.polygonClick[action](event, polyData.datasource); + } + } + }); } } From 7245f88014dbce966bfe67347c08eb9a8182bbe6 Mon Sep 17 00:00:00 2001 From: Artem Halushko Date: Mon, 4 May 2020 16:44:14 +0300 Subject: [PATCH 3/4] remove image map zoom --- .../home/components/widget/lib/maps/providers/image-map.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/image-map.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/image-map.ts index a9680665a5..ab9dc60ef4 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/image-map.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/image-map.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import L, { LatLngLiteral } from 'leaflet'; +import L, { LatLngLiteral, LatLngBounds, LatLngTuple } from 'leaflet'; import LeafletMap from '../leaflet-map'; import { UnitedMapSettings } from '../map-models'; import { aspectCache, parseFunction } from '@app/core/utils'; @@ -108,11 +108,12 @@ export class ImageMap extends LeafletMap { this.updateBounds(updateImage, lastCenterPos); this.map.invalidateSize(true); } - } } } + fitBounds(bounds: LatLngBounds, useDefaultZoom = false, padding?: LatLngTuple) { } + initMap(updateImage?) { if (!this.map && this.aspect > 0) { const center = this.pointToLatLng(this.width / 2, this.height / 2); From cd88afea005b6fcec3cde05f97d20798a95c840a Mon Sep 17 00:00:00 2001 From: Artem Halushko Date: Mon, 4 May 2020 18:04:35 +0300 Subject: [PATCH 4/4] fix empty center position & fix entity id --- .../home/components/widget/lib/maps/leaflet-map.ts | 11 ++++++++--- .../home/components/widget/lib/maps/map-widget2.ts | 9 ++++----- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/leaflet-map.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/leaflet-map.ts index d98b564c2f..64a8621920 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/leaflet-map.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/leaflet-map.ts @@ -198,9 +198,15 @@ export default abstract class LeafletMap { fitBounds(bounds: LatLngBounds, useDefaultZoom = false, padding?: LatLngTuple) { if (bounds.isValid()) { - if ((!this.options.fitMapBounds || this.options.useDefaultCenterPosition) && this.options.defaultZoomLevel) { + this.bounds = this.bounds.extend(bounds); + if (!this.options.fitMapBounds && this.options.defaultZoomLevel) { this.map.setZoom(this.options.defaultZoomLevel, { animate: false }); - this.map.panTo(this.options.defaultCenterPosition, { animate: false }); + if (this.options.useDefaultCenterPosition) { + this.map.panTo(this.options.defaultCenterPosition, { animate: false }); + } + else { + this.map.panTo(this.bounds.getCenter()); + } } else { this.map.once('zoomend', () => { if (!this.options.defaultZoomLevel && this.map.getZoom() > this.options.minZoomLevel) { @@ -212,7 +218,6 @@ export default abstract class LeafletMap { } this.map.fitBounds(bounds, { padding: padding || [50, 50], animate: false }); } - this.bounds = bounds; } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget2.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget2.ts index 82db9c2b29..330e739c31 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget2.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget2.ts @@ -154,12 +154,11 @@ export class MapWidgetController implements MapWidgetInterface { if ($event && $event.stopPropagation) { $event?.stopPropagation(); } - const { id, entityName, entityLabel, entityType } = entityInfo; - const entityId: EntityId = { + const { entityId, entityName, entityLabel, entityType } = entityInfo; + this.ctx.actionsApi.handleWidgetAction($event, descriptor, { entityType, - id - }; - this.ctx.actionsApi.handleWidgetAction($event, descriptor, entityId, entityName, null, entityLabel); + id: entityId + }, entityName, null, entityLabel); } setMarkerLocation = (e) => {