UI: Map - improve edit mode states. Add draw map items buttons.

This commit is contained in:
Igor Kulikov 2025-01-28 20:11:46 +02:00
parent cec50eac80
commit f8751003b5
8 changed files with 222 additions and 83 deletions

View File

@ -52,7 +52,6 @@ export abstract class TbDataLayerItem<S extends MapDataLayerSettings = MapDataLa
protected tooltip: L.Popup; protected tooltip: L.Popup;
protected data: FormattedData<TbMapDatasource>; protected data: FormattedData<TbMapDatasource>;
protected selected = false; protected selected = false;
protected removed = false;
protected constructor(data: FormattedData<TbMapDatasource>, protected constructor(data: FormattedData<TbMapDatasource>,
dsData: FormattedData<TbMapDatasource>[], dsData: FormattedData<TbMapDatasource>[],
@ -101,11 +100,6 @@ export abstract class TbDataLayerItem<S extends MapDataLayerSettings = MapDataLa
this.dataLayer.getMap().selectItem(this); this.dataLayer.getMap().selectItem(this);
} }
}); });
this.layer.on('remove', () => {
if (this.selected) {
this.dataLayer.getMap().deselectItem();
}
});
} }
} }
@ -163,9 +157,9 @@ export abstract class TbDataLayerItem<S extends MapDataLayerSettings = MapDataLa
} }
} }
public deselect(cancel = false): boolean { public deselect(cancel = false, force = false): boolean {
if (this.selected) { if (this.selected) {
if (this.canDeselect(cancel)) { if (this.canDeselect(cancel) || force) {
this.selected = false; this.selected = false;
this.layer.closePopup(); this.layer.closePopup();
this.updateSelectedState(); this.updateSelectedState();
@ -197,9 +191,8 @@ export abstract class TbDataLayerItem<S extends MapDataLayerSettings = MapDataLa
} }
public remove() { public remove() {
this.removed = true;
if (this.selected) { if (this.selected) {
this.dataLayer.getMap().deselectItem(); this.dataLayer.getMap().deselectItem(false, true);
} }
this.dataLayer.getDataLayerContainer().removeLayer(this.layer); this.dataLayer.getDataLayerContainer().removeLayer(this.layer);
this.layer.off(); this.layer.off();
@ -399,6 +392,8 @@ export abstract class TbMapDataLayer<S extends MapDataLayerSettings, D extends T
private editMode = false; private editMode = false;
private unplacedItems: FormattedData<TbMapDatasource>[] = [];
public dataLayerLabelProcessor: DataLayerPatternProcessor; public dataLayerLabelProcessor: DataLayerPatternProcessor;
public dataLayerTooltipProcessor: DataLayerPatternProcessor; public dataLayerTooltipProcessor: DataLayerPatternProcessor;
@ -504,6 +499,12 @@ export abstract class TbMapDataLayer<S extends MapDataLayerSettings, D extends T
this.map.getMap().addLayer(this.dataLayerContainer); this.map.getMap().addLayer(this.dataLayerContainer);
this.updateItemsEditMode(); this.updateItemsEditMode();
} else { } else {
for (const item of this.layerItems) {
if (item[1].isSelected()) {
this.getMap().deselectItem(false, true);
break;
}
}
this.map.getMap().removeLayer(this.dataLayerContainer); this.map.getMap().removeLayer(this.dataLayerContainer);
} }
return true; return true;
@ -513,11 +514,12 @@ export abstract class TbMapDataLayer<S extends MapDataLayerSettings, D extends T
} }
public updateData(dsData: FormattedData<TbMapDatasource>[]) { public updateData(dsData: FormattedData<TbMapDatasource>[]) {
this.unplacedItems.length = 0;
const layerData = dsData.filter(d => d.$datasource.mapDataIds.includes(this.mapDataId)); const layerData = dsData.filter(d => d.$datasource.mapDataIds.includes(this.mapDataId));
const rawItems = layerData.filter(d => this.isValidLayerData(d));
const toDelete = new Set(Array.from(this.layerItems.keys())); const toDelete = new Set(Array.from(this.layerItems.keys()));
const updatedItems: TbDataLayerItem<S,D,L>[] = []; const updatedItems: TbDataLayerItem<S,D,L>[] = [];
rawItems.forEach((data) => { layerData.forEach((data) => {
if (this.isValidLayerData(data)) {
let layerItem = this.layerItems.get(data.entityId); let layerItem = this.layerItems.get(data.entityId);
if (layerItem) { if (layerItem) {
layerItem.update(data, dsData); layerItem.update(data, dsData);
@ -527,6 +529,9 @@ export abstract class TbMapDataLayer<S extends MapDataLayerSettings, D extends T
this.layerItems.set(data.entityId, layerItem); this.layerItems.set(data.entityId, layerItem);
} }
toDelete.delete(data.entityId); toDelete.delete(data.entityId);
} else {
this.unplacedItems.push(data);
}
}); });
toDelete.forEach((key) => { toDelete.forEach((key) => {
const item = this.layerItems.get(key); const item = this.layerItems.get(key);
@ -545,10 +550,15 @@ export abstract class TbMapDataLayer<S extends MapDataLayerSettings, D extends T
public getCtx(): WidgetContext { public getCtx(): WidgetContext {
return this.map.getCtx(); return this.map.getCtx();
} }
public getMap(): TbMap<any> { public getMap(): TbMap<any> {
return this.map; return this.map;
} }
public hasUnplacedItems(): boolean {
return !!this.unplacedItems.length;
}
protected createDataLayerContainer(): L.FeatureGroup { protected createDataLayerContainer(): L.FeatureGroup {
return L.featureGroup([], {snapIgnore: !this.snappable}); return L.featureGroup([], {snapIgnore: !this.snappable});
} }

View File

@ -202,10 +202,10 @@ class TbMarkerDataLayerItem extends TbDataLayerItem<MarkersDataLayerSettings, Tb
let icon: L.Icon | L.DivIcon; let icon: L.Icon | L.DivIcon;
const options = deepClone(iconInfo.icon.options); const options = deepClone(iconInfo.icon.options);
options.className = this.updateIconClasses(options.className); options.className = this.updateIconClasses(options.className);
if (iconInfo.icon instanceof L.Icon) { if (iconInfo.icon instanceof L.DivIcon) {
icon = L.icon(options as L.IconOptions);
} else {
icon = L.divIcon(options); icon = L.divIcon(options);
} else {
icon = L.icon(options as L.IconOptions);
} }
this.marker.setIcon(icon); this.marker.setIcon(icon);
const anchor = options.iconAnchor; const anchor = options.iconAnchor;

View File

@ -77,8 +77,10 @@ class TbPolygonDataLayerItem extends TbDataLayerItem<PolygonsDataLayerSettings,
this.updatePolygonShape(data); this.updatePolygonShape(data);
this.updateTooltip(data, dsData); this.updateTooltip(data, dsData);
this.updateLabel(data, dsData); this.updateLabel(data, dsData);
if (!this.editing || !this.dataLayer.getMap().getMap().pm.globalCutModeEnabled()) {
this.polygon.setStyle(this.polygonStyle); this.polygon.setStyle(this.polygonStyle);
} }
}
protected doInvalidateCoordinates(data: FormattedData<TbMapDatasource>, _dsData: FormattedData<TbMapDatasource>[]): void { protected doInvalidateCoordinates(data: FormattedData<TbMapDatasource>, _dsData: FormattedData<TbMapDatasource>[]): void {
this.updatePolygonShape(data); this.updatePolygonShape(data);
@ -171,7 +173,6 @@ class TbPolygonDataLayerItem extends TbDataLayerItem<PolygonsDataLayerSettings,
} }
protected canDeselect(cancel = false): boolean { protected canDeselect(cancel = false): boolean {
if (!this.removed) {
const map = this.dataLayer.getMap().getMap(); const map = this.dataLayer.getMap().getMap();
if (map.pm.globalCutModeEnabled()) { if (map.pm.globalCutModeEnabled()) {
if (cancel) { if (cancel) {
@ -184,7 +185,6 @@ class TbPolygonDataLayerItem extends TbDataLayerItem<PolygonsDataLayerSettings,
} }
return false; return false;
} }
}
return true; return true;
} }
@ -222,6 +222,9 @@ class TbPolygonDataLayerItem extends TbDataLayerItem<PolygonsDataLayerSettings,
this.polygonContainer.closePopup(); this.polygonContainer.closePopup();
this.editing = true; this.editing = true;
this.polygon.options.bubblingMouseEvents = true; this.polygon.options.bubblingMouseEvents = true;
this.polygon.setStyle({...this.polygonStyle, dashArray: '5 5', weight: 3,
color: '#3388ff', opacity: 1, fillColor: '#3388ff', fillOpacity: 0.2});
this.addItemClass('tb-cut-mode');
this.polygon.once('pm:cut', (e) => { this.polygon.once('pm:cut', (e) => {
if (this.polygon instanceof L.Rectangle) { if (this.polygon instanceof L.Rectangle) {
this.polygonContainer.removeLayer(this.polygon); this.polygonContainer.removeLayer(this.polygon);
@ -271,6 +274,8 @@ class TbPolygonDataLayerItem extends TbDataLayerItem<PolygonsDataLayerSettings,
private disablePolygonCutMode(cutButton?: L.TB.ToolbarButton) { private disablePolygonCutMode(cutButton?: L.TB.ToolbarButton) {
this.editing = false; this.editing = false;
this.polygon.options.bubblingMouseEvents = false; this.polygon.options.bubblingMouseEvents = false;
this.polygon.setStyle({...this.polygonStyle, dashArray: null});
this.removeItemClass('tb-cut-mode');
this.polygon.off('pm:cut'); this.polygon.off('pm:cut');
const map = this.dataLayer.getMap().getMap(); const map = this.dataLayer.getMap().getMap();
map.pm.disableGlobalCutMode(); map.pm.disableGlobalCutMode();

View File

@ -331,13 +331,39 @@ class ToolbarButton extends L.Control<TB.ToolbarButtonOptions> {
return this.disabled; return this.disabled;
} }
addToToolbar(toolbar: BottomToolbarControl): void {
this.button.appendTo(toolbar.container);
}
getId(): string { getId(): string {
return this.id; return this.id;
} }
getButtonElement(): JQuery<HTMLElement> {
return this.button;
}
}
class ToolbarControl extends L.Control<L.ControlOptions> {
private buttonContainer: JQuery<HTMLElement>;
constructor(options: L.ControlOptions) {
super(options);
}
toolbarButton(options: TB.ToolbarButtonOptions): ToolbarButton {
const button = new ToolbarButton(options);
button.getButtonElement().appendTo(this.buttonContainer);
return button;
}
onAdd(map: L.Map): HTMLElement {
this.buttonContainer = $("<div>")
.attr('class', 'leaflet-bar');
return this.buttonContainer[0];
}
addTo(map: L.Map): this {
return super.addTo(map);
}
} }
class BottomToolbarControl extends L.Control<TB.BottomToolbarControlOptions> { class BottomToolbarControl extends L.Control<TB.BottomToolbarControlOptions> {
@ -372,7 +398,7 @@ class BottomToolbarControl extends L.Control<TB.BottomToolbarControlOptions> {
buttons.forEach(buttonOption => { buttons.forEach(buttonOption => {
const button = new ToolbarButton(buttonOption); const button = new ToolbarButton(buttonOption);
this.toolbarButtons.push(button); this.toolbarButtons.push(button);
button.addToToolbar(this); button.getButtonElement().appendTo(this.container);
}); });
const closeButton = $("<a>") const closeButton = $("<a>")
@ -418,6 +444,10 @@ const groups = (options: TB.GroupsControlOptions): GroupsControl => {
return new GroupsControl(options); return new GroupsControl(options);
} }
const toolbar = (options: L.ControlOptions): ToolbarControl => {
return new ToolbarControl(options);
}
const bottomToolbar = (options: TB.BottomToolbarControlOptions): BottomToolbarControl => { const bottomToolbar = (options: TB.BottomToolbarControlOptions): BottomToolbarControl => {
return new BottomToolbarControl(options); return new BottomToolbarControl(options);
} }
@ -478,11 +508,13 @@ L.TB = L.TB || {
LayersControl, LayersControl,
GroupsControl, GroupsControl,
ToolbarButton, ToolbarButton,
ToolbarControl,
BottomToolbarControl, BottomToolbarControl,
sidebar, sidebar,
sidebarPane, sidebarPane,
layers, layers,
groups, groups,
toolbar,
bottomToolbar, bottomToolbar,
TileLayer: { TileLayer: {
ChinaProvider ChinaProvider

View File

@ -15,10 +15,6 @@
*/ */
@import '../../../../../../../scss/constants'; @import '../../../../../../../scss/constants';
//$map-element-hover-color: #307FE5;
$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;
width: 100%; width: 100%;
@ -36,19 +32,11 @@ $map-element-selected-color: #307FE5;
flex: 1; flex: 1;
&.leaflet-touch { &.leaflet-touch {
.leaflet-bar { .leaflet-bar {
border: none; border: 1px solid rgba(0,0,0,0.38);
border-radius: 15px; border-radius: 15px;
box-shadow: 4px 4px 4px 0 rgba(0, 0, 0, 0.15); box-shadow: 0 4px 6px 0 rgba(0, 0, 0, 0.32);
background: #fff; background: #fff;
position: relative; position: relative;
&:before {
content: "";
position: absolute;
inset: 0;
border-radius: 15px;
background: $tb-primary-color; // primary color
opacity: 0.05;
}
a { a {
color: rgba(0, 0, 0, 0.54); color: rgba(0, 0, 0, 0.54);
border-bottom: none; border-bottom: none;
@ -111,10 +99,10 @@ $map-element-selected-color: #307FE5;
mask-repeat: no-repeat; mask-repeat: no-repeat;
mask-position: center; mask-position: center;
&.tb-layers { &.tb-layers {
mask-image: url('data:image/svg+xml,<svg width="20" height="20" viewBox="0 0 18 16" xmlns="http://www.w3.org/2000/svg"><path d="M9 9.5L0.75 5L9 0.5L17.25 5L9 9.5ZM9 12.5L1.18125 8.24375L2.75625 7.38125L9 10.7938L15.2438 7.38125L16.8188 8.24375L9 12.5ZM9 15.5L1.18125 11.2438L2.75625 10.3813L9 13.7938L15.2438 10.3813L16.8188 11.2438L9 15.5Z"/></svg>'); mask-image: url('data:image/svg+xml,<svg width="18" height="18" viewBox="0 0 18 16" xmlns="http://www.w3.org/2000/svg"><path d="M9 9.5L0.75 5L9 0.5L17.25 5L9 9.5ZM9 12.5L1.18125 8.24375L2.75625 7.38125L9 10.7938L15.2438 7.38125L16.8188 8.24375L9 12.5ZM9 15.5L1.18125 11.2438L2.75625 10.3813L9 13.7938L15.2438 10.3813L16.8188 11.2438L9 15.5Z"/></svg>');
} }
&.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="18" height="18" 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 { &.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>'); 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>');
@ -125,6 +113,18 @@ $map-element-selected-color: #307FE5;
&.tb-rotate { &.tb-rotate {
mask-image: url('data:image/svg+xml,<svg width="16" height="16" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg"><path d="M1.77 1.7625C2.8575 0.675 4.35 0 6.0075 0C9.3225 0 12 2.685 12 6C12 9.315 9.3225 12 6.0075 12C3.21 12 0.8775 10.0875 0.21 7.5H1.77C2.385 9.2475 4.05 10.5 6.0075 10.5C8.49 10.5 10.5075 8.4825 10.5075 6C10.5075 3.5175 8.49 1.5 6.0075 1.5C4.7625 1.5 3.6525 2.0175 2.8425 2.835L5.2575 5.25H0.00749922V0L1.77 1.7625Z"/></svg>'); mask-image: url('data:image/svg+xml,<svg width="16" height="16" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg"><path d="M1.77 1.7625C2.8575 0.675 4.35 0 6.0075 0C9.3225 0 12 2.685 12 6C12 9.315 9.3225 12 6.0075 12C3.21 12 0.8775 10.0875 0.21 7.5H1.77C2.385 9.2475 4.05 10.5 6.0075 10.5C8.49 10.5 10.5075 8.4825 10.5075 6C10.5075 3.5175 8.49 1.5 6.0075 1.5C4.7625 1.5 3.6525 2.0175 2.8425 2.835L5.2575 5.25H0.00749922V0L1.77 1.7625Z"/></svg>');
} }
&.tb-place-marker {
mask-image: url('data:image/svg+xml,<svg width="12" height="16" viewBox="0 0 12 16" xmlns="http://www.w3.org/2000/svg"><path d="M6 0.5C3.0975 0.5 0.75 2.8475 0.75 5.75C0.75 9.6875 6 15.5 6 15.5C6 15.5 11.25 9.6875 11.25 5.75C11.25 2.8475 8.9025 0.5 6 0.5ZM6 7.625C4.965 7.625 4.125 6.785 4.125 5.75C4.125 4.715 4.965 3.875 6 3.875C7.035 3.875 7.875 4.715 7.875 5.75C7.875 6.785 7.035 7.625 6 7.625Z"/></svg>');
}
&.tb-draw-rectangle {
mask-image: url('data:image/svg+xml,<svg width="18" height="18" viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg"><path d="M1.5 1.5H6V3H12V1.5H16.5V6H15V12H16.5V16.5H12V15H6V16.5H1.5V12H3V6H1.5V1.5ZM12 6V4.5H6V6H4.5V12H6V13.5H12V12H13.5V6H12ZM3 3V4.5H4.5V3H3ZM13.5 3V4.5H15V3H13.5ZM3 13.5V15H4.5V13.5H3ZM13.5 13.5V15H15V13.5H13.5Z"/></svg>');
}
&.tb-draw-polygon {
mask-image: url('data:image/svg+xml,<svg width="18" height="18" viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg"><path d="M16.5 6V1.5H12V4.35L10.8 6H7.2L6 4.35V1.5H1.5V6H3V12H1.5V16.5H6V15H12V16.5H16.5V12H15V6H16.5ZM8.25 7.5H9.75V9H8.25V7.5ZM3 3H4.5V4.5H3V3ZM4.5 15H3V13.5H4.5V15ZM12 13.5H6V12H4.5V6H5.325L6.75 7.95V10.5H11.25V7.95L12.675 6H13.5V12H12V13.5ZM15 15H13.5V13.5H15V15ZM13.5 4.5V3H15V4.5H13.5Z"/></svg>');
}
&.tb-draw-circle {
mask-image: url('data:image/svg+xml,<svg width="18" height="18" viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg"><path d="M16.5 6.75H14.9775C14.025 4.0575 11.4825 2.25 8.625 2.25C6.83479 2.25 5.1179 2.96116 3.85203 4.22703C2.58616 5.4929 1.875 7.20979 1.875 9C1.875 12.75 4.8975 15.75 8.625 15.75C11.4825 15.75 14.025 13.95 15 11.25H16.5M15 8.25V9.75H13.5V8.25M13.365 11.25C12.495 13.08 10.65 14.25 8.625 14.25C5.73 14.25 3.375 11.9025 3.375 9C3.375 6.105 5.73 3.75 8.625 3.75C10.65 3.75 12.495 4.9275 13.3575 6.75H12V11.25"/></svg>');
}
&.tb-close { &.tb-close {
background: #D12730; 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>'); 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>');
@ -156,15 +156,19 @@ $map-element-selected-color: #307FE5;
&.tb-hoverable:not(.tb-selected) { &.tb-hoverable:not(.tb-selected) {
&:hover { &:hover {
svg { svg {
//filter: drop-shadow( 0 0 4px $map-element-hover-color); filter: brightness(1.3)
filter: brightness(0.8) drop-shadow( 0 0 4px $map-element-hover-color); drop-shadow( 0 0 4px rgba(0,0,0,0.56))
drop-shadow( 0 0 4px rgba(255,255,255,0.56));
} }
} }
} }
&.tb-selected { &.tb-selected {
svg { svg {
filter: brightness(0.8); filter: brightness(1.3)
//animation: tb-selected-animation 0.5s linear 0s infinite alternate; drop-shadow( 0 0 2px rgba(0,0,0,.6))
drop-shadow( 0 0 4px rgba(255,255,255,.7))
drop-shadow( 0 0 6px rgba(0,0,0,.8))
drop-shadow( 0 0 8px rgba(255,255,255,.9));
} }
} }
} }
@ -175,12 +179,28 @@ $map-element-selected-color: #307FE5;
} }
&.tb-hoverable:not(.tb-selected) { &.tb-hoverable:not(.tb-selected) {
&:hover { &:hover {
filter: brightness(0.8) drop-shadow( 0 0 4px $map-element-hover-color); filter: brightness(1.3)
drop-shadow( 0 0 4px rgba(0,0,0,0.56))
drop-shadow( 0 0 4px rgba(255,255,255,0.56));
} }
} }
}
img.leaflet-marker-icon {
&.tb-selected { &.tb-selected {
filter: brightness(0.8); filter: brightness(1.3)
//animation: tb-selected-animation 0.5s linear 0s infinite alternate; drop-shadow( 0 0 2px rgba(0,0,0,.6))
drop-shadow( 0 0 4px rgba(255,255,255,.7))
drop-shadow( 0 0 6px rgba(0,0,0,.8))
drop-shadow( 0 0 8px rgba(255,255,255,.9));
}
}
path {
&.tb-selected:not(.tb-cut-mode) {
filter: brightness(1.3)
drop-shadow( 0 0 4px rgba(0,0,0,.4))
drop-shadow( 0 0 4px rgba(255,255,255,.3))
drop-shadow( 0 0 8px rgba(0,0,0,.6))
drop-shadow( 0 0 8px rgba(255,255,255,.5));
} }
} }
.tb-cluster-marker-container { .tb-cluster-marker-container {
@ -322,14 +342,3 @@ $map-element-selected-color: #307FE5;
} }
} }
} }
@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);
}
}

View File

@ -23,7 +23,9 @@ import {
mergeMapDatasources, mergeMapDatasources,
parseCenterPosition, parseCenterPosition,
TbCircleData, TbCircleData,
TbMapDatasource, TbPolygonCoordinates, TbPolygonRawCoordinates TbMapDatasource,
TbPolygonCoordinates,
TbPolygonRawCoordinates
} from '@home/components/widget/lib/maps/models/map.models'; } from '@home/components/widget/lib/maps/models/map.models';
import { WidgetContext } from '@home/models/widget-component.models'; import { WidgetContext } from '@home/models/widget-component.models';
import { formattedDataFormDatasourceData, isDefinedAndNotNull, mergeDeepIgnoreArray } from '@core/utils'; import { formattedDataFormDatasourceData, isDefinedAndNotNull, mergeDeepIgnoreArray } from '@core/utils';
@ -74,6 +76,15 @@ export abstract class TbMap<S extends BaseMapSettings> {
protected editToolbar: L.TB.BottomToolbarControl; protected editToolbar: L.TB.BottomToolbarControl;
protected addMarkerButton: L.TB.ToolbarButton;
protected addRectangleButton: L.TB.ToolbarButton;
protected addPolygonButton: L.TB.ToolbarButton;
protected addCircleButton: L.TB.ToolbarButton;
protected addMarkerDataLayers: TbMapDataLayer<any,any>[];
protected addPolygonDataLayers: TbMapDataLayer<any,any>[];
protected addCircleDataLayers: TbMapDataLayer<any,any>[];
private readonly mapResize$: ResizeObserver; private readonly mapResize$: ResizeObserver;
private readonly tooltipActions: { [name: string]: MapActionHandler }; private readonly tooltipActions: { [name: string]: MapActionHandler };
@ -253,6 +264,51 @@ export abstract class TbMap<S extends BaseMapSettings> {
this.map.on('click', () => { this.map.on('click', () => {
this.deselectItem(); this.deselectItem();
}); });
const addSupportedDataLayers = this.dataLayers.filter(dl => dl.isAddEnabled());
if (addSupportedDataLayers.length) {
const drawToolbar = L.TB.toolbar({
position: this.settings.controlsPosition
}).addTo(this.map);
this.addMarkerDataLayers = addSupportedDataLayers.filter(dl => dl.dataLayerType() === MapDataLayerType.marker);
if (this.addMarkerDataLayers.length) {
this.addMarkerButton = drawToolbar.toolbarButton({
id: 'addMarker',
title: this.ctx.translate.instant('widgets.maps.data-layer.marker.place-marker'),
iconClass: 'tb-place-marker',
click: (e, button) => {}
});
this.addMarkerButton.setDisabled(true);
}
this.addPolygonDataLayers = addSupportedDataLayers.filter(dl => dl.dataLayerType() === MapDataLayerType.polygon);
if (this.addPolygonDataLayers.length) {
this.addRectangleButton = drawToolbar.toolbarButton({
id: 'addRectangle',
title: this.ctx.translate.instant('widgets.maps.data-layer.polygon.draw-rectangle'),
iconClass: 'tb-draw-rectangle',
click: (e, button) => {}
});
this.addRectangleButton.setDisabled(true);
this.addPolygonButton = drawToolbar.toolbarButton({
id: 'addPolygon',
title: this.ctx.translate.instant('widgets.maps.data-layer.polygon.draw-polygon'),
iconClass: 'tb-draw-polygon',
click: (e, button) => {}
});
this.addPolygonButton.setDisabled(true);
}
this.addCircleDataLayers = addSupportedDataLayers.filter(dl => dl.dataLayerType() === MapDataLayerType.circle);
if (this.addCircleDataLayers.length) {
this.addCircleButton = drawToolbar.toolbarButton({
id: 'addCircle',
title: this.ctx.translate.instant('widgets.maps.data-layer.circle.draw-circle'),
iconClass: 'tb-draw-circle',
click: (e, button) => {}
});
this.addCircleButton.setDisabled(true);
}
}
} }
private createdControlButtonTooltip(root: HTMLElement, side: TooltipPositioningSide) { private createdControlButtonTooltip(root: HTMLElement, side: TooltipPositioningSide) {
@ -320,6 +376,7 @@ export abstract class TbMap<S extends BaseMapSettings> {
undefined, undefined, el => el.datasource.entityId + el.datasource.mapDataIds[0]); undefined, undefined, el => el.datasource.entityId + el.datasource.mapDataIds[0]);
this.dataLayers.forEach(dl => dl.updateData(this.dsData)); this.dataLayers.forEach(dl => dl.updateData(this.dsData));
this.updateBounds(); this.updateBounds();
this.updateAddButtonsStates();
} }
private resize() { private resize() {
@ -365,6 +422,21 @@ export abstract class TbMap<S extends BaseMapSettings> {
}, entityName, null, entityLabel); }, entityName, null, entityLabel);
} }
private updateAddButtonsStates() {
if (this.addMarkerButton) {
this.addMarkerButton.setDisabled(!this.addMarkerDataLayers.some(dl => dl.hasUnplacedItems()));
}
if (this.addRectangleButton) {
this.addRectangleButton.setDisabled(!this.addPolygonDataLayers.some(dl => dl.hasUnplacedItems()));
}
if (this.addPolygonButton) {
this.addPolygonButton.setDisabled(!this.addPolygonDataLayers.some(dl => dl.hasUnplacedItems()));
}
if (this.addCircleButton) {
this.addCircleButton.setDisabled(!this.addCircleDataLayers.some(dl => dl.hasUnplacedItems()));
}
}
protected abstract defaultSettings(): S; protected abstract defaultSettings(): S;
protected abstract createMap(): Observable<L.Map>; protected abstract createMap(): Observable<L.Map>;
@ -454,10 +526,10 @@ export abstract class TbMap<S extends BaseMapSettings> {
} }
} }
public selectItem(item: TbDataLayerItem, cancel = false): boolean { public selectItem(item: TbDataLayerItem, cancel = false, force = false): boolean {
let deselected = true; let deselected = true;
if (this.selectedDataItem) { if (this.selectedDataItem) {
deselected = this.selectedDataItem.deselect(cancel); deselected = this.selectedDataItem.deselect(cancel, force);
if (deselected) { if (deselected) {
this.selectedDataItem = null; this.selectedDataItem = null;
this.editToolbar.close(); this.editToolbar.close();
@ -475,8 +547,8 @@ export abstract class TbMap<S extends BaseMapSettings> {
return deselected; return deselected;
} }
public deselectItem(cancel = false): boolean { public deselectItem(cancel = false, force = false): boolean {
return this.selectItem(null, cancel); return this.selectItem(null, cancel, force);
} }
public getSelectedDataItem(): TbDataLayerItem { public getSelectedDataItem(): TbDataLayerItem {

View File

@ -7717,7 +7717,8 @@
"marker-color-function": "Marker color function" "marker-color-function": "Marker color function"
}, },
"edit": "Edit marker", "edit": "Edit marker",
"remove-marker-for": "Remove marker for '{{entityName}}'" "remove-marker-for": "Remove marker for '{{entityName}}'",
"place-marker": "Place marker"
}, },
"polygon": { "polygon": {
"polygon-key": "Polygon key", "polygon-key": "Polygon key",
@ -7732,7 +7733,9 @@
"rotate": "Rotate polygon", "rotate": "Rotate polygon",
"firstVertex-cut": "Click to place first point", "firstVertex-cut": "Click to place first point",
"continueLine-cut": "Click to continue drawing", "continueLine-cut": "Click to continue drawing",
"finishPoly-cut": "Click first marker to finish and save" "finishPoly-cut": "Click first marker to finish and save",
"draw-rectangle": "Draw rectangle",
"draw-polygon": "Draw polygon"
}, },
"circle": { "circle": {
"circle-key": "Circle key", "circle-key": "Circle key",
@ -7742,7 +7745,8 @@
"circle-configuration": "Circle configuration", "circle-configuration": "Circle configuration",
"remove-circle": "Remove circle", "remove-circle": "Remove circle",
"edit": "Edit circle", "edit": "Edit circle",
"remove-circle-for": "Remove circle for '{{entityName}}'" "remove-circle-for": "Remove circle for '{{entityName}}'",
"draw-circle": "Draw circle"
} }
}, },
"select-entity": "Select entity", "select-entity": "Select entity",

View File

@ -104,6 +104,11 @@ declare module 'leaflet' {
isDisabled(): boolean; isDisabled(): boolean;
} }
class ToolbarControl extends Control<ControlOptions> {
constructor(options: ControlOptions);
toolbarButton(options: ToolbarButtonOptions): ToolbarButton;
}
interface BottomToolbarControlOptions extends ControlOptions { interface BottomToolbarControlOptions extends ControlOptions {
mapElement: JQuery<HTMLElement>; mapElement: JQuery<HTMLElement>;
closeTitle: string; closeTitle: string;
@ -126,6 +131,8 @@ declare module 'leaflet' {
function groups(options: GroupsControlOptions): GroupsControl; function groups(options: GroupsControlOptions): GroupsControl;
function toolbar(options: ControlOptions): ToolbarControl;
function bottomToolbar(options: BottomToolbarControlOptions): BottomToolbarControl; function bottomToolbar(options: BottomToolbarControlOptions): BottomToolbarControl;
namespace TileLayer { namespace TileLayer {