diff --git a/ui-ngx/angular.json b/ui-ngx/angular.json index 9d00aa421d..0bdb380c66 100644 --- a/ui-ngx/angular.json +++ b/ui-ngx/angular.json @@ -39,7 +39,9 @@ "node_modules/rc-select/assets/index.less", "node_modules/jstree-bootstrap-theme/dist/themes/proton/style.min.css", "node_modules/leaflet/dist/leaflet.css", - "src/app/modules/home/components/widget/lib/maps/markers.scss" + "src/app/modules/home/components/widget/lib/maps/markers.scss", + "node_modules/leaflet.markercluster/dist/MarkerCluster.css", + "node_modules/leaflet.markercluster/dist/MarkerCluster.Default.css" ], "stylePreprocessorOptions": { "includePaths": [ diff --git a/ui-ngx/package-lock.json b/ui-ngx/package-lock.json index 30d331acfb..aec8d12a34 100644 --- a/ui-ngx/package-lock.json +++ b/ui-ngx/package-lock.json @@ -1816,8 +1816,7 @@ "@types/geojson": { "version": "7946.0.7", "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.7.tgz", - "integrity": "sha512-wE2v81i4C4Ol09RtsWFAqg3BUitWbHSpSlIo+bNdsCJijO9sjme+zm+73ZMCa/qMC8UEERxzGbvmr1cffo2SiQ==", - "dev": true + "integrity": "sha512-wE2v81i4C4Ol09RtsWFAqg3BUitWbHSpSlIo+bNdsCJijO9sjme+zm+73ZMCa/qMC8UEERxzGbvmr1cffo2SiQ==" }, "@types/glob": { "version": "7.1.1", @@ -1881,7 +1880,6 @@ "version": "1.5.12", "resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.5.12.tgz", "integrity": "sha512-61HRMIng+bWvnnAIqUWLBlrd/TQZc4gU+gN1JL4K47EDtwIrcMEhWgi7PdcpbG1YmpH4F0EfOimkvV82gJIl9w==", - "dev": true, "requires": { "@types/geojson": "*" } @@ -1895,6 +1893,14 @@ "@types/leaflet": "*" } }, + "@types/leaflet.markercluster": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@types/leaflet.markercluster/-/leaflet.markercluster-1.4.2.tgz", + "integrity": "sha512-QQ//hevAxMH2dlRQdRre7V/1G+TbtuDtZnZF/75TNwVIgklrsQVCIcS/cvLsl7UUryfPJ6xmoYHfFzK5iGVgpg==", + "requires": { + "@types/leaflet": "*" + } + }, "@types/lodash": { "version": "4.14.150", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.150.tgz", @@ -8605,7 +8611,7 @@ "integrity": "sha512-4O3GWAYJaauMCILm07weko2rHA8a4kjn7+8Lg4s1d7SxwS/3IpkVD/GljbRrIJ1c1W/XGJ3GbuK7RyYZEJChhw==" }, "ngx-flowchart": { - "version": "git://github.com/thingsboard/ngx-flowchart.git#a4157b0eef2eb3646ef920447c7b06b39d54f87f", + "version": "git://github.com/thingsboard/ngx-flowchart.git#97a77477ca8579becf0e3a07866046b4536fe30a", "from": "git://github.com/thingsboard/ngx-flowchart.git#master", "requires": { "tslib": "^1.10.0" diff --git a/ui-ngx/package.json b/ui-ngx/package.json index 7e5ad6a8ec..8a25206c32 100644 --- a/ui-ngx/package.json +++ b/ui-ngx/package.json @@ -108,6 +108,7 @@ "@types/jstree": "^3.3.39", "@types/jszip": "^3.1.7", "@types/leaflet": "^1.5.12", + "@types/leaflet.markercluster": "^1.4.2", "@types/leaflet-polylinedecorator": "^1.6.0", "@types/lodash": "^4.14.150", "@types/raphael": "^2.3.0", diff --git a/ui-ngx/src/app/core/utils.ts b/ui-ngx/src/app/core/utils.ts index 39241c10d1..b0ab617a42 100644 --- a/ui-ngx/src/app/core/utils.ts +++ b/ui-ngx/src/app/core/utils.ts @@ -15,7 +15,7 @@ /// import _ from 'lodash'; -import { Observable, Subject, from, fromEvent, of } from 'rxjs'; +import { Observable, Subject, fromEvent, of } from 'rxjs'; import { finalize, share, map } from 'rxjs/operators'; import base64js from 'base64-js'; @@ -430,7 +430,7 @@ export function getDescendantProp(obj: any, path: string): any { export function imageLoader(imageUrl: string): Observable { const image = new Image(); - const imageLoad$ = fromEvent(image, 'load').pipe(map(event => image)); + const imageLoad$ = fromEvent(image, 'load').pipe(map(() => image)); image.src = imageUrl; return imageLoad$; } @@ -524,36 +524,49 @@ export function parseFunction(source: any, params: string[] = []): Function { return res; } -export function parseTemplate(template: string, data: object) { +export function parseTemplate(template: string, data: object, translateFn?: (key: string) => string) { let res = ''; + let variables = ''; try { - let variables = ''; - const expressions = template - .match(/\{(.*?)\}/g) // find expressions - .map(exp => exp.replace(/{|}/g, '')) // remove brackets - .map(exp => exp.split(':')) - .map(arr => { - variables += `let ${arr[0]} = ''; `; - return arr; - }) - .filter(arr => !!arr[1]) // filter expressions without format - .reduce((res, current) => { - res[current[0]] = current[1]; - return res; - }, {}); - - for (const key in data) { - if (!key.includes('|')) - variables += `${key} = '${expressions[key] ? padValue(data[key], +expressions[key]) : data[key]}';`; + if (template.match(//g, 'a>').replace(/name=(\'|")(.*?)(\'|")/g, `class='tb-custom-action' id='$2'`); + } + if (template.includes('i18n')) { + const translateRegexp = /\{i18n:(.*?)\}/; + template.match(new RegExp(translateRegexp.source, translateRegexp.flags + 'g')).forEach(match => { + template = template.replace(match, translateFn(match.match(translateRegexp)[1])); + }); + } + const expressions = template.match(/\{(.*?)\}/g); + if (expressions) { + const clearMatches = template.match(/(?<=\{)(.+?)(?=(\}|\:))/g); + for (const key in data) { + if (!key.includes('|')) + variables += `let ${key} = '${clearMatches[key] ? padValue(data[key], +clearMatches[key]) : data[key]}';`; + } + template = template.replace(/\:\d+\}/g, '}'); + res = safeExecute(parseFunction(variables + ' return' + '`' + template + '`')); } - template = template.replace(/:\d+}/g, '}'); - res = safeExecute(parseFunction(variables + 'return' + '`' + template + '`')); + else res = template; } catch (ex) { + console.log(ex, variables, template) } return res; } +export let parseWithTranslation = { + translate(): string { + throw console.error('Translate not assigned'); + }, + parseTemplate(template: string, data: object, forceTranslate = false): string { + return parseTemplate(forceTranslate ? this.translate(template) : template, data, this?.translate); + }, + setTranslate(translateFn: (key: string, defaultTranslation?: string) => string) { + this.translate = translateFn; + } +} + export function padValue(val: any, dec: number): string { let strVal; let n; 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 90bd9341d6..da870813f3 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 @@ -14,12 +14,12 @@ /// limitations under the License. /// -import L, { LatLngTuple } from 'leaflet'; +import L, { LatLngTuple, LatLngBounds, Point } from 'leaflet'; import 'leaflet-providers'; -import 'leaflet.markercluster/dist/MarkerCluster.css' -import 'leaflet.markercluster/dist/MarkerCluster.Default.css' -import 'leaflet.markercluster/dist/leaflet.markercluster' +import 'leaflet.markercluster/dist/MarkerCluster.css'; +import 'leaflet.markercluster/dist/MarkerCluster.Default.css'; +import LM from 'leaflet.markercluster/dist/leaflet.markercluster'; import { MapSettings, MarkerSettings, FormattedData, UnitedMapSettings, PolygonSettings, PolylineSettings } from './map-models'; import { Marker } from './markers'; @@ -34,7 +34,7 @@ export default abstract class LeafletMap { markers: Map = new Map(); polylines: Map = new Map(); polygons: Map = new Map(); - dragMode = true; + dragMode = false; map: L.Map; map$: BehaviorSubject = new BehaviorSubject(null); ready$: Observable = this.map$.pipe(filter(map => !!map)); @@ -43,6 +43,7 @@ export default abstract class LeafletMap { bounds: L.LatLngBounds; newMarker: L.Marker; datasources: FormattedData[]; + markersCluster: LM.markerClusterGroup; constructor(public $container: HTMLElement, options: UnitedMapSettings) { this.options = options; @@ -50,13 +51,38 @@ export default abstract class LeafletMap { public initSettings(options: MapSettings) { const { initCallback, - disableScrollZooming, }: MapSettings = options; + disableScrollZooming, + useClusterMarkers, + zoomOnClick, + showCoverageOnHover, + removeOutsideVisibleBounds, + animate, + chunkedLoading, + maxClusterRadius, + maxZoom }: MapSettings = options; if (disableScrollZooming) { this.map.scrollWheelZoom.disable(); } if (initCallback) { setTimeout(options.initCallback, 0); } + if (useClusterMarkers) { + const clusteringSettings: LM.MarkerClusterGroupOptions = { + zoomToBoundsOnClick: zoomOnClick, + showCoverageOnHover, + removeOutsideVisibleBounds, + animate, + chunkedLoading + }; + if (maxClusterRadius && maxClusterRadius > 0) { + clusteringSettings.maxClusterRadius = Math.floor(maxClusterRadius); + } + if (maxZoom && maxZoom >= 0 && maxZoom < 19) { + clusteringSettings.disableClusteringAtZoom = Math.floor(maxZoom); + } + this.markersCluster = LM.markerClusterGroup(clusteringSettings); + this.ready$.subscribe(map => map.addLayer(this.markersCluster)); + } } addMarkerControl() { @@ -65,7 +91,7 @@ export default abstract class LeafletMap { let addMarker: L.Control; this.map.on('mouseup', (e: L.LeafletMouseEvent) => { mousePositionOnMap = e.latlng; - }) + }); const dragListener = (e: L.DragEndEvent) => { if (e.type === 'dragend' && mousePositionOnMap) { const newMarker = L.marker(mousePositionOnMap).addTo(this.map); @@ -80,10 +106,17 @@ export default abstract class LeafletMap { this.saveMarkerLocation(updatedEnttity); this.map.removeLayer(newMarker); this.deleteMarker(ds.entityName); - this.createMarker(ds.entityName, updatedEnttity, this.datasources, this.options, false); + this.createMarker(ds.entityName, updatedEnttity, this.datasources, this.options); } datasourcesList.append(dsItem); - }) + }); + const deleteBtn = document.createElement('a'); + deleteBtn.appendChild(document.createTextNode('Delete position')); + deleteBtn.setAttribute('color', 'red'); + deleteBtn.onclick = () => { + this.map.removeLayer(newMarker); + } + datasourcesList.append(deleteBtn); const popup = L.popup(); popup.setContent(datasourcesList); newMarker.bindPopup(popup).openPopup(); @@ -96,6 +129,7 @@ export default abstract class LeafletMap { img.src = `assets/add_location.svg`; img.style.width = '32px'; img.style.height = '32px'; + img.title = 'Drag and drop to add marker'; img.onclick = this.dragMarker; img.draggable = true; const draggableImg = new L.Draggable(img); @@ -106,13 +140,10 @@ export default abstract class LeafletMap { onRemove(map) { }, dragMarker: this.dragMarker - } as any); - L.control.addMarker = (opts) => { return new L.Control.AddMarker(opts); } - addMarker = L.control.addMarker({ position: 'topright' }).addTo(this.map); } } @@ -164,9 +195,9 @@ export default abstract class LeafletMap { return this.map.getCenter(); } - fitBounds(bounds, useDefaultZoom = false) { + fitBounds(bounds: LatLngBounds, useDefaultZoom = false, padding?: LatLngTuple) { if (bounds.isValid()) { - if ((this.options.dontFitMapBounds || useDefaultZoom) && this.options.defaultZoomLevel) { + if ((!this.options.fitMapBounds || useDefaultZoom) && this.options.defaultZoomLevel) { this.map.setZoom(this.options.defaultZoomLevel, { animate: false }); this.map.panTo(bounds.getCenter(), { animate: false }); } else { @@ -175,7 +206,7 @@ export default abstract class LeafletMap { this.map.setZoom(this.options.minZoomLevel, { animate: false }); } }); - this.map.fitBounds(bounds, { padding: [50, 50], animate: false }); + this.map.fitBounds(bounds, { padding: padding || [50, 50], animate: false }); } this.bounds = this.bounds.extend(bounds); } @@ -193,8 +224,8 @@ export default abstract class LeafletMap { convertToCustomFormat(position: L.LatLng): object { return { - [this.options.latKeyName]: position.lat, - [this.options.lngKeyName]: position.lng + [this.options.latKeyName]: position.lat % 180, + [this.options.lngKeyName]: position.lng % 180 } } @@ -225,11 +256,17 @@ export default abstract class LeafletMap { this.saveMarkerLocation({ ...data, ...this.convertToCustomFormat(e.target._latlng) }); } - private createMarker(key: string, data: FormattedData, dataSources: FormattedData[], settings: MarkerSettings, setFocus = true) { + private createMarker(key: string, data: FormattedData, dataSources: FormattedData[], settings: MarkerSettings) { this.ready$.subscribe(() => { - const newMarker = new Marker(this.map, this.convertPosition(data), settings, data, dataSources, () => { }, this.dragMarker); - this.fitBounds(this.bounds.extend(newMarker.leafletMarker.getLatLng()), setFocus); + const newMarker = new Marker(this.convertPosition(data), settings, data, dataSources, this.dragMarker); + this.fitBounds(this.bounds.extend(newMarker.leafletMarker.getLatLng()), settings.draggableMarker && this.markers.size > 2); this.markers.set(key, newMarker); + if (this.options.useClusterMarkers) { + this.markersCluster.addLayer(newMarker.leafletMarker); + } + else { + this.map.addLayer(newMarker.leafletMarker); + } }); } @@ -242,6 +279,8 @@ export default abstract class LeafletMap { if (settings.showTooltip) { marker.updateMarkerTooltip(data); } + if (settings.useClusterMarkers) + this.markersCluster.refreshClusters() marker.setDataSources(data, dataSources); marker.updateMarkerIcon(settings); } @@ -320,7 +359,9 @@ export default abstract class LeafletMap { updatePolygon(key: string, data: LatLngTuple[], dataSources: DatasourceData[], settings: PolygonSettings) { this.ready$.subscribe(() => { - this.polygons.get(key).updatePolygon(data, dataSources, settings); + const poly = this.polygons.get(key); + poly.updatePolygon(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 1d2c993ef1..931f70f658 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 @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { LatLngTuple } from 'leaflet'; +import { LatLngTuple, LeafletMouseEvent } from 'leaflet'; export type GenericFunction = (data: FormattedData, dsData: FormattedData[], dsIndex: number) => string; export type MarkerImageFunction = (data: FormattedData, dsData: FormattedData[], dsIndex: number) => string; @@ -24,14 +24,15 @@ export type MapSettings = { draggableMarker: boolean; initCallback?: () => any; defaultZoomLevel?: number; - dontFitMapBounds?: boolean; disableScrollZooming?: boolean; minZoomLevel?: number; + useClusterMarkers: boolean; latKeyName?: string; lngKeyName?: string; xPosKeyName?: string; yPosKeyName?: string; mapProvider: MapProviders; + mapProviderHere: string; mapUrl?: string; mapImageUrl?: string; provider?: MapProviders; @@ -42,6 +43,13 @@ export type MapSettings = { gmDefaultMapType?: string; useLabelFunction: string; icon?: any; + zoomOnClick: boolean, + maxZoom: number, + showCoverageOnHover: boolean, + animate: boolean, + maxClusterRadius: number, + chunkedLoading: boolean, + removeOutsideVisibleBounds: boolean } export enum MapProviders { @@ -54,6 +62,7 @@ export enum MapProviders { export type MarkerSettings = { tooltipPattern?: any; + tooltipAction: { [name: string]: actionsHandler }; icon?: any; showLabel?: boolean; label: string; @@ -62,19 +71,19 @@ export type MarkerSettings = { useLabelFunction: boolean; draggableMarker: boolean; showTooltip?: boolean; + useTooltipFunction: boolean; + useColorFunction: boolean; color?: string; autocloseTooltip: boolean; - displayTooltipAction: string; + showTooltipAction: string; + useClusterMarkers: boolean; currentImage?: string; useMarkerImageFunction?: boolean; markerImages?: string[]; - useMarkerImage: boolean; markerImageSize: number; fitMapBounds: boolean; - markerImage: { - length: number - } - + markerImage: string; + markerClick: { [name: string]: actionsHandler }; colorFunction: GenericFunction; tooltipFunction: GenericFunction; labelFunction: GenericFunction; @@ -98,16 +107,18 @@ export type PolygonSettings = { polygonStrokeColor: string; polygonColor: string; autocloseTooltip: boolean; - displayTooltipAction: string; - + showTooltipAction: string; + tooltipAction: object; + polygonClick: { [name: string]: actionsHandler }; polygonColorFunction?: GenericFunction; } export type PolylineSettings = { usePolylineDecorator: any; autocloseTooltip: boolean; - displayTooltipAction: string; + showTooltipAction: string; useColorFunction: any; + tooltipAction: { [name: string]: actionsHandler }; color: string; useStrokeOpacityFunction: any; strokeOpacity: number; @@ -131,4 +142,6 @@ export interface HistorySelectSettings { buttonColor: string; } -export type UnitedMapSettings = MapSettings & PolygonSettings & MarkerSettings & PolylineSettings; \ No newline at end of file +export type actionsHandler = ($event: Event | LeafletMouseEvent) => void; + +export type UnitedMapSettings = MapSettings & PolygonSettings & MarkerSettings & PolylineSettings; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget.interface.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget.interface.ts index f9a176137e..9900d98e0b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget.interface.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget.interface.ts @@ -15,6 +15,7 @@ /// import { JsonSettingsSchema } from '@shared/models/widget.models'; +import { MapProviders } from './map-models'; export interface MapWidgetInterface { resize(), @@ -24,8 +25,8 @@ export interface MapWidgetInterface { } export interface MapWidgetStaticInterface { - settingsSchema(mapProvider?, drawRoutes?): JsonSettingsSchema; - getProvidersSchema():JsonSettingsSchema + settingsSchema(mapProvider?: MapProviders, drawRoutes?: boolean): JsonSettingsSchema; + getProvidersSchema(mapProvider?: MapProviders): JsonSettingsSchema dataKeySettingsSchema(): object; actionSources(): object; } 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 c2cd46c6b2..5794144bec 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 @@ -26,20 +26,23 @@ import { markerClusteringSettingsSchema, markerClusteringSettingsSchemaLeaflet, hereMapSettingsSchema, - mapProviderSchema + mapProviderSchema, + mapPolygonSchema } from './schemes'; import { MapWidgetStaticInterface, MapWidgetInterface } from './map-widget.interface'; import { OpenStreetMap, TencentMap, GoogleMap, HEREMap, ImageMap } from './providers'; -import { parseFunction, parseArray, parseData } from '@core/utils'; +import { parseFunction, parseArray, parseData, safeExecute, parseWithTranslation } from '@core/utils'; import { initSchema, addToSchema, mergeSchemes, addCondition, addGroupInfo } from '@core/schema-utils'; import { forkJoin } from 'rxjs'; import { WidgetContext } from '@app/modules/home/models/widget-component.models'; import { getDefCenterPosition } from './maps-utils'; -import { JsonSettingsSchema } from '@shared/models/widget.models'; +import { JsonSettingsSchema, WidgetActionDescriptor } from '@shared/models/widget.models'; import { EntityId } from '@shared/models/id/entity-id'; import { AttributeScope } from '@shared/models/telemetry/telemetry.models'; import { AttributeService } from '@core/http/attribute.service'; import { Type } from '@angular/core'; +import { TranslateService } from '@ngx-translate/core'; +import { UtilsService } from '@app/core/public-api'; // @dynamic export class MapWidgetController implements MapWidgetInterface { @@ -55,11 +58,16 @@ export class MapWidgetController implements MapWidgetInterface { $element = ctx.$container[0]; } this.settings = this.initSettings(ctx.settings); + this.settings.tooltipAction = this.getDescriptors('tooltipAction'); + this.settings.markerClick = this.getDescriptors('markerClick'); + this.settings.polygonClick = this.getDescriptors('polygonClick'); + // this.settings. const MapClass = providerSets[this.provider]?.MapClass; if (!MapClass) { return; } + parseWithTranslation.setTranslate(this.translate); this.map = new MapClass($element, this.settings); this.map.saveMarkerLocation = this.setMarkerLocation; } @@ -74,7 +82,8 @@ export class MapWidgetController implements MapWidgetInterface { return {}; } - public static getProvidersSchema() { + public static getProvidersSchema(mapProvider: MapProviders) { + mapProviderSchema.schema.properties.provider.default = mapProvider; return mergeSchemes([mapProviderSchema, ...Object.values(providerSets)?.map( (setting: IProvider) => addCondition(setting?.schema, `model.provider === '${setting.name}'`))]); @@ -82,19 +91,20 @@ export class MapWidgetController implements MapWidgetInterface { public static settingsSchema(mapProvider: MapProviders, drawRoutes: boolean): JsonSettingsSchema { const schema = initSchema(); - addToSchema(schema, this.getProvidersSchema()); - addGroupInfo(schema, 'Map Provider Settings'); - addToSchema(schema, commonMapSettingsSchema); + addToSchema(schema, this.getProvidersSchema(mapProvider)); + if(mapProvider!=='image-map'){ + addGroupInfo(schema, 'Map Provider Settings'); + addToSchema(schema, mergeSchemes([commonMapSettingsSchema, addCondition(mapPolygonSchema, 'model.showPolygon === true')])); addGroupInfo(schema, 'Common Map Settings'); - if (drawRoutes) { addToSchema(schema, routeMapSettingsSchema); addGroupInfo(schema, 'Route Map Settings'); } else if (mapProvider !== 'image-map') { - const clusteringSchema = mergeSchemes([markerClusteringSettingsSchemaLeaflet, markerClusteringSettingsSchema]) + const clusteringSchema = mergeSchemes([markerClusteringSettingsSchema, + addCondition(markerClusteringSettingsSchemaLeaflet, `model.useClusterMarkers === true`)]) addToSchema(schema, clusteringSchema); addGroupInfo(schema, 'Markers Clustering Settings'); - } + }} return schema; } @@ -115,9 +125,35 @@ export class MapWidgetController implements MapWidgetInterface { }; } + translate = (key: string, defaultTranslation?: string):string => { + return (this.ctx.$injector.get(UtilsService).customTranslation(key, defaultTranslation || key) + || this.ctx.$injector.get(TranslateService).instant(key)); + } + + getDescriptors(name: string): { [name: string]: ($event: Event) => void } { + const descriptors = this.ctx.actionsApi.getActionDescriptors(name); + const actions = {}; + descriptors.forEach(descriptor => { + actions[descriptor.name] = ($event: Event) => this.onCustomAction(descriptor, $event); + }, actions); + return actions; + } + onInit() { } + private onCustomAction(descriptor: WidgetActionDescriptor, $event: any) { + 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; + this.ctx.actionsApi.handleWidgetAction($event, descriptor, entityId, entityName, null, entityLabel); + } + setMarkerLocation = (e) => { const attributeService = this.ctx.$injector.get(AttributeService); forkJoin( @@ -136,13 +172,17 @@ export class MapWidgetController implements MapWidgetInterface { }] ); })).subscribe(res => { - console.log('MapWidgetController -> setMarkerLocation -> res', res) }); } initSettings(settings: UnitedMapSettings): UnitedMapSettings { const functionParams = ['data', 'dsData', 'dsIndex']; this.provider = settings.provider || this.mapProvider; + if (!settings.mapProviderHere) { + if (settings.mapProvider && hereProviders.includes(settings.mapProvider)) + settings.mapProviderHere = settings.mapProvider + else settings.mapProviderHere = hereProviders[0]; + } const customOptions = { provider: this.provider, mapUrl: settings?.mapImageUrl, @@ -156,7 +196,7 @@ export class MapWidgetController implements MapWidgetInterface { '${entityName}

Latitude: ${' + settings.latKeyName + ':7}
Longitude: ${' + settings.lngKeyName + ':7}', defaultCenterPosition: getDefCenterPosition(settings?.defaultCenterPosition), - currentImage: (settings.useMarkerImage && settings.markerImage?.length) ? { + currentImage: (settings.markerImage?.length) ? { url: settings.markerImage, size: settings.markerImageSize || 34 } : null @@ -194,7 +234,7 @@ interface IProvider { name: string } -export const providerSets: {[key: string]: IProvider} = { +export const providerSets: { [key: string]: IProvider } = { 'openstreet-map': { MapClass: OpenStreetMap, schema: openstreetMapSettingsSchema, @@ -236,7 +276,7 @@ export const defaultSettings: any = { useDefaultCenterPosition: false, showTooltipAction: 'click', autocloseTooltip: false, - showPolygon: true, + showPolygon: false, labelColor: '#000000', color: '#FE7569', polygonColor: '#0000ff', @@ -250,10 +290,16 @@ export const defaultSettings: any = { strokeOpacity: 1.0, initCallback: () => { }, defaultZoomLevel: 8, - dontFitMapBounds: false, disableScrollZooming: false, minZoomLevel: 16, credentials: '', markerClusteringSetting: null, - draggableMarker: false + draggableMarker: false, + fitMapBounds: true }; + +export const hereProviders = [ + 'HERE.normalDay', + 'HERE.normalNight', + 'HERE.hybridDay', + 'HERE.terrainDay'] 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 7af0828891..4ac437830c 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 @@ -17,20 +17,32 @@ import L from 'leaflet'; import _ from 'lodash'; import { MarkerSettings, PolylineSettings, PolygonSettings } from './map-models'; +import { parseWithTranslation } from '@app/core/utils'; -export function createTooltip(target: L.Layer, settings: MarkerSettings | PolylineSettings | PolygonSettings): L.Popup { +export function createTooltip(target: L.Layer, + settings: MarkerSettings | PolylineSettings | PolygonSettings, + content?: string | HTMLElement): L.Popup { const popup = L.popup(); - popup.setContent(''); + popup.setContent(content); target.bindPopup(popup, { autoClose: settings.autocloseTooltip, closeOnClick: false }); - if (settings.displayTooltipAction === 'hover') { + if (settings.showTooltipAction === 'hover') { target.off('click'); target.on('mouseover', function () { - this.openPopup(); + target.openPopup(); }); target.on('mouseout', function () { - this.closePopup(); + target.closePopup(); }); } + target.on('popupopen', () => { + const actions = document.getElementsByClassName('tb-custom-action'); + Array.from(actions).forEach( + (element: HTMLElement) => { + if (element && settings.tooltipAction[element.id]) { + element.addEventListener('click', settings.tooltipAction[element.id]) + } + }); + }); return popup; } @@ -51,5 +63,4 @@ export function getDefCenterPosition(position) { if (typeof (position) === 'object') return position; return [0, 0]; -} - +} \ No newline at end of file diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.scss b/ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.scss index a017edeb9f..d402a4b5d9 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.scss @@ -29,3 +29,7 @@ background: none; box-shadow: none; } + +.leaflet-container{ + background-color: white; +} 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 0903c5cd77..e032254b4d 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 @@ -14,13 +14,13 @@ /// limitations under the License. /// -import L from 'leaflet'; -import { MarkerSettings, FormattedData } from './map-models'; -import { aspectCache, safeExecute, parseTemplate } from '@app/core/utils'; +import { aspectCache, parseWithTranslation, safeExecute } from '@app/core/utils'; +import L, { LeafletMouseEvent } from 'leaflet'; +import { FormattedData, MarkerSettings } from './map-models'; import { createTooltip } from './maps-utils'; +import tinycolor from 'tinycolor2'; export class Marker { - leafletMarker: L.Marker; tooltipOffset: [number, number]; tooltip: L.Popup; @@ -28,8 +28,8 @@ export class Marker { data: FormattedData; dataSources: FormattedData[]; - constructor(private map: L.Map, location: L.LatLngExpression, public settings: MarkerSettings, - data?, dataSources?, onClickListener?, onDragendListener?) { + constructor(location: L.LatLngExpression, public settings: MarkerSettings, + data?: FormattedData, dataSources?, onDragendListener?) { this.setDataSources(data, dataSources); this.leafletMarker = L.marker(location, { draggable: settings.draggableMarker @@ -39,16 +39,21 @@ export class Marker { this.leafletMarker.setIcon(iconInfo.icon); this.tooltipOffset = [0, -iconInfo.size[1] + 10]; this.updateMarkerLabel(settings); - this.leafletMarker.addTo(map); }); if (settings.showTooltip) { this.tooltip = createTooltip(this.leafletMarker, settings); - this.tooltip.setContent(parseTemplate(this.settings.tooltipPattern, data)); + this.updateMarkerTooltip(data); } - if (onClickListener) { - this.leafletMarker.on('click', onClickListener); + if (this.settings.markerClick) { + 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); + } + } + }); } if (onDragendListener) { @@ -62,7 +67,9 @@ export class Marker { } updateMarkerTooltip(data: FormattedData) { - this.tooltip.setContent(parseTemplate(this.settings.tooltipPattern, data)); + 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)); } updateMarkerPosition(position: L.LatLngExpression) { @@ -71,13 +78,10 @@ export class Marker { updateMarkerLabel(settings: MarkerSettings) { this.leafletMarker.unbindTooltip(); - if (settings.showLabel) { - if (settings.useLabelFunction) { - settings.labelText = parseTemplate( - safeExecute(settings.labelFunction, [this.data, this.dataSources, this.data.dsIndex]), this.data) - } - else settings.labelText = parseTemplate(settings.label, this.data); + const pattern = settings.useLabelFunction ? + safeExecute(settings.labelFunction, [this.data, this.dataSources, this.data.dsIndex]) : settings.label; + settings.labelText = parseWithTranslation.parseTemplate(pattern, this.data, true); this.leafletMarker.bindTooltip(`
${settings.labelText}
`, { className: 'tb-marker-label', permanent: true, direction: 'top', offset: this.tooltipOffset }); } @@ -98,7 +102,6 @@ export class Marker { } createMarkerIcon(onMarkerIconReady) { - if (this.settings.icon) { onMarkerIconReady({ size: [30, 30], @@ -106,11 +109,11 @@ export class Marker { }); return; } - const currentImage = this.settings.useMarkerImageFunction ? safeExecute(this.settings.markerImageFunction, [this.data, this.settings.markerImages, this.dataSources, this.data.dsIndex]) : this.settings.currentImage; - + const currentColor = tinycolor(this.settings.useColorFunction ? safeExecute(this.settings.colorFunction, + [this.data, this.dataSources, this.data.dsIndex]) : this.settings.color).toHex(); if (currentImage && currentImage.url) { aspectCache(currentImage.url).subscribe( @@ -137,19 +140,19 @@ export class Marker { }; onMarkerIconReady(iconInfo); } else { - this.createDefaultMarkerIcon(this.settings.color, onMarkerIconReady); + this.createDefaultMarkerIcon(currentColor, onMarkerIconReady); } } ); } else { - this.createDefaultMarkerIcon(this.settings.color, onMarkerIconReady); + this.createDefaultMarkerIcon(currentColor, onMarkerIconReady); } } createDefaultMarkerIcon(color, onMarkerIconReady) { const pinColor = color.substr(1); const icon = L.icon({ - iconUrl: 'https://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=%E2%80%A2|' + pinColor, + iconUrl: 'https://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=%E2%80%A2|' + color, iconSize: [21, 34], iconAnchor: [10, 34], popupAnchor: [0, -34], diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/here-map.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/here-map.ts index 60221b62a3..2b48e3ee8b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/here-map.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/here-map.ts @@ -20,14 +20,9 @@ import { MapSettings, UnitedMapSettings } from '../map-models'; export class HEREMap extends LeafletMap { constructor($container, options: UnitedMapSettings) { - const defaultCredentials = - { - app_id: 'AhM6TzD9ThyK78CT3ptx', - app_code: 'p6NPiITB3Vv0GMUFnkLOOg' - } super($container, options); const map = L.map($container).setView(options?.defaultCenterPosition, options?.defaultZoomLevel); - const tileLayer = (L.tileLayer as any).provider(options.mapProvider || 'HERE.normalDay', options.credentials || defaultCredentials); + const tileLayer = (L.tileLayer as any).provider(options.mapProviderHere || 'HERE.normalDay', options.credentials); tileLayer.addTo(map); super.setMap(map); super.initSettings(options); 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 4222ad11fd..64e3a39543 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 @@ -110,7 +110,7 @@ export const hereMapSettingsSchema = title: 'HERE Map Configuration', type: 'object', properties: { - mapProvider: { + mapProviderHere: { title: 'Map layer', type: 'string', default: 'HERE.normalDay' @@ -120,11 +120,13 @@ export const hereMapSettingsSchema = properties: { app_id: { title: 'HERE app id', - type: 'string' + type: 'string', + default: 'AhM6TzD9ThyK78CT3ptx' }, app_code: { title: 'HERE app code', - type: 'string' + type: 'string', + default: 'p6NPiITB3Vv0GMUFnkLOOg' } }, required: ['app_id', 'app_code'] @@ -134,7 +136,7 @@ export const hereMapSettingsSchema = }, form: [ { - key: 'mapProvider', + key: 'mapProviderHere', type: 'rc-select', multiple: false, items: [ @@ -339,43 +341,6 @@ export const commonMapSettingsSchema = type: 'boolean', default: false }, - polygonKeyName: { - title: 'Polygon key name', - type: 'string', - default: 'coordinates' - }, - polygonColor: { - title: 'Polygon color', - type: 'string' - }, - polygonOpacity: { - title: 'Polygon opacity', - type: 'number', - default: 0.5 - }, - polygonStrokeColor: { - title: 'Stroke color', - type: 'string' - }, - polygonStrokeOpacity: { - title: 'Stroke opacity', - type: 'number', - default: 1 - }, - polygonStrokeWeight: { - title: 'Stroke weight', - type: 'number', - default: 1 - }, - usePolygonColorFunction: { - title: 'Use polygon color function', - type: 'boolean', - default: false - }, - polygonColorFunction: { - title: 'Polygon Color function: f(data, dsData, dsIndex)', - type: 'string' - }, markerImage: { title: 'Custom marker image', type: 'string' @@ -455,20 +420,6 @@ export const commonMapSettingsSchema = { key: 'colorFunction', type: 'javascript' - }, 'showPolygon', 'polygonKeyName', - { - key: 'polygonColor', - type: 'color' - }, - 'polygonOpacity', - { - key: 'polygonStrokeColor', - type: 'color' - }, - 'polygonStrokeOpacity', 'polygonStrokeWeight', 'usePolygonColorFunction', - { - key: 'polygonColorFunction', - type: 'javascript' }, { key: 'markerImage', @@ -488,7 +439,73 @@ export const commonMapSettingsSchema = type: 'image' } ] - } + }, + 'showPolygon', + ] +}; + +export const mapPolygonSchema = +{ + schema: { + title: 'Map Polygon Configuration', + type: 'object', + properties: { + polygonKeyName: { + title: 'Polygon key name', + type: 'string', + default: 'coordinates' + }, + polygonColor: { + title: 'Polygon color', + type: 'string' + }, + polygonOpacity: { + title: 'Polygon opacity', + type: 'number', + default: 0.5 + }, + polygonStrokeColor: { + title: 'Stroke color', + type: 'string' + }, + polygonStrokeOpacity: { + title: 'Stroke opacity', + type: 'number', + default: 1 + }, + polygonStrokeWeight: { + title: 'Stroke weight', + type: 'number', + default: 1 + }, + usePolygonColorFunction: { + title: 'Use polygon color function', + type: 'boolean', + default: false + }, + polygonColorFunction: { + title: 'Polygon Color function: f(data, dsData, dsIndex)', + type: 'string' + }, + }, + required: [] + }, + form: [ + 'polygonKeyName', + { + key: 'polygonColor', + type: 'color' + }, + 'polygonOpacity', + { + key: 'polygonStrokeColor', + type: 'color' + }, + 'polygonStrokeOpacity', 'polygonStrokeWeight', 'usePolygonColorFunction', + { + key: 'polygonColorFunction', + type: 'javascript' + }, ] }; @@ -527,23 +544,12 @@ export const markerClusteringSettingsSchema = title: 'Use map markers clustering', type: 'boolean', default: false - }, - zoomOnClick: { - title: 'Zoom when clicking on a cluster', - type: 'boolean', - default: true - }, - maxZoom: { - title: 'The maximum zoom level when a marker can be part of a cluster (0 - 18)', - type: 'number' } }, required: [] }, form: [ 'useClusterMarkers', - 'zoomOnClick', - 'maxZoom' ] }; @@ -571,12 +577,23 @@ export const markerClusteringSettingsSchemaGoogle = ] }; + + export const markerClusteringSettingsSchemaLeaflet = { schema: { title: 'Markers Clustering Configuration Leaflet', type: 'object', properties: { + zoomOnClick: { + title: 'Zoom when clicking on a cluster', + type: 'boolean', + default: true + }, + maxZoom: { + title: 'The maximum zoom level when a marker can be part of a cluster (0 - 18)', + type: 'number' + }, showCoverageOnHover: { title: 'Show the bounds of markers when mouse over a cluster', type: 'boolean', @@ -606,6 +623,8 @@ export const markerClusteringSettingsSchemaLeaflet = required: [] }, form: [ + 'zoomOnClick', + 'maxZoom', 'showCoverageOnHover', 'animate', 'maxClusterRadius', diff --git a/ui-ngx/src/app/modules/home/components/widget/trip-animation/trip-animation.component.html b/ui-ngx/src/app/modules/home/components/widget/trip-animation/trip-animation.component.html index 1b21fe253f..2b070052a5 100644 --- a/ui-ngx/src/app/modules/home/components/widget/trip-animation/trip-animation.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/trip-animation/trip-animation.component.html @@ -17,17 +17,17 @@ -->
- {{settings.label | tbParseTemplate: activeTrip}} + {{label }}
-
+
-
+
-
diff --git a/ui-ngx/src/app/modules/home/components/widget/trip-animation/trip-animation.component.scss b/ui-ngx/src/app/modules/home/components/widget/trip-animation/trip-animation.component.scss index d7be4629c3..cdb28f8a27 100644 --- a/ui-ngx/src/app/modules/home/components/widget/trip-animation/trip-animation.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/trip-animation/trip-animation.component.scss @@ -74,10 +74,10 @@ .trip-animation-tooltip { position: absolute; - top: 38px; + top: 30px; right: 0; - z-index: 400; - padding: 10px; + z-index: 1000; + padding: 5px; background-color: #fff; transition: 0.3s ease-in-out; @@ -86,4 +86,4 @@ } } } -} +} \ No newline at end of file diff --git a/ui-ngx/src/app/modules/home/components/widget/trip-animation/trip-animation.component.ts b/ui-ngx/src/app/modules/home/components/widget/trip-animation/trip-animation.component.ts index 21e3f87873..5d2f8bfa2e 100644 --- a/ui-ngx/src/app/modules/home/components/widget/trip-animation/trip-animation.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/trip-animation/trip-animation.component.ts @@ -22,13 +22,14 @@ import { interpolateOnPointSegment } from 'leaflet-geometryutil'; import { Component, OnInit, Input, ViewChild, AfterViewInit, ChangeDetectorRef, SecurityContext } from '@angular/core'; import { MapWidgetController, TbMapWidgetV2 } from '../lib/maps/map-widget2'; import { MapProviders } from '../lib/maps/map-models'; -import { parseArray, parseTemplate, safeExecute } from '@app/core/utils'; +import { parseArray, parseWithTranslation, safeExecute, parseTemplate } from '@app/core/utils'; import { initSchema, addToSchema, addGroupInfo } from '@app/core/schema-utils'; import { tripAnimationSchema } from '../lib/maps/schemes'; import { DomSanitizer } from '@angular/platform-browser'; import { WidgetContext } from '@app/modules/home/models/widget-component.models'; import { getRatio, findAngle } from '../lib/maps/maps-utils'; import { JsonSettingsSchema, WidgetConfig } from '@shared/models/widget.models'; +import moment from 'moment'; @Component({ @@ -55,6 +56,9 @@ export class TripAnimationComponent implements OnInit, AfterViewInit { mainTooltip = ''; visibleTooltip = false; activeTrip; + label; + minTime; + maxTime; static getSettingsSchema(): JsonSettingsSchema { const schema = initSchema(); @@ -90,48 +94,58 @@ export class TripAnimationComponent implements OnInit, AfterViewInit { ngAfterViewInit() { const ctxCopy: WidgetContext = _.cloneDeep(this.ctx); - ctxCopy.settings.showLabel = false; - ctxCopy.settings.showTooltip = false; this.mapWidget = new MapWidgetController(MapProviders.openstreet, false, ctxCopy, this.mapContainer.nativeElement); } timeUpdated(time: number) { const currentPosition = this.interpolatedData.map(dataSource => dataSource[time]); this.activeTrip = currentPosition[0]; - if (this.settings.showPolygon) { - this.mapWidget.map.updatePolygons(this.interpolatedData); + this.minTime = moment(this.historicalData[0][this.historicalData.length - 1]?.time).format('YYYY-MM-DD HH:mm:ss') + this.maxTime = moment(this.historicalData[0][0]?.time).format('YYYY-MM-DD HH:mm:ss') + this.calcLabel(); + this.calcTooltip(); + if (this.mapWidget) { + if (this.settings.showPolygon) { + this.mapWidget.map.updatePolygons(this.interpolatedData); + } + this.mapWidget.map.updateMarkers(currentPosition); } - this.mapWidget.map.updateMarkers(currentPosition); } setActiveTrip() { - } calculateIntervals() { this.historicalData.forEach((dataSource, index) => { this.intervals = []; + for (let time = dataSource[0]?.time; time < dataSource[dataSource.length - 1]?.time; time += this.normalizationStep) { this.intervals.push(time); } + this.intervals.push(dataSource[dataSource.length - 1]?.time); this.interpolatedData[index] = this.interpolateArray(dataSource, this.intervals); }); + } - showHideTooltip() { + calcTooltip() { + const data = { ...this.activeTrip, maxTime: this.maxTime, minTime: this.minTime } const tooltipText: string = this.settings.useTooltipFunction ? - safeExecute(this.settings.tooolTipFunction, [this.activeTrip, this.historicalData, 0]) - : this.settings.tooltipPattern; + safeExecute(this.settings.tooolTipFunction, [data, this.historicalData, 0]) : this.settings.tooltipPattern; + this.mainTooltip = this.sanitizer.sanitize( + SecurityContext.HTML, (parseWithTranslation.parseTemplate(tooltipText, data, true))); + } - this.mainTooltip = this.sanitizer.sanitize(SecurityContext.HTML, parseTemplate(tooltipText, this.activeTrip)) - this.visibleTooltip = !this.visibleTooltip; + calcLabel() { + const data = { ...this.activeTrip, maxTime: this.maxTime, minTime: this.minTime } + const labelText: string = this.settings.useLabelFunction ? + safeExecute(this.settings.labelFunction, [data, this.historicalData, 0]) : this.settings.label; + this.label = (parseWithTranslation.parseTemplate(labelText, data, true)); } interpolateArray(originData, interpolatedIntervals) { - const result = {}; - for (let i = 1, j = 0; i < originData.length && j < interpolatedIntervals.length;) { const currentTime = interpolatedIntervals[j]; while (originData[i].time < currentTime) i++; diff --git a/ui-ngx/src/app/shared/components/time/history-selector/history-selector.component.html b/ui-ngx/src/app/shared/components/time/history-selector/history-selector.component.html index 05d6b20c89..54bba32bae 100644 --- a/ui-ngx/src/app/shared/components/time/history-selector/history-selector.component.html +++ b/ui-ngx/src/app/shared/components/time/history-selector/history-selector.component.html @@ -16,15 +16,21 @@ -->
-
+
- - +
+ + +
+ {{ this.intervals[this.index] | date:'medium'}} + {{ "widget.no-data-found" | translate}} +
+
@@ -41,11 +47,8 @@ pause_circle_outline - - {{speedValue}} + + {{speedValue}} -
-
- {{ this.intervals[this.index] | date:'medium'}} - {{ "widget.no-data-found" | translate}} -
\ No newline at end of file +
\ No newline at end of file diff --git a/ui-ngx/src/app/shared/components/time/history-selector/history-selector.component.scss b/ui-ngx/src/app/shared/components/time/history-selector/history-selector.component.scss index d4e4ae17ed..0ca2a9e5b8 100644 --- a/ui-ngx/src/app/shared/components/time/history-selector/history-selector.component.scss +++ b/ui-ngx/src/app/shared/components/time/history-selector/history-selector.component.scss @@ -13,128 +13,120 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -.trip-animation-widget { + +.trip-animation-label-container { + height: 24px; +} + +.trip-animation-container { position: relative; + z-index: 1; + flex: 1; width: 100%; - height: 100%; - font-size: 16px; - line-height: 24px; - .trip-animation-label-container { - height: 24px; - } - - .trip-animation-container { - position: relative; + #trip-animation-map { z-index: 1; - flex: 1; width: 100%; + height: 100%; - #trip-animation-map { - z-index: 1; - width: 100%; - height: 100%; - - .pointsLayerMarkerIcon { - border-radius: 50%; - } + .pointsLayerMarkerIcon { + border-radius: 50%; } + } + + .trip-animation-info-panel { + position: absolute; + top: 0; + right: 0; + z-index: 2; + pointer-events: none; - .trip-animation-info-panel { - position: absolute; + .mat-button { top: 0; - right: 0; - z-index: 2; - pointer-events: none; - - .md-button { - top: 0; - left: 0; - width: 32px; - min-width: 32px; - height: 32px; - min-height: 32px; - padding: 0 0 2px; - margin: 2px; - line-height: 24px; - - ng-md-icon { - width: 24px; - height: 24px; - - svg { - width: inherit; - height: inherit; - } + left: 0; + width: 32px; + min-width: 32px; + height: 32px; + min-height: 32px; + padding: 0 0 2px; + margin: 2px; + line-height: 24px; + + ng-mat-icon { + width: 24px; + height: 24px; + + svg { + width: inherit; + height: inherit; } } } + } - .trip-animation-tooltip { - position: absolute; - top: 38px; - right: 0; - z-index: 2; - padding: 10px; - background-color: #fff; - transition: 0.3s ease-in-out; - - &-hidden { - transform: translateX(110%); - } + .trip-animation-tooltip { + position: absolute; + top: 38px; + right: 0; + z-index: 2; + padding: 10px; + background-color: #fff; + transition: 0.3s ease-in-out; + + &-hidden { + transform: translateX(110%); } } +} - .trip-animation-control-panel { - position: relative; - box-sizing: border-box; - width: 100%; - padding-bottom: 16px; - padding-left: 10px; +.trip-animation-control-panel { + position: relative; + box-sizing: border-box; + width: 100%; + padding-bottom: 16px; + padding-left: 10px; - md-slider-container { - md-slider { - min-width: 80px; - } + mat-slider-container { + mat-slider { + min-width: 80px; + } - button.md-button.md-icon-button { - width: 44px; - min-width: 44px; - height: 44px; - min-height: 44px; - margin: 0; - line-height: 28px; - - md-icon { - width: 28px; - height: 28px; - font-size: 28px; - - svg { - width: inherit; - height: inherit; - } + button.mat-button.mat-icon-button { + width: 44px; + min-width: 44px; + height: 44px; + min-height: 44px; + margin: 0; + line-height: 28px; + + mat-icon { + width: 28px; + height: 28px; + font-size: 28px; + + svg { + width: inherit; + height: inherit; } } - - md-select { - margin: 0; - } } - .panel-timer { - max-width: none; - padding-right: 250px; - padding-left: 90px; - margin-top: -20px; - font-size: 12px; - font-weight: 500; - text-align: center; + mat-select { + margin: 0; } } + + .panel-timer { + max-width: none; + margin-top: -20px; + font-size: 12px; + font-weight: 500; + text-align: center; + } } -.speed-select{ +.speed-select { width: 50px; - margin-left: 20px; + margin-left: 10px; + margin-top: 10px; } diff --git a/ui-ngx/src/app/shared/components/time/history-selector/history-selector.component.ts b/ui-ngx/src/app/shared/components/time/history-selector/history-selector.component.ts index f8659de2d5..252720033c 100644 --- a/ui-ngx/src/app/shared/components/time/history-selector/history-selector.component.ts +++ b/ui-ngx/src/app/shared/components/time/history-selector/history-selector.component.ts @@ -15,8 +15,8 @@ /// import { Component, OnInit, OnChanges, Input, Output, EventEmitter, ChangeDetectorRef } from '@angular/core'; -import { interval, Subscription, Subscriber, SubscriptionLike, Observer } from 'rxjs'; -import { filter, tap } from 'rxjs/operators'; +import { interval } from 'rxjs'; +import { filter } from 'rxjs/operators'; import { HistorySelectSettings } from '@app/modules/home/components/widget/lib/maps/map-models'; @Component({ diff --git a/ui-ngx/src/app/shared/pipe/template.pipe.ts b/ui-ngx/src/app/shared/pipe/template.pipe.ts index 4970672325..2cce658adb 100644 --- a/ui-ngx/src/app/shared/pipe/template.pipe.ts +++ b/ui-ngx/src/app/shared/pipe/template.pipe.ts @@ -15,11 +15,11 @@ /// import { Pipe, PipeTransform } from '@angular/core'; -import { parseTemplate } from '@app/core/utils'; +import { parseTemplate, parseWithTranslation } from '@app/core/utils'; @Pipe({ name: 'tbParseTemplate' }) export class TbTemplatePipe implements PipeTransform { transform(template, data): string { - return parseTemplate(template, data); + return parseWithTranslation.parseTemplate(template, data); } }