UI: New maps V3

This commit is contained in:
Igor Kulikov 2024-12-27 15:26:19 +02:00
parent 36f990cf15
commit 7f4a0ef48f
56 changed files with 866 additions and 46 deletions

View File

@ -101,7 +101,8 @@
"node_modules/tooltipster/dist/css/plugins/tooltipster/sideTip/themes/tooltipster-sideTip-shadow.min.css",
"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/map.scss",
"src/app/modules/home/components/widget/lib/maps-legacy/markers.scss",
"src/app/modules/home/components/widget/lib/home-page/home-page.scss",
"node_modules/leaflet.markercluster/dist/MarkerCluster.css",
"node_modules/leaflet.markercluster/dist/MarkerCluster.Default.css",

View File

@ -15,10 +15,10 @@
///
import L, { LeafletMouseEvent } from 'leaflet';
import { CircleData, WidgetCircleSettings } from '@home/components/widget/lib/maps/map-models';
import { functionValueCalculator, parseWithTranslation } from '@home/components/widget/lib/maps/common-maps-utils';
import LeafletMap from '@home/components/widget/lib/maps/leaflet-map';
import { createTooltip } from '@home/components/widget/lib/maps/maps-utils';
import { CircleData, WidgetCircleSettings } from '@home/components/widget/lib/maps-legacy/map-models';
import { functionValueCalculator, parseWithTranslation } from '@home/components/widget/lib/maps-legacy/common-maps-utils';
import LeafletMap from '@home/components/widget/lib/maps-legacy/leaflet-map';
import { createTooltip } from '@home/components/widget/lib/maps-legacy/maps-utils';
import { FormattedData } from '@shared/models/widget.models';
import { fillDataPattern, processDataPattern, safeExecuteTbFunction } from '@core/utils';

View File

@ -43,8 +43,8 @@ import {
isJSON,
isValidLatLng,
LabelSettings
} from '@home/components/widget/lib/maps/maps-utils';
import { checkLngLat, createLoadingDiv } from '@home/components/widget/lib/maps/common-maps-utils';
} from '@home/components/widget/lib/maps-legacy/maps-utils';
import { checkLngLat, createLoadingDiv } from '@home/components/widget/lib/maps-legacy/common-maps-utils';
import { WidgetContext } from '@home/models/widget-component.models';
import {
deepClone,
@ -60,7 +60,7 @@ import { TranslateService } from '@ngx-translate/core';
import {
SelectEntityDialogComponent,
SelectEntityDialogData
} from '@home/components/widget/lib/maps/dialogs/select-entity-dialog.component';
} from '@home/components/widget/lib/maps-legacy/dialogs/select-entity-dialog.component';
import { MatDialog } from '@angular/material/dialog';
import { FormattedData, ReplaceInfo } from '@shared/models/widget.models';
import { ImagePipe } from '@shared/pipe/image.pipe';

View File

