1
0
mirror of https://github.com/home-assistant/frontend.git synced 2025-12-20 02:38:53 +00:00

Add zones config UI (#4556)

* Add zones config UI

* Update en.json

* Update dialog-zone-detail.ts

* Update hc-cast.ts

* Update more-info-content.ts

* Add drag radius and icon to dialog

* Review comments
This commit is contained in:
Bram Kragten
2020-01-22 20:29:51 +01:00
committed by Paulus Schoutsen
parent def0c51669
commit 49611e285f
14 changed files with 1142 additions and 24 deletions

View File

@@ -8,24 +8,35 @@ import {
customElement,
PropertyValues,
} from "lit-element";
import { Marker, Map, LeafletMouseEvent, DragEndEvent, LatLng } from "leaflet";
import {
Marker,
Map,
LeafletMouseEvent,
DragEndEvent,
LatLng,
Circle,
DivIcon,
} from "leaflet";
import {
setupLeafletMap,
LeafletModuleType,
} from "../../common/dom/setup-leaflet-map";
import { fireEvent } from "../../common/dom/fire_event";
import { nextRender } from "../../common/util/render-status";
@customElement("ha-location-editor")
class LocationEditor extends LitElement {
@property() public location?: [number, number];
@property() public radius?: number;
@property() public icon?: string;
public fitZoom = 16;
private _iconEl?: DivIcon;
private _ignoreFitToMap?: [number, number];
// tslint:disable-next-line
private Leaflet?: LeafletModuleType;
private _leafletMap?: Map;
private _locationMarker?: Marker;
private _locationMarker?: Marker | Circle;
public fitMap(): void {
if (!this._leafletMap || !this.location) {
@@ -53,11 +64,24 @@ class LocationEditor extends LitElement {
return;
}
this._updateMarker();
if (!this._ignoreFitToMap || this._ignoreFitToMap !== this.location) {
this.fitMap();
if (changedProps.has("location")) {
this._updateMarker();
if (
this.location &&
(!this._ignoreFitToMap ||
this._ignoreFitToMap[0] !== this.location[0] ||
this._ignoreFitToMap[1] !== this.location[1])
) {
this.fitMap();
}
this._ignoreFitToMap = undefined;
}
if (changedProps.has("radius")) {
this._updateRadius();
}
if (changedProps.has("icon")) {
this._updateIcon();
}
this._ignoreFitToMap = undefined;
}
private get _mapEl(): HTMLDivElement {
@@ -65,18 +89,23 @@ class LocationEditor extends LitElement {
}
private async _initMap(): Promise<void> {
[this._leafletMap, this.Leaflet] = await setupLeafletMap(this._mapEl);
[this._leafletMap, this.Leaflet] = await setupLeafletMap(
this._mapEl,
false,
Boolean(this.radius)
);
this._leafletMap.addEventListener(
"click",
// @ts-ignore
(ev: LeafletMouseEvent) => this._updateLocation(ev.latlng)
(ev: LeafletMouseEvent) => this._locationUpdated(ev.latlng)
);
this._updateIcon();
this._updateMarker();
this.fitMap();
this._leafletMap.invalidateSize();
}
private _updateLocation(latlng: LatLng) {
private _locationUpdated(latlng: LatLng) {
let longitude = latlng.lng;
if (Math.abs(longitude) > 180.0) {
// Normalize longitude if map provides values beyond -180 to +180 degrees.
@@ -86,7 +115,68 @@ class LocationEditor extends LitElement {
fireEvent(this, "change", undefined, { bubbles: false });
}
private _updateMarker(): void {
private _radiusUpdated() {
this._ignoreFitToMap = this.location;
this.radius = (this._locationMarker as Circle).getRadius();
fireEvent(this, "change", undefined, { bubbles: false });
}
private _updateIcon() {
if (!this.icon) {
this._iconEl = undefined;
return;
}
// create icon
let iconHTML = "";
const el = document.createElement("ha-icon");
el.setAttribute("icon", this.icon);
iconHTML = el.outerHTML;
this._iconEl = this.Leaflet!.divIcon({
html: iconHTML,
iconSize: [24, 24],
className: "light leaflet-edit-move",
});
this._setIcon();
}
private _setIcon() {
if (!this._locationMarker || !this._iconEl) {
return;
}
if (!this.radius) {
(this._locationMarker as Marker).setIcon(this._iconEl);
return;
}
// @ts-ignore
const moveMarker = this._locationMarker.editing._moveMarker;
moveMarker.setIcon(this._iconEl);
}
private _setupEdit() {
// @ts-ignore
this._locationMarker.editing.enable();
// @ts-ignore
const moveMarker = this._locationMarker.editing._moveMarker;
// @ts-ignore
const resizeMarker = this._locationMarker.editing._resizeMarkers[0];
this._setIcon();
moveMarker.addEventListener(
"dragend",
// @ts-ignore
(ev: DragEndEvent) => this._locationUpdated(ev.target.getLatLng())
);
resizeMarker.addEventListener(
"dragend",
// @ts-ignore
(ev: DragEndEvent) => this._radiusUpdated(ev)
);
}
private async _updateMarker(): Promise<void> {
if (!this.location) {
if (this._locationMarker) {
this._locationMarker.remove();
@@ -97,17 +187,41 @@ class LocationEditor extends LitElement {
if (this._locationMarker) {
this._locationMarker.setLatLng(this.location);
if (this.radius) {
// @ts-ignore
this._locationMarker.editing.disable();
await nextRender();
this._setupEdit();
}
return;
}
this._locationMarker = this.Leaflet!.marker(this.location, {
draggable: true,
});
this._locationMarker.addEventListener(
"dragend",
// @ts-ignore
(ev: DragEndEvent) => this._updateLocation(ev.target.getLatLng())
);
this._leafletMap!.addLayer(this._locationMarker);
if (!this.radius) {
this._locationMarker = this.Leaflet!.marker(this.location, {
draggable: true,
});
this._setIcon();
this._locationMarker.addEventListener(
"dragend",
// @ts-ignore
(ev: DragEndEvent) => this._locationUpdated(ev.target.getLatLng())
);
this._leafletMap!.addLayer(this._locationMarker);
} else {
this._locationMarker = this.Leaflet!.circle(this.location, {
color: "#FF9800",
radius: this.radius,
});
this._leafletMap!.addLayer(this._locationMarker);
this._setupEdit();
}
}
private _updateRadius(): void {
if (!this._locationMarker || !this.radius) {
return;
}
(this._locationMarker as Circle).setRadius(this.radius);
}
static get styles(): CSSResult {
@@ -119,6 +233,13 @@ class LocationEditor extends LitElement {
#map {
height: 100%;
}
.leaflet-edit-move {
cursor: move !important;
}
.leaflet-edit-resize {
border-radius: 50%;
cursor: nesw-resize !important;
}
`;
}
}