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
This commit is contained in:
karwosts 2024-12-04 22:55:53 -08:00 committed by GitHub
parent a7406b3201
commit a3ca889ca3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 64 additions and 6 deletions

View File

@ -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
></ha-locations-editor>
<ha-form
.hass=${this.hass}

View File

@ -58,6 +58,9 @@ export class HaLocationsEditor extends LitElement {
@property({ attribute: "theme-mode", type: String })
public themeMode: ThemeMode = "auto";
@property({ type: Boolean, attribute: "pin-on-click" })
public pinOnClick = false;
@state() private _locationMarkers?: Record<string, Marker | Circle>;
@state() private _circles: Record<string, Circle> = {};
@ -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}
></ha-map>
${this.helper
? html`<ha-input-helper-text>${this.helper}</ha-input-helper-text>`
@ -193,15 +198,21 @@ export class HaLocationsEditor extends LitElement {
}
}
private _normalizeLongitude(longitude: number): number {
if (Math.abs(longitude) > 180.0) {
// Normalize longitude if map provides values beyond -180 to +180 degrees.
return (((longitude % 360.0) + 540.0) % 360.0) - 180.0;
}
return longitude;
}
private _updateLocation(ev: DragEndEvent) {
const marker = ev.target;
const latlng: LatLng = marker.getLatLng();
let longitude: number = latlng.lng;
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;
}
const location: [number, number] = [latlng.lat, longitude];
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 = {};

View File

@ -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<Polyline | CircleMarker> = [];
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;