@ -14,7 +14,7 @@
/// limitations under the License.
///
import LeafletMap from '@home/components/widget/lib/maps/leaflet-map';
import LeafletMap from '@home/components/widget/lib/maps-legacy/leaflet-map';
export interface MapWidgetInterface {
map?: LeafletMap;

View File

@ -23,7 +23,7 @@ import { Datasource, DatasourceData, FormattedData, WidgetActionDescriptor } fro
import { TranslateService } from '@ngx-translate/core';
import { UtilsService } from '@core/services/utils.service';
import { EntityDataPageLink } from '@shared/models/query/query.models';
import { providerClass } from '@home/components/widget/lib/maps/providers/public-api';
import { providerClass } from '@home/components/widget/lib/maps-legacy/providers/public-api';
import { isDefined, isDefinedAndNotNull, parseTbFunction } from '@core/utils';
import L from 'leaflet';
import { firstValueFrom, forkJoin, from, Observable, of } from 'rxjs';

View File

@ -18,7 +18,7 @@ import L from 'leaflet';
import { GenericFunction, ShowTooltipAction, WidgetToolipSettings } from './map-models';
import { Datasource, FormattedData } from '@app/shared/models/widget.models';
import { fillDataPattern, isDefinedAndNotNull, isString, processDataPattern, safeExecuteTbFunction } from '@core/utils';
import { parseWithTranslation } from '@home/components/widget/lib/maps/common-maps-utils';
import { parseWithTranslation } from '@home/components/widget/lib/maps-legacy/common-maps-utils';
import { CompiledTbFunction } from '@shared/models/js-function.models';
export function createTooltip(target: L.Layer,

View File

@ -20,7 +20,7 @@ import { functionValueCalculator, parseWithTranslation } from './common-maps-uti
import { WidgetPolygonSettings } from './map-models';
import { FormattedData } from '@shared/models/widget.models';
import { fillDataPattern, processDataPattern, safeExecuteTbFunction } from '@core/utils';
import LeafletMap from '@home/components/widget/lib/maps/leaflet-map';
import LeafletMap from '@home/components/widget/lib/maps-legacy/leaflet-map';
export class Polygon {

View File

@ -19,7 +19,7 @@ import L, { PolylineDecorator, PolylineDecoratorOptions, Symbol } from 'leaflet'
import 'leaflet-polylinedecorator';
import { WidgetPolylineSettings } from './map-models';
import { functionValueCalculator } from '@home/components/widget/lib/maps/common-maps-utils';
import { functionValueCalculator } from '@home/components/widget/lib/maps-legacy/common-maps-utils';
import { FormattedData } from '@shared/models/widget.models';
export class Polyline {

View File

@ -25,7 +25,7 @@ import {
} from '../map-models';
import { combineLatest, Observable, of, ReplaySubject, switchMap } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { calculateNewPointCoordinate, loadImageWithAspect } from '@home/components/widget/lib/maps/common-maps-utils';
import { calculateNewPointCoordinate, loadImageWithAspect } from '@home/components/widget/lib/maps-legacy/common-maps-utils';
import { WidgetContext } from '@home/models/widget-component.models';
import { DataSet, DatasourceType, FormattedData, widgetType } from '@shared/models/widget.models';
import { DataKeyType } from '@shared/models/telemetry/telemetry.models';

View File

@ -20,7 +20,7 @@ import { GoogleMap } from './google-map';
import { HEREMap } from './here-map';
import { ImageMap } from './image-map';
import { Type } from '@angular/core';
import LeafletMap from '@home/components/widget/lib/maps/leaflet-map';
import LeafletMap from '@home/components/widget/lib/maps-legacy/leaflet-map';
export const providerClass: { [key: string]: Type<LeafletMap> } = {
'openstreet-map': OpenStreetMap,

View File

@ -0,0 +1,90 @@
import {
defaultOpenStreetMapLayerSettings,
MapLayerSettings,
MapProvider, OpenStreetLayerType,
OpenStreetMapLayerSettings, openStreetMapLayerTranslationMap
} from '@home/components/widget/lib/maps/map.models';
import { WidgetContext } from '@home/models/widget-component.models';
import { DeepPartial } from '@shared/models/common';
import { mergeDeep } from '@core/utils';
import { Observable, of } from 'rxjs';
import { CustomTranslatePipe } from '@shared/pipe/custom-translate.pipe';
import L from 'leaflet';
import { map } from 'rxjs/operators';
export abstract class TbMapLayer<S extends MapLayerSettings> {
static fromSettings(ctx: WidgetContext,
inputSettings: DeepPartial<MapLayerSettings>) {
switch (inputSettings.provider) {
case MapProvider.google:
break;
case MapProvider.openstreet:
return new TbOpenStreetMapLayer(ctx, inputSettings);
case MapProvider.tencent:
break;
case MapProvider.here:
break;
case MapProvider.custom:
break;
}
}
protected settings: S;
protected constructor(protected ctx: WidgetContext,
protected inputSettings: DeepPartial<MapLayerSettings>) {
this.settings = mergeDeep({} as S, this.defaultSettings(), this.inputSettings as S);
}
protected abstract defaultSettings(): S;
protected title(): string {
const customTranslate = this.ctx.$injector.get(CustomTranslatePipe);
if (this.settings.label) {
return customTranslate.transform(this.settings.label);
} else {
return this.generateTitle();
}
}
protected abstract generateTitle(): string;
protected abstract createLayer(): Observable<L.Layer>;
public loadLayer(): Observable<{title: string, layer: L.Layer}> {
return this.createLayer().pipe(
map((layer) => {
return {
title: this.title(),
layer
};
})
);
}
}
class TbOpenStreetMapLayer extends TbMapLayer<OpenStreetMapLayerSettings> {
constructor(protected ctx: WidgetContext,
protected inputSettings: DeepPartial<MapLayerSettings>) {
super(ctx, inputSettings);
}
protected defaultSettings(): OpenStreetMapLayerSettings {
return defaultOpenStreetMapLayerSettings;
}
protected generateTitle(): string {
const layerType = OpenStreetLayerType[this.settings.layerType];
return this.ctx.translate.instant(openStreetMapLayerTranslationMap.get(layerType));
}
protected createLayer(): Observable<L.Layer> {
const layer = L.tileLayer.provider(OpenStreetLayerType[this.settings.layerType]);
return of(layer);
}
}

View File

@ -0,0 +1,25 @@
<!--
Copyright © 2016-2024 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<div class="tb-map-panel" [style.padding]="padding" [style]="backgroundStyle$ | async">
<div class="tb-map-overlay" [style]="overlayStyle"></div>
<ng-container *ngIf="widgetComponent.dashboardWidget.showWidgetTitlePanel">
<ng-container *ngTemplateOutlet="widgetTitlePanel"></ng-container>
</ng-container>
<div #mapElement>
</div>
</div>

View File

@ -0,0 +1,38 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
.tb-map-panel {
width: 100%;
height: 100%;
position: relative;
display: flex;
flex-direction: column;
gap: 8px;
padding: 20px 24px 24px 24px;
> div:not(.tb-map-overlay) {
z-index: 1;
}
.tb-map-overlay {
position: absolute;
top: 12px;
left: 12px;
bottom: 12px;
right: 12px;
}
div.tb-widget-title {
padding: 0;
}
}

View File

@ -0,0 +1,97 @@
///
/// Copyright © 2016-2024 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import {
AfterViewInit,
ChangeDetectorRef,
Component,
ElementRef,
Input,
OnDestroy,
OnInit,
Renderer2,
TemplateRef,
ViewChild,
ViewEncapsulation
} from '@angular/core';
import { mapWidgetDefaultSettings, MapWidgetSettings } from '@home/components/widget/lib/maps/map-widget.models';
import { WidgetContext } from '@home/models/widget-component.models';
import { Observable } from 'rxjs';
import { backgroundStyle, ComponentStyle, overlayStyle } from '@shared/models/widget-settings.models';
import { TbMap } from '@home/components/widget/lib/maps/map';
import { MapSetting } from '@home/components/widget/lib/maps/map.models';
import { WidgetComponent } from '@home/components/widget/widget.component';
import { ImagePipe } from '@shared/pipe/image.pipe';
import { DomSanitizer } from '@angular/platform-browser';
@Component({
selector: 'tb-map-widget',
templateUrl: './map-widget.component.html',
styleUrls: ['./map-widget.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class MapWidgetComponent implements OnInit, OnDestroy, AfterViewInit {
@ViewChild('mapElement', {static: false})
mapElement: ElementRef<HTMLElement>;
settings: MapWidgetSettings;
@Input()
ctx: WidgetContext;
@Input()
widgetTitlePanel: TemplateRef<any>;
backgroundStyle$: Observable<ComponentStyle>;
overlayStyle: ComponentStyle = {};
padding: string;
private map: TbMap<MapSetting>;
constructor(public widgetComponent: WidgetComponent,
private imagePipe: ImagePipe,
private sanitizer: DomSanitizer,
private renderer: Renderer2,
private cd: ChangeDetectorRef) {
}
ngOnInit(): void {
this.ctx.$scope.mapWidget = this;
this.settings = {...mapWidgetDefaultSettings, ...this.ctx.settings};
this.backgroundStyle$ = backgroundStyle(this.settings.background, this.imagePipe, this.sanitizer);
this.overlayStyle = overlayStyle(this.settings.background.overlay);
this.padding = this.settings.background.overlay.enabled ? undefined : this.settings.padding;
}
ngAfterViewInit() {
this.map = TbMap.fromSettings(this.ctx, this.settings, this.mapElement.nativeElement);
}
ngOnDestroy() {
if (this.map) {
this.map.destroy();
}
}
public onInit() {
const borderRadius = this.ctx.$widgetElement.css('borderRadius');
this.overlayStyle = {...this.overlayStyle, ...{borderRadius}};
this.cd.detectChanges();
}
}

View File

@ -0,0 +1,38 @@
///
/// Copyright © 2016-2024 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import { defaultMapSettings, MapSetting } from '@home/components/widget/lib/maps/map.models';
import { BackgroundSettings, BackgroundType } from '@shared/models/widget-settings.models';
import { mergeDeep } from '@core/utils';
export interface MapWidgetSettings extends MapSetting {
background: BackgroundSettings;
padding: string;
}
export const mapWidgetDefaultSettings: MapWidgetSettings =
mergeDeep({} as MapWidgetSettings, defaultMapSettings as MapWidgetSettings, {
background: {
type: BackgroundType.color,
color: '#fff',
overlay: {
enabled: false,
color: 'rgba(255,255,255,0.72)',
blur: 3
}
},
padding: '12px'
} as MapWidgetSettings);

View File

@ -0,0 +1,310 @@
///
/// Copyright © 2016-2024 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import { DataKey, DatasourceType } from '@shared/models/widget.models';
import { EntityType } from '@shared/models/entity-type.models';
import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
import { mergeDeep } from '@core/utils';
export enum MapType {
geoMap = 'geoMap',
image = 'image'
}
export interface MapDataSourceSettings {
dsType: DatasourceType;
dsEntityType?: EntityType;
dsEntityId?: string;
dsEntityAliasId?: string;
dsFilterId?: string;
}
export interface MapDataLayerSettings extends MapDataSourceSettings {
additionalDataKeys?: DataKey[];
group?: string;
}
export interface MarkersDataLayerSettings extends MapDataLayerSettings {
xKey: DataKey;
yKey: DataKey;
}
export const defaultMarkersDataLayerSettings = (mapType: MapType): MarkersDataLayerSettings => ({
dsType: DatasourceType.entity,
xKey: {
name: MapType.geoMap === mapType ? 'latitude' : 'xPos',
label: MapType.geoMap === mapType ? 'latitude' : 'xPos',
type: DataKeyType.attribute
},
yKey: {
name: MapType.geoMap === mapType ? 'longitude' : 'yPos',
label: MapType.geoMap === mapType ? 'longitude' : 'yPos',
type: DataKeyType.attribute
}
});
export interface PolygonsDataLayerSettings extends MapDataLayerSettings {
polygonKey: DataKey;
}
export const defaultPolygonsDataLayerSettings: PolygonsDataLayerSettings = {
dsType: DatasourceType.entity,
polygonKey: {
name: 'perimeter',
label: 'perimeter',
type: DataKeyType.attribute
}
};
export interface CirclesDataLayerSettings extends MapDataLayerSettings {
circleKey: DataKey;
}
export const defaultCirclesDataLayerSettings: CirclesDataLayerSettings = {
dsType: DatasourceType.entity,
circleKey: {
name: 'perimeter',
label: 'perimeter',
type: DataKeyType.attribute
}
};
export interface AdditionalMapDataSourceSettings extends MapDataSourceSettings {
dataKeys: DataKey[];
}
export enum MapControlsPosition {
topleft = 'topleft',
topright = 'topright',
bottomleft = 'bottomleft',
bottomright = 'bottomright'
}
export enum MapZoomAction {
scroll = 'scroll',
doubleClick = 'doubleClick',
controlButtons = 'controlButtons'
}
export interface BaseMapSettings {
mapType: MapType;
markers: MarkersDataLayerSettings[];
polygons: PolygonsDataLayerSettings[];
circles: CirclesDataLayerSettings[];
additionalDataSources: AdditionalMapDataSourceSettings[];
controlsPosition: MapControlsPosition;
zoomActions: MapZoomAction[];
fitMapBounds: boolean;
useDefaultCenterPosition: boolean;
defaultCenterPosition?: string;
defaultZoomLevel: number;
mapPageSize: number;
}
export const DEFAULT_MAP_PAGE_SIZE = 16384;
export const DEFAULT_ZOOM_LEVEL = 8;
export const defaultBaseMapSettings: BaseMapSettings = {
mapType: MapType.geoMap,
markers: [],
polygons: [],
circles: [],
additionalDataSources: [],
controlsPosition: MapControlsPosition.topleft,
zoomActions: [MapZoomAction.scroll, MapZoomAction.doubleClick, MapZoomAction.controlButtons],
fitMapBounds: true,
useDefaultCenterPosition: false,
defaultCenterPosition: '0,0',
defaultZoomLevel: null,
mapPageSize: DEFAULT_MAP_PAGE_SIZE
};
export enum MapProvider {
google = 'google-map',
openstreet = 'openstreet-map',
here = 'here',
tencent = 'tencent-map',
custom = 'custom'
}
export interface MapLayerSettings {
label?: string;
provider: MapProvider;
}
export enum GoogleLayerType {
roadmap = 'roadmap',
satellite = 'satellite',
hybrid = 'hybrid',
terrain = 'terrain'
}
export interface GoogleMapLayerSettings extends MapLayerSettings {
provider: MapProvider.google;
layerType: GoogleLayerType;
apiKey: string;
}
export const defaultGoogleMapLayerSettings: GoogleMapLayerSettings = {
provider: MapProvider.google,
layerType: GoogleLayerType.roadmap,
apiKey: 'AIzaSyDoEx2kaGz3PxwbI9T7ccTSg5xjdw8Nw8Q'
};
export enum OpenStreetLayerType {
openStreetMapnik = 'OpenStreetMap.Mapnik',
openStreetHot = 'OpenStreetMap.HOT',
esriWorldStreetMap = 'Esri.WorldStreetMap',
esriWorldTopoMap = 'Esri.WorldTopoMap',
esriWorldImagery = 'Esri.WorldImagery',
cartoDbPositron = 'CartoDB.Positron',
cartoDbDarkMatter = 'CartoDB.DarkMatter'
}
export const openStreetMapLayerTranslationMap = new Map<OpenStreetLayerType, string>(
[
[OpenStreetLayerType.openStreetMapnik, 'widgets.maps.openstreet-provider-mapnik'],
[OpenStreetLayerType.openStreetHot, 'widgets.maps.openstreet-provider-hot'],
[OpenStreetLayerType.esriWorldStreetMap, 'widgets.maps.openstreet-provider-esri-street'],
[OpenStreetLayerType.esriWorldTopoMap, 'widgets.maps.openstreet-provider-esri-topo'],
[OpenStreetLayerType.esriWorldImagery, 'widgets.maps.openstreet-provider-esri-imagery'],
[OpenStreetLayerType.cartoDbPositron, 'widgets.maps.openstreet-provider-cartodb-positron'],
[OpenStreetLayerType.cartoDbDarkMatter, 'widgets.maps.openstreet-provider-cartodb-dark-matter']
]
);
export interface OpenStreetMapLayerSettings extends MapLayerSettings {
provider: MapProvider.openstreet;
layerType: OpenStreetLayerType;
}
export const defaultOpenStreetMapLayerSettings: OpenStreetMapLayerSettings = {
provider: MapProvider.openstreet,
layerType: OpenStreetLayerType.openStreetMapnik
}
export enum HereLayerType {
hereNormalDay = 'HERE.normalDay',
hereNormalNight = 'HERE.normalNight',
hereHybridDay = 'HERE.hybridDay',
hereTerrainDay = 'HERE.terrainDay'
}
export interface HereMapLayerSettings extends MapLayerSettings {
provider: MapProvider.here;
layerType: HereLayerType;
apiKey: string;
}
export const defaultHereMapLayerSettings: HereMapLayerSettings = {
provider: MapProvider.here,
layerType: HereLayerType.hereNormalDay,
apiKey: 'kVXykxAfZ6LS4EbCTO02soFVfjA7HoBzNVVH9u7nzoE'
}
export enum TencentLayerType {
roadmap = 'roadmap',
satellite = 'satellite',
hybrid = 'hybrid'
}
export interface TencentMapLayerSettings extends MapLayerSettings {
provider: MapProvider.tencent;
layerType: TencentLayerType
apiKey: string;
}
export const defaultTencentMapLayerSettings: TencentMapLayerSettings = {
provider: MapProvider.tencent,
layerType: TencentLayerType.roadmap,
apiKey: '84d6d83e0e51e481e50454ccbe8986b'
}
export interface CustomMapLayerSettings extends MapLayerSettings {
provider: MapProvider.custom;
tileUrl: string;
}
export const defaultCustomMapLayerSettings: CustomMapLayerSettings = {
provider: MapProvider.custom,
tileUrl: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'
}
export const defaultMapLayerSettings = (provider: MapProvider): MapLayerSettings => {
switch (provider) {
case MapProvider.google:
return defaultGoogleMapLayerSettings;
case MapProvider.openstreet:
return defaultOpenStreetMapLayerSettings;
case MapProvider.here:
return defaultHereMapLayerSettings;
case MapProvider.tencent:
return defaultTencentMapLayerSettings;
case MapProvider.custom:
return defaultCustomMapLayerSettings;
}
};
export const defaultMapLayers: MapLayerSettings[] = (Object.keys(OpenStreetLayerType) as OpenStreetLayerType[]).map(type => ({
provider: MapProvider.openstreet,
layerType: type
}));
export interface GeoMapSettings extends BaseMapSettings {
layers?: MapLayerSettings[];
}
export const defaultGeoMapSettings: GeoMapSettings = {
mapType: MapType.geoMap,
layers: mergeDeep([], defaultMapLayers),
...mergeDeep({} as BaseMapSettings, defaultBaseMapSettings)
};
export enum ImageSourceType {
image = 'image',
attribute = 'attribute'
}
export interface ImageMapSettings extends BaseMapSettings {
imageSourceType?: ImageSourceType;
imageUrl?: string;
imageEntityAlias?: string;
imageUrlAttribute?: string;
}
export const defaultImageMapSettings: ImageMapSettings = {
mapType: MapType.image,
imageSourceType: ImageSourceType.image,
imageUrl: 'data:image/svg+xml;base64,PHN2ZyBpZD0ic3ZnMiIgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMTAwIiB3aWR0aD0iMTAwIiB2ZXJzaW9uPSIxLjEiIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgdmlld0JveD0iMCAwIDEwMCAxMDAiPgogPGcgaWQ9ImxheWVyMSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCAtOTUyLjM2KSI+CiAgPHJlY3QgaWQ9InJlY3Q0Njg0IiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBoZWlnaHQ9Ijk5LjAxIiB3aWR0aD0iOTkuMDEiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiB5PSI5NTIuODYiIHg9Ii40OTUwNSIgc3Ryb2tlLXdpZHRoPSIuOTkwMTAiIGZpbGw9IiNlZWUiLz4KICA8dGV4dCBpZD0idGV4dDQ2ODYiIHN0eWxlPSJ3b3JkLXNwYWNpbmc6MHB4O2xldHRlci1zcGFjaW5nOjBweDt0ZXh0LWFuY2hvcjptaWRkbGU7dGV4dC1hbGlnbjpjZW50ZXIiIGZvbnQtd2VpZ2h0PSJib2xkIiB4bWw6c3BhY2U9InByZXNlcnZlIiBmb250LXNpemU9IjEwcHgiIGxpbmUtaGVpZ2h0PSIxMjUlIiB5PSI5NzAuNzI4MDkiIHg9IjQ5LjM5NjQ3NyIgZm9udC1mYW1pbHk9IlJvYm90byIgZmlsbD0iIzY2NjY2NiI+PHRzcGFuIGlkPSJ0c3BhbjQ2OTAiIHg9IjUwLjY0NjQ3NyIgeT0iOTcwLjcyODA5Ij5JbWFnZSBiYWNrZ3JvdW5kIDwvdHNwYW4+PHRzcGFuIGlkPSJ0c3BhbjQ2OTIiIHg9IjQ5LjM5NjQ3NyIgeT0iOTgzLjIyODA5Ij5pcyBub3QgY29uZmlndXJlZDwvdHNwYW4+PC90ZXh0PgogIDxyZWN0IGlkPSJyZWN0NDY5NCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgaGVpZ2h0PSIxOS4zNiIgd2lkdGg9IjY5LjM2IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgeT0iOTkyLjY4IiB4PSIxNS4zMiIgc3Ryb2tlLXdpZHRoPSIuNjM5ODYiIGZpbGw9Im5vbmUiLz4KIDwvZz4KPC9zdmc+Cg==',
...mergeDeep({} as BaseMapSettings, defaultBaseMapSettings)
}
export type MapSetting = GeoMapSettings & ImageMapSettings;
export const defaultMapSettings: MapSetting = defaultGeoMapSettings;
export function parseCenterPosition(position: string | [number, number]): [number, number] {
if (typeof (position) === 'string') {
const parts = position.split(',');
if (parts.length === 2) {
return [Number(parts[0]), Number(parts[1])];
}
}
if (typeof (position) === 'object') {
return position;
}
return [0, 0];
}

View File

@ -0,0 +1,19 @@
.tb-map-layout {
display: flex;
width: 100%;
height: 100%;
min-width: 0;
min-height: 0;
flex: 1;
&.tb-sidebar-left {
flex-direction: row-reverse;
}
&.tb-sidebar-right {
flex-direction: row;
}
.tb-map {
flex: 1;
}
.tb-map-sidebar {
}
}

View File

@ -0,0 +1,196 @@
///
/// Copyright © 2016-2024 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import {
BaseMapSettings,
DEFAULT_ZOOM_LEVEL,
defaultGeoMapSettings,
defaultImageMapSettings,
GeoMapSettings,
ImageMapSettings,
MapSetting,
MapType,
MapZoomAction,
parseCenterPosition
} from '@home/components/widget/lib/maps/map.models';
import { WidgetContext } from '@home/models/widget-component.models';
import { mergeDeep } from '@core/utils';
import { DeepPartial } from '@shared/models/common';
import L from 'leaflet';
import { forkJoin, Observable, of } from 'rxjs';
import { TbMapLayer } from '@home/components/widget/lib/maps/map-layer';
import { map } from 'rxjs/operators';
export abstract class TbMap<S extends BaseMapSettings> {
static fromSettings(ctx: WidgetContext,
inputSettings: DeepPartial<MapSetting>,
mapElement: HTMLElement): TbMap<MapSetting> {
switch (inputSettings.mapType) {
case MapType.geoMap:
return new TbGeoMap(ctx, inputSettings, mapElement);
case MapType.image:
return new TbImageMap(ctx, inputSettings, mapElement);
}
}
protected settings: S;
protected map: L.Map;
protected defaultCenterPosition: [number, number];
protected bounds: L.LatLngBounds;
protected layerControl: L.Control.Layers;
protected mapElement: HTMLElement;
protected sidebarElement: HTMLElement;
protected constructor(protected ctx: WidgetContext,
protected inputSettings: DeepPartial<S>,
protected containerElement: HTMLElement) {
this.settings = mergeDeep({} as S, this.defaultSettings(), this.inputSettings as S);
$(containerElement).empty();
$(containerElement).addClass('tb-map-layout');
if (this.settings.controlsPosition.endsWith('left')) {
$(containerElement).addClass('tb-sidebar-left');
} else {
$(containerElement).addClass('tb-sidebar-right');
}
const mapElement = $('<div class="tb-map"></div>');
const sidebarElement = $('<div class="tb-map-sidebar"></div>');
$(containerElement).append(mapElement);
$(containerElement).append(sidebarElement);
this.mapElement = mapElement[0];
this.sidebarElement = sidebarElement[0];
this.defaultCenterPosition = parseCenterPosition(this.settings.defaultCenterPosition);
this.layerControl = L.control.layers({}, {}, {position: this.settings.controlsPosition, collapsed: true});
this.createMap().subscribe((map) => {
this.map = map;
this.initMap();
});
L.TB = {
sidebar: (s) => { return null;}
};
}
private initMap() {
this.map.zoomControl.setPosition(this.settings.controlsPosition);
this.layerControl.addTo(this.map);
this.map.on('move', () => {
this.ctx.updatePopoverPositions();
});
this.map.on('zoomstart', () => {
this.ctx.setPopoversHidden(true);
});
this.map.on('zoomend', () => {
this.ctx.setPopoversHidden(false);
this.ctx.updatePopoverPositions();
setTimeout(() => {
this.ctx.updatePopoverPositions();
});
});
if (this.settings.useDefaultCenterPosition) {
this.map.panTo(this.defaultCenterPosition);
this.bounds = this.map.getBounds();
} else {
this.bounds = new L.LatLngBounds(null, null);
}
}
protected abstract defaultSettings(): S;
protected abstract createMap(): Observable<L.Map>;
public destroy() {
if (this.map) {
this.map.remove();
}
}
}
class TbGeoMap extends TbMap<GeoMapSettings> {
constructor(protected ctx: WidgetContext,
protected inputSettings: DeepPartial<GeoMapSettings>,
protected containerElement: HTMLElement) {
super(ctx, inputSettings, containerElement);
}
protected defaultSettings(): GeoMapSettings {
return defaultGeoMapSettings;
}
protected createMap(): Observable<L.Map> {
const theMap = L.map(this.mapElement, {
scrollWheelZoom: this.settings.zoomActions.includes(MapZoomAction.scroll),
doubleClickZoom: this.settings.zoomActions.includes(MapZoomAction.doubleClick),
zoomControl: this.settings.zoomActions.includes(MapZoomAction.controlButtons)
}).setView(this.defaultCenterPosition, this.settings.defaultZoomLevel || DEFAULT_ZOOM_LEVEL);
return this.loadLayers().pipe(
map((layers) => {
if (layers.length) {
const layer = layers[0];
layer.layer.addTo(theMap);
if (layers.length > 1) {
layers.forEach(l => {
this.layerControl.addBaseLayer(l.layer, l.title);
});
}
}
return theMap;
})
);
}
private loadLayers(): Observable<{title: string, layer: L.Layer}[]> {
const layers = this.settings.layers.map(settings => TbMapLayer.fromSettings(this.ctx, settings));
return forkJoin(layers.map(layer => layer.loadLayer()));
}
}
class TbImageMap extends TbMap<ImageMapSettings> {
private maxZoom = 4;
constructor(protected ctx: WidgetContext,
protected inputSettings: DeepPartial<ImageMapSettings>,
protected mapElement: HTMLElement) {
super(ctx, inputSettings, mapElement);
}
protected defaultSettings(): ImageMapSettings {
return defaultImageMapSettings;
}
protected createMap(): Observable<L.Map> {
const map = L.map(this.mapElement, {
scrollWheelZoom: this.settings.zoomActions.includes(MapZoomAction.scroll),
doubleClickZoom: this.settings.zoomActions.includes(MapZoomAction.doubleClick),
zoomControl: this.settings.zoomActions.includes(MapZoomAction.controlButtons),
minZoom: 1,
maxZoom: this.maxZoom,
zoom: 1,
crs: L.CRS.Simple,
attributionControl: false
}).setView(this.defaultCenterPosition, this.settings.defaultZoomLevel || DEFAULT_ZOOM_LEVEL);
return of(map);
}
}

View File

@ -33,7 +33,7 @@ import {
CircleSettings,
ShowTooltipAction,
showTooltipActionTranslationMap
} from '@home/components/widget/lib/maps/map-models';
} from '@home/components/widget/lib/maps-legacy/map-models';
import { WidgetService } from '@core/http/widget.service';
import { Widget } from '@shared/models/widget.models';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';

View File

@ -29,7 +29,7 @@ import { PageComponent } from '@shared/components/page.component';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { TranslateService } from '@ngx-translate/core';
import { CommonMapSettings, MapProviders } from '@home/components/widget/lib/maps/map-models';
import { CommonMapSettings, MapProviders } from '@home/components/widget/lib/maps-legacy/map-models';
import { Widget } from '@shared/models/widget.models';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';

View File

@ -33,7 +33,7 @@ import {
GoogleMapProviderSettings,
GoogleMapType,
googleMapTypeProviderTranslationMap
} from '@home/components/widget/lib/maps/map-models';
} from '@home/components/widget/lib/maps-legacy/map-models';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({

View File

@ -33,7 +33,7 @@ import {
HereMapProvider,
HereMapProviderSettings,
hereMapProviderTranslationMap
} from '@home/components/widget/lib/maps/map-models';
} from '@home/components/widget/lib/maps-legacy/map-models';
import { isDefinedAndNotNull } from '@core/utils';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';

View File

@ -28,7 +28,7 @@ import { PageComponent } from '@shared/components/page.component';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { TranslateService } from '@ngx-translate/core';
import { ImageMapProviderSettings } from '@home/components/widget/lib/maps/map-models';
import { ImageMapProviderSettings } from '@home/components/widget/lib/maps-legacy/map-models';
import { IAliasController } from '@core/api/widget-api.models';
import { Observable, of } from 'rxjs';
import { catchError, map, mergeMap, publishReplay, refCount, startWith, tap } from 'rxjs/operators';

View File

@ -28,7 +28,7 @@ import { PageComponent } from '@shared/components/page.component';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { TranslateService } from '@ngx-translate/core';
import { MapEditorSettings } from '@home/components/widget/lib/maps/map-models';
import { MapEditorSettings } from '@home/components/widget/lib/maps-legacy/map-models';
import { WidgetService } from '@core/http/widget.service';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';

View File

@ -41,7 +41,7 @@ import {
mapProviderTranslationMap,
OpenStreetMapProviderSettings,
TencentMapProviderSettings
} from '@home/components/widget/lib/maps/map-models';
} from '@home/components/widget/lib/maps-legacy/map-models';
import { extractType } from '@core/utils';
import { IAliasController } from '@core/api/widget-api.models';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';

View File

@ -46,7 +46,7 @@ import {
PolygonSettings,
PolylineSettings,
UnitedMapSettings
} from '@home/components/widget/lib/maps/map-models';
} from '@home/components/widget/lib/maps-legacy/map-models';
import { extractType } from '@core/utils';
import { IAliasController } from '@core/api/widget-api.models';
import { Widget } from '@shared/models/widget.models';

View File

@ -19,7 +19,7 @@ import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.m
import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { defaultMapSettings } from 'src/app/modules/home/components/widget/lib/maps/map-models';
import { defaultMapSettings } from 'src/app/modules/home/components/widget/lib/maps-legacy/map-models';
@Component({
selector: 'tb-map-widget-settings',

View File

@ -29,7 +29,7 @@ import { PageComponent } from '@shared/components/page.component';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { TranslateService } from '@ngx-translate/core';
import { MarkerClusteringSettings } from '@home/components/widget/lib/maps/map-models';
import { MarkerClusteringSettings } from '@home/components/widget/lib/maps-legacy/map-models';
import { WidgetService } from '@core/http/widget.service';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';

View File

@ -31,7 +31,7 @@ import { TranslateService } from '@ngx-translate/core';
import {
MapProviders,
MarkersSettings, ShowTooltipAction, showTooltipActionTranslationMap
} from '@home/components/widget/lib/maps/map-models';
} from '@home/components/widget/lib/maps-legacy/map-models';
import { WidgetService } from '@core/http/widget.service';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';

View File

@ -33,7 +33,7 @@ import {
OpenStreetMapProvider,
OpenStreetMapProviderSettings,
openStreetMapProviderTranslationMap
} from '@home/components/widget/lib/maps/map-models';
} from '@home/components/widget/lib/maps-legacy/map-models';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({

View File

@ -33,7 +33,7 @@ import {
PolygonSettings,
ShowTooltipAction,
showTooltipActionTranslationMap
} from '@home/components/widget/lib/maps/map-models';
} from '@home/components/widget/lib/maps-legacy/map-models';
import { WidgetService } from '@core/http/widget.service';
import { Widget } from '@shared/models/widget.models';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';

View File

@ -28,7 +28,7 @@ import { PageComponent } from '@shared/components/page.component';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { TranslateService } from '@ngx-translate/core';
import { PolylineSettings } from '@home/components/widget/lib/maps/map-models';
import { PolylineSettings } from '@home/components/widget/lib/maps-legacy/map-models';
import { WidgetService } from '@core/http/widget.service';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';

View File

@ -19,7 +19,7 @@ import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.m
import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { defaultMapSettings } from 'src/app/modules/home/components/widget/lib/maps/map-models';
import { defaultMapSettings } from 'src/app/modules/home/components/widget/lib/maps-legacy/map-models';
@Component({
selector: 'tb-route-map-widget-settings',

View File

@ -33,7 +33,7 @@ import {
TencentMapProviderSettings,
TencentMapType,
tencentMapTypeProviderTranslationMap
} from '@home/components/widget/lib/maps/map-models';
} from '@home/components/widget/lib/maps-legacy/map-models';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({

View File

@ -29,7 +29,7 @@ import { PageComponent } from '@shared/components/page.component';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { TranslateService } from '@ngx-translate/core';
import { TripAnimationCommonSettings } from '@home/components/widget/lib/maps/map-models';
import { TripAnimationCommonSettings } from '@home/components/widget/lib/maps-legacy/map-models';
import { Widget } from '@shared/models/widget.models';
import { WidgetService } from '@core/http/widget.service';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';

View File

@ -29,7 +29,7 @@ import { PageComponent } from '@shared/components/page.component';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { TranslateService } from '@ngx-translate/core';
import { TripAnimationMarkerSettings } from '@home/components/widget/lib/maps/map-models';
import { TripAnimationMarkerSettings } from '@home/components/widget/lib/maps-legacy/map-models';
import { WidgetService } from '@core/http/widget.service';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';

View File

@ -32,7 +32,7 @@ import {
PolylineDecoratorSymbol,
polylineDecoratorSymbolTranslationMap,
PolylineSettings
} from '@home/components/widget/lib/maps/map-models';
} from '@home/components/widget/lib/maps-legacy/map-models';
import { WidgetService } from '@core/http/widget.service';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';

View File

@ -33,7 +33,7 @@ import {
PolylineDecoratorSymbol,
polylineDecoratorSymbolTranslationMap,
PolylineSettings
} from '@home/components/widget/lib/maps/map-models';
} from '@home/components/widget/lib/maps-legacy/map-models';
import { WidgetService } from '@core/http/widget.service';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';

View File

@ -35,7 +35,7 @@ import {
PointsSettings,
PolygonSettings,
PolylineSettings
} from 'src/app/modules/home/components/widget/lib/maps/map-models';
} from 'src/app/modules/home/components/widget/lib/maps-legacy/map-models';
import { extractType } from '@core/utils';
@Component({

View File

@ -31,7 +31,7 @@ import {
defaultTripAnimationSettings,
MapProviders,
WidgetUnitedTripAnimationSettings
} from '@home/components/widget/lib/maps/map-models';
} from '@home/components/widget/lib/maps-legacy/map-models';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
import { WidgetContext } from '@app/modules/home/models/widget-component.models';
import {
@ -39,7 +39,7 @@ import {
getRatio,
interpolateOnLineSegment,
parseWithTranslation
} from '@home/components/widget/lib/maps/common-maps-utils';
} from '@home/components/widget/lib/maps-legacy/common-maps-utils';
import { FormattedData, WidgetConfig } from '@shared/models/widget.models';
import moment from 'moment';
import {
@ -51,7 +51,7 @@ import {
parseTbFunction,
safeExecuteTbFunction
} from '@core/utils';
import { MapWidgetInterface } from '@home/components/widget/lib/maps/map-widget.interface';
import { MapWidgetInterface } from '@home/components/widget/lib/maps-legacy/map-widget.interface';
import { firstValueFrom, from } from 'rxjs';
interface DataMap {
@ -123,7 +123,7 @@ export class TripAnimationComponent implements OnInit, AfterViewInit, OnDestroy
}
ngAfterViewInit() {
import('@home/components/widget/lib/maps/map-widget2').then(
import('@home/components/widget/lib/maps-legacy/map-widget2').then(
(mod) => {
this.mapWidget = new mod.MapWidgetController(MapProviders.openstreet, false, this.ctx, this.mapContainer.nativeElement, false,
() => {
@ -378,4 +378,4 @@ export class TripAnimationComponent implements OnInit, AfterViewInit, OnDestroy
}
}
export let TbTripAnimationWidget = TripAnimationComponent;
export const TbTripAnimationWidget = TripAnimationComponent;

View File

@ -186,7 +186,7 @@ export class WidgetComponentService {
(window as any).TbCanvasDigitalGauge = mod.TbCanvasDigitalGauge;
}))
);
widgetModulesTasks.push(from(import('@home/components/widget/lib/maps/map-widget2')).pipe(
widgetModulesTasks.push(from(import('@home/components/widget/lib/maps-legacy/map-widget2')).pipe(
tap((mod) => {
(window as any).TbMapWidgetV2 = mod.TbMapWidgetV2;
}))

View File

@ -39,7 +39,7 @@ import { EdgesOverviewWidgetComponent } from '@home/components/widget/lib/edges-
import { JsonInputWidgetComponent } from '@home/components/widget/lib/json-input-widget.component';
import { QrCodeWidgetComponent } from '@home/components/widget/lib/qrcode-widget.component';
import { MarkdownWidgetComponent } from '@home/components/widget/lib/markdown-widget.component';
import { SelectEntityDialogComponent } from '@home/components/widget/lib/maps/dialogs/select-entity-dialog.component';
import { SelectEntityDialogComponent } from '@home/components/widget/lib/maps-legacy/dialogs/select-entity-dialog.component';
import { HomePageWidgetsModule } from '@home/components/widget/lib/home-page/home-page-widgets.module';
import { WIDGET_COMPONENTS_MODULE_TOKEN } from '@home/components/tokens';
import { FlotWidgetComponent } from '@home/components/widget/lib/flot-widget.component';
@ -88,6 +88,7 @@ import {
import { EllipsisChipListDirective } from '@shared/directives/ellipsis-chip-list.directive';
import { ScadaSymbolWidgetComponent } from '@home/components/widget/lib/scada/scada-symbol-widget.component';
import { TwoSegmentButtonWidgetComponent } from '@home/components/widget/lib/button/two-segment-button-widget.component';
import { MapWidgetComponent } from '@home/components/widget/lib/maps/map-widget.component';
@NgModule({
declarations: [
@ -141,7 +142,8 @@ import { TwoSegmentButtonWidgetComponent } from '@home/components/widget/lib/but
LabelValueCardWidgetComponent,
UnreadNotificationWidgetComponent,
NotificationTypeFilterPanelComponent,
ScadaSymbolWidgetComponent
ScadaSymbolWidgetComponent,
MapWidgetComponent
],
imports: [
CommonModule,
@ -202,7 +204,8 @@ import { TwoSegmentButtonWidgetComponent } from '@home/components/widget/lib/but
LabelValueCardWidgetComponent,
UnreadNotificationWidgetComponent,
NotificationTypeFilterPanelComponent,
ScadaSymbolWidgetComponent
ScadaSymbolWidgetComponent,
MapWidgetComponent
],
providers: [
{provide: WIDGET_COMPONENTS_MODULE_TOKEN, useValue: WidgetComponentsModule},

View File

@ -17,7 +17,7 @@
import { ChangeDetectorRef, Component, EventEmitter, Input, OnChanges, Output } from '@angular/core';
import { interval } from 'rxjs';
import { filter } from 'rxjs/operators';
import { HistorySelectSettings } from '@app/modules/home/components/widget/lib/maps/map-models';
import { HistorySelectSettings } from '@app/modules/home/components/widget/lib/maps-legacy/map-models';
@Component({
selector: 'tb-history-selector',

View File

@ -21,4 +21,7 @@ declare module 'leaflet' {
interface MarkerOptions {
tbMarkerData?: FormattedData;
}
namespace TB {
function sidebar(selector: string): Control;
}
}