From a3ca889ca355cc2dd5eccaff20d012020b38c178 Mon Sep 17 00:00:00 2001 From: karwosts <32912880+karwosts@users.noreply.github.com> Date: Wed, 4 Dec 2024 22:55:53 -0800 Subject: [PATCH] Location selector: Move location on map click (#22198) * Add button to location-selector to move marker to current view * move on click * double-click handling --- .../ha-selector/ha-selector-location.ts | 1 + src/components/map/ha-locations-editor.ts | 39 ++++++++++++++++--- src/components/map/ha-map.ts | 30 ++++++++++++++ 3 files changed, 64 insertions(+), 6 deletions(-) diff --git a/src/components/ha-selector/ha-selector-location.ts b/src/components/ha-selector/ha-selector-location.ts index 2ac0b8b82bbe..c169d74e62f6 100644 --- a/src/components/ha-selector/ha-selector-location.ts +++ b/src/components/ha-selector/ha-selector-location.ts @@ -79,6 +79,7 @@ export class HaLocationSelector extends LitElement { .locations=${this._location(this.selector, this.value)} @location-updated=${this._locationChanged} @radius-updated=${this._radiusChanged} + pin-on-click > ; @state() private _circles: Record = {}; @@ -129,6 +132,8 @@ export class HaLocationsEditor extends LitElement { .zoom=${this.zoom} .autoFit=${this.autoFit} .themeMode=${this.themeMode} + .clickable=${this.pinOnClick} + @map-clicked=${this._mapClicked} > ${this.helper ? html`${this.helper}` @@ -193,15 +198,21 @@ export class HaLocationsEditor extends LitElement { } } - private _updateLocation(ev: DragEndEvent) { - const marker = ev.target; - const latlng: LatLng = marker.getLatLng(); - let longitude: number = latlng.lng; + private _normalizeLongitude(longitude: number): number { if (Math.abs(longitude) > 180.0) { // Normalize longitude if map provides values beyond -180 to +180 degrees. - longitude = (((longitude % 360.0) + 540.0) % 360.0) - 180.0; + return (((longitude % 360.0) + 540.0) % 360.0) - 180.0; } - const location: [number, number] = [latlng.lat, longitude]; + return longitude; + } + + private _updateLocation(ev: DragEndEvent) { + const marker = ev.target; + const latlng: LatLng = marker.getLatLng(); + const location: [number, number] = [ + latlng.lat, + this._normalizeLongitude(latlng.lng), + ]; fireEvent( this, "location-updated", @@ -226,6 +237,22 @@ export class HaLocationsEditor extends LitElement { fireEvent(this, "marker-clicked", { id: marker.id }, { bubbles: false }); } + private _mapClicked(ev) { + if (this.pinOnClick && this._locationMarkers) { + const id = Object.keys(this._locationMarkers)[0]; + const location: [number, number] = [ + ev.detail.location[0], + this._normalizeLongitude(ev.detail.location[1]), + ]; + fireEvent(this, "location-updated", { id, location }, { bubbles: false }); + + // If the normalized longitude wraps around the globe, pan to the new location. + if (location[1] !== ev.detail.location[1]) { + this.map.leafletMap?.panTo({ lat: location[0], lng: location[1] }); + } + } + } + private _updateMarkers(): void { if (!this.locations || !this.locations.length) { this._circles = {}; diff --git a/src/components/map/ha-map.ts b/src/components/map/ha-map.ts index 0cce2ded0b2a..d2d44f2ade3e 100644 --- a/src/components/map/ha-map.ts +++ b/src/components/map/ha-map.ts @@ -12,6 +12,7 @@ import type { import type { CSSResultGroup, PropertyValues } from "lit"; import { ReactiveElement, css } from "lit"; import { customElement, property, state } from "lit/decorators"; +import { fireEvent } from "../../common/dom/fire_event"; import { formatDateTime } from "../../common/datetime/format_date_time"; import { formatTimeWeekday, @@ -26,6 +27,13 @@ import { isTouch } from "../../util/is_touch"; import "../ha-icon-button"; import "./ha-entity-marker"; +declare global { + // for fire event + interface HASSDomEvents { + "map-clicked": { location: [number, number] }; + } +} + const getEntityId = (entity: string | HaMapEntity): string => typeof entity === "string" ? entity : entity.entity_id; @@ -59,6 +67,8 @@ export class HaMap extends ReactiveElement { @property({ attribute: false }) public layers?: Layer[]; + @property({ type: Boolean }) public clickable = false; + @property({ type: Boolean }) public autoFit = false; @property({ type: Boolean }) public renderPassive = false; @@ -90,6 +100,8 @@ export class HaMap extends ReactiveElement { private _mapPaths: Array = []; + private _clickCount = 0; + public connectedCallback(): void { super.connectedCallback(); this._loadMap(); @@ -173,6 +185,7 @@ export class HaMap extends ReactiveElement { private _updateMapStyle(): void { const map = this.renderRoot.querySelector("#map"); + map!.classList.toggle("clickable", this.clickable); map!.classList.toggle("dark", this._darkMode); map!.classList.toggle("forced-dark", this.themeMode === "dark"); map!.classList.toggle("forced-light", this.themeMode === "light"); @@ -192,6 +205,19 @@ export class HaMap extends ReactiveElement { try { [this.leafletMap, this.Leaflet] = await setupLeafletMap(map); this._updateMapStyle(); + this.leafletMap.on("click", (ev) => { + if (this._clickCount === 0) { + setTimeout(() => { + if (this._clickCount === 1) { + fireEvent(this, "map-clicked", { + location: [ev.latlng.lat, ev.latlng.lng], + }); + } + this._clickCount = 0; + }, 250); + } + this._clickCount++; + }); this._loaded = true; } finally { this._loading = false; @@ -558,6 +584,9 @@ export class HaMap extends ReactiveElement { #map { height: 100%; } + #map.clickable { + cursor: pointer; + } #map.dark { background: #090909; } @@ -571,6 +600,7 @@ export class HaMap extends ReactiveElement { color: #000000; --map-filter: invert(0); } + #map.clickable:active, #map:active { cursor: grabbing; cursor: -moz-grabbing;