UI: Map editor toolbar.
This commit is contained in:
parent
bd86e47f52
commit
4ab9fb9f7b
@ -45,6 +45,7 @@ class TbCircleDataLayerItem extends TbDataLayerItem<CirclesDataLayerSettings, Tb
|
|||||||
const center = new L.LatLng(circleData.latitude, circleData.longitude);
|
const center = new L.LatLng(circleData.latitude, circleData.longitude);
|
||||||
this.circleStyle = this.dataLayer.getShapeStyle(data, dsData);
|
this.circleStyle = this.dataLayer.getShapeStyle(data, dsData);
|
||||||
this.circle = L.circle(center, {
|
this.circle = L.circle(center, {
|
||||||
|
bubblingMouseEvents: false,
|
||||||
radius: circleData.radius,
|
radius: circleData.radius,
|
||||||
...this.circleStyle,
|
...this.circleStyle,
|
||||||
snapIgnore: !this.dataLayer.isSnappable()
|
snapIgnore: !this.dataLayer.isSnappable()
|
||||||
@ -54,7 +55,7 @@ class TbCircleDataLayerItem extends TbDataLayerItem<CirclesDataLayerSettings, Tb
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected createEventListeners(data: FormattedData<TbMapDatasource>, _dsData: FormattedData<TbMapDatasource>[]): void {
|
protected createEventListeners(data: FormattedData<TbMapDatasource>, _dsData: FormattedData<TbMapDatasource>[]): void {
|
||||||
this.dataLayer.getMap().circleClick(this.circle, data.$datasource);
|
this.dataLayer.getMap().circleClick(this, data.$datasource);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected unbindLabel() {
|
protected unbindLabel() {
|
||||||
@ -62,7 +63,7 @@ class TbCircleDataLayerItem extends TbDataLayerItem<CirclesDataLayerSettings, Tb
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected bindLabel(content: L.Content): void {
|
protected bindLabel(content: L.Content): void {
|
||||||
this.circle.bindTooltip(content, { className: 'tb-polygon-label', permanent: true, direction: 'center'})
|
this.circle.bindTooltip(content, { className: 'tb-circle-label', permanent: true, direction: 'center'})
|
||||||
.openTooltip(this.circle.getLatLng());
|
.openTooltip(this.circle.getLatLng());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,6 +111,14 @@ class TbCircleDataLayerItem extends TbDataLayerItem<CirclesDataLayerSettings, Tb
|
|||||||
this.circle.off('pm:dragend');
|
this.circle.off('pm:dragend');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected removeDataItem(): void {
|
||||||
|
this.dataLayer.saveCircleCoordinates(this.data, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public isEditing() {
|
||||||
|
return this.editing;
|
||||||
|
}
|
||||||
|
|
||||||
private saveCircleCoordinates() {
|
private saveCircleCoordinates() {
|
||||||
const center = this.circle.getLatLng();
|
const center = this.circle.getLatLng();
|
||||||
const radius = this.circle.getRadius();
|
const radius = this.circle.getRadius();
|
||||||
@ -169,7 +178,7 @@ export class TbCirclesDataLayer extends TbShapesDataLayer<CirclesDataLayerSettin
|
|||||||
}
|
}
|
||||||
|
|
||||||
public saveCircleCoordinates(data: FormattedData<TbMapDatasource>, center: L.LatLng, radius: number): void {
|
public saveCircleCoordinates(data: FormattedData<TbMapDatasource>, center: L.LatLng, radius: number): void {
|
||||||
const converted = this.map.coordinatesToCircleData(center, radius);
|
const converted = center ? this.map.coordinatesToCircleData(center, radius) : null;
|
||||||
const circleData = [
|
const circleData = [
|
||||||
{
|
{
|
||||||
dataKey: this.settings.circleKey,
|
dataKey: this.settings.circleKey,
|
||||||
|
|||||||
@ -39,17 +39,19 @@ import {
|
|||||||
parseTbFunction,
|
parseTbFunction,
|
||||||
safeExecuteTbFunction
|
safeExecuteTbFunction
|
||||||
} from '@core/utils';
|
} from '@core/utils';
|
||||||
import L, { LatLngBounds } from 'leaflet';
|
import L from 'leaflet';
|
||||||
import { CompiledTbFunction } from '@shared/models/js-function.models';
|
import { CompiledTbFunction } from '@shared/models/js-function.models';
|
||||||
import { map } from 'rxjs/operators';
|
import { map } from 'rxjs/operators';
|
||||||
import { WidgetContext } from '@home/models/widget-component.models';
|
import { WidgetContext } from '@home/models/widget-component.models';
|
||||||
import { CustomTranslatePipe } from '@shared/pipe/custom-translate.pipe';
|
import { CustomTranslatePipe } from '@shared/pipe/custom-translate.pipe';
|
||||||
|
|
||||||
export abstract class TbDataLayerItem<S extends MapDataLayerSettings, D extends TbMapDataLayer<S,D>, L extends L.Layer = L.Layer> {
|
export abstract class TbDataLayerItem<S extends MapDataLayerSettings = MapDataLayerSettings,
|
||||||
|
D extends TbMapDataLayer<S,D> = TbMapDataLayer<any, any>, L extends L.Layer = L.Layer> {
|
||||||
|
|
||||||
protected layer: L;
|
protected layer: L;
|
||||||
protected tooltip: L.Popup;
|
protected tooltip: L.Popup;
|
||||||
protected data: FormattedData<TbMapDatasource>;
|
protected data: FormattedData<TbMapDatasource>;
|
||||||
|
protected selected = false;
|
||||||
|
|
||||||
protected constructor(data: FormattedData<TbMapDatasource>,
|
protected constructor(data: FormattedData<TbMapDatasource>,
|
||||||
dsData: FormattedData<TbMapDatasource>[],
|
dsData: FormattedData<TbMapDatasource>[],
|
||||||
@ -61,6 +63,7 @@ export abstract class TbDataLayerItem<S extends MapDataLayerSettings, D extends
|
|||||||
this.createTooltip(data.$datasource);
|
this.createTooltip(data.$datasource);
|
||||||
this.updateTooltip(data, dsData);
|
this.updateTooltip(data, dsData);
|
||||||
}
|
}
|
||||||
|
this.bindEvents();
|
||||||
this.createEventListeners(data, dsData);
|
this.createEventListeners(data, dsData);
|
||||||
try {
|
try {
|
||||||
this.dataLayer.getDataLayerContainer().addLayer(this.layer);
|
this.dataLayer.getDataLayerContainer().addLayer(this.layer);
|
||||||
@ -90,6 +93,21 @@ export abstract class TbDataLayerItem<S extends MapDataLayerSettings, D extends
|
|||||||
|
|
||||||
protected abstract disableDrag(): void;
|
protected abstract disableDrag(): void;
|
||||||
|
|
||||||
|
protected bindEvents(): void {
|
||||||
|
if (this.dataLayer.isSelectable()) {
|
||||||
|
this.layer.on('click', () => {
|
||||||
|
if (!this.isEditing()) {
|
||||||
|
this.dataLayer.getMap().selectItem(this);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.layer.on('remove', () => {
|
||||||
|
if (this.selected) {
|
||||||
|
this.dataLayer.getMap().deselectItem();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected enableEdit(): void {
|
protected enableEdit(): void {
|
||||||
if (this.dataLayer.isHoverable()) {
|
if (this.dataLayer.isHoverable()) {
|
||||||
this.addItemClass('tb-hoverable');
|
this.addItemClass('tb-hoverable');
|
||||||
@ -110,16 +128,57 @@ export abstract class TbDataLayerItem<S extends MapDataLayerSettings, D extends
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected updateSelectedState() {
|
||||||
|
if (this.selected) {
|
||||||
|
this.addItemClass('tb-selected');
|
||||||
|
} else {
|
||||||
|
this.removeItemClass('tb-selected');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public invalidateCoordinates(): void {
|
public invalidateCoordinates(): void {
|
||||||
this.doInvalidateCoordinates(this.data, this.dataLayer.getMap().getData());
|
this.doInvalidateCoordinates(this.data, this.dataLayer.getMap().getData());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public select(): L.TB.ToolbarButtonOptions[] {
|
||||||
|
if (!this.selected) {
|
||||||
|
this.selected = true;
|
||||||
|
this.updateSelectedState();
|
||||||
|
const buttons: L.TB.ToolbarButtonOptions[] = [];
|
||||||
|
if (this.dataLayer.isRemoveEnabled()) {
|
||||||
|
buttons.push({
|
||||||
|
title: this.dataLayer.getCtx().translate.instant('action.remove'),
|
||||||
|
click: () => {
|
||||||
|
this.removeDataItem();
|
||||||
|
},
|
||||||
|
iconClass: 'tb-remove'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return buttons;
|
||||||
|
} else {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public deselect() {
|
||||||
|
if (this.selected) {
|
||||||
|
this.selected = false;
|
||||||
|
this.layer.closePopup();
|
||||||
|
this.updateSelectedState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public isSelected() {
|
||||||
|
return this.selected;
|
||||||
|
}
|
||||||
|
|
||||||
public editModeUpdated() {
|
public editModeUpdated() {
|
||||||
if (this.dataLayer.isEditMode()) {
|
if (this.dataLayer.isEditMode()) {
|
||||||
this.enableEdit();
|
this.enableEdit();
|
||||||
} else {
|
} else {
|
||||||
this.disableEdit();
|
this.disableEdit();
|
||||||
}
|
}
|
||||||
|
this.updateSelectedState();
|
||||||
}
|
}
|
||||||
|
|
||||||
public update(data: FormattedData<TbMapDatasource>, dsData: FormattedData<TbMapDatasource>[]): void {
|
public update(data: FormattedData<TbMapDatasource>, dsData: FormattedData<TbMapDatasource>[]): void {
|
||||||
@ -128,14 +187,25 @@ export abstract class TbDataLayerItem<S extends MapDataLayerSettings, D extends
|
|||||||
}
|
}
|
||||||
|
|
||||||
public remove() {
|
public remove() {
|
||||||
this.layer.off();
|
if (this.selected) {
|
||||||
|
this.dataLayer.getMap().deselectItem();
|
||||||
|
}
|
||||||
this.dataLayer.getDataLayerContainer().removeLayer(this.layer);
|
this.dataLayer.getDataLayerContainer().removeLayer(this.layer);
|
||||||
|
this.layer.off();
|
||||||
}
|
}
|
||||||
|
|
||||||
public getLayer(): L {
|
public getLayer(): L {
|
||||||
return this.layer;
|
return this.layer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getDataLayer(): D {
|
||||||
|
return this.dataLayer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public isEditing() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
protected updateTooltip(data: FormattedData<TbMapDatasource>, dsData: FormattedData<TbMapDatasource>[]) {
|
protected updateTooltip(data: FormattedData<TbMapDatasource>, dsData: FormattedData<TbMapDatasource>[]) {
|
||||||
if (this.settings.tooltip.show) {
|
if (this.settings.tooltip.show) {
|
||||||
let tooltipTemplate = this.dataLayer.dataLayerTooltipProcessor.processPattern(data, dsData);
|
let tooltipTemplate = this.dataLayer.dataLayerTooltipProcessor.processPattern(data, dsData);
|
||||||
@ -157,13 +227,25 @@ export abstract class TbDataLayerItem<S extends MapDataLayerSettings, D extends
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected abstract removeDataItem(): void;
|
||||||
|
|
||||||
private createTooltip(datasource: TbMapDatasource) {
|
private createTooltip(datasource: TbMapDatasource) {
|
||||||
this.tooltip = L.popup();
|
this.tooltip = L.popup();
|
||||||
this.layer.bindPopup(this.tooltip, {autoClose: this.settings.tooltip.autoclose, closeOnClick: false});
|
this.layer.bindPopup(this.tooltip, {autoClose: this.settings.tooltip.autoclose, closeOnClick: false});
|
||||||
if (this.settings.tooltip.trigger === DataLayerTooltipTrigger.hover) {
|
|
||||||
this.layer.off('click');
|
this.layer.off('click');
|
||||||
this.layer.on('mouseover', () => {
|
if (this.settings.tooltip.trigger === DataLayerTooltipTrigger.click) {
|
||||||
|
this.layer.on('click', () => {
|
||||||
|
if (this.tooltip.isOpen()) {
|
||||||
|
this.layer.closePopup();
|
||||||
|
} else if (!this.isEditing()) {
|
||||||
this.layer.openPopup();
|
this.layer.openPopup();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (this.settings.tooltip.trigger === DataLayerTooltipTrigger.hover) {
|
||||||
|
this.layer.on('mouseover', () => {
|
||||||
|
if (!this.isEditing()) {
|
||||||
|
this.layer.openPopup();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
this.layer.on('mousemove', (e) => {
|
this.layer.on('mousemove', (e) => {
|
||||||
this.tooltip.setLatLng(e.latlng);
|
this.tooltip.setLatLng(e.latlng);
|
||||||
@ -345,7 +427,7 @@ export abstract class TbMapDataLayer<S extends MapDataLayerSettings, D extends T
|
|||||||
return this.dataLayerContainer;
|
return this.dataLayerContainer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getBounds(): LatLngBounds {
|
public getBounds(): L.LatLngBounds {
|
||||||
return this.dataLayerContainer.getBounds();
|
return this.dataLayerContainer.getBounds();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -39,7 +39,14 @@ import L, { FeatureGroup } from 'leaflet';
|
|||||||
import { FormattedData } from '@shared/models/widget.models';
|
import { FormattedData } from '@shared/models/widget.models';
|
||||||
import { forkJoin, Observable, of } from 'rxjs';
|
import { forkJoin, Observable, of } from 'rxjs';
|
||||||
import { CompiledTbFunction } from '@shared/models/js-function.models';
|
import { CompiledTbFunction } from '@shared/models/js-function.models';
|
||||||
import { isDefined, isDefinedAndNotNull, isEmptyStr, parseTbFunction, safeExecuteTbFunction } from '@core/utils';
|
import {
|
||||||
|
deepClone,
|
||||||
|
isDefined,
|
||||||
|
isDefinedAndNotNull,
|
||||||
|
isEmptyStr,
|
||||||
|
parseTbFunction,
|
||||||
|
safeExecuteTbFunction
|
||||||
|
} from '@core/utils';
|
||||||
import { catchError, map, switchMap } from 'rxjs/operators';
|
import { catchError, map, switchMap } from 'rxjs/operators';
|
||||||
import tinycolor from 'tinycolor2';
|
import tinycolor from 'tinycolor2';
|
||||||
import { ImagePipe } from '@shared/pipe/image.pipe';
|
import { ImagePipe } from '@shared/pipe/image.pipe';
|
||||||
@ -77,16 +84,15 @@ class TbMarkerDataLayerItem extends TbDataLayerItem<MarkersDataLayerSettings, Tb
|
|||||||
const location = this.dataLayer.extractLocation(data, dsData);
|
const location = this.dataLayer.extractLocation(data, dsData);
|
||||||
this.marker = L.marker(location, {
|
this.marker = L.marker(location, {
|
||||||
tbMarkerData: data,
|
tbMarkerData: data,
|
||||||
snapIgnore: !this.dataLayer.isSnappable()
|
snapIgnore: !this.dataLayer.isSnappable(),
|
||||||
|
bubblingMouseEvents: false
|
||||||
});
|
});
|
||||||
|
|
||||||
this.updateMarkerIcon(data, dsData);
|
this.updateMarkerIcon(data, dsData);
|
||||||
|
|
||||||
return this.marker;
|
return this.marker;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected createEventListeners(data: FormattedData<TbMapDatasource>, _dsData: FormattedData<TbMapDatasource>[]): void {
|
protected createEventListeners(data: FormattedData<TbMapDatasource>, _dsData: FormattedData<TbMapDatasource>[]): void {
|
||||||
this.dataLayer.getMap().markerClick(this.marker, data.$datasource);
|
this.dataLayer.getMap().markerClick(this, data.$datasource);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected unbindLabel() {
|
protected unbindLabel() {
|
||||||
@ -122,7 +128,7 @@ class TbMarkerDataLayerItem extends TbDataLayerItem<MarkersDataLayerSettings, Tb
|
|||||||
const index = this.iconClassList.indexOf(clazz);
|
const index = this.iconClassList.indexOf(clazz);
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
this.iconClassList.splice(index, 1);
|
this.iconClassList.splice(index, 1);
|
||||||
this.marker.options.icon.options.className = this.updateIconClasses(this.marker.options.icon.options.className);
|
this.marker.options.icon.options.className = this.updateIconClasses(this.marker.options.icon.options.className, clazz);
|
||||||
if ((this.marker as any)._icon) {
|
if ((this.marker as any)._icon) {
|
||||||
L.DomUtil.removeClass((this.marker as any)._icon, clazz);
|
L.DomUtil.removeClass((this.marker as any)._icon, clazz);
|
||||||
}
|
}
|
||||||
@ -166,6 +172,14 @@ class TbMarkerDataLayerItem extends TbDataLayerItem<MarkersDataLayerSettings, Tb
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected removeDataItem(): void {
|
||||||
|
this.dataLayer.saveMarkerLocation(this.data, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public isEditing() {
|
||||||
|
return this.moving;
|
||||||
|
}
|
||||||
|
|
||||||
private saveMarkerLocation() {
|
private saveMarkerLocation() {
|
||||||
const location = this.marker.getLatLng();
|
const location = this.marker.getLatLng();
|
||||||
this.dataLayer.saveMarkerLocation(this.data, location);
|
this.dataLayer.saveMarkerLocation(this.data, location);
|
||||||
@ -181,9 +195,16 @@ class TbMarkerDataLayerItem extends TbDataLayerItem<MarkersDataLayerSettings, Tb
|
|||||||
private updateMarkerIcon(data: FormattedData<TbMapDatasource>, dsData: FormattedData<TbMapDatasource>[]) {
|
private updateMarkerIcon(data: FormattedData<TbMapDatasource>, dsData: FormattedData<TbMapDatasource>[]) {
|
||||||
this.dataLayer.markerIconProcessor.createMarkerIcon(data, dsData).subscribe(
|
this.dataLayer.markerIconProcessor.createMarkerIcon(data, dsData).subscribe(
|
||||||
(iconInfo) => {
|
(iconInfo) => {
|
||||||
iconInfo.icon.options.className = this.updateIconClasses(iconInfo.icon.options.className);
|
let icon: L.Icon | L.DivIcon;
|
||||||
this.marker.setIcon(iconInfo.icon);
|
const options = deepClone(iconInfo.icon.options);
|
||||||
const anchor = iconInfo.icon.options.iconAnchor;
|
options.className = this.updateIconClasses(options.className);
|
||||||
|
if (iconInfo.icon instanceof L.Icon) {
|
||||||
|
icon = L.icon(options as L.IconOptions);
|
||||||
|
} else {
|
||||||
|
icon = L.divIcon(options);
|
||||||
|
}
|
||||||
|
this.marker.setIcon(icon);
|
||||||
|
const anchor = options.iconAnchor;
|
||||||
if (anchor && Array.isArray(anchor)) {
|
if (anchor && Array.isArray(anchor)) {
|
||||||
this.labelOffset = [iconInfo.size[0] / 2 - anchor[0], 10 - anchor[1]];
|
this.labelOffset = [iconInfo.size[0] / 2 - anchor[0], 10 - anchor[1]];
|
||||||
} else {
|
} else {
|
||||||
@ -195,11 +216,17 @@ class TbMarkerDataLayerItem extends TbDataLayerItem<MarkersDataLayerSettings, Tb
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateIconClasses(className: string): string {
|
private updateIconClasses(className: string, toRemove?: string): string {
|
||||||
const classes: string[] = [];
|
const classes: string[] = [];
|
||||||
if (className?.length) {
|
if (className?.length) {
|
||||||
classes.push(...className.split(' '));
|
classes.push(...className.split(' '));
|
||||||
}
|
}
|
||||||
|
if (toRemove?.length) {
|
||||||
|
const index = classes.indexOf(toRemove);
|
||||||
|
if (index !== -1) {
|
||||||
|
classes.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
this.iconClassList.forEach(clazz => {
|
this.iconClassList.forEach(clazz => {
|
||||||
if (!classes.includes(clazz)) {
|
if (!classes.includes(clazz)) {
|
||||||
classes.push(clazz);
|
classes.push(clazz);
|
||||||
|
|||||||
@ -48,7 +48,8 @@ class TbPolygonDataLayerItem extends TbDataLayerItem<PolygonsDataLayerSettings,
|
|||||||
this.polygonStyle = this.dataLayer.getShapeStyle(data, dsData);
|
this.polygonStyle = this.dataLayer.getShapeStyle(data, dsData);
|
||||||
this.polygon = polyConstructor(polyData as (TbPolygonRawCoordinates & L.LatLngTuple[]), {
|
this.polygon = polyConstructor(polyData as (TbPolygonRawCoordinates & L.LatLngTuple[]), {
|
||||||
...this.polygonStyle,
|
...this.polygonStyle,
|
||||||
snapIgnore: !this.dataLayer.isSnappable()
|
snapIgnore: !this.dataLayer.isSnappable(),
|
||||||
|
bubblingMouseEvents: false
|
||||||
});
|
});
|
||||||
|
|
||||||
this.polygonContainer = L.featureGroup();
|
this.polygonContainer = L.featureGroup();
|
||||||
@ -59,7 +60,7 @@ class TbPolygonDataLayerItem extends TbDataLayerItem<PolygonsDataLayerSettings,
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected createEventListeners(data: FormattedData<TbMapDatasource>, _dsData: FormattedData<TbMapDatasource>[]): void {
|
protected createEventListeners(data: FormattedData<TbMapDatasource>, _dsData: FormattedData<TbMapDatasource>[]): void {
|
||||||
this.dataLayer.getMap().polygonClick(this.polygonContainer, data.$datasource);
|
this.dataLayer.getMap().polygonClick(this, data.$datasource);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected unbindLabel() {
|
protected unbindLabel() {
|
||||||
@ -103,7 +104,12 @@ class TbPolygonDataLayerItem extends TbDataLayerItem<PolygonsDataLayerSettings,
|
|||||||
this.polygon.on('pm:dragstart', () => {
|
this.polygon.on('pm:dragstart', () => {
|
||||||
this.editing = true;
|
this.editing = true;
|
||||||
});
|
});
|
||||||
this.polygon.on('pm:dragend', () => {
|
this.polygon.on('pm:drag', () => {
|
||||||
|
if (this.tooltip?.isOpen()) {
|
||||||
|
this.tooltip.setLatLng(this.polygon.getBounds().getCenter());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.polygon.on('pm:dragend', (e) => {
|
||||||
this.savePolygonCoordinates();
|
this.savePolygonCoordinates();
|
||||||
this.editing = false;
|
this.editing = false;
|
||||||
});
|
});
|
||||||
@ -115,6 +121,14 @@ class TbPolygonDataLayerItem extends TbDataLayerItem<PolygonsDataLayerSettings,
|
|||||||
this.polygon.off('pm:dragend');
|
this.polygon.off('pm:dragend');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected removeDataItem(): void {
|
||||||
|
this.dataLayer.savePolygonCoordinates(this.data, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public isEditing() {
|
||||||
|
return this.editing;
|
||||||
|
}
|
||||||
|
|
||||||
private savePolygonCoordinates() {
|
private savePolygonCoordinates() {
|
||||||
let coordinates: TbPolygonCoordinates = this.polygon.getLatLngs();
|
let coordinates: TbPolygonCoordinates = this.polygon.getLatLngs();
|
||||||
if (coordinates.length === 1) {
|
if (coordinates.length === 1) {
|
||||||
@ -140,7 +154,8 @@ class TbPolygonDataLayerItem extends TbDataLayerItem<PolygonsDataLayerSettings,
|
|||||||
this.polygonContainer.removeLayer(this.polygon);
|
this.polygonContainer.removeLayer(this.polygon);
|
||||||
this.polygon = L.polygon(polyData, {
|
this.polygon = L.polygon(polyData, {
|
||||||
...this.polygonStyle,
|
...this.polygonStyle,
|
||||||
snapIgnore: !this.dataLayer.isSnappable()
|
snapIgnore: !this.dataLayer.isSnappable(),
|
||||||
|
bubblingMouseEvents: false
|
||||||
});
|
});
|
||||||
this.polygon.addTo(this.polygonContainer);
|
this.polygon.addTo(this.polygonContainer);
|
||||||
this.editModeUpdated();
|
this.editModeUpdated();
|
||||||
@ -197,7 +212,7 @@ export class TbPolygonsDataLayer extends TbShapesDataLayer<PolygonsDataLayerSett
|
|||||||
}
|
}
|
||||||
|
|
||||||
public savePolygonCoordinates(data: FormattedData<TbMapDatasource>, coordinates: TbPolygonCoordinates): void {
|
public savePolygonCoordinates(data: FormattedData<TbMapDatasource>, coordinates: TbPolygonCoordinates): void {
|
||||||
const converted = this.map.coordinatesToPolygonData(coordinates);
|
const converted = coordinates ? this.map.coordinatesToPolygonData(coordinates) : null;
|
||||||
const polygonData = [
|
const polygonData = [
|
||||||
{
|
{
|
||||||
dataKey: this.settings.polygonKey,
|
dataKey: this.settings.polygonKey,
|
||||||
|
|||||||
@ -14,7 +14,7 @@
|
|||||||
/// limitations under the License.
|
/// limitations under the License.
|
||||||
///
|
///
|
||||||
|
|
||||||
import L, { Coords, TB, TileLayerOptions } from 'leaflet';
|
import L, { TB } from 'leaflet';
|
||||||
import { guid } from '@core/utils';
|
import { guid } from '@core/utils';
|
||||||
import 'leaflet-providers';
|
import 'leaflet-providers';
|
||||||
import '@geoman-io/leaflet-geoman-free';
|
import '@geoman-io/leaflet-geoman-free';
|
||||||
@ -276,6 +276,81 @@ class GroupsControl extends SidebarPaneControl<TB.GroupsControlOptions> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ToolbarButton extends L.Control<TB.ToolbarButtonOptions> {
|
||||||
|
private readonly button: JQuery<HTMLElement>;
|
||||||
|
constructor(options: TB.ToolbarButtonOptions) {
|
||||||
|
super(options);
|
||||||
|
|
||||||
|
this.button = $("<a>")
|
||||||
|
.attr('class', 'tb-control-button')
|
||||||
|
.attr('href', '#')
|
||||||
|
.attr('role', 'button')
|
||||||
|
.attr('title', this.options.title)
|
||||||
|
.html('<div class="'+this.options.iconClass+'"></div>');
|
||||||
|
|
||||||
|
this.button.on('click', (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
this.options.click(e.originalEvent, this);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
addToToolbar(toolbar: BottomToolbarControl): void {
|
||||||
|
this.button.appendTo(toolbar.container);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BottomToolbarControl extends L.Control<TB.BottomToolbarControlOptions> {
|
||||||
|
|
||||||
|
private readonly buttonContainer: JQuery<HTMLElement>;
|
||||||
|
|
||||||
|
container: HTMLElement;
|
||||||
|
|
||||||
|
constructor(options: TB.BottomToolbarControlOptions) {
|
||||||
|
super(options);
|
||||||
|
const controlContainer = $('.leaflet-control-container', options.mapElement);
|
||||||
|
const toolbar = $('<div class="tb-map-bottom-toolbar leaflet-bottom"></div>');
|
||||||
|
toolbar.appendTo(controlContainer);
|
||||||
|
this.buttonContainer = $('<div class="leaflet-bar leaflet-control"></div>');
|
||||||
|
this.buttonContainer.appendTo(toolbar);
|
||||||
|
this.container = this.buttonContainer[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
addTo(map: L.Map): this {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
open(buttons: TB.ToolbarButtonOptions[]): void {
|
||||||
|
|
||||||
|
buttons.forEach(buttonOption => {
|
||||||
|
const button = new ToolbarButton(buttonOption);
|
||||||
|
button.addToToolbar(this);
|
||||||
|
});
|
||||||
|
|
||||||
|
const closeButton = $("<a>")
|
||||||
|
.attr('class', 'tb-control-button')
|
||||||
|
.attr('href', '#')
|
||||||
|
.attr('role', 'button')
|
||||||
|
.attr('title', this.options.closeTitle)
|
||||||
|
.html('<div class="tb-close"></div>');
|
||||||
|
|
||||||
|
closeButton.on('click', (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
this.close();
|
||||||
|
});
|
||||||
|
closeButton.appendTo(this.buttonContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
close(): void {
|
||||||
|
this.buttonContainer.empty();
|
||||||
|
if (this.options.onClose) {
|
||||||
|
this.options.onClose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
const sidebar = (options: TB.SidebarControlOptions): SidebarControl => {
|
const sidebar = (options: TB.SidebarControlOptions): SidebarControl => {
|
||||||
return new SidebarControl(options);
|
return new SidebarControl(options);
|
||||||
}
|
}
|
||||||
@ -292,6 +367,10 @@ const groups = (options: TB.GroupsControlOptions): GroupsControl => {
|
|||||||
return new GroupsControl(options);
|
return new GroupsControl(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const bottomToolbar = (options: TB.BottomToolbarControlOptions): BottomToolbarControl => {
|
||||||
|
return new BottomToolbarControl(options);
|
||||||
|
}
|
||||||
|
|
||||||
class ChinaProvider extends L.TileLayer {
|
class ChinaProvider extends L.TileLayer {
|
||||||
|
|
||||||
static chinaProviders: L.TB.TileLayer.ChinaProvidersData = {
|
static chinaProviders: L.TB.TileLayer.ChinaProvidersData = {
|
||||||
@ -303,7 +382,7 @@ class ChinaProvider extends L.TileLayer {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(type: string, options?: TileLayerOptions) {
|
constructor(type: string, options?: L.TileLayerOptions) {
|
||||||
options = options || {};
|
options = options || {};
|
||||||
|
|
||||||
const parts = type.split('.');
|
const parts = type.split('.');
|
||||||
@ -316,7 +395,7 @@ class ChinaProvider extends L.TileLayer {
|
|||||||
super(url, options);
|
super(url, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
getTileUrl(coords: Coords): string {
|
getTileUrl(coords: L.Coords): string {
|
||||||
const data = {
|
const data = {
|
||||||
s: this._getSubdomain(coords),
|
s: this._getSubdomain(coords),
|
||||||
x: coords.x,
|
x: coords.x,
|
||||||
@ -338,7 +417,7 @@ class ChinaProvider extends L.TileLayer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const chinaProvider = (type: string, options?: TileLayerOptions): ChinaProvider => {
|
const chinaProvider = (type: string, options?: L.TileLayerOptions): ChinaProvider => {
|
||||||
return new ChinaProvider(type, options);
|
return new ChinaProvider(type, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -347,10 +426,13 @@ L.TB = L.TB || {
|
|||||||
SidebarPaneControl,
|
SidebarPaneControl,
|
||||||
LayersControl,
|
LayersControl,
|
||||||
GroupsControl,
|
GroupsControl,
|
||||||
|
ToolbarButton,
|
||||||
|
BottomToolbarControl,
|
||||||
sidebar,
|
sidebar,
|
||||||
sidebarPane,
|
sidebarPane,
|
||||||
layers,
|
layers,
|
||||||
groups,
|
groups,
|
||||||
|
bottomToolbar,
|
||||||
TileLayer: {
|
TileLayer: {
|
||||||
ChinaProvider
|
ChinaProvider
|
||||||
},
|
},
|
||||||
|
|||||||
@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
//$map-element-hover-color: #307FE5;
|
//$map-element-hover-color: #307FE5;
|
||||||
$map-element-hover-color: rgba(0,0,0,0.56);
|
$map-element-hover-color: rgba(0,0,0,0.56);
|
||||||
|
$map-element-selected-color: #307FE5;
|
||||||
|
|
||||||
.tb-map-layout {
|
.tb-map-layout {
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -79,6 +80,17 @@ $map-element-hover-color: rgba(0,0,0,0.56);
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.tb-map-bottom-toolbar {
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
.leaflet-bar {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.leaflet-control {
|
.leaflet-control {
|
||||||
.tb-control-button {
|
.tb-control-button {
|
||||||
@ -100,6 +112,27 @@ $map-element-hover-color: rgba(0,0,0,0.56);
|
|||||||
&.tb-groups {
|
&.tb-groups {
|
||||||
mask-image: url('data:image/svg+xml,<svg width="20" height="20" viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg"><path d="M6 13.5C5.5875 13.5 5.2345 13.3533 4.941 13.0597C4.6475 12.7662 4.5005 12.413 4.5 12V3C4.5 2.5875 4.647 2.2345 4.941 1.941C5.235 1.6475 5.588 1.5005 6 1.5H15C15.4125 1.5 15.7657 1.647 16.0597 1.941C16.3538 2.235 16.5005 2.588 16.5 3V12C16.5 12.4125 16.3533 12.7657 16.0597 13.0597C15.7662 13.3538 15.413 13.5005 15 13.5H6ZM6 4.5H15V3H6V4.5ZM3 16.5C2.5875 16.5 2.2345 16.3533 1.941 16.0597C1.6475 15.7662 1.5005 15.413 1.5 15V4.5H3V15H13.5V16.5H3Z"/></svg>');
|
mask-image: url('data:image/svg+xml,<svg width="20" height="20" viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg"><path d="M6 13.5C5.5875 13.5 5.2345 13.3533 4.941 13.0597C4.6475 12.7662 4.5005 12.413 4.5 12V3C4.5 2.5875 4.647 2.2345 4.941 1.941C5.235 1.6475 5.588 1.5005 6 1.5H15C15.4125 1.5 15.7657 1.647 16.0597 1.941C16.3538 2.235 16.5005 2.588 16.5 3V12C16.5 12.4125 16.3533 12.7657 16.0597 13.0597C15.7662 13.3538 15.413 13.5005 15 13.5H6ZM6 4.5H15V3H6V4.5ZM3 16.5C2.5875 16.5 2.2345 16.3533 1.941 16.0597C1.6475 15.7662 1.5005 15.413 1.5 15V4.5H3V15H13.5V16.5H3Z"/></svg>');
|
||||||
}
|
}
|
||||||
|
&.tb-remove {
|
||||||
|
mask-image: url('data:image/svg+xml,<svg width="20" height="20" viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg"><path d="M4.5 14.25C4.5 14.6478 4.65804 15.0294 4.93934 15.3107C5.22064 15.592 5.60218 15.75 6 15.75H12C12.3978 15.75 12.7794 15.592 13.0607 15.3107C13.342 15.0294 13.5 14.6478 13.5 14.25V5.25H4.5V14.25ZM6 6.75H12V14.25H6V6.75ZM11.625 3L10.875 2.25H7.125L6.375 3H3.75V4.5H14.25V3H11.625Z"/></svg>');
|
||||||
|
}
|
||||||
|
&.tb-close {
|
||||||
|
background: #D12730;
|
||||||
|
mask-image: url('data:image/svg+xml,<svg width="20" height="20" viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg"><path d="M14.25 4.8075L13.1925 3.75L9 7.9425L4.8075 3.75L3.75 4.8075L7.9425 9L3.75 13.1925L4.8075 14.25L9 10.0575L13.1925 14.25L14.25 13.1925L10.0575 9L14.25 4.8075Z"/></svg>');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.leaflet-map-pane:not(.leaflet-zoom-anim) {
|
||||||
|
.leaflet-marker-icon {
|
||||||
|
&.tb-hoverable:not(.tb-selected) {
|
||||||
|
svg {
|
||||||
|
transition: filter 0.2s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
img.leaflet-marker-icon, path {
|
||||||
|
&.tb-hoverable:not(.tb-selected) {
|
||||||
|
transition: filter 0.2s;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -110,28 +143,35 @@ $map-element-hover-color: rgba(0,0,0,0.56);
|
|||||||
&.tb-draggable {
|
&.tb-draggable {
|
||||||
cursor: move;
|
cursor: move;
|
||||||
}
|
}
|
||||||
&.tb-hoverable {
|
&.tb-hoverable:not(.tb-selected) {
|
||||||
svg {
|
|
||||||
transition: filter 0.2s;
|
|
||||||
}
|
|
||||||
&:hover {
|
&:hover {
|
||||||
svg {
|
svg {
|
||||||
filter: drop-shadow( 0 0 4px $map-element-hover-color);
|
//filter: drop-shadow( 0 0 4px $map-element-hover-color);
|
||||||
|
filter: brightness(0.8) drop-shadow( 0 0 4px $map-element-hover-color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
&.tb-selected {
|
||||||
|
svg {
|
||||||
|
filter: brightness(0.8);
|
||||||
|
//animation: tb-selected-animation 0.5s linear 0s infinite alternate;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
img.leaflet-marker-icon, path {
|
img.leaflet-marker-icon, path {
|
||||||
&.tb-draggable {
|
&.tb-draggable {
|
||||||
cursor: move;
|
cursor: move;
|
||||||
}
|
}
|
||||||
&.tb-hoverable {
|
&.tb-hoverable:not(.tb-selected) {
|
||||||
transition: filter 0.2s;
|
|
||||||
&:hover {
|
&:hover {
|
||||||
filter: drop-shadow( 0 0 4px $map-element-hover-color);
|
filter: brightness(0.8) drop-shadow( 0 0 4px $map-element-hover-color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
&.tb-selected {
|
||||||
|
filter: brightness(0.8);
|
||||||
|
//animation: tb-selected-animation 0.5s linear 0s infinite alternate;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.tb-cluster-marker-container {
|
.tb-cluster-marker-container {
|
||||||
border: none;
|
border: none;
|
||||||
@ -144,6 +184,11 @@ $map-element-hover-color: rgba(0,0,0,0.56);
|
|||||||
width: 40px;
|
width: 40px;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
}
|
}
|
||||||
|
.tb-marker-label, .tb-polygon-label, .tb-circle-label {
|
||||||
|
border: none;
|
||||||
|
background: none;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.tb-map-sidebar {
|
.tb-map-sidebar {
|
||||||
.tb-layers, .tb-groups {
|
.tb-layers, .tb-groups {
|
||||||
@ -267,3 +312,14 @@ $map-element-hover-color: rgba(0,0,0,0.56);
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@keyframes tb-selected-animation {
|
||||||
|
0% {
|
||||||
|
//filter: drop-shadow( 0 0 2px $map-element-selected-color);
|
||||||
|
filter: brightness(1);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
//filter: drop-shadow( 0 0 4px $map-element-selected-color) drop-shadow( 0 0 4px $map-element-selected-color);
|
||||||
|
filter: brightness(0.8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -32,7 +32,11 @@ import L from 'leaflet';
|
|||||||
import { forkJoin, Observable, of } from 'rxjs';
|
import { forkJoin, Observable, of } from 'rxjs';
|
||||||
import { switchMap } from 'rxjs/operators';
|
import { switchMap } from 'rxjs/operators';
|
||||||
import '@home/components/widget/lib/maps/leaflet/leaflet-tb';
|
import '@home/components/widget/lib/maps/leaflet/leaflet-tb';
|
||||||
import { MapDataLayerType, TbMapDataLayer, } from '@home/components/widget/lib/maps/data-layer/map-data-layer';
|
import {
|
||||||
|
MapDataLayerType,
|
||||||
|
TbDataLayerItem,
|
||||||
|
TbMapDataLayer,
|
||||||
|
} from '@home/components/widget/lib/maps/data-layer/map-data-layer';
|
||||||
import { IWidgetSubscription, WidgetSubscriptionOptions } from '@core/api/widget-api.models';
|
import { IWidgetSubscription, WidgetSubscriptionOptions } from '@core/api/widget-api.models';
|
||||||
import { FormattedData, WidgetActionDescriptor, widgetType } from '@shared/models/widget.models';
|
import { FormattedData, WidgetActionDescriptor, widgetType } from '@shared/models/widget.models';
|
||||||
import { EntityDataPageLink } from '@shared/models/query/query.models';
|
import { EntityDataPageLink } from '@shared/models/query/query.models';
|
||||||
@ -44,6 +48,9 @@ import { AttributeService } from '@core/http/attribute.service';
|
|||||||
import { AttributeData, AttributeScope, DataKeyType, LatestTelemetry } from '@shared/models/telemetry/telemetry.models';
|
import { AttributeData, AttributeScope, DataKeyType, LatestTelemetry } from '@shared/models/telemetry/telemetry.models';
|
||||||
import { EntityId } from '@shared/models/id/entity-id';
|
import { EntityId } from '@shared/models/id/entity-id';
|
||||||
import ITooltipsterInstance = JQueryTooltipster.ITooltipsterInstance;
|
import ITooltipsterInstance = JQueryTooltipster.ITooltipsterInstance;
|
||||||
|
import TooltipPositioningSide = JQueryTooltipster.TooltipPositioningSide;
|
||||||
|
|
||||||
|
type TooltipInstancesData = {root: HTMLElement, instances: ITooltipsterInstance[]};
|
||||||
|
|
||||||
export abstract class TbMap<S extends BaseMapSettings> {
|
export abstract class TbMap<S extends BaseMapSettings> {
|
||||||
|
|
||||||
@ -59,10 +66,14 @@ export abstract class TbMap<S extends BaseMapSettings> {
|
|||||||
protected dataLayers: TbMapDataLayer<any,any>[];
|
protected dataLayers: TbMapDataLayer<any,any>[];
|
||||||
protected dsData: FormattedData<TbMapDatasource>[];
|
protected dsData: FormattedData<TbMapDatasource>[];
|
||||||
|
|
||||||
|
protected selectedDataItem: TbDataLayerItem;
|
||||||
|
|
||||||
protected mapElement: HTMLElement;
|
protected mapElement: HTMLElement;
|
||||||
|
|
||||||
protected sidebar: L.TB.SidebarControl;
|
protected sidebar: L.TB.SidebarControl;
|
||||||
|
|
||||||
|
protected editToolbar: L.TB.BottomToolbarControl;
|
||||||
|
|
||||||
private readonly mapResize$: ResizeObserver;
|
private readonly mapResize$: ResizeObserver;
|
||||||
|
|
||||||
private readonly tooltipActions: { [name: string]: MapActionHandler };
|
private readonly tooltipActions: { [name: string]: MapActionHandler };
|
||||||
@ -70,7 +81,7 @@ export abstract class TbMap<S extends BaseMapSettings> {
|
|||||||
private readonly polygonClickActions: { [name: string]: MapActionHandler };
|
private readonly polygonClickActions: { [name: string]: MapActionHandler };
|
||||||
private readonly circleClickActions: { [name: string]: MapActionHandler };
|
private readonly circleClickActions: { [name: string]: MapActionHandler };
|
||||||
|
|
||||||
private tooltipInstances: ITooltipsterInstance[] = [];
|
private tooltipInstances: TooltipInstancesData[] = [];
|
||||||
|
|
||||||
protected constructor(protected ctx: WidgetContext,
|
protected constructor(protected ctx: WidgetContext,
|
||||||
protected inputSettings: DeepPartial<S>,
|
protected inputSettings: DeepPartial<S>,
|
||||||
@ -133,7 +144,7 @@ export abstract class TbMap<S extends BaseMapSettings> {
|
|||||||
}
|
}
|
||||||
this.setupDataLayers();
|
this.setupDataLayers();
|
||||||
this.setupEditMode();
|
this.setupEditMode();
|
||||||
this.createdControlButtonTooltip();
|
this.createdControlButtonTooltip(this.mapElement, ['topleft', 'bottomleft'].includes(this.settings.controlsPosition) ? 'right' : 'left');
|
||||||
}
|
}
|
||||||
|
|
||||||
private setupDataLayers() {
|
private setupDataLayers() {
|
||||||
@ -231,21 +242,36 @@ export abstract class TbMap<S extends BaseMapSettings> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private setupEditMode() {
|
private setupEditMode() {
|
||||||
const dragEnabled = this.dataLayers.some(dl => dl.isDragEnabled());
|
this.editToolbar = L.TB.bottomToolbar({
|
||||||
if (dragEnabled) {
|
mapElement: $(this.mapElement),
|
||||||
//this.map.pm.enableGlobalDragMode();
|
closeTitle: this.ctx.translate.instant('action.cancel'),
|
||||||
|
onClose: () => {
|
||||||
|
this.deselectItem();
|
||||||
}
|
}
|
||||||
|
}).addTo(this.map);
|
||||||
|
|
||||||
|
this.map.on('click', () => {
|
||||||
|
this.deselectItem();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private createdControlButtonTooltip() {
|
private createdControlButtonTooltip(root: HTMLElement, side: TooltipPositioningSide) {
|
||||||
import('tooltipster').then(() => {
|
import('tooltipster').then(() => {
|
||||||
|
let tooltipData = this.tooltipInstances.find(d => d.root === root);
|
||||||
|
if (!tooltipData) {
|
||||||
|
tooltipData = {
|
||||||
|
root,
|
||||||
|
instances: []
|
||||||
|
}
|
||||||
|
this.tooltipInstances.push(tooltipData);
|
||||||
|
}
|
||||||
if ($.tooltipster) {
|
if ($.tooltipster) {
|
||||||
this.tooltipInstances.forEach((instance) => {
|
tooltipData.instances.forEach((instance) => {
|
||||||
instance.destroy();
|
instance.destroy();
|
||||||
});
|
});
|
||||||
this.tooltipInstances = [];
|
tooltipData.instances = [];
|
||||||
}
|
}
|
||||||
$(this.mapElement)
|
$(root)
|
||||||
.find('a[role="button"]:not(.leaflet-pm-action)')
|
.find('a[role="button"]:not(.leaflet-pm-action)')
|
||||||
.each((_index, element) => {
|
.each((_index, element) => {
|
||||||
let title: string;
|
let title: string;
|
||||||
@ -267,7 +293,7 @@ export abstract class TbMap<S extends BaseMapSettings> {
|
|||||||
scroll: true,
|
scroll: true,
|
||||||
mouseleave: true
|
mouseleave: true
|
||||||
},
|
},
|
||||||
side: ['topleft', 'bottomleft'].includes(this.settings.controlsPosition) ? 'right' : 'left',
|
side,
|
||||||
distance: 2,
|
distance: 2,
|
||||||
trackOrigin: true,
|
trackOrigin: true,
|
||||||
functionBefore: (_instance, helper) => {
|
functionBefore: (_instance, helper) => {
|
||||||
@ -277,7 +303,14 @@ export abstract class TbMap<S extends BaseMapSettings> {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
this.tooltipInstances.push(tooltip.tooltipster('instance'));
|
const instance = tooltip.tooltipster('instance');
|
||||||
|
tooltipData.instances.push(instance);
|
||||||
|
instance.on('destroyed', () => {
|
||||||
|
const index = tooltipData.instances.indexOf(instance);
|
||||||
|
if (index > -1) {
|
||||||
|
tooltipData.instances.splice(index, 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -385,36 +418,64 @@ export abstract class TbMap<S extends BaseMapSettings> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public markerClick(marker: L.Layer, datasource: TbMapDatasource): void {
|
public markerClick(marker: TbDataLayerItem, datasource: TbMapDatasource): void {
|
||||||
if (Object.keys(this.markerClickActions).length) {
|
if (Object.keys(this.markerClickActions).length) {
|
||||||
marker.on('click', (event: L.LeafletMouseEvent) => {
|
marker.getLayer().on('click', (event: L.LeafletMouseEvent) => {
|
||||||
|
if (!marker.isEditing()) {
|
||||||
for (const action in this.markerClickActions) {
|
for (const action in this.markerClickActions) {
|
||||||
this.markerClickActions[action](event.originalEvent, datasource);
|
this.markerClickActions[action](event.originalEvent, datasource);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public polygonClick(polygon: L.Layer, datasource: TbMapDatasource): void {
|
public polygonClick(polygon: TbDataLayerItem, datasource: TbMapDatasource): void {
|
||||||
if (Object.keys(this.polygonClickActions).length) {
|
if (Object.keys(this.polygonClickActions).length) {
|
||||||
polygon.on('click', (event: L.LeafletMouseEvent) => {
|
polygon.getLayer().on('click', (event: L.LeafletMouseEvent) => {
|
||||||
|
if (!polygon.isEditing()) {
|
||||||
for (const action in this.polygonClickActions) {
|
for (const action in this.polygonClickActions) {
|
||||||
this.polygonClickActions[action](event.originalEvent, datasource);
|
this.polygonClickActions[action](event.originalEvent, datasource);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public circleClick(circle: L.Layer, datasource: TbMapDatasource): void {
|
public circleClick(circle: TbDataLayerItem, datasource: TbMapDatasource): void {
|
||||||
if (Object.keys(this.circleClickActions).length) {
|
if (Object.keys(this.circleClickActions).length) {
|
||||||
circle.on('click', (event: L.LeafletMouseEvent) => {
|
circle.getLayer().on('click', (event: L.LeafletMouseEvent) => {
|
||||||
|
if (!circle.isEditing()) {
|
||||||
for (const action in this.circleClickActions) {
|
for (const action in this.circleClickActions) {
|
||||||
this.circleClickActions[action](event.originalEvent, datasource);
|
this.circleClickActions[action](event.originalEvent, datasource);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public selectItem(item: TbDataLayerItem): void {
|
||||||
|
if (this.selectedDataItem) {
|
||||||
|
this.selectedDataItem.deselect();
|
||||||
|
this.selectedDataItem = null;
|
||||||
|
this.editToolbar.close();
|
||||||
|
}
|
||||||
|
this.selectedDataItem = item;
|
||||||
|
if (this.selectedDataItem) {
|
||||||
|
const buttons = this.selectedDataItem.select();
|
||||||
|
this.editToolbar.open(buttons);
|
||||||
|
this.createdControlButtonTooltip(this.editToolbar.container, 'top');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public deselectItem(): void {
|
||||||
|
this.selectItem(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getSelectedDataItem(): TbDataLayerItem {
|
||||||
|
return this.selectedDataItem;
|
||||||
|
}
|
||||||
|
|
||||||
public saveItemData(datasource: TbMapDatasource, data: DataKeyValuePair[]): Observable<any> {
|
public saveItemData(datasource: TbMapDatasource, data: DataKeyValuePair[]): Observable<any> {
|
||||||
const attributeService = this.ctx.$injector.get(AttributeService);
|
const attributeService = this.ctx.$injector.get(AttributeService);
|
||||||
const attributes: AttributeData[] = [];
|
const attributes: AttributeData[] = [];
|
||||||
@ -466,8 +527,10 @@ export abstract class TbMap<S extends BaseMapSettings> {
|
|||||||
if (this.map) {
|
if (this.map) {
|
||||||
this.map.remove();
|
this.map.remove();
|
||||||
}
|
}
|
||||||
this.tooltipInstances.forEach((instance) => {
|
this.tooltipInstances.forEach((data) => {
|
||||||
|
data.instances.forEach(instance => {
|
||||||
instance.destroy();
|
instance.destroy();
|
||||||
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
28
ui-ngx/src/typings/leaflet-extend-tb.d.ts
vendored
28
ui-ngx/src/typings/leaflet-extend-tb.d.ts
vendored
@ -15,7 +15,7 @@
|
|||||||
///
|
///
|
||||||
|
|
||||||
import { FormattedData } from '@shared/models/widget.models';
|
import { FormattedData } from '@shared/models/widget.models';
|
||||||
import L from 'leaflet';
|
import L, { Control, ControlOptions } from 'leaflet';
|
||||||
import { TbMapDatasource } from '@home/components/widget/lib/maps/models/map.models';
|
import { TbMapDatasource } from '@home/components/widget/lib/maps/models/map.models';
|
||||||
|
|
||||||
// redeclare module, maintains compatibility with @types/leaflet
|
// redeclare module, maintains compatibility with @types/leaflet
|
||||||
@ -89,6 +89,30 @@ declare module 'leaflet' {
|
|||||||
constructor(options: GroupsControlOptions);
|
constructor(options: GroupsControlOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ToolbarButtonOptions extends ControlOptions{
|
||||||
|
title: string;
|
||||||
|
click: (e: MouseEvent, button: ToolbarButton) => void;
|
||||||
|
iconClass: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ToolbarButton extends Control<ToolbarButtonOptions>{
|
||||||
|
constructor(options: ToolbarButtonOptions);
|
||||||
|
addToToolbar(toolbar: BottomToolbarControl): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface BottomToolbarControlOptions extends ControlOptions {
|
||||||
|
mapElement: JQuery<HTMLElement>;
|
||||||
|
closeTitle: string;
|
||||||
|
onClose: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
class BottomToolbarControl extends Control<BottomToolbarControlOptions> {
|
||||||
|
constructor(options: BottomToolbarControlOptions);
|
||||||
|
open(buttons: ToolbarButtonOptions[]): void;
|
||||||
|
close(): void;
|
||||||
|
container: HTMLElement;
|
||||||
|
}
|
||||||
|
|
||||||
function sidebar(options: SidebarControlOptions): SidebarControl;
|
function sidebar(options: SidebarControlOptions): SidebarControl;
|
||||||
|
|
||||||
function sidebarPane<O extends SidebarPaneControlOptions>(options: O): SidebarPaneControl<O>;
|
function sidebarPane<O extends SidebarPaneControlOptions>(options: O): SidebarPaneControl<O>;
|
||||||
@ -97,6 +121,8 @@ declare module 'leaflet' {
|
|||||||
|
|
||||||
function groups(options: GroupsControlOptions): GroupsControl;
|
function groups(options: GroupsControlOptions): GroupsControl;
|
||||||
|
|
||||||
|
function bottomToolbar(options: BottomToolbarControlOptions): BottomToolbarControl;
|
||||||
|
|
||||||
namespace TileLayer {
|
namespace TileLayer {
|
||||||
|
|
||||||
interface ChinaProvidersData {
|
interface ChinaProvidersData {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